From c5d69ab1b25cda1d074f41ef2d0203d385f20d04 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 27 Apr 2022 17:05:00 +0200 Subject: [PATCH 001/930] Handle removed entites in collection.sync_entity_lifecycle (#70759) * Handle removed entites in collection.sync_entity_lifecycle * Add comment --- homeassistant/helpers/collection.py | 15 ++- homeassistant/helpers/entity.py | 18 ++- homeassistant/helpers/entity_platform.py | 2 +- tests/helpers/test_collection.py | 142 ++++++++++++++++++++++- 4 files changed, 168 insertions(+), 9 deletions(-) diff --git a/homeassistant/helpers/collection.py b/homeassistant/helpers/collection.py index 9017c60c23f..7a73f90539c 100644 --- a/homeassistant/helpers/collection.py +++ b/homeassistant/helpers/collection.py @@ -334,7 +334,13 @@ def sync_entity_lifecycle( ent_reg = entity_registry.async_get(hass) async def _add_entity(change_set: CollectionChangeSet) -> Entity: + def entity_removed() -> None: + """Remove entity from entities if it's removed or not added.""" + if change_set.item_id in entities: + entities.pop(change_set.item_id) + entities[change_set.item_id] = create_entity(change_set.item) + entities[change_set.item_id].async_on_remove(entity_removed) return entities[change_set.item_id] async def _remove_entity(change_set: CollectionChangeSet) -> None: @@ -343,11 +349,16 @@ def sync_entity_lifecycle( ) if ent_to_remove is not None: ent_reg.async_remove(ent_to_remove) - else: + elif change_set.item_id in entities: await entities[change_set.item_id].async_remove(force_remove=True) - entities.pop(change_set.item_id) + # Unconditionally pop the entity from the entity list to avoid racing against + # the entity registry event handled by Entity._async_registry_updated + if change_set.item_id in entities: + entities.pop(change_set.item_id) async def _update_entity(change_set: CollectionChangeSet) -> None: + if change_set.item_id not in entities: + return await entities[change_set.item_id].async_update_config(change_set.item) # type: ignore[attr-defined] _func_map: dict[ diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index f94b4257d30..791a80f7731 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -759,7 +759,7 @@ class Entity(ABC): @callback def async_on_remove(self, func: CALLBACK_TYPE) -> None: - """Add a function to call when entity removed.""" + """Add a function to call when entity is removed or not added.""" if self._on_remove is None: self._on_remove = [] self._on_remove.append(func) @@ -788,13 +788,23 @@ class Entity(ABC): self.parallel_updates = parallel_updates self._platform_state = EntityPlatformState.ADDED + def _call_on_remove_callbacks(self) -> None: + """Call callbacks registered by async_on_remove.""" + if self._on_remove is None: + return + while self._on_remove: + self._on_remove.pop()() + @callback def add_to_platform_abort(self) -> None: """Abort adding an entity to a platform.""" + + self._platform_state = EntityPlatformState.NOT_ADDED + self._call_on_remove_callbacks() + self.hass = None # type: ignore[assignment] self.platform = None self.parallel_updates = None - self._platform_state = EntityPlatformState.NOT_ADDED async def add_to_platform_finish(self) -> None: """Finish adding an entity to a platform.""" @@ -819,9 +829,7 @@ class Entity(ABC): self._platform_state = EntityPlatformState.REMOVED - if self._on_remove is not None: - while self._on_remove: - self._on_remove.pop()() + self._call_on_remove_callbacks() await self.async_internal_will_remove_from_hass() await self.async_will_remove_from_hass() diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 65cfe706f14..6972dbf7c16 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -611,7 +611,7 @@ class EntityPlatform: self.hass.states.async_reserve(entity.entity_id) def remove_entity_cb() -> None: - """Remove entity from entities list.""" + """Remove entity from entities dict.""" self.entities.pop(entity_id) entity.async_on_remove(remove_entity_cb) diff --git a/tests/helpers/test_collection.py b/tests/helpers/test_collection.py index 11ab0f46ce4..cd4bbba1a3e 100644 --- a/tests/helpers/test_collection.py +++ b/tests/helpers/test_collection.py @@ -4,7 +4,13 @@ import logging import pytest import voluptuous as vol -from homeassistant.helpers import collection, entity, entity_component, storage +from homeassistant.helpers import ( + collection, + entity, + entity_component, + entity_registry as er, + storage, +) from tests.common import flush_store @@ -261,6 +267,140 @@ async def test_attach_entity_component_collection(hass): assert hass.states.get("test.mock_1") is None +async def test_entity_component_collection_abort(hass): + """Test aborted entity adding is handled.""" + ent_comp = entity_component.EntityComponent(_LOGGER, "test", hass) + coll = collection.ObservableCollection(_LOGGER) + + async_update_config_calls = [] + async_remove_calls = [] + + class MockMockEntity(MockEntity): + """Track calls to async_update_config and async_remove.""" + + async def async_update_config(self, config): + nonlocal async_update_config_calls + async_update_config_calls.append(None) + await super().async_update_config() + + async def async_remove(self, *, force_remove: bool = False): + nonlocal async_remove_calls + async_remove_calls.append(None) + await super().async_remove() + + collection.sync_entity_lifecycle( + hass, "test", "test", ent_comp, coll, MockMockEntity + ) + entity_registry = er.async_get(hass) + entity_registry.async_get_or_create( + "test", + "test", + "mock_id", + suggested_object_id="mock_1", + disabled_by=er.RegistryEntryDisabler.INTEGRATION, + ) + + await coll.notify_changes( + [ + collection.CollectionChangeSet( + collection.CHANGE_ADDED, + "mock_id", + {"id": "mock_id", "state": "initial", "name": "Mock 1"}, + ) + ], + ) + + assert hass.states.get("test.mock_1") is None + + await coll.notify_changes( + [ + collection.CollectionChangeSet( + collection.CHANGE_UPDATED, + "mock_id", + {"id": "mock_id", "state": "second", "name": "Mock 1 updated"}, + ) + ], + ) + + assert hass.states.get("test.mock_1") is None + assert len(async_update_config_calls) == 0 + + await coll.notify_changes( + [collection.CollectionChangeSet(collection.CHANGE_REMOVED, "mock_id", None)], + ) + + assert hass.states.get("test.mock_1") is None + assert len(async_remove_calls) == 0 + + +async def test_entity_component_collection_entity_removed(hass): + """Test entity removal is handled.""" + ent_comp = entity_component.EntityComponent(_LOGGER, "test", hass) + coll = collection.ObservableCollection(_LOGGER) + + async_update_config_calls = [] + async_remove_calls = [] + + class MockMockEntity(MockEntity): + """Track calls to async_update_config and async_remove.""" + + async def async_update_config(self, config): + nonlocal async_update_config_calls + async_update_config_calls.append(None) + await super().async_update_config() + + async def async_remove(self, *, force_remove: bool = False): + nonlocal async_remove_calls + async_remove_calls.append(None) + await super().async_remove() + + collection.sync_entity_lifecycle( + hass, "test", "test", ent_comp, coll, MockMockEntity + ) + entity_registry = er.async_get(hass) + entity_registry.async_get_or_create( + "test", "test", "mock_id", suggested_object_id="mock_1" + ) + + await coll.notify_changes( + [ + collection.CollectionChangeSet( + collection.CHANGE_ADDED, + "mock_id", + {"id": "mock_id", "state": "initial", "name": "Mock 1"}, + ) + ], + ) + + assert hass.states.get("test.mock_1").name == "Mock 1" + assert hass.states.get("test.mock_1").state == "initial" + + entity_registry.async_remove("test.mock_1") + await hass.async_block_till_done() + assert hass.states.get("test.mock_1") is None + assert len(async_remove_calls) == 1 + + await coll.notify_changes( + [ + collection.CollectionChangeSet( + collection.CHANGE_UPDATED, + "mock_id", + {"id": "mock_id", "state": "second", "name": "Mock 1 updated"}, + ) + ], + ) + + assert hass.states.get("test.mock_1") is None + assert len(async_update_config_calls) == 0 + + await coll.notify_changes( + [collection.CollectionChangeSet(collection.CHANGE_REMOVED, "mock_id", None)], + ) + + assert hass.states.get("test.mock_1") is None + assert len(async_remove_calls) == 1 + + async def test_storage_collection_websocket(hass, hass_ws_client): """Test exposing a storage collection via websockets.""" store = storage.Store(hass, 1, "test-data") From 71f95d199aa63e2b31d5d20566d64fec97d26b00 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:18:35 +0200 Subject: [PATCH 002/930] Migrate vera light to ColorMode (#70861) --- homeassistant/components/vera/light.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/vera/light.py b/homeassistant/components/vera/light.py index f54cfb8ddcc..e91492a934f 100644 --- a/homeassistant/components/vera/light.py +++ b/homeassistant/components/vera/light.py @@ -9,8 +9,7 @@ from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ENTITY_ID_FORMAT, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, + ColorMode, LightEntity, ) from homeassistant.config_entries import ConfigEntry @@ -63,11 +62,18 @@ class VeraLight(VeraDevice[veraApi.VeraDimmer], LightEntity): return self._color @property - def supported_features(self) -> int: - """Flag supported features.""" - if self._color: - return SUPPORT_BRIGHTNESS | SUPPORT_COLOR - return SUPPORT_BRIGHTNESS + def color_mode(self) -> ColorMode: + """Return the color mode of the light.""" + if self.vera_device.is_dimmable: + if self._color: + return ColorMode.HS + return ColorMode.BRIGHTNESS + return ColorMode.ONOFF + + @property + def supported_color_modes(self) -> set[ColorMode]: + """Flag supported color modes.""" + return {self.color_mode} def turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" From 7dfe8591c493c486233de06cfa56cf4f978d7c80 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:19:06 +0200 Subject: [PATCH 003/930] Improve typing [helpers.event] (#70891) --- .strict-typing | 1 + homeassistant/helpers/event.py | 4 ++-- mypy.ini | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.strict-typing b/.strict-typing index cda4f9e12fc..546e99a96b8 100644 --- a/.strict-typing +++ b/.strict-typing @@ -17,6 +17,7 @@ homeassistant.helpers.area_registry homeassistant.helpers.condition homeassistant.helpers.discovery homeassistant.helpers.entity_values +homeassistant.helpers.event homeassistant.helpers.reload homeassistant.helpers.script_variables homeassistant.helpers.translation diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index b85af09cfb9..427297b2f1d 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -558,7 +558,7 @@ class _TrackStateChangeFiltered: self._setup_entities_listener(track_states.domains, track_states.entities) @property - def listeners(self) -> dict: + def listeners(self) -> dict[str, bool | set[str]]: """State changes that will cause a re-render.""" track_states = self._last_track_states return { @@ -853,7 +853,7 @@ class _TrackTemplateResultInfo: ) @property - def listeners(self) -> dict: + def listeners(self) -> dict[str, bool | set[str]]: """State changes that will cause a re-render.""" assert self._track_state_changes return { diff --git a/mypy.ini b/mypy.ini index 6010475602d..519191a77cd 100644 --- a/mypy.ini +++ b/mypy.ini @@ -62,6 +62,9 @@ disallow_any_generics = true [mypy-homeassistant.helpers.entity_values] disallow_any_generics = true +[mypy-homeassistant.helpers.event] +disallow_any_generics = true + [mypy-homeassistant.helpers.reload] disallow_any_generics = true From 964c764dae1c7731664fb20e6eaac36cb5ef7bd3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:19:46 +0200 Subject: [PATCH 004/930] Improve typing [helpers.sun] (#70892) --- .strict-typing | 1 + homeassistant/helpers/sun.py | 16 ++++++++++------ mypy.ini | 3 +++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.strict-typing b/.strict-typing index 546e99a96b8..f63122d66bb 100644 --- a/.strict-typing +++ b/.strict-typing @@ -20,6 +20,7 @@ homeassistant.helpers.entity_values homeassistant.helpers.event homeassistant.helpers.reload homeassistant.helpers.script_variables +homeassistant.helpers.sun homeassistant.helpers.translation homeassistant.util.async_ homeassistant.util.color diff --git a/homeassistant/helpers/sun.py b/homeassistant/helpers/sun.py index 09a329cd275..25bef38ed0b 100644 --- a/homeassistant/helpers/sun.py +++ b/homeassistant/helpers/sun.py @@ -1,8 +1,9 @@ """Helpers for sun events.""" from __future__ import annotations +from collections.abc import Callable import datetime -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, cast from homeassistant.const import SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET from homeassistant.core import HomeAssistant, callback @@ -11,11 +12,14 @@ from homeassistant.util import dt as dt_util if TYPE_CHECKING: import astral + import astral.location DATA_LOCATION_CACHE = "astral_location_cache" ELEVATION_AGNOSTIC_EVENTS = ("noon", "midnight") +_AstralSunEventCallable = Callable[..., datetime.datetime] + @callback @bind_hass @@ -73,15 +77,15 @@ def get_location_astral_event_next( if utc_point_in_time is None: utc_point_in_time = dt_util.utcnow() - kwargs = {"local": False} + kwargs: dict[str, Any] = {"local": False} if event not in ELEVATION_AGNOSTIC_EVENTS: kwargs["observer_elevation"] = elevation mod = -1 while True: try: - next_dt: datetime.datetime = ( - getattr(location, event)( + next_dt = ( + cast(_AstralSunEventCallable, getattr(location, event))( dt_util.as_local(utc_point_in_time).date() + datetime.timedelta(days=mod), **kwargs, @@ -111,12 +115,12 @@ def get_astral_event_date( if isinstance(date, datetime.datetime): date = dt_util.as_local(date).date() - kwargs = {"local": False} + kwargs: dict[str, Any] = {"local": False} if event not in ELEVATION_AGNOSTIC_EVENTS: kwargs["observer_elevation"] = elevation try: - return getattr(location, event)(date, **kwargs) # type: ignore[no-any-return] + return cast(_AstralSunEventCallable, getattr(location, event))(date, **kwargs) except ValueError: # Event never occurs for specified date. return None diff --git a/mypy.ini b/mypy.ini index 519191a77cd..abd37cb3783 100644 --- a/mypy.ini +++ b/mypy.ini @@ -71,6 +71,9 @@ disallow_any_generics = true [mypy-homeassistant.helpers.script_variables] disallow_any_generics = true +[mypy-homeassistant.helpers.sun] +disallow_any_generics = true + [mypy-homeassistant.helpers.translation] disallow_any_generics = true From 02ddfd513aa1d6b76c890c81db1b64afb099e5bb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:20:56 +0200 Subject: [PATCH 005/930] Improve typing [util.location] (#70893) --- .strict-typing | 1 + homeassistant/util/location.py | 34 ++++++++++++++++------------------ mypy.ini | 3 +++ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/.strict-typing b/.strict-typing index f63122d66bb..d907421d50e 100644 --- a/.strict-typing +++ b/.strict-typing @@ -25,6 +25,7 @@ homeassistant.helpers.translation homeassistant.util.async_ homeassistant.util.color homeassistant.util.decorator +homeassistant.util.location homeassistant.util.process homeassistant.util.unit_system diff --git a/homeassistant/util/location.py b/homeassistant/util/location.py index e653b439e0e..b4d7274ded7 100644 --- a/homeassistant/util/location.py +++ b/homeassistant/util/location.py @@ -6,9 +6,8 @@ detect_location_info and elevation are mocked by default during tests. from __future__ import annotations import asyncio -import collections import math -from typing import Any +from typing import Any, NamedTuple import aiohttp @@ -30,22 +29,21 @@ MILES_PER_KILOMETER = 0.621371 MAX_ITERATIONS = 200 CONVERGENCE_THRESHOLD = 1e-12 -LocationInfo = collections.namedtuple( - "LocationInfo", - [ - "ip", - "country_code", - "currency", - "region_code", - "region_name", - "city", - "zip_code", - "time_zone", - "latitude", - "longitude", - "use_metric", - ], -) + +class LocationInfo(NamedTuple): + """Tuple with location information.""" + + ip: str + country_code: str + currency: str + region_code: str + region_name: str + city: str + zip_code: str + time_zone: str + latitude: float + longitude: float + use_metric: bool async def async_detect_location_info( diff --git a/mypy.ini b/mypy.ini index abd37cb3783..165bca681ff 100644 --- a/mypy.ini +++ b/mypy.ini @@ -86,6 +86,9 @@ disallow_any_generics = true [mypy-homeassistant.util.decorator] disallow_any_generics = true +[mypy-homeassistant.util.location] +disallow_any_generics = true + [mypy-homeassistant.util.process] disallow_any_generics = true From 361119d5c166be28f41966d0f9acf466e066a566 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 27 Apr 2022 11:24:26 -0400 Subject: [PATCH 006/930] Improve ZHA startup performance (#70111) * Remove semaphores and background mains init * additional logging * correct cache usage and update tests --- .../components/zha/core/channels/__init__.py | 14 +------- .../components/zha/core/channels/base.py | 27 +++++++++++++--- .../components/zha/core/channels/general.py | 4 ++- .../zha/core/channels/homeautomation.py | 2 +- homeassistant/components/zha/core/device.py | 18 +++++++++++ homeassistant/components/zha/core/gateway.py | 32 ++++++++----------- homeassistant/components/zha/light.py | 2 +- tests/components/zha/conftest.py | 1 + tests/components/zha/test_device.py | 6 ++-- tests/components/zha/test_fan.py | 15 +++++++-- 10 files changed, 76 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index 2011f92a63b..00409794473 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations import asyncio -from collections.abc import Coroutine from typing import TYPE_CHECKING, Any, TypeVar import zigpy.endpoint @@ -50,7 +49,6 @@ class Channels: self._pools: list[ChannelPool] = [] self._power_config: base.ZigbeeChannel | None = None self._identify: base.ZigbeeChannel | None = None - self._semaphore = asyncio.Semaphore(3) self._unique_id = str(zha_device.ieee) self._zdo_channel = base.ZDOChannel(zha_device.device.endpoints[0], zha_device) self._zha_device = zha_device @@ -82,11 +80,6 @@ class Channels: if self._identify is None: self._identify = channel - @property - def semaphore(self) -> asyncio.Semaphore: - """Return semaphore for concurrent tasks.""" - return self._semaphore - @property def zdo_channel(self) -> base.ZDOChannel: """Return ZDO channel.""" @@ -336,13 +329,8 @@ class ChannelPool: async def _execute_channel_tasks(self, func_name: str, *args: Any) -> None: """Add a throttled channel task and swallow exceptions.""" - - async def _throttle(coro: Coroutine[Any, Any, None]) -> None: - async with self._channels.semaphore: - return await coro - channels = [*self.claimed_channels.values(), *self.client_channels.values()] - tasks = [_throttle(getattr(ch, func_name)(*args)) for ch in channels] + tasks = [getattr(ch, func_name)(*args) for ch in channels] results = await asyncio.gather(*tasks, return_exceptions=True) for channel, outcome in zip(channels, results): if isinstance(outcome, Exception): diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index b10209f45f4..7beefe2f0d0 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -310,11 +310,14 @@ class ZigbeeChannel(LogMixin): """Set cluster binding and attribute reporting.""" if not self._ch_pool.skip_configuration: if self.BIND: + self.debug("Performing cluster binding") await self.bind() if self.cluster.is_server: + self.debug("Configuring cluster attribute reporting") await self.configure_reporting() ch_specific_cfg = getattr(self, "async_configure_channel_specific", None) if ch_specific_cfg: + self.debug("Performing channel specific configuration") await ch_specific_cfg() self.debug("finished channel configuration") else: @@ -325,6 +328,7 @@ class ZigbeeChannel(LogMixin): async def async_initialize(self, from_cache: bool) -> None: """Initialize channel.""" if not from_cache and self._ch_pool.skip_configuration: + self.debug("Skipping channel initialization") self._status = ChannelStatus.INITIALIZED return @@ -334,12 +338,23 @@ class ZigbeeChannel(LogMixin): uncached.extend([cfg["attr"] for cfg in self.REPORT_CONFIG]) if cached: - await self._get_attributes(True, cached, from_cache=True) + self.debug("initializing cached channel attributes: %s", cached) + await self._get_attributes( + True, cached, from_cache=True, only_cache=from_cache + ) if uncached: - await self._get_attributes(True, uncached, from_cache=from_cache) + self.debug( + "initializing uncached channel attributes: %s - from cache[%s]", + uncached, + from_cache, + ) + await self._get_attributes( + True, uncached, from_cache=from_cache, only_cache=from_cache + ) ch_specific_init = getattr(self, "async_initialize_channel_specific", None) if ch_specific_init: + self.debug("Performing channel specific initialization: %s", uncached) await ch_specific_init(from_cache=from_cache) self.debug("finished channel initialization") @@ -407,7 +422,7 @@ class ZigbeeChannel(LogMixin): self._cluster, [attribute], allow_cache=from_cache, - only_cache=from_cache and not self._ch_pool.is_mains_powered, + only_cache=from_cache, manufacturer=manufacturer, ) return result.get(attribute) @@ -417,6 +432,7 @@ class ZigbeeChannel(LogMixin): raise_exceptions: bool, attributes: list[int | str], from_cache: bool = True, + only_cache: bool = True, ) -> dict[int | str, Any]: """Get the values for a list of attributes.""" manufacturer = None @@ -428,17 +444,18 @@ class ZigbeeChannel(LogMixin): result = {} while chunk: try: + self.debug("Reading attributes in chunks: %s", chunk) read, _ = await self.cluster.read_attributes( attributes, allow_cache=from_cache, - only_cache=from_cache and not self._ch_pool.is_mains_powered, + only_cache=only_cache, manufacturer=manufacturer, ) result.update(read) except (asyncio.TimeoutError, zigpy.exceptions.ZigbeeException) as ex: self.debug( "failed to get attributes '%s' on '%s' cluster: %s", - attributes, + chunk, self.cluster.ep_attribute, str(ex), ) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index f528057c313..81152bb8869 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -463,7 +463,9 @@ class PowerConfigurationChannel(ZigbeeChannel): "battery_size", "battery_quantity", ] - return self.get_attributes(attributes, from_cache=from_cache) + return self.get_attributes( + attributes, from_cache=from_cache, only_cache=from_cache + ) @registries.ZIGBEE_CHANNEL_REGISTRY.register(general.PowerProfile.cluster_id) diff --git a/homeassistant/components/zha/core/channels/homeautomation.py b/homeassistant/components/zha/core/channels/homeautomation.py index 02fa835dc20..e1019ed31bf 100644 --- a/homeassistant/components/zha/core/channels/homeautomation.py +++ b/homeassistant/components/zha/core/channels/homeautomation.py @@ -97,7 +97,7 @@ class ElectricalMeasurementChannel(ZigbeeChannel): for a in self.REPORT_CONFIG if a["attr"] not in self.cluster.unsupported_attributes ] - result = await self.get_attributes(attrs, from_cache=False) + result = await self.get_attributes(attrs, from_cache=False, only_cache=False) if result: for attr, value in result.items(): self.async_send_signal( diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 41d90b48869..854d80ffb78 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -351,11 +351,15 @@ class ZHADevice(LogMixin): if self.is_coordinator: return if self.last_seen is None: + self.debug("last_seen is None, marking the device unavailable") self.update_available(False) return difference = time.time() - self.last_seen if difference < self.consider_unavailable_time: + self.debug( + "Device seen - marking the device available and resetting counter" + ) self.update_available(True) self._checkins_missed_count = 0 return @@ -365,6 +369,10 @@ class ZHADevice(LogMixin): or self.manufacturer == "LUMI" or not self._channels.pools ): + self.debug( + "last_seen is %s seconds ago and ping attempts have been exhausted, marking the device unavailable", + difference, + ) self.update_available(False) return @@ -386,13 +394,23 @@ class ZHADevice(LogMixin): def update_available(self, available: bool) -> None: """Update device availability and signal entities.""" + self.debug( + "Update device availability - device available: %s - new availability: %s - changed: %s", + self.available, + available, + self.available ^ available, + ) availability_changed = self.available ^ available self.available = available if availability_changed and available: # reinit channels then signal entities + self.debug( + "Device availability changed and device became available, reinitializing channels" + ) self.hass.async_create_task(self._async_became_available()) return if availability_changed and not available: + self.debug("Device availability changed and device became unavailable") self._channels.zha_send_event( { "device_event_type": "device_offline", diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 64f7b24ff99..1280f3defe3 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -239,29 +239,25 @@ class ZHAGateway: async def async_initialize_devices_and_entities(self) -> None: """Initialize devices and load entities.""" - semaphore = asyncio.Semaphore(2) - async def _throttle(zha_device: ZHADevice, cached: bool) -> None: - async with semaphore: - await zha_device.async_initialize(from_cache=cached) - - _LOGGER.debug("Loading battery powered devices") + _LOGGER.warning("Loading all devices") await asyncio.gather( - *( - _throttle(dev, cached=True) - for dev in self.devices.values() - if not dev.is_mains_powered - ) + *(dev.async_initialize(from_cache=True) for dev in self.devices.values()) ) - _LOGGER.debug("Loading mains powered devices") - await asyncio.gather( - *( - _throttle(dev, cached=False) - for dev in self.devices.values() - if dev.is_mains_powered + async def fetch_updated_state() -> None: + """Fetch updated state for mains powered devices.""" + _LOGGER.warning("Fetching current state for mains powered devices") + await asyncio.gather( + *( + dev.async_initialize(from_cache=False) + for dev in self.devices.values() + if dev.is_mains_powered + ) ) - ) + + # background the fetching of state for mains powered devices + asyncio.create_task(fetch_updated_state()) def device_joined(self, device: zigpy.device.Device) -> None: """Handle device joined. diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index b6d344a57e7..ef7205feb87 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -488,7 +488,7 @@ class Light(BaseLight, ZhaEntity): ] results = await self._color_channel.get_attributes( - attributes, from_cache=False + attributes, from_cache=False, only_cache=False ) if (color_mode := results.get("color_mode")) is not None: diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index cb562cd5eaa..482a11b95de 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -177,6 +177,7 @@ def zha_device_joined(hass, setup_zha): """Return a newly joined ZHA device.""" async def _zha_device(zigpy_dev): + zigpy_dev.last_seen = time.time() await setup_zha() zha_gateway = common.get_zha_gateway(hass) await zha_gateway.async_device_initialized(zigpy_dev) diff --git a/tests/components/zha/test_device.py b/tests/components/zha/test_device.py index 76877e71ffc..0f2caceada5 100644 --- a/tests/components/zha/test_device.py +++ b/tests/components/zha/test_device.py @@ -106,7 +106,7 @@ def _send_time_changed(hass, seconds): @patch( "homeassistant.components.zha.core.channels.general.BasicChannel.async_initialize", - new=mock.MagicMock(), + new=mock.AsyncMock(), ) async def test_check_available_success( hass, device_with_basic_channel, zha_device_restored @@ -160,7 +160,7 @@ async def test_check_available_success( @patch( "homeassistant.components.zha.core.channels.general.BasicChannel.async_initialize", - new=mock.MagicMock(), + new=mock.AsyncMock(), ) async def test_check_available_unsuccessful( hass, device_with_basic_channel, zha_device_restored @@ -203,7 +203,7 @@ async def test_check_available_unsuccessful( @patch( "homeassistant.components.zha.core.channels.general.BasicChannel.async_initialize", - new=mock.MagicMock(), + new=mock.AsyncMock(), ) async def test_check_available_no_basic_channel( hass, device_without_basic_channel, zha_device_restored, caplog diff --git a/tests/components/zha/test_fan.py b/tests/components/zha/test_fan.py index e367857b260..19f5f39a22f 100644 --- a/tests/components/zha/test_fan.py +++ b/tests/components/zha/test_fan.py @@ -471,7 +471,10 @@ async def test_fan_update_entity( assert hass.states.get(entity_id).attributes[ATTR_PERCENTAGE] == 0 assert hass.states.get(entity_id).attributes[ATTR_PRESET_MODE] is None assert hass.states.get(entity_id).attributes[ATTR_PERCENTAGE_STEP] == 100 / 3 - assert cluster.read_attributes.await_count == 2 + if zha_device_joined_restored.name == "zha_device_joined": + assert cluster.read_attributes.await_count == 2 + else: + assert cluster.read_attributes.await_count == 4 await async_setup_component(hass, "homeassistant", {}) await hass.async_block_till_done() @@ -480,7 +483,10 @@ async def test_fan_update_entity( "homeassistant", "update_entity", {"entity_id": entity_id}, blocking=True ) assert hass.states.get(entity_id).state == STATE_OFF - assert cluster.read_attributes.await_count == 3 + if zha_device_joined_restored.name == "zha_device_joined": + assert cluster.read_attributes.await_count == 3 + else: + assert cluster.read_attributes.await_count == 5 cluster.PLUGGED_ATTR_READS = {"fan_mode": 1} await hass.services.async_call( @@ -490,4 +496,7 @@ async def test_fan_update_entity( assert hass.states.get(entity_id).attributes[ATTR_PERCENTAGE] == 33 assert hass.states.get(entity_id).attributes[ATTR_PRESET_MODE] is None assert hass.states.get(entity_id).attributes[ATTR_PERCENTAGE_STEP] == 100 / 3 - assert cluster.read_attributes.await_count == 4 + if zha_device_joined_restored.name == "zha_device_joined": + assert cluster.read_attributes.await_count == 4 + else: + assert cluster.read_attributes.await_count == 6 From c6d8fffeb40b9987901d84624f2e9f6f0cafc982 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 27 Apr 2022 17:27:51 +0200 Subject: [PATCH 007/930] Migrate netatmo light to color_mode (#70912) --- homeassistant/components/netatmo/light.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/netatmo/light.py b/homeassistant/components/netatmo/light.py index 58b1a0d4f43..7e078153a8a 100644 --- a/homeassistant/components/netatmo/light.py +++ b/homeassistant/components/netatmo/light.py @@ -6,7 +6,7 @@ from typing import Any, cast import pyatmo -from homeassistant.components.light import LightEntity +from homeassistant.components.light import ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady @@ -62,6 +62,9 @@ async def async_setup_entry( class NetatmoLight(NetatmoBase, LightEntity): """Representation of a Netatmo Presence camera light.""" + _attr_color_mode = ColorMode.ONOFF + _attr_supported_color_modes = {ColorMode.ONOFF} + def __init__( self, data_handler: NetatmoDataHandler, From f17cf7c6a20000a4310971e5e698963042d13a12 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 27 Apr 2022 17:34:34 +0200 Subject: [PATCH 008/930] Bump version to 2022.6.0dev0 (#70913) --- .github/workflows/ci.yaml | 2 +- homeassistant/const.py | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c95c09bd175..50a809a5d21 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ on: env: CACHE_VERSION: 9 PIP_CACHE_VERSION: 3 - HA_SHORT_VERSION: 2022.5 + HA_SHORT_VERSION: 2022.6 DEFAULT_PYTHON: 3.9 PRE_COMMIT_CACHE: ~/.cache/pre-commit PIP_CACHE: /tmp/pip-cache diff --git a/homeassistant/const.py b/homeassistant/const.py index ffe5825bf89..2f9a90032ef 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -6,7 +6,7 @@ from typing import Final from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 -MINOR_VERSION: Final = 5 +MINOR_VERSION: Final = 6 PATCH_VERSION: Final = "0.dev0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" diff --git a/setup.cfg b/setup.cfg index 4b23f5f7988..51226c30924 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = homeassistant -version = 2022.5.0.dev0 +version = 2022.6.0.dev0 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0 From 27daba441839a5fad0d92b81b07fc267e759758d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 27 Apr 2022 17:35:13 +0200 Subject: [PATCH 009/930] Tuning jemalloc (#70899) --- rootfs/etc/services.d/home-assistant/run | 1 + 1 file changed, 1 insertion(+) diff --git a/rootfs/etc/services.d/home-assistant/run b/rootfs/etc/services.d/home-assistant/run index 4fcbcce417a..40ec07c1543 100644 --- a/rootfs/etc/services.d/home-assistant/run +++ b/rootfs/etc/services.d/home-assistant/run @@ -8,5 +8,6 @@ cd /config || bashio::exit.nok "Can't find config folder!" # Enable mimalloc for Home Assistant Core, unless disabled if [[ -z "${DISABLE_JEMALLOC+x}" ]]; then export LD_PRELOAD="/usr/local/lib/libjemalloc.so.2" + export MALLOC_CONF="background_thread:true,metadata_thp:auto,dirty_decay_ms:20000,muzzy_decay_ms:20000" fi exec python3 -m homeassistant --config /config From b17d27262cd2cacf8436eae793d9442ac6d81d8a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 27 Apr 2022 05:54:47 -1000 Subject: [PATCH 010/930] Add additional OUI for tplink light devices (#70922) --- homeassistant/components/tplink/manifest.json | 4 ++++ homeassistant/generated/dhcp.py | 1 + 2 files changed, 5 insertions(+) diff --git a/homeassistant/components/tplink/manifest.json b/homeassistant/components/tplink/manifest.json index 383031d3417..9a419302a18 100644 --- a/homeassistant/components/tplink/manifest.json +++ b/homeassistant/components/tplink/manifest.json @@ -74,6 +74,10 @@ "hostname": "k[lp]*", "macaddress": "1027F5*" }, + { + "hostname": "k[lp]*", + "macaddress": "B0A7B9*" + }, { "hostname": "k[lp]*", "macaddress": "403F8C*" diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 3ae5b0a1376..d0ffb9b097a 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -137,6 +137,7 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '60A4B7*'}, {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '005F67*'}, {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '1027F5*'}, + {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'B0A7B9*'}, {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '403F8C*'}, {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': 'C0C9E3*'}, {'domain': 'tplink', 'hostname': 'k[lp]*', 'macaddress': '909A4A*'}, From 6151306e3d8f0bc774145280276a1801613f8aa7 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Wed, 27 Apr 2022 11:59:05 -0500 Subject: [PATCH 011/930] Use standard attribute for Sonos group members (#70924) --- homeassistant/components/sonos/media_player.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 4b4dc99ad71..30fdb28b02f 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -76,8 +76,6 @@ REPEAT_TO_SONOS = { SONOS_TO_REPEAT = {meaning: mode for mode, meaning in REPEAT_TO_SONOS.items()} -ATTR_SONOS_GROUP = "sonos_group" - UPNP_ERRORS_TO_IGNORE = ["701", "711", "712"] SERVICE_JOIN = "join" @@ -265,6 +263,11 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): """Return the current coordinator SonosSpeaker.""" return self.speaker.coordinator or self.speaker + @property + def group_members(self) -> list[str] | None: + """List of entity_ids which are currently grouped together.""" + return self.speaker.sonos_group_entities + def __hash__(self) -> int: """Return a hash of self.""" return hash(self.unique_id) @@ -654,9 +657,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): @property def extra_state_attributes(self) -> dict[str, Any]: """Return entity specific state attributes.""" - attributes: dict[str, Any] = { - ATTR_SONOS_GROUP: self.speaker.sonos_group_entities - } + attributes: dict[str, Any] = {} if self.media.queue_position is not None: attributes[ATTR_QUEUE_POSITION] = self.media.queue_position From 31e3f4892e46d3746c8423621391a6caccb70ae4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 27 Apr 2022 19:05:42 +0200 Subject: [PATCH 012/930] Bump hatasmota to 0.4.1 (#70799) Co-authored-by: Erik Montnemery --- .../components/tasmota/manifest.json | 2 +- homeassistant/components/tasmota/sensor.py | 23 ++++++++++++++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_sensor.py | 4 ++-- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 2f3a1b66fea..6a743683d94 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.4.0"], + "requirements": ["hatasmota==0.4.1"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"], diff --git a/homeassistant/components/tasmota/sensor.py b/homeassistant/components/tasmota/sensor.py index feb15cd5639..34526904f17 100644 --- a/homeassistant/components/tasmota/sensor.py +++ b/homeassistant/components/tasmota/sensor.py @@ -54,11 +54,24 @@ ICON = "icon" # A Tasmota sensor type may be mapped to either a device class or an icon, not both SENSOR_DEVICE_CLASS_ICON_MAP: dict[str, dict[str, Any]] = { + hc.SENSOR_ACTIVE_ENERGYEXPORT: { + DEVICE_CLASS: SensorDeviceClass.ENERGY, + STATE_CLASS: SensorStateClass.TOTAL, + }, + hc.SENSOR_ACTIVE_ENERGYIMPORT: { + DEVICE_CLASS: SensorDeviceClass.ENERGY, + STATE_CLASS: SensorStateClass.TOTAL, + }, + hc.SENSOR_ACTIVE_POWERUSAGE: { + DEVICE_CLASS: SensorDeviceClass.POWER, + STATE_CLASS: SensorStateClass.MEASUREMENT, + }, hc.SENSOR_AMBIENT: { DEVICE_CLASS: SensorDeviceClass.ILLUMINANCE, STATE_CLASS: SensorStateClass.MEASUREMENT, }, hc.SENSOR_APPARENT_POWERUSAGE: { + ICON: "mdi:flash", STATE_CLASS: SensorStateClass.MEASUREMENT, }, hc.SENSOR_BATTERY: { @@ -80,6 +93,11 @@ SENSOR_DEVICE_CLASS_ICON_MAP: dict[str, dict[str, Any]] = { ICON: "mdi:alpha-a-circle-outline", STATE_CLASS: SensorStateClass.MEASUREMENT, }, + hc.SENSOR_CURRENTNEUTRAL: { + ICON: "mdi:alpha-a-circle-outline", + DEVICE_CLASS: SensorDeviceClass.CURRENT, + STATE_CLASS: SensorStateClass.MEASUREMENT, + }, hc.SENSOR_DEWPOINT: { DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, ICON: "mdi:weather-rainy", @@ -141,7 +159,10 @@ SENSOR_DEVICE_CLASS_ICON_MAP: dict[str, dict[str, Any]] = { STATE_CLASS: SensorStateClass.MEASUREMENT, }, hc.SENSOR_PROXIMITY: {ICON: "mdi:ruler"}, + hc.SENSOR_REACTIVE_ENERGYEXPORT: {STATE_CLASS: SensorStateClass.TOTAL}, + hc.SENSOR_REACTIVE_ENERGYIMPORT: {STATE_CLASS: SensorStateClass.TOTAL}, hc.SENSOR_REACTIVE_POWERUSAGE: { + ICON: "mdi:flash", STATE_CLASS: SensorStateClass.MEASUREMENT, }, hc.SENSOR_STATUS_LAST_RESTART_TIME: {DEVICE_CLASS: SensorDeviceClass.TIMESTAMP}, @@ -162,7 +183,7 @@ SENSOR_DEVICE_CLASS_ICON_MAP: dict[str, dict[str, Any]] = { hc.SENSOR_TODAY: {DEVICE_CLASS: SensorDeviceClass.ENERGY}, hc.SENSOR_TOTAL: { DEVICE_CLASS: SensorDeviceClass.ENERGY, - STATE_CLASS: SensorStateClass.TOTAL_INCREASING, + STATE_CLASS: SensorStateClass.TOTAL, }, hc.SENSOR_TOTAL_START_TIME: {ICON: "mdi:progress-clock"}, hc.SENSOR_TVOC: {ICON: "mdi:air-filter"}, diff --git a/requirements_all.txt b/requirements_all.txt index daa9ae78610..ca5a28b5cb2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -792,7 +792,7 @@ hass-nabucasa==0.54.0 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.4.0 +hatasmota==0.4.1 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9975c08b5df..326eb00e596 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -562,7 +562,7 @@ hangups==0.4.18 hass-nabucasa==0.54.0 # homeassistant.components.tasmota -hatasmota==0.4.0 +hatasmota==0.4.1 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/tests/components/tasmota/test_sensor.py b/tests/components/tasmota/test_sensor.py index 8e810c82a43..b27a9b8ca49 100644 --- a/tests/components/tasmota/test_sensor.py +++ b/tests/components/tasmota/test_sensor.py @@ -284,7 +284,7 @@ async def test_indexed_sensor_state_via_mqtt2(hass, mqtt_mock, setup_tasmota): state = hass.states.get("sensor.tasmota_energy_total") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) - assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL_INCREASING + assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") await hass.async_block_till_done() @@ -333,7 +333,7 @@ async def test_indexed_sensor_state_via_mqtt3(hass, mqtt_mock, setup_tasmota): state = hass.states.get("sensor.tasmota_energy_total_1") assert state.state == "unavailable" assert not state.attributes.get(ATTR_ASSUMED_STATE) - assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL_INCREASING + assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online") await hass.async_block_till_done() From b4a0345b38b9a6cde0d44c7a3e94f81ab693e74d Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 27 Apr 2022 13:55:31 -0400 Subject: [PATCH 013/930] Bump ZHA dependencies (#70900) * Bump ZHA libs * bump Zigpy --- homeassistant/components/zha/manifest.json | 6 +++--- requirements_all.txt | 6 +++--- requirements_test_all.txt | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 3704e9715fb..8befa039382 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,9 +7,9 @@ "bellows==0.29.0", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.72", - "zigpy-deconz==0.14.0", - "zigpy==0.44.2", + "zha-quirks==0.0.73", + "zigpy-deconz==0.16.0", + "zigpy==0.45.1", "zigpy-xbee==0.14.0", "zigpy-zigate==0.7.4", "zigpy-znp==0.7.0" diff --git a/requirements_all.txt b/requirements_all.txt index ca5a28b5cb2..c76ee52cff7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2480,7 +2480,7 @@ zengge==0.2 zeroconf==0.38.4 # homeassistant.components.zha -zha-quirks==0.0.72 +zha-quirks==0.0.73 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 @@ -2489,7 +2489,7 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.14.0 +zigpy-deconz==0.16.0 # homeassistant.components.zha zigpy-xbee==0.14.0 @@ -2501,7 +2501,7 @@ zigpy-zigate==0.7.4 zigpy-znp==0.7.0 # homeassistant.components.zha -zigpy==0.44.2 +zigpy==0.45.1 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 326eb00e596..bc1eb01a3d2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1620,10 +1620,10 @@ youless-api==0.16 zeroconf==0.38.4 # homeassistant.components.zha -zha-quirks==0.0.72 +zha-quirks==0.0.73 # homeassistant.components.zha -zigpy-deconz==0.14.0 +zigpy-deconz==0.16.0 # homeassistant.components.zha zigpy-xbee==0.14.0 @@ -1635,7 +1635,7 @@ zigpy-zigate==0.7.4 zigpy-znp==0.7.0 # homeassistant.components.zha -zigpy==0.44.2 +zigpy==0.45.1 # homeassistant.components.zwave_js zwave-js-server-python==0.36.0 From 9a3908d21dc87f7a40a641399aaae3faab973f2d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 27 Apr 2022 22:26:56 +0200 Subject: [PATCH 014/930] Improve typing [util.logging] (#70894) --- .strict-typing | 1 + homeassistant/util/logging.py | 38 ++++++++++++++++++++--------------- mypy.ini | 3 +++ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/.strict-typing b/.strict-typing index d907421d50e..bb2a163f40d 100644 --- a/.strict-typing +++ b/.strict-typing @@ -26,6 +26,7 @@ homeassistant.util.async_ homeassistant.util.color homeassistant.util.decorator homeassistant.util.location +homeassistant.util.logging homeassistant.util.process homeassistant.util.unit_system diff --git a/homeassistant/util/logging.py b/homeassistant/util/logging.py index d09feec5237..6c4a55f51f2 100644 --- a/homeassistant/util/logging.py +++ b/homeassistant/util/logging.py @@ -2,18 +2,20 @@ from __future__ import annotations import asyncio -from collections.abc import Awaitable, Callable, Coroutine +from collections.abc import Callable, Coroutine from functools import partial, wraps import inspect import logging import logging.handlers import queue import traceback -from typing import Any, cast, overload +from typing import Any, TypeVar, cast, overload from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE from homeassistant.core import HomeAssistant, callback, is_callback +_T = TypeVar("_T") + class HideSensitiveDataFilter(logging.Filter): """Filter API password calls.""" @@ -115,22 +117,24 @@ def log_exception(format_err: Callable[..., Any], *args: Any) -> None: @overload -def catch_log_exception( # type: ignore[misc] - func: Callable[..., Awaitable[Any]], format_err: Callable[..., Any], *args: Any -) -> Callable[..., Awaitable[None]]: - """Overload for Callables that return an Awaitable.""" +def catch_log_exception( + func: Callable[..., Coroutine[Any, Any, Any]], + format_err: Callable[..., Any], + *args: Any, +) -> Callable[..., Coroutine[Any, Any, None]]: + """Overload for Callables that return a Coroutine.""" @overload def catch_log_exception( func: Callable[..., Any], format_err: Callable[..., Any], *args: Any -) -> Callable[..., None]: +) -> Callable[..., None | Coroutine[Any, Any, None]]: """Overload for Callables that return Any.""" def catch_log_exception( func: Callable[..., Any], format_err: Callable[..., Any], *args: Any -) -> Callable[..., None] | Callable[..., Awaitable[None]]: +) -> Callable[..., None | Coroutine[Any, Any, None]]: """Decorate a callback to catch and log exceptions.""" # Check for partials to properly determine if coroutine function @@ -138,9 +142,9 @@ def catch_log_exception( while isinstance(check_func, partial): check_func = check_func.func - wrapper_func: Callable[..., None] | Callable[..., Awaitable[None]] + wrapper_func: Callable[..., None | Coroutine[Any, Any, None]] if asyncio.iscoroutinefunction(check_func): - async_func = cast(Callable[..., Awaitable[None]], func) + async_func = cast(Callable[..., Coroutine[Any, Any, None]], func) @wraps(async_func) async def async_wrapper(*args: Any) -> None: @@ -170,11 +174,11 @@ def catch_log_exception( def catch_log_coro_exception( - target: Coroutine[Any, Any, Any], format_err: Callable[..., Any], *args: Any -) -> Coroutine[Any, Any, Any]: + target: Coroutine[Any, Any, _T], format_err: Callable[..., Any], *args: Any +) -> Coroutine[Any, Any, _T | None]: """Decorate a coroutine to catch and log exceptions.""" - async def coro_wrapper(*args: Any) -> Any: + async def coro_wrapper(*args: Any) -> _T | None: """Catch and log exception.""" try: return await target @@ -182,10 +186,12 @@ def catch_log_coro_exception( log_exception(format_err, *args) return None - return coro_wrapper() + return coro_wrapper(*args) -def async_create_catching_coro(target: Coroutine) -> Coroutine: +def async_create_catching_coro( + target: Coroutine[Any, Any, _T] +) -> Coroutine[Any, Any, _T | None]: """Wrap a coroutine to catch and log exceptions. The exception will be logged together with a stacktrace of where the @@ -196,7 +202,7 @@ def async_create_catching_coro(target: Coroutine) -> Coroutine: trace = traceback.extract_stack() wrapped_target = catch_log_coro_exception( target, - lambda *args: "Exception in {} called from\n {}".format( + lambda: "Exception in {} called from\n {}".format( target.__name__, "".join(traceback.format_list(trace[:-1])), ), diff --git a/mypy.ini b/mypy.ini index 165bca681ff..96228c5c93e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -89,6 +89,9 @@ disallow_any_generics = true [mypy-homeassistant.util.location] disallow_any_generics = true +[mypy-homeassistant.util.logging] +disallow_any_generics = true + [mypy-homeassistant.util.process] disallow_any_generics = true From 2c028d203fddd3e6b54e7ebeda99f832aed20520 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Wed, 27 Apr 2022 23:08:18 +0100 Subject: [PATCH 015/930] Remove invalid unique id from generic camera (#70568) Co-authored-by: J. Nick Koston --- homeassistant/components/generic/__init__.py | 22 +++++++++++- homeassistant/components/generic/camera.py | 2 +- .../components/generic/config_flow.py | 2 -- tests/components/generic/test_config_flow.py | 34 +++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/generic/__init__.py b/homeassistant/components/generic/__init__.py index f243f1639b3..cb669d8b906 100644 --- a/homeassistant/components/generic/__init__.py +++ b/homeassistant/components/generic/__init__.py @@ -1,8 +1,12 @@ """The generic component.""" +from __future__ import annotations + +from typing import Any from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er DOMAIN = "generic" PLATFORMS = [Platform.CAMERA] @@ -13,9 +17,25 @@ async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> Non await hass.config_entries.async_reload(entry.entry_id) +async def _async_migrate_unique_ids(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Migrate entities to the new unique id.""" + + @callback + def _async_migrator(entity_entry: er.RegistryEntry) -> dict[str, Any] | None: + if entity_entry.unique_id == entry.entry_id: + # Already correct, nothing to do + return None + # There is only one entity, and its unique id + # should always be the same as the config entry entry_id + return {"new_unique_id": entry.entry_id} + + await er.async_migrate_entries(hass, entry.entry_id, _async_migrator) + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up generic IP camera from a config entry.""" + await _async_migrate_unique_ids(hass, entry) hass.config_entries.async_setup_platforms(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(_async_update_listener)) diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index e83cb0df0aa..d20032e2607 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -109,7 +109,7 @@ async def async_setup_entry( """Set up a generic IP Camera.""" async_add_entities( - [GenericCamera(hass, entry.options, entry.unique_id, entry.title)] + [GenericCamera(hass, entry.options, entry.entry_id, entry.title)] ) diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 3d15069bca8..4298b473681 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -274,7 +274,6 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN): # is always jpeg user_input[CONF_CONTENT_TYPE] = "image/jpeg" - await self.async_set_unique_id(self.flow_id) return self.async_create_entry( title=name, data={}, options=user_input ) @@ -302,7 +301,6 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN): import_config[CONF_LIMIT_REFETCH_TO_URL_CHANGE] = False still_format = import_config.get(CONF_CONTENT_TYPE, "image/jpeg") import_config[CONF_CONTENT_TYPE] = still_format - await self.async_set_unique_id(self.flow_id) return self.async_create_entry(title=name, data={}, options=import_config) diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index 3caa1aa7adf..f5411ed3ea0 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -28,6 +28,7 @@ from homeassistant.const import ( CONF_VERIFY_SSL, HTTP_BASIC_AUTHENTICATION, ) +from homeassistant.helpers import entity_registry from tests.common import MockConfigEntry @@ -577,3 +578,36 @@ async def test_reload_on_title_change(hass) -> None: await hass.async_block_till_done() assert hass.states.get("camera.my_title").attributes["friendly_name"] == "New Title" + + +async def test_migrate_existing_ids(hass) -> None: + """Test that existing ids are migrated for issue #70568.""" + + registry = entity_registry.async_get(hass) + + test_data = TESTDATA_OPTIONS.copy() + test_data[CONF_CONTENT_TYPE] = "image/png" + old_unique_id = "54321" + entity_id = "camera.sample_camera" + + mock_entry = MockConfigEntry( + domain=DOMAIN, unique_id=old_unique_id, options=test_data, title="My Title" + ) + new_unique_id = mock_entry.entry_id + mock_entry.add_to_hass(hass) + + entity_entry = registry.async_get_or_create( + "camera", + DOMAIN, + old_unique_id, + suggested_object_id="sample camera", + config_entry=mock_entry, + ) + assert entity_entry.entity_id == entity_id + assert entity_entry.unique_id == old_unique_id + + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + entity_entry = registry.async_get(entity_id) + assert entity_entry.unique_id == new_unique_id From c8c27dba0f569b23b94fc3e8b7d8b62898866ee6 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Thu, 28 Apr 2022 00:17:56 +0200 Subject: [PATCH 016/930] Remove conditional logic in AndroidTV tests (#70944) --- tests/components/androidtv/test_media_player.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/components/androidtv/test_media_player.py b/tests/components/androidtv/test_media_player.py index 7496f973631..aa27ee8f564 100644 --- a/tests/components/androidtv/test_media_player.py +++ b/tests/components/androidtv/test_media_player.py @@ -74,6 +74,8 @@ from tests.common import MockConfigEntry CONF_OPTIONS = "options" HOST = "127.0.0.1" + +ADB_PATCH_KEY = "patch_key" TEST_ENTITY_NAME = "entity_name" PATCH_ACCESS = patch("homeassistant.components.androidtv.os.access", return_value=True) @@ -86,6 +88,7 @@ SHELL_RESPONSE_STANDBY = "1" # Android TV device with Python ADB implementation CONFIG_ANDROIDTV_PYTHON_ADB = { + ADB_PATCH_KEY: patchers.KEY_PYTHON, TEST_ENTITY_NAME: f"{PREFIX_ANDROIDTV} {HOST}", DOMAIN: { CONF_HOST: HOST, @@ -96,6 +99,7 @@ CONFIG_ANDROIDTV_PYTHON_ADB = { # Android TV device with Python ADB implementation imported from YAML CONFIG_ANDROIDTV_PYTHON_ADB_YAML = { + ADB_PATCH_KEY: patchers.KEY_PYTHON, TEST_ENTITY_NAME: "ADB yaml import", DOMAIN: { CONF_NAME: "ADB yaml import", @@ -105,6 +109,7 @@ CONFIG_ANDROIDTV_PYTHON_ADB_YAML = { # Android TV device with ADB server CONFIG_ANDROIDTV_ADB_SERVER = { + ADB_PATCH_KEY: patchers.KEY_SERVER, TEST_ENTITY_NAME: f"{PREFIX_ANDROIDTV} {HOST}", DOMAIN: { CONF_HOST: HOST, @@ -117,6 +122,7 @@ CONFIG_ANDROIDTV_ADB_SERVER = { # Fire TV device with Python ADB implementation CONFIG_FIRETV_PYTHON_ADB = { + ADB_PATCH_KEY: patchers.KEY_PYTHON, TEST_ENTITY_NAME: f"{PREFIX_FIRETV} {HOST}", DOMAIN: { CONF_HOST: HOST, @@ -127,6 +133,7 @@ CONFIG_FIRETV_PYTHON_ADB = { # Fire TV device with ADB server CONFIG_FIRETV_ADB_SERVER = { + ADB_PATCH_KEY: patchers.KEY_SERVER, TEST_ENTITY_NAME: f"{PREFIX_FIRETV} {HOST}", DOMAIN: { CONF_HOST: HOST, @@ -140,7 +147,7 @@ CONFIG_FIRETV_ADB_SERVER = { def _setup(config): """Perform common setup tasks for the tests.""" - patch_key = "server" if CONF_ADB_SERVER_IP in config[DOMAIN] else "python" + patch_key = config[ADB_PATCH_KEY] entity_id = f"{MP_DOMAIN}.{slugify(config[TEST_ENTITY_NAME])}" config_entry = MockConfigEntry( domain=DOMAIN, From 27a5851ee2454bdb1d096711e1327ba01d85f0aa Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 27 Apr 2022 20:52:32 -0400 Subject: [PATCH 017/930] Fix flaky ZHA tests (#70956) --- tests/components/zha/test_gateway.py | 11 ++++++----- tests/components/zha/test_select.py | 6 +++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/components/zha/test_gateway.py b/tests/components/zha/test_gateway.py index a263a6d7ed3..67ce6481542 100644 --- a/tests/components/zha/test_gateway.py +++ b/tests/components/zha/test_gateway.py @@ -1,5 +1,6 @@ """Test ZHA Gateway.""" import asyncio +import math import time from unittest.mock import patch @@ -207,14 +208,14 @@ async def test_updating_device_store(hass, zigpy_dev_basic, zha_dev_basic): assert zha_dev_basic.last_seen is not None entry = zha_gateway.zha_storage.async_get_or_create_device(zha_dev_basic) - assert entry.last_seen == zha_dev_basic.last_seen + assert math.isclose(entry.last_seen, zha_dev_basic.last_seen, rel_tol=1e-06) assert zha_dev_basic.last_seen is not None last_seen = zha_dev_basic.last_seen # test that we can't set None as last seen any more zha_dev_basic.async_update_last_seen(None) - assert last_seen == zha_dev_basic.last_seen + assert math.isclose(last_seen, zha_dev_basic.last_seen, rel_tol=1e-06) # test that we won't put None in storage zigpy_dev_basic.last_seen = None @@ -222,18 +223,18 @@ async def test_updating_device_store(hass, zigpy_dev_basic, zha_dev_basic): await zha_gateway.async_update_device_storage() await hass.async_block_till_done() entry = zha_gateway.zha_storage.async_get_or_create_device(zha_dev_basic) - assert entry.last_seen == last_seen + assert math.isclose(entry.last_seen, last_seen, rel_tol=1e-06) # test that we can still set a good last_seen last_seen = time.time() zha_dev_basic.async_update_last_seen(last_seen) - assert last_seen == zha_dev_basic.last_seen + assert math.isclose(last_seen, zha_dev_basic.last_seen, rel_tol=1e-06) # test that we still put good values in storage await zha_gateway.async_update_device_storage() await hass.async_block_till_done() entry = zha_gateway.zha_storage.async_get_or_create_device(zha_dev_basic) - assert entry.last_seen == last_seen + assert math.isclose(entry.last_seen, last_seen, rel_tol=1e-06) async def test_cleaning_up_storage(hass, zigpy_dev_basic, zha_dev_basic, hass_storage): diff --git a/tests/components/zha/test_select.py b/tests/components/zha/test_select.py index a761b8ea36b..452939420bf 100644 --- a/tests/components/zha/test_select.py +++ b/tests/components/zha/test_select.py @@ -193,7 +193,11 @@ async def test_on_off_select(hass, light, zha_device_joined_restored): state = hass.states.get(entity_id) assert state - assert state.state == STATE_UNKNOWN + if zha_device_joined_restored.name == "zha_device_joined": + assert state.state == general.OnOff.StartUpOnOff.On.name + else: + assert state.state == STATE_UNKNOWN + assert state.attributes["options"] == ["Off", "On", "Toggle", "PreviousValue"] entity_entry = entity_registry.async_get(entity_id) From 93cbb331e5212383398a5d4a0f6314bf70a8e760 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 03:49:54 +0200 Subject: [PATCH 018/930] Update Mypy to 0.950 (#70948) Co-authored-by: J. Nick Koston --- homeassistant/components/dlna_dmr/media_player.py | 6 +++--- homeassistant/components/evil_genius_labs/util.py | 6 +++--- homeassistant/components/http/auth.py | 2 +- homeassistant/components/http/ban.py | 6 +++--- homeassistant/components/plugwise/util.py | 4 ++-- homeassistant/components/roku/helpers.py | 4 ++-- homeassistant/components/sentry/__init__.py | 3 ++- homeassistant/components/sonarr/sensor.py | 4 ++-- homeassistant/components/sonos/helpers.py | 12 +++++------- homeassistant/components/sonos/media.py | 2 +- homeassistant/components/tplink/entity.py | 4 ++-- homeassistant/components/vlc_telnet/media_player.py | 4 ++-- homeassistant/components/webhook/__init__.py | 2 +- homeassistant/components/webostv/media_player.py | 4 ++-- homeassistant/components/zabbix/__init__.py | 6 +++++- homeassistant/helpers/event.py | 4 ++-- homeassistant/helpers/integration_platform.py | 2 +- homeassistant/runner.py | 4 ++-- mypy.ini | 1 + requirements_test.txt | 2 +- script/hassfest/mypy_config.py | 2 ++ 21 files changed, 45 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 101b59c7125..807a983983b 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -67,8 +67,8 @@ _P = ParamSpec("_P") def catch_request_errors( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R | None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[_R]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R | None]]: """Catch UpnpError errors.""" @functools.wraps(func) @@ -80,7 +80,7 @@ def catch_request_errors( ) return None try: - return await func(self, *args, **kwargs) # type: ignore[no-any-return] # mypy can't yet infer 'func' + return await func(self, *args, **kwargs) except UpnpError as err: self.check_available = True _LOGGER.error("Error during call %s: %r", func.__name__, err) diff --git a/homeassistant/components/evil_genius_labs/util.py b/homeassistant/components/evil_genius_labs/util.py index 40ba375a2b4..7cbcc821bfe 100644 --- a/homeassistant/components/evil_genius_labs/util.py +++ b/homeassistant/components/evil_genius_labs/util.py @@ -15,8 +15,8 @@ _P = ParamSpec("_P") def update_when_done( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[_R]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: """Decorate function to trigger update when function is done.""" @wraps(func) @@ -24,6 +24,6 @@ def update_when_done( """Wrap function.""" result = await func(self, *args, **kwargs) await self.coordinator.async_request_refresh() - return result # type: ignore[no-any-return] # mypy can't yet infer 'func' + return result return wrapper diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 117d1b2d92e..4788680a6fa 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -96,7 +96,7 @@ def async_user_not_allowed_do_auth( return "User is local only" try: - remote = ip_address(request.remote) + remote = ip_address(request.remote) # type: ignore[arg-type] except ValueError: return "Invalid remote IP" diff --git a/homeassistant/components/http/ban.py b/homeassistant/components/http/ban.py index 292c46e55f9..620bdc7613c 100644 --- a/homeassistant/components/http/ban.py +++ b/homeassistant/components/http/ban.py @@ -67,7 +67,7 @@ async def ban_middleware( return await handler(request) # Verify if IP is not banned - ip_address_ = ip_address(request.remote) + ip_address_ = ip_address(request.remote) # type: ignore[arg-type] is_banned = any( ip_ban.ip_address == ip_address_ for ip_ban in request.app[KEY_BANNED_IPS] ) @@ -107,7 +107,7 @@ async def process_wrong_login(request: Request) -> None: """ hass = request.app["hass"] - remote_addr = ip_address(request.remote) + remote_addr = ip_address(request.remote) # type: ignore[arg-type] remote_host = request.remote with suppress(herror): remote_host, _, _ = await hass.async_add_executor_job( @@ -170,7 +170,7 @@ async def process_success_login(request: Request) -> None: No release IP address from banned list function, it can only be done by manual modify ip bans config file. """ - remote_addr = ip_address(request.remote) + remote_addr = ip_address(request.remote) # type: ignore[arg-type] # Check if ban middleware is loaded if KEY_BANNED_IPS not in request.app or request.app[KEY_LOGIN_THRESHOLD] < 1: diff --git a/homeassistant/components/plugwise/util.py b/homeassistant/components/plugwise/util.py index 58c7715815e..769bfc4ecea 100644 --- a/homeassistant/components/plugwise/util.py +++ b/homeassistant/components/plugwise/util.py @@ -15,8 +15,8 @@ _T = TypeVar("_T", bound=PlugwiseEntity) def plugwise_command( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[_R]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: """Decorate Plugwise calls that send commands/make changes to the device. A decorator that wraps the passed in function, catches Plugwise errors, diff --git a/homeassistant/components/roku/helpers.py b/homeassistant/components/roku/helpers.py index 26fdb53c935..d7e28066f90 100644 --- a/homeassistant/components/roku/helpers.py +++ b/homeassistant/components/roku/helpers.py @@ -29,8 +29,8 @@ def roku_exception_handler(ignore_timeout: bool = False) -> Callable[..., Callab """Decorate Roku calls to handle Roku exceptions.""" def decorator( - func: Callable[Concatenate[_T, _P], Awaitable[None]], # type: ignore[misc] - ) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[None]], + ) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: @wraps(func) async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: try: diff --git a/homeassistant/components/sentry/__init__.py b/homeassistant/components/sentry/__init__.py index 7ea26c04810..c02b16fcfb0 100644 --- a/homeassistant/components/sentry/__init__.py +++ b/homeassistant/components/sentry/__init__.py @@ -79,7 +79,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ), } - sentry_sdk.init( # pylint: disable=abstract-class-instantiated + # pylint: disable-next=abstract-class-instantiated + sentry_sdk.init( # type: ignore[abstract] dsn=entry.data[CONF_DSN], environment=entry.options.get(CONF_ENVIRONMENT), integrations=[sentry_logging, AioHttpIntegration(), SqlalchemyIntegration()], diff --git a/homeassistant/components/sonarr/sensor.py b/homeassistant/components/sonarr/sensor.py index c182bb2bbeb..1604f500ab9 100644 --- a/homeassistant/components/sonarr/sensor.py +++ b/homeassistant/components/sonarr/sensor.py @@ -109,8 +109,8 @@ async def async_setup_entry( def sonarr_exception_handler( - func: Callable[Concatenate[_T, _P], Awaitable[None]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[None]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: """Decorate Sonarr calls to handle Sonarr exceptions. A decorator that wraps the passed in function, catches Sonarr errors, diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index 3edf23f0c3c..22d9f6c08a5 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -35,16 +35,14 @@ _P = ParamSpec("_P") @overload def soco_error( errorcodes: None = ..., -) -> Callable[ # type: ignore[misc] - [Callable[Concatenate[_T, _P], _R]], Callable[Concatenate[_T, _P], _R] -]: +) -> Callable[[Callable[Concatenate[_T, _P], _R]], Callable[Concatenate[_T, _P], _R]]: ... @overload def soco_error( errorcodes: list[str], -) -> Callable[ # type: ignore[misc] +) -> Callable[ [Callable[Concatenate[_T, _P], _R]], Callable[Concatenate[_T, _P], _R | None] ]: ... @@ -52,14 +50,14 @@ def soco_error( def soco_error( errorcodes: list[str] | None = None, -) -> Callable[ # type: ignore[misc] +) -> Callable[ [Callable[Concatenate[_T, _P], _R]], Callable[Concatenate[_T, _P], _R | None] ]: """Filter out specified UPnP errors and raise exceptions for service calls.""" def decorator( - funct: Callable[Concatenate[_T, _P], _R] # type: ignore[misc] - ) -> Callable[Concatenate[_T, _P], _R | None]: # type: ignore[misc] + funct: Callable[Concatenate[_T, _P], _R] + ) -> Callable[Concatenate[_T, _P], _R | None]: """Decorate functions.""" def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R | None: diff --git a/homeassistant/components/sonos/media.py b/homeassistant/components/sonos/media.py index 1b4dbd00d59..e661a8320dc 100644 --- a/homeassistant/components/sonos/media.py +++ b/homeassistant/components/sonos/media.py @@ -135,7 +135,7 @@ class SonosMedia: self.title = track_info.get("title") self.image_url = track_info.get("album_art") - playlist_position = int(track_info.get("playlist_position")) + playlist_position = int(track_info.get("playlist_position", -1)) if playlist_position > 0: self.queue_position = playlist_position diff --git a/homeassistant/components/tplink/entity.py b/homeassistant/components/tplink/entity.py index 173d1d7930f..471d32631c4 100644 --- a/homeassistant/components/tplink/entity.py +++ b/homeassistant/components/tplink/entity.py @@ -19,8 +19,8 @@ _P = ParamSpec("_P") def async_refresh_after( - func: Callable[Concatenate[_T, _P], Awaitable[None]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[None]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: """Define a wrapper to refresh after.""" async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: diff --git a/homeassistant/components/vlc_telnet/media_player.py b/homeassistant/components/vlc_telnet/media_player.py index 9d4e0a5a7a8..c7009c6c14d 100644 --- a/homeassistant/components/vlc_telnet/media_player.py +++ b/homeassistant/components/vlc_telnet/media_player.py @@ -48,8 +48,8 @@ async def async_setup_entry( def catch_vlc_errors( - func: Callable[Concatenate[_T, _P], Awaitable[None]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[None]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: """Catch VLC errors.""" @wraps(func) diff --git a/homeassistant/components/webhook/__init__.py b/homeassistant/components/webhook/__init__.py index 798f863b8ee..fb9927f1b37 100644 --- a/homeassistant/components/webhook/__init__.py +++ b/homeassistant/components/webhook/__init__.py @@ -108,7 +108,7 @@ async def async_handle_webhook( if webhook["local_only"]: try: - remote = ip_address(request.remote) + remote = ip_address(request.remote) # type: ignore[arg-type] except ValueError: _LOGGER.debug("Unable to parse remote ip %s", request.remote) return Response(status=HTTPStatus.OK) diff --git a/homeassistant/components/webostv/media_player.py b/homeassistant/components/webostv/media_player.py index 49f9a29052b..3806ee6c2bb 100644 --- a/homeassistant/components/webostv/media_player.py +++ b/homeassistant/components/webostv/media_player.py @@ -87,8 +87,8 @@ _P = ParamSpec("_P") def cmd( - func: Callable[Concatenate[_T, _P], Awaitable[None]] # type: ignore[misc] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: # type: ignore[misc] + func: Callable[Concatenate[_T, _P], Awaitable[None]] +) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: """Catch command exceptions.""" @wraps(func) diff --git a/homeassistant/components/zabbix/__init__.py b/homeassistant/components/zabbix/__init__.py index 21c3edd56bf..3fc38af4cf1 100644 --- a/homeassistant/components/zabbix/__init__.py +++ b/homeassistant/components/zabbix/__init__.py @@ -90,7 +90,11 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.error("HTTPError when connecting to Zabbix API: %s", http_error) zapi = None _LOGGER.error(RETRY_MESSAGE, http_error) - event_helper.call_later(hass, RETRY_INTERVAL, lambda _: setup(hass, config)) + event_helper.call_later( + hass, + RETRY_INTERVAL, + lambda _: setup(hass, config), # type: ignore[arg-type,return-value] + ) return True hass.data[DOMAIN] = zapi diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 427297b2f1d..686fde89fbb 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -111,8 +111,8 @@ class TrackTemplateResult: def threaded_listener_factory( - async_factory: Callable[Concatenate[HomeAssistant, _P], Any] # type: ignore[misc] -) -> Callable[Concatenate[HomeAssistant, _P], CALLBACK_TYPE]: # type: ignore[misc] + async_factory: Callable[Concatenate[HomeAssistant, _P], Any] +) -> Callable[Concatenate[HomeAssistant, _P], CALLBACK_TYPE]: """Convert an async event helper to a threaded one.""" @ft.wraps(async_factory) diff --git a/homeassistant/helpers/integration_platform.py b/homeassistant/helpers/integration_platform.py index 21d15e4fc73..9255824cddf 100644 --- a/homeassistant/helpers/integration_platform.py +++ b/homeassistant/helpers/integration_platform.py @@ -48,7 +48,7 @@ async def _async_process_single_integration_platform_component( return try: - await integration_platform.process_platform(hass, component_name, platform) # type: ignore[misc,operator] # https://github.com/python/mypy/issues/5485 + await integration_platform.process_platform(hass, component_name, platform) except Exception: # pylint: disable=broad-except _LOGGER.exception( "Error processing platform %s.%s", component_name, platform_name diff --git a/homeassistant/runner.py b/homeassistant/runner.py index 472d399713d..5788a6b155a 100644 --- a/homeassistant/runner.py +++ b/homeassistant/runner.py @@ -48,7 +48,7 @@ class RuntimeConfig: open_ui: bool = False -class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[valid-type,misc] +class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): """Event loop policy for Home Assistant.""" def __init__(self, debug: bool) -> None: @@ -59,7 +59,7 @@ class HassEventLoopPolicy(asyncio.DefaultEventLoopPolicy): # type: ignore[valid @property def loop_name(self) -> str: """Return name of the loop.""" - return self._loop_factory.__name__ # type: ignore[no-any-return] + return self._loop_factory.__name__ # type: ignore[no-any-return,attr-defined] def new_event_loop(self) -> asyncio.AbstractEventLoop: """Get the event loop.""" diff --git a/mypy.ini b/mypy.ini index 96228c5c93e..5df02c46252 100644 --- a/mypy.ini +++ b/mypy.ini @@ -13,6 +13,7 @@ warn_redundant_casts = true warn_unused_configs = true warn_unused_ignores = true enable_error_code = ignore-without-code +strict_concatenate = false check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true diff --git a/requirements_test.txt b/requirements_test.txt index 31a87356fcc..744ca7bebcf 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -11,7 +11,7 @@ codecov==2.1.12 coverage==6.3.2 freezegun==1.2.1 mock-open==1.4.0 -mypy==0.942 +mypy==0.950 pre-commit==2.17.0 pylint==2.13.7 pipdeptree==2.2.1 diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index cf3ef92fbdb..d21dc0faf7d 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -221,6 +221,8 @@ GENERAL_SETTINGS: Final[dict[str, str]] = { "warn_unused_configs": "true", "warn_unused_ignores": "true", "enable_error_code": "ignore-without-code", + # Strict_concatenate breaks passthrough ParamSpec typing + "strict_concatenate": "false", } # This is basically the list of checks which is enabled for "strict=true". From 2270b7df8dc5152c81f1c4dea7eaa95a6aee6067 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 27 Apr 2022 21:16:50 -0700 Subject: [PATCH 019/930] Set nest climate min/max temp range (#70960) --- homeassistant/components/nest/climate_sdm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/nest/climate_sdm.py b/homeassistant/components/nest/climate_sdm.py index 26fb88482ee..89048b9f624 100644 --- a/homeassistant/components/nest/climate_sdm.py +++ b/homeassistant/components/nest/climate_sdm.py @@ -71,6 +71,8 @@ FAN_MODE_MAP = { FAN_INV_MODE_MAP = {v: k for k, v in FAN_MODE_MAP.items()} MAX_FAN_DURATION = 43200 # 15 hours is the max in the SDM API +MIN_TEMP = 10 +MAX_TEMP = 32 async def async_setup_sdm_entry( @@ -94,6 +96,9 @@ async def async_setup_sdm_entry( class ThermostatEntity(ClimateEntity): """A nest thermostat climate entity.""" + _attr_min_temp = MIN_TEMP + _attr_max_temp = MAX_TEMP + def __init__(self, device: Device) -> None: """Initialize ThermostatEntity.""" self._device = device From 474087bf7d121b010a9103a719255a058444c730 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 27 Apr 2022 21:17:25 -0700 Subject: [PATCH 020/930] Bump gcal_sync 0.6.3 to fix calendar path encoding bug (#70959) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 8af1fa00437..0e9a2fe7ddb 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["auth"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.6.2", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.6.3", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index c76ee52cff7..df74fa12364 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.6.2 +gcal-sync==0.6.3 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bc1eb01a3d2..ec628688ad0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -486,7 +486,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.6.2 +gcal-sync==0.6.3 # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 From 66440508116975f0441166019757b54e5a55e967 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 27 Apr 2022 18:18:32 -1000 Subject: [PATCH 021/930] Add discovery support for polisy to isy994 (#70940) --- .../components/isy994/config_flow.py | 5 +- homeassistant/components/isy994/manifest.json | 3 +- homeassistant/generated/dhcp.py | 1 + tests/components/isy994/test_config_flow.py | 55 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/isy994/config_flow.py b/homeassistant/components/isy994/config_flow.py index 866ec800402..dea4bce4eeb 100644 --- a/homeassistant/components/isy994/config_flow.py +++ b/homeassistant/components/isy994/config_flow.py @@ -203,7 +203,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> data_entry_flow.FlowResult: """Handle a discovered isy994 via dhcp.""" friendly_name = discovery_info.hostname - url = f"http://{discovery_info.ip}" + if friendly_name.startswith("polisy"): + url = f"http://{discovery_info.ip}:8080" + else: + url = f"http://{discovery_info.ip}" mac = discovery_info.macaddress isy_mac = ( f"{mac[0:2]}:{mac[2:4]}:{mac[4:6]}:{mac[6:8]}:{mac[8:10]}:{mac[10:12]}" diff --git a/homeassistant/components/isy994/manifest.json b/homeassistant/components/isy994/manifest.json index d92226a4277..d131a150fb3 100644 --- a/homeassistant/components/isy994/manifest.json +++ b/homeassistant/components/isy994/manifest.json @@ -13,7 +13,8 @@ ], "dhcp": [ { "registered_devices": true }, - { "hostname": "isy*", "macaddress": "0021B9*" } + { "hostname": "isy*", "macaddress": "0021B9*" }, + { "hostname": "polisy*", "macaddress": "000DB9*" } ], "iot_class": "local_push", "loggers": ["pyisy"] diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index d0ffb9b097a..4ebe47ded17 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -54,6 +54,7 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'intellifire', 'hostname': 'zentrios-*'}, {'domain': 'isy994', 'registered_devices': True}, {'domain': 'isy994', 'hostname': 'isy*', 'macaddress': '0021B9*'}, + {'domain': 'isy994', 'hostname': 'polisy*', 'macaddress': '000DB9*'}, {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '48A2E6*'}, {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': 'B82CA0*'}, {'domain': 'lyric', 'hostname': 'lyric-*', 'macaddress': '00D02D*'}, diff --git a/tests/components/isy994/test_config_flow.py b/tests/components/isy994/test_config_flow.py index b16f5c0070d..60e9fd964b6 100644 --- a/tests/components/isy994/test_config_flow.py +++ b/tests/components/isy994/test_config_flow.py @@ -44,6 +44,12 @@ MOCK_USER_INPUT = { CONF_PASSWORD: MOCK_PASSWORD, CONF_TLS_VER: MOCK_TLS_VERSION, } +MOCK_POLISY_USER_INPUT = { + CONF_HOST: f"http://{MOCK_HOSTNAME}:8080", + CONF_USERNAME: MOCK_USERNAME, + CONF_PASSWORD: MOCK_PASSWORD, + CONF_TLS_VER: MOCK_TLS_VERSION, +} MOCK_IMPORT_WITH_SSL = { CONF_HOST: f"https://{MOCK_HOSTNAME}", CONF_USERNAME: MOCK_USERNAME, @@ -69,6 +75,7 @@ MOCK_IMPORT_FULL_CONFIG = { MOCK_DEVICE_NAME = "Name of the device" MOCK_UUID = "ce:fb:72:31:b7:b9" MOCK_MAC = "cefb7231b7b9" +MOCK_POLISY_MAC = "000db9123456" MOCK_CONFIG_RESPONSE = """ @@ -95,6 +102,14 @@ PATCH_ASYNC_SETUP = f"{INTEGRATION}.async_setup" PATCH_ASYNC_SETUP_ENTRY = f"{INTEGRATION}.async_setup_entry" +def _get_schema_default(schema, key_name): + """Iterate schema to find a key.""" + for schema_key in schema: + if schema_key == key_name: + return schema_key.default() + raise KeyError(f"{key_name} not found in schema") + + async def test_form(hass: HomeAssistant): """Test we get the form.""" @@ -544,6 +559,46 @@ async def test_form_dhcp(hass: HomeAssistant): assert len(mock_setup_entry.mock_calls) == 1 +async def test_form_dhcp_with_polisy(hass: HomeAssistant): + """Test we can setup from dhcp with polisy.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_DHCP}, + data=dhcp.DhcpServiceInfo( + ip="1.2.3.4", + hostname="polisy", + macaddress=MOCK_POLISY_MAC, + ), + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + assert ( + _get_schema_default(result["data_schema"].schema, CONF_HOST) + == "http://1.2.3.4:8080" + ) + + with patch(PATCH_CONNECTION, return_value=MOCK_CONFIG_RESPONSE), patch( + PATCH_ASYNC_SETUP, return_value=True + ) as mock_setup, patch( + PATCH_ASYNC_SETUP_ENTRY, + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + MOCK_POLISY_USER_INPUT, + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == f"{MOCK_DEVICE_NAME} ({MOCK_HOSTNAME})" + assert result2["result"].unique_id == MOCK_UUID + assert result2["data"] == MOCK_POLISY_USER_INPUT + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + async def test_form_dhcp_existing_entry(hass: HomeAssistant): """Test we update the ip of an existing entry from dhcp.""" From 79c9d22893e9d2d6f1b19034a6f9dd3d88a74d4a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 27 Apr 2022 18:18:59 -1000 Subject: [PATCH 022/930] Add dhcp hostname of older ZJ series Magic Home bulbs to discovery (#70958) --- homeassistant/components/flux_led/manifest.json | 3 +++ homeassistant/generated/dhcp.py | 1 + 2 files changed, 4 insertions(+) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 1dac7b81d89..e28b55869c7 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -38,6 +38,9 @@ "macaddress": "8CCE4E*", "hostname": "lwip*" }, + { + "hostname": "hf-lpb100-zj*" + }, { "hostname": "zengge_[0-9a-f][0-9a-f]_*" }, diff --git a/homeassistant/generated/dhcp.py b/homeassistant/generated/dhcp.py index 4ebe47ded17..015d70e2939 100644 --- a/homeassistant/generated/dhcp.py +++ b/homeassistant/generated/dhcp.py @@ -36,6 +36,7 @@ DHCP: list[dict[str, str | bool]] = [ {'domain': 'flux_led', 'hostname': '[ba][lk]*', 'macaddress': 'B4E842*'}, {'domain': 'flux_led', 'hostname': '[hba][flk]*', 'macaddress': 'F0FE6B*'}, {'domain': 'flux_led', 'hostname': 'lwip*', 'macaddress': '8CCE4E*'}, + {'domain': 'flux_led', 'hostname': 'hf-lpb100-zj*'}, {'domain': 'flux_led', 'hostname': 'zengge_[0-9a-f][0-9a-f]_*'}, {'domain': 'flux_led', 'hostname': 'sta*', 'macaddress': 'C82E47*'}, {'domain': 'fronius', 'macaddress': '0003AC*'}, From 27a4a9eed48b9cd1482729fbd2ba41515ae4f75c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 27 Apr 2022 18:19:36 -1000 Subject: [PATCH 023/930] Adjust get_latest_short_term_statistics query to be postgresql compatible (#70953) --- homeassistant/components/recorder/statistics.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 0056a81fb60..1c993b32bb6 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -1136,16 +1136,20 @@ def get_latest_short_term_statistics( ] most_recent_statistic_row = ( session.query( - StatisticsShortTerm.id, - func.max(StatisticsShortTerm.start), + StatisticsShortTerm.metadata_id, + func.max(StatisticsShortTerm.start).label("start_max"), ) + .filter(StatisticsShortTerm.metadata_id.in_(metadata_ids)) .group_by(StatisticsShortTerm.metadata_id) - .having(StatisticsShortTerm.metadata_id.in_(metadata_ids)) ).subquery() stats = execute( session.query(*QUERY_STATISTICS_SHORT_TERM).join( most_recent_statistic_row, - StatisticsShortTerm.id == most_recent_statistic_row.c.id, + ( + StatisticsShortTerm.metadata_id # pylint: disable=comparison-with-callable + == most_recent_statistic_row.c.metadata_id + ) + & (StatisticsShortTerm.start == most_recent_statistic_row.c.start_max), ) ) if not stats: From 2e3e7f1e945d9e39ae1caa2ca307ce481f18c455 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 27 Apr 2022 22:32:13 -0700 Subject: [PATCH 024/930] Sync area changes to google (#70936) Co-authored-by: Martin Hjelmare --- .../components/cloud/google_config.py | 42 +++++++++++-- tests/components/cloud/test_google_config.py | 62 ++++++++++++++++++- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index e2b21ffc56d..8f190103e87 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -9,8 +9,8 @@ from hass_nabucasa.google_report_state import ErrorResponse from homeassistant.components.google_assistant.const import DOMAIN as GOOGLE_DOMAIN from homeassistant.components.google_assistant.helpers import AbstractConfig from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES -from homeassistant.core import CoreState, split_entity_id -from homeassistant.helpers import entity_registry as er, start +from homeassistant.core import CoreState, Event, callback, split_entity_id +from homeassistant.helpers import device_registry as dr, entity_registry as er, start from homeassistant.setup import async_setup_component from .const import ( @@ -103,6 +103,10 @@ class CloudGoogleConfig(AbstractConfig): er.EVENT_ENTITY_REGISTRY_UPDATED, self._handle_entity_registry_updated, ) + self.hass.bus.async_listen( + dr.EVENT_DEVICE_REGISTRY_UPDATED, + self._handle_device_registry_updated, + ) def should_expose(self, state): """If a state object should be exposed.""" @@ -217,9 +221,14 @@ class CloudGoogleConfig(AbstractConfig): self._cur_entity_prefs = prefs.google_entity_configs self._cur_default_expose = prefs.google_default_expose - async def _handle_entity_registry_updated(self, event): + @callback + def _handle_entity_registry_updated(self, event: Event) -> None: """Handle when entity registry updated.""" - if not self.enabled or not self._cloud.is_logged_in: + if ( + not self.enabled + or not self._cloud.is_logged_in + or self.hass.state != CoreState.running + ): return # Only consider entity registry updates if info relevant for Google has changed @@ -233,7 +242,30 @@ class CloudGoogleConfig(AbstractConfig): if not self._should_expose_entity_id(entity_id): return - if self.hass.state != CoreState.running: + self.async_schedule_google_sync_all() + + @callback + def _handle_device_registry_updated(self, event: Event) -> None: + """Handle when device registry updated.""" + if ( + not self.enabled + or not self._cloud.is_logged_in + or self.hass.state != CoreState.running + ): + return + + # Device registry is only used for area changes. All other changes are ignored. + if event.data["action"] != "update" or "area_id" not in event.data["changes"]: + return + + # Check if any exposed entity uses the device area + if not any( + entity_entry.area_id is None + and self._should_expose_entity_id(entity_entry.entity_id) + for entity_entry in er.async_entries_for_device( + er.async_get(self.hass), event.data["device_id"] + ) + ): return self.async_schedule_google_sync_all() diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 98674ff6c86..95746eb67ae 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -10,7 +10,7 @@ from homeassistant.components.cloud.google_config import CloudGoogleConfig from homeassistant.components.google_assistant import helpers as ga_helpers from homeassistant.const import EVENT_HOMEASSISTANT_STARTED from homeassistant.core import CoreState, State -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.entity import EntityCategory from homeassistant.util.dt import utcnow @@ -191,6 +191,66 @@ async def test_google_entity_registry_sync(hass, mock_cloud_login, cloud_prefs): assert len(mock_sync.mock_calls) == 3 +async def test_google_device_registry_sync(hass, mock_cloud_login, cloud_prefs): + """Test Google config responds to device registry.""" + config = CloudGoogleConfig( + hass, GACTIONS_SCHEMA({}), "mock-user-id", cloud_prefs, hass.data["cloud"] + ) + ent_reg = er.async_get(hass) + entity_entry = ent_reg.async_get_or_create( + "light", "hue", "1234", device_id="1234", area_id="ABCD" + ) + + with patch.object(config, "async_sync_entities_all"): + await config.async_initialize() + await hass.async_block_till_done() + await config.async_connect_agent_user("mock-user-id") + + with patch.object(config, "async_schedule_google_sync_all") as mock_sync: + # Device registry updated with non-relevant changes + hass.bus.async_fire( + dr.EVENT_DEVICE_REGISTRY_UPDATED, + { + "action": "update", + "device_id": "1234", + "changes": ["manufacturer"], + }, + ) + await hass.async_block_till_done() + + assert len(mock_sync.mock_calls) == 0 + + # Device registry updated with relevant changes + # but entity has area ID so not impacted + hass.bus.async_fire( + dr.EVENT_DEVICE_REGISTRY_UPDATED, + { + "action": "update", + "device_id": "1234", + "changes": ["area_id"], + }, + ) + await hass.async_block_till_done() + + assert len(mock_sync.mock_calls) == 0 + + ent_reg.async_update_entity(entity_entry.entity_id, area_id=None) + + # Device registry updated with relevant changes + # but entity has area ID so not impacted + hass.bus.async_fire( + dr.EVENT_DEVICE_REGISTRY_UPDATED, + { + "action": "update", + "device_id": "1234", + "changes": ["area_id"], + }, + ) + await hass.async_block_till_done() + + assert len(mock_sync.mock_calls) == 1 + + async def test_sync_google_when_started(hass, mock_cloud_login, cloud_prefs): """Test Google config syncs on init.""" config = CloudGoogleConfig( From e6da1d73183d5fc6686718649a1524ab552661b0 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 07:38:54 +0200 Subject: [PATCH 025/930] Improve aurora_abb_powerone typing (#70919) --- homeassistant/components/aurora_abb_powerone/config_flow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/aurora_abb_powerone/config_flow.py b/homeassistant/components/aurora_abb_powerone/config_flow.py index 643d3b3f865..07741bd4e3c 100644 --- a/homeassistant/components/aurora_abb_powerone/config_flow.py +++ b/homeassistant/components/aurora_abb_powerone/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Aurora ABB PowerOne integration.""" from __future__ import annotations +from collections.abc import Mapping import logging from typing import Any @@ -27,7 +28,7 @@ _LOGGER = logging.getLogger(__name__) def validate_and_connect( - hass: core.HomeAssistant, data: dict[str, Any] + hass: core.HomeAssistant, data: Mapping[str, Any] ) -> dict[str, str]: """Validate the user input allows us to connect. From db3eb26c74c905767f79dc642eda30d58124d4f8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 09:05:08 +0200 Subject: [PATCH 026/930] Correct color mode in shelly light (#70967) --- homeassistant/components/shelly/light.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 3530241f102..295f64b01f6 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -380,6 +380,9 @@ class BlockShellyLight(ShellyBlockEntity, LightEntity): class RpcShellyLight(ShellyRpcEntity, LightEntity): """Entity that controls a light on RPC based Shelly devices.""" + _attr_color_mode = ColorMode.ONOFF + _attr_supported_color_modes = {ColorMode.ONOFF} + def __init__(self, wrapper: RpcDeviceWrapper, id_: int) -> None: """Initialize light.""" super().__init__(wrapper, f"switch:{id_}") From 13d67747cb76fca5429a3eced78f171bd127ab07 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 09:36:14 +0200 Subject: [PATCH 027/930] Fix color_mode property in fritzbox light (#70965) --- homeassistant/components/fritzbox/light.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 65ed96dd603..0edcb8ee260 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -118,7 +118,14 @@ class FritzboxLight(FritzBoxEntity, LightEntity): return color.color_temperature_kelvin_to_mired(kelvin) @property - def supported_color_modes(self) -> set: + def color_mode(self) -> ColorMode: + """Return the color mode of the light.""" + if self.device.color_mode == COLOR_MODE: + return ColorMode.HS + return ColorMode.COLOR_TEMP + + @property + def supported_color_modes(self) -> set[ColorMode]: """Flag supported color modes.""" return SUPPORTED_COLOR_MODES From 9f43fca5862f31f3f819911a7db72175dc89402a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 09:36:53 +0200 Subject: [PATCH 028/930] Fix color mode in overkiz light (#70966) --- homeassistant/components/overkiz/light.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/overkiz/light.py b/homeassistant/components/overkiz/light.py index 0ad6cb0d9c5..bb06b645d24 100644 --- a/homeassistant/components/overkiz/light.py +++ b/homeassistant/components/overkiz/light.py @@ -48,11 +48,12 @@ class OverkizLight(OverkizEntity, LightEntity): self._attr_supported_color_modes: set[ColorMode] = set() if self.executor.has_command(OverkizCommand.SET_RGB): - self._attr_supported_color_modes.add(ColorMode.RGB) - if self.executor.has_command(OverkizCommand.SET_INTENSITY): - self._attr_supported_color_modes.add(ColorMode.BRIGHTNESS) - if not self.supported_color_modes: - self._attr_supported_color_modes = {ColorMode.ONOFF} + self._attr_color_mode = ColorMode.RGB + elif self.executor.has_command(OverkizCommand.SET_INTENSITY): + self._attr_color_mode = ColorMode.BRIGHTNESS + else: + self._attr_color_mode = ColorMode.ONOFF + self._attr_supported_color_modes = {self._attr_color_mode} @property def is_on(self) -> bool: From 59c6282c6c1c3a96df334a685074b8a47049d905 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 09:49:06 +0200 Subject: [PATCH 029/930] Migrate lifx light to color_mode (#69420) * Migrate lifx light to color_mode * Update LIFXColor to support both hs and color_temp * Apply suggestions from code review Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Update light.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/lifx/light.py | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 428ae8745e0..188959adc76 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -29,14 +29,11 @@ from homeassistant.components.light import ( COLOR_GROUP, DOMAIN, LIGHT_TURN_ON_SCHEMA, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_TRANSITION, VALID_BRIGHTNESS, VALID_BRIGHTNESS_PCT, + ColorMode, LightEntity, + LightEntityFeature, preprocess_turn_on_alternatives, ) from homeassistant.config_entries import ConfigEntry @@ -506,6 +503,8 @@ def convert_16_to_8(value): class LIFXLight(LightEntity): """Representation of a LIFX light.""" + _attr_supported_features = LightEntityFeature.TRANSITION | LightEntityFeature.EFFECT + def __init__(self, bulb, effects_conductor): """Initialize the light.""" self.bulb = bulb @@ -577,15 +576,17 @@ class LIFXLight(LightEntity): return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin)) @property - def supported_features(self): - """Flag supported features.""" - support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT - + def color_mode(self) -> ColorMode: + """Return the color mode of the light.""" bulb_features = lifx_features(self.bulb) if bulb_features["min_kelvin"] != bulb_features["max_kelvin"]: - support |= SUPPORT_COLOR_TEMP + return ColorMode.COLOR_TEMP + return ColorMode.BRIGHTNESS - return support + @property + def supported_color_modes(self) -> set[ColorMode]: + """Flag supported color modes.""" + return {self.color_mode} @property def brightness(self): @@ -735,11 +736,17 @@ class LIFXColor(LIFXLight): """Representation of a color LIFX light.""" @property - def supported_features(self): - """Flag supported features.""" - support = super().supported_features - support |= SUPPORT_COLOR - return support + def color_mode(self) -> ColorMode: + """Return the color mode of the light.""" + sat = self.bulb.color[1] + if sat: + return ColorMode.HS + return ColorMode.COLOR_TEMP + + @property + def supported_color_modes(self) -> set[ColorMode]: + """Flag supported color modes.""" + return {ColorMode.COLOR_TEMP, ColorMode.HS} @property def effect_list(self): From 573e966d74221641b13e3530fcf60240da6596be Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 09:49:51 +0200 Subject: [PATCH 030/930] Migrate hue v1 light to color_mode (#69275) * Migrate hue v1 light to color_mode * Fix test * Correct filter_supported_color_modes + add test * Use ColorMode enum --- homeassistant/components/hue/v1/light.py | 80 ++++++++++++++++++---- homeassistant/components/light/__init__.py | 17 +++++ tests/components/hue/test_light_v1.py | 22 ++++++ tests/components/light/test_init.py | 43 +++++++++++- 4 files changed, 147 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/hue/v1/light.py b/homeassistant/components/hue/v1/light.py index 9cddb665006..eed8a35f705 100644 --- a/homeassistant/components/hue/v1/light.py +++ b/homeassistant/components/hue/v1/light.py @@ -20,13 +20,12 @@ from homeassistant.components.light import ( EFFECT_RANDOM, FLASH_LONG, FLASH_SHORT, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, SUPPORT_FLASH, SUPPORT_TRANSITION, + ColorMode, LightEntity, + filter_supported_color_modes, ) from homeassistant.core import callback from homeassistant.exceptions import PlatformNotReady @@ -60,10 +59,24 @@ SCAN_INTERVAL = timedelta(seconds=5) LOGGER = logging.getLogger(__name__) +COLOR_MODES_HUE_ON_OFF = {ColorMode.ONOFF} +COLOR_MODES_HUE_DIMMABLE = {ColorMode.BRIGHTNESS} +COLOR_MODES_HUE_COLOR_TEMP = {ColorMode.COLOR_TEMP} +COLOR_MODES_HUE_COLOR = {ColorMode.HS} +COLOR_MODES_HUE_EXTENDED = {ColorMode.COLOR_TEMP, ColorMode.HS} + +COLOR_MODES_HUE = { + "Extended color light": COLOR_MODES_HUE_EXTENDED, + "Color light": COLOR_MODES_HUE_COLOR, + "Dimmable light": COLOR_MODES_HUE_DIMMABLE, + "On/Off plug-in unit": COLOR_MODES_HUE_ON_OFF, + "Color temperature light": COLOR_MODES_HUE_COLOR_TEMP, +} + SUPPORT_HUE_ON_OFF = SUPPORT_FLASH | SUPPORT_TRANSITION -SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF | SUPPORT_BRIGHTNESS -SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE | SUPPORT_COLOR_TEMP -SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT | SUPPORT_COLOR +SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF +SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE +SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT SUPPORT_HUE_EXTENDED = SUPPORT_HUE_COLOR_TEMP | SUPPORT_HUE_COLOR SUPPORT_HUE = { @@ -96,17 +109,32 @@ def create_light(item_class, coordinator, bridge, is_group, rooms, api, item_id) api_item = api[item_id] if is_group: + supported_color_modes = set() supported_features = 0 for light_id in api_item.lights: if light_id not in bridge.api.lights: continue light = bridge.api.lights[light_id] supported_features |= SUPPORT_HUE.get(light.type, SUPPORT_HUE_EXTENDED) + supported_color_modes.update( + COLOR_MODES_HUE.get(light.type, COLOR_MODES_HUE_EXTENDED) + ) supported_features = supported_features or SUPPORT_HUE_EXTENDED + supported_color_modes = supported_color_modes or COLOR_MODES_HUE_EXTENDED + supported_color_modes = filter_supported_color_modes(supported_color_modes) else: + supported_color_modes = COLOR_MODES_HUE.get( + api_item.type, COLOR_MODES_HUE_EXTENDED + ) supported_features = SUPPORT_HUE.get(api_item.type, SUPPORT_HUE_EXTENDED) return item_class( - coordinator, bridge, is_group, api_item, supported_features, rooms + coordinator, + bridge, + is_group, + api_item, + supported_color_modes, + supported_features, + rooms, ) @@ -281,18 +309,34 @@ def hass_to_hue_brightness(value): class HueLight(CoordinatorEntity, LightEntity): """Representation of a Hue light.""" - def __init__(self, coordinator, bridge, is_group, light, supported_features, rooms): + def __init__( + self, + coordinator, + bridge, + is_group, + light, + supported_color_modes, + supported_features, + rooms, + ): """Initialize the light.""" super().__init__(coordinator) + self._attr_supported_color_modes = supported_color_modes + self._attr_supported_features = supported_features self.light = light self.bridge = bridge self.is_group = is_group - self._supported_features = supported_features self._rooms = rooms self.allow_unreachable = self.bridge.config_entry.options.get( CONF_ALLOW_UNREACHABLE, DEFAULT_ALLOW_UNREACHABLE ) + self._fixed_color_mode = None + if len(supported_color_modes) == 1: + self._fixed_color_mode = next(iter(supported_color_modes)) + else: + assert supported_color_modes == {ColorMode.COLOR_TEMP, ColorMode.HS} + if is_group: self.is_osram = False self.is_philips = False @@ -354,6 +398,19 @@ class HueLight(CoordinatorEntity, LightEntity): return hue_brightness_to_hass(bri) + @property + def color_mode(self) -> str: + """Return the color mode of the light.""" + if self._fixed_color_mode: + return self._fixed_color_mode + + # The light supports both hs/xy and white with adjustabe color_temperature + mode = self._color_mode + if mode in ("xy", "hs"): + return ColorMode.HS + + return ColorMode.COLOR_TEMP + @property def _color_mode(self): """Return the hue color mode.""" @@ -426,11 +483,6 @@ class HueLight(CoordinatorEntity, LightEntity): self.is_group or self.allow_unreachable or self.light.state["reachable"] ) - @property - def supported_features(self): - """Flag supported features.""" - return self._supported_features - @property def effect(self): """Return the current effect.""" diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 4f3240d7b31..099f917bc46 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -116,6 +116,23 @@ COLOR_MODES_COLOR = { } +def filter_supported_color_modes(color_modes: Iterable[ColorMode]) -> set[ColorMode]: + """Filter the given color modes.""" + color_modes = set(color_modes) + if ( + not color_modes + or ColorMode.UNKNOWN in color_modes + or (ColorMode.WHITE in color_modes and not color_supported(color_modes)) + ): + raise HomeAssistantError + + if ColorMode.ONOFF in color_modes and len(color_modes) > 1: + color_modes.remove(ColorMode.ONOFF) + if ColorMode.BRIGHTNESS in color_modes and len(color_modes) > 1: + color_modes.remove(ColorMode.BRIGHTNESS) + return color_modes + + def valid_supported_color_modes( color_modes: Iterable[ColorMode | str], ) -> set[ColorMode | str]: diff --git a/tests/components/hue/test_light_v1.py b/tests/components/hue/test_light_v1.py index 8c82a544ede..5423e6bd799 100644 --- a/tests/components/hue/test_light_v1.py +++ b/tests/components/hue/test_light_v1.py @@ -7,6 +7,7 @@ import aiohue from homeassistant.components import hue from homeassistant.components.hue.const import CONF_ALLOW_HUE_GROUPS from homeassistant.components.hue.v1 import light as hue_light +from homeassistant.components.light import COLOR_MODE_COLOR_TEMP, COLOR_MODE_HS from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.util import color @@ -236,6 +237,11 @@ async def test_lights_color_mode(hass, mock_bridge_v1): assert lamp_1.attributes["brightness"] == 145 assert lamp_1.attributes["hs_color"] == (36.067, 69.804) assert "color_temp" not in lamp_1.attributes + assert lamp_1.attributes["color_mode"] == COLOR_MODE_HS + assert lamp_1.attributes["supported_color_modes"] == [ + COLOR_MODE_COLOR_TEMP, + COLOR_MODE_HS, + ] new_light1_on = LIGHT_1_ON.copy() new_light1_on["state"] = new_light1_on["state"].copy() @@ -256,6 +262,11 @@ async def test_lights_color_mode(hass, mock_bridge_v1): assert lamp_1.attributes["brightness"] == 145 assert lamp_1.attributes["color_temp"] == 467 assert "hs_color" in lamp_1.attributes + assert lamp_1.attributes["color_mode"] == COLOR_MODE_COLOR_TEMP + assert lamp_1.attributes["supported_color_modes"] == [ + COLOR_MODE_COLOR_TEMP, + COLOR_MODE_HS, + ] async def test_groups(hass, mock_bridge_v1): @@ -651,6 +662,7 @@ def test_available(): bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})), coordinator=Mock(last_update_success=True), is_group=False, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) @@ -666,6 +678,7 @@ def test_available(): ), coordinator=Mock(last_update_success=True), is_group=False, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, bridge=Mock(config_entry=Mock(options={"allow_unreachable": True})), @@ -682,6 +695,7 @@ def test_available(): ), coordinator=Mock(last_update_success=True), is_group=True, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, bridge=Mock(config_entry=Mock(options={"allow_unreachable": False})), @@ -702,6 +716,7 @@ def test_hs_color(): coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) @@ -718,6 +733,7 @@ def test_hs_color(): coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) @@ -734,6 +750,7 @@ def test_hs_color(): coordinator=Mock(last_update_success=True), bridge=Mock(), is_group=False, + supported_color_modes=hue_light.COLOR_MODES_HUE_EXTENDED, supported_features=hue_light.SUPPORT_HUE_EXTENDED, rooms={}, ) @@ -910,15 +927,20 @@ async def test_group_features(hass, mock_bridge_v1): assert len(mock_bridge_v1.mock_requests) == 2 color_temp_feature = hue_light.SUPPORT_HUE["Color temperature light"] + color_temp_mode = sorted(hue_light.COLOR_MODES_HUE["Color temperature light"]) extended_color_feature = hue_light.SUPPORT_HUE["Extended color light"] + extended_color_mode = sorted(hue_light.COLOR_MODES_HUE["Extended color light"]) group_1 = hass.states.get("light.group_1") + assert group_1.attributes["supported_color_modes"] == color_temp_mode assert group_1.attributes["supported_features"] == color_temp_feature group_2 = hass.states.get("light.living_room") + assert group_2.attributes["supported_color_modes"] == extended_color_mode assert group_2.attributes["supported_features"] == extended_color_feature group_3 = hass.states.get("light.dining_room") + assert group_3.attributes["supported_color_modes"] == extended_color_mode assert group_3.attributes["supported_features"] == extended_color_feature entity_registry = er.async_get(hass) diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index 3a8efa5fd97..ae9b00baeaa 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -16,7 +16,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) -from homeassistant.exceptions import Unauthorized +from homeassistant.exceptions import HomeAssistantError, Unauthorized from homeassistant.setup import async_setup_component import homeassistant.util.color as color_util @@ -2417,3 +2417,44 @@ def test_valid_supported_color_modes(): supported = {light.ColorMode.BRIGHTNESS, light.ColorMode.COLOR_TEMP} with pytest.raises(vol.Error): light.valid_supported_color_modes(supported) + + +def test_filter_supported_color_modes(): + """Test filter_supported_color_modes.""" + supported = {light.ColorMode.HS} + assert light.filter_supported_color_modes(supported) == supported + + # Supported color modes must not be empty + supported = set() + with pytest.raises(HomeAssistantError): + light.filter_supported_color_modes(supported) + + # ColorMode.WHITE must be combined with a color mode supporting color + supported = {light.ColorMode.WHITE} + with pytest.raises(HomeAssistantError): + light.filter_supported_color_modes(supported) + + supported = {light.ColorMode.WHITE, light.ColorMode.COLOR_TEMP} + with pytest.raises(HomeAssistantError): + light.filter_supported_color_modes(supported) + + supported = {light.ColorMode.WHITE, light.ColorMode.HS} + assert light.filter_supported_color_modes(supported) == supported + + # ColorMode.ONOFF will be removed if combined with other modes + supported = {light.ColorMode.ONOFF} + assert light.filter_supported_color_modes(supported) == supported + + supported = {light.ColorMode.ONOFF, light.ColorMode.COLOR_TEMP} + assert light.filter_supported_color_modes(supported) == {light.ColorMode.COLOR_TEMP} + + # ColorMode.BRIGHTNESS will be removed if combined with other modes + supported = {light.ColorMode.BRIGHTNESS} + assert light.filter_supported_color_modes(supported) == supported + + supported = {light.ColorMode.BRIGHTNESS, light.ColorMode.COLOR_TEMP} + assert light.filter_supported_color_modes(supported) == {light.ColorMode.COLOR_TEMP} + + # ColorMode.BRIGHTNESS has priority over ColorMode.ONOFF + supported = {light.ColorMode.ONOFF, light.ColorMode.BRIGHTNESS} + assert light.filter_supported_color_modes(supported) == {light.ColorMode.BRIGHTNESS} From 0264f060e4fc988f3a0442ba8f951677816c11ea Mon Sep 17 00:00:00 2001 From: Raj Laud <50647620+rajlaud@users.noreply.github.com> Date: Thu, 28 Apr 2022 04:35:04 -0400 Subject: [PATCH 031/930] Improve repeat and shuffle support for Squeezebox (#70941) --- .../components/squeezebox/media_player.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index e302a75e0c2..a3cc2120751 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -20,6 +20,9 @@ from homeassistant.components.media_player.const import ( ATTR_MEDIA_ENQUEUE, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, + REPEAT_MODE_ALL, + REPEAT_MODE_OFF, + REPEAT_MODE_ONE, ) from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry from homeassistant.const import ( @@ -231,6 +234,7 @@ class SqueezeBoxEntity(MediaPlayerEntity): | MediaPlayerEntityFeature.TURN_OFF | MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.PLAY + | MediaPlayerEntityFeature.REPEAT_SET | MediaPlayerEntityFeature.SHUFFLE_SET | MediaPlayerEntityFeature.CLEAR_PLAYLIST | MediaPlayerEntityFeature.STOP @@ -373,10 +377,20 @@ class SqueezeBoxEntity(MediaPlayerEntity): """Album of current playing media.""" return self._player.album + @property + def repeat(self): + """Repeat setting.""" + if self._player.repeat == "song": + return REPEAT_MODE_ONE + if self._player.repeat == "playlist": + return REPEAT_MODE_ALL + return REPEAT_MODE_OFF + @property def shuffle(self): """Boolean if shuffle is enabled.""" - return self._player.shuffle + # Squeezebox has a third shuffle mode (album) not recognized by Home Assistant + return self._player.shuffle == "song" @property def sync_group(self): @@ -499,6 +513,17 @@ class SqueezeBoxEntity(MediaPlayerEntity): if index is not None: await self._player.async_index(index) + async def async_set_repeat(self, repeat): + """Set the repeat mode.""" + if repeat == REPEAT_MODE_ALL: + repeat_mode = "playlist" + elif repeat == REPEAT_MODE_ONE: + repeat_mode = "song" + else: + repeat_mode = "none" + + await self._player.async_set_repeat(repeat_mode) + async def async_set_shuffle(self, shuffle): """Enable/disable shuffle mode.""" shuffle_mode = "song" if shuffle else "none" From d9433e698dba09eb91487792c9c5e5f6b9715240 Mon Sep 17 00:00:00 2001 From: j-a-n Date: Thu, 28 Apr 2022 10:37:23 +0200 Subject: [PATCH 032/930] Add unique_id attribute to Alpha2Climate entity (#70964) Co-authored-by: Franck Nijhof --- homeassistant/components/moehlenhoff_alpha2/climate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/moehlenhoff_alpha2/climate.py b/homeassistant/components/moehlenhoff_alpha2/climate.py index 71be199b622..225735293ca 100644 --- a/homeassistant/components/moehlenhoff_alpha2/climate.py +++ b/homeassistant/components/moehlenhoff_alpha2/climate.py @@ -50,6 +50,7 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): """Initialize Alpha2 ClimateEntity.""" super().__init__(coordinator) self.heat_area_id = heat_area_id + self._attr_unique_id = heat_area_id @property def name(self) -> str: From 385f199691ab6cf83cabf903144339637422326f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 28 Apr 2022 10:48:04 +0200 Subject: [PATCH 033/930] Use shorthand attributes in tuya vacuum (#70846) --- homeassistant/components/tuya/vacuum.py | 37 ++++++++++--------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/tuya/vacuum.py b/homeassistant/components/tuya/vacuum.py index 6dea121c980..d17452b11c6 100644 --- a/homeassistant/components/tuya/vacuum.py +++ b/homeassistant/components/tuya/vacuum.py @@ -78,51 +78,54 @@ class TuyaVacuumEntity(TuyaEntity, StateVacuumEntity): _fan_speed: EnumTypeData | None = None _battery_level: IntegerTypeData | None = None - _supported_features = 0 def __init__(self, device: TuyaDevice, device_manager: TuyaDeviceManager) -> None: """Init Tuya vacuum.""" super().__init__(device, device_manager) - self._supported_features |= VacuumEntityFeature.SEND_COMMAND + self._attr_supported_features = 0 + self._attr_fan_speed_list = [] + + self._attr_supported_features |= VacuumEntityFeature.SEND_COMMAND if self.find_dpcode(DPCode.PAUSE, prefer_function=True): - self._supported_features |= VacuumEntityFeature.PAUSE + self._attr_supported_features |= VacuumEntityFeature.PAUSE if self.find_dpcode(DPCode.SWITCH_CHARGE, prefer_function=True): - self._supported_features |= VacuumEntityFeature.RETURN_HOME + self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME elif ( enum_type := self.find_dpcode( DPCode.MODE, dptype=DPType.ENUM, prefer_function=True ) ) and TUYA_MODE_RETURN_HOME in enum_type.range: - self._supported_features |= VacuumEntityFeature.RETURN_HOME + self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME if self.find_dpcode(DPCode.SEEK, prefer_function=True): - self._supported_features |= VacuumEntityFeature.LOCATE + self._attr_supported_features |= VacuumEntityFeature.LOCATE if self.find_dpcode(DPCode.STATUS, prefer_function=True): - self._supported_features |= ( + self._attr_supported_features |= ( VacuumEntityFeature.STATE | VacuumEntityFeature.STATUS ) if self.find_dpcode(DPCode.POWER, prefer_function=True): - self._supported_features |= ( + self._attr_supported_features |= ( VacuumEntityFeature.TURN_ON | VacuumEntityFeature.TURN_OFF ) if self.find_dpcode(DPCode.POWER_GO, prefer_function=True): - self._supported_features |= ( + self._attr_supported_features |= ( VacuumEntityFeature.STOP | VacuumEntityFeature.START ) if enum_type := self.find_dpcode( DPCode.SUCTION, dptype=DPType.ENUM, prefer_function=True ): - self._supported_features |= VacuumEntityFeature.FAN_SPEED self._fan_speed = enum_type + self._attr_fan_speed_list = enum_type.range + self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED if int_type := self.find_dpcode(DPCode.ELECTRICITY_LEFT, dptype=DPType.INTEGER): - self._supported_features |= VacuumEntityFeature.BATTERY + self._attr_supported_features |= VacuumEntityFeature.BATTERY self._battery_level = int_type @property @@ -139,13 +142,6 @@ class TuyaVacuumEntity(TuyaEntity, StateVacuumEntity): """Return the fan speed of the vacuum cleaner.""" return self.device.status.get(DPCode.SUCTION) - @property - def fan_speed_list(self) -> list[str]: - """Get the list of available fan speed steps of the vacuum cleaner.""" - if self._fan_speed is None: - return [] - return self._fan_speed.range - @property def state(self) -> str | None: """Return Tuya vacuum device state.""" @@ -157,11 +153,6 @@ class TuyaVacuumEntity(TuyaEntity, StateVacuumEntity): return None return TUYA_STATUS_TO_HA.get(status) - @property - def supported_features(self) -> int: - """Flag supported features.""" - return self._supported_features - def turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" self._send_command([{"code": DPCode.POWER, "value": True}]) From 283c04e4246239f618fd897cced1c51d576d7b74 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 10:52:42 +0200 Subject: [PATCH 034/930] Improve typing [helpers.entity] (#70890) --- .strict-typing | 1 + homeassistant/helpers/entity.py | 6 +++--- mypy.ini | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.strict-typing b/.strict-typing index bb2a163f40d..ea4124abde5 100644 --- a/.strict-typing +++ b/.strict-typing @@ -16,6 +16,7 @@ homeassistant.auth.providers.* homeassistant.helpers.area_registry homeassistant.helpers.condition homeassistant.helpers.discovery +homeassistant.helpers.entity homeassistant.helpers.entity_values homeassistant.helpers.event homeassistant.helpers.reload diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index 791a80f7731..f07f4c1c7b8 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import ABC import asyncio -from collections.abc import Awaitable, Iterable, Mapping, MutableMapping +from collections.abc import Coroutine, Iterable, Mapping, MutableMapping from dataclasses import dataclass from datetime import datetime, timedelta from enum import Enum, auto @@ -621,7 +621,7 @@ class Entity(ABC): if DATA_CUSTOMIZE in self.hass.data: attr.update(self.hass.data[DATA_CUSTOMIZE].get(self.entity_id)) - def _convert_temperature(state: str, attr: dict) -> str: + def _convert_temperature(state: str, attr: dict[str, Any]) -> str: # Convert temperature if we detect one # pylint: disable-next=import-outside-toplevel from homeassistant.components.sensor import SensorEntity @@ -958,7 +958,7 @@ class Entity(ABC): """Return the representation.""" return f"" - async def async_request_call(self, coro: Awaitable) -> None: + async def async_request_call(self, coro: Coroutine[Any, Any, Any]) -> None: """Process request batched.""" if self.parallel_updates: await self.parallel_updates.acquire() diff --git a/mypy.ini b/mypy.ini index 5df02c46252..74ed61e5c57 100644 --- a/mypy.ini +++ b/mypy.ini @@ -60,6 +60,9 @@ disallow_any_generics = true [mypy-homeassistant.helpers.discovery] disallow_any_generics = true +[mypy-homeassistant.helpers.entity] +disallow_any_generics = true + [mypy-homeassistant.helpers.entity_values] disallow_any_generics = true From 9672cddb0755518810654b05826ac0aeab391e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 28 Apr 2022 11:46:48 +0200 Subject: [PATCH 035/930] Update aioqsw to v0.0.7 (#70931) --- homeassistant/components/qnap_qsw/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/qnap_qsw/manifest.json b/homeassistant/components/qnap_qsw/manifest.json index 17709b275ca..0624edd000c 100644 --- a/homeassistant/components/qnap_qsw/manifest.json +++ b/homeassistant/components/qnap_qsw/manifest.json @@ -3,7 +3,7 @@ "name": "QNAP QSW", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/qnap_qsw", - "requirements": ["aioqsw==0.0.5"], + "requirements": ["aioqsw==0.0.7"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioqsw"] diff --git a/requirements_all.txt b/requirements_all.txt index df74fa12364..601f8a5fcf4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -226,7 +226,7 @@ aiopvpc==3.0.0 aiopyarr==22.2.2 # homeassistant.components.qnap_qsw -aioqsw==0.0.5 +aioqsw==0.0.7 # homeassistant.components.recollect_waste aiorecollect==1.0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ec628688ad0..c8f37153330 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -192,7 +192,7 @@ aiopvpc==3.0.0 aiopyarr==22.2.2 # homeassistant.components.qnap_qsw -aioqsw==0.0.5 +aioqsw==0.0.7 # homeassistant.components.recollect_waste aiorecollect==1.0.8 From cdafbbe10f723adf6f8dc2a7db22a7b390d4e6bc Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 13:19:51 +0200 Subject: [PATCH 036/930] Rename bound TypeVars (#70975) --- homeassistant/components/dlna_dmr/media_player.py | 10 ++++++---- homeassistant/components/evil_genius_labs/util.py | 10 ++++++---- homeassistant/components/plugwise/util.py | 12 +++++++----- homeassistant/components/sonarr/sensor.py | 10 ++++++---- homeassistant/components/vlc_telnet/media_player.py | 8 ++++---- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 807a983983b..9a7eef4bf89 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -61,18 +61,20 @@ from .data import EventListenAddr, get_domain_data PARALLEL_UPDATES = 0 -_T = TypeVar("_T", bound="DlnaDmrEntity") +_DlnaDmrEntityT = TypeVar("_DlnaDmrEntityT", bound="DlnaDmrEntity") _R = TypeVar("_R") _P = ParamSpec("_P") def catch_request_errors( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R | None]]: + func: Callable[Concatenate[_DlnaDmrEntityT, _P], Awaitable[_R]] +) -> Callable[Concatenate[_DlnaDmrEntityT, _P], Coroutine[Any, Any, _R | None]]: """Catch UpnpError errors.""" @functools.wraps(func) - async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R | None: + async def wrapper( + self: _DlnaDmrEntityT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R | None: """Catch UpnpError errors and check availability before and after request.""" if not self.available: _LOGGER.warning( diff --git a/homeassistant/components/evil_genius_labs/util.py b/homeassistant/components/evil_genius_labs/util.py index 7cbcc821bfe..1071b953027 100644 --- a/homeassistant/components/evil_genius_labs/util.py +++ b/homeassistant/components/evil_genius_labs/util.py @@ -9,18 +9,20 @@ from typing_extensions import Concatenate, ParamSpec from . import EvilGeniusEntity -_T = TypeVar("_T", bound=EvilGeniusEntity) +_EvilGeniusEntityT = TypeVar("_EvilGeniusEntityT", bound=EvilGeniusEntity) _R = TypeVar("_R") _P = ParamSpec("_P") def update_when_done( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: + func: Callable[Concatenate[_EvilGeniusEntityT, _P], Awaitable[_R]] +) -> Callable[Concatenate[_EvilGeniusEntityT, _P], Coroutine[Any, Any, _R]]: """Decorate function to trigger update when function is done.""" @wraps(func) - async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R: + async def wrapper( + self: _EvilGeniusEntityT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R: """Wrap function.""" result = await func(self, *args, **kwargs) await self.coordinator.async_request_refresh() diff --git a/homeassistant/components/plugwise/util.py b/homeassistant/components/plugwise/util.py index 769bfc4ecea..55d9c204dd3 100644 --- a/homeassistant/components/plugwise/util.py +++ b/homeassistant/components/plugwise/util.py @@ -9,21 +9,23 @@ from homeassistant.exceptions import HomeAssistantError from .entity import PlugwiseEntity -_P = ParamSpec("_P") +_PlugwiseEntityT = TypeVar("_PlugwiseEntityT", bound=PlugwiseEntity) _R = TypeVar("_R") -_T = TypeVar("_T", bound=PlugwiseEntity) +_P = ParamSpec("_P") def plugwise_command( - func: Callable[Concatenate[_T, _P], Awaitable[_R]] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, _R]]: + func: Callable[Concatenate[_PlugwiseEntityT, _P], Awaitable[_R]] +) -> Callable[Concatenate[_PlugwiseEntityT, _P], Coroutine[Any, Any, _R]]: """Decorate Plugwise calls that send commands/make changes to the device. A decorator that wraps the passed in function, catches Plugwise errors, and requests an coordinator update to update status of the devices asap. """ - async def handler(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R: + async def handler( + self: _PlugwiseEntityT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R: try: return await func(self, *args, **kwargs) except PlugwiseException as error: diff --git a/homeassistant/components/sonarr/sensor.py b/homeassistant/components/sonarr/sensor.py index 1604f500ab9..c9a946758c5 100644 --- a/homeassistant/components/sonarr/sensor.py +++ b/homeassistant/components/sonarr/sensor.py @@ -76,7 +76,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), ) -_T = TypeVar("_T", bound="SonarrSensor") +_SonarrSensorT = TypeVar("_SonarrSensorT", bound="SonarrSensor") _P = ParamSpec("_P") @@ -109,8 +109,8 @@ async def async_setup_entry( def sonarr_exception_handler( - func: Callable[Concatenate[_T, _P], Awaitable[None]] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: + func: Callable[Concatenate[_SonarrSensorT, _P], Awaitable[None]] +) -> Callable[Concatenate[_SonarrSensorT, _P], Coroutine[Any, Any, None]]: """Decorate Sonarr calls to handle Sonarr exceptions. A decorator that wraps the passed in function, catches Sonarr errors, @@ -118,7 +118,9 @@ def sonarr_exception_handler( """ @wraps(func) - async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: + async def wrapper( + self: _SonarrSensorT, *args: _P.args, **kwargs: _P.kwargs + ) -> None: try: await func(self, *args, **kwargs) self.last_update_success = True diff --git a/homeassistant/components/vlc_telnet/media_player.py b/homeassistant/components/vlc_telnet/media_player.py index c7009c6c14d..7a0a796f665 100644 --- a/homeassistant/components/vlc_telnet/media_player.py +++ b/homeassistant/components/vlc_telnet/media_player.py @@ -31,7 +31,7 @@ from .const import DATA_AVAILABLE, DATA_VLC, DEFAULT_NAME, DOMAIN, LOGGER MAX_VOLUME = 500 -_T = TypeVar("_T", bound="VlcDevice") +_VlcDeviceT = TypeVar("_VlcDeviceT", bound="VlcDevice") _P = ParamSpec("_P") @@ -48,12 +48,12 @@ async def async_setup_entry( def catch_vlc_errors( - func: Callable[Concatenate[_T, _P], Awaitable[None]] -) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: + func: Callable[Concatenate[_VlcDeviceT, _P], Awaitable[None]] +) -> Callable[Concatenate[_VlcDeviceT, _P], Coroutine[Any, Any, None]]: """Catch VLC errors.""" @wraps(func) - async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: + async def wrapper(self: _VlcDeviceT, *args: _P.args, **kwargs: _P.kwargs) -> None: """Catch VLC errors and modify availability.""" try: await func(self, *args, **kwargs) From 603c7c89802cf40ce8e77553eef1d4c7c5702bde Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 28 Apr 2022 13:45:18 +0200 Subject: [PATCH 037/930] Migrate niko_home_control light to color_mode (#70914) * Migrate niko_home_control light to color_mode * Remove useless brightness related code --- homeassistant/components/niko_home_control/light.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/niko_home_control/light.py b/homeassistant/components/niko_home_control/light.py index ef3efec9b71..f4d62fd6dc2 100644 --- a/homeassistant/components/niko_home_control/light.py +++ b/homeassistant/components/niko_home_control/light.py @@ -8,7 +8,7 @@ import nikohomecontrol import voluptuous as vol # Import the device class from the component that you want to support -from homeassistant.components.light import ATTR_BRIGHTNESS, PLATFORM_SCHEMA, LightEntity +from homeassistant.components.light import PLATFORM_SCHEMA, ColorMode, LightEntity from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -51,6 +51,9 @@ async def async_setup_platform( class NikoHomeControlLight(LightEntity): """Representation of an Niko Light.""" + _attr_color_mode = ColorMode.ONOFF + _attr_supported_color_modes = {ColorMode.ONOFF} + def __init__(self, light, data): """Set up the Niko Home Control light platform.""" self._data = data @@ -58,7 +61,6 @@ class NikoHomeControlLight(LightEntity): self._unique_id = f"light-{light.id}" self._name = light.name self._state = light.is_on - self._brightness = None @property def unique_id(self): @@ -70,11 +72,6 @@ class NikoHomeControlLight(LightEntity): """Return the display name of this light.""" return self._name - @property - def brightness(self): - """Return the brightness of the light.""" - return self._brightness - @property def is_on(self): """Return true if light is on.""" @@ -82,7 +79,6 @@ class NikoHomeControlLight(LightEntity): def turn_on(self, **kwargs): """Instruct the light to turn on.""" - self._light.brightness = kwargs.get(ATTR_BRIGHTNESS, 255) _LOGGER.debug("Turn on: %s", self.name) self._light.turn_on() From d9f3d2b429a16a2e0cc759492781098fed9eab6a Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 28 Apr 2022 13:57:05 +0200 Subject: [PATCH 038/930] Add supported_brands Marantz for denonavr (#70986) --- homeassistant/components/denonavr/manifest.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/denonavr/manifest.json b/homeassistant/components/denonavr/manifest.json index bb6e59053fb..b43dbe3acb7 100644 --- a/homeassistant/components/denonavr/manifest.json +++ b/homeassistant/components/denonavr/manifest.json @@ -56,5 +56,8 @@ } ], "iot_class": "local_polling", - "loggers": ["denonavr"] + "loggers": ["denonavr"], + "supported_brands": { + "marantz": "Marantz" + } } From 3db7f945eb84ec9175462deae479bb23ac14d3e8 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 28 Apr 2022 14:07:34 +0200 Subject: [PATCH 039/930] Add supported brands for Motion Blinds (#70996) --- .../components/motion_blinds/manifest.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 54aa5c97659..505e1943678 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -19,5 +19,21 @@ ], "codeowners": ["@starkillerOG"], "iot_class": "local_push", - "loggers": ["motionblinds"] + "loggers": ["motionblinds"], + "supported_brands": { + "amp_motorization": "AMP Motorization", + "bliss_automation": "Bliss Automation", + "bloc_blinds": "Bloc Blinds", + "brel_home": "Brel Home", + "3_day_blinds": "3 Day Blinds", + "dooya": "Dooya", + "gaviota": "Gaviota", + "hurrican_shutters_wholesale": "Hurrican Shutters Wholesale", + "ismartwindow": "iSmartWindow", + "martec": "Martec", + "raven_rock_mrg": "Raven Rock MRG", + "smart_blinds": "Smart Blinds", + "smart_home": "Smart Home", + "uprise_smart_shades": "Uprise Smart Shades" + } } From 27cf4165fa8b8804c9323fdb868a9887bba5466f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 17:01:54 +0200 Subject: [PATCH 040/930] Type iaqualink refresh decorator (#70988) --- homeassistant/components/iaqualink/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/iaqualink/__init__.py b/homeassistant/components/iaqualink/__init__.py index 030bb8cdcc8..0d413b1ad7c 100644 --- a/homeassistant/components/iaqualink/__init__.py +++ b/homeassistant/components/iaqualink/__init__.py @@ -2,8 +2,10 @@ from __future__ import annotations import asyncio +from collections.abc import Awaitable, Callable, Coroutine from functools import wraps import logging +from typing import Any, TypeVar import aiohttp.client_exceptions from iaqualink.client import AqualinkClient @@ -16,6 +18,7 @@ from iaqualink.device import ( AqualinkToggle, ) from iaqualink.exception import AqualinkServiceException +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant import config_entries @@ -40,6 +43,9 @@ from homeassistant.helpers.typing import ConfigType from .const import DOMAIN, UPDATE_INTERVAL +_AqualinkEntityT = TypeVar("_AqualinkEntityT", bound="AqualinkEntity") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) ATTR_CONFIG = "config" @@ -193,11 +199,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return await hass.config_entries.async_unload_platforms(entry, platforms_to_unload) -def refresh_system(func): +def refresh_system( + func: Callable[Concatenate[_AqualinkEntityT, _P], Awaitable[Any]] +) -> Callable[Concatenate[_AqualinkEntityT, _P], Coroutine[Any, Any, None]]: """Force update all entities after state change.""" @wraps(func) - async def wrapper(self, *args, **kwargs): + async def wrapper( + self: _AqualinkEntityT, *args: _P.args, **kwargs: _P.kwargs + ) -> None: """Call decorated function and send update signal to all entities.""" await func(self, *args, **kwargs) async_dispatcher_send(self.hass, DOMAIN) From 4a574eb701921804ba2bc146f4bbff44412a356e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 17:03:27 +0200 Subject: [PATCH 041/930] Type kodi error decorator (#70989) --- homeassistant/components/kodi/media_player.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index 0e254a7d209..0e6d51ba3cd 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -1,16 +1,18 @@ """Support for interfacing with the XBMC/Kodi JSON-RPC API.""" from __future__ import annotations +from collections.abc import Awaitable, Callable, Coroutine from datetime import timedelta from functools import wraps import logging import re -from typing import Any +from typing import Any, TypeVar import urllib.parse import jsonrpc_base from jsonrpc_base.jsonrpc import ProtocolError, TransportError from pykodi import CannotConnectError +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant.components import media_source @@ -86,6 +88,9 @@ from .const import ( EVENT_TURN_ON, ) +_KodiEntityT = TypeVar("_KodiEntityT", bound="KodiEntity") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) EVENT_KODI_CALL_METHOD_RESULT = "kodi_call_method_result" @@ -243,11 +248,13 @@ async def async_setup_entry( async_add_entities([entity]) -def cmd(func): +def cmd( + func: Callable[Concatenate[_KodiEntityT, _P], Awaitable[Any]] +) -> Callable[Concatenate[_KodiEntityT, _P], Coroutine[Any, Any, None]]: """Catch command exceptions.""" @wraps(func) - async def wrapper(obj, *args, **kwargs): + async def wrapper(obj: _KodiEntityT, *args: _P.args, **kwargs: _P.kwargs) -> None: """Wrap all command methods.""" try: await func(obj, *args, **kwargs) From ac044c8ffa52bd92232d92d37da13f8fb728321c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 17:04:19 +0200 Subject: [PATCH 042/930] Type openhome error decorator (#70990) --- .../components/openhome/media_player.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index 95d9ca57992..fa9cce1cfb6 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -2,12 +2,15 @@ from __future__ import annotations import asyncio +from collections.abc import Awaitable, Callable, Coroutine import functools import logging +from typing import Any, TypeVar import aiohttp from async_upnp_client.client import UpnpError from openhomedevice.device import Device +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant.components import media_source @@ -27,6 +30,10 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import ATTR_PIN_INDEX, DATA_OPENHOME, SERVICE_INVOKE_PIN +_OpenhomeDeviceT = TypeVar("_OpenhomeDeviceT", bound="OpenhomeDevice") +_R = TypeVar("_R") +_P = ParamSpec("_P") + SUPPORT_OPENHOME = ( MediaPlayerEntityFeature.SELECT_SOURCE | MediaPlayerEntityFeature.TURN_OFF @@ -74,19 +81,27 @@ async def async_setup_platform( ) -def catch_request_errors(): +def catch_request_errors() -> Callable[ + [Callable[Concatenate[_OpenhomeDeviceT, _P], Awaitable[_R]]], + Callable[Concatenate[_OpenhomeDeviceT, _P], Coroutine[Any, Any, _R | None]], +]: """Catch asyncio.TimeoutError, aiohttp.ClientError, UpnpError errors.""" - def call_wrapper(func): + def call_wrapper( + func: Callable[Concatenate[_OpenhomeDeviceT, _P], Awaitable[_R]] + ) -> Callable[Concatenate[_OpenhomeDeviceT, _P], Coroutine[Any, Any, _R | None]]: """Call wrapper for decorator.""" @functools.wraps(func) - async def wrapper(self, *args, **kwargs): + async def wrapper( + self: _OpenhomeDeviceT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R | None: """Catch asyncio.TimeoutError, aiohttp.ClientError, UpnpError errors.""" try: return await func(self, *args, **kwargs) except (asyncio.TimeoutError, aiohttp.ClientError, UpnpError): _LOGGER.error("Error during call %s", func.__name__) + return None return wrapper From 7fbc3f63643c314d8c8097e6fd4bc9a1b2314dc2 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 28 Apr 2022 17:31:11 +0200 Subject: [PATCH 043/930] Skip translations when integration no longer exists (#71004) * Skip translations when integration no longer exists * Update script/translations/download.py Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- script/translations/download.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/script/translations/download.py b/script/translations/download.py index eab9c40370e..6d4ce91263a 100755 --- a/script/translations/download.py +++ b/script/translations/download.py @@ -70,7 +70,7 @@ def get_component_path(lang, component): return os.path.join( "homeassistant", "components", component, "translations", f"{lang}.json" ) - raise ExitApp(f"Integration {component} not found under homeassistant/components/") + return None def get_platform_path(lang, component, platform): @@ -98,7 +98,11 @@ def save_language_translations(lang, translations): for component, component_translations in components.items(): base_translations = get_component_translations(component_translations) if base_translations: - path = get_component_path(lang, component) + if (path := get_component_path(lang, component)) is None: + print( + f"Skipping {lang} for {component}, as the integration doesn't seem to exist." + ) + continue os.makedirs(os.path.dirname(path), exist_ok=True) save_json(path, base_translations) From d907eb28101a91ab2826ec161137faaa86ccb129 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:32:39 +0200 Subject: [PATCH 044/930] Use LightEntityFeature enum in hue (#70987) --- homeassistant/components/hue/v1/light.py | 8 +++----- homeassistant/components/hue/v2/group.py | 7 +++---- homeassistant/components/hue/v2/light.py | 10 ++++------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/hue/v1/light.py b/homeassistant/components/hue/v1/light.py index eed8a35f705..420256f421b 100644 --- a/homeassistant/components/hue/v1/light.py +++ b/homeassistant/components/hue/v1/light.py @@ -20,11 +20,9 @@ from homeassistant.components.light import ( EFFECT_RANDOM, FLASH_LONG, FLASH_SHORT, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, ColorMode, LightEntity, + LightEntityFeature, filter_supported_color_modes, ) from homeassistant.core import callback @@ -73,10 +71,10 @@ COLOR_MODES_HUE = { "Color temperature light": COLOR_MODES_HUE_COLOR_TEMP, } -SUPPORT_HUE_ON_OFF = SUPPORT_FLASH | SUPPORT_TRANSITION +SUPPORT_HUE_ON_OFF = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION SUPPORT_HUE_DIMMABLE = SUPPORT_HUE_ON_OFF SUPPORT_HUE_COLOR_TEMP = SUPPORT_HUE_DIMMABLE -SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | SUPPORT_EFFECT +SUPPORT_HUE_COLOR = SUPPORT_HUE_DIMMABLE | LightEntityFeature.EFFECT SUPPORT_HUE_EXTENDED = SUPPORT_HUE_COLOR_TEMP | SUPPORT_HUE_COLOR SUPPORT_HUE = { diff --git a/homeassistant/components/hue/v2/group.py b/homeassistant/components/hue/v2/group.py index 8a6752307cc..cbf974325e0 100644 --- a/homeassistant/components/hue/v2/group.py +++ b/homeassistant/components/hue/v2/group.py @@ -15,10 +15,9 @@ from homeassistant.components.light import ( ATTR_TRANSITION, ATTR_XY_COLOR, FLASH_SHORT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, ColorMode, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -81,8 +80,8 @@ class GroupedHueLight(HueBaseEntity, LightEntity): self.group = group self.controller = controller self.api: HueBridgeV2 = bridge.api - self._attr_supported_features |= SUPPORT_FLASH - self._attr_supported_features |= SUPPORT_TRANSITION + self._attr_supported_features |= LightEntityFeature.FLASH + self._attr_supported_features |= LightEntityFeature.TRANSITION self._dynamic_mode_active = False self._update_values() diff --git a/homeassistant/components/hue/v2/light.py b/homeassistant/components/hue/v2/light.py index aaf96a3ca17..0f7cc6cdbab 100644 --- a/homeassistant/components/hue/v2/light.py +++ b/homeassistant/components/hue/v2/light.py @@ -17,11 +17,9 @@ from homeassistant.components.light import ( ATTR_TRANSITION, ATTR_XY_COLOR, FLASH_SHORT, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, ColorMode, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -74,7 +72,7 @@ class HueLight(HueBaseEntity, LightEntity): """Initialize the light.""" super().__init__(bridge, controller, resource) if self.resource.alert and self.resource.alert.action_values: - self._attr_supported_features |= SUPPORT_FLASH + self._attr_supported_features |= LightEntityFeature.FLASH self.resource = resource self.controller = controller self._supported_color_modes: set[ColorMode | str] = set() @@ -87,7 +85,7 @@ class HueLight(HueBaseEntity, LightEntity): # only add color mode brightness if no color variants self._supported_color_modes.add(ColorMode.BRIGHTNESS) # support transition if brightness control - self._attr_supported_features |= SUPPORT_TRANSITION + self._attr_supported_features |= LightEntityFeature.TRANSITION # get list of supported effects (combine effects and timed_effects) self._attr_effect_list = [] if effects := resource.effects: @@ -102,7 +100,7 @@ class HueLight(HueBaseEntity, LightEntity): ] if len(self._attr_effect_list) > 0: self._attr_effect_list.insert(0, EFFECT_NONE) - self._attr_supported_features |= SUPPORT_EFFECT + self._attr_supported_features |= LightEntityFeature.EFFECT @property def brightness(self) -> int | None: From 70e8f81be1950d839b32feabe6aa38fe797b0164 Mon Sep 17 00:00:00 2001 From: Yuval Aboulafia Date: Thu, 28 Apr 2022 21:35:42 +0300 Subject: [PATCH 045/930] Loop load Jewish Calendar platforms (#70714) --- .../components/jewish_calendar/__init__.py | 13 ++++++------- homeassistant/components/jewish_calendar/sensor.py | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/jewish_calendar/__init__.py b/homeassistant/components/jewish_calendar/__init__.py index 4d2cee744ef..d3291e51bc1 100644 --- a/homeassistant/components/jewish_calendar/__init__.py +++ b/homeassistant/components/jewish_calendar/__init__.py @@ -11,6 +11,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.typing import ConfigType DOMAIN = "jewish_calendar" +PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.BINARY_SENSOR] CONF_DIASPORA = "diaspora" CONF_LANGUAGE = "language" @@ -67,6 +68,9 @@ def get_unique_prefix( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Jewish Calendar component.""" + if DOMAIN not in config: + return True + name = config[DOMAIN][CONF_NAME] language = config[DOMAIN][CONF_LANGUAGE] @@ -97,12 +101,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: "prefix": prefix, } - hass.async_create_task( - async_load_platform(hass, Platform.SENSOR, DOMAIN, {}, config) - ) - - hass.async_create_task( - async_load_platform(hass, Platform.BINARY_SENSOR, DOMAIN, {}, config) - ) + for platform in PLATFORMS: + hass.async_create_task(async_load_platform(hass, platform, DOMAIN, {}, config)) return True diff --git a/homeassistant/components/jewish_calendar/sensor.py b/homeassistant/components/jewish_calendar/sensor.py index d53f3702bcc..827a35587f8 100644 --- a/homeassistant/components/jewish_calendar/sensor.py +++ b/homeassistant/components/jewish_calendar/sensor.py @@ -24,7 +24,7 @@ from . import DOMAIN _LOGGER = logging.getLogger(__name__) -DATA_SENSORS = ( +INFO_SENSORS = ( SensorEntityDescription( key="date", name="Date", @@ -143,7 +143,7 @@ async def async_setup_platform( sensors = [ JewishCalendarSensor(hass.data[DOMAIN], description) - for description in DATA_SENSORS + for description in INFO_SENSORS ] sensors.extend( JewishCalendarTimeSensor(hass.data[DOMAIN], description) From caf71c854fd0ecccf750faa35f5972183c2760a7 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 28 Apr 2022 20:36:52 +0200 Subject: [PATCH 046/930] Handle situation where mac might not exist in clients (#71016) --- homeassistant/components/unifi/device_tracker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 0ad1c920fa7..2ae8eb2e32b 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -107,11 +107,11 @@ def add_client_entities(controller, async_add_entities, clients): trackers = [] for mac in clients: - if mac in controller.entities[DOMAIN][UniFiClientTracker.TYPE]: + if mac in controller.entities[DOMAIN][UniFiClientTracker.TYPE] or not ( + client := controller.api.clients.get(mac) + ): continue - client = controller.api.clients[mac] - if mac not in controller.wireless_clients: if not controller.option_track_wired_clients: continue From 805aa3375a0be2f491ed49487ad7ead592aaec98 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 28 Apr 2022 20:39:50 +0200 Subject: [PATCH 047/930] Add support for OpenWeatherMap's visibility (#71013) * Add support for visibility * Add docstrings --- homeassistant/components/openweathermap/const.py | 8 ++++++++ .../openweathermap/weather_update_coordinator.py | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/homeassistant/components/openweathermap/const.py b/homeassistant/components/openweathermap/const.py index b623ed86c3a..8d673507929 100644 --- a/homeassistant/components/openweathermap/const.py +++ b/homeassistant/components/openweathermap/const.py @@ -31,6 +31,7 @@ from homeassistant.components.weather import ( ) from homeassistant.const import ( DEGREE, + LENGTH_KILOMETERS, LENGTH_MILLIMETERS, PERCENTAGE, PRESSURE_HPA, @@ -65,6 +66,7 @@ ATTR_API_CLOUDS = "clouds" ATTR_API_RAIN = "rain" ATTR_API_SNOW = "snow" ATTR_API_UV_INDEX = "uv_index" +ATTR_API_VISIBILITY_DISTANCE = "visibility_distance" ATTR_API_WEATHER_CODE = "weather_code" ATTR_API_FORECAST = "forecast" UPDATE_LISTENER = "update_listener" @@ -243,6 +245,12 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( native_unit_of_measurement=UV_INDEX, state_class=SensorStateClass.MEASUREMENT, ), + SensorEntityDescription( + key=ATTR_API_VISIBILITY_DISTANCE, + name="Visibility", + native_unit_of_measurement=LENGTH_KILOMETERS, + state_class=SensorStateClass.MEASUREMENT, + ), SensorEntityDescription( key=ATTR_API_CONDITION, name="Condition", diff --git a/homeassistant/components/openweathermap/weather_update_coordinator.py b/homeassistant/components/openweathermap/weather_update_coordinator.py index f4814e64d9a..26341621051 100644 --- a/homeassistant/components/openweathermap/weather_update_coordinator.py +++ b/homeassistant/components/openweathermap/weather_update_coordinator.py @@ -36,6 +36,7 @@ from .const import ( ATTR_API_SNOW, ATTR_API_TEMPERATURE, ATTR_API_UV_INDEX, + ATTR_API_VISIBILITY_DISTANCE, ATTR_API_WEATHER, ATTR_API_WEATHER_CODE, ATTR_API_WIND_BEARING, @@ -72,6 +73,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): ) async def _async_update_data(self): + """Update the data.""" data = {} async with async_timeout.timeout(20): try: @@ -137,11 +139,13 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): ATTR_API_WEATHER: current_weather.detailed_status, ATTR_API_CONDITION: self._get_condition(current_weather.weather_code), ATTR_API_UV_INDEX: current_weather.uvi, + ATTR_API_VISIBILITY_DISTANCE: current_weather.visibility_distance, ATTR_API_WEATHER_CODE: current_weather.weather_code, ATTR_API_FORECAST: forecast_weather, } def _get_forecast_from_weather_response(self, weather_response): + """Extract the forecast data from the weather response.""" forecast_arg = "forecast" if self._forecast_mode == FORECAST_MODE_ONECALL_HOURLY: forecast_arg = "forecast_hourly" @@ -152,6 +156,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): ] def _convert_forecast(self, entry): + """Convert the forecast data.""" forecast = { ATTR_FORECAST_TIME: dt.utc_from_timestamp( entry.reference_time("unix") @@ -182,6 +187,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): @staticmethod def _fmt_dewpoint(dewpoint): + """Format the dewpoint data.""" if dewpoint is not None: return round(kelvin_to_celsius(dewpoint), 1) return None From ce9d000cd849237c7419e8ce4d23e9f6aab2a9f5 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 28 Apr 2022 20:42:14 +0200 Subject: [PATCH 048/930] Motionblinds brands fix spelling (#71012) * add supported_brands * fix spelling --- homeassistant/components/motion_blinds/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 505e1943678..5ec39d8c638 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -31,7 +31,7 @@ "hurrican_shutters_wholesale": "Hurrican Shutters Wholesale", "ismartwindow": "iSmartWindow", "martec": "Martec", - "raven_rock_mrg": "Raven Rock MRG", + "raven_rock_mfg": "Raven Rock MFG", "smart_blinds": "Smart Blinds", "smart_home": "Smart Home", "uprise_smart_shades": "Uprise Smart Shades" From d0f1168ff07d561f53521068ae6fb2128f878f04 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 28 Apr 2022 12:45:37 -0600 Subject: [PATCH 049/930] Ensure SimpliSafe re-auth only looks at SimpliSafe config entries (#71009) * Ensure SimpliSafe re-auth only looks at SimpliSafe config entries * Add a test * Trigger Build * Linting * Comment * Simplify test --- .../components/simplisafe/config_flow.py | 3 ++- .../components/simplisafe/test_config_flow.py | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/config_flow.py b/homeassistant/components/simplisafe/config_flow.py index 93a2e152ad8..ad9614c0546 100644 --- a/homeassistant/components/simplisafe/config_flow.py +++ b/homeassistant/components/simplisafe/config_flow.py @@ -126,7 +126,8 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if existing_entries := [ entry for entry in self.hass.config_entries.async_entries() - if entry.unique_id in (self._username, user_id) + if entry.domain == DOMAIN + and entry.unique_id in (self._username, user_id) ]: existing_entry = existing_entries[0] self.hass.config_entries.async_update_entry( diff --git a/tests/components/simplisafe/test_config_flow.py b/tests/components/simplisafe/test_config_flow.py index 91274f8f0c5..a4ef98e0e09 100644 --- a/tests/components/simplisafe/test_config_flow.py +++ b/tests/components/simplisafe/test_config_flow.py @@ -12,6 +12,8 @@ from homeassistant.const import CONF_CODE, CONF_TOKEN, CONF_USERNAME from .common import REFRESH_TOKEN, USER_ID, USERNAME +from tests.common import MockConfigEntry + CONF_USER_ID = "user_id" @@ -57,9 +59,15 @@ async def test_options_flow(hass, config_entry): @pytest.mark.parametrize("unique_id", [USERNAME, USER_ID]) async def test_step_reauth( - hass, config, config_entry, reauth_config, setup_simplisafe, sms_config + hass, config, config_entry, reauth_config, setup_simplisafe, sms_config, unique_id ): """Test the re-auth step (testing both username and user ID as unique ID).""" + # Add a second config entry (tied to a random domain, but with the same unique ID + # that could exist in a SimpliSafe entry) to ensure that this reauth process only + # touches the SimpliSafe entry: + entry = MockConfigEntry(domain="random", unique_id=USERNAME, data={"some": "data"}) + entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH}, data=config ) @@ -78,11 +86,17 @@ async def test_step_reauth( assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "reauth_successful" - assert len(hass.config_entries.async_entries()) == 1 + assert len(hass.config_entries.async_entries()) == 2 + + # Test that the SimpliSafe config flow is updated: [config_entry] = hass.config_entries.async_entries(DOMAIN) assert config_entry.unique_id == USER_ID assert config_entry.data == config + # Test that the non-SimpliSafe config flow remains the same: + [config_entry] = hass.config_entries.async_entries("random") + assert config_entry == entry + @pytest.mark.parametrize( "exc,error_string", From a46b38d648e5f628e04c7a022ae024b0321b7947 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:51:21 +0200 Subject: [PATCH 050/930] Type androidtv error decorator (#70976) --- .../components/androidtv/media_player.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index d2a0b97ed6b..0d43ada7bc0 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -1,9 +1,11 @@ """Support for functionality to interact with Android TV / Fire TV devices.""" from __future__ import annotations +from collections.abc import Awaitable, Callable, Coroutine from datetime import datetime import functools import logging +from typing import Any, TypeVar from adb_shell.exceptions import ( AdbTimeoutError, @@ -14,6 +16,7 @@ from adb_shell.exceptions import ( ) from androidtv.constants import APPS, KEYS from androidtv.exceptions import LockNotAcquiredException +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant.components import persistent_notification @@ -61,6 +64,10 @@ from .const import ( SIGNAL_CONFIG_ENTITY, ) +_ADBDeviceT = TypeVar("_ADBDeviceT", bound="ADBDevice") +_R = TypeVar("_R") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) ATTR_ADB_RESPONSE = "adb_response" @@ -144,18 +151,27 @@ async def async_setup_entry( ) -def adb_decorator(override_available=False): +def adb_decorator( + override_available: bool = False, +) -> Callable[ + [Callable[Concatenate[_ADBDeviceT, _P], Awaitable[_R]]], + Callable[Concatenate[_ADBDeviceT, _P], Coroutine[Any, Any, _R | None]], +]: """Wrap ADB methods and catch exceptions. Allows for overriding the available status of the ADB connection via the `override_available` parameter. """ - def _adb_decorator(func): + def _adb_decorator( + func: Callable[Concatenate[_ADBDeviceT, _P], Awaitable[_R]] + ) -> Callable[Concatenate[_ADBDeviceT, _P], Coroutine[Any, Any, _R | None]]: """Wrap the provided ADB method and catch exceptions.""" @functools.wraps(func) - async def _adb_exception_catcher(self, *args, **kwargs): + async def _adb_exception_catcher( + self: _ADBDeviceT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R | None: """Call an ADB-related method and catch exceptions.""" # pylint: disable=protected-access if not self.available and not override_available: @@ -168,7 +184,7 @@ def adb_decorator(override_available=False): _LOGGER.info( "ADB command not executed because the connection is currently in use" ) - return + return None except self.exceptions as err: _LOGGER.error( "Failed to execute an ADB command. ADB connection re-" From 319ae8b0b730091737511f547350ee5635fe9c4a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:52:08 +0200 Subject: [PATCH 051/930] Type hive refresh decorator (#70979) --- homeassistant/components/hive/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hive/__init__.py b/homeassistant/components/hive/__init__.py index fab89bd5b19..00c3a327578 100644 --- a/homeassistant/components/hive/__init__.py +++ b/homeassistant/components/hive/__init__.py @@ -1,10 +1,15 @@ """Support for the Hive devices and services.""" +from __future__ import annotations + +from collections.abc import Awaitable, Callable, Coroutine from functools import wraps import logging +from typing import Any, TypeVar from aiohttp.web_exceptions import HTTPException from apyhiveapi import Hive from apyhiveapi.helper.hive_exceptions import HiveReauthRequired +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant import config_entries @@ -22,6 +27,9 @@ from homeassistant.helpers.typing import ConfigType from .const import DOMAIN, PLATFORM_LOOKUP, PLATFORMS +_HiveEntityT = TypeVar("_HiveEntityT", bound="HiveEntity") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( @@ -104,11 +112,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -def refresh_system(func): +def refresh_system( + func: Callable[Concatenate[_HiveEntityT, _P], Awaitable[Any]] +) -> Callable[Concatenate[_HiveEntityT, _P], Coroutine[Any, Any, None]]: """Force update all entities after state change.""" @wraps(func) - async def wrapper(self, *args, **kwargs): + async def wrapper(self: _HiveEntityT, *args: _P.args, **kwargs: _P.kwargs) -> None: await func(self, *args, **kwargs) async_dispatcher_send(self.hass, DOMAIN) From 64fc93ba5f0aece08fb1da03caef3f7cadd3c39a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:53:39 +0200 Subject: [PATCH 052/930] Type heos error decorator (#70978) --- homeassistant/components/heos/media_player.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 2a68ecdc7df..dabe79afb03 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -1,11 +1,14 @@ """Denon HEOS Media Player.""" from __future__ import annotations +from collections.abc import Awaitable, Callable, Coroutine from functools import reduce, wraps import logging from operator import ior +from typing import Any from pyheos import HeosError, const as heos_const +from typing_extensions import ParamSpec from homeassistant.components import media_source from homeassistant.components.media_player import ( @@ -42,6 +45,8 @@ from .const import ( SIGNAL_HEOS_UPDATED, ) +_P = ParamSpec("_P") + BASE_SUPPORTED_FEATURES = ( MediaPlayerEntityFeature.VOLUME_MUTE | MediaPlayerEntityFeature.VOLUME_SET @@ -80,12 +85,16 @@ async def async_setup_entry( async_add_entities(devices, True) -def log_command_error(command: str): +def log_command_error( + command: str, +) -> Callable[[Callable[_P, Awaitable[Any]]], Callable[_P, Coroutine[Any, Any, None]]]: """Return decorator that logs command failure.""" - def decorator(func): + def decorator( + func: Callable[_P, Awaitable[Any]] + ) -> Callable[_P, Coroutine[Any, Any, None]]: @wraps(func) - async def wrapper(*args, **kwargs): + async def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> None: try: await func(*args, **kwargs) except (HeosError, ValueError) as ex: From 1eb5316d89644f5f519f5a0a774587a6d840ec5e Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Thu, 28 Apr 2022 14:57:26 -0400 Subject: [PATCH 053/930] Remove unnecessary update_before_add from ZHA (#71010) * Additional streamlining for ZHA entity init * fix tests --- homeassistant/components/zha/button.py | 1 - .../components/zha/core/discovery.py | 3 +- homeassistant/components/zha/entity.py | 1 + homeassistant/components/zha/fan.py | 1 - homeassistant/components/zha/number.py | 1 - homeassistant/components/zha/select.py | 13 +++- homeassistant/components/zha/sensor.py | 1 - homeassistant/components/zha/siren.py | 1 - tests/components/zha/test_cover.py | 8 +- tests/components/zha/test_device_tracker.py | 2 +- tests/components/zha/test_select.py | 76 +++++++++++++++++-- 11 files changed, 86 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/zha/button.py b/homeassistant/components/zha/button.py index abfa94f5906..f130936df02 100644 --- a/homeassistant/components/zha/button.py +++ b/homeassistant/components/zha/button.py @@ -41,7 +41,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) diff --git a/homeassistant/components/zha/core/discovery.py b/homeassistant/components/zha/core/discovery.py index 8d7d53468e2..cdad57834eb 100644 --- a/homeassistant/components/zha/core/discovery.py +++ b/homeassistant/components/zha/core/discovery.py @@ -54,14 +54,13 @@ async def async_add_entities( tuple[str, ZHADevice, list[base.ZigbeeChannel]], ] ], - update_before_add: bool = True, ) -> None: """Add entities helper.""" if not entities: return to_add = [ent_cls.create_entity(*args) for ent_cls, args in entities] entities_to_add = [entity for entity in to_add if entity is not None] - _async_add_entities(entities_to_add, update_before_add=update_before_add) + _async_add_entities(entities_to_add, update_before_add=False) entities.clear() diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index 13e43aa9ff0..50ffb8f4fcd 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -291,6 +291,7 @@ class ZhaGroupEntity(BaseZhaEntity): async def async_added_to_hass(self) -> None: """Register callbacks.""" await super().async_added_to_hass() + await self.async_update() self.async_accept_signal( None, diff --git a/homeassistant/components/zha/fan.py b/homeassistant/components/zha/fan.py index bad1d8e94f1..1c2f52c6038 100644 --- a/homeassistant/components/zha/fan.py +++ b/homeassistant/components/zha/fan.py @@ -67,7 +67,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 3c5a374e588..e1191b4ece4 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -250,7 +250,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 0a67f1eac5f..b9237e24eef 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -3,6 +3,7 @@ from __future__ import annotations from enum import Enum import functools +import logging from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.clusters.security import IasWd @@ -30,6 +31,7 @@ from .entity import ZhaEntity CONFIG_DIAGNOSTIC_MATCH = functools.partial( ZHA_ENTITIES.config_diagnostic_match, Platform.SELECT ) +_LOGGER = logging.getLogger(__name__) async def async_setup_entry( @@ -47,7 +49,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) @@ -163,7 +164,15 @@ class ZCLEnumSelectEntity(ZhaEntity, SelectEntity): Return entity if it is a supported configuration, otherwise return None """ channel = channels[0] - if cls._select_attr in channel.cluster.unsupported_attributes: + if ( + cls._select_attr in channel.cluster.unsupported_attributes + or channel.cluster.get(cls._select_attr) is None + ): + _LOGGER.debug( + "%s is not supported - skipping %s entity creation", + cls._select_attr, + cls.__name__, + ) return None return cls(unique_id, zha_device, channels, **kwargs) diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index f150304c33d..0a5fe204648 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -103,7 +103,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) diff --git a/homeassistant/components/zha/siren.py b/homeassistant/components/zha/siren.py index 587efc49c9c..38b58b8dc54 100644 --- a/homeassistant/components/zha/siren.py +++ b/homeassistant/components/zha/siren.py @@ -60,7 +60,6 @@ async def async_setup_entry( discovery.async_add_entities, async_add_entities, entities_to_create, - update_before_add=False, ), ) config_entry.async_on_unload(unsub) diff --git a/tests/components/zha/test_cover.py b/tests/components/zha/test_cover.py index 60a4fab25be..3c00b5d3109 100644 --- a/tests/components/zha/test_cover.py +++ b/tests/components/zha/test_cover.py @@ -104,17 +104,14 @@ def zigpy_keen_vent(zigpy_device_mock): ) -@patch( - "homeassistant.components.zha.core.channels.closures.WindowCovering.async_initialize" -) -async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): +async def test_cover(hass, zha_device_joined_restored, zigpy_cover_device): """Test zha cover platform.""" # load up cover domain cluster = zigpy_cover_device.endpoints.get(1).window_covering cluster.PLUGGED_ATTR_READS = {"current_position_lift_percentage": 100} zha_device = await zha_device_joined_restored(zigpy_cover_device) - assert cluster.read_attributes.call_count == 2 + assert cluster.read_attributes.call_count == 1 assert "current_position_lift_percentage" in cluster.read_attributes.call_args[0][0] entity_id = await find_entity_id(Platform.COVER, zha_device, hass) @@ -193,6 +190,7 @@ async def test_cover(m1, hass, zha_device_joined_restored, zigpy_cover_device): assert cluster.request.call_args[1]["expect_reply"] is True # test rejoin + cluster.PLUGGED_ATTR_READS = {"current_position_lift_percentage": 0} await async_test_rejoin(hass, zigpy_cover_device, [cluster], (1,)) assert hass.states.get(entity_id).state == STATE_OPEN diff --git a/tests/components/zha/test_device_tracker.py b/tests/components/zha/test_device_tracker.py index e345918179e..06caac91cb2 100644 --- a/tests/components/zha/test_device_tracker.py +++ b/tests/components/zha/test_device_tracker.py @@ -52,7 +52,7 @@ async def test_device_tracker(hass, zha_device_joined_restored, zigpy_device_dt) entity_id = await find_entity_id(Platform.DEVICE_TRACKER, zha_device, hass) assert entity_id is not None - assert hass.states.get(entity_id).state == STATE_HOME + assert hass.states.get(entity_id).state == STATE_NOT_HOME await async_enable_traffic(hass, [zha_device], enabled=False) # test that the device tracker was created and that it is unavailable assert hass.states.get(entity_id).state == STATE_UNAVAILABLE diff --git a/tests/components/zha/test_select.py b/tests/components/zha/test_select.py index 452939420bf..70b943d5ea2 100644 --- a/tests/components/zha/test_select.py +++ b/tests/components/zha/test_select.py @@ -1,5 +1,7 @@ """Test ZHA select entities.""" +from unittest.mock import call + import pytest from zigpy.const import SIG_EP_PROFILE import zigpy.profiles.zha as zha @@ -50,6 +52,7 @@ async def light(hass, zigpy_device_mock): SIG_EP_OUTPUT: [general.Ota.cluster_id], } }, + node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00", ) return zigpy_device @@ -173,15 +176,15 @@ async def test_select_restore_state( assert state.state == security.IasWd.Warning.WarningMode.Burglar.name -async def test_on_off_select(hass, light, zha_device_joined_restored): - """Test zha on off select.""" +async def test_on_off_select_new_join(hass, light, zha_device_joined): + """Test zha on off select - new join.""" entity_registry = er.async_get(hass) on_off_cluster = light.endpoints[1].on_off on_off_cluster.PLUGGED_ATTR_READS = { "start_up_on_off": general.OnOff.StartUpOnOff.On } - zha_device = await zha_device_joined_restored(light) + zha_device = await zha_device_joined(light) select_name = general.OnOff.StartUpOnOff.__name__ entity_id = await find_entity_id( Platform.SELECT, @@ -191,12 +194,19 @@ async def test_on_off_select(hass, light, zha_device_joined_restored): ) assert entity_id is not None + assert on_off_cluster.read_attributes.call_count == 2 + assert ( + call(["start_up_on_off"], allow_cache=True, only_cache=False, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + assert ( + call(["on_off"], allow_cache=False, only_cache=False, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + state = hass.states.get(entity_id) assert state - if zha_device_joined_restored.name == "zha_device_joined": - assert state.state == general.OnOff.StartUpOnOff.On.name - else: - assert state.state == STATE_UNKNOWN + assert state.state == general.OnOff.StartUpOnOff.On.name assert state.attributes["options"] == ["Off", "On", "Toggle", "PreviousValue"] @@ -225,6 +235,58 @@ async def test_on_off_select(hass, light, zha_device_joined_restored): assert state.state == general.OnOff.StartUpOnOff.Off.name +async def test_on_off_select_restored(hass, light, zha_device_restored): + """Test zha on off select - restored.""" + + entity_registry = er.async_get(hass) + on_off_cluster = light.endpoints[1].on_off + on_off_cluster.PLUGGED_ATTR_READS = { + "start_up_on_off": general.OnOff.StartUpOnOff.On + } + zha_device = await zha_device_restored(light) + + assert zha_device.is_mains_powered + + assert on_off_cluster.read_attributes.call_count == 4 + # first 2 calls hit cache only + assert ( + call(["start_up_on_off"], allow_cache=True, only_cache=True, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + assert ( + call(["on_off"], allow_cache=True, only_cache=True, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + + # 2nd set of calls can actually read from the device + assert ( + call(["start_up_on_off"], allow_cache=True, only_cache=False, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + assert ( + call(["on_off"], allow_cache=False, only_cache=False, manufacturer=None) + in on_off_cluster.read_attributes.call_args_list + ) + + select_name = general.OnOff.StartUpOnOff.__name__ + entity_id = await find_entity_id( + Platform.SELECT, + zha_device, + hass, + qualifier=select_name.lower(), + ) + assert entity_id is not None + + state = hass.states.get(entity_id) + assert state + assert state.state == general.OnOff.StartUpOnOff.On.name + assert state.attributes["options"] == ["Off", "On", "Toggle", "PreviousValue"] + + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == ENTITY_CATEGORY_CONFIG + + async def test_on_off_select_unsupported(hass, light, zha_device_joined_restored): """Test zha on off select unsupported.""" From 7fefd4bc675550952884337b6405b67256aeab4f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:03:37 +0200 Subject: [PATCH 054/930] Type decora error decorator (#70977) --- homeassistant/components/decora/light.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/decora/light.py b/homeassistant/components/decora/light.py index b43881c39b9..9dbc031d476 100644 --- a/homeassistant/components/decora/light.py +++ b/homeassistant/components/decora/light.py @@ -1,13 +1,16 @@ """Support for Decora dimmers.""" from __future__ import annotations +from collections.abc import Callable import copy from functools import wraps import logging import time +from typing import TypeVar from bluepy.btle import BTLEException # pylint: disable=import-error import decora # pylint: disable=import-error +from typing_extensions import Concatenate, ParamSpec import voluptuous as vol from homeassistant import util @@ -23,6 +26,10 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +_DecoraLightT = TypeVar("_DecoraLightT", bound="DecoraLight") +_R = TypeVar("_R") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) @@ -50,11 +57,15 @@ PLATFORM_SCHEMA = vol.Schema( ) -def retry(method): +def retry( + method: Callable[Concatenate[_DecoraLightT, _P], _R] +) -> Callable[Concatenate[_DecoraLightT, _P], _R | None]: """Retry bluetooth commands.""" @wraps(method) - def wrapper_retry(device, *args, **kwargs): + def wrapper_retry( + device: _DecoraLightT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R | None: """Try send command and retry on error.""" initial = time.monotonic() From cf90e347764292835e76040d2ff24c3492839f3c Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:04:41 +0200 Subject: [PATCH 055/930] Type recorder retry decorator (#70993) --- homeassistant/components/recorder/__init__.py | 10 +++++++--- homeassistant/components/recorder/util.py | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index a0d2f1c8702..ef53721efd2 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -83,6 +83,8 @@ from .models import ( Events, StateAttributes, States, + StatisticData, + StatisticMetaData, StatisticsRuns, process_timestamp, ) @@ -460,8 +462,8 @@ class StatisticsTask(RecorderTask): class ExternalStatisticsTask(RecorderTask): """An object to insert into the recorder queue to run an external statistics task.""" - metadata: dict - statistics: Iterable[dict] + metadata: StatisticMetaData + statistics: Iterable[StatisticData] def run(self, instance: Recorder) -> None: """Run statistics task.""" @@ -916,7 +918,9 @@ class Recorder(threading.Thread): self.queue.put(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) @callback - def async_external_statistics(self, metadata: dict, stats: Iterable[dict]) -> None: + def async_external_statistics( + self, metadata: StatisticMetaData, stats: Iterable[StatisticData] + ) -> None: """Schedule external statistics.""" self.queue.put(ExternalStatisticsTask(metadata, stats)) diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index b67f4c6d558..b63bbe740bc 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -8,7 +8,7 @@ import functools import logging import os import time -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, TypeVar from awesomeversion import ( AwesomeVersion, @@ -20,6 +20,7 @@ from sqlalchemy.engine.cursor import CursorFetchStrategy from sqlalchemy.exc import OperationalError, SQLAlchemyError from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session +from typing_extensions import Concatenate, ParamSpec from homeassistant.core import HomeAssistant import homeassistant.util.dt as dt_util @@ -40,6 +41,9 @@ from .models import ( if TYPE_CHECKING: from . import Recorder +_RecorderT = TypeVar("_RecorderT", bound="Recorder") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) RETRIES = 3 @@ -430,15 +434,22 @@ def end_incomplete_runs(session: Session, start_time: datetime) -> None: session.add(run) -def retryable_database_job(description: str) -> Callable: +def retryable_database_job( + description: str, +) -> Callable[ + [Callable[Concatenate[_RecorderT, _P], bool]], + Callable[Concatenate[_RecorderT, _P], bool], +]: """Try to execute a database job. The job should return True if it finished, and False if it needs to be rescheduled. """ - def decorator(job: Callable[[Any], bool]) -> Callable: + def decorator( + job: Callable[Concatenate[_RecorderT, _P], bool] + ) -> Callable[Concatenate[_RecorderT, _P], bool]: @functools.wraps(job) - def wrapper(instance: Recorder, *args: Any, **kwargs: Any) -> bool: + def wrapper(instance: _RecorderT, *args: _P.args, **kwargs: _P.kwargs) -> bool: try: return job(instance, *args, **kwargs) except OperationalError as err: From 7649b5e6c91db6cadee5ec6ca25a3631ab5f5e51 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:05:22 +0200 Subject: [PATCH 056/930] Improve roku error decorator typing (#70992) --- homeassistant/components/roku/helpers.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/roku/helpers.py b/homeassistant/components/roku/helpers.py index d7e28066f90..f5a68f44ab8 100644 --- a/homeassistant/components/roku/helpers.py +++ b/homeassistant/components/roku/helpers.py @@ -13,7 +13,7 @@ from .entity import RokuEntity _LOGGER = logging.getLogger(__name__) -_T = TypeVar("_T", bound=RokuEntity) +_RokuEntityT = TypeVar("_RokuEntityT", bound=RokuEntity) _P = ParamSpec("_P") @@ -25,14 +25,21 @@ def format_channel_name(channel_number: str, channel_name: str | None = None) -> return channel_number -def roku_exception_handler(ignore_timeout: bool = False) -> Callable[..., Callable]: +def roku_exception_handler( + ignore_timeout: bool = False, +) -> Callable[ + [Callable[Concatenate[_RokuEntityT, _P], Awaitable[Any]]], + Callable[Concatenate[_RokuEntityT, _P], Coroutine[Any, Any, None]], +]: """Decorate Roku calls to handle Roku exceptions.""" def decorator( - func: Callable[Concatenate[_T, _P], Awaitable[None]], - ) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]: + func: Callable[Concatenate[_RokuEntityT, _P], Awaitable[Any]], + ) -> Callable[Concatenate[_RokuEntityT, _P], Coroutine[Any, Any, None]]: @wraps(func) - async def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None: + async def wrapper( + self: _RokuEntityT, *args: _P.args, **kwargs: _P.kwargs + ) -> None: try: await func(self, *args, **kwargs) except RokuConnectionTimeoutError as error: From 9af8cd030a62396c690b0185ad049b76039501fb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:06:05 +0200 Subject: [PATCH 057/930] Type plex session decorator (#70991) --- homeassistant/components/plex/media_player.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index e5d420f46b0..a9f688d9b13 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -1,11 +1,14 @@ """Support to interface with the Plex API.""" from __future__ import annotations +from collections.abc import Callable from functools import wraps import logging +from typing import TypeVar import plexapi.exceptions import requests.exceptions +from typing_extensions import Concatenate, ParamSpec from homeassistant.components.media_player import ( DOMAIN as MP_DOMAIN, @@ -43,17 +46,25 @@ from .const import ( from .media_browser import browse_media from .services import process_plex_payload +_PlexMediaPlayerT = TypeVar("_PlexMediaPlayerT", bound="PlexMediaPlayer") +_R = TypeVar("_R") +_P = ParamSpec("_P") + _LOGGER = logging.getLogger(__name__) -def needs_session(func): +def needs_session( + func: Callable[Concatenate[_PlexMediaPlayerT, _P], _R] +) -> Callable[Concatenate[_PlexMediaPlayerT, _P], _R | None]: """Ensure session is available for certain attributes.""" @wraps(func) - def get_session_attribute(self, *args): + def get_session_attribute( + self: _PlexMediaPlayerT, *args: _P.args, **kwargs: _P.kwargs + ) -> _R | None: if self.session is None: return None - return func(self, *args) + return func(self, *args, **kwargs) return get_session_attribute From a9ca774e7ed1d8fe502a53d5b765c1d9b393a524 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Thu, 28 Apr 2022 15:35:43 -0400 Subject: [PATCH 058/930] Insteon Device Control Panel (#70834) Co-authored-by: Paulus Schoutsen --- homeassistant/components/insteon/__init__.py | 37 +- .../components/insteon/api/__init__.py | 45 +- .../components/insteon/api/device.py | 66 ++- .../components/insteon/api/properties.py | 340 ++++--------- homeassistant/components/insteon/climate.py | 2 +- homeassistant/components/insteon/const.py | 2 + .../components/insteon/insteon_entity.py | 2 +- homeassistant/components/insteon/light.py | 2 +- .../components/insteon/manifest.json | 8 +- homeassistant/components/insteon/schemas.py | 2 + homeassistant/components/insteon/utils.py | 7 +- requirements_all.txt | 5 +- requirements_test_all.txt | 5 +- .../insteon/fixtures/iolinc_properties.json | 18 + tests/components/insteon/mock_devices.py | 38 +- tests/components/insteon/test_api_device.py | 51 +- .../components/insteon/test_api_properties.py | 467 ++++++++++-------- tests/components/insteon/test_config_flow.py | 50 +- tests/components/insteon/test_init.py | 22 + 19 files changed, 650 insertions(+), 519 deletions(-) create mode 100644 tests/components/insteon/fixtures/iolinc_properties.json diff --git a/homeassistant/components/insteon/__init__.py b/homeassistant/components/insteon/__init__.py index 0f17e1231e4..15181cb827c 100644 --- a/homeassistant/components/insteon/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -9,11 +9,13 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType from . import api from .const import ( CONF_CAT, + CONF_DEV_PATH, CONF_DIM_STEPS, CONF_HOUSECODE, CONF_OVERRIDE, @@ -74,13 +76,19 @@ async def close_insteon_connection(*args): async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Insteon platform.""" + hass.data[DOMAIN] = {} if DOMAIN not in config: return True - conf = config[DOMAIN] + conf = dict(config[DOMAIN]) + hass.data[DOMAIN][CONF_DEV_PATH] = conf.pop(CONF_DEV_PATH, None) + + if not conf: + return True + data, options = convert_yaml_to_config_flow(conf) + if options: - hass.data[DOMAIN] = {} hass.data[DOMAIN][OPTIONS] = options # Create a config entry with the connection data hass.async_create_task( @@ -154,23 +162,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: platforms = get_device_platforms(device) if ON_OFF_EVENTS in platforms: add_on_off_event_device(hass, device) + create_insteon_device(hass, device, entry.entry_id) _LOGGER.debug("Insteon device count: %s", len(devices)) register_new_device_callback(hass) async_register_services(hass) - device_registry = await hass.helpers.device_registry.async_get_registry() - device_registry.async_get_or_create( - config_entry_id=entry.entry_id, - identifiers={(DOMAIN, str(devices.modem.address))}, - manufacturer="Smart Home", - name=f"{devices.modem.description} {devices.modem.address}", - model=f"{devices.modem.model} ({devices.modem.cat!r}, 0x{devices.modem.subcat:02x})", - sw_version=f"{devices.modem.firmware:02x} Engine Version: {devices.modem.engine_version}", - ) + create_insteon_device(hass, devices.modem, entry.entry_id) api.async_load_api(hass) + await api.async_register_insteon_frontend(hass) asyncio.create_task(async_get_device_config(hass, entry)) return True + + +def create_insteon_device(hass, device, config_entry_id): + """Create an Insteon device.""" + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=config_entry_id, # entry.entry_id, + identifiers={(DOMAIN, str(device.address))}, + manufacturer="SmartLabs, Inc", + name=f"{device.description} {device.address}", + model=f"{device.model} ({device.cat!r}, 0x{device.subcat:02x})", + sw_version=f"{device.firmware:02x} Engine Version: {device.engine_version}", + ) diff --git a/homeassistant/components/insteon/api/__init__.py b/homeassistant/components/insteon/api/__init__.py index 3b786a38343..71dd1a0463e 100644 --- a/homeassistant/components/insteon/api/__init__.py +++ b/homeassistant/components/insteon/api/__init__.py @@ -1,7 +1,10 @@ """Insteon API interface for the frontend.""" -from homeassistant.components import websocket_api -from homeassistant.core import callback +from insteon_frontend import get_build_id, locate_dir + +from homeassistant.components import panel_custom, websocket_api +from homeassistant.components.insteon.const import CONF_DEV_PATH, DOMAIN +from homeassistant.core import HomeAssistant, callback from .aldb import ( websocket_add_default_links, @@ -13,7 +16,11 @@ from .aldb import ( websocket_reset_aldb, websocket_write_aldb, ) -from .device import websocket_get_device +from .device import ( + websocket_add_device, + websocket_cancel_add_device, + websocket_get_device, +) from .properties import ( websocket_change_properties_record, websocket_get_properties, @@ -22,11 +29,15 @@ from .properties import ( websocket_write_properties, ) +URL_BASE = "/insteon_static" + @callback def async_load_api(hass): """Set up the web socket API.""" websocket_api.async_register_command(hass, websocket_get_device) + websocket_api.async_register_command(hass, websocket_add_device) + websocket_api.async_register_command(hass, websocket_cancel_add_device) websocket_api.async_register_command(hass, websocket_get_aldb) websocket_api.async_register_command(hass, websocket_change_aldb_record) @@ -42,3 +53,31 @@ def async_load_api(hass): websocket_api.async_register_command(hass, websocket_write_properties) websocket_api.async_register_command(hass, websocket_load_properties) websocket_api.async_register_command(hass, websocket_reset_properties) + + +def get_entrypoint(is_dev): + """Get the entry point for the frontend.""" + if is_dev: + return "entrypoint.js" + + +async def async_register_insteon_frontend(hass: HomeAssistant): + """Register the Insteon frontend configuration panel.""" + # Add to sidepanel if needed + if DOMAIN not in hass.data.get("frontend_panels", {}): + dev_path = hass.data.get(DOMAIN, {}).get(CONF_DEV_PATH) + is_dev = dev_path is not None + path = dev_path if dev_path else locate_dir() + build_id = get_build_id(is_dev) + hass.http.register_static_path(URL_BASE, path, cache_headers=not is_dev) + + await panel_custom.async_register_panel( + hass=hass, + frontend_url_path=DOMAIN, + webcomponent_name="insteon-frontend", + sidebar_title=DOMAIN.capitalize(), + sidebar_icon="mdi:power", + module_url=f"{URL_BASE}/entrypoint-{build_id}.js", + embed_iframe=True, + require_admin=True, + ) diff --git a/homeassistant/components/insteon/api/device.py b/homeassistant/components/insteon/api/device.py index 1071451876b..beef78394fa 100644 --- a/homeassistant/components/insteon/api/device.py +++ b/homeassistant/components/insteon/api/device.py @@ -1,17 +1,20 @@ """API interface to get an Insteon device.""" from pyinsteon import devices +from pyinsteon.constants import DeviceAction import voluptuous as vol from homeassistant.components import websocket_api -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from ..const import ( + DEVICE_ADDRESS, DEVICE_ID, DOMAIN, HA_DEVICE_NOT_FOUND, ID, INSTEON_DEVICE_NOT_FOUND, + MULTIPLE, TYPE, ) @@ -21,6 +24,12 @@ def compute_device_name(ha_device): return ha_device.name_by_user if ha_device.name_by_user else ha_device.name +async def async_add_devices(address, multiple): + """Add one or more Insteon devices.""" + async for _ in devices.async_add_device(address=address, multiple=multiple): + pass + + def get_insteon_device_from_ha_device(ha_device): """Return the Insteon device from an HA device.""" for identifier in ha_device.identifiers: @@ -74,3 +83,58 @@ async def websocket_get_device( "aldb_status": str(device.aldb.status), } connection.send_result(msg[ID], device_info) + + +@websocket_api.websocket_command( + { + vol.Required(TYPE): "insteon/device/add", + vol.Required(MULTIPLE): bool, + vol.Optional(DEVICE_ADDRESS): str, + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def websocket_add_device( + hass: HomeAssistant, + connection: websocket_api.connection.ActiveConnection, + msg: dict, +) -> None: + """Add one or more Insteon devices.""" + + @callback + def linking_complete(address: str, action: DeviceAction): + """Forward device events to websocket.""" + if action == DeviceAction.COMPLETED: + forward_data = {"type": "linking_stopped", "address": ""} + else: + return + connection.send_message(websocket_api.event_message(msg["id"], forward_data)) + + @callback + def async_cleanup() -> None: + """Remove signal listeners.""" + devices.unsubscribe(linking_complete) + + connection.subscriptions[msg["id"]] = async_cleanup + devices.subscribe(linking_complete) + + async for address in devices.async_add_device( + address=msg.get(DEVICE_ADDRESS), multiple=msg[MULTIPLE] + ): + forward_data = {"type": "device_added", "address": str(address)} + connection.send_message(websocket_api.event_message(msg["id"], forward_data)) + + connection.send_result(msg[ID]) + + +@websocket_api.websocket_command({vol.Required(TYPE): "insteon/device/add/cancel"}) +@websocket_api.require_admin +@websocket_api.async_response +async def websocket_cancel_add_device( + hass: HomeAssistant, + connection: websocket_api.connection.ActiveConnection, + msg: dict, +) -> None: + """Cancel the Insteon all-linking process.""" + await devices.async_cancel_all_linking() + connection.send_result(msg[ID]) diff --git a/homeassistant/components/insteon/api/properties.py b/homeassistant/components/insteon/api/properties.py index 6ec12a5fd89..47def71c1ab 100644 --- a/homeassistant/components/insteon/api/properties.py +++ b/homeassistant/components/insteon/api/properties.py @@ -1,17 +1,15 @@ """Property update methods and schemas.""" -from itertools import chain from pyinsteon import devices -from pyinsteon.constants import RAMP_RATES, ResponseStatus -from pyinsteon.device_types.device_base import Device -from pyinsteon.extended_property import ( - NON_TOGGLE_MASK, - NON_TOGGLE_ON_OFF_MASK, - OFF_MASK, - ON_MASK, - RAMP_RATE, +from pyinsteon.config import RADIO_BUTTON_GROUPS, RAMP_RATE_IN_SEC, get_usable_value +from pyinsteon.constants import ( + RAMP_RATES_SEC, + PropertyType, + RelayMode, + ResponseStatus, + ToggleMode, ) -from pyinsteon.utils import ramp_rate_to_seconds, seconds_to_ramp_rate +from pyinsteon.device_types.device_base import Device import voluptuous as vol import voluptuous_serialize @@ -29,19 +27,12 @@ from ..const import ( ) from .device import notify_device_not_found -TOGGLE_ON_OFF_MODE = "toggle_on_off_mode" -NON_TOGGLE_ON_MODE = "non_toggle_on_mode" -NON_TOGGLE_OFF_MODE = "non_toggle_off_mode" -RADIO_BUTTON_GROUP_PROP = "radio_button_group_" -TOGGLE_PROP = "toggle_" -RAMP_RATE_SECONDS = list(dict.fromkeys(RAMP_RATES.values())) +SHOW_ADVANCED = "show_advanced" +RAMP_RATE_SECONDS = list(dict.fromkeys(RAMP_RATES_SEC)) RAMP_RATE_SECONDS.sort() -TOGGLE_MODES = {TOGGLE_ON_OFF_MODE: 0, NON_TOGGLE_ON_MODE: 1, NON_TOGGLE_OFF_MODE: 2} -TOGGLE_MODES_SCHEMA = { - 0: TOGGLE_ON_OFF_MODE, - 1: NON_TOGGLE_ON_MODE, - 2: NON_TOGGLE_OFF_MODE, -} +RAMP_RATE_LIST = [str(seconds) for seconds in RAMP_RATE_SECONDS] +TOGGLE_MODES = [str(ToggleMode(v)).lower() for v in list(ToggleMode)] +RELAY_MODES = [str(RelayMode(v)).lower() for v in list(RelayMode)] def _bool_schema(name): @@ -52,239 +43,116 @@ def _byte_schema(name): return voluptuous_serialize.convert(vol.Schema({vol.Required(name): cv.byte}))[0] -def _ramp_rate_schema(name): +def _float_schema(name): + return voluptuous_serialize.convert(vol.Schema({vol.Required(name): float}))[0] + + +def _list_schema(name, values): return voluptuous_serialize.convert( - vol.Schema({vol.Required(name): vol.In(RAMP_RATE_SECONDS)}), + vol.Schema({vol.Required(name): vol.In(values)}), custom_serializer=cv.custom_serializer, )[0] -def get_properties(device: Device): +def _multi_select_schema(name, values): + return voluptuous_serialize.convert( + vol.Schema({vol.Optional(name): cv.multi_select(values)}), + custom_serializer=cv.custom_serializer, + )[0] + + +def _read_only_schema(name, value): + """Return a constant value schema.""" + return voluptuous_serialize.convert(vol.Schema({vol.Required(name): value}))[0] + + +def get_schema(prop, name, groups): + """Return the correct shema type.""" + if prop.is_read_only: + return _read_only_schema(name, prop.value) + if name == RAMP_RATE_IN_SEC: + return _list_schema(name, RAMP_RATE_LIST) + if name == RADIO_BUTTON_GROUPS: + button_list = {str(group): groups[group].name for group in groups if group != 1} + return _multi_select_schema(name, button_list) + if prop.value_type == bool: + return _bool_schema(name) + if prop.value_type == int: + return _byte_schema(name) + if prop.value_type == float: + return _float_schema(name) + if prop.value_type == ToggleMode: + return _list_schema(name, TOGGLE_MODES) + if prop.value_type == RelayMode: + return _list_schema(name, RELAY_MODES) + return None + + +def get_properties(device: Device, show_advanced=False): """Get the properties of an Insteon device and return the records and schema.""" properties = [] schema = {} - # Limit the properties we manage at this time. - for prop_name in device.operating_flags: - if not device.operating_flags[prop_name].is_read_only: - prop_dict, schema_dict = _get_property(device.operating_flags[prop_name]) - properties.append(prop_dict) - schema[prop_name] = schema_dict - - mask_found = False - for prop_name in device.properties: - if device.properties[prop_name].is_read_only: + for name, prop in device.configuration.items(): + if prop.is_read_only and not show_advanced: continue - if prop_name == RAMP_RATE: - rr_prop, rr_schema = _get_ramp_rate_property(device.properties[prop_name]) - properties.append(rr_prop) - schema[RAMP_RATE] = rr_schema + prop_schema = get_schema(prop, name, device.groups) + if name == "momentary_delay": + print(prop_schema) + if prop_schema is None: + continue + schema[name] = prop_schema + properties.append(property_to_dict(prop)) - elif not mask_found and "mask" in prop_name: - mask_found = True - toggle_props, toggle_schema = _get_toggle_properties(device) - properties.extend(toggle_props) - schema.update(toggle_schema) - - rb_props, rb_schema = _get_radio_button_properties(device) - properties.extend(rb_props) - schema.update(rb_schema) - else: - prop_dict, schema_dict = _get_property(device.properties[prop_name]) - properties.append(prop_dict) - schema[prop_name] = schema_dict + if show_advanced: + for name, prop in device.operating_flags.items(): + if prop.property_type != PropertyType.ADVANCED: + continue + prop_schema = get_schema(prop, name, device.groups) + if prop_schema is not None: + schema[name] = prop_schema + properties.append(property_to_dict(prop)) + for name, prop in device.properties.items(): + if prop.property_type != PropertyType.ADVANCED: + continue + prop_schema = get_schema(prop, name, device.groups) + if prop_schema is not None: + schema[name] = prop_schema + properties.append(property_to_dict(prop)) return properties, schema -def set_property(device, prop_name: str, value): - """Update a property value.""" - if isinstance(value, bool) and prop_name in device.operating_flags: - device.operating_flags[prop_name].new_value = value - - elif prop_name == RAMP_RATE: - device.properties[prop_name].new_value = seconds_to_ramp_rate(value) - - elif prop_name.startswith(RADIO_BUTTON_GROUP_PROP): - buttons = [int(button) for button in value] - rb_groups = _calc_radio_button_groups(device) - curr_group = int(prop_name[len(RADIO_BUTTON_GROUP_PROP) :]) - if len(rb_groups) > curr_group: - removed = [btn for btn in rb_groups[curr_group] if btn not in buttons] - if removed: - device.clear_radio_buttons(removed) - if buttons: - device.set_radio_buttons(buttons) - - elif prop_name.startswith(TOGGLE_PROP): - button_name = prop_name[len(TOGGLE_PROP) :] - for button in device.groups: - if device.groups[button].name == button_name: - device.set_toggle_mode(button, int(value)) - - else: - device.properties[prop_name].new_value = value - - -def _get_property(prop): +def property_to_dict(prop): """Return a property data row.""" - value, modified = _get_usable_value(prop) + value = get_usable_value(prop) + modified = value == prop.new_value + if prop.value_type in [ToggleMode, RelayMode] or prop.name == RAMP_RATE_IN_SEC: + value = str(value).lower() prop_dict = {"name": prop.name, "value": value, "modified": modified} - if isinstance(prop.value, bool): - schema = _bool_schema(prop.name) + return prop_dict + + +def update_property(device, prop_name, value): + """Update the value of a device property.""" + prop = device.configuration[prop_name] + if prop.value_type == ToggleMode: + toggle_mode = getattr(ToggleMode, value.upper()) + prop.new_value = toggle_mode + elif prop.value_type == RelayMode: + relay_mode = getattr(RelayMode, value.upper()) + prop.new_value = relay_mode else: - schema = _byte_schema(prop.name) - return prop_dict, {"name": prop.name, **schema} - - -def _get_toggle_properties(device): - """Generate the mask properties for a KPL device.""" - props = [] - schema = {} - toggle_prop = device.properties[NON_TOGGLE_MASK] - toggle_on_prop = device.properties[NON_TOGGLE_ON_OFF_MASK] - for button in device.groups: - name = f"{TOGGLE_PROP}{device.groups[button].name}" - value, modified = _toggle_button_value(toggle_prop, toggle_on_prop, button) - props.append({"name": name, "value": value, "modified": modified}) - toggle_schema = vol.Schema({vol.Required(name): vol.In(TOGGLE_MODES_SCHEMA)}) - toggle_schema_dict = voluptuous_serialize.convert( - toggle_schema, custom_serializer=cv.custom_serializer - ) - schema[name] = toggle_schema_dict[0] - return props, schema - - -def _toggle_button_value(non_toggle_prop, toggle_on_prop, button): - """Determine the toggle value of a button.""" - toggle_mask, toggle_modified = _get_usable_value(non_toggle_prop) - toggle_on_mask, toggle_on_modified = _get_usable_value(toggle_on_prop) - - bit = button - 1 - if not toggle_mask & 1 << bit: - value = 0 - else: - if toggle_on_mask & 1 << bit: - value = 1 - else: - value = 2 - - modified = False - if toggle_modified: - curr_bit = non_toggle_prop.value & 1 << bit - new_bit = non_toggle_prop.new_value & 1 << bit - modified = not curr_bit == new_bit - - if not modified and value != 0 and toggle_on_modified: - curr_bit = toggle_on_prop.value & 1 << bit - new_bit = toggle_on_prop.new_value & 1 << bit - modified = not curr_bit == new_bit - - return value, modified - - -def _get_radio_button_properties(device): - """Return the values and schema to set KPL buttons as radio buttons.""" - rb_groups = _calc_radio_button_groups(device) - props = [] - schema = {} - index = 0 - remaining_buttons = [] - - buttons_in_groups = list(chain.from_iterable(rb_groups)) - - # Identify buttons not belonging to any group - for button in device.groups: - if button not in buttons_in_groups: - remaining_buttons.append(button) - - for rb_group in rb_groups: - name = f"{RADIO_BUTTON_GROUP_PROP}{index}" - button_1 = rb_group[0] - button_str = f"_{button_1}" if button_1 != 1 else "" - on_mask = device.properties[f"{ON_MASK}{button_str}"] - off_mask = device.properties[f"{OFF_MASK}{button_str}"] - modified = on_mask.is_dirty or off_mask.is_dirty - - props.append( - { - "name": name, - "modified": modified, - "value": rb_group, - } - ) - - options = { - button: device.groups[button].name - for button in chain.from_iterable([rb_group, remaining_buttons]) - } - rb_schema = vol.Schema({vol.Optional(name): cv.multi_select(options)}) - - rb_schema_dict = voluptuous_serialize.convert( - rb_schema, custom_serializer=cv.custom_serializer - ) - schema[name] = rb_schema_dict[0] - - index += 1 - - if len(remaining_buttons) > 1: - name = f"{RADIO_BUTTON_GROUP_PROP}{index}" - - props.append( - { - "name": name, - "modified": False, - "value": [], - } - ) - - options = {button: device.groups[button].name for button in remaining_buttons} - rb_schema = vol.Schema({vol.Optional(name): cv.multi_select(options)}) - - rb_schema_dict = voluptuous_serialize.convert( - rb_schema, custom_serializer=cv.custom_serializer - ) - schema[name] = rb_schema_dict[0] - - return props, schema - - -def _calc_radio_button_groups(device): - """Return existing radio button groups.""" - rb_groups = [] - for button in device.groups: - if button not in list(chain.from_iterable(rb_groups)): - button_str = "" if button == 1 else f"_{button}" - on_mask, _ = _get_usable_value(device.properties[f"{ON_MASK}{button_str}"]) - if on_mask != 0: - rb_group = [button] - for bit in list(range(0, button - 1)) + list(range(button, 8)): - if on_mask & 1 << bit: - rb_group.append(bit + 1) - if len(rb_group) > 1: - rb_groups.append(rb_group) - return rb_groups - - -def _get_ramp_rate_property(prop): - """Return the value and schema of a ramp rate property.""" - rr_prop, _ = _get_property(prop) - rr_prop["value"] = ramp_rate_to_seconds(rr_prop["value"]) - return rr_prop, _ramp_rate_schema(prop.name) - - -def _get_usable_value(prop): - """Return the current or the modified value of a property.""" - value = prop.value if prop.new_value is None else prop.new_value - return value, prop.is_dirty + prop.new_value = value @websocket_api.websocket_command( { vol.Required(TYPE): "insteon/properties/get", vol.Required(DEVICE_ADDRESS): str, + vol.Required(SHOW_ADVANCED): bool, } ) @websocket_api.require_admin @@ -299,7 +167,7 @@ async def websocket_get_properties( notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND) return - properties, schema = get_properties(device) + properties, schema = get_properties(device, msg[SHOW_ADVANCED]) connection.send_result(msg[ID], {"properties": properties, "schema": schema}) @@ -324,7 +192,7 @@ async def websocket_change_properties_record( notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND) return - set_property(device, msg[PROPERTY_NAME], msg[PROPERTY_VALUE]) + update_property(device, msg[PROPERTY_NAME], msg[PROPERTY_VALUE]) connection.send_result(msg[ID]) @@ -346,10 +214,9 @@ async def websocket_write_properties( notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND) return - result1 = await device.async_write_op_flags() - result2 = await device.async_write_ext_properties() + result = await device.async_write_config() await devices.async_save(workdir=hass.config.config_dir) - if result1 != ResponseStatus.SUCCESS or result2 != ResponseStatus.SUCCESS: + if result != ResponseStatus.SUCCESS: connection.send_message( websocket_api.error_message( msg[ID], "write_failed", "properties not written to device" @@ -377,10 +244,9 @@ async def websocket_load_properties( notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND) return - result1 = await device.async_read_op_flags() - result2 = await device.async_read_ext_properties() + result, _ = await device.async_read_config(read_aldb=False) await devices.async_save(workdir=hass.config.config_dir) - if result1 != ResponseStatus.SUCCESS or result2 != ResponseStatus.SUCCESS: + if result != ResponseStatus.SUCCESS: connection.send_message( websocket_api.error_message( msg[ID], "load_failed", "properties not loaded from device" diff --git a/homeassistant/components/insteon/climate.py b/homeassistant/components/insteon/climate.py index e393c6eea0a..833180583e2 100644 --- a/homeassistant/components/insteon/climate.py +++ b/homeassistant/components/insteon/climate.py @@ -1,8 +1,8 @@ """Support for Insteon thermostat.""" from __future__ import annotations +from pyinsteon.config import CELSIUS from pyinsteon.constants import ThermostatMode -from pyinsteon.operating_flag import CELSIUS from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( diff --git a/homeassistant/components/insteon/const.py b/homeassistant/components/insteon/const.py index bc3eaf6234b..fb7b2387d73 100644 --- a/homeassistant/components/insteon/const.py +++ b/homeassistant/components/insteon/const.py @@ -70,6 +70,7 @@ CONF_DIM_STEPS = "dim_steps" CONF_X10_ALL_UNITS_OFF = "x10_all_units_off" CONF_X10_ALL_LIGHTS_ON = "x10_all_lights_on" CONF_X10_ALL_LIGHTS_OFF = "x10_all_lights_off" +CONF_DEV_PATH = "dev_path" PORT_HUB_V1 = 9761 PORT_HUB_V2 = 25105 @@ -172,5 +173,6 @@ PROPERTY_NAME = "name" PROPERTY_VALUE = "value" HA_DEVICE_NOT_FOUND = "ha_device_not_found" INSTEON_DEVICE_NOT_FOUND = "insteon_device_not_found" +MULTIPLE = "multiple" INSTEON_ADDR_REGEX = re.compile(r"([A-Fa-f0-9]{2}\.?[A-Fa-f0-9]{2}\.?[A-Fa-f0-9]{2})$") diff --git a/homeassistant/components/insteon/insteon_entity.py b/homeassistant/components/insteon/insteon_entity.py index d8fd9b2cbc9..60935f3f951 100644 --- a/homeassistant/components/insteon/insteon_entity.py +++ b/homeassistant/components/insteon/insteon_entity.py @@ -85,7 +85,7 @@ class InsteonEntity(Entity): """Return device information.""" return DeviceInfo( identifiers={(DOMAIN, str(self._insteon_device.address))}, - manufacturer="Smart Home", + manufacturer="SmartLabs, Inc", model=f"{self._insteon_device.model} ({self._insteon_device.cat!r}, 0x{self._insteon_device.subcat:02x})", name=f"{self._insteon_device.description} {self._insteon_device.address}", sw_version=f"{self._insteon_device.firmware:02x} Engine Version: {self._insteon_device.engine_version}", diff --git a/homeassistant/components/insteon/light.py b/homeassistant/components/insteon/light.py index 0b1fd2270e8..05ad9794042 100644 --- a/homeassistant/components/insteon/light.py +++ b/homeassistant/components/insteon/light.py @@ -1,5 +1,5 @@ """Support for Insteon lights via PowerLinc Modem.""" -from pyinsteon.extended_property import ON_LEVEL +from pyinsteon.config import ON_LEVEL from homeassistant.components.light import ( ATTR_BRIGHTNESS, diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index d9e1f1bfb18..ad5736f4d53 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -2,13 +2,17 @@ "domain": "insteon", "name": "Insteon", "documentation": "https://www.home-assistant.io/integrations/insteon", - "requirements": ["pyinsteon==1.0.13"], + "dependencies": ["http", "websocket_api"], + "requirements": [ + "pyinsteon==1.1.0b1", + "insteon-frontend-home-assistant==0.1.0" + ], "codeowners": ["@teharris1"], "dhcp": [{ "macaddress": "000EF3*" }, { "registered_devices": true }], "config_flow": true, "iot_class": "local_push", "loggers": ["pyinsteon", "pypubsub"], - "after_dependencies": ["usb"], + "after_dependencies": ["panel_custom", "usb"], "usb": [ { "vid": "10BF" diff --git a/homeassistant/components/insteon/schemas.py b/homeassistant/components/insteon/schemas.py index 09315919052..6bcde545e34 100644 --- a/homeassistant/components/insteon/schemas.py +++ b/homeassistant/components/insteon/schemas.py @@ -22,6 +22,7 @@ import homeassistant.helpers.config_validation as cv from .const import ( CONF_CAT, + CONF_DEV_PATH, CONF_DIM_STEPS, CONF_FIRMWARE, CONF_HOUSECODE, @@ -121,6 +122,7 @@ CONFIG_SCHEMA = vol.Schema( vol.Optional(CONF_X10): vol.All( cv.ensure_list_csv, [CONF_X10_SCHEMA] ), + vol.Optional(CONF_DEV_PATH): cv.string, }, extra=vol.ALLOW_EXTRA, required=True, diff --git a/homeassistant/components/insteon/utils.py b/homeassistant/components/insteon/utils.py index 1599975f462..03647559345 100644 --- a/homeassistant/components/insteon/utils.py +++ b/homeassistant/components/insteon/utils.py @@ -4,7 +4,7 @@ import logging from pyinsteon import devices from pyinsteon.address import Address -from pyinsteon.constants import ALDBStatus +from pyinsteon.constants import ALDBStatus, DeviceAction from pyinsteon.events import OFF_EVENT, OFF_FAST_EVENT, ON_EVENT, ON_FAST_EVENT from pyinsteon.managers.link_manager import ( async_enter_linking_mode, @@ -137,9 +137,10 @@ def register_new_device_callback(hass): """Register callback for new Insteon device.""" @callback - def async_new_insteon_device(address=None): + def async_new_insteon_device(address, action: DeviceAction): """Detect device from transport to be delegated to platform.""" - hass.async_create_task(async_create_new_entities(address)) + if action == DeviceAction.ADDED: + hass.async_create_task(async_create_new_entities(address)) async def async_create_new_entities(address): _LOGGER.debug( diff --git a/requirements_all.txt b/requirements_all.txt index 601f8a5fcf4..c7fcbd54144 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -881,6 +881,9 @@ influxdb-client==1.24.0 # homeassistant.components.influxdb influxdb==5.3.1 +# homeassistant.components.insteon +insteon-frontend-home-assistant==0.1.0 + # homeassistant.components.intellifire intellifire4py==1.0.2 @@ -1544,7 +1547,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.0.13 +pyinsteon==1.1.0b1 # homeassistant.components.intesishome pyintesishome==1.7.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c8f37153330..824e843639d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -618,6 +618,9 @@ influxdb-client==1.24.0 # homeassistant.components.influxdb influxdb==5.3.1 +# homeassistant.components.insteon +insteon-frontend-home-assistant==0.1.0 + # homeassistant.components.intellifire intellifire4py==1.0.2 @@ -1026,7 +1029,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.0.13 +pyinsteon==1.1.0b1 # homeassistant.components.ipma pyipma==2.0.5 diff --git a/tests/components/insteon/fixtures/iolinc_properties.json b/tests/components/insteon/fixtures/iolinc_properties.json new file mode 100644 index 00000000000..904ba054b7a --- /dev/null +++ b/tests/components/insteon/fixtures/iolinc_properties.json @@ -0,0 +1,18 @@ +{ + "operating_flags": { + "program_lock_on": false, + "blink_on_tx_on": true, + "relay_on_sense_on": false, + "momentary_on": true, + "momentary_on_off_trigger": false, + "x10_off": true, + "sense_sends_off": true, + "momentary_follow_sense": false + }, + "properties": { + "prescaler": 1, + "delay": 50, + "x10_house": 32, + "x10_unit": 0 + } +} diff --git a/tests/components/insteon/mock_devices.py b/tests/components/insteon/mock_devices.py index e28e25bf41b..6e6a8eccfcc 100644 --- a/tests/components/insteon/mock_devices.py +++ b/tests/components/insteon/mock_devices.py @@ -1,15 +1,20 @@ """Mock devices object to test Insteon.""" + +import asyncio from unittest.mock import AsyncMock, MagicMock from pyinsteon.address import Address from pyinsteon.constants import ALDBStatus, ResponseStatus from pyinsteon.device_types import ( DimmableLightingControl_KeypadLinc_8, - GeneralController, + GeneralController_RemoteLinc, Hub, + SensorsActuators_IOLink, SwitchedLightingControl_SwitchLinc, ) from pyinsteon.managers.saved_devices_manager import dict_to_aldb_record +from pyinsteon.topics import DEVICE_LIST_CHANGED +from pyinsteon.utils import subscribe_topic class MockSwitchLinc(SwitchedLightingControl_SwitchLinc): @@ -31,7 +36,10 @@ class MockDevices: self._connected = connected self.async_save = AsyncMock() self.add_x10_device = MagicMock() + self.async_read_config = AsyncMock() self.set_id = MagicMock() + self.async_add_device_called_with = {} + self.async_cancel_all_linking = AsyncMock() def __getitem__(self, address): """Return a a device from the device address.""" @@ -56,18 +64,24 @@ class MockDevices: addr1 = Address("11.11.11") addr2 = Address("22.22.22") addr3 = Address("33.33.33") + addr4 = Address("44.44.44") self._devices[addr0] = Hub(addr0, 0x03, 0x00, 0x00, "Hub AA.AA.AA", "0") self._devices[addr1] = MockSwitchLinc( addr1, 0x02, 0x00, 0x00, "Device 11.11.11", "1" ) - self._devices[addr2] = GeneralController( + self._devices[addr2] = GeneralController_RemoteLinc( addr2, 0x00, 0x00, 0x00, "Device 22.22.22", "2" ) self._devices[addr3] = DimmableLightingControl_KeypadLinc_8( addr3, 0x02, 0x00, 0x00, "Device 33.33.33", "3" ) + self._devices[addr4] = SensorsActuators_IOLink( + addr4, 0x07, 0x00, 0x00, "Device 44.44.44", "4" + ) - for device in [self._devices[addr] for addr in [addr1, addr2, addr3]]: + for device in [ + self._devices[addr] for addr in [addr1, addr2, addr3, addr4] + ]: device.async_read_config = AsyncMock() device.aldb.async_write = AsyncMock() device.aldb.async_load = AsyncMock() @@ -85,7 +99,7 @@ class MockDevices: return_value=ResponseStatus.SUCCESS ) - for device in [self._devices[addr] for addr in [addr2, addr3]]: + for device in [self._devices[addr] for addr in [addr2, addr3, addr4]]: device.async_status = AsyncMock() self._devices[addr1].async_status = AsyncMock(side_effect=AttributeError) self._devices[addr0].aldb.async_load = AsyncMock() @@ -104,6 +118,7 @@ class MockDevices: ) self.modem = self._devices[addr0] + self.modem.async_read_config = AsyncMock() def fill_aldb(self, address, records): """Fill the All-Link Database for a device.""" @@ -126,3 +141,18 @@ class MockDevices: value = properties[flag] if device.properties.get(flag): device.properties[flag].load(value) + + async def async_add_device(self, address=None, multiple=False): + """Mock the async_add_device method.""" + self.async_add_device_called_with = {"address": address, "multiple": multiple} + if multiple: + yield "aa.bb.cc" + await asyncio.sleep(0.01) + yield "bb.cc.dd" + if address: + yield address + await asyncio.sleep(0.01) + + def subscribe(self, listener): + """Mock the subscribe function.""" + subscribe_topic(listener, DEVICE_LIST_CHANGED) diff --git a/tests/components/insteon/test_api_device.py b/tests/components/insteon/test_api_device.py index 528d44cc691..49588c6ea8f 100644 --- a/tests/components/insteon/test_api_device.py +++ b/tests/components/insteon/test_api_device.py @@ -1,6 +1,11 @@ """Test the device level APIs.""" +import asyncio from unittest.mock import patch +from pyinsteon.constants import DeviceAction +from pyinsteon.topics import DEVICE_LIST_CHANGED +from pyinsteon.utils import publish_topic + from homeassistant.components import insteon from homeassistant.components.insteon.api import async_load_api from homeassistant.components.insteon.api.device import ( @@ -11,7 +16,7 @@ from homeassistant.components.insteon.api.device import ( TYPE, async_device_name, ) -from homeassistant.components.insteon.const import DOMAIN +from homeassistant.components.insteon.const import DOMAIN, MULTIPLE from homeassistant.helpers.device_registry import async_get_registry from .const import MOCK_USER_INPUT_PLM @@ -137,3 +142,47 @@ async def test_get_ha_device_name(hass, hass_ws_client): # Test no HA or Insteon device name = await async_device_name(device_reg, "BB.BB.BB") assert name == "" + + +async def test_add_device_api(hass, hass_ws_client): + """Test adding an Insteon device.""" + + ws_client, devices, _, _ = await _async_setup(hass, hass_ws_client) + with patch.object(insteon.api.device, "devices", devices): + await ws_client.send_json({ID: 2, TYPE: "insteon/device/add", MULTIPLE: True}) + + await asyncio.sleep(0.01) + assert devices.async_add_device_called_with.get("address") is None + assert devices.async_add_device_called_with["multiple"] is True + + msg = await ws_client.receive_json() + assert msg["event"]["type"] == "device_added" + assert msg["event"]["address"] == "aa.bb.cc" + + msg = await ws_client.receive_json() + assert msg["event"]["type"] == "device_added" + assert msg["event"]["address"] == "bb.cc.dd" + + publish_topic( + DEVICE_LIST_CHANGED, + address=None, + action=DeviceAction.COMPLETED, + ) + msg = await ws_client.receive_json() + assert msg["event"]["type"] == "linking_stopped" + + +async def test_cancel_add_device(hass, hass_ws_client): + """Test cancelling adding of a new device.""" + + ws_client, devices, _, _ = await _async_setup(hass, hass_ws_client) + + with patch.object(insteon.api.aldb, "devices", devices): + await ws_client.send_json( + { + ID: 2, + TYPE: "insteon/device/add/cancel", + } + ) + msg = await ws_client.receive_json() + assert msg["success"] diff --git a/tests/components/insteon/test_api_properties.py b/tests/components/insteon/test_api_properties.py index 683e687ec85..7211402e343 100644 --- a/tests/components/insteon/test_api_properties.py +++ b/tests/components/insteon/test_api_properties.py @@ -1,8 +1,11 @@ """Test the Insteon properties APIs.""" import json -from unittest.mock import patch +from unittest.mock import AsyncMock, patch +from pyinsteon.config import MOMENTARY_DELAY, RELAY_MODE, TOGGLE_BUTTON +from pyinsteon.config.extended_property import ExtendedProperty +from pyinsteon.constants import RelayMode, ToggleMode import pytest from homeassistant.components import insteon @@ -11,19 +14,12 @@ from homeassistant.components.insteon.api.device import INSTEON_DEVICE_NOT_FOUND from homeassistant.components.insteon.api.properties import ( DEVICE_ADDRESS, ID, - NON_TOGGLE_MASK, - NON_TOGGLE_OFF_MODE, - NON_TOGGLE_ON_MODE, - NON_TOGGLE_ON_OFF_MASK, PROPERTY_NAME, PROPERTY_VALUE, - RADIO_BUTTON_GROUP_PROP, - TOGGLE_MODES, - TOGGLE_ON_OFF_MODE, - TOGGLE_PROP, + RADIO_BUTTON_GROUPS, + RAMP_RATE_IN_SEC, + SHOW_ADVANCED, TYPE, - _get_radio_button_properties, - _get_toggle_properties, ) from .mock_devices import MockDevices @@ -31,43 +27,172 @@ from .mock_devices import MockDevices from tests.common import load_fixture -@pytest.fixture(name="properties_data", scope="session") -def aldb_data_fixture(): +@pytest.fixture(name="kpl_properties_data", scope="session") +def kpl_properties_data_fixture(): """Load the controller state fixture data.""" return json.loads(load_fixture("insteon/kpl_properties.json")) -async def _setup(hass, hass_ws_client, properties_data): +@pytest.fixture(name="iolinc_properties_data", scope="session") +def iolinc_properties_data_fixture(): + """Load the controller state fixture data.""" + return json.loads(load_fixture("insteon/iolinc_properties.json")) + + +async def _setup(hass, hass_ws_client, address, properties_data): """Set up tests.""" ws_client = await hass_ws_client(hass) devices = MockDevices() await devices.async_load() - devices.fill_properties("33.33.33", properties_data) + devices.fill_properties(address, properties_data) async_load_api(hass) return ws_client, devices -async def test_get_properties(hass, hass_ws_client, properties_data): +async def test_get_properties( + hass, hass_ws_client, kpl_properties_data, iolinc_properties_data +): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) - - with patch.object(insteon.api.properties, "devices", devices): - await ws_client.send_json( - {ID: 2, TYPE: "insteon/properties/get", DEVICE_ADDRESS: "33.33.33"} - ) - msg = await ws_client.receive_json() - assert msg["success"] - assert len(msg["result"]["properties"]) == 54 - - -async def test_change_operating_flag(hass, hass_ws_client, properties_data): - """Test changing an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) + devices.fill_properties("44.44.44", iolinc_properties_data) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { ID: 2, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "33.33.33", + SHOW_ADVANCED: False, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 18 + + await ws_client.send_json( + { + ID: 3, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: False, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 6 + + await ws_client.send_json( + { + ID: 4, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "33.33.33", + SHOW_ADVANCED: True, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 69 + + await ws_client.send_json( + { + ID: 5, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: True, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 14 + + +async def test_get_read_only_properties(hass, hass_ws_client, iolinc_properties_data): + """Test getting an Insteon device's properties.""" + mock_read_only = ExtendedProperty( + "44.44.44", "mock_read_only", bool, is_read_only=True + ) + mock_read_only.load(False) + + ws_client, devices = await _setup( + hass, hass_ws_client, "44.44.44", iolinc_properties_data + ) + device = devices["44.44.44"] + device.configuration["mock_read_only"] = mock_read_only + with patch.object(insteon.api.properties, "devices", devices): + await ws_client.send_json( + { + ID: 2, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: False, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 6 + await ws_client.send_json( + { + ID: 3, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: True, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 15 + + +async def test_get_unknown_properties(hass, hass_ws_client, iolinc_properties_data): + """Test getting an Insteon device's properties.""" + + class UnknownType: + """Mock unknown data type.""" + + mock_unknown = ExtendedProperty("44.44.44", "mock_unknown", UnknownType) + + ws_client, devices = await _setup( + hass, hass_ws_client, "44.44.44", iolinc_properties_data + ) + device = devices["44.44.44"] + device.configuration["mock_unknown"] = mock_unknown + with patch.object(insteon.api.properties, "devices", devices): + await ws_client.send_json( + { + ID: 2, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: False, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 6 + await ws_client.send_json( + { + ID: 3, + TYPE: "insteon/properties/get", + DEVICE_ADDRESS: "44.44.44", + SHOW_ADVANCED: True, + } + ) + msg = await ws_client.receive_json() + assert msg["success"] + assert len(msg["result"]["properties"]) == 14 + + +async def test_change_bool_property(hass, hass_ws_client, kpl_properties_data): + """Test changing a bool type properties.""" + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) + + with patch.object(insteon.api.properties, "devices", devices): + await ws_client.send_json( + { + ID: 3, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", PROPERTY_NAME: "led_off", @@ -79,29 +204,33 @@ async def test_change_operating_flag(hass, hass_ws_client, properties_data): assert devices["33.33.33"].operating_flags["led_off"].is_dirty -async def test_change_property(hass, hass_ws_client, properties_data): - """Test changing an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) +async def test_change_int_property(hass, hass_ws_client, kpl_properties_data): + """Test changing a int type properties.""" + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { - ID: 2, + ID: 4, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: "on_mask", + PROPERTY_NAME: "led_dimming", PROPERTY_VALUE: 100, } ) msg = await ws_client.receive_json() assert msg["success"] - assert devices["33.33.33"].properties["on_mask"].new_value == 100 - assert devices["33.33.33"].properties["on_mask"].is_dirty + assert devices["33.33.33"].properties["led_dimming"].new_value == 100 + assert devices["33.33.33"].properties["led_dimming"].is_dirty -async def test_change_ramp_rate_property(hass, hass_ws_client, properties_data): - """Test changing an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) +async def test_change_ramp_rate_property(hass, hass_ws_client, kpl_properties_data): + """Test changing an Insteon device's ramp rate properties.""" + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( @@ -109,7 +238,7 @@ async def test_change_ramp_rate_property(hass, hass_ws_client, properties_data): ID: 2, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: "ramp_rate", + PROPERTY_NAME: RAMP_RATE_IN_SEC, PROPERTY_VALUE: 4.5, } ) @@ -119,208 +248,126 @@ async def test_change_ramp_rate_property(hass, hass_ws_client, properties_data): assert devices["33.33.33"].properties["ramp_rate"].is_dirty -async def test_change_radio_button_group(hass, hass_ws_client, properties_data): +async def test_change_radio_button_group(hass, hass_ws_client, kpl_properties_data): """Test changing an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) - rb_props, schema = _get_radio_button_properties(devices["33.33.33"]) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) + rb_groups = devices["33.33.33"].configuration[RADIO_BUTTON_GROUPS] # Make sure the baseline is correct - assert rb_props[0]["name"] == f"{RADIO_BUTTON_GROUP_PROP}0" - assert rb_props[0]["value"] == [4, 5] - assert rb_props[1]["value"] == [7, 8] - assert rb_props[2]["value"] == [] - assert schema[f"{RADIO_BUTTON_GROUP_PROP}0"]["options"].get(1) - assert schema[f"{RADIO_BUTTON_GROUP_PROP}1"]["options"].get(1) - assert devices["33.33.33"].properties["on_mask"].value == 0 - assert devices["33.33.33"].properties["off_mask"].value == 0 - assert not devices["33.33.33"].properties["on_mask"].is_dirty - assert not devices["33.33.33"].properties["off_mask"].is_dirty + assert rb_groups.value[0] == [4, 5] + assert rb_groups.value[1] == [7, 8] # Add button 1 to the group - rb_props[0]["value"].append(1) + new_groups_1 = [[1, 4, 5], [7, 8]] with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { ID: 2, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: f"{RADIO_BUTTON_GROUP_PROP}0", - PROPERTY_VALUE: rb_props[0]["value"], + PROPERTY_NAME: RADIO_BUTTON_GROUPS, + PROPERTY_VALUE: new_groups_1, } ) msg = await ws_client.receive_json() assert msg["success"] + assert rb_groups.new_value[0] == [1, 4, 5] + assert rb_groups.new_value[1] == [7, 8] - new_rb_props, _ = _get_radio_button_properties(devices["33.33.33"]) - assert 1 in new_rb_props[0]["value"] - assert 4 in new_rb_props[0]["value"] - assert 5 in new_rb_props[0]["value"] - assert schema[f"{RADIO_BUTTON_GROUP_PROP}0"]["options"].get(1) - assert schema[f"{RADIO_BUTTON_GROUP_PROP}1"]["options"].get(1) - - assert devices["33.33.33"].properties["on_mask"].new_value == 0x18 - assert devices["33.33.33"].properties["off_mask"].new_value == 0x18 - assert devices["33.33.33"].properties["on_mask"].is_dirty - assert devices["33.33.33"].properties["off_mask"].is_dirty - - # Remove button 5 - rb_props[0]["value"].remove(5) + new_groups_2 = [[1, 4], [7, 8]] await ws_client.send_json( { ID: 3, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: f"{RADIO_BUTTON_GROUP_PROP}0", - PROPERTY_VALUE: rb_props[0]["value"], + PROPERTY_NAME: RADIO_BUTTON_GROUPS, + PROPERTY_VALUE: new_groups_2, } ) msg = await ws_client.receive_json() assert msg["success"] - - new_rb_props, _ = _get_radio_button_properties(devices["33.33.33"]) - assert 1 in new_rb_props[0]["value"] - assert 4 in new_rb_props[0]["value"] - assert 5 not in new_rb_props[0]["value"] - assert schema[f"{RADIO_BUTTON_GROUP_PROP}0"]["options"].get(1) - assert schema[f"{RADIO_BUTTON_GROUP_PROP}1"]["options"].get(1) - - assert devices["33.33.33"].properties["on_mask"].new_value == 0x08 - assert devices["33.33.33"].properties["off_mask"].new_value == 0x08 - assert devices["33.33.33"].properties["on_mask"].is_dirty - assert devices["33.33.33"].properties["off_mask"].is_dirty - - # Remove button group 1 - rb_props[1]["value"] = [] - await ws_client.send_json( - { - ID: 5, - TYPE: "insteon/properties/change", - DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: f"{RADIO_BUTTON_GROUP_PROP}1", - PROPERTY_VALUE: rb_props[1]["value"], - } - ) - msg = await ws_client.receive_json() - assert msg["success"] - - new_rb_props, _ = _get_radio_button_properties(devices["33.33.33"]) - assert len(new_rb_props) == 2 - assert new_rb_props[0]["value"] == [1, 4] - assert new_rb_props[1]["value"] == [] + assert rb_groups.new_value[0] == [1, 4] + assert rb_groups.new_value[1] == [7, 8] -async def test_create_radio_button_group(hass, hass_ws_client, properties_data): - """Test changing an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) - rb_props, _ = _get_radio_button_properties(devices["33.33.33"]) - - # Make sure the baseline is correct - assert len(rb_props) == 3 - - rb_props[0]["value"].append("1") - - with patch.object(insteon.api.properties, "devices", devices): - await ws_client.send_json( - { - ID: 2, - TYPE: "insteon/properties/change", - DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: f"{RADIO_BUTTON_GROUP_PROP}2", - PROPERTY_VALUE: ["1", "3"], - } - ) - msg = await ws_client.receive_json() - assert msg["success"] - - new_rb_props, new_schema = _get_radio_button_properties(devices["33.33.33"]) - assert len(new_rb_props) == 4 - assert 1 in new_rb_props[0]["value"] - assert new_schema[f"{RADIO_BUTTON_GROUP_PROP}0"]["options"].get(1) - assert not new_schema[f"{RADIO_BUTTON_GROUP_PROP}1"]["options"].get(1) - - assert devices["33.33.33"].properties["on_mask"].new_value == 4 - assert devices["33.33.33"].properties["off_mask"].new_value == 4 - assert devices["33.33.33"].properties["on_mask"].is_dirty - assert devices["33.33.33"].properties["off_mask"].is_dirty - - -async def test_change_toggle_property(hass, hass_ws_client, properties_data): +async def test_change_toggle_property(hass, hass_ws_client, kpl_properties_data): """Update a button's toggle mode.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) device = devices["33.33.33"] - toggle_props, _ = _get_toggle_properties(devices["33.33.33"]) - - # Make sure the baseline is correct - assert toggle_props[0]["name"] == f"{TOGGLE_PROP}{device.groups[1].name}" - assert toggle_props[0]["value"] == TOGGLE_MODES[TOGGLE_ON_OFF_MODE] - assert toggle_props[1]["value"] == TOGGLE_MODES[NON_TOGGLE_ON_MODE] - assert device.properties[NON_TOGGLE_MASK].value == 2 - assert device.properties[NON_TOGGLE_ON_OFF_MASK].value == 2 - assert not device.properties[NON_TOGGLE_MASK].is_dirty - assert not device.properties[NON_TOGGLE_ON_OFF_MASK].is_dirty - + prop_name = f"{TOGGLE_BUTTON}_c" + toggle_prop = device.configuration[prop_name] + assert toggle_prop.value == ToggleMode.TOGGLE with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { ID: 2, TYPE: "insteon/properties/change", DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: toggle_props[0]["name"], - PROPERTY_VALUE: 1, + PROPERTY_NAME: prop_name, + PROPERTY_VALUE: str(ToggleMode.ON_ONLY).lower(), } ) msg = await ws_client.receive_json() assert msg["success"] + assert toggle_prop.new_value == ToggleMode.ON_ONLY - new_toggle_props, _ = _get_toggle_properties(devices["33.33.33"]) - assert new_toggle_props[0]["value"] == TOGGLE_MODES[NON_TOGGLE_ON_MODE] - assert device.properties[NON_TOGGLE_MASK].new_value == 3 - assert device.properties[NON_TOGGLE_ON_OFF_MASK].new_value == 3 - assert device.properties[NON_TOGGLE_MASK].is_dirty - assert device.properties[NON_TOGGLE_ON_OFF_MASK].is_dirty +async def test_change_relay_mode(hass, hass_ws_client, iolinc_properties_data): + """Update a device's relay mode.""" + ws_client, devices = await _setup( + hass, hass_ws_client, "44.44.44", iolinc_properties_data + ) + device = devices["44.44.44"] + relay_prop = device.configuration[RELAY_MODE] + assert relay_prop.value == RelayMode.MOMENTARY_A + with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { - ID: 3, + ID: 2, TYPE: "insteon/properties/change", - DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: toggle_props[0]["name"], - PROPERTY_VALUE: 2, + DEVICE_ADDRESS: "44.44.44", + PROPERTY_NAME: RELAY_MODE, + PROPERTY_VALUE: str(RelayMode.LATCHING).lower(), } ) msg = await ws_client.receive_json() assert msg["success"] + assert relay_prop.new_value == RelayMode.LATCHING - new_toggle_props, _ = _get_toggle_properties(devices["33.33.33"]) - assert new_toggle_props[0]["value"] == TOGGLE_MODES[NON_TOGGLE_OFF_MODE] - assert device.properties[NON_TOGGLE_MASK].new_value == 3 - assert device.properties[NON_TOGGLE_ON_OFF_MASK].new_value is None - assert device.properties[NON_TOGGLE_MASK].is_dirty - assert not device.properties[NON_TOGGLE_ON_OFF_MASK].is_dirty +async def test_change_float_property(hass, hass_ws_client, iolinc_properties_data): + """Update a float type property.""" + ws_client, devices = await _setup( + hass, hass_ws_client, "44.44.44", iolinc_properties_data + ) + device = devices["44.44.44"] + delay_prop = device.configuration[MOMENTARY_DELAY] + delay_prop.load(0) + with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( { - ID: 4, + ID: 2, TYPE: "insteon/properties/change", - DEVICE_ADDRESS: "33.33.33", - PROPERTY_NAME: toggle_props[1]["name"], - PROPERTY_VALUE: 0, + DEVICE_ADDRESS: "44.44.44", + PROPERTY_NAME: MOMENTARY_DELAY, + PROPERTY_VALUE: 1.8, } ) msg = await ws_client.receive_json() assert msg["success"] - new_toggle_props, _ = _get_toggle_properties(devices["33.33.33"]) - assert new_toggle_props[1]["value"] == TOGGLE_MODES[TOGGLE_ON_OFF_MODE] - assert device.properties[NON_TOGGLE_MASK].new_value == 1 - assert device.properties[NON_TOGGLE_ON_OFF_MASK].new_value == 0 - assert device.properties[NON_TOGGLE_MASK].is_dirty - assert device.properties[NON_TOGGLE_ON_OFF_MASK].is_dirty + assert delay_prop.new_value == 1.8 -async def test_write_properties(hass, hass_ws_client, properties_data): +async def test_write_properties(hass, hass_ws_client, kpl_properties_data): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( @@ -332,9 +379,11 @@ async def test_write_properties(hass, hass_ws_client, properties_data): assert devices["33.33.33"].async_write_ext_properties.call_count == 1 -async def test_write_properties_failure(hass, hass_ws_client, properties_data): +async def test_write_properties_failure(hass, hass_ws_client, kpl_properties_data): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( @@ -345,39 +394,48 @@ async def test_write_properties_failure(hass, hass_ws_client, properties_data): assert msg["error"]["code"] == "write_failed" -async def test_load_properties(hass, hass_ws_client, properties_data): +async def test_load_properties(hass, hass_ws_client, kpl_properties_data): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) + device = devices["33.33.33"] + device.async_read_config = AsyncMock(return_value=(1, 1)) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( {ID: 2, TYPE: "insteon/properties/load", DEVICE_ADDRESS: "33.33.33"} ) msg = await ws_client.receive_json() assert msg["success"] - assert devices["33.33.33"].async_read_op_flags.call_count == 1 - assert devices["33.33.33"].async_read_ext_properties.call_count == 1 + assert devices["33.33.33"].async_read_config.call_count == 1 -async def test_load_properties_failure(hass, hass_ws_client, properties_data): +async def test_load_properties_failure(hass, hass_ws_client, kpl_properties_data): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) + device = devices["33.33.33"] + device.async_read_config = AsyncMock(return_value=(0, 0)) with patch.object(insteon.api.properties, "devices", devices): await ws_client.send_json( - {ID: 2, TYPE: "insteon/properties/load", DEVICE_ADDRESS: "22.22.22"} + {ID: 2, TYPE: "insteon/properties/load", DEVICE_ADDRESS: "33.33.33"} ) msg = await ws_client.receive_json() assert not msg["success"] assert msg["error"]["code"] == "load_failed" -async def test_reset_properties(hass, hass_ws_client, properties_data): +async def test_reset_properties(hass, hass_ws_client, kpl_properties_data): """Test getting an Insteon device's properties.""" - ws_client, devices = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) device = devices["33.33.33"] - device.operating_flags["led_off"].new_value = True + device.configuration["led_off"].new_value = True device.properties["on_mask"].new_value = 100 assert device.operating_flags["led_off"].is_dirty assert device.properties["on_mask"].is_dirty @@ -391,20 +449,23 @@ async def test_reset_properties(hass, hass_ws_client, properties_data): assert not device.properties["on_mask"].is_dirty -async def test_bad_address(hass, hass_ws_client, properties_data): +async def test_bad_address(hass, hass_ws_client, kpl_properties_data): """Test for a bad Insteon address.""" - ws_client, _ = await _setup(hass, hass_ws_client, properties_data) + ws_client, devices = await _setup( + hass, hass_ws_client, "33.33.33", kpl_properties_data + ) ws_id = 0 for call in ["get", "write", "load", "reset"]: ws_id += 1 - await ws_client.send_json( - { - ID: ws_id, - TYPE: f"insteon/properties/{call}", - DEVICE_ADDRESS: "99.99.99", - } - ) + params = { + ID: ws_id, + TYPE: f"insteon/properties/{call}", + DEVICE_ADDRESS: "99.99.99", + } + if call == "get": + params[SHOW_ADVANCED] = False + await ws_client.send_json(params) msg = await ws_client.receive_json() assert not msg["success"] assert msg["error"]["message"] == INSTEON_DEVICE_NOT_FOUND diff --git a/tests/components/insteon/test_config_flow.py b/tests/components/insteon/test_config_flow.py index ce49f9df816..878b540b721 100644 --- a/tests/components/insteon/test_config_flow.py +++ b/tests/components/insteon/test_config_flow.py @@ -2,10 +2,8 @@ from unittest.mock import patch -import voluptuous_serialize - from homeassistant import config_entries, data_entry_flow -from homeassistant.components import dhcp, usb +from homeassistant.components import usb from homeassistant.components.insteon.config_flow import ( HUB1, HUB2, @@ -39,7 +37,6 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from .const import ( MOCK_HOSTNAME, @@ -651,48 +648,3 @@ async def test_discovery_via_usb_already_setup(hass): assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "single_instance_allowed" - - -async def test_discovery_via_dhcp_hubv1(hass): - """Test usb flow.""" - await _test_dhcp(hass, HUB1) - - -async def test_discovery_via_dhcp_hubv2(hass): - """Test usb flow.""" - await _test_dhcp(hass, HUB2) - - -async def _test_dhcp(hass, modem_type): - """Test the dhcp discovery for a moddem type.""" - discovery_info = dhcp.DhcpServiceInfo( - ip="11.22.33.44", hostname="", macaddress="00:0e:f3:aa:bb:cc" - ) - result = await hass.config_entries.flow.async_init( - "insteon", - context={"source": config_entries.SOURCE_DHCP}, - data=discovery_info, - ) - await hass.async_block_till_done() - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "user" - - with patch("homeassistant.components.insteon.config_flow.async_connect"), patch( - "homeassistant.components.insteon.async_setup_entry", return_value=True - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={"modem_type": modem_type} - ) - await hass.async_block_till_done() - - assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "user" - - schema = voluptuous_serialize.convert( - result2["data_schema"], - custom_serializer=cv.custom_serializer, - ) - for field in schema: - if field["name"] == "host": - assert field.get("default") == "11.22.33.44" - break diff --git a/tests/components/insteon/test_init.py b/tests/components/insteon/test_init.py index ecd3dfc5620..eb821f15cb5 100644 --- a/tests/components/insteon/test_init.py +++ b/tests/components/insteon/test_init.py @@ -7,6 +7,7 @@ from pyinsteon.address import Address from homeassistant.components import insteon from homeassistant.components.insteon.const import ( CONF_CAT, + CONF_DEV_PATH, CONF_OVERRIDE, CONF_SUBCAT, CONF_X10, @@ -222,3 +223,24 @@ async def test_setup_entry_failed_connection(hass: HomeAssistant, caplog): {}, ) assert "Could not connect to Insteon modem" in caplog.text + + +async def test_import_frontend_dev_url(hass: HomeAssistant): + """Test importing a dev_url config entry.""" + config = {} + config[DOMAIN] = {CONF_DEV_PATH: "/some/path"} + + with patch.object( + insteon, "async_connect", new=mock_successful_connection + ), patch.object(insteon, "close_insteon_connection"), patch.object( + insteon, "devices", new=MockDevices() + ), patch( + PATCH_CONNECTION, new=mock_successful_connection + ): + assert await async_setup_component( + hass, + insteon.DOMAIN, + config, + ) + await hass.async_block_till_done() + await asyncio.sleep(0.01) From 8883f5482b8cb225f37d1299f4c08c8d1cc58845 Mon Sep 17 00:00:00 2001 From: Shai Ungar Date: Thu, 28 Apr 2022 23:25:17 +0300 Subject: [PATCH 059/930] Sabnzbd config flow improvments (#70981) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> Co-authored-by: Martin Hjelmare --- homeassistant/components/sabnzbd/__init__.py | 158 ++++++++++++++---- .../components/sabnzbd/config_flow.py | 10 +- homeassistant/components/sabnzbd/const.py | 5 +- homeassistant/components/sabnzbd/errors.py | 10 -- homeassistant/components/sabnzbd/sensor.py | 7 +- .../components/sabnzbd/services.yaml | 20 +++ tests/components/sabnzbd/test_config_flow.py | 59 +++---- 7 files changed, 181 insertions(+), 88 deletions(-) delete mode 100644 homeassistant/components/sabnzbd/errors.py diff --git a/homeassistant/components/sabnzbd/__init__.py b/homeassistant/components/sabnzbd/__init__.py index bbbfbe18bc1..aca50e404a2 100644 --- a/homeassistant/components/sabnzbd/__init__.py +++ b/homeassistant/components/sabnzbd/__init__.py @@ -1,24 +1,39 @@ """Support for monitoring an SABnzbd NZB client.""" +from collections.abc import Callable import logging from pysabnzbd import SabnzbdApiException import voluptuous as vol -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_PATH, CONF_URL -from homeassistant.core import HomeAssistant, ServiceCall -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryState +from homeassistant.const import ( + CONF_API_KEY, + CONF_HOST, + CONF_NAME, + CONF_PATH, + CONF_PORT, + CONF_SENSORS, + CONF_SSL, + CONF_URL, +) +from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType from .const import ( + ATTR_API_KEY, ATTR_SPEED, + DEFAULT_HOST, DEFAULT_NAME, + DEFAULT_PORT, DEFAULT_SPEED_LIMIT, + DEFAULT_SSL, DOMAIN, KEY_API, + KEY_API_DATA, KEY_NAME, SERVICE_PAUSE, SERVICE_RESUME, @@ -27,23 +42,50 @@ from .const import ( UPDATE_INTERVAL, ) from .sab import get_client +from .sensor import SENSOR_KEYS PLATFORMS = ["sensor"] _LOGGER = logging.getLogger(__name__) -SPEED_LIMIT_SCHEMA = vol.Schema( - {vol.Optional(ATTR_SPEED, default=DEFAULT_SPEED_LIMIT): cv.string} +SERVICES = ( + SERVICE_PAUSE, + SERVICE_RESUME, + SERVICE_SET_SPEED, +) + +SERVICE_BASE_SCHEMA = vol.Schema( + { + vol.Required(ATTR_API_KEY): cv.string, + } +) + +SERVICE_SPEED_SCHEMA = SERVICE_BASE_SCHEMA.extend( + { + vol.Optional(ATTR_SPEED, default=DEFAULT_SPEED_LIMIT): cv.string, + } ) CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( - { - vol.Required(CONF_API_KEY): str, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, - vol.Required(CONF_URL): str, - vol.Optional(CONF_PATH): str, - } + vol.All( + cv.deprecated(CONF_HOST), + cv.deprecated(CONF_PORT), + cv.deprecated(CONF_SENSORS), + cv.deprecated(CONF_SSL), + { + vol.Required(CONF_API_KEY): str, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, + vol.Required(CONF_URL): str, + vol.Optional(CONF_PATH): str, + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_SENSORS): vol.All( + cv.ensure_list, [vol.In(SENSOR_KEYS)] + ), + vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + }, + ) ) }, extra=vol.ALLOW_EXTRA, @@ -69,42 +111,73 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True +@callback +def async_get_entry_id_for_service_call(hass: HomeAssistant, call: ServiceCall) -> str: + """Get the entry ID related to a service call (by device ID).""" + call_data_api_key = call.data[ATTR_API_KEY] + + for entry in hass.config_entries.async_entries(DOMAIN): + if entry.data[ATTR_API_KEY] == call_data_api_key: + return entry.entry_id + + raise ValueError(f"No api for API key: {call_data_api_key}") + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the SabNzbd Component.""" sab_api = await get_client(hass, entry.data) if not sab_api: raise ConfigEntryNotReady + sab_api_data = SabnzbdApiData(sab_api) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { KEY_API: sab_api, + KEY_API_DATA: sab_api_data, KEY_NAME: entry.data[CONF_NAME], } - hass.config_entries.async_setup_platforms(entry, PLATFORMS) + @callback + def extract_api(func: Callable) -> Callable: + """Define a decorator to get the correct api for a service call.""" - sab_api_data = SabnzbdApiData(sab_api) + async def wrapper(call: ServiceCall) -> None: + """Wrap the service function.""" + entry_id = async_get_entry_id_for_service_call(hass, call) + api_data = hass.data[DOMAIN][entry_id][KEY_API_DATA] - async def async_service_handler(service: ServiceCall) -> None: - """Handle service calls.""" - if service.service == SERVICE_PAUSE: - await sab_api_data.async_pause_queue() - elif service.service == SERVICE_RESUME: - await sab_api_data.async_resume_queue() - elif service.service == SERVICE_SET_SPEED: - speed = service.data.get(ATTR_SPEED) - await sab_api_data.async_set_queue_speed(speed) + try: + await func(call, api_data) + except Exception as err: + raise HomeAssistantError( + f"Error while executing {func.__name__}: {err}" + ) from err - hass.services.async_register( - DOMAIN, SERVICE_PAUSE, async_service_handler, schema=vol.Schema({}) - ) + return wrapper - hass.services.async_register( - DOMAIN, SERVICE_RESUME, async_service_handler, schema=vol.Schema({}) - ) + @extract_api + async def async_pause_queue(call: ServiceCall, api: SabnzbdApiData) -> None: + await api.async_pause_queue() - hass.services.async_register( - DOMAIN, SERVICE_SET_SPEED, async_service_handler, schema=SPEED_LIMIT_SCHEMA - ) + @extract_api + async def async_resume_queue(call: ServiceCall, api: SabnzbdApiData) -> None: + await api.async_resume_queue() + + @extract_api + async def async_set_queue_speed(call: ServiceCall, api: SabnzbdApiData) -> None: + speed = call.data.get(ATTR_SPEED) + await api.async_set_queue_speed(speed) + + for service, method, schema in ( + (SERVICE_PAUSE, async_pause_queue, SERVICE_BASE_SCHEMA), + (SERVICE_RESUME, async_resume_queue, SERVICE_BASE_SCHEMA), + (SERVICE_SET_SPEED, async_set_queue_speed, SERVICE_SPEED_SCHEMA), + ): + + if hass.services.has_service(DOMAIN, service): + continue + + hass.services.async_register(DOMAIN, service, method, schema=schema) async def async_update_sabnzbd(now): """Refresh SABnzbd queue data.""" @@ -115,10 +188,31 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.error(err) async_track_time_interval(hass, async_update_sabnzbd, UPDATE_INTERVAL) + hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a Sabnzbd config entry.""" + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload_ok: + hass.data[DOMAIN].pop(entry.entry_id) + + loaded_entries = [ + entry + for entry in hass.config_entries.async_entries(DOMAIN) + if entry.state == ConfigEntryState.LOADED + ] + if len(loaded_entries) == 1: + # If this is the last loaded instance of Sabnzbd, deregister any services + # defined during integration setup: + for service_name in SERVICES: + hass.services.async_remove(DOMAIN, service_name) + + return unload_ok + + class SabnzbdApiData: """Class for storing/refreshing sabnzbd api queue data.""" diff --git a/homeassistant/components/sabnzbd/config_flow.py b/homeassistant/components/sabnzbd/config_flow.py index 914b1febefc..7930363b2ac 100644 --- a/homeassistant/components/sabnzbd/config_flow.py +++ b/homeassistant/components/sabnzbd/config_flow.py @@ -70,10 +70,8 @@ class SABnzbdConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_import(self, import_data): """Import sabnzbd config from configuration.yaml.""" - import_data[CONF_URL] = ( - ("https://" if import_data[CONF_SSL] else "http://") - + import_data[CONF_HOST] - + ":" - + str(import_data[CONF_PORT]) - ) + protocol = "https://" if import_data[CONF_SSL] else "http://" + import_data[ + CONF_URL + ] = f"{protocol}{import_data[CONF_HOST]}:{import_data[CONF_PORT]}" return await self.async_step_user(import_data) diff --git a/homeassistant/components/sabnzbd/const.py b/homeassistant/components/sabnzbd/const.py index 9092b877b1b..8add1f61493 100644 --- a/homeassistant/components/sabnzbd/const.py +++ b/homeassistant/components/sabnzbd/const.py @@ -5,8 +5,8 @@ DOMAIN = "sabnzbd" DATA_SABNZBD = "sabnzbd" ATTR_SPEED = "speed" -BASE_URL_FORMAT = "{}://{}:{}/" -CONFIG_FILE = "sabnzbd.conf" +ATTR_API_KEY = "api_key" + DEFAULT_HOST = "localhost" DEFAULT_NAME = "SABnzbd" DEFAULT_PORT = 8080 @@ -22,4 +22,5 @@ SERVICE_SET_SPEED = "set_speed" SIGNAL_SABNZBD_UPDATED = "sabnzbd_updated" KEY_API = "api" +KEY_API_DATA = "api_data" KEY_NAME = "name" diff --git a/homeassistant/components/sabnzbd/errors.py b/homeassistant/components/sabnzbd/errors.py deleted file mode 100644 index a14a0af4775..00000000000 --- a/homeassistant/components/sabnzbd/errors.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Errors for the Sabnzbd component.""" -from homeassistant.exceptions import HomeAssistantError - - -class AuthenticationError(HomeAssistantError): - """Wrong Username or Password.""" - - -class UnknownError(HomeAssistantError): - """Unknown Error.""" diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index 293b14a604b..1d661d90848 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -10,12 +10,12 @@ from homeassistant.components.sensor import ( ) from homeassistant.helpers.dispatcher import async_dispatcher_connect -from . import DOMAIN, SIGNAL_SABNZBD_UPDATED, SabnzbdApiData +from . import DOMAIN, SIGNAL_SABNZBD_UPDATED from ...config_entries import ConfigEntry from ...const import DATA_GIGABYTES, DATA_MEGABYTES, DATA_RATE_MEGABYTES_PER_SECOND from ...core import HomeAssistant from ...helpers.entity_platform import AddEntitiesCallback -from .const import KEY_API, KEY_NAME +from .const import KEY_API_DATA, KEY_NAME @dataclass @@ -109,9 +109,8 @@ async def async_setup_entry( ) -> None: """Set up a Sabnzbd sensor entry.""" - sab_api = hass.data[DOMAIN][config_entry.entry_id][KEY_API] + sab_api_data = hass.data[DOMAIN][config_entry.entry_id][KEY_API_DATA] client_name = hass.data[DOMAIN][config_entry.entry_id][KEY_NAME] - sab_api_data = SabnzbdApiData(sab_api) async_add_entities( [SabnzbdSensor(sab_api_data, client_name, sensor) for sensor in SENSOR_TYPES] diff --git a/homeassistant/components/sabnzbd/services.yaml b/homeassistant/components/sabnzbd/services.yaml index 38f68bfe5dd..2221eed169f 100644 --- a/homeassistant/components/sabnzbd/services.yaml +++ b/homeassistant/components/sabnzbd/services.yaml @@ -1,13 +1,33 @@ pause: name: Pause description: Pauses downloads. + fields: + api_key: + name: Sabnzbd API key + description: The Sabnzbd API key to pause downloads + required: true + selector: + text: resume: name: Resume description: Resumes downloads. + fields: + api_key: + name: Sabnzbd API key + description: The Sabnzbd API key to resume downloads + required: true + selector: + text: set_speed: name: Set speed description: Sets the download speed limit. fields: + api_key: + name: Sabnzbd API key + description: The Sabnzbd API key to set speed limit + required: true + selector: + text: speed: name: Speed description: Speed limit. If specified as a number with no units, will be interpreted as a percent. If units are provided (e.g., 500K) will be interpreted absolutely. diff --git a/tests/components/sabnzbd/test_config_flow.py b/tests/components/sabnzbd/test_config_flow.py index 381928457d2..d04c5b18ab1 100644 --- a/tests/components/sabnzbd/test_config_flow.py +++ b/tests/components/sabnzbd/test_config_flow.py @@ -3,7 +3,7 @@ from unittest.mock import patch from pysabnzbd import SabnzbdApiException -from homeassistant import data_entry_flow +from homeassistant import config_entries, data_entry_flow from homeassistant.components.sabnzbd import DOMAIN from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER from homeassistant.const import ( @@ -15,8 +15,7 @@ from homeassistant.const import ( CONF_SSL, CONF_URL, ) - -from tests.common import MockConfigEntry +from homeassistant.data_entry_flow import RESULT_TYPE_FORM VALID_CONFIG = { CONF_NAME: "Sabnzbd", @@ -37,21 +36,34 @@ VALID_CONFIG_OLD = { async def test_create_entry(hass): """Test that the user step works.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + with patch( "homeassistant.components.sabnzbd.sab.SabnzbdApi.check_available", return_value=True, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER}, - data=VALID_CONFIG, + ), patch( + "homeassistant.components.sabnzbd.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + VALID_CONFIG, ) + await hass.async_block_till_done() - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "edc3eee7330e" - assert result["data"][CONF_NAME] == "Sabnzbd" - assert result["data"][CONF_API_KEY] == "edc3eee7330e4fdda04489e3fbc283d0" - assert result["data"][CONF_PATH] == "" + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == "edc3eee7330e" + assert result2["data"] == { + CONF_API_KEY: "edc3eee7330e4fdda04489e3fbc283d0", + CONF_NAME: "Sabnzbd", + CONF_PATH: "", + CONF_URL: "http://localhost:8080", + } + assert len(mock_setup_entry.mock_calls) == 1 async def test_auth_error(hass): @@ -69,27 +81,6 @@ async def test_auth_error(hass): assert result["errors"] == {"base": "cannot_connect"} -async def test_integration_already_exists(hass): - """Test we only allow a single config flow.""" - with patch( - "homeassistant.components.sabnzbd.sab.SabnzbdApi.check_available", - return_value=True, - ): - MockConfigEntry( - domain=DOMAIN, - unique_id="123456", - data=VALID_CONFIG, - ).add_to_hass(hass) - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER}, - data=VALID_CONFIG, - ) - - assert result["type"] == "create_entry" - - async def test_import_flow(hass) -> None: """Test the import configuration flow.""" with patch( From d791a0800219a04f1b556cd3455f68a65a896a02 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 28 Apr 2022 15:05:55 -0600 Subject: [PATCH 060/930] Ensure that email-based 2FA in SimpliSafe shows the progress UI (#71021) --- .../components/simplisafe/config_flow.py | 92 ++++++++++++------- .../components/simplisafe/strings.json | 5 +- .../simplisafe/translations/en.json | 5 +- tests/components/simplisafe/conftest.py | 2 + .../components/simplisafe/test_config_flow.py | 46 +++++----- 5 files changed, 95 insertions(+), 55 deletions(-) diff --git a/homeassistant/components/simplisafe/config_flow.py b/homeassistant/components/simplisafe/config_flow.py index ad9614c0546..926f904a912 100644 --- a/homeassistant/components/simplisafe/config_flow.py +++ b/homeassistant/components/simplisafe/config_flow.py @@ -49,13 +49,14 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self) -> None: """Initialize the config flow.""" + self._email_2fa_task: asyncio.Task | None = None self._password: str | None = None self._reauth: bool = False self._simplisafe: API | None = None self._username: str | None = None async def _async_authenticate( - self, error_step_id: str, error_schema: vol.Schema + self, originating_step_id: str, originating_step_schema: vol.Schema ) -> FlowResult: """Attempt to authenticate to the SimpliSafe API.""" assert self._password @@ -76,8 +77,8 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if errors: return self.async_show_form( - step_id=error_step_id, - data_schema=error_schema, + step_id=originating_step_id, + data_schema=originating_step_schema, errors=errors, description_placeholders={CONF_USERNAME: self._username}, ) @@ -86,6 +87,31 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if self._simplisafe.auth_state == AuthStates.PENDING_2FA_SMS: return await self.async_step_sms_2fa() + return await self.async_step_email_2fa() + + @staticmethod + @callback + def async_get_options_flow( + config_entry: ConfigEntry, + ) -> SimpliSafeOptionsFlowHandler: + """Define the config flow to handle options.""" + return SimpliSafeOptionsFlowHandler(config_entry) + + async def async_step_reauth(self, config: dict[str, Any]) -> FlowResult: + """Handle configuration by re-auth.""" + self._reauth = True + + if CONF_USERNAME not in config: + # Old versions of the config flow may not have the username by this point; + # in that case, we reauth them by making them go through the user flow: + return await self.async_step_user() + + self._username = config[CONF_USERNAME] + return await self.async_step_reauth_confirm() + + async def _async_get_email_2fa(self) -> None: + """Define a task to wait for email-based 2FA.""" + assert self._simplisafe try: async with async_timeout.timeout(DEFAULT_EMAIL_2FA_TIMEOUT): @@ -97,17 +123,39 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await asyncio.sleep(DEFAULT_EMAIL_2FA_SLEEP) else: break - except asyncio.TimeoutError: - return self.async_show_form( - step_id="user", - data_schema=STEP_USER_SCHEMA, - errors={"base": "2fa_timed_out"}, + finally: + self.hass.async_create_task( + self.hass.config_entries.flow.async_configure(flow_id=self.flow_id) ) - return await self._async_finish_setup() + async def async_step_email_2fa( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle email-based two-factor authentication.""" + if not self._email_2fa_task: + self._email_2fa_task = self.hass.async_create_task( + self._async_get_email_2fa() + ) + return self.async_show_progress( + step_id="email_2fa", progress_action="email_2fa" + ) - async def _async_finish_setup(self) -> FlowResult: - """Complete setup with an authenticated API object.""" + try: + await self._email_2fa_task + except asyncio.TimeoutError: + return self.async_show_progress_done(next_step_id="email_2fa_error") + return self.async_show_progress_done(next_step_id="finish") + + async def async_step_email_2fa_error( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle an error during email-based two-factor authentication.""" + return self.async_abort(reason="email_2fa_timed_out") + + async def async_step_finish( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the final step.""" assert self._simplisafe assert self._username @@ -142,26 +190,6 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() return self.async_create_entry(title=self._username, data=data) - @staticmethod - @callback - def async_get_options_flow( - config_entry: ConfigEntry, - ) -> SimpliSafeOptionsFlowHandler: - """Define the config flow to handle options.""" - return SimpliSafeOptionsFlowHandler(config_entry) - - async def async_step_reauth(self, config: dict[str, Any]) -> FlowResult: - """Handle configuration by re-auth.""" - self._reauth = True - - if CONF_USERNAME not in config: - # Old versions of the config flow may not have the username by this point; - # in that case, we reauth them by making them go through the user flow: - return await self.async_step_user() - - self._username = config[CONF_USERNAME] - return await self.async_step_reauth_confirm() - async def async_step_reauth_confirm( self, user_input: dict[str, Any] | None = None ) -> FlowResult: @@ -197,7 +225,7 @@ class SimpliSafeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): errors={CONF_CODE: "invalid_auth"}, ) - return await self._async_finish_setup() + return await self.async_step_finish() async def async_step_user( self, user_input: dict[str, Any] | None = None diff --git a/homeassistant/components/simplisafe/strings.json b/homeassistant/components/simplisafe/strings.json index a5962a89abc..45e0ef84f5a 100644 --- a/homeassistant/components/simplisafe/strings.json +++ b/homeassistant/components/simplisafe/strings.json @@ -23,13 +23,16 @@ } }, "error": { - "2fa_timed_out": "Timed out while waiting for two-factor authentication", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "unknown": "[%key:common::config_flow::error::unknown%]" }, "abort": { "already_configured": "This SimpliSafe account is already in use.", + "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + }, + "progress": { + "email_2fa": "Input the two-factor authentication code\nsent to you via email." } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index 5dd146d757b..2f8a4366df1 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -2,13 +2,16 @@ "config": { "abort": { "already_configured": "This SimpliSafe account is already in use.", + "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", "reauth_successful": "Re-authentication was successful" }, "error": { - "2fa_timed_out": "Timed out while waiting for two-factor authentication", "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, + "progress": { + "email_2fa": "Input the two-factor authentication code\nsent to you via email." + }, "step": { "reauth_confirm": { "data": { diff --git a/tests/components/simplisafe/conftest.py b/tests/components/simplisafe/conftest.py index d3a07bd8fc8..56967ac24c5 100644 --- a/tests/components/simplisafe/conftest.py +++ b/tests/components/simplisafe/conftest.py @@ -102,6 +102,8 @@ def reauth_config_fixture(): async def setup_simplisafe_fixture(hass, api, config): """Define a fixture to set up SimpliSafe.""" with patch( + "homeassistant.components.simplisafe.config_flow.DEFAULT_EMAIL_2FA_SLEEP", 0 + ), patch( "homeassistant.components.simplisafe.config_flow.API.async_from_credentials", return_value=api, ), patch( diff --git a/tests/components/simplisafe/test_config_flow.py b/tests/components/simplisafe/test_config_flow.py index a4ef98e0e09..2e0b85bc6c6 100644 --- a/tests/components/simplisafe/test_config_flow.py +++ b/tests/components/simplisafe/test_config_flow.py @@ -118,6 +118,7 @@ async def test_step_reauth_errors(hass, config, error_string, exc, reauth_config result["flow_id"], user_input=reauth_config ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": error_string} @@ -191,12 +192,13 @@ async def test_step_user_errors(hass, credentials_config, error_string, exc): result["flow_id"], user_input=credentials_config ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" assert result["errors"] == {"base": error_string} @pytest.mark.parametrize("api_auth_state", [AuthStates.PENDING_2FA_EMAIL]) async def test_step_user_email_2fa( - api, hass, config, credentials_config, setup_simplisafe + api, api_auth_state, hass, config, credentials_config, setup_simplisafe ): """Test the user step with email-based 2FA.""" result = await hass.config_entries.flow.async_init( @@ -208,14 +210,15 @@ async def test_step_user_email_2fa( # Patch API.async_verify_2fa_email to first return pending, then return all done: api.async_verify_2fa_email.side_effect = [Verify2FAPending, None] - # Patch the amount of time slept between calls so to not slow down this test: - with patch( - "homeassistant.components.simplisafe.config_flow.DEFAULT_EMAIL_2FA_SLEEP", 0 - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=credentials_config - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=credentials_config + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS_DONE + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) assert len(hass.config_entries.async_entries()) == 1 [config_entry] = hass.config_entries.async_entries(DOMAIN) @@ -223,6 +226,7 @@ async def test_step_user_email_2fa( assert config_entry.data == config +@patch("homeassistant.components.simplisafe.config_flow.DEFAULT_EMAIL_2FA_TIMEOUT", 0) @pytest.mark.parametrize("api_auth_state", [AuthStates.PENDING_2FA_EMAIL]) async def test_step_user_email_2fa_timeout( api, hass, config, credentials_config, setup_simplisafe @@ -237,18 +241,18 @@ async def test_step_user_email_2fa_timeout( # Patch API.async_verify_2fa_email to return pending: api.async_verify_2fa_email.side_effect = Verify2FAPending - # Patch the amount of time slept between calls and the timeout duration so to not - # slow down this test: - with patch( - "homeassistant.components.simplisafe.config_flow.DEFAULT_EMAIL_2FA_SLEEP", 0 - ), patch( - "homeassistant.components.simplisafe.config_flow.DEFAULT_EMAIL_2FA_TIMEOUT", 0 - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=credentials_config - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "2fa_timed_out"} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=credentials_config + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS_DONE + assert result["step_id"] == "email_2fa_error" + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "email_2fa_timed_out" async def test_step_user_sms_2fa( From 408946cfee791d7b811d488a7f29ffa8d3a0d2a3 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Thu, 28 Apr 2022 23:09:35 +0200 Subject: [PATCH 061/930] Bump pydeconz to v91 (#71030) --- homeassistant/components/deconz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index e26c659ff73..dee41435779 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -3,7 +3,7 @@ "name": "deCONZ", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/deconz", - "requirements": ["pydeconz==90"], + "requirements": ["pydeconz==91"], "ssdp": [ { "manufacturer": "Royal Philips Electronics" diff --git a/requirements_all.txt b/requirements_all.txt index c7fcbd54144..2346a37354e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1432,7 +1432,7 @@ pydaikin==2.7.0 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==90 +pydeconz==91 # homeassistant.components.delijn pydelijn==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 824e843639d..f60854a862c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -953,7 +953,7 @@ pycoolmasternet-async==0.1.2 pydaikin==2.7.0 # homeassistant.components.deconz -pydeconz==90 +pydeconz==91 # homeassistant.components.dexcom pydexcom==0.2.3 From 00070e8804507b40f345dc541f7555e19cfbff66 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 28 Apr 2022 14:09:54 -0700 Subject: [PATCH 062/930] Add redirect for server controls (#71027) Co-authored-by: Zack Barett --- homeassistant/components/frontend/__init__.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 558b24f3286..55d745eb309 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -364,15 +364,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async_register_built_in_panel(hass, "profile") - # To smooth transition to new urls, add redirects to new urls of dev tools - # Added June 27, 2019. Can be removed in 2021. - for panel in ("event", "service", "state", "template"): - hass.http.register_redirect(f"/dev-{panel}", f"/developer-tools/{panel}") - for panel in ("logs", "info", "mqtt"): - # Can be removed in 2021. - hass.http.register_redirect(f"/dev-{panel}", f"/config/{panel}") - # Added June 20 2020. Can be removed in 2022. - hass.http.register_redirect(f"/developer-tools/{panel}", f"/config/{panel}") + # Can be removed in 2023 + hass.http.register_redirect("/config/server_control", "/developer-tools/yaml") async_register_built_in_panel( hass, From 7e8c6d563fc8ec4b2aeb7443ea2aa4c4a8538211 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Thu, 28 Apr 2022 16:24:04 -0500 Subject: [PATCH 063/930] Frontend Bump to 20220428.0 (#71029) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index c622d930ab1..c806b21e723 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220427.0"], + "requirements": ["home-assistant-frontend==20220428.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f58eb91b087..9d258ddbd4a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220427.0 +home-assistant-frontend==20220428.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index 2346a37354e..7039bc0da94 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220427.0 +home-assistant-frontend==20220428.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f60854a862c..7f4380e4220 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220427.0 +home-assistant-frontend==20220428.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 1f1932d224f26ec8b80fa41bea3cf127a97ca7fb Mon Sep 17 00:00:00 2001 From: jjlawren Date: Thu, 28 Apr 2022 16:26:29 -0500 Subject: [PATCH 064/930] Fix Sonos races related to grouping and startup (#71026) --- .../components/sonos/media_player.py | 9 ++++ homeassistant/components/sonos/speaker.py | 41 ++++++++++++++++--- tests/components/sonos/conftest.py | 1 + 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 30fdb28b02f..f7f5e2722ff 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -258,6 +258,15 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): if self.coordinator.uid == uid: self.async_write_ha_state() + @property + def available(self) -> bool: + """Return if the media_player is available.""" + return ( + self.speaker.available + and self.speaker.sonos_group_entities + and self.media.playback_status + ) + @property def coordinator(self) -> SonosSpeaker: """Return the current coordinator SonosSpeaker.""" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 4435a65db3d..4e4661b389b 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -189,7 +189,12 @@ class SonosSpeaker: def setup(self, entry: ConfigEntry) -> None: """Run initial setup of the speaker.""" - self.set_basic_info() + self.media.play_mode = self.soco.play_mode + self.update_volume() + self.update_groups() + if self.is_coordinator: + self.media.poll_media() + future = asyncio.run_coroutine_threadsafe( self.async_setup_dispatchers(entry), self.hass.loop ) @@ -247,11 +252,6 @@ class SonosSpeaker: """Write states for associated SonosEntity instances.""" async_dispatcher_send(self.hass, f"{SONOS_STATE_UPDATED}-{self.soco.uid}") - def set_basic_info(self) -> None: - """Set basic information when speaker is reconnected.""" - self.media.play_mode = self.soco.play_mode - self.update_volume() - # # Properties # @@ -456,6 +456,34 @@ class SonosSpeaker: @callback def async_dispatch_media_update(self, event: SonosEvent) -> None: """Update information about currently playing media from an event.""" + # The new coordinator can be provided in a media update event but + # before the ZoneGroupState updates. If this happens the playback + # state will be incorrect and should be ignored. Switching to the + # new coordinator will use its media. The regrouping process will + # be completed during the next ZoneGroupState update. + av_transport_uri = event.variables.get("av_transport_uri", "") + current_track_uri = event.variables.get("current_track_uri", "") + if av_transport_uri == current_track_uri and av_transport_uri.startswith( + "x-rincon:" + ): + new_coordinator_uid = av_transport_uri.split(":")[-1] + if new_coordinator_speaker := self.hass.data[DATA_SONOS].discovered.get( + new_coordinator_uid + ): + _LOGGER.debug( + "Media update coordinator (%s) received for %s", + new_coordinator_speaker.zone_name, + self.zone_name, + ) + self.coordinator = new_coordinator_speaker + else: + _LOGGER.debug( + "Media update coordinator (%s) for %s not yet available", + new_coordinator_uid, + self.zone_name, + ) + return + if crossfade := event.variables.get("current_crossfade_mode"): self.cross_fade = bool(int(crossfade)) @@ -774,6 +802,7 @@ class SonosSpeaker: self.zone_name, uid, ) + return if self.sonos_group_entities == sonos_group_entities: # Useful in polling mode for speakers with stereo pairs or surrounds diff --git a/tests/components/sonos/conftest.py b/tests/components/sonos/conftest.py index d18a5eecc8a..8c804f466d4 100644 --- a/tests/components/sonos/conftest.py +++ b/tests/components/sonos/conftest.py @@ -120,6 +120,7 @@ def soco_fixture( mock_soco.get_battery_info.return_value = battery_info mock_soco.all_zones = {mock_soco} mock_soco.visible_zones = {mock_soco} + mock_soco.group.coordinator = mock_soco yield mock_soco From 24d6cf1d58216458ca59af41b2c81493724afa52 Mon Sep 17 00:00:00 2001 From: Sven <85389871+wrt54g@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:29:22 +0200 Subject: [PATCH 065/930] Update screenshot for new MQTT logo (#71023) --- docs/screenshot-components.png | Bin 154546 -> 121315 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/screenshot-components.png b/docs/screenshot-components.png index 999f83ed03250e3cc630f30f1185c67c65e921a4..8c5e41f5dddc50c7f270baa6e866b078950fd76e 100644 GIT binary patch literal 121315 zcmdSAWmH^E&_5VLu;4zpyAJN|?h-6G1PBg;1PL(M;O-$n0txOegAXpj-7U!A5?G%1 z-QDy5yx(@toikJSR(Ds+t@?FUb+nd-BIbM2_ix_3!Bkd~(|Pj-(f-Ywx5lW*uXi}q z)G=Ov5Il7hW!_YdlOMhk@9d@3rQf`%NkD(HM0%ys+?0$x-@L)<{qKt~=vr#?=FLlq zvYfOY$owP^DMatj^I&k@*!1cuO)?QP_4m@KnAQ6e|A&*)xvmNSxw21BEC!?oW?B|aMwmzpTLbp3-0YUq@$IGqWzby#lwg2}@{i33CuKRxz z1oDfKJAUI&LEhh3RJ1Sd1ADrlMZ0R+G4biV^2%Z##t9fM!rAOQIZPUN(l|LBVst?5 zNbL31ek19x3@rc4;2^A<+J|43YwWJD>4C(4rK_Df)C}GEhq1cPq5EGILG`&TDqB+? zKbWJL(mnQ;+x_ZTG(%<5b8>R3x`k!B*kC;nHZd&2>g42P_%oLHKpL9>hkc9=ccJZ2 z8ry&W0>jg33@iVu<^uNv*tF%n;rPjVj(`hXw>dc_r9`hx=SE6idn=5m{>!7!@axBL zp?f*X@l~+@=?-4P|8yq_Mq+{&!7eIl!o$N;AHt$SQ+T2UW>&4d@Mu1J6|}_Rf4cJD z0N`oXd!&b3ZKL>05xn*@l?H}q`~HB?67fH2#QqO92VqZIj%a9lTAHmdYm5#bvuY}f zD>iDd(`(yXvLjjF=_y}}B!i%7yrSh(ECTY{G03*VCf(%!wRbo9-Kk zrtDS8pWB-qdOlP^yW%++NQ{U+Q|Cb1__V~lv^4G$51c}(3b3ZpA+SK#!*z`*z zLhYwNM;o~R+sXcf7~cPP+2e)$e_X7SqoU23>_1Y?rf9L<*1f-bBA z)O>szM>B!4d*gX*CQYBya&yUzI|EzPas<@p%8jij78_l1)N+Ni(>cvp+kE~eQ;7Nu zwfkM#ygsb89U%&!c$IN?W1#8NA67|B<$n*?VQpJX^IWwvPj!Kj7L2Z=NsBq2HoF>0 zW5z8vP19w{Ivl-BajHzp%kW{HF1_Sb6(Z#uB(87FD5&VfoT<|#s;VD8WYaSsJxF+X zdREXF%zFGd5jRqK$+9<3C1f)oEfQ5^H7KWMDp)bWk}OZr`)$AvO0BJ0Sl)GphNJO# z>?j$xc$CzCUj0BS_?f-VexmVv47aIU4-$_b+@{U-a&OFI6ga5l@83%3dOTN=qLRis zT=p}cbv^i{!+EKG7<_p(qd9>eh}y=eo~_gs^pBpOpC4o>zt}`zFT9+^Q0t&xiIJbq zfxo{Gi-{W$BIug^l}S%Q#yAok$*39!S5B^6v=AOTPp{kAv3XTW$$^i5n$SbPwCNFoN@FB^AYncj=={XNrR z;NwW~U2*-QUJ=U`Yh4n{t-ZkKmCULTgb8^rf6I}q+S{J z^61$zvk`;uuufaoj5i_UgJN7Q;^bC7hr$Px4X-==lQMip}}7_-hQo_nG|(gI8J={ysj5nzC^ z3w7=@E^bY)renUqftQQ=p4MOFCMV;GS!r6K#r|HWTO}6aoNoBWyf0IKnI_H>@1cP5 zuNE`t-r1|MU4l>b*)*4ptSQGS)5dbSdW?%VV-NPNOt^MzA-aK;K{?9{u6+JB#v^2# z!`|!bf`4sweqG)Vt+BOax?Y3*p&d-&#_j>c-AdyY0^!OJwe_4C>?9)H@NW?@K{xBc zCSlF`$8!TXoPOvj9s}2>&D&UxOZ8Cdz`TlvO z+1=x!pV%an2s7XF`sfdj-Du`U{+*PK_sPQ4+Ke;>L`lwzXD3-RocR&h?H8{QN2iyRJ_ ztzkJ+@N~7MELqTBqv(V!W_#87``uLw-{I0l;aaDO;$g*4wL`e-DZA6wIwj_|{O{Ak z!rb!fWTNX_glV_u_J_Wu8P|HCh+Fkgi4`;6)6*@kNIe-0{T)T)T7&&~C^l@+J2dBC z^q)=*!``zDeA-m3b(oTAIej=Tv8(vdV{s#SlRlN{tB&tG2LSe?7omsYa$zHhO*K=_ zx-2)2XyB)tUJBx1K@-!{KR;`j3LQ+X*I55-xz+GIGbLN^3s6w-mnf>@CQ!)Tw4~$0 z=d`D8y`naaK5bwUA0ieIR|6=KZLEcdR?~}M^hx{pq+=o?3vAMuO{-_8@;K_4%HG7( z<+@{C$aG>7OZG-$XJnZB569s3AyG1b1>x+G6h4{P@wm669HP6ax+kuqRJ}9crjtZj zteP&|mr=nsIE>BqWTEEf$#x`tktP|H|J{`PAO+LQ-Jm2rFE20XR-DEK1)I`2QfgqC z<^3)!t>J&Sr5gb}I_Og~JMZO>QaJaW| z)Dl^YY2Z*O{tKU7f9h}$oxf22H%d=;^i`4>1#_pXO!r47vR{Xnz<`FaHR?$f{ikNa zc`5o=g{W;%WlpD>!I=>eOEl8%hYl$$56i>=-_h%%E!931lT^H=>Uv;;Pw%PQDtMFz1ze<;EZrp6Lp$o-9^0 z2N;a_<0EfSgQugONG$>9Y8PqObaNInOd#e1j6-Ph1 z*&d`~6Z2twukw84Sk`y(-mV6!HK?ES+8b{)@GHFZVER0XWIHtZ$}RK!U`&r3edb-TwoS#An9eqLp)d(L-( z0{@UYjigtM@?y!d=>Dh&S~)3=q>q(&?#Jtl0P0Ir?FT5Z@{=$TuI4R*M|N0(VJC~8 z5G~BnKWGu}qLWAKnaC*|be;}9~c|H%C7nj&aWZqzXNFI3E^!(ZQV>U#??T~xKB z)f-aHqFWkhoV$B+F-X<4bvVj9k@`#j_9|uz(;Aq7uPHW#)vx-lkC^iJ?OMQu|M>T4 zoOtZ6T#Nblpqt~z>5gAY`)<&GEJI`gmc<|I5A$QPwOxL3OcA1F64>L==bw{^Wz>xypI-+ zBf_xAM3kQIwy|4|E8CnqKPs_YK&uBx`9Cd)mG)_9k1TefoFzZwTX|(gxXs~~c_`1% zz9mLo%j{NwEEJh5<00K>99%&-Wm@e?9|jU*{jvWN%VuwT$( zN-I;mO_hXMx^Gu3$9~f7gRXV1cXpeMn<^@q=JOIg-1!@)v#(^YTl>iE614o%Zk^>w zvmw6w=w;BLby0E#wG`yeJP#ob3Gkpz4yTQFU!gvAgl-R-t{Q8tk!)S zdAY4DzwT)Rla$}TJaGL*(Rx-bX?Mh6VH3iayt!e{m;0(MdE>^tj%$WS*=-@0@QBJ4 zfovx=9%yy&3&JZxWoCPe6ARj>l@@ArUbO3AnfW=9xT|pB<|~J=8O6ZfM=~Mlb-=}A zGsLCvPQ7-bp5E#ALl7S^FSvsX*qQre ztqWxH<%qg=UYEP9cgp@ElkKdwL`4lJf%j*u58Ykhk9l&`eihbf#o-k5E#9{X9;+7AxpO5oP-X2Ex2htUp>>zy5h_(>anxFo15 z`Dy1#>$nLR#C`Y?I2WHm5P(nAV4RR=K5zQ7BB%zB;X4-B=9u8Rb5*F;uGn6V_tEU| zfM73=AIF;lKtm*@e<`Nu1@r<`^@^cv#bYr{{&qdM$u`Afk0QvFeDYoz1zWa-q*Z-UgKq_0Q#5aC zN`W9;hFps{(eT#bC&q0NevC*S4?f$bm_5)phxYrA&#<&CJ5PDc9&f!r1ow))d@L?d z+}OcbS)m)qQrUFJhfiz5rk?jVc>(3WF(7ArkU)|?+gWM3v{^W}P~W|3<39m9;4%_? zYzM}X^4FW=G!E>!VwkZaLwq6L;oS>0+qQS->uTFP9H)HW5X`OmaQ0uGF5T#A?Uo8X zPq@iK*<(EBNAFFhl|Wn8zF(dMh5wkyri zHzFxaFOI$DMjjpjoyH-tLDU)4_|z`@iH2M1&Y5YQICmrCS9|Q(^vkCwEMzblWMf1_ z>plSKf&kAWG027bf|&BQ5fJ1)Jo9~w0w(`7DwHSpq=1Gr;hmZOHsEmXq&9=4u{m7= z{RaK0(O$mw$hzdy23~ZZD^J@*;f|s9zV5IV+>*MQ4gWoO7yDh>31Ez63p85NYHk?Q zKNEAAf0E~bdh85^pspG2ItPctHJ^V<`)ZvoDvCue3o#K85%p})uHtZyvh;31g}kA- z)&wU|@-NBA)ETQ_7hzqP-fWKeXP0e&2BKs;a&RXpS7l%(A%(hAfA0%VMq1Ao8mo zouN!g$)O9bDsX+_L~U2|QkztkqO=?W=AnTcjSo;JbPFWDI0t#>3803D5iX&M?ReP% zu`>!sFB-`r5PD=uwfFlF*O=R*!o6J^IM|=A?)M9p+x7LVZgO3QZ$@6yZH5On!hye9 zh<1~Aq5h6@bVOWHwRX12wrf6Jd2PsY>&Pi{aS;FCA5hOoV!W?^=?hrdX%ukI0B$UK z@O$aYH?7`Nr?odF4}=tTYYWvuHmg7VQF#S+ooWJ@0~U<3+=g_kxhdMeQe9RuWqViR z{RSNDg5J!pe;oe_=dvZGaz%_n7;Z4kMn>b)BckP7$TcH5|%f0cmvXq}gqa-sf+sbDMmbA`=3-_XX=DVXo4r{+M#`mCtdc-_1VJK zVDb31pskSn#jm7yTSrq&Ya}|i%V$O9%^%u9(S*nd+ik75wwNd9+>mufbyC${{ySYexZJSq#GeR2V8|XPz%qXA02-qT+28sx9)&ee{y0jFx@( zep6Xfb0jv>ZJ>^LE5cs7`uX!|0x_<1UyvAalx}+fhkB=jWjLLQ!^Hh@pk_oB;vOqNvE@Ezt9fjYaf#`|m#wHc{Esr}jN_Yz!NM)p^L znb_+6N%hY!{S(h0S)ifu;YQoU=8~Y*k8sBO87ueE45FR{XXV)0#^~$0EV)8^}Yi{GhP4a!=ZLnEf4|;~&qh_Ro=;ib+e6n7IO>Pobsntlc-+ zn>IW8uy>aVevCKTKa&M7(zQP?HM%$VU<-c=^WR)WBJit`+=Mg zjU+v7r`1PPVO;FEyj`gXPA>kb{t14mb+}d($=~;qE22M_se(nLSTamY7)K_UgwEMM ziHguA9Fz)NvR`u2LGu{>U_C1+ps=|6a;LaP?!a-R zTo;5FfN{CcoMh1 z;MS`^p9c;co#I~H$^{25`=V1INqlpFiQwg{o$SWsPctgxLYI7Hb`V=vbJU3Kiz9B+ zzd5eK)s}DITo>z|tA(L0sukI)EgPSl%$u2dSI*-h>mM3E4UzoQNTG}W zXc_uMi0U3Ocy{dC+0@H3tQ*2DA42JWK;uy?jExHzKW8Y9T9kYS(DB-h^U*r!ukoWw ze&2)y{rqqtBa0tDDQr*3nwqhlaOOB|8-ZJPIrdB_V8I(1+w8FZti={CL-uA1feZJt zMj?*Ux*rFc0@^Tor@Y?DG(4r~Tl1Ecv=7}8KK#;I{Wi{iixk0fN+tf(KNS-N-6b51 zXsMb{DsERkOl*DfmLfc^UaAz2N|DmL*-RUVw}VFXfy7mAvpY1bLXe;t`E9lC;LX60 z@XGsIH#g2T3$g0poQaTgOfMmFe+QGpFHrZ!I+_oiUIdE5o?=&%vW=%7gOYv6jj6xD z7Sd2jNENyKn}r?Cqv+ldT{f4-9!A?MQ98<*5mlt1_wK(sXVXWxx>iyzD0@1Rq<1{I zPNh+%!>}coGqU_eR@&K5<8XRWH(GFvW>F+xp6bkY6aP>x-p-l08UqQiP8Hka?`g-Q z$uNvF=9>q<+^#ZBNIaGk)-|pAtvTM^>x~$FlE#?oi zD8jv8N#@~6_W{LKCb&EVONt^`Bnrcm{35z6#ItPdw+V?QUk11_ICP`5M0kJ$Z)5?X znka--!cyOK&k!!cd%!)TXQ^gO%<-OFoIgL)2GhJfXD3RXFAY1O{$aM`IvsK6al0jd z5Ec6!b@Y3BU981A=ZGMIMqWc@3VlLu7N@Okn{&{!`!MNcy|2mrYC9X?MCzD6<73R=)YezG@&i&{VgIa zRn2#2!7olrzlvO*8D4mQ{*7U)to9Qm-yuB0n0E}>z?`>BDKK3uGT&@A`u3zVphPuZ zWDP69XWP-K`q;LKiJ1{psj;16zYz!z;6r8e7QLphT{CH_{>8PM_%Qez={?kB92Lbf zZqZY~K|h{-p)~-Y<3tBDN=g#8lP~nD)ifY$i}rJt0y2KA7-3laLt!1!XWZyq#yyT-S8jlEe^ZJDW zRYTo#|IB9DM2e-(JR5*C_Yj|tnC{?s-dB5t0n%OcgNO>3>2HyKr<9G{z7kJvYY(mmzy_ZR5e18%P_dg=XHHM-75FE4PKc_0XmI|V zUn65KfsS-0$8XI(&rL>%wmo^j^9)k`W%!$Qmo2r9a#ZOfJEVZyIEa*cf%H#!C@gI= z*1o_SOzyX{`NOKxev7MJFzJh4`W2|*^Qw*ysk98z80;%3HXeBYTRkV8Q37rV?Zc-W zosf80^3OShY~#1kIbO3F4oS~Dfpr6m0e&+*$=>X*(z>p z>Xnz>Fww!0^PU4OdadP<=>1aD7P!Yr&EOE)AeB8a1sZ7UrX6duH!P~u*(Dt1Ek6!6 zLXE0l;b#^8-A?yy1s@BcW=UD%u7?NFwsX`ArH2{qnf;x$>7qyAbg4Tcn-{bn?QLdz zJaKHll0ep#tpm$^f6*XGiQ0!bfn8cWzVOk_$D?Cc2(Wnxyn6GAwD8MO*_ZOk0a+qW ziBP!wvm!wX6^VE^x9U~~I19N&#g??~>;C^JLvGFYMc_dDV?S%XnQC7DkmIM_( z=aA>XKVfXdz28-xwc-mPUQ~Q@U{LQlR)$2P(klc(vZmS`*u&6<)5xZsTe)LyrFm_D z;x7icT<|9}bKNky0tzDbxrRIyGNVzzDN`g$|3ZY&3B$)kJA5<1G8lNXXn#Yx z9(Wm7=JZ@m<_UuJy*uYLiCUsmwi=&Vauc3Qyp?n(Z$yWsbo}*H_E~1ulM71jFD65m zy$@M$p}KahZ)GPqLWc8PW6qa|=}0)V9Vnp^Hj0s5lh}sTo*3x#YuHKpCAWuRYTOq} zd?0PE`&Dv(;j<*Tn4404)lZy_}6RWBM&j2n-nN;h;I&6JR3%o&YH{Obgb1C*_Q3mwZ0PXRfs z)ka{Db5GiNw5#=b@}c72(ve50ZACg=oLtqqkD+Z3j2K*=6eGI*b=!hKqavL`ajS{V zJfaB$`ZWDg45I`37-bi5CNTauE&eKrd;FyksAY`7AIFiEcgJL^=UmnK6jPB})pdbf z_V>_ofGz5yO+@VxBP0!yq(tt~WvB4Ys#}fkey1IM96KL3|GR))cP{;&5^yjE7JJ;_ zFs0^ybHX{MZd9sQ#NXIx$-TdkMss>WP9uPWtXIn(nc&vDjEe;wSz!m`*Yb&3Ze#=g z3?g^=IQ5p1g*i~enN$1sx#HPmX8^*73`mYc&$*CBb)3}4m;jeo{TxConW#(BS_JRNR zNHkL(wabU5TF23rq>b!c*=G({Xo!@Y8~iCW!ZuJK{wSwaWZF|GrYKjKTgX8T_}1uO zVq6z4=@rI|10B!zXx`H}_osrFr@{BUM<1ZjhP=%XcaJgtdAY#XECt?o^e*VvjL6Z; z+(Sy2ZujpI6Po*mq?D{?FUhYIbdeXD1rAk~zTzIFzWzi}HFyz+rtC=wGv@<*PdTop_jH$y=+QN4A1Xm!|T#vr;r& z?88uSLVjLhqh3Jz#1OthuKamcPsgMl>y#NVje)C5*MYN^`=xp9f<%&rG`(2e)+^20 zz=ZbU8QVq-gh1%otYRO+X0N~{n2UUt1p~1}EoI(;_t1u>AbEYrcP&t*lRg1&@JWGk zSE9a6jdv>$#_tKm(R&(5)P_;K2AE0{4o0_9>~}%av3GW#iT800K}|JP!U?s~!t~G0 zf}20a=nC*O1dhBq@z1(j#E9MHzsTO(Yzx8YCzKcEGdIO|{KF#xa5Rokk3>%geK$m1 zBHEa$;8EbFTapPP_$u`pLkex!s+Lm7&2Y{p1qoTytT{ z(H!Y^9OD#7H!W>I0qA?&M;?y#muIK+TsJFMM`&(x;w4GUP_-O*F3)d#QQ$IMrkK{l zF6%hg|KZTD169tWSWVNq+U}av>AcCsx3nU+mp>m%Oz^DhG5=aFZ;s0in>B$;;&Ej> zu5G{b5gv_+G7&ap(>Zx=E8x!5#$?J{I)u3g9o7*Dpp>UNFa>r|zz&JuRr#K}FOll_3k?zdb)MpBX4w zw+CGu+Vcc1aNqB{G^sevO#1f%I7Ur#WQN-oI9qqNHrCDw8i3YA%T#oVBdKLGebH(Mqz#Of@X0ZS-7Lev2jXB<_1FN$Wi{o}Vf_9w0`NMf2ov9>m?0YC(_6+Nh0 z1iq#t9ZaI`1o%WJZhkS*TS3L2wqCxeP;mHDod+m1=ZZ%sv@fQjY>`!$5Z%kH{2^22 z#CBlhcCqmeV@(@F*wdJdviAKY)rE7!p6AD?gCXrAbvoes06L}WR>{OLQ?{2)P2AW0 z**BZtPB$ovIjU?n79IK=^KTgd&r+K^$4k{$0+^>J91-D%DjcW#&a-PP_! zzWatQq9qa4qq<{|5i7Gv(dfJOtj8YGcJ}c{4C^5)waGg?2@G$4r9+B%`tfEaP1YuMP7;#->Z+D<4C8b*!y(nQgz3bNtwKdpbus?C;QL-2~TUZlV_xS=z z<`!aLfRq^a_1nXN0pU&P1b?m&9S6{j`zlr^Lx&wr@1tbtT`py~KFBTTO25vhjbYg)~ zxEkiP<3O<%3kT!A<4YJ@)dKegEG*!(Wzalqvo4Uy9N6AMd|TX0S_t4Ad;h(3w_J^- zzeuR0~yB5q$6m$>Zol5cwIUd7jd_uzWke`u07 z3eAOC#iA5M<679OLl&s!5O+8uSmHt^Uka3{s3dzTzcPiZ+@VP+(@c{rMBGMTDf7G| zN~yn<%UzSO&5{86TrFMNHcVV!5}s6@cs3N)p8nM-Jcbv>-&oZLal<6?I9zE7b6HvCKPb->a<9dOh~6;UBhUp#5RO!?P(zk71AA1`HX)~9rHpt+ zr=wX6h>OH^f=fK@w)%ozr?{ybiPNNcNU2Qda5idL) z2Kr7uNeS9qD@Kr^+|Id^1+~EyA7euOUeltC>GH)wh4%d)zk1>4{q#~wD#|7)B>YmN zu9_{pH^eyX;n8NpVjrHjIzQfLqH?EEU96%{-y*&7C9p>3-qZ0wWLmZmt6P`Qk?sZO zXVklJ#P5+_QZ^|lICoxqS_z4_W_e&5&DU2gl|OzPG%hQa7=-b2?_u^al`ekp5~B5u zE-X;yo@Sl)UD2*@WoUKLm?DHawV7(3S`tq}M0IF>>SH{m6=a;v(1I>1N>U_W#!#m? z{u{eCXZy|Dzn#YiGKo0YVs&i3SR!gHCYvI?r5Pa3aZgKx=RFcXPM97aDlrx=O!6X< zx)+OD-7y0Vdt!Gh-HmQw!8Blm|3GdVv=n0Gyab9`Z(SfG7{95*9scAzN=3%+QH=3; zPfTLXvqo#}Olt$D&p$dbt&6lVc|d=)UrIJ>Z@KEE&mFOmsn6S;pL{V_DJLE0V< zI2|_3a^}ejc)W4(F;|yH=8XU#^pgLOlMSV-;|;$`SP#zF?7~Pv+a6q|#AZb#vX72% z4A$Vk$iP4FHf6L<7FC;z*B}#n$|wIvj}+=SSt{&RWCqYPpIsOq}n&na3q|sIW7|Qq$PHP(R}S z=9u$xV}qmfTN#oB0J={iicBFpmr1AC*)!j#_RF|_N!qlZU%O+6s+CM zWtMaXCuL)R#{&M+EU}!DU_{`6mO?xCtZLE*DZuD8C9$kRH^$`K1;*7Ayw;hqs||B| zmBR<8BJP{v9S>b6Hx%&P_c*?C&hSm*XU1bF3T2{GRfi9@H9G->4I*XE9f!J^A}{ry z4x+Ky9G(_QF`=9T$Sd&Cg}89QeZ#LacG z&qz?F-14w-Sc52LT>)!_Ildrf+X9iWJB4rCz+L=MDPx;WUO3_nqHR7;c(tt2M#ab_sG=b;z3uUEp3s#4~CX<9R+q?h~F|g^^ zVSb^Ws0Rd<$KBLadh^3_oi7vV<-!)faFi05P~!25*JC735cV4OVWj$US4cs0yYc0A zc-42sF|vfv_nktR+T&`|I_&RDyFO*Wj<(;+s_*W<+qDe)0{;)E&3`T6hcD0eWKl|P z&x%w*)H!k5H&vo%E7qt8o`!%*z~`(dEgc9DtQ|EJW#>#l$ipHoiM{#~vf_U^2A>OF zOT<eGTIYLb)PVPNkN6wYj#pqS39K3r9^zZg>)9w-53(Z89*j3Nt*+}rZzX@gV4zA~! zXt~iP(VSe5N(e>?;`^Wue{cBOs{ZSC|Cd3TGe-65@8AlBT#kwLkGcYYO5pLCa4n4k z0glC3jv&8+&Ms{P4Utj3bs6qC)?S}wdz&S#d}w4&#zmf3L4b1HX<^wa&TdBA$;4@&qQXG#LW|k9g3$)p2#&+gxYT;h|%kC zjpTxSExyV0?>nX_?V1vom`S}0YM#(^kPqMeZ+?LXp2RTqQBsY^5@=doNMC?d4n zr=f5PtZ6YmbCRzkEmEC@by;H4{+kGx94}s4)IUsHY3gyMcW^6v&(@w~GE3bWqtG!e*oQCGjxxv#)l!9m5B#>{1a}(d&Fi)zv7|&)%0&IlofVEqiT-rQj^WrI zWxHt+k(euGo)28Ahg*QUZ**)ArL3rAc@MN`uv$7* zt6z<(YY_dHtgj~U{9rb~eF3p%PqU(2NO82b-293JA2)GbvedTE3y5F2-9Uuj>1x7? zG{tCq*F2ldWV%QrXh4DArTeidg|s^V9XUBJm1nJZjf}yEwiAZ&H62G~$0cRjo5kcyL%T$gEu4PZSiRku$L)e$bZ91uo_3Dx z0W_*TUvQL4Tz)I0iOi^SGc|FUcrqrw)z@QFA1hGkWl>hfdF3gqaJ zCqrIg(baarfOQc_$Zx?zU)zY=QEq@H_PBPsJ7{#b5r$oUu_3VMKEC;`+l zW}?%%nvuVT3uDa&M>|a7;sFA$`T_(os*TPRg@%a%Qc=th4Y@yb31U|J5s5FrY{2P( z|7~xmd+#Of&4JQJibxdqfNl^f_BA!wwVccm5A(@dZpq#VQKmbhDEfsL640P z7o})>^o~ip4X7zALuF+u3!1CkegYyZ3#k5*H8S8N5A|l%VxoIKkg@_XUG_VmqfvR| z^`?~-eNWS6(zr0iL&31HdnUc?Lsg`23v|WvM4zf3{mc*@pWVi9D9}PE6Z7WbbVB`J z-~6wRgmOZWb&{N&G@3bO;DE6`tn6o=9hvH$Rn2=^)n?!0snr{BW-fskTMad8(BrkB zqqE!}Bw`Djj0O8LU(yL=OtKGL8f`a!o$W9p)Wp^R7+o(nzs>w#hBy@?^x-AyvKjg5 zpDj*JaYhA~YpmNiHfs3rB0|54eVSDg)!V3Lt4!S16zZwu89s{a6OVN}&;AOdoRH@O z{WS71Xv8gKneG9gi3eOK>uUUU%IH>&J!F=WlXF7d4uwMsKd=_*nOl$k!(}SW&#qw9XmnlXSO7I3UJ!ETZ7(|98K3m+ z)VGU-Vv+BW`tRrAazfD0-0)#-2_!vvXJ#BnGM?msRg|8717I@M#yE5G8kc=`Gfu z`*2Z3LH~@Dpn7_;+~0M8bm2DOJ1a_52DDQI1~MQl@7dBJl7YckflL@+IHL zd(}77)J)~)SSBsJi_#WxJg4**yxDZzH%3z}ebay1JJMhJP7WX|9F2Hn%0wPbEE0;N z2ns}_q7Z(HH)O+m^WOI48e7fr7 z&TXe2HANi7sd`NY8y?|$QKa;{Dx-CA?YOQqBVF$iuT>-0Dr=1iM@H<;BnFF?3;i}O zT98wIjnqzzahth{WN-}f+{u&40N&@5y83Ocm8TCmaeUvcDc+&iU4xNYH-L_aV92{k z3PWS}wCtlam@f5xR0{?vk`$NwT!~y=51h^-gLUFJ3%qbRTh;z0G7bu0t@3Byza89n zZSy!(qMyIEU#{QwnhWOtEeY;od<~7`Hu13BqBc=0(-z``X#@Z!02=4niavzvPtxAl z-pG`!a(Ov?l%IXZQ_j`Yx;(YjW6D;JFg9#;1^TU0u*Pdz3Xbj*60Yq3GaV_%%Tg_kpJ z_w--#tT(_lfoG%qzeIGe{jYD+9C^?r=yhrZT>TYsw7@PK&(4Zzi znxMma43{wLh^OwB!uwM0KIdowR%^=6+0))8oBQ4UKa2A4eD^)1UrGZS9}Zo|Lc{+H!z+e?C&#|$zrSTssyn564sI#wIlzcu<6LVNW? z{c)yKON0_6NlLg>_OpPo;nRF$+NXqk@!ibNt2W)5l1uaNg&a;wJ7w7LQ6k1TWD6YH z2e597YNd*S*k#OQ=J8rZ^Jfzc_mkp8=J==kUo_yrG*-tP=4L8)@=piA*@|Y-56Qiz zGp?s2|B6buE<#Y|iMO3Fta?m!rl%1{_CJjNLp)59BV>k}@n!0sz~NJA@k?Q)8LhoN*84#c9{V zrt3VouuW@sTgDVz`51YB!luLp#;1Wk|2qp_-ZTc_MDdB)k5X;=6^*IWx(pFly08tI z80i#lCu*2NOEU_IQ@;EoPzi);7t@r(t*@M3qvDOXzxhTbH3O_IM9zO3NC+Z9XqJoyQ9+=j#KKrCeK>)_`tU^T!fJ1FIc!s8&Uu9d-g zt#Qz#ya85fwjvtK{^+dKI)SVNzj&~xoA}LF*y{$qs#JbW>LIU_$-I&0H>D(wvNKkxA3t%OOxr}G>X4;eZ zK?x!S9_qb)m;od{BH}`2WqJiR57De>k6St(k^YBRiACgKo!NnJ@1cxh zE2-a}t_I$i+4Ao@)gl;<5f?4af{Hy^_-nyy56I(5fy3n9(0FT$?a7nw+MYYzZo#LM zPJ>V8VJXBG+bg%FX$Oc-c^SKYv9ceF*^&}Ht#OJ+7%%Hr0{6_luPez42$N80p1RFw zi@uD>cf)zN&!VvwxYyG#Nqjc@yJW6d+dm=pbBW?aZz1|ZmO-lkNm`s7*6p`>3meKk zEy+7&(p*`r1PYM`-j-q0eG&&^Z40i49|K7XH(^B{xtNII#EJRIu8*$auXI`|SPsuy zD89L970irKMfKjd`Mf8*0-3RN$6$n;F>D}_FpOPklGY;4hcR4V(yNuCtE+zBjqsN` z>#9|X@8(mWu7%qw_!v;N%daScNSqs-tl@}7#7(iuw_dT=scAlINjE*hfuU<57GvY( zI(R-^{m#e$^8KA3F`*qXuge&t@#uy9p!+*zHEg4GJ3fha(Mdi}aE>FR!t@`3eWVLl zkz^qBB=YnQ#Hs$E)C8ovUI_CEugof)(W!Nl5sNSr`t6dJC#J+hN37ABjEiQ}yP{g3 z4&?NVVS~1A&vN)4PVlT=^Y@_3&Eeu>N8H2jWV_IBi5{rqn0@w_catz(j%)N4^X_eN zx|sW@>*Z(^^YVlK?6(})$gSVgcd=t&a-WGYfRz0sQ&aKZUXm9L*GVj)zjy<|M=88Q z)hz%$pFIA`E}gR#6u>~Sa*Imnh>1Iwv9$_RYo>^r27EAMaQ&eyb2l3@7?>YO^el5t z$!vq(yPq&6GMnB_JJB2#K^Bg;6XsQtkk_YJq)vz@9`Tl2d4+yf^5NR1vyGs@w_j^< zK;><;Y#pB&@m?PWZ2%o@;O`WPD0jZ9JU1;;zJnhIBsTz%J-_?J1 zda2{CSqfBq#-%)ZCHo^Git49agZ@yfYZ1ppg58dlwBH;!d_Cds@U}cFC5;ghbs)@> z<5ZXShVcM59Ufyi>@}AQ{vP*g0jJyKGP7w|fr#d(oE$ERgnw+Ojm48I;h(_~zr?@-BY`Pco2D`=g*@_o9=U^kt%cej9O(9_Y6Q3=4NlQmfy!zVDoVItu(Ih?Eh zud~McLdnP5JZujA+cl-OaB|r}npuvL#)*NW>wWyt6}VVbJPC#ILq66+MM=lP4bSg* za0@(&kcC(B3L!+A?REJr%hITiqyv`*GD@KxE#hYbbV@QcY7Bwlz0x;}#AiJoA96!a zOUAa{>Xl;&>G#M}1_H$0p4e)V&V2uH%noJEeL>JXNXhA<17_%Jc7%{}8L_Pe++@U) zODGP1Ubc~+!6(FcmXyE~P(5Xv^-K;T9VG~`IsOX|Mocws%^Bk#am&uQd z5~?mYy6dy4u7O+Gb#K_Yo#cA4cZIug#rgE8g__GIE#81oe9SPwNwE*o?SwCRR z6AnNJy6*;a!|x;AM;k#t`Ka^#X1kIo$|rKQzD<4&Yy{p*3^){i9hF`Tef6)U=THv( z3df|HfI7DwwfzRYqI7HhNZ%_K*L)m*dzM8ENoSA-6 zWLux~c3Io+pJ`d6ZiCnxn~Nu;Rs>!WFs2s+*#RRLqM1$`eDpz>oEyV5bZcq6$^!in z?gMW>lt5+)$s`^IbndgObc)mS zXU5KTYO?1CSsi~yz>BI1GnT6*H4?3eOkQHh43)`J=qw3#%| zL%`!B?x;n>Ld_mm`mDz*@F)S!BvChGhE#C&z8imD!V5+(p|jFb4{ z;uBpx3v}fL^2;6)1-Q8h*V_-8MPI)(Lr80X*ErTgo$w4*!55R|FwTw>DRn!j_Gq;^;t@t5jX z{eXGFFtZi$dL8+C`gH7=i><#a`sEz*+r;OSOo4(N@Qu36Ra&;|CRFgpdVUAHlB9Va z^CzEelN2omEj{si+t={jFeKtEAnv#O3U zsv_O!pQn70|FFGmiC1}>IalUnXk#Rl&R3$ijEpbnN^Fdg-LT0RaV+mYaOQuO!$B3S zdH*H4QFN}_zcm_Ln@)CjvVie^T71BQ@`UQ%jze1>qaPP7HX3qpGT^LeW%L=&xHQW3 z=GV98kzgoA?;*{x2E-^R7mfd24i-{oqv?2;KjA?23+RNW{z1&CQ0Auh*12JiCt$r*z?$0|t562|J_Hw2&MqHy?hHX7r?AP!vZ zGx3gJ4$Vp$r$t9dU%h95e#R4jKRL2xWay~#7GC+A-UYwDmN?syqNLttc*)lu2Zn>; z5wX5v)jwuUi|KlqKqI88m<<_x%ZkgMtW*b5o*?zMpxCUY^N0*lXk&Azk!3MQ8R*60 zfr+fZa~-LKm$3?BH^Pbh##5B2>?N>KMcw_(0&lf{l_0 zv!0$d_gS^`U-mVy@a=8>!A6%4K6%FL*mt%$Z8IPQ`Oa;ZxG^HjZI*OPNZNlGPIr@g zLQMR)!8Yw_0J{4XrFK+G6uQQs-I2i54A&3XNy0p0qu7T16y4+}^&g4joZntHDsoo* zbc~5Dj6W>7FMZe9)cb4O;?-2ZoY6@^Z8{EOXL6C=BSv5$g^~Z2c?*tF3b&Kb5vhYV z$0Do71j~d@M(R%TnaX7+S$Sst_!d5AgKZ|Euj|F#9J}N`RNmN)@reIwH97?4DF+6H z5fT>JYn3<^f=aSDO)Yk~LW}bj&e7K;qeIy^QV9RTUV$&7aDYs+Gn0TViMA1Js zkysdb^cWvPWaJwI+h3^+%Y$3HJ1|5L2 z@AC5dSXjN`#wn;VPfGWi5oKJHkR2kA^ImrpKVA_?IMshGBWu$MKh^cUXcjq?U~A=8{Lb#w4ABO!^VQr?+f0#LNdqBm>J`b>!js4Xl)X_zcKCx#{?P3 zQ=P8ntKC><2%~=Z)fA69Gh@w+s2vrXOx}})pVhD*q1MXc*cn`$jmx_!qb=@fFk_qjBVpoRv zlXod22oJdt_K94tKvd>WP=5Y!n-vc;d@U@m1cIDiH~1idc_~@`T=+ypLS5Zw$y6n{ zx8HFWG$|vHd)jtScZ~dt43pTLnR43Rr;D`G@|oyTr7O*YByE=LG=&G-KK`@!M2n?j z`6#bzW}o$`MvXtH)JF>}*rP5jT!`{J*kxArhh_F~H+T*6)D+rxIVK+N=EYEYZfEHm zH@N3kiihr{t6ZqmzN%ErV^$*{R^S*9QU~6i$NOXK9I!{K&`M5JR7%i;FU-^ig0w*N zzh%2y57V+8p6m%%(00Il^|iuYe$3dr!WimjX=4Y_5A1-pce~xmUM84dcG8QV?wqnG z>+RQEI3PhB9oq#q*{CAU*#D`NDT5>^o<&{>5Q~$8mdw)pt%0N#z3>TU`_R)BwUASO==FJw zVHnBWkJVwD2)_H>T(@yU%RoEa;K*-Kcj)ZazLn^HXk-#&d7H48C_{uOMR+dM1P-SH zg@}{MZNTvm9^xqTJ_9G&USUz_Pslq$!3+`Lh{!1ZcoD2)ZJ8TX=m=9VA{3LHcaT~x zCtHf>nRpWX`6x;eI3TvpU!riEES$2{P^*FE*)A?!{d=$ zBhFhK*AOd0tYuMg&1!Kv%zuC-M_EhHz)g&Bo-}2`qY5WXVu7+uk1F9*mINt|@>j*z zdAT5bKPwK)+Z4tLoT?q&DGm0lJD$U`Z$|c;nJQ1WHa~gP+~{6k7M0455LyHdLVU@1 zIT(OScC8KQt{b918gBPkx-dw7FiDcr%xoB0J)K!e>5Z=yj^!xX-XQa?iG%D%rE%^~ z7CmP}HNpRKk^ff(T})Wh+8W*;QzBZV{RV6xWqd2Fv~M+f&4QXGe;!B}buVR;i>Du$cSYMC!ob;ftQ$zYLH&8eLy z?Y*%Hiv?(3=hVBttZS-#^eBp%-1IlpR|yp#$m18c{VKNo+G@=o`_q#Z<1G z?$3I6i)}v@Ucp$F>El@qv@*(f@8FqJhUuv|xGk-3pOA1RxN+^oukB41IB{(6v^@1z z#PTXIsw%3(<%U{q`I#}!y>WQkPBwfEG-Mr0PM6U4uJ4F}<@jQ*nSx~}s02o85^E`WPaCoIjFD;P^XVYAR-(uwg9%)oq zQnABD(eyUM zKm&DowycCT#nEvVY^?TEgIzA6>$fRBYRGivtJ;9qr%+${%BPPcL=sm5hxlU&m(&8$dhWvZnb?K7*?zh0Y zVf4`L^U4`-x{E zJ6$Yx+9aboR=O~h6ULwWDn4r5QPR-ytSuJPd+r#zQSvBBzJ^|?F{1-OoIav<+DS=iHy zXKq_hm^p%9z2H)2b-A&)R38{+E=i4FK&e>27_AsC>qvwyE`CJL*ymd%DH?PqMB zX$u&_;)JwIg?=F~F~i;x8Nfs(Re)j^`adKLWA2);@A{y=GyJL$dQQq?#FHehpA2}wfqy^de}*8|NTOowq~~k+4EA(moHqPoC zEf(qFEVSn)e6^9bm9q@cxpI%iU!0D`)wkDvnk3Na^rkOp8Kq#9B%QQm+hSEu1=w&V ze1oE9JqFbjp4ZU)!+si^$J60_M!mcYj-gKLcMI1HIG^ULn!V)jc0~{l&Zv|A)m2r_ zoTbrG5pkKLO^yNw{!^qpCe9H&BMgFK-VVI^FWwMXWD)Ri^#%&e5o(OF&{nl>$WqbA zx4dhUsUXA;kAn1Z{vM46yj) zZ2}8(#*T4JnDmeP%YZB%yfdkOQmx%kv^d-TLH6ATBD?QZi{5Q)j!FRpk)iVA-C&AS z%~X+gUIIk~Y#>`8fo*2CX%PPHK%I-4^OHPK)`~- zAHjFxJ9a|vVnUvxE5I9H)CSh8G#TnHNuqZ?B0rJ7zZdbEGgvSbz7E(mPJ23`b-Y9Z z`V$GV-bf(qX$W1yWVHQmBvOjv#AS^EB5p8AC1dquV ziOi-GM=kX7jMtVYu)ZdOZ6*8wiEoqLoX_n04q&LHz0&`r8ijYboi5qs0>RXR0C&cL zX@Go-S!q%Iu8UV~oKf)fnoGDpZi_zod%FyMQ{OO3KM0>hj&r_R$r-i`X7n}@bmLj= zpymd81{!aRWx6-ZHdcJ@(Jd@GrjSW+2Ftp)*jKwbD_->PM#%=r_J;^$NzLfp2(-kp zPj%aMnH`ac$|k4z??_fNBeU#Z-#*!pB^UviOpTXw)O}IcN)>2>(Dz^7u8j)7|0rW5 z);kKoE7WsdOyRBWK<4sbTM>Jcdz@@MUmf()2mj6ghIknK_(nmZu$Z=KvWg|si;Xx1 z=4_@QD@*w!-?gaH5B(4Jz8aPE6kLF+^S2)c;|c z$Hcf&L`^INf36{Db6&*qH?Ns_b7D&`=zEl&-YXV#P3;l-SvuhS;JX^EAm0nFMX5IH z^K$_iE1M;`foWcyvJr%Et7#_0N1)(+jyYk>3lI5CCtjM+$=ACsu-4b5-!kcn1kj z6%ePAqG$3^iH88?wF&MKxmc_ax094oQvFtes-e&+r66b5xXn$G{fHLCFxJo08Jds5 zWBFX9mudtUma)&~NlnMeaXUh2%+@5L3ZlLL#=@_>O5gyv<{*r|}-)@?3P!`O5d+f!%{MuayYG zBcHU9g)b+8UM-c@Uu4$fh{w63qT1Plk*94N`G35YUf;&RaG-=(SD0PAR@aQGxgt&+ z0HdJ^Doxzc6eG{%;k^LOKUm>JXr{4h)Q1YKldK4+7ZE7RgJ>A$HUWkvm#7*?ZATo#!?Ib`PE0S2gyF^(N|>00b~ZRyHgSMx23VMl zG<@NmBFB79b3%eVmVRF_35OXd1{Ek9H&Sejthg#ZSNv}r{HA)85Qi$CZr*@2q)>tG zBM3Tx@I~2r~dMyrO-wgV*;30j%9^sCatd=_XL4sD9carM)&QA zKF|?P;J|yH44rWv%+M1k9&vH9yL+%{Wj1W2aVV7b+b6?jT*%Y)x@qb|)=f52!rn&k zW6U@DoYpYI=!89+XFf14cgw4TLG078?9F^<4GNJK-Wf$bdMw|ZX0y-? z%y%pR_N)16Z4Mz^$VsmiJB&_vk)S<1stq&RwuU+zMH#*@BLGnPRepZ0;pR3G6ma~E zta(0$m2g@b5$5zVrSYe|0ukvPikrTlVsF}Lo$T(`hQe!Pe8wtE=7C&=8D^;K*}leEB!CWhR5UI*32i7r2z86WWCoWrmQ9L}PCz;DwS-<*MA` zMJ<8r&g{gIa@4M6{~28x>hBe7&sb6<63n7}_2Et@nRRCja*v=RWjS&J6ms1$%(S11 z@`3>F%YXjL|N5Xkkf6!&xm8^>P#JExyP3%KLmYB^Qz;QHi zGiM^8fH&CRPl= z+PH=!_b=+}lQ@GX6^Q)_zAl{xy!KlpZtg+D`ATX84M7QfG~03*!Gr3G*oZ;ju+i6; zv-!RjjIqTGW=(*|Wz)wJWMCTfzXfj|8_9bD=Zn(X4L6jo%ec9B^9s?c>Dsuhm-GwIN_1efS`cSW_VQJ(3~0IPd1D+0r)@H zo_lZOIxCrzihuG(Zi?n@e|@b?lYpIW4)vAzM6sH5_PbFH)W_GDXDcbAS8z*;`; zdE^1I!fB3Q%Ety{h{AfPRvZw3j)F>@&bt8U3a(oQO4GP@CbC*bJ`3Lgh@dngO-Nr0 zE}5Boh{W!KO;QHX$u@4W2gFgDNBo}o+z;T1sU!W5B{uSdsKr`W3TwLrEz@xgv@OL`_67CMObYoLf zxJT?km%|dz`<}2*1H@n`(H7!-I*rG?V-#S|S@~<>c?((qZ#f%@pYsBkbNmPR$3nf0`>`vK zu&=tx{%z6b4s~}saFG`uNdWQ5YD!$_<@Pve)os-6U{RnOr3*6}_`3ntfU|#v?+d1B zO2pw2W-=p?+po9cX!&v&1gA-F2Rrv7Uk_U|cHENK;wyEGJoG+comRds4Ei&6Nj$qe z-|S?lqNdguL-b0ouXQbmGfCny+su2>?rafOxad*&_-DRnoA&WKs(N7!zJg79{02+5M1F%@T9}HhOiT82Mo=w7rYM^Qg{Od zhvzN)s}UF9P5B+?m^$46mq$y;--+T<6eENr9s>A>_+}`|i$yr?p7KEhb>Z-F3cHH} z|D$?|B>2;Rw(x9b2%Q$JB{X#|(Lg*+%suy)rbbT`$%_vJfoasn`eNJb1%&S{Hg^9( zP3KW6tT51E`o%9UG4V>DW27e_cbK5|SxK3R`ztbO2cF_k0+3t`AcSxKCIv(P(!fTY z*7&|>@mjjh42+h<+cbILFF8m8If&q64}7~v=zNX2FfZd$y)T0f0QtWG_)OdJ0@IjB zF;t+bgyE(saZ*RvK1SXbxFrZ_(D}TRmT<&?1J9u{W zYksE3`)~=dwGxz=FB#~E3`+C8mn@udBW_AwHlOuvRH+*7%J4?wir z9|04=ZecfQdTW|*~EAB|i^0&x#1@?NW#4v0QXoL|ZJO`gPF1_ZCgnOQ z47^;tJO&7y3v1YvZns|)jTtoHyTDqrn9zgYKB4*7YQM|zz}olj_W!i20*yN1#G+cK}2JdlTC#qR9@;9wU)rnR|3t8 z0+p}B3yl))ZMxKLR+J*H&i*1RvyvElfe$x(XTgtG!Dq8__$t6$Jq%(gOQexlx?F)r zrMXvk2i`6r)7riE(65fjEv7fbBtpswFs_bN5T~0~L)_w4;sRcn>d;2Yc{L4|_Z81WRDUakwG6mi@xUc4GxbJVT$X;%Y zn15HdvFcjGtBIS>rAMd>CJ8y8rqe61YyR-XH?JFT`EBd}YXfNfncG#1{GGzPe`5~; z*+usxco2zuiZt1o4Y)Mr0LGO@xwsQQKI?w9rjwK0uof(Qp`9Gz8K`_VwO@?V+YNQ( zW&g7Mxr!;xlEwFG(|Nx~GgD3SL8s9>Dnp({(C+Y!1#f%9qZN*YSW`mN)aWSY5pFLV zrdxrX*TAW@3*)9lVjyyBG^>d>h3!>tpGW$BFHQpq9mW=VMUUeEepD<_uJo9g-u3Yf3oQEbDwct7;)z+DGl>yh!UZQh#?zU zYIbtj=(s)VEQ8Gy+z1cf>fX2qV&JlVVkBf0S$&<>XQjcf?SLhLFNzzj4H9P1bt@~R z*NlWW9;RG^L5&%?IKMQc{sN%0j{+MtGq8#T78rTTW8?RTf_t+K-hy9Km*d5|#2?N6 z9c?k$Wi>Zb&A{GsaQbQbOU*Gu9xbs$!CR9MU@9+erQfHy?<-Lso&5w#I`Z5WY^q%3 zt43T_JM=&5Ra%RoA(B7jOpwPEH<2GCeM8#FymzW;AI53{RyP%Z>U+TPX~F)KibxeY zefs=Uf)U6c9=!4PIgA+7j~$e&#wgcc&$pWyD_npEE|y86#MNGIaj91QFdv~Vztuli zRW$n#`06LHvt?4cwn3B7IX@FCA?SDu zBPYSDIu>OE>nx+7nWv#_&q)9xr)=;ewNhu_dakQ6f zm_ep%3s{49vA=~pqWKDR zIm(gB+kIWEp8rB02_W~x@!NoPQa0v_s+rKot2bHz3w1ot1HtCaAMPV|d(e28ZOYHQ zH4*m|7bHQzwWcx?9!7(`TLN(yg=^tNM9@%NX_E6+@yOw0lFu&ZO3z?^nwh%E_S-~UzxI7S?89#? zg}OCxOJejbWH0&rN|vT120F9FlX%y0s1CvlQ@R{boNx$5Tr?<_6dBfqh$vT%w(L+0?jV66%fhz+pT;F98?7gex8RGJo`*IE#t1NRuF^9$03o_SWeKOBC;{@LBs6ak@r=P$ooX0Qbgf_#oe{**} zv&<}&#bf?zX2T>!xOD}lshJo&SE}Ou>@(mXSf0`&^%m9j2|l9aL8M$Zk}z8!)KVHW za`>5f1H=U;GTz0G73Sx=3_Jpb=Lhu%e9OHdD2Jza(;oGMxmS1hF3Xl#G(7vJO?PDb zkh3gDoI9uQRb#gj1r3+Lc7OBNa%L*r#yat12DpD8Lt05#oIfXisb1HZF#gT`z3i8< z{ZhW1q-~7bQam8DT38$V^mTq%c|1#totMjodP!4$$qZghJ>yoSX7MGGykt64rhi}$ z@M7fFR%6fGE4?B!Z<9+3JZzpnJL+qs)OlrzyOk(v)QVD+2lxjk%3BF`Cj0d)1qbq|1+^{<*J z>vl7IB>@MGg1>~1rp!nRHy=S}%ibFjuBHJ7S}q*j*`E>P zeR2O=_Q-CZtv1LJ$AwmymS(fIIyC+s!TVB&q3D0YSO34lSIOJwH=)6R>69v*^RL{s z4F0cJ;lCZGv;1WR|J}Gr0$ai^W*d@gYAD?Sq_P$L%*ZI0nwI7g61K8p2KZzNfq(J26!8CLKK+;cl}Jr=3slH% z7KHT;2-VpE$D_-%{ylChlLcZsks9|W!7sA`D<*SJPR?bT>>d_KJBaOHDqQdX_D}_$ z_N@T=<8~hny23~q97W2(Z5!J_=|+#z{beg9HPu?=0Z$^5|L@RQ|2G-zCOa(P4`4mA z9@l=)8m?vdzR8MRwQrkK-TwR%+@a<6FYoK=zmi!0*51~q3vf z{nt|X|48K<02~0%fRLL0&-q%~l`=L}sBxQnQ96&g$=uu=6Fd9t72s;)1VSv&|4v8o zgQT5<;SZ0=_!yI!Q_BsZ{y?3=;-p^tkHPY@vwZVVe^vCQ0w5)R`d`}DpQ0~`73ytW zvR2S~l#dP`HrHBM+Ft6!;`JXi*V~+WDoOiZMmTjq> znu)5Fn#Oo$`nH)0|NJI}+0;+obA10suT&;Z5o2$=4+}0z2C>eoIV`3=cB8_LF31!f zN*~+Na+jQjAk)aIWnIPZuH4+YKIOSi$rzInlW(4FJz&u^;STWP1}$GX`2StK^7hn+ zfT_T^lzkYreUMruBN7{Y>2u?M%GsK_{ZcBkw4pgvEIqaV!C*QS-=Fi^P=zJ?Dq&XT zY)ADFi?UQNeL6^# z8Ynlt5RCe`^zX-20-lu#LcIs8Ko=e?L&NQ>fDyfsSrdn zz|roh6x%Ho2`qwDNK}apwMC^x)L; zZKrqQ1RgDHxh~7og2rt%sgsua8oO0B9A1-qX6NnpPUX%k7URx24N%kagK?FXz0ALL z7UO*pz^2bgDyl#QuLQmm$)iV`gC zUnHNeO>4BINsvJ^H`CHN$`n!qQoCGpuf4akrEb3ErKQsAeuyjm?Pna2qkkZ2w^p>t z^*rdjM~lPGHN$lZrq*iTxIH?2esBlgjp1YEk`Uq?R0?npD{+Atd~{yS;mP%}HL6|8 z_D!(6UYoKTtYT^Kb|MFy(u@BZ8ioELV*`Us=Y={Mz*FiX)#|q9ip?)FF*%w3cQef3 z&@Tltt(Uvw1yze=tK`ROwB$thk<2CR2iWLXA zfJYOl|B|{WOCn|S-@W4?Cnq;>rkifF?DD&)a(G$Pk_$*MN83EMoY9mKf{t_YORa8h z5!Je9pNFf$t;3CviZ@aP{urOuX0u72uO4agXISkMtJEa3t5lZj4(2H|(GRP4*tx9m zwC8%q&O{3ZHKH5e{7GH8u~&KJ(Q>UjclgHBK9E@yrREtGl&4P?t(&8o`QqRSLuJ_k zpX#YcU7SE9hj4YbuE{qu`Y0hIIwg^OZkJzNDL5Cg%&6n{nvA0lX1b<9`K$87JPu3( zne)kuoS(>U1B6Imv7YWaRhu|lXir*z2v&}WTS3<~a2u*_<)Q#;QY&?JJH1^TSK8`H zXn~AfV~DQ2VmydG3>|lz2nO6M(r6_779bps1jE|&n!~DETCK&oGhrHVCmVyOV^f(2 z?8nMS+ag0f9w~(OA(C#vv3&0m*wol_>AS5=PSvEXQrvWm+f%#VeAup9v#)Agk9E6C z*U<@>nn_M)JR-2HR82kKi%PwnaJhu9ld9;OC}VAMbycl?Ag0Cm06AC7m^0nEHV`QD z$(1U5Gqp2UvGj^PtD7;|JfrKWo&WhXPEKpbM|Desm*`UtjA7-?sozZ1axLQu2!NZz z&N{>2bae})$lZ0RNyUEZ-QWC0E|A)yZLsPK6$fOie3KLD>^2g1N96}2Z`WE}awiKU zOoQ$(41Y2AZ$*04qSkd?GN&ILIOkr*k@05c2*7rI{|v{IGXl1rmO>&H^<*H!Y5chx zhy|LaaE^EP_INeg;@@Un2M22hP?9ZVAyrc??;IsnDoqr1C&;1p9CB5rs`*l)`kpUU zbNNAn4FM{tx-aB(KT(EcKreGBh~ySpZSt=_bTf)dS#Y#d?&hHEt3P`fhPK!XUDX@v zrx&khz1R^MwF$hP7`&xT^WpKe|%1C*T2ng^xD%jZgtH+z+6hSNw{}kV*jB01s!+ik5f&33ySc_@BJ@! zt4qzk`Mu+PXpRNI73nx`{gIdSedAkNuUOulj()_MF#yLxc;|0As>yn}we%?OrZ+ zF-u7XU=IC{Ou!^e2W-A7)qwm137M5}T>4_1!X2=C&rR$z;5zHL^yGw75JU`9f zzSmJ2pCl2oz5hN}z8jqsHslqFd-G?ac42xIgT~iL^Tj?>&cmLd?{y!B-O56}=h4mH z7-NzYPp*hdW;bi0GvhS?dP#1amYDbcLx#EmSj|f+Td&Mwn)K|;sryK3B|_=e-y!{w zDUO>lPQ%#wtHvynazHv!WPY+q6Ms`IC7r*;IP%%;FfL_+i)OANyzW^HwYaqF3!=i) zULp5Q=hX&c^NUEE_+p4^E;Lp+~H13MHZ zUHaz7>|fD+g{;2CEfL4G>OYFwJCh=I4tEoqmZE)K7+F5QVr$humZr}TTaF)fuw+F@ z%yGsfZA9tC)=ZD{%Q|-wIxKc3SYPfrFR{1hw&dQML9S8e>ttO;x>W&eLD?o3A=T(6 zPVJ-z_hagpWvz5%^@rsQ(v17KH>Lu1v+>{0rb;jQ;fiXDO^lb`$I_&*Y5Vty_a|Lf zJ4F~Yb&)5ciqR6kM;X0Qzsp^%@d7+o`bWftLHkiTrQK%uGtWzMn91@+F`(=+b?wyG zeW}a593AaI-3>b96~D@>vTWsPgNck#2P1wXhlkiEuvn8@N{Et zR;Ig3uf3s3beljF=*%sX8^Kcy6U^k$|0F+2xw4n@p@9F&gk&YH@Llt(G10wzR=^>e z0LSG`kD?F5n3(O8Q>U51t$I*55P1a0p0tx$VghYW3~>Tjr06f?=JVQ~B5X0ac9S>m z&q!@~yzt8b|x-$A+N4>P2EAH?O6#Wd*9wKLSE58?=0)28EHga=y35%QQkuHDd-Fgm< z8gp{%re8E(JnId-8eb0y+g>EINDhMK(evJ~#hsl!b^%LSb1DXnpYjXp@5YJ`4^fjs zmkPB+iCIf;exe>H7w!zB%Y5bLYk9>sZ|0M4<`!;=a1ob1y=$DQpLsr>4^*$rn+2Pn zm^xdjnS1P)8N^3=1_0Ugnft4;FIwX4C_#68Z6bVTJ_8rRnwrPU^Mk)u){&b}PBr|l zBo6f$t+XY_qZ*H???-P9v|%`>u@xwziCKg zz$q`J%~l0hAi$62ivR7vqu?5gxzz@oqQ(I|wu=OeV^Wnz?>&yC9Ul4li1O8oa{hyF zpR%YUdz1C%x`9p8+k*@h6x#3?Uj30h8ZNJ5Y4#Pang5>GaUIPbZwJVR+4&zk1f<`` z=TkEKY?q{p{b6UEgj1vSPi~8lK2+!!q)wFXk=a+~FxY*JTSjrnb$4 z^ggb9cHLgZD`Vpg8_hDU)o`$(hlg*pFxk>h&S1xfWl!fLS^VYsyxq~fY&x`=Wa>ba ztXD-s`>wm+mm?DW`d{LCYdG{Y{aeFH=@3|;J}l_3!`@Rxn$^HP`Rd75>p_ndskc0h z8MY-j9adtF^UoY|*_E(~8pH?m%-Yr3i4P9iTxnTRaM9@z-XWT`HXEIyk_-m5+do6W zfpan0zH9!xRn&x#>D!|&u4K9My1@JG>_{s6I_qnZ>vdJ;Q6-c-%VW?jqhreuK~jm1 z{DX4W%^krls*A;bnpWkTX-{}`>G1|pug&~d11>YN?i_A)m}ms;+4#Q*i>Wc`Dpbm3 zxv;2m;%NeQry_0vV9J}lLp!&1hz)90!Ui=_8q2?JlU^^padp zA$wb8*{j1Gh%(ikZ5Iy8fHCt`c1c|6fygzQbEJdq3}KQ|u_7kt8j|di~F?@q`8k*YtE=Df(vh!>j}Qkl^BRqpx2%E^bHT zq)VYvx-t+xT~EzfDUV^i@DfOTZrVq19qS8$_Pg%v(Ue?%3~BsNhLIPix2w}?Z}lG> z*=wSvT?D8*z1yx0-BwmRhFh(j&dRoUW-c=Zk`Xh9WF-xK`6m-$`8Ugj?4SLHw-L#j#r*Q#Hg zTMnMmBB)UCeAthL1mA1{}B)Z}~CX~Mt!{f{m@ZJ>eG(BD!vZ+1}+ zw>2FPW4sNs`2l@+Tq8jwax9rz^Z_gUj~ScYcWg#?Tg)D-i4r3is5Rd8!286&mB#?~ zF_F^j`VczW_N~b5hiPJq{*lMaz1r%a8Gze_yr#CuI8W%|)#~`N%jZM(N9s(!CQ!=n zFp|0iQfuya9eeHvEJxV(kPJIH z4c1p--#2`$)irj9)U}zlfez|l!76F9q?VyK zPbHqd;b^SjurnmH@X*cC!%o-QGB0c3u*;ydpkw+rhS0H@8~U`ltAS6bGZ3GZ=792i zY2l&2!RU}VrsfjYmeuBJnU>eKlSP>(eIImk1ecXC(qs7ZbjicH1?2Os8yK1la!fEt zJvrlF0SlBl&rY4!Ej&4b*zc!pTTS7$I8SP)#+9}#d4j0A@8-Zy_$s(e+D3g=wpTsS zSEe@RTka{OP<8d!5wslbr)0DC+PJHHp@JD|yQ@i2ZE1H9sBgUn=AeIJit}BcKX4@< z7!~y1qb5lGD(^|B>u+JOh(0llvf?D^LZ+ttT=w4UsWahDVKi}ONcVZ#{Q+WJAd|2$ zS%V&)Sh{um|A1XHk9n@__@etGPbNQ(wNd8_{sRQdoJ`D~mMk5%JqEY^PSZe=+8B9g zN;cEeKU4~KWwfkA4VrU0C{cNw<>7bsefhSfs0_ zRb*u&33sxwPUBNwp`jU8#BunU<7+XEYKd+aYtDTbo=ES$d$M}BBJpab`QrN2On@N7 z$&|gzEVO~>@_D1fQy~W`aPW?CJ?_mb+e_bR+P?i}b0uWb7LIzG zzI0fCO*lAsv^{%N)rOd4pZkOToZ-lXDs#hk_6*^8x-mT+xK#~po4Gf`np+oP>-D-m zt4%xKs;>EW*?{&5Q`6($VOe6?<6K5v?9ub)Wbnqk&{@;*r@{eK_}J4+6VXf={+aBk z9tgeF(oSeJ@@TF-U}lSRDVw*@;=YVA>@AJ>ky`d{G=tzYp`5vo?RG6SF~>CR1)I;C z?^1GK#)^6{B5OFZ?#HK94M zGFz5d?uL%!uxas@`tJC81+P6xDv0ay@3-jPs{DFZ!LD;MsS1tk4VWxzK`c2Y)mt>Q zX7N5ZMW@kqmE;C@`NfizRo^vE`f%++UDCfs=H8iR?;=w*kuO_L+1*u_J(-p7aE?M) z&e))RG(_HK&Mjk0yZJU-tcKIHLX=@ojdf&is{lWyXsd*L>~a1e-xSTMnBS=_ulR?y zm-jap@eU1O_ZJBAB0~B86!FdGSaSTRi=t?z_npsZ7JiCwWD;V*iawt0PPgDO;?u@J z?6{fFwm##@5eA6te9Bj%a9G4fRhcl zV-#e}!0hIsLmi51rbE~fwpCK-N9XIp< z9K$i_#n0~X^!u&`UeTfPE(!>StKTSvd}03Myu9rv#wnV6nLv~2^V_#B(!pwOI>BVD zQ}#0FDZgqN{JdL^${BceyMrQ~#(4l;?pn3|2uh-Z?WuYseNotp_T81>)0-HZQzCbn zlz}Df+2*y7qo4+-CH=O$G2R2-^ad?@X~4JTM^W_6Asy~I;8oZabRgz%FPshsR~SB1Hzz4oJG&BJo?p3VxRhNcN!4+Jw>gBSQtwSfq*JM9f*BCmPp_Ox95=KXI2bP0LO9n z!(K5$Nzw|t(x|psaOiNoyW4DomFzViFj3er%<2*YcXMO#EIX!S%O45ce9`0~o5cQE zDU#NWB!a$@$%E1HDwp4UBnC*yr@3}r4@FJl?dXz9{c|T5i0%X(DDaW4^P!{LQtiZ1 z+7lZpq$E{kTmkHlQ6Nm~5Ut0S@h>4f$h9VoxW7lO1HZf;^v6@$r0c-F2a+=>Xc*Xw zk_x|5bx7aGTpx7-1Id3e2U(W2Ho5EAr0A~(<;#0@%z`{9HrKjTdtCWa%n#gerR0PX z+_C!-3PQJ!oVRS`526S08_zuuyarlnPNlX_hMp0vEaKK#o#pYB=;g0RNBa_avNm%ZqqJUa$f^4^xH5H~RL-9{ zk1`qP7%Gy?{`TX6$`6@`tx=VnZzAjH*K%~!%)*8bes@1MT0ZO5{PJ-C+a*4$`TiU+ z|LOFt0j6^)Y1TfRgI1(5bALp@A7?a$mf!ZSug@1fV{-8+$@w2au4Wh$+;gM8ZxOUk zCmlIQS=)U>*S0;BXB+CUt|Qd9wKXkA#K$)OIg(O6;oQC0!)evw#fSPr;`#BKR>i}B z4Yao3Ga6MNTK5qeNGkS4`>7lkS&yxu1U%l4m%bhBA#BcH#n<|>hmje`x?QM*Q-@k> zbJZszG~^nIVYfn%)Mm-?Sre40+1+L+w`t{!5~(0(!lqa%8!C zlqwl$s3G2A1Yg9cSv-30oj^xahig&*InXO*%X}6iW&jFCqnQhIo&aSjLryBo+2K-zx7Pz?7t#gyjx1!D{t&ax{7}aTsqnjo#QuDhQxsE zM`;m)rf&jiRlKkUGcFixGsM}5S&a-i8mo}lA);LIEQ@1?X<+tWttGPk`ChayZnbiZs|FyDmK^7M3q;|~_bCPGn6IW*ogKlOs`}MVcW2C(Vo+Z>rl8i2 zYtw}d*l(|jZZ&hsThVs=_wH_rd$gyc@@rXwwf;8A2k{%1qm)O=i7Qf-6=RFn7n6+x zPB+RN!!k<-@&z=re^XK|-%KqCT&dB$el8^a2=x=W(NuTV?>w3lG z+B=3|BuHF#J#b6DEQruTqJ)M%$rp~be6YA-ob`A28{uXX9^<}fnNEwKSTeSv82ox~ zp*DS5{{D6F^>ucqHOUZI$qG|?i)o9xSc>Y{lH~`fV!abCgN)qNnR0{(CTagN9w>rX zQ0#E2v7GUAp)IfvFV5TVod^k5P5XT_ z(bYyXO0<1rYS%4K3dPmZwCTA=%Sp&AG7Zao#IMERi`6W5VJP0O;5X{l*MX+@w7n{G zmIFD-lf(hGCnQu@UoCC3QccSk+?Gl>JY5{vFGXe8v7PjMwsV1Wr&N(9(Fx;YO$=Uf zj?mbj@Iha=G*CiqOBm}N_LhyJRgyVPX-}J|qMX|Hnd8$5-m??2}yRwLWUvfpL39sWw z!uGeY^|+}cPx|pDyKmv^_^PXM^hoSHZ$}!r>VRY8is7mX{Y7_b(hkKxirrxiT*ym3UKx+7py6&4b zML!NX#3_Rs2M@%io^njN3F69-6Ml{N?pNxQl95F9X!Pkm+g<9Z>_fhcAjW6N^2Kx)&(mV zMcAZ*4rte17Qg|?4shHGov$jApY+v9A4YRc49aT&<53aOK1@MI)UK<9Dgmi5G{+hQ z8R(SQ+SJpGki^(oV*Ri>R@m_|RL3xag9s{sNPJfIf?XT955|cCtT$Y3ndKAdAd(=7 zi?|irVO&g|05L=~U2>c*kPyyA(puj|;bczI8!X=U8BvAY7_WMw{*5?0(6HaS2q=xUFJt7(RB-ZaKd17lbKsKyFPt+Y85S5mvuik#@MV%B% z4P{5Dt{&opxuDE@Z8oaM&%2Ob>NP+0(zIyOf!iL#Q}yjV_s|}+F@=|%aeK(com8T< zw5c-2Qrl5%gDLAzZ>qQTc7rIRexA{dcCKG!ws%XvIlS(wg+rQeZwuK-9)74k+z#tC zSOuJ;N*nx2(Dx(lw zG~vyvt@Cjim|XnTm{5Jc`|Il|0F)hnlzr@D^6VB7)mZU!EJ)BA#%*{vzSGzMv2uWy z)X(8<_81bPU}A#R%S~zHwHJi#TITM2;84D__pv?nnqe|e9KgmrpER2b~KSZ8$#drC90z9o%_geZMtc^9i; z^zQQ}B3n|4gR7!Xi6Zp@`l2nGJbOaHlOR0Bejt}zH&E5->@J2Y+v2!v^NrF(xX6gFAk6{caIV>dUxNX1fY<#{|U?9A%4 z-=Gl;t-};gdPDI=FE~9zaL6i{llE?|mRMdT%k}A1P|l!2Z>e#~<1Zz<>!IHlA>Twy zk!i&$uhx~ghWSdk_^P;Ub|tv=)djQLlqwFR&qxWveA9v+{yE+G_+$h*3WjREU*?)q>dsl8g0(_z}VkKH_JM>+k@PMn?nJGBg5(?#l-wU3Xs zoGb{BK@XDeoy8bNt4)9W&;&g52=WN+GB}pS-aTr={e4Dsv87-1=}Yuv3UiP|2$z@5 z2pzzfQtR8iXYn}J-n@6WOkfXW`)ZBT$s6+w=WiC_P*m?BS#$ua@o}0mMlloGCRTCb z-tj0lnB?SlgcS#_1(4znuahQWVbPB*Uf1fq`qw;_9?fZqhSm>#!6Q*Z(R}pn#iAYo z3L%ircr3rrtJMHPMJ_!`rcETR@u%&76;+_P&@V^8D|jliA8>!Ma4?oplO0x9r}u7z zgwRa66|e`#9|M8;5!JE{pzZf-%J+N<^qN?AuVtho^6+E^;JL&6Xo&} zt_7rG^FjOCM*vfoGR1-l$0^fu^RkJ2UQD4}{;at8<8<*r4|z;0zD}YOMPu4p8QT%k zb_zLjk;Cl{^C_iKPZ0`Al2X)lD8w3#Lgo{6kaP^m)#1uV5%vP8`%EK&=6uUZgoL5g ze($E+>=ySfnVn*bnJfJxGY&!Sv`vg47zumXWMzS298gsgm|N z99Fwbd@*4!&IAT)8Mt)?*u=(uj{E-HysCOfrx=FMy|LTg=@gw11Z9jaK3!lC zRs5T=F8i_6GVpZXRdxKR(N~9nn@==rKEpTofX9_se#pfY>AZ?e7mgl33lA7wwI~!I zSU6x3!^oKM4LRajdZ3Xt{aKNQ>$I9FAV?_J^+Rm3LTlT0?IVUO<-$B)-&|F$*?IhB z9!P{l{yYVkp)Nf|+9cy7$1zeBK+ohKQv(h=3)^~~_FIW<+ zuWxWEIW+Ft{XlicD_1uMED}-w?5Z)TEPGu;W+7E+&{6~UzwItjZ#?#<;gyg(S+r>w z6g8`m1STdnLDrlyEi;U4c(y>I!aVQ6=aotDodiI8+?AHiK02ZJq}z3T#z;E(pU( zTLHGPc%93Wz!IC*T`T7JD3*P&j8I0pj!aC9rZR)Wh>&WtE=fg0x6P>?Om*T;OYQ>s zCeJajO-1lP8`ZasOu5_b@(>P?Z;U*vdx|65Xk?iFMq78RtBx2&;!xfVWYQNQav zFe72GJbGDmwQCZPTUXa5BV5-a^L(QuW@WrTDKk;}RPdtPUxSv`G$Q-u@K`%AseGiz zOBfP@*H5gch_BdKasabd)gsgcr^1W>pg4$Yd+?%G#$Z1ds;yqAG&%@$$GhSs+Wr%*@e5ig?mwd}sF-l!x#c{;!hgQqH@Fl5zX8>v{pqoL~H(%$9HYuLvb*Rrq zN^Q_taJ|h!_#ZF>tjh9XRZC%=lJiBgyt?^KTRD)KJ`U)M%d`y8mAOKW)pWm_wr>ny zx7o_H@E@r&nFOx9aX80a$J~DLLld%lXWyFE+C9su^)VdA^T|{>@@Zb8cHDPA@(n(s zLuq`KpcTHZ0A(VRF%mO}sJ@>p?&?!bIhN=)z4@AN>*RRN>cvBeVe6-&elr~DC`9FB zJ!Om2YNIIgO1WlkgT8nYD>OFZ*fM>v!3ttk9u;4n+Wp8%FM;WF=2+7?^gO6f;Uj`I zkxQ;#6o-h0J7oIfzl78it&hvpuT>k^i$fWgi+Afiw&#r0{fA&yQ{o?o+J9KOlNveq zxG51@z=xqpF_HrWyqF@kghi356J5If*+=`sjsv>rP4Yh!x&6TC=i2mKVDA8j*Rf5( z5nQ%%u)@6)iO=Yp8ynYBFiH%iTGc4_z=`NMu30G>!?^5;L6Q{K{2#|dQtnFN#usXj z5BCR(v_F-dx#1Y@3C26FHMbDF-kmkQKHBCiZgi-)sF91tcj%IP6H0y4C7)@ zZP$)A$(QG>4N69r9Pq;nd>Nd+%riWB>$zJ(0r?kI(`6Y9f%EJtYsb51Nm!Rsnsj-! z58wwpCnU}hsOUIPc8K|l<7soxbSTDU4~!BH6rQ%I=Cf6UB?r4a9YgxJcbbr3zl*+P zASbc78wtc|yjFH0V`T*aHdCvI-vd`_p3!!H33VbqDT~x^hhzKQhb3=+JCh%6=?ogZ z)$asX6@vqTEFZ5)tH349FAEWZ7zQSh;fuR12Rgrmnhk5$xYVR(o0(TT^MZz#06VRf zbn8(!O3v?mGkQC5!J@@krkT(uirOh+ZYZm7E$@0k^ZGY%ifA}kGzR<@2H20MjquTD7j)cdAo( z1I9+lRb-~ z_HdRlT-&Q}#R9}DWaim^i_>Fq0)Z!rfhh{44#XWg;Or~kHAO_kJzx2B80!Eq^+a}C zqSTadh*ckf69?fmJMMFrMYMe?%6su$s63%{TPEVEVrpE@UR%?SB-bxRcXtWs@kXE@ zJ}$=Rv+9<$G=NAl9~YEFgp=9alKte|JYvvW(#@=P#NZM~O{ZlW=jkB9XLVEYLPnQ& z;vq>_EiNJ}Dx-;YCwU<&Dks~vw6nZ*eZ{6K>t=%Ty^brBOl91DDdFby?g<@o9j54O zXjzt;=4BQbbZ<=9jlAyr?ap#?zEwnl?;ki%^7XmFG*%}!_%k76tXKozEL`@bGNfkRi#MZ+DQ+>oY3;eX_lO;Vo)d(7gXoD#5o-D<1nwR4J{$?+9#9k?J0RER`0` zC&CQ&$m;ptZrkm4cRb0(da!2IpacKuXs~=XG>GN$b*Yd zq_k}eM%!9Z;LxQJj<~k9VUDUf%SZu9t<#Dz^n+Pf0_2FQ^Y}y1D5FhV|IQXn1rF0o~ zMJPUFkx;S?+=37NWzq?lBj;AhuM+*<@cD=0(g|BfP`&|2?`>V|0RSb&8QcL@Yl2V> zum{5ei@+k3xMtcNKFz2knIpOg)LZH(H8`L?9ntF{kDZi(;BmiaCcWu_{p;X#H#d1i zxJ_5kbmuc?T41(8U?v6^0-qalBM15(A z`k=->((FRI)GX%)135R?WrAFhTHQ#eRlWvR$&Mnh(n-N-`W25jaiCFL;(c{KC-bG@ zaqWL|V=igbQ#XoJhtCxB5ytnVBeT?Rh6Pu^e@tKI0h@Cx3;#~&DZ`36IjrY(@IS)%)nSW$GW zo`V6|#zl%y?$?+7rra3Pry%=mPFYUVuJ#8p2h)JB_i|8s4L_HjSgg24$n=B$@|=ls zW<8FD)_XyT`y+S{_3bSa>$Ba1wT1RcLcj0pM*6~I(gzKm=@Z`4Bm9L0oV*g-A*i?i z(>0@jNNueeHlctO(2ogBQu-BhUjzg@u0m#79mxFvHJKCSvJa%W(i*s5?I!C+(rSZ{Sjl1bWIs$ ze=hpn!jR3#OK-GAg?gchMFC&eUQJ7~lc;K-z0{|jt(v9f>8^+jaZ$vAkk=LCf@c_s4)JGhvYI-O z5j_3qBu4a;HOJaXPAazTGo0NBIfC5WNJBQfjj)aDFOq(8yOi5>0~iZFpHl+Z2h^Dn z6oOeAynV^IsxqmE3>t3{Ka~bRz;ehIoIwte3f+Dl!_k#aElKPi^+B;`bJSDTIe#yz zk4yl8Umw%Mp?RlG*T7aHK|~uhmSWPUbmuPjCsdbfN|NiXmG>9P6$xbU3Lv~%N4I!Y zs^P&&$YemJDVIL;Z6KQ}XW&3bEUcpn1c5ar6QoN3L9Cc*r%T66fmD9p1W5uyyHTKT z{h$){KJT_dQiJBUT{0Y)P#(MIdSszy5H=hudFeW<*xQPlKMm!P|1q>A50bt#{qT%^ zqOa3t((|n*R1kF3W)Alz)gtseM+8x+%AtKk(enI8w=J>7JIV-8(NG;?VEXf8Kc8b% zxcl%rV57usm$jAMEZOetQQ}g_4>_D}H+r*55GX~g>^>y5HI$UO3(PI!lP_yX4V#eY{2ffhCb%qM0C;&$Jor(sfbjap<>ea{IfK z+PqGq3CLO~$laWixAWL?$C5#gf{#P*o+FIT_;iMC^M^5f-bk!%`5blnfRc=##v+`y z<1#(QV!1~D&Q*p5pdVZB;xGk|m{ZqHcU|p^UjGg!aen)>XMxT&hvAmnE@`J`H)QJB-!C^)~)0|Q|_@U zt)2Z#$5X6hIq(=Y>n`fuh<2u7>j`p_h~zytStGZlVb9YJhWHJZ{d^6q;V{a5u2gW; zrd*6+R@ClyVlommMX($3aNBL$)dtN>hU4tk2W|utT+m!H8se##aQkzsmsDL@>d;Pv z+q2tkiTw-w$BU@UG~yDReuDxbjGDo3AhQ@gInX2+LwHaO1RkL8OS^|>pMU-V5{ywi zWuBhAj)=bX6v#96iWrJ$Vt-KAI8CFTiFRyrbYtcrs)_7DF0EE#eeP{rL&uItY~|LJ zGO+HK-@{0qB*4hhf8W57Iriy4tYA$+t}cq_!M|NaAD;`-*>SG%4`BLH2ZhV8fyvfG#No=dHfZa{aAL7WqT*1JYT&y@jHEduIi#ha0(i;$+@;&$PG00=|Z^BTand4IXyYy-h_- zlPC*a&T8ZiJ0S|D9BKRa{n3uebvP|*3pU$h(Km%`f`kX0l@ig+))yP{&w-&bio!^0 zD?gHeY9z+0-!8pv=bGA_h1$&j>sRf@Z4mp&yCc80XZ?dw#7u0Wkckwz@_g$Rzop`X8TDq3kTP#1>4T51&ZHm~>QYPlf!er4Z8 zh1co@>onq&K3NeIKu^^@4u}>sD(9<#Y$Qg}bK7G}$L)y^ZP=>T^2zBjFz#8TmKOD@|8k<9Gpb<( zZ~%cpyn`Rg!!2m%i`9B$*bn)c{CM#gOox{_japKlW?A1tV||}fect4Ufb%@@`KLa_ zv(V8fMuC!g<3Zy)9Xx^7i|igW+C%wb9g;L7=W%X?3r}&;6Wz5!-4>oq?Ds><>*{oI z(D8-Io@WSC9LBb??t{6WRmHGH5Lvx6X8D2tijSIl0Au*{PoE}~lnZJJ;1I1ixTjx2 z@#JHbTq!7-tv1?lF}hj%I#Qr_hlqm2-3{qm5Lra7qqN>@3aA_a>Twgq&sf2w&n@hZ z#XzJUvtt+NKH(dvryd|ZK_W7b2GHmi*behsZDB05IeX-V;JjT9@Zr*Gn-e(xoXaU9 z&x@6b^y>hGn?WO(Wq*Jl#R?YCX>E(gDnd9hhf1zcIAQ7=oW?gkOD`KO6WEZpet&D< z=uEbrl(-{$nb-ztuxY&VX!Pk08HyDR`D8HcI%DjE-SO_B2|w-E9UJdV24edKDWpzR z`ppym8s2{UZz=QNvekiw;nRj{j}6O)vV@NdY65;Jk!y#y^h>FfeuJ$Tdim}G1TTJ7 zne;C@ByJ}(PB%1T3UUP>bssG^KQskH)QHY*Q&_{RQBkE!x$A>77lk1=Unw0ZdEYeg z9*Q}@2bPnAbfm>TJEwEI``pk%i~~a3^e1sGRKD|>H9V^lio>c|xk(Y9Z}cf1nlxMFL& zn$gf?`b%brL-8gbP29e5XL6t6xeAF2KJSS+4FI3FN=)4-I(gyB=fp#?>Twf-0CKzQ=Uzi*30F`y9ox#N6nT%;dY$WX>s67ENB&P^hJ6Vtg4Sjnv|P6MX=c1Lp| zg^b>EDHtcsK18((PiwS^3rkANP3Q#~yo6kZy&s#MO{-;NBmgMlx1C#`(b1{;_^=Fq zQ+Q%g?iU8C{J^K{sXZ{1kC#jDJ*jEXlEuL}IV_tp3@ z<%wBp%u+~8t9L#YP2*3cQfR1l*{m*mH+ULd`Sq7hUO+RIpS{lK{LfI^wurEWj0Hw# z7-K8R0b<0jq^$RnrzIkVw58PxlZv=CY8#U*>cStn8!~4H$rsB2og| zx`#{S8ZkeN2c05EeN3R)?lf`3GGBpmzR61+ zhzX59zsx-B%hgY7w{X;oc^s?V2wF}*H@I33s$(vOeal3)v3739yEEF3*W4)`Zf#nE zAaS2Dak-|@OSPUpYkiR`7)9!-LgxuPP7$tk>iK}?Ll1OZgDC&2yw$CD=R0)I`dJQ9 z)64k$p`MPy>e8m=Dv?{v1hdLNYpQ(r=-0uegm+g}4sPD({CWsUGY>9W@9}47MS3}xfVjIWWhZ3ZifDJm z4*88!odLI@D&Lm2%1;oR#XekbnP?75Uk$I3<26eTr4;mHV(cY}#iLIyE2DM1M@ zZ1g$Z$K-fK2qk#L*|sA1V4in+=#v>t5Mgt!_4U6x z>nt%Elc4w`sumc?GdR}EH`P(X3DK}$*8cj|zw#U?L*_4Ar7`?`*TtcM^v!!(9}W>w zO|#vndPU^+gicG2nkC04*y6GB*U^8kSiTCd8ET zK_}30D643EB+HLqHa;6gzz(sZ%F_oWLUdo`Wnz*#P=1WdfBrhq?mf=s)4`~?M%4<~&`+7Gha`y+`8u2&F+2 z1)9tqlzQ%Y!Aui?yO~%egqAiO4 zI3h(NzBQb!rCm`6%BFs}xvxE6PK6ih1h+9^3{mNqp=Lk1p0o_SYkD0ipyoM|00ykZWa$Nh0M;PfUx5Loezghp&CTgcdFBbz`Akb5*#vA(vC=+Hp2}2*|2&|LUq38agJrdL4~00!H0J?fZ!Eb2lO*V^hW@ z&EGgY>>B#M7wG-|d-l@O&s7tVp8GRL`}XqkZ!T{NDu9|hFQ}RSD@dH#CF1^73}tqO z7ukr#Jo1epGYAsq_MR1?N&24V=>%rcj?~Y6K9}DMBU7G?gA0#B5eZbvFEpWOe_uk1 zgEhtf&X2t;_`)#2+ejAI&M8fo`!^tQ-a_Vc2!U|GXLh9&;YWX)gLTz0QH3vl%K3PTMC_5dEWc#9 zJ{GLr|FsUh+M}{L;83pQe9`Kvcbsz9b}tjv&bW39eWIg?*UrSxmvd2l@a%C$qcT ze-l?A#r!|FO^cTUxh%M0|L4Zc0{rd&+0siw3OxRQHf-R;NRt2028Ck`;3fah29_%{ zwyFQSf%NV_;9dN`e=Yb!jOhRKJn}F8|M)b$wWDKWfOpbVS6|=S352O2DfRE5{d)=P z9veMqfXG66u@OS@&zC_#NlCj7Y|0P(pF`2>T14Zx)*&?|bEX*)5%HZ$bY68-P)H(sP71X6Ap6?^V`F1N0hdmIXR&aIO2KUcxYv=$s;6}KZt%kh z;JWzmvsjf~Ol)q^BcVXr*ZS8NJsL?j7IEk0h`YN$AUDtGzq!uA$@vmRT2fxV=e_wW z)FH|t+F>MO`@b`X;N|{?zW`$Y*Y<+%dQkb9$*kNJJtV&IeDs;`C};L3fG=PS5DMz& zq&l=!hg&Q(O})R#k0XrC{a%)bs5UvwVgpW)8*|{LByMb2ZspnJTq5_c_|B3Uq^$RY z2z^mjw;dH8B!{QZHrJz%=5ozmV}(o(=R)dW<2M4%qgA(nprV$WkMG%Kg$bb2AOY;f zAi&)*@2ziIawk3Xlyk}$s+BJZ0!YC>tvCNx)(%bNik<)otG$UcfPg#`NzSXsMgOb= z;C-95$UNcT_#IFn3L!r1x^>|Lw~|5%yr}>}Ftd&8x}s9RUUiGtCJ$~o8->k+nQy_v zZJukF0^misSicmK|0rw|HC2h(OFNt3~weDJyrDPyF)@I@+3$Al%Gh~SIu(mf@8oel3iFxc?7hs z|Gr`#M*#>B`T(Z>2Z`qR`?HF_vbg2M4>`%MYaJ#M#3G4+Jw=&7M4X|?X^9M=tCKSY z9{186<*R9G4gg1`+WFvfyny117avX%DW5(%scCCB@2|Emd}bO_@BTvgi;SGy#_w_o z!p{RTyfNoAj~~0^=Sh8gdUnPe6e2^-nZqX=aM|Rv`h9K_wZ>PG@-6W8pyg)m+5|9z zY~BL!rnfGwo7m%@6vFueF89A47YJr$WKhKOwDJK=C%3En(WW0+?+gG6SXNaPU+3|W z!RS;)qflb&D-?X{=w6Ip77+bDuNIxmTLGjBRkha*;{uHgKu@Xf;gaBJ`~lVQ+FV;j zTJ%i4j1?#0c_*vArjq{6+sl#)TS{1zfXhUK+;kv zJNy-)6ntR`G3CR!6xEhvM{)G#%@j{iHvlQs01`|5g&_}Z+dyD%e&JofI@q{u-DkyF zd%E7LA3MkbAkWWE|9~I<{l1@F0S|BAMNE%>)ra||W6PM_@iWVNAY<(I7wfZt&tIKB z?SPjwuym~fJc$3F5rVV*Ic)sv+lQrP6Ca;uPA;xtkFoRodd_(MgujD(S=BKyxWFf+ z?qYx5)OD^J2jG|v89Evr13IFT)zsfzJ=1{jrWH;(Ixu?=z#hHg!F%)7dbQ@V2Ok@3 zN6A*n{)_|-Ms!*L0+8^Pt(E@yI6(CH0#69+|EHAlt?~cj?k%I@TDq>$B)Gdf1PK-h zE{#iqOK_)y1$T$w4j}{!?(PI>+!}&AjWim9dvLq^ocBKCea8KN$N0wgySw+^UA1=A zsyWx1^8=tYc9!$}fTFIhzO+3fLssYE&1*#K-2rgdZL1Ew4mMhnEGJ9?*WB>ed&7C? znsD-b^Z@4*T)+6eYTt`70bAeA-Y%`MaoASc$PDnR)yx1^NYd^F}m{19=#5~_Vz2Z)#`fXdxR5f>}EN>P;@F(Xe;0!wm2*F$ux@b3FO zr=ktD+H?mBx2VEI_mfqlwusKFC0od*M4kNgFK8TvEr|l)oDSZ+bjhH-iAKU8wkC-J zT&(O>?P$&DT!G5uk1lgXBKU*ZT=uFzct4bYwP6me+r&mJ{y>Ff%by>%jd9Z!qoHw3 zjWidOhUn<%l-VJ*-9;5MaRRHQ7#e>t>=2!iFl?71_xK0gCAk*60=pb;%$J^k_ovaf zvqrbcF5Q6_{4@bY3#bEOG}TB8z#CFQfQG^6cnuED zf9WFLgj_2nXxjaP!!olcn-BdRlh;!`5~lc~k3=6&!U-yMYL)lc4?dozmIyLbtgWNw zwRyST`@9>!%_!!^n|EQav0^~rcAWs1wSU$laa(kAVhzFoyR+Q2XJEZ7T*o);)s|@> z8V}g;2ypC;rjWL6=(WUlvm`I9HAhOivRZTMfs({OZw4sMQ1gUe&G_#!YI&==UpWiBv$a zZm1kW3Y8KKpv27G>5Ms(&2YbA-t<1BHh2IkqP+?TG1koNlUZfvjeOtYe)>_h2H4}w zg3$ZIu_|5*3pJC~B!GPK^73{8U$joK6yH^3ZqtFJtD$VErKTDuPn_PekP5#{n^a>> zyA~_5KIESA6qA+fz~}p%CZu_stRA;KsCybbKLJ{i z7wBz(JNbzF=yT_of|)Lu8&*e*#g;f~;4|klQ?3_(g;nHK$ef|0NmZCv9hR1%?Dwx? z6>>tprtj)m10FLIH(@_1(m0%W(LJ~uoGvWI$*JJ?O1h@CB%@@6AMDai9jT3kc3CYr zD<5R#9AfFkx{i#sl*ObySc*myasx{m-|IIG$#hQ?E~s+ zFJ1qgBIFmu*gz6j*Tg_zc}h7n;Wi}kim7n|(4Z%^oote}PO?N4BsOIqVyUYw64mgeQEWrb3Zg>PLV!DfFKP-#lq~ekZ zbT~oPuYzGIpiut)dEX|l3@ockN^UpM0PRt?P8z3TlLd#tk)FzQfVWALgN~?x!ImB# zPspxXTKzYu%b=DiuDsfc5Pnm9bxceFYQXAh2yI%e^A!K1R`bKAV~fh!z+Dl;u1DIp zZ<|_FUoEIa$VuHI_m)E!2C%Ww#g;6Ulwv*9AAk(RJ%Rh;bxOgg6{%O~!jAo|O-#vx z^E;Q}NFFKaTF)8NVg&{+3{LG*rJLzDGHFvkk_D4aPo1(PFT8lsRj;xnwbovSabO(&a+$|}udc7HUCj1lwU?UfindngYX_#=v$-wn zNyzEqnQ%)M$G5*of4rz5vglG;xgC=oX=L?}y+;qLp*Gn~GWT^6%XTxYX2>U{U+Twh zZaa=zl(ffnPbcmB=C4{Nw->}r%#c#~yHmssslI4rtAe{~gbrPqdWvFyYGqA1B#QD1M zSZ?KThzisMEm$srj*_TJf!yY9spHK;nu%aJYJGAxrj2(-hrvl4z=ssqXCQ!Sui z*V*!fsx8M`w5d_esct)%^LwRT{lP6@55^T>HekoMai(XMtBBdwvWR{#jH8dM!V7CN zb@VjU{m@tit6vE6=p~l9nw?<`0j*Lx+MMLmqh@!vr+IU*S-R;Ju3PymLEJ0qIX8edftU`_S+eCZG=<&eN>0Rbf_20$8FaIDXx6d z7p$-mas@A#xKQDRx_JdRjT~$bwCmhMjGD6#2RTif%mv?l12WisVnq2` zHFL%UgeLe;F2UgI@%evr)r5#5+>*^O7%`}?$d%H#Bq=Dd;^3Q>F<}f0`9UtC6uxM? z$bHjr+MC&Ln(3;8C0#$w_a9I zZL^3GGs-b+JzKi+aC#uaV3#b#E?zGY1T&Vh2Io}Gv#VM&xDl9d4|{BQ0nd_R+q9ir zW46V_X24zodokpaHaQYz!Tg^Q_cgN0{4J*1FGBs@(4VCl;M1#d&ssW1k!|G4{NbBOU?PqnQmVq1DRZXge8QG`tCu zL{)u|SmY@_nS<#QG5+eI{LB8Z4yBcZoe~j5hJ}`3ELIH6~l8Ts;5?=aS&E4RiQql?{nE1ZcQm$;3CX8-{B-1@sMgh)P` zB1MrML(jQ@<XFw`%PCFQ>-H-$w6d)Zd+_9F3(4z80!_j>ppW+Iz>_egk<6Iqz+I`r?)SQ2 z9pFu-x}Y`os3&i^B{Cw?AU5J}-_a=ujg^Ykl&ljQhWe&NQ93GTfU}k|&S#$fO&uA3 zF)u2eWV0)D7_9nV8^z!&`S1sFKHMkk7}u5d8`uOgGO@Gb>3x@&zCXVJ+$igR|M{bB zBhDDg&U&V=-(+V4h^NGF0|mm(4^Q_ecLEqBoU`|*K(Ssnak9in;NX@YfEjEL>VyT! zTHvP?78aiS8*-F)HE zi;bFEA|dh$rfFl29y{cAHOjwvSuE9(zmnnnHV0P z#&{MZI;vzH0Xi~Ri+q>EmTy7VJk8wxRYq;|E?aQ|pM5v;(K8g9T$k;e_Xg&vZLFF+ ztT+`Uw3a*pv8C-DQ(?;=I$`BaFPGQiD21lKefy@-IcDgURnE1rqZwA9LUFYqBl7#_ z1yJSeUBdYGZOydvCn2Gu*#Ci`R{pU2{}_oTVskSV1B|}1Je2O&9Su1|sV|(g-qZsn zRihhqt-p763_da{qzbipo_Mq*w6-)D=`{6(Z8Oh^odD8cHpE&{vpawI1ebC!YO=0R zp%jj~A1#!ZzZKkD++G5~CBn`IH%dL)90Q%goihG&l0WMZRuK4OzGR}Vo@lYzAGJh1 z3K{>wXZ!c0HEpnAVo+04lWqI*k4Dz{$fAc8Mor9R2cV6!d%xN0g;Pnglma&NGI@x< z?*3aDnr{JkjHU>z`Drfy+31V73;-Nf>qO6o7`EMI1L`viIAGKhN#`@_w>!XcFcedj zb=J!L7GoXxQiCmYhcI5;j?wG@!2lEXia9fFQuaHCtepi9Tr&C0hQ_^)NAeWQ-0D9F zF4)$}Ek}#~de+7EFAC}9*Xpn)*OKqwzgs&)ISr990RZz~^mb!bx_{9*{Hfue5PNF+ z+g7x84g`$Jk+1KZmX4OlyM+H>w9HKOVVHAoiyR!;B%IH|g|z++=EspQ;&j^=GK_d{{YAT7vaPI4Cnu!Xg4A< zA|feZbhnN{x%eHHPJ2TPOn9kx|2Tl&*EPS{6Z$XAy-_ep8yAR%YP%1IO1OR@z}II* z{|ybndPd-zq2%53utHpd&>xb(=bx$ny^@{1VuH{X0M(h>;Ung;PKGP_*318Yf8M~u zG5!hxA@Te7(ENOQa04yss>E_0)>O(N)|erQ2tnvR-M`Gizjx$3Vh4fX8ft1^Mn{!C zefqQxKttP5%R1e^CA6r?+*ogTc?ki_R9QnqN^>w6kD59@H<#Alsj#vV6TnDuMFz}v-XpSX)lZfJfMa56YKgdwUlTGDlnHf^D=y~Lkz7+o29chD;gUIS zoR>@LK2JF+3oUr(zvv7BkK$B7RUX4Ya`$>DEO413nKYf2vTuF}6+r&*C@CvRp6t7D z?B4^o6aAli{yQ-3bvyyczkmN`p{Q>PJ)PlY;bxa~Qbhu5#MomL+}hY`0wcQ)`rkKi zD=N#z87gjv1@I#$+*o4|%f!pQioiw7TjnBJssuTibe_vAUT!YQQu0QsC0zSo?CnaU|Ex8o)c^I5hsR%)!ETTYBoHM_ zg&XOX@a>~(wL&p~*+X2T{ojDN^016}dTAX~^fO|OA_cm7w5Q!ak{``>(&&nQ4>b@nCx8!C?f6>Pp4-?INtVx&<2!Qz1= zAzwCz7|JAxTR!~sc#lU!6gD}jhKQ`cECw(?Y9Bv_k~cI=_$^%npTar3)p98XQf&d#8OgHu^pft zz=W>_u(UI+(~aebM^n2UOi~q#0w}IEmh(4u17nE{$AI7ugIKAb2^@JnFRdvF!sp$4FQ@AQ zUYqwKsI&2+N(O0QW2~ClT1iSu3cn==_&BlOEyR}Iw=8;flts(Uw7CxUp_rthU!cmf zBpQOK%mPo^HhutZ&22NC0R7>ZS8Ki8#Nl&w7$)X_mkW5?`(%_v3c9k7zn_oyhFvcc zu>6{M*W@K<`d;uyoDZS9kBPb68D{a+ucW}ay`sj(S=c@q;V$4xzUZvH%v>P|o)-D0 z7diI98}=hJf$Ghz;8)|w75YmR(f27=ZJ`l}`O+Pze{HGbW-{1(t0>I;7*LO$UyoPL zGokiIg|Qkbq_9f@MzwkCkDgzj=M`BVjx}{%TwgD|xcJeMPVmcv=!25 z%jI3UBN-gFsOm*+6%`x&HOyLu|7;8C+G%4MW&?_a>Co#j{`PjYRuz^aro4N5qIzVk zui;}zEQVm6%@hWrB_EQCfj}grr!SN*7XC#1ef{Tc#Odw14BQ~4Mke`f9_@0H{uk90CiBGr z8y2GSc-0khy|bLV>5rqnp418-H?P0fws1AltWtMy?uN}j8WaRn+@WH+RFdd%+5c!j zn=$?o{-}6eP&rhm9wrlNddLHI9+%!BHDGo1+A^zrb_FTKP&I$==Z}@`V!NlKhc=VZ zVNCn8U6~ymRwc_sevq+442Sb!O43O}iLXz^@SB z>O8!>$V#dxVUGP=v-|MUwN5c|kI$tGSs>S>l}5u+AJ-JfORzK+KAekO`h+q%cb)bz zmR|)P*)h^?n^lSbQX(dRW76NwvnIO(G&qJO6IGWluqwAIVDd&<qFRi%V(;A4CJV%wB*63YCt)CNmHX*}Dgb z+aCL7c=)>M!pz$TkUBL!rc53Q*!o<8F{l-??`)#3M)%>&1j26TlEy11?#D|clUuOZwF44G`D=NS1 z0^(Q5{prUwJc`z5&T=*<^-tbWC3V+I@b0go!Y2;m@jPQCJ|3}bWeiioJqZK5E(KEj zApK<$7}yw4U+)t#+hjdH8zQEacQ0jIWuYn2YL%FG6SRg;T_bRG636!|DWwx_R(sSa zLcz&P9JX8OaTBz>)6PIcoG(@L*kbrLLF(2Jl;OR|*W$4Hw(pd=_|Bi&@jq7`i)*? zk*ue%m~H=VCZ8=;Jx(O=S1Adec2~jb7@iy0ZzGJvt((;GB*W$w5T%)sG>;cxju9-7 zyK%F56<{1V3v)c)0f^*P>4o1sXU*JuXQssk&2S7CN@MHzaR1&pk?s zh(q;6-qqe)cb>(8g#{w}g{U^;eiBfK&8+bko6FZ<`2GJ{L z!dlnKc<(8D^O#k*)-4mhSaL;NHDF5+|*wQeVeczGuvjMJ}lN-vrW+mv+Z<+&OLG^&vsgqDB(sug**qewsOJS&zfudf3?5_1AK2B%4Bx^Wm4W zOVUL|L@5p3X98=Oq*|t!Z^jXvZEVw+?|jRGBtxk?Bk`JJyQ#X?f?D{Uwf7Fe==JDa zCy(AN`*G0bhSq5$8Ce~)qMTPGy;I|65 z5d6VB8t6 z$J*rWv~C4v&KZtv5?t(n6N_O=@b0&_2a_0{6=lZUeeWu%&xKm7k0~Li|F*`;-BDGG z?>YQtqVRbg;#l2xg98P3DnuJ> z_hG;s90Q!k*V$kQ@BD5e6-FlW`X+&*5V!r=4-rZNMnj@ORTE`pWj6r77|X$oleslE zb#d-N_U^Y*LblD(YARjl%qiGBQYw~S$^Wx}oI7eqweNWRWwsu>?0cgdd<7R)pJkvl z0zCX({HJYMGq3gJi&PV_Rb7H|blVKy8FaN>$pRh`(^pw6Jkg?KiJ4xt-HxFZl?J3;BirTC{U0$=9(WbL?PxUnC<;X4?KoGKoo%t5T=g` z3JSgDT5Q~aVmOuDn&#a#&6j@&Jq@R?1m2^IKm&bwh}li>snf&?Gc#yZe4s!a^I{Dx*4ZP)#Fj`^61T4q!<2cqq(9qD^o{W9LnCnkkH*p{(l*b>k zTTeV05;nYsVrCgC2Sfqj2C1O4o5eBimgEx=vL~!>F40c6&C6+P5VuSE3$4%E0-b^U>1gjFa5Lw2$kS@H&j%(wSTjp8LKY&&5zAMMj$Fd7U z&LYp%?$CD#$(&4fg|hL~nhhg!piB2kO}4AL@}BJYI1}}UMsZZ@W%jKta>ljs&Y#Ra zC?8q=q&l{0J2f_n+8Jtj&wI2uyWu!iB)djj3UBwgZAFsA^}}LoAw+A!U|Yk~RWDJY zYWzH>aGhJH@wA<4lIOeyg}#M11U`!$uC(^)iU^syN!{;g%?)TuZT-mI_-cShN$e2G zC1O?_jd}pNGe{8E47||f@V>XqU!Ron&#bT4pnCD?`WWN`r-0ddta|T!*!82q&3zI& zz-nM-=px+nj^ARoQyw0KNDD!9MsM*Wv*QYWcH5SQu7>ixDW|rf>_$5}kj;Jkz-z;Y z5Y&QiSt#kog3O!X!8$6qvr-(oqF_FnZRu=gU7nJZxV7N=2N6AC%vAt9&`;!psimHB zv09l%%-wQ0HZpl1yqb)NEISfhL%cr0{3Ii(AEfFtu5@Pv$5{w;2EBrZKDhM<@u8gF zCg(6%lpIvs6WnL>L>K(*sv78DX4?OHHJ)2)JzIq5V=_%~6y7M^7muZ0QH^gdXG@ZU z<9mwjC>YDp&}@2HI5+gcd6jkKdhsrHzQucUEYg;;#J~f7=F*kw{HYHQTt$zmIzc^z zhZAe+ut%7^a0PdH$Y>*;|DE;AtL$1)#rKLut>)?GGl!CqS>SOwEeL=u6Auq5fv%A} z*^58WlzrFytIRC_tHpl~IB|T}0KThq-CkJKigs3Q`WqTS6F>^EaokP1LW#cPq+#cG z2aa6x@idIBWrFW6EIv786j3Xc5u47RSv>HDk~Np9K2jM`-)7y3l;7g^=bXK|q8y*U znDQ1O{(2I$`2Zll3xUrWnV2A}-)(che4JmCA2u7VbZ|Nxt*HSaOM2t1~sJ{s6Gk#uwUh6abnf=*9h<^-TxU+c9 z>%-tX;}d@zWh>T#tP|kF*h?YkxG}tx6Gn4#i)R~Pp!o)DwH>%VhcE^9!vV*fnDWX* zvpa6^=w1&B7aqBq)Cx23&=JIlnfz|rsT$ekY}p<(;M$*3w-zr1nePvNw<->uh;aSk z3<-9A7Qps=@-8|4;EJ4cvRdTJ{akR%+E;fq)M~II`SRC+(_aJEIudz9&&HgiO%=7h z(CT+D<-2|wm9vckcn>BsJPYJoT|JAoItADcVv=%A7AOc0m|0pDi(1j(Qfi^N-^I#X z+ZjoR?)i8knORb~TC!6{6#Gtnclw|o9cwSXH72lDs_~V$^CIRg_0E(CiD*cN<_0x? zwX0z|1D+fm9tGZ^4_dQ!<*#ySA%_9VotyoN(BWUb&`?oO=L8jl$Q_|&iKeRh}DmLe_|IJqpZZoeixxPkOTe-sW;8@n$(=|&pYq>hI#akO_ zc9rF#-(0JCI&v?)oqa6RJ$sD)9EZ2u>cs5v)G)K>TCl#ym5<=?v|$N1`19d}+IL$& zdmi6YwSz(#BA|}K-B}ybq3w1ss^mr!tS$Ib-DX%1f}hDSF)G4B%uaj_T9>kb{TQaD z%Wl~|6SDgB$>;YrZ3{DCt2BG$-->Ve(W01EGtfl~gSTLz^1-pJ|SnUfZ zqlo1g%oW`5;x}msoC`QKZeRZK3iN4DKrykYYrn&iP(G$p_jf%>y=)h8%fZG`0cRFc!s?n0leL+}(?NwbNl)7B2_HXk%fDuoJ`R z%?}CrcH6IY-(gpL3yZ^@t9(yVgdiUfVR158`^jEA;=(?xuCh{>D4(K^=hCr|gmN-D z(4f^x5~!NNv7;|15%iMaEh?`zyQ3F6MjIWtbn?Tk03TOM{F#y*BQ zMJ@OC6Oh8~e|;;0wK>uht3FfIbUY!i`aUDr!JCZW7j;{%U9A@Y)q^7r9nA5IPOi|- zEA0QF`pL=)Jbx$zYjGwBcyRuT^%UuG><&f|Za1xc3bLeo-iI zpV!#Z{?1IiS5TN=!$al+YQa%FfI8|ANddhJYkn08_R&=ZK-#xo;dlxtXlxawn)H<9 z_{U2}XbIz&V;O81G})C67Pf|*gq$oYp-5etzJFXQfd}g=x3NuGD9PAG^Il$an?buc zP|Z)$iLEVA>p!jMsRNa2SE8!xZDK&-?NpJX4mf6=ZU5az|4gf(PEv{A8Oej^UFBah z4JxH~+iVl|7xJ|kuYVB`UAa{WehtwO*k1QGI-q45Z7tE#mPGTQc8A|X-=K&8jNzGA zQHhT^#ALyexEpt4zxN{xXy1}=51hJByMprmE)=4^Ak-2;FQBFjV-ttq2mPcpF`t^E ze|T8SsRP?=znrYQteD#W-LLWv$IATZ#8&;|y57m+6iB}AbSBJaRF~0>$?B-uHDmoVHkUF8`r+p=xF^YtrXyQj`Fe$LMk%a|f}CC_953v2BZ8DB~4v%U~H=!AIeC{b{_LoDftlgXa z{$Bol=7yp>R)+Tzgze4Fe3sO(7!I2bGbUk3(oujCS60!VLG}&Z3w1 zEit>|dv^oD)?lK8tTvC0=CvmYJ41Gd_6;9QYFKj z5U4wGR3QM-0-oArgj|Pi+Fczk>b5yh0j>Nc{;*q5Gs%|@kTW?$_iZoGQ;fre=<2B6 z=e3^)PFIn5EmuD(@Mvfvasr;ne*Kz9SCBHNw;auk(yCaKW zvO#4?u!@xO?&c1TH#E!NjBsT0Slae@ccc|ncr}t~;c4rTLGJf+LWAxVl?+YcmIy43`$>6 zOIcG5rq4}P$#ZXAk_{=G!GC8|-8D~7VIP?>S}rb(H14{fAT6G#KE${44nh~NqdR?K zlSH(iTBa>($OdD7lELJN(6t=B(PGg1YDFsPF0vAnGDG5K_{o6~{WJA0G6o{=x3MU1oT`IF015OdZo-qMsgnG1wUwYhRBS(>W>I{N(*! zT*EV8p;}giyG+2uMrS@I02l(-z1enr^mNxvuBWM~)TFl^qkL%cWWaZ6pcJTvb*HGK z=VQIu^Rn4vG8A_gqE7F^ldm_LnAk*z6uBSIq9LGQO28rRQ$PnxCZAmzzIwx7i3h;3 zlz{<6wUg}{AHP3e_<KJ?gJbS zecpu)$eY}Mx=J?sCZtk1jV2pyH2tgZE)T%tnXPxaN)&pZyD-MZRZU0+=mWbf4zwTn z2BaG-l@m+)&5eOV&PJQK%uK^aioClt(D!WI(#QRBV`|i7w5o-d&PIcDETK~r%0b1Q zsA}e;yB`{&`6^f%AOb0GDA)(mjJ>Vbh928>3D+_$^T31gP~x72zYh3eH}5CNg?5p+ zy}@(s*)YLoEi@iaJPZ-ONSWBA4H0H~jB87}i7_`tBMP~^53v_e6tOlT5hHo|lE+KU z%~4H(IRc^_xKaAq1SeCR>#V~Y3XRj_UC*-_usEHWh?P;z!*WW8{^s38wY-w%V25gN?~$ts`niFnl5=&5D4{7S*M2#f~-a=%t|%zk}%*W@errHJsQMN?J$ zaP^3;NbG>OQS@^Yr9T%$M^H|Xk_x+Rv-_5i9f`XB-_3$`~OTN)80h)@1 z%ATg9-SL@BDB5v_HS)OGiCRVSkgO9!4;Gk#>$`{c=6$L+;`TF%Lb7 z?O`Jcy%&}pVi*-qh8A{Z)S0h+Z$N$Sq(1VMw>DT!6U~qd!F;Y}wQH~KV(sJAZLgLf z0nEnmU!F*6ypQ4HI%`#^sK z)O;>XXEPxYd^V8Yce-86bGEbVTDKP+!{xdn)KVoc-xwP=@4AB9^u!_dHMp&~YM`k= zYZs)EA5P?AaCHQapk@5Ql9>c@;IJ*utw0;pTu$y!n&jOutKELUQJ^Ll_>Bs$4o!kie z-P`bc=*;$oF7#a0kxl)694!5;Ye$Leup-WcGvJi`bU&t-&**%-5^>v#a>D#m5bH7Y z0%g?b~nQ%Qbw^VOi-^{X29EGMe<0lfBOhJ9mf3u1^NWd}|4eqZ`=;K;R$D5}qH zUPN|Lg`%P19LfSWWx$Ms@|1_EfgANIng844n7n7QXu4)hNnJSC>k~yeNuQS$7|^$e zYK{ySrcCq7dJ!p)2KmM#K2Q>E(#>|8g<@2lw-zI72CP(r8g@O|+rC`uOD|F5oJ`&b z*55{En=Yyo4lHqe-uhdivXF?g@Z7nouhS4D6{d-l=`mm<%-#-)n5XG&Bq^p7Z>TpQ zU{<8Qgs~8A6PYbGkHl6Hnc>V&iiFc2Nstr!L*C0ryCK5qQQ+@Tu|#-zn?6zz?`KNe z&onASQ-+;_|8O$2wfJ>rz7`Srf%ZScy83xwmVwM03Sht~^o0y9y%HaTxuQsIUk2gedN z2R^@4%HXF$K*pB1zdHKO(t~u`%vhh zRj$=aiXW(C2?OP~$*4F963b2Y-*wq0JFI|CF6ol^EVU99drF~ir8`Uc%Y`-28A<_> zi?21EcN#g%RQ5}aQ^&U1y|-flx7$|#v?szgDLZW|Yz>*gF+^e&K&Rd>U03X!t*>;_ zK)n$PSaGbI_vv!Mgu=EuDk^FU%q~PFtwj=@1N0P8$X5#YI9HkdQk8LDB2ISv0kWD` ztsvFR0j!qA<1T5F1w8HaBa4>%60=N%Xh?2ETnzv z=viN!*%^eEw&#UEG|-oqtA;9$+jhB?lBk=Ve@5>kTb?-`UXi}BbtNwrzKLu7>!6QuX?xpr zHZ?z9xhg33V<-01^zO^Pg@zzv1TsUXjp%sbp1ye9tyix(Na!br=vCyheD1`YzgtZrh|p8hIDNBWHR~7*IdHm&UoRXad>6msk)moC zKgy5{ProsQ#P3HVAAu-d3}>^-4+@win7jF}g_&-*xd8iib^Hp;jX=@JpM2{nOhg$O z`osJTJP&vQs@+W7Ped>d^JD;pr{w8S1m3I}DM8v<7;7v>y?WCwUvfy6A*f{L3ea+c zD{9esRC1M;gu*~@GU?f5CBw6{UsBYjwt(o+S%Z0L_*E(%y&J9B6%{kixpF4!uI`lZg z{b06CZE<9Dv^y9n=5(tths*U3JJoE#ha@P3mK5}vFrS{`?4Ty;Y;Iv~m)pMYV_2OQ zo9)w}u`tAFQKQDesGn_v7!NS&+sTe8?3X!kf8Zmk&9N&W4DA3UCFW?zy9;9%e`LSa(* z5UNpl{C<1^q?s8Qw|q;v(i2r9u6{3eaUIZpJ3;5y#`+;<`DcV{?SXYCzD8Jb3aISX z2R%r3UsSwCwWlp6o+Yn=CWk{+=0}WGw`(g#K<{q!(SdvmCAj%hAaOl>G-0%LAGtZY zZYhV#&xG(Cq!A|+YUB(mmj8rPfU1NzE;Rj|g5{1mf}HeON0H<^af0YpyY+M1cSSw@nmrZj;&={z>bt!2BGfK4?trwc2VXc}8+n$4;YA6{ zRR%hbqVIE7JGhSXG5)SG70AW)r%lag4^x96TY%$DH^_4>tfCt7s>A29AK`kfLgJAmK$lI*2)~vIr6$cdQMI_a+L>PW-kAxnldLc-#7=rH zQR+JXV|g`eK%87TRqnC5j-_J3-eB7yp3u76hU(l|I|1BBWsSig7h8Iktq>Toxs%?7 zv$;ry(kM{?Gq*_ZAe#%it0*ZCT?3US;#tm*R($4*rKnF2qWqAS)zJpA_9o!&(u71*MfBn)%}0uYVU6{%URO z)uY68=&e<$Ky4LxMcRtHnu`ujuK~wDoZiy-v%6c7;GO*4LMIp5k);i5yc1YaA}CAo z8saHUHO1K^3Q7Ygv@HDUW76fKD`yND7emG2$TZZyowWiE&u1Q)&*!(OsGbkSF8Hpe z2Mk@Gucdv-J?$#tx0K2nSxq8X6#^ILXdfMhqaOPFd4^U zc%1v*vQYeL9^FjIvuz?e{ z9_7d{%|1w7_oiA>_18bH%C7w3De^Tml;PTclV`zsIoFFWDqXeaX{p&R+{8$KNZy|T z4bP8{FoU@!ld_suts%I~cDm)c$-6R+{~+UZCuHDhF0s{VZxr3B{>nrsq^`1w=|nk~ z%9eB*?2|3T36-6ao$}LeQnap3RHLf3c4P`z{&YE78wPHNW&HZkY}9k+j%C#H>R9y0 z2|uQIYo|${i*J2Ib$XdYHk~gU%FlAqx%qR1MF-}Icyz4()}w@+wszHiG9xt2t3p~E zd3+6=!u>PF@lXVe#39)G*Z?Qq>c8yGQoBNU*@->@ECQc%&U_eMD`JTx7t=B z1C@oWl z7Aww-jw0KyemZ#(|tX9wKbh(14Dq=^S+>u~0} zS!`44t$yd0nnT5mk^UILhKmpX2yz4$Jn^!YJ}mF@H3U**WssPC%%Oa2OJtDGasy?p zc2nn?2o#$qQ3yyFungVtSA|ZFz^~_wV*F!Z|9TIkAtG3BD6i}CsLFP{J&h6spGMa> z+@L$=-1CdI+#vY*A1vHFF0R$xUC*iP&=#xDob8lp$u?$rS zPIA3wM_dT>>*&jF{?hwG$;aVLT*#o_M;C4!EdA;ov=hj=4(m|!>(NpUMDBHt5;+)Nwb=SArC!@}C zyKi2ejp6AwSJhb#d`rSqY;s%)e^&OeqL$F_sguiD&!xq*oi0389-OBkqD!8(`9(Urcn}%pxh30u@$mXJY~;%j|2WoBb#M z$f?*$Hu>N4gIZfE&Y0`1)ild-OpANRoG*0 z7t7ceZ+gSE5%lve7PI2`Ch|z5ZGP)E;y0DM#8TAcLAY*)=hNrWEU0>3MS{Eu2ZEn_ z>zQsEdr%|YvhjQ{3j~`EP4ZmK-V7t3e*>xmQy1?^^c9UlJe=dEBsn?@E&N_1^Hpd* z-JmNf)`^4U7po3l4H;ll2^WThPcl{ue=1#bgtdHVTR`JOktG>STI8HV`_|i@K1FuY z_5YCfmO*iKZMbGgAh^4GkjC8!p5PGNp@VyHcejw>?hxF)AvnR^o#5^cv-#wlcV?=l zYJQwkH8np{sY=nE?zPw2?DeeYzOEN2{85*;9nGV^RtB54?V<#I}eqp2kz;V zIxIp^Ux&@?ZKai0SJX5$$mad*Dl-s<`6GW zJBPv~92(Xj+ikhi8cewut%70h_{GY@T|4q+)isbWiMN%i0=mRoBPb4n-NKMug^VZs zrh_$Jy9HlsHPJ5I_u90sLQ#p6w|X5e!LtrJ89p8)<@oN<%}}A6^Lj-gew=9$)k*MX zZapd_Hhwp~qMnlWT!%PhR=H4uIOK-;7QOfz&XfnxGUN5uVo?IbsG95&d!l3hf{Y-8 z5SwOt(23^TTeqRa-03FvNi`G+UNS&yeLr3!Dmec9DujKx9;JGRo|5v5`U1Q#e$aTm zR0MFVGrms(0ta|5<3ab6frckd!d;cPSVWd`f9t!usfZ7oyp zKGciEoYTgxpualiNzw~-lG14@yU=hf@i0CXDQ%cfH0sa;a^LHVycr`T2#5;3mgpns zy;Wc870Db9GeNOE&Ve59xy#AqcaNL)eGlFcwN_+XgJRCAw+gT5Y$V)cdX>yDc5a_& zcCg$WUyyUe#(sJCR4th|^=Pb{v{Kq8k8|>>ow|P0Nm+Qy05!+$L%Krzz?2}bqeUkx zbkf$hOM+)N`6k4V<(hdlCd$W_V-E`|@neZ-JLtX%3F${AFVutDF{lDf73Te0%}ckfjL+H>mQp!`MNGXZYZcIe zbgX0DXP2m|6luxDatn;0Ac?S4%;e?d2ark<9$cQhYb^=Yk&J7tmLa+OWAYz9>X9WF zm_L91bPQ?M40LVKP*WRjbd$z@|6VjpS8pc)(2#SRaxkwlqq6364g~u9$>7vo9eFnd=vTOeAb;-@RHO>67MZT7g{Z1`t4e<#k0d(vV_h z9phkL*e)t8o_I-U8ovM5@Z!61DAos^{LC@Z)2^D{r(A$@knpXZ2(IUHy6*C%Kq0e$ zkV#%uAbp6NjRUTb+hxg88T(eACozR)`U3*KX}M#)q7^GoFi1n;;hRJbNuhCP<-ON5 zq=|5_TL#i7rF#%g>@lI!axwqSdi6ejfG;>yzjd~02gXIXLChM-QoHJ>-$iAUN60dP z#GW3FsiKDbYKBcIZ}85^1Pg$>;xz&rWn{2*5I<>mtMK&>0BtY2N_WwC?!tVJx6Ndv zxb~7NTw#%d_7%Ck)&*0pS4M8ED`l-V1 z=nVvsuIhpAh`klA>uR9!3r;X*zV+@UE`m==HiZM8TKe#edne6-Yed*KGQ;40SEA2=0rp!4O5{5%bN_=_GO*p7X$yKnxJe zO`C$4M4H?JC5xgh)xtp_8&!b&IyaaCU{SfEw+X|QXDD1wI0TKuB;i>ZdsXU<=w3Jy z!3Rw+P)XHn2(VwuuD0YCJFgm zA~vhXIxm8?XYwdelZ(7o>fts1HwJ^Wi2J(@uX&z*qfQ%T1;LjLISbxH-Kn3>G&_5H zZYcL>K0VZ2hJ5)-gdG- zGMz2*g4VtomSWfCuZ*Z#`MTN>rdu(3Sac zy%_^}ID5!=y0yN6dTc&;<2~>8M)*W=us-0{twgJys57`5DX!YY@Heg2F+$DhDT^R4 z2TfLi7wP9uAKTl!s_Z8}EQ90Y;v&_`v=dfA!XRyvHzP!B8ZCV19?c(Y78>yZF1h5l zZwLU%EXQL`RMg*o?RVAQN2_eht$j|*@p?H+FZt(Sn{ceR=H_blGnIz#h?Ew&ZI%MN z^Qf>E8}+{Zu*isjIKYR6u_X+{+U0BjkiU)kMVmevpp!^4ia@=@lNUW}al6vc^p~+l zd?J8z#+ND)GKSgf*LmWSjyNx?w+d%|t!z9@9%($5GklycbMorBh8xoLDLsq47OqEzl5tuR;JRou;V8~9rWjoDxK+^o4VEKfs01m@0Ik^qG5T7 zl66z))TqWd`v>28vSwK2GgU)FoNCDT@giN#MhgdjI1+Ff7Cm2IQ8)(nRP;9VF+LHh zA*{Ajv!W(T|84e1XXQ0ym&m4c9YyjviI!x|E{=2|ie#d0z>~RdLamjspY0rBj0GRk z9$K9&qZ1Q~^I+9+4%_3J5~v2he0yP@tIPQo_iKpohW=;CE!6=> zMF1SX@I*?^tOR7tBI{v*E|);>_B;qh?9@YM$AK@`O&HgK`Bxw7L6qk0#h!4ichN=? znyF$x@rolJ=1EFu)ED}eh?L5;6Q|G297iita}>A8#XpDyET||ld{EXEO192+(47|p zqy?r~or{E^kohQgmvGS>uOr|H$`d*iepy2zZFSKw-Z^?eK`pBbp0``PJ$rmI1Z&QgOF$+0dUsSEQntP8V_ zFaNWqul5dOH=_g2daq`L-BV;wqshIp&fTnXWM)pn>NJ}hosf1NB22N-IwL?IRW*%p zS2p@_Oq|^U{`RdJM+AAF+fJL#v?3nV}FPtn9=r(!{pN59nFV$rJOwEOv z?hp$Z^rD}fhNj+UCL_yL#jZ(&n}8(kUZmK z?RB)0FARavP_tY_wepoFL1>qh*y-7&23EXEt#&hjD1W-a?v2Z!ceKXJ2d!kV*vo}y zq%}7m$_(~Z5XS6(4q5K7mn;`YH{4vD1wjyh^sMBCgmCukG3GciuPmjP2k?#~pM~p< z2qrdjTryPK2JDVWW6~CG#T$fR(B|2wghv1EuQFzivrak z%nN@hg+i~?JloqdiSL!#L)IH6Lm5y~i91EC4$3yy{(3&a+fm;_M*BC%#!1jZ{FL)0 zn&-<`(vjX$7ZdUavmTsSAdwf@2o5avBp|fvUy2V4Jc92{!9HGf`g?ZPWXe1d#H_np ze^WY`#AG%<%MJFiXef$eFBk6G;-%BrX=BG-UAje1@P^b*u-8$$nX)~5Z`pdf4JT@_ zn@4fR^JVKjxZ~aJegey($rJ51yHee}IS+SGA@3AxZ+XX0-=IB~@kZJW1K!U(O|xk*c8wAQ zzeg?|Zxa<09jmj%jl>t!YT;xv`kMjqxO8cK26#0FQ$?{nwug9F^w~||KKZBRUr0R( zB|+KKn;RQrW!l_2j^jK67eh6-YXPWNkEWY_`q>^M@>{0|FCXqao+W3lh2wXt7wV^n z=!T|ff|BkTCMlwT&i%5I_K1kLr+-%Md43!>!kTpW`>@#RZDo_t@L$or=j-+OTiwXW zOM1d2kV6A1r%xlh@6m*dBOh~f5{+&RP7lxBTb#HOUnyVXfV#fOWtHAO zf5+SJz#c+Oh&p1nw`gFPf$=>p&1$`s-b$j3K5P;5><@J&nSF#)T3}(Ic*t74zO7gy zI(C2@)rnj$O`?~7SIN<~in8r)TH#1XBf#n)ETuY;@BMO=p{%d1Pad__^#k>4HCp4} zu|^REDrc!AO@(tpPW|X&^h?xjW|dm( ze`1a*NvFvo{B5n*GNn((WmP@ASl_X}$$4-}Op?fyJp&r*4+S1DuPCkRQ?5S~7p;_^;lh2NK0X4n1`)gCZy55x=JD`YNLiSihk z0?cMC2Hj?rm?nI_OWEavt7DH7AmTvM_;Mr zt((=2kne3($NGu%m(}!GNAS8NUpfHkIyNIC`1+^}-4K;jC=z%HG|<4yy+2cyJIU0S z$EaD2g{x;rlEP&jys<&?)pTd~BbH;BthBtOjXcx-)UN=4f6v!OfTxEWDIhT8^bzek z4(RMt(8mmjbyS~SDJiu5a4%%zyiE=~Gb@iyd4Uf13Nxd%ZJMSvYB~>gycCfxh-Zpb zWfBeBHvR(=aOAUt8NK`aQ(*%DWpme0ArbBLa~vJJ_Y=D;`5jcWcW6A^(EbsM{5y|I zE^Hp6l``2=*P*L4#~ z*Pg;uZMyN_#5H2-=7hsxHMdeu!A5E9*bWiA$vQ4c=g>((LBkHH1?!RVpIb>3YD-@n zBF=UaI0Qe}jxmr z-p|!~X*AC|)&AzXq8JM^T++oNEJ58?*%hI0muVS9P~%!@w&5WFiAcKiBf~fCPLbs- zaZX(6Y8X0-H-AeRCr(dhmB@tf=P?rPYm+f}) z_@g*Mk=s0b9iKHW$8)4@b7!$3b_aplAh`GP&z{;-)^JR9Y8n<=Y1GyVogUy7vHg{S zpC4V?i}=eQ9{B{Blh|9aO{thEQ8V@Lc!quIFM;gui;O|31y2QR%uiG_v)IERJS zY-*UR^no&1&?P3H-`<9cGQe4Az#5_ohWOq`l|HCo4nF78CchNZS4|;+NPy17{amYx z9?cozG^Hej!GY8`Nswf6br0JWkw1ZcXjZ~3-DatGiO*+qArspsT!wIIm0<1C5p>dN zf}MKzx#j@md5PqHh9Tb`tuLMG+TOh@mwDgpanSJm5EoEI`fj)PEiEG&iq-b4q!8I4 z!|n{;rcbrred(90LzhwQS8eW9Hk+N~wRM!Dp9<^Di#=!_s8gDf6? ztbd8ugLAJQ5n)31^7~PKTZYcgPDiZ=ARlJGoh&w%)lsF#DtK%)Y4X~*a?&O61G}?E z3Tf7-M#|>=?R+$N6Ysz1G|7stJYOu>T_^%pFX# z(X3Cy9pGs^64_pdW3}H)1O< zr9J#9_@=avG`q^zo6O@dRFz)*jR|#Vjy&UOXs%=*y%pzp7>|&92 z{Gecc0Y18WzAEmVxypK*>u;V~8b6%|-7;U5_-LX6vVh__}swaK%z;E|@kg%Q!qek;61%@hd^iiM7;8_tFe&vxWa5m9aPzRE(TTn&SHO`kQw2J!_<5#s(aRM^tiQKC{Boo zD>^%f$jqbv61|FyXV~y7Oa_CA=FH^GJE*B}De>2ea%& zdpqLkDiY!qEK2@vb%K)~Ugl+Z_Kp|BwVx#qQ&LZ6t22o&PWEbXs)6~!44gBimC9o| z>WCo}G*6%Slu=aTBmGd1rgQEZ$WgC<#=?a7X)gX`~LaKKLtgzKxSxKtP`{k&YygyP{+KvQZIk zykPDOB_};F@I7hA{o{3e5M7$=;jG+YAKCcU_EEhN`oaoeQ;v4AYi%udIA;!#~ikoph(?^>2zG_>gc}SuP;${)H--_r%ev2n}2Fj_sDQFMg%{{{?yhe*eQ`71&OGM z{K{|``bm)^;fY5=w18ESIV&AgS6mpFLo*f;8~co*@-&T(mDIJ^v%WRXcnLiaO@R{} z0=-{lT1b(`U#>7|)~)71z|bszZHul;H}rBwArD^@GGY-NOMm1#QM88q(yUEs&e{q? zl6sX?WkPN2gRnw!#x>!rj&wmXO@=V*<0aUfzBmkYOzeZcux_2v-P@`mSWJ{vHLLWL zjpJOSl|CDD7PNB^)3qoUbB-o6dfGskj`Cp)RCV6;l%4T3Yh&Z&>_#=j-VCe1;iu|k zfU{YFQ#$bu@GWSAm;BOvWE=PBo#<>XUI_z*UWX=ds0r;{#Is$45%h~Tl^-b~$1$#$ zLzMt6G|MEBBJZFE4z671?j|9w^eU-so#|*qBQIJvPw1e>xdS_WrG$#)knT&}ua_bB zw=mRDy-76&NEQp2E>5H+Y_mR)GHEgOj|c=cxZy095N;C@tL8*R?kCLtVvU^Yl@1>mS|yGMGA$u#8Cj+H=XaTi5|#Ugz8ptN)XB zFHhtL>4#{Qt9iA$E<5)KJczB6{ok2gO#6gtewfUt7bMtOo(%Rr;;J(6*}#bUB~0?) zf}M?*ky~M+Wi?h8#4z}dI(@we@6CIB?b6amH|n;=N5m)YW%AWgAN`l3P)yylFE_~r?-D!(Sz@1-Xk^Vd}+|cV@a@#cXQSDhB5c$DQt$W z^I5YJEsBtH3KJPKT3Ea48}*11_CqDh&E4CWv*89zyAA-=Xh0mD_5G8Kj1;94i8cy} zKpsu=@noS3jQy@{D9;5Ex~VGV&2MR<`vY{W9V$%;^`JB3?LxY-DOB2#DGV%(;L4Gg z4$MrOPJk&{3VZPup#}g%B905C)l?VX2Bc%hYkbe5Kar{qcXVvel`b;gP9$ql468E= z=ZvW9EGv(w#0|o}W>GbdR&(l@3pJQh^AB_IWNj3~R9!d8f<+BSSce9;W0+J7^9MSG zs1kSg>ka7?=+~H0Bjld-6{rX{q=`kQ)2+PN%7TdZ&Ro|%rD>LxrLa&Kgw*hyF;UF+X4BP^%qgP{_?9tdYavC{i!)U3b928TxKP|c_Vm)qS)*>xaLo!^uXe5V$ zPPl%$q@$Iwf{`&2qt@bPjnv|SSN?F$w`0eN!iWm zz$Ac=<2j95ayls1P9{9rQd!4%UvKBCptFEt9&u};8u4`#r5SvF8$B|ZZ=cw!MTF27 z_8Sdad@<^1U>Q+HY3mK&N+)d;oo4Ek{xQgT1aGER*fOU#1%rxLs*0UbiMVRN?-IN8 zmSw^aNCABBv$Fqc+>g2b*%fJod0U!+h|dvk^5uC^qedh>C$F}9(*eHo&!^8e^vdcl zp*cgjTgMSoKN6G|5~uUW_ead4SvOrC`O&hG-;=6nG_*wv@j z41~bebDBC~vr+4^2f_L7uKJ|>s~o@!qNxN?3u2YGI%ZM(Y}i8}~zwc?Wy zEU1jm_k51)yD17;x88I_i%qgaH=&`#0t=VRjFaEX8Je)kLOcr4qZ#uCOZeTnqa?*h zW5_RXGB4B6)%Jfnf5>L(^E${Dgc5-=n}Hk6I z3IcmQn5CaalSQWu7#3G0_pV(WYbCGK;$ecwTW=nKeL#A~7sP97xvpZ!2}K1}g|to7 zUjuoH6-{>ZkQ(;L)4Pt)h|;K=5S^Ia?>;W2)VcJ}&wZI@)78}X*GIV^v%NihT3XtV zxxG=fmhTNyq(6Ta5f!BXf*o}3xcL4FoY-|ph!mNSki4)=fi_7DxUagprfw}leTO9=7>$Hk+pEFl zMCfYK4r2F6uwz<6m$x(I0Q`|Lb092S8>`)k-=!aefM+t;t|D(6*hCjeI3PAws8PjH zd;yta;t@5f?hHL1CyPb_^ri>fmbJ`R zYPg&_&;H~e)jh0pm;5-pJBMDL+()ZR1{1fp6u;9dStVw+DBX!ve8X`#hQ@ebd+H9n zrs)~NsAD^k|9Q*HgS&!i=##i?;Im#}a{TvGv?m6FT3g?H>jTOHy-4r%ykTFi#>ezQ z^w}vlRDvDJ)R$+^Auro<`m9!vM&K7BRWN+fw3e!YRnA2`cCPCS_kB(AU)&FyYva7M zOuu+y3mQ10i(}XcZY1s2s#dmgn>2xxv*%ZwrVO16S24p{vooXC`vpR+tmL7W9a5lm znE(Ux=jrGpj5|uLTq^fLIHn+_>erOont#*`1TxAGwqra?*Z^v8pra}z5x!iKf$ z<02~cy^|yVv_GkNmjhi~MjNQY8#k*j-I4h0ti}WSx0f}e_LsX=!RbJZU}LbNts}cD z1aRy~w%$C$YP5b06UwkN>TvuSAHP)-)OR5s$Q$54J8oZ6?JBL*Ye>a(i$xt2Ia0K5 zxg}`aySnc@l)-Lkn|z;|X%S}ShSHgeGSdf-gw$E2lpFusqHGpIPDl57ej)Sz4&Hw) zqeAV2DuxLW*x5MqUMq=s7}T56H9r!R+%bQ&8C}BS5IpKW5{QY2lSKHuq)*+Gguh3|n(}CLK z<6uQ2fR7$))8!{F{o3iWdhn-Ylz>ZRhf?4B4%_`^FPA2XiH>eu)V}{a`R-z2gT2Zs zrJ!lXruiuT9uT$&x;rbDxj9~*Zej*0Gz9KVPiEApm1*tK5f;msnc-#lJi9f-iZSL> z&k5fd=Gi72LxkefH3EDqhyN77Mu~`A(llrbQ|{ME4zU!{>a6yG3 zqW0n@HcTp!#n5mrIDAOcINf4b?A`L+w^O#TnKHqG4jYw7LR5p+|S5KVic-Owl z8J1%`sRkdmer>DH!Jc;ZBH@}K1p8ylVpL@0n4mrt9j~Tp0s`o;E3yr!oAc%F)D&x1 zl$T?r3RQRXPE1d)Edww0M4RUfNd5EU zFlemR7$3}7YgMYp1Lgxhf%1mIYGWBe=?8O5Z_)c1-o638tL(m?zZB#}D?El8%RMHC z^w<|fX!nu4bNpE-t>Cg#%`UcA6so$ymT2Ve$2Bkkky-LxK92>>(5GcBa^M$qay!jr zJ_{w${@8Uva;-N0ozs>$7}Bk1_2Ow$6l=SnHICI0KqQHgjWy$06Q=?vC7T*nyY<{&CBn^sXW>be*wz z;-~03@sPS5Ejc9cmkAgX47#0LkkF&(N3zW@NJ(RWf~RUjxW_wdtCdzajYc~W7!OS7 zY>Fm{d=yLcOM|buAhDg%#dt$2C)t)}bi8@il~RPq17I|J*5_&+NQH1Wh4EsdO z9iu2{Q~hnm(9gus4V#*)B@_%Ug*Wfr(jlZAQ;kkPluS=U|Zg)Digv ze`_*GBGBN7S)N%>WGb5!K(k%mSKRgM)6hUyON2AU=pPQ$5j$0EaM2_7pt?c#={g>p zD@%FvJ_2y8BYpG34Z&lQC!w}EUg%m9D4;=^qTojwnj-f!Mlk)pJxe?wc_+f7L<>*} z_lrrD>>OjNdQiey5jYqp=D^OSvka{^9^K9)A--fg83wvDfi9 zM9_#Zu?cn+RgPppzgKES1@-kr3oiaF^S&%gEbnJ&rukf^paY`GW6lE~IiDq=J7x=RXUeH$#YC93l5 zR*Mzr6LE?D6PIhzjRunWLdWqO4%OdngMnvVSMU?P0K^EMM5h|gg;^tXal6=fgknj^ z6#h7^2I(ceouHy(E*#$jQ&&KH0h* zOD|3}JEdNo++XdR&q_yI=MXM(uX=|>5%Mnkk)`4NC7uthm1Dn5VlSlinDLiIKi7YG*8cqJ0aT+Ym0Jm|ot$m)lP-v*E@Pv4BUa-Ah(7=jI`+)nRc(gwZMijzM!q z&05*k-quD{RIu%ZYf>%~vqvD;@T)s-zbw z3JBD)KQ|T#iwdN*e1}53zd8z#L?%p7;29<(1vYKDC`t+_L_DEOO9GLEF6h0H_)&;V zJo+Ky`n6_NHs(95HjU5C2!6zPtjDcY-~rtd+L3a|wne;7r{k_CyP_x%yJWHW%e%F+ z(Igc`6c*YKh9Vxp<3Z~RMw#|0%+Fu{B>J|z`5YmVnEgF)WKPoTeB^IFStwI0xgPi% zI5iiWd_nDLnh7RLq{U<=btyn|>W~X!!e0JAcl|fRHeWdqNQHP9HjS zwmZwG0PAkzPHBG7uuvU4HOX7Bk)ksPK2&~>^czl*Icjx^B5l$Vy9+2N!#Xs@qCjQ) zXdDX>Vo6O(&Stw+ds$x$r(v|ETTf^yMV6u+qtKFSUpCcMrI)FiYNif2r1GHer;

?*gA zQ;2q=;b8frsh;H=ZbV{4PL=4U{J!hF^?rw5oZ;g5iIzZ8B1cz;<2~1}ke2(pKEj>L z-$CgFAXalZ*bEFp!V-xeba_b$c=6-13iAZDciD(w5p9p z<3Y8&{DM6*M|gOtN{cNE!RIK7;cL&fzm4W5}D!1*71D5-{9dslmH`z=fuT~tF>3{D{#(#-m)l+SgvleRPD}i$n z_r-(PHjc0CMzeb1uEuKe3o7f_w=ruu;q#eavD(2TYP{d6squ|NP$)jC7QIVt_{!LQ zJ0XSjwev@n0v!e7+kHo0W%XWM)js7P5fhJIv_P!Z{Bv`0=~l6+N+6q1MHVe|P&dea z2F);*QB{I!=2lf=fFb|W=wi_04+im|88Od5+XB?epgRv~5fYT0Eec*{)Z~3)6I`4U zhXnI%oIG(O4Ffca0I^!05?uV~4@&4}1jI@rLsQTm*T+3=kcV0_1IZD+>XCH*_#0PZ z1q=+#D)0l!-=fRm7Lzy72sOLAW(O#ozU@J`-T(t!FuZiU29_;j7}6wp4Ze?nXd zoVdZZQoIG2=D^VYr~{K+KV1h7M6^bX5a3}%o8*#6q^~660i0sGs!6{?yMTDsJ}1 zoufENqzpV>Vvn^i5kw{(#^LkJ{1q!qAc^olyc7Qd=J&<&)M%yt*An7?T0!yuFERB0 zOYsNkc_$(>87(awu*~~_+!25@_%k{R0GgMwZ#JK=zZx5_=f((iYMQ3!--Ja(bS1HZ zS?o8&0ro$Ehvpq%1G*AA#>wbQ^@8b0Oma4Cp8}9OxH@)F9~3eofBe9JRG5?M+%~Wu z-muROUmon)d%|0n{ttUJ1t8LG%CtNG67T3EaG`&3U;IT+HE7eYBsn?R73iV@#pkv^ z--8ek5u+f`9X_WV!}kk%Y4e+BLlJ=kp*$r}+oPE=!>Qbame{;@Yj1$Or%9>mSHNot z#A+Dye4d=r?0fNn)zj`IQ4Rp78gEuS>;W~j0ie+BT^U;hc9C1>TYw2*V)4$rB^_JT z_8J>nf3ir4QsD8l3wvS#F#P5s<^e2m^i|IbWComwss^$&qBOX@w&Jb$_8)+CgwUoD z3NWJ*rEuGpXWqSCUJyD*8{-`MjYN!eTAQ}>;k=!=|k0vgFafGVI*UM6@o_2m{&gW8Wv;I0>DdW#wv z~hJ$pvI^Vv1I|a^) zvp@g*(97MoyHE$x^E{LA(!l}D^lZPwS1S#>90T6zy2}A>h)TyjWowY**{kl``|(UF zW|{P;oqHC5CAe7!0O<8;8_LbTm|4M@dI+w$xw-3hvgufpgQ=HIb24ycP=#DV*ed?4 zcy-S^bFD1_?&Af**Ct3 z3%HsacysBu;=^e6VrM(Sr$n{X9w(?qwk3hiBW$rzLA75dQLHcCn2t%*0H^ZJL$%Z_ zEQ;K&*L#z&gj`lE^;-5W%-ftofF0Wv&~jf2wI-Todd*~O11eytt2zgLcH3Y3yg-w! z)ggIWsS5RQGl){ISlh-Eu&SdhU)9vqICWtj2PgA^*g85ontfUCfYjkB(86N0 z)M(#)RS67RvoBy;_lXoN?gjEi(XSy?bsc+&yE$ex-vkEDoRx zT1S9AAS&1dQj3!hkA#&!fC*~0?Aw)~$^-)g(`-M(YJ|oV!);B+ zqt4)K7vf~&n)d7HGK6Xr&9#J&odS;7m{;4WcIjAIP+8ccu9;a;RUMm^wXvlT;&5xL z0JwxKEeQ)7+i)O`&K=DWSdXAfyjeg@LhAF#3fv_4mcM->^<2P`_~N9s$HvBX3VgKv za8m7hfO7Xm*Dq%g&I4hHmFtkV1yB+@mhganT8*z%8%JojzW|Ff@uqQ}8Mxg+f_DC1 zjUz{wCAHcxF_=}fo=d-K$!#iRPDyq0Junqkc_*>8E9R@8fN=TlW006(x@NhQoJv=EWvLV3p z25A8BNs2eY-Mzj0Z)34Vm>2@ZL`5BA{$z`VL_%PEIu{*nH-`C5-M8!;Nn!9k@{?wg z5(GOI&j%(gH@#E2tS7MLwOjUPD>8-3*Q`U@oFLa#uNF&qF*KxS(xZfKL4CvqbP5f#|y}9w>V!GY6e>uqbnF=a8o+wRO$U}hS9G;i|49(RF=Ci9({@B&=c&yQ^w>{0(YA%C;#y=x z@;YF>>C|al)_oUAsWG3cn3qcJfo_G1vkOS=$01n$FYDIL+-KM6>}#0XW6ARP$UipK zTRT$!H5f@)6M*LS$-HwQgJuhtP|_8-l}3oY`PN<046fBW7b5?)g<@%yL7uIUHi2f=`0xNG_xBW|4u2Rv<|_T&VskZUn3tU9 zy_HFq5l8dr%?%BVFL-X@+*|RVRVOKrhT=;|Kw0fi*M!Y3C*`{Icdf3vhi4|nmGqT2 zz9POn84(l-*t&X!pPj4*9UqK@L_|I(O1A0SG)Nq(pT2i7pZVe$dpsA=r^gn~KBMF| zWV76?y4!^584+4T(0PbpVq^l!+uL3%QZ#B{iL#q1)x@UQ7=zNi>((h(So($h%{t1btUg&$DM~~^ zDI(pP$fAj8YWH|Rw{^36lYT-Pyk7%(P~IAUe_QW8_WPC_Q4i$}E_8};O)Ixf=(y3Y3znt4(eZYpkz;~2(J(A&Y zJ;j1Oc?riFGdYe+MH=j z#F>J{Ol;qJ^v#2Ri_Uv9yy>fD@rBlAeg#FRGgnH)XAfxh4`dzwM0GhytMv2B$zbv^ z$t4CUVSPO8S-;6>=63(0=axd^Ml_<-JL7)bB^mqjeAC`Mv4^B~|7tmX`m7|C3Y|I2iFf-g26O~ajGNM{nGTK!zZ#HG;NiPlSdIOCzTcA5l;x^r(DDC;mni7 zf`VqFR%G}*c3C(+qQmH~UG3R}eos0d{9igm6POf;UPPg27;&FrwF8sQt13-ET-ljC zeOO`XPab1ip+Q839W-Dr1*?DQ5P@x}m0=-N;Zas&ZfgD-q1Dk4lI#aTQyMf+!|kuR z6acmSRX`b-5)4_rJj@xani94o0gA-`*~6;%+C(}++JQ>h71|ql&duRD?raNS>-+Cl zL1({p1RL*qMq7IU56Sf66xbFh_zr-0Ox*JlCVTrI4!PHV{m*-@0amg9zW#ryCG!9C z{|y`x930CtU_r#?sip&rfTxT`|5Isd`(@%EV+w0#|DlY`e~nOQ{>Q68@clmpf-R#5)c@y?64zI2 z`1L|_MI#O1SRA1@4`&fa@?vtP09acc9{*infhm2M{F57zK63;~fCLmwkaE|+SRa%K zyr~NDKQ4rcDcdOESp3gAAXPk`FKq2~m2tr6_3T>lT2*ZSU z5q8C=6?6EL23lML(Iz=x=hfmE=0hhKVU_B+p%UU`vucsx;6*<|G@$D(LbI3{(Z5Nm zLth>F{&u&ADRl z_z1R&nH+m>9;Q;`a7#l%JT1MMNSz-*G7=?XnB$B=o^? zhPWZE0RNpfiILEd?>A$8`;BhB4KyEq(kS$XqEp%dPmgOe+O|DF+3ECkXKc@qwgj|_ zk=>%1kK?|ITZaUjV1OqUekf7}y-58X+mGIcO4NxG+H+dkAPTIrarDqIBr%n*Je{Kc z=-4FwxVFRr8Aqvj9ted9oYtmT#uDv};vJa!S%;res-|6_>&ydB6K%x%VNA)$IJ)QD zjVisL+>uCyi1zmOdT~Ipv9SOEuE8D%oL*n3!NI zbb%hF(d2&@;?1%C^(i?YFeXmtGiA=6hNgLv;tZ|IWV4E)Im$>QR*q70WDX?FCi9q5 zI77v{7Fdfg3B$~&Cuqm~{iA)T6>o2TedY(r$VxqLW@g_u^i+PG)q5~BAUJHK!O(&8>kl+>C4-^XlF-8pcitD==f%8y+xC7KyH-Pe-$YgN+_2C1Bu&{n64Hp*$^q~&B~{a!f8K2AJo1&AxVE07lN zUWWezeL;f0D8s_Sj3d+>Oyhj!%a$$E>0sl=jo7zupMDp`#l^a{w?TsjXw#+*diCms z0Rsl;YpiPe6mjO9MeFzC{&yy0%f2KO6qOiTE5dPPLwdHswIh`+c4~(Bm`KD%g(D<5 z$k;;=YR=Be4kRJxqNh>1ZjZ6Wj>s?s#@7N=x(tX>pW(p>3|IMBcqkAQ2upDZ0#w>A z%P)W>w-89rh9xyiiM)LJ@fruINbvApsP)}r2&nJ>0*!fQ&z?Q8ZQC|uOGQ|YGM|l( zjyAR&KSZ4Q9ua4PP{a7LFynX-x-@Vw@Nd8UhAmsRASp=|YB@Q&qRwOT*feAsG%Mje zH{A;A)vKo~z@tWu(u0V7EmC-G6o~WY&BNlwi?ul8welWWN$0gum~lT|A1mR!K339M zpfqgQP!DXTh#WFxh<1tKJ@|sHTes>$g2LeT+i%zR?kf>^=+Ghj{`>FR<-~rxPS>to zan@O9Y0=>;_#Aib+NIq{m<}k&_?-FvDau%gGYt?vKR)O1@Ng~EYS*rfjvYJVj5E%_ znP;AfR;^m;{d@@<4Jg9q%$b9~|NdL2|BQ?boep??{GQK=&!5-EmR??4-MV$rwryLS zd+xayI&`Q`58k|g5oeXj$uGpGe=No0A5TSbNvW~*7(synh>i@!pdM}T__gO~Q5LAS z34$|f#LiUOEc|8$%61%7LaDY|;;g|u>cs(dRbH)@fCweTm^M|};W6sEEI9=zJDh^j zorh7ndOJ!MY(nYUo!Wg8>QEu)^hM;y_aU%(1LH^j3G+;r-HzM`3G>V;Q>N(h^mrrU z%=d^m6NDPZSA`iX-4q$qrcKis^ybZ*b)}q@T~7!O4n~tEO>_$z#n0JipRGkhbp;Ub z_wKE)k3Bu?`Kd1Ky_+y$f))%HUU;GQ zgKylpu}^vt`5s<-?KOS>_Tr4=12_(V_flQVUb5MAF$D z6GWUlud6=U# zsPYaAdwUBCbuXADM=iN(VPFq#x9tgzP0MSEsB_UyCI;-QBgs&;u^6-1o*p5r3USTN_6 zmf>hoG1659v!JXLtV-yUsm~Dg8LcXgbyWh3r7ycTEhB>9=lG&9qcB^$cCGF$`}5B~ zbq|#%6<$X$Bc}jj#HaJmKVMhS*|S1>^cIRkx~ox`OrJhoSH{^pQq^lSp@3j7A1mnW zN$b#|gBF)nh2refPd~+1Uwx%T*)`W(gK^`=p?&-I`rfMpT}z*P?m7Kl>_rk=I@wb< zX3Q94OVyye>WURBFk{9H%$++|_ndhu$Q;3U%@$VP|HzRewLt6Et($)CRe|rDy|+vU z9QQzhmXeZUeCs_F=}ebn$Bxwkmu{Kv5OH>lq_k|@^~NulzHA%vRk>>m-~H*Go8zIY z&ce`MZ4npkevC>{aS0MqvvD{j6KSeK%RnN7d$dNwIx^7zRKursrs{>3yaH9gsXAdw z2Fms&p>+2ll(Cm<_YqhQ9fc)11D4EOSPF{NiM%UUf=Bm5)RzzIxz6k<-J6K)ez#jA zKq{LEyZ1swzH+N7Agf-^91mW4=_M_O_^g@l@;$ID|N85%tDJvV7ZGQ^$B{S-QtJCz zRtD)-R8{gyO0oKA7Jk{6fX{Xw#OJ#Y;->>g@K-`IW+WZOjO0|c9K}qvOixO|A8PwA ziAk86oPv#6Imj(7MSN%oqW*SrHh}ZcD85F9{ljb5BT}#pLNB& zI&>fWb%VYBSSEmP!Zlx@M90_K#7l~xG@!Bnh*TQ$i@eUt<{4wUwpRYxGbzpBB-8I=G%XibP zSu?j0zkFe5Cj%5_mp<_|X0O_z>bwFIs5#~7E^SLFaX5rU) z8}aIQbMg2m)A07B`S|YdRrqD@I!s%-1;cu`L#xJhj9&<+3cj?n!XM`%_r^C+@caZ6 zzB?I3-_1tJ^pz-GzD)}>OH#V-DWiCE8CKMuzyJiF(-%=6-iyG-b&ZV`;rO+H$Z!ed=T9hw3Xc`fLXc?3Ⓢ;^VJ17@Cw&%qh|j=e7wk$i?6 zzeNGT<9LfH&bSa>FWoN*d(tRMs@gMGQ{s#wnr?zW{`jNr^`j7`2yzFXwV9cF0^dIe znNz59EFN3FnO;4Gf}H8-<(FU9ZmoP*t}w699lU>D6VpJWMvZjOo-2G=X*^M3&({6# z{DxU8cc9EJEjDiuhZk-cg$J$}hRE=cO6h~WV+WHnFh_~HCqA8uhd%gIiL>?Cye|aa{2gPF{RvXAIVnd-eaJY{wxcJ|4vQrkbd03K@Gk zqP}<#flXYyLVK1HOXkk!2-a{`pxG=P=>Du$*RtGHs$N^%F9N zSJ|!cNKOI1+OQwXkEG#fVG)W0%237uoa&iaf&$cHd~zHAHa_(rPY2Mwv6d2L9iwaE z+U89#C_YZRK?=f&M0MRj=#ocYa#qeMsGLFLWhToOG-i6-m!HmHFb=l9-GBf6IPbjk z%6q0ffjv2Iz4exMUt^}^4qg|JPZ7kwdEboQ$WJ~CSMZwnTVCT+Pd%l_&3T&vnr@As zeDaC0#d=&88g!kz@4ox=IJc@$Q*MpS@Y!3+`D@sV25=zYA;Z2VfyF$=ec-oyhd01v&WQfjeI|S`}Q@qI_6Y& zYs}3r!s8$Pg)jbEUcr@-7Tvx@J-mAR`54*1qr>QnyAG*$ICnj!sLJWeEe9(JJ9`+} zamuSVVR*j|#unjJ!6$a61b*(l@1aPgSD(TvW&ciy`uY(BwRJxSA6+VQMm>g-Mb?Ho z{yyyX^AP#Y-A?PJod+NF&Zbo+`dpP%X8z_D587%Cp0nFuouM)YF@MP|&* zaQPdC*ymWItFF3AXH?7}J!yU8HM1v+ZWI(|bYF8+x#oQ`Q(~_XGhFt1P+;*r^ZdLH ziaH*Pq1NdN$G{rSgdVSru9tV;eK)SW@=BcndV?czMtGgPu0ewaX;H!{2l$@6p(ex` zT}D~JP>k{!IeRX=e)jzJ>C;CS2z<_b?i4_LFHB!76nTv-Bw2VdT{wDYd@oD~beVba z#TRwp7A_JCDxW7sAEQ?CI@t@x!iISO3s8=wWCdW&nl-w>Hh;tG zU_r`C16^<3&EIn>i!;8@*S?>Jm%g5_pD+2Yox2nM#|%XLdv6dv_zKvuEW4 zqc`S^S7K*Rg0V&AsY=w<`t3P&jH>4sx88ayzWCw`W3B2i|K&L2!Gj0ub$WgNW$;F`>r0d?#i=)=ey{li?q;Uz2MF}@5Gm1erbGr42}Wk&;S!x z;*1TsfBp5Bc6q0$aR$>8%P+cVvR*}}?V3tzA3?|YqK2FA=RROay z=E!i5H>?~^{O)_ar!tGFQ>Q88%G2HS;T{^viAHtZK$z)r{1r1HTX-yH)Yo5sy>2yQ zM!?YMYjnolBygKMs%vK_D!j+3OSa&)*M8QnhbDXm-P$z7XOE6Y z&vs2J!8ryt8kV==ezIg;e?K}%U!Le-F7d~xutF(cFF}PP- z1gWK}Fr+;jovI5(7xPb3@Y36aLkaB58RoSN>csJAwsua~yTkmHu7~ctT=)(ty7(LR z!cpuvx(}9-6mBNY+!|R%vkarab5yR|!h2#_%79mF^ku`QJJ{e%@x$_mf7?G7M=*`D zK1I>XMo>;c;I3hs*Yn_m59&rsHX`#F?qFHShEX;=QykL`)!x0+6}EQ9_sjR{wK&_K zo`(r5_u_CyzO``HX~P)cQF3HW4&tM{rBJ3>A{hk9V^=u4D8)vVZn+wGbBf8gXXG~GV_rdEnd#X%$E{ZnVHP6BK>8b^jLXL$u zuhqW5V!=!o8M@7~=Zu1k*KS^)`Tlure4k9C6mD!tpsOhx8~7f051w$UinGmo5^?w2 zzalX;+t^~IWy5&9ao2@PP_?d*CQ4Nq&2eS-{BJU5uiWX-g)uxd1f5$pz&ZUo;P!E6 z;F%lG#gjLjgDcPOjk8bdfB{`wiZDAxsji)srDmc)rB_B~^a<(`f>e5p`t*JTb@HIM zi`TmA2(pLXkFsqCjg2;xS#)H`u&ahUtc!YrKZrB#LvIGw5tyIb|Hc{IkL4gE@A6nI zkL>YOoUweN=Lge$kD)W~4SH;_(U0f$)aBn6rd6JYbq!bN;dQV_lJ1hu z8cJQkd#4!W^>RubPwN#v5odpINx-6ADabFfe*qN*T4YEN1~#g#!|KmFl8PN!Ij9pB zf@V<>IFy%u{{;Zr5m1r1D(4{rYSC_2(4Koij%( z@_B06xnrlQ$VO;)Iwt;;6&e|z!`EMag+KoIL%Tb%l4$lg&|rW5=_h>i)z`Y4*4?sr z@giM0K22o+yLauvi_gEHdr;^y)vsS)#Hh-lZ6&}NscETLx@4(72CuPQyS6yJe}AvL zeNPmsTbRB1=9{`F!!`rrvDkZb(@i(&`Kow)XYia%=d6@7@CPg16ilAHD-$z=p}#m= zg6uOxUJvhsE)g66%rNNQv=Z_9`2Hy}DA1TTSwPrlhP)0wPYN^k5_#Ht=1QCqz7L*@ z1qq)uGf);l6ejMTlP|>?MIv4N=+Exxcq~9Du-Ws;LYME})6AatLE%Y(#g=cm{kd3(IMAY9*UxkO>{{x%%R<0cLJ@o0& z6#ujBn5_zWjcp6u{BcmwNC9A-i{eNQ}d z9Emf}nkfx(3rnzWUz#fDm9Q$cAV4pwB4GP>nf0@z*n-01GQ@-gBVIj(=)fSN(B~!I-Qi*i=vSs-7 zmtXMK8*k!|-+tF(<79;uy=i|<#oyDXV~)nIC|_DTzttTxa6`+F?!4xG;ZR4@b0OKv*${KrAYE7 z)1ZOdXLO#xu|yOLY~ZjzACE_YKtV=-@t!?F&DgE?r=I)AMOULCXT~jyvwq z1(G8H%5yXFA`5mF($3(1tdu!|$7EsWO&aBI?5{;|&e|P^@Wbpi72Fvq#{PT7P+W0# zZ!M@yDBiyNdo`Z=WGW6MReqFe?@2}8 z5%0Y7j&3w`R(>&`W#ijpk3EJDKKKAnJn@8fFSn&CJyOo#`8Y0`jmPR|@OUiIlr{;LNPX~04uZ1mX*+|MQwEj;2MjcrF-ok%2_rO21RbpA0k!9WB05fvf z;zf(J%Nz~h3{?R+|AGtjm<+b&QjpPal%gaj+nP!6VetiUsp^CCCC;wJyiRx5$nROv zWJ?PbIE}2dtW;N|%-=_ZN9a~~M#|~iub*1_>Xv>pGoYJXM`gU$@A~NqT`MNMYD6(2 ztjMz)`Bqhtzvb3jF;E$(Ik%E|f1cc9bvRz<@dbQd_8A!8BL|E!Lvhx+;=5wO%)-R}o$-Dt6e)t4zMR3|F^}atvoDb7^2nej zY{X$toWI-Ii3Y>k|8LSiNXa-hFg;McgQ30J;Ot3XZ6DMj9246y<#3^A|t}^m28(`@w$i`mQ*C`SmL6<@bBwP3!mMe~68N^`~ z^yp&!oIA=%)+H^SSb3|jz!nlXoRamf`LHHswcm% ztrG38Tw^&+lmz{VNusdccsri{S1Hn z{yWyG$`#MY3N15)waWO|dl(!PjOHzx>#-ia`}9@<_6!VAi7hTZPF1FMD$$y)UB}pi zalwTb=u5Y7#kadNE?B{O52RT zXQ+Gs9jnxHC{pj9=VD6==#mq-{@zYoScG2jT@D_hp{rgYv(Q$ZcRM+Tk?zp&kMAh=lm5fx^4TR5>nG~OoEli87_%huveXv)hy28jAd`}!F z=%}}f(f2q;h^

33Qa^y@i#1+7E>R@6o=H;X2RD1_Mvr`rNrSI)e5>w?Fn8(tVK6 z*wMZD%B_*m!o9D_v{LAEwBEmapjW4e;EAZG#NND$J#SWwf2)RC?6nwd6v z&CGXf@1NCmK^0ZN@7T=BVdn`~|xYry5(VM1_aqs&o3_?Rzgmul5yxBM6SDuASvSHvyJp=e_Zs5ZI|XqP~3; zA%k7deP~jamW|vy{)dv8D*w03qn%+%$w2rMR~c)*h2BQY%gwye9zIt#c0TaH0~LDL zJaubiqawwIBRB?~)3eZB(%$oezolSgFer*9zE4MZGd~C;Y7C6VvY6$lEwl{^YQ78BoA}Ob;TSgtTVeg(6}EQ9d*^w*5@)M+rDEl-qt-}I z>c?&BN8^H?&CobD0u7?W(Y0Y5@`_54npcQYRfsUKM|xo~8bpP`qMqH7BWXC4O*ciW zFl!zafzeItqkCKo;#9)v5>pG2>f-j~se)hc1=J1=!8!HoBG~TX6Sh3hRu%lL%q%of z75=NQy#{^y_C;M)F=(hNb1ho7)GEFIfB`yDghzxUUsZaSEM9^ifA~SW@vtJt-~bdn zOI4!dHoElfP?fJ;s&dL_Q!6?epQ;!4$M1h&*^;HkKHGFfa;y4GOHI{+BRVF!JW=r_ zZ{D;?C)D33|As{i7ir_9(BiSTsdMbwwM*Xvh2F*u8c)`}+Iz zv~>NFe^bX>yCQLP#dXA zJr|13EVVydbTU-Ml&*&3F1}blKYq{W$MHbS7!p*4&J<@lF{^D{|7ZSuC4!FK6DzET z4iYo7Qg%V!he*3L<>7-lhhV|>Upxd#1hyHzny8m_S*6M3zZxAbtJ$v=i z_m-)I?ELxv;M;G$(buv{Wehwo-|cqwd*099y?d1SuC2$#*k=HasBU4#^!Wenod(ie7JXVQP&N$E5v14t!<-NM0{nMc{r_axum=h>t>ImUds(wnm#sRDFTmwzH#&?K}6#<&VB&7oB!UNludUJGGFj z`?QzTB8S@knvL6JfDwql|0O&;D7|PAx$(l&I0)Il$2opqMH{PN4b##+jYu?Yh=vH;2%^0OPhL#aYB1(t_lO!tDELl`7w~?TB-4J% zu=CBDA6H5$q!zWIj+N4iO7~h7<{PUdMePe4MP!SZfubDpiAxlX9~P*z2#`Pq#qHJ-jmF6`KSGWfsA(kSQ1L zhep0+@sg0TH#1A%rxe)ueu1ekD2FI+2+QTlm9_N+;hO89q~S?@?Uh%oML zp4(1Jq#v5^Ndk~ZUY{uQmWHPbCv)ClJZ_c%D({>s6&0-n; z!3_C_IWKFgy*+WhELpKu_GIoef?>FFuP9y)ZqNDJX35nnO8Rp*NTE(GLVKcfaP5`J0|7}kA5}}I%b%Nf$>LR8WWn#7 zD8dJbgEIoE?T{K-8P%4@!UF<<>U&brK(Zjnw2SG2ZA~4 zjAFHMn=IM9LrNI|pk%Tc^vyH!oV$!-M#e`!C^UoxD&J-!SbX%shw|*x&&cReqm1A_ z$0|#1VcrV`iRGbk@4EYLx#|D@Px@anKpG$4#0s-RF1t*wH|IXy2+fZ6 z&D^_due;7}c*A08ps|4PwKsFG^t-s9DT`ZdSx_dYb~x2qF;Fz8Pn~9!Q;v-VtPfQ9 z`t|C|jW^yTx0v^Ei#g|ImtSr>4BjE{;p6{(Y{L+Na(&kb(RltSA3Sn>&g)~}-yL_| zY0CI^n}7sGjrYOttU?jmwr|^Ry_G2RbP~AQufP5Vx$&kO&Aa_SxoYTDwsGTiM3~J< z(uOeYw!iIdi})a_(u(rB%a2Ydq0LzMA)|eD>|)uypy@&dOhP^Ug{`S(RGVt$j$q9p>l;a2dQ?7gR1Nn5u zkCI`OxH!8RrJ#Vkm;;ilmY4KbZk58l+lBUIgOL$;-}8!OemEnvBL`$AE+Gcv2y@6V zN_is;9FQ-VE5^8&Qy8x!8Pn*iiR6d{#)F*9cXQ)phK_}3^VNyW0OV;u`GxTj12OPD zaslhZGXISEZ6wfQ9vCuXH2DYF5!oAqq~Dl_G148%h%psUCW>p9quD1 z#HE}a*k^9Qdd>pYgSH}Lhtx(Ht5H_k)u|x$DwMF{hsReeDHk-WAuTJHk)PLZm-iR` zA)^Mmq*C+Mi_W*_;5SV3ojLo#g7UVE6CZs&z4Ir zy+qD9^GrF@2*&;TUu-_l%z7yleD@xwTW$ZWo@Yt-(@&R+`dwsQ#t3-`{lBgFO;)e| z)4puHIJ0NZvH~V=f)%7&K(C z{q05l`q^va?d#OJlezaZjL=oh-ZST;@^hbTcsrbD2_tfSiVEi(WXht~*=O6o`(Dsj zF7Dso3X}-htJkct7LVP#!o#&9VFh#Z+lvSEmy3+jce)Y&cn`c2J`n&B;ubFa-gZR1 z54@T)XU;O$*kuJy%Jp)iR1YvpE14mge@J=tIsZJ{`8j3GCx4+}5)O+3!}Tj2SILII zG8eB?=g#(duD|gHx&DS5?0w~hLgEJs8E$~I0Ot;(){^(6GnMFd3XeosmV}{ z!I20X#>z^~^qEnX)Xn|4X#$|dEbnq_74&PNwoB< z1k9N=%Z6C;H}CK4b9#qaW4Jez(w@C~*?V)&VPBv5hfbtyxw4X8EZx=%LLgr?%FU>^ zM%jcQC^aNR>D;Art~;%GVV)5R1o766g67n_Xiye(Y`D=;(nvu|1|k# z;8-n?Ie~LKeUTTQF*{|R^1^e*JOl>xFtq*dvxr209E0%)6uBs)7~{*xfw z?2z5}zABk-O%CnJIZ4G5V{|0Z6Go^=N&<2mNjee{woY4()L!Eqt8zQGp_ImRj!6Bz zBLhT|Sr|W7WAB-9C3An?AoG@OlyUP{$&97zWX4Y!@^i+v z;6l~rxM13Ra{>kq-iO(?@7^c#*KCn-%h$?RMoC*>l(g}`XUNxUHd)2Z9&fhfgTdq> zktkWJr1h>0z2<7U=DKTbNa8u?o-0j`Ki(=0%!6W$Gpx2NJeoL_AU!SpFhK}Gn3u-y zoxjV4JY;SV_|wcw=RCY%l3Zk(jWD|1yLZ|BgvF7P-cNWT%VXmvjg4>}(ZH<9)yL{2@92W;}3U!?E7uUXF-bJlVK zwPn+`-SW)))8w|{V`Z!BK9qt@Qia%)&m@&CA?ZdjD>|SX!dYl<6igqo>!Fdd_vLZs z@7VgRh|rvJxCpfyqE)WC>Z*u+8DkGZJIX~O`gmlgNCs1;+Yp@*3&>J_S%c$&XO4Az zr2mf;K99!_P-6Yynf&I&`@y)xoP07Vq60kq@WXPh(GT47>yQQg47BvCggjC<(cnEY z-GiheD1CU=Sl^9|*2tcbjKA3tmvUA(*(gfpKC{*}GoN_QW-VPW-!9IORqJ-hzP$(R z#{9D~sfFdFYGtKPnPO6McHp`cPY>*`zw;KhA zHPnLVM-fCUFOp3|<>7Ht5@L7d(5vKzn{Je$S6^-akC00xAbG^Xg^P^h7jD^0GUv`t z=;eFT(R=a!7MX@hM~pEROukD+$%|lB=6e*6?7R^i=e~{~R1l8IeRGeF?FQ?$cSZ&)FXo9D`*{gK@4x7S2fJlVZvqZwZ}Z@La+DDBfi188m3H z^fSuOX&pP-`A+-Ic?o0t{IhX3JodR~pOyFDeJ^5McV5Bs&}pD}(&))dM5j+ehgd@g zsE6D)iW&3Lu;@jKe|MlIS#Nk5!H1n&+? z2wArx4Jz@#yN%TDvCzdjsOALDAIS^py#H9>SV9~L`CySzype=D#%f5SJyW0X?2 zjrIJLAUk&LmAOCvVfwj4{b~Jb<)uN5a8KCEjLq`yl<+j&70j61w{we#2|!q+`hU}Z zkiLI=%_vieF*vZM=_{YkvWY`tRq&N#o8$(ud54mUr%CFAgQUp$F?!6l?Udd3jF8M{ z#)$0AeS)Fn8o7^-A%PU*NhBqQKA*X_J$v@dvB2&HV>##bgQ);0C9%Mm=FIiVDa`$f zRcS)TiX@A$WDdlV_1F#L5lR zjw>Y>wx}WfPN*S0>K|u2@R`4FkgY~3BWyAFo6v7VN?-7AquiMN#gbBNdi_Y?(p>e& zA6dc-F9=1AIb#=Gc%ju?Ve{rKl9932X!ha4Weoj5L=GQ-0`|qFrKMYi3_+81wYat) zSQ*xv?+MR~PWIMptK6h`6r4O5AMM#A+>3K%`)wW!&Q>`Km2rA{nzariJpZvWa-1>WlW9;gXyOTQHFSu5)oeL2N^RXSsb~i+{Wj7 zp&l`bnX)8|lyFBsrNU9r2p44@8uO=eD}2(qQ^tPRV{V;yy_7`3eWS4Z#mjRq)Telq zUqBc6X?aajg5dY~E+yXasieF>HA9 zoHLPNg#;#RH@dfNc7B~3yoxKyh@?gt+w;V`vgf(~g@ldVQ>1^F&|f)&K8xszNSOf{qX`42k4z+t zi_w-_AuKWbYwQT)%`S!OB)??PZVrgkszNA!1 zPnANP%PM&0#98Mw?bPfqWqwyYg1b4mPSsa-fnnyDA#`L3P3D8~!WlHO#;B}6Lgc1# zy9BhL;t;gRSFc{phWN1$VR+7***2S`8@v#N(%}Z(v^h3wLo|3!QYQ0BB|%xu+!xzC zqj<1S94gajOLJl zsm*cU6T0ctcQ-r(3L44~6K~LYMXyHO~*QMQL$>_Vyc2Bn3 z!jX4KkJjN{XU>1S5uDd-IAnAjFzhX9-2ZWDbn#O-{M;nXK&xCESee#`b+p$Lm{p0PhbNFpd2R<1(TfwDp z#}mvyQM^QHMI?3DASpVyM{Hq*<|Xd=*L$+}iT6Z|qMZ!tjY?Vw^)Mc7B6A z$ulw5C2HJ`OF64phVU?>0GM}(GDe)QRnW}e<%$)N_Khk^*JhQayV?4+sV&t?r_1CY z*2psxm&y|p7RwvoE;l!RKzcW>D(5t+BB$4>B;9LQl!j%B8%4|r%^^k4QP9ZdSjGrS zeqoIW;$M6*L7sc&Ss7(s%!vQKEMv!vmD#gqTY;MjIqtJ@)*=fOFkwvYkg!NEBd0i( z8+Rua41wjh6)SAU$d5n%*j6Iuz5Vj@&sL%M;Jx?l8e984R=b>l$B@Ocne&BZZ|Tw{ zHk6aelL%tbEC=zEeIuqUa@*LwxX;<4!)bOxEy5l`9qW~`kw%_1%1G?--n;Ku z#menM=L{$qtWEda@aN>IC!UlKKls2V1tBB>A)HKT2h2+h}5iH z*7i*fWM}*%BRpY9@wB3HVVB5jL;bLPt-LjH?qT(q`rfWtExDm@2PtBdv?4~RjFQ)_03n ztjWqexoP_@S-LXAC~1d&U$k&?Sa|2MRU72TILyxTr=F9n_CI#nd4u73tz zNl%s3+b@*VD|^P|F$H=Xd{(=fN<~xI@VO^UptTKO@OKTn8hq9~h-EmU4cs z-z5?e8Mn&-ap#v82xJh#N9oE=v#T}q^xn+p|!qoA?o*@^YaOUv5jWb@X& z^3%$#rnAkop0wholRubwKn`Rcl){oEC5sj^7fq7o8+ORA>vu@iVrkODC~3t_0S6Vm zpn}G^3k8ydMXC=K`5{ow7A%-AYu2u{f;Zau@MoWuCmwrDp85CF^0@h=ps;ddU1-wu zcw6{~2?eefnj2`C0m3lMXRX?`ZI(a0JG{vG^XAJ-FTN=Me(GQH-~asACYxYkYZL-r ze6+B%SV3s7TeqIA$UG5B>dQu9edXm5b}cU|(UF2mZ2PIh`>I@F%Q(~l)^AW>&N7`+ zC8Kb&4@Hdf9scZa^W6WnODVAS#k8r@Y=d;(yM5i}cV?a~XQ z-x?*O-h4}5d-XM|-10k=dW6mV2IY)0MfeyU0~22ft>9i62;7|&I2{SXd8E)WIv+?chWqg2pvABv3#j? zdE)xsQp}9;u|N`#7sq@pYc?FdCL#vH*-KZ-PrnD-GhvqLso@5$A6I0^hAqMDyCgGl zFKWGJ;||FPdCpMU2q$!d5?Q|VAuq(}iSum&6+Z|M{c6E7`**{d6^(*cBTS|+m$&!1 z+U4o{?#JKl^SNnrd`+oWJ@5LUW09nar6q;XMo%|NX2i4y2TJN){mq~gTN1IoBR-Qo zkG>szVTr=@?>3~-W$=iH88c>NEoSTo;|Gce(tfJR+A|a*4eU$6?}y=O8@M&1t8dX2S;k zKyJV*#yLrxhT)lH96TcmC+A`wGYTX60-=uoHL{`KyJ9?|{QV+3xDK8(o(Cn2(0dGs zoSVoH);;4n9fK$Jf#=Pwd=!^fP_ay!RIO0V2&}=-FhGeiYLzsoTvC>=*={<))iPnu zDw+4|1}R=NMJg0C8g0lUh5}ZpSW#KOeUE(p!)lqdbdAjXeWR2$N?aM!K(pqqq41gG zOPb?PiZI9^sIidq8E5pcDbo=Gn7f6xO!`U`k{w0>MX+mq$|=^mfU=ZGyn z2)JFlb(8b^o-fTpR`?C;H`vh2_uqTZE~bnH8(|2AF_t+Vu?UGLp4iI9+49UNB!uhX znOnYWd6?%Y&v4yhuq+bSiPE6xv<`+kF3R}Aeiupa-e*hQ&?3=1yP|UFYF@O=8>IZT6+*4jX zGi|VI*RD3P3L&|E-bn`1yqa%!wDF5aAt_UK%y_@>m(^iypK#4h+jiOO?AR5YC+0w%Y1O?|R2*H`wVmJ&!QBasYkWh)jwa|s4J{d{NpOfGM$vt)>>brpd?k4 zsAXv8N|cYXduaOyi{eT~4M5Hy@p^k6>`n&`K4p+HX(BCEFm0rLIKGpLD625L5W}qK zbUQ5A1n^Gf(#M32I6%ZMlint{U2apDiv2j;<{BS3LVxCsZe6s*x=X|t5i+}wK#J@J`jC^6Rta{t)8$4Z zRkh?2%R~1HM?$#q?GFqp^0tukGn4|AB1zYylhzfKIO_!kvCS~; z;(L1&PM7(fuMf+1#b#xSEl@LbDWAI~<9@3)YAR&mu4nlt#^;z-E|i<>x2toH;L?3; z`;a6b^pty5_sKO1lctf^AG`P}b}wMBvpRPjbU58OoQpQ z1gDTB^~prw&LkAv{P_t2LK^VmPrO`&4wov+n(Tk75^~xbPxrY(ulIfO8exBf5ZP=C zU)#swyJ;9*TCz;d(8Bmdi^D>N)BYU#WjguI2iy6&TM@IZ+$D@JCyg0#kbBP_I=;PD zXU2U?1rC;*<<7|8KTiZM1(7<|aeAyA7d1*%yiVqSBBgsA{cN^aZPHQefSc3MLT^62 z96LbBPKmpu5u-wFXtkM{{7~4bEU!GOy%ppoZW`Jr7IBxR5D>aA+Mb`yCaElFc))m2 zUIZj9#JuCZH?rqYLs3Nq4adZb)1+*AXE_~~IX9Hye{^7I`;+j32)t=9@dVce8#(aSe-#P5w~8b$@w7f z!<35R`aLWsWDqWad|bb=!E(}_e0%GQM;6UoDpX#1+9|CBmuTUn1D9+9pt&zJlF6rF z!3mPKcyt^}WL`XPUJ0uZ>nzykO|0x%c`IZ3L!RFHf5Myu_4n?G$ z)PEfMN}X@9N)o+f;P+xAD)@9V9_nv<@j$vhT6E#4KgjsNE=N!hD4iwzHzQUn-q2CY|dY4XV_|hm=!7^X}Y`7azAHnE}wrhHCSJ3+HLmJj(`!Pj_2yW zG=0H;?#B46GgoD{ugG;pD(VS1#g?SJt9dY-S*Qt)I6qS-M9N9;4H-l5W5ka%-EG0W z%GPeFBpBM=QPY(>;&P}W-^7^#Yk`Z@Ik`nOv(%QQ8?ip?Nes5*?u##)Q0#I!tmsmt zC_$R99&b|Bma|P0r0#n|2fFf!f5FO%(XrkB93~#%M-`=Mth`(0j%zY_yqTRx5UQ5f zsY}C)Vk(E2;7k!uczn^Qw8gU#T!O&L#%U?Lj~9MX8E-Rse^X@MFuh_OgTug#O7sCT zUplEATfgH>>`|Gd;(5n}%t!U4#zhk+EZyEfhE6r&J3V$*sa|cmfwSObjqQ4xw2d!) z5ntHmakRdcW&-&6?EXM28wdM8EMA}5s@GCn?M)?};rTh?!~n)SOA?hb-oeJ!6B7`e zp3PRshrmYf>%R$Sc1B{eMOb5*?OY$NsYY_`IfB9EDOc!)9bWI2PocNSM$~Iy4>r{d z@?Fs-Q=9(K?G~iByPwM5QGE7_Xr5RLFzr*ptjESoP&x?DD~zO#%<#t*h}RIYbimAA zW2uL^j&oCiFA`bF_al}gJzS?-u%2Nq7pgCPGb}7jfVC%*QB5QzLou1MFInL7+n-8;?TuKEV0wN@FYog<`tI&x8(mK-LNL0)sz#Ud<+rCNwfDg_qRCZ0 z;ldYcXhk79IXaOF0uGNPgYxBq3<7Slw$4cGgu1&kDI+Gx1xI?sixdaWr$ z(gTDzyD#tZBI%6lHkyKit#YZid-QC%r8u2bD+^Ih<`m-@^kzQ}49gpUwz~~Xm*vkt zsSc^_&IjaJjISCUyS?=?`tfIb?c23-o#)vai_zz^95+|}``JU+2fDbE_VrvImOl!K zE#GV>X53bEoJel(liK%cYxeh_;N$#4ZykO=S+G0wu~&Lu+DmanSzF1JPO1`WrcZj6 zo~xb+nh#U%dnFdDe#bID&sy{9p?|AoIUQ!QtWwW?3c;SM0jeqQ!rEz&!ani~{RtJv zh{b2?PirOW9ypI(-2rL@#?~hnV^yXfv#b|A7pt?}Pgj??uLa@!I(5;g>$5m*R1X3p zjdkCQBoXk=AI&YKRE3_4|JI?#uT4798ING&piShAtCQ@T z+drQjvR(9OGW)WUo!3!@LLV+9|UxxqF50v%~+`Q?ybPlK5O`m;v*pZE$qYb+EnwALuJgx5f5d`imKzXB;I`whV=U0Ywx`Hkwey@$zBduRES{Bx2}5) z>M}+IhHV~*6#cu$wevt24^b3wyjP!pUDBH%D~iEHv+QSLvx$W#IEuxy>{YDYX_wVd zs(jODTAO|;vH(!e(^E`WAirU!FYfc6pML0ZAXZO?aTh;7d^A;#?w2DSI=w1M2;Ld- z&@<0fyf@xOvUI^ai||LOPc$k}lCZfHx(2_v5w^`)3f(Sr9n0YXC;OJTvr?+d(8oQ3 z#f%A=#R9#cJ)i7z4v5gySo;$@|3hDg*(p&p!EF@O!9G*L6CznJvNX}fe1bH!C>Z)T zp$CLFreTL#HH!0murmAmnX=~(g1ro%RH2sy_&8SennMWNH7~BWQ>ReB4hA{C^Xl2V za=GkueE+S-!-1{f$L(h}Yt`F3fm^&2gMthSVGPqKoH>JB2;!|9VCfcibEifQY1E>H zK~3AcU;I2TsQBRFlzQD+oJVE{fx-04$ngv$D9TuMkxBnVnyV$0Nl-c|5yHS(sU>09 zxeb-2yYbeEBokFCK#+FQ4DX#=VZfTMZY^2#w?Aj;V_!V_+70xJK8DZEKHPo2ywg+b zk8Kogqa2gT^>O;li2BVSbUqHK0?aMLx2R0ipd|CPOIGkbn^^~`avS|nS${CCxn2h! zuQqA{;)v~8iOaX>%ZE0dd9yv?{sd;)`E)$N&lEz{{O(=YMHz;3WnJ#J8q;a@d9IRc zdQW;0liF?ljz(G4RVw^b_F2AmuSPP3%Q~*#WHA8(EZXE_DweeI9GA=&bzP)Z8`Ez0 z^)_U+R(7FhMNh*Ar6g~>5Bu)fDFL&cYN8C8+iB{Q2FY@GDLaIz#mWm<)YqTaRd=#) z`ZjhFp9-_zZqvLY8!eVUD|o$qnMZa-|1Lr$72nizk#!ccI^_2uQhJ;q9s0GYEN3H-vp!aPjLSE> z{G0wONIMww`9VjYmiSF45H9qPoyUXSKG=wH-O!~(~k&-SqVJd zM(#8!2{m6KPe7Q=U~LFtNH9_HDEyD5%J|Llx z(Bd-j@=f*KX6GI8EaXG?O@!o6J~3@|bcL`?$2w2L=vu`!NzZ(rTXIIj7KhN@wg>sk zzYHA>T>D7IZ<2V!x_65u?t6EBMA#ebbnD?y!8=V&6cQyl|18;|#R61{zbQf)hUNbS zfz72g9e?IM@=oOt-Yl(BE+?UxM77v1p5Olpp*jx$S$PpaZ>Yhm3}ze!nM(TqdRlk8 z(z6ma2^#KwSGg4#p5`U9LfM@M`L-&wu+R6K_I95X0VwWmLwHk6y|>0Uy&ZJ%78>K? z={pEvd@1duqeLM^cXsdYm#6N;*V%@?0&sMvVkyiI!J4Y34;zimPKHvvdD<)tHt9s; z``0dw78P#mkLNczZn_-zy&z?C8~5J1#VSJ$8{K^#8IEYmt5sYkA`xGI;gjZc8a56 zlA^Cx+g8PA@AX0YaAp=JV0_6WsGb6e(4ZOm;oJXSP0^M- ze3>grbZ-ssbY3<(R+UyuDRf(nD) zVN(TCa>XXbPR9nua>ts8-?k-)$cP{gU;$GeH;8=lWC)}bIMo)+KbSFe@uxmE6ki|{ zFT8)?(L!Fb;wc+lb{BVnHgitLM z7?4Y`?xpyJwf_i98>ZK5yHJ}-jupnsKgZA5HaRIZxSR^JV7I#9UFuu|nM%sIMFc$B z{FV=5S5iz4BFKZL+nak53X%wYU3re|mScvPH;VtPRu_H9(^gxa2kq-!V8crL2EGFM zPLCaHR_*Ms8E9h>(S$S(bHj1$*Oe9QbM9R7q9uLoujkPWHBIZyogp&i2&U&l-`j ziU-6pN(VLr+9GbVh50PVo#9{w5A#88-9=`WLA^-fKT%79dwev7@RgOD>dTX|p{HH= z$^T$=3#WltP~hZ$$8lb8W1_s#J{skbZjptfy=gC?qV-@2HRN|@h7dK#T%6K*34xT- z4vmf9|A7ksemFxCj0hBy63FN+mM0{QXNnNQX4GNAQU2EQR9^5-hrmXY-6{|jGnSvN zVr{amlF8DCepZ85X1%C0@z^EtnMm0FM4$fz_@tH07zVnoOvS@dHh*9_-G1u!ayzV3 zk-Y!Z9Zw85``S^S!tX7C-f@}!?JoRW z1wF~@pr=SAFXnjQ*#v*rpRENmv)B9Ea0V1V{5nl+pCLZksN{k0>lwE}h|4FpOT-`V zPV4w8o=PnGp`3|LxW4}eg%S(a!93cn z8w{}znV$^*;x#`L;9UHO5-IqS8&^&Ny|qdcxCkOH*Y7TV`g0>NpEzNZbX^1H6os*_QG6XcY&WD;y@F^z7|3pFQ1dxqI{UqVo!uL|b7X|~7>=JIMx$i+Ooo4@0g9gXpn+#53B7G!ywu7x#(l%Q1* z|Ba@^1XYI1LJGg|yC+SBa&!_w=sHYg*xR#_PU=TTmGwo|8~jYjy-o17-tMoj3a+eX zF);Iv5NL*l36cK5TT9txEG?O&Zov9Sg)tWFJt|GS8{uLJFt9!2WNFUM)uy)4?A!+i zcXTymr6X}uOH8WSRU5QbR;*+D9hnoUB_?mfmyl?(dUTrD+q^AQ8gb;97*i)oC!##Y z$H3LLqxqg5#cH9^s{O}W5N&dpAahndE~k22N;p(F3@#V}dfw{?cd7#epP#Aodg5uL zoz$%+fKLkVi=6YkBX!fwpgF2W?3KY+EPA*B#RJaQ|8o08F{;)zBX$FvBiW#Y4A zw1_H<=xiuzg(#K7<5Ji)V!co}Rg`fh?`Nqc z_3n?dJ)+OpRAttRr>63fL)e_x`)>*5+4Vu!{$d}c_h+8yxntL?njI+9b6r&Dhy4KZ z67z@tTD$=N?sqNI)^m$Z+xeH%Pt96__fs`?aGe}h>v+-MyFxowcA*Sk%u6+2G|o#co=X zd#zi5a`JavsY+V~=gXy;LIFAP9|M)s4h#lLw&cwW>CL1AszWilO6%X{4VxMv@0+PB zMCq1ZPZ9=`;k-V(kv=hr-2^r(-}v(wuy16L0|l-Ph$8G8_w;x7r|AvT_L*lH4u7zh z_-q@%fRT4R=gQHx#n~s9hT9S)>Z_j@$@eenOWclW$9XL?Q*L~1_RZ9(@+nOtv8JAV z^%WTxhOEWxJ%YL5cqrh znR-fY**t`qRwLzxRy_&L@@TQ&o~crLKTV^=cf!_HG3&FdOXTuws{+&EVq5Z$T6NKA zyl;91I$$o5QOSGW>mO)bneY_O*S{AUGx4T>|5U4KKh^Ch7SWh%Z-Za#q-+f($b}0? z$XED8)Dr`@Zy5s@!W>O-6vrC1h{zp_Q)LyVi{U!F0kU55nQ5a9bFrF08%GWg#&^MG zv|N7|t;2tNn3z6J;DKNP7^s1&s<1-KqOB$?T;a0uCJ2y_&X7wIcri}ac(0L$(&Ks> zE0H^ewyxn(3C+7cf!6EO*hVfC@#}mn#|e&nII}+{uK8~=Sx{)4#&Bh?2~@bIaVSgY z`UieYW8aEUJ)Zws!0+mzq(+9$5{UUxonDX9UhigK(g#RbRLyR~hT97#V3jD$iA``U z@|v$N-JPX{-4GXA?y_@Tz>Rc1EXik8%=)J{B>|?>%3m}YEzVxQ(r%<{9Bm`hQE@hk z+F*Kc*1Jy3=lGgmX6M;sayWM8q6Yw<&u_AcY-jAdk%hzSN2@QZR*-K+nfx|Z zrH(kZhFYqMRVxcUQH^iWn*mFyLj}(I3g&%_t^ZKS+15RlryLDO-6o9WLO>FR+rgfU041L1q2Ii ztg9gVqH4+Kdqb%2CP~sbP-pC|sO;$aB}2rJ-m15wgcuBehDAeY5q+--)|d+VD8IyB zr#jCsgeYx_Nb?PCJmt}%KoQ$`FWdxm_wz-N>qwUibEm(znvdsiFU4O37ir0f>{ zL=8(Ej=g8hT*X4pLfX;uSeJx=&xa@h>=@sf0DJqEBOlDdGx)0+)~#K4MorW0Pavyk z)kK*=;|S64+g|2&j7wLF{HsUpPG#b0fkc6*op+NPl+6t5QLF9^-tGs-D$dH3b)^^C zt9YBK{8m-Y%()4Z3adl~_NxpbA3Kpk@%9XpM38HN&|PF>K8{2-eO9l;(-?@Rmet!*Lu2W-HAaDwTG4;}PN zLbBqWX_ZIN#{qpXV`JCH>jvPt!0*~qpFgfMm0xw98<*KKI)&;5GT_(9{raMBj;)s^(PMakdRvXxYUfqT$&I8x z$D3Nsop`-a%7UQ9TA^yy;>h^yn{A!S!_8)4$LTEeC0l~FJz0hLYfCcZtH$oH=;nE% zG>plv(xxif3UsvOrWNLf1+ZoxLexlX`#jp85&0zJ3roy!wyxe_3~vbg9#0B!tj}q{ zOsmlCs+%Xj>LsqwE~6Hr2(|F!JGPkq%~(C5$Wl``jUVV+b|3UM?}O1#U}Ua zgF1P`){?;QhTtut&oJXsWOE*=rbtRgeVtHtAfb;h2Y*y1&)v7!#9V}xy~Y_@jbY7= zO|SY186n6EN-+FnR~6d72v}%Sz916G!$8Ok>BiH+hT2jWvurb}>21uRbwzb^^+lrv zcdGCe@E{dXFVJw%N~-?5gvy)-YW)`Q@~AFJYo0jQ<-OepK@>@TEw2)teNI%4h4-F3 zH{W84-+#6fP0Vf#xI%0HUGKG zZ@-D*{o)97`kWp&(h1`JGad5I5Svlt3m!_axkCfT3TMl8_)xPEJ_prghv$Bf<7|qQueq>ic~6YR3j@8w1IziN`!8DHFP0 zpXG1(pVDAjPD5DLdL3#k)32`4f7bg|#SkD#O}2=|M+1QHj@liT1=@Y~je5 zH2Vz&7H&ur;+8eup8wU!?sZ@$+W;&((u-Y70A0mL#zs{P$pb*gr@oug#^}Yk0bIFP zP^PDN67>=Zv}^5ul&joyG592ObA(VKYkU~7KB+C7~_u(Ba$)aVP zX^`O$H0`E`P!$<(O|}58YAT!A@ZzVaY(*3DuLVkZf}Tj>^p=4FU$W_<3qi7Pi`*|b z?-vyI$Jd}cNYa|943*Cnt(7$lzix}#7@i#%dh4jfOD;Ft(E=!dhA6?&Nn*POTnJyX zOVX3s&S;XSV%z0HkqPik33IN>s$7yfSVfuLuKQn}7T;gaF4kJ~u~eRjPSuNI#@ph^ z$xM5WOhBjSFz?D8eUu)-T%L*-%z&4rq}wazfcJj*bI6tX10ZoYAW*yg$i!37G{NRg zj$l*Dq*fRiCq6Gi?WvR0n8KzBJ}?H(PeM!Mps40xo@PzT#RhYdQqR4LC5r(~$Bp^J z`zK*PId%)fT7WK5sepCpV~cgTSXlFpKIBXU>^bB4cW-*D=;V?Ayiw<7Z*XYC-}1tC z&qn#jfc8m)%g8G24waUZRjTmVON}B`0vp}3Xm>{qrN22g3%OFV7PJi4$qEYp|8yV_>$na%Mq(z1 z)wqV10S(|-p9Fzm+sqb6tpc9*mjHA!JpSPb?0udu|Cv)p@a4|z@fDyZj{|^ji!<)! z`7as__W-ST=cu-)<#nHfu%5IGbe{yEK*oW>WLkr-?)BGg?uWCpjaEwcz`bvAw%%1x zgk56HX8WVOij89SW=0G>JYJA^7u8MMt^c!I#t|SH{g}FwU-T+TQxdc*y9l)n75g@vZ0(EbuuzX=) z`t>H8IZ>(CX8ZL{otNv{z_FW?m6KN>!%g4id)sn5!DYY5YO~SZwRX3%B;@={m`>vh z+k5^iim^;r!+aFImEC8}iK@m>LFM!#-R>juW zVc`B<;wD2h9BD~O2w^7?W*@I?)< z3B!GmH2?WUV;(c4wpgx|~zBw8MoS1EKq`6qM?J>lIMMC>#QRg$}6#2fM&!=P3$SywP zBRjQi!c}Nie+H~(STv*B0UOa1ufMSHBb$fEjaSFGsCyhMKVqwi_Z=63bhGdX>rJ^GU{1J0~zkd^hGG6z!As4B}6v~ z0o1g(Ro}BNO&2QcOMqpQ1kh8Ko&n1GS0-^rM`T>ax*oSXfXi*)Ms1QpJm6%rcc;~u&z3#VSSgL3z5j7?e`8lAG_jh0FVLsNLc~*DiWl9nrdezy?z4xu1 zVuQTv*}k2+Ki5>uWFJanDqvM4*eSzM-8~4yD3**PlmSpPV!k2d{4)V{Pb~UvtWDh7 zcmPVrw|y&`!QS6u`%EGRH#{DJP=Ixoyj;a8=VqR(mgQHj{A00ExzLe+IJr%{4JD47RAp$^Lp4Gl#>H+ znC27MO5Q6$z`a1ZEg@#t4+Thea9B>(nWX)J{+nrqqIZx|iHG^K5D;R)X>AlsBA&{V z&$=l=WuJV3xDD??s+@Z#~ULHUA zUbH*TP}?Mn%${E4cRu!dp>N>a2kWW7jn#e*_@|C0kC_TEt2lReoPL$d_1;4CC0V}x zn;4FJSU=iTj~ASshOn@Opsm6#PDVV;I2p*5?WeKw5*3 zwTrZ2fF~Z5r7@zprRB0eK!ry|A?BIM9YEpL8TtHnB=;)Isk{F5btgp$Du>StEklIr z2)_mJuU~qZUpmv@1}HM<#5~UAM?6LRaPku98PNb5y(?3efVIC6m;@fX+zi#~8&7lM zj!+~A|tNfMAuml^W`c%8XCF4!**b9lc%#WC#bH|VQ`2LP=dQy>XY1ddmHB<|DJkDgeIx&Cjd`{v z299#r*e(1Uy|t~4rW}hNm5o?Y%Mn^CR?>McN(aRptJs~_$D5Nzhe-q-&(31Ml~yM* zd_K>r=XC_lw^Tf^xgUSiJ-%v>2vcRi0dVYG4-&325?6tmBf(*Wah;Ki8bDgZW`!yc zm|DtP+lzHOz4$;yHPB*k(`qv}+;R;duohcRUmQeMkzDzTx@H`$g_@{Z^#}KU&`9M* zX>hp)?5BPoP9P&ISFcc=cfq1nY+_%@2D>%Ho9NXU2d_=vUJ&oMf-m3SVmWN%$M16U zTcy%#Kmlvyab9$bYhZR-di1K<=eJVaIauYN``*^RLf`?{-Y)Dt6_lY%1=4A?-s}&r zo{zVv0oZUiPC^NUnwVCXoOu9n&-HSQHgZS@XyjY_xGKNf^hz6x&a{De=1h))tX0EZ zJf_~npQ#_+u_$nnb%f;^&LBW|Y6X`Yhk?6Co&jfR@SQlcWBDxuttKkBwz2|r1BFR^ zYn{bJ+WgOIw@t{Zu>Sz1QWrHbR1E(~e2aaUXx7OOckdhR&N=P<3&jqx&xi^Agi8L@ zVH15T-S?=kBZZM4Smv)XgNGwro&fvZ<`Xnz#*^qoyw07|4SHEaVQ=PjcK%lC@hFdM zih{^@x%MD3f5B(&T$%?benas>jAiK?vtfa~P5ez!-Bw43K(ZtJQJ)OkUO!x_#6^GV z&0aT21l9uiRcJ%-G8(DfurpUbF(O~KfNu+a@bC*)*^Hu8EWXKff>xq3{a$4ni_!WG zi~husidww?LF-}d#f^U8^Ry;=DO{eoQ`z*ck4w?=v6krXlpbpf%ZpsAXthI7)gqxFa-0eOuIL!8ACXPjVT>T z!rz)8Qy?wS)t`i|yvsU^=<7p%Tn!}-g-SnP^rw zM`8y@b1BmH0IVoIt>?X^y6KhoT_-DYRr^#IP@9Peo(Ny5sVJLb-VWR4t3bpX&Bri_ z;z?tS55u{F5YZZp( zK`U4iPuQnW!5Be8##QVS>$HTgc$C|D3SHNBm0tXwA=ZVw6d$ZC*iFnTwsdM0yUMZ{ zQPO#w$@>?6n6-BfugCc3oxiY>$atQQ1t!Dv+PsF)rZrJZmcq3289v@&@V@3zIL*r7o#e_Pl<9`YSEp{{7= z7iaJ2X7#PTxtat;^nP1Wl-Wh*R)H|cN}gO2s>voEgAYx+`NVe+N$#B0y zKcNsSl=8myL>Sj>u$tC8?XEg>Ct?1>;B5i2+t8qBiQIF>Syr~19scd*Byz9@SSn%CetHnxE-;0dO90vK|Fws_l6&F!y7*s^zzPU3rgtI2t zi`h_`lobdvj-Ic!K!G4Hd`!f?TWBY^hPO}@)1|~Ktd2a0!qPkM?8cwGEvWc~*0=A3 zwu$ylR(nI`SEBa_-cRzRZDYcoU?vc}Q}&)&HSYY?e&FU>#6xmuHC~I2up8TMfr6|3 zvkTXbkjhQ{;He&M500J|*#%Q6rg_?B&QwJvOzX5ZLUKSD@IBhpMT2ba=LP2C)QA$K zsKBEKuY4X9o-74{VV&Nj-2nX00o|1@D6Cvv)Ah`=5MzlJi7?T50idg})7IQ<(3MO{ z@`v_xB>Wo|w*EZm^@nVviRfe!$39HhxNcs|CiDmA)(;Qic!qVN6#1VD62C8)sT(t= z@u7@mrVslmHSli9gnEw%PmSq@<1>s7exlq7r}h-2cCO58PJDsIM1)puVY4|39KS(IUesx z+qmGMP$=-pR6;V4LRQDU_el2K&C82|#8ph=k>=&Yq?fr417ud@1d0`I&NepCiV?gV6NEhn zKML;A=YKNsr}-%uatO3H<~6%V=)KW^hzBho_9cG}ht2);ZaLhGYE!~92^m3alFzKb zs~@#u+7TmPc@clRu9%M^0Zag`;+Mf7YwHLj)cO|PEEcO&p2$pp@JfEA2o;82*@+ZW0f+qu`Cu#{O`V#GJ<#({e~H`pYTa;sILDZb$N$`lV1)CJ_8A&x0X> znB4cxWXa<`U4|Gj_B+CW)AXEDwU_VH-GwJg$6;yK@!jTVL9EkMw#uIUY#P2?1PXyE zY7meMh(2l)@5_im8Bvwtr(M{7v9SU*iAV3GQ9aAwCwstBU!T7`KRQIKUAa0uILv*A z1Jo@%)=c?oje3X0Itt@3`7Ew9c4`_% zo7A-tmE2b#?zES+%ngle;DQpLq{~Sdj%yLmVn+%oy_*_fqN1WQ4r>o0Kyg?rJk1ng zG7h7E%z4!Qz4CN_1)Xl-nOR+HU)*1I!aE{vs}=BQ1|}n_cn19Xr>eSxlsGbM2%ll> zm#5G+vb%XZm}VC@*Rt!GW-k~g`gwMXyS#4P>>17SeW^6M-+hGw9N+|x`Id>yxaEG$ z%-c9g(#6;?W^y~_l6c*bY~yUFwFVmNb^H1J0&r-}a$P*TgZ_;_6hs_rVJmXL*K0nn zNnEtmeu@@Ug6&g3D%7AQlNv|9$4kcIzgq;t()sp9YdMzA^ALlrV7Hl4ZNYxMNHc+{ zjtgS`ghut)jbX1-^#}VV#N`M#0YRJX(}4b3zq@t5+y;1*-e=pE1>>#hgn0)dS zVG1`I5br*_p!7G$Wj5jkU9jk)*5{|c2;4#M7;nq%i_IfD za^9sHL%xS(RjZ5VStBxB0AqLpk%n&)dM76CYl!v?YLhwF=PHKY+_Oc3kzFLW>DZTXhO&W?nHoH z@gCk=$!D;N{Ka{7sEY(p_iSv`NBo~%fCj+k@$n%%BgT`J)^sJIXZ4%YHKK=`MOr2% z1w-#m*p7?VWbOZ|q7eK(y&FKYkWFVcCLdVa;`PoCHKkNaiD>OzSXdfE<>J2~I(QeNW`4X_fAh); zG;bZwl_dZ##0&$UJ^n6)EUth5{Hs2U0$2YD8`{$;+3Q^-3IV(Ly8o>KANO&~iem~x zZKtZ|$uiCB{dC~YtQ<=!JgK*^WCE!};6PXR&9WW4`+>6W3A)gevLMi;1jInG`u^8H zFaJg>+mvg0>DjAiL2sdVfUmP9nrh(>sh0BpE*#Tets4Q2vM{8jfQ}#&NBE&wA&cws z8~4U25F?684JiWQV0CijJ`y_St5iFopkLcHW7p+4!+c}7YM7}90MQ-;D6;>^jvO#P zFZcTvGx;JnZGS`R?x#yR8=ztPuNF58+|89$IRG_zzUVJGbqNr&CbPkFr>`p;FYkd9 z+&umh5Ob|?>cf%omaiUwqh4KQ84-N`GpD&!f`ap3A)V&EG)!`4rj3AQZ+4~Q+SjpS znH1L?{6UlZ%iYU0uZ=MvkY*W*#z8*8sU*KXUDE?Fa&|f`_Bz(JqhqvrjDY@#@~lKH zAg~rth>t_BKKf$N>FrVhu!4Z-yZ7TaWkw(>q4+Dupfb7tA_UCDn~{h|hD9CE(l^du zfxW}R4RF=}cb(eziv0nG)SFiQM@y2>ZLz0Q090&kQw(e>NQG=FefSM=h=0%1WlWCJ zUXW`lpnN}UP^`-0ZP3z^G2d`Z+W~jrLe2tSEF*h1uU0lPk`YJO6XlW8KL%|`g-pR>E@a{}6mgK1oBZ0t;~SY547 zqorz%e)~*rcHe3{pc#t-v_1YK8a+TORzM0#mQ+pj=E1zxOZ-1$1SR+RaU|O%@`N^B zpPu|Jf^eHwp3;U#O(1mZQ4U!hV+F{xc_*u!4S31sFcxFadYJKvYl#@+^cr8nZuU^v z>9$U&qTmgM;X6a!_9hXZS-9c{pK+=G_g#Z9rg(rj;1FoC$7a&yY`Vqp&YI%ClF!25 z0dfebe<-v3zTY9NR?R%V&@S z0!;h$V$xoB;Oh&Qg}~w28j^-$xo-29Z;9R`BwmYu9=%_+2V%ii-hHdyXVVi&Z5ttB z|9h;x?GynKdk+Jy(KyS#JjDyNI-`FM=NwEiW(Ru!fpA{Uvkw+eTM1{J!020r20M#Na4Gm?P}_69%ZDHKY>lCIemcG>x6b3%dT)!Vc^jN;8ZOg zM^DYZ3Ao*-sXc2YPig|L`ZuXlIkT!NknD@cvz`@5o%UpQl{7p$Y175tGLW-k`zKK) zwUSx5BXH`uXI`&9{r{RqN*+T3(u!N>c4~hIsQ*6$taFv*{@(!o(l}33wL0a6X$X}{|++v zuT0!O={Ip(D^0MRqGB4rWgnZIL^;vKrXjWFYob_AZY==6E&#)|i~Vo7sO2dxr*-1R zMymw-wXYg2_DhtNn$_dB_QArv0rb(XJc?rUs>PqwD>W5>X@;ppHZ}Rj4|N$CnR-+- zG*W&LJQ^_uFfOLhKL`K)Z+20M@g&wPS$-R7rrh&voe7Ze8}c{>y`blYOF@y&gm~xN zadg0ZHajGt1)|}{!yD^n)?$B0_59z=pXqpF(xB(%`{+>G1YwEhP8k&#douMss4 G`Tqcr1ZG+Q literal 154546 zcmc$_byQSu)IVy0f+&i#NSA=*P|`}b%1|QRUDBZ{${4dpxNaAcDB zat!{%u$EM_yLRn<)78K0;f(hm!@=A3QgULqm#*Kt%gY&=jK=WaAu&~Zk@xn-R<_r& z^SdPB5SBe05;d^Xvo*1{H?gw3_6UQ53XWr59e-(M?QCmeWNd#e7rW~@9KHYFQCkC@ zt5eDCO)LzqaUNo0z$+M6ue`A`w0G38HMsW9`ts`ZyZ>F>#7@s#2TrxUR{pCb2oB%5 zI;^5&YiVL>bnVyUB_B9;`+wg5-d4}x+Ww{2>i?eh(#pcZz|#KOL03c_97Df4P07UG z+~8W)#sJ;5Yvk9YL`9UG;?`=M%}K`GXD_q=ozC`GV{hZrgoM!hRtEL6QFR*|3LK1n z6Ick;X_T5}dhqMZ!^fjq-}9}ci-7QJ(~+2n?e?7)@-_Nx(YtU#WcAVlM!rqQHOS;K9xdh zAmkFA!stf*;ET3s;jOiYwzLoUT{j{*sN5)(Vlti`9iClc-uiSq6rV{dqz`dVDg^)R z@#i>r6cRFCAGio_SlO04TA>WZ(-}pE?mxd-2v54kbHB(7{b%xv3 zTY5>aKB#zE#5TjVXQ07=mC%}p2FA7NudeRo&mG=JcM=3% z^xXPn)ce|&wvp~dge-00nE8uew?4IsMi`iLJ#N>x4aWQLZ2JfQ-S>Z=l92vKR{m~m z3O#kG6ovMU`bU(V?CttEVy`}gAw`i$;j62c{6Eh>U6ehH%SK641|P{jrh9PaN)asi zm+i>pzpqK~-#ttG_i&2+}dmbj|NM0);pS&}AXiuU@08x_FW@%+_peXO z*OKAq_}@#{+kH^%`9FXC|M#)Kj?}oX?lwUJS4_v=KETuIG;^|wTa+^R*W8cYW)_cx z^V5Ux(b30h+^_!oruB6kpO^nX`_f8gxK~d|8x59COG}F)GJJAUgXI2g!9?ux2{}tk zW@l&TCSA5KK0dGr|12%(vJyrmB+wFKy;f8d78gga*Zv+H{BUP?x4ox_mNbZilhZrH z1Q#D)w^UtdRRRyLU2oL%=1mXc-aJZN@?&t8xQ~_rc^W&>t29{1)V`L_sTjeiS1#E$ zU^Hd=akhBQH=N_1WEOET#$OB+qL&`)i;^zN4<(fqli|r7ri5`DDgF|t`wejp33{2~ zoCT4qm1`W+(w{29Mu?S#>S*>SsBqjQ zRms=TIoz0XJKJtGAN}!B>ujE&daL2?WQCoN-Ez-EUr|CbvdoN(FW7=dAI{F)zL5%a zytS%n_963-^zabmww(NBSWMB$PN$HdzuXi5=g*(qEIAY^rTeXsiXXL9ThZAsUan|a zoIAI^HtQP4d}-FLpR?BHiX}UWwSOC77R1!qwzIP|di$M32zAXAwr+)>T6+ze0-2nb zJ*0OU=1t4+exg7|0R|Q;#E;BjNu7w#OkSgCcRWU%C>LdK zreNBmA$t^_+8^Q0qqsKhEc&q#LL2c zsc-8-Zhl_#iaq94S@RPdoqLE&NbovsksB6Amz8myoSd-6cCU>VdgbRc=WEsVtgSJ< zEiWsuI+#4USrKY9^1O)UA4yO9NUHbD! z@$Flg_wPBIo166w4VjI#D1(zyQ*V|}1g6kqR~PgfH_cf?F^Fhtk}^l$^>0q`%%{h! zsj0!j#%6J0E*|SbA#<|w^4=9$DIpE?^*>)+c%=0IP3i6I?94`?8VrjCY&|hZ6Vp&O zj!KJ0kse{;Q*%nTaDFjFT>3zrh z#gUsJW$+!+ARkyQ8JU^Ki!7e)v$N!(p`o7}b(r5(!U_oZ_RaR<%(&x>WxjP!JGV?^bzo8i!d zzCGbP8ZNJGmSL$8b#N#XjDQ8>c^v84e!5v#XYNWZMajtMKR&L0|H<>FWuZ&wf2*te z$AZCuamvigoGcc)`jx2O^C}z%`oqw(MI;rnmb-zM7t~>NS{jv^ZT&oVd?uEOE3sB$arvl=uv6|(zsduCc;30Vzhri}*>3;k?NRNR%zDbBmVH#Bwl+Ws^bA0XhT^vD7 zgA@1wLt;x-j*V@O78mm)x^irI^(2X(zyA-uHPt+2bLG`HQ(>9mg_6|L2z;tpY*sdnV5bVmg|(N{unb?nEa;CF2zquM;!PtP&_(*pPhH5Wpy`L zmX?he3##t5s_KW%@W5`vM$=N=!l=aP*WYDneVcVfDPMi6Ctp|yl~qu9Oa)-qGS`lJ)F~-tZ|L( z>XKz(V8Ab>gI`X?b6*9n7ZM%qTdz&d$S8BkA1zztaqe;rm6P)^EE4^-w1^AyM6EmD zH6#)VYm&=iyd>*Mzj0XN`I^z%M0pUW*)Y?DVQT-^AyXRCphy1A->ET*$6#`(zHWh9 z#rJ=G^>lQu;6qlmLX)DStml#CUu)xx{u&~gm^N5GxvR$+39@Oi;VOQxE(`{(Z73=Xt zDrE0~DbhdkA^{#ZygR7%6%sGB{qvZ4i#eANf!?}fp#KzRxes!_J?xbSFXHt~^!9T*QG)S1{)gW)Mc$p_S-uF zVd^yOa!(7QMNw2pIjUr$JG7kQROrmzS@yR>J}-wMv@}wkl_?r;@!83a8q5hiy0esu zA{I=H%z0)pS#1;h@uP@g%`{D&S*iL{#dQ2Fx*#KRYp}h)0w8=H4HEPJzbtiS^0K_dd0? z9)LsI|ID*8s717QbtP4zLiSf5kCtQy&(Ei1Rw}0Fl%#Z{AkLJd=yfDkz}h<6HG5a6uwp;d;9sela$y>dBjMBqSo0bKclEwUmCT<);^{{4F^(8^8kW2>ph{lSsi1J3&-JRhbkJq_1+To0Hox3Dj_`V_@7 z2iwgGYQ$mik$Il637zx@eHBkVYfn7G@WCddrJ~XkNi>c5(!0FCaxu^H)BW_|lNs8L z>)*(PdK5f%9zj*|^76VZGHT7)C7zN1J_ES3K2d(WU*Op-LFV36<9g_Hx?ZkUYiPYIQ$IDRZ1_|)|36l+(J+- zA0*N}UngfMw&?1&-=K(zkWM1Pkl)#sUZC?A$t&B(ue+4#oHs!|K) z&bUTePw@dm#`0{*tCixfUyn)_Z{PktE5i#PBy zYn2*Ge+YM~_bPEX&H}0`R^KO=j zGfdA&#-n0A3t8+3C}@T zOgG(SS<677aJ+O3LS0O^_w0^4kj$FZ4ZnXQ3RMF{DZinZYv#>jzRZv%)ue__*Lw8C z@%Hw-n}8pkfH$8+HiK?AC7mmVLj7%l&A;D7lcd=F#^%g%Ha4+9oLpQYglEy8tj8-U zBe&-3&jCK)&l%cL-J}kdoZXhS`M0O0{&wQdtxru07$GwBhTk_H2{~@q_e~78dizK6 zx*dseSxkNyI&s2$O^>@YJxjh>drAR6hG^=m`R(*m@~I_XUnxUFLlY>)rS8YPdjld2 z3>|`R{O#e_YmDBYJLe846nZJRWA%MFfcF>3k&P-<1}QBa{uV)&#a`c z8745H7OZ*j-+5YFf8Sm11;!*MB68jHB-68Fr0(KUl~?q)wq~TW_yKgh8${Bh(dwlXKjw^Q(PGX#Hj#I4-C8pND}!ZGlV)gGSX+t(#XwCgGDnW zG_(;ux~;>-R`2U2FTgKvB9cxqeEQ6bkKKrozQsL32;Q_Z7WSKGEIR_W+hsN(vW&3cioJe$?5@KV3%%BD$xna$8s2@M_O`ZPbJqOB?@65~cY5(@|w#jZt5|eBoC?vrcCbMY%zdtL;>9dSGeWgN>=>(h)!Y7!PRLc z{U$5`{sBHoT0BETqo4ML(Tv^C0)iC&VjQm6m={J7d}gJ=kxJ>$n*IV{orh#@V9`*# zwPp0Mft1V25{L@Z=>_2ALv$ZryCweh^(RMe7igG@rj6z;`qJJQ8Tf-g^ARn&g%#f$ z?~u9gu{AcPuFcd%a+Y!i3D_|BbsQbtie%=rJC^2n^JcJV;c0Jjz4kBD(!ay!0N)TX zm^5#2y(L8{hy(k_#=5(@n5a_q)@I6ma&x;RFsra6DD55ht)V6>pI2dgc|q?N;zA}) zb>HVn&QMBSkSdc1HBSF2Af1P+GTv$Ova+^YzwTCwIne-RhHI(3RlmAvJ-tUKu2NQA zPww*>hCVd041E;X;wwsNr$-@6jkEM`NI8T5P4!s^#c*CuYmqFcaQ%w`9k1P1IrZoY zp)-ZSlH1DaB#$32OxL(dhJZkZ2d$q%NXWC^5y}I~3BGRPV5WlI`Zy=(BCt$0)VcQjYGW zBUXpU|2Spa$A^Pi{KZW-ov$>)#n+9q<{pf9A=D{9u62lXw~@yM%=>oXeD#scl9~)A zsxG|dm!bWqev>~~vTtfCZ#ndNyu5OOrX1X>x3{;;*ZkInRla9@jgBX#V{01{N~iEz zL!+zK{WQV(nHQ`T;Lh*fy)&PwjeqvG?5cv)cz5ZN0ZV0P2uT0nYW$V#Ml?Ycr$XC( zKFrXVtbSJJRIzM?QfAtfUSkbSVmtS(L^^b@3L`nHUl(7rObA;CzsgcIXhQK7RmCEw5;y4 z3vj|BLZDJYqQj-soO!QMqq(cP-Jwg(t+Ok*rCAWEity_E=J;Ckfw5^K`yacr=Cd9$kQQ0dd8T_P%8Ag)S^E`3#@UC_idU8HK z*ZLcI^q9%o-xYZR23JHQsLw90ac~TF_4Ldi9<#*+{8=VYd>vC{$Q=GvAhASu;qY|3 zrw3G(Ex4VVBGee{#YOZ?OmHur&9U_*IBW2_6Tj1>CETdES8&{<27og7q+gchtC$^4 zM?6JLp<0SRvgo~^tK@3i>fZ1fl+x7HWI!o5wBkE>)x7z3( zHyxmJTunfz>OlL)AGN=iPoD{3-rXfTVIH*DIzEdNd>>)A%)-vMI>fE#P!$+}MaLZJ z@3+Igb9bk3EEgDh!AS3H-vi6?+jTPvtb_wO_3z)OqkhqZO2x!*fh<>QJQ&5C+dnbV z>qC~1RCCS@FkjLFcb)5Tv|9FLQ4um-zpAJ(svCxCMn(n^DUpGT3wmO2rfZ3zQB6td zJG$9JyFWvbYIT;*GH0)iV>$qkSy*r%9d+>nA~7uXU0X9}=ZFV-rKtY;Q{&3DXL#&f z&$+&}a(K^>fxy%CwC|y~xHzxPJXM%}$oc7ENNnt`qO;_3sm&^hAl9=tk9$NX4}u!Y+Eid? z_<7%jBCfYKeb-`VcjZ8ViQQ(iB^`IGSoc3uQ&UuXcXz!x`$J40qQ_4%bENOFH7 zSF+-rf)N>A%jsSihUNwANz0=ms7o^a4^l*yl%W5qRwT9Szb+rmd{N1v^5@+VqEB-D zkW@{g@AV~Ba&ogS^+}bK#!=LtK62{NHeOyrE-MWyH?T{=&p$rAYJgSbE+z8_mQ=_+;0f)Wq4D*P-eOP~UFD!6zg#@A%GV_S9E) zkK4MsG-e5%6(=_8S{hIEvdz+K-WRH_eXR2&49bmrkETRR$-wXwjbf0xkZ(jdLChof z=1l_(_COq;y8!{>N*P08hvw`l?6}HL%R5y`MTibh&oyeDf*DovSr?;YihmKkLR#c& ze`-k}IM^AOYs4iWpz34?(BV(O+&3~IVPWyCxVYHf(Q$6~|e$5&SVQ4(TNw&VCZeG+DEi{UIi^BK`HI{$L7GG%CHq*a1Wo}HCdm0|BUpkVd`L4LyTXb;CxyH?Yhf4je`WP6W>RKEr98m&Zfk3so%LtgX{dGA*Vli5;p4nHAz3Q|?rr^>7$MS1$E^1a{IjD1x_I<`m3WKYwC&lWf#H61B zXU3#gF9z^OVZUR*L-TcsV9Cy zwb+oehlVRrN=!#zKkPA(COgCQNM#DD(wWB*-S z8#bsnW>r*DYRiwG7%j74^!7gXZ|E9$^k-}9j!Mj16AO#Rg=q0txbxdQP~J_tHeIxj z_V>+Ha?LaS3Y&NwFK|Zkr+%&O>mZ1qGBO&B^gMCVoH*BP7o}`(YPxkruhuI3LD_r| zM5l-gowc<+NM4UU)@h~r93Cyx#20;bK(X`^K_0@6js?>!T(RLTE&J6+^Vf<0jW82M z)_GoP($Y<7R8DrS3>s}8pV9j@fUXuwCm$Lg-VEQmP?dC&EGFY&V6(AVP0RKpvbk;9 zn>a;G(5Ra)++D|tjp)+TY5YB_4LIhkKTl5!etUBH_1i&}s9tsh^UIf1XD1^P&bxUt zsgFL17TNnC+RHLG!rHF8McgKF(P(9|h zbTj@o2{r;41|=mWto;07JoMS6LBky_w;}_rKXD~_$)Q-3=Yq#*TO{IL1NGCa)W#-{ zZBO5s$}bUuv?38~dk#8C+yl#r7vVXdKF1{W1xwUEBkhv3*$R8(LqF)dkb47PM4I@coyPIHeRpD%6{#u1obXxcnoy7ag> zSN^vxO&jXOPYNKj+3w4~SZ*tJHZ~D@TuMf>NR+J6uICVT?4^n!wwuep2G@&7r=JS6u60LSK~(EC}=D$s`BrEDK)?tYQn_lft} zlYWj%&D~SxXjyhXJ{f+}))o7I-@e#8xjmP#^yp|!6>Z@!vK=N>l4Lv4Bpu}a+_TL`%8fL3hzynS%KE@0B{jC z$z0F++sU6E@miNVk@`keZSCzkBhv!FA9ac+e8njzYv0EXN8qdHmnkZ1=C_{3b{nEl zIa%+jXpxlDbHWk#L>qJ}E1#QGj5jR$N4E8{B!ML1Khy!8SXx^8&0_yD*!^-tQBj1; zOZ$(b^wYerDa^-E|#7kkf}C$ zRLZf>(kE|u?b$mn*th};OslSri?4DB4iES4*VlD0{IWWdZ?k)HPlK1)PuefC>uuiz z2Eg1h##jN@!;sL}mxhM4K+Z+^>yMgv@>Jgbbeg*%)+|>2!)oRxolnW)!t}3en>DA* z$%;|%sDl5+5R=@e^A(NTF0_Gu&SSTP6|EKx=>17fcT4_M)v@R0UhmmKBX)aNM+!*d z*Jz^C3d$o>H>+Rj=p<_z$c0d270J54yvgUV-n7_JZ&^;&i6fYpSoP#d+{IrikfA&e z2s~>W9v&Y-i?f`uT<(hAZCbEF$Tb=kzkcT-|meKFfW<#?;?@mFvdZv{Q?YUaJoNRA%Ti2wfgeHs=cElrKH5E zS+fZ$X~97OY*oM)+9Ubv)qC^T!O12id~Sdsre|iRq-$-ie82CuHD_ZvT^-oFoLDT5 zUSXe2q~gHkdbr_|gydQe7WA0M_40b@BE}!v-7@#`Hm~(88ZZgcC{_zrLRbK^ZHzw9 zx#p)Fg|wZVsPR&G_&Udmm%;Q%%HOC*-dNb?Nu^44+p1?XG^{>}qNi_L&g1qBIyrg1 zc5plLL-|ahJgANsXc+cCr(}1pJllfvKr(7SCfD zccwIgQ58*RgR$bOC1|)XXXZW63oe@r^;+-vG(s1tX%lvE;3}v&7&l7!N*LHe!zhf8 zj}Jd(venk*O5z2EBr>6^v-9l(3(!d*1AKq6!>-8i@a-$@SHg2oB3HBK zgo6$98m^JdWbA-yisj(VU}|f18tI~XLq(}LUXheys`RLIya!0J_&9Q#Ijo+4L%)Y| z(Z_zINC$$6I5($*<9iE?nk!#8y5cNu_eW#z8@vE2ry5OMF$xZjH@y?pLg};u2-VLBRl4czS+vxa_mlqaaaH=f3haS(sdym9WXQv`}f%)K{2s@rK<-IZ~$mDGh2-lVb3pEfKhZc`qb2j8OF(JCbnwt2P z6%%sK0xF9#{Ha#y-JHHhi!-#NTNlWTNwidQ2>O{7= ze5z)Ry4xR0GyL4(xf|vyu^0Ua+EfOe<{#Q01Oo!PWC42C{1KLES^#=@JXCic;(GJu zk-H$t{np8(A=bA{0BGCpo*o7#UP`q^rqKUcT%32PBKk|FeflkaZR+p_L{#p+pxWKp z*>al1!oJ5Qz$qp#&mfvznen9qg`(M=lI~P~=-<4uxvJ-6{1s`C^8@gnvAO=sg$5%T zt1dyeOZT<=x2qgBQz~hZWffI9{xSqCuaz}>9>aQE#KUs|A6gH^)S#*MgD;+nFo$><-P=xp|n+&*^8LiyEeCzX_Z zzT6sr$Yo*LH1~t$@pSNp3IH5uXCkeO0fIK03ggYcX+=eGk($qiEiHRePC*y~<^P3> zBX%&aS;Ua30oYR{f@hRD%HjO>RFSpijdMxK$@1k}Gqr;v;3(h+4UTvQ$wcOF4anSv z4ypBuX9i%nUkZrT-5c-tu6iyQ$&pj$y_-JB9BJosy8=+hf7+&d3zL69p@4gWv|8x$ zo>NTBTjszFBz}2UI_QlQI8yumjXFlX3Gae_-}h;h*IRfFHh$gtx{W}Pjgx)=*o9${ z1r4U9&rn&>3=C|IKro{1T@`T+@-fr>Sv^zJpv*C7{|)?eZ?pg(97Lavh%Nc zt>0Gs507`3D}Xc=F%^S#3<-k0E{1P%a&l~}tiRUQ);`s}Wx~h2dpDAV%)r24ZM@Xq zpMV8Obwe+#s_}^l=j~>Ku^LzI#l=O4Y=wk}?@K1zf7l_+Nl>81nY(KCy_d&ApuJ+< z_wfSbt;l8Hw5X`ao@IV(i&f}+MS+Nzc)so|^76&YmtG#8K8v}n9-s`F%&+3=^cEei`5ESDNh zeKjzsq=hIKLS@WO#-ev$t}jWjr4P~!@V)}3kycgpxNfGPVC>3h953;O755GU3>WC< zj*i^rRx^gSwve~{ZV+8v&3`qo;E6#huEiMfZ#o$sJy@3M5;{bI;(mU9AoYNogznQ= zztru&++Ct#YHa*kb3ZON>-Q8zKCnh=q4mOJb}n?Nn+6^I94`=A5ZotBoC=l#Fm8v3$h8kJ4BppCf5o7>nFUhqRnNk3WSa6E^2N3-tn9_FW` zN7;UX*YLeWze%<1@oWbirkdcY8|0#6c(T90>G_%}!}!O%ELX(P#XWH<5QqN!S%Nr; zz16L>Tm%u3JOcw`Ten)&A%zr9PXN}=&d#>8dzeaEwJz8!e0+~10b2Fq_8ZG;AtKB| zp=o6O{0g%J1hEnWhF-CThxwWnFW$a=9Lr^)JF+nayn6QV`0x4#C>mv(qarIPr2n{8 zq3Y<5^t5^|<(craI)dYTl#} zNXEm(oweqaoV00oOZo@NlPaH-bh~`tGoMoF2lRrzs51* zps-X_AXUT%EJlls2;}5qt$9dulV%i7Y6{em9dnH=uhrB<0If?)))yl+O%(GErV=VKzuP=BD~s+Rxu<&U*?AI|w-0tzWSA^YTWFoxa}QFrk#f z0UnG%Aef?pAdgV5Z7t@fWIFk6r;+U9`{A^!0=Au((j6pN4W_04k~w=n?R^~0%gejF zBLocr_}f+VO2$P81WiaNWYpCqUIjC+?3BsLE=U)G@pxsatW8x#g68~2OG^QMc#trX z=M*13dUQ55bN=M=q~H>D`HVOan)IiU)n?toAMaO{JuVMD6W^~63v4|HVdM(OE^c04 zY^qEWtT4D@@8D;nIv^c!6zO?P0A9G);L0CpiR?3{(}!94`7QeFkdOkrcNHaDIzOCQ zhM@xa$E}&mQ_qp`-(I(|q2L>>Ja!*@926(cZ9741gXFpW*8af-qXN9gVyD~mfiNe7{KwX_Rmsx#y&PS zc6Z!!b@-XbPG};?CSCq4wQYyBhXRnf%p;@s*qj!qy*%Hv`}g~)2Pi~^Fk^d}s%m;nKSfi@8Qo#F_+-zr1UYh5*UWn94{owI)q=mcED|)it~AGzwW@=yFhCKxuy3uY@FQh6e|KM4=!`*bhEgh6y}O zrspj*@h$yRQ*w5891y1(U3Y+00CRDB+Xmv27~U`7Njgg{sE;%9aB+!~KS(Mn3W0G3 z!DAX48u0!h?e=YCbtpVok-q8IuWNJGQSp3^MK%lK=f&1Mxvm4CEUZ8}`Nxm%FMaO6 z0)Vd4zUNXSD=!b}5D`|w--$9vB%~ZyHa9bYF+sTIm7-!2!7UgFFl<6nQc}X$E~Xrx zval=zc z@oI2D#SN)w4F&0s0&b{sSXb}=S)C}M+%vz6&m1e#U>N2^{DlzXM<)*cOZ96M5lT{~ z+0yw`+02ln|@P60Op+KAajOwf*auxk$)sw1>*C&yD(B zB~{p^1fHX(UYB#VCVB7j0t6j8Tr3a{zf8^bHTo~nxuM?>x$%+QxuSw_IYBFgT7#Dt zL>98p&_{9n9-oq3y)-N=m%QCi$12@VQ(jP9i$SGKI^J=3uJKEsU0`hNK4a5DbV^Rn z?K+{P8PCCtdk9NapRu#QbBgF*T^VwLlNE!>%bLgMA}+3{S4}Y`#k)hz)P^jrzzUSn zl*45>$J}K6^Wd@5aFWtbVOY&FJ}bhN#7cAvcFsZBfyMqcu8wci)V4?GwDJADD=RtI zrfWjAx2R}oC!yD;O*BsRFq~);93~3U*-K9W{I)~7e zXf=18D%W!Vot@^njATV==hztOc%2BBZ%9a_Z%DlES}IS5WLrF685<%D^RB;W3e*jU zprCxWhW@W&(8a;eY5`aXtmA~lls%fq_RpOhl>*H&Pw<^}bw3H677H!JJ5Kk%)Zd^3 zxe?y!=_v%o>iI7@5QVZ`?s)=0mY~|-ezZFg(q>FVzR<=?OG}{_3xQ_mM|OGCy0^c- z!rKtZqP4WM*!f?h1&r*rZl<*$qjfS6BHn>mk&}lYB&`0s8{r2Js!#sqxB(Q^2mcJa zyH)b-g39xj2#ymGX(@9{%cUmYN0691*qrIb_B?v2!5ryJw?V*~e>`)9etNJzIAH-oN`C&5 z!sVVq1<-ToTN+gkfdZ}vimIwVQ!ZR%aP_6Dr}ZgAK2)Wm5c){M2?v|M`JK-K1s2v+>);#P06yE~@{| z>M}he1x0J~@&fz&UjeFz7-D8h$_=mjt5FIvz*Sh_ZMo&joCAmlka|@KY6HMX0}0p2 z$mwZKXrhp*2doJl?TWd8oeKKf)i8uk-L4-}UoQgGWADexX{-D5pgqtp07wG)$;!z| z1zE|ON7mJq4{ilKoe;1T{|)6t=I#+=p+jT3mXwtxdH4eG)^~UXA_AmA5-Rxv)3wLE z?^ie*=d2ZxNHHe-)XYpH5sD;xJXs*V5WWJO>>@vQb^+X_^3dvHf4Tf=Y47&xY2==( z{3S$NK*MD%2(kSP9}UJFqdkz)n&)Nh`Vg&D7mB#~+D;Twhaj7PU-|DT;mY>$Pj9`1 zze$NcG<1Rwae_)Q3Bow$5GGnQRLxFj_p5z*pNp`NVsD(oGfb-t(T{DmrX#Q)XF^s zBs5*$w_|F67JlFR&e1v4^4$J0Yqad2#pQ*L9$I2w{=TYX#LCLb(94v8WHY%~?%>d3 z9yMp0G_IX7NS5|b+YrbqlEZWJ7n4BDer`NHmTAHssUo>V6D49fiT+nno-+AEQTFJC02vuYAcMoeV}XARlzN5xu$iUbMq1J_+#faa>+P=3I9)$)3e4Dc#B~U$onam2Bh12rR z?}23zPD|CpQJR22aVjxy?_AAyUYq0l6|6yb;^W^{?+sjtk#_iEaM#qFpG=yRs;kvh ziVIDi+Q9C-+`Jqb(0)6Rwb`PcY&B&mI)EYkF%7Ao}Ed?a)Ua6|KmDinHT(uE( zb@i*9Edh&W8f+thNCiFa{6MjoR_wW;Ev@p=N zbCmOR0dN97$<1Qd0ZM39vyTt)CAGb&)0e~)rOa2E?khOiyt>cqEC*`_K*yF7KhVaV zrC2Lw#jb(2qAp;k&WqtHJdPVAfIc~ldVVd_Jf{`Z)QCX$gam)C@@fe3 z5^kclHUTn&(7U8~NHmj3gi&|e0R4gkcqL0}Y1Qdhjeyq%X=^ zcAQ<1KB}vUPOFfhz#Ul%C{C4nui#$I5_{-bJGxKJj6WBBopH34DUq{yaH{1PXo zeMf4nisPsQQekE$arKPn3vNq^bWxJj_|CBdSNj0|+kN)s!nyK+=m}23Nj;{^T@6 zwCw!83lV4+)zxmnwh>(8fk8o`;o<2L4p}H^Wo3N8nXu^uL`#fbYS+;LOpNC{SEa1 zxs*$$@K>(y!G%DqxU z?(5&vylx`azwTgnJWm52)7GgwVPFQD*E7-}9abg}JQK#Y(NRM1#vrg~bF>(l9V4Uo zdumgiq42jpyCDB%tA3L)>-VU;dCvHY+HWxBqxjYxdEEcuHhR5;Z9?Y)ZAG6^881pq zM}9Lq2kysnQD>y9>lOzVxb1#NxYF8>F@(xZ!T?&T>9H|90(2*w%F5F_K5ve3 z#Pt{yo(c(V`cA7bY?|RQN6H+w9pAhT0ZG;J{OewGp-l_M2ysURmS9PxOA7-7v}Q4x zhI5qY{r|Uy|}4*3%Oc#mSbLp z_s|?l zc62@m30*`T&UhwXnb8~^po(U~^grA35&CV1+-P02wfWme@r{JHXqJ>V)TCZbADap} zndt{@DBt)(%7=-CpkaC2iI9ZkR^Q#10+Ji+4t&sTt{4ihJ+}I7(y-#2i6pwUN_*XI z1FGK5M9X zEv8dN4Rd{PH|QrZv4U=b?obs!Hl_yb3k1jRPlYs{?2zXMn`Py^0L8$;!Ev=s69OFM zQXw9F#e*y0^lB88#nvjQQ%D_N{`o^Kn5b~c#LcY|G%T$$#ss;jx;nCg5&wyGLl>7A zh{;MxVE~0asU+`Y2QCI_myB;O)G7ZxB@J5j(+2R-zU8?)W0{ZSfa|aOqtpvmZ?DUQE~_i`Vs7*|p~d`}r;*Rt+V zY&9eJu8WR>;$u^jC}gPs`}`gm>HYbaXPPSW{8XKF`5|YIZjvK)K&~)3^&mkQMh7~R zN~|NT)K7`VTMd!9#XQCK*$ih)xk73Y*?HNELzgbzimXj1u~r#rC7+LuoZiW%!sM%Q zJwKcB1KRt!e(7fHY^I1X?f_)(kFpJ$cG}i9B&}w3C8fSY6`xBu8 zWZmCA;m)h`E!s&B-68wU`Vd*>nV~;nO&^fkMi_{WHS(7tMiQ*lwK3CaxAS`qa}OLn zw*7H&@gJC#KO)URI$TWhPEXXtMti78g|HIcgI}@8WY-V}2ZxZDL|30$ST*7*zer0P z93AdExDtmOg7O}Y%gZ-Rsv|RUapFA`whG4tMQj%&ZT8$21i9nde&8< zHujJIZYbM{{XXU2uN6-Xo{R&LWSLWK6u1?f_Ha31&|fmg=E(iljq3t#O6~+$0}~TD z*j$Bd{nRwHIC$?DIirMY$i~#7T^`yE@nFtwtkf_1jrgnjG_tQhiLb2BchOsrl9Q|W zH$(IMxgT(r8U)y^aMSy!Z@dJ!Vsp^z^nh7a#uXj#9~>U`8jAwB!DrTx1h4>2N`0YA zoAvwm@4sM|ZvI=XqKS1Ep1yEODb!l+(z%qhv@Qrexo^wA&75yI0>(SL4P>Pn+Gm`g zdrY_<#rz}K$sL&&%N2P59I}SL%e*Wxf41z30)w>XBKM>7nXPfCbLOCwo?fb;TJ3;Y zG~*+>sBz3XrcO{M`^Lwg=Oq{5$sU}Zb6}=vC}5#u_+Qs>ux=q#i@TZp!}mw7_V?eL z-QsAWA}>N{KTi{5X|w5dKzex5^~Y2xQG?Ep(O)Ei`o6l%j^D*EDus9G`pSD(587Wq4Pc%s*P_O?A&-C%bdYp7+q#c1KvWeh-WR zS6gBC-T;=Jd@)7iL@n52}{T*O;TNG^N^6g)mY{`cR%l+;wOTEX`LZ!2tR{PCY%JyO7q z{l9;wE!V?r_y#2dl#sUDIW;APo{^D?n>!NT3pm->%F0MvTUg~aAY1pQhL$4_+&i~; zJTLeOm^HNFDzF(Mm|yu3K~hGBc%4e|oO)g7vU%KM6CoiXa@R&2&zCl_K~a*z<-YCV zqgNlk=PGjW(_W+e_$oxQjOZ&DJ0sPL(Bs?+r-!}YeOM-IeNjU>zC`_*+ z+er;lEpsC>Sy@>dFANo`y+Fsnz^HtknnM43__y`k85%gQo?foL_4ff_9zUo3=L zt#_IvM$QuCz|o4eR(*Viw^mpP8M>*csA_GtsT<;dmetQ6w0wFYF5v|d3st91$q0y% zO;dx7ZIad+ZZ!Zo0#*}bH+GMSNQfyB%;*A7pW3`W56b~#MI+xSB=k{sb~YNQkAZkO z_XB(tW!;&PI6`QvoR!$(sUHC?;ylu@fB$~9aQ)`_O_zF$pGsCe%Qa!0`o&{PRvW+U zbLJL2PgfENhTiD+-8Qsf-CPaa!|Ak*nogMh>d-u{xBuMaO`>Dvf#}7=>V9BNdEdnE z!9kAh_wQUmu+Pqh02y0*Utcv>Bu7iB@31WT$FdHT_o^GQu`~I2Q~&brN+<=;U=58p zX0kl+i6!ObX_=Y70WolQ7Xax3h& zq-??4ylYSkutVa9?V_}MK)k7}%C1ak22<*m6jgGBLQ&Ljq4W48`pUnpe-3?aNZ&Ci zK{ME5b?OyvyWQ_B`3;km`IcT6?x|c*=VA)T9|s2qY!4}sqI3}Zke>Pol7yBPS*CnE z@XYTaAun89o(l+=3cLev4t7I5efspF^q5DTR-{k)m^nz&F!nwQe*g~jF3R%k({~6t z4hrk)>Ak<%zqTD@;MV!JDGI9N^Yrw?)2 zRZT%E{*8u4z?5assHMKXDE2*b3k%5n2n%I0w1l@-JO5woz4cpF?YB2-qJl^Y2nZ6= z(t@-iARr;2l!SCQQW6$j(#?|YmM$eF7a<^B(k0z+#`N9av-kU+Kj8du&U0O^M-b+k z>z;RvF+MT2cWUyHzz;{{l#xg(*NHI@9d`)+Vjd8>hdoPxSfo4q7Uj zHC|~+$?tQvP;^ZvCK7QyV2A1)uvWsNqaQH_N|g|Qd(jICWpPOf&DK!<{HdU(NIaIE zdt79T^h+Gk7hZ+AF9cZnmsKQ-KPpreFG`4i$j8gPdy9)RJxYf|)C*fzwnPB-2~dDw zU|jPl9hH)OPD957?cR5)h{%tRp0N(4ljp$Lt){K=^BBDCvvoy(|K00y!gc!|mO;_gUGBG*?b8 z{;~PUf2;a5HfK-#*|S*^vNzlXU(gFfDo-**t8q*TromEfD1o#KpItQLFfPkjI)VAG^ce z$XGhyCD$mN?oscURhQU_qIh4R}^TVStm9RXp36;4moY7Ggc4rw942=f{ti z`uh5Itf!#_%BoQT>ggeIj&GtvAfeJz5cUPc`Ac|s69DQk^;6T+mlF$KFo*$`1YZJ5 z9U2-c`TRN9Z@@J^j;^KW=ijOxmxFzYB7dZ(=j+geIG-}()EhymX=xx=kyZ><=Qn)w z<~;=NPz3@-u?vU6?jSR8(S(S3;jDzKNF5&ic!TTTEDFW+@$vDU{r#1s4?JuhPrcM(<^YE{$Kiq-#BA1~wDUXFED=FC%kzb0>1AE4S zYn{*JD}o#{O8(qh`qG+)%DPeRf>S4}(!KnOeDO^GRSd;k{4T0jD%K1Pc7?n$hT&qx z1Mh8uHkhtn|B1P=|Cxj&;%&^?%eW7##+M`cND44g@IL92VEI>ae>YXOV<*vn^X7{V znq4yN>DBy)M;$K|(qu|sZEoTKnAVdMUTkQf zl__lJr9>|uk`P8PZPL>{@avb%Zj9-SCTnqV351oMEPf*UW;{W8>|Rf)B%Vpotd}T9 zTN_(nN1?rk1>M}ZUI-b%u}OQM-ma`F_P)V$hIkQ=cBfNcW*P|DR>w;~%P+isxD^Ee zuUIH8lS*E4QPPK}*KjyMWmal7lzrF9ypZfhz&-CdA3I}lAyB&BaJ$53G}69zVB}bn z+*>LTOti&5vCEdD`#^7Du)$EoH^N3u`MPIgz(y=x$(`AUoL<~eo|xxg^L9v>wl;1l zd?Tf3hf@$S^i<*d8}@912d7+D`N)IV10Fv}6h7mUHC|NbcqI}+^U|nyQipWtsFHl6 z?SR%+;NiCueAz9n#)a)7k8^jhl;GBT*J;RAK4yyrCvpD-K^{-H3`NkA3nbA_AC#w-(T-q3ujqJ@&Gn zAj<^s78pJ(s)dhvyT7rw07*+%R~IP40l1?i9*L=`v;YAE;Sx?Vg4?&7+rsD}d+*&| zhf^42h(|{*5D!Yp%Vz*j0Q93_k&)?S-jI4tOvu5O!NI{%HH{HfaTWnN%&29@Kk zBW$uAS_`#E_`%r(3oB4e{9KFU4MS<;;$dc%9@-A&=R0(1$Ttz)7FUM5<;=DudiRVO!sQN$K34+_w5ID z_GlrR=>ZBMdT`)SI0>wTn+lp+sw)^ zr#fP|jEv3ALHm@_)Y>Y~S;L*R0kaoma8LyUGNOkmRjkTWMMZopB9?o?!ilgbCY%@U zf*d*T9U(XHLt&Z%hy5A=N8q2;2=JZA?TrjCo*xCUxg2cHf+?FA>Z=?r?)60~E0jcF zNsh#gh8~6RB$|8IqPefPJt5S_?wfiNULj3uhflD=CFji(qE&8r7j6)VIN@~FESy!; z_f&6q!c3&4uAcpjv$?6MP`4QqBL1qXSXg$kadEx2MPR!`Pwx$i1fhUJ2&@6i1eY{v zz<|Ms3v`s4edBYsMX>K-Vq$`e0_+i>jY}8t2X;zYdb**a53`2LGc%nE^~^1Rl)-)l zimO`0uw2q zNV5?6vlg-atM0L=6^j)Rq~Ae}9De$Nn3(~25HN+M_4Pq4L9;m=D8%hHj2V`w@s0th z145rlt#ZfBnPzffS5-ELyg|~gRH(@%CQ{UDcsf{ucaE-Z^15?>>ghwaF{f|u@&a2S z7>SQ3pGTwQVJCjKx7zD5x?&oyhK=u^AR$ZNECW6$dS9%T3Y1O6j*T*%P8R<)Fj#E2Aa{#GP!gU#LpOUxpmkEUWH=eiQzkx_l=jAKw9Di-pxEnh#ucePaA}1EI>~l`Ny}pm zr{t({DroPyx!Bx=V$)fZ%)z_tfCSx>q9Lj z`schr*$V|VDJaIqZB%@EFY%V~vW2>(gSUvCFZGiJ3*CKDX_P1({m?v|(~f zL({j2+a=8bz3gt$UJf3uL%4Ck>wO|Fel-vq`(!ha_xJgaYZV%sPHw_S$44Oi1Ne2y zp1)|A)iVcX)R&N$wydy>gA9+8XIog9PCJ{eP}m0M{L~$C{h@r#cz);5)p&CV+@5^= z;QrR2RhPqV!)?FF{Lq%an*Z>W`CQxuZnoUq_eF7^dz3eYdo;>oynWqq@;(=rf5V|? zu3CAJW0}QlOh%oooF!{9b$|bwp}u{1ahkzc@uu>`c%@l->t=S5DvJwCRhz#4*~z6} zq}owh-w8BNL&8`GqU*+ZLitDO?(;EN7 zb{gkl;@JpY@wHD+F1gpQGjfsKRLu(uD``!6aLLK7?z;T!z*h#$A1}Uq|LLQZ{6vA! zoX4!G`sbT3g)&b2kW&&xvfg|6@P5OWp&*X4NhLRjN)I@lx3(9=Gk==;F3$$Xgd|Ex zwOvY1Hdhifvt}%`oi}or`PgKxTwidzm_-SWsUL6Nw1wV3qE)UYE-ES-%Cp{yNcQXk zc(Vs8d)|x2IVV~LOxav#b#k|}0WE7r{;{#Kv1-3GBf}en4p2}`jbF-ovCVw#?es<2#40#~ z;oA`6q<1|TXee}f_YM$jD$vT=4#UAwm7PF0UF&*i3tK=-^+sJsnZ+mrBjYsW824FO zZ>98RVm(F}BM85GanV>gU!%?MHuFy1;-=H$#s*T4BGv*_6-w}UWMts0Af`YF%Xn#R zZQ%CSw=b-+iGuw6>QO8ktE<1B>RtYD0TM$Ay$IZRNw8ZgpUj`H@tD79az9{3=o9`{c2WRA2;p_?lq)0bpS`|^U+7!K zHUbtO1PO>;5fPCsz{VgLScK0=audAdB&DU3fmsGZOGQORdxcwppFdv#L=&;?92gjY zU;_7+_gfm5=>|`{q(TJ+1s3&hK2SvmuHjQzS!`fg;JjmnDjW^XQ*8!L$gUUzzJ23H z{uQ=Uc%oJ1`>VqxRiP_WfPU<48zZGCB3**Wm-gA&ihFW{612rMH5Lk||W zS>|$68)$M!Gy0v?@MwUG_VFqf;O#D%3$YK)R14o+1cU`DPI$MxAuIy_N+dvg`I68t znFu}j`SC9wCa$)&Hi%rq*p<;kZ`ccgfDUIOawH9sUr^AYkO;-Ml zie}Hop7AGQqki}xBD^SiHZzkUvOx7o?s&JaUaN_TCB~a4o0p&vDF&f*%8z?_U0t+v z_XF>1(cAPN#W65G=q2_gq@$%^#w;pwD6Q4Yxekm@!JVH&YOXU^DBbKsDazw0Dv5KN z8`}Lam6db#-4*a)jvVy|3MQ1xlkrxV~c8VMr4d$$Y1q%FZl+D_b2OU{^|-EGr9 z3fux|yl-XYyQ6}eK8r!>q}vwCIWt2JrEZXs@3c9=Q_UI5h3e`H6{Y98iuk<>rt2y- z%!v;iC!YK`5e=f?lQw&fCEFz{nMu(P_NXP+o7B>+f4)5ZaS!~ld?8!2zuoFv|H}P| zFpkG8o|jOg>8fo_n83ynH9Tf=K6{_D4;R5KnVCshvFuy?NAOXoswx^S(CTxeBM(vq zCC#r0vJ~Gj=gP4<6B-+vTc@P?E$_H&s0BAYb6afUT0BqNV5%myTudDKeNh3q!>BJF0`I#MkJTXfJyTq#K&3=7#3bp->-z--1t@)E@Qnc#bnA!9*AU$I@NXkdB~X5mBjkKJ;=myAhrYg0^|e@A}5U^f44f=wn2CoH)>gJ z(P9mG>QAu1Y(hNZG&S!6{stn$Ga$ox0$0s?A;EF0i9lY)IUcqLkcW07BE{w9W&6rQ z&1wf5u;E;<-Pfu++bvk(djbic;K9_Vct8T6#BsJ~bsh-?#S&o4;u8=6CiMp38^qoB ze=#^Fj|R)_aUxhc{_&O99qUsd4urT=Uu}NB{#+8US1; zjeBQ=7xBxtGdXE(^inUOLls1M+695MqCM^e(qX~8svAeLB@j1tsV^yAmo%Hw&hw3<%KM%N&! z{VeWJq^Uso^LBHiDy`}wJ161G-@|!5ywn8B_cFhG{=j`O!R~pBfO&>idN$xUeG?sq z{;QMO@pX1$uZDp3?CYlS&5y-D`rc-x=}l)|(FbG@EEd-*w&jpNZ$x!#Xd$-ZJZ=4D z>0%>w1FJ>!Zbt0af54qOVbP@6%UScyR13(@8Q9n|0DwQ0d4i+U7gW$(_bHpH(qwzy zrz}{$5cRuk6XzuWV&&#zKfPXC-6y#jG$6tyZ|iKP+8R&-jzOo#zQ#Cz`-=9^J6}yE z9<4Q}Xto{z=U5Dm&L-hlg+jwMbpYRU3gXQc{7Xj}waWswwhTTSltx$RtRkhyWj9hY zZ7p|U;7O!g6wqc#j05q@#{voFODrFl4dw+_oR0`Qcz^oX@UcVdycF>{oYpCQq_2u( zx!&!Re{aq8gfQ=y?!@QVo3b7_V0 zE6{sz5;$T4D)2P`7l&l2d+GZf;%CpiN7ku=jbu!e>XJ{QI_~HyB@H;044YT&r@?Q$ z8kK%tyIQBx=e4pKAnV<0{O6j;!9|bLA&)$H^%jC#-p;Ey%auxg@UJf^1Xll!v)ys z2L#w*eXI=(PK~{IHmVzPC=a?a6f=}1{wvS z87;6`wX9mA12YUOk|$sIbl9JCQqzXry}x?V;N!K|oF6%ea%0~3DPfoLCGSRT&iWJ4 z@y6}<9Pl-nw=!6Ca}lUd47E~v$UqDXx+#eMmc=t4%A0{^ATVX~ZFvh<(bsMB=#D+5 z0kt^n5x3|pxs9gTt~o9A&bUl#>=Fq<{m^=IM7LW0&qrajg?Dt+YVc@(FWljnNYm)F-R zG&VPfEWSV~Ltj6YfO13MSTbSCSx{?vnN+pd2~TceKx)}cN4~4;!48f_s7G*cFg=Zs zTOZd$JhjNB)z#FjtQa8JB`4>12QK0q0O6x?Wj8plN(!RBFNu*}&G^p21o#?I*}y_L zRJJ;~(1oT0Oi-Z*01s?|fSz_ZGE~=s9FVrQoY!zD{vhtFe9l|6kP(0)3}iZLMY>lJ zQ4OpH#B16JZy=-UMn=Gv1?(qVh?uK)!xJJSi9mr0{?jfK(}6vfKL%`?WB%Z0Kaz~- z@aZsrqiyA5cbV!VCOh3uuRu0Cd3ea}(h~b%hSilDHn#UZztd-l|9Qc)NJ5!DR`m}% zzMUrp*NaEMAip?y?6F=k^(Iq*-A@y_4U(d3r7o9D(_1{CxRkl9mcnB zcMn`mNnmT?U0~Co6%ty@4)~Bo=Qj)X)qvc>?eS>#e#2Z0!adv`k)$Cb7HKp1QhL9Vxme^~#8T^al$N zne?B&;r&arG+dYCy1XVgD3z8?lPW8(dN+RPeN@SVE`_A%OK7a9p)#Gt+Fb^}Y#&I* z(e1Z8Y$|6&`rT}28YY^~4Gj#YtsjAk6|A89CnoZVwGu=;gdwZ|FKjsT5aW}Q5`qWI z)O>tu=p`tg|2a9_20;LD-r<=+pNp6BU#%=H5t{=EtITFyo!(?|JgTQ9noHhAFSNX$ z^4fp9j@Z4e*Bx6z?CL$VVgT{&YQmdAyUbq;5BhqFht180i#=akRvIX(rYkQx^H^|^ z1_D7xj+nitN3^iTdrrP|15oKUG&=ajv%$S!U*DKNA-`8~*vxxY6p9D6 zIM4>o%+Au#wmhVj>a&xj^YSy5j#Q8Cwwpv^}b@}RUdhtE^a6I_xokMmb^jt zur}_j7iFY#Ux|($u(26AIjII07OJwqa)2w*OQAd>fk_npK75xjl!LT#;qEJGO0|cb zy2PR_1d=wlv%j4!m}6v#i#^b+dZ}V0XfyL!3&lc+3H9$r$oAlt&t*ubKDKE@bVgnI zee=aK`xhdE`Hzj?L(bT{CfM~#Q*SU%-C%O%CYiUk^2x{Z>@EB_aAB!eB2plba1@*N zb*uh%2GeI6+!J50c71Lr!4GwLm(}ABpS(ektlyj5$1Jy;XldX^4a7_Dd6|cA z-$vQK2R4w}3vSAce!i^8*e+88Ez4UV@aE<%4;a{7(V4tdA!DEH;!#RLjio02Bib*i*YMpyETE9ri}8KpJ%71fy`_(p9OKL`Rny73ml{vxW`$ofD%p66@!K4P62QRgR%J7%F5yGb zN|jXcj)=LkN`|WMn^*N+Lpjslpyg@`7ws+~iRb;aK=Qekp%?4R%n+6M+$Rn{^z0vn z&|mTCV`CZ+$bz(U9%3HI_?E;=h##nn>F7-7zBIUs!VW+S?T_JQ?Q+-a*9Z@E#XioG`T* zq6Zjs(A@zf6l(Z+f?Uv4ZOBQD1x)2fr<=~ zyx!YabF}DSQpKg|1K%)^QA+&5GATXMUE_eeki)+ooArPuC52qT{o`E$NAN_*Ecj|< zkPd|XJa`N;&Q^gE@XskgeOuX0_wufVP7{6o{e|iaS|ZGF9y?SY&c+#OtZI zS#${ujg2M0d;9F!C;PXKVR3PRLWAz&SE~ke^N5|dyDVsFD|R@ExNJ&K&Eb)XX=soG z^$*Y_$Ok$AB=Gg~O9mTO$b*VNF#yC&m^yHddt#IEWG2h&y=8^6F{n6iLxCP(3^1Ac zU8r*zI6ROhLuImJF7h>m$LF2izL>%yuA!&qrnIzeKKg!7{T_(hT8K%H1rV0ww}oE9 zz(PH1lzAFTJKnGP;swG|M6Sa-ZchLT27o0r22)rL%-QQunx!`BR7vwe;&HTn<`_hL zH**72YIULb#FG|VDKyY@L`z^`gTa|Xt1F97XR>E|2pxJnbMAJ2yZr_w@>og9pY_HX zaI?3ZpnTY5SkfhZU37a7Hs4|+wD82}sW7pb>E=ymNf{Yh9v;JXF|CJ$ZQWDf?*R$Z z#kOW>rEzVYijIfWdt6xR$ejtMgL^#+#3ftZdzvp*P$_wNpX$!sA)oj4uO_5R%O{Kl z`YJci!&%B4XJSlGo9cJhy*@Vakt-23#;=cQYL1f&I(^>XUr&Kr?h6J%I=1-kbXhVu z7ZV9@aYopr&(Ah5?kDBsn8jDk8MK=RnEAJIbr$K-|E@iDgSy5I&zDqRQpJcPa+qLV z#T82y@jPyAYU-KDpO3xz>4|94Jt3i55^RhaO*wO((Oe9Zhv(hTG&O~+>UM9n2D^zV zDRJEk2@MsrAOkoZT3bL}?&FUy`Rt49>&&2@{;CO&l6^!=g;3!gkzA`>;2~2n3;WsP= z&_I1aod#`Kf@c<-mXZ>N>=38mCjb#0aOA+YRZq*xBy_<9I7-txZ19_iUm)+Kt9MI_ zM2*&~MSUP6D=H!fKAuI7DH;@6;94wuk6jdW^$XizgD30Z;qh64Q7S6c=Aj-pJLkt% zE*UUqsGc0{xOR*KoPwv1a+#v<(^XYCpQ=Z)F{6sl`d+rg44WJFCgameHLUFRPKrE! z?w?JaG2r#-XOG}oqO)KlB)xVAKatwflV{J$6VWMy0qPB2aaPn+KOW{4<#^V{JTL?X z>E`j}E1#%fGq87fyOhY5Cz0?2_b|@5Gy_a-02++ve;ZpqInf=nYzG@4oM%v2tEq8* zlwhMo`pmH8j*$jA1)6=`k~ihYSIbA`rR4+$B|0VI;_)7?WWOfe2+@TAK*&wJvtrv= z8-D)!-L(&oFMty(JE8t`L+G8raCw)$%(<(;$y%A=fXh{n=aZ$zeho6DHLKK6cgx$% znTow=&P}>I>o;KaBhZT-`oFCN|9kC($I88-rfkR$p(EUzo9+1E#E)US1@Lad2ru&e zil?UBvehU7E9c-~mTT4Bip{FN7I+L2uEO=gNi0mU=;eV3;81G}k`%U%07V*D5(-wI zn9c>igAbXBv3Uy@k$Q%lFg6qnFs^xn?sMU1FV1k?tfuh6)YtqoV|J3l;oRK(?BrtG zX4=~lvf;E`Tz3o5!J}(~5u-4DjIB11m=o%&ckbN%0d6NPn)ld0j*d5aZW9lCOyOG9 z@7^-XL30TzhpTk0_!xD?GVxf}ct%GrGCmc^;Ox#-#(D+IlZiEG-xg1ZMTJ^gX9mgHMq zE7}JSJzp^766Ne3zhzT}<-5?W*(9*qY|QzlRJE1%$5T@(8&q)QAdj%1!$-%7;WlLI z4G*WiyE;2V&Z4a)=j=TEjVe_AmYA|Kcw%XSt`GkvMyu(EUAs+PY)C6VKYaqLj8*FH zE${0XPj~iltc%hkarW*`<&{rN&-M>MgtHH)wo>rn^P6;Rh;>_)8T4( zP#?v#&MJJt?D zq^6hMLdkbM>94r}xH6l19`;ny3nvzGj?|&Cem`zHSZ5~V>flr3-+zZUZ$U270G|Hu zYAcbAjp64iH^a`0vdk0i1i(tQ0>Qa+9|+`4MAY4*SPqFGH~j=$x=F1-tsH!g_P7Z(?c zs%(Il=Kk@A!f72)XU5r|%gWA|1(p}OyiLoeaIDUtt!vxV;?BkJvEIHks> zj4{+LEqS1Nxi#N4`*%9!o1;DF>YWzK>x*8}41|634MZh(wxbIrG~z$O#``i)UCO~> zFy0`lw2E(ges0RPnTw4r3MZH%rBwEIIylAZO>n41Wey24DCc-)GLXq-FokFr*Tv_oFT=6Hrcy@cCU}R@(l&*~#I&_$pr??2bEj4O zsN|nt-~TWMlQg|jb(`O7h(5=5j&mN(M&5xJR~?e+NyVzAA#JLT*&3YiwMzYgBLC`c zu&?K5dz@s`7k64}@`!-S0s4~URHuu~%Hd%UY~&nsQj`pfsjD+V6aW{r{o8{V8~5wd zQ`v#~+iN{astAn)`^n2q<^JgW;6+BYsnkmP`BBB=RGgnK~CXom(1cc3g3V zzsHvWc(b-)83M_E0`xutY!PbKNX=`?4~&%jPd*A_Vr{jg4i*GH6Gd9z;VrL_0Vb4@ zUKUw42uyEp(2);$T_LWl93~|E!&T3Y`tv7Mjrmaa88}Flmj}af!>W~=mQ>^mXXMkT z6u<}~@{g%_bPYlt@at1l{UQ%fpEb%%j=)1L5`e7u`WKCmH`uDSm~%kL{d74xb~=s6 zb7|cHN$d`>9s)ewGc*)DN%`HF{f*=Awx5#pR$8o3B>WN<_O>f6xN({tLO1wWwr8T) z*w}&gj{5=5cRuNfo2=2^zt6$O8wa!{E-S5DH*Vyc(T!4EzXG1c&^$2+N+y61LS}OT zC6C=4=vR!lVGjx}39Si1cxv|qn794RxP7geVbRmTUSNHCXA+G}VU24|Z;%~aOoZTi zzDP3a#+4zIkNchpFECY+7MlNN`}J9{5B|?rmRFG9`_ETbcrP?O`XB8`$u8*p&vvBP z@8P)rPjk`_Qy7m){zn&7>&y27{zn(oii@~N$oHR1k6rlR-}b`)r{5O!Zgv%DY_KeB zH){9$CMVBLco)2M(vN5MkDa&L=$)rMiG@7}Z(KnMt5Z6`H-L(dapn`IXoa$jnyr_Y#O>K~KFOJBjlH#R~fl;!OnItWZ#0KCN^=TFT&iyA0e0*Adwo z%k6x5QwrBKy>x&Gz!Y_;)B@GSmw!K9zWU-2fw;o&dJhqxJ|I+9Xh~q*@u1vwr0eJx z-o7}@f;0QyHO^dSgSjzV0zeIe1odkWHd0QO7e0iwa329xn_5~B02hkTEkJWQSnxPA zg1Q>C!iD_+9_VRn}hwYr7UiT>n5-FbX{3atktRTUcR?e+&=>K-p6tu2D=u z6%c`IbJa>Pat4k}c-NG~#{f>bchIjh*B*g1IZCjphBgRVE=%|8_DTyj5fwA+Q;UalB-!J3*?MI!3BYl>VL0b==M;_7-YK2`xw`+kparVzO>!iZN&o#f16b&2~AmEVk zm?KUz08}f$U=#(;d!6u?3?}{?J1NbXo$h0g$I;{*41?<(!!AkDUd1-Gt)zWiJYBoxS! zKCn7ps|WT4wDvqC{x>H-QJ%a{O&iJ$SO-yuaI*vC_)V|9w z-}rR^?fpT8Zt zpg=Xp2%ciUWmIHj3u1CtcQVJeg6jG=g^))Gyk(KI&ET=OLz;rfwa$)KqH4Wz?r<3j zOl}{a3Z9>ABQ2gb+&11JcoKkKTX|M){)nSX$%G+w$?yV9y*aD%J*%wD%qe7Yz}E@e zFEp-aZ&n zGh1c2X@M3F>~dC68^4R5hR{kK!iJIxfJ`{w3gg@gTR9!{27I_*x|%}UB|c*G_DbZJt;*1-vhjlj*+>K6^oS1^8~AU5x3_M$TOO=hKt5CJ#^Eymt}QRP`57S6 zg|V{X9jZ;=>M?rJtj7SnM*t6xF_c#JI76s)L>Lyq3)rm)y&IFp;I`Uf zPs#+?R22G#hXoY`(i2P$gI_5Gc?uNoRypnq328c?pY9utQ%Hq|BNwtPe+`;t=iLF7 z2v9T6jXO>TK01S8ftyQZ0R5*TJUe8zo3yTGwK&4NKn?N*{KjypSyZvprtTbg2jUIc zZ2gfk0E+ez-`UG^QsI=-T%bx+&ISqW@QrHh#dVQz46Q*&%rk1;YS{J|z=6_*vL@Df z=%q(V!iLGH$>~dh))KUEj^OD|zhCQY%mB~GX6Z^y7uaAj*8?Gn=IF$H!N)EJM_#Zs z@PM}thjv3(yx3 zS|YO7!;+$DkODmnVt9#64aBVr(E;tXK=$1&IYPP)8^1KNp70eim7~hYP3-Xc`#R(L5V0(yb&fBo4f;CJy(5 zMoPp5)Vi+-EJ%?30#4i5x9?z>3%r{Usudh>mbJUUe^gDfqLIP}qAl=O)#ab{kcb$9 zKQ3Tc^np>}5(YlY__w@it`I=KMP#<^t_ebe%HCd>I)A+g6mUDipcqm7g@r-A8*lOy zKKdLSRDK7mo2`@!8Q`9ryPyXhsI$vn0~Y{n^W)1-?P45do8iO*w`B+!H5t+>DoDcG znh+=bmDboU9)hP_MkVN%oen{nHfh__WHIP8o59IRH1tJ%iU9Nn&0r@530Mp#Ri808 zwNziKULfT+sHDD$Cci<7=?PtKQp?IBLFci3cDzyYZr`%*XrO?TpPzK|4RSvH+n7C$ zN1>VKlXF4b>$qfO&!wf?Q)e?$Q;`4xCT&q|)U5QnzXULX5yIU&FeeRf1!fo?ei{rF zvR@z@?a65UDH$k7qQ9Xv*5jt+<0=I`z~iXK{P%#yacsw2>@RxgyI=w$f2aUHzQ>+Z z=Gi#Sw@dY5N}c2N6I^VtNd|*cH2eXNtYmPE76E|iU*`cp%Ah5VKi;VklU@!eB!DCY z+NS~{L4!TOnZQ>2c9E>TJvSo6 zbXpUe=ieYjYs|j`!JXh{6M^gb@nlABZUBffkO_p0Y_aJ;wwe`J2=lO!AXgXO!+d>V z*Ocu_?ruONAHjM9V?D%!HFoC|A~GZ*gw0dsErqy()9LQeA@HlvAVYzOqVHO2me>PW z*uSBX-|I5->3V2$d*=JROw0ec*lMz_qs7t;M4><?w+oD(82(EKOUCl-b%J?}RIj+);wni!c&THD z#8@!h-JxCwSg;*%H$mw}6HwCLE`8>)w=T$^zbvWEUZ%aY!HBNaL4{QWY;1rE|n zS2}xmsXlj$T~D}j^wu%7yK3AyM}XP6ZDo35{dlXQaw*lSUL#blf;LIuGOp+~b`tgm zg0f9>wZw<2+p2a6c6-MuFAbz3_1nq{+3V5jTlW(9OJBXJHa+jQ+L+AG$+^sV2j%}j zohxKo{2^miiK}|;Z47jN?ASgew2UIR3&Zb3a&9u>@{>Y#Gk++R_jdtu@{L`$D z7KjbNEBOBLUYW% zX;Ii_JSL9&)VC4N5Yn?1wj(5eN_3u&8FS9qssN zi%fsh#y%p^XCWfnEP-~9u!94BNF33-dsei)YY26a&jAT$te`zk&qs$Qbk1A0q9X6Z<$) zWOsQuC2B5xDgi0Rgv-i<^_p!tVdx@lyOLKPbl|d>HZe(nlq z#dOCZfxLVQfd%d&Q;s93!rwnQ{k$kRoeB5l=si+87I{!ot zAUt3LXb9|xO1lb;e7ydL^md)b#I|H#IH)x~^pk>%dpo@E;Y$743dceTw1+{GS0t8# zY_3b^&gN5Jw$RdXk7vN@%QwJP9K5K{5MP94YQH`;19h>+e$%wH-Wt1 z55#y?O2krBUv_UdBfS0Y{%^ft@NUE*@D)SSs%Ty-3d*y;-q4OG-d6j#U*uGY!uby# zxP&8(&KFl!W;f2(H=wbnQ|$y}xkd8q=ZaqljU6Gv2OMt$!41llNHyRvr~Zif3~D5? zNK!rq2~}sJP@E)f1JFL(k%|kLZN392cRR846tdfYn-jwDKIn_8Yif^R1`g{2oSqb@ zgC=?j!poH}K(~S%pC`kp(>wo_63_e$p@EBwBtiY+80sD~9(Xotz;H!MA6t-}MvXZy zB#gn9hrInuWo1~*U*b&R>-|mm)YH(0^!POFDBuE}+ZYlBWeD#wf+Xq?t|$eT1e51y z`=CcmEL*F2J69Y?lCkaQkgT!XXP2YdBOI1aY@zi{{x7R@Qna5oL2bm%8P%D7Mt_mDm z#g@>k9K*#mP%ERb8HN0FlqP5#;%s-=7wjLA4}#$Ix9(w|)wk_-u1gu!dp{KPN?~T& za8;=6^gw~ExQyjF7$AEb^`jD%_I}%tpMnRTmN!&Xpj7m7v`i?gb6o*Jy(?BVv!-TdyNFnAd|)8r`0(Y?NO|4vgVPo5b9LmCfv-0uOri0f9&4!)o?e@+ zw`TP^_w`P!-wfd3ppM+;=zm?-qog z9gcAIZX!+Z(TvVQpn$IjGxZhN>GHz+U}_verDD@rq@?v&qqFSYPLasL4;Tf$C4rxkHEUA7YaHx?m+Esd60%Wjctd$K45=lv%=ECVnF+NWXRUd9ntqe zMT$fr``ooPU|^EYZUT1sf1_{xD1LPB4X0jHR)BYFRdB~?YeFcGYx>eGpVpsS4$4skS`gDYKZosKS#Uwq0^tS5X~Yjq#%w&JezA~m~Q!H-GPMA zQSURZ=yj^k75EAKZ~fk_S9&8`!-A^^6nzlY{v|GshTG-yGVmj~qCPQSlaoK@uY zW=XgkdN2XuOPT)8-5eHV(vkkxq=T8Kx62i!h?)1$OJ~Zq5_Fy!Sy{e74-Rdky`a9* zF=kI?n7?{05}KhwS+=1;6!I3102y8Wx*my2Cd{E9hf8;{`0$)X6qJwl$8%&zCGI>Wru+k4FNdsu@LnIwX;szVP^yLr)QFe3_$yGI_g-&gbkO z>`&UnF5B4D?F8?hOhsK|bL3*Z;VHq$9{x$k_-4eRqUrB??;wTuilnyZLtpLZkKa=q zi86Cz=6SC_$>~IM8|PVj)h*mSw)CNv8b!H6tYztTbswL z61$wAxA32sh2L(?JjFHbn#E20#5s=t)z^RPmp|O&4FbjC_a=*05+~Z1hf8ru`+Kek zF>YPvz`V>MdPYlS75{}z>U#q9lky9r<09KL+pwQk5hsLy$8nMJC9B#VG_R=aH@RG zG|_6f{8Hi}p?Re&R&YndO0hK=&l7pSt-tq5{9UqGSCg{82Ddi|Gi@I;-jrzDrsKfA zmeyv_$vON@M~y4u#&S4m!cePqxNUbdQiLAR+m2wdyOc-NE7z5vZ zh7o5eX>>T~! zlWBE!d019MsM=S2&Vuk-${%N?&owt(6YW{C3`t3!@?|m$?!BlK`K~t-J}=IIDW;~_ z>orG0t7wT4rbR8qHr{*h*+_?Td6t)BoMC_8>&M&?YR8dfnvb}gx?*u>PA$|WZo-)f zb^fEHBRCYHz1`HKThJgA+|0m#9ssS+qDe~?$IPPKq=?P&Xtze&O;1r^eoWi8-IMph zO22E*??qwUFno4C74Je2v3fZ|t@BihDPl%-0QL?Zm(Zi74MZD&5`CZD{F=Yh;h@w5KMzHCUomQ^*)C`2sDhIXum_^5 zO+;(tO$wD3)4P6GyT4!m?O%)^qb7%u`JPOrj@G$v~;(0m&DLWhlF&!d;aVFo#mIql4r)5JNCZz6{5~62znpKo4WM{NfRkvB4jZH zZIu-yvx3!ySeD#++3Q$gAVYUBV!%|nB>Mf;rDC_vc`4CcQbeRM6R16E5q_DxVYjCY?$6jKAj-JLW=Ox=$w>{i{iyoaDTwbN4 zhAUsxw0zvE|I@V8L-);3dtmzj!_wbv(i}TSAXIwlbIymW97}t5;#r5MIxA}*=3HT= zjj&JSel+Ws_SDhKmKPUz{GyAMfH(VnS3+iGb?P zlK51=eDUl)S7;j;`~e3EOt^sN9M>Ch8^_NRvD3lIGU`Nb|Vg?JW}*A4NV2b-GHKD|qSB&cR6=*d&o*!RyxcIt%c>9lP|R6uD&oY#ZwRt(C> z-sar^{r^~#{8~oW?+6G!Vr|G^PFm`2 zy!g+Uxs%>3raq%=3Q&36Y+1)_v91aq^RZv^3*oe(Tr@J2W-)_fi-T&}tXJEfINT8^ zoo}-;*5!-el|MUJ^ZMR1do#4%Rd(}4dxX_pCVtaPvnZFZ-9z<=o`a58d@`X~Prql0 zru$nz8vQ8ww#?&y_Kd}uLH@rR+SEi|9LyvunhJ3FM>oDp7khL-&KRMmQhx*<2|IBH z>sx=9w;(kL@EGv_Tj@Y^PkB_xhLo8i<}aHlsp*&GQ?4<#Eb5}_>+A2ME~}cz??{64^Nfs;jEhdTXd5F+YLex$!HNr7gDM*C>dMvZ*8m$L8b6~D#SolFa ztpYYW1RBEHwB?p!4sRajc91+i*5N~0?pN={bHf*;a&;HTHj0oW7bE|^mPmmp8onX= zd*US-0Rfhvwc2;CkEK7X59{B1LrWCK0+xKNSZUzi6MAe9H;W$;&EA=t&*8hs=oqBo zC49Rr^}L{3Ft5UIW9)DXQI3vMb7%X#ylh?5)!Uo!uQcCugydox=UN@b`z^F$gD#;3 z=nn^$_D{JyaUu0U4*&|7*Tn;g*oOJFstFugFq(V}8BIA5xl+gmHaN7J^|5bWTBb>1 z{k`2=%4xZ93nnUQSy?NIG&mVIg%wD{O`tW6fDavb)KmkF0KDX44ksQfaqqlIf3fzD zob%Mi_Ci{;6XyO>z7HH34_zr8^xfh_ zvOnAp#rVSrBmTy4`}R z(xBg;iTY2~gqw$Z^0bEr>BDDxz8&cZtRcnTm@@4Z>QDlsh2}=LU(5SqRd&?%{KPb1 z4AbJ}TVJMw3gZGD#K)o#KP^e>h2GGMf^tlh|E`Yn{>KHld*?P_%ql-EU!lLC%hVr8 zRBzR%ow|#RUZn^n)B%wd@&*0cFl{mW2k`kHy?-=YY=i?N4Q7v)|2`J0v~59$Oj>;`P9PpR52I+ALywRED!@p+n(m7LQ7aE$UI~D=L zq+fdO{#9MxcRm~N@K~x}>#9v%=&LkCBA$@RV9oF}>nTL>BGt)#Nj#7-^R;&c@3373 zlv46ILnL@Ah_!i>tghLVyJ9L)V-payH% zszx0dPFPoGWNr?o)&a`29grXhOBbA{zVPf;dwY3#;hwlwRaTb5f%pX;PS6ZT zvr<4Fcet6zxU=S@KN4HI1LuV0x6#qivcae3$hH|BCgYG##}~I{;=km?2j6nfaYO*HXlgzL-y;Y=hWmZ)J1E|2jpaCa0GVJ?c zDfeAT(|sjz>XR|$46*Gz}Z64?Y)^9oqLl<>!;6-dG=il35=yeo<;*$%BJYyq0;a{XAyBG;t8}CZc4D9(P%r9L( zbGh&Qy}G|7$b~cX?TGa-Av%_X(~xQiTCE={m$=^VqLqs!pDU|+dXryUOhz^9GN%D# z^^?Dl$uJbdCp{uSrm9(rjrH!o1zcL8@nsJWN+bB8Obk%$tS*O z*V9E&t^8R2@7WM;H>pvLjh#*Tz+IK8Nl87KK;hiJnOJuDmGIO4<*skQaXb*Y_{bR>sudddez# z<3I6SIj1{wQJ2M4=1Z~&S`@6tM+(E=49R*rVm>Xd$q4j=Z}RDOMvBh<(reS|6C(G^ zKVh8i+niv_wU|;YRSv`{6iKvkwA9ZU#YTCIoiu7sK--HK&Q(T> zkLWHaDK(`5T7cQ1uCft2g1;`C2GF(<65DRw2);UpQQLD?LbDy-gEN72&|b&9Ei+31 zN=n&&l#=6ORa4fYsy)N{qhwzp>{hNR?$*8HE0$$$c9SJf-yz0TqFm6d>wbUXof%3##$W;}VL^@Z?ijQyfvds^j##+!4qF^&P^=Uo_Ag?`89 z_z(J4pPuaP*{PjPPHR+SP{cGi8{H^g{__jJYe+FH#0|!<{tTRE0s(1Ako0`>#b4kA?PSJ<;V5A~ zy|^l-gH$Az#ev@Q&EwuuSn3d>ngF~?WI+&LVS;pEY;wDH3Psw|`CFN~{0jCz$sb-0 zDd`{{wMgRy(M%8Svk3$ z9O7#X$fIs$vx(U(NwBv34U$<<7MLf+4X(j1T&zKr|6AMDqvLS=%F>YATC@Es1u!#*Hgdp_fL=a$}!=blIOMmEJALhg!9#AZA7pj&gMZ1PtyIF@^>ab3B0d; z!YV!YzDkb!GDc=M6H`R|3r&a`!PBOC}$KR4_sRRe<{#9KErm}+z z3XB22D!OeM%Vn>Ov1r?p@+FL$!+tfx9foGHd@JInle+CMY~V7N#$%oXenE&Tv$Yz7 zl&pgK;4t33Tll*TF)0F9teBW0KjP#iC<$8iY8fM2t{X1)9P4M?*OFqhyMq|&n?rXH zNP>e7vBx);zfEDE89BB4FQSB~eAAf6(aI1~A=es7Y5zTK^I*R`o?7<1aXBsVGx{9A z9EM$1g&!VAphGwzw9}0u2Q|UA>l)$jd3$6o2n=N1ZzYGzv6%6mzNK=HUhyXd^kV54KWZkBRk*kOF|n8Fc54;q@^g7avQdIDT6UIs3|cq{)1}?W16OLK{)bC zgW(pkCIP`#8zcC$rorG1O5YjV7SqogCl^5am4BE?+e;EI`bT`)!OkvR9NCfd#k0{& z9C!0S&mfKgGeJn=@>=EuiUPoLoghM?cowTil?6}Z`fD}yg@=!I<)zCrn5}^~9#Pm@ zeNJsfGz*)cmQuH&X~q4^n`GdepTAnc)lfyCnmX zl^D&ej1LYNuQVf>SeeAB$~W$Lyz=XfNf3yNn_KsO^D4HDe!H4?{mr86hi|td?U@tg z>s&coM+vwxu9QJiTNwGwc{;YWs^EmNdkFSFH#e$q* z6wX?V?ZFxp#B)&>lo-F}UW~se!xt2a(5y^~?QQz2+MBz(e~0B(6GeN^COLntJ_lPn zUrjps)cmpB$z8tsG#$(Gw+^h$2_Zr2K^KE5h4|h(IOWGL4)m8vqLnHl)}*A*w|h%>bkWBTNJX>|+8zp)%1{Ac&>(Q4F2R?SWKWlv~kAmcgsuU}TpTcN^= zK$^Hi?1vn9t-jS-e5R=1l$jYTaM~qOy;^HBSahd%;EFIpl5Dc+=2pVIqjzX%=#wf2 zYWmexHS@*1m0lpW=*;;;06?w2y}9s=WwwzVp9?#3-?5qeUN}mkYmUJ5!f5My%>iYu zA?HlK{N-krAOaF})D z0F}yJua+5Sw%$@^ogMJN*>;baZ`k|KZG623CFj@VJii&JjG4cJ7 zcGX~jr4N!QFuM)Ac&RU+2Kla9z}Nx?gYz#h$*#N4AzKAH`kxTht`9EsTAfv93ErrW zN`YPhL~LQ&ygrm~6Phk`*+!)%6ZFtVLO(fU*@lTEPcKVj6BJ(|Hx+x*t`1n6>`VUST@Yxb2!y=`Y%^o$v z!^gK-aR#0we715%*1|)d+lK+nJ-&V%%7?zc`-J%8=?o6P139BSHTIX9%LN9ZxZSkv zC{K3Da*kN46pL6=lX)81Z1kN+2B8udsRZc0o?5lT*CgeO?{ZKEiv$m=huj}=v$3wt zhzZuCyZuwy7RBC-FHQ@LN#lJbt2A)yog{#pM5F1~51~tF={nek5PNA{pJuDXKI~5DiD#@I{_m&-d^}u3~EAEmxTc;oq*>kR=E{ScumU z!s2hN+W4q?{y=Q1C}@CafEAQ$m&+bk#mK~)=1%?&h!Q(p0+A7Hx2CHl-0O*^OZ0hQ z&z~Knc-#XN)Y5PdGmdHA0NloPncsr@KWs>5V1TDH2Nxk2Zz7eWo0(Mteje2Dit@z*Rh+%TJoCW zpa7moaJc4_#tV+ZqO+J2GS#z+$J&rYxtOhHzItuu$xmju0v-AV&wQ z_!gGn*iGQ_=cz6n-E>roX#$sm*L(6QndRlSeQOnPRe)>hYF}Ji;gxm)^wH3?P4>R-_#{MGe!VKJxZF+iXek7^!ZZz#uqkAcb)kU;@2B86uMi;; zq%sJ}c2}llUrle`wgTcaJ;d{`#TYjeXtp#vINWn0Vaazc%uD8)#!E*J584Ap(iCJ)#T<* z^{GH!5|H7#q#{K;*jFZntV~j_6jD@ z+ZGTPoXU<1W3LQ$en26+XenVnF$w+POE(o}?vAca`18E?$JvRu+Oh3rsuk`-EssPI z^U2nnVuY~a1KgN6m^jBf2RW`ZGqbbNl0^Iq;dwnI8-MvG_(ExSWjFt!EY4RAg~xeA zP{%#JyX4rRE)h+ICijGhfZCP$*bS51wH~3+A9u}2wbhEcZ`7Gt(h$sn_uOi{faR2L zL+qY`O7`O8gV(2Hb_g6v$FsR?#Ci~Fakbn9qS9RN;S2J4yO)ZhVqb2qRxj9rm4RMm;rdRd zJk`OwSCtn1!B6ITr_@LL88K$)U##A3`J$&%JyTSY(?{dm8>J$*OlW}cTs&b*-afQ$ z)R7!T3T+f=l&t#MCiZrVG)N`77qrYthGjKZRGd>@pV$eS zJ1QE6gb}W6?Vc7DR=j>GwnOKJ@6_9=75X4P1Pc?%+K3RjKtQVGAm0sGN2Rpj>F0!` zTpJtr+#|_00=XGz94NEE3aC%&8PLB81l$RFORIrq3WH6Y{q)Vhe+Q%d!aA30a^t5C zYi7-1#DlE&DhtcXz5~r1@OkA1=cBL9tZLN!b?tACU9Zzhe?pI8$SO|zyl%aL8h)WO zjMxBZ)JUL6CGM@(%g3ode?Eq73*C%fsCEesWcjqRN+CV}pc`>Y;piFAP1Y&*Iw3H*_I6(FZug8m9*ZrT6x=EE!u@ALqfotKCrP7-SWGCAOut7bk5-h6=uKPASa(4jzr*aLLek&LsT6!a&kg4N^LkRR zylvm>gFtJTA3?};W?92>;x8|`z`!2y-P64!5I0C0a|LMOfI0BTBGq=b33Z9l+|hq0 zDQ+pSBs{OL`;v_`l9qQZaw6a13hPTUMe=qJlx{)$yE1>149>j#2hAb?S&o*D>GiZs z?V0+4e&XMF0BAR*lX=CslZYI+@eRmp;9E_U<4Zg%zbNUwO6x$v@csh=1hFAx1P$^Lk22XV)0T%WMOrUAiDudzE0l$u^kY1T&-frOG!>|u>XJP>q zfFFYqmLY2o=nLR=QBpxJ20E5M$(_&!0NetwG+2dsL#zeJikgu*545y@;!TQP;f0I< zIXa8e_J?UAh$)OZ<3<-i?sthM#E_t-k}mP_!2Y z;Va7x^V@qqECgN>so4r^c~p31|NSy|@r@aNOPwmzh{_@8B%kv5EFdM7qr&LHPxG;8VY-$Z;vJZ@J!WYNDZ4I0^i&eLMmvCTAzW}HC;mZM zp=*=PWbfRSz=+dYddjKm>0kon7L8=}X8%*C&H{@;gO%}oG9x@;pSi z(-o@&%%$JcKJVSZ1-6S260^GcXs@*a$xk-9dj+thKtq9C9?%sDk+gu}0g?z4%i}-~ zvtz(Yp<;?f3=Qtsvv^#)LOCi7=Z)~)BxLL&BnHEUk^r(%+FoR0FmyF?%F3dg1liyL z=;!CB{$6mD6-H`rcU{|9OkuY`bS>zkq7XFZ$V&!lHxL7p#3beC5Eg{UOyt^G|k zye5=NVmYHiBu`-G)(Ha8dKRWeCW)45PSh-UetKLX~UcVf$gv1)(3zY*u$|b2GXk` z=|8^#ZQm4;4FRnVw}I+l4Qf7lWo0xBDZ1)kDuPy?k@!&pFW#{k%>OF)cH8^(mJla? zi8Hcc1ckDHYCqz?wx}~aYKc@0qP?}WndB#LqnR-xJA>FY9tXXl4kfYS$9RDkg=?u# z-)#^WWQfVlv;KRbzq3Fk?BRGSh7$RNeyr%*Z;yylc=|_F%nI_%i2#)D)8`_;6OiWh z-HWg?ns3@Xp&lhFCrY|j_Q~x9Bi2h1pXlAsymj(%F1`I@$S=JIF1f6dmh2X=l zPpRIpTbMc%A}Vc03DMEfbL$pS01Lx+YiKC1NW!%_C;^}VW`^lYxf+iL^;h-#;d-1Kt$lH4Cq!#9?^@H4XayKVFSv@c$?_wY6ZwmReu$`b+;C3_1GVjBFj1g7ySDId7wQ(xUj- z?110Szg)MspH3W_<-p83v{b)8lb)y;; zy14wD)!e;3$f_-q`@zAcQhjFVnR;6YGvDWGGO~-QxUfh6;{x15ca#Hu=7)$EPM;L? z33_!U26D{2{f@0QdCGmJm&{!SakGNlt`MP~{WU3Sm0FK>USxL=MyiFjY)v>34H`^GGM?AOhFX^DD)Y+M$ILz|2%*9@K zQb~s-ZX}-$+(>Xng6$Q_(*cS^V)CaCm@KYc^B_P@PV3$%D`?cy%QC#p)T%}V7@ObR z+`4n;3ge;hPr4v{i`sC^lewTyx(jj52cgSDbH6(*Qe2YH`z=Chj%j!_CDn{byq~}! zwLw@J+0812oWNaIs-1P+dBkWiK){Va1axYze_-i~l!Fj9nC~+DI8D^TId$WAy}sqg zY{}_zT~;DtI}>LjPBlZsfuu5J>$C3=62g@bTse5W;mAAin@)|2p*R(k(wkFdIWq$R zFc3q^nX&Ez2R=@6HG4eC!gB#!Z9xZ*=g=Q&h5#@x7&hoivTj%>uu6bB1%R&ga25Cz zK_m$&^=`;Vb@e`gRJq__go??qkjw3y6}onq9S_gWCaIldMQX!luA%;-1LNSnI2M06 z>eVarAzc?92AmGatJ1QxWOQf*3th;HW;bWmfG0wsg?u8oy)5(F>);X)WM(XH6U>6R z3rZ%-x>-2JVW&)0Sr7v<)?N6dFFO6ixdBvft3~#bzZiqn-$WN`a%v=C*Up-YOGv=? z(o<-+1<_c56SH$O(9)9OU;ut84yL5$7l157`vX!s7*vBX0kY<5ZEc-VU)fHyOBtka zDbQF{C=#tAic1`6!!qV}nR(n6>-e`MjcZ*MS1{|GL!N2F?0^ESP%j#B}l&`bQHX%VRpk1Pl^-YAhh^4~vsCBZ`uTtufe zT{;tejwgGWj5(8wQ6q5^B6Ba@PsZpc2W%5G-Pmlx?I>NAfG&mZtL)Ox4Z|N^t)r$A z0OB;~%L85{-_TvTxfuj`gx5@}$;*Gwy`;d_pbs#8RhJPG5;xcy5ZXWUvPxKj$`{B~ zyY)^5O-CQuXB-$n%-_6YjcJBg*h$by(^mf@oxa9STjy!F4FRe_a7swEN7mkXy}FuG zXO6%j?)93$%@-EBLQ?jc&`xbr*E2nFaf#Cm23M^=?ejsbD+>j1Lsp?GGsjKk0~1nG zxkZ|)T~SHHZotpBE#;f(VgU4fYwppr0X%9#cbIPry1VlO=XkAfMow1t4g3$Rk{Y*% zr59oENy49aYQKNq?(_)_08%hx0is_bV$TVT1(a#+2O|4ROH1#2f7!OJwdCYoS)k>4 z&q^a8AW%iV1&VHfY{Gun$Ic!ggX@r$G1LN5#`0#PtN<{>nP!FnzsJyovwGzztW%IT zBfD$(oi%%xLHz-!@uBRlS+jA}r-Kpz36FpJBm>|sptEq+$^v)?7;kPtL1sFMjqh^9 z9IZyZ1G5Fynp!6d?YlND$ajz%;CI9=HY=ZM}+_wQ3;F5Oc zDWjCN8E-2I5@Za>EM_Y4ds?TZwC`N8UnXKO;ij#w8@dSU)CqQ&I3hgi`TuHbc{-~ z(b?I4$lg_{v0m}1;+apz29$1YZcaVA^Y6*pZvvHQHf{4C^M6yu2=uBDeA+LQF_lX_ z$M;?`)uqh}mB!@^uOLLK9&uXKjF?`~>n0l?J9Ke79q5|VsWZp*J=1`(5>mj66b?e3 z%wr4`iiQAoUH5tTL|#5d`YXfS3Ro+?F^=9MomPfG(E=z>r!y2V5$iC6%0pcq#Bhd) z&4nR`hdCe7qX~6j9O*^?gviYJ%_(Ex`G03OQ#^;KKQ?V{ng(g(x|zkuc|6I;UVXd! zAKLwZ?NYRN`PymJ561HBj4CFs%avTVM&30v*coZ$IkaJJSw050h)2Hf3kg@52!eC7}An zM=x=HqWfg>3%}taZ$c4hHz{x!sf5jxO?v& zxT{&!(eDJ51W_fu%OB5sqMU>n=UOW$yCWROtJI3tD=q^G;CE$izgRPXxw0r1bQ=1+QNxNmBq! zoe6pHD$&Cmwn8Ez*pDBl`u(m+)G1bA*mXlMf{0I zx+m}<_N)&}N~UyG?=O!ZNJNm?_3Gi#@nDQ zprmYkXJ$MuA$>%BrxCCjzJq4zjd1+s--bd@f zHwl@YvVuphuC6#-)|sk7z4P+ETXZH zt8}ZoR1(;NkVxdFx(=kWyxaGQbY%90A*L#AVoOmcA3!7^CAhlOe4RDtfOZ|MUjJRF zQ=OilBY(fA1N@sme|{Mr9uEIuY+5{J+M&yg%xi@; zTvR08k6BPys921E`wjk!74UFxs?YY4xL2d|GuyFJSMmLaeOD)5eT}T+K}@ktoK)(( z5OE&qd~dY7J;VpxOI6dK;l(wod@fr_)c$kpIRey7CmFy2D{1_UHtrGpi z_<^p>zGcBfx8fxtIT88pys{TPs$)wQ5hM1CpUow0A_*jXfB(|Zl)h2p9m7TI z#&7F#Zm+he59M6+#X}bdrOW^qc0kF`>74l`dsQDQ6w-(YNP7BU6M4Sr|I8v-v)G_# zEEiI1_|Nx9AA-TiZi~0;<)XijpD(hjF|RrL2nT0UjLyEK*Tp{^PhE>Dor)<-`(T<2 zmZEQZxC!HdLb2T>CrtHC#-cPwxS_JY04f4F=FX=qQLYGc@gy z_#YgMt-$Dkik@H0P0%~u{P1YCpLM!{!gSl7he9TCLHX@%&=>8T?ANK&BefyDeCtuy z8J`*P#6$_+Itu;zGLS2sLrO&zkO0dYChZUHuny%h$_1UA2pFhar)AQ9@t<%EJ$st< z3l5%<-@k=6 zF)>$w_6J^>6~ZdY$~Y}8Eqov>%O;0bMIroQh_E1dJ~cKr8t`E^deA#0xYGjTqDaBPLpaPc>9-h?4cpi+S4UJwkaD?;#euxH)G zg!R}PNhOcNZmL{`OQ{kK)^%wW6^fWIcK*KK*xHf++f^9(rKY95Wy_4JZ}L>10ZTA| z3IL;qcb7M;2$>P!;DHz7_wCzbaF!4j67q@J&lxvX^scS?_Tl~cr<_qO_5|>IA|oRM z=W68hfkgwdhCDkCNJ0mvwU;C$+Oln|_g*}#8Eu+-N0=1CbicU0wux(*$oX1*_t^dM zq4&L*+tM-7rSkgMXIG9LLQ0R9=JWh!lRg3A6mjyA6^}#z=Hv6w{;X8)z!3lMMwMsB zaUQGICWfWwr7M%7BYx4?M#io4jF#^OaRcmgLXY`dugS#7$C{$-bEFGD=sOrw{W!pB zf2cHAV;w<;y@crbRgLB6Kb|pBtsq#?IG$42DLgcUH?-C`Tl$MCpCKWP?0b}@*UOA& zg}>c8SpHf|u&RVy9yUAnNXJ2Mb_C-}iHf3`@!?oBq-ClB<{7RQL%$+$ zbM2s*rtu8dst&cFaSq?$*<^ue3kTfCs;YU;Y^7yo5G^hVe;HN^+n?esscawA-(zDx zA-V?EHxRW%sQ&S|Uphi5FourMWo9y+WCSsSqRb?yr4;iKgM%`J>NJRfe)yVG-^><; zg*}3UK5zdSF0Po6&|Tzlf3gZD$DI=sN@jISxi;KMdHea1FLQfwGc4Xt#<;t|1H5&!V8`d?3 zU=0NsbHItge(|Ccyi#@)1q&GdIrG6?5sc3f3fmmYqcZ?%8F4VcjQ<4Ty=ut`^A&AS zc|fDa;;^d@IWE#U@|fX@#-DB3`kqGmD?`>b{Dke@v)A3Q%R`9;q$MSTV_LF&c&YCd zCL|1XUju^4K7k(#k^WYkm-0Jr?-oatN+kYz8QJ{eP(_-1!BK2!VeqxRAgC6`KHkcXNW zlP zbd#}Qd0RePYHPS`6q`8*b^J>B0Il);d(6e3{GSRN5c|(~tgT~X!=V-(!p(?@?B1|& z*xAKdPhlU3{82JFiSwUiHo8S3=3}u4H0C%QtY^MWO#-mAHcvr583Iv{VWnq3$jD`q zdiIw)OH>LqjlY63s_D%t{@f-SmBQ0;m%OTN>l&ae>4Bm5<>BbykD|PTa}72;NFfYo zGSaCrUJ?oxUj7oya_{ELt)e(SB++Zm?ZONX56@XKP2j}<_m=IiuTQ`*5Oxgo0JnK- zWPswB3XGLMe{|to2?!1Sm~Q~L6!4iqpt%OtOn@47G@s#2Pv@oIh6@Y?L=xuaMVnCop&|=(eEj@}>);hGCLtk@ zx&iZZ7$tlzQAVE7{QN{2!=iA`BasIv))W*KbFHu8CW56e-QVy49i0?nr@*6aoTnUQ zkMPD~p%Q`!f%PM2usKGCp@H#qUK)oi+=cLC$R-8)`ky$hA3r{WnHh4L3Ed&=4|rAh zIFRoQb|i$TM#jX*D=7SeFujW%rUz(fpO=0GAY&@wt^)=a*ph=>&X-C)`^Woum$nUC zYvBA?TU!HxML1^^<|oSKhT%%rDYU(?19hqpbxxg;jN{-jJ@k8Z=ekO@5LDtNV1Wi< z-GKN+ZMtm*QoK4VHz>)`{gAa9*S09zD~vWc{n{(fXQJCB`!cI%t~coy5BHo}LU>Yx zOk~%WKi1Bg#+Ym>n4KHuNp?8qF&{msPj6dRmn*O(oNje+#Rl*6;k%(Vbnv}oh`=&l zyAoq~p_bD%H%I=2jQ4%wk*ZSI!NCL75PY#^m9`%a-*7#=;z+~fW}k|hNzC8T&dun* zknrCh{M-5D*<|0h1z|GXL50AAyX48sQW-P92@zJcY>}#c1i8mP=$qV5zs4%Xm_3E@ zl`!cjsM4noU2I)*-gazH^h7@K-KG>z9d_1#63HACXCd=&EiYCs-79|RpPZ$7^lJio z%qf3)M}C)wgOFd)$P$4I_AHnL@P z+Z4z`u}=N8Y}AHb-zcxorVL!mR`A{3Pm*S;*U2}Hw@zJm1akB94Z8aU^!}|6^X_eZ z(lPq^nLS~gS<+1lYJYyK2ttGn+Jgu0MjSg{;&sc@$e$tTTu@GPwo;7uPEJUzgh(!$ z5ILd5Eu)>0PhT9aZdnYC%(R~77m<+t3QiOJD#gSeh1c?IWyN&nXE>y3Tjpi_`R)aG z1BC36g^H5Ae7O+8I)EWenzpEQdU4?rUGgP7ARJnh2*p@>&Ho9y1*~}!Pv@~!B(z~^;oO`$vg_96qxGlK! zRY+f*<$HB?b(wVU2a@{d*QX*1iXTfC^NWGwf)~kKHjoIxXyeAXY{pNylR}G|uH#|- zj#HaMt~kh@fRyt_k#o$D6^dcS%(g{K#{m7)voj|9ZRKp~q-mYxk5H_|#l?Zf8@#^L z{5E%huL)IzX2agI01rI`Jo4*Tw4$0-ykhhj2+)j-6oSb-3_umzxnS;1Nk{j-cp{(P zM9(EqOP@I|mdhqvbqJ3aM%`RIdD?kLw3v<$xQ%j@3&ys1_$N#Y+Vdgos7=^ z3?YyX4HHu_>jVsLq1VboEdj4Fx=)`OanLX@;KV_Nf)>ifJTw(e%>hV1Q2d)Lobe&( z8yHl3gOj)e_J`rEqu74^ahe3+Ws`|Pi^vQ&I4fR=s`x9&3h(I=SLA(vMkGC`a1R9~ zkL@>%V%sytzBAhSFv16j`Qvl;f`7J&p;qUOIDPDPdVapdI~4zC-9}keo@o?z1v<7g zRNkT-qJ+t;x$>cEGIPv}yx6u2)+Rr>Tq*O=*DMnB7cC4aNO#GuQ7&*_Bc7E<;1dHx z@HR&@q-ed%pWAJx=fG%IHiRr=qLy(ziI90`?qW!SK3y_IQi3+UVs=5#kus*vx(;vw zVPS=|iQ2v4TC{zW*iiBn}!uN}*;3i8?bTY9_c^UdAFZ{jdrez-Efl938>jHKcjx~8_RN)dgygs4 zs6HR;yB8|M_Hy4N$@Y${7^zF+nTDh8UZ~db>2X&ncB|!^-Op?{U#~FrQj0b;JJA~K zUabG=^!&Zu;v@loOwt5K^Zb0uJpt9Pw7sz4%2u|D&pOpCK3Hac5sQnPxeEF;G&9rM zN$ei%@kOAdq@>{HfChSjCfB{&iS%GEFLKK5f-Pa*7}w5m6Ilf$%Hqb$2>7DRYgV2E zjI|g(^fzZy2V(A1+%K6@+ztZHRb+^8R}Kes#@jjO`%d?lq2+%Xk|Pb&gls%)XlwqS z{9XGKfE`T078sr(j73D`Nj2p2F{9nwB4E()G$`b7a&2MQ}0f%90p zgeG$6@nR>HB|XW>$l}%FsTgR$c>3#fJf6Bn$HIu0^tG=cN|uvlh0YJPrx1HS8M zZsVi&&d#!b$A};Kg|7`Rj@<)pJ@m1`)5F6+fxyB2!$TdoH8rs=0*}xgyVD%Z>dC}> ze0{CP6_vuHqlF>sK1|qKZrK>f%C0b?hg%PBs}j{h&Gt+li+KPyAf0tASH1y*N^dGP z^(DDXaxj?~_&!6M={IYxG&`nI1uYk}kKj0-1T1K0Cx@pQbflzrz`nS}8}(u)nM5)j zN9GP31aQsw0s#T(-wXT&z;)Opf0`hkwdsug_#m0Yt@uoNgCWps6=q=IIsU@e*B27+ z0s4Ue2r>?i7XWkEb@Z%QYn4qwMT~S<=R*arZFtFuAfYAOk5VC_H}t+^<{r;1}1#_y-LQ zDQKUCGs3}r8bHd1qN;_3&+sXXTh0Pr0&VUd0E2ydl1bE0i5RpqrC(d~G03zSaz8K- zkH`!@$TvWOV7Q;SwW;hqWOt{yb|69Lw>SQO=WhI=Si$0^P(%n8{cm6Yq5@#*_M>@q} zE}%}n?lmt;ZrJYt8UIe>)gKv>_)d~+=g#H{1<$K?oo5=IQCUQuGrleXUlg%inbq=6 zdG4Kl1)=xlcpTG280IQ|mG;D?k8<{^q{_Htp9@OVrO6bpWCb%5!AEX7UmL=4?Z5^H z`7a9cpQh~(fWzW+cm#?>Dt(xso!U97P8Seqcw6aoYCm9vFYRH6hTStInPf>pH@=&R zOa3$ehx;28Da`t&cyI4?c?%%Q@5LZOGQ&j)!hh9ots;4gY4yC@_I2?de{8u+SXuXW zc4xN{WjP5gKYZke?QYwK3DEYVp8XA5?g)9gYNw4t2rIMZ6!^r~_;%{Lrhrxq3zp5~r1F3N{RgN9 z4sg+-S{C_7K>(K+Y2fd=k*{qhomS$4{RMX={^2kcFqDczo=L8!3V~ zasUH?9fYJJ!ImiwQiVcxZA}DT3mMV^kJat^R#rO&kA*rK`HUw|THri`Dhni5D$g`O zO#eRn4+a36XP|(1MMc$}-=PfMF*dnmvvGwS)DM9%lpW<*9}U?Pa0T3vhLW1pyn%~x z;o+X6|L4yS`xBgBz}{8}s)l6AJs+b`940SFIx%OFb9Z+S#$`de1h5GyPALK1@56@= z25#4P1#U&yPIGRu8p_=$)$5ys77eqzFoRS&|EB8;B4E3mv^y!qY-EGVw%E1mI@kVw z5v{IDtMQ4JhZt@CZ@LA`mB-Qg+pnm9JjrOZcqVdx$XK@=PibEMmIiG`a?!gG+i6ie zOQE#8s##s@=ugWiW%}yS z)W48%h0Xa9+p|Bd$rKY}pl8hsKGO8l(fRoKAC4wiEanAZ|DY`Xe5Lfs$@-fx`71Fj zy28S8``~-9BE$&7MF);|Os5XmbUMRy<}S|?OB1!>9GC19rC_97M|B%OLAht$zdcnw z%WST)YU$3u!=Au>Y*jbAV9%0jN+w1!X{vv`J$3o-x0C;*YD zqB8CC3d&M#&W$5yXu@DkB6~h;>}zw?j(hriO&5 zDD;iU!KBrnt|!fhf)3FVm=7M1yn5vc{5tq>NJ!EVQhDkG@UqC$0lchWs0wDpo#s^; zPt!vK14-c5VSuEWb#)OWoudPx!#+MfkBNx~!%0OT_#qW=%A*-Oexi1?Fi6~k&ezA! z&z|M$Q-)}0gaLMee)sc^D2Af>Pxv4JoyX;KE;nwbv6}>ib35Zhbo6Ctoi+XHT0Xyl zrEUg&FDWx~Y3DNfqelk2Gc_=!%QXwv!_64}TDZJOgMxTLf%Q@%NkfOvv4pI7=gX?I zt{C24?1+ww-P4R+sE|hIu?TV5{(I|?Xii7Bqk2G-k$Xn|O@}RfhT{rf&QC#mjAz(* z#(=Xdt`2flH} zIAgr5F?%<+Ck@a@(LF!_W=~+s2v?1@x595R4)2jtBsn0 z-!-bZn4@H!JT0vwK_Cb^bp;^7RBAa=4;E|SM>qQ|YdXKe5DChLxw&{QecrxI1>?Oq z)|>-zRSutQtQJL*He&$Uva#{VK(cigH>qr6gxZgphc zsaS&v0$Fr2!SU74O6ZK@C5rJ<&wmO9MkIuXKee#D)MIk$zfegD zJQ_=4kk)(_OehRl6wKE^to-dpYP1H`)vH&*LE+%zJ2eLcr>2s@j14F@_(Qz~lI0&G z7G~V9ot?{9LIJe-t*y27%97C%;7KClP~M~dMZn0A1)D9$vl&@e@M2L*!rfafU(w<;7%VV_WeILrrA8VT^kpA6vyxWt{mqW zTWFOf2pi3YHrSuYxz<` zgCD0K3?wBL$Ggf=4X3qMa!U*1TOU3&(l&m>DV_+Yf=D`2B2;W~j>yeXCU>SoVX>A2zHWvpPsnPoE^Rf&`cG3XlSL88XZgZ1<+HS#P*d@U;iZJC_bX~hf>)gZ5{gj(e3GLBQ@VKxZ0Xzi}4lbgNIH-Xl4|Zp)^ZOY~gAX9=>@eY0H5 zgBn!(U*8EiuQx&RAJ|92`z7beBk_u=(r&Yo9Mt!P3gY!-{6a?~k5J4w637_6RnmKr z&;d%AJk~Pte(%G=XaxmphUU8rhGinNrk!8Cn&v5^1;tPX6lEJ*N~uU%O9|d{ok)IH zoATt=!aL(xe7W{)@$#B)TdfEpv1^7SHZuSIrW>)24aX7Px<044-Z$%D3MuehMTq@~ z5=)?ZTR?oY)>C&ik{VaOldJv%x0oMb#^OVPce|3MhM7?6U`C~e;X_*6LeM~7t={=E zaR@4)+pfGw8+m~3(`iP{Lw!T})u$ZlS4E}0f5uUsj&IEblM47NhlCgyYC|(&iKt14 zgBU}rp|bsA?TfcDDO%qt?IBDccOz(v1*`}WF!DeK+Gs>Vo*H7Z)__maxm`nto0 zt%ECxu($WofvEW_wGI|f&!J?sT$!sXH@zoYZhCoLdJb@}2EWXrq~Y>6WQeivQ?ROE zKiGR8`IkQa)k>P|TM7|lX&L>-R;q=rs+gDsPzb*BB03tdRy~78H`JS#7K3=`;#$cS zA<3H|yZe^@;jeEGL=_|>#a=zX%iwvy9&@&rrPGr01>;7l@CPk{X3UoUKoC(&WmNw| z&h=N|g$41nvr20h1~%ZjkU@~2p^ZIoGmt@nJy~;Ng+BG`*W|SFNx$apvU${@=PXjF z{gC_S-;_yuHrw@(R_2zs4QwYRs<9;F^8En?IV^wLgY%yDTxY|!bh`es3QKoBYfF2X zMX*~=e%7dHk);s>rv9UXw1ZakZ@m^6OS*sIg=ECb4QFM{G|*99{IemGGqG zif)x@+BXp!CNH1KcEoXfh}HG*qYS`eH<08Ymijzl)HHFgx_?xWmzItt+{echec2y3vF3#k1Av&BD)O1=5(OylP3N14#e23fj`&W8QZgv-Zf*ckN?Rh(2G85oL zdhCn#T+iRqRN`HinoAenOh4@Z8WOmZq>$A|dF9VN((HGBkH|x=<5k|dgC#U3x@(p% zpLM^#&HZ%}2BZM@eCs|1dLgaU4K*dD$gEI*Dkv#P!&3uc5+;baS_^|=yenNIggE?X z_0xw-p)E2_34J?*Z`ZCo>vJI@cI;+YOL)rgF=JM9@UvR#zW3v`kil-(EoxZ>*>WsQ)zM?z=8=bKe`TYiqL2YIsH(u?BNQ;u zJkD7Og*F4ow?PW_u3-k^6Bp<43z_6t()=|7p220o$V*G4c37mDr19uAT(%NmYoZDDq8tFJ*5s2}T2Zdmy}gHA@_R|Z z;Sj~P&r4}JaD$&xV~imkQlwgj?b+IsCh9jN`;hyY{OLCX8{UX@Cj)^+@~DDC{8o8d z?ln5{(7-1Of0J)@;UkUN6!=g1rO<8jhU23uTPpEE7AKP%%eJ&_!9sPdU(L`pXo=;r zT(t%I*Rl7=6i(hRk~+0xTa}4b$+Yz5NIIpyH@4ok!Hjuk|DD%|tLkbj9JVcPy0Wtw%9Xzf?5O+#zzR!>o6!@CU^bZw8i1~!7xle5b717CW-O{o$ zv8c;k{&M#ZLv_r=`r=`7OvLhYKb8A8{`}ILx#3e|(~h0%bp1HG7k5x>YamVKS_qf_ zjZ{6mS=&q+5^A2Dz^CaL>66~Vi)$ra@t=&Nl02)F0|wHv+Km26zQ7#|qb^9NizrmC z;}{7p9`X25R`Y2EX2oDkp|MBggyjM?0iShOJ(nCtq2x5mu%)T3W`$+ku(6HcFgkU^C>#LYrITzXy?* zt1B;5mkBEAuhm6+Q;|P2posSJHOQ@|vmjSDetMhiX7lrKvGR}8XEQO>qshE$@+2WI zb${RY9dY5mTHP!jo*Lb_r6D2KA4#Yq>TV)XK#tM)j^pmV;o#b^>U7*jKfypUO!Wi8 zid-i$vjo{Fu6NJ>+^zp`@;uOtlJ5Jhtgl>2&0M+*l@D z6TWDM`+f}9l&)LnT^A4)iHRmwG`q(U_Uir4o5jKyrDt&kYa7L9%lQM2)KU{Q6eEw{ z;8sffc}tGMqjxQGFd(R216S+7LB&)l-GZbr#-Gb8K_=wy3o-qk3N)5sw7n8+gJQjd zqI-D#ty|%)dZMczFf1QF`Vj$-);0Ir+`L4VV_NjM2?xO&%*1DZbn2D^&pH65mp-w<`{K1Ej;=2$Lh<(@;yDpoa>TaO-HzgLe+U3;VYZP3hg#uVu-I7IjL# zP<}{_kp}ByQz#hq(J@UW>Go&XG7}l;6WQ=z%km$zdsuKv70D5uX&V=E(<>e=tv|es zdOyX>Ya0*cf8l1WBVs*XHnpADG^l>h<%35SH1LmzX(!?#o?gndF~*I$%y?Zd1jXC1663-8-Ng!z5YR zBM0i-1b6=}@x66@>yLHTl^+X1`UJ^0bA{+6lE$c|Ym=~KJze45O8GeO&9Rg+p zP@;vrP-ki3DyU*Myt{=1OrC_rmcI)-4yU}jMX?0L%*-BX)A75XXtr5xUbJUcmNOc{%$a(P%>OzEDwhn^2#`fsO zu9>%(`8@>6y8VOpc_v}j6-Gpn0WA!=eSXul!8p}J!p@G3%%R7DZ`bru}(yD8)Ws&CBjHM*UP55yv|y@0LV514HQJZ=?E0PT(+ACq@<-Q zdpb!}00o4|ygP%&dc2UAAo3QL#7<gW8dtcvG+Aj924>W2QY?TaTsn2unM)fx0x5qjeMnv1P)waRX_&fccwtD^t(n$P7CIml30u( z2HSwTD{0`BV1~tJT0GsO$quxP@y$5sve`9OU zs3Gb43p$Sa&^NdO-?HF>GWpY_9o;aE4Ind=z{*9 ztcl6jp+)1uWp#>hsPokHp6fm z9_VzxS=kv9QOgzrOsc|u5z%i4iUQ_(J`~?j>^!$2?Kk&e0KwUr8&N@r z!p;htbZZ|Xa?WKv*#VP*y=El{9ryK{n9$$u0A0SDfL?&U>@>)_FFENZLtPvULD`VU zUVk0{i2hj)$Qg88=JE373(VE#z*wo*xbq-%?-UgE3_s-_UO~WF-Zx37fqNfDZ2f^@ zAM^Lq9FX=}A1h-AAQnI#o`TE_lsf(ALiuYUX zKwgpxOc)my@D?JP^)QC=7k1m)uD4Z*#xdx#w;<2B(BKw`Uh2VzAnLn1W1Anq839rb znaa76b%IE#Z_Nfh#wH<|Xn9P17d;p#HwsO9DS++_ zW|N;d0}B!&)$R|pBVbInH4gd|gb*&XaoZM>pO+V>(0f6gFG4Q^5cK2x%ag}3Oq$KG zh;5*loSqj8t{a;2{T@HT&JA0Xk{(_qZCC>?!&w+#y;Kz?}kV?xxI=uJ+M<0YraS*{* zn6JO_3(g**=Z!o`;L@Tj0)S$V-RfZO?(fY69(x)%0Vb*B@&K_!sI!m{myKf)*0zSm z9Z;Yq!6RgKJ6Izf%7rZfhchlN&XepANF~Az;}NZNv-iksK8u0!BA(XM$0@ssxY7XVhk%!r^DD7!kt(}VW4 zfuW%yOfSQwfg^h>nzjYX(x**Ff)wkniE+52k0;Fo1DI ztp7ySn?ph_>T)}T3bdewl#-S<0A!z2wnx>#vbD!P-rTN2R0+XPYI#_fXtZ^8!4}~O ze!znbWtB|X_TsFtf%MZ z8onx~$H4;u3PSyvdcW(^LV)J7W{CxtX6;6M9XD!Sw5US2E%j{#pMvPsqu=L3 z=RUk(U}py!{d#aDt|XTu7F;mt84o>3q|(Ui4~=t#X9GNL2mE|rNHH=r+(xVs@OjuZ zU_1f(+j^kRKLXQ}DZkbo#|k(ZVNcJR6^fG-5Oz(F7?A>n_L3{W*gLsrAzxQKHBUwE^4 z9FE|N>v#A`nj@)YTsgZ8)DaC_FpSS%yl4Vb(7y>2oNO16S5*n?hR5mGwe!Uigg%3eNfYikPHZTg+SO6(N(r}&lI_ixZ+qIXcwG)C^n3yyW>LwQh zrv(-C$uBQ9FPkm6K1N3RK;DLZo7e7d>)Vs=BGZ;v0;$ZXuno}ppNI^xVEESk0u*Gv zYXKmToWYb{RcLutqCv##@Rj$yG7E7CEGQtRe`w^MYvjTB7g!{~5ma@)f+jkf)p$AS zm}Sw(*StI!yYpQrcDUp z`S<&k0Vbg9^yr5VnLkhuZi8GnSPi6pinwbe8UeZ1W!vM6+di#@-{ocDq-Cc*d-m*O ze0&>dx^~l{v)$!oAmN2Mugf$0!F=6raN-rqnTeGcg)sFo488}5YhGYbNJYkh94n+l z?y2<;1pr?L)lOvrg4@;EoCjYlc#t)A)&FslUxNl<^GA=QNv>lWcx3fS!Ic0=4X|)< zIPtkGM;rfS$P|pN1ERoov-)ty`tra!m`o@IhFv+m)>ppegC9aKzvNGJLXWw5rZ*kP z-UX0)r0X#6j0cb*_@oXW5ihiD zfCB?*4Z-gtZ0DNy6Q2DcLzui^w{W{3u|UePZi@uQ_K-QG>?ij zykKDA>}04GA`wpagAwCJ@%K+{=06dh0q^Wj$Rj-hg_A!ZwB5<|$fK3IeJYi3l8ak4Zv>fSA=ekhPHb% zbaAsMcJgoix(-CYPtNx9z19mvzvbr6S07F)Pn{kev4BeiM*@l|VJ8)g?Q5eYLEtw3 z^z{Lz;kt$u#ANQEg0o@=Q??+H{ioeyf=Ha#0gyZvlRrnyZ4kK*;LrfFN5}jYQarpS zBoqd?EE2o}EsM?+NO14&v_A&2y9Y3e=nsex2n&OQ(ZLX9`8$`CmATDVl>j?zD`nW$ z<;B$HF55}e@7-&4#}e@l$4lv9KR1?PVzdBwjlr9AtCMg*oe73Zet@RQ<6|obW)SNL zoUQbaA8$H(NJS!H5t0*+7^ltylxVS^)cYyxkuKzf5P*IxVms1iCO~)zIXR=ptZS1Y z0d%cK(DL9z6r7w&RlPWeAr!WUEFK5$1_D1Z00&+H#3C3M8@RC|$jz*z3; zsB}DMd%nA~;|%)>bhE5YmU|HQ?-}h!OwoayoSVv0IWJY?)KUk zROLyQM~WY?Eg0}0>jaNjWCDj6gnRbjz_AGlzcRZ)&<0aC-)g$s+FnDPja1-PjCqs0 z7-V;~PP#8jk+XQm97W;JWl^`_w?W?HHU&?SmoM=Fpbb%eHfk60Yr&t35NduF4=P{H zt8$+9zDdaE#0a(&sQ``JwKKu~v47g$2_Y>ITi8JQX$#AZ9H~i{MG`oU!^UGpu)ZMU zS9>y$&+WYR5Qc*g={lqs1`{c}>15|$hoC2;xBMpqmw=#1ujw}8R;+4{jbPq!r0UIM z>zZR4DDNT4TYw9KFbfGAVKD&A#cDmt52g%~S#lvEUB{r)(^Ga}izanlA_ojIR6?#y zr(T}XB9bz2SOHr3sc)GjUiLejMsiXA@Q4WVFa9@teb|=mT1eg?1%N_}5jH4;B9##! z0-AwUrDb8+3NE>FS`Q%e`Sw_QKcOR_EPnTU+CN2S} zD80p z{>1|oA9%}$fK>`62Z@evqXH00n|mp7c1>&R_ZgO;Bti zqBdVpgn>Jfl#-f(k44rx_3SS#K=@}@SI0IlidJ1#t@)T4Af%p$^V|mE-wv=98bEy2 z^b?9qe_Q9@s>qzzj%&&a!~6(?FTEv2Xv1F2V)XsD%e>8&WP_%vdo9}soa z?A82iuOOS;NGZ9=ensNJ+wOcbv$rWalREOXi#**t-7b)`l_*IwWb8p1I>&4C&{IJd zqVBr=qLJ?xk1ywImmpU$%ZL(m-uiR_{v&Vq_zXka+TC62GT?HH%t2Q-)ymww53yoy z=Ve}}Wg7Guy0G}`r!}5%sP`an-v`UTQr5n>3+H#Od*!g!eo_9ss`j+X)xf|&y?e}2 z`y7nrYPZLRePw>W0$d8Dj(e(b^lltRbT96EU36cnZ;n?KU7Wa|uj&rLfNUR@(AkfR z(V?1`wcVSZhgDGIP94lvkjN=IQ7z|9SGJ`?JllG6m$1P`!1Z zPW2u*sc_!@OnU!5==${Wer#KkrP)ohG=8$P3-J}x(RlX-OdCf{)_T=?LfvmA)+Hqi ztc3RYSnc^(Wp1uqSxL$AWA~XmcSgIQlp}r)6-ZloH+Q(>G5cjQw zfffUlxP@|cU6-lXD>stH1R$1mwXm?LD{xE6y8NqfAqIE638DY0EKSjLoP>nL9zo64 zjb!+Du{C?t+H|;rTZaZr7g|5BkBtlu$9b+~XYC>1u<9vjE`$`P9bpkJ26WE>3d^Qs zO;)n2k)8^*8ac0ZbKO1x0f7^UqRgCSoAeaDj@xwy;dJ*ecBks@9OVx=#hTr(Axjdf z-ST_91YBh25C<;NrZ|iI^Ufx{f*Jjq{XxyjtgL63Gr;KCHE(wkq7dXtji3>d-MmsdwtA`N?2v$q7exxac$4S^U}-{r>Cd6lcKEz~DZ z6fXWMIB+E<+k3V1criO&Xl~e!cL@;~m#{~78kH=%fCvF|Y@%|C^4V-P)Q`_rfqVXV zu&id_he?3iDYI^kEJehx()AOE=TnCn$+v9pNb%w5?f>)AX=+TiSLd#FzMOhNf4P== z&Q3!^vl3g8*A9g zclLDZQV_8?$LT^R>4|wAl>bsgFRzwdSi5cqTX6e{*Y1-m?hv0Y4tbpq5fKtj-v{B# z+U@(bV@;{VZFzbo?n zD)#^THK@)1_cO8gVbAEl>G(ioTrtK>=1vYGY`U(?&fD=| z2#fCUV+}dlY7=ztZh2f@7jP!GqV<0b>a#J1uhm})hzleZvy)qe!pqB4IyJJ6I6dRJa^LeZtJEMyit;3>n0GrJ)9zm5~rNY~D>yMX;mN=h=3@p27O zUq8G&c;>d||MQ+sYvX3@Av{}H+lPKnCSvJp)f%EE4w1f&GmA>(FU&#Qz3ouWkg<^S=vE<+L`OR_f^Bi#^ z?<)D9>oh7WIr5=`85j_U z#4r9tgtIA@OAeK&FH@A2ZLKTMxclhs0n?6JqP4F67RIu-T@JfVPj8|_$#h^bTD zHh4fUS9P9s0T@D&q}FTK+A{zg4`cf^j*Nmk!L{eHvMJ8@%Hq@y&WW7fiERqHrW}!c zUUm8$9PO9|#DK@&YEKwy4y!knvQ=Un6V({Ij0Jbw&$>3xHW^$Qd*cSD&E)d_vc7PT ziq-OG!;`RQo^uKK;M1;BN3Cf`Uqy^-l6zHBf^l>XzdHMFq6Xz&iP(~?Msc&n=NNA# zvaOFeWx4k;yZDE)lJcLozNDKc(HhG4dUdrpf197IW#31^E83L!od|xWVTaat&V6$e zAFtI>FUR#qMV>bg*WCo^-iwQ>#dyCarpx#tN)uraCpq3+jBe8N^mYZsc?Kk*eqrpVg*F^w#@2jtmh@{q zJCh(Y9iROGr$JdZRF;*Cqiwk4R%Q!(l(eB%OH(1!=K?gQbvcKF(%!)dY0Wox)+cfs zBNfs0w+SrVO(J{iY#&kI%-v5K%Ivi_bx1xQNwh+3g4Ilp&X zl(r2kRotzs&?;LXU{*~i-ah}XnN9$axp4MsEmqCuTqP!ydgh??)VTZ8kj%0`Z5m2Y ztT!P;0PIWg+SrE*j_KnB_a^;Ul_HnU5i-h#d9u+&Z9KS-NK>-5-9xhKhKRM#NuHZ< zj1;!mWOgh^P{c%# zd=KsF+ITYU^L1|0zU2q=N`4D5go#heKAHvU5u?^D&dIu|NONsxqB%8cIG|9nN6&S8 z9(LPm=aZr{6Suy&#J@c@e!5-lzM<=3)KsTlp|&u;`Lm0`>y%f%Nu2$Ojkj6P+Z-~{ z&-Ae4FX>6$cWV6W;s@)@Bjpw=oJ`|zHk0Q6#D7EACN(@8rK)haI^^hkn0Z3XkimlP zXPOXKDwRVm^-O_5z(d=j+6|`)(<0qmly6(bUH+5PiKLL%xtCX}NFJesV9a*Hz-8wB zGdFq}?fhQ-{OPzA;nDByi#MZ!NLnaUUBbO~_-dUd8ZO0qGO)X3gud)7UXmmyKHYc1 zz8JTD?R7%-IYCo!sR3tJbRNR+n@>E*G<*jIVM)2!z3cn?abuaa|3Xe}49q1++Yg#Q z?1*8p;by!#ioqsgtGBwDX1Yk5Z6J|t&=TXwx9(Q%XZpw3OS)X2p_7m7rl@VT!-qu| zm|TQ9n~d1ROrSoM9;D!@XkWiD3e5~Xe7s9ydIyRgs2^J4DJvXE!Iw2KR+JyUY$7g{#w zjal8vlvPD~zF;hco!K(a--(Y&^p1XWH@Ro0Bx{Clr%5p<{R}oGrb@*5J~i4&>v{Yz z?A$j%ilgb)GZDhfx_+?)8DrE|S)=Xq`}c`zaL{!K)!FNameDUhe00Xap=$>ny_xDu z=0Vz&@M+oix<5qHe&ihHmoz(S=V?{cx3;1|!Es^4Sg1umOh5vH`Y8HNhD^n{2wXWn z(sX{?h!6T+VFy}yeSGrq+mD;@vNF=+JekuNLO{bUv z4moIv9Urv_34`)cp%o>5PI*GmN9v2&9=gTmK-@=yq4~a_PEgq=cjp;Mvz`TUPg41( zIe*Q+RkTgTOpdQ+YxDB=i`K>8xB|4T7jd`Kc?lGi6Y*bGmDVevN{fRWczo6nAx~EJ9X-RnGU5{D*GYKwtIP8?|!idIc;K6C}YQa(4T_sWLt{ zRNv%^lol^w!m$+1R;l|PN2QVeWP(MjWk&jvGqdH7!*_FxYc+<%HN*ymF?`ac50v#y zC*7A>rjAFk2y56RGPLS;&*qbq@#jwyIvhgTpFOtO?!S`nacSwfugl+XJ#3RUC3vE6 zu4V$yt_kbp?n?38(~N6*L0lZa4||&)l(*lvxhpcR<0C9nOZr6sTI>cz>PUG8s?=0Z zHPc>&4?OH2moYol$Mx@4d{Fz|sePz|gibWQnj)j1?~6jM~7f4L(*_d zZ*?jr#>Dwqs<6l2#=T8uBhdM@Qa1B%XTk>Img8L%orFiWdm1U#tFvHSyN6V?gyMqP zt&al+iTj?vle<2q5{D5JJ1(!KpzP%FJ4I)U`0Bp86nXnYJ$jLa`nB4;Nrkq3`T=ab zk`dV=BhGl~d!@LFV}CK7mtT@5;o0Loc)Wy$>Kzp}h`OZySVNoh6Wdk8w@P$3B2?<;S##@vz|ifK<9_KrBj2lD4g3AGyUKIRnvu4T@P2Ep z*ESzs%u-KY^eWOwWNvA1W^QVlhvXU2YY)w>xe*-iUhWMg@i|3wUmSeoHeBAy=e)fT zdSJ?wZZG1FW|C|BARsNx{A^#>E769#k-ghJL5!l{vpUJO=eHX|?`}?r?sh%14U<)B z3+5`fVsX27J?al###yu*>W%LzQEkggo;9R5I+22{|?GtBd*Ih~Y=1t27 zKdZ~b^68_W_SEekdMT)l7sQnYINd(|kZoERyc=d^NrS6Rm~>oY#J{=q@xnAuHkP0t z=c|+id!H z8A=O5 zKVhr5h<)!c?w7oFmGfiW_Zxo@L=+O;WGH>`J5Fd;%BCT8TVr+F7+23_qkow*YZ{70 zz|E)eNsEc5Y*_OZow&8~d)<+~-zko)dZp?fVaA2p@saIq5&W>0lh_`-x!L%5y-6et zTYuu|nH8c}z1z;Z`cD>E_;@Mah%K}Xu|#EW{i!ew)o!10E*ewZ8jnts^phLa%MCK_ zEY4grgS~K6w=Ct)7QHfGwE0M~-oiA<&RXFtbUAghCxN{|iEv=sd^ z$?Cv`HA-DHmrpQppbDD=2R6;ewvSzZxt-Q-1E;pdaF09(aSqq}k@$)c$-{FJHgXXD*Ju6v@JmRl6B^!hm60~TgMN&2-qevDyf!l=rr z`H#WM!Fa5TQU~kwQp|N|A|vosi^=hpN=Jb=va~e^uJM^4l-TFnN>cdTejKoIo>W(1L-7NFZ-Iv?**N8xqt`?wGk5Y++EpB=ECw?~-gF$?V7NHIlnCsXm5{l4m0&bA-ikYGtvccKlBe#)`U~`$ zUrO2BY2k8QvBnM@qRhH_ea%2kJ-1IE#cp&t^|k97mx-zTH5Sh3#ic6M;aY3T^=yMJ zLuZCb&fGS`ojtOuj=bw*d2WtVyYYl6dP{#=3Xg9T=^sqDG@EJsy z$^EIS2ui83Qp%OGX#b*Q8&9mj-s2S#;cZ)}%<~8@>-9^8(*)`4`mJAtGbZol_*@Gt z@67Siez42#7%q{@Y$2H9Yi7aF7)>-;wW7hrWIA$7->(RIsiIZ?>zMqH@`!k9>T6=L zxqGfp-Y~hMi=f#u_<`=8#fXUY=85au%&44v%JxNjSvfgY4h}KF)E4c9lU}9lMgZGz zJFalZ$#sG@?+Z_wZwmB&mSZ4+(mOULLxp|A$NueW!hL)1&C~s5`4anvvt@-CLj;_P z{ox2Z%=j${dCjt?#-U(StLD_NeB7R+JJ;Ctb;5ygzv-SGNX&oMeuJ}7bJGyjyy3Rr zH4v$rg>Fuk7IQuwH;{$`3{X(~&6_fnzdvtL4;lZQR&x4DUBr;XYdH`{pQN4T9r=xi zRcpFl>_gztQD~G>aWmtIhKS0~SLf<^Ys?9cUp!?r_%lSD$#Ac}vXc^h-TV)Ato@vL z^R9fu6)uBEgJojFJ-9j?+`I0bCN=fFb!#22MaQ^e79TCw@A2XskVH7XyN}jYoNp_D zHME~Y?d9lE5zpomJUV{(n&y4N*u!A6SBu@1IxNx#WBBnFDHdFSX7jZe?H4vyW%p-` zJDpIu^ZRSqb3c*5m<6hoMRtxTnrK4G628hmSrsVatg5s1ZSBr^s#BV|_qq=&Mui$1 zxAsDWz6?eM8R{HQCjQ)=J8YGIGCAJQf*YasE~bm-K*;?`>RCxxZ{LtO2Yz!QlZxZm zwXC)0t#~|d)+mIIQ>|?vhv~ynPqH)YEio_IH;I8=Xf4ou5wJ-U^PbVM)gZIbef{!; z0khif`%J+``I|eJXO~A0+7|q1J2deWF1KsXi32=$1YINY#rq&-G{1hi^6fIo!2a{e zxTA-|KG$U^uE=-RQpu5j#3o7s8**3B&{^J~dcm6C&dR~P|N+e>0p=a{sm;4urUkh=xJiOCPjjJ|VoGsJ2$ zV=06);TDME&xDI}TipBLj}~AlOWB(8q89tx)p+kg``U1hPjYx1xB?6|%Ba%n98ahV zEEAWP(Fq=hDlxtsF3!ejXETix?yIX6B@a3%rE%}pcc2orEwUF)^wEe>XL96QEicJb z%ClMDL0fKP?6l0Lz7ukk(p^xdpvcUegsIjpvO=x2R?_mIlI{0l%?0*yn?=JaeZ~)q zpfEvO=!z<&zMAr_V(Bn7xN{UJRoFfc$gB{H#i_-uDV+|nSM+=*(6`TtMIDV86~~) za>Cs|4egm|v13WNnG_pmO!F?k24CWb#(In=CHNxWg9j^9W{y~luYle-eo!B+ z<3BU+Oc4+Sga#S#{mmeddm>WOOr>m&pHriI%`hLtA}A=K(9IiFS)KKZ9&fFMD6GZQ z+B!Wq_nct2+~~c{sgZ`0P-tScgv%_&@2%CQr$#r8B-W>-eP_INKXL5gryV3Weop=? z%?usx1+YEoS~?enZ$CMB-h@#WZW5AYvK20S2EstI8;z@SIR48%Es}$2Z*(MD4mk;E zWMt>HmF^Y2u4OVH8ZRSnGv2gn;=+5JD#QN5=9~8ng>R$3o~TS*2-7{;xdT$%LE_(e znO>h;f9=gqsg?+8XpS4XJ&XEUz2zot9>-jnM9RRKcOZE;$r7l13V7U~sg!?LKTcrR zWe1iSXN$^&k*_abeaAnNqydHRM#&64wbBo1=uv5`d*c=Rb+|3@fx-JC6jw%naggJO zydp-8w+Nj{Gkt^?R#PKS5pjI_z9V@SO|aAwNY#Qjc?LrKRVNEJCJI9%ybEruelPaV z?uGCIJwmbxEj~ygppSR>{b?hgF~ORpQ5jLn1Z%%_caH%$H|1ctMZc{#_I6%E%GvF zP4cuXzv(WrQYMH{bMEr!ay@82@yWjNb$?FXe`F}pE3s)~uJTax{6Uy@;)?q9!t zMf!@>Y_baqb`1}6r!1-tnY0}a2GaYzZjrPQlr-JLwem!nYE$(s6u-tU+AV9}SjU8} zOzeCk-R1sbz%(+;65lb~cS_*pEDnN^zr0TYJa=7*_Y(gHy7(iDS4{c>s+E83Jb$)! zb>W9392rl1;=l?gL-%gXn~KP9vhb1+pSBvqv`GyK~gP^QL}~nGyXcVG?m0 zugty9j&`yIRk^kZJ%Ot7E$O<_tBD%*7FHPSLh4^jnul1zneAWy9R7h_aSi+R$Eqs5 z;-=V1-7Tl0+3Ai_xswxK$&zL|Jd(hs+kPT`B5AUMy5zA;?ED;Z+#w~G6 zHf8lQYBu&EW{lmHsp^nbj+8;{kZ0wa5dvqwYn=n3s3bBo@AVu5Y(#K@%OdJUZ|vpO zCas9WN&XN`^&{t$)d;yom-D7K<(Iu4?^bVUZlI&CR4TvlyUT#1f}8Yrz09jiKh<5< zr^t{^?xQ4C{WGBxMc^Czy|uL!*>~S#iZ->Ios-jV(dCV-VmOu$o7|eg&FuABF~iTP z^*wH%81^}h?60$H*P>rXZ>WpsZEI|^-u)=5$gH)b+sn(2_&P&&1^(?u$E??Z^B;y$ zlW!%yw~VBBm09Qx4DF8Jroh6}($IGjc*Pb)LCW@FBq5`+TUhYnR}N>F!Urmj7F!pI zw@3B3z*jUjl)cHk|7+XFhdwq-x^7`I==p7>dqGT=ioDndJ|o)1;v@UqLW+9as~n!| z=;Cf23n?imGh;vB|8{4%Sm2WL72^-S$Ch*Y+LXV$#gW5LBwi~|j9HZ;k-=`Jo)A1j z?`XoOYD)2EZrFVs#?|-O)!DW0O2rXn6dl;km{5V1v++Zt7=a$GFrUst$&v| zD(l|O6m9BEcl7QSJC;ozPzscUNpvq*)$EZ8g$)w(84T!d9-B{Djs21{->QD~u<_8B z(|LftRU>=l-5o!+j_J!H0+sRM>pllLLMo~~n&S>_{U_Dt#RuP%GaWf)UVXNjIUmY# zJw~B)slm*gKORVy!QkvOWs9m;$*GJxTL=|44_Rd5TKT$w&UHii^M2BDnKXUW>t8=l zzdCN2nd_LP&W+BOm;WwLr=6$zFR=GB%WFO(;=q7$r?@SHYOKihX-R-PgEQ?Y5!+dx!Neribj@%QEE| zw{RyrG>Xh#8O5=$_L{|W*0KrD{Kkr}yqm71b-~M?fxGRTM4Rd!2&gitS5860nRWg# zENws9mK=?q@TM%BImdfD#A7hM7AYEJqmLQ7*@M!z!z>@;6OwZug}37)ZQs)QP-m3Vi;p$F-P7@3I#%N`0WC)!&smfv zy@=3IOsCCJaa~=qOjAGxfDJ_h5#?Qiu8-Y|%NrXDljo-(V+lTX(s@_TKj7I{l?NiF zofqeaKim%U5f~O~A_DurPRzbMoEjP{Ic-zoI$ngYQ9oW_10xhuB>Np&X}`@Mx}!v?qb~9@j__ zv%G!B&-Ea66AWeX*o!v1=Leqmq;Dj;TbqX!q?vBJ7>BOfJ63i?x3Q&ke{LF&Qu;zc zb2`-YVYHc~V0+Kv3CCg`M^1WKC2`!0{>nqX3>6lp4h}gu8@Pr4A5CW!R7cxv;Xu&f z?(PJ4cXtc!?(P~OKyVAXae_MpcMlH1*|@vAp7~DIe+9+GK(S}0-|k-PS+bO-3MFp9 z;)_Wi*TR-w+RKZ*P8+wM#S;5!zTR@h%+nd0_MkxvojQXd!~67_ga~U7j5p_^aVzYdT%Hm@_qhBmSF)9T=!--}j}to~}^# zYe|WoY_k3;7bf91uQU75Tn{>N_i$e}(w6+YsNXKocdHGvaf2;5sj^gT<;D#wRF|gR z;c_>SBGX9e^UO`W7mKql`ogy~5m;=BP-;B7zx)7vtk^$k%y6UQ9%&IRII=YHF~72E z3W})D&-nEty8dCGwehI+Qo8f-pl1SR^<58lI8Vz67b*@L1)sEnum?GZzfU|<_q*SR(uqW#;dD9 z*a4mY#;2(s4Qw>_fqnBNSid%&rM1znIBZ>VvOaDWeBCk)TwFrZT;^Jrei-7wLs4p) zlEVw(_#GZ#4+Xn;$K(FUl5^m)+0*HrGc_2E<8$$5cDmL8Sx5rp%x>C|jMdYojS&j^{_3?`TxvCgonV}ZL5#mVpDDDi(&_F>D=V`B zRG_$wj1$1*M3okk$K{$b+{F3f-494O2Hdu_W-rJBQ#%7A$sO-Q1OV>f-|}+Z_M!-s zEwo18*59no8SKJ}m-)x8=7RF?1*{h7+E<=Q;GBU8L={3l#LnOvPv%ejTZvW%bz zg^3~L+;|6Owxqa3MttwUorfua=-b}@pQ#cE(@U*fW;Dl*N-Z5+xTh@%zu{@@bi&Dp zQ0}j@gC<^0r^KqmXEL%GxZ`}=k(6pk@wK>l!kkY2Qr9V{yK(yLS>)mT-of^5`&ApQ zq_?L_YzDJ#X%VB)M)d9HX5g2&;iDPILLaNEDp(B`B6l;lA4J(R{H~roNam9v#?)|= z>G0#@D+H=1k1!dW8nr&=JcI=*49Q9dxCCQtg4rb{B`H9ln{m0>-2y@h&!j#wOn!o- zI{nD*ui-=MOy{M7<`R!Y)!0oTL~%yesOnk;3*xb&6L`v;q_@l@at9!_g428SeMA5xOv=#H#;}O?3}#ybZ=%pV{t@ObLo98{x?H z0>>n$w@Vgbzbzu_;n~;W?#Jeez*TN}JCdv~?=y?ehCaj(r(}Qh;LuITpsDYsy43Ol za7K|^d^-M7zVG_P$k&*y-{_3D3y0bsL!0^FiJ$o;2u9=x#WBS~8^JE$lpgJ*N1i!1 z^x_GHU9%Ksn3>Cq|GK2zV)?~C@;n!)fTcSlley5lq(4dLW@`JB#87Z-W&{le0a^|i zg28k-noUL~a_r^pD+nR`5nnD?#Ys=+xb=PA#TLR(v@ZbQJZ z=l$6lfw>yye4`BY+M2;qQ|v-!;*2U#qW)94112S)jMY}R6ZCUvTV2DBZaEyz9KHs|59*J- z@hlb>tp~rQpoFX8YNpFP7;b}vP z-=IYyeR0GO7p27n0&e_TKHlU1wvFp4Ks1%Lw6l*AxPbfzQP_0laYr?HI5mIQForlV z2{-K7DfV6+<(UP?e(cTQBbME04S~Tf7Oi)<3{k;V}x<_xVetF+)ruj<0EdN^_aOa!Y z8#5c&G0Du_ZeWw2hZE6aw?$g#De7TSRH(SZOq5X1ngDYT_W`73UQc*_bl1)fWUz05jB-q*-Tt0|7j< zlN`mb^q=Ypi*4#py3X`@x#wz=ejj)PY7CRvd&5(hcoL}VXp(jt{APwc{U{S!#Ic^a zn>g`rp9hW)cY&5tI?yv~{~ojZE$AKQLEsNTh3@vehk(%aQsDc`ML@Jy;O%)i$<4%U zsVVV6ERM_jd(EX369kq9M|hMpD@a#`@eK7#u%5($)JzfL%19^^MDH_4&@nyX)7M|g zgDnpvg=!}~E$@p`sHZ7CURIpKBxfncf$d$1RwbO(M!kzl0i%FKBd2=P&S{XBUHL`f zm+PzdC5LdEZ)Uz}V%E;ii|9SK<B&`>Yt-lH<0o* zEh96FG*O|TG5+F-((=p+D^?Au=$jf0(u1IaGaWODl4F<5WnFD5GOay4cMIdY( z`m-+m(ra^PRV?5LQlU>d!P?DKQ@pcRV!$6P)_22m1uK8Q* zYVg6NQTobrzrT=jky@*x3H(@ntwa0PLv;(<7xrJ2T~{VqWwX0XU5JK(mhlZM%A7J4 zRx{qG#@pFc*~)d-m5Rv_H{UDZYox@Vwif-+vJk|~oNt!hp4}p} z=7~RtWvxO2rkfn}r0KsA#OA_d5-93avNB?N^KyBC) zt80j3Y|AZGY1RT_bls2Z3|%$A8pyT->3img?nJt*;6hY?<-KfaAmWV~QZ;VBiEjY^y z&(ltlur-pw({;oqB8K!H=``2FeD=o!!@>4zCO&q!wwP-JcSpjD@#qBW;iVh9QW)u zAai-PEiV2~jhS9~Sc`=jeWBqw_d!cmj>1iHu&N;m?oOY1PFcv9dEK}&bNm(${!ZTZ z=Ch{hy{mFx?@GQqpZH*zu?}^LT$nvqw3-x4c{L{^W0k<`bm;AEb1D4wVWDaBLN$1zC@t!M%gMI%(DQ(8Qk2*0h*=Gmva;k zV}lnZuYlMc8^Vo}Z5u+Z9oyIob>_JrnMd3c8`18QLZwiH6f4_Ytl&)%rx=RQI`oD+ z1z4Z3rVfrZBJGa8II#LH_v*}Cd0*(pjdXxpXv5X)t&wLJ@dr8fu@kQwhweD27E5+* z<>KPC1ard`LN%5sO$oHec!(8ip*06@%Dh!P1x9m*?mQZ0BFh6wruewT(YTQMn#fJ@ zmWunh($+Uyx1aSh8C+}%mYkK@BpsX@;7sgHltnOpZ3zn+nwB4OF%+P)AqCJ3YSnq)IX1Y0yQNHJ-_v389i+Zx%Tcn!<4XV!JN7Q(FhMT;(e5X zA_)|+2k9nQyL?-_`eUrca#+?eHJW5?aydIIj3LI!NP=QfJuQThVxEjh`!~*vHW6uZF=cVNIcz^6dI}Gjnkiil9Wr%DLt5+c8%{AF7_XlLq zCEUtL!5=BBh?$bE=#5UCLODWEK);iPn0c)VL&-Qn^MHk$UhDnl4lLdLEWum=^x{7&AbE=1+H~C-9Y?19 z^3Jh)*qFM-kSp>IblOpYeAlU}O8}wr&IG|U@^ilLd07>H2OfPoWKxbVSkUzBLgP0uGJ*>O3)(DRWIXTc$_(i+?K;y9}CqT@{6#Ri7aXbhpY?D~rzd zqe}3YI@SLR6o%Qbh+IJNjtZBFu_Griu)44i`_2nnr`o~t>R2$dSjAtMo}#47@V{%+ zAVK+6OFAT{rb~LUNf^ne8yK*1HT{6si-xuCJ=t}0ZTainrFzvyy3^RjD`8}3K68_I z0lVC+-v31U$>=r^b0(p;DUS4oi-u)Z^!wepNU9Stk(IsS;NRzAF;O=AtD`kWe_ySy zkt);5HQt$Cu&(|$hq&Syqm?h&c%M|hm^Ss+Y|Qr7oa%HQJp(F=1>M5VJi=zSkcom& z5gAy~XuL|i*_E1)i2;-lhN_u)Qn3@`H*I>nVGhK984OxAaj+>@y2+v<1#g>hYdVDr zE1xJHMRbZ~1+a_Ms#%0`s4`;ywRd5u&jYRk)%78xfnuA2s#c^9t`(>BFJH~;wyKGf z6yd4+W*>8e-2{O_z9#M=iP#Hp<30%H&h)}lhnHXzL7W^qSgaLK8cVcuoz?V)%sk;t zd~8P!kW+%toBg+H)kHUOVx>b)$q|esi$XkfrF1r}225qDvd{OBL84X-)qVYSZX@TSmEJ z$_C1@%wf(moYcmfc0SxX=!1E^h+8CWrYnLw0PkdbZ2Awx z>AKPBjV^T8!tNTnQ+(F;MDLFISIHhqb6tBmkL$T&L4oPk$W&)c`3XL{iHld&32j^$ zzZG@=tsF*oss7hyii1`zg942!@3U>(lVsL~k^*;X@-f^i4dBR{ot+J^Nh6R+dT;!> z?i@@Xsttat$tKB83=6%pD&-1IC>sSJK@aTw$K{Y^z^1^D{kJ?XOHN#~VJDP(aJuZa z>&nY9J+nLlSafBm(fx0OVR_u{8KmR8A`V^#3Rr=O;^1U9xQ+p&K`-kk&7UZPnhq+K zR98=$oJ7{u(`|X^>Fl_fBn!M>nUbMSc6a~N06eQ~8Hm*rMBi7>^-TiU5N6Tc+I*Oju@=&CBTq{J0XD$${{ZSxG02W2<^(jMz-vE?eu*7Dp7;3Cur z$X5v?!_B|34fLvl#i-!uH(Gw}4XEVgSBmGOo0AD{SZ(FcuP9zJtJ5*4qcteDC2_$n z(0HF#Q!j8!Vpn3_{IO)v+Ip19Q42jw!xY5^9JN2rcl#jF6;TQA;&EE}45ae0yK?bW zGMmyTa;kZ*5_m7BME6g|wIdq}q3PGh1;>y4%1M*kD%dqQxgVqq zZ*pVHjMeRaO;lCzi*#;pR%S7}J*`Fm9{%@Z+eEAkE^!}stC-f3<41k@jqAj+UmUbL zkyVXo1+=5CxBzkG;^_W0BC?Wkrc`bCcxCEf&D+9V{8ZNU%ZPc_(VyY@iXD#TfyiN7 z0_7L^)PFS=lYhD&F++^M#EWteo7|ko0u{SZ-(W(|&wJwruaisElTV9zbn8XPYF#j1 z&jkl*lFR;xYCdV86h|uqCu>43hmp^f<-~QYNz;PD+5-0nIaj`Q=~X6c|A5;YRYeG6 z)`E6tkYzDvbyLRpa>r-mg-mv6#%x7_>oM3?SIY#P_iLIgzYp@Y6MofVQ^27z;x!3* zSz+XDIUoNN{v`B%`Dy)->SqMJ;Psd`iV#*Mv-()NigmiNH0#1eM0>8Nvf;p#A;ejT zR&Vz!-_ZUr=}hP8HmTjWLARC!;>`nb8oaLxg^j@$nZP0YaeTj%@N`9N6q&i*0Z$H% zRk4YK>QUP3A(76{|Lqc5>MPZdE0v)TcV9mN_;J@Dn^H_Z=ne+Vx5UBCUH6gWclnlA zUSOm1!@j-CYiS6HKzngbu}lLpN zl4E}1t1vJCe!{I(kbp9@(f36zml2yyLRdp$3o-{}F!Di)De4C~zET znGYBj%V&95#W0`mo3|gVc^$Q?b09eJ1V?S-{N{?C@-P5uxUX;2h5td&HNZGXaOdl+ zyt5?Wsw@EjrEy2QFI~z^Y3*BJf^bnvbh5OuZ|p7d-Qn`(N=_VV1d)!ukY3MFhVS%; zYMp93S>$ALQ#yXw?40e~sZWn<(1tu&qzqS}}|jQ1r#>C#z|;mz~+k3^;M+AO~+JMB{4z*TF` zRHJ)_`;0}v&BxTbAM}XFo=sr4n}e^qPS=-jEYR}bNe>kXk|u2UExkNTtp4m^ezhrr zrNi`Hj3&R$qY$l5=bC$xm z@yWEZoHY+INljDfpN?P&@Eiwq12(MG4*0n52?7(ubfBdRu6*@tw#>kv)d8(N#YU>p zKE{%YS^-&C9$GOO_P#4s?kT9vf)X8Dsl}Y;9i*K5M@d`P2`a>SC%}S z^QGsr1Y<{f>q~CzFQ$VChU*pDc&+#aPTa*&B6DIC;O3#3Mcnt|Li*#UT=wr8^0VJ9 ze`$WLT-T+^iK5^T-Gt5(_Q6}cN^!5T(#eOX_KHs$r4}a$Vsy7V+OM{!* z!7I<5D~tP6g+Vj#1#Jqd5XXlP)&yl5#|Yey9qLe`-%r{Avv6u!TC4LGB!CKe_>Xh= zN4E-`^o0znPCJGIkmOj?UWt&Qw#D-L#|)e<+890|yn5;hn)U~&&c*yJy?Y&_qB7!N z-~GoJ&3?lL1fB?dyxzpb#RZbNO$Zpdy5{$G2RMa2sg;0e-2J{(<*>4fqq2J_1emR? zOhenqTTzqjEH7gY(KB(rgpuz^`M3jT2yE?rhdo$Yy@b6K>jIk@%g z3u+>`%{8D&;uxZ(RFsfu3UtjMufQ4A*K_(>m;`ZRE0z!qilLrxs4A*20@gf8B~ivo|Yj(k|L_b*bu< zq;ACvr(P1oG}D?~ml_vI+$_o*ySwA6B5F!*@<2u$#+A(kHBl6eRXuE+ZyQsfLo9`FhAm^tp7bgOu7%PXkRl>YOv4M1G;eLr@o6}w zwt8H{tQd6+KSmmicd$rXdcv{Wco`kaAeF(c!DS{1(YsPp4PdZB!l%fkfViWzQ$+f-y=E@r7<^*fn2#8Cei*v`Q6WH~h3Z;=2SI7W_C7Sdkv| zThrFDowdS_Gu5e@wb6@E5#zrnim|fn^J@PorvXKZB1@AUE`__}EQbSz=xS8<`mn91 z(rL#ujRYn9sj8Z%39c_~HlB0BV7T7Sf`MiuRG+S?zl(?lf`wEkWNvsGFh-kwDz@y)b4da@HSQ zFNoX6%n4PgNo+63u5WBDiU1_OJ1`VJasa{KaUrc$yGwNJgcNG2ZDcE7HV1D*e)P{p zYiSWG{j#?s?O>Xfl($>g->N%o#o>cD5LI;zcgDoIv_&Hmoo&B zS@++FO#$mpPuVbMGv@M?ktYUC8@`a0Gop(SB13#+)gC$DpVh|x! zDB7xT>EV%5R77Rlh@Y)=5LX~rY{SOSKL(&*h*xTbjp6e1hr?@2y2G{J6DH-btjk{O z6)9txWTTC7lME<`nMs_~UBi9OTdjZ{%HaXE^ra)K(fMUzy!?0?AJ6-yDDO1qJ+4yv zDM=)Ias%yeh3$NsFgS6Wwqlsge%a6%2V=aI;@}QysIVWRWMv9M9S~{)aC;X@B87#& zzw_fMw0G@}`bHF$yM813GoSx^$O~1N*i0<58yPd0KjZC1AWB-s4?)7%T*@EXfQqDOro}Y0>MW3j=INN;tN~iusYI+A-*SaPiF(DsbibF$lpDaOU z;4&cBvSOsKkI(NuCd@~)0Q^0WDXzE-K!j`SFxI!JSKj zL79jD8$Zo8z1zS4?Ok%_{I&xUxbH(-jT_!WslkAvhi~*Uw%3ITyAq&C?k5P%sHz{T za!3gPQXPX)y{k$1=GTU!CFT4VVawp`yBSZ~!m(0CaDV(oHP>rL<45MZpm`p}FT8M9 z#0!Bd)dH=yxJOGh1|K|oS4*?Gf4bAptvJ|k_X(TwKO6Y$^w22=7wmG$^PE47+UDY= z+F*EyZF`N$UzN5cF<#KXGPuXmYz3+~dLr0I&J81fBvP8e}HD+q3Eef6BNsr5z077@7L| z`l{dFkP##Pa5OVNs4WQs26e>U4L9}yP>Ld}NdhiCy>&?f_oMQ=u1~P5D^pDCovH_~ zYRQwI%aY z54Lrq)f$4X%vHoYmi=RZ+viRV)ctIo-CVuMEqeshL?Eg3fHI6HXOFl+!TyUq8p*I- z&7^4m)L5Lk)}yx&1Kw~wm&&9ZE}cY1NZbB$tm5>u33&= znZC`LNGKPx7e8gll++{+O>n3!DA>aKlyM+^!YK{YH$UU=k)8+N=TRKE9@LP4eizR@ zE(KXgI-PRfECbt8+MVyS#sndSJMgY9J2^B?7nlz+lTdBTK`PkZoBTqJVQhDU^ztQ6 z^#^tl8BfO$w=G4ML~Xq01Wa+qC44r(v0nI+4%_ax8tYIWEiSERV)3Ue?`3IreDN57dEVR!@?w{W<1I$1#I1uixzsoVNXhs&89d zJ&BEz)hWK49LFditK)8XquPm;9_G(I8=-*yF2g{8OV22e{t%&Oyx6IcK`gply@~8a z+}dP09xl1~8CTWIEVO(pN4pc{hRS@W`-|^^B)Y5FllzHmnjT=34`kQIhF!{EIgiL#!ZdhGP2Ok zIKn$&y9ALs1Izp-8L$IISX};*LeZD;-w}0pFE^X>VME^ZL%~}dR*oq)UjFoqX%#1m znQ;*gt3GR4*{Dzud#)J|$~i!EW+$PL`(aKK#g9DA3b&_gEI>;9*P#N5S%I1)r`ZTr1}pfbAh#m9Ig`UleEbuG_~l-wPT<81BnBRW z`0OwLTjKk&IV5s_n9x-+U8+8`u`}X%blOINYHaWy*u>8eP5G?J%^A#Ik5^CV2U?kb zR>2P>zx3TWEb9IKQqQo%p!_!P+`D-f3kB^>#!)I`u;kRWfqy>lJthHfP=q}tBlPmP zU{VvC-}aI}Lq*ga{PcCSsHsAu9EK5lPgP<%#p@h=Ox4QonHLBrnVMme+{vKm*mVTmys*9UR@PSLeJG6XK4>u}sa)e7TipkTyg0kg(}L(+?C*8VH$O{`H1zN9pa@ zXyN`EU@(FY%Y;gP35g&5ZSp;M4XPo z1&9*6+1WrDt?jbC2SvN1NuhAP&xW^hRbL%tjS(l`26W2%CtAX`e^1g{*K3nRGLK@< zT}!kx4DtT`%nzc<7i3R%NJnuwLiSm3vS7iQ+{STk3{W;=Ijupn^Om(T{xixh%jRNw{ z-Xu^V8M7sGGbOd}X(+73KKF|a+>~Fsf9DH#Bv_>`_;ByDA`ZUQajaCa)#}q{-<#%1 zCZ@KcnLfwZ!Gu&x?RBSC8lO)5mw)!mpsA~|Lfk6xaVUieZ^wa8nIy|FJK&<9IYC`) zlb}HTO?BH60~|F6f&X%_Pm38o_dDcZ#-3|_y!gZyv9N6C&##W$N~uy&K8P+EWe zFMk2J5XEA6Rf&?dBNe6;mn>vJ!Dr6}=48GUh}W%BD$^H(NwVh>=K>9KC<3S%-3nDY zEuhW;v$;TPxEB-igJe8)fWiX&ZlBY-;m3XM?vca8zc_)lA2y=TL>;44eB4HZc3S6; zKDaX0{2>oay07zK&Zr^fr;={&zNMwd&yH7C7-O7Pd4x> z1JxU`kM=zs@H8DlM9{f8lQd<_oA#xC9C|~fMLB5No{Oj>es3vrDg3w93G&#(#zW^- z$AC&AiVp)`oFuX=nQW}S<{p8h8O)98MozOXusGi}!KkN1ajc+tkH`!XnPZ~dXgde` zv-k6qOln*ud_w%lWE6w121c%ht0rB)pJ%+fA+O<+LknF}9-8--B$1=Fq8zZxvxAF} z6o{zVuwTM=wDl;%(6yb~{9ESw!1=W;Ovdzfb<(x6WawsrbgaU^xo^BPh!u#Jh_>|szXxkkAF=|xt@S=vrLN{3? zDV5Nsj8;}^08Wk3EU^=vruM*Qm!&3I2FqD%4jsKJRzaI{TD#Rul*S`H2MT>A4pB!Q zag+LZ$qoeO86gf4W6>-ZKcZY)fwDFb%_HRVNE@;4kN84u@;_qM-a7 z)SMkv!wNP&L4nfbhaaSHv^AQSeCHzntd^;l^KYdz%270w49p4ZGAwL!Gb`(+e2_tf z(&40HD!3CjejjIEhwg;_%z3lNm)&nDAF!=rnZmERfobkcBMpgnzOZuuKaB1kVUB9bX1ex9|HYQk?ed z7IaI_?J@r}x@XsUr?$8R5v4O2m@!}>7*RtqCscA1jWVX;@jl9ERD2V9yrj6FIy1Ak zk4Z_v5_-K9r=}j_^}VHS)Xi=G$Bh-^M!5$7Uj_3E^zj`ZjdC_}^g|Os zT;-NVCMh;_x&*=_)EKZMhj;$0IQ(ZzzFnG8+$USx!IC3V&K44{)22+6+2kP_8vgI% zX!c-BhpN9XG|JmP#Kkt~@xwfxx3mCKTZ8_aVbBgz!Vh!gIAZ?M-GPAs!ATeiu-!9QOa|sx!PGB$bSD-5I zaZ<#kXvHoXSrBd`zcXTBlasB8fl}zHxOmW~8@>0u$weTx=Y2*JH*>KhTSka2tgGp> z92#e^I=9SCXvNdoitD|r!fSNwT(xv=7l_`AJaT4k+3)ZV!8rfe)uB_6WkZ>?);%^L zj0XeHZMdWVC9FL@%foH*HkgKjTpJB?aoDM~GFvB4-NEHFKG))K8Y&Z2#z0(J@YKBd zdn37Vq(qG7SE;InVtq#zwj|M6NnDTUl79vm`36@z?QK1{>?ay zWU$Fx{h2OH6{@hOKDS^e*f>zIQ+~f;!;6_V#=o-s(C79xo$A*w)_lYu$p#xHd$f%3F^%vDCfXBxq^$}9m@M-YZd5IgP8SQ!oLxoYa}i#v5^ z!wuHwaSOY)4ZAYvk5NQQCKyQ?IoF4}Q;%@1hUk|_SChqO=ao1W!h?hhW-*2959yIO zB>jQh+_^p{NY8;m*O!*5g2DE%<1Rgg8!UpshcAXEGh;+dI8g*GDl+U<%EQTLSOWf+ z?wHt!aVU18i@Z_OV!S%d+Giz8N_7_}T_nhJgr_l?vBSeqQ<*SzprdQJzp#y7{h#U; z0w5Tj43fj55Rzgy;E@mtai-V>>Z!w;&rmU8eyGbilUvAMa#EHxb9sJ4eID z9`*D8v;g$7t@``fUK+PMslNotFHrQtP6|s9e4L&%y0z2 zYV2a?g4WOCeOyK(OPyqkuBdep(YTFYr5Ro0|0HiNp0SHF|F{__w&;;GLTU6gs`Ys8 z6_g85ea(>7SYGkP;Y0<94}idx|3X*DvX#OdeZXc!1T|NT=tz{A#q5HDu?`!c&nK2L zzc+dfUcT?&k?b^A7?4x9{Z??7)xj)^Or_(766Vd`>?;U6d17E-xO_h8{?F+F5Ow^w z&g0Y3(D#Xme}497JjV$3nplO57@x znkR)t;vZK&tD9^JA58+{v2Rw+P|7_T3zkM?O;xx}B&B`xlMXlc#$4i3uZ@=vVAT!*qc$@x|}ha$)ragBrY7nhVO z2i_sM56DzO`|WTo z*;CIbRr19z@Tjcv{!_pH&Uji!ojHe=5eXBCHO-xfw z#?KLqWBg0}<8}EsFWI$n0QbxnWk}Kq4)Pbc!(4w)BMf{Ox1UG(R)2rr zZ;pN{1I17wo7xh9)6~@5=qsC2%RI|@x|C;6oEtn5XnqoAL&aUV=6A@dM9)5B_xn5ke$Cb8seE1Gg-e~(WspChUP6Ei2s-Lq}pN}whB3rW|t zC^hO9sn)RTs0EZPNKCDx`h2s4b`-7#l5=+eFdnHvQs_>H16W&9@1$Z5+Lw<5*V63|1~;;I0h*8Mi?bmn94TkEV2Tw(Xr>$%2?n;0JX=ut-$wVa79v zmXnw+{|tU|68ySd2o>ApdSu_spT*}9^O#>1LR*3ZQH zJBMNBge0lqgHzXN)57ll0^@!Y)mBPJs^X-^wy_jbkngY+>MR#Wuf)*)ax*v9)|xKvYurMao~@LwILNu3LQ>S+o)|5}{(mRLQr-9w!kF5a6V z-oVR?pwamT?rEg#nvY(sr_ETm*TL>rp;oa}cBdQ)JsIKY`*oxN72=bJ?w3u^Jdkxn zH&Vn!{bdsisTX1*e=(O^E{N)f5?38a8so!1qKMLQ)7j+hvmxk%RX!HIn%TK1_$%kC z2rS}nL~V!p$Mrq0z6*DKn@t%cSR(1;7PU=Se9=6#N5~Bv@8F&r=&q7L->&e3&QZW%45TqTi# zJ*L>Bbp8a$fUk^Bn>#37u`Dzz1g`)?i8!FKo7Xll*xL?^-fE(X^bWD7jy5?QA0OX* zfxuzX-vv?t%-QgA{IB5whBGcP)8(AHvh!A-WVK27 z-n&qf^qAOK*TZQOr7Z6l0LWgT_xf_D>3YQDbW^7>*_8-TcP&hCPNIYhq+Z|M&m1U|f z?duNRgdN>~vq~%9HRV&+;g2z01JZfd- zJKTDP1KnbbO2I?8gtJ5EL|6*8;U@~ZWQrYl;#@fwck&VUg`v|jX@bOMUGR=;T@@k^ z%&9U;VLvOwC4M@2HQEPTlNn1cryw+$YHlvM_{7HI9IUg(49l-5HZ8?-Rr*J=uoV zr7i1r7UFPBg`0-}#fiLtwPSd7#ISd=|F<;}0?ATnyXceGiS8G893?|b^ZK5P_h_dE zyA7y{=BSRFo43 zAT;v|%!0!aDcRuWVp=rV_n@|7d{CwSLH2DlI&;x?SV40Bo#QAnhzAYDrFA;unzQ3^ zkhQp9k%w)W08hd+QYeu9mn2t|o}JygBbrrgGhoWfcEpittvC8D-jVuD5jL6s(Qo+pq#AYi-jZor zg@mUD1_loJRvIu6`+MpuRZL6bo}_e(59Nd#mKn^<%*H1t>zy_T0n$4n5>n!bd9A~X zoAS6&G~6o88PG*Ae|&1zp{6$^9KjA1$wQC?h75+;J0kxM;R0Q$d&??V?@kYccX;$W zydz284yFAc+Bc}u2!7u`y>2~fB*ibTR0iqyUG0k4WbDAcZp&rF;aq|u+{Sn9P4~0; z*W0?vXYa-;zP;bC@^O!b#>zm^kGi|F^*RxcsLSAfrs-M}aW$!$5GxAhZaL@3E8t#k z>HP|O?f)hcH&Q7%jPPjPI-=W8kj`8LpZ-@jrdT0!@?PJGI{_0NMjZZ|hTk;n>0^?- zox^&Y;og9IRC@l5yFU{Sm{Kt|==zXFdI%jTfNH7X4z_>vbMI3HVS0e!y4%>qG)?8l zyMh5rVj~NOXxWiRu2$lWHyDAZyF6~q*pNyF%x6Xy+Jv+D*gHg7@(Wm<-h5@$JPG}# zn5iN;@_=Kiu_Dx_0s(AjogG^TZTktl$H~Rh-wIuHs|Qe;zxx_@t!}znDJTgDKJCXRLv95P^c? z@*^C_A7>}g_zqRsSa)%XY}w5hp)TQ`PHMs|C;wJn!ZuDCd2V;$JPNFL=Ef9(cF)bp zFTQ6#+Q+>>q$%N?|I;yYZ4-wYx4S7ks8!mlp}tnCeVy1lVZ-;1Qr&NO+0A^h$x(`J zaPi|_Q1{N0J8AC^VX9;0Rp-Y|1fj2gT!|6yWRA4u3CLmR~ zJ2@UwGo=uA0ZWP<{(7Vv!M`K2ZS;tag-vqj8fpGGwDZ4f&at@f!Tj{G9rSM1@s_Nv zv0!%^V4hlH$oKwT&mYIxz`QZao?}ih51s|-j{@=e@TS)DUrp#$Y#HUf<|FLB$?uNr z)AB|zo7{}j*)$lCd@fl z2hDwPio2ACad?9B_W1|~sl$5yAtw2vD>huI&ag|{dLPn{QgeogxPOd@^xr`TehRJ$ zOJu(cXq(|YLbw!tBn=3@B(u=&sC7zj-P(=2hEs$?kA!N~j{^>$h_L%Us48U|G9_wj zeCCNq!H&3V5bf1=1|yEQVhWV#KrOsn_q$*h4hj|Y^Q3?%nax+Bbzhw25N%lQdk8?k z;)y2;`lVhn#Ud;`Refah1N9F-5yMy?3nH*@1?EQ*$ac{ep%valN=Ts^@^&!jK`y$j~ay8ZfkDq-uBq@qIZO`@i1mM|#SQmktarTFa zj@^d1t=}Vy`}GVOeEQlpGp?asXbB=>tw&ckV-`#`Ts|&LW07T);_w(_sOfF^Xyv>v zclx(f-l8I4q?T_kkIQ>soTe6mH3D$@F%hG6CF171j~{qqCMMYLk?A-&T|`l&+3d3g z-4%1IppmI4Meo=o*N}|gjJ^|w{RRdfb{=U66;(uTIU>uq-M(dgdfZ|Z71Ne%N&mcF zzuOLjS%aofC>P}&-cKhiitDL{*fhC)vDi5^kC{?D842Sbkk845q0n*=Mr_yiwr%Z( zx%|3zcPIF`<+7{$_xDS+MCy)I?OQ%KQzUW_=63UHZKrA3fuxSq=+nlF+8WGwA?dJK zKTt|EpFnO!&II*eC+zv(<~`8x9ecWctTI~iy*d}UnpFZULG*kNG|Ki`1*?qb6Q~kk zvIBcP6j&l>IKtdPuLC?Gwvo$3S@*9DLzQ-NsT7~5p@9vp5>4p>zD5N=n+j!d#degJ zLLYV>BKXG7Kpekyu7b;!Nrb18WD43e8zOT| zSGK$|mlEC_T6qeC|K$%qxNPlWVb?G|tiFOGjctnhK*GMcrz)8WWQ`;1>s|r-Qyb<_ z`}LXYu(?YhN$zkPG#>qdBc%q0&efiRn{ zyG$&4*nIwwpYDjm#Q#Su271JPe&p_XHJ8*Xx-I)t?HYK*OxNVNO^PO!Ww2br!}iL! zErqRUeb~1Ns9O|@ywtr;yn~h39IhU2oUVIUs0gJe`u0p^yV_3R?=5L+-9aK^q!K)! zk}zJ10uaC0)Ws-)q-Xuu!-|&mtZb}q!*;@>{9vFr4E_b!tRpn& z!>UgN-XGyzTM3W;WS+u@d^cws$ysZs`w>=QR>#x8-eBI4aZWnLp}4<)4|shJCUXz| z?TsC_4LvULWGI9KMgsa2Q=na~(5hkM_+{NnG>GLmsq`tz7F1XKkQ%say>V>QyU-568&{~K%S<(Lmg{1qu)-j1=lva$3J76d+KQKDLs ze7XQTXrCM9V3}xPZ{>&q6BMP-N$8Z9A65*H97p}MkWV;up_E*I>qzNXwN}wAH_SskV#x8!6A|5 z-qFH|Y;q9=k&CQ@U^Tf?6&GK(Cg8a^+;N80QBDG3FJz0Ri(s0GGyUU9vc3V@)RWQ) z@n*R1TakvxkUh9y`E=Tm_qSSIf&et=zBuQX+zM`VhV?%Ze|Vsb7&ykIOCEB|(|un9 zD(7K#bPG{hthHWuXKC~ri$^%kbpTaFCMxJS%fXo`(y~qY?BPlRcsq$!$O$roUV%Pv(;NY?I)&lIM_$Z?-SX}?&rrwDOXx_k1x=9} zSx24!$KG3b#kD=_qDdeGg1cLAcXti$5ZpbuJHg#Ua0u=Yf@^}iyC=8?cYCvc=iGbl z{SV%F@!~Ve^#Lotjzx%fD{VQDeQf{or z1*npzk$XA-xzT2FBh19UCpi<2*gC|j81Mn*X9mrdz5!broL z>sHzKc1iPYe9)63D=l$EVx`NQNJ}fN?Uhl(4v%!dKTRfg?koTG?^yYnxx$PPA!UX# z82!#>^ZK&hKq$}l@^eAY%v&+IsOnKOPnQkAN8YFfYfp2q*UtBR;1{*w&|6^iUAcGv zb5GyypS4(hM3t==jEa=(PT5Vat@pk_jglA?_<7|^&=SIbMigI~wj9R)7t>%yQZN*8 z;O-B%V1rR3kz8WsJBQBP?ETe7P`-8!Z)7;HH-scvRvBmf#+Bj7SL7xt6HcBiA=3G_ z^vUOPPC^5F?sVn3R;E%iW8VLi=t?Gu%0RI1sM9K#l9?gN_oF*oyMN+m3wwv_tf)-y z8?;evS;Rc=B_=bR(dB#b_b+vuoMG9iCQjoxea8fR`=u;%yUbQbV~auA_!#$dprFJ> z?{Fvct>5p14c?b?|GUPvi)mFCZN}!m21PT7hi7Uc%B~<-mM() z1;tY{WF3|W=_2}^C}d~2H~79Tt4f3FC?_WqN$qalbR`yu_l2GDf<_t`i!J!4fC?>` zz20@Ea~lv8Cl1rHwEnH3bg?SjM%q|1vm&c^%2rr9Wc7Fc-+GZiSD}M$Us3V#H)KBM zmkNP491?q#P?)5Vu=X73;ZmI>fu=ZpoOJe>y=?C!Tk~9vYmSo?pBipZkk1aGac@0h zIIOY##GUU5rY=yuxc4v;K9{pTg@=-he9z{ydv#IH`>PG5cxB4{%L?Z zv6TCugvuvhvN8T3x!G371TZAVU2rY}o89z|l!|-)Owl&XLTD)N9cHk}SV7?#C{^ij zCLcGIOf$CWwOle~#WpGYH+lW$4Nc>5D_0Nc{bNU^t$#} zFqEPdI>>ABVy2MqA@ntvqhbP$ad`VK!cZ<16eP7A;LGQyq>BV^g)xntF*u;^& zcB}ml?4$`g;VrO@WP`nk&y$mH41`jP}ge^Ttqad;d~uAIB)n4mDA-Xls?wVesWB4ToNhnBs^eNuj}IXv^o(|DSw zMoL8q^xz%4J))+A?+rMVWR?p8K<&};1==7tBET-D1#3;J7_omE zq(g|=o^*|otnB^#a)-Efw`leJ=>IZ2j==M9BpS?PB{@$2>C0t0iF>MGhZyu!;qkZA zAPs7(rfvw#|h9~#@jJi?i19~DCSuDD9{o+KLT%;Rbv1h4z{DZ8XLroLxbAGFH`d z*Dh>yStnf;eJtEoKDp)2ZY!q_r-Ux58lm{U~<2M-^1X-ZFvt*pGh1*dv{8{5^L-ZEok6m-W{h$|QQV%mBChAa5^xJl71d zx|)U~nYZ4cXFuEuFZA()(fE2grIa?_CTm8utk+wx54Dr`*}KyNX|)VL^Xos#fV}>*|^Ij&J#( z!HcJ2hf&x-4>in6^C?|wu&&;U6b4v}4unPT! zO}cqM^$)T7h_VOJ6=~8`S|`o7`|z%0YN^WVwX$)W_b;z01io%9+VhWXW!Lbj9(kT#YmCK^gRI-Y3BOFiCjy>Ll5Ej@Kx9@msK6MLmP~{V1F@5upvFmh?Gxw zc{1jT@GMV8b>+{cGFH6tyeR(#(?>AOx%pyOx=>|gbTs!fUhkDF7zhqJDkI$a`1Rwu z(ooE5$K0%Y=lDKc*LdF?H9U*6mRZ-!&?bX!uceDU!dl+dm{CXBS*u$Ix4@Y828iD? z9&cZK6}ks)QbX)mt!~smaM}#8qhdj!S)yHv2lVtfZ>5-M&aV}0&-}BgR#B;*zx#{q z|9fSH08Gqz5nc69SAxPyxfOHb&w&NrhTIU9i`LJ@DDic!9h@oMXJ)tQNk+ut z?k_D1p39+(pd$KFa!M`XclMqR^x}oqiAQZYEbggRU0XV9c$MZP;ks?*!+V6s3Ikl6 z8ndRi)uehA}Em(-^(Lb+Ooc9=P|U%nU()Blv4f5x7QH4_3|XgGMI&)+4Ww zT_r|tu^T?i$36v-p zyYbcdG|%n414ZO}iL<$BJw*Zg24sLL&1Gl95pFP{J0)!iXi!CQv96F{@~$siqy(co z1bV8;3QjiXG)nbvaYuVEpsT_rz8W#S&W%X#-Tq$xK09skAuSv5pdR4QK2IZdrmC6{YFs;`xT*c^A>Gscznw{ z2QxS_i5i8LCp=8~2mPNexm_W})bM(+_q}7{IA#LOc)P!)GDm!W^!?IVxgz3_*UB&F zwcvwJwnvv)^}+*3HfnSW>our2zpAN-Gr>+TKuc}^v0S8uYuEprpwNFG9N8KtRj>j} zQ69c(j>m{|M2>S+Z@&z|p{(rb`Aur?8Lk?|-n3XT3vdNkT?x&khY!diJHMZ-j+Hm3 z?!5qFr8j?fyJfZ%GZu@t{ovVJy`sBm|9qwBd64{Z(c9*mIOy#zdkd zbJsbHr|#kuRl#B!F*IgSuQ)y1TvDN7$+?74O@D6ja`#cm!&*TR6ksk+M97D z(XdN+=$iC}o2UP{obf*V+$6wF@0t<|Y9+=LTtY%4AD_0vpPKYlj)!v$PR$4+f7tl>R~YzMSXdP3 zi=?ap1v;rTrax*PWnL>Sm;*{~+zyjHvq4$F*!elDiVWzSku1Y1C>U#Ytc!d`O0>cD zmIiKx@~YfkrS%@kuN5vmn1moM_~eT`w7g9Gn^k6Jbd#PU19Hws*asCNgZrtJCa3N9 z_1ed=BUpLf51=#l?o6pFXpf7oXE7e8)XhDOg40qJDdtWU!^s5om)x;L-RBni-lzMD zVqS(}Exp?I>yU`V=rvOBZGQj91=a8~uH8AU5^j%9lI9w6D=U7!Buea;u)(yB!*;xe z08Fk{je9;J^PVDNok@Ix-VlXKu_ui*O+i$wFb#3p6TD!{g@yp5y%}p;$`D#+1k&pL zxumstvGmXeRGvcYx5L}2k7kua)g3QHQJ+4g z@^xf%iD!%Rh8p25KTvm3e>U|1?LA8o6p1cVZS%*ph@Iw43Udf$R8w&_4i>>|s(E|B zE6~A9fFhHs&9fzg_Ri9IA!CocQ{ZNszX+$&1a1*AC;e#=7M{@WWy%uYQut2g=IDMk zM&5CvQe|LR|A=H;+|!O?MhcC`yBYE3W7tjYQpNPOdiyuoZA`xwhwQJo7PQG^MgE*% z(#!mISFfyYhfi8{`y=c0wCZYC{d|=HRhCSxhOUXNYw~7)ob%CwGW3L9eY4Xx?W{FN zX3Vdlf2*}%Voso2>*&-JYm8(TJ{NXNNSyEbE*OwoFui}eAC@ADr;JgmUHP-FPP}|}E#7*y2yya6 znU7uGFdZvRZl_^B|B{gR^Cz0DC%&b_C}_VQM%Sdm@SN!Eq_et7!cYt%HXB+Ss^j@u z(kh`_XZ<;UW=f|aM^C`%L%xI7lZT3mic@$08(NSaKU%6kgf~KRDpoq@_R?JF-W|rq zRS{iI^7*k~m^-x?&t@H1PVi++;)lF=xp;rG4Xp|dGLTBh zFtSfH3Kntwji5>;l)l}3!r!^I#-i}DK_EN-$uSGDd3sf|(oTw3qEQ~dR>_I1@M9nX z`KWbL+*;f3`ZzmQ4z986{MLnw0fdN4^F~gJ&ok=nbqqOV7?U$XAxa%Y(jQy-*1s49 z7pQ=Y*55tVcXy|wad(RT*J8$S`15DNu)6)*-Y@gWR*eK57yV2^r<^bMVqb{FWQobc zV*)?D+ef=4G(AHU-$CiRmuDv%{Vh5tpfsPaxU99aGlZ;Jo%L?Umuv62UH))jiAEus z;E%OyTB0*ze~?V;AbumG$sXtWifgdIe&ElVUk`f0$NwfzksV^%lNy#9Mtg3#FD@=_ zJJz$b479b4PqGETc&b!65_|A83ISVuiOnpjD?E zEYi#}x!zLc{CI29_IN(Lb#d4FZP%5T5D-!VKNPy$)zVbu*EI6Mv5w!WZ!=7k)uaYg<~WN_c8=xmbD1 zhz#C&S$S~o#03VY8uqoAV^THY0L#}~m%frGhnp>Wi=>MbjbsbBC_Gs0`^^|C=+TPc(*One>1xTzDDNL zBer>Os0m}j)}8t-;N+^Yp>dNl$@`=G*XR9~s)39hd%kadwx<5yY=7=1=*b=9TRd;- zWO^Td=-p<`(s;KH-!MqkK>6t!oL;lD?AhQvQ_EB+7i(Wq<1FwE&qfJUSpIw8oWWkk z05U1IR2D{6y7nSR^1kzz%L5-yklDouH-(q|*bZ9ODoK;2dT;wq{F9N_c*oCUFyrHg zC6IPMjVfOX8UuED+Rx5#QT_AJpIl;msLSk*2450;@5X$mptg@Yey8gz8&tN%PG zIrBYU_Py26Vd!9cfc@x{--C$0$p{yU4ejD1-F=49$?BrMxg&^J^>w)9yBec1ZL*dD zlX9g7S2GV~lC)&MBq&vvqLD6CeG&3@Cn8q&WqN*b9ZREN8#G-myy%oPsQx_ zc#CeW$W34l#!h}sPIjdpY+STyTD2=u%uxV^8g%0phM{jfWIn03TCqqDxLaZ=6!81G zm{_T?X)qcn3$u{D6`_x%N4p>G`Nje(@nko%y6?j3b!pjgXHMbj>U#V1%&BN^V`*tP zm`H&@$Q%9m_*m<7oQBeGlKuxijld<$GTXg0Qq;OFxqus9WYy3+Ic2Etk20|ssmh;x zW})m0FLIZq=6mrvKAu{>%7QIQ)XVLfmt~o@E26IF0b^&qS(B(=`7d+oZzX0~)Cw10 zVg_C~-imQ3vA@+*SLO&24q{RXkl?Euqi-SQcm<nE>rZ%-|Hyv4V;2wBXh6Xr7? zfA(taPYRu+Qhm*IW}S4+H&ag7J6WzQ*ez<%tewrwqt!gtpy!154ZZr%=#KU7THX0a z+qcjld5;XA+xFL@oy=ZRLV51WvuZX-bZ%iW!QxaAR*XNrijp$Zq{QUW`7}~~_>YS` z+6lE75jZ}TQzuOJI>UhZtK2?uWav?&T|7bO(sDdMF@9J{Ca%}DeO)kL)GOR=_UxhJ zVTsuj#7rPkyqHL@?U{k5*MP?=msBZ>^7iwmd+pEsZKNe}qehcK*RMOoGJVe45!;db z?FK8UGcCJKNFO1!Iqc=DmSTkUDht?$;hI84(RzzKOUEy7w?qy{}P9rbIO~>KTc03wc7my_ZIHpklNO|2c5GN`Cd~)OGa81 zJ;}+NjaMLr{^J`s4SWrC3DpnFy6o#HYs_R1r?*|ahyf^=@`4)4^D1iELH*UvR_iu) zIEzsjVU^ucQ~6|QoEceQbo0+f7p7<7FDom$=iXcWyaXSzkP{S$x|u($r^vXth(y#2 zocj?Rwnw_SY0-Z>;j9;ASLeScYSLvaD=X8iyCpl~#Mg+5Ar*s>b#hB*L6X-wMu^Nq zK?;Kl6SE~^;MHX}I->4oMzZ~C*N~2r_Lt=OKySwODOGs-^~jDbX9k6(Ik`yG7_Blp zqC`}80$*gXAa*r>E92T4VKjg9W z_VHj#;*cLkDEu~tUF>8b#-c6KK};hpPit6*lj*hEYUfEda)+RkK<4(>DrK9ObhZph zW66Sw0**1sr)MubWcEMq4=ml3UYc>y~%vyI^Pb#q}Ap(7X3&TWEe+z}Chx8a=XzK2$9`m(Qog_N2G~g{}>#V3~TUHM{ z6}W|81DO#>l=_})aP5v|Sj{EG{&xfD_fI8jV%}5Umg7hQp?TB`8x`PR^1{|Vwze-{ zn8J%Yqky^FLq@v{feq=xX(7@IGOIUqU--dS!M(BRK*<{()!5Hipzrhc7zXxVdSHH*$%^3i}&VX2#qtK`Wo znxY0HwlxQIBZ=PagOB=481xMobuHV3wBvcy?-|R3YIvY(EI9>NY9lIL-@yiV;v=2e zK8d)o&t1eiV?!mf?(NEu&Zh~Nt%|hGN^a@A_sL}jBKolE=QiiLps79{`h+;s6ch{v zqoMkC)znyow~7G1K>>&5`oVW*Ms2$GqhPz&RoAu*3BV7<)42O`JXI%i@9K}AE8#=B zzNVxkWh8a;>}I0VL2o!Ulk=b#znd1nnIkL%xoEa6p&?%+3d=CwJ2{G}zr~gs71Hfe zdwfu`qMSujPPAZRu;avc5xES`n8?WYZTiHv$7P^xjhT~t4$S~N2HTo@*NYgyY#!lGazvu+Aw%Kf9ULFioCNz_b=o-=vt z$m8ftBe518?yj4`D;3Gri@4^O$dUJejRxKaw_j4B<<_+6qk-0w9Xh%zHVog4QZL8i zFigWSyuvFa*zz&q^QSK|H3%Xa7Kqd>&?N%6!w@r8-=`+Tux3>HzBWEd_A0s25vW+H zFQev0Cq%$oPRWLH(t5Zvr!yCDDwz5fFEt##myoFG$=@D@k?5v22;W#mHitz=7dR5LUy2xSMV{UepWk3+=i_}( z+I8#7M9L;N0GqExKu&&H^l^+pB}sB(kLmLy%=W|siXQFaoE?*dvV*ow^w^PnB16D9 zU8I&)eO%D+LC4M7{Oa)io8n6SL94=gdGsj->+2o_+i52h64;uYZ7pE&`AJAy#$8t{yBs%d@0?SWa2(*Hf)rOUS`D03u zl9C31E*CX+t6rS8OX2JOU!hM+M%hZM1P8*1{iHNBC_O#zsBT3=xY=mlbKBAisBTbK zUZ77QV;-@{S@V-(*;P=oDCDnU3mVU8tCMt^8_6MKryxj?4mutbBjqSL6;Z!0`6E|{ zj%^<$H$0u+TL1Cq+|FS}-ix3QRC9jJLF09;$&usgQ1tbPj zwGU>`m2Py>xQt`19^4OZN5#gUFlRAcgM)ahW<2f@4m}??nDY)7Hc2=zXZL&5#HbyS zW-jISDzeojLW1dk@G5u7Q)40aW(Qj-id!8Dnni|g3`ic{*{5EsjrjFQeqJp-Y<4@G;b|NC6)~u$ke(rQ3B`+Nl<6~xF#$vakiH44DqB{R@xi2qi z4#Sy|9126MuC5-9!xA|2)l>-s(l0O+F=KDKn90hj^!T1Xm|IJ38hRQOF)M5~=KST; z3@jz~k3yP$rK&t(gS5dqMzOyVHJM4%|M;&)yK-xHm(BCsbZB_Eq@u!R+C>q7Q|LDp z`t6aE`5zOqvq|$UMA`Gd#~aS$BoUAIz31vsir-TyjJG>K7Ga{6v2h?B%99+M?LObQ z>l~2tB42Lp{RU43ik$ghPiUucQY-i;=sU;suw(CL?~?!WM|yXyp;~kJN*XtU+=6oG z@kUijTA_>AjCCjA3FqCgviDA&d^0phvom9;yoA`k0B)rtvE;VbS8ghq99(MY_FMt8 z9ZbaDZ-Er{oZ3T{bqCcG2XknbDMm0z#s@?gWG*K#&vZxW~ak}~;W z$~g$tW7OiK8h7JUSihn0BIe>HZg@|3a}Z=;tyd+~?$DRX{2Uz%5rAQ4&40IiP_Ct} zIye>zGbEsI?eiM%4XIBH>i#1OSwv(J^#VPfLR1WSc~&&{JKsdz=s|_;K%)JvR)Pfv zy!5FCB(m~ZnK+`m%RSV6(ogEi!M7e8Sqc~uplAvhioZ&!w#$u4$!QVG$GJbt5{d5a z7Hl|=-0;)VaBEQMfZgNG;wFeSPu5cZ`Sa(%&`{*+I}#qAh^?)H1zTWD7|H;4lF`=2 zD=jUpskhZAPX>c8;G!BbY&CgqfQNeieW#xjqXdt zO!ZoxZ=|8_DZKh%wzs{Y;w3IQ8cQZ9nC-aP2TThu5)r@02#3R{gzO=c-aRJNJEXi% z)u7u!F^UDR#Op8#Gc~_A8RdXdZO1aG9U@7XLi-q-YDT$$^C^CSqkDQ#_NQx94+3v| zW<+hA`J|MLd&1X$dFunt3a-x3hT8O&`5%qO$9gKNC8XuWMu$)864)eWl~lZPqo3XD zir)X=At`+qDG+dNAw(N#E#`RL^(`lc!WqxapTg@$xac4GRgAagSmQkV)6$9r!9qurYuO>g-y+Hg8jAJ2^_db(W#3L6EA*M| zu<%}Hqy{t_yTqDU>L#07^>SypPS%uav>55Vvs*o;{2pP7#23-mo&?CkZv z43V)4^==tuWd%h=tZql1N9zU=LMXzumI#Eb!N8fIU}L+l^oLu^?#i&g{(M}su}L<& zcl~cUthE#leQ))F!0UR?QNuRddypdKybr%R=fFJNjX990%?#gi;hBci-Z#TvQJI52 zmOSxZkuuj1p+q}F@`yv8jE*jeMwPPG_rZ;p?k4~6Ba_mEuFH6%dq)*sV^ zExGXN)I93=_%Ck%elvQ#!K8PjrfDZBOk#vlql>hbs3rf-gA95rp)K-~E!7t05m>yi zsZTmAOgSvHn5#O-t4}J`e8y6-alDk56P!^)50Y|~oMu->u3Y3#Gjqq3=rped#sm5M z{Cpxm#1E+wm2Z(A3mzADzo4+N@7n0I{^FUwV&a{rhyXyQcL|%4KE7wTrl{rqr8#F`KLI>sG2xFbSh=#R^$N zO^%yOD+;7zSP3e(w7ye>BQcGvv`hh*-uPPTJ4HWP!pnqzA-wB$M#srUALl7`Q_0dG zSarD0nVOj~J!ny&#^^eBleLdV$@*Sb$F5@2X&s~YT~d}uHWO}=hkPt}&kM~$)TJHE z(X9-h4D@<{B@IS-BR%%tzvc18Z^=Pk0%Zson3iqbQ~h!{KiXTRS^MXSMC# zH$RPgBlDlvmg+w@A6B(e)6tpyTWMbLe|drq5)r)LBik9x=!a)&`05Se$psd^pt~sdacbj_&GQ3%9>nH3fE9k&}%n#fYlpum@sg(V>X z>Rm&V?|8PritD^yL2>cL5M6bAN(%PF;z$}>^VOWre|}z`AMSiw%rh{e*a>n;qpo1GrMc{f_mgR?Y;4VUI~lmlhQd+HyI?WeJTGqNe?9k_rRYgI zIXPtzjsJCkoU-eE_V+;E_$!3Hp|*Fl*-@LdcUQ*d>Q>PKE>>aiD;rWTGnhoxZ^wlY z1Wv;TcwX$VIPb^--fPWjoJRcP01I87skRCB;MkaXUo>uoW=$aA53qzTJotUC7cC$X z1B`H=9hZC(kR0^J@iNJW%@yiT%LA+QR8#v3gG5g8Jo~qs8vy#&y8k!2p?leE9Xk zm%(X!FvA&%w9*e-W#ZOfgoom$vJx=i6GU}pu z4@Y{eW#TQi?w9dh6QDv2c928Ng@uKgKz|wRfGIHD9!i$W;!PIxy|24(%9o5W?h1yh z(5l@jsLV&rqf0B21{cSQDwL>x;yY>U2I;ebqs3aSHV+mk7=%C9%l5m&OuvpMF4~{2 zmmeQ@g?3vGtD-8O(!nx->p^|MmO>W1B{A@S;<#D&7b5n5WI?A=*ox)*_Xl)KJDPBI z7gttBP0@41wQI}(>kHTqz)7@)aZr zTUV>!{^#;f_g8JFUGR^8-UwXJuve2n5({;fIm1=~h!-USRdF=X9&-qO4Qrx%v5vIR8g7PFK^7o`{hQ zuDD+>PbU~ySSlJCeLy2`H;G>c3Z#=>rSf~S<|>6(>NIYgdBpOZ_;-gQBH*#_1uwg^ z6UBu=KBeoqo&0#7@~g+L5y}BHGrs^Ld-ixS&Ib5xtvH8t?m0)|+3fMhq6|A@AZjBy zLOCzbCoemY|3JPT*ahbd#`Goc4(rd$ft@5wZKrR9fC}7=+WU$5`0!)vxuAB3A@7Fb z+gY;smN#2kY`5R64TB&2&*KX`j@QtpjDeyHEjKg;F zCsxM|@%QlXgF{2sXW4aE^(EE?Lf*f>yym}NFaaS}zn?!4c!o8;Y4$sC+iC1pXipC} zC#TNgYzE%3izS;`WbOL)t+B0h7#JALARYroAQc?aI%)xZ)wVDHt@3F=iMSvEHH%-h zvw$ooc*3$WS|?}cGY@`xordT%Hj9RIH8XQ_q6CO# zQ>-R^2>&`B> z5mecRFV9cFXeK5kbgn;NbE&AP;BY&TK(v)5%Y;Y!b$AIFRDq7?yWD6`{CTe8@BLx5 z1-NSlKl6HRtwuXSGc&V~6`#?H^}XXBO0Io&fJI#Ky;*$(YO=GFVGkikQuMtdz%fzI z`kMbJ7y}CI56=s8yVm0dEW&ptebIlxt=QBHNJ~reKOEJ5Tp@ZwI(>NBYgs!8Hv!v+ zEcD1cukX$9azFnIAj{=CU^d-o_z+m{^L&!^87Y*9Hp%>wbKo6Zfl-kPx7v zQ2=SgA6EZ@glfHB#5&X3uVKUGT<<|4!Sd=(dO9AUg?d1FiT_Mat1jcg!NJl~Q{G|y zO;eopiG`rsk)~bSnIvR6u;?{^H}7UISGhIp`pnKXNX-8R`%{10ylQ8!dX9<|R$5VE zx;vI-vs4!WT2KT+>=NXu}mKRs~J^Y z_f=Q0)cq}A+JPNP74r8xYG1A~dAAJ=Yb@!EHFyuBUq-tHDrFdxZ&42z4PB=}iVEH4L`drk;H|=e|8f8@ zwXet`>%D9WWAad0aat_VjqTHj0Fj~%;e3jL-yrZj%lu9-$9xbET!~3G$sW_x)TH4L z&Qd{=#zY=}Ub_L%`Q&)tt{HAFl&l+{q0iip*-V;l!`C7~i?u1s&1Kt9L-j0R-^sI`(2x zU6{c-_T2(g4IFpG)31hxZ|>{9DIls%%F2=lSONksGVD7dR{M=>2v|7E*(}pTyZ?gt z5hJc2z10_h2jCz$uU`P*0hk$D@b(WBFvup2VckZcx>32RY2!Ujqi?9yxJ3t;-jVIG z0cY2|jb+!ePYL-G{C|X-eeJ~Dg}TnNhLX~65a0kLVHCCLeY{jpuh&vH>jo0bKY)8S zA4#1wzN~I)x($zX3>yZ|2KvNo=e#^pfqmiqAhHUO7{p!y97RG(nmcX$86d%1A3?}Q z?M>Skf^&Akh_}x$EIBmvou{!(_Zm?PG3VaLS zbWm2#TaT7MYHl)BAid(T8S}V-{mXE!Ld#~YEe*_axd9GjoHy_nC@XVXb)5&m z!r&BG)C+*b>pZ`=IX)RuyGh>Xgmdj6J9p=i*t4$w+!wC;v^9WxKX}DMoC z;j#e1+1TUVMe|u7KBVs@uP^Tz2qW*nh7UmkW?=iYnw=h;;q}=x2RZ1ONX0?R3twI7rSkuDanj zt~kMi@ReS-DIQ>WW#FBCeSOP+2?6n1^V}u$1KlECpFXU=WP@kgg~*flzveIq>40@| z?8(rNmUaD>+4W$CtSCt4G1A29gk@PH%X7sly$|44ht}0-Vj=woyZsL`s;Wt)hrGE; zu#L;UJ^GY+4=nOYu7`6qV1N9sYjdQ>CAieIwPk=nYLs(mT+e}XYqhE)fHD~uxOtU; zo>Q^N-!2X4rYrI>ac2n$2?#!d!2i{TruqF6&&SzL<#00XB*#|}cA)LZ% zg~1|B=1Bk(Dgp!35>IaNA96}GJm0BxwGG7o6*`Tv6tdqSJ5K2SOVM!yq&ENSEdAKDH+tS@ssGx1z%@qZ`EWU5A=Wu?`wZcc_oR{FiGAergb2s~!9{j&(sM#` zcIicsj)S7P6Q!SXQHpDnsCxj-Gr`V)^+_4RSt3QSk+x)mOY5CjI+`nEyag<+I%ZU_ zC_ru?{saMjrZCB?FIMTrP5YuQ29xODR_&;OVF%_JI8&wBf!{8d_T9{Y1LYQhe4ha1 z`_|4jM$p<-*#|Nes9u2#=w1twR6Lv`{UpgWJ7QRO!1O+tiSfPc097Ccd?pj;f9}D< zu?W;Np??S{IOP57kP7^K)gex(qYOS%&%U_2I=g(gagAY(rm@(}DCBur{`V#IKRXtA z@Ds5ubsMFd)BnixmputX>BrXTS+qI$c-gSG4tZxZ;U|8y}j2DJr%hkWCg6S!Qf=lcu z;NF-1^OgVEH9n0@EhOY0dyjv2UQ-2gcUT${3i7Y=$3i&b|9Q-6aK!5sL!j(5JMc+5 zE6T8woS{a4F775=1+c_Mz40i(ySe{oQ7j#cy4*#ukis0f3D68hGfC9d!EeFcf62U1 zA#Wx4->a}Txgi$=qvKNj9*&umS5kd;A)5(hA2UMZCq#st|3`$v`n7DfMwk(RT+rBN zqF6*KQDbBT5&vfeAaDJz6-e*bs%Rx^nQleN+jP+rI_R}TlR0maiXaIwY1iDH;{2;Y5yDAqStFr3zdLH4 zQffy|rLU?_*1?us|H&qVv!z<4;}k_yWz2ARYSr5kM}4z5^L6Cm8%9z&xz5E;c&LBI zp{nWJs4#^QO}(DJwRoeiUKNwb1`{tV+ z<=3?6B_{03A57oiL-P=iLJ|q$jO#NSBD8I782~P~S^W9-ONZ|RtKsigAg=wPxyAY{ zN1js#aSAZ-(A_hwzCJDYI{J^6h#4}pr?)}SV!XLOVvUISSU@&TBI1!jycqs4wd0kf z1Y)N@J>}(JE}TO3)U{VQl83*rai@9_4*x*mweP9B?q%GdJev~ch8UP+aUd<%Pb)c3 zD`h%iw6%;Wr@FLD4p2W-~B=;(I(=PC%C0Vp%iYDwDu6E(&M90X(b z2OkUxk!f}Uq<(4p=`_jz_@xa5P~nIi%B^`~K6>p-|L6l{V7mZDK4Ke?%ovl)sbkWo zM~>zhHEKyYEJ#Pjb#F&^qgwP9SzQh)7Jt-W#cJL|%Nk%u7=gM9UVFxSF<9?jPz^yt z_=r}8PFTeuFV~3d`nP;UD@eL>#?um-Z7f?|cLy(caE@&iFS59WWmE z)y&L{1F+aCs;ZEH2$0HD^?IOA00cO-Ak_5{G&pjTpN2#zOP{ML1O)VWoq7Muxo#jB zr&AiO8j3mcRMTq>KMXhgs4gQ~we+khKh-mKQ?SDm8yhtL!?SofCZGS&{JmBwTmePz zrqfc54ufSnh6h&G)yd9Jm3611x9p1| zjMGzFP;sr;M#TI-Nb7rSGLF1>6Z&4*9SOByRJt>AxB4q*w6yRF-ff`+3RH{)CA^)f zrAk##@4w?B{{2BM5){MHbPfmtnVFyOx0`2=0bQP*7R^!;zI~${)Osi7MA>82#V#+J zivj{m^TnTLpyD9icDX@WODkf07i5fp#8FdI8vt2P)1Q)Nm@O1{_Bpz6)nEZTs)#x$VNLy-uFK*)s4VhnwEcUNM)YjCOl>65CC zj;xy-XGBCq*7I#~B;cw+VIU+b0V#AP5K;rzd3xtV9VrT68i2llb;}?X3sut8l+xEH z0*+2^4GgL{yYqqMEFc?Z)D0seBU4gZ+Iez=PE4$*p@9WRz2YA~;xESd;=o4@K@T+X zHR-@y=9uhP@IvdqWr?eeysbcCrO@aFtGv2HpVpv^D~pk(JtF}Ctpcplim_CkMr31l zy?IuaWr1b1C6Qh&Bhcu@aBLMON0nVK{?)nFv|#)@2Mv*`vXa_cRdppox9~VI$0f4C zucLv+^?{>r$|Ca)Q8<~QMC1%!@xP9iMD_?u2qbNZ#hqe#61iM`V0k`6HMb>mbRQMr zYouDJT=j|sc@Q%1n}4RUO_w`QhQ=l~h*7tl9y;yz4#5k_a60 zC~l|-pc5e6Px2>yBM_v6UEaL%f`bVP?lwZ`7FNc9e5|Cz5JanO&-aI5Tbx0D&S_Tt z(`2K)0U#iY8*RbjV}Q&Oh>vPq4>dq6diL-HSRKt}xoL4_3_xK~GccF|$>{0tPs+|7 z1I@}Ift{j}5h-Iz`7~Pn(2xx1Wr{g9Jq-!`Kt>JBJ6r=4r2VfaH=DAEJnGb}&J zFi=hi-Uh@ftN94d`NajF0BWQ@HQ;`+A%YDKM3hTBbp3TW!dU<(AMm;Cwm{l~1Q6$4 zU7}ST_tqec;RiV~vw#OUR)i#B>XEe}UVY8BbZjB*(SR1pH@O2LDlWnFokmn-JlUmW z_m10NjSvnqWK~27IHTUGA9rAvZH(kWiw1wP5o0(CJ!7{77tSk}K08H=mzhywzlOGEdbukGL{*uM#ehxA&7vM)#S2ue8(6-)! zmH-JI7TVI2YkH$nxKHvz9e!jfL=XRt&RRt?Y& zz+|3!wfEE0gOXmbxvvQLJ>nrB2FWq-VH_?y0uUxs0Jfiuho`w4P8?Vbke_S=X+Z!J zAzaAM+S(dzK0j7FOUpliI`|8csuAr(;hD@-3Yh~yAol004T{@BA%~$YCqI0n*Xjb@ zcp-!jB*`p#WJ1HlR9sj074Snqiw7uDbi2UmyTOr>9`jn)5?>VD!3V%UL(Yb1HrU_- z1?b(qs9)+ZWDcJ{ECj4HkTCFl{a{clgpmDY;L8EXSW{C|mnJ=nj2~6Cp7Vas_vWbf z{`C=*_TPIg)6`Y>le4-CP2RD14>7-g*haNz%%hDYH`~~%)v3|vhY>Y`#s7Y2n3^hS zYMtl!9f_jA#0x4xnZ@AMG;whJ^QsrS#Ov}<>&C$DShN%2K{?6egn+s62sZ`JIrvax zUC-B@Ompa7esF!)zR*JYK}I;-erIAc&Q2dFZd!9MFI^x}IY{S#lmO5}5?0aV%*+wM zZYjFCUDMbV?!DLhmYG%*G0t3q6j}lwdWy$O8CE9vnp1+JGcUoeHqo@^yX$7I8L2{u zFv)M=usndMLzupjvNB%t4dj()GXJ3#s)DkL8y@f zxR8pH(wEh>nk$%=Dqv1b^2I>a#|HQXzL9V0dTB`;kUrfY^zXFD@$Cc1WWu$5^`y4y zke!`Mr3<$=d+Ht{Qkaq$G+v?K;Xhykt z_QQ6W^ZMVatfM0f7>vCQk_-jl&h%JUujIC_-)h#oGWp-nt9-Lx6w9X)zEqu13%M-t zD2h4g&nyjzCWu#*lzHR!c{l+){UjokKZzU(M$sVtAH%GSpev$upVyggbWrd=_mZ;$ zXFo`Y^uSFZjz}V#2fgdw?63IT#lP@HSvg2cIYvt>N_q8*)=8(!1DUjz=DSs476iG~ zaB_Qd({^$ct*D~1@1-UC(wdBTAy5Cxl~namEP|llX={IQyu-D8yLjvO>^`!Z9Gp@? zF++vCT$z%;RrrypIKz=QOV{si`)H*(2{5iIdU}Y$z_EdPjLo0_4^3wQRn_)&@h2jp zprlGkmw>c%NOzZnAf3`64bq*`CEXw;-7O6wEdnYcB?8j$t@9t_dtT4h zwdVZIwTGGc^9r{k_MEL8e`0Lel)pAHb-79Wo9M~G`92X}@ZDh1d{M>Pbpt4k!exV( z$DV5mY|zj+>O115x@!B9kJ$zrg820bjKp#9Wnti_LI^1f0Vd*pz`g()Hk=@O6O&XR z1$1++fi%ms&oeXCRx=D)K4+FtHzce_zpK|ctWIWrDGP21bfWA1#CG2^oW?|^>+XA# z0bl`FWf(D=U`;`IiC@bt$1>UhE|l`q`LF_k>Q@WW%9je=kj0(dcLPV~mtN;P4TvfB znBUcYtD*r}vfl{Nlne|EAmiw{%xHk{ zoF^9JUdVx&p!~vo=1ZHvzYAPV+b4aV#khH;40B8jyp}Q-Cx5H;oD3-hYgIp3SZ&vG zMM|&6zT$}##?H-cj{A{*{AV;t)peU+`gPtTUvlXe+`kf%$FASZi2r4e2f2i<_wW`v z^F?^4@IzxST{KeP2o=_#UrG1xinlx(kuGX?m~$`eyT*S${Xvm1cxRz}Nu+dULDQa7M`9_E7AvErYMvE{DU;%V- zDxlpb83aN~T2?kb{>Z#oQD)4`Fo5M#sk_}98X5)CC65$x+UC2#cMA)j*LJ9cqDGV6 zX%1EnRgDQ5$5Kz&2{qR7U|1O+qvozW*O~^i9vX4jvjxEl*;%JGixUi`JlKzE3$rod zG~1jifaTHt`to=hOdYQ5SOHQj#FY2FSSQm4$fE1jpT-?kjb~hEhG{oD}aC>QJ^sQW9y;-v5Ml?K7uYbL>^v)b&#RS9_BKunIKz__u z8%w22uI3K>H&M9Wx#N1!b(>6CO|3`C_e_ns`4^=gvsTN3wzl>Vo{~UUv&&X=ddf55 z{^%L5m$UP2p4B{RijoIbAOH8Zqdny6HQ@fBJLd*Un2e|Bj z0{Cy85+M`(r#J3G6yf48To8>N$p8R!9DO&+|Wd-v`Qyv`mq>oKccJvfa{e@{nE&FXbx zkA_9MRQNHY0wssF@BBXdbyU@hYE!JQ&#|XbBMPy}%#D8!0(9bO-1N%LF2jL~Ddf|) z+hT?S?c~I+ntx6gGQH|^s8uH?i95;A@k-=$?9raY^!?)h_m;(N z%`@e(q)hk#y94^a>@mr$Y~K;w)9}*wX4EE2v zuAfLASPy&p2OLVB~#nH}U zGO=xAj)FuVtOqkQGw+g;5Z@OJDEcQ)x^`UiEQ!FA0?>lh^be}j!ozHba^?jzHk5M zn~3Y~($G|aoOXu~vSmGuuif6Zfon1Xg(WX6NnjWCZ92OaA2f&!X;-iea+JN!xsipqwW*9cO~c!7fnopJLf91+_mZXcRrD&-lD9A1`xSVg}7kObi;hZWYTa^%qc2 z3o>VmOQBxeu-c+DUEN$mQ(N`fC&}4xbw69d^hjIhYz}u@1cy_hl=t5TrxFH%`aB*6{QSdc|o4^ z9Y9GOU|oYB_F>)zdar@?oW{l!sJ;IHuLx$x zJ514#iGQwhE4yN&f`wbPb&vcW?8RFq5)_)E!hiRq2`_olZ^_**B8aHfa(l_vb*%NA zb;&Y}u+-mW!G8N#FQDK93yB?>P){#?S%101K-w#HZtN?IXZ?wID!2P{<`#wJdSEvK zE&}W^JCcUl-)=yTbb|W@bqsf~;RgV^^?`teccn}14p#~ZVVDfYQsL6RoQ0?C9>U2& z8_QK+UoUl;r4P*s+}sQtW+MolMovQ$1m6%3sN{{RS>hJpfb`r}gTQ{tzP#0Jgqt7qlV3FHcq~C#ZS_d{^13Ij(^?j0~ zcI(;7AK;&Y{*(#V4RJ=Joz+3Rpps`cN3HPQ=1IYoLT(Cn}-DRgyW zoaO$L5K&(M2pC4_I?kupfe9QvtlF(@@pk{z*YBgUsVj5pL`tTM%VwDM&ZkUrBnj-~p`^JR)`~y}Gs(-LZ^kHn01=UA#of9a@+MvBe?b<|#t5?6I} z!QvVT@7!a)0D;5u^9!@4TwSzQz@}B1bpnU3SS1fJgHwgP`Je=V{cJ#J<>JEY?BC8X zY!~zZy8#aj0>osQ77Nm@BL#y8RDnOU^6Ys=Km&R4;nfYOVr*Tav>{6h2*FxuS*6iZ zqXXP*-yEBw>G|OE1Dz<6=vg>Wlpml2{px-E(sl2vcW<&j4@n_F1K@5(AqW7FSFrIe zoNJPb%)0#E+R_HF!>1z~fWLG$<9_&QirN{dC^wJ?!)aXbO>c1CXaQsmz)-Jj{VN-c zMW$$GMhk1+)54G9MV*n4`ID?Gn}QMV4pfE@z}rzVlcep*{R5o%Aj06nAF|!Xq+}qR z!Z}a1ro%~8lFASwV`h#9+x4J_>?#)CUwm4cEYLT=)u+)y1COwpNFiMPac8+D8DQuX z0AbT$IRfsBg62rqkbt)RdF-QyRX;}yi0#k)L6I(H;IVmF81sAFciEmIU~H)0HmWtG z&~k16wszZVmV=d(v;tz+wSN+jk(VFAUn?i7bmJdlrkgJbI~-D=5@rng+EfzB*KnJw z%!@hKTRw&?&jQ0tLLvXNH=QyrNnG!?aAg;J-SNIu>ypsn>w=T(KP-m|Eoq4>xAR)l zlqYGo+t{f*vorfaY(nr}_n)ceMi6n}Qln}9yb5A=U<&z@6r3aUj4XITy2N((>hHg?Fu7C(ngj~h&x zIT#H)knq=(kA5-IJ{MKVZw$?+S6$-9{!e6^V8qMU&rkeqK7IJuEN%1Hel_)f2P2qh z<4!B%-pjMkek^d{4c5r}`U=CT?nFSqU}9k@ZMpwE@+hgODCp?O{`irr{pzSnO<9?B zlaFbb9TXUVQvQN4;duB<1rQaX?7iR((}qP}uM^g&s3?Regb6W>YSA}yo>hW0hd=_b z)rREc!M8TCJH~zpv6$L0Pt|zBe`kc4Cx-E;_d4l@IeGd|yn6dTM%EEd5<|4Z zwgDz6MGpVyw%|14N)L;OI8U(3aONS&5O6>V=;~Yx50_Tn+IJ%$A?dZPRrqtS=_A(a zLPvx@(tB#p&qEc-SLKWwi}K5b?=Qw(GbZ}U#YH)nBW)sLB5Vnq+IjhzPl*i)wNEC# zekIk&!Ig-E;2f#%g#$bq;qx}p&BrFGiy zkpNo&M=hx&w34HPLEs4e$N@YVYTDW|US1dPj6-v!;l_tr6`GaU>}(}aGhlxh2L&Ug z(24&?4y>kexssF&aQze^H4t(kdQdie;C>p%PZ^d<9vI(rqb%Uk zT<7Gpa{9BuE?M$Cmf+1?^7pf)(&5~y8JVKmK70>owBJ24i%lhp5qXp=ZuN|T)9OL# z;@z|chlJRckl5b~%6eH_i$i9&D8BkxY2R7s@W9k?7a}OSWcy2i`#H3+g$SHeZ0Gja zFmMsl+AN?xAejoR>pxhq4X@M!TkklKz;Vw{hmm4d3nsA`Z46Q=iH8P7L zCT2Q-tNiVM`$YF5;@4#~tO@>}DT}W%^zEm)J-^Y9f}5>tQ?8`gU7nuikxzd&S%;##~tm2!KW~GBkpr^@Lcf_}n;%X;{zd^^apRpTT(!^QOJ4^VE)4BHxd? zxTXWt z-rukK-;4T9I5}1DWt5c6L4fC$r|j1I1;QO(}BtvIVM!?5T4= zP#T;s%)*}7{>0>55r>%b?&d4J3m(DyF~suLpDBYc$(Ea@8UCz0IQf^ZoN!@f{bvW*CM(bApJt;EWlk)p0T^2e8EYH8mW2Cj?s0R^J zybMeaqz^AvXZUP&^v;OP@NG1)#w#<9M-1VjPEH&hu*^5XkyH5(Xhg6ne-Er`2H4BP zM!@TPCE!oKr0NKPAh1y4*eW0IEo^mgwZj*O$HxbgQe=i)=}_co0g4mZN(w@}t?f^P zs~-k1EO8|Ncc0;YwYBF(WyeZ*?gZFv=X)J7LndoKk`1z+XJeruOU)m!6JcKHu1rv6 z+C^p*di<@a$E z)#v@h=<$`a3B{Ut=%4Z}o)zCPAP>dHv_6cav-s4Ds-TL`+@e?4vaCjH9-gcG^5tmN z%GrY%^KnVs+CNdMCF*sY4a!=&!#%nUH4f9X^0d=7%$FU4(7Yi_t*2{7cwkuP z#sM1->tMl`FN9%y0EEXTXU9;>xGmW-8`$=++jQ+6j235*Nb1`%V+mfc(yHMyBn~dP zY=L2#HS_H1i5NC2wai%n=TH7jMKF|j0i^$&;!iH#s@c9DWv8sJ&dDet0S^WcO>n@i z861)eCvt_IUDYcj&Y1);eu((&R73iqIEf~bhX z&~S&3VcSE)ps`d`@>$SS# zu2A&**<2kqtjiUFx6M{U7KPau4`Rrg^2qjtukUVt?i<46cpm3ZzbrPm(?&lmi@k=) z?V|6|mIWh>V2z+V(N`oe)*H+%E5iusVY7TDnKuc0jGU1XrB%I$BpZ=l#q%AgH}a%n z6r1-+HFfo%KVj6Wq^6;{RW|bp_TQ4SG9Y=DYJ@kA)@kVIYCVpuzzSkb9|gH4noM{M zzGZW3EB!qaiCOFgczWj0rsI^6NvQmSO%2v#AfIV}Uxnv}>QvA|4-y}b3+!xIR1{J! zflfd9e7$GyJB$L2S3(t4jw>9GeM1`=QEc6#C>Sq zAw1Y5M4Wzi+Ra^rG?$+I$z7@26`0MUs-=dHrLdLz@#_BQJY~Ea?I)fv-51_Flt>&B z9DMTaa>e0djtzDh8AZiHK!Vlc!{Fv&Zg|-b!C0^A)G7Ca7rZ`dZ>3`$D)5i44bX*) zXS0XZuZ89}yoJba-nceVB#eIYpZAL2!;ZP znQ3bF&atX54|bR`XzO_iSkG1%Ppvq-Sb@_(udGZlN^%vK8zcBLx zaNfk+JRvdhC`dn`&uH~<6FGHDL5BjU`i-iq-&NqbH`0`x+hs zW|f2e^6As3pL(4=d&f@ywzc8eRV+#XfY!F(|49Rigo=f=bzXC`$CRWa)PYdt41t{C zJp4{BN>U6&Fc9+_n!HZgM@L6hFfx~IHy~95p1+5zRi0#C7%J8*V;n>fqDcj1jg8l@ z_q@G)ZDJn;{S#u`BjXz%EXL5jL!*t>7JOSXvD*Cy%373%dTuBa<6Qrem)i+S$AV|D zP{hp@w?u`qhirX>jb*u^!oF?~G)$WajO&W811+u__Ctcyr^B~XPtA5s;Epf;bO3`G zxYXt33X9lL!WZTKi1N433P0y_$LU*5(+L$<>13g3ILhM@2t`8Y+Z;=_$AQ)ujG|S5d_t6}#%7)!ehk#q9V<%E zeIRi#NRM%Hb4$&ULXS~d$t(m1P9%W^n z;{&1ig@UyeN*1730A!0bO?|~WI5>zNf<rHPt_E0vWvCX-faR{m1vr^(~*gsc1{j+O@T^wkE8Oe6LwHG&o`+F>@D+ z34h7Fu2p;c-`}6W2S`XtBw2tfBPW;V1wJzK%W%N{!*L(%2DHpM$2B+WczAk1vI0U- zs(e-Nf`0sfc`z_{Bg$z+#MYKHMz}f(h7A2?(fiiW;Z64B8z7VrY`(@`ioAX=z zK`TV@mVd7~;pD{a-|oEF8?8r2kA72WR45&Li^@4+m^m>JbJ?0WNZisTa}R{K7xcL$ z|3BC?F3iqb4*wm+WyL|BSg;Qw9)7WLmR_RW`Gg(RGJ3@=ovE;t?(Ry)Q{&>|Az8!W zWR{+cq=z!UL5x3#=uWNq*ncQ*z&33=t{i{|7sOG;OaU4B$hR70S}NL2?~wIW*>(7mZv)HF5ucKC;A-ehcieD}qz1MTYE@OPiz&0px51%cNV92`t5k8Sw% z$oFc)7pRRMSRZ=9k8Q`Ek&}u4wYj+pkCY0(7Ziu^_u$ZZS1OVmSPM z$P+lK`8ZFDl+DzDR)c6Qa1SppFRNUd9~~VzJ3B+5x>Y8-g7xu?3A@D8qX!r`Dfl8==95iSRoTsPf=QM_JN*EA=pv*D}&yI#Q0pg)qbw%*Dw5BB#*LEc6d&{XK7tdY<| z!+;SeT)y8HKDhz+T0`%MO4$rTMQLerkp7{4Z9iY$Q`ORXq5C?F{l}@rX*{;!2qo!n-^11g8ci`EY4#&v#k&)(*XGs^Bmjecy>zHXtkUCU zYxl$b0U#QqsIlROTUr{S^E^gAR9JE-|4u_y21|>qtlY!^0gvk;z486q5RowKDU3>y zr#(A6IP z#7Kl)GnyT4kyXt`Ks^V8tnlcfYk%Z~%%2T@xg&gECZ0#@<>*+9tEzzkY*b{-MHpaJ z^b-wTY;z<%HHbEKOHrd!TD9kyu;4^u5&8M~&@1wKd`BnSTavNfqA91Mg4vd#afYyu zJ%E{CJ7KSxu5O+hExT`I!rdaJ!PV_}inJN9fFRKv7&#FL%4vK?hJblk{OlH7U`CZL ze!U~v#X3tov;f1g_(v-EumSuJY@3;3o%85(zcG(Mg0w#1AJokxN~cLzgd&UhDG zrzLoG3AE;*X2B!!kVRi$6V0Dm23nE7ZF}A^FI!w4K(&InF%ck!wpvqWcRKL%o19{m z#yQuWL%4(mKX(5=3-A?{{A=;Xq{{;)hEJh~f12BT#`L#;b_fYcNM%2xyvWgr_@1A7 z@+nC}peZLl^7{#~Dna7@5jXj*J7{9Q_xnCEd}d&yto0>sV0--CkiYxH>~d6eq8Mh{ zn-Mp}>ka#-{ya|GoaKn7D)OhFmu@&5E=q-TJ0T&WHy( zCiQPD=qS*=w*ng`010?_&Q}geHl6tpd96n3H?&i)px##r(PM;YO3a%jQ`x6DAaqr5a^(t?nF|nVAqG6W7N;3vhn(X8Ow)Nl2`U5A83E>WE-N zorLh19T&qL)KIHh-DJf^|3+XQ)U>=?O!|v8>Myu?h`#osLemQHHSr;Fm3)Y)Bdb(G zO(3;Q_YX%lR#v|mtIW^Ei)OVBza}VPB61Mn%CNQs$wxLa)CYmDq(7Uv7&3U&hp)fx zX+*(29#JZy>=DoI5z8Kqm@hVVC=7GqeUjhz`wqkVy$6;b`EhWbL_bqC;k37~4~+B6 zmMC=FRb*G;=9BE04WaZ?i~sY9J>fKA%Xz^;>ETqX^XSZa1 z{$X}tahi63YD%aK?H_9X zfA9cd-oI~D{o;ep>wNQkCy+xWJ#@!3v>-Eknw=r&#I^Rukd*)iN4vG@>!j)a+S(B- zPz}<@QhMm`x3X7M`7vo{&!)(=mGFxVS3*)&^z3?0SxY+HLXbg+MR?urde?4fH2W2Cvv8XCyT}IEbe?i=c)|(wx2i1 zot<_^QgH1;@6fmQ-e_ioDBAQijhVi%qa62FZXt0##~Y@fZ*j+&&@+>r{AA`mIQ2R_ zI@*Wa9z0ArGR6W4L%lb#1Ufm!1@JU3L7CHML5eQE|E%iyjJPtBlDI0u9l;%d(zOUS zAh?d&H0G=IhL9trrlywX&4OJ6W|V1^X;wqAdS@t=X&7cyNZ&Ae^~x_RE9=e%-mb<9 ztlTh*0+ct{{A?SD(?=yeJ^7)^fGQD+7Ag4RehAg?ub>O1rKeN<9(}~3ANji?CM?Vl zKn38&ghgweoAjXXo|>N@g5ANw#wH*9JNSpb`YAc(M@Nnn4acARve@e6#pI>pHMqvV zJ~lPYafB~#6wyD6{?tm%z#*T{x5P9mlgmSMKZLt8bWus)1~cZN>0g;y>>hox2;6n_ z!MhtzT}isgiBwF&o@BYHNC@8zr~H;|>ZK;YgrKFSrfi%%fiqU710np4QmTP zKB!O5JkI9;umnu_1=GW565j6cQU+Xwu(@(gp;HsGY%-z9J|hYUIET|96#;B{PvAyg zSX|_F`psEUkrO_vE-Vit>3B2YmF-XTV1aNDHvB^}Z{~x2M)pN_1k2QlaA!1t)ZIZC z|CP5JKZ8y~47Oo-fPVd??>zKqBfv!aD7cTBD90EU#>UFSGngZGyQD;Dptz?iY<)nx z$?8Yss0ob>Fd-`K7C6M*uPGTB@qkk3E2I_rdddcA%{qhj5jfXWTc-)%xI8YWi^At% z*N(@D&qUlpkpni!OeQ7+#LlrzPVeuRRLtg+u(M-NN8wcLS2fZiygRc^=AK`29|jM zw2FWS$Vf|f8db}?jqV+DaB||ag|w3zvZWxD%f*QU(!U3XgwVYlY13yB6coe}NrvhU zO2MY}XnvUHX!5ny9qcd3GgMn+N9TKUJesp_2YfYB=-9Y|St?%2N6!ap=839wZejhI zZ;~V7ne9#);QxC4*kdp6jn<#F01XrB0j1QGC*j>6MrFJ`$wJ2J9qM-3D_-`vdB5Fn z|AQ?qB3|)uNRFf0Oy$N_a&nEcKG*zDarL#=Z<7~O&?VA>&ax$=UZh1``Mwr&@_is` zGx9ksHP^5FJ?8xWrSqe!;}}2wd`o+^SpS^scSAdwwrKFdf$`x3BdGquqBya#LiXUq zx?bl?<;!8ls#YWpZuYZdIMYXDuyN*m@h)uUm2AQE4qBLvY@gdI~N9hIHf^3i-`kQDsd^L5o=`7UArQ7_U1; z(|rYBG#3_Grk9udpz{XxuBf;%rSHK5b><3c7?Kg5i+?}xqre6BB1|uWPQXOl%wirJ zhyw!%&!4pF!}$ltwvEY;3_Rih8y8pU1@3Rgl8}@%x1%b1w~`s|{8VmV(x*?K!j}9n zqR;;cTig*b*B_`X5Q!VggLrB-0Re<=>+s%d;Cy-Dr;jCq9U2j_J~=>Yx|pD3q^Quh zb?*snKV5GK5!GxZ5O@Io69NOItW3|qY}V5s1(o6W&$29B5FR{tUyft2v`Z`6N-M7H z9UCiha2OHh6f~v6-O$X(@1V5$)kjd>>e=b7Q-Mo!C5YZg8ey`5KsIGM{Kv!F#&;uA zdEMIkrfW+Sffy!?FF~40sLer~aQjML z?>;CemqL<{^WcFz^bpjZ*Dlv-OB)%H$Ct=)+*Ey-luU(=g7J`v+>}WFRF4u63Ye6^ z&dJ$}K&Bp^o<$m}@us}Ezh-A=YprLQp;~VQ`w_9!z@LWwYa}IWNPyh{kdF&7Px zj>b3ccXSj&OYvjvB7C>Yy^Hv$E5dC-zO$C74R)G|e5j)9BV9kcL9Ic? zIUrU&-E!;HrZZC4d+k^o7z1})YlagzPf8kNKIKBq%j@1xs*4uz_T=P5S4Ve!ZEeD; zo@3$tN7F~djg5E0HOioDA7?-q~kd&JX}w%%+1Yf!P_JL8e`2?Hm?>( zs5JIm#YB-C%kXna$)KV*_N@&4uv#rihtjvb zT?p#;;>yaOz0m1Yo87~*o$v1c8^#TX2!cZ_0g;^VA~+H_P=&$6;W|Fa_W-*;Q88TM@vu?}yxsS?qxdh_Z#|;1Sa|SccylwLZEZI_O*3vOx+W zo@m$ze&Jh0rAzDXA5Ev?q%$rjGTvU=D;-BgM4Y9@xbp3C^jWR48JJQk;-gRcc%Ams z{Q&}@4qr+C|2#6IVH87kZfQ7ym<9VC;ia*)}xR9%Qs^7H?1icxn!3QIdUqG67O=79T;OZi?CXu`q%$Pj=$=yN7#@6wa-0^{8 zOm=_t4YYD&>8{wSV$BMpm!@pIu=}p_cV0b@?Rl15V!6}x2%ScqsQ$T#B(Boyauc5{ z!YB{Q9o8kapU&rV?sadk&rYZnw9p)Q+NS%bRyX=so}AE-ust=jgfO`5Md5!?I>emRSMfz<1f6Sd)MMLFz!F>7E;3>zhNNeIV|9yu)(0O1l#+;GgMKM#?s$jUwHx4gqSKI0wDAY9v;VL zeMsNMS0MkSvC?O^GM%IFP;W#@26VlGi8mBeR=8JtW)*p6i5Yifi>+u;#cGb*0u=vg z9LHupjL@N)dhNva>o9k+lwLZFhE0=y^%C_4@4N53g1E_&@S~cZNOKxZlx!Y&Y}$Lq zDnF<3dr!_Vt@IbX2!(X+T8l{|9bH}U;|tr`GUX~Nob@-qPq~r3o89Uou+C7H0qeWB zzfaP^L_(z(T5k?!!U_=4WWth?x%&HYjT?!+OUDPoH}e@qsV_nMI5<2Wv+x)P^#(ky z7pEA~ii%lWyb%!*EvCFXO`VzX{<0mKQLiHiPflHau<%q6VPDP0?M}+ zDu_k_MFRy{SxP~wOsHa!V#=T{#dJ7XVIkl^f)Y$v=%bV1c!RHQ>-XSVh7A8ma}TIx za@t>$fRRd9QQ!_DzBfSKmn+SDY|DZ7oTO4!%Vxm#-j9JJr-8pbXj!?82JA#;uL5TI zK>BlT$Mp*4&^Tzmiy2~FJ|^J$e38qVnb|A<$WjFRMUBaw>%T15$XqKoPziE>6;F$( z;DhT3PsbgQ@-BWDT+t{A?4v>f$L;decZ8rWdU9?uMi`q!sZktEbf)xdv2u|R`n#Y$ zl=bvgu1CF)tQx}#t7fy&R#P;!093ySGhw`$2HK~D;}S@6@!O~2Im@P z*3k)h#lR_aL{Vs&ic$cufjh?nd7rRiZv91OY)rJtO=lJ2*&3#G$0l0SxIpjm?saGg zL8oj4%NF?;*m?>M4yUQ8pQ*1jhf*c6vHkCMpX>(ucgD_)ek;B$uB?s{F8f^bz0;hG zz9Z3aZNk0```7pK4muOY!r zN(Ps@1%*&8kE4~Iw^8_*r=cb442K?N;7@MWO&>%JG?j6m#os~|H_Vkgdvv2- zGQcCA{h~hlkG&H=TP}UBYDr$zK9OCGHN`M{JbtLx7mLVD$AOmxsyWj^9RXzN%6^J!+XM1|fpGynbjS6p3{cybAYVm-M4qNmBeH0z(W zs>kX0V2z`w(>*iXq&U?*ko`$ozi4_i#SE`Kgr)5ecWbt41LoTrL24EbZ?lvvHCW9G z0|x+kN})+XT3R4`xxHWdMq2FEPfXfH9eP>LFg*NOl#q}_kVAiVfosm8s$&K3J3GLp*mjTWV8-d9m>IYB-KTH5*?Zqt-EayxIFs&0ne1TFs(baMXR*`oETvaCE60-}4*~B;i=-nDhS^R*&Al z$!LaZ3fgqL@Ac!WH}|6>e3x`a@;)gB=}M1*-eW$Nv5HLGv$V1b&s`5{8)^TWScQyi z982}6&@7{YO<=9XW#<$*-wyUu|E_O!);=lyEA#Wb>!bBs6f(F#xPko_8yCm+G!yD{ zL?o1w8VK~oI^US^`oHoGSRglroD)4!QKI4<|xE_yP&BK!igHX7Nhqv9Sls7NogEa+ZA`d9M~CUlc(8K60ShP<3C!(Lwpn z)PyM&2U~FXh`6HyTB}Iv=NFF^J9p8g2)%9E zU~CLxe_1l;7Vu+w#KdwQ$J$rzozOt(rKzsL=G%qR%-7QI`LJbkXR`n#(Fg^Fy88P4 ze^tH;^W8y9F2^qow(DPpe8~58%Vl`-B&WVU5ojEzwca~AIyxXdrzD*N`3$l$46@Ys zy>x-8t(P5fw6jA{TZNHT`RR9bLaYqzE-1L2zG6KnG*Ohib%&8|wEp--^r&TUFE20Q ze9RWO+WRn9OJO1APf>7$R`7?pmvK*K-_I)Rzr#_nKqYYT;MMUYJWok~Pm;FcO!SQL z=SEFFCq|p?+5ujnNLKOQw^`3|#{yr}s%tBw!iN-W%)yNgYFk?r=vv8s_PHFt4_^@< zkl%XHjJ9*}^Z7mZO&I|%Enb;@v`?ChSb|3ZviSC%mvj9BCnywD zD+Cp2YF?OpQE9J~`*#n&*}GHy`!AO~{?+FYp(@;1e|BSLmXB<2z6-88V0t_qx+4iB z&EnR;>R-wzE$!|w?N*f-Hc8?pGhgySznQl)tI=-5VgAn}6~*+M?_?WvZ-y26O5t|z z2hq~)D4p|aK4m7K`*K^2P-TlVRQt)Vt2324gJupB6v4-dlVs>&zO79Oogo9t<;D$3 zDR)ppQH(^;))a`8#eSVbbqI&z{e{UF??CjWmvM!f4Y);^HMS0PURLcFKh`ov5HpN) z@g*A_UC$0J0Sb`R*Z-P7fZnkOc^5dZ^)!jW9AJQLh%8J^slwE8T5p57##3l@Rg!pD zeYT*O`E2zJplnbu!CV!1aw$xL(8I{s=7&#p#Nkl}&>K8O@wKiGX~s4-+xc6Tva)w! zc(pp{#%&K3`@1jyKsw4~f2|Lhmj^H!unTNNQ8+V$AA-9oigCuR`jjGPf?adIW6P;v zi-j$mm}1*a#2ZcJO_xkqX>cAz86Bh2%SZJUt2ns3Maq4_W^q%I6}W-VBqZFUFFf^Z zhDYQMjg@~fHeKn2(mwg(j_X(!uh?jllREQDB2_tH3mdH;el5TYEb+y5itPy`BdySs zgP8z#!oon-1uYX9L1M#Y+#H-n0Bq=<1)MQF9r+@6xP=?pG%f1Tb3;j#|D3kge!Z1? zpQ9vnnZein(|Yt?Yw3*D&(Z9GNh&G(6E6~ek9QCiQK@(Q5!g8ef~=OV)XUe)8r&X1 z=-KmdXywdI(C{W@2YL2ii63#FL|?vsEbMXV0)#=xs99e7RX3RcF9wzT>r8Mu;4?+0 zuHGhf^8-ka3Xjo0@n_ z$>de=VSR#M6W6hF7xFf&sq!i^ADD&#fVKbe=|QXhUOw^t29$88sU*<3OBsaE1`iKut{yUNHpJb6Z+GWvf&F*#55ay|hM#R`y2`o4a`X%3CzE z`we?O`xegeD>d1=pTH^6a?+fzpuLT3rSVlLePI5A(|JA^AvVVJE-r;*UkAzwmLEevQmj_;#lh3I8h~4;Xm#oS>s$*w^9#i_iy@09m>5*i3_6-r>b; zb-dW``%dbtzv=*+Ue9i?NySfy8y`3LdAs8@W4J9Tl+~bBWqkdZX_Yvpl7eP3H1|1l zP`-;-gy&_te{R@m&CYnNWla|IvRj5xR(eL2#_{xWZ)ExA zzA$!aA_lgQV2DwKrr(1)tH49*x+;NC*F~M)cY(xZ>jG0G{>ZYvgwa&Z zu5SA1|5<u>Qh6dprMq#W{603R(m3_RXmtW5?{E{%n&_A>L0 zJr4;CEPcgF0CQxV^_se+I#xu)jHOAr{c0fJF0Qmlqk-e6to?CgB5?uhg60J5;$ za6|(o1%=_1`IrS~v{oh_WKZ$*otpHZmT>@yG1O{P56lA<*cS{83_hde%vgbEpI!Z| zXH*pQsm#n#H6_8INsI5u?9^lygRIZ#W|drzL4yWZ65wzcvx0DI(YnqfrpWL{uxvmH zmS;$|VsBaccfQKHH%})$vJqZh70KLYN3@M>XM)iY`$Sgly(=PEod_1oK@NH(Wph9Wg91M zW+ie}Q(V-ysK%JOyYw4b1$`!kwDOA_@PaAH6}arm)zG9?#syF)pe~k-)9Z-tN9mSf;0n*4<*m z$PUm9uu{1pD#^ZN5|a;_9r)D&WEXxgGDdu~#L%G#WW7JLzL&Fc;S+k4P+9^g0JDe) z!87t`%gK2Q#|hxo>x?1+7KOIbOhC^Os11%j-~)@lq4nb}lp8=pAfs-9=@Gd2(*v-U zI)g^2te^mrEIrwe@s$TEFYr`&WC0mj1N}l=zAJ!8h_w9W%a`I&)&I?;qK5S#*Oo+a z1ZYn9BT{v4e{JKRqWZTM!6NxuexSb};d}wwhY~f$X!i8d2c%E{@gQ_Bf!oRYw4=Q} z3nrogM`(Oy{p|lX*s#{nPb5+I%=x<=dCIci*yu>vY~jGgd>x!!&>c z#Kqx^X=FntB*(^(P?yvrG_g$cvI`eeLPeQ&6DK9$rj*b7gP-H(2j0hq4T~T_e5)19 zo`hx}z*M$Wd7l1$3rkxw3;WPltUNhY$}0H5Xt#O)o?^F;Q;gpU%(!R>O5nO=)1ZFcRE@LH~YmPb7dkUW}>`PYqoS00bHF z3&pcr;LXv20Xa4{cBe(!Q05RhS;%=)HL|~njT^!_0AwRH{kE&;=PgjrA<-C{l;k>W ztZ)l*DiAS2Bx$$-ku!2Y3$?trW_JsuM}PlVEXZtcJM&RJC~DG~=n>@`h#@PgsCddi zfsgtY=C^`*yunHF*@6=&!#^--x(0HRNLUUzv<4AmVcq-8TmadA$Td1~U0s_T>Pl8k z?P0U_5f4hYFAUJ!GUYpeO~J6jb0AI=&SVf9FptkKBTO|nAh69pFP?3B$ah8t5?fF5 zm#4H;G={44m)1D%9xxy4oLp`kfoVj3tBO>Plx)LB?a^_CPG{?q`or%ftcfOmaD3__b zwfYb(oHu&R@zaD$yynM6-M;yoe_0 zd&P^?Bj`ylBmQJ8!kJUTm(#)ub0qCP)8hCNz)6A*19a@`{yJOWcj3-KPG2b~ps{MZ zi;9ADLE~^{k-Da4u+3XlRZ5}Kjqz-_ny){Reoq%^)X_397`Lu!>xUtBz-WM;L1A>J z2h$9}bW$G(!1Yq>QqIMcc_87c^jR#i43oqe2ti z=(bM@U+Tet?s(1)nAe5w->C(f$sK$t;2vRAIK-OrAp8^2$8Jah!$1JZd2|;EuEUItu87x0Mm5wi#8AE0!#4FZUyS zWh&(i9HoIyg6!-)S8c`KZ6dAOqH(jyl@1iA2*NDhn7l*V(dAs?uGk5eh$8*Lz z6K(5XyJ5YMmK0W0S4+a?wsbUu6x>zN2GmQCHl{QZc#~7f{FfA-lhBx=ni}Xfy!_VU z*DM9|Y;V;tNx7mx5%{9x3dYu8k?ov>f;u1hf(bKb&nEEJ4XE}V_8owOTRr%?5F=Kh zS=*TFFY(|3Xwo*W{18%sKYSW~1dw|Tugq8>82&7&TGedr&Jdk_vmTts zMA6(L`tWXBTU(qH8v!QBK{@nyfmftp=E>Q}ZOji0X|OdNVUvXUiola7!Jw;Y7-1J# z1QH|@{WrN7AtKYBKP&fEgV$lsM?8$_`U_(@rXc;=Cq@QDaa-Htm;o_8J-yEk-Om27 z2`Od?@QP>(3Q+7q01I+{*@=u=V`2Co02w;oT}~i+b=Utn3@eCu_%!X%YXf&Ra}35L zKvmI+9O?w2a6vkZ*(2zgkQ0yN;zZuOISveZ8BYyN)I3a11WP92U)fit?z%3JlkKMR zrLhCi4SXw~lo^6{3`r_Dv*HF6Ctla*D>3XSSg0t#WBO98Un}(#4+k(Yu6tvl$T*-u zI7eLNxr%82v>2CpsOx~6uSR5WjXx*m*)N4bKLle9P1>apXfNA(Pmi8YO{A@>9XO94 zVfvYunfVq`eKZes|E1cb=2&XN z>1>0Xf?XjcGfy_CUt-y#lbFO867fr4|LoBBwD+C1QD={>{c&raMpmoh1t)*%4jcW{ z3A2FNmf|gi)I7p;1p|M z|7LlH$>b1&D(8Bg_Y+cm?UQEF^D@cwjZL!O9b;JYStU!7Llb=%94U;b{P=#Iej88G zF;^;E+CCAc87-#1Lul;>k8-nRKmE8Y8H29&LAgPX_@=%Rv70f^l^7WUT>B}WII#wq zLT#^!ks9m?x*GRp8t%nF)Eu48pj>>%MFB7hh%ZcHVq(0hpAaJ_XG4E&B)qrQBim!L zR_P?ujZdEp#dE*G5kQ0mjTNeKm#cHnmQ8FLW0=(=Y%X0I03O33>#DKsy^~B_ZhpQS z>&J1^H^s2$P(OA|cQ7r2kAa)JiRkLh%($??6mGRkJh~GpsHKajsE6Wq;EwM4FqhZe zSYN5=IkSuCyU|4SyuYoU&0pUQU(9D+ zKd01&DTM?5GP{DMzi9YXYGk}q3FtUf{7%KIcvI~@M%+8)eo5y`}9+n;@4Ic ziI!a0%GLg=;IGuu(lR)wBOhP1d@z2?%M+xfb_lc;cjF;*2Q?Z|5&bGdo1892Lgw!8up=x1Zx@oVLZ z>*;@`6sE-__Uu@@RyI(~^ENZ%ftHqnSk0s&y)VORCAULDsxQv+N(jF`K9T&aZxa_Y zH^xnW`dgiMeWF^wo)P*hs#8KrHkPbL8T?Xw0hms~bA)E&E9VbkxB&a5Q+S88D;QEjvv(@T^UmP(#BZ7h^;QcrV|4)=7{BCL6Za;phzyS)A0MCFrM=XUAD0z#MU5N!< zyY?CWS~WSon4qANW?%A`Bd7K5ZO>f)$QJrzsv~C)^yxY(48X?h%o&K#;5q;yv3`@* z$?D(RwZ_x=!cIOd#NfE@MzPbK4JmbXUh*d2i!PfLjYOW@=Lp?5@zjcYpGr$j2}dhp zN=W{P0k+G*-~YmjBL$zjBcoy zoQ3Q6raiwz89m7S+1AD3c}YzFZwmX`+bIp>@0(Kj%b$dPwd$NZyki%YZQkurI({`@ z*9=iUzJSx0i|^FxJ%c6Zu@f$I5|6eex2Rn-;n2}3yBXDDzQe9&i=UsrqYQ_#va;)y zopzQn1%KVSOmc&eI+IiTZD1tN_nKnPOQ4{QTMiDpxLCkXy<)1Cfrtc&-z05!QokjIyV7 zjFirMtaF81pxK5vf#z32t&;pV<~NNe7Z%0+fh(uuwplh(@YI3ZCrP@GD97$@ z$!@U;(h9Qf_1}CpuR~;LMp;!gULgBd)v$wzXFW^R9d51KG(FpIE|&LF3}fmB&aRe~ zA~Gzrc@5{4>i3m4Vh5eSXj9YIKS@u&1ys)Xw6T)ki{B2ObK}eYUUtIb-?`QIcMTkg zrStXmg`lmbw$=itjA_<;#8L$lm#09qpgzkBaru)Y+jh^xFVF)_D~NzCqcg%LC*!AB zpv_Y(*=n=iY_qYe>uX~cFSPrndpOK!TUuI7e3$>Ezv_*lR8m*p02F#BzEot1ZQU)9_%E{rs zCE1%laAn`_96RvcIMda{{_;?qvi7x@LyDAFn0Fdy8p@`mv%ToxRMe`yp zBc45c z*UBa>hFlIdTpGK$U#^4U*@na|+qTxE*7JRukU4h2g;H2jzg;qC&|&wTWKo*UQ`R#* zd}WP$_1i@*xvsAqaJgqFd1c6w|LE~_gGrmX=R3uB`uPMi47pqpTpv=cH)M6XcSEeO zB-@?pJ{AU!H`x<3<;`KMg8l*$w99_OQu*9frma-2>l?i*3o_VS-XC>0+}8NM-Qt1w z3${hY6}sqM)%6*TyI7rVbbmB|SJ=K=>J4N50&msXmk!7gBfgNZ!vm149^E$_Lk;5= zJbj!p{y6c^T%Ekp^dl~%KKyE9x9#QrMpQ^frJmw~fSWUJiMi8er>5>2FC63=M{udI z-MlArEX31|PGhZTGKtP$4H@ghwyRDU2X5*|5)C~{attuzFKeX{qQLrqWOs?d6& z;D?iCzY}+pE7wGlFGf`)c?w~yt?Q!B6!q{A{iRVfmOD7C?8dQd=%EKSp(ODj18~5EP&KG{-8p6JEU8v zlhSJO@>HX(hR3LTeZ2f5Fmp8HWCo@wU5W~b1NGYw4xJlV8wx*pAG>F%I4dc zL=dw!0Aj{rS+b;AEAy6x#v*R~=}8_V3LgRQqxSg$Es1|vOw-ps5aiHWyL5uu#9q=f zQ$txB|M^I?YDvTRld`l--msC}hdi7G+f=%k%q1L-7@n!xd}60mb52j{u8r+<`-Hlk zuW<$*f6>vTFn`50K=^gXuc6$7wJ#sP4_J5Qw=%zG$|H$Ks1&S1quR7vdt$bHU<=DF zt;^JIZ+_dN*M9G_*vC)yb5AITa`}|089(o&*462JKmP8in3$JgP97UwX_8-Uqgua> z5nYtaiGpwTUoX1S$EE+UQKzGfNUBi_eS9XHX6m$-c}}rIVd9gb>;8g!Iy_PXzCM#M zXlQAAVr{eP{)4w@v$)gVyC=2lvv~Qzo>NNU&vX+jFFWTPrZ|xxWJNg@v}VZuLsi*D z(PQWKj{B!LJnsyS-Ey7DUP-!D_S~iFLJP@Rx0O4(Mqx>on6Ol5vrZ0%a z`<`dDq>IqpB*UtnJx${?Wcg%=qF5q%^@~Ld*rb7Jp{re%m3ksqkMLKpiS!{5+e`A{mrF)y>(fj97ajdM6 z$lefQ#)|y!x9{FH8D~g{h}2m^>l$Qr*L&(33k(GsSb?_r=GbM*)%IbQ5v^ZBBIWaCC(E zT3O<{noSaomPYTM{F2ht7mLg zd3r4@YK->@r$jz{Xk2Fe(yx@$@k@AkINzLlS^rdjG#iGurly>*t+YhMED6L8xOtNV z+hQtv6b?1pLsaNorqij##l#%o=0gYr=qm*MkHW)-g+&eiCr#38=xJz3un3}&{Yj{d zg8C~nG>E5Oo?VCYy8r%ZdkgqL!hHzZk2WRhl66+{^Y$nl$sWb=i(L4PI49Z%5}&o( zB7uM#zMP1Cn7rj-w07-U78&Q%bk_ygZNde~Tm8yOc*3E}(@0Q=gQ)iu7 zdG&X7=#gP!49jdzB5ek`h?Ks&|3xeE@bLVeo12^blAoVXLQl|CPvBdOu1b;|)S;Nu zD9w6z=x+0{80~2Hak{8TK2!@VvQPFiJYG{^y3siE=9357LMzXN2OrX>Fc!Byy%p$u zqx$8J7R3V*Mt#&DWd*284Gp8X=D79^X6##^`rOWN)Ym1@Pmt;sWa z?-u5g5>wYc>e6G+e3caw7kI2z)?uw(v0$6wC$#{oh49FjfXebk6OE>2$3BOz;d~A< zrV1Y3pASZ;`ZJlUuD&@@5O%-!uPgVhJkG__XLhnWvy49qwUqeFqBj1)^V?!b3%zDl zmmphVSMP=ka?7%Ub~3cW`y!OqtI9`o^z1%6X05z&>gdMVqZ>CUGqMFSZS9%={A0B= z4@`Mu)6E}SW1Mh^7XAd&4vZ@;!vwb#o<3y4r|K7dF#YLLn ztOT!(aAIeJn3PwpW5)PpRZK8JpTW?y*R>ch1kRD>a$jFiPf6kjQnn{P{TkcsDLy~P?r-3B-GQskKIQxPwaQtjy zOoJ7k8Q~n@4j7s$Ky*FOiD|SJ#83<8T{VhKRHKt=`9DgVFnt)z$YvVMs{0=opp59= z(5u)_Z~Eg&qO*lgy|{)`jmzVC$<`{c=Sk4YRZtjkuCYLT0jt-)FeG9Hk_6<<>UU3t zZR#z^qM@iD+FMu=0Fx>H6%98n9G=XsqP}P4R^N#IS&iVsuD6j9h+C))-NB*&JEl%bbE42jqByzfK>Ch{>#B(v{jbPE#E$c z`c+eW^LP<3c|D(q|RIEeRZ!WfF%zHk*K6s|>uV9Fwe=OU?f#zd&j-2~c(o$2FqnaEi_8yjWle=<^ zgDpgn#aT^fC(C#_L*=K?AI?gF(GObR+VF2+x>MCBA1Tgtu3=P~GHj`=b4b>BMk@Nmwiia>NvOJjPl8b+ZbDCC4iKEf|;Gm!(!-CpJ zDN%RtzCoPo1?ZJuz6_t8b#4Fh>XZ=nTxv)OT+PAJaQQGeCUv%vn1qm0f3gsMa z{lq@m6BWjdxL>$OB7C8xtqr~F|_``W#uL6H5J1QGoJdc{!^E9+85u!k78Z~dqIdlP$GG9K|okuUM@3O$RaG0 zqj2#3d&>M-!JNyxR#(RPDxaP>)fp#cVvmBA^HxE8+j*mrE$imLv$Q(fG}z`H^z_%T zNxVL)$5VKKH|DS(P*UIPZVLb+1_Cv2-Lx2&An-!AceLr8^rPf;%{ zs~5z!Hf|m95GREIvA$`ikb>HLJ4}3P>gxJnk7zjkxWq-ONcZqmQi~04OzWxsS_l>9 zY7Vud5qbKQgbG0p3gzI$$D!$;VV7W5(eOwzgG&Y<2p<_t5;Py0A6L5mh)PL405d!i zH}Ja@;kqd0`y}l59U8pQ@^VE|1meqxR_@v(CU%d6AcKKIlCN}-NbpoB2jd&qz}k*I zynA=c)~#DOv+rlF;MZ+WlW>fbox)kY}%>3A(wYF51+Eb$9z=#Xg zgFBpQYfT4}7Fhp$yW1Y^cw}JB5W**3YXu;HjC z_o2OYR1fHIXp2Pz)F$#uOWX^n*^)kT+M(#%J$-R)gZ<}nWNAd$S?@C4;j6g6Tz%o? zYd01@dJ|zuW1}Ny^>^W>Z&72@)NjjKbl7ld%=M4$^D$S6F=P!WEI~Pvn7O^O?^Z+ zO(%jcmn}H0#!L+ ze3m?NWYe(|yyIU5`b9PsC1|NKGP<@zKWQDN*|dpQ=+}?1_HElk21>5nFS!wY-pH{1 z4KEK*o80b+2M(SVHoujX&T zy5P&?H}*+5f3|yd8|c?m{t&0SqnfNGJUeW0NAdl){)n1*R_BA~a*}E_!+M&7sa?6! zEmmL3d`Lh2BG2ny^fwC5ouRjAYvK*)DsvOL1WJ#>fyco`QurD)4aF(IMb1W&_hM*Qh6To^W@Q^ddo zMGz)bw(cP(cV-CAb!4oSb{jXD@$?S!aoeVXJM}uL&Jo}wY{0EmZBI(y44?Il?fbLC zWo+A0on}=WRvthAcZ5<#d7n(+=?mgp9T}9qNx1It?tANfOWRItE9GL<7IyoJ?~fl0 zh9_R`?Gu+Zit^nO-8olm-Rz}&V5#9|sDnCPaLeN-Re?E+Y8xb?L!P~M)n;;*iMe}7 zbeWO&g{;Y`r4wQX;?19$(?2PQKg^xEGZ(=3S~iwL%B=DFkB#5#FDEI8y?&h|vnt}i z@;ffKgHsrr;pN0B>30iW{WGnR$wuX)G=CZlP3+f2cb=Z<%(?b$@16%O{t+?(r=GNq zyjb8-zstnA=z))SpG?S~r5BVk-8DvkPl8?dwCt^};YDfntr424ky``h ztU10~EZ?1pJ~}^lrPs8iv(Sln@1efQu$ZtG<@sixUo>?3)TuAK*GmDZ1PU86(z*Hk z`&f_Ay2_`iW1bSi!;-D3(X3x2?_KvkB5Rmx(k7YL`sHj&*Z7osi`;i>XJb~y$otKQ zD6b1FR|YI?%yLAr>`%S@)9MgLCJW=BIgh9#Uo zE_R%$@#d0p*a0~>>EKQGSh@t_5DEx@jh7qo<%UEvEh}sM_2xm|O`Kb{ym=qYh`Lxa zY}1R4jhiEtjvAlkTVuuhr$ORd3hU+ws*3AY_N;NWLARCHE?rGNnft1W_dNsGld_D$ zhN_X0BeAK^tH*zf*Ym0Kyt&A?`xB$z&~m8C4eI7{H>T@8s$a)4(*=+1wF}xv_ry(O z7cgCm?Y2y2J4|69AVujl}UXCjh=_?NyEbDFx@9 zse3C{Xf=a*CFFvhNj%b`j}sKrsk`g50mW3j!S*vcOdP^DPTu#a8$EMzQu&km)?L~! ze{|&d2z=&=DBrLg*LU~rzT8#cgdQ1(w~<<|3xhumwT&;C=VhGbwr@;*`gQ01vD!Bi}q1>rlX-9#YvrLzC6t}Ms zis^$cBlHGEMhiW2m}JphVhrt{myu-=z|c``I~!nl6wXzG{mC| z4OEC>+SJWIz-Dg4NM6+S*D3gDW*vK1AEKGU3se&o4jmX3xW(0VbXrunPu@R|I~&et z#X-Tr5F#xw6L?YQ!C9?PP9~=Jh~9c2YE9qK(Sh+x?A)G2v+R=tH>?WNLU`5esgKfAMyEhTxzUuX_Bh2TI-#4(y534ml$j z1;FvD+y(lSkdcM$hJ&2_`Ua(8n*J%{%%zbrp=*1yjFORpeVbSfc25n+&nLMW(sQFYF z*{ziJ+Bp<{i%qvrOicBA?YiDR|8Q|vWkqDvFGaJ)zmIoAZ2~0KB-!%sqzbK#QZ_Hfp_*vXPk9SY< z5ovwLMb9fTtUtQOTGbLiKMvlgD(kxArdP_2%^IpxqANq^r?rR$Jlq%&`8MC#y;7BB zT1)}W`!h5r7?i*;^00%$3oV~lCu4_kNA+dqfB*62r0UFAi1l7rcXaAF`nZ{_GdWH64$H#C;5d4YN#Y@eW{6nSxHY+Kl7!s-M6Aa96P;8U2C@C<-$o%*jlggW$O( zh-Fq&qn02-2YiiQmpSC~p+^Y+g|#0@zH<>XU3Q6Go}-yKMt?)e^b-EBAn(bxn+$LG zz8DKDOni_+u(UvxC^E0q&8Bri76@^KMWP#;|7}p3L1*vS_*H_~dH`O)ES{t-ak_nb zeDep54-5H%n-66*Y5c57vo*ftsx@hA#Itix(4%V0X2HgDO9iqe4A#nlYvMhe^fKCy2nf!jGbyzAzZ+2eu*`01-(osd#63v)W76Syb2^W3Glrlfl_rLkp} z5{ayLgL(%en>AVdzU`e79Nb5#WidR~nLjar(_GF~kd#DY4*Mcv=>Z>1P%vd-eiSOBd;+iDF&;p)P03iO<& znKHhf4RqP>Ghw)9BlcrrLhIs1A;MCz?T@>>>zteWod^=dW81b1%?QCA5m%Ok3}XoR zy6jKdJ=%3E>60qlD&U?3Rz*-~s1?8-0#|~?4ov}n$N)VZ-FaL=a4X1|JF0Up)NiCT z=+>3s|vC~~XiuY(JtnvH_{0Oozd{2+T@&Ka+@O1Kg?tI?$ z$wti9n;Blc7-V6dO;{D!QzFQzKAt2nFm?{!Twps4-ekA$fz09@=9VBTrr$EO{`7fr zwn6W5^I=#%K{s>meGr3nrW0?0Gs_>DXQ4w!&6Exp{MiTkfHPL0_9p&zI?NI`7akFL zQ(bfa(8%2ir{b>I=NEfI?s$QZFv6F%|2Qg}s-cQn<(q$b6J|~7*n+<}Y>LcT?7J-S z@!QYI0@c8E2YE_Q23`vajlEv_Qr5IhGJlQvq~rlx^R2)D#9kY>z>wr%mSLj%ernJt}h?~?qfxQpSBa|w{)|MPi@InG96vQly48Ru!hxnjF&mlkvwT+De zA|k)nYniRndI@Wkv#!6sQ~-^Z2C)iLKHCZ_um?_^VgX=8%OY|O-pVl32nq`NTf6kz z>-Y}$r-A$bxOhAr%D>#L>El!8nP6vYYjgSX(FlTR0=y&_X@&oK(SHN(t{P3vnFuqZ zY)72@4a_t{kx1kO^C~DCAnw4_gdmnDPMn8T+-;TvhxfI4?3^13bZ^XB2Rxu5b4+M{ z&ed$u`P@;pBtNs1%EZ>B_V`(^-fgE8mM7@JUHL`NJZ9bh@WT0E?GWy{CXQNpy}%Y( zf!sO;_a`%^QnGTLo^jig%>p$ulynli#($-T|D?0K*D`9ULCq-~eblI>*SU8}SVN61 z_Z78bBv+=8n|rogUdMCS^_nT{`BUDf;|%+Df9lc^NNl&A(7xcDF(^8e%Q9n}J2f{4 zH00YYzxJPRY~7r{(50|%E<5&sq2lCg+M3EMMhT*Uj88K;-PA!*JN(TntaneAa+c6D`IZ{2Es_L!?L*J_8Jez#??DTE4MVNoK(44ys%hZzs;+WXu-~QNkt2m7=R+umIHj@o? z2=~Lghs0i5*t``oFYvT`u(AcK6$YsDzu#>J%IVg&rBc*@x#>oVYLZ~vRlp)kQ2p`A z%AQshH4)JCZB#y$d}zlO8miAbCc~AMepK~}y*`Du>+Xkz?P1jKSXJu_S7v$a_^?w2 zXip53J$j{nYlDhcXmHnjYJge&qSx2i!vPURp50+xJBs(*<>M=*mlozoIwG&~q)Gei zJu$X0YmNIlB`T@e2TJmGGQ&Is#}!)E?SM~UXdW9Kkqtn3hI=c%LLz2gQ`MzL3GixPoW<_W8vp8kE)-!W9<-t$h zf>^b!Hs_l^s5y@@L^l4NGk*7|&JN|Ls1+^PGbE=Ld6TbvSADPru3l*~5ZBGz9;X=y zC>>wy?c20fVcbpN#^KbjvJs2#tM}vHNWR*&*AF?|!DHCUG>2ITJF#JVt9jJZNFW_kCnUttWNp`hRx^v2&msK|(vocR$ zdF!{-{DJL2A#Tnc(COi|T8rH-+m}pM?d{^M@c4gU!_nE8^wXcM1rEH>5)wUX?eL=D z*4TP2MBbB`(Y{L(v@-izEVk?R@v?kXjL-cZ&Zio<)m$Rh;O`N#lH`xDSmSx&|57yD z@Sz71T4{MAs3L0j?akgUe*A}mX9csCm&h|EKfd;Q&mQIf{l-5JdfSfwKJGt1Qm}E$ zf9v6&Uocrm)2H$8@BQ;c)&@8V{PS1-`C}*7kjmhHf7&mD;=eENpI@jjxF*v0zyC&F z_R#-jFMG|dQ3|P8n7@#@F{V`r3qaAr<@M_TirQ@FIen-l5Yk5nx6k#7aM(n4-Ojox zqcf0bbI3RcefwsDbtox0_k3{pg6#DkTY_qrFAeO!%`QoAbbrIfjc0Ly5U>-T8GF0r zulL;gU5@;xL5{ufVJ@`qPI&a_INEktp$xlr!~1vl-o4WugI5aOAr^jsEI@FBqnC<= zw|#en!_b_s_v`4Ws7d5Hh&m5x|GNW7mY>hEC-M}wjb!#*>4SU6SRRnz=Z2%}njU98 zD~xvY@p#=G01uT=ynCI~r@dIcP`{Oazsf#8=R&K1efhGh&!3Dl#EqbLaM0~(@t!RvhH#gKu+=eoarRfo zJE#l%3_F*9cVoNVq-A{z3!fM%$E2n*a9(h2V|)x$l7W#idED;8gZx-fF)BnBH#>e1GJ~w%bm7) z=^6LK6c%;~MAoJ(zdTp%>HIA=mcvB&^V8APLk=(?*M&P_0p_g$S>J>c|H!)G$&jPv5kix(o)bZqZ2^ui2J zV8;%MYnWP)M}-gq=_~y-u=RwyRO-ZDM7E(9g!F&2(f$3)H(_D9Er6?}pvypR-Yw~SJ?ncPZnV1y zLuy$5k+)cN5>}#k^MjZ~!gzp^E8oe`aQo@k7klALM$To-jL8fG^M)_4&TonoIC1#? zd8>wpm8jgjLp`v<$QA<%hW&;g<+}P?2^@u|uP5XCkkW3A#RR(wVtOE;8CwHiZQ{$* zu3fpQ^e6L~t0ZF&L=vL{2aA;HgmGiV<%h7x?si>NxK>mo19S4{uRZV?L3rGRZ8|c^ zq+)?QbwX)KOx*E@%jCq)Cw|<5m?)B*UHC~Ri~(6jfqD>wXN2Qr*h3;9?=|ayZkK_J zOViz5ig4E8q^H*jTW-L=?}@Pv0=nAc|0&?+wsHvaw){(&Z$i4+k471zthCO$4<7(ln`4@T|N6qZi3^me_GJ+6 zU`~Da;lquwUbMNLV{!{a$xBOvigVKRZ?Ft%K79Crw?Qe_)Crv&YJpIh`{YOBs-A8_ z>Y*MotrC^|5tuO3VJ*V)01-tLzy%A3oHqEx65Dp1Avs=i_An1$*M4nckK9W^C*J*F zjf^ZEg#4Id{>vB8|U5XUiv8soBWJHDW4@NGK9|Wa%Csx zLZ~=@LN-K+iT&W}O806V@<2xzWv(d5%`_iDMl*@1{O40cS%}bvZWqtLTGd_+@Y5@S zu!Dpk#(n?%_3H=tKEDHTvSz>U{BaT-k9)x5Z*O2)db(~{9*!>(-9oqrNN6Oxh{Rmt zqh9mOrt;fl@#fkZLi0l$h3kj)(0o{6j!v=_M_$98Lvb3OLM-fk}$Tk!s^h7Ie3FaHpMl%J)Rz_^FDEM8$@x(jvVoaWnv~Si{1A5 zi-OA^8X636j6R{FrrwkP?MlcLFwMP?1L|LtIGyquGay|)bzP#psI<@DYoC7_V`}~J zvPJvz&524MAg5@1jaWn7wUm@m;JNI=4-n=>6sT-UaPOUw{)w+L6IYR1*SLH&kkylj z&PW?e&&Y@ZK(PyF{_vCxCe2Y0&h3Fmi$4B;@z`RdmO|QWm)8nRT#>B(CZ!8EW>KkC zerKkT(X00<-ALmwKzu>K5a`8v48q zpA-Q_1LzR7aW^wSq)AL}j%jL!PE|6WA&(iw$4RTE_)HSgaI3Rx`oN3aTzygzcyi@0 zE>4b-U*(_=)M8I*O?z!;W1~&D%L7Q1%>)zC^Qu-!-?!mDJ8%sD`7jZDy8&;1Ktbho z9v8#0OP9vBt%jnw9OHc! zhK1#AF$yq@yq0IY4~zzjcQ;!Q1)X;SiUaIt+h3CiM>8sR&1C!D`xLAp@R?jTUieU7 zzoZm#T=2xh{>V4qvd<`@l5*3mjBCwi1qU!3w;Z$){$2J&^4*er6o%=zgQus-=2NI9 z=NC{$a=)eLBZ0mGjo?E69vZ5_5VU8QJ_tckX8|P&IhWCK2M5kk!~9Ue;~~}%SdN4iinIQ?pV!_wQb5mlv4b9b0pzeA{*onIeo`}J=Pe8cH;Rxk3?kr0Leh^J^ zoU9upf?r-=Vu${i*(Cu*$2>+njUY3zqC6rr2~eB_(FcRxO5FZ6xx_2961*2@N$K)W z>DaV7uV}q3Ksuq2;gGYl7~uKe(mxSTR$E_h3FZ~*Z+Nxf?ItEA9bW!^883=*)Lh~b zikIVIopB+k*zvc12%o{NI_ez9D4;>8pd+s85%?5D>IkTJzW+zugxrFzX=_-N-LWn2 zkDHtCKyhmj!Kd)+=*yzr7d>=#-%7qJ$!IejAu=p$F_HE0aH`hnH@kz`_KV=e<77iiO z3RpG9)*?e4qQ%n0vJF%O-G?vn91iJ|5gad4XMoz7wiKLHLnA&eQDgSe6;2L zlm=QMSfC*iJdl{`$IF2qEDijC0!9`@2{$!B947`rK*N-y8BIk(U#uPNz4I~Zkph!% zC^u{C>nr0L!wo11VhkF5Q$Ugp4?fX^$5~uQewc_=PiVL-i?*H(=qJVtu5Z3efjs|^xT{v z%$ngH2X}Gedyg6cH{I(xCwlC5crtM1<#b#`2BQ6n|?j{hlhzXW{sJ+P?4xE+5ihkp+O&%&O=k7bZXZMN8Jo%t2*x<27X! z6&Kl;Gq>!W4azqpf_McJ2|Y8jM(n)d&iw4AV{_L z5vy?yqYB%xRCuGI!fca3>HH)yks`XTeK-nf*a-^W!o$mve8J7l+NfD7;GTr}nFwx8 zTqLF+r)jb@OFSOp5Ex`?p1h*qlJ)q$Ld(7OW*wf0~X<#%O@d z1s`TeMSwHo?_h2GYI_Z|3ozTXv5R7NUyOzT=x}ujt4r1-#anS@ax=l?>AB>-^f_&$ zk{@tha~J)5?or0NEV69?;K6|95l%=VpEoy8Y+4x_vLS6FItaWKRCDojEG!uNqK_l} z60v>2K*+#>GAz&nHl4^%o+`=i#iccXy7w8zEv*XKlhJtW?(XixP1)F70=oBA^CJ7r9 zwf+S=yXg+=oQ~A=be@9;r;=z>ND0UO`JST$dNP9eqq6F{Q2BM(c?wi@GK)a7>ZLOX z(=`rx&wIEG4`5u-exmdHcOUhVD<|1X&9uPoA!j+<^+#M&T5B=t%)FUFipNLYOE>Y9 z+N{NKAO=AvUibZSg;pRWPas5)IN~PIf@MxTK;>@)w;N~>aP&pqyQl2xDp7ZT-?Yap zI~yA*(s3{jJ`MWX+Bfj?=Rec2>FIU}$76goYX=pnk43!4jVa~+d&la+=W_CtkFc$Q zmtTyC#o?5}On6cf{{}gc~7TBXBt|~etNdT8Tq(w7H&P>8{ z;-;5O^rWrNRo(7Ik=BDkUw_lFs_N=i^g-|=f;-MRa48gmW10OxxJPs!8ffZ}%C|VG z$&HS75tG5xb@EL&0ebJ)aSf@XMA84k;+^MASzRT7=LaGdYd0(WB`lX6Fn<0=A+a~i zjI|%ZX;hCIo(bIZfq;nwcVYX&pfkITpT_6?-}(8;FXzGz_^wJ-_kDlkPORWSJA7;2J$k1ODN9mG$QTOEpriIQo0s5<()+`9QiL|#0zSovJc2XIn+(0>l{7YtAE#{Pkq!xrN5(qiCE||o4H@dJ%fbis*wFWzZ;0s_yf6INyfSKz_hX}xCu zo;|k!C-WgQaPX9D44&;i zP9SFZ?ysQ;B1vdux{Ytr7f1`yB1XUE8uaH=C&+Qw!~^LfV=Yfs@IQ7u9h26;^3{^{*79{1i>W@d z@wv^_%bu0oOGSsLJTHcNj29*>DxAxXoBrw4I0!L!e)7nD!t2o0)_z0sM0f8-N``r! z@$P;5`1kE=$T@>$p`@e)UVaqrLJC~9M*(u;Oy)Hx`$mnyB%$a4Kz;|)H+}tpcfC76 z|G;97BAbSdZKOjH*-03y>*(qp#^Fc-c<(IEGjGFtqhTwznc3JrtmnS^h6LI}){BZ4 zUCP-!XZpLYZM~&skao?#kQ{h?5`yL0WZ%{eI^b7cA3D@j4)>IhE!%Y4J zUJ~8yZ?$OAvncLp=;=XK?QZ=wH#4)#n&+LGG~=ZFz{MfwOMH@jQ90qlsw;e}()@P|dEypBf;8;dhv{odoc%cz6zOOqFKD z3oWpUC+csuHW(Mz{mpfSKkBE zJq3L#981}HN9?)(_nS3iu6i&r26de1UUB)besm`dA%e(TTSYat(Py`qb&nnH{`YMh zJOtmVYO5UNhExKUZzT1VhKA+00!LoculpF#Q-X`D7VX&CQXfO!VGTu`l%rp_&*B|u zWI$8zK#od^X57@*l(|pkV6L#+H#Id`1EdGll7T`y9hXV78lasX1 zl6%5S;-Ep#A@v6IKM^((4devYK4u}PIQTG|g(wnVY^nVbXPLOGf7(wHj1#>EDo>KJ zUQ)u5((2K*kmYi^&wK>s0R;V9xw$9)#GH$IC`z7c_?n?#zxr{nf(vmSt#u@H%z*PG z9?|7@%J)p74DbMQ^751rX6_O3@$`paVTWo2O+O@i5+s5HKoJ1)ow%cB*pg90;#3O( z;T}alJzytt?}Cd3CzELe`Q!kqBiJq$2)&8@Gcx?a6^9l94KTP`*E=p+5f+VsLD!42 z47z9EaryuNJb~_G@5!uRIo+p{JK3!r6xQB^CJ4ef7;BDye_P-X?XsY(?SZ1%e~ZHQbEA&u+*uqcG(v2z77dVPD=|0F!HgbMakM)5T@Md`ax5|w?csz4~MM=KJ%#phhFi~ zVo!#`9dPHsrE08Wg_IJPzKS)i3Tix{IHaq)RiJfX{y|TVIhT9S8ek9* zLqRK{FyGHeYN!kKo{D`)s(F%}Oc`D0a(sjib3)MjVrAXzHL_PyWBy$OKDL)GoreSz z#+pDL~_o*#jF#AZSezAVpP}59Lfmortb5|NXFV zWWZTQ+IK|`i~I+Mi&t^Cff z#BQ^S+Iu@v_PZ9Rp;<*gCMG7f1@bz4A$Tf(ADnq`?#X|r!SMQjb)J~_oJH9{@^8(u zCB?*E0KB;a+yy3X_86sAFoR$*LgBb>@2KVJU zR1y6A>mD^!%})=)1{=sP{0Nl2yo#-3adr+oYa)jsDW>qbOTT*|AcQP!KsA6oC+>zq ztiy`MYHORGa=^bX$6pbk+jH*)F@Ql#rehA~bkw8gKYks{!{JBJn#+YOtg;zwmjT4pBV z$UMdM{n1FN;ub*E{dv@4_$u!vBsAfg(t<%W-DvTrmy1Q;Umj}s_pm#G-K}R}paNw! zatDjXeQ+CNDV43Rc*)4h#=!O(&_A)d!u5cNVj$Rj!orQC-ekx?DC7910oap$h4l=^ zAL-vP)I@8nLWbp}0Yz$A;8hLqzlb(3ATaP;p0N?=fdHC+eJhLv80r8QbYZg+cbs)U zM?I_8`oHUSC^iu;DlR51pYcasW$F~{Uu+a`+T}BN{``M0N{#n(C08UqR+6I||Jd6x zk)x4;b=&`O<0z}H5x4&5V&X}@_U{SG5U%szgTMvW(;mx8dow)!NdN0A$wIqv6(9NM zD#j~h)kk!~EC(*A5F@;Vs=M`p-%~Xz`W`<8olF z-YXwdV}of!uL%6``VbQ|(IsSk6C4^z6Z(j{fw#lXKL9%vn#|11+_stIS>?xX;N9a_ qG0zrN@#e>;w)x+Knedce Date: Fri, 29 Apr 2022 00:22:21 +0000 Subject: [PATCH 066/930] [ci skip] Translation update --- .../accuweather/translations/ca.json | 3 + .../accuweather/translations/de.json | 3 + .../accuweather/translations/el.json | 3 + .../accuweather/translations/en.json | 3 + .../accuweather/translations/et.json | 3 + .../accuweather/translations/fr.json | 5 +- .../accuweather/translations/hu.json | 5 +- .../accuweather/translations/id.json | 3 + .../accuweather/translations/it.json | 3 + .../accuweather/translations/ja.json | 3 + .../accuweather/translations/nl.json | 3 + .../accuweather/translations/no.json | 3 + .../accuweather/translations/pl.json | 3 + .../accuweather/translations/pt-BR.json | 3 + .../accuweather/translations/ru.json | 3 + .../accuweather/translations/tr.json | 3 + .../accuweather/translations/zh-Hant.json | 3 + .../components/adax/translations/hu.json | 4 +- .../components/adax/translations/nl.json | 2 +- .../components/aemet/translations/ca.json | 2 +- .../components/aemet/translations/de.json | 2 +- .../components/aemet/translations/en.json | 2 +- .../components/aemet/translations/et.json | 2 +- .../components/aemet/translations/fr.json | 2 +- .../components/aemet/translations/hu.json | 2 +- .../components/aemet/translations/id.json | 2 +- .../components/aemet/translations/it.json | 2 +- .../components/aemet/translations/nl.json | 2 +- .../components/aemet/translations/no.json | 2 +- .../components/aemet/translations/pl.json | 2 +- .../components/aemet/translations/pt-BR.json | 2 +- .../components/aemet/translations/ru.json | 2 +- .../components/aemet/translations/tr.json | 2 +- .../aemet/translations/zh-Hant.json | 2 +- .../components/agent_dvr/translations/hu.json | 2 +- .../components/airly/translations/ca.json | 2 +- .../components/airly/translations/de.json | 2 +- .../components/airly/translations/en.json | 2 +- .../components/airly/translations/et.json | 2 +- .../components/airly/translations/fr.json | 2 +- .../components/airly/translations/hu.json | 4 +- .../components/airly/translations/id.json | 2 +- .../components/airly/translations/it.json | 2 +- .../components/airly/translations/nl.json | 2 +- .../components/airly/translations/no.json | 2 +- .../components/airly/translations/pl.json | 2 +- .../components/airly/translations/pt-BR.json | 2 +- .../components/airly/translations/ru.json | 2 +- .../components/airly/translations/tr.json | 2 +- .../airly/translations/zh-Hant.json | 2 +- .../components/airnow/translations/ca.json | 2 +- .../components/airnow/translations/de.json | 2 +- .../components/airnow/translations/en.json | 2 +- .../components/airnow/translations/et.json | 2 +- .../components/airnow/translations/fr.json | 2 +- .../components/airnow/translations/hu.json | 2 +- .../components/airnow/translations/id.json | 2 +- .../components/airnow/translations/it.json | 2 +- .../components/airnow/translations/nl.json | 2 +- .../components/airnow/translations/no.json | 2 +- .../components/airnow/translations/pl.json | 2 +- .../components/airnow/translations/pt-BR.json | 2 +- .../components/airnow/translations/ru.json | 2 +- .../components/airnow/translations/tr.json | 2 +- .../airnow/translations/zh-Hant.json | 2 +- .../airvisual/translations/sensor.fr.json | 2 +- .../components/airzone/translations/ca.json | 3 +- .../components/airzone/translations/cs.json | 18 ++ .../components/airzone/translations/de.json | 3 +- .../components/airzone/translations/el.json | 3 +- .../components/airzone/translations/et.json | 3 +- .../components/airzone/translations/fr.json | 3 +- .../components/airzone/translations/hu.json | 3 +- .../components/airzone/translations/id.json | 3 +- .../components/airzone/translations/it.json | 3 +- .../components/airzone/translations/ja.json | 3 +- .../components/airzone/translations/nl.json | 3 +- .../components/airzone/translations/no.json | 3 +- .../components/airzone/translations/pl.json | 3 +- .../airzone/translations/pt-BR.json | 3 +- .../components/airzone/translations/ru.json | 3 +- .../components/airzone/translations/tr.json | 20 ++ .../airzone/translations/zh-Hant.json | 3 +- .../components/almond/translations/hu.json | 4 +- .../components/almond/translations/it.json | 2 +- .../components/ambee/translations/hu.json | 2 +- .../ambiclimate/translations/hu.json | 4 +- .../ambiclimate/translations/it.json | 4 +- .../ambient_station/translations/hu.json | 2 +- .../components/androidtv/translations/hu.json | 10 +- .../components/apple_tv/translations/bg.json | 1 + .../components/apple_tv/translations/ca.json | 1 + .../components/apple_tv/translations/de.json | 1 + .../components/apple_tv/translations/el.json | 1 + .../components/apple_tv/translations/en.json | 6 +- .../components/apple_tv/translations/et.json | 1 + .../components/apple_tv/translations/fr.json | 1 + .../components/apple_tv/translations/he.json | 1 + .../components/apple_tv/translations/hu.json | 13 +- .../components/apple_tv/translations/id.json | 1 + .../components/apple_tv/translations/it.json | 1 + .../components/apple_tv/translations/ja.json | 1 + .../components/apple_tv/translations/nl.json | 1 + .../components/apple_tv/translations/no.json | 1 + .../components/apple_tv/translations/pl.json | 1 + .../apple_tv/translations/pt-BR.json | 1 + .../components/apple_tv/translations/ru.json | 1 + .../components/apple_tv/translations/tr.json | 1 + .../apple_tv/translations/zh-Hant.json | 1 + .../components/arcam_fmj/translations/hu.json | 2 +- .../components/arcam_fmj/translations/it.json | 4 +- .../components/asuswrt/translations/ca.json | 2 +- .../components/asuswrt/translations/de.json | 2 +- .../components/asuswrt/translations/et.json | 2 +- .../components/asuswrt/translations/fr.json | 2 +- .../components/asuswrt/translations/he.json | 2 +- .../components/asuswrt/translations/hu.json | 4 +- .../components/asuswrt/translations/id.json | 2 +- .../components/asuswrt/translations/it.json | 4 +- .../components/asuswrt/translations/nl.json | 2 +- .../components/asuswrt/translations/no.json | 2 +- .../components/asuswrt/translations/pl.json | 2 +- .../asuswrt/translations/pt-BR.json | 2 +- .../components/asuswrt/translations/ru.json | 2 +- .../components/asuswrt/translations/tr.json | 2 +- .../asuswrt/translations/zh-Hant.json | 2 +- .../components/aurora/translations/hu.json | 4 +- .../components/auth/translations/fr.json | 4 +- .../components/axis/translations/hu.json | 2 +- .../azure_devops/translations/it.json | 4 +- .../azure_event_hub/translations/de.json | 2 +- .../binary_sensor/translations/cs.json | 4 + .../binary_sensor/translations/he.json | 8 +- .../binary_sensor/translations/pt-BR.json | 2 +- .../binary_sensor/translations/zh-Hant.json | 2 +- .../components/blebox/translations/it.json | 2 +- .../components/braviatv/translations/ca.json | 2 +- .../components/braviatv/translations/de.json | 2 +- .../components/braviatv/translations/en.json | 2 +- .../components/braviatv/translations/et.json | 2 +- .../components/braviatv/translations/fr.json | 2 +- .../components/braviatv/translations/hu.json | 2 +- .../components/braviatv/translations/id.json | 2 +- .../components/braviatv/translations/it.json | 2 +- .../components/braviatv/translations/nl.json | 2 +- .../components/braviatv/translations/no.json | 2 +- .../components/braviatv/translations/pl.json | 2 +- .../braviatv/translations/pt-BR.json | 2 +- .../components/braviatv/translations/ru.json | 2 +- .../components/braviatv/translations/tr.json | 2 +- .../braviatv/translations/zh-Hant.json | 2 +- .../components/broadlink/translations/hu.json | 4 +- .../components/brother/translations/ca.json | 5 +- .../components/brother/translations/da.json | 3 +- .../components/brother/translations/de.json | 5 +- .../components/brother/translations/el.json | 3 +- .../components/brother/translations/en.json | 5 +- .../brother/translations/es-419.json | 3 +- .../components/brother/translations/es.json | 3 +- .../components/brother/translations/et.json | 5 +- .../components/brother/translations/fr.json | 5 +- .../components/brother/translations/hu.json | 5 +- .../components/brother/translations/id.json | 5 +- .../components/brother/translations/it.json | 5 +- .../components/brother/translations/ja.json | 3 +- .../components/brother/translations/ko.json | 3 +- .../components/brother/translations/lb.json | 3 +- .../components/brother/translations/nl.json | 5 +- .../components/brother/translations/no.json | 5 +- .../components/brother/translations/pl.json | 5 +- .../brother/translations/pt-BR.json | 5 +- .../components/brother/translations/ru.json | 5 +- .../components/brother/translations/sl.json | 3 +- .../components/brother/translations/sv.json | 3 +- .../components/brother/translations/tr.json | 5 +- .../components/brother/translations/uk.json | 3 +- .../brother/translations/zh-Hans.json | 3 - .../brother/translations/zh-Hant.json | 3 +- .../components/bsblan/translations/hu.json | 2 +- .../components/cast/translations/cs.json | 32 +++ .../components/climacell/translations/ca.json | 2 +- .../components/climacell/translations/de.json | 2 +- .../components/climacell/translations/en.json | 23 +- .../components/climacell/translations/fr.json | 4 +- .../components/climacell/translations/hu.json | 6 +- .../components/climacell/translations/pl.json | 2 +- .../climacell/translations/sensor.fr.json | 2 +- .../climacell/translations/sensor.hu.json | 2 +- .../components/climate/translations/et.json | 4 +- .../cloudflare/translations/he.json | 3 +- .../components/coinbase/translations/ca.json | 3 +- .../components/coinbase/translations/de.json | 3 +- .../components/coinbase/translations/el.json | 3 +- .../components/coinbase/translations/en.json | 6 +- .../components/coinbase/translations/et.json | 3 +- .../components/coinbase/translations/fr.json | 3 +- .../components/coinbase/translations/hu.json | 3 +- .../components/coinbase/translations/id.json | 3 +- .../components/coinbase/translations/it.json | 3 +- .../components/coinbase/translations/ja.json | 3 +- .../components/coinbase/translations/nl.json | 3 +- .../components/coinbase/translations/no.json | 3 +- .../components/coinbase/translations/pl.json | 3 +- .../coinbase/translations/pt-BR.json | 3 +- .../components/coinbase/translations/ru.json | 3 +- .../components/coinbase/translations/tr.json | 3 +- .../coinbase/translations/zh-Hant.json | 3 +- .../coronavirus/translations/hu.json | 2 +- .../components/cover/translations/he.json | 4 +- .../components/cpuspeed/translations/bg.json | 1 - .../components/cpuspeed/translations/ca.json | 1 - .../components/cpuspeed/translations/de.json | 1 - .../components/cpuspeed/translations/el.json | 1 - .../components/cpuspeed/translations/en.json | 1 - .../components/cpuspeed/translations/es.json | 1 - .../components/cpuspeed/translations/et.json | 1 - .../components/cpuspeed/translations/fr.json | 1 - .../components/cpuspeed/translations/he.json | 7 +- .../components/cpuspeed/translations/hu.json | 1 - .../components/cpuspeed/translations/id.json | 1 - .../components/cpuspeed/translations/it.json | 1 - .../components/cpuspeed/translations/ja.json | 1 - .../components/cpuspeed/translations/nl.json | 1 - .../components/cpuspeed/translations/no.json | 1 - .../components/cpuspeed/translations/pl.json | 1 - .../cpuspeed/translations/pt-BR.json | 1 - .../components/cpuspeed/translations/ru.json | 1 - .../components/cpuspeed/translations/tr.json | 1 - .../cpuspeed/translations/zh-Hans.json | 1 - .../cpuspeed/translations/zh-Hant.json | 1 - .../crownstone/translations/ca.json | 21 -- .../crownstone/translations/cs.json | 10 - .../crownstone/translations/de.json | 21 -- .../crownstone/translations/el.json | 21 -- .../crownstone/translations/en.json | 21 -- .../crownstone/translations/es-419.json | 3 - .../crownstone/translations/es.json | 21 -- .../crownstone/translations/et.json | 21 -- .../crownstone/translations/fr.json | 21 -- .../crownstone/translations/he.json | 10 - .../crownstone/translations/hu.json | 21 -- .../crownstone/translations/id.json | 21 -- .../crownstone/translations/it.json | 21 -- .../crownstone/translations/ja.json | 21 -- .../crownstone/translations/ko.json | 16 -- .../crownstone/translations/nl.json | 21 -- .../crownstone/translations/no.json | 21 -- .../crownstone/translations/pl.json | 21 -- .../crownstone/translations/pt-BR.json | 21 -- .../crownstone/translations/ru.json | 21 -- .../crownstone/translations/tr.json | 21 -- .../crownstone/translations/zh-Hant.json | 21 -- .../components/deconz/translations/he.json | 2 +- .../components/deconz/translations/hu.json | 2 +- .../components/deluge/translations/bg.json | 21 ++ .../components/deluge/translations/ca.json | 23 ++ .../components/deluge/translations/cs.json | 21 ++ .../components/deluge/translations/de.json | 23 ++ .../components/deluge/translations/el.json | 23 ++ .../components/deluge/translations/en.json | 26 +-- .../components/deluge/translations/et.json | 23 ++ .../components/deluge/translations/fr.json | 23 ++ .../components/deluge/translations/he.json | 21 ++ .../components/deluge/translations/hu.json | 23 ++ .../components/deluge/translations/id.json | 23 ++ .../components/deluge/translations/it.json | 23 ++ .../components/deluge/translations/ja.json | 23 ++ .../components/deluge/translations/nl.json | 23 ++ .../components/deluge/translations/no.json | 23 ++ .../components/deluge/translations/pl.json | 23 ++ .../components/deluge/translations/pt-BR.json | 23 ++ .../components/deluge/translations/ru.json | 23 ++ .../components/deluge/translations/tr.json | 23 ++ .../deluge/translations/zh-Hant.json | 23 ++ .../components/demo/translations/it.json | 4 +- .../components/denonavr/translations/bg.json | 3 + .../components/denonavr/translations/ca.json | 3 + .../components/denonavr/translations/de.json | 3 + .../components/denonavr/translations/el.json | 3 + .../components/denonavr/translations/en.json | 3 + .../components/denonavr/translations/et.json | 3 + .../components/denonavr/translations/fr.json | 3 + .../components/denonavr/translations/hu.json | 7 +- .../components/denonavr/translations/id.json | 3 + .../components/denonavr/translations/it.json | 3 + .../components/denonavr/translations/ja.json | 3 + .../components/denonavr/translations/nl.json | 3 + .../components/denonavr/translations/no.json | 3 + .../components/denonavr/translations/pl.json | 3 + .../denonavr/translations/pt-BR.json | 3 + .../components/denonavr/translations/ru.json | 3 + .../components/denonavr/translations/tr.json | 3 + .../denonavr/translations/zh-Hant.json | 3 + .../derivative/translations/bg.json | 25 +++ .../derivative/translations/ca.json | 59 ++++++ .../derivative/translations/cs.json | 11 + .../derivative/translations/de.json | 59 ++++++ .../derivative/translations/el.json | 59 ++++++ .../derivative/translations/en.json | 16 ++ .../derivative/translations/et.json | 59 ++++++ .../derivative/translations/fr.json | 59 ++++++ .../derivative/translations/he.json | 11 + .../derivative/translations/hu.json | 59 ++++++ .../derivative/translations/id.json | 59 ++++++ .../derivative/translations/it.json | 59 ++++++ .../derivative/translations/ja.json | 59 ++++++ .../derivative/translations/nl.json | 59 ++++++ .../derivative/translations/no.json | 59 ++++++ .../derivative/translations/pl.json | 59 ++++++ .../derivative/translations/pt-BR.json | 59 ++++++ .../derivative/translations/ru.json | 53 +++++ .../derivative/translations/sv.json | 12 ++ .../derivative/translations/tr.json | 59 ++++++ .../derivative/translations/zh-Hans.json | 59 ++++++ .../derivative/translations/zh-Hant.json | 59 ++++++ .../components/directv/translations/it.json | 4 +- .../components/discord/translations/bg.json | 13 ++ .../components/discord/translations/ca.json | 27 +++ .../components/discord/translations/de.json | 27 +++ .../components/discord/translations/el.json | 27 +++ .../components/discord/translations/en.json | 4 +- .../components/discord/translations/et.json | 27 +++ .../components/discord/translations/fr.json | 27 +++ .../components/discord/translations/he.json | 25 +++ .../components/discord/translations/hu.json | 27 +++ .../components/discord/translations/id.json | 27 +++ .../components/discord/translations/it.json | 27 +++ .../components/discord/translations/ja.json | 27 +++ .../components/discord/translations/nl.json | 27 +++ .../components/discord/translations/no.json | 27 +++ .../components/discord/translations/pl.json | 27 +++ .../discord/translations/pt-BR.json | 27 +++ .../components/discord/translations/ru.json | 27 +++ .../components/discord/translations/tr.json | 27 +++ .../discord/translations/zh-Hant.json | 27 +++ .../components/dlna_dmr/translations/hu.json | 4 +- .../components/dlna_dms/translations/cs.json | 20 ++ .../components/dlna_dms/translations/hu.json | 2 +- .../components/doorbird/translations/ca.json | 3 + .../components/doorbird/translations/de.json | 3 + .../components/doorbird/translations/el.json | 3 + .../components/doorbird/translations/en.json | 3 + .../components/doorbird/translations/et.json | 3 + .../components/doorbird/translations/fr.json | 3 + .../components/doorbird/translations/hu.json | 3 + .../components/doorbird/translations/id.json | 3 + .../components/doorbird/translations/it.json | 3 + .../components/doorbird/translations/ja.json | 3 + .../components/doorbird/translations/nl.json | 3 + .../components/doorbird/translations/no.json | 3 + .../components/doorbird/translations/pl.json | 3 + .../doorbird/translations/pt-BR.json | 3 + .../components/doorbird/translations/ru.json | 3 + .../components/doorbird/translations/tr.json | 3 + .../doorbird/translations/zh-Hant.json | 3 + .../components/dsmr/translations/it.json | 8 +- .../components/dunehd/translations/ca.json | 2 +- .../components/dunehd/translations/de.json | 2 +- .../components/dunehd/translations/en.json | 2 +- .../components/dunehd/translations/et.json | 2 +- .../components/dunehd/translations/fr.json | 2 +- .../components/dunehd/translations/hu.json | 2 +- .../components/dunehd/translations/id.json | 2 +- .../components/dunehd/translations/it.json | 2 +- .../components/dunehd/translations/nl.json | 2 +- .../components/dunehd/translations/no.json | 2 +- .../components/dunehd/translations/pl.json | 2 +- .../components/dunehd/translations/pt-BR.json | 2 +- .../components/dunehd/translations/ru.json | 2 +- .../components/dunehd/translations/tr.json | 2 +- .../dunehd/translations/zh-Hant.json | 2 +- .../components/ecobee/translations/hu.json | 2 +- .../components/elkm1/translations/bg.json | 4 +- .../components/elkm1/translations/ca.json | 12 +- .../components/elkm1/translations/cs.json | 7 - .../components/elkm1/translations/de.json | 12 +- .../components/elkm1/translations/el.json | 12 +- .../components/elkm1/translations/es-419.json | 8 - .../components/elkm1/translations/es.json | 8 +- .../components/elkm1/translations/et.json | 12 +- .../components/elkm1/translations/fr.json | 12 +- .../components/elkm1/translations/he.json | 9 +- .../components/elkm1/translations/hu.json | 14 +- .../components/elkm1/translations/id.json | 12 +- .../components/elkm1/translations/it.json | 12 +- .../components/elkm1/translations/ja.json | 12 +- .../components/elkm1/translations/ko.json | 8 - .../components/elkm1/translations/lb.json | 8 - .../components/elkm1/translations/nl.json | 12 +- .../components/elkm1/translations/no.json | 12 +- .../components/elkm1/translations/pl.json | 12 +- .../components/elkm1/translations/pt-BR.json | 12 +- .../components/elkm1/translations/pt.json | 9 - .../components/elkm1/translations/ru.json | 12 +- .../components/elkm1/translations/sl.json | 8 - .../components/elkm1/translations/sv.json | 7 - .../components/elkm1/translations/tr.json | 12 +- .../components/elkm1/translations/uk.json | 8 - .../elkm1/translations/zh-Hant.json | 12 +- .../emulated_roku/translations/hu.json | 2 +- .../enphase_envoy/translations/en.json | 3 +- .../environment_canada/translations/de.json | 2 +- .../components/epson/translations/hu.json | 2 +- .../components/esphome/translations/cs.json | 2 +- .../components/esphome/translations/hu.json | 2 +- .../evil_genius_labs/translations/cs.json | 1 + .../components/ezviz/translations/it.json | 2 +- .../components/fan/translations/hu.json | 1 + .../components/fibaro/translations/bg.json | 21 ++ .../components/fibaro/translations/ca.json | 22 ++ .../components/fibaro/translations/de.json | 22 ++ .../components/fibaro/translations/el.json | 22 ++ .../components/fibaro/translations/en.json | 2 +- .../components/fibaro/translations/et.json | 22 ++ .../components/fibaro/translations/fr.json | 22 ++ .../components/fibaro/translations/he.json | 20 ++ .../components/fibaro/translations/hu.json | 22 ++ .../components/fibaro/translations/id.json | 22 ++ .../components/fibaro/translations/it.json | 22 ++ .../components/fibaro/translations/ja.json | 22 ++ .../components/fibaro/translations/nl.json | 22 ++ .../components/fibaro/translations/no.json | 22 ++ .../components/fibaro/translations/pl.json | 22 ++ .../components/fibaro/translations/pt-BR.json | 22 ++ .../components/fibaro/translations/ru.json | 22 ++ .../components/fibaro/translations/tr.json | 22 ++ .../fibaro/translations/zh-Hant.json | 22 ++ .../components/filesize/translations/bg.json | 19 ++ .../components/filesize/translations/ca.json | 19 ++ .../components/filesize/translations/de.json | 19 ++ .../components/filesize/translations/el.json | 19 ++ .../components/filesize/translations/en.json | 28 +-- .../components/filesize/translations/et.json | 19 ++ .../components/filesize/translations/fr.json | 19 ++ .../components/filesize/translations/he.json | 7 + .../components/filesize/translations/hu.json | 19 ++ .../components/filesize/translations/id.json | 19 ++ .../components/filesize/translations/it.json | 19 ++ .../components/filesize/translations/ja.json | 19 ++ .../components/filesize/translations/nl.json | 19 ++ .../components/filesize/translations/no.json | 19 ++ .../components/filesize/translations/pl.json | 19 ++ .../filesize/translations/pt-BR.json | 19 ++ .../components/filesize/translations/ru.json | 19 ++ .../components/filesize/translations/tr.json | 19 ++ .../filesize/translations/zh-Hant.json | 19 ++ .../components/fivem/translations/hu.json | 2 +- .../components/flux_led/translations/hu.json | 2 +- .../forecast_solar/translations/ca.json | 1 + .../forecast_solar/translations/de.json | 1 + .../forecast_solar/translations/el.json | 1 + .../forecast_solar/translations/en.json | 2 +- .../forecast_solar/translations/et.json | 1 + .../forecast_solar/translations/fr.json | 1 + .../forecast_solar/translations/hu.json | 3 +- .../forecast_solar/translations/id.json | 1 + .../forecast_solar/translations/it.json | 1 + .../forecast_solar/translations/ja.json | 1 + .../forecast_solar/translations/nl.json | 1 + .../forecast_solar/translations/no.json | 1 + .../forecast_solar/translations/pl.json | 1 + .../forecast_solar/translations/pt-BR.json | 1 + .../forecast_solar/translations/ru.json | 1 + .../forecast_solar/translations/tr.json | 1 + .../forecast_solar/translations/zh-Hant.json | 1 + .../components/foscam/translations/ca.json | 3 +- .../components/foscam/translations/cs.json | 3 +- .../components/foscam/translations/de.json | 3 +- .../components/foscam/translations/el.json | 3 +- .../components/foscam/translations/en.json | 3 +- .../foscam/translations/es-419.json | 3 +- .../components/foscam/translations/es.json | 3 +- .../components/foscam/translations/et.json | 3 +- .../components/foscam/translations/fr.json | 3 +- .../components/foscam/translations/hu.json | 3 +- .../components/foscam/translations/id.json | 3 +- .../components/foscam/translations/it.json | 3 +- .../components/foscam/translations/ja.json | 3 +- .../components/foscam/translations/ko.json | 3 +- .../components/foscam/translations/lb.json | 3 +- .../components/foscam/translations/nl.json | 3 +- .../components/foscam/translations/no.json | 3 +- .../components/foscam/translations/pl.json | 3 +- .../components/foscam/translations/pt-BR.json | 3 +- .../components/foscam/translations/ru.json | 3 +- .../components/foscam/translations/tr.json | 3 +- .../foscam/translations/zh-Hant.json | 3 +- .../components/freebox/translations/hu.json | 2 +- .../components/freebox/translations/it.json | 2 +- .../components/fritz/translations/ca.json | 1 + .../components/fritz/translations/de.json | 1 + .../components/fritz/translations/el.json | 1 + .../components/fritz/translations/en.json | 11 + .../components/fritz/translations/et.json | 1 + .../components/fritz/translations/fr.json | 1 + .../components/fritz/translations/hu.json | 1 + .../components/fritz/translations/id.json | 1 + .../components/fritz/translations/it.json | 1 + .../components/fritz/translations/ja.json | 1 + .../components/fritz/translations/nl.json | 1 + .../components/fritz/translations/no.json | 1 + .../components/fritz/translations/pl.json | 1 + .../components/fritz/translations/pt-BR.json | 1 + .../components/fritz/translations/ru.json | 1 + .../components/fritz/translations/tr.json | 1 + .../fritz/translations/zh-Hant.json | 1 + .../components/fritzbox/translations/ca.json | 1 + .../components/fritzbox/translations/de.json | 1 + .../components/fritzbox/translations/el.json | 1 + .../components/fritzbox/translations/et.json | 1 + .../components/fritzbox/translations/fr.json | 1 + .../components/fritzbox/translations/hu.json | 3 +- .../components/fritzbox/translations/id.json | 1 + .../components/fritzbox/translations/it.json | 1 + .../components/fritzbox/translations/ja.json | 1 + .../components/fritzbox/translations/nl.json | 1 + .../components/fritzbox/translations/no.json | 1 + .../components/fritzbox/translations/pl.json | 1 + .../fritzbox/translations/pt-BR.json | 1 + .../components/fritzbox/translations/ru.json | 1 + .../components/fritzbox/translations/tr.json | 1 + .../fritzbox/translations/zh-Hant.json | 1 + .../components/generic/translations/bg.json | 53 +++++ .../components/generic/translations/ca.json | 88 ++++++++ .../components/generic/translations/cs.json | 33 +++ .../components/generic/translations/de.json | 88 ++++++++ .../components/generic/translations/el.json | 88 ++++++++ .../components/generic/translations/en.json | 2 + .../components/generic/translations/et.json | 88 ++++++++ .../components/generic/translations/fr.json | 88 ++++++++ .../components/generic/translations/he.json | 88 ++++++++ .../components/generic/translations/hu.json | 88 ++++++++ .../components/generic/translations/id.json | 88 ++++++++ .../components/generic/translations/it.json | 88 ++++++++ .../components/generic/translations/ja.json | 88 ++++++++ .../components/generic/translations/nl.json | 88 ++++++++ .../components/generic/translations/no.json | 88 ++++++++ .../components/generic/translations/pl.json | 88 ++++++++ .../generic/translations/pt-BR.json | 88 ++++++++ .../components/generic/translations/ru.json | 88 ++++++++ .../components/generic/translations/tr.json | 88 ++++++++ .../generic/translations/zh-Hant.json | 88 ++++++++ .../components/gios/translations/hu.json | 2 +- .../components/glances/translations/hu.json | 2 +- .../components/goalzero/translations/ca.json | 2 +- .../components/goalzero/translations/de.json | 2 +- .../components/goalzero/translations/en.json | 2 +- .../components/goalzero/translations/et.json | 2 +- .../components/goalzero/translations/fr.json | 2 +- .../components/goalzero/translations/hu.json | 4 +- .../components/goalzero/translations/id.json | 2 +- .../components/goalzero/translations/it.json | 2 +- .../components/goalzero/translations/nl.json | 2 +- .../components/goalzero/translations/no.json | 2 +- .../components/goalzero/translations/pl.json | 2 +- .../goalzero/translations/pt-BR.json | 2 +- .../components/goalzero/translations/ru.json | 2 +- .../components/goalzero/translations/tr.json | 2 +- .../goalzero/translations/zh-Hant.json | 2 +- .../components/goodwe/translations/hu.json | 2 +- .../components/google/translations/bg.json | 20 ++ .../components/google/translations/ca.json | 31 +++ .../components/google/translations/cs.json | 23 ++ .../components/google/translations/de.json | 31 +++ .../components/google/translations/el.json | 31 +++ .../components/google/translations/es.json | 12 ++ .../components/google/translations/et.json | 31 +++ .../components/google/translations/fr.json | 31 +++ .../components/google/translations/he.json | 27 +++ .../components/google/translations/hu.json | 31 +++ .../components/google/translations/id.json | 31 +++ .../components/google/translations/it.json | 31 +++ .../components/google/translations/ja.json | 31 +++ .../components/google/translations/nl.json | 31 +++ .../components/google/translations/no.json | 31 +++ .../components/google/translations/pl.json | 31 +++ .../components/google/translations/pt-BR.json | 31 +++ .../components/google/translations/ru.json | 31 +++ .../components/google/translations/tr.json | 31 +++ .../google/translations/zh-Hant.json | 31 +++ .../google_travel_time/translations/hu.json | 2 +- .../components/group/translations/bg.json | 109 +++++++++- .../components/group/translations/ca.json | 105 +++++++++- .../components/group/translations/cs.json | 196 ++++++++++++++++++ .../components/group/translations/de.json | 105 +++++++++- .../components/group/translations/el.json | 95 ++++++++- .../components/group/translations/en.json | 77 ++++++- .../components/group/translations/et.json | 95 ++++++++- .../components/group/translations/fr.json | 105 +++++++++- .../components/group/translations/he.json | 93 ++++++++- .../components/group/translations/hu.json | 115 ++++++++-- .../components/group/translations/id.json | 105 +++++++++- .../components/group/translations/it.json | 105 +++++++++- .../components/group/translations/ja.json | 93 ++++++++- .../components/group/translations/nl.json | 105 +++++++++- .../components/group/translations/no.json | 105 +++++++++- .../components/group/translations/pl.json | 107 +++++++++- .../components/group/translations/pt-BR.json | 105 +++++++++- .../components/group/translations/ru.json | 105 +++++++++- .../components/group/translations/tr.json | 158 ++++++++++++-- .../group/translations/zh-Hant.json | 93 ++++++++- .../growatt_server/translations/hu.json | 2 +- .../components/guardian/translations/hu.json | 2 +- .../components/hangouts/translations/ca.json | 2 +- .../components/hangouts/translations/de.json | 2 +- .../components/hangouts/translations/et.json | 2 +- .../components/hangouts/translations/fr.json | 6 +- .../components/hangouts/translations/he.json | 2 +- .../components/hangouts/translations/hu.json | 2 +- .../components/hangouts/translations/id.json | 2 +- .../components/hangouts/translations/it.json | 2 +- .../components/hangouts/translations/nl.json | 2 +- .../components/hangouts/translations/no.json | 2 +- .../components/hangouts/translations/pl.json | 2 +- .../hangouts/translations/pt-BR.json | 2 +- .../components/hangouts/translations/ru.json | 2 +- .../components/hangouts/translations/tr.json | 2 +- .../hangouts/translations/zh-Hant.json | 2 +- .../components/hassio/translations/ja.json | 2 +- .../home_connect/translations/hu.json | 4 +- .../home_connect/translations/it.json | 2 +- .../home_plus_control/translations/hu.json | 6 +- .../home_plus_control/translations/it.json | 2 +- .../components/homekit/translations/bg.json | 6 - .../components/homekit/translations/ca.json | 9 - .../components/homekit/translations/cs.json | 9 - .../components/homekit/translations/de.json | 9 - .../components/homekit/translations/el.json | 9 - .../components/homekit/translations/en.json | 9 - .../homekit/translations/es-419.json | 3 - .../components/homekit/translations/es.json | 9 - .../components/homekit/translations/et.json | 9 - .../components/homekit/translations/fr.json | 9 - .../components/homekit/translations/he.json | 9 +- .../components/homekit/translations/hu.json | 11 +- .../components/homekit/translations/id.json | 9 - .../components/homekit/translations/it.json | 9 - .../components/homekit/translations/ja.json | 9 - .../components/homekit/translations/ka.json | 8 - .../components/homekit/translations/ko.json | 9 - .../components/homekit/translations/lb.json | 9 - .../components/homekit/translations/nl.json | 9 - .../components/homekit/translations/no.json | 9 - .../components/homekit/translations/pl.json | 9 - .../homekit/translations/pt-BR.json | 11 +- .../components/homekit/translations/pt.json | 8 - .../components/homekit/translations/ru.json | 9 - .../components/homekit/translations/sl.json | 10 - .../components/homekit/translations/sv.json | 3 - .../components/homekit/translations/tr.json | 9 - .../components/homekit/translations/uk.json | 9 - .../homekit/translations/zh-Hans.json | 9 - .../homekit/translations/zh-Hant.json | 9 - .../homekit_controller/translations/hu.json | 2 +- .../homematicip_cloud/translations/fr.json | 2 +- .../homematicip_cloud/translations/hu.json | 2 +- .../homematicip_cloud/translations/it.json | 2 +- .../components/honeywell/translations/ca.json | 14 +- .../components/honeywell/translations/de.json | 14 +- .../components/honeywell/translations/el.json | 14 +- .../components/honeywell/translations/en.json | 6 +- .../components/honeywell/translations/es.json | 3 +- .../components/honeywell/translations/et.json | 14 +- .../components/honeywell/translations/fr.json | 14 +- .../components/honeywell/translations/hu.json | 14 +- .../components/honeywell/translations/id.json | 14 +- .../components/honeywell/translations/it.json | 14 +- .../components/honeywell/translations/ja.json | 14 +- .../components/honeywell/translations/nl.json | 14 +- .../components/honeywell/translations/no.json | 14 +- .../components/honeywell/translations/pl.json | 14 +- .../honeywell/translations/pt-BR.json | 14 +- .../components/honeywell/translations/ru.json | 14 +- .../components/honeywell/translations/tr.json | 14 +- .../honeywell/translations/zh-Hant.json | 14 +- .../huawei_lte/translations/hu.json | 2 +- .../components/hue/translations/ca.json | 1 + .../components/hue/translations/de.json | 1 + .../components/hue/translations/el.json | 1 + .../components/hue/translations/en.json | 1 + .../components/hue/translations/et.json | 1 + .../components/hue/translations/fr.json | 1 + .../components/hue/translations/hu.json | 3 +- .../components/hue/translations/id.json | 1 + .../components/hue/translations/it.json | 1 + .../components/hue/translations/nl.json | 1 + .../components/hue/translations/no.json | 1 + .../components/hue/translations/pl.json | 1 + .../components/hue/translations/pt-BR.json | 1 + .../components/hue/translations/ru.json | 1 + .../components/hue/translations/tr.json | 1 + .../components/hue/translations/zh-Hant.json | 1 + .../humidifier/translations/hu.json | 1 + .../components/hyperion/translations/hu.json | 4 +- .../components/ifttt/translations/ja.json | 2 +- .../components/insteon/translations/ca.json | 5 + .../components/insteon/translations/de.json | 5 + .../components/insteon/translations/el.json | 5 + .../components/insteon/translations/en.json | 1 + .../components/insteon/translations/et.json | 5 + .../components/insteon/translations/fr.json | 5 + .../components/insteon/translations/he.json | 1 + .../components/insteon/translations/hu.json | 5 + .../components/insteon/translations/id.json | 5 + .../components/insteon/translations/it.json | 7 +- .../components/insteon/translations/ja.json | 6 +- .../components/insteon/translations/nl.json | 5 + .../components/insteon/translations/no.json | 5 + .../components/insteon/translations/pl.json | 5 + .../insteon/translations/pt-BR.json | 5 + .../components/insteon/translations/ru.json | 5 + .../components/insteon/translations/tr.json | 5 + .../insteon/translations/zh-Hant.json | 5 + .../translations/bg.json} | 2 +- .../integration/translations/ca.json | 42 ++++ .../integration/translations/cs.json | 41 ++++ .../integration/translations/de.json | 42 ++++ .../integration/translations/el.json | 42 ++++ .../integration/translations/en.json | 6 + .../integration/translations/et.json | 42 ++++ .../integration/translations/fr.json | 42 ++++ .../integration/translations/he.json | 40 ++++ .../integration/translations/hu.json | 42 ++++ .../integration/translations/id.json | 42 ++++ .../integration/translations/it.json | 42 ++++ .../integration/translations/ja.json | 42 ++++ .../integration/translations/nl.json | 42 ++++ .../integration/translations/no.json | 42 ++++ .../integration/translations/pl.json | 42 ++++ .../integration/translations/pt-BR.json | 42 ++++ .../integration/translations/ru.json | 42 ++++ .../integration/translations/tr.json | 42 ++++ .../integration/translations/zh-Hans.json | 42 ++++ .../integration/translations/zh-Hant.json | 42 ++++ .../intellifire/translations/bg.json | 15 ++ .../intellifire/translations/ca.json | 29 ++- .../intellifire/translations/cs.json | 11 + .../intellifire/translations/de.json | 29 ++- .../intellifire/translations/el.json | 29 ++- .../intellifire/translations/en.json | 16 +- .../intellifire/translations/et.json | 29 ++- .../intellifire/translations/fr.json | 29 ++- .../intellifire/translations/he.json | 19 +- .../intellifire/translations/hu.json | 29 ++- .../intellifire/translations/id.json | 29 ++- .../intellifire/translations/it.json | 29 ++- .../intellifire/translations/ja.json | 29 ++- .../intellifire/translations/nl.json | 29 ++- .../intellifire/translations/no.json | 29 ++- .../intellifire/translations/pl.json | 29 ++- .../intellifire/translations/pt-BR.json | 29 ++- .../intellifire/translations/ru.json | 29 ++- .../intellifire/translations/tr.json | 29 ++- .../intellifire/translations/zh-Hant.json | 29 ++- .../components/iotawatt/translations/hu.json | 2 +- .../components/iotawatt/translations/id.json | 2 +- .../components/ipma/translations/hu.json | 2 +- .../components/ipp/translations/it.json | 2 +- .../components/ipp/translations/pl.json | 2 +- .../components/iss/translations/hu.json | 2 +- .../components/iss/translations/pt-BR.json | 4 +- .../kaleidescape/translations/cs.json | 13 ++ .../kaleidescape/translations/hu.json | 2 +- .../components/knx/translations/bg.json | 18 +- .../components/knx/translations/ca.json | 78 ++++++- .../components/knx/translations/de.json | 82 ++++++-- .../components/knx/translations/el.json | 60 +++++- .../components/knx/translations/en.json | 6 +- .../components/knx/translations/et.json | 78 ++++++- .../components/knx/translations/fr.json | 78 ++++++- .../components/knx/translations/hu.json | 78 ++++++- .../components/knx/translations/id.json | 78 ++++++- .../components/knx/translations/it.json | 78 ++++++- .../components/knx/translations/ja.json | 58 +++++- .../components/knx/translations/nl.json | 78 ++++++- .../components/knx/translations/no.json | 78 ++++++- .../components/knx/translations/pl.json | 78 ++++++- .../components/knx/translations/pt-BR.json | 78 ++++++- .../components/knx/translations/ru.json | 78 ++++++- .../components/knx/translations/tr.json | 78 ++++++- .../components/knx/translations/zh-Hant.json | 78 ++++++- .../components/konnected/translations/fr.json | 2 +- .../components/konnected/translations/hu.json | 8 +- .../components/konnected/translations/it.json | 6 +- .../konnected/translations/pt-BR.json | 2 +- .../kostal_plenticore/translations/ca.json | 3 +- .../kostal_plenticore/translations/de.json | 3 +- .../kostal_plenticore/translations/el.json | 3 +- .../kostal_plenticore/translations/en.json | 3 +- .../kostal_plenticore/translations/es.json | 3 +- .../kostal_plenticore/translations/et.json | 3 +- .../kostal_plenticore/translations/fr.json | 3 +- .../kostal_plenticore/translations/hu.json | 3 +- .../kostal_plenticore/translations/id.json | 3 +- .../kostal_plenticore/translations/it.json | 3 +- .../kostal_plenticore/translations/ja.json | 3 +- .../kostal_plenticore/translations/nl.json | 3 +- .../kostal_plenticore/translations/no.json | 3 +- .../kostal_plenticore/translations/pl.json | 3 +- .../kostal_plenticore/translations/pt-BR.json | 3 +- .../kostal_plenticore/translations/ru.json | 3 +- .../kostal_plenticore/translations/tr.json | 3 +- .../translations/zh-Hant.json | 3 +- .../components/kraken/translations/it.json | 8 +- .../components/light/translations/hu.json | 1 + .../logi_circle/translations/hu.json | 4 +- .../logi_circle/translations/it.json | 2 +- .../components/lookin/translations/hu.json | 4 +- .../components/luftdaten/translations/hu.json | 2 +- .../luftdaten/translations/pt-BR.json | 2 +- .../lutron_caseta/translations/pl.json | 2 +- .../components/lyric/translations/hu.json | 2 +- .../components/lyric/translations/it.json | 2 +- .../components/mailgun/translations/ja.json | 2 +- .../components/mazda/translations/ca.json | 6 +- .../components/mazda/translations/de.json | 6 +- .../components/mazda/translations/el.json | 6 +- .../components/mazda/translations/en.json | 6 +- .../components/mazda/translations/es.json | 6 +- .../components/mazda/translations/et.json | 6 +- .../components/mazda/translations/fr.json | 6 +- .../components/mazda/translations/hu.json | 6 +- .../components/mazda/translations/id.json | 6 +- .../components/mazda/translations/it.json | 6 +- .../components/mazda/translations/ja.json | 6 +- .../components/mazda/translations/ko.json | 6 +- .../components/mazda/translations/nl.json | 6 +- .../components/mazda/translations/no.json | 6 +- .../components/mazda/translations/pl.json | 6 +- .../components/mazda/translations/pt-BR.json | 6 +- .../components/mazda/translations/ru.json | 6 +- .../components/mazda/translations/tr.json | 6 +- .../mazda/translations/zh-Hant.json | 6 +- .../components/meater/translations/bg.json | 17 ++ .../components/meater/translations/ca.json | 18 ++ .../components/meater/translations/cs.json | 16 ++ .../components/meater/translations/de.json | 18 ++ .../components/meater/translations/el.json | 18 ++ .../components/meater/translations/et.json | 18 ++ .../components/meater/translations/fr.json | 18 ++ .../components/meater/translations/he.json | 16 ++ .../components/meater/translations/hu.json | 18 ++ .../components/meater/translations/id.json | 18 ++ .../components/meater/translations/it.json | 18 ++ .../components/meater/translations/ja.json | 18 ++ .../components/meater/translations/nl.json | 18 ++ .../components/meater/translations/no.json | 18 ++ .../components/meater/translations/pl.json | 18 ++ .../components/meater/translations/pt-BR.json | 18 ++ .../components/meater/translations/ru.json | 18 ++ .../components/meater/translations/tr.json | 18 ++ .../meater/translations/zh-Hant.json | 18 ++ .../media_player/translations/ca.json | 3 + .../media_player/translations/de.json | 3 + .../media_player/translations/el.json | 3 + .../media_player/translations/et.json | 3 + .../media_player/translations/fr.json | 3 + .../media_player/translations/he.json | 3 + .../media_player/translations/hu.json | 4 + .../media_player/translations/id.json | 3 + .../media_player/translations/it.json | 3 + .../media_player/translations/nl.json | 3 + .../media_player/translations/no.json | 3 + .../media_player/translations/pl.json | 3 + .../media_player/translations/pt-BR.json | 3 + .../media_player/translations/ru.json | 3 + .../media_player/translations/zh-Hant.json | 3 + .../components/met/translations/hu.json | 2 +- .../met_eireann/translations/hu.json | 2 +- .../meteo_france/translations/bg.json | 6 +- .../meteo_france/translations/ca.json | 8 +- .../meteo_france/translations/cs.json | 6 +- .../meteo_france/translations/da.json | 3 +- .../meteo_france/translations/de.json | 6 +- .../meteo_france/translations/el.json | 6 +- .../meteo_france/translations/en.json | 6 +- .../meteo_france/translations/es-419.json | 3 +- .../meteo_france/translations/es.json | 6 +- .../meteo_france/translations/et.json | 6 +- .../meteo_france/translations/fr.json | 6 +- .../meteo_france/translations/hu.json | 6 +- .../meteo_france/translations/id.json | 6 +- .../meteo_france/translations/it.json | 6 +- .../meteo_france/translations/ja.json | 6 +- .../meteo_france/translations/ko.json | 6 +- .../meteo_france/translations/lb.json | 6 +- .../meteo_france/translations/nl.json | 6 +- .../meteo_france/translations/no.json | 6 +- .../meteo_france/translations/pl.json | 6 +- .../meteo_france/translations/pt-BR.json | 6 +- .../meteo_france/translations/pt.json | 3 +- .../meteo_france/translations/ru.json | 6 +- .../meteo_france/translations/sl.json | 3 +- .../meteo_france/translations/sv.json | 3 +- .../meteo_france/translations/tr.json | 6 +- .../meteo_france/translations/uk.json | 6 +- .../meteo_france/translations/zh-Hant.json | 6 +- .../meteoclimatic/translations/bg.json | 5 - .../meteoclimatic/translations/ca.json | 5 +- .../meteoclimatic/translations/de.json | 5 +- .../meteoclimatic/translations/el.json | 5 +- .../meteoclimatic/translations/en.json | 5 +- .../meteoclimatic/translations/es.json | 4 +- .../meteoclimatic/translations/et.json | 5 +- .../meteoclimatic/translations/fr.json | 5 +- .../meteoclimatic/translations/gl.json | 9 - .../meteoclimatic/translations/hu.json | 5 +- .../meteoclimatic/translations/id.json | 5 +- .../meteoclimatic/translations/it.json | 5 +- .../meteoclimatic/translations/ja.json | 5 +- .../meteoclimatic/translations/nl.json | 5 +- .../meteoclimatic/translations/no.json | 5 +- .../meteoclimatic/translations/pl.json | 5 +- .../meteoclimatic/translations/pt-BR.json | 5 +- .../meteoclimatic/translations/pt.json | 9 - .../meteoclimatic/translations/ru.json | 5 +- .../meteoclimatic/translations/tr.json | 5 +- .../meteoclimatic/translations/zh-Hant.json | 5 +- .../components/mikrotik/translations/hu.json | 2 +- .../components/min_max/translations/bg.json | 11 + .../components/min_max/translations/ca.json | 42 ++++ .../components/min_max/translations/cs.json | 34 +++ .../components/min_max/translations/de.json | 42 ++++ .../components/min_max/translations/el.json | 42 ++++ .../components/min_max/translations/en.json | 8 + .../components/min_max/translations/et.json | 42 ++++ .../components/min_max/translations/fr.json | 42 ++++ .../components/min_max/translations/he.json | 37 ++++ .../components/min_max/translations/hu.json | 42 ++++ .../components/min_max/translations/id.json | 42 ++++ .../components/min_max/translations/it.json | 42 ++++ .../components/min_max/translations/ja.json | 42 ++++ .../components/min_max/translations/nl.json | 42 ++++ .../components/min_max/translations/no.json | 42 ++++ .../components/min_max/translations/pl.json | 42 ++++ .../min_max/translations/pt-BR.json | 42 ++++ .../components/min_max/translations/ru.json | 42 ++++ .../components/min_max/translations/sv.json | 11 + .../components/min_max/translations/tr.json | 42 ++++ .../min_max/translations/zh-Hans.json | 42 ++++ .../min_max/translations/zh-Hant.json | 42 ++++ .../minecraft_server/translations/hu.json | 2 +- .../minecraft_server/translations/pl.json | 2 +- .../components/mjpeg/translations/fr.json | 2 + .../components/mjpeg/translations/hu.json | 4 +- .../modem_callerid/translations/hu.json | 4 +- .../modern_forms/translations/et.json | 2 +- .../components/moon/translations/cs.json | 13 ++ .../moon/translations/sensor.fi.json | 6 +- .../motion_blinds/translations/ca.json | 4 +- .../motion_blinds/translations/de.json | 4 +- .../motion_blinds/translations/en.json | 6 +- .../motion_blinds/translations/et.json | 4 +- .../motion_blinds/translations/fr.json | 4 +- .../motion_blinds/translations/he.json | 2 +- .../motion_blinds/translations/hu.json | 12 +- .../motion_blinds/translations/id.json | 4 +- .../motion_blinds/translations/it.json | 4 +- .../motion_blinds/translations/nl.json | 4 +- .../motion_blinds/translations/no.json | 4 +- .../motion_blinds/translations/pl.json | 4 +- .../motion_blinds/translations/pt-BR.json | 4 +- .../motion_blinds/translations/ru.json | 4 +- .../motion_blinds/translations/tr.json | 4 +- .../motion_blinds/translations/zh-Hant.json | 4 +- .../components/nam/translations/hu.json | 2 +- .../components/nanoleaf/translations/fr.json | 8 + .../components/nanoleaf/translations/hu.json | 2 +- .../components/neato/translations/hu.json | 4 +- .../components/neato/translations/it.json | 2 +- .../components/nest/translations/hu.json | 8 +- .../components/nest/translations/it.json | 2 +- .../components/nest/translations/pl.json | 2 +- .../components/netatmo/translations/hu.json | 4 +- .../components/netatmo/translations/it.json | 2 +- .../netatmo/translations/pt-BR.json | 2 +- .../nfandroidtv/translations/ca.json | 2 +- .../nfandroidtv/translations/de.json | 2 +- .../nfandroidtv/translations/en.json | 2 +- .../nfandroidtv/translations/et.json | 2 +- .../nfandroidtv/translations/fr.json | 2 +- .../nfandroidtv/translations/he.json | 2 +- .../nfandroidtv/translations/hu.json | 4 +- .../nfandroidtv/translations/id.json | 2 +- .../nfandroidtv/translations/it.json | 2 +- .../nfandroidtv/translations/nl.json | 2 +- .../nfandroidtv/translations/no.json | 2 +- .../nfandroidtv/translations/pl.json | 2 +- .../nfandroidtv/translations/pt-BR.json | 2 +- .../nfandroidtv/translations/ru.json | 2 +- .../nfandroidtv/translations/tr.json | 2 +- .../nfandroidtv/translations/zh-Hant.json | 2 +- .../components/nina/translations/zh-Hant.json | 18 +- .../nmap_tracker/translations/en.json | 3 +- .../nmap_tracker/translations/et.json | 4 +- .../nmap_tracker/translations/hu.json | 4 +- .../nmap_tracker/translations/no.json | 4 +- .../nmap_tracker/translations/pt-BR.json | 12 +- .../nmap_tracker/translations/tr.json | 4 +- .../components/nzbget/translations/hu.json | 2 +- .../ondilo_ico/translations/hu.json | 2 +- .../ondilo_ico/translations/it.json | 2 +- .../components/onewire/translations/es.json | 5 + .../components/onewire/translations/fr.json | 3 +- .../components/onewire/translations/nl.json | 4 + .../components/onvif/translations/hu.json | 8 +- .../open_meteo/translations/ca.json | 2 +- .../opentherm_gw/translations/hu.json | 2 +- .../openweathermap/translations/ca.json | 4 +- .../openweathermap/translations/de.json | 4 +- .../openweathermap/translations/en.json | 4 +- .../openweathermap/translations/et.json | 4 +- .../openweathermap/translations/fr.json | 4 +- .../openweathermap/translations/he.json | 2 +- .../openweathermap/translations/hu.json | 4 +- .../openweathermap/translations/id.json | 4 +- .../openweathermap/translations/it.json | 4 +- .../openweathermap/translations/nl.json | 4 +- .../openweathermap/translations/no.json | 4 +- .../openweathermap/translations/pl.json | 4 +- .../openweathermap/translations/pt-BR.json | 4 +- .../openweathermap/translations/ru.json | 2 +- .../openweathermap/translations/tr.json | 4 +- .../openweathermap/translations/zh-Hant.json | 4 +- .../components/overkiz/translations/ca.json | 1 + .../components/overkiz/translations/de.json | 1 + .../components/overkiz/translations/el.json | 1 + .../components/overkiz/translations/et.json | 1 + .../components/overkiz/translations/fr.json | 1 + .../components/overkiz/translations/hu.json | 1 + .../components/overkiz/translations/id.json | 1 + .../components/overkiz/translations/it.json | 1 + .../components/overkiz/translations/ja.json | 1 + .../components/overkiz/translations/nl.json | 1 + .../components/overkiz/translations/no.json | 1 + .../components/overkiz/translations/pl.json | 1 + .../overkiz/translations/pt-BR.json | 1 + .../components/overkiz/translations/ru.json | 1 + .../components/overkiz/translations/tr.json | 1 + .../overkiz/translations/zh-Hant.json | 1 + .../components/owntracks/translations/cs.json | 1 + .../p1_monitor/translations/hu.json | 2 +- .../panasonic_viera/translations/hu.json | 2 +- .../components/peco/translations/bg.json | 7 + .../components/peco/translations/ca.json | 16 ++ .../components/peco/translations/cs.json | 7 + .../components/peco/translations/de.json | 16 ++ .../components/peco/translations/el.json | 16 ++ .../components/peco/translations/en.json | 4 +- .../components/peco/translations/et.json | 16 ++ .../components/peco/translations/fr.json | 16 ++ .../components/peco/translations/he.json | 7 + .../components/peco/translations/hu.json | 16 ++ .../components/peco/translations/id.json | 16 ++ .../components/peco/translations/it.json | 16 ++ .../components/peco/translations/ja.json | 16 ++ .../components/peco/translations/nl.json | 16 ++ .../components/peco/translations/no.json | 16 ++ .../components/peco/translations/pl.json | 16 ++ .../components/peco/translations/pt-BR.json | 16 ++ .../components/peco/translations/ru.json | 16 ++ .../components/peco/translations/tr.json | 16 ++ .../components/peco/translations/zh-Hant.json | 16 ++ .../components/pi_hole/translations/hu.json | 2 +- .../components/plaato/translations/cs.json | 1 + .../plant/translations/zh-Hant.json | 2 +- .../components/plex/translations/hu.json | 2 +- .../components/plugwise/translations/ca.json | 1 + .../components/plugwise/translations/de.json | 1 + .../components/plugwise/translations/el.json | 1 + .../components/plugwise/translations/en.json | 1 + .../components/plugwise/translations/et.json | 1 + .../components/plugwise/translations/fr.json | 1 + .../components/plugwise/translations/hu.json | 1 + .../components/plugwise/translations/id.json | 1 + .../components/plugwise/translations/it.json | 1 + .../components/plugwise/translations/ja.json | 1 + .../components/plugwise/translations/nl.json | 1 + .../components/plugwise/translations/no.json | 1 + .../components/plugwise/translations/pl.json | 1 + .../plugwise/translations/pt-BR.json | 1 + .../components/plugwise/translations/ru.json | 1 + .../components/plugwise/translations/tr.json | 1 + .../plugwise/translations/zh-Hant.json | 1 + .../components/point/translations/hu.json | 6 +- .../components/point/translations/it.json | 2 +- .../components/powerwall/translations/de.json | 2 +- .../components/powerwall/translations/es.json | 3 + .../components/ps4/translations/ca.json | 6 + .../components/ps4/translations/de.json | 6 + .../components/ps4/translations/el.json | 6 + .../components/ps4/translations/en.json | 6 + .../components/ps4/translations/et.json | 6 + .../components/ps4/translations/fr.json | 6 + .../components/ps4/translations/hu.json | 16 +- .../components/ps4/translations/id.json | 6 + .../components/ps4/translations/it.json | 6 + .../components/ps4/translations/ja.json | 6 + .../components/ps4/translations/nl.json | 6 + .../components/ps4/translations/no.json | 6 + .../components/ps4/translations/pl.json | 6 + .../components/ps4/translations/pt-BR.json | 6 + .../components/ps4/translations/ru.json | 6 + .../components/ps4/translations/tr.json | 6 + .../components/ps4/translations/zh-Hant.json | 6 + .../pure_energie/translations/cs.json | 19 ++ .../pure_energie/translations/fr.json | 4 + .../pvpc_hourly_pricing/translations/ja.json | 4 +- .../components/qnap_qsw/translations/ca.json | 21 ++ .../components/qnap_qsw/translations/de.json | 21 ++ .../components/qnap_qsw/translations/el.json | 21 ++ .../components/qnap_qsw/translations/en.json | 4 +- .../components/qnap_qsw/translations/et.json | 21 ++ .../components/qnap_qsw/translations/fr.json | 21 ++ .../components/qnap_qsw/translations/he.json | 20 ++ .../components/qnap_qsw/translations/hu.json | 21 ++ .../components/qnap_qsw/translations/id.json | 21 ++ .../components/qnap_qsw/translations/it.json | 21 ++ .../components/qnap_qsw/translations/nl.json | 21 ++ .../components/qnap_qsw/translations/no.json | 21 ++ .../components/qnap_qsw/translations/pl.json | 21 ++ .../qnap_qsw/translations/pt-BR.json | 21 ++ .../components/qnap_qsw/translations/ru.json | 21 ++ .../qnap_qsw/translations/zh-Hant.json | 21 ++ .../radio_browser/translations/cs.json | 7 + .../radio_browser/translations/fr.json | 5 + .../radio_browser/translations/he.json | 5 + .../components/remote/translations/hu.json | 1 + .../components/roku/translations/hu.json | 2 +- .../components/roku/translations/it.json | 8 +- .../components/roomba/translations/pl.json | 4 +- .../components/roon/translations/ca.json | 7 + .../components/roon/translations/de.json | 7 + .../components/roon/translations/el.json | 7 + .../components/roon/translations/en.json | 7 + .../components/roon/translations/et.json | 7 + .../components/roon/translations/fr.json | 7 + .../components/roon/translations/he.json | 6 + .../components/roon/translations/hu.json | 13 +- .../components/roon/translations/id.json | 7 + .../components/roon/translations/it.json | 11 +- .../components/roon/translations/ja.json | 7 + .../components/roon/translations/nl.json | 7 + .../components/roon/translations/no.json | 7 + .../components/roon/translations/pl.json | 7 + .../components/roon/translations/pt-BR.json | 7 + .../components/roon/translations/ru.json | 7 + .../components/roon/translations/tr.json | 7 + .../components/roon/translations/zh-Hant.json | 7 + .../rtsp_to_webrtc/translations/hu.json | 3 +- .../is.json => sabnzbd/translations/bg.json} | 2 +- .../components/sabnzbd/translations/ca.json | 17 ++ .../components/sabnzbd/translations/de.json | 18 ++ .../components/sabnzbd/translations/el.json | 18 ++ .../components/sabnzbd/translations/en.json | 8 +- .../components/sabnzbd/translations/et.json | 18 ++ .../components/sabnzbd/translations/fr.json | 18 ++ .../components/sabnzbd/translations/he.json | 18 ++ .../components/sabnzbd/translations/hu.json | 18 ++ .../components/sabnzbd/translations/id.json | 18 ++ .../components/sabnzbd/translations/it.json | 18 ++ .../components/sabnzbd/translations/nl.json | 18 ++ .../components/sabnzbd/translations/no.json | 18 ++ .../components/sabnzbd/translations/pl.json | 18 ++ .../sabnzbd/translations/pt-BR.json | 18 ++ .../components/sabnzbd/translations/ru.json | 18 ++ .../sabnzbd/translations/zh-Hant.json | 18 ++ .../components/samsungtv/translations/bg.json | 6 + .../components/samsungtv/translations/ca.json | 14 +- .../components/samsungtv/translations/cs.json | 9 + .../components/samsungtv/translations/de.json | 14 +- .../components/samsungtv/translations/el.json | 14 +- .../components/samsungtv/translations/en.json | 4 +- .../components/samsungtv/translations/et.json | 14 +- .../components/samsungtv/translations/fr.json | 16 +- .../components/samsungtv/translations/hu.json | 18 +- .../components/samsungtv/translations/id.json | 14 +- .../components/samsungtv/translations/it.json | 14 +- .../components/samsungtv/translations/ja.json | 12 +- .../components/samsungtv/translations/nl.json | 14 +- .../components/samsungtv/translations/no.json | 14 +- .../components/samsungtv/translations/pl.json | 14 +- .../samsungtv/translations/pt-BR.json | 14 +- .../components/samsungtv/translations/ru.json | 14 +- .../components/samsungtv/translations/tr.json | 14 +- .../samsungtv/translations/zh-Hant.json | 14 +- .../screenlogic/translations/fr.json | 2 +- .../components/season/translations/cs.json | 14 ++ .../components/sense/translations/cs.json | 9 +- .../components/sense/translations/fr.json | 4 +- .../components/sensibo/translations/hu.json | 2 +- .../components/sensor/translations/pl.json | 4 +- .../components/sentry/translations/ca.json | 2 +- .../components/sentry/translations/de.json | 2 +- .../components/sentry/translations/en.json | 2 +- .../components/sentry/translations/et.json | 2 +- .../components/sentry/translations/fr.json | 2 +- .../components/sentry/translations/he.json | 2 +- .../components/sentry/translations/hu.json | 2 +- .../components/sentry/translations/id.json | 2 +- .../components/sentry/translations/it.json | 2 +- .../components/sentry/translations/nl.json | 2 +- .../components/sentry/translations/no.json | 2 +- .../components/sentry/translations/pl.json | 2 +- .../components/sentry/translations/pt-BR.json | 2 +- .../components/sentry/translations/ru.json | 2 +- .../components/sentry/translations/tr.json | 2 +- .../sentry/translations/zh-Hant.json | 2 +- .../components/senz/translations/bg.json | 7 + .../components/senz/translations/ca.json | 20 ++ .../components/senz/translations/de.json | 20 ++ .../components/senz/translations/el.json | 20 ++ .../components/senz/translations/et.json | 20 ++ .../components/senz/translations/fr.json | 20 ++ .../components/senz/translations/he.json | 20 ++ .../components/senz/translations/hu.json | 20 ++ .../components/senz/translations/id.json | 20 ++ .../components/senz/translations/it.json | 20 ++ .../components/senz/translations/ja.json | 20 ++ .../components/senz/translations/nl.json | 20 ++ .../components/senz/translations/no.json | 20 ++ .../components/senz/translations/pl.json | 20 ++ .../components/senz/translations/pt-BR.json | 20 ++ .../components/senz/translations/ru.json | 20 ++ .../components/senz/translations/tr.json | 20 ++ .../components/senz/translations/zh-Hant.json | 20 ++ .../simplisafe/translations/bg.json | 5 - .../simplisafe/translations/ca.json | 15 +- .../simplisafe/translations/cs.json | 1 - .../simplisafe/translations/de.json | 21 +- .../simplisafe/translations/el.json | 19 +- .../simplisafe/translations/en.json | 14 +- .../simplisafe/translations/es.json | 8 - .../simplisafe/translations/et.json | 21 +- .../simplisafe/translations/fr.json | 25 ++- .../simplisafe/translations/hu.json | 21 +- .../simplisafe/translations/id.json | 21 +- .../simplisafe/translations/it.json | 21 +- .../simplisafe/translations/ja.json | 8 - .../simplisafe/translations/ko.json | 1 - .../simplisafe/translations/lb.json | 1 - .../simplisafe/translations/nl.json | 21 +- .../simplisafe/translations/no.json | 21 +- .../simplisafe/translations/pl.json | 25 ++- .../simplisafe/translations/pt-BR.json | 25 ++- .../simplisafe/translations/ru.json | 21 +- .../simplisafe/translations/sl.json | 7 - .../simplisafe/translations/tr.json | 8 - .../simplisafe/translations/uk.json | 1 - .../simplisafe/translations/zh-Hant.json | 21 +- .../components/sleepiq/translations/fr.json | 1 + .../components/slimproto/translations/ca.json | 7 + .../components/slimproto/translations/de.json | 7 + .../components/slimproto/translations/el.json | 7 + .../components/slimproto/translations/en.json | 14 +- .../components/slimproto/translations/et.json | 7 + .../components/slimproto/translations/fr.json | 7 + .../components/slimproto/translations/he.json | 7 + .../components/slimproto/translations/hu.json | 13 ++ .../components/slimproto/translations/id.json | 7 + .../components/slimproto/translations/it.json | 13 ++ .../components/slimproto/translations/nl.json | 7 + .../components/slimproto/translations/no.json | 7 + .../components/slimproto/translations/pl.json | 7 + .../slimproto/translations/pt-BR.json | 7 + .../components/slimproto/translations/ru.json | 7 + .../slimproto/translations/zh-Hant.json | 7 + .../components/smappee/translations/hu.json | 4 +- .../components/smappee/translations/it.json | 2 +- .../smartthings/translations/it.json | 4 +- .../components/smhi/translations/hu.json | 5 +- .../components/solax/translations/es.json | 7 + .../components/soma/translations/it.json | 2 +- .../components/somfy/translations/hu.json | 4 +- .../components/somfy/translations/it.json | 2 +- .../components/sonarr/translations/cs.json | 1 + .../components/sonarr/translations/hu.json | 2 +- .../components/spotify/translations/hu.json | 4 +- .../components/spotify/translations/it.json | 2 +- .../components/sql/translations/ca.json | 55 +++++ .../components/sql/translations/de.json | 55 +++++ .../components/sql/translations/el.json | 55 +++++ .../components/sql/translations/en.json | 4 +- .../components/sql/translations/et.json | 55 +++++ .../components/sql/translations/fr.json | 55 +++++ .../components/sql/translations/he.json | 7 + .../components/sql/translations/hu.json | 55 +++++ .../components/sql/translations/id.json | 55 +++++ .../components/sql/translations/it.json | 55 +++++ .../components/sql/translations/nl.json | 55 +++++ .../components/sql/translations/no.json | 55 +++++ .../components/sql/translations/pl.json | 55 +++++ .../components/sql/translations/pt-BR.json | 55 +++++ .../components/sql/translations/ru.json | 55 +++++ .../components/sql/translations/tr.json | 55 +++++ .../components/sql/translations/zh-Hant.json | 55 +++++ .../srp_energy/translations/ca.json | 3 +- .../srp_energy/translations/cs.json | 3 +- .../srp_energy/translations/de.json | 3 +- .../srp_energy/translations/el.json | 3 +- .../srp_energy/translations/en.json | 3 +- .../srp_energy/translations/es.json | 3 +- .../srp_energy/translations/et.json | 3 +- .../srp_energy/translations/fr.json | 3 +- .../srp_energy/translations/hu.json | 3 +- .../srp_energy/translations/id.json | 3 +- .../srp_energy/translations/it.json | 3 +- .../srp_energy/translations/ja.json | 3 +- .../srp_energy/translations/ka.json | 3 +- .../srp_energy/translations/ko.json | 3 +- .../srp_energy/translations/nl.json | 3 +- .../srp_energy/translations/no.json | 3 +- .../srp_energy/translations/pl.json | 3 +- .../srp_energy/translations/pt-BR.json | 3 +- .../srp_energy/translations/ru.json | 3 +- .../srp_energy/translations/tr.json | 3 +- .../srp_energy/translations/uk.json | 3 +- .../srp_energy/translations/zh-Hans.json | 3 +- .../srp_energy/translations/zh-Hant.json | 3 +- .../steam_online/translations/ca.json | 24 +++ .../steam_online/translations/de.json | 36 ++++ .../steam_online/translations/el.json | 36 ++++ .../steam_online/translations/en.json | 18 +- .../steam_online/translations/et.json | 36 ++++ .../steam_online/translations/fr.json | 36 ++++ .../steam_online/translations/he.json | 23 ++ .../steam_online/translations/hu.json | 36 ++++ .../steam_online/translations/id.json | 36 ++++ .../steam_online/translations/it.json | 36 ++++ .../steam_online/translations/nl.json | 36 ++++ .../steam_online/translations/no.json | 36 ++++ .../steam_online/translations/pl.json | 36 ++++ .../steam_online/translations/pt-BR.json | 36 ++++ .../steam_online/translations/ru.json | 36 ++++ .../steam_online/translations/zh-Hant.json | 36 ++++ .../components/steamist/translations/hu.json | 2 +- .../components/subaru/translations/ca.json | 19 +- .../components/subaru/translations/de.json | 19 +- .../components/subaru/translations/el.json | 19 +- .../components/subaru/translations/en.json | 19 +- .../components/subaru/translations/et.json | 19 +- .../components/subaru/translations/fr.json | 19 +- .../components/subaru/translations/hu.json | 21 +- .../components/subaru/translations/id.json | 19 +- .../components/subaru/translations/it.json | 19 +- .../components/subaru/translations/ja.json | 19 +- .../components/subaru/translations/nl.json | 19 +- .../components/subaru/translations/no.json | 19 +- .../components/subaru/translations/pl.json | 19 +- .../components/subaru/translations/pt-BR.json | 19 +- .../components/subaru/translations/ru.json | 19 +- .../components/subaru/translations/tr.json | 19 +- .../subaru/translations/zh-Hant.json | 19 +- .../components/sun/translations/bg.json | 5 + .../components/sun/translations/ca.json | 10 + .../components/sun/translations/cs.json | 10 + .../components/sun/translations/de.json | 10 + .../components/sun/translations/el.json | 10 + .../components/sun/translations/en.json | 10 + .../components/sun/translations/et.json | 10 + .../components/sun/translations/fr.json | 10 + .../components/sun/translations/he.json | 10 + .../components/sun/translations/hu.json | 10 + .../components/sun/translations/id.json | 10 + .../components/sun/translations/it.json | 10 + .../components/sun/translations/ja.json | 10 + .../components/sun/translations/nl.json | 10 + .../components/sun/translations/no.json | 10 + .../components/sun/translations/pl.json | 10 + .../components/sun/translations/pt-BR.json | 10 + .../components/sun/translations/ru.json | 10 + .../components/sun/translations/tr.json | 10 + .../components/sun/translations/zh-Hant.json | 10 + .../components/switch/translations/cs.json | 10 + .../components/switch/translations/el.json | 4 +- .../components/switch/translations/hu.json | 1 + .../components/switch/translations/pt-BR.json | 4 +- .../switch_as_x/translations/bg.json | 11 + .../switch_as_x/translations/ca.json | 16 +- .../switch_as_x/translations/cs.json | 18 ++ .../switch_as_x/translations/de.json | 16 +- .../switch_as_x/translations/el.json | 10 +- .../switch_as_x/translations/en.json | 8 +- .../switch_as_x/translations/et.json | 16 +- .../switch_as_x/translations/fr.json | 16 +- .../switch_as_x/translations/he.json | 4 +- .../switch_as_x/translations/hu.json | 16 +- .../switch_as_x/translations/id.json | 16 +- .../switch_as_x/translations/it.json | 16 +- .../switch_as_x/translations/ja.json | 10 +- .../switch_as_x/translations/nl.json | 16 +- .../switch_as_x/translations/no.json | 16 +- .../switch_as_x/translations/pl.json | 16 +- .../switch_as_x/translations/pt-BR.json | 16 +- .../switch_as_x/translations/pt.json | 2 +- .../switch_as_x/translations/ru.json | 14 +- .../switch_as_x/translations/sv.json | 8 +- .../switch_as_x/translations/tr.json | 20 ++ .../switch_as_x/translations/zh-Hans.json | 20 ++ .../switch_as_x/translations/zh-Hant.json | 16 +- .../components/switchbot/translations/fr.json | 2 +- .../components/switchbot/translations/hu.json | 2 +- .../components/switchbot/translations/it.json | 4 +- .../components/syncthing/translations/ca.json | 3 +- .../components/syncthing/translations/de.json | 3 +- .../components/syncthing/translations/el.json | 3 +- .../components/syncthing/translations/en.json | 3 +- .../components/syncthing/translations/es.json | 3 +- .../components/syncthing/translations/et.json | 3 +- .../components/syncthing/translations/fr.json | 3 +- .../components/syncthing/translations/he.json | 3 +- .../components/syncthing/translations/hu.json | 3 +- .../components/syncthing/translations/id.json | 3 +- .../components/syncthing/translations/it.json | 3 +- .../components/syncthing/translations/ja.json | 3 +- .../components/syncthing/translations/nl.json | 3 +- .../components/syncthing/translations/no.json | 3 +- .../components/syncthing/translations/pl.json | 3 +- .../syncthing/translations/pt-BR.json | 3 +- .../components/syncthing/translations/ru.json | 3 +- .../components/syncthing/translations/sv.json | 3 +- .../components/syncthing/translations/tr.json | 3 +- .../syncthing/translations/zh-Hans.json | 3 +- .../syncthing/translations/zh-Hant.json | 3 +- .../components/syncthru/translations/hu.json | 4 +- .../system_bridge/translations/ca.json | 3 +- .../system_bridge/translations/de.json | 3 +- .../system_bridge/translations/el.json | 3 +- .../system_bridge/translations/en.json | 3 +- .../system_bridge/translations/es.json | 3 +- .../system_bridge/translations/et.json | 3 +- .../system_bridge/translations/fr.json | 3 +- .../system_bridge/translations/he.json | 3 +- .../system_bridge/translations/hu.json | 3 +- .../system_bridge/translations/id.json | 3 +- .../system_bridge/translations/it.json | 3 +- .../system_bridge/translations/ja.json | 3 +- .../system_bridge/translations/nl.json | 3 +- .../system_bridge/translations/no.json | 3 +- .../system_bridge/translations/pl.json | 3 +- .../system_bridge/translations/pt-BR.json | 3 +- .../system_bridge/translations/ru.json | 3 +- .../system_bridge/translations/tr.json | 3 +- .../system_bridge/translations/zh-Hant.json | 3 +- .../system_health/translations/pt-BR.json | 2 +- .../components/tado/translations/ca.json | 4 +- .../components/tado/translations/de.json | 4 +- .../components/tado/translations/en.json | 4 +- .../components/tado/translations/et.json | 4 +- .../components/tado/translations/fr.json | 4 +- .../components/tado/translations/hu.json | 4 +- .../components/tado/translations/id.json | 4 +- .../components/tado/translations/it.json | 4 +- .../components/tado/translations/nl.json | 4 +- .../components/tado/translations/no.json | 4 +- .../components/tado/translations/pl.json | 4 +- .../components/tado/translations/pt-BR.json | 4 +- .../components/tado/translations/ru.json | 4 +- .../components/tado/translations/tr.json | 4 +- .../components/tado/translations/zh-Hant.json | 4 +- .../components/tailscale/translations/ca.json | 2 +- .../components/tailscale/translations/de.json | 2 +- .../components/tailscale/translations/et.json | 2 +- .../components/tailscale/translations/fr.json | 2 +- .../components/tailscale/translations/hu.json | 2 +- .../components/tailscale/translations/id.json | 2 +- .../components/tailscale/translations/it.json | 2 +- .../components/tailscale/translations/nl.json | 2 +- .../components/tailscale/translations/no.json | 2 +- .../components/tailscale/translations/pl.json | 2 +- .../tailscale/translations/pt-BR.json | 2 +- .../components/tailscale/translations/ru.json | 2 +- .../components/tailscale/translations/tr.json | 2 +- .../tailscale/translations/zh-Hant.json | 2 +- .../tankerkoenig/translations/bg.json | 29 +++ .../tankerkoenig/translations/ca.json | 41 ++++ .../tankerkoenig/translations/de.json | 41 ++++ .../tankerkoenig/translations/el.json | 41 ++++ .../tankerkoenig/translations/et.json | 41 ++++ .../tankerkoenig/translations/fr.json | 41 ++++ .../tankerkoenig/translations/he.json | 18 ++ .../tankerkoenig/translations/hu.json | 41 ++++ .../tankerkoenig/translations/id.json | 41 ++++ .../tankerkoenig/translations/it.json | 41 ++++ .../tankerkoenig/translations/ja.json | 41 ++++ .../tankerkoenig/translations/nl.json | 41 ++++ .../tankerkoenig/translations/no.json | 41 ++++ .../tankerkoenig/translations/pl.json | 41 ++++ .../tankerkoenig/translations/pt-BR.json | 41 ++++ .../tankerkoenig/translations/ru.json | 41 ++++ .../tankerkoenig/translations/tr.json | 41 ++++ .../tankerkoenig/translations/zh-Hant.json | 41 ++++ .../components/tautulli/translations/ca.json | 27 +++ .../components/tautulli/translations/de.json | 30 +++ .../components/tautulli/translations/el.json | 30 +++ .../components/tautulli/translations/en.json | 55 +++-- .../components/tautulli/translations/et.json | 30 +++ .../components/tautulli/translations/fr.json | 30 +++ .../components/tautulli/translations/he.json | 27 +++ .../components/tautulli/translations/hu.json | 30 +++ .../components/tautulli/translations/id.json | 30 +++ .../components/tautulli/translations/it.json | 30 +++ .../components/tautulli/translations/nl.json | 28 +++ .../components/tautulli/translations/no.json | 30 +++ .../components/tautulli/translations/pl.json | 30 +++ .../tautulli/translations/pt-BR.json | 30 +++ .../components/tautulli/translations/ru.json | 30 +++ .../tautulli/translations/zh-Hant.json | 30 +++ .../tellduslive/translations/hu.json | 2 +- .../tesla_wall_connector/translations/ca.json | 10 - .../tesla_wall_connector/translations/de.json | 10 - .../tesla_wall_connector/translations/el.json | 10 - .../tesla_wall_connector/translations/en.json | 10 - .../tesla_wall_connector/translations/es.json | 10 - .../tesla_wall_connector/translations/et.json | 10 - .../tesla_wall_connector/translations/fr.json | 10 - .../tesla_wall_connector/translations/hu.json | 10 - .../tesla_wall_connector/translations/id.json | 10 - .../tesla_wall_connector/translations/it.json | 10 - .../tesla_wall_connector/translations/ja.json | 10 - .../tesla_wall_connector/translations/nl.json | 10 - .../tesla_wall_connector/translations/no.json | 10 - .../tesla_wall_connector/translations/pl.json | 10 - .../translations/pt-BR.json | 10 - .../tesla_wall_connector/translations/ru.json | 10 - .../tesla_wall_connector/translations/tr.json | 10 - .../translations/zh-Hans.json | 10 - .../translations/zh-Hant.json | 10 - .../components/threshold/translations/bg.json | 22 ++ .../components/threshold/translations/ca.json | 40 ++++ .../components/threshold/translations/de.json | 40 ++++ .../components/threshold/translations/el.json | 40 ++++ .../components/threshold/translations/en.json | 2 + .../components/threshold/translations/et.json | 40 ++++ .../components/threshold/translations/fr.json | 40 ++++ .../components/threshold/translations/hu.json | 40 ++++ .../components/threshold/translations/id.json | 40 ++++ .../components/threshold/translations/it.json | 40 ++++ .../components/threshold/translations/ja.json | 40 ++++ .../components/threshold/translations/nl.json | 40 ++++ .../components/threshold/translations/no.json | 40 ++++ .../components/threshold/translations/pl.json | 40 ++++ .../threshold/translations/pt-BR.json | 40 ++++ .../components/threshold/translations/ru.json | 40 ++++ .../components/threshold/translations/tr.json | 40 ++++ .../threshold/translations/zh-Hans.json | 40 ++++ .../threshold/translations/zh-Hant.json | 40 ++++ .../components/tod/translations/bg.json | 11 + .../components/tod/translations/ca.json | 31 +++ .../components/tod/translations/de.json | 31 +++ .../components/tod/translations/el.json | 31 +++ .../components/tod/translations/en.json | 7 +- .../components/tod/translations/et.json | 31 +++ .../components/tod/translations/fr.json | 31 +++ .../components/tod/translations/he.json | 30 +++ .../components/tod/translations/hu.json | 31 +++ .../components/tod/translations/id.json | 31 +++ .../components/tod/translations/it.json | 31 +++ .../components/tod/translations/ja.json | 31 +++ .../components/tod/translations/nl.json | 31 +++ .../components/tod/translations/no.json | 31 +++ .../components/tod/translations/pl.json | 31 +++ .../components/tod/translations/pt-BR.json | 31 +++ .../components/tod/translations/ru.json | 31 +++ .../components/tod/translations/tr.json | 31 +++ .../components/tod/translations/zh-Hans.json | 31 +++ .../components/tod/translations/zh-Hant.json | 31 +++ .../tomorrowio/translations/bg.json | 20 ++ .../tomorrowio/translations/ca.json | 33 +++ .../tomorrowio/translations/cs.json | 11 + .../tomorrowio/translations/de.json | 33 +++ .../tomorrowio/translations/el.json | 33 +++ .../tomorrowio/translations/en.json | 1 + .../tomorrowio/translations/et.json | 33 +++ .../tomorrowio/translations/fr.json | 33 +++ .../tomorrowio/translations/he.json | 20 ++ .../tomorrowio/translations/hu.json | 33 +++ .../tomorrowio/translations/id.json | 33 +++ .../tomorrowio/translations/it.json | 33 +++ .../tomorrowio/translations/ja.json | 33 +++ .../tomorrowio/translations/nl.json | 33 +++ .../tomorrowio/translations/no.json | 33 +++ .../tomorrowio/translations/pl.json | 33 +++ .../tomorrowio/translations/pt-BR.json | 33 +++ .../tomorrowio/translations/ru.json | 33 +++ .../tomorrowio/translations/sensor.bg.json | 8 + .../tomorrowio/translations/sensor.ca.json | 27 +++ .../tomorrowio/translations/sensor.de.json | 27 +++ .../tomorrowio/translations/sensor.el.json | 27 +++ .../tomorrowio/translations/sensor.et.json | 27 +++ .../tomorrowio/translations/sensor.fr.json | 27 +++ .../tomorrowio/translations/sensor.hu.json | 27 +++ .../tomorrowio/translations/sensor.id.json | 27 +++ .../tomorrowio/translations/sensor.it.json | 27 +++ .../tomorrowio/translations/sensor.ja.json | 27 +++ .../tomorrowio/translations/sensor.nl.json | 27 +++ .../tomorrowio/translations/sensor.no.json | 27 +++ .../tomorrowio/translations/sensor.pl.json | 27 +++ .../tomorrowio/translations/sensor.pt-BR.json | 27 +++ .../tomorrowio/translations/sensor.ru.json | 27 +++ .../tomorrowio/translations/sensor.tr.json | 27 +++ .../translations/sensor.zh-Hant.json | 27 +++ .../tomorrowio/translations/tr.json | 33 +++ .../tomorrowio/translations/zh-Hant.json | 33 +++ .../components/toon/translations/hu.json | 2 +- .../components/toon/translations/it.json | 2 +- .../components/tplink/translations/it.json | 2 +- .../components/tradfri/translations/hu.json | 2 +- .../trafikverket_ferry/translations/ca.json | 25 +++ .../trafikverket_ferry/translations/de.json | 30 +++ .../trafikverket_ferry/translations/el.json | 30 +++ .../trafikverket_ferry/translations/et.json | 30 +++ .../trafikverket_ferry/translations/fr.json | 30 +++ .../trafikverket_ferry/translations/he.json | 24 +++ .../trafikverket_ferry/translations/hu.json | 30 +++ .../trafikverket_ferry/translations/id.json | 30 +++ .../trafikverket_ferry/translations/it.json | 30 +++ .../trafikverket_ferry/translations/nl.json | 30 +++ .../trafikverket_ferry/translations/no.json | 30 +++ .../trafikverket_ferry/translations/pl.json | 30 +++ .../translations/pt-BR.json | 30 +++ .../trafikverket_ferry/translations/ru.json | 30 +++ .../translations/zh-Hant.json | 30 +++ .../trafikverket_train/translations/bg.json | 24 +++ .../trafikverket_train/translations/ca.json | 32 +++ .../trafikverket_train/translations/de.json | 32 +++ .../trafikverket_train/translations/el.json | 32 +++ .../trafikverket_train/translations/en.json | 22 +- .../trafikverket_train/translations/et.json | 32 +++ .../trafikverket_train/translations/fr.json | 32 +++ .../trafikverket_train/translations/he.json | 24 +++ .../trafikverket_train/translations/hu.json | 32 +++ .../trafikverket_train/translations/id.json | 32 +++ .../trafikverket_train/translations/it.json | 32 +++ .../trafikverket_train/translations/ja.json | 32 +++ .../trafikverket_train/translations/nl.json | 32 +++ .../trafikverket_train/translations/no.json | 32 +++ .../trafikverket_train/translations/pl.json | 32 +++ .../translations/pt-BR.json | 32 +++ .../trafikverket_train/translations/ru.json | 32 +++ .../trafikverket_train/translations/tr.json | 32 +++ .../translations/zh-Hant.json | 32 +++ .../transmission/translations/hu.json | 2 +- .../tuya/translations/select.he.json | 2 +- .../tuya/translations/select.pt-BR.json | 12 +- .../twentemilieu/translations/bg.json | 3 +- .../twentemilieu/translations/ca.json | 3 +- .../twentemilieu/translations/cs.json | 3 +- .../twentemilieu/translations/da.json | 3 +- .../twentemilieu/translations/de.json | 3 +- .../twentemilieu/translations/el.json | 3 +- .../twentemilieu/translations/en.json | 3 +- .../twentemilieu/translations/es-419.json | 3 +- .../twentemilieu/translations/es.json | 3 +- .../twentemilieu/translations/et.json | 3 +- .../twentemilieu/translations/fr.json | 3 +- .../twentemilieu/translations/hu.json | 3 +- .../twentemilieu/translations/id.json | 3 +- .../twentemilieu/translations/it.json | 3 +- .../twentemilieu/translations/ja.json | 3 +- .../twentemilieu/translations/ko.json | 3 +- .../twentemilieu/translations/lb.json | 3 +- .../twentemilieu/translations/nl.json | 3 +- .../twentemilieu/translations/nn.json | 9 - .../twentemilieu/translations/no.json | 3 +- .../twentemilieu/translations/pl.json | 3 +- .../twentemilieu/translations/pt-BR.json | 3 +- .../twentemilieu/translations/ru.json | 3 +- .../twentemilieu/translations/sl.json | 3 +- .../twentemilieu/translations/sv.json | 3 +- .../twentemilieu/translations/tr.json | 3 +- .../twentemilieu/translations/uk.json | 3 +- .../twentemilieu/translations/zh-Hant.json | 3 +- .../components/twilio/translations/cs.json | 1 + .../components/twilio/translations/id.json | 2 +- .../components/twilio/translations/ja.json | 2 +- .../components/twinkly/translations/ca.json | 4 +- .../components/twinkly/translations/cs.json | 4 +- .../components/twinkly/translations/de.json | 4 +- .../components/twinkly/translations/el.json | 4 +- .../components/twinkly/translations/en.json | 4 +- .../components/twinkly/translations/es.json | 4 +- .../components/twinkly/translations/et.json | 4 +- .../components/twinkly/translations/fr.json | 4 +- .../components/twinkly/translations/hu.json | 6 +- .../components/twinkly/translations/id.json | 4 +- .../components/twinkly/translations/it.json | 4 +- .../components/twinkly/translations/ja.json | 4 +- .../components/twinkly/translations/ka.json | 4 +- .../components/twinkly/translations/ko.json | 4 +- .../components/twinkly/translations/nl.json | 4 +- .../components/twinkly/translations/no.json | 4 +- .../components/twinkly/translations/pl.json | 4 +- .../twinkly/translations/pt-BR.json | 4 +- .../components/twinkly/translations/ru.json | 4 +- .../components/twinkly/translations/tr.json | 4 +- .../components/twinkly/translations/uk.json | 4 +- .../twinkly/translations/zh-Hant.json | 4 +- .../components/unifi/translations/hu.json | 4 +- .../components/unifi/translations/it.json | 4 +- .../components/update/translations/ca.json | 7 + .../components/update/translations/cs.json | 9 + .../components/update/translations/de.json | 7 + .../components/update/translations/el.json | 7 + .../components/update/translations/es.json | 3 + .../components/update/translations/et.json | 7 + .../components/update/translations/fr.json | 7 + .../components/update/translations/he.json | 10 + .../components/update/translations/hu.json | 7 + .../components/update/translations/id.json | 7 + .../components/update/translations/it.json | 7 + .../components/update/translations/ja.json | 7 + .../components/update/translations/nl.json | 7 + .../components/update/translations/no.json | 7 + .../components/update/translations/pl.json | 7 + .../components/update/translations/pt-BR.json | 7 + .../components/update/translations/ru.json | 7 + .../components/update/translations/tr.json | 7 + .../update/translations/zh-Hans.json | 9 + .../update/translations/zh-Hant.json | 7 + .../components/upnp/translations/it.json | 8 +- .../components/uptime/translations/bg.json | 12 ++ .../components/uptime/translations/cs.json | 12 ++ .../components/uptime/translations/id.json | 13 ++ .../components/uptime/translations/it.json | 3 + .../components/uptime/translations/no.json | 13 ++ .../components/uptime/translations/pl.json | 13 ++ .../components/uptime/translations/pt-BR.json | 2 +- .../components/uptime/translations/tr.json | 13 ++ .../uptimerobot/translations/ca.json | 5 +- .../uptimerobot/translations/de.json | 5 +- .../uptimerobot/translations/el.json | 1 + .../uptimerobot/translations/et.json | 5 +- .../uptimerobot/translations/fr.json | 5 +- .../uptimerobot/translations/hu.json | 5 +- .../uptimerobot/translations/id.json | 5 +- .../uptimerobot/translations/it.json | 5 +- .../uptimerobot/translations/ja.json | 1 + .../uptimerobot/translations/nl.json | 5 +- .../uptimerobot/translations/no.json | 5 +- .../uptimerobot/translations/pl.json | 5 +- .../uptimerobot/translations/pt-BR.json | 5 +- .../uptimerobot/translations/ru.json | 5 +- .../uptimerobot/translations/tr.json | 5 +- .../uptimerobot/translations/zh-Hant.json | 5 +- .../utility_meter/translations/bg.json | 11 + .../utility_meter/translations/ca.json | 35 ++++ .../utility_meter/translations/cs.json | 35 ++++ .../utility_meter/translations/de.json | 35 ++++ .../utility_meter/translations/el.json | 35 ++++ .../utility_meter/translations/et.json | 35 ++++ .../utility_meter/translations/fr.json | 35 ++++ .../utility_meter/translations/hu.json | 35 ++++ .../utility_meter/translations/id.json | 35 ++++ .../utility_meter/translations/it.json | 35 ++++ .../utility_meter/translations/ja.json | 35 ++++ .../utility_meter/translations/nl.json | 35 ++++ .../utility_meter/translations/no.json | 35 ++++ .../utility_meter/translations/pl.json | 35 ++++ .../utility_meter/translations/pt-BR.json | 35 ++++ .../utility_meter/translations/ru.json | 35 ++++ .../utility_meter/translations/tr.json | 35 ++++ .../utility_meter/translations/zh-Hans.json | 35 ++++ .../utility_meter/translations/zh-Hant.json | 35 ++++ .../components/vallox/translations/bg.json | 6 +- .../components/vallox/translations/ca.json | 7 +- .../components/vallox/translations/cs.json | 7 - .../components/vallox/translations/de.json | 7 +- .../components/vallox/translations/el.json | 7 +- .../components/vallox/translations/en.json | 7 +- .../components/vallox/translations/es.json | 7 +- .../components/vallox/translations/et.json | 7 +- .../components/vallox/translations/fr.json | 7 +- .../components/vallox/translations/he.json | 3 +- .../components/vallox/translations/hu.json | 7 +- .../components/vallox/translations/id.json | 7 +- .../components/vallox/translations/it.json | 7 +- .../components/vallox/translations/ja.json | 7 +- .../components/vallox/translations/lv.json | 9 - .../components/vallox/translations/nl.json | 7 +- .../components/vallox/translations/no.json | 7 +- .../components/vallox/translations/pl.json | 7 +- .../components/vallox/translations/pt-BR.json | 7 +- .../components/vallox/translations/ru.json | 7 +- .../components/vallox/translations/tr.json | 7 +- .../vallox/translations/zh-Hant.json | 7 +- .../components/venstar/translations/pl.json | 2 +- .../components/vera/translations/ca.json | 5 +- .../components/vera/translations/cs.json | 4 +- .../components/vera/translations/de.json | 5 +- .../components/vera/translations/el.json | 5 +- .../components/vera/translations/en.json | 5 +- .../components/vera/translations/es-419.json | 4 +- .../components/vera/translations/es.json | 4 +- .../components/vera/translations/et.json | 5 +- .../components/vera/translations/fr.json | 5 +- .../components/vera/translations/hu.json | 5 +- .../components/vera/translations/id.json | 5 +- .../components/vera/translations/it.json | 5 +- .../components/vera/translations/ja.json | 5 +- .../components/vera/translations/ko.json | 4 +- .../components/vera/translations/lb.json | 4 +- .../components/vera/translations/nl.json | 5 +- .../components/vera/translations/no.json | 5 +- .../components/vera/translations/pl.json | 5 +- .../components/vera/translations/pt-BR.json | 5 +- .../components/vera/translations/ru.json | 5 +- .../components/vera/translations/sl.json | 4 +- .../components/vera/translations/tr.json | 5 +- .../components/vera/translations/uk.json | 4 +- .../components/vera/translations/zh-Hant.json | 5 +- .../components/verisure/translations/hu.json | 4 +- .../components/version/translations/he.json | 11 + .../components/vicare/translations/fr.json | 2 +- .../components/vicare/translations/hu.json | 2 +- .../components/vizio/translations/hu.json | 2 +- .../vlc_telnet/translations/hu.json | 2 +- .../components/vulcan/translations/bg.json | 30 +++ .../components/vulcan/translations/ca.json | 69 ++++++ .../components/vulcan/translations/de.json | 69 ++++++ .../components/vulcan/translations/el.json | 69 ++++++ .../components/vulcan/translations/en.json | 130 ++++++------ .../components/vulcan/translations/et.json | 69 ++++++ .../components/vulcan/translations/fr.json | 69 ++++++ .../components/vulcan/translations/hu.json | 69 ++++++ .../components/vulcan/translations/id.json | 69 ++++++ .../components/vulcan/translations/it.json | 69 ++++++ .../components/vulcan/translations/ja.json | 69 ++++++ .../components/vulcan/translations/nl.json | 69 ++++++ .../components/vulcan/translations/no.json | 69 ++++++ .../components/vulcan/translations/pl.json | 69 ++++++ .../components/vulcan/translations/pt-BR.json | 69 ++++++ .../components/vulcan/translations/ru.json | 69 ++++++ .../components/vulcan/translations/tr.json | 69 ++++++ .../vulcan/translations/zh-Hant.json | 69 ++++++ .../components/wallbox/translations/bg.json | 3 +- .../components/wallbox/translations/ca.json | 3 +- .../components/wallbox/translations/de.json | 3 +- .../components/wallbox/translations/el.json | 3 +- .../components/wallbox/translations/en.json | 3 +- .../components/wallbox/translations/es.json | 3 +- .../components/wallbox/translations/et.json | 3 +- .../components/wallbox/translations/fr.json | 3 +- .../components/wallbox/translations/he.json | 3 +- .../components/wallbox/translations/hu.json | 3 +- .../components/wallbox/translations/id.json | 3 +- .../components/wallbox/translations/it.json | 3 +- .../components/wallbox/translations/ja.json | 3 +- .../components/wallbox/translations/nl.json | 3 +- .../components/wallbox/translations/no.json | 3 +- .../components/wallbox/translations/pl.json | 3 +- .../wallbox/translations/pt-BR.json | 3 +- .../components/wallbox/translations/ru.json | 3 +- .../components/wallbox/translations/tr.json | 3 +- .../wallbox/translations/zh-Hant.json | 3 +- .../waze_travel_time/translations/hu.json | 2 +- .../components/webostv/translations/hu.json | 47 +++++ .../components/wilight/translations/ca.json | 2 +- .../components/wilight/translations/de.json | 2 +- .../components/wilight/translations/en.json | 2 +- .../components/wilight/translations/et.json | 2 +- .../components/wilight/translations/fr.json | 2 +- .../components/wilight/translations/hu.json | 2 +- .../components/wilight/translations/id.json | 2 +- .../components/wilight/translations/it.json | 2 +- .../components/wilight/translations/nl.json | 2 +- .../components/wilight/translations/no.json | 2 +- .../components/wilight/translations/pl.json | 2 +- .../wilight/translations/pt-BR.json | 2 +- .../components/wilight/translations/ru.json | 2 +- .../components/wilight/translations/tr.json | 2 +- .../wilight/translations/zh-Hant.json | 2 +- .../components/withings/translations/hu.json | 4 +- .../components/withings/translations/it.json | 2 +- .../components/wiz/translations/hu.json | 2 +- .../wolflink/translations/sensor.hu.json | 2 +- .../components/xbox/translations/hu.json | 2 +- .../components/xbox/translations/it.json | 2 +- .../xiaomi_aqara/translations/ca.json | 6 +- .../xiaomi_aqara/translations/de.json | 6 +- .../xiaomi_aqara/translations/en.json | 6 +- .../xiaomi_aqara/translations/et.json | 6 +- .../xiaomi_aqara/translations/fr.json | 6 +- .../xiaomi_aqara/translations/he.json | 4 +- .../xiaomi_aqara/translations/hu.json | 8 +- .../xiaomi_aqara/translations/id.json | 6 +- .../xiaomi_aqara/translations/it.json | 6 +- .../xiaomi_aqara/translations/nl.json | 6 +- .../xiaomi_aqara/translations/no.json | 6 +- .../xiaomi_aqara/translations/pl.json | 6 +- .../xiaomi_aqara/translations/pt-BR.json | 8 +- .../xiaomi_aqara/translations/ru.json | 6 +- .../xiaomi_aqara/translations/tr.json | 6 +- .../xiaomi_aqara/translations/zh-Hant.json | 6 +- .../xiaomi_miio/translations/he.json | 2 +- .../xiaomi_miio/translations/hu.json | 2 +- .../xiaomi_miio/translations/pt-BR.json | 2 +- .../yale_smart_alarm/translations/hu.json | 4 +- .../components/yeelight/translations/cs.json | 2 +- .../components/yeelight/translations/fr.json | 2 +- .../components/youless/translations/hu.json | 2 +- .../components/zha/translations/cs.json | 2 +- .../components/zwave_js/translations/bg.json | 3 + .../components/zwave_js/translations/ca.json | 4 + .../components/zwave_js/translations/de.json | 4 + .../components/zwave_js/translations/el.json | 4 + .../components/zwave_js/translations/en.json | 5 +- .../components/zwave_js/translations/et.json | 4 + .../components/zwave_js/translations/fr.json | 4 + .../components/zwave_js/translations/hu.json | 6 +- .../components/zwave_js/translations/id.json | 6 +- .../components/zwave_js/translations/it.json | 4 + .../components/zwave_js/translations/ja.json | 4 + .../components/zwave_js/translations/nl.json | 4 + .../components/zwave_js/translations/no.json | 4 + .../components/zwave_js/translations/pl.json | 4 + .../zwave_js/translations/pt-BR.json | 4 + .../components/zwave_js/translations/ru.json | 4 + .../components/zwave_js/translations/tr.json | 4 + .../zwave_js/translations/zh-Hant.json | 4 + .../components/zwave_me/translations/ca.json | 4 +- .../components/zwave_me/translations/de.json | 6 +- .../components/zwave_me/translations/en.json | 4 +- .../components/zwave_me/translations/et.json | 4 +- .../components/zwave_me/translations/fr.json | 4 +- .../components/zwave_me/translations/he.json | 2 +- .../components/zwave_me/translations/hu.json | 4 +- .../components/zwave_me/translations/id.json | 4 +- .../components/zwave_me/translations/it.json | 4 +- .../components/zwave_me/translations/nl.json | 4 +- .../components/zwave_me/translations/no.json | 4 +- .../components/zwave_me/translations/pl.json | 4 +- .../zwave_me/translations/pt-BR.json | 4 +- .../components/zwave_me/translations/ru.json | 4 +- .../components/zwave_me/translations/tr.json | 4 +- .../zwave_me/translations/zh-Hant.json | 4 +- 1936 files changed, 23362 insertions(+), 3550 deletions(-) create mode 100644 homeassistant/components/airzone/translations/cs.json create mode 100644 homeassistant/components/airzone/translations/tr.json delete mode 100644 homeassistant/components/crownstone/translations/ko.json create mode 100644 homeassistant/components/deluge/translations/bg.json create mode 100644 homeassistant/components/deluge/translations/ca.json create mode 100644 homeassistant/components/deluge/translations/cs.json create mode 100644 homeassistant/components/deluge/translations/de.json create mode 100644 homeassistant/components/deluge/translations/el.json create mode 100644 homeassistant/components/deluge/translations/et.json create mode 100644 homeassistant/components/deluge/translations/fr.json create mode 100644 homeassistant/components/deluge/translations/he.json create mode 100644 homeassistant/components/deluge/translations/hu.json create mode 100644 homeassistant/components/deluge/translations/id.json create mode 100644 homeassistant/components/deluge/translations/it.json create mode 100644 homeassistant/components/deluge/translations/ja.json create mode 100644 homeassistant/components/deluge/translations/nl.json create mode 100644 homeassistant/components/deluge/translations/no.json create mode 100644 homeassistant/components/deluge/translations/pl.json create mode 100644 homeassistant/components/deluge/translations/pt-BR.json create mode 100644 homeassistant/components/deluge/translations/ru.json create mode 100644 homeassistant/components/deluge/translations/tr.json create mode 100644 homeassistant/components/deluge/translations/zh-Hant.json create mode 100644 homeassistant/components/derivative/translations/bg.json create mode 100644 homeassistant/components/derivative/translations/ca.json create mode 100644 homeassistant/components/derivative/translations/cs.json create mode 100644 homeassistant/components/derivative/translations/de.json create mode 100644 homeassistant/components/derivative/translations/el.json create mode 100644 homeassistant/components/derivative/translations/et.json create mode 100644 homeassistant/components/derivative/translations/fr.json create mode 100644 homeassistant/components/derivative/translations/he.json create mode 100644 homeassistant/components/derivative/translations/hu.json create mode 100644 homeassistant/components/derivative/translations/id.json create mode 100644 homeassistant/components/derivative/translations/it.json create mode 100644 homeassistant/components/derivative/translations/ja.json create mode 100644 homeassistant/components/derivative/translations/nl.json create mode 100644 homeassistant/components/derivative/translations/no.json create mode 100644 homeassistant/components/derivative/translations/pl.json create mode 100644 homeassistant/components/derivative/translations/pt-BR.json create mode 100644 homeassistant/components/derivative/translations/ru.json create mode 100644 homeassistant/components/derivative/translations/sv.json create mode 100644 homeassistant/components/derivative/translations/tr.json create mode 100644 homeassistant/components/derivative/translations/zh-Hans.json create mode 100644 homeassistant/components/derivative/translations/zh-Hant.json create mode 100644 homeassistant/components/discord/translations/bg.json create mode 100644 homeassistant/components/discord/translations/ca.json create mode 100644 homeassistant/components/discord/translations/de.json create mode 100644 homeassistant/components/discord/translations/el.json create mode 100644 homeassistant/components/discord/translations/et.json create mode 100644 homeassistant/components/discord/translations/fr.json create mode 100644 homeassistant/components/discord/translations/he.json create mode 100644 homeassistant/components/discord/translations/hu.json create mode 100644 homeassistant/components/discord/translations/id.json create mode 100644 homeassistant/components/discord/translations/it.json create mode 100644 homeassistant/components/discord/translations/ja.json create mode 100644 homeassistant/components/discord/translations/nl.json create mode 100644 homeassistant/components/discord/translations/no.json create mode 100644 homeassistant/components/discord/translations/pl.json create mode 100644 homeassistant/components/discord/translations/pt-BR.json create mode 100644 homeassistant/components/discord/translations/ru.json create mode 100644 homeassistant/components/discord/translations/tr.json create mode 100644 homeassistant/components/discord/translations/zh-Hant.json create mode 100644 homeassistant/components/dlna_dms/translations/cs.json create mode 100644 homeassistant/components/fibaro/translations/bg.json create mode 100644 homeassistant/components/fibaro/translations/ca.json create mode 100644 homeassistant/components/fibaro/translations/de.json create mode 100644 homeassistant/components/fibaro/translations/el.json create mode 100644 homeassistant/components/fibaro/translations/et.json create mode 100644 homeassistant/components/fibaro/translations/fr.json create mode 100644 homeassistant/components/fibaro/translations/he.json create mode 100644 homeassistant/components/fibaro/translations/hu.json create mode 100644 homeassistant/components/fibaro/translations/id.json create mode 100644 homeassistant/components/fibaro/translations/it.json create mode 100644 homeassistant/components/fibaro/translations/ja.json create mode 100644 homeassistant/components/fibaro/translations/nl.json create mode 100644 homeassistant/components/fibaro/translations/no.json create mode 100644 homeassistant/components/fibaro/translations/pl.json create mode 100644 homeassistant/components/fibaro/translations/pt-BR.json create mode 100644 homeassistant/components/fibaro/translations/ru.json create mode 100644 homeassistant/components/fibaro/translations/tr.json create mode 100644 homeassistant/components/fibaro/translations/zh-Hant.json create mode 100644 homeassistant/components/filesize/translations/bg.json create mode 100644 homeassistant/components/filesize/translations/ca.json create mode 100644 homeassistant/components/filesize/translations/de.json create mode 100644 homeassistant/components/filesize/translations/el.json create mode 100644 homeassistant/components/filesize/translations/et.json create mode 100644 homeassistant/components/filesize/translations/fr.json create mode 100644 homeassistant/components/filesize/translations/he.json create mode 100644 homeassistant/components/filesize/translations/hu.json create mode 100644 homeassistant/components/filesize/translations/id.json create mode 100644 homeassistant/components/filesize/translations/it.json create mode 100644 homeassistant/components/filesize/translations/ja.json create mode 100644 homeassistant/components/filesize/translations/nl.json create mode 100644 homeassistant/components/filesize/translations/no.json create mode 100644 homeassistant/components/filesize/translations/pl.json create mode 100644 homeassistant/components/filesize/translations/pt-BR.json create mode 100644 homeassistant/components/filesize/translations/ru.json create mode 100644 homeassistant/components/filesize/translations/tr.json create mode 100644 homeassistant/components/filesize/translations/zh-Hant.json create mode 100644 homeassistant/components/generic/translations/bg.json create mode 100644 homeassistant/components/generic/translations/ca.json create mode 100644 homeassistant/components/generic/translations/cs.json create mode 100644 homeassistant/components/generic/translations/de.json create mode 100644 homeassistant/components/generic/translations/el.json create mode 100644 homeassistant/components/generic/translations/et.json create mode 100644 homeassistant/components/generic/translations/fr.json create mode 100644 homeassistant/components/generic/translations/he.json create mode 100644 homeassistant/components/generic/translations/hu.json create mode 100644 homeassistant/components/generic/translations/id.json create mode 100644 homeassistant/components/generic/translations/it.json create mode 100644 homeassistant/components/generic/translations/ja.json create mode 100644 homeassistant/components/generic/translations/nl.json create mode 100644 homeassistant/components/generic/translations/no.json create mode 100644 homeassistant/components/generic/translations/pl.json create mode 100644 homeassistant/components/generic/translations/pt-BR.json create mode 100644 homeassistant/components/generic/translations/ru.json create mode 100644 homeassistant/components/generic/translations/tr.json create mode 100644 homeassistant/components/generic/translations/zh-Hant.json create mode 100644 homeassistant/components/google/translations/bg.json create mode 100644 homeassistant/components/google/translations/ca.json create mode 100644 homeassistant/components/google/translations/cs.json create mode 100644 homeassistant/components/google/translations/de.json create mode 100644 homeassistant/components/google/translations/el.json create mode 100644 homeassistant/components/google/translations/es.json create mode 100644 homeassistant/components/google/translations/et.json create mode 100644 homeassistant/components/google/translations/fr.json create mode 100644 homeassistant/components/google/translations/he.json create mode 100644 homeassistant/components/google/translations/hu.json create mode 100644 homeassistant/components/google/translations/id.json create mode 100644 homeassistant/components/google/translations/it.json create mode 100644 homeassistant/components/google/translations/ja.json create mode 100644 homeassistant/components/google/translations/nl.json create mode 100644 homeassistant/components/google/translations/no.json create mode 100644 homeassistant/components/google/translations/pl.json create mode 100644 homeassistant/components/google/translations/pt-BR.json create mode 100644 homeassistant/components/google/translations/ru.json create mode 100644 homeassistant/components/google/translations/tr.json create mode 100644 homeassistant/components/google/translations/zh-Hant.json rename homeassistant/components/{vallox/translations/sk.json => integration/translations/bg.json} (73%) create mode 100644 homeassistant/components/integration/translations/ca.json create mode 100644 homeassistant/components/integration/translations/cs.json create mode 100644 homeassistant/components/integration/translations/de.json create mode 100644 homeassistant/components/integration/translations/el.json create mode 100644 homeassistant/components/integration/translations/et.json create mode 100644 homeassistant/components/integration/translations/fr.json create mode 100644 homeassistant/components/integration/translations/he.json create mode 100644 homeassistant/components/integration/translations/hu.json create mode 100644 homeassistant/components/integration/translations/id.json create mode 100644 homeassistant/components/integration/translations/it.json create mode 100644 homeassistant/components/integration/translations/ja.json create mode 100644 homeassistant/components/integration/translations/nl.json create mode 100644 homeassistant/components/integration/translations/no.json create mode 100644 homeassistant/components/integration/translations/pl.json create mode 100644 homeassistant/components/integration/translations/pt-BR.json create mode 100644 homeassistant/components/integration/translations/ru.json create mode 100644 homeassistant/components/integration/translations/tr.json create mode 100644 homeassistant/components/integration/translations/zh-Hans.json create mode 100644 homeassistant/components/integration/translations/zh-Hant.json create mode 100644 homeassistant/components/kaleidescape/translations/cs.json create mode 100644 homeassistant/components/meater/translations/bg.json create mode 100644 homeassistant/components/meater/translations/ca.json create mode 100644 homeassistant/components/meater/translations/cs.json create mode 100644 homeassistant/components/meater/translations/de.json create mode 100644 homeassistant/components/meater/translations/el.json create mode 100644 homeassistant/components/meater/translations/et.json create mode 100644 homeassistant/components/meater/translations/fr.json create mode 100644 homeassistant/components/meater/translations/he.json create mode 100644 homeassistant/components/meater/translations/hu.json create mode 100644 homeassistant/components/meater/translations/id.json create mode 100644 homeassistant/components/meater/translations/it.json create mode 100644 homeassistant/components/meater/translations/ja.json create mode 100644 homeassistant/components/meater/translations/nl.json create mode 100644 homeassistant/components/meater/translations/no.json create mode 100644 homeassistant/components/meater/translations/pl.json create mode 100644 homeassistant/components/meater/translations/pt-BR.json create mode 100644 homeassistant/components/meater/translations/ru.json create mode 100644 homeassistant/components/meater/translations/tr.json create mode 100644 homeassistant/components/meater/translations/zh-Hant.json delete mode 100644 homeassistant/components/meteoclimatic/translations/gl.json delete mode 100644 homeassistant/components/meteoclimatic/translations/pt.json create mode 100644 homeassistant/components/min_max/translations/bg.json create mode 100644 homeassistant/components/min_max/translations/ca.json create mode 100644 homeassistant/components/min_max/translations/cs.json create mode 100644 homeassistant/components/min_max/translations/de.json create mode 100644 homeassistant/components/min_max/translations/el.json create mode 100644 homeassistant/components/min_max/translations/et.json create mode 100644 homeassistant/components/min_max/translations/fr.json create mode 100644 homeassistant/components/min_max/translations/he.json create mode 100644 homeassistant/components/min_max/translations/hu.json create mode 100644 homeassistant/components/min_max/translations/id.json create mode 100644 homeassistant/components/min_max/translations/it.json create mode 100644 homeassistant/components/min_max/translations/ja.json create mode 100644 homeassistant/components/min_max/translations/nl.json create mode 100644 homeassistant/components/min_max/translations/no.json create mode 100644 homeassistant/components/min_max/translations/pl.json create mode 100644 homeassistant/components/min_max/translations/pt-BR.json create mode 100644 homeassistant/components/min_max/translations/ru.json create mode 100644 homeassistant/components/min_max/translations/sv.json create mode 100644 homeassistant/components/min_max/translations/tr.json create mode 100644 homeassistant/components/min_max/translations/zh-Hans.json create mode 100644 homeassistant/components/min_max/translations/zh-Hant.json create mode 100644 homeassistant/components/moon/translations/cs.json create mode 100644 homeassistant/components/peco/translations/bg.json create mode 100644 homeassistant/components/peco/translations/ca.json create mode 100644 homeassistant/components/peco/translations/cs.json create mode 100644 homeassistant/components/peco/translations/de.json create mode 100644 homeassistant/components/peco/translations/el.json create mode 100644 homeassistant/components/peco/translations/et.json create mode 100644 homeassistant/components/peco/translations/fr.json create mode 100644 homeassistant/components/peco/translations/he.json create mode 100644 homeassistant/components/peco/translations/hu.json create mode 100644 homeassistant/components/peco/translations/id.json create mode 100644 homeassistant/components/peco/translations/it.json create mode 100644 homeassistant/components/peco/translations/ja.json create mode 100644 homeassistant/components/peco/translations/nl.json create mode 100644 homeassistant/components/peco/translations/no.json create mode 100644 homeassistant/components/peco/translations/pl.json create mode 100644 homeassistant/components/peco/translations/pt-BR.json create mode 100644 homeassistant/components/peco/translations/ru.json create mode 100644 homeassistant/components/peco/translations/tr.json create mode 100644 homeassistant/components/peco/translations/zh-Hant.json create mode 100644 homeassistant/components/pure_energie/translations/cs.json create mode 100644 homeassistant/components/qnap_qsw/translations/ca.json create mode 100644 homeassistant/components/qnap_qsw/translations/de.json create mode 100644 homeassistant/components/qnap_qsw/translations/el.json create mode 100644 homeassistant/components/qnap_qsw/translations/et.json create mode 100644 homeassistant/components/qnap_qsw/translations/fr.json create mode 100644 homeassistant/components/qnap_qsw/translations/he.json create mode 100644 homeassistant/components/qnap_qsw/translations/hu.json create mode 100644 homeassistant/components/qnap_qsw/translations/id.json create mode 100644 homeassistant/components/qnap_qsw/translations/it.json create mode 100644 homeassistant/components/qnap_qsw/translations/nl.json create mode 100644 homeassistant/components/qnap_qsw/translations/no.json create mode 100644 homeassistant/components/qnap_qsw/translations/pl.json create mode 100644 homeassistant/components/qnap_qsw/translations/pt-BR.json create mode 100644 homeassistant/components/qnap_qsw/translations/ru.json create mode 100644 homeassistant/components/qnap_qsw/translations/zh-Hant.json create mode 100644 homeassistant/components/radio_browser/translations/cs.json rename homeassistant/components/{vallox/translations/is.json => sabnzbd/translations/bg.json} (79%) create mode 100644 homeassistant/components/sabnzbd/translations/ca.json create mode 100644 homeassistant/components/sabnzbd/translations/de.json create mode 100644 homeassistant/components/sabnzbd/translations/el.json create mode 100644 homeassistant/components/sabnzbd/translations/et.json create mode 100644 homeassistant/components/sabnzbd/translations/fr.json create mode 100644 homeassistant/components/sabnzbd/translations/he.json create mode 100644 homeassistant/components/sabnzbd/translations/hu.json create mode 100644 homeassistant/components/sabnzbd/translations/id.json create mode 100644 homeassistant/components/sabnzbd/translations/it.json create mode 100644 homeassistant/components/sabnzbd/translations/nl.json create mode 100644 homeassistant/components/sabnzbd/translations/no.json create mode 100644 homeassistant/components/sabnzbd/translations/pl.json create mode 100644 homeassistant/components/sabnzbd/translations/pt-BR.json create mode 100644 homeassistant/components/sabnzbd/translations/ru.json create mode 100644 homeassistant/components/sabnzbd/translations/zh-Hant.json create mode 100644 homeassistant/components/season/translations/cs.json create mode 100644 homeassistant/components/senz/translations/bg.json create mode 100644 homeassistant/components/senz/translations/ca.json create mode 100644 homeassistant/components/senz/translations/de.json create mode 100644 homeassistant/components/senz/translations/el.json create mode 100644 homeassistant/components/senz/translations/et.json create mode 100644 homeassistant/components/senz/translations/fr.json create mode 100644 homeassistant/components/senz/translations/he.json create mode 100644 homeassistant/components/senz/translations/hu.json create mode 100644 homeassistant/components/senz/translations/id.json create mode 100644 homeassistant/components/senz/translations/it.json create mode 100644 homeassistant/components/senz/translations/ja.json create mode 100644 homeassistant/components/senz/translations/nl.json create mode 100644 homeassistant/components/senz/translations/no.json create mode 100644 homeassistant/components/senz/translations/pl.json create mode 100644 homeassistant/components/senz/translations/pt-BR.json create mode 100644 homeassistant/components/senz/translations/ru.json create mode 100644 homeassistant/components/senz/translations/tr.json create mode 100644 homeassistant/components/senz/translations/zh-Hant.json create mode 100644 homeassistant/components/slimproto/translations/ca.json create mode 100644 homeassistant/components/slimproto/translations/de.json create mode 100644 homeassistant/components/slimproto/translations/el.json create mode 100644 homeassistant/components/slimproto/translations/et.json create mode 100644 homeassistant/components/slimproto/translations/fr.json create mode 100644 homeassistant/components/slimproto/translations/he.json create mode 100644 homeassistant/components/slimproto/translations/hu.json create mode 100644 homeassistant/components/slimproto/translations/id.json create mode 100644 homeassistant/components/slimproto/translations/it.json create mode 100644 homeassistant/components/slimproto/translations/nl.json create mode 100644 homeassistant/components/slimproto/translations/no.json create mode 100644 homeassistant/components/slimproto/translations/pl.json create mode 100644 homeassistant/components/slimproto/translations/pt-BR.json create mode 100644 homeassistant/components/slimproto/translations/ru.json create mode 100644 homeassistant/components/slimproto/translations/zh-Hant.json create mode 100644 homeassistant/components/solax/translations/es.json create mode 100644 homeassistant/components/sql/translations/ca.json create mode 100644 homeassistant/components/sql/translations/de.json create mode 100644 homeassistant/components/sql/translations/el.json create mode 100644 homeassistant/components/sql/translations/et.json create mode 100644 homeassistant/components/sql/translations/fr.json create mode 100644 homeassistant/components/sql/translations/he.json create mode 100644 homeassistant/components/sql/translations/hu.json create mode 100644 homeassistant/components/sql/translations/id.json create mode 100644 homeassistant/components/sql/translations/it.json create mode 100644 homeassistant/components/sql/translations/nl.json create mode 100644 homeassistant/components/sql/translations/no.json create mode 100644 homeassistant/components/sql/translations/pl.json create mode 100644 homeassistant/components/sql/translations/pt-BR.json create mode 100644 homeassistant/components/sql/translations/ru.json create mode 100644 homeassistant/components/sql/translations/tr.json create mode 100644 homeassistant/components/sql/translations/zh-Hant.json create mode 100644 homeassistant/components/steam_online/translations/ca.json create mode 100644 homeassistant/components/steam_online/translations/de.json create mode 100644 homeassistant/components/steam_online/translations/el.json create mode 100644 homeassistant/components/steam_online/translations/et.json create mode 100644 homeassistant/components/steam_online/translations/fr.json create mode 100644 homeassistant/components/steam_online/translations/he.json create mode 100644 homeassistant/components/steam_online/translations/hu.json create mode 100644 homeassistant/components/steam_online/translations/id.json create mode 100644 homeassistant/components/steam_online/translations/it.json create mode 100644 homeassistant/components/steam_online/translations/nl.json create mode 100644 homeassistant/components/steam_online/translations/no.json create mode 100644 homeassistant/components/steam_online/translations/pl.json create mode 100644 homeassistant/components/steam_online/translations/pt-BR.json create mode 100644 homeassistant/components/steam_online/translations/ru.json create mode 100644 homeassistant/components/steam_online/translations/zh-Hant.json create mode 100644 homeassistant/components/switch_as_x/translations/bg.json create mode 100644 homeassistant/components/switch_as_x/translations/cs.json create mode 100644 homeassistant/components/switch_as_x/translations/tr.json create mode 100644 homeassistant/components/switch_as_x/translations/zh-Hans.json create mode 100644 homeassistant/components/tankerkoenig/translations/bg.json create mode 100644 homeassistant/components/tankerkoenig/translations/ca.json create mode 100644 homeassistant/components/tankerkoenig/translations/de.json create mode 100644 homeassistant/components/tankerkoenig/translations/el.json create mode 100644 homeassistant/components/tankerkoenig/translations/et.json create mode 100644 homeassistant/components/tankerkoenig/translations/fr.json create mode 100644 homeassistant/components/tankerkoenig/translations/he.json create mode 100644 homeassistant/components/tankerkoenig/translations/hu.json create mode 100644 homeassistant/components/tankerkoenig/translations/id.json create mode 100644 homeassistant/components/tankerkoenig/translations/it.json create mode 100644 homeassistant/components/tankerkoenig/translations/ja.json create mode 100644 homeassistant/components/tankerkoenig/translations/nl.json create mode 100644 homeassistant/components/tankerkoenig/translations/no.json create mode 100644 homeassistant/components/tankerkoenig/translations/pl.json create mode 100644 homeassistant/components/tankerkoenig/translations/pt-BR.json create mode 100644 homeassistant/components/tankerkoenig/translations/ru.json create mode 100644 homeassistant/components/tankerkoenig/translations/tr.json create mode 100644 homeassistant/components/tankerkoenig/translations/zh-Hant.json create mode 100644 homeassistant/components/tautulli/translations/ca.json create mode 100644 homeassistant/components/tautulli/translations/de.json create mode 100644 homeassistant/components/tautulli/translations/el.json create mode 100644 homeassistant/components/tautulli/translations/et.json create mode 100644 homeassistant/components/tautulli/translations/fr.json create mode 100644 homeassistant/components/tautulli/translations/he.json create mode 100644 homeassistant/components/tautulli/translations/hu.json create mode 100644 homeassistant/components/tautulli/translations/id.json create mode 100644 homeassistant/components/tautulli/translations/it.json create mode 100644 homeassistant/components/tautulli/translations/nl.json create mode 100644 homeassistant/components/tautulli/translations/no.json create mode 100644 homeassistant/components/tautulli/translations/pl.json create mode 100644 homeassistant/components/tautulli/translations/pt-BR.json create mode 100644 homeassistant/components/tautulli/translations/ru.json create mode 100644 homeassistant/components/tautulli/translations/zh-Hant.json create mode 100644 homeassistant/components/threshold/translations/bg.json create mode 100644 homeassistant/components/threshold/translations/ca.json create mode 100644 homeassistant/components/threshold/translations/de.json create mode 100644 homeassistant/components/threshold/translations/el.json create mode 100644 homeassistant/components/threshold/translations/et.json create mode 100644 homeassistant/components/threshold/translations/fr.json create mode 100644 homeassistant/components/threshold/translations/hu.json create mode 100644 homeassistant/components/threshold/translations/id.json create mode 100644 homeassistant/components/threshold/translations/it.json create mode 100644 homeassistant/components/threshold/translations/ja.json create mode 100644 homeassistant/components/threshold/translations/nl.json create mode 100644 homeassistant/components/threshold/translations/no.json create mode 100644 homeassistant/components/threshold/translations/pl.json create mode 100644 homeassistant/components/threshold/translations/pt-BR.json create mode 100644 homeassistant/components/threshold/translations/ru.json create mode 100644 homeassistant/components/threshold/translations/tr.json create mode 100644 homeassistant/components/threshold/translations/zh-Hans.json create mode 100644 homeassistant/components/threshold/translations/zh-Hant.json create mode 100644 homeassistant/components/tod/translations/bg.json create mode 100644 homeassistant/components/tod/translations/ca.json create mode 100644 homeassistant/components/tod/translations/de.json create mode 100644 homeassistant/components/tod/translations/el.json create mode 100644 homeassistant/components/tod/translations/et.json create mode 100644 homeassistant/components/tod/translations/fr.json create mode 100644 homeassistant/components/tod/translations/he.json create mode 100644 homeassistant/components/tod/translations/hu.json create mode 100644 homeassistant/components/tod/translations/id.json create mode 100644 homeassistant/components/tod/translations/it.json create mode 100644 homeassistant/components/tod/translations/ja.json create mode 100644 homeassistant/components/tod/translations/nl.json create mode 100644 homeassistant/components/tod/translations/no.json create mode 100644 homeassistant/components/tod/translations/pl.json create mode 100644 homeassistant/components/tod/translations/pt-BR.json create mode 100644 homeassistant/components/tod/translations/ru.json create mode 100644 homeassistant/components/tod/translations/tr.json create mode 100644 homeassistant/components/tod/translations/zh-Hans.json create mode 100644 homeassistant/components/tod/translations/zh-Hant.json create mode 100644 homeassistant/components/tomorrowio/translations/bg.json create mode 100644 homeassistant/components/tomorrowio/translations/ca.json create mode 100644 homeassistant/components/tomorrowio/translations/cs.json create mode 100644 homeassistant/components/tomorrowio/translations/de.json create mode 100644 homeassistant/components/tomorrowio/translations/el.json create mode 100644 homeassistant/components/tomorrowio/translations/et.json create mode 100644 homeassistant/components/tomorrowio/translations/fr.json create mode 100644 homeassistant/components/tomorrowio/translations/he.json create mode 100644 homeassistant/components/tomorrowio/translations/hu.json create mode 100644 homeassistant/components/tomorrowio/translations/id.json create mode 100644 homeassistant/components/tomorrowio/translations/it.json create mode 100644 homeassistant/components/tomorrowio/translations/ja.json create mode 100644 homeassistant/components/tomorrowio/translations/nl.json create mode 100644 homeassistant/components/tomorrowio/translations/no.json create mode 100644 homeassistant/components/tomorrowio/translations/pl.json create mode 100644 homeassistant/components/tomorrowio/translations/pt-BR.json create mode 100644 homeassistant/components/tomorrowio/translations/ru.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.bg.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.ca.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.de.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.el.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.et.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.fr.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.hu.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.id.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.it.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.ja.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.nl.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.no.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.pl.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.pt-BR.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.ru.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.tr.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.zh-Hant.json create mode 100644 homeassistant/components/tomorrowio/translations/tr.json create mode 100644 homeassistant/components/tomorrowio/translations/zh-Hant.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/ca.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/de.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/el.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/et.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/fr.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/he.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/hu.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/id.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/it.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/nl.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/no.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/pl.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/pt-BR.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/ru.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/zh-Hant.json create mode 100644 homeassistant/components/trafikverket_train/translations/bg.json create mode 100644 homeassistant/components/trafikverket_train/translations/ca.json create mode 100644 homeassistant/components/trafikverket_train/translations/de.json create mode 100644 homeassistant/components/trafikverket_train/translations/el.json create mode 100644 homeassistant/components/trafikverket_train/translations/et.json create mode 100644 homeassistant/components/trafikverket_train/translations/fr.json create mode 100644 homeassistant/components/trafikverket_train/translations/he.json create mode 100644 homeassistant/components/trafikverket_train/translations/hu.json create mode 100644 homeassistant/components/trafikverket_train/translations/id.json create mode 100644 homeassistant/components/trafikverket_train/translations/it.json create mode 100644 homeassistant/components/trafikverket_train/translations/ja.json create mode 100644 homeassistant/components/trafikverket_train/translations/nl.json create mode 100644 homeassistant/components/trafikverket_train/translations/no.json create mode 100644 homeassistant/components/trafikverket_train/translations/pl.json create mode 100644 homeassistant/components/trafikverket_train/translations/pt-BR.json create mode 100644 homeassistant/components/trafikverket_train/translations/ru.json create mode 100644 homeassistant/components/trafikverket_train/translations/tr.json create mode 100644 homeassistant/components/trafikverket_train/translations/zh-Hant.json delete mode 100644 homeassistant/components/twentemilieu/translations/nn.json create mode 100644 homeassistant/components/update/translations/cs.json create mode 100644 homeassistant/components/update/translations/es.json create mode 100644 homeassistant/components/update/translations/he.json create mode 100644 homeassistant/components/update/translations/zh-Hans.json create mode 100644 homeassistant/components/uptime/translations/bg.json create mode 100644 homeassistant/components/uptime/translations/cs.json create mode 100644 homeassistant/components/uptime/translations/id.json create mode 100644 homeassistant/components/uptime/translations/no.json create mode 100644 homeassistant/components/uptime/translations/pl.json create mode 100644 homeassistant/components/uptime/translations/tr.json create mode 100644 homeassistant/components/utility_meter/translations/bg.json create mode 100644 homeassistant/components/utility_meter/translations/ca.json create mode 100644 homeassistant/components/utility_meter/translations/cs.json create mode 100644 homeassistant/components/utility_meter/translations/de.json create mode 100644 homeassistant/components/utility_meter/translations/el.json create mode 100644 homeassistant/components/utility_meter/translations/et.json create mode 100644 homeassistant/components/utility_meter/translations/fr.json create mode 100644 homeassistant/components/utility_meter/translations/hu.json create mode 100644 homeassistant/components/utility_meter/translations/id.json create mode 100644 homeassistant/components/utility_meter/translations/it.json create mode 100644 homeassistant/components/utility_meter/translations/ja.json create mode 100644 homeassistant/components/utility_meter/translations/nl.json create mode 100644 homeassistant/components/utility_meter/translations/no.json create mode 100644 homeassistant/components/utility_meter/translations/pl.json create mode 100644 homeassistant/components/utility_meter/translations/pt-BR.json create mode 100644 homeassistant/components/utility_meter/translations/ru.json create mode 100644 homeassistant/components/utility_meter/translations/tr.json create mode 100644 homeassistant/components/utility_meter/translations/zh-Hans.json create mode 100644 homeassistant/components/utility_meter/translations/zh-Hant.json delete mode 100644 homeassistant/components/vallox/translations/lv.json create mode 100644 homeassistant/components/vulcan/translations/bg.json create mode 100644 homeassistant/components/vulcan/translations/ca.json create mode 100644 homeassistant/components/vulcan/translations/de.json create mode 100644 homeassistant/components/vulcan/translations/el.json create mode 100644 homeassistant/components/vulcan/translations/et.json create mode 100644 homeassistant/components/vulcan/translations/fr.json create mode 100644 homeassistant/components/vulcan/translations/hu.json create mode 100644 homeassistant/components/vulcan/translations/id.json create mode 100644 homeassistant/components/vulcan/translations/it.json create mode 100644 homeassistant/components/vulcan/translations/ja.json create mode 100644 homeassistant/components/vulcan/translations/nl.json create mode 100644 homeassistant/components/vulcan/translations/no.json create mode 100644 homeassistant/components/vulcan/translations/pl.json create mode 100644 homeassistant/components/vulcan/translations/pt-BR.json create mode 100644 homeassistant/components/vulcan/translations/ru.json create mode 100644 homeassistant/components/vulcan/translations/tr.json create mode 100644 homeassistant/components/vulcan/translations/zh-Hant.json create mode 100644 homeassistant/components/webostv/translations/hu.json diff --git a/homeassistant/components/accuweather/translations/ca.json b/homeassistant/components/accuweather/translations/ca.json index 8178a5caef0..feda485d8fd 100644 --- a/homeassistant/components/accuweather/translations/ca.json +++ b/homeassistant/components/accuweather/translations/ca.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, + "create_entry": { + "default": "Alguns sensors no estan activats de manera predeterminada. Els pots activar des del registre d'entitats, despr\u00e9s de la configuraci\u00f3 de la integraci\u00f3.\nLa previsi\u00f3 meteorol\u00f2gica no est\u00e0 activada de manera predeterminada. Pots activar-la a les opcions de la integraci\u00f3." + }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_api_key": "Clau API inv\u00e0lida", diff --git a/homeassistant/components/accuweather/translations/de.json b/homeassistant/components/accuweather/translations/de.json index 17eb0ee31fc..ac0c430de04 100644 --- a/homeassistant/components/accuweather/translations/de.json +++ b/homeassistant/components/accuweather/translations/de.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, + "create_entry": { + "default": "Einige Sensoren sind standardm\u00e4\u00dfig nicht aktiviert. Du kannst sie nach der Integrationskonfiguration in der Entit\u00e4tsregistrierung aktivieren.\nDie Wettervorhersage ist nicht standardm\u00e4\u00dfig aktiviert. Du kannst sie in den Integrationsoptionen aktivieren." + }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", diff --git a/homeassistant/components/accuweather/translations/el.json b/homeassistant/components/accuweather/translations/el.json index 4f7a23e1d6f..43a65158455 100644 --- a/homeassistant/components/accuweather/translations/el.json +++ b/homeassistant/components/accuweather/translations/el.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, + "create_entry": { + "default": "\u039f\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03b9 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03b9 \u03b1\u03c0\u03cc \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae. \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c3\u03c4\u03bf \u03bc\u03b7\u03c4\u03c1\u03ce\u03bf \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03bc\u03b5\u03c4\u03ac \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2.\n \u0397 \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b1\u03c0\u03cc \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae. \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03bf \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2." + }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", diff --git a/homeassistant/components/accuweather/translations/en.json b/homeassistant/components/accuweather/translations/en.json index 8f2261b93c7..a984080fd86 100644 --- a/homeassistant/components/accuweather/translations/en.json +++ b/homeassistant/components/accuweather/translations/en.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Already configured. Only a single configuration possible." }, + "create_entry": { + "default": "Some sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options." + }, "error": { "cannot_connect": "Failed to connect", "invalid_api_key": "Invalid API key", diff --git a/homeassistant/components/accuweather/translations/et.json b/homeassistant/components/accuweather/translations/et.json index 6e2dc1ffd96..429e2dec61e 100644 --- a/homeassistant/components/accuweather/translations/et.json +++ b/homeassistant/components/accuweather/translations/et.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Sidumine juba tehtud. V\u00f5imalik on ainult 1 sidumine." }, + "create_entry": { + "default": "M\u00f5ned andurid ei ole vaikimisi lubatud. Saad neid lubada \u00fcksuse registris p\u00e4rast sidumise seadistamist.\nIlmaprognoos ei ole vaikimisi lubatud. Saad selle lubada sidumise valikutes." + }, "error": { "cannot_connect": "\u00dchendamine nurjus", "invalid_api_key": "API v\u00f5ti on vale", diff --git a/homeassistant/components/accuweather/translations/fr.json b/homeassistant/components/accuweather/translations/fr.json index 1de45f8da22..cf407ef3962 100644 --- a/homeassistant/components/accuweather/translations/fr.json +++ b/homeassistant/components/accuweather/translations/fr.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, + "create_entry": { + "default": "Certains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s une fois la configuration de l'int\u00e9gration termin\u00e9e.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez les activer dans les options de l'int\u00e9gration." + }, "error": { "cannot_connect": "\u00c9chec de connexion", "invalid_api_key": "Cl\u00e9 d'API non valide", @@ -16,7 +19,7 @@ "longitude": "Longitude", "name": "Nom" }, - "description": "Si vous avez besoin d'aide pour la configuration, consultez le site suivant : https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s apr\u00e8s la configuration de l'int\u00e9gration.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez l'activer dans les options d'int\u00e9gration.", + "description": "Si vous avez besoin d'aide pour la configuration, consultez\u00a0: https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s une fois la configuration de l'int\u00e9gration termin\u00e9e.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez les activer dans les options de l'int\u00e9gration.", "title": "AccuWeather" } } diff --git a/homeassistant/components/accuweather/translations/hu.json b/homeassistant/components/accuweather/translations/hu.json index 8b0409d1f22..1b30dd09daf 100644 --- a/homeassistant/components/accuweather/translations/hu.json +++ b/homeassistant/components/accuweather/translations/hu.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, + "create_entry": { + "default": "Egyes \u00e9rz\u00e9kel\u0151k alap\u00e9rtelmez\u00e9s szerint nincsenek enged\u00e9lyezve. Az integr\u00e1ci\u00f3s konfigur\u00e1ci\u00f3 ut\u00e1n enged\u00e9lyezheti \u0151ket az entit\u00e1s rendszerle\u00edr\u00f3 adatb\u00e1zis\u00e1ban.\nAz id\u0151j\u00e1r\u00e1s-el\u0151rejelz\u00e9s alap\u00e9rtelmez\u00e9s szerint nincs enged\u00e9lyezve. Ezt az integr\u00e1ci\u00f3s be\u00e1ll\u00edt\u00e1sokban enged\u00e9lyezheti." + }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", @@ -14,7 +17,7 @@ "api_key": "API kulcs", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ge a konfigur\u00e1l\u00e1shoz, n\u00e9zze meg itt: https://www.home-assistant.io/integrations/accuweather/ \n\nEgyes \u00e9rz\u00e9kel\u0151k alap\u00e9rtelmez\u00e9s szerint nincsenek enged\u00e9lyezve. Az integr\u00e1ci\u00f3s konfigur\u00e1ci\u00f3 ut\u00e1n enged\u00e9lyezheti \u0151ket az entit\u00e1s-nyilv\u00e1ntart\u00e1sban.\nAz id\u0151j\u00e1r\u00e1s-el\u0151rejelz\u00e9s alap\u00e9rtelmez\u00e9s szerint nincs enged\u00e9lyezve. Ezt az integr\u00e1ci\u00f3s be\u00e1ll\u00edt\u00e1sokban enged\u00e9lyezheti.", "title": "AccuWeather" diff --git a/homeassistant/components/accuweather/translations/id.json b/homeassistant/components/accuweather/translations/id.json index 970b3a026b7..7bf9f27e8b2 100644 --- a/homeassistant/components/accuweather/translations/id.json +++ b/homeassistant/components/accuweather/translations/id.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, + "create_entry": { + "default": "Beberapa sensor tidak diaktifkan secara default. Anda dapat mengaktifkannya di registri entitas setelah konfigurasi integrasi.\nPrakiraan cuaca tidak diaktifkan secara default. Anda dapat mengaktifkannya di opsi integrasi." + }, "error": { "cannot_connect": "Gagal terhubung", "invalid_api_key": "Kunci API tidak valid", diff --git a/homeassistant/components/accuweather/translations/it.json b/homeassistant/components/accuweather/translations/it.json index 8a1f9b96463..9daaf378fb4 100644 --- a/homeassistant/components/accuweather/translations/it.json +++ b/homeassistant/components/accuweather/translations/it.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, + "create_entry": { + "default": "Alcuni sensori non sono abilitati per impostazione predefinita. Puoi abilitarli nel registro delle entit\u00e0 dopo la configurazione dell'integrazione.\nLe previsioni del tempo non sono abilitate per impostazione predefinita. Puoi abilitarlo nelle opzioni di integrazione." + }, "error": { "cannot_connect": "Impossibile connettersi", "invalid_api_key": "Chiave API non valida", diff --git a/homeassistant/components/accuweather/translations/ja.json b/homeassistant/components/accuweather/translations/ja.json index 10fe398ba04..d05e75e74b5 100644 --- a/homeassistant/components/accuweather/translations/ja.json +++ b/homeassistant/components/accuweather/translations/ja.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" }, + "create_entry": { + "default": "\u4e00\u90e8\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u69cb\u6210\u5f8c\u3001\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30ec\u30b8\u30b9\u30c8\u30ea\u3067\u305d\u308c\u3089\u3092\u6709\u52b9\u306b\u3067\u304d\u307e\u3059\u3002\n\u5929\u6c17\u4e88\u5831\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u6709\u52b9\u306b\u3067\u304d\u307e\u3059\u3002" + }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", diff --git a/homeassistant/components/accuweather/translations/nl.json b/homeassistant/components/accuweather/translations/nl.json index f04d93b5921..2a6dd1a812b 100644 --- a/homeassistant/components/accuweather/translations/nl.json +++ b/homeassistant/components/accuweather/translations/nl.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." }, + "create_entry": { + "default": "Sommige sensoren zijn standaard niet ingeschakeld. U kunt ze inschakelen in het entiteitenregister na de integratieconfiguratie.\nWeersvoorspelling is niet standaard ingeschakeld. U kunt deze inschakelen in de integratieopties." + }, "error": { "cannot_connect": "Kan geen verbinding maken", "invalid_api_key": "API-sleutel", diff --git a/homeassistant/components/accuweather/translations/no.json b/homeassistant/components/accuweather/translations/no.json index be87b1ab244..af689b2fd1c 100644 --- a/homeassistant/components/accuweather/translations/no.json +++ b/homeassistant/components/accuweather/translations/no.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, + "create_entry": { + "default": "Noen sensorer er ikke aktivert som standard. Du kan aktivere dem i enhetsregisteret etter integrasjonskonfigurasjonen.\n V\u00e6rmelding er ikke aktivert som standard. Du kan aktivere det i integreringsalternativene." + }, "error": { "cannot_connect": "Tilkobling mislyktes", "invalid_api_key": "Ugyldig API-n\u00f8kkel", diff --git a/homeassistant/components/accuweather/translations/pl.json b/homeassistant/components/accuweather/translations/pl.json index 2794bc8b7b6..a8ef8a3bfa0 100644 --- a/homeassistant/components/accuweather/translations/pl.json +++ b/homeassistant/components/accuweather/translations/pl.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, + "create_entry": { + "default": "Niekt\u00f3re sensory nie s\u0105 domy\u015blnie w\u0142\u0105czone. Mo\u017cesz je w\u0142\u0105czy\u0107 w rejestrze encji po skonfigurowaniu integracji.\nPrognoza pogody nie jest domy\u015blnie w\u0142\u0105czona. Mo\u017cesz to w\u0142\u0105czy\u0107 w opcjach integracji." + }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_api_key": "Nieprawid\u0142owy klucz API", diff --git a/homeassistant/components/accuweather/translations/pt-BR.json b/homeassistant/components/accuweather/translations/pt-BR.json index 469dd81ff91..4bb1bbbc97e 100644 --- a/homeassistant/components/accuweather/translations/pt-BR.json +++ b/homeassistant/components/accuweather/translations/pt-BR.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, + "create_entry": { + "default": "Alguns sensores n\u00e3o s\u00e3o ativados por padr\u00e3o. Voc\u00ea pode habilit\u00e1-los no registro da entidade ap\u00f3s a configura\u00e7\u00e3o da integra\u00e7\u00e3o.\n A previs\u00e3o do tempo n\u00e3o est\u00e1 habilitada por padr\u00e3o. Voc\u00ea pode habilit\u00e1-lo nas op\u00e7\u00f5es de integra\u00e7\u00e3o." + }, "error": { "cannot_connect": "Falha ao conectar", "invalid_api_key": "Chave de API inv\u00e1lida", diff --git a/homeassistant/components/accuweather/translations/ru.json b/homeassistant/components/accuweather/translations/ru.json index 7bc767b1baf..dfd24b6f147 100644 --- a/homeassistant/components/accuweather/translations/ru.json +++ b/homeassistant/components/accuweather/translations/ru.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, + "create_entry": { + "default": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0441\u043a\u0440\u044b\u0442\u044b \u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438." + }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", diff --git a/homeassistant/components/accuweather/translations/tr.json b/homeassistant/components/accuweather/translations/tr.json index 7b0fa476458..b5efeb7080e 100644 --- a/homeassistant/components/accuweather/translations/tr.json +++ b/homeassistant/components/accuweather/translations/tr.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, + "create_entry": { + "default": "Baz\u0131 sens\u00f6rler varsay\u0131lan olarak etkin de\u011fildir. Bunlar\u0131, entegrasyon yap\u0131land\u0131rmas\u0131ndan sonra varl\u0131k kay\u0131t defterinde etkinle\u015ftirebilirsiniz.\n Hava tahmini varsay\u0131lan olarak etkin de\u011fildir. Entegrasyon se\u00e7eneklerinde etkinle\u015ftirebilirsiniz." + }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", diff --git a/homeassistant/components/accuweather/translations/zh-Hant.json b/homeassistant/components/accuweather/translations/zh-Hant.json index fbf72991e92..6d9e7e1c36f 100644 --- a/homeassistant/components/accuweather/translations/zh-Hant.json +++ b/homeassistant/components/accuweather/translations/zh-Hant.json @@ -3,6 +3,9 @@ "abort": { "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, + "create_entry": { + "default": "\u90e8\u5206\u611f\u6e2c\u5668\u9810\u8a2d\u70ba\u4e0d\u555f\u7528\u72c0\u614b\u3002\u53ef\u4ee5\u7a0d\u5f8c\u65bc\u6574\u5408\u8a2d\u5b9a\u9801\u9762\u4e2d\u7684\u5be6\u9ad4\u8a3b\u518a\u8868\u9032\u884c\u555f\u7528\u3002\n\u5929\u6c23\u9810\u5831\u9810\u8a2d\u70ba\u4e0d\u555f\u7528\u3001\u53ef\u4ee5\u65bc\u6574\u5408\u9078\u9805\u4e2d\u9032\u884c\u555f\u7528\u3002" + }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_api_key": "API \u91d1\u9470\u7121\u6548", diff --git a/homeassistant/components/adax/translations/hu.json b/homeassistant/components/adax/translations/hu.json index 9f37837420f..a2a6dcdacaf 100644 --- a/homeassistant/components/adax/translations/hu.json +++ b/homeassistant/components/adax/translations/hu.json @@ -20,9 +20,9 @@ "local": { "data": { "wifi_pswd": "WiFi jelsz\u00f3", - "wifi_ssid": "WiFi ssid" + "wifi_ssid": "Wi-Fi SSID" }, - "description": "\u00c1ll\u00edtsa vissza a f\u0171t\u0151berendez\u00e9st a + \u00e9s az OK gomb nyomvatart\u00e1s\u00e1val, m\u00edg a kijelz\u0151n a \"Reset\" (Vissza\u00e1ll\u00edt\u00e1s) felirat nem jelenik meg. Ezut\u00e1n nyomja meg \u00e9s tartsa lenyomva a f\u0171t\u0151berendez\u00e9s OK gombj\u00e1t, am\u00edg a k\u00e9k led villogni nem kezd, majd nyomja meg a K\u00fcld\u00e9s gombot. A f\u0171t\u0151berendez\u00e9s konfigur\u00e1l\u00e1sa n\u00e9h\u00e1ny percet vehet ig\u00e9nybe." + "description": "\u00c1ll\u00edtsa vissza a f\u0171t\u0151berendez\u00e9st a + \u00e9s az OK gomb nyomvatart\u00e1s\u00e1val, m\u00edg a kijelz\u0151n a \"Reset\" (Vissza\u00e1ll\u00edt\u00e1s) felirat nem jelenik meg. Ezut\u00e1n nyomja meg \u00e9s tartsa lenyomva a f\u0171t\u0151berendez\u00e9s OK gombj\u00e1t, am\u00edg a k\u00e9k led villogni nem kezd, l\u00e9pjen tov\u00e1bb. A f\u0171t\u0151berendez\u00e9s konfigur\u00e1l\u00e1sa n\u00e9h\u00e1ny percet vehet ig\u00e9nybe." }, "user": { "data": { diff --git a/homeassistant/components/adax/translations/nl.json b/homeassistant/components/adax/translations/nl.json index 026c3c06dae..ced3b8bdab0 100644 --- a/homeassistant/components/adax/translations/nl.json +++ b/homeassistant/components/adax/translations/nl.json @@ -31,7 +31,7 @@ "host": "Host", "password": "Wachtwoord" }, - "description": "Selecteer verbindingstype. Lokaal vereist verwarming met bluetooth" + "description": "Selecteer verbindingstype. Lokaal vereist verwarming met Bluetooth." } } } diff --git a/homeassistant/components/aemet/translations/ca.json b/homeassistant/components/aemet/translations/ca.json index 75784ddfc87..7dab53fc049 100644 --- a/homeassistant/components/aemet/translations/ca.json +++ b/homeassistant/components/aemet/translations/ca.json @@ -14,7 +14,7 @@ "longitude": "Longitud", "name": "Nom de la integraci\u00f3" }, - "description": "Configura la integraci\u00f3 d'AEMET OpenData. Per generar la clau API, v\u00e9s a https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Per generar la clau API, v\u00e9s a https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/de.json b/homeassistant/components/aemet/translations/de.json index 0704e7d71ba..25f374f2e43 100644 --- a/homeassistant/components/aemet/translations/de.json +++ b/homeassistant/components/aemet/translations/de.json @@ -14,7 +14,7 @@ "longitude": "L\u00e4ngengrad", "name": "Name der Integration" }, - "description": "Richte die AEMET OpenData Integration ein. Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/en.json b/homeassistant/components/aemet/translations/en.json index 3888ccdafc0..7c10d1dc676 100644 --- a/homeassistant/components/aemet/translations/en.json +++ b/homeassistant/components/aemet/translations/en.json @@ -14,7 +14,7 @@ "longitude": "Longitude", "name": "Name of the integration" }, - "description": "Set up AEMET OpenData integration. To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/et.json b/homeassistant/components/aemet/translations/et.json index 0d542fcc744..04292b3d912 100644 --- a/homeassistant/components/aemet/translations/et.json +++ b/homeassistant/components/aemet/translations/et.json @@ -14,7 +14,7 @@ "longitude": "Pikkuskraad", "name": "Sidumise nimi" }, - "description": "Seadista AEMET OpenData sidumine. API v\u00f5tme loomiseks mine aadressile https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "API-v\u00f5tme loomiseks mine aadressile https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/fr.json b/homeassistant/components/aemet/translations/fr.json index 0d3d0e77a5e..bddfd2507bf 100644 --- a/homeassistant/components/aemet/translations/fr.json +++ b/homeassistant/components/aemet/translations/fr.json @@ -14,7 +14,7 @@ "longitude": "Longitude", "name": "Nom de l'int\u00e9gration" }, - "description": "Configurez l'int\u00e9gration AEMET OpenData. Pour g\u00e9n\u00e9rer la cl\u00e9 API, acc\u00e9dez \u00e0 https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/hu.json b/homeassistant/components/aemet/translations/hu.json index 31a7654efd9..2f2984b1c90 100644 --- a/homeassistant/components/aemet/translations/hu.json +++ b/homeassistant/components/aemet/translations/hu.json @@ -14,7 +14,7 @@ "longitude": "Hossz\u00fas\u00e1g", "name": "Az integr\u00e1ci\u00f3 neve" }, - "description": "\u00c1ll\u00edtsa be az AEMET OpenData integr\u00e1ci\u00f3t. Az API-kulcs el\u0151\u00e1ll\u00edt\u00e1s\u00e1hoz keresse fel a https://opendata.aemet.es/centrodedescargas/altaUsuario webhelyet.", + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://opendata.aemet.es/centrodedescargas/altaUsuario webhelyet", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/id.json b/homeassistant/components/aemet/translations/id.json index e3a602a9a7c..62239172aae 100644 --- a/homeassistant/components/aemet/translations/id.json +++ b/homeassistant/components/aemet/translations/id.json @@ -14,7 +14,7 @@ "longitude": "Bujur", "name": "Nama integrasi" }, - "description": "Siapkan integrasi AEMET OpenData. Untuk menghasilkan kunci API, buka https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Untuk membuat kunci API, buka https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/it.json b/homeassistant/components/aemet/translations/it.json index a55e003ca4e..4b3f6f2251b 100644 --- a/homeassistant/components/aemet/translations/it.json +++ b/homeassistant/components/aemet/translations/it.json @@ -14,7 +14,7 @@ "longitude": "Logitudine", "name": "Nome dell'integrazione" }, - "description": "Imposta l'integrazione di AEMET OpenData. Per generare la chiave API, vai su https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Per generare la chiave API, vai su https://opendata.aemet.es/centrodescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/nl.json b/homeassistant/components/aemet/translations/nl.json index 40fab5d9e0f..abf590e5a36 100644 --- a/homeassistant/components/aemet/translations/nl.json +++ b/homeassistant/components/aemet/translations/nl.json @@ -14,7 +14,7 @@ "longitude": "Lengtegraad", "name": "Naam van de integratie" }, - "description": "Stel AEMET OpenData-integratie in. Ga naar https://opendata.aemet.es/centrodedescargas/altaUsuario om een API-sleutel te genereren", + "description": "Om een API sleutel te genereren ga naar https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/no.json b/homeassistant/components/aemet/translations/no.json index fe36ff835ee..a6076844122 100644 --- a/homeassistant/components/aemet/translations/no.json +++ b/homeassistant/components/aemet/translations/no.json @@ -14,7 +14,7 @@ "longitude": "Lengdegrad", "name": "Navnet p\u00e5 integrasjonen" }, - "description": "Sett opp AEMET OpenData-integrasjon. For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/pl.json b/homeassistant/components/aemet/translations/pl.json index 8531ca47fd6..f1021585140 100644 --- a/homeassistant/components/aemet/translations/pl.json +++ b/homeassistant/components/aemet/translations/pl.json @@ -14,7 +14,7 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa integracji" }, - "description": "Skonfiguruj integracj\u0119 AEMET OpenData. Aby wygenerowa\u0107 klucz API, przejd\u017a do https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/pt-BR.json b/homeassistant/components/aemet/translations/pt-BR.json index 6a0c8800b02..7c5972dccd8 100644 --- a/homeassistant/components/aemet/translations/pt-BR.json +++ b/homeassistant/components/aemet/translations/pt-BR.json @@ -14,7 +14,7 @@ "longitude": "Longitude", "name": "Nome da integra\u00e7\u00e3o" }, - "description": "Configure a integra\u00e7\u00e3o AEMET OpenData. Para gerar a chave API acesse https://opendata.aemet.es/centrodedescargas/altaUsuario", + "description": "Para gerar a chave API acesse https://opendata.aemet.es/centrodedescargas/altaUsuario", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/ru.json b/homeassistant/components/aemet/translations/ru.json index f9278af712b..9d8496cbb2d 100644 --- a/homeassistant/components/aemet/translations/ru.json +++ b/homeassistant/components/aemet/translations/ru.json @@ -14,7 +14,7 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 AEMET OpenData. \u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://opendata.aemet.es/centrodedescargas/altaUsuario.", + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://opendata.aemet.es/centrodedescargas/altaUsuario.", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/tr.json b/homeassistant/components/aemet/translations/tr.json index 7cb0048b0e0..e1e3cae01ff 100644 --- a/homeassistant/components/aemet/translations/tr.json +++ b/homeassistant/components/aemet/translations/tr.json @@ -14,7 +14,7 @@ "longitude": "Boylam", "name": "Entegrasyonun ad\u0131" }, - "description": "AEMET OpenData entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://opendata.aemet.es/centrodedescargas/altaUsuario adresine gidin.", + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://opendata.aemet.es/centrodedescargas/altaUsuario adresine gidin.", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/aemet/translations/zh-Hant.json b/homeassistant/components/aemet/translations/zh-Hant.json index e064a6c0192..4d77ca17505 100644 --- a/homeassistant/components/aemet/translations/zh-Hant.json +++ b/homeassistant/components/aemet/translations/zh-Hant.json @@ -14,7 +14,7 @@ "longitude": "\u7d93\u5ea6", "name": "\u6574\u5408\u540d\u7a31" }, - "description": "\u6b32\u8a2d\u5b9a AEMET OpenData \u6574\u5408\u3002\u8acb\u81f3 https://opendata.aemet.es/centrodedescargas/altaUsuario \u7522\u751f API \u91d1\u9470", + "description": "\u8acb\u81f3 https://opendata.aemet.es/centrodedescargas/altaUsuario \u4ee5\u7522\u751f API \u91d1\u9470", "title": "AEMET OpenData" } } diff --git a/homeassistant/components/agent_dvr/translations/hu.json b/homeassistant/components/agent_dvr/translations/hu.json index 83751d72eaf..fdddd0ba92e 100644 --- a/homeassistant/components/agent_dvr/translations/hu.json +++ b/homeassistant/components/agent_dvr/translations/hu.json @@ -4,7 +4,7 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { diff --git a/homeassistant/components/airly/translations/ca.json b/homeassistant/components/airly/translations/ca.json index 0d5177df33e..ac956185c14 100644 --- a/homeassistant/components/airly/translations/ca.json +++ b/homeassistant/components/airly/translations/ca.json @@ -15,7 +15,7 @@ "longitude": "Longitud", "name": "Nom" }, - "description": "Configura la integraci\u00f3 de qualitat de l'aire Airly. Per generar la clau API, v\u00e9s a https://developer.airly.eu/register", + "description": "Per generar la clau API, v\u00e9s a https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/de.json b/homeassistant/components/airly/translations/de.json index b13798c0ae3..216a8c56c6d 100644 --- a/homeassistant/components/airly/translations/de.json +++ b/homeassistant/components/airly/translations/de.json @@ -15,7 +15,7 @@ "longitude": "L\u00e4ngengrad", "name": "Name" }, - "description": "Einrichtung der Airly-Luftqualit\u00e4t Integration. Um einen API-Schl\u00fcssel zu generieren, registriere dich auf https://developer.airly.eu/register", + "description": "Um einen API-Schl\u00fcssel zu generieren, besuche https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/en.json b/homeassistant/components/airly/translations/en.json index 0a5426c87d8..249905eff2a 100644 --- a/homeassistant/components/airly/translations/en.json +++ b/homeassistant/components/airly/translations/en.json @@ -15,7 +15,7 @@ "longitude": "Longitude", "name": "Name" }, - "description": "Set up Airly air quality integration. To generate API key go to https://developer.airly.eu/register", + "description": "To generate API key go to https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/et.json b/homeassistant/components/airly/translations/et.json index 6730e131ac2..6a55262ac6c 100644 --- a/homeassistant/components/airly/translations/et.json +++ b/homeassistant/components/airly/translations/et.json @@ -15,7 +15,7 @@ "longitude": "Pikkuskraad", "name": "Nimi" }, - "description": "Seadista Airly \u00f5hukvaliteedi andmete sidumine. API v\u00f5tme loomiseks mine aadressile https://developer.airly.eu/register", + "description": "API-v\u00f5tme loomiseks mine aadressile https://developer.airly.eu/register", "title": "" } } diff --git a/homeassistant/components/airly/translations/fr.json b/homeassistant/components/airly/translations/fr.json index 85d1a3b478e..17a7248f7ad 100644 --- a/homeassistant/components/airly/translations/fr.json +++ b/homeassistant/components/airly/translations/fr.json @@ -15,7 +15,7 @@ "longitude": "Longitude", "name": "Nom" }, - "description": "Configurez l'int\u00e9gration de la qualit\u00e9 de l'air Airly. Pour g\u00e9n\u00e9rer une cl\u00e9 API, rendez-vous sur https://developer.airly.eu/register.", + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/hu.json b/homeassistant/components/airly/translations/hu.json index f730edde85f..04d667f4079 100644 --- a/homeassistant/components/airly/translations/hu.json +++ b/homeassistant/components/airly/translations/hu.json @@ -13,9 +13,9 @@ "api_key": "API kulcs", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, - "description": "Az Airly leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Api-kulcs l\u00e9trehoz\u00e1s\u00e1hoz nyissa meg a k\u00f6vetkez\u0151 weboldalt: https://developer.airly.eu/register", + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://developer.airly.eu/register webhelyet", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/id.json b/homeassistant/components/airly/translations/id.json index 57b4c0d95f9..bc97db4135a 100644 --- a/homeassistant/components/airly/translations/id.json +++ b/homeassistant/components/airly/translations/id.json @@ -15,7 +15,7 @@ "longitude": "Bujur", "name": "Nama" }, - "description": "Siapkan integrasi kualitas udara Airly. Untuk membuat kunci API, buka https://developer.airly.eu/register", + "description": "Untuk membuat kunci API, buka https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/it.json b/homeassistant/components/airly/translations/it.json index 170455ffb15..b96d5c8a74c 100644 --- a/homeassistant/components/airly/translations/it.json +++ b/homeassistant/components/airly/translations/it.json @@ -15,7 +15,7 @@ "longitude": "Logitudine", "name": "Nome" }, - "description": "Configurazione dell'integrazione della qualit\u00e0 dell'aria Airly. Per generare la chiave API vai su https://developer.airly.eu/register", + "description": "Per generare la chiave API, vai su https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/nl.json b/homeassistant/components/airly/translations/nl.json index 14cbaf1711e..70fbca2074f 100644 --- a/homeassistant/components/airly/translations/nl.json +++ b/homeassistant/components/airly/translations/nl.json @@ -15,7 +15,7 @@ "longitude": "Lengtegraad", "name": "Naam" }, - "description": "Airly-integratie van luchtkwaliteit instellen. Ga naar https://developer.airly.eu/register om de API-sleutel te genereren", + "description": "Om een API sleutel te genereren ga naar https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/no.json b/homeassistant/components/airly/translations/no.json index 4c81422d93c..015e95af8f2 100644 --- a/homeassistant/components/airly/translations/no.json +++ b/homeassistant/components/airly/translations/no.json @@ -15,7 +15,7 @@ "longitude": "Lengdegrad", "name": "Navn" }, - "description": "Sett opp Airly luftkvalitet integrasjon. For \u00e5 opprette API-n\u00f8kkel, g\u00e5 til [https://developer.airly.eu/register](https://developer.airly.eu/register)", + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://developer.airly.eu/register", "title": "" } } diff --git a/homeassistant/components/airly/translations/pl.json b/homeassistant/components/airly/translations/pl.json index f205a569474..dd44f2bf635 100644 --- a/homeassistant/components/airly/translations/pl.json +++ b/homeassistant/components/airly/translations/pl.json @@ -15,7 +15,7 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa" }, - "description": "Konfiguracja integracji Airly. By wygenerowa\u0107 klucz API, przejd\u017a na stron\u0119 https://developer.airly.eu/register", + "description": "By wygenerowa\u0107 klucz API, przejd\u017a na stron\u0119 https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/pt-BR.json b/homeassistant/components/airly/translations/pt-BR.json index e1f2fe097a6..0e9913b559c 100644 --- a/homeassistant/components/airly/translations/pt-BR.json +++ b/homeassistant/components/airly/translations/pt-BR.json @@ -15,7 +15,7 @@ "longitude": "Longitude", "name": "Nome" }, - "description": "Configure a integra\u00e7\u00e3o da qualidade do ar airly. Para gerar a chave de API v\u00e1 para https://developer.airly.eu/register", + "description": "Para gerar a chave de API, acesse https://developer.airly.eu/register", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/ru.json b/homeassistant/components/airly/translations/ru.json index 41ca90a8c02..80c6a98813c 100644 --- a/homeassistant/components/airly/translations/ru.json +++ b/homeassistant/components/airly/translations/ru.json @@ -15,7 +15,7 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0443 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u043e\u0437\u0434\u0443\u0445\u0430 Airly. \u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://developer.airly.eu/register.", + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://developer.airly.eu/register.", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/tr.json b/homeassistant/components/airly/translations/tr.json index fcae9294da2..7f5643be2d0 100644 --- a/homeassistant/components/airly/translations/tr.json +++ b/homeassistant/components/airly/translations/tr.json @@ -15,7 +15,7 @@ "longitude": "Boylam", "name": "Ad" }, - "description": "Airly hava kalitesi entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.airly.eu/register adresine gidin.", + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.airly.eu/register adresine gidin.", "title": "Airly" } } diff --git a/homeassistant/components/airly/translations/zh-Hant.json b/homeassistant/components/airly/translations/zh-Hant.json index e289bc7cd50..65f0bf8cde5 100644 --- a/homeassistant/components/airly/translations/zh-Hant.json +++ b/homeassistant/components/airly/translations/zh-Hant.json @@ -15,7 +15,7 @@ "longitude": "\u7d93\u5ea6", "name": "\u540d\u7a31" }, - "description": "\u6b32\u8a2d\u5b9a Airly \u7a7a\u6c23\u54c1\u8cea\u6574\u5408\u3002\u8acb\u81f3 https://developer.airly.eu/register \u7522\u751f API \u91d1\u9470", + "description": "\u8acb\u81f3 https://developer.airly.eu/register \u4ee5\u7522\u751f API \u91d1\u9470", "title": "Airly" } } diff --git a/homeassistant/components/airnow/translations/ca.json b/homeassistant/components/airnow/translations/ca.json index 2db3cfad563..9a2b0aa9afc 100644 --- a/homeassistant/components/airnow/translations/ca.json +++ b/homeassistant/components/airnow/translations/ca.json @@ -17,7 +17,7 @@ "longitude": "Longitud", "radius": "Radi de l'estaci\u00f3 (milles; opcional)" }, - "description": "Configura la integraci\u00f3 de qualitat d'aire AirNow. Per generar la clau API, v\u00e9s a https://docs.airnowapi.org/account/request/", + "description": "Per generar la clau API, v\u00e9s a https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/de.json b/homeassistant/components/airnow/translations/de.json index adf9ddf85a3..3c207a39cce 100644 --- a/homeassistant/components/airnow/translations/de.json +++ b/homeassistant/components/airnow/translations/de.json @@ -17,7 +17,7 @@ "longitude": "L\u00e4ngengrad", "radius": "Stationsradius (Meilen; optional)" }, - "description": "Richte die AirNow-Luftqualit\u00e4tsintegration ein. Um den API-Schl\u00fcssel zu generieren, besuche https://docs.airnowapi.org/account/request/.", + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/en.json b/homeassistant/components/airnow/translations/en.json index 371bb270ac1..cb3081284b4 100644 --- a/homeassistant/components/airnow/translations/en.json +++ b/homeassistant/components/airnow/translations/en.json @@ -17,7 +17,7 @@ "longitude": "Longitude", "radius": "Station Radius (miles; optional)" }, - "description": "Set up AirNow air quality integration. To generate API key go to https://docs.airnowapi.org/account/request/", + "description": "To generate API key go to https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/et.json b/homeassistant/components/airnow/translations/et.json index 52b2bb618e0..3bdf8661d36 100644 --- a/homeassistant/components/airnow/translations/et.json +++ b/homeassistant/components/airnow/translations/et.json @@ -17,7 +17,7 @@ "longitude": "Pikkuskraad", "radius": "Jaama raadius (miilid; valikuline)" }, - "description": "Seadista AirNow \u00f5hukvaliteedi sidumine. API-v\u00f5tme loomiseks mine aadressile https://docs.airnowapi.org/account/request/", + "description": "API-v\u00f5tme loomiseks mine aadressile https://docs.airnowapi.org/account/request/", "title": "" } } diff --git a/homeassistant/components/airnow/translations/fr.json b/homeassistant/components/airnow/translations/fr.json index 1cfe1652771..2da7427e1be 100644 --- a/homeassistant/components/airnow/translations/fr.json +++ b/homeassistant/components/airnow/translations/fr.json @@ -17,7 +17,7 @@ "longitude": "Longitude", "radius": "Rayon d'action de la station (en miles, facultatif)" }, - "description": "Configurez l'int\u00e9gration de la qualit\u00e9 de l'air AirNow. Pour g\u00e9n\u00e9rer la cl\u00e9 API, acc\u00e9dez \u00e0 https://docs.airnowapi.org/account/request/", + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/hu.json b/homeassistant/components/airnow/translations/hu.json index 3f1bef471ee..52bf8cd63a0 100644 --- a/homeassistant/components/airnow/translations/hu.json +++ b/homeassistant/components/airnow/translations/hu.json @@ -17,7 +17,7 @@ "longitude": "Hossz\u00fas\u00e1g", "radius": "\u00c1llom\u00e1s sugara (m\u00e9rf\u00f6ld; opcion\u00e1lis)" }, - "description": "\u00c1ll\u00edtsa be az AirNow leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3t. Az API-kulcs el\u0151\u00e1ll\u00edt\u00e1s\u00e1hoz keresse fel a https://docs.airnowapi.org/account/request/ oldalt.", + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://docs.airnowapi.org/account/request/ webhelyet", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/id.json b/homeassistant/components/airnow/translations/id.json index 66fdff72fae..4d4ac987320 100644 --- a/homeassistant/components/airnow/translations/id.json +++ b/homeassistant/components/airnow/translations/id.json @@ -17,7 +17,7 @@ "longitude": "Bujur", "radius": "Radius Stasiun (mil; opsional)" }, - "description": "Siapkan integrasi kualitas udara AirNow. Untuk membuat kunci API buka https://docs.airnowapi.org/account/request/", + "description": "Untuk membuat kunci API buka https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/it.json b/homeassistant/components/airnow/translations/it.json index 9dda15dfbd2..77715910c75 100644 --- a/homeassistant/components/airnow/translations/it.json +++ b/homeassistant/components/airnow/translations/it.json @@ -17,7 +17,7 @@ "longitude": "Logitudine", "radius": "Raggio stazione (miglia; opzionale)" }, - "description": "Configura l'integrazione per la qualit\u00e0 dell'aria AirNow. Per generare la chiave API, vai su https://docs.airnowapi.org/account/request/", + "description": "Per generare la chiave API vai su https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/nl.json b/homeassistant/components/airnow/translations/nl.json index 090a5363823..5e5e33bf93d 100644 --- a/homeassistant/components/airnow/translations/nl.json +++ b/homeassistant/components/airnow/translations/nl.json @@ -17,7 +17,7 @@ "longitude": "Lengtegraad", "radius": "Stationsradius (mijl; optioneel)" }, - "description": "AirNow luchtkwaliteit integratie opzetten. Om een API sleutel te genereren ga naar https://docs.airnowapi.org/account/request/", + "description": "Om een API sleutel te genereren ga naar https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/no.json b/homeassistant/components/airnow/translations/no.json index 19fa7e12207..b56b7096e2b 100644 --- a/homeassistant/components/airnow/translations/no.json +++ b/homeassistant/components/airnow/translations/no.json @@ -17,7 +17,7 @@ "longitude": "Lengdegrad", "radius": "Stasjonsradius (miles; valgfritt)" }, - "description": "Konfigurer integrering av luftkvalitet i AirNow. For \u00e5 generere en API-n\u00f8kkel, g\u00e5r du til https://docs.airnowapi.org/account/request/", + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://docs.airnowapi.org/account/request/", "title": "" } } diff --git a/homeassistant/components/airnow/translations/pl.json b/homeassistant/components/airnow/translations/pl.json index fe4310607b9..f29a48dc47b 100644 --- a/homeassistant/components/airnow/translations/pl.json +++ b/homeassistant/components/airnow/translations/pl.json @@ -17,7 +17,7 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "radius": "Promie\u0144 od stacji (w milach; opcjonalnie)" }, - "description": "Konfiguracja integracji jako\u015bci powietrza AirNow. Aby wygenerowa\u0107 klucz API, przejd\u017a do https://docs.airnowapi.org/account/request/", + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/pt-BR.json b/homeassistant/components/airnow/translations/pt-BR.json index fa24093b419..c1bda0098cd 100644 --- a/homeassistant/components/airnow/translations/pt-BR.json +++ b/homeassistant/components/airnow/translations/pt-BR.json @@ -17,7 +17,7 @@ "longitude": "Longitude", "radius": "Raio da Esta\u00e7\u00e3o (milhas; opcional)" }, - "description": "Configure a integra\u00e7\u00e3o da qualidade do ar AirNow. Para gerar a chave de API, acesse https://docs.airnowapi.org/account/request/", + "description": "Para gerar a chave de API, acesse https://docs.airnowapi.org/account/request/", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/ru.json b/homeassistant/components/airnow/translations/ru.json index 9667accb7c4..c5a9137f4c7 100644 --- a/homeassistant/components/airnow/translations/ru.json +++ b/homeassistant/components/airnow/translations/ru.json @@ -17,7 +17,7 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "radius": "\u0420\u0430\u0434\u0438\u0443\u0441 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 (\u0432 \u043c\u0438\u043b\u044f\u0445; \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" }, - "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0443 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u043e\u0437\u0434\u0443\u0445\u0430 AirNow. \u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://docs.airnowapi.org/account/request/.", + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://docs.airnowapi.org/account/request/.", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/tr.json b/homeassistant/components/airnow/translations/tr.json index 590332b496c..1f80d7805da 100644 --- a/homeassistant/components/airnow/translations/tr.json +++ b/homeassistant/components/airnow/translations/tr.json @@ -17,7 +17,7 @@ "longitude": "Boylam", "radius": "\u0130stasyon Yar\u0131\u00e7ap\u0131 (mil; iste\u011fe ba\u011fl\u0131)" }, - "description": "AirNow hava kalitesi entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://docs.airnowapi.org/account/request/ adresine gidin.", + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://docs.airnowapi.org/account/request/ adresine gidin.", "title": "AirNow" } } diff --git a/homeassistant/components/airnow/translations/zh-Hant.json b/homeassistant/components/airnow/translations/zh-Hant.json index 08d7aab5878..bb18a5d8975 100644 --- a/homeassistant/components/airnow/translations/zh-Hant.json +++ b/homeassistant/components/airnow/translations/zh-Hant.json @@ -17,7 +17,7 @@ "longitude": "\u7d93\u5ea6", "radius": "\u89c0\u6e2c\u7ad9\u534a\u5f91\uff08\u82f1\u91cc\uff1b\u9078\u9805\uff09" }, - "description": "\u6b32\u8a2d\u5b9a AirNow \u7a7a\u6c23\u54c1\u8cea\u6574\u5408\u3002\u8acb\u81f3 https://docs.airnowapi.org/account/request/ \u7522\u751f API \u91d1\u9470", + "description": "\u8acb\u81f3 https://docs.airnowapi.org/account/request/ \u4ee5\u7522\u751f API \u91d1\u9470", "title": "AirNow" } } diff --git a/homeassistant/components/airvisual/translations/sensor.fr.json b/homeassistant/components/airvisual/translations/sensor.fr.json index 47feff6bd79..2f9aacc775b 100644 --- a/homeassistant/components/airvisual/translations/sensor.fr.json +++ b/homeassistant/components/airvisual/translations/sensor.fr.json @@ -10,7 +10,7 @@ }, "airvisual__pollutant_level": { "good": "Bon", - "hazardous": "Hasardeux", + "hazardous": "Dangereux", "moderate": "Mod\u00e9r\u00e9", "unhealthy": "Malsain", "unhealthy_sensitive": "Malsain pour les groupes sensibles", diff --git a/homeassistant/components/airzone/translations/ca.json b/homeassistant/components/airzone/translations/ca.json index bbff47a9904..111a53fcf3a 100644 --- a/homeassistant/components/airzone/translations/ca.json +++ b/homeassistant/components/airzone/translations/ca.json @@ -4,7 +4,8 @@ "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_system_id": "ID de sistema Airzone inv\u00e0lid" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/cs.json b/homeassistant/components/airzone/translations/cs.json new file mode 100644 index 00000000000..aad89c1cbe3 --- /dev/null +++ b/homeassistant/components/airzone/translations/cs.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + }, + "step": { + "user": { + "data": { + "host": "Hostitel", + "port": "Port" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/de.json b/homeassistant/components/airzone/translations/de.json index 7b3f5030f06..2edb50330f4 100644 --- a/homeassistant/components/airzone/translations/de.json +++ b/homeassistant/components/airzone/translations/de.json @@ -4,7 +4,8 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_system_id": "Ung\u00fcltige Airzone-System-ID" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/el.json b/homeassistant/components/airzone/translations/el.json index 7b04fe27743..13da6efe27d 100644 --- a/homeassistant/components/airzone/translations/el.json +++ b/homeassistant/components/airzone/translations/el.json @@ -4,7 +4,8 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_system_id": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 Airzone" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/et.json b/homeassistant/components/airzone/translations/et.json index 307aa0de0a5..dff9d1173f6 100644 --- a/homeassistant/components/airzone/translations/et.json +++ b/homeassistant/components/airzone/translations/et.json @@ -4,7 +4,8 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "invalid_system_id": "Airzone'i s\u00fcsteemi ID on vigane" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/fr.json b/homeassistant/components/airzone/translations/fr.json index 1fdf1e4397b..40d22ad8bd9 100644 --- a/homeassistant/components/airzone/translations/fr.json +++ b/homeassistant/components/airzone/translations/fr.json @@ -4,7 +4,8 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { - "cannot_connect": "\u00c9chec de connexion" + "cannot_connect": "\u00c9chec de connexion", + "invalid_system_id": "ID syst\u00e8me Airzone non valide" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/hu.json b/homeassistant/components/airzone/translations/hu.json index 88e7449a1c4..9a835f88dfc 100644 --- a/homeassistant/components/airzone/translations/hu.json +++ b/homeassistant/components/airzone/translations/hu.json @@ -4,7 +4,8 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_system_id": "\u00c9rv\u00e9nytelen Airzone rendszerazonos\u00edt\u00f3" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/id.json b/homeassistant/components/airzone/translations/id.json index c37058bac34..ec37639811b 100644 --- a/homeassistant/components/airzone/translations/id.json +++ b/homeassistant/components/airzone/translations/id.json @@ -4,7 +4,8 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "invalid_system_id": "ID Sistem Airzone Tidak Valid" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/it.json b/homeassistant/components/airzone/translations/it.json index db377b36fcc..32f8c67b545 100644 --- a/homeassistant/components/airzone/translations/it.json +++ b/homeassistant/components/airzone/translations/it.json @@ -4,7 +4,8 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "invalid_system_id": "ID sistema Airzone non valido" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/ja.json b/homeassistant/components/airzone/translations/ja.json index 71b8bf1c908..28ebf809dbf 100644 --- a/homeassistant/components/airzone/translations/ja.json +++ b/homeassistant/components/airzone/translations/ja.json @@ -4,7 +4,8 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_system_id": "Airzone\u30b7\u30b9\u30c6\u30e0ID\u304c\u7121\u52b9\u3067\u3059" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/nl.json b/homeassistant/components/airzone/translations/nl.json index 4611c23eb1e..0e84f756de1 100644 --- a/homeassistant/components/airzone/translations/nl.json +++ b/homeassistant/components/airzone/translations/nl.json @@ -4,7 +4,8 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kon niet verbinden", + "invalid_system_id": "Ongeldige Airzone systeem ID" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/no.json b/homeassistant/components/airzone/translations/no.json index f078016b761..6eeaee3a53a 100644 --- a/homeassistant/components/airzone/translations/no.json +++ b/homeassistant/components/airzone/translations/no.json @@ -4,7 +4,8 @@ "already_configured": "Enheten er allerede konfigurert" }, "error": { - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "invalid_system_id": "Ugyldig Airzone System ID" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/pl.json b/homeassistant/components/airzone/translations/pl.json index 38dc359b248..e389618ff80 100644 --- a/homeassistant/components/airzone/translations/pl.json +++ b/homeassistant/components/airzone/translations/pl.json @@ -4,7 +4,8 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_system_id": "Nieprawid\u0142owy identyfikator systemu Airzone" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/pt-BR.json b/homeassistant/components/airzone/translations/pt-BR.json index 1a8df1fef99..c2668c937b4 100644 --- a/homeassistant/components/airzone/translations/pt-BR.json +++ b/homeassistant/components/airzone/translations/pt-BR.json @@ -4,7 +4,8 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "invalid_system_id": "ID do sistema Airzone inv\u00e1lido" }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/ru.json b/homeassistant/components/airzone/translations/ru.json index 6032b0bdf00..d480866b262 100644 --- a/homeassistant/components/airzone/translations/ru.json +++ b/homeassistant/components/airzone/translations/ru.json @@ -4,7 +4,8 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_system_id": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0438\u0441\u0442\u0435\u043c\u044b Airzone." }, "step": { "user": { diff --git a/homeassistant/components/airzone/translations/tr.json b/homeassistant/components/airzone/translations/tr.json new file mode 100644 index 00000000000..c911478ec32 --- /dev/null +++ b/homeassistant/components/airzone/translations/tr.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_system_id": "Ge\u00e7ersiz Airzone Sistem Kimli\u011fi" + }, + "step": { + "user": { + "data": { + "host": "Sunucu", + "port": "Port" + }, + "description": "Airzone entegrasyonunu ayarlay\u0131n." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/zh-Hant.json b/homeassistant/components/airzone/translations/zh-Hant.json index 01e2db9730b..42166fe39ec 100644 --- a/homeassistant/components/airzone/translations/zh-Hant.json +++ b/homeassistant/components/airzone/translations/zh-Hant.json @@ -4,7 +4,8 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_system_id": "\u7121\u6548\u7684 Airzone \u7cfb\u7d71 ID" }, "step": { "user": { diff --git a/homeassistant/components/almond/translations/hu.json b/homeassistant/components/almond/translations/hu.json index b0a3a1b2461..b424675faaa 100644 --- a/homeassistant/components/almond/translations/hu.json +++ b/homeassistant/components/almond/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "step": { @@ -12,7 +12,7 @@ "title": "Almond - Home Assistant b\u0151v\u00edtm\u00e9nnyel" }, "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/almond/translations/it.json b/homeassistant/components/almond/translations/it.json index 58eadad0d80..1d028e73111 100644 --- a/homeassistant/components/almond/translations/it.json +++ b/homeassistant/components/almond/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "cannot_connect": "Impossibile connettersi", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, diff --git a/homeassistant/components/ambee/translations/hu.json b/homeassistant/components/ambee/translations/hu.json index e4cef44c5ba..80b14ac7470 100644 --- a/homeassistant/components/ambee/translations/hu.json +++ b/homeassistant/components/ambee/translations/hu.json @@ -19,7 +19,7 @@ "api_key": "API kulcs", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Integr\u00e1lja \u00f6ssze Ambeet Home Assistanttal." } diff --git a/homeassistant/components/ambiclimate/translations/hu.json b/homeassistant/components/ambiclimate/translations/hu.json index 1e67873f1aa..3de93421f42 100644 --- a/homeassistant/components/ambiclimate/translations/hu.json +++ b/homeassistant/components/ambiclimate/translations/hu.json @@ -9,12 +9,12 @@ "default": "Sikeres hiteles\u00edt\u00e9s" }, "error": { - "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt megnyomn\u00e1 a K\u00fcld\u00e9s gombot", + "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt folytatn\u00e1", "no_token": "Ambiclimate-al nem siker\u00fclt a hiteles\u00edt\u00e9s" }, "step": { "auth": { - "description": "K\u00e9rj\u00fck, k\u00f6vesse ezt a [link]({authorization_url}}) \u00e9s ** Enged\u00e9lyezze ** a hozz\u00e1f\u00e9r\u00e9st Ambiclimate -fi\u00f3kj\u00e1hoz, majd t\u00e9rjen vissza, \u00e9s nyomja meg az al\u00e1bbi ** K\u00fcld\u00e9s ** gombot.\n(Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a megadott visszah\u00edv\u00e1si URL {cb_url})", + "description": "K\u00e9rj\u00fck, k\u00f6vesse ezt a [link]({authorization_url}}) \u00e9s **Enged\u00e9lyezze** a hozz\u00e1f\u00e9r\u00e9st Ambiclimate -fi\u00f3kj\u00e1hoz, majd t\u00e9rjen vissza, \u00e9s nyomja meg az al\u00e1bbi **Mehet** gombot.\n(Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a megadott visszah\u00edv\u00e1si URL {cb_url})", "title": "Ambiclimate hiteles\u00edt\u00e9se" } } diff --git a/homeassistant/components/ambiclimate/translations/it.json b/homeassistant/components/ambiclimate/translations/it.json index ccf0836ee1d..d48f6748bbe 100644 --- a/homeassistant/components/ambiclimate/translations/it.json +++ b/homeassistant/components/ambiclimate/translations/it.json @@ -3,13 +3,13 @@ "abort": { "access_token": "Errore sconosciuto durante la generazione di un token di accesso.", "already_configured": "L'account \u00e8 gi\u00e0 configurato", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione." + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione." }, "create_entry": { "default": "Autenticazione riuscita" }, "error": { - "follow_link": "Si prega di seguire il link e di autenticarsi prima di premere Invia", + "follow_link": "Segui il collegamento e autenticati prima di premere Invia", "no_token": "Non autenticato con Ambiclimate" }, "step": { diff --git a/homeassistant/components/ambient_station/translations/hu.json b/homeassistant/components/ambient_station/translations/hu.json index 6974bda2c20..bc2ff29b7ee 100644 --- a/homeassistant/components/ambient_station/translations/hu.json +++ b/homeassistant/components/ambient_station/translations/hu.json @@ -5,7 +5,7 @@ }, "error": { "invalid_key": "\u00c9rv\u00e9nytelen API kulcs", - "no_devices": "Nincs a fi\u00f3kodban tal\u00e1lhat\u00f3 eszk\u00f6z" + "no_devices": "Nem tal\u00e1lhat\u00f3 a fi\u00f3kj\u00e1ban eszk\u00f6z" }, "step": { "user": { diff --git a/homeassistant/components/androidtv/translations/hu.json b/homeassistant/components/androidtv/translations/hu.json index c8d8c0fd97b..3b75153a351 100644 --- a/homeassistant/components/androidtv/translations/hu.json +++ b/homeassistant/components/androidtv/translations/hu.json @@ -43,12 +43,12 @@ "init": { "data": { "apps": "Alkalmaz\u00e1slista konfigur\u00e1l\u00e1sa", - "exclude_unnamed_apps": "Ismeretlen nev\u0171 alkalmaz\u00e1s kiz\u00e1r\u00e1sa", - "get_sources": "A fut\u00f3 alkalmaz\u00e1sok forr\u00e1slist\u00e1jak\u00e9nt val\u00f3 megjelen\u00edt\u00e9se", - "screencap": "A k\u00e9perny\u0151n megjelen\u0151 k\u00e9p legyen-e az albumbor\u00edt\u00f3", + "exclude_unnamed_apps": "Az ismeretlen nev\u0171 alkalmaz\u00e1sok kiz\u00e1r\u00e1sa a forr\u00e1slist\u00e1b\u00f3l", + "get_sources": "A fut\u00f3 alkalmaz\u00e1sok megjelen\u00edt\u00e9se a bemeneti forr\u00e1sok list\u00e1j\u00e1ban", + "screencap": "Haszn\u00e1ljon k\u00e9perny\u0151felv\u00e9telt az albumbor\u00edt\u00f3khoz", "state_detection_rules": "\u00c1llapotfelismer\u00e9si szab\u00e1lyok konfigur\u00e1l\u00e1sa", - "turn_off_command": "ADB shell parancs az alap\u00e9rtelmezett kikapcsol\u00e1si (turn_off) parancs fel\u00fcl\u00edr\u00e1s\u00e1ra", - "turn_on_command": "ADB shell parancs az alap\u00e9rtelmezett bekapcsol\u00e1si (turn_on) parancs fel\u00fcl\u00edr\u00e1s\u00e1ra" + "turn_off_command": "ADB shell kikapcsol\u00e1si parancs (alap\u00e9rtelmez\u00e9s szerint hagyja \u00fcresen)", + "turn_on_command": "ADB shell bekapcsol\u00e1si parancs (alap\u00e9rtelmez\u00e9s szerint hagyja \u00fcresen)" }, "title": "Android TV be\u00e1ll\u00edt\u00e1sok" }, diff --git a/homeassistant/components/apple_tv/translations/bg.json b/homeassistant/components/apple_tv/translations/bg.json index 4629a79d152..b1923cab650 100644 --- a/homeassistant/components/apple_tv/translations/bg.json +++ b/homeassistant/components/apple_tv/translations/bg.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "already_configured_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "ipv6_not_supported": "IPv6 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.", "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/apple_tv/translations/ca.json b/homeassistant/components/apple_tv/translations/ca.json index 88c49059067..c672733193a 100644 --- a/homeassistant/components/apple_tv/translations/ca.json +++ b/homeassistant/components/apple_tv/translations/ca.json @@ -9,6 +9,7 @@ "device_not_found": "No s'ha trobat el dispositiu durant el descobriment, prova de tornar-lo a afegir.", "inconsistent_device": "Els protocols esperats no s'han trobat durant el descobriment. Normalment aix\u00f2 indica un problema amb el DNS multicast (Zeroconf). Prova d'afegir el dispositiu de nou.", "invalid_config": "La configuraci\u00f3 d'aquest dispositiu no est\u00e0 completa. Intenta'l tornar a afegir.", + "ipv6_not_supported": "IPv6 no est\u00e0 suportat.", "no_devices_found": "No s'han trobat dispositius a la xarxa", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", "setup_failed": "No s'ha pogut configurar el dispositiu.", diff --git a/homeassistant/components/apple_tv/translations/de.json b/homeassistant/components/apple_tv/translations/de.json index ca6f69f5442..48149ce8394 100644 --- a/homeassistant/components/apple_tv/translations/de.json +++ b/homeassistant/components/apple_tv/translations/de.json @@ -9,6 +9,7 @@ "device_not_found": "Das Ger\u00e4t wurde bei der Erkennung nicht gefunden. Bitte versuche es erneut hinzuzuf\u00fcgen.", "inconsistent_device": "Die erwarteten Protokolle wurden bei der Erkennung nicht gefunden. Dies deutet normalerweise auf ein Problem mit Multicast-DNS (Zeroconf) hin. Bitte versuche das Ger\u00e4t erneut hinzuzuf\u00fcgen.", "invalid_config": "Die Konfiguration f\u00fcr dieses Ger\u00e4t ist unvollst\u00e4ndig. Bitte versuche, es erneut hinzuzuf\u00fcgen.", + "ipv6_not_supported": "IPv6 wird nicht unterst\u00fctzt.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "setup_failed": "Fehler beim Einrichten des Ger\u00e4ts.", diff --git a/homeassistant/components/apple_tv/translations/el.json b/homeassistant/components/apple_tv/translations/el.json index 11a61899b84..cbe36bf56fb 100644 --- a/homeassistant/components/apple_tv/translations/el.json +++ b/homeassistant/components/apple_tv/translations/el.json @@ -9,6 +9,7 @@ "device_not_found": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", "inconsistent_device": "\u03a4\u03b1 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03b1 \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u0391\u03c5\u03c4\u03cc \u03c3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03c5\u03c0\u03bf\u03b4\u03b7\u03bb\u03ce\u03bd\u03b5\u03b9 \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1 \u03bc\u03b5 \u03c4\u03bf multicast DNS (Zeroconf). \u03a0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae.", "invalid_config": "\u0397 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bb\u03bb\u03b9\u03c0\u03ae\u03c2. \u03a0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", + "ipv6_not_supported": "\u03a4\u03bf IPv6 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9.", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "setup_failed": "\u0391\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2.", diff --git a/homeassistant/components/apple_tv/translations/en.json b/homeassistant/components/apple_tv/translations/en.json index f455d590d79..792eaf32c29 100644 --- a/homeassistant/components/apple_tv/translations/en.json +++ b/homeassistant/components/apple_tv/translations/en.json @@ -2,11 +2,13 @@ "config": { "abort": { "already_configured": "Device is already configured", + "already_configured_device": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "backoff": "Device does not accept pairing requests at this time (you might have entered an invalid PIN code too many times), try again later.", "device_did_not_pair": "No attempt to finish pairing process was made from the device.", "device_not_found": "Device was not found during discovery, please try adding it again.", "inconsistent_device": "Expected protocols were not found during discovery. This normally indicates a problem with multicast DNS (Zeroconf). Please try adding the device again.", + "invalid_config": "The configuration for this device is incomplete. Please try adding it again.", "ipv6_not_supported": "IPv6 is not supported.", "no_devices_found": "No devices found on the network", "reauth_successful": "Re-authentication was successful", @@ -17,6 +19,7 @@ "already_configured": "Device is already configured", "invalid_auth": "Invalid authentication", "no_devices_found": "No devices found on the network", + "no_usable_service": "A device was found but could not identify any way to establish a connection to it. If you keep seeing this message, try specifying its IP address or restarting your Apple TV.", "unknown": "Unexpected error" }, "flow_title": "{name} ({type})", @@ -70,5 +73,6 @@ "description": "Configure general device settings" } } - } + }, + "title": "Apple TV" } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/et.json b/homeassistant/components/apple_tv/translations/et.json index fa660662d8b..8180b95fe12 100644 --- a/homeassistant/components/apple_tv/translations/et.json +++ b/homeassistant/components/apple_tv/translations/et.json @@ -9,6 +9,7 @@ "device_not_found": "Seadet avastamise ajal ei leitud, proovi seda uuesti lisada.", "inconsistent_device": "Eeldatavaid protokolle avastamise ajal ei leitud. See n\u00e4itab tavaliselt probleemi multcast DNS-iga (Zeroconf). Proovi seade uuesti lisada.", "invalid_config": "Selle seadme s\u00e4tted on puudulikud. Proovi see uuesti lisada.", + "ipv6_not_supported": "IPv6 ei ole toetatud.", "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet", "reauth_successful": "Taastuvastamine \u00f5nnestus", "setup_failed": "Seadme h\u00e4\u00e4lestamine nurjus.", diff --git a/homeassistant/components/apple_tv/translations/fr.json b/homeassistant/components/apple_tv/translations/fr.json index 510d22c79a5..6d7deea6e5a 100644 --- a/homeassistant/components/apple_tv/translations/fr.json +++ b/homeassistant/components/apple_tv/translations/fr.json @@ -9,6 +9,7 @@ "device_not_found": "L'appareil n'a pas \u00e9t\u00e9 trouv\u00e9 lors de la d\u00e9couverte, veuillez r\u00e9essayer de l'ajouter.", "inconsistent_device": "Les protocoles attendus n'ont pas \u00e9t\u00e9 trouv\u00e9s lors de la d\u00e9couverte. Cela indique normalement un probl\u00e8me avec le DNS multicast (Zeroconf). Veuillez r\u00e9essayer d'ajouter l'appareil.", "invalid_config": "La configuration de cet appareil est incompl\u00e8te. Veuillez r\u00e9essayer de l'ajouter.", + "ipv6_not_supported": "IPv6 n'est pas pris en charge.", "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", "setup_failed": "\u00c9chec de la configuration de l'appareil.", diff --git a/homeassistant/components/apple_tv/translations/he.json b/homeassistant/components/apple_tv/translations/he.json index 03e7dc5d4fe..61ca863bd66 100644 --- a/homeassistant/components/apple_tv/translations/he.json +++ b/homeassistant/components/apple_tv/translations/he.json @@ -4,6 +4,7 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_configured_device": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "ipv6_not_supported": "IPv6 \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da.", "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" diff --git a/homeassistant/components/apple_tv/translations/hu.json b/homeassistant/components/apple_tv/translations/hu.json index f76063c5eeb..73a30fbdd9a 100644 --- a/homeassistant/components/apple_tv/translations/hu.json +++ b/homeassistant/components/apple_tv/translations/hu.json @@ -3,12 +3,13 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_configured_device": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "backoff": "Az eszk\u00f6z jelenleg nem fogadja el a p\u00e1ros\u00edt\u00e1si k\u00e9relmeket (lehet, hogy t\u00fal sokszor adott meg \u00e9rv\u00e9nytelen PIN-k\u00f3dot), pr\u00f3b\u00e1lkozzon \u00fajra k\u00e9s\u0151bb.", "device_did_not_pair": "A p\u00e1ros\u00edt\u00e1s folyamat\u00e1t az eszk\u00f6zr\u0151l nem pr\u00f3b\u00e1lt\u00e1k befejezni.", "device_not_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a felder\u00edt\u00e9s sor\u00e1n, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni.", "inconsistent_device": "Az elv\u00e1rt protokollok nem tal\u00e1lhat\u00f3k a felder\u00edt\u00e9s sor\u00e1n. Ez \u00e1ltal\u00e1ban a multicast DNS (Zeroconf) probl\u00e9m\u00e1j\u00e1t jelzi. K\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni az eszk\u00f6zt.", "invalid_config": "Az eszk\u00f6z konfigur\u00e1l\u00e1sa nem teljes. K\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni.", + "ipv6_not_supported": "Az IPv6 nem t\u00e1mogatott.", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "setup_failed": "Az eszk\u00f6z be\u00e1ll\u00edt\u00e1sa sikertelen.", @@ -21,14 +22,14 @@ "no_usable_service": "Tal\u00e1ltunk egy eszk\u00f6zt, de nem tudtuk azonos\u00edtani, hogyan lehetne kapcsolatot l\u00e9tes\u00edteni vele. Ha tov\u00e1bbra is ezt az \u00fczenetet l\u00e1tja, pr\u00f3b\u00e1lja meg megadni az IP-c\u00edm\u00e9t, vagy ind\u00edtsa \u00fajra az Apple TV-t.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, - "flow_title": "{name}", + "flow_title": "{name} ({type})", "step": { "confirm": { - "description": "Arra k\u00e9sz\u00fcl, hogy felvegye {name} nev\u0171 Apple TV-t a Home Assistant p\u00e9ld\u00e1ny\u00e1ba. \n\n ** A folyamat befejez\u00e9s\u00e9hez t\u00f6bb PIN-k\u00f3dot kell megadnia. ** \n\nFelh\u00edvjuk figyelm\u00e9t, hogy ezzel az integr\u00e1ci\u00f3val *nem* fogja tudni kikapcsolni az Apple TV-t. Csak a Home Assistant saj\u00e1t m\u00e9dialej\u00e1tsz\u00f3ja kapcsol ki!", + "description": "\u00d6n a `{name}`, `{type}` t\u00edpus\u00fa eszk\u00f6zt k\u00e9sz\u00fcl hozz\u00e1adni a Home Assistanthoz.\n\n**A folyamat befejez\u00e9s\u00e9hez t\u00f6bb PIN k\u00f3dot is meg kell adnia.**\n\nK\u00e9rj\u00fck, vegye figyelembe, hogy ezzel az integr\u00e1ci\u00f3val *nem* tudja kikapcsolni az Apple TV k\u00e9sz\u00fcl\u00e9k\u00e9t. Csak a Home Assistant m\u00e9dialej\u00e1tsz\u00f3ja fog kikapcsolni!", "title": "Apple TV sikeresen hozz\u00e1adva" }, "pair_no_pin": { - "description": "P\u00e1ros\u00edt\u00e1sra van sz\u00fcks\u00e9g a {protocol} szolg\u00e1ltat\u00e1shoz. A folytat\u00e1shoz k\u00e9rj\u00fck, \u00edrja be az Apple TV {pin}-t.", + "description": "P\u00e1ros\u00edt\u00e1sra van sz\u00fcks\u00e9g a {protocol} szolg\u00e1ltat\u00e1shoz. A folytat\u00e1shoz k\u00e9rj\u00fck, \u00edrja be k\u00e9sz\u00fcl\u00e9ken a PIN k\u00f3dot: {pin}.", "title": "P\u00e1ros\u00edt\u00e1s" }, "pair_with_pin": { @@ -47,7 +48,7 @@ "title": "A p\u00e1ros\u00edt\u00e1s nem lehets\u00e9ges" }, "reconfigure": { - "description": "Ez az Apple TV csatlakoz\u00e1si neh\u00e9zs\u00e9gekkel k\u00fczd, ez\u00e9rt \u00fajra kell konfigur\u00e1lni.", + "description": "Konfigur\u00e1lja \u00fajra ezt az eszk\u00f6zt a m\u0171k\u00f6d\u0151k\u00e9pess\u00e9g vissza\u00e1ll\u00edt\u00e1s\u00e1hoz.", "title": "Eszk\u00f6z \u00fajrakonfigur\u00e1l\u00e1sa" }, "service_problem": { @@ -58,7 +59,7 @@ "data": { "device_input": "Eszk\u00f6z" }, - "description": "El\u0151sz\u00f6r \u00edrja be a hozz\u00e1adni k\u00edv\u00e1nt Apple TV eszk\u00f6znev\u00e9t (pl. Konyha vagy H\u00e1l\u00f3szoba) vagy IP-c\u00edm\u00e9t. Ha valamilyen eszk\u00f6zt automatikusan tal\u00e1ltak a h\u00e1l\u00f3zat\u00e1n, az al\u00e1bb l\u00e1that\u00f3. \n\nHa nem l\u00e1tja eszk\u00f6z\u00e9t, vagy b\u00e1rmilyen probl\u00e9m\u00e1t tapasztal, pr\u00f3b\u00e1lja meg megadni az eszk\u00f6z IP-c\u00edm\u00e9t. \n\n {devices}", + "description": "Kezdje a hozz\u00e1adni k\u00edv\u00e1nt Apple TV eszk\u00f6znev\u00e9nek (pl. Konyha vagy H\u00e1l\u00f3szoba) vagy IP-c\u00edm\u00e9nek megad\u00e1s\u00e1val. \n\n Ha nem l\u00e1tja az eszk\u00f6zt, vagy b\u00e1rmilyen probl\u00e9m\u00e1t tapasztal, pr\u00f3b\u00e1lja meg megadni az eszk\u00f6z IP-c\u00edm\u00e9t.", "title": "\u00daj Apple TV be\u00e1ll\u00edt\u00e1sa" } } diff --git a/homeassistant/components/apple_tv/translations/id.json b/homeassistant/components/apple_tv/translations/id.json index 8a978eca737..7120d0671b8 100644 --- a/homeassistant/components/apple_tv/translations/id.json +++ b/homeassistant/components/apple_tv/translations/id.json @@ -9,6 +9,7 @@ "device_not_found": "Perangkat tidak ditemukan selama penemuan, coba tambahkan lagi.", "inconsistent_device": "Protokol yang diharapkan tidak ditemukan selama penemuan. Ini biasanya terjadi karena masalah dengan DNS multicast (Zeroconf). Coba tambahkan perangkat lagi.", "invalid_config": "Konfigurasi untuk perangkat ini tidak lengkap. Coba tambahkan lagi.", + "ipv6_not_supported": "IPv6 tidak didukung.", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", "reauth_successful": "Autentikasi ulang berhasil", "setup_failed": "Gagal menyiapkan perangkat.", diff --git a/homeassistant/components/apple_tv/translations/it.json b/homeassistant/components/apple_tv/translations/it.json index 47bd8612265..b1e3a06440f 100644 --- a/homeassistant/components/apple_tv/translations/it.json +++ b/homeassistant/components/apple_tv/translations/it.json @@ -9,6 +9,7 @@ "device_not_found": "Il dispositivo non \u00e8 stato trovato durante il rilevamento, prova ad aggiungerlo di nuovo.", "inconsistent_device": "I protocolli previsti non sono stati trovati durante il rilevamento. Questo normalmente indica un problema con DNS multicast (Zeroconf). Prova ad aggiungere di nuovo il dispositivo.", "invalid_config": "La configurazione per questo dispositivo \u00e8 incompleta. Prova ad aggiungerlo di nuovo.", + "ipv6_not_supported": "IPv6 non \u00e8 supportato.", "no_devices_found": "Nessun dispositivo trovato sulla rete", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "setup_failed": "Impossibile configurare il dispositivo.", diff --git a/homeassistant/components/apple_tv/translations/ja.json b/homeassistant/components/apple_tv/translations/ja.json index c70dda18d01..9984006365e 100644 --- a/homeassistant/components/apple_tv/translations/ja.json +++ b/homeassistant/components/apple_tv/translations/ja.json @@ -9,6 +9,7 @@ "device_not_found": "\u691c\u51fa\u4e2d\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", "inconsistent_device": "\u691c\u51fa\u4e2d\u306b\u671f\u5f85\u3057\u305f\u30d7\u30ed\u30c8\u30b3\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3053\u308c\u306f\u901a\u5e38\u3001\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8DNS(Zeroconf)\u306b\u554f\u984c\u304c\u3042\u308b\u3053\u3068\u3092\u793a\u3057\u3066\u3044\u307e\u3059\u3002\u30c7\u30d0\u30a4\u30b9\u3092\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", "invalid_config": "\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u306e\u8a2d\u5b9a\u306f\u4e0d\u5b8c\u5168\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", + "ipv6_not_supported": "IPv6\u306b\u306f\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093\u3002", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", "setup_failed": "\u30c7\u30d0\u30a4\u30b9\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002", diff --git a/homeassistant/components/apple_tv/translations/nl.json b/homeassistant/components/apple_tv/translations/nl.json index 7fdc20c7291..8aaf120403c 100644 --- a/homeassistant/components/apple_tv/translations/nl.json +++ b/homeassistant/components/apple_tv/translations/nl.json @@ -9,6 +9,7 @@ "device_not_found": "Apparaat werd niet gevonden tijdens het zoeken, probeer het opnieuw toe te voegen.", "inconsistent_device": "De verwachte protocollen zijn niet gevonden tijdens het zoeken. Dit wijst gewoonlijk op een probleem met multicast DNS (Zeroconf). Probeer het apparaat opnieuw toe te voegen.", "invalid_config": "De configuratie voor dit apparaat is onvolledig. Probeer het opnieuw toe te voegen.", + "ipv6_not_supported": "IPv6 wordt niet ondersteund.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "reauth_successful": "Herauthenticatie was succesvol", "setup_failed": "Kan het apparaat niet instellen.", diff --git a/homeassistant/components/apple_tv/translations/no.json b/homeassistant/components/apple_tv/translations/no.json index b24c48a396e..e364633597e 100644 --- a/homeassistant/components/apple_tv/translations/no.json +++ b/homeassistant/components/apple_tv/translations/no.json @@ -9,6 +9,7 @@ "device_not_found": "Enheten ble ikke funnet under oppdagelsen. Pr\u00f8v \u00e5 legge den til p\u00e5 nytt.", "inconsistent_device": "Forventede protokoller ble ikke funnet under oppdagelsen. Dette indikerer vanligvis et problem med multicast DNS (Zeroconf). Pr\u00f8v \u00e5 legge til enheten p\u00e5 nytt.", "invalid_config": "Konfigurasjonen for denne enheten er ufullstendig. Pr\u00f8v \u00e5 legge den til p\u00e5 nytt.", + "ipv6_not_supported": "IPv6 st\u00f8ttes ikke.", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", "setup_failed": "Kunne ikke konfigurere enheten.", diff --git a/homeassistant/components/apple_tv/translations/pl.json b/homeassistant/components/apple_tv/translations/pl.json index 1ba9b46dc3b..303de09c1dd 100644 --- a/homeassistant/components/apple_tv/translations/pl.json +++ b/homeassistant/components/apple_tv/translations/pl.json @@ -9,6 +9,7 @@ "device_not_found": "Urz\u0105dzenie nie zosta\u0142o znalezione podczas wykrywania, spr\u00f3buj doda\u0107 je ponownie.", "inconsistent_device": "Oczekiwane protoko\u0142y nie zosta\u0142y znalezione podczas wykrywania. Zwykle wskazuje to na problem z multicastem DNS (Zeroconf). Spr\u00f3buj ponownie doda\u0107 urz\u0105dzenie.", "invalid_config": "Konfiguracja tego urz\u0105dzenia jest niekompletna. Spr\u00f3buj doda\u0107 go ponownie.", + "ipv6_not_supported": "IPv6 nie jest obs\u0142ugiwany.", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", "setup_failed": "Nie uda\u0142o si\u0119 skonfigurowa\u0107 urz\u0105dzenia.", diff --git a/homeassistant/components/apple_tv/translations/pt-BR.json b/homeassistant/components/apple_tv/translations/pt-BR.json index 79fee5f02ec..539335c17d3 100644 --- a/homeassistant/components/apple_tv/translations/pt-BR.json +++ b/homeassistant/components/apple_tv/translations/pt-BR.json @@ -9,6 +9,7 @@ "device_not_found": "O dispositivo n\u00e3o foi encontrado durante a descoberta. Tente adicion\u00e1-lo novamente.", "inconsistent_device": "Os protocolos esperados n\u00e3o foram encontrados durante a descoberta. Isso normalmente indica um problema com o DNS multicast (Zeroconf). Tente adicionar o dispositivo novamente.", "invalid_config": "A configura\u00e7\u00e3o deste dispositivo est\u00e1 incompleta. Tente adicion\u00e1-lo novamente.", + "ipv6_not_supported": "IPv6 n\u00e3o \u00e9 suportado.", "no_devices_found": "Nenhum dispositivo encontrado na rede", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", "setup_failed": "Falha ao configurar o dispositivo.", diff --git a/homeassistant/components/apple_tv/translations/ru.json b/homeassistant/components/apple_tv/translations/ru.json index 4863a541603..88516d45af3 100644 --- a/homeassistant/components/apple_tv/translations/ru.json +++ b/homeassistant/components/apple_tv/translations/ru.json @@ -9,6 +9,7 @@ "device_not_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", "inconsistent_device": "\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b. \u041e\u0431\u044b\u0447\u043d\u043e \u044d\u0442\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u044b\u043c DNS (Zeroconf). \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", "invalid_config": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", + "ipv6_not_supported": "IPv6 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", "setup_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e.", diff --git a/homeassistant/components/apple_tv/translations/tr.json b/homeassistant/components/apple_tv/translations/tr.json index 4b9c2a4ca07..cee9fcce81e 100644 --- a/homeassistant/components/apple_tv/translations/tr.json +++ b/homeassistant/components/apple_tv/translations/tr.json @@ -9,6 +9,7 @@ "device_not_found": "Cihaz ke\u015fif s\u0131ras\u0131nda bulunamad\u0131, l\u00fctfen tekrar eklemeyi deneyin.", "inconsistent_device": "Ke\u015fif s\u0131ras\u0131nda beklenen protokoller bulunamad\u0131. Bu normalde \u00e7ok noktaya yay\u0131n DNS (Zeroconf) ile ilgili bir sorunu g\u00f6sterir. L\u00fctfen cihaz\u0131 tekrar eklemeyi deneyin.", "invalid_config": "Bu ayg\u0131t\u0131n yap\u0131land\u0131rmas\u0131 tamamlanmad\u0131. L\u00fctfen tekrar eklemeyi deneyin.", + "ipv6_not_supported": "IPv6 desteklenmiyor.", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", "setup_failed": "Cihaz kurulumu ba\u015far\u0131s\u0131z.", diff --git a/homeassistant/components/apple_tv/translations/zh-Hant.json b/homeassistant/components/apple_tv/translations/zh-Hant.json index 41732c0813e..b4e5108d474 100644 --- a/homeassistant/components/apple_tv/translations/zh-Hant.json +++ b/homeassistant/components/apple_tv/translations/zh-Hant.json @@ -9,6 +9,7 @@ "device_not_found": "\u641c\u5c0b\u4e0d\u5230\u88dd\u7f6e\u3001\u8acb\u8a66\u8457\u518d\u65b0\u589e\u4e00\u6b21\u3002", "inconsistent_device": "\u641c\u5c0b\u4e0d\u5230\u9810\u671f\u7684\u901a\u8a0a\u5354\u5b9a\u3002\u901a\u5e38\u539f\u56e0\u70ba Multicast DNS (Zeroconf) \u554f\u984c\u3001\u8acb\u8a66\u8457\u518d\u65b0\u589e\u4e00\u6b21\u3002", "invalid_config": "\u6b64\u88dd\u7f6e\u8a2d\u5b9a\u4e0d\u5b8c\u6574\uff0c\u8acb\u7a0d\u5019\u518d\u8a66\u4e00\u6b21\u3002", + "ipv6_not_supported": "\u4e0d\u652f\u63f4 IPv6\u3002", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", "setup_failed": "\u88dd\u7f6e\u8a2d\u5b9a\u5931\u6557\u3002", diff --git a/homeassistant/components/arcam_fmj/translations/hu.json b/homeassistant/components/arcam_fmj/translations/hu.json index 964ebe2a33d..897b462eb48 100644 --- a/homeassistant/components/arcam_fmj/translations/hu.json +++ b/homeassistant/components/arcam_fmj/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "error": { diff --git a/homeassistant/components/arcam_fmj/translations/it.json b/homeassistant/components/arcam_fmj/translations/it.json index 2b99566888b..637bfa6533d 100644 --- a/homeassistant/components/arcam_fmj/translations/it.json +++ b/homeassistant/components/arcam_fmj/translations/it.json @@ -6,8 +6,8 @@ "cannot_connect": "Impossibile connettersi" }, "error": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "flow_title": "{host}", "step": { diff --git a/homeassistant/components/asuswrt/translations/ca.json b/homeassistant/components/asuswrt/translations/ca.json index d034aefb407..9149cd1ae7a 100644 --- a/homeassistant/components/asuswrt/translations/ca.json +++ b/homeassistant/components/asuswrt/translations/ca.json @@ -18,7 +18,7 @@ "mode": "Mode", "name": "Nom", "password": "Contrasenya", - "port": "Port", + "port": "Port (deixa-ho buit pel predeterminat del protocol)", "protocol": "Protocol de comunicacions a utilitzar", "ssh_key": "Ruta al fitxer de claus SSH (en lloc de la contrasenya)", "username": "Nom d'usuari" diff --git a/homeassistant/components/asuswrt/translations/de.json b/homeassistant/components/asuswrt/translations/de.json index f5e41e09ac2..af4beb984a4 100644 --- a/homeassistant/components/asuswrt/translations/de.json +++ b/homeassistant/components/asuswrt/translations/de.json @@ -18,7 +18,7 @@ "mode": "Modus", "name": "Name", "password": "Passwort", - "port": "Port", + "port": "Port (leer lassen f\u00fcr Protokollstandard)", "protocol": "Zu verwendendes Kommunikationsprotokoll", "ssh_key": "Pfad zu deiner SSH-Schl\u00fcsseldatei (anstelle des Passworts)", "username": "Benutzername" diff --git a/homeassistant/components/asuswrt/translations/et.json b/homeassistant/components/asuswrt/translations/et.json index 61e8b1a8d4f..7b7a0869061 100644 --- a/homeassistant/components/asuswrt/translations/et.json +++ b/homeassistant/components/asuswrt/translations/et.json @@ -18,7 +18,7 @@ "mode": "Re\u017eiim", "name": "Nimi", "password": "Salas\u00f5na", - "port": "Port", + "port": "Port (vaikepordi kasutamiseks j\u00e4ta t\u00fchjaks)", "protocol": "Kasutatav sideprotokoll", "ssh_key": "Rada SSH v\u00f5tmefailini (parooli asemel)", "username": "Kasutajanimi" diff --git a/homeassistant/components/asuswrt/translations/fr.json b/homeassistant/components/asuswrt/translations/fr.json index 0d53f3f24cf..5c8882d5813 100644 --- a/homeassistant/components/asuswrt/translations/fr.json +++ b/homeassistant/components/asuswrt/translations/fr.json @@ -18,7 +18,7 @@ "mode": "Mode", "name": "Nom", "password": "Mot de passe", - "port": "Port", + "port": "Port (laisser vide pour utiliser la valeur par d\u00e9faut du protocole)", "protocol": "Protocole de communication \u00e0 utiliser", "ssh_key": "Chemin d'acc\u00e8s \u00e0 votre fichier de cl\u00e9s SSH (au lieu du mot de passe)", "username": "Nom d'utilisateur" diff --git a/homeassistant/components/asuswrt/translations/he.json b/homeassistant/components/asuswrt/translations/he.json index 2d2cebaa7e3..867cc6e4f5c 100644 --- a/homeassistant/components/asuswrt/translations/he.json +++ b/homeassistant/components/asuswrt/translations/he.json @@ -18,7 +18,7 @@ "mode": "\u05de\u05e6\u05d1", "name": "\u05e9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "port": "\u05e4\u05ea\u05d7\u05d4", + "port": "\u05e4\u05ea\u05d7\u05d4 (\u05e8\u05d9\u05e7 \u05e2\u05d1\u05d5\u05e8 \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc)", "ssh_key": "\u05e0\u05ea\u05d9\u05d1 \u05dc\u05e7\u05d5\u05d1\u05e5 \u05d4\u05de\u05e4\u05ea\u05d7 \u05e9\u05dc SSH (\u05d1\u05de\u05e7\u05d5\u05dd \u05dc\u05e1\u05d9\u05e1\u05de\u05d4)", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" } diff --git a/homeassistant/components/asuswrt/translations/hu.json b/homeassistant/components/asuswrt/translations/hu.json index ff64372f1b0..d8133061380 100644 --- a/homeassistant/components/asuswrt/translations/hu.json +++ b/homeassistant/components/asuswrt/translations/hu.json @@ -16,9 +16,9 @@ "data": { "host": "C\u00edm", "mode": "M\u00f3d", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", - "port": "Port", + "port": "Port (hagyja \u00fcresen az alap\u00e9rtelmezetthez)", "protocol": "Haszn\u00e1lhat\u00f3 kommunik\u00e1ci\u00f3s protokoll", "ssh_key": "Az SSH kulcsf\u00e1jl el\u00e9r\u00e9si \u00fatja (jelsz\u00f3 helyett)", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" diff --git a/homeassistant/components/asuswrt/translations/id.json b/homeassistant/components/asuswrt/translations/id.json index aa4eebd1f86..83ade7b6462 100644 --- a/homeassistant/components/asuswrt/translations/id.json +++ b/homeassistant/components/asuswrt/translations/id.json @@ -18,7 +18,7 @@ "mode": "Mode", "name": "Nama", "password": "Kata Sandi", - "port": "Port", + "port": "Port (Biarkan kosong untuk nilai default)", "protocol": "Protokol komunikasi yang akan digunakan", "ssh_key": "Jalur ke file kunci SSH Anda (bukan kata sandi)", "username": "Nama Pengguna" diff --git a/homeassistant/components/asuswrt/translations/it.json b/homeassistant/components/asuswrt/translations/it.json index 824db386176..0cf06679d83 100644 --- a/homeassistant/components/asuswrt/translations/it.json +++ b/homeassistant/components/asuswrt/translations/it.json @@ -7,7 +7,7 @@ "cannot_connect": "Impossibile connettersi", "invalid_host": "Nome host o indirizzo IP non valido", "pwd_and_ssh": "Fornire solo la password o il file della chiave SSH", - "pwd_or_ssh": "Si prega di fornire la password o il file della chiave SSH", + "pwd_or_ssh": "Fornisci la password o il file della chiave SSH", "ssh_not_file": "File chiave SSH non trovato", "unknown": "Errore imprevisto" }, @@ -18,7 +18,7 @@ "mode": "Modalit\u00e0", "name": "Nome", "password": "Password", - "port": "Porta", + "port": "Porta (lascia vuoto per impostazione predefinita del protocollo)", "protocol": "Protocollo di comunicazione da utilizzare", "ssh_key": "Percorso del file della chiave SSH (invece della password)", "username": "Nome utente" diff --git a/homeassistant/components/asuswrt/translations/nl.json b/homeassistant/components/asuswrt/translations/nl.json index f6f347f771f..e6db5fae621 100644 --- a/homeassistant/components/asuswrt/translations/nl.json +++ b/homeassistant/components/asuswrt/translations/nl.json @@ -18,7 +18,7 @@ "mode": "Mode", "name": "Naam", "password": "Wachtwoord", - "port": "Poort", + "port": "Poort (leeg laten voor protocol standaard)", "protocol": "Te gebruiken communicatieprotocol", "ssh_key": "Pad naar uw SSH-sleutelbestand (in plaats van wachtwoord)", "username": "Gebruikersnaam" diff --git a/homeassistant/components/asuswrt/translations/no.json b/homeassistant/components/asuswrt/translations/no.json index c1e5fd5d99a..84ece53ed42 100644 --- a/homeassistant/components/asuswrt/translations/no.json +++ b/homeassistant/components/asuswrt/translations/no.json @@ -18,7 +18,7 @@ "mode": "Modus", "name": "Navn", "password": "Passord", - "port": "Port", + "port": "Port (la st\u00e5 tomt for protokollstandard)", "protocol": "Kommunikasjonsprotokoll som skal brukes", "ssh_key": "Bane til SSH-n\u00f8kkelfilen (i stedet for passord)", "username": "Brukernavn" diff --git a/homeassistant/components/asuswrt/translations/pl.json b/homeassistant/components/asuswrt/translations/pl.json index 9fd5d00b1c4..76d8f30d950 100644 --- a/homeassistant/components/asuswrt/translations/pl.json +++ b/homeassistant/components/asuswrt/translations/pl.json @@ -18,7 +18,7 @@ "mode": "Tryb", "name": "Nazwa", "password": "Has\u0142o", - "port": "Port", + "port": "Port (pozostaw puste dla domy\u015blnego protoko\u0142u)", "protocol": "Wybierz protok\u00f3\u0142 komunikacyjny", "ssh_key": "\u015acie\u017cka do pliku z kluczem SSH (zamiast has\u0142a)", "username": "Nazwa u\u017cytkownika" diff --git a/homeassistant/components/asuswrt/translations/pt-BR.json b/homeassistant/components/asuswrt/translations/pt-BR.json index 06982ade622..88999128895 100644 --- a/homeassistant/components/asuswrt/translations/pt-BR.json +++ b/homeassistant/components/asuswrt/translations/pt-BR.json @@ -18,7 +18,7 @@ "mode": "Modo", "name": "Nome", "password": "Senha", - "port": "Porta", + "port": "Porta (deixe em branco para o protocolo padr\u00e3o)", "protocol": "Protocolo de comunica\u00e7\u00e3o a ser usado", "ssh_key": "Caminho para seu arquivo de chave SSH (em vez de senha)", "username": "Usu\u00e1rio" diff --git a/homeassistant/components/asuswrt/translations/ru.json b/homeassistant/components/asuswrt/translations/ru.json index 35254821f23..8edc9786f5b 100644 --- a/homeassistant/components/asuswrt/translations/ru.json +++ b/homeassistant/components/asuswrt/translations/ru.json @@ -18,7 +18,7 @@ "mode": "\u0420\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "port": "\u041f\u043e\u0440\u0442", + "port": "\u041f\u043e\u0440\u0442 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)", "protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0441\u0432\u044f\u0437\u0438", "ssh_key": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u043a\u043b\u044e\u0447\u0435\u0439 SSH (\u0432\u043c\u0435\u0441\u0442\u043e \u043f\u0430\u0440\u043e\u043b\u044f)", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" diff --git a/homeassistant/components/asuswrt/translations/tr.json b/homeassistant/components/asuswrt/translations/tr.json index 9b7870a3fba..3225c78b40a 100644 --- a/homeassistant/components/asuswrt/translations/tr.json +++ b/homeassistant/components/asuswrt/translations/tr.json @@ -18,7 +18,7 @@ "mode": "Mod", "name": "Ad", "password": "Parola", - "port": "Port", + "port": "Port (protokol varsay\u0131lan\u0131 i\u00e7in bo\u015f b\u0131rak\u0131n)", "protocol": "Kullan\u0131lacak ileti\u015fim protokol\u00fc", "ssh_key": "SSH anahtar dosyan\u0131z\u0131n yolu (\u015fifre yerine)", "username": "Kullan\u0131c\u0131 Ad\u0131" diff --git a/homeassistant/components/asuswrt/translations/zh-Hant.json b/homeassistant/components/asuswrt/translations/zh-Hant.json index d0997e495c5..17c5cd698cd 100644 --- a/homeassistant/components/asuswrt/translations/zh-Hant.json +++ b/homeassistant/components/asuswrt/translations/zh-Hant.json @@ -18,7 +18,7 @@ "mode": "\u6a21\u5f0f", "name": "\u540d\u7a31", "password": "\u5bc6\u78bc", - "port": "\u901a\u8a0a\u57e0", + "port": "\u901a\u8a0a\u57e0 (\u4fdd\u6301\u7a7a\u767d\u4f7f\u7528\u9810\u8a2d\u5354\u5b9a)", "protocol": "\u4f7f\u7528\u901a\u8a0a\u606f\u5354\u5b9a", "ssh_key": "SSH \u91d1\u9470\u6a94\u6848\u8def\u5f91\uff08\u975e\u5bc6\u78bc\uff09", "username": "\u4f7f\u7528\u8005\u540d\u7a31" diff --git a/homeassistant/components/aurora/translations/hu.json b/homeassistant/components/aurora/translations/hu.json index 292ed552235..cbca495254d 100644 --- a/homeassistant/components/aurora/translations/hu.json +++ b/homeassistant/components/aurora/translations/hu.json @@ -8,7 +8,7 @@ "data": { "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" } } } @@ -17,7 +17,7 @@ "step": { "init": { "data": { - "threshold": "K\u00fcsz\u00f6b (%)" + "threshold": "K\u00fcsz\u00f6b\u00e9rt\u00e9k (%)" } } } diff --git a/homeassistant/components/auth/translations/fr.json b/homeassistant/components/auth/translations/fr.json index cf0a1888495..79b467d0255 100644 --- a/homeassistant/components/auth/translations/fr.json +++ b/homeassistant/components/auth/translations/fr.json @@ -5,7 +5,7 @@ "no_available_service": "Aucun service de notification disponible." }, "error": { - "invalid_code": "Code invalide. Veuillez essayer \u00e0 nouveau." + "invalid_code": "Code non valide, veuillez r\u00e9essayer." }, "step": { "init": { @@ -21,7 +21,7 @@ }, "totp": { "error": { - "invalid_code": "Code invalide. Veuillez essayez \u00e0 nouveau. Si cette erreur persiste, assurez-vous que l'horloge de votre syst\u00e8me Home Assistant est correcte." + "invalid_code": "Code non valide, veuillez r\u00e9essayer. Si cette erreur persiste, assurez-vous que l'heure de votre syst\u00e8me Home Assistant est correcte." }, "step": { "init": { diff --git a/homeassistant/components/axis/translations/hu.json b/homeassistant/components/axis/translations/hu.json index cb2f9a17c93..c9121ed2f54 100644 --- a/homeassistant/components/axis/translations/hu.json +++ b/homeassistant/components/axis/translations/hu.json @@ -7,7 +7,7 @@ }, "error": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" }, diff --git a/homeassistant/components/azure_devops/translations/it.json b/homeassistant/components/azure_devops/translations/it.json index 232264d1026..bd14e7926cd 100644 --- a/homeassistant/components/azure_devops/translations/it.json +++ b/homeassistant/components/azure_devops/translations/it.json @@ -21,10 +21,10 @@ "user": { "data": { "organization": "Organizzazione", - "personal_access_token": "Token di Accesso Personale (PAT)", + "personal_access_token": "Token di accesso personale (PAT)", "project": "Progetto" }, - "description": "Configura un'istanza di DevOps di Azure per accedere al progetto. Un Token di Accesso Personale (PAT) \u00e8 richiesto solo per un progetto privato.", + "description": "Configura un'istanza di DevOps di Azure per accedere al progetto. Un token di accesso personale (PAT) \u00e8 richiesto solo per un progetto privato.", "title": "Aggiungere un progetto Azure DevOps" } } diff --git a/homeassistant/components/azure_event_hub/translations/de.json b/homeassistant/components/azure_event_hub/translations/de.json index c1552371f76..b72ef47ab34 100644 --- a/homeassistant/components/azure_event_hub/translations/de.json +++ b/homeassistant/components/azure_event_hub/translations/de.json @@ -4,7 +4,7 @@ "already_configured": "Der Dienst ist bereits konfiguriert", "cannot_connect": "Die Verbindung mit den Anmeldeinformationen aus der configuration.yaml ist fehlgeschlagen, bitte entferne diese aus der yaml und verwende den Konfigurationsfluss.", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", - "unknown": "Die Verbindung mit den Anmeldeinformationen aus der configuration.yaml ist mit einem unbekannten Fehler fehlgeschlagen. Bitte entferne diese aus der yaml und verwende den Konfigurationsfluss." + "unknown": "Die Verbindung mit den Anmeldeinformationen aus der configuration.yaml ist mit einem unbekannten Fehler fehlgeschlagen. Bitte entferne diese aus der yaml und verwende den Konfigurationsfluss." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/binary_sensor/translations/cs.json b/homeassistant/components/binary_sensor/translations/cs.json index fb19cc00fda..ced02ffc326 100644 --- a/homeassistant/components/binary_sensor/translations/cs.json +++ b/homeassistant/components/binary_sensor/translations/cs.json @@ -112,6 +112,10 @@ "off": "Nenab\u00edj\u00ed se", "on": "Nab\u00edjen\u00ed" }, + "carbon_monoxide": { + "off": "\u017d\u00e1dn\u00fd plyn", + "on": "Zji\u0161t\u011bn plyn" + }, "cold": { "off": "Norm\u00e1ln\u00ed", "on": "Studen\u00e9" diff --git a/homeassistant/components/binary_sensor/translations/he.json b/homeassistant/components/binary_sensor/translations/he.json index 293a4f3f264..5f0e14ccac1 100644 --- a/homeassistant/components/binary_sensor/translations/he.json +++ b/homeassistant/components/binary_sensor/translations/he.json @@ -151,11 +151,11 @@ "on": "\u05de\u05d7\u05d5\u05d1\u05e8" }, "door": { - "off": "\u05e1\u05d2\u05d5\u05e8", + "off": "\u05e0\u05e1\u05d2\u05e8", "on": "\u05e4\u05ea\u05d5\u05d7" }, "garage_door": { - "off": "\u05e1\u05d2\u05d5\u05e8", + "off": "\u05e0\u05e1\u05d2\u05e8", "on": "\u05e4\u05ea\u05d5\u05d7" }, "gas": { @@ -191,7 +191,7 @@ "on": "\u05d6\u05d5\u05d4\u05d4" }, "opening": { - "off": "\u05e1\u05d2\u05d5\u05e8", + "off": "\u05e0\u05e1\u05d2\u05e8", "on": "\u05e4\u05ea\u05d5\u05d7" }, "plug": { @@ -231,7 +231,7 @@ "on": "\u05d6\u05d5\u05d4\u05d4" }, "window": { - "off": "\u05e1\u05d2\u05d5\u05e8", + "off": "\u05e0\u05e1\u05d2\u05e8", "on": "\u05e4\u05ea\u05d5\u05d7" } }, diff --git a/homeassistant/components/binary_sensor/translations/pt-BR.json b/homeassistant/components/binary_sensor/translations/pt-BR.json index d858dec6664..5b1ba2a1abf 100644 --- a/homeassistant/components/binary_sensor/translations/pt-BR.json +++ b/homeassistant/components/binary_sensor/translations/pt-BR.json @@ -179,7 +179,7 @@ "on": "Molhado" }, "motion": { - "off": "Sem movimento", + "off": "N\u00e3o detectado", "on": "Detectado" }, "moving": { diff --git a/homeassistant/components/binary_sensor/translations/zh-Hant.json b/homeassistant/components/binary_sensor/translations/zh-Hant.json index a0adaaab083..417ca652cb5 100644 --- a/homeassistant/components/binary_sensor/translations/zh-Hant.json +++ b/homeassistant/components/binary_sensor/translations/zh-Hant.json @@ -203,7 +203,7 @@ "on": "\u5728\u5bb6" }, "problem": { - "off": "\u78ba\u5b9a", + "off": "\u6b63\u5e38", "on": "\u7570\u5e38" }, "running": { diff --git a/homeassistant/components/blebox/translations/it.json b/homeassistant/components/blebox/translations/it.json index 6d377840e90..86025ffdeee 100644 --- a/homeassistant/components/blebox/translations/it.json +++ b/homeassistant/components/blebox/translations/it.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "Impossibile connettersi", "unknown": "Errore imprevisto", - "unsupported_version": "Il dispositivo BleBox ha un firmware obsoleto. Si prega di aggiornarlo prima." + "unsupported_version": "Il dispositivo BleBox ha un firmware obsoleto. Aggiornalo prima." }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/braviatv/translations/ca.json b/homeassistant/components/braviatv/translations/ca.json index 94fe36dcddc..b8616f5e8ba 100644 --- a/homeassistant/components/braviatv/translations/ca.json +++ b/homeassistant/components/braviatv/translations/ca.json @@ -21,7 +21,7 @@ "data": { "host": "Amfitri\u00f3" }, - "description": "Configura la integraci\u00f3 de televisor Sony Bravia. Si tens problemes durant la configuraci\u00f3, v\u00e9s a: https://www.home-assistant.io/integrations/braviatv\n\nAssegura't que el televisor estigui engegat.", + "description": "Assegura't que el televisor est\u00e0 engegat abans de configurar-lo.", "title": "Televisor Sony Bravia" } } diff --git a/homeassistant/components/braviatv/translations/de.json b/homeassistant/components/braviatv/translations/de.json index cca5c5aa47f..55ddbec464e 100644 --- a/homeassistant/components/braviatv/translations/de.json +++ b/homeassistant/components/braviatv/translations/de.json @@ -21,7 +21,7 @@ "data": { "host": "Host" }, - "description": "Richte die Sony Bravia TV-Integration ein. Wenn du Probleme mit der Konfiguration hast, gehe zu: https://www.home-assistant.io/integrations/braviatv \n\nStelle sicher, dass dein Fernseher eingeschaltet ist.", + "description": "Stelle sicher, dass dein Fernseher eingeschaltet ist, bevor du versuchst, ihn einzurichten.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/en.json b/homeassistant/components/braviatv/translations/en.json index 05ced6af856..9cc2ac23d17 100644 --- a/homeassistant/components/braviatv/translations/en.json +++ b/homeassistant/components/braviatv/translations/en.json @@ -21,7 +21,7 @@ "data": { "host": "Host" }, - "description": "Set up Sony Bravia TV integration. If you have problems with configuration go to: https://www.home-assistant.io/integrations/braviatv \n\nEnsure that your TV is turned on.", + "description": "Ensure that your TV is turned on before trying to set it up.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/et.json b/homeassistant/components/braviatv/translations/et.json index 6930186aeba..c63e3635185 100644 --- a/homeassistant/components/braviatv/translations/et.json +++ b/homeassistant/components/braviatv/translations/et.json @@ -21,7 +21,7 @@ "data": { "host": "" }, - "description": "Seadista Sony Bravia TV sidumine. Kuion probleeme seadetega mine: https://www.home-assistant.io/integrations/braviatv \n\nVeendu, et teler on sisse l\u00fclitatud.", + "description": "Enne teleri seadistamist veendu, et see oleks sisse l\u00fclitatud.", "title": "" } } diff --git a/homeassistant/components/braviatv/translations/fr.json b/homeassistant/components/braviatv/translations/fr.json index d609f1a2fa1..f85aea705fb 100644 --- a/homeassistant/components/braviatv/translations/fr.json +++ b/homeassistant/components/braviatv/translations/fr.json @@ -21,7 +21,7 @@ "data": { "host": "H\u00f4te" }, - "description": "Configurez l'int\u00e9gration du t\u00e9l\u00e9viseur Sony Bravia. Si vous rencontrez des probl\u00e8mes de configuration, rendez-vous sur: https://www.home-assistant.io/integrations/braviatv \n\n Assurez-vous que votre t\u00e9l\u00e9viseur est allum\u00e9.", + "description": "Assurez-vous que votre t\u00e9l\u00e9viseur est allum\u00e9 avant d'essayer de le configurer.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/hu.json b/homeassistant/components/braviatv/translations/hu.json index 00e88955c81..554e74c52b1 100644 --- a/homeassistant/components/braviatv/translations/hu.json +++ b/homeassistant/components/braviatv/translations/hu.json @@ -21,7 +21,7 @@ "data": { "host": "C\u00edm" }, - "description": "\u00c1ll\u00edtsa be a Sony Bravia TV integr\u00e1ci\u00f3t. Ha probl\u00e9m\u00e1i vannak a konfigur\u00e1ci\u00f3val, l\u00e1togasson el a k\u00f6vetkez\u0151 oldalra: https://www.home-assistant.io/integrations/braviatv \n\n Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a TV be van kapcsolva.", + "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a TV k\u00e9sz\u00fcl\u00e9k be van kapcsolva a be\u00e1ll\u00edt\u00e1s el\u0151tt.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/id.json b/homeassistant/components/braviatv/translations/id.json index def84dacdbb..cb7d5396f7a 100644 --- a/homeassistant/components/braviatv/translations/id.json +++ b/homeassistant/components/braviatv/translations/id.json @@ -21,7 +21,7 @@ "data": { "host": "Host" }, - "description": "Siapkan integrasi TV Sony Bravia. Jika Anda memiliki masalah dengan konfigurasi, buka: https://www.home-assistant.io/integrations/braviatv \n\nPastikan TV Anda dinyalakan.", + "description": "Pastikan TV Anda dinyalakan sebelum menyiapkan.", "title": "TV Sony Bravia" } } diff --git a/homeassistant/components/braviatv/translations/it.json b/homeassistant/components/braviatv/translations/it.json index bbd02157496..7ab149fa6c7 100644 --- a/homeassistant/components/braviatv/translations/it.json +++ b/homeassistant/components/braviatv/translations/it.json @@ -21,7 +21,7 @@ "data": { "host": "Host" }, - "description": "Configura l'integrazione TV di Sony Bravia. In caso di problemi con la configurazione visita: https://www.home-assistant.io/integrations/braviatv\n\nAssicurati che il televisore sia acceso.", + "description": "Assicurati che la tua TV sia accesa prima di provare a configurarla.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/nl.json b/homeassistant/components/braviatv/translations/nl.json index 5354f5761ec..133c5277045 100644 --- a/homeassistant/components/braviatv/translations/nl.json +++ b/homeassistant/components/braviatv/translations/nl.json @@ -21,7 +21,7 @@ "data": { "host": "Host" }, - "description": "Stel Sony Bravia TV-integratie in. Als je problemen hebt met de configuratie ga dan naar: https://www.home-assistant.io/integrations/braviatv \n\nZorg ervoor dat uw tv is ingeschakeld.", + "description": "Zorg ervoor dat uw TV aan staat voordat u hem probeert in te stellen.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/no.json b/homeassistant/components/braviatv/translations/no.json index 0c960a850e2..6465db8d3c0 100644 --- a/homeassistant/components/braviatv/translations/no.json +++ b/homeassistant/components/braviatv/translations/no.json @@ -21,7 +21,7 @@ "data": { "host": "Vert" }, - "description": "Sett opp Sony Bravia TV-integrasjon. Hvis du har problemer med konfigurasjonen, g\u00e5 til: [https://www.home-assistant.io/integrations/braviatv](https://www.home-assistant.io/integrations/braviatv)\n\n Forsikre deg om at TV-en er sl\u00e5tt p\u00e5.", + "description": "S\u00f8rg for at TV-en er sl\u00e5tt p\u00e5 f\u00f8r du pr\u00f8ver \u00e5 sette den opp.", "title": "" } } diff --git a/homeassistant/components/braviatv/translations/pl.json b/homeassistant/components/braviatv/translations/pl.json index 1aa5d7cb58a..354962a62d9 100644 --- a/homeassistant/components/braviatv/translations/pl.json +++ b/homeassistant/components/braviatv/translations/pl.json @@ -21,7 +21,7 @@ "data": { "host": "Nazwa hosta lub adres IP" }, - "description": "Konfiguracja integracji telewizora Sony Bravia. Je\u015bli masz problemy z konfiguracj\u0105, przejd\u017a do strony: https://www.home-assistant.io/integrations/braviatv\n\nUpewnij si\u0119, \u017ce telewizor jest w\u0142\u0105czony.", + "description": "Upewnij si\u0119, \u017ce telewizor jest w\u0142\u0105czony, zanim spr\u00f3bujesz go skonfigurowa\u0107.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/pt-BR.json b/homeassistant/components/braviatv/translations/pt-BR.json index bd6d47af018..a784e30d84c 100644 --- a/homeassistant/components/braviatv/translations/pt-BR.json +++ b/homeassistant/components/braviatv/translations/pt-BR.json @@ -21,7 +21,7 @@ "data": { "host": "Nome do host" }, - "description": "Configure a integra\u00e7\u00e3o do Sony Bravia TV. Se voc\u00ea tiver problemas com a configura\u00e7\u00e3o, acesse: https://www.home-assistant.io/integrations/braviatv \n\n Verifique se a sua TV est\u00e1 ligada.", + "description": "Certifique-se de que sua TV esteja ligada antes de tentar configur\u00e1-la.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/ru.json b/homeassistant/components/braviatv/translations/ru.json index add32dd0d79..3aeb83f7686 100644 --- a/homeassistant/components/braviatv/translations/ru.json +++ b/homeassistant/components/braviatv/translations/ru.json @@ -21,7 +21,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438:\nhttps://www.home-assistant.io/integrations/braviatv", + "description": "\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d.", "title": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Sony Bravia" } } diff --git a/homeassistant/components/braviatv/translations/tr.json b/homeassistant/components/braviatv/translations/tr.json index 6d0f82e29a4..b6df4a7999a 100644 --- a/homeassistant/components/braviatv/translations/tr.json +++ b/homeassistant/components/braviatv/translations/tr.json @@ -21,7 +21,7 @@ "data": { "host": "Ana Bilgisayar" }, - "description": "Sony Bravia TV entegrasyonunu ayarlay\u0131n. Yap\u0131land\u0131rmayla ilgili sorunlar\u0131n\u0131z varsa \u015fu adrese gidin: https://www.home-assistant.io/integrations/braviatv \n\n TV'nizin a\u00e7\u0131k oldu\u011fundan emin olun.", + "description": "Kurmaya \u00e7al\u0131\u015fmadan \u00f6nce TV'nizin a\u00e7\u0131k oldu\u011fundan emin olun.", "title": "Sony Bravia TV" } } diff --git a/homeassistant/components/braviatv/translations/zh-Hant.json b/homeassistant/components/braviatv/translations/zh-Hant.json index f736b601a74..35ef6ef2e4f 100644 --- a/homeassistant/components/braviatv/translations/zh-Hant.json +++ b/homeassistant/components/braviatv/translations/zh-Hant.json @@ -21,7 +21,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef" }, - "description": "\u8a2d\u5b9a Sony Bravia \u96fb\u8996\u6574\u5408\u3002\u5047\u5982\u65bc\u8a2d\u5b9a\u904e\u7a0b\u4e2d\u906d\u9047\u56f0\u7136\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/braviatv \n\n\u78ba\u5b9a\u96fb\u8996\u5df2\u7d93\u958b\u555f\u3002", + "description": "\u65bc\u8a2d\u5b9a\u524d\u78ba\u5b9a\u96fb\u8996\u5df2\u7d93\u958b\u555f\u3002", "title": "Sony Bravia \u96fb\u8996" } } diff --git a/homeassistant/components/broadlink/translations/hu.json b/homeassistant/components/broadlink/translations/hu.json index 0bab0c1752f..70fd71b5897 100644 --- a/homeassistant/components/broadlink/translations/hu.json +++ b/homeassistant/components/broadlink/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_host": "\u00c9rv\u00e9nytelen hosztn\u00e9v vagy IP-c\u00edm", "not_supported": "Az eszk\u00f6z nem t\u00e1mogatott", @@ -20,7 +20,7 @@ }, "finish": { "data": { - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "title": "V\u00e1lasszonegy nevet az eszk\u00f6znek" }, diff --git a/homeassistant/components/brother/translations/ca.json b/homeassistant/components/brother/translations/ca.json index 689495478bb..d9b664dfe82 100644 --- a/homeassistant/components/brother/translations/ca.json +++ b/homeassistant/components/brother/translations/ca.json @@ -15,14 +15,13 @@ "data": { "host": "Amfitri\u00f3", "type": "Tipus d'impressora" - }, - "description": "Configura la integraci\u00f3 d'impressora Brother. Si tens problemes amb la configuraci\u00f3, visita: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Tipus d'impressora" }, - "description": "Vols afegir la impressora Brother {model} amb n\u00famero de s\u00e8rie `{serial_number}` a Home Assistant?", + "description": "Vols afegir la impressora {model} amb n\u00famero de s\u00e8rie `{serial_number}` a Home Assistant?", "title": "Impressora Brother descoberta" } } diff --git a/homeassistant/components/brother/translations/da.json b/homeassistant/components/brother/translations/da.json index fc9cd0079d8..7dcd8188085 100644 --- a/homeassistant/components/brother/translations/da.json +++ b/homeassistant/components/brother/translations/da.json @@ -14,8 +14,7 @@ "data": { "host": "Printerens v\u00e6rtsnavn eller IP-adresse", "type": "Type af printer" - }, - "description": "Konfigurer Brother-printerintegration. Hvis du har problemer med konfiguration, kan du g\u00e5 til: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/de.json b/homeassistant/components/brother/translations/de.json index 8126a04f21d..e8056b0505a 100644 --- a/homeassistant/components/brother/translations/de.json +++ b/homeassistant/components/brother/translations/de.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Typ des Druckers" - }, - "description": "Einrichten der Brother-Drucker-Integration. Wenn Du Probleme mit der Konfiguration hast, gehe zu: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Typ des Druckers" }, - "description": "M\u00f6chtest du den Brother Drucker {model} mit der Seriennummer `{serial_number}` zum Home Assistant hinzuf\u00fcgen?", + "description": "M\u00f6chtest du den Drucker {model} mit der Seriennummer ` {serial_number} ` zu Home Assistant hinzuf\u00fcgen?", "title": "Brother-Drucker entdeckt" } } diff --git a/homeassistant/components/brother/translations/el.json b/homeassistant/components/brother/translations/el.json index f8a27353a6a..e8c9e4ea8e7 100644 --- a/homeassistant/components/brother/translations/el.json +++ b/homeassistant/components/brother/translations/el.json @@ -15,8 +15,7 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03ba\u03c4\u03c5\u03c0\u03c9\u03c4\u03ae" - }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b5\u03ba\u03c4\u03c5\u03c0\u03c9\u03c4\u03ae Brother. \u0395\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/en.json b/homeassistant/components/brother/translations/en.json index 8ec9d84c7fb..997cdb1ea83 100644 --- a/homeassistant/components/brother/translations/en.json +++ b/homeassistant/components/brother/translations/en.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Type of the printer" - }, - "description": "Set up Brother printer integration. If you have problems with configuration go to: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Type of the printer" }, - "description": "Do you want to add the Brother Printer {model} with serial number `{serial_number}` to Home Assistant?", + "description": "Do you want to add the printer {model} with serial number `{serial_number}` to Home Assistant?", "title": "Discovered Brother Printer" } } diff --git a/homeassistant/components/brother/translations/es-419.json b/homeassistant/components/brother/translations/es-419.json index 33d06705017..21c57cab021 100644 --- a/homeassistant/components/brother/translations/es-419.json +++ b/homeassistant/components/brother/translations/es-419.json @@ -14,8 +14,7 @@ "data": { "host": "Nombre de host de la impresora o direcci\u00f3n IP", "type": "Tipo de impresora" - }, - "description": "Configure la integraci\u00f3n de la impresora Brother. Si tiene problemas con la configuraci\u00f3n, vaya a: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/es.json b/homeassistant/components/brother/translations/es.json index 43b3cefdded..bc6aa749445 100644 --- a/homeassistant/components/brother/translations/es.json +++ b/homeassistant/components/brother/translations/es.json @@ -15,8 +15,7 @@ "data": { "host": "Host", "type": "Tipo de impresora" - }, - "description": "Configura la integraci\u00f3n de impresoras Brother. Si tienes problemas con la configuraci\u00f3n, ve a: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/et.json b/homeassistant/components/brother/translations/et.json index 1115e5cb3ca..99928a5d1ab 100644 --- a/homeassistant/components/brother/translations/et.json +++ b/homeassistant/components/brother/translations/et.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Printeri t\u00fc\u00fcp" - }, - "description": "Seadista Brotheri printeri sidumine. Kui seadistamisega on probleeme mine aadressile https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Printeri t\u00fc\u00fcp" }, - "description": "Kas soovite lisada Home Assistanti Brotheri printeri {model} seerianumbriga \" {serial_number} \"?", + "description": "Kas lisada Home Assistantile printer {model} seerianumbriga ` {serial_number} `?", "title": "Avastatud Brotheri printer" } } diff --git a/homeassistant/components/brother/translations/fr.json b/homeassistant/components/brother/translations/fr.json index 851d46f1772..4d248d05102 100644 --- a/homeassistant/components/brother/translations/fr.json +++ b/homeassistant/components/brother/translations/fr.json @@ -15,14 +15,13 @@ "data": { "host": "H\u00f4te", "type": "Type d'imprimante" - }, - "description": "Configurez l'int\u00e9gration de l'imprimante Brother. Si vous avez des probl\u00e8mes avec la configuration, allez \u00e0 : https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Type d'imprimante" }, - "description": "Voulez-vous ajouter l'imprimante Brother {model} avec le num\u00e9ro de s\u00e9rie `{serial_number}` \u00e0 Home Assistant ?", + "description": "Voulez-vous ajouter l'imprimante {model} portant le num\u00e9ro de s\u00e9rie `{serial_number}` \u00e0 Home Assistant\u00a0?", "title": "Imprimante Brother d\u00e9couverte" } } diff --git a/homeassistant/components/brother/translations/hu.json b/homeassistant/components/brother/translations/hu.json index f0218dc2647..93c368cf74a 100644 --- a/homeassistant/components/brother/translations/hu.json +++ b/homeassistant/components/brother/translations/hu.json @@ -15,14 +15,13 @@ "data": { "host": "C\u00edm", "type": "A nyomtat\u00f3 t\u00edpusa" - }, - "description": "A Brother nyomtat\u00f3 integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Ha probl\u00e9m\u00e1id vannak a konfigur\u00e1ci\u00f3val, l\u00e1togass el a k\u00f6vetkez\u0151 oldalra: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "A nyomtat\u00f3 t\u00edpusa" }, - "description": "Hozz\u00e1 szeretn\u00e9 adni a {model} Brother nyomtat\u00f3t, amelynek sorsz\u00e1ma: `{serial_number}`, Home Assistanthoz?", + "description": "Hozz\u00e1 szeretn\u00e9 adni a {model} nyomtat\u00f3t, amelynek sorsz\u00e1ma: `{serial_number}`, Home Assistanthoz?", "title": "Felfedezett Brother nyomtat\u00f3" } } diff --git a/homeassistant/components/brother/translations/id.json b/homeassistant/components/brother/translations/id.json index ed02999710e..135274450d3 100644 --- a/homeassistant/components/brother/translations/id.json +++ b/homeassistant/components/brother/translations/id.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Jenis printer" - }, - "description": "Siapkan integrasi printer Brother. Jika Anda memiliki masalah dengan konfigurasi, buka: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Jenis printer" }, - "description": "Ingin menambahkan Printer Brother {model} dengan nomor seri `{serial_number}` ke Home Assistant?", + "description": "Ingin menambahkan printer {model} dengan nomor seri `{serial_number}` ke Home Assistant?", "title": "Perangkat Printer Brother yang Ditemukan" } } diff --git a/homeassistant/components/brother/translations/it.json b/homeassistant/components/brother/translations/it.json index 29059723159..21fd1bf15ab 100644 --- a/homeassistant/components/brother/translations/it.json +++ b/homeassistant/components/brother/translations/it.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Tipo di stampante" - }, - "description": "Configura l'integrazione della stampante Brother. In caso di problemi con la configurazione, visita: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Tipo di stampante" }, - "description": "Vuoi aggiungere la stampante Brother {model} con il numero seriale `{serial_number}` a Home Assistant?", + "description": "Vuoi aggiungere la stampante {model} con il numero seriale `{serial_number}` a Home Assistant?", "title": "Trovata stampante Brother" } } diff --git a/homeassistant/components/brother/translations/ja.json b/homeassistant/components/brother/translations/ja.json index 6c0e1e57767..ce3341ef676 100644 --- a/homeassistant/components/brother/translations/ja.json +++ b/homeassistant/components/brother/translations/ja.json @@ -15,8 +15,7 @@ "data": { "host": "\u30db\u30b9\u30c8", "type": "\u30d7\u30ea\u30f3\u30bf\u30fc\u306e\u7a2e\u985e" - }, - "description": "\u30d6\u30e9\u30b6\u30fc\u793e\u88fd\u30d7\u30ea\u30f3\u30bf\u30fc\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/brother \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/ko.json b/homeassistant/components/brother/translations/ko.json index 2d3b587e475..a69c415f061 100644 --- a/homeassistant/components/brother/translations/ko.json +++ b/homeassistant/components/brother/translations/ko.json @@ -15,8 +15,7 @@ "data": { "host": "\ud638\uc2a4\ud2b8", "type": "\ud504\ub9b0\ud130\uc758 \uc885\ub958" - }, - "description": "\ube0c\ub77c\ub354 \ud504\ub9b0\ud130 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. \uad6c\uc131\uc5d0 \ubb38\uc81c\uac00\uc788\ub294 \uacbd\uc6b0 https://www.home-assistant.io/integrations/brother \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/lb.json b/homeassistant/components/brother/translations/lb.json index 91964ec90ca..0a0d3785274 100644 --- a/homeassistant/components/brother/translations/lb.json +++ b/homeassistant/components/brother/translations/lb.json @@ -15,8 +15,7 @@ "data": { "host": "Host", "type": "Typ vum Printer" - }, - "description": "Brother Printer Integratioun ariichten. Am Fall vun Problemer kuckt op: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/nl.json b/homeassistant/components/brother/translations/nl.json index 6440be77e75..97c506299a7 100644 --- a/homeassistant/components/brother/translations/nl.json +++ b/homeassistant/components/brother/translations/nl.json @@ -15,14 +15,13 @@ "data": { "host": "Host", "type": "Type printer" - }, - "description": "Zet Brother printerintegratie op. Als u problemen heeft met de configuratie ga dan naar: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Type printer" }, - "description": "Wilt u het Brother Printer {model} met serienummer {serial_number}' toevoegen aan Home Assistant?", + "description": "Wilt u de printer {model} met serienummer ` {serial_number} ` toevoegen aan Home Assistant?", "title": "Ontdekte Brother Printer" } } diff --git a/homeassistant/components/brother/translations/no.json b/homeassistant/components/brother/translations/no.json index 9d3618cad62..2f383701288 100644 --- a/homeassistant/components/brother/translations/no.json +++ b/homeassistant/components/brother/translations/no.json @@ -15,14 +15,13 @@ "data": { "host": "Vert", "type": "Skriver type" - }, - "description": "Sett opp Brother skriver integrasjonen. Hvis du har problemer med konfigurasjonen, bes\u00f8k dokumentasjonen her: [https://www.home-assistant.io/integrations/brother](https://www.home-assistant.io/integrations/brother)" + } }, "zeroconf_confirm": { "data": { "type": "Type skriver" }, - "description": "Vil du legge til Brother-skriveren {model} med serienummeret `{serial_number}` til Home Assistant?", + "description": "Vil du legge til skriveren {model} med serienummeret ` {serial_number} ` til Home Assistant?", "title": "Oppdaget Brother Skriver" } } diff --git a/homeassistant/components/brother/translations/pl.json b/homeassistant/components/brother/translations/pl.json index 20031bf94ad..159b0308ab7 100644 --- a/homeassistant/components/brother/translations/pl.json +++ b/homeassistant/components/brother/translations/pl.json @@ -15,14 +15,13 @@ "data": { "host": "Nazwa hosta lub adres IP", "type": "Typ drukarki" - }, - "description": "Konfiguracja integracji drukarek Brother. Je\u015bli masz problemy z konfiguracj\u0105, przejd\u017a na stron\u0119: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Typ drukarki" }, - "description": "Czy chcesz doda\u0107 drukark\u0119 Brother {model} o numerze seryjnym `{serial_number}` do Home Assistanta?", + "description": "Czy chcesz doda\u0107 drukark\u0119 {model} o numerze seryjnym `{serial_number}` do Home Assistanta?", "title": "Wykryto drukark\u0119 Brother" } } diff --git a/homeassistant/components/brother/translations/pt-BR.json b/homeassistant/components/brother/translations/pt-BR.json index 33113d12881..5e938d90edd 100644 --- a/homeassistant/components/brother/translations/pt-BR.json +++ b/homeassistant/components/brother/translations/pt-BR.json @@ -15,14 +15,13 @@ "data": { "host": "Nome do host", "type": "Tipo de impressora" - }, - "description": "Configure a integra\u00e7\u00e3o da impressora Brother. Se voc\u00ea tiver problemas com a configura\u00e7\u00e3o, acesse: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Tipo de impressora" }, - "description": "Deseja adicionar a impressora Brother {model} com n\u00famero de s\u00e9rie ` {serial_number} ` ao Home Assistant?", + "description": "Deseja adicionar a impressora {model} com n\u00famero de s\u00e9rie ` {serial_number} ` ao Home Assistant?", "title": "Impressora Brother descoberta" } } diff --git a/homeassistant/components/brother/translations/ru.json b/homeassistant/components/brother/translations/ru.json index 6fd15e30ac3..a9f6158ccf8 100644 --- a/homeassistant/components/brother/translations/ru.json +++ b/homeassistant/components/brother/translations/ru.json @@ -15,14 +15,13 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "type": "\u0422\u0438\u043f \u043f\u0440\u0438\u043d\u0442\u0435\u0440\u0430" - }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438: https://www.home-assistant.io/integrations/brother." + } }, "zeroconf_confirm": { "data": { "type": "\u0422\u0438\u043f \u043f\u0440\u0438\u043d\u0442\u0435\u0440\u0430" }, - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043d\u0442\u0435\u0440 Brother {model} \u0441 \u0441\u0435\u0440\u0438\u0439\u043d\u044b\u043c \u043d\u043e\u043c\u0435\u0440\u043e\u043c `{serial_number}`?", + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043d\u0442\u0435\u0440 {model} \u0441 \u0441\u0435\u0440\u0438\u0439\u043d\u044b\u043c \u043d\u043e\u043c\u0435\u0440\u043e\u043c `{serial_number}`?", "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043f\u0440\u0438\u043d\u0442\u0435\u0440 Brother" } } diff --git a/homeassistant/components/brother/translations/sl.json b/homeassistant/components/brother/translations/sl.json index 53ce1423819..fcd53a65413 100644 --- a/homeassistant/components/brother/translations/sl.json +++ b/homeassistant/components/brother/translations/sl.json @@ -14,8 +14,7 @@ "data": { "host": "Gostiteljsko ime tiskalnika ali naslov IP", "type": "Vrsta tiskalnika" - }, - "description": "Nastavite integracijo tiskalnika Brother. \u010ce imate te\u017eave s konfiguracijo, pojdite na: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/sv.json b/homeassistant/components/brother/translations/sv.json index 3a049bb92c8..00d29aa3a0a 100644 --- a/homeassistant/components/brother/translations/sv.json +++ b/homeassistant/components/brother/translations/sv.json @@ -14,8 +14,7 @@ "data": { "host": "Skrivarens v\u00e4rdnamn eller IP-adress", "type": "Typ av skrivare" - }, - "description": "St\u00e4ll in Brother-skrivarintegration. Om du har problem med konfigurationen g\u00e5r du till: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/tr.json b/homeassistant/components/brother/translations/tr.json index c989844c6f7..291be1c4138 100644 --- a/homeassistant/components/brother/translations/tr.json +++ b/homeassistant/components/brother/translations/tr.json @@ -15,14 +15,13 @@ "data": { "host": "Sunucu", "type": "Yaz\u0131c\u0131n\u0131n t\u00fcr\u00fc" - }, - "description": "Brother yaz\u0131c\u0131 entegrasyonunu ayarlay\u0131n. Yap\u0131land\u0131rmayla ilgili sorunlar\u0131n\u0131z varsa \u015fu adrese gidin: https://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { "type": "Yaz\u0131c\u0131n\u0131n t\u00fcr\u00fc" }, - "description": "Seri numaras\u0131 ` {serial_number} ` olan Brother Yaz\u0131c\u0131 {model} }'i Home Assistant'a eklemek ister misiniz?", + "description": "Seri numaras\u0131 ` {serial_number} ` olan {model} yaz\u0131c\u0131y\u0131 Home Assistant'a eklemek istiyor musunuz?", "title": "Ke\u015ffedilen Brother Yaz\u0131c\u0131" } } diff --git a/homeassistant/components/brother/translations/uk.json b/homeassistant/components/brother/translations/uk.json index ac5943aa85c..89ce35d988e 100644 --- a/homeassistant/components/brother/translations/uk.json +++ b/homeassistant/components/brother/translations/uk.json @@ -15,8 +15,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "type": "\u0422\u0438\u043f \u043f\u0440\u0438\u043d\u0442\u0435\u0440\u0430" - }, - "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0454\u044e \u043f\u043e \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044e \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457: https://www.home-assistant.io/integrations/brother." + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/brother/translations/zh-Hans.json b/homeassistant/components/brother/translations/zh-Hans.json index 91e0c310dd1..5a0e6f80393 100644 --- a/homeassistant/components/brother/translations/zh-Hans.json +++ b/homeassistant/components/brother/translations/zh-Hans.json @@ -8,9 +8,6 @@ "snmp_error": "SNMP\u670d\u52a1\u5668\u5df2\u5173\u95ed\u6216\u4e0d\u652f\u6301\u6253\u5370\u3002" }, "step": { - "user": { - "description": "\u8bbe\u7f6e Brother \u6253\u5370\u673a\u96c6\u6210\u3002\u5982\u679c\u60a8\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u8bbf\u95ee\uff1ahttps://www.home-assistant.io/integrations/brother" - }, "zeroconf_confirm": { "data": { "type": "\u6253\u5370\u673a\u7c7b\u578b" diff --git a/homeassistant/components/brother/translations/zh-Hant.json b/homeassistant/components/brother/translations/zh-Hant.json index 016d83309f2..1fdeedbb556 100644 --- a/homeassistant/components/brother/translations/zh-Hant.json +++ b/homeassistant/components/brother/translations/zh-Hant.json @@ -15,8 +15,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef", "type": "\u5370\u8868\u6a5f\u985e\u5225" - }, - "description": "\u8a2d\u5b9a Brother \u5370\u8868\u6a5f\u6574\u5408\u3002\u5047\u5982\u9700\u8981\u5354\u52a9\uff0c\u8acb\u53c3\u8003\uff1ahttps://www.home-assistant.io/integrations/brother" + } }, "zeroconf_confirm": { "data": { diff --git a/homeassistant/components/bsblan/translations/hu.json b/homeassistant/components/bsblan/translations/hu.json index f04a0a3af0e..0c02cfb733d 100644 --- a/homeassistant/components/bsblan/translations/hu.json +++ b/homeassistant/components/bsblan/translations/hu.json @@ -17,7 +17,7 @@ "port": "Port", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "\u00c1ll\u00edtsa be a BSB-Lan eszk\u00f6zt az HomeAssistantba val\u00f3 integr\u00e1ci\u00f3hoz.", + "description": "\u00c1ll\u00edtsa be a BSB-Lan eszk\u00f6zt az Home Assistantba val\u00f3 integr\u00e1ci\u00f3hoz.", "title": "Csatlakoz\u00e1s a BSB-Lan eszk\u00f6zh\u00f6z" } } diff --git a/homeassistant/components/cast/translations/cs.json b/homeassistant/components/cast/translations/cs.json index f04465341e3..cfcc3594575 100644 --- a/homeassistant/components/cast/translations/cs.json +++ b/homeassistant/components/cast/translations/cs.json @@ -3,10 +3,42 @@ "abort": { "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, + "error": { + "invalid_known_hosts": "Zn\u00e1m\u00ed hostitel\u00e9 mus\u00ed b\u00fdt seznam hostitel\u016f odd\u011blen\u00fd \u010d\u00e1rkou." + }, "step": { + "config": { + "data": { + "known_hosts": "Zn\u00e1m\u00ed hostitel\u00e9" + }, + "description": "Zn\u00e1m\u00ed hostitel\u00e9 - seznam n\u00e1zv\u016f hostitel\u016f nebo IP adres za\u0159\u00edzen\u00ed odd\u011blen\u00fd \u010d\u00e1rkou, kter\u00fd se pou\u017eije, pokud nefunguje zji\u0161\u0165ov\u00e1n\u00ed pomoc\u00ed mDNS.", + "title": "Konfigurace Google Cast" + }, "confirm": { "description": "Chcete za\u010d\u00edt nastavovat?" } } + }, + "options": { + "error": { + "invalid_known_hosts": "Zn\u00e1m\u00ed hostitel\u00e9 mus\u00ed b\u00fdt seznam hostitel\u016f odd\u011blen\u00fd \u010d\u00e1rkou." + }, + "step": { + "advanced_options": { + "data": { + "ignore_cec": "Ignorovat CEC", + "uuid": "Povolen\u00e9 UUID" + }, + "description": "Allowed UUIDs - \u010d\u00e1rkou odd\u011blen\u00fd seznam UUID za\u0159\u00edzen\u00ed Cast, kter\u00e9 chcete p\u0159idat do aplikace Home Assistant. Pou\u017eijte pouze v p\u0159\u00edpad\u011b, \u017ee nechcete p\u0159idat v\u0161echna dostupn\u00e1 za\u0159\u00edzen\u00ed Cast.\nIgnorovat CEC - \u010c\u00e1rkou odd\u011blen\u00fd seznam za\u0159\u00edzen\u00ed Chromecast, kter\u00e1 maj\u00ed p\u0159i ur\u010dov\u00e1n\u00ed aktivn\u00edho vstupu ignorovat data CEC. Tento \u00fadaj bude p\u0159ed\u00e1n do pychromecast.IGNORE_CEC.", + "title": "Pokro\u010dil\u00e1 konfigurace Google Cast" + }, + "basic_options": { + "data": { + "known_hosts": "Zn\u00e1m\u00ed hostitel\u00e9" + }, + "description": "Zn\u00e1m\u00ed hostitel\u00e9 - seznam n\u00e1zv\u016f hostitel\u016f nebo IP adres za\u0159\u00edzen\u00ed odd\u011blen\u00fd \u010d\u00e1rkou, kter\u00fd se pou\u017eije, pokud nefunguje zji\u0161\u0165ov\u00e1n\u00ed pomoc\u00ed mDNS.", + "title": "Konfigurace Google Cast" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ca.json b/homeassistant/components/climacell/translations/ca.json index fa924ae8272..78d43991e90 100644 --- a/homeassistant/components/climacell/translations/ca.json +++ b/homeassistant/components/climacell/translations/ca.json @@ -25,7 +25,7 @@ "data": { "timestep": "Minuts entre previsions NowCast" }, - "description": "Si decideixes activar l'entitat de predicci\u00f3 \"nowcast\", podr\u00e0s configurar l'interval en minuts entre cada previsi\u00f3. El nombre de previsions proporcionades dep\u00e8n d'aquest interval de minuts.", + "description": "Si decideixes activar l'entitat de previsi\u00f3 `nowcast`, podr\u00e0s configurar l'interval en minuts entre cada previsi\u00f3. El nombre de previsions proporcionades dep\u00e8n d'aquest interval de minuts.", "title": "Actualitzaci\u00f3 d'opcions de ClimaCell" } } diff --git a/homeassistant/components/climacell/translations/de.json b/homeassistant/components/climacell/translations/de.json index eb5f3f73faf..01e9a81647e 100644 --- a/homeassistant/components/climacell/translations/de.json +++ b/homeassistant/components/climacell/translations/de.json @@ -23,7 +23,7 @@ "step": { "init": { "data": { - "timestep": "Minuten zwischen den Kurzvorhersagen" + "timestep": "Minuten zwischen den NowCast Kurzvorhersagen" }, "description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.", "title": "ClimaCell-Optionen aktualisieren" diff --git a/homeassistant/components/climacell/translations/en.json b/homeassistant/components/climacell/translations/en.json index a35be85d5b2..3e5cd436ba8 100644 --- a/homeassistant/components/climacell/translations/en.json +++ b/homeassistant/components/climacell/translations/en.json @@ -1,4 +1,24 @@ { + "config": { + "error": { + "cannot_connect": "Failed to connect", + "invalid_api_key": "Invalid API key", + "rate_limited": "Currently rate limited, please try again later.", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "api_key": "API Key", + "api_version": "API Version", + "latitude": "Latitude", + "longitude": "Longitude", + "name": "Name" + }, + "description": "If Latitude and Longitude are not provided, the default values in the Home Assistant configuration will be used. An entity will be created for each forecast type but only the ones you select will be enabled by default." + } + } + }, "options": { "step": { "init": { @@ -9,5 +29,6 @@ "title": "Update ClimaCell Options" } } - } + }, + "title": "ClimaCell" } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/fr.json b/homeassistant/components/climacell/translations/fr.json index 19b986a3db7..9194073bb5a 100644 --- a/homeassistant/components/climacell/translations/fr.json +++ b/homeassistant/components/climacell/translations/fr.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "timestep": "Min. Entre les pr\u00e9visions NowCast" + "timestep": "Min. entre les pr\u00e9visions NowCast" }, - "description": "Si vous choisissez d'activer l'entit\u00e9 de pr\u00e9vision \u00abnowcast\u00bb, vous pouvez configurer le nombre de minutes entre chaque pr\u00e9vision. Le nombre de pr\u00e9visions fournies d\u00e9pend du nombre de minutes choisies entre les pr\u00e9visions.", + "description": "Si vous choisissez d'activer l'entit\u00e9 de pr\u00e9vision \u00ab\u00a0nowcast\u00a0\u00bb, vous pouvez configurer le nombre de minutes entre chaque pr\u00e9vision. Le nombre de pr\u00e9visions fournies d\u00e9pend du nombre de minutes choisies entre les pr\u00e9visions.", "title": "Mettre \u00e0 jour les options ClimaCell" } } diff --git a/homeassistant/components/climacell/translations/hu.json b/homeassistant/components/climacell/translations/hu.json index 3454a489455..bdeb913275c 100644 --- a/homeassistant/components/climacell/translations/hu.json +++ b/homeassistant/components/climacell/translations/hu.json @@ -3,7 +3,7 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", - "rate_limited": "Jelenleg korl\u00e1tozott sebess\u00e9g\u0171, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", + "rate_limited": "Jelenleg korl\u00e1tozott a hozz\u00e1f\u00e9r\u00e9s, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { @@ -13,7 +13,7 @@ "api_version": "API Verzi\u00f3", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Ha a Sz\u00e9less\u00e9g \u00e9s Hossz\u00fas\u00e1g nincs megadva, akkor a Home Assistant konfigur\u00e1ci\u00f3j\u00e1ban l\u00e9v\u0151 alap\u00e9rtelmezett \u00e9rt\u00e9keket fogjuk haszn\u00e1lni. Minden el\u0151rejelz\u00e9si t\u00edpushoz l\u00e9trej\u00f6n egy entit\u00e1s, de alap\u00e9rtelmez\u00e9s szerint csak az \u00d6n \u00e1ltal kiv\u00e1lasztottak lesznek enged\u00e9lyezve." } @@ -26,7 +26,7 @@ "timestep": "Min. A NowCast el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt" }, "description": "Ha a `nowcast` el\u0151rejelz\u00e9si entit\u00e1s enged\u00e9lyez\u00e9s\u00e9t v\u00e1lasztja, be\u00e1ll\u00edthatja az egyes el\u0151rejelz\u00e9sek k\u00f6z\u00f6tti percek sz\u00e1m\u00e1t. A megadott el\u0151rejelz\u00e9sek sz\u00e1ma az el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt kiv\u00e1lasztott percek sz\u00e1m\u00e1t\u00f3l f\u00fcgg.", - "title": "Friss\u00edtse a ClimaCell be\u00e1ll\u00edt\u00e1sokat" + "title": "ClimaCell be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" } } }, diff --git a/homeassistant/components/climacell/translations/pl.json b/homeassistant/components/climacell/translations/pl.json index dc08cb9b1bf..e107be1e001 100644 --- a/homeassistant/components/climacell/translations/pl.json +++ b/homeassistant/components/climacell/translations/pl.json @@ -23,7 +23,7 @@ "step": { "init": { "data": { - "timestep": "Minuty pomi\u0119dzy prognozami" + "timestep": "Czas (min) mi\u0119dzy prognozami NowCast" }, "description": "Je\u015bli zdecydujesz si\u0119 w\u0142\u0105czy\u0107 encj\u0119 prognozy \u201enowcast\u201d, mo\u017cesz skonfigurowa\u0107 liczb\u0119 minut mi\u0119dzy ka\u017cd\u0105 prognoz\u0105. Liczba dostarczonych prognoz zale\u017cy od liczby minut wybranych mi\u0119dzy prognozami.", "title": "Opcje aktualizacji ClimaCell" diff --git a/homeassistant/components/climacell/translations/sensor.fr.json b/homeassistant/components/climacell/translations/sensor.fr.json index 95625c2b8da..acff91fc570 100644 --- a/homeassistant/components/climacell/translations/sensor.fr.json +++ b/homeassistant/components/climacell/translations/sensor.fr.json @@ -2,7 +2,7 @@ "state": { "climacell__health_concern": { "good": "Bon", - "hazardous": "Hasardeux", + "hazardous": "Dangereux", "moderate": "Mod\u00e9r\u00e9", "unhealthy": "Mauvais pour la sant\u00e9", "unhealthy_for_sensitive_groups": "Mauvaise qualit\u00e9 pour les groupes sensibles", diff --git a/homeassistant/components/climacell/translations/sensor.hu.json b/homeassistant/components/climacell/translations/sensor.hu.json index d864d505143..656a460f429 100644 --- a/homeassistant/components/climacell/translations/sensor.hu.json +++ b/homeassistant/components/climacell/translations/sensor.hu.json @@ -17,7 +17,7 @@ "very_low": "Nagyon alacsony" }, "climacell__precipitation_type": { - "freezing_rain": "Fagyos es\u0151", + "freezing_rain": "Havas es\u0151", "ice_pellets": "\u00d3nos es\u0151", "none": "Nincs", "rain": "Es\u0151", diff --git a/homeassistant/components/climate/translations/et.json b/homeassistant/components/climate/translations/et.json index 7be57f4cdaa..c57a4d72991 100644 --- a/homeassistant/components/climate/translations/et.json +++ b/homeassistant/components/climate/translations/et.json @@ -18,8 +18,8 @@ "_": { "auto": "Automaatne", "cool": "Jahuta", - "dry": "Kuiv", - "fan_only": "Ainult ventilaator", + "dry": "Kuivata", + "fan_only": "Ventileeri", "heat": "Soojenda", "heat_cool": "K\u00fcta/jahuta", "off": "V\u00e4ljas" diff --git a/homeassistant/components/cloudflare/translations/he.json b/homeassistant/components/cloudflare/translations/he.json index 1f53e94240c..9303ea9143c 100644 --- a/homeassistant/components/cloudflare/translations/he.json +++ b/homeassistant/components/cloudflare/translations/he.json @@ -25,7 +25,8 @@ "user": { "data": { "api_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" - } + }, + "description": "\u05e9\u05d9\u05dc\u05d5\u05d1 \u05d6\u05d4 \u05d3\u05d5\u05e8\u05e9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05e0\u05d5\u05e6\u05e8 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea Zone:Zone:Read and Zone:DNS:\u05e2\u05e8\u05d9\u05db\u05ea \u05d4\u05e8\u05e9\u05d0\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05db\u05dc \u05d4\u05d0\u05d6\u05d5\u05e8\u05d9\u05dd \u05d1\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da." }, "zone": { "data": { diff --git a/homeassistant/components/coinbase/translations/ca.json b/homeassistant/components/coinbase/translations/ca.json index 0772a04aa15..0543f97003a 100644 --- a/homeassistant/components/coinbase/translations/ca.json +++ b/homeassistant/components/coinbase/translations/ca.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Saldos de cartera a informar.", "exchange_base": "Moneda base per als sensors de canvi de tipus.", - "exchange_rate_currencies": "Tipus de canvi a informar." + "exchange_rate_currencies": "Tipus de canvi a informar.", + "exchnage_rate_precision": "Nombre de posicions decimals per als tipus de canvi." }, "description": "Ajusta les opcions de Coinbase" } diff --git a/homeassistant/components/coinbase/translations/de.json b/homeassistant/components/coinbase/translations/de.json index 4c1d2edc5ca..d4b58ae42bd 100644 --- a/homeassistant/components/coinbase/translations/de.json +++ b/homeassistant/components/coinbase/translations/de.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Zu meldende Wallet-Guthaben.", "exchange_base": "Basisw\u00e4hrung f\u00fcr Wechselkurssensoren.", - "exchange_rate_currencies": "Zu meldende Wechselkurse." + "exchange_rate_currencies": "Zu meldende Wechselkurse.", + "exchnage_rate_precision": "Anzahl der Dezimalstellen f\u00fcr Wechselkurse." }, "description": "Coinbase-Optionen anpassen" } diff --git a/homeassistant/components/coinbase/translations/el.json b/homeassistant/components/coinbase/translations/el.json index 196aaad8643..05d6a1f7415 100644 --- a/homeassistant/components/coinbase/translations/el.json +++ b/homeassistant/components/coinbase/translations/el.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "\u03a5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03b1 \u03c0\u03bf\u03c1\u03c4\u03bf\u03c6\u03bf\u03bb\u03b9\u03bf\u03cd \u03c0\u03c1\u03bf\u03c2 \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac.", "exchange_base": "\u0392\u03b1\u03c3\u03b9\u03ba\u03cc \u03bd\u03cc\u03bc\u03b9\u03c3\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03c5\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ce\u03bd \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03b9\u03ce\u03bd.", - "exchange_rate_currencies": "\u03a3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2 \u03c0\u03c1\u03bf\u03c2 \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac." + "exchange_rate_currencies": "\u03a3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2 \u03c0\u03c1\u03bf\u03c2 \u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac.", + "exchnage_rate_precision": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03b3\u03b9\u03b1 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2." }, "description": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd Coinbase" } diff --git a/homeassistant/components/coinbase/translations/en.json b/homeassistant/components/coinbase/translations/en.json index 019159c8057..d5d7483e260 100644 --- a/homeassistant/components/coinbase/translations/en.json +++ b/homeassistant/components/coinbase/translations/en.json @@ -14,7 +14,9 @@ "user": { "data": { "api_key": "API Key", - "api_token": "API Secret" + "api_token": "API Secret", + "currencies": "Account Balance Currencies", + "exchange_rates": "Exchange Rates" }, "description": "Please enter the details of your API key as provided by Coinbase.", "title": "Coinbase API Key Details" @@ -24,7 +26,9 @@ "options": { "error": { "currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.", + "currency_unavaliable": "One or more of the requested currency balances is not provided by your Coinbase API.", "exchange_rate_unavailable": "One or more of the requested exchange rates is not provided by Coinbase.", + "exchange_rate_unavaliable": "One or more of the requested exchange rates is not provided by Coinbase.", "unknown": "Unexpected error" }, "step": { diff --git a/homeassistant/components/coinbase/translations/et.json b/homeassistant/components/coinbase/translations/et.json index 0c30ec9ff34..821d17656d2 100644 --- a/homeassistant/components/coinbase/translations/et.json +++ b/homeassistant/components/coinbase/translations/et.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Rahakoti saldod teavitamine.", "exchange_base": "Vahetuskursiandurite baasvaluuta.", - "exchange_rate_currencies": "Vahetuskursside aruanne." + "exchange_rate_currencies": "Vahetuskursside aruanne.", + "exchnage_rate_precision": "Vahetuskursside k\u00fcmnendkohtade arv." }, "description": "Kohanda Coinbase'i valikuid" } diff --git a/homeassistant/components/coinbase/translations/fr.json b/homeassistant/components/coinbase/translations/fr.json index 01cd7ec6dbf..77664cca73d 100644 --- a/homeassistant/components/coinbase/translations/fr.json +++ b/homeassistant/components/coinbase/translations/fr.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Soldes du portefeuille \u00e0 d\u00e9clarer.", "exchange_base": "Devise de base pour les capteurs de taux de change.", - "exchange_rate_currencies": "Taux de change \u00e0 d\u00e9clarer." + "exchange_rate_currencies": "Taux de change \u00e0 d\u00e9clarer.", + "exchnage_rate_precision": "Nombre de d\u00e9cimales pour les taux de change." }, "description": "Ajuster les options de Coinbase" } diff --git a/homeassistant/components/coinbase/translations/hu.json b/homeassistant/components/coinbase/translations/hu.json index 3db29058e68..44287da0ee6 100644 --- a/homeassistant/components/coinbase/translations/hu.json +++ b/homeassistant/components/coinbase/translations/hu.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Jelentend\u0151 p\u00e9nzt\u00e1rca egyenlegek.", "exchange_base": "Az \u00e1rfolyam-\u00e9rz\u00e9kel\u0151k alapvalut\u00e1ja.", - "exchange_rate_currencies": "Jelentend\u0151 \u00e1rfolyamok." + "exchange_rate_currencies": "Jelentend\u0151 \u00e1rfolyamok.", + "exchnage_rate_precision": "A tizedesjegyek sz\u00e1ma az \u00e1tv\u00e1lt\u00e1si \u00e1rfolyamokn\u00e1l." }, "description": "\u00c1ll\u00edtsa be a Coinbase opci\u00f3kat" } diff --git a/homeassistant/components/coinbase/translations/id.json b/homeassistant/components/coinbase/translations/id.json index 1d431ffd2fd..477aceafa45 100644 --- a/homeassistant/components/coinbase/translations/id.json +++ b/homeassistant/components/coinbase/translations/id.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Saldo dompet untuk dilaporkan.", "exchange_base": "Mata uang dasar untuk sensor nilai tukar.", - "exchange_rate_currencies": "Nilai tukar untuk dilaporkan." + "exchange_rate_currencies": "Nilai tukar untuk dilaporkan.", + "exchnage_rate_precision": "Jumlah digit desimal untuk nilai tukar." }, "description": "Sesuaikan Opsi Coinbase" } diff --git a/homeassistant/components/coinbase/translations/it.json b/homeassistant/components/coinbase/translations/it.json index ecc0d93e747..64b5b0cdca7 100644 --- a/homeassistant/components/coinbase/translations/it.json +++ b/homeassistant/components/coinbase/translations/it.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Saldi del portafoglio da segnalare.", "exchange_base": "Valuta di base per i sensori di tasso di cambio.", - "exchange_rate_currencies": "Tassi di cambio da segnalare." + "exchange_rate_currencies": "Tassi di cambio da segnalare.", + "exchnage_rate_precision": "Numero di cifre decimali per i tassi di cambio." }, "description": "Regola le opzioni di Coinbase" } diff --git a/homeassistant/components/coinbase/translations/ja.json b/homeassistant/components/coinbase/translations/ja.json index 6b6791d40b2..321e0c05d9d 100644 --- a/homeassistant/components/coinbase/translations/ja.json +++ b/homeassistant/components/coinbase/translations/ja.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "\u30a6\u30a9\u30ec\u30c3\u30c8\u306e\u6b8b\u9ad8\u3092\u5831\u544a\u3059\u308b\u3002", "exchange_base": "\u70ba\u66ff\u30ec\u30fc\u30c8\u30bb\u30f3\u30b5\u30fc\u306e\u57fa\u6e96\u901a\u8ca8\u3002", - "exchange_rate_currencies": "\u30ec\u30dd\u30fc\u30c8\u3059\u3079\u304d\u70ba\u66ff\u30ec\u30fc\u30c8" + "exchange_rate_currencies": "\u30ec\u30dd\u30fc\u30c8\u3059\u3079\u304d\u70ba\u66ff\u30ec\u30fc\u30c8", + "exchnage_rate_precision": "\u70ba\u66ff\u30ec\u30fc\u30c8\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002" }, "description": "Coinbase\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u8abf\u6574" } diff --git a/homeassistant/components/coinbase/translations/nl.json b/homeassistant/components/coinbase/translations/nl.json index fc2068cb01a..98763deb9a7 100644 --- a/homeassistant/components/coinbase/translations/nl.json +++ b/homeassistant/components/coinbase/translations/nl.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Wallet-saldi om te rapporteren.", "exchange_base": "Basisvaluta voor wisselkoerssensoren.", - "exchange_rate_currencies": "Wisselkoersen om te rapporteren." + "exchange_rate_currencies": "Wisselkoersen om te rapporteren.", + "exchnage_rate_precision": "Aantal decimalen voor wisselkoersen." }, "description": "Coinbase-opties aanpassen" } diff --git a/homeassistant/components/coinbase/translations/no.json b/homeassistant/components/coinbase/translations/no.json index 9a24d984c32..5171814cf9d 100644 --- a/homeassistant/components/coinbase/translations/no.json +++ b/homeassistant/components/coinbase/translations/no.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Lommeboksaldoer som skal rapporteres.", "exchange_base": "Standardvaluta for valutakurssensorer.", - "exchange_rate_currencies": "Valutakurser som skal rapporteres." + "exchange_rate_currencies": "Valutakurser som skal rapporteres.", + "exchnage_rate_precision": "Antall desimaler for valutakurser." }, "description": "Juster Coinbase-alternativer" } diff --git a/homeassistant/components/coinbase/translations/pl.json b/homeassistant/components/coinbase/translations/pl.json index 14432be5928..7465ae24486 100644 --- a/homeassistant/components/coinbase/translations/pl.json +++ b/homeassistant/components/coinbase/translations/pl.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Salda portfela do zg\u0142oszenia.", "exchange_base": "Waluta bazowa dla sensor\u00f3w kurs\u00f3w walut.", - "exchange_rate_currencies": "Kursy walut do zg\u0142oszenia." + "exchange_rate_currencies": "Kursy walut do zg\u0142oszenia.", + "exchnage_rate_precision": "Liczba miejsc po przecinku dla kurs\u00f3w walut." }, "description": "Dostosuj opcje Coinbase" } diff --git a/homeassistant/components/coinbase/translations/pt-BR.json b/homeassistant/components/coinbase/translations/pt-BR.json index 6596135208b..5ed52fa7afc 100644 --- a/homeassistant/components/coinbase/translations/pt-BR.json +++ b/homeassistant/components/coinbase/translations/pt-BR.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Saldos da carteira a relatar.", "exchange_base": "Moeda base para sensores de taxa de c\u00e2mbio.", - "exchange_rate_currencies": "Taxas de c\u00e2mbio a informar." + "exchange_rate_currencies": "Taxas de c\u00e2mbio a informar.", + "exchnage_rate_precision": "N\u00famero de casas decimais para taxas de c\u00e2mbio." }, "description": "Ajustar as op\u00e7\u00f5es da Coinbase" } diff --git a/homeassistant/components/coinbase/translations/ru.json b/homeassistant/components/coinbase/translations/ru.json index 41439aacea8..951c0182320 100644 --- a/homeassistant/components/coinbase/translations/ru.json +++ b/homeassistant/components/coinbase/translations/ru.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "\u0411\u0430\u043b\u0430\u043d\u0441\u044b \u043a\u043e\u0448\u0435\u043b\u044c\u043a\u0430 \u0434\u043b\u044f \u043e\u0442\u0447\u0435\u0442\u043d\u043e\u0441\u0442\u0438.", "exchange_base": "\u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0432\u0430\u043b\u044e\u0442\u0430 \u0434\u043b\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432 \u043e\u0431\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u043a\u0443\u0440\u0441\u0430.", - "exchange_rate_currencies": "\u041a\u0443\u0440\u0441\u044b \u0432\u0430\u043b\u044e\u0442 \u0434\u043b\u044f \u043e\u0442\u0447\u0435\u0442\u043d\u043e\u0441\u0442\u0438." + "exchange_rate_currencies": "\u041a\u0443\u0440\u0441\u044b \u0432\u0430\u043b\u044e\u0442 \u0434\u043b\u044f \u043e\u0442\u0447\u0435\u0442\u043d\u043e\u0441\u0442\u0438.", + "exchnage_rate_precision": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439 \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u043d\u044b\u0445 \u043a\u0443\u0440\u0441\u043e\u0432." }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 Coinbase" } diff --git a/homeassistant/components/coinbase/translations/tr.json b/homeassistant/components/coinbase/translations/tr.json index e52b6a81e76..e21cab489e4 100644 --- a/homeassistant/components/coinbase/translations/tr.json +++ b/homeassistant/components/coinbase/translations/tr.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "Rapor edilecek c\u00fczdan bakiyeleri.", "exchange_base": "D\u00f6viz kuru sens\u00f6rleri i\u00e7in temel para birimi.", - "exchange_rate_currencies": "Raporlanacak d\u00f6viz kurlar\u0131." + "exchange_rate_currencies": "Raporlanacak d\u00f6viz kurlar\u0131.", + "exchnage_rate_precision": "D\u00f6viz kurlar\u0131 i\u00e7in ondal\u0131k basamak say\u0131s\u0131." }, "description": "Coinbase Se\u00e7eneklerini Ayarlay\u0131n" } diff --git a/homeassistant/components/coinbase/translations/zh-Hant.json b/homeassistant/components/coinbase/translations/zh-Hant.json index 4510515bff2..315fe90254f 100644 --- a/homeassistant/components/coinbase/translations/zh-Hant.json +++ b/homeassistant/components/coinbase/translations/zh-Hant.json @@ -36,7 +36,8 @@ "data": { "account_balance_currencies": "\u5e33\u6236\u9918\u984d\u56de\u5831\u503c\u3002", "exchange_base": "\u532f\u7387\u611f\u6e2c\u5668\u57fa\u6e96\u8ca8\u5e63\u3002", - "exchange_rate_currencies": "\u532f\u7387\u56de\u5831\u503c\u3002" + "exchange_rate_currencies": "\u532f\u7387\u56de\u5831\u503c\u3002", + "exchnage_rate_precision": "\u532f\u7387\u5c0f\u6578\u4f4d\u6578\u3002" }, "description": "\u8abf\u6574 Coinbase \u9078\u9805" } diff --git a/homeassistant/components/coronavirus/translations/hu.json b/homeassistant/components/coronavirus/translations/hu.json index 9b79c82a014..880990f35c0 100644 --- a/homeassistant/components/coronavirus/translations/hu.json +++ b/homeassistant/components/coronavirus/translations/hu.json @@ -9,7 +9,7 @@ "data": { "country": "Orsz\u00e1g" }, - "title": "V\u00e1lassz egy orsz\u00e1got a megfigyel\u00e9shez" + "title": "V\u00e1lasszon egy orsz\u00e1got a figyel\u00e9shez" } } } diff --git a/homeassistant/components/cover/translations/he.json b/homeassistant/components/cover/translations/he.json index 66f6b9f3bbe..9e6fa77594f 100644 --- a/homeassistant/components/cover/translations/he.json +++ b/homeassistant/components/cover/translations/he.json @@ -10,7 +10,7 @@ "stop": "\u05e2\u05e6\u05d5\u05e8 {entity_name}" }, "condition_type": { - "is_closed": "{entity_name} \u05e1\u05d2\u05d5\u05e8", + "is_closed": "{entity_name} \u05e0\u05e1\u05d2\u05e8", "is_closing": "{entity_name} \u05e0\u05e1\u05d2\u05e8", "is_open": "{entity_name} \u05e4\u05ea\u05d5\u05d7", "is_opening": "{entity_name} \u05e0\u05e4\u05ea\u05d7", @@ -28,7 +28,7 @@ }, "state": { "_": { - "closed": "\u05e1\u05d2\u05d5\u05e8", + "closed": "\u05e0\u05e1\u05d2\u05e8", "closing": "\u05e1\u05d5\u05d2\u05e8", "open": "\u05e4\u05ea\u05d5\u05d7", "opening": "\u05e4\u05d5\u05ea\u05d7", diff --git a/homeassistant/components/cpuspeed/translations/bg.json b/homeassistant/components/cpuspeed/translations/bg.json index df41c1d7b0a..22f23d4428d 100644 --- a/homeassistant/components/cpuspeed/translations/bg.json +++ b/homeassistant/components/cpuspeed/translations/bg.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f.", "already_configured": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "step": { diff --git a/homeassistant/components/cpuspeed/translations/ca.json b/homeassistant/components/cpuspeed/translations/ca.json index 17e6295a5a9..79d3ccdf412 100644 --- a/homeassistant/components/cpuspeed/translations/ca.json +++ b/homeassistant/components/cpuspeed/translations/ca.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3.", "already_configured": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3.", "not_compatible": "No es pot obtenir la informaci\u00f3 de CPU, aquesta integraci\u00f3 no \u00e9s compatible amb el teu sistema" }, diff --git a/homeassistant/components/cpuspeed/translations/de.json b/homeassistant/components/cpuspeed/translations/de.json index f32ef08adba..60f5123a61c 100644 --- a/homeassistant/components/cpuspeed/translations/de.json +++ b/homeassistant/components/cpuspeed/translations/de.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", "already_configured": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", "not_compatible": "CPU-Informationen k\u00f6nnen nicht abgerufen werden, diese Integration ist nicht mit deinem System kompatibel" }, diff --git a/homeassistant/components/cpuspeed/translations/el.json b/homeassistant/components/cpuspeed/translations/el.json index 4c3541e2640..aac30ca2a82 100644 --- a/homeassistant/components/cpuspeed/translations/el.json +++ b/homeassistant/components/cpuspeed/translations/el.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae.", "already_configured": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae.", "not_compatible": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd CPU, \u03b1\u03c5\u03c4\u03ae \u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bc\u03b2\u03b1\u03c4\u03ae \u03bc\u03b5 \u03c4\u03bf \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03ac \u03c3\u03b1\u03c2" }, diff --git a/homeassistant/components/cpuspeed/translations/en.json b/homeassistant/components/cpuspeed/translations/en.json index adcf047b382..d482e5d3d3d 100644 --- a/homeassistant/components/cpuspeed/translations/en.json +++ b/homeassistant/components/cpuspeed/translations/en.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Already configured. Only a single configuration possible.", "already_configured": "Already configured. Only a single configuration possible.", "not_compatible": "Unable to get CPU information, this integration is not compatible with your system" }, diff --git a/homeassistant/components/cpuspeed/translations/es.json b/homeassistant/components/cpuspeed/translations/es.json index 03b2a17dba3..87ff7b0c783 100644 --- a/homeassistant/components/cpuspeed/translations/es.json +++ b/homeassistant/components/cpuspeed/translations/es.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "already_configured": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "not_compatible": "No se puede obtener informaci\u00f3n de la CPU, esta integraci\u00f3n no es compatible con su sistema" }, diff --git a/homeassistant/components/cpuspeed/translations/et.json b/homeassistant/components/cpuspeed/translations/et.json index 037d1b73086..69805d26d58 100644 --- a/homeassistant/components/cpuspeed/translations/et.json +++ b/homeassistant/components/cpuspeed/translations/et.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Juba seadistatud. Lubatud on ainult \u00fcks sidumine.", "already_configured": "Juba seadistatud. Lubatud on ainult \u00fcks seadistamine.", "not_compatible": "CPU teavet ei saa hankida, see sidumine ei \u00fchildu s\u00fcsteemiga" }, diff --git a/homeassistant/components/cpuspeed/translations/fr.json b/homeassistant/components/cpuspeed/translations/fr.json index 5de49927c49..26428d40ef2 100644 --- a/homeassistant/components/cpuspeed/translations/fr.json +++ b/homeassistant/components/cpuspeed/translations/fr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.", "already_configured": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.", "not_compatible": "Impossible d'obtenir les informations CPU, cette int\u00e9gration n'est pas compatible avec votre syst\u00e8me" }, diff --git a/homeassistant/components/cpuspeed/translations/he.json b/homeassistant/components/cpuspeed/translations/he.json index e38d585f2a4..9c1bb588a8f 100644 --- a/homeassistant/components/cpuspeed/translations/he.json +++ b/homeassistant/components/cpuspeed/translations/he.json @@ -1,13 +1,14 @@ { "config": { "abort": { - "alread_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea.", "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { "user": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?", + "title": "\u05de\u05d4\u05d9\u05e8\u05d5\u05ea \u05de\u05e2\u05d1\u05d3" } } - } + }, + "title": "\u05de\u05d4\u05d9\u05e8\u05d5\u05ea \u05de\u05e2\u05d1\u05d3" } \ No newline at end of file diff --git a/homeassistant/components/cpuspeed/translations/hu.json b/homeassistant/components/cpuspeed/translations/hu.json index 9ab3aa355dd..400baa4e362 100644 --- a/homeassistant/components/cpuspeed/translations/hu.json +++ b/homeassistant/components/cpuspeed/translations/hu.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges.", "already_configured": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges.", "not_compatible": "Nem lehet CPU-inform\u00e1ci\u00f3kat lek\u00e9rni, ez az integr\u00e1ci\u00f3 nem kompatibilis a rendszer\u00e9vel." }, diff --git a/homeassistant/components/cpuspeed/translations/id.json b/homeassistant/components/cpuspeed/translations/id.json index ab3b16da4e9..e8a87d3707d 100644 --- a/homeassistant/components/cpuspeed/translations/id.json +++ b/homeassistant/components/cpuspeed/translations/id.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan.", "already_configured": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan.", "not_compatible": "Tidak dapat mendapatkan informasi CPU, integrasi ini tidak kompatibel dengan sistem Anda" }, diff --git a/homeassistant/components/cpuspeed/translations/it.json b/homeassistant/components/cpuspeed/translations/it.json index 803458e48d7..ff84b5bf5ad 100644 --- a/homeassistant/components/cpuspeed/translations/it.json +++ b/homeassistant/components/cpuspeed/translations/it.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", "already_configured": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", "not_compatible": "Impossibile ottenere informazioni sulla CPU, questa integrazione non \u00e8 compatibile con il tuo sistema" }, diff --git a/homeassistant/components/cpuspeed/translations/ja.json b/homeassistant/components/cpuspeed/translations/ja.json index d8e2fa85fb7..e2264b57dcb 100644 --- a/homeassistant/components/cpuspeed/translations/ja.json +++ b/homeassistant/components/cpuspeed/translations/ja.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002", "already_configured": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002", "not_compatible": "CPU\u60c5\u5831\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3002\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306f\u3001\u304a\u4f7f\u3044\u306e\u30b7\u30b9\u30c6\u30e0\u3068\u4e92\u63db\u6027\u304c\u3042\u308a\u307e\u305b\u3093\u3002" }, diff --git a/homeassistant/components/cpuspeed/translations/nl.json b/homeassistant/components/cpuspeed/translations/nl.json index b29d9c9ddf1..c3d09b0b8a6 100644 --- a/homeassistant/components/cpuspeed/translations/nl.json +++ b/homeassistant/components/cpuspeed/translations/nl.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", "already_configured": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", "not_compatible": "Kan geen CPU-informatie ophalen, deze integratie is niet compatibel met uw systeem" }, diff --git a/homeassistant/components/cpuspeed/translations/no.json b/homeassistant/components/cpuspeed/translations/no.json index 40d5ed1de01..9f3cb464424 100644 --- a/homeassistant/components/cpuspeed/translations/no.json +++ b/homeassistant/components/cpuspeed/translations/no.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig.", "already_configured": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig.", "not_compatible": "Kan ikke hente CPU-informasjon, denne integrasjonen er ikke kompatibel med systemet ditt" }, diff --git a/homeassistant/components/cpuspeed/translations/pl.json b/homeassistant/components/cpuspeed/translations/pl.json index 81c37098c29..be477365437 100644 --- a/homeassistant/components/cpuspeed/translations/pl.json +++ b/homeassistant/components/cpuspeed/translations/pl.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja.", "already_configured": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja.", "not_compatible": "Nie mo\u017cna uzyska\u0107 informacji o procesorze, ta integracja nie jest zgodna z twoim systemem" }, diff --git a/homeassistant/components/cpuspeed/translations/pt-BR.json b/homeassistant/components/cpuspeed/translations/pt-BR.json index f39ce9b4c9a..b21fcb00def 100644 --- a/homeassistant/components/cpuspeed/translations/pt-BR.json +++ b/homeassistant/components/cpuspeed/translations/pt-BR.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel.", "already_configured": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel.", "not_compatible": "N\u00e3o \u00e9 poss\u00edvel obter informa\u00e7\u00f5es da CPU, esta integra\u00e7\u00e3o n\u00e3o \u00e9 compat\u00edvel com seu sistema" }, diff --git a/homeassistant/components/cpuspeed/translations/ru.json b/homeassistant/components/cpuspeed/translations/ru.json index 9638a43f7f7..0a44b56086c 100644 --- a/homeassistant/components/cpuspeed/translations/ru.json +++ b/homeassistant/components/cpuspeed/translations/ru.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.", "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.", "not_compatible": "\u041d\u0435 \u0443\u0434\u0430\u0451\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0435, \u044d\u0442\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u0430 \u0441 \u0412\u0430\u0448\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439." }, diff --git a/homeassistant/components/cpuspeed/translations/tr.json b/homeassistant/components/cpuspeed/translations/tr.json index d4dd5a271e0..f5689f2f3b4 100644 --- a/homeassistant/components/cpuspeed/translations/tr.json +++ b/homeassistant/components/cpuspeed/translations/tr.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr.", "already_configured": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr.", "not_compatible": "CPU bilgisi al\u0131nam\u0131yor, bu entegrasyon sisteminizle uyumlu de\u011fil" }, diff --git a/homeassistant/components/cpuspeed/translations/zh-Hans.json b/homeassistant/components/cpuspeed/translations/zh-Hans.json index 41130cbdd39..95f5766edc9 100644 --- a/homeassistant/components/cpuspeed/translations/zh-Hans.json +++ b/homeassistant/components/cpuspeed/translations/zh-Hans.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u5f53\u524d\u96c6\u6210\u5df2\u88ab\u914d\u7f6e\uff0c\u4ec5\u80fd\u53ea\u6709\u4e00\u4e2a\u914d\u7f6e", "already_configured": "\u5f53\u524d\u96c6\u6210\u5df2\u88ab\u914d\u7f6e\uff0c\u4ec5\u80fd\u53ea\u6709\u4e00\u4e2a\u914d\u7f6e", "not_compatible": "\u65e0\u6cd5\u83b7\u53d6 CPU \u4fe1\u606f\uff0c\u8be5\u96c6\u6210\u4e0e\u60a8\u7684\u7cfb\u7edf\u4e0d\u517c\u5bb9" }, diff --git a/homeassistant/components/cpuspeed/translations/zh-Hant.json b/homeassistant/components/cpuspeed/translations/zh-Hant.json index 4885aead70a..b88f1d1b1d4 100644 --- a/homeassistant/components/cpuspeed/translations/zh-Hant.json +++ b/homeassistant/components/cpuspeed/translations/zh-Hant.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "alread_configured": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002", "already_configured": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002", "not_compatible": "\u7121\u6cd5\u53d6\u5f97 CPU \u8cc7\u8a0a\uff0c\u9019\u500b\u63d2\u4ef6\u8207\u4f60\u7684\u7cfb\u7d71\u4e0d\u76f8\u5bb9" }, diff --git a/homeassistant/components/crownstone/translations/ca.json b/homeassistant/components/crownstone/translations/ca.json index 9de845d87c6..deded7c6b71 100644 --- a/homeassistant/components/crownstone/translations/ca.json +++ b/homeassistant/components/crownstone/translations/ca.json @@ -56,13 +56,6 @@ "description": "Selecciona el port s\u00e8rie de l'adaptador USB Crownstone.\n\nBusca un dispositiu amb VID 10C4 i PID EA60.", "title": "Configuraci\u00f3 de l'adaptador USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Ruta del dispositiu USB" - }, - "description": "Selecciona el port s\u00e8rie de l'adaptador USB Crownstone.\n\nBusca un dispositiu amb VID 10C4 i PID EA60.", - "title": "Configuraci\u00f3 de l'adaptador USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Ruta del dispositiu USB" @@ -70,26 +63,12 @@ "description": "Introdueix manualment la ruta de l'adaptador USB Crownstone.", "title": "Ruta manual de l'adaptador USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Ruta del dispositiu USB" - }, - "description": "Introdueix manualment la ruta de l'adaptador USB Crownstone.", - "title": "Ruta manual de l'adaptador USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Esfera Crownstone" }, "description": "Selecciona la esfera Crownstone on es troba l'USB.", "title": "Esfera Crownstone USB" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Esfera Crownstone" - }, - "description": "Selecciona la esfera Crownstone on es troba l'USB.", - "title": "Esfera Crownstone USB" } } } diff --git a/homeassistant/components/crownstone/translations/cs.json b/homeassistant/components/crownstone/translations/cs.json index 1f14cdd8eff..b6c0cae1be2 100644 --- a/homeassistant/components/crownstone/translations/cs.json +++ b/homeassistant/components/crownstone/translations/cs.json @@ -28,20 +28,10 @@ "usb_path": "Cesta k USB za\u0159\u00edzen\u00ed" } }, - "usb_config_option": { - "data": { - "usb_path": "Cesta k USB za\u0159\u00edzen\u00ed" - } - }, "usb_manual_config": { "data": { "usb_manual_path": "Cesta k USB za\u0159\u00edzen\u00ed" } - }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Cesta k USB za\u0159\u00edzen\u00ed" - } } } } diff --git a/homeassistant/components/crownstone/translations/de.json b/homeassistant/components/crownstone/translations/de.json index a969d9b2999..054d6987c29 100644 --- a/homeassistant/components/crownstone/translations/de.json +++ b/homeassistant/components/crownstone/translations/de.json @@ -56,13 +56,6 @@ "description": "W\u00e4hle den seriellen Anschluss des Crownstone-USB-Dongles.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", "title": "Crownstone USB-Dongle-Konfiguration" }, - "usb_config_option": { - "data": { - "usb_path": "USB-Ger\u00e4te-Pfad" - }, - "description": "W\u00e4hle den seriellen Anschluss des Crownstone-USB-Dongles.\n\nSuche nach einem Ger\u00e4t mit VID 10C4 und PID EA60.", - "title": "Crownstone USB-Dongle-Konfiguration" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB-Ger\u00e4te-Pfad" @@ -70,26 +63,12 @@ "description": "Gib den Pfad eines Crownstone USB-Dongles manuell ein.", "title": "Crownstone USB-Dongle manueller Pfad" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB-Ger\u00e4te-Pfad" - }, - "description": "Gib den Pfad eines Crownstone USB-Dongles manuell ein.", - "title": "Crownstone USB-Dongle manueller Pfad" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "W\u00e4hle eine Crownstone Sphere aus, in der sich der USB-Stick befindet.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "W\u00e4hle eine Crownstone Sphere aus, in der sich der USB-Stick befindet.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/el.json b/homeassistant/components/crownstone/translations/el.json index 81cfbd9ad39..b682dedcb1b 100644 --- a/homeassistant/components/crownstone/translations/el.json +++ b/homeassistant/components/crownstone/translations/el.json @@ -56,13 +56,6 @@ "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03c4\u03bf\u03c5 Crownstone USB dongle.\n\n\u0391\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03bc\u03b5 VID 10C4 \u03ba\u03b1\u03b9 PID EA60.", "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 Crownstone USB dongle" }, - "usb_config_option": { - "data": { - "usb_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 USB" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03c4\u03bf\u03c5 Crownstone USB dongle.\n\n\u0391\u03bd\u03b1\u03b6\u03b7\u03c4\u03ae\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03bc\u03b5 VID 10C4 \u03ba\u03b1\u03b9 PID EA60.", - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 Crownstone USB dongle" - }, "usb_manual_config": { "data": { "usb_manual_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 USB" @@ -70,26 +63,12 @@ "description": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 dongle USB Crownstone.", "title": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2 USB dongle Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 USB" - }, - "description": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2 \u03b5\u03bd\u03cc\u03c2 dongle USB Crownstone.", - "title": "\u03a7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2 USB dongle Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 Crownstone Sphere \u03cc\u03c0\u03bf\u03c5 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c4\u03bf USB.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 Crownstone Sphere \u03cc\u03c0\u03bf\u03c5 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c4\u03bf USB.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/en.json b/homeassistant/components/crownstone/translations/en.json index d6070c90a0f..09a26b9739c 100644 --- a/homeassistant/components/crownstone/translations/en.json +++ b/homeassistant/components/crownstone/translations/en.json @@ -56,13 +56,6 @@ "description": "Select the serial port of the Crownstone USB dongle.\n\nLook for a device with VID 10C4 and PID EA60.", "title": "Crownstone USB dongle configuration" }, - "usb_config_option": { - "data": { - "usb_path": "USB Device Path" - }, - "description": "Select the serial port of the Crownstone USB dongle.\n\nLook for a device with VID 10C4 and PID EA60.", - "title": "Crownstone USB dongle configuration" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB Device Path" @@ -70,26 +63,12 @@ "description": "Manually enter the path of a Crownstone USB dongle.", "title": "Crownstone USB dongle manual path" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB Device Path" - }, - "description": "Manually enter the path of a Crownstone USB dongle.", - "title": "Crownstone USB dongle manual path" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Select a Crownstone Sphere where the USB is located.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "Select a Crownstone Sphere where the USB is located.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/es-419.json b/homeassistant/components/crownstone/translations/es-419.json index 1c904302541..40b4cebe97b 100644 --- a/homeassistant/components/crownstone/translations/es-419.json +++ b/homeassistant/components/crownstone/translations/es-419.json @@ -39,9 +39,6 @@ "usb_config": { "description": "Seleccione el puerto serie del dongle USB Crownstone. \n\n Busque un dispositivo con VID 10C4 y PID EA60.", "title": "Configuraci\u00f3n del dongle USB Crownstone" - }, - "usb_config_option": { - "description": "Seleccione el puerto serie del dongle USB Crownstone. \n\n Busque un dispositivo con VID 10C4 y PID EA60." } } } diff --git a/homeassistant/components/crownstone/translations/es.json b/homeassistant/components/crownstone/translations/es.json index 9e4ac60832f..f52b0074322 100644 --- a/homeassistant/components/crownstone/translations/es.json +++ b/homeassistant/components/crownstone/translations/es.json @@ -56,13 +56,6 @@ "description": "Seleccione el puerto serie del dispositivo USB Crownstone.\n\nBusque un dispositivo con VID 10C4 y PID EA60.", "title": "Configuraci\u00f3n del dispositivo USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Ruta del dispositivo USB" - }, - "description": "Seleccione el puerto serie del dispositivo USB Crownstone.\n\nBusque un dispositivo con VID 10C4 y PID EA60.", - "title": "Configuraci\u00f3n del dispositivo USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Ruta del dispositivo USB" @@ -70,26 +63,12 @@ "description": "Introduzca manualmente la ruta de un dispositivo USB Crownstone.", "title": "Ruta manual del dispositivo USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Ruta del dispositivo USB" - }, - "description": "Introduzca manualmente la ruta de un dispositivo USB Crownstone.", - "title": "Ruta manual del dispositivo USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Esfera Crownstone" }, "description": "Selecciona una Esfera Crownstone donde se encuentra el USB.", "title": "USB de Esfera Crownstone" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Esfera Crownstone" - }, - "description": "Selecciona una Esfera Crownstone donde se encuentra el USB.", - "title": "USB de Esfera Crownstone" } } } diff --git a/homeassistant/components/crownstone/translations/et.json b/homeassistant/components/crownstone/translations/et.json index 3a651257e1a..da7831e8959 100644 --- a/homeassistant/components/crownstone/translations/et.json +++ b/homeassistant/components/crownstone/translations/et.json @@ -56,13 +56,6 @@ "description": "Vali Crownstone'i USB seadme jadaport. \n\n Otsi seadet mille VID on 10C4 ja PID on EA60.", "title": "Crownstone'i USB seadme s\u00e4tted" }, - "usb_config_option": { - "data": { - "usb_path": "USB seadme rada" - }, - "description": "Vali Crownstone'i USB seadme jadaport. \n\n Otsi seadet mille VID on 10C4 ja PID on EA60.", - "title": "Crownstone'i USB seadme s\u00e4tted" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB seadme rada" @@ -70,26 +63,12 @@ "description": "Sisesta k\u00e4sitsi Crownstone'i USBseadme rada.", "title": "Crownstone'i USB seadme rada" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB seadme rada" - }, - "description": "Sisesta k\u00e4sitsi Crownstone'i USBseadme rada.", - "title": "Crownstone'i USB seadme rada" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Vali Crownstone Sphere kus USB asub.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "Vali Crownstone Sphere kus USB asub.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/fr.json b/homeassistant/components/crownstone/translations/fr.json index 61a2f0da436..59f9aa0f20b 100644 --- a/homeassistant/components/crownstone/translations/fr.json +++ b/homeassistant/components/crownstone/translations/fr.json @@ -56,13 +56,6 @@ "description": "S\u00e9lectionnez le port s\u00e9rie du dongle USB Crownstone. \n\n Recherchez un appareil avec VID 10C4 et PID EA60.", "title": "Configuration du dongle USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Chemin du p\u00e9riph\u00e9rique USB" - }, - "description": "S\u00e9lectionnez le port s\u00e9rie du dongle USB Crownstone. \n\n Recherchez un appareil avec VID 10C4 et PID EA60.", - "title": "Configuration du dongle USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Chemin du p\u00e9riph\u00e9rique USB" @@ -70,26 +63,12 @@ "description": "Entrez manuellement le chemin d'un dongle USB Crownstone.", "title": "Chemin d'acc\u00e8s manuel du dongle USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Chemin du p\u00e9riph\u00e9rique USB" - }, - "description": "Entrez manuellement le chemin d'un dongle USB Crownstone.", - "title": "Chemin d'acc\u00e8s manuel du dongle USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "S\u00e9lectionnez une sph\u00e8re Crownstone o\u00f9 se trouve l\u2019USB.", "title": "Sph\u00e8re USB Crownstone" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "S\u00e9lectionnez une sph\u00e8re Crownstone o\u00f9 se trouve l\u2019USB.", - "title": "Sph\u00e8re USB Crownstone" } } } diff --git a/homeassistant/components/crownstone/translations/he.json b/homeassistant/components/crownstone/translations/he.json index af11b65839b..db4289ffebc 100644 --- a/homeassistant/components/crownstone/translations/he.json +++ b/homeassistant/components/crownstone/translations/he.json @@ -33,20 +33,10 @@ "usb_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" } }, - "usb_config_option": { - "data": { - "usb_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" - } - }, "usb_manual_config": { "data": { "usb_manual_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" } - }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" - } } } } diff --git a/homeassistant/components/crownstone/translations/hu.json b/homeassistant/components/crownstone/translations/hu.json index 2c2a2e34fe1..f2ffe6f2b07 100644 --- a/homeassistant/components/crownstone/translations/hu.json +++ b/homeassistant/components/crownstone/translations/hu.json @@ -56,13 +56,6 @@ "description": "V\u00e1lassza ki a Crownstone USB kulcs soros portj\u00e1t.\n\nKeressen egy VID 10C4 \u00e9s PID EA60 azonos\u00edt\u00f3val rendelkez\u0151 eszk\u00f6zt.", "title": "Crownstone USB kulcs konfigur\u00e1ci\u00f3" }, - "usb_config_option": { - "data": { - "usb_path": "USB eszk\u00f6z el\u00e9r\u00e9si \u00fat" - }, - "description": "V\u00e1lassza ki a Crownstone USB kulcs soros portj\u00e1t.\n\nKeressen egy VID 10C4 \u00e9s PID EA60 azonos\u00edt\u00f3val rendelkez\u0151 eszk\u00f6zt.", - "title": "Crownstone USB kulcs konfigur\u00e1ci\u00f3" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB eszk\u00f6z el\u00e9r\u00e9si \u00fat" @@ -70,26 +63,12 @@ "description": "Adja meg manu\u00e1lisan a Crownstone USB kulcs \u00fatvonal\u00e1t.", "title": "A Crownstone USB kulcs manu\u00e1lis el\u00e9r\u00e9si \u00fatja" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB eszk\u00f6z el\u00e9r\u00e9si \u00fat" - }, - "description": "Adja meg manu\u00e1lisan a Crownstone USB kulcs \u00fatvonal\u00e1t.", - "title": "A Crownstone USB kulcs manu\u00e1lis el\u00e9r\u00e9si \u00fatja" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "V\u00e1lasszon egy Crownstone Sphere-t, ahol az USB tal\u00e1lhat\u00f3.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "V\u00e1lasszon egy Crownstone Sphere-t, ahol az USB tal\u00e1lhat\u00f3.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/id.json b/homeassistant/components/crownstone/translations/id.json index aef98346fd2..381645f1453 100644 --- a/homeassistant/components/crownstone/translations/id.json +++ b/homeassistant/components/crownstone/translations/id.json @@ -56,13 +56,6 @@ "description": "Pilih port serial dongle USB Crownstone. \n\nCari perangkat dengan VID 10C4 dan PID EA60.", "title": "Konfigurasi dongle USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Jalur Perangkat USB" - }, - "description": "Pilih port serial dongle USB Crownstone. \n\nCari perangkat dengan VID 10C4 dan PID EA60.", - "title": "Konfigurasi dongle USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Jalur Perangkat USB" @@ -70,26 +63,12 @@ "description": "Masukkan jalur dongle USB Crownstone secara manual.", "title": "Jalur manual dongle USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Jalur Perangkat USB" - }, - "description": "Masukkan jalur dongle USB Crownstone secara manual.", - "title": "Jalur manual dongle USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Pilih Crownstone Sphere tempat USB berada.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "Pilih Crownstone Sphere tempat USB berada.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/it.json b/homeassistant/components/crownstone/translations/it.json index 062f77c9349..5600ff09de8 100644 --- a/homeassistant/components/crownstone/translations/it.json +++ b/homeassistant/components/crownstone/translations/it.json @@ -56,13 +56,6 @@ "description": "Seleziona la porta seriale della chiavetta USB Crownstone. \n\nCerca un dispositivo con VID 10C4 e PID EA60.", "title": "Configurazione della chiavetta USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Percorso del dispositivo USB" - }, - "description": "Seleziona la porta seriale della chiavetta USB Crownstone. \n\nCerca un dispositivo con VID 10C4 e PID EA60.", - "title": "Configurazione della chiavetta USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Percorso del dispositivo USB" @@ -70,26 +63,12 @@ "description": "Immettere manualmente il percorso di una chiavetta USB Crownstone.", "title": "Percorso manuale della chiavetta USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Percorso del dispositivo USB" - }, - "description": "Immetti manualmente il percorso di una chiavetta USB Crownstone.", - "title": "Percorso manuale della chiavetta USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Sfera Crownstone" }, "description": "Seleziona una sfera di Crownstone dove si trova l'USB.", "title": "Sfera USB Crownstone" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Sfera Crownstone" - }, - "description": "Seleziona una sfera Crownstone in cui si trova l'USB.", - "title": "Sfera USB Crownstone" } } } diff --git a/homeassistant/components/crownstone/translations/ja.json b/homeassistant/components/crownstone/translations/ja.json index 6ab8f858af4..639922c4d94 100644 --- a/homeassistant/components/crownstone/translations/ja.json +++ b/homeassistant/components/crownstone/translations/ja.json @@ -56,13 +56,6 @@ "description": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u30b7\u30ea\u30a2\u30eb \u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059\u3002\n\nVID 10C4 \u3067 PID EA60 \u306a\u5024\u306e\u30c7\u30d0\u30a4\u30b9\u3092\u63a2\u3057\u307e\u3059\u3002", "title": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u8a2d\u5b9a" }, - "usb_config_option": { - "data": { - "usb_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" - }, - "description": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u30b7\u30ea\u30a2\u30eb \u30dd\u30fc\u30c8\u3092\u9078\u629e\u3057\u307e\u3059\u3002\n\nVID 10C4 \u3067 PID EA60 \u306a\u5024\u306e\u30c7\u30d0\u30a4\u30b9\u3092\u63a2\u3057\u307e\u3059\u3002", - "title": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u8a2d\u5b9a" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" @@ -70,26 +63,12 @@ "description": "\u624b\u52d5\u3067\u3001Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u30d1\u30b9\u3092\u5165\u529b\u3057\u307e\u3059\u3002", "title": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u3078\u306e\u624b\u52d5\u30d1\u30b9" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" - }, - "description": "\u624b\u52d5\u3067\u3001Crownstone USB\u30c9\u30f3\u30b0\u30eb\u306e\u30d1\u30b9\u3092\u5165\u529b\u3057\u307e\u3059\u3002", - "title": "Crownstone USB\u30c9\u30f3\u30b0\u30eb\u3078\u306e\u624b\u52d5\u30d1\u30b9" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "USB\u306b\u914d\u7f6e\u3055\u308c\u3066\u3044\u308b\u3001CrownstoneSphere\u3092\u9078\u629e\u3057\u307e\u3059\u3002", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "USB\u306b\u914d\u7f6e\u3055\u308c\u3066\u3044\u308b\u3001CrownstoneSphere\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/ko.json b/homeassistant/components/crownstone/translations/ko.json deleted file mode 100644 index aadd2d3da42..00000000000 --- a/homeassistant/components/crownstone/translations/ko.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "options": { - "step": { - "usb_config_option": { - "data": { - "usb_path": "USB \uc7a5\uce58 \uacbd\ub85c" - } - }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB \uc7a5\uce58 \uacbd\ub85c" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/crownstone/translations/nl.json b/homeassistant/components/crownstone/translations/nl.json index 1da12c8f841..91dddf8619e 100644 --- a/homeassistant/components/crownstone/translations/nl.json +++ b/homeassistant/components/crownstone/translations/nl.json @@ -56,13 +56,6 @@ "description": "Selecteer de seri\u00eble poort van de Crownstone USB dongle.\n\nZoek naar een apparaat met VID 10C4 en PID EA60.", "title": "Crownstone USB dongle configuratie" }, - "usb_config_option": { - "data": { - "usb_path": "USB-apparaatpad" - }, - "description": "Selecteer de seri\u00eble poort van de Crownstone USB dongle.\n\nZoek naar een apparaat met VID 10C4 en PID EA60.", - "title": "Crownstone USB dongle configuratie" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB-apparaatpad" @@ -70,26 +63,12 @@ "description": "Voer handmatig het pad van een Crownstone USB dongle in.", "title": "Crownstone USB-dongle handmatig pad" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB-apparaatpad" - }, - "description": "Voer handmatig het pad van een Crownstone USB dongle in.", - "title": "Crownstone USB-dongle handmatig pad" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Selecteer een Crownstone Sphere waar de USB zich bevindt.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "Selecteer een Crownstone Sphere waar de USB zich bevindt.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/no.json b/homeassistant/components/crownstone/translations/no.json index 88f3578a9a4..33e61211205 100644 --- a/homeassistant/components/crownstone/translations/no.json +++ b/homeassistant/components/crownstone/translations/no.json @@ -56,13 +56,6 @@ "description": "Velg serieporten til Crownstone USB -dongelen. \n\n Se etter en enhet med VID 10C4 og PID EA60.", "title": "Crownstone USB -dongle -konfigurasjon" }, - "usb_config_option": { - "data": { - "usb_path": "USB enhetsbane" - }, - "description": "Velg serieporten til Crownstone USB -dongelen. \n\n Se etter en enhet med VID 10C4 og PID EA60.", - "title": "Crownstone USB -dongle -konfigurasjon" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB enhetsbane" @@ -70,26 +63,12 @@ "description": "Skriv inn banen til en Crownstone USB -dongle manuelt.", "title": "Crownstone USB -dongle manuell bane" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB enhetsbane" - }, - "description": "Skriv inn banen til en Crownstone USB -dongle manuelt.", - "title": "Crownstone USB -dongle manuell bane" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Velg en Crownstone Sphere der USB -en er plassert.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone USB Sphere" - }, - "description": "Velg en Crownstone Sphere der USB -en er plassert.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/pl.json b/homeassistant/components/crownstone/translations/pl.json index 12ac55d668c..49a1d888a94 100644 --- a/homeassistant/components/crownstone/translations/pl.json +++ b/homeassistant/components/crownstone/translations/pl.json @@ -56,13 +56,6 @@ "description": "Wybierz port szeregowy urz\u0105dzenia USB Crownstone. \n\nPoszukaj urz\u0105dzenia z VID 10C4 i PID EA60.", "title": "Konfiguracja urz\u0105dzenia USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "\u015acie\u017cka urz\u0105dzenia USB" - }, - "description": "Wybierz port szeregowy urz\u0105dzenia USB Crownstone. \n\nPoszukaj urz\u0105dzenia z VID 10C4 i PID EA60.", - "title": "Konfiguracja urz\u0105dzenia USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "\u015acie\u017cka urz\u0105dzenia USB" @@ -70,26 +63,12 @@ "description": "Wprowad\u017a r\u0119cznie \u015bcie\u017ck\u0119 urz\u0105dzenia USB Crownstone.", "title": "Wpisz r\u0119cznie \u015bcie\u017ck\u0119 urz\u0105dzenia USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "\u015acie\u017cka urz\u0105dzenia USB" - }, - "description": "Wprowad\u017a r\u0119cznie \u015bcie\u017ck\u0119 urz\u0105dzenia USB Crownstone.", - "title": "Wpisz r\u0119cznie \u015bcie\u017ck\u0119 urz\u0105dzenia USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "Wybierz USB, w kt\u00f3rym znajduje si\u0119 Crownstone Sphere.", "title": "USB Crownstone Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "Wybierz USB w kt\u00f3rym znajduje si\u0119 Crownstone Sphere.", - "title": "USB Crownstone Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/pt-BR.json b/homeassistant/components/crownstone/translations/pt-BR.json index df4e446837e..f68c7a7f483 100644 --- a/homeassistant/components/crownstone/translations/pt-BR.json +++ b/homeassistant/components/crownstone/translations/pt-BR.json @@ -56,13 +56,6 @@ "description": "Selecione a porta serial do dongle USB Crownstone. \n\n Procure um dispositivo com VID 10C4 e PID EA60.", "title": "Configura\u00e7\u00e3o do dongle USB Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "Caminho do Dispositivo USB" - }, - "description": "Selecione a porta serial do dongle USB Crownstone. \n\n Procure um dispositivo com VID 10C4 e PID EA60.", - "title": "Configura\u00e7\u00e3o do dongle USB Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "Caminho do Dispositivo USB" @@ -70,26 +63,12 @@ "description": "Insira manualmente o caminho de um dongle USB Crownstone.", "title": "Caminho manual do dongle USB Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "Caminho do Dispositivo USB" - }, - "description": "Insira manualmente o caminho de um dongle USB Crownstone.", - "title": "Caminho manual do dongle USB Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Esfera da Pedra da Coroa" }, "description": "Selecione um Crownstone Sphere onde o USB est\u00e1 localizado.", "title": "Esfera USB Crownstone" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Esfera de Crownstone" - }, - "description": "Selecione um Crownstone Sphere onde o USB est\u00e1 localizado.", - "title": "Esfera USB Crownstone" } } } diff --git a/homeassistant/components/crownstone/translations/ru.json b/homeassistant/components/crownstone/translations/ru.json index 7dfd88bd63e..d16b16f457e 100644 --- a/homeassistant/components/crownstone/translations/ru.json +++ b/homeassistant/components/crownstone/translations/ru.json @@ -56,13 +56,6 @@ "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Crownstone. \n\n\u0414\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 VID 10C4 \u0438 PID EA60.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Crownstone" }, - "usb_config_option": { - "data": { - "usb_path": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Crownstone. \n\n\u0414\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 VID 10C4 \u0438 PID EA60.", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Crownstone" - }, "usb_manual_config": { "data": { "usb_manual_path": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" @@ -70,26 +63,12 @@ "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Crownstone.", "title": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Crownstone" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Crownstone.", - "title": "\u041f\u0443\u0442\u044c \u043a USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Crownstone" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 Crownstone Sphere, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e.", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 Crownstone Sphere, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f USB-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e.", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/crownstone/translations/tr.json b/homeassistant/components/crownstone/translations/tr.json index 1c97cbdf170..d244ed239a3 100644 --- a/homeassistant/components/crownstone/translations/tr.json +++ b/homeassistant/components/crownstone/translations/tr.json @@ -56,13 +56,6 @@ "description": "Crownstone USB donan\u0131m kilidinin seri ba\u011flant\u0131 noktas\u0131n\u0131 se\u00e7in. \n\n VID 10C4 ve PID EA60'a sahip bir cihaz aray\u0131n.", "title": "Crownstone USB dongle yap\u0131land\u0131rmas\u0131" }, - "usb_config_option": { - "data": { - "usb_path": "USB Cihaz Yolu" - }, - "description": "Crownstone USB donan\u0131m kilidinin seri ba\u011flant\u0131 noktas\u0131n\u0131 se\u00e7in. \n\n VID 10C4 ve PID EA60'a sahip bir cihaz aray\u0131n.", - "title": "Crownstone USB dongle yap\u0131land\u0131rmas\u0131" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB Cihaz Yolu" @@ -70,26 +63,12 @@ "description": "Crownstone USB dongle'\u0131n yolunu manuel olarak girin.", "title": "Crownstone USB dongle manuel yolu" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB Cihaz Yolu" - }, - "description": "Crownstone USB dongle'\u0131n yolunu manuel olarak girin.", - "title": "Crownstone USB dongle manuel yolu" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Stick" }, "description": "USB'nin bulundu\u011fu bir Crownstone Stick se\u00e7in.", "title": "Crownstone USB Stick" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Stick" - }, - "description": "USB'nin bulundu\u011fu bir Crownstone Stick se\u00e7in.", - "title": "Crownstone USB Stick" } } } diff --git a/homeassistant/components/crownstone/translations/zh-Hant.json b/homeassistant/components/crownstone/translations/zh-Hant.json index 2c362ba0bcb..32ef596aba3 100644 --- a/homeassistant/components/crownstone/translations/zh-Hant.json +++ b/homeassistant/components/crownstone/translations/zh-Hant.json @@ -56,13 +56,6 @@ "description": "\u9078\u64c7 Crownstone USB \u88dd\u7f6e\u5e8f\u5217\u57e0\u3002\n\n\u8acb\u641c\u5c0b VID 10C4 \u53ca PID EA60 \u88dd\u7f6e\u3002", "title": "Crownstone USB \u88dd\u7f6e\u8a2d\u5b9a" }, - "usb_config_option": { - "data": { - "usb_path": "USB \u88dd\u7f6e\u8def\u5f91" - }, - "description": "\u9078\u64c7 Crownstone USB \u88dd\u7f6e\u5e8f\u5217\u57e0\u3002\n\n\u8acb\u641c\u5c0b VID 10C4 \u53ca PID EA60 \u88dd\u7f6e\u3002", - "title": "Crownstone USB \u88dd\u7f6e\u8a2d\u5b9a" - }, "usb_manual_config": { "data": { "usb_manual_path": "USB \u88dd\u7f6e\u8def\u5f91" @@ -70,26 +63,12 @@ "description": "\u624b\u52d5\u8f38\u5165 Crownstone USB \u88dd\u7f6e\u8def\u5f91\u3002", "title": "Crownstone USB \u88dd\u7f6e\u624b\u52d5\u8def\u5f91" }, - "usb_manual_config_option": { - "data": { - "usb_manual_path": "USB \u88dd\u7f6e\u8def\u5f91" - }, - "description": "\u624b\u52d5\u8f38\u5165 Crownstone USB \u88dd\u7f6e\u8def\u5f91\u3002", - "title": "Crownstone USB \u88dd\u7f6e\u624b\u52d5\u8def\u5f91" - }, "usb_sphere_config": { "data": { "usb_sphere": "Crownstone Sphere" }, "description": "\u9078\u64c7 Crownstone Sphere \u6240\u5728 USB \u8def\u5f91\u3002", "title": "Crownstone USB Sphere" - }, - "usb_sphere_config_option": { - "data": { - "usb_sphere": "Crownstone Sphere" - }, - "description": "\u9078\u64c7 Crownstone Sphere \u6240\u5728 USB \u8def\u5f91\u3002", - "title": "Crownstone USB Sphere" } } } diff --git a/homeassistant/components/deconz/translations/he.json b/homeassistant/components/deconz/translations/he.json index 3e2b350a0d9..97274180aef 100644 --- a/homeassistant/components/deconz/translations/he.json +++ b/homeassistant/components/deconz/translations/he.json @@ -12,7 +12,7 @@ "flow_title": "{host}", "step": { "link": { - "description": "\u05d1\u05d8\u05dc \u05d0\u05ea \u05e0\u05e2\u05d9\u05dc\u05ea \u05d4\u05de\u05e9\u05e8 deCONZ \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05e2\u05dd Home Assistant.\n\n 1. \u05e2\u05d1\u05d5\u05e8 \u05d0\u05dc \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05de\u05e2\u05e8\u05db\u05ea deCONZ \n .2 \u05dc\u05d7\u05e5 \u05e2\u05dc \"Unlock Gateway\"", + "description": "\u05d1\u05d8\u05dc \u05d0\u05ea \u05e0\u05e2\u05d9\u05dc\u05ea \u05d4\u05de\u05e9\u05e8 deCONZ \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05e2\u05dd Home Assistant.\n\n 1. \u05d9\u05e9 \u05dc\u05e2\u05d1\u05d5\u05e8 \u05d0\u05dc \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea deCONZ > \u05e9\u05e2\u05e8 > \u05de\u05ea\u05e7\u05d3\u05dd\n 2. \u05dc\u05d7\u05d9\u05e6\u05d4 \u05e2\u05dc \u05db\u05e4\u05ea\u05d5\u05e8 \"\u05d0\u05d9\u05de\u05d5\u05ea \u05d9\u05d9\u05e9\u05d5\u05dd\"", "title": "\u05e7\u05e9\u05e8 \u05e2\u05dd deCONZ" }, "manual_input": { diff --git a/homeassistant/components/deconz/translations/hu.json b/homeassistant/components/deconz/translations/hu.json index 4ffc1662ecb..47bb343247c 100644 --- a/homeassistant/components/deconz/translations/hu.json +++ b/homeassistant/components/deconz/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "A bridge m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "no_bridges": "Nem tal\u00e1lhat\u00f3 deCONZ \u00e1tj\u00e1r\u00f3", "no_hardware_available": "Nincs deCONZ-hoz csatlakoztatott r\u00e1di\u00f3hardver", "not_deconz_bridge": "Nem egy deCONZ \u00e1tj\u00e1r\u00f3", diff --git a/homeassistant/components/deluge/translations/bg.json b/homeassistant/components/deluge/translations/bg.json new file mode 100644 index 00000000000..2a0cd494422 --- /dev/null +++ b/homeassistant/components/deluge/translations/bg.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "user": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "port": "\u041f\u043e\u0440\u0442", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/ca.json b/homeassistant/components/deluge/translations/ca.json new file mode 100644 index 00000000000..df0a2a31ee4 --- /dev/null +++ b/homeassistant/components/deluge/translations/ca.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "host": "Amfitri\u00f3", + "password": "Contrasenya", + "port": "Port", + "username": "Nom d'usuari", + "web_port": "Port web (per al servei de visita)" + }, + "description": "Per poder utilitzar aquesta integraci\u00f3, has d'activar l'opci\u00f3 seg\u00fcent a la configuraci\u00f3 de Deluge: Daemon > Permet controls remots" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/cs.json b/homeassistant/components/deluge/translations/cs.json new file mode 100644 index 00000000000..2845835ef00 --- /dev/null +++ b/homeassistant/components/deluge/translations/cs.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" + }, + "step": { + "user": { + "data": { + "host": "Hostitel", + "password": "Heslo", + "port": "Port", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/de.json b/homeassistant/components/deluge/translations/de.json new file mode 100644 index 00000000000..9e8d559a523 --- /dev/null +++ b/homeassistant/components/deluge/translations/de.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Passwort", + "port": "Port", + "username": "Benutzername", + "web_port": "Webport (f\u00fcr Besuchsdienste)" + }, + "description": "Um diese Integration nutzen zu k\u00f6nnen, musst du die folgende Option in den Deluge-Einstellungen aktivieren: Daemon > Fernsteuerungen zulassen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/el.json b/homeassistant/components/deluge/translations/el.json new file mode 100644 index 00000000000..48645a7378a --- /dev/null +++ b/homeassistant/components/deluge/translations/el.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "user": { + "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "port": "\u0398\u03cd\u03c1\u03b1", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "web_port": "\u0398\u03cd\u03c1\u03b1 \u0399\u03c3\u03c4\u03bf\u03cd (\u03b3\u03b9\u03b1 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03b5\u03c0\u03af\u03c3\u03ba\u03b5\u03c8\u03b7\u03c2)" + }, + "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bc\u03c0\u03bf\u03c1\u03ad\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7, \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03ba\u03cc\u03bb\u03bf\u03c5\u03b8\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03c4\u03b1\u03ba\u03bb\u03c5\u03c3\u03bc\u03bf\u03cd: Daemon > \u039d\u03b1 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03bf \u03b1\u03c0\u03bf\u03bc\u03b1\u03ba\u03c1\u03c5\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/en.json b/homeassistant/components/deluge/translations/en.json index a3a2f539126..3f4e4b0b445 100644 --- a/homeassistant/components/deluge/translations/en.json +++ b/homeassistant/components/deluge/translations/en.json @@ -1,23 +1,23 @@ { "config": { - "step": { - "user": { - "description": "To be able to use this integration, you have to enable the following option in deluge settings: Daemon > Allow remote controls", - "data": { - "host": "Host", - "username": "Username", - "password": "Password", - "port": "Port", - "web_port": "Web port (for visiting service)" - } - } + "abort": { + "already_configured": "Service is already configured" }, "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication" }, - "abort": { - "already_configured": "Service is already configured" + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Port", + "username": "Username", + "web_port": "Web port (for visiting service)" + }, + "description": "To be able to use this integration, you have to enable the following option in deluge settings: Daemon > Allow remote controls" + } } } } \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/et.json b/homeassistant/components/deluge/translations/et.json new file mode 100644 index 00000000000..26d8d23438d --- /dev/null +++ b/homeassistant/components/deluge/translations/et.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba seadistatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Salas\u00f5na", + "port": "Port", + "username": "Kasutajanimi", + "web_port": "Veebiport (teenuse k\u00fclastamiseks)" + }, + "description": "Selle sidumise kasutamiseks deluge s\u00e4tetes lubama j\u00e4rgmise suvandi: Daemon > Luba kaugjuhtimispuldid" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/fr.json b/homeassistant/components/deluge/translations/fr.json new file mode 100644 index 00000000000..4607d78f78c --- /dev/null +++ b/homeassistant/components/deluge/translations/fr.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide" + }, + "step": { + "user": { + "data": { + "host": "H\u00f4te", + "password": "Mot de passe", + "port": "Port", + "username": "Nom d'utilisateur", + "web_port": "Port web (pour consulter le service)" + }, + "description": "Afin de pouvoir utiliser cette int\u00e9gration, vous devez activer l'option suivante dans les param\u00e8tres de Deluge\u00a0: D\u00e9mon > Autoriser les connexion \u00e0 distance" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/he.json b/homeassistant/components/deluge/translations/he.json new file mode 100644 index 00000000000..80971c19dfc --- /dev/null +++ b/homeassistant/components/deluge/translations/he.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "user": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7", + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "port": "\u05e4\u05ea\u05d7\u05d4", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/hu.json b/homeassistant/components/deluge/translations/hu.json new file mode 100644 index 00000000000..6058a985c55 --- /dev/null +++ b/homeassistant/components/deluge/translations/hu.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" + }, + "step": { + "user": { + "data": { + "host": "C\u00edm", + "password": "Jelsz\u00f3", + "port": "Port", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v", + "web_port": "Web port (a szolg\u00e1ltat\u00e1s l\u00e1togat\u00e1s\u00e1hoz)" + }, + "description": "Ahhoz, hogy ezt az integr\u00e1ci\u00f3t haszn\u00e1lni tudja, enged\u00e9lyeznie kell a k\u00f6vetkez\u0151 opci\u00f3t a be\u00e1ll\u00edt\u00e1sokban: Daemon > Allow remote controls" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/id.json b/homeassistant/components/deluge/translations/id.json new file mode 100644 index 00000000000..8a2fce22fd5 --- /dev/null +++ b/homeassistant/components/deluge/translations/id.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Kata Sandi", + "port": "Port", + "username": "Nama Pengguna", + "web_port": "Port web (untuk mengunjungi layanan)" + }, + "description": "Untuk dapat menggunakan integrasi ini, Anda harus mengaktifkan opsi berikut dalam pengaturan deluge: Daemon > Izinkan kendali jarak jauh" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/it.json b/homeassistant/components/deluge/translations/it.json new file mode 100644 index 00000000000..d9407f0c29f --- /dev/null +++ b/homeassistant/components/deluge/translations/it.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Porta", + "username": "Nome utente", + "web_port": "Porta web (per il servizio di visita)" + }, + "description": "Per poter utilizzare questa integrazione, devi abilitare la seguente opzione nelle impostazioni di diluvio: Demone > Consenti controlli " + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/ja.json b/homeassistant/components/deluge/translations/ja.json new file mode 100644 index 00000000000..ab796327717 --- /dev/null +++ b/homeassistant/components/deluge/translations/ja.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + }, + "step": { + "user": { + "data": { + "host": "\u30db\u30b9\u30c8", + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "port": "\u30dd\u30fc\u30c8", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d", + "web_port": "Web\u30dd\u30fc\u30c8\uff08\u8a2a\u554f\u30b5\u30fc\u30d3\u30b9\u7528\uff09" + }, + "description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u5229\u7528\u3059\u308b\u305f\u3081\u306b\u306f\u3001deluge\u306e\u8a2d\u5b9a\u3067\u4ee5\u4e0b\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u6709\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30c7\u30fc\u30e2\u30f3 -> \u30ea\u30e2\u30fc\u30c8\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u306e\u8a31\u53ef" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/nl.json b/homeassistant/components/deluge/translations/nl.json new file mode 100644 index 00000000000..6c824130708 --- /dev/null +++ b/homeassistant/components/deluge/translations/nl.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Wachtwoord", + "port": "Poort", + "username": "Gebruikersnaam", + "web_port": "Webpoort (voor bezoekdienst)" + }, + "description": "Om deze integratie te kunnen gebruiken, moet u de volgende optie inschakelen in de deluge instellingen: Daemon > Afstandsbediening toestaan" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/no.json b/homeassistant/components/deluge/translations/no.json new file mode 100644 index 00000000000..02d22dfb1a5 --- /dev/null +++ b/homeassistant/components/deluge/translations/no.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning" + }, + "step": { + "user": { + "data": { + "host": "Vert", + "password": "Passord", + "port": "Port", + "username": "Brukernavn", + "web_port": "Webport (for bes\u00f8kstjeneste)" + }, + "description": "For \u00e5 kunne bruke denne integrasjonen, m\u00e5 du aktivere f\u00f8lgende alternativ i deluge-innstillingene: Daemon > Tillat fjernkontroller" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/pl.json b/homeassistant/components/deluge/translations/pl.json new file mode 100644 index 00000000000..64269b9ab36 --- /dev/null +++ b/homeassistant/components/deluge/translations/pl.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie" + }, + "step": { + "user": { + "data": { + "host": "Nazwa hosta lub adres IP", + "password": "Has\u0142o", + "port": "Port", + "username": "Nazwa u\u017cytkownika", + "web_port": "Port WWW (do odwiedzania us\u0142ugi)" + }, + "description": "Aby m\u00f3c korzysta\u0107 z tej integracji, musisz w\u0142\u0105czy\u0107 nast\u0119puj\u0105c\u0105 opcj\u0119 w ustawieniach Deluge: Daemon > Zezw\u00f3l na zdalne sterowanie" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/pt-BR.json b/homeassistant/components/deluge/translations/pt-BR.json new file mode 100644 index 00000000000..ba45a44117b --- /dev/null +++ b/homeassistant/components/deluge/translations/pt-BR.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "host": "Nome do host", + "password": "Senha", + "port": "Porta", + "username": "Usu\u00e1rio", + "web_port": "Porta Web (para servi\u00e7o de visita)" + }, + "description": "Para poder usar essa integra\u00e7\u00e3o, voc\u00ea deve habilitar a seguinte op\u00e7\u00e3o nas configura\u00e7\u00f5es de Deluge: Daemon > Permitir controles remotos" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/ru.json b/homeassistant/components/deluge/translations/ru.json new file mode 100644 index 00000000000..04ab9df50e5 --- /dev/null +++ b/homeassistant/components/deluge/translations/ru.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." + }, + "step": { + "user": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "port": "\u041f\u043e\u0440\u0442", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "web_port": "\u0412\u0435\u0431-\u043f\u043e\u0440\u0442 (\u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441\u043b\u0443\u0436\u0431\u044b)" + }, + "description": "\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439, \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0439\u0442\u0435 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 Deluge \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435: Daemon > Allow remote controls." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/tr.json b/homeassistant/components/deluge/translations/tr.json new file mode 100644 index 00000000000..0cf6d9ffe35 --- /dev/null +++ b/homeassistant/components/deluge/translations/tr.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" + }, + "step": { + "user": { + "data": { + "host": "Sunucu", + "password": "Parola", + "port": "Port", + "username": "Kullan\u0131c\u0131 Ad\u0131", + "web_port": "Web ba\u011flant\u0131 noktas\u0131 (ziyaret hizmeti i\u00e7in)" + }, + "description": "Bu entegrasyonu kullanabilmek i\u00e7in, deluge ayarlar\u0131nda a\u015fa\u011f\u0131daki se\u00e7ene\u011fi etkinle\u015ftirmeniz gerekir: Daemon > Uzaktan kontrollere izin ver" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/zh-Hant.json b/homeassistant/components/deluge/translations/zh-Hant.json new file mode 100644 index 00000000000..c9ad139b2dc --- /dev/null +++ b/homeassistant/components/deluge/translations/zh-Hant.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" + }, + "step": { + "user": { + "data": { + "host": "\u4e3b\u6a5f\u7aef", + "password": "\u5bc6\u78bc", + "port": "\u901a\u8a0a\u57e0", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "web_port": "Web \u901a\u8a0a\u57e0\uff08\u8a2a\u554f\u670d\u52d9\uff09" + }, + "description": "\u6b32\u4f7f\u7528\u6b64\u6574\u5408\uff0c\u5fc5\u9808\u5148\u5728 deluge \u8a2d\u5b9a\u4e2d\u958b\u555f\u4ee5\u4e0b\u9078\u9805\uff1aDaemon > \u5141\u8a31\u9060\u7aef\u5b58\u53d6" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/demo/translations/it.json b/homeassistant/components/demo/translations/it.json index 0fb3f52b916..1c94dea053f 100644 --- a/homeassistant/components/demo/translations/it.json +++ b/homeassistant/components/demo/translations/it.json @@ -3,8 +3,8 @@ "step": { "init": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" } }, "options_1": { diff --git a/homeassistant/components/denonavr/translations/bg.json b/homeassistant/components/denonavr/translations/bg.json index 6ec6215c6e1..4b4384d0bc9 100644 --- a/homeassistant/components/denonavr/translations/bg.json +++ b/homeassistant/components/denonavr/translations/bg.json @@ -8,6 +8,9 @@ "user": { "data": { "host": "IP \u0430\u0434\u0440\u0435\u0441" + }, + "data_description": { + "host": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435" } } } diff --git a/homeassistant/components/denonavr/translations/ca.json b/homeassistant/components/denonavr/translations/ca.json index f499c6a0a17..11065514ce4 100644 --- a/homeassistant/components/denonavr/translations/ca.json +++ b/homeassistant/components/denonavr/translations/ca.json @@ -27,6 +27,9 @@ "data": { "host": "Adre\u00e7a IP" }, + "data_description": { + "host": "Deixeu-ho en blanc per utilitzar descobriment autom\u00e0tic" + }, "description": "Connecta el teu receptor, si no es configura l'adre\u00e7a IP, s'utilitza el descobriment autom\u00e0tic", "title": "Receptors de xarxa AVR de Denon" } diff --git a/homeassistant/components/denonavr/translations/de.json b/homeassistant/components/denonavr/translations/de.json index 414bd798f62..1c9a1a8ec95 100644 --- a/homeassistant/components/denonavr/translations/de.json +++ b/homeassistant/components/denonavr/translations/de.json @@ -27,6 +27,9 @@ "data": { "host": "IP-Adresse" }, + "data_description": { + "host": "Leer lassen, um automatische Erkennung zu verwenden" + }, "description": "Verbinde dich mit deinem Receiver, wenn die IP-Adresse nicht eingestellt ist, wird die automatische Erkennung verwendet", "title": "Denon AVR-Netzwerk-Receiver" } diff --git a/homeassistant/components/denonavr/translations/el.json b/homeassistant/components/denonavr/translations/el.json index 180cacc2459..d176fd944d6 100644 --- a/homeassistant/components/denonavr/translations/el.json +++ b/homeassistant/components/denonavr/translations/el.json @@ -27,6 +27,9 @@ "data": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" }, + "data_description": { + "host": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7" + }, "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b4\u03ad\u03ba\u03c4\u03b7 \u03c3\u03b1\u03c2, \u03b5\u03ac\u03bd \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", "title": "\u0394\u03ad\u03ba\u03c4\u03b5\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/en.json b/homeassistant/components/denonavr/translations/en.json index 383ae5a502c..2d937856b1c 100644 --- a/homeassistant/components/denonavr/translations/en.json +++ b/homeassistant/components/denonavr/translations/en.json @@ -27,6 +27,9 @@ "data": { "host": "IP Address" }, + "data_description": { + "host": "Leave blank to use auto-discovery" + }, "description": "Connect to your receiver, if the IP address is not set, auto-discovery is used", "title": "Denon AVR Network Receivers" } diff --git a/homeassistant/components/denonavr/translations/et.json b/homeassistant/components/denonavr/translations/et.json index 5dc9f3cc771..f133aaf9dd7 100644 --- a/homeassistant/components/denonavr/translations/et.json +++ b/homeassistant/components/denonavr/translations/et.json @@ -27,6 +27,9 @@ "data": { "host": "IP aadress" }, + "data_description": { + "host": "Automaatse avastamise kasutamiseks j\u00e4ta v\u00e4li t\u00fchjaks." + }, "description": "Kui IP-aadressi pole m\u00e4\u00e4ratud, kasutatakse automaatset avastamist", "title": "" } diff --git a/homeassistant/components/denonavr/translations/fr.json b/homeassistant/components/denonavr/translations/fr.json index 474f02f5e21..27a72477164 100644 --- a/homeassistant/components/denonavr/translations/fr.json +++ b/homeassistant/components/denonavr/translations/fr.json @@ -27,6 +27,9 @@ "data": { "host": "Adresse IP" }, + "data_description": { + "host": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique" + }, "description": "Connectez-vous \u00e0 votre r\u00e9cepteur, si l'adresse IP n'est pas d\u00e9finie, la d\u00e9tection automatique est utilis\u00e9e", "title": "R\u00e9cepteurs r\u00e9seaux Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/hu.json b/homeassistant/components/denonavr/translations/hu.json index 874f190ff01..6891d18a9c4 100644 --- a/homeassistant/components/denonavr/translations/hu.json +++ b/homeassistant/components/denonavr/translations/hu.json @@ -2,9 +2,9 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Nem siker\u00fclt csatlakozni, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja \u00fajra. A h\u00e1l\u00f3zati \u00e9s Ethernet k\u00e1belek kih\u00faz\u00e1sa \u00e9s \u00fajracsatlakoztat\u00e1sa seg\u00edthet", - "not_denonavr_manufacturer": "Nem egy Denon AVR h\u00e1l\u00f3zati vev\u0151, felfedezett gy\u00e1rt\u00f3 nem egyezik", + "not_denonavr_manufacturer": "Nem egy Denon AVR h\u00e1l\u00f3zati vev\u0151, a felfedezett gy\u00e1rt\u00f3n\u00e9v nem megfelel\u0151", "not_denonavr_missing": "Nem Denon AVR h\u00e1l\u00f3zati vev\u0151, a felfedez\u00e9si inform\u00e1ci\u00f3k nem teljesek" }, "error": { @@ -27,6 +27,9 @@ "data": { "host": "IP c\u00edm" }, + "data_description": { + "host": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen" + }, "description": "Csatlakozzon a vev\u0151h\u00f6z, ha az IP-c\u00edm nincs be\u00e1ll\u00edtva, az automatikus felder\u00edt\u00e9st haszn\u00e1lja", "title": "Denon AVR h\u00e1l\u00f3zati vev\u0151k\u00e9sz\u00fcl\u00e9kek" } diff --git a/homeassistant/components/denonavr/translations/id.json b/homeassistant/components/denonavr/translations/id.json index b2543d1d877..50a28ef5447 100644 --- a/homeassistant/components/denonavr/translations/id.json +++ b/homeassistant/components/denonavr/translations/id.json @@ -27,6 +27,9 @@ "data": { "host": "Alamat IP" }, + "data_description": { + "host": "Kosongkan untuk menggunakan penemuan otomatis" + }, "description": "Hubungkan ke Receiver Anda. Jika alamat IP tidak ditentukan, penemuan otomatis akan digunakan", "title": "Network Receiver Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/it.json b/homeassistant/components/denonavr/translations/it.json index 76d0d627ca3..23fffd3ab44 100644 --- a/homeassistant/components/denonavr/translations/it.json +++ b/homeassistant/components/denonavr/translations/it.json @@ -27,6 +27,9 @@ "data": { "host": "Indirizzo IP" }, + "data_description": { + "host": "Lascia vuoto per usare il rilevamento automatico" + }, "description": "Collega il ricevitore, se l'indirizzo IP non \u00e8 impostato, sar\u00e0 utilizzato il rilevamento automatico", "title": "Ricevitori di rete Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/ja.json b/homeassistant/components/denonavr/translations/ja.json index 1a5b41a3368..cd3198b7e10 100644 --- a/homeassistant/components/denonavr/translations/ja.json +++ b/homeassistant/components/denonavr/translations/ja.json @@ -27,6 +27,9 @@ "data": { "host": "IP\u30a2\u30c9\u30ec\u30b9" }, + "data_description": { + "host": "\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059" + }, "description": "\u53d7\u4fe1\u6a5f\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", "title": "\u30c7\u30ce\u30f3(Denon)AVR\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ec\u30b7\u30fc\u30d0\u30fc" } diff --git a/homeassistant/components/denonavr/translations/nl.json b/homeassistant/components/denonavr/translations/nl.json index 47da10106f3..f03895452df 100644 --- a/homeassistant/components/denonavr/translations/nl.json +++ b/homeassistant/components/denonavr/translations/nl.json @@ -27,6 +27,9 @@ "data": { "host": "IP-adres" }, + "data_description": { + "host": "Leeg laten om auto-discovery te gebruiken" + }, "description": "Maak verbinding met uw ontvanger. Als het IP-adres niet is ingesteld, wordt automatische detectie gebruikt", "title": "Denon AVR Netwerk Ontvangers" } diff --git a/homeassistant/components/denonavr/translations/no.json b/homeassistant/components/denonavr/translations/no.json index 892cf8d5767..333c55a44f7 100644 --- a/homeassistant/components/denonavr/translations/no.json +++ b/homeassistant/components/denonavr/translations/no.json @@ -27,6 +27,9 @@ "data": { "host": "IP adresse" }, + "data_description": { + "host": "La feltet st\u00e5 tomt hvis du vil bruke automatisk s\u00f8k" + }, "description": "Koble til mottakeren, hvis IP-adressen ikke er angitt, brukes automatisk oppdagelse", "title": "Denon AVR Network Receivers" } diff --git a/homeassistant/components/denonavr/translations/pl.json b/homeassistant/components/denonavr/translations/pl.json index 6b09baf7d4c..054371c6ba1 100644 --- a/homeassistant/components/denonavr/translations/pl.json +++ b/homeassistant/components/denonavr/translations/pl.json @@ -27,6 +27,9 @@ "data": { "host": "Adres IP" }, + "data_description": { + "host": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania" + }, "description": "\u0141\u0105czenie z urz\u0105dzeniem, je\u015bli adres IP nie jest zdefiniowany, u\u017cywane jest automatyczne wykrywanie.", "title": "Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/pt-BR.json b/homeassistant/components/denonavr/translations/pt-BR.json index 084c7dd3c18..2a716dacdca 100644 --- a/homeassistant/components/denonavr/translations/pt-BR.json +++ b/homeassistant/components/denonavr/translations/pt-BR.json @@ -27,6 +27,9 @@ "data": { "host": "Endere\u00e7o IP" }, + "data_description": { + "host": "Deixe em branco para usar a descoberta autom\u00e1tica" + }, "description": "Conecte-se ao seu receptor, se o endere\u00e7o IP n\u00e3o estiver definido, a descoberta autom\u00e1tica ser\u00e1 usada", "title": "Receptores de rede Denon AVR" } diff --git a/homeassistant/components/denonavr/translations/ru.json b/homeassistant/components/denonavr/translations/ru.json index c1fb25a9889..1db49decaad 100644 --- a/homeassistant/components/denonavr/translations/ru.json +++ b/homeassistant/components/denonavr/translations/ru.json @@ -27,6 +27,9 @@ "data": { "host": "IP-\u0430\u0434\u0440\u0435\u0441" }, + "data_description": { + "host": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435" + }, "description": "\u0415\u0441\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435", "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" } diff --git a/homeassistant/components/denonavr/translations/tr.json b/homeassistant/components/denonavr/translations/tr.json index 2c32de293b8..046e535c621 100644 --- a/homeassistant/components/denonavr/translations/tr.json +++ b/homeassistant/components/denonavr/translations/tr.json @@ -27,6 +27,9 @@ "data": { "host": "IP Adresi" }, + "data_description": { + "host": "Otomatik bulmay\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n" + }, "description": "Al\u0131c\u0131n\u0131za ba\u011flan\u0131n, IP adresi ayarlanmazsa otomatik bulma kullan\u0131l\u0131r", "title": "Denon AVR A\u011f Al\u0131c\u0131lar\u0131" } diff --git a/homeassistant/components/denonavr/translations/zh-Hant.json b/homeassistant/components/denonavr/translations/zh-Hant.json index 073de46866d..1217a6d7b87 100644 --- a/homeassistant/components/denonavr/translations/zh-Hant.json +++ b/homeassistant/components/denonavr/translations/zh-Hant.json @@ -27,6 +27,9 @@ "data": { "host": "IP \u4f4d\u5740" }, + "data_description": { + "host": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22" + }, "description": "\u9023\u7dda\u81f3\u63a5\u6536\u5668\u3002\u5047\u5982\u672a\u8a2d\u5b9a IP \u4f4d\u5740\uff0c\u5c07\u4f7f\u7528\u81ea\u52d5\u63a2\u7d22\u3002", "title": "Denon AVR \u7db2\u8def\u63a5\u6536\u5668" } diff --git a/homeassistant/components/derivative/translations/bg.json b/homeassistant/components/derivative/translations/bg.json new file mode 100644 index 00000000000..14946d95fe0 --- /dev/null +++ b/homeassistant/components/derivative/translations/bg.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u0418\u043c\u0435" + } + }, + "options": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/ca.json b/homeassistant/components/derivative/translations/ca.json new file mode 100644 index 00000000000..3003b9349fd --- /dev/null +++ b/homeassistant/components/derivative/translations/ca.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nom", + "round": "Precisi\u00f3", + "source": "Sensor d'entrada", + "time_window": "Finestra de temps", + "unit_prefix": "Prefix m\u00e8tric", + "unit_time": "Unitat de temps" + }, + "data_description": { + "round": "Controla el nombre de d\u00edgits decimals a la sortida.", + "time_window": "Si s'estableix, el valor del sensor \u00e9s una mitjana m\u00f2bil ponderada en el temps de les derivades dins d'aquesta finestra.", + "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric i la unitat de temps de la derivada seleccionats." + }, + "description": "Crea un sensor que estima la derivada d'un altre sensor.", + "title": "Afegeix sensor derivatiu" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nom", + "round": "Precisi\u00f3", + "source": "Sensor d'entrada", + "time_window": "Finestra de temps", + "unit_prefix": "Prefix m\u00e8tric", + "unit_time": "Unitat de temps" + }, + "data_description": { + "round": "Controla el nombre de d\u00edgits decimals a la sortida.", + "time_window": "Si s'estableix, el valor del sensor \u00e9s una mitjana m\u00f2bil ponderada en el temps de les derivades dins d'aquesta finestra.", + "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric i la unitat de temps de la derivada seleccionats." + } + }, + "options": { + "data": { + "name": "Nom", + "round": "Precisi\u00f3", + "source": "Sensor d'entrada", + "time_window": "Finestra de temps", + "unit_prefix": "Prefix m\u00e8tric", + "unit_time": "Unitat de temps" + }, + "data_description": { + "round": "Controla el nombre de d\u00edgits decimals a la sortida.", + "time_window": "Si s'estableix, el valor del sensor \u00e9s una mitjana m\u00f2bil ponderada en el temps de les derivades dins d'aquesta finestra.", + "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric i la unitat de temps de la derivada seleccionats." + }, + "description": "Crea un sensor que estima la derivada d'un altre sensor." + } + } + }, + "title": "Sensor derivatiu" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/cs.json b/homeassistant/components/derivative/translations/cs.json new file mode 100644 index 00000000000..ee8ee0e6d86 --- /dev/null +++ b/homeassistant/components/derivative/translations/cs.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data_description": { + "unit_prefix": "." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/de.json b/homeassistant/components/derivative/translations/de.json new file mode 100644 index 00000000000..4e1dbc1929c --- /dev/null +++ b/homeassistant/components/derivative/translations/de.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Name", + "round": "Genauigkeit", + "source": "Eingangssensor", + "time_window": "Zeitfenster", + "unit_prefix": "Metrisches Pr\u00e4fix", + "unit_time": "Zeiteinheit" + }, + "data_description": { + "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe.", + "time_window": "Wenn gesetzt, ist der Sensorwert ein zeitgewichteter gleitender Durchschnitt von Ableitungen innerhalb dieses Fensters.", + "unit_prefix": "Die Ausgabe wird gem\u00e4\u00df dem ausgew\u00e4hlten metrischen Pr\u00e4fix und der Zeiteinheit der Ableitung skaliert." + }, + "description": "Erstelle einen Sensor, der die Ableitung eines Sensors sch\u00e4tzt.", + "title": "Ableitungssensor hinzuf\u00fcgen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Name", + "round": "Genauigkeit", + "source": "Eingangssensor", + "time_window": "Zeitfenster", + "unit_prefix": "Metrisches Pr\u00e4fix", + "unit_time": "Zeiteinheit" + }, + "data_description": { + "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe.", + "time_window": "Wenn gesetzt, ist der Sensorwert ein zeitgewichteter gleitender Durchschnitt von Ableitungen innerhalb dieses Fensters.", + "unit_prefix": "Die Ausgabe wird gem\u00e4\u00df dem ausgew\u00e4hlten metrischen Pr\u00e4fix und der Zeiteinheit der Ableitung skaliert.." + } + }, + "options": { + "data": { + "name": "Name", + "round": "Genauigkeit", + "source": "Eingangssensor", + "time_window": "Zeitfenster", + "unit_prefix": "Metrisches Pr\u00e4fix", + "unit_time": "Zeiteinheit" + }, + "data_description": { + "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe.", + "time_window": "Wenn gesetzt, ist der Sensorwert ein zeitgewichteter gleitender Durchschnitt von Ableitungen innerhalb dieses Fensters.", + "unit_prefix": "Die Ausgabe wird gem\u00e4\u00df dem ausgew\u00e4hlten metrischen Pr\u00e4fix und der Zeiteinheit der Ableitung skaliert.." + }, + "description": "Erstelle einen Sensor, der die Ableitung eines Sensors sch\u00e4tzt." + } + } + }, + "title": "Ableitungssensor" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/el.json b/homeassistant/components/derivative/translations/el.json new file mode 100644 index 00000000000..a5a19efc1e5 --- /dev/null +++ b/homeassistant/components/derivative/translations/el.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "time_window": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf", + "unit_prefix": "\u039c\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1", + "unit_time": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" + }, + "data_description": { + "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.", + "time_window": "\u0395\u03ac\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.", + "unit_prefix": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.\n\u0395\u03ac\u03bd \u03c4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 0, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.\n\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5.", + "title": "\u039d\u03ad\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 Derivative" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "time_window": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf", + "unit_prefix": "\u039c\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1", + "unit_time": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" + }, + "data_description": { + "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.", + "time_window": "\u0395\u03ac\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.", + "unit_prefix": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." + } + }, + "options": { + "data": { + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "time_window": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf", + "unit_prefix": "\u039c\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1", + "unit_time": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" + }, + "data_description": { + "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.", + "time_window": "\u0395\u03ac\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.", + "unit_prefix": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.\n\u0395\u03ac\u03bd \u03c4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 0, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.\n\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." + } + } + }, + "title": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/en.json b/homeassistant/components/derivative/translations/en.json index b91318b5237..884f1fa5244 100644 --- a/homeassistant/components/derivative/translations/en.json +++ b/homeassistant/components/derivative/translations/en.json @@ -36,6 +36,22 @@ "time_window": "If set, the sensor's value is a time weighted moving average of derivatives within this window.", "unit_prefix": "The output will be scaled according to the selected metric prefix and time unit of the derivative.." } + }, + "options": { + "data": { + "name": "Name", + "round": "Precision", + "source": "Input sensor", + "time_window": "Time window", + "unit_prefix": "Metric prefix", + "unit_time": "Time unit" + }, + "data_description": { + "round": "Controls the number of decimal digits in the output.", + "time_window": "If set, the sensor's value is a time weighted moving average of derivatives within this window.", + "unit_prefix": "The output will be scaled according to the selected metric prefix and time unit of the derivative.." + }, + "description": "Create a sensor that estimates the derivative of a sensor." } } }, diff --git a/homeassistant/components/derivative/translations/et.json b/homeassistant/components/derivative/translations/et.json new file mode 100644 index 00000000000..45c566fac9b --- /dev/null +++ b/homeassistant/components/derivative/translations/et.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nimi", + "round": "T\u00e4psus", + "source": "Sisendandur", + "time_window": "Ajavahemik", + "unit_prefix": "M\u00f5\u00f5diku eesliide", + "unit_time": "Aja\u00fchik" + }, + "data_description": { + "round": "K\u00fcmnendkohtade arv v\u00e4ljundis.", + "time_window": "Kui see on m\u00e4\u00e4ratud on anduri v\u00e4\u00e4rtus selle akna tuletisinstrumentide ajaga kaalutud liikuv keskmine.", + "unit_prefix": "Tuletis skaleeritakse vastavalt valitud meetrilisele eesliitele ja tuletise aja\u00fchikule." + }, + "description": "Loo andur mis hindab anduri tuletist.", + "title": "Lisa uus tuletisandur" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nimi", + "round": "T\u00e4psus", + "source": "Sisendandur", + "time_window": "Ajavahemik", + "unit_prefix": "M\u00f5\u00f5diku eesliide", + "unit_time": "Aja\u00fchik" + }, + "data_description": { + "round": "K\u00fcmnendkohtade arv v\u00e4ljundis.", + "time_window": "Kui see on m\u00e4\u00e4ratud on anduri v\u00e4\u00e4rtus selle akna tuletisinstrumentide ajaga kaalutud liikuv keskmine.", + "unit_prefix": "Tuletis skaleeritakse vastavalt valitud meetrilisele eesliitele ja tuletise aja\u00fchikule." + } + }, + "options": { + "data": { + "name": "Nimi", + "round": "T\u00e4psus", + "source": "Sisendandur", + "time_window": "Ajavahemik", + "unit_prefix": "M\u00f5\u00f5diku eesliide", + "unit_time": "Aja\u00fchik" + }, + "data_description": { + "round": "K\u00fcmnendkohtade arv v\u00e4ljundis.", + "time_window": "Kui see on m\u00e4\u00e4ratud, on anduri v\u00e4\u00e4rtus selle akna tuletisinstrumentide ajaga kaalutud liikuv keskmine.", + "unit_prefix": "Tuletis skaleeritakse vastavalt valitud meetrilisele eesliitele ja tuletise aja\u00fchikule." + }, + "description": "T\u00e4psus reguleerib k\u00fcmnendkohtade arvu v\u00e4ljundis.\n Kui ajaaken ei ole 0, on anduri v\u00e4\u00e4rtuseks aknas olevate tuletisinstrumentide ajaga kaalutud liikuv keskmine.\n Tuletist skaleeritakse vastavalt valitud meetrilise prefiksile ja tuletise aja\u00fchikule." + } + } + }, + "title": "Tuletisandur" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/fr.json b/homeassistant/components/derivative/translations/fr.json new file mode 100644 index 00000000000..d967a3abc89 --- /dev/null +++ b/homeassistant/components/derivative/translations/fr.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nom", + "round": "Pr\u00e9cision", + "source": "Capteur d'entr\u00e9e", + "time_window": "P\u00e9riode", + "unit_prefix": "Pr\u00e9fixe m\u00e9trique", + "unit_time": "Unit\u00e9 de temps" + }, + "data_description": { + "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie.", + "time_window": "Si d\u00e9finie, la valeur du capteur est une moyenne mobile pond\u00e9r\u00e9e dans le temps des d\u00e9riv\u00e9es dans cette p\u00e9riode.", + "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique et de l'unit\u00e9 de temps de la d\u00e9riv\u00e9e s\u00e9lectionn\u00e9s." + }, + "description": "Cr\u00e9ez un capteur calculant la d\u00e9riv\u00e9e d'un autre capteur.", + "title": "Ajouter un capteur de d\u00e9riv\u00e9e" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nom", + "round": "Pr\u00e9cision", + "source": "Capteur d'entr\u00e9e", + "time_window": "P\u00e9riode", + "unit_prefix": "Pr\u00e9fixe m\u00e9trique", + "unit_time": "Unit\u00e9 de temps" + }, + "data_description": { + "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie.", + "time_window": "Si d\u00e9finie, la valeur du capteur est une moyenne mobile pond\u00e9r\u00e9e dans le temps des d\u00e9riv\u00e9es dans cette p\u00e9riode.", + "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique et de l'unit\u00e9 de temps de la d\u00e9riv\u00e9e s\u00e9lectionn\u00e9s.." + } + }, + "options": { + "data": { + "name": "Nom", + "round": "Pr\u00e9cision", + "source": "Capteur d'entr\u00e9e", + "time_window": "P\u00e9riode", + "unit_prefix": "Pr\u00e9fixe m\u00e9trique", + "unit_time": "Unit\u00e9 de temps" + }, + "data_description": { + "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie.", + "time_window": "Si d\u00e9finie, la valeur du capteur est une moyenne mobile pond\u00e9r\u00e9e dans le temps des d\u00e9riv\u00e9es dans cette p\u00e9riode.", + "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique et de l'unit\u00e9 de temps de la d\u00e9riv\u00e9e s\u00e9lectionn\u00e9s." + }, + "description": "Cr\u00e9ez un capteur calculant la d\u00e9riv\u00e9e d'un autre capteur." + } + } + }, + "title": "Capteur de d\u00e9riv\u00e9e" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/he.json b/homeassistant/components/derivative/translations/he.json new file mode 100644 index 00000000000..317b836da6d --- /dev/null +++ b/homeassistant/components/derivative/translations/he.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "options": { + "data_description": { + "unit_prefix": "." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/hu.json b/homeassistant/components/derivative/translations/hu.json new file mode 100644 index 00000000000..e71175d2a32 --- /dev/null +++ b/homeassistant/components/derivative/translations/hu.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Elnevez\u00e9s", + "round": "Pontoss\u00e1g", + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "time_window": "Id\u0151ablak", + "unit_prefix": "M\u00e9rt\u00e9kegys\u00e9g el\u0151tag", + "unit_time": "Id\u0151egys\u00e9g" + }, + "data_description": { + "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma.", + "time_window": "Ha be van \u00e1ll\u00edtva, az \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az ablakon bel\u00fcli sz\u00e1rmaz\u00e9kok id\u0151vel s\u00falyozott mozg\u00f3\u00e1tlaga.", + "unit_prefix": "A sz\u00e1rmaz\u00e9kos \u00e9rt\u00e9k a sz\u00e1rmaztatott term\u00e9k kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9g el\u0151tagja \u00e9s id\u0151egys\u00e9ge szerint lesz sk\u00e1l\u00e1zva." + }, + "description": "Hozzon l\u00e9tre egy \u00e9rz\u00e9kel\u0151t, amely megbecs\u00fcli a forr\u00e1s \u00e9rz\u00e9kel\u0151 sz\u00e1rmaz\u00e9k\u00e1t.", + "title": "\u00daj sz\u00e1rmaz\u00e9kos \u00e9rz\u00e9kel\u0151" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Elnevez\u00e9s", + "round": "Pontoss\u00e1g", + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "time_window": "Id\u0151ablak", + "unit_prefix": "M\u00e9rt\u00e9kegys\u00e9g el\u0151tag", + "unit_time": "Id\u0151egys\u00e9g" + }, + "data_description": { + "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma.", + "time_window": "Ha be van \u00e1ll\u00edtva, az \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az ablakon bel\u00fcli sz\u00e1rmaz\u00e9kok id\u0151vel s\u00falyozott mozg\u00f3\u00e1tlaga.", + "unit_prefix": "A sz\u00e1rmaz\u00e9kos \u00e9rt\u00e9k a sz\u00e1rmaztatott term\u00e9k kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9g el\u0151tagja \u00e9s id\u0151egys\u00e9ge szerint lesz sk\u00e1l\u00e1zva.." + } + }, + "options": { + "data": { + "name": "Elnevez\u00e9s", + "round": "Pontoss\u00e1g", + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "time_window": "Id\u0151ablak", + "unit_prefix": "M\u00e9rt\u00e9kegys\u00e9g el\u0151tag", + "unit_time": "Id\u0151egys\u00e9g" + }, + "data_description": { + "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma.", + "time_window": "Ha be van \u00e1ll\u00edtva, az \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az ablakon bel\u00fcli sz\u00e1rmaz\u00e9kok id\u0151vel s\u00falyozott mozg\u00f3\u00e1tlaga.", + "unit_prefix": "A sz\u00e1rmaz\u00e9kos \u00e9rt\u00e9k a sz\u00e1rmaztatott term\u00e9k kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9g el\u0151tagja \u00e9s id\u0151egys\u00e9ge szerint lesz sk\u00e1l\u00e1zva.." + }, + "description": "Hozzon l\u00e9tre egy \u00e9rz\u00e9kel\u0151t, amely megbecs\u00fcli a forr\u00e1s \u00e9rz\u00e9kel\u0151 sz\u00e1rmaz\u00e9k\u00e1t." + } + } + }, + "title": "Sz\u00e1rmaz\u00e9kos \u00e9rz\u00e9kel\u0151" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/id.json b/homeassistant/components/derivative/translations/id.json new file mode 100644 index 00000000000..67d6752a182 --- /dev/null +++ b/homeassistant/components/derivative/translations/id.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nama", + "round": "Presisi", + "source": "Sensor input", + "time_window": "Jangka waktu", + "unit_prefix": "Prefiks metrik", + "unit_time": "Unit waktu" + }, + "data_description": { + "round": "Mengontrol jumlah digit desimal dalam output.", + "time_window": "Jika disetel, nilai sensor adalah rata-rata bergerak berbobot waktu dari turunan dalam jangka ini.", + "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih dan unit waktu turunan." + }, + "description": "Buat sensor yang memperkirakan nilai turunan dari sebuah sensor.", + "title": "Tambahkan sensor Turunan" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nama", + "round": "Presisi", + "source": "Sensor input", + "time_window": "Jangka waktu", + "unit_prefix": "Prefiks metrik", + "unit_time": "Unit waktu" + }, + "data_description": { + "round": "Mengontrol jumlah digit desimal dalam output.", + "time_window": "Jika disetel, nilai sensor adalah rata-rata bergerak berbobot waktu dari turunan dalam jangka ini.", + "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih dan unit waktu turunan.." + } + }, + "options": { + "data": { + "name": "Nama", + "round": "Presisi", + "source": "Sensor input", + "time_window": "Jangka waktu", + "unit_prefix": "Prefiks metrik", + "unit_time": "Unit waktu" + }, + "data_description": { + "round": "Mengontrol jumlah digit desimal dalam output.", + "time_window": "Jika disetel, nilai sensor adalah rata-rata bergerak berbobot waktu dari turunan dalam jangka ini.", + "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih dan unit waktu turunan.." + }, + "description": "Buat sensor yang memperkirakan nilai turunan dari sebuah sensor." + } + } + }, + "title": "Sensor Turunan" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/it.json b/homeassistant/components/derivative/translations/it.json new file mode 100644 index 00000000000..ad888e13745 --- /dev/null +++ b/homeassistant/components/derivative/translations/it.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nome", + "round": "Precisione", + "source": "Sensore di ingresso", + "time_window": "Finestra temporale", + "unit_prefix": "Prefisso metrico", + "unit_time": "Unit\u00e0 di tempo" + }, + "data_description": { + "round": "Controlla il numero di cifre decimali nell'uscita.", + "time_window": "Se impostato, il valore del sensore \u00e8 una media mobile delle derivate ponderata nel tempo all'interno di questa finestra.", + "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso metrico selezionato e all'unit\u00e0 di tempo della derivata." + }, + "description": "Crea un sensore che stimi la derivata di un sensore.", + "title": "Aggiungi sensore derivata" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nome", + "round": "Precisione", + "source": "Sensore di ingresso", + "time_window": "Finestra temporale", + "unit_prefix": "Prefisso metrico", + "unit_time": "Unit\u00e0 di tempo" + }, + "data_description": { + "round": "Controlla il numero di cifre decimali nell'uscita.", + "time_window": "Se impostato, il valore del sensore \u00e8 una media mobile delle derivate ponderata nel tempo all'interno di questa finestra.", + "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso metrico selezionato e all'unit\u00e0 di tempo della derivata.." + } + }, + "options": { + "data": { + "name": "Nome", + "round": "Precisione", + "source": "Sensore di ingresso", + "time_window": "Finestra temporale", + "unit_prefix": "Prefisso metrico", + "unit_time": "Unit\u00e0 di tempo" + }, + "data_description": { + "round": "Controlla il numero di cifre decimali nell'uscita.", + "time_window": "Se impostato, il valore del sensore \u00e8 una media mobile delle derivate ponderata nel tempo all'interno di questa finestra.", + "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso metrico selezionato e all'unit\u00e0 di tempo della derivata.." + }, + "description": "Crea un sensore che stimi la derivata di un sensore." + } + } + }, + "title": "Sensore derivata" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/ja.json b/homeassistant/components/derivative/translations/ja.json new file mode 100644 index 00000000000..10232f996d6 --- /dev/null +++ b/homeassistant/components/derivative/translations/ja.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u540d\u524d", + "round": "\u7cbe\u5ea6", + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "time_window": "\u6642\u9593\u8ef8", + "unit_prefix": "\u30e1\u30c8\u30ea\u30c3\u30af\u63a5\u982d\u8f9e", + "unit_time": "\u6642\u9593\u5358\u4f4d" + }, + "data_description": { + "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002", + "time_window": "\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u3068\u306a\u308a\u307e\u3059\u3002", + "unit_prefix": "\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002" + }, + "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002\n\u6642\u9593\u7a93\u304c0\u3067\u306a\u3044\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u7a93\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u306b\u306a\u308a\u307e\u3059\u3002\n\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002", + "title": "\u65b0\u3057\u3044\u6d3e\u751f(Derivative)\u30bb\u30f3\u30b5\u30fc" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u540d\u524d", + "round": "\u7cbe\u5ea6", + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "time_window": "\u6642\u9593\u8ef8", + "unit_prefix": "\u30e1\u30c8\u30ea\u30c3\u30af\u63a5\u982d\u8f9e", + "unit_time": "\u6642\u9593\u5358\u4f4d" + }, + "data_description": { + "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002", + "time_window": "\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u3068\u306a\u308a\u307e\u3059\u3002", + "unit_prefix": "\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002." + } + }, + "options": { + "data": { + "name": "\u540d\u524d", + "round": "\u7cbe\u5ea6", + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "time_window": "\u6642\u9593\u8ef8", + "unit_prefix": "\u30e1\u30c8\u30ea\u30c3\u30af\u63a5\u982d\u8f9e", + "unit_time": "\u6642\u9593\u5358\u4f4d" + }, + "data_description": { + "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002", + "time_window": "\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u3068\u306a\u308a\u307e\u3059\u3002", + "unit_prefix": "\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002." + }, + "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002\n\u6642\u9593\u7a93\u304c0\u3067\u306a\u3044\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u7a93\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u306b\u306a\u308a\u307e\u3059\u3002\n\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002" + } + } + }, + "title": "\u6d3e\u751f(Derivative)\u30bb\u30f3\u30b5\u30fc" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/nl.json b/homeassistant/components/derivative/translations/nl.json new file mode 100644 index 00000000000..8b7cf5a9402 --- /dev/null +++ b/homeassistant/components/derivative/translations/nl.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Naam", + "round": "Precisie", + "source": "Invoer sensor", + "time_window": "Tijdsvenster", + "unit_prefix": "Metrisch voorvoegsel", + "unit_time": "Tijdseenheid" + }, + "data_description": { + "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", + "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide" + }, + "description": "Maak een sensor die de afgeleide van een sensor schat.", + "title": "Voeg afgeleide sensor toe" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Naam", + "round": "Precisie", + "source": "Invoer sensor", + "time_window": "Tijdsvenster", + "unit_prefix": "Metrisch voorvoegsel", + "unit_time": "Tijdseenheid" + }, + "data_description": { + "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", + "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." + } + }, + "options": { + "data": { + "name": "Naam", + "round": "Precisie", + "source": "Invoer sensor", + "time_window": "Tijdsvenster", + "unit_prefix": "Metrisch voorvoegsel", + "unit_time": "Tijdseenheid" + }, + "data_description": { + "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", + "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." + }, + "description": "Maak een sensor die de afgeleide van een sensor schat." + } + } + }, + "title": "Derivatieve sensor" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/no.json b/homeassistant/components/derivative/translations/no.json new file mode 100644 index 00000000000..735f1fae6ae --- /dev/null +++ b/homeassistant/components/derivative/translations/no.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Navn", + "round": "Presisjon", + "source": "Inngangssensor", + "time_window": "Tidsvindu", + "unit_prefix": "Metrisk prefiks", + "unit_time": "Tidsenhet" + }, + "data_description": { + "round": "Styrer antall desimaler i utdataene.", + "time_window": "Hvis den er angitt, er sensorens verdi et tidsvektet glidende gjennomsnitt av derivater i dette vinduet.", + "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset og tidsenheten til den deriverte." + }, + "description": "Lag en sensor som estimerer den deriverte av en sensor.", + "title": "Legg til derivatsensor" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Navn", + "round": "Presisjon", + "source": "Inngangssensor", + "time_window": "Tidsvindu", + "unit_prefix": "Metrisk prefiks", + "unit_time": "Tidsenhet" + }, + "data_description": { + "round": "Styrer antall desimaler i utdataene.", + "time_window": "Hvis den er angitt, er sensorens verdi et tidsvektet glidende gjennomsnitt av derivater i dette vinduet.", + "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset og tidsenheten til den deriverte.." + } + }, + "options": { + "data": { + "name": "Navn", + "round": "Presisjon", + "source": "Inngangssensor", + "time_window": "Tidsvindu", + "unit_prefix": "Metrisk prefiks", + "unit_time": "Tidsenhet" + }, + "data_description": { + "round": "Styrer antall desimaler i utdataene.", + "time_window": "Hvis den er angitt, er sensorens verdi et tidsvektet glidende gjennomsnitt av derivater i dette vinduet.", + "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset og tidsenheten til den deriverte.." + }, + "description": "Lag en sensor som estimerer den deriverte av en sensor." + } + } + }, + "title": "Avledet sensor" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/pl.json b/homeassistant/components/derivative/translations/pl.json new file mode 100644 index 00000000000..041d52ffeff --- /dev/null +++ b/homeassistant/components/derivative/translations/pl.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nazwa", + "round": "Precyzja", + "source": "Sensor wej\u015bciowy", + "time_window": "Okno czasowe", + "unit_prefix": "Prefiks metryczny", + "unit_time": "Jednostka czasu" + }, + "data_description": { + "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych.", + "time_window": "Je\u015bli jest ustawiona, warto\u015b\u0107 sensora jest wa\u017con\u0105 w czasie \u015bredni\u0105 ruchom\u0105 pochodnych w tym oknie.", + "unit_prefix": "Wynik b\u0119dzie skalowany zgodnie z wybranym prefiksem metrycznym i jednostk\u0105 czasu pochodnej." + }, + "description": "Tworzy sensor, kt\u00f3ry szacuje pochodn\u0105 sensora.", + "title": "Dodaj sensor pochodnej" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nazwa", + "round": "Precyzja", + "source": "Sensor wej\u015bciowy", + "time_window": "Okno czasowe", + "unit_prefix": "Prefiks metryczny", + "unit_time": "Jednostka czasu" + }, + "data_description": { + "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych.", + "time_window": "Je\u015bli jest ustawiona, warto\u015b\u0107 sensora jest wa\u017con\u0105 w czasie \u015bredni\u0105 ruchom\u0105 pochodnych w tym oknie.", + "unit_prefix": "Wynik b\u0119dzie skalowany zgodnie z wybranym prefiksem metrycznym i jednostk\u0105 czasu pochodnej." + } + }, + "options": { + "data": { + "name": "Nazwa", + "round": "Precyzja", + "source": "Sensor wej\u015bciowy", + "time_window": "Okno czasowe", + "unit_prefix": "Prefiks metryczny", + "unit_time": "Jednostka czasu" + }, + "data_description": { + "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych.", + "time_window": "Je\u015bli jest ustawiona, warto\u015b\u0107 sensora jest wa\u017con\u0105 w czasie \u015bredni\u0105 ruchom\u0105 pochodnych w tym oknie.", + "unit_prefix": "Wynik b\u0119dzie skalowany zgodnie z wybranym prefiksem metrycznym i jednostk\u0105 czasu pochodnej." + }, + "description": "Tworzy sensor, kt\u00f3ry szacuje pochodn\u0105 sensora." + } + } + }, + "title": "Sensor pochodnej" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/pt-BR.json b/homeassistant/components/derivative/translations/pt-BR.json new file mode 100644 index 00000000000..4d29a3970ee --- /dev/null +++ b/homeassistant/components/derivative/translations/pt-BR.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nome", + "round": "Precis\u00e3o", + "source": "Sensor de entrada", + "time_window": "Janela do tempo", + "unit_prefix": "Prefixo da m\u00e9trica", + "unit_time": "Unidade de tempo" + }, + "data_description": { + "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda.", + "time_window": "Se definido, o valor do sensor \u00e9 uma m\u00e9dia m\u00f3vel ponderada no tempo das derivadas dentro desta janela.", + "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado e a unidade de tempo da derivada." + }, + "description": "Crie um sensor que estime a derivada de um sensor.", + "title": "Adicionar sensor Derivative" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nome", + "round": "Precis\u00e3o", + "source": "Sensor de entrada", + "time_window": "Janela do tempo", + "unit_prefix": "Prefixo da m\u00e9trica", + "unit_time": "Unidade de tempo" + }, + "data_description": { + "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda.", + "time_window": "Se definido, o valor do sensor \u00e9 uma m\u00e9dia m\u00f3vel ponderada no tempo das derivadas dentro desta janela.", + "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado e a unidade de tempo da derivada.." + } + }, + "options": { + "data": { + "name": "Nome", + "round": "Precis\u00e3o", + "source": "Sensor de entrada", + "time_window": "Janela do tempo", + "unit_prefix": "Prefixo da m\u00e9trica", + "unit_time": "Unidade de tempo" + }, + "data_description": { + "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda.", + "time_window": "Se definido, o valor do sensor \u00e9 uma m\u00e9dia m\u00f3vel ponderada no tempo das derivadas dentro desta janela.", + "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado e a unidade de tempo da derivada.." + }, + "description": "Crie um sensor que estime a derivada de um sensor." + } + } + }, + "title": "Sensor derivativo" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/ru.json b/homeassistant/components/derivative/translations/ru.json new file mode 100644 index 00000000000..d7c62f070e1 --- /dev/null +++ b/homeassistant/components/derivative/translations/ru.json @@ -0,0 +1,53 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "time_window": "\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u043e\u043a\u043d\u043e", + "unit_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0438", + "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" + }, + "data_description": { + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." + }, + "title": "\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0430\u044f" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "time_window": "\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u043e\u043a\u043d\u043e", + "unit_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0438", + "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" + }, + "data_description": { + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", + "unit_prefix": "." + } + }, + "options": { + "data": { + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "time_window": "\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u043e\u043a\u043d\u043e", + "unit_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0438", + "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" + }, + "data_description": { + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", + "unit_prefix": "." + } + } + } + }, + "title": "\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0430\u044f" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/sv.json b/homeassistant/components/derivative/translations/sv.json new file mode 100644 index 00000000000..66dcd34b1d7 --- /dev/null +++ b/homeassistant/components/derivative/translations/sv.json @@ -0,0 +1,12 @@ +{ + "options": { + "step": { + "init": { + "data": { + "name": "Namn", + "round": "Precision" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/tr.json b/homeassistant/components/derivative/translations/tr.json new file mode 100644 index 00000000000..68016c74372 --- /dev/null +++ b/homeassistant/components/derivative/translations/tr.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Ad", + "round": "Hassas", + "source": "Giri\u015f sens\u00f6r\u00fc", + "time_window": "Zaman penceresi", + "unit_prefix": "Metrik \u00f6neki", + "unit_time": "Zaman birimi" + }, + "data_description": { + "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder.", + "time_window": "Ayarlan\u0131rsa, sens\u00f6r\u00fcn de\u011feri, bu penceredeki t\u00fcrevlerin zaman a\u011f\u0131rl\u0131kl\u0131 hareketli ortalamas\u0131d\u0131r.", + "unit_prefix": "\u00c7\u0131kt\u0131, t\u00fcrevin se\u00e7ilen metrik \u00f6nekine ve zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir." + }, + "description": "Bir sens\u00f6r\u00fcn t\u00fcrevini tahmin eden bir sens\u00f6r olu\u015fturun.", + "title": "T\u00fcrev sens\u00f6r\u00fc ekle" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Ad", + "round": "Hassas", + "source": "Giri\u015f sens\u00f6r\u00fc", + "time_window": "Zaman penceresi", + "unit_prefix": "Metrik \u00f6neki", + "unit_time": "Zaman birimi" + }, + "data_description": { + "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder.", + "time_window": "Ayarlan\u0131rsa, sens\u00f6r\u00fcn de\u011feri, bu penceredeki t\u00fcrevlerin zaman a\u011f\u0131rl\u0131kl\u0131 hareketli ortalamas\u0131d\u0131r.", + "unit_prefix": "\u00c7\u0131kt\u0131, t\u00fcrevin se\u00e7ilen metrik \u00f6nekine ve zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir.." + } + }, + "options": { + "data": { + "name": "Ad", + "round": "Hassas", + "source": "Giri\u015f sens\u00f6r\u00fc", + "time_window": "Zaman penceresi", + "unit_prefix": "Metrik \u00f6neki", + "unit_time": "Zaman birimi" + }, + "data_description": { + "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder.", + "time_window": "Ayarlan\u0131rsa, sens\u00f6r\u00fcn de\u011feri, bu penceredeki t\u00fcrevlerin zaman a\u011f\u0131rl\u0131kl\u0131 hareketli ortalamas\u0131d\u0131r.", + "unit_prefix": "\u00c7\u0131kt\u0131, t\u00fcrevin se\u00e7ilen metrik \u00f6nekine ve zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir.." + }, + "description": "Bir sens\u00f6r\u00fcn t\u00fcrevini tahmin eden bir sens\u00f6r olu\u015fturun." + } + } + }, + "title": "T\u00fcrev sens\u00f6r\u00fc" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/zh-Hans.json b/homeassistant/components/derivative/translations/zh-Hans.json new file mode 100644 index 00000000000..689f057dec0 --- /dev/null +++ b/homeassistant/components/derivative/translations/zh-Hans.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u540d\u79f0", + "round": "\u7cbe\u5ea6", + "source": "\u8f93\u5165\u4f20\u611f\u5668", + "time_window": "\u65f6\u95f4\u7a97\u53e3", + "unit_prefix": "\u5355\u4f4d\u524d\u7f00", + "unit_time": "\u65f6\u95f4\u5355\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002", + "time_window": "\u5982\u679c\u8bbe\u7f6e\uff0c\u4f20\u611f\u5668\u5c06\u8f93\u51fa\u6b64\u65f6\u95f4\u7a97\u53e3\u5185\u7684\u53d8\u5316\u7387\u6309\u7167\u201c\u65f6\u95f4\u52a0\u6743\u79fb\u52a8\u5e73\u5747\u6cd5\u201d\u5904\u7406\u540e\u7684\u503c\u3002", + "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u548c\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u4f30\u7b97\u53e6\u4e00\u4e2a\u4f20\u611f\u5668\u7684\u53d8\u5316\u7387\u3002", + "title": "\u6dfb\u52a0\u53d8\u5316\u7387\uff08\u5bfc\u6570\uff09\u4f20\u611f\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u540d\u79f0", + "round": "\u7cbe\u5ea6", + "source": "\u8f93\u5165\u4f20\u611f\u5668", + "time_window": "\u65f6\u95f4\u7a97\u53e3", + "unit_prefix": "\u5355\u4f4d\u524d\u7f00", + "unit_time": "\u65f6\u95f4\u5355\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002", + "time_window": "\u5982\u679c\u8bbe\u7f6e\uff0c\u4f20\u611f\u5668\u5c06\u8f93\u51fa\u6b64\u65f6\u95f4\u7a97\u53e3\u5185\u7684\u53d8\u5316\u7387\u6309\u7167\u201c\u65f6\u95f4\u52a0\u6743\u79fb\u52a8\u5e73\u5747\u6cd5\u201d\u5904\u7406\u540e\u7684\u503c\u3002", + "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u548c\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" + } + }, + "options": { + "data": { + "name": "\u540d\u79f0", + "round": "\u7cbe\u5ea6", + "source": "\u8f93\u5165\u4f20\u611f\u5668", + "time_window": "\u65f6\u95f4\u7a97\u53e3", + "unit_prefix": "\u5355\u4f4d\u524d\u7f00", + "unit_time": "\u65f6\u95f4\u5355\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002", + "time_window": "\u5982\u679c\u8bbe\u7f6e\uff0c\u4f20\u611f\u5668\u5c06\u8f93\u51fa\u6b64\u65f6\u95f4\u7a97\u53e3\u5185\u7684\u53d8\u5316\u7387\u6309\u7167\u201c\u65f6\u95f4\u52a0\u6743\u79fb\u52a8\u5e73\u5747\u6cd5\u201d\u5904\u7406\u540e\u7684\u503c\u3002", + "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u548c\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u4f30\u7b97\u53e6\u4e00\u4e2a\u4f20\u611f\u5668\u7684\u53d8\u5316\u7387\u3002" + } + } + }, + "title": "\u53d8\u5316\u7387\u4f20\u611f\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/zh-Hant.json b/homeassistant/components/derivative/translations/zh-Hant.json new file mode 100644 index 00000000000..3c100df8034 --- /dev/null +++ b/homeassistant/components/derivative/translations/zh-Hant.json @@ -0,0 +1,59 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u540d\u7a31", + "round": "\u6e96\u78ba\u5ea6", + "source": "\u8f38\u5165\u611f\u6e2c\u5668", + "time_window": "\u6642\u9593\u8996\u7a97", + "unit_prefix": "\u516c\u5236\u524d\u7db4", + "unit_time": "\u6642\u9593\u55ae\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002", + "time_window": "\u8a2d\u5b9a\u5f8c\u3001\u611f\u6e2c\u5668\u6578\u503c\u5c07\u70ba\u8996\u7a97\u5167\u5c0e\u6578\u7684\u6642\u9593\u52a0\u6b0a\u52a0\u6b0a\u79fb\u52d5\u5e73\u5747\u503c\u3002", + "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u53ca\u5c0e\u6578\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002" + }, + "description": "\u65b0\u589e\u9810\u4f30\u611f\u6e2c\u5668\u5c0e\u6578\u4e4b\u611f\u6e2c\u5668\u3002", + "title": "\u65b0\u589e\u5c0e\u6578\u611f\u6e2c\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u540d\u7a31", + "round": "\u6e96\u78ba\u5ea6", + "source": "\u8f38\u5165\u611f\u6e2c\u5668", + "time_window": "\u6642\u9593\u8996\u7a97", + "unit_prefix": "\u516c\u5236\u524d\u7db4", + "unit_time": "\u6642\u9593\u55ae\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002", + "time_window": "\u8a2d\u5b9a\u5f8c\u3001\u611f\u6e2c\u5668\u6578\u503c\u5c07\u70ba\u8996\u7a97\u5167\u5c0e\u6578\u7684\u6642\u9593\u52a0\u6b0a\u52a0\u6b0a\u79fb\u52d5\u5e73\u5747\u503c\u3002", + "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u53ca\u5c0e\u6578\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002." + } + }, + "options": { + "data": { + "name": "\u540d\u7a31", + "round": "\u6e96\u78ba\u5ea6", + "source": "\u8f38\u5165\u611f\u6e2c\u5668", + "time_window": "\u6642\u9593\u8996\u7a97", + "unit_prefix": "\u516c\u5236\u524d\u7db4", + "unit_time": "\u6642\u9593\u55ae\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002", + "time_window": "\u8a2d\u5b9a\u5f8c\u3001\u611f\u6e2c\u5668\u6578\u503c\u5c07\u70ba\u8996\u7a97\u5167\u5c0e\u6578\u7684\u6642\u9593\u52a0\u6b0a\u52a0\u6b0a\u79fb\u52d5\u5e73\u5747\u503c\u3002", + "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u53ca\u5c0e\u6578\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002" + }, + "description": "\u65b0\u589e\u9810\u4f30\u611f\u6e2c\u5668\u5c0e\u6578\u4e4b\u611f\u6e2c\u5668\u3002" + } + } + }, + "title": "\u5c0e\u6578\u611f\u6e2c\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/directv/translations/it.json b/homeassistant/components/directv/translations/it.json index 9fb0932c342..3653e3876c2 100644 --- a/homeassistant/components/directv/translations/it.json +++ b/homeassistant/components/directv/translations/it.json @@ -11,8 +11,8 @@ "step": { "ssdp_confirm": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "description": "Vuoi impostare {name} ?" }, diff --git a/homeassistant/components/discord/translations/bg.json b/homeassistant/components/discord/translations/bg.json new file mode 100644 index 00000000000..00faba1155f --- /dev/null +++ b/homeassistant/components/discord/translations/bg.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/ca.json b/homeassistant/components/discord/translations/ca.json new file mode 100644 index 00000000000..bd87ba1f8dc --- /dev/null +++ b/homeassistant/components/discord/translations/ca.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token d'API" + }, + "description": "Consulta la documentaci\u00f3 per obtenir la teva clau de bot de Discord. \n\n{url}" + }, + "user": { + "data": { + "api_token": "Token d'API" + }, + "description": "Consulta la documentaci\u00f3 per obtenir la teva clau de bot de Discord. \n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/de.json b/homeassistant/components/discord/translations/de.json new file mode 100644 index 00000000000..e4f745573c0 --- /dev/null +++ b/homeassistant/components/discord/translations/de.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API-Token" + }, + "description": "Weitere Informationen zum Abrufen deines Discord-Bot-Schl\u00fcssels findest du in der Dokumentation. \n\n {url}" + }, + "user": { + "data": { + "api_token": "API-Token" + }, + "description": "Weitere Informationen zum Abrufen deines Discord-Bot-Schl\u00fcssels findest du in der Dokumentation. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/el.json b/homeassistant/components/discord/translations/el.json new file mode 100644 index 00000000000..a090d61d3c4 --- /dev/null +++ b/homeassistant/components/discord/translations/el.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API" + }, + "description": "\u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd Discord bot. \n\n {url}" + }, + "user": { + "data": { + "api_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API" + }, + "description": "\u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd Discord bot. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/en.json b/homeassistant/components/discord/translations/en.json index 77e16e9312d..ee72905b4d3 100644 --- a/homeassistant/components/discord/translations/en.json +++ b/homeassistant/components/discord/translations/en.json @@ -10,13 +10,13 @@ "unknown": "Unexpected error" }, "step": { - "user": { + "reauth_confirm": { "data": { "api_token": "API Token" }, "description": "Refer to the documentation on getting your Discord bot key.\n\n{url}" }, - "reauth_confirm": { + "user": { "data": { "api_token": "API Token" }, diff --git a/homeassistant/components/discord/translations/et.json b/homeassistant/components/discord/translations/et.json new file mode 100644 index 00000000000..ba69a715841 --- /dev/null +++ b/homeassistant/components/discord/translations/et.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API v\u00f5ti" + }, + "description": "Vaata oma Discordi roboti v\u00f5tme hankimise dokumentatsiooni. \n\n {url}" + }, + "user": { + "data": { + "api_token": "API v\u00f5ti" + }, + "description": "Vaata oma Discordi roboti v\u00f5tme hankimise dokumentatsiooni. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/fr.json b/homeassistant/components/discord/translations/fr.json new file mode 100644 index 00000000000..3ffac86c505 --- /dev/null +++ b/homeassistant/components/discord/translations/fr.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Jeton d'API" + }, + "description": "Consultez la documentation pour obtenir votre cl\u00e9 de bot Discord.\n\n{url}" + }, + "user": { + "data": { + "api_token": "Jeton d'API" + }, + "description": "Consultez la documentation pour obtenir votre cl\u00e9 de bot Discord.\n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/he.json b/homeassistant/components/discord/translations/he.json new file mode 100644 index 00000000000..d23c46a99bc --- /dev/null +++ b/homeassistant/components/discord/translations/he.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" + } + }, + "user": { + "data": { + "api_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/hu.json b/homeassistant/components/discord/translations/hu.json new file mode 100644 index 00000000000..bcbd8748808 --- /dev/null +++ b/homeassistant/components/discord/translations/hu.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API Token" + }, + "description": "Tekintse meg a Discord botkulcs beszerz\u00e9s\u00e9nek dokument\u00e1ci\u00f3j\u00e1t. \n\n {url}" + }, + "user": { + "data": { + "api_token": "API Token" + }, + "description": "Tekintse meg a Discord botkulcs beszerz\u00e9s\u00e9nek dokument\u00e1ci\u00f3j\u00e1t. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/id.json b/homeassistant/components/discord/translations/id.json new file mode 100644 index 00000000000..2e5d7d41f23 --- /dev/null +++ b/homeassistant/components/discord/translations/id.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token API" + }, + "description": "Lihat dokumentasi tentang mendapatkan kunci bot Discord Anda. \n\n {url}" + }, + "user": { + "data": { + "api_token": "Token API" + }, + "description": "Lihat dokumentasi tentang mendapatkan kunci bot Discord Anda. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/it.json b/homeassistant/components/discord/translations/it.json new file mode 100644 index 00000000000..c8be2cb0ed1 --- /dev/null +++ b/homeassistant/components/discord/translations/it.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token API" + }, + "description": "Fai riferimento alla documentazione su come ottenere la chiave del bot Discord. \n\n {url}" + }, + "user": { + "data": { + "api_token": "Token API" + }, + "description": "Fai riferimento alla documentazione su come ottenere la chiave del bot Discord. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/ja.json b/homeassistant/components/discord/translations/ja.json new file mode 100644 index 00000000000..3d96ea99890 --- /dev/null +++ b/homeassistant/components/discord/translations/ja.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API\u30c8\u30fc\u30af\u30f3" + }, + "description": "Discord\u30dc\u30c3\u30c8\u30ad\u30fc\u306e\u53d6\u5f97\u306b\u3064\u3044\u3066\u306f\u3001\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n{url}" + }, + "user": { + "data": { + "api_token": "API\u30c8\u30fc\u30af\u30f3" + }, + "description": "Discord\u30dc\u30c3\u30c8\u30ad\u30fc\u306e\u53d6\u5f97\u306b\u3064\u3044\u3066\u306f\u3001\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/nl.json b/homeassistant/components/discord/translations/nl.json new file mode 100644 index 00000000000..55fb894031d --- /dev/null +++ b/homeassistant/components/discord/translations/nl.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API-token" + }, + "description": "Raadpleeg de documentatie over het verkrijgen van uw Discord bot key.\n\n{url}" + }, + "user": { + "data": { + "api_token": "API-token" + }, + "description": "Raadpleeg de documentatie over het verkrijgen van uw Discord bot key.\n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/no.json b/homeassistant/components/discord/translations/no.json new file mode 100644 index 00000000000..e8a36e5c794 --- /dev/null +++ b/homeassistant/components/discord/translations/no.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API-token" + }, + "description": "Se dokumentasjonen for \u00e5 f\u00e5 din Discord-botn\u00f8kkel. \n\n {url}" + }, + "user": { + "data": { + "api_token": "API-token" + }, + "description": "Se dokumentasjonen for \u00e5 f\u00e5 din Discord-botn\u00f8kkel. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/pl.json b/homeassistant/components/discord/translations/pl.json new file mode 100644 index 00000000000..96fb45d1bc5 --- /dev/null +++ b/homeassistant/components/discord/translations/pl.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token API" + }, + "description": "Zapoznaj si\u0119 z dokumentacj\u0105 dotycz\u0105c\u0105 uzyskiwania klucza bota Discord. \n\n{url}" + }, + "user": { + "data": { + "api_token": "Token API" + }, + "description": "Zapoznaj si\u0119 z dokumentacj\u0105 dotycz\u0105c\u0105 uzyskiwania klucza bota Discord. \n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/pt-BR.json b/homeassistant/components/discord/translations/pt-BR.json new file mode 100644 index 00000000000..cedd60f6fb6 --- /dev/null +++ b/homeassistant/components/discord/translations/pt-BR.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token da API" + }, + "description": "Consulte a documenta\u00e7\u00e3o sobre como obter sua chave de bot do Discord. \n\n {url}" + }, + "user": { + "data": { + "api_token": "Token da API" + }, + "description": "Consulte a documenta\u00e7\u00e3o sobre como obter sua chave de bot do Discord. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/ru.json b/homeassistant/components/discord/translations/ru.json new file mode 100644 index 00000000000..19be65ea874 --- /dev/null +++ b/homeassistant/components/discord/translations/ru.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "\u0422\u043e\u043a\u0435\u043d API" + }, + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u043a\u043b\u044e\u0447\u0430 \u0431\u043e\u0442\u0430 Discord. \n\n{url}" + }, + "user": { + "data": { + "api_token": "\u0422\u043e\u043a\u0435\u043d API" + }, + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u043a\u043b\u044e\u0447\u0430 \u0431\u043e\u0442\u0430 Discord. \n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/tr.json b/homeassistant/components/discord/translations/tr.json new file mode 100644 index 00000000000..2bce7e54907 --- /dev/null +++ b/homeassistant/components/discord/translations/tr.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API Anahtar\u0131" + }, + "description": "Discord bot anahtar\u0131n\u0131z\u0131 almayla ilgili belgelere bak\u0131n. \n\n {url}" + }, + "user": { + "data": { + "api_token": "API Anahtar\u0131" + }, + "description": "Discord bot anahtar\u0131n\u0131z\u0131 almayla ilgili belgelere bak\u0131n. \n\n {url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/discord/translations/zh-Hant.json b/homeassistant/components/discord/translations/zh-Hant.json new file mode 100644 index 00000000000..5f0f4500903 --- /dev/null +++ b/homeassistant/components/discord/translations/zh-Hant.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "API \u6b0a\u6756" + }, + "description": "\u8acb\u53c3\u8003\u6587\u4ef6\u4ee5\u53d6\u5f97 Discord \u6a5f\u5668\u4eba\u91d1\u9470\u3002\n\n{url}" + }, + "user": { + "data": { + "api_token": "API \u6b0a\u6756" + }, + "description": "\u8acb\u53c3\u8003\u6587\u4ef6\u4ee5\u53d6\u5f97 Discord \u6a5f\u5668\u4eba\u91d1\u9470\u3002\n\n{url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/hu.json b/homeassistant/components/dlna_dmr/translations/hu.json index 6596e71e05e..5e2ba148602 100644 --- a/homeassistant/components/dlna_dmr/translations/hu.json +++ b/homeassistant/components/dlna_dmr/translations/hu.json @@ -21,7 +21,7 @@ "description": "Kezd\u0151dhet a be\u00e1ll\u00edt\u00e1s?" }, "import_turn_on": { - "description": "Kapcsolja be az eszk\u00f6zt, \u00e9s kattintson a K\u00fcld\u00e9s gombra a migr\u00e1ci\u00f3 folytat\u00e1s\u00e1hoz" + "description": "Kapcsolja be az eszk\u00f6zt, majd folytassa a migr\u00e1ci\u00f3t" }, "manual": { "data": { @@ -35,7 +35,7 @@ "host": "C\u00edm", "url": "URL" }, - "description": "V\u00e1lassz egy be\u00e1ll\u00edtand\u00f3 eszk\u00f6zt vagy adj meg egy URL-t", + "description": "V\u00e1lassza ki a konfigur\u00e1lni k\u00edv\u00e1nt eszk\u00f6zt, vagy hagyja \u00fcresen az URL-c\u00edm k\u00e9zi megad\u00e1s\u00e1hoz", "title": "DLNA digit\u00e1lis m\u00e9dia renderel\u0151" } } diff --git a/homeassistant/components/dlna_dms/translations/cs.json b/homeassistant/components/dlna_dms/translations/cs.json new file mode 100644 index 00000000000..cfcad870880 --- /dev/null +++ b/homeassistant/components/dlna_dms/translations/cs.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed" + }, + "flow_title": "{name}", + "step": { + "confirm": { + "description": "Chcete za\u010d\u00edt nastavovat?" + }, + "user": { + "data": { + "host": "Hostitel" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dms/translations/hu.json b/homeassistant/components/dlna_dms/translations/hu.json index 8c645d42aa8..74ba47053a4 100644 --- a/homeassistant/components/dlna_dms/translations/hu.json +++ b/homeassistant/components/dlna_dms/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "bad_ssdp": "Az SSDP-adatok hi\u00e1nyosak", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "not_dms": "A m\u00e9diaszerver eszk\u00f6z nem t\u00e1mogatott" diff --git a/homeassistant/components/doorbird/translations/ca.json b/homeassistant/components/doorbird/translations/ca.json index 880360234b1..0049709caa3 100644 --- a/homeassistant/components/doorbird/translations/ca.json +++ b/homeassistant/components/doorbird/translations/ca.json @@ -29,6 +29,9 @@ "data": { "events": "Llista d'esdeveniments separats per comes." }, + "data_description": { + "events": "Afegeix el/s noms del/s esdeveniment/s que vulguis seguir separats per comes. Despr\u00e9s d'introduir-los, utilitza l'aplicaci\u00f3 de DoorBird per assignar-los a un esdeveniment espec\u00edfic.\n\nExemple: algu_ha_premut_el_boto, moviment" + }, "description": "Afegeix el/s noms del/s esdeveniment/s que vulguis seguir separats per comes. Despr\u00e9s d'introduir-los, utilitza l'aplicaci\u00f3 de DoorBird per assignar-los a un esdeveniment espec\u00edfic. Consulta la documentaci\u00f3 a https://www.home-assistant.io/integrations/doorbird/#events.\nExemple: algu_ha_premut_el_boto, moviment_detectat" } } diff --git a/homeassistant/components/doorbird/translations/de.json b/homeassistant/components/doorbird/translations/de.json index 3f025e67386..7a60a3bf4ae 100644 --- a/homeassistant/components/doorbird/translations/de.json +++ b/homeassistant/components/doorbird/translations/de.json @@ -29,6 +29,9 @@ "data": { "events": "Durch Kommas getrennte Liste von Ereignissen." }, + "data_description": { + "events": "F\u00fcge f\u00fcr jedes Ereignis, das du verfolgen m\u00f6chtest, einen durch Komma getrennten Ereignisnamen hinzu. Nachdem du sie hier eingegeben hast, verwende die DoorBird-App, um sie einem bestimmten Ereignis zuzuordnen.\n\nBeispiel: jemand_drueckte_den_Knopf, bewegung" + }, "description": "F\u00fcge f\u00fcr jedes Ereignis, das du verfolgen m\u00f6chtest, einen durch Kommas getrennten Ereignisnamen hinzu. Nachdem du sie hier eingegeben hast, verwende die DoorBird-App, um sie einem bestimmten Ereignis zuzuweisen. Weitere Informationen findest du in der Dokumentation unter https://www.home-assistant.io/integrations/doorbird/#events. Beispiel: jemand_hat_den_knopf_gedr\u00fcckt, bewegung" } } diff --git a/homeassistant/components/doorbird/translations/el.json b/homeassistant/components/doorbird/translations/el.json index ffbf523e7d8..da06dc3e0be 100644 --- a/homeassistant/components/doorbird/translations/el.json +++ b/homeassistant/components/doorbird/translations/el.json @@ -29,6 +29,9 @@ "data": { "events": "\u039b\u03af\u03c3\u03c4\u03b1 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03c9\u03bd \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1." }, + "data_description": { + "events": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5. \u0391\u03c6\u03bf\u03cd \u03c4\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03b4\u03ce, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae DoorBird \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03b9\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03bf \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd.\n\n\u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: somebody_pressed_the_button, motion" + }, "description": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5. \u0391\u03c6\u03bf\u03cd \u03c4\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03b4\u03ce, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae DoorBird \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03b9\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03bf \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/doorbird/#events. \u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/en.json b/homeassistant/components/doorbird/translations/en.json index db1cea2d73f..ee005dfbfed 100644 --- a/homeassistant/components/doorbird/translations/en.json +++ b/homeassistant/components/doorbird/translations/en.json @@ -29,6 +29,9 @@ "data": { "events": "Comma separated list of events." }, + "data_description": { + "events": "Add an comma separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event.\n\nExample: somebody_pressed_the_button, motion" + }, "description": "Add an comma separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event. See the documentation at https://www.home-assistant.io/integrations/doorbird/#events. Example: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/et.json b/homeassistant/components/doorbird/translations/et.json index 08b163c0ca2..f37d5d0244b 100644 --- a/homeassistant/components/doorbird/translations/et.json +++ b/homeassistant/components/doorbird/translations/et.json @@ -29,6 +29,9 @@ "data": { "events": "Komadega eraldatud s\u00fcndmuste loend." }, + "data_description": { + "events": "Lisa iga s\u00fcndmuse jaoks, mida soovid j\u00e4lgida, komaga eraldatud s\u00fcndmuse nimi. P\u00e4rast nende sisestamist siia kasuta DoorBirdi rakendust, et m\u00e4\u00e4rata need konkreetsele s\u00fcndmusele.\n\nN\u00e4ide: somebody_pressed_the_button, liikumine" + }, "description": "Lisa komaga eraldatud s\u00fcndmuse nimi igale j\u00e4lgitavale s\u00fcndmusele. P\u00e4rast nende sisestamist kasuta rakendust DoorBird, et siduda need konkreetse s\u00fcndmusega. Vaata dokumentatsiooni aadressil https://www.home-assistant.io/integrations/doorbird/#events. N\u00e4ide: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/fr.json b/homeassistant/components/doorbird/translations/fr.json index 40569250e5f..b4819897366 100644 --- a/homeassistant/components/doorbird/translations/fr.json +++ b/homeassistant/components/doorbird/translations/fr.json @@ -29,6 +29,9 @@ "data": { "events": "Liste d'\u00e9v\u00e9nements s\u00e9par\u00e9s par des virgules." }, + "data_description": { + "events": "Ajoutez les noms des \u00e9v\u00e9nements que vous souhaitez suivre, s\u00e9par\u00e9s par des virgules. Apr\u00e8s les avoir saisis ici, utilisez l'application DoorBird pour les affecter \u00e0 un \u00e9v\u00e9nement sp\u00e9cifique.\n\nExemple\u00a0: somebody_pressed_the_button, motion" + }, "description": "Ajoutez un nom d'\u00e9v\u00e9nement s\u00e9par\u00e9 par des virgules pour chaque \u00e9v\u00e9nement que vous souhaitez suivre. Apr\u00e8s les avoir saisis ici, utilisez l'application DoorBird pour les affecter \u00e0 un \u00e9v\u00e9nement sp\u00e9cifique. Consultez la documentation sur https://www.home-assistant.io/integrations/doorbird/#events. Exemple: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/hu.json b/homeassistant/components/doorbird/translations/hu.json index 48a124b4f17..51bbb0864dd 100644 --- a/homeassistant/components/doorbird/translations/hu.json +++ b/homeassistant/components/doorbird/translations/hu.json @@ -29,6 +29,9 @@ "data": { "events": "Vessz\u0151vel elv\u00e1lasztott esem\u00e9nyek list\u00e1ja." }, + "data_description": { + "events": "Adjon hozz\u00e1 egy vessz\u0151vel elv\u00e1lasztott esem\u00e9nynevet minden egyes esem\u00e9nyhez, amelyet nyomon k\u00edv\u00e1n k\u00f6vetni. Miut\u00e1n be\u00edrta \u0151ket ide, a DoorBird alkalmaz\u00e1ssal rendelje \u0151ket egy adott esem\u00e9nyhez.\n\nP\u00e9lda: valaki_megnyomta_a_gombot, motion" + }, "description": "Adjon hozz\u00e1 vessz\u0151vel elv\u00e1lasztott esem\u00e9nynevet minden k\u00f6vetni k\u00edv\u00e1nt esem\u00e9nyhez. Miut\u00e1n itt megadta \u0151ket, haszn\u00e1lja a DoorBird alkalmaz\u00e1st, hogy hozz\u00e1rendelje \u0151ket egy adott esem\u00e9nyhez. Tekintse meg a dokument\u00e1ci\u00f3t a https://www.home-assistant.io/integrations/doorbird/#events c\u00edmen. P\u00e9lda: valaki_pr\u00e9selt_gomb, mozg\u00e1s" } } diff --git a/homeassistant/components/doorbird/translations/id.json b/homeassistant/components/doorbird/translations/id.json index 60348ec26a1..c5d5733457d 100644 --- a/homeassistant/components/doorbird/translations/id.json +++ b/homeassistant/components/doorbird/translations/id.json @@ -29,6 +29,9 @@ "data": { "events": "Daftar event yang dipisahkan koma." }, + "data_description": { + "events": "Tambahkan nama event yang dipisahkan koma untuk setiap event yang ingin dilacak. Setelah memasukkannya di sini, gunakan aplikasi DoorBird untuk menetapkannya ke event tertentu.\n\nMisalnya: ada_yang_menekan_tombol, gerakan" + }, "description": "Tambahkan nama event yang dipisahkan koma untuk setiap event yang ingin dilacak. Setelah memasukkannya di sini, gunakan aplikasi DoorBird untuk menetapkannya ke event tertentu. Baca dokumentasi di https://www.home-assistant.io/integrations/doorbird/#events. Contoh: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/it.json b/homeassistant/components/doorbird/translations/it.json index 83f1ab9c5eb..9864f100954 100644 --- a/homeassistant/components/doorbird/translations/it.json +++ b/homeassistant/components/doorbird/translations/it.json @@ -29,6 +29,9 @@ "data": { "events": "Elenco di eventi separati da virgole." }, + "data_description": { + "events": "Aggiungi un nome evento separato da virgole per ogni evento che desideri monitorare. Dopo averli inseriti qui, usa l'applicazione DoorBird per assegnarli a un evento specifico. \n\nEsempio: somebody_pressed_the_button, movimento" + }, "description": "Aggiungere un nome di evento separato da virgola per ogni evento che desideri monitorare. Dopo averli inseriti qui, usa l'applicazione DoorBird per assegnarli a un evento specifico. Consulta la documentazione su https://www.home-assistant.io/integrations/doorbird/#events. Esempio: qualcuno_premuto_il_pulsante, movimento" } } diff --git a/homeassistant/components/doorbird/translations/ja.json b/homeassistant/components/doorbird/translations/ja.json index 179edc8943c..55363310ec5 100644 --- a/homeassistant/components/doorbird/translations/ja.json +++ b/homeassistant/components/doorbird/translations/ja.json @@ -29,6 +29,9 @@ "data": { "events": "\u30a4\u30d9\u30f3\u30c8\u306e\u30b3\u30f3\u30de\u533a\u5207\u308a\u30ea\u30b9\u30c8\u3002" }, + "data_description": { + "events": "\u8ffd\u8de1\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u3054\u3068\u306b\u30b3\u30f3\u30de\u533a\u5207\u308a\u306e\u30a4\u30d9\u30f3\u30c8\u540d\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u3053\u3053\u306b\u5165\u529b\u5f8c\u3001DoorBird\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u3066\u7279\u5b9a\u306e\u30a4\u30d9\u30f3\u30c8\u306b\u5272\u308a\u5f53\u3066\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 \n\n\u4f8b: somebody_pressed_the_button, motion" + }, "description": "\u8ffd\u8de1\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u3054\u3068\u306b\u3001\u30b3\u30f3\u30de\u533a\u5207\u308a\u3067\u30a4\u30d9\u30f3\u30c8\u540d\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u3053\u3053\u306b\u5165\u529b\u3057\u305f\u5f8c\u3001DoorBird\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u3066\u7279\u5b9a\u306e\u30a4\u30d9\u30f3\u30c8\u306b\u5272\u308a\u5f53\u3066\u307e\u3059\u3002https://www.home-assistant.io/integrations/doorbird/#events. \u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4f8b: somebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/doorbird/translations/nl.json b/homeassistant/components/doorbird/translations/nl.json index db32d96d831..83e47ca4c5e 100644 --- a/homeassistant/components/doorbird/translations/nl.json +++ b/homeassistant/components/doorbird/translations/nl.json @@ -29,6 +29,9 @@ "data": { "events": "Door komma's gescheiden lijst met gebeurtenissen." }, + "data_description": { + "events": "Voeg een door komma's gescheiden evenementnaam toe voor elk evenement dat u wilt volgen. Nadat u ze hier hebt ingevoerd, gebruikt u de DoorBird app om ze aan een specifieke gebeurtenis toe te wijzen.\n\nVoorbeeld: iemand_drukt_op_de_knop, beweging" + }, "description": "Voeg een door komma's gescheiden evenementnaam toe voor elk evenement dat u wilt volgen. Nadat je ze hier hebt ingevoerd, gebruik je de DoorBird-app om ze toe te wijzen aan een specifiek evenement. Zie de documentatie op https://www.home-assistant.io/integrations/doorbird/#events. Voorbeeld: iemand_drukte_knop, beweging" } } diff --git a/homeassistant/components/doorbird/translations/no.json b/homeassistant/components/doorbird/translations/no.json index 356c86a8b54..a30fc93013a 100644 --- a/homeassistant/components/doorbird/translations/no.json +++ b/homeassistant/components/doorbird/translations/no.json @@ -29,6 +29,9 @@ "data": { "events": "Kommaseparert liste over hendelser." }, + "data_description": { + "events": "Legg til et navn p\u00e5 en kommadelt hendelse for hver hendelse du vil spore. N\u00e5r du har skrevet dem inn her, bruker du DoorBird-appen til \u00e5 tilordne dem til en bestemt hendelse.\n\nEksempel: somebody_pressed_the_button, bevegelse" + }, "description": "Legg til et kommaseparert hendelsesnavn for hvert arrangement du \u00f8nsker \u00e5 spore. Etter \u00e5 ha skrevet dem inn her, bruker du DoorBird-appen til \u00e5 tilordne dem til en bestemt hendelse. Se dokumentasjonen p\u00e5 https://www.home-assistant.io/integrations/doorbird/#events. Eksempel: noen_trykket_knappen, bevegelse" } } diff --git a/homeassistant/components/doorbird/translations/pl.json b/homeassistant/components/doorbird/translations/pl.json index f55d2d406f2..85a7a8765a7 100644 --- a/homeassistant/components/doorbird/translations/pl.json +++ b/homeassistant/components/doorbird/translations/pl.json @@ -29,6 +29,9 @@ "data": { "events": "Lista wydarze\u0144 oddzielona przecinkami" }, + "data_description": { + "events": "Dodaj nazw\u0119 oddzielon\u0105 przecinkami dla ka\u017cdego wydarzenia, kt\u00f3re chcesz \u015bledzi\u0107. Po wprowadzeniu ich tutaj u\u017cyj aplikacji DoorBird, aby przypisa\u0107 je do konkretnego wydarzenia. \n\nPrzyk\u0142ad: kto\u015b_nacisn\u0105\u0142_przycisk, ruch" + }, "description": "Dodaj nazwy wydarze\u0144 oddzielonych przecinkami, kt\u00f3re chcesz \u015bledzi\u0107. Po wprowadzeniu ich tutaj u\u017cyj aplikacji DoorBird, aby przypisa\u0107 je do okre\u015blonych zdarze\u0144. Zapoznaj si\u0119 z dokumentacj\u0105 na stronie https://www.home-assistant.io/integrations/doorbird/#events.\nPrzyk\u0142ad: nacisniecie_przycisku, ruch" } } diff --git a/homeassistant/components/doorbird/translations/pt-BR.json b/homeassistant/components/doorbird/translations/pt-BR.json index 3f2c479df8f..07170c086b7 100644 --- a/homeassistant/components/doorbird/translations/pt-BR.json +++ b/homeassistant/components/doorbird/translations/pt-BR.json @@ -29,6 +29,9 @@ "data": { "events": "Lista de eventos separados por v\u00edrgulas." }, + "data_description": { + "events": "Adicione um nome de evento separado por v\u00edrgula para cada evento que voc\u00ea deseja rastrear. Depois de inseri-los aqui, use o aplicativo DoorBird para atribu\u00ed-los a um evento espec\u00edfico. \n\n Exemplo: someone_pressed_the_button, movimento" + }, "description": "Adicione um nome de evento separado por v\u00edrgula para cada evento que voc\u00ea deseja rastrear. Depois de inseri-los aqui, use o aplicativo DoorBird para atribu\u00ed-los a um evento espec\u00edfico. Consulte a documenta\u00e7\u00e3o em https://www.home-assistant.io/integrations/doorbird/#events. Exemplo: alguem_pressionou_o_botao movimento" } } diff --git a/homeassistant/components/doorbird/translations/ru.json b/homeassistant/components/doorbird/translations/ru.json index df156bb640a..045092a2b97 100644 --- a/homeassistant/components/doorbird/translations/ru.json +++ b/homeassistant/components/doorbird/translations/ru.json @@ -29,6 +29,9 @@ "data": { "events": "\u0421\u043f\u0438\u0441\u043e\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e." }, + "data_description": { + "events": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 DoorBird, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044e.\n\n\u041f\u0440\u0438\u043c\u0435\u0440: somebody_pressed_the_button, motion." + }, "description": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 DoorBird, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044e. \u041f\u0440\u0438\u043c\u0435\u0440: somebody_pressed_the_button, motion. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438: https://www.home-assistant.io/integrations/doorbird/#events." } } diff --git a/homeassistant/components/doorbird/translations/tr.json b/homeassistant/components/doorbird/translations/tr.json index 1bb08b6f81e..1e472a43535 100644 --- a/homeassistant/components/doorbird/translations/tr.json +++ b/homeassistant/components/doorbird/translations/tr.json @@ -29,6 +29,9 @@ "data": { "events": "Virg\u00fclle ayr\u0131lm\u0131\u015f olaylar\u0131n listesi." }, + "data_description": { + "events": "\u0130zlemek istedi\u011finiz her etkinlik i\u00e7in virg\u00fclle ayr\u0131lm\u0131\u015f bir etkinlik ad\u0131 ekleyin. Bunlar\u0131 buraya girdikten sonra, onlar\u0131 belirli bir etkinli\u011fe atamak i\u00e7in DoorBird uygulamas\u0131n\u0131 kullan\u0131n. \n\n \u00d6rnek: birisi_butona_basti, hareket" + }, "description": "\u0130zlemek istedi\u011finiz her etkinlik i\u00e7in virg\u00fclle ayr\u0131lm\u0131\u015f bir etkinlik ad\u0131 ekleyin. Bunlar\u0131 buraya girdikten sonra, onlar\u0131 belirli bir etkinli\u011fe atamak i\u00e7in DoorBird uygulamas\u0131n\u0131 kullan\u0131n. https://www.home-assistant.io/integrations/doorbird/#events adresindeki belgelere bak\u0131n. \u00d6rnek: birisi_pressed_the_button, hareket" } } diff --git a/homeassistant/components/doorbird/translations/zh-Hant.json b/homeassistant/components/doorbird/translations/zh-Hant.json index 020267b4921..616e346b0dc 100644 --- a/homeassistant/components/doorbird/translations/zh-Hant.json +++ b/homeassistant/components/doorbird/translations/zh-Hant.json @@ -29,6 +29,9 @@ "data": { "events": "\u4ee5\u9017\u865f\u5206\u5225\u4e8b\u4ef6\u5217\u8868\u3002" }, + "data_description": { + "events": "\u4ee5\u9017\u865f\u5206\u5225\u6240\u8981\u8ffd\u8e64\u7684\u4e8b\u4ef6\u540d\u7a31\u3002\u65bc\u6b64\u8f38\u5165\u5f8c\uff0c\u4f7f\u7528 DoorBird App \u6307\u5b9a\u81f3\u7279\u5b9a\u4e8b\u4ef6\u3002\n\n\u4f8b\u5982\uff1asomebody_pressed_the_button, motion" + }, "description": "\u4ee5\u9017\u865f\u5206\u5225\u6240\u8981\u8ffd\u8e64\u7684\u4e8b\u4ef6\u540d\u7a31\u3002\u65bc\u6b64\u8f38\u5165\u5f8c\uff0c\u4f7f\u7528 DoorBird App \u6307\u5b9a\u81f3\u7279\u5b9a\u4e8b\u4ef6\u3002\u8acb\u53c3\u95b1\u6587\u4ef6\uff1ahttps://www.home-assistant.io/integrations/doorbird/#events\u3002\u4f8b\u5982\uff1asomebody_pressed_the_button, motion" } } diff --git a/homeassistant/components/dsmr/translations/it.json b/homeassistant/components/dsmr/translations/it.json index 9b50979d016..67ca750dd02 100644 --- a/homeassistant/components/dsmr/translations/it.json +++ b/homeassistant/components/dsmr/translations/it.json @@ -9,12 +9,12 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "cannot_communicate": "Impossibile comunicare", "cannot_connect": "Impossibile connettersi", - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "step": { - "one": "Pi\u00f9", - "other": "Altri", + "one": "Vuoto", + "other": "Vuoti", "setup_network": { "data": { "dsmr_version": "Seleziona la versione DSMR", diff --git a/homeassistant/components/dunehd/translations/ca.json b/homeassistant/components/dunehd/translations/ca.json index 12f139afe60..08dd841bf4f 100644 --- a/homeassistant/components/dunehd/translations/ca.json +++ b/homeassistant/components/dunehd/translations/ca.json @@ -13,7 +13,7 @@ "data": { "host": "Amfitri\u00f3" }, - "description": "Configura la integraci\u00f3 Dune HD. Si tens problemes durant la configuraci\u00f3, v\u00e9s a: https://www.home-assistant.io/integrations/dunehd\n\nAssegura't que el reproductor estigui engegat.", + "description": "Assegura't que el reproductor est\u00e0 engegat.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/de.json b/homeassistant/components/dunehd/translations/de.json index f3d7ecd725a..bcab02b2a09 100644 --- a/homeassistant/components/dunehd/translations/de.json +++ b/homeassistant/components/dunehd/translations/de.json @@ -13,7 +13,7 @@ "data": { "host": "Host" }, - "description": "Richte die Dune HD-Integration ein. Wenn du Probleme mit der Konfiguration hast, gehe zu: https://www.home-assistant.io/integrations/dunehd \n\nStelle sicher, dass dein Player eingeschaltet ist.", + "description": "Stelle sicher, dass dein Player eingeschaltet ist.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/en.json b/homeassistant/components/dunehd/translations/en.json index 41540c987be..d6392509fcf 100644 --- a/homeassistant/components/dunehd/translations/en.json +++ b/homeassistant/components/dunehd/translations/en.json @@ -13,7 +13,7 @@ "data": { "host": "Host" }, - "description": "Set up Dune HD integration. If you have problems with configuration go to: https://www.home-assistant.io/integrations/dunehd \n\nEnsure that your player is turned on.", + "description": "Ensure that your player is turned on.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/et.json b/homeassistant/components/dunehd/translations/et.json index bb53d124200..3f4a9b4b085 100644 --- a/homeassistant/components/dunehd/translations/et.json +++ b/homeassistant/components/dunehd/translations/et.json @@ -13,7 +13,7 @@ "data": { "host": "" }, - "description": "Seadista Dune HD sidumine. Kui seadistamisega on probleeme, mine aadressile https://www.home-assistant.io/integrations/dunehd\n\n Veendu, et seade on sisse l\u00fclitatud.", + "description": "Veendu, et m\u00e4ngija on sisse l\u00fclitatud.", "title": "" } } diff --git a/homeassistant/components/dunehd/translations/fr.json b/homeassistant/components/dunehd/translations/fr.json index 0e8cb6d6ff8..8c3bc89a0e6 100644 --- a/homeassistant/components/dunehd/translations/fr.json +++ b/homeassistant/components/dunehd/translations/fr.json @@ -13,7 +13,7 @@ "data": { "host": "H\u00f4te" }, - "description": "Configurez l'int\u00e9gration Dune HD. Si vous rencontrez des probl\u00e8mes de configuration, acc\u00e9dez \u00e0: https://www.home-assistant.io/integrations/dunehd \n\n Assurez-vous que votre lecteur est allum\u00e9.", + "description": "Assurez-vous que votre lecteur est allum\u00e9.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/hu.json b/homeassistant/components/dunehd/translations/hu.json index 15b2d297363..1dc7c8ec6cc 100644 --- a/homeassistant/components/dunehd/translations/hu.json +++ b/homeassistant/components/dunehd/translations/hu.json @@ -13,7 +13,7 @@ "data": { "host": "C\u00edm" }, - "description": "\u00c1ll\u00edtsa be a Dune HD integr\u00e1ci\u00f3t. Ha probl\u00e9m\u00e1i vannak a konfigur\u00e1ci\u00f3val, l\u00e1togasson el a k\u00f6vetkez\u0151 oldalra: https://www.home-assistant.io/integrations/dunehd \n\n Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a lej\u00e1tsz\u00f3 be van kapcsolva.", + "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a lej\u00e1tsz\u00f3 be van kapcsolva.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/id.json b/homeassistant/components/dunehd/translations/id.json index 25cb96bedea..5a86947a07a 100644 --- a/homeassistant/components/dunehd/translations/id.json +++ b/homeassistant/components/dunehd/translations/id.json @@ -13,7 +13,7 @@ "data": { "host": "Host" }, - "description": "Siapkan integrasi Dune HD. Jika Anda memiliki masalah dengan konfigurasi, buka: https://www.home-assistant.io/integrations/dunehd \n\nPastikan pemutar Anda dinyalakan.", + "description": "Pastikan pemutar Anda dinyalakan.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/it.json b/homeassistant/components/dunehd/translations/it.json index e296a59e474..0e33feb2640 100644 --- a/homeassistant/components/dunehd/translations/it.json +++ b/homeassistant/components/dunehd/translations/it.json @@ -13,7 +13,7 @@ "data": { "host": "Host" }, - "description": "Configura l'integrazione Dune HD. In caso di problemi con la configurazione, visita: https://www.home-assistant.io/integrations/dunehd \n\n Assicurati che il tuo lettore sia acceso.", + "description": "Assicurati che il tuo lettore sia acceso.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/nl.json b/homeassistant/components/dunehd/translations/nl.json index bb3dd7def47..f4c54a5dba5 100644 --- a/homeassistant/components/dunehd/translations/nl.json +++ b/homeassistant/components/dunehd/translations/nl.json @@ -13,7 +13,7 @@ "data": { "host": "Host" }, - "description": "Stel Dune HD integratie in. Als u problemen heeft met de configuratie ga dan naar: https://www.home-assistant.io/integrations/dunehd \n\nZorg ervoor dat uw speler is ingeschakeld.", + "description": "Zorg ervoor dat uw speler is ingeschakeld.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/no.json b/homeassistant/components/dunehd/translations/no.json index a887228c322..1b35f5581b1 100644 --- a/homeassistant/components/dunehd/translations/no.json +++ b/homeassistant/components/dunehd/translations/no.json @@ -13,7 +13,7 @@ "data": { "host": "Vert" }, - "description": "Konfigurer Dune HD-integrering. Hvis du har problemer med konfigurasjonen, kan du g\u00e5 til: https://www.home-assistant.io/integrations/dunehd \n\nKontroller at spilleren er sl\u00e5tt p\u00e5.", + "description": "S\u00f8rg for at spilleren er sl\u00e5tt p\u00e5.", "title": "" } } diff --git a/homeassistant/components/dunehd/translations/pl.json b/homeassistant/components/dunehd/translations/pl.json index 5def31d1c46..376b84e05d9 100644 --- a/homeassistant/components/dunehd/translations/pl.json +++ b/homeassistant/components/dunehd/translations/pl.json @@ -13,7 +13,7 @@ "data": { "host": "Nazwa hosta lub adres IP" }, - "description": "Konfiguracja integracji odtwarzacza Dune HD. Je\u015bli masz problemy z konfiguracj\u0105, przejd\u017a do strony: https://www.home-assistant.io/integrations/dunehd\n\nUpewnij si\u0119, \u017ce odtwarzacz jest w\u0142\u0105czony.", + "description": "Upewnij si\u0119, \u017ce odtwarzacz jest w\u0142\u0105czony.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/pt-BR.json b/homeassistant/components/dunehd/translations/pt-BR.json index 072cf6011ea..4775f1af379 100644 --- a/homeassistant/components/dunehd/translations/pt-BR.json +++ b/homeassistant/components/dunehd/translations/pt-BR.json @@ -13,7 +13,7 @@ "data": { "host": "Nome do host" }, - "description": "Configure a integra\u00e7\u00e3o Dune HD. Se voc\u00ea tiver problemas com a configura\u00e7\u00e3o, acesse: https://www.home-assistant.io/integrations/dunehd \n\n Certifique-se de que seu player est\u00e1 ligado.", + "description": "Certifique-se de que seu player est\u00e1 ligado.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/ru.json b/homeassistant/components/dunehd/translations/ru.json index c1537de579f..134511e66f2 100644 --- a/homeassistant/components/dunehd/translations/ru.json +++ b/homeassistant/components/dunehd/translations/ru.json @@ -13,7 +13,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Dune HD. \u0415\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443: https://www.home-assistant.io/integrations/dunehd\n\n\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 \u043f\u043b\u0435\u0435\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d.", + "description": "\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/tr.json b/homeassistant/components/dunehd/translations/tr.json index 121267c34e3..79c2f033cfc 100644 --- a/homeassistant/components/dunehd/translations/tr.json +++ b/homeassistant/components/dunehd/translations/tr.json @@ -13,7 +13,7 @@ "data": { "host": "Sunucu" }, - "description": "Dune HD entegrasyonunu ayarlay\u0131n. Yap\u0131land\u0131rmayla ilgili sorunlar\u0131n\u0131z varsa \u015fu adrese gidin: https://www.home-assistant.io/integrations/dunehd \n\n Oynat\u0131c\u0131n\u0131z\u0131n a\u00e7\u0131k oldu\u011fundan emin olun.", + "description": "Oynat\u0131c\u0131n\u0131z\u0131n a\u00e7\u0131k oldu\u011fundan emin olun.", "title": "Dune HD" } } diff --git a/homeassistant/components/dunehd/translations/zh-Hant.json b/homeassistant/components/dunehd/translations/zh-Hant.json index a81055b9576..993a6d9a210 100644 --- a/homeassistant/components/dunehd/translations/zh-Hant.json +++ b/homeassistant/components/dunehd/translations/zh-Hant.json @@ -13,7 +13,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef" }, - "description": "\u8a2d\u5b9a Dune HD \u6574\u5408\u3002\u5047\u5982\u65bc\u8a2d\u5b9a\u904e\u7a0b\u4e2d\u906d\u9047\u56f0\u7136\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/dunehd \n\n\u78ba\u5b9a\u64ad\u653e\u68c4\u5df2\u7d93\u958b\u555f\u3002", + "description": "\u78ba\u5b9a\u64ad\u653e\u5668\u5df2\u7d93\u958b\u555f\u3002", "title": "Dune HD" } } diff --git a/homeassistant/components/ecobee/translations/hu.json b/homeassistant/components/ecobee/translations/hu.json index a91478ff038..a2fdd5553a4 100644 --- a/homeassistant/components/ecobee/translations/hu.json +++ b/homeassistant/components/ecobee/translations/hu.json @@ -9,7 +9,7 @@ }, "step": { "authorize": { - "description": "K\u00e9rj\u00fck, enged\u00e9lyezze ezt az alkalmaz\u00e1st a https://www.ecobee.com/consumerportal/index.html c\u00edmen a k\u00f6vetkez\u0151 PIN-k\u00f3ddal: \n\n {pin} \n \n Ezut\u00e1n nyomja meg a K\u00fcld\u00e9s gombot.", + "description": "K\u00e9rj\u00fck, enged\u00e9lyezze ezt az alkalmaz\u00e1st a https://www.ecobee.com/consumerportal/index.html c\u00edmen a k\u00f6vetkez\u0151 PIN-k\u00f3ddal: \n\n {pin} \n \nEzut\u00e1n nyomja meg a Mehet gombot.", "title": "Alkalmaz\u00e1s enged\u00e9lyez\u00e9se ecobee.com-on" }, "user": { diff --git a/homeassistant/components/elkm1/translations/bg.json b/homeassistant/components/elkm1/translations/bg.json index 8fd587a0640..5e83523d419 100644 --- a/homeassistant/components/elkm1/translations/bg.json +++ b/homeassistant/components/elkm1/translations/bg.json @@ -1,7 +1,9 @@ { "config": { "abort": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "flow_title": "{mac_address} ({host})", "step": { diff --git a/homeassistant/components/elkm1/translations/ca.json b/homeassistant/components/elkm1/translations/ca.json index 2317e475a37..b0a979f98b5 100644 --- a/homeassistant/components/elkm1/translations/ca.json +++ b/homeassistant/components/elkm1/translations/ca.json @@ -4,7 +4,9 @@ "address_already_configured": "Ja hi ha un Elk-M1 configurat amb aquesta adre\u00e7a", "already_configured": "Ja hi ha un Elk-M1 configurat amb aquest prefix", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Adre\u00e7a IP, domini o port s\u00e8rie (en cas d'una connexi\u00f3 s\u00e8rie).", - "device": "Dispositiu", - "password": "Contrasenya", - "prefix": "Prefix \u00fanic (deixa-ho en blanc si nom\u00e9s tens un \u00fanic controlador Elk-M1).", - "protocol": "Protocol", - "temperature_unit": "Unitats de temperatura que utilitza l'Elk-M1.", - "username": "Nom d'usuari" + "device": "Dispositiu" }, "description": "Selecciona un sistema descobert o 'entrada manual' si no s'han descobert dispositius.", "title": "Connexi\u00f3 amb el controlador Elk-M1" diff --git a/homeassistant/components/elkm1/translations/cs.json b/homeassistant/components/elkm1/translations/cs.json index f2f4c17af9c..9ff8da3067d 100644 --- a/homeassistant/components/elkm1/translations/cs.json +++ b/homeassistant/components/elkm1/translations/cs.json @@ -28,13 +28,6 @@ "title": "P\u0159ipojen\u00ed k ovlada\u010di Elk-M1" }, "user": { - "data": { - "password": "Heslo", - "prefix": "Jedine\u010dn\u00fd prefix (ponechte pr\u00e1zdn\u00e9, pokud m\u00e1te pouze jeden ElkM1).", - "protocol": "Protokol", - "temperature_unit": "Jednotka teploty pou\u017e\u00edvan\u00e1 ElkM1.", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, "title": "P\u0159ipojen\u00ed k ovlada\u010di Elk-M1" } } diff --git a/homeassistant/components/elkm1/translations/de.json b/homeassistant/components/elkm1/translations/de.json index cf318a626c9..7c08a9b254d 100644 --- a/homeassistant/components/elkm1/translations/de.json +++ b/homeassistant/components/elkm1/translations/de.json @@ -4,7 +4,9 @@ "address_already_configured": "Ein ElkM1 mit dieser Adresse ist bereits konfiguriert", "already_configured": "Ein ElkM1 mit diesem Pr\u00e4fix ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", - "cannot_connect": "Verbinden fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Die IP-Adresse, die Domain oder der serielle Port bei einer seriellen Verbindung.", - "device": "Ger\u00e4t", - "password": "Passwort", - "prefix": "Ein eindeutiges Pr\u00e4fix (leer lassen, wenn du nur einen ElkM1 hast).", - "protocol": "Protokoll", - "temperature_unit": "Die von ElkM1 verwendete Temperatureinheit.", - "username": "Benutzername" + "device": "Ger\u00e4t" }, "description": "W\u00e4hle ein erkanntes System oder \"Manuelle Eingabe\", wenn keine Ger\u00e4te erkannt wurden.", "title": "Stelle eine Verbindung zur Elk-M1-Steuerung her" diff --git a/homeassistant/components/elkm1/translations/el.json b/homeassistant/components/elkm1/translations/el.json index 9f600806d83..bc1d64649aa 100644 --- a/homeassistant/components/elkm1/translations/el.json +++ b/homeassistant/components/elkm1/translations/el.json @@ -4,7 +4,9 @@ "address_already_configured": "\u0388\u03bd\u03b1 ElkM1 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_configured": "\u0388\u03bd\u03b1 ElkM1 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03bf \u03c4\u03bf\u03bc\u03ad\u03b1\u03c2 \u03ae \u03b7 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b5\u03ac\u03bd \u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b3\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bc\u03ad\u03c3\u03c9 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2.", - "device": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "prefix": "\u0388\u03bd\u03b1 \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b1\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03bc\u03cc\u03bd\u03bf \u03ad\u03bd\u03b1 ElkM1).", - "protocol": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf", - "temperature_unit": "\u0397 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf ElkM1.", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + "device": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03ae \"\u039c\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b9\u03c3\u03b7\" \u03b5\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03bf\u03c5\u03bd \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2.", "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf Elk-M1 Control" diff --git a/homeassistant/components/elkm1/translations/es-419.json b/homeassistant/components/elkm1/translations/es-419.json index 02271c4ea6c..09b5bc190f7 100644 --- a/homeassistant/components/elkm1/translations/es-419.json +++ b/homeassistant/components/elkm1/translations/es-419.json @@ -11,14 +11,6 @@ }, "step": { "user": { - "data": { - "address": "La direcci\u00f3n IP, dominio o puerto serie si se conecta via serial.", - "password": "Contrase\u00f1a (solo segura).", - "prefix": "Un prefijo \u00fanico (d\u00e9jelo en blanco si solo tiene un ElkM1).", - "protocol": "Protocolo", - "temperature_unit": "La unidad de temperatura que utiliza ElkM1.", - "username": "Nombre de usuario (solo seguro)." - }, "description": "La cadena de direcci\u00f3n debe tener el formato 'direcci\u00f3n[:puerto]' para 'seguro' y 'no seguro'. Ejemplo: '192.168.1.1'. El puerto es opcional y el valor predeterminado es 2101 para 'no seguro' y 2601 para 'seguro'. Para el protocolo serie, la direcci\u00f3n debe estar en la forma 'tty[:baudios]'. Ejemplo: '/dev/ttyS1'. La velocidad en baudios es opcional y su valor predeterminado es 115200.", "title": "Con\u00e9ctese al control Elk-M1" } diff --git a/homeassistant/components/elkm1/translations/es.json b/homeassistant/components/elkm1/translations/es.json index 06c0d9e257e..46e25dd288f 100644 --- a/homeassistant/components/elkm1/translations/es.json +++ b/homeassistant/components/elkm1/translations/es.json @@ -37,13 +37,7 @@ }, "user": { "data": { - "address": "La direcci\u00f3n IP o dominio o puerto serie si se conecta a trav\u00e9s de serie.", - "device": "Dispositivo", - "password": "Contrase\u00f1a", - "prefix": "Un prefijo \u00fanico (d\u00e9jalo en blanco si s\u00f3lo tienes un Elk-M1).", - "protocol": "Protocolo", - "temperature_unit": "La temperatura que usa la unidad Elk-M1", - "username": "Usuario" + "device": "Dispositivo" }, "description": "La cadena de direcci\u00f3n debe estar en el formato 'direcci\u00f3n[:puerto]' para 'seguro' y 'no-seguro'. Ejemplo: '192.168.1.1'. El puerto es opcional y el valor predeterminado es 2101 para 'no-seguro' y 2601 para 'seguro'. Para el protocolo serie, la direcci\u00f3n debe tener la forma 'tty[:baudios]'. Ejemplo: '/dev/ttyS1'. Los baudios son opcionales y el valor predeterminado es 115200.", "title": "Conectar con Control Elk-M1" diff --git a/homeassistant/components/elkm1/translations/et.json b/homeassistant/components/elkm1/translations/et.json index d874763045f..fbefde0a118 100644 --- a/homeassistant/components/elkm1/translations/et.json +++ b/homeassistant/components/elkm1/translations/et.json @@ -4,7 +4,9 @@ "address_already_configured": "Selle aadressiga ElkM1 on juba seadistatud", "already_configured": "Selle eesliitega ElkM1 on juba seadistatud", "already_in_progress": "Seadistamine juba k\u00e4ib", - "cannot_connect": "\u00dchendumine nurjus" + "cannot_connect": "\u00dchendumine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" }, "error": { "cannot_connect": "\u00dchendamine nurjus", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "IP-aadress v\u00f5i domeen v\u00f5i jadaport, kui \u00fchendatakse jadaliidese kaudu.", - "device": "Seade", - "password": "Salas\u00f5na", - "prefix": "Unikaalne eesliide (j\u00e4ta t\u00fchjaks kui on ainult \u00fcks ElkM1).", - "protocol": "Protokoll", - "temperature_unit": "ElkM1'i temperatuuri\u00fchik.", - "username": "Kasutajanimi" + "device": "Seade" }, "description": "Vali avastatud s\u00fcsteem v\u00f5i \"K\u00e4sitsi sisestamine\" kui \u00fchtegi seadet ei ole avastatud.", "title": "\u00dchendu Elk-M1 Control" diff --git a/homeassistant/components/elkm1/translations/fr.json b/homeassistant/components/elkm1/translations/fr.json index 07b5d132968..e7512a596d1 100644 --- a/homeassistant/components/elkm1/translations/fr.json +++ b/homeassistant/components/elkm1/translations/fr.json @@ -4,7 +4,9 @@ "address_already_configured": "Un ElkM1 avec cette adresse est d\u00e9j\u00e0 configur\u00e9", "already_configured": "Un ElkM1 avec ce pr\u00e9fixe est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", - "cannot_connect": "\u00c9chec de connexion" + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" }, "error": { "cannot_connect": "\u00c9chec de connexion", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "L'adresse IP ou le domaine ou le port s\u00e9rie si vous vous connectez via s\u00e9rie.", - "device": "Appareil", - "password": "Mot de passe", - "prefix": "Un pr\u00e9fixe unique (laissez vide si vous n'avez qu'un seul ElkM1).", - "protocol": "Protocole", - "temperature_unit": "L'unit\u00e9 de temp\u00e9rature utilis\u00e9e par ElkM1.", - "username": "Nom d'utilisateur" + "device": "Appareil" }, "description": "Choisissez un syst\u00e8me d\u00e9couvert ou 'Entr\u00e9e manuelle' si aucun appareil n'a \u00e9t\u00e9 d\u00e9couvert.", "title": "Se connecter a Elk-M1 Control" diff --git a/homeassistant/components/elkm1/translations/he.json b/homeassistant/components/elkm1/translations/he.json index 8d7b5c9b945..3dda78332f9 100644 --- a/homeassistant/components/elkm1/translations/he.json +++ b/homeassistant/components/elkm1/translations/he.json @@ -2,7 +2,9 @@ "config": { "abort": { "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", @@ -31,10 +33,7 @@ }, "user": { "data": { - "device": "\u05d4\u05ea\u05e7\u05df", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "protocol": "\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + "device": "\u05d4\u05ea\u05e7\u05df" }, "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05d0\u05dc \u05d1\u05e7\u05e8\u05ea Elk-M1" } diff --git a/homeassistant/components/elkm1/translations/hu.json b/homeassistant/components/elkm1/translations/hu.json index d076ad684c2..d744ac578ab 100644 --- a/homeassistant/components/elkm1/translations/hu.json +++ b/homeassistant/components/elkm1/translations/hu.json @@ -3,8 +3,10 @@ "abort": { "address_already_configured": "Az ElkM1 ezzel a c\u00edmmel m\u00e1r konfigur\u00e1lva van", "already_configured": "Az ezzel az el\u0151taggal rendelkez\u0151 ElkM1 m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Az IP-c\u00edm vagy tartom\u00e1ny vagy soros port, ha soros kapcsolaton kereszt\u00fcl csatlakozik.", - "device": "Eszk\u00f6z", - "password": "Jelsz\u00f3", - "prefix": "Egyedi el\u0151tag (hagyja \u00fcresen, ha csak egy ElkM1 van).", - "protocol": "Protokoll", - "temperature_unit": "Az ElkM1 h\u0151m\u00e9rs\u00e9kleti egys\u00e9g haszn\u00e1lja.", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + "device": "Eszk\u00f6z" }, "description": "V\u00e1lasszon egy felfedezett rendszert vagy a \u201eK\u00e9zi bevitelt\u201d, ha nem \u00e9szlelt eszk\u00f6zt.", "title": "Csatlakoz\u00e1s az Elk-M1 vez\u00e9rl\u0151h\u00f6z" diff --git a/homeassistant/components/elkm1/translations/id.json b/homeassistant/components/elkm1/translations/id.json index 782906fac0a..4512ad040e1 100644 --- a/homeassistant/components/elkm1/translations/id.json +++ b/homeassistant/components/elkm1/translations/id.json @@ -4,7 +4,9 @@ "address_already_configured": "ElkM1 dengan alamat ini sudah dikonfigurasi", "already_configured": "ElkM1 dengan prefiks ini sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" }, "error": { "cannot_connect": "Gagal terhubung", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Alamat IP atau domain atau port serial jika terhubung melalui serial.", - "device": "Perangkat", - "password": "Kata Sandi", - "prefix": "Prefiks unik (kosongkan jika hanya ada satu ElkM1).", - "protocol": "Protokol", - "temperature_unit": "Unit suhu yang digunakan ElkM1.", - "username": "Nama Pengguna" + "device": "Perangkat" }, "description": "Pilih sistem yang ditemukan atau 'Entri Manual' jika tidak ada perangkat yang ditemukan.", "title": "Hubungkan ke Kontrol Elk-M1" diff --git a/homeassistant/components/elkm1/translations/it.json b/homeassistant/components/elkm1/translations/it.json index fa05ab5d436..9284d2a495c 100644 --- a/homeassistant/components/elkm1/translations/it.json +++ b/homeassistant/components/elkm1/translations/it.json @@ -4,7 +4,9 @@ "address_already_configured": "Un ElkM1 con questo indirizzo \u00e8 gi\u00e0 configurato", "already_configured": "Un ElkM1 con questo prefisso \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" }, "error": { "cannot_connect": "Impossibile connettersi", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "L'indirizzo IP o il dominio o la porta seriale se ci si connette tramite seriale.", - "device": "Dispositivo", - "password": "Password", - "prefix": "Un prefisso univoco (lascia vuoto se disponi di un solo ElkM1).", - "protocol": "Protocollo", - "temperature_unit": "L'unit\u00e0 di temperatura utilizzata da ElkM1.", - "username": "Nome utente" + "device": "Dispositivo" }, "description": "Scegli un sistema rilevato o \"Inserimento manuale\" se non sono stati rilevati dispositivi.", "title": "Connettiti al controllo Elk-M1" diff --git a/homeassistant/components/elkm1/translations/ja.json b/homeassistant/components/elkm1/translations/ja.json index f280d6d6276..3d86e5f673d 100644 --- a/homeassistant/components/elkm1/translations/ja.json +++ b/homeassistant/components/elkm1/translations/ja.json @@ -4,7 +4,9 @@ "address_already_configured": "\u3053\u306e\u30a2\u30c9\u30ec\u30b9\u306eElkM1\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_configured": "\u3053\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3092\u6301\u3064ElkM1\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "IP\u30a2\u30c9\u30ec\u30b9\u307e\u305f\u306f\u30c9\u30e1\u30a4\u30f3\u3001\u30b7\u30ea\u30a2\u30eb\u3067\u63a5\u7d9a\u3059\u308b\u5834\u5408\u306f\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3002", - "device": "\u30c7\u30d0\u30a4\u30b9", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "prefix": "\u30e6\u30cb\u30fc\u30af(\u4e00\u610f)\u306a\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9(ElkM1\u304c1\u3064\u3057\u304b\u306a\u3044\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059)", - "protocol": "\u30d7\u30ed\u30c8\u30b3\u30eb", - "temperature_unit": "ElkM1\u304c\u4f7f\u7528\u3059\u308b\u6e29\u5ea6\u5358\u4f4d\u3002", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + "device": "\u30c7\u30d0\u30a4\u30b9" }, "description": "\u30a2\u30c9\u30ec\u30b9\u6587\u5b57\u5217\u306f\u3001 '\u30bb\u30ad\u30e5\u30a2 '\u304a\u3088\u3073 '\u975e\u30bb\u30ad\u30e5\u30a2 '\u306e\u5834\u5408\u306f\u3001'address[:port]'\u306e\u5f62\u5f0f\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u4f8b: '192.168.1.1'\u3002\u30dd\u30fc\u30c8\u306f\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306f'\u975e\u30bb\u30ad\u30e5\u30a2'\u306e\u5834\u5408\u306f\u30012101 \u3067'\u30bb\u30ad\u30e5\u30a2'\u306e\u5834\u5408\u306f\u30012601 \u3067\u3059\u3002\u30b7\u30ea\u30a2\u30eb \u30d7\u30ed\u30c8\u30b3\u30eb\u306e\u5834\u5408\u3001\u30a2\u30c9\u30ec\u30b9\u306f\u3001'tty[:baud]' \u306e\u5f62\u5f0f\u3067\u306a\u3051\u308c\u3070\u306a\u308a\u307e\u305b\u3093\u3002\u4f8b: '/dev/ttyS1'\u3002\u30dc\u30fc\u306f\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306f115200\u3067\u3059\u3002", "title": "Elk-M1 Control\u306b\u63a5\u7d9a" diff --git a/homeassistant/components/elkm1/translations/ko.json b/homeassistant/components/elkm1/translations/ko.json index 507f741676a..6ffa3679946 100644 --- a/homeassistant/components/elkm1/translations/ko.json +++ b/homeassistant/components/elkm1/translations/ko.json @@ -11,14 +11,6 @@ }, "step": { "user": { - "data": { - "address": "\uc2dc\ub9ac\uc5bc\uc744 \ud1b5\ud574 \uc5f0\uacb0\ud558\ub294 \uacbd\uc6b0\uc758 IP \uc8fc\uc18c\ub098 \ub3c4\uba54\uc778 \ub610\ub294 \uc2dc\ub9ac\uc5bc \ud3ec\ud2b8.", - "password": "\ube44\ubc00\ubc88\ud638", - "prefix": "\uace0\uc720\ud55c \uc811\ub450\uc0ac (ElkM1 \uc774 \ud558\ub098\ub9cc \uc788\uc73c\uba74 \ube44\uc6cc\ub450\uc138\uc694).", - "protocol": "\ud504\ub85c\ud1a0\ucf5c", - "temperature_unit": "ElkM1 \uc774 \uc0ac\uc6a9\ud558\ub294 \uc628\ub3c4 \ub2e8\uc704.", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - }, "description": "\uc8fc\uc18c \ubb38\uc790\uc5f4\uc740 '\ubcf4\uc548' \ubc0f '\ube44\ubcf4\uc548'\uc5d0 \ub300\ud574 'address[:port]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '192.168.1.1'. \ud3ec\ud2b8\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 '\ube44\ubcf4\uc548' \uc758 \uacbd\uc6b0 2101 \uc774\uace0 '\ubcf4\uc548' \uc758 \uacbd\uc6b0 2601 \uc785\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ud504\ub85c\ud1a0\ucf5c\uc758 \uacbd\uc6b0 \uc8fc\uc18c\ub294 'tty[:baud]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '/dev/ttyS1'. \uc804\uc1a1 \uc18d\ub3c4\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 115200 \uc785\ub2c8\ub2e4.", "title": "Elk-M1 \uc81c\uc5b4\uc5d0 \uc5f0\uacb0\ud558\uae30" } diff --git a/homeassistant/components/elkm1/translations/lb.json b/homeassistant/components/elkm1/translations/lb.json index 084b4a1347f..14d2a82b97e 100644 --- a/homeassistant/components/elkm1/translations/lb.json +++ b/homeassistant/components/elkm1/translations/lb.json @@ -11,14 +11,6 @@ }, "step": { "user": { - "data": { - "address": "IP Adress oder Domain oder Serielle Port falls d'Verbindung seriell ass.", - "password": "Passwuert", - "prefix": "Een eenzegaartege Pr\u00e4fix (eidel lossen wann et n\u00ebmmen 1 ElkM1 g\u00ebtt)", - "protocol": "Protokoll", - "temperature_unit": "Temperatur Eenheet d\u00e9i den ElkM1 benotzt.", - "username": "Benotzernumm" - }, "description": "D'Adress muss an der Form 'adress[:port]' fir 'ges\u00e9chert' an 'onges\u00e9chert' sinn. Beispill: '192.168.1.1'. De Port os optionell an ass standardm\u00e9isseg op 2101 fir 'onges\u00e9chert' an op 2601 fir 'ges\u00e9chert' d\u00e9fin\u00e9iert. Fir de serielle Protokoll, muss d'Adress an der Form 'tty[:baud]' sinn. Beispill: '/dev/ttyS1'. Baud Rate ass optionell an ass standardm\u00e9isseg op 115200 d\u00e9fin\u00e9iert.", "title": "Mat Elk-M1 Control verbannen" } diff --git a/homeassistant/components/elkm1/translations/nl.json b/homeassistant/components/elkm1/translations/nl.json index c3efd45dbc0..c27293ce050 100644 --- a/homeassistant/components/elkm1/translations/nl.json +++ b/homeassistant/components/elkm1/translations/nl.json @@ -4,7 +4,9 @@ "address_already_configured": "Een ElkM1 met dit adres is al geconfigureerd", "already_configured": "Een ElkM1 met dit voorvoegsel is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", - "cannot_connect": "Kan geen verbinding maken" + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Het IP-adres of domein of seri\u00eble poort bij verbinding via serieel.", - "device": "Apparaat", - "password": "Wachtwoord", - "prefix": "Een uniek voorvoegsel (laat dit leeg als u maar \u00e9\u00e9n ElkM1 heeft).", - "protocol": "Protocol", - "temperature_unit": "De temperatuureenheid die ElkM1 gebruikt.", - "username": "Gebruikersnaam" + "device": "Apparaat" }, "description": "Kies een ontdekt systeem of 'Handmatige invoer' als er geen apparaten zijn ontdekt.", "title": "Maak verbinding met Elk-M1 Control" diff --git a/homeassistant/components/elkm1/translations/no.json b/homeassistant/components/elkm1/translations/no.json index ced77ff0d04..221b9206c81 100644 --- a/homeassistant/components/elkm1/translations/no.json +++ b/homeassistant/components/elkm1/translations/no.json @@ -4,7 +4,9 @@ "address_already_configured": "En ElkM1 med denne adressen er allerede konfigurert", "already_configured": "En ElkM1 med dette prefikset er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" }, "error": { "cannot_connect": "Tilkobling mislyktes", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "IP-adressen eller domenet eller seriell port hvis du kobler til via seriell.", - "device": "Enhet", - "password": "Passord", - "prefix": "Et unikt prefiks (la v\u00e6re tomt hvis du bare har en ElkM1).", - "protocol": "Protokoll", - "temperature_unit": "Temperaturenheten ElkM1 bruker.", - "username": "Brukernavn" + "device": "Enhet" }, "description": "Velg et oppdaget system eller \"Manuell oppf\u00f8ring\" hvis ingen enheter har blitt oppdaget.", "title": "Koble til Elk-M1-kontroll" diff --git a/homeassistant/components/elkm1/translations/pl.json b/homeassistant/components/elkm1/translations/pl.json index 62900716f62..3b27226438f 100644 --- a/homeassistant/components/elkm1/translations/pl.json +++ b/homeassistant/components/elkm1/translations/pl.json @@ -4,7 +4,9 @@ "address_already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane z tym adresem", "already_configured": "ElkM1 z tym prefiksem jest ju\u017c skonfigurowany", "already_in_progress": "Konfiguracja jest ju\u017c w toku", - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Adres IP, domena lub port szeregowy w przypadku po\u0142\u0105czenia szeregowego.", - "device": "Urz\u0105dzenie", - "password": "Has\u0142o", - "prefix": "Unikatowy prefiks (pozostaw pusty, je\u015bli masz tylko jeden ElkM1).", - "protocol": "Protok\u00f3\u0142", - "temperature_unit": "Jednostka temperatury u\u017cywanej przez ElkM1.", - "username": "Nazwa u\u017cytkownika" + "device": "Urz\u0105dzenie" }, "description": "Wybierz wykryty system lub \u201eWpis r\u0119czny\u201d, je\u015bli nie wykryto \u017cadnych urz\u0105dze\u0144.", "title": "Pod\u0142\u0105czenie do sterownika Elk-M1" diff --git a/homeassistant/components/elkm1/translations/pt-BR.json b/homeassistant/components/elkm1/translations/pt-BR.json index 7cb9a5f101a..6fb03ab66e8 100644 --- a/homeassistant/components/elkm1/translations/pt-BR.json +++ b/homeassistant/components/elkm1/translations/pt-BR.json @@ -4,7 +4,9 @@ "address_already_configured": "Um ElkM1 com este endere\u00e7o j\u00e1 est\u00e1 configurado", "already_configured": "A conta j\u00e1 foi configurada", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" }, "error": { "cannot_connect": "Falha ao conectar", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "O endere\u00e7o IP ou dom\u00ednio ou porta serial se estiver conectando via serial.", - "device": "Dispositivo", - "password": "Senha", - "prefix": "Um prefixo exclusivo (deixe em branco se voc\u00ea tiver apenas um ElkM1).", - "protocol": "Protocolo", - "temperature_unit": "A unidade de temperatura que ElkM1 usa.", - "username": "Usu\u00e1rio" + "device": "Dispositivo" }, "description": "Escolha um sistema descoberto ou 'Entrada Manual' se nenhum dispositivo foi descoberto.", "title": "Conecte ao controle Elk-M1" diff --git a/homeassistant/components/elkm1/translations/pt.json b/homeassistant/components/elkm1/translations/pt.json index 48d278ac354..2e669c21f1e 100644 --- a/homeassistant/components/elkm1/translations/pt.json +++ b/homeassistant/components/elkm1/translations/pt.json @@ -4,15 +4,6 @@ "cannot_connect": "Falha na liga\u00e7\u00e3o", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" - }, - "step": { - "user": { - "data": { - "password": "Palavra-passe (segura apenas)", - "protocol": "Protocolo", - "username": "Nome de utilizador (apenas seguro)." - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/elkm1/translations/ru.json b/homeassistant/components/elkm1/translations/ru.json index 1b4bf7250d7..624166fea48 100644 --- a/homeassistant/components/elkm1/translations/ru.json +++ b/homeassistant/components/elkm1/translations/ru.json @@ -4,7 +4,9 @@ "address_already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 \u044d\u0442\u0438\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 \u044d\u0442\u0438\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "IP-\u0430\u0434\u0440\u0435\u0441, \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442", - "device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "prefix": "\u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0435\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d ElkM1)", - "protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", - "temperature_unit": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + "device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438\u043b\u0438 'Manual Entry', \u0435\u0441\u043b\u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0431\u044b\u043b\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b.", "title": "Elk-M1 Control" diff --git a/homeassistant/components/elkm1/translations/sl.json b/homeassistant/components/elkm1/translations/sl.json index a815011988e..50988abb1d3 100644 --- a/homeassistant/components/elkm1/translations/sl.json +++ b/homeassistant/components/elkm1/translations/sl.json @@ -11,14 +11,6 @@ }, "step": { "user": { - "data": { - "address": "IP naslov, domena ali serijska vrata, \u010de se povezujete prek serijske povezave.", - "password": "Geslo (samo varno).", - "prefix": "Edinstvena predpona (pustite prazno, \u010de imate samo en ElkM1).", - "protocol": "Protokol", - "temperature_unit": "Temperaturna enota, ki jo uporablja ElkM1.", - "username": "Uporabni\u0161ko ime (samo varno)." - }, "description": "Naslov mora biti v obliki \"naslov[:port]\" za \"varno\" in \"ne-varno'. Primer: '192.168.1.1'. Vrata so neobvezna in so privzeto nastavljena na 2101 za \"non-secure\" in 2601 za 'varno'. Za serijski protokol, mora biti naslov v obliki \" tty[:baud]'. Primer: '/dev/ttyS1'. Baud je neobvezen in privzeto nastavljen na 115200.", "title": "Pove\u017eite se z Elk-M1 Control" } diff --git a/homeassistant/components/elkm1/translations/sv.json b/homeassistant/components/elkm1/translations/sv.json index 23a7d475a6f..19d9bb17e4b 100644 --- a/homeassistant/components/elkm1/translations/sv.json +++ b/homeassistant/components/elkm1/translations/sv.json @@ -4,13 +4,6 @@ "cannot_connect": "Det gick inte att ansluta, f\u00f6rs\u00f6k igen", "invalid_auth": "Ogiltig autentisering", "unknown": "Ov\u00e4ntat fel" - }, - "step": { - "user": { - "data": { - "protocol": "Protokoll" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/elkm1/translations/tr.json b/homeassistant/components/elkm1/translations/tr.json index e8f147c8d57..d1c865bffd5 100644 --- a/homeassistant/components/elkm1/translations/tr.json +++ b/homeassistant/components/elkm1/translations/tr.json @@ -4,7 +4,9 @@ "address_already_configured": "Bu adrese sahip bir ElkM1 zaten yap\u0131land\u0131r\u0131lm\u0131\u015ft\u0131r", "already_configured": "Bu \u00f6nek ile bir ElkM1 zaten yap\u0131land\u0131r\u0131lm\u0131\u015ft\u0131r", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", - "cannot_connect": "Ba\u011flanma hatas\u0131" + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "Seri yoluyla ba\u011flan\u0131l\u0131yorsa IP adresi veya etki alan\u0131 veya seri ba\u011flant\u0131 noktas\u0131.", - "device": "Cihaz", - "password": "Parola", - "prefix": "Benzersiz bir \u00f6nek (yaln\u0131zca bir ElkM1'iniz varsa bo\u015f b\u0131rak\u0131n).", - "protocol": "Protokol", - "temperature_unit": "ElkM1'in kulland\u0131\u011f\u0131 s\u0131cakl\u0131k birimi.", - "username": "Kullan\u0131c\u0131 Ad\u0131" + "device": "Cihaz" }, "description": "Ke\u015ffedilen bir sistem veya ke\u015ffedilmemi\u015fse 'Manuel Giri\u015f' se\u00e7in.", "title": "Elk-M1 Kontrol\u00fcne Ba\u011flan\u0131n" diff --git a/homeassistant/components/elkm1/translations/uk.json b/homeassistant/components/elkm1/translations/uk.json index a8e711a4590..d794c6ecf69 100644 --- a/homeassistant/components/elkm1/translations/uk.json +++ b/homeassistant/components/elkm1/translations/uk.json @@ -11,14 +11,6 @@ }, "step": { "user": { - "data": { - "address": "IP-\u0430\u0434\u0440\u0435\u0441\u0430, \u0434\u043e\u043c\u0435\u043d\u043d\u0435 \u0456\u043c'\u044f \u0430\u0431\u043e \u043f\u043e\u0441\u043b\u0456\u0434\u043e\u0432\u043d\u0438\u0439 \u043f\u043e\u0440\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "prefix": "\u0423\u043d\u0456\u043a\u0430\u043b\u044c\u043d\u0438\u0439 \u043f\u0440\u0435\u0444\u0456\u043a\u0441 (\u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c, \u044f\u043a\u0449\u043e \u0443 \u0412\u0430\u0441 \u0442\u0456\u043b\u044c\u043a\u0438 \u043e\u0434\u0438\u043d ElkM1)", - "protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", - "temperature_unit": "\u041e\u0434\u0438\u043d\u0438\u0446\u044f \u0432\u0438\u043c\u0456\u0440\u0443 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0438", - "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - }, "description": "\u0420\u044f\u0434\u043e\u043a \u0430\u0434\u0440\u0435\u0441\u0438 \u043f\u043e\u0432\u0438\u043d\u043d\u0430 \u0431\u0443\u0442\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0456 'addres[:port]' \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0456\u0432 'secure' \u0456 'non-secure' (\u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434: '192.168.1.1'). \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'port' \u0432\u043a\u0430\u0437\u0443\u0432\u0430\u0442\u0438 \u043d\u0435\u043e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u043e, \u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0432\u0456\u043d \u0434\u043e\u0440\u0456\u0432\u043d\u044e\u0454 2101 \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 'non-secure' \u0456 2601 \u0434\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 'secure'. \u0414\u043b\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 'serial' \u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u043e\u0432\u0438\u043d\u043d\u0430 \u0431\u0443\u0442\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0456 'tty[:baud]' (\u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434: '/dev/ttyS1'). \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 'baud' \u0432\u043a\u0430\u0437\u0443\u0432\u0430\u0442\u0438 \u043d\u0435\u043e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u043e, \u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0432\u0456\u043d \u0434\u043e\u0440\u0456\u0432\u043d\u044e\u0454 115200.", "title": "Elk-M1 Control" } diff --git a/homeassistant/components/elkm1/translations/zh-Hant.json b/homeassistant/components/elkm1/translations/zh-Hant.json index 543f93626ce..9405180fe0d 100644 --- a/homeassistant/components/elkm1/translations/zh-Hant.json +++ b/homeassistant/components/elkm1/translations/zh-Hant.json @@ -4,7 +4,9 @@ "address_already_configured": "\u4f7f\u7528\u6b64\u4f4d\u5740\u7684\u4e00\u7d44 ElkM1 \u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_configured": "\u4f7f\u7528\u6b64 Prefix \u7684\u4e00\u7d44 ElkM1 \u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", @@ -37,13 +39,7 @@ }, "user": { "data": { - "address": "IP \u6216\u7db2\u57df\u540d\u7a31\u3001\u5e8f\u5217\u57e0\uff08\u5047\u5982\u900f\u904e\u5e8f\u5217\u9023\u7dda\uff09\u3002", - "device": "\u88dd\u7f6e", - "password": "\u5bc6\u78bc", - "prefix": "\u7368\u4e00\u7684 Prefix\uff08\u5047\u5982\u50c5\u6709\u4e00\u7d44 ElkM1 \u5247\u4fdd\u7559\u7a7a\u767d\uff09\u3002", - "protocol": "\u901a\u8a0a\u5354\u5b9a", - "temperature_unit": "ElkM1 \u4f7f\u7528\u6eab\u5ea6\u55ae\u4f4d\u3002", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" + "device": "\u88dd\u7f6e" }, "description": "\u9078\u64c7\u6240\u767c\u73fe\u5230\u7684\u7cfb\u7d71\uff0c\u6216\u5047\u5982\u6c92\u627e\u5230\u7684\u8a71\u9032\u884c\u624b\u52d5\u8f38\u5165\u3002", "title": "\u9023\u7dda\u81f3 Elk-M1 Control" diff --git a/homeassistant/components/emulated_roku/translations/hu.json b/homeassistant/components/emulated_roku/translations/hu.json index 53b66f6db19..74cbdbcec79 100644 --- a/homeassistant/components/emulated_roku/translations/hu.json +++ b/homeassistant/components/emulated_roku/translations/hu.json @@ -10,7 +10,7 @@ "advertise_port": "Port k\u00f6zl\u00e9se", "host_ip": "IP c\u00edm", "listen_port": "Port", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "upnp_bind_multicast": "K\u00f6t\u00f6tt multicast (igaz/hamis)" }, "title": "A kiszolg\u00e1l\u00f3 szerver konfigur\u00e1l\u00e1sa" diff --git a/homeassistant/components/enphase_envoy/translations/en.json b/homeassistant/components/enphase_envoy/translations/en.json index ff600fea454..5d4617ed9fa 100644 --- a/homeassistant/components/enphase_envoy/translations/en.json +++ b/homeassistant/components/enphase_envoy/translations/en.json @@ -2,8 +2,7 @@ "config": { "abort": { "already_configured": "Device is already configured", - "reauth_successful": "Re-authentication was successful", - "not_ipv4_address": "Only IPv4 addresess are supported" + "reauth_successful": "Re-authentication was successful" }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/environment_canada/translations/de.json b/homeassistant/components/environment_canada/translations/de.json index c351683007f..6c9be14be27 100644 --- a/homeassistant/components/environment_canada/translations/de.json +++ b/homeassistant/components/environment_canada/translations/de.json @@ -15,7 +15,7 @@ "longitude": "L\u00e4ngengrad", "station": "ID der Wetterstation" }, - "description": "Es muss entweder eine Stations-ID oder der Breitengrad/L\u00e4ngengrad angegeben werden. Als Standardwerte f\u00fcr Breitengrad/L\u00e4ngengrad werden die in Ihrer Home Assistant-Installation konfigurierten Werte verwendet. Bei Angabe von Koordinaten wird die den Koordinaten am n\u00e4chsten gelegene Wetterstation verwendet. Wenn ein Stationscode verwendet wird, muss er dem Format entsprechen: PP/Code, wobei PP f\u00fcr die zweistellige Provinz und Code f\u00fcr die Stationskennung steht. Die Liste der Stations-IDs findest du hier: https://dd.weather.gc.ca/citypage_weather/docs/site_list_towns_en.csv. Die Wetterinformationen k\u00f6nnen entweder in Englisch oder Franz\u00f6sisch abgerufen werden.", + "description": "Es muss entweder eine Stations-ID oder der Breitengrad/L\u00e4ngengrad angegeben werden. Als Standardwerte f\u00fcr Breitengrad/L\u00e4ngengrad werden die in deiner Home Assistant-Installation konfigurierten Werte verwendet. Bei Angabe von Koordinaten wird die den Koordinaten am n\u00e4chsten gelegene Wetterstation verwendet. Wenn ein Stationscode verwendet wird, muss er dem Format entsprechen: PP/Code, wobei PP f\u00fcr die zweistellige Provinz und Code f\u00fcr die Stationskennung steht. Die Liste der Stations-IDs findest du hier: https://dd.weather.gc.ca/citypage_weather/docs/site_list_towns_en.csv. Die Wetterinformationen k\u00f6nnen entweder in Englisch oder Franz\u00f6sisch abgerufen werden.", "title": "Standort Kanada: Wetterstandort und Sprache" } } diff --git a/homeassistant/components/epson/translations/hu.json b/homeassistant/components/epson/translations/hu.json index e3aa507b7c1..ff56db57da9 100644 --- a/homeassistant/components/epson/translations/hu.json +++ b/homeassistant/components/epson/translations/hu.json @@ -8,7 +8,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" } } } diff --git a/homeassistant/components/esphome/translations/cs.json b/homeassistant/components/esphome/translations/cs.json index c6885e06851..cdd86bd2577 100644 --- a/homeassistant/components/esphome/translations/cs.json +++ b/homeassistant/components/esphome/translations/cs.json @@ -11,7 +11,7 @@ "invalid_psk": "Transportn\u00ed \u0161ifrovac\u00ed kl\u00ed\u010d je neplatn\u00fd. Ujist\u011bte se, \u017ee odpov\u00edd\u00e1 tomu, co m\u00e1te ve sv\u00e9 konfiguraci", "resolve_error": "Nelze naj\u00edt IP adresu uzlu ESP. Pokud tato chyba p\u0159etrv\u00e1v\u00e1, nastavte statickou adresu IP: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips" }, - "flow_title": "ESPHome: {name}", + "flow_title": "{name}", "step": { "authenticate": { "data": { diff --git a/homeassistant/components/esphome/translations/hu.json b/homeassistant/components/esphome/translations/hu.json index 17af0e57d26..e8e6c9b2dc2 100644 --- a/homeassistant/components/esphome/translations/hu.json +++ b/homeassistant/components/esphome/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { diff --git a/homeassistant/components/evil_genius_labs/translations/cs.json b/homeassistant/components/evil_genius_labs/translations/cs.json index e1bf8e7f45f..7a929c1286f 100644 --- a/homeassistant/components/evil_genius_labs/translations/cs.json +++ b/homeassistant/components/evil_genius_labs/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "error": { + "timeout": "Vypr\u0161el \u010dasov\u00fd limit pro nav\u00e1z\u00e1n\u00ed spojen\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" } } diff --git a/homeassistant/components/ezviz/translations/it.json b/homeassistant/components/ezviz/translations/it.json index 0c6ce669ba3..febba0cad51 100644 --- a/homeassistant/components/ezviz/translations/it.json +++ b/homeassistant/components/ezviz/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured_account": "L'account \u00e8 gi\u00e0 configurato", - "ezviz_cloud_account_missing": "Ezviz cloud account mancante. Si prega di riconfigurare l'account Ezviz cloud", + "ezviz_cloud_account_missing": "Ezviz cloud account mancante. Riconfigura l'account Ezviz cloud", "unknown": "Errore imprevisto" }, "error": { diff --git a/homeassistant/components/fan/translations/hu.json b/homeassistant/components/fan/translations/hu.json index ec3175b8290..50e659b001a 100644 --- a/homeassistant/components/fan/translations/hu.json +++ b/homeassistant/components/fan/translations/hu.json @@ -9,6 +9,7 @@ "is_on": "{entity_name} be van kapcsolva" }, "trigger_type": { + "changed_states": "{entity_name} be- vagy kikapcsolt", "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" diff --git a/homeassistant/components/fibaro/translations/bg.json b/homeassistant/components/fibaro/translations/bg.json new file mode 100644 index 00000000000..9f39b8c9185 --- /dev/null +++ b/homeassistant/components/fibaro/translations/bg.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "url": "URL \u0432\u044a\u0432 \u0444\u043e\u0440\u043c\u0430\u0442 URL", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/ca.json b/homeassistant/components/fibaro/translations/ca.json new file mode 100644 index 00000000000..8e04b3567f8 --- /dev/null +++ b/homeassistant/components/fibaro/translations/ca.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "user": { + "data": { + "import_plugins": "Vols importar les entitats dels complements Fibaro?", + "password": "Contrasenya", + "url": "URL en el format http://HOST/api/", + "username": "Nom d'usuari" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/de.json b/homeassistant/components/fibaro/translations/de.json new file mode 100644 index 00000000000..831abc85929 --- /dev/null +++ b/homeassistant/components/fibaro/translations/de.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "user": { + "data": { + "import_plugins": "Entit\u00e4ten aus Fibaro-Plugins importieren?", + "password": "Passwort", + "url": "URL im Format http://HOST/api/", + "username": "Benutzername" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/el.json b/homeassistant/components/fibaro/translations/el.json new file mode 100644 index 00000000000..d2a2659646f --- /dev/null +++ b/homeassistant/components/fibaro/translations/el.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "user": { + "data": { + "import_plugins": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03b1\u03c0\u03cc \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1 fibaro;", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03bc\u03b5 \u03c4\u03b7 \u03bc\u03bf\u03c1\u03c6\u03ae http://HOST/api/", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/en.json b/homeassistant/components/fibaro/translations/en.json index 2baeb3a7213..6bcff530798 100644 --- a/homeassistant/components/fibaro/translations/en.json +++ b/homeassistant/components/fibaro/translations/en.json @@ -11,9 +11,9 @@ "step": { "user": { "data": { - "url": "URL in the format http://HOST/api/", "import_plugins": "Import entities from fibaro plugins?", "password": "Password", + "url": "URL in the format http://HOST/api/", "username": "Username" } } diff --git a/homeassistant/components/fibaro/translations/et.json b/homeassistant/components/fibaro/translations/et.json new file mode 100644 index 00000000000..d9f140f8380 --- /dev/null +++ b/homeassistant/components/fibaro/translations/et.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "user": { + "data": { + "import_plugins": "Kas importida olemid fibaro pistikprogrammidest?", + "password": "Salas\u00f5na", + "url": "URL vormingus http://HOST/api/", + "username": "Kasutajanimi" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/fr.json b/homeassistant/components/fibaro/translations/fr.json new file mode 100644 index 00000000000..8dee1529959 --- /dev/null +++ b/homeassistant/components/fibaro/translations/fr.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "user": { + "data": { + "import_plugins": "Importer les entit\u00e9s \u00e0 partir des plugins fibaro\u00a0?", + "password": "Mot de passe", + "url": "URL au format http://HOST/api/", + "username": "Nom d'utilisateur" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/he.json b/homeassistant/components/fibaro/translations/he.json new file mode 100644 index 00000000000..c479d8488f2 --- /dev/null +++ b/homeassistant/components/fibaro/translations/he.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/hu.json b/homeassistant/components/fibaro/translations/hu.json new file mode 100644 index 00000000000..d976d4b1a96 --- /dev/null +++ b/homeassistant/components/fibaro/translations/hu.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "user": { + "data": { + "import_plugins": "Import\u00e1ln\u00e1 az entit\u00e1sokat a fibaro be\u00e9p\u00fcl\u0151 modulokb\u00f3l?", + "password": "Jelsz\u00f3", + "url": "URL a k\u00f6vetkez\u0151 form\u00e1tumban: http://C\u00cdM/api/", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/id.json b/homeassistant/components/fibaro/translations/id.json new file mode 100644 index 00000000000..715ad91c275 --- /dev/null +++ b/homeassistant/components/fibaro/translations/id.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "user": { + "data": { + "import_plugins": "Impor entitas dari plugin fibaro?", + "password": "Kata Sandi", + "url": "URL dalam format http://HOST/api/", + "username": "Nama Pengguna" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/it.json b/homeassistant/components/fibaro/translations/it.json new file mode 100644 index 00000000000..641ed94e49f --- /dev/null +++ b/homeassistant/components/fibaro/translations/it.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "user": { + "data": { + "import_plugins": "Vuoi importare le entit\u00e0 dai plugin fibaro?", + "password": "Password", + "url": "URL nel formato http://HOST/api/", + "username": "Nome utente" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/ja.json b/homeassistant/components/fibaro/translations/ja.json new file mode 100644 index 00000000000..8fc6562ff3b --- /dev/null +++ b/homeassistant/components/fibaro/translations/ja.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "user": { + "data": { + "import_plugins": "fibaro\u30d7\u30e9\u30b0\u30a4\u30f3\u304b\u3089\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u30a4\u30f3\u30dd\u30fc\u30c8\u3057\u307e\u3059\u304b\uff1f", + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "url": "URL\u306e\u5f62\u5f0f\u306f\u3001http://HOST/api/ \u3067\u3059", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/nl.json b/homeassistant/components/fibaro/translations/nl.json new file mode 100644 index 00000000000..72b8588d1e4 --- /dev/null +++ b/homeassistant/components/fibaro/translations/nl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd" + }, + "error": { + "cannot_connect": "Kon niet verbinden", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "user": { + "data": { + "import_plugins": "Entiteiten importeren uit fibaro-plug-ins?", + "password": "Wachtwoord", + "url": "URL in het formaat http://HOST/api/", + "username": "Gebruikersnaam" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/no.json b/homeassistant/components/fibaro/translations/no.json new file mode 100644 index 00000000000..8c868bb1ad8 --- /dev/null +++ b/homeassistant/components/fibaro/translations/no.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "user": { + "data": { + "import_plugins": "Importere enheter fra fibaro plugins?", + "password": "Passord", + "url": "URL i formatet http://HOST/api/", + "username": "Brukernavn" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/pl.json b/homeassistant/components/fibaro/translations/pl.json new file mode 100644 index 00000000000..ce4b2652d80 --- /dev/null +++ b/homeassistant/components/fibaro/translations/pl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "import_plugins": "Zaimportowa\u0107 encje z wtyczek fibaro?", + "password": "Has\u0142o", + "url": "Adres URL w formacie http://HOST/api/", + "username": "Nazwa u\u017cytkownika" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/pt-BR.json b/homeassistant/components/fibaro/translations/pt-BR.json new file mode 100644 index 00000000000..3e0f3139fa7 --- /dev/null +++ b/homeassistant/components/fibaro/translations/pt-BR.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "import_plugins": "Importar entidades de plugins fibaro?", + "password": "Senha", + "url": "URL no formato http://HOST/api/", + "username": "Usu\u00e1rio" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/ru.json b/homeassistant/components/fibaro/translations/ru.json new file mode 100644 index 00000000000..56e75b5fa34 --- /dev/null +++ b/homeassistant/components/fibaro/translations/ru.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "user": { + "data": { + "import_plugins": "\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0438\u0437 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432 fibaro", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 http://HOST/api/", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/tr.json b/homeassistant/components/fibaro/translations/tr.json new file mode 100644 index 00000000000..c873e0dafd3 --- /dev/null +++ b/homeassistant/components/fibaro/translations/tr.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "user": { + "data": { + "import_plugins": "Varl\u0131klar\u0131 fibaro eklentilerinden i\u00e7e aktar\u0131ls\u0131n m\u0131?", + "password": "Parola", + "url": "http://HOST/api/ bi\u00e7imindeki URL", + "username": "Kullan\u0131c\u0131 Ad\u0131" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/zh-Hant.json b/homeassistant/components/fibaro/translations/zh-Hant.json new file mode 100644 index 00000000000..e494fb1012f --- /dev/null +++ b/homeassistant/components/fibaro/translations/zh-Hant.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "user": { + "data": { + "import_plugins": "\u5f9e fibaro \u5916\u639b\u532f\u5165\u5be6\u9ad4\uff1f", + "password": "\u5bc6\u78bc", + "url": "URL \u683c\u5f0f\u70ba http://HOST/api/", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/bg.json b/homeassistant/components/filesize/translations/bg.json new file mode 100644 index 00000000000..cfee062d421 --- /dev/null +++ b/homeassistant/components/filesize/translations/bg.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + }, + "error": { + "not_allowed": "\u041f\u044a\u0442\u044f\u0442 \u043d\u0435 \u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d", + "not_valid": "\u041f\u044a\u0442\u044f\u0442 \u043d\u0435 \u0435 \u0432\u0430\u043b\u0438\u0434\u0435\u043d" + }, + "step": { + "user": { + "data": { + "file_path": "\u041f\u044a\u0442 \u043a\u044a\u043c \u0444\u0430\u0439\u043b\u0430" + } + } + } + }, + "title": "\u0420\u0430\u0437\u043c\u0435\u0440 \u043d\u0430 \u0444\u0430\u0439\u043b\u0430" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/ca.json b/homeassistant/components/filesize/translations/ca.json new file mode 100644 index 00000000000..7bdcb4ae522 --- /dev/null +++ b/homeassistant/components/filesize/translations/ca.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat" + }, + "error": { + "not_allowed": "Ruta no permesa", + "not_valid": "Ruta inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "file_path": "Ruta al fitxer" + } + } + } + }, + "title": "Mida de fitxer" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/de.json b/homeassistant/components/filesize/translations/de.json new file mode 100644 index 00000000000..ac101d35783 --- /dev/null +++ b/homeassistant/components/filesize/translations/de.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert" + }, + "error": { + "not_allowed": "Pfad nicht erlaubt", + "not_valid": "Pfad ung\u00fcltig" + }, + "step": { + "user": { + "data": { + "file_path": "Pfad zur Datei" + } + } + } + }, + "title": "Dateigr\u00f6\u00dfe" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/el.json b/homeassistant/components/filesize/translations/el.json new file mode 100644 index 00000000000..c1b8f4eab2c --- /dev/null +++ b/homeassistant/components/filesize/translations/el.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, + "error": { + "not_allowed": "\u0397 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03b4\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9", + "not_valid": "\u0397 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7" + }, + "step": { + "user": { + "data": { + "file_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf" + } + } + } + }, + "title": "Filesize" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/en.json b/homeassistant/components/filesize/translations/en.json index cd8954e5a71..1bad0b9b081 100644 --- a/homeassistant/components/filesize/translations/en.json +++ b/homeassistant/components/filesize/translations/en.json @@ -1,19 +1,19 @@ { "config": { - "step": { - "user": { - "data": { - "file_path": "Path to file" - } + "abort": { + "already_configured": "Service is already configured" + }, + "error": { + "not_allowed": "Path is not allowed", + "not_valid": "Path is not valid" + }, + "step": { + "user": { + "data": { + "file_path": "Path to file" + } + } } - }, - "error": { - "not_valid": "Path is not valid", - "not_allowed": "Path is not allowed" - }, - "abort": { - "already_configured": "Filepath is already configured" - } }, "title": "Filesize" - } \ No newline at end of file +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/et.json b/homeassistant/components/filesize/translations/et.json new file mode 100644 index 00000000000..b27b482e4cd --- /dev/null +++ b/homeassistant/components/filesize/translations/et.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba h\u00e4\u00e4lestatud" + }, + "error": { + "not_allowed": "Kirje asukoht on keelatud", + "not_valid": "Kirjet ei leitud" + }, + "step": { + "user": { + "data": { + "file_path": "Kirje asukoht" + } + } + } + }, + "title": "Filesize" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/fr.json b/homeassistant/components/filesize/translations/fr.json new file mode 100644 index 00000000000..8f2f8ffe1ce --- /dev/null +++ b/homeassistant/components/filesize/translations/fr.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "not_allowed": "Le chemin d'acc\u00e8s n'est pas autoris\u00e9", + "not_valid": "Le chemin d'acc\u00e8s n'est pas valide" + }, + "step": { + "user": { + "data": { + "file_path": "Chemin d'acc\u00e8s au fichier" + } + } + } + }, + "title": "Taille de fichier" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/he.json b/homeassistant/components/filesize/translations/he.json new file mode 100644 index 00000000000..48a6eeeea33 --- /dev/null +++ b/homeassistant/components/filesize/translations/he.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/hu.json b/homeassistant/components/filesize/translations/hu.json new file mode 100644 index 00000000000..74c9f598bb9 --- /dev/null +++ b/homeassistant/components/filesize/translations/hu.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "not_allowed": "Az el\u00e9r\u00e9si \u00fat nem enged\u00e9lyezett", + "not_valid": "Az el\u00e9r\u00e9si \u00fat \u00e9rv\u00e9nytelen" + }, + "step": { + "user": { + "data": { + "file_path": "A f\u00e1jl el\u00e9r\u00e9si \u00fatja" + } + } + } + }, + "title": "F\u00e1jlm\u00e9ret" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/id.json b/homeassistant/components/filesize/translations/id.json new file mode 100644 index 00000000000..cb4bfdebaa7 --- /dev/null +++ b/homeassistant/components/filesize/translations/id.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi" + }, + "error": { + "not_allowed": "Jalur tidak diperbolehkan", + "not_valid": "Jalur tidak valid" + }, + "step": { + "user": { + "data": { + "file_path": "Jalur ke file" + } + } + } + }, + "title": "Ukuran file" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/it.json b/homeassistant/components/filesize/translations/it.json new file mode 100644 index 00000000000..4372477b24c --- /dev/null +++ b/homeassistant/components/filesize/translations/it.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato" + }, + "error": { + "not_allowed": "Il percorso non \u00e8 consentito", + "not_valid": "Il percorso non \u00e8 valido" + }, + "step": { + "user": { + "data": { + "file_path": "Percorso del file" + } + } + } + }, + "title": "Dimensione file" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/ja.json b/homeassistant/components/filesize/translations/ja.json new file mode 100644 index 00000000000..3d8ca72452b --- /dev/null +++ b/homeassistant/components/filesize/translations/ja.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "not_allowed": "\u30d1\u30b9\u304c\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093", + "not_valid": "\u30d1\u30b9\u304c\u7121\u52b9\u3067\u3059" + }, + "step": { + "user": { + "data": { + "file_path": "\u30d5\u30a1\u30a4\u30eb\u3078\u306e\u30d1\u30b9" + } + } + } + }, + "title": "\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/nl.json b/homeassistant/components/filesize/translations/nl.json new file mode 100644 index 00000000000..d8a35446044 --- /dev/null +++ b/homeassistant/components/filesize/translations/nl.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd" + }, + "error": { + "not_allowed": "Pad is niet toegestaan", + "not_valid": "Pad is niet geldig" + }, + "step": { + "user": { + "data": { + "file_path": "Pad naar bestand" + } + } + } + }, + "title": "Bestandsgrootte" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/no.json b/homeassistant/components/filesize/translations/no.json new file mode 100644 index 00000000000..18bdf37d667 --- /dev/null +++ b/homeassistant/components/filesize/translations/no.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert" + }, + "error": { + "not_allowed": "Stien er ikke tillatt", + "not_valid": "Banen er ikke gyldig" + }, + "step": { + "user": { + "data": { + "file_path": "Bane til fil" + } + } + } + }, + "title": "Filst\u00f8rrelse" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/pl.json b/homeassistant/components/filesize/translations/pl.json new file mode 100644 index 00000000000..2b2cd21a79c --- /dev/null +++ b/homeassistant/components/filesize/translations/pl.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana" + }, + "error": { + "not_allowed": "Niedozwolona \u015bcie\u017cka", + "not_valid": "Nieprawid\u0142owa \u015bcie\u017cka" + }, + "step": { + "user": { + "data": { + "file_path": "\u015acie\u017cka do pliku" + } + } + } + }, + "title": "Rozmiar pliku" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/pt-BR.json b/homeassistant/components/filesize/translations/pt-BR.json new file mode 100644 index 00000000000..dfcc9cc5348 --- /dev/null +++ b/homeassistant/components/filesize/translations/pt-BR.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "error": { + "not_allowed": "O caminho n\u00e3o \u00e9 permitido", + "not_valid": "O caminho n\u00e3o \u00e9 v\u00e1lido" + }, + "step": { + "user": { + "data": { + "file_path": "Caminho para o arquivo" + } + } + } + }, + "title": "Tamanho do arquivo" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/ru.json b/homeassistant/components/filesize/translations/ru.json new file mode 100644 index 00000000000..1071fd09ca5 --- /dev/null +++ b/homeassistant/components/filesize/translations/ru.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "error": { + "not_allowed": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043d\u0443\u0436\u043d\u044b\u0445 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0439.", + "not_valid": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443." + }, + "step": { + "user": { + "data": { + "file_path": "\u041f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443" + } + } + } + }, + "title": "\u0420\u0430\u0437\u043c\u0435\u0440 \u0444\u0430\u0439\u043b\u0430" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/tr.json b/homeassistant/components/filesize/translations/tr.json new file mode 100644 index 00000000000..cf62d56284e --- /dev/null +++ b/homeassistant/components/filesize/translations/tr.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "not_allowed": "Yola izin verilmiyor", + "not_valid": "Yol ge\u00e7erli de\u011fil" + }, + "step": { + "user": { + "data": { + "file_path": "Dosya yolu" + } + } + } + }, + "title": "Dosya boyutu" +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/zh-Hant.json b/homeassistant/components/filesize/translations/zh-Hant.json new file mode 100644 index 00000000000..692d770d337 --- /dev/null +++ b/homeassistant/components/filesize/translations/zh-Hant.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "not_allowed": "\u8def\u5f91\u4e0d\u5141\u8a31", + "not_valid": "\u8def\u5f91\u7121\u6548" + }, + "step": { + "user": { + "data": { + "file_path": "\u6a94\u6848\u8def\u5f91" + } + } + } + }, + "title": "\u6a94\u6848\u5927\u5c0f" +} \ No newline at end of file diff --git a/homeassistant/components/fivem/translations/hu.json b/homeassistant/components/fivem/translations/hu.json index b307c6e79fc..4e56dc3c17f 100644 --- a/homeassistant/components/fivem/translations/hu.json +++ b/homeassistant/components/fivem/translations/hu.json @@ -13,7 +13,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "port": "Port" } } diff --git a/homeassistant/components/flux_led/translations/hu.json b/homeassistant/components/flux_led/translations/hu.json index 1208f87fe70..f2f5d9b8751 100644 --- a/homeassistant/components/flux_led/translations/hu.json +++ b/homeassistant/components/flux_led/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton" }, "error": { diff --git a/homeassistant/components/forecast_solar/translations/ca.json b/homeassistant/components/forecast_solar/translations/ca.json index 7bd31828080..141ad11c165 100644 --- a/homeassistant/components/forecast_solar/translations/ca.json +++ b/homeassistant/components/forecast_solar/translations/ca.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 graus, 0 = nord, 90 = est, 180 = sud, 270 = oest)", "damping": "Factor d'amortiment: ajusta els resultats al mat\u00ed i al vespre", "declination": "Inclinaci\u00f3 (0 = horitzontal, 90 = vertical)", + "inverter_size": "Pot\u00e8ncia de l'inversor (Watts)", "modules power": "Pot\u00e8ncia m\u00e0xima total dels panells solars" }, "description": "Aquests valors permeten ajustar els resultats de Solar.Forecast. Consulta la documentaci\u00f3 si tens dubtes sobre algun camp." diff --git a/homeassistant/components/forecast_solar/translations/de.json b/homeassistant/components/forecast_solar/translations/de.json index 43b60424cf1..06e51e04659 100644 --- a/homeassistant/components/forecast_solar/translations/de.json +++ b/homeassistant/components/forecast_solar/translations/de.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 Grad, 0 = Norden, 90 = Osten, 180 = S\u00fcden, 270 = Westen)", "damping": "D\u00e4mpfungsfaktor: passt die Ergebnisse morgens und abends an", "declination": "Deklination (0 = Horizontal, 90 = Vertikal)", + "inverter_size": "Wechselrichtergr\u00f6\u00dfe (Watt)", "modules power": "Gesamt-Watt-Spitzenleistung deiner Solarmodule" }, "description": "Mit diesen Werten kann das Solar.Forecast-Ergebnis angepasst werden. Wenn ein Feld unklar ist, lies bitte in der Dokumentation nach." diff --git a/homeassistant/components/forecast_solar/translations/el.json b/homeassistant/components/forecast_solar/translations/el.json index 0f6603a622b..e2e75c05f65 100644 --- a/homeassistant/components/forecast_solar/translations/el.json +++ b/homeassistant/components/forecast_solar/translations/el.json @@ -22,6 +22,7 @@ "azimuth": "\u0391\u03b6\u03b9\u03bc\u03bf\u03cd\u03b8\u03b9\u03bf (360 \u03bc\u03bf\u03af\u03c1\u03b5\u03c2, 0 = \u0392\u03bf\u03c1\u03c1\u03ac\u03c2, 90 = \u0391\u03bd\u03b1\u03c4\u03bf\u03bb\u03ae, 180 = \u039d\u03cc\u03c4\u03bf\u03c2, 270 = \u0394\u03cd\u03c3\u03b7)", "damping": "\u03a3\u03c5\u03bd\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03ae\u03c2 \u03b1\u03c0\u03cc\u03c3\u03b2\u03b5\u03c3\u03b7\u03c2: \u03c1\u03c5\u03b8\u03bc\u03af\u03b6\u03b5\u03b9 \u03c4\u03b1 \u03b1\u03c0\u03bf\u03c4\u03b5\u03bb\u03ad\u03c3\u03bc\u03b1\u03c4\u03b1 \u03c0\u03c1\u03c9\u03af \u03ba\u03b1\u03b9 \u03b2\u03c1\u03ac\u03b4\u03c5", "declination": "\u0391\u03c0\u03cc\u03ba\u03bb\u03b9\u03c3\u03b7 (0 = \u03bf\u03c1\u03b9\u03b6\u03cc\u03bd\u03c4\u03b9\u03b1, 90 = \u03ba\u03b1\u03c4\u03b1\u03ba\u03cc\u03c1\u03c5\u03c6\u03b7)", + "inverter_size": "\u039c\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2 \u03bc\u03b5\u03c4\u03b1\u03c4\u03c1\u03bf\u03c0\u03ad\u03b1 (Watt)", "modules power": "\u03a3\u03c5\u03bd\u03bf\u03bb\u03b9\u03ba\u03ae \u03bc\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b9\u03c3\u03c7\u03cd\u03c2 Watt \u03c4\u03c9\u03bd \u03b7\u03bb\u03b9\u03b1\u03ba\u03ce\u03bd \u03c3\u03b1\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03c9\u03bd" }, "description": "\u0391\u03c5\u03c4\u03ad\u03c2 \u03bf\u03b9 \u03c4\u03b9\u03bc\u03ad\u03c2 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03bf\u03c5\u03bd \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c0\u03bf\u03c4\u03b5\u03bb\u03ad\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2 Solar.Forecast. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b5\u03ac\u03bd \u03ba\u03ac\u03c0\u03bf\u03b9\u03bf \u03c0\u03b5\u03b4\u03af\u03bf \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03b1\u03c6\u03ad\u03c2." diff --git a/homeassistant/components/forecast_solar/translations/en.json b/homeassistant/components/forecast_solar/translations/en.json index db9bead2e8c..2aa5a37cd1c 100644 --- a/homeassistant/components/forecast_solar/translations/en.json +++ b/homeassistant/components/forecast_solar/translations/en.json @@ -21,8 +21,8 @@ "api_key": "Forecast.Solar API Key (optional)", "azimuth": "Azimuth (360 degrees, 0 = North, 90 = East, 180 = South, 270 = West)", "damping": "Damping factor: adjusts the results in the morning and evening", - "inverter_size": "Inverter size (Watt)", "declination": "Declination (0 = Horizontal, 90 = Vertical)", + "inverter_size": "Inverter size (Watt)", "modules power": "Total Watt peak power of your solar modules" }, "description": "These values allow tweaking the Solar.Forecast result. Please refer to the documentation if a field is unclear." diff --git a/homeassistant/components/forecast_solar/translations/et.json b/homeassistant/components/forecast_solar/translations/et.json index 7aa87f4cf58..d2b72b30708 100644 --- a/homeassistant/components/forecast_solar/translations/et.json +++ b/homeassistant/components/forecast_solar/translations/et.json @@ -22,6 +22,7 @@ "azimuth": "Asimuut (360 kraadi, 0 = p\u00f5hi, 90 = ida, 180 = l\u00f5una, 270 = l\u00e4\u00e4s)", "damping": "Summutustegur: reguleerib tulemusi hommikul ja \u00f5htul", "declination": "Deklinatsioon (0 = horisontaalne, 90 = vertikaalne)", + "inverter_size": "Inverteri v\u00f5imsus (vatti)", "modules power": "P\u00e4ikesemoodulite koguv\u00f5imsus vattides" }, "description": "Need v\u00e4\u00e4rtused v\u00f5imaldavad muuta Solar.Forecast tulemust. Vaata dokumentatsiooni kui asi on ebaselge." diff --git a/homeassistant/components/forecast_solar/translations/fr.json b/homeassistant/components/forecast_solar/translations/fr.json index efd9f7be3a6..78b4d3f8f88 100644 --- a/homeassistant/components/forecast_solar/translations/fr.json +++ b/homeassistant/components/forecast_solar/translations/fr.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 degr\u00e9s, 0 = Nord, 90 = Est, 180 = Sud, 270 = Ouest)", "damping": "Facteur d'amortissement : ajuste les r\u00e9sultats matin et soir", "declination": "D\u00e9clinaison (0 = horizontale, 90 = verticale)", + "inverter_size": "Taille de l'onduleur (en watts)", "modules power": "Puissance de cr\u00eate totale en watts de vos modules solaires" }, "description": "Ces valeurs permettent de peaufiner le r\u00e9sultat Solar.Forecast. Veuillez vous r\u00e9f\u00e9rer \u00e0 la documentation si un champ n'est pas clair." diff --git a/homeassistant/components/forecast_solar/translations/hu.json b/homeassistant/components/forecast_solar/translations/hu.json index 0bd814f16be..33a69ad2fd7 100644 --- a/homeassistant/components/forecast_solar/translations/hu.json +++ b/homeassistant/components/forecast_solar/translations/hu.json @@ -8,7 +8,7 @@ "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", "modules power": "A napelemmodulok teljes cs\u00facsteljes\u00edtm\u00e9nye (Watt)", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "T\u00f6ltse ki a napelemek adatait. K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, ha egy mez\u0151 nem egy\u00e9rtelm\u0171." } @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 fok, 0 = \u00e9szak, 90 = keleti, 180 = d\u00e9li, 270 = nyugati)", "damping": "Csillap\u00edt\u00e1si t\u00e9nyez\u0151: be\u00e1ll\u00edtja az eredm\u00e9nyeket reggelre \u00e9s est\u00e9re", "declination": "Deklin\u00e1ci\u00f3 (0 = v\u00edzszintes, 90 = f\u00fcgg\u0151leges)", + "inverter_size": "Inverter m\u00e9rete (Watt)", "modules power": "A napelemmodulok teljes cs\u00facsteljes\u00edtm\u00e9nye (Watt)" }, "description": "Ezek az \u00e9rt\u00e9kek lehet\u0151v\u00e9 teszik a Solar.Forecast eredm\u00e9ny m\u00f3dos\u00edt\u00e1s\u00e1t. K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, ha egy mez\u0151 nem egy\u00e9rtelm\u0171." diff --git a/homeassistant/components/forecast_solar/translations/id.json b/homeassistant/components/forecast_solar/translations/id.json index 27ef16e0266..5bd1236d6a6 100644 --- a/homeassistant/components/forecast_solar/translations/id.json +++ b/homeassistant/components/forecast_solar/translations/id.json @@ -22,6 +22,7 @@ "azimuth": "Azimuth (360 derajat, 0 = Utara, 90 = Timur, 180 = Selatan, 270 = Barat)", "damping": "Faktor redaman: menyesuaikan hasil di pagi dan sore hari", "declination": "Deklinasi (0 = Horizontal, 90 = Vertikal)", + "inverter_size": "Ukuran inverter (Watt)", "modules power": "Total daya puncak modul surya Anda dalam Watt" }, "description": "Nilai-nilai ini memungkinkan penyesuaian hasil Solar.Forecast. Rujuk ke dokumentasi jika bidang isian tidak jelas." diff --git a/homeassistant/components/forecast_solar/translations/it.json b/homeassistant/components/forecast_solar/translations/it.json index 7920eee43eb..598c67695cc 100644 --- a/homeassistant/components/forecast_solar/translations/it.json +++ b/homeassistant/components/forecast_solar/translations/it.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 gradi, 0 = Nord, 90 = Est, 180 = Sud, 270 = Ovest)", "damping": "Fattore di smorzamento: regola i risultati al mattino e alla sera", "declination": "Declinazione (0 = Orizzontale, 90 = Verticale)", + "inverter_size": "Dimensioni inverter (Watt)", "modules power": "Potenza di picco totale in Watt dei tuoi moduli solari" }, "description": "Questi valori consentono di modificare il risultato di Solar.Forecast. Fai riferimento alla documentazione se un campo non \u00e8 chiaro." diff --git a/homeassistant/components/forecast_solar/translations/ja.json b/homeassistant/components/forecast_solar/translations/ja.json index 62090376bed..d86dc08f2b7 100644 --- a/homeassistant/components/forecast_solar/translations/ja.json +++ b/homeassistant/components/forecast_solar/translations/ja.json @@ -22,6 +22,7 @@ "azimuth": "\u65b9\u4f4d\u89d2(360\u5ea6\u30010=\u5317\u300190=\u6771\u3001180=\u5357\u3001270=\u897f)", "damping": "\u6e1b\u8870\u4fc2\u6570(\u30c0\u30f3\u30d4\u30f3\u30b0\u30d5\u30a1\u30af\u30bf\u30fc): \u671d\u3068\u5915\u65b9\u306e\u7d50\u679c\u3092\u8abf\u6574\u3059\u308b", "declination": "\u504f\u89d2(0\uff1d\u6c34\u5e73\u300190\uff1d\u5782\u76f4)", + "inverter_size": "\u30a4\u30f3\u30d0\u30fc\u30bf\u30fc\u306e\u30b5\u30a4\u30ba\uff08\u30ef\u30c3\u30c8\uff09", "modules power": "\u30bd\u30fc\u30e9\u30fc\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u7dcf\u30ef\u30c3\u30c8\u30d4\u30fc\u30af\u96fb\u529b" }, "description": "\u3053\u308c\u3089\u306e\u5024\u306b\u3088\u308a\u3001Solar.Forecast\u306e\u7d50\u679c\u3092\u5fae\u8abf\u6574\u3067\u304d\u307e\u3059\u3002\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u4e0d\u660e\u306a\u5834\u5408\u306f\u3001\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" diff --git a/homeassistant/components/forecast_solar/translations/nl.json b/homeassistant/components/forecast_solar/translations/nl.json index c66d272782d..dbc966e59fc 100644 --- a/homeassistant/components/forecast_solar/translations/nl.json +++ b/homeassistant/components/forecast_solar/translations/nl.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 graden, 0 = Noord, 90 = Oost, 180 = Zuid, 270 = West)", "damping": "Dempingsfactor: past de resultaten 's ochtends en 's avonds aan", "declination": "Declinatie (0 = Horizontaal, 90 = Verticaal)", + "inverter_size": "Omvormer grootte (Watt)", "modules power": "Totaal Watt piekvermogen van uw zonnepanelen" }, "description": "Met deze waarden kan het resultaat van Solar.Forecast worden aangepast. Raadpleeg de documentatie als een veld onduidelijk is." diff --git a/homeassistant/components/forecast_solar/translations/no.json b/homeassistant/components/forecast_solar/translations/no.json index 1504727c1ae..a9acbb86f00 100644 --- a/homeassistant/components/forecast_solar/translations/no.json +++ b/homeassistant/components/forecast_solar/translations/no.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 grader, 0 = Nord, 90 = \u00d8st, 180 = S\u00f8r, 270 = Vest)", "damping": "Dempingsfaktor: justerer resultatene om morgenen og kvelden", "declination": "Deklinasjon (0 = horisontal, 90 = vertikal)", + "inverter_size": "Inverterst\u00f8rrelse (Watt)", "modules power": "Total Watt-toppeffekt i solcellemodulene dine" }, "description": "Disse verdiene tillater justering av Solar.Forecast -resultatet. Se dokumentasjonen hvis et felt er uklart." diff --git a/homeassistant/components/forecast_solar/translations/pl.json b/homeassistant/components/forecast_solar/translations/pl.json index 3fc782fe7c3..ad01ce4bb54 100644 --- a/homeassistant/components/forecast_solar/translations/pl.json +++ b/homeassistant/components/forecast_solar/translations/pl.json @@ -22,6 +22,7 @@ "azimuth": "Azymut (360 stopni, 0 = P\u00f3\u0142noc, 90 = Wsch\u00f3d, 180 = Po\u0142udnie, 270 = Zach\u00f3d)", "damping": "Wsp\u00f3\u0142czynnik t\u0142umienia: dostosowuje wyniki rano i wieczorem", "declination": "Deklinacja (0 = Poziomo, 90 = Pionowo)", + "inverter_size": "Rozmiar falownika (Wat)", "modules power": "Ca\u0142kowita moc szczytowa modu\u0142\u00f3w fotowoltaicznych w watach" }, "description": "Te warto\u015bci pozwalaj\u0105 dostosowa\u0107 wyniki dla Solar.Forecast. Prosz\u0119 zapozna\u0107 si\u0119 z dokumentacj\u0105, je\u015bli pole jest niejasne." diff --git a/homeassistant/components/forecast_solar/translations/pt-BR.json b/homeassistant/components/forecast_solar/translations/pt-BR.json index ad6cca066c4..6761e17e8bd 100644 --- a/homeassistant/components/forecast_solar/translations/pt-BR.json +++ b/homeassistant/components/forecast_solar/translations/pt-BR.json @@ -22,6 +22,7 @@ "azimuth": "Azimute (360\u00b0, 0\u00b0 = Norte, 90\u00b0 = Leste, 180\u00b0 = Sul, 270\u00b0 = Oeste)", "damping": "Fator de amortecimento: ajusta os resultados de manh\u00e3 e \u00e0 noite", "declination": "Declina\u00e7\u00e3o (0\u00b0 = Horizontal, 90\u00b0 = Vertical)", + "inverter_size": "Pot\u00eancia do inversor (Watt)", "modules power": "Pot\u00eancia de pico total em Watt de seus m\u00f3dulos solares" }, "description": "Preencha os dados de seus pain\u00e9is solares. Consulte a documenta\u00e7\u00e3o se um campo n\u00e3o estiver claro." diff --git a/homeassistant/components/forecast_solar/translations/ru.json b/homeassistant/components/forecast_solar/translations/ru.json index 9cf8e87a8e2..f7d4d502691 100644 --- a/homeassistant/components/forecast_solar/translations/ru.json +++ b/homeassistant/components/forecast_solar/translations/ru.json @@ -22,6 +22,7 @@ "azimuth": "\u0410\u0437\u0438\u043c\u0443\u0442 (360 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432, 0 = \u0441\u0435\u0432\u0435\u0440, 90 = \u0432\u043e\u0441\u0442\u043e\u043a, 180 = \u044e\u0433, 270 = \u0437\u0430\u043f\u0430\u0434)", "damping": "\u0424\u0430\u043a\u0442\u043e\u0440 \u0437\u0430\u0442\u0443\u0445\u0430\u043d\u0438\u044f: \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0443\u0442\u0440\u043e\u043c \u0438 \u0432\u0435\u0447\u0435\u0440\u043e\u043c", "declination": "\u0421\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u0435 (0 = \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435, 90 = \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435)", + "inverter_size": "\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0438\u043d\u0432\u0435\u0440\u0442\u043e\u0440\u0430 (\u0432 \u0412\u0430\u0442\u0442\u0430\u0445)", "modules power": "\u041e\u0431\u0449\u0430\u044f \u043f\u0438\u043a\u043e\u0432\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0412\u0430\u0448\u0438\u0445 \u0441\u043e\u043b\u043d\u0435\u0447\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 (\u0432 \u0412\u0430\u0442\u0442\u0430\u0445)" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Forecast.Solar." diff --git a/homeassistant/components/forecast_solar/translations/tr.json b/homeassistant/components/forecast_solar/translations/tr.json index fecd8d7889a..1fa6def5714 100644 --- a/homeassistant/components/forecast_solar/translations/tr.json +++ b/homeassistant/components/forecast_solar/translations/tr.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 derece, 0 = Kuzey, 90 = Do\u011fu, 180 = G\u00fcney, 270 = Bat\u0131)", "damping": "S\u00f6n\u00fcmleme fakt\u00f6r\u00fc: sonu\u00e7lar\u0131 sabah ve ak\u015fam ayarlar", "declination": "Sapma (0 = Yatay, 90 = Dikey)", + "inverter_size": "\u0130nverter boyutu (Watt)", "modules power": "Solar mod\u00fcllerinizin toplam en y\u00fcksek Watt g\u00fcc\u00fc" }, "description": "Bu de\u011ferler Solar.Forecast sonucunun ayarlanmas\u0131na izin verir. Bir alan net de\u011filse l\u00fctfen belgelere bak\u0131n." diff --git a/homeassistant/components/forecast_solar/translations/zh-Hant.json b/homeassistant/components/forecast_solar/translations/zh-Hant.json index fca97b9da01..3870ca29846 100644 --- a/homeassistant/components/forecast_solar/translations/zh-Hant.json +++ b/homeassistant/components/forecast_solar/translations/zh-Hant.json @@ -22,6 +22,7 @@ "azimuth": "\u65b9\u4f4d\u89d2\uff08360 \u5ea6\u55ae\u4f4d\u30020 = \u5317\u300190 = \u6771\u3001180 = \u5357\u3001270 = \u897f\uff09", "damping": "\u963b\u5c3c\u56e0\u7d20\uff1a\u8abf\u6574\u6e05\u6668\u8207\u508d\u665a\u7d50\u679c", "declination": "\u504f\u89d2\uff080 = \u6c34\u5e73\u300190 = \u5782\u76f4\uff09", + "inverter_size": "\u8b8a\u6d41\u5668\u5c3a\u5bf8\uff08Watt\uff09", "modules power": "\u7e3d\u5cf0\u503c\u529f\u7387" }, "description": "\u6b64\u4e9b\u6578\u503c\u5141\u8a31\u5fae\u8abf Solar.Forecast \u7d50\u679c\u3002\u5982\u679c\u6709\u4e0d\u6e05\u695a\u7684\u5730\u65b9\u3001\u8acb\u53c3\u8003\u6587\u4ef6\u8aaa\u660e\u3002" diff --git a/homeassistant/components/foscam/translations/ca.json b/homeassistant/components/foscam/translations/ca.json index b7f71c8c922..2fbd19dc20c 100644 --- a/homeassistant/components/foscam/translations/ca.json +++ b/homeassistant/components/foscam/translations/ca.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/cs.json b/homeassistant/components/foscam/translations/cs.json index b6f3c40abf6..ae1fc69cc77 100644 --- a/homeassistant/components/foscam/translations/cs.json +++ b/homeassistant/components/foscam/translations/cs.json @@ -18,6 +18,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/de.json b/homeassistant/components/foscam/translations/de.json index bc1c12ea130..30d331848a4 100644 --- a/homeassistant/components/foscam/translations/de.json +++ b/homeassistant/components/foscam/translations/de.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/el.json b/homeassistant/components/foscam/translations/el.json index 0e6c9b7c65d..44aa096837c 100644 --- a/homeassistant/components/foscam/translations/el.json +++ b/homeassistant/components/foscam/translations/el.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/en.json b/homeassistant/components/foscam/translations/en.json index 16a7d0b7800..29fd01d030d 100644 --- a/homeassistant/components/foscam/translations/en.json +++ b/homeassistant/components/foscam/translations/en.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/es-419.json b/homeassistant/components/foscam/translations/es-419.json index 39027bdf914..720a14b8523 100644 --- a/homeassistant/components/foscam/translations/es-419.json +++ b/homeassistant/components/foscam/translations/es-419.json @@ -11,6 +11,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/es.json b/homeassistant/components/foscam/translations/es.json index 7e8b7c1427d..f80ef3335d1 100644 --- a/homeassistant/components/foscam/translations/es.json +++ b/homeassistant/components/foscam/translations/es.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/et.json b/homeassistant/components/foscam/translations/et.json index c21ffa0cdd1..9c2801c6135 100644 --- a/homeassistant/components/foscam/translations/et.json +++ b/homeassistant/components/foscam/translations/et.json @@ -21,6 +21,5 @@ } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/fr.json b/homeassistant/components/foscam/translations/fr.json index f728c6d7414..dcc4b45cc83 100644 --- a/homeassistant/components/foscam/translations/fr.json +++ b/homeassistant/components/foscam/translations/fr.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/hu.json b/homeassistant/components/foscam/translations/hu.json index b303db792bb..575e2b34982 100644 --- a/homeassistant/components/foscam/translations/hu.json +++ b/homeassistant/components/foscam/translations/hu.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/id.json b/homeassistant/components/foscam/translations/id.json index 21a7682b92c..89d57eb8e4a 100644 --- a/homeassistant/components/foscam/translations/id.json +++ b/homeassistant/components/foscam/translations/id.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/it.json b/homeassistant/components/foscam/translations/it.json index 63868a0f07f..e38b7d3e8a2 100644 --- a/homeassistant/components/foscam/translations/it.json +++ b/homeassistant/components/foscam/translations/it.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/ja.json b/homeassistant/components/foscam/translations/ja.json index 5a02ae5f446..3089882c63e 100644 --- a/homeassistant/components/foscam/translations/ja.json +++ b/homeassistant/components/foscam/translations/ja.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/ko.json b/homeassistant/components/foscam/translations/ko.json index ba743f6b27a..762957e840e 100644 --- a/homeassistant/components/foscam/translations/ko.json +++ b/homeassistant/components/foscam/translations/ko.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/lb.json b/homeassistant/components/foscam/translations/lb.json index 123b3f4be76..11dd851f381 100644 --- a/homeassistant/components/foscam/translations/lb.json +++ b/homeassistant/components/foscam/translations/lb.json @@ -11,6 +11,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/nl.json b/homeassistant/components/foscam/translations/nl.json index 9bea23ad702..5a6bfcaa4b3 100644 --- a/homeassistant/components/foscam/translations/nl.json +++ b/homeassistant/components/foscam/translations/nl.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/no.json b/homeassistant/components/foscam/translations/no.json index 0184213de27..03b96cebe65 100644 --- a/homeassistant/components/foscam/translations/no.json +++ b/homeassistant/components/foscam/translations/no.json @@ -21,6 +21,5 @@ } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/pl.json b/homeassistant/components/foscam/translations/pl.json index d7494e22063..03a6a47fb74 100644 --- a/homeassistant/components/foscam/translations/pl.json +++ b/homeassistant/components/foscam/translations/pl.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/pt-BR.json b/homeassistant/components/foscam/translations/pt-BR.json index b33dce1e6fe..18af16044e8 100644 --- a/homeassistant/components/foscam/translations/pt-BR.json +++ b/homeassistant/components/foscam/translations/pt-BR.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/ru.json b/homeassistant/components/foscam/translations/ru.json index 8e8404c501e..1089ef63d59 100644 --- a/homeassistant/components/foscam/translations/ru.json +++ b/homeassistant/components/foscam/translations/ru.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/tr.json b/homeassistant/components/foscam/translations/tr.json index e6e5adc434c..1f8aab543fb 100644 --- a/homeassistant/components/foscam/translations/tr.json +++ b/homeassistant/components/foscam/translations/tr.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/foscam/translations/zh-Hant.json b/homeassistant/components/foscam/translations/zh-Hant.json index d10746842a8..007a378a36b 100644 --- a/homeassistant/components/foscam/translations/zh-Hant.json +++ b/homeassistant/components/foscam/translations/zh-Hant.json @@ -21,6 +21,5 @@ } } } - }, - "title": "Foscam" + } } \ No newline at end of file diff --git a/homeassistant/components/freebox/translations/hu.json b/homeassistant/components/freebox/translations/hu.json index 873e1057c15..39cfe189449 100644 --- a/homeassistant/components/freebox/translations/hu.json +++ b/homeassistant/components/freebox/translations/hu.json @@ -10,7 +10,7 @@ }, "step": { "link": { - "description": "Kattintson a \u201eK\u00fcld\u00e9s\u201d gombra, majd \u00e9rintse meg a jobbra mutat\u00f3 nyilat az \u00fatv\u00e1laszt\u00f3n a Freebox regisztr\u00e1l\u00e1s\u00e1hoz Home Assistant seg\u00edts\u00e9g\u00e9vel. \n\n![A gomb helye a routeren] (/static/images/config_freebox.png)", + "description": "A k\u00f6vetkez\u0151 l\u00e9p\u00e9sben \u00e9rintse meg a jobbra mutat\u00f3 nyilat az \u00fatv\u00e1laszt\u00f3n a Freebox regisztr\u00e1l\u00e1s\u00e1hoz Home Assistant seg\u00edts\u00e9g\u00e9vel. \n\n![A gomb helye a routeren] (/static/images/config_freebox.png)", "title": "Freebox \u00fatv\u00e1laszt\u00f3 linkel\u00e9se" }, "user": { diff --git a/homeassistant/components/freebox/translations/it.json b/homeassistant/components/freebox/translations/it.json index 4eb49d5fdfb..f1978217547 100644 --- a/homeassistant/components/freebox/translations/it.json +++ b/homeassistant/components/freebox/translations/it.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "Impossibile connettersi", - "register_failed": "Errore in fase di registrazione, si prega di riprovare", + "register_failed": "Errore in fase di registrazione, riprova", "unknown": "Errore imprevisto" }, "step": { diff --git a/homeassistant/components/fritz/translations/ca.json b/homeassistant/components/fritz/translations/ca.json index d39805a6302..04d5b14cac3 100644 --- a/homeassistant/components/fritz/translations/ca.json +++ b/homeassistant/components/fritz/translations/ca.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "ignore_ip6_link_local": "L'enlla\u00e7 amb adreces IPv6 locals no est\u00e0 perm\u00e8s", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { diff --git a/homeassistant/components/fritz/translations/de.json b/homeassistant/components/fritz/translations/de.json index ae36b3450ca..d64845ba9b7 100644 --- a/homeassistant/components/fritz/translations/de.json +++ b/homeassistant/components/fritz/translations/de.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "ignore_ip6_link_local": "IPv6 link local address wird nicht unterst\u00fctzt.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { diff --git a/homeassistant/components/fritz/translations/el.json b/homeassistant/components/fritz/translations/el.json index d514848040f..6dfc67e08d6 100644 --- a/homeassistant/components/fritz/translations/el.json +++ b/homeassistant/components/fritz/translations/el.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "ignore_ip6_link_local": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 IPv6 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9.", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { diff --git a/homeassistant/components/fritz/translations/en.json b/homeassistant/components/fritz/translations/en.json index e7ee1568684..3ce31866c2f 100644 --- a/homeassistant/components/fritz/translations/en.json +++ b/homeassistant/components/fritz/translations/en.json @@ -10,6 +10,7 @@ "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "cannot_connect": "Failed to connect", + "connection_error": "Failed to connect", "invalid_auth": "Invalid authentication", "upnp_not_configured": "Missing UPnP settings on device." }, @@ -31,6 +32,16 @@ "description": "Update FRITZ!Box Tools credentials for: {host}.\n\nFRITZ!Box Tools is unable to log in to your FRITZ!Box.", "title": "Updating FRITZ!Box Tools - credentials" }, + "start_config": { + "data": { + "host": "Host", + "password": "Password", + "port": "Port", + "username": "Username" + }, + "description": "Setup FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", + "title": "Setup FRITZ!Box Tools - mandatory" + }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/et.json b/homeassistant/components/fritz/translations/et.json index 2051e9f4e63..89bf4a3b7c9 100644 --- a/homeassistant/components/fritz/translations/et.json +++ b/homeassistant/components/fritz/translations/et.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "H\u00e4\u00e4lestamine on k\u00e4imas", + "ignore_ip6_link_local": "IPv6 lingi kohalikku aadressi ei toetata.", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { diff --git a/homeassistant/components/fritz/translations/fr.json b/homeassistant/components/fritz/translations/fr.json index 3219164d98b..cfcd942b530 100644 --- a/homeassistant/components/fritz/translations/fr.json +++ b/homeassistant/components/fritz/translations/fr.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "ignore_ip6_link_local": "Les adresses IPv6 de liaison locale ne sont pas prises en charge.", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { diff --git a/homeassistant/components/fritz/translations/hu.json b/homeassistant/components/fritz/translations/hu.json index 81d3bb19e27..d1ff2c0c6bf 100644 --- a/homeassistant/components/fritz/translations/hu.json +++ b/homeassistant/components/fritz/translations/hu.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A konfigur\u00e1ci\u00f3 m\u00e1r folyamatban van", + "ignore_ip6_link_local": "Az IPv6-kapcsolat helyi c\u00edme nem t\u00e1mogatott.", "reauth_successful": "Az \u00fajhiteles\u00edt\u00e9s sikeres volt" }, "error": { diff --git a/homeassistant/components/fritz/translations/id.json b/homeassistant/components/fritz/translations/id.json index 816885b6391..c31bdf8b77c 100644 --- a/homeassistant/components/fritz/translations/id.json +++ b/homeassistant/components/fritz/translations/id.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", + "ignore_ip6_link_local": "Alamat lokal tautan IPv6 tidak didukung.", "reauth_successful": "Autentikasi ulang berhasil" }, "error": { diff --git a/homeassistant/components/fritz/translations/it.json b/homeassistant/components/fritz/translations/it.json index bf577223b6e..0516449f5db 100644 --- a/homeassistant/components/fritz/translations/it.json +++ b/homeassistant/components/fritz/translations/it.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "ignore_ip6_link_local": "L'indirizzo locale del collegamento IPv6 non \u00e8 supportato.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { diff --git a/homeassistant/components/fritz/translations/ja.json b/homeassistant/components/fritz/translations/ja.json index afbda92d3fe..8bd9747c9bc 100644 --- a/homeassistant/components/fritz/translations/ja.json +++ b/homeassistant/components/fritz/translations/ja.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "ignore_ip6_link_local": "IPv6\u30ea\u30f3\u30af\u306e\u30ed\u30fc\u30ab\u30eb\u30a2\u30c9\u30ec\u30b9\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { diff --git a/homeassistant/components/fritz/translations/nl.json b/homeassistant/components/fritz/translations/nl.json index b5577f11a0d..11fabceaf8d 100644 --- a/homeassistant/components/fritz/translations/nl.json +++ b/homeassistant/components/fritz/translations/nl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", + "ignore_ip6_link_local": "Lokaal IPv6-linkadres wordt niet ondersteund.", "reauth_successful": "Herauthenticatie was succesvol" }, "error": { diff --git a/homeassistant/components/fritz/translations/no.json b/homeassistant/components/fritz/translations/no.json index 6d6a805bab8..a18df1a7b14 100644 --- a/homeassistant/components/fritz/translations/no.json +++ b/homeassistant/components/fritz/translations/no.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "ignore_ip6_link_local": "IPv6-lenkens lokale adresse st\u00f8ttes ikke.", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" }, "error": { diff --git a/homeassistant/components/fritz/translations/pl.json b/homeassistant/components/fritz/translations/pl.json index 7ec1f539aab..fed010f1987 100644 --- a/homeassistant/components/fritz/translations/pl.json +++ b/homeassistant/components/fritz/translations/pl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "ignore_ip6_link_local": "Adres lokalny \u0142\u0105cza IPv6 nie jest obs\u0142ugiwany.", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { diff --git a/homeassistant/components/fritz/translations/pt-BR.json b/homeassistant/components/fritz/translations/pt-BR.json index 2170b34b1ee..ceb295310c7 100644 --- a/homeassistant/components/fritz/translations/pt-BR.json +++ b/homeassistant/components/fritz/translations/pt-BR.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "ignore_ip6_link_local": "O endere\u00e7o local IPv6 n\u00e3o \u00e9 suportado.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { diff --git a/homeassistant/components/fritz/translations/ru.json b/homeassistant/components/fritz/translations/ru.json index 82530cca298..e45c36fe736 100644 --- a/homeassistant/components/fritz/translations/ru.json +++ b/homeassistant/components/fritz/translations/ru.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "ignore_ip6_link_local": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430 IPv6 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { diff --git a/homeassistant/components/fritz/translations/tr.json b/homeassistant/components/fritz/translations/tr.json index 9a9cd3da6bf..86cabbb782b 100644 --- a/homeassistant/components/fritz/translations/tr.json +++ b/homeassistant/components/fritz/translations/tr.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "ignore_ip6_link_local": "IPv6 ba\u011flant\u0131 yerel adresi desteklenmiyor.", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { diff --git a/homeassistant/components/fritz/translations/zh-Hant.json b/homeassistant/components/fritz/translations/zh-Hant.json index 7eeb3ae5e4b..778c38e36f7 100644 --- a/homeassistant/components/fritz/translations/zh-Hant.json +++ b/homeassistant/components/fritz/translations/zh-Hant.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "ignore_ip6_link_local": "\u4e0d\u652f\u63f4\u9023\u7d50\u672c\u5730\u7aef IPv6 \u4f4d\u5740", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { diff --git a/homeassistant/components/fritzbox/translations/ca.json b/homeassistant/components/fritzbox/translations/ca.json index efd81ddff84..c17b1fc87c9 100644 --- a/homeassistant/components/fritzbox/translations/ca.json +++ b/homeassistant/components/fritzbox/translations/ca.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "ignore_ip6_link_local": "L'enlla\u00e7 amb adreces IPv6 locals no est\u00e0 perm\u00e8s", "no_devices_found": "No s'han trobat dispositius a la xarxa", "not_supported": "Connectat a AVM FRITZ!Box per\u00f2 no es poden controlar dispositius Smart Home.", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" diff --git a/homeassistant/components/fritzbox/translations/de.json b/homeassistant/components/fritzbox/translations/de.json index 7da8e616cfc..ef2a6083608 100644 --- a/homeassistant/components/fritzbox/translations/de.json +++ b/homeassistant/components/fritzbox/translations/de.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "ignore_ip6_link_local": "IPv6 link local address wird nicht unterst\u00fctzt.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "not_supported": "Verbunden mit AVM FRITZ!Box, kann jedoch keine Smart Home-Ger\u00e4te steuern.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" diff --git a/homeassistant/components/fritzbox/translations/el.json b/homeassistant/components/fritzbox/translations/el.json index 975126772d5..9e1c95e41f1 100644 --- a/homeassistant/components/fritzbox/translations/el.json +++ b/homeassistant/components/fritzbox/translations/el.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "ignore_ip6_link_local": "\u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 IPv6 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9.", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "not_supported": "\u03a3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03bf AVM FRITZ!Box \u03b1\u03bb\u03bb\u03ac \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03b9 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 Smart Home.", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" diff --git a/homeassistant/components/fritzbox/translations/et.json b/homeassistant/components/fritzbox/translations/et.json index 849dc7fadee..3db11e7355c 100644 --- a/homeassistant/components/fritzbox/translations/et.json +++ b/homeassistant/components/fritzbox/translations/et.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", + "ignore_ip6_link_local": "IPv6 lingi kohalikku aadressi ei toetata.", "no_devices_found": "V\u00f5rgust ei leitud seadmeid", "not_supported": "\u00dchendatud AVM FRITZ!Boxiga! kuid see ei saa juhtida Smart Home seadmeid.", "reauth_successful": "Taastuvastamine \u00f5nnestus" diff --git a/homeassistant/components/fritzbox/translations/fr.json b/homeassistant/components/fritzbox/translations/fr.json index 69f024e26d7..f9e3d354a6d 100644 --- a/homeassistant/components/fritzbox/translations/fr.json +++ b/homeassistant/components/fritzbox/translations/fr.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "ignore_ip6_link_local": "Les adresses IPv6 de liaison locale ne sont pas prises en charge.", "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", "not_supported": "Connect\u00e9 \u00e0 AVM FRITZ! Box mais impossible de contr\u00f4ler les appareils Smart Home.", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" diff --git a/homeassistant/components/fritzbox/translations/hu.json b/homeassistant/components/fritzbox/translations/hu.json index c1cf8154aea..f079bf8e1df 100644 --- a/homeassistant/components/fritzbox/translations/hu.json +++ b/homeassistant/components/fritzbox/translations/hu.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "ignore_ip6_link_local": "Az IPv6-kapcsolat helyi c\u00edme nem t\u00e1mogatott.", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "not_supported": "Csatlakoztatva az AVM FRITZ! Boxhoz, de nem tudja vez\u00e9relni az intelligens otthoni eszk\u00f6z\u00f6ket.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." diff --git a/homeassistant/components/fritzbox/translations/id.json b/homeassistant/components/fritzbox/translations/id.json index f9c4f09b4ae..63e0ebb4823 100644 --- a/homeassistant/components/fritzbox/translations/id.json +++ b/homeassistant/components/fritzbox/translations/id.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", + "ignore_ip6_link_local": "Alamat lokal tautan IPv6 tidak didukung.", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", "not_supported": "Tersambung ke AVM FRITZ!Box tetapi tidak dapat mengontrol perangkat Smart Home.", "reauth_successful": "Autentikasi ulang berhasil" diff --git a/homeassistant/components/fritzbox/translations/it.json b/homeassistant/components/fritzbox/translations/it.json index 68cbe08b1b9..65a819d8329 100644 --- a/homeassistant/components/fritzbox/translations/it.json +++ b/homeassistant/components/fritzbox/translations/it.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "ignore_ip6_link_local": "L'indirizzo locale del collegamento IPv6 non \u00e8 supportato.", "no_devices_found": "Nessun dispositivo trovato sulla rete", "not_supported": "Collegato a AVM FRITZ!Box, ma non \u00e8 in grado di controllare i dispositivi Smart Home.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" diff --git a/homeassistant/components/fritzbox/translations/ja.json b/homeassistant/components/fritzbox/translations/ja.json index c246ea5fb0d..047442d5ee7 100644 --- a/homeassistant/components/fritzbox/translations/ja.json +++ b/homeassistant/components/fritzbox/translations/ja.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "ignore_ip6_link_local": "IPv6\u30ea\u30f3\u30af\u306e\u30ed\u30fc\u30ab\u30eb\u30a2\u30c9\u30ec\u30b9\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "not_supported": "AVM FRITZ!Box\u306b\u63a5\u7d9a\u3057\u307e\u3057\u305f\u304c\u3001Smart Home devices\u3092\u5236\u5fa1\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3002", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" diff --git a/homeassistant/components/fritzbox/translations/nl.json b/homeassistant/components/fritzbox/translations/nl.json index b1be4c8214f..43d51df760d 100644 --- a/homeassistant/components/fritzbox/translations/nl.json +++ b/homeassistant/components/fritzbox/translations/nl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", + "ignore_ip6_link_local": "IPv6 link lokaal adres wordt niet ondersteund.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "not_supported": "Verbonden met AVM FRITZ! Box, maar het kan geen Smart Home-apparaten bedienen.", "reauth_successful": "Herauthenticatie was succesvol" diff --git a/homeassistant/components/fritzbox/translations/no.json b/homeassistant/components/fritzbox/translations/no.json index 5ec0cc1acdc..98174053a61 100644 --- a/homeassistant/components/fritzbox/translations/no.json +++ b/homeassistant/components/fritzbox/translations/no.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "ignore_ip6_link_local": "IPv6-lenkens lokale adresse st\u00f8ttes ikke.", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", "not_supported": "Tilkoblet AVM FRITZ! Box, men den klarer ikke \u00e5 kontrollere Smart Home-enheter.", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" diff --git a/homeassistant/components/fritzbox/translations/pl.json b/homeassistant/components/fritzbox/translations/pl.json index d9832ee51a4..f1ff975af8d 100644 --- a/homeassistant/components/fritzbox/translations/pl.json +++ b/homeassistant/components/fritzbox/translations/pl.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "ignore_ip6_link_local": "Adres lokalny \u0142\u0105cza IPv6 nie jest obs\u0142ugiwany.", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", "not_supported": "Po\u0142\u0105czony z AVM FRITZ!Box, ale nie jest w stanie kontrolowa\u0107 urz\u0105dze\u0144 Smart Home", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" diff --git a/homeassistant/components/fritzbox/translations/pt-BR.json b/homeassistant/components/fritzbox/translations/pt-BR.json index 3e3884cbc41..1fbe79772db 100644 --- a/homeassistant/components/fritzbox/translations/pt-BR.json +++ b/homeassistant/components/fritzbox/translations/pt-BR.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "ignore_ip6_link_local": "O endere\u00e7o local IPv6 n\u00e3o \u00e9 suportado.", "no_devices_found": "Nenhum dispositivo encontrado na rede", "not_supported": "Conectado ao AVM FRITZ!Box, mas n\u00e3o consegue controlar os dispositivos Smart Home.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" diff --git a/homeassistant/components/fritzbox/translations/ru.json b/homeassistant/components/fritzbox/translations/ru.json index 51e9aedc632..5f939462db2 100644 --- a/homeassistant/components/fritzbox/translations/ru.json +++ b/homeassistant/components/fritzbox/translations/ru.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "ignore_ip6_link_local": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430 IPv6 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "not_supported": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a AVM FRITZ! Box \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e, \u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 Smart Home \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." diff --git a/homeassistant/components/fritzbox/translations/tr.json b/homeassistant/components/fritzbox/translations/tr.json index 300ca1a096a..a870cbcae11 100644 --- a/homeassistant/components/fritzbox/translations/tr.json +++ b/homeassistant/components/fritzbox/translations/tr.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "ignore_ip6_link_local": "IPv6 ba\u011flant\u0131 yerel adresi desteklenmiyor.", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", "not_supported": "AVM FRITZ!Box'a ba\u011fl\u0131 ancak Ak\u0131ll\u0131 Ev cihazlar\u0131n\u0131 kontrol edemiyor.", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" diff --git a/homeassistant/components/fritzbox/translations/zh-Hant.json b/homeassistant/components/fritzbox/translations/zh-Hant.json index b90b87aaee7..904cd81f625 100644 --- a/homeassistant/components/fritzbox/translations/zh-Hant.json +++ b/homeassistant/components/fritzbox/translations/zh-Hant.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "ignore_ip6_link_local": "\u4e0d\u652f\u63f4\u9023\u7d50\u672c\u5730\u7aef IPv6 \u4f4d\u5740", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", "not_supported": "\u5df2\u9023\u7dda\u81f3 AVM FRITZ!Box \u4f46\u7121\u6cd5\u63a7\u5236\u667a\u80fd\u5bb6\u5ead\u88dd\u7f6e\u3002", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json new file mode 100644 index 00000000000..dc9bd439f0b --- /dev/null +++ b/homeassistant/components/generic/translations/bg.json @@ -0,0 +1,53 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?" + }, + "content_type": { + "data": { + "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435" + }, + "description": "\u041f\u043e\u0441\u043e\u0447\u0435\u0442\u0435 \u0442\u0438\u043f\u0430 \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435 \u0437\u0430 \u043f\u043e\u0442\u043e\u043a\u0430." + }, + "user": { + "data": { + "authentication": "\u0423\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + }, + "options": { + "error": { + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435" + }, + "description": "\u041f\u043e\u0441\u043e\u0447\u0435\u0442\u0435 \u0442\u0438\u043f\u0430 \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435 \u0437\u0430 \u043f\u043e\u0442\u043e\u043a\u0430." + }, + "init": { + "data": { + "authentication": "\u0423\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/ca.json b/homeassistant/components/generic/translations/ca.json new file mode 100644 index 00000000000..3c2f5055ba5 --- /dev/null +++ b/homeassistant/components/generic/translations/ca.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "No s'han trobat dispositius a la xarxa", + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + }, + "error": { + "already_exists": "Ja hi ha una c\u00e0mera amb aquest URL de configuraci\u00f3.", + "invalid_still_image": "L'URL no ha retornat una imatge fixa v\u00e0lida", + "no_still_image_or_stream_url": "Has d'especificar almenys una imatge un URL de flux", + "stream_file_not_found": "Fitxer no trobat mentre s'intentava connectar al flux de dades (est\u00e0 instal\u00b7lat ffmpeg?)", + "stream_http_not_found": "HTTP 404 'Not found' a l'intentar connectar-se al flux de dades ('stream')", + "stream_io_error": "Error d'entrada/sortida mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", + "stream_no_route_to_host": "No s'ha pogut trobar l'amfitri\u00f3 mentre intentava connectar al flux de dades", + "stream_no_video": "El flux no cont\u00e9 v\u00eddeo", + "stream_not_permitted": "Operaci\u00f3 no permesa mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", + "stream_unauthorised": "L'autoritzaci\u00f3 ha fallat mentre s'intentava connectar amb el flux de dades", + "timeout": "El temps m\u00e0xim de c\u00e0rrega de l'URL ha expirat", + "unable_still_load": "No s'ha pogut carregar cap imatge v\u00e0lida des de l'URL d'imatge fixa (pot ser per un amfitri\u00f3 o URL inv\u00e0lid o un error d'autenticaci\u00f3). Revisa els registres per a m\u00e9s informaci\u00f3.", + "unknown": "Error inesperat" + }, + "step": { + "confirm": { + "description": "Vols comen\u00e7ar la configuraci\u00f3?" + }, + "content_type": { + "data": { + "content_type": "Tipus de contingut" + }, + "description": "Especifica el tipus de contingut per al flux de dades (stream)." + }, + "user": { + "data": { + "authentication": "Autenticaci\u00f3", + "content_type": "Tipus de contingut", + "framerate": "Freq\u00fc\u00e8ncia de visualitzaci\u00f3 (Hz)", + "limit_refetch_to_url_change": "Limita la lectura al canvi d'URL", + "password": "Contrasenya", + "rtsp_transport": "Protocol de transport RTSP", + "still_image_url": "URL d'imatge fixa (p. ex. http://...)", + "stream_source": "URL origen del flux (p. ex. rtsp://...)", + "username": "Nom d'usuari", + "verify_ssl": "Verifica el certificat SSL" + }, + "description": "Introdueix la configuraci\u00f3 de connexi\u00f3 amb la c\u00e0mera." + } + } + }, + "options": { + "error": { + "already_exists": "Ja hi ha una c\u00e0mera amb aquest URL de configuraci\u00f3.", + "invalid_still_image": "L'URL no ha retornat una imatge fixa v\u00e0lida", + "no_still_image_or_stream_url": "Has d'especificar almenys una imatge un URL de flux", + "stream_file_not_found": "Fitxer no trobat mentre s'intentava connectar al flux de dades (est\u00e0 instal\u00b7lat ffmpeg?)", + "stream_http_not_found": "HTTP 404 'Not found' a l'intentar connectar-se al flux de dades ('stream')", + "stream_io_error": "Error d'entrada/sortida mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", + "stream_no_route_to_host": "No s'ha pogut trobar l'amfitri\u00f3 mentre intentava connectar al flux de dades", + "stream_no_video": "El flux no cont\u00e9 v\u00eddeo", + "stream_not_permitted": "Operaci\u00f3 no permesa mentre s'intentava connectar al flux de dades. Protocol de transport RTSP incorrecte?", + "stream_unauthorised": "L'autoritzaci\u00f3 ha fallat mentre s'intentava connectar amb el flux de dades", + "timeout": "El temps m\u00e0xim de c\u00e0rrega de l'URL ha expirat", + "unable_still_load": "No s'ha pogut carregar cap imatge v\u00e0lida des de l'URL d'imatge fixa (pot ser per un amfitri\u00f3 o URL inv\u00e0lid o un error d'autenticaci\u00f3). Revisa els registres per a m\u00e9s informaci\u00f3.", + "unknown": "Error inesperat" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tipus de contingut" + }, + "description": "Especifica el tipus de contingut per al flux de dades (stream)." + }, + "init": { + "data": { + "authentication": "Autenticaci\u00f3", + "content_type": "Tipus de contingut", + "framerate": "Freq\u00fc\u00e8ncia de visualitzaci\u00f3 (Hz)", + "limit_refetch_to_url_change": "Limita la lectura al canvi d'URL", + "password": "Contrasenya", + "rtsp_transport": "Protocol de transport RTSP", + "still_image_url": "URL d'imatge fixa (p. ex. http://...)", + "stream_source": "URL origen del flux (p. ex. rtsp://...)", + "username": "Nom d'usuari", + "verify_ssl": "Verifica el certificat SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/cs.json b/homeassistant/components/generic/translations/cs.json new file mode 100644 index 00000000000..520e0743edd --- /dev/null +++ b/homeassistant/components/generic/translations/cs.json @@ -0,0 +1,33 @@ +{ + "config": { + "step": { + "content_type": { + "data": { + "content_type": "Typ obsahu" + } + }, + "user": { + "data": { + "password": "Heslo", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + } + } + } + }, + "options": { + "step": { + "content_type": { + "data": { + "content_type": "Typ obsahu" + } + }, + "init": { + "data": { + "password": "Heslo", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no", + "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/de.json b/homeassistant/components/generic/translations/de.json new file mode 100644 index 00000000000..849555dcc5e --- /dev/null +++ b/homeassistant/components/generic/translations/de.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + }, + "error": { + "already_exists": "Es existiert bereits eine Kamera mit diesen URL-Einstellungen.", + "invalid_still_image": "URL hat kein g\u00fcltiges Standbild zur\u00fcckgegeben", + "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", + "stream_file_not_found": "Datei nicht gefunden beim Versuch, eine Verbindung zum Stream herzustellen (ist ffmpeg installiert?)", + "stream_http_not_found": "HTTP 404 Not found beim Versuch, eine Verbindung zum Stream herzustellen", + "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", + "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", + "stream_no_video": "Stream enth\u00e4lt kein Video", + "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", + "stream_unauthorised": "Autorisierung beim Versuch, eine Verbindung zum Stream herzustellen, fehlgeschlagen", + "timeout": "Zeit\u00fcberschreitung beim Laden der URL", + "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "confirm": { + "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" + }, + "content_type": { + "data": { + "content_type": "Inhaltstyp" + }, + "description": "Gib den Inhaltstyp des Streams an." + }, + "user": { + "data": { + "authentication": "Authentifizierung", + "content_type": "Inhaltstyp", + "framerate": "Bildfrequenz (Hz)", + "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", + "password": "Passwort", + "rtsp_transport": "RTSP-Transportprotokoll", + "still_image_url": "Standbild-URL (z.B. http://...)", + "stream_source": "Stream-Quell-URL (z.B. rtsp://...)", + "username": "Benutzername", + "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + }, + "description": "Gib die Einstellungen f\u00fcr die Verbindung mit der Kamera ein." + } + } + }, + "options": { + "error": { + "already_exists": "Es existiert bereits eine Kamera mit diesen URL-Einstellungen.", + "invalid_still_image": "URL hat kein g\u00fcltiges Standbild zur\u00fcckgegeben", + "no_still_image_or_stream_url": "Du musst mindestens eine Standbild- oder Stream-URL angeben", + "stream_file_not_found": "Datei nicht gefunden beim Versuch, eine Verbindung zum Stream herzustellen (ist ffmpeg installiert?)", + "stream_http_not_found": "HTTP 404 Not found beim Versuch, eine Verbindung zum Stream herzustellen", + "stream_io_error": "Eingabe-/Ausgabefehler beim Versuch, eine Verbindung zum Stream herzustellen. Falsches RTSP-Transportprotokoll?", + "stream_no_route_to_host": "Beim Versuch, eine Verbindung zum Stream herzustellen, konnte der Host nicht gefunden werden", + "stream_no_video": "Stream enth\u00e4lt kein Video", + "stream_not_permitted": "Beim Versuch, eine Verbindung zum Stream herzustellen, ist ein Vorgang nicht zul\u00e4ssig. Falsches RTSP-Transportprotokoll?", + "stream_unauthorised": "Autorisierung beim Versuch, eine Verbindung zum Stream herzustellen, fehlgeschlagen", + "timeout": "Zeit\u00fcberschreitung beim Laden der URL", + "unable_still_load": "Es konnte kein g\u00fcltiges Bild von der Standbild-URL geladen werden (z. B. ung\u00fcltiger Host, URL oder Authentifizierungsfehler). \u00dcberpr\u00fcfe das Protokoll f\u00fcr weitere Informationen.", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "content_type": { + "data": { + "content_type": "Inhaltstyp" + }, + "description": "Gib den Inhaltstyp des Streams an." + }, + "init": { + "data": { + "authentication": "Authentifizierung", + "content_type": "Inhaltstyp", + "framerate": "Bildfrequenz (Hz)", + "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", + "password": "Passwort", + "rtsp_transport": "RTSP-Transportprotokoll", + "still_image_url": "Standbild-URL (z.B. http://...)", + "stream_source": "Stream-Quell-URL (z.B. rtsp://...)", + "username": "Benutzername", + "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/el.json b/homeassistant/components/generic/translations/el.json new file mode 100644 index 00000000000..f3fae37f5ca --- /dev/null +++ b/homeassistant/components/generic/translations/el.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", + "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + }, + "error": { + "already_exists": "\u03a5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 URL.", + "invalid_still_image": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b4\u03b5\u03bd \u03b5\u03c0\u03ad\u03c3\u03c4\u03c1\u03b5\u03c8\u03b5 \u03bc\u03b9\u03b1 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1", + "no_still_image_or_stream_url": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03ae \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c1\u03bf\u03ae\u03c2", + "stream_file_not_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae (\u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b5\u03c3\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf ffmpeg;)", + "stream_http_not_found": "HTTP 404 \u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "stream_io_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5/\u03b5\u03be\u03cc\u03b4\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", + "stream_no_route_to_host": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "stream_no_video": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", + "stream_not_permitted": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", + "stream_unauthorised": "\u0397 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL", + "unable_still_load": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2, \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03ae \u03b1\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2). \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" + }, + "content_type": { + "data": { + "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5" + }, + "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03bf\u03ae." + }, + "user": { + "data": { + "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5", + "framerate": "\u03a1\u03c5\u03b8\u03bc\u03cc\u03c2 \u03ba\u03b1\u03c1\u03ad (Hz)", + "limit_refetch_to_url_change": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae url", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "rtsp_transport": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP", + "still_image_url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. http://...)", + "stream_source": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c0\u03b7\u03b3\u03ae\u03c2 \u03c1\u03bf\u03ae\u03c2 (\u03c0.\u03c7. rtsp://...)", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1." + } + } + }, + "options": { + "error": { + "already_exists": "\u03a5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 URL.", + "invalid_still_image": "\u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b4\u03b5\u03bd \u03b5\u03c0\u03ad\u03c3\u03c4\u03c1\u03b5\u03c8\u03b5 \u03bc\u03b9\u03b1 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1", + "no_still_image_or_stream_url": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03c5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03bd \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1 \u03ae \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c1\u03bf\u03ae\u03c2", + "stream_file_not_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae (\u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b5\u03c3\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf ffmpeg;)", + "stream_http_not_found": "HTTP 404 \u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "stream_io_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5/\u03b5\u03be\u03cc\u03b4\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", + "stream_no_route_to_host": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "stream_no_video": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", + "stream_not_permitted": "\u0394\u03b5\u03bd \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03c4\u03b1\u03b9 \u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03b5 \u03c1\u03bf\u03ae. \u039b\u03ac\u03b8\u03bf\u03c2 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP;", + "stream_unauthorised": "\u0397 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03bf\u03ae", + "timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL", + "unable_still_load": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b1\u03ba\u03af\u03bd\u03b7\u03c4\u03b7\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2, \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03ae \u03b1\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2). \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5" + }, + "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03bf\u03ae." + }, + "init": { + "data": { + "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5", + "framerate": "\u03a1\u03c5\u03b8\u03bc\u03cc\u03c2 \u03ba\u03b1\u03c1\u03ad (Hz)", + "limit_refetch_to_url_change": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae url", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "rtsp_transport": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP", + "still_image_url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. http://...)", + "stream_source": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c0\u03b7\u03b3\u03ae\u03c2 \u03c1\u03bf\u03ae\u03c2 (\u03c0.\u03c7. rtsp://...)", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index 478ea76b476..b158488f178 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -32,6 +32,7 @@ "user": { "data": { "authentication": "Authentication", + "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", @@ -71,6 +72,7 @@ "init": { "data": { "authentication": "Authentication", + "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", diff --git a/homeassistant/components/generic/translations/et.json b/homeassistant/components/generic/translations/et.json new file mode 100644 index 00000000000..ae6f5aa75f8 --- /dev/null +++ b/homeassistant/components/generic/translations/et.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet", + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "error": { + "already_exists": "Nende URL-i seadetega kaamera on juba olemas.", + "invalid_still_image": "URL ei tagastanud kehtivat pilti", + "no_still_image_or_stream_url": "Pead m\u00e4\u00e4rama v\u00e4hemalt liikumatu pildi v\u00f5i voo URL-i", + "stream_file_not_found": "Vooga \u00fchenduse loomisel ei leitud faili (kas ffmpeg on installitud?)", + "stream_http_not_found": "HTTP 404 viga kui \u00fcritatakse vooga \u00fchendust luua", + "stream_io_error": "Sisend-/v\u00e4ljundviga vooga \u00fchenduse loomisel. Vale RTSP transpordiprotokoll?", + "stream_no_route_to_host": "Vooga \u00fchenduse loomisel ei leitud hosti", + "stream_no_video": "Voos pole videot", + "stream_not_permitted": "Vooga \u00fchenduse loomisel pole toiming lubatud. Vale RTSP transpordiprotokoll?", + "stream_unauthorised": "Autoriseerimine eba\u00f5nnestus vooga \u00fchendamise ajal", + "timeout": "URL-i laadimise ajal\u00f5pp", + "unable_still_load": "Pilti ei saa laadida URL-ist (nt kehtetu host, URL v\u00f5i autentimise t\u00f5rge). Lisateabe saamiseks vaata logi.", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "confirm": { + "description": "Kas alustada seadistamist?" + }, + "content_type": { + "data": { + "content_type": "Sisu t\u00fc\u00fcp" + }, + "description": "M\u00e4\u00e4ra voo sisut\u00fc\u00fcp." + }, + "user": { + "data": { + "authentication": "Autentimine", + "content_type": "Sisu t\u00fc\u00fcp", + "framerate": "Kaadrisagedus (Hz)", + "limit_refetch_to_url_change": "Piira laadimist URL-i muutmiseni", + "password": "Salas\u00f5na", + "rtsp_transport": "RTSP transpordiprotokoll", + "still_image_url": "Pildi URL (nt http://...)", + "stream_source": "Voo allikas URL (nt rtsp://...)", + "username": "Kasutajanimi", + "verify_ssl": "Kontrolli SSL sertifikaati" + }, + "description": "Sisesta s\u00e4tted kaameraga \u00fchenduse loomiseks." + } + } + }, + "options": { + "error": { + "already_exists": "Nende URL-i seadetega kaamera on juba olemas.", + "invalid_still_image": "URL ei tagastanud kehtivat pilti", + "no_still_image_or_stream_url": "Pead m\u00e4\u00e4rama v\u00e4hemalt liikumatu pildi v\u00f5i voo URL-i", + "stream_file_not_found": "Vooga \u00fchenduse loomisel ei leitud faili (kas ffmpeg on installitud?)", + "stream_http_not_found": "HTTP 404 viga kui \u00fcritatakse vooga \u00fchendust luua", + "stream_io_error": "Sisend-/v\u00e4ljundviga vooga \u00fchenduse loomisel. Vale RTSP transpordiprotokoll?", + "stream_no_route_to_host": "Vooga \u00fchenduse loomisel ei leitud hosti", + "stream_no_video": "Voos pole videot", + "stream_not_permitted": "Vooga \u00fchenduse loomisel pole toiming lubatud. Vale RTSP transpordiprotokoll?", + "stream_unauthorised": "Autoriseerimine eba\u00f5nnestus vooga \u00fchendamise ajal", + "timeout": "URL-i laadimise ajal\u00f5pp", + "unable_still_load": "Pilti ei saa laadida URL-ist (nt kehtetu host, URL v\u00f5i autentimise t\u00f5rge). Lisateabe saamiseks vaata logi.", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "content_type": { + "data": { + "content_type": "Sisu t\u00fc\u00fcp" + }, + "description": "M\u00e4\u00e4ra voo sisut\u00fc\u00fcp." + }, + "init": { + "data": { + "authentication": "Autentimine", + "content_type": "Sisu t\u00fc\u00fcp", + "framerate": "Kaadrisagedus (Hz)", + "limit_refetch_to_url_change": "Piira laadimist URL-i muutmiseni", + "password": "Salas\u00f5na", + "rtsp_transport": "RTSP transpordiprotokoll", + "still_image_url": "Pildi URL (nt http://...)", + "stream_source": "Voo allikas URL (nt rtsp://...)", + "username": "Kasutajanimi", + "verify_ssl": "Kontrolli SSL sertifikaati" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/fr.json b/homeassistant/components/generic/translations/fr.json new file mode 100644 index 00000000000..4905afb64e7 --- /dev/null +++ b/homeassistant/components/generic/translations/fr.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + }, + "error": { + "already_exists": "Une cam\u00e9ra avec ces param\u00e8tres d'URL existe d\u00e9j\u00e0.", + "invalid_still_image": "L'URL n'a pas renvoy\u00e9 d'image fixe valide", + "no_still_image_or_stream_url": "Vous devez au moins renseigner une URL d'image fixe ou de flux", + "stream_file_not_found": "Fichier non trouv\u00e9 lors de la tentative de connexion au flux (ffmpeg est-il install\u00e9\u00a0?)", + "stream_http_not_found": "Erreur\u00a0404 (introuvable) lors de la tentative de connexion au flux", + "stream_io_error": "Erreur d'entr\u00e9e/sortie lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", + "stream_no_route_to_host": "Impossible de trouver l'h\u00f4te lors de la tentative de connexion au flux", + "stream_no_video": "Le flux ne contient pas de vid\u00e9o", + "stream_not_permitted": "Op\u00e9ration non autoris\u00e9e lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", + "stream_unauthorised": "\u00c9chec de l'autorisation lors de la tentative de connexion au flux", + "timeout": "D\u00e9lai d'attente expir\u00e9 lors du chargement de l'URL", + "unable_still_load": "Impossible de charger une image valide depuis l'URL d'image fixe (cela pourrait \u00eatre d\u00fb \u00e0 un h\u00f4te ou \u00e0 une URL non valide, ou \u00e0 un \u00e9chec de l'authentification). Consultez le journal pour plus d'informations.", + "unknown": "Erreur inattendue" + }, + "step": { + "confirm": { + "description": "Voulez-vous commencer la configuration\u00a0?" + }, + "content_type": { + "data": { + "content_type": "Type de contenu" + }, + "description": "Sp\u00e9cifiez le type de contenu du flux." + }, + "user": { + "data": { + "authentication": "Authentification", + "content_type": "Type de contenu", + "framerate": "Fr\u00e9quence d'images (en hertz)", + "limit_refetch_to_url_change": "Limiter la r\u00e9cup\u00e9ration aux changements d'URL", + "password": "Mot de passe", + "rtsp_transport": "Protocole de transport RTSP", + "still_image_url": "URL d'image fixe (par exemple, http://\u2026)", + "stream_source": "URL de la source du flux (par exemple, rtsp://\u2026)", + "username": "Nom d'utilisateur", + "verify_ssl": "V\u00e9rifier le certificat SSL" + }, + "description": "Saisissez les param\u00e8tres de connexion \u00e0 la cam\u00e9ra." + } + } + }, + "options": { + "error": { + "already_exists": "Une cam\u00e9ra avec ces param\u00e8tres d'URL existe d\u00e9j\u00e0.", + "invalid_still_image": "L'URL n'a pas renvoy\u00e9 d'image fixe valide", + "no_still_image_or_stream_url": "Vous devez au moins renseigner une URL d'image fixe ou de flux", + "stream_file_not_found": "Fichier non trouv\u00e9 lors de la tentative de connexion au flux (ffmpeg est-il install\u00e9\u00a0?)", + "stream_http_not_found": "Erreur\u00a0404 (introuvable) lors de la tentative de connexion au flux", + "stream_io_error": "Erreur d'entr\u00e9e/sortie lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", + "stream_no_route_to_host": "Impossible de trouver l'h\u00f4te lors de la tentative de connexion au flux", + "stream_no_video": "Le flux ne contient pas de vid\u00e9o", + "stream_not_permitted": "Op\u00e9ration non autoris\u00e9e lors de la tentative de connexion au flux. Mauvais protocole de transport RTSP\u00a0?", + "stream_unauthorised": "\u00c9chec de l'autorisation lors de la tentative de connexion au flux", + "timeout": "D\u00e9lai d'attente expir\u00e9 lors du chargement de l'URL", + "unable_still_load": "Impossible de charger une image valide depuis l'URL d'image fixe (cela pourrait \u00eatre d\u00fb \u00e0 un h\u00f4te ou \u00e0 une URL non valide, ou \u00e0 un \u00e9chec de l'authentification). Consultez le journal pour plus d'informations.", + "unknown": "Erreur inattendue" + }, + "step": { + "content_type": { + "data": { + "content_type": "Type de contenu" + }, + "description": "Sp\u00e9cifiez le type de contenu du flux." + }, + "init": { + "data": { + "authentication": "Authentification", + "content_type": "Type de contenu", + "framerate": "Fr\u00e9quence d'images (en hertz)", + "limit_refetch_to_url_change": "Limiter la r\u00e9cup\u00e9ration aux changements d'URL", + "password": "Mot de passe", + "rtsp_transport": "Protocole de transport RTSP", + "still_image_url": "URL d'image fixe (par exemple, http://\u2026)", + "stream_source": "URL de la source du flux (par exemple, rtsp://\u2026)", + "username": "Nom d'utilisateur", + "verify_ssl": "V\u00e9rifier le certificat SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json new file mode 100644 index 00000000000..b20b0ea88a7 --- /dev/null +++ b/homeassistant/components/generic/translations/he.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + }, + "error": { + "already_exists": "\u05de\u05e6\u05dc\u05de\u05d4 \u05e2\u05dd \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05d5 \u05db\u05d1\u05e8 \u05e7\u05d9\u05d9\u05de\u05ea.", + "invalid_still_image": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05dc\u05d0 \u05d4\u05d7\u05d6\u05d9\u05e8\u05d4 \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d7\u05d5\u05e7\u05d9\u05ea", + "no_still_image_or_stream_url": "\u05d9\u05e9 \u05dc\u05e6\u05d9\u05d9\u05df \u05dc\u05e4\u05d7\u05d5\u05ea \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05d4\u05d6\u05e8\u05de\u05d4", + "stream_file_not_found": "\u05d4\u05e7\u05d5\u05d1\u05e5 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4 (\u05d4\u05d0\u05dd ffmpeg \u05de\u05d5\u05ea\u05e7\u05df?)", + "stream_http_not_found": "HTTP 404 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_io_error": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05e7\u05dc\u05d8/\u05e4\u05dc\u05d8 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", + "stream_no_route_to_host": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05d4\u05d9\u05d4 \u05dc\u05de\u05e6\u05d5\u05d0 \u05d0\u05ea \u05d4\u05de\u05d0\u05e8\u05d7 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_no_video": "\u05d0\u05d9\u05df \u05d5\u05d9\u05d3\u05d9\u05d0\u05d5 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", + "stream_unauthorised": "\u05d4\u05d4\u05e8\u05e9\u05d0\u05d4 \u05e0\u05db\u05e9\u05dc\u05d4 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", + "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "confirm": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" + }, + "content_type": { + "data": { + "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df" + }, + "description": "\u05e0\u05d0 \u05dc\u05e6\u05d9\u05d9\u05df \u05d0\u05ea \u05e1\u05d5\u05d2 \u05d4\u05ea\u05d5\u05db\u05df \u05e2\u05d1\u05d5\u05e8 \u05d4\u05d6\u05e8\u05dd." + }, + "user": { + "data": { + "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", + "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df", + "framerate": "\u05e7\u05e6\u05d1 \u05e4\u05e8\u05d9\u05d9\u05de\u05d9\u05dd (\u05d4\u05e8\u05e5)", + "limit_refetch_to_url_change": "\u05d4\u05d2\u05d1\u05dc\u05d4 \u05e9\u05dc \u05d0\u05d7\u05e1\u05d5\u05df \u05d7\u05d5\u05d6\u05e8 \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8", + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "rtsp_transport": "\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 RTSP", + "still_image_url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, http://...)", + "stream_source": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05de\u05e7\u05d5\u05e8 \u05d6\u05e8\u05dd (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, rtsp://...)", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", + "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" + }, + "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05e6\u05dc\u05de\u05d4." + } + } + }, + "options": { + "error": { + "already_exists": "\u05de\u05e6\u05dc\u05de\u05d4 \u05e2\u05dd \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05d5 \u05db\u05d1\u05e8 \u05e7\u05d9\u05d9\u05de\u05ea.", + "invalid_still_image": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05dc\u05d0 \u05d4\u05d7\u05d6\u05d9\u05e8\u05d4 \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d7\u05d5\u05e7\u05d9\u05ea", + "no_still_image_or_stream_url": "\u05d9\u05e9 \u05dc\u05e6\u05d9\u05d9\u05df \u05dc\u05e4\u05d7\u05d5\u05ea \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05d4\u05d6\u05e8\u05de\u05d4", + "stream_file_not_found": "\u05d4\u05e7\u05d5\u05d1\u05e5 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4 (\u05d4\u05d0\u05dd ffmpeg \u05de\u05d5\u05ea\u05e7\u05df?)", + "stream_http_not_found": "HTTP 404 \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_io_error": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05e7\u05dc\u05d8/\u05e4\u05dc\u05d8 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", + "stream_no_route_to_host": "\u05dc\u05d0 \u05e0\u05d9\u05ea\u05df \u05d4\u05d9\u05d4 \u05dc\u05de\u05e6\u05d5\u05d0 \u05d0\u05ea \u05d4\u05de\u05d0\u05e8\u05d7 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_no_video": "\u05d0\u05d9\u05df \u05d5\u05d9\u05d3\u05d9\u05d0\u05d5 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "stream_not_permitted": "\u05d4\u05e4\u05e2\u05d5\u05dc\u05d4 \u05d0\u05d9\u05e0\u05d4 \u05de\u05d5\u05ea\u05e8\u05ea \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4. \u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 \u05e9\u05d2\u05d5\u05d9 \u05e9\u05dc RTSP?", + "stream_unauthorised": "\u05d4\u05d4\u05e8\u05e9\u05d0\u05d4 \u05e0\u05db\u05e9\u05dc\u05d4 \u05d1\u05e2\u05ea \u05e0\u05d9\u05e1\u05d9\u05d5\u05df \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d4\u05d6\u05e8\u05de\u05d4", + "timeout": "\u05d6\u05de\u05df \u05e7\u05e6\u05d5\u05d1 \u05d1\u05e2\u05ea \u05d8\u05e2\u05d9\u05e0\u05ea \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", + "unable_still_load": "\u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d8\u05e2\u05d5\u05df \u05ea\u05de\u05d5\u05e0\u05d4 \u05d7\u05d5\u05e7\u05d9\u05ea \u05de\u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, \u05db\u05e9\u05dc \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9 \u05d1\u05de\u05d7\u05e9\u05d1 \u05de\u05d0\u05e8\u05d7, \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d0\u05d5 \u05d0\u05d9\u05de\u05d5\u05ea). \u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05d9\u05d5\u05de\u05df \u05d4\u05e8\u05d9\u05e9\u05d5\u05dd \u05dc\u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3.", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df" + }, + "description": "\u05e0\u05d0 \u05dc\u05e6\u05d9\u05d9\u05df \u05d0\u05ea \u05e1\u05d5\u05d2 \u05d4\u05ea\u05d5\u05db\u05df \u05e2\u05d1\u05d5\u05e8 \u05d4\u05d6\u05e8\u05dd." + }, + "init": { + "data": { + "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", + "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df", + "framerate": "\u05e7\u05e6\u05d1 \u05e4\u05e8\u05d9\u05d9\u05de\u05d9\u05dd (\u05d4\u05e8\u05e5)", + "limit_refetch_to_url_change": "\u05d4\u05d2\u05d1\u05dc\u05d4 \u05e9\u05dc \u05d0\u05d7\u05e1\u05d5\u05df \u05d7\u05d5\u05d6\u05e8 \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8", + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "rtsp_transport": "\u05e4\u05e8\u05d5\u05d8\u05d5\u05e7\u05d5\u05dc \u05ea\u05e2\u05d1\u05d5\u05e8\u05d4 RTSP", + "still_image_url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05ea\u05de\u05d5\u05e0\u05ea \u05e1\u05d8\u05d9\u05dc\u05e1 (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, http://...)", + "stream_source": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05e9\u05dc \u05de\u05e7\u05d5\u05e8 \u05d6\u05e8\u05dd (\u05dc\u05d3\u05d5\u05d2\u05de\u05d4, rtsp://...)", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", + "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/hu.json b/homeassistant/components/generic/translations/hu.json new file mode 100644 index 00000000000..4e97e594d1e --- /dev/null +++ b/homeassistant/components/generic/translations/hu.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + }, + "error": { + "already_exists": "M\u00e1r l\u00e9tezik egy kamera ezekkel az URL-be\u00e1ll\u00edt\u00e1sokkal.", + "invalid_still_image": "Az URL nem adott vissza \u00e9rv\u00e9nyes \u00e1ll\u00f3k\u00e9pet", + "no_still_image_or_stream_url": "Legal\u00e1bb egy \u00e1ll\u00f3k\u00e9pet vagy stream URL-c\u00edmet kell megadnia.", + "stream_file_not_found": "F\u00e1jl nem tal\u00e1lhat\u00f3 a streamhez val\u00f3 csatlakoz\u00e1s sor\u00e1n (telep\u00edtve van az ffmpeg?)", + "stream_http_not_found": "HTTP 404 Not found - hiba az adatfolyamhoz val\u00f3 csatlakoz\u00e1s k\u00f6zben", + "stream_io_error": "Bemeneti/kimeneti hiba t\u00f6rt\u00e9nt az adatfolyamhoz val\u00f3 kapcsol\u00f3d\u00e1s k\u00f6zben. Rossz RTSP sz\u00e1ll\u00edt\u00e1si protokoll?", + "stream_no_route_to_host": "Nem tal\u00e1lhat\u00f3 a c\u00edm, mik\u00f6zben a rendszer az adatfolyamhoz pr\u00f3b\u00e1l csatlakozni", + "stream_no_video": "Az adatfolyamban nincs vide\u00f3", + "stream_not_permitted": "A m\u0171velet nem enged\u00e9lyezett, mik\u00f6zben megpr\u00f3b\u00e1l csatlakozni a folyamhoz. Rossz fajta RTSP protokoll?", + "stream_unauthorised": "A hiteles\u00edt\u00e9s meghi\u00fasult, mik\u00f6zben megpr\u00f3b\u00e1lt csatlakozni az adatfolyamhoz", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az URL bet\u00f6lt\u00e9se k\u00f6zben", + "unable_still_load": "Nem siker\u00fclt \u00e9rv\u00e9nyes k\u00e9pet bet\u00f6lteni az \u00e1ll\u00f3k\u00e9p URL-c\u00edm\u00e9r\u0151l (pl. \u00e9rv\u00e9nytelen host, URL vagy hiteles\u00edt\u00e9si hiba). Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "confirm": { + "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" + }, + "content_type": { + "data": { + "content_type": "Tartalom t\u00edpus" + }, + "description": "Az adatfolyam tartalomt\u00edpua (Content-Type)." + }, + "user": { + "data": { + "authentication": "Hiteles\u00edt\u00e9s", + "content_type": "Tartalom t\u00edpusa", + "framerate": "K\u00e9pkockasebess\u00e9g (Hz)", + "limit_refetch_to_url_change": "Korl\u00e1tozza a visszah\u00edv\u00e1st az url v\u00e1ltoz\u00e1sra", + "password": "Jelsz\u00f3", + "rtsp_transport": "RTSP protokoll", + "still_image_url": "\u00c1ll\u00f3k\u00e9p URL (pl. http://...)", + "stream_source": "Mozg\u00f3k\u00e9p adatfolyam URL (pl. rtsp://...)", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v", + "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" + }, + "description": "Adja meg a kamer\u00e1hoz val\u00f3 csatlakoz\u00e1s be\u00e1ll\u00edt\u00e1sait." + } + } + }, + "options": { + "error": { + "already_exists": "M\u00e1r l\u00e9tezik egy kamera ezekkel az URL-be\u00e1ll\u00edt\u00e1sokkal.", + "invalid_still_image": "Az URL nem adott vissza \u00e9rv\u00e9nyes \u00e1ll\u00f3k\u00e9pet", + "no_still_image_or_stream_url": "Legal\u00e1bb egy \u00e1ll\u00f3k\u00e9pet vagy stream URL-c\u00edmet kell megadnia.", + "stream_file_not_found": "F\u00e1jl nem tal\u00e1lhat\u00f3 a streamhez val\u00f3 csatlakoz\u00e1s sor\u00e1n (telep\u00edtve van az ffmpeg?)", + "stream_http_not_found": "HTTP 404 Not found - hiba az adatfolyamhoz val\u00f3 csatlakoz\u00e1s k\u00f6zben", + "stream_io_error": "Bemeneti/kimeneti hiba t\u00f6rt\u00e9nt az adatfolyamhoz val\u00f3 kapcsol\u00f3d\u00e1s k\u00f6zben. Rossz RTSP sz\u00e1ll\u00edt\u00e1si protokoll?", + "stream_no_route_to_host": "Nem tal\u00e1lhat\u00f3 a c\u00edm, mik\u00f6zben a rendszer az adatfolyamhoz pr\u00f3b\u00e1l csatlakozni", + "stream_no_video": "Az adatfolyamban nincs vide\u00f3", + "stream_not_permitted": "A m\u0171velet nem enged\u00e9lyezett, mik\u00f6zben megpr\u00f3b\u00e1l csatlakozni a folyamhoz. Rossz fajta RTSP protokoll?", + "stream_unauthorised": "A hiteles\u00edt\u00e9s meghi\u00fasult, mik\u00f6zben megpr\u00f3b\u00e1lt csatlakozni az adatfolyamhoz", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az URL bet\u00f6lt\u00e9se k\u00f6zben", + "unable_still_load": "Nem siker\u00fclt \u00e9rv\u00e9nyes k\u00e9pet bet\u00f6lteni az \u00e1ll\u00f3k\u00e9p URL-c\u00edm\u00e9r\u0151l (pl. \u00e9rv\u00e9nytelen host, URL vagy hiteles\u00edt\u00e9si hiba). Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse \u00e1t a napl\u00f3t.", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tartalom t\u00edpus" + }, + "description": "Az adatfolyam tartalomt\u00edpua (Content-Type)." + }, + "init": { + "data": { + "authentication": "Hiteles\u00edt\u00e9s", + "content_type": "Tartalom t\u00edpusa", + "framerate": "K\u00e9pkockasebess\u00e9g (Hz)", + "limit_refetch_to_url_change": "Korl\u00e1tozza a visszah\u00edv\u00e1st az url v\u00e1ltoz\u00e1sra", + "password": "Jelsz\u00f3", + "rtsp_transport": "RTSP protokoll", + "still_image_url": "\u00c1ll\u00f3k\u00e9p URL (pl. http://...)", + "stream_source": "Mozg\u00f3k\u00e9p adatfolyam URL (pl. rtsp://...)", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v", + "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/id.json b/homeassistant/components/generic/translations/id.json new file mode 100644 index 00000000000..4b3106ad1df --- /dev/null +++ b/homeassistant/components/generic/translations/id.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + }, + "error": { + "already_exists": "Kamera dengan setelan URL ini sudah ada.", + "invalid_still_image": "URL tidak mengembalikan gambar diam yang valid", + "no_still_image_or_stream_url": "Anda harus menentukan setidaknya gambar diam atau URL streaming", + "stream_file_not_found": "File tidak ditemukan saat mencoba menyambung ke streaming (sudahkah ffmpeg diinstal?)", + "stream_http_not_found": "HTTP 404 Tidak ditemukan saat mencoba menyambung ke streaming", + "stream_io_error": "Kesalahan Input/Output saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", + "stream_no_route_to_host": "Tidak dapat menemukan host saat mencoba menyambung ke streaming", + "stream_no_video": "Streaming tidak memiliki video", + "stream_not_permitted": "Operasi tidak diizinkan saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", + "stream_unauthorised": "Otorisasi gagal saat mencoba menyambung ke streaming", + "timeout": "Tenggang waktu habis saat memuat URL", + "unable_still_load": "Tidak dapat memuat gambar yang valid dari URL gambar diam (mis. host yang tidak valid, URL, atau kegagalan autentikasi). Tinjau log untuk info lebih lanjut.", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "confirm": { + "description": "Ingin memulai penyiapan?" + }, + "content_type": { + "data": { + "content_type": "Jenis Konten" + }, + "description": "Tentukan jenis konten untuk streaming." + }, + "user": { + "data": { + "authentication": "Autentikasi", + "content_type": "Jenis Konten", + "framerate": "Frame Rate (Hz)", + "limit_refetch_to_url_change": "Batasi pengambilan ulang untuk perubahan URL", + "password": "Kata Sandi", + "rtsp_transport": "Protokol transportasi RTSP", + "still_image_url": "URL Gambar Diam (mis. http://...)", + "stream_source": "URL Sumber Streaming (mis. rtsp://...)", + "username": "Nama Pengguna", + "verify_ssl": "Verifikasi sertifikat SSL" + }, + "description": "Masukkan pengaturan untuk terhubung ke kamera." + } + } + }, + "options": { + "error": { + "already_exists": "Kamera dengan setelan URL ini sudah ada.", + "invalid_still_image": "URL tidak mengembalikan gambar diam yang valid", + "no_still_image_or_stream_url": "Anda harus menentukan setidaknya gambar diam atau URL streaming", + "stream_file_not_found": "File tidak ditemukan saat mencoba menyambung ke streaming (sudahkah ffmpeg diinstal?)", + "stream_http_not_found": "HTTP 404 Tidak ditemukan saat mencoba menyambung ke streaming", + "stream_io_error": "Kesalahan Input/Output saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", + "stream_no_route_to_host": "Tidak dapat menemukan host saat mencoba menyambung ke streaming", + "stream_no_video": "Streaming tidak memiliki video", + "stream_not_permitted": "Operasi tidak diizinkan saat mencoba menyambung ke streaming. Apakah protokol transportasi RTSP salah?", + "stream_unauthorised": "Otorisasi gagal saat mencoba menyambung ke streaming", + "timeout": "Tenggang waktu habis saat memuat URL", + "unable_still_load": "Tidak dapat memuat gambar yang valid dari URL gambar diam (mis. host yang tidak valid, URL, atau kegagalan autentikasi). Tinjau log untuk info lebih lanjut.", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "content_type": { + "data": { + "content_type": "Jenis Konten" + }, + "description": "Tentukan jenis konten untuk streaming." + }, + "init": { + "data": { + "authentication": "Autentikasi", + "content_type": "Jenis Konten", + "framerate": "Frame Rate (Hz)", + "limit_refetch_to_url_change": "Batasi pengambilan ulang untuk perubahan URL", + "password": "Kata Sandi", + "rtsp_transport": "Protokol transportasi RTSP", + "still_image_url": "URL Gambar Diam (mis. http://...)", + "stream_source": "URL Sumber Streaming (mis. rtsp://...)", + "username": "Nama Pengguna", + "verify_ssl": "Verifikasi sertifikat SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json new file mode 100644 index 00000000000..880fcff500b --- /dev/null +++ b/homeassistant/components/generic/translations/it.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Nessun dispositivo trovato sulla rete", + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, + "error": { + "already_exists": "Esiste gi\u00e0 una telecamera con queste impostazioni URL.", + "invalid_still_image": "L'URL non ha restituito un'immagine fissa valida", + "no_still_image_or_stream_url": "Devi specificare almeno un'immagine fissa o un URL di un flusso", + "stream_file_not_found": "File non trovato durante il tentativo di connessione al (\u00e8 installato ffmpeg?)", + "stream_http_not_found": "HTTP 404 Non trovato durante il tentativo di connessione al flusso", + "stream_io_error": "Errore di input/output durante il tentativo di connessione al flusso. Protocollo di trasporto RTSP errato?", + "stream_no_route_to_host": "Impossibile trovare l'host durante il tentativo di connessione al flusso", + "stream_no_video": "Il flusso non ha video", + "stream_not_permitted": "Operazione non consentita durante il tentativo di connessione al . Protocollo di trasporto RTSP errato?", + "stream_unauthorised": "Autorizzazione non riuscita durante il tentativo di connessione al flusso", + "timeout": "Timeout durante il caricamento dell'URL", + "unable_still_load": "Impossibile caricare un'immagine valida dall'URL dell'immagine fissa (ad es. host, URL non valido o errore di autenticazione). Esamina il registro per ulteriori informazioni.", + "unknown": "Errore imprevisto" + }, + "step": { + "confirm": { + "description": "Vuoi iniziare la configurazione?" + }, + "content_type": { + "data": { + "content_type": "Tipo di contenuto" + }, + "description": "Specificare il tipo di contenuto per il flusso." + }, + "user": { + "data": { + "authentication": "Autenticazione", + "content_type": "Tipo di contenuto", + "framerate": "Frequenza fotogrammi (Hz)", + "limit_refetch_to_url_change": "Limita il recupero alla modifica dell'URL", + "password": "Password", + "rtsp_transport": "Protocollo di trasporto RTSP", + "still_image_url": "URL immagine fissa (ad es. http://...)", + "stream_source": "URL sorgente del flusso (ad es. rtsp://...)", + "username": "Nome utente", + "verify_ssl": "Verifica il certificato SSL" + }, + "description": "Inserisci le impostazioni per connetterti alla fotocamera." + } + } + }, + "options": { + "error": { + "already_exists": "Esiste gi\u00e0 una telecamera con queste impostazioni URL.", + "invalid_still_image": "L'URL non ha restituito un'immagine fissa valida", + "no_still_image_or_stream_url": "Devi specificare almeno un'immagine fissa o un URL di un flusso", + "stream_file_not_found": "File non trovato durante il tentativo di connessione al (\u00e8 installato ffmpeg?)", + "stream_http_not_found": "HTTP 404 Non trovato durante il tentativo di connessione al flusso", + "stream_io_error": "Errore di input/output durante il tentativo di connessione al flusso. Protocollo di trasporto RTSP errato?", + "stream_no_route_to_host": "Impossibile trovare l'host durante il tentativo di connessione al flusso", + "stream_no_video": "Il flusso non ha video", + "stream_not_permitted": "Operazione non consentita durante il tentativo di connessione al . Protocollo di trasporto RTSP errato?", + "stream_unauthorised": "Autorizzazione non riuscita durante il tentativo di connessione al flusso", + "timeout": "Timeout durante il caricamento dell'URL", + "unable_still_load": "Impossibile caricare un'immagine valida dall'URL dell'immagine fissa (ad es. host, URL non valido o errore di autenticazione). Esamina il registro per ulteriori informazioni.", + "unknown": "Errore imprevisto" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tipo di contenuto" + }, + "description": "Specificare il tipo di contenuto per il flusso." + }, + "init": { + "data": { + "authentication": "Autenticazione", + "content_type": "Tipo di contenuto", + "framerate": "Frequenza fotogrammi (Hz)", + "limit_refetch_to_url_change": "Limita il recupero alla modifica dell'URL", + "password": "Password", + "rtsp_transport": "Protocollo di trasporto RTSP", + "still_image_url": "URL immagine fissa (ad es. http://...)", + "stream_source": "URL sorgente del flusso (ad es. rtsp://...)", + "username": "Nome utente", + "verify_ssl": "Verifica il certificato SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/ja.json b/homeassistant/components/generic/translations/ja.json new file mode 100644 index 00000000000..916142125e6 --- /dev/null +++ b/homeassistant/components/generic/translations/ja.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + }, + "error": { + "already_exists": "\u3053\u306eURL\u8a2d\u5b9a\u306e\u30ab\u30e1\u30e9\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002", + "invalid_still_image": "URL\u304c\u6709\u52b9\u306a\u9759\u6b62\u753b\u50cf\u3092\u8fd4\u3057\u307e\u305b\u3093\u3067\u3057\u305f", + "no_still_image_or_stream_url": "\u9759\u6b62\u753b\u50cf\u3082\u3057\u304f\u306f\u3001\u30b9\u30c8\u30ea\u30fc\u30e0URL\u306e\u3069\u3061\u3089\u304b\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "stream_file_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3068\u304d\u306b\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093(ffmpeg\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u307e\u3059\u304b\uff1f)", + "stream_http_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001HTTP 404\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093", + "stream_io_error": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u305f\u3068\u304d\u306b\u5165\u51fa\u529b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", + "stream_no_route_to_host": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u304c\u3001\u30db\u30b9\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", + "stream_no_video": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u52d5\u753b\u304c\u3042\u308a\u307e\u305b\u3093", + "stream_not_permitted": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u9593\u3001\u64cd\u4f5c\u3067\u304d\u307e\u305b\u3093\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", + "stream_unauthorised": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "timeout": "URL\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", + "unable_still_load": "\u9759\u6b62\u753b\u306eURL\u304b\u3089\u6709\u52b9\u306a\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\uff08\u4f8b: \u7121\u52b9\u306a\u30db\u30b9\u30c8\u3001URL\u3001\u307e\u305f\u306f\u8a8d\u8a3c\u5931\u6557)\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "confirm": { + "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" + }, + "content_type": { + "data": { + "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e" + }, + "description": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30a4\u30d7\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002" + }, + "user": { + "data": { + "authentication": "\u8a8d\u8a3c", + "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e", + "framerate": "\u30d5\u30ec\u30fc\u30e0\u30ec\u30fc\u30c8\uff08Hz\uff09", + "limit_refetch_to_url_change": "URL\u5909\u66f4\u6642\u306e\u518d\u53d6\u5f97\u3092\u5236\u9650\u3059\u308b", + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "rtsp_transport": "RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb", + "still_image_url": "\u9759\u6b62\u753b\u50cf\u306eURL(\u4f8b: http://...)", + "stream_source": "\u30b9\u30c8\u30ea\u30fc\u30e0\u30bd\u30fc\u30b9\u306eURL(\u4f8b: rtsp://...)", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d", + "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" + }, + "description": "\u30ab\u30e1\u30e9\u306b\u63a5\u7d9a\u3059\u308b\u305f\u3081\u306e\u8a2d\u5b9a\u3092\u5165\u529b\u3057\u307e\u3059\u3002" + } + } + }, + "options": { + "error": { + "already_exists": "\u3053\u306eURL\u8a2d\u5b9a\u306e\u30ab\u30e1\u30e9\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002", + "invalid_still_image": "URL\u304c\u6709\u52b9\u306a\u9759\u6b62\u753b\u50cf\u3092\u8fd4\u3057\u307e\u305b\u3093\u3067\u3057\u305f", + "no_still_image_or_stream_url": "\u9759\u6b62\u753b\u50cf\u3082\u3057\u304f\u306f\u3001\u30b9\u30c8\u30ea\u30fc\u30e0URL\u306e\u3069\u3061\u3089\u304b\u3092\u6307\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "stream_file_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u3068\u304d\u306b\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093(ffmpeg\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u3066\u3044\u307e\u3059\u304b\uff1f)", + "stream_http_not_found": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001HTTP 404\u3067\u898b\u3064\u304b\u308a\u307e\u305b\u3093", + "stream_io_error": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u305f\u3068\u304d\u306b\u5165\u51fa\u529b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", + "stream_no_route_to_host": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u307e\u3057\u305f\u304c\u3001\u30db\u30b9\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", + "stream_no_video": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u52d5\u753b\u304c\u3042\u308a\u307e\u305b\u3093", + "stream_not_permitted": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306b\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u9593\u3001\u64cd\u4f5c\u3067\u304d\u307e\u305b\u3093\u3002RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb\u3092\u9593\u9055\u3048\u305f\uff1f", + "stream_unauthorised": "\u30b9\u30c8\u30ea\u30fc\u30e0\u3078\u306e\u63a5\u7d9a\u6642\u306b\u3001\u8a8d\u8a3c\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "timeout": "URL\u306e\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", + "unable_still_load": "\u9759\u6b62\u753b\u306eURL\u304b\u3089\u6709\u52b9\u306a\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\uff08\u4f8b: \u7121\u52b9\u306a\u30db\u30b9\u30c8\u3001URL\u3001\u307e\u305f\u306f\u8a8d\u8a3c\u5931\u6557)\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e" + }, + "description": "\u30b9\u30c8\u30ea\u30fc\u30e0\u306e\u30b3\u30f3\u30c6\u30f3\u30c4\u30bf\u30a4\u30d7\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002" + }, + "init": { + "data": { + "authentication": "\u8a8d\u8a3c", + "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e", + "framerate": "\u30d5\u30ec\u30fc\u30e0\u30ec\u30fc\u30c8\uff08Hz\uff09", + "limit_refetch_to_url_change": "URL\u5909\u66f4\u6642\u306e\u518d\u53d6\u5f97\u3092\u5236\u9650\u3059\u308b", + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "rtsp_transport": "RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb", + "still_image_url": "\u9759\u6b62\u753b\u50cf\u306eURL(\u4f8b: http://...)", + "stream_source": "\u30b9\u30c8\u30ea\u30fc\u30e0\u30bd\u30fc\u30b9\u306eURL(\u4f8b: rtsp://...)", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d", + "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/nl.json b/homeassistant/components/generic/translations/nl.json new file mode 100644 index 00000000000..167374c7aeb --- /dev/null +++ b/homeassistant/components/generic/translations/nl.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Geen apparaten gevonden op het netwerk", + "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + }, + "error": { + "already_exists": "Een camera met deze URL instellingen bestaat al.", + "invalid_still_image": "URL heeft geen geldig stilstaand beeld geretourneerd", + "no_still_image_or_stream_url": "U moet ten minste een stilstaand beeld of stream-URL specificeren", + "stream_file_not_found": "Bestand niet gevonden tijdens verbinding met stream (is ffmpeg ge\u00efnstalleerd?)", + "stream_http_not_found": "HTTP 404 Niet gevonden bij poging om verbinding te maken met stream", + "stream_io_error": "Input/Output fout bij het proberen te verbinden met stream. Verkeerde RTSP transport protocol?", + "stream_no_route_to_host": "Kan de host niet vinden terwijl u verbinding probeert te maken met de stream", + "stream_no_video": "Stream heeft geen video", + "stream_not_permitted": "Operatie niet toegestaan bij poging om verbinding te maken met stream. Verkeerd RTSP transport protocol?", + "stream_unauthorised": "Autorisatie mislukt bij poging om verbinding te maken met stream", + "timeout": "Time-out tijdens het laden van URL", + "unable_still_load": "Kan geen geldige afbeelding laden van stilstaande afbeelding URL (b.v. ongeldige host, URL of authenticatie fout). Bekijk het log voor meer informatie.", + "unknown": "Onverwachte fout" + }, + "step": { + "confirm": { + "description": "Wilt u beginnen met instellen?" + }, + "content_type": { + "data": { + "content_type": "Inhoudstype" + }, + "description": "Geef het inhoudstype voor de stream op." + }, + "user": { + "data": { + "authentication": "Authenticatie", + "content_type": "Inhoudstype", + "framerate": "Framesnelheid (Hz)", + "limit_refetch_to_url_change": "Beperk refetch tot url verandering", + "password": "Wachtwoord", + "rtsp_transport": "RTSP-transportprotocol", + "still_image_url": "URL van stilstaande afbeelding (bijv. http://...)", + "stream_source": "Url van streambron (bijv. rtsp://...)", + "username": "Gebruikersnaam", + "verify_ssl": "SSL-certificaat verifi\u00ebren" + }, + "description": "Voer de instellingen in om verbinding te maken met de camera." + } + } + }, + "options": { + "error": { + "already_exists": "Een camera met deze URL instellingen bestaat al.", + "invalid_still_image": "URL heeft geen geldig stilstaand beeld geretourneerd", + "no_still_image_or_stream_url": "U moet ten minste een stilstaand beeld of stream-URL specificeren", + "stream_file_not_found": "Bestand niet gevonden tijdens verbinding met stream (is ffmpeg ge\u00efnstalleerd?)", + "stream_http_not_found": "HTTP 404 Niet gevonden bij poging om verbinding te maken met stream", + "stream_io_error": "Input/Output fout bij het proberen te verbinden met stream. Verkeerde RTSP transport protocol?", + "stream_no_route_to_host": "Kan de host niet vinden terwijl u verbinding probeert te maken met de stream", + "stream_no_video": "Stream heeft geen video", + "stream_not_permitted": "Operatie niet toegestaan bij poging om verbinding te maken met stream. Verkeerd RTSP transport protocol?", + "stream_unauthorised": "Autorisatie mislukt bij poging om verbinding te maken met stream", + "timeout": "Time-out tijdens het laden van URL", + "unable_still_load": "Kan geen geldige afbeelding laden van stilstaande afbeelding URL (b.v. ongeldige host, URL of authenticatie fout). Bekijk het log voor meer informatie.", + "unknown": "Onverwachte fout" + }, + "step": { + "content_type": { + "data": { + "content_type": "Inhoudstype" + }, + "description": "Geef het inhoudstype voor de stream op." + }, + "init": { + "data": { + "authentication": "Authenticatie", + "content_type": "Inhoudstype", + "framerate": "Framesnelheid (Hz)", + "limit_refetch_to_url_change": "Beperk refetch tot url verandering", + "password": "Wachtwoord", + "rtsp_transport": "RTSP-transportprotocol", + "still_image_url": "URL van stilstaande afbeelding (bijv. http://...)", + "stream_source": "Url van streambron (bijv. rtsp://...)", + "username": "Gebruikersnaam", + "verify_ssl": "SSL-certificaat verifi\u00ebren" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/no.json b/homeassistant/components/generic/translations/no.json new file mode 100644 index 00000000000..1f9eedaced4 --- /dev/null +++ b/homeassistant/components/generic/translations/no.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + }, + "error": { + "already_exists": "Et kamera med disse URL-innstillingene finnes allerede.", + "invalid_still_image": "URL returnerte ikke et gyldig stillbilde", + "no_still_image_or_stream_url": "Du m\u00e5 angi minst en URL-adresse for stillbilde eller dataflyt", + "stream_file_not_found": "Filen ble ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m (er ffmpeg installert?)", + "stream_http_not_found": "HTTP 404 Ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m", + "stream_io_error": "Inn-/utdatafeil under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", + "stream_no_route_to_host": "Kunne ikke finne verten under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", + "stream_no_video": "Stream har ingen video", + "stream_not_permitted": "Operasjon er ikke tillatt mens du pr\u00f8ver \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", + "stream_unauthorised": "Autorisasjonen mislyktes under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", + "timeout": "Tidsavbrudd under innlasting av URL", + "unable_still_load": "Kan ikke laste inn gyldig bilde fra URL-adresse for stillbilde (f.eks. ugyldig verts-, URL- eller godkjenningsfeil). Se gjennom loggen hvis du vil ha mer informasjon.", + "unknown": "Uventet feil" + }, + "step": { + "confirm": { + "description": "Vil du starte oppsettet?" + }, + "content_type": { + "data": { + "content_type": "Innholdstype" + }, + "description": "Angi innholdstypen for str\u00f8mmen." + }, + "user": { + "data": { + "authentication": "Godkjenning", + "content_type": "Innholdstype", + "framerate": "Bildefrekvens (Hz)", + "limit_refetch_to_url_change": "Begrens gjenhenting til endring av nettadresse", + "password": "Passord", + "rtsp_transport": "RTSP transportprotokoll", + "still_image_url": "Stillbilde-URL (f.eks. http://...)", + "stream_source": "Str\u00f8mkilde-URL (f.eks. rtsp://...)", + "username": "Brukernavn", + "verify_ssl": "Verifisere SSL-sertifikat" + }, + "description": "Angi innstillingene for \u00e5 koble til kameraet." + } + } + }, + "options": { + "error": { + "already_exists": "Et kamera med disse URL-innstillingene finnes allerede.", + "invalid_still_image": "URL returnerte ikke et gyldig stillbilde", + "no_still_image_or_stream_url": "Du m\u00e5 angi minst en URL-adresse for stillbilde eller dataflyt", + "stream_file_not_found": "Filen ble ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m (er ffmpeg installert?)", + "stream_http_not_found": "HTTP 404 Ikke funnet under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m", + "stream_io_error": "Inn-/utdatafeil under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", + "stream_no_route_to_host": "Kunne ikke finne verten under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", + "stream_no_video": "Stream har ingen video", + "stream_not_permitted": "Operasjon er ikke tillatt mens du pr\u00f8ver \u00e5 koble til str\u00f8m. Feil RTSP-transportprotokoll?", + "stream_unauthorised": "Autorisasjonen mislyktes under fors\u00f8k p\u00e5 \u00e5 koble til str\u00f8mmen", + "timeout": "Tidsavbrudd under innlasting av URL", + "unable_still_load": "Kan ikke laste inn gyldig bilde fra URL-adresse for stillbilde (f.eks. ugyldig verts-, URL- eller godkjenningsfeil). Se gjennom loggen hvis du vil ha mer informasjon.", + "unknown": "Uventet feil" + }, + "step": { + "content_type": { + "data": { + "content_type": "Innholdstype" + }, + "description": "Angi innholdstypen for str\u00f8mmen." + }, + "init": { + "data": { + "authentication": "Godkjenning", + "content_type": "Innholdstype", + "framerate": "Bildefrekvens (Hz)", + "limit_refetch_to_url_change": "Begrens gjenhenting til endring av nettadresse", + "password": "Passord", + "rtsp_transport": "RTSP transportprotokoll", + "still_image_url": "Stillbilde-URL (f.eks. http://...)", + "stream_source": "Str\u00f8mkilde-URL (f.eks. rtsp://...)", + "username": "Brukernavn", + "verify_ssl": "Verifisere SSL-sertifikat" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/pl.json b/homeassistant/components/generic/translations/pl.json new file mode 100644 index 00000000000..a791a56c065 --- /dev/null +++ b/homeassistant/components/generic/translations/pl.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + }, + "error": { + "already_exists": "Kamera z tymi ustawieniami adresu URL ju\u017c istnieje.", + "invalid_still_image": "Adres URL nie zwr\u00f3ci\u0142 prawid\u0142owego obrazu nieruchomego (still image)", + "no_still_image_or_stream_url": "Musisz poda\u0107 przynajmniej nieruchomy obraz (still image) lub adres URL strumienia", + "stream_file_not_found": "Nie znaleziono pliku podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem (czy ffmpeg jest zainstalowany?)", + "stream_http_not_found": "\"HTTP 404 Nie znaleziono\" podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "stream_io_error": "B\u0142\u0105d wej\u015bcia/wyj\u015bcia podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", + "stream_no_route_to_host": "Nie mo\u017cna znale\u017a\u0107 hosta podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "stream_no_video": "Strumie\u0144 nie zawiera wideo", + "stream_not_permitted": "Operacja nie jest dozwolona podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", + "stream_unauthorised": "Autoryzacja nie powiod\u0142a si\u0119 podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "timeout": "Przekroczono limit czasu podczas \u0142adowania adresu URL", + "unable_still_load": "Nie mo\u017cna za\u0142adowa\u0107 prawid\u0142owego obrazu z adresu URL nieruchomego obrazu (np. nieprawid\u0142owy host, adres URL lub b\u0142\u0105d uwierzytelniania). Przejrzyj logi, aby uzyska\u0107 wi\u0119cej informacji.", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "confirm": { + "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" + }, + "content_type": { + "data": { + "content_type": "Typ zawarto\u015bci" + }, + "description": "Okre\u015bl typ zawarto\u015bci strumienia." + }, + "user": { + "data": { + "authentication": "Uwierzytelnianie", + "content_type": "Typ zawarto\u015bci", + "framerate": "Od\u015bwie\u017canie (Hz)", + "limit_refetch_to_url_change": "Ogranicz pobieranie do zmiany adresu URL", + "password": "Has\u0142o", + "rtsp_transport": "Protok\u00f3\u0142 transportowy RTSP", + "still_image_url": "Adres URL obrazu nieruchomego (np. http://...)", + "stream_source": "Adres URL strumienia (np. rtsp://...)", + "username": "Nazwa u\u017cytkownika", + "verify_ssl": "Weryfikacja certyfikatu SSL" + }, + "description": "Wprowad\u017a ustawienia, aby po\u0142\u0105czy\u0107 si\u0119 z kamer\u0105." + } + } + }, + "options": { + "error": { + "already_exists": "Kamera z tymi ustawieniami adresu URL ju\u017c istnieje.", + "invalid_still_image": "Adres URL nie zwr\u00f3ci\u0142 prawid\u0142owego obrazu nieruchomego (still image)", + "no_still_image_or_stream_url": "Musisz poda\u0107 przynajmniej nieruchomy obraz (still image) lub adres URL strumienia", + "stream_file_not_found": "Nie znaleziono pliku podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem (czy ffmpeg jest zainstalowany?)", + "stream_http_not_found": "\"HTTP 404 Nie znaleziono\" podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "stream_io_error": "B\u0142\u0105d wej\u015bcia/wyj\u015bcia podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", + "stream_no_route_to_host": "Nie mo\u017cna znale\u017a\u0107 hosta podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "stream_no_video": "Strumie\u0144 nie zawiera wideo", + "stream_not_permitted": "Operacja nie jest dozwolona podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem. Z\u0142y protok\u00f3\u0142 transportowy RTSP?", + "stream_unauthorised": "Autoryzacja nie powiod\u0142a si\u0119 podczas pr\u00f3by po\u0142\u0105czenia ze strumieniem", + "timeout": "Przekroczono limit czasu podczas \u0142adowania adresu URL", + "unable_still_load": "Nie mo\u017cna za\u0142adowa\u0107 prawid\u0142owego obrazu z adresu URL nieruchomego obrazu (np. nieprawid\u0142owy host, adres URL lub b\u0142\u0105d uwierzytelniania). Przejrzyj logi, aby uzyska\u0107 wi\u0119cej informacji.", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "content_type": { + "data": { + "content_type": "Typ zawarto\u015bci" + }, + "description": "Okre\u015bl typ zawarto\u015bci strumienia." + }, + "init": { + "data": { + "authentication": "Uwierzytelnianie", + "content_type": "Typ zawarto\u015bci", + "framerate": "Od\u015bwie\u017canie (Hz)", + "limit_refetch_to_url_change": "Ogranicz pobieranie do zmiany adresu URL", + "password": "Has\u0142o", + "rtsp_transport": "Protok\u00f3\u0142 transportowy RTSP", + "still_image_url": "Adres URL obrazu nieruchomego (np. http://...)", + "stream_source": "Adres URL strumienia (np. rtsp://...)", + "username": "Nazwa u\u017cytkownika", + "verify_ssl": "Weryfikacja certyfikatu SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/pt-BR.json b/homeassistant/components/generic/translations/pt-BR.json new file mode 100644 index 00000000000..0e3ce2bd75d --- /dev/null +++ b/homeassistant/components/generic/translations/pt-BR.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "Nenhum dispositivo encontrado na rede", + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + }, + "error": { + "already_exists": "J\u00e1 existe uma c\u00e2mera com essas configura\u00e7\u00f5es de URL.", + "invalid_still_image": "A URL n\u00e3o retornou uma imagem est\u00e1tica v\u00e1lida", + "no_still_image_or_stream_url": "Voc\u00ea deve especificar pelo menos uma imagem est\u00e1tica ou uma URL de stream", + "stream_file_not_found": "Arquivo n\u00e3o encontrado ao tentar se conectar a stream (o ffmpeg est\u00e1 instalado?)", + "stream_http_not_found": "HTTP 404 n\u00e3o encontrado ao tentar se conectar a stream", + "stream_io_error": "Erro de entrada/sa\u00edda ao tentar se conectar a stream. Protocolo RTSP errado?", + "stream_no_route_to_host": "N\u00e3o foi poss\u00edvel encontrar o host ao tentar se conectar a stream", + "stream_no_video": "A stream n\u00e3o tem v\u00eddeo", + "stream_not_permitted": "Opera\u00e7\u00e3o n\u00e3o permitida ao tentar se conectar a stream. Protocolo RTSP errado?", + "stream_unauthorised": "Falha na autoriza\u00e7\u00e3o ao tentar se conectar a stream", + "timeout": "Tempo limite ao carregar a URL", + "unable_still_load": "N\u00e3o foi poss\u00edvel carregar uma imagem v\u00e1lida do URL da imagem est\u00e1tica (por exemplo, host inv\u00e1lido, URL ou falha de autentica\u00e7\u00e3o). Revise o log para obter mais informa\u00e7\u00f5es.", + "unknown": "Erro inesperado" + }, + "step": { + "confirm": { + "description": "Deseja iniciar a configura\u00e7\u00e3o?" + }, + "content_type": { + "data": { + "content_type": "Tipo de conte\u00fado" + }, + "description": "Especifique o tipo de conte\u00fado para o stream." + }, + "user": { + "data": { + "authentication": "Autentica\u00e7\u00e3o", + "content_type": "Tipo de conte\u00fado", + "framerate": "Taxa de quadros (Hz)", + "limit_refetch_to_url_change": "Limitar a nova busca \u00e0 altera\u00e7\u00e3o de URL", + "password": "Senha", + "rtsp_transport": "Protocolo RTSP", + "still_image_url": "URL da imagem est\u00e1tica (por exemplo, http://...)", + "stream_source": "URL de origem da Stream (por exemplo, rtsp://...)", + "username": "Usu\u00e1rio", + "verify_ssl": "Verifique o certificado SSL" + }, + "description": "Insira as configura\u00e7\u00f5es para se conectar \u00e0 c\u00e2mera." + } + } + }, + "options": { + "error": { + "already_exists": "J\u00e1 existe uma c\u00e2mera com essas configura\u00e7\u00f5es de URL.", + "invalid_still_image": "A URL n\u00e3o retornou uma imagem est\u00e1tica v\u00e1lida", + "no_still_image_or_stream_url": "Voc\u00ea deve especificar pelo menos uma imagem est\u00e1tica ou uma URL de stream", + "stream_file_not_found": "Arquivo n\u00e3o encontrado ao tentar se conectar a stream (o ffmpeg est\u00e1 instalado?)", + "stream_http_not_found": "HTTP 404 n\u00e3o encontrado ao tentar se conectar a stream", + "stream_io_error": "Erro de entrada/sa\u00edda ao tentar se conectar a stream. Protocolo RTSP errado?", + "stream_no_route_to_host": "N\u00e3o foi poss\u00edvel encontrar o host ao tentar se conectar a stream", + "stream_no_video": "A stream n\u00e3o tem v\u00eddeo", + "stream_not_permitted": "Opera\u00e7\u00e3o n\u00e3o permitida ao tentar se conectar a stream. Protocolo RTSP errado?", + "stream_unauthorised": "Falha na autoriza\u00e7\u00e3o ao tentar se conectar a stream", + "timeout": "Tempo limite ao carregar a URL", + "unable_still_load": "N\u00e3o foi poss\u00edvel carregar uma imagem v\u00e1lida do URL da imagem est\u00e1tica (por exemplo, host inv\u00e1lido, URL ou falha de autentica\u00e7\u00e3o). Revise o log para obter mais informa\u00e7\u00f5es.", + "unknown": "Erro inesperado" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tipo de conte\u00fado" + }, + "description": "Especifique o tipo de conte\u00fado para o stream." + }, + "init": { + "data": { + "authentication": "Autentica\u00e7\u00e3o", + "content_type": "Tipo de conte\u00fado", + "framerate": "Taxa de quadros (Hz)", + "limit_refetch_to_url_change": "Limitar a nova busca \u00e0 altera\u00e7\u00e3o de URL", + "password": "Senha", + "rtsp_transport": "Protocolo RTSP", + "still_image_url": "URL da imagem est\u00e1tica (por exemplo, http://...)", + "stream_source": "URL de origem da Stream (por exemplo, rtsp://...)", + "username": "Usu\u00e1rio", + "verify_ssl": "Verifique o certificado SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/ru.json b/homeassistant/components/generic/translations/ru.json new file mode 100644 index 00000000000..06f41613a50 --- /dev/null +++ b/homeassistant/components/generic/translations/ru.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + }, + "error": { + "already_exists": "\u041a\u0430\u043c\u0435\u0440\u0430 \u0441 \u0442\u0430\u043a\u0438\u043c URL-\u0430\u0434\u0440\u0435\u0441\u043e\u043c \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.", + "invalid_still_image": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.", + "no_still_image_or_stream_url": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a\u0430.", + "stream_file_not_found": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438 ffmpeg?", + "stream_http_not_found": "HTTP 404 \u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "stream_io_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", + "stream_no_route_to_host": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0445\u043e\u0441\u0442 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "stream_no_video": "\u0412 \u043f\u043e\u0442\u043e\u043a\u0435 \u043d\u0435\u0442 \u0432\u0438\u0434\u0435\u043e.", + "stream_not_permitted": "\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", + "stream_unauthorised": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430.", + "unable_still_load": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0445\u043e\u0441\u0442, URL-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438). \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "confirm": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" + }, + "content_type": { + "data": { + "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e" + }, + "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0442\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0434\u043b\u044f \u043f\u043e\u0442\u043e\u043a\u0430." + }, + "user": { + "data": { + "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e", + "framerate": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432 (\u0413\u0446)", + "limit_refetch_to_url_change": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "rtsp_transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP", + "still_image_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, http://...)", + "stream_source": "URL-\u0430\u0434\u0440\u0435\u0441 \u043f\u043e\u0442\u043e\u043a\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, rtsp://...)", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043a\u0430\u043c\u0435\u0440\u0435." + } + } + }, + "options": { + "error": { + "already_exists": "\u041a\u0430\u043c\u0435\u0440\u0430 \u0441 \u0442\u0430\u043a\u0438\u043c URL-\u0430\u0434\u0440\u0435\u0441\u043e\u043c \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442.", + "invalid_still_image": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.", + "no_still_image_or_stream_url": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a\u0430.", + "stream_file_not_found": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043b\u0438 ffmpeg?", + "stream_http_not_found": "HTTP 404 \u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "stream_io_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0432\u043e\u0434\u0430/\u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", + "stream_no_route_to_host": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0445\u043e\u0441\u0442 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "stream_no_video": "\u0412 \u043f\u043e\u0442\u043e\u043a\u0435 \u043d\u0435\u0442 \u0432\u0438\u0434\u0435\u043e.", + "stream_not_permitted": "\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443. \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP?", + "stream_unauthorised": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043f\u043e\u0442\u043e\u043a\u0443.", + "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430.", + "unable_still_load": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0445\u043e\u0441\u0442, URL-\u0430\u0434\u0440\u0435\u0441 \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438). \u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0436\u0443\u0440\u043d\u0430\u043b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "content_type": { + "data": { + "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e" + }, + "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0442\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0434\u043b\u044f \u043f\u043e\u0442\u043e\u043a\u0430." + }, + "init": { + "data": { + "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e", + "framerate": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432 (\u0413\u0446)", + "limit_refetch_to_url_change": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "rtsp_transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP", + "still_image_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, http://...)", + "stream_source": "URL-\u0430\u0434\u0440\u0435\u0441 \u043f\u043e\u0442\u043e\u043a\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, rtsp://...)", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/tr.json b/homeassistant/components/generic/translations/tr.json new file mode 100644 index 00000000000..c2fb343f227 --- /dev/null +++ b/homeassistant/components/generic/translations/tr.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "A\u011fda cihaz bulunamad\u0131", + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "error": { + "already_exists": "Bu URL ayarlar\u0131na sahip bir kamera zaten var.", + "invalid_still_image": "URL ge\u00e7erli bir hareketsiz resim d\u00f6nd\u00fcrmedi", + "no_still_image_or_stream_url": "En az\u0131ndan bir dura\u011fan resim veya ak\u0131\u015f URL'si belirtmelisiniz", + "stream_file_not_found": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken dosya bulunamad\u0131 (ffmpeg y\u00fckl\u00fc m\u00fc?)", + "stream_http_not_found": "HTTP 404 Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken bulunamad\u0131", + "stream_io_error": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken Giri\u015f/\u00c7\u0131k\u0131\u015f hatas\u0131. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", + "stream_no_route_to_host": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken ana bilgisayar bulunamad\u0131", + "stream_no_video": "Ak\u0131\u015fta video yok", + "stream_not_permitted": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken i\u015fleme izin verilmiyor. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", + "stream_unauthorised": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken yetkilendirme ba\u015far\u0131s\u0131z oldu", + "timeout": "URL y\u00fcklenirken zaman a\u015f\u0131m\u0131", + "unable_still_load": "Hareketsiz resim URL'sinden ge\u00e7erli resim y\u00fcklenemiyor (\u00f6r. ge\u00e7ersiz ana bilgisayar, URL veya kimlik do\u011frulama hatas\u0131). Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", + "unknown": "Beklenmeyen hata" + }, + "step": { + "confirm": { + "description": "Kuruluma ba\u015flamak ister misiniz?" + }, + "content_type": { + "data": { + "content_type": "\u0130\u00e7erik T\u00fcr\u00fc" + }, + "description": "Ak\u0131\u015f i\u00e7in i\u00e7erik t\u00fcr\u00fcn\u00fc belirtin." + }, + "user": { + "data": { + "authentication": "Kimlik Do\u011frulama", + "content_type": "\u0130\u00e7erik T\u00fcr\u00fc", + "framerate": "Kare H\u0131z\u0131 (Hz)", + "limit_refetch_to_url_change": "Yeniden getirmeyi url de\u011fi\u015fikli\u011fiyle s\u0131n\u0131rla", + "password": "Parola", + "rtsp_transport": "RTSP aktar\u0131m protokol\u00fc", + "still_image_url": "Hareketsiz G\u00f6r\u00fcnt\u00fc URL'si (\u00f6rne\u011fin http://...)", + "stream_source": "Ak\u0131\u015f Kayna\u011f\u0131 URL'si (\u00f6r. rtsp://...)", + "username": "Kullan\u0131c\u0131 Ad\u0131", + "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" + }, + "description": "Kameraya ba\u011flanmak i\u00e7in ayarlar\u0131 girin." + } + } + }, + "options": { + "error": { + "already_exists": "Bu URL ayarlar\u0131na sahip bir kamera zaten var.", + "invalid_still_image": "URL ge\u00e7erli bir hareketsiz resim d\u00f6nd\u00fcrmedi", + "no_still_image_or_stream_url": "En az\u0131ndan bir dura\u011fan resim veya ak\u0131\u015f URL'si belirtmelisiniz", + "stream_file_not_found": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken dosya bulunamad\u0131 (ffmpeg y\u00fckl\u00fc m\u00fc?)", + "stream_http_not_found": "HTTP 404 Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken bulunamad\u0131", + "stream_io_error": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken Giri\u015f/\u00c7\u0131k\u0131\u015f hatas\u0131. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", + "stream_no_route_to_host": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken ana bilgisayar bulunamad\u0131", + "stream_no_video": "Ak\u0131\u015fta video yok", + "stream_not_permitted": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken i\u015fleme izin verilmiyor. Yanl\u0131\u015f RTSP aktar\u0131m protokol\u00fc?", + "stream_unauthorised": "Ak\u0131\u015fa ba\u011flanmaya \u00e7al\u0131\u015f\u0131rken yetkilendirme ba\u015far\u0131s\u0131z oldu", + "timeout": "URL y\u00fcklenirken zaman a\u015f\u0131m\u0131", + "unable_still_load": "Hareketsiz resim URL'sinden ge\u00e7erli resim y\u00fcklenemiyor (\u00f6r. ge\u00e7ersiz ana bilgisayar, URL veya kimlik do\u011frulama hatas\u0131). Daha fazla bilgi i\u00e7in g\u00fcnl\u00fc\u011f\u00fc inceleyin.", + "unknown": "Beklenmeyen hata" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u0130\u00e7erik T\u00fcr\u00fc" + }, + "description": "Ak\u0131\u015f i\u00e7in i\u00e7erik t\u00fcr\u00fcn\u00fc belirtin." + }, + "init": { + "data": { + "authentication": "Kimlik Do\u011frulama", + "content_type": "\u0130\u00e7erik T\u00fcr\u00fc", + "framerate": "Kare H\u0131z\u0131 (Hz)", + "limit_refetch_to_url_change": "Yeniden getirmeyi url de\u011fi\u015fikli\u011fiyle s\u0131n\u0131rla", + "password": "Parola", + "rtsp_transport": "RTSP aktar\u0131m protokol\u00fc", + "still_image_url": "Hareketsiz G\u00f6r\u00fcnt\u00fc URL'si (\u00f6rne\u011fin http://...)", + "stream_source": "Ak\u0131\u015f Kayna\u011f\u0131 URL'si (\u00f6r. rtsp://...)", + "username": "Kullan\u0131c\u0131 Ad\u0131", + "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/zh-Hant.json b/homeassistant/components/generic/translations/zh-Hant.json new file mode 100644 index 00000000000..1b72dcb0ce4 --- /dev/null +++ b/homeassistant/components/generic/translations/zh-Hant.json @@ -0,0 +1,88 @@ +{ + "config": { + "abort": { + "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + }, + "error": { + "already_exists": "\u5df2\u7d93\u6709\u4f7f\u7528\u76f8\u540c URL \u7684\u651d\u5f71\u6a5f\u3002", + "invalid_still_image": "URL \u56de\u50b3\u4e26\u975e\u6709\u6548\u975c\u614b\u5f71\u50cf", + "no_still_image_or_stream_url": "\u5fc5\u9808\u81f3\u5c11\u6307\u5b9a\u975c\u614b\u5f71\u50cf\u6216\u4e32\u6d41 URL", + "stream_file_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u627e\u4e0d\u5230\u6a94\u6848\u932f\u8aa4\uff08\u662f\u5426\u5df2\u5b89\u88dd ffmpeg\uff1f\uff09", + "stream_http_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe HTTP 404 \u672a\u627e\u5230\u932f\u8aa4", + "stream_io_error": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u8f38\u5165/\u8f38\u51fa\u932f\u8aa4\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", + "stream_no_route_to_host": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u627e\u4e0d\u5230\u4e3b\u6a5f", + "stream_no_video": "\u4e32\u6d41\u6c92\u6709\u5f71\u50cf", + "stream_not_permitted": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u4e0d\u5141\u8a31\u64cd\u4f5c\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", + "stream_unauthorised": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u8a8d\u8b49\u5931\u6557", + "timeout": "\u8f09\u5165 URL \u903e\u6642\u6642\u9593", + "unable_still_load": "\u7121\u6cd5\u7531\u8a2d\u5b9a\u975c\u614b\u5f71\u50cf URL \u8f09\u5165\u6709\u6548\u5f71\u50cf\uff08\u4f8b\u5982\uff1a\u7121\u6548\u4e3b\u6a5f\u3001URL \u6216\u8a8d\u8b49\u5931\u6557\uff09\u3002\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8a0a\u606f\u3002", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "confirm": { + "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" + }, + "content_type": { + "data": { + "content_type": "\u5167\u5bb9\u985e\u578b" + }, + "description": "\u6307\u5b9a\u4e32\u6d41\u5167\u5bb9\u985e\u5225" + }, + "user": { + "data": { + "authentication": "\u9a57\u8b49", + "content_type": "\u5167\u5bb9\u985e\u578b", + "framerate": "\u5f71\u683c\u7387 (Hz)", + "limit_refetch_to_url_change": "\u9650\u5236 URL \u8b8a\u66f4\u66f4\u65b0", + "password": "\u5bc6\u78bc", + "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a", + "still_image_url": "\u975c\u614b\u5f71\u50cf URL\uff08\u4f8b\u5982 http://...\uff09", + "stream_source": "\u4e32\u6d41\u4f86\u6e90 URL\uff08\u4f8b\u5982 rtsp://...\uff09", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + }, + "description": "\u8f38\u5165\u651d\u5f71\u6a5f\u9023\u7dda\u8a2d\u5b9a\u3002" + } + } + }, + "options": { + "error": { + "already_exists": "\u5df2\u7d93\u6709\u4f7f\u7528\u76f8\u540c URL \u7684\u651d\u5f71\u6a5f\u3002", + "invalid_still_image": "URL \u56de\u50b3\u4e26\u975e\u6709\u6548\u975c\u614b\u5f71\u50cf", + "no_still_image_or_stream_url": "\u5fc5\u9808\u81f3\u5c11\u6307\u5b9a\u975c\u614b\u5f71\u50cf\u6216\u4e32\u6d41 URL", + "stream_file_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u627e\u4e0d\u5230\u6a94\u6848\u932f\u8aa4\uff08\u662f\u5426\u5df2\u5b89\u88dd ffmpeg\uff1f\uff09", + "stream_http_not_found": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe HTTP 404 \u672a\u627e\u5230\u932f\u8aa4", + "stream_io_error": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u51fa\u73fe\u8f38\u5165/\u8f38\u51fa\u932f\u8aa4\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", + "stream_no_route_to_host": "\u5617\u8a66\u9023\u7dda\u4e32\u6d41\u6642\u627e\u4e0d\u5230\u4e3b\u6a5f", + "stream_no_video": "\u4e32\u6d41\u6c92\u6709\u5f71\u50cf", + "stream_not_permitted": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u4e0d\u5141\u8a31\u64cd\u4f5c\u3002\u8f38\u5165\u932f\u8aa4\u7684 RTSP \u50b3\u8f38\u5354\u5b9a\uff1f", + "stream_unauthorised": "\u5617\u8a66\u4e32\u6d41\u9023\u7dda\u6642\u8a8d\u8b49\u5931\u6557", + "timeout": "\u8f09\u5165 URL \u903e\u6642\u6642\u9593", + "unable_still_load": "\u7121\u6cd5\u7531\u8a2d\u5b9a\u975c\u614b\u5f71\u50cf URL \u8f09\u5165\u6709\u6548\u5f71\u50cf\uff08\u4f8b\u5982\uff1a\u7121\u6548\u4e3b\u6a5f\u3001URL \u6216\u8a8d\u8b49\u5931\u6557\uff09\u3002\u8acb\u53c3\u95b1\u65e5\u8a8c\u4ee5\u7372\u5f97\u66f4\u8a73\u7d30\u8a0a\u606f\u3002", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "content_type": { + "data": { + "content_type": "\u5167\u5bb9\u985e\u578b" + }, + "description": "\u6307\u5b9a\u4e32\u6d41\u5167\u5bb9\u985e\u5225" + }, + "init": { + "data": { + "authentication": "\u9a57\u8b49", + "content_type": "\u5167\u5bb9\u985e\u578b", + "framerate": "\u5f71\u683c\u7387 (Hz)", + "limit_refetch_to_url_change": "\u9650\u5236 URL \u8b8a\u66f4\u66f4\u65b0", + "password": "\u5bc6\u78bc", + "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a", + "still_image_url": "\u975c\u614b\u5f71\u50cf URL\uff08\u4f8b\u5982 http://...\uff09", + "stream_source": "\u4e32\u6d41\u4f86\u6e90 URL\uff08\u4f8b\u5982 rtsp://...\uff09", + "username": "\u4f7f\u7528\u8005\u540d\u7a31", + "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gios/translations/hu.json b/homeassistant/components/gios/translations/hu.json index 9454aceb13d..1de37cee96a 100644 --- a/homeassistant/components/gios/translations/hu.json +++ b/homeassistant/components/gios/translations/hu.json @@ -11,7 +11,7 @@ "step": { "user": { "data": { - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "station_id": "A m\u00e9r\u0151\u00e1llom\u00e1s azonos\u00edt\u00f3ja" }, "description": "A GIO\u015a (lengyel k\u00f6rnyezetv\u00e9delmi f\u0151fel\u00fcgyel\u0151) leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ged a konfigur\u00e1ci\u00f3val kapcsolatban, l\u00e1togass ide: https://www.home-assistant.io/integrations/gios", diff --git a/homeassistant/components/glances/translations/hu.json b/homeassistant/components/glances/translations/hu.json index d93fa4bb66e..71649b51d34 100644 --- a/homeassistant/components/glances/translations/hu.json +++ b/homeassistant/components/glances/translations/hu.json @@ -11,7 +11,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", diff --git a/homeassistant/components/goalzero/translations/ca.json b/homeassistant/components/goalzero/translations/ca.json index 0f9979eb378..3e7d6e714d3 100644 --- a/homeassistant/components/goalzero/translations/ca.json +++ b/homeassistant/components/goalzero/translations/ca.json @@ -20,7 +20,7 @@ "host": "Amfitri\u00f3", "name": "Nom" }, - "description": "En primer lloc, has de descarregar-te l'aplicaci\u00f3 Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\nSegueix les instruccions per connectar el Yeti al teu Wi-Fi. Es recomana que la reserva DHCP del router estigui configurada, si no ho est\u00e0, pot ser que el dispositiu no estigui disponible mentre Home Assistant no detecti la nova IP. Consulta el manual del router.", + "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/de.json b/homeassistant/components/goalzero/translations/de.json index 5133488c247..a9f7e3fa887 100644 --- a/homeassistant/components/goalzero/translations/de.json +++ b/homeassistant/components/goalzero/translations/de.json @@ -20,7 +20,7 @@ "host": "Host", "name": "Name" }, - "description": "Zuerst musst du die Goal Zero App herunterladen: https://www.goalzero.com/product-features/yeti-app/ \n\nFolge den Anweisungen, um deinen Yeti mit deinem WLAN-Netzwerk zu verbinden. Eine DHCP-Reservierung auf deinem Router wird empfohlen. Wenn es nicht eingerichtet ist, ist das Ger\u00e4t m\u00f6glicherweise nicht verf\u00fcgbar, bis Home Assistant die neue IP-Adresse erkennt. Schlage dazu im Benutzerhandbuch deines Routers nach.", + "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/en.json b/homeassistant/components/goalzero/translations/en.json index 26a92757a4b..fd27892c794 100644 --- a/homeassistant/components/goalzero/translations/en.json +++ b/homeassistant/components/goalzero/translations/en.json @@ -20,7 +20,7 @@ "host": "Host", "name": "Name" }, - "description": "First, you need to download the Goal Zero app: https://www.goalzero.com/product-features/yeti-app/\n\nFollow the instructions to connect your Yeti to your Wi-fi network. DHCP reservation on your router is recommended. If not set up, the device may become unavailable until Home Assistant detects the new ip address. Refer to your router's user manual.", + "description": "Please refer to the documentation to make sure all requirements are met.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/et.json b/homeassistant/components/goalzero/translations/et.json index 5d0111aa946..e529f0a3a4e 100644 --- a/homeassistant/components/goalzero/translations/et.json +++ b/homeassistant/components/goalzero/translations/et.json @@ -20,7 +20,7 @@ "host": "", "name": "Nimi" }, - "description": "Alustuseks pead alla laadima rakenduse Goal Zero: https://www.goalzero.com/product-features/yeti-app/\n\nYeti Wifi-v\u00f5rguga \u00fchendamiseks j\u00e4rgi juhiseid. DHCP peab olema ruuteri seadetes seadistatud nii, et hosti IP ei muutuks. Vaata ruuteri kasutusjuhendit.", + "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud.", "title": "" } } diff --git a/homeassistant/components/goalzero/translations/fr.json b/homeassistant/components/goalzero/translations/fr.json index b554ec1ea1d..430ad9927c4 100644 --- a/homeassistant/components/goalzero/translations/fr.json +++ b/homeassistant/components/goalzero/translations/fr.json @@ -20,7 +20,7 @@ "host": "H\u00f4te", "name": "Nom" }, - "description": "Vous devez tout d'abord t\u00e9l\u00e9charger l'application Goal Zero\u00a0: https://www.goalzero.com/product-features/yeti-app/\n\nSuivez les instructions pour connecter votre Yeti \u00e0 votre r\u00e9seau Wi-Fi. Il est recommand\u00e9 d'utiliser la r\u00e9servation DHCP sur votre routeur, sans quoi l'appareil risque de devenir indisponible le temps que Home Assistant d\u00e9tecte la nouvelle adresse IP. Reportez-vous au manuel d'utilisation de votre routeur.", + "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/hu.json b/homeassistant/components/goalzero/translations/hu.json index 62c0a1626f9..00196952e0c 100644 --- a/homeassistant/components/goalzero/translations/hu.json +++ b/homeassistant/components/goalzero/translations/hu.json @@ -18,9 +18,9 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, - "description": "El\u0151sz\u00f6r le kell t\u00f6ltenie a Goal Zero alkalmaz\u00e1st: https://www.goalzero.com/product-features/yeti-app/ \n\nK\u00f6vesse az utas\u00edt\u00e1sokat, hogy csatlakoztassa Yeti k\u00e9sz\u00fcl\u00e9k\u00e9t a Wi-Fi h\u00e1l\u00f3zathoz. DHCP foglal\u00e1s aj\u00e1nlott az routeren. Ha nincs be\u00e1ll\u00edtva, akkor az eszk\u00f6z el\u00e9rhetetlenn\u00e9 v\u00e1lhat, am\u00edg a Home Assistant \u00e9szleli az \u00faj IP-c\u00edmet. Olvassa el az router felhaszn\u00e1l\u00f3i k\u00e9zik\u00f6nyv\u00e9t.", + "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/id.json b/homeassistant/components/goalzero/translations/id.json index d5897a2d944..d524b13bb0c 100644 --- a/homeassistant/components/goalzero/translations/id.json +++ b/homeassistant/components/goalzero/translations/id.json @@ -20,7 +20,7 @@ "host": "Host", "name": "Nama" }, - "description": "Pertama, Anda perlu mengunduh aplikasi Goal Zero: https://www.goalzero.com/product-features/yeti-app/\n\nIkuti petunjuk untuk menghubungkan Yeti Anda ke jaringan Wi-Fi Anda. Kemudian dapatkan IP host dari router Anda. DHCP harus disetel di pengaturan router Anda untuk perangkat host agar IP host tidak berubah. Lihat manual pengguna router Anda.", + "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/it.json b/homeassistant/components/goalzero/translations/it.json index ad5042cc602..dbd71c82f4a 100644 --- a/homeassistant/components/goalzero/translations/it.json +++ b/homeassistant/components/goalzero/translations/it.json @@ -20,7 +20,7 @@ "host": "Host", "name": "Nome" }, - "description": "Innanzitutto, devi scaricare l'app Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\nSegui le istruzioni per connettere il tuo Yeti alla tua rete Wi-Fi. Si consiglia la prenotazione DHCP sul router. Se non configurato, il dispositivo potrebbe non essere disponibile fino a quando Home Assistant non rileva il nuovo indirizzo IP. Fare riferimento al manuale utente del router.", + "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/nl.json b/homeassistant/components/goalzero/translations/nl.json index d73c4d648ed..680ff1a10cc 100644 --- a/homeassistant/components/goalzero/translations/nl.json +++ b/homeassistant/components/goalzero/translations/nl.json @@ -20,7 +20,7 @@ "host": "Host", "name": "Naam" }, - "description": "Eerst moet u de Goal Zero-app downloaden: https://www.goalzero.com/product-features/yeti-app/ \n\n Volg de instructies om Yeti te verbinden met uw wifi-netwerk. DHCP-reservering op uw router wordt aanbevolen. Als het niet is ingesteld, is het apparaat mogelijk niet meer beschikbaar totdat Home Assistant het nieuwe IP-adres detecteert. Raadpleeg de gebruikershandleiding van uw router.", + "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten wordt voldaan.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/no.json b/homeassistant/components/goalzero/translations/no.json index 1bc2d6feae1..6bb00952fab 100644 --- a/homeassistant/components/goalzero/translations/no.json +++ b/homeassistant/components/goalzero/translations/no.json @@ -20,7 +20,7 @@ "host": "Vert", "name": "Navn" }, - "description": "F\u00f8rst m\u00e5 du laste ned Goal Zero-appen: https://www.goalzero.com/product-features/yeti-app/\n\nF\u00f8lg instruksjonene for \u00e5 koble Yeti til Wi-Fi-nettverket ditt. DHCP-reservasjon p\u00e5 ruteren anbefales. Hvis den ikke er konfigurert, kan enheten bli utilgjengelig til Home Assistant oppdager den nye IP-adressen. Se brukerh\u00e5ndboken for ruteren.", + "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt.", "title": "" } } diff --git a/homeassistant/components/goalzero/translations/pl.json b/homeassistant/components/goalzero/translations/pl.json index 3aba221bc4a..ee8f2022a0d 100644 --- a/homeassistant/components/goalzero/translations/pl.json +++ b/homeassistant/components/goalzero/translations/pl.json @@ -20,7 +20,7 @@ "host": "Nazwa hosta lub adres IP", "name": "Nazwa" }, - "description": "Najpierw musisz pobra\u0107 aplikacj\u0119 Goal Zero: https://www.goalzero.com/product-features/yeti-app/\n\nPost\u0119puj zgodnie z instrukcjami, aby pod\u0142\u0105czy\u0107 Yeti do sieci Wi-Fi. Zaleca si\u0119 rezerwacj\u0119 DHCP w ustawieniach routera. Je\u015bli tego nie ustawisz, urz\u0105dzenie mo\u017ce sta\u0107 si\u0119 niedost\u0119pne, do czasu a\u017c Home Assistant wykryje nowy adres IP. Post\u0119puj wg instrukcji obs\u0142ugi routera.", + "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/pt-BR.json b/homeassistant/components/goalzero/translations/pt-BR.json index 7ecea696702..fa67129b08a 100644 --- a/homeassistant/components/goalzero/translations/pt-BR.json +++ b/homeassistant/components/goalzero/translations/pt-BR.json @@ -20,7 +20,7 @@ "host": "Nome do host", "name": "Nome" }, - "description": "Primeiro, voc\u00ea precisa baixar o aplicativo Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\n Siga as instru\u00e7\u00f5es para conectar seu Yeti \u00e0 sua rede Wi-fi. A reserva de DHCP em seu roteador \u00e9 recomendada. Se n\u00e3o estiver configurado, o dispositivo pode ficar indispon\u00edvel at\u00e9 que o Home Assistant detecte o novo endere\u00e7o IP. Consulte o manual do usu\u00e1rio do seu roteador.", + "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos.", "title": "Gol Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/ru.json b/homeassistant/components/goalzero/translations/ru.json index 52d8bbcbdc7..ca114f6d92e 100644 --- a/homeassistant/components/goalzero/translations/ru.json +++ b/homeassistant/components/goalzero/translations/ru.json @@ -20,7 +20,7 @@ "host": "\u0425\u043e\u0441\u0442", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Goal Zero: https://www.goalzero.com/product-features/yeti-app/.\n\n\u0421\u043b\u0435\u0434\u0443\u0439\u0442\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c \u043f\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044e Yeti \u043a \u0441\u0435\u0442\u0438 WiFi. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0438\u043c\u0438, \u0447\u0442\u043e\u0431\u044b IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 Home Assistant \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u043d\u043e\u0432\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441. \u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0432 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430.", + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/tr.json b/homeassistant/components/goalzero/translations/tr.json index a2c5b4f8986..9be07def514 100644 --- a/homeassistant/components/goalzero/translations/tr.json +++ b/homeassistant/components/goalzero/translations/tr.json @@ -20,7 +20,7 @@ "host": "Sunucu", "name": "Ad" }, - "description": "\u00d6ncelikle Goal Zero uygulamas\u0131n\u0131 indirmeniz gerekiyor: https://www.goalzero.com/product-features/yeti-app/ \n\n Yeti'nizi Wi-fi a\u011f\u0131n\u0131za ba\u011flamak i\u00e7in talimatlar\u0131 izleyin. Y\u00f6nlendiricinizde DHCP rezervasyonu yap\u0131lmas\u0131 \u00f6nerilir. Kurulmazsa, Home Assistant yeni ip adresini alg\u0131layana kadar cihaz kullan\u0131lamayabilir. Y\u00f6nlendiricinizin kullan\u0131m k\u0131lavuzuna bak\u0131n.", + "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n.", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goalzero/translations/zh-Hant.json b/homeassistant/components/goalzero/translations/zh-Hant.json index 13c49f8d2ac..4f5b01fb9d4 100644 --- a/homeassistant/components/goalzero/translations/zh-Hant.json +++ b/homeassistant/components/goalzero/translations/zh-Hant.json @@ -20,7 +20,7 @@ "host": "\u4e3b\u6a5f\u7aef", "name": "\u540d\u7a31" }, - "description": "\u9996\u5148\u5fc5\u9808\u5148\u4e0b\u8f09 Goal Zero app\uff1ahttps://www.goalzero.com/product-features/yeti-app/\n\n\u8ddf\u96a8\u6307\u793a\u5c07 Yeti \u9023\u7dda\u81f3\u7121\u7dda\u7db2\u8def\u3002\u5efa\u8b70\u65bc\u8def\u7531\u5668\u7684 DHCP \u8a2d\u5b9a\u4e2d\u4fdd\u7559\u56fa\u5b9a IP\uff0c\u5047\u5982\u672a\u8a2d\u5b9a\u3001\u88dd\u7f6e\u53ef\u80fd\u6703\u5728 Home Assistant \u5075\u6e2c\u5230\u65b0 IP \u4e4b\u524d\u8b8a\u6210\u7121\u6cd5\u4f7f\u7528\u3002\u8acb\u53c3\u95b1\u8def\u7531\u5668\u624b\u518a\u4e86\u89e3\u5982\u4f55\u8a2d\u5b9a\u3002", + "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002", "title": "Goal Zero Yeti" } } diff --git a/homeassistant/components/goodwe/translations/hu.json b/homeassistant/components/goodwe/translations/hu.json index 6f3fa5bbff3..e6086eab7aa 100644 --- a/homeassistant/components/goodwe/translations/hu.json +++ b/homeassistant/components/goodwe/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van" + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve" }, "error": { "connection_error": "Sikertelen csatlakoz\u00e1s" diff --git a/homeassistant/components/google/translations/bg.json b/homeassistant/components/google/translations/bg.json new file mode 100644 index 00000000000..38b08fc3616 --- /dev/null +++ b/homeassistant/components/google/translations/bg.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "create_entry": { + "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d\u043e" + }, + "step": { + "auth": { + "title": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0430\u043a\u0430\u0443\u043d\u0442 \u0432 Google" + }, + "reauth_confirm": { + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/ca.json b/homeassistant/components/google/translations/ca.json new file mode 100644 index 00000000000..829ce1413d5 --- /dev/null +++ b/homeassistant/components/google/translations/ca.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "code_expired": "El codi d'autenticaci\u00f3 ha caducat o la configuraci\u00f3 de credencials no \u00e9s v\u00e0lida. Torna-ho a provar.", + "invalid_access_token": "Token d'acc\u00e9s inv\u00e0lid", + "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", + "oauth_error": "S'han rebut dades token inv\u00e0lides.", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, + "progress": { + "exchange": "Per enlla\u00e7ar un compte de Google, v\u00e9s a [{url}]({url}) i introdueix el codi: \n\n{user_code}" + }, + "step": { + "auth": { + "title": "Vinculaci\u00f3 amb compte de Google" + }, + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" + }, + "reauth_confirm": { + "description": "La integraci\u00f3 Google Calendar ha de tornar a autenticar-se amb el teu compte", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/cs.json b/homeassistant/components/google/translations/cs.json new file mode 100644 index 00000000000..0c11a65f69b --- /dev/null +++ b/homeassistant/components/google/translations/cs.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u00da\u010det je ji\u017e nastaven", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "invalid_access_token": "Neplatn\u00fd p\u0159\u00edstupov\u00fd token", + "missing_configuration": "Komponenta nen\u00ed nastavena. Postupujte podle dokumentace.", + "oauth_error": "P\u0159ijata neplatn\u00e1 data tokenu.", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" + }, + "create_entry": { + "default": "\u00dasp\u011b\u0161n\u011b ov\u011b\u0159eno" + }, + "step": { + "pick_implementation": { + "title": "Vyberte metodu ov\u011b\u0159en\u00ed" + }, + "reauth_confirm": { + "title": "Znovu ov\u011b\u0159it integraci" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/de.json b/homeassistant/components/google/translations/de.json new file mode 100644 index 00000000000..1b15c465497 --- /dev/null +++ b/homeassistant/components/google/translations/de.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "code_expired": "Der Authentifizierungscode ist abgelaufen oder die Anmeldedaten sind ung\u00fcltig, bitte versuche es erneut.", + "invalid_access_token": "Ung\u00fcltiger Zugriffs-Token", + "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", + "oauth_error": "Ung\u00fcltige Token-Daten empfangen.", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "create_entry": { + "default": "Erfolgreich authentifiziert" + }, + "progress": { + "exchange": "Um dein Google-Konto zu verkn\u00fcpfen, besuche [{url}]({url}) und gib folgenden Code ein:\n\n{user_code}" + }, + "step": { + "auth": { + "title": "Google-Konto verkn\u00fcpfen" + }, + "pick_implementation": { + "title": "W\u00e4hle die Authentifizierungsmethode" + }, + "reauth_confirm": { + "description": "Die Google Kalender-Integration muss dein Konto erneut authentifizieren", + "title": "Integration erneut authentifizieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/el.json b/homeassistant/components/google/translations/el.json new file mode 100644 index 00000000000..11c78f96a93 --- /dev/null +++ b/homeassistant/components/google/translations/el.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "code_expired": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ad\u03bb\u03b7\u03be\u03b5 \u03ae \u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03c9\u03bd \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ac\u03ba\u03c5\u03c1\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", + "invalid_access_token": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "oauth_error": "\u039b\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03bf\u03cd.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "create_entry": { + "default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "progress": { + "exchange": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Google, \u03b5\u03c0\u03b9\u03c3\u03ba\u03b5\u03c6\u03b8\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf [{url}]({url}) \u03ba\u03b1\u03b9 \u03c0\u03bb\u03b7\u03ba\u03c4\u03c1\u03bf\u03bb\u03bf\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc:\n\n{user_code}" + }, + "step": { + "auth": { + "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd Google" + }, + "pick_implementation": { + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Nest \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03b7\u03bd \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03c3\u03b1\u03c2", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/es.json b/homeassistant/components/google/translations/es.json new file mode 100644 index 00000000000..8072ac95d4b --- /dev/null +++ b/homeassistant/components/google/translations/es.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n." + }, + "step": { + "auth": { + "title": "Vincular cuenta de Google" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/et.json b/homeassistant/components/google/translations/et.json new file mode 100644 index 00000000000..27dfa3f5290 --- /dev/null +++ b/homeassistant/components/google/translations/et.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Kasutaja on juba seadistatud", + "already_in_progress": "Seadistamine on juba k\u00e4imas", + "code_expired": "Tuvastuskood on aegunud v\u00f5i mandaadi seadistus on vale, proovi uuesti.", + "invalid_access_token": "Vigane juurdep\u00e4\u00e4sut\u00f5end", + "missing_configuration": "Komponent pole seadistatud. Palun loe dokumentatsiooni.", + "oauth_error": "Saadi sobimatud loaandmed.", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "create_entry": { + "default": "Tuvastamine \u00f5nnestus" + }, + "progress": { + "exchange": "Google'i konto linkimiseks k\u00fclasta aadressi [ {url} ]( {url} ) ja sisesta kood: \n\n {user_code}" + }, + "step": { + "auth": { + "title": "Google'i konto linkimine" + }, + "pick_implementation": { + "title": "Vali tuvastusmeetod" + }, + "reauth_confirm": { + "description": "Google'i kalendri sidumine peab konto uuesti tuvastama", + "title": "Taastuvasta sidumine" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/fr.json b/homeassistant/components/google/translations/fr.json new file mode 100644 index 00000000000..1224ba76c9b --- /dev/null +++ b/homeassistant/components/google/translations/fr.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "code_expired": "Le code d'authentification a expir\u00e9 ou la configuration des informations d'identification n'est pas valide, veuillez r\u00e9essayer.", + "invalid_access_token": "Jeton d'acc\u00e8s non valide", + "missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.", + "oauth_error": "Des donn\u00e9es de jeton non valides ont \u00e9t\u00e9 re\u00e7ues.", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "create_entry": { + "default": "Authentification r\u00e9ussie" + }, + "progress": { + "exchange": "Afin d'associer votre compte Google, rendez-vous sur [{url}]({url}) et saisissez le code suivant\u00a0:\n\n{user_code}" + }, + "step": { + "auth": { + "title": "Associer un compte Google" + }, + "pick_implementation": { + "title": "S\u00e9lectionner une m\u00e9thode d'authentification" + }, + "reauth_confirm": { + "description": "L'int\u00e9gration Google Agenda doit r\u00e9-authentifier votre compte", + "title": "R\u00e9-authentifier l'int\u00e9gration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/he.json b/homeassistant/components/google/translations/he.json new file mode 100644 index 00000000000..df5ec28163e --- /dev/null +++ b/homeassistant/components/google/translations/he.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "invalid_access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", + "oauth_error": "\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd.", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "create_entry": { + "default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4" + }, + "step": { + "auth": { + "title": "\u05e7\u05d9\u05e9\u05d5\u05e8 \u05d7\u05e9\u05d1\u05d5\u05df \u05d2\u05d5\u05d2\u05dc" + }, + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + }, + "reauth_confirm": { + "description": "\u05e9\u05d9\u05dc\u05d5\u05d1 Nest \u05e6\u05e8\u05d9\u05da \u05dc\u05d0\u05de\u05ea \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da", + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/hu.json b/homeassistant/components/google/translations/hu.json new file mode 100644 index 00000000000..2c552eca30e --- /dev/null +++ b/homeassistant/components/google/translations/hu.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "code_expired": "A hiteles\u00edt\u00e9si k\u00f3d lej\u00e1rt vagy a hiteles\u00edt\u0151 adatok be\u00e1ll\u00edt\u00e1sa \u00e9rv\u00e9nytelen, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra.", + "invalid_access_token": "\u00c9rv\u00e9nytelen hozz\u00e1f\u00e9r\u00e9si token", + "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", + "oauth_error": "\u00c9rv\u00e9nytelen token adatok \u00e9rkeztek.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "create_entry": { + "default": "Sikeres hiteles\u00edt\u00e9s" + }, + "progress": { + "exchange": "Google-fi\u00f3kja \u00f6sszekapcsol\u00e1s\u00e1hoz keresse fel a [{url}]({url}) c\u00edmet, \u00e9s \u00edrja be a k\u00f3dot: \n\n{user_code}" + }, + "step": { + "auth": { + "title": "\u00d6sszekapcsol\u00e1s Google-al" + }, + "pick_implementation": { + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" + }, + "reauth_confirm": { + "description": "A Google Napt\u00e1r integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie fi\u00f3kj\u00e1t", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/id.json b/homeassistant/components/google/translations/id.json new file mode 100644 index 00000000000..371fa7551b5 --- /dev/null +++ b/homeassistant/components/google/translations/id.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "code_expired": "Kode autentikasi kedaluwarsa atau penyiapan kredensial tidak valid, silakan coba lagi.", + "invalid_access_token": "Token akses tidak valid", + "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", + "oauth_error": "Menerima respons token yang tidak valid.", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "create_entry": { + "default": "Berhasil diautentikasi" + }, + "progress": { + "exchange": "Untuk menautkan akun Google Anda, kunjungi [{url}]({url}) dan masukkan kode:\n\n{user_code}" + }, + "step": { + "auth": { + "title": "Tautkan Akun Google" + }, + "pick_implementation": { + "title": "Pilih Metode Autentikasi" + }, + "reauth_confirm": { + "description": "Integrasi Google Kalender perlu mengautentikasi ulang akun Anda", + "title": "Autentikasi Ulang Integrasi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/it.json b/homeassistant/components/google/translations/it.json new file mode 100644 index 00000000000..4530a690ed6 --- /dev/null +++ b/homeassistant/components/google/translations/it.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "code_expired": "Il codice di autenticazione \u00e8 scaduto o la configurazione delle credenziali non \u00e8 valida, riprova.", + "invalid_access_token": "Token di accesso non valido", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", + "oauth_error": "Ricevuti dati token non validi.", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "create_entry": { + "default": "Autenticazione riuscita" + }, + "progress": { + "exchange": "Per collegare il tuo account Google, visita [{url}]({url}) e inserisci il codice: \n\n {user_code}" + }, + "step": { + "auth": { + "title": "Collega l'account Google" + }, + "pick_implementation": { + "title": "Scegli il metodo di autenticazione" + }, + "reauth_confirm": { + "description": "L'integrazione di Google Calendar deve riautenticare il tuo account", + "title": "Autentica nuovamente l'integrazione" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/ja.json b/homeassistant/components/google/translations/ja.json new file mode 100644 index 00000000000..7eab5abc6f6 --- /dev/null +++ b/homeassistant/components/google/translations/ja.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "code_expired": "\u8a8d\u8a3c\u30b3\u30fc\u30c9\u306e\u6709\u52b9\u671f\u9650\u304c\u5207\u308c\u3066\u3044\u308b\u304b\u3001\u8cc7\u683c\u60c5\u5831\u306e\u8a2d\u5b9a\u304c\u7121\u52b9\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", + "invalid_access_token": "\u7121\u52b9\u306a\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", + "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", + "oauth_error": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u30c7\u30fc\u30bf\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "create_entry": { + "default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f" + }, + "progress": { + "exchange": "Google\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ea\u30f3\u30af\u3059\u308b\u306b\u306f\u3001 [{url}]({url}) \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044: \n\n{user_code}" + }, + "step": { + "auth": { + "title": "Google\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u30ea\u30f3\u30af\u3059\u308b" + }, + "pick_implementation": { + "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" + }, + "reauth_confirm": { + "description": "Nest\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json new file mode 100644 index 00000000000..e732fd7cd19 --- /dev/null +++ b/homeassistant/components/google/translations/nl.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "already_in_progress": "Configuratie flow is al in bewerking", + "code_expired": "De authenticatiecode is verlopen of de instelling van de inloggegevens is ongeldig, probeer het opnieuw.", + "invalid_access_token": "Ongeldige toegang token", + "missing_configuration": "Het component is niet geconfigureerd. Volg a.u.b. de documentatie", + "oauth_error": "Ongeldige token data ontvangen", + "reauth_successful": "Herauthentiecatie was succesvol" + }, + "create_entry": { + "default": "Authenticatie succesvol" + }, + "progress": { + "exchange": "Om uw Google-account te koppelen, gaat u naar de [ {url} ]( {url} ) en voert u de code in: \n\n {user_code}" + }, + "step": { + "auth": { + "title": "Link Google Account" + }, + "pick_implementation": { + "title": "Kies een authenticatie methode" + }, + "reauth_confirm": { + "description": "De Google Agenda-integratie moet uw account opnieuw verifi\u00ebren", + "title": "Herauthentiseer integratie" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/no.json b/homeassistant/components/google/translations/no.json new file mode 100644 index 00000000000..4065583192c --- /dev/null +++ b/homeassistant/components/google/translations/no.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "code_expired": "Autentiseringskoden er utl\u00f8pt eller p\u00e5loggingsoppsettet er ugyldig. Pr\u00f8v p\u00e5 nytt.", + "invalid_access_token": "Ugyldig tilgangstoken", + "missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen", + "oauth_error": "Mottatt ugyldige token data.", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "create_entry": { + "default": "Vellykket godkjenning" + }, + "progress": { + "exchange": "Hvis du vil knytte sammen Google-kontoen din, g\u00e5r du til [{url}]({url}) og skriver inn kode:\n\n{user_code}" + }, + "step": { + "auth": { + "title": "Koble til Google-kontoen" + }, + "pick_implementation": { + "title": "Velg godkjenningsmetode" + }, + "reauth_confirm": { + "description": "Google Kalender-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt", + "title": "Godkjenne integrering p\u00e5 nytt" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/pl.json b/homeassistant/components/google/translations/pl.json new file mode 100644 index 00000000000..b4f45c0abe4 --- /dev/null +++ b/homeassistant/components/google/translations/pl.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "code_expired": "Kod uwierzytelniaj\u0105cy wygas\u0142 lub konfiguracja po\u015bwiadcze\u0144 jest nieprawid\u0142owa, spr\u00f3buj ponownie.", + "invalid_access_token": "Niepoprawny token dost\u0119pu", + "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", + "oauth_error": "Otrzymano nieprawid\u0142owe dane tokena.", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, + "progress": { + "exchange": "Aby po\u0142\u0105czy\u0107 swoje konto Google, odwied\u017a [{url}]({url}) i wpisz kod: \n\n{user_code}" + }, + "step": { + "auth": { + "title": "Po\u0142\u0105czenie z kontem Google" + }, + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" + }, + "reauth_confirm": { + "description": "Integracja Kalendarz Google wymaga ponownego uwierzytelnienia Twojego konta", + "title": "Ponownie uwierzytelnij integracj\u0119" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/pt-BR.json b/homeassistant/components/google/translations/pt-BR.json new file mode 100644 index 00000000000..8ab124f1b5c --- /dev/null +++ b/homeassistant/components/google/translations/pt-BR.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "code_expired": "O c\u00f3digo de autentica\u00e7\u00e3o expirou ou a configura\u00e7\u00e3o da credencial \u00e9 inv\u00e1lida. Tente novamente.", + "invalid_access_token": "Token de acesso inv\u00e1lido", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "oauth_error": "Dados de token recebidos inv\u00e1lidos.", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "create_entry": { + "default": "Autenticado com sucesso" + }, + "progress": { + "exchange": "Para vincular sua conta do Google, visite o [{url}]({url}) e insira o c\u00f3digo: \n\n {user_code}" + }, + "step": { + "auth": { + "title": "Vincular Conta do Google" + }, + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o do Google Agenda precisa autenticar novamente sua conta", + "title": "Reautenticar Integra\u00e7\u00e3o" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/ru.json b/homeassistant/components/google/translations/ru.json new file mode 100644 index 00000000000..a7db4b2f48b --- /dev/null +++ b/homeassistant/components/google/translations/ru.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "code_expired": "\u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043a\u043e\u0434\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u0441\u0442\u0435\u043a \u0438\u043b\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u043e, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443.", + "invalid_access_token": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430.", + "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", + "oauth_error": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "progress": { + "exchange": "\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Google, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 [{url}]({url}) \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434: \n\n{user_code}" + }, + "step": { + "auth": { + "title": "\u0423\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Google" + }, + "pick_implementation": { + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + }, + "reauth_confirm": { + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Google.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/tr.json b/homeassistant/components/google/translations/tr.json new file mode 100644 index 00000000000..9d5fc8d2416 --- /dev/null +++ b/homeassistant/components/google/translations/tr.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "code_expired": "Kimlik do\u011frulama kodunun s\u00fcresi doldu veya kimlik bilgisi kurulumu ge\u00e7ersiz, l\u00fctfen tekrar deneyin.", + "invalid_access_token": "Ge\u00e7ersiz eri\u015fim anahtar\u0131", + "missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.", + "oauth_error": "Ge\u00e7ersiz anahtar verileri al\u0131nd\u0131.", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "create_entry": { + "default": "Ba\u015far\u0131yla do\u011fruland\u0131" + }, + "progress": { + "exchange": "Google hesab\u0131n\u0131z\u0131 ba\u011flamak i\u00e7in [ {url} ]( {url} ) adresini ziyaret edin ve kodu girin: \n\n {user_code}" + }, + "step": { + "auth": { + "title": "Google Hesab\u0131n\u0131 Ba\u011fla" + }, + "pick_implementation": { + "title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7" + }, + "reauth_confirm": { + "description": "Google Takvim entegrasyonunun hesab\u0131n\u0131z\u0131 yeniden do\u011frulamas\u0131 gerekiyor", + "title": "Entegrasyonu Yeniden Do\u011frula" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/zh-Hant.json b/homeassistant/components/google/translations/zh-Hant.json new file mode 100644 index 00000000000..70e7d81c01e --- /dev/null +++ b/homeassistant/components/google/translations/zh-Hant.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "code_expired": "\u8a8d\u8b49\u78bc\u5df2\u904e\u671f\u6216\u6191\u8b49\u8a2d\u5b9a\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002", + "invalid_access_token": "\u5b58\u53d6\u6b0a\u6756\u7121\u6548", + "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", + "oauth_error": "\u6536\u5230\u7121\u6548\u7684\u6b0a\u6756\u8cc7\u6599\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, + "progress": { + "exchange": "\u6b32\u9023\u7d50 Google \u5e33\u865f\u3001\u8acb\u700f\u89bd [{url}]({url}) \u4e26\u8f38\u5165\u4ee3\u78bc\uff1a\n{user_code}" + }, + "step": { + "auth": { + "title": "\u9023\u7d50 Google \u5e33\u865f" + }, + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" + }, + "reauth_confirm": { + "description": "Google Calendar \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/hu.json b/homeassistant/components/google_travel_time/translations/hu.json index 85a15a98e58..03cfdc18b59 100644 --- a/homeassistant/components/google_travel_time/translations/hu.json +++ b/homeassistant/components/google_travel_time/translations/hu.json @@ -11,7 +11,7 @@ "data": { "api_key": "Api kucs", "destination": "C\u00e9l", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "origin": "Eredet" }, "description": "Az eredet \u00e9s a c\u00e9l megad\u00e1sakor megadhat egy vagy t\u00f6bb helyet a pipa karakterrel elv\u00e1lasztva, c\u00edm, sz\u00e9less\u00e9gi / hossz\u00fas\u00e1gi koordin\u00e1t\u00e1k vagy Google helyazonos\u00edt\u00f3 form\u00e1j\u00e1ban. Amikor a helyet megadja egy Google helyazonos\u00edt\u00f3val, akkor az azonos\u00edt\u00f3t el\u0151taggal kell ell\u00e1tni a `hely_azonos\u00edt\u00f3:` sz\u00f3val." diff --git a/homeassistant/components/group/translations/bg.json b/homeassistant/components/group/translations/bg.json index d28a170bcd0..584d48e3f28 100644 --- a/homeassistant/components/group/translations/bg.json +++ b/homeassistant/components/group/translations/bg.json @@ -1,9 +1,116 @@ { "config": { "step": { + "binary_sensor": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435", + "name": "\u0418\u043c\u0435" + }, + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + }, + "cover": { + "data": { + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + } + }, + "fan": { + "data": { + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + } + }, + "light": { + "data": { + "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + } + }, + "lock": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435", + "name": "\u0418\u043c\u0435" + } + }, "media_player": { "data": { - "name": "\u0418\u043c\u0435 \u043d\u0430 \u0433\u0440\u0443\u043f\u0430" + "name": "\u0418\u043c\u0435 \u043d\u0430 \u0433\u0440\u0443\u043f\u0430", + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + } + }, + "switch": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435", + "name": "\u0418\u043c\u0435" + }, + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + }, + "user": { + "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + } + } + }, + "options": { + "step": { + "binary_sensor": { + "data": { + "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "binary_sensor_options": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "cover": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "cover_options": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "fan": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "fan_options": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "light": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "light_options": { + "data": { + "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "lock": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "media_player": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "media_player_options": { + "data": { + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" + } + }, + "switch": { + "data": { + "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", + "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } } } diff --git a/homeassistant/components/group/translations/ca.json b/homeassistant/components/group/translations/ca.json index b17d98b5084..bd824fc7428 100644 --- a/homeassistant/components/group/translations/ca.json +++ b/homeassistant/components/group/translations/ca.json @@ -5,18 +5,21 @@ "data": { "all": "Totes les entitats", "entities": "Membres", + "hide_members": "Amaga membres", "name": "Nom" }, "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre.", - "title": "Nou grup" + "title": "Afegeix grup" }, "cover": { "data": { "entities": "Membres", + "hide_members": "Amaga membres", "name": "Nom", - "title": "Nou grup" + "title": "Afegeix grup" }, - "description": "Selecciona les opcions del grup" + "description": "Selecciona les opcions del grup", + "title": "Afegeix grup" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Membres", + "hide_members": "Amaga membres", "name": "Nom", - "title": "Nou grup" + "title": "Afegeix grup" }, - "description": "Selecciona les opcions del grup" + "description": "Selecciona les opcions del grup", + "title": "Afegeix grup" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Totes les entitats", "entities": "Membres", + "hide_members": "Amaga membres", "name": "Nom", - "title": "Nou grup" + "title": "Afegeix grup" }, - "description": "Selecciona les opcions del grup" + "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre.", + "title": "Afegeix grup" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Selecciona les opcions del grup" }, + "lock": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres", + "name": "Nom" + }, + "title": "Afegeix grup" + }, "media_player": { "data": { "entities": "Membres", + "hide_members": "Amaga membres", "name": "Nom", - "title": "Nou grup" + "title": "Afegeix grup" }, - "description": "Selecciona les opcions del grup" + "description": "Selecciona les opcions del grup", + "title": "Afegeix grup" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Selecciona les opcions del grup" }, + "switch": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres", + "name": "Nom" + }, + "title": "Afegeix grup" + }, "user": { "data": { "group_type": "Tipus de grup" }, - "title": "Nou grup" + "description": "Els grups et permeten crear una nova entitat que representa m\u00faltiples entitats del mateix tipus.", + "menu_options": { + "binary_sensor": "Grup de sensors binaris", + "cover": "Grup de cobertes", + "fan": "Grup de ventiladors", + "light": "Grup de llums", + "lock": "Grup de panys", + "media_player": "Grup de reproductors multim\u00e8dia", + "switch": "Grup de commutadors" + }, + "title": "Afegeix grup" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Totes les entitats", + "entities": "Membres", + "hide_members": "Amaga membres" + }, + "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre." + }, "binary_sensor_options": { "data": { "all": "Totes les entitats", "entities": "Membres" } }, + "cover": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres" + } + }, "cover_options": { "data": { "entities": "Membres" } }, + "fan": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres" + } + }, "fan_options": { "data": { "entities": "Membres" } }, + "light": { + "data": { + "all": "Totes les entitats", + "entities": "Membres", + "hide_members": "Amaga membres" + }, + "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre." + }, "light_options": { "data": { + "all": "Totes les entitats", "entities": "Membres" } }, + "lock": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres" + } + }, + "media_player": { + "data": { + "entities": "Membres", + "hide_members": "Amaga membres" + } + }, "media_player_options": { "data": { "entities": "Membres" } + }, + "switch": { + "data": { + "all": "Totes les entitats", + "entities": "Membres", + "hide_members": "Amaga membres" + }, + "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre." } } }, diff --git a/homeassistant/components/group/translations/cs.json b/homeassistant/components/group/translations/cs.json index d35e48058e7..232dbe9e629 100644 --- a/homeassistant/components/group/translations/cs.json +++ b/homeassistant/components/group/translations/cs.json @@ -1,4 +1,200 @@ { + "config": { + "step": { + "binary_sensor": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no" + }, + "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen.", + "title": "Nov\u00e1 skupina" + }, + "cover": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no", + "title": "Nov\u00e1 skupina" + }, + "description": "Vyberte mo\u017enosti skupiny", + "title": "Nov\u00e1 skupina" + }, + "cover_options": { + "data": { + "entities": "\u010clenov\u00e9 skupiny" + }, + "description": "Vyberte mo\u017enosti skupiny" + }, + "fan": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no", + "title": "Nov\u00e1 skupina" + }, + "description": "Vyberte mo\u017enosti skupiny", + "title": "Nov\u00e1 skupina" + }, + "fan_options": { + "data": { + "entities": "\u010clenov\u00e9 skupiny" + }, + "description": "Vyberte mo\u017enosti skupiny" + }, + "init": { + "data": { + "group_type": "Typ skupiny" + }, + "description": "Vyberte typ skupiny" + }, + "light": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no", + "title": "Nov\u00e1 skupina" + }, + "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen.", + "title": "Nov\u00e1 skupina" + }, + "light_options": { + "data": { + "entities": "\u010clenov\u00e9 skupiny" + }, + "description": "Vyberte mo\u017enosti skupiny" + }, + "lock": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no" + }, + "title": "Nov\u00e1 skupina" + }, + "media_player": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no", + "title": "Nov\u00e1 skupina" + }, + "description": "Vyberte mo\u017enosti skupiny", + "title": "Nov\u00e1 skupina" + }, + "media_player_options": { + "data": { + "entities": "\u010clenov\u00e9 skupiny" + }, + "description": "Vyberte mo\u017enosti skupiny" + }, + "switch": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny", + "name": "Jm\u00e9no" + }, + "title": "Nov\u00e1 skupina" + }, + "user": { + "data": { + "group_type": "Typ skupiny" + }, + "description": "Vyberte typ skupiny", + "menu_options": { + "binary_sensor": "Skupina bin\u00e1rn\u00edch senzor\u016f", + "cover": "Skupina rolet", + "fan": "Skupina ventil\u00e1tor\u016f", + "light": "Skupina sv\u011btel", + "lock": "Skupina z\u00e1mk\u016f", + "media_player": "Skupina p\u0159ehr\u00e1va\u010d\u016f m\u00e9di\u00ed", + "switch": "Skupina vyp\u00edna\u010d\u016f" + }, + "title": "Nov\u00e1 skupina" + } + } + }, + "options": { + "step": { + "binary_sensor": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + }, + "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen." + }, + "binary_sensor_options": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9" + } + }, + "cover": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + } + }, + "cover_options": { + "data": { + "entities": "\u010clenov\u00e9" + } + }, + "fan": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + } + }, + "fan_options": { + "data": { + "entities": "\u010clenov\u00e9" + } + }, + "light": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + }, + "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen." + }, + "light_options": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9" + } + }, + "lock": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + } + }, + "media_player": { + "data": { + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + } + }, + "media_player_options": { + "data": { + "entities": "\u010clenov\u00e9" + } + }, + "switch": { + "data": { + "all": "V\u0161echny entity", + "entities": "\u010clenov\u00e9", + "hide_members": "Skr\u00fdt \u010dleny" + }, + "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen." + } + } + }, "state": { "_": { "closed": "Zav\u0159eno", diff --git a/homeassistant/components/group/translations/de.json b/homeassistant/components/group/translations/de.json index c06dd2a4a8f..aeaedd3a7cc 100644 --- a/homeassistant/components/group/translations/de.json +++ b/homeassistant/components/group/translations/de.json @@ -5,18 +5,21 @@ "data": { "all": "Alle Entit\u00e4ten", "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", "name": "Name" }, "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist.", - "title": "Neue Gruppe" + "title": "Gruppe hinzuf\u00fcgen" }, "cover": { "data": { "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", "name": "Name", - "title": "Neue Gruppe" + "title": "Gruppe hinzuf\u00fcgen" }, - "description": "Gruppenoptionen ausw\u00e4hlen" + "description": "Gruppenoptionen ausw\u00e4hlen", + "title": "Gruppe hinzuf\u00fcgen" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", "name": "Name", - "title": "Neue Gruppe" + "title": "Gruppe hinzuf\u00fcgen" }, - "description": "Gruppenoptionen ausw\u00e4hlen" + "description": "Gruppenoptionen ausw\u00e4hlen", + "title": "Gruppe hinzuf\u00fcgen" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Alle Entit\u00e4ten", "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", "name": "Name", - "title": "Neue Gruppe" + "title": "Gruppe hinzuf\u00fcgen" }, - "description": "Gruppenoptionen ausw\u00e4hlen" + "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist.", + "title": "Gruppe hinzuf\u00fcgen" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Gruppenoptionen ausw\u00e4hlen" }, + "lock": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", + "name": "Name" + }, + "title": "Gruppe hinzuf\u00fcgen" + }, "media_player": { "data": { "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", "name": "Name", - "title": "Neue Gruppe" + "title": "Gruppe hinzuf\u00fcgen" }, - "description": "Gruppenoptionen ausw\u00e4hlen" + "description": "Gruppenoptionen ausw\u00e4hlen", + "title": "Gruppe hinzuf\u00fcgen" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Gruppenoptionen ausw\u00e4hlen" }, + "switch": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden", + "name": "Name" + }, + "title": "Gruppe hinzuf\u00fcgen" + }, "user": { "data": { "group_type": "Gruppentyp" }, - "title": "Neue Gruppe" + "description": "Mit Gruppen kannst du eine neue Entit\u00e4t erstellen, die mehrere Entit\u00e4ten desselben Typs darstellt.", + "menu_options": { + "binary_sensor": "Bin\u00e4rer Sensor-Gruppe", + "cover": "Abdeckung-Gruppe", + "fan": "L\u00fcfter-Gruppe", + "light": "Licht-Gruppe", + "lock": "Gruppe sperren", + "media_player": "Media-Player-Gruppe", + "switch": "Gruppe wechseln" + }, + "title": "Gruppe hinzuf\u00fcgen" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Alle Entit\u00e4ten", + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + }, + "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist." + }, "binary_sensor_options": { "data": { "all": "Alle Entit\u00e4ten", "entities": "Mitglieder" } }, + "cover": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + } + }, "cover_options": { "data": { "entities": "Mitglieder" } }, + "fan": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + } + }, "fan_options": { "data": { "entities": "Mitglieder" } }, + "light": { + "data": { + "all": "Alle Entit\u00e4ten", + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + }, + "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist." + }, "light_options": { "data": { + "all": "Alle Entit\u00e4ten", "entities": "Mitglieder" } }, + "lock": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + } + }, + "media_player": { + "data": { + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + } + }, "media_player_options": { "data": { "entities": "Mitglieder" } + }, + "switch": { + "data": { + "all": "Alle Entit\u00e4ten", + "entities": "Mitglieder", + "hide_members": "Mitglieder ausblenden" + }, + "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist." } } }, diff --git a/homeassistant/components/group/translations/el.json b/homeassistant/components/group/translations/el.json index bebfb8f1cb8..edd67034de8 100644 --- a/homeassistant/components/group/translations/el.json +++ b/homeassistant/components/group/translations/el.json @@ -5,6 +5,7 @@ "data": { "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf.", @@ -13,10 +14,12 @@ "cover": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, + "lock": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + }, + "title": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + }, "media_player": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, + "switch": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + }, + "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + }, "user": { "data": { "group_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, - "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03cd\u03c0\u03bf \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", + "menu_options": { + "binary_sensor": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03b4\u03c5\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd", + "cover": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03ba\u03b1\u03bb\u03c5\u03bc\u03bc\u03ac\u03c4\u03c9\u03bd", + "fan": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03b1\u03bd\u03b5\u03bc\u03b9\u03c3\u03c4\u03ae\u03c1\u03c9\u03bd", + "light": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c6\u03ce\u03c4\u03c9\u03bd", + "lock": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2", + "media_player": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd", + "switch": "\u0395\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" + }, + "title": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + }, + "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf." + }, "binary_sensor_options": { "data": { "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", "entities": "\u039c\u03ad\u03bb\u03b7" } }, + "cover": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + } + }, "cover_options": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7" } }, + "fan": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + } + }, "fan_options": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7" } }, + "light": { + "data": { + "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + }, + "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf." + }, "light_options": { "data": { + "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", "entities": "\u039c\u03ad\u03bb\u03b7" } }, + "lock": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + } + }, + "media_player": { + "data": { + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + } + }, "media_player_options": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7" } + }, + "switch": { + "data": { + "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", + "entities": "\u039c\u03ad\u03bb\u03b7", + "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" + }, + "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf." } } }, diff --git a/homeassistant/components/group/translations/en.json b/homeassistant/components/group/translations/en.json index a67d23b812d..f7b3942c696 100644 --- a/homeassistant/components/group/translations/en.json +++ b/homeassistant/components/group/translations/en.json @@ -15,26 +15,57 @@ "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name" + "name": "Name", + "title": "Add Group" }, + "description": "Select group options", "title": "Add Group" }, + "cover_options": { + "data": { + "entities": "Group members" + }, + "description": "Select group options" + }, "fan": { "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name" + "name": "Name", + "title": "Add Group" }, + "description": "Select group options", "title": "Add Group" }, + "fan_options": { + "data": { + "entities": "Group members" + }, + "description": "Select group options" + }, + "init": { + "data": { + "group_type": "Group type" + }, + "description": "Select group type" + }, "light": { "data": { + "all": "All entities", "entities": "Members", "hide_members": "Hide members", - "name": "Name" + "name": "Name", + "title": "Add Group" }, + "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on.", "title": "Add Group" }, + "light_options": { + "data": { + "entities": "Group members" + }, + "description": "Select group options" + }, "lock": { "data": { "entities": "Members", @@ -47,10 +78,18 @@ "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name" + "name": "Name", + "title": "Add Group" }, + "description": "Select group options", "title": "Add Group" }, + "media_player_options": { + "data": { + "entities": "Group members" + }, + "description": "Select group options" + }, "switch": { "data": { "entities": "Members", @@ -60,6 +99,9 @@ "title": "Add Group" }, "user": { + "data": { + "group_type": "Group type" + }, "description": "Groups allow you to create a new entity that represents multiple entities of the same type.", "menu_options": { "binary_sensor": "Binary sensor group", @@ -84,18 +126,34 @@ }, "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on." }, + "binary_sensor_options": { + "data": { + "all": "All entities", + "entities": "Members" + } + }, "cover": { "data": { "entities": "Members", "hide_members": "Hide members" } }, + "cover_options": { + "data": { + "entities": "Members" + } + }, "fan": { "data": { "entities": "Members", "hide_members": "Hide members" } }, + "fan_options": { + "data": { + "entities": "Members" + } + }, "light": { "data": { "all": "All entities", @@ -104,6 +162,12 @@ }, "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on." }, + "light_options": { + "data": { + "all": "All entities", + "entities": "Members" + } + }, "lock": { "data": { "entities": "Members", @@ -116,6 +180,11 @@ "hide_members": "Hide members" } }, + "media_player_options": { + "data": { + "entities": "Members" + } + }, "switch": { "data": { "all": "All entities", diff --git a/homeassistant/components/group/translations/et.json b/homeassistant/components/group/translations/et.json index c45094aeedd..d0d1466e26b 100644 --- a/homeassistant/components/group/translations/et.json +++ b/homeassistant/components/group/translations/et.json @@ -5,6 +5,7 @@ "data": { "all": "K\u00f5ik olemid", "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", "name": "Nimi" }, "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud.", @@ -13,10 +14,12 @@ "cover": { "data": { "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", "name": "Nimi", "title": "Uus r\u00fchm" }, - "description": "R\u00fchmasuvandite valimine" + "description": "R\u00fchmasuvandite valimine", + "title": "Uus grupp" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", "name": "Nimi", "title": "Uus r\u00fchm" }, - "description": "R\u00fchmasuvandite valimine" + "description": "R\u00fchmasuvandite valimine", + "title": "Uus grupp" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "K\u00f5ik olemid", "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", "name": "Nimi", "title": "Uus r\u00fchm" }, - "description": "R\u00fchmasuvandite valimine" + "description": "Kui on vlitud \"K\u00f5ik olemid\" siis on grupi olek Sees kui k\u00f5ik olemid on sees. Kui \"K\u00f5ik olemid\" on keelatud siis on grupi olek Sees kui m\u00f5ni olem on sisse l\u00fclitatud.", + "title": "Uus grupp" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "R\u00fchmasuvandite valimine" }, + "lock": { + "data": { + "entities": "Liikmed", + "hide_members": "Peida liikmed", + "name": "Nimi" + }, + "title": "Uus grupp" + }, "media_player": { "data": { "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", "name": "Nimi", "title": "Uus r\u00fchm" }, - "description": "R\u00fchmasuvandite valimine" + "description": "R\u00fchmasuvandite valimine", + "title": "Uus grupp" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "R\u00fchmasuvandite valimine" }, + "switch": { + "data": { + "entities": "Liikmed", + "hide_members": "Peida grupi liikmed", + "name": "Nimi" + }, + "title": "Uus grupp" + }, "user": { "data": { "group_type": "R\u00fchma t\u00fc\u00fcp" }, - "title": "Uus r\u00fchm" + "description": "R\u00fchmad v\u00f5imaldavad luua uue olemi,mis esindab mitut sama t\u00fc\u00fcpi olemit.", + "menu_options": { + "binary_sensor": "Olekuandurite r\u00fchm", + "cover": "Aknakatete r\u00fchm", + "fan": "Ventilaatorite r\u00fchm", + "light": "Valgustite r\u00fchm", + "lock": "Lukusta grupp", + "media_player": "Meediumipleieri r\u00fchm", + "switch": "Grupi vahetamine" + }, + "title": "Lisa grupp" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "K\u00f5ik olemid", + "entities": "Grupi liikmed", + "hide_members": "Peida grupi liikmed" + }, + "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud." + }, "binary_sensor_options": { "data": { "all": "K\u00f5ik olemid", "entities": "Liikmed" } }, + "cover": { + "data": { + "entities": "Grupi liikmed", + "hide_members": "Peida grupi liikmed" + } + }, "cover_options": { "data": { "entities": "Liikmed" } }, + "fan": { + "data": { + "entities": "Grupi liikmed", + "hide_members": "Peida grupi liikmed" + } + }, "fan_options": { "data": { "entities": "Liikmed" } }, + "light": { + "data": { + "all": "K\u00f5ik olemid", + "entities": "Grupi liikmed", + "hide_members": "Peida grupi liikmed" + }, + "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud." + }, "light_options": { "data": { + "all": "K\u00f5ik olemid", "entities": "Liikmed" } }, + "lock": { + "data": { + "entities": "Liikmed", + "hide_members": "Peida liikmed" + } + }, + "media_player": { + "data": { + "entities": "Grupi liikmed", + "hide_members": "Peida grupi liikmed" + } + }, "media_player_options": { "data": { "entities": "Liikmed" } + }, + "switch": { + "data": { + "all": "K\u00f5ik olemid", + "entities": "Liikmed", + "hide_members": "Peida grupi liikmed" + }, + "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud." } } }, diff --git a/homeassistant/components/group/translations/fr.json b/homeassistant/components/group/translations/fr.json index 8e8b5ee4233..e8b74e2f374 100644 --- a/homeassistant/components/group/translations/fr.json +++ b/homeassistant/components/group/translations/fr.json @@ -5,18 +5,21 @@ "data": { "all": "Toutes les entit\u00e9s", "entities": "Membres", + "hide_members": "Cacher les membres", "name": "Nom" }, "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9.", - "title": "Nouveau groupe" + "title": "Ajouter un groupe" }, "cover": { "data": { "entities": "Membres", + "hide_members": "Cacher les membres", "name": "Nom", - "title": "Nouveau groupe" + "title": "Ajouter un groupe" }, - "description": "S\u00e9lectionnez les options du groupe" + "description": "S\u00e9lectionnez les options du groupe", + "title": "Ajouter un groupe" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Membres", + "hide_members": "Cacher les membres", "name": "Nom", - "title": "Nouveau groupe" + "title": "Ajouter un groupe" }, - "description": "S\u00e9lectionnez les options du groupe" + "description": "S\u00e9lectionnez les options du groupe", + "title": "Ajouter un groupe" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Toutes les entit\u00e9s", "entities": "Membres", + "hide_members": "Cacher les membres", "name": "Nom", - "title": "Nouveau groupe" + "title": "Ajouter un groupe" }, - "description": "S\u00e9lectionnez les options du groupe" + "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9.", + "title": "Ajouter un groupe" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "S\u00e9lectionnez les options du groupe" }, + "lock": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres", + "name": "Nom" + }, + "title": "Ajouter un groupe" + }, "media_player": { "data": { "entities": "Membres", + "hide_members": "Cacher les membres", "name": "Nom", - "title": "Nouveau groupe" + "title": "Ajouter un groupe" }, - "description": "S\u00e9lectionnez les options du groupe" + "description": "S\u00e9lectionnez les options du groupe", + "title": "Ajouter un groupe" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "S\u00e9lectionnez les options du groupe" }, + "switch": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres", + "name": "Nom" + }, + "title": "Ajouter un groupe" + }, "user": { "data": { "group_type": "Type de groupe" }, - "title": "Nouveau groupe" + "description": "Les groupes vous permettent de cr\u00e9er une nouvelle entit\u00e9 repr\u00e9sentant plusieurs entit\u00e9s d'un m\u00eame type.", + "menu_options": { + "binary_sensor": "Groupe de capteurs binaires", + "cover": "Groupe de fermetures", + "fan": "Groupe de ventilateurs", + "light": "Groupe de lumi\u00e8res", + "lock": "Groupe de verrous", + "media_player": "Groupe de lecteurs multim\u00e9dia", + "switch": "Groupe d'interrupteurs" + }, + "title": "Ajouter un groupe" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Toutes les entit\u00e9s", + "entities": "Membres", + "hide_members": "Cacher les membres" + }, + "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9." + }, "binary_sensor_options": { "data": { "all": "Toutes les entit\u00e9s", "entities": "Membres" } }, + "cover": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres" + } + }, "cover_options": { "data": { "entities": "Membres" } }, + "fan": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres" + } + }, "fan_options": { "data": { "entities": "Membres" } }, + "light": { + "data": { + "all": "Toutes les entit\u00e9s", + "entities": "Membres", + "hide_members": "Cacher les membres" + }, + "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9." + }, "light_options": { "data": { + "all": "Toutes les entit\u00e9s", "entities": "Membres" } }, + "lock": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres" + } + }, + "media_player": { + "data": { + "entities": "Membres", + "hide_members": "Cacher les membres" + } + }, "media_player_options": { "data": { "entities": "Membres" } + }, + "switch": { + "data": { + "all": "Toutes les entit\u00e9s", + "entities": "Membres", + "hide_members": "Cacher les membres" + }, + "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9." } } }, diff --git a/homeassistant/components/group/translations/he.json b/homeassistant/components/group/translations/he.json index 06c1a8e0fa8..ab7a90cb699 100644 --- a/homeassistant/components/group/translations/he.json +++ b/homeassistant/components/group/translations/he.json @@ -5,6 +5,7 @@ "data": { "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", "name": "\u05e9\u05dd" }, "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc.", @@ -13,10 +14,12 @@ "cover": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", "name": "\u05e9\u05dd", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", "name": "\u05e9\u05dd", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", "name": "\u05e9\u05dd", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" + "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc.", + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" }, + "lock": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", + "name": "\u05e9\u05dd" + }, + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + }, "media_player": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", "name": "\u05e9\u05dd", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" + "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, "media_player_options": { "data": { @@ -72,47 +90,112 @@ }, "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" }, + "switch": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", + "name": "\u05e9\u05dd" + }, + "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + }, "user": { "data": { "group_type": "\u05e1\u05d5\u05d2 \u05e7\u05d1\u05d5\u05e6\u05d4" }, + "description": "\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05de\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05da \u05dc\u05d9\u05e6\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05d7\u05d3\u05e9\u05d4 \u05d4\u05de\u05d9\u05d9\u05e6\u05d2\u05ea \u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05de\u05e8\u05d5\u05d1\u05d5\u05ea \u05de\u05d0\u05d5\u05ea\u05d5 \u05e1\u05d5\u05d2.", + "menu_options": { + "binary_sensor": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d7\u05d9\u05d9\u05e9\u05e0\u05d9\u05dd \u05d1\u05d9\u05e0\u05d0\u05e8\u05d9\u05d9\u05dd", + "cover": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d5\u05d9\u05dc\u05d5\u05e0\u05d5\u05ea", + "fan": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d0\u05d9\u05d5\u05d5\u05e8\u05d5\u05e8", + "light": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05ea\u05d0\u05d5\u05e8\u05d4", + "media_player": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05e0\u05d2\u05e0\u05d9 \u05de\u05d3\u05d9\u05d4" + }, "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + }, + "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc." + }, "binary_sensor_options": { "data": { "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" } }, + "cover": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + } + }, "cover_options": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" } }, + "fan": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + } + }, "fan_options": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" } }, + "light": { + "data": { + "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + }, + "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc." + }, "light_options": { "data": { + "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" } }, + "lock": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + } + }, + "media_player": { + "data": { + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + } + }, "media_player_options": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" } + }, + "switch": { + "data": { + "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", + "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", + "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" + }, + "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc." } } }, "state": { "_": { - "closed": "\u05e1\u05d2\u05d5\u05e8", + "closed": "\u05e0\u05e1\u05d2\u05e8", "home": "\u05d1\u05d1\u05d9\u05ea", "locked": "\u05e0\u05e2\u05d5\u05dc", "not_home": "\u05d1\u05d7\u05d5\u05e5", diff --git a/homeassistant/components/group/translations/hu.json b/homeassistant/components/group/translations/hu.json index 08c7d5e5afb..ba368532cff 100644 --- a/homeassistant/components/group/translations/hu.json +++ b/homeassistant/components/group/translations/hu.json @@ -5,18 +5,21 @@ "data": { "all": "Minden entit\u00e1s", "entities": "A csoport tagjai", - "name": "N\u00e9v" + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s" }, "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van.", - "title": "\u00daj csoport" + "title": "Csoport hozz\u00e1ad\u00e1sa" }, "cover": { "data": { "entities": "A csoport tagjai", - "name": "Csoport neve", - "title": "\u00daj csoport" + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" + "description": "Csoport be\u00e1ll\u00edt\u00e1sai", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "A csoport tagjai", - "name": "A csoport elnevez\u00e9se", - "title": "\u00daj csoport" + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" + "description": "Csoport be\u00e1ll\u00edt\u00e1sai", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Minden entit\u00e1s", "entities": "A csoport tagjai", - "name": "A csoport elnevez\u00e9se", - "title": "\u00daj csoport" + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" + "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van.", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Csoport be\u00e1ll\u00edt\u00e1sai" }, + "lock": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s" + }, + "title": "Csoport hozz\u00e1ad\u00e1sa" + }, "media_player": { "data": { "entities": "A csoport tagjai", - "name": "A csoport elnevez\u00e9se", - "title": "\u00daj csoport" + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" + "description": "Csoport be\u00e1ll\u00edt\u00e1sai", + "title": "Csoport hozz\u00e1ad\u00e1sa" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Csoport be\u00e1ll\u00edt\u00e1sai" }, + "switch": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se", + "name": "Elnevez\u00e9s" + }, + "title": "Csoport hozz\u00e1ad\u00e1sa" + }, "user": { "data": { "group_type": "Csoport t\u00edpusa" }, - "title": "\u00daj csoport" + "description": "A csoportok lehet\u0151v\u00e9 teszik egy \u00faj entit\u00e1s l\u00e9trehoz\u00e1s\u00e1t, amely t\u00f6bb azonos t\u00edpus\u00fa entit\u00e1st k\u00e9pvisel.", + "menu_options": { + "binary_sensor": "Bin\u00e1ris \u00e9rz\u00e9kel\u0151 csoport", + "cover": "Red\u0151ny csoport", + "fan": "Ventil\u00e1tor csoport", + "light": "L\u00e1mpa csoport", + "lock": "Csoport z\u00e1rol\u00e1sa", + "media_player": "M\u00e9dialej\u00e1tsz\u00f3 csoport", + "switch": "Kapcsol\u00f3csoport" + }, + "title": "Csoport hozz\u00e1ad\u00e1sa" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Minden entit\u00e1s", + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + }, + "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van." + }, "binary_sensor_options": { "data": { "all": "Minden entit\u00e1s", "entities": "A csoport tagjai" } }, + "cover": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + } + }, "cover_options": { "data": { "entities": "A csoport tagjai" } }, + "fan": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + } + }, "fan_options": { "data": { "entities": "A csoport tagjai" } }, + "light": { + "data": { + "all": "Minden entit\u00e1s", + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + }, + "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van." + }, "light_options": { "data": { + "all": "Minden entit\u00e1s", "entities": "A csoport tagjai" } }, + "lock": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + } + }, + "media_player": { + "data": { + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + } + }, "media_player_options": { "data": { "entities": "A csoport tagjai" } + }, + "switch": { + "data": { + "all": "Minden entit\u00e1s", + "entities": "A csoport tagjai", + "hide_members": "Tagok elrejt\u00e9se" + }, + "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van." } } }, diff --git a/homeassistant/components/group/translations/id.json b/homeassistant/components/group/translations/id.json index 2a92526d644..c0425c5ede6 100644 --- a/homeassistant/components/group/translations/id.json +++ b/homeassistant/components/group/translations/id.json @@ -5,18 +5,21 @@ "data": { "all": "Semua entitas", "entities": "Anggota", + "hide_members": "Sembunyikan anggota", "name": "Nama" }, "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala.", - "title": "Grup Baru" + "title": "Tambahkan Grup" }, "cover": { "data": { "entities": "Anggota", + "hide_members": "Sembunyikan anggota", "name": "Nama", - "title": "Grup Baru" + "title": "Tambahkan Grup" }, - "description": "Pilih opsi grup" + "description": "Pilih opsi grup", + "title": "Tambahkan Grup" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Anggota", + "hide_members": "Sembunyikan anggota", "name": "Nama", - "title": "Grup Baru" + "title": "Tambahkan Grup" }, - "description": "Pilih opsi grup" + "description": "Pilih opsi grup", + "title": "Tambahkan Grup" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Semua entitas", "entities": "Anggota", + "hide_members": "Sembunyikan anggota", "name": "Nama", - "title": "Grup Baru" + "title": "Tambahkan Grup" }, - "description": "Pilih opsi grup" + "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala.", + "title": "Tambahkan Grup" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Pilih opsi grup" }, + "lock": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota", + "name": "Nama" + }, + "title": "Tambahkan Grup" + }, "media_player": { "data": { "entities": "Anggota", + "hide_members": "Sembunyikan anggota", "name": "Nama", - "title": "Grup Baru" + "title": "Tambahkan Grup" }, - "description": "Pilih opsi grup" + "description": "Pilih opsi grup", + "title": "Tambahkan Grup" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Pilih opsi grup" }, + "switch": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota", + "name": "Nama" + }, + "title": "Tambahkan Grup" + }, "user": { "data": { "group_type": "Jenis grup" }, - "title": "Grup Baru" + "description": "Grup memungkinkan Anda membuat entitas baru yang mewakili beberapa entitas dari jenis yang sama.", + "menu_options": { + "binary_sensor": "Grup sensor biner", + "cover": "Grup penutup", + "fan": "Grup kipas", + "light": "Grup lampu", + "lock": "Grup kunci", + "media_player": "Grup pemutar media", + "switch": "Ganti sakelar" + }, + "title": "Tambahkan Grup" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Semua entitas", + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + }, + "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala." + }, "binary_sensor_options": { "data": { "all": "Semua entitas", "entities": "Anggota" } }, + "cover": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + } + }, "cover_options": { "data": { "entities": "Anggota" } }, + "fan": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + } + }, "fan_options": { "data": { "entities": "Anggota" } }, + "light": { + "data": { + "all": "Semua entitas", + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + }, + "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala." + }, "light_options": { "data": { + "all": "Semua entitas", "entities": "Anggota" } }, + "lock": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + } + }, + "media_player": { + "data": { + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + } + }, "media_player_options": { "data": { "entities": "Anggota" } + }, + "switch": { + "data": { + "all": "Semua entitas", + "entities": "Anggota", + "hide_members": "Sembunyikan anggota" + }, + "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala." } } }, diff --git a/homeassistant/components/group/translations/it.json b/homeassistant/components/group/translations/it.json index 6dc9ff2cce8..4cbac9145d8 100644 --- a/homeassistant/components/group/translations/it.json +++ b/homeassistant/components/group/translations/it.json @@ -5,18 +5,21 @@ "data": { "all": "Tutte le entit\u00e0", "entities": "Membri", + "hide_members": "Nascondi membri", "name": "Nome" }, "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo.", - "title": "Nuovo gruppo" + "title": "Aggiungi gruppo" }, "cover": { "data": { "entities": "Membri", + "hide_members": "Nascondi membri", "name": "Nome", - "title": "Nuovo gruppo" + "title": "Aggiungi gruppo" }, - "description": "Seleziona le opzioni di gruppo" + "description": "Seleziona le opzioni di gruppo", + "title": "Aggiungi gruppo" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Membri", + "hide_members": "Nascondi membri", "name": "Nome", - "title": "Nuovo gruppo" + "title": "Aggiungi gruppo" }, - "description": "Seleziona le opzioni di gruppo" + "description": "Seleziona le opzioni di gruppo", + "title": "Aggiungi gruppo" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Tutte le entit\u00e0", "entities": "Membri", + "hide_members": "Nascondi membri", "name": "Nome", - "title": "Nuovo gruppo" + "title": "Aggiungi gruppo" }, - "description": "Seleziona le opzioni di gruppo" + "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo.", + "title": "Aggiungi gruppo" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Seleziona le opzioni di gruppo" }, + "lock": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri", + "name": "Nome" + }, + "title": "Aggiungi gruppo" + }, "media_player": { "data": { "entities": "Membri", + "hide_members": "Nascondi membri", "name": "Nome", - "title": "Nuovo gruppo" + "title": "Aggiungi gruppo" }, - "description": "Seleziona le opzioni di gruppo" + "description": "Seleziona le opzioni di gruppo", + "title": "Aggiungi gruppo" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Seleziona le opzioni di gruppo" }, + "switch": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri", + "name": "Nome" + }, + "title": "Aggiungi gruppo" + }, "user": { "data": { "group_type": "Tipo di gruppo" }, - "title": "Nuovo gruppo" + "description": "I gruppi consentono di creare una nuova entit\u00e0 che rappresenta pi\u00f9 entit\u00e0 dello stesso tipo.", + "menu_options": { + "binary_sensor": "Gruppo di sensori binari", + "cover": "Gruppo di coperture", + "fan": "Gruppo di ventole", + "light": "Gruppo di luci", + "lock": "Blocca gruppo", + "media_player": "Gruppo di lettori multimediali", + "switch": "Gruppo di interruttori" + }, + "title": "Aggiungi gruppo" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Tutte le entit\u00e0", + "entities": "Membri", + "hide_members": "Nascondi membri" + }, + "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo." + }, "binary_sensor_options": { "data": { "all": "Tutte le entit\u00e0", "entities": "Membri" } }, + "cover": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri" + } + }, "cover_options": { "data": { "entities": "Membri" } }, + "fan": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri" + } + }, "fan_options": { "data": { "entities": "Membri" } }, + "light": { + "data": { + "all": "Tutte le entit\u00e0", + "entities": "Membri", + "hide_members": "Nascondi membri" + }, + "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo." + }, "light_options": { "data": { + "all": "Tutte le entit\u00e0", "entities": "Membri" } }, + "lock": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri" + } + }, + "media_player": { + "data": { + "entities": "Membri", + "hide_members": "Nascondi membri" + } + }, "media_player_options": { "data": { "entities": "Membri" } + }, + "switch": { + "data": { + "all": "Tutte le entit\u00e0", + "entities": "Membri", + "hide_members": "Nascondi membri" + }, + "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo." } } }, diff --git a/homeassistant/components/group/translations/ja.json b/homeassistant/components/group/translations/ja.json index dac7583169d..55e414927a4 100644 --- a/homeassistant/components/group/translations/ja.json +++ b/homeassistant/components/group/translations/ja.json @@ -5,6 +5,7 @@ "data": { "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", "name": "\u540d\u524d" }, "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002", @@ -13,10 +14,12 @@ "cover": { "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", "name": "\u30b0\u30eb\u30fc\u30d7\u540d", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" + "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", "name": "\u30b0\u30eb\u30fc\u30d7\u540d", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" + "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", "name": "\u30b0\u30eb\u30fc\u30d7\u540d", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" + "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" }, + "lock": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", + "name": "\u540d\u524d" + }, + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + }, "media_player": { "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", "name": "\u30b0\u30eb\u30fc\u30d7\u540d", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" + "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" }, + "switch": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", + "name": "\u540d\u524d" + }, + "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + }, "user": { "data": { "group_type": "\u30b0\u30eb\u30fc\u30d7\u30bf\u30a4\u30d7" }, + "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u7a2e\u985e\u3092\u9078\u629e", + "menu_options": { + "binary_sensor": "\u30d0\u30a4\u30ca\u30ea\u30fc\u30bb\u30f3\u30b5\u30fc\u30b0\u30eb\u30fc\u30d7", + "cover": "\u30ab\u30d0\u30fc\u30b0\u30eb\u30fc\u30d7", + "fan": "\u30d5\u30a1\u30f3\u30b0\u30eb\u30fc\u30d7", + "light": "\u30e9\u30a4\u30c8(Light)\u30b0\u30eb\u30fc\u30d7", + "lock": "\u30ed\u30c3\u30af\u30b0\u30eb\u30fc\u30d7", + "media_player": "\u30e1\u30c7\u30a3\u30a2\u30d7\u30ec\u30fc\u30e4\u30fc\u30b0\u30eb\u30fc\u30d7", + "switch": "\u30b9\u30a4\u30c3\u30c1\u30b0\u30eb\u30fc\u30d7" + }, "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + }, + "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002" + }, "binary_sensor_options": { "data": { "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", "entities": "\u30e1\u30f3\u30d0\u30fc" } }, + "cover": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + } + }, "cover_options": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc" } }, + "fan": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + } + }, "fan_options": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc" } }, + "light": { + "data": { + "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + }, + "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002" + }, "light_options": { "data": { + "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", "entities": "\u30e1\u30f3\u30d0\u30fc" } }, + "lock": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + } + }, + "media_player": { + "data": { + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + } + }, "media_player_options": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc" } + }, + "switch": { + "data": { + "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "entities": "\u30e1\u30f3\u30d0\u30fc", + "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" + }, + "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/group/translations/nl.json b/homeassistant/components/group/translations/nl.json index c636c56f744..e670e2e853a 100644 --- a/homeassistant/components/group/translations/nl.json +++ b/homeassistant/components/group/translations/nl.json @@ -5,18 +5,21 @@ "data": { "all": "Alle entiteiten", "entities": "Leden", + "hide_members": "Verberg leden", "name": "Naam" }, "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld.", - "title": "Nieuwe groep" + "title": "Groep toevoegen" }, "cover": { "data": { "entities": "Leden", + "hide_members": "Verberg leden", "name": "Naam", - "title": "Nieuwe groep" + "title": "Groep toevoegen" }, - "description": "Selecteer groepsopties" + "description": "Selecteer groepsopties", + "title": "Groep toevoegen" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Leden", + "hide_members": "Verberg leden", "name": "Naam", - "title": "Nieuwe groep" + "title": "Groep toevoegen" }, - "description": "Selecteer groepsopties" + "description": "Selecteer groepsopties", + "title": "Groep toevoegen" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Alle entiteiten", "entities": "Leden", + "hide_members": "Verberg leden", "name": "Naam", - "title": "Nieuwe groep" + "title": "Groep toevoegen" }, - "description": "Selecteer groepsopties" + "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld.", + "title": "Groep toevoegen" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Selecteer groepsopties" }, + "lock": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden", + "name": "Naam" + }, + "title": "Groep toevoegen" + }, "media_player": { "data": { "entities": "Leden", + "hide_members": "Verberg leden", "name": "Naam", - "title": "Nieuwe groep" + "title": "Groep toevoegen" }, - "description": "Selecteer groepsopties" + "description": "Selecteer groepsopties", + "title": "Groep toevoegen" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Selecteer groepsopties" }, + "switch": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden", + "name": "Naam" + }, + "title": "Nieuwe groep" + }, "user": { "data": { "group_type": "Groepstype" }, - "title": "Nieuwe groep" + "description": "Met groepen kunt u een nieuwe entiteit cre\u00ebren die meerdere entiteiten van hetzelfde type vertegenwoordigt.", + "menu_options": { + "binary_sensor": "Binaire sensorgroep", + "cover": "Rolluikgroep", + "fan": "Ventilatorgroep", + "light": "Lichtgroep", + "lock": "Groep vergrendelen", + "media_player": "Mediaspelergroep", + "switch": "Groep wisselen" + }, + "title": "Groep toevoegen" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Alle entiteiten", + "entities": "Leden", + "hide_members": "Verberg leden" + }, + "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld." + }, "binary_sensor_options": { "data": { "all": "Alle entiteiten", "entities": "Leden" } }, + "cover": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden" + } + }, "cover_options": { "data": { "entities": "Leden" } }, + "fan": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden" + } + }, "fan_options": { "data": { "entities": "Leden" } }, + "light": { + "data": { + "all": "Alle entiteiten", + "entities": "Leden", + "hide_members": "Verberg leden" + }, + "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld." + }, "light_options": { "data": { + "all": "Alle entiteiten", "entities": "Leden" } }, + "lock": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden" + } + }, + "media_player": { + "data": { + "entities": "Leden", + "hide_members": "Verberg leden" + } + }, "media_player_options": { "data": { "entities": "Leden" } + }, + "switch": { + "data": { + "all": "Alle entiteiten", + "entities": "Leden", + "hide_members": "Verberg leden" + }, + "description": "Als \"alle entitieiten\" is ingeschakeld, is de status van de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \" alle entititeiten\" is uitgeschakeld, is de status van de groep ingeschakeld als een lid is ingeschakeld." } } }, diff --git a/homeassistant/components/group/translations/no.json b/homeassistant/components/group/translations/no.json index 0046479d686..016a9c8e934 100644 --- a/homeassistant/components/group/translations/no.json +++ b/homeassistant/components/group/translations/no.json @@ -5,18 +5,21 @@ "data": { "all": "Alle enheter", "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", "name": "Navn" }, "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5.", - "title": "Ny gruppe" + "title": "Legg til gruppe" }, "cover": { "data": { "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", "name": "Navn", - "title": "Ny gruppe" + "title": "Legg til gruppe" }, - "description": "Velg gruppealternativer" + "description": "Velg gruppealternativer", + "title": "Legg til gruppe" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", "name": "Navn", - "title": "Ny gruppe" + "title": "Legg til gruppe" }, - "description": "Velg gruppealternativer" + "description": "Velg gruppealternativer", + "title": "Legg til gruppe" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Alle enheter", "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", "name": "Navn", - "title": "Ny gruppe" + "title": "Legg til gruppe" }, - "description": "Velg gruppealternativer" + "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5.", + "title": "Legg til gruppe" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Velg gruppealternativer" }, + "lock": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", + "name": "Navn" + }, + "title": "Legg til gruppe" + }, "media_player": { "data": { "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", "name": "Navn", - "title": "Ny gruppe" + "title": "Legg til gruppe" }, - "description": "Velg gruppealternativer" + "description": "Velg gruppealternativer", + "title": "Legg til gruppe" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Velg gruppealternativer" }, + "switch": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer", + "name": "Navn" + }, + "title": "Legg til gruppe" + }, "user": { "data": { "group_type": "Gruppetype" }, - "title": "Ny gruppe" + "description": "Grupper lar deg opprette en ny enhet som representerer flere enheter av samme type.", + "menu_options": { + "binary_sensor": "Bin\u00e6r sensorgruppe", + "cover": "Dekkgruppe", + "fan": "Viftegruppe", + "light": "Lys-gruppen", + "lock": "L\u00e5s gruppe", + "media_player": "Mediespillergruppe", + "switch": "Bytt gruppe" + }, + "title": "Legg til gruppe" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Alle enheter", + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + }, + "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5." + }, "binary_sensor_options": { "data": { "all": "Alle enheter", "entities": "Medlemmer" } }, + "cover": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + } + }, "cover_options": { "data": { "entities": "Medlemmer" } }, + "fan": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + } + }, "fan_options": { "data": { "entities": "Medlemmer" } }, + "light": { + "data": { + "all": "Alle enheter", + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + }, + "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5." + }, "light_options": { "data": { + "all": "Alle enheter", "entities": "Medlemmer" } }, + "lock": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + } + }, + "media_player": { + "data": { + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + } + }, "media_player_options": { "data": { "entities": "Medlemmer" } + }, + "switch": { + "data": { + "all": "Alle enheter", + "entities": "Medlemmer", + "hide_members": "Skjul medlemmer" + }, + "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5." } } }, diff --git a/homeassistant/components/group/translations/pl.json b/homeassistant/components/group/translations/pl.json index 08595dd8a59..84cbd9f7b4d 100644 --- a/homeassistant/components/group/translations/pl.json +++ b/homeassistant/components/group/translations/pl.json @@ -5,18 +5,21 @@ "data": { "all": "Wszystkie encje", "entities": "Encje", + "hide_members": "Ukryj encje", "name": "Nazwa" }, - "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d s\u0105 wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona.", - "title": "Nowa grupa" + "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona.", + "title": "Dodaj grup\u0119" }, "cover": { "data": { "entities": "Encje", + "hide_members": "Ukryj encje", "name": "Nazwa", - "title": "Nowa grupa" + "title": "Dodaj grup\u0119" }, - "description": "Wybierz opcje grupy" + "description": "Wybierz opcje grupy", + "title": "Dodaj grup\u0119" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Encje", + "hide_members": "Ukryj encje", "name": "Nazwa", - "title": "Nowa grupa" + "title": "Dodaj grup\u0119" }, - "description": "Wybierz opcje grupy" + "description": "Wybierz opcje grupy", + "title": "Dodaj grup\u0119" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Wszystkie encje", "entities": "Encje", + "hide_members": "Ukryj encje", "name": "Nazwa", - "title": "Nowa grupa" + "title": "Dodaj grup\u0119" }, - "description": "Wybierz opcje grupy" + "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona.", + "title": "Dodaj grup\u0119" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Wybierz opcje grupy" }, + "lock": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje", + "name": "Nazwa" + }, + "title": "Dodaj grup\u0119" + }, "media_player": { "data": { "entities": "Encje", + "hide_members": "Ukryj encje", "name": "Nazwa", - "title": "Nowa grupa" + "title": "Dodaj grup\u0119" }, - "description": "Wybierz opcje grupy" + "description": "Wybierz opcje grupy", + "title": "Dodaj grup\u0119" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Wybierz opcje grupy" }, + "switch": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje", + "name": "Nazwa" + }, + "title": "Dodaj grup\u0119" + }, "user": { "data": { "group_type": "Rodzaj grupy" }, - "title": "Nowa grupa" + "description": "Grupy umo\u017cliwiaj\u0105 tworzenie nowej encji, kt\u00f3ra reprezentuje wiele encji tego samego typu.", + "menu_options": { + "binary_sensor": "Grupa sensor\u00f3w binarnych", + "cover": "Grupa rolet", + "fan": "Grupa wentylator\u00f3w", + "light": "Grupa \u015bwiate\u0142", + "lock": "Grupa zamk\u00f3w", + "media_player": "Grupa odtwarzaczy multimedialnych", + "switch": "Grupa prze\u0142\u0105cznik\u00f3w" + }, + "title": "Dodaj grup\u0119" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Wszystkie encje", + "entities": "Encje", + "hide_members": "Ukryj encje" + }, + "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona." + }, "binary_sensor_options": { "data": { "all": "Wszystkie encje", "entities": "Encje" } }, + "cover": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje" + } + }, "cover_options": { "data": { "entities": "Encje" } }, + "fan": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje" + } + }, "fan_options": { "data": { "entities": "Encje" } }, + "light": { + "data": { + "all": "Wszystkie encje", + "entities": "Encje", + "hide_members": "Ukryj encje" + }, + "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona." + }, "light_options": { "data": { + "all": "Wszystkie encje", "entities": "Encje" } }, + "lock": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje" + } + }, + "media_player": { + "data": { + "entities": "Encje", + "hide_members": "Ukryj encje" + } + }, "media_player_options": { "data": { "entities": "Encje" } + }, + "switch": { + "data": { + "all": "Wszystkie encje", + "entities": "Encje", + "hide_members": "Ukryj encje" + }, + "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona." } } }, diff --git a/homeassistant/components/group/translations/pt-BR.json b/homeassistant/components/group/translations/pt-BR.json index 5959ba66da7..0987b41f4ae 100644 --- a/homeassistant/components/group/translations/pt-BR.json +++ b/homeassistant/components/group/translations/pt-BR.json @@ -5,18 +5,21 @@ "data": { "all": "Todas as entidades", "entities": "Membros", + "hide_members": "Ocultar membros", "name": "Nome" }, "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado.", - "title": "Novo grupo" + "title": "Adicionar grupo" }, "cover": { "data": { "entities": "Membros", + "hide_members": "Ocultar membros", "name": "Nome", - "title": "Novo grupo" + "title": "Adicionar grupo" }, - "description": "Selecione as op\u00e7\u00f5es do grupo" + "description": "Selecione as op\u00e7\u00f5es do grupo", + "title": "Adicionar grupo" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "Membros", + "hide_members": "Ocultar membros", "name": "Nome", - "title": "Novo grupo" + "title": "Adicionar grupo" }, - "description": "Selecione as op\u00e7\u00f5es do grupo" + "description": "Selecione as op\u00e7\u00f5es do grupo", + "title": "Adicionar grupo" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "Todas as entidades", "entities": "Membros", + "hide_members": "Ocultar membros", "name": "Nome", - "title": "Novo grupo" + "title": "Adicionar grupo" }, - "description": "Selecione as op\u00e7\u00f5es do grupo" + "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado.", + "title": "Adicionar grupo" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "Selecione as op\u00e7\u00f5es do grupo" }, + "lock": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros", + "name": "Nome" + }, + "title": "Adicionar grupo" + }, "media_player": { "data": { "entities": "Membros", + "hide_members": "Ocultar membros", "name": "Nome", - "title": "Novo grupo" + "title": "Adicionar grupo" }, - "description": "Selecione as op\u00e7\u00f5es do grupo" + "description": "Selecione as op\u00e7\u00f5es do grupo", + "title": "Adicionar grupo" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "Selecione as op\u00e7\u00f5es do grupo" }, + "switch": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros", + "name": "Nome" + }, + "title": "Adicionar grupo" + }, "user": { "data": { "group_type": "Tipo de grupo" }, - "title": "Novo grupo" + "description": "Os grupos permitem que voc\u00ea crie uma nova entidade que representa v\u00e1rias entidades do mesmo tipo.", + "menu_options": { + "binary_sensor": "Grupo de sensores bin\u00e1rios", + "cover": "Grupo de persianas", + "fan": "Grupo de ventiladores", + "light": "Grupo de l\u00e2mpadas", + "lock": "Grupo de fechaduras", + "media_player": "Grupo de reprodutores de m\u00eddia", + "switch": "Grupo de interruptores" + }, + "title": "Adicionar grupo" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "Todas as entidades", + "entities": "Membros", + "hide_members": "Ocultar membros" + }, + "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado." + }, "binary_sensor_options": { "data": { "all": "Todas as entidades", "entities": "Membros" } }, + "cover": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros" + } + }, "cover_options": { "data": { "entities": "Membros" } }, + "fan": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros" + } + }, "fan_options": { "data": { "entities": "Membros" } }, + "light": { + "data": { + "all": "Todas as entidades", + "entities": "Membros", + "hide_members": "Ocultar membros" + }, + "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado." + }, "light_options": { "data": { + "all": "Todas as entidades", "entities": "Membros" } }, + "lock": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros" + } + }, + "media_player": { + "data": { + "entities": "Membros", + "hide_members": "Ocultar membros" + } + }, "media_player_options": { "data": { "entities": "Membros" } + }, + "switch": { + "data": { + "all": "Todas as entidades", + "entities": "Membros", + "hide_members": "Ocultar membros" + }, + "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado." } } }, diff --git a/homeassistant/components/group/translations/ru.json b/homeassistant/components/group/translations/ru.json index baba258b654..0ba6d79900e 100644 --- a/homeassistant/components/group/translations/ru.json +++ b/homeassistant/components/group/translations/ru.json @@ -5,18 +5,21 @@ "data": { "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432.", - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "cover": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." + "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432.", + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." }, + "lock": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + }, + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + }, "media_player": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." }, + "switch": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + }, + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + }, "user": { "data": { "group_type": "\u0422\u0438\u043f \u0433\u0440\u0443\u043f\u043f\u044b" }, - "title": "\u041d\u043e\u0432\u0430\u044f \u0433\u0440\u0443\u043f\u043f\u0430" + "description": "\u0413\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.", + "menu_options": { + "binary_sensor": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432", + "cover": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0448\u0442\u043e\u0440 \u0438\u043b\u0438 \u0436\u0430\u043b\u044e\u0437\u0438", + "fan": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0432\u0435\u043d\u0442\u0438\u043b\u044f\u0442\u043e\u0440\u043e\u0432", + "light": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u044f", + "lock": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0437\u0430\u043c\u043a\u043e\u0432", + "media_player": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u0435\u0434\u0438\u0430\u043f\u043b\u0435\u0435\u0440\u043e\u0432", + "switch": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u0435\u0439" + }, + "title": "\u0413\u0440\u0443\u043f\u043f\u0430" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + }, + "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432." + }, "binary_sensor_options": { "data": { "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" } }, + "cover": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + } + }, "cover_options": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" } }, + "fan": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + } + }, "fan_options": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" } }, + "light": { + "data": { + "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + }, + "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432." + }, "light_options": { "data": { + "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" } }, + "lock": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + } + }, + "media_player": { + "data": { + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + } + }, "media_player_options": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" } + }, + "switch": { + "data": { + "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", + "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" + }, + "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432." } } }, diff --git a/homeassistant/components/group/translations/tr.json b/homeassistant/components/group/translations/tr.json index bbb4600a4ed..354ed29214c 100644 --- a/homeassistant/components/group/translations/tr.json +++ b/homeassistant/components/group/translations/tr.json @@ -1,12 +1,25 @@ { "config": { "step": { + "binary_sensor": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad" + }, + "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r.", + "title": "Grup Ekle" + }, "cover": { "data": { - "entities": "Grup \u00fcyeleri", - "name": "Grup ismi" + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad", + "title": "Grup Ekle" }, - "description": "Grup se\u00e7eneklerini se\u00e7in" + "description": "Grup se\u00e7eneklerini se\u00e7in", + "title": "Grup Ekle" }, "cover_options": { "data": { @@ -16,10 +29,13 @@ }, "fan": { "data": { - "entities": "Grup \u00fcyeleri", - "name": "Grup ismi" + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad", + "title": "Grup Ekle" }, - "description": "Grup se\u00e7eneklerini se\u00e7in" + "description": "Grup se\u00e7eneklerini se\u00e7in", + "title": "Grup Ekle" }, "fan_options": { "data": { @@ -35,10 +51,14 @@ }, "light": { "data": { - "entities": "Grup \u00fcyeleri", - "name": "Grup ismi" + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad", + "title": "Grup Ekle" }, - "description": "Grup se\u00e7eneklerini se\u00e7in" + "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r.", + "title": "Grup Ekle" }, "light_options": { "data": { @@ -46,18 +66,132 @@ }, "description": "Grup se\u00e7eneklerini se\u00e7in" }, + "lock": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad" + }, + "title": "Grup Ekle" + }, "media_player": { "data": { - "entities": "Grup \u00fcyeleri", - "name": "Grup ismi" + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad", + "title": "Grup Ekle" }, - "description": "Grup se\u00e7eneklerini se\u00e7in" + "description": "Grup se\u00e7eneklerini se\u00e7in", + "title": "Grup Ekle" }, "media_player_options": { "data": { "entities": "Grup \u00fcyeleri" }, "description": "Grup se\u00e7eneklerini se\u00e7in" + }, + "switch": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle", + "name": "Ad" + }, + "title": "Grup Ekle" + }, + "user": { + "data": { + "group_type": "Grup t\u00fcr\u00fc" + }, + "description": "Gruplar, ayn\u0131 t\u00fcrden birden \u00e7ok varl\u0131\u011f\u0131 temsil eden yeni bir varl\u0131k olu\u015fturman\u0131za olanak tan\u0131r.", + "menu_options": { + "binary_sensor": "\u0130kili sens\u00f6r grubu", + "cover": "Kepenk grubu", + "fan": "Fan grubu", + "light": "I\u015f\u0131k grubu", + "lock": "Grubu kilitle", + "media_player": "Medya oynat\u0131c\u0131 grubu", + "switch": "Grubu de\u011fi\u015ftir" + }, + "title": "Grup Ekle" + } + } + }, + "options": { + "step": { + "binary_sensor": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + }, + "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r." + }, + "binary_sensor_options": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler" + } + }, + "cover": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + } + }, + "cover_options": { + "data": { + "entities": "\u00dcyeler" + } + }, + "fan": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + } + }, + "fan_options": { + "data": { + "entities": "\u00dcyeler" + } + }, + "light": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + }, + "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r." + }, + "light_options": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler" + } + }, + "lock": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + } + }, + "media_player": { + "data": { + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + } + }, + "media_player_options": { + "data": { + "entities": "\u00dcyeler" + } + }, + "switch": { + "data": { + "all": "T\u00fcm varl\u0131klar", + "entities": "\u00dcyeler", + "hide_members": "\u00dcyeleri gizle" + }, + "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r." } } }, diff --git a/homeassistant/components/group/translations/zh-Hant.json b/homeassistant/components/group/translations/zh-Hant.json index 9187455ad17..380c2216976 100644 --- a/homeassistant/components/group/translations/zh-Hant.json +++ b/homeassistant/components/group/translations/zh-Hant.json @@ -5,6 +5,7 @@ "data": { "all": "\u6240\u6709\u5be6\u9ad4", "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31" }, "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002", @@ -13,10 +14,12 @@ "cover": { "data": { "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" + "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", + "title": "\u65b0\u589e\u7fa4\u7d44" }, "cover_options": { "data": { @@ -27,10 +30,12 @@ "fan": { "data": { "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" + "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", + "title": "\u65b0\u589e\u7fa4\u7d44" }, "fan_options": { "data": { @@ -46,11 +51,14 @@ }, "light": { "data": { + "all": "\u6240\u6709\u5be6\u9ad4", "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002", + "title": "\u65b0\u589e\u7fa4\u7d44" }, "light_options": { "data": { @@ -58,13 +66,23 @@ }, "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" }, + "lock": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", + "name": "\u540d\u7a31" + }, + "title": "\u65b0\u589e\u7fa4\u7d44" + }, "media_player": { "data": { "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", "name": "\u540d\u7a31", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" + "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", + "title": "\u65b0\u589e\u7fa4\u7d44" }, "media_player_options": { "data": { @@ -72,41 +90,108 @@ }, "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" }, + "switch": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1", + "name": "\u540d\u7a31" + }, + "title": "\u65b0\u589e\u7fa4\u7d44" + }, "user": { "data": { "group_type": "\u7fa4\u7d44\u985e\u5225" }, + "description": "\u7fa4\u7d44\u5141\u8a31\u4f7f\u7528\u76f8\u540c\u985e\u5225\u7684\u591a\u500b\u5be6\u9ad4\u7d44\u6210\u65b0\u5be6\u9ad4\u3002", + "menu_options": { + "binary_sensor": "\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u7fa4\u7d44", + "cover": "\u6372\u7c3e\u7fa4\u7d44", + "fan": "\u98a8\u6247\u7fa4\u7d44", + "light": "\u71c8\u5149\u7fa4\u7d44", + "lock": "\u9580\u9396\u7fa4\u7d44", + "media_player": "\u5a92\u9ad4\u64ad\u653e\u5668\u7fa4\u7d44", + "switch": "\u958b\u95dc\u7fa4\u7d44" + }, "title": "\u65b0\u589e\u7fa4\u7d44" } } }, "options": { "step": { + "binary_sensor": { + "data": { + "all": "\u6240\u6709\u5be6\u9ad4", + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + }, + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" + }, "binary_sensor_options": { "data": { "all": "\u6240\u6709\u5be6\u9ad4", "entities": "\u6210\u54e1" } }, + "cover": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + } + }, "cover_options": { "data": { "entities": "\u6210\u54e1" } }, + "fan": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + } + }, "fan_options": { "data": { "entities": "\u6210\u54e1" } }, + "light": { + "data": { + "all": "\u6240\u6709\u5be6\u9ad4", + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + }, + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" + }, "light_options": { "data": { + "all": "\u6240\u6709\u5be6\u9ad4", "entities": "\u6210\u54e1" } }, + "lock": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + } + }, + "media_player": { + "data": { + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + } + }, "media_player_options": { "data": { "entities": "\u6210\u54e1" } + }, + "switch": { + "data": { + "all": "\u6240\u6709\u5be6\u9ad4", + "entities": "\u6210\u54e1", + "hide_members": "\u96b1\u85cf\u6210\u54e1" + }, + "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" } } }, diff --git a/homeassistant/components/growatt_server/translations/hu.json b/homeassistant/components/growatt_server/translations/hu.json index 5b2efc737fe..44d87bf753e 100644 --- a/homeassistant/components/growatt_server/translations/hu.json +++ b/homeassistant/components/growatt_server/translations/hu.json @@ -15,7 +15,7 @@ }, "user": { "data": { - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "url": "URL", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" diff --git a/homeassistant/components/guardian/translations/hu.json b/homeassistant/components/guardian/translations/hu.json index 04b62bb660e..82fd422abf7 100644 --- a/homeassistant/components/guardian/translations/hu.json +++ b/homeassistant/components/guardian/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { diff --git a/homeassistant/components/hangouts/translations/ca.json b/homeassistant/components/hangouts/translations/ca.json index b4114312f3e..8b629201cc1 100644 --- a/homeassistant/components/hangouts/translations/ca.json +++ b/homeassistant/components/hangouts/translations/ca.json @@ -24,7 +24,7 @@ "password": "Contrasenya" }, "description": "Buit", - "title": "Inici de sessi\u00f3 de Google Hangouts" + "title": "Inici de sessi\u00f3 de Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/de.json b/homeassistant/components/hangouts/translations/de.json index 02481670a09..b26618940be 100644 --- a/homeassistant/components/hangouts/translations/de.json +++ b/homeassistant/components/hangouts/translations/de.json @@ -23,7 +23,7 @@ "password": "Passwort" }, "description": "Leer", - "title": "Google Hangouts Login" + "title": "Google Chat Anmeldung" } } } diff --git a/homeassistant/components/hangouts/translations/et.json b/homeassistant/components/hangouts/translations/et.json index 7d6deb2ef53..96ed8b998ec 100644 --- a/homeassistant/components/hangouts/translations/et.json +++ b/homeassistant/components/hangouts/translations/et.json @@ -24,7 +24,7 @@ "password": "Salas\u00f5na" }, "description": "", - "title": "Google Hangoutsi sisselogimine" + "title": "Google Chat'i sisselogimine" } } } diff --git a/homeassistant/components/hangouts/translations/fr.json b/homeassistant/components/hangouts/translations/fr.json index da674b1c775..78a2517d29a 100644 --- a/homeassistant/components/hangouts/translations/fr.json +++ b/homeassistant/components/hangouts/translations/fr.json @@ -5,9 +5,9 @@ "unknown": "Erreur inattendue" }, "error": { - "invalid_2fa": "Authentification \u00e0 2 facteurs invalide, veuillez r\u00e9essayer.", + "invalid_2fa": "Authentification \u00e0 deux facteurs non valide, veuillez r\u00e9essayer.", "invalid_2fa_method": "M\u00e9thode 2FA non valide (v\u00e9rifiez sur le t\u00e9l\u00e9phone).", - "invalid_login": "Login invalide, veuillez r\u00e9essayer." + "invalid_login": "Identifiant non valide, veuillez r\u00e9essayer." }, "step": { "2fa": { @@ -24,7 +24,7 @@ "password": "Mot de passe" }, "description": "Vide", - "title": "Connexion \u00e0 Google Hangouts" + "title": "Connexion \u00e0 Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/he.json b/homeassistant/components/hangouts/translations/he.json index 9f0e3b48a62..ad696cad365 100644 --- a/homeassistant/components/hangouts/translations/he.json +++ b/homeassistant/components/hangouts/translations/he.json @@ -21,7 +21,7 @@ "email": "\u05d3\u05d5\u05d0\"\u05dc", "password": "\u05e1\u05d9\u05e1\u05de\u05d4" }, - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05dc- Google Hangouts" + "title": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05dc\u05e6'\u05d0\u05d8 \u05e9\u05dc \u05d2\u05d5\u05d2\u05dc" } } } diff --git a/homeassistant/components/hangouts/translations/hu.json b/homeassistant/components/hangouts/translations/hu.json index eda0144a818..1ea997aa098 100644 --- a/homeassistant/components/hangouts/translations/hu.json +++ b/homeassistant/components/hangouts/translations/hu.json @@ -24,7 +24,7 @@ "password": "Jelsz\u00f3" }, "description": "\u00dcres", - "title": "Google Hangouts Bejelentkez\u00e9s" + "title": "Google Chat bejelentkez\u00e9s" } } } diff --git a/homeassistant/components/hangouts/translations/id.json b/homeassistant/components/hangouts/translations/id.json index 39c68dda211..2336b211c9e 100644 --- a/homeassistant/components/hangouts/translations/id.json +++ b/homeassistant/components/hangouts/translations/id.json @@ -24,7 +24,7 @@ "password": "Kata Sandi" }, "description": "Kosong", - "title": "Info Masuk Google Hangouts" + "title": "Info Masuk Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/it.json b/homeassistant/components/hangouts/translations/it.json index 3d9322b1152..76d81a184d9 100644 --- a/homeassistant/components/hangouts/translations/it.json +++ b/homeassistant/components/hangouts/translations/it.json @@ -24,7 +24,7 @@ "password": "Password" }, "description": "Vuoto", - "title": "Accesso a Google Hangouts" + "title": "Accesso a Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/nl.json b/homeassistant/components/hangouts/translations/nl.json index 456d2193922..276c310da5a 100644 --- a/homeassistant/components/hangouts/translations/nl.json +++ b/homeassistant/components/hangouts/translations/nl.json @@ -24,7 +24,7 @@ "password": "Wachtwoord" }, "description": "Leeg", - "title": "Google Hangouts inlog" + "title": "Google Chat-login" } } } diff --git a/homeassistant/components/hangouts/translations/no.json b/homeassistant/components/hangouts/translations/no.json index d4fe4dbb5a6..751d54c852e 100644 --- a/homeassistant/components/hangouts/translations/no.json +++ b/homeassistant/components/hangouts/translations/no.json @@ -24,7 +24,7 @@ "password": "Passord" }, "description": "", - "title": "Google Hangouts p\u00e5logging" + "title": "Google Chat-p\u00e5logging" } } } diff --git a/homeassistant/components/hangouts/translations/pl.json b/homeassistant/components/hangouts/translations/pl.json index 8fb7e9e64d9..4dafdc5d996 100644 --- a/homeassistant/components/hangouts/translations/pl.json +++ b/homeassistant/components/hangouts/translations/pl.json @@ -24,7 +24,7 @@ "password": "Has\u0142o" }, "description": "Pusty", - "title": "Logowanie do Google Hangouts" + "title": "Logowanie do Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/pt-BR.json b/homeassistant/components/hangouts/translations/pt-BR.json index c60d9d8ec47..9e4d04cd989 100644 --- a/homeassistant/components/hangouts/translations/pt-BR.json +++ b/homeassistant/components/hangouts/translations/pt-BR.json @@ -24,7 +24,7 @@ "password": "Senha" }, "description": "Vazio", - "title": "Login do Hangouts do Google" + "title": "Login no Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/ru.json b/homeassistant/components/hangouts/translations/ru.json index d352258ba33..781e1e25eef 100644 --- a/homeassistant/components/hangouts/translations/ru.json +++ b/homeassistant/components/hangouts/translations/ru.json @@ -24,7 +24,7 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c" }, "description": "\u043f\u0443\u0441\u0442\u043e", - "title": "Google Hangouts" + "title": "Google Chat" } } } diff --git a/homeassistant/components/hangouts/translations/tr.json b/homeassistant/components/hangouts/translations/tr.json index 84fb80abaf5..5ddf2a64cbb 100644 --- a/homeassistant/components/hangouts/translations/tr.json +++ b/homeassistant/components/hangouts/translations/tr.json @@ -24,7 +24,7 @@ "password": "Parola" }, "description": "Bo\u015f", - "title": "Google Hangouts Giri\u015fi" + "title": "Google Sohbet Giri\u015fi" } } } diff --git a/homeassistant/components/hangouts/translations/zh-Hant.json b/homeassistant/components/hangouts/translations/zh-Hant.json index 678aacc5b62..f9884d5e214 100644 --- a/homeassistant/components/hangouts/translations/zh-Hant.json +++ b/homeassistant/components/hangouts/translations/zh-Hant.json @@ -24,7 +24,7 @@ "password": "\u5bc6\u78bc" }, "description": "\u7a7a\u767d", - "title": "\u767b\u5165 Google Hangouts" + "title": "\u767b\u5165 Google Chat" } } } diff --git a/homeassistant/components/hassio/translations/ja.json b/homeassistant/components/hassio/translations/ja.json index da3409d91cd..b7489852bdb 100644 --- a/homeassistant/components/hassio/translations/ja.json +++ b/homeassistant/components/hassio/translations/ja.json @@ -10,7 +10,7 @@ "installed_addons": "\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08\u307f\u306e\u30a2\u30c9\u30aa\u30f3", "supervisor_api": "Supervisor API", "supervisor_version": "Supervisor\u306e\u30d0\u30fc\u30b8\u30e7\u30f3", - "supported": "\u30b5\u30dd\u30fc\u30c8", + "supported": "\u30b5\u30dd\u30fc\u30c8\u306e\u6709\u7121", "update_channel": "\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30c1\u30e3\u30f3\u30cd\u30eb", "version_api": "\u30d0\u30fc\u30b8\u30e7\u30f3API" } diff --git a/homeassistant/components/home_connect/translations/hu.json b/homeassistant/components/home_connect/translations/hu.json index ca5f3e1e9ae..77b76df14d7 100644 --- a/homeassistant/components/home_connect/translations/hu.json +++ b/homeassistant/components/home_connect/translations/hu.json @@ -2,14 +2,14 @@ "config": { "abort": { "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz." + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3." }, "create_entry": { "default": "Sikeres hiteles\u00edt\u00e9s" }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/home_connect/translations/it.json b/homeassistant/components/home_connect/translations/it.json index 3dc834bfd85..74a598757d8 100644 --- a/homeassistant/components/home_connect/translations/it.json +++ b/homeassistant/components/home_connect/translations/it.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})" }, "create_entry": { diff --git a/homeassistant/components/home_plus_control/translations/hu.json b/homeassistant/components/home_plus_control/translations/hu.json index 09625a222f2..3dc4b451332 100644 --- a/homeassistant/components/home_plus_control/translations/hu.json +++ b/homeassistant/components/home_plus_control/translations/hu.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "create_entry": { @@ -13,7 +13,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } }, diff --git a/homeassistant/components/home_plus_control/translations/it.json b/homeassistant/components/home_plus_control/translations/it.json index 789a7db85eb..e79d22275f7 100644 --- a/homeassistant/components/home_plus_control/translations/it.json +++ b/homeassistant/components/home_plus_control/translations/it.json @@ -4,7 +4,7 @@ "already_configured": "L'account \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, diff --git a/homeassistant/components/homekit/translations/bg.json b/homeassistant/components/homekit/translations/bg.json index a7aa5bb792b..1a0ba552ae7 100644 --- a/homeassistant/components/homekit/translations/bg.json +++ b/homeassistant/components/homekit/translations/bg.json @@ -17,12 +17,6 @@ "entities": "\u041e\u0431\u0435\u043a\u0442\u0438" } }, - "include_exclude": { - "data": { - "entities": "\u041e\u0431\u0435\u043a\u0442\u0438", - "mode": "\u0420\u0435\u0436\u0438\u043c" - } - }, "init": { "data": { "mode": "\u0420\u0435\u0436\u0438\u043c" diff --git a/homeassistant/components/homekit/translations/ca.json b/homeassistant/components/homekit/translations/ca.json index f347268d90e..f1036cceb25 100644 --- a/homeassistant/components/homekit/translations/ca.json +++ b/homeassistant/components/homekit/translations/ca.json @@ -55,18 +55,9 @@ "description": "S'inclouran totes les entitats de \u201c{domains}\u201d tret que se'n seleccionin d'espec\u00edfiques.", "title": "Selecciona les entitats a incloure" }, - "include_exclude": { - "data": { - "entities": "Entitats", - "mode": "Mode" - }, - "description": "Tria les entitats a incloure. En mode accessori, nom\u00e9s s'inclou una sola entitat. En mode enlla\u00e7 inclusiu, s'inclouran totes les entitats del domini tret de que se'n seleccionin algunes en concret. En mode enlla\u00e7 excusiu, s'inclouran totes les entitats del domini excepte les entitats excloses. Per obtenir el millor rendiment, es crea una inst\u00e0ncia HomeKit per a cada repoductor multim\u00e8dia/TV i c\u00e0mera.", - "title": "Selecciona les entitats a incloure" - }, "init": { "data": { "domains": "Dominis a incloure", - "include_domains": "Dominis a incloure", "include_exclude_mode": "Mode d'inclusi\u00f3", "mode": "Mode de HomeKit" }, diff --git a/homeassistant/components/homekit/translations/cs.json b/homeassistant/components/homekit/translations/cs.json index 765b3c3c1b4..7103e1e3caa 100644 --- a/homeassistant/components/homekit/translations/cs.json +++ b/homeassistant/components/homekit/translations/cs.json @@ -28,18 +28,9 @@ "description": "Zkontrolujte v\u0161echny kamery, kter\u00e9 podporuj\u00ed nativn\u00ed streamy H.264. Pokud kamera nepodporuje stream H.264, syst\u00e9m video p\u0159ek\u00f3duje pro HomeKit na H.264. P\u0159ek\u00f3dov\u00e1n\u00ed vy\u017eaduje v\u00fdkonn\u00fd procesor a je pravd\u011bpodobn\u00e9, \u017ee nebude fungovat na po\u010d\u00edta\u010d\u00edch s jednou z\u00e1kladn\u00ed deskou.", "title": "Vyberte videokodek kamery." }, - "include_exclude": { - "data": { - "entities": "Entity", - "mode": "Re\u017eim" - }, - "description": "Vyberte entity, kter\u00e9 maj\u00ed b\u00fdt vystaveny. V re\u017eimu P\u0159\u00edslu\u0161enstv\u00ed je vystavena pouze jedna entita. V re\u017eimu Zahrnut\u00ed p\u0159emost\u011bn\u00ed budou vystaveny v\u0161echny entity v dom\u00e9n\u011b, pokud nebudou vybr\u00e1ny konkr\u00e9tn\u00ed entity. V re\u017eimu Vylou\u010den\u00ed p\u0159emost\u011bn\u00ed budou vystaveny v\u0161echny entity v dom\u00e9n\u011b krom\u011b vylou\u010den\u00fdch entit.", - "title": "Vyberte entity, kter\u00e9 chcete vystavit" - }, "init": { "data": { "domains": "Dom\u00e9ny, kter\u00e9 maj\u00ed b\u00fdt zahrnuty", - "include_domains": "Dom\u00e9ny, kter\u00e9 maj\u00ed b\u00fdt zahrnuty", "mode": "Re\u017eim" }, "title": "Vyberte dom\u00e9ny, kter\u00e9 chcete vystavit." diff --git a/homeassistant/components/homekit/translations/de.json b/homeassistant/components/homekit/translations/de.json index 24f6fd84eb0..d9c07e4cf21 100644 --- a/homeassistant/components/homekit/translations/de.json +++ b/homeassistant/components/homekit/translations/de.json @@ -55,18 +55,9 @@ "description": "Alle \"{domains}\"-Entit\u00e4ten werden einbezogen, sofern nicht bestimmte Entit\u00e4ten ausgew\u00e4hlt werden.", "title": "W\u00e4hle die einzuschlie\u00dfenden Entit\u00e4ten aus" }, - "include_exclude": { - "data": { - "entities": "Entit\u00e4ten", - "mode": "Modus" - }, - "description": "W\u00e4hle die einzubeziehenden Entit\u00e4ten aus. Im Zubeh\u00f6rmodus wird nur eine einzelne Entit\u00e4t eingeschlossen. Im Bridge-Include-Modus werden alle Entit\u00e4ten in der Dom\u00e4ne eingeschlossen, sofern nicht bestimmte Entit\u00e4ten ausgew\u00e4hlt sind. Im Bridge-Exclude-Modus werden alle Entit\u00e4ten in der Dom\u00e4ne eingeschlossen, au\u00dfer den ausgeschlossenen Entit\u00e4ten. F\u00fcr eine optimale Leistung wird f\u00fcr jeden TV-Media-Player, jede aktivit\u00e4tsbasierte Fernbedienung, jedes Schloss und jede Kamera ein separates HomeKit-Zubeh\u00f6r erstellt.", - "title": "W\u00e4hle die Entit\u00e4ten aus, die aufgenommen werden sollen" - }, "init": { "data": { "domains": "Einzubeziehende Domains", - "include_domains": "Einzubeziehende Domains", "include_exclude_mode": "Inklusionsmodus", "mode": "HomeKit-Modus" }, diff --git a/homeassistant/components/homekit/translations/el.json b/homeassistant/components/homekit/translations/el.json index 632d9aecd76..370617bd5fd 100644 --- a/homeassistant/components/homekit/translations/el.json +++ b/homeassistant/components/homekit/translations/el.json @@ -55,18 +55,9 @@ "description": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \"{domains}\" \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd, \u03b5\u03ba\u03c4\u03cc\u03c2 \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bf\u03cd\u03bd \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2.", "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd" }, - "include_exclude": { - "data": { - "entities": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", - "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd. \u03a3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03be\u03b5\u03c3\u03bf\u03c5\u03ac\u03c1, \u03c0\u03b5\u03c1\u03b9\u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bc\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1. \u03a3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 include bridge, \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd \u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c4\u03bf\u03c5 \u03c4\u03bf\u03bc\u03ad\u03b1, \u03b5\u03ba\u03c4\u03cc\u03c2 \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bf\u03cd\u03bd \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b5\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2. \u03a3\u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03b9\u03c3\u03bc\u03bf\u03cd \u03b3\u03ad\u03c6\u03c5\u03c1\u03b1\u03c2, \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd \u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c4\u03bf\u03c5 \u03c4\u03bf\u03bc\u03ad\u03b1 \u03b5\u03ba\u03c4\u03cc\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03c0\u03bf\u03c5 \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03b9\u03c3\u03c4\u03b5\u03af. \u0393\u03b9\u03b1 \u03ba\u03b1\u03bb\u03cd\u03c4\u03b5\u03c1\u03b7 \u03b1\u03c0\u03cc\u03b4\u03bf\u03c3\u03b7, \u03b8\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03ad\u03bd\u03b1 \u03be\u03b5\u03c7\u03c9\u03c1\u03b9\u03c3\u03c4\u03cc \u03b1\u03be\u03b5\u03c3\u03bf\u03c5\u03ac\u03c1 HomeKit \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd tv, \u03c4\u03b7\u03bb\u03b5\u03c7\u03b5\u03b9\u03c1\u03b9\u03c3\u03c4\u03ae\u03c1\u03b9\u03bf \u03bc\u03b5 \u03b2\u03ac\u03c3\u03b7 \u03c4\u03b7 \u03b4\u03c1\u03b1\u03c3\u03c4\u03b7\u03c1\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1, \u03ba\u03bb\u03b5\u03b9\u03b4\u03b1\u03c1\u03b9\u03ac \u03ba\u03b1\u03b9 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b1.", - "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd" - }, "init": { "data": { "domains": "\u03a4\u03bf\u03bc\u03b5\u03af\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd", - "include_domains": "\u03a4\u03bf\u03bc\u03b5\u03af\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03b9\u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd", "include_exclude_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c3\u03c5\u03bc\u03c0\u03b5\u03c1\u03af\u03bb\u03b7\u03c8\u03b7\u03c2", "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 HomeKit" }, diff --git a/homeassistant/components/homekit/translations/en.json b/homeassistant/components/homekit/translations/en.json index 72c6ba9df36..14585b60f1e 100644 --- a/homeassistant/components/homekit/translations/en.json +++ b/homeassistant/components/homekit/translations/en.json @@ -55,18 +55,9 @@ "description": "All \u201c{domains}\u201d entities will be included unless specific entities are selected.", "title": "Select the entities to be included" }, - "include_exclude": { - "data": { - "entities": "Entities", - "mode": "Mode" - }, - "description": "Choose the entities to be included. In accessory mode, only a single entity is included. In bridge include mode, all entities in the domain will be included unless specific entities are selected. In bridge exclude mode, all entities in the domain will be included except for the excluded entities. For best performance, a separate HomeKit accessory will be created for each tv media player, activity based remote, lock, and camera.", - "title": "Select entities to be included" - }, "init": { "data": { "domains": "Domains to include", - "include_domains": "Domains to include", "include_exclude_mode": "Inclusion Mode", "mode": "HomeKit Mode" }, diff --git a/homeassistant/components/homekit/translations/es-419.json b/homeassistant/components/homekit/translations/es-419.json index 2b670f13c7e..459832cac80 100644 --- a/homeassistant/components/homekit/translations/es-419.json +++ b/homeassistant/components/homekit/translations/es-419.json @@ -34,9 +34,6 @@ "title": "Seleccione el c\u00f3dec de video de la c\u00e1mara." }, "init": { - "data": { - "include_domains": "Dominios para incluir" - }, "description": "Las entidades en los \"Dominios para incluir\" se vincular\u00e1n a HomeKit. Podr\u00e1 seleccionar qu\u00e9 entidades excluir de esta lista en la siguiente pantalla.", "title": "Seleccione dominios para puentear." }, diff --git a/homeassistant/components/homekit/translations/es.json b/homeassistant/components/homekit/translations/es.json index cc1925b8095..ef60610a8a1 100644 --- a/homeassistant/components/homekit/translations/es.json +++ b/homeassistant/components/homekit/translations/es.json @@ -40,17 +40,8 @@ "entities": "Entidades" } }, - "include_exclude": { - "data": { - "entities": "Entidades", - "mode": "Modo" - }, - "description": "Selecciona las entidades que deseas exponer. En el modo de accesorio, solo se expone una \u00fanica entidad. En el modo de inclusi\u00f3n de puente, todas las entidades del dominio se expondr\u00e1n a menos que se seleccionen entidades espec\u00edficas. En el modo de exclusi\u00f3n de puentes, todas las entidades del dominio se expondr\u00e1n excepto las entidades excluidas.", - "title": "Seleccionar entidades para exponer" - }, "init": { "data": { - "include_domains": "Dominios para incluir", "mode": "Modo" }, "description": "Las entidades de los \"Dominios que se van a incluir\" se establecer\u00e1n en HomeKit. Podr\u00e1 seleccionar qu\u00e9 entidades excluir de esta lista en la siguiente pantalla.", diff --git a/homeassistant/components/homekit/translations/et.json b/homeassistant/components/homekit/translations/et.json index d0ef2e79f03..671dede46e0 100644 --- a/homeassistant/components/homekit/translations/et.json +++ b/homeassistant/components/homekit/translations/et.json @@ -55,18 +55,9 @@ "description": "Kaasatakse k\u00f5ik olemid {domains}, v\u00e4lja arvatud juhul, kui valitud on konkreetsed olemid.", "title": "Vali kaasatavad olemid" }, - "include_exclude": { - "data": { - "entities": "Olemid", - "mode": "Re\u017eiim" - }, - "description": "Vali kaasatavad olemid. Tarvikute re\u017eiimis on kaasatav ainult \u00fcks olem. Silla re\u017eiimis kaasatakse k\u00f5ik domeeni olemid, v\u00e4lja arvatud juhul, kui valitud on kindlad olemid. Silla v\u00e4listamisre\u017eiimis kaasatakse k\u00f5ik domeeni olemid, v\u00e4lja arvatud v\u00e4listatud olemid. Parima kasutuskogemuse jaoks on eraldi HomeKit seadmed iga TV meediumim\u00e4ngija, luku, juhtpuldi ja kaamera jaoks.", - "title": "Vali kaasatavd olemid" - }, "init": { "data": { "domains": "Kaasa domeenid", - "include_domains": "Kaasatud domeenid", "include_exclude_mode": "Kaasamise re\u017eiim", "mode": "HomeKiti re\u017eiim" }, diff --git a/homeassistant/components/homekit/translations/fr.json b/homeassistant/components/homekit/translations/fr.json index 1977f7e6c18..e4850893bd3 100644 --- a/homeassistant/components/homekit/translations/fr.json +++ b/homeassistant/components/homekit/translations/fr.json @@ -55,18 +55,9 @@ "description": "Toutes les entit\u00e9s \"{domaines}\" seront incluses, sauf si des entit\u00e9s sp\u00e9cifiques sont s\u00e9lectionn\u00e9es.", "title": "S\u00e9lectionnez les entit\u00e9s \u00e0 inclure" }, - "include_exclude": { - "data": { - "entities": "Entit\u00e9s", - "mode": "Mode" - }, - "description": "Choisissez les entit\u00e9s \u00e0 inclure. En mode accessoire, une seule entit\u00e9 est incluse. En mode d'inclusion de pont, toutes les entit\u00e9s du domaine seront incluses \u00e0 moins que des entit\u00e9s sp\u00e9cifiques ne soient s\u00e9lectionn\u00e9es. En mode d'exclusion de pont, toutes les entit\u00e9s du domaine seront incluses \u00e0 l'exception des entit\u00e9s exclues. Pour de meilleures performances, un accessoire HomeKit distinct sera cr\u00e9\u00e9 pour chaque lecteur multim\u00e9dia TV et cam\u00e9ra.", - "title": "S\u00e9lectionnez les entit\u00e9s \u00e0 exposer" - }, "init": { "data": { "domains": "Domaines \u00e0 inclure", - "include_domains": "Domaines \u00e0 inclure", "include_exclude_mode": "Mode d'inclusion", "mode": "Mode HomeKit" }, diff --git a/homeassistant/components/homekit/translations/he.json b/homeassistant/components/homekit/translations/he.json index 22f3515b497..defce6a05a2 100644 --- a/homeassistant/components/homekit/translations/he.json +++ b/homeassistant/components/homekit/translations/he.json @@ -28,17 +28,10 @@ "entities": "\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea" } }, - "include_exclude": { - "data": { - "mode": "\u05de\u05e6\u05d1" - }, - "title": "\u05d1\u05d7\u05e8 \u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e9\u05d9\u05d9\u05db\u05dc\u05dc\u05d5" - }, "init": { "data": { "domains": "\u05ea\u05d7\u05d5\u05de\u05d9\u05dd \u05e9\u05d9\u05e9 \u05dc\u05db\u05dc\u05d5\u05dc", - "include_domains": "\u05ea\u05d7\u05d5\u05de\u05d9\u05dd \u05e9\u05d9\u05e9 \u05dc\u05db\u05dc\u05d5\u05dc", - "mode": "\u05de\u05e6\u05d1" + "mode": "\u05de\u05e6\u05d1 HomeKit" } }, "yaml": { diff --git a/homeassistant/components/homekit/translations/hu.json b/homeassistant/components/homekit/translations/hu.json index 7c3a28abfd4..a1c64f6fac3 100644 --- a/homeassistant/components/homekit/translations/hu.json +++ b/homeassistant/components/homekit/translations/hu.json @@ -55,23 +55,14 @@ "description": "Az \u00f6sszes \"{domains}\" entit\u00e1s beker\u00fcl, kiv\u00e9ve, ha konkr\u00e9t entit\u00e1sok vannak kijel\u00f6lve.", "title": "V\u00e1lassza ki a felvenni k\u00edv\u00e1nt entit\u00e1sokat" }, - "include_exclude": { - "data": { - "entities": "Entit\u00e1sok", - "mode": "M\u00f3d" - }, - "description": "V\u00e1lassza ki a felvenni k\u00edv\u00e1nt entit\u00e1sokat. Kieg\u00e9sz\u00edt\u0151 m\u00f3dban csak egyetlen entit\u00e1s szerepel. H\u00eddbefogad\u00e1si m\u00f3dban a tartom\u00e1ny \u00f6sszes entit\u00e1sa szerepelni fog, hacsak nincsenek kijel\u00f6lve konkr\u00e9t entit\u00e1sok. H\u00eddkiz\u00e1r\u00e1si m\u00f3dban a domain \u00f6sszes entit\u00e1sa szerepelni fog, kiv\u00e9ve a kiz\u00e1rt entit\u00e1sokat. A legjobb teljes\u00edtm\u00e9ny \u00e9rdek\u00e9ben minden TV m\u00e9dialej\u00e1tsz\u00f3hoz, tev\u00e9kenys\u00e9galap\u00fa t\u00e1vir\u00e1ny\u00edt\u00f3hoz, z\u00e1rhoz \u00e9s f\u00e9nyk\u00e9pez\u0151g\u00e9phez k\u00fcl\u00f6n HomeKit tartoz\u00e9kot hoznak l\u00e9tre.", - "title": "V\u00e1lassza ki a felvenni k\u00edv\u00e1nt entit\u00e1sokat" - }, "init": { "data": { "domains": "Felvenni k\u00edv\u00e1nt domainek", - "include_domains": "Felvenni k\u00edv\u00e1nt domainek", "include_exclude_mode": "Felv\u00e9tel m\u00f3dja", "mode": "HomeKit m\u00f3d" }, "description": "A HomeKit konfigur\u00e1lhat\u00f3 \u00fagy, hogy egy h\u00edd vagy egyetlen tartoz\u00e9k l\u00e1that\u00f3 legyen. Kieg\u00e9sz\u00edt\u0151 m\u00f3dban csak egyetlen entit\u00e1s haszn\u00e1lhat\u00f3. A tartoz\u00e9k m\u00f3dra van sz\u00fcks\u00e9g ahhoz, hogy a TV -eszk\u00f6zoszt\u00e1ly\u00fa m\u00e9dialej\u00e1tsz\u00f3k megfelel\u0151en m\u0171k\u00f6djenek. A \u201eTartalmazand\u00f3 tartom\u00e1nyok\u201d entit\u00e1sai szerepelni fognak a HomeKitben. A k\u00f6vetkez\u0151 k\u00e9perny\u0151n kiv\u00e1laszthatja, hogy mely entit\u00e1sokat k\u00edv\u00e1nja felvenni vagy kiz\u00e1rni a list\u00e1b\u00f3l.", - "title": "V\u00e1lassza ki a felvenni k\u00edv\u00e1nt domaineket." + "title": "V\u00e1lassza ki a m\u00f3dot \u00e9s a tartom\u00e1nyokat." }, "yaml": { "description": "Ez a bejegyz\u00e9s YAML-en kereszt\u00fcl vez\u00e9relhet\u0151", diff --git a/homeassistant/components/homekit/translations/id.json b/homeassistant/components/homekit/translations/id.json index 5faedff893b..8293ad9d72c 100644 --- a/homeassistant/components/homekit/translations/id.json +++ b/homeassistant/components/homekit/translations/id.json @@ -55,18 +55,9 @@ "description": "Semua entitas \"{domains}\" akan disertakan kecuali entitas tertentu dipilih.", "title": "Pilih entitas yang akan disertakan" }, - "include_exclude": { - "data": { - "entities": "Entitas", - "mode": "Mode" - }, - "description": "Pilih entitas yang akan disertakan. Dalam mode aksesori, hanya satu entitas yang disertakan. Dalam mode \"bridge include\", semua entitas di domain akan disertakan, kecuali entitas tertentu dipilih. Dalam mode \"bridge exclude\", semua entitas di domain akan disertakan, kecuali untuk entitas tertentu yang dipilih. Untuk kinerja terbaik, aksesori HomeKit terpisah diperlukan untuk masing-masing pemutar media TV, remote berbasis aktivitas, kunci, dan kamera.", - "title": "Pilih entitas untuk disertakan" - }, "init": { "data": { "domains": "Domain yang disertakan", - "include_domains": "Domain yang disertakan", "include_exclude_mode": "Mode Penyertaan", "mode": "Mode HomeKit" }, diff --git a/homeassistant/components/homekit/translations/it.json b/homeassistant/components/homekit/translations/it.json index 186bf989a4e..7acec1af5c3 100644 --- a/homeassistant/components/homekit/translations/it.json +++ b/homeassistant/components/homekit/translations/it.json @@ -55,18 +55,9 @@ "description": "Tutte le entit\u00e0 \"{domini}\" saranno incluse a eccezione delle entit\u00e0 escluse e delle entit\u00e0 categorizzate.", "title": "Seleziona le entit\u00e0 da includere" }, - "include_exclude": { - "data": { - "entities": "Entit\u00e0", - "mode": "Modalit\u00e0" - }, - "description": "Scegli le entit\u00e0 da includere. In modalit\u00e0 accessorio, \u00e8 inclusa una sola entit\u00e0. In modalit\u00e0 di inclusione bridge, tutte le entit\u00e0 nel dominio saranno incluse, a meno che non siano selezionate entit\u00e0 specifiche. In modalit\u00e0 di esclusione bridge, tutte le entit\u00e0 nel dominio saranno incluse, a eccezione delle entit\u00e0 escluse. Per prestazioni ottimali, sar\u00e0 creata una HomeKit separata accessoria per ogni lettore multimediale TV, telecomando basato sulle attivit\u00e0, serratura e videocamera.", - "title": "Seleziona le entit\u00e0 da includere" - }, "init": { "data": { "domains": "Domini da includere", - "include_domains": "Domini da includere", "include_exclude_mode": "Modalit\u00e0 di inclusione", "mode": "Modalit\u00e0 HomeKit" }, diff --git a/homeassistant/components/homekit/translations/ja.json b/homeassistant/components/homekit/translations/ja.json index 6d6b441484f..6bcad37ad61 100644 --- a/homeassistant/components/homekit/translations/ja.json +++ b/homeassistant/components/homekit/translations/ja.json @@ -55,18 +55,9 @@ "description": "\u7279\u5b9a\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u9650\u308a\u3001\u3059\u3079\u3066\u306e \u201c{domains}\u201d \u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u542b\u307e\u308c\u307e\u3059\u3002", "title": "\u542b\u3081\u308b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u9078\u629e" }, - "include_exclude": { - "data": { - "entities": "\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", - "mode": "\u30e2\u30fc\u30c9" - }, - "description": "\u542b\u307e\u308c\u308b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u30a2\u30af\u30bb\u30b5\u30ea \u30e2\u30fc\u30c9\u3067\u306f\u30011\u3064\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u306e\u307f\u304c\u542b\u307e\u308c\u307e\u3059\u3002\u30d6\u30ea\u30c3\u30b8\u30a4\u30f3\u30af\u30eb\u30fc\u30c9\u30e2\u30fc\u30c9\u3067\u306f\u3001\u7279\u5b9a\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u306a\u3044\u9650\u308a\u3001\u30c9\u30e1\u30a4\u30f3\u5185\u306e\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u542b\u307e\u308c\u307e\u3059\u3002\u30d6\u30ea\u30c3\u30b8\u9664\u5916\u30e2\u30fc\u30c9\u3067\u306f\u3001\u9664\u5916\u3055\u308c\u305f\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u9664\u3044\u3066\u3001\u30c9\u30e1\u30a4\u30f3\u5185\u306e\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u542b\u307e\u308c\u307e\u3059\u3002\u6700\u9ad8\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5b9f\u73fe\u3059\u308b\u305f\u3081\u306b\u3001\u30c6\u30ec\u30d3\u30e1\u30c7\u30a3\u30a2\u30d7\u30ec\u30fc\u30e4\u30fc\u3001\u30a2\u30af\u30c6\u30a3\u30d3\u30c6\u30a3\u30d9\u30fc\u30b9\u306e\u30ea\u30e2\u30b3\u30f3(remote)\u3001\u30ed\u30c3\u30af\u3001\u30ab\u30e1\u30e9\u306b\u5bfe\u3057\u3066\u500b\u5225\u306b\u3001HomeKit\u30a2\u30af\u30bb\u30b5\u30ea\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002", - "title": "\u542b\u3081\u308b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u9078\u629e" - }, "init": { "data": { "domains": "\u542b\u3081\u308b\u30c9\u30e1\u30a4\u30f3", - "include_domains": "\u542b\u3081\u308b\u30c9\u30e1\u30a4\u30f3", "include_exclude_mode": "\u30a4\u30f3\u30af\u30eb\u30fc\u30b8\u30e7\u30f3\u30e2\u30fc\u30c9", "mode": "\u30e2\u30fc\u30c9" }, diff --git a/homeassistant/components/homekit/translations/ka.json b/homeassistant/components/homekit/translations/ka.json index 97787f722fc..63b99f73416 100644 --- a/homeassistant/components/homekit/translations/ka.json +++ b/homeassistant/components/homekit/translations/ka.json @@ -1,14 +1,6 @@ { "options": { "step": { - "include_exclude": { - "data": { - "entities": "\u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d4\u10d1\u10d8", - "mode": "\u10e0\u10d4\u10df\u10db\u10d8" - }, - "description": "\u10e8\u10d4\u10d0\u10e0\u10e9\u10d8\u10d4\u10d7 \u10d2\u10d0\u10db\u10dd\u10e1\u10d0\u10d5\u10da\u10d4\u10dc\u10d8 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d4\u10d1\u10d8. \u10d0\u10e5\u10e1\u10d4\u10e1\u10e3\u10d0\u10e0\u10d4\u10d1\u10d8\u10e1 \u10e0\u10d4\u10df\u10d8\u10db\u10e8\u10d8 \u10d2\u10d0\u10db\u10dd\u10d5\u10da\u10d4\u10dc\u10d0\u10e1 \u10d4\u10e5\u10d5\u10d4\u10db\u10d3\u10d4\u10d1\u10d0\u10e0\u10d4\u10d1\u10d0 \u10db\u10ee\u10dd\u10da\u10dd\u10d3 \u10d4\u10e0\u10d7\u10d8 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d8. \u10d1\u10e0\u10d8\u10ef\u10d8\u10e1 \u10db\u10dd\u10ea\u10e3\u10da\u10dd\u10d1\u10d8\u10e1 \u10e0\u10d4\u10df\u10d8\u10db\u10e8\u10d8 \u10d7\u10e3 \u10e1\u10de\u10d4\u10ea\u10d8\u10e4\u10d8\u10d9\u10e3\u10e0\u10d8 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d4\u10d1\u10d8 \u10d0\u10e0 \u10d0\u10e0\u10d8\u10e1 \u10e8\u10d4\u10e0\u10e9\u10d4\u10e3\u10da\u10d8, \u10db\u10d8\u10e1\u10d0\u10ec\u10d5\u10d3\u10dd\u10db\u10d8 \u10d8\u10e5\u10dc\u10d4\u10d1\u10d0 \u10d3\u10dd\u10db\u10d4\u10dc\u10d8\u10e1 \u10e7\u10d5\u10d4\u10da\u10d0 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d8. \u10ee\u10d8\u10d3\u10d8\u10e1 \u10d2\u10d0\u10db\u10dd\u10e0\u10d8\u10ea\u10ee\u10d5\u10d8\u10e1 \u10e0\u10d4\u10df\u10d8\u10db\u10e8\u10d8, \u10d3\u10dd\u10db\u10d4\u10dc\u10d8\u10e1 \u10e7\u10d5\u10d4\u10da\u10d0 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d8 \u10d8\u10e5\u10dc\u10d4\u10d1\u10d0 \u10dc\u10d8\u10e1\u10d0\u10ec\u10d5\u10d3\u10dd\u10db\u10d8 \u10d2\u10d0\u10db\u10dd\u10e0\u10d8\u10ea\u10ee\u10e3\u10da \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d4\u10d1\u10d8\u10e1 \u10d2\u10d0\u10e0\u10d3\u10d0.", - "title": "\u10d0\u10d8\u10e0\u10e9\u10d8\u10d4\u10d7 \u10d2\u10d0\u10db\u10dd\u10e1\u10d0\u10d5\u10da\u10d4\u10dc\u10d8 \u10dd\u10d1\u10d8\u10d4\u10e5\u10e2\u10d4\u10d1\u10d8" - }, "init": { "data": { "mode": "\u10e0\u10d4\u10df\u10d8\u10db\u10d8" diff --git a/homeassistant/components/homekit/translations/ko.json b/homeassistant/components/homekit/translations/ko.json index e93a55db971..cd8cbb81943 100644 --- a/homeassistant/components/homekit/translations/ko.json +++ b/homeassistant/components/homekit/translations/ko.json @@ -33,17 +33,8 @@ "description": "\ub124\uc774\ud2f0\ube0c H.264 \uc2a4\ud2b8\ub9bc\uc744 \uc9c0\uc6d0\ud558\ub294 \ubaa8\ub4e0 \uce74\uba54\ub77c\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694. \uce74\uba54\ub77c\uac00 H.264 \uc2a4\ud2b8\ub9bc\uc744 \ucd9c\ub825\ud558\uc9c0 \uc54a\uc73c\uba74 \uc2dc\uc2a4\ud15c\uc740 \ube44\ub514\uc624\ub97c HomeKit\uc6a9 H.264 \ud3ec\ub9f7\uc73c\ub85c \ubcc0\ud658\uc2dc\ud0b5\ub2c8\ub2e4. \ud2b8\ub79c\uc2a4\ucf54\ub529 \ubcc0\ud658\uc5d0\ub294 \ub192\uc740 CPU \uc131\ub2a5\uc774 \ud544\uc694\ud558\uba70 Raspberry Pi\uc640 \uac19\uc740 \ub2e8\uc77c \ubcf4\ub4dc \ucef4\ud4e8\ud130\uc5d0\uc11c\ub294 \uc791\ub3d9\ud558\uc9c0 \uc54a\uc744 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "title": "\uce74\uba54\ub77c \ube44\ub514\uc624 \ucf54\ub371 \uc120\ud0dd\ud558\uae30" }, - "include_exclude": { - "data": { - "entities": "\uad6c\uc131\uc694\uc18c", - "mode": "\ubaa8\ub4dc" - }, - "description": "\ud3ec\ud568\ud560 \uad6c\uc131\uc694\uc18c\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \uc561\uc138\uc11c\ub9ac \ubaa8\ub4dc\uc5d0\uc11c\ub294 \ub2e8\uc77c \uad6c\uc131\uc694\uc18c\ub9cc \ud3ec\ud568\ub429\ub2c8\ub2e4. \ube0c\ub9ac\uc9c0 \ud3ec\ud568 \ubaa8\ub4dc\uc5d0\uc11c\ub294 \ud2b9\uc815 \uad6c\uc131\uc694\uc18c\ub97c \uc120\ud0dd\ud558\uc9c0 \uc54a\ub294 \ud55c \ub3c4\uba54\uc778\uc758 \ubaa8\ub4e0 \uad6c\uc131\uc694\uc18c\uac00 \ud3ec\ud568\ub429\ub2c8\ub2e4. \ube0c\ub9ac\uc9c0 \uc81c\uc678 \ubaa8\ub4dc\uc5d0\uc11c\ub294 \uc81c\uc678\ub41c \uad6c\uc131\uc694\uc18c\ub97c \uc81c\uc678\ud55c \ub3c4\uba54\uc778\uc758 \ub098\uba38\uc9c0 \uad6c\uc131\uc694\uc18c\uac00 \ud3ec\ud568\ub429\ub2c8\ub2e4. \ucd5c\uc0c1\uc758 \uc131\ub2a5\uc744 \uc704\ud574 \uac01 TV \ubbf8\ub514\uc5b4 \ud50c\ub808\uc774\uc5b4\uc640 \ud65c\ub3d9 \uae30\ubc18 \ub9ac\ubaa8\ucf58, \uc7a0\uae08\uae30\uae30, \uce74\uba54\ub77c\ub294 \ubcc4\ub3c4\uc758 HomeKit \uc561\uc138\uc11c\ub9ac\ub85c \uc0dd\uc131\ub429\ub2c8\ub2e4.", - "title": "\ud3ec\ud568\ud560 \ub3c4\uba54\uc778 \uc120\ud0dd\ud558\uae30" - }, "init": { "data": { - "include_domains": "\ud3ec\ud568\ud560 \ub3c4\uba54\uc778", "mode": "\ubaa8\ub4dc" }, "description": "\ube0c\ub9ac\uc9c0 \ub610\ub294 \ub2e8\uc77c \uc561\uc138\uc11c\ub9ac\ub97c \ub178\ucd9c\ud558\uc5ec HomeKit\ub97c \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc561\uc138\uc11c\ub9ac \ubaa8\ub4dc\uc5d0\uc11c\ub294 \ub2e8\uc77c \uad6c\uc131\uc694\uc18c\ub9cc \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. TV \uae30\uae30 \ud074\ub798\uc2a4\uac00 \uc788\ub294 \ubbf8\ub514\uc5b4 \ud50c\ub808\uc774\uc5b4\uac00 \uc81c\ub300\ub85c \uc791\ub3d9\ud558\ub824\uba74 \uc561\uc138\uc11c\ub9ac \ubaa8\ub4dc\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \"\ud3ec\ud568\ud560 \ub3c4\uba54\uc778\"\uc758 \uad6c\uc131\uc694\uc18c\uac00 HomeKit\uc5d0 \ud3ec\ud568\ub418\uba70, \ub2e4\uc74c \ud654\uba74\uc5d0\uc11c \uc774 \ubaa9\ub85d\uc5d0 \ud3ec\ud568\ud558\uac70\ub098 \uc81c\uc678\ud560 \uad6c\uc131\uc694\uc18c\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", diff --git a/homeassistant/components/homekit/translations/lb.json b/homeassistant/components/homekit/translations/lb.json index 6261d4b5ed4..409d860940d 100644 --- a/homeassistant/components/homekit/translations/lb.json +++ b/homeassistant/components/homekit/translations/lb.json @@ -33,17 +33,8 @@ "description": "Iwwerpr\u00e9if all Kamera op nativ H.264 \u00cbnnerst\u00ebtzung. Falls d'Kamera keng H.264 Stream Ausgab huet, transkod\u00e9iert de System de Video op H.264 fir Homekit. Transkod\u00e9iere ben\u00e9idegt eng performant CPU an w\u00e4ert net anst\u00e4nneg op Single Board Computer funktion\u00e9ieren.", "title": "Kamera Video Codec auswielen." }, - "include_exclude": { - "data": { - "entities": "Entit\u00e9iten", - "mode": "Modus" - }, - "description": "Entit\u00e9iten auswielen d\u00e9i expos\u00e9iert solle ginn. Am Acessoire Modus g\u00ebtt n\u00ebmmen eng eenzeg Entit\u00e9it expos\u00e9iert. Am Bridge include Modus, ginn all Entit\u00e9iten vum Domaine expos\u00e9iert ausser spezifesch ausgewielten Entit\u00e9iten. Am Bride Exclude Modus, ginn all Entit\u00e9ite vum Domaine expos\u00e9iert ausser d\u00e9i ausgeschlossen Entit\u00e9iten.", - "title": "Entit\u00e9iten auswielen d\u00e9i expos\u00e9iert solle ginn" - }, "init": { "data": { - "include_domains": "Domaine d\u00e9i solle ageschloss ginn", "mode": "Modus" }, "description": "HomeKit kann entweder als Bridge oder Single Accessoire konfigur\u00e9iert ginn. Am Acessoire Modus ka n\u00ebmmen eng eenzeg Entit\u00e9it benotzt ginn. D\u00ebse Modus ass n\u00e9ideg fir de korretke Betrib vu Medie Spiller vun der TV Klasse. Entit\u00e9ite an der \"Domaine fir z'expos\u00e9ieren\" ginn mat HomeKit expos\u00e9iert. Du kanns am n\u00e4chste Schr\u00ebtt auswielen w\u00e9ieng Entit\u00e9ite aus oder ageschloss solle ginn.", diff --git a/homeassistant/components/homekit/translations/nl.json b/homeassistant/components/homekit/translations/nl.json index 2e16ffde8ef..7f8ab1e2ff8 100644 --- a/homeassistant/components/homekit/translations/nl.json +++ b/homeassistant/components/homekit/translations/nl.json @@ -55,18 +55,9 @@ "description": "Alle \" {domains} \"-entiteiten zullen worden opgenomen, tenzij specifieke entiteiten zijn geselecteerd.", "title": "Selecteer de entiteiten die moeten worden opgenomen" }, - "include_exclude": { - "data": { - "entities": "Entiteiten", - "mode": "Mode" - }, - "description": "Kies de entiteiten die u wilt opnemen. In de accessoiremodus is slechts een enkele entiteit inbegrepen. In de bridge-include-modus worden alle entiteiten in het domein opgenomen, tenzij specifieke entiteiten zijn geselecteerd. In de bridge-uitsluitingsmodus worden alle entiteiten in het domein opgenomen, behalve de uitgesloten entiteiten. Voor de beste prestaties wordt voor elke media speler, activiteit gebaseerde afstandsbediening, slot en camera een afzonderlijke Homekit-accessoire gemaakt.", - "title": "Selecteer de entiteiten die u wilt opnemen" - }, "init": { "data": { "domains": "Domeinen om op te nemen", - "include_domains": "Domeinen om op te nemen", "include_exclude_mode": "Inclusiemodus", "mode": "HomeKit-modus" }, diff --git a/homeassistant/components/homekit/translations/no.json b/homeassistant/components/homekit/translations/no.json index 88e39d0835e..29f775853fd 100644 --- a/homeassistant/components/homekit/translations/no.json +++ b/homeassistant/components/homekit/translations/no.json @@ -55,18 +55,9 @@ "description": "Alle \" {domains} \"-enheter vil bli inkludert med mindre spesifikke enheter er valgt.", "title": "Velg enhetene som skal inkluderes" }, - "include_exclude": { - "data": { - "entities": "Entiteter", - "mode": "Modus" - }, - "description": "Velg entitetene som skal inkluderes. I tilbeh\u00f8rsmodus er bare en enkelt entitet inkludert. I bridge-inkluderingsmodus vil alle entiteter i domenet bli inkludert, med mindre spesifikke entiteter er valgt. I bridge-ekskluderingsmodus vil alle entiteter i domenet bli inkludert, bortsett fra de ekskluderte entitetene. For best ytelse opprettes et eget HomeKit-tilbeh\u00f8r for hver tv-mediaspiller, aktivitetsbasert fjernkontroll, l\u00e5s og kamera.", - "title": "Velg entiteter som skal inkluderes" - }, "init": { "data": { "domains": "Domener \u00e5 inkludere", - "include_domains": "Domener \u00e5 inkludere", "include_exclude_mode": "Inkluderingsmodus", "mode": "HomeKit-modus" }, diff --git a/homeassistant/components/homekit/translations/pl.json b/homeassistant/components/homekit/translations/pl.json index 745f07bdadb..73ed271d636 100644 --- a/homeassistant/components/homekit/translations/pl.json +++ b/homeassistant/components/homekit/translations/pl.json @@ -55,18 +55,9 @@ "description": "Wszystkie encje \"{domains}\" zostan\u0105 uwzgl\u0119dnione, chyba \u017ce zostan\u0105 wybrane konkretne encje.", "title": "Wybierz encje, kt\u00f3re maj\u0105 zosta\u0107 uwzgl\u0119dnione" }, - "include_exclude": { - "data": { - "entities": "Encje", - "mode": "Tryb" - }, - "description": "Wybierz encje, kt\u00f3re maj\u0105 by\u0107 uwzgl\u0119dnione. W trybie \"Akcesorium\" tylko jedna encja jest uwzgl\u0119dniona. W trybie \"Uwzgl\u0119dnij mostek\", wszystkie encje w danej domenie b\u0119d\u0105 uwzgl\u0119dnione, chyba \u017ce wybrane s\u0105 tylko konkretne encje. W trybie \"Wyklucz mostek\", wszystkie encje b\u0119d\u0105 uwzgl\u0119dnione, z wyj\u0105tkiem tych wybranych. Dla najlepszej wydajno\u015bci, zostanie utworzone oddzielne akcesorium HomeKit dla ka\u017cdego tv media playera, aktywno\u015bci pilota, zamka oraz kamery.", - "title": "Wybierz encje, kt\u00f3re maj\u0105 by\u0107 uwzgl\u0119dnione" - }, "init": { "data": { "domains": "Domeny do uwzgl\u0119dnienia", - "include_domains": "Domeny do uwzgl\u0119dnienia", "include_exclude_mode": "Tryb dodawania", "mode": "Tryb HomeKit" }, diff --git a/homeassistant/components/homekit/translations/pt-BR.json b/homeassistant/components/homekit/translations/pt-BR.json index ad8aab120f4..5fcc2748d0c 100644 --- a/homeassistant/components/homekit/translations/pt-BR.json +++ b/homeassistant/components/homekit/translations/pt-BR.json @@ -55,18 +55,9 @@ "description": "Todas as entidades \"{domains}\" ser\u00e3o inclu\u00eddas, a menos que entidades espec\u00edficas sejam selecionadas.", "title": "Selecione as entidades a serem inclu\u00eddas" }, - "include_exclude": { - "data": { - "entities": "Entidades", - "mode": "Modo" - }, - "description": "Escolha as entidades a serem inclu\u00eddas. No modo acess\u00f3rio, apenas uma \u00fanica entidade est\u00e1 inclu\u00edda. No modo de incluir ponte, todas as entidades do dom\u00ednio ser\u00e3o inclu\u00eddas a menos que entidades espec\u00edficas sejam selecionadas. No modo de exclus\u00e3o da ponte, todas as entidades do dom\u00ednio ser\u00e3o inclu\u00eddas, exceto para as entidades exclu\u00eddas. Para melhor desempenho, um acess\u00f3rio HomeKit separado ser\u00e1 criado para cada leitor de m\u00eddia de TV, controle remoto, bloqueio e c\u00e2mera baseados em atividades.", - "title": "Selecione as entidades a serem inclu\u00eddas" - }, "init": { "data": { - "domains": "Dom\u00ednios a serem inclu\u00eddos", - "include_domains": "Dom\u00ednios para incluir", + "domains": "Dom\u00ednios para incluir", "include_exclude_mode": "Modo de inclus\u00e3o", "mode": "Modo HomeKit" }, diff --git a/homeassistant/components/homekit/translations/pt.json b/homeassistant/components/homekit/translations/pt.json index 920beaa98df..3df84b70866 100644 --- a/homeassistant/components/homekit/translations/pt.json +++ b/homeassistant/components/homekit/translations/pt.json @@ -23,16 +23,8 @@ "cameras": { "title": "Selecione o codec de v\u00eddeo da c\u00e2mera." }, - "include_exclude": { - "data": { - "entities": "Entidades", - "mode": "Modo" - }, - "title": "Selecione as entidades a serem expostas" - }, "init": { "data": { - "include_domains": "Dom\u00ednios a incluir", "mode": "Modo" }, "title": "Selecione os dom\u00ednios a serem expostos." diff --git a/homeassistant/components/homekit/translations/ru.json b/homeassistant/components/homekit/translations/ru.json index 6de8ed572c0..81dd9afa302 100644 --- a/homeassistant/components/homekit/translations/ru.json +++ b/homeassistant/components/homekit/translations/ru.json @@ -55,18 +55,9 @@ "description": "\u0411\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u201c{domains}\u201c, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u044b \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b.", "title": "\u041e\u0431\u044a\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0432 \u0441\u043f\u0438\u0441\u043e\u043a" }, - "include_exclude": { - "data": { - "entities": "\u041e\u0431\u044a\u0435\u043a\u0442\u044b", - "mode": "\u0420\u0435\u0436\u0438\u043c" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432 HomeKit. \u0412 \u0440\u0435\u0436\u0438\u043c\u0435 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043e\u0431\u044a\u0435\u043a\u0442. \u0412 \u0440\u0435\u0436\u0438\u043c\u0435 \u043c\u043e\u0441\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0430\u0449\u0438\u0435 \u0434\u043e\u043c\u0435\u043d\u0443, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b. \u0412 \u0440\u0435\u0436\u0438\u043c\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0430\u0449\u0438\u0435 \u0434\u043e\u043c\u0435\u043d\u0443, \u043a\u0440\u043e\u043c\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0445. \u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043c\u0435\u0434\u0438\u0430\u043f\u043b\u0435\u0435\u0440\u044b, \u043f\u0443\u043b\u044c\u0442\u044b \u0414\u0423 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439, \u0437\u0430\u043c\u043a\u0438 \u0438 \u043a\u0430\u043c\u0435\u0440\u044b \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u0430.", - "title": "\u0412\u044b\u0431\u043e\u0440 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432 HomeKit" - }, "init": { "data": { "domains": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0434\u043e\u043c\u0435\u043d\u044b", - "include_domains": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0434\u043e\u043c\u0435\u043d\u044b", "include_exclude_mode": "\u0420\u0435\u0436\u0438\u043c \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "mode": "\u0420\u0435\u0436\u0438\u043c HomeKit" }, diff --git a/homeassistant/components/homekit/translations/sl.json b/homeassistant/components/homekit/translations/sl.json index dcfc2496011..333ec699253 100644 --- a/homeassistant/components/homekit/translations/sl.json +++ b/homeassistant/components/homekit/translations/sl.json @@ -32,17 +32,7 @@ }, "title": "Izberite video kodek kamere." }, - "include_exclude": { - "data": { - "entities": "Entitete", - "mode": "Na\u010din" - }, - "title": "Izberite entitete, ki jih \u017eelite izpostaviti" - }, "init": { - "data": { - "include_domains": "Domene za vklju\u010ditev" - }, "description": "Subjekti v domenah, ki jih \u017eelite vklju\u010diti, bodo prevezani na HomeKit. Na naslednjem zaslonu boste lahko izbrali subjekte, ki jih \u017eelite izklju\u010diti s tega seznama.", "title": "Izberite domene za premostitev." }, diff --git a/homeassistant/components/homekit/translations/sv.json b/homeassistant/components/homekit/translations/sv.json index 0bc23c456ff..be1e79453e8 100644 --- a/homeassistant/components/homekit/translations/sv.json +++ b/homeassistant/components/homekit/translations/sv.json @@ -19,9 +19,6 @@ "title": "V\u00e4lj kamerans videoavkodare." }, "init": { - "data": { - "include_domains": "Dom\u00e4ner att inkludera" - }, "title": "V\u00e4lj dom\u00e4ner som ska inkluderas." } } diff --git a/homeassistant/components/homekit/translations/tr.json b/homeassistant/components/homekit/translations/tr.json index 49d4a2cf1fa..52683302a34 100644 --- a/homeassistant/components/homekit/translations/tr.json +++ b/homeassistant/components/homekit/translations/tr.json @@ -55,18 +55,9 @@ "description": "Belirli varl\u0131klar se\u00e7ilmedi\u011fi s\u00fcrece t\u00fcm \u201c {domains} \u201d varl\u0131klar\u0131 dahil edilecektir.", "title": "Dahil edilecek varl\u0131klar\u0131 se\u00e7in" }, - "include_exclude": { - "data": { - "entities": "Varl\u0131klar", - "mode": "Mod" - }, - "description": "Dahil edilecek varl\u0131klar\u0131 se\u00e7in. Aksesuar modunda yaln\u0131zca tek bir varl\u0131k dahil edilir. K\u00f6pr\u00fc dahil modunda, belirli varl\u0131klar se\u00e7ilmedi\u011fi s\u00fcrece etki alan\u0131ndaki t\u00fcm varl\u0131klar dahil edilecektir. K\u00f6pr\u00fc hari\u00e7 tutma modunda, hari\u00e7 tutulan varl\u0131klar d\u0131\u015f\u0131nda etki alan\u0131ndaki t\u00fcm varl\u0131klar dahil edilecektir. En iyi performans i\u00e7in, her TV medya oynat\u0131c\u0131, aktivite tabanl\u0131 uzaktan kumanda, kilit ve kamera i\u00e7in ayr\u0131 bir HomeKit aksesuar\u0131 olu\u015fturulacakt\u0131r.", - "title": "Dahil edilecek varl\u0131klar\u0131 se\u00e7in" - }, "init": { "data": { "domains": "\u0130\u00e7erecek etki alanlar\u0131", - "include_domains": "\u0130\u00e7erecek etki alanlar\u0131", "include_exclude_mode": "Ekleme Modu", "mode": "HomeKit Modu" }, diff --git a/homeassistant/components/homekit/translations/uk.json b/homeassistant/components/homekit/translations/uk.json index 0210380ad38..52da83cca73 100644 --- a/homeassistant/components/homekit/translations/uk.json +++ b/homeassistant/components/homekit/translations/uk.json @@ -33,17 +33,8 @@ "description": "\u042f\u043a\u0449\u043e \u043a\u0430\u043c\u0435\u0440\u0430 \u043d\u0435 \u0432\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u043e\u0442\u0456\u043a H.264, \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u043e\u0432\u0443\u0454 \u0432\u0456\u0434\u0435\u043e \u0432 H.264 \u0434\u043b\u044f HomeKit. \u0422\u0440\u0430\u043d\u0441\u043a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0438\u043c\u0430\u0433\u0430\u0454 \u0432\u0438\u0441\u043e\u043a\u043e\u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u043e\u0440\u0430 \u0456 \u043d\u0430\u0432\u0440\u044f\u0434 \u0447\u0438 \u0431\u0443\u0434\u0435 \u043f\u0440\u0430\u0446\u044e\u0432\u0430\u0442\u0438 \u043d\u0430 \u043e\u0434\u043d\u043e\u043f\u043b\u0430\u0442\u043d\u0438\u0445 \u043a\u043e\u043c\u043f'\u044e\u0442\u0435\u0440\u0430\u0445.", "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0432\u0456\u0434\u0435\u043e\u043a\u043e\u0434\u0435\u043a \u043a\u0430\u043c\u0435\u0440\u0438." }, - "include_exclude": { - "data": { - "entities": "\u0421\u0443\u0442\u043d\u043e\u0441\u0442\u0456", - "mode": "\u0420\u0435\u0436\u0438\u043c" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0456 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0456 \u0432 HomeKit. \u0423 \u0440\u0435\u0436\u0438\u043c\u0456 \u0430\u043a\u0441\u0435\u0441\u0443\u0430\u0440\u0430 \u043c\u043e\u0436\u043d\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u0438 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0443 \u0441\u0443\u0442\u043d\u0456\u0441\u0442\u044c. \u0423 \u0440\u0435\u0436\u0438\u043c\u0456 \u043c\u043e\u0441\u0442\u0430 \u0431\u0443\u0434\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0456 \u0432\u0441\u0456 \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0456, \u0449\u043e \u043d\u0430\u043b\u0435\u0436\u0430\u0442\u044c \u0434\u043e\u043c\u0435\u043d\u0443, \u044f\u043a\u0449\u043e \u043d\u0435 \u0432\u0438\u0431\u0440\u0430\u043d\u0456 \u043f\u0435\u0432\u043d\u0456 \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0456. \u0423 \u0440\u0435\u0436\u0438\u043c\u0456 \u0432\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0431\u0443\u0434\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0456 \u0432\u0441\u0456 \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0456, \u0449\u043e \u043d\u0430\u043b\u0435\u0436\u0430\u0442\u044c \u0434\u043e\u043c\u0435\u043d\u0443, \u043a\u0440\u0456\u043c \u0432\u0438\u0431\u0440\u0430\u043d\u0438\u0445.", - "title": "\u0412\u0438\u0431\u0456\u0440 \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0456 \u0432 HomeKit" - }, "init": { "data": { - "include_domains": "\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0434\u043e\u043c\u0435\u043d\u0438", "mode": "\u0420\u0435\u0436\u0438\u043c" }, "description": "\u0406\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044f \u0437 HomeKit \u043c\u043e\u0436\u0435 \u0431\u0443\u0442\u0438 \u0432\u0438\u043a\u043e\u043d\u0430\u043d\u0430 \u0432 \u0440\u0435\u0436\u0438\u043c\u0456 \u043c\u043e\u0441\u0442\u0430 \u0430\u0431\u043e \u044f\u043a \u043e\u043a\u0440\u0435\u043c\u0438\u0439 \u0430\u043a\u0441\u0435\u0441\u0443\u0430\u0440. \u0423 \u0440\u0435\u0436\u0438\u043c\u0456 \u0430\u043a\u0441\u0435\u0441\u0443\u0430\u0440\u0430 \u043c\u043e\u0436\u0435 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u0442\u0456\u043b\u044c\u043a\u0438 \u043e\u0434\u0438\u043d \u043e\u0431'\u0454\u043a\u0442. \u041c\u0435\u0434\u0456\u0430\u043f\u043b\u0435\u0454\u0440\u0438, \u044f\u043a\u0456 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u044e\u0442\u044c\u0441\u044f \u0432 Home Assistant \u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c 'device_class: tv', \u0434\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0457 \u0440\u043e\u0431\u043e\u0442\u0438 \u043f\u043e\u0432\u0438\u043d\u043d\u0456 \u0431\u0443\u0442\u0438 \u0456\u043d\u0442\u0435\u0433\u0440\u043e\u0432\u0430\u043d\u0456 \u0432 Homekit \u0432 \u0440\u0435\u0436\u0438\u043c\u0456 \u0430\u043a\u0441\u0435\u0441\u0443\u0430\u0440\u0430. \u041e\u0431'\u0454\u043a\u0442\u0438, \u0449\u043e \u043d\u0430\u043b\u0435\u0436\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u043d\u0438\u043c \u0434\u043e\u043c\u0435\u043d\u0430\u043c, \u0431\u0443\u0434\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0456 \u0432 HomeKit. \u041d\u0430 \u043d\u0430\u0441\u0442\u0443\u043f\u043d\u043e\u043c\u0443 \u0435\u0442\u0430\u043f\u0456 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0412\u0438 \u0437\u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u0438\u0431\u0440\u0430\u0442\u0438, \u044f\u043a\u0456 \u043e\u0431'\u0454\u043a\u0442\u0438 \u0432\u0438\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0437 \u0446\u0438\u0445 \u0434\u043e\u043c\u0435\u043d\u0456\u0432.", diff --git a/homeassistant/components/homekit/translations/zh-Hans.json b/homeassistant/components/homekit/translations/zh-Hans.json index 3f6d005f045..852979fb12a 100644 --- a/homeassistant/components/homekit/translations/zh-Hans.json +++ b/homeassistant/components/homekit/translations/zh-Hans.json @@ -55,18 +55,9 @@ "description": "\u9664\u975e\u5df2\u9009\u62e9\u4e86\u6307\u5b9a\u7684\u5b9e\u4f53\uff0c\u5426\u5219\u6240\u6709\u201c{domains}\u201d\u7c7b\u578b\u7684\u5b9e\u4f53\u90fd\u5c06\u88ab\u5305\u542b\u3002", "title": "\u9009\u62e9\u8981\u5305\u542b\u7684\u5b9e\u4f53" }, - "include_exclude": { - "data": { - "entities": "\u5b9e\u4f53", - "mode": "\u6a21\u5f0f" - }, - "description": "\u9009\u62e9\u8981\u5f00\u653e\u7684\u5b9e\u4f53\u3002\n\u5728\u914d\u4ef6\u6a21\u5f0f\u4e2d\uff0c\u53ea\u80fd\u5f00\u653e\u4e00\u4e2a\u5b9e\u4f53\u3002\u5728\u6865\u63a5\u5305\u542b\u6a21\u5f0f\u4e2d\uff0c\u5982\u679c\u4e0d\u9009\u62e9\u5305\u542b\u7684\u5b9e\u4f53\uff0c\u57df\u4e2d\u6240\u6709\u5b9e\u4f53\u90fd\u4f1a\u5f00\u653e\u3002\u5728\u6865\u63a5\u6392\u9664\u6a21\u5f0f\u4e2d\uff0c\u5982\u679c\u4e0d\u9009\u62e9\u6392\u9664\u7684\u5b9e\u4f53\uff0c\u57df\u4e2d\u6240\u6709\u5b9e\u4f53\u4e5f\u90fd\u4f1a\u5f00\u653e\u3002\n\u4e3a\u83b7\u5f97\u6700\u4f73\u4f53\u9a8c\uff0c\u5c06\u4f1a\u4e3a\u6bcf\u4e2a\u7535\u89c6\u5a92\u4f53\u64ad\u653e\u5668\u3001\u57fa\u4e8e\u6d3b\u52a8\u7684\u9065\u63a7\u5668\u3001\u9501\u548c\u6444\u50cf\u5934\u521b\u5efa\u5355\u72ec\u7684 HomeKit \u914d\u4ef6\u3002", - "title": "\u9009\u62e9\u8981\u5305\u542b\u7684\u5b9e\u4f53" - }, "init": { "data": { "domains": "\u8981\u5305\u542b\u7684\u57df", - "include_domains": "\u8981\u5305\u542b\u7684\u57df", "include_exclude_mode": "\u5305\u542b\u6a21\u5f0f", "mode": "HomeKit \u6a21\u5f0f" }, diff --git a/homeassistant/components/homekit/translations/zh-Hant.json b/homeassistant/components/homekit/translations/zh-Hant.json index b5aa54c72c4..3ad28a22f45 100644 --- a/homeassistant/components/homekit/translations/zh-Hant.json +++ b/homeassistant/components/homekit/translations/zh-Hant.json @@ -55,18 +55,9 @@ "description": "\u9664\u975e\u9078\u64c7\u7279\u5b9a\u5be6\u9ad4\u5916\uff0c\u5c07\u6703\u5305\u542b\u6240\u6709 \u201c{domains}\u201d \u5167\u5be6\u9ad4\u3002", "title": "\u9078\u64c7\u8981\u5305\u542b\u7684\u5be6\u9ad4" }, - "include_exclude": { - "data": { - "entities": "\u5be6\u9ad4", - "mode": "\u6a21\u5f0f" - }, - "description": "\u9078\u64c7\u8981\u5305\u542b\u7684\u5be6\u9ad4\u3002\u65bc\u914d\u4ef6\u6a21\u5f0f\u4e0b\u3001\u50c5\u6709\u55ae\u4e00\u5be6\u9ad4\u5c07\u6703\u5305\u542b\u3002\u65bc\u6a4b\u63a5\u5305\u542b\u6a21\u5f0f\u4e0b\u3001\u6240\u6709\u7db2\u57df\u7684\u5be6\u9ad4\u90fd\u5c07\u5305\u542b\uff0c\u9664\u975e\u9078\u64c7\u7279\u5b9a\u7684\u5be6\u9ad4\u3002\u65bc\u6a4b\u63a5\u6392\u9664\u6a21\u5f0f\u4e2d\u3001\u6240\u6709\u7db2\u57df\u4e2d\u7684\u5be6\u9ad4\u90fd\u5c07\u5305\u542b\uff0c\u9664\u4e86\u6392\u9664\u7684\u5be6\u9ad4\u3002\u70ba\u53d6\u5f97\u6700\u4f73\u6548\u80fd\u3001\u6bcf\u4e00\u500b\u96fb\u8996\u5a92\u9ad4\u64ad\u653e\u5668\u3001\u9060\u7aef\u9059\u63a7\u5668\u3001\u9580\u9396\u8207\u651d\u5f71\u6a5f\uff0c\u5c07\u65bc Homekit \u914d\u4ef6\u6a21\u5f0f\u9032\u884c\u3002", - "title": "\u9078\u64c7\u8981\u5305\u542b\u7684\u5be6\u9ad4" - }, "init": { "data": { "domains": "\u5305\u542b\u7db2\u57df", - "include_domains": "\u5305\u542b\u7db2\u57df", "include_exclude_mode": "\u5305\u542b\u6a21\u5f0f", "mode": "HomeKit \u6a21\u5f0f" }, diff --git a/homeassistant/components/homekit_controller/translations/hu.json b/homeassistant/components/homekit_controller/translations/hu.json index 6fad9050a20..607c46ede6d 100644 --- a/homeassistant/components/homekit_controller/translations/hu.json +++ b/homeassistant/components/homekit_controller/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "accessory_not_found_error": "Nem adhat\u00f3 hozz\u00e1 p\u00e1ros\u00edt\u00e1s, mert az eszk\u00f6z m\u00e1r nem tal\u00e1lhat\u00f3.", "already_configured": "A tartoz\u00e9k m\u00e1r konfigur\u00e1lva van ezzel a vez\u00e9rl\u0151vel.", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "already_paired": "Ez a tartoz\u00e9k m\u00e1r p\u00e1ros\u00edtva van egy m\u00e1sik eszk\u00f6zzel. \u00c1ll\u00edtsa alaphelyzetbe a tartoz\u00e9kot, majd pr\u00f3b\u00e1lkozzon \u00fajra.", "ignored_model": "A HomeKit t\u00e1mogat\u00e1sa e modelln\u00e9l blokkolva van, mivel a szolg\u00e1ltat\u00e1shoz teljes nat\u00edv integr\u00e1ci\u00f3 \u00e9rhet\u0151 el.", "invalid_config_entry": "Ez az eszk\u00f6z k\u00e9szen \u00e1ll a p\u00e1ros\u00edt\u00e1sra, de m\u00e1r van egy \u00fctk\u00f6z\u0151 konfigur\u00e1ci\u00f3s bejegyz\u00e9s Home Assistantban, amelyet el\u0151sz\u00f6r el kell t\u00e1vol\u00edtani.", diff --git a/homeassistant/components/homematicip_cloud/translations/fr.json b/homeassistant/components/homematicip_cloud/translations/fr.json index 01e1d1ed8c2..dd85878991f 100644 --- a/homeassistant/components/homematicip_cloud/translations/fr.json +++ b/homeassistant/components/homematicip_cloud/translations/fr.json @@ -6,7 +6,7 @@ "unknown": "Erreur inattendue" }, "error": { - "invalid_sgtin_or_pin": "Code SGTIN ou PIN invalide, veuillez r\u00e9essayer.", + "invalid_sgtin_or_pin": "Code SGTIN ou PIN non valide, veuillez r\u00e9essayer.", "press_the_button": "Veuillez appuyer sur le bouton bleu.", "register_failed": "\u00c9chec d'enregistrement. Veuillez r\u00e9essayer.", "timeout_button": "D\u00e9lai d'attente expir\u00e9, veuillez r\u00e9\u00e9ssayer." diff --git a/homeassistant/components/homematicip_cloud/translations/hu.json b/homeassistant/components/homematicip_cloud/translations/hu.json index 2915d442a37..975daa36126 100644 --- a/homeassistant/components/homematicip_cloud/translations/hu.json +++ b/homeassistant/components/homematicip_cloud/translations/hu.json @@ -15,7 +15,7 @@ "init": { "data": { "hapid": "Hozz\u00e1f\u00e9r\u00e9si pont azonos\u00edt\u00f3ja (SGTIN)", - "name": "N\u00e9v (opcion\u00e1lis, minden eszk\u00f6z n\u00e9vel\u0151tagjak\u00e9nt haszn\u00e1latos)", + "name": "Elnevez\u00e9s (opcion\u00e1lis, minden eszk\u00f6z n\u00e9vel\u0151tagjak\u00e9nt haszn\u00e1latos)", "pin": "PIN-k\u00f3d" }, "title": "V\u00e1lasszon HomematicIP hozz\u00e1f\u00e9r\u00e9si pontot" diff --git a/homeassistant/components/homematicip_cloud/translations/it.json b/homeassistant/components/homematicip_cloud/translations/it.json index 55c26532fb5..88a2dcd5fc8 100644 --- a/homeassistant/components/homematicip_cloud/translations/it.json +++ b/homeassistant/components/homematicip_cloud/translations/it.json @@ -6,7 +6,7 @@ "unknown": "Errore imprevisto" }, "error": { - "invalid_sgtin_or_pin": "SGTIN o Codice PIN non valido, si prega di riprovare.", + "invalid_sgtin_or_pin": "SGTIN o Codice PIN non valido, riprova.", "press_the_button": "Premi il pulsante blu.", "register_failed": "Registrazione non riuscita, riprova.", "timeout_button": "Timeout della pressione del pulsante blu, riprovare." diff --git a/homeassistant/components/honeywell/translations/ca.json b/homeassistant/components/honeywell/translations/ca.json index 34da1b89f10..0830d657173 100644 --- a/homeassistant/components/honeywell/translations/ca.json +++ b/homeassistant/components/honeywell/translations/ca.json @@ -9,8 +9,18 @@ "password": "Contrasenya", "username": "Nom d'usuari" }, - "description": "Introdueix les credencials utilitzades per iniciar sessi\u00f3 a mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (EUA)" + "description": "Introdueix les credencials utilitzades per iniciar sessi\u00f3 a mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Temperatura freda, mode fora", + "away_heat_temperature": "Temperatura calenta, mode fora" + }, + "description": "Opcions addicionals de configuraci\u00f3 de Honeywell. Les temperatures es configuren en Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/de.json b/homeassistant/components/honeywell/translations/de.json index a146d442eef..6cbceffce51 100644 --- a/homeassistant/components/honeywell/translations/de.json +++ b/homeassistant/components/honeywell/translations/de.json @@ -9,8 +9,18 @@ "password": "Passwort", "username": "Benutzername" }, - "description": "Bitte gib die Anmeldedaten ein, mit denen du dich bei mytotalconnectcomfort.com anmeldest.", - "title": "Honeywell Total Connect Comfort (US)" + "description": "Bitte gib die Anmeldedaten ein, mit denen du dich bei mytotalconnectcomfort.com anmeldest." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Abwesend K\u00fchlungstemperatur", + "away_heat_temperature": "Abwesend Heiztemperatur" + }, + "description": "Zus\u00e4tzliche Honeywell-Konfigurationsoptionen. Die Temperaturen werden in Fahrenheit eingestellt." } } } diff --git a/homeassistant/components/honeywell/translations/el.json b/homeassistant/components/honeywell/translations/el.json index 7ad0c5b0181..b3fd654ded8 100644 --- a/homeassistant/components/honeywell/translations/el.json +++ b/homeassistant/components/honeywell/translations/el.json @@ -9,8 +9,18 @@ "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, - "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b1\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (\u0397\u03a0\u0391)" + "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b1\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03c8\u03cd\u03be\u03b7\u03c2 \u03bc\u03b1\u03ba\u03c1\u03b9\u03ac", + "away_heat_temperature": "\u0398\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03b8\u03b5\u03c1\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03bc\u03b1\u03ba\u03c1\u03b9\u03ac" + }, + "description": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 Honeywell. \u039f\u03b9 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b5\u03c2 \u03bf\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1\u03b9 \u03c3\u03b5 Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/en.json b/homeassistant/components/honeywell/translations/en.json index 168d3a5b93d..bf47a15be55 100644 --- a/homeassistant/components/honeywell/translations/en.json +++ b/homeassistant/components/honeywell/translations/en.json @@ -9,8 +9,7 @@ "password": "Password", "username": "Username" }, - "description": "Please enter the credentials used to log into mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (US)" + "description": "Please enter the credentials used to log into mytotalconnectcomfort.com." } } }, @@ -21,8 +20,7 @@ "away_cool_temperature": "Away cool temperature", "away_heat_temperature": "Away heat temperature" }, - "description": "Additional Honeywell config options. Temperatures are set in Fahrenheit.", - "title": "Honeywell Options" + "description": "Additional Honeywell config options. Temperatures are set in Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/es.json b/homeassistant/components/honeywell/translations/es.json index 9f6c562e888..bdae26b8794 100644 --- a/homeassistant/components/honeywell/translations/es.json +++ b/homeassistant/components/honeywell/translations/es.json @@ -9,8 +9,7 @@ "password": "Contrase\u00f1a", "username": "Usuario" }, - "description": "Por favor, introduzca las credenciales utilizadas para iniciar sesi\u00f3n en mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (US)" + "description": "Por favor, introduzca las credenciales utilizadas para iniciar sesi\u00f3n en mytotalconnectcomfort.com." } } } diff --git a/homeassistant/components/honeywell/translations/et.json b/homeassistant/components/honeywell/translations/et.json index 264a1efeca5..6673959ad31 100644 --- a/homeassistant/components/honeywell/translations/et.json +++ b/homeassistant/components/honeywell/translations/et.json @@ -9,8 +9,18 @@ "password": "Salas\u00f5na", "username": "Kasutajanimi" }, - "description": "Sisesta saidile mytotalconnectcomfort.com sisenemiseks kasutatav mandaat.", - "title": "Honeywell Total Connect Comfort (USA)" + "description": "Sisesta saidile mytotalconnectcomfort.com sisenemiseks kasutatav mandaat." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Eemaloleku jahutuse temperatuur", + "away_heat_temperature": "Eemaloleku k\u00fctte temperatuur" + }, + "description": "T\u00e4iendavad Honeywelli seadistusv\u00f5imalused. Temperatuurid on Fahrenheiti kraadides." } } } diff --git a/homeassistant/components/honeywell/translations/fr.json b/homeassistant/components/honeywell/translations/fr.json index ac11cf20576..0043e8990f1 100644 --- a/homeassistant/components/honeywell/translations/fr.json +++ b/homeassistant/components/honeywell/translations/fr.json @@ -9,8 +9,18 @@ "password": "Mot de passe", "username": "Nom d'utilisateur" }, - "description": "Veuillez saisir les informations d'identification utilis\u00e9es pour vous connecter \u00e0 mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (\u00c9tats-Unis)" + "description": "Veuillez saisir les informations d'identification utilis\u00e9es pour vous connecter \u00e0 mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Temp\u00e9rature de refroidissement en absence", + "away_heat_temperature": "Temp\u00e9rature de chauffage en absence" + }, + "description": "Options de configuration Honeywell suppl\u00e9mentaires. Les temp\u00e9ratures sont exprim\u00e9es en degr\u00e9s Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/hu.json b/homeassistant/components/honeywell/translations/hu.json index 5583dc22f2e..14ba9167ea5 100644 --- a/homeassistant/components/honeywell/translations/hu.json +++ b/homeassistant/components/honeywell/translations/hu.json @@ -9,8 +9,18 @@ "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "K\u00e9rj\u00fck, adja meg a mytotalconnectcomfort.com webhelyre val\u00f3 bejelentkez\u00e9shez haszn\u00e1lt hiteles\u00edt\u0151 adatokat.", - "title": "Honeywell Total Connect Comfort (USA)" + "description": "K\u00e9rj\u00fck, adja meg a mytotalconnectcomfort.com webhelyre val\u00f3 bejelentkez\u00e9shez haszn\u00e1lt hiteles\u00edt\u0151 adatokat." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "H\u0171t\u00e9si h\u0151m\u00e9r\u00e9s\u00e9klet t\u00e1voll\u00e9ti m\u00f3dban", + "away_heat_temperature": "F\u0171t\u00e9si h\u0151m\u00e9r\u00e9s\u00e9klet t\u00e1voll\u00e9ti m\u00f3dban" + }, + "description": "Tov\u00e1bbi Honeywell konfigur\u00e1ci\u00f3s lehet\u0151s\u00e9gek. A h\u0151m\u00e9rs\u00e9kletek Fahrenheitben vannak be\u00e1ll\u00edtva." } } } diff --git a/homeassistant/components/honeywell/translations/id.json b/homeassistant/components/honeywell/translations/id.json index 62151da1481..5ed95a27a76 100644 --- a/homeassistant/components/honeywell/translations/id.json +++ b/homeassistant/components/honeywell/translations/id.json @@ -9,8 +9,18 @@ "password": "Kata Sandi", "username": "Nama Pengguna" }, - "description": "Masukkan kredensial yang digunakan untuk masuk ke mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (AS)" + "description": "Masukkan kredensial yang digunakan untuk masuk ke mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Suhu dingin ketika jauh", + "away_heat_temperature": "Suhu panas ketika jauh" + }, + "description": "Opsi konfigurasi Honeywell tambahan. Suhu diatur dalam Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/it.json b/homeassistant/components/honeywell/translations/it.json index 52c828ddcde..87669762fa2 100644 --- a/homeassistant/components/honeywell/translations/it.json +++ b/homeassistant/components/honeywell/translations/it.json @@ -9,8 +9,18 @@ "password": "Password", "username": "Nome utente" }, - "description": "Inserisci le credenziali utilizzate per accedere a mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (USA)" + "description": "Inserisci le credenziali utilizzate per accedere a mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Fuori casa temperatura fresca", + "away_heat_temperature": "Fuori casa temperatura calda" + }, + "description": "Ulteriori opzioni di configurazione di Honeywell. Le temperature sono impostate in Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/ja.json b/homeassistant/components/honeywell/translations/ja.json index 1d3e19750c6..1af30341d33 100644 --- a/homeassistant/components/honeywell/translations/ja.json +++ b/homeassistant/components/honeywell/translations/ja.json @@ -9,8 +9,18 @@ "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, - "description": "mytotalconnectcomfort.com \u306b\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3059\u308b\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Honeywell Total Connect Comfort (US)" + "description": "mytotalconnectcomfort.com \u306b\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u305f\u3081\u306b\u4f7f\u7528\u3059\u308b\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "\u30a2\u30a6\u30a7\u30a4\u30af\u30fc\u30eb(Away cool)\u6e29\u5ea6", + "away_heat_temperature": "\u30a2\u30a6\u30a7\u30a4\u30d2\u30fc\u30c8(Away heat)\u6e29\u5ea6" + }, + "description": "Honeywell\u306e\u8ffd\u52a0\u8a2d\u5b9a\u30aa\u30d7\u30b7\u30e7\u30f3\u3002\u6e29\u5ea6\u306f\u83ef\u6c0f(\u00b0F)\u3067\u8a2d\u5b9a\u3055\u308c\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/honeywell/translations/nl.json b/homeassistant/components/honeywell/translations/nl.json index 0abd80fa088..9d82ef53b21 100644 --- a/homeassistant/components/honeywell/translations/nl.json +++ b/homeassistant/components/honeywell/translations/nl.json @@ -9,8 +9,18 @@ "password": "Wachtwoord", "username": "Gebruikersnaam" }, - "description": "Voer de inloggegevens in die zijn gebruikt om in te loggen op mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (US)" + "description": "Voer de inloggegevens in die zijn gebruikt om in te loggen op mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Afwezig koeltemperatuur", + "away_heat_temperature": "Afwezig warmte temperatuur" + }, + "description": "Extra Honeywell configuratie opties. Temperaturen zijn ingesteld in Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/no.json b/homeassistant/components/honeywell/translations/no.json index 97d31d34961..e35e8a2b278 100644 --- a/homeassistant/components/honeywell/translations/no.json +++ b/homeassistant/components/honeywell/translations/no.json @@ -9,8 +9,18 @@ "password": "Passord", "username": "Brukernavn" }, - "description": "Vennligst skriv inn legitimasjonen som brukes for \u00e5 logge deg p\u00e5 mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (USA)" + "description": "Vennligst skriv inn legitimasjonen som brukes for \u00e5 logge deg p\u00e5 mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Borte kj\u00f8lig temperatur", + "away_heat_temperature": "Borte varmetemperatur" + }, + "description": "Ytterligere Honeywell-konfigurasjonsalternativer. Temperaturene er satt i Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/pl.json b/homeassistant/components/honeywell/translations/pl.json index c109565e33a..e484f88cb49 100644 --- a/homeassistant/components/honeywell/translations/pl.json +++ b/homeassistant/components/honeywell/translations/pl.json @@ -9,8 +9,18 @@ "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, - "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce u\u017cywane na mytotalconnectcomfort.com", - "title": "Honeywell Total Connect Comfort (USA)" + "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce u\u017cywane na mytotalconnectcomfort.com" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Temperatura ch\u0142odzenia w trybie \"poza domem\"", + "away_heat_temperature": "Temperatura grzania w trybie \"poza domem\"" + }, + "description": "Dodatkowe opcje konfiguracji Honeywell. Temperatury s\u0105 ustawiane w stopniach Fahrenheita." } } } diff --git a/homeassistant/components/honeywell/translations/pt-BR.json b/homeassistant/components/honeywell/translations/pt-BR.json index f16a6c71637..4cae96c5b1b 100644 --- a/homeassistant/components/honeywell/translations/pt-BR.json +++ b/homeassistant/components/honeywell/translations/pt-BR.json @@ -9,8 +9,18 @@ "password": "Senha", "username": "Usu\u00e1rio" }, - "description": "Insira as credenciais usadas para fazer login em mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (EUA)" + "description": "Insira as credenciais usadas para fazer login em mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Temperatura de frio ausente", + "away_heat_temperature": "Temperatura de calor ausente" + }, + "description": "Op\u00e7\u00f5es adicionais de configura\u00e7\u00e3o Honeywell. As temperaturas s\u00e3o definidas em Fahrenheit." } } } diff --git a/homeassistant/components/honeywell/translations/ru.json b/homeassistant/components/honeywell/translations/ru.json index 1d775e6c2c7..b370df892f6 100644 --- a/homeassistant/components/honeywell/translations/ru.json +++ b/homeassistant/components/honeywell/translations/ru.json @@ -9,8 +9,18 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043d\u0430 mytotalconnectcomfort.com.", - "title": "Honeywell Total Connect Comfort (\u0421\u0428\u0410)" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043d\u0430 mytotalconnectcomfort.com." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \"\u041d\u0435 \u0434\u043e\u043c\u0430\"", + "away_heat_temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043d\u0430\u0433\u0440\u0435\u0432\u0430 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \"\u041d\u0435 \u0434\u043e\u043c\u0430\"" + }, + "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Honeywell. \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445 \u0424\u0430\u0440\u0435\u043d\u0433\u0435\u0439\u0442\u0430." } } } diff --git a/homeassistant/components/honeywell/translations/tr.json b/homeassistant/components/honeywell/translations/tr.json index e6eb57aca1f..d0c02e5029f 100644 --- a/homeassistant/components/honeywell/translations/tr.json +++ b/homeassistant/components/honeywell/translations/tr.json @@ -9,8 +9,18 @@ "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "L\u00fctfen mytotalconnectcomfort.com'da oturum a\u00e7mak i\u00e7in kullan\u0131lan kimlik bilgilerini girin.", - "title": "Honeywell Toplam Ba\u011flant\u0131 Konforu (ABD)" + "description": "L\u00fctfen mytotalconnectcomfort.com'da oturum a\u00e7mak i\u00e7in kullan\u0131lan kimlik bilgilerini girin." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Uzak so\u011futma derecesi", + "away_heat_temperature": "Uzak \u0131s\u0131tma derecesi" + }, + "description": "Ek Honeywell yap\u0131land\u0131rma se\u00e7enekleri. S\u0131cakl\u0131klar Fahrenheit cinsinden ayarlan\u0131r." } } } diff --git a/homeassistant/components/honeywell/translations/zh-Hant.json b/homeassistant/components/honeywell/translations/zh-Hant.json index 906506d41a5..c6a657b13be 100644 --- a/homeassistant/components/honeywell/translations/zh-Hant.json +++ b/homeassistant/components/honeywell/translations/zh-Hant.json @@ -9,8 +9,18 @@ "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "\u8acb\u8f38\u5165\u767b\u5165 mytotalconnectcomfort.com \u4e4b\u6191\u8b49\u3002", - "title": "Honeywell Total Connect Comfort\uff08\u7f8e\u570b\uff09" + "description": "\u8acb\u8f38\u5165\u767b\u5165 mytotalconnectcomfort.com \u4e4b\u6191\u8b49\u3002" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "\u96e2\u5bb6\u5236\u51b7\u6eab\u5ea6", + "away_heat_temperature": "\u96e2\u5bb6\u52a0\u71b1\u6eab\u5ea6" + }, + "description": "\u9644\u52a0 Honeywell \u8a2d\u5b9a\u9078\u9805\u3002\u6eab\u5ea6\u4ee5\u83ef\u6c0f\u9032\u884c\u8a2d\u5b9a\u3002" } } } diff --git a/homeassistant/components/huawei_lte/translations/hu.json b/homeassistant/components/huawei_lte/translations/hu.json index 36d08438fca..b3cc71d5a9d 100644 --- a/homeassistant/components/huawei_lte/translations/hu.json +++ b/homeassistant/components/huawei_lte/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "not_huawei_lte": "Nem Huawei LTE eszk\u00f6z" }, "error": { diff --git a/homeassistant/components/hue/translations/ca.json b/homeassistant/components/hue/translations/ca.json index 92fb30b15d9..3b192e80b00 100644 --- a/homeassistant/components/hue/translations/ca.json +++ b/homeassistant/components/hue/translations/ca.json @@ -6,6 +6,7 @@ "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "cannot_connect": "Ha fallat la connexi\u00f3", "discover_timeout": "No s'han pogut descobrir enlla\u00e7os Hue", + "invalid_host": "Amfitri\u00f3 inv\u00e0lid", "no_bridges": "No s'han trobat enlla\u00e7os Philips Hue", "not_hue_bridge": "No \u00e9s un enlla\u00e7 Hue", "unknown": "Error inesperat" diff --git a/homeassistant/components/hue/translations/de.json b/homeassistant/components/hue/translations/de.json index 2f8485aae0c..c28014031cc 100644 --- a/homeassistant/components/hue/translations/de.json +++ b/homeassistant/components/hue/translations/de.json @@ -6,6 +6,7 @@ "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "cannot_connect": "Verbindung fehlgeschlagen", "discover_timeout": "Es k\u00f6nnen keine Hue Bridges erkannt werden", + "invalid_host": "Ung\u00fcltiger Host", "no_bridges": "Keine Philips Hue Bridges erkannt", "not_hue_bridge": "Keine Philips Hue Bridge entdeckt", "unknown": "Unerwarteter Fehler" diff --git a/homeassistant/components/hue/translations/el.json b/homeassistant/components/hue/translations/el.json index 99bdc934929..7d1c2cabd44 100644 --- a/homeassistant/components/hue/translations/el.json +++ b/homeassistant/components/hue/translations/el.json @@ -6,6 +6,7 @@ "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "discover_timeout": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03bf\u03cd \u03b3\u03b5\u03c6\u03c5\u03c1\u03ce\u03bd Hue", + "invalid_host": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "no_bridges": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03b3\u03ad\u03c6\u03c5\u03c1\u03b5\u03c2 Philips Hue", "not_hue_bridge": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b3\u03ad\u03c6\u03c5\u03c1\u03b1 Hue", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" diff --git a/homeassistant/components/hue/translations/en.json b/homeassistant/components/hue/translations/en.json index 7757aca9373..f617be431b9 100644 --- a/homeassistant/components/hue/translations/en.json +++ b/homeassistant/components/hue/translations/en.json @@ -6,6 +6,7 @@ "already_in_progress": "Configuration flow is already in progress", "cannot_connect": "Failed to connect", "discover_timeout": "Unable to discover Hue bridges", + "invalid_host": "Invalid host", "no_bridges": "No Philips Hue bridges discovered", "not_hue_bridge": "Not a Hue bridge", "unknown": "Unexpected error" diff --git a/homeassistant/components/hue/translations/et.json b/homeassistant/components/hue/translations/et.json index 748c5726dfe..df1288d415d 100644 --- a/homeassistant/components/hue/translations/et.json +++ b/homeassistant/components/hue/translations/et.json @@ -6,6 +6,7 @@ "already_in_progress": "Seadistamine on juba k\u00e4imas", "cannot_connect": "\u00dchendus nurjus", "discover_timeout": "Ei leia Philips Hue sildu", + "invalid_host": "Kehtetu host", "no_bridges": "Philips Hue sildu ei avastatud", "not_hue_bridge": "See pole Hue sild", "unknown": "Ilmnes tundmatu viga" diff --git a/homeassistant/components/hue/translations/fr.json b/homeassistant/components/hue/translations/fr.json index ec9d104aa65..cd07cff9fea 100644 --- a/homeassistant/components/hue/translations/fr.json +++ b/homeassistant/components/hue/translations/fr.json @@ -6,6 +6,7 @@ "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "cannot_connect": "\u00c9chec de connexion", "discover_timeout": "D\u00e9tection de ponts Philips Hue impossible", + "invalid_host": "H\u00f4te non valide", "no_bridges": "Aucun pont Philips Hue n'a \u00e9t\u00e9 d\u00e9couvert", "not_hue_bridge": "Pas de pont Hue", "unknown": "Erreur inattendue" diff --git a/homeassistant/components/hue/translations/hu.json b/homeassistant/components/hue/translations/hu.json index 02b8652b253..dd5c13c5f59 100644 --- a/homeassistant/components/hue/translations/hu.json +++ b/homeassistant/components/hue/translations/hu.json @@ -3,9 +3,10 @@ "abort": { "all_configured": "M\u00e1r minden Philips Hue bridge konfigur\u00e1lt", "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "discover_timeout": "Nem tal\u00e1lhat\u00f3 a Hue bridge", + "invalid_host": "\u00c9rv\u00e9nytelen c\u00edm", "no_bridges": "Nem tal\u00e1lhat\u00f3 Philips Hue bridget", "not_hue_bridge": "Nem egy Hue Bridge", "unknown": "Ismeretlen hiba t\u00f6rt\u00e9nt" diff --git a/homeassistant/components/hue/translations/id.json b/homeassistant/components/hue/translations/id.json index d7178f6d135..d450adfb3c4 100644 --- a/homeassistant/components/hue/translations/id.json +++ b/homeassistant/components/hue/translations/id.json @@ -6,6 +6,7 @@ "already_in_progress": "Alur konfigurasi sedang berlangsung", "cannot_connect": "Gagal terhubung", "discover_timeout": "Tidak dapat menemukan bridge Hue", + "invalid_host": "Host tidak valid", "no_bridges": "Bridge Philips Hue tidak ditemukan", "not_hue_bridge": "Bukan bridge Hue", "unknown": "Kesalahan yang tidak diharapkan" diff --git a/homeassistant/components/hue/translations/it.json b/homeassistant/components/hue/translations/it.json index fdafef01f5b..0c1dfce4c1a 100644 --- a/homeassistant/components/hue/translations/it.json +++ b/homeassistant/components/hue/translations/it.json @@ -6,6 +6,7 @@ "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "cannot_connect": "Impossibile connettersi", "discover_timeout": "Impossibile trovare i bridge Hue", + "invalid_host": "Host non valido", "no_bridges": "Nessun bridge di Philips Hue trovato", "not_hue_bridge": "Non \u00e8 un bridge Hue", "unknown": "Errore imprevisto" diff --git a/homeassistant/components/hue/translations/nl.json b/homeassistant/components/hue/translations/nl.json index a7d29534218..56bac6b89d8 100644 --- a/homeassistant/components/hue/translations/nl.json +++ b/homeassistant/components/hue/translations/nl.json @@ -6,6 +6,7 @@ "already_in_progress": "De configuratiestroom is al aan de gang", "cannot_connect": "Kan geen verbinding maken", "discover_timeout": "Hue bridges kunnen niet worden gevonden", + "invalid_host": "Ongeldige host", "no_bridges": "Geen Philips Hue bridges ontdekt", "not_hue_bridge": "Dit is geen Hue bridge", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/hue/translations/no.json b/homeassistant/components/hue/translations/no.json index 08c19a9cc51..9c18003aca0 100644 --- a/homeassistant/components/hue/translations/no.json +++ b/homeassistant/components/hue/translations/no.json @@ -6,6 +6,7 @@ "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "cannot_connect": "Tilkobling mislyktes", "discover_timeout": "Kunne ikke oppdage Hue Bridger", + "invalid_host": "Ugyldig vert", "no_bridges": "Ingen Philips Hue Bridger oppdaget", "not_hue_bridge": "Ikke en Hue bro", "unknown": "Uventet feil" diff --git a/homeassistant/components/hue/translations/pl.json b/homeassistant/components/hue/translations/pl.json index 2bdcbc7e053..abfb6fa2a05 100644 --- a/homeassistant/components/hue/translations/pl.json +++ b/homeassistant/components/hue/translations/pl.json @@ -6,6 +6,7 @@ "already_in_progress": "Konfiguracja jest ju\u017c w toku", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "discover_timeout": "Nie mo\u017cna wykry\u0107 \u017cadnych mostk\u00f3w Hue", + "invalid_host": "Nieprawid\u0142owy host", "no_bridges": "Nie wykryto mostk\u00f3w Hue", "not_hue_bridge": "To nie jest mostek Hue", "unknown": "Nieoczekiwany b\u0142\u0105d" diff --git a/homeassistant/components/hue/translations/pt-BR.json b/homeassistant/components/hue/translations/pt-BR.json index 05dec678318..cc97e5e055e 100644 --- a/homeassistant/components/hue/translations/pt-BR.json +++ b/homeassistant/components/hue/translations/pt-BR.json @@ -6,6 +6,7 @@ "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "cannot_connect": "Falha ao conectar", "discover_timeout": "Incapaz de descobrir pontes Hue", + "invalid_host": "Host inv\u00e1lido", "no_bridges": "N\u00e3o h\u00e1 pontes Philips Hue descobertas", "not_hue_bridge": "N\u00e3o \u00e9 uma ponte Hue", "unknown": "Erro inesperado" diff --git a/homeassistant/components/hue/translations/ru.json b/homeassistant/components/hue/translations/ru.json index 8bd5ac77d52..6e470b74f1f 100644 --- a/homeassistant/components/hue/translations/ru.json +++ b/homeassistant/components/hue/translations/ru.json @@ -6,6 +6,7 @@ "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "discover_timeout": "\u0428\u043b\u044e\u0437 Philips Hue \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d.", + "invalid_host": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0445\u043e\u0441\u0442.", "no_bridges": "\u0428\u043b\u044e\u0437\u044b Philips Hue \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b.", "not_hue_bridge": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0448\u043b\u044e\u0437\u043e\u043c Hue.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." diff --git a/homeassistant/components/hue/translations/tr.json b/homeassistant/components/hue/translations/tr.json index 8d23af54c66..32ab9bb1887 100644 --- a/homeassistant/components/hue/translations/tr.json +++ b/homeassistant/components/hue/translations/tr.json @@ -6,6 +6,7 @@ "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "cannot_connect": "Ba\u011flanma hatas\u0131", "discover_timeout": "Hue k\u00f6pr\u00fcleri bulunam\u0131yor", + "invalid_host": "Ge\u00e7ersiz sunucu", "no_bridges": "Philips Hue k\u00f6pr\u00fcs\u00fc bulunamad\u0131", "not_hue_bridge": "Hue k\u00f6pr\u00fcs\u00fc de\u011fil", "unknown": "Beklenmeyen hata" diff --git a/homeassistant/components/hue/translations/zh-Hant.json b/homeassistant/components/hue/translations/zh-Hant.json index 6c37c699340..1816b459a85 100644 --- a/homeassistant/components/hue/translations/zh-Hant.json +++ b/homeassistant/components/hue/translations/zh-Hant.json @@ -6,6 +6,7 @@ "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "cannot_connect": "\u9023\u7dda\u5931\u6557", "discover_timeout": "\u7121\u6cd5\u641c\u5c0b\u5230 Hue Bridge", + "invalid_host": "\u4e3b\u6a5f\u7aef\u7121\u6548", "no_bridges": "\u672a\u767c\u73fe\u5230 Philips Hue Bridge", "not_hue_bridge": "\u975e Hue Bridge \u88dd\u7f6e", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" diff --git a/homeassistant/components/humidifier/translations/hu.json b/homeassistant/components/humidifier/translations/hu.json index d8e2ce86a9a..7979334429d 100644 --- a/homeassistant/components/humidifier/translations/hu.json +++ b/homeassistant/components/humidifier/translations/hu.json @@ -13,6 +13,7 @@ "is_on": "{entity_name} be van kapcsolva" }, "trigger_type": { + "changed_states": "{entity_name} be- vagy kikapcsolt", "target_humidity_changed": "{name} k\u00edv\u00e1nt p\u00e1ratartalom megv\u00e1ltozott", "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", diff --git a/homeassistant/components/hyperion/translations/hu.json b/homeassistant/components/hyperion/translations/hu.json index 750f2380624..aaffa705279 100644 --- a/homeassistant/components/hyperion/translations/hu.json +++ b/homeassistant/components/hyperion/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "auth_new_token_not_granted_error": "Az \u00fajonnan l\u00e9trehozott tokent nem hagyt\u00e1k j\u00f3v\u00e1 a Hyperion felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n", "auth_new_token_not_work_error": "Nem siker\u00fclt hiteles\u00edteni az \u00fajonnan l\u00e9trehozott token haszn\u00e1lat\u00e1val", "auth_required_error": "Nem siker\u00fclt meghat\u00e1rozni, hogy sz\u00fcks\u00e9ges-e enged\u00e9ly", @@ -27,7 +27,7 @@ "title": "Er\u0151s\u00edtse meg a Hyperion Ambilight szolg\u00e1ltat\u00e1s hozz\u00e1ad\u00e1s\u00e1t" }, "create_token": { - "description": "Az al\u00e1bbiakban v\u00e1lassza a **K\u00fcld\u00e9s** lehet\u0151s\u00e9get \u00faj hiteles\u00edt\u00e9si token k\u00e9r\u00e9s\u00e9hez. A k\u00e9relem j\u00f3v\u00e1hagy\u00e1s\u00e1hoz \u00e1tir\u00e1ny\u00edtunk a Hyperion felhaszn\u00e1l\u00f3i fel\u00fcletre. K\u00e9rj\u00fck, ellen\u0151rizze, hogy a megjelen\u00edtett azonos\u00edt\u00f3 \"{auth_id}\"", + "description": "Az al\u00e1bbiakban v\u00e1lassza a **Mehet** lehet\u0151s\u00e9get \u00faj hiteles\u00edt\u00e9si token k\u00e9r\u00e9s\u00e9hez. A k\u00e9relem j\u00f3v\u00e1hagy\u00e1s\u00e1hoz \u00e1tir\u00e1ny\u00edtunk a Hyperion felhaszn\u00e1l\u00f3i fel\u00fcletre. K\u00e9rj\u00fck, ellen\u0151rizze, hogy a megjelen\u00edtett azonos\u00edt\u00f3 \"{auth_id}\"", "title": "\u00daj hiteles\u00edt\u00e9si token automatikus l\u00e9trehoz\u00e1sa" }, "create_token_external": { diff --git a/homeassistant/components/ifttt/translations/ja.json b/homeassistant/components/ifttt/translations/ja.json index a3a63f532cf..87844be51d4 100644 --- a/homeassistant/components/ifttt/translations/ja.json +++ b/homeassistant/components/ifttt/translations/ja.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Webhook\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u4fe1\u3059\u308b\u306b\u306f\u3001Home Assistant\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3001\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u304b\u3089\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002" }, "create_entry": { - "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[IFTTT Webhook applet]({applet_url})\u306e\"Make a web request\"\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type: application/json\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[IFTTT Webhook applet]({applet_url})\u306e\"Make a web request\"\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u4f7f\u7528\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type(\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e): application/json\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "step": { "user": { diff --git a/homeassistant/components/insteon/translations/ca.json b/homeassistant/components/insteon/translations/ca.json index 59c711c3dae..9805d03c685 100644 --- a/homeassistant/components/insteon/translations/ca.json +++ b/homeassistant/components/insteon/translations/ca.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Ha fallat la connexi\u00f3", + "not_insteon_device": "El dispositiu descobert no \u00e9s un dispositiu Insteon", "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "select_single": "Selecciona una opci\u00f3." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Vols configurar {name}?" + }, "hubv1": { "data": { "host": "Adre\u00e7a IP", diff --git a/homeassistant/components/insteon/translations/de.json b/homeassistant/components/insteon/translations/de.json index 0866f53481f..164fbccceed 100644 --- a/homeassistant/components/insteon/translations/de.json +++ b/homeassistant/components/insteon/translations/de.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Verbindung fehlgeschlagen", + "not_insteon_device": "Erkanntes Ger\u00e4t ist kein Insteon-Ger\u00e4t", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", "select_single": "W\u00e4hle eine Option aus." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "M\u00f6chtest du {name} einrichten?" + }, "hubv1": { "data": { "host": "IP-Adresse", diff --git a/homeassistant/components/insteon/translations/el.json b/homeassistant/components/insteon/translations/el.json index f26c9cba54c..4a4c402ddc0 100644 --- a/homeassistant/components/insteon/translations/el.json +++ b/homeassistant/components/insteon/translations/el.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "not_insteon_device": "\u0397 \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03c5\u03c6\u03b8\u03b5\u03af\u03c3\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Insteon", "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "select_single": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" + }, "hubv1": { "data": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", diff --git a/homeassistant/components/insteon/translations/en.json b/homeassistant/components/insteon/translations/en.json index 4c4a439b938..f9e64dcf3cf 100644 --- a/homeassistant/components/insteon/translations/en.json +++ b/homeassistant/components/insteon/translations/en.json @@ -2,6 +2,7 @@ "config": { "abort": { "cannot_connect": "Failed to connect", + "not_insteon_device": "Discovered device not an Insteon device", "single_instance_allowed": "Already configured. Only a single configuration possible." }, "error": { diff --git a/homeassistant/components/insteon/translations/et.json b/homeassistant/components/insteon/translations/et.json index 69368300c7e..eff2fd9b9b2 100644 --- a/homeassistant/components/insteon/translations/et.json +++ b/homeassistant/components/insteon/translations/et.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "\u00dchendamine nurjus", + "not_insteon_device": "Avastatud seade ei ole Insteoni seade", "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." }, "error": { "cannot_connect": "\u00dchendamine nurjus", "select_single": "Vali \u00fcks suvand." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Kas seadistada {name} ?" + }, "hubv1": { "data": { "host": "IP aagress", diff --git a/homeassistant/components/insteon/translations/fr.json b/homeassistant/components/insteon/translations/fr.json index 2feb3c5d9c9..2bb0e4d2e33 100644 --- a/homeassistant/components/insteon/translations/fr.json +++ b/homeassistant/components/insteon/translations/fr.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "\u00c9chec de connexion", + "not_insteon_device": "L'appareil d\u00e9couvert n'est pas un appareil Insteon", "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "error": { "cannot_connect": "\u00c9chec de connexion", "select_single": "S\u00e9lectionnez une option." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Voulez-vous configurer {name}\u00a0?" + }, "hubv1": { "data": { "host": "Adresse IP", diff --git a/homeassistant/components/insteon/translations/he.json b/homeassistant/components/insteon/translations/he.json index 220bbcbb632..68f13ca5a01 100644 --- a/homeassistant/components/insteon/translations/he.json +++ b/homeassistant/components/insteon/translations/he.json @@ -7,6 +7,7 @@ "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, + "flow_title": "{name}", "step": { "hubv1": { "data": { diff --git a/homeassistant/components/insteon/translations/hu.json b/homeassistant/components/insteon/translations/hu.json index f34307a67a4..560c1fc118e 100644 --- a/homeassistant/components/insteon/translations/hu.json +++ b/homeassistant/components/insteon/translations/hu.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "not_insteon_device": "A felfedezett eszk\u00f6z nem egy Insteon", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "select_single": "V\u00e1lasszon egy lehet\u0151s\u00e9get" }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?" + }, "hubv1": { "data": { "host": "IP c\u00edm", diff --git a/homeassistant/components/insteon/translations/id.json b/homeassistant/components/insteon/translations/id.json index efae5284150..aae7e9f71ac 100644 --- a/homeassistant/components/insteon/translations/id.json +++ b/homeassistant/components/insteon/translations/id.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Gagal terhubung", + "not_insteon_device": "Perangkat yang ditemukan bukan perangkat Insteon", "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "error": { "cannot_connect": "Gagal terhubung", "select_single": "Pilih satu opsi." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Ingin menyiapkan {name}?" + }, "hubv1": { "data": { "host": "Alamat IP", diff --git a/homeassistant/components/insteon/translations/it.json b/homeassistant/components/insteon/translations/it.json index 83a4e87d6f6..b47a06c9d7a 100644 --- a/homeassistant/components/insteon/translations/it.json +++ b/homeassistant/components/insteon/translations/it.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Impossibile connettersi", + "not_insteon_device": "Dispositivo rilevato non \u00e8 un dispositivo Insteon", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { "cannot_connect": "Impossibile connettersi", "select_single": "Seleziona un'opzione." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Vuoi configurare {name}?" + }, "hubv1": { "data": { "host": "Indirizzo IP", @@ -46,7 +51,7 @@ "options": { "error": { "cannot_connect": "Impossibile connettersi", - "input_error": "Voci non valide, si prega di controllare i valori.", + "input_error": "Voci non valide, controlla i valori.", "select_single": "Seleziona un'opzione." }, "step": { diff --git a/homeassistant/components/insteon/translations/ja.json b/homeassistant/components/insteon/translations/ja.json index d4ddf083f1b..0b8d93518bd 100644 --- a/homeassistant/components/insteon/translations/ja.json +++ b/homeassistant/components/insteon/translations/ja.json @@ -8,7 +8,11 @@ "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "select_single": "\u30aa\u30d7\u30b7\u30e7\u30f3\u30921\u3064\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "{name} \u3092\u8a2d\u5b9a\u3057\u307e\u3059\u304b\uff1f" + }, "hubv1": { "data": { "host": "IP\u30a2\u30c9\u30ec\u30b9", @@ -61,7 +65,7 @@ }, "add_x10": { "data": { - "housecode": "\u30cf\u30a6\u30b9\u30b3\u30fc\u30c9(a\uff5ep)", + "housecode": "\u30cf\u30a6\u30b9\u30b3\u30fc\u30c9(a - p)", "platform": "\u30d7\u30e9\u30c3\u30c8\u30db\u30fc\u30e0", "steps": "\u8abf\u5149\u30b9\u30c6\u30c3\u30d7(\u30e9\u30a4\u30c8\u30c7\u30d0\u30a4\u30b9\u306e\u307f\u3001\u30c7\u30d5\u30a9\u30eb\u30c822)", "unitcode": "\u30e6\u30cb\u30c3\u30c8\u30b3\u30fc\u30c9(1\u301c16)" diff --git a/homeassistant/components/insteon/translations/nl.json b/homeassistant/components/insteon/translations/nl.json index 63a0bb059d5..207e370e245 100644 --- a/homeassistant/components/insteon/translations/nl.json +++ b/homeassistant/components/insteon/translations/nl.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Kon niet verbinden", + "not_insteon_device": "Ontdekt apparaat is geen Insteon apparaat", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kon niet verbinden", "select_single": "Selecteer een optie." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Wilt u {name} instellen?" + }, "hubv1": { "data": { "host": "IP-adres", diff --git a/homeassistant/components/insteon/translations/no.json b/homeassistant/components/insteon/translations/no.json index b5d6a2c3105..3716312b00f 100644 --- a/homeassistant/components/insteon/translations/no.json +++ b/homeassistant/components/insteon/translations/no.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Tilkobling mislyktes", + "not_insteon_device": "Oppdaget enhet, ikke en Insteon-enhet", "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "error": { "cannot_connect": "Tilkobling mislyktes", "select_single": "Velg ett alternativ." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Vil du konfigurere {name}?" + }, "hubv1": { "data": { "host": "IP adresse", diff --git a/homeassistant/components/insteon/translations/pl.json b/homeassistant/components/insteon/translations/pl.json index c4a58e0e09a..6d3b6c4391d 100644 --- a/homeassistant/components/insteon/translations/pl.json +++ b/homeassistant/components/insteon/translations/pl.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "not_insteon_device": "Wykryte urz\u0105dzenie nie jest urz\u0105dzeniem Insteon", "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "select_single": "Wybierz jedn\u0105 z opcji" }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Czy chcesz skonfigurowa\u0107 {name}?" + }, "hubv1": { "data": { "host": "Adres IP", diff --git a/homeassistant/components/insteon/translations/pt-BR.json b/homeassistant/components/insteon/translations/pt-BR.json index 409ac9d6283..b5487d9260a 100644 --- a/homeassistant/components/insteon/translations/pt-BR.json +++ b/homeassistant/components/insteon/translations/pt-BR.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Falha ao conectar", + "not_insteon_device": "O dispositivo descoberto n\u00e3o \u00e9 um dispositivo Insteon", "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { "cannot_connect": "Falha ao conectar", "select_single": "Selecione uma op\u00e7\u00e3o." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "Deseja configurar {name}?" + }, "hubv1": { "data": { "host": "Endere\u00e7o IP", diff --git a/homeassistant/components/insteon/translations/ru.json b/homeassistant/components/insteon/translations/ru.json index 69b5354fe87..3151fa35252 100644 --- a/homeassistant/components/insteon/translations/ru.json +++ b/homeassistant/components/insteon/translations/ru.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "not_insteon_device": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 Insteon.", "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "select_single": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u044e." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?" + }, "hubv1": { "data": { "host": "IP-\u0430\u0434\u0440\u0435\u0441", diff --git a/homeassistant/components/insteon/translations/tr.json b/homeassistant/components/insteon/translations/tr.json index 3cb23de55f7..2f74d6a98ae 100644 --- a/homeassistant/components/insteon/translations/tr.json +++ b/homeassistant/components/insteon/translations/tr.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "Ba\u011flanma hatas\u0131", + "not_insteon_device": "Ke\u015ffedilen cihaz bir Insteon cihaz\u0131 de\u011fil", "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "select_single": "Bir se\u00e7enek belirleyin." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "{name} kurulumunu yapmak istiyor musunuz?" + }, "hubv1": { "data": { "host": "IP Adresi", diff --git a/homeassistant/components/insteon/translations/zh-Hant.json b/homeassistant/components/insteon/translations/zh-Hant.json index 2176ea67b94..c55a1ea0a5c 100644 --- a/homeassistant/components/insteon/translations/zh-Hant.json +++ b/homeassistant/components/insteon/translations/zh-Hant.json @@ -2,13 +2,18 @@ "config": { "abort": { "cannot_connect": "\u9023\u7dda\u5931\u6557", + "not_insteon_device": "\u6240\u767c\u73fe\u7684\u88dd\u7f6e\u4e26\u975e Insteon \u88dd\u7f6e", "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "select_single": "\u9078\u64c7\u9078\u9805\u3002" }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f" + }, "hubv1": { "data": { "host": "IP \u4f4d\u5740", diff --git a/homeassistant/components/vallox/translations/sk.json b/homeassistant/components/integration/translations/bg.json similarity index 73% rename from homeassistant/components/vallox/translations/sk.json rename to homeassistant/components/integration/translations/bg.json index af15f92c2f2..35cfa0ad1d7 100644 --- a/homeassistant/components/vallox/translations/sk.json +++ b/homeassistant/components/integration/translations/bg.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "name": "N\u00e1zov" + "name": "\u0418\u043c\u0435" } } } diff --git a/homeassistant/components/integration/translations/ca.json b/homeassistant/components/integration/translations/ca.json new file mode 100644 index 00000000000..bbe5e6e31b4 --- /dev/null +++ b/homeassistant/components/integration/translations/ca.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "M\u00e8tode d'integraci\u00f3", + "name": "Nom", + "round": "Precisi\u00f3", + "source": "Sensor d'entrada", + "unit_prefix": "Prefix m\u00e8tric", + "unit_time": "Unitat de temps" + }, + "data_description": { + "round": "Controla el nombre de d\u00edgits decimals a la sortida.", + "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric seleccionat.", + "unit_time": "La sortida s'escalar\u00e0 segons la unitat de temps seleccionada." + }, + "description": "Crea un sensor que calcula la suma de Riemann que estima la integral d'un sensor.", + "title": "Afegeix sensor d'integraci\u00f3 de suma Riemann" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precisi\u00f3" + }, + "data_description": { + "round": "Controla el nombre de d\u00edgits decimals a la sortida." + } + }, + "options": { + "data": { + "round": "Precisi\u00f3" + }, + "description": "La precisi\u00f3 controla el nombre de d\u00edgits decimals a la sortida." + } + } + }, + "title": "Integraci\u00f3 - Sensor integral de suma de Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/cs.json b/homeassistant/components/integration/translations/cs.json new file mode 100644 index 00000000000..3e7fdfca729 --- /dev/null +++ b/homeassistant/components/integration/translations/cs.json @@ -0,0 +1,41 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Metoda integrace", + "name": "Jm\u00e9no", + "round": "P\u0159esnost", + "source": "Vstupn\u00ed senzor", + "unit_time": "\u010casov\u00e1 jednotka" + }, + "data_description": { + "round": "Ur\u010duje po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu.", + "unit_prefix": "V\u00fdstup bude \u0161k\u00e1lov\u00e1n podle vybran\u00e9ho metrick\u00e9ho prefixu.", + "unit_time": "V\u00fdstup bude \u0161k\u00e1lov\u00e1n podle zvolen\u00e9 \u010dasov\u00e9 jednotky." + }, + "description": "Vytvo\u0159\u00ed senzor, kter\u00fd vypo\u010d\u00edt\u00e1 Riemann\u016fv sou\u010det pro odhad integr\u00e1lu senzoru.", + "title": "P\u0159idat Riemann\u016fv sou\u010dtov\u00fd integr\u00e1ln\u00ed senzor" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "P\u0159esnost" + }, + "data_description": { + "round": "Ur\u010duje po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu." + } + }, + "options": { + "data": { + "round": "P\u0159esnost" + }, + "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu." + } + } + }, + "title": "Integrace - Riemann\u016fv sou\u010dtov\u00fd integr\u00e1ln\u00ed senzor" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/de.json b/homeassistant/components/integration/translations/de.json new file mode 100644 index 00000000000..3013fa9d039 --- /dev/null +++ b/homeassistant/components/integration/translations/de.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Integrationsmethode", + "name": "Name", + "round": "Genauigkeit", + "source": "Eingangssensor", + "unit_prefix": "Metrisches Pr\u00e4fix", + "unit_time": "Zeiteinheit" + }, + "data_description": { + "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe.", + "unit_prefix": "Die Ausgabe wird entsprechend dem gew\u00e4hlten metrischen Pr\u00e4fix skaliert.", + "unit_time": "Die Ausgabe wird entsprechend der gew\u00e4hlten Zeiteinheit skaliert." + }, + "description": "Erstelle einen Sensor, der eine Riemann-Summe berechnet, um das Integral eines Sensors zu sch\u00e4tzen.", + "title": "Riemann-Summenintegralsensor hinzuf\u00fcgen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Genauigkeit" + }, + "data_description": { + "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe." + } + }, + "options": { + "data": { + "round": "Genauigkeit" + }, + "description": "Die Genauigkeit steuert die Anzahl der Dezimalstellen in der Ausgabe." + } + } + }, + "title": "Integration - Riemann-Summenintegralsensor" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/el.json b/homeassistant/components/integration/translations/el.json new file mode 100644 index 00000000000..e366137fce1 --- /dev/null +++ b/homeassistant/components/integration/translations/el.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "unit_prefix": "\u039c\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1", + "unit_time": "\u03a7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + }, + "data_description": { + "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.", + "unit_prefix": "\u0397 \u03ad\u03be\u03bf\u03b4\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1.", + "unit_time": "\u0397 \u03ad\u03be\u03bf\u03b4\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5." + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.\n\u03a4\u03bf \u03ac\u03b8\u03c1\u03bf\u03b9\u03c3\u03bc\u03b1 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03bf \u03c7\u03c1\u03cc\u03bd\u03bf \u03bf\u03bb\u03bf\u03ba\u03bb\u03ae\u03c1\u03c9\u03c3\u03b7\u03c2.", + "title": "\u039d\u03ad\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1" + }, + "data_description": { + "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf." + } + }, + "options": { + "data": { + "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1" + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf." + } + } + }, + "title": "\u039f\u03bb\u03bf\u03ba\u03bb\u03ae\u03c1\u03c9\u03c3\u03b7 - A\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b1\u03b8\u03c1\u03bf\u03af\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2 Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/en.json b/homeassistant/components/integration/translations/en.json index 1ee047b447f..3174eab3f69 100644 --- a/homeassistant/components/integration/translations/en.json +++ b/homeassistant/components/integration/translations/en.json @@ -29,6 +29,12 @@ "data_description": { "round": "Controls the number of decimal digits in the output." } + }, + "options": { + "data": { + "round": "Precision" + }, + "description": "Precision controls the number of decimal digits in the output." } } }, diff --git a/homeassistant/components/integration/translations/et.json b/homeassistant/components/integration/translations/et.json new file mode 100644 index 00000000000..31901bbb6f6 --- /dev/null +++ b/homeassistant/components/integration/translations/et.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Integreerimismeetod", + "name": "Nimi", + "round": "T\u00e4psus", + "source": "Sisendandur", + "unit_prefix": "M\u00f5\u00f5diku eesliide", + "unit_time": "Aja\u00fchik" + }, + "data_description": { + "round": "K\u00fcmnendkohtade arv v\u00e4ljundis.", + "unit_prefix": "V\u00e4ljund skaleeritakse vastavalt valitud m\u00f5\u00f5diku prefiksile.", + "unit_time": "V\u00e4ljund skaleeritakse vastavalt valitud aja\u00fchikule." + }, + "description": "Looge andur, mis arvutab anduri integraali hindamiseks Riemanni summa.", + "title": "Lisa Riemanni summa integraalandur" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "T\u00e4psus" + }, + "data_description": { + "round": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis." + } + }, + "options": { + "data": { + "round": "T\u00e4psus" + }, + "description": "T\u00e4psus reguleerib k\u00fcmnendkohtade arvu v\u00e4ljundis." + } + } + }, + "title": "Sidumine - Riemanni integraalsumma andur" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/fr.json b/homeassistant/components/integration/translations/fr.json new file mode 100644 index 00000000000..33b5ab86e7f --- /dev/null +++ b/homeassistant/components/integration/translations/fr.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "M\u00e9thode de calcul de l'int\u00e9grale", + "name": "Nom", + "round": "Pr\u00e9cision", + "source": "Capteur d'entr\u00e9e", + "unit_prefix": "Pr\u00e9fixe m\u00e9trique", + "unit_time": "Unit\u00e9 de temps" + }, + "data_description": { + "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie.", + "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique s\u00e9lectionn\u00e9.", + "unit_time": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction de l'unit\u00e9 de temps s\u00e9lectionn\u00e9e." + }, + "description": "Cr\u00e9ez un capteur qui calcule une somme de Riemann afin d'estimer l'int\u00e9grale d'un autre capteur.", + "title": "Ajouter un capteur d'int\u00e9grale de Riemann" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Pr\u00e9cision" + }, + "data_description": { + "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie." + } + }, + "options": { + "data": { + "round": "Pr\u00e9cision" + }, + "description": "La pr\u00e9cision contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie." + } + } + }, + "title": "Int\u00e9grale \u2013\u00a0Capteur d'int\u00e9grale de Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/he.json b/homeassistant/components/integration/translations/he.json new file mode 100644 index 00000000000..4061da5f233 --- /dev/null +++ b/homeassistant/components/integration/translations/he.json @@ -0,0 +1,40 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u05e9\u05d9\u05d8\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1", + "name": "\u05e9\u05dd", + "round": "\u05d3\u05d9\u05d5\u05e7", + "source": "\u05d7\u05d9\u05d9\u05e9\u05df \u05e7\u05dc\u05d8", + "unit_prefix": "\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05de\u05d8\u05e8\u05d9\u05ea", + "unit_time": "\u05d6\u05de\u05df \u05e9\u05d9\u05dc\u05d5\u05d1" + }, + "data_description": { + "round": "\u05e9\u05dc\u05d9\u05d8\u05d4 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8." + }, + "description": "\u05d3\u05d9\u05d5\u05e7 \u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8.\n\u05d4\u05e1\u05db\u05d5\u05dd \u05d9\u05e9\u05ea\u05e0\u05d4 \u05d1\u05d4\u05ea\u05d0\u05dd \u05dc\u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05d4\u05de\u05d8\u05e8\u05d9\u05ea \u05e9\u05e0\u05d1\u05d7\u05e8\u05d4 \u05d5\u05d6\u05de\u05df \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1.", + "title": "\u05d7\u05d9\u05d9\u05e9\u05df \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d7\u05d3\u05e9" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u05d3\u05d9\u05d5\u05e7" + }, + "data_description": { + "round": "\u05e9\u05dc\u05d9\u05d8\u05d4 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8." + } + }, + "options": { + "data": { + "round": "\u05d3\u05d9\u05d5\u05e7" + }, + "description": "\u05d3\u05d9\u05d5\u05e7 \u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8." + } + } + }, + "title": "\u05e9\u05d9\u05dc\u05d5\u05d1 - \u05d7\u05d9\u05d9\u05e9\u05df \u05e1\u05db\u05d5\u05dd \u05d0\u05d9\u05e0\u05d8\u05d2\u05e8\u05dc\u05d9 \u05e9\u05dc \u05e8\u05d9\u05de\u05df" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/hu.json b/homeassistant/components/integration/translations/hu.json new file mode 100644 index 00000000000..c892e858b3f --- /dev/null +++ b/homeassistant/components/integration/translations/hu.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Integr\u00e1ci\u00f3s m\u00f3dszer", + "name": "Elnevez\u00e9s", + "round": "Pontoss\u00e1g", + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "unit_prefix": "M\u00e9rt\u00e9kegys\u00e9g el\u0151tag", + "unit_time": "Id\u0151egys\u00e9g" + }, + "data_description": { + "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma.", + "unit_prefix": "A kimenet a kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9gnek megfelel\u0151en lesz sk\u00e1l\u00e1zva.", + "unit_time": "A kimenet a kiv\u00e1lasztott id\u0151egys\u00e9gnek megfelel\u0151en lesz sk\u00e1l\u00e1zva." + }, + "description": "Hozzon l\u00e9tre egy \u00faj \u00e9rz\u00e9kel\u0151t, amely Riemann-integr\u00e1lt sz\u00e1mol egy m\u00e1sik, megl\u00e9v\u0151 \u00e9rz\u00e9kel\u0151 alapj\u00e1n.", + "title": "Riemann-integr\u00e1l \u00e9rz\u00e9kel\u0151 l\u00e9trehoz\u00e1sa" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Pontoss\u00e1g" + }, + "data_description": { + "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma." + } + }, + "options": { + "data": { + "round": "Pontoss\u00e1g" + }, + "description": "A pontoss\u00e1g hat\u00e1rozza meg az eredm\u00e9ny tizedesjegyeinek sz\u00e1m\u00e1t." + } + } + }, + "title": "Integr\u00e1ci\u00f3 - Riemann-integr\u00e1l" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/id.json b/homeassistant/components/integration/translations/id.json new file mode 100644 index 00000000000..d585a4409e9 --- /dev/null +++ b/homeassistant/components/integration/translations/id.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Metode integrasi", + "name": "Nama", + "round": "Presisi", + "source": "Sensor input", + "unit_prefix": "Prefiks metrik", + "unit_time": "Unit waktu" + }, + "data_description": { + "round": "Mengontrol jumlah digit desimal dalam output.", + "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih.", + "unit_time": "Output akan diskalakan sesuai dengan unit waktu dipilih." + }, + "description": "Buat sensor yang menghitung jumlah Riemann untuk memperkirakan integral dari sebuah sensor.", + "title": "Tambahkan sensor integral penjumlahan Riemann" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Presisi" + }, + "data_description": { + "round": "Mengontrol jumlah digit desimal dalam output." + } + }, + "options": { + "data": { + "round": "Presisi" + }, + "description": "Presisi mengontrol jumlah digit desimal pada output." + } + } + }, + "title": "Integrasi - Sensor jumlah integral Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/it.json b/homeassistant/components/integration/translations/it.json new file mode 100644 index 00000000000..a1904f45cd1 --- /dev/null +++ b/homeassistant/components/integration/translations/it.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Metodo di integrazione", + "name": "Nome", + "round": "Precisione", + "source": "Sensore di ingresso", + "unit_prefix": "Prefisso metrico", + "unit_time": "Unit\u00e0 di tempo" + }, + "data_description": { + "round": "Controlla il numero di cifre decimali nell'output.", + "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso della metrica selezionato.", + "unit_time": "L'output verr\u00e0 ridimensionato in base all'unit\u00e0 di tempo selezionata." + }, + "description": "Crea un sensore che calcoli una somma di Riemann per stimare l'integrale di un sensore.", + "title": "Aggiungi il sensore integrale della somma di Riemann" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precisione" + }, + "data_description": { + "round": "Controlla il numero di cifre decimali nell'output." + } + }, + "options": { + "data": { + "round": "Precisione" + }, + "description": "Precisione controlla il numero di cifre decimali nell'uscita." + } + } + }, + "title": "Integrazione - Sensore integrale di somma Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/ja.json b/homeassistant/components/integration/translations/ja.json new file mode 100644 index 00000000000..5fac6ba692b --- /dev/null +++ b/homeassistant/components/integration/translations/ja.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u7a4d\u7b97\u65b9\u6cd5", + "name": "\u540d\u524d", + "round": "\u7cbe\u5ea6", + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "unit_prefix": "\u30e1\u30c8\u30ea\u30c3\u30af\u63a5\u982d\u8f9e", + "unit_time": "\u7a4d\u7b97\u6642\u9593" + }, + "data_description": { + "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002", + "unit_prefix": "\u51fa\u529b\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002", + "unit_time": "\u51fa\u529b\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002" + }, + "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002\n\u5408\u8a08\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u7a4d\u5206\u6642\u9593\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002", + "title": "\u65b0\u3057\u3044\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30bb\u30f3\u30b5\u30fc" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u7cbe\u5ea6" + }, + "data_description": { + "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002" + } + }, + "options": { + "data": { + "round": "\u7cbe\u5ea6" + }, + "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" + } + } + }, + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3 - \u30ea\u30fc\u30de\u30f3\u548c\u7a4d\u5206\u30bb\u30f3\u30b5\u30fc" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/nl.json b/homeassistant/components/integration/translations/nl.json new file mode 100644 index 00000000000..8753ee30ea7 --- /dev/null +++ b/homeassistant/components/integration/translations/nl.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Integratie methode", + "name": "Naam", + "round": "Precisie", + "source": "Invoer sensor", + "unit_prefix": "Metrisch voorvoegsel", + "unit_time": "Tijdseenheid" + }, + "data_description": { + "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel.", + "unit_time": "De output wordt geschaald volgens de geselecteerde tijdseenheid." + }, + "description": "Maak een sensor die een Riemann som berekent om de integraal van een sensor te schatten.", + "title": "Riemann som integrale sensor toevoegen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precisie" + }, + "data_description": { + "round": "Regelt het aantal decimale cijfers in de uitvoer." + } + }, + "options": { + "data": { + "round": "Precisie" + }, + "description": "Precisie bepaalt het aantal decimale cijfers in de uitvoer." + } + } + }, + "title": "Integratie - Riemann som integrale sensor" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/no.json b/homeassistant/components/integration/translations/no.json new file mode 100644 index 00000000000..aafa60b5811 --- /dev/null +++ b/homeassistant/components/integration/translations/no.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Integrasjonsmetode", + "name": "Navn", + "round": "Presisjon", + "source": "Inngangssensor", + "unit_prefix": "Metrisk prefiks", + "unit_time": "Tidsenhet" + }, + "data_description": { + "round": "Styrer antall desimaler i utdataene.", + "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset.", + "unit_time": "Utgangen vil bli skalert i henhold til den valgte tidsenheten." + }, + "description": "Lag en sensor som beregner en Riemann-sum for \u00e5 estimere integralet til en sensor.", + "title": "Legg til Riemann sum integral sensor" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Presisjon" + }, + "data_description": { + "round": "Styrer antall desimaler i utdataene." + } + }, + "options": { + "data": { + "round": "Presisjon" + }, + "description": "Presisjon styrer antall desimaler i utdataene." + } + } + }, + "title": "Integrasjon - Riemann sum integral sensor" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/pl.json b/homeassistant/components/integration/translations/pl.json new file mode 100644 index 00000000000..7971d97129c --- /dev/null +++ b/homeassistant/components/integration/translations/pl.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Metoda integracji", + "name": "Nazwa", + "round": "Precyzja", + "source": "Sensor wej\u015bciowy", + "unit_prefix": "Prefiks metryczny", + "unit_time": "Jednostka czasu" + }, + "data_description": { + "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych.", + "unit_prefix": "Dane wyj\u015bciowe b\u0119d\u0105 skalowane zgodnie z wybranym prefiksem metrycznym.", + "unit_time": "Dane wyj\u015bciowe b\u0119d\u0105 skalowane zgodnie z wybran\u0105 jednostk\u0105 czasu." + }, + "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza sum\u0119 Riemanna, aby oszacowa\u0107 ca\u0142k\u0119 sensora.", + "title": "Dodaj sensor ca\u0142kuj\u0105cy sum\u0119 Riemanna" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precyzja" + }, + "data_description": { + "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych." + } + }, + "options": { + "data": { + "round": "Precyzja" + }, + "description": "Precyzja kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych." + } + } + }, + "title": "Integracja - czujnik ca\u0142kuj\u0105cy sum\u0119 Riemanna" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/pt-BR.json b/homeassistant/components/integration/translations/pt-BR.json new file mode 100644 index 00000000000..ae512b93fd4 --- /dev/null +++ b/homeassistant/components/integration/translations/pt-BR.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "M\u00e9todo de integra\u00e7\u00e3o", + "name": "Nome", + "round": "Precis\u00e3o", + "source": "Sensor fonte", + "unit_prefix": "Prefixo m\u00e9trico", + "unit_time": "Unidade de tempo" + }, + "data_description": { + "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda.", + "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado.", + "unit_time": "A sa\u00edda ser\u00e1 dimensionada de acordo com a unidade de tempo selecionada." + }, + "description": "Crie um sensor que calcule uma soma de Riemann para estimar a integral de um sensor.", + "title": "Adicionar sensor de soma de Riemann" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precis\u00e3o" + }, + "data_description": { + "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda." + } + }, + "options": { + "data": { + "round": "Precis\u00e3o" + }, + "description": "A precis\u00e3o controla o n\u00famero de d\u00edgitos decimais na sa\u00edda." + } + } + }, + "title": "Integra\u00e7\u00e3o - Sensor de soma de Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/ru.json b/homeassistant/components/integration/translations/ru.json new file mode 100644 index 00000000000..67891293d7b --- /dev/null +++ b/homeassistant/components/integration/translations/ru.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u041c\u0435\u0442\u043e\u0434 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "unit_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0438", + "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" + }, + "data_description": { + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", + "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438.", + "unit_time": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438." + }, + "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0441\u0443\u043c\u043c\u0443 \u0420\u0438\u043c\u0430\u043d\u0430 \u0434\u043b\u044f \u043e\u0446\u0435\u043d\u043a\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u043b\u0430 \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", + "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u043b \u0420\u0438\u043c\u0430\u043d\u0430" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435" + }, + "data_description": { + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." + } + }, + "options": { + "data": { + "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435" + }, + "description": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." + } + } + }, + "title": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u043b \u0420\u0438\u043c\u0430\u043d\u0430" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/tr.json b/homeassistant/components/integration/translations/tr.json new file mode 100644 index 00000000000..de99ebd3633 --- /dev/null +++ b/homeassistant/components/integration/translations/tr.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "Entegrasyon y\u00f6ntemi", + "name": "Ad", + "round": "Hassas", + "source": "Giri\u015f sens\u00f6r\u00fc", + "unit_prefix": "Metrik \u00f6neki", + "unit_time": "Zaman birimi" + }, + "data_description": { + "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder.", + "unit_prefix": "\u00c7\u0131kt\u0131, se\u00e7ilen metrik \u00f6nekine g\u00f6re \u00f6l\u00e7eklenecektir.", + "unit_time": "\u00c7\u0131kt\u0131, se\u00e7ilen zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir." + }, + "description": "Bir sens\u00f6r\u00fcn integralini tahmin etmek i\u00e7in bir Riemann toplam\u0131n\u0131 hesaplayan bir sens\u00f6r olu\u015fturun.", + "title": "Riemann toplam integral sens\u00f6r\u00fcn\u00fc ekleyin" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Hassas" + }, + "data_description": { + "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." + } + }, + "options": { + "data": { + "round": "Hassas" + }, + "description": "Kesinlik, \u00e7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." + } + } + }, + "title": "Entegrasyon - Riemann toplam integral sens\u00f6r\u00fc" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/zh-Hans.json b/homeassistant/components/integration/translations/zh-Hans.json new file mode 100644 index 00000000000..f7ebea98f4a --- /dev/null +++ b/homeassistant/components/integration/translations/zh-Hans.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u79ef\u5206\u65b9\u6cd5", + "name": "\u540d\u79f0", + "round": "\u7cbe\u5ea6", + "source": "\u8f93\u5165\u4f20\u611f\u5668", + "unit_prefix": "\u5355\u4f4d\u524d\u7f00", + "unit_time": "\u65f6\u95f4\u5355\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002", + "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u8fdb\u884c\u7f29\u653e\u3002", + "unit_time": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u53e6\u4e00\u4e2a\u4f20\u611f\u5668\u7684\u5b9a\u79ef\u5206\u3002", + "title": "\u6dfb\u52a0\u5b9a\u79ef\u5206\u4f20\u611f\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u7cbe\u5ea6" + }, + "data_description": { + "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" + } + }, + "options": { + "data": { + "round": "\u7cbe\u5ea6" + }, + "description": "\u7cbe\u5ea6\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" + } + } + }, + "title": "\u5b9a\u79ef\u5206\u4f20\u611f\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/zh-Hant.json b/homeassistant/components/integration/translations/zh-Hant.json new file mode 100644 index 00000000000..2adbb3edc28 --- /dev/null +++ b/homeassistant/components/integration/translations/zh-Hant.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\u6574\u5408\u65b9\u5f0f", + "name": "\u540d\u7a31", + "round": "\u6e96\u78ba\u5ea6", + "source": "\u8f38\u5165\u611f\u6e2c\u5668", + "unit_prefix": "\u516c\u5236\u524d\u7db4", + "unit_time": "\u6642\u9593\u55ae\u4f4d" + }, + "data_description": { + "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002", + "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u800c\u8b8a\u5316\u3002", + "unit_time": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002" + }, + "description": "\u65b0\u589e\u9810\u4f30\u8a08\u7b97\u9ece\u66fc\u548c\u7a4d\u5206\u611f\u6e2c\u5668\u6574\u5408\u4e4b\u611f\u6e2c\u5668\u3002", + "title": "\u65b0\u589e\u9ece\u66fc\u548c\u7a4d\u5206\u611f\u6e2c\u5668\u6574\u5408" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\u6e96\u78ba\u5ea6" + }, + "data_description": { + "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002" + } + }, + "options": { + "data": { + "round": "\u6e96\u78ba\u5ea6" + }, + "description": "\u7cbe\u6e96\u5ea6\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002" + } + } + }, + "title": "\u6574\u5408 - \u9ece\u66fc\u548c\u7a4d\u5206\u611f\u6e2c\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/bg.json b/homeassistant/components/intellifire/translations/bg.json index cbf1e2ae7c9..1a8dd460097 100644 --- a/homeassistant/components/intellifire/translations/bg.json +++ b/homeassistant/components/intellifire/translations/bg.json @@ -7,7 +7,22 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, + "flow_title": "{serial} ({host})", "step": { + "dhcp_confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {host}\n\u0421\u0435\u0440\u0438\u0435\u043d: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "\u0425\u043e\u0441\u0442" + }, + "description": "\u041b\u043e\u043a\u0430\u043b\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f" + }, + "pick_device": { + "data": { + "host": "\u0425\u043e\u0441\u0442" + } + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" diff --git a/homeassistant/components/intellifire/translations/ca.json b/homeassistant/components/intellifire/translations/ca.json index 3673c1c60c6..9894ec4920a 100644 --- a/homeassistant/components/intellifire/translations/ca.json +++ b/homeassistant/components/intellifire/translations/ca.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat" + "already_configured": "El dispositiu ja est\u00e0 configurat", + "not_intellifire_device": "No \u00e9s un dispositiu IntelliFire.", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { + "api_error": "Ha fallat l'inici de sessi\u00f3", "cannot_connect": "Ha fallat la connexi\u00f3", + "iftapi_connect": "S'ha produ\u00eft un error en connectar a iftapi.net", "unknown": "Error inesperat" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Contrasenya", + "username": "Correu electr\u00f2nic" + } + }, + "dhcp_confirm": { + "description": "Vols configurar {host}\nS\u00e8rie: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Amfitri\u00f3 (adre\u00e7a IP)" + }, + "description": "Configuraci\u00f3 local" + }, + "pick_device": { + "data": { + "host": "Amfitri\u00f3" + }, + "description": "S'han descobert els dispositius IntelliFire seg\u00fcents. Selecciona el que vulguis configurar.", + "title": "Selecci\u00f3 de dispositiu" + }, "user": { "data": { "host": "Amfitri\u00f3" diff --git a/homeassistant/components/intellifire/translations/cs.json b/homeassistant/components/intellifire/translations/cs.json index 5eac883adf0..48dbc509ea8 100644 --- a/homeassistant/components/intellifire/translations/cs.json +++ b/homeassistant/components/intellifire/translations/cs.json @@ -7,7 +7,18 @@ "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, + "flow_title": "{serial} ({host})", "step": { + "manual_device_entry": { + "data": { + "host": "Hostitel" + } + }, + "pick_device": { + "data": { + "host": "Hostitel" + } + }, "user": { "data": { "host": "Hostitel" diff --git a/homeassistant/components/intellifire/translations/de.json b/homeassistant/components/intellifire/translations/de.json index 6abbe1b2b27..f1c37a8a475 100644 --- a/homeassistant/components/intellifire/translations/de.json +++ b/homeassistant/components/intellifire/translations/de.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert" + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "not_intellifire_device": "Kein IntelliFire-Ger\u00e4t.", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { + "api_error": "Login fehlgeschlagen", "cannot_connect": "Verbindung fehlgeschlagen", + "iftapi_connect": "Fehler beim Verbinden mit iftapi.net", "unknown": "Unerwarteter Fehler" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Passwort", + "username": "E-Mail" + } + }, + "dhcp_confirm": { + "description": "M\u00f6chtest du {host} einrichten\nSeriennummer: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Host (IP-Adresse)" + }, + "description": "Lokale Konfiguration" + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "Die folgenden IntelliFire-Ger\u00e4te wurden gefunden. Bitte w\u00e4hle aus, welche du konfigurieren m\u00f6chtest.", + "title": "Ger\u00e4teauswahl" + }, "user": { "data": { "host": "Host" diff --git a/homeassistant/components/intellifire/translations/el.json b/homeassistant/components/intellifire/translations/el.json index c7b88a9b3d8..fa72581a4a6 100644 --- a/homeassistant/components/intellifire/translations/el.json +++ b/homeassistant/components/intellifire/translations/el.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "not_intellifire_device": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae IntelliFire.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { + "api_error": "\u0397 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "iftapi_connect": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03bf iftapi.net", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "username": "Email" + } + }, + "dhcp_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {host}\nSerial: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" + }, + "description": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7" + }, + "pick_device": { + "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" + }, + "description": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bf\u03b9 \u03b1\u03ba\u03cc\u03bb\u03bf\u03c5\u03b8\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 IntelliFire. \u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03bf\u03b9\u03b1 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5.", + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" + }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" diff --git a/homeassistant/components/intellifire/translations/en.json b/homeassistant/components/intellifire/translations/en.json index d7c157d8917..f0c317efb93 100644 --- a/homeassistant/components/intellifire/translations/en.json +++ b/homeassistant/components/intellifire/translations/en.json @@ -7,18 +7,17 @@ }, "error": { "api_error": "Login failed", - "cannot_connect": "Could not connect to a fireplace endpoint at url: http://{host}/poll\nVerify IP address and try again", - "iftapi_connect": "Error conecting to iftapi.net" + "cannot_connect": "Failed to connect", + "iftapi_connect": "Error conecting to iftapi.net", + "unknown": "Unexpected error" }, "flow_title": "{serial} ({host})", "step": { "api_config": { "data": { "password": "Password", - "username": "Username (Email)" - }, - "description": "IntelliFire will need to reach out to [iftapi.net](https://iftapi.net/webaccess/login.html) in order to obtain an API key. Once it has obtained this API key, the rest of its interactions will occur completely within the local network. If the API key were to expire it would again need to reach out to https://iftapi.net/webaccess/login.html\n\nUsername and Password are the same information used in your IntelliFire Android/iOS application. ", - "title": "IntelliFire - API Configuration" + "username": "Email" + } }, "dhcp_confirm": { "description": "Do you want to setup {host}\nSerial: {serial}?" @@ -35,6 +34,11 @@ }, "description": "The following IntelliFire devices were discovered. Please select which you wish to configure.", "title": "Device Selection" + }, + "user": { + "data": { + "host": "Host" + } } } } diff --git a/homeassistant/components/intellifire/translations/et.json b/homeassistant/components/intellifire/translations/et.json index 939fa44224f..53c87f112a7 100644 --- a/homeassistant/components/intellifire/translations/et.json +++ b/homeassistant/components/intellifire/translations/et.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "not_intellifire_device": "See pole IntelliFire seade.", + "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { + "api_error": "Sisselogimine nurjus", "cannot_connect": "\u00dchendamine nurjus", + "iftapi_connect": "\u00dchendumine iftapi.net'iga nurjus", "unknown": "Ootamatu t\u00f5rge" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Salas\u00f5na", + "username": "E-posti aadress" + } + }, + "dhcp_confirm": { + "description": "Kas h\u00e4\u00e4lestada {host}\nSeerianumber: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Host (IP aadress)" + }, + "description": "Kohalik konfiguratsioon" + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "Avastati j\u00e4rgmised IntelliFire seadmed. Palun vali millist soovid seadistada.", + "title": "Seadme valik" + }, "user": { "data": { "host": "Host" diff --git a/homeassistant/components/intellifire/translations/fr.json b/homeassistant/components/intellifire/translations/fr.json index 3b0762be825..5c478358345 100644 --- a/homeassistant/components/intellifire/translations/fr.json +++ b/homeassistant/components/intellifire/translations/fr.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "not_intellifire_device": "N'est pas un appareil IntelliFire.", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { + "api_error": "La connexion a \u00e9chou\u00e9", "cannot_connect": "\u00c9chec de connexion", + "iftapi_connect": "Erreur lors de la connexion \u00e0 iftapi.net", "unknown": "Erreur inattendue" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Mot de passe", + "username": "Courriel" + } + }, + "dhcp_confirm": { + "description": "Voulez-vous configurer {host}\nNum\u00e9ro de s\u00e9rie\u00a0: {serial}\u00a0?" + }, + "manual_device_entry": { + "data": { + "host": "H\u00f4te (adresse IP)" + }, + "description": "Configuration locale" + }, + "pick_device": { + "data": { + "host": "H\u00f4te" + }, + "description": "Les appareils IntelliFire suivants ont \u00e9t\u00e9 d\u00e9couverts. Veuillez s\u00e9lectionner celui que vous souhaitez configurer.", + "title": "S\u00e9lection de l'appareil" + }, "user": { "data": { "host": "H\u00f4te" diff --git a/homeassistant/components/intellifire/translations/he.json b/homeassistant/components/intellifire/translations/he.json index 1699e0f8e19..e4b64392380 100644 --- a/homeassistant/components/intellifire/translations/he.json +++ b/homeassistant/components/intellifire/translations/he.json @@ -1,13 +1,30 @@ { "config": { "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { + "api_config": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "username": "\u05d3\u05d5\u05d0\"\u05dc" + } + }, + "manual_device_entry": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7" + } + }, + "pick_device": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7" + } + }, "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7" diff --git a/homeassistant/components/intellifire/translations/hu.json b/homeassistant/components/intellifire/translations/hu.json index c46c7b02f5a..f5d11abe4c0 100644 --- a/homeassistant/components/intellifire/translations/hu.json +++ b/homeassistant/components/intellifire/translations/hu.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "not_intellifire_device": "Nem egy IntelliFire eszk\u00f6z.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { + "api_error": "A bejelentkez\u00e9s sikertelen", "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "iftapi_connect": "Hiba az iftapi.net-hez val\u00f3 csatlakoz\u00e1sban", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Jelsz\u00f3", + "username": "E-mail" + } + }, + "dhcp_confirm": { + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {host}\nGy\u00e1ri sz\u00e1m: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "IP C\u00edm" + }, + "description": "Helyi konfigur\u00e1ci\u00f3" + }, + "pick_device": { + "data": { + "host": "C\u00edm" + }, + "description": "A k\u00f6vetkez\u0151 IntelliFire eszk\u00f6z\u00f6k \u00e9szlelve. K\u00e9rj\u00fck, v\u00e1lassza ki, melyiket szeretn\u00e9 konfigur\u00e1lni.", + "title": "Eszk\u00f6z v\u00e1laszt\u00e1sa" + }, "user": { "data": { "host": "C\u00edm" diff --git a/homeassistant/components/intellifire/translations/id.json b/homeassistant/components/intellifire/translations/id.json index c04f83e4b59..07ec946b6ba 100644 --- a/homeassistant/components/intellifire/translations/id.json +++ b/homeassistant/components/intellifire/translations/id.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi" + "already_configured": "Perangkat sudah dikonfigurasi", + "not_intellifire_device": "Bukan Perangkat IntelliFire.", + "reauth_successful": "Autentikasi ulang berhasil" }, "error": { + "api_error": "Gagal masuk", "cannot_connect": "Gagal terhubung", + "iftapi_connect": "Kesalahan saat menyambung ke iftapi.net", "unknown": "Kesalahan yang tidak diharapkan" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Kata Sandi", + "username": "Email" + } + }, + "dhcp_confirm": { + "description": "Ingin menyiapkan {host}\nSerial: ({serial})?" + }, + "manual_device_entry": { + "data": { + "host": "Host (Alamat IP)" + }, + "description": "Konfigurasi Lokal" + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "Perangkat IntelliFire berikut ditemukan. Pilih yang ingin dikonfigurasikan.", + "title": "Pemilihan Perangkat" + }, "user": { "data": { "host": "Host" diff --git a/homeassistant/components/intellifire/translations/it.json b/homeassistant/components/intellifire/translations/it.json index e8bfd780908..8689840c82c 100644 --- a/homeassistant/components/intellifire/translations/it.json +++ b/homeassistant/components/intellifire/translations/it.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "not_intellifire_device": "Non \u00e8 un dispositivo IntelliFire.", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { + "api_error": "Accesso non riuscito", "cannot_connect": "Impossibile connettersi", + "iftapi_connect": "Errore durante la connessione a iftapi.net", "unknown": "Errore imprevisto" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Password", + "username": "Email" + } + }, + "dhcp_confirm": { + "description": "Vuoi configurare {host}\n Seriale: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Host (indirizzo IP)" + }, + "description": "Configurazione locale" + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "Sono stati rilevati i seguenti dispositivi IntelliFire. Seleziona quello che desideri configurare.", + "title": "Selezione del dispositivo" + }, "user": { "data": { "host": "Host" diff --git a/homeassistant/components/intellifire/translations/ja.json b/homeassistant/components/intellifire/translations/ja.json index 718b4432290..c623f0f59cf 100644 --- a/homeassistant/components/intellifire/translations/ja.json +++ b/homeassistant/components/intellifire/translations/ja.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "not_intellifire_device": "IntelliFire\u30c7\u30d0\u30a4\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { + "api_error": "\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "iftapi_connect": "iftapi.net\u3078\u306e\u63a5\u7d9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "username": "E\u30e1\u30fc\u30eb" + } + }, + "dhcp_confirm": { + "description": "{host} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f\n\u30b7\u30ea\u30a2\u30eb: {serial}\uff1f" + }, + "manual_device_entry": { + "data": { + "host": "\u30db\u30b9\u30c8" + }, + "description": "\u30ed\u30fc\u30ab\u30eb\u8a2d\u5b9a" + }, + "pick_device": { + "data": { + "host": "\u30db\u30b9\u30c8" + }, + "description": "\u4ee5\u4e0b\u306eIntelliFire\u30c7\u30d0\u30a4\u30b9\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002\u8a2d\u5b9a\u3057\u305f\u3044\u3082\u306e\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "title": "\u30c7\u30d0\u30a4\u30b9\u306e\u9078\u629e" + }, "user": { "data": { "host": "\u30db\u30b9\u30c8" diff --git a/homeassistant/components/intellifire/translations/nl.json b/homeassistant/components/intellifire/translations/nl.json index 735a4df572f..a4f2b1c304a 100644 --- a/homeassistant/components/intellifire/translations/nl.json +++ b/homeassistant/components/intellifire/translations/nl.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd" + "already_configured": "Apparaat is al geconfigureerd", + "not_intellifire_device": "Niet een IntelliFire apparaat.", + "reauth_successful": "Herauthenticatie was succesvol" }, "error": { + "api_error": "Inloggen mislukt", "cannot_connect": "Kan geen verbinding maken", + "iftapi_connect": "Fout bij het verbinden met iftapi.net", "unknown": "Onverwachte fout" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Wachtwoord", + "username": "E-mail" + } + }, + "dhcp_confirm": { + "description": "Wilt u {host} instellen\n Serieel: {serial} ?" + }, + "manual_device_entry": { + "data": { + "host": "Host (IP-adres)" + }, + "description": "Lokale configuratie" + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "De volgende IntelliFire-apparaten zijn ontdekt. Selecteer welke u wilt configureren.", + "title": "Apparaat selectie" + }, "user": { "data": { "host": "Host" diff --git a/homeassistant/components/intellifire/translations/no.json b/homeassistant/components/intellifire/translations/no.json index 12ee27af925..8175a085f30 100644 --- a/homeassistant/components/intellifire/translations/no.json +++ b/homeassistant/components/intellifire/translations/no.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert" + "already_configured": "Enheten er allerede konfigurert", + "not_intellifire_device": "Ikke en IntelliFire-enhet.", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" }, "error": { + "api_error": "Innlogging feilet", "cannot_connect": "Tilkobling mislyktes", + "iftapi_connect": "Feil ved tilkobling til iftapi.net", "unknown": "Uventet feil" }, + "flow_title": "{serial} ( {host} )", "step": { + "api_config": { + "data": { + "password": "Passord", + "username": "E-post" + } + }, + "dhcp_confirm": { + "description": "Vil du konfigurere {host}\n Serienummer: {serial} ?" + }, + "manual_device_entry": { + "data": { + "host": "Vert (IP-adresse)" + }, + "description": "Lokal konfigurasjon" + }, + "pick_device": { + "data": { + "host": "Vert" + }, + "description": "F\u00f8lgende IntelliFire-enheter ble oppdaget. Velg hvilken du \u00f8nsker \u00e5 konfigurere.", + "title": "Enhetsvalg" + }, "user": { "data": { "host": "Vert" diff --git a/homeassistant/components/intellifire/translations/pl.json b/homeassistant/components/intellifire/translations/pl.json index d455990b1f0..f63be88814f 100644 --- a/homeassistant/components/intellifire/translations/pl.json +++ b/homeassistant/components/intellifire/translations/pl.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "not_intellifire_device": "To nie jest urz\u0105dzenie IntelliFire.", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { + "api_error": "Logowanie nie powiod\u0142o si\u0119", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "iftapi_connect": "B\u0142\u0105d po\u0142\u0105czenia z iftapi.net", "unknown": "Nieoczekiwany b\u0142\u0105d" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Has\u0142o", + "username": "Adres e-mail" + } + }, + "dhcp_confirm": { + "description": "Czy chcesz skonfigurowa\u0107 {host}\nNumer seryjny: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Nazwa hosta lub adres IP" + }, + "description": "Konfiguracja lokalna" + }, + "pick_device": { + "data": { + "host": "Nazwa hosta lub adres IP" + }, + "description": "Wykryto nast\u0119puj\u0105ce urz\u0105dzenia IntelliFire. Wybierz, kt\u00f3re chcesz skonfigurowa\u0107.", + "title": "Wyb\u00f3r urz\u0105dzenia" + }, "user": { "data": { "host": "Nazwa hosta lub adres IP" diff --git a/homeassistant/components/intellifire/translations/pt-BR.json b/homeassistant/components/intellifire/translations/pt-BR.json index ff6ede166a9..babcc22bd97 100644 --- a/homeassistant/components/intellifire/translations/pt-BR.json +++ b/homeassistant/components/intellifire/translations/pt-BR.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "not_intellifire_device": "N\u00e3o \u00e9 um dispositivo IntelliFire.", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { + "api_error": "Login falhou", "cannot_connect": "Falha ao conectar", + "iftapi_connect": "Erro ao conectar ao iftapi.net", "unknown": "Erro inesperado" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Senha", + "username": "Email" + } + }, + "dhcp_confirm": { + "description": "Voc\u00ea deseja configurar {host}\nSerial: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Host (endere\u00e7o IP)" + }, + "description": "Configura\u00e7\u00e3o local" + }, + "pick_device": { + "data": { + "host": "Nome do host" + }, + "description": "Os seguintes dispositivos IntelliFire foram descobertos. Por favor, selecione o que voc\u00ea deseja configura.", + "title": "Sele\u00e7\u00e3o de dispositivo" + }, "user": { "data": { "host": "Nome do host" diff --git a/homeassistant/components/intellifire/translations/ru.json b/homeassistant/components/intellifire/translations/ru.json index ffde0514cde..8e201924028 100644 --- a/homeassistant/components/intellifire/translations/ru.json +++ b/homeassistant/components/intellifire/translations/ru.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "not_intellifire_device": "\u041d\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e IntelliFire.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { + "api_error": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "iftapi_connect": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a iftapi.net", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "username": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b" + } + }, + "dhcp_confirm": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {host}\n\u0421\u0435\u0440\u0438\u0439\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "\u0414\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441" + }, + "description": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "pick_device": { + "data": { + "host": "\u0425\u043e\u0441\u0442" + }, + "description": "\u0411\u044b\u043b\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 IntelliFire. \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u043a\u0430\u043a\u043e\u0435 \u0438\u0437 \u043d\u0438\u0445 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c.", + "title": "\u0412\u044b\u0431\u043e\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" diff --git a/homeassistant/components/intellifire/translations/tr.json b/homeassistant/components/intellifire/translations/tr.json index 05846c9d760..3a22e7c3680 100644 --- a/homeassistant/components/intellifire/translations/tr.json +++ b/homeassistant/components/intellifire/translations/tr.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "not_intellifire_device": "IntelliFire Cihaz\u0131 de\u011fil.", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { + "api_error": "Oturum a\u00e7ma ba\u015far\u0131s\u0131z oldu", "cannot_connect": "Ba\u011flanma hatas\u0131", + "iftapi_connect": "iftapi.net'e ba\u011flan\u0131rken hata olu\u015ftu", "unknown": "Beklenmeyen hata" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "Parola", + "username": "E-posta" + } + }, + "dhcp_confirm": { + "description": "Kurulumu yapmak {host} ister misiniz?\nSeri No: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Sunucu (IP Adresi)" + }, + "description": "Yerel Yap\u0131land\u0131rma" + }, + "pick_device": { + "data": { + "host": "Sunucu" + }, + "description": "A\u015fa\u011f\u0131daki IntelliFire cihazlar\u0131 ke\u015ffedildi. L\u00fctfen yap\u0131land\u0131rmak istedi\u011finizi se\u00e7in.", + "title": "Cihaz Se\u00e7imi" + }, "user": { "data": { "host": "Sunucu" diff --git a/homeassistant/components/intellifire/translations/zh-Hant.json b/homeassistant/components/intellifire/translations/zh-Hant.json index 9847ae248f7..107f9cca74c 100644 --- a/homeassistant/components/intellifire/translations/zh-Hant.json +++ b/homeassistant/components/intellifire/translations/zh-Hant.json @@ -1,13 +1,40 @@ { "config": { "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "not_intellifire_device": "\u975e IntelliFire \u88dd\u7f6e\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { + "api_error": "\u767b\u5165\u5931\u6557", "cannot_connect": "\u9023\u7dda\u5931\u6557", + "iftapi_connect": "\u9023\u7dda\u81f3 iftapi.net \u932f\u8aa4", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, + "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "\u5bc6\u78bc", + "username": "\u96fb\u5b50\u90f5\u4ef6" + } + }, + "dhcp_confirm": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {host}\n\u5e8f\u865f\uff1a{serial}\uff1f" + }, + "manual_device_entry": { + "data": { + "host": "\u4e3b\u6a5f\uff08IP \u4f4d\u5740\uff09" + }, + "description": "\u672c\u5730\u7aef\u8a2d\u5b9a" + }, + "pick_device": { + "data": { + "host": "\u4e3b\u6a5f\u7aef" + }, + "description": "\u641c\u5c0b\u5230\u4ee5\u4e0b IntelliFire \u88dd\u7f6e\uff0c\u8acb\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e\u3002", + "title": "\u88dd\u7f6e\u9078\u64c7" + }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef" diff --git a/homeassistant/components/iotawatt/translations/hu.json b/homeassistant/components/iotawatt/translations/hu.json index 52d46f97a84..5f5d30136da 100644 --- a/homeassistant/components/iotawatt/translations/hu.json +++ b/homeassistant/components/iotawatt/translations/hu.json @@ -11,7 +11,7 @@ "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "Az IoTawatt eszk\u00f6z hiteles\u00edt\u00e9st ig\u00e9nyel. K\u00e9rj\u00fck, adja meg felhaszn\u00e1l\u00f3nev\u00e9t \u00e9s jelszav\u00e1t, majd kattintson a K\u00fcld\u00e9s gombra." + "description": "Az IoTawatt eszk\u00f6z hiteles\u00edt\u00e9st ig\u00e9nyel. K\u00e9rj\u00fck, adja meg felhaszn\u00e1l\u00f3nev\u00e9t \u00e9s jelszav\u00e1t, majd folytassa." }, "user": { "data": { diff --git a/homeassistant/components/iotawatt/translations/id.json b/homeassistant/components/iotawatt/translations/id.json index ef50c938292..bddda73fa50 100644 --- a/homeassistant/components/iotawatt/translations/id.json +++ b/homeassistant/components/iotawatt/translations/id.json @@ -11,7 +11,7 @@ "password": "Kata Sandi", "username": "Nama Pengguna" }, - "description": "Perangkat IoTawatt memerlukan otentikasi. Masukkan nama pengguna dan kata sandi dan klik tombol Kirim." + "description": "Perangkat IoTawatt memerlukan autentikasi. Masukkan nama pengguna dan kata sandi dan klik tombol Kirim." }, "user": { "data": { diff --git a/homeassistant/components/ipma/translations/hu.json b/homeassistant/components/ipma/translations/hu.json index 00ba66d2dd7..16653645052 100644 --- a/homeassistant/components/ipma/translations/hu.json +++ b/homeassistant/components/ipma/translations/hu.json @@ -9,7 +9,7 @@ "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", "mode": "M\u00f3d", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Portug\u00e1l Atmoszf\u00e9ra Int\u00e9zet", "title": "Elhelyezked\u00e9s" diff --git a/homeassistant/components/ipp/translations/it.json b/homeassistant/components/ipp/translations/it.json index c5a2a613eeb..96319641c6b 100644 --- a/homeassistant/components/ipp/translations/it.json +++ b/homeassistant/components/ipp/translations/it.json @@ -6,7 +6,7 @@ "connection_upgrade": "Impossibile connettersi alla stampante a causa della necessit\u00e0 dell'aggiornamento della connessione.", "ipp_error": "Si \u00e8 verificato un errore IPP.", "ipp_version_error": "Versione IPP non supportata dalla stampante.", - "parse_error": "Impossibile analizza la risposta dalla stampante.", + "parse_error": "Impossibile analizzare la risposta dalla stampante.", "unique_id_required": "Identificazione univoca del dispositivo mancante necessaria per l'individuazione." }, "error": { diff --git a/homeassistant/components/ipp/translations/pl.json b/homeassistant/components/ipp/translations/pl.json index b44904095de..b2eb67a6d93 100644 --- a/homeassistant/components/ipp/translations/pl.json +++ b/homeassistant/components/ipp/translations/pl.json @@ -24,7 +24,7 @@ "verify_ssl": "Weryfikacja certyfikatu SSL" }, "description": "Skonfiguruj drukark\u0119 za pomoc\u0105 protoko\u0142u IPP (Internet Printing Protocol), aby zintegrowa\u0107 j\u0105 z Home Assistantem.", - "title": "Po\u0142\u0105cz swoj\u0105 drukark\u0119" + "title": "Po\u0142\u0105czenie z Twoj\u0105 drukark\u0105" }, "zeroconf_confirm": { "description": "Czy chcesz doda\u0107 drukark\u0119 o nazwie `{name}` do Home Assistanta?", diff --git a/homeassistant/components/iss/translations/hu.json b/homeassistant/components/iss/translations/hu.json index 1d75dd9ed3f..bfe593e9592 100644 --- a/homeassistant/components/iss/translations/hu.json +++ b/homeassistant/components/iss/translations/hu.json @@ -9,7 +9,7 @@ "data": { "show_on_map": "Megjelenjen a t\u00e9rk\u00e9pen?" }, - "description": "Szeretn\u00e9 konfigur\u00e1lni a Nemzetk\u00f6zi \u0170r\u00e1llom\u00e1st?" + "description": "Szeretn\u00e9 konfigur\u00e1lni a Nemzetk\u00f6zi \u0170r\u00e1llom\u00e1st (ISS)?" } } }, diff --git a/homeassistant/components/iss/translations/pt-BR.json b/homeassistant/components/iss/translations/pt-BR.json index 5e34b2eec1b..c69fefed0c5 100644 --- a/homeassistant/components/iss/translations/pt-BR.json +++ b/homeassistant/components/iss/translations/pt-BR.json @@ -7,7 +7,7 @@ "step": { "user": { "data": { - "show_on_map": "Mostrar no mapa?" + "show_on_map": "Mostrar no mapa" }, "description": "Deseja configurar a Esta\u00e7\u00e3o Espacial Internacional (ISS)?" } @@ -17,7 +17,7 @@ "step": { "init": { "data": { - "show_on_map": "Mostrar no mapa?" + "show_on_map": "Mostrar no mapa" } } } diff --git a/homeassistant/components/kaleidescape/translations/cs.json b/homeassistant/components/kaleidescape/translations/cs.json new file mode 100644 index 00000000000..deb0693b00d --- /dev/null +++ b/homeassistant/components/kaleidescape/translations/cs.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + }, + "flow_title": "{model} ({name})" + } +} \ No newline at end of file diff --git a/homeassistant/components/kaleidescape/translations/hu.json b/homeassistant/components/kaleidescape/translations/hu.json index 953de8119ed..cd8b00e18a3 100644 --- a/homeassistant/components/kaleidescape/translations/hu.json +++ b/homeassistant/components/kaleidescape/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", "unsupported": "Nem t\u00e1mogatott eszk\u00f6z" }, diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 43f72f49867..3cbc8b57b0b 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -5,7 +5,8 @@ "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_ip_address": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d IPv4 \u0430\u0434\u0440\u0435\u0441." }, "step": { "manual_tunnel": { @@ -14,11 +15,23 @@ "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u043d\u0435 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043d\u0438)", "port": "\u041f\u043e\u0440\u0442", "tunneling_type": "KNX \u0442\u0443\u043d\u0435\u043b\u0435\u043d \u0442\u0438\u043f" + }, + "data_description": { + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, "routing": { "data": { "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u043d\u0435 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043d\u0438)" + }, + "data_description": { + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0437\u0430 \u0434\u0430 \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." + } + }, + "secure_manual": { + "data": { + "user_id": "\u0418\u0414 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f" } } } @@ -28,6 +41,9 @@ "init": { "data": { "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441 (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u043d\u0435 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043d\u0438)" + }, + "data_description": { + "local_ip": "\u0418\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 `0.0.0.0` \u0437\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index dd553f293be..b79887e1586 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3" + "cannot_connect": "Ha fallat la connexi\u00f3", + "file_not_found": "No s'ha trobat el fitxer `.knxkeys` especificat a la ruta config/.storage/knx/", + "invalid_individual_address": "El valor no coincideix amb el patr\u00f3 d'adre\u00e7a KNX individual.\n'area.line.device'", + "invalid_ip_address": "Adre\u00e7a IPv4 inv\u00e0lida.", + "invalid_signature": "La contrasenya per desxifrar el fitxer `.knxkeys` \u00e9s incorrecta." }, "step": { "manual_tunnel": { "data": { "host": "Amfitri\u00f3", "individual_address": "Adre\u00e7a individual de la connexi\u00f3", - "local_ip": "IP local de Home Assistant (deixa-ho en blanc si no n'est\u00e0s segur/a)", + "local_ip": "IP local de Home Assistant", "port": "Port", "route_back": "Encaminament de retorn / Mode NAT", "tunneling_type": "Tipus de t\u00fanel KNX" }, + "data_description": { + "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", + "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic.", + "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP." + }, "description": "Introdueix la informaci\u00f3 de connexi\u00f3 del dispositiu de t\u00fanel." }, "routing": { "data": { - "individual_address": "Adre\u00e7a individual de la connexi\u00f3 d'encaminament", - "local_ip": "IP local de Home Assistant (deixa-ho en blanc si no n'est\u00e0s segur/a)", - "multicast_group": "Grup de multidifusi\u00f3 utilitzat per a l'encaminament", - "multicast_port": "Port de multidifusi\u00f3 utilitzat per a l'encaminament" + "individual_address": "Adre\u00e7a individual", + "local_ip": "IP local de Home Assistant", + "multicast_group": "Grup multidifusi\u00f3", + "multicast_port": "Port multidifusi\u00f3" + }, + "data_description": { + "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", + "local_ip": "Deixa-ho en blanc per utilitzar el descobriment autom\u00e0tic." }, "description": "Configura les opcions d'encaminament." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nom del teu fitxer `.knxkeys` (inclosa l'extensi\u00f3)", + "knxkeys_password": "Contrasenya per desxifrar el fitxer `.knxkeys`." + }, + "data_description": { + "knxkeys_filename": "S'espera que el fitxer es trobi al teu directori de configuraci\u00f3 a `.storage/knx/`.\nA Home Assistant aix\u00f2 estaria a `/config/.storage/knx/`\nExemple: `el_meu_projecte.knxkeys`", + "knxkeys_password": "S'ha definit durant l'exportaci\u00f3 del fitxer des d'ETS." + }, + "description": "Introdueix la informaci\u00f3 del teu fitxer `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "Contrasenya d'autenticaci\u00f3 del dispositiu", + "user_id": "ID d'usuari", + "user_password": "Contrasenya d'usuari" + }, + "data_description": { + "device_authentication": "S'estableix al panell 'IP' de la interf\u00edcie d'ETS.", + "user_id": "Sovint \u00e9s el n\u00famero del t\u00fanel +1. Per tant, 'T\u00fanel 2' tindria l'ID d'usuari '3'.", + "user_password": "Contrasenya per a la connexi\u00f3 t\u00fanel espec\u00edfica configurada al panell 'Propietats' del t\u00fanel a ETS." + }, + "description": "Introdueix la informaci\u00f3 de seguretat IP (IP Secure)." + }, + "secure_tunneling": { + "description": "Selecciona com vols configurar KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilitza un fitxer `.knxkeys` que contingui les claus de seguretat IP (IP Secure)", + "secure_manual": "Configura manualment les claus de seguretat IP (IP Secure)" + } + }, "tunnel": { "data": { "gateway": "Connexi\u00f3 t\u00fanel KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Tipus de connexi\u00f3 KNX", "individual_address": "Adre\u00e7a individual predeterminada", - "local_ip": "IP local de Home Assistant (utilitza 0.0.0.0 per a detecci\u00f3 autom\u00e0tica)", - "multicast_group": "Grup de multidifusi\u00f3 utilitzat per a encaminament i descobriment", - "multicast_port": "Port de multidifusi\u00f3 utilitzat per a encaminament i descobriment", - "rate_limit": "Telegrames de sortida m\u00e0xims per segon", - "state_updater": "Habilita la lectura global d'estats del bus KNX" + "local_ip": "IP local de Home Assistant", + "multicast_group": "Grup multidifusi\u00f3", + "multicast_port": "Port multidifusi\u00f3", + "rate_limit": "Freq\u00fc\u00e8ncia m\u00e0xima", + "state_updater": "Actualitzador d'estat" + }, + "data_description": { + "individual_address": "Adre\u00e7a KNX per utilitzar amb Home Assistant, p. ex. `0.0.4`", + "local_ip": "Utilitza `0.0.0.0` per al descobriment autom\u00e0tic.", + "multicast_group": "Utilitzada per a l'encaminament i el descobriment. Per defecte: `224.0.23.12`", + "multicast_port": "Utilitzat per a l'encaminament i el descobriment. Per defecte: `3671`", + "rate_limit": "Telegrames de sortida m\u00e0xims per segon.\nRecomanat: de 20 a 40", + "state_updater": "Activa o desactiva globalment la lectura d'estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX, les opcions d'entitat `sync_state` no tindran cap efecte." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Encaminament de retorn / Mode NAT", "tunneling_type": "Tipus de t\u00fanel KNX" + }, + "data_description": { + "host": "Adre\u00e7a IP del dispositiu de tunelitzaci\u00f3 KNX/IP.", + "port": "Port del dispositiu de tunelitzaci\u00f3 KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 2f7795153dc..588a853a8b6 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -5,34 +5,78 @@ "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen" + "cannot_connect": "Verbindung fehlgeschlagen", + "file_not_found": "Die angegebene `.knxkeys`-Datei wurde im Pfad config/.storage/knx/ nicht gefunden.", + "invalid_individual_address": "Wert ist keine g\u00fcltige physikalische Adresse. 'Bereich.Linie.Teilnehmer'", + "invalid_ip_address": "Ung\u00fcltige IPv4 Adresse.", + "invalid_signature": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei ist ung\u00fcltig." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "Physikalische Adresse f\u00fcr die Verbindung", - "local_ip": "Lokale IP von Home Assistant (f\u00fcr automatische Erkennung leer lassen)", + "local_ip": "Lokale IP von Home Assistant", "port": "Port", "route_back": "Route Back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, - "description": "Bitte gib die Verbindungsinformationen deines Tunnelger\u00e4ts ein." + "data_description": { + "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", + "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden.", + "port": "Port der KNX/IP-Tunneling Schnittstelle." + }, + "description": "Bitte gib die Verbindungsinformationen deiner Tunnel-Schnittstelle ein." }, "routing": { "data": { - "individual_address": "Physikalische Adresse f\u00fcr die Routingverbindung", - "local_ip": "Lokale IP von Home Assistant (f\u00fcr automatische Erkennung leer lassen)", - "multicast_group": "Die f\u00fcr das Routing verwendete Multicast-Gruppe", - "multicast_port": "Der f\u00fcr das Routing verwendete Multicast-Port" + "individual_address": "Physikalische Adresse", + "local_ip": "Lokale IP von Home Assistant", + "multicast_group": "Multicast-Gruppe", + "multicast_port": "Multicast-Port" + }, + "data_description": { + "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z. B. \u201e0.0.4\u201c.", + "local_ip": "Lasse das Feld leer, um die automatische Erkennung zu verwenden." }, "description": "Bitte konfiguriere die Routing-Optionen." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Der Dateiname deiner `.knxkeys`-Datei (einschlie\u00dflich Erweiterung)", + "knxkeys_password": "Das Passwort zum Entschl\u00fcsseln der `.knxkeys`-Datei" + }, + "data_description": { + "knxkeys_filename": "Die Datei wird in deinem Konfigurationsverzeichnis unter `.storage/knx/` erwartet.\nIm Home Assistant OS w\u00e4re dies `/config/.storage/knx/`\nBeispiel: `my_project.knxkeys`", + "knxkeys_password": "Dies wurde beim Exportieren der Datei aus ETS gesetzt." + }, + "description": "Bitte gib die Informationen f\u00fcr deine `.knxkeys`-Datei ein." + }, + "secure_manual": { + "data": { + "device_authentication": "Ger\u00e4te-Authentifizierungscode", + "user_id": "Benutzer-ID", + "user_password": "Benutzer-Passwort" + }, + "data_description": { + "device_authentication": "Dies wird im Feld \"IP\" der Schnittstelle in ETS eingestellt.", + "user_id": "Dies ist oft die Tunnelnummer +1. \u201eTunnel 2\u201c h\u00e4tte also die Benutzer-ID \u201e3\u201c.", + "user_password": "Passwort f\u00fcr die spezifische Tunnelverbindung, die im Bereich \u201eEigenschaften\u201c des Tunnels in ETS festgelegt wurde." + }, + "description": "Bitte gib deine IP-Secure Informationen ein." + }, + "secure_tunneling": { + "description": "W\u00e4hle aus, wie du KNX/IP-Secure konfigurieren m\u00f6chtest.", + "menu_options": { + "secure_knxkeys": "Verwende eine `.knxkeys`-Datei, die IP-Secure-Schl\u00fcssel enth\u00e4lt", + "secure_manual": "IP-Secure Schl\u00fcssel manuell konfigurieren" + } + }, "tunnel": { "data": { "gateway": "KNX Tunnel Verbindung" }, - "description": "Bitte w\u00e4hle ein Gateway aus der Liste aus." + "description": "Bitte w\u00e4hle eine Schnittstelle aus der Liste aus." }, "type": { "data": { @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX-Verbindungstyp", "individual_address": "Standard physikalische Adresse", - "local_ip": "Lokale IP von Home Assistant (verwende 0.0.0.0 f\u00fcr automatische Erkennung)", - "multicast_group": "Multicast-Gruppe f\u00fcr Routing und Discovery", - "multicast_port": "Multicast-Port f\u00fcr Routing und Discovery", - "rate_limit": "Maximal ausgehende Telegramme pro Sekunde", - "state_updater": "Lesen von Zust\u00e4nden von dem KNX Bus global freigeben" + "local_ip": "Lokale IP von Home Assistant", + "multicast_group": "Multicast-Gruppe", + "multicast_port": "Multicast-Port", + "rate_limit": "Telegrammdrossel", + "state_updater": "Status-Updater" + }, + "data_description": { + "individual_address": "Physikalische Adresse, die von Home Assistant verwendet werden soll, z.\u00a0B. \u201e0.0.4\u201c.", + "local_ip": "Verwende \"0.0.0.0\" f\u00fcr die automatische Erkennung.", + "multicast_group": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: `224.0.23.12`", + "multicast_port": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: \u201e3671\u201c.", + "rate_limit": "Maximal gesendete Telegramme pro Sekunde.\nEmpfohlen: 20 bis 40", + "state_updater": "Aktiviere oder deaktiviere global das Lesen von Zust\u00e4nden vom KNX-Bus. Wenn deaktiviert, ruft Home Assistant nicht aktiv Zust\u00e4nde vom KNX-Bus ab, \u201esync_state\u201c-Entity-Optionen haben keine Auswirkung." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Route Back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" + }, + "data_description": { + "host": "IP-Adresse der KNX/IP-Tunneling Schnittstelle.", + "port": "Port der KNX/IP-Tunneling Schnittstelle." } } } diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 75be33560ca..81dd03f73fa 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -5,7 +5,11 @@ "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "file_not_found": "\u03a4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae config/.storage/knx/", + "invalid_individual_address": "\u0397 \u03c4\u03b9\u03bc\u03ae \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03bc\u03bf\u03c4\u03af\u03b2\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bc\u03b5\u03bc\u03bf\u03bd\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX.\n \"area.line.device\"", + "invalid_ip_address": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IPv4.", + "invalid_signature": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03b5\u03af\u03bd\u03b1\u03b9 \u03bb\u03ac\u03b8\u03bf\u03c2." }, "step": { "manual_tunnel": { @@ -17,6 +21,11 @@ "route_back": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 Route Back / NAT", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, + "data_description": { + "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", + "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", + "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP." + }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03ac\u03c2 \u03c3\u03b1\u03c2." }, "routing": { @@ -26,8 +35,43 @@ "multicast_group": "\u0397 \u03bf\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b5\u03ba\u03c0\u03bf\u03bc\u03c0\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7", "multicast_port": "\u0397 \u03b8\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7" }, + "data_description": { + "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", + "local_ip": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." + }, "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7\u03c2." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "\u03a4\u03bf \u03c0\u03bb\u03ae\u03c1\u03b5\u03c2 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys \u03c3\u03b1\u03c2", + "knxkeys_password": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03ba\u03c1\u03c5\u03c0\u03c4\u03bf\u03b3\u03c1\u03ac\u03c6\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 knxkeys" + }, + "data_description": { + "knxkeys_filename": "\u03a4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03b1\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03ba\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf `.storage/knx/`.\n \u03a3\u03c4\u03bf Home Assistant OS \u03b1\u03c5\u03c4\u03cc \u03b8\u03b1 \u03ae\u03c4\u03b1\u03bd `/config/.storage/knx/`\n \u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: `my_project.knxkeys`", + "knxkeys_password": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03be\u03b1\u03b3\u03c9\u03b3\u03ae \u03c4\u03bf\u03c5 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5 \u03b1\u03c0\u03cc \u03c4\u03bf ETS." + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c3\u03b1\u03c2." + }, + "secure_manual": { + "data": { + "device_authentication": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", + "user_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", + "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "data_description": { + "device_authentication": "\u0391\u03c5\u03c4\u03cc \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u00abIP\u00bb \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae\u03c2 \u03c3\u03c4\u03bf ETS.", + "user_id": "\u0391\u03c5\u03c4\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c7\u03bd\u03ac \u03c4\u03bf \u03bd\u03bf\u03cd\u03bc\u03b5\u03c1\u03bf +1 \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2. \u0388\u03c4\u03c3\u03b9, \u03b7 '\u03a3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1 2' \u03b8\u03b1 \u03ad\u03c7\u03b5\u03b9 User-ID '3'.", + "user_password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03c3\u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \"\u0399\u03b4\u03b9\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03c4\u03b7\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 \u03c3\u03c4\u03bf ETS." + }, + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure." + }, + "secure_tunneling": { + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03ce\u03c2 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf IP Secure.", + "menu_options": { + "secure_knxkeys": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf knxkeys \u03c0\u03bf\u03c5 \u03c0\u03b5\u03c1\u03b9\u03ad\u03c7\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 IP secure", + "secure_manual": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 IP secure \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1" + } + }, "tunnel": { "data": { "gateway": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" @@ -47,12 +91,20 @@ "init": { "data": { "connection_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 KNX", - "individual_address": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b1\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", + "individual_address": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03c6\u03c5\u03c3\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7", "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 0.0.0.0.0 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", "multicast_group": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7", "multicast_port": "\u0398\u03cd\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ae\u03c2 \u03b4\u03b9\u03b1\u03bd\u03bf\u03bc\u03ae\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7", "rate_limit": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b1 \u03b5\u03be\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b7\u03bb\u03b5\u03b3\u03c1\u03b1\u03c6\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf", "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b8\u03bf\u03bb\u03b9\u03ba\u03ac \u03c4\u03b9\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus" + }, + "data_description": { + "individual_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 KNX \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b1\u03c0\u03cc \u03c4\u03bf Home Assistant, \u03c0.\u03c7. `0.0.4`.", + "local_ip": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 `0.0.0.0.0` \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7.", + "multicast_group": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae: `224.0.23.12`", + "multicast_port": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae: `3671`", + "rate_limit": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b1 \u03b5\u03be\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b7\u03bb\u03b5\u03b3\u03c1\u03b1\u03c6\u03ae\u03bc\u03b1\u03c4\u03b1 \u03b1\u03bd\u03ac \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03bf.\n \u03a0\u03c1\u03bf\u03c4\u03b5\u03af\u03bd\u03b5\u03c4\u03b1\u03b9: 20 \u03ad\u03c9\u03c2 40", + "state_updater": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03c9\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b4\u03af\u03b1\u03c5\u03bb\u03bf KNX. \u038c\u03c4\u03b1\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf, \u03c4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b8\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ac \u03b5\u03bd\u03b5\u03c1\u03b3\u03ac \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf KNX Bus, \u03bf\u03b9 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 `sync_state` \u03b4\u03b5\u03bd \u03b8\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ba\u03b1\u03bc\u03af\u03b1 \u03b5\u03c0\u03af\u03b4\u03c1\u03b1\u03c3\u03b7." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "\u0398\u03cd\u03c1\u03b1", "route_back": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 Route Back / NAT", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" + }, + "data_description": { + "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP.", + "port": "\u0398\u03cd\u03c1\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b9\u03bf\u03c7\u03ad\u03c4\u03b5\u03c5\u03c3\u03b7\u03c2 KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index 538f6b6a5c6..ba073eba888 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "Failed to connect", "file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", - "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'..'", + "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_ip_address": "Invalid IPv4 address.", "invalid_signature": "The password to decrypt the `.knxkeys` file is wrong." }, @@ -15,8 +15,10 @@ "manual_tunnel": { "data": { "host": "Host", + "individual_address": "Individual address for the connection", "local_ip": "Local IP of Home Assistant", "port": "Port", + "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { @@ -108,7 +110,9 @@ "tunnel": { "data": { "host": "Host", + "local_ip": "Local IP (leave empty if unsure)", "port": "Port", + "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index e9417cdac37..c4410d490e4 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Juba seadistatud. Lubatud on ainult \u00fcks sidumine." }, "error": { - "cannot_connect": "\u00dchendamine nurjus" + "cannot_connect": "\u00dchendamine nurjus", + "file_not_found": "M\u00e4\u00e4ratud faili \".knxkeys\" ei leitud asukohas config/.storage/knx/", + "invalid_individual_address": "V\u00e4\u00e4rtus ei \u00fchti KNX-i individuaalse aadressi mustriga.\n 'area.line.device'", + "invalid_ip_address": "Kehtetu IPv4 aadress.", + "invalid_signature": "Parool faili `.knxkeys` dekr\u00fcpteerimiseks on vale." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "\u00dchenduse individuaalne aadress", - "local_ip": "Home Assistanti kohalik IP (automaatseks tuvastuseks j\u00e4ta t\u00fchjaks)", + "local_ip": "Home Assistanti kohalik IP aadress", "port": "Port", "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" }, + "data_description": { + "host": "KNX/IP tunneldusseadme IP-aadress.", + "local_ip": "Automaatse avastamise kasutamiseks j\u00e4ta t\u00fchjaks.", + "port": "KNX/IP-tunneldusseadme port." + }, "description": "Sisesta tunneldamisseadme \u00fchenduse teave." }, "routing": { "data": { - "individual_address": "Marsruutimis\u00fchenduse individuaalne aadress", - "local_ip": "Home Assistanti kohalik IP (automaatseks tuvastuseks j\u00e4ta t\u00fchjaks)", - "multicast_group": "Marsruutimiseks kasutatav multisaater\u00fchm", - "multicast_port": "Marsruutimiseks kasutatav multisaateport" + "individual_address": "Individuaalne aadress", + "local_ip": "Home Assistanti kohalik IP aadress", + "multicast_group": "Multicast grupp", + "multicast_port": "Mulicasti port" + }, + "data_description": { + "individual_address": "Home Assistantis kasutatav KNX-aadress, nt \"0.0.4\".", + "local_ip": "Automaatse avastamise kasutamiseks j\u00e4ta t\u00fchjaks." }, "description": "Konfigureeri marsruutimissuvandid." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.nxkeys` faili t\u00e4ielik nimi (koos laiendiga)", + "knxkeys_password": "Parool `.knxkeys` faili dekr\u00fcpteerimiseks" + }, + "data_description": { + "knxkeys_filename": "Eeldatakse, et fail asub konfiguratsioonikataloogis kaustas \".storage/knx/\".\nHome Assistant OS-is oleks see `/config/.storage/knx/`\n N\u00e4ide: \"minu_projekt.knxkeys\".", + "knxkeys_password": "See m\u00e4\u00e4rati faili eksportimisel ETSist." + }, + "description": "Sisesta oma `.knxkeys` faili teave." + }, + "secure_manual": { + "data": { + "device_authentication": "Seadme autentimise parool", + "user_id": "Kasutaja ID", + "user_password": "Kasutaja salas\u00f5na" + }, + "data_description": { + "device_authentication": "See m\u00e4\u00e4ratakse ETSi liidese IP-paneelil.", + "user_id": "See on sageli tunneli number +1. Nii et tunnel 2 oleks kasutaja ID-ga 3.", + "user_password": "Konkreetse tunneli\u00fchenduse parool, mis on m\u00e4\u00e4ratud ETS-i tunneli paneelil \u201eAtribuudid\u201d." + }, + "description": "Sisesta IP Secure teave." + }, + "secure_tunneling": { + "description": "Vali kuidas soovid KNX/IP Secure'i seadistada.", + "menu_options": { + "secure_knxkeys": "Kasuta knxkeys fail, mis sisaldab IP Secure teavet.", + "secure_manual": "IP Secure v\u00f5tmete k\u00e4sitsi seadistamine" + } + }, "tunnel": { "data": { "gateway": "KNX tunneli \u00fchendus" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX \u00fchenduse t\u00fc\u00fcp", "individual_address": "Vaikimisi individuaalne aadress", - "local_ip": "Home Assistanti kohalik IP (sisesta 0.0.0.0 automaatseks tuvastuseks)", - "multicast_group": "Marsruutimiseks ja avastamiseks kasutatav multisaategrupp", - "multicast_port": "Marsruutimiseks ja avastamiseks kasutatav multisaateport", - "rate_limit": "Maksimaalne v\u00e4ljaminevate teavituste arv sekundis", - "state_updater": "Luba globaalselt seisundi lugemine KNX-siinilt" + "local_ip": "Home Assistanti kohalik IP aadress", + "multicast_group": "Multicast grupp", + "multicast_port": "Mulicasti port", + "rate_limit": "Teavituste m\u00e4\u00e4r", + "state_updater": "Oleku uuendaja" + }, + "data_description": { + "individual_address": "Home Assistantis kasutatav KNX-aadress, nt \"0.0.4\".", + "local_ip": "Automaatse tuvastamise jaoks kasuta `0.0.0.0.0`.", + "multicast_group": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikimisi: \"224.0.23.12\"", + "multicast_port": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikev\u00e4\u00e4rtus: \"3671\"", + "rate_limit": "Maksimaalne v\u00e4ljaminevate telegrammide arv sekundis.\nSoovitatav: 20 kuni 40", + "state_updater": "KNX siini lugemisolekute globaalne lubamine v\u00f5i keelamine. Kui see on keelatud, ei too Home Assistant aktiivselt olekuid KNX siinilt, olemi s\u00fcnkroonimisoleku valikudi ei m\u00f5juta." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" + }, + "data_description": { + "host": "KNX/IP tunneldusseadme IP-aadress.", + "port": "KNX/IP-tunneldusseadme port." } } } diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index 31221e900cd..c75fd76debb 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -5,29 +5,73 @@ "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "error": { - "cannot_connect": "\u00c9chec de connexion" + "cannot_connect": "\u00c9chec de connexion", + "file_not_found": "Le fichier `.knxkeys` sp\u00e9cifi\u00e9 n'a pas \u00e9t\u00e9 trouv\u00e9 dans config/.storage/knx/", + "invalid_individual_address": "La valeur de l'adresse individuelle KNX ne correspond pas au mod\u00e8le.\n'area.line.device'", + "invalid_ip_address": "Adresse IPv4 non valide.", + "invalid_signature": "Le mot de passe pour d\u00e9chiffrer le fichier `.knxkeys` est erron\u00e9." }, "step": { "manual_tunnel": { "data": { "host": "H\u00f4te", "individual_address": "Adresse individuelle pour la connexion", - "local_ip": "IP locale (laisser vide en cas de doute)", + "local_ip": "IP locale de Home Assistant", "port": "Port", "route_back": "Retour/Mode NAT", "tunneling_type": "Type de tunnel KNX" }, + "data_description": { + "host": "Adresse IP de l'appareil de tunnel KNX/IP.", + "local_ip": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique.", + "port": "Port de l'appareil de tunnel KNX/IP." + }, "description": "Veuillez saisir les informations de connexion de votre appareil de cr\u00e9ation de tunnel." }, "routing": { "data": { - "individual_address": "Adresse individuelle pour la connexion de routage", - "local_ip": "IP locale (laisser vide en cas de doute)", - "multicast_group": "Le groupe multicast utilis\u00e9 pour le routage", - "multicast_port": "Le port multicast utilis\u00e9 pour le routage" + "individual_address": "Adresse individuelle", + "local_ip": "IP locale de Home Assistant", + "multicast_group": "Groupe multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Adresse KNX que Home Assistant doit utiliser, par exemple `0.0.4`.", + "local_ip": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique." }, "description": "Veuillez configurer les options de routage." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Le nom de votre fichier `.knxkeys` (extension incluse)", + "knxkeys_password": "Le mot de passe pour d\u00e9chiffrer le fichier `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Le fichier devrait se trouver dans votre r\u00e9pertoire de configuration dans `.storage/knx/`.\nSous Home Assistant OS, il s'agirait de `/config/.storage/knx/`\nPar exemple\u00a0: `my_project.knxkeys`", + "knxkeys_password": "D\u00e9fini lors de l'exportation du fichier depuis ETS." + }, + "description": "Veuillez saisir les informations relatives \u00e0 votre fichier `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "Mot de passe d'authentification de l'appareil", + "user_id": "ID de l'utilisateur", + "user_password": "Mot de passe de l'utilisateur" + }, + "data_description": { + "device_authentication": "D\u00e9fini dans le panneau \u00ab\u00a0IP\u00a0\u00bb de l'interface dans ETS.", + "user_id": "G\u00e9n\u00e9ralement le num\u00e9ro du tunnel +\u00a01. Par exemple, \u00ab\u00a0Tunnel 2\u00a0\u00bb aurait l'ID utilisateur \u00ab\u00a03\u00a0\u00bb.", + "user_password": "Mot de passe pour la connexion de tunnel sp\u00e9cifique, d\u00e9fini dans le panneau \u00ab\u00a0Propri\u00e9t\u00e9s\u00a0\u00bb du tunnel dans ETS." + }, + "description": "Veuillez saisir vos informations de s\u00e9curit\u00e9 IP." + }, + "secure_tunneling": { + "description": "S\u00e9lectionnez la mani\u00e8re dont vous souhaitez configurer la s\u00e9curit\u00e9 IP de KNX.", + "menu_options": { + "secure_knxkeys": "Utiliser un fichier `.knxkeys` contenant les cl\u00e9s de s\u00e9curit\u00e9 IP", + "secure_manual": "Configurer manuellement les cl\u00e9s de s\u00e9curit\u00e9 IP" + } + }, "tunnel": { "data": { "gateway": "Connexion tunnel KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Type de connexion KNX", "individual_address": "Adresse individuelle par d\u00e9faut", - "local_ip": "IP locale de Home Assistant (utilisez 0.0.0.0 pour la d\u00e9tection automatique)", - "multicast_group": "Groupe de multidiffusion utilis\u00e9 pour le routage et la d\u00e9couverte", - "multicast_port": "Port de multidiffusion utilis\u00e9 pour le routage et la d\u00e9couverte", - "rate_limit": "Nombre maximal de t\u00e9l\u00e9grammes sortants par seconde", - "state_updater": "Activer globalement la lecture des \u00e9tats depuis le bus KNX" + "local_ip": "IP locale de Home Assistant", + "multicast_group": "Groupe multicast", + "multicast_port": "Port multicast", + "rate_limit": "Limite d'envoi", + "state_updater": "Mises \u00e0 jour d'\u00e9tat" + }, + "data_description": { + "individual_address": "Adresse KNX que Home Assistant doit utiliser, par exemple `0.0.4`.", + "local_ip": "Utilisez `0.0.0.0` pour la d\u00e9couverte automatique.", + "multicast_group": "Utilis\u00e9 pour le routage et la d\u00e9couverte. Valeur par d\u00e9faut\u00a0: `224.0.23.12`", + "multicast_port": "Utilis\u00e9 pour le routage et la d\u00e9couverte. Valeur par d\u00e9faut\u00a0: `3671`", + "rate_limit": "Nombre maximal de t\u00e9l\u00e9grammes sortants par seconde.\nValeur recommand\u00e9e\u00a0: entre 20 et 40", + "state_updater": "Active ou d\u00e9sactive globalement la lecture des \u00e9tats depuis le bus KNX. Lorsqu'elle est d\u00e9sactiv\u00e9e, Home Assistant ne r\u00e9cup\u00e8re pas activement les \u00e9tats depuis le bus KNX et les options d'entit\u00e9 `sync_state` n'ont aucun effet." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Retour/Mode NAT", "tunneling_type": "Type de tunnel KNX" + }, + "data_description": { + "host": "Adresse IP de l'appareil de tunnel KNX/IP.", + "port": "Port de l'appareil de tunnel KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index 13bd4650378..02b8b0a0465 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -5,29 +5,73 @@ "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s" + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "file_not_found": "A megadott '.knxkeys' f\u00e1jl nem tal\u00e1lhat\u00f3 a config/.storage/knx/ el\u00e9r\u00e9si \u00fatvonalon.", + "invalid_individual_address": "Az \u00e9rt\u00e9k nem felel meg a KNX egyedi c\u00edm mint\u00e1j\u00e1nak.\n'area.line.device'", + "invalid_ip_address": "\u00c9rv\u00e9nytelen IPv4-c\u00edm.", + "invalid_signature": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez haszn\u00e1lt jelsz\u00f3 helytelen." }, "step": { "manual_tunnel": { "data": { "host": "C\u00edm", "individual_address": "A kapcsolat egy\u00e9ni c\u00edme", - "local_ip": "Helyi IP c\u00edm (hagyja \u00fcresen, ha nem biztos benne)", + "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", "port": "Port", "route_back": "\u00datvonal (route) vissza / NAT m\u00f3d", "tunneling_type": "KNX alag\u00fat t\u00edpusa" }, + "data_description": { + "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", + "local_ip": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen.", + "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma." + }, "description": "Adja meg az alag\u00fatkezel\u0151 (tunneling) eszk\u00f6z csatlakoz\u00e1si adatait." }, "routing": { "data": { - "individual_address": "Az \u00fatv\u00e1laszt\u00e1si (routing) kapcsolat egy\u00e9ni c\u00edme", - "local_ip": "Helyi IP c\u00edm (hagyja \u00fcresen, ha nem biztos benne)", - "multicast_group": "Az \u00fatv\u00e1laszt\u00e1shoz haszn\u00e1lt multicast csoport", - "multicast_port": "Az \u00fatv\u00e1laszt\u00e1shoz haszn\u00e1lt multicast portsz\u00e1m" + "individual_address": "Egy\u00e9ni c\u00edm", + "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", + "multicast_group": "Multicast csoport", + "multicast_port": "Multicast portsz\u00e1m" + }, + "data_description": { + "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", + "local_ip": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen." }, "description": "K\u00e9rem, konfigur\u00e1lja az \u00fatv\u00e1laszt\u00e1si (routing) be\u00e1ll\u00edt\u00e1sokat." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "A '.knxkeys' f\u00e1jl teljes neve (kiterjeszt\u00e9ssel)", + "knxkeys_password": "A '.knxkeys' f\u00e1jl visszafejt\u00e9s\u00e9hez sz\u00fcks\u00e9ges jelsz\u00f3" + }, + "data_description": { + "knxkeys_filename": "A f\u00e1jl a `.storage/knx/` konfigur\u00e1ci\u00f3s k\u00f6nyvt\u00e1r\u00e1ban helyezend\u0151.\nHome Assistant oper\u00e1ci\u00f3s rendszer eset\u00e9n ez a k\u00f6vetkez\u0151 lenne: `/config/.storage/knx/`\nP\u00e9lda: \"my_project.knxkeys\".", + "knxkeys_password": "Ez a be\u00e1ll\u00edt\u00e1s a f\u00e1jl ETS-b\u0151l t\u00f6rt\u00e9n\u0151 export\u00e1l\u00e1sakor t\u00f6rt\u00e9nt." + }, + "description": "K\u00e9rj\u00fck, adja meg a '.knxkeys' f\u00e1jl adatait." + }, + "secure_manual": { + "data": { + "device_authentication": "Eszk\u00f6z hiteles\u00edt\u00e9si jelsz\u00f3", + "user_id": "Felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3", + "user_password": "Felhaszn\u00e1l\u00f3i jelsz\u00f3" + }, + "data_description": { + "device_authentication": "Ezt az ETS-ben az interf\u00e9sz \"IP\" panelj\u00e9n kell be\u00e1ll\u00edtani.", + "user_id": "Ez gyakran a tunnel sz\u00e1ma +1. Teh\u00e1t a \"Tunnel 2\" felhaszn\u00e1l\u00f3i azonos\u00edt\u00f3ja \"3\".", + "user_password": "Jelsz\u00f3 az adott tunnelhez, amely a tunnel \u201eProperties\u201d panelj\u00e9n van be\u00e1ll\u00edtva az ETS-ben." + }, + "description": "K\u00e9rj\u00fck, adja meg az IP secure adatokat." + }, + "secure_tunneling": { + "description": "V\u00e1lassza ki, hogyan szeretn\u00e9 konfigur\u00e1lni az KNX/IP secure-t.", + "menu_options": { + "secure_knxkeys": "IP secure kulcsokat tartalmaz\u00f3 '.knxkeys' f\u00e1jl haszn\u00e1lata", + "secure_manual": "IP secure kulcsok manu\u00e1lis be\u00e1ll\u00edt\u00e1sa" + } + }, "tunnel": { "data": { "gateway": "KNX alag\u00fat (tunnel) kapcsolat" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX csatlakoz\u00e1s t\u00edpusa", "individual_address": "Alap\u00e9rtelmezett egy\u00e9ni c\u00edm", - "local_ip": "Helyi IP c\u00edm (hagyja \u00fcresen, ha nem biztos benne)", - "multicast_group": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1lt multicast csoport", - "multicast_port": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1lt multicast portsz\u00e1m\n", - "rate_limit": "Maxim\u00e1lis kimen\u0151 \u00fczenet darabsz\u00e1m m\u00e1sodpercenk\u00e9nt", - "state_updater": "Glob\u00e1lisan enged\u00e9lyezi az \u00e1llapotok olvas\u00e1s\u00e1t a KNX buszr\u00f3l." + "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", + "multicast_group": "Multicast csoport", + "multicast_port": "Multicast portsz\u00e1m", + "rate_limit": "Lek\u00e9r\u00e9si korl\u00e1toz\u00e1s", + "state_updater": "\u00c1llapot friss\u00edt\u0151" + }, + "data_description": { + "individual_address": "A Home Assistant \u00e1ltal haszn\u00e1land\u00f3 KNX-c\u00edm, pl. \"0.0.4\".", + "local_ip": "Haszn\u00e1lja a `0.0.0.0` c\u00edmet az automatikus felder\u00edt\u00e9shez.", + "multicast_group": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `224.0.23.12`.", + "multicast_port": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `3671`", + "rate_limit": "Maxim\u00e1lis kimen\u0151 \u00fczenet m\u00e1sodpercenk\u00e9nt.\nAj\u00e1nlott: 20 \u00e9s 40 k\u00f6z\u00f6tt", + "state_updater": "Glob\u00e1lisan enged\u00e9lyezze vagy tiltsa le az olvas\u00e1si \u00e1llapotokat a KNX-buszr\u00f3l. Ha le van tiltva, a Home Assistant nem fogja akt\u00edvan lek\u00e9rni az \u00e1llapotokat a KNX-buszr\u00f3l, a \"sync_state\" entit\u00e1sopci\u00f3knak nincs hat\u00e1sa." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "\u00datvonal (route) vissza / NAT m\u00f3d", "tunneling_type": "KNX alag\u00fat t\u00edpusa" + }, + "data_description": { + "host": "A KNX/IP tunnel eszk\u00f6z IP-c\u00edme.", + "port": "A KNX/IP tunnel eszk\u00f6z portsz\u00e1ma." } } } diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 1cdb614a3cc..92d812c1b1b 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "error": { - "cannot_connect": "Gagal terhubung" + "cannot_connect": "Gagal terhubung", + "file_not_found": "File `.knxkeys` yang ditentukan tidak ditemukan di jalur config/.storage/knx/", + "invalid_individual_address": "Nilai tidak cocok dengan pola untuk alamat individual KNX.\n'area.line.device'", + "invalid_ip_address": "Alamat IPv4 tidak valid", + "invalid_signature": "Kata sandi untuk mendekripsi file `.knxkeys` salah." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "Alamat individu untuk koneksi", - "local_ip": "IP lokal Home Assistant (kosongkan jika tidak yakin)", + "local_ip": "IP lokal Home Assistant", "port": "Port", "route_back": "Dirutekan Kembali/Mode NAT", "tunneling_type": "Jenis Tunnel KNX" }, + "data_description": { + "host": "Alamat IP perangkat tunneling KNX/IP.", + "local_ip": "Kosongkan untuk menggunakan penemuan otomatis.", + "port": "Port perangkat tunneling KNX/IP." + }, "description": "Masukkan informasi koneksi untuk perangkat tunneling Anda." }, "routing": { "data": { - "individual_address": "Alamat individu untuk koneksi routing", - "local_ip": "IP lokal Home Assistant (kosongkan jika tidak yakin)", - "multicast_group": "Grup multicast yang digunakan untuk routing", - "multicast_port": "Port multicast yang digunakan untuk routing" + "individual_address": "Alamat individual", + "local_ip": "IP lokal Home Assistant", + "multicast_group": "Grup multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", + "local_ip": "Kosongkan untuk menggunakan penemuan otomatis." }, "description": "Konfigurasikan opsi routing." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nama file '.knxkeys' Anda (termasuk ekstensi)", + "knxkeys_password": "Kata sandi untuk mendekripsi file `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "File diharapkan dapat ditemukan di direktori konfigurasi Anda di `.storage/knx/`.\nDi Home Assistant OS ini akan menjadi `/config/.storage/knx/`\nContoh: `proyek_saya.knxkeys`", + "knxkeys_password": "Ini disetel saat mengekspor file dari ETS." + }, + "description": "Masukkan informasi untuk file `.knxkeys` Anda." + }, + "secure_manual": { + "data": { + "device_authentication": "Kata sandi autentikasi perangkat", + "user_id": "ID pengguna", + "user_password": "Kata sandi pengguna" + }, + "data_description": { + "device_authentication": "Ini diatur dalam panel 'IP' dalam antarmuka di ETS.", + "user_id": "Ini sering kali merupakan tunnel nomor +1. Jadi 'Tunnel 2' akan memiliki User-ID '3'.", + "user_password": "Kata sandi untuk koneksi tunnel tertentu yang diatur di panel 'Properties' tunnel di ETS." + }, + "description": "Masukkan informasi IP aman Anda." + }, + "secure_tunneling": { + "description": "Pilih cara Anda ingin mengonfigurasi KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Gunakan file `.knxkeys` yang berisi kunci aman IP", + "secure_manual": "Konfigurasikan kunci aman IP secara manual" + } + }, "tunnel": { "data": { "gateway": "Koneksi Tunnel KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Jenis Koneksi KNX", "individual_address": "Alamat individu default", - "local_ip": "IP lokal Home Assistant (gunakan 0.0.0.0 untuk deteksi otomatis)", - "multicast_group": "Grup multicast yang digunakan untuk routing dan penemuan", - "multicast_port": "Port multicast yang digunakan untuk routing dan penemuan", - "rate_limit": "Jumlah maksimal telegram keluar per detik", - "state_updater": "Aktifkan status membaca secara global dari KNX Bus" + "local_ip": "IP lokal Home Assistant", + "multicast_group": "Grup multicast", + "multicast_port": "Port multicast", + "rate_limit": "Batas data", + "state_updater": "Pembaruan status" + }, + "data_description": { + "individual_address": "Alamat KNX yang akan digunakan oleh Home Assistant, misalnya `0.0.4`", + "local_ip": "Gunakan `0.0.0.0` untuk penemuan otomatis.", + "multicast_group": "Digunakan untuk perutean dan penemuan. Bawaan: `224.0.23.12`", + "multicast_port": "Digunakan untuk perutean dan penemuan. Bawaan: `3671`", + "rate_limit": "Telegram keluar maksimum per detik.\nDirekomendasikan: 20 hingga 40", + "state_updater": "Secara global mengaktifkan atau menonaktifkan status pembacaan dari KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status dari KNX Bus, opsi entitas 'sync_state' tidak akan berpengaruh." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Dirutekan Kembali/Mode NAT", "tunneling_type": "Jenis Tunnel KNX" + }, + "data_description": { + "host": "Alamat IP perangkat tunneling KNX/IP.", + "port": "Port perangkat tunneling KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index ad4e9f34610..c05bdba3944 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { - "cannot_connect": "Impossibile connettersi" + "cannot_connect": "Impossibile connettersi", + "file_not_found": "Il file `.knxkeys` specificato non \u00e8 stato trovato nel percorso config/.storage/knx/", + "invalid_individual_address": "Il valore non corrisponde al modello per l'indirizzo individuale KNX. 'area.line.device'", + "invalid_ip_address": "Indirizzo IPv4 non valido.", + "invalid_signature": "La password per decifrare il file `.knxkeys` \u00e8 errata." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "Indirizzo individuale per la connessione", - "local_ip": "IP locale di Home Assistant (lascia vuoto per il rilevamento automatico)", + "local_ip": "IP locale di Home Assistant", "port": "Porta", "route_back": "Torna indietro / Modalit\u00e0 NAT", "tunneling_type": "Tipo tunnel KNX" }, + "data_description": { + "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", + "local_ip": "Lascia vuoto per usare il rilevamento automatico.", + "port": "Porta del dispositivo di tunneling KNX/IP." + }, "description": "Inserisci le informazioni di connessione del tuo dispositivo di tunneling." }, "routing": { "data": { - "individual_address": "Indirizzo individuale per la connessione di routing", - "local_ip": "IP locale di Home Assistant (lascia vuoto per il rilevamento automatico)", - "multicast_group": "Il gruppo multicast utilizzato per il routing", - "multicast_port": "La porta multicast usata per il routing" + "individual_address": "Indirizzo individuale", + "local_ip": "IP locale di Home Assistant", + "multicast_group": "Gruppo multicast", + "multicast_port": "Porta multicast" + }, + "data_description": { + "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", + "local_ip": "Lasciare vuoto per usare il rilevamento automatico." }, "description": "Configura le opzioni di instradamento." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Il nome del file `.knxkeys` (inclusa l'estensione)", + "knxkeys_password": "La password per decifrare il file `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Il file dovrebbe essere trovato nella tua cartella di configurazione in `.storage/knx/`.\n Nel sistema operativo Home Assistant questo sarebbe `/config/.storage/knx/`\n Esempio: `mio_progetto.knxkeys`", + "knxkeys_password": "Questo \u00e8 stato impostato durante l'esportazione del file da ETS." + }, + "description": "Inserisci le informazioni per il tuo file `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "Password di autenticazione del dispositivo", + "user_id": "ID utente", + "user_password": "Password utente" + }, + "data_description": { + "device_authentication": "Questo \u00e8 impostato nel pannello 'IP' dell'interfaccia in ETS.", + "user_id": "Questo \u00e8 spesso il tunnel numero +1. Quindi \"Tunnel 2\" avrebbe l'ID utente \"3\".", + "user_password": "Password per la connessione specifica del tunnel impostata nel pannello 'Propriet\u00e0' del tunnel in ETS." + }, + "description": "Inserisci le tue informazioni di sicurezza IP." + }, + "secure_tunneling": { + "description": "Seleziona come vuoi configurare KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Utilizza un file `.knxkeys` contenente chiavi di sicurezza IP", + "secure_manual": "Configura manualmente le chiavi di sicurezza IP" + } + }, "tunnel": { "data": { "gateway": "Connessione tunnel KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Tipo di connessione KNX", "individual_address": "Indirizzo individuale predefinito", - "local_ip": "IP locale di Home Assistant (usare 0.0.0.0 per il rilevamento automatico)", - "multicast_group": "Gruppo multicast utilizzato per il routing e il rilevamento", - "multicast_port": "Porta multicast utilizzata per il routing e il rilevamento", - "rate_limit": "Numero massimo di telegrammi in uscita al secondo", - "state_updater": "Abilita globalmente la lettura degli stati dal bus KNX" + "local_ip": "IP locale di Home Assistant", + "multicast_group": "Gruppo multicast", + "multicast_port": "Porta multicast", + "rate_limit": "Limite di tariffa", + "state_updater": "Aggiornatore di stato" + }, + "data_description": { + "individual_address": "Indirizzo KNX che deve essere utilizzato da Home Assistant, ad es. `0.0.4`", + "local_ip": "Usa `0.0.0.0` per il rilevamento automatico.", + "multicast_group": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `224.0.23.12`", + "multicast_port": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `3671`", + "rate_limit": "Numero massimo di telegrammi in uscita al secondo.\n Consigliato: da 20 a 40", + "state_updater": "Abilita o disabilita globalmente gli stati di lettura dal bus KNX. Se disabilitato, Home Assistant non recuperer\u00e0 attivamente gli stati dal bus KNX, le opzioni dell'entit\u00e0 `sync_state` non avranno alcun effetto." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Porta", "route_back": "Torna indietro / Modalit\u00e0 NAT", "tunneling_type": "Tipo tunnel KNX" + }, + "data_description": { + "host": "Indirizzo IP del dispositivo di tunneling KNX/IP.", + "port": "Porta del dispositivo di tunneling KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/ja.json b/homeassistant/components/knx/translations/ja.json index a4744a41a2c..52eed8780b8 100644 --- a/homeassistant/components/knx/translations/ja.json +++ b/homeassistant/components/knx/translations/ja.json @@ -5,7 +5,11 @@ "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "file_not_found": "\u6307\u5b9a\u3055\u308c\u305f'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u304c\u3001\u30d1\u30b9: config/.storage/knx/ \u306b\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", + "invalid_individual_address": "\u5024\u304cKNX\u500b\u5225\u30a2\u30c9\u30ec\u30b9\u306e\u30d1\u30bf\u30fc\u30f3\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002\n'area.line.device'", + "invalid_ip_address": "IPv4\u30a2\u30c9\u30ec\u30b9\u304c\u7121\u52b9\u3067\u3059\u3002", + "invalid_signature": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u3092\u5fa9\u53f7\u5316\u3059\u308b\u305f\u3081\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3059\u3002" }, "step": { "manual_tunnel": { @@ -17,6 +21,11 @@ "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" }, + "data_description": { + "host": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306eIP\u30a2\u30c9\u30ec\u30b9\u3002", + "local_ip": "\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002", + "port": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306e\u30dd\u30fc\u30c8\u3002" + }, "description": "\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "routing": { @@ -26,8 +35,43 @@ "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30b0\u30eb\u30fc\u30d7", "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30dd\u30fc\u30c8" }, + "data_description": { + "individual_address": "Home Assistant\u304c\u4f7f\u7528\u3059\u308bKNX\u30a2\u30c9\u30ec\u30b9\u3001\u4f8b. `0.0.4`", + "local_ip": "\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" + }, "description": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u306e\u30d5\u30a1\u30a4\u30eb\u540d(\u62e1\u5f35\u5b50\u3092\u542b\u3080)", + "knxkeys_password": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u3092\u5fa9\u53f7\u5316\u3059\u308b\u305f\u3081\u306e\u30d1\u30b9\u30ef\u30fc\u30c9" + }, + "data_description": { + "knxkeys_filename": "\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u3001config\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e `.storage/knx/` \u306b\u3042\u308b\u306f\u305a\u3067\u3059\u3002\nHome Assistant OS\u306e\u5834\u5408\u3001\u3053\u308c\u306f\u3001`/config/.storage/knx/` \u306b\u306a\u308a\u307e\u3059\n\u4f8b: `my_project.knxkeys`", + "knxkeys_password": "\u3053\u308c\u306f\u3001ETS\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u30a8\u30af\u30b9\u30dd\u30fc\u30c8\u3059\u308b\u3068\u304d\u306b\u8a2d\u5b9a\u3055\u308c\u307e\u3057\u305f\u3002" + }, + "description": "'.knxkeys'\u30d5\u30a1\u30a4\u30eb\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, + "secure_manual": { + "data": { + "device_authentication": "\u30c7\u30d0\u30a4\u30b9\u8a8d\u8a3c\u30d1\u30b9\u30ef\u30fc\u30c9", + "user_id": "\u30e6\u30fc\u30b6\u30fcID", + "user_password": "\u30e6\u30fc\u30b6\u30fc\u30d1\u30b9\u30ef\u30fc\u30c9" + }, + "data_description": { + "device_authentication": "\u3053\u308c\u306f\u3001ETS\u306e\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u306e 'IP' \u30d1\u30cd\u30eb\u3067\u8a2d\u5b9a\u3057\u307e\u3059\u3002", + "user_id": "\u591a\u304f\u306e\u5834\u5408\u3001\u3053\u308c\u306f\u30c8\u30f3\u30cd\u30eb\u756a\u53f7+1\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001 '\u30c8\u30f3\u30cd\u30eb2' \u306e\u30e6\u30fc\u30b6\u30fcID\u306f\u3001'3 '\u306b\u306a\u308a\u307e\u3059\u3002", + "user_password": "ETS\u306e\u30c8\u30f3\u30cd\u30eb\u306e\u3001'\u30d7\u30ed\u30d1\u30c6\u30a3' \u30d1\u30cd\u30eb\u3067\u8a2d\u5b9a\u3055\u308c\u305f\u7279\u5b9a\u306e\u30c8\u30f3\u30cd\u30eb\u63a5\u7d9a\u7528\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3002" + }, + "description": "IP\u30bb\u30ad\u30e5\u30a2\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, + "secure_tunneling": { + "description": "IP\u30bb\u30ad\u30e5\u30a2\u306e\u8a2d\u5b9a\u65b9\u6cd5\u3092\u9078\u629e\u3057\u307e\u3059\u3002", + "menu_options": { + "secure_knxkeys": "IP\u30bb\u30ad\u30e5\u30a2\u60c5\u5831\u3092\u542b\u3080knxkeys\u30d5\u30a1\u30a4\u30eb\u3092\u8a2d\u5b9a\u3057\u307e\u3059", + "secure_manual": "IP\u30bb\u30ad\u30e5\u30a2\u3092\u624b\u52d5\u3067\u8a2d\u5b9a\u3059\u308b" + } + }, "tunnel": { "data": { "gateway": "KNX\u30c8\u30f3\u30cd\u30eb\u63a5\u7d9a" @@ -53,6 +97,14 @@ "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa(discovery)\u306b\u4f7f\u7528\u3055\u308c\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30dd\u30fc\u30c8", "rate_limit": "1 \u79d2\u3042\u305f\u308a\u306e\u6700\u5927\u9001\u4fe1\u96fb\u5831(telegrams )\u6570", "state_updater": "KNX\u30d0\u30b9\u304b\u3089\u306e\u8aad\u307f\u53d6\u308a\u72b6\u614b\u3092\u30b0\u30ed\u30fc\u30d0\u30eb\u306b\u6709\u52b9\u306b\u3059\u308b" + }, + "data_description": { + "individual_address": "Home Assistant\u304c\u4f7f\u7528\u3059\u308bKNX\u30a2\u30c9\u30ec\u30b9\u3001\u4f8b. `0.0.4`", + "local_ip": "\u81ea\u52d5\u691c\u51fa\u306b\u306f\u3001`0.0.0.0` \u3092\u4f7f\u7528\u3057\u307e\u3059\u3002", + "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8t: `224.0.23.12`", + "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa\u306b\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8: `3671`", + "rate_limit": "1\u79d2\u3042\u305f\u308a\u306e\u6700\u5927\u9001\u4fe1\u30c6\u30ec\u30b0\u30e9\u30e0\u3002\n\u63a8\u5968: 20\uff5e40", + "state_updater": "KNX Bus\u304b\u3089\u306e\u72b6\u614b\u306e\u8aad\u307f\u53d6\u308a\u3092\u30b0\u30ed\u30fc\u30d0\u30eb\u306b\u6709\u52b9\u307e\u305f\u306f\u7121\u52b9\u306b\u3057\u307e\u3059\u3002\u7121\u52b9\u306b\u3059\u308b\u3068\u3001Home Assistant\u306f\u3001KNX Bus\u304b\u3089\u30a2\u30af\u30c6\u30a3\u30d6\u306b\u72b6\u614b\u3092\u53d6\u5f97\u3057\u306a\u304f\u306a\u308b\u306e\u3067\u3001`sync_state`\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u610f\u5473\u3092\u6301\u305f\u306a\u304f\u306a\u308a\u307e\u3059\u3002" } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "\u30dd\u30fc\u30c8", "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" + }, + "data_description": { + "host": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306eIP\u30a2\u30c9\u30ec\u30b9\u3002", + "port": "KNX/IP\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30c7\u30d0\u30a4\u30b9\u306e\u30dd\u30fc\u30c8\u3002" } } } diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index 9b68bd02d2f..f40b9d7729f 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." }, "error": { - "cannot_connect": "Kan geen verbinding maken" + "cannot_connect": "Kan geen verbinding maken", + "file_not_found": "Het opgegeven `.knxkeys`-bestand is niet gevonden in het pad config/.storage/knx/", + "invalid_individual_address": "Waarde komt niet overeen met patroon voor KNX individueel adres.\n\"area.line.device", + "invalid_ip_address": "Ongeldig IPv4-adres.", + "invalid_signature": "Het wachtwoord om het `.knxkeys`-bestand te decoderen is verkeerd." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "Individueel adres voor de verbinding", - "local_ip": "Lokaal IP van Home Assistant (leeg laten voor automatische detectie)", + "local_ip": "Lokale IP van Home Assistant", "port": "Poort", "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, + "data_description": { + "host": "IP adres van het KNX/IP tunneling apparaat.", + "local_ip": "Leeg laten om auto-discovery te gebruiken.", + "port": "Poort van het KNX/IP-tunnelapparaat." + }, "description": "Voer de verbindingsinformatie van uw tunneling-apparaat in." }, "routing": { "data": { - "individual_address": "Individueel adres voor de routing verbinding", - "local_ip": "Lokaal IP van Home Assistant (leeg laten voor automatische detectie)", - "multicast_group": "De multicast groep gebruikt voor de routing", - "multicast_port": "De multicast-poort gebruikt voor de routing" + "individual_address": "Individueel adres", + "local_ip": "Lokale IP van Home Assistant", + "multicast_group": "Multicast-groep", + "multicast_port": "Multicast-poort" + }, + "data_description": { + "individual_address": "KNX-adres te gebruiken door Home Assistant, bijv. `0.0.4`", + "local_ip": "Leeg laten om auto-discovery te gebruiken." }, "description": "Configureer de routing opties" }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "De bestandsnaam van uw `.knxkeys` bestand (inclusief extensie)", + "knxkeys_password": "Het wachtwoord om het bestand `.knxkeys` te ontcijferen" + }, + "data_description": { + "knxkeys_filename": "Het bestand zal naar verwachting worden gevonden in uw configuratiemap in '.storage/knx/'.\nIn Home Assistant OS zou dit '/config/.storage/knx/' zijn.\nVoorbeeld: 'my_project.knxkeys'", + "knxkeys_password": "Dit werd ingesteld bij het exporteren van het bestand van ETS." + }, + "description": "Voer de informatie voor uw `.knxkeys` bestand in." + }, + "secure_manual": { + "data": { + "device_authentication": "Wachtwoord voor apparaatverificatie", + "user_id": "User ID", + "user_password": "Gebruikerswachtwoord" + }, + "data_description": { + "device_authentication": "Dit wordt ingesteld in het \"IP\"-paneel van de interface in ETS.", + "user_id": "Dit is vaak tunnelnummer +1. Dus 'Tunnel 2' zou User-ID '3' hebben.", + "user_password": "Wachtwoord voor de specifieke tunnelverbinding, ingesteld in het paneel \"Eigenschappen\" van de tunnel in ETS." + }, + "description": "Voer uw beveiligde IP-gegevens in." + }, + "secure_tunneling": { + "description": "Kies hoe u KNX/IP Secure wilt configureren.", + "menu_options": { + "secure_knxkeys": "Gebruik een `.knxkeys` bestand met IP beveiligde sleutels", + "secure_manual": "IP-beveiligingssleutels handmatig configureren" + } + }, "tunnel": { "data": { "gateway": "KNX Tunnel Connection" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX-verbindingstype", "individual_address": "Standaard individueel adres", - "local_ip": "Lokaal IP van Home Assistant (gebruik 0.0.0.0 voor automatische detectie)", - "multicast_group": "Multicast groep gebruikt voor routing en ontdekking", - "multicast_port": "Multicast poort gebruikt voor routing en ontdekking", - "rate_limit": "Maximaal aantal uitgaande telegrammen per seconde", - "state_updater": "Globaal vrijgeven van het lezen van de KNX bus" + "local_ip": "Lokale IP van Home Assistant", + "multicast_group": "Multicast-groep", + "multicast_port": "Multicast-poort", + "rate_limit": "Rate limit", + "state_updater": "Statusupdater" + }, + "data_description": { + "individual_address": "KNX-adres dat door Home Assistant moet worden gebruikt, bijv. `0.0.4`", + "local_ip": "Gebruik `0.0.0.0` voor auto-discovery.", + "multicast_group": "Gebruikt voor routing en discovery. Standaard: `224.0.23.12`.", + "multicast_port": "Gebruikt voor routing en discovery. Standaard: `3671`", + "rate_limit": "Maximaal aantal uitgaande telegrammen per seconde.\nAanbevolen: 20 tot 40", + "state_updater": "Globaal in- of uitschakelen van het lezen van de status van de KNX bus. Indien uitgeschakeld, zal Home Assistant niet actief de status van de KNX Bus ophalen, `sync_state` entiteitsopties zullen geen effect hebben." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Poort", "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" + }, + "data_description": { + "host": "IP adres van het KNX/IP tunneling apparaat.", + "port": "Poort van het KNX/IP-tunnelapparaat." } } } diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index 231945d5233..f5d03e3160c 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "error": { - "cannot_connect": "Tilkobling mislyktes" + "cannot_connect": "Tilkobling mislyktes", + "file_not_found": "Den angitte `.knxkeys`-filen ble ikke funnet i banen config/.storage/knx/", + "invalid_individual_address": "Verdien samsvarer ikke med m\u00f8nsteret for individuelle KNX-adresser.\n 'area.line.device'", + "invalid_ip_address": "Ugyldig IPv4-adresse.", + "invalid_signature": "Passordet for \u00e5 dekryptere `.knxkeys`-filen er feil." }, "step": { "manual_tunnel": { "data": { "host": "Vert", "individual_address": "Individuell adresse for tilkoblingen", - "local_ip": "Lokal IP for Home Assistant (la st\u00e5 tomt for automatisk gjenkjenning)", + "local_ip": "Lokal IP for hjemmeassistent", "port": "Port", "route_back": "Rute tilbake / NAT-modus", "tunneling_type": "KNX tunneltype" }, + "data_description": { + "host": "IP-adressen til KNX/IP-tunnelenheten.", + "local_ip": "La st\u00e5 tomt for \u00e5 bruke automatisk oppdagelse.", + "port": "Port p\u00e5 KNX/IP-tunnelenheten." + }, "description": "Vennligst skriv inn tilkoblingsinformasjonen til tunnelenheten din." }, "routing": { "data": { - "individual_address": "Individuell adresse for ruteforbindelsen", - "local_ip": "Lokal IP for Home Assistant (la st\u00e5 tomt for automatisk gjenkjenning)", - "multicast_group": "Multicast-gruppen som brukes til ruting", - "multicast_port": "Multicast-porten som brukes til ruting" + "individual_address": "Individuell adresse", + "local_ip": "Lokal IP for hjemmeassistent", + "multicast_group": "Multicast gruppe", + "multicast_port": "Multicast port" + }, + "data_description": { + "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", + "local_ip": "La st\u00e5 tomt for \u00e5 bruke automatisk oppdagelse." }, "description": "Vennligst konfigurer rutealternativene." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Filnavnet til `.knxkeys`-filen (inkludert utvidelse)", + "knxkeys_password": "Passordet for \u00e5 dekryptere `.knxkeys`-filen" + }, + "data_description": { + "knxkeys_filename": "Filen forventes \u00e5 bli funnet i konfigurasjonskatalogen din i `.storage/knx/`.\n I Home Assistant OS vil dette v\u00e6re `/config/.storage/knx/`\n Eksempel: `mitt_prosjekt.knxkeys`", + "knxkeys_password": "Dette ble satt ved eksport av filen fra ETS." + }, + "description": "Vennligst skriv inn informasjonen for `.knxkeys`-filen." + }, + "secure_manual": { + "data": { + "device_authentication": "Passord for enhetsgodkjenning", + "user_id": "bruker-ID", + "user_password": "Brukerpassord" + }, + "data_description": { + "device_authentication": "Dette settes i 'IP'-panelet til grensesnittet i ETS.", + "user_id": "Dette er ofte tunnelnummer +1. S\u00e5 'Tunnel 2' ville ha bruker-ID '3'.", + "user_password": "Passord for den spesifikke tunnelforbindelsen satt i 'Egenskaper'-panelet i tunnelen i ETS." + }, + "description": "Vennligst skriv inn din sikre IP-informasjon." + }, + "secure_tunneling": { + "description": "Velg hvordan du vil konfigurere KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Bruk en `.knxkeys`-fil som inneholder IP-sikre n\u00f8kler", + "secure_manual": "Konfigurer IP-sikre n\u00f8kler manuelt" + } + }, "tunnel": { "data": { "gateway": "KNX Tunneltilkobling" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX tilkoblingstype", "individual_address": "Standard individuell adresse", - "local_ip": "Lokal IP for Home Assistant (bruk 0.0.0.0 for automatisk deteksjon)", - "multicast_group": "Multicast-gruppe brukt til ruting og oppdagelse", - "multicast_port": "Multicast-port som brukes til ruting og oppdagelse", - "rate_limit": "Maksimalt utg\u00e5ende telegrammer per sekund", - "state_updater": "Aktiver lesetilstander globalt fra KNX-bussen" + "local_ip": "Lokal IP for hjemmeassistent", + "multicast_group": "Multicast gruppe", + "multicast_port": "Multicast port", + "rate_limit": "Satsgrense", + "state_updater": "Statens oppdatering" + }, + "data_description": { + "individual_address": "KNX-adresse som skal brukes av Home Assistant, f.eks. `0.0.4`", + "local_ip": "Bruk `0.0.0.0` for automatisk oppdagelse.", + "multicast_group": "Brukes til ruting og oppdagelse. Standard: `224.0.23.12`", + "multicast_port": "Brukes til ruting og oppdagelse. Standard: `3671`", + "rate_limit": "Maksimalt utg\u00e5ende telegrammer per sekund.\n Anbefalt: 20 til 40", + "state_updater": "Globalt aktiver eller deaktiver lesetilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente statuser fra KNX-bussen, \"sync_state\"-enhetsalternativer vil ikke ha noen effekt." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Rute tilbake / NAT-modus", "tunneling_type": "KNX tunneltype" + }, + "data_description": { + "host": "IP-adressen til KNX/IP-tunnelenheten.", + "port": "Port p\u00e5 KNX/IP-tunnelenheten." } } } diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index 8a30af3fd59..e0821090f29 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "file_not_found": "Podany plik '.knxkeys' nie zosta\u0142 znaleziony w \u015bcie\u017cce config/.storage/knx/", + "invalid_individual_address": "Warto\u015b\u0107 nie pasuje do wzorca dla indywidualnego adresu KNX.\n 'obszar.linia.urz\u0105dzenie'", + "invalid_ip_address": "Nieprawid\u0142owy adres IPv4.", + "invalid_signature": "Has\u0142o do odszyfrowania pliku '.knxkeys' jest nieprawid\u0142owe." }, "step": { "manual_tunnel": { "data": { "host": "Nazwa hosta lub adres IP", "individual_address": "Indywidualny adres dla po\u0142\u0105czenia", - "local_ip": "Lokalny adres IP Home Assistant (pozostaw puste w celu automatycznego wykrywania)", + "local_ip": "Lokalny adres IP Home Assistanta", "port": "Port", "route_back": "Tryb Route Back / NAT", "tunneling_type": "Typ tunelowania KNX" }, + "data_description": { + "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", + "local_ip": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania.", + "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP." + }, "description": "Prosz\u0119 wprowadzi\u0107 informacje o po\u0142\u0105czeniu urz\u0105dzenia tuneluj\u0105cego." }, "routing": { "data": { - "individual_address": "Indywidualny adres dla po\u0142\u0105czenia routingowego", - "local_ip": "Lokalny adres IP Home Assistant (pozostaw puste w celu automatycznego wykrywania)", - "multicast_group": "Grupa multicast u\u017cyta do routingu", - "multicast_port": "Port multicast u\u017cyty do routingu" + "individual_address": "Adres indywidualny", + "local_ip": "Lokalny adres IP Home Assistanta", + "multicast_group": "Grupa multicast", + "multicast_port": "Port multicast" + }, + "data_description": { + "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", + "local_ip": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania." }, "description": "Prosz\u0119 skonfigurowa\u0107 opcje routingu." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "Nazwa pliku `.knxkeys` (wraz z rozszerzeniem)", + "knxkeys_password": "Has\u0142o do odszyfrowania pliku `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Plik powinien znajdowa\u0107 si\u0119 w katalogu konfiguracyjnym w `.storage/knx/`.\nW systemie Home Assistant OS b\u0119dzie to `/config/.storage/knx/`\nPrzyk\u0142ad: `m\u00f3j_projekt.knxkeys`", + "knxkeys_password": "Zosta\u0142o to ustawione podczas eksportowania pliku z ETS." + }, + "description": "Wprowad\u017a informacje dotycz\u0105ce pliku `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "Has\u0142o uwierzytelniania urz\u0105dzenia", + "user_id": "Identyfikator u\u017cytkownika", + "user_password": "Has\u0142o u\u017cytkownika" + }, + "data_description": { + "device_authentication": "Jest to ustawiane w panelu \u201eIP\u201d interfejsu w ETS.", + "user_id": "Cz\u0119sto jest to numer tunelu plus 1. Tak wi\u0119c \u201eTunnel 2\u201d mia\u0142by identyfikator u\u017cytkownika \u201e3\u201d.", + "user_password": "Has\u0142o dla konkretnego po\u0142\u0105czenia tunelowego ustawione w panelu \u201eW\u0142a\u015bciwo\u015bci\u201d tunelu w ETS." + }, + "description": "Wprowad\u017a informacje o IP secure." + }, + "secure_tunneling": { + "description": "Wybierz, jak chcesz skonfigurowa\u0107 KNX/IP secure.", + "menu_options": { + "secure_knxkeys": "U\u017cyj pliku `.knxkeys` zawieraj\u0105cego klucze IP secure", + "secure_manual": "R\u0119czna konfiguracja kluczy IP secure" + } + }, "tunnel": { "data": { "gateway": "Po\u0142\u0105czenie tunelowe KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Typ po\u0142\u0105czenia KNX", "individual_address": "Domy\u015blny adres indywidualny", - "local_ip": "Lokalny adres IP Home Assistant (u\u017cyj 0.0.0.0 w celu automatycznego wykrywania)", - "multicast_group": "Grupa multicast u\u017cywana do routingu i wykrywania", - "multicast_port": "Port multicast u\u017cywany do routingu i wykrywania", - "rate_limit": "Maksymalna liczba wychodz\u0105cych wiadomo\u015bci na sekund\u0119", - "state_updater": "Zezw\u00f3l globalnie na odczyt stan\u00f3w z magistrali KNX" + "local_ip": "Lokalny adres IP Home Assistanta", + "multicast_group": "Grupa multicast", + "multicast_port": "Port multicast", + "rate_limit": "Limit", + "state_updater": "Aktualizator stanu" + }, + "data_description": { + "individual_address": "Adres KNX u\u017cywany przez Home Assistanta, np. `0.0.4`", + "local_ip": "U\u017cyj `0.0.0.0` do automatycznego wykrywania.", + "multicast_group": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `224.0.23.12`", + "multicast_port": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `3671`", + "rate_limit": "Maksymalna liczba wychodz\u0105cych wiadomo\u015bci na sekund\u0119.\nZalecane: od 20 do 40", + "state_updater": "Globalnie w\u0142\u0105czaj lub wy\u0142\u0105czaj odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w z magistrali KNX, opcje encji `sync_state` nie b\u0119d\u0105 mia\u0142y \u017cadnego efektu." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Tryb Route Back / NAT", "tunneling_type": "Typ tunelowania KNX" + }, + "data_description": { + "host": "Adres IP urz\u0105dzenia tuneluj\u0105cego KNX/IP.", + "port": "Port urz\u0105dzenia tuneluj\u0105cego KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index 0e8e3402961..950cedc0721 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -5,29 +5,73 @@ "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { - "cannot_connect": "Falha ao conectar" + "cannot_connect": "Falha ao conectar", + "file_not_found": "O arquivo `.knxkeys` especificado n\u00e3o foi encontrado no caminho config/.storage/knx/", + "invalid_individual_address": "O valor n\u00e3o corresponde ao padr\u00e3o do endere\u00e7o individual KNX.\n '\u00e1rea.linha.dispositivo'", + "invalid_ip_address": "Endere\u00e7o IPv4 inv\u00e1lido.", + "invalid_signature": "A senha para descriptografar o arquivo `.knxkeys` est\u00e1 errada." }, "step": { "manual_tunnel": { "data": { "host": "Nome do host", "individual_address": "Endere\u00e7o individual para a conex\u00e3o", - "local_ip": "IP local do Home Assistant (deixe em branco para detec\u00e7\u00e3o autom\u00e1tica)", + "local_ip": "IP local do Home Assistant", "port": "Porta", "route_back": "Modo Rota de Retorno / NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, + "data_description": { + "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", + "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica.", + "port": "Porta do dispositivo de tunelamento KNX/IP." + }, "description": "Por favor, digite as informa\u00e7\u00f5es de conex\u00e3o do seu dispositivo de tunelamento." }, "routing": { "data": { - "individual_address": "Endere\u00e7o individual para a conex\u00e3o de roteamento", - "local_ip": "IP local do Home Assistant (deixe vazio para detec\u00e7\u00e3o autom\u00e1tica)", - "multicast_group": "O grupo multicast usado para roteamento", - "multicast_port": "A porta multicast usada para roteamento" + "individual_address": "Endere\u00e7o individual", + "local_ip": "IP local do Home Assistant", + "multicast_group": "Grupo multicast", + "multicast_port": "Porta multicast" + }, + "data_description": { + "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", + "local_ip": "Deixe em branco para usar a descoberta autom\u00e1tica." }, "description": "Por favor, configure as op\u00e7\u00f5es de roteamento." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "O nome do seu arquivo `.knxkeys` (incluindo extens\u00e3o)", + "knxkeys_password": "A senha para descriptografar o arquivo `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "Espera-se que o arquivo seja encontrado em seu diret\u00f3rio de configura\u00e7\u00e3o em `.storage/knx/`.\n No sistema operacional Home Assistant seria `/config/.storage/knx/`\n Exemplo: `my_project.knxkeys`", + "knxkeys_password": "Isso foi definido ao exportar o arquivo do ETS." + }, + "description": "Por favor, insira as informa\u00e7\u00f5es para o seu arquivo `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "Senha de autentica\u00e7\u00e3o do dispositivo", + "user_id": "ID do usu\u00e1rio", + "user_password": "Senha do usu\u00e1rio" + }, + "data_description": { + "device_authentication": "Isso \u00e9 definido no painel 'IP' da interface no ETS.", + "user_id": "Isso geralmente \u00e9 o n\u00famero do t\u00fanel +1. Portanto, 'T\u00fanel 2' teria o ID de usu\u00e1rio '3'.", + "user_password": "Senha para a conex\u00e3o de t\u00fanel espec\u00edfica definida no painel 'Propriedades' do t\u00fanel no ETS." + }, + "description": "Por favor, insira suas informa\u00e7\u00f5es seguras de IP." + }, + "secure_tunneling": { + "description": "Selecione como deseja configurar o KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "Use um arquivo `.knxkeys` contendo chaves seguras de IP", + "secure_manual": "Configurar manualmente as chaves de seguran\u00e7a IP" + } + }, "tunnel": { "data": { "gateway": "Conex\u00e3o do t\u00fanel KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "Tipo de conex\u00e3o KNX", "individual_address": "Endere\u00e7o individual padr\u00e3o", - "local_ip": "IP local do Home Assistant (use 0.0.0.0 para detec\u00e7\u00e3o autom\u00e1tica)", - "multicast_group": "Grupo multicast usado para roteamento e descoberta", - "multicast_port": "Porta multicast usada para roteamento e descoberta", - "rate_limit": "M\u00e1ximo de telegramas de sa\u00edda por segundo", - "state_updater": "Permitir globalmente estados de leitura a partir do KNX Bus" + "local_ip": "IP local do Home Assistant", + "multicast_group": "Grupo multicast", + "multicast_port": "Porta multicast", + "rate_limit": "Taxa limite", + "state_updater": "Atualizador de estado" + }, + "data_description": { + "individual_address": "Endere\u00e7o KNX a ser usado pelo Home Assistant, por exemplo, `0.0.4`", + "local_ip": "Use `0.0.0.0` para descoberta autom\u00e1tica.", + "multicast_group": "Usado para roteamento e descoberta. Padr\u00e3o: `224.0.23.12`", + "multicast_port": "Usado para roteamento e descoberta. Padr\u00e3o: `3671`", + "rate_limit": "M\u00e1ximo de telegramas de sa\u00edda por segundo.\n Recomendado: 20 a 40", + "state_updater": "Habilite ou desabilite globalmente os estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados do barramento KNX, as op\u00e7\u00f5es de entidade `sync_state` n\u00e3o ter\u00e3o efeito." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Porta", "route_back": "Modo Rota de Retorno / NAT", "tunneling_type": "Tipo de t\u00fanel KNX" + }, + "data_description": { + "host": "Endere\u00e7o IP do dispositivo de tunelamento KNX/IP.", + "port": "Porta do dispositivo de tunelamento KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index 6c1a41dac91..14b9f919aa2 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -5,29 +5,73 @@ "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "file_not_found": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b `.knxkeys` \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 config/.storage/knx/", + "invalid_individual_address": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0448\u0430\u0431\u043b\u043e\u043d\u0443 \u0434\u043b\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 KNX 'area.line.device'.", + "invalid_ip_address": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 IPv4.", + "invalid_signature": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`." }, "step": { "manual_tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f)", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", "port": "\u041f\u043e\u0440\u0442", "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" }, + "data_description": { + "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435.", + "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP." + }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438." }, "routing": { "data": { - "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f)", - "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438", - "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438" + "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441", + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", + "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438" + }, + "data_description": { + "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", + "local_ip": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435." }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "\u0418\u043c\u044f \u0444\u0430\u0439\u043b\u0430 `.knxkeys` (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435)", + "knxkeys_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 `.knxkeys`" + }, + "data_description": { + "knxkeys_filename": "\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0439\u0434\u0435\u043d \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 `.storage/knx/`.\n\u0415\u0441\u043b\u0438 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Home Assistant OS \u044d\u0442\u043e\u0442 \u043f\u0443\u0442\u044c \u0431\u0443\u0434\u0435\u0442 `/config/.storage/knx/`\n\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: `my_project.knxkeys`", + "knxkeys_password": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0431\u044b\u043b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0437 ETS." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0444\u0430\u0439\u043b\u0435 `.knxkeys`." + }, + "secure_manual": { + "data": { + "device_authentication": "\u041f\u0430\u0440\u043e\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", + "user_id": "ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "data_description": { + "device_authentication": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'IP' \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 ETS.", + "user_id": "\u0427\u0430\u0441\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u0442\u0443\u043d\u043d\u0435\u043b\u044f +1. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, 'Tunnel 2' \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c User-ID '3'.", + "user_password": "\u041f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0442\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 'Properties' \u0442\u0443\u043d\u043d\u0435\u043b\u044f \u0432 ETS." + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e IP Secure." + }, + "secure_tunneling": { + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 KNX/IP Secure.", + "menu_options": { + "secure_knxkeys": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b `.knxkeys`, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u044e\u0447\u0438 IP secure", + "secure_manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 IP Secure \u0432\u0440\u0443\u0447\u043d\u0443\u044e" + } + }, "tunnel": { "data": { "gateway": "\u0422\u0443\u043d\u043d\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f KNX" @@ -48,11 +92,19 @@ "data": { "connection_type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f KNX", "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant (0.0.0.0 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f)", - "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f", - "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f", - "rate_limit": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443", - "state_updater": "\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0441 \u0448\u0438\u043d\u044b KNX" + "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", + "multicast_group": "\u0413\u0440\u0443\u043f\u043f\u0430 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "multicast_port": "\u041f\u043e\u0440\u0442 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438", + "rate_limit": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438", + "state_updater": "\u0421\u0440\u0435\u0434\u0441\u0442\u0432\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f" + }, + "data_description": { + "individual_address": "\u0410\u0434\u0440\u0435\u0441 KNX, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f Home Assistant, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, `0.0.4`", + "local_ip": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 `0.0.0.0` \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f.", + "multicast_group": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: `224.0.23.12`", + "multicast_port": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: `3671`", + "rate_limit": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\n\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f: \u043e\u0442 20 \u0434\u043e 40", + "state_updater": "\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438\u0437 \u0448\u0438\u043d\u044b KNX. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d, Home Assistant \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438\u0437 \u0448\u0438\u043d\u044b KNX, \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u0430 sync_state \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0430." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "\u041f\u043e\u0440\u0442", "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" + }, + "data_description": { + "host": "IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP.", + "port": "\u041f\u043e\u0440\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/tr.json b/homeassistant/components/knx/translations/tr.json index 6267038a6e0..c10f35ca48f 100644 --- a/homeassistant/components/knx/translations/tr.json +++ b/homeassistant/components/knx/translations/tr.json @@ -5,29 +5,73 @@ "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131" + "cannot_connect": "Ba\u011flanma hatas\u0131", + "file_not_found": "Belirtilen `.knxkeys` dosyas\u0131 config/.storage/knx/ yolunda bulunamad\u0131", + "invalid_individual_address": "De\u011fer, KNX bireysel adresi i\u00e7in modelle e\u015fle\u015fmiyor.\n 'alan.hat.cihaz'", + "invalid_ip_address": "Ge\u00e7ersiz IPv4 adresi.", + "invalid_signature": "`.knxkeys` dosyas\u0131n\u0131n \u015fifresini \u00e7\u00f6zmek i\u00e7in \u015fifre yanl\u0131\u015f." }, "step": { "manual_tunnel": { "data": { "host": "Sunucu", "individual_address": "Ba\u011flant\u0131 i\u00e7in bireysel adres", - "local_ip": "Home Assistant Yerel IP'si (otomatik alg\u0131lama i\u00e7in bo\u015f b\u0131rak\u0131n)", + "local_ip": "Home Asistan\u0131n\u0131n Yerel IP'si", "port": "Port", "route_back": "Geri Y\u00f6nlendirme / NAT Modu", "tunneling_type": "KNX T\u00fcnel Tipi" }, + "data_description": { + "host": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n IP adresi.", + "local_ip": "Otomatik bulmay\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n.", + "port": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n ba\u011flant\u0131 noktas\u0131." + }, "description": "L\u00fctfen t\u00fcnel cihaz\u0131n\u0131z\u0131n ba\u011flant\u0131 bilgilerini girin." }, "routing": { "data": { - "individual_address": "Y\u00f6nlendirme ba\u011flant\u0131s\u0131 i\u00e7in bireysel adres", - "local_ip": "Home Assistant Yerel IP'si (otomatik alg\u0131lama i\u00e7in bo\u015f b\u0131rak\u0131n)", - "multicast_group": "Y\u00f6nlendirme i\u00e7in kullan\u0131lan \u00e7ok noktaya yay\u0131n grubu", - "multicast_port": "Y\u00f6nlendirme i\u00e7in kullan\u0131lan \u00e7ok noktaya yay\u0131n ba\u011flant\u0131 noktas\u0131" + "individual_address": "Bireysel adres", + "local_ip": "Home Asistan\u0131n\u0131n Yerel IP'si", + "multicast_group": "\u00c7ok noktaya yay\u0131n grubu", + "multicast_port": "\u00c7ok noktaya yay\u0131n ba\u011flant\u0131 noktas\u0131" + }, + "data_description": { + "individual_address": "Home Assistant taraf\u0131ndan kullan\u0131lacak KNX adresi, \u00f6r. \"0.0.4\"", + "local_ip": "Otomatik bulmay\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n." }, "description": "L\u00fctfen y\u00f6nlendirme se\u00e7eneklerini yap\u0131land\u0131r\u0131n." }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.knxkeys` dosyan\u0131z\u0131n dosya ad\u0131 (uzant\u0131 dahil)", + "knxkeys_password": "`.knxkeys` dosyas\u0131n\u0131n \u015fifresini \u00e7\u00f6zmek i\u00e7in \u015fifre" + }, + "data_description": { + "knxkeys_filename": "Dosyan\u0131n yap\u0131land\u0131rma dizininizde `.storage/knx/` i\u00e7inde bulunmas\u0131 bekleniyor.\n Home Assistant OS'de bu, `/config/.storage/knx/` olacakt\u0131r.\n \u00d6rnek: \"my_project.knxkeys\"", + "knxkeys_password": "Bu, dosyay\u0131 ETS'den d\u0131\u015fa aktar\u0131rken ayarland\u0131." + }, + "description": "L\u00fctfen `.knxkeys` dosyan\u0131z i\u00e7in bilgileri girin." + }, + "secure_manual": { + "data": { + "device_authentication": "Cihaz do\u011frulama \u015fifresi", + "user_id": "Kullan\u0131c\u0131 Kimli\u011fi", + "user_password": "Kullan\u0131c\u0131 \u015fifresi" + }, + "data_description": { + "device_authentication": "Bu, ETS'deki aray\u00fcz\u00fcn 'IP' panelinde ayarlan\u0131r.", + "user_id": "Bu genellikle t\u00fcnel numaras\u0131 +1'dir. Yani 'T\u00fcnel 2' Kullan\u0131c\u0131 Kimli\u011fi '3' olacakt\u0131r.", + "user_password": "ETS'de t\u00fcnelin '\u00d6zellikler' panelinde ayarlanan belirli t\u00fcnel ba\u011flant\u0131s\u0131 i\u00e7in \u015fifre." + }, + "description": "L\u00fctfen IP g\u00fcvenli bilgilerinizi giriniz." + }, + "secure_tunneling": { + "description": "KNX/IP Secure'u nas\u0131l yap\u0131land\u0131rmak istedi\u011finizi se\u00e7in.", + "menu_options": { + "secure_knxkeys": "IP g\u00fcvenli anahtarlar\u0131 i\u00e7eren bir \".knxkeys\" dosyas\u0131 kullan\u0131n", + "secure_manual": "IP g\u00fcvenli anahtarlar\u0131n\u0131 manuel olarak yap\u0131land\u0131r\u0131n" + } + }, "tunnel": { "data": { "gateway": "KNX T\u00fcnel Ba\u011flant\u0131s\u0131" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX Ba\u011flant\u0131 T\u00fcr\u00fc", "individual_address": "Varsay\u0131lan bireysel adres", - "local_ip": "Home Assistant Yerel IP'si (otomatik alg\u0131lama i\u00e7in 0.0.0.0 kullan\u0131n)", - "multicast_group": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131lan \u00e7ok noktaya yay\u0131n grubu", - "multicast_port": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131lan \u00e7ok noktaya yay\u0131n ba\u011flant\u0131 noktas\u0131", - "rate_limit": "Saniyede maksimum giden telegram say\u0131s\u0131", - "state_updater": "KNX Veri Yolu'ndan okuma durumlar\u0131n\u0131 genel olarak etkinle\u015ftirin" + "local_ip": "Home Asistan\u0131n\u0131n Yerel IP'si", + "multicast_group": "\u00c7ok noktaya yay\u0131n grubu", + "multicast_port": "\u00c7ok noktaya yay\u0131n ba\u011flant\u0131 noktas\u0131", + "rate_limit": "H\u0131z s\u0131n\u0131r\u0131", + "state_updater": "Durum g\u00fcncelleyici" + }, + "data_description": { + "individual_address": "Home Assistant taraf\u0131ndan kullan\u0131lacak KNX adresi, \u00f6r. \"0.0.4\"", + "local_ip": "Otomatik ke\u015fif i\u00e7in \"0.0.0.0\"\u0131 kullan\u0131n.", + "multicast_group": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"224.0.23.12\"", + "multicast_port": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"3671\"", + "rate_limit": "Saniyede maksimum giden telegram say\u0131s\u0131.\n \u00d6nerilen: 20 ila 40", + "state_updater": "KNX Bus'tan okuma durumlar\u0131n\u0131 k\u00fcresel olarak etkinle\u015ftirin veya devre d\u0131\u015f\u0131 b\u0131rak\u0131n. Devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131\u011f\u0131nda, Home Assistant KNX Bus'tan durumlar\u0131 aktif olarak almaz, 'sync_state' varl\u0131k se\u00e7eneklerinin hi\u00e7bir etkisi olmaz." } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "Port", "route_back": "Geri Y\u00f6nlendirme / NAT Modu", "tunneling_type": "KNX T\u00fcnel Tipi" + }, + "data_description": { + "host": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n IP adresi.", + "port": "KNX/IP t\u00fcnelleme cihaz\u0131n\u0131n ba\u011flant\u0131 noktas\u0131." } } } diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index b6c09456fb3..8f740b85f6d 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -5,29 +5,73 @@ "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "file_not_found": "\u8def\u5f91 config/.storage/knx/ \u5167\u627e\u4e0d\u5230\u6307\u5b9a `.knxkeys` \u6a94\u6848", + "invalid_individual_address": "\u6578\u503c\u8207 KNX \u500b\u5225\u4f4d\u5740\u4e0d\u76f8\u7b26\u3002\n'area.line.device'", + "invalid_ip_address": "IPv4 \u4f4d\u5740\u7121\u6548\u3002", + "invalid_signature": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc\u932f\u8aa4\u3002" }, "step": { "manual_tunnel": { "data": { "host": "\u4e3b\u6a5f\u7aef", "individual_address": "\u9023\u7dda\u500b\u5225\u4f4d\u5740", - "local_ip": "Home Assistant \u672c\u5730\u7aef IP\uff08\u4fdd\u7559\u7a7a\u767d\u4ee5\u81ea\u52d5\u5075\u6e2c\uff09", + "local_ip": "Home Assistant \u672c\u5730\u7aef IP", "port": "\u901a\u8a0a\u57e0", "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" }, + "data_description": { + "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", + "local_ip": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u3002", + "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002" + }, "description": "\u8acb\u8f38\u5165\u901a\u9053\u88dd\u7f6e\u7684\u9023\u7dda\u8cc7\u8a0a\u3002" }, "routing": { "data": { - "individual_address": "\u8def\u7531\u9023\u7dda\u500b\u5225\u4f4d\u5740", - "local_ip": "Home Assistant \u672c\u5730\u7aef IP\uff08\u4fdd\u7559\u7a7a\u767d\u4ee5\u81ea\u52d5\u5075\u6e2c\uff09", - "multicast_group": "\u4f7f\u7528\u65bc\u8def\u7531\u7684 Multicast \u7fa4\u7d44", - "multicast_port": "\u4f7f\u7528\u65bc\u8def\u7531\u7684 Multicast \u901a\u8a0a\u57e0" + "individual_address": "\u500b\u5225\u4f4d\u5740", + "local_ip": "Home Assistant \u672c\u5730\u7aef IP", + "multicast_group": "Multicast \u7fa4\u7d44", + "multicast_port": "Multicast \u901a\u8a0a\u57e0" + }, + "data_description": { + "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", + "local_ip": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u3002" }, "description": "\u8acb\u8a2d\u5b9a\u8def\u7531\u9078\u9805\u3002" }, + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.knxkeys` \u6a94\u6848\u5168\u540d\uff08\u5305\u542b\u526f\u6a94\u540d\uff09", + "knxkeys_password": "\u52a0\u5bc6 `.knxkeys` \u6a94\u6848\u5bc6\u78bc" + }, + "data_description": { + "knxkeys_filename": "\u6a94\u6848\u61c9\u8a72\u4f4d\u65bc\u8a2d\u5b9a\u8cc7\u6599\u593e `.storage/knx/` \u5167\u3002\n\u82e5\u70ba Home Assistant OS\u3001\u5247\u61c9\u8a72\u70ba `/config/.storage/knx/`\n\u4f8b\u5982\uff1a`my_project.knxkeys`", + "knxkeys_password": "\u81ea ETS \u532f\u51fa\u6a94\u6848\u4e2d\u9032\u884c\u8a2d\u5b9a\u3002" + }, + "description": "\u8acb\u8f38\u5165 `.knxkeys` \u6a94\u6848\u8cc7\u8a0a\u3002" + }, + "secure_manual": { + "data": { + "device_authentication": "\u88dd\u7f6e\u8a8d\u8b49\u5bc6\u78bc", + "user_id": "\u4f7f\u7528\u8005 ID", + "user_password": "\u4f7f\u7528\u8005\u5bc6\u78bc" + }, + "data_description": { + "device_authentication": "\u65bc EST \u4ecb\u9762\u4e2d 'IP' \u9762\u677f\u9032\u884c\u8a2d\u5b9a\u3002", + "user_id": "\u901a\u5e38\u70ba\u901a\u9053\u6578 +1\u3002\u56e0\u6b64 'Tunnel 2' \u5c07\u5177\u6709\u4f7f\u7528\u8005 ID '3'\u3002", + "user_password": "\u65bc ETS \u901a\u9053 'Properties' \u9762\u677f\u53ef\u8a2d\u5b9a\u6307\u5b9a\u901a\u9053\u9023\u7dda\u5bc6\u78bc\u3002" + }, + "description": "\u8acb\u8f38\u5165 IP \u52a0\u5bc6\u8cc7\u8a0a\u3002" + }, + "secure_tunneling": { + "description": "\u9078\u64c7\u5982\u4f55\u8a2d\u5b9a KNX/IP \u52a0\u5bc6\u3002", + "menu_options": { + "secure_knxkeys": "\u4f7f\u7528\u5305\u542b IP \u52a0\u5bc6\u91d1\u8000\u7684 knxkeys \u6a94\u6848", + "secure_manual": "\u624b\u52d5\u8a2d\u5b9a IP \u52a0\u5bc6\u91d1\u8000" + } + }, "tunnel": { "data": { "gateway": "KNX \u901a\u9053\u9023\u7dda" @@ -48,11 +92,19 @@ "data": { "connection_type": "KNX \u9023\u7dda\u985e\u5225", "individual_address": "\u9810\u8a2d\u500b\u5225\u4f4d\u5740", - "local_ip": "Home Assistant \u672c\u5730\u7aef IP\uff08\u586b\u5165 0.0.0.0 \u555f\u7528\u81ea\u52d5\u5075\u6e2c\uff09", - "multicast_group": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u641c\u7d22\u7684 Multicast \u7fa4\u7d44", - "multicast_port": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u641c\u7d22\u7684 Multicast \u901a\u8a0a\u57e0", - "rate_limit": "\u6700\u5927\u6bcf\u79d2\u767c\u51fa Telegram", - "state_updater": "\u7531 KNX Bus \u8b80\u53d6\u72c0\u614b\u5168\u555f\u7528" + "local_ip": "Home Assistant \u672c\u5730\u7aef IP", + "multicast_group": "Multicast \u7fa4\u7d44", + "multicast_port": "Multicast \u901a\u8a0a\u57e0", + "rate_limit": "\u983b\u7387\u9650\u5236", + "state_updater": "\u88dd\u614b\u66f4\u65b0\u5668" + }, + "data_description": { + "individual_address": "Home Assistant \u6240\u4f7f\u7528\u4e4b KNX \u4f4d\u5740\u3002\u4f8b\u5982\uff1a`0.0.4`", + "local_ip": "\u4f7f\u7528 `0.0.0.0` \u9032\u884c\u81ea\u52d5\u641c\u7d22\u3002", + "multicast_group": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`224.0.23.12`", + "multicast_port": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`3671`", + "rate_limit": "\u6bcf\u79d2\u6700\u5927 Telegram \u767c\u9001\u91cf\u3002\u5efa\u8b70\uff1a20 - 40", + "state_updater": "\u5168\u5c40\u958b\u555f\u6216\u95dc\u9589\u81ea KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u72c0\u614b\uff0c`sync_state` \u5be6\u9ad4\u9078\u9805\u5c07\u4e0d\u5177\u6548\u679c\u3002" } }, "tunnel": { @@ -62,6 +114,10 @@ "port": "\u901a\u8a0a\u57e0", "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" + }, + "data_description": { + "host": "KNX/IP \u901a\u9053\u88dd\u7f6e IP \u4f4d\u5740\u3002", + "port": "KNX/IP \u901a\u9053\u88dd\u7f6e\u901a\u8a0a\u57e0\u3002" } } } diff --git a/homeassistant/components/konnected/translations/fr.json b/homeassistant/components/konnected/translations/fr.json index 2ddd335fa9e..4b84efa351d 100644 --- a/homeassistant/components/konnected/translations/fr.json +++ b/homeassistant/components/konnected/translations/fr.json @@ -97,7 +97,7 @@ "options_switch": { "data": { "activation": "Sortie lorsque activ\u00e9", - "momentary": "Dur\u00e9e de l'impulsion (ms) (facultatif)", + "momentary": "Dur\u00e9e de l'impulsion (en millisecondes, facultatif)", "more_states": "Configurer des \u00e9tats suppl\u00e9mentaires pour cette zone", "name": "Nom (facultatif)", "pause": "Pause entre les impulsions (ms) (facultatif)", diff --git a/homeassistant/components/konnected/translations/hu.json b/homeassistant/components/konnected/translations/hu.json index e634727593b..c40b1823424 100644 --- a/homeassistant/components/konnected/translations/hu.json +++ b/homeassistant/components/konnected/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "not_konn_panel": "Nem felismert Konnected.io eszk\u00f6z", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" @@ -41,7 +41,7 @@ "options_binary": { "data": { "inverse": "Invert\u00e1lja a nyitott/z\u00e1rt \u00e1llapotot", - "name": "N\u00e9v (nem k\u00f6telez\u0151)", + "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", "type": "Bin\u00e1ris \u00e9rz\u00e9kel\u0151 t\u00edpusa" }, "description": "{zone} opci\u00f3k", @@ -49,7 +49,7 @@ }, "options_digital": { "data": { - "name": "N\u00e9v (nem k\u00f6telez\u0151)", + "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", "poll_interval": "Lek\u00e9rdez\u00e9si id\u0151k\u00f6z (perc) (opcion\u00e1lis)", "type": "\u00c9rz\u00e9kel\u0151 t\u00edpusa" }, @@ -99,7 +99,7 @@ "activation": "Kimenet bekapcsolt \u00e1llapotban", "momentary": "Impulzus id\u0151tartama (ms) (opcion\u00e1lis)", "more_states": "Tov\u00e1bbi \u00e1llapotok konfigur\u00e1l\u00e1sa ehhez a z\u00f3n\u00e1hoz", - "name": "N\u00e9v (nem k\u00f6telez\u0151)", + "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", "pause": "Sz\u00fcnet impulzusok k\u00f6z\u00f6tt (ms) (opcion\u00e1lis)", "repeat": "Ism\u00e9tl\u00e9si id\u0151k (-1 = v\u00e9gtelen) (opcion\u00e1lis)" }, diff --git a/homeassistant/components/konnected/translations/it.json b/homeassistant/components/konnected/translations/it.json index 81127f0dff2..682a27b50d0 100644 --- a/homeassistant/components/konnected/translations/it.json +++ b/homeassistant/components/konnected/translations/it.json @@ -34,8 +34,8 @@ }, "error": { "bad_host": "URL host API di sostituzione non valido", - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "step": { "options_binary": { @@ -92,7 +92,7 @@ "override_api_host": "Sovrascrivi l'URL predefinito del pannello host API di Home Assistant" }, "description": "Seleziona il comportamento desiderato per il tuo pannello", - "title": "Configura Altro" + "title": "Configura altro" }, "options_switch": { "data": { diff --git a/homeassistant/components/konnected/translations/pt-BR.json b/homeassistant/components/konnected/translations/pt-BR.json index b49b487ab0d..e3079fc50f7 100644 --- a/homeassistant/components/konnected/translations/pt-BR.json +++ b/homeassistant/components/konnected/translations/pt-BR.json @@ -102,7 +102,7 @@ "repeat": "Intervalo para repetir (-1=infinito) (opcional)" }, "description": "Selecione as op\u00e7\u00f5es para o switch conectado a {zone}: estado {state}", - "title": "Configurar o switch de sa\u00edda" + "title": "Configure o interruptor de sa\u00edda" } } } diff --git a/homeassistant/components/kostal_plenticore/translations/ca.json b/homeassistant/components/kostal_plenticore/translations/ca.json index 2ce39d904a6..fe22d6e3acd 100644 --- a/homeassistant/components/kostal_plenticore/translations/ca.json +++ b/homeassistant/components/kostal_plenticore/translations/ca.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Inversor solar Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/de.json b/homeassistant/components/kostal_plenticore/translations/de.json index dfd568f937c..095487fff3f 100644 --- a/homeassistant/components/kostal_plenticore/translations/de.json +++ b/homeassistant/components/kostal_plenticore/translations/de.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar-Wechselrichter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/el.json b/homeassistant/components/kostal_plenticore/translations/el.json index 518070a76ef..c29ca36535e 100644 --- a/homeassistant/components/kostal_plenticore/translations/el.json +++ b/homeassistant/components/kostal_plenticore/translations/el.json @@ -16,6 +16,5 @@ } } } - }, - "title": "\u0397\u03bb\u03b9\u03b1\u03ba\u03cc\u03c2 \u03bc\u03b5\u03c4\u03b1\u03c4\u03c1\u03bf\u03c0\u03ad\u03b1\u03c2 Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/en.json b/homeassistant/components/kostal_plenticore/translations/en.json index a058336b077..f9334da7aad 100644 --- a/homeassistant/components/kostal_plenticore/translations/en.json +++ b/homeassistant/components/kostal_plenticore/translations/en.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar Inverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/es.json b/homeassistant/components/kostal_plenticore/translations/es.json index e763acfbe4d..66eed9e0642 100644 --- a/homeassistant/components/kostal_plenticore/translations/es.json +++ b/homeassistant/components/kostal_plenticore/translations/es.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Inversor solar Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/et.json b/homeassistant/components/kostal_plenticore/translations/et.json index c96935d5db8..6b090a0c959 100644 --- a/homeassistant/components/kostal_plenticore/translations/et.json +++ b/homeassistant/components/kostal_plenticore/translations/et.json @@ -16,6 +16,5 @@ } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/fr.json b/homeassistant/components/kostal_plenticore/translations/fr.json index 66888e8879b..c0f59229275 100644 --- a/homeassistant/components/kostal_plenticore/translations/fr.json +++ b/homeassistant/components/kostal_plenticore/translations/fr.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Onduleur solaire Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/hu.json b/homeassistant/components/kostal_plenticore/translations/hu.json index 3ffe413a82b..10e87e8deca 100644 --- a/homeassistant/components/kostal_plenticore/translations/hu.json +++ b/homeassistant/components/kostal_plenticore/translations/hu.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore szol\u00e1r inverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/id.json b/homeassistant/components/kostal_plenticore/translations/id.json index c249355f8ca..90c843f5d4d 100644 --- a/homeassistant/components/kostal_plenticore/translations/id.json +++ b/homeassistant/components/kostal_plenticore/translations/id.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Solar Inverter Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/it.json b/homeassistant/components/kostal_plenticore/translations/it.json index 8e46b765fe0..b2b7b688bc4 100644 --- a/homeassistant/components/kostal_plenticore/translations/it.json +++ b/homeassistant/components/kostal_plenticore/translations/it.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Inverter solare Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/ja.json b/homeassistant/components/kostal_plenticore/translations/ja.json index 8a16c8c918e..0526d7f7729 100644 --- a/homeassistant/components/kostal_plenticore/translations/ja.json +++ b/homeassistant/components/kostal_plenticore/translations/ja.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar Inverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/nl.json b/homeassistant/components/kostal_plenticore/translations/nl.json index 83a77fb6e0d..05bdafd606b 100644 --- a/homeassistant/components/kostal_plenticore/translations/nl.json +++ b/homeassistant/components/kostal_plenticore/translations/nl.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore omvormer voor zonne-energie" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/no.json b/homeassistant/components/kostal_plenticore/translations/no.json index 0f0d77a83e6..b79a6b1795c 100644 --- a/homeassistant/components/kostal_plenticore/translations/no.json +++ b/homeassistant/components/kostal_plenticore/translations/no.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar Inverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/pl.json b/homeassistant/components/kostal_plenticore/translations/pl.json index 781bddfc979..c9025af7205 100644 --- a/homeassistant/components/kostal_plenticore/translations/pl.json +++ b/homeassistant/components/kostal_plenticore/translations/pl.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Inwerter solarny Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/pt-BR.json b/homeassistant/components/kostal_plenticore/translations/pt-BR.json index a670c5a41be..b829ba6e92b 100644 --- a/homeassistant/components/kostal_plenticore/translations/pt-BR.json +++ b/homeassistant/components/kostal_plenticore/translations/pt-BR.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Inversor Solar Kostal Plenticore" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/ru.json b/homeassistant/components/kostal_plenticore/translations/ru.json index d272fd0f304..02ecda0034c 100644 --- a/homeassistant/components/kostal_plenticore/translations/ru.json +++ b/homeassistant/components/kostal_plenticore/translations/ru.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar Inverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/tr.json b/homeassistant/components/kostal_plenticore/translations/tr.json index f0742d20e78..78ade55a949 100644 --- a/homeassistant/components/kostal_plenticore/translations/tr.json +++ b/homeassistant/components/kostal_plenticore/translations/tr.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore Solar \u0130nverter" + } } \ No newline at end of file diff --git a/homeassistant/components/kostal_plenticore/translations/zh-Hant.json b/homeassistant/components/kostal_plenticore/translations/zh-Hant.json index f115cf74c89..fa1b3d064be 100644 --- a/homeassistant/components/kostal_plenticore/translations/zh-Hant.json +++ b/homeassistant/components/kostal_plenticore/translations/zh-Hant.json @@ -16,6 +16,5 @@ } } } - }, - "title": "Kostal Plenticore \u592a\u967d\u80fd\u63db\u6d41\u5668" + } } \ No newline at end of file diff --git a/homeassistant/components/kraken/translations/it.json b/homeassistant/components/kraken/translations/it.json index 4b646be7a8d..2fabbc9ad5c 100644 --- a/homeassistant/components/kraken/translations/it.json +++ b/homeassistant/components/kraken/translations/it.json @@ -4,14 +4,14 @@ "already_configured": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "step": { "user": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "description": "Vuoi iniziare la configurazione?" } diff --git a/homeassistant/components/light/translations/hu.json b/homeassistant/components/light/translations/hu.json index 0be84e20763..986fd5d1787 100644 --- a/homeassistant/components/light/translations/hu.json +++ b/homeassistant/components/light/translations/hu.json @@ -13,6 +13,7 @@ "is_on": "{entity_name} fel van kapcsolva" }, "trigger_type": { + "changed_states": "{entity_name} be- vagy kikapcsolt", "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} le lett kapcsolva", "turned_on": "{entity_name} fel lett kapcsolva" diff --git a/homeassistant/components/logi_circle/translations/hu.json b/homeassistant/components/logi_circle/translations/hu.json index f79ab3944dc..947f11e4907 100644 --- a/homeassistant/components/logi_circle/translations/hu.json +++ b/homeassistant/components/logi_circle/translations/hu.json @@ -8,12 +8,12 @@ }, "error": { "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", - "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt megnyomn\u00e1 a K\u00fcld\u00e9s gombot", + "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt folytatn\u00e1", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" }, "step": { "auth": { - "description": "K\u00e9rj\u00fck, k\u00f6vesse az al\u00e1bbi linket, \u00e9s ** Fogadja el ** a LogiCircle -fi\u00f3kj\u00e1hoz val\u00f3 hozz\u00e1f\u00e9r\u00e9st, majd t\u00e9rjen vissza, \u00e9s nyomja meg az al\u00e1bbi ** K\u00fcld\u00e9s ** gombot. \n\n [Link]({authorization_url})", + "description": "K\u00e9rj\u00fck, k\u00f6vesse az al\u00e1bbi linket, \u00e9s ** Fogadja el ** a LogiCircle -fi\u00f3kj\u00e1hoz val\u00f3 hozz\u00e1f\u00e9r\u00e9st, majd t\u00e9rjen vissza, \u00e9s nyomja meg az al\u00e1bbi **Mehet** gombot. \n\n [Link]({authorization_url})", "title": "Hiteles\u00edt\u00e9s a LogiCircle seg\u00edts\u00e9g\u00e9vel" }, "user": { diff --git a/homeassistant/components/logi_circle/translations/it.json b/homeassistant/components/logi_circle/translations/it.json index 1299c4c53e4..fe17a15cae3 100644 --- a/homeassistant/components/logi_circle/translations/it.json +++ b/homeassistant/components/logi_circle/translations/it.json @@ -4,7 +4,7 @@ "already_configured": "L'account \u00e8 gi\u00e0 configurato", "external_error": "Si \u00e8 verificata un'eccezione da un altro flusso.", "external_setup": "Logi Circle configurato con successo da un altro flusso.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione." + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione." }, "error": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", diff --git a/homeassistant/components/lookin/translations/hu.json b/homeassistant/components/lookin/translations/hu.json index ab18b579bd4..b6db30f8d99 100644 --- a/homeassistant/components/lookin/translations/hu.json +++ b/homeassistant/components/lookin/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton" }, @@ -15,7 +15,7 @@ "step": { "device_name": { "data": { - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" } }, "discovery_confirm": { diff --git a/homeassistant/components/luftdaten/translations/hu.json b/homeassistant/components/luftdaten/translations/hu.json index f7a241533da..7439546f796 100644 --- a/homeassistant/components/luftdaten/translations/hu.json +++ b/homeassistant/components/luftdaten/translations/hu.json @@ -9,7 +9,7 @@ "user": { "data": { "show_on_map": "Megjelen\u00edt\u00e9s a t\u00e9rk\u00e9pen", - "station_id": "Luftdaten \u00e9rz\u00e9kel\u0151 ID" + "station_id": "\u00c9rz\u00e9kel\u0151 azonos\u00edt\u00f3" }, "title": "Luftdaten be\u00e1ll\u00edt\u00e1sa" } diff --git a/homeassistant/components/luftdaten/translations/pt-BR.json b/homeassistant/components/luftdaten/translations/pt-BR.json index b4cdaf000ab..82b1f09735b 100644 --- a/homeassistant/components/luftdaten/translations/pt-BR.json +++ b/homeassistant/components/luftdaten/translations/pt-BR.json @@ -8,7 +8,7 @@ "step": { "user": { "data": { - "show_on_map": "Mostrar no mapa?", + "show_on_map": "Mostrar no mapa", "station_id": "ID do Sensor Luftdaten" }, "title": "Definir Luftdaten" diff --git a/homeassistant/components/lutron_caseta/translations/pl.json b/homeassistant/components/lutron_caseta/translations/pl.json index bc4f9d61f39..47e6a07e146 100644 --- a/homeassistant/components/lutron_caseta/translations/pl.json +++ b/homeassistant/components/lutron_caseta/translations/pl.json @@ -23,7 +23,7 @@ "host": "Nazwa hosta lub adres IP" }, "description": "Wprowad\u017a adres IP urz\u0105dzenia", - "title": "Po\u0142\u0105cz si\u0119 automatycznie z mostkiem" + "title": "Automatyczne po\u0142\u0105czenie z mostkiem" } } }, diff --git a/homeassistant/components/lyric/translations/hu.json b/homeassistant/components/lyric/translations/hu.json index 7586310c8a7..b7eac97525c 100644 --- a/homeassistant/components/lyric/translations/hu.json +++ b/homeassistant/components/lyric/translations/hu.json @@ -10,7 +10,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "reauth_confirm": { "description": "A Lyric integr\u00e1ci\u00f3nak \u00fajra hiteles\u00edtenie kell a fi\u00f3kj\u00e1t.", diff --git a/homeassistant/components/lyric/translations/it.json b/homeassistant/components/lyric/translations/it.json index c544598079e..6fb3fb44275 100644 --- a/homeassistant/components/lyric/translations/it.json +++ b/homeassistant/components/lyric/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "create_entry": { diff --git a/homeassistant/components/mailgun/translations/ja.json b/homeassistant/components/mailgun/translations/ja.json index 58818dd99de..34c6ced3f38 100644 --- a/homeassistant/components/mailgun/translations/ja.json +++ b/homeassistant/components/mailgun/translations/ja.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Webhook\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u4fe1\u3059\u308b\u306b\u306f\u3001Home Assistant\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3001\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u304b\u3089\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002" }, "create_entry": { - "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[Webhooks with Mailgun]({mailgun_url})\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type: application/x-www-fjsorm-urlencoded\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[Webhooks with Mailgun]({mailgun_url})\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type(\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e): application/x-www-fjsorm-urlencoded\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "step": { "user": { diff --git a/homeassistant/components/mazda/translations/ca.json b/homeassistant/components/mazda/translations/ca.json index 17ef370b007..2289ba3986c 100644 --- a/homeassistant/components/mazda/translations/ca.json +++ b/homeassistant/components/mazda/translations/ca.json @@ -17,10 +17,8 @@ "password": "Contrasenya", "region": "Regi\u00f3" }, - "description": "Introdueix el correu electr\u00f2nic i la contrasenya que utilitzes per iniciar sessi\u00f3 a l'aplicaci\u00f3 de m\u00f2bil MyMazda.", - "title": "Serveis connectats de Mazda - Afegeix un compte" + "description": "Introdueix el correu electr\u00f2nic i la contrasenya que utilitzes per iniciar sessi\u00f3 a l'aplicaci\u00f3 de m\u00f2bil MyMazda." } } - }, - "title": "Serveis connectats de Mazda" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/de.json b/homeassistant/components/mazda/translations/de.json index 58409ec9b8a..ba116a6fe2c 100644 --- a/homeassistant/components/mazda/translations/de.json +++ b/homeassistant/components/mazda/translations/de.json @@ -17,10 +17,8 @@ "password": "Passwort", "region": "Region" }, - "description": "Bitte gib die E-Mail-Adresse und das Passwort ein, die du f\u00fcr die Anmeldung bei der MyMazda Mobile App verwendest.", - "title": "Mazda Connected Services - Konto hinzuf\u00fcgen" + "description": "Bitte gib die E-Mail-Adresse und das Passwort ein, die du f\u00fcr die Anmeldung bei der MyMazda Mobile App verwendest." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/el.json b/homeassistant/components/mazda/translations/el.json index c95e8ad4747..7fbf538de63 100644 --- a/homeassistant/components/mazda/translations/el.json +++ b/homeassistant/components/mazda/translations/el.json @@ -17,10 +17,8 @@ "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b5\u03af\u03bf\u03c5 \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae MyMazda \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac.", - "title": "Mazda Connected Services - \u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd" + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b5\u03af\u03bf\u03c5 \u03ba\u03b1\u03b9 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae MyMazda \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/en.json b/homeassistant/components/mazda/translations/en.json index b483947aaa0..9130a1b6334 100644 --- a/homeassistant/components/mazda/translations/en.json +++ b/homeassistant/components/mazda/translations/en.json @@ -17,10 +17,8 @@ "password": "Password", "region": "Region" }, - "description": "Please enter the email address and password you use to log into the MyMazda mobile app.", - "title": "Mazda Connected Services - Add Account" + "description": "Please enter the email address and password you use to log into the MyMazda mobile app." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/es.json b/homeassistant/components/mazda/translations/es.json index a9ffa26787c..f0ba0f4da49 100644 --- a/homeassistant/components/mazda/translations/es.json +++ b/homeassistant/components/mazda/translations/es.json @@ -17,10 +17,8 @@ "password": "Contrase\u00f1a", "region": "Regi\u00f3n" }, - "description": "Introduce la direcci\u00f3n de correo electr\u00f3nico y la contrase\u00f1a que utilizas para iniciar sesi\u00f3n en la aplicaci\u00f3n m\u00f3vil MyMazda.", - "title": "Servicios Conectados de Mazda - A\u00f1adir cuenta" + "description": "Introduce la direcci\u00f3n de correo electr\u00f3nico y la contrase\u00f1a que utilizas para iniciar sesi\u00f3n en la aplicaci\u00f3n m\u00f3vil MyMazda." } } - }, - "title": "Servicios Conectados de Mazda" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/et.json b/homeassistant/components/mazda/translations/et.json index 39ead99765c..1b6cac11093 100644 --- a/homeassistant/components/mazda/translations/et.json +++ b/homeassistant/components/mazda/translations/et.json @@ -17,10 +17,8 @@ "password": "Salas\u00f5na", "region": "Piirkond" }, - "description": "Sisesta e-posti aadress ja salas\u00f5na mida kasutad MyMazda mobiilirakendusse sisselogimiseks.", - "title": "Mazda Connected Services - lisa konto" + "description": "Sisesta e-posti aadress ja salas\u00f5na mida kasutad MyMazda mobiilirakendusse sisselogimiseks." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/fr.json b/homeassistant/components/mazda/translations/fr.json index 8024852de46..d42ab5453c4 100644 --- a/homeassistant/components/mazda/translations/fr.json +++ b/homeassistant/components/mazda/translations/fr.json @@ -17,10 +17,8 @@ "password": "Mot de passe", "region": "R\u00e9gion" }, - "description": "Veuillez saisir l'adresse e-mail et le mot de passe que vous utilisez pour vous connecter \u00e0 l'application mobile MyMazda.", - "title": "Services connect\u00e9s Mazda - Ajouter un compte" + "description": "Veuillez saisir l'adresse e-mail et le mot de passe que vous utilisez pour vous connecter \u00e0 l'application mobile MyMazda." } } - }, - "title": "Services connect\u00e9s Mazda" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/hu.json b/homeassistant/components/mazda/translations/hu.json index ec62b456021..42afc763687 100644 --- a/homeassistant/components/mazda/translations/hu.json +++ b/homeassistant/components/mazda/translations/hu.json @@ -17,10 +17,8 @@ "password": "Jelsz\u00f3", "region": "R\u00e9gi\u00f3" }, - "description": "K\u00e9rj\u00fck, adja meg azt az e-mail c\u00edmet \u00e9s jelsz\u00f3t, amelyet a MyMazda mobilalkalmaz\u00e1sba val\u00f3 bejelentkez\u00e9shez haszn\u00e1lt.", - "title": "Mazda Connected Services - Fi\u00f3k hozz\u00e1ad\u00e1sa" + "description": "K\u00e9rj\u00fck, adja meg azt az e-mail c\u00edmet \u00e9s jelsz\u00f3t, amelyet a MyMazda mobilalkalmaz\u00e1sba val\u00f3 bejelentkez\u00e9shez haszn\u00e1lt." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/id.json b/homeassistant/components/mazda/translations/id.json index fdcc736162e..cd4f73f23f6 100644 --- a/homeassistant/components/mazda/translations/id.json +++ b/homeassistant/components/mazda/translations/id.json @@ -17,10 +17,8 @@ "password": "Kata Sandi", "region": "Wilayah" }, - "description": "Masukkan alamat email dan kata sandi yang digunakan untuk masuk ke aplikasi seluler MyMazda.", - "title": "Mazda Connected Services - Tambahkan Akun" + "description": "Masukkan alamat email dan kata sandi yang digunakan untuk masuk ke aplikasi seluler MyMazda." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/it.json b/homeassistant/components/mazda/translations/it.json index 21d003b030b..920e2b5ce3c 100644 --- a/homeassistant/components/mazda/translations/it.json +++ b/homeassistant/components/mazda/translations/it.json @@ -17,10 +17,8 @@ "password": "Password", "region": "Area geografica" }, - "description": "Inserisci l'indirizzo email e la password che utilizzi per accedere all'app mobile MyMazda.", - "title": "Mazda Connected Services - Aggiungi account" + "description": "Inserisci l'indirizzo email e la password che utilizzi per accedere all'app mobile MyMazda." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/ja.json b/homeassistant/components/mazda/translations/ja.json index 3bf5b7f88b3..690f5a097de 100644 --- a/homeassistant/components/mazda/translations/ja.json +++ b/homeassistant/components/mazda/translations/ja.json @@ -17,10 +17,8 @@ "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" }, - "description": "MyMazda\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u306b\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u969b\u306b\u4f7f\u7528\u3059\u308b\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30de\u30c4\u30c0 \u30b3\u30cd\u30af\u30c6\u30c3\u30c9\u30b5\u30fc\u30d3\u30b9 - \u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u8ffd\u52a0" + "description": "MyMazda\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u306b\u30ed\u30b0\u30a4\u30f3\u3059\u308b\u969b\u306b\u4f7f\u7528\u3059\u308b\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3068\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } - }, - "title": "\u30de\u30c4\u30c0 \u30b3\u30cd\u30af\u30c6\u30c3\u30c9\u30b5\u30fc\u30d3\u30b9" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/ko.json b/homeassistant/components/mazda/translations/ko.json index 6023a053a7e..b95170c6bbd 100644 --- a/homeassistant/components/mazda/translations/ko.json +++ b/homeassistant/components/mazda/translations/ko.json @@ -17,10 +17,8 @@ "password": "\ube44\ubc00\ubc88\ud638", "region": "\uc9c0\uc5ed" }, - "description": "MyMazda \ubaa8\ubc14\uc77c \uc571\uc5d0 \ub85c\uadf8\uc778\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ud558\ub294 \uc774\uba54\uc77c \uc8fc\uc18c\uc640 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", - "title": "Mazda Connected Services - \uacc4\uc815 \ucd94\uac00\ud558\uae30" + "description": "MyMazda \ubaa8\ubc14\uc77c \uc571\uc5d0 \ub85c\uadf8\uc778\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ud558\ub294 \uc774\uba54\uc77c \uc8fc\uc18c\uc640 \ube44\ubc00\ubc88\ud638\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/nl.json b/homeassistant/components/mazda/translations/nl.json index 3370fe29bb8..f7532b9fc45 100644 --- a/homeassistant/components/mazda/translations/nl.json +++ b/homeassistant/components/mazda/translations/nl.json @@ -17,10 +17,8 @@ "password": "Wachtwoord", "region": "Regio" }, - "description": "Voer het e-mailadres en wachtwoord in dat u gebruikt om in te loggen op de MyMazda mobiele app.", - "title": "Mazda Connected Services - Account toevoegen" + "description": "Voer het e-mailadres en wachtwoord in dat u gebruikt om in te loggen op de MyMazda mobiele app." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/no.json b/homeassistant/components/mazda/translations/no.json index 3f4db47d2b0..875e21e8c04 100644 --- a/homeassistant/components/mazda/translations/no.json +++ b/homeassistant/components/mazda/translations/no.json @@ -17,10 +17,8 @@ "password": "Passord", "region": "Region" }, - "description": "Vennligst skriv inn e-postadressen og passordet du bruker for \u00e5 logge p\u00e5 MyMazda-mobilappen.", - "title": "Mazda Connected Services - Legg til konto" + "description": "Vennligst skriv inn e-postadressen og passordet du bruker for \u00e5 logge p\u00e5 MyMazda-mobilappen." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/pl.json b/homeassistant/components/mazda/translations/pl.json index fdd1a8c5ce9..d4aea82b6c1 100644 --- a/homeassistant/components/mazda/translations/pl.json +++ b/homeassistant/components/mazda/translations/pl.json @@ -17,10 +17,8 @@ "password": "Has\u0142o", "region": "Region" }, - "description": "Wprowad\u017a adres e-mail i has\u0142o, kt\u00f3rych u\u017cywasz do logowania si\u0119 do aplikacji mobilnej MyMazda.", - "title": "Mazda Connected Services - Dodawanie konta" + "description": "Wprowad\u017a adres e-mail i has\u0142o, kt\u00f3rych u\u017cywasz do logowania si\u0119 do aplikacji mobilnej MyMazda." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/pt-BR.json b/homeassistant/components/mazda/translations/pt-BR.json index 7b28450bfd0..5dcfd6d59ab 100644 --- a/homeassistant/components/mazda/translations/pt-BR.json +++ b/homeassistant/components/mazda/translations/pt-BR.json @@ -17,10 +17,8 @@ "password": "Senha", "region": "Regi\u00e3o" }, - "description": "Digite o endere\u00e7o de e-mail e senha que voc\u00ea usa para entrar no aplicativo MyMazda.", - "title": "Mazda Connected Services - Adicionar conta" + "description": "Digite o endere\u00e7o de e-mail e senha que voc\u00ea usa para entrar no aplicativo MyMazda." } } - }, - "title": "Servi\u00e7os conectados Mazda" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/ru.json b/homeassistant/components/mazda/translations/ru.json index cf949416274..4d5082bcc49 100644 --- a/homeassistant/components/mazda/translations/ru.json +++ b/homeassistant/components/mazda/translations/ru.json @@ -17,10 +17,8 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0438 \u043f\u0430\u0440\u043e\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 MyMazda.", - "title": "Mazda Connected Services" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0438 \u043f\u0430\u0440\u043e\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 MyMazda." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/tr.json b/homeassistant/components/mazda/translations/tr.json index 6c574d1de0b..788cee66b00 100644 --- a/homeassistant/components/mazda/translations/tr.json +++ b/homeassistant/components/mazda/translations/tr.json @@ -17,10 +17,8 @@ "password": "Parola", "region": "B\u00f6lge" }, - "description": "L\u00fctfen MyMazda mobil uygulamas\u0131na giri\u015f yapmak i\u00e7in kulland\u0131\u011f\u0131n\u0131z e-posta adresini ve \u015fifreyi giriniz.", - "title": "Mazda Connected Services - Hesap Ekle" + "description": "L\u00fctfen MyMazda mobil uygulamas\u0131na giri\u015f yapmak i\u00e7in kulland\u0131\u011f\u0131n\u0131z e-posta adresini ve \u015fifreyi giriniz." } } - }, - "title": "Mazda Connected Services" + } } \ No newline at end of file diff --git a/homeassistant/components/mazda/translations/zh-Hant.json b/homeassistant/components/mazda/translations/zh-Hant.json index 0c0e3f0fd8c..bb52f321766 100644 --- a/homeassistant/components/mazda/translations/zh-Hant.json +++ b/homeassistant/components/mazda/translations/zh-Hant.json @@ -17,10 +17,8 @@ "password": "\u5bc6\u78bc", "region": "\u5340\u57df" }, - "description": "\u8acb\u8f38\u5165\u767b\u5165MyMazda \u884c\u52d5 App \u4e4b Email \u5730\u5740\u8207\u5bc6\u78bc\u3002", - "title": "Mazda Connected \u670d\u52d9 - \u65b0\u589e\u5e33\u865f" + "description": "\u8acb\u8f38\u5165\u767b\u5165MyMazda \u884c\u52d5 App \u4e4b Email \u5730\u5740\u8207\u5bc6\u78bc\u3002" } } - }, - "title": "Mazda Connected \u670d\u52d9" + } } \ No newline at end of file diff --git a/homeassistant/components/meater/translations/bg.json b/homeassistant/components/meater/translations/bg.json new file mode 100644 index 00000000000..e5396fbc999 --- /dev/null +++ b/homeassistant/components/meater/translations/bg.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown_auth_error": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0430\u043a\u0430\u0443\u043d\u0442\u0430 \u0441\u0438 \u0432 Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ca.json b/homeassistant/components/meater/translations/ca.json new file mode 100644 index 00000000000..0174767bc52 --- /dev/null +++ b/homeassistant/components/meater/translations/ca.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "service_unavailable_error": "L'API no est\u00e0 disponible actualment, torna-ho a provar m\u00e9s tard.", + "unknown_auth_error": "Error inesperat" + }, + "step": { + "user": { + "data": { + "password": "Contrasenya", + "username": "Nom d'usuari" + }, + "description": "Configura el teu compte de Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/cs.json b/homeassistant/components/meater/translations/cs.json new file mode 100644 index 00000000000..72c98504526 --- /dev/null +++ b/homeassistant/components/meater/translations/cs.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", + "unknown_auth_error": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "password": "Heslo", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/de.json b/homeassistant/components/meater/translations/de.json new file mode 100644 index 00000000000..ed143e26b4c --- /dev/null +++ b/homeassistant/components/meater/translations/de.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "service_unavailable_error": "Die API ist derzeit nicht verf\u00fcgbar. Bitte versuche es sp\u00e4ter erneut.", + "unknown_auth_error": "Unerwarteter Fehler" + }, + "step": { + "user": { + "data": { + "password": "Passwort", + "username": "Benutzername" + }, + "description": "Richte dein Meater Cloud-Konto ein." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/el.json b/homeassistant/components/meater/translations/el.json new file mode 100644 index 00000000000..2d02110dd49 --- /dev/null +++ b/homeassistant/components/meater/translations/el.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "service_unavailable_error": "\u03a4\u03bf API \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf \u03c0\u03b1\u03c1\u03cc\u03bd \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03bf, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1.", + "unknown_auth_error": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "user": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/et.json b/homeassistant/components/meater/translations/et.json new file mode 100644 index 00000000000..55328467d3a --- /dev/null +++ b/homeassistant/components/meater/translations/et.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Tuvastamine nurjus", + "service_unavailable_error": "API pole praegu saadaval, proovi hiljem uuesti.", + "unknown_auth_error": "Ootamatu t\u00f5rge" + }, + "step": { + "user": { + "data": { + "password": "Salas\u00f5na", + "username": "Kasutajanimi" + }, + "description": "Seadista Meater Cloudi konto." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/fr.json b/homeassistant/components/meater/translations/fr.json new file mode 100644 index 00000000000..9940cb6e24b --- /dev/null +++ b/homeassistant/components/meater/translations/fr.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Authentification non valide", + "service_unavailable_error": "L'API est actuellement indisponible, veuillez r\u00e9essayer ult\u00e9rieurement.", + "unknown_auth_error": "Erreur inattendue" + }, + "step": { + "user": { + "data": { + "password": "Mot de passe", + "username": "Nom d'utilisateur" + }, + "description": "Configurez votre compte Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/he.json b/homeassistant/components/meater/translations/he.json new file mode 100644 index 00000000000..f1376b2cf0d --- /dev/null +++ b/homeassistant/components/meater/translations/he.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown_auth_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/hu.json b/homeassistant/components/meater/translations/hu.json new file mode 100644 index 00000000000..fd55bae3bdd --- /dev/null +++ b/homeassistant/components/meater/translations/hu.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "service_unavailable_error": "Az API jelenleg nem el\u00e9rhet\u0151, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", + "unknown_auth_error": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "user": { + "data": { + "password": "Jelsz\u00f3", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + }, + "description": "\u00c1ll\u00edtsa be a Meater Cloud fi\u00f3kj\u00e1t." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/id.json b/homeassistant/components/meater/translations/id.json new file mode 100644 index 00000000000..5d9e28c583c --- /dev/null +++ b/homeassistant/components/meater/translations/id.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Autentikasi tidak valid", + "service_unavailable_error": "API saat ini tidak tersedia, harap coba lagi nanti.", + "unknown_auth_error": "Kesalahan yang tidak diharapkan" + }, + "step": { + "user": { + "data": { + "password": "Kata Sandi", + "username": "Nama Pengguna" + }, + "description": "Siapkan akun Meater Cloud Anda." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/it.json b/homeassistant/components/meater/translations/it.json new file mode 100644 index 00000000000..fe3bc189ef6 --- /dev/null +++ b/homeassistant/components/meater/translations/it.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Autenticazione non valida", + "service_unavailable_error": "L'API non \u00e8 attualmente disponibile, riprova pi\u00f9 tardi.", + "unknown_auth_error": "Errore imprevisto" + }, + "step": { + "user": { + "data": { + "password": "Password", + "username": "Nome utente" + }, + "description": "Configura il tuo account Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ja.json b/homeassistant/components/meater/translations/ja.json new file mode 100644 index 00000000000..db4dd2e9c6b --- /dev/null +++ b/homeassistant/components/meater/translations/ja.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "service_unavailable_error": "\u73fe\u5728API\u304c\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093\u3002\u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", + "unknown_auth_error": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "user": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + }, + "description": "Meater Cloud\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/nl.json b/homeassistant/components/meater/translations/nl.json new file mode 100644 index 00000000000..a87175c7574 --- /dev/null +++ b/homeassistant/components/meater/translations/nl.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Ongeldige authenticatie", + "service_unavailable_error": "De API is momenteel niet beschikbaar, probeer het later opnieuw.", + "unknown_auth_error": "Onverwachte fout" + }, + "step": { + "user": { + "data": { + "password": "Wachtwoord", + "username": "Gebruikersnaam" + }, + "description": "Stel uw Meater Cloud-account in." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/no.json b/homeassistant/components/meater/translations/no.json new file mode 100644 index 00000000000..60bec4e6864 --- /dev/null +++ b/homeassistant/components/meater/translations/no.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Ugyldig godkjenning", + "service_unavailable_error": "API-en er for \u00f8yeblikket utilgjengelig, pr\u00f8v igjen senere.", + "unknown_auth_error": "Uventet feil" + }, + "step": { + "user": { + "data": { + "password": "Passord", + "username": "Brukernavn" + }, + "description": "Sett opp din Meater Cloud-konto." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/pl.json b/homeassistant/components/meater/translations/pl.json new file mode 100644 index 00000000000..1816069dd34 --- /dev/null +++ b/homeassistant/components/meater/translations/pl.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Niepoprawne uwierzytelnienie", + "service_unavailable_error": "Interfejs API jest obecnie niedost\u0119pny, spr\u00f3buj ponownie p\u00f3\u017aniej.", + "unknown_auth_error": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "password": "Has\u0142o", + "username": "Nazwa u\u017cytkownika" + }, + "description": "Skonfiguruj swoje konto w Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/pt-BR.json b/homeassistant/components/meater/translations/pt-BR.json new file mode 100644 index 00000000000..103f3f76986 --- /dev/null +++ b/homeassistant/components/meater/translations/pt-BR.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "service_unavailable_error": "A API est\u00e1 indispon\u00edvel no momento. Tente novamente mais tarde.", + "unknown_auth_error": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "password": "Senha", + "username": "Usu\u00e1rio" + }, + "description": "Configure sua conta Meeater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ru.json b/homeassistant/components/meater/translations/ru.json new file mode 100644 index 00000000000..a56f2f8ebbe --- /dev/null +++ b/homeassistant/components/meater/translations/ru.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "service_unavailable_error": "\u0412 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f API \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", + "unknown_auth_error": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Meater Cloud." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/tr.json b/homeassistant/components/meater/translations/tr.json new file mode 100644 index 00000000000..03f77d2ed51 --- /dev/null +++ b/homeassistant/components/meater/translations/tr.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "service_unavailable_error": "API \u015fu anda kullan\u0131lam\u0131yor, l\u00fctfen daha sonra tekrar deneyin.", + "unknown_auth_error": "Beklenmeyen hata" + }, + "step": { + "user": { + "data": { + "password": "Parola", + "username": "Kullan\u0131c\u0131 Ad\u0131" + }, + "description": "Meater Cloud hesab\u0131n\u0131z\u0131 kurun." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/zh-Hant.json b/homeassistant/components/meater/translations/zh-Hant.json new file mode 100644 index 00000000000..b04f4a54076 --- /dev/null +++ b/homeassistant/components/meater/translations/zh-Hant.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "service_unavailable_error": "API \u76ee\u524d\u7121\u6cd5\u4f7f\u7528\uff0c\u8acb\u7a0d\u5019\u518d\u8a66\u3002", + "unknown_auth_error": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "user": { + "data": { + "password": "\u5bc6\u78bc", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + }, + "description": "\u8a2d\u5b9a Meater Cloud \u5e33\u865f\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/media_player/translations/ca.json b/homeassistant/components/media_player/translations/ca.json index eede8911e5f..98ad4bb5a95 100644 --- a/homeassistant/components/media_player/translations/ca.json +++ b/homeassistant/components/media_player/translations/ca.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} s'est\u00e0 carregant en mem\u00f2ria", "is_idle": "{entity_name} est\u00e0 inactiu", "is_off": "{entity_name} est\u00e0 apagat", "is_on": "{entity_name} est\u00e0 enc\u00e8s", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} est\u00e0 reproduint" }, "trigger_type": { + "buffering": "{entity_name} comen\u00e7a a carregar-se en mem\u00f2ria", "changed_states": "{entity_name} canvia d'estat", "idle": "{entity_name} es torna inactiu", "paused": "{entity_name} est\u00e0 en pausa", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Carregant", "idle": "Inactiu", "off": "OFF", "on": "ON", diff --git a/homeassistant/components/media_player/translations/de.json b/homeassistant/components/media_player/translations/de.json index 653533ba03a..d6468920628 100644 --- a/homeassistant/components/media_player/translations/de.json +++ b/homeassistant/components/media_player/translations/de.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} puffert", "is_idle": "{entity_name} ist unt\u00e4tig", "is_off": "{entity_name} ist ausgeschaltet", "is_on": "{entity_name} ist eingeschaltet", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} spielt" }, "trigger_type": { + "buffering": "{entity_name} beginnt mit dem Puffern", "changed_states": "{entity_name} hat den Status ge\u00e4ndert", "idle": "{entity_name} wird inaktiv", "paused": "{entity_name} ist angehalten", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Puffern", "idle": "Unt\u00e4tig", "off": "Aus", "on": "An", diff --git a/homeassistant/components/media_player/translations/el.json b/homeassistant/components/media_player/translations/el.json index 242de3e829a..d7819069e26 100644 --- a/homeassistant/components/media_player/translations/el.json +++ b/homeassistant/components/media_player/translations/el.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u03be\u03b5\u03ba\u03b9\u03bd\u03ac\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ae \u03b1\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7", "is_idle": "{entity_name} \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03b4\u03c1\u03b1\u03bd\u03ad\u03c2", "is_off": "{entity_name} \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", "is_on": "{entity_name} \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} \u03c0\u03b1\u03af\u03b6\u03b5\u03b9" }, "trigger_type": { + "buffering": "{entity_name} \u03be\u03b5\u03ba\u03b9\u03bd\u03ac\u03b5\u03b9 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ae \u03b1\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7", "changed_states": "\u03a4\u03bf {entity_name} \u03ac\u03bb\u03bb\u03b1\u03be\u03b5 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2", "idle": "{entity_name} \u03b3\u03af\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b1\u03b4\u03c1\u03b1\u03bd\u03ad\u03c2", "paused": "{entity_name} \u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03b5 \u03c0\u03b1\u03cd\u03c3\u03b7", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u03a0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ae \u03b1\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7", "idle": "\u03a3\u03b5 \u03b1\u03b4\u03c1\u03ac\u03bd\u03b5\u03b9\u03b1", "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", "on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03cc", diff --git a/homeassistant/components/media_player/translations/et.json b/homeassistant/components/media_player/translations/et.json index 5461600c9d5..ae3293e951f 100644 --- a/homeassistant/components/media_player/translations/et.json +++ b/homeassistant/components/media_player/translations/et.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} puhverdab", "is_idle": "{entity_name} on j\u00f5udeolekus", "is_off": "{entity_name} on v\u00e4lja l\u00fclitatud", "is_on": "{entity_name} on sisse l\u00fclitatud", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} m\u00e4ngib" }, "trigger_type": { + "buffering": "{entity_name} alustab puhverdamist", "changed_states": "{entity_name} muutis olekut", "idle": "{entity_name} muutub j\u00f5udeolekusse", "paused": "{entity_name} on pausil", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Puhverdamine", "idle": "Ootel", "off": "V\u00e4ljas", "on": "Sees", diff --git a/homeassistant/components/media_player/translations/fr.json b/homeassistant/components/media_player/translations/fr.json index 17a6cbf92b3..d6c41bf5e09 100644 --- a/homeassistant/components/media_player/translations/fr.json +++ b/homeassistant/components/media_player/translations/fr.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} est en train de mettre en m\u00e9moire tampon", "is_idle": "{entity_name} est inactif", "is_off": "{entity_name} est d\u00e9sactiv\u00e9", "is_on": "{entity_name} est activ\u00e9", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} joue" }, "trigger_type": { + "buffering": "{entity_name} commence \u00e0 mettre en m\u00e9moire tampon", "changed_states": "{entity_name} a chang\u00e9 d'\u00e9tat", "idle": "{entity_name} devient inactif", "paused": "{entity_name} est mis en pause", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Mise en m\u00e9moire tampon", "idle": "Inactif", "off": "D\u00e9sactiv\u00e9", "on": "Activ\u00e9", diff --git a/homeassistant/components/media_player/translations/he.json b/homeassistant/components/media_player/translations/he.json index d4ba1b29bcb..645490856af 100644 --- a/homeassistant/components/media_player/translations/he.json +++ b/homeassistant/components/media_player/translations/he.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u05d0\u05d5\u05d2\u05e8", "is_idle": "{entity_name} \u05de\u05de\u05ea\u05d9\u05df", "is_off": "{entity_name} \u05db\u05d1\u05d5\u05d9", "is_on": "{entity_name} \u05e4\u05d5\u05e2\u05dc", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} \u05de\u05ea\u05e0\u05d2\u05df" }, "trigger_type": { + "buffering": "{entity_name} \u05de\u05ea\u05d7\u05d9\u05dc \u05dc\u05d0\u05d2\u05d5\u05e8", "changed_states": "{entity_name} \u05e9\u05d9\u05e0\u05d4 \u05de\u05e6\u05d1", "idle": "{entity_name} \u05d4\u05d5\u05e4\u05da \u05dc\u05de\u05de\u05ea\u05d9\u05df", "paused": "{entity_name} \u05de\u05d5\u05e9\u05d4\u05d4", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u05d0\u05d5\u05d2\u05e8", "idle": "\u05de\u05de\u05ea\u05d9\u05df", "off": "\u05db\u05d1\u05d5\u05d9", "on": "\u05de\u05d5\u05e4\u05e2\u05dc", diff --git a/homeassistant/components/media_player/translations/hu.json b/homeassistant/components/media_player/translations/hu.json index 83b5dc4e122..3a885c70b64 100644 --- a/homeassistant/components/media_player/translations/hu.json +++ b/homeassistant/components/media_player/translations/hu.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} pufferel", "is_idle": "{entity_name} t\u00e9tlen", "is_off": "{entity_name} ki van kapcsolva", "is_on": "{entity_name} be van kapcsolva", @@ -8,6 +9,8 @@ "is_playing": "{entity_name} lej\u00e1tszik" }, "trigger_type": { + "buffering": "{entity_name} pufferelni kezd", + "changed_states": "{entity_name} \u00e1llapota megv\u00e1ltozott", "idle": "{entity_name} t\u00e9tlenn\u00e9 v\u00e1lik", "paused": "{entity_name} sz\u00fcneteltetve van", "playing": "{entity_name} megkezdi a lej\u00e1tsz\u00e1st", @@ -17,6 +20,7 @@ }, "state": { "_": { + "buffering": "Pufferel\u00e9s", "idle": "T\u00e9tlen", "off": "Ki", "on": "Be", diff --git a/homeassistant/components/media_player/translations/id.json b/homeassistant/components/media_player/translations/id.json index 9446d1e9e89..469687f9110 100644 --- a/homeassistant/components/media_player/translations/id.json +++ b/homeassistant/components/media_player/translations/id.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} sedang buffering", "is_idle": "{entity_name} siaga", "is_off": "{entity_name} mati", "is_on": "{entity_name} nyala", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} sedang memutar" }, "trigger_type": { + "buffering": "{entity_name} mulai buffering", "changed_states": "{entity_name} mengubah status", "idle": "{entity_name} menjadi siaga", "paused": "{entity_name} dijeda", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Buffering", "idle": "Siaga", "off": "Mati", "on": "Nyala", diff --git a/homeassistant/components/media_player/translations/it.json b/homeassistant/components/media_player/translations/it.json index 7c075420aba..5fe897a9a3f 100644 --- a/homeassistant/components/media_player/translations/it.json +++ b/homeassistant/components/media_player/translations/it.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u00e8 in buffering", "is_idle": "{entity_name} \u00e8 inattivo", "is_off": "{entity_name} \u00e8 spento", "is_on": "{entity_name} \u00e8 acceso", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} \u00e8 in esecuzione" }, "trigger_type": { + "buffering": "{entity_name} avvia il buffering", "changed_states": "{entity_name} ha cambiato stato", "idle": "{entity_name} diventa inattivo", "paused": "{entity_name} \u00e8 in pausa", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Riempimento buffer", "idle": "Inattivo", "off": "Spento", "on": "Acceso", diff --git a/homeassistant/components/media_player/translations/nl.json b/homeassistant/components/media_player/translations/nl.json index 5fae215f4f9..23fe64d452a 100644 --- a/homeassistant/components/media_player/translations/nl.json +++ b/homeassistant/components/media_player/translations/nl.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} is aan het bufferen", "is_idle": "{entity_name} is niet actief", "is_off": "{entity_name} is uitgeschakeld", "is_on": "{entity_name} is ingeschakeld", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} wordt afgespeeld" }, "trigger_type": { + "buffering": "{entity_name} start met bufferen", "changed_states": "{entity_name} veranderde van status", "idle": "{entity_name} wordt inactief", "paused": "{entity_name} is gepauzeerd", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Bufferen", "idle": "Inactief", "off": "Uit", "on": "Aan", diff --git a/homeassistant/components/media_player/translations/no.json b/homeassistant/components/media_player/translations/no.json index c51920a67bd..d7dd387b4e3 100644 --- a/homeassistant/components/media_player/translations/no.json +++ b/homeassistant/components/media_player/translations/no.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} bufre", "is_idle": "{entity_name} er inaktiv", "is_off": "{entity_name} er sl\u00e5tt av", "is_on": "{entity_name} er sl\u00e5tt p\u00e5", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} spiller n\u00e5" }, "trigger_type": { + "buffering": "{entity_name} starter bufring", "changed_states": "{entity_name} endret tilstander", "idle": "{entity_name} blir inaktiv", "paused": "{entity_name} er satt p\u00e5 pause", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Bufring", "idle": "Inaktiv", "off": "Av", "on": "P\u00e5", diff --git a/homeassistant/components/media_player/translations/pl.json b/homeassistant/components/media_player/translations/pl.json index 08c664a909d..886cb07f93f 100644 --- a/homeassistant/components/media_player/translations/pl.json +++ b/homeassistant/components/media_player/translations/pl.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} buforuje", "is_idle": "odtwarzacz {entity_name} jest nieaktywny", "is_off": "odtwarzacz {entity_name} jest wy\u0142\u0105czony", "is_on": "odtwarzacz {entity_name} jest w\u0142\u0105czony", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} odtwarza media" }, "trigger_type": { + "buffering": "{entity_name} rozpocznie buforowanie", "changed_states": "{entity_name} zmieni\u0142o stan", "idle": "odtwarzacz {entity_name} stanie si\u0119 bezczynny", "paused": "odtwarzacz {entity_name} zostanie wstrzymany", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Buforowanie", "idle": "nieaktywny", "off": "wy\u0142.", "on": "w\u0142.", diff --git a/homeassistant/components/media_player/translations/pt-BR.json b/homeassistant/components/media_player/translations/pt-BR.json index 147f66ec0e7..332d4d0bcb4 100644 --- a/homeassistant/components/media_player/translations/pt-BR.json +++ b/homeassistant/components/media_player/translations/pt-BR.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} est\u00e1 em buffer", "is_idle": "{entity_name} est\u00e1 ocioso", "is_off": "{entity_name} est\u00e1 desligado", "is_on": "{entity_name} est\u00e1 ligado", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} est\u00e1 reproduzindo" }, "trigger_type": { + "buffering": "{entity_name} inicia o armazenamento em buffer", "changed_states": "{entity_name} ligado ou desligado", "idle": "{entity_name} ficar ocioso", "paused": "{entity_name} for pausado", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "Carregando", "idle": "Ocioso", "off": "Desligado", "on": "Ligado", diff --git a/homeassistant/components/media_player/translations/ru.json b/homeassistant/components/media_player/translations/ru.json index 74a4fd3fbbd..bb531479c32 100644 --- a/homeassistant/components/media_player/translations/ru.json +++ b/homeassistant/components/media_player/translations/ru.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044e", "is_idle": "{entity_name} \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f", "is_off": "{entity_name} \u0432 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438", "is_on": "{entity_name} \u0432\u043e \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442 \u043c\u0435\u0434\u0438\u0430" }, "trigger_type": { + "buffering": "{entity_name} \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044e", "changed_states": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435", "idle": "{entity_name} \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u0432 \u0440\u0435\u0436\u0438\u043c \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f", "paused": "{entity_name} \u043d\u0430 \u043f\u0430\u0443\u0437\u0435", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044f", "idle": "\u0411\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435", "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d", "on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d", diff --git a/homeassistant/components/media_player/translations/zh-Hant.json b/homeassistant/components/media_player/translations/zh-Hant.json index b4e8442ea8d..99e55cc328c 100644 --- a/homeassistant/components/media_player/translations/zh-Hant.json +++ b/homeassistant/components/media_player/translations/zh-Hant.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u7de9\u885d\u4e2d", "is_idle": "{entity_name}\u9592\u7f6e", "is_off": "{entity_name}\u95dc\u9589", "is_on": "{entity_name}\u958b\u555f", @@ -8,6 +9,7 @@ "is_playing": "{entity_name}\u6b63\u5728\u64ad\u653e" }, "trigger_type": { + "buffering": "{entity_name} \u958b\u59cb\u7de9\u885d", "changed_states": "{entity_name}\u5df2\u8b8a\u66f4\u72c0\u614b", "idle": "{entity_name}\u8b8a\u6210\u9592\u7f6e", "paused": "{entity_name}\u5df2\u66ab\u505c", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u7de9\u885d", "idle": "\u9592\u7f6e", "off": "\u95dc\u9589", "on": "\u958b\u555f", diff --git a/homeassistant/components/met/translations/hu.json b/homeassistant/components/met/translations/hu.json index 38c84a3f8dc..ffa19641cd1 100644 --- a/homeassistant/components/met/translations/hu.json +++ b/homeassistant/components/met/translations/hu.json @@ -12,7 +12,7 @@ "elevation": "Magass\u00e1g", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Meteorol\u00f3giai int\u00e9zet", "title": "Elhelyezked\u00e9s" diff --git a/homeassistant/components/met_eireann/translations/hu.json b/homeassistant/components/met_eireann/translations/hu.json index b70aa2dcf67..8ce232f26a9 100644 --- a/homeassistant/components/met_eireann/translations/hu.json +++ b/homeassistant/components/met_eireann/translations/hu.json @@ -9,7 +9,7 @@ "elevation": "Magass\u00e1g", "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Adja meg tart\u00f3zkod\u00e1si hely\u00e9t a Met \u00c9ireann Public Weather Forecast API id\u0151j\u00e1r\u00e1si adatainak haszn\u00e1lat\u00e1hoz", "title": "Elhelyezked\u00e9s" diff --git a/homeassistant/components/meteo_france/translations/bg.json b/homeassistant/components/meteo_france/translations/bg.json index c426ae62387..d4a3ab09c9d 100644 --- a/homeassistant/components/meteo_france/translations/bg.json +++ b/homeassistant/components/meteo_france/translations/bg.json @@ -9,14 +9,12 @@ "data": { "city": "\u0413\u0440\u0430\u0434" }, - "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u044f \u0433\u0440\u0430\u0434 \u043e\u0442 \u0441\u043f\u0438\u0441\u044a\u043a\u0430", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u044f \u0433\u0440\u0430\u0434 \u043e\u0442 \u0441\u043f\u0438\u0441\u044a\u043a\u0430" }, "user": { "data": { "city": "\u0413\u0440\u0430\u0434" - }, - "title": "M\u00e9t\u00e9o-France" + } } } } diff --git a/homeassistant/components/meteo_france/translations/ca.json b/homeassistant/components/meteo_france/translations/ca.json index 3fb28025f99..af3ee56159c 100644 --- a/homeassistant/components/meteo_france/translations/ca.json +++ b/homeassistant/components/meteo_france/translations/ca.json @@ -12,15 +12,13 @@ "data": { "city": "Ciutat" }, - "description": "Tria una ciutat de la llista", - "title": "M\u00e9t\u00e9o-France" + "description": "Tria una ciutat de la llista" }, "user": { "data": { "city": "Ciutat" }, - "description": "Introdueix el codi postal (nom\u00e9s recomanat per Fran\u00e7a) o nom de la ciutat", - "title": "M\u00e9t\u00e9o-France" + "description": "Introdueix el codi postal (nom\u00e9s recomanat per Fran\u00e7a) o nom de la ciutat" } } }, @@ -28,7 +26,7 @@ "step": { "init": { "data": { - "mode": "Mode de predicci\u00f3" + "mode": "Mode previsi\u00f3" } } } diff --git a/homeassistant/components/meteo_france/translations/cs.json b/homeassistant/components/meteo_france/translations/cs.json index d1c8f632220..0f05ce14ffc 100644 --- a/homeassistant/components/meteo_france/translations/cs.json +++ b/homeassistant/components/meteo_france/translations/cs.json @@ -12,15 +12,13 @@ "data": { "city": "M\u011bsto" }, - "description": "Vyberte m\u011bsto ze seznamu", - "title": "M\u00e9t\u00e9o-France" + "description": "Vyberte m\u011bsto ze seznamu" }, "user": { "data": { "city": "M\u011bsto" }, - "description": "Zadejte PS\u010c (pouze pro Francii, doporu\u010deno) nebo jm\u00e9no m\u011bsta.", - "title": "M\u00e9t\u00e9o-France" + "description": "Zadejte PS\u010c (pouze pro Francii, doporu\u010deno) nebo jm\u00e9no m\u011bsta." } } }, diff --git a/homeassistant/components/meteo_france/translations/da.json b/homeassistant/components/meteo_france/translations/da.json index d0fe7cba892..35c26b38083 100644 --- a/homeassistant/components/meteo_france/translations/da.json +++ b/homeassistant/components/meteo_france/translations/da.json @@ -9,8 +9,7 @@ "data": { "city": "By" }, - "description": "Indtast postnummer (kun for Frankrig, anbefalet) eller bynavn", - "title": "M\u00e9t\u00e9o-France" + "description": "Indtast postnummer (kun for Frankrig, anbefalet) eller bynavn" } } } diff --git a/homeassistant/components/meteo_france/translations/de.json b/homeassistant/components/meteo_france/translations/de.json index 04d038cb65d..ae0d7ab2f14 100644 --- a/homeassistant/components/meteo_france/translations/de.json +++ b/homeassistant/components/meteo_france/translations/de.json @@ -12,15 +12,13 @@ "data": { "city": "Stadt" }, - "description": "W\u00e4hle deine Stadt aus der Liste", - "title": "M\u00e9t\u00e9o-France" + "description": "W\u00e4hle deine Stadt aus der Liste" }, "user": { "data": { "city": "Stadt" }, - "description": "Gib die Postleitzahl (nur f\u00fcr Frankreich empfohlen) oder den St\u00e4dtenamen ein", - "title": "M\u00e9t\u00e9o-France" + "description": "Gib die Postleitzahl (nur f\u00fcr Frankreich empfohlen) oder den St\u00e4dtenamen ein" } } }, diff --git a/homeassistant/components/meteo_france/translations/el.json b/homeassistant/components/meteo_france/translations/el.json index 3dfad5a493d..038279abece 100644 --- a/homeassistant/components/meteo_france/translations/el.json +++ b/homeassistant/components/meteo_france/translations/el.json @@ -12,15 +12,13 @@ "data": { "city": "\u03a0\u03cc\u03bb\u03b7" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03cc\u03bb\u03b7 \u03c3\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03cc\u03bb\u03b7 \u03c3\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03bb\u03af\u03c3\u03c4\u03b1" }, "user": { "data": { "city": "\u03a0\u03cc\u03bb\u03b7" }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 (\u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u0393\u03b1\u03bb\u03bb\u03af\u03b1, \u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9) \u03ae \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c0\u03cc\u03bb\u03b7\u03c2.", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 (\u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c4\u03b7 \u0393\u03b1\u03bb\u03bb\u03af\u03b1, \u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9) \u03ae \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c0\u03cc\u03bb\u03b7\u03c2." } } }, diff --git a/homeassistant/components/meteo_france/translations/en.json b/homeassistant/components/meteo_france/translations/en.json index b1c4a7c0216..96302d7105b 100644 --- a/homeassistant/components/meteo_france/translations/en.json +++ b/homeassistant/components/meteo_france/translations/en.json @@ -12,15 +12,13 @@ "data": { "city": "City" }, - "description": "Choose your city from the list", - "title": "M\u00e9t\u00e9o-France" + "description": "Choose your city from the list" }, "user": { "data": { "city": "City" }, - "description": "Enter the postal code (only for France, recommended) or city name", - "title": "M\u00e9t\u00e9o-France" + "description": "Enter the postal code (only for France, recommended) or city name" } } }, diff --git a/homeassistant/components/meteo_france/translations/es-419.json b/homeassistant/components/meteo_france/translations/es-419.json index 471b965a824..e2c2feb5ed0 100644 --- a/homeassistant/components/meteo_france/translations/es-419.json +++ b/homeassistant/components/meteo_france/translations/es-419.json @@ -9,8 +9,7 @@ "data": { "city": "Ciudad" }, - "description": "Ingrese el c\u00f3digo postal (solo para Francia, recomendado) o el nombre de la ciudad", - "title": "M\u00e9t\u00e9o-Francia" + "description": "Ingrese el c\u00f3digo postal (solo para Francia, recomendado) o el nombre de la ciudad" } } } diff --git a/homeassistant/components/meteo_france/translations/es.json b/homeassistant/components/meteo_france/translations/es.json index 31d221eba09..8843d301779 100644 --- a/homeassistant/components/meteo_france/translations/es.json +++ b/homeassistant/components/meteo_france/translations/es.json @@ -12,15 +12,13 @@ "data": { "city": "Ciudad" }, - "description": "Elige tu ciudad de la lista", - "title": "M\u00e9t\u00e9o-France" + "description": "Elige tu ciudad de la lista" }, "user": { "data": { "city": "Ciudad" }, - "description": "Introduzca el c\u00f3digo postal (solo para Francia, recomendado) o el nombre de la ciudad", - "title": "M\u00e9t\u00e9o-France" + "description": "Introduzca el c\u00f3digo postal (solo para Francia, recomendado) o el nombre de la ciudad" } } }, diff --git a/homeassistant/components/meteo_france/translations/et.json b/homeassistant/components/meteo_france/translations/et.json index c23d2107f54..98c739fbcff 100644 --- a/homeassistant/components/meteo_france/translations/et.json +++ b/homeassistant/components/meteo_france/translations/et.json @@ -12,15 +12,13 @@ "data": { "city": "Linn" }, - "description": "Vali loendist oma linn", - "title": "" + "description": "Vali loendist oma linn" }, "user": { "data": { "city": "Linn" }, - "description": "Sisesta sihtnumber (ainult Prantsusmaa, soovitatav) v\u00f5i linna nimi", - "title": "" + "description": "Sisesta sihtnumber (ainult Prantsusmaa, soovitatav) v\u00f5i linna nimi" } } }, diff --git a/homeassistant/components/meteo_france/translations/fr.json b/homeassistant/components/meteo_france/translations/fr.json index 1121dd02b17..a2c543ee1fa 100644 --- a/homeassistant/components/meteo_france/translations/fr.json +++ b/homeassistant/components/meteo_france/translations/fr.json @@ -12,15 +12,13 @@ "data": { "city": "Ville" }, - "description": "Choisissez votre ville dans la liste", - "title": "M\u00e9t\u00e9o-France" + "description": "Choisissez votre ville dans la liste" }, "user": { "data": { "city": "Ville" }, - "description": "Entrez le code postal (uniquement pour la France, recommand\u00e9) ou le nom de la ville", - "title": "M\u00e9t\u00e9o-France" + "description": "Entrez le code postal (uniquement pour la France, recommand\u00e9) ou le nom de la ville" } } }, diff --git a/homeassistant/components/meteo_france/translations/hu.json b/homeassistant/components/meteo_france/translations/hu.json index 8034f6d0586..b9c2485a30b 100644 --- a/homeassistant/components/meteo_france/translations/hu.json +++ b/homeassistant/components/meteo_france/translations/hu.json @@ -12,15 +12,13 @@ "data": { "city": "V\u00e1ros" }, - "description": "V\u00e1lassza ki a v\u00e1rost a list\u00e1b\u00f3l", - "title": "M\u00e9t\u00e9o-France" + "description": "V\u00e1lassza ki a v\u00e1rost a list\u00e1b\u00f3l" }, "user": { "data": { "city": "V\u00e1ros" }, - "description": "\u00cdrja be az ir\u00e1ny\u00edt\u00f3sz\u00e1mot (csak Franciaorsz\u00e1g eset\u00e9ben aj\u00e1nlott) vagy a v\u00e1ros nev\u00e9t", - "title": "M\u00e9t\u00e9o-France" + "description": "\u00cdrja be az ir\u00e1ny\u00edt\u00f3sz\u00e1mot (csak Franciaorsz\u00e1g eset\u00e9ben aj\u00e1nlott) vagy a v\u00e1ros nev\u00e9t" } } }, diff --git a/homeassistant/components/meteo_france/translations/id.json b/homeassistant/components/meteo_france/translations/id.json index 07d8450e873..5c7d90710b5 100644 --- a/homeassistant/components/meteo_france/translations/id.json +++ b/homeassistant/components/meteo_france/translations/id.json @@ -12,15 +12,13 @@ "data": { "city": "Kota" }, - "description": "Pilih kota Anda dari daftar", - "title": "M\u00e9t\u00e9o-France" + "description": "Pilih kota Anda dari daftar" }, "user": { "data": { "city": "Kota" }, - "description": "Masukkan kode pos (hanya untuk Prancis, disarankan) atau nama kota", - "title": "M\u00e9t\u00e9o-France" + "description": "Masukkan kode pos (hanya untuk Prancis, disarankan) atau nama kota" } } }, diff --git a/homeassistant/components/meteo_france/translations/it.json b/homeassistant/components/meteo_france/translations/it.json index 37588fb4a32..78bad9d7dfd 100644 --- a/homeassistant/components/meteo_france/translations/it.json +++ b/homeassistant/components/meteo_france/translations/it.json @@ -12,15 +12,13 @@ "data": { "city": "Citt\u00e0" }, - "description": "Scegli la tua citt\u00e0 dall'elenco", - "title": "M\u00e9t\u00e9o-France" + "description": "Scegli la tua citt\u00e0 dall'elenco" }, "user": { "data": { "city": "Citt\u00e0" }, - "description": "Inserisci il codice postale (solo per la Francia, consigliato) o il nome della citt\u00e0", - "title": "M\u00e9t\u00e9o-France" + "description": "Inserisci il codice postale (solo per la Francia, consigliato) o il nome della citt\u00e0" } } }, diff --git a/homeassistant/components/meteo_france/translations/ja.json b/homeassistant/components/meteo_france/translations/ja.json index 2fa1f60225e..8e6b37cd53d 100644 --- a/homeassistant/components/meteo_france/translations/ja.json +++ b/homeassistant/components/meteo_france/translations/ja.json @@ -12,15 +12,13 @@ "data": { "city": "\u90fd\u5e02" }, - "description": "\u30ea\u30b9\u30c8\u304b\u3089\u3042\u306a\u305f\u306e\u90fd\u5e02\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "M\u00e9t\u00e9o-France" + "description": "\u30ea\u30b9\u30c8\u304b\u3089\u3042\u306a\u305f\u306e\u90fd\u5e02\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" }, "user": { "data": { "city": "\u90fd\u5e02" }, - "description": "\u90f5\u4fbf\u756a\u53f7(\u30d5\u30e9\u30f3\u30b9\u306e\u307f\u3001\u63a8\u5968) \u307e\u305f\u306f\u3001\u5e02\u533a\u753a\u6751\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "M\u00e9t\u00e9o-France" + "description": "\u90f5\u4fbf\u756a\u53f7(\u30d5\u30e9\u30f3\u30b9\u306e\u307f\u3001\u63a8\u5968) \u307e\u305f\u306f\u3001\u5e02\u533a\u753a\u6751\u540d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044" } } }, diff --git a/homeassistant/components/meteo_france/translations/ko.json b/homeassistant/components/meteo_france/translations/ko.json index 83cda0e4dcf..f977d7d6915 100644 --- a/homeassistant/components/meteo_france/translations/ko.json +++ b/homeassistant/components/meteo_france/translations/ko.json @@ -12,15 +12,13 @@ "data": { "city": "\ub3c4\uc2dc" }, - "description": "\ubaa9\ub85d\uc5d0\uc11c \ub3c4\uc2dc\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694", - "title": "\ud504\ub791\uc2a4 \uae30\uc0c1\uccad (M\u00e9t\u00e9o-France)" + "description": "\ubaa9\ub85d\uc5d0\uc11c \ub3c4\uc2dc\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694" }, "user": { "data": { "city": "\ub3c4\uc2dc" }, - "description": "\uc6b0\ud3b8\ubc88\ud638 (\ud504\ub791\uc2a4) \ub610\ub294 \ub3c4\uc2dc \uc774\ub984\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694", - "title": "\ud504\ub791\uc2a4 \uae30\uc0c1\uccad (M\u00e9t\u00e9o-France)" + "description": "\uc6b0\ud3b8\ubc88\ud638 (\ud504\ub791\uc2a4) \ub610\ub294 \ub3c4\uc2dc \uc774\ub984\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694" } } }, diff --git a/homeassistant/components/meteo_france/translations/lb.json b/homeassistant/components/meteo_france/translations/lb.json index 310d53b1d8f..ea204d6092e 100644 --- a/homeassistant/components/meteo_france/translations/lb.json +++ b/homeassistant/components/meteo_france/translations/lb.json @@ -12,15 +12,13 @@ "data": { "city": "Stad" }, - "description": "Wiel deng Stad aus der L\u00ebscht aus", - "title": "M\u00e9t\u00e9o-France" + "description": "Wiel deng Stad aus der L\u00ebscht aus" }, "user": { "data": { "city": "Stad" }, - "description": "Gitt de Postcode an (n\u00ebmme fir Frankr\u00e4ich, recommand\u00e9iert) oder den Numm vun der Stad", - "title": "M\u00e9t\u00e9o-France" + "description": "Gitt de Postcode an (n\u00ebmme fir Frankr\u00e4ich, recommand\u00e9iert) oder den Numm vun der Stad" } } }, diff --git a/homeassistant/components/meteo_france/translations/nl.json b/homeassistant/components/meteo_france/translations/nl.json index 11b0f567776..5e36e0585c7 100644 --- a/homeassistant/components/meteo_france/translations/nl.json +++ b/homeassistant/components/meteo_france/translations/nl.json @@ -12,15 +12,13 @@ "data": { "city": "Stad" }, - "description": "Kies uw stad uit de lijst", - "title": "M\u00e9t\u00e9o-France" + "description": "Kies uw stad uit de lijst" }, "user": { "data": { "city": "Stad" }, - "description": "Vul de postcode (alleen voor Frankrijk, aanbevolen) of de plaatsnaam in", - "title": "M\u00e9t\u00e9o-France" + "description": "Vul de postcode (alleen voor Frankrijk, aanbevolen) of de plaatsnaam in" } } }, diff --git a/homeassistant/components/meteo_france/translations/no.json b/homeassistant/components/meteo_france/translations/no.json index e323d5426da..dea58523260 100644 --- a/homeassistant/components/meteo_france/translations/no.json +++ b/homeassistant/components/meteo_france/translations/no.json @@ -12,15 +12,13 @@ "data": { "city": "By" }, - "description": "Velg din by fra listen", - "title": "" + "description": "Velg din by fra listen" }, "user": { "data": { "city": "By" }, - "description": "Fyll inn postnummeret (bare for Frankrike, anbefalt) eller bynavn", - "title": "" + "description": "Fyll inn postnummeret (bare for Frankrike, anbefalt) eller bynavn" } } }, diff --git a/homeassistant/components/meteo_france/translations/pl.json b/homeassistant/components/meteo_france/translations/pl.json index 0030aa83865..c826a92ac41 100644 --- a/homeassistant/components/meteo_france/translations/pl.json +++ b/homeassistant/components/meteo_france/translations/pl.json @@ -12,15 +12,13 @@ "data": { "city": "Miasto" }, - "description": "Wybierz swoje miasto z listy", - "title": "M\u00e9t\u00e9o-France" + "description": "Wybierz swoje miasto z listy" }, "user": { "data": { "city": "Miasto" }, - "description": "Wprowad\u017a kod pocztowy (tylko dla Francji, zalecane) lub nazw\u0119 miasta", - "title": "M\u00e9t\u00e9o-France" + "description": "Wprowad\u017a kod pocztowy (tylko dla Francji, zalecane) lub nazw\u0119 miasta" } } }, diff --git a/homeassistant/components/meteo_france/translations/pt-BR.json b/homeassistant/components/meteo_france/translations/pt-BR.json index 456c6eef17c..9d5bb35f586 100644 --- a/homeassistant/components/meteo_france/translations/pt-BR.json +++ b/homeassistant/components/meteo_france/translations/pt-BR.json @@ -12,15 +12,13 @@ "data": { "city": "Cidade" }, - "description": "Escolha sua cidade na lista", - "title": "M\u00e9t\u00e9o-France" + "description": "Escolha sua cidade na lista" }, "user": { "data": { "city": "Cidade" }, - "description": "Insira o c\u00f3digo postal (somente para a Fran\u00e7a, recomendado) ou o nome da cidade", - "title": "M\u00e9t\u00e9o-France" + "description": "Insira o c\u00f3digo postal (somente para a Fran\u00e7a, recomendado) ou o nome da cidade" } } }, diff --git a/homeassistant/components/meteo_france/translations/pt.json b/homeassistant/components/meteo_france/translations/pt.json index f53975ecf00..d059033bdcb 100644 --- a/homeassistant/components/meteo_france/translations/pt.json +++ b/homeassistant/components/meteo_france/translations/pt.json @@ -8,8 +8,7 @@ "user": { "data": { "city": "Cidade" - }, - "title": "" + } } } } diff --git a/homeassistant/components/meteo_france/translations/ru.json b/homeassistant/components/meteo_france/translations/ru.json index 6f42dae3b49..6ef7a82849e 100644 --- a/homeassistant/components/meteo_france/translations/ru.json +++ b/homeassistant/components/meteo_france/translations/ru.json @@ -12,15 +12,13 @@ "data": { "city": "\u0413\u043e\u0440\u043e\u0434" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u043e\u0440\u043e\u0434 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u043e\u0440\u043e\u0434 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430" }, "user": { "data": { "city": "\u0413\u043e\u0440\u043e\u0434" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0424\u0440\u0430\u043d\u0446\u0438\u0438) \u0438\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0433\u043e\u0440\u043e\u0434\u0430", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0424\u0440\u0430\u043d\u0446\u0438\u0438) \u0438\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0433\u043e\u0440\u043e\u0434\u0430" } } }, diff --git a/homeassistant/components/meteo_france/translations/sl.json b/homeassistant/components/meteo_france/translations/sl.json index 912748ada8f..6acfeb28e08 100644 --- a/homeassistant/components/meteo_france/translations/sl.json +++ b/homeassistant/components/meteo_france/translations/sl.json @@ -9,8 +9,7 @@ "data": { "city": "Mesto" }, - "description": "Vnesite po\u0161tno \u0161tevilko (samo za Francijo) ali ime mesta", - "title": "M\u00e9t\u00e9o-France" + "description": "Vnesite po\u0161tno \u0161tevilko (samo za Francijo) ali ime mesta" } } } diff --git a/homeassistant/components/meteo_france/translations/sv.json b/homeassistant/components/meteo_france/translations/sv.json index 70fb537fc82..f7f1a68478f 100644 --- a/homeassistant/components/meteo_france/translations/sv.json +++ b/homeassistant/components/meteo_france/translations/sv.json @@ -14,8 +14,7 @@ "data": { "city": "Stad" }, - "description": "Ange postnumret (endast f\u00f6r Frankrike, rekommenderat) eller ortsnamn", - "title": "M\u00e9t\u00e9o-France" + "description": "Ange postnumret (endast f\u00f6r Frankrike, rekommenderat) eller ortsnamn" } } } diff --git a/homeassistant/components/meteo_france/translations/tr.json b/homeassistant/components/meteo_france/translations/tr.json index 4793dbb4fe7..38e60b14b8e 100644 --- a/homeassistant/components/meteo_france/translations/tr.json +++ b/homeassistant/components/meteo_france/translations/tr.json @@ -12,15 +12,13 @@ "data": { "city": "\u015eehir" }, - "description": "Listeden \u015fehrinizi se\u00e7in", - "title": "M\u00e9t\u00e9o-Fransa" + "description": "Listeden \u015fehrinizi se\u00e7in" }, "user": { "data": { "city": "\u015eehir" }, - "description": "Posta kodunu (yaln\u0131zca Fransa i\u00e7in, \u00f6nerilir) veya \u015fehir ad\u0131n\u0131 girin", - "title": "M\u00e9t\u00e9o-Fransa" + "description": "Posta kodunu (yaln\u0131zca Fransa i\u00e7in, \u00f6nerilir) veya \u015fehir ad\u0131n\u0131 girin" } } }, diff --git a/homeassistant/components/meteo_france/translations/uk.json b/homeassistant/components/meteo_france/translations/uk.json index a84c230e218..124aacb9d67 100644 --- a/homeassistant/components/meteo_france/translations/uk.json +++ b/homeassistant/components/meteo_france/translations/uk.json @@ -12,15 +12,13 @@ "data": { "city": "\u041c\u0456\u0441\u0442\u043e" }, - "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043c\u0456\u0441\u0442\u043e \u0437\u0456 \u0441\u043f\u0438\u0441\u043a\u0443", - "title": "M\u00e9t\u00e9o-France" + "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043c\u0456\u0441\u0442\u043e \u0437\u0456 \u0441\u043f\u0438\u0441\u043a\u0443" }, "user": { "data": { "city": "\u041c\u0456\u0441\u0442\u043e" }, - "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441 (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0454\u0442\u044c\u0441\u044f \u0442\u0456\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u0424\u0440\u0430\u043d\u0446\u0456\u0457) \u0430\u0431\u043e \u043d\u0430\u0437\u0432\u0443 \u043c\u0456\u0441\u0442\u0430", - "title": "M\u00e9t\u00e9o-France" + "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441 (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0454\u0442\u044c\u0441\u044f \u0442\u0456\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u0424\u0440\u0430\u043d\u0446\u0456\u0457) \u0430\u0431\u043e \u043d\u0430\u0437\u0432\u0443 \u043c\u0456\u0441\u0442\u0430" } } }, diff --git a/homeassistant/components/meteo_france/translations/zh-Hant.json b/homeassistant/components/meteo_france/translations/zh-Hant.json index 32d92956d73..2f1aa7fe89e 100644 --- a/homeassistant/components/meteo_france/translations/zh-Hant.json +++ b/homeassistant/components/meteo_france/translations/zh-Hant.json @@ -12,15 +12,13 @@ "data": { "city": "\u57ce\u5e02\u540d\u7a31" }, - "description": "\u7531\u5217\u8868\u4e2d\u9078\u64c7\u57ce\u5e02", - "title": "M\u00e9t\u00e9o-France" + "description": "\u7531\u5217\u8868\u4e2d\u9078\u64c7\u57ce\u5e02" }, "user": { "data": { "city": "\u57ce\u5e02\u540d\u7a31" }, - "description": "\u8f38\u5165\u90f5\u905e\u5340\u865f\uff08\u50c5\u652f\u63f4\u6cd5\u570b\uff09\u6216\u57ce\u5e02\u540d\u7a31", - "title": "M\u00e9t\u00e9o-France" + "description": "\u8f38\u5165\u90f5\u905e\u5340\u865f\uff08\u50c5\u652f\u63f4\u6cd5\u570b\uff09\u6216\u57ce\u5e02\u540d\u7a31" } } }, diff --git a/homeassistant/components/meteoclimatic/translations/bg.json b/homeassistant/components/meteoclimatic/translations/bg.json index 63b0e7be8b7..4bac01649f7 100644 --- a/homeassistant/components/meteoclimatic/translations/bg.json +++ b/homeassistant/components/meteoclimatic/translations/bg.json @@ -6,11 +6,6 @@ }, "error": { "not_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430" - }, - "step": { - "user": { - "title": "Meteoclimatic" - } } } } \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/ca.json b/homeassistant/components/meteoclimatic/translations/ca.json index 5f672d87535..e7d760253e7 100644 --- a/homeassistant/components/meteoclimatic/translations/ca.json +++ b/homeassistant/components/meteoclimatic/translations/ca.json @@ -12,8 +12,9 @@ "data": { "code": "Codi d'estaci\u00f3" }, - "description": "Introdueix el codi d'estaci\u00f3 meteorol\u00f2gica (per exemple, ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "T\u00e9 el format com ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/de.json b/homeassistant/components/meteoclimatic/translations/de.json index c9b9ea90e61..7365dba674d 100644 --- a/homeassistant/components/meteoclimatic/translations/de.json +++ b/homeassistant/components/meteoclimatic/translations/de.json @@ -12,8 +12,9 @@ "data": { "code": "Stationscode" }, - "description": "Gib den Code der Meteoclimatic-Station ein (z. B. ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Sieht aus wie ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/el.json b/homeassistant/components/meteoclimatic/translations/el.json index 085e6fe1643..01854a07830 100644 --- a/homeassistant/components/meteoclimatic/translations/el.json +++ b/homeassistant/components/meteoclimatic/translations/el.json @@ -12,8 +12,9 @@ "data": { "code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd" }, - "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd Meteoclimatic (\u03c0.\u03c7. ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "\u039c\u03bf\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/en.json b/homeassistant/components/meteoclimatic/translations/en.json index a066971be25..50a4795410d 100644 --- a/homeassistant/components/meteoclimatic/translations/en.json +++ b/homeassistant/components/meteoclimatic/translations/en.json @@ -12,8 +12,9 @@ "data": { "code": "Station code" }, - "description": "Enter the Meteoclimatic station code (e.g., ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Looks like ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/es.json b/homeassistant/components/meteoclimatic/translations/es.json index ab84e6604e3..027f85c60df 100644 --- a/homeassistant/components/meteoclimatic/translations/es.json +++ b/homeassistant/components/meteoclimatic/translations/es.json @@ -11,9 +11,7 @@ "user": { "data": { "code": "C\u00f3digo de la estaci\u00f3n" - }, - "description": "Introduzca el c\u00f3digo de la estaci\u00f3n Meteoclimatic (por ejemplo, ESCAT4300000043206B)", - "title": "Meteoclimatic" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/et.json b/homeassistant/components/meteoclimatic/translations/et.json index e019a4a0e12..886de9279f9 100644 --- a/homeassistant/components/meteoclimatic/translations/et.json +++ b/homeassistant/components/meteoclimatic/translations/et.json @@ -12,8 +12,9 @@ "data": { "code": "Jaama kood" }, - "description": "Sisesta Meteoclimatic jaama kood (nt ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "N\u00e4eb v\u00e4lja nagu ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/fr.json b/homeassistant/components/meteoclimatic/translations/fr.json index ae087db2e6e..95151d8f553 100644 --- a/homeassistant/components/meteoclimatic/translations/fr.json +++ b/homeassistant/components/meteoclimatic/translations/fr.json @@ -12,8 +12,9 @@ "data": { "code": "Code de la station" }, - "description": "Entrer le code de la station m\u00e9t\u00e9orologique (par exemple, ESCAT4300000043206)", - "title": "M\u00e9t\u00e9oclimatique" + "data_description": { + "code": "Ressemble \u00e0 ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/gl.json b/homeassistant/components/meteoclimatic/translations/gl.json deleted file mode 100644 index be1629dd6e4..00000000000 --- a/homeassistant/components/meteoclimatic/translations/gl.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "description": "Introduza o c\u00f3digo da estaci\u00f3n Meteoclimatic (por exemplo, ESCAT4300000043206B)" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/hu.json b/homeassistant/components/meteoclimatic/translations/hu.json index 582c78f263d..d9583656eb2 100644 --- a/homeassistant/components/meteoclimatic/translations/hu.json +++ b/homeassistant/components/meteoclimatic/translations/hu.json @@ -12,8 +12,9 @@ "data": { "code": "\u00c1llom\u00e1s k\u00f3dja" }, - "description": "Adja meg a meteorol\u00f3giai \u00e1llom\u00e1s k\u00f3dj\u00e1t (pl. ESCAT4300000043206B)", - "title": "Meteoklimatikus" + "data_description": { + "code": "\u00dagy n\u00e9z ki, mint ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/id.json b/homeassistant/components/meteoclimatic/translations/id.json index 4e23dad8dc4..3f8221f059a 100644 --- a/homeassistant/components/meteoclimatic/translations/id.json +++ b/homeassistant/components/meteoclimatic/translations/id.json @@ -12,8 +12,9 @@ "data": { "code": "Kode stasiun" }, - "description": "Masukkan kode stasiun Meteoclimatic (misalnya, ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Sepertinya ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/it.json b/homeassistant/components/meteoclimatic/translations/it.json index fcdfa25c496..f069b393a1b 100644 --- a/homeassistant/components/meteoclimatic/translations/it.json +++ b/homeassistant/components/meteoclimatic/translations/it.json @@ -12,8 +12,9 @@ "data": { "code": "Codice della stazione" }, - "description": "Immettere il codice della stazione Meteoclimatic (ad esempio ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Assomiglia a ECAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/ja.json b/homeassistant/components/meteoclimatic/translations/ja.json index 0274ff70e88..adae157aeee 100644 --- a/homeassistant/components/meteoclimatic/translations/ja.json +++ b/homeassistant/components/meteoclimatic/translations/ja.json @@ -12,8 +12,9 @@ "data": { "code": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u30b3\u30fc\u30c9" }, - "description": "Meteoclimatic\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u30b3\u30fc\u30c9\u3092\u5165\u529b(\u4f8b: ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "ESCAT4300000043206B\u306e\u3088\u3046\u306b\u898b\u3048\u307e\u3059" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/nl.json b/homeassistant/components/meteoclimatic/translations/nl.json index dd2a318ec37..c6f7151ade6 100644 --- a/homeassistant/components/meteoclimatic/translations/nl.json +++ b/homeassistant/components/meteoclimatic/translations/nl.json @@ -12,8 +12,9 @@ "data": { "code": "Station code" }, - "description": "Voer de code van het meteoklimatologische station in (bv. ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Lijkt op ESCAT43000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/no.json b/homeassistant/components/meteoclimatic/translations/no.json index 0e6e080d146..1bcf4ae09fe 100644 --- a/homeassistant/components/meteoclimatic/translations/no.json +++ b/homeassistant/components/meteoclimatic/translations/no.json @@ -12,8 +12,9 @@ "data": { "code": "Stasjonskode" }, - "description": "Angi Meteoclimatic stasjonskode (f.eks. ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Ser ut som ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/pl.json b/homeassistant/components/meteoclimatic/translations/pl.json index c4539bd6c8b..aab41ba7d12 100644 --- a/homeassistant/components/meteoclimatic/translations/pl.json +++ b/homeassistant/components/meteoclimatic/translations/pl.json @@ -12,8 +12,9 @@ "data": { "code": "Kod stacji" }, - "description": "Wpisz kod stacji Meteoclimatic (np. ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "Wygl\u0105da jak ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/pt-BR.json b/homeassistant/components/meteoclimatic/translations/pt-BR.json index c81109b8939..88223d4b177 100644 --- a/homeassistant/components/meteoclimatic/translations/pt-BR.json +++ b/homeassistant/components/meteoclimatic/translations/pt-BR.json @@ -12,8 +12,9 @@ "data": { "code": "C\u00f3digo da esta\u00e7\u00e3o" }, - "description": "Digite o c\u00f3digo da esta\u00e7\u00e3o Meteoclim\u00e1tica (por exemplo, ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "\u00c9 parecido com isso ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/pt.json b/homeassistant/components/meteoclimatic/translations/pt.json deleted file mode 100644 index 71eded9f5de..00000000000 --- a/homeassistant/components/meteoclimatic/translations/pt.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "description": "Introduza o c\u00f3digo da esta\u00e7\u00e3o Meteoclimatic (por exemplo, ESCAT4300000043206B)" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/meteoclimatic/translations/ru.json b/homeassistant/components/meteoclimatic/translations/ru.json index 14df114c903..79421f0f325 100644 --- a/homeassistant/components/meteoclimatic/translations/ru.json +++ b/homeassistant/components/meteoclimatic/translations/ru.json @@ -12,8 +12,9 @@ "data": { "code": "\u041a\u043e\u0434 \u0441\u0442\u0430\u043d\u0446\u0438\u0438" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 Meteoclimatic (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/tr.json b/homeassistant/components/meteoclimatic/translations/tr.json index a56fe984e2e..5221bc971c0 100644 --- a/homeassistant/components/meteoclimatic/translations/tr.json +++ b/homeassistant/components/meteoclimatic/translations/tr.json @@ -12,8 +12,9 @@ "data": { "code": "\u0130stasyon kodu" }, - "description": "Meteoclimatic istasyon kodunu girin (\u00f6rne\u011fin, ESCAT4300000043206B)", - "title": "Meteoclimatic" + "data_description": { + "code": "ESCAT4300000043206B'ye benziyor" + } } } } diff --git a/homeassistant/components/meteoclimatic/translations/zh-Hant.json b/homeassistant/components/meteoclimatic/translations/zh-Hant.json index d5c3793be7d..7aa165509f2 100644 --- a/homeassistant/components/meteoclimatic/translations/zh-Hant.json +++ b/homeassistant/components/meteoclimatic/translations/zh-Hant.json @@ -12,8 +12,9 @@ "data": { "code": "\u6c23\u8c61\u7ad9\u4ee3\u78bc" }, - "description": "\u8f38\u5165 Meteoclimatic \u6c23\u8c61\u7ad9\u4ee3\u78bc\uff08\u4f8b\u5982 ESCAT4300000043206B\uff09", - "title": "Meteoclimatic" + "data_description": { + "code": "\u770b\u8d77\u4f86\u985e\u4f3c ESCAT4300000043206B" + } } } } diff --git a/homeassistant/components/mikrotik/translations/hu.json b/homeassistant/components/mikrotik/translations/hu.json index 3e5281fc06a..c15ba2f07aa 100644 --- a/homeassistant/components/mikrotik/translations/hu.json +++ b/homeassistant/components/mikrotik/translations/hu.json @@ -12,7 +12,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", diff --git a/homeassistant/components/min_max/translations/bg.json b/homeassistant/components/min_max/translations/bg.json new file mode 100644 index 00000000000..35cfa0ad1d7 --- /dev/null +++ b/homeassistant/components/min_max/translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/ca.json b/homeassistant/components/min_max/translations/ca.json new file mode 100644 index 00000000000..b114e8ecf40 --- /dev/null +++ b/homeassistant/components/min_max/translations/ca.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entitats d'entrada", + "name": "Nom", + "round_digits": "Precisi\u00f3", + "type": "Caracter\u00edstica estad\u00edstica" + }, + "data_description": { + "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana o la mediana." + }, + "description": "Crea un sensor que calcula el m\u00ednim, m\u00e0xim, la mitjana o la mediana d'una llista de sensors d'entrada.", + "title": "Afegeix sensor m\u00edn / m\u00e0x / mitjana / mediana" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entitats d'entrada", + "round_digits": "Precisi\u00f3", + "type": "Caracter\u00edstica estad\u00edstica" + }, + "data_description": { + "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana o la mediana." + } + }, + "options": { + "data": { + "entity_ids": "Entitats d'entrada", + "round_digits": "Precisi\u00f3", + "type": "Caracter\u00edstica estad\u00edstica" + }, + "description": "Crea un sensor que calcula el m\u00ednim, m\u00e0xim, la mitjana o la mediana d'una llista de sensors d'entrada." + } + } + }, + "title": "Sensor m\u00edn / m\u00e0x / mitjana / mediana" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/cs.json b/homeassistant/components/min_max/translations/cs.json new file mode 100644 index 00000000000..a969a39234a --- /dev/null +++ b/homeassistant/components/min_max/translations/cs.json @@ -0,0 +1,34 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Vstupn\u00ed entity", + "name": "Jm\u00e9no", + "round_digits": "P\u0159esnost", + "type": "Statistika" + }, + "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst, kdy\u017e je statistikou pr\u016fm\u011br nebo medi\u00e1n." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Vstupn\u00ed entity", + "round_digits": "P\u0159esnost", + "type": "Statistika" + } + }, + "options": { + "data": { + "entity_ids": "Vstupn\u00ed entity", + "round_digits": "P\u0159esnost", + "type": "Statistika" + }, + "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst, kdy\u017e je statistikou pr\u016fm\u011br nebo medi\u00e1n." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/de.json b/homeassistant/components/min_max/translations/de.json new file mode 100644 index 00000000000..c8b9d5ddf63 --- /dev/null +++ b/homeassistant/components/min_max/translations/de.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Eingabe-Entit\u00e4ten", + "name": "Name", + "round_digits": "Genauigkeit", + "type": "Statistisches Merkmal" + }, + "data_description": { + "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert oder Median ist." + }, + "description": "Erstelle einen Sensor, der einen Mindest-, H\u00f6chst-, Mittel- oder Medianwert aus einer Liste von Eingabesensoren berechnet.", + "title": "Min/Max/Mittelwert/Median-Sensor hinzuf\u00fcgen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Eingabe-Entit\u00e4ten", + "round_digits": "Genauigkeit", + "type": "Statistisches Merkmal" + }, + "data_description": { + "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert oder Median ist." + } + }, + "options": { + "data": { + "entity_ids": "Eingabe-Entit\u00e4ten", + "round_digits": "Genauigkeit", + "type": "Statistisches Merkmal" + }, + "description": "Erstelle einen Sensor, der einen Mindest-, H\u00f6chst-, Mittel- oder Medianwert aus einer Liste von Eingabesensoren berechnet." + } + } + }, + "title": "Min / Max / Mittelwert / Mediansensor" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/el.json b/homeassistant/components/min_max/translations/el.json new file mode 100644 index 00000000000..218c46fd683 --- /dev/null +++ b/homeassistant/components/min_max/translations/el.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "name": "\u039f\u03bd\u03bf\u03bc\u03b1", + "round_digits": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" + }, + "data_description": { + "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03ae \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bf \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03ae \u03b7 \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2.", + "title": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 min / max / mean / median" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "round_digits": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" + }, + "data_description": { + "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03ae \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." + } + }, + "options": { + "data": { + "entity_ids": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "round_digits": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", + "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" + }, + "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bf \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03ae \u03b7 \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." + } + } + }, + "title": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03c2 / \u03bc\u03ad\u03b3\u03b9\u03c3\u03c4\u03bf\u03c2 / \u03bc\u03ad\u03c3\u03bf\u03c2 / \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/en.json b/homeassistant/components/min_max/translations/en.json index 8cc0d41c419..b5b6eb5d731 100644 --- a/homeassistant/components/min_max/translations/en.json +++ b/homeassistant/components/min_max/translations/en.json @@ -27,6 +27,14 @@ "data_description": { "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean or median." } + }, + "options": { + "data": { + "entity_ids": "Input entities", + "round_digits": "Precision", + "type": "Statistic characteristic" + }, + "description": "Create a sensor that calculates a min, max, mean or median value from a list of input sensors." } } }, diff --git a/homeassistant/components/min_max/translations/et.json b/homeassistant/components/min_max/translations/et.json new file mode 100644 index 00000000000..2f1689e8577 --- /dev/null +++ b/homeassistant/components/min_max/translations/et.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Sisendolemid", + "name": "Nimi", + "round_digits": "T\u00e4psus", + "type": "Statistiline tunnus" + }, + "data_description": { + "round_digits": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis kui statistika tunnus on keskmine v\u00f5i mediaan." + }, + "description": "Loo andur mis arvutab sisendandurite loendist minimaalse, maksimaalse, keskmise v\u00f5i mediaanv\u00e4\u00e4rtuse.", + "title": "Lisa min / max / keskmine / mediaanandur" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Sisendolemid", + "round_digits": "T\u00e4psus", + "type": "Statistiline omadus" + }, + "data_description": { + "round_digits": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis kui statistika tunnus on keskmine v\u00f5i mediaan." + } + }, + "options": { + "data": { + "entity_ids": "Sisendolemid", + "round_digits": "T\u00e4psus", + "type": "Statistiline tunnus" + }, + "description": "T\u00e4psus kontrollib k\u00fcmnendnumbrite arvu kui statistika tunnus on keskmine v\u00f5i mediaan." + } + } + }, + "title": "Min / max / keskmine / mediaanandur" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/fr.json b/homeassistant/components/min_max/translations/fr.json new file mode 100644 index 00000000000..b42ccf0bb2a --- /dev/null +++ b/homeassistant/components/min_max/translations/fr.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entit\u00e9s d'entr\u00e9e", + "name": "Nom", + "round_digits": "Pr\u00e9cision", + "type": "Indicateur statistique" + }, + "data_description": { + "round_digits": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux de la sortie lorsque l'indicateur statistique est la moyenne ou la m\u00e9diane." + }, + "description": "Cr\u00e9ez un capteur qui calcule une valeur minimale, maximale, moyenne ou m\u00e9diane \u00e0 partir d'une liste de capteurs d'entr\u00e9e.", + "title": "Ajouter un capteur de valeur minimale, maximale, moyenne ou m\u00e9diane" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entit\u00e9s d'entr\u00e9e", + "round_digits": "Pr\u00e9cision", + "type": "Indicateur statistique" + }, + "data_description": { + "round_digits": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux de la sortie lorsque l'indicateur statistique est la moyenne ou la m\u00e9diane." + } + }, + "options": { + "data": { + "entity_ids": "Entit\u00e9s d'entr\u00e9e", + "round_digits": "Pr\u00e9cision", + "type": "Indicateur statistique" + }, + "description": "Cr\u00e9ez un capteur qui calcule une valeur minimale, maximale, moyenne ou m\u00e9diane \u00e0 partir d'une liste de capteurs d'entr\u00e9e." + } + } + }, + "title": "Capteur de valeur minimale, maximale, moyenne ou m\u00e9diane" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/he.json b/homeassistant/components/min_max/translations/he.json new file mode 100644 index 00000000000..267e38f0ce2 --- /dev/null +++ b/homeassistant/components/min_max/translations/he.json @@ -0,0 +1,37 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e7\u05dc\u05d8", + "round_digits": "\u05d3\u05d9\u05d5\u05e7", + "type": "\u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" + }, + "data_description": { + "round_digits": "\u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8 \u05db\u05d0\u05e9\u05e8 \u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d4\u05d5\u05d0 \u05de\u05de\u05d5\u05e6\u05e2 \u05d0\u05d5 \u05d7\u05e6\u05d9\u05d5\u05e0\u05d9." + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e7\u05dc\u05d8", + "round_digits": "\u05d3\u05d9\u05d5\u05e7", + "type": "\u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" + }, + "data_description": { + "round_digits": "\u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8 \u05db\u05d0\u05e9\u05e8 \u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d4\u05d5\u05d0 \u05de\u05de\u05d5\u05e6\u05e2 \u05d0\u05d5 \u05d7\u05e6\u05d9\u05d5\u05e0\u05d9." + } + }, + "options": { + "data": { + "entity_ids": "\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e7\u05dc\u05d8", + "round_digits": "\u05d3\u05d9\u05d5\u05e7", + "type": "\u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/hu.json b/homeassistant/components/min_max/translations/hu.json new file mode 100644 index 00000000000..98d021dbb31 --- /dev/null +++ b/homeassistant/components/min_max/translations/hu.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Forr\u00e1s entit\u00e1sok", + "name": "Elnevez\u00e9s", + "round_digits": "Pontoss\u00e1g", + "type": "Statisztikai jellemz\u0151" + }, + "data_description": { + "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." + }, + "description": "A pontoss\u00e1g a tizedesjegyek sz\u00e1m\u00e1t szab\u00e1lyozza, ha a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n.", + "title": "Min / max / \u00e1tlag / medi\u00e1n \u00e9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Forr\u00e1s entit\u00e1sok", + "round_digits": "Pontoss\u00e1g", + "type": "Statisztikai jellemz\u0151" + }, + "data_description": { + "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." + } + }, + "options": { + "data": { + "entity_ids": "Forr\u00e1s entit\u00e1sok", + "round_digits": "Pontoss\u00e1g", + "type": "Statisztikai jellemz\u0151" + }, + "description": "A pontoss\u00e1g a tizedesjegyek sz\u00e1m\u00e1t szab\u00e1lyozza, ha a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." + } + } + }, + "title": "Min / max / \u00e1tlag / medi\u00e1n \u00e9rz\u00e9kel\u0151" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/id.json b/homeassistant/components/min_max/translations/id.json new file mode 100644 index 00000000000..32ccfd2596d --- /dev/null +++ b/homeassistant/components/min_max/translations/id.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entitas input", + "name": "Nama", + "round_digits": "Presisi", + "type": "Karakteristik statistik" + }, + "data_description": { + "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata atau median." + }, + "description": "Buat sensor yang menghitung nilai min, maks, rata-rata, atau median dari sejumlah sensor input.", + "title": "Tambahkan sensor min/maks/rata-rata/median" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entitas input", + "round_digits": "Presisi", + "type": "Karakteristik statistik" + }, + "data_description": { + "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata atau median." + } + }, + "options": { + "data": { + "entity_ids": "Entitas input", + "round_digits": "Presisi", + "type": "Karakteristik statistik" + }, + "description": "Buat sensor yang menghitung nilai min, maks, rata-rata, atau median dari sejumlah sensor input." + } + } + }, + "title": "Sensor min/maks/rata-rata/median" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/it.json b/homeassistant/components/min_max/translations/it.json new file mode 100644 index 00000000000..49c715a7507 --- /dev/null +++ b/homeassistant/components/min_max/translations/it.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entit\u00e0 di ingresso", + "name": "Nome", + "round_digits": "Precisione", + "type": "Caratteristica statistica" + }, + "data_description": { + "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media o mediana." + }, + "description": "Crea un sensore che calcoli un valore minimo, massimo, medio o mediano da un elenco di sensori di input.", + "title": "Aggiungi sensore min / max / media / mediana" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entit\u00e0 di ingresso", + "round_digits": "Precisione", + "type": "Caratteristica statistica" + }, + "data_description": { + "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media o mediana." + } + }, + "options": { + "data": { + "entity_ids": "Entit\u00e0 di ingresso", + "round_digits": "Precisione", + "type": "Caratteristica statistica" + }, + "description": "Crea un sensore che calcoli un valore minimo, massimo, medio o mediano da un elenco di sensori di input." + } + } + }, + "title": "Sensore min / max / media / mediana" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/ja.json b/homeassistant/components/min_max/translations/ja.json new file mode 100644 index 00000000000..c853a7c389e --- /dev/null +++ b/homeassistant/components/min_max/translations/ja.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u5165\u529b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "name": "\u540d\u524d", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7d71\u8a08\u7684\u7279\u6027" + }, + "data_description": { + "round_digits": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u5024\u307e\u305f\u306f\u4e2d\u592e\u5024\u306e\u5834\u5408\u306b\u3001\u51fa\u529b\u306b\u542b\u307e\u308c\u308b\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" + }, + "description": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u307e\u305f\u306f\u4e2d\u592e\u5024\u306a\u5834\u5408\u306e\u7cbe\u5ea6\u3067\u3001\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002", + "title": "\u6700\u5c0f/\u6700\u5927/\u5e73\u5747/\u4e2d\u592e\u5024 \u30bb\u30f3\u30b5\u30fc\u3092\u8ffd\u52a0" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u5165\u529b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7d71\u8a08\u7684\u7279\u6027" + }, + "data_description": { + "round_digits": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u5024\u307e\u305f\u306f\u4e2d\u592e\u5024\u306e\u5834\u5408\u306b\u3001\u51fa\u529b\u306b\u542b\u307e\u308c\u308b\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" + } + }, + "options": { + "data": { + "entity_ids": "\u5165\u529b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7d71\u8a08\u7684\u7279\u6027" + }, + "description": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u307e\u305f\u306f\u4e2d\u592e\u5024\u306a\u5834\u5408\u306e\u7cbe\u5ea6\u3067\u3001\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" + } + } + }, + "title": "\u6700\u5c0f/\u6700\u5927/\u5e73\u5747/\u4e2d\u592e\u5024 \u30bb\u30f3\u30b5\u30fc" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/nl.json b/homeassistant/components/min_max/translations/nl.json new file mode 100644 index 00000000000..36f09f97d54 --- /dev/null +++ b/homeassistant/components/min_max/translations/nl.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entiteiten invoeren", + "name": "Naam", + "round_digits": "Precisie", + "type": "statistisch kenmerk" + }, + "data_description": { + "round_digits": "Regelt het aantal decimale cijfers in de uitvoer wanneer de statistische eigenschap gemiddelde of mediaan is." + }, + "description": "Maak een sensor die een min, max, gemiddelde of mediaanwaarde berekent uit een lijst van invoersensoren.", + "title": "Voeg min / max / gemiddelde / mediaan sensor toe" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entiteiten invoeren", + "round_digits": "Precisie", + "type": "statistisch kenmerk" + }, + "data_description": { + "round_digits": "Regelt het aantal decimale cijfers in de uitvoer wanneer de statistische eigenschap gemiddelde of mediaan is." + } + }, + "options": { + "data": { + "entity_ids": "Invoer entiteiten", + "round_digits": "Precisie", + "type": "Statistische karakteristieken" + }, + "description": "Precisie bepaalt het aantal decimale cijfers wanneer het statistische kenmerk gemiddelde of mediaan is." + } + } + }, + "title": "Min / max / gemiddelde / mediaan sensor" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/no.json b/homeassistant/components/min_max/translations/no.json new file mode 100644 index 00000000000..798430cec03 --- /dev/null +++ b/homeassistant/components/min_max/translations/no.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Inndataenheter", + "name": "Navn", + "round_digits": "Presisjon", + "type": "Statistisk karakteristikk" + }, + "data_description": { + "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt eller median." + }, + "description": "Lag en sensor som beregner en min, maks, middelverdi eller medianverdi fra en liste over inngangssensorer.", + "title": "Legg til min / maks / gjennomsnitt / median sensor" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Inndataenheter", + "round_digits": "Presisjon", + "type": "Statistisk karakteristikk" + }, + "data_description": { + "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt eller median." + } + }, + "options": { + "data": { + "entity_ids": "Inndataenheter", + "round_digits": "Presisjon", + "type": "Statistisk karakteristikk" + }, + "description": "Lag en sensor som beregner en min, maks, middelverdi eller medianverdi fra en liste over inngangssensorer." + } + } + }, + "title": "Min / maks / gjennomsnitt / median sensor" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/pl.json b/homeassistant/components/min_max/translations/pl.json new file mode 100644 index 00000000000..0a29ad78339 --- /dev/null +++ b/homeassistant/components/min_max/translations/pl.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Encje wej\u015bciowe", + "name": "Nazwa", + "round_digits": "Precyzja", + "type": "Charakterystyka statystyczna" + }, + "data_description": { + "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105 lub median\u0105." + }, + "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza warto\u015b\u0107 minimaln\u0105, maksymaln\u0105, \u015bredni\u0105 lub median\u0119 z listy sensor\u00f3w wej\u015bciowych.", + "title": "Dodawanie sensora min / maks / \u015brednia / mediana" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Encje wej\u015bciowe", + "round_digits": "Precyzja", + "type": "Charakterystyka statystyczna" + }, + "data_description": { + "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105 lub median\u0105." + } + }, + "options": { + "data": { + "entity_ids": "Encje wej\u015bciowe", + "round_digits": "Precyzja", + "type": "Charakterystyka statystyczna" + }, + "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza warto\u015b\u0107 minimaln\u0105, maksymaln\u0105, \u015bredni\u0105 lub median\u0119 z listy sensor\u00f3w wej\u015bciowych." + } + } + }, + "title": "Sensor min./maks./\u015brednia/mediana" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/pt-BR.json b/homeassistant/components/min_max/translations/pt-BR.json new file mode 100644 index 00000000000..9c1c77c304a --- /dev/null +++ b/homeassistant/components/min_max/translations/pt-BR.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entidades de entrada", + "name": "Nome", + "round_digits": "Precis\u00e3o", + "type": "Caracter\u00edstica estat\u00edstica" + }, + "data_description": { + "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia ou mediana." + }, + "description": "Crie um sensor que calcula um valor m\u00ednimo, m\u00e1ximo, m\u00e9dio ou mediano de uma lista de sensores de entrada.", + "title": "Adicionar sensor de m\u00ednima / m\u00e1xima / m\u00e9dia / mediana" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entidades de entrada", + "round_digits": "Precis\u00e3o", + "type": "Caracter\u00edstica estat\u00edstica" + }, + "data_description": { + "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia ou mediana." + } + }, + "options": { + "data": { + "entity_ids": "Entidades de entrada", + "round_digits": "Precis\u00e3o", + "type": "Caracter\u00edstica estat\u00edstica" + }, + "description": "Crie um sensor que calcula um valor m\u00ednimo, m\u00e1ximo, m\u00e9dio ou mediano de uma lista de sensores de entrada." + } + } + }, + "title": "Sensor m\u00edn./m\u00e1x./m\u00e9dio/mediano" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/ru.json b/homeassistant/components/min_max/translations/ru.json new file mode 100644 index 00000000000..551660fcabb --- /dev/null +++ b/homeassistant/components/min_max/translations/ru.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "round_digits": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" + }, + "data_description": { + "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439." + }, + "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432.", + "title": "\u041c\u0438\u043d\u0438\u043c\u0443\u043c / \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c / \u0441\u0440\u0435\u0434\u043d\u0435\u0435 / \u043c\u0435\u0434\u0438\u0430\u043d\u0430" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "round_digits": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" + }, + "data_description": { + "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439." + } + }, + "options": { + "data": { + "entity_ids": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", + "round_digits": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", + "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" + }, + "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432." + } + } + }, + "title": "\u041d\u043e\u0432\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/sv.json b/homeassistant/components/min_max/translations/sv.json new file mode 100644 index 00000000000..d39c277daff --- /dev/null +++ b/homeassistant/components/min_max/translations/sv.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "round_digits": "Precision" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/tr.json b/homeassistant/components/min_max/translations/tr.json new file mode 100644 index 00000000000..7f192888615 --- /dev/null +++ b/homeassistant/components/min_max/translations/tr.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Giri\u015f varl\u0131klar\u0131", + "name": "Ad", + "round_digits": "Hassas", + "type": "\u0130statistik \u00f6zelli\u011fi" + }, + "data_description": { + "round_digits": "\u0130statistik \u00f6zelli\u011fi ortalama veya medyan oldu\u011funda \u00e7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." + }, + "description": "Giri\u015f sens\u00f6rleri listesinden minimum, maksimum, ortalama veya medyan de\u011feri hesaplayan bir sens\u00f6r olu\u015fturun.", + "title": "Min / maks / ortalama / medyan sens\u00f6r\u00fc ekle" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Giri\u015f varl\u0131klar\u0131", + "round_digits": "Hassas", + "type": "\u0130statistik \u00f6zelli\u011fi" + }, + "data_description": { + "round_digits": "\u0130statistik \u00f6zelli\u011fi ortalama veya medyan oldu\u011funda \u00e7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." + } + }, + "options": { + "data": { + "entity_ids": "Giri\u015f varl\u0131klar\u0131", + "round_digits": "Hassas", + "type": "\u0130statistik \u00f6zelli\u011fi" + }, + "description": "Giri\u015f sens\u00f6rleri listesinden minimum, maksimum, ortalama veya medyan de\u011feri hesaplayan bir sens\u00f6r olu\u015fturun." + } + } + }, + "title": "Min / maks / ortalama / medyan sens\u00f6r" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/zh-Hans.json b/homeassistant/components/min_max/translations/zh-Hans.json new file mode 100644 index 00000000000..ca5555bbd0a --- /dev/null +++ b/homeassistant/components/min_max/translations/zh-Hans.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u8f93\u5165\u5b9e\u4f53", + "name": "\u540d\u79f0", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7edf\u8ba1\u9879" + }, + "data_description": { + "round_digits": "\u5f53\u7edf\u8ba1\u9879\u4e3a\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u65f6\uff0c\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u3001\u6216\u4e2d\u4f4d\u6570\u3002", + "title": "\u6dfb\u52a0\u6700\u5927\u503c/\u6700\u5c0f\u503c/\u5e73\u5747\u503c/\u4e2d\u4f4d\u6570\u4f20\u611f\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u8f93\u5165\u5b9e\u4f53", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7edf\u8ba1\u9879" + }, + "data_description": { + "round_digits": "\u5f53\u7edf\u8ba1\u9879\u4e3a\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u65f6\uff0c\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" + } + }, + "options": { + "data": { + "entity_ids": "\u8f93\u5165\u5b9e\u4f53", + "round_digits": "\u7cbe\u5ea6", + "type": "\u7edf\u8ba1\u9879" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u3001\u6216\u4e2d\u4f4d\u6570\u3002" + } + } + }, + "title": "\u6700\u5927\u503c/\u6700\u5c0f\u503c/\u5e73\u5747\u503c/\u4e2d\u4f4d\u6570\u4f20\u611f\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/zh-Hant.json b/homeassistant/components/min_max/translations/zh-Hant.json new file mode 100644 index 00000000000..b0166e26cac --- /dev/null +++ b/homeassistant/components/min_max/translations/zh-Hant.json @@ -0,0 +1,42 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\u8f38\u5165\u5be6\u9ad4", + "name": "\u540d\u7a31", + "round_digits": "\u6e96\u78ba\u5ea6", + "type": "\u7d71\u8a08\u7279\u5fb5" + }, + "data_description": { + "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u6216\u4e2d\u503c\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" + }, + "description": "\u65b0\u589e\u81ea\u8f38\u5165\u611f\u6e2c\u5668\u4e86\u8868\u4e2d\uff0c\u8a08\u7b97\u6700\u4f4e\u3001\u6700\u9ad8\u3001\u5e73\u5747\u503c\u6216\u4e2d\u503c\u611f\u6e2c\u5668\u3002", + "title": "\u65b0\u589e\u6700\u5c0f\u503c / \u6700\u5927\u503c / \u5e73\u5747\u503c / \u4e2d\u503c\u611f\u6e2c\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\u8f38\u5165\u5be6\u9ad4", + "round_digits": "\u6e96\u78ba\u5ea6", + "type": "\u7d71\u8a08\u7279\u5fb5" + }, + "data_description": { + "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u6216\u4e2d\u503c\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" + } + }, + "options": { + "data": { + "entity_ids": "\u8f38\u5165\u5be6\u9ad4", + "round_digits": "\u6e96\u78ba\u5ea6", + "type": "\u7d71\u8a08\u7279\u5fb5" + }, + "description": "\u65b0\u589e\u81ea\u8f38\u5165\u611f\u6e2c\u5668\u4e86\u8868\u4e2d\uff0c\u8a08\u7b97\u6700\u4f4e\u3001\u6700\u9ad8\u3001\u5e73\u5747\u503c\u6216\u4e2d\u503c\u611f\u6e2c\u5668\u3002" + } + } + }, + "title": "\u6700\u5c0f\u503c / \u6700\u5927\u503c / \u5e73\u5747\u503c / \u4e2d\u503c\u611f\u6e2c\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/minecraft_server/translations/hu.json b/homeassistant/components/minecraft_server/translations/hu.json index 02c2a06d8ab..00fff153254 100644 --- a/homeassistant/components/minecraft_server/translations/hu.json +++ b/homeassistant/components/minecraft_server/translations/hu.json @@ -12,7 +12,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "\u00c1ll\u00edtsa be a Minecraft Server p\u00e9ld\u00e1nyt, hogy lehet\u0151v\u00e9 tegye a megfigyel\u00e9st.", "title": "Kapcsold \u00f6ssze a Minecraft szervered" diff --git a/homeassistant/components/minecraft_server/translations/pl.json b/homeassistant/components/minecraft_server/translations/pl.json index fed8d250a90..88b5dd869e4 100644 --- a/homeassistant/components/minecraft_server/translations/pl.json +++ b/homeassistant/components/minecraft_server/translations/pl.json @@ -15,7 +15,7 @@ "name": "Nazwa" }, "description": "Skonfiguruj instancj\u0119 serwera Minecraft, aby umo\u017cliwi\u0107 monitorowanie.", - "title": "Po\u0142\u0105cz sw\u00f3j serwer Minecraft" + "title": "Po\u0142\u0105czenie z Twoim serwerem Minecraft" } } } diff --git a/homeassistant/components/mjpeg/translations/fr.json b/homeassistant/components/mjpeg/translations/fr.json index f54c60631a6..405cbc8bf51 100644 --- a/homeassistant/components/mjpeg/translations/fr.json +++ b/homeassistant/components/mjpeg/translations/fr.json @@ -13,6 +13,7 @@ "mjpeg_url": "URL MJPEG", "name": "Nom", "password": "Mot de passe", + "still_image_url": "URL de l'image fixe", "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL" } @@ -31,6 +32,7 @@ "mjpeg_url": "URL MJPEG", "name": "Nom", "password": "Mot de passe", + "still_image_url": "URL de l'image fixe", "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL" } diff --git a/homeassistant/components/mjpeg/translations/hu.json b/homeassistant/components/mjpeg/translations/hu.json index 0a87f484887..f3029663306 100644 --- a/homeassistant/components/mjpeg/translations/hu.json +++ b/homeassistant/components/mjpeg/translations/hu.json @@ -11,7 +11,7 @@ "user": { "data": { "mjpeg_url": "MJPEG URL-c\u00edme", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "still_image_url": "\u00c1ll\u00f3k\u00e9p URL-c\u00edme", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", @@ -30,7 +30,7 @@ "init": { "data": { "mjpeg_url": "MJPEG URL-c\u00edme", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "still_image_url": "\u00c1ll\u00f3k\u00e9p URL-c\u00edme", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", diff --git a/homeassistant/components/modem_callerid/translations/hu.json b/homeassistant/components/modem_callerid/translations/hu.json index 3a75df30a7d..2a53c24d902 100644 --- a/homeassistant/components/modem_callerid/translations/hu.json +++ b/homeassistant/components/modem_callerid/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "no_devices_found": "Nem tal\u00e1lhat\u00f3 egy\u00e9b eszk\u00f6z" }, "error": { @@ -15,7 +15,7 @@ }, "user": { "data": { - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "port": "Port" }, "description": "Ez egy integr\u00e1ci\u00f3 a CX93001 hangmodemmel t\u00f6rt\u00e9n\u0151 vezet\u00e9kes h\u00edv\u00e1sokhoz. Ez k\u00e9pes lek\u00e9rdezni a h\u00edv\u00f3azonos\u00edt\u00f3 inform\u00e1ci\u00f3t a bej\u00f6v\u0151 h\u00edv\u00e1s visszautas\u00edt\u00e1s\u00e1nak lehet\u0151s\u00e9g\u00e9vel.", diff --git a/homeassistant/components/modern_forms/translations/et.json b/homeassistant/components/modern_forms/translations/et.json index ee325440c13..7140888db84 100644 --- a/homeassistant/components/modern_forms/translations/et.json +++ b/homeassistant/components/modern_forms/translations/et.json @@ -19,7 +19,7 @@ "description": "Seadista Modern Forms'i ventilaator sidumiseks Home Assistantiga." }, "zeroconf_confirm": { - "description": "Kas lisada Modern Forms'i ventilaator nimega `{nimi}` Home Assistanti?", + "description": "Kas lisada Modern Forms'i ventilaator nimega `{name}` Home Assistanti?", "title": "Leitud Modern Forms ventilaator" } } diff --git a/homeassistant/components/moon/translations/cs.json b/homeassistant/components/moon/translations/cs.json new file mode 100644 index 00000000000..f1c26a20f6e --- /dev/null +++ b/homeassistant/components/moon/translations/cs.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + }, + "step": { + "user": { + "description": "Chcete za\u010d\u00edt nastavovat?" + } + } + }, + "title": "M\u011bs\u00edc" +} \ No newline at end of file diff --git a/homeassistant/components/moon/translations/sensor.fi.json b/homeassistant/components/moon/translations/sensor.fi.json index f7788972044..a35afac10d6 100644 --- a/homeassistant/components/moon/translations/sensor.fi.json +++ b/homeassistant/components/moon/translations/sensor.fi.json @@ -4,7 +4,11 @@ "first_quarter": "Ensimm\u00e4inen nelj\u00e4nnes", "full_moon": "T\u00e4ysikuu", "last_quarter": "Viimeinen nelj\u00e4nnes", - "new_moon": "Uusikuu" + "new_moon": "Uusikuu", + "waning_crescent": "V\u00e4henev\u00e4 sirppi", + "waning_gibbous": "V\u00e4henev\u00e4 kuperakuu", + "waxing_crescent": "Kasvava sirppi", + "waxing_gibbous": "Kasvava kuperakuu" } } } \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/ca.json b/homeassistant/components/motion_blinds/translations/ca.json index 1db1976cbd3..18cc8f892d6 100644 --- a/homeassistant/components/motion_blinds/translations/ca.json +++ b/homeassistant/components/motion_blinds/translations/ca.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat", + "already_configured": "El dispositiu ja est\u00e0 configurat, la configuraci\u00f3 de connexi\u00f3 s'ha actualitzat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "connection_error": "Ha fallat la connexi\u00f3" }, @@ -9,7 +9,7 @@ "discovery_error": "No s'ha pogut descobrir cap Motion Gateway", "invalid_interface": "Interf\u00edcie de xarxa no v\u00e0lida" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/de.json b/homeassistant/components/motion_blinds/translations/de.json index a4758c85d4b..c5ced3ddd55 100644 --- a/homeassistant/components/motion_blinds/translations/de.json +++ b/homeassistant/components/motion_blinds/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "already_configured": "Ger\u00e4t ist bereits konfiguriert, Verbindungseinstellungen werden aktualisiert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "connection_error": "Verbindung fehlgeschlagen" }, @@ -9,7 +9,7 @@ "discovery_error": "Motion-Gateway konnte nicht gefunden werden", "invalid_interface": "Ung\u00fcltige Netzwerkschnittstelle" }, - "flow_title": "Jalousien", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/en.json b/homeassistant/components/motion_blinds/translations/en.json index 4033a8f2016..92931ee27ab 100644 --- a/homeassistant/components/motion_blinds/translations/en.json +++ b/homeassistant/components/motion_blinds/translations/en.json @@ -6,13 +6,15 @@ "connection_error": "Failed to connect" }, "error": { - "discovery_error": "Failed to discover a Motion Gateway" + "discovery_error": "Failed to discover a Motion Gateway", + "invalid_interface": "Invalid network interface" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API Key" + "api_key": "API Key", + "interface": "The network interface to use" }, "description": "You will need the 16 character API Key, see https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key for instructions", "title": "Motion Blinds" diff --git a/homeassistant/components/motion_blinds/translations/et.json b/homeassistant/components/motion_blinds/translations/et.json index 7091c402a2e..935e345ea3a 100644 --- a/homeassistant/components/motion_blinds/translations/et.json +++ b/homeassistant/components/motion_blinds/translations/et.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "already_configured": "Seade on juba h\u00e4\u00e4lestatud, \u00fchenduse s\u00e4tted on v\u00e4rskendatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", "connection_error": "\u00dchendamine nurjus" }, @@ -9,7 +9,7 @@ "discovery_error": "Motion Gateway avastamine nurjus", "invalid_interface": "Sobimatu v\u00f5rguliides" }, - "flow_title": "", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/fr.json b/homeassistant/components/motion_blinds/translations/fr.json index 75fb1daa9be..09cf93fde60 100644 --- a/homeassistant/components/motion_blinds/translations/fr.json +++ b/homeassistant/components/motion_blinds/translations/fr.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9, les param\u00e8tres de connexion ont \u00e9t\u00e9 mis \u00e0 jour", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "connection_error": "\u00c9chec de connexion" }, @@ -9,7 +9,7 @@ "discovery_error": "Impossible de d\u00e9couvrir une Motion Gateway", "invalid_interface": "Interface r\u00e9seau non valide" }, - "flow_title": "Stores de mouvement", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/he.json b/homeassistant/components/motion_blinds/translations/he.json index 0876de6504a..ce6f3d2918e 100644 --- a/homeassistant/components/motion_blinds/translations/he.json +++ b/homeassistant/components/motion_blinds/translations/he.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e2\u05d5\u05d3\u05db\u05e0\u05d5", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "connection_error": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, diff --git a/homeassistant/components/motion_blinds/translations/hu.json b/homeassistant/components/motion_blinds/translations/hu.json index 9d0f41db143..64334c54a28 100644 --- a/homeassistant/components/motion_blinds/translations/hu.json +++ b/homeassistant/components/motion_blinds/translations/hu.json @@ -1,15 +1,15 @@ { "config": { "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van, a csatlakoz\u00e1si be\u00e1ll\u00edt\u00e1sai friss\u00edtve vannak", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "connection_error": "Sikertelen csatlakoz\u00e1s" }, "error": { "discovery_error": "Nem siker\u00fclt felfedezni a Motion Gateway-t", "invalid_interface": "\u00c9rv\u00e9nytelen h\u00e1l\u00f3zati interf\u00e9sz" }, - "flow_title": "Mozg\u00f3 red\u0151ny", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { @@ -17,7 +17,7 @@ "interface": "A haszn\u00e1lni k\u00edv\u00e1nt h\u00e1l\u00f3zati interf\u00e9sz" }, "description": "Sz\u00fcks\u00e9ge lesz a 16 karakteres API kulcsra, \u00fatmutat\u00e1s\u00e9rt l\u00e1sd: https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "Mozg\u00f3 red\u0151ny" + "title": "Red\u0151ny/rol\u00f3" }, "select": { "data": { @@ -32,7 +32,7 @@ "host": "IP c\u00edm" }, "description": "Csatlakozzon a Motion Gateway-hez, ha az IP-c\u00edm nincs be\u00e1ll\u00edtva, akkor az automatikus felder\u00edt\u00e9st haszn\u00e1lja", - "title": "Mozg\u00f3 red\u0151ny" + "title": "Red\u0151ny/rol\u00f3" } } }, @@ -43,7 +43,7 @@ "wait_for_push": "Multicast adatokra v\u00e1rakoz\u00e1s friss\u00edt\u00e9skor" }, "description": "Opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1sa", - "title": "Motion Blinds" + "title": "Red\u0151ny/rol\u00f3" } } } diff --git a/homeassistant/components/motion_blinds/translations/id.json b/homeassistant/components/motion_blinds/translations/id.json index 269470110ec..d576ced8c0a 100644 --- a/homeassistant/components/motion_blinds/translations/id.json +++ b/homeassistant/components/motion_blinds/translations/id.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi", + "already_configured": "Perangkat sudah dikonfigurasi, pengaturan koneksi diperbarui", "already_in_progress": "Alur konfigurasi sedang berlangsung", "connection_error": "Gagal terhubung" }, @@ -9,7 +9,7 @@ "discovery_error": "Gagal menemukan Motion Gateway", "invalid_interface": "Antarmuka jaringan tidak valid" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/it.json b/homeassistant/components/motion_blinds/translations/it.json index 6a9cef8a0af..0871ce07bf8 100644 --- a/homeassistant/components/motion_blinds/translations/it.json +++ b/homeassistant/components/motion_blinds/translations/it.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato, le impostazioni di connessione sono aggiornate", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "connection_error": "Impossibile connettersi" }, @@ -9,7 +9,7 @@ "discovery_error": "Impossibile rilevare un Motion Gateway", "invalid_interface": "Interfaccia di rete non valida" }, - "flow_title": "Tende Motion", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/nl.json b/homeassistant/components/motion_blinds/translations/nl.json index 3d3d078b814..6cb69b06b87 100644 --- a/homeassistant/components/motion_blinds/translations/nl.json +++ b/homeassistant/components/motion_blinds/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd", + "already_configured": "Apparaat is al geconfigureerd, verbindingsinstellingen zijn bijgewerkt", "already_in_progress": "De configuratiestroom is al aan de gang", "connection_error": "Kan geen verbinding maken" }, @@ -9,7 +9,7 @@ "discovery_error": "Kan geen Motion Gateway vinden", "invalid_interface": "Ongeldige netwerkinterface" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/no.json b/homeassistant/components/motion_blinds/translations/no.json index 242a6647da9..d6c4708ea8c 100644 --- a/homeassistant/components/motion_blinds/translations/no.json +++ b/homeassistant/components/motion_blinds/translations/no.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert", + "already_configured": "Enheten er allerede konfigurert , tilkoblingsinnstillingene er oppdatert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "connection_error": "Tilkobling mislyktes" }, @@ -9,7 +9,7 @@ "discovery_error": "Kunne ikke oppdage en Motion Gateway", "invalid_interface": "Ugyldig nettverksgrensesnitt" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ( {ip_address} )", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/pl.json b/homeassistant/components/motion_blinds/translations/pl.json index 6161b6aa3da..2a042859b88 100644 --- a/homeassistant/components/motion_blinds/translations/pl.json +++ b/homeassistant/components/motion_blinds/translations/pl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane, ustawienia po\u0142\u0105czenia zosta\u0142y zaktualizowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", "connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, @@ -9,7 +9,7 @@ "discovery_error": "Nie uda\u0142o si\u0119 wykry\u0107 bramki ruchu", "invalid_interface": "Nieprawid\u0142owy interfejs sieciowy" }, - "flow_title": "Rolety Motion", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/pt-BR.json b/homeassistant/components/motion_blinds/translations/pt-BR.json index dcabdbd16e5..01658cea852 100644 --- a/homeassistant/components/motion_blinds/translations/pt-BR.json +++ b/homeassistant/components/motion_blinds/translations/pt-BR.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado, as configura\u00e7\u00f5es de conex\u00e3o s\u00e3o atualizadas", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "connection_error": "Falha ao conectar" }, @@ -9,7 +9,7 @@ "discovery_error": "Falha ao descobrir um Motion Gateway", "invalid_interface": "Interface de rede inv\u00e1lida" }, - "flow_title": "Cortinas de movimento", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/ru.json b/homeassistant/components/motion_blinds/translations/ru.json index 83fe77ff9eb..e7a4492bd54 100644 --- a/homeassistant/components/motion_blinds/translations/ru.json +++ b/homeassistant/components/motion_blinds/translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "connection_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, @@ -9,7 +9,7 @@ "discovery_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0448\u043b\u044e\u0437 Motion.", "invalid_interface": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441." }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/tr.json b/homeassistant/components/motion_blinds/translations/tr.json index c9a0efe2538..824569ce173 100644 --- a/homeassistant/components/motion_blinds/translations/tr.json +++ b/homeassistant/components/motion_blinds/translations/tr.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f , ba\u011flant\u0131 ayarlar\u0131 g\u00fcncellendi", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "connection_error": "Ba\u011flanma hatas\u0131" }, @@ -9,7 +9,7 @@ "discovery_error": "Motion Gateway bulunamad\u0131", "invalid_interface": "Ge\u00e7ersiz a\u011f aray\u00fcz\u00fc" }, - "flow_title": "Hareketli Panjurlar", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/motion_blinds/translations/zh-Hant.json b/homeassistant/components/motion_blinds/translations/zh-Hant.json index df300e0511f..e7fa565ba36 100644 --- a/homeassistant/components/motion_blinds/translations/zh-Hant.json +++ b/homeassistant/components/motion_blinds/translations/zh-Hant.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u9023\u7dda\u8a2d\u5b9a\u5df2\u66f4\u65b0", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "connection_error": "\u9023\u7dda\u5931\u6557" }, @@ -9,7 +9,7 @@ "discovery_error": "\u641c\u7d22 Motion \u9598\u9053\u5668\u5931\u6557", "invalid_interface": "\u7db2\u8def\u4ecb\u9762\u7121\u6548" }, - "flow_title": "Motion Blinds", + "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { diff --git a/homeassistant/components/nam/translations/hu.json b/homeassistant/components/nam/translations/hu.json index f0cf505163c..e0aa942afd8 100644 --- a/homeassistant/components/nam/translations/hu.json +++ b/homeassistant/components/nam/translations/hu.json @@ -11,7 +11,7 @@ "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "unknown": "V\u00e1ratlan hiba" }, - "flow_title": "{name}", + "flow_title": "{host}", "step": { "confirm_discovery": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani Nettigo Air Monitor-ot a {host} c\u00edmen?" diff --git a/homeassistant/components/nanoleaf/translations/fr.json b/homeassistant/components/nanoleaf/translations/fr.json index 5893635580b..5a9a8022ecb 100644 --- a/homeassistant/components/nanoleaf/translations/fr.json +++ b/homeassistant/components/nanoleaf/translations/fr.json @@ -24,5 +24,13 @@ } } } + }, + "device_automation": { + "trigger_type": { + "swipe_down": "Balayer vers le bas", + "swipe_left": "Balayer vers la gauche", + "swipe_right": "Balayer vers la droite", + "swipe_up": "Balayer vers le haut" + } } } \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/hu.json b/homeassistant/components/nanoleaf/translations/hu.json index c67c4f958de..03ed5c92828 100644 --- a/homeassistant/components/nanoleaf/translations/hu.json +++ b/homeassistant/components/nanoleaf/translations/hu.json @@ -15,7 +15,7 @@ "flow_title": "{name}", "step": { "link": { - "description": "Nyomja meg \u00e9s tartsa lenyomva a Nanoleaf bekapcsol\u00f3gombj\u00e1t 5 m\u00e1sodpercig, am\u00edg a gomb LED-je villogni nem kezd, majd kattintson a **K\u00fcld\u00e9s** gombra 30 m\u00e1sodpercen bel\u00fcl.", + "description": "Nyomja meg \u00e9s tartsa lenyomva a Nanoleaf bekapcsol\u00f3gombj\u00e1t 5 m\u00e1sodpercig, am\u00edg a gomb LED-je villogni nem kezd, majd kattintson a **Mehet** gombra 30 m\u00e1sodpercen bel\u00fcl.", "title": "Nanoleaf link" }, "user": { diff --git a/homeassistant/components/neato/translations/hu.json b/homeassistant/components/neato/translations/hu.json index 281c5cd61a9..255378eda35 100644 --- a/homeassistant/components/neato/translations/hu.json +++ b/homeassistant/components/neato/translations/hu.json @@ -4,7 +4,7 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "create_entry": { @@ -12,7 +12,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "reauth_confirm": { "title": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" diff --git a/homeassistant/components/neato/translations/it.json b/homeassistant/components/neato/translations/it.json index 51d9119eb2c..000625a0294 100644 --- a/homeassistant/components/neato/translations/it.json +++ b/homeassistant/components/neato/translations/it.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, diff --git a/homeassistant/components/nest/translations/hu.json b/homeassistant/components/nest/translations/hu.json index 2bb9d2dbaec..1f98e162a7b 100644 --- a/homeassistant/components/nest/translations/hu.json +++ b/homeassistant/components/nest/translations/hu.json @@ -4,7 +4,7 @@ "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az \u00e9rv\u00e9nyes\u00edt\u00e9si url gener\u00e1l\u00e1sa sor\u00e1n.", "invalid_access_token": "\u00c9rv\u00e9nytelen hozz\u00e1f\u00e9r\u00e9si token", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges.", "unknown_authorize_url_generation": "Ismeretlen hiba t\u00f6rt\u00e9nt a hiteles\u00edt\u00e9si link gener\u00e1l\u00e1sa sor\u00e1n." @@ -33,7 +33,7 @@ "data": { "flow_impl": "Szolg\u00e1ltat\u00f3" }, - "description": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert", + "description": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert", "title": "Hiteles\u00edt\u00e9si Szolg\u00e1ltat\u00f3" }, "link": { @@ -44,7 +44,7 @@ "title": "Nest fi\u00f3k \u00f6sszekapcsol\u00e1sa" }, "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "pubsub": { "data": { @@ -54,7 +54,7 @@ "title": "Google Cloud konfigur\u00e1l\u00e1sa" }, "reauth_confirm": { - "description": "A Nest integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie a fi\u00f3kodat", + "description": "A Nest integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie a fi\u00f3kj\u00e1t", "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" } } diff --git a/homeassistant/components/nest/translations/it.json b/homeassistant/components/nest/translations/it.json index 7c92631351e..5942414bcf3 100644 --- a/homeassistant/components/nest/translations/it.json +++ b/homeassistant/components/nest/translations/it.json @@ -3,7 +3,7 @@ "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", "invalid_access_token": "Token di accesso non valido", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", diff --git a/homeassistant/components/nest/translations/pl.json b/homeassistant/components/nest/translations/pl.json index c81e5f180b9..5e9b9895db4 100644 --- a/homeassistant/components/nest/translations/pl.json +++ b/homeassistant/components/nest/translations/pl.json @@ -27,7 +27,7 @@ "code": "Token dost\u0119pu" }, "description": "Aby po\u0142\u0105czy\u0107 swoje konto Google, [authorize your account]({url}). \n\nPo autoryzacji skopiuj i wklej podany poni\u017cej token uwierzytelniaj\u0105cy.", - "title": "Po\u0142\u0105cz z kontem Google" + "title": "Po\u0142\u0105czenie z kontem Google" }, "init": { "data": { diff --git a/homeassistant/components/netatmo/translations/hu.json b/homeassistant/components/netatmo/translations/hu.json index ae781d86ca8..51c46edbe86 100644 --- a/homeassistant/components/netatmo/translations/hu.json +++ b/homeassistant/components/netatmo/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, @@ -12,7 +12,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "reauth_confirm": { "description": "A Netatmo integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie a fi\u00f3kj\u00e1t.", diff --git a/homeassistant/components/netatmo/translations/it.json b/homeassistant/components/netatmo/translations/it.json index e26cd64705f..b2210a4375c 100644 --- a/homeassistant/components/netatmo/translations/it.json +++ b/homeassistant/components/netatmo/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." diff --git a/homeassistant/components/netatmo/translations/pt-BR.json b/homeassistant/components/netatmo/translations/pt-BR.json index 32cc610f596..b47c0ea3646 100644 --- a/homeassistant/components/netatmo/translations/pt-BR.json +++ b/homeassistant/components/netatmo/translations/pt-BR.json @@ -52,7 +52,7 @@ "lon_ne": "Longitude nordeste", "lon_sw": "Longitude sudoeste", "mode": "C\u00e1lculo", - "show_on_map": "Mostrar no mapa?" + "show_on_map": "Mostrar no mapa" }, "description": "Configure um sensor meteorol\u00f3gico p\u00fablico para uma \u00e1rea.", "title": "Sensor meteorol\u00f3gico p\u00fablico Netatmo" diff --git a/homeassistant/components/nfandroidtv/translations/ca.json b/homeassistant/components/nfandroidtv/translations/ca.json index 861ad41a39b..0eda4938d9d 100644 --- a/homeassistant/components/nfandroidtv/translations/ca.json +++ b/homeassistant/components/nfandroidtv/translations/ca.json @@ -13,7 +13,7 @@ "host": "Amfitri\u00f3", "name": "Nom" }, - "description": "Aquesta integraci\u00f3 necessita l'aplicaci\u00f3 Notificacions per a Android TV. \n\nPer Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPer Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\nHauries de configurar o b\u00e9 una reserva DHCP al router (consulta el manual del teu rounter) o b\u00e9 adre\u00e7a IP est\u00e0tica al dispositiu. Si no o fas, el disositiu acabar\u00e0 deixant d'estar disponible.", + "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits.", "title": "Notificacions per a Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/de.json b/homeassistant/components/nfandroidtv/translations/de.json index b3adce9ac07..d9df9058d13 100644 --- a/homeassistant/components/nfandroidtv/translations/de.json +++ b/homeassistant/components/nfandroidtv/translations/de.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Name" }, - "description": "Diese Integration erfordert die App \"Benachrichtigungen f\u00fcr Android TV\".\n\nF\u00fcr Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nF\u00fcr Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nDu solltest entweder eine DHCP-Reservierung auf deinem Router (siehe Benutzerhandbuch deines Routers) oder eine statische IP-Adresse auf dem Ger\u00e4t einrichten. Andernfalls wird das Ger\u00e4t irgendwann nicht mehr verf\u00fcgbar sein.", + "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind.", "title": "Benachrichtigungen f\u00fcr Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/en.json b/homeassistant/components/nfandroidtv/translations/en.json index f117428df35..3c1383b91b8 100644 --- a/homeassistant/components/nfandroidtv/translations/en.json +++ b/homeassistant/components/nfandroidtv/translations/en.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Name" }, - "description": "This integration requires the Notifications for Android TV app.\n\nFor Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFor Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nYou should set up either DHCP reservation on your router (refer to your router's user manual) or a static IP address on the device. If not, the device will eventually become unavailable.", + "description": "Please refer to the documentation to make sure all requirements are met.", "title": "Notifications for Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/et.json b/homeassistant/components/nfandroidtv/translations/et.json index f2405ab1421..f567795b608 100644 --- a/homeassistant/components/nfandroidtv/translations/et.json +++ b/homeassistant/components/nfandroidtv/translations/et.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Nimi" }, - "description": "See sidumine n\u00f5uab Android TV rakenduse Notifications for Android TV kasutamist.\n\nAndroid TV jaoks: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFire TV jaoks: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nPead seadma ruuterile kas DHCP-reservatsiooni (vt ruuteri kasutusjuhendit) v\u00f5i seadme staatilise IP-aadressi. Vastasel juhul muutub seade l\u00f5puks k\u00e4ttesaamatuks.", + "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud.", "title": "Android TV / Fire TV teavitused" } } diff --git a/homeassistant/components/nfandroidtv/translations/fr.json b/homeassistant/components/nfandroidtv/translations/fr.json index 6d00852889b..7c69bbc6ac0 100644 --- a/homeassistant/components/nfandroidtv/translations/fr.json +++ b/homeassistant/components/nfandroidtv/translations/fr.json @@ -13,7 +13,7 @@ "host": "H\u00f4te", "name": "Nom" }, - "description": "Cette int\u00e9gration n\u00e9cessite l'application Notifications pour Android TV. \n\nPour Android TV\u00a0: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPour Fire TV\u00a0: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\nVous devez configurer soit une r\u00e9servation DHCP sur votre routeur (reportez-vous au manuel d'utilisation de votre routeur) soit une adresse IP statique sur l'appareil. Sinon, l'appareil finira par devenir indisponible.", + "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es.", "title": "Notifications pour Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/he.json b/homeassistant/components/nfandroidtv/translations/he.json index dc4d71c70ca..65eefcd05b4 100644 --- a/homeassistant/components/nfandroidtv/translations/he.json +++ b/homeassistant/components/nfandroidtv/translations/he.json @@ -13,7 +13,7 @@ "host": "\u05de\u05d0\u05e8\u05d7", "name": "\u05e9\u05dd" }, - "description": "\u05e9\u05d9\u05dc\u05d5\u05d1 \u05d6\u05d4 \u05d3\u05d5\u05e8\u05e9 \u05d0\u05ea \u05d4\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d9\u05d9\u05e9\u05d5\u05dd \u05d4\u05ea\u05e8\u05d0\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3.\n\n\u05e2\u05d1\u05d5\u05e8 \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n\u05e2\u05d1\u05d5\u05d3 \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05ea \u05d0\u05de\u05d6\u05d5\u05df: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\n\u05e2\u05dc\u05d9\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d4\u05d6\u05de\u05e0\u05ea DHCP \u05d1\u05e0\u05ea\u05d1 \u05e9\u05dc\u05da (\u05e2\u05d9\u05d9\u05df \u05d1\u05de\u05d3\u05e8\u05d9\u05da \u05dc\u05de\u05e9\u05ea\u05de\u05e9 \u05e9\u05dc \u05d4\u05e0\u05ea\u05d1) \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea IP \u05e1\u05d8\u05d8\u05d9\u05ea \u05d1\u05de\u05db\u05e9\u05d9\u05e8. \u05d0\u05dd \u05dc\u05d0, \u05d4\u05d4\u05ea\u05e7\u05df \u05d9\u05d4\u05e4\u05d5\u05da \u05d1\u05e1\u05d5\u05e4\u05d5 \u05e9\u05dc \u05d3\u05d1\u05e8 \u05dc\u05dc\u05d0 \u05d6\u05de\u05d9\u05df.", + "description": "\u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05ea\u05d9\u05e2\u05d5\u05d3 \u05db\u05d3\u05d9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05db\u05dc \u05d4\u05d3\u05e8\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05e7\u05d9\u05d9\u05de\u05d5\u05ea.", "title": "\u05d4\u05ea\u05e8\u05d0\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3 / \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d0\u05de\u05d6\u05d5\u05df" } } diff --git a/homeassistant/components/nfandroidtv/translations/hu.json b/homeassistant/components/nfandroidtv/translations/hu.json index c0dc8d679d6..0c8d0567483 100644 --- a/homeassistant/components/nfandroidtv/translations/hu.json +++ b/homeassistant/components/nfandroidtv/translations/hu.json @@ -11,9 +11,9 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, - "description": "Ehhez az integr\u00e1ci\u00f3hoz az \u00c9rtes\u00edt\u00e9sek az Android TV alkalmaz\u00e1shoz sz\u00fcks\u00e9ges. \n\nAndroid TV eset\u00e9n: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nA Fire TV eset\u00e9ben: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\nBe kell \u00e1ll\u00edtania a DHCP -foglal\u00e1st az \u00fatv\u00e1laszt\u00f3n (l\u00e1sd az \u00fatv\u00e1laszt\u00f3 felhaszn\u00e1l\u00f3i k\u00e9zik\u00f6nyv\u00e9t), vagy egy statikus IP -c\u00edmet az eszk\u00f6z\u00f6n. Ha nem, az eszk\u00f6z v\u00e9g\u00fcl el\u00e9rhetetlenn\u00e9 v\u00e1lik.", + "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl.", "title": "\u00c9rtes\u00edt\u00e9sek Android TV / Fire TV eset\u00e9n" } } diff --git a/homeassistant/components/nfandroidtv/translations/id.json b/homeassistant/components/nfandroidtv/translations/id.json index fd70679d5a4..de4ded1e98a 100644 --- a/homeassistant/components/nfandroidtv/translations/id.json +++ b/homeassistant/components/nfandroidtv/translations/id.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Nama" }, - "description": "Integrasi ini memerlukan aplikasi Notifikasi untuk Android TV.\n\nUntuk Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nUntuk Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nAnda harus mengatur reservasi DHCP di router Anda (lihat manual pengguna router Anda) atau alamat IP statis pada perangkat. Jika tidak, perangkat akhirnya akan menjadi tidak tersedia.", + "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi.", "title": "Notifikasi untuk Android TV/Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/it.json b/homeassistant/components/nfandroidtv/translations/it.json index 6251a31e3bc..83eea49b419 100644 --- a/homeassistant/components/nfandroidtv/translations/it.json +++ b/homeassistant/components/nfandroidtv/translations/it.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Nome" }, - "description": "Questa integrazione richiede l'app Notifiche per Android TV. \n\nPer Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPer Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\nDovresti configurare la prenotazione DHCP sul router (fai riferimento al manuale utente del router) o un indirizzo IP statico sul dispositivo. In caso contrario, il dispositivo alla fine non sar\u00e0 pi\u00f9 disponibile.", + "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti.", "title": "Notifiche per Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/nl.json b/homeassistant/components/nfandroidtv/translations/nl.json index acd936abe70..10c9f44a94a 100644 --- a/homeassistant/components/nfandroidtv/translations/nl.json +++ b/homeassistant/components/nfandroidtv/translations/nl.json @@ -13,7 +13,7 @@ "host": "Host", "name": "Naam" }, - "description": "Voor deze integratie is de app Notifications for Android TV vereist.\n\nVoor Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nVoor Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nU moet een DHCP-reservering op uw router instellen (raadpleeg de gebruikershandleiding van uw router) of een statisch IP-adres op het apparaat instellen. Zo niet, dan zal het apparaat uiteindelijk onbeschikbaar worden.", + "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten is voldaan.", "title": "Meldingen voor Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/no.json b/homeassistant/components/nfandroidtv/translations/no.json index e8aea574c96..8d59ca40b0d 100644 --- a/homeassistant/components/nfandroidtv/translations/no.json +++ b/homeassistant/components/nfandroidtv/translations/no.json @@ -13,7 +13,7 @@ "host": "Vert", "name": "Navn" }, - "description": "Denne integrasjonen krever Notifications for Android TV -appen. \n\n For Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n For Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\n Du b\u00f8r konfigurere enten DHCP -reservasjon p\u00e5 ruteren din (se brukerh\u00e5ndboken til ruteren din) eller en statisk IP -adresse p\u00e5 enheten. Hvis ikke, vil enheten til slutt bli utilgjengelig.", + "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt.", "title": "Varsler for Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/pl.json b/homeassistant/components/nfandroidtv/translations/pl.json index 4dd742b7c1f..a42335ceb10 100644 --- a/homeassistant/components/nfandroidtv/translations/pl.json +++ b/homeassistant/components/nfandroidtv/translations/pl.json @@ -13,7 +13,7 @@ "host": "Nazwa hosta lub adres IP", "name": "Nazwa" }, - "description": "Ta integracja wymaga aplikacji Powiadomienia dla Androida TV. \n\nAndroid TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\nNale\u017cy skonfigurowa\u0107 rezerwacj\u0119 DHCP na routerze (patrz instrukcja obs\u0142ugi routera) lub ustawi\u0107 statyczny adres IP na urz\u0105dzeniu. Je\u015bli tego nie zrobisz, urz\u0105dzenie ostatecznie stanie si\u0119 niedost\u0119pne.", + "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione.", "title": "Powiadomienia dla Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/pt-BR.json b/homeassistant/components/nfandroidtv/translations/pt-BR.json index 43b7a296c21..f4488408b42 100644 --- a/homeassistant/components/nfandroidtv/translations/pt-BR.json +++ b/homeassistant/components/nfandroidtv/translations/pt-BR.json @@ -13,7 +13,7 @@ "host": "Nome do host", "name": "Nome" }, - "description": "Essa integra\u00e7\u00e3o requer as Notifica\u00e7\u00f5es para o aplicativo Android TV.\n\nPara Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPara Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nVoc\u00ea deve configurar a reserva DHCP no roteador (consulte o manual do usu\u00e1rio do roteador) ou um endere\u00e7o IP est\u00e1tico no dispositivo. Se n\u00e3o, o dispositivo acabar\u00e1 por ficar indispon\u00edvel.", + "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos.", "title": "Notifica\u00e7\u00f5es para Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/ru.json b/homeassistant/components/nfandroidtv/translations/ru.json index ce0d4651dfc..12451d4735b 100644 --- a/homeassistant/components/nfandroidtv/translations/ru.json +++ b/homeassistant/components/nfandroidtv/translations/ru.json @@ -13,7 +13,7 @@ "host": "\u0425\u043e\u0441\u0442", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \"Notifications for Android TV\". \n\n\u0414\u043b\u044f Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n\u0414\u043b\u044f Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\n\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 DHCP \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0435 (\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u0412\u0430\u0448\u0435\u043c\u0443 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0443) \u0438\u043b\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 IP-\u0430\u0434\u0440\u0435\u0441 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c.", + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b.", "title": "Notifications for Android TV / Fire TV" } } diff --git a/homeassistant/components/nfandroidtv/translations/tr.json b/homeassistant/components/nfandroidtv/translations/tr.json index b894cf83687..835e61eea66 100644 --- a/homeassistant/components/nfandroidtv/translations/tr.json +++ b/homeassistant/components/nfandroidtv/translations/tr.json @@ -13,7 +13,7 @@ "host": "Sunucu", "name": "Ad" }, - "description": "Bu entegrasyon, Android TV i\u00e7in Bildirimler uygulamas\u0131n\u0131 gerektirir. \n\n Android TV i\u00e7in: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n Fire TV i\u00e7in: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\n Y\u00f6nlendiricinizde DHCP rezervasyonu (y\u00f6nlendiricinizin kullan\u0131m k\u0131lavuzuna bak\u0131n) veya cihazda statik bir IP adresi ayarlamal\u0131s\u0131n\u0131z. Aksi takdirde, cihaz sonunda kullan\u0131lamaz hale gelecektir.", + "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n.", "title": "Android TV / Fire TV i\u00e7in Bildirimler" } } diff --git a/homeassistant/components/nfandroidtv/translations/zh-Hant.json b/homeassistant/components/nfandroidtv/translations/zh-Hant.json index b16d55a44bd..755ccdfeec8 100644 --- a/homeassistant/components/nfandroidtv/translations/zh-Hant.json +++ b/homeassistant/components/nfandroidtv/translations/zh-Hant.json @@ -13,7 +13,7 @@ "host": "\u4e3b\u6a5f\u7aef", "name": "\u540d\u7a31" }, - "description": "\u6b64\u6574\u5408\u9700\u8981\u5b89\u88dd Notifications for Android TV App\u3002\n\nAndroid TV \u7248\u672c\uff1ahttps://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFire TV \u7248\u672c\uff1ahttps://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\n\u8acb\u65bc\u8def\u7531\u5668\uff08\u8acb\u53c3\u8003\u8def\u7531\u5668\u624b\u518a\uff09\u4e2d\u8a2d\u5b9a\u4fdd\u7559\u88dd\u7f6e DHCP IP \u6216\u975c\u614b IP\u3002\u5047\u5982\u672a\u9032\u884c\u6b64\u8a2d\u5b9a\uff0c\u88dd\u7f6e\u53ef\u80fd\u6703\u8b8a\u6210\u4e0d\u53ef\u7528\u3002", + "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002", "title": "Android TV / Fire TV \u901a\u77e5" } } diff --git a/homeassistant/components/nina/translations/zh-Hant.json b/homeassistant/components/nina/translations/zh-Hant.json index 0ba4436722d..212c65070d8 100644 --- a/homeassistant/components/nina/translations/zh-Hant.json +++ b/homeassistant/components/nina/translations/zh-Hant.json @@ -5,22 +5,22 @@ }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", - "no_selection": "\u8acb\u81f3\u5c11\u9078\u64c7\u4e00\u500b\u57ce\u5e02/\u570b\u5bb6", + "no_selection": "\u8acb\u81f3\u5c11\u9078\u64c7\u4e00\u500b\u57ce\u5e02/\u7e23\u5e02", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { "user": { "data": { - "_a_to_d": "\u57ce\u5e02/\u570b\u5bb6\uff08A-D\uff09", - "_e_to_h": "\u57ce\u5e02/\u570b\u5bb6\uff08E-H\uff09", - "_i_to_l": "\u57ce\u5e02/\u570b\u5bb6\uff08I-L\uff09", - "_m_to_q": "\u57ce\u5e02/\u570b\u5bb6\uff08M-Q\uff09", - "_r_to_u": "\u57ce\u5e02/\u570b\u5bb6\uff08R-U\uff09", - "_v_to_z": "\u57ce\u5e02/\u570b\u5bb6\uff08V-Z\uff09", + "_a_to_d": "\u57ce\u5e02/\u7e23\u5e02\uff08A-D\uff09", + "_e_to_h": "\u57ce\u5e02/\u7e23\u5e02\uff08E-H\uff09", + "_i_to_l": "\u57ce\u5e02/\u7e23\u5e02\uff08I-L\uff09", + "_m_to_q": "\u57ce\u5e02/\u7e23\u5e02\uff08M-Q\uff09", + "_r_to_u": "\u57ce\u5e02/\u7e23\u5e02\uff08R-U\uff09", + "_v_to_z": "\u57ce\u5e02/\u7e23\u5e02\uff08V-Z\uff09", "corona_filter": "\u79fb\u9664 Corona \u8b66\u544a", - "slots": "\u6bcf\u500b\u57ce\u5e02/\u570b\u5bb6\u6700\u5927\u8b66\u544a\u503c" + "slots": "\u6bcf\u500b\u57ce\u5e02/\u7e23\u5e02\u6700\u5927\u8b66\u544a\u503c" }, - "title": "\u9078\u64c7\u57ce\u5e02/\u570b\u5bb6" + "title": "\u9078\u64c7\u57ce\u5e02/\u7e23\u5e02" } } } diff --git a/homeassistant/components/nmap_tracker/translations/en.json b/homeassistant/components/nmap_tracker/translations/en.json index ae4175e0f14..ee615b87e91 100644 --- a/homeassistant/components/nmap_tracker/translations/en.json +++ b/homeassistant/components/nmap_tracker/translations/en.json @@ -30,7 +30,8 @@ "home_interval": "Minimum number of minutes between scans of active devices (preserve battery)", "hosts": "Network addresses (comma separated) to scan", "interval_seconds": "Scan interval", - "scan_options": "Raw configurable scan options for Nmap" + "scan_options": "Raw configurable scan options for Nmap", + "track_new_devices": "Track new devices" }, "description": "Configure hosts to be scanned by Nmap. Network address and excludes can be IP Addresses (192.168.1.1), IP Networks (192.168.0.0/24) or IP Ranges (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/et.json b/homeassistant/components/nmap_tracker/translations/et.json index 538f1127448..ac23cb7004f 100644 --- a/homeassistant/components/nmap_tracker/translations/et.json +++ b/homeassistant/components/nmap_tracker/translations/et.json @@ -9,9 +9,9 @@ "step": { "user": { "data": { - "exclude": "V\u00f5rguaadressid (komadega eraldatud), mis tuleb skaneerimisest v\u00e4lja j\u00e4tta", + "exclude": "V\u00f5rguaadressid (komadega eraldatud) mis tuleb skaneerimisest v\u00e4lja j\u00e4tta", "home_interval": "Minimaalne minutite arv aktiivsete seadmete skaneerimise vahel (aku s\u00e4ilitamine)", - "hosts": "Skaneeritavad v\u00f5rgu aadressid (komadega eraldatud)", + "hosts": "V\u00f5rgu aadressid (komadega eraldatud)", "scan_options": "Nmapi algseadistavad skaneerimisvalikud" }, "description": "Konfigureeri hostid, mida Nmap skannib. V\u00f5rguaadress ja v\u00e4ljaj\u00e4etud v\u00f5ivad olla IP-aadressid (192.168.1.1), IP-v\u00f5rgud (192.168.0.0/24) v\u00f5i IP-vahemikud (192.168.1.0-32)." diff --git a/homeassistant/components/nmap_tracker/translations/hu.json b/homeassistant/components/nmap_tracker/translations/hu.json index 7385f12b3df..f54cf208e92 100644 --- a/homeassistant/components/nmap_tracker/translations/hu.json +++ b/homeassistant/components/nmap_tracker/translations/hu.json @@ -9,9 +9,9 @@ "step": { "user": { "data": { - "exclude": "H\u00e1l\u00f3zati c\u00edmek (vessz\u0151vel elv\u00e1lasztva), amelyeket kiz\u00e1r\u00e1sra ker\u00fclnek a vizsg\u00e1latb\u00f3l", + "exclude": "H\u00e1l\u00f3zati c\u00edmek (vessz\u0151vel elv\u00e1lasztva), amelyeket kiz\u00e1r\u00e1sra ker\u00fclnek a keres\u00e9sb\u0151l", "home_interval": "Minim\u00e1lis percsz\u00e1m az akt\u00edv eszk\u00f6z\u00f6k vizsg\u00e1lata k\u00f6z\u00f6tt (akkumul\u00e1tor k\u00edm\u00e9l\u00e9se)", - "hosts": "H\u00e1l\u00f3zati c\u00edmek (vessz\u0151vel elv\u00e1lasztva) a beolvas\u00e1shoz", + "hosts": "H\u00e1l\u00f3zati c\u00edmek (vessz\u0151vel elv\u00e1lasztva) a keres\u00e9shez", "scan_options": "Nyersen konfigur\u00e1lhat\u00f3 szkennel\u00e9si lehet\u0151s\u00e9gek az Nmap sz\u00e1m\u00e1ra" }, "description": "\u00c1ll\u00edtsa be a hogy milyen c\u00edmeket szkenneljen az Nmap. A h\u00e1l\u00f3zati c\u00edm lehet IP-c\u00edm (pl. 192.168.1.1), IP-h\u00e1l\u00f3zat (pl. 192.168.0.0/24) vagy IP-tartom\u00e1ny (pl. 192.168.1.0-32)." diff --git a/homeassistant/components/nmap_tracker/translations/no.json b/homeassistant/components/nmap_tracker/translations/no.json index acd25607c4f..1de32bed121 100644 --- a/homeassistant/components/nmap_tracker/translations/no.json +++ b/homeassistant/components/nmap_tracker/translations/no.json @@ -11,7 +11,7 @@ "data": { "exclude": "Nettverksadresser (kommaseparert) for \u00e5 ekskludere fra skanning", "home_interval": "Minimum antall minutter mellom skanninger av aktive enheter (lagre batteri)", - "hosts": "Nettverksadresser (kommaseparert) for \u00e5 skanne", + "hosts": "Nettverksadresser (atskilt med komma) som skal skannes", "scan_options": "R\u00e5 konfigurerbare skannealternativer for Nmap" }, "description": "Konfigurer verter som skal skannes av Nmap. Nettverksadresse og ekskluderer kan v\u00e6re IP-adresser (192.168.1.1), IP-nettverk (192.168.0.0/24) eller IP-omr\u00e5der (192.168.1.0-32)." @@ -28,7 +28,7 @@ "consider_home": "Sekunder \u00e5 vente til du merker en enhetssporing som ikke hjemme etter at den ikke er blitt sett.", "exclude": "Nettverksadresser (kommaseparert) for \u00e5 ekskludere fra skanning", "home_interval": "Minimum antall minutter mellom skanninger av aktive enheter (lagre batteri)", - "hosts": "Nettverksadresser (kommaseparert) for \u00e5 skanne", + "hosts": "Nettverksadresser (atskilt med komma) som skal skannes", "interval_seconds": "Skanneintervall", "scan_options": "R\u00e5 konfigurerbare skannealternativer for Nmap", "track_new_devices": "Spor nye enheter" diff --git a/homeassistant/components/nmap_tracker/translations/pt-BR.json b/homeassistant/components/nmap_tracker/translations/pt-BR.json index bf058495cb9..a80213ce5d6 100644 --- a/homeassistant/components/nmap_tracker/translations/pt-BR.json +++ b/homeassistant/components/nmap_tracker/translations/pt-BR.json @@ -9,9 +9,9 @@ "step": { "user": { "data": { - "exclude": "Endere\u00e7os de rede (separados por v\u00edrgula) para excluir do escaneamento", + "exclude": "Endere\u00e7os de rede (separados por v\u00edrgula) para excluir da verifica\u00e7\u00e3o", "home_interval": "N\u00famero m\u00ednimo de minutos entre escaneamento de dispositivos ativos (preservar bateria)", - "hosts": "Endere\u00e7os de rede (separados por v\u00edrgula) para escanear", + "hosts": "Endere\u00e7os de rede (separados por v\u00edrgula) para digitalizar", "scan_options": "Op\u00e7\u00f5es de escaneamento bruto configur\u00e1veis para Nmap" }, "description": "Configure os hosts a serem verificados pelo Nmap. O endere\u00e7o de rede e as exclus\u00f5es podem ser endere\u00e7os IP (192.168.1.1), redes IP (192.168.0.0/24) ou intervalos de IP (192.168.1.0-32)." @@ -26,14 +26,14 @@ "init": { "data": { "consider_home": "Segundos para esperar at\u00e9 marcar um rastreador de dispositivo como fora de casa depois de n\u00e3o ser visto.", - "exclude": "Endere\u00e7os de rede (separados por v\u00edrgula) para excluir do escaneamento", + "exclude": "Endere\u00e7os de rede (separados por v\u00edrgula) para excluir da verifica\u00e7\u00e3o", "home_interval": "N\u00famero m\u00ednimo de minutos entre escaneamento de dispositivos ativos (preservar bateria)", - "hosts": "Endere\u00e7os de rede (separados por v\u00edrgula) para escanear", + "hosts": "Endere\u00e7os de rede (separados por v\u00edrgula) para digitalizar", "interval_seconds": "Intervalo de varredura", - "scan_options": "Op\u00e7\u00f5es de varredura configur\u00e1veis brutas para Nmap", + "scan_options": "Op\u00e7\u00f5es de escaneamento bruto configur\u00e1veis para Nmap", "track_new_devices": "Rastrear novos dispositivos" }, - "description": "Configure hosts a serem digitalizados pelo Nmap. O endere\u00e7o de rede e exclus\u00f5es podem ser Endere\u00e7os IP (192.168.1.1), Redes IP (192.168.0.0/24) ou Faixas IP (192.168.1.0-32)." + "description": "Configure os hosts a serem verificados pelo Nmap. O endere\u00e7o de rede e as exclus\u00f5es podem ser endere\u00e7os IP (192.168.1.1), redes IP (192.168.0.0/24) ou intervalos de IP (192.168.1.0-32)." } } }, diff --git a/homeassistant/components/nmap_tracker/translations/tr.json b/homeassistant/components/nmap_tracker/translations/tr.json index 2d277e979b3..8e5dadb8100 100644 --- a/homeassistant/components/nmap_tracker/translations/tr.json +++ b/homeassistant/components/nmap_tracker/translations/tr.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "exclude": "Taraman\u0131n d\u0131\u015f\u0131nda tutulacak a\u011f adresleri (virg\u00fcl ayr\u0131lm\u0131\u015f)", + "exclude": "Tarama d\u0131\u015f\u0131nda b\u0131rak\u0131lacak a\u011f adresleri (virg\u00fclle ayr\u0131lm\u0131\u015f)", "home_interval": "Aktif cihazlar\u0131n taranmas\u0131 aras\u0131ndaki minimum dakika say\u0131s\u0131 (pilden tasarruf edin)", "hosts": "Taranacak a\u011f adresleri (virg\u00fclle ayr\u0131lm\u0131\u015f)", "scan_options": "Nmap i\u00e7in ham yap\u0131land\u0131r\u0131labilir tarama se\u00e7enekleri" @@ -26,7 +26,7 @@ "init": { "data": { "consider_home": "Bir cihaz izleyicisi g\u00f6r\u00fclmedikten sonra evde de\u011fil olarak i\u015faretlenene kadar beklemek i\u00e7in saniyeler kald\u0131.", - "exclude": "Taraman\u0131n d\u0131\u015f\u0131nda tutulacak a\u011f adresleri (virg\u00fcl ayr\u0131lm\u0131\u015f)", + "exclude": "Tarama d\u0131\u015f\u0131nda b\u0131rak\u0131lacak a\u011f adresleri (virg\u00fclle ayr\u0131lm\u0131\u015f)", "home_interval": "Aktif cihazlar\u0131n taranmas\u0131 aras\u0131ndaki minimum dakika say\u0131s\u0131 (pilden tasarruf edin)", "hosts": "Taranacak a\u011f adresleri (virg\u00fclle ayr\u0131lm\u0131\u015f)", "interval_seconds": "Tarama aral\u0131\u011f\u0131", diff --git a/homeassistant/components/nzbget/translations/hu.json b/homeassistant/components/nzbget/translations/hu.json index 6db44f83c28..928d054707b 100644 --- a/homeassistant/components/nzbget/translations/hu.json +++ b/homeassistant/components/nzbget/translations/hu.json @@ -12,7 +12,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", diff --git a/homeassistant/components/ondilo_ico/translations/hu.json b/homeassistant/components/ondilo_ico/translations/hu.json index a6979721779..91e05b319b6 100644 --- a/homeassistant/components/ondilo_ico/translations/hu.json +++ b/homeassistant/components/ondilo_ico/translations/hu.json @@ -9,7 +9,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/ondilo_ico/translations/it.json b/homeassistant/components/ondilo_ico/translations/it.json index 42536508716..f1e5fe58fb1 100644 --- a/homeassistant/components/ondilo_ico/translations/it.json +++ b/homeassistant/components/ondilo_ico/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione." + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione." }, "create_entry": { "default": "Autenticazione riuscita" diff --git a/homeassistant/components/onewire/translations/es.json b/homeassistant/components/onewire/translations/es.json index a2d8da218ba..d789e46875f 100644 --- a/homeassistant/components/onewire/translations/es.json +++ b/homeassistant/components/onewire/translations/es.json @@ -22,5 +22,10 @@ "title": "Configurar 1 cable" } } + }, + "options": { + "error": { + "device_not_selected": "Seleccionar los dispositivos a configurar" + } } } \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/fr.json b/homeassistant/components/onewire/translations/fr.json index 0c75fb23ad1..08e3d3cf18b 100644 --- a/homeassistant/components/onewire/translations/fr.json +++ b/homeassistant/components/onewire/translations/fr.json @@ -29,7 +29,8 @@ }, "step": { "ack_no_options": { - "description": "Il n'y a pas d'option pour l'impl\u00e9mentation de SysBus" + "description": "Il n'y a pas d'option pour l'impl\u00e9mentation de SysBus", + "title": "Options de bus syst\u00e8me OneWire" }, "configure_device": { "data": { diff --git a/homeassistant/components/onewire/translations/nl.json b/homeassistant/components/onewire/translations/nl.json index cc310900d92..3ea1fd8e13c 100644 --- a/homeassistant/components/onewire/translations/nl.json +++ b/homeassistant/components/onewire/translations/nl.json @@ -29,6 +29,10 @@ }, "step": { "ack_no_options": { + "data": { + "one": "Leeg", + "other": "Ander" + }, "description": "Er zijn geen opties voor de SysBus implementatie", "title": "OneWire SysBus opties" }, diff --git a/homeassistant/components/onvif/translations/hu.json b/homeassistant/components/onvif/translations/hu.json index 6b777b76ab0..3754243a740 100644 --- a/homeassistant/components/onvif/translations/hu.json +++ b/homeassistant/components/onvif/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "no_h264": "Nem voltak el\u00e9rhet\u0151 H264 streamek. Ellen\u0151rizze a profil konfigur\u00e1ci\u00f3j\u00e1t a k\u00e9sz\u00fcl\u00e9ken.", "no_mac": "Nem siker\u00fclt konfigur\u00e1lni az egyedi azonos\u00edt\u00f3t az ONVIF eszk\u00f6zh\u00f6z.", "onvif_error": "Hiba t\u00f6rt\u00e9nt az ONVIF eszk\u00f6z be\u00e1ll\u00edt\u00e1sakor. Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt ellen\u0151rizze a napl\u00f3kat." @@ -21,7 +21,7 @@ "configure": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" @@ -44,7 +44,7 @@ "manual_input": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "port": "Port" }, "title": "ONVIF eszk\u00f6z konfigur\u00e1l\u00e1sa" @@ -53,7 +53,7 @@ "data": { "auto": "Automatikus keres\u00e9s" }, - "description": "A K\u00fcld\u00e9s gombra kattintva olyan ONVIF-eszk\u00f6z\u00f6ket keres\u00fcnk a h\u00e1l\u00f3zat\u00e1ban, amelyek t\u00e1mogatj\u00e1k az S profilt.\n\nEgyes gy\u00e1rt\u00f3k alap\u00e9rtelmez\u00e9s szerint elkezdt\u00e9k letiltani az ONVIF-et. Ellen\u0151rizze, hogy az ONVIF enged\u00e9lyezve van-e a kamera konfigur\u00e1ci\u00f3j\u00e1ban.", + "description": "A k\u00f6vetkez\u0151 l\u00e9p\u00e9sben olyan ONVIF-eszk\u00f6z\u00f6ket keres\u00fcnk a h\u00e1l\u00f3zat\u00e1ban, amelyek t\u00e1mogatj\u00e1k az S profilt.\n\nEgyes gy\u00e1rt\u00f3k alap\u00e9rtelmez\u00e9s szerint elkezdt\u00e9k letiltani az ONVIF-et. Ellen\u0151rizze, hogy az ONVIF enged\u00e9lyezve van-e a kamera konfigur\u00e1ci\u00f3j\u00e1ban.", "title": "ONVIF eszk\u00f6z be\u00e1ll\u00edt\u00e1sa" } } diff --git a/homeassistant/components/open_meteo/translations/ca.json b/homeassistant/components/open_meteo/translations/ca.json index 4fb1a502b6f..a401eeaa99e 100644 --- a/homeassistant/components/open_meteo/translations/ca.json +++ b/homeassistant/components/open_meteo/translations/ca.json @@ -5,7 +5,7 @@ "data": { "zone": "Zona" }, - "description": "Selecciona la ubicaci\u00f3 que s'utilitzar\u00e0 per a la predicci\u00f3 meteorol\u00f2gica" + "description": "Selecciona la ubicaci\u00f3 que s'utilitzar\u00e0 per a la previsi\u00f3 meteorol\u00f2gica" } } } diff --git a/homeassistant/components/opentherm_gw/translations/hu.json b/homeassistant/components/opentherm_gw/translations/hu.json index 3127dc523ce..e2a284d7dd1 100644 --- a/homeassistant/components/opentherm_gw/translations/hu.json +++ b/homeassistant/components/opentherm_gw/translations/hu.json @@ -10,7 +10,7 @@ "data": { "device": "El\u00e9r\u00e9si \u00fat vagy URL", "id": "ID", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "title": "OpenTherm \u00e1tj\u00e1r\u00f3" } diff --git a/homeassistant/components/openweathermap/translations/ca.json b/homeassistant/components/openweathermap/translations/ca.json index b3b1ebbb85e..f304a8d4f9f 100644 --- a/homeassistant/components/openweathermap/translations/ca.json +++ b/homeassistant/components/openweathermap/translations/ca.json @@ -15,9 +15,9 @@ "latitude": "Latitud", "longitude": "Longitud", "mode": "Mode", - "name": "Nom de la integraci\u00f3" + "name": "Nom" }, - "description": "Configura la integraci\u00f3 OpenWeatherMap. Per generar la clau API, ves a https://openweathermap.org/appid", + "description": "Per generar la clau API, v\u00e9s a https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/de.json b/homeassistant/components/openweathermap/translations/de.json index 615a642a859..f74065ca1a1 100644 --- a/homeassistant/components/openweathermap/translations/de.json +++ b/homeassistant/components/openweathermap/translations/de.json @@ -15,9 +15,9 @@ "latitude": "Breitengrad", "longitude": "L\u00e4ngengrad", "mode": "Modus", - "name": "Name der Integration" + "name": "Name" }, - "description": "Richte die OpenWeatherMap-Integration ein. Zum Generieren des API-Schl\u00fcssels gehe auf https://openweathermap.org/appid", + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/en.json b/homeassistant/components/openweathermap/translations/en.json index 57feaccf6b4..55f8d0a2338 100644 --- a/homeassistant/components/openweathermap/translations/en.json +++ b/homeassistant/components/openweathermap/translations/en.json @@ -15,9 +15,9 @@ "latitude": "Latitude", "longitude": "Longitude", "mode": "Mode", - "name": "Name of the integration" + "name": "Name" }, - "description": "Set up OpenWeatherMap integration. To generate API key go to https://openweathermap.org/appid", + "description": "To generate API key go to https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/et.json b/homeassistant/components/openweathermap/translations/et.json index e548c07236e..0d991c99e3f 100644 --- a/homeassistant/components/openweathermap/translations/et.json +++ b/homeassistant/components/openweathermap/translations/et.json @@ -15,9 +15,9 @@ "latitude": "Laiuskraad", "longitude": "Pikkuskraad", "mode": "Re\u017eiim", - "name": "Sidumise nimi" + "name": "Nimi" }, - "description": "Seadista OpenWeatherMapi sidumine. API-v\u00f5tme loomiseks mine aadressile https://openweathermap.org/appid", + "description": "API-v\u00f5tme loomiseks mine aadressile https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/fr.json b/homeassistant/components/openweathermap/translations/fr.json index efa76d4d7e4..387c3eefb28 100644 --- a/homeassistant/components/openweathermap/translations/fr.json +++ b/homeassistant/components/openweathermap/translations/fr.json @@ -15,9 +15,9 @@ "latitude": "Latitude", "longitude": "Longitude", "mode": "Mode", - "name": "Nom de l'int\u00e9gration" + "name": "Nom" }, - "description": "Configurez l'int\u00e9gration OpenWeatherMap. Pour g\u00e9n\u00e9rer la cl\u00e9 API, acc\u00e9dez \u00e0 https://openweathermap.org/appid", + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/he.json b/homeassistant/components/openweathermap/translations/he.json index ca5e388ea98..cadadeb1865 100644 --- a/homeassistant/components/openweathermap/translations/he.json +++ b/homeassistant/components/openweathermap/translations/he.json @@ -15,7 +15,7 @@ "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "mode": "\u05de\u05e6\u05d1", - "name": "\u05e9\u05dd \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1" + "name": "\u05e9\u05dd" }, "title": "\u05de\u05e4\u05ea OpenWeather" } diff --git a/homeassistant/components/openweathermap/translations/hu.json b/homeassistant/components/openweathermap/translations/hu.json index 99932ff5c68..f5a7dade833 100644 --- a/homeassistant/components/openweathermap/translations/hu.json +++ b/homeassistant/components/openweathermap/translations/hu.json @@ -15,9 +15,9 @@ "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", "mode": "M\u00f3d", - "name": "Az integr\u00e1ci\u00f3 neve" + "name": "Elnevez\u00e9s" }, - "description": "Az OpenWeatherMap integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa. Az API kulcs l\u00e9trehoz\u00e1s\u00e1hoz l\u00e1togasson el a https://openweathermap.org/appid oldalra", + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://openweathermap.org/appid webhelyet", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/id.json b/homeassistant/components/openweathermap/translations/id.json index 61d2713f42a..d8ea0344cea 100644 --- a/homeassistant/components/openweathermap/translations/id.json +++ b/homeassistant/components/openweathermap/translations/id.json @@ -15,9 +15,9 @@ "latitude": "Lintang", "longitude": "Bujur", "mode": "Mode", - "name": "Nama integrasi" + "name": "Nama" }, - "description": "Siapkan integrasi OpenWeatherMap. Untuk membuat kunci API, buka https://openweathermap.org/appid", + "description": "Untuk membuat kunci API, buka https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/it.json b/homeassistant/components/openweathermap/translations/it.json index ffe49a466dc..133fa704109 100644 --- a/homeassistant/components/openweathermap/translations/it.json +++ b/homeassistant/components/openweathermap/translations/it.json @@ -15,9 +15,9 @@ "latitude": "Latitudine", "longitude": "Logitudine", "mode": "Modalit\u00e0", - "name": "Nome dell'integrazione" + "name": "Nome" }, - "description": "Configura l'integrazione di OpenWeatherMap. Per generare la chiave API, vai su https://openweathermap.org/appid", + "description": "Per generare la chiave API, vai su https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/nl.json b/homeassistant/components/openweathermap/translations/nl.json index 04f3c503353..1c72f10c6d9 100644 --- a/homeassistant/components/openweathermap/translations/nl.json +++ b/homeassistant/components/openweathermap/translations/nl.json @@ -15,9 +15,9 @@ "latitude": "Breedtegraad", "longitude": "Lengtegraad", "mode": "Mode", - "name": "Naam van de integratie" + "name": "Naam" }, - "description": "Stel OpenWeatherMap-integratie in. Ga naar https://openweathermap.org/appid om een API-sleutel te genereren", + "description": "Om een API sleutel te genereren ga naar https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/no.json b/homeassistant/components/openweathermap/translations/no.json index 7982628fe62..d751553465a 100644 --- a/homeassistant/components/openweathermap/translations/no.json +++ b/homeassistant/components/openweathermap/translations/no.json @@ -15,9 +15,9 @@ "latitude": "Breddegrad", "longitude": "Lengdegrad", "mode": "Modus", - "name": "Navn p\u00e5 integrasjon" + "name": "Navn" }, - "description": "Sett opp OpenWeatherMap-integrasjon. For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://openweathermap.org/appid", + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/pl.json b/homeassistant/components/openweathermap/translations/pl.json index b3f31831f09..de8d857f168 100644 --- a/homeassistant/components/openweathermap/translations/pl.json +++ b/homeassistant/components/openweathermap/translations/pl.json @@ -15,9 +15,9 @@ "latitude": "Szeroko\u015b\u0107 geograficzna", "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "mode": "Tryb", - "name": "Nazwa integracji" + "name": "Nazwa" }, - "description": "Konfiguracja integracji OpenWeatherMap. Aby wygenerowa\u0107 klucz API, przejd\u017a do https://openweathermap.org/appid", + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/pt-BR.json b/homeassistant/components/openweathermap/translations/pt-BR.json index dd88767bb61..795db96f36f 100644 --- a/homeassistant/components/openweathermap/translations/pt-BR.json +++ b/homeassistant/components/openweathermap/translations/pt-BR.json @@ -15,9 +15,9 @@ "latitude": "Latitude", "longitude": "Longitude", "mode": "Modo", - "name": "Nome da integra\u00e7\u00e3o" + "name": "Nome" }, - "description": "Configure a integra\u00e7\u00e3o do OpenWeatherMap. Para gerar a chave de API, acesse https://openweathermap.org/appid", + "description": "Para gerar a chave de API, acesse https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/ru.json b/homeassistant/components/openweathermap/translations/ru.json index 1a22b38d546..a0724e90f01 100644 --- a/homeassistant/components/openweathermap/translations/ru.json +++ b/homeassistant/components/openweathermap/translations/ru.json @@ -17,7 +17,7 @@ "mode": "\u0420\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 OpenWeatherMap. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://openweathermap.org/appid.", + "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://openweathermap.org/appid.", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/tr.json b/homeassistant/components/openweathermap/translations/tr.json index 83109869e39..6458852712e 100644 --- a/homeassistant/components/openweathermap/translations/tr.json +++ b/homeassistant/components/openweathermap/translations/tr.json @@ -15,9 +15,9 @@ "latitude": "Enlem", "longitude": "Boylam", "mode": "Mod", - "name": "Cihaz\u0131n ad\u0131" + "name": "Ad" }, - "description": "OpenWeatherMap entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://openweathermap.org/appid adresine gidin.", + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://openweathermap.org/appid adresine gidin.", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/openweathermap/translations/zh-Hant.json b/homeassistant/components/openweathermap/translations/zh-Hant.json index 653fb373af3..fcebc12fe31 100644 --- a/homeassistant/components/openweathermap/translations/zh-Hant.json +++ b/homeassistant/components/openweathermap/translations/zh-Hant.json @@ -15,9 +15,9 @@ "latitude": "\u7def\u5ea6", "longitude": "\u7d93\u5ea6", "mode": "\u6a21\u5f0f", - "name": "\u6574\u5408\u540d\u7a31" + "name": "\u540d\u7a31" }, - "description": "\u6b32\u8a2d\u5b9a OpenWeatherMap \u6574\u5408\u3002\u8acb\u81f3 https://openweathermap.org/appid \u7522\u751f API \u91d1\u9470", + "description": "\u8acb\u81f3 https://openweathermap.org/appid \u4ee5\u7522\u751f API \u91d1\u9470", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/overkiz/translations/ca.json b/homeassistant/components/overkiz/translations/ca.json index 1e1bccd1fb7..d3a5a38edc3 100644 --- a/homeassistant/components/overkiz/translations/ca.json +++ b/homeassistant/components/overkiz/translations/ca.json @@ -9,6 +9,7 @@ "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "server_in_maintenance": "El servidor est\u00e0 inoperatiu per manteniment", + "too_many_attempts": "Massa intents amb un 'token' inv\u00e0lid, bloquejat temporalment", "too_many_requests": "Massa sol\u00b7licituds, torna-ho a provar m\u00e9s tard", "unknown": "Error inesperat" }, diff --git a/homeassistant/components/overkiz/translations/de.json b/homeassistant/components/overkiz/translations/de.json index 84d09f9116b..09cce8ea63f 100644 --- a/homeassistant/components/overkiz/translations/de.json +++ b/homeassistant/components/overkiz/translations/de.json @@ -9,6 +9,7 @@ "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", "server_in_maintenance": "Server ist wegen Wartungsarbeiten au\u00dfer Betrieb", + "too_many_attempts": "Zu viele Versuche mit einem ung\u00fcltigen Token, vor\u00fcbergehend gesperrt", "too_many_requests": "Zu viele Anfragen, versuche es sp\u00e4ter erneut.", "unknown": "Unerwarteter Fehler" }, diff --git a/homeassistant/components/overkiz/translations/el.json b/homeassistant/components/overkiz/translations/el.json index 9f7ad60cb09..924ecc3219d 100644 --- a/homeassistant/components/overkiz/translations/el.json +++ b/homeassistant/components/overkiz/translations/el.json @@ -9,6 +9,7 @@ "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "server_in_maintenance": "\u039f \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03ba\u03c4\u03cc\u03c2 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c3\u03c5\u03bd\u03c4\u03ae\u03c1\u03b7\u03c3\u03b7", + "too_many_attempts": "\u03a0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ad\u03c2 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b5\u03c2 \u03bc\u03b5 \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc, \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ac \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2", "too_many_requests": "\u03a0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ac \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1, \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, diff --git a/homeassistant/components/overkiz/translations/et.json b/homeassistant/components/overkiz/translations/et.json index c19d9c39ca9..3cbfcb6af80 100644 --- a/homeassistant/components/overkiz/translations/et.json +++ b/homeassistant/components/overkiz/translations/et.json @@ -9,6 +9,7 @@ "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", "server_in_maintenance": "Server on hoolduse t\u00f5ttu maas", + "too_many_attempts": "Liiga palju katseid kehtetu v\u00f5tmega, ajutiselt keelatud", "too_many_requests": "Liiga palju p\u00e4ringuid, proovi hiljem uuesti", "unknown": "Ootamatu t\u00f5rge" }, diff --git a/homeassistant/components/overkiz/translations/fr.json b/homeassistant/components/overkiz/translations/fr.json index f6a56db80a5..c919301d541 100644 --- a/homeassistant/components/overkiz/translations/fr.json +++ b/homeassistant/components/overkiz/translations/fr.json @@ -9,6 +9,7 @@ "cannot_connect": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", "server_in_maintenance": "Le serveur est ferm\u00e9 pour maintenance", + "too_many_attempts": "Trop de tentatives avec un jeton non valide\u00a0: banni temporairement", "too_many_requests": "Trop de demandes, r\u00e9essayez plus tard.", "unknown": "Erreur inattendue" }, diff --git a/homeassistant/components/overkiz/translations/hu.json b/homeassistant/components/overkiz/translations/hu.json index ef7cacaaf96..b6810749bd3 100644 --- a/homeassistant/components/overkiz/translations/hu.json +++ b/homeassistant/components/overkiz/translations/hu.json @@ -9,6 +9,7 @@ "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "server_in_maintenance": "A szerver karbantart\u00e1s miatt nem el\u00e9rhet\u0151", + "too_many_attempts": "T\u00fal sok pr\u00f3b\u00e1lkoz\u00e1s \u00e9rv\u00e9nytelen tokennel, ideiglenesen kitiltva", "too_many_requests": "T\u00fal sok a k\u00e9r\u00e9s, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, diff --git a/homeassistant/components/overkiz/translations/id.json b/homeassistant/components/overkiz/translations/id.json index becbae65f9e..c58b66b10a7 100644 --- a/homeassistant/components/overkiz/translations/id.json +++ b/homeassistant/components/overkiz/translations/id.json @@ -9,6 +9,7 @@ "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", "server_in_maintenance": "Server sedang dalam masa pemeliharaan", + "too_many_attempts": "Terlalu banyak percobaan dengan token yang tidak valid, untuk sementara diblokir", "too_many_requests": "Terlalu banyak permintaan, coba lagi nanti.", "unknown": "Kesalahan yang tidak diharapkan" }, diff --git a/homeassistant/components/overkiz/translations/it.json b/homeassistant/components/overkiz/translations/it.json index 00898513f1f..3dcc4e94e10 100644 --- a/homeassistant/components/overkiz/translations/it.json +++ b/homeassistant/components/overkiz/translations/it.json @@ -9,6 +9,7 @@ "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", "server_in_maintenance": "Il server \u00e8 inattivo per manutenzione", + "too_many_attempts": "Troppi tentativi con un token non valido, temporaneamente bandito", "too_many_requests": "Troppe richieste, riprova pi\u00f9 tardi.", "unknown": "Errore imprevisto" }, diff --git a/homeassistant/components/overkiz/translations/ja.json b/homeassistant/components/overkiz/translations/ja.json index e978a8f36f2..357408847fd 100644 --- a/homeassistant/components/overkiz/translations/ja.json +++ b/homeassistant/components/overkiz/translations/ja.json @@ -9,6 +9,7 @@ "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "server_in_maintenance": "\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u306e\u305f\u3081\u30b5\u30fc\u30d0\u30fc\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u307e\u3059", + "too_many_attempts": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u306b\u3088\u308b\u8a66\u884c\u56de\u6570\u304c\u591a\u3059\u304e\u305f\u305f\u3081\u3001\u4e00\u6642\u7684\u306b\u7981\u6b62\u3055\u308c\u307e\u3057\u305f\u3002", "too_many_requests": "\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u591a\u3059\u304e\u307e\u3059\u3002\u3057\u3070\u3089\u304f\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, diff --git a/homeassistant/components/overkiz/translations/nl.json b/homeassistant/components/overkiz/translations/nl.json index d33dd5bb44c..5057f12afa6 100644 --- a/homeassistant/components/overkiz/translations/nl.json +++ b/homeassistant/components/overkiz/translations/nl.json @@ -9,6 +9,7 @@ "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "server_in_maintenance": "Server is offline wegens onderhoud", + "too_many_attempts": "Te veel pogingen met een ongeldig token, tijdelijk verbannen", "too_many_requests": "Te veel verzoeken, probeer het later opnieuw.", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/overkiz/translations/no.json b/homeassistant/components/overkiz/translations/no.json index ed691aa388f..e0d52e0fc0e 100644 --- a/homeassistant/components/overkiz/translations/no.json +++ b/homeassistant/components/overkiz/translations/no.json @@ -9,6 +9,7 @@ "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", "server_in_maintenance": "serveren er nede for vedlikehold", + "too_many_attempts": "For mange fors\u00f8k med et ugyldig token, midlertidig utestengt", "too_many_requests": "For mange foresp\u00f8rsler. Pr\u00f8v igjen senere", "unknown": "Uventet feil" }, diff --git a/homeassistant/components/overkiz/translations/pl.json b/homeassistant/components/overkiz/translations/pl.json index 6881d7edb5b..7044dc717fd 100644 --- a/homeassistant/components/overkiz/translations/pl.json +++ b/homeassistant/components/overkiz/translations/pl.json @@ -9,6 +9,7 @@ "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", "server_in_maintenance": "Serwer wy\u0142\u0105czony z powodu przerwy technicznej", + "too_many_attempts": "Zbyt wiele pr\u00f3b z nieprawid\u0142owym tokenem, konto tymczasowo zablokowane", "too_many_requests": "Zbyt wiele \u017c\u0105da\u0144, spr\u00f3buj ponownie p\u00f3\u017aniej.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, diff --git a/homeassistant/components/overkiz/translations/pt-BR.json b/homeassistant/components/overkiz/translations/pt-BR.json index 2f2c335ebd5..123b2a83d42 100644 --- a/homeassistant/components/overkiz/translations/pt-BR.json +++ b/homeassistant/components/overkiz/translations/pt-BR.json @@ -9,6 +9,7 @@ "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "server_in_maintenance": "O servidor est\u00e1 fora de servi\u00e7o para manuten\u00e7\u00e3o", + "too_many_attempts": "Muitas tentativas com um token inv\u00e1lido, banido temporariamente", "too_many_requests": "Muitas solicita\u00e7\u00f5es, tente novamente mais tarde", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/overkiz/translations/ru.json b/homeassistant/components/overkiz/translations/ru.json index 611c4784902..53f08e1dbd6 100644 --- a/homeassistant/components/overkiz/translations/ru.json +++ b/homeassistant/components/overkiz/translations/ru.json @@ -9,6 +9,7 @@ "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "server_in_maintenance": "\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u0441\u0432\u044f\u0437\u0438 \u0441 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u0435\u043c.", + "too_many_attempts": "\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a \u0441 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c, \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e.", "too_many_requests": "\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, diff --git a/homeassistant/components/overkiz/translations/tr.json b/homeassistant/components/overkiz/translations/tr.json index 8e6a232db7f..1d04fbbd3cc 100644 --- a/homeassistant/components/overkiz/translations/tr.json +++ b/homeassistant/components/overkiz/translations/tr.json @@ -9,6 +9,7 @@ "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "server_in_maintenance": "Sunucu bak\u0131m nedeniyle kapal\u0131", + "too_many_attempts": "Ge\u00e7ersiz anahtarla \u00e7ok fazla deneme, ge\u00e7ici olarak yasakland\u0131", "too_many_requests": "\u00c7ok fazla istek var, daha sonra tekrar deneyin", "unknown": "Beklenmeyen hata" }, diff --git a/homeassistant/components/overkiz/translations/zh-Hant.json b/homeassistant/components/overkiz/translations/zh-Hant.json index 04c2fc2b07a..ab265301761 100644 --- a/homeassistant/components/overkiz/translations/zh-Hant.json +++ b/homeassistant/components/overkiz/translations/zh-Hant.json @@ -9,6 +9,7 @@ "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "server_in_maintenance": "\u4f3a\u670d\u5668\u7dad\u8b77\u4e2d", + "too_many_attempts": "\u4f7f\u7528\u7121\u6548\u6b0a\u6756\u5617\u8a66\u6b21\u6578\u904e\u591a\uff0c\u66ab\u6642\u906d\u5230\u5c01\u9396", "too_many_requests": "\u8acb\u6c42\u6b21\u6578\u904e\u591a\uff0c\u8acb\u7a0d\u5f8c\u91cd\u8a66\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, diff --git a/homeassistant/components/owntracks/translations/cs.json b/homeassistant/components/owntracks/translations/cs.json index d242a1275f1..a564efb190f 100644 --- a/homeassistant/components/owntracks/translations/cs.json +++ b/homeassistant/components/owntracks/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Nen\u00ed p\u0159ipojeno k Home Assistant Cloud.", "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, "create_entry": { diff --git a/homeassistant/components/p1_monitor/translations/hu.json b/homeassistant/components/p1_monitor/translations/hu.json index f9025022c6d..b0c30613234 100644 --- a/homeassistant/components/p1_monitor/translations/hu.json +++ b/homeassistant/components/p1_monitor/translations/hu.json @@ -8,7 +8,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "\u00c1ll\u00edtsa be a P1 monitort az Otthoni asszisztenssel val\u00f3 integr\u00e1ci\u00f3hoz." } diff --git a/homeassistant/components/panasonic_viera/translations/hu.json b/homeassistant/components/panasonic_viera/translations/hu.json index e373a352a45..7fd4d2524da 100644 --- a/homeassistant/components/panasonic_viera/translations/hu.json +++ b/homeassistant/components/panasonic_viera/translations/hu.json @@ -20,7 +20,7 @@ "user": { "data": { "host": "IP c\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Adja meg a Panasonic Viera TV-hez tartoz\u00f3 IP c\u00edmet", "title": "A TV be\u00e1ll\u00edt\u00e1sa" diff --git a/homeassistant/components/peco/translations/bg.json b/homeassistant/components/peco/translations/bg.json new file mode 100644 index 00000000000..80a7cc489a9 --- /dev/null +++ b/homeassistant/components/peco/translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/ca.json b/homeassistant/components/peco/translations/ca.json new file mode 100644 index 00000000000..1cca8a08a7f --- /dev/null +++ b/homeassistant/components/peco/translations/ca.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat" + }, + "step": { + "user": { + "data": { + "county": "Comtat" + }, + "description": "Trieu el teu comtat a continuaci\u00f3.", + "title": "Comptador d'interrupcions PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/cs.json b/homeassistant/components/peco/translations/cs.json new file mode 100644 index 00000000000..8440070c91a --- /dev/null +++ b/homeassistant/components/peco/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/de.json b/homeassistant/components/peco/translations/de.json new file mode 100644 index 00000000000..4eb10b3e5e9 --- /dev/null +++ b/homeassistant/components/peco/translations/de.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert" + }, + "step": { + "user": { + "data": { + "county": "Bundesland" + }, + "description": "Bitte w\u00e4hle unten dein Bundesland aus.", + "title": "PECO-St\u00f6rungsz\u00e4hler" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/el.json b/homeassistant/components/peco/translations/el.json new file mode 100644 index 00000000000..6b47cc3ccc1 --- /dev/null +++ b/homeassistant/components/peco/translations/el.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, + "step": { + "user": { + "data": { + "county": "\u039a\u03bf\u03bc\u03b7\u03c4\u03b5\u03af\u03b1" + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03bf\u03bc\u03b7\u03c4\u03b5\u03af\u03b1 \u03c3\u03b1\u03c2 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9.", + "title": "\u039c\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ce\u03bd PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/en.json b/homeassistant/components/peco/translations/en.json index 6f7ff2b0b12..60483a1d65c 100644 --- a/homeassistant/components/peco/translations/en.json +++ b/homeassistant/components/peco/translations/en.json @@ -7,7 +7,9 @@ "user": { "data": { "county": "County" - } + }, + "description": "Please choose your county below.", + "title": "PECO Outage Counter" } } } diff --git a/homeassistant/components/peco/translations/et.json b/homeassistant/components/peco/translations/et.json new file mode 100644 index 00000000000..117d0502ca3 --- /dev/null +++ b/homeassistant/components/peco/translations/et.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba h\u00e4\u00e4lestatud" + }, + "step": { + "user": { + "data": { + "county": "Maakond" + }, + "description": "Vali allpool oma maakond.", + "title": "PECO katkestuste loendur" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/fr.json b/homeassistant/components/peco/translations/fr.json new file mode 100644 index 00000000000..e78f828f1af --- /dev/null +++ b/homeassistant/components/peco/translations/fr.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9" + }, + "step": { + "user": { + "data": { + "county": "Comt\u00e9" + }, + "description": "Veuillez choisir votre comt\u00e9 ci-dessous.", + "title": "Compteur de pannes PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/he.json b/homeassistant/components/peco/translations/he.json new file mode 100644 index 00000000000..48a6eeeea33 --- /dev/null +++ b/homeassistant/components/peco/translations/he.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/hu.json b/homeassistant/components/peco/translations/hu.json new file mode 100644 index 00000000000..2e1e3482eb5 --- /dev/null +++ b/homeassistant/components/peco/translations/hu.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" + }, + "step": { + "user": { + "data": { + "county": "Megye" + }, + "description": "K\u00e9rj\u00fck, v\u00e1lassza ki az al\u00e1bbiakban a megy\u00e9t.", + "title": "PECO kimarad\u00e1s sz\u00e1ml\u00e1l\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/id.json b/homeassistant/components/peco/translations/id.json new file mode 100644 index 00000000000..0879348f593 --- /dev/null +++ b/homeassistant/components/peco/translations/id.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi" + }, + "step": { + "user": { + "data": { + "county": "Kota/kabupaten" + }, + "description": "Pilih kota/kabupaten Anda di bawah ini.", + "title": "Penghitung Pemadaman PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/it.json b/homeassistant/components/peco/translations/it.json new file mode 100644 index 00000000000..a7b76c27f43 --- /dev/null +++ b/homeassistant/components/peco/translations/it.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato" + }, + "step": { + "user": { + "data": { + "county": "Provincia" + }, + "description": "Scegli la tua provincia qui sotto.", + "title": "Contatore interruzioni PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/ja.json b/homeassistant/components/peco/translations/ja.json new file mode 100644 index 00000000000..139e1f77bd9 --- /dev/null +++ b/homeassistant/components/peco/translations/ja.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "step": { + "user": { + "data": { + "county": "\u90e1" + }, + "description": "\u4ee5\u4e0b\u304b\u3089\u304a\u4f4f\u307e\u3044\u306e\u56fd\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "title": "PECO Outage\u30ab\u30a6\u30f3\u30bf\u30fc" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/nl.json b/homeassistant/components/peco/translations/nl.json new file mode 100644 index 00000000000..8e98f5077e4 --- /dev/null +++ b/homeassistant/components/peco/translations/nl.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd" + }, + "step": { + "user": { + "data": { + "county": "County" + }, + "description": "Kies hieronder uw county", + "title": "PECO Uitval Teller" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/no.json b/homeassistant/components/peco/translations/no.json new file mode 100644 index 00000000000..00f9c025114 --- /dev/null +++ b/homeassistant/components/peco/translations/no.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert" + }, + "step": { + "user": { + "data": { + "county": "fylke" + }, + "description": "Velg ditt fylke nedenfor.", + "title": "PECO avbruddsteller" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/pl.json b/homeassistant/components/peco/translations/pl.json new file mode 100644 index 00000000000..bdd8a7ffb7c --- /dev/null +++ b/homeassistant/components/peco/translations/pl.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana" + }, + "step": { + "user": { + "data": { + "county": "Hrabstwo" + }, + "description": "Wybierz swoje hrabstwo poni\u017cej.", + "title": "Licznik awarii PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/pt-BR.json b/homeassistant/components/peco/translations/pt-BR.json new file mode 100644 index 00000000000..845baf5b479 --- /dev/null +++ b/homeassistant/components/peco/translations/pt-BR.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "step": { + "user": { + "data": { + "county": "Munic\u00edpio" + }, + "description": "Por favor, escolha seu munic\u00edpio abaixo.", + "title": "Contador de Interrup\u00e7\u00e3o PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/ru.json b/homeassistant/components/peco/translations/ru.json new file mode 100644 index 00000000000..6c2c8d85cc6 --- /dev/null +++ b/homeassistant/components/peco/translations/ru.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "step": { + "user": { + "data": { + "county": "\u041e\u043a\u0440\u0443\u0433" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043e\u043a\u0440\u0443\u0433.", + "title": "\u0421\u0447\u0435\u0442\u0447\u0438\u043a \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 PECO" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/tr.json b/homeassistant/components/peco/translations/tr.json new file mode 100644 index 00000000000..6a76e600789 --- /dev/null +++ b/homeassistant/components/peco/translations/tr.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "step": { + "user": { + "data": { + "county": "\u0130l\u00e7e" + }, + "description": "L\u00fctfen a\u015fa\u011f\u0131dan il\u00e7enizi se\u00e7iniz.", + "title": "PECO Kesinti Sayac\u0131" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/peco/translations/zh-Hant.json b/homeassistant/components/peco/translations/zh-Hant.json new file mode 100644 index 00000000000..94318397f6f --- /dev/null +++ b/homeassistant/components/peco/translations/zh-Hant.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "step": { + "user": { + "data": { + "county": "\u7e23\u5e02" + }, + "description": "\u8acb\u9078\u64c7\u7e23\u5e02\u3002", + "title": "PECO Outage \u8a08\u6578\u5668" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pi_hole/translations/hu.json b/homeassistant/components/pi_hole/translations/hu.json index 71321c4cf85..e55c7d543e3 100644 --- a/homeassistant/components/pi_hole/translations/hu.json +++ b/homeassistant/components/pi_hole/translations/hu.json @@ -17,7 +17,7 @@ "api_key": "API kulcs", "host": "C\u00edm", "location": "Elhelyezked\u00e9s", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "port": "Port", "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "statistics_only": "Csak statisztik\u00e1k", diff --git a/homeassistant/components/plaato/translations/cs.json b/homeassistant/components/plaato/translations/cs.json index 4d736d1c695..5e3c9682226 100644 --- a/homeassistant/components/plaato/translations/cs.json +++ b/homeassistant/components/plaato/translations/cs.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u00da\u010det je ji\u017e nastaven", + "cloud_not_connected": "Nen\u00ed p\u0159ipojeno k Home Assistant Cloud.", "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.", "webhook_not_internet_accessible": "V\u00e1\u0161 Home Assistant mus\u00ed b\u00fdt p\u0159\u00edstupn\u00fd z internetu, aby mohl p\u0159ij\u00edmat zpr\u00e1vy webhook." }, diff --git a/homeassistant/components/plant/translations/zh-Hant.json b/homeassistant/components/plant/translations/zh-Hant.json index af06d09eef6..052086d04eb 100644 --- a/homeassistant/components/plant/translations/zh-Hant.json +++ b/homeassistant/components/plant/translations/zh-Hant.json @@ -1,7 +1,7 @@ { "state": { "_": { - "ok": "\u5065\u5eb7", + "ok": "\u6b63\u5e38", "problem": "\u7570\u5e38" } }, diff --git a/homeassistant/components/plex/translations/hu.json b/homeassistant/components/plex/translations/hu.json index d3aabedc58d..3be797cc04f 100644 --- a/homeassistant/components/plex/translations/hu.json +++ b/homeassistant/components/plex/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "all_configured": "Az \u00f6sszes \u00f6sszekapcsolt szerver m\u00e1r konfigur\u00e1lva van", "already_configured": "Ez a Plex szerver m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "token_request_timeout": "Token k\u00e9r\u00e9sre sz\u00e1nt id\u0151 lej\u00e1rt", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" diff --git a/homeassistant/components/plugwise/translations/ca.json b/homeassistant/components/plugwise/translations/ca.json index c29fa230519..85f7c357803 100644 --- a/homeassistant/components/plugwise/translations/ca.json +++ b/homeassistant/components/plugwise/translations/ca.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_setup": "Afegeix l'Adam en lloc de l'Anna; consulta la documentaci\u00f3 de la integraci\u00f3 Plugwise de Home Assistant per a m\u00e9s informaci\u00f3.", "unknown": "Error inesperat" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/de.json b/homeassistant/components/plugwise/translations/de.json index a5c11645d6f..f6ce5fb1b2d 100644 --- a/homeassistant/components/plugwise/translations/de.json +++ b/homeassistant/components/plugwise/translations/de.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", + "invalid_setup": "F\u00fcge deinen Adam anstelle deiner Anna hinzu. Weitere Informationen findest du in der Dokumentation zur Integration von Home Assistant Plugwise.", "unknown": "Unerwarteter Fehler" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/el.json b/homeassistant/components/plugwise/translations/el.json index a891a74d3ad..8407cb38cb1 100644 --- a/homeassistant/components/plugwise/translations/el.json +++ b/homeassistant/components/plugwise/translations/el.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "invalid_setup": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd Adam \u03c3\u03b1\u03c2 \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd Anna \u03c3\u03b1\u03c2, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Home Assistant Plugwise \u03b3\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/en.json b/homeassistant/components/plugwise/translations/en.json index 3ee5551fd83..616e450cb11 100644 --- a/homeassistant/components/plugwise/translations/en.json +++ b/homeassistant/components/plugwise/translations/en.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", + "invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", "unknown": "Unexpected error" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/et.json b/homeassistant/components/plugwise/translations/et.json index 029b334f7c4..f863dd30b19 100644 --- a/homeassistant/components/plugwise/translations/et.json +++ b/homeassistant/components/plugwise/translations/et.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", + "invalid_setup": "Lisa oma Anna asemel oma Adam, lisateabe saamiseks vaata Home Assistant Plugwise'i sidumise dokumentatsiooni", "unknown": "Tundmatu viga" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/fr.json b/homeassistant/components/plugwise/translations/fr.json index c6098db43f0..baa6074e68a 100644 --- a/homeassistant/components/plugwise/translations/fr.json +++ b/homeassistant/components/plugwise/translations/fr.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", + "invalid_setup": "Ajoutez votre Adam au lieu de votre Anna\u00a0; consultez la documentation de l'int\u00e9gration Plugwise de Home Assistant pour plus d'informations", "unknown": "Erreur inattendue" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/hu.json b/homeassistant/components/plugwise/translations/hu.json index 4d588c84277..265a99186ba 100644 --- a/homeassistant/components/plugwise/translations/hu.json +++ b/homeassistant/components/plugwise/translations/hu.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "invalid_setup": "Adja hozz\u00e1 Adamot Anna helyett. Tov\u00e1bbi inform\u00e1ci\u00f3\u00e9rt tekintse meg a Home Assistant Plugwise integr\u00e1ci\u00f3s dokument\u00e1ci\u00f3j\u00e1t", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/id.json b/homeassistant/components/plugwise/translations/id.json index f3eeb0926ba..8878a4e775d 100644 --- a/homeassistant/components/plugwise/translations/id.json +++ b/homeassistant/components/plugwise/translations/id.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", + "invalid_setup": "Tambahkan Adam Anda, alih-alih Anna. Baca dokumentasi integrasi Plugwise Home Assistant untuk informasi lebih lanjut", "unknown": "Kesalahan yang tidak diharapkan" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/it.json b/homeassistant/components/plugwise/translations/it.json index e60b91a106b..9e051a29e13 100644 --- a/homeassistant/components/plugwise/translations/it.json +++ b/homeassistant/components/plugwise/translations/it.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", + "invalid_setup": "Aggiungi il tuo Adam invece di Anna, consulta la documentazione sull'integrazione di Home Assistant Plugwise per ulteriori informazioni", "unknown": "Errore imprevisto" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/ja.json b/homeassistant/components/plugwise/translations/ja.json index cc3810edcd3..f15d62d38d1 100644 --- a/homeassistant/components/plugwise/translations/ja.json +++ b/homeassistant/components/plugwise/translations/ja.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "invalid_setup": "Anna\u306e\u4ee3\u308f\u308a\u306b\u3001Adam\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001Home Assistant Plugwise\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index 160cf182d3d..d295695016f 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", + "invalid_setup": "Voeg je Adam toe in plaats van je Anna, zie de Home Assistant Plugwise integratiedocumentatie voor meer informatie", "unknown": "Onverwachte fout" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/no.json b/homeassistant/components/plugwise/translations/no.json index 58ee9d4aed8..d8ce2d8956a 100644 --- a/homeassistant/components/plugwise/translations/no.json +++ b/homeassistant/components/plugwise/translations/no.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", + "invalid_setup": "Legg til din Adam i stedet for din Anna, se integrasjonsdokumentasjonen for Home Assistant Plugwise for mer informasjon", "unknown": "Uventet feil" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/pl.json b/homeassistant/components/plugwise/translations/pl.json index 5d0bdd0f4e6..c4a2efe95c3 100644 --- a/homeassistant/components/plugwise/translations/pl.json +++ b/homeassistant/components/plugwise/translations/pl.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_setup": "Dodaj urz\u0105dzenie Adam zamiast Anna. Zobacz dokumentacj\u0119 integracji Plugwise dla Home Assistant, aby uzyska\u0107 wi\u0119cej informacji.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/pt-BR.json b/homeassistant/components/plugwise/translations/pt-BR.json index f53f6c7c379..35a08d93114 100644 --- a/homeassistant/components/plugwise/translations/pt-BR.json +++ b/homeassistant/components/plugwise/translations/pt-BR.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_setup": "Adicione seu Adam em vez de sua Anna, consulte a documenta\u00e7\u00e3o de integra\u00e7\u00e3o do Home Assistant Plugwise para obter mais informa\u00e7\u00f5es", "unknown": "Erro inesperado" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/ru.json b/homeassistant/components/plugwise/translations/ru.json index c7b28d5b415..62b7c1e27d3 100644 --- a/homeassistant/components/plugwise/translations/ru.json +++ b/homeassistant/components/plugwise/translations/ru.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "invalid_setup": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 Adam \u0432\u043c\u0435\u0441\u0442\u043e Anna. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Plugwise \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/tr.json b/homeassistant/components/plugwise/translations/tr.json index 4c752efbd4a..ae756bf15d4 100644 --- a/homeassistant/components/plugwise/translations/tr.json +++ b/homeassistant/components/plugwise/translations/tr.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "invalid_setup": "Anna'n\u0131z yerine Adam'\u0131n\u0131z\u0131 ekleyin, daha fazla bilgi i\u00e7in Home Assistant Plugwise entegrasyon belgelerine bak\u0131n", "unknown": "Beklenmeyen hata" }, "flow_title": "{name}", diff --git a/homeassistant/components/plugwise/translations/zh-Hant.json b/homeassistant/components/plugwise/translations/zh-Hant.json index 0a1e21a12ed..57a7add22e0 100644 --- a/homeassistant/components/plugwise/translations/zh-Hant.json +++ b/homeassistant/components/plugwise/translations/zh-Hant.json @@ -6,6 +6,7 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_setup": "\u65b0\u589e Adam \u800c\u975e Anna\u3001\u8acb\u53c3\u95b1 Home Assistant Plugwise \u6574\u5408\u8aaa\u660e\u6587\u4ef6\u4ee5\u4e86\u89e3\u66f4\u8a73\u7d30\u8cc7\u6599", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "flow_title": "{name}", diff --git a/homeassistant/components/point/translations/hu.json b/homeassistant/components/point/translations/hu.json index c582bbfc7cd..27c35a1966f 100644 --- a/homeassistant/components/point/translations/hu.json +++ b/homeassistant/components/point/translations/hu.json @@ -11,12 +11,12 @@ "default": "Sikeres hiteles\u00edt\u00e9s" }, "error": { - "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt megnyomn\u00e1 a K\u00fcld\u00e9s gombot", + "follow_link": "K\u00e9rem, k\u00f6vesse a hivatkoz\u00e1st \u00e9s hiteles\u00edtse mag\u00e1t miel\u0151tt folytatn\u00e1", "no_token": "\u00c9rv\u00e9nytelen hozz\u00e1f\u00e9r\u00e9si token" }, "step": { "auth": { - "description": "K\u00e9rem k\u00f6vesse az al\u00e1bbi linket \u00e9s a **Fogadja el** a hozz\u00e1f\u00e9r\u00e9st a Minut fi\u00f3kj\u00e1hoz, majd t\u00e9rjen vissza \u00e9s nyomja meg a **K\u00fcld\u00e9s ** gombot. \n\n [Link]({authorization_url})", + "description": "K\u00e9rem k\u00f6vesse az al\u00e1bbi linket \u00e9s a **Fogadja el** a hozz\u00e1f\u00e9r\u00e9st a Minut fi\u00f3kj\u00e1hoz, majd t\u00e9rjen vissza \u00e9s nyomja meg a **Mehet** gombot. \n\n [Link]({authorization_url})", "title": "Point hiteles\u00edt\u00e9se" }, "user": { @@ -24,7 +24,7 @@ "flow_impl": "Szolg\u00e1ltat\u00f3" }, "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?", - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/point/translations/it.json b/homeassistant/components/point/translations/it.json index 8a35f7acdf3..c527666e132 100644 --- a/homeassistant/components/point/translations/it.json +++ b/homeassistant/components/point/translations/it.json @@ -4,7 +4,7 @@ "already_setup": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", "external_setup": "Point configurato correttamente da un altro flusso.", - "no_flows": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "no_flows": "Il componente non \u00e8 configurato. Segui la documentazione.", "unknown_authorize_url_generation": "Errore sconosciuto durante la generazione di un URL di autorizzazione." }, "create_entry": { diff --git a/homeassistant/components/powerwall/translations/de.json b/homeassistant/components/powerwall/translations/de.json index 17f85254208..e68254918fb 100644 --- a/homeassistant/components/powerwall/translations/de.json +++ b/homeassistant/components/powerwall/translations/de.json @@ -15,7 +15,7 @@ "step": { "confirm_discovery": { "description": "M\u00f6chtest du {name} ({ip_address}) einrichten?", - "title": "Powerwall verbinden" + "title": "Stelle eine Verbindung zur Powerwall her" }, "reauth_confim": { "data": { diff --git a/homeassistant/components/powerwall/translations/es.json b/homeassistant/components/powerwall/translations/es.json index 767f77e58bd..5d82ecda8e5 100644 --- a/homeassistant/components/powerwall/translations/es.json +++ b/homeassistant/components/powerwall/translations/es.json @@ -13,6 +13,9 @@ "flow_title": "Powerwall de Tesla ({ip_address})", "step": { "reauth_confim": { + "data": { + "password": "Contrase\u00f1a" + }, "title": "Reautorizar la powerwall" }, "user": { diff --git a/homeassistant/components/ps4/translations/ca.json b/homeassistant/components/ps4/translations/ca.json index 53d45e5104d..6486c154360 100644 --- a/homeassistant/components/ps4/translations/ca.json +++ b/homeassistant/components/ps4/translations/ca.json @@ -25,6 +25,9 @@ "name": "Nom", "region": "Regi\u00f3" }, + "data_description": { + "code": "V\u00e9s a 'Configuraci\u00f3' de la teva consola PlayStation 4. A continuaci\u00f3, v\u00e9s a 'Configuraci\u00f3 de connexi\u00f3 de l'aplicaci\u00f3 m\u00f2bil' i selecciona 'Afegeix un dispositiu' per obtenir el PIN." + }, "description": "Introdueix la informaci\u00f3 de la teva PlayStation 4. Per al Codi PIN, ves a 'Configuraci\u00f3' a la consola de la PlayStation 4. Despr\u00e9s navega fins a 'Configuraci\u00f3 de la connexi\u00f3 de l'aplicaci\u00f3 m\u00f2bil' i selecciona 'Afegir dispositiu'. Introdueix el Codi PIN que es mostra. Consulta la [documentaci\u00f3](https://www.home-assistant.io/components/ps4/) per a m\u00e9s informaci\u00f3.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Adre\u00e7a IP (deixa-ho en blanc si fas servir la detecci\u00f3 autom\u00e0tica).", "mode": "Mode de configuraci\u00f3" }, + "data_description": { + "ip_address": "Deixa-ho en blanc si selecciones el descobriment autom\u00e0tic." + }, "description": "Selecciona el mode de configuraci\u00f3. El camp de l'Adre\u00e7a IP es pot deixar en blanc si selecciones descobriment autom\u00e0tic (els dispositius es descobriran autom\u00e0ticament).", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/de.json b/homeassistant/components/ps4/translations/de.json index c177c5d81cc..22a50cdf3e3 100644 --- a/homeassistant/components/ps4/translations/de.json +++ b/homeassistant/components/ps4/translations/de.json @@ -25,6 +25,9 @@ "name": "Name", "region": "Region" }, + "data_description": { + "code": "Navigiere auf deiner PlayStation 4-Konsole zu \"Einstellungen\". Navigiere dann zu \"Mobile App-Verbindungseinstellungen\" und w\u00e4hle \"Ger\u00e4t hinzuf\u00fcgen\", um den Pin zu erhalten." + }, "description": "Gib deine PlayStation 4-Informationen ein. Navigiere f\u00fcr den PIN-Code auf der PlayStation 4-Konsole zu \"Einstellungen\". Navigiere dann zu \"Mobile App-Verbindungseinstellungen\" und w\u00e4hle \"Ger\u00e4t hinzuf\u00fcgen\" aus. Gib die angezeigte PIN-Code ein. Weitere Informationen findest du in der [Dokumentation](https://www.home-assistant.io/components/ps4/).", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP-Adresse (Leer lassen, wenn automatische Erkennung verwendet wird).", "mode": "Konfigurationsmodus" }, + "data_description": { + "ip_address": "Lasse das Feld leer, wenn du die automatische Erkennung ausw\u00e4hlst." + }, "description": "W\u00e4hle den Modus f\u00fcr die Konfiguration aus. Das Feld IP-Adresse kann leer bleiben, wenn die automatische Erkennung ausgew\u00e4hlt wird, da Ger\u00e4te automatisch erkannt werden.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/el.json b/homeassistant/components/ps4/translations/el.json index 5bc6b4a7b0e..f3899b682f3 100644 --- a/homeassistant/components/ps4/translations/el.json +++ b/homeassistant/components/ps4/translations/el.json @@ -25,6 +25,9 @@ "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" }, + "data_description": { + "code": "\u03a0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \"\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2\" \u03c3\u03c4\u03b7\u03bd \u03ba\u03bf\u03bd\u03c3\u03cc\u03bb\u03b1 PlayStation 4. \u03a3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf 'Mobile App Connection Settings' \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 'Add Device' \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c4\u03bf pin." + }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c4\u03bf\u03c5 PlayStation 4. \u0393\u03b9\u03b1 \u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \"\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2\" \u03c3\u03c4\u03b7\u03bd \u03ba\u03bf\u03bd\u03c3\u03cc\u03bb\u03b1 PlayStation 4. \u03a3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf 'Mobile App Connection Settings' (\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac) \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 'Add Device' (\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2). \u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7](https://www.home-assistant.io/components/ps4/) \u03b3\u03b9\u03b1 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP (\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03b5\u03ac\u03bd \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u0391\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7).", "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2" }, + "data_description": { + "ip_address": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." + }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2. \u03a4\u03bf \u03c0\u03b5\u03b4\u03af\u03bf \u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03af\u03bd\u03b5\u03b9 \u03ba\u03b5\u03bd\u03cc \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 Auto Discovery, \u03ba\u03b1\u03b8\u03ce\u03c2 \u03bf\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03b8\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/en.json b/homeassistant/components/ps4/translations/en.json index 12b154f48de..1c8640efe2e 100644 --- a/homeassistant/components/ps4/translations/en.json +++ b/homeassistant/components/ps4/translations/en.json @@ -25,6 +25,9 @@ "name": "Name", "region": "Region" }, + "data_description": { + "code": "Navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device' to get the pin." + }, "description": "Enter your PlayStation 4 information. For PIN Code, navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN Code that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP Address (Leave empty if using Auto Discovery).", "mode": "Config Mode" }, + "data_description": { + "ip_address": "Leave blank if selecting auto-discovery." + }, "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/et.json b/homeassistant/components/ps4/translations/et.json index ddc6f0b73a9..83ef7a6f14c 100644 --- a/homeassistant/components/ps4/translations/et.json +++ b/homeassistant/components/ps4/translations/et.json @@ -25,6 +25,9 @@ "name": "Nimi", "region": "Piirkond" }, + "data_description": { + "code": "Navigeeri oma PlayStation 4 konsoolis jaotisse \u201eSeaded\u201d. Seej\u00e4rel navigeeri jaotisse \"Mobiilirakenduse \u00fchenduse s\u00e4tted\" ja vali PIN-koodi saamiseks \"Lisa seade\"." + }, "description": "Sisesta oma PlayStation 4 teave. PIN koodi saamiseks mine PlayStation 4 konsooli \u201eSeaded\u201d. Seej\u00e4rel liigu jaotisse \u201eMobiilirakenduse \u00fchenduse seaded\u201d ja vali \u201eLisa seade\u201d. SisestaPIN kood . Lisateavet leiad [dokumentatsioonist] (https://www.home-assistant.io/components/ps4/).", "title": "" }, @@ -33,6 +36,9 @@ "ip_address": "IP-aadress (J\u00e4tke t\u00fchjaks, kui kasutate automaatset tuvastamist)", "mode": "Seadistuste re\u017eiim" }, + "data_description": { + "ip_address": "Automaatse avastamise valimiseks j\u00e4ta v\u00e4li t\u00fchjaks." + }, "description": "Valikonfigureerimise re\u017eiim. V\u00e4lja IP-aadress saab automaatse tuvastamise valimisel t\u00fchjaks j\u00e4tta, kuna seadmed avastatakse automaatselt.", "title": "" } diff --git a/homeassistant/components/ps4/translations/fr.json b/homeassistant/components/ps4/translations/fr.json index 8fc216fd20b..8bc2575d246 100644 --- a/homeassistant/components/ps4/translations/fr.json +++ b/homeassistant/components/ps4/translations/fr.json @@ -25,6 +25,9 @@ "name": "Nom", "region": "R\u00e9gion" }, + "data_description": { + "code": "Acc\u00e9dez aux \u00ab\u00a0Param\u00e8tres\u00a0\u00bb sur votre console PlayStation\u00a04, puis acc\u00e9dez \u00e0 \u00ab\u00a0Param\u00e8tres de connexion de l'application mobile\u00a0\u00bb et s\u00e9lectionnez \u00ab\u00a0Ajouter un appareil\u00a0\u00bb afin d'obtenir le code PIN." + }, "description": "Saisissez les informations de votre PlayStation\u00a04. Pour le Code PIN, acc\u00e9dez aux \u00ab\u00a0Param\u00e8tres\u00a0\u00bb sur votre console PlayStation\u00a04, puis acc\u00e9dez \u00e0 \u00ab\u00a0Param\u00e8tres de connexion de l'application mobile\u00a0\u00bb et s\u00e9lectionnez \u00ab\u00a0Ajouter un appareil\u00a0\u00bb. Entrez le Code PIN affich\u00e9. Consultez la [documentation](https://www.home-assistant.io/components/ps4/) pour plus d'informations.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Adresse IP (laissez vide si vous utilisez la d\u00e9couverte automatique).", "mode": "Mode de configuration" }, + "data_description": { + "ip_address": "Laissez le champ vide si vous s\u00e9lectionnez la d\u00e9couverte automatique." + }, "description": "S\u00e9lectionnez le mode de configuration. Le champ Adresse IP peut \u00eatre laiss\u00e9 vide si vous s\u00e9lectionnez D\u00e9couverte automatique, car les appareils seront automatiquement d\u00e9couverts.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/hu.json b/homeassistant/components/ps4/translations/hu.json index 753ea60b282..24da7fc15f5 100644 --- a/homeassistant/components/ps4/translations/hu.json +++ b/homeassistant/components/ps4/translations/hu.json @@ -4,27 +4,30 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "credential_error": "Hiba t\u00f6rt\u00e9nt a hiteles\u00edt\u0151 adatok beolvas\u00e1sa sor\u00e1n.", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", - "port_987_bind_error": "Nem siker\u00fclt a 987. porthoz kapcsol\u00f3dni. Tov\u00e1bbi inform\u00e1ci\u00f3 a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3 (https://www.home-assistant.io/components/ps4/).", - "port_997_bind_error": "Nem siker\u00fclt a 997-es porthoz kapcsol\u00f3dni. Tov\u00e1bbi inform\u00e1ci\u00f3 a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3 (https://www.home-assistant.io/components/ps4/)." + "port_987_bind_error": "Nem siker\u00fclt a 987. portot lefoglalni. Tov\u00e1bbi inform\u00e1ci\u00f3 a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3 (https://www.home-assistant.io/components/ps4/).", + "port_997_bind_error": "Nem siker\u00fclt a 997. portot lefoglalni. Tov\u00e1bbi inform\u00e1ci\u00f3 a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3 (https://www.home-assistant.io/components/ps4/)." }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "credential_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s. Az \u00fajraind\u00edt\u00e1shoz nyomja meg a K\u00fcld\u00e9s gombot.", + "credential_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s. Az \u00fajraind\u00edt\u00e1shoz nyomja meg a Mehet gombot.", "login_failed": "Nem siker\u00fclt p\u00e1ros\u00edtani a PlayStation 4-gyel. Ellen\u0151rizze, hogy a PIN-k\u00f3d helyes-e.", "no_ipaddress": "\u00cdrja be a konfigur\u00e1lni k\u00edv\u00e1nt PlayStation 4 IP c\u00edm\u00e9t" }, "step": { "creds": { - "description": "Hiteles\u00edt\u0151 adatok sz\u00fcks\u00e9gesek. Nyomja meg a \u201eK\u00fcld\u00e9s\u201d gombot, majd a PS4 2. k\u00e9perny\u0151 alkalmaz\u00e1sban friss\u00edtse az eszk\u00f6z\u00f6ket, \u00e9s a folytat\u00e1shoz v\u00e1lassza a \u201eHome-Assistant\u201d eszk\u00f6zt.", + "description": "Hiteles\u00edt\u0151 adatok sz\u00fcks\u00e9gesek. Nyomja meg a \u201eMehet\u201d gombot, majd a PS4 2. k\u00e9perny\u0151 alkalmaz\u00e1sban friss\u00edtse az eszk\u00f6z\u00f6ket, \u00e9s a folytat\u00e1shoz v\u00e1lassza a \u201eHome-Assistant\u201d eszk\u00f6zt.", "title": "PlayStation 4" }, "link": { "data": { "code": "PIN-k\u00f3d", "ip_address": "IP c\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "region": "R\u00e9gi\u00f3" }, + "data_description": { + "code": "Keresse meg a PlayStation 4 konzol \"Be\u00e1ll\u00edt\u00e1sok\" gombj\u00e1t. Ezut\u00e1n keresse meg a \"Mobilalkalmaz\u00e1s-kapcsolat be\u00e1ll\u00edt\u00e1sai\" lehet\u0151s\u00e9get, \u00e9s v\u00e1lassza az \"Eszk\u00f6z hozz\u00e1ad\u00e1sa\" lehet\u0151s\u00e9get a pin lek\u00e9r\u00e9s\u00e9hez." + }, "description": "Adja meg a PlayStation 4 adatait. A PIN-k\u00f3d eset\u00e9ben keresse meg a PlayStation 4 konzol \u201eBe\u00e1ll\u00edt\u00e1sok\u201d elem\u00e9t. Ezut\u00e1n keresse meg a \u201eMobilalkalmaz\u00e1s-kapcsolat be\u00e1ll\u00edt\u00e1sai\u201d elemet, \u00e9s v\u00e1lassza az \u201eEszk\u00f6z hozz\u00e1ad\u00e1sa\u201d lehet\u0151s\u00e9get. \u00cdrja be a megjelen\u0151 PIN-k\u00f3d . Tov\u00e1bbi inform\u00e1ci\u00f3k a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3k (https://www.home-assistant.io/components/ps4/).", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP c\u00edm (Hagyja \u00fcresen az Automatikus Felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz).", "mode": "Konfigur\u00e1ci\u00f3s m\u00f3d" }, + "data_description": { + "ip_address": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen" + }, "description": "V\u00e1lassza ki a m\u00f3dot a konfigur\u00e1l\u00e1shoz. Az IP c\u00edm mez\u0151 \u00fcresen maradhat, ha az Automatikus felder\u00edt\u00e9s lehet\u0151s\u00e9get v\u00e1lasztja, mivel az eszk\u00f6z\u00f6k automatikusan felfedez\u0151dnek.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/id.json b/homeassistant/components/ps4/translations/id.json index aab31564e16..beb15fb5c4c 100644 --- a/homeassistant/components/ps4/translations/id.json +++ b/homeassistant/components/ps4/translations/id.json @@ -25,6 +25,9 @@ "name": "Nama", "region": "Wilayah" }, + "data_description": { + "code": "Navigasikan ke 'Pengaturan' di konsol PlayStation 4 Anda. Kemudian navigasikan ke 'Pengaturan Koneksi Aplikasi Seluler' dan pilih 'Tambahkan Perangkat' untuk mendapatkan PIN." + }, "description": "Masukkan informasi PlayStation 4 Anda. Untuk Kode PIN, buka 'Pengaturan' di konsol PlayStation 4 Anda. Kemudian buka 'Pengaturan Koneksi Aplikasi Seluler' dan pilih 'Tambah Perangkat'. Masukkan Kode PIN yang ditampilkan. Lihat [dokumentasi] (https://www.home-assistant.io/components/ps4/) untuk info lebih lanjut.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Alamat IP (Kosongkan jika menggunakan Penemuan Otomatis).", "mode": "Mode Konfigurasi" }, + "data_description": { + "ip_address": "Kosongkan jika memilih penemuan otomatis." + }, "description": "Pilih mode untuk konfigurasi. Alamat IP dapat dikosongkan jika memilih Penemuan Otomatis karena perangkat akan ditemukan secara otomatis.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/it.json b/homeassistant/components/ps4/translations/it.json index 8df24f24afb..3faca963317 100644 --- a/homeassistant/components/ps4/translations/it.json +++ b/homeassistant/components/ps4/translations/it.json @@ -25,6 +25,9 @@ "name": "Nome", "region": "Area geografica" }, + "data_description": { + "code": "Vai a 'Impostazioni' sulla tua console PlayStation 4. Quindi vai su 'Impostazioni connessione app mobili' e seleziona 'Aggiungi dispositivo' per ottenere il pin." + }, "description": "Inserisci le informazioni per la tua PlayStation 4. Per il codice PIN, vai a \"Impostazioni\" sulla PlayStation 4. Quindi vai a 'Impostazioni di connessione Mobile App' e seleziona 'Aggiungi dispositivo'. Immettere il codice PIN visualizzato. Fai riferimento alla [documentazione](https://www.home-assistant.io/components/ps4/) per ulteriori informazioni.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Indirizzo IP (Lascia vuoto se stai usando il rilevamento automatico).", "mode": "Modalit\u00e0 di configurazione" }, + "data_description": { + "ip_address": "Lascia vuoto se selezioni il rilevamento automatico." + }, "description": "Seleziona la modalit\u00e0 per la configurazione. Il campo per l'indirizzo IP pu\u00f2 essere lasciato vuoto se si seleziona il rilevamento automatico, poich\u00e9 i dispositivi saranno rilevati automaticamente.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/ja.json b/homeassistant/components/ps4/translations/ja.json index 6b1a162b387..92be62f7229 100644 --- a/homeassistant/components/ps4/translations/ja.json +++ b/homeassistant/components/ps4/translations/ja.json @@ -25,6 +25,9 @@ "name": "\u540d\u524d", "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" }, + "data_description": { + "code": "PlayStation 4\u672c\u4f53\u306e' \u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u307e\u3059\u3002\u6b21\u306b\u3001' \u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u63a5\u7d9a\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u3001' \u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0' \u3092\u9078\u629e\u3057\u3066\u3001\u30d4\u30f3\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002" + }, "description": "PlayStation4\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002 PIN\u30b3\u30fc\u30c9(PIN CodePIN Code)\u306e\u5834\u5408\u306f\u3001PlayStation4\u672c\u4f53\u306e '\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u307e\u3059\u3002\u6b21\u306b\u3001'\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u63a5\u7d9a\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u3066\u3001'\u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0' \u3092\u9078\u629e\u3057\u307e\u3059\u3002\u8868\u793a\u3055\u308c\u305f PIN\u30b3\u30fc\u30c9(PIN CodePIN Code) \u3092\u5165\u529b\u3057\u307e\u3059\u3002\u8a73\u7d30\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/components/ps4/) \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "Play Station 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP\u30a2\u30c9\u30ec\u30b9(\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u307e\u3059)", "mode": "\u30b3\u30f3\u30d5\u30a3\u30b0\u30e2\u30fc\u30c9" }, + "data_description": { + "ip_address": "\u81ea\u52d5\u691c\u51fa\u3092\u9078\u629e\u3059\u308b\u5834\u5408\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" + }, "description": "\u30b3\u30f3\u30d5\u30a3\u30ae\u30e5\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30e2\u30fc\u30c9\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u81ea\u52d5\u691c\u51fa\u3092\u9078\u629e\u3059\u308b\u3068\u3001IP\u30a2\u30c9\u30ec\u30b9\u306e\u6b04\u304c\u7a7a\u767d\u306e\u307e\u307e\u3067\u3082\u30c7\u30d0\u30a4\u30b9\u306f\u81ea\u52d5\u7684\u306b\u691c\u51fa\u3055\u308c\u307e\u3059\u3002", "title": "Play Station 4" } diff --git a/homeassistant/components/ps4/translations/nl.json b/homeassistant/components/ps4/translations/nl.json index db59c7ab30a..1a6ddd26f7f 100644 --- a/homeassistant/components/ps4/translations/nl.json +++ b/homeassistant/components/ps4/translations/nl.json @@ -25,6 +25,9 @@ "name": "Naam", "region": "Regio" }, + "data_description": { + "code": "Navigeer naar 'Instellingen' op je PlayStation 4-systeem. Navigeer vervolgens naar 'Instellingen mobiele app-verbinding' en selecteer 'Apparaat toevoegen' om de pin te krijgen." + }, "description": "Voer je PlayStation 4-informatie in. Voor PIN-code navigeer je naar 'Instellingen' op je PlayStation 4-console. Ga vervolgens naar 'Verbindingsinstellingen mobiele app' en selecteer 'Apparaat toevoegen'. Voer de PIN-code in die wordt weergegeven. Raadpleeg de [documentatie](https://www.home-assistant.io/components/ps4/) voor meer informatie.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP-adres (leeg laten indien Auto Discovery wordt gebruikt).", "mode": "Configuratiemodus" }, + "data_description": { + "ip_address": "Leeg laten indien u auto-discovery selecteert." + }, "description": "Selecteer modus voor configuratie. Het veld IP-adres kan leeg worden gelaten als Auto Discovery wordt geselecteerd, omdat apparaten dan automatisch worden ontdekt.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/no.json b/homeassistant/components/ps4/translations/no.json index 185b0e031c5..69332862a33 100644 --- a/homeassistant/components/ps4/translations/no.json +++ b/homeassistant/components/ps4/translations/no.json @@ -25,6 +25,9 @@ "name": "Navn", "region": "" }, + "data_description": { + "code": "Naviger til \"Innstillinger\" p\u00e5 PlayStation 4-konsollen. Naviger deretter til \"Mobile App Connection Settings\" og velg \"Add Device\" for \u00e5 hente pinnen." + }, "description": "Skriv inn PlayStation 4-informasjonen din. For PIN kode , naviger til 'Innstillinger' p\u00e5 PlayStation 4-konsollen. Naviger deretter til 'Innstillinger for tilkobling av mobilapp' og velg 'Legg til enhet'. Skriv inn PIN kode som vises. Se [dokumentasjon] (https://www.home-assistant.io/components/ps4/) for mer informasjon.", "title": "" }, @@ -33,6 +36,9 @@ "ip_address": "IP adresse (La v\u00e6re tom hvis du bruker automatisk oppdagelse)", "mode": "Konfigureringsmodus" }, + "data_description": { + "ip_address": "La st\u00e5 tomt hvis du velger automatisk oppdagelse." + }, "description": "Velg modus for konfigurasjon. Feltet IP adresse kan st\u00e5 tomt hvis du velger automatisk oppdagelse, da enheter automatisk blir oppdaget.", "title": "" } diff --git a/homeassistant/components/ps4/translations/pl.json b/homeassistant/components/ps4/translations/pl.json index 9840363630d..af4d36b9bf2 100644 --- a/homeassistant/components/ps4/translations/pl.json +++ b/homeassistant/components/ps4/translations/pl.json @@ -25,6 +25,9 @@ "name": "Nazwa", "region": "Region" }, + "data_description": { + "code": "Przejd\u017a do \u201eUstawienia\u201d na konsoli PlayStation 4. Nast\u0119pnie przejd\u017a do \u201eUstawienia po\u0142\u0105czenia aplikacji mobilnej\u201d i wybierz \u201eDodaj urz\u0105dzenie\u201d, aby uzyska\u0107 kod PIN." + }, "description": "Wprowad\u017a informacje o PlayStation 4. Aby uzyska\u0107 'PIN', przejd\u017a do 'Ustawienia' na konsoli PlayStation 4. Nast\u0119pnie przejd\u017a do 'Ustawienia po\u0142\u0105czenia aplikacji mobilnej' i wybierz 'Dodaj urz\u0105dzenie'. Wprowad\u017a wy\u015bwietlony kod PIN. Zapoznaj si\u0119 z [dokumentacj\u0105](https://www.home-assistant.io/components/ps4/), by pozna\u0107 szczeg\u00f3\u0142y.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Adres IP (pozostaw puste, je\u015bli u\u017cywasz wykrywania)", "mode": "Tryb konfiguracji" }, + "data_description": { + "ip_address": "Pozostaw puste, je\u015bli wybierasz automatyczne wykrywanie." + }, "description": "Wybierz tryb konfiguracji. Pole adresu IP mo\u017cna pozostawi\u0107 puste, je\u015bli wybierzesz opcj\u0119 Auto Discovery, poniewa\u017c urz\u0105dzenia zostan\u0105 automatycznie wykryte.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/pt-BR.json b/homeassistant/components/ps4/translations/pt-BR.json index 7938cbb6241..12d54264f78 100644 --- a/homeassistant/components/ps4/translations/pt-BR.json +++ b/homeassistant/components/ps4/translations/pt-BR.json @@ -25,6 +25,9 @@ "name": "Nome", "region": "Regi\u00e3o" }, + "data_description": { + "code": "Navegue at\u00e9 'Configura\u00e7\u00f5es' em seu console PlayStation 4. Em seguida, navegue at\u00e9 'Configura\u00e7\u00f5es de conex\u00e3o do aplicativo m\u00f3vel' e selecione 'Adicionar dispositivo' para obter o PIN." + }, "description": "Digite suas informa\u00e7\u00f5es do PlayStation 4. Para C\u00f3digo PIN, navegue at\u00e9 'Configura\u00e7\u00f5es' no seu console PlayStation 4. Em seguida, navegue at\u00e9 \"Configura\u00e7\u00f5es de conex\u00e3o de aplicativos m\u00f3veis\" e selecione \"Adicionar dispositivo\". Digite o C\u00f3digo PIN exibido. Consulte a [documenta\u00e7\u00e3o] (https://www.home-assistant.io/components/ps4/) para informa\u00e7\u00f5es adicionais.", "title": "Playstation 4" }, @@ -33,6 +36,9 @@ "ip_address": "Endere\u00e7o IP (Deixe em branco se estiver usando a Detec\u00e7\u00e3o autom\u00e1tica).", "mode": "Modo de configura\u00e7\u00e3o" }, + "data_description": { + "ip_address": "Deixe em branco se selecionar a descoberta autom\u00e1tica." + }, "description": "Selecione o modo para configura\u00e7\u00e3o. O campo Endere\u00e7o IP pode ser deixado em branco se selecionar Detec\u00e7\u00e3o Autom\u00e1tica, pois os dispositivos ser\u00e3o descobertos automaticamente.", "title": "Playstation 4" } diff --git a/homeassistant/components/ps4/translations/ru.json b/homeassistant/components/ps4/translations/ru.json index ea42e260ec8..646346095b0 100644 --- a/homeassistant/components/ps4/translations/ru.json +++ b/homeassistant/components/ps4/translations/ru.json @@ -25,6 +25,9 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, + "data_description": { + "code": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**." + }, "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](https://www.home-assistant.io/components/ps4/) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441 (\u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0440\u0435\u0436\u0438\u043c\u0430 \u0430\u0432\u0442\u043e\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f)", "mode": "\u0420\u0435\u0436\u0438\u043c" }, + "data_description": { + "ip_address": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435" + }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u041f\u043e\u043b\u0435 'IP-\u0430\u0434\u0440\u0435\u0441' \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c, \u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043e 'Auto Discovery', \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/tr.json b/homeassistant/components/ps4/translations/tr.json index 8243e179a98..a2e8149db7d 100644 --- a/homeassistant/components/ps4/translations/tr.json +++ b/homeassistant/components/ps4/translations/tr.json @@ -25,6 +25,9 @@ "name": "Ad", "region": "B\u00f6lge" }, + "data_description": { + "code": "PlayStation 4 konsolunuzda 'Ayarlar'a gidin. Ard\u0131ndan, \"Mobil Uygulama Ba\u011flant\u0131 Ayarlar\u0131\"na gidin ve PIN'i almak i\u00e7in \"Cihaz Ekle\"yi se\u00e7in." + }, "description": "PlayStation 4 bilgilerinizi girin. PIN Kodu i\u00e7in PlayStation 4 konsolunuzda 'Ayarlar'a gidin. Ard\u0131ndan \"Mobil Uygulama Ba\u011flant\u0131 Ayarlar\u0131\"na gidin ve \"Cihaz Ekle\"yi se\u00e7in. PIN Kodu kodunu girin. Ek bilgi i\u00e7in [belgelere](https://www.home-assistant.io/components/ps4/) bak\u0131n.", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP Adresi (Otomatik Ke\u015fif kullan\u0131l\u0131yorsa bo\u015f b\u0131rak\u0131n).", "mode": "Yap\u0131land\u0131rma Modu" }, + "data_description": { + "ip_address": "Otomatik bulma se\u00e7iliyorsa bo\u015f b\u0131rak\u0131n." + }, "description": "Yap\u0131land\u0131rma i\u00e7in modu se\u00e7in. IP Adresi alan\u0131, Otomatik Ke\u015fif se\u00e7ildi\u011finde cihazlar otomatik olarak ke\u015ffedilece\u011finden bo\u015f b\u0131rak\u0131labilir.", "title": "PlayStation 4" } diff --git a/homeassistant/components/ps4/translations/zh-Hant.json b/homeassistant/components/ps4/translations/zh-Hant.json index e9c8dac2a26..ae7e86377d8 100644 --- a/homeassistant/components/ps4/translations/zh-Hant.json +++ b/homeassistant/components/ps4/translations/zh-Hant.json @@ -25,6 +25,9 @@ "name": "\u540d\u7a31", "region": "\u5340\u57df" }, + "data_description": { + "code": "\u5207\u63db\u81f3 PlayStation 4 \u4e3b\u6a5f\u7684\u300c\u8a2d\u5b9a\u300d\u5167\uff0c\u4e26\u65bc\u300c\u884c\u52d5\u7a0b\u5f0f\u9023\u7dda\u8a2d\u5b9a\uff08Mobile App Connection Settings\uff09\u300d\u4e2d\u9078\u64c7\u300c\u65b0\u589e\u88dd\u7f6e\u300d\u4ee5\u53d6\u5f97 PIN \u78bc\u3002" + }, "description": "\u8f38\u5165\u60a8\u7684 PlayStation 4 \u8cc7\u8a0a\uff0c\u300cPIN \u78bc\u300d\u65bc PlayStation 4 \u4e3b\u6a5f\u7684\u300c\u8a2d\u5b9a\u300d\u5167\uff0c\u4e26\u65bc\u300c\u884c\u52d5\u7a0b\u5f0f\u9023\u7dda\u8a2d\u5b9a\uff08Mobile App Connection Settings\uff09\u300d\u4e2d\u9078\u64c7\u300c\u65b0\u589e\u88dd\u7f6e\u300d\u3002\u8f38\u5165\u6240\u986f\u793a\u7684 PIN \u78bc\u3002\u8acb\u53c3\u8003 [documentation](https://www.home-assistant.io/components/ps4/) \u4ee5\u7372\u5f97\u66f4\u591a\u8cc7\u8a0a\u3002", "title": "PlayStation 4" }, @@ -33,6 +36,9 @@ "ip_address": "IP \u4f4d\u5740\uff08\u5982\u679c\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u65b9\u5f0f\uff0c\u8acb\u4fdd\u7559\u7a7a\u767d\uff09\u3002", "mode": "\u8a2d\u5b9a\u6a21\u5f0f" }, + "data_description": { + "ip_address": "\u5047\u5982\u9078\u64c7\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u8acb\u4fdd\u6301\u7a7a\u767d" + }, "description": "\u9078\u64c7\u6a21\u5f0f\u4ee5\u9032\u884c\u8a2d\u5b9a\u3002\u5047\u5982\u9078\u64c7\u81ea\u52d5\u641c\u7d22\u6a21\u5f0f\u7684\u8a71\uff0c\u7531\u65bc\u6703\u81ea\u52d5\u9032\u884c\u88dd\u7f6e\u641c\u5c0b\uff0cIP \u4f4d\u5740\u53ef\u4fdd\u7559\u70ba\u7a7a\u767d\u3002", "title": "PlayStation 4" } diff --git a/homeassistant/components/pure_energie/translations/cs.json b/homeassistant/components/pure_energie/translations/cs.json new file mode 100644 index 00000000000..76dabdfb963 --- /dev/null +++ b/homeassistant/components/pure_energie/translations/cs.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" + }, + "flow_title": "{model} ({host})", + "step": { + "user": { + "data": { + "host": "Hostitel" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pure_energie/translations/fr.json b/homeassistant/components/pure_energie/translations/fr.json index 7ab74aae074..e121c7f655c 100644 --- a/homeassistant/components/pure_energie/translations/fr.json +++ b/homeassistant/components/pure_energie/translations/fr.json @@ -13,6 +13,10 @@ "data": { "host": "H\u00f4te" } + }, + "zeroconf_confirm": { + "description": "Voulez-vous ajouter Pure Energie Meter (`{model}`) \u00e0 Home Assistant\u00a0?", + "title": "D\u00e9couverte de l'appareil Pure Energie Meter" } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/ja.json b/homeassistant/components/pvpc_hourly_pricing/translations/ja.json index 919186d0e62..d88ee379678 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/ja.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/ja.json @@ -9,7 +9,7 @@ "name": "\u30bb\u30f3\u30b5\u30fc\u540d", "power": "\u5951\u7d04\u96fb\u529b (kW)", "power_p3": "\u8c37\u9593(valley period) P3 (kW)\u306e\u5951\u7d04\u96fb\u529b", - "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e" + "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e(Applicable tariff)" }, "description": "\u3053\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u516c\u5f0fAPI\u3092\u4f7f\u7528\u3057\u3066\u3001\u30b9\u30da\u30a4\u30f3\u3067\u306e[\u96fb\u6c17\u306e\u6642\u9593\u4fa1\u683c((hourly pricing of electricity)PVPC)](https://www.esios.ree.es/es/pvpc) \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\n\u3088\u308a\u6b63\u78ba\u306a\u8aac\u660e\u306b\u3064\u3044\u3066\u306f\u3001[\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3 \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "\u30bb\u30f3\u30b5\u30fc\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" @@ -22,7 +22,7 @@ "data": { "power": "\u5951\u7d04\u96fb\u529b (kW)", "power_p3": "\u8c37\u9593(valley period) P3 (kW)\u306e\u5951\u7d04\u96fb\u529b", - "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e" + "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e(Applicable tariff)" }, "description": "\u3053\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u516c\u5f0fAPI\u3092\u4f7f\u7528\u3057\u3066\u3001\u30b9\u30da\u30a4\u30f3\u3067\u306e[\u96fb\u6c17\u306e\u6642\u9593\u4fa1\u683c(hourly pricing of electricity)PVPC)](https://www.esios.ree.es/es/pvpc) \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\n\u3088\u308a\u6b63\u78ba\u306a\u8aac\u660e\u306b\u3064\u3044\u3066\u306f\u3001[\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3 \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "\u30bb\u30f3\u30b5\u30fc\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" diff --git a/homeassistant/components/qnap_qsw/translations/ca.json b/homeassistant/components/qnap_qsw/translations/ca.json new file mode 100644 index 00000000000..575ed369b7b --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/ca.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "invalid_id": "El dispositiu ha retornat un ID \u00fanic inv\u00e0lid" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "password": "Contrasenya", + "url": "URL", + "username": "Nom d'usuari" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/de.json b/homeassistant/components/qnap_qsw/translations/de.json new file mode 100644 index 00000000000..ad2599e24c4 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/de.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "invalid_id": "Ger\u00e4t hat eine ung\u00fcltige eindeutige ID zur\u00fcckgegeben" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung" + }, + "step": { + "user": { + "data": { + "password": "Passwort", + "url": "URL", + "username": "Benutzername" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/el.json b/homeassistant/components/qnap_qsw/translations/el.json new file mode 100644 index 00000000000..1bea5dcc9d7 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/el.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "invalid_id": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03c0\u03ad\u03c3\u03c4\u03c1\u03b5\u03c8\u03b5 \u03ad\u03bd\u03b1 \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "user": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/en.json b/homeassistant/components/qnap_qsw/translations/en.json index 2d0180d1670..b6f68f2f062 100644 --- a/homeassistant/components/qnap_qsw/translations/en.json +++ b/homeassistant/components/qnap_qsw/translations/en.json @@ -11,9 +11,9 @@ "step": { "user": { "data": { + "password": "Password", "url": "URL", - "username": "Username", - "password": "Password" + "username": "Username" } } } diff --git a/homeassistant/components/qnap_qsw/translations/et.json b/homeassistant/components/qnap_qsw/translations/et.json new file mode 100644 index 00000000000..eb7ddb6dbe7 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/et.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "invalid_id": "Seade tagastas kehtetu kordumatu ID" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus" + }, + "step": { + "user": { + "data": { + "password": "Salas\u00f5na", + "url": "URL", + "username": "Kasutajanimi" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/fr.json b/homeassistant/components/qnap_qsw/translations/fr.json new file mode 100644 index 00000000000..2631bcdfc87 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/fr.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "invalid_id": "L'appareil a renvoy\u00e9 un ID unique non valide" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide" + }, + "step": { + "user": { + "data": { + "password": "Mot de passe", + "url": "URL", + "username": "Nom d'utilisateur" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/he.json b/homeassistant/components/qnap_qsw/translations/he.json new file mode 100644 index 00000000000..fbe984e0b32 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/he.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "user": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/hu.json b/homeassistant/components/qnap_qsw/translations/hu.json new file mode 100644 index 00000000000..c41ba084d95 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/hu.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "invalid_id": "A k\u00e9sz\u00fcl\u00e9k \u00e9rv\u00e9nytelen egyedi azonos\u00edt\u00f3t k\u00fcld\u00f6tt vissza" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" + }, + "step": { + "user": { + "data": { + "password": "Jelsz\u00f3", + "url": "URL", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/id.json b/homeassistant/components/qnap_qsw/translations/id.json new file mode 100644 index 00000000000..b34bcd046d9 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/id.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "invalid_id": "Perangkat mengembalikan ID unik yang tidak valid" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid" + }, + "step": { + "user": { + "data": { + "password": "Kata Sandi", + "url": "URL", + "username": "Nama Pengguna" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/it.json b/homeassistant/components/qnap_qsw/translations/it.json new file mode 100644 index 00000000000..0759ef4ca8a --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/it.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "invalid_id": "Il dispositivo ha restituito un ID univoco non valido" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida" + }, + "step": { + "user": { + "data": { + "password": "Password", + "url": "URL", + "username": "Nome utente" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/nl.json b/homeassistant/components/qnap_qsw/translations/nl.json new file mode 100644 index 00000000000..10ec94f589d --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/nl.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd", + "invalid_id": "Het apparaat heeft een ongeldige unieke ID teruggestuurd" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie" + }, + "step": { + "user": { + "data": { + "password": "Wachtwoord", + "url": "URL", + "username": "Gebruikersnaam" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/no.json b/homeassistant/components/qnap_qsw/translations/no.json new file mode 100644 index 00000000000..9eff9c22080 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/no.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "invalid_id": "Enheten returnerte en ugyldig unik ID" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning" + }, + "step": { + "user": { + "data": { + "password": "Passord", + "url": "URL", + "username": "Brukernavn" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/pl.json b/homeassistant/components/qnap_qsw/translations/pl.json new file mode 100644 index 00000000000..e90d527a7a7 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/pl.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "invalid_id": "Urz\u0105dzenie zwr\u00f3ci\u0142o nieprawid\u0142owy unikalny identyfikator" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie" + }, + "step": { + "user": { + "data": { + "password": "Has\u0142o", + "url": "URL", + "username": "Nazwa u\u017cytkownika" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/pt-BR.json b/homeassistant/components/qnap_qsw/translations/pt-BR.json new file mode 100644 index 00000000000..b9829d78944 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/pt-BR.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", + "invalid_id": "O dispositivo retornou um ID exclusivo inv\u00e1lido" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "password": "Senha", + "url": "URL", + "username": "Usu\u00e1rio" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/ru.json b/homeassistant/components/qnap_qsw/translations/ru.json new file mode 100644 index 00000000000..ae3869552d0 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/ru.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "invalid_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0432\u0435\u0440\u043d\u0443\u043b\u043e \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 ID." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." + }, + "step": { + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "url": "URL-\u0430\u0434\u0440\u0435\u0441", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/zh-Hant.json b/homeassistant/components/qnap_qsw/translations/zh-Hant.json new file mode 100644 index 00000000000..f29c42d6eb6 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/zh-Hant.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "invalid_id": "\u88dd\u7f6e\u56de\u8986\u4e86\u7121\u6548\u7684\u552f\u4e00 ID" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" + }, + "step": { + "user": { + "data": { + "password": "\u5bc6\u78bc", + "url": "\u7db2\u5740", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radio_browser/translations/cs.json b/homeassistant/components/radio_browser/translations/cs.json new file mode 100644 index 00000000000..19f5d1e1587 --- /dev/null +++ b/homeassistant/components/radio_browser/translations/cs.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/radio_browser/translations/fr.json b/homeassistant/components/radio_browser/translations/fr.json index 807ba246694..de1e4f25261 100644 --- a/homeassistant/components/radio_browser/translations/fr.json +++ b/homeassistant/components/radio_browser/translations/fr.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + }, + "step": { + "user": { + "description": "Voulez-vous ajouter le navigateur radio \u00e0 Home Assistant\u00a0?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/radio_browser/translations/he.json b/homeassistant/components/radio_browser/translations/he.json index d0c3523da94..89c5e30a440 100644 --- a/homeassistant/components/radio_browser/translations/he.json +++ b/homeassistant/components/radio_browser/translations/he.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + }, + "step": { + "user": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d3\u05e4\u05d3\u05e4\u05df \u05e8\u05d3\u05d9\u05d5 \u05d0\u05dc Home Assistant?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/remote/translations/hu.json b/homeassistant/components/remote/translations/hu.json index 2291d4f8b94..52b24f79f08 100644 --- a/homeassistant/components/remote/translations/hu.json +++ b/homeassistant/components/remote/translations/hu.json @@ -10,6 +10,7 @@ "is_on": "{entity_name} be van kapcsolva" }, "trigger_type": { + "changed_states": "{entity_name} be- vagy kikapcsolt", "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" diff --git a/homeassistant/components/roku/translations/hu.json b/homeassistant/components/roku/translations/hu.json index 256a8d45997..c28b0a712ea 100644 --- a/homeassistant/components/roku/translations/hu.json +++ b/homeassistant/components/roku/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "error": { diff --git a/homeassistant/components/roku/translations/it.json b/homeassistant/components/roku/translations/it.json index 8c2d9d4e84a..9f42c49c74f 100644 --- a/homeassistant/components/roku/translations/it.json +++ b/homeassistant/components/roku/translations/it.json @@ -12,16 +12,16 @@ "step": { "discovery_confirm": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "description": "Vuoi configurare {name}?", "title": "Roku" }, "ssdp_confirm": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "description": "Vuoi impostare {name}?", "title": "Roku" diff --git a/homeassistant/components/roomba/translations/pl.json b/homeassistant/components/roomba/translations/pl.json index c56c00a98b0..d2d76bee5ff 100644 --- a/homeassistant/components/roomba/translations/pl.json +++ b/homeassistant/components/roomba/translations/pl.json @@ -16,7 +16,7 @@ "host": "Nazwa hosta lub adres IP" }, "description": "Wybierz Roomb\u0119 lub Braava", - "title": "Po\u0142\u0105cz si\u0119 automatycznie z urz\u0105dzeniem" + "title": "Automatyczne po\u0142\u0105czenie z urz\u0105dzeniem" }, "link": { "description": "Naci\u015bnij i przytrzymaj przycisk Home na {name} a\u017c urz\u0105dzenie wygeneruje d\u017awi\u0119k (oko\u0142o dwie sekundy), a nast\u0119pnie zatwierd\u017a w ci\u0105gu 30 sekund.", @@ -46,7 +46,7 @@ "password": "Has\u0142o" }, "description": "Wybierz Roomb\u0119 lub Braava", - "title": "Po\u0142\u0105cz si\u0119 automatycznie z urz\u0105dzeniem" + "title": "Automatyczne po\u0142\u0105czenie z urz\u0105dzeniem" } } }, diff --git a/homeassistant/components/roon/translations/ca.json b/homeassistant/components/roon/translations/ca.json index f05ac1a4acb..3f601ded490 100644 --- a/homeassistant/components/roon/translations/ca.json +++ b/homeassistant/components/roon/translations/ca.json @@ -8,6 +8,13 @@ "unknown": "Error inesperat" }, "step": { + "fallback": { + "data": { + "host": "Amfitri\u00f3", + "port": "Port" + }, + "description": "No s'ha pogut descobrir el servidor Roon, introdueix l'amfitri\u00f3 i el port." + }, "link": { "description": "Has d'autoritzar Home Assistant a Roon. Despr\u00e9s de fer clic a envia, ves a l'aplicaci\u00f3 Roon Core, obre la Configuraci\u00f3 i activa Home Assistant a la pestanya d'Extensions.", "title": "Autoritza Home Assistant a Roon" diff --git a/homeassistant/components/roon/translations/de.json b/homeassistant/components/roon/translations/de.json index 4eadf9c363a..e41fe77c3cd 100644 --- a/homeassistant/components/roon/translations/de.json +++ b/homeassistant/components/roon/translations/de.json @@ -8,6 +8,13 @@ "unknown": "Unerwarteter Fehler" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Port" + }, + "description": "Der Roon-Server konnte nicht gefunden werden, bitte gib deinen Hostnamen und Port ein." + }, "link": { "description": "Du musst den Home Assistant in Roon autorisieren. Nachdem du auf \"Submit\" geklickt hast, gehe zur Roon Core-Anwendung, \u00f6ffne die Einstellungen und aktiviere HomeAssistant auf der Registerkarte \"Extensions\".", "title": "HomeAssistant in Roon autorisieren" diff --git a/homeassistant/components/roon/translations/el.json b/homeassistant/components/roon/translations/el.json index e88cbed685c..63cda0821b1 100644 --- a/homeassistant/components/roon/translations/el.json +++ b/homeassistant/components/roon/translations/el.json @@ -8,6 +8,13 @@ "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { + "fallback": { + "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "port": "\u0398\u03cd\u03c1\u03b1" + }, + "description": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Roon, \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03c3\u03b1\u03c2." + }, "link": { "description": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03c3\u03c4\u03bf Roon. \u0391\u03c6\u03bf\u03cd \u03ba\u03ac\u03bd\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Roon Core, \u03b1\u03bd\u03bf\u03af\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf HomeAssistant \u03c3\u03c4\u03b7\u03bd \u03ba\u03b1\u03c1\u03c4\u03ad\u03bb\u03b1 \u0395\u03c0\u03b5\u03ba\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2.", "title": "\u0395\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf HomeAssistant \u03c3\u03c4\u03bf Roon" diff --git a/homeassistant/components/roon/translations/en.json b/homeassistant/components/roon/translations/en.json index d1f86fbce5f..b0affedc026 100644 --- a/homeassistant/components/roon/translations/en.json +++ b/homeassistant/components/roon/translations/en.json @@ -8,6 +8,13 @@ "unknown": "Unexpected error" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Port" + }, + "description": "Could not discover Roon server, please enter your Hostname and Port." + }, "link": { "description": "You must authorize Home Assistant in Roon. After you click submit, go to the Roon Core application, open Settings and enable HomeAssistant on the Extensions tab.", "title": "Authorize HomeAssistant in Roon" diff --git a/homeassistant/components/roon/translations/et.json b/homeassistant/components/roon/translations/et.json index bbdf2b5edc5..d091f8e4064 100644 --- a/homeassistant/components/roon/translations/et.json +++ b/homeassistant/components/roon/translations/et.json @@ -8,6 +8,13 @@ "unknown": "Tundmatu viga" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Port" + }, + "description": "Rooni serverit ei leitud, sisesta hostinimi ja port." + }, "link": { "description": "Pead Roonis koduabilise volitama. Kui oled kl\u00f5psanud nuppu Esita, mine rakendusse Roon Core, ava Seaded ja luba vahekaardil Laiendused Home Assistant.", "title": "Volita HomeAssistant Roonis" diff --git a/homeassistant/components/roon/translations/fr.json b/homeassistant/components/roon/translations/fr.json index b0fb3e6784b..7af98de188a 100644 --- a/homeassistant/components/roon/translations/fr.json +++ b/homeassistant/components/roon/translations/fr.json @@ -8,6 +8,13 @@ "unknown": "Erreur inattendue" }, "step": { + "fallback": { + "data": { + "host": "H\u00f4te", + "port": "Port" + }, + "description": "Impossible de trouver le serveur Roon, veuillez saisir le nom de l'h\u00f4te et le port." + }, "link": { "description": "Vous devez autoriser Home Assistant dans Roon. Apr\u00e8s avoir cliqu\u00e9 sur soumettre, acc\u00e9dez \u00e0 l'application Roon Core, ouvrez Param\u00e8tres et activez Home Assistant dans l'onglet Extensions.", "title": "Autoriser Home Assistant dans Roon" diff --git a/homeassistant/components/roon/translations/he.json b/homeassistant/components/roon/translations/he.json index 9d3230d257e..05781910772 100644 --- a/homeassistant/components/roon/translations/he.json +++ b/homeassistant/components/roon/translations/he.json @@ -8,6 +8,12 @@ "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { + "fallback": { + "data": { + "host": "\u05de\u05d0\u05e8\u05d7", + "port": "\u05e4\u05ea\u05d7\u05d4" + } + }, "link": { "description": "\u05e2\u05dc\u05d9\u05da \u05dc\u05d0\u05e9\u05e8 \u05dc-Home Assistant \u05d1-Roon. \u05dc\u05d0\u05d7\u05e8 \u05e9\u05ea\u05dc\u05d7\u05e5 \u05e2\u05dc \u05e9\u05dc\u05d7, \u05e2\u05d1\u05d5\u05e8 \u05dc\u05d9\u05d9\u05e9\u05d5\u05dd Roon Core, \u05e4\u05ea\u05d7 \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d5\u05d4\u05e4\u05e2\u05dc \u05d0\u05ea HomeAssistant \u05d1\u05db\u05e8\u05d8\u05d9\u05e1\u05d9\u05d9\u05d4 \u05d4\u05e8\u05d7\u05d1\u05d5\u05ea." }, diff --git a/homeassistant/components/roon/translations/hu.json b/homeassistant/components/roon/translations/hu.json index 7d2b63f0f4b..3a9bcf3f522 100644 --- a/homeassistant/components/roon/translations/hu.json +++ b/homeassistant/components/roon/translations/hu.json @@ -8,15 +8,24 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { + "fallback": { + "data": { + "host": "C\u00edm", + "port": "Port" + }, + "description": "Nem siker\u00fclt felfedezni a Roon-kiszolg\u00e1l\u00f3t. K\u00e9rj\u00fck, adja meg g\u00e9p c\u00edm\u00e9t \u00e9s portj\u00e1t." + }, "link": { - "description": "Enged\u00e9lyeznie kell az Home Assistantot a Roonban. Miut\u00e1n r\u00e1kattintott a K\u00fcld\u00e9s gombra, nyissa meg a Roon Core alkalmaz\u00e1st, nyissa meg a Be\u00e1ll\u00edt\u00e1sokat, \u00e9s enged\u00e9lyezze a Home Assistant funkci\u00f3t a B\u0151v\u00edtm\u00e9nyek lapon.", + "description": "Enged\u00e9lyeznie kell az Home Assistantot a Roonban. Miut\u00e1n r\u00e1kattintott a Mehet gombra, nyissa meg a Roon Core alkalmaz\u00e1st, nyissa meg a Be\u00e1ll\u00edt\u00e1sokat, \u00e9s enged\u00e9lyezze a Home Assistant funkci\u00f3t a B\u0151v\u00edtm\u00e9nyek lapon.", "title": "Enged\u00e9lyezze a Home Assistant alkalmaz\u00e1st Roon-ban" }, "user": { "data": { "host": "C\u00edm" }, - "description": "A Roon szerver nem tal\u00e1lhat\u00f3, adja meg a hosztnev\u00e9t vagy c\u00edm\u00e9t" + "description": "A Roon szerver nem tal\u00e1lhat\u00f3, adja meg a hosztnev\u00e9t vagy c\u00edm\u00e9t", + "one": "\u00dcres", + "other": "\u00dcres" } } } diff --git a/homeassistant/components/roon/translations/id.json b/homeassistant/components/roon/translations/id.json index 96b26640320..85c1519e7be 100644 --- a/homeassistant/components/roon/translations/id.json +++ b/homeassistant/components/roon/translations/id.json @@ -8,6 +8,13 @@ "unknown": "Kesalahan yang tidak diharapkan" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Port" + }, + "description": "Tidak dapat menemukan server Roon, masukkan Nama Host dan Port Anda." + }, "link": { "description": "Anda harus mengotorisasi Home Assistant di Roon. Setelah Anda mengeklik kirim, buka aplikasi Roon Core, buka Pengaturan dan aktifkan HomeAssistant pada tab Ekstensi.", "title": "Otorisasi HomeAssistant di Roon" diff --git a/homeassistant/components/roon/translations/it.json b/homeassistant/components/roon/translations/it.json index ac5922e1e56..3236ebd0bb2 100644 --- a/homeassistant/components/roon/translations/it.json +++ b/homeassistant/components/roon/translations/it.json @@ -8,6 +8,13 @@ "unknown": "Errore imprevisto" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Porta" + }, + "description": "Impossibile scoprire il server Roon, inserisci il tuo nome host e la porta." + }, "link": { "description": "Devi autorizzare l'Assistente Home in Roon. Dopo aver fatto clic su Invia, passa all'applicazione Roon Core, apri Impostazioni e abilita HomeAssistant nella scheda Estensioni.", "title": "Autorizza HomeAssistant in Roon" @@ -16,7 +23,9 @@ "data": { "host": "Host" }, - "description": "Impossibile individuare il server Roon, inserire l'hostname o l'IP." + "description": "Impossibile individuare il server Roon, inserire l'hostname o l'IP.", + "one": "Vuoto", + "other": "Vuoti" } } } diff --git a/homeassistant/components/roon/translations/ja.json b/homeassistant/components/roon/translations/ja.json index dba8ccc9237..400d982d27c 100644 --- a/homeassistant/components/roon/translations/ja.json +++ b/homeassistant/components/roon/translations/ja.json @@ -8,6 +8,13 @@ "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { + "fallback": { + "data": { + "host": "\u30db\u30b9\u30c8", + "port": "\u30dd\u30fc\u30c8" + }, + "description": "Roon\u30b5\u30fc\u30d0\u30fc\u3092\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30db\u30b9\u30c8\u540d\u3068\u30dd\u30fc\u30c8\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, "link": { "description": "Roon\u3067Home Assistant\u3092\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u9001\u4fe1(submit) \u3092\u30af\u30ea\u30c3\u30af\u3057\u305f\u5f8c\u3001Roon Core\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u3001\u8a2d\u5b9a(Settings )\u3092\u958b\u304d\u3001\u6a5f\u80fd\u62e1\u5f35\u30bf\u30d6(extensions tab)\u3067Home Assistant\u3092\u6709\u52b9(enable )\u306b\u3057\u307e\u3059\u3002", "title": "Roon\u3067HomeAssistant\u3092\u8a8d\u8a3c\u3059\u308b" diff --git a/homeassistant/components/roon/translations/nl.json b/homeassistant/components/roon/translations/nl.json index df8fa80b4dd..9aafadc918b 100644 --- a/homeassistant/components/roon/translations/nl.json +++ b/homeassistant/components/roon/translations/nl.json @@ -8,6 +8,13 @@ "unknown": "Onverwachte fout" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Poort" + }, + "description": "Kon de Roon server niet vinden, voer uw Hostnaam en Poort in." + }, "link": { "description": "U moet Home Assistant autoriseren in Roon. Nadat je op verzenden hebt geklikt, ga je naar de Roon Core-applicatie, open je Instellingen en schakel je Home Assistant in op het tabblad Extensies.", "title": "Autoriseer Home Assistant in Roon" diff --git a/homeassistant/components/roon/translations/no.json b/homeassistant/components/roon/translations/no.json index 2558c2c27c7..11eb60a04ce 100644 --- a/homeassistant/components/roon/translations/no.json +++ b/homeassistant/components/roon/translations/no.json @@ -8,6 +8,13 @@ "unknown": "Uventet feil" }, "step": { + "fallback": { + "data": { + "host": "Vert", + "port": "Port" + }, + "description": "Kunne ikke finne Roon-serveren, skriv inn vertsnavnet og porten." + }, "link": { "description": "Du m\u00e5 godkjenne Home Assistant i Roon. N\u00e5r du klikker send inn, g\u00e5r du til Roon Core-programmet, \u00e5pner innstillingene og aktiverer Home Assistant p\u00e5 utvidelser-fanen.", "title": "Autoriser Home Assistant i Roon" diff --git a/homeassistant/components/roon/translations/pl.json b/homeassistant/components/roon/translations/pl.json index fab5fb9657e..b45a7fcb562 100644 --- a/homeassistant/components/roon/translations/pl.json +++ b/homeassistant/components/roon/translations/pl.json @@ -8,6 +8,13 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { + "fallback": { + "data": { + "host": "Nazwa hosta lub adres IP", + "port": "Port" + }, + "description": "Nie mo\u017cna wykry\u0107 serwera Roon, wprowad\u017a nazw\u0119 hosta i port." + }, "link": { "description": "Musisz autoryzowa\u0107 Home Assistant w Roon. Po klikni\u0119ciu przycisku \"Zatwierd\u017a\", przejd\u017a do aplikacji Roon Core, otw\u00f3rz \"Ustawienia\" i w\u0142\u0105cz Home Assistant w karcie \"Rozszerzenia\" (Extensions).", "title": "Autoryzuj Home Assistant w Roon" diff --git a/homeassistant/components/roon/translations/pt-BR.json b/homeassistant/components/roon/translations/pt-BR.json index a96222b15f3..85628df4a7c 100644 --- a/homeassistant/components/roon/translations/pt-BR.json +++ b/homeassistant/components/roon/translations/pt-BR.json @@ -8,6 +8,13 @@ "unknown": "Erro inesperado" }, "step": { + "fallback": { + "data": { + "host": "Nome do host", + "port": "Porta" + }, + "description": "N\u00e3o foi poss\u00edvel descobrir o servidor Roon, digite seu nome de host e porta." + }, "link": { "description": "Voc\u00ea deve autorizar o Home Assistant no Roon. Depois de clicar em enviar, v\u00e1 para o aplicativo Roon principal, abra Configura\u00e7\u00f5es e habilite o HomeAssistant na aba Extens\u00f5es.", "title": "Autorizar HomeAssistant no Roon" diff --git a/homeassistant/components/roon/translations/ru.json b/homeassistant/components/roon/translations/ru.json index 1bcb07c7695..cc89dc2d2dd 100644 --- a/homeassistant/components/roon/translations/ru.json +++ b/homeassistant/components/roon/translations/ru.json @@ -8,6 +8,13 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { + "fallback": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "port": "\u041f\u043e\u0440\u0442" + }, + "description": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Roon, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438 \u043f\u043e\u0440\u0442." + }, "link": { "description": "\u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u00ab\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\u00bb \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Roon Core, \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u00bb \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0435 HomeAssistant \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u00ab\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u00bb.", "title": "Roon" diff --git a/homeassistant/components/roon/translations/tr.json b/homeassistant/components/roon/translations/tr.json index 75fedf6383e..c18df90b9b8 100644 --- a/homeassistant/components/roon/translations/tr.json +++ b/homeassistant/components/roon/translations/tr.json @@ -8,6 +8,13 @@ "unknown": "Beklenmeyen hata" }, "step": { + "fallback": { + "data": { + "host": "Sunucu", + "port": "Port" + }, + "description": "Roon sunucusu bulunamad\u0131, l\u00fctfen Ana Bilgisayar Ad\u0131n\u0131z\u0131 ve Ba\u011flant\u0131 Noktan\u0131z\u0131 girin." + }, "link": { "description": "Roon'da HomeAssistant\u0131 yetkilendirmelisiniz. G\u00f6nder'e t\u0131klad\u0131ktan sonra, Roon Core uygulamas\u0131na gidin, Ayarlar'\u0131 a\u00e7\u0131n ve Uzant\u0131lar sekmesinde HomeAssistant'\u0131 etkinle\u015ftirin.", "title": "Roon'da HomeAssistant'\u0131 Yetkilendirme" diff --git a/homeassistant/components/roon/translations/zh-Hant.json b/homeassistant/components/roon/translations/zh-Hant.json index f8f52d11b1c..f887b2d9847 100644 --- a/homeassistant/components/roon/translations/zh-Hant.json +++ b/homeassistant/components/roon/translations/zh-Hant.json @@ -8,6 +8,13 @@ "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { + "fallback": { + "data": { + "host": "\u4e3b\u6a5f\u7aef", + "port": "\u901a\u8a0a\u57e0" + }, + "description": "\u627e\u4e0d\u5230 Roon \u4f3a\u670d\u5668\uff0c\u8acb\u8f38\u5165\u4e3b\u6a5f\u540d\u7a31\u53ca\u901a\u8a0a\u57e0\u3002" + }, "link": { "description": "\u5fc5\u9808\u65bc Roon \u4e2d\u8a8d\u8b49 Home Assistant\u3002\u9ede\u9078\u50b3\u9001\u5f8c\u3001\u958b\u555f Roon Core \u61c9\u7528\u7a0b\u5f0f\u3001\u6253\u958b\u8a2d\u5b9a\u4e26\u65bc\u64f4\u5145\uff08Extensions\uff09\u4e2d\u555f\u7528 HomeAssistant\u3002", "title": "\u65bc Roon \u4e2d\u8a8d\u8b49 HomeAssistant" diff --git a/homeassistant/components/rtsp_to_webrtc/translations/hu.json b/homeassistant/components/rtsp_to_webrtc/translations/hu.json index 75c471a6710..5da10dd88e4 100644 --- a/homeassistant/components/rtsp_to_webrtc/translations/hu.json +++ b/homeassistant/components/rtsp_to_webrtc/translations/hu.json @@ -2,7 +2,8 @@ "config": { "abort": { "server_failure": "Az RTSPtoWebRTC szerver hib\u00e1t jelzett vissza. Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt ellen\u0151rizze a napl\u00f3kat.", - "server_unreachable": "Nem lehet kommunik\u00e1lni az RTSPtoWebRTC szerverrel. Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt ellen\u0151rizze a napl\u00f3kat." + "server_unreachable": "Nem lehet kommunik\u00e1lni az RTSPtoWebRTC szerverrel. Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt ellen\u0151rizze a napl\u00f3kat.", + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "error": { "invalid_url": "\u00c9rv\u00e9nyes RTSPtoWebRTC szerver URL-nek kell lennie, pl. https://example.com", diff --git a/homeassistant/components/vallox/translations/is.json b/homeassistant/components/sabnzbd/translations/bg.json similarity index 79% rename from homeassistant/components/vallox/translations/is.json rename to homeassistant/components/sabnzbd/translations/bg.json index 6878b2ecf11..02c83a6e916 100644 --- a/homeassistant/components/vallox/translations/is.json +++ b/homeassistant/components/sabnzbd/translations/bg.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "name": "Nafn" + "url": "URL" } } } diff --git a/homeassistant/components/sabnzbd/translations/ca.json b/homeassistant/components/sabnzbd/translations/ca.json new file mode 100644 index 00000000000..6c980e23fa1 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/ca.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_api_key": "Clau API inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "api_key": "Clau API", + "name": "Nom", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/de.json b/homeassistant/components/sabnzbd/translations/de.json new file mode 100644 index 00000000000..9f128f2878f --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/de.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel" + }, + "step": { + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "name": "Name", + "path": "Pfad", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/el.json b/homeassistant/components/sabnzbd/translations/el.json new file mode 100644 index 00000000000..f3d461e3dee --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/el.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "step": { + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae", + "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/en.json b/homeassistant/components/sabnzbd/translations/en.json index 2336ba4e198..4e857c42b64 100644 --- a/homeassistant/components/sabnzbd/translations/en.json +++ b/homeassistant/components/sabnzbd/translations/en.json @@ -9,11 +9,9 @@ "data": { "api_key": "API Key", "name": "Name", - "url": "URL", - "path": "Path" - }, - "description": "If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/sabnzbd/", - "title": "Sabnzbd" + "path": "Path", + "url": "URL" + } } } } diff --git a/homeassistant/components/sabnzbd/translations/et.json b/homeassistant/components/sabnzbd/translations/et.json new file mode 100644 index 00000000000..b940ab99569 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/et.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_api_key": "Kehtetu API v\u00f5ti" + }, + "step": { + "user": { + "data": { + "api_key": "API v\u00f5ti", + "name": "Nimi", + "path": "Rada", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/fr.json b/homeassistant/components/sabnzbd/translations/fr.json new file mode 100644 index 00000000000..9809ccf8a0b --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/fr.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_api_key": "Cl\u00e9 d'API non valide" + }, + "step": { + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "name": "Nom", + "path": "Chemin d'acc\u00e8s", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/he.json b/homeassistant/components/sabnzbd/translations/he.json new file mode 100644 index 00000000000..1574a5e7719 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/he.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "name": "\u05e9\u05dd", + "path": "\u05e0\u05ea\u05d9\u05d1", + "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/hu.json b/homeassistant/components/sabnzbd/translations/hu.json new file mode 100644 index 00000000000..0136c11fc88 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/hu.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs" + }, + "step": { + "user": { + "data": { + "api_key": "API kulcs", + "name": "Elnevez\u00e9s", + "path": "\u00datvonal", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/id.json b/homeassistant/components/sabnzbd/translations/id.json new file mode 100644 index 00000000000..caa7e73815e --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/id.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_api_key": "Kunci API tidak valid" + }, + "step": { + "user": { + "data": { + "api_key": "Kunci API", + "name": "Nama", + "path": "Jalur", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/it.json b/homeassistant/components/sabnzbd/translations/it.json new file mode 100644 index 00000000000..48f2dea100c --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/it.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_api_key": "Chiave API non valida" + }, + "step": { + "user": { + "data": { + "api_key": "Chiave API", + "name": "Nome", + "path": "Percorso", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/nl.json b/homeassistant/components/sabnzbd/translations/nl.json new file mode 100644 index 00000000000..c737cd0d07e --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/nl.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_api_key": "Ongeldige API-sleutel" + }, + "step": { + "user": { + "data": { + "api_key": "API-sleutel", + "name": "Naam", + "path": "Pad", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/no.json b/homeassistant/components/sabnzbd/translations/no.json new file mode 100644 index 00000000000..4da8a925a29 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/no.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_api_key": "Ugyldig API-n\u00f8kkel" + }, + "step": { + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "name": "Navn", + "path": "Sti", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/pl.json b/homeassistant/components/sabnzbd/translations/pl.json new file mode 100644 index 00000000000..b083df2b8dc --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/pl.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_api_key": "Nieprawid\u0142owy klucz API" + }, + "step": { + "user": { + "data": { + "api_key": "Klucz API", + "name": "Nazwa", + "path": "\u015acie\u017cka", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/pt-BR.json b/homeassistant/components/sabnzbd/translations/pt-BR.json new file mode 100644 index 00000000000..b9015b40b14 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/pt-BR.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_api_key": "Chave de API inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "api_key": "Chave da API", + "name": "Nome", + "path": "Caminho", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/ru.json b/homeassistant/components/sabnzbd/translations/ru.json new file mode 100644 index 00000000000..68e5f255f96 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/ru.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API." + }, + "step": { + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "path": "\u041f\u0443\u0442\u044c", + "url": "URL-\u0430\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/zh-Hant.json b/homeassistant/components/sabnzbd/translations/zh-Hant.json new file mode 100644 index 00000000000..018952ba66c --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/zh-Hant.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_api_key": "API \u91d1\u9470\u7121\u6548" + }, + "step": { + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "name": "\u540d\u7a31", + "path": "\u8def\u5f91", + "url": "\u7db2\u5740" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/samsungtv/translations/bg.json b/homeassistant/components/samsungtv/translations/bg.json index 332b6b62236..2246b4ad954 100644 --- a/homeassistant/components/samsungtv/translations/bg.json +++ b/homeassistant/components/samsungtv/translations/bg.json @@ -6,11 +6,17 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, + "error": { + "invalid_pin": "\u041f\u0418\u041d \u043a\u043e\u0434\u044a\u0442 \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e." + }, "flow_title": "{device}", "step": { "confirm": { "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u041f\u0418\u041d \u043a\u043e\u0434\u0430, \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u043d\u0430 {device}." + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", diff --git a/homeassistant/components/samsungtv/translations/ca.json b/homeassistant/components/samsungtv/translations/ca.json index e701bdb1d92..95738ae65d9 100644 --- a/homeassistant/components/samsungtv/translations/ca.json +++ b/homeassistant/components/samsungtv/translations/ca.json @@ -12,7 +12,8 @@ "unknown": "Error inesperat" }, "error": { - "auth_missing": "Home Assistant no est\u00e0 autenticat per connectar-se amb aquest televisor Samsung. V\u00e9s a la configuraci\u00f3 de dispositius externs del televisor per autoritzar Home Assistant." + "auth_missing": "Home Assistant no est\u00e0 autenticat per connectar-se amb aquest televisor Samsung. V\u00e9s a la configuraci\u00f3 de dispositius externs del televisor per autoritzar Home Assistant.", + "invalid_pin": "El PIN \u00e9s inv\u00e0lid; torna-ho a provar." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Vols configurar {device}? Si mai abans has connectat Home Assistant hauries de veure una finestra emergent a la pantalla del televisor demanant autenticaci\u00f3.", "title": "Televisor Samsung" }, + "encrypted_pairing": { + "description": "Introdueix el PIN que es mostra a {device}." + }, + "pairing": { + "description": "Vols configurar {device}? Si mai abans has connectat Home Assistant hauries de veure una finestra emergent a la pantalla del televisor demanant autenticaci\u00f3." + }, "reauth_confirm": { - "description": "Despr\u00e9s d'enviar, tens 30 segons per acceptar la finestra emergent de {device} que sol\u00b7licita autoritzaci\u00f3." + "description": "Despr\u00e9s d'enviar, tens 30 segons per acceptar o introduir el PIN a la finestra emergent de {device} que sol\u00b7licita autoritzaci\u00f3." + }, + "reauth_confirm_encrypted": { + "description": "Introdueix el PIN que es mostra a {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/cs.json b/homeassistant/components/samsungtv/translations/cs.json index d45c9247b4e..4c2241d4caa 100644 --- a/homeassistant/components/samsungtv/translations/cs.json +++ b/homeassistant/components/samsungtv/translations/cs.json @@ -19,6 +19,15 @@ "description": "Chcete nastavit {device}? Pokud jste Home Assistant doposud nikdy nep\u0159ipojili, m\u011bla by se v\u00e1m na televizi zobrazit \u017e\u00e1dost o povolen\u00ed.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Zadejte pros\u00edm PIN zobrazen\u00fd na {device}." + }, + "pairing": { + "description": "Chcete nastavit {device}? Pokud jste Home Assistant doposud nikdy nep\u0159ipojili, m\u011bla by se v\u00e1m na televizi zobrazit \u017e\u00e1dost o povolen\u00ed." + }, + "reauth_confirm_encrypted": { + "description": "Zadejte pros\u00edm PIN zobrazen\u00fd na {device}." + }, "user": { "data": { "host": "Hostitel", diff --git a/homeassistant/components/samsungtv/translations/de.json b/homeassistant/components/samsungtv/translations/de.json index ec5b791626a..bfacbcd4b3c 100644 --- a/homeassistant/components/samsungtv/translations/de.json +++ b/homeassistant/components/samsungtv/translations/de.json @@ -12,7 +12,8 @@ "unknown": "Unerwarteter Fehler" }, "error": { - "auth_missing": "Home Assistant ist nicht berechtigt, eine Verbindung zu diesem Samsung TV herzustellen. \u00dcberpr\u00fcfe den Ger\u00e4teverbindungsmanager in den Einstellungen deines Fernsehger\u00e4ts, um Home Assistant zu autorisieren." + "auth_missing": "Home Assistant ist nicht berechtigt, eine Verbindung zu diesem Samsung TV herzustellen. \u00dcberpr\u00fcfe den Ger\u00e4teverbindungsmanager in den Einstellungen deines Fernsehger\u00e4ts, um Home Assistant zu autorisieren.", + "invalid_pin": "PIN ist ung\u00fcltig, bitte versuche es erneut." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "M\u00f6chtest du Samsung TV {device} einrichten? Wenn du noch nie eine Verbindung zum Home Assistant hergestellt hast, solltest du eine Meldung auf deinem Fernseher sehen, die nach einer Autorisierung fragt. Manuelle Konfigurationen f\u00fcr dieses Fernsehger\u00e4t werden \u00fcberschrieben.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Bitte gib die PIN ein, die auf {device} angezeigt wird." + }, + "pairing": { + "description": "M\u00f6chtest du Samsung TV {device} einrichten? Wenn du noch nie eine Verbindung zum Home Assistant hergestellt hast, solltest du eine Meldung auf deinem Fernseher sehen, die nach einer Autorisierung fragt. Manuelle Konfigurationen f\u00fcr dieses Fernsehger\u00e4t werden \u00fcberschrieben." + }, "reauth_confirm": { - "description": "Akzeptiere nach dem Absenden die Meldung auf {device}, das eine Autorisierung innerhalb von 30 Sekunden anfordert." + "description": "Akzeptiere nach dem Absenden das Popup-Fenster auf {device}, das dich auffordert, sich innerhalb von 30 Sekunden zu autorisieren, oder gib die PIN ein." + }, + "reauth_confirm_encrypted": { + "description": "Bitte gib die PIN ein, die auf {device} angezeigt wird." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/el.json b/homeassistant/components/samsungtv/translations/el.json index aa2ec61978b..0e6c4df03bc 100644 --- a/homeassistant/components/samsungtv/translations/el.json +++ b/homeassistant/components/samsungtv/translations/el.json @@ -12,17 +12,27 @@ "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { - "auth_missing": "\u03a4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7 Samsung. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03b7\u03c2 \u0394\u03b9\u03b1\u03c7\u03b5\u03af\u03c1\u03b9\u03c3\u03b7\u03c2 \u03b5\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ce\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd \u03c4\u03b7\u03c2 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant." + "auth_missing": "\u03a4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7 Samsung. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03b7\u03c2 \u0394\u03b9\u03b1\u03c7\u03b5\u03af\u03c1\u03b9\u03c3\u03b7\u03c2 \u03b5\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ce\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd \u03c4\u03b7\u03c2 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant.", + "invalid_pin": "\u03a4\u03bf PIN \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, "flow_title": "{device}", "step": { "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 {device}; \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9 \u03c0\u03bf\u03c4\u03ad \u03c4\u03bf Home Assistant \u03c0\u03c1\u03b9\u03bd, \u03b8\u03b1 \u03b4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7.", + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {device}; \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9 \u03c0\u03bf\u03c4\u03ad \u03c4\u03bf Home Assistant \u03c0\u03c1\u03b9\u03bd, \u03b8\u03b1 \u03b4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf PIN \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 {device}." + }, + "pairing": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {device}; \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9 \u03c0\u03bf\u03c4\u03ad \u03c4\u03bf Home Assistant \u03c0\u03c1\u03b9\u03bd, \u03b8\u03b1 \u03b4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7." + }, "reauth_confirm": { "description": "\u039c\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae, \u03b1\u03c0\u03bf\u03b4\u03b5\u03c7\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7 {device} \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7 \u03b5\u03bd\u03c4\u03cc\u03c2 30 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03bf\u03bb\u03ad\u03c0\u03c4\u03c9\u03bd." }, + "reauth_confirm_encrypted": { + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf PIN \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 {device}." + }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", diff --git a/homeassistant/components/samsungtv/translations/en.json b/homeassistant/components/samsungtv/translations/en.json index c4e0e181090..827f9992e46 100644 --- a/homeassistant/components/samsungtv/translations/en.json +++ b/homeassistant/components/samsungtv/translations/en.json @@ -6,6 +6,7 @@ "auth_missing": "Home Assistant is not authorized to connect to this Samsung TV. Check your TV's External Device Manager settings to authorize Home Assistant.", "cannot_connect": "Failed to connect", "id_missing": "This Samsung device doesn't have a SerialNumber.", + "missing_config_entry": "This Samsung device doesn't have a configuration entry.", "not_supported": "This Samsung device is currently not supported.", "reauth_successful": "Re-authentication was successful", "unknown": "Unexpected error" @@ -17,7 +18,8 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Do you want to set up {device}? If you never connected Home Assistant before you should see a popup on your TV asking for authorization." + "description": "Do you want to set up {device}? If you never connected Home Assistant before you should see a popup on your TV asking for authorization.", + "title": "Samsung TV" }, "encrypted_pairing": { "description": "Please enter the PIN displayed on {device}." diff --git a/homeassistant/components/samsungtv/translations/et.json b/homeassistant/components/samsungtv/translations/et.json index 47360f4ed06..94e9db78ec2 100644 --- a/homeassistant/components/samsungtv/translations/et.json +++ b/homeassistant/components/samsungtv/translations/et.json @@ -12,7 +12,8 @@ "unknown": "Tundmatu t\u00f5rge" }, "error": { - "auth_missing": "Tuvastamine nurjus" + "auth_missing": "Tuvastamine nurjus", + "invalid_pin": "PIN on kehtetu, proovi uuesti." }, "flow_title": "{devicel}", "step": { @@ -20,8 +21,17 @@ "description": "Kas soovid seadistada {devicel} ? Kui seda pole kunagi enne Home Assistantiga \u00fchendatud, n\u00e4ed oma teleris h\u00fcpikakent, mis k\u00fcsib tuvastamist.", "title": "" }, + "encrypted_pairing": { + "description": "Sisesta seadmes {device} kuvatav PIN-kood." + }, + "pairing": { + "description": "Kas h\u00e4\u00e4lestada seade {device}? Kui varem pole Home Assistantiga \u00fchendutud peaks teler kuvama h\u00fcpikakna tuvastusteabe sisestamiseks." + }, "reauth_confirm": { - "description": "P\u00e4rast esitamist n\u00f5ustu {device} h\u00fcpikaknaga, mis taotleb autoriseerimist 30 sekundi jooksul." + "description": "P\u00e4rast esitamist n\u00f5ustu {device} h\u00fcpikaknaga, mis taotleb autoriseerimist 30 sekundi jooksul v\u00f5i sisesta PIN." + }, + "reauth_confirm_encrypted": { + "description": "Sisesta seadmes {device} kuvatav PIN-kood." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/fr.json b/homeassistant/components/samsungtv/translations/fr.json index 1d9bebb5569..438336f2818 100644 --- a/homeassistant/components/samsungtv/translations/fr.json +++ b/homeassistant/components/samsungtv/translations/fr.json @@ -12,16 +12,26 @@ "unknown": "Erreur inattendue" }, "error": { - "auth_missing": "Home Assistant n'est pas autoris\u00e9 \u00e0 se connecter \u00e0 ce t\u00e9l\u00e9viseur Samsung. Veuillez v\u00e9rifier les param\u00e8tres de votre t\u00e9l\u00e9viseur pour autoriser Home Assistant." + "auth_missing": "Home Assistant n'est pas autoris\u00e9 \u00e0 se connecter \u00e0 ce t\u00e9l\u00e9viseur Samsung. Veuillez v\u00e9rifier les param\u00e8tres de votre t\u00e9l\u00e9viseur pour autoriser Home Assistant.", + "invalid_pin": "Le code PIN n'est pas valide, veuillez r\u00e9essayer." }, "flow_title": "{device}", "step": { "confirm": { - "description": "Voulez vous installer la TV {device} Samsung? Si vous n'avez jamais connect\u00e9 Home Assistant avant, vous devriez voir une fen\u00eatre contextuelle sur votre t\u00e9l\u00e9viseur demandant une authentification. Les configurations manuelles de ce t\u00e9l\u00e9viseur seront \u00e9cras\u00e9es.", + "description": "Voulez-vous configurer {device}\u00a0? Si vous n'avez jamais connect\u00e9 Home Assistant auparavant, une fen\u00eatre contextuelle d'autorisation devrait appara\u00eetre sur votre t\u00e9l\u00e9viseur.", "title": "TV Samsung" }, + "encrypted_pairing": { + "description": "Veuillez saisir le code PIN affich\u00e9 sur {device}." + }, + "pairing": { + "description": "Voulez-vous configurer {device}\u00a0? Si vous n'avez jamais connect\u00e9 Home Assistant auparavant, une fen\u00eatre contextuelle d'autorisation devrait appara\u00eetre sur votre t\u00e9l\u00e9viseur." + }, "reauth_confirm": { - "description": "Apr\u00e8s avoir soumis, acceptez la fen\u00eatre contextuelle sur {device} demandant l'autorisation dans les 30 secondes." + "description": "Une fois envoy\u00e9, acceptez la fen\u00eatre contextuelle de demande d'autorisation sur {device} dans les 30\u00a0secondes ou saisissez le code PIN." + }, + "reauth_confirm_encrypted": { + "description": "Veuillez saisir le code PIN affich\u00e9 sur {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/hu.json b/homeassistant/components/samsungtv/translations/hu.json index a85f295317d..dc42596e290 100644 --- a/homeassistant/components/samsungtv/translations/hu.json +++ b/homeassistant/components/samsungtv/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "auth_missing": "Home Assistant nem jogosult csatlakozni ehhez a Samsung TV-hez. Ellen\u0151rizze a TV be\u00e1ll\u00edt\u00e1sait Home Assistant enged\u00e9lyez\u00e9s\u00e9hez.", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "id_missing": "Ennek a Samsung eszk\u00f6znek nincs sorsz\u00e1ma.", @@ -12,7 +12,8 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "error": { - "auth_missing": "Home Assistant nem jogosult csatlakozni ehhez a Samsung TV-hez. Ellen\u0151rizze a TV be\u00e1ll\u00edt\u00e1sait Home Assistant enged\u00e9lyez\u00e9s\u00e9hez." + "auth_missing": "Home Assistant nem jogosult csatlakozni ehhez a Samsung TV-hez. Ellen\u0151rizze a TV be\u00e1ll\u00edt\u00e1sait Home Assistant enged\u00e9lyez\u00e9s\u00e9hez.", + "invalid_pin": "A PIN k\u00f3d \u00e9rv\u00e9nytelen, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra." }, "flow_title": "{device}", "step": { @@ -20,13 +21,22 @@ "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {device} k\u00e9sz\u00fcl\u00e9k\u00e9t? Ha kor\u00e1bban m\u00e9g sosem csatlakoztatta Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ami j\u00f3v\u00e1hagy\u00e1sra v\u00e1r.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "K\u00e9rj\u00fck, adja meg az eszk\u00f6z\u00f6n megjelen\u0151 PIN-k\u00f3dot: {device}" + }, + "pairing": { + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {device} k\u00e9sz\u00fcl\u00e9k\u00e9t? Ha kor\u00e1bban m\u00e9g sosem csatlakoztatta Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ami j\u00f3v\u00e1hagy\u00e1sra v\u00e1r." + }, "reauth_confirm": { - "description": "A bek\u00fcld\u00e9s ut\u00e1n, fogadja el a {device} felugr\u00f3 ablak\u00e1ban l\u00e1that\u00f3 \u00fczenetet, mely 30 m\u00e1sodpercig \u00e1ll rendelkez\u00e9sre." + "description": "A bek\u00fcld\u00e9s ut\u00e1n, fogadja el a {device} felugr\u00f3 ablak\u00e1ban l\u00e1that\u00f3 \u00fczenetet, mely 30 m\u00e1sodpercig \u00e1ll rendelkez\u00e9sre, vagy adja meg a PIN k\u00f3dot." + }, + "reauth_confirm_encrypted": { + "description": "K\u00e9rj\u00fck, adja meg az eszk\u00f6z\u00f6n megjelen\u0151 PIN-k\u00f3dot: {device}" }, "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "\u00cdrja be a Samsung TV adatait. Ha m\u00e9g soha nem csatlakozott Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ahol meg kell adni az enged\u00e9lyt." } diff --git a/homeassistant/components/samsungtv/translations/id.json b/homeassistant/components/samsungtv/translations/id.json index 0714af37146..394cfc41ae1 100644 --- a/homeassistant/components/samsungtv/translations/id.json +++ b/homeassistant/components/samsungtv/translations/id.json @@ -12,7 +12,8 @@ "unknown": "Kesalahan yang tidak diharapkan" }, "error": { - "auth_missing": "Home Assistant tidak diizinkan untuk tersambung ke TV Samsung ini. Periksa Manajer Perangkat Eksternal TV Anda untuk mengotorisasi Home Assistant." + "auth_missing": "Home Assistant tidak diizinkan untuk tersambung ke TV Samsung ini. Periksa Manajer Perangkat Eksternal TV Anda untuk mengotorisasi Home Assistant.", + "invalid_pin": "PIN tidak valid, silakan coba lagi." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Apakah Anda ingin menyiapkan {device}? Jika Anda belum pernah menyambungkan Home Assistant sebelumnya, Anda akan melihat dialog di TV yang meminta otorisasi.", "title": "TV Samsung" }, + "encrypted_pairing": { + "description": "Masukkan PIN yang ditampilkan di {device} ." + }, + "pairing": { + "description": "Apakah Anda ingin menyiapkan {device}? Jika Anda belum pernah menyambungkan Home Assistant sebelumnya, Anda akan melihat dialog di TV yang meminta otorisasi." + }, "reauth_confirm": { - "description": "Setelah mengirimkan, setujui pada popup di {device} yang meminta otorisasi dalam waktu 30 detik." + "description": "Setelah mengirimkan, setujui pada popup di {device} yang meminta otorisasi dalam waktu 30 detik atau masukkan PIN." + }, + "reauth_confirm_encrypted": { + "description": "Masukkan PIN yang ditampilkan di {device} ." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/it.json b/homeassistant/components/samsungtv/translations/it.json index 51f9b4e2ef9..6bfc26fb445 100644 --- a/homeassistant/components/samsungtv/translations/it.json +++ b/homeassistant/components/samsungtv/translations/it.json @@ -12,7 +12,8 @@ "unknown": "Errore imprevisto" }, "error": { - "auth_missing": "Home Assistant non \u00e8 autorizzato a connettersi a questo televisore Samsung. Controlla le impostazioni di Gestione dispositivi esterni della tua TV per autorizzare Home Assistant." + "auth_missing": "Home Assistant non \u00e8 autorizzato a connettersi a questo televisore Samsung. Controlla le impostazioni di Gestione dispositivi esterni della tua TV per autorizzare Home Assistant.", + "invalid_pin": "Il PIN non \u00e8 valido, riprova." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Vuoi configurare {device}? Se non hai mai collegato Home Assistant, dovresti vedere un popup sulla tua TV che chiede l'autorizzazione.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Digita il PIN visualizzato su {device}." + }, + "pairing": { + "description": "Vuoi configurare {device}? Se non hai mai collegato Home Assistant, dovresti vedere un popup sulla tua TV che chiede l'autorizzazione." + }, "reauth_confirm": { - "description": "Dopo l'invio, accetta il popup su {device} richiedendo l'autorizzazione entro 30 secondi." + "description": "Dopo l'invio, accetta la notifica su {device} richiedendo l'autorizzazione entro 30 secondi o digita il PIN." + }, + "reauth_confirm_encrypted": { + "description": "Digita il PIN visualizzato su {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/ja.json b/homeassistant/components/samsungtv/translations/ja.json index 0cfad60efcb..d1b5670e25c 100644 --- a/homeassistant/components/samsungtv/translations/ja.json +++ b/homeassistant/components/samsungtv/translations/ja.json @@ -12,7 +12,8 @@ "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "error": { - "auth_missing": "Home Assistant\u306f\u3001\u3053\u306eSamsungTV\u3078\u306e\u63a5\u7d9a\u3092\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c6\u30ec\u30d3\u306e\u5916\u90e8\u30c7\u30d0\u30a4\u30b9\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u306e\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u3001Home Assistant\u3092\u8a8d\u8a3c\u3057\u307e\u3059\u3002" + "auth_missing": "Home Assistant\u306f\u3001\u3053\u306eSamsungTV\u3078\u306e\u63a5\u7d9a\u3092\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c6\u30ec\u30d3\u306e\u5916\u90e8\u30c7\u30d0\u30a4\u30b9\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u306e\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u3001Home Assistant\u3092\u8a8d\u8a3c\u3057\u307e\u3059\u3002", + "invalid_pin": "PIN\u304c\u7121\u52b9\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002" }, "flow_title": "{device}", "step": { @@ -20,9 +21,18 @@ "description": "{device} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f\u3053\u308c\u307e\u3067\u306bHome Assistant\u3092\u4e00\u5ea6\u3082\u63a5\u7d9a\u3057\u305f\u3053\u3068\u304c\u306a\u3044\u5834\u5408\u306f\u3001\u30c6\u30ec\u30d3\u306b\u8a8d\u8a3c\u3092\u6c42\u3081\u308b\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "{device} \u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308bPIN\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, + "pairing": { + "description": "{device} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f\u3053\u308c\u307e\u3067\u306bHome Assistant\u3092\u4e00\u5ea6\u3082\u63a5\u7d9a\u3057\u305f\u3053\u3068\u304c\u306a\u3044\u5834\u5408\u306f\u3001\u30c6\u30ec\u30d3\u306b\u8a8d\u8a3c\u3092\u6c42\u3081\u308b\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002" + }, "reauth_confirm": { "description": "\u9001\u4fe1(submit)\u3001\u8a8d\u8a3c\u3092\u8981\u6c42\u3059\u308b {device} \u306e\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u3092\u300130\u79d2\u4ee5\u5185\u306b\u53d7\u3051\u5165\u308c\u307e\u3059\u3002" }, + "reauth_confirm_encrypted": { + "description": "{device} \u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308bPIN\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, "user": { "data": { "host": "\u30db\u30b9\u30c8", diff --git a/homeassistant/components/samsungtv/translations/nl.json b/homeassistant/components/samsungtv/translations/nl.json index 692c70642d6..b75f9b5970e 100644 --- a/homeassistant/components/samsungtv/translations/nl.json +++ b/homeassistant/components/samsungtv/translations/nl.json @@ -12,7 +12,8 @@ "unknown": "Onverwachte fout" }, "error": { - "auth_missing": "Home Assistant is niet gemachtigd om verbinding te maken met deze Samsung TV. Controleer de instellingen van Extern apparaatbeheer van uw tv om Home Assistant te machtigen." + "auth_missing": "Home Assistant is niet gemachtigd om verbinding te maken met deze Samsung TV. Controleer de instellingen van Extern apparaatbeheer van uw tv om Home Assistant te machtigen.", + "invalid_pin": "Pincode is ongeldig, probeer het opnieuw." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Wilt u Samsung TV {device} instellen? Als u nooit eerder Home Assistant hebt verbonden dan zou u een popup op uw TV moeten zien waarin u om toestemming wordt vraagt. Handmatige configuraties voor deze TV worden overschreven", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Voer de pincode in die wordt weergegeven op {device} ." + }, + "pairing": { + "description": "Wil je {device} instellen? Als je de Home Assistant nog nooit eerder hebt aangesloten, zou je een pop-up op je tv moeten zien die om autorisatie vraagt." + }, "reauth_confirm": { - "description": "Na het indienen, accepteer binnen 30 seconden de pop-up op {device} om autorisatie toe te staan." + "description": "Na het indienen, accepteer binnen 30 seconden de pop-up op {device} om autorisatie toe te staan of voer PIN in." + }, + "reauth_confirm_encrypted": { + "description": "Voer de pincode in die wordt weergegeven op {device} ." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/no.json b/homeassistant/components/samsungtv/translations/no.json index 7b7108cbf77..2125b407f29 100644 --- a/homeassistant/components/samsungtv/translations/no.json +++ b/homeassistant/components/samsungtv/translations/no.json @@ -12,7 +12,8 @@ "unknown": "Uventet feil" }, "error": { - "auth_missing": "Home Assistant er ikke autorisert til \u00e5 koble til denne Samsung TV-en. Sjekk TV-ens innstillinger for ekstern enhetsbehandling for \u00e5 autorisere Home Assistant." + "auth_missing": "Home Assistant er ikke autorisert til \u00e5 koble til denne Samsung TV-en. Sjekk TV-ens innstillinger for ekstern enhetsbehandling for \u00e5 autorisere Home Assistant.", + "invalid_pin": "PIN-koden er ugyldig, pr\u00f8v igjen." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Vil du konfigurere {device} ? Hvis du aldri har koblet til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 TV-en din som ber om autorisasjon.", "title": "" }, + "encrypted_pairing": { + "description": "Vennligst skriv inn PIN-koden som vises p\u00e5 {device} ." + }, + "pairing": { + "description": "Vil du konfigurere {device} ? Hvis du aldri har koblet til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 TV-en din som ber om autorisasjon." + }, "reauth_confirm": { - "description": "Etter innsending, godta popup-vinduet p\u00e5 {device} ber om autorisasjon innen 30 sekunder." + "description": "Etter innsending, godta popup-vinduet p\u00e5 {device} ber om autorisasjon innen 30 sekunder eller angi PIN-kode." + }, + "reauth_confirm_encrypted": { + "description": "Vennligst skriv inn PIN-koden som vises p\u00e5 {device} ." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/pl.json b/homeassistant/components/samsungtv/translations/pl.json index 1f810248f92..015f048dc60 100644 --- a/homeassistant/components/samsungtv/translations/pl.json +++ b/homeassistant/components/samsungtv/translations/pl.json @@ -12,7 +12,8 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "error": { - "auth_missing": "Home Assistant nie ma uprawnie\u0144 do po\u0142\u0105czenia si\u0119 z tym telewizorem Samsung. Sprawd\u017a ustawienia \"Mened\u017cera urz\u0105dze\u0144 zewn\u0119trznych\", aby autoryzowa\u0107 Home Assistant." + "auth_missing": "Home Assistant nie ma uprawnie\u0144 do po\u0142\u0105czenia si\u0119 z tym telewizorem Samsung. Sprawd\u017a ustawienia \"Mened\u017cera urz\u0105dze\u0144 zewn\u0119trznych\", aby autoryzowa\u0107 Home Assistant.", + "invalid_pin": "Nieprawid\u0142owy kod PIN, spr\u00f3buj ponownie." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Czy chcesz skonfigurowa\u0107 {device}? Je\u015bli nigdy wcze\u015bniej nie \u0142\u0105czy\u0142e\u015b go z Home Assistantem, na jego ekranie powinna pojawi\u0107 si\u0119 pro\u015bba o uwierzytelnienie.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Wprowad\u017a kod PIN wy\u015bwietlony na {device}." + }, + "pairing": { + "description": "Czy chcesz skonfigurowa\u0107 {device}? Je\u015bli nigdy wcze\u015bniej nie \u0142\u0105czy\u0142e\u015b go z Home Assistantem, na jego ekranie powinna pojawi\u0107 si\u0119 pro\u015bba o uwierzytelnienie." + }, "reauth_confirm": { - "description": "Po wys\u0142aniu \u017c\u0105dania, zaakceptuj wyskakuj\u0105ce okienko na {device} z pro\u015bb\u0105 o autoryzacj\u0119 w ci\u0105gu 30 sekund." + "description": "Po wys\u0142aniu \u017c\u0105dania, zaakceptuj wyskakuj\u0105ce okienko na {device} z pro\u015bb\u0105 o autoryzacj\u0119 w ci\u0105gu 30 sekund lub wprowad\u017a PIN." + }, + "reauth_confirm_encrypted": { + "description": "Wprowad\u017a kod PIN wy\u015bwietlony na {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/pt-BR.json b/homeassistant/components/samsungtv/translations/pt-BR.json index 407e9d94d0a..dbb4b50fe93 100644 --- a/homeassistant/components/samsungtv/translations/pt-BR.json +++ b/homeassistant/components/samsungtv/translations/pt-BR.json @@ -12,7 +12,8 @@ "unknown": "Erro inesperado" }, "error": { - "auth_missing": "O Home Assistant n\u00e3o est\u00e1 autorizado a se conectar a esta TV Samsung. Verifique as configura\u00e7\u00f5es do Gerenciador de dispositivos externos da sua TV para autorizar o Home Assistant." + "auth_missing": "O Home Assistant n\u00e3o est\u00e1 autorizado a se conectar a esta TV Samsung. Verifique as configura\u00e7\u00f5es do Gerenciador de dispositivos externos da sua TV para autorizar o Home Assistant.", + "invalid_pin": "O PIN \u00e9 inv\u00e1lido, tente novamente." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "Deseja configurar {device}? Se voc\u00ea nunca conectou o Home Assistant antes, aparecer\u00e1 um pop-up na sua TV pedindo autoriza\u00e7\u00e3o.", "title": "TV Samsung" }, + "encrypted_pairing": { + "description": "Insira o PIN exibido em {device}." + }, + "pairing": { + "description": "Deseja configurar {device}? Se voc\u00ea nunca conectou o Home Assistant antes, aparecer\u00e1 um pop-up na sua TV pedindo autoriza\u00e7\u00e3o." + }, "reauth_confirm": { - "description": "Ap\u00f3s o envio, aceite o pop-up em {device} solicitando autoriza\u00e7\u00e3o em 30 segundos." + "description": "Ap\u00f3s o envio, aceite o pop-up em {device} solicitando autoriza\u00e7\u00e3o em 30 segundos ou insira o PIN." + }, + "reauth_confirm_encrypted": { + "description": "Insira o PIN exibido em {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/ru.json b/homeassistant/components/samsungtv/translations/ru.json index 111b30c5488..ee73070cf0e 100644 --- a/homeassistant/components/samsungtv/translations/ru.json +++ b/homeassistant/components/samsungtv/translations/ru.json @@ -12,7 +12,8 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { - "auth_missing": "Home Assistant \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Samsung TV. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 External Device Manager \u0412\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430." + "auth_missing": "Home Assistant \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Samsung TV. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 External Device Manager \u0412\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430.", + "invalid_pin": "PIN-\u043a\u043e\u0434 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {device}? \u0415\u0441\u043b\u0438 \u0412\u044b \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u043b\u0438 \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043a Home Assistant, \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", "title": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Samsung" }, + "encrypted_pairing": { + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 PIN-\u043a\u043e\u0434, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0439 \u043d\u0430 {device}." + }, + "pairing": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {device}? \u0415\u0441\u043b\u0438 \u0412\u044b \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u043b\u0438 \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043a Home Assistant, \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438." + }, "reauth_confirm": { - "description": "\u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u0442\u043e\u0439 \u0444\u043e\u0440\u043c\u044b, \u043f\u0440\u0438\u043c\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u043e \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u043c \u043e\u043a\u043d\u0435 \u043d\u0430 {device} \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 30 \u0441\u0435\u043a\u0443\u043d\u0434." + "description": "\u041f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u044d\u0442\u043e\u0439 \u0444\u043e\u0440\u043c\u044b, \u043f\u0440\u0438\u043c\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u043e \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u043c \u043e\u043a\u043d\u0435 \u043d\u0430 {device} \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 30 \u0441\u0435\u043a\u0443\u043d\u0434 \u0438\u043b\u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 PIN-\u043a\u043e\u0434." + }, + "reauth_confirm_encrypted": { + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 PIN-\u043a\u043e\u0434, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0439 \u043d\u0430 {device}." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/tr.json b/homeassistant/components/samsungtv/translations/tr.json index 399f135bbe8..668b321e26d 100644 --- a/homeassistant/components/samsungtv/translations/tr.json +++ b/homeassistant/components/samsungtv/translations/tr.json @@ -12,7 +12,8 @@ "unknown": "Beklenmeyen hata" }, "error": { - "auth_missing": "Home Assistant'\u0131n bu Samsung TV'ye ba\u011flanma izni yok. Home Assistant'\u0131 yetkilendirmek i\u00e7in l\u00fctfen TV'nin ayarlar\u0131n\u0131 kontrol et." + "auth_missing": "Home Assistant'\u0131n bu Samsung TV'ye ba\u011flanma izni yok. Home Assistant'\u0131 yetkilendirmek i\u00e7in l\u00fctfen TV'nin ayarlar\u0131n\u0131 kontrol et.", + "invalid_pin": "PIN ge\u00e7ersiz, l\u00fctfen tekrar deneyin." }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "{device} kurulumunu yapmak istiyor musunuz? Home Assistant'\u0131 daha \u00f6nce hi\u00e7 ba\u011flamad\u0131ysan\u0131z, TV'nizde yetki isteyen bir a\u00e7\u0131l\u0131r pencere g\u00f6rmelisiniz.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "L\u00fctfen {device} \u00fczerinde g\u00f6r\u00fcnt\u00fclenen PIN'i girin." + }, + "pairing": { + "description": "{device} kurulumunu yapmak istiyor musunuz? Home Assistant'\u0131 daha \u00f6nce hi\u00e7 ba\u011flamad\u0131ysan\u0131z, TV'nizde yetki isteyen bir a\u00e7\u0131l\u0131r pencere g\u00f6rmelisiniz." + }, "reauth_confirm": { - "description": "G\u00f6nderdikten sonra, 30 saniye i\u00e7inde yetkilendirme isteyen {device} \u00fczerindeki a\u00e7\u0131l\u0131r pencereyi kabul edin." + "description": "G\u00f6nderdikten sonra, 30 saniye i\u00e7inde yetkilendirme isteyen {device} \u00fczerindeki a\u00e7\u0131l\u0131r pencereyi kabul edin veya PIN'i girin." + }, + "reauth_confirm_encrypted": { + "description": "L\u00fctfen {device} \u00fczerinde g\u00f6r\u00fcnt\u00fclenen PIN'i girin." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/zh-Hant.json b/homeassistant/components/samsungtv/translations/zh-Hant.json index ba828665cea..8f30090dd24 100644 --- a/homeassistant/components/samsungtv/translations/zh-Hant.json +++ b/homeassistant/components/samsungtv/translations/zh-Hant.json @@ -12,7 +12,8 @@ "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "error": { - "auth_missing": "Home Assistant \u672a\u7372\u5f97\u9a57\u8b49\u4ee5\u9023\u7dda\u81f3\u6b64\u4e09\u661f\u96fb\u8996\u3002\u8acb\u6aa2\u67e5\u60a8\u7684\u96fb\u8996\u5916\u90e8\u88dd\u7f6e\u7ba1\u7406\u54e1\u8a2d\u5b9a\u4ee5\u9032\u884c\u9a57\u8b49\u3002" + "auth_missing": "Home Assistant \u672a\u7372\u5f97\u9a57\u8b49\u4ee5\u9023\u7dda\u81f3\u6b64\u4e09\u661f\u96fb\u8996\u3002\u8acb\u6aa2\u67e5\u60a8\u7684\u96fb\u8996\u5916\u90e8\u88dd\u7f6e\u7ba1\u7406\u54e1\u8a2d\u5b9a\u4ee5\u9032\u884c\u9a57\u8b49\u3002", + "invalid_pin": "PIN \u78bc\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002" }, "flow_title": "{device}", "step": { @@ -20,8 +21,17 @@ "description": "\u662f\u5426\u8981\u8a2d\u5b9a {device}\uff1f\u5047\u5982\u60a8\u4e4b\u524d\u672a\u66fe\u9023\u7dda\u81f3 Home Assistant\uff0c\u61c9\u8a72\u6703\u65bc\u96fb\u8996\u4e0a\u6536\u5230\u9a57\u8b49\u8a0a\u606f\u3002", "title": "\u4e09\u661f\u96fb\u8996" }, + "encrypted_pairing": { + "description": "\u8acb\u8f38\u5165\u986f\u793a\u65bc {device} \u4e0a\u7684 PIN \u78bc\u3002" + }, + "pairing": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {device}\uff1f\u5047\u5982\u60a8\u4e4b\u524d\u672a\u66fe\u9023\u7dda\u81f3 Home Assistant\uff0c\u61c9\u8a72\u6703\u65bc\u96fb\u8996\u4e0a\u6536\u5230\u9a57\u8b49\u8a0a\u606f\u3002" + }, "reauth_confirm": { - "description": "\u50b3\u9001\u5f8c\u3001\u8acb\u65bc 30 \u79d2\u5167\u540c\u610f {device} \u4e0a\u9396\u986f\u793a\u7684\u5f48\u51fa\u8996\u7a97\u6388\u6b0a\u8a31\u53ef\u3002" + "description": "\u50b3\u9001\u5f8c\u3001\u8acb\u65bc 30 \u79d2\u5167\u540c\u610f {device} \u4e0a\u9396\u986f\u793a\u7684\u5f48\u51fa\u8996\u7a97\u6388\u6b0a\u8a31\u53ef\u6216\u8f38\u5165 PIN\u3002" + }, + "reauth_confirm_encrypted": { + "description": "\u8acb\u8f38\u5165\u986f\u793a\u65bc {device} \u4e0a\u7684 PIN \u78bc\u3002" }, "user": { "data": { diff --git a/homeassistant/components/screenlogic/translations/fr.json b/homeassistant/components/screenlogic/translations/fr.json index 62f26aae2b6..458deec97de 100644 --- a/homeassistant/components/screenlogic/translations/fr.json +++ b/homeassistant/components/screenlogic/translations/fr.json @@ -20,7 +20,7 @@ "data": { "selected_gateway": "Passerelle" }, - "description": "Les passerelles ScreenLogic suivantes ont \u00e9t\u00e9 d\u00e9couvertes. S\u2019il vous pla\u00eet s\u00e9lectionner un \u00e0 configurer, ou choisissez de configurer manuellement une passerelle ScreenLogic.", + "description": "Les passerelles ScreenLogic suivantes ont \u00e9t\u00e9 d\u00e9couvertes. Veuillez s\u00e9lectionner celle \u00e0 configurer. Vous pouvez \u00e9galement choisir de configurer une passerelle ScreenLogic manuellement.", "title": "ScreenLogic" } } diff --git a/homeassistant/components/season/translations/cs.json b/homeassistant/components/season/translations/cs.json new file mode 100644 index 00000000000..17ff202bb38 --- /dev/null +++ b/homeassistant/components/season/translations/cs.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena" + }, + "step": { + "user": { + "data": { + "type": "Typ definice sez\u00f3ny" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sense/translations/cs.json b/homeassistant/components/sense/translations/cs.json index 59984b18992..aa5613d3ab4 100644 --- a/homeassistant/components/sense/translations/cs.json +++ b/homeassistant/components/sense/translations/cs.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", + "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", @@ -9,6 +10,12 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { + "reauth_validate": { + "data": { + "password": "Heslo" + }, + "title": "Znovu ov\u011b\u0159it integraci" + }, "user": { "data": { "email": "E-mail", diff --git a/homeassistant/components/sense/translations/fr.json b/homeassistant/components/sense/translations/fr.json index 000240517b9..ece97b4df4f 100644 --- a/homeassistant/components/sense/translations/fr.json +++ b/homeassistant/components/sense/translations/fr.json @@ -14,6 +14,7 @@ "data": { "password": "Mot de passe" }, + "description": "L'int\u00e9gration Sense doit r\u00e9-authentifier votre compte {email}.", "title": "R\u00e9-authentifier l'int\u00e9gration" }, "user": { @@ -27,7 +28,8 @@ "validation": { "data": { "code": "Code de v\u00e9rification" - } + }, + "title": "Authentification multi-facteurs Sense" } } } diff --git a/homeassistant/components/sensibo/translations/hu.json b/homeassistant/components/sensibo/translations/hu.json index 802b718cc82..065a7b982ed 100644 --- a/homeassistant/components/sensibo/translations/hu.json +++ b/homeassistant/components/sensibo/translations/hu.json @@ -20,7 +20,7 @@ "user": { "data": { "api_key": "API kulcs", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" } } } diff --git a/homeassistant/components/sensor/translations/pl.json b/homeassistant/components/sensor/translations/pl.json index 05c0a69708c..b6e93865425 100644 --- a/homeassistant/components/sensor/translations/pl.json +++ b/homeassistant/components/sensor/translations/pl.json @@ -7,8 +7,8 @@ "is_carbon_monoxide": "obecny poziom st\u0119\u017cenia tlenku w\u0119gla w {entity_name}", "is_current": "obecne nat\u0119\u017cenie pr\u0105du {entity_name}", "is_energy": "obecna energia {entity_name}", - "is_frequency": "Obecna cz\u0119stotliwo\u015b\u0107 {entity_name}", - "is_gas": "Obecny poziom gazu {entity_name}", + "is_frequency": "obecna cz\u0119stotliwo\u015b\u0107 {entity_name}", + "is_gas": "obecny poziom gazu {entity_name}", "is_humidity": "obecna wilgotno\u015b\u0107 {entity_name}", "is_illuminance": "obecne nat\u0119\u017cenie o\u015bwietlenia {entity_name}", "is_nitrogen_dioxide": "obecny poziom st\u0119\u017cenia dwutlenku azotu {entity_name}", diff --git a/homeassistant/components/sentry/translations/ca.json b/homeassistant/components/sentry/translations/ca.json index 25d5a5bf37e..d83abb16f1e 100644 --- a/homeassistant/components/sentry/translations/ca.json +++ b/homeassistant/components/sentry/translations/ca.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Introdueix el DSN de Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/de.json b/homeassistant/components/sentry/translations/de.json index 62aca6a65e9..d408af0d885 100644 --- a/homeassistant/components/sentry/translations/de.json +++ b/homeassistant/components/sentry/translations/de.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry-DSN" }, "description": "Gib deine Sentry-DSN ein", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/en.json b/homeassistant/components/sentry/translations/en.json index f00116c85d9..16bdac6b510 100644 --- a/homeassistant/components/sentry/translations/en.json +++ b/homeassistant/components/sentry/translations/en.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Enter your Sentry DSN", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/et.json b/homeassistant/components/sentry/translations/et.json index 5c501b2b0aa..01386fa97e6 100644 --- a/homeassistant/components/sentry/translations/et.json +++ b/homeassistant/components/sentry/translations/et.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "" + "dsn": "Sentry DSN" }, "description": "Sisesta oma Sentry DSN", "title": "" diff --git a/homeassistant/components/sentry/translations/fr.json b/homeassistant/components/sentry/translations/fr.json index acad5566ec3..6850bfe8a71 100644 --- a/homeassistant/components/sentry/translations/fr.json +++ b/homeassistant/components/sentry/translations/fr.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "DSN Sentry" }, "description": "Entrez votre DSN Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/he.json b/homeassistant/components/sentry/translations/he.json index 3383895686e..2a89b9e5556 100644 --- a/homeassistant/components/sentry/translations/he.json +++ b/homeassistant/components/sentry/translations/he.json @@ -9,7 +9,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" } } } diff --git a/homeassistant/components/sentry/translations/hu.json b/homeassistant/components/sentry/translations/hu.json index 9c28d57eb5d..0535657c3d0 100644 --- a/homeassistant/components/sentry/translations/hu.json +++ b/homeassistant/components/sentry/translations/hu.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Adja meg a Sentry DSN-t", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/id.json b/homeassistant/components/sentry/translations/id.json index b2004e8e3d5..5f81eb1a445 100644 --- a/homeassistant/components/sentry/translations/id.json +++ b/homeassistant/components/sentry/translations/id.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Masukkan DSN Sentry Anda", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/it.json b/homeassistant/components/sentry/translations/it.json index 9e3e6974883..10a0d1bad2b 100644 --- a/homeassistant/components/sentry/translations/it.json +++ b/homeassistant/components/sentry/translations/it.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "DSN Sentry" }, "description": "Inserisci il tuo DSN Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/nl.json b/homeassistant/components/sentry/translations/nl.json index 50af985b47c..67be245ffde 100644 --- a/homeassistant/components/sentry/translations/nl.json +++ b/homeassistant/components/sentry/translations/nl.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Voer uw Sentry DSN in", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/no.json b/homeassistant/components/sentry/translations/no.json index 31f4ef30d12..c3bb4acd26e 100644 --- a/homeassistant/components/sentry/translations/no.json +++ b/homeassistant/components/sentry/translations/no.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Fyll inn din Sentry DNS", "title": "" diff --git a/homeassistant/components/sentry/translations/pl.json b/homeassistant/components/sentry/translations/pl.json index 8d1bc2092aa..be5d008dcff 100644 --- a/homeassistant/components/sentry/translations/pl.json +++ b/homeassistant/components/sentry/translations/pl.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Wprowad\u017a DSN Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/pt-BR.json b/homeassistant/components/sentry/translations/pt-BR.json index 21f1e3ef91a..c489de8ee6d 100644 --- a/homeassistant/components/sentry/translations/pt-BR.json +++ b/homeassistant/components/sentry/translations/pt-BR.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentinela DSN" }, "description": "Digite seu DSN Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/ru.json b/homeassistant/components/sentry/translations/ru.json index eb6cbd0310c..6a7192a5385 100644 --- a/homeassistant/components/sentry/translations/ru.json +++ b/homeassistant/components/sentry/translations/ru.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0412\u0430\u0448 DSN Sentry", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/tr.json b/homeassistant/components/sentry/translations/tr.json index fa3f39e9cde..6369f727d17 100644 --- a/homeassistant/components/sentry/translations/tr.json +++ b/homeassistant/components/sentry/translations/tr.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "Sentry DSN'nizi girin", "title": "Sentry" diff --git a/homeassistant/components/sentry/translations/zh-Hant.json b/homeassistant/components/sentry/translations/zh-Hant.json index 04fe4682a42..6d4615db892 100644 --- a/homeassistant/components/sentry/translations/zh-Hant.json +++ b/homeassistant/components/sentry/translations/zh-Hant.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "dsn": "DSN" + "dsn": "Sentry DSN" }, "description": "\u8f38\u5165 Sentry DSN", "title": "Sentry" diff --git a/homeassistant/components/senz/translations/bg.json b/homeassistant/components/senz/translations/bg.json new file mode 100644 index 00000000000..ddd6040e9cf --- /dev/null +++ b/homeassistant/components/senz/translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/ca.json b/homeassistant/components/senz/translations/ca.json new file mode 100644 index 00000000000..20b2ceceddd --- /dev/null +++ b/homeassistant/components/senz/translations/ca.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "authorize_url_timeout": "Temps d'espera esgotat durant la generaci\u00f3 de l'URL d'autoritzaci\u00f3.", + "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", + "no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})", + "oauth_error": "S'han rebut dades token inv\u00e0lides." + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, + "step": { + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/de.json b/homeassistant/components/senz/translations/de.json new file mode 100644 index 00000000000..16c6d8a883d --- /dev/null +++ b/homeassistant/components/senz/translations/de.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", + "missing_configuration": "Diese Komponente ist nicht konfiguriert. Bitte in der Anleitung nachlesen.", + "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", + "oauth_error": "Ung\u00fcltige Token-Daten empfangen." + }, + "create_entry": { + "default": "Anmeldung erfolgreich" + }, + "step": { + "pick_implementation": { + "title": "Art der Anmeldung ausw\u00e4hlen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/el.json b/homeassistant/components/senz/translations/el.json new file mode 100644 index 00000000000..cd34896da39 --- /dev/null +++ b/homeassistant/components/senz/translations/el.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "authorize_url_timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2.", + "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", + "oauth_error": "\u039b\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03bf\u03cd." + }, + "create_entry": { + "default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "pick_implementation": { + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/et.json b/homeassistant/components/senz/translations/et.json new file mode 100644 index 00000000000..1ea0537a1b7 --- /dev/null +++ b/homeassistant/components/senz/translations/et.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Kasutaja on juba seadistatud", + "already_in_progress": "Seadistamine on juba k\u00e4imas", + "authorize_url_timeout": "Kinnitus-URLi loomise ajal\u00f5pp", + "missing_configuration": "Komponent pole seadistatud. Palun loe dokumentatsiooni.", + "no_url_available": "URL pole saadaval. Selle t\u00f5rke kohta teabe saamiseks vaata [spikrijaotis]({docs_url})", + "oauth_error": "Saadi sobimatud loaandmed." + }, + "create_entry": { + "default": "Tuvastamine \u00f5nnestus" + }, + "step": { + "pick_implementation": { + "title": "Vali tuvastusmeetod" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/fr.json b/homeassistant/components/senz/translations/fr.json new file mode 100644 index 00000000000..3190eb10b6d --- /dev/null +++ b/homeassistant/components/senz/translations/fr.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification expir\u00e9.", + "missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.", + "no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})", + "oauth_error": "Des donn\u00e9es de jeton non valides ont \u00e9t\u00e9 re\u00e7ues." + }, + "create_entry": { + "default": "Authentification r\u00e9ussie" + }, + "step": { + "pick_implementation": { + "title": "S\u00e9lectionner une m\u00e9thode d'authentification" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/he.json b/homeassistant/components/senz/translations/he.json new file mode 100644 index 00000000000..fcac267c910 --- /dev/null +++ b/homeassistant/components/senz/translations/he.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "authorize_url_timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05db\u05ea\u05d5\u05d1\u05ea URL \u05dc\u05d0\u05d9\u05e9\u05d5\u05e8.", + "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", + "no_url_available": "\u05d0\u05d9\u05df \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4. \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05d9\u05d9\u05df \u05d1\u05e1\u05e2\u05d9\u05e3 \u05d4\u05e2\u05d6\u05e8\u05d4] ({docs_url})", + "oauth_error": "\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd." + }, + "create_entry": { + "default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4" + }, + "step": { + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/hu.json b/homeassistant/components/senz/translations/hu.json new file mode 100644 index 00000000000..a3b07f0d3ef --- /dev/null +++ b/homeassistant/components/senz/translations/hu.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", + "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", + "oauth_error": "\u00c9rv\u00e9nytelen token adatok \u00e9rkeztek." + }, + "create_entry": { + "default": "Sikeres hiteles\u00edt\u00e9s" + }, + "step": { + "pick_implementation": { + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/id.json b/homeassistant/components/senz/translations/id.json new file mode 100644 index 00000000000..a2fdf8837bd --- /dev/null +++ b/homeassistant/components/senz/translations/id.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "authorize_url_timeout": "Tenggang waktu pembuatan URL otorisasi habis.", + "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", + "no_url_available": "Tidak ada URL yang tersedia. Untuk informasi tentang kesalahan ini, [lihat bagian bantuan]({docs_url})", + "oauth_error": "Menerima respons token yang tidak valid." + }, + "create_entry": { + "default": "Berhasil diautentikasi" + }, + "step": { + "pick_implementation": { + "title": "Pilih Metode Autentikasi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/it.json b/homeassistant/components/senz/translations/it.json new file mode 100644 index 00000000000..c92ebb2a57c --- /dev/null +++ b/homeassistant/components/senz/translations/it.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", + "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", + "oauth_error": "Ricevuti dati token non validi." + }, + "create_entry": { + "default": "Autenticazione riuscita" + }, + "step": { + "pick_implementation": { + "title": "Scegli il metodo di autenticazione" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/ja.json b/homeassistant/components/senz/translations/ja.json new file mode 100644 index 00000000000..b6aa94ef30c --- /dev/null +++ b/homeassistant/components/senz/translations/ja.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "authorize_url_timeout": "\u8a8d\u8a3cURL\u306e\u751f\u6210\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", + "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", + "no_url_available": "\u4f7f\u7528\u53ef\u80fd\u306aURL\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30a8\u30e9\u30fc\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001[\u30d8\u30eb\u30d7\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044]({docs_url})", + "oauth_error": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u30c7\u30fc\u30bf\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002" + }, + "create_entry": { + "default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f" + }, + "step": { + "pick_implementation": { + "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/nl.json b/homeassistant/components/senz/translations/nl.json new file mode 100644 index 00000000000..0f50cb0f918 --- /dev/null +++ b/homeassistant/components/senz/translations/nl.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "already_in_progress": "De configuratiestroom is al aan de gang", + "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", + "oauth_error": "Ongeldige token data ontvangen." + }, + "create_entry": { + "default": "Succesvol geauthenticeerd" + }, + "step": { + "pick_implementation": { + "title": "Kies een authenticatie methode" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/no.json b/homeassistant/components/senz/translations/no.json new file mode 100644 index 00000000000..6c384bb2e15 --- /dev/null +++ b/homeassistant/components/senz/translations/no.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "authorize_url_timeout": "Tidsavbrudd ved oppretting av godkjenningsadresse", + "missing_configuration": "Komponenten er ikke konfigurert. Venligst f\u00f8lg dokumentasjonen.", + "no_url_available": "Ingen URL tilgengelig. For mer informasjon om denne feilen, [sjekk hjelp seksjonen]({docs_url})", + "oauth_error": "Mottatt ugyldige token data." + }, + "create_entry": { + "default": "Vellykket Autentisering" + }, + "step": { + "pick_implementation": { + "title": "Velg Autentiserings metode" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/pl.json b/homeassistant/components/senz/translations/pl.json new file mode 100644 index 00000000000..d58148cb8fa --- /dev/null +++ b/homeassistant/components/senz/translations/pl.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "authorize_url_timeout": "Przekroczono limit czasu generowania URL autoryzacji", + "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", + "no_url_available": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})", + "oauth_error": "Otrzymano nieprawid\u0142owe dane tokena." + }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, + "step": { + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/pt-BR.json b/homeassistant/components/senz/translations/pt-BR.json new file mode 100644 index 00000000000..7e3ff2f64a9 --- /dev/null +++ b/homeassistant/components/senz/translations/pt-BR.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "authorize_url_timeout": "Tempo limite gerando URL de autoriza\u00e7\u00e3o.", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "no_url_available": "N\u00e3o h\u00e1 URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre esse erro, [verifique a se\u00e7\u00e3o de ajuda]({docs_url})", + "oauth_error": "Dados de token recebidos inv\u00e1lidos." + }, + "create_entry": { + "default": "Autenticado com sucesso" + }, + "step": { + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/ru.json b/homeassistant/components/senz/translations/ru.json new file mode 100644 index 00000000000..2f572831b5b --- /dev/null +++ b/homeassistant/components/senz/translations/ru.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", + "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", + "no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435.", + "oauth_error": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u0430." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "step": { + "pick_implementation": { + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/tr.json b/homeassistant/components/senz/translations/tr.json new file mode 100644 index 00000000000..3f6fa6f27ba --- /dev/null +++ b/homeassistant/components/senz/translations/tr.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "authorize_url_timeout": "Yetkilendirme URL'si olu\u015ftururken zaman a\u015f\u0131m\u0131.", + "missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.", + "no_url_available": "Kullan\u0131labilir URL yok. Bu hata hakk\u0131nda bilgi i\u00e7in [yard\u0131m b\u00f6l\u00fcm\u00fcne bak\u0131n]({docs_url})", + "oauth_error": "Ge\u00e7ersiz anahtar verileri al\u0131nd\u0131." + }, + "create_entry": { + "default": "Ba\u015far\u0131yla do\u011fruland\u0131" + }, + "step": { + "pick_implementation": { + "title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/senz/translations/zh-Hant.json b/homeassistant/components/senz/translations/zh-Hant.json new file mode 100644 index 00000000000..3bf08cf34c7 --- /dev/null +++ b/homeassistant/components/senz/translations/zh-Hant.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "authorize_url_timeout": "\u7522\u751f\u8a8d\u8b49 URL \u6642\u903e\u6642\u3002", + "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", + "no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})", + "oauth_error": "\u6536\u5230\u7121\u6548\u7684\u6b0a\u6756\u8cc7\u6599\u3002" + }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, + "step": { + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index 21cdaabf9a2..9d32e18ae5c 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -9,11 +9,6 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f" - } - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index 3ab643b534f..4392a99ffea 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -12,24 +12,21 @@ "unknown": "Error inesperat" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Codi d'autoritzaci\u00f3" - }, - "description": "Introdueix el codi d'autoritzaci\u00f3 de l'URL de l'aplicaci\u00f3 web SimpliSafe:", - "title": "Acabament d'autoritzaci\u00f3" - }, "mfa": { - "description": "Consulta el correu i busca-hi un missatge amb un enlla\u00e7 de SimpliSafe. Despr\u00e9s de verificar l'enlla\u00e7, torna aqu\u00ed per completar la instal\u00b7laci\u00f3 de la integraci\u00f3.", "title": "Autenticaci\u00f3 multi-factor SimpliSafe" }, "reauth_confirm": { "data": { "password": "Contrasenya" }, - "description": "L'acc\u00e9s ha caducat o ha estat revocat. Introdueix la contrasenya per tornar a vincular el compte.", + "description": "Torna a introduir la contrasenya de {username}", "title": "Reautenticaci\u00f3 de la integraci\u00f3" }, + "sms_2fa": { + "data": { + "code": "Codi" + } + }, "user": { "data": { "auth_code": "Codi d'autoritzaci\u00f3", diff --git a/homeassistant/components/simplisafe/translations/cs.json b/homeassistant/components/simplisafe/translations/cs.json index bbb47121fbc..152c0282216 100644 --- a/homeassistant/components/simplisafe/translations/cs.json +++ b/homeassistant/components/simplisafe/translations/cs.json @@ -11,7 +11,6 @@ }, "step": { "mfa": { - "description": "Zkontrolujte, zda v\u00e1m do e-mail p\u0159i\u0161el odkaz od SimpliSafe. Po ov\u011b\u0159en\u00ed odkazu se vra\u0165te sem a dokon\u010dete instalaci integrace.", "title": "V\u00edcefaktorov\u00e9 ov\u011b\u0159ov\u00e1n\u00ed SimpliSafe" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index 1ee4802e77f..51083565770 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -6,38 +6,37 @@ "wrong_account": "Die angegebenen Benutzeranmeldeinformationen stimmen nicht mit diesem SimpliSafe-Konto \u00fcberein." }, "error": { + "2fa_timed_out": "Zeit\u00fcberschreitung beim Warten auf Zwei-Faktor-Authentifizierung", "identifier_exists": "Konto bereits registriert", "invalid_auth": "Ung\u00fcltige Authentifizierung", "still_awaiting_mfa": "Immernoch warten auf MFA-E-Mail-Klick", "unknown": "Unerwarteter Fehler" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Autorisierungscode" - }, - "description": "Gib den Autorisierungscode von der URL der SimpliSafe-Webanwendung ein:", - "title": "Autorisierung abschlie\u00dfen" - }, "mfa": { - "description": "Pr\u00fcfe deine E-Mail auf einen Link von SimpliSafe. Kehre nach der Verifizierung des Links hierher zur\u00fcck, um die Installation der Integration abzuschlie\u00dfen.", "title": "SimpliSafe Multi-Faktor-Authentifizierung" }, "reauth_confirm": { "data": { "password": "Passwort" }, - "description": "Dein Zugriffstoken ist abgelaufen oder wurde widerrufen. Gib dein Passwort ein, um dein Konto erneut zu verkn\u00fcpfen.", + "description": "Bitte gib das Passwort f\u00fcr {username} erneut ein.", "title": "Integration erneut authentifizieren" }, + "sms_2fa": { + "data": { + "code": "Code" + }, + "description": "Gib den Code f\u00fcr die Zwei-Faktor-Authentifizierung ein, den du per SMS erhalten hast." + }, "user": { "data": { "auth_code": "Autorisierungscode", "code": "Code (wird in der Benutzeroberfl\u00e4che von Home Assistant verwendet)", "password": "Passwort", - "username": "E-Mail" + "username": "Benutzername" }, - "description": "SimpliSafe authentifiziert sich bei Home Assistant \u00fcber die SimpliSafe Web-App. Aufgrund technischer Beschr\u00e4nkungen gibt es am Ende dieses Prozesses einen manuellen Schritt; bitte stelle sicher, dass du die [Dokumentation]({docs_url}) liest, bevor du beginnst.\n\n1. Klicke [hier]({url}), um die SimpliSafe-Webanwendung zu \u00f6ffnen und deine Anmeldedaten einzugeben.\n\n2. Wenn der Anmeldevorgang abgeschlossen ist, kehre hierher zur\u00fcck und gib unten stehenden Autorisierungscode ein.", + "description": "Gib deinen Benutzernamen und Passwort ein.", "title": "Gib deine Informationen ein" } } diff --git a/homeassistant/components/simplisafe/translations/el.json b/homeassistant/components/simplisafe/translations/el.json index d35c59bcc40..5253a1bfc52 100644 --- a/homeassistant/components/simplisafe/translations/el.json +++ b/homeassistant/components/simplisafe/translations/el.json @@ -2,25 +2,22 @@ "config": { "abort": { "already_configured": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 SimpliSafe \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7.", + "email_2fa_timed_out": "\u03a4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03ad\u03bb\u03b7\u03be\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03b2\u03b1\u03c3\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 email.", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "wrong_account": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03bf\u03c5\u03bd \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc SimpliSafe." }, "error": { + "2fa_timed_out": "\u0388\u03bb\u03b7\u03be\u03b5 \u03c4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd", "identifier_exists": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ae\u03b4\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "still_awaiting_mfa": "\u0391\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b1\u03ba\u03cc\u03bc\u03b7 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf email \u03c4\u03bf\u03c5 \u03a5\u03c0\u03bf\u03c5\u03c1\u03b3\u03b5\u03af\u03bf\u03c5 \u039f\u03b9\u03ba\u03bf\u03bd\u03bf\u03bc\u03b9\u03ba\u03ce\u03bd", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, + "progress": { + "email_2fa": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd\n\u03c0\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b5\u03c3\u03c4\u03ac\u03bb\u03b7 \u03bc\u03ad\u03c3\u03c9 email." + }, "step": { - "input_auth_code": { - "data": { - "auth_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2" - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 SimpliSafe web:", - "title": "\u039f\u03bb\u03bf\u03ba\u03bb\u03ae\u03c1\u03c9\u03c3\u03b7 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2" - }, "mfa": { - "description": "\u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf email \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03ad\u03bd\u03b1\u03bd \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd SimpliSafe. \u0391\u03c6\u03bf\u03cd \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2.", "title": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd SimpliSafe" }, "reauth_confirm": { @@ -30,6 +27,12 @@ "description": "\u0397 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03bb\u03ae\u03be\u03b5\u03b9 \u03ae \u03b1\u03bd\u03b1\u03ba\u03bb\u03b7\u03b8\u03b5\u03af. \u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2.", "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" }, + "sms_2fa": { + "data": { + "code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2" + }, + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b5\u03c3\u03c4\u03ac\u03bb\u03b7 \u03bc\u03ad\u03c3\u03c9 SMS." + }, "user": { "data": { "auth_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index 2f8a4366df1..a3bf0049681 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -3,16 +3,23 @@ "abort": { "already_configured": "This SimpliSafe account is already in use.", "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", - "reauth_successful": "Re-authentication was successful" + "reauth_successful": "Re-authentication was successful", + "wrong_account": "The user credentials provided do not match this SimpliSafe account." }, "error": { + "2fa_timed_out": "Timed out while waiting for two-factor authentication", + "identifier_exists": "Account already registered", "invalid_auth": "Invalid authentication", + "still_awaiting_mfa": "Still awaiting MFA email click", "unknown": "Unexpected error" }, "progress": { "email_2fa": "Input the two-factor authentication code\nsent to you via email." }, "step": { + "mfa": { + "title": "SimpliSafe Multi-Factor Authentication" + }, "reauth_confirm": { "data": { "password": "Password" @@ -28,10 +35,13 @@ }, "user": { "data": { + "auth_code": "Authorization Code", + "code": "Code (used in Home Assistant UI)", "password": "Password", "username": "Username" }, - "description": "Input your username and password." + "description": "Input your username and password.", + "title": "Fill in your information." } } }, diff --git a/homeassistant/components/simplisafe/translations/es.json b/homeassistant/components/simplisafe/translations/es.json index 690e01206d3..d4b066890f1 100644 --- a/homeassistant/components/simplisafe/translations/es.json +++ b/homeassistant/components/simplisafe/translations/es.json @@ -12,15 +12,7 @@ "unknown": "Error inesperado" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "C\u00f3digo de Autorizaci\u00f3n" - }, - "description": "Introduzca el c\u00f3digo de autorizaci\u00f3n desde la URL de la aplicaci\u00f3n web de SimpliSafe:", - "title": "Terminar Autorizaci\u00f3n" - }, "mfa": { - "description": "Comprueba tu correo electr\u00f3nico para obtener un enlace desde SimpliSafe. Despu\u00e9s de verificar el enlace, vulve aqu\u00ed para completar la instalaci\u00f3n de la integraci\u00f3n.", "title": "Autenticaci\u00f3n Multi-Factor SimpliSafe" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index 97e59415123..83c441de9f9 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -6,38 +6,37 @@ "wrong_account": "Esitatud kasutaja mandaadid ei \u00fchti selle SimpliSafe kontoga." }, "error": { + "2fa_timed_out": "Kahefaktoriline autentimine aegus", "identifier_exists": "Konto on juba registreeritud", "invalid_auth": "Tuvastamise viga", "still_awaiting_mfa": "Ootan endiselt MFA e-posti klikki", "unknown": "Tundmatu viga" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Tuvastuskood" - }, - "description": "Sisesta tuvastuskood SimpliSafe veebirakenduse URL-ist:", - "title": "L\u00f5peta tuvastamine" - }, "mfa": { - "description": "Kontrolli oma e-posti: link SimpliSafe-lt. P\u00e4rast lingi kontrollimist naase siia, et viia l\u00f5pule sidumise installimine.", "title": "SimpliSafe mitmeastmeline autentimine" }, "reauth_confirm": { "data": { "password": "Salas\u00f5na" }, - "description": "Juurdep\u00e4\u00e4suluba on aegunud v\u00f5i on see t\u00fchistatud. Konto taassidumiseks sisesta salas\u00f5na.", + "description": "Taassisesta salas\u00f5na kasutajanimele {username}.", "title": "Taastuvasta SimpliSafe'i konto" }, + "sms_2fa": { + "data": { + "code": "Kood" + }, + "description": "Sisesta SMS-iga saadetud kahefaktoriline autentimiskood." + }, "user": { "data": { "auth_code": "Tuvastuskood", "code": "Kood (kasutatakse Home Assistant'i kasutajaliideses)", "password": "Salas\u00f5na", - "username": "E-post" + "username": "Kasutajanimi" }, - "description": "SimpliSafe autendib Home Assistantiga SimpliSafe'i veebirakenduse kaudu. Tehniliste piirangute t\u00f5ttu on selle protsessi l\u00f5pus k\u00e4sitsi samm; enne alustamist loe kindlasti [dokumentatsioon]( {docs_url} \n\n 1. SimpliSafe'i veebirakenduse avamiseks ja oma mandaatide sisestamiseks kl\u00f5psa [siin]( {url} \n\n 2. Kui sisselogimisprotsess on l\u00f5ppenud, naase siia ja sisesta alltoodud tuvastuskood.", + "description": "Sisesta kasutajatunnus ja salas\u00f5na", "title": "Sisesta oma teave." } } diff --git a/homeassistant/components/simplisafe/translations/fr.json b/homeassistant/components/simplisafe/translations/fr.json index d50ac11f851..b3456fc35ef 100644 --- a/homeassistant/components/simplisafe/translations/fr.json +++ b/homeassistant/components/simplisafe/translations/fr.json @@ -2,42 +2,45 @@ "config": { "abort": { "already_configured": "Ce compte SimpliSafe est d\u00e9j\u00e0 utilis\u00e9.", + "email_2fa_timed_out": "D\u00e9lai d'attente de l'authentification \u00e0 deux facteurs par courriel expir\u00e9.", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", "wrong_account": "Les informations d'identification d'utilisateur fournies ne correspondent pas \u00e0 ce compte SimpliSafe." }, "error": { + "2fa_timed_out": "D\u00e9lai d'attente de l'authentification \u00e0 deux facteurs expir\u00e9", "identifier_exists": "Compte d\u00e9j\u00e0 enregistr\u00e9", "invalid_auth": "Authentification non valide", "still_awaiting_mfa": "En attente de clic sur le message \u00e9lectronique d'authentification multi facteur", "unknown": "Erreur inattendue" }, + "progress": { + "email_2fa": "Saisissez le code d'authentification \u00e0 deux facteurs qui vous a \u00e9t\u00e9 envoy\u00e9 par courriel." + }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Code d'autorisation" - }, - "description": "Saisissez le code d'autorisation \u00e0 partir de l'URL de l'application Web SimpliSafe\u00a0:", - "title": "Terminer l'autorisation" - }, "mfa": { - "description": "V\u00e9rifiez votre messagerie pour un lien de SimpliSafe. Apr\u00e8s avoir v\u00e9rifi\u00e9 le lien, revenez ici pour terminer l'installation de l'int\u00e9gration.", "title": "Authentification multi facteur SimpliSafe" }, "reauth_confirm": { "data": { "password": "Mot de passe" }, - "description": "Votre jeton d'acc\u00e8s a expir\u00e9 ou a \u00e9t\u00e9 r\u00e9voqu\u00e9. Entrez votre mot de passe pour r\u00e9 associer votre compte.", + "description": "Veuillez de nouveau saisir le mot de passe pour {username}.", "title": "R\u00e9-authentifier l'int\u00e9gration" }, + "sms_2fa": { + "data": { + "code": "Code" + }, + "description": "Saisissez le code d'authentification \u00e0 deux facteurs qui vous a \u00e9t\u00e9 envoy\u00e9 par SMS." + }, "user": { "data": { "auth_code": "Code d'autorisation", "code": "Code (utilis\u00e9 dans l'interface Home Assistant)", "password": "Mot de passe", - "username": "Courriel" + "username": "Nom d'utilisateur" }, - "description": "SimpliSafe s'authentifie avec Home Assistant via l'application Web SimpliSafe. En raison de limitations techniques, il y a une \u00e9tape manuelle \u00e0 la fin de ce processus ; veuillez vous assurer de lire la [documentation]( {docs_url} ) avant de commencer. \n\n 1. Cliquez sur [ici]( {url} ) pour ouvrir l'application Web SimpliSafe et saisissez vos informations d'identification. \n\n 2. Une fois le processus de connexion termin\u00e9, revenez ici et saisissez le code d'autorisation ci-dessous.", + "description": "Saisissez votre nom d'utilisateur et votre mot de passe.", "title": "Veuillez saisir vos informations" } } diff --git a/homeassistant/components/simplisafe/translations/hu.json b/homeassistant/components/simplisafe/translations/hu.json index 69881266c4f..e1b8769f4b6 100644 --- a/homeassistant/components/simplisafe/translations/hu.json +++ b/homeassistant/components/simplisafe/translations/hu.json @@ -6,38 +6,37 @@ "wrong_account": "A megadott felhaszn\u00e1l\u00f3i hiteles\u00edt\u0151 adatok nem j\u00f3k ehhez a SimpliSafe fi\u00f3khoz." }, "error": { + "2fa_timed_out": "A k\u00e9tfaktoros hiteles\u00edt\u00e9sre val\u00f3 v\u00e1rakoz\u00e1s ideje lej\u00e1rt", "identifier_exists": "Fi\u00f3k m\u00e1r regisztr\u00e1lva van", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "still_awaiting_mfa": "M\u00e9g v\u00e1r az MFA e-mail kattint\u00e1sra", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Enged\u00e9lyez\u00e9si k\u00f3d" - }, - "description": "Adja meg a SimpliSafe webes alkalmaz\u00e1s URL-c\u00edm\u00e9n tal\u00e1lhat\u00f3 enged\u00e9lyez\u00e9si k\u00f3dot:", - "title": "Enged\u00e9lyez\u00e9s befejez\u00e9se" - }, "mfa": { - "description": "Ellen\u0151rizze e-mailj\u00e9ben a SimpliSafe linkj\u00e9t. A link ellen\u0151rz\u00e9se ut\u00e1n t\u00e9rjen vissza ide, \u00e9s fejezze be az integr\u00e1ci\u00f3 telep\u00edt\u00e9s\u00e9t.", "title": "SimpliSafe t\u00f6bbt\u00e9nyez\u0151s hiteles\u00edt\u00e9s" }, "reauth_confirm": { "data": { "password": "Jelsz\u00f3" }, - "description": "Hozz\u00e1f\u00e9r\u00e9se lej\u00e1rt vagy visszavont\u00e1k. Adja meg jelszav\u00e1t a fi\u00f3k \u00fajb\u00f3li \u00f6sszekapcsol\u00e1s\u00e1hoz.", + "description": "K\u00e9rem, adja meg ism\u00e9t {username} jelszav\u00e1t.", "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" }, + "sms_2fa": { + "data": { + "code": "K\u00f3d" + }, + "description": "\u00cdrja be a k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3dot, amelyet SMS-ben k\u00fcldtek \u00d6nnek." + }, "user": { "data": { "auth_code": "Enged\u00e9lyez\u00e9si k\u00f3d", "code": "K\u00f3d (a Home Assistant felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n haszn\u00e1latos)", "password": "Jelsz\u00f3", - "username": "E-mail" + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "A SimpliSafe rendszere webalkalmaz\u00e1son kereszt\u00fcl hiteles\u00edt\u00e9si mag\u00e1t. A technikai korl\u00e1toz\u00e1sok miatt a folyamat v\u00e9g\u00e9n van egy k\u00e9zi l\u00e9p\u00e9s; k\u00e9rj\u00fck, indul\u00e1s el\u0151tt olvassa el a [dokument\u00e1ci\u00f3t]({docs_url}).\n\n1. Ha k\u00e9szen \u00e1ll, kattintson [ide]({url}) a SimpliSafe webalkalmaz\u00e1s megnyit\u00e1s\u00e1hoz \u00e9s a hiteles\u00edt\u0151 adatok bevitel\u00e9hez. \n\n2. Amikor a folyamat befejez\u0151d\u00f6tt, t\u00e9rjen vissza ide, \u00e9s \u00edrja be a k\u00f3dot.", + "description": "Adja meg felhaszn\u00e1l\u00f3nev\u00e9t \u00e9s jelszav\u00e1t.", "title": "T\u00f6ltse ki az adatait" } } diff --git a/homeassistant/components/simplisafe/translations/id.json b/homeassistant/components/simplisafe/translations/id.json index 743af691714..ad0ad01be63 100644 --- a/homeassistant/components/simplisafe/translations/id.json +++ b/homeassistant/components/simplisafe/translations/id.json @@ -6,38 +6,37 @@ "wrong_account": "Kredensial pengguna yang diberikan tidak cocok dengan akun SimpliSafe ini." }, "error": { + "2fa_timed_out": "Tenggang waktu habis saat menunggu autentikasi dua faktor", "identifier_exists": "Akun sudah terdaftar", "invalid_auth": "Autentikasi tidak valid", "still_awaiting_mfa": "Masih menunggu pengeklikan dari email MFA", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Kode Otorisasi" - }, - "description": "Masukkan kode otorisasi dari URL aplikasi web SimpliSafe:", - "title": "Selesaikan Otorisasi" - }, "mfa": { - "description": "Periksa email Anda untuk mendapatkan tautan dari SimpliSafe. Setelah memverifikasi tautan, kembali ke sini untuk menyelesaikan instalasi integrasi.", "title": "Autentikasi Multi-Faktor SimpliSafe" }, "reauth_confirm": { "data": { "password": "Kata Sandi" }, - "description": "Akses Anda telah kedaluwarsa atau dicabut. Masukkan kata sandi Anda untuk menautkan kembali akun Anda.", + "description": "Masukkan kembali kata sandi untuk {username}.", "title": "Autentikasi Ulang Integrasi" }, + "sms_2fa": { + "data": { + "code": "Kode" + }, + "description": "Masukkan kode autentikasi dua faktor yang dikirimkan kepada Anda melalui SMS." + }, "user": { "data": { "auth_code": "Kode Otorisasi", "code": "Kode (digunakan di antarmuka Home Assistant)", "password": "Kata Sandi", - "username": "Email" + "username": "Nama Pengguna" }, - "description": "SimpliSafe mengautentikasi dengan Home Assistant melalui aplikasi web. Karena keterbatasan teknis, ada langkah manual di akhir proses ini; pastikan Anda membaca [dokumentasi] ({docs_url}) sebelum memulai.\n\n1. Klik [di sini]({url}) untuk membuka aplikasi web SimpliSafe dan masukkan kredensial Anda. \n\n2. Setelah proses masuk selesai, kembali ke sini dan klik masukkan kode otorisasi di bawah ini.", + "description": "Masukkan nama pengguna dan kata sandi Anda.", "title": "Isi informasi Anda." } } diff --git a/homeassistant/components/simplisafe/translations/it.json b/homeassistant/components/simplisafe/translations/it.json index 0221536698c..ad9e491ab9a 100644 --- a/homeassistant/components/simplisafe/translations/it.json +++ b/homeassistant/components/simplisafe/translations/it.json @@ -6,38 +6,37 @@ "wrong_account": "Le credenziali utente fornite non corrispondono a questo account SimpliSafe." }, "error": { + "2fa_timed_out": "Timeout durante l'attesa dell'autenticazione a due fattori", "identifier_exists": "Account gi\u00e0 registrato", "invalid_auth": "Autenticazione non valida", "still_awaiting_mfa": "Ancora in attesa del clic sull'email MFA", "unknown": "Errore imprevisto" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Codice di autorizzazione" - }, - "description": "Immettere il codice di autorizzazione dall'URL dell'app web SimpliSafe:", - "title": "Completa l'autorizzazione" - }, "mfa": { - "description": "Controlla la tua email per trovare un collegamento da SimpliSafe. Dopo aver verificato il collegamento, torna qui per completare l'installazione dell'integrazione.", "title": "Autenticazione a pi\u00f9 fattori (MFA) SimpliSafe " }, "reauth_confirm": { "data": { "password": "Password" }, - "description": "Il tuo accesso \u00e8 scaduto o revocato. Inserisci la password per ricollegare il tuo account.", + "description": "Digita nuovamente la password per {username}.", "title": "Autentica nuovamente l'integrazione" }, + "sms_2fa": { + "data": { + "code": "Codice" + }, + "description": "Digita il codice di autenticazione a due fattori che ti \u00e8 stato inviato tramite SMS." + }, "user": { "data": { "auth_code": "Codice di autorizzazione", "code": "Codice (utilizzato nell'Interfaccia Utente di Home Assistant)", "password": "Password", - "username": "Email" + "username": "Nome utente" }, - "description": "SimpliSafe si autentica con Home Assistant tramite l'app Web SimpliSafe. A causa di limitazioni tecniche, alla fine di questo processo \u00e8 previsto un passaggio manuale; assicurati di leggere la [documentazione]({docs_url}) prima di iniziare. \n\n 1. Fai clic su [qui]({url}) per aprire l'app Web SimpliSafe e inserire le tue credenziali. \n\n 2. Quando il processo di accesso \u00e8 completo, torna qui e inserisci il codice di autorizzazione di seguito.", + "description": "Digita il tuo nome utente e password.", "title": "Inserisci le tue informazioni." } } diff --git a/homeassistant/components/simplisafe/translations/ja.json b/homeassistant/components/simplisafe/translations/ja.json index 925ae801a0d..74b0b20c65e 100644 --- a/homeassistant/components/simplisafe/translations/ja.json +++ b/homeassistant/components/simplisafe/translations/ja.json @@ -12,15 +12,7 @@ "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9" - }, - "description": "SimpliSafe Web\u30a2\u30d7\u30ea\u306eURL\u304b\u3089\u8a8d\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b:", - "title": "\u627f\u8a8d\u7d42\u4e86" - }, "mfa": { - "description": "SimpliSafe\u304b\u3089\u306e\u30ea\u30f3\u30af\u306b\u3064\u3044\u3066\u306f\u30e1\u30fc\u30eb\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30ea\u30f3\u30af\u3092\u78ba\u8a8d\u3057\u305f\u3089\u3001\u3053\u3053\u306b\u623b\u3063\u3066\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u5b8c\u4e86\u3057\u307e\u3059\u3002", "title": "SimpliSafe\u591a\u8981\u7d20\u8a8d\u8a3c" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/ko.json b/homeassistant/components/simplisafe/translations/ko.json index 194fa6cecf4..5c98e8abaa6 100644 --- a/homeassistant/components/simplisafe/translations/ko.json +++ b/homeassistant/components/simplisafe/translations/ko.json @@ -12,7 +12,6 @@ }, "step": { "mfa": { - "description": "\uc774\uba54\uc77c\uc5d0\uc11c SimpliSafe\uc758 \ub9c1\ud06c\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694. \ub9c1\ud06c\ub97c \ud655\uc778\ud55c \ud6c4 \uc5ec\uae30\ub85c \ub3cc\uc544\uc640 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\uc758 \uc124\uce58\ub97c \uc644\ub8cc\ud574\uc8fc\uc138\uc694.", "title": "SimpliSafe \ub2e4\ub2e8\uacc4 \uc778\uc99d" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/lb.json b/homeassistant/components/simplisafe/translations/lb.json index 225c4457a10..080d8afd394 100644 --- a/homeassistant/components/simplisafe/translations/lb.json +++ b/homeassistant/components/simplisafe/translations/lb.json @@ -12,7 +12,6 @@ }, "step": { "mfa": { - "description": "Kuck den E-Mailen fir ee Link vun SimpliSafe. Nodeem de Link opgeruff gouf, komm heihinner zer\u00e9ck fir d'Installatioun vun der Integratioun ofzeschl\u00e9issen.", "title": "SimpliSafe Multi-Faktor Authentifikatioun" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index 3380746db25..33bc10ac85d 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -6,38 +6,37 @@ "wrong_account": "De opgegeven gebruikersgegevens komen niet overeen met deze SimpliSafe-account." }, "error": { + "2fa_timed_out": "Timed out tijdens het wachten op twee-factor authenticatie", "identifier_exists": "Account bestaat al", "invalid_auth": "Ongeldige authenticatie", "still_awaiting_mfa": "Wacht nog steeds op MFA-e-mailklik", "unknown": "Onverwachte fout" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Autorisatie Code" - }, - "description": "Voer de autorisatiecode in van de SimpliSafe web app URL:", - "title": "Autorisatie voltooien" - }, "mfa": { - "description": "Controleer uw e-mail voor een link van SimpliSafe. Nadat u de link hebt geverifieerd, kom hier terug om de installatie van de integratie te voltooien.", "title": "SimpliSafe Multi-Factor Authenticatie" }, "reauth_confirm": { "data": { "password": "Wachtwoord" }, - "description": "Uw toegangstoken is verlopen of ingetrokken. Voer uw wachtwoord in om uw account opnieuw te koppelen.", + "description": "Voer het wachtwoord voor {username} opnieuw in.", "title": "Verifieer de integratie opnieuw" }, + "sms_2fa": { + "data": { + "code": "Code" + }, + "description": "Voer de twee-factor authenticatiecode in die u heeft ontvangen via SMS" + }, "user": { "data": { "auth_code": "Autorisatie Code", "code": "Code (gebruikt in Home Assistant)", "password": "Wachtwoord", - "username": "E-mail" + "username": "Gebruikersnaam" }, - "description": "SimpliSafe verifieert met Home Assistant via de SimpliSafe web app. Vanwege technische beperkingen is er een handmatige stap aan het einde van dit proces; zorg ervoor dat u de [documentatie]({docs_url}) leest voordat u begint.\n\n1. Klik op [hier]({url}) om de SimpliSafe web app te openen en voer uw referenties in.\n\n2. Wanneer het aanmeldingsproces is voltooid, gaat u hier terug en voert u de onderstaande autorisatiecode in.", + "description": "Voer uw gebruikersnaam en wachtwoord in.", "title": "Vul uw gegevens in" } } diff --git a/homeassistant/components/simplisafe/translations/no.json b/homeassistant/components/simplisafe/translations/no.json index 2ac57586f0b..6527f2e4ae2 100644 --- a/homeassistant/components/simplisafe/translations/no.json +++ b/homeassistant/components/simplisafe/translations/no.json @@ -6,38 +6,37 @@ "wrong_account": "Brukerlegitimasjonen som er oppgitt, samsvarer ikke med denne SimpliSafe -kontoen." }, "error": { + "2fa_timed_out": "Tidsavbrutt mens du ventet p\u00e5 tofaktorautentisering", "identifier_exists": "Konto er allerede registrert", "invalid_auth": "Ugyldig godkjenning", "still_awaiting_mfa": "Forventer fortsatt MFA-e-postklikk", "unknown": "Uventet feil" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Autorisasjonskode" - }, - "description": "Skriv inn autorisasjonskoden fra SimpliSafe -webappens URL:", - "title": "Fullf\u00f8r autorisasjon" - }, "mfa": { - "description": "Sjekk e-posten din for en lenke fra SimpliSafe. Etter \u00e5 ha bekreftet lenken, g\u00e5 tilbake hit for \u00e5 fullf\u00f8re installasjonen av integrasjonen.", "title": "SimpliSafe flertrinnsbekreftelse" }, "reauth_confirm": { "data": { "password": "Passord" }, - "description": "Tilgangen din har utl\u00f8pt eller blitt tilbakekalt. Skriv inn passordet ditt for \u00e5 koble kontoen din til p\u00e5 nytt.", + "description": "Vennligst skriv inn passordet for {username} p\u00e5 nytt.", "title": "Godkjenne integrering p\u00e5 nytt" }, + "sms_2fa": { + "data": { + "code": "Kode" + }, + "description": "Skriv inn tofaktorautentiseringskoden sendt til deg via SMS." + }, "user": { "data": { "auth_code": "Autorisasjonskode", "code": "Kode (brukt i Home Assistant brukergrensesnittet)", "password": "Passord", - "username": "E-post" + "username": "Brukernavn" }, - "description": "SimpliSafe autentiserer med Home Assistant via SimpliSafe-nettappen. P\u00e5 grunn av tekniske begrensninger er det et manuelt trinn p\u00e5 slutten av denne prosessen; s\u00f8rg for at du leser [dokumentasjonen]( {docs_url} ) f\u00f8r du starter. \n\n 1. Klikk [her]( {url} ) for \u00e5 \u00e5pne SimpliSafe-nettappen og angi legitimasjonen din. \n\n 2. N\u00e5r p\u00e5loggingsprosessen er fullf\u00f8rt, g\u00e5 tilbake hit og skriv inn autorisasjonskoden nedenfor.", + "description": "Skriv inn brukernavn og passord.", "title": "Fyll ut informasjonen din." } } diff --git a/homeassistant/components/simplisafe/translations/pl.json b/homeassistant/components/simplisafe/translations/pl.json index e95f99c1d2b..f4c8f5096cf 100644 --- a/homeassistant/components/simplisafe/translations/pl.json +++ b/homeassistant/components/simplisafe/translations/pl.json @@ -2,42 +2,45 @@ "config": { "abort": { "already_configured": "To konto SimpliSafe jest ju\u017c w u\u017cyciu", + "email_2fa_timed_out": "Przekroczono limit czasu oczekiwania na e-mailowe uwierzytelnianie dwusk\u0142adnikowe.", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", "wrong_account": "Podane dane uwierzytelniaj\u0105ce u\u017cytkownika nie pasuj\u0105 do tego konta SimpliSafe." }, "error": { + "2fa_timed_out": "Przekroczono limit czasu oczekiwania na uwierzytelnianie dwusk\u0142adnikowe", "identifier_exists": "Konto jest ju\u017c zarejestrowane", "invalid_auth": "Niepoprawne uwierzytelnienie", "still_awaiting_mfa": "Wci\u0105\u017c nie potwierdzono linka w e-mailu od SimpliSafe", "unknown": "Nieoczekiwany b\u0142\u0105d" }, + "progress": { + "email_2fa": "Wprowad\u017a kod uwierzytelniania dwusk\u0142adnikowego\nwys\u0142any do Ciebie e-mailem." + }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Kod autoryzacji" - }, - "description": "Wprowad\u017a kod autoryzacyjny z adresu URL aplikacji internetowej SimpliSafe:", - "title": "Zako\u0144cz autoryzacj\u0119" - }, "mfa": { - "description": "Sprawd\u017a e-mail od SimpliSafe. Po zweryfikowaniu linka, wr\u00f3\u0107 tutaj, aby doko\u0144czy\u0107 instalacj\u0119 integracji.", "title": "Uwierzytelnianie wielosk\u0142adnikowe SimpliSafe" }, "reauth_confirm": { "data": { "password": "Has\u0142o" }, - "description": "Tw\u00f3j dost\u0119p wygas\u0142 lub zosta\u0142 uniewa\u017cniony. Wprowad\u017a has\u0142o, aby ponownie po\u0142\u0105czy\u0107 swoje konto.", + "description": "Wprowad\u017a ponownie has\u0142o dla u\u017cytkownika {username}.", "title": "Ponownie uwierzytelnij integracj\u0119" }, + "sms_2fa": { + "data": { + "code": "Kod" + }, + "description": "Wprowad\u017a kod uwierzytelniania dwusk\u0142adnikowego, kt\u00f3ry zosta\u0142 wys\u0142any do Ciebie SMS-em." + }, "user": { "data": { "auth_code": "Kod autoryzacji", "code": "Kod (u\u017cywany w interfejsie Home Assistant)", "password": "Has\u0142o", - "username": "Adres e-mail" + "username": "Nazwa u\u017cytkownika" }, - "description": "SimpliSafe uwierzytelnia si\u0119 z Home Assistantem za po\u015brednictwem aplikacji internetowej SimpliSafe. Ze wzgl\u0119du na ograniczenia techniczne na ko\u0144cu tego procesu znajduje si\u0119 r\u0119czny krok; upewnij si\u0119, \u017ce przed rozpocz\u0119ciem przeczyta\u0142e\u015b [dokumentacj\u0119]( {docs_url} ).\n\n1. Kliknij [tutaj]( {url} ), aby otworzy\u0107 aplikacj\u0119 internetow\u0105 SimpliSafe i wprowadzi\u0107 swoje dane uwierzytelniaj\u0105ce. \n\n2. Po zako\u0144czeniu procesu logowania wr\u00f3\u0107 tutaj i wprowad\u017a poni\u017cszy kod autoryzacyjny.", + "description": "Wprowad\u017a swoj\u0105 nazw\u0119 u\u017cytkownika i has\u0142o.", "title": "Wprowad\u017a dane" } } diff --git a/homeassistant/components/simplisafe/translations/pt-BR.json b/homeassistant/components/simplisafe/translations/pt-BR.json index d1473074cca..596f1f441e6 100644 --- a/homeassistant/components/simplisafe/translations/pt-BR.json +++ b/homeassistant/components/simplisafe/translations/pt-BR.json @@ -2,42 +2,45 @@ "config": { "abort": { "already_configured": "A conta j\u00e1 foi configurada", + "email_2fa_timed_out": "Expirou enquanto aguardava a autentica\u00e7\u00e3o de dois fatores enviada por e-mail.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", "wrong_account": "As credenciais de usu\u00e1rio fornecidas n\u00e3o correspondem a esta conta SimpliSafe." }, "error": { + "2fa_timed_out": "Expirou enquanto aguardava a autentica\u00e7\u00e3o de dois fatores", "identifier_exists": "Conta j\u00e1 cadastrada", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "still_awaiting_mfa": "Ainda aguardando clique no e-mail da MFA", "unknown": "Erro inesperado" }, + "progress": { + "email_2fa": "Insira o c\u00f3digo de autentica\u00e7\u00e3o de dois fatores\n enviado para voc\u00ea por e-mail." + }, "step": { - "input_auth_code": { - "data": { - "auth_code": "C\u00f3digo de Autoriza\u00e7\u00e3o" - }, - "description": "Insira o c\u00f3digo de autoriza\u00e7\u00e3o do URL do aplicativo Web SimpliSafe:", - "title": "Concluir autoriza\u00e7\u00e3o" - }, "mfa": { - "description": "Verifique seu e-mail para obter um link do SimpliSafe. Ap\u00f3s verificar o link, volte aqui para concluir a instala\u00e7\u00e3o da integra\u00e7\u00e3o.", "title": "Autentica\u00e7\u00e3o SimpliSafe multifator" }, "reauth_confirm": { "data": { "password": "Senha" }, - "description": "Seu acesso expirou ou foi revogado. Digite sua senha para vincular novamente sua conta.", + "description": "Por favor, digite novamente a senha para {username} .", "title": "Reautenticar Integra\u00e7\u00e3o" }, + "sms_2fa": { + "data": { + "code": "C\u00f3digo" + }, + "description": "Insira o c\u00f3digo de autentica\u00e7\u00e3o de dois fatores enviado a voc\u00ea via SMS." + }, "user": { "data": { "auth_code": "C\u00f3digo de autoriza\u00e7\u00e3o", "code": "C\u00f3digo (usado na IU do Home Assistant)", "password": "Senha", - "username": "Email" + "username": "Usu\u00e1rio" }, - "description": "O SimpliSafe autentica com o Home Assistant por meio do aplicativo da Web SimpliSafe. Por limita\u00e7\u00f5es t\u00e9cnicas, existe uma etapa manual ao final deste processo; certifique-se de ler a [documenta\u00e7\u00e3o]( {docs_url} ) antes de come\u00e7ar. \n\n 1. Clique [aqui]( {url} ) para abrir o aplicativo da web SimpliSafe e insira suas credenciais. \n\n 2. Quando o processo de login estiver conclu\u00eddo, retorne aqui e insira o c\u00f3digo de autoriza\u00e7\u00e3o abaixo.", + "description": "Insira seu nome de usu\u00e1rio e senha.", "title": "Preencha suas informa\u00e7\u00f5es." } } diff --git a/homeassistant/components/simplisafe/translations/ru.json b/homeassistant/components/simplisafe/translations/ru.json index 306a0180d88..66bd439a0c1 100644 --- a/homeassistant/components/simplisafe/translations/ru.json +++ b/homeassistant/components/simplisafe/translations/ru.json @@ -6,38 +6,37 @@ "wrong_account": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u044d\u0442\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 SimpliSafe." }, "error": { + "2fa_timed_out": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "identifier_exists": "\u0423\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "still_awaiting_mfa": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { - "input_auth_code": { - "data": { - "auth_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438" - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f SimpliSafe:", - "title": "\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438" - }, "mfa": { - "description": "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0441\u0432\u043e\u044e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443 \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043e\u0442 SimpliSafe. \u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043e\u0442\u043a\u0440\u043e\u0435\u0442\u0435 \u0441\u0441\u044b\u043b\u043a\u0443, \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", "title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f SimpliSafe" }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" }, - "description": "\u0421\u0440\u043e\u043a \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438\u0441\u0442\u0435\u043a \u0438\u043b\u0438 \u0431\u044b\u043b \u0430\u043d\u043d\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c.", + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f {username}.", "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" }, + "sms_2fa": { + "data": { + "code": "\u041a\u043e\u0434" + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0412\u0430\u043c \u0432 SMS." + }, "user": { "data": { "auth_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438", "code": "\u041a\u043e\u0434 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Home Assistant)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b" + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 SimpliSafe. \u0418\u0437-\u0437\u0430 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439, \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \n\n1. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e [\u0441\u0441\u044b\u043b\u043a\u0435]({url}), \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 SimpliSafe \u0438 \u0432\u0432\u0435\u0441\u0442\u0438 \u0441\u0432\u043e\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \n\n2. \u041a\u043e\u0433\u0434\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u0445\u043e\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d, \u0432\u0435\u0440\u043d\u0438\u0442\u0435\u0441\u044c \u0441\u044e\u0434\u0430 \u0438 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.", "title": "SimpliSafe" } } diff --git a/homeassistant/components/simplisafe/translations/sl.json b/homeassistant/components/simplisafe/translations/sl.json index bbbe8034d06..6f326bc5b68 100644 --- a/homeassistant/components/simplisafe/translations/sl.json +++ b/homeassistant/components/simplisafe/translations/sl.json @@ -8,13 +8,6 @@ "identifier_exists": "Ra\u010dun je \u017ee registriran" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Avtorizacijska koda" - }, - "description": "Vnesite avtorizacijsko kodo iz URL-ja spletne aplikacije SimpliSafe:", - "title": "Dokon\u010daj avtorizacijo" - }, "user": { "data": { "code": "Koda (uporablja se v uporabni\u0161kem vmesniku Home Assistant)", diff --git a/homeassistant/components/simplisafe/translations/tr.json b/homeassistant/components/simplisafe/translations/tr.json index 721502ca0b1..255207833c7 100644 --- a/homeassistant/components/simplisafe/translations/tr.json +++ b/homeassistant/components/simplisafe/translations/tr.json @@ -12,15 +12,7 @@ "unknown": "Beklenmeyen hata" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "Yetkilendirme Kodu" - }, - "description": "SimpliSafe web uygulamas\u0131 URL'sinden yetkilendirme kodunu girin:", - "title": "Yetkilendirmeyi Bitir" - }, "mfa": { - "description": "SimpliSafe'den bir ba\u011flant\u0131 i\u00e7in e-postan\u0131z\u0131 kontrol edin. Ba\u011flant\u0131y\u0131 do\u011frulad\u0131ktan sonra, entegrasyonun kurulumunu tamamlamak i\u00e7in buraya geri d\u00f6n\u00fcn.", "title": "SimpliSafe \u00c7ok Fakt\u00f6rl\u00fc Kimlik Do\u011frulama" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/uk.json b/homeassistant/components/simplisafe/translations/uk.json index 0a51f129e5f..8ca29743c5b 100644 --- a/homeassistant/components/simplisafe/translations/uk.json +++ b/homeassistant/components/simplisafe/translations/uk.json @@ -12,7 +12,6 @@ }, "step": { "mfa": { - "description": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435 \u0441\u0432\u043e\u044e \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443 \u043f\u043e\u0448\u0442\u0443 \u043d\u0430 \u043d\u0430\u044f\u0432\u043d\u0456\u0441\u0442\u044c \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u0432\u0456\u0434 SimpliSafe. \u041f\u0456\u0441\u043b\u044f \u0442\u043e\u0433\u043e \u044f\u043a \u0432\u0456\u0434\u043a\u0440\u0438\u0454\u0442\u0435 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f, \u043f\u043e\u0432\u0435\u0440\u043d\u0456\u0442\u044c\u0441\u044f \u0441\u044e\u0434\u0438, \u0449\u043e\u0431 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457.", "title": "\u0414\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f SimpliSafe" }, "reauth_confirm": { diff --git a/homeassistant/components/simplisafe/translations/zh-Hant.json b/homeassistant/components/simplisafe/translations/zh-Hant.json index ae22f8c8f0d..1b10153ab05 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hant.json +++ b/homeassistant/components/simplisafe/translations/zh-Hant.json @@ -6,38 +6,37 @@ "wrong_account": "\u6240\u4ee5\u63d0\u4f9b\u7684\u6191\u8b49\u8207 Simplisafe \u5e33\u865f\u4e0d\u7b26\u3002" }, "error": { + "2fa_timed_out": "\u7b49\u5f85\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u903e\u6642", "identifier_exists": "\u5e33\u865f\u5df2\u8a3b\u518a", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "still_awaiting_mfa": "\u4ecd\u5728\u7b49\u5019\u9ede\u64ca\u591a\u6b65\u9a5f\u8a8d\u8b49\u90f5\u4ef6", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { - "input_auth_code": { - "data": { - "auth_code": "\u8a8d\u8b49\u78bc" - }, - "description": "\u8f38\u5165\u7531 SimpliSafe \u7db2\u9801 App URL \u6240\u53d6\u5f97\u4e4b\u8a8d\u8b49\u78bc\uff1a", - "title": "\u5b8c\u6210\u8a8d\u8b49" - }, "mfa": { - "description": "\u8acb\u6aa2\u67e5\u4f86\u81ea SimpliSafe \u7684\u90f5\u4ef6\u4ee5\u53d6\u5f97\u9023\u7d50\u3002\u78ba\u8a8d\u9023\u7d50\u5f8c\uff0c\u518d\u56de\u5230\u6b64\u8655\u4ee5\u5b8c\u6210\u6574\u5408\u5b89\u88dd\u3002", "title": "SimpliSafe \u591a\u6b65\u9a5f\u9a57\u8b49" }, "reauth_confirm": { "data": { "password": "\u5bc6\u78bc" }, - "description": "\u5b58\u53d6\u6b0a\u9650\u5df2\u7d93\u904e\u671f\u6216\u53d6\u6d88\uff0c\u8acb\u8f38\u5165\u5bc6\u78bc\u4ee5\u91cd\u65b0\u9023\u7d50\u5e33\u865f\u3002", + "description": "\u8acb\u8f38\u5165\u5e33\u865f {username} \u5bc6\u78bc\u3002", "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" }, + "sms_2fa": { + "data": { + "code": "\u9a57\u8b49\u78bc" + }, + "description": "\u8f38\u5165\u7c21\u8a0a\u6240\u6536\u5230\u7684\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u3002" + }, "user": { "data": { "auth_code": "\u8a8d\u8b49\u78bc", "code": "\u9a57\u8b49\u78bc\uff08\u4f7f\u7528\u65bc Home Assistant UI\uff09", "password": "\u5bc6\u78bc", - "username": "\u96fb\u5b50\u90f5\u4ef6" + "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "SimpliSafe \u70ba\u900f\u904e Web App \u65b9\u5f0f\u7684\u8a8d\u8b49\u6a5f\u5236\u3002\u7531\u65bc\u6280\u8853\u9650\u5236\u3001\u65bc\u6b64\u904e\u7a0b\u7d50\u675f\u6642\u5c07\u6703\u6709\u4e00\u6b65\u624b\u52d5\u968e\u6bb5\uff1b\u65bc\u958b\u59cb\u524d\u3001\u8acb\u78ba\u5b9a\u53c3\u95b1 [\u76f8\u95dc\u6587\u4ef6]({docs_url})\u3002\n\n1. \u9ede\u9078 [\u6b64\u8655] ({url}) \u4ee5\u958b\u555f SimpliSafe Web App \u4e26\u8f38\u5165\u9a57\u8b49\u3002\n\n2. \u7576\u767b\u5165\u5b8c\u6210\u5f8c\u3001\u56de\u5230\u6b64\u8655\u65bc\u4e0b\u65b9\u8f38\u5165\u8a8d\u8b49\u78bc\u3002", + "description": "\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31\u8207\u5bc6\u78bc\u3002", "title": "\u586b\u5beb\u8cc7\u8a0a\u3002" } } diff --git a/homeassistant/components/sleepiq/translations/fr.json b/homeassistant/components/sleepiq/translations/fr.json index 9c43b00feba..f40576959bb 100644 --- a/homeassistant/components/sleepiq/translations/fr.json +++ b/homeassistant/components/sleepiq/translations/fr.json @@ -13,6 +13,7 @@ "data": { "password": "Mot de passe" }, + "description": "L'int\u00e9gration SleepIQ doit r\u00e9-authentifier votre compte {username}.", "title": "R\u00e9-authentifier l'int\u00e9gration" }, "user": { diff --git a/homeassistant/components/slimproto/translations/ca.json b/homeassistant/components/slimproto/translations/ca.json new file mode 100644 index 00000000000..cc92e3ec9f1 --- /dev/null +++ b/homeassistant/components/slimproto/translations/ca.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/de.json b/homeassistant/components/slimproto/translations/de.json new file mode 100644 index 00000000000..69b89b76225 --- /dev/null +++ b/homeassistant/components/slimproto/translations/de.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/el.json b/homeassistant/components/slimproto/translations/el.json new file mode 100644 index 00000000000..df583a2f8f8 --- /dev/null +++ b/homeassistant/components/slimproto/translations/el.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/en.json b/homeassistant/components/slimproto/translations/en.json index fcefa6de190..2d579aab7c8 100644 --- a/homeassistant/components/slimproto/translations/en.json +++ b/homeassistant/components/slimproto/translations/en.json @@ -1,11 +1,7 @@ { - "config": { - "abort": { - "single_instance_allowed": "Already configured. Only a single configuration possible." - }, - "step": { - "user": { - } - } - } + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible." + } + } } \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/et.json b/homeassistant/components/slimproto/translations/et.json new file mode 100644 index 00000000000..e8b5eae4149 --- /dev/null +++ b/homeassistant/components/slimproto/translations/et.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/fr.json b/homeassistant/components/slimproto/translations/fr.json new file mode 100644 index 00000000000..807ba246694 --- /dev/null +++ b/homeassistant/components/slimproto/translations/fr.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/he.json b/homeassistant/components/slimproto/translations/he.json new file mode 100644 index 00000000000..d0c3523da94 --- /dev/null +++ b/homeassistant/components/slimproto/translations/he.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/hu.json b/homeassistant/components/slimproto/translations/hu.json new file mode 100644 index 00000000000..edd3c258b81 --- /dev/null +++ b/homeassistant/components/slimproto/translations/hu.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + }, + "step": { + "user": { + "one": "\u00dcres", + "other": "\u00dcres" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/id.json b/homeassistant/components/slimproto/translations/id.json new file mode 100644 index 00000000000..3a870e47986 --- /dev/null +++ b/homeassistant/components/slimproto/translations/id.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/it.json b/homeassistant/components/slimproto/translations/it.json new file mode 100644 index 00000000000..42bc1ef9f7d --- /dev/null +++ b/homeassistant/components/slimproto/translations/it.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, + "step": { + "user": { + "one": "Vuoto", + "other": "Vuoti" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/nl.json b/homeassistant/components/slimproto/translations/nl.json new file mode 100644 index 00000000000..79aaec23123 --- /dev/null +++ b/homeassistant/components/slimproto/translations/nl.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/no.json b/homeassistant/components/slimproto/translations/no.json new file mode 100644 index 00000000000..aa380f0385a --- /dev/null +++ b/homeassistant/components/slimproto/translations/no.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/pl.json b/homeassistant/components/slimproto/translations/pl.json new file mode 100644 index 00000000000..96fba53c20f --- /dev/null +++ b/homeassistant/components/slimproto/translations/pl.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/pt-BR.json b/homeassistant/components/slimproto/translations/pt-BR.json new file mode 100644 index 00000000000..9ab59f40649 --- /dev/null +++ b/homeassistant/components/slimproto/translations/pt-BR.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/ru.json b/homeassistant/components/slimproto/translations/ru.json new file mode 100644 index 00000000000..55894412230 --- /dev/null +++ b/homeassistant/components/slimproto/translations/ru.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/zh-Hant.json b/homeassistant/components/slimproto/translations/zh-Hant.json new file mode 100644 index 00000000000..942e17282bb --- /dev/null +++ b/homeassistant/components/slimproto/translations/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/smappee/translations/hu.json b/homeassistant/components/smappee/translations/hu.json index 9c3d90ac43e..712cbd0951d 100644 --- a/homeassistant/components/smappee/translations/hu.json +++ b/homeassistant/components/smappee/translations/hu.json @@ -7,7 +7,7 @@ "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_mdns": "Nem t\u00e1mogatott eszk\u00f6z a Smappee integr\u00e1ci\u00f3hoz.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz." + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3." }, "flow_title": "{name}", "step": { @@ -24,7 +24,7 @@ "description": "Adja meg a c\u00edmet a Smappee helyi integr\u00e1ci\u00f3j\u00e1nak elind\u00edt\u00e1s\u00e1hoz" }, "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "zeroconf_confirm": { "description": "Hozz\u00e1 szeretn\u00e9 adni a \"{serialnumber} serialnumber}\" sorozatsz\u00e1m\u00fa Smappee -eszk\u00f6zt az HomeAssistanthoz?", diff --git a/homeassistant/components/smappee/translations/it.json b/homeassistant/components/smappee/translations/it.json index 3a493371b16..7c18d944ab7 100644 --- a/homeassistant/components/smappee/translations/it.json +++ b/homeassistant/components/smappee/translations/it.json @@ -6,7 +6,7 @@ "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", "cannot_connect": "Impossibile connettersi", "invalid_mdns": "Dispositivo non supportato per l'integrazione Smappee.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})" }, "flow_title": "{name}", diff --git a/homeassistant/components/smartthings/translations/it.json b/homeassistant/components/smartthings/translations/it.json index 4f2006e52c3..258abb0da8d 100644 --- a/homeassistant/components/smartthings/translations/it.json +++ b/homeassistant/components/smartthings/translations/it.json @@ -19,8 +19,8 @@ "data": { "access_token": "Token di accesso" }, - "description": "Inserisci un SmartThings [Personal Access Token]({token_url}) che \u00e8 stato creato secondo le [istruzioni]({component_url}). Questo sar\u00e0 utilizzato per creare l'integrazione Home Assistant all'interno del tuo account SmartThings.", - "title": "Inserisci il Token di Accesso Personale" + "description": "Inserisci un [token di accesso personale]({token_url}) che \u00e8 stato creato secondo le [istruzioni]({component_url}). Questo sar\u00e0 utilizzato per creare l'integrazione Home Assistant all'interno del tuo account SmartThings.", + "title": "Inserisci il token di accesso personale" }, "select_location": { "data": { diff --git a/homeassistant/components/smhi/translations/hu.json b/homeassistant/components/smhi/translations/hu.json index 425cf927631..e83ea03a193 100644 --- a/homeassistant/components/smhi/translations/hu.json +++ b/homeassistant/components/smhi/translations/hu.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van" + }, "error": { "name_exists": "A n\u00e9v m\u00e1r l\u00e9tezik", "wrong_location": "Csak sv\u00e9dorsz\u00e1gi helysz\u00edn megengedett" @@ -9,7 +12,7 @@ "data": { "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "title": "Helysz\u00edn Sv\u00e9dorsz\u00e1gban" } diff --git a/homeassistant/components/solax/translations/es.json b/homeassistant/components/solax/translations/es.json new file mode 100644 index 00000000000..4728aed6395 --- /dev/null +++ b/homeassistant/components/solax/translations/es.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unknown": "Error inesperado" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/soma/translations/it.json b/homeassistant/components/soma/translations/it.json index c3cd51e6351..00d454e2c96 100644 --- a/homeassistant/components/soma/translations/it.json +++ b/homeassistant/components/soma/translations/it.json @@ -4,7 +4,7 @@ "already_setup": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", "connection_error": "Impossibile connettersi", - "missing_configuration": "Il componente Soma non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente Soma non \u00e8 configurato. Segui la documentazione.", "result_error": "SOMA Connect ha risposto con stato di errore." }, "create_entry": { diff --git a/homeassistant/components/somfy/translations/hu.json b/homeassistant/components/somfy/translations/hu.json index 06b0894faf1..96b873b2c42 100644 --- a/homeassistant/components/somfy/translations/hu.json +++ b/homeassistant/components/somfy/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "create_entry": { @@ -11,7 +11,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/somfy/translations/it.json b/homeassistant/components/somfy/translations/it.json index 1c6650232dc..0201e1e2569 100644 --- a/homeassistant/components/somfy/translations/it.json +++ b/homeassistant/components/somfy/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, diff --git a/homeassistant/components/sonarr/translations/cs.json b/homeassistant/components/sonarr/translations/cs.json index 00ff1611319..9be2106b4e2 100644 --- a/homeassistant/components/sonarr/translations/cs.json +++ b/homeassistant/components/sonarr/translations/cs.json @@ -21,6 +21,7 @@ "host": "Hostitel", "port": "Port", "ssl": "Pou\u017e\u00edv\u00e1 SSL certifik\u00e1t", + "url": "URL", "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" } } diff --git a/homeassistant/components/sonarr/translations/hu.json b/homeassistant/components/sonarr/translations/hu.json index c3cc2ac0f6c..5aabd38a974 100644 --- a/homeassistant/components/sonarr/translations/hu.json +++ b/homeassistant/components/sonarr/translations/hu.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "reauth_confirm": { - "description": "A Sonarr integr\u00e1ci\u00f3t manu\u00e1lisan kell hiteles\u00edteni: {host}", + "description": "A Sonarr-integr\u00e1ci\u00f3t manu\u00e1lisan \u00fajra kell hiteles\u00edteni a Sonarr API-n\u00e1l: {url}", "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" }, "user": { diff --git a/homeassistant/components/spotify/translations/hu.json b/homeassistant/components/spotify/translations/hu.json index 136e6185b46..f387ff6bb3a 100644 --- a/homeassistant/components/spotify/translations/hu.json +++ b/homeassistant/components/spotify/translations/hu.json @@ -3,7 +3,7 @@ "abort": { "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s az \u00e9rv\u00e9nyes\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A Spotify integr\u00e1ci\u00f3 nincs konfigur\u00e1lva. K\u00e9rj\u00fck, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "reauth_account_mismatch": "A Spotify-fi\u00f3kkal hiteles\u00edtett fi\u00f3k nem egyezik meg az \u00faj hiteles\u00edt\u00e9shez sz\u00fcks\u00e9ges fi\u00f3kkal." }, "create_entry": { @@ -11,7 +11,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "reauth_confirm": { "description": "A Spotify integr\u00e1ci\u00f3nak \u00fajra hiteles\u00edtenie kell a Spotify fi\u00f3kot: {account}", diff --git a/homeassistant/components/spotify/translations/it.json b/homeassistant/components/spotify/translations/it.json index d40fa60b4d4..445b7a233e4 100644 --- a/homeassistant/components/spotify/translations/it.json +++ b/homeassistant/components/spotify/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nella generazione dell'URL di autorizzazione.", - "missing_configuration": "L'integrazione di Spotify non \u00e8 configurata. Si prega di seguire la documentazione.", + "missing_configuration": "L'integrazione di Spotify non \u00e8 configurata. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "reauth_account_mismatch": "L'account Spotify con cui sei autenticato non corrisponde all'account necessario per la nuova autenticazione." }, diff --git a/homeassistant/components/sql/translations/ca.json b/homeassistant/components/sql/translations/ca.json new file mode 100644 index 00000000000..d69f22ca855 --- /dev/null +++ b/homeassistant/components/sql/translations/ca.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat" + }, + "error": { + "db_url_invalid": "URL de la base de dades inv\u00e0lid", + "query_invalid": "Consulta SQL inv\u00e0lida", + "value_template_invalid": "Plantilla de valor inv\u00e0lida" + }, + "step": { + "user": { + "data": { + "column": "Columna", + "db_url": "URL de la base de dades", + "query": "Selecciona la consulta", + "unit_of_measurement": "Unitat de mesura", + "value_template": "Plantilla de valor" + }, + "data_description": { + "column": "Columna de resposta de la consulta per presentar com a estat", + "db_url": "URL de la base de dades, deixa-ho en blanc per utilitzar la predeterminada de HA", + "query": "Consulta a executar, ha de comen\u00e7ar per 'SELECT'", + "unit_of_measurement": "Unitat de mesura (opcional)", + "value_template": "Plantilla de valor (opcional)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL de la base de dades inv\u00e0lid", + "query_invalid": "Consulta SQL inv\u00e0lida", + "value_template_invalid": "Plantilla de valor inv\u00e0lida" + }, + "step": { + "init": { + "data": { + "column": "Columna", + "db_url": "URL de la base de dades", + "query": "Selecciona la consulta", + "unit_of_measurement": "Unitat de mesura", + "value_template": "Plantilla de valor" + }, + "data_description": { + "column": "Columna de resposta de la consulta per presentar com a estat", + "db_url": "URL de la base de dades, deixa-ho en blanc per utilitzar la predeterminada de HA", + "query": "Consulta a executar, ha de comen\u00e7ar per 'SELECT'", + "unit_of_measurement": "Unitat de mesura (opcional)", + "value_template": "Plantilla de valor (opcional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/de.json b/homeassistant/components/sql/translations/de.json new file mode 100644 index 00000000000..2207997133d --- /dev/null +++ b/homeassistant/components/sql/translations/de.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert" + }, + "error": { + "db_url_invalid": "Datenbank-URL ung\u00fcltig", + "query_invalid": "SQL-Abfrage ung\u00fcltig", + "value_template_invalid": "Wertvorlage ung\u00fcltig" + }, + "step": { + "user": { + "data": { + "column": "Spalte", + "db_url": "Datenbank-URL", + "query": "Abfrage ausw\u00e4hlen", + "unit_of_measurement": "Ma\u00dfeinheit", + "value_template": "Wertvorlage" + }, + "data_description": { + "column": "Spalte f\u00fcr die zur\u00fcckgegebene Abfrage, die als Status angezeigt werden soll", + "db_url": "Datenbank-URL, leer lassen, um die Standard-HA-Datenbank zu verwenden", + "query": "Auszuf\u00fchrende Abfrage, muss mit 'SELECT' beginnen", + "unit_of_measurement": "Ma\u00dfeinheit (optional)", + "value_template": "Wertvorlage (optional)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Datenbank-URL ung\u00fcltig", + "query_invalid": "SQL-Abfrage ung\u00fcltig", + "value_template_invalid": "Wertvorlage ung\u00fcltig" + }, + "step": { + "init": { + "data": { + "column": "Spalte", + "db_url": "Datenbank-URL", + "query": "Abfrage ausw\u00e4hlen", + "unit_of_measurement": "Ma\u00dfeinheit", + "value_template": "Wertvorlage" + }, + "data_description": { + "column": "Spalte f\u00fcr die zur\u00fcckgegebene Abfrage, die als Status angezeigt werden soll", + "db_url": "Datenbank-URL, leer lassen, um die Standard-HA-Datenbank zu verwenden", + "query": "Auszuf\u00fchrende Abfrage, muss mit 'SELECT' beginnen", + "unit_of_measurement": "Ma\u00dfeinheit (optional)", + "value_template": "Wertvorlage (optional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/el.json b/homeassistant/components/sql/translations/el.json new file mode 100644 index 00000000000..4f19401a8f3 --- /dev/null +++ b/homeassistant/components/sql/translations/el.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "db_url_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL", + "value_template_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + }, + "step": { + "user": { + "data": { + "column": "\u03a3\u03c4\u03ae\u03bb\u03b7", + "db_url": "URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "query": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03c1\u03c9\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b1\u03be\u03af\u03b1\u03c2" + }, + "data_description": { + "column": "\u03a3\u03c4\u03ae\u03bb\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03b5\u03c6\u03cc\u03bc\u03b5\u03bd\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03b1\u03c3\u03c4\u03b5\u03af \u03c9\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", + "db_url": "URL \u03c4\u03b7\u03c2 \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b2\u03ac\u03c3\u03b7 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd HA", + "query": "\u03a4\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03b5\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ac \u03bc\u03b5 'SELECT'", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL", + "value_template_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + }, + "step": { + "init": { + "data": { + "column": "\u03a3\u03c4\u03ae\u03bb\u03b7", + "db_url": "URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "query": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03c1\u03c9\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + }, + "data_description": { + "column": "\u03a3\u03c4\u03ae\u03bb\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03b5\u03c6\u03cc\u03bc\u03b5\u03bd\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03b1\u03c3\u03c4\u03b5\u03af \u03c9\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", + "db_url": "URL \u03c4\u03b7\u03c2 \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b2\u03ac\u03c3\u03b7 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd HA", + "query": "\u03a4\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03b5\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ac \u03bc\u03b5 'SELECT'", + "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", + "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/en.json b/homeassistant/components/sql/translations/en.json index 2d3bf3c1999..3b1fc223c00 100644 --- a/homeassistant/components/sql/translations/en.json +++ b/homeassistant/components/sql/translations/en.json @@ -18,9 +18,9 @@ "value_template": "Value Template" }, "data_description": { + "column": "Column for returned query to present as state", "db_url": "Database URL, leave empty to use default HA database", "query": "Query to run, needs to start with 'SELECT'", - "column": "Column for returned query to present as state", "unit_of_measurement": "Unit of Measure (optional)", "value_template": "Value Template (optional)" } @@ -43,9 +43,9 @@ "value_template": "Value Template" }, "data_description": { + "column": "Column for returned query to present as state", "db_url": "Database URL, leave empty to use default HA database", "query": "Query to run, needs to start with 'SELECT'", - "column": "Column for returned query to present as state", "unit_of_measurement": "Unit of Measure (optional)", "value_template": "Value Template (optional)" } diff --git a/homeassistant/components/sql/translations/et.json b/homeassistant/components/sql/translations/et.json new file mode 100644 index 00000000000..b1f7339aba2 --- /dev/null +++ b/homeassistant/components/sql/translations/et.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Kasutaja on juba seadistatud" + }, + "error": { + "db_url_invalid": "Andmebaasi URL on vigane", + "query_invalid": "SQL-p\u00e4ring on kehtetu", + "value_template_invalid": "V\u00e4\u00e4rtuse mall on kehtetu" + }, + "step": { + "user": { + "data": { + "column": "Veerg", + "db_url": "Andmebaasi URL", + "query": "Vali p\u00e4ring", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", + "value_template": "V\u00e4\u00e4rtuse mall" + }, + "data_description": { + "column": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks", + "db_url": "Andmebaasi URL, j\u00e4ta t\u00fchjaks, et kasutada HA vaikeandmebaasi", + "query": "K\u00e4ivitatav p\u00e4ring peab algama 'SELECT'-iga.", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik (valikuline)", + "value_template": "V\u00e4\u00e4rtuse mall (valikuline)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Andmebaasi URL on vigane", + "query_invalid": "V\u00e4\u00e4rtuse mall on kehtetu", + "value_template_invalid": "V\u00e4\u00e4rtuse mall on kehtetu" + }, + "step": { + "init": { + "data": { + "column": "Veerg", + "db_url": "Andmebaasi URL", + "query": "Vali p\u00e4ring", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", + "value_template": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks" + }, + "data_description": { + "column": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks", + "db_url": "Andmebaasi URL, j\u00e4ta t\u00fchjaks, et kasutada HA vaikeandmebaasi", + "query": "K\u00e4ivitatav p\u00e4ring peab algama 'SELECT'-iga.", + "unit_of_measurement": "M\u00f5\u00f5t\u00fchik (valikuline)", + "value_template": "V\u00e4\u00e4rtuse mall (valikuline)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/fr.json b/homeassistant/components/sql/translations/fr.json new file mode 100644 index 00000000000..078fdc44d24 --- /dev/null +++ b/homeassistant/components/sql/translations/fr.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "db_url_invalid": "URL de la base de donn\u00e9es non valide", + "query_invalid": "Requ\u00eate SQL non valide", + "value_template_invalid": "Mod\u00e8le de valeur non valide" + }, + "step": { + "user": { + "data": { + "column": "Colonne", + "db_url": "URL de la base de donn\u00e9es", + "query": "Requ\u00eate de s\u00e9lection", + "unit_of_measurement": "Unit\u00e9 de mesure", + "value_template": "Mod\u00e8le de valeur" + }, + "data_description": { + "column": "La colonne de la r\u00e9ponse \u00e0 la requ\u00eate \u00e0 pr\u00e9senter en tant qu'\u00e9tat", + "db_url": "L'URL de la base de donn\u00e9es, laisser vide pour utiliser la base de donn\u00e9es HA par d\u00e9faut", + "query": "La requ\u00eate \u00e0 ex\u00e9cuter, doit commencer par \u00ab\u00a0SELECT\u00a0\u00bb", + "unit_of_measurement": "Unit\u00e9 de mesure (facultatif)", + "value_template": "Mod\u00e8le de valeur (facultatif)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL de la base de donn\u00e9es non valide", + "query_invalid": "Requ\u00eate SQL non valide", + "value_template_invalid": "Mod\u00e8le de valeur non valide" + }, + "step": { + "init": { + "data": { + "column": "Colonne", + "db_url": "URL de la base de donn\u00e9es", + "query": "Requ\u00eate de s\u00e9lection", + "unit_of_measurement": "Unit\u00e9 de mesure", + "value_template": "Mod\u00e8le de valeur" + }, + "data_description": { + "column": "La colonne de la r\u00e9ponse \u00e0 la requ\u00eate \u00e0 pr\u00e9senter en tant qu'\u00e9tat", + "db_url": "L'URL de la base de donn\u00e9es, laisser vide pour utiliser la base de donn\u00e9es HA par d\u00e9faut", + "query": "La requ\u00eate \u00e0 ex\u00e9cuter, doit commencer par \u00ab\u00a0SELECT\u00a0\u00bb", + "unit_of_measurement": "Unit\u00e9 de mesure (facultatif)", + "value_template": "Mod\u00e8le de valeur (facultatif)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/he.json b/homeassistant/components/sql/translations/he.json new file mode 100644 index 00000000000..937391f9327 --- /dev/null +++ b/homeassistant/components/sql/translations/he.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/hu.json b/homeassistant/components/sql/translations/hu.json new file mode 100644 index 00000000000..cf125215ae3 --- /dev/null +++ b/homeassistant/components/sql/translations/hu.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "db_url_invalid": "\u00c9rv\u00e9nytelen adatb\u00e1zis URL-c\u00edm", + "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s", + "value_template_invalid": "\u00c9rv\u00e9nytelen \u00e9rt\u00e9ksablon" + }, + "step": { + "user": { + "data": { + "column": "Oszlop", + "db_url": "Adatb\u00e1zis URL", + "query": "Lek\u00e9rdez\u00e9s kijel\u00f6l\u00e9se", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", + "value_template": "\u00c9rt\u00e9ksablon" + }, + "data_description": { + "column": "\u00c1llapotk\u00e9nt megjelen\u00edtend\u0151, lek\u00e9rdezett oszlop", + "db_url": "Adatb\u00e1zis URL-c\u00edme, hagyja \u00fcresen az alap\u00e9rtelmezett HA-adatb\u00e1zis haszn\u00e1lat\u00e1hoz", + "query": "A futtatand\u00f3 lek\u00e9rdez\u00e9snek 'SELECT'-el kell kezd\u0151dnie.", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g (nem k\u00f6telez\u0151)", + "value_template": "\u00c9rt\u00e9ksablon (nem k\u00f6telez\u0151)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "\u00c9rv\u00e9nytelen adatb\u00e1zis URL-c\u00edm", + "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s", + "value_template_invalid": "\u00c9rv\u00e9nytelen \u00e9rt\u00e9ksablon" + }, + "step": { + "init": { + "data": { + "column": "Oszlop", + "db_url": "Adatb\u00e1zis URL", + "query": "Lek\u00e9rdez\u00e9s kijel\u00f6l\u00e9se", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", + "value_template": "\u00c9rt\u00e9ksablon" + }, + "data_description": { + "column": "\u00c1llapotk\u00e9nt megjelen\u00edtend\u0151, lek\u00e9rdezett oszlop", + "db_url": "Adatb\u00e1zis URL-c\u00edme, hagyja \u00fcresen az alap\u00e9rtelmezett HA-adatb\u00e1zis haszn\u00e1lat\u00e1hoz", + "query": "A futtatand\u00f3 lek\u00e9rdez\u00e9snek 'SELECT'-el kell kezd\u0151dnie.", + "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g (nem k\u00f6telez\u0151)", + "value_template": "\u00c9rt\u00e9ksablon (nem k\u00f6telez\u0151)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/id.json b/homeassistant/components/sql/translations/id.json new file mode 100644 index 00000000000..9cd3574c6c9 --- /dev/null +++ b/homeassistant/components/sql/translations/id.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi" + }, + "error": { + "db_url_invalid": "URL database tidak valid", + "query_invalid": "Kueri SQL tidak valid", + "value_template_invalid": "Templat Nilai tidak valid" + }, + "step": { + "user": { + "data": { + "column": "Kolom", + "db_url": "URL Database", + "query": "Kueri Select", + "unit_of_measurement": "Satuan Ukuran", + "value_template": "Templat Nilai" + }, + "data_description": { + "column": "Kolom pada kueri yang dikembalikan untuk ditampilkan sebagai status", + "db_url": "URL database, kosongkan untuk menggunakan database HA default", + "query": "Kueri untuk dijalankan, perlu dimulai dengan 'SELECT'", + "unit_of_measurement": "Satuan Ukuran (opsional)", + "value_template": "Template Nilai (opsional)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL database tidak valid", + "query_invalid": "Kueri SQL tidak valid", + "value_template_invalid": "Templat Nilai tidak valid" + }, + "step": { + "init": { + "data": { + "column": "Kolom", + "db_url": "URL Database", + "query": "Kueri Select", + "unit_of_measurement": "Satuan Ukuran", + "value_template": "Templat Nilai" + }, + "data_description": { + "column": "Kolom pada kueri yang dikembalikan untuk ditampilkan sebagai status", + "db_url": "URL database, kosongkan untuk menggunakan database HA default", + "query": "Kueri untuk dijalankan, perlu dimulai dengan 'SELECT'", + "unit_of_measurement": "Satuan Ukuran (opsional)", + "value_template": "Template Nilai (opsional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/it.json b/homeassistant/components/sql/translations/it.json new file mode 100644 index 00000000000..01672eb5a51 --- /dev/null +++ b/homeassistant/components/sql/translations/it.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato" + }, + "error": { + "db_url_invalid": "URL database non valido", + "query_invalid": "Query SQL non valida", + "value_template_invalid": "Modello di valore non valido" + }, + "step": { + "user": { + "data": { + "column": "Colonna", + "db_url": "URL del database", + "query": "Query Select", + "unit_of_measurement": "Unit\u00e0 di misura", + "value_template": "Modello di valore" + }, + "data_description": { + "column": "Colonna per la query restituita da presentare come stato", + "db_url": "URL del database, lascia vuoto per utilizzare il database predefinito di Home Assistant", + "query": "Query da eseguire, deve iniziare con 'SELECT'", + "unit_of_measurement": "Unit\u00e0 di misura (opzionale)", + "value_template": "Modello di valore (opzionale)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL database non valido", + "query_invalid": "Query SQL non valida", + "value_template_invalid": "Modello di valore non valido" + }, + "step": { + "init": { + "data": { + "column": "Colonna", + "db_url": "URL del database", + "query": "Query Select", + "unit_of_measurement": "Unit\u00e0 di misura", + "value_template": "Modello di valore" + }, + "data_description": { + "column": "Colonna per la query restituita da presentare come stato", + "db_url": "URL del database, lascia vuoto per utilizzare il database predefinito di Home Assistant", + "query": "Query da eseguire, deve iniziare con 'SELECT'", + "unit_of_measurement": "Unit\u00e0 di misura (opzionale)", + "value_template": "Modello di valore (opzionale)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/nl.json b/homeassistant/components/sql/translations/nl.json new file mode 100644 index 00000000000..947066307b5 --- /dev/null +++ b/homeassistant/components/sql/translations/nl.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd" + }, + "error": { + "db_url_invalid": "Database URL ongeldig", + "query_invalid": "SQL Query ongeldig", + "value_template_invalid": "Waarde Template ongeldig" + }, + "step": { + "user": { + "data": { + "column": "Kolom", + "db_url": "Database URL", + "query": "Selecteer Query", + "unit_of_measurement": "Meeteenheid", + "value_template": "Waarde Template" + }, + "data_description": { + "column": "Kolom voor teruggestuurde query om als status te presenteren", + "db_url": "Database URL, leeg laten om de standaard HA database te gebruiken", + "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", + "unit_of_measurement": "Meeteenheid (optioneel)", + "value_template": "Waarde Template (optioneel)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Database URL ongeldig", + "query_invalid": "SQL Query ongeldig", + "value_template_invalid": "Waarde Template ongeldig" + }, + "step": { + "init": { + "data": { + "column": "Kolom", + "db_url": "Database URL", + "query": "Selecteer Query", + "unit_of_measurement": "Meeteenheid", + "value_template": "Waarde Template" + }, + "data_description": { + "column": "Kolom voor teruggestuurde query om als status te presenteren", + "db_url": "Database URL, leeg laten om de standaard HA database te gebruiken", + "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", + "unit_of_measurement": "Meeteenheid (optioneel)", + "value_template": "Waarde Template (optioneel)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/no.json b/homeassistant/components/sql/translations/no.json new file mode 100644 index 00000000000..292486d3c0f --- /dev/null +++ b/homeassistant/components/sql/translations/no.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert" + }, + "error": { + "db_url_invalid": "Database-URL er ugyldig", + "query_invalid": "SQL-sp\u00f8rringen er ugyldig", + "value_template_invalid": "Verdimalen er ugyldig" + }, + "step": { + "user": { + "data": { + "column": "Kolonne", + "db_url": "Database URL", + "query": "Velg Sp\u00f8rring", + "unit_of_measurement": "M\u00e5leenhet", + "value_template": "Verdimal" + }, + "data_description": { + "column": "Kolonne for returnert foresp\u00f8rsel for \u00e5 presentere som tilstand", + "db_url": "Database-URL, la st\u00e5 tom for \u00e5 bruke standard HA-database", + "query": "Sp\u00f8rsm\u00e5let skal kj\u00f8res, m\u00e5 starte med \"SELECT\"", + "unit_of_measurement": "M\u00e5leenhet (valgfritt)", + "value_template": "Verdimal (valgfritt)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Database-URL er ugyldig", + "query_invalid": "SQL-sp\u00f8rringen er ugyldig", + "value_template_invalid": "Verdimalen er ugyldig" + }, + "step": { + "init": { + "data": { + "column": "Kolonne", + "db_url": "Database URL", + "query": "Velg Sp\u00f8rring", + "unit_of_measurement": "M\u00e5leenhet", + "value_template": "Verdimal" + }, + "data_description": { + "column": "Kolonne for returnert foresp\u00f8rsel for \u00e5 presentere som tilstand", + "db_url": "Database-URL, la st\u00e5 tom for \u00e5 bruke standard HA-database", + "query": "Sp\u00f8rsm\u00e5let skal kj\u00f8res, m\u00e5 starte med \"SELECT\"", + "unit_of_measurement": "M\u00e5leenhet (valgfritt)", + "value_template": "Verdimal (valgfritt)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/pl.json b/homeassistant/components/sql/translations/pl.json new file mode 100644 index 00000000000..0e8cb47b148 --- /dev/null +++ b/homeassistant/components/sql/translations/pl.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane" + }, + "error": { + "db_url_invalid": "Nieprawid\u0142owy adres URL bazy danych", + "query_invalid": "Nieprawid\u0142owe zapytanie SQL", + "value_template_invalid": "Nieprawid\u0142owy szablon warto\u015bci" + }, + "step": { + "user": { + "data": { + "column": "Kolumna", + "db_url": "Adres URL bazy danych", + "query": "Wybierz zapytanie", + "unit_of_measurement": "Jednostka miary", + "value_template": "Szablon warto\u015bci" + }, + "data_description": { + "column": "Kolumna dla zwr\u00f3conego zapytania do przedstawienia jako stan", + "db_url": "Adres URL bazy danych, pozostaw puste, aby u\u017cy\u0107 domy\u015blnej bazy danych HA", + "query": "Zapytanie do uruchomienia, musi zaczyna\u0107 si\u0119 od \u201eSELECT\u201d", + "unit_of_measurement": "Jednostka miary (opcjonalnie)", + "value_template": "Szablon warto\u015bci (opcjonalnie)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Nieprawid\u0142owy adres URL bazy danych", + "query_invalid": "Nieprawid\u0142owe zapytanie SQL", + "value_template_invalid": "Nieprawid\u0142owy szablon warto\u015bci" + }, + "step": { + "init": { + "data": { + "column": "Kolumna", + "db_url": "Adres URL bazy danych", + "query": "Wybierz zapytanie", + "unit_of_measurement": "Jednostka miary", + "value_template": "Szablon warto\u015bci" + }, + "data_description": { + "column": "Kolumna dla zwr\u00f3conego zapytania do przedstawienia jako stan", + "db_url": "Adres URL bazy danych, pozostaw puste, aby u\u017cy\u0107 domy\u015blnej bazy danych HA", + "query": "Zapytanie do uruchomienia, musi zaczyna\u0107 si\u0119 od \u201eSELECT\u201d", + "unit_of_measurement": "Jednostka miary (opcjonalnie)", + "value_template": "Szablon warto\u015bci (opcjonalnie)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/pt-BR.json b/homeassistant/components/sql/translations/pt-BR.json new file mode 100644 index 00000000000..10df43c7e1f --- /dev/null +++ b/homeassistant/components/sql/translations/pt-BR.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada" + }, + "error": { + "db_url_invalid": "URL do banco de dados inv\u00e1lida", + "query_invalid": "Consulta SQL inv\u00e1lida", + "value_template_invalid": "Modelo do valor inv\u00e1lido" + }, + "step": { + "user": { + "data": { + "column": "Coluna", + "db_url": "URL do banco de dados", + "query": "Selecionar consulta", + "unit_of_measurement": "Unidade de medida", + "value_template": "Modelo do valor" + }, + "data_description": { + "column": "Coluna para a consulta retornada para apresentar como estado", + "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", + "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", + "unit_of_measurement": "Unidade de medida (opcional)", + "value_template": "Modelo do valor (opcional)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL do banco de dados inv\u00e1lida", + "query_invalid": "Consulta SQL inv\u00e1lida", + "value_template_invalid": "Modelo do valor inv\u00e1lido" + }, + "step": { + "init": { + "data": { + "column": "Coluna", + "db_url": "URL do banco de dados", + "query": "Selecionar consulta", + "unit_of_measurement": "Unidade de medida", + "value_template": "Modelo do valor" + }, + "data_description": { + "column": "Coluna para a consulta retornada para apresentar como estado", + "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", + "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", + "unit_of_measurement": "Unidade de medida (opcional)", + "value_template": "Modelo do valor (opcional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/ru.json b/homeassistant/components/sql/translations/ru.json new file mode 100644 index 00000000000..8f8e0741583 --- /dev/null +++ b/homeassistant/components/sql/translations/ru.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "error": { + "db_url_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.", + "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441.", + "value_template_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f." + }, + "step": { + "user": { + "data": { + "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", + "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", + "query": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "data_description": { + "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 HA \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", + "query": "\u0417\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 'SELECT'", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.", + "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441.", + "value_template_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f." + }, + "step": { + "init": { + "data": { + "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", + "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", + "query": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "data_description": { + "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", + "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 HA \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", + "query": "\u0417\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 'SELECT'", + "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/tr.json b/homeassistant/components/sql/translations/tr.json new file mode 100644 index 00000000000..edbce58d65a --- /dev/null +++ b/homeassistant/components/sql/translations/tr.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "db_url_invalid": "Veritaban\u0131 URL'si ge\u00e7ersiz", + "query_invalid": "SQL Sorgusu ge\u00e7ersiz", + "value_template_invalid": "De\u011fer \u015eablonu ge\u00e7ersiz" + }, + "step": { + "user": { + "data": { + "column": "S\u00fctun", + "db_url": "Veritaban\u0131 URL'si", + "query": "Sorgu Se\u00e7", + "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", + "value_template": "De\u011fer \u015eablonu" + }, + "data_description": { + "column": "D\u00f6nd\u00fcr\u00fclen sorgunun durum olarak sunulmas\u0131 i\u00e7in s\u00fctun", + "db_url": "Veritaban\u0131 URL'si, varsay\u0131lan HA veritaban\u0131n\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n", + "query": "\u00c7al\u0131\u015ft\u0131r\u0131lacak sorgu, 'SE\u00c7' ile ba\u015flamal\u0131d\u0131r", + "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi (iste\u011fe ba\u011fl\u0131)", + "value_template": "De\u011fer \u015eablonu (iste\u011fe ba\u011fl\u0131)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "Veritaban\u0131 URL'si ge\u00e7ersiz", + "query_invalid": "SQL Sorgusu ge\u00e7ersiz", + "value_template_invalid": "De\u011fer \u015eablonu ge\u00e7ersiz" + }, + "step": { + "init": { + "data": { + "column": "S\u00fctun", + "db_url": "Veritaban\u0131 URL'si", + "query": "Sorgu Se\u00e7", + "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", + "value_template": "De\u011fer \u015eablonu" + }, + "data_description": { + "column": "D\u00f6nd\u00fcr\u00fclen sorgunun durum olarak sunulmas\u0131 i\u00e7in s\u00fctun", + "db_url": "Veritaban\u0131 URL'si, varsay\u0131lan HA veritaban\u0131n\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n", + "query": "\u00c7al\u0131\u015ft\u0131r\u0131lacak sorgu, 'SE\u00c7' ile ba\u015flamal\u0131d\u0131r", + "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi (iste\u011fe ba\u011fl\u0131)", + "value_template": "De\u011fer \u015eablonu (iste\u011fe ba\u011fl\u0131)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/zh-Hant.json b/homeassistant/components/sql/translations/zh-Hant.json new file mode 100644 index 00000000000..801d973049d --- /dev/null +++ b/homeassistant/components/sql/translations/zh-Hant.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "db_url_invalid": "\u8cc7\u6599\u5eab URL \u7121\u6548", + "query_invalid": "SQL \u67e5\u8a62\u7121\u6548", + "value_template_invalid": "\u6578\u503c\u6a21\u677f\u7121\u6548" + }, + "step": { + "user": { + "data": { + "column": "\u6b04\u4f4d", + "db_url": "\u8cc7\u6599\u5eab URL", + "query": "\u9078\u64c7\u67e5\u8a62", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", + "value_template": "\u6578\u503c\u6a21\u677f" + }, + "data_description": { + "column": "\u67e5\u8a62\u56de\u8986\u6b04\u4f4d\u70ba\u72c0\u614b", + "db_url": "\u8cc7\u6599\u5eab URL\u3001\u4fdd\u7559\u7a7a\u767d\u4ee5\u4f7f\u7528\u9810\u8a2d HA \u8cc7\u6599\u5eab", + "query": "\u57f7\u884c\u7684\u67e5\u8a62\u3001\u9700\u8981\u4ee5 'SELECT' \u4f5c\u70ba\u958b\u982d", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d\uff08\u9078\u9805\uff09", + "value_template": "\u6578\u503c\u6a21\u677f\uff08\u9078\u9805\uff09" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "\u8cc7\u6599\u5eab URL \u7121\u6548", + "query_invalid": "SQL \u67e5\u8a62\u7121\u6548", + "value_template_invalid": "\u6578\u503c\u6a21\u677f\u7121\u6548" + }, + "step": { + "init": { + "data": { + "column": "\u6b04\u4f4d", + "db_url": "\u8cc7\u6599\u5eab URL", + "query": "\u9078\u64c7\u67e5\u8a62", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", + "value_template": "\u6578\u503c\u6a21\u677f" + }, + "data_description": { + "column": "\u67e5\u8a62\u56de\u8986\u6b04\u4f4d\u70ba\u72c0\u614b", + "db_url": "\u8cc7\u6599\u5eab URL\u3001\u4fdd\u7559\u7a7a\u767d\u4ee5\u4f7f\u7528\u9810\u8a2d HA \u8cc7\u6599\u5eab", + "query": "\u57f7\u884c\u7684\u67e5\u8a62\u3001\u9700\u8981\u4ee5 'SELECT' \u4f5c\u70ba\u958b\u982d", + "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d\uff08\u9078\u9805\uff09", + "value_template": "\u6578\u503c\u6a21\u677f\uff08\u9078\u9805\uff09" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/ca.json b/homeassistant/components/srp_energy/translations/ca.json index c6617e617d4..45a2d2fa943 100644 --- a/homeassistant/components/srp_energy/translations/ca.json +++ b/homeassistant/components/srp_energy/translations/ca.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/cs.json b/homeassistant/components/srp_energy/translations/cs.json index 74b4bd53090..74045d75b2e 100644 --- a/homeassistant/components/srp_energy/translations/cs.json +++ b/homeassistant/components/srp_energy/translations/cs.json @@ -18,6 +18,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/de.json b/homeassistant/components/srp_energy/translations/de.json index a7992cac9b1..d1975b8f59d 100644 --- a/homeassistant/components/srp_energy/translations/de.json +++ b/homeassistant/components/srp_energy/translations/de.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/el.json b/homeassistant/components/srp_energy/translations/el.json index 4583eb47823..99a5f1ea850 100644 --- a/homeassistant/components/srp_energy/translations/el.json +++ b/homeassistant/components/srp_energy/translations/el.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/en.json b/homeassistant/components/srp_energy/translations/en.json index 99926b18b4f..a031e56022e 100644 --- a/homeassistant/components/srp_energy/translations/en.json +++ b/homeassistant/components/srp_energy/translations/en.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/es.json b/homeassistant/components/srp_energy/translations/es.json index 849c5019d3b..ebd4583fe43 100644 --- a/homeassistant/components/srp_energy/translations/es.json +++ b/homeassistant/components/srp_energy/translations/es.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/et.json b/homeassistant/components/srp_energy/translations/et.json index 558bb4a19ed..52f3de847fb 100644 --- a/homeassistant/components/srp_energy/translations/et.json +++ b/homeassistant/components/srp_energy/translations/et.json @@ -19,6 +19,5 @@ } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/fr.json b/homeassistant/components/srp_energy/translations/fr.json index b8544d4d469..93c6ade07c4 100644 --- a/homeassistant/components/srp_energy/translations/fr.json +++ b/homeassistant/components/srp_energy/translations/fr.json @@ -19,6 +19,5 @@ } } } - }, - "title": "\u00c9nergie SRP" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/hu.json b/homeassistant/components/srp_energy/translations/hu.json index 4d617e09cfc..b032f24e5fc 100644 --- a/homeassistant/components/srp_energy/translations/hu.json +++ b/homeassistant/components/srp_energy/translations/hu.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/id.json b/homeassistant/components/srp_energy/translations/id.json index fefcbff2ecb..113e07b987f 100644 --- a/homeassistant/components/srp_energy/translations/id.json +++ b/homeassistant/components/srp_energy/translations/id.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/it.json b/homeassistant/components/srp_energy/translations/it.json index d5ccf02d74c..f81b5e2e1a4 100644 --- a/homeassistant/components/srp_energy/translations/it.json +++ b/homeassistant/components/srp_energy/translations/it.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/ja.json b/homeassistant/components/srp_energy/translations/ja.json index 432c553910b..805a500502b 100644 --- a/homeassistant/components/srp_energy/translations/ja.json +++ b/homeassistant/components/srp_energy/translations/ja.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/ka.json b/homeassistant/components/srp_energy/translations/ka.json index f4e15ad5d9e..3aec1ed2f43 100644 --- a/homeassistant/components/srp_energy/translations/ka.json +++ b/homeassistant/components/srp_energy/translations/ka.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/ko.json b/homeassistant/components/srp_energy/translations/ko.json index b329cf1f2b1..bb0d912111e 100644 --- a/homeassistant/components/srp_energy/translations/ko.json +++ b/homeassistant/components/srp_energy/translations/ko.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/nl.json b/homeassistant/components/srp_energy/translations/nl.json index 3cf2298b348..ce4ac90c223 100644 --- a/homeassistant/components/srp_energy/translations/nl.json +++ b/homeassistant/components/srp_energy/translations/nl.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/no.json b/homeassistant/components/srp_energy/translations/no.json index 5505e140cd3..51bb1c7fd18 100644 --- a/homeassistant/components/srp_energy/translations/no.json +++ b/homeassistant/components/srp_energy/translations/no.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/pl.json b/homeassistant/components/srp_energy/translations/pl.json index f89165a9065..eec873b6820 100644 --- a/homeassistant/components/srp_energy/translations/pl.json +++ b/homeassistant/components/srp_energy/translations/pl.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/pt-BR.json b/homeassistant/components/srp_energy/translations/pt-BR.json index b0dbfe9c6d0..e7613d468a3 100644 --- a/homeassistant/components/srp_energy/translations/pt-BR.json +++ b/homeassistant/components/srp_energy/translations/pt-BR.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/ru.json b/homeassistant/components/srp_energy/translations/ru.json index a492fa7dfb2..a94e18aa20a 100644 --- a/homeassistant/components/srp_energy/translations/ru.json +++ b/homeassistant/components/srp_energy/translations/ru.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/tr.json b/homeassistant/components/srp_energy/translations/tr.json index ead8238d82c..0c815f97186 100644 --- a/homeassistant/components/srp_energy/translations/tr.json +++ b/homeassistant/components/srp_energy/translations/tr.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Enerji" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/uk.json b/homeassistant/components/srp_energy/translations/uk.json index 144e40f15bb..2d4b399947f 100644 --- a/homeassistant/components/srp_energy/translations/uk.json +++ b/homeassistant/components/srp_energy/translations/uk.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/zh-Hans.json b/homeassistant/components/srp_energy/translations/zh-Hans.json index 36016f3e217..b0b26b02261 100644 --- a/homeassistant/components/srp_energy/translations/zh-Hans.json +++ b/homeassistant/components/srp_energy/translations/zh-Hans.json @@ -8,6 +8,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/srp_energy/translations/zh-Hant.json b/homeassistant/components/srp_energy/translations/zh-Hant.json index adbf635100c..bed1cd5c3c1 100644 --- a/homeassistant/components/srp_energy/translations/zh-Hant.json +++ b/homeassistant/components/srp_energy/translations/zh-Hant.json @@ -19,6 +19,5 @@ } } } - }, - "title": "SRP Energy" + } } \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/ca.json b/homeassistant/components/steam_online/translations/ca.json new file mode 100644 index 00000000000..22a8a83a896 --- /dev/null +++ b/homeassistant/components/steam_online/translations/ca.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_account": "ID del compte inv\u00e0lid", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "reauth_confirm": { + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + }, + "user": { + "data": { + "api_key": "Clau API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/de.json b/homeassistant/components/steam_online/translations/de.json new file mode 100644 index 00000000000..17950e14e24 --- /dev/null +++ b/homeassistant/components/steam_online/translations/de.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_account": "Ung\u00fcltige Konto-ID", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "reauth_confirm": { + "description": "Die Steam-Integration muss manuell erneut authentifiziert werden \n\nDeinen Schl\u00fcssel findest du hier: https://steamcommunity.com/dev/apikey", + "title": "Integration erneut authentifizieren" + }, + "user": { + "data": { + "account": "Steam-Konto-ID", + "api_key": "API-Schl\u00fcssel" + }, + "description": "Verwende https://steamid.io, um deine Steam-Konto-ID zu finden" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Namen der zu \u00fcberwachenden Konten" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/el.json b/homeassistant/components/steam_online/translations/el.json new file mode 100644 index 00000000000..0f598dbc395 --- /dev/null +++ b/homeassistant/components/steam_online/translations/el.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_account": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Steam \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03c5\u03c4\u03b5\u03af \u03be\u03b1\u03bd\u03ac \u03bc\u03b5 \u03bc\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03bf \u03c4\u03c1\u03cc\u03c0\u03bf \n\n \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c3\u03b1\u03c2 \u03b5\u03b4\u03ce: https://steamcommunity.com/dev/apikey", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + }, + "user": { + "data": { + "account": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd Steam", + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "description": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf https://steamid.io \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\u039f\u03bd\u03cc\u03bc\u03b1\u03c4\u03b1 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03ce\u03bd \u03c0\u03c1\u03bf\u03c2 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/en.json b/homeassistant/components/steam_online/translations/en.json index 69d80890d5a..8b34a85922d 100644 --- a/homeassistant/components/steam_online/translations/en.json +++ b/homeassistant/components/steam_online/translations/en.json @@ -6,22 +6,22 @@ }, "error": { "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", "invalid_account": "Invalid account ID", + "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, "step": { + "reauth_confirm": { + "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: https://steamcommunity.com/dev/apikey", + "title": "Reauthenticate Integration" + }, "user": { "data": { - "api_key": "API Key", - "account": "Steam account ID" + "account": "Steam account ID", + "api_key": "API Key" }, - "description": "Documentation: https://www.home-assistant.io/integrations/steam_online\n\nUse https://steamid.io/ to find your Steam account ID" - }, - "reauth_confirm": { - "title": "Reauthenticate Integration", - "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: https://steamcommunity.com/dev/apikey" - } + "description": "Use https://steamid.io to find your Steam account ID" + } } }, "options": { diff --git a/homeassistant/components/steam_online/translations/et.json b/homeassistant/components/steam_online/translations/et.json new file mode 100644 index 00000000000..8d501ccf50d --- /dev/null +++ b/homeassistant/components/steam_online/translations/et.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_account": "Kehtetu konto ID", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "reauth_confirm": { + "description": "Steami sidumine tuleb uuesti autentida\n\nV\u00f5tme leiad siit: https://steamcommunity.com/dev/apikey", + "title": "Taastuvasta sidumine" + }, + "user": { + "data": { + "account": "Steami konto ID", + "api_key": "API v\u00f5ti" + }, + "description": "Kasuta https://steamid.io, et leida oma Steam'i konto ID" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "J\u00e4lgitavate kontode nimed" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/fr.json b/homeassistant/components/steam_online/translations/fr.json new file mode 100644 index 00000000000..5ee6892eef7 --- /dev/null +++ b/homeassistant/components/steam_online/translations/fr.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_account": "ID de compte non valide", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "reauth_confirm": { + "description": "L'int\u00e9gration Steam doit \u00eatre r\u00e9-authentifi\u00e9e manuellement\n\nVous pouvez trouver votre cl\u00e9 ici\u00a0: https://steamcommunity.com/dev/apikey", + "title": "R\u00e9-authentifier l'int\u00e9gration" + }, + "user": { + "data": { + "account": "ID de compte Steam", + "api_key": "Cl\u00e9 d'API" + }, + "description": "Utilisez https://steamid.io pour trouver l'ID de votre compte Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Noms des comptes \u00e0 surveiller" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/he.json b/homeassistant/components/steam_online/translations/he.json new file mode 100644 index 00000000000..85d9e59a96c --- /dev/null +++ b/homeassistant/components/steam_online/translations/he.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "reauth_confirm": { + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + }, + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/hu.json b/homeassistant/components/steam_online/translations/hu.json new file mode 100644 index 00000000000..9bd0976345e --- /dev/null +++ b/homeassistant/components/steam_online/translations/hu.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_account": "\u00c9rv\u00e9nytelen fi\u00f3kazonos\u00edt\u00f3", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "reauth_confirm": { + "description": "A Steam integr\u00e1ci\u00f3t manu\u00e1lisan \u00fajra kell hiteles\u00edteni .\n\nA kulcs itt tal\u00e1lhat\u00f3: https://steamcommunity.com/dev/apikey", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + }, + "user": { + "data": { + "account": "Steam fi\u00f3k azonos\u00edt\u00f3ja", + "api_key": "API kulcs" + }, + "description": "A https://steamid.io haszn\u00e1lat\u00e1val kaphat\u00f3 meg a Steam fi\u00f3kazonos\u00edt\u00f3" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "A nyomon k\u00f6vetend\u0151 fi\u00f3kok nevei" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/id.json b/homeassistant/components/steam_online/translations/id.json new file mode 100644 index 00000000000..07d708067fc --- /dev/null +++ b/homeassistant/components/steam_online/translations/id.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_account": "ID akun tidak valid", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "reauth_confirm": { + "description": "Integrasi Steam perlu diautentikasi ulang secara manual \n\n Anda dapat menemukan kunci Anda di sini: https://steamcommunity.com/dev/apikey", + "title": "Autentikasi Ulang Integrasi" + }, + "user": { + "data": { + "account": "ID akun Steam", + "api_key": "Kunci API" + }, + "description": "Gunakan https://steamid.io untuk menemukan ID akun Steam Anda" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Nama akun yang akan dipantau" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/it.json b/homeassistant/components/steam_online/translations/it.json new file mode 100644 index 00000000000..71036870961 --- /dev/null +++ b/homeassistant/components/steam_online/translations/it.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_account": "ID account non valido", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "reauth_confirm": { + "description": "L'integrazione di Steam richiede una nuova autenticazione manuale \n\nPuoi trovare la tua chiave qui: https://steamcommunity.com/dev/apikey", + "title": "Autentica nuovamente l'integrazione" + }, + "user": { + "data": { + "account": "ID dell'account Steam", + "api_key": "Chiave API" + }, + "description": "Usa https://steamid.io per trovare l'ID del tuo account Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Nomi degli account da monitorare" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json new file mode 100644 index 00000000000..20600463f6a --- /dev/null +++ b/homeassistant/components/steam_online/translations/nl.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_account": "Ongeldige account ID", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "reauth_confirm": { + "description": "De Steam-integratie moet handmatig opnieuw worden geauthenticeerd.\n\nU vind uw API-sleutel hier: https://steamcommunity.com/dev/apikey", + "title": "Verifieer de integratie opnieuw" + }, + "user": { + "data": { + "account": "Steam Account-ID", + "api_key": "API-sleutel" + }, + "description": "Gebruik https://steamid.io om je Steam Account-ID te vinden." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Namen van de accounts die gemonitord moeten worden" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/no.json b/homeassistant/components/steam_online/translations/no.json new file mode 100644 index 00000000000..7e0122dfe18 --- /dev/null +++ b/homeassistant/components/steam_online/translations/no.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_account": "Ugyldig konto-ID", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "reauth_confirm": { + "description": "Steam-integrasjonen m\u00e5 re-autentiseres manuelt \n\n Du finner n\u00f8kkelen din her: https://steamcommunity.com/dev/apikey", + "title": "Godkjenne integrering p\u00e5 nytt" + }, + "user": { + "data": { + "account": "Steam-konto-ID", + "api_key": "API-n\u00f8kkel" + }, + "description": "Bruk https://steamid.io for \u00e5 finne din Steam-konto-ID" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Navn p\u00e5 kontoer som skal overv\u00e5kes" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/pl.json b/homeassistant/components/steam_online/translations/pl.json new file mode 100644 index 00000000000..09c95bca9c4 --- /dev/null +++ b/homeassistant/components/steam_online/translations/pl.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_account": "Niepoprawny identyfikator konta", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "reauth_confirm": { + "description": "Integracja Steam musi zosta\u0107 ponownie uwierzytelniona r\u0119cznie\n\nSw\u00f3j klucz znajdziesz tutaj: https://steamcommunity.com/dev/apikey", + "title": "Ponownie uwierzytelnij integracj\u0119" + }, + "user": { + "data": { + "account": "Identyfikator konta Steam", + "api_key": "Klucz API" + }, + "description": "U\u017cyj https://steamid.io, aby znale\u017a\u0107 sw\u00f3j identyfikator konta Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Nazwy kont, kt\u00f3re maj\u0105 by\u0107 monitorowane" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/pt-BR.json b/homeassistant/components/steam_online/translations/pt-BR.json new file mode 100644 index 00000000000..93ff3e00dc5 --- /dev/null +++ b/homeassistant/components/steam_online/translations/pt-BR.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_account": "ID de conta inv\u00e1lido", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o da Steam precisa ser autenticada manualmente\n\nVoc\u00ea pode encontrar sua chave aqui: https://steamcommunity.com/dev/apikey", + "title": "Reautenticar Integra\u00e7\u00e3o" + }, + "user": { + "data": { + "account": "ID da conta Steam", + "api_key": "Chave da API" + }, + "description": "Use https://steamid.io para encontrar o ID da sua conta Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Nomes das contas a serem monitoradas" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/ru.json b/homeassistant/components/steam_online/translations/ru.json new file mode 100644 index 00000000000..8a6ea715455 --- /dev/null +++ b/homeassistant/components/steam_online/translations/ru.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_account": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "reauth_confirm": { + "description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0441\u0432\u043e\u0439 \u043a\u043b\u044e\u0447 \u0437\u0434\u0435\u0441\u044c: https://steamcommunity.com/dev/apikey", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, + "user": { + "data": { + "account": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Steam", + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "description": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 https://steamid.io, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0432\u043e\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Steam." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\u0418\u043c\u0435\u043d\u0430 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/zh-Hant.json b/homeassistant/components/steam_online/translations/zh-Hant.json new file mode 100644 index 00000000000..775c9710592 --- /dev/null +++ b/homeassistant/components/steam_online/translations/zh-Hant.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_account": "\u5e33\u865f ID \u7121\u6548", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "reauth_confirm": { + "description": "Steam \u6574\u5408\u9700\u8981\u624b\u52d5\u91cd\u65b0\u8a8d\u8b49\n\n\u53ef\u4ee5\u65bc\u5f8c\u65b9\u7db2\u5740\u627e\u5230\u91d1\u9470\uff1ahttps://steamcommunity.com/dev/apikey", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + }, + "user": { + "data": { + "account": "Steam \u5e33\u865f ID", + "api_key": "API \u91d1\u9470" + }, + "description": "\u4f7f\u7528 https://steamid.io \u4ee5\u78ba\u8a8d\u60a8\u7684 Steam \u5e33\u865f ID" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\u6240\u8981\u76e3\u63a7\u7684\u5e33\u865f\u540d\u7a31" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steamist/translations/hu.json b/homeassistant/components/steamist/translations/hu.json index 50cf36394cc..66448b03ab2 100644 --- a/homeassistant/components/steamist/translations/hu.json +++ b/homeassistant/components/steamist/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "not_steamist_device": "Nem egy seamist k\u00e9sz\u00fcl\u00e9k" diff --git a/homeassistant/components/subaru/translations/ca.json b/homeassistant/components/subaru/translations/ca.json index 6b83af06bb8..31ea78bcd46 100644 --- a/homeassistant/components/subaru/translations/ca.json +++ b/homeassistant/components/subaru/translations/ca.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "El PIN ha de tenir 4 d\u00edgits", + "bad_validation_code_format": "El codi de validaci\u00f3 ha de tenir 6 d\u00edgits", "cannot_connect": "Ha fallat la connexi\u00f3", "incorrect_pin": "PIN incorrecte", - "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + "incorrect_validation_code": "Codi de validaci\u00f3 inv\u00e0lid", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "two_factor_request_failed": "La sol\u00b7licitud de codi 2FA ha fallat. Torna-ho a provar" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Introdueix el teu PIN de MySubaru\nNOTA: tots els vehicles associats a un compte han de tenir el mateix PIN", "title": "Configuraci\u00f3 de Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Selecciona un m\u00e8tode de contacte:" + }, + "description": "Autenticaci\u00f3 de dos factors requerida", + "title": "Configuraci\u00f3 de Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "Codi de validaci\u00f3" + }, + "description": "Introdueix el codi de validaci\u00f3 rebut", + "title": "Configuraci\u00f3 de Subaru Starlink" + }, "user": { "data": { "country": "Selecciona un pa\u00eds", diff --git a/homeassistant/components/subaru/translations/de.json b/homeassistant/components/subaru/translations/de.json index 4f10a4266ae..2156df0d1a8 100644 --- a/homeassistant/components/subaru/translations/de.json +++ b/homeassistant/components/subaru/translations/de.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "Die PIN sollte 4-stellig sein", + "bad_validation_code_format": "Der Validierungscode sollte 6-stellig sein", "cannot_connect": "Verbindung fehlgeschlagen", "incorrect_pin": "Falsche PIN", - "invalid_auth": "Ung\u00fcltige Authentifizierung" + "incorrect_validation_code": "Falscher Validierungscode", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "two_factor_request_failed": "Anfrage f\u00fcr 2FA-Code fehlgeschlagen, bitte versuche es erneut" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Bitte gib deinen MySubaru-PIN ein\nHINWEIS: Alle Fahrzeuge im Konto m\u00fcssen dieselbe PIN haben", "title": "Subaru Starlink Konfiguration" }, + "two_factor": { + "data": { + "contact_method": "Bitte w\u00e4hle eine Kontaktmethode:" + }, + "description": "Zwei-Faktor-Authentifizierung erforderlich", + "title": "Subaru Starlink Konfiguration" + }, + "two_factor_validate": { + "data": { + "validation_code": "Validierungscode" + }, + "description": "Bitte gib den erhaltenen Validierungscode ein", + "title": "Subaru Starlink Konfiguration" + }, "user": { "data": { "country": "Land ausw\u00e4hlen", diff --git a/homeassistant/components/subaru/translations/el.json b/homeassistant/components/subaru/translations/el.json index 30ff37ccc1e..0376e95225b 100644 --- a/homeassistant/components/subaru/translations/el.json +++ b/homeassistant/components/subaru/translations/el.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "\u03a4\u03bf PIN \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 4 \u03c8\u03b7\u03c6\u03af\u03b1", + "bad_validation_code_format": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 6 \u03c8\u03b7\u03c6\u03af\u03c9\u03bd", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "incorrect_pin": "\u039b\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03bf PIN", - "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + "incorrect_validation_code": "\u0395\u03c3\u03c6\u03b1\u03bb\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "two_factor_request_failed": "\u03a4\u03bf \u03b1\u03af\u03c4\u03b7\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc 2FA \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf PIN \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf MySubaru\n\u03a3\u0397\u039c\u0395\u0399\u03a9\u03a3\u0397: \u038c\u03bb\u03b1 \u03c4\u03b1 \u03bf\u03c7\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c3\u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03c4\u03bf\u03bd \u03af\u03b4\u03b9\u03bf \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc PIN", "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03af\u03b1\u03c2:" + }, + "description": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd", + "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7\u03c2" + }, + "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03c0\u03b9\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03b5\u03bb\u03ae\u03c6\u03b8\u03b7", + "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Subaru Starlink" + }, "user": { "data": { "country": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c7\u03ce\u03c1\u03b1\u03c2", diff --git a/homeassistant/components/subaru/translations/en.json b/homeassistant/components/subaru/translations/en.json index 722363d4d74..ca2ad3c2c92 100644 --- a/homeassistant/components/subaru/translations/en.json +++ b/homeassistant/components/subaru/translations/en.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN should be 4 digits", + "bad_validation_code_format": "Validation code should be 6 digits", "cannot_connect": "Failed to connect", "incorrect_pin": "Incorrect PIN", - "invalid_auth": "Invalid authentication" + "incorrect_validation_code": "Incorrect validation code", + "invalid_auth": "Invalid authentication", + "two_factor_request_failed": "Request for 2FA code failed, please try again" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Please enter your MySubaru PIN\nNOTE: All vehicles in account must have the same PIN", "title": "Subaru Starlink Configuration" }, + "two_factor": { + "data": { + "contact_method": "Please select a contact method:" + }, + "description": "Two factor authentication required", + "title": "Subaru Starlink Configuration" + }, + "two_factor_validate": { + "data": { + "validation_code": "Validation code" + }, + "description": "Please enter validation code received", + "title": "Subaru Starlink Configuration" + }, "user": { "data": { "country": "Select country", diff --git a/homeassistant/components/subaru/translations/et.json b/homeassistant/components/subaru/translations/et.json index e7390b3b77f..acfa5055402 100644 --- a/homeassistant/components/subaru/translations/et.json +++ b/homeassistant/components/subaru/translations/et.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN-kood peaks olema 4-kohaline", + "bad_validation_code_format": "Kinnituskood peaks olema 6 kohaline", "cannot_connect": "\u00dchendamine nurjus", "incorrect_pin": "Vale PIN-kood", - "invalid_auth": "Vigane autentimine" + "incorrect_validation_code": "Vale kinnituskood", + "invalid_auth": "Vigane autentimine", + "two_factor_request_failed": "2FA koodi taotlus nurjus, proovi uuesti" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Sisesta oma MySubaru PIN-kood\n M\u00c4RKUS. K\u00f5igil kontol olevatel s\u00f5idukitel peab olema sama PIN-kood", "title": "Subaru Starlinki konfiguratsioon" }, + "two_factor": { + "data": { + "contact_method": "Vali kontaktimeetod" + }, + "description": "N\u00f5utav on kaheastmeline autentimine", + "title": "Subaru Starlink s\u00e4tted" + }, + "two_factor_validate": { + "data": { + "validation_code": "Kinnituskood" + }, + "description": "Sisesta saadud kinnituskood", + "title": "Subaru Starlink s\u00e4tted" + }, "user": { "data": { "country": "Vali riik", diff --git a/homeassistant/components/subaru/translations/fr.json b/homeassistant/components/subaru/translations/fr.json index fafcf81157b..a2b7159a6be 100644 --- a/homeassistant/components/subaru/translations/fr.json +++ b/homeassistant/components/subaru/translations/fr.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "Le code PIN doit \u00eatre compos\u00e9 de 4 chiffres", + "bad_validation_code_format": "Le code de validation doit \u00eatre compos\u00e9 de six chiffres", "cannot_connect": "\u00c9chec de connexion", "incorrect_pin": "PIN incorrect", - "invalid_auth": "Authentification non valide" + "incorrect_validation_code": "Code de validation incorrect", + "invalid_auth": "Authentification non valide", + "two_factor_request_failed": "La demande de code 2FA a \u00e9chou\u00e9, veuillez r\u00e9essayer" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Veuillez entrer votre NIP MySubaru\nREMARQUE : Tous les v\u00e9hicules en compte doivent avoir le m\u00eame NIP", "title": "Configuration de Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Veuillez s\u00e9lectionner une m\u00e9thode de contact\u00a0:" + }, + "description": "Authentification \u00e0 deux facteurs requise", + "title": "Configuration Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "Code de validation" + }, + "description": "Veuillez saisir le code de validation re\u00e7u", + "title": "Configuration Subaru Starlink" + }, "user": { "data": { "country": "Choisissez le pays", diff --git a/homeassistant/components/subaru/translations/hu.json b/homeassistant/components/subaru/translations/hu.json index a54ddd57c39..42f9f6bb2c9 100644 --- a/homeassistant/components/subaru/translations/hu.json +++ b/homeassistant/components/subaru/translations/hu.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "A PIN-nek 4 sz\u00e1mjegy\u0171nek kell lennie", + "bad_validation_code_format": "Az \u00e9rv\u00e9nyes\u00edt\u0151 k\u00f3dnak 6 sz\u00e1mjegyb\u0151l kell \u00e1llnia", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "incorrect_pin": "Helytelen PIN", - "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" + "incorrect_validation_code": "Helytelen \u00e9rv\u00e9nyes\u00edt\u00e9si k\u00f3d", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "two_factor_request_failed": "A k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3d lek\u00e9r\u00e9se sikertelen, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra" }, "step": { "pin": { @@ -18,9 +21,23 @@ "description": "K\u00e9rj\u00fck, adja meg MySubaru PIN-k\u00f3dj\u00e1t\n MEGJEGYZ\u00c9S: A sz\u00e1ml\u00e1n szerepl\u0151 \u00f6sszes j\u00e1rm\u0171nek azonos PIN-k\u00f3ddal kell rendelkeznie", "title": "Subaru Starlink konfigur\u00e1ci\u00f3" }, + "two_factor": { + "data": { + "contact_method": "V\u00e1lasszon kapcsolatfelv\u00e9teli m\u00f3dot:" + }, + "description": "K\u00e9tfaktoros hiteles\u00edt\u00e9s sz\u00fcks\u00e9ges", + "title": "Subaru Starlink konfigur\u00e1ci\u00f3" + }, + "two_factor_validate": { + "data": { + "validation_code": "\u00c9rv\u00e9nyes\u00edt\u00e9si k\u00f3d" + }, + "description": "K\u00e9rj\u00fck, adja meg az \u00e9rv\u00e9nyes\u00edt\u00e9si k\u00f3dot", + "title": "Subaru Starlink konfigur\u00e1ci\u00f3" + }, "user": { "data": { - "country": "V\u00e1lassz orsz\u00e1got", + "country": "V\u00e1lasszon orsz\u00e1got", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, diff --git a/homeassistant/components/subaru/translations/id.json b/homeassistant/components/subaru/translations/id.json index 2a44ec16b92..cf0d37a0c30 100644 --- a/homeassistant/components/subaru/translations/id.json +++ b/homeassistant/components/subaru/translations/id.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN harus terdiri dari 4 angka", + "bad_validation_code_format": "Kode validasi harus terdiri dari 6 angka", "cannot_connect": "Gagal terhubung", "incorrect_pin": "PIN salah", - "invalid_auth": "Autentikasi tidak valid" + "incorrect_validation_code": "Kode validasi salah", + "invalid_auth": "Autentikasi tidak valid", + "two_factor_request_failed": "Permintaan kode 2FA gagal, silakan coba lagi" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Masukkan PIN MySubaru Anda\nCATATAN: Semua kendaraan dalam akun harus memiliki PIN yang sama", "title": "Konfigurasi Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Pilih metode kontak:" + }, + "description": "Diperlukan autentikasi dua faktor", + "title": "Konfigurasi Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "Kode validasi" + }, + "description": "Masukkan kode validasi yang diterima", + "title": "Konfigurasi Subaru Starlink" + }, "user": { "data": { "country": "Pilih negara", diff --git a/homeassistant/components/subaru/translations/it.json b/homeassistant/components/subaru/translations/it.json index a6752b810eb..ea8399dbdc5 100644 --- a/homeassistant/components/subaru/translations/it.json +++ b/homeassistant/components/subaru/translations/it.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "Il PIN deve essere di 4 cifre", + "bad_validation_code_format": "Il codice di convalida deve essere di 6 cifre", "cannot_connect": "Impossibile connettersi", "incorrect_pin": "PIN errato", - "invalid_auth": "Autenticazione non valida" + "incorrect_validation_code": "Codice di convalida errato", + "invalid_auth": "Autenticazione non valida", + "two_factor_request_failed": "Richiesta di codice 2FA non riuscita, riprova" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Inserisci il tuo PIN MySubaru\nNOTA: tutti i veicoli nell'account devono avere lo stesso PIN", "title": "Configurazione Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Seleziona un metodo di contatto:" + }, + "description": "Autenticazione a due fattori richiesta", + "title": "Configurazione Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "Codice di validazione" + }, + "description": "Inserisci il codice di convalida ricevuto", + "title": "Configurazione Subaru Starlink" + }, "user": { "data": { "country": "Seleziona il paese", diff --git a/homeassistant/components/subaru/translations/ja.json b/homeassistant/components/subaru/translations/ja.json index b8949709464..8dcba4b4b23 100644 --- a/homeassistant/components/subaru/translations/ja.json +++ b/homeassistant/components/subaru/translations/ja.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN\u306f4\u6841\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "bad_validation_code_format": "\u691c\u8a3c\u30b3\u30fc\u30c9\u306f6\u6841\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "incorrect_pin": "PIN\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093", - "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + "incorrect_validation_code": "\u691c\u8a3c\u30b3\u30fc\u30c9\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "two_factor_request_failed": "2FA\u30b3\u30fc\u30c9\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "MySubaru PIN\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\n\u6ce8: \u30a2\u30ab\u30a6\u30f3\u30c8\u5185\u306e\u3059\u3079\u3066\u306e\u8eca\u4e21\u306f\u3001\u540c\u3058PIN\u3092\u6301\u3063\u3066\u3044\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "Subaru Starlink\u306e\u8a2d\u5b9a" }, + "two_factor": { + "data": { + "contact_method": "\u9023\u7d61\u65b9\u6cd5\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\uff1a" + }, + "description": "\u4e8c\u8981\u7d20\u8a8d\u8a3c\u304c\u5fc5\u8981", + "title": "Subaru Starlink\u306e\u8a2d\u5b9a" + }, + "two_factor_validate": { + "data": { + "validation_code": "\u691c\u8a3c\u30b3\u30fc\u30c9" + }, + "description": "\u53d7\u3051\u53d6\u3063\u305f\u691c\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", + "title": "Subaru Starlink\u306e\u8a2d\u5b9a" + }, "user": { "data": { "country": "\u56fd\u3092\u9078\u629e", diff --git a/homeassistant/components/subaru/translations/nl.json b/homeassistant/components/subaru/translations/nl.json index 931ed6f967f..256180d444b 100644 --- a/homeassistant/components/subaru/translations/nl.json +++ b/homeassistant/components/subaru/translations/nl.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "De pincode moet uit 4 cijfers bestaan", + "bad_validation_code_format": "De validatiecode moet uit 6 cijfers bestaan", "cannot_connect": "Kan geen verbinding maken", "incorrect_pin": "Onjuiste PIN", - "invalid_auth": "Ongeldige authenticatie" + "incorrect_validation_code": "Onjuiste validatiecode", + "invalid_auth": "Ongeldige authenticatie", + "two_factor_request_failed": "Verzoek om 2FA code mislukt, probeer opnieuw" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Voer uw MySubaru-pincode in\n OPMERKING: Alle voertuigen in een account moeten dezelfde pincode hebben", "title": "Subaru Starlink Configuratie" }, + "two_factor": { + "data": { + "contact_method": "Selecteer een contactmethode:" + }, + "description": "Tweefactorauthenticatie vereist", + "title": "Subaru Starlink-configuratie" + }, + "two_factor_validate": { + "data": { + "validation_code": "Validatiecode" + }, + "description": "Voer de ontvangen validatiecode in", + "title": "Subaru Starlink-configuratie" + }, "user": { "data": { "country": "Selecteer land", diff --git a/homeassistant/components/subaru/translations/no.json b/homeassistant/components/subaru/translations/no.json index 6ac200ec131..3e3a78949db 100644 --- a/homeassistant/components/subaru/translations/no.json +++ b/homeassistant/components/subaru/translations/no.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN-koden skal best\u00e5 av fire sifre", + "bad_validation_code_format": "Valideringskoden skal v\u00e6re p\u00e5 6 sifre", "cannot_connect": "Tilkobling mislyktes", "incorrect_pin": "Feil PIN", - "invalid_auth": "Ugyldig godkjenning" + "incorrect_validation_code": "Feil valideringskode", + "invalid_auth": "Ugyldig godkjenning", + "two_factor_request_failed": "Foresp\u00f8rsel om 2FA-kode mislyktes, pr\u00f8v igjen" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Vennligst skriv inn MySubaru PIN-koden\n MERKNAD: Alle kj\u00f8ret\u00f8yer som er kontoen m\u00e5 ha samme PIN-kode", "title": "Subaru Starlink-konfigurasjon" }, + "two_factor": { + "data": { + "contact_method": "Velg en kontaktmetode:" + }, + "description": "Tofaktorautentisering kreves", + "title": "Subaru Starlink-konfigurasjon" + }, + "two_factor_validate": { + "data": { + "validation_code": "Valideringskode" + }, + "description": "Vennligst skriv inn valideringskode mottatt", + "title": "Subaru Starlink-konfigurasjon" + }, "user": { "data": { "country": "Velg land", diff --git a/homeassistant/components/subaru/translations/pl.json b/homeassistant/components/subaru/translations/pl.json index b0d491d475e..679e5779507 100644 --- a/homeassistant/components/subaru/translations/pl.json +++ b/homeassistant/components/subaru/translations/pl.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN powinien sk\u0142ada\u0107 si\u0119 z 4 cyfr", + "bad_validation_code_format": "Kod weryfikacyjny powinien mie\u0107 6 cyfr", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "incorrect_pin": "Nieprawid\u0142owy PIN", - "invalid_auth": "Niepoprawne uwierzytelnienie" + "incorrect_validation_code": "Nieprawid\u0142owy kod weryfikacyjny", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "two_factor_request_failed": "\u017b\u0105danie kodu 2FA nie powiod\u0142o si\u0119, spr\u00f3buj ponownie" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Wprowad\u017a sw\u00f3j PIN dla MySubaru\nUWAGA: Wszystkie pojazdy na koncie musz\u0105 mie\u0107 ten sam kod PIN", "title": "Konfiguracja Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Prosz\u0119 wybra\u0107 spos\u00f3b kontaktu:" + }, + "description": "Wymagane uwierzytelnianie dwusk\u0142adnikowe", + "title": "Konfiguracja Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "Kod weryfikacyjny" + }, + "description": "Wprowad\u017a otrzymany kod weryfikacyjny", + "title": "Konfiguracja Subaru Starlink" + }, "user": { "data": { "country": "Wybierz kraj", diff --git a/homeassistant/components/subaru/translations/pt-BR.json b/homeassistant/components/subaru/translations/pt-BR.json index e88a9e883c0..e53a16fa33f 100644 --- a/homeassistant/components/subaru/translations/pt-BR.json +++ b/homeassistant/components/subaru/translations/pt-BR.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "O PIN deve ter 4 d\u00edgitos", + "bad_validation_code_format": "O c\u00f3digo de valida\u00e7\u00e3o deve ter 6 d\u00edgitos", "cannot_connect": "Falha ao conectar", "incorrect_pin": "PIN incorreto", - "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" + "incorrect_validation_code": "C\u00f3digo de valida\u00e7\u00e3o incorreto", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "two_factor_request_failed": "Falha na solicita\u00e7\u00e3o do c\u00f3digo 2FA, por favor tente novamente" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "Por favor, digite seu PIN MySubaru\n NOTA: Todos os ve\u00edculos em conta devem ter o mesmo PIN", "title": "Configura\u00e7\u00e3o do Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Selecione um m\u00e9todo de contato:" + }, + "description": "Autentica\u00e7\u00e3o de dois fatores necess\u00e1ria", + "title": "Configura\u00e7\u00e3o do Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "C\u00f3digo de valida\u00e7\u00e3o" + }, + "description": "Insira o c\u00f3digo de valida\u00e7\u00e3o recebido", + "title": "Configura\u00e7\u00e3o do Subaru Starlink" + }, "user": { "data": { "country": "Selecione o pa\u00eds", diff --git a/homeassistant/components/subaru/translations/ru.json b/homeassistant/components/subaru/translations/ru.json index 87f56f8ac8a..45eddd02bc1 100644 --- a/homeassistant/components/subaru/translations/ru.json +++ b/homeassistant/components/subaru/translations/ru.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN-\u043a\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 4 \u0446\u0438\u0444\u0440.", + "bad_validation_code_format": "\u041a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 6 \u0446\u0438\u0444\u0440.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "incorrect_pin": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 PIN-\u043a\u043e\u0434.", - "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." + "incorrect_validation_code": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "two_factor_request_failed": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043a\u043e\u0434 2FA. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443." }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 PIN-\u043a\u043e\u0434 MySubaru.\n\u0412\u0441\u0435 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0438 \u0432 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u043c\u0435\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 PIN-\u043a\u043e\u0434.", "title": "Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0432\u044f\u0437\u0438:" + }, + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", + "title": "Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "\u041a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f" + }, + "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f.", + "title": "Subaru Starlink" + }, "user": { "data": { "country": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0443", diff --git a/homeassistant/components/subaru/translations/tr.json b/homeassistant/components/subaru/translations/tr.json index 5724bd4f2c3..3b9f1acc60e 100644 --- a/homeassistant/components/subaru/translations/tr.json +++ b/homeassistant/components/subaru/translations/tr.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN 4 haneli olmal\u0131d\u0131r", + "bad_validation_code_format": "Do\u011frulama kodu 6 basamakl\u0131 olmal\u0131d\u0131r", "cannot_connect": "Ba\u011flanma hatas\u0131", "incorrect_pin": "Yanl\u0131\u015f PIN", - "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" + "incorrect_validation_code": "Yanl\u0131\u015f do\u011frulama kodu", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "two_factor_request_failed": "2FA kodu iste\u011fi ba\u015far\u0131s\u0131z oldu, l\u00fctfen tekrar deneyin" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "L\u00fctfen MySubaru PIN'inizi girin\n NOT: Hesaptaki t\u00fcm ara\u00e7lar ayn\u0131 PIN'e sahip olmal\u0131d\u0131r", "title": "Subaru Starlink Yap\u0131land\u0131rmas\u0131" }, + "two_factor": { + "data": { + "contact_method": "L\u00fctfen bir ileti\u015fim y\u00f6ntemi se\u00e7in:" + }, + "description": "\u0130ki fakt\u00f6rl\u00fc kimlik do\u011frulama gerekli", + "title": "Subaru Starlink Yap\u0131land\u0131rmas\u0131" + }, + "two_factor_validate": { + "data": { + "validation_code": "Do\u011frulama kodu" + }, + "description": "L\u00fctfen al\u0131nan do\u011frulama kodunu girin", + "title": "Subaru Starlink Yap\u0131land\u0131rmas\u0131" + }, "user": { "data": { "country": "\u00dclkeyi se\u00e7", diff --git a/homeassistant/components/subaru/translations/zh-Hant.json b/homeassistant/components/subaru/translations/zh-Hant.json index 6264f01791e..2e4a22423e0 100644 --- a/homeassistant/components/subaru/translations/zh-Hant.json +++ b/homeassistant/components/subaru/translations/zh-Hant.json @@ -6,9 +6,12 @@ }, "error": { "bad_pin_format": "PIN \u78bc\u61c9\u8a72\u70ba 4 \u4f4d\u6578\u5b57", + "bad_validation_code_format": "\u9a57\u8b49\u78bc\u5fc5\u9808\u5305\u542b 6 \u4f4d\u6578\u5b57", "cannot_connect": "\u9023\u7dda\u5931\u6557", "incorrect_pin": "PIN \u78bc\u932f\u8aa4", - "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" + "incorrect_validation_code": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "two_factor_request_failed": "\u6240\u9700\u96d9\u91cd\u8a8d\u8b49\u5931\u6557\uff0c\u8acb\u518d\u8a66\u4e00\u6b21" }, "step": { "pin": { @@ -18,6 +21,20 @@ "description": "\u8acb\u8f38\u5165 MySubaru PIN \u78bc\n\u6ce8\u610f\uff1a\u6240\u4ee5\u5e33\u865f\u5167\u8eca\u8f1b\u90fd\u5fc5\u9808\u4f7f\u7528\u76f8\u540c PIN \u78bc", "title": "Subaru Starlink \u8a2d\u5b9a" }, + "two_factor": { + "data": { + "contact_method": "\u8acb\u9078\u64c7\u806f\u7d61\u65b9\u5f0f\uff1a" + }, + "description": "\u9700\u8981\u96d9\u91cd\u8a8d\u8b49\u78bc", + "title": "Subaru Starlink \u8a2d\u5b9a" + }, + "two_factor_validate": { + "data": { + "validation_code": "\u9a57\u8b49\u78bc" + }, + "description": "\u8acb\u8f38\u5165\u6240\u6536\u5230\u7684\u9a57\u8b49\u78bc", + "title": "Subaru Starlink \u8a2d\u5b9a" + }, "user": { "data": { "country": "\u9078\u64c7\u570b\u5bb6", diff --git a/homeassistant/components/sun/translations/bg.json b/homeassistant/components/sun/translations/bg.json index 7b6c5241cd2..81ead95c95f 100644 --- a/homeassistant/components/sun/translations/bg.json +++ b/homeassistant/components/sun/translations/bg.json @@ -1,4 +1,9 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + }, "state": { "_": { "above_horizon": "\u041d\u0430\u0434 \u0445\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430", diff --git a/homeassistant/components/sun/translations/ca.json b/homeassistant/components/sun/translations/ca.json index 5a49afefc5d..00a37d8b2b3 100644 --- a/homeassistant/components/sun/translations/ca.json +++ b/homeassistant/components/sun/translations/ca.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + }, + "step": { + "user": { + "description": "Vols comen\u00e7ar la configuraci\u00f3?" + } + } + }, "state": { "_": { "above_horizon": "Sobre l'horitz\u00f3", diff --git a/homeassistant/components/sun/translations/cs.json b/homeassistant/components/sun/translations/cs.json index fe13d3e4fb0..b4a01f4473e 100644 --- a/homeassistant/components/sun/translations/cs.json +++ b/homeassistant/components/sun/translations/cs.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + }, + "step": { + "user": { + "description": "Chcete za\u010d\u00edt nastavovat?" + } + } + }, "state": { "_": { "above_horizon": "Nad obzorem", diff --git a/homeassistant/components/sun/translations/de.json b/homeassistant/components/sun/translations/de.json index 6b81cf14f76..405c9dac6d4 100644 --- a/homeassistant/components/sun/translations/de.json +++ b/homeassistant/components/sun/translations/de.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + }, + "step": { + "user": { + "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" + } + } + }, "state": { "_": { "above_horizon": "\u00dcber dem Horizont", diff --git a/homeassistant/components/sun/translations/el.json b/homeassistant/components/sun/translations/el.json index 5079c2476aa..fa3d87d574b 100644 --- a/homeassistant/components/sun/translations/el.json +++ b/homeassistant/components/sun/translations/el.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + }, + "step": { + "user": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" + } + } + }, "state": { "_": { "above_horizon": "\u03a0\u03ac\u03bd\u03c9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03bf\u03c1\u03af\u03b6\u03bf\u03bd\u03c4\u03b1", diff --git a/homeassistant/components/sun/translations/en.json b/homeassistant/components/sun/translations/en.json index 2278e262bb8..367fdab917c 100644 --- a/homeassistant/components/sun/translations/en.json +++ b/homeassistant/components/sun/translations/en.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible." + }, + "step": { + "user": { + "description": "Do you want to start set up?" + } + } + }, "state": { "_": { "above_horizon": "Above horizon", diff --git a/homeassistant/components/sun/translations/et.json b/homeassistant/components/sun/translations/et.json index 1a4020215f0..d2fe374ddfc 100644 --- a/homeassistant/components/sun/translations/et.json +++ b/homeassistant/components/sun/translations/et.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "step": { + "user": { + "description": "Kas soovid alustada seadistamist?" + } + } + }, "state": { "_": { "above_horizon": "T\u00f5usnud", diff --git a/homeassistant/components/sun/translations/fr.json b/homeassistant/components/sun/translations/fr.json index a878c8022b1..b743a33499d 100644 --- a/homeassistant/components/sun/translations/fr.json +++ b/homeassistant/components/sun/translations/fr.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + }, + "step": { + "user": { + "description": "Voulez-vous commencer la configuration\u00a0?" + } + } + }, "state": { "_": { "above_horizon": "Au-dessus de l'horizon", diff --git a/homeassistant/components/sun/translations/he.json b/homeassistant/components/sun/translations/he.json index 26a2def7d00..2c851de857e 100644 --- a/homeassistant/components/sun/translations/he.json +++ b/homeassistant/components/sun/translations/he.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + }, + "step": { + "user": { + "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" + } + } + }, "state": { "_": { "above_horizon": "\u05de\u05e2\u05dc \u05d4\u05d0\u05d5\u05e4\u05e7", diff --git a/homeassistant/components/sun/translations/hu.json b/homeassistant/components/sun/translations/hu.json index 2275d61b35a..402320f02f9 100644 --- a/homeassistant/components/sun/translations/hu.json +++ b/homeassistant/components/sun/translations/hu.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + }, + "step": { + "user": { + "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" + } + } + }, "state": { "_": { "above_horizon": "L\u00e1t\u00f3hat\u00e1r felett", diff --git a/homeassistant/components/sun/translations/id.json b/homeassistant/components/sun/translations/id.json index df6c960e67d..789a42556e9 100644 --- a/homeassistant/components/sun/translations/id.json +++ b/homeassistant/components/sun/translations/id.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + }, + "step": { + "user": { + "description": "Ingin memulai penyiapan?" + } + } + }, "state": { "_": { "above_horizon": "Terbit", diff --git a/homeassistant/components/sun/translations/it.json b/homeassistant/components/sun/translations/it.json index fe2c65461cd..48f0e4a8d90 100644 --- a/homeassistant/components/sun/translations/it.json +++ b/homeassistant/components/sun/translations/it.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, + "step": { + "user": { + "description": "Vuoi iniziare la configurazione?" + } + } + }, "state": { "_": { "above_horizon": "Sopra l'orizzonte", diff --git a/homeassistant/components/sun/translations/ja.json b/homeassistant/components/sun/translations/ja.json index 758beba9dbf..8188e950389 100644 --- a/homeassistant/components/sun/translations/ja.json +++ b/homeassistant/components/sun/translations/ja.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + }, + "step": { + "user": { + "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" + } + } + }, "state": { "_": { "above_horizon": "\u5730\u5e73\u7dda\u3088\u308a\u4e0a", diff --git a/homeassistant/components/sun/translations/nl.json b/homeassistant/components/sun/translations/nl.json index 6abe34481fa..8c284e4e43d 100644 --- a/homeassistant/components/sun/translations/nl.json +++ b/homeassistant/components/sun/translations/nl.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + }, + "step": { + "user": { + "description": "Wilt u beginnen met instellen?" + } + } + }, "state": { "_": { "above_horizon": "Boven de horizon", diff --git a/homeassistant/components/sun/translations/no.json b/homeassistant/components/sun/translations/no.json index 8597fea2ce5..18bb6e25545 100644 --- a/homeassistant/components/sun/translations/no.json +++ b/homeassistant/components/sun/translations/no.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + }, + "step": { + "user": { + "description": "Vil du starte oppsettet?" + } + } + }, "state": { "_": { "above_horizon": "Over horisonten", diff --git a/homeassistant/components/sun/translations/pl.json b/homeassistant/components/sun/translations/pl.json index 1f00babd1fd..7e95dd568c6 100644 --- a/homeassistant/components/sun/translations/pl.json +++ b/homeassistant/components/sun/translations/pl.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + }, + "step": { + "user": { + "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" + } + } + }, "state": { "_": { "above_horizon": "nad horyzontem", diff --git a/homeassistant/components/sun/translations/pt-BR.json b/homeassistant/components/sun/translations/pt-BR.json index 2f060112a0c..fe965b08372 100644 --- a/homeassistant/components/sun/translations/pt-BR.json +++ b/homeassistant/components/sun/translations/pt-BR.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + }, + "step": { + "user": { + "description": "Deseja iniciar a configura\u00e7\u00e3o?" + } + } + }, "state": { "_": { "above_horizon": "Acima do horizonte", diff --git a/homeassistant/components/sun/translations/ru.json b/homeassistant/components/sun/translations/ru.json index 7ddf3165aa9..2eca13058de 100644 --- a/homeassistant/components/sun/translations/ru.json +++ b/homeassistant/components/sun/translations/ru.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + }, + "step": { + "user": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" + } + } + }, "state": { "_": { "above_horizon": "\u041d\u0430\u0434 \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u043e\u043c", diff --git a/homeassistant/components/sun/translations/tr.json b/homeassistant/components/sun/translations/tr.json index 50634454b98..cff510523fa 100644 --- a/homeassistant/components/sun/translations/tr.json +++ b/homeassistant/components/sun/translations/tr.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "step": { + "user": { + "description": "Kuruluma ba\u015flamak ister misiniz?" + } + } + }, "state": { "_": { "above_horizon": "G\u00fcnd\u00fcz", diff --git a/homeassistant/components/sun/translations/zh-Hant.json b/homeassistant/components/sun/translations/zh-Hant.json index 4b8da898c70..e3b7cfb9c54 100644 --- a/homeassistant/components/sun/translations/zh-Hant.json +++ b/homeassistant/components/sun/translations/zh-Hant.json @@ -1,4 +1,14 @@ { + "config": { + "abort": { + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + }, + "step": { + "user": { + "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" + } + } + }, "state": { "_": { "above_horizon": "\u65e5\u51fa\u6771\u6d77", diff --git a/homeassistant/components/switch/translations/cs.json b/homeassistant/components/switch/translations/cs.json index fe398d9b72e..a7b7f35033e 100644 --- a/homeassistant/components/switch/translations/cs.json +++ b/homeassistant/components/switch/translations/cs.json @@ -1,4 +1,14 @@ { + "config": { + "step": { + "init": { + "data": { + "entity_id": "Entita vyp\u00edna\u010de" + }, + "description": "Vyberte vyp\u00edna\u010d pro sv\u011btlo." + } + } + }, "device_automation": { "action_type": { "toggle": "P\u0159epnout {entity_name}", diff --git a/homeassistant/components/switch/translations/el.json b/homeassistant/components/switch/translations/el.json index 2067276af42..986ee7cff92 100644 --- a/homeassistant/components/switch/translations/el.json +++ b/homeassistant/components/switch/translations/el.json @@ -28,8 +28,8 @@ }, "state": { "_": { - "off": "\u039a\u03bb\u03b5\u03b9\u03c3\u03c4\u03cc\u03c2", - "on": "\u0391\u03bd\u03bf\u03b9\u03c7\u03c4\u03cc\u03c2" + "off": "\u039a\u03bb\u03b5\u03b9\u03c3\u03c4\u03cc", + "on": "\u0391\u03bd\u03bf\u03b9\u03c7\u03c4\u03cc" } }, "title": "\u0394\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2" diff --git a/homeassistant/components/switch/translations/hu.json b/homeassistant/components/switch/translations/hu.json index 423a48f345d..2dff00040ca 100644 --- a/homeassistant/components/switch/translations/hu.json +++ b/homeassistant/components/switch/translations/hu.json @@ -20,6 +20,7 @@ "is_on": "{entity_name} be van kapcsolva" }, "trigger_type": { + "changed_states": "{entity_name} be- vagy kikapcsolt", "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" diff --git a/homeassistant/components/switch/translations/pt-BR.json b/homeassistant/components/switch/translations/pt-BR.json index 6f7f076332a..0d24cd74bfe 100644 --- a/homeassistant/components/switch/translations/pt-BR.json +++ b/homeassistant/components/switch/translations/pt-BR.json @@ -3,9 +3,9 @@ "step": { "init": { "data": { - "entity_id": "Entidade de switch" + "entity_id": "Entidade de interruptor" }, - "description": "Selecione o switch para o interruptor de luz." + "description": "Selecione o interruptor para a l\u00e2mpada." } } }, diff --git a/homeassistant/components/switch_as_x/translations/bg.json b/homeassistant/components/switch_as_x/translations/bg.json new file mode 100644 index 00000000000..f5c2a6e0433 --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "target_domain": "\u0422\u0438\u043f" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/ca.json b/homeassistant/components/switch_as_x/translations/ca.json index 07d6288d33e..f48a744066c 100644 --- a/homeassistant/components/switch_as_x/translations/ca.json +++ b/homeassistant/components/switch_as_x/translations/ca.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Entitat d'interruptor" + } + }, + "user": { "data": { - "entity_id": "Entitat d'interruptor", - "target_domain": "Tipus" + "entity_id": "Commutador", + "target_domain": "Nou tipus" }, - "title": "Converteix un interruptor en \u2026" + "description": "Tria un commutador que vulguis que aparegui a Home Assistant com a llum, coberta o qualsevol altra cosa. El commutador original s'amagar\u00e0.", + "title": "Canvia el tipus de dispositiu commutador" } } }, - "title": "Interruptor com a X" + "title": "Canvia el tipus de dispositiu d'un commutador" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/cs.json b/homeassistant/components/switch_as_x/translations/cs.json new file mode 100644 index 00000000000..11f8471a62a --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/cs.json @@ -0,0 +1,18 @@ +{ + "config": { + "step": { + "config": { + "user": { + "entity_id": "Entita vyp\u00edna\u010de" + } + }, + "user": { + "data": { + "target_domain": "Typ" + }, + "title": "Ud\u011blejte vyp\u00edna\u010dem ..." + } + } + }, + "title": "Zm\u011bna typy vyp\u00edna\u010de" +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/de.json b/homeassistant/components/switch_as_x/translations/de.json index 63a99ad40e2..1125f4f5a8f 100644 --- a/homeassistant/components/switch_as_x/translations/de.json +++ b/homeassistant/components/switch_as_x/translations/de.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Switch-Entit\u00e4t" + } + }, + "user": { "data": { - "entity_id": "Switch-Entit\u00e4t", - "target_domain": "Typ" + "entity_id": "Schalter", + "target_domain": "Neuer Typ" }, - "title": "Mache einen Schalter zu..." + "description": "W\u00e4hle einen Schalter, der im Home Assistant als Licht, Abdeckung oder sonstiges angezeigt werden soll. Der urspr\u00fcngliche Schalter wird ausgeblendet.", + "title": "Switch-Ger\u00e4tetyp \u00e4ndern" } } }, - "title": "Schalter als X" + "title": "Ger\u00e4tetyp eines Schalters \u00e4ndern" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/el.json b/homeassistant/components/switch_as_x/translations/el.json index 1fb546c13b7..ed10f241927 100644 --- a/homeassistant/components/switch_as_x/translations/el.json +++ b/homeassistant/components/switch_as_x/translations/el.json @@ -1,11 +1,17 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7" + } + }, + "user": { "data": { - "entity_id": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7", + "entity_id": "\u0394\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2", "target_domain": "\u03a4\u03cd\u03c0\u03bf\u03c2" }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf Home Assistant \u03c9\u03c2 \u03c6\u03c9\u03c2, \u03ba\u03ac\u03bb\u03c5\u03bc\u03bc\u03b1 \u03ae \u03bf\u03c4\u03b9\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03ac\u03bb\u03bb\u03bf. \u039f \u03b1\u03c1\u03c7\u03b9\u03ba\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2 \u03b8\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03c1\u03c5\u03bc\u03bc\u03ad\u03bd\u03bf\u03c2.", "title": "\u039a\u03ac\u03bd\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03ad\u03bd\u03b1 ..." } } diff --git a/homeassistant/components/switch_as_x/translations/en.json b/homeassistant/components/switch_as_x/translations/en.json index 7709a27cf35..4253f0506ef 100644 --- a/homeassistant/components/switch_as_x/translations/en.json +++ b/homeassistant/components/switch_as_x/translations/en.json @@ -1,12 +1,18 @@ { "config": { "step": { + "config": { + "user": { + "entity_id": "Switch entity" + } + }, "user": { "data": { "entity_id": "Switch", "target_domain": "New Type" }, - "description": "Pick a switch that you want to show up in Home Assistant as a light, cover or anything else. The original switch will be hidden." + "description": "Pick a switch that you want to show up in Home Assistant as a light, cover or anything else. The original switch will be hidden.", + "title": "Change switch device type" } } }, diff --git a/homeassistant/components/switch_as_x/translations/et.json b/homeassistant/components/switch_as_x/translations/et.json index 9e18ddced09..9d5a5839fbf 100644 --- a/homeassistant/components/switch_as_x/translations/et.json +++ b/homeassistant/components/switch_as_x/translations/et.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "L\u00fcliti olem" + } + }, + "user": { "data": { - "entity_id": "L\u00fcliti olem", - "target_domain": "T\u00fc\u00fcp" + "entity_id": "L\u00fcliti", + "target_domain": "Uus t\u00fc\u00fcp" }, - "title": "Tee l\u00fcliti ..." + "description": "Vali l\u00fcliti mida soovid Home Assistantis valgustina, avakattena v\u00f5i millegi muuna kuvada. Algne l\u00fcliti peidetakse.", + "title": "Muuda l\u00fclitusseadme t\u00fc\u00fcpi" } } }, - "title": "L\u00fclita kui X" + "title": "Muuda l\u00fcliti t\u00fc\u00fcpi" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/fr.json b/homeassistant/components/switch_as_x/translations/fr.json index 4b5bd6beebe..fa14bd80885 100644 --- a/homeassistant/components/switch_as_x/translations/fr.json +++ b/homeassistant/components/switch_as_x/translations/fr.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Entit\u00e9 du commutateur" + } + }, + "user": { "data": { - "entity_id": "Entit\u00e9 du commutateur", - "target_domain": "Type" + "entity_id": "Interrupteur", + "target_domain": "Nouveau type" }, - "title": "Transformer un commutateur en \u2026" + "description": "Choisissez un interrupteur que vous voulez faire appara\u00eetre dans Home Assistant comme une lumi\u00e8re, une fermeture ou autre. L'interrupteur original sera cach\u00e9.", + "title": "Modifier le type d'appareil de l'interrupteur" } } }, - "title": "Commutateur en tant que X" + "title": "Modifier le type d'appareil d'un commutateur" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/he.json b/homeassistant/components/switch_as_x/translations/he.json index 8ca833876fe..39889dab709 100644 --- a/homeassistant/components/switch_as_x/translations/he.json +++ b/homeassistant/components/switch_as_x/translations/he.json @@ -1,8 +1,8 @@ { "config": { "step": { - "init": { - "data": { + "config": { + "user": { "entity_id": "\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05d2" } } diff --git a/homeassistant/components/switch_as_x/translations/hu.json b/homeassistant/components/switch_as_x/translations/hu.json index b3ea0af39fd..0d919f5ecd3 100644 --- a/homeassistant/components/switch_as_x/translations/hu.json +++ b/homeassistant/components/switch_as_x/translations/hu.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Kapcsol\u00f3 entit\u00e1s" + } + }, + "user": { "data": { - "entity_id": "Kapcsol\u00f3 entit\u00e1s", - "target_domain": "T\u00edpus" + "entity_id": "Kapcsol\u00f3", + "target_domain": "\u00daj t\u00edpus" }, - "title": "Kapcsol\u00f3 mint..." + "description": "V\u00e1lassza ki azt a kapcsol\u00f3t, amelyet meg szeretne jelen\u00edteni a Home Assistantban l\u00e1mpak\u00e9nt, red\u0151nyk\u00e9nt vagy b\u00e1rmi m\u00e1sk\u00e9nt. Az eredeti kapcsol\u00f3 el lesz rejtve.", + "title": "Kapcsol\u00f3 eszk\u00f6zt\u00edpus m\u00f3dos\u00edt\u00e1sa" } } }, - "title": "Kapcsol\u00f3 mint X" + "title": "Kapcsol\u00f3 mint m\u00e1s eszk\u00f6z" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/id.json b/homeassistant/components/switch_as_x/translations/id.json index 1a5cce08f01..47d31e26c03 100644 --- a/homeassistant/components/switch_as_x/translations/id.json +++ b/homeassistant/components/switch_as_x/translations/id.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Entitas saklar" + } + }, + "user": { "data": { - "entity_id": "Entitas saklar", - "target_domain": "Jenis" + "entity_id": "Sakelar", + "target_domain": "Tipe Baru" }, - "title": "Jadikan saklar sebagai\u2026" + "description": "Pilih sakelar yang ingin Anda tampilkan di Home Assistant sebagai lampu, penutup, atau apa pun. Sakelar asli akan disembunyikan.", + "title": "Ubah jenis perangkat sakelar" } } }, - "title": "Saklar sebagai X" + "title": "Ubah jenis perangkat sakelar" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/it.json b/homeassistant/components/switch_as_x/translations/it.json index 1ef154b0578..4706b4d9ece 100644 --- a/homeassistant/components/switch_as_x/translations/it.json +++ b/homeassistant/components/switch_as_x/translations/it.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Cambia entit\u00e0" + } + }, + "user": { "data": { - "entity_id": "Cambia entit\u00e0", - "target_domain": "Tipo" + "entity_id": "Interruttore", + "target_domain": "Nuovo tipo" }, - "title": "Rendi un interruttore un..." + "description": "Scegli un interruttore che vuoi mostrare in Home Assistant come luce, copertura o qualsiasi altra cosa. L'interruttore originale sar\u00e0 nascosto.", + "title": "Cambia tipo di dispositivo dell'interruttore" } } }, - "title": "Interruttore come X" + "title": "Cambia il tipo di dispositivo di un interruttore" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/ja.json b/homeassistant/components/switch_as_x/translations/ja.json index 44ceaecdd75..434e1b588d5 100644 --- a/homeassistant/components/switch_as_x/translations/ja.json +++ b/homeassistant/components/switch_as_x/translations/ja.json @@ -1,11 +1,17 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "\u30b9\u30a4\u30c3\u30c1\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3" + } + }, + "user": { "data": { - "entity_id": "\u30b9\u30a4\u30c3\u30c1\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", + "entity_id": "\u30b9\u30a4\u30c3\u30c1", "target_domain": "\u30bf\u30a4\u30d7" }, + "description": "Home Assistant\u306b\u3001\u30e9\u30a4\u30c8\u3084\u30ab\u30d0\u30fc\u306a\u3069\u306e\u8868\u793a\u3055\u305b\u305f\u3044\u30b9\u30a4\u30c3\u30c1\u3092\u9078\u3073\u307e\u3059\u3002\u5143\u306e\u30b9\u30a4\u30c3\u30c1\u306f\u975e\u8868\u793a\u306b\u306a\u308a\u307e\u3059\u3002", "title": "\u30b9\u30a4\u30c3\u30c1\u3092..." } } diff --git a/homeassistant/components/switch_as_x/translations/nl.json b/homeassistant/components/switch_as_x/translations/nl.json index b1712904a76..4fcea818b9a 100644 --- a/homeassistant/components/switch_as_x/translations/nl.json +++ b/homeassistant/components/switch_as_x/translations/nl.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Entiteit wijzigen" + } + }, + "user": { "data": { - "entity_id": "Entiteit wijzigen", - "target_domain": "Type" + "entity_id": "Schakelaar", + "target_domain": "Nieuw type" }, - "title": "Schakel een..." + "description": "Kies een schakelaar die u in Home Assistant wilt laten verschijnen als licht, klep of iets anders. De oorspronkelijke schakelaar wordt verborgen.", + "title": "Type schakelapparaat wijzigen" } } }, - "title": "Schakelen als X" + "title": "Apparaattype van een schakelaar wijzigen" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/no.json b/homeassistant/components/switch_as_x/translations/no.json index 09d8d4e813e..5958c87f201 100644 --- a/homeassistant/components/switch_as_x/translations/no.json +++ b/homeassistant/components/switch_as_x/translations/no.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Bytt enhet" + } + }, + "user": { "data": { - "entity_id": "Bytt enhet", - "target_domain": "Type" + "entity_id": "Bryter", + "target_domain": "Ny type" }, - "title": "Gj\u00f8r en bryter til en ..." + "description": "Velg en bryter du vil vise i Home Assistant som lys, deksel eller noe annet. Den opprinnelige bryteren vil v\u00e6re skjult.", + "title": "Endre bryterenhetstype" } } }, - "title": "Bryter som X" + "title": "Endre enhetstype for en bryter" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/pl.json b/homeassistant/components/switch_as_x/translations/pl.json index c3f9af2601d..7491ef0d7fc 100644 --- a/homeassistant/components/switch_as_x/translations/pl.json +++ b/homeassistant/components/switch_as_x/translations/pl.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Encja prze\u0142\u0105cznika" + } + }, + "user": { "data": { - "entity_id": "Encja prze\u0142\u0105cznika", - "target_domain": "Rodzaj" + "entity_id": "Prze\u0142\u0105cznik", + "target_domain": "Nowy rodzaj" }, - "title": "Zmie\u0144 prze\u0142\u0105cznik na ..." + "description": "Wybierz prze\u0142\u0105cznik, kt\u00f3ry chcesz pokaza\u0107 w Home Assistant jako \u015bwiat\u0142o, rolet\u0119 lub cokolwiek innego. Oryginalny prze\u0142\u0105cznik zostanie ukryty.", + "title": "Zmiana typu urz\u0105dzenia prze\u0142\u0105czaj\u0105cego" } } }, - "title": "Prze\u0142\u0105cznik jako \"X\"" + "title": "Zmiana typu urz\u0105dzenia w prze\u0142\u0105czniku" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/pt-BR.json b/homeassistant/components/switch_as_x/translations/pt-BR.json index e8ccabe7841..bf8bc276781 100644 --- a/homeassistant/components/switch_as_x/translations/pt-BR.json +++ b/homeassistant/components/switch_as_x/translations/pt-BR.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Entidade de interruptor" + } + }, + "user": { "data": { - "entity_id": "Entidade de interruptor", - "target_domain": "Tipo" + "entity_id": "Interruptor", + "target_domain": "Novo tipo" }, - "title": "Fa\u00e7a um interruptor um ..." + "description": "Escolha um interruptor que voc\u00ea deseja que apare\u00e7a no Home Assistant como luz, cortina ou qualquer outra coisa. A op\u00e7\u00e3o original ficar\u00e1 oculta.", + "title": "Alterar o tipo de dispositivo do interruptor" } } }, - "title": "Switch as X" + "title": "Alterar o tipo de dispositivo de um interruptor" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/pt.json b/homeassistant/components/switch_as_x/translations/pt.json index 558030dc42d..70f238e0a08 100644 --- a/homeassistant/components/switch_as_x/translations/pt.json +++ b/homeassistant/components/switch_as_x/translations/pt.json @@ -1,7 +1,7 @@ { "config": { "step": { - "init": { + "user": { "data": { "target_domain": "Tipo" } diff --git a/homeassistant/components/switch_as_x/translations/ru.json b/homeassistant/components/switch_as_x/translations/ru.json index b4136768f03..3074365dd76 100644 --- a/homeassistant/components/switch_as_x/translations/ru.json +++ b/homeassistant/components/switch_as_x/translations/ru.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c" + } + }, + "user": { "data": { "entity_id": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c", - "target_domain": "\u0422\u0438\u043f" + "target_domain": "\u041d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f" }, - "title": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u043a\u0430\u043a \u2026" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432 Home Assistant \u043a\u0430\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435, \u0448\u0442\u043e\u0440\u044b \u0438\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0451. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0440\u044b\u0442.", + "title": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u0438\u043f \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044f" } } }, - "title": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u043a\u0430\u043a \u2026" + "title": "\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044f" } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/sv.json b/homeassistant/components/switch_as_x/translations/sv.json index 835de2f5a7f..96419c39bf1 100644 --- a/homeassistant/components/switch_as_x/translations/sv.json +++ b/homeassistant/components/switch_as_x/translations/sv.json @@ -1,9 +1,13 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "Kontakt-entitet" + } + }, + "user": { "data": { - "entity_id": "Kontakt-entitet", "target_domain": "Typ" }, "title": "G\u00f6r en kontakt till ..." diff --git a/homeassistant/components/switch_as_x/translations/tr.json b/homeassistant/components/switch_as_x/translations/tr.json new file mode 100644 index 00000000000..b793be6baf0 --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/tr.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "config": { + "user": { + "entity_id": "Varl\u0131\u011f\u0131 de\u011fi\u015ftir" + } + }, + "user": { + "data": { + "entity_id": "Anahtar", + "target_domain": "Yeni T\u00fcr" + }, + "description": "Home Assistant'ta \u0131\u015f\u0131k, \u00f6rt\u00fc veya ba\u015fka bir \u015fey olarak g\u00f6r\u00fcnmesini istedi\u011finiz bir anahtar se\u00e7in. Orijinal anahtar gizlenecektir.", + "title": "Anahtar cihaz t\u00fcr\u00fcn\u00fc de\u011fi\u015ftir" + } + } + }, + "title": "Bir anahtar\u0131n cihaz t\u00fcr\u00fcn\u00fc de\u011fi\u015ftirme" +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/zh-Hans.json b/homeassistant/components/switch_as_x/translations/zh-Hans.json new file mode 100644 index 00000000000..e765a436849 --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/zh-Hans.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "config": { + "user": { + "entity_id": "\u5f00\u5173\u5b9e\u4f53" + } + }, + "user": { + "data": { + "entity_id": "\u5f00\u5173", + "target_domain": "\u65b0\u7c7b\u578b" + }, + "description": "\u9009\u62e9\u4e00\u4e2a\u5f00\u5173\uff0c\u8ba9\u5b83\u5728 Home Assistant \u4e2d\u663e\u793a\u4e3a\u706f\u3001\u5377\u5e18\u7b49\u5404\u79cd\u7c7b\u578b\u3002\u539f\u6765\u7684\u5f00\u5173\u5c06\u88ab\u9690\u85cf\u3002", + "title": "\u66f4\u6539\u5f00\u5173\u7684\u8bbe\u5907\u7c7b\u578b" + } + } + }, + "title": "\u66f4\u6539\u5f00\u5173\u7684\u8bbe\u5907\u7c7b\u578b" +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/zh-Hant.json b/homeassistant/components/switch_as_x/translations/zh-Hant.json index 231f5b58eff..bd6a1e15ba0 100644 --- a/homeassistant/components/switch_as_x/translations/zh-Hant.json +++ b/homeassistant/components/switch_as_x/translations/zh-Hant.json @@ -1,14 +1,20 @@ { "config": { "step": { - "init": { + "config": { + "user": { + "entity_id": "\u958b\u95dc\u5be6\u9ad4" + } + }, + "user": { "data": { - "entity_id": "\u958b\u95dc\u5be6\u9ad4", - "target_domain": "\u985e\u5225" + "entity_id": "\u958b\u95dc", + "target_domain": "\u65b0\u589e\u985e\u5225" }, - "title": "\u5c07\u958b\u95dc\u8a2d\u5b9a\u70ba ..." + "description": "\u9078\u64c7\u6240\u8981\u65bc Home Assistant \u4e2d\u986f\u793a\u70ba\u71c8\u5149\u7684\u958b\u95dc\u3001\u7a97\u7c3e\u6216\u5176\u4ed6\u5be6\u9ad4\u3002\u539f\u59cb\u958b\u95dc\u5c07\u6703\u9032\u884c\u96b1\u85cf\u3002", + "title": "\u8b8a\u66f4\u958b\u95dc\u985e\u5225" } } }, - "title": "\u958b\u95dc\u8a2d\u70ba X" + "title": "\u8b8a\u66f4\u958b\u95dc\u985e\u5225" } \ No newline at end of file diff --git a/homeassistant/components/switchbot/translations/fr.json b/homeassistant/components/switchbot/translations/fr.json index 41b52f253ac..aca51b4e4c4 100644 --- a/homeassistant/components/switchbot/translations/fr.json +++ b/homeassistant/components/switchbot/translations/fr.json @@ -29,7 +29,7 @@ "retry_count": "Nombre de nouvelles tentatives", "retry_timeout": "D\u00e9lai d'attente entre les tentatives", "scan_timeout": "Dur\u00e9e de la recherche de donn\u00e9es publicitaires", - "update_time": "Dur\u00e9e (en secondes) entre deux mises \u00e0 jour" + "update_time": "Intervalle de temps entre deux mises \u00e0 jour (en secondes)" } } } diff --git a/homeassistant/components/switchbot/translations/hu.json b/homeassistant/components/switchbot/translations/hu.json index 5af1acb5d35..2b80fbedbd8 100644 --- a/homeassistant/components/switchbot/translations/hu.json +++ b/homeassistant/components/switchbot/translations/hu.json @@ -17,7 +17,7 @@ "user": { "data": { "mac": "Eszk\u00f6z MAC-c\u00edme", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3" }, "title": "Switchbot eszk\u00f6z be\u00e1ll\u00edt\u00e1sa" diff --git a/homeassistant/components/switchbot/translations/it.json b/homeassistant/components/switchbot/translations/it.json index 68071ec94e5..f589046d4db 100644 --- a/homeassistant/components/switchbot/translations/it.json +++ b/homeassistant/components/switchbot/translations/it.json @@ -9,8 +9,8 @@ }, "error": { "cannot_connect": "Impossibile connettersi", - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/syncthing/translations/ca.json b/homeassistant/components/syncthing/translations/ca.json index a10b5d8c134..e6a1625159d 100644 --- a/homeassistant/components/syncthing/translations/ca.json +++ b/homeassistant/components/syncthing/translations/ca.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/de.json b/homeassistant/components/syncthing/translations/de.json index 06db3e89b97..a753ae08cd9 100644 --- a/homeassistant/components/syncthing/translations/de.json +++ b/homeassistant/components/syncthing/translations/de.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/el.json b/homeassistant/components/syncthing/translations/el.json index 7d8c1e635df..d31063f30d3 100644 --- a/homeassistant/components/syncthing/translations/el.json +++ b/homeassistant/components/syncthing/translations/el.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/en.json b/homeassistant/components/syncthing/translations/en.json index 68efde737f2..e25581817a9 100644 --- a/homeassistant/components/syncthing/translations/en.json +++ b/homeassistant/components/syncthing/translations/en.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/es.json b/homeassistant/components/syncthing/translations/es.json index 550c1874010..e39a1436c2a 100644 --- a/homeassistant/components/syncthing/translations/es.json +++ b/homeassistant/components/syncthing/translations/es.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/et.json b/homeassistant/components/syncthing/translations/et.json index 12922ad3f6d..06addc30e2f 100644 --- a/homeassistant/components/syncthing/translations/et.json +++ b/homeassistant/components/syncthing/translations/et.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/fr.json b/homeassistant/components/syncthing/translations/fr.json index f0d93986445..75589f10740 100644 --- a/homeassistant/components/syncthing/translations/fr.json +++ b/homeassistant/components/syncthing/translations/fr.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Synchroniser" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/he.json b/homeassistant/components/syncthing/translations/he.json index 7e87dacd7e5..5df905492f0 100644 --- a/homeassistant/components/syncthing/translations/he.json +++ b/homeassistant/components/syncthing/translations/he.json @@ -16,6 +16,5 @@ } } } - }, - "title": "\u05e1\u05d9\u05e0\u05db\u05e8\u05d5\u05df" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/hu.json b/homeassistant/components/syncthing/translations/hu.json index 59ed4021b3f..90aca8becea 100644 --- a/homeassistant/components/syncthing/translations/hu.json +++ b/homeassistant/components/syncthing/translations/hu.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Szinkroniz\u00e1l\u00e1s" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/id.json b/homeassistant/components/syncthing/translations/id.json index d8f2a76c41d..db83504f4af 100644 --- a/homeassistant/components/syncthing/translations/id.json +++ b/homeassistant/components/syncthing/translations/id.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/it.json b/homeassistant/components/syncthing/translations/it.json index 0973a3ccf92..061be57e295 100644 --- a/homeassistant/components/syncthing/translations/it.json +++ b/homeassistant/components/syncthing/translations/it.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/ja.json b/homeassistant/components/syncthing/translations/ja.json index 482087ed730..2a725cbf3cc 100644 --- a/homeassistant/components/syncthing/translations/ja.json +++ b/homeassistant/components/syncthing/translations/ja.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/nl.json b/homeassistant/components/syncthing/translations/nl.json index 358490c6c83..4f66222ada8 100644 --- a/homeassistant/components/syncthing/translations/nl.json +++ b/homeassistant/components/syncthing/translations/nl.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/no.json b/homeassistant/components/syncthing/translations/no.json index 9fdb9f5f305..00a4de27e7f 100644 --- a/homeassistant/components/syncthing/translations/no.json +++ b/homeassistant/components/syncthing/translations/no.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Synkronisering" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/pl.json b/homeassistant/components/syncthing/translations/pl.json index e4e2619f1eb..367b1522930 100644 --- a/homeassistant/components/syncthing/translations/pl.json +++ b/homeassistant/components/syncthing/translations/pl.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/pt-BR.json b/homeassistant/components/syncthing/translations/pt-BR.json index 08f66569d93..d7f246e1aa6 100644 --- a/homeassistant/components/syncthing/translations/pt-BR.json +++ b/homeassistant/components/syncthing/translations/pt-BR.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Sincroniza\u00e7\u00e3o" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/ru.json b/homeassistant/components/syncthing/translations/ru.json index 337b0214c6b..60897aade50 100644 --- a/homeassistant/components/syncthing/translations/ru.json +++ b/homeassistant/components/syncthing/translations/ru.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/sv.json b/homeassistant/components/syncthing/translations/sv.json index 7862919ad54..9be5ea00256 100644 --- a/homeassistant/components/syncthing/translations/sv.json +++ b/homeassistant/components/syncthing/translations/sv.json @@ -9,6 +9,5 @@ } } } - }, - "title": "Synkronisering" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/tr.json b/homeassistant/components/syncthing/translations/tr.json index eeeb7e02c3c..e490274eb25 100644 --- a/homeassistant/components/syncthing/translations/tr.json +++ b/homeassistant/components/syncthing/translations/tr.json @@ -17,6 +17,5 @@ } } } - }, - "title": "E\u015fitleme" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/zh-Hans.json b/homeassistant/components/syncthing/translations/zh-Hans.json index 87d3db5c83f..96af1e51799 100644 --- a/homeassistant/components/syncthing/translations/zh-Hans.json +++ b/homeassistant/components/syncthing/translations/zh-Hans.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthing/translations/zh-Hant.json b/homeassistant/components/syncthing/translations/zh-Hant.json index f06c16ac157..6b6bc948c5f 100644 --- a/homeassistant/components/syncthing/translations/zh-Hant.json +++ b/homeassistant/components/syncthing/translations/zh-Hant.json @@ -17,6 +17,5 @@ } } } - }, - "title": "Syncthing" + } } \ No newline at end of file diff --git a/homeassistant/components/syncthru/translations/hu.json b/homeassistant/components/syncthru/translations/hu.json index a5b645200db..3de6022dcb6 100644 --- a/homeassistant/components/syncthru/translations/hu.json +++ b/homeassistant/components/syncthru/translations/hu.json @@ -12,13 +12,13 @@ "step": { "confirm": { "data": { - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "url": "Webes fel\u00fclet URL-je" } }, "user": { "data": { - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "url": "Webes fel\u00fclet URL-je" } } diff --git a/homeassistant/components/system_bridge/translations/ca.json b/homeassistant/components/system_bridge/translations/ca.json index aac2e139db0..b17295d59e0 100644 --- a/homeassistant/components/system_bridge/translations/ca.json +++ b/homeassistant/components/system_bridge/translations/ca.json @@ -27,6 +27,5 @@ "description": "Introdueix les dades de connexi\u00f3." } } - }, - "title": "Enlla\u00e7 de sistema" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/de.json b/homeassistant/components/system_bridge/translations/de.json index dc85589dd32..0ea5a0abd3a 100644 --- a/homeassistant/components/system_bridge/translations/de.json +++ b/homeassistant/components/system_bridge/translations/de.json @@ -27,6 +27,5 @@ "description": "Bitte gib Verbindungsdaten ein." } } - }, - "title": "System-Br\u00fccke" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/el.json b/homeassistant/components/system_bridge/translations/el.json index 5576af325de..cc15d636f72 100644 --- a/homeassistant/components/system_bridge/translations/el.json +++ b/homeassistant/components/system_bridge/translations/el.json @@ -27,6 +27,5 @@ "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2." } } - }, - "title": "\u0393\u03ad\u03c6\u03c5\u03c1\u03b1 \u03c3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/en.json b/homeassistant/components/system_bridge/translations/en.json index 3ebcfc6959e..dc94dfe2ac6 100644 --- a/homeassistant/components/system_bridge/translations/en.json +++ b/homeassistant/components/system_bridge/translations/en.json @@ -27,6 +27,5 @@ "description": "Please enter your connection details." } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/es.json b/homeassistant/components/system_bridge/translations/es.json index 2b7a859fcd1..d7fba5e7c32 100644 --- a/homeassistant/components/system_bridge/translations/es.json +++ b/homeassistant/components/system_bridge/translations/es.json @@ -27,6 +27,5 @@ "description": "Por favor, introduce tus datos de conexi\u00f3n." } } - }, - "title": "Pasarela del sistema" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/et.json b/homeassistant/components/system_bridge/translations/et.json index 3724895f147..69f32524c3b 100644 --- a/homeassistant/components/system_bridge/translations/et.json +++ b/homeassistant/components/system_bridge/translations/et.json @@ -27,6 +27,5 @@ "description": "Sisesta oma \u00fchenduse \u00fcksikasjad." } } - }, - "title": "S\u00fcsteemi sild" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/fr.json b/homeassistant/components/system_bridge/translations/fr.json index cbcad2d0330..e04dca1b751 100644 --- a/homeassistant/components/system_bridge/translations/fr.json +++ b/homeassistant/components/system_bridge/translations/fr.json @@ -27,6 +27,5 @@ "description": "Veuillez saisir vos informations de connexion." } } - }, - "title": "Pont syst\u00e8me" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/he.json b/homeassistant/components/system_bridge/translations/he.json index 5eb235ffb8f..ed758978398 100644 --- a/homeassistant/components/system_bridge/translations/he.json +++ b/homeassistant/components/system_bridge/translations/he.json @@ -27,6 +27,5 @@ "description": "\u05e0\u05d0 \u05d4\u05d6\u05df \u05d0\u05ea \u05e4\u05e8\u05d8\u05d9 \u05d4\u05d7\u05d9\u05d1\u05d5\u05e8 \u05e9\u05dc\u05da." } } - }, - "title": "\u05d2\u05e9\u05e8 \u05d4\u05de\u05e2\u05e8\u05db\u05ea" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/hu.json b/homeassistant/components/system_bridge/translations/hu.json index 8fdacd8718f..c570cd2e3c2 100644 --- a/homeassistant/components/system_bridge/translations/hu.json +++ b/homeassistant/components/system_bridge/translations/hu.json @@ -27,6 +27,5 @@ "description": "K\u00e9rj\u00fck, adja meg kapcsolati adatait." } } - }, - "title": "Rendszer h\u00edd" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/id.json b/homeassistant/components/system_bridge/translations/id.json index 9995253cbca..7cc07ebb2e1 100644 --- a/homeassistant/components/system_bridge/translations/id.json +++ b/homeassistant/components/system_bridge/translations/id.json @@ -27,6 +27,5 @@ "description": "Masukkan detail koneksi Anda." } } - }, - "title": "Jembatan Sistem" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/it.json b/homeassistant/components/system_bridge/translations/it.json index 2b6885edc44..7257d92ed7a 100644 --- a/homeassistant/components/system_bridge/translations/it.json +++ b/homeassistant/components/system_bridge/translations/it.json @@ -27,6 +27,5 @@ "description": "Inserisci i dettagli della tua connessione." } } - }, - "title": "Bridge di sistema" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/ja.json b/homeassistant/components/system_bridge/translations/ja.json index 48ee7fc2378..8358130359f 100644 --- a/homeassistant/components/system_bridge/translations/ja.json +++ b/homeassistant/components/system_bridge/translations/ja.json @@ -27,6 +27,5 @@ "description": "\u63a5\u7d9a\u306e\u8a73\u7d30\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } - }, - "title": "\u30b7\u30b9\u30c6\u30e0\u30d6\u30ea\u30c3\u30b8" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/nl.json b/homeassistant/components/system_bridge/translations/nl.json index eefadebda35..c419123e50d 100644 --- a/homeassistant/components/system_bridge/translations/nl.json +++ b/homeassistant/components/system_bridge/translations/nl.json @@ -27,6 +27,5 @@ "description": "Voer uw verbindingsgegevens in." } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/no.json b/homeassistant/components/system_bridge/translations/no.json index 5651dd7cb67..22ab7f91a57 100644 --- a/homeassistant/components/system_bridge/translations/no.json +++ b/homeassistant/components/system_bridge/translations/no.json @@ -27,6 +27,5 @@ "description": "Vennligst skriv inn tilkoblingsdetaljene dine." } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/pl.json b/homeassistant/components/system_bridge/translations/pl.json index 0ae579842d6..6c3a1561b5d 100644 --- a/homeassistant/components/system_bridge/translations/pl.json +++ b/homeassistant/components/system_bridge/translations/pl.json @@ -27,6 +27,5 @@ "description": "Wprowad\u017a dane po\u0142\u0105czenia." } } - }, - "title": "Mostek System" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/pt-BR.json b/homeassistant/components/system_bridge/translations/pt-BR.json index ed1bcd01ded..6b86e19ab35 100644 --- a/homeassistant/components/system_bridge/translations/pt-BR.json +++ b/homeassistant/components/system_bridge/translations/pt-BR.json @@ -27,6 +27,5 @@ "description": "Por favor, insira os detalhes da sua conex\u00e3o." } } - }, - "title": "Ponte do sistema" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/ru.json b/homeassistant/components/system_bridge/translations/ru.json index be2cddfdc69..c18bd977413 100644 --- a/homeassistant/components/system_bridge/translations/ru.json +++ b/homeassistant/components/system_bridge/translations/ru.json @@ -27,6 +27,5 @@ "description": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f." } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/tr.json b/homeassistant/components/system_bridge/translations/tr.json index 17e43680cca..15259c306a8 100644 --- a/homeassistant/components/system_bridge/translations/tr.json +++ b/homeassistant/components/system_bridge/translations/tr.json @@ -27,6 +27,5 @@ "description": "L\u00fctfen ba\u011flant\u0131 bilgilerinizi giriniz." } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/zh-Hant.json b/homeassistant/components/system_bridge/translations/zh-Hant.json index 5f45cc5dfcd..cca10d161b1 100644 --- a/homeassistant/components/system_bridge/translations/zh-Hant.json +++ b/homeassistant/components/system_bridge/translations/zh-Hant.json @@ -27,6 +27,5 @@ "description": "\u8acb\u8f38\u5165\u9023\u7dda\u8a0a\u606f\u3002" } } - }, - "title": "System Bridge" + } } \ No newline at end of file diff --git a/homeassistant/components/system_health/translations/pt-BR.json b/homeassistant/components/system_health/translations/pt-BR.json index eb6f66e8784..15f9774cb16 100644 --- a/homeassistant/components/system_health/translations/pt-BR.json +++ b/homeassistant/components/system_health/translations/pt-BR.json @@ -1,3 +1,3 @@ { - "title": "Integridade Do Sistema" + "title": "Integridade do Sistema" } \ No newline at end of file diff --git a/homeassistant/components/tado/translations/ca.json b/homeassistant/components/tado/translations/ca.json index 935b58483d7..c34d937cf7f 100644 --- a/homeassistant/components/tado/translations/ca.json +++ b/homeassistant/components/tado/translations/ca.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Activa el mode salvaguarda." + "fallback": "Tria el mode alternatiu." }, - "description": "El mode salvaguarda canviar\u00e0 a Planificaci\u00f3 Intel\u00b7ligent en el proper canvi de programaci\u00f3 o quan s'ajusti manualment una zona.", + "description": "El mode alternatiu et permet triar quan passar al mode alternatiu d'horari intel\u00b7ligent des de la teva zona manual. (NEXT_TIME_BLOCK:= Canvia al pr\u00f2xim canvi de l'horari intel\u00b7ligent; MANUAL:= No canvia fins que es cancel\u00b7la; TADO_DEFAULT:= Canvia en funci\u00f3 de la configuraci\u00f3 de l'app Tado).", "title": "Ajusta les opcions de Tado" } } diff --git a/homeassistant/components/tado/translations/de.json b/homeassistant/components/tado/translations/de.json index dec90a46aef..73f068949c3 100644 --- a/homeassistant/components/tado/translations/de.json +++ b/homeassistant/components/tado/translations/de.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Aktiviert den Fallback-Modus." + "fallback": "Aktiviere den Fallback-Modus." }, - "description": "Der Fallback-Modus wechselt beim n\u00e4chsten Zeitplanwechsel nach dem manuellen Anpassen einer Zone zu Smart Schedule.", + "description": "Mit dem Fallback-Modus kannst du festlegen, wann von deinem manuellen Zonen-Overlay auf den intelligenten Zeitplan umgeschaltet werden soll. (NEXT_TIME_BLOCK:= Wechsel bei der n\u00e4chsten Smart Schedule-\u00c4nderung; MANUAL:= Kein Wechsel, bis du abbrichst; TADO_DEFAULT:= Wechsel basierend auf deiner Einstellung in der Tado-App).", "title": "Passe die Tado-Optionen an." } } diff --git a/homeassistant/components/tado/translations/en.json b/homeassistant/components/tado/translations/en.json index 4e0df459437..30d090c77a1 100644 --- a/homeassistant/components/tado/translations/en.json +++ b/homeassistant/components/tado/translations/en.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Enable fallback mode." + "fallback": "Choose fallback mode." }, - "description": "Fallback mode will switch to Smart Schedule at next schedule switch after manually adjusting a zone.", + "description": "Fallback mode lets you choose when to fallback to Smart Schedule from your manual zone overlay. (NEXT_TIME_BLOCK:= Change at next Smart Schedule change; MANUAL:= Dont change until you cancel; TADO_DEFAULT:= Change based on your setting in Tado App).", "title": "Adjust Tado options." } } diff --git a/homeassistant/components/tado/translations/et.json b/homeassistant/components/tado/translations/et.json index 6a201b4fd2d..cb8cf14ad3b 100644 --- a/homeassistant/components/tado/translations/et.json +++ b/homeassistant/components/tado/translations/et.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Luba varure\u017eiim." + "fallback": "Vali varure\u017eiim." }, - "description": "P\u00e4rast tsooni k\u00e4sitsi reguleerimist l\u00fclitub varure\u017eiim j\u00e4rgmisel ajakava l\u00fclitil nutikasse ajakavasse.", + "description": "Varure\u017eiim v\u00f5imaldab valida millal naasta nutikale ajakavale k\u00e4sitsi tsooni \u00fclekattega. (NEXT_TIME_BLOCK:= Muuda j\u00e4rgmisel nutika ajakava muudatusel; MANUAL:= \u00c4ra muuda enne kui oled t\u00fchistanud; TADO_DEFAULT:= Muuda, l\u00e4htudes Tado rakenduse seadistustest).", "title": "Kohanda Tado suvandeid." } } diff --git a/homeassistant/components/tado/translations/fr.json b/homeassistant/components/tado/translations/fr.json index 2e10dc38a2e..a3ff3a9fabf 100644 --- a/homeassistant/components/tado/translations/fr.json +++ b/homeassistant/components/tado/translations/fr.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Activer le mode restreint." + "fallback": "Choisissez le mode de retour." }, - "description": "Le mode de repli passera au programme intelligent au prochain changement de programme apr\u00e8s avoir ajust\u00e9 manuellement une zone.", + "description": "Le mode de retour vous permet de choisir le moment du retour \u00e0 la programmation intelligente depuis votre zone de superposition manuelle. (NEXT_TIME_BLOCK\u00a0:= Modifier au prochain changement de programmation intelligente\u00a0; MANUAL\u00a0:= Ne pas modifier jusqu'\u00e0 annulation manuelle\u00a0; TADO_DEFAULT\u00a0:= Modifier en fonction de vos param\u00e8tres dans l'application Tado).", "title": "Ajustez les options de Tado." } } diff --git a/homeassistant/components/tado/translations/hu.json b/homeassistant/components/tado/translations/hu.json index dfde73ce428..8a96591e429 100644 --- a/homeassistant/components/tado/translations/hu.json +++ b/homeassistant/components/tado/translations/hu.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "A tartal\u00e9k m\u00f3d enged\u00e9lyez\u00e9se." + "fallback": "A tartal\u00e9k m\u00f3d be\u00e1ll\u00edt\u00e1sa." }, - "description": "A tartal\u00e9k m\u00f3d intelligens \u00fctemez\u00e9sre v\u00e1lt a k\u00f6vetkez\u0151 \u00fctemez\u00e9s kapcsol\u00f3n\u00e1l, miut\u00e1n manu\u00e1lisan be\u00e1ll\u00edtotta a z\u00f3n\u00e1t.", + "description": "A tartal\u00e9k m\u00f3dban kiv\u00e1laszthatja, hogy mikor t\u00e9rjen vissza Smart Schedule-ra a k\u00e9zi m\u00f3db\u00f3l. (NEXT_TIME_BLOCK:= V\u00e1ltoz\u00e1s a k\u00f6vetkez\u0151 intelligens \u00fctemez\u00e9s m\u00f3dos\u00edt\u00e1s\u00e1n\u00e1l; MANUAL:= Ne m\u00f3dos\u00edtsa, am\u00edg le nem mondja; TADO_DEFAULT:= V\u00e1ltoz\u00e1s a Tado App be\u00e1ll\u00edt\u00e1sai alapj\u00e1n).", "title": "\u00c1ll\u00edtsa be a Tado-t." } } diff --git a/homeassistant/components/tado/translations/id.json b/homeassistant/components/tado/translations/id.json index e6a00f9ee07..c3bd3d305e6 100644 --- a/homeassistant/components/tado/translations/id.json +++ b/homeassistant/components/tado/translations/id.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Aktifkan mode alternatif." + "fallback": "Pilih mode alternatif" }, - "description": "Mode fallback akan beralih ke Smart Schedule pada pergantian jadwal berikutnya setelah menyesuaikan zona secara manual.", + "description": "Mode alternatif memungkinkan Anda memilih kapan harus berubah ke Smart Schedule dari overlay zona manual Anda. (NEXT_TIME_BLOCK:= Ubah pada perubahan Smart Schedule berikutnya; MANUAL:= Jangan ubah sampai Anda membatalkan; TADO_DEFAULT:= Ubah berdasarkan pengaturan Anda di Aplikasi Tado).", "title": "Sesuaikan opsi Tado." } } diff --git a/homeassistant/components/tado/translations/it.json b/homeassistant/components/tado/translations/it.json index 34c9dfe597d..6c42534b32a 100644 --- a/homeassistant/components/tado/translations/it.json +++ b/homeassistant/components/tado/translations/it.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Abilita la modalit\u00e0 di ripiego." + "fallback": "Scegli la modalit\u00e0 di ripiego." }, - "description": "La modalit\u00e0 di ripiego passer\u00e0 a Smart Schedule al prossimo cambio di programma dopo aver regolato manualmente una zona.", + "description": "La modalit\u00e0 di ripiego ti consente di scegliere quando eseguire il ripiego alla pianificazione intelligente dalla sovrapposizione manuale delle zone. (NEXT_TIME_BLOCK:= Modifica alla successiva modifica della pianificazione intelligente; MANUAL:= Non modificare fino all'annullamento; TADO_DEFAULT:= Modifica in base alle tue impostazioni nell'applicazione Tado).", "title": "Regola le opzioni di Tado." } } diff --git a/homeassistant/components/tado/translations/nl.json b/homeassistant/components/tado/translations/nl.json index 3b6d914b71c..43b969b69eb 100644 --- a/homeassistant/components/tado/translations/nl.json +++ b/homeassistant/components/tado/translations/nl.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Schakel de terugvalmodus in." + "fallback": "Kies de terugvalmodus." }, - "description": "De fallback-modus schakelt over naar Smart Schedule bij de volgende schemaschakeling na het handmatig aanpassen van een zone.", + "description": "Met de terugvalmodus kunt u kiezen wanneer u wilt terugvallen op Smart Schedule vanuit uw handmatige zoneoverlay. (NEXT_TIME_BLOCK:= Verander bij de volgende wijziging van Smart Schedule; MANUAL:= Verander niet tot je annuleert; TADO_DEFAULT:= Verander op basis van je instelling in Tado App).", "title": "Pas Tado-opties aan." } } diff --git a/homeassistant/components/tado/translations/no.json b/homeassistant/components/tado/translations/no.json index 4b6db19e0af..f2ef0b7de6e 100644 --- a/homeassistant/components/tado/translations/no.json +++ b/homeassistant/components/tado/translations/no.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Aktiver tilbakefallsmodus." + "fallback": "Velg reservemodus." }, - "description": "Fallback-modus bytter til Smart Schedule ved neste planbryter etter manuell justering av en sone.", + "description": "Reservemodus lar deg velge n\u00e5r du vil falle tilbake til Smart Schedule fra det manuelle soneoverlegget ditt. (NEXT_TIME_BLOCK:= Endre ved neste Smart Schedule-endring; MANUAL:= Ikke endre f\u00f8r du avbryter; TADO_DEFAULT:= Endre basert p\u00e5 innstillingen din i Tado-appen).", "title": "Juster Tado-alternativene." } } diff --git a/homeassistant/components/tado/translations/pl.json b/homeassistant/components/tado/translations/pl.json index 4f109fc9e98..d88fb939530 100644 --- a/homeassistant/components/tado/translations/pl.json +++ b/homeassistant/components/tado/translations/pl.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "W\u0142\u0105cz tryb awaryjny." + "fallback": "Wybierz tryb awaryjny." }, - "description": "Tryb rezerwowy prze\u0142\u0105czy si\u0119 na inteligentny harmonogram przy nast\u0119pnym prze\u0142\u0105czeniu z harmonogramu po r\u0119cznym dostosowaniu strefy.", + "description": "Tryb awaryjny pozwala wybra\u0107, kiedy wr\u00f3ci\u0107 do inteligentnego harmonogramu z r\u0119cznej nak\u0142adki strefy. (NEXT_TIME_BLOCK:= Zmie\u0144 przy nast\u0119pnej zmianie inteligentnego harmonogramu; MANUAL:= Nie zmieniaj, dop\u00f3ki nie anulujesz; TADO_DEFAULT:= Zmie\u0144 na podstawie ustawie\u0144 w aplikacji Tado).", "title": "Dostosuj opcje Tado" } } diff --git a/homeassistant/components/tado/translations/pt-BR.json b/homeassistant/components/tado/translations/pt-BR.json index 68cf24fe5df..e0c11a94830 100644 --- a/homeassistant/components/tado/translations/pt-BR.json +++ b/homeassistant/components/tado/translations/pt-BR.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Ative o modo de fallback." + "fallback": "Escolha o modo de fallback." }, - "description": "O modo Fallback mudar\u00e1 para Smart Schedule na pr\u00f3xima troca de agendamento ap\u00f3s ajustar manualmente uma zona.", + "description": "O modo fallback permite que voc\u00ea escolha quando fazer fallback para o Smart Schedule a partir de sua sobreposi\u00e7\u00e3o de zona manual. (NEXT_TIME_BLOCK:= Altere na pr\u00f3xima altera\u00e7\u00e3o do Smart Schedule; MANUAL:= N\u00e3o altere at\u00e9 cancelar; TADO_DEFAULT:= Altere com base na sua configura\u00e7\u00e3o no Tado App).", "title": "Ajuste as op\u00e7\u00f5es do Tado." } } diff --git a/homeassistant/components/tado/translations/ru.json b/homeassistant/components/tado/translations/ru.json index 18e9ddff67b..af2e96ae5ef 100644 --- a/homeassistant/components/tado/translations/ru.json +++ b/homeassistant/components/tado/translations/ru.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0435\u0436\u0438\u043c Fallback" + "fallback": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c." }, - "description": "\u0420\u0435\u0436\u0438\u043c Fallback \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043d\u0430 Smart Schedule \u043f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u043e\u0441\u043b\u0435 \u0440\u0443\u0447\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0437\u043e\u043d\u044b.", + "description": "\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0438\u0437 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u043e\u043d\u044b. (NEXT_TIME_BLOCK:= \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u043c\u0430\u0440\u0442-\u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044f; MANUAL:= \u041d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c, \u043f\u043e\u043a\u0430 \u0412\u044b \u043d\u0435 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u0435; TADO_DEFAULT:= \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0412\u0430\u0448\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 Tado).", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Tado" } } diff --git a/homeassistant/components/tado/translations/tr.json b/homeassistant/components/tado/translations/tr.json index 30fc86b8e6a..76e4c25eefb 100644 --- a/homeassistant/components/tado/translations/tr.json +++ b/homeassistant/components/tado/translations/tr.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "Geri d\u00f6n\u00fc\u015f modunu etkinle\u015ftirin." + "fallback": "Geri d\u00f6n\u00fc\u015f modunu se\u00e7in." }, - "description": "Geri d\u00f6n\u00fc\u015f modu, bir b\u00f6lgeyi manuel olarak ayarlad\u0131ktan sonra bir sonraki program anahtar\u0131nda Ak\u0131ll\u0131 Programa ge\u00e7ecektir.", + "description": "Geri d\u00f6n\u00fc\u015f modu, manuel b\u00f6lge yerle\u015fiminizden Ak\u0131ll\u0131 Programa ne zaman geri d\u00f6nece\u011finizi se\u00e7menizi sa\u011flar. (NEXT_TIME_BLOCK:= Sonraki Ak\u0131ll\u0131 Program de\u011fi\u015fikli\u011finde de\u011fi\u015ftirin; MANUAL:= \u0130ptal edene kadar de\u011fi\u015ftirmeyin; TADO_DEFAULT:= Tado Uygulamas\u0131ndaki ayar\u0131n\u0131za g\u00f6re de\u011fi\u015ftirin).", "title": "Tado se\u00e7eneklerini ayarlay\u0131n." } } diff --git a/homeassistant/components/tado/translations/zh-Hant.json b/homeassistant/components/tado/translations/zh-Hant.json index e7f1f41ce3b..794496d7994 100644 --- a/homeassistant/components/tado/translations/zh-Hant.json +++ b/homeassistant/components/tado/translations/zh-Hant.json @@ -23,9 +23,9 @@ "step": { "init": { "data": { - "fallback": "\u958b\u555f\u5f8c\u964d\u6a21\u5f0f" + "fallback": "\u9078\u64c7\u5f8c\u964d\u6a21\u5f0f\u3002" }, - "description": "\u5f8c\u964d\u6a21\u5f0f\u5c07\u6703\u65bc\u624b\u52d5\u8abf\u6574\u5340\u57df\u5f8c\uff0c\u4e0b\u4e00\u6b21\u898f\u5283\u5207\u63db\u6642\u3001\u5207\u63db\u5230\u667a\u80fd\u884c\u7a0b\u3002", + "description": "\u5f8c\u964d\u6a21\u5f0f\u8b93\u60a8\u9078\u64c7\u7576\u81ea\u624b\u52d5\u8abf\u6574\u5340\u57df\u5f8c\u3001\u5207\u63db\u5230\u667a\u80fd\u6a21\u5f0f\u3002\uff08NEXT_TIME_BLOCK:= \u65bc\u4e0b\u6b21\u667a\u80fd\u6a21\u5f0f\u8b8a\u66f4\u6642\u8b8a\u66f4; MANUAL:= \u76f4\u5230\u53d6\u6d88\u524d\u4e0d\u8981\u8b8a\u66f4; TADO_DEFAULT:= \u57fa\u65bc Tado App \u4e2d\u8a2d\u5b9a\u8b8a\u66f4)\u3002", "title": "\u8abf\u6574 Tado \u9078\u9805\u3002" } } diff --git a/homeassistant/components/tailscale/translations/ca.json b/homeassistant/components/tailscale/translations/ca.json index d111da767f2..a339ce6a22b 100644 --- a/homeassistant/components/tailscale/translations/ca.json +++ b/homeassistant/components/tailscale/translations/ca.json @@ -19,7 +19,7 @@ "api_key": "Clau API", "tailnet": "Tailnet" }, - "description": "Per autenticar-te amb Tailscale, has de crear una clau API a https://login.tailscale.com/admin/settings/authkeys. \n\nLa Tailnet \u00e9s el nom de la teva xarxa Tailscale. La pots trobar a l'extrem superior esquerre del tauler d'administraci\u00f3 de Tailscale (al costat del logotip de Tailscale)." + "description": "Aquesta integraci\u00f3 monitoritza la teva xarxa Tailscale, per\u00f2 NO fa que Home Assistant sigui accessible a trav\u00e9s de la VPN de Tailscale.\n\nPer autenticar-te amb Tailscale, has de crear una clau API a {authkeys_url}. \n\nTailnet \u00e9s el nom de la teva xarxa Tailscale. La pots trobar a l'extrem superior esquerre del tauler d'administraci\u00f3 de Tailscale (al costat del logotip de Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/de.json b/homeassistant/components/tailscale/translations/de.json index 9fbcceaa674..fca05d46a6e 100644 --- a/homeassistant/components/tailscale/translations/de.json +++ b/homeassistant/components/tailscale/translations/de.json @@ -19,7 +19,7 @@ "api_key": "API-Schl\u00fcssel", "tailnet": "Tailnet" }, - "description": "Um sich bei Tailscale zu authentifizieren, musst du einen API-Schl\u00fcssel unter https://login.tailscale.com/admin/settings/authkeys erstellen.\n\nEin Tailnet ist der Name Ihres Tailscale-Netzwerks. Sie finden ihn in der linken oberen Ecke des Tailscale Admin Panels (neben dem Tailscale Logo)." + "description": "Diese Integration \u00fcberwacht dein Tailscale-Netzwerk, sie macht deinen Home Assistant **nicht** \u00fcber Tailscale VPN zug\u00e4nglich. \n\nUm sich bei Tailscale zu authentifizieren, musst du einen API-Schl\u00fcssel unter {authkeys_url} erstellen.\n\nEin Tailnet ist der Name deines Tailscale-Netzwerks. Du findest es oben links im Tailscale Admin Panel (neben dem Tailscale Logo)." } } } diff --git a/homeassistant/components/tailscale/translations/et.json b/homeassistant/components/tailscale/translations/et.json index d542c930ecd..1f8a677f2af 100644 --- a/homeassistant/components/tailscale/translations/et.json +++ b/homeassistant/components/tailscale/translations/et.json @@ -19,7 +19,7 @@ "api_key": "API v\u00f5ti", "tailnet": "Tailnet" }, - "description": "Tailscale'iga autentimiseks pead looma API v\u00f5tme aadressil https://login.tailscale.com/admin/settings/authkeys. \n\n Tailnet on Tailscale v\u00f5rgu nimi. Leiad selle Tailscale halduspaneeli vasakus \u00fclanurgas (Tailscale logo k\u00f5rval)." + "description": "See sidumine j\u00e4lgib Tailscale v\u00f5rku, see **EI TEE** seda Home Assistantile juurdep\u00e4\u00e4setavaks Tailscale VPN-i kaudu. \n\n Tailscale'iga autentimiseks pead looma API v\u00f5tme aadressil {authkeys_url} . \n\n Tailnet on Tailscale v\u00f5rgu nimi. Leiadselle Tailscale halduspaneeli vasakus \u00fclanurgas (Tailscale logo k\u00f5rval)." } } } diff --git a/homeassistant/components/tailscale/translations/fr.json b/homeassistant/components/tailscale/translations/fr.json index 1163ec2d151..c879c2a72e7 100644 --- a/homeassistant/components/tailscale/translations/fr.json +++ b/homeassistant/components/tailscale/translations/fr.json @@ -19,7 +19,7 @@ "api_key": "Cl\u00e9 d'API", "tailnet": "Tailnet" }, - "description": "Pour vous authentifier avec Tailscale, vous devrez cr\u00e9er une cl\u00e9 API sur https://login.tailscale.com/admin/settings/authkeys. \n\n Un Tailnet est le nom de votre r\u00e9seau Tailscale. Vous pouvez le trouver dans le coin sup\u00e9rieur gauche du panneau d'administration Tailscale (\u00e0 c\u00f4t\u00e9 du logo Tailscale)." + "description": "Cette int\u00e9gration permet de surveiller votre r\u00e9seau Tailscale\u00a0; elle **NE REND PAS** votre instance Home Assistant accessible via le VPN Tailscale.\n\nAfin de vous authentifier aupr\u00e8s de Tailscale, vous devez cr\u00e9er une cl\u00e9 d'API sur {authkeys_url}.\n\nUn Tailnet est le nom de votre r\u00e9seau Tailscale. Vous pouvez le trouver dans le coin sup\u00e9rieur gauche du panneau d'administration Tailscale (\u00e0 c\u00f4t\u00e9 du logo Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/hu.json b/homeassistant/components/tailscale/translations/hu.json index ec727cbc00f..bbc41483704 100644 --- a/homeassistant/components/tailscale/translations/hu.json +++ b/homeassistant/components/tailscale/translations/hu.json @@ -19,7 +19,7 @@ "api_key": "API kulcs", "tailnet": "Tailnet" }, - "description": "A Tailscale-rel val\u00f3 hiteles\u00edt\u00e9shez l\u00e9tre kell hoznia egy API-kulcsot a https://login.tailscale.com/admin/settings/authkeys oldalon.\n\nTailnet az \u00f6n tailscale h\u00e1l\u00f3zat\u00e1nak neve. Megtal\u00e1lhat\u00f3 a bal fels\u0151 sarokban a Tailscale Admin panelen (a Tailscale log\u00f3 mellett)." + "description": "Ez az integr\u00e1ci\u00f3 figyeli a Tailscale h\u00e1l\u00f3zat\u00e1t, \u00e9s **NEM** teszi el\u00e9rhet\u0151v\u00e9 az otthoni asszisztenst a Tailscale VPN-en kereszt\u00fcl. \n\nA Tailscale-n\u00e1l t\u00f6rt\u00e9n\u0151 hiteles\u00edt\u00e9shez l\u00e9tre kell hoznia egy API-kulcsot a {authkeys_url} c\u00edmen.\n\nA Tailnet az \u00d6n Tailscale h\u00e1l\u00f3zat\u00e1nak neve. Ezt a Tailscale Admin Panel bal fels\u0151 sark\u00e1ban tal\u00e1lja (a Tailscale log\u00f3 mellett)." } } } diff --git a/homeassistant/components/tailscale/translations/id.json b/homeassistant/components/tailscale/translations/id.json index d88a47fa82e..53b82c440aa 100644 --- a/homeassistant/components/tailscale/translations/id.json +++ b/homeassistant/components/tailscale/translations/id.json @@ -19,7 +19,7 @@ "api_key": "Kunci API", "tailnet": "Tailnet" }, - "description": "Untuk mengautentikasi dengan Tailscale, Anda harus membuat kunci API di https://login.tailscale.com/admin/settings/authkeys. \n\nTailnet adalah nama jaringan Tailscale Anda. Anda dapat menemukannya di pojok kiri atas di Panel Admin Tailscale (di samping logo Tailscale)." + "description": "Integrasi ini memantau jaringan Tailscale Anda, integrasi ini **TIDAK** membuat Home Assistant dapat diakses melalui Tailscale VPN. \n\nUntuk mengautentikasi dengan Tailscale, Anda harus membuat kunci API di {authkeys_url} . \n\nTailnet adalah nama jaringan Tailscale Anda. Anda dapat menemukannya di sudut kiri atas di Panel Admin Tailscale (di samping logo Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/it.json b/homeassistant/components/tailscale/translations/it.json index 0dfe90b8f2e..dba31833cf9 100644 --- a/homeassistant/components/tailscale/translations/it.json +++ b/homeassistant/components/tailscale/translations/it.json @@ -19,7 +19,7 @@ "api_key": "Chiave API", "tailnet": "Tailnet" }, - "description": "Per autenticarti con Tailscale dovrai creare una chiave API su https://login.tailscale.com/admin/settings/authkeys. \n\nUna Tailnet \u00e8 il nome della tua rete Tailscale. Puoi trovarlo nell'angolo in alto a sinistra nel pannello di amministrazione di Tailscale (accanto al logo Tailscale)." + "description": "Questa integrazione monitora la tua rete Tailscale, **NON** rende il tuo Home Assistant accessibile tramite Tailscale VPN. \n\nPer autenticarti con Tailscale dovrai creare una chiave API in {authkeys_url}. \n\nUn Tailnet \u00e8 il nome della tua rete Tailscale. Puoi trovarlo nell'angolo in alto a sinistra del pannello di amministrazione di Tailscale (accanto al logo di Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/nl.json b/homeassistant/components/tailscale/translations/nl.json index 5e46f4f0511..1a59a3ca029 100644 --- a/homeassistant/components/tailscale/translations/nl.json +++ b/homeassistant/components/tailscale/translations/nl.json @@ -19,7 +19,7 @@ "api_key": "API-sleutel", "tailnet": "Tailnet" }, - "description": "Om te authenticeren met Tailscale moet je een API-sleutel maken op https://login.tailscale.com/admin/settings/authkeys. \n\n Een Tailnet is de naam van uw Tailscale-netwerk. Je vindt het in de linkerbovenhoek in het Tailscale Admin Panel (naast het Tailscale-logo)." + "description": "Deze integratie controleert uw Tailscale netwerk, het maakt uw Home Assistant **NIET** toegankelijk via Tailscale VPN. \n\nOm te authenticeren met Tailscale moet u een API sleutel aanmaken op {authkeys_url}.\n\nEen Tailnet is de naam van uw Tailscale netwerk. U kunt het vinden in de linkerbovenhoek in het Tailscale Admin Panel (naast het Tailscale logo)." } } } diff --git a/homeassistant/components/tailscale/translations/no.json b/homeassistant/components/tailscale/translations/no.json index 627facd8f66..5c1ae4c6bc0 100644 --- a/homeassistant/components/tailscale/translations/no.json +++ b/homeassistant/components/tailscale/translations/no.json @@ -19,7 +19,7 @@ "api_key": "API-n\u00f8kkel", "tailnet": "Tailnet" }, - "description": "For \u00e5 autentisere med Tailscale m\u00e5 du opprette en API-n\u00f8kkel p\u00e5 https://login.tailscale.com/admin/settings/authkeys. \n\n Et Tailnet er navnet p\u00e5 Tailscale-nettverket ditt. Du finner den i \u00f8verste venstre hj\u00f8rne i Tailscale Admin Panel (ved siden av Tailscale-logoen)." + "description": "Denne integrasjonen overv\u00e5ker Tailscale-nettverket ditt, det ** GJ\u00d8R IKKE ** gj\u00f8r hjemmeassistenten din tilgjengelig via Tailscale VPN. \n\nHvis du vil godkjenne med Tailscale, m\u00e5 du opprette en API-n\u00f8kkel p\u00e5 {authkeys_url}.\n\nEt Tailnet er navnet p\u00e5 Tailscale-nettverket. Du finner den \u00f8verst til venstre i Tailscale Admin Panel (ved siden av Tailscale-logoen)." } } } diff --git a/homeassistant/components/tailscale/translations/pl.json b/homeassistant/components/tailscale/translations/pl.json index 579d8de83e1..ef5f1a4dabf 100644 --- a/homeassistant/components/tailscale/translations/pl.json +++ b/homeassistant/components/tailscale/translations/pl.json @@ -19,7 +19,7 @@ "api_key": "Klucz API", "tailnet": "Tailnet" }, - "description": "Aby uwierzytelni\u0107 si\u0119 w Tailscale, musisz utworzy\u0107 klucz API na stronie https://login.tailscale.com/admin/settings/authkeys.\n\nTailnet to nazwa Twojej sieci Tailscale. Mo\u017cna j\u0105 znale\u017a\u0107 w lewym g\u00f3rnym rogu w panelu administracyjnym Tailscale (obok loga Tailscale)." + "description": "Ta integracja monitoruje Twoj\u0105 sie\u0107 Tailscale, a **NIE SPRAWIA**, \u017ce Tw\u00f3j Home Assistant jest dost\u0119pny przez Tailscale VPN.\n\nAby uwierzytelni\u0107 si\u0119 w Tailscale, musisz utworzy\u0107 klucz API na stronie {authkeys_url}.\n\nTailnet to nazwa Twojej sieci Tailscale. Mo\u017cna j\u0105 znale\u017a\u0107 w lewym g\u00f3rnym rogu w panelu administracyjnym Tailscale (obok loga Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/pt-BR.json b/homeassistant/components/tailscale/translations/pt-BR.json index ddbdda9d5a6..edd55ea6e87 100644 --- a/homeassistant/components/tailscale/translations/pt-BR.json +++ b/homeassistant/components/tailscale/translations/pt-BR.json @@ -19,7 +19,7 @@ "api_key": "Chave da API", "tailnet": "Tailnet" }, - "description": "Para autenticar com o Tailscale, voc\u00ea precisar\u00e1 criar uma chave de API em https://login.tailscale.com/admin/settings/authkeys. \n\nTailnet \u00e9 o nome da sua rede Tailscale. Voc\u00ea pode encontr\u00e1-lo no canto superior esquerdo no painel de administra\u00e7\u00e3o do Tailscale (ao lado do logotipo do Tailscale)." + "description": "Esta integra\u00e7\u00e3o monitora sua rede Tailscale, **N\u00c3O** torna seu Home Assistant acess\u00edvel via Tailscale VPN. \n\n Para autenticar com o Tailscale, voc\u00ea precisar\u00e1 criar uma chave de API em {authkeys_url} . \n\n Um Tailnet \u00e9 o nome da sua rede Tailscale. Voc\u00ea pode encontr\u00e1-lo no canto superior esquerdo no painel de administra\u00e7\u00e3o do Tailscale (ao lado do logotipo do Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/ru.json b/homeassistant/components/tailscale/translations/ru.json index 1b97b0998e7..c05e3a09eac 100644 --- a/homeassistant/components/tailscale/translations/ru.json +++ b/homeassistant/components/tailscale/translations/ru.json @@ -19,7 +19,7 @@ "api_key": "\u041a\u043b\u044e\u0447 API", "tailnet": "Tailnet" }, - "description": "\u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 https://login.tailscale.com/admin/settings/authkeys. \n\nTailnet \u2014 \u044d\u0442\u043e \u0438\u043c\u044f \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438 Tailscale. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0435\u0433\u043e \u0432 \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u043b\u0435\u0432\u043e\u043c \u0443\u0433\u043b\u0443 \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 Tailscale (\u0440\u044f\u0434\u043e\u043c \u0441 \u043b\u043e\u0433\u043e\u0442\u0438\u043f\u043e\u043c Tailscale)." + "description": "\u042d\u0442\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0412\u0430\u0448\u0443 \u0441\u0435\u0442\u044c Tailscale, \u043e\u043d\u0430 **\u041d\u0415** \u0434\u0435\u043b\u0430\u0435\u0442 \u0412\u0430\u0448 Home Assistant \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0447\u0435\u0440\u0435\u0437 Tailscale VPN. \n\n\u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 {authkeys_url}. \n\nTailnet \u2014 \u044d\u0442\u043e \u0438\u043c\u044f \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438 Tailscale. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0435\u0433\u043e \u0432 \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u043b\u0435\u0432\u043e\u043c \u0443\u0433\u043b\u0443 \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 Tailscale (\u0440\u044f\u0434\u043e\u043c \u0441 \u043b\u043e\u0433\u043e\u0442\u0438\u043f\u043e\u043c Tailscale)." } } } diff --git a/homeassistant/components/tailscale/translations/tr.json b/homeassistant/components/tailscale/translations/tr.json index 680acd65d70..784bda86b7d 100644 --- a/homeassistant/components/tailscale/translations/tr.json +++ b/homeassistant/components/tailscale/translations/tr.json @@ -19,7 +19,7 @@ "api_key": "API Anahtar\u0131", "tailnet": "Tailnet" }, - "description": "Tailscale ile kimlik do\u011frulamas\u0131 yapmak i\u00e7in https://login.tailscale.com/admin/settings/authkeys adresinde bir API anahtar\u0131 olu\u015fturman\u0131z gerekir. \n\n Kuyruk a\u011f\u0131, Kuyruk \u00f6l\u00e7e\u011fi a\u011f\u0131n\u0131z\u0131n ad\u0131d\u0131r. Bunu, Tailscale Y\u00f6netici Panelinin sol \u00fcst k\u00f6\u015fesinde (Tailscale logosunun yan\u0131nda) bulabilirsiniz." + "description": "Bu entegrasyon, Tailscale a\u011f\u0131n\u0131z\u0131 izler, ancak Ev Asistan\u0131n\u0131z\u0131 Tailscale VPN arac\u0131l\u0131\u011f\u0131yla eri\u015filebilir k\u0131lmaz. \n\n Tailscale ile kimlik do\u011frulamas\u0131 yapmak i\u00e7in {authkeys_url} adresinde bir API anahtar\u0131 olu\u015fturman\u0131z gerekir. \n\n Kuyruk a\u011f\u0131, Kuyruk \u00f6l\u00e7e\u011fi a\u011f\u0131n\u0131z\u0131n ad\u0131d\u0131r. Bunu, Tailscale Y\u00f6netici Panelinin sol \u00fcst k\u00f6\u015fesinde (Tailscale logosunun yan\u0131nda) bulabilirsiniz." } } } diff --git a/homeassistant/components/tailscale/translations/zh-Hant.json b/homeassistant/components/tailscale/translations/zh-Hant.json index 5ed5f1deb8f..316168fa67a 100644 --- a/homeassistant/components/tailscale/translations/zh-Hant.json +++ b/homeassistant/components/tailscale/translations/zh-Hant.json @@ -19,7 +19,7 @@ "api_key": "API \u91d1\u9470", "tailnet": "Tailnet" }, - "description": "\u6b32\u4f7f\u7528 Tailscale \u8a8d\u8b49\u3001\u5c07\u9700\u8981\u65bc https://login.tailscale.com/admin/settings/authkeys \u65b0\u589e\u4e00\u7d44 API \u91d1\u9470 \n\nTailnet \u70ba Tailscale \u7db2\u8def\u7684\u540d\u7a31\uff0c\u53ef\u4ee5\u65bc Tailscale \u7ba1\u7406\u9762\u677f\uff08Tailscale Logo \u65c1\uff09\u7684\u5de6\u4e0a\u65b9\u627e\u5230\u6b64\u8cc7\u8a0a\u3002" + "description": "\u6574\u5408\u5c07\u76e3\u63a7 Tailscale \u7db2\u8def\uff0c**\u4e26\u975e** \u8b93\u60a8\u7684 Home Assistant \u900f\u904e Tailscale VPN \u5b58\u53d6\u3002 \n\n\u6b32\u4f7f\u7528 Tailscale \u8a8d\u8b49\u3001\u5c07\u9700\u8981\u65bc {authkeys_url} \u65b0\u589e\u4e00\u7d44 API \u91d1\u9470\u3002\n\nTailnet \u70ba Tailscale \u7db2\u8def\u7684\u540d\u7a31\uff0c\u53ef\u4ee5\u65bc Tailscale \u7ba1\u7406\u9762\u677f\uff08Tailscale Logo \u65c1\uff09\u7684\u5de6\u4e0a\u65b9\u627e\u5230\u6b64\u8cc7\u8a0a\u3002" } } } diff --git a/homeassistant/components/tankerkoenig/translations/bg.json b/homeassistant/components/tankerkoenig/translations/bg.json new file mode 100644 index 00000000000..700c4f3000b --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/bg.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "fuel_types": "\u0412\u0438\u0434\u043e\u0432\u0435 \u0433\u043e\u0440\u0438\u0432\u043e", + "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", + "radius": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043d\u0430 \u0442\u044a\u0440\u0441\u0435\u043d\u0435" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/ca.json b/homeassistant/components/tankerkoenig/translations/ca.json new file mode 100644 index 00000000000..676bb1ccb55 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/ca.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada" + }, + "error": { + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "no_stations": "No s'ha pogut trobar cap estaci\u00f3 a l'abast." + }, + "step": { + "select_station": { + "data": { + "stations": "Estacions" + }, + "description": "S'han trobat {stations_count} estacions dins el radi", + "title": "Selecciona les estacions a afegir" + }, + "user": { + "data": { + "api_key": "Clau API", + "fuel_types": "Tipus de combustible", + "location": "Ubicaci\u00f3", + "name": "Nom de la regi\u00f3", + "radius": "Radi de cerca", + "stations": "Estacions de servei addicionals" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval d'actualitzaci\u00f3", + "show_on_map": "Mostra les estacions al mapa" + }, + "title": "Opcions de Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/de.json b/homeassistant/components/tankerkoenig/translations/de.json new file mode 100644 index 00000000000..3c2a5f1ec72 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/de.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Standort ist bereits konfiguriert" + }, + "error": { + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "no_stations": "Konnte keine Station in Reichweite finden." + }, + "step": { + "select_station": { + "data": { + "stations": "Stationen" + }, + "description": "{stations_count} Stationen im Umkreis gefunden", + "title": "W\u00e4hle Stationen zum Hinzuf\u00fcgen aus" + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "fuel_types": "Kraftstoffarten", + "location": "Standort", + "name": "Name der Region", + "radius": "Suchradius", + "stations": "Zus\u00e4tzliche Tankstellen" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Update-Intervall", + "show_on_map": "Stationen auf der Karte anzeigen" + }, + "title": "Tankerkoenig Optionen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/el.json b/homeassistant/components/tankerkoenig/translations/el.json new file mode 100644 index 00000000000..7f814b9760c --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/el.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "no_stations": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b5\u03bc\u03b2\u03ad\u03bb\u03b5\u03b9\u03b1\u03c2." + }, + "step": { + "select_station": { + "data": { + "stations": "\u03a3\u03c4\u03b1\u03b8\u03bc\u03bf\u03af" + }, + "description": "\u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd {stations_count} \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03af \u03c3\u03b5 \u03b1\u03ba\u03c4\u03af\u03bd\u03b1", + "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7" + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "fuel_types": "\u03a4\u03cd\u03c0\u03bf\u03b9 \u03ba\u03b1\u03c5\u03c3\u03af\u03bc\u03c9\u03bd", + "location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae\u03c2", + "radius": "\u0391\u03ba\u03c4\u03af\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7\u03c2", + "stations": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b1 \u03c0\u03c1\u03b1\u03c4\u03ae\u03c1\u03b9\u03b1 \u03ba\u03b1\u03c5\u03c3\u03af\u03bc\u03c9\u03bd" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2", + "show_on_map": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c3\u03c4\u03b1\u03b8\u03bc\u03ce\u03bd \u03c3\u03c4\u03bf \u03c7\u03ac\u03c1\u03c4\u03b7" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/et.json b/homeassistant/components/tankerkoenig/translations/et.json new file mode 100644 index 00000000000..c15e4b78ddf --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/et.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Asukoht on juba m\u00e4\u00e4ratud" + }, + "error": { + "invalid_auth": "Tuvastamine nurjus", + "no_stations": "Piirkonnas ei leitud \u00fchtegi tanklat" + }, + "step": { + "select_station": { + "data": { + "stations": "Tanklad" + }, + "description": "piirkonnas on leitud {stations_count} tanklat", + "title": "Vali lisatavad tanklad" + }, + "user": { + "data": { + "api_key": "API v\u00f5ti", + "fuel_types": "K\u00fctuse liigid", + "location": "Asukoht", + "name": "Piirkonna nimi", + "radius": "Otsingu raadius", + "stations": "T\u00e4iendavad tanklad" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "V\u00e4rskendamise intervall", + "show_on_map": "N\u00e4ita jaamu kaardil" + }, + "title": "Tankerkoenig valikud" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/fr.json b/homeassistant/components/tankerkoenig/translations/fr.json new file mode 100644 index 00000000000..1a52eee5d39 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/fr.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "invalid_auth": "Authentification non valide", + "no_stations": "Aucune station-service n'a \u00e9t\u00e9 trouv\u00e9e dans le rayon indiqu\u00e9." + }, + "step": { + "select_station": { + "data": { + "stations": "Stations-services" + }, + "description": "{stations_count}\u00a0stations-services trouv\u00e9es dans le rayon", + "title": "S\u00e9lectionnez les stations-services \u00e0 ajouter" + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "fuel_types": "Types de carburant", + "location": "Emplacement", + "name": "Nom de la r\u00e9gion", + "radius": "Rayon de recherche", + "stations": "Stations-services suppl\u00e9mentaires" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Intervalle de mise \u00e0 jour", + "show_on_map": "Afficher les stations-services sur la carte" + }, + "title": "Options Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/he.json b/homeassistant/components/tankerkoenig/translations/he.json new file mode 100644 index 00000000000..9ccb9aecfe6 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/he.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "location": "\u05de\u05d9\u05e7\u05d5\u05dd" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/hu.json b/homeassistant/components/tankerkoenig/translations/hu.json new file mode 100644 index 00000000000..e2c31e9e354 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/hu.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "A hely m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "no_stations": "Nem tal\u00e1lhat\u00f3 \u00e1llom\u00e1s a hat\u00f3t\u00e1vols\u00e1gon bel\u00fcl." + }, + "step": { + "select_station": { + "data": { + "stations": "\u00c1llom\u00e1sok" + }, + "description": "{stations_count} \u00e1llom\u00e1s tal\u00e1lhat\u00f3 a hat\u00f3t\u00e1vols\u00e1gon bel\u00fcl", + "title": "Hozz\u00e1adand\u00f3 \u00e1llom\u00e1sok kiv\u00e1laszt\u00e1sa" + }, + "user": { + "data": { + "api_key": "API kulcs", + "fuel_types": "\u00dczemanyag t\u00edpusok", + "location": "Elhelyezked\u00e9s", + "name": "R\u00e9gi\u00f3 neve", + "radius": "Keres\u00e9s hat\u00f3t\u00e1vols\u00e1ga", + "stations": "Tov\u00e1bbi \u00fczemanyagt\u00f6lt\u0151 \u00e1llom\u00e1sok" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Friss\u00edt\u00e9si id\u0151k\u00f6z", + "show_on_map": "\u00c1llom\u00e1sok megjelen\u00edt\u00e9se a t\u00e9rk\u00e9pen" + }, + "title": "Tankerkoenig be\u00e1ll\u00edt\u00e1sok" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/id.json b/homeassistant/components/tankerkoenig/translations/id.json new file mode 100644 index 00000000000..cddeb02b17f --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/id.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Lokasi sudah dikonfigurasi" + }, + "error": { + "invalid_auth": "Autentikasi tidak valid", + "no_stations": "Tidak dapat menemukan SPBU dalam jangkauan." + }, + "step": { + "select_station": { + "data": { + "stations": "SPBU" + }, + "description": "ditemukan {stations_count} SPBU dalam radius", + "title": "Pilih SPBU untuk ditambahkan" + }, + "user": { + "data": { + "api_key": "Kunci API", + "fuel_types": "Jenis bahan bakar", + "location": "Lokasi", + "name": "Nama wilayah", + "radius": "Radius pencarian", + "stations": "SPBU tambahan" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval pembaruan", + "show_on_map": "Tampilkan SPBU di peta" + }, + "title": "Opsi Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/it.json b/homeassistant/components/tankerkoenig/translations/it.json new file mode 100644 index 00000000000..e24c353d4f1 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/it.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "La posizione \u00e8 gi\u00e0 configurata" + }, + "error": { + "invalid_auth": "Autenticazione non valida", + "no_stations": "Impossibile trovare nessuna stazione nel raggio d'azione." + }, + "step": { + "select_station": { + "data": { + "stations": "Stazioni" + }, + "description": "trovato {stations_count} stazioni nel raggio", + "title": "Seleziona le stazioni da aggiungere" + }, + "user": { + "data": { + "api_key": "Chiave API", + "fuel_types": "Tipi di carburante", + "location": "Posizione", + "name": "Nome della regione", + "radius": "Raggio di ricerca", + "stations": "Stazioni di servizio aggiuntive" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Intervallo di aggiornamento", + "show_on_map": "Mostra stazioni sulla mappa" + }, + "title": "Opzioni Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/ja.json b/homeassistant/components/tankerkoenig/translations/ja.json new file mode 100644 index 00000000000..687e05322d5 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/ja.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "no_stations": "\u7bc4\u56f2\u5185\u306b\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002" + }, + "step": { + "select_station": { + "data": { + "stations": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3" + }, + "description": "\u534a\u5f84\u5185\u306b {stations_count} \u500b\u306e\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f", + "title": "\u8ffd\u52a0\u3059\u308b\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u307e\u3059" + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "fuel_types": "\u71c3\u6599\u306e\u7a2e\u985e", + "location": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3", + "name": "\u5730\u57df\u540d", + "radius": "\u691c\u7d22\u534a\u5f84", + "stations": "\u8ffd\u52a0\u306e\u71c3\u6599\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "\u66f4\u65b0\u9593\u9694", + "show_on_map": "\u5730\u56f3\u4e0a\u306b\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3092\u8868\u793a\u3059\u308b" + }, + "title": "Tankerkoenig\u30aa\u30d7\u30b7\u30e7\u30f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/nl.json b/homeassistant/components/tankerkoenig/translations/nl.json new file mode 100644 index 00000000000..96de058ad74 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/nl.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Locatie is al geconfigureerd." + }, + "error": { + "invalid_auth": "Ongeldige authenticatie", + "no_stations": "Kon geen station in bereik vinden." + }, + "step": { + "select_station": { + "data": { + "stations": "Stations" + }, + "description": "{stations_count} gevonden in radius", + "title": "Selecteer stations om toe te voegen" + }, + "user": { + "data": { + "api_key": "API-sleutel", + "fuel_types": "Brandstofsoorten", + "location": "Locatie", + "name": "Regionaam", + "radius": "Zoekradius", + "stations": "Extra tankstations" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Update Interval", + "show_on_map": "Toon stations op kaart" + }, + "title": "Tankerkoenig opties" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/no.json b/homeassistant/components/tankerkoenig/translations/no.json new file mode 100644 index 00000000000..9d0b6ddab52 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/no.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Plasseringen er allerede konfigurert" + }, + "error": { + "invalid_auth": "Ugyldig godkjenning", + "no_stations": "Kunne ikke finne noen stasjon innen rekkevidde." + }, + "step": { + "select_station": { + "data": { + "stations": "Stasjoner" + }, + "description": "fant {stations_count} stasjoner i radius", + "title": "Velg stasjoner du vil legge til" + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "fuel_types": "Drivstofftyper", + "location": "Plassering", + "name": "Navn p\u00e5 omr\u00e5de", + "radius": "Radius for s\u00f8k", + "stations": "Flere bensinstasjoner" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Oppdateringsintervall", + "show_on_map": "Vis stasjoner p\u00e5 kart" + }, + "title": "Tankerkoenig alternativer" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/pl.json b/homeassistant/components/tankerkoenig/translations/pl.json new file mode 100644 index 00000000000..e13ae2c4783 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/pl.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Lokalizacja jest ju\u017c skonfigurowana" + }, + "error": { + "invalid_auth": "Niepoprawne uwierzytelnienie", + "no_stations": "Nie mo\u017cna znale\u017a\u0107 \u017cadnej stacji w zasi\u0119gu." + }, + "step": { + "select_station": { + "data": { + "stations": "Stacje" + }, + "description": "liczba znalezionych stacji w promieniu: {stations_count}", + "title": "Wybierz stacje do dodania" + }, + "user": { + "data": { + "api_key": "Klucz API", + "fuel_types": "Rodzaje paliw", + "location": "Lokalizacja", + "name": "Nazwa regionu", + "radius": "Promie\u0144 wyszukiwania", + "stations": "Dodatkowe stacje paliw" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji", + "show_on_map": "Poka\u017c stacje na mapie" + }, + "title": "Opcje Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/pt-BR.json b/homeassistant/components/tankerkoenig/translations/pt-BR.json new file mode 100644 index 00000000000..b24e0b1dca8 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/pt-BR.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Localiza\u00e7\u00e3o j\u00e1 est\u00e1 configurada" + }, + "error": { + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "no_stations": "N\u00e3o foi poss\u00edvel encontrar nenhum posto ao alcance." + }, + "step": { + "select_station": { + "data": { + "stations": "Postos de combustiveis" + }, + "description": "encontrou {stations_count} postos no raio", + "title": "Selecione postos para adicionar" + }, + "user": { + "data": { + "api_key": "Chave da API", + "fuel_types": "Tipos de combust\u00edvel", + "location": "Localiza\u00e7\u00e3o", + "name": "Nome da regi\u00e3o", + "radius": "Raio de pesquisa", + "stations": "Postos de combust\u00edvel adicionais" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Intervalo de atualiza\u00e7\u00e3o", + "show_on_map": "Mostrar postos no mapa" + }, + "title": "Op\u00e7\u00f5es de Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/ru.json b/homeassistant/components/tankerkoenig/translations/ru.json new file mode 100644 index 00000000000..bf61fddc0c5 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/ru.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." + }, + "error": { + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "no_stations": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u0432 \u0440\u0430\u0434\u0438\u0443\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f." + }, + "step": { + "select_station": { + "data": { + "stations": "\u0421\u0442\u0430\u043d\u0446\u0438\u0438" + }, + "description": "\u041d\u0430\u0439\u0434\u0435\u043d\u043e {stations_count} \u0441\u0442\u0430\u043d\u0446\u0438\u0439 \u0432 \u0440\u0430\u0434\u0438\u0443\u0441\u0435.", + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438" + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "fuel_types": "\u0412\u0438\u0434\u044b \u0442\u043e\u043f\u043b\u0438\u0432\u0430", + "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0433\u0438\u043e\u043d\u0430", + "radius": "\u0420\u0430\u0434\u0438\u0443\u0441 \u043f\u043e\u0438\u0441\u043a\u0430", + "stations": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u044b\u0435 \u0441\u0442\u0430\u043d\u0446\u0438\u0438" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f", + "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 \u043a\u0430\u0440\u0442\u0435" + }, + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Tankerkoenig" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/tr.json b/homeassistant/components/tankerkoenig/translations/tr.json new file mode 100644 index 00000000000..2d88d2fa670 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/tr.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "Konum zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "no_stations": "Menzilde herhangi bir istasyon bulunamad\u0131." + }, + "step": { + "select_station": { + "data": { + "stations": "\u0130stasyonlar" + }, + "description": "yar\u0131\u00e7ap i\u00e7inde {stations_count} istasyon bulundu", + "title": "Eklenecek istasyonlar\u0131 se\u00e7in" + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "fuel_types": "Yak\u0131t t\u00fcrleri", + "location": "Konum", + "name": "B\u00f6lge ad\u0131", + "radius": "Yar\u0131\u00e7ap\u0131 ara\u015ft\u0131r", + "stations": "Ek yak\u0131t istasyonlar\u0131" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "G\u00fcncelle\u015ftirme aral\u0131\u011f\u0131", + "show_on_map": "\u0130stasyonlar\u0131 haritada g\u00f6ster" + }, + "title": "Tankerkoenig se\u00e7enekleri" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/zh-Hant.json b/homeassistant/components/tankerkoenig/translations/zh-Hant.json new file mode 100644 index 00000000000..059d07ccdc6 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/zh-Hant.json @@ -0,0 +1,41 @@ +{ + "config": { + "abort": { + "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "no_stations": "\u7bc4\u570d\u5167\u627e\u4e0d\u5230\u4efb\u4f55\u52a0\u6cb9\u7ad9\u3002" + }, + "step": { + "select_station": { + "data": { + "stations": "\u52a0\u6cb9\u7ad9" + }, + "description": "\u65bc\u534a\u5f91\u5167\u627e\u5230 {stations_count} \u5ea7\u52a0\u6cb9\u7ad9", + "title": "\u9078\u64c7\u65b0\u589e\u52a0\u6cb9\u7ad9" + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "fuel_types": "\u71c3\u6599\u985e\u5225", + "location": "\u5ea7\u6a19", + "name": "\u5340\u57df\u540d\u7a31", + "radius": "\u641c\u5c0b\u534a\u5f91", + "stations": "\u5176\u4ed6\u52a0\u6cb9\u7ad9" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "scan_interval": "\u66f4\u65b0\u983b\u7387", + "show_on_map": "\u65bc\u5730\u5716\u986f\u793a\u52a0\u6cb9\u7ad9" + }, + "title": "Tankerkoenig \u9078\u9805" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/ca.json b/homeassistant/components/tautulli/translations/ca.json new file mode 100644 index 00000000000..15895636b8e --- /dev/null +++ b/homeassistant/components/tautulli/translations/ca.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Clau API" + } + }, + "user": { + "data": { + "api_key": "Clau API", + "url": "URL", + "verify_ssl": "Verifica el certificat SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/de.json b/homeassistant/components/tautulli/translations/de.json new file mode 100644 index 00000000000..bb1e2bc8c95 --- /dev/null +++ b/homeassistant/components/tautulli/translations/de.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Die erneute Authentifizierung war erfolgreich", + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-Schl\u00fcssel" + }, + "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli-Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.", + "title": "Tautulli erneut authentifizieren" + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "url": "URL", + "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + }, + "description": "Um deinen API-Schl\u00fcssel zu finden, \u00f6ffne die Tautulli-Webseite und navigiere zu Einstellungen und dann zu Webinterface. Der API-Schl\u00fcssel befindet sich unten auf dieser Seite.\n\nBeispiel f\u00fcr die URL: ```http://192.168.0.10:8181`` mit 8181 als Standard-Port." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/el.json b/homeassistant/components/tautulli/translations/el.json new file mode 100644 index 00000000000..a83662fb6e6 --- /dev/null +++ b/homeassistant/components/tautulli/translations/el.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", + "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03b1\u03bd\u03bf\u03af\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b9\u03c3\u03c4\u03bf\u03c3\u03b5\u03bb\u03af\u03b4\u03b1 Tautulli \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03ac \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u0399\u03c3\u03c4\u03bf\u03cd. \u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b8\u03b1 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf \u03ba\u03ac\u03c4\u03c9 \u03bc\u03ad\u03c1\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1\u03c2.", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 Tautulli" + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", + "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + }, + "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03b2\u03c1\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03b1\u03bd\u03bf\u03af\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b9\u03c3\u03c4\u03bf\u03c3\u03b5\u03bb\u03af\u03b4\u03b1 Tautulli \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03b9 \u03bc\u03b5\u03c4\u03ac \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u0399\u03c3\u03c4\u03bf\u03cd. \u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b8\u03b1 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf \u03ba\u03ac\u03c4\u03c9 \u03bc\u03ad\u03c1\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1\u03c2. \n\n \u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL: ```http://192.168.0.10:8181``` \u03bc\u03b5 \u03c4\u03bf 8181 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b7 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b8\u03cd\u03c1\u03b1." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/en.json b/homeassistant/components/tautulli/translations/en.json index 90ce8c7bbfc..c24497cfbd9 100644 --- a/homeassistant/components/tautulli/translations/en.json +++ b/homeassistant/components/tautulli/translations/en.json @@ -1,31 +1,30 @@ { - "config": { - "step": { - "user": { - "description": "To find your API key, open the Tautulli webpage and navigate to Settings and then to Web interface. The API key will be at the bottom of that page.\n\nExample of the URL: ```http://192.168.0.10:8181``` with 8181 being the default port.", - "data": { - "api_key": "Api Key", - "url": "URL", - "verify_ssl": "Verify SSL certificate" + "config": { + "abort": { + "reauth_successful": "Re-authentication was successful", + "single_instance_allowed": "Already configured. Only a single configuration possible." + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API Key" + }, + "description": "To find your API key, open the Tautulli webpage and navigate to Settings and then to Web interface. The API key will be at the bottom of that page.", + "title": "Re-authenticate Tautulli" + }, + "user": { + "data": { + "api_key": "API Key", + "url": "URL", + "verify_ssl": "Verify SSL certificate" + }, + "description": "To find your API key, open the Tautulli webpage and navigate to Settings and then to Web interface. The API key will be at the bottom of that page.\n\nExample of the URL: ```http://192.168.0.10:8181``` with 8181 being the default port." + } } - }, - "reauth_confirm": { - "title": "Re-authenticate Tautulli", - "description": "To find your API key, open the Tautulli webpage and navigate to Settings and then to Web interface. The API key will be at the bottom of that page.", - "data": { - "api_key": "Api Key" - } - } - }, - "error": { - "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", - "unknown": "Unexpected error" - }, - "abort": { - "single_instance_allowed": "Already configured. Only a single configuration possible.", - "reauth_successful": "Re-authentication was successful" } - } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/et.json b/homeassistant/components/tautulli/translations/et.json new file mode 100644 index 00000000000..bc690db7a28 --- /dev/null +++ b/homeassistant/components/tautulli/translations/et.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Taastuvastamine \u00f5nnestus", + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API v\u00f5ti" + }, + "description": "API-v\u00f5tme leidmiseks ava Tautulli veebileht ja navigeeri jaotisse Seaded ja seej\u00e4rel veebiliidesesse. API v\u00f5ti asub selle lehe allosas.", + "title": "Taastuvasta Tautulli" + }, + "user": { + "data": { + "api_key": "API v\u00f5ti", + "url": "URL", + "verify_ssl": "Kontrolli SSL sertifikaati" + }, + "description": "API-v\u00f5tme leidmiseks ava Tautulli veebileht ja navigeeri jaotisse Seaded ja seej\u00e4rel veebiliidesesse. API v\u00f5ti asub selle lehe allosas. \n\n URL-i n\u00e4ide: ```http://192.168.0.10:8181```, vaikeportiks on 8181." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/fr.json b/homeassistant/components/tautulli/translations/fr.json new file mode 100644 index 00000000000..05b66af0972 --- /dev/null +++ b/homeassistant/components/tautulli/translations/fr.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Cl\u00e9 d'API" + }, + "description": "Pour trouver votre cl\u00e9 d'API, ouvrez la page web de Tautulli et acc\u00e9dez aux param\u00e8tres puis \u00e0 l'interface web. La cl\u00e9 d'API se trouve au bas de cette page.", + "title": "R\u00e9-authentifier Tautulli" + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "url": "URL", + "verify_ssl": "V\u00e9rifier le certificat SSL" + }, + "description": "Pour trouver votre cl\u00e9 d'API, ouvrez la page web de Tautulli et acc\u00e9dez aux param\u00e8tres puis \u00e0 l'interface web. La cl\u00e9 d'API se trouve au bas de cette page.\n\nExemple d'URL\u00a0: ```http://192.168.0.10:8181``` o\u00f9 8181 est le port par d\u00e9faut." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/he.json b/homeassistant/components/tautulli/translations/he.json new file mode 100644 index 00000000000..80d0bba902b --- /dev/null +++ b/homeassistant/components/tautulli/translations/he.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + }, + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", + "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/hu.json b/homeassistant/components/tautulli/translations/hu.json new file mode 100644 index 00000000000..b654081ecd1 --- /dev/null +++ b/homeassistant/components/tautulli/translations/hu.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API kulcs" + }, + "description": "Az API-kulcs megkeres\u00e9s\u00e9hez nyissa meg a Tautulli weblapot, \u00e9s keresse meg a Be\u00e1ll\u00edt\u00e1sok, majd a webes fel\u00fcletet. Az API-kulcs az oldal alj\u00e1n lesz.", + "title": "Tautulli \u00fajrahiteles\u00edt\u00e9se" + }, + "user": { + "data": { + "api_key": "API kulcs", + "url": "URL", + "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" + }, + "description": "Az API-kulcs megtal\u00e1l\u00e1s\u00e1hoz nyissa meg a Tautulli weboldalt, \u00e9s navig\u00e1ljon a Be\u00e1ll\u00edt\u00e1sok, majd a Webes fel\u00fcletre. Az API-kulcs az oldal alj\u00e1n tal\u00e1lhat\u00f3. \n\nP\u00e9lda az URL-re: ```http://192.168.0.10:8181```, ahol a 8181 az alap\u00e9rtelmezett port." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/id.json b/homeassistant/components/tautulli/translations/id.json new file mode 100644 index 00000000000..c2042dacffa --- /dev/null +++ b/homeassistant/components/tautulli/translations/id.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Autentikasi ulang berhasil", + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Kunci API" + }, + "description": "Untuk menemukan kunci API Anda, buka halaman web Tautulli dan navigasikan ke Pengaturan lalu ke antarmuka Web. Kunci API akan berada di bagian bawah halaman tersebut.", + "title": "Autentikasi Ulang Tautulli" + }, + "user": { + "data": { + "api_key": "Kunci API", + "url": "URL", + "verify_ssl": "Verifikasi sertifikat SSL" + }, + "description": "Untuk menemukan kunci API Anda, buka halaman web Tautulli dan navigasikan ke Pengaturan lalu ke antarmuka Web. Kunci API akan berada di bagian bawah halaman tersebut.\n\nContoh URL: ```http://192.168.0.10:8181``` dengan 8181 sebagai port default." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/it.json b/homeassistant/components/tautulli/translations/it.json new file mode 100644 index 00000000000..7dcfb1dd057 --- /dev/null +++ b/homeassistant/components/tautulli/translations/it.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chiave API" + }, + "description": "Per trovare la tua chiave API, apri la pagina web di Tautulli e vai su Impostazioni e poi su Interfaccia web. La chiave API sar\u00e0 in fondo a quella pagina.", + "title": "Autentica nuovamente Tautulli" + }, + "user": { + "data": { + "api_key": "Chiave API", + "url": "URL", + "verify_ssl": "Verifica il certificato SSL" + }, + "description": "Per trovare la tua chiave API, apri la pagina web di Tautulli e vai su Impostazioni e poi su Interfaccia web. La chiave API sar\u00e0 in fondo a quella pagina. \n\n Esempio dell'URL: ```http://192.168.0.10:8181``` con 8181 come porta predefinita." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/nl.json b/homeassistant/components/tautulli/translations/nl.json new file mode 100644 index 00000000000..eeecbd56477 --- /dev/null +++ b/homeassistant/components/tautulli/translations/nl.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "reauth_successful": "Herauthenticatie was succesvol", + "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-sleutel" + }, + "title": "Herauthenticeer Tautulli" + }, + "user": { + "data": { + "api_key": "API-sleutel", + "url": "URL", + "verify_ssl": "SSL-certificaat verifi\u00ebren" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/no.json b/homeassistant/components/tautulli/translations/no.json new file mode 100644 index 00000000000..cd6667b26fe --- /dev/null +++ b/homeassistant/components/tautulli/translations/no.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-n\u00f8kkel" + }, + "description": "For \u00e5 finne API-n\u00f8kkelen din, \u00e5pne Tautulli-nettsiden og naviger til Innstillinger og deretter til nettgrensesnitt. API-n\u00f8kkelen vil v\u00e6re nederst p\u00e5 siden.", + "title": "Autentiser Tautulli p\u00e5 nytt" + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "url": "URL", + "verify_ssl": "Verifisere SSL-sertifikat" + }, + "description": "For \u00e5 finne API-n\u00f8kkelen din, \u00e5pne Tautulli-nettsiden og naviger til Innstillinger og deretter til nettgrensesnitt. API-n\u00f8kkelen vil v\u00e6re nederst p\u00e5 siden. \n\n Eksempel p\u00e5 URL: ```http://192.168.0.10:8181``` med 8181 som standardport." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/pl.json b/homeassistant/components/tautulli/translations/pl.json new file mode 100644 index 00000000000..25684ac6b3c --- /dev/null +++ b/homeassistant/components/tautulli/translations/pl.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Klucz API" + }, + "description": "Aby znale\u017a\u0107 klucz API, otw\u00f3rz stron\u0119 Tautulli i przejd\u017a do \"Settings\", a nast\u0119pnie do \"Web interface\". Klucz API b\u0119dzie znajdowa\u0107 si\u0119 na dole tej strony.", + "title": "Ponowne uwierzytelnienie Tautulli" + }, + "user": { + "data": { + "api_key": "Klucz API", + "url": "URL", + "verify_ssl": "Weryfikacja certyfikatu SSL" + }, + "description": "Aby znale\u017a\u0107 klucz API, otw\u00f3rz stron\u0119 Tautulli i przejd\u017a do \"Settings\", a nast\u0119pnie do \"Web interface\". Klucz API b\u0119dzie znajdowa\u0107 si\u0119 na dole tej strony. \n\nPrzyk\u0142ad adresu URL: \"http://192.168.0.10:8181\" z portem domy\u015blnym 8181." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/pt-BR.json b/homeassistant/components/tautulli/translations/pt-BR.json new file mode 100644 index 00000000000..45c5b508f96 --- /dev/null +++ b/homeassistant/components/tautulli/translations/pt-BR.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + }, + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + }, + "description": "Para encontrar sua chave de API, abra a p\u00e1gina da Web do Tautulli e navegue at\u00e9 Configura\u00e7\u00f5es e, em seguida, para a interface da Web. A chave da API estar\u00e1 na parte inferior dessa p\u00e1gina.", + "title": "Re-autenticar Tautulli" + }, + "user": { + "data": { + "api_key": "Chave da API", + "url": "URL", + "verify_ssl": "Verifique o certificado SSL" + }, + "description": "Para encontrar sua chave de API, abra a p\u00e1gina da Web do Tautulli e navegue at\u00e9 Configura\u00e7\u00f5es e, em seguida, para a interface da Web. A chave da API estar\u00e1 na parte inferior dessa p\u00e1gina. \n\n Exemplo da URL: ```http://192.168.0.10:8181``` com 8181 sendo a porta padr\u00e3o." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/ru.json b/homeassistant/components/tautulli/translations/ru.json new file mode 100644 index 00000000000..fc5e6584157 --- /dev/null +++ b/homeassistant/components/tautulli/translations/ru.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "description": "\u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u043a\u043b\u044e\u0447 API, \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Tautulli \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u00ab\u0412\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u00bb. \u041a\u043b\u044e\u0447 API \u0431\u0443\u0434\u0435\u0442 \u0432\u043d\u0438\u0437\u0443 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0444\u0438\u043b\u044f" + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "url": "URL-\u0430\u0434\u0440\u0435\u0441", + "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + }, + "description": "\u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u0441\u0432\u043e\u0439 \u043a\u043b\u044e\u0447 API, \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 Tautulli \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u00ab\u0412\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u00bb. \u041a\u043b\u044e\u0447 API \u0431\u0443\u0434\u0435\u0442 \u0432\u043d\u0438\u0437\u0443 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \n\n\u041f\u0440\u0438\u043c\u0435\u0440 URL-\u0430\u0434\u0440\u0435\u0441\u0430: ```http://192.168.0.10:8181```, \u0433\u0434\u0435 8181 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0440\u0442\u043e\u043c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/zh-Hant.json b/homeassistant/components/tautulli/translations/zh-Hant.json new file mode 100644 index 00000000000..aed9ce361ee --- /dev/null +++ b/homeassistant/components/tautulli/translations/zh-Hant.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u91d1\u9470" + }, + "description": "\u8981\u53d6\u5f97 API \u91d1\u9470\uff0c\u958b\u555f Tautulli \u7db2\u9801\u3001\u4e26\u700f\u89bd\u8a2d\u5b9a\u4e2d\u7684\u7db2\u7ad9\u4ecb\u9762\uff08Web interface\uff09\u3002\u53ef\u4ee5\u65bc\u9801\u9762\u4e0b\u65b9\u627e\u5230 API \u91d1\u9470\u3002", + "title": "\u91cd\u65b0\u8a8d\u8b49 Tautulli" + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "url": "\u7db2\u5740", + "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + }, + "description": "\u8981\u53d6\u5f97 API \u91d1\u9470\uff0c\u958b\u555f Tautulli \u7db2\u9801\u3001\u4e26\u700f\u89bd\u8a2d\u5b9a\u4e2d\u7684\u7db2\u7ad9\u4ecb\u9762\uff08Web interface\uff09\u3002\u53ef\u4ee5\u65bc\u9801\u9762\u4e0b\u65b9\u627e\u5230 API \u91d1\u9470\u3002\n\nURL \u7bc4\u4f8b\uff1a```http://192.168.0.10:8181``` \u4e26\u4ee5 8181 \u70ba\u9810\u8a2d\u901a\u8a0a\u57e0\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/translations/hu.json b/homeassistant/components/tellduslive/translations/hu.json index a07259b67f9..2844415f630 100644 --- a/homeassistant/components/tellduslive/translations/hu.json +++ b/homeassistant/components/tellduslive/translations/hu.json @@ -11,7 +11,7 @@ }, "step": { "auth": { - "description": "A TelldusLive-fi\u00f3k \u00f6sszekapcsol\u00e1sa:\n 1. Kattintson az al\u00e1bbi linkre\n 2. Jelentkezzen be a Telldus Live szolg\u00e1ltat\u00e1sba\n 3. Enged\u00e9lyezzeie kell **{app_name}** (kattintson a ** Yes ** gombra).\n 4. J\u00f6jj\u00f6n vissza ide, \u00e9s kattintson a ** K\u00fcld\u00e9s ** gombra. \n\n [Link TelldusLive-fi\u00f3k]({auth_url})", + "description": "A TelldusLive-fi\u00f3k \u00f6sszekapcsol\u00e1sa:\n 1. Kattintson az al\u00e1bbi linkre\n 2. Jelentkezzen be a Telldus Live szolg\u00e1ltat\u00e1sba\n 3. Enged\u00e9lyezzeie kell **{app_name}** (kattintson a **Yes** gombra).\n 4. J\u00f6jj\u00f6n vissza ide, \u00e9s kattintson a **Mehet** gombra. \n\n [Link TelldusLive-fi\u00f3k]({auth_url})", "title": "Hiteles\u00edtsen a TelldusLive-on" }, "user": { diff --git a/homeassistant/components/tesla_wall_connector/translations/ca.json b/homeassistant/components/tesla_wall_connector/translations/ca.json index 7f0056f3413..ff92037845f 100644 --- a/homeassistant/components/tesla_wall_connector/translations/ca.json +++ b/homeassistant/components/tesla_wall_connector/translations/ca.json @@ -16,15 +16,5 @@ "title": "Configuraci\u00f3 de Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Freq\u00fc\u00e8ncia d'actualitzaci\u00f3" - }, - "title": "Configuraci\u00f3 d'opcions de Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/de.json b/homeassistant/components/tesla_wall_connector/translations/de.json index 3500fc1aa7b..3fac77da9b1 100644 --- a/homeassistant/components/tesla_wall_connector/translations/de.json +++ b/homeassistant/components/tesla_wall_connector/translations/de.json @@ -16,15 +16,5 @@ "title": "Tesla Wall Connector konfigurieren" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Aktualisierungsfrequenz" - }, - "title": "Optionen f\u00fcr Tesla Wall Connector konfigurieren" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/el.json b/homeassistant/components/tesla_wall_connector/translations/el.json index 3a835ab5d57..c007cd83f2a 100644 --- a/homeassistant/components/tesla_wall_connector/translations/el.json +++ b/homeassistant/components/tesla_wall_connector/translations/el.json @@ -16,15 +16,5 @@ "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u03a3\u03c5\u03c7\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2" - }, - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c4\u03bf Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/en.json b/homeassistant/components/tesla_wall_connector/translations/en.json index 79a3005299f..436cc06ffb4 100644 --- a/homeassistant/components/tesla_wall_connector/translations/en.json +++ b/homeassistant/components/tesla_wall_connector/translations/en.json @@ -16,15 +16,5 @@ "title": "Configure Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Update frequency" - }, - "title": "Configure options for Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/es.json b/homeassistant/components/tesla_wall_connector/translations/es.json index 0e531ec1c01..34cdf425528 100644 --- a/homeassistant/components/tesla_wall_connector/translations/es.json +++ b/homeassistant/components/tesla_wall_connector/translations/es.json @@ -16,15 +16,5 @@ "title": "Configurar el conector de pared Tesla" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frecuencia de actualizaci\u00f3n" - }, - "title": "Configurar las opciones del conector de pared Tesla" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/et.json b/homeassistant/components/tesla_wall_connector/translations/et.json index a13b447d542..877e80759e6 100644 --- a/homeassistant/components/tesla_wall_connector/translations/et.json +++ b/homeassistant/components/tesla_wall_connector/translations/et.json @@ -16,15 +16,5 @@ "title": "Tesla Wall Connector'i seadistamine" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "V\u00e4rskendussagedus" - }, - "title": "Tesla Wall Connector'i seadistamise valikud" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/fr.json b/homeassistant/components/tesla_wall_connector/translations/fr.json index 6cf4aacf7d3..679b5f4b489 100644 --- a/homeassistant/components/tesla_wall_connector/translations/fr.json +++ b/homeassistant/components/tesla_wall_connector/translations/fr.json @@ -16,15 +16,5 @@ "title": "Configurer le connecteur mural Tesla" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Fr\u00e9quence de mise \u00e0 jour" - }, - "title": "Configurer les options pour le connecteur mural Tesla" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/hu.json b/homeassistant/components/tesla_wall_connector/translations/hu.json index 951d4de5a86..ef490721c43 100644 --- a/homeassistant/components/tesla_wall_connector/translations/hu.json +++ b/homeassistant/components/tesla_wall_connector/translations/hu.json @@ -16,15 +16,5 @@ "title": "Tesla Wall Connector konfigur\u00e1l\u00e1sa" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Friss\u00edt\u00e9si gyakoris\u00e1g" - }, - "title": "Tesla Wall Connector konfigur\u00e1l\u00e1sa" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/id.json b/homeassistant/components/tesla_wall_connector/translations/id.json index 6214e3d153f..3ff19213a6c 100644 --- a/homeassistant/components/tesla_wall_connector/translations/id.json +++ b/homeassistant/components/tesla_wall_connector/translations/id.json @@ -16,15 +16,5 @@ "title": "Konfigurasikan Konektor Dinding Tesla" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frekuensi pembaruan" - }, - "title": "Konfigurasikan opsi untuk Konektor Dinding Tesla" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/it.json b/homeassistant/components/tesla_wall_connector/translations/it.json index 5f79a0ee9e0..c67ff4c8cd0 100644 --- a/homeassistant/components/tesla_wall_connector/translations/it.json +++ b/homeassistant/components/tesla_wall_connector/translations/it.json @@ -16,15 +16,5 @@ "title": "Configura Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequenza di aggiornamento" - }, - "title": "Configura le opzioni per Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/ja.json b/homeassistant/components/tesla_wall_connector/translations/ja.json index 5370b0b161a..2eb8c3e695b 100644 --- a/homeassistant/components/tesla_wall_connector/translations/ja.json +++ b/homeassistant/components/tesla_wall_connector/translations/ja.json @@ -16,15 +16,5 @@ "title": "Tesla Wall Connector\u306e\u8a2d\u5b9a" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u983b\u5ea6" - }, - "title": "Tesla Wall Connector\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/nl.json b/homeassistant/components/tesla_wall_connector/translations/nl.json index b6182cdcf5a..f766433231b 100644 --- a/homeassistant/components/tesla_wall_connector/translations/nl.json +++ b/homeassistant/components/tesla_wall_connector/translations/nl.json @@ -16,15 +16,5 @@ "title": "Configureer Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Update frequentie" - }, - "title": "Configureer opties voor Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/no.json b/homeassistant/components/tesla_wall_connector/translations/no.json index ed6b4c30cfd..7c5c10de679 100644 --- a/homeassistant/components/tesla_wall_connector/translations/no.json +++ b/homeassistant/components/tesla_wall_connector/translations/no.json @@ -16,15 +16,5 @@ "title": "Konfigurer Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Oppdateringsfrekvens" - }, - "title": "Konfigurer alternativer for Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/pl.json b/homeassistant/components/tesla_wall_connector/translations/pl.json index 6ee749485ee..59ff982a024 100644 --- a/homeassistant/components/tesla_wall_connector/translations/pl.json +++ b/homeassistant/components/tesla_wall_connector/translations/pl.json @@ -16,15 +16,5 @@ "title": "Konfiguracja z\u0142\u0105cza \u015bciennego Tesla" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji" - }, - "title": "Konfiguracja opcji dla z\u0142\u0105cza \u015bciennego Tesla" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/pt-BR.json b/homeassistant/components/tesla_wall_connector/translations/pt-BR.json index da2c9d50f34..8b38c7654e3 100644 --- a/homeassistant/components/tesla_wall_connector/translations/pt-BR.json +++ b/homeassistant/components/tesla_wall_connector/translations/pt-BR.json @@ -16,15 +16,5 @@ "title": "Configurar o conector de parede Tesla" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "Frequ\u00eancia de atualiza\u00e7\u00e3o" - }, - "title": "Configurar op\u00e7\u00f5es para o Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/ru.json b/homeassistant/components/tesla_wall_connector/translations/ru.json index 0fc1ef88790..7af1aa27a6b 100644 --- a/homeassistant/components/tesla_wall_connector/translations/ru.json +++ b/homeassistant/components/tesla_wall_connector/translations/ru.json @@ -16,15 +16,5 @@ "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Tesla Wall Connector" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Tesla Wall Connector" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/tr.json b/homeassistant/components/tesla_wall_connector/translations/tr.json index b409c4ddc33..89d926e7eab 100644 --- a/homeassistant/components/tesla_wall_connector/translations/tr.json +++ b/homeassistant/components/tesla_wall_connector/translations/tr.json @@ -16,15 +16,5 @@ "title": "Tesla Duvar Ba\u011flant\u0131s\u0131n\u0131 Yap\u0131land\u0131r\u0131n" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "G\u00fcncelleme s\u0131kl\u0131\u011f\u0131" - }, - "title": "Tesla Duvar Konekt\u00f6r\u00fc i\u00e7in se\u00e7enekleri yap\u0131land\u0131r\u0131n" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/zh-Hans.json b/homeassistant/components/tesla_wall_connector/translations/zh-Hans.json index 21ee02edbf0..22533320802 100644 --- a/homeassistant/components/tesla_wall_connector/translations/zh-Hans.json +++ b/homeassistant/components/tesla_wall_connector/translations/zh-Hans.json @@ -16,15 +16,5 @@ "title": "\u914d\u7f6e Tesla \u58c1\u6302\u5f0f\u5145\u7535\u8fde\u63a5\u5668" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u9891\u7387" - }, - "title": "Tesla \u58c1\u6302\u5f0f\u5145\u7535\u8fde\u63a5\u5668\u914d\u7f6e\u9009\u9879" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/tesla_wall_connector/translations/zh-Hant.json b/homeassistant/components/tesla_wall_connector/translations/zh-Hant.json index 69de7dd2eb3..7e1a967689c 100644 --- a/homeassistant/components/tesla_wall_connector/translations/zh-Hant.json +++ b/homeassistant/components/tesla_wall_connector/translations/zh-Hant.json @@ -16,15 +16,5 @@ "title": "\u8a2d\u5b9a\u7279\u65af\u62c9\u58c1\u639b\u5f0f\u5145\u96fb\u5ea7" } } - }, - "options": { - "step": { - "init": { - "data": { - "scan_interval": "\u66f4\u65b0\u983b\u7387" - }, - "title": "\u7279\u65af\u62c9\u58c1\u639b\u5f0f\u5145\u96fb\u5ea7\u8a2d\u5b9a\u9078\u9805" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/bg.json b/homeassistant/components/threshold/translations/bg.json new file mode 100644 index 00000000000..05bbb89437a --- /dev/null +++ b/homeassistant/components/threshold/translations/bg.json @@ -0,0 +1,22 @@ +{ + "config": { + "step": { + "user": { + "data": { + "hysteresis": "\u0425\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", + "name": "\u0418\u043c\u0435" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "hysteresis": "\u0425\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/ca.json b/homeassistant/components/threshold/translations/ca.json new file mode 100644 index 00000000000..b33bd8a04ed --- /dev/null +++ b/homeassistant/components/threshold/translations/ca.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Els l\u00edmits inferior i superior no poden estar tots dos buits" + }, + "step": { + "user": { + "data": { + "entity_id": "Sensor d'entrada", + "hysteresis": "Hist\u00e8resi", + "lower": "L\u00edmit inferior", + "mode": "Mode llindar", + "name": "Nom", + "upper": "L\u00edmit superior" + }, + "description": "Crea un sensor binari que s'activa o es desactiva en funci\u00f3 del valor d'un sensor\n\nNom\u00e9s amb l\u00edmit inferior configurat - S'activa quan el valor del sensor d'entrada est\u00e0 per sota del l\u00edmit.\nNom\u00e9s amb l\u00edmit superior configurat - S'activa quan el valor del sensor d'entrada est\u00e0 per sobre del l\u00edmit.\nAmbd\u00f3s l\u00edmits configurats - S'activa quan el valor del sensor d'entrada est\u00e0 dins l'interval entre els l\u00edmits [inferior .. superior].", + "title": "Afegeix sensor de llindar" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Els l\u00edmits inferior i superior no poden estar tots dos buits" + }, + "step": { + "init": { + "data": { + "entity_id": "Sensor d'entrada", + "hysteresis": "Hist\u00e8resi", + "lower": "L\u00edmit inferior", + "mode": "Mode llindar", + "name": "Nom", + "upper": "L\u00edmit superior" + }, + "description": "Nom\u00e9s amb l\u00edmit inferior configurat - S'activa quan el valor del sensor d'entrada est\u00e0 per sota del l\u00edmit.\nNom\u00e9s amb l\u00edmit superior configurat - S'activa quan el valor del sensor d'entrada est\u00e0 per sobre del l\u00edmit.\nAmbd\u00f3s l\u00edmits configurats - S'activa quan el valor del sensor d'entrada est\u00e0 dins l'interval entre els l\u00edmits [inferior .. superior]." + } + } + }, + "title": "Sensor de llindar" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/de.json b/homeassistant/components/threshold/translations/de.json new file mode 100644 index 00000000000..579dd4f49b0 --- /dev/null +++ b/homeassistant/components/threshold/translations/de.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Untere und obere Grenze k\u00f6nnen nicht beide leer sein" + }, + "step": { + "user": { + "data": { + "entity_id": "Eingangssensor", + "hysteresis": "Hysterese", + "lower": "Untere Grenze", + "mode": "Schwellenwertmodus", + "name": "Name", + "upper": "Obergrenze" + }, + "description": "Erstellen eines bin\u00e4ren Sensors, der sich je nach Wert eines Sensors ein- und ausschaltet\n\nNur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl untere als auch obere Grenze konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [untere Grenze ... obere Grenze] liegt.", + "title": "Schwellenwertsensor hinzuf\u00fcgen" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Untere und obere Grenze k\u00f6nnen nicht beide leer sein" + }, + "step": { + "init": { + "data": { + "entity_id": "Eingangssensor", + "hysteresis": "Hysterese", + "lower": "Untere Grenze", + "mode": "Schwellenwertmodus", + "name": "Name", + "upper": "Obergrenze" + }, + "description": "Nur unterer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors kleiner als der untere Grenzwert ist.\nNur oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors gr\u00f6\u00dfer als der obere Grenzwert ist.\nSowohl unterer als auch oberer Grenzwert konfiguriert - Einschalten, wenn der Wert des Eingangssensors im Bereich [unterer Grenzwert ... oberer Grenzwert] liegt." + } + } + }, + "title": "Schwellenwertsensor" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/el.json b/homeassistant/components/threshold/translations/el.json new file mode 100644 index 00000000000..2ad5f25e5f8 --- /dev/null +++ b/homeassistant/components/threshold/translations/el.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "\u03a4\u03b1 \u03ba\u03ac\u03c4\u03c9 \u03ba\u03b1\u03b9 \u03ac\u03bd\u03c9 \u03cc\u03c1\u03b9\u03b1 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03c4\u03b1 \u03b4\u03cd\u03bf \u03ba\u03b5\u03bd\u03ac" + }, + "step": { + "user": { + "data": { + "entity_id": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "hysteresis": "\u03a5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7", + "lower": "\u039a\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf", + "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03c1\u03af\u03bf\u03c5", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "upper": "\u0386\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf" + }, + "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c0\u03cc\u03c4\u03b5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2. \n\n \u039c\u03cc\u03bd\u03bf \u03c4\u03bf \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03ba\u03c1\u03cc\u03c4\u03b5\u03c1\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf.\n \u039c\u03cc\u03bd\u03bf \u03c4\u03bf \u03b1\u03bd\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b5\u03b3\u03b1\u03bb\u03cd\u03c4\u03b5\u03c1\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03bd\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf.\n \u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf\u03c5 \u03ba\u03b1\u03b9 \u03ac\u03bd\u03c9 \u03bf\u03c1\u03af\u03bf\u03c5 - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c4\u03bf \u03b5\u03cd\u03c1\u03bf\u03c2 [\u03ba\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf .. \u03ac\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf].", + "title": "\u039d\u03ad\u03bf\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03bf\u03c1\u03af\u03bf\u03c5" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\u03a4\u03b1 \u03ba\u03ac\u03c4\u03c9 \u03ba\u03b1\u03b9 \u03ac\u03bd\u03c9 \u03cc\u03c1\u03b9\u03b1 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03c4\u03b1 \u03b4\u03cd\u03bf \u03ba\u03b5\u03bd\u03ac" + }, + "step": { + "init": { + "data": { + "entity_id": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "hysteresis": "\u03a5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7", + "lower": "\u039a\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf", + "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03c1\u03af\u03bf\u03c5", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "upper": "\u0386\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf" + }, + "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c0\u03cc\u03c4\u03b5 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2. \n\n \u039c\u03cc\u03bd\u03bf \u03c4\u03bf \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03ba\u03c1\u03cc\u03c4\u03b5\u03c1\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf.\n \u039c\u03cc\u03bd\u03bf \u03c4\u03bf \u03b1\u03bd\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b5\u03b3\u03b1\u03bb\u03cd\u03c4\u03b5\u03c1\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf \u03b1\u03bd\u03ce\u03c4\u03b5\u03c1\u03bf \u03cc\u03c1\u03b9\u03bf.\n \u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ce\u03c4\u03b5\u03c1\u03bf\u03c5 \u03ba\u03b1\u03b9 \u03ac\u03bd\u03c9 \u03bf\u03c1\u03af\u03bf\u03c5 - \u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b1\u03bd \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c4\u03bf \u03b5\u03cd\u03c1\u03bf\u03c2 [\u03ba\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf .. \u03ac\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf]." + } + } + }, + "title": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03bf\u03c1\u03af\u03bf\u03c5" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/en.json b/homeassistant/components/threshold/translations/en.json index 461ca244353..66a2bb33ddb 100644 --- a/homeassistant/components/threshold/translations/en.json +++ b/homeassistant/components/threshold/translations/en.json @@ -9,6 +9,7 @@ "entity_id": "Input sensor", "hysteresis": "Hysteresis", "lower": "Lower limit", + "mode": "Threshold mode", "name": "Name", "upper": "Upper limit" }, @@ -27,6 +28,7 @@ "entity_id": "Input sensor", "hysteresis": "Hysteresis", "lower": "Lower limit", + "mode": "Threshold mode", "name": "Name", "upper": "Upper limit" }, diff --git a/homeassistant/components/threshold/translations/et.json b/homeassistant/components/threshold/translations/et.json new file mode 100644 index 00000000000..7a41c0bfc3f --- /dev/null +++ b/homeassistant/components/threshold/translations/et.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Alam- ja \u00fclempiir ei saa korraga puududa" + }, + "step": { + "user": { + "data": { + "entity_id": "Sisendandur", + "hysteresis": "H\u00fcsterees", + "lower": "Alampiir", + "mode": "L\u00e4vendi kasutamine", + "name": "Nimi", + "upper": "\u00dclempiir" + }, + "description": "Loo olekuandur mis l\u00fcltub sisse v\u00f5i v\u00e4lja vastavalt m\u00e4\u00e4ratud ajale.\n\nAinult alampiir - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on alla almpiiri.\nAinult \u00fclempiir - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on \u00fcle \u00fclempiiri.\nAlam- ja \u00fclempiir m\u00f5lemad m\u00e4\u00e4ratud - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on vahemikus [alampiir .. \u00fclempiir]", + "title": "Lisa uus l\u00e4vendiandur" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Alam- ja \u00fclempiir ei saa korraga puududa" + }, + "step": { + "init": { + "data": { + "entity_id": "Sisendandur", + "hysteresis": "H\u00fcsterees", + "lower": "Alampiir", + "mode": "L\u00e4vendi kasutamine", + "name": "Nimi", + "upper": "\u00dclempiir" + }, + "description": "Ainult alampiir - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on alla almpiiri.\nAinult \u00fclempiir - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on \u00fcle \u00fclempiiri.\nAlam- ja \u00fclempiir m\u00f5lemad m\u00e4\u00e4ratud - l\u00fclitu sisse kui anduri v\u00e4\u00e4rtus on vahemikus [alampiir .. \u00fclempiir]" + } + } + }, + "title": "L\u00e4vendiandur" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/fr.json b/homeassistant/components/threshold/translations/fr.json new file mode 100644 index 00000000000..29bc21e9cd5 --- /dev/null +++ b/homeassistant/components/threshold/translations/fr.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Les limites inf\u00e9rieure et sup\u00e9rieure ne peuvent pas \u00eatre toutes les deux vides" + }, + "step": { + "user": { + "data": { + "entity_id": "Capteur d'entr\u00e9e", + "hysteresis": "Hyst\u00e9r\u00e9sis", + "lower": "Limite inf\u00e9rieure", + "mode": "Mode de seuil", + "name": "Nom", + "upper": "Limite sup\u00e9rieure" + }, + "description": "Cr\u00e9ez un capteur binaire qui s'active et se d\u00e9sactive en fonction de la valeur d'un autre capteur.\n\nSi seule la limite inf\u00e9rieure est configur\u00e9e, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est inf\u00e9rieure \u00e0 cette limite.\nSi seule la limite sup\u00e9rieure est configur\u00e9e, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est sup\u00e9rieure \u00e0 cette limite.\nSi les deux limites sont configur\u00e9es, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est comprise entre les limites inf\u00e9rieure et sup\u00e9rieure.", + "title": "Ajouter un capteur de seuil" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Les limites inf\u00e9rieure et sup\u00e9rieure ne peuvent pas \u00eatre toutes les deux vides" + }, + "step": { + "init": { + "data": { + "entity_id": "Capteur d'entr\u00e9e", + "hysteresis": "Hyst\u00e9r\u00e9sis", + "lower": "Limite inf\u00e9rieure", + "mode": "Mode de seuil", + "name": "Nom", + "upper": "Limite sup\u00e9rieure" + }, + "description": "Si seule la limite inf\u00e9rieure est configur\u00e9e, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est inf\u00e9rieure \u00e0 cette limite.\nSi seule la limite sup\u00e9rieure est configur\u00e9e, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est sup\u00e9rieure \u00e0 cette limite.\nSi les deux limites sont configur\u00e9es, le capteur est activ\u00e9 lorsque la valeur du capteur d'entr\u00e9e est comprise entre les limites inf\u00e9rieure et sup\u00e9rieure." + } + } + }, + "title": "Capteur de seuil" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/hu.json b/homeassistant/components/threshold/translations/hu.json new file mode 100644 index 00000000000..944a13680d3 --- /dev/null +++ b/homeassistant/components/threshold/translations/hu.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Az als\u00f3 \u00e9s a fels\u0151 hat\u00e1r\u00e9rt\u00e9k nem lehet \u00fcres" + }, + "step": { + "user": { + "data": { + "entity_id": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "hysteresis": "Hiszter\u00e9zis", + "lower": "Als\u00f3 hat\u00e1r", + "mode": "K\u00fcsz\u00f6b\u00e9rt\u00e9k m\u00f3d", + "name": "Elnevez\u00e9s", + "upper": "Fels\u0151 hat\u00e1r" + }, + "description": "Itt \u00e1ll\u00edthatja be, hogy az \u00e9rz\u00e9kel\u0151 mikor kapcsoljon be \u00e9s mikor kapcsoljon ki.\n\nCsak als\u00f3 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol\u00e1s, amikor a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke kisebb, mint az als\u00f3 hat\u00e1r\u00e9rt\u00e9k.\nCsak fels\u0151 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol, ha a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke nagyobb, mint a fels\u0151 hat\u00e1r\u00e9rt\u00e9k.\nAls\u00f3 \u00e9s fels\u0151 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol\u00e1s, ha a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az als\u00f3 hat\u00e1r\u00e9rt\u00e9k \u00e9s fels\u0151 hat\u00e1r\u00e9rt\u00e9k k\u00f6z\u00f6tti tartom\u00e1nyban van.", + "title": "\u00daj k\u00fcsz\u00f6b\u00e9rt\u00e9k-\u00e9rz\u00e9kel\u0151" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Az als\u00f3 \u00e9s a fels\u0151 hat\u00e1r\u00e9rt\u00e9k nem lehet \u00fcres" + }, + "step": { + "init": { + "data": { + "entity_id": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "hysteresis": "Hiszter\u00e9zis", + "lower": "Als\u00f3 hat\u00e1r", + "mode": "K\u00fcsz\u00f6b\u00e9rt\u00e9k m\u00f3d", + "name": "Elnevez\u00e9s", + "upper": "Fels\u0151 hat\u00e1r" + }, + "description": "Itt \u00e1ll\u00edthatja be, hogy az \u00e9rz\u00e9kel\u0151 mikor kapcsoljon be \u00e9s mikor kapcsoljon ki.\n\nCsak als\u00f3 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol\u00e1s, amikor a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke kisebb, mint az als\u00f3 hat\u00e1r\u00e9rt\u00e9k.\nCsak fels\u0151 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol, ha a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke nagyobb, mint a fels\u0151 hat\u00e1r\u00e9rt\u00e9k.\nAls\u00f3 \u00e9s fels\u0151 hat\u00e1r\u00e9rt\u00e9k konfigur\u00e1lva - Bekapcsol\u00e1s, ha a forr\u00e1s \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az als\u00f3 hat\u00e1r\u00e9rt\u00e9k \u00e9s fels\u0151 hat\u00e1r\u00e9rt\u00e9k k\u00f6z\u00f6tti tartom\u00e1nyban van." + } + } + }, + "title": "K\u00fcsz\u00f6b\u00e9rt\u00e9k \u00e9rz\u00e9kel\u0151" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/id.json b/homeassistant/components/threshold/translations/id.json new file mode 100644 index 00000000000..7356dd772e1 --- /dev/null +++ b/homeassistant/components/threshold/translations/id.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Batas bawah dan batas atas tidak boleh kosong sekaligus" + }, + "step": { + "user": { + "data": { + "entity_id": "Sensor input", + "hysteresis": "Histeresis", + "lower": "Batas bawah", + "mode": "Mode ambang batas", + "name": "Nama", + "upper": "Batas atas" + }, + "description": "Buat sensor biner yang nyala dan mati tergantung pada nilai sensor \n\nHanya batas bawah yang dikonfigurasi: Nyalakan saat nilai sensor input kurang dari batas bawah.\nHanya batas atas yang dikonfigurasi: Nyalakan saat nilai sensor input lebih besar dari batas atas.\nKedua batas dikonfigurasi: Nyalakan ketika nilai sensor input berada dalam rentang [batas bawah ... batas atas].", + "title": "Tambahkan Sensor Ambang Batas" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Batas bawah dan batas atas tidak boleh kosong sekaligus" + }, + "step": { + "init": { + "data": { + "entity_id": "Sensor input", + "hysteresis": "Histeresis", + "lower": "Batas bawah", + "mode": "Mode ambang batas", + "name": "Nama", + "upper": "Batas atas" + }, + "description": "Hanya batas bawah yang dikonfigurasi: Nyalakan saat nilai sensor input kurang dari batas bawah.\nHanya batas atas yang dikonfigurasi: Nyalakan saat nilai sensor input lebih besar dari batas atas.\nKedua batas dikonfigurasi: Nyalakan ketika nilai sensor input berada dalam rentang [batas bawah ... batas atas]." + } + } + }, + "title": "Sensor Ambang Batas" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/it.json b/homeassistant/components/threshold/translations/it.json new file mode 100644 index 00000000000..6bc62dce765 --- /dev/null +++ b/homeassistant/components/threshold/translations/it.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "I limiti inferiore e superiore non possono essere entrambi vuoti" + }, + "step": { + "user": { + "data": { + "entity_id": "Sensore di ingresso", + "hysteresis": "Isteresi", + "lower": "Limite inferiore", + "mode": "Modalit\u00e0 soglia", + "name": "Nome", + "upper": "Limite superiore" + }, + "description": "Crea un sensore binario che si accende e si spegne a seconda del valore di un sensore \n\nSolo limite inferiore configurato - si accende quando il valore del sensore di ingresso \u00e8 inferiore al limite inferiore.\nSolo limite superiore configurato - si accende quando il valore del sensore di ingresso \u00e8 maggiore del limite superiore.\nSia il limite inferiore che quello superiore configurati - si accende quando il valore del sensore di ingresso \u00e8 compreso nell'intervallo [limite inferiore .. limite superiore].", + "title": "Aggiungi sensore di soglia" + } + } + }, + "options": { + "error": { + "need_lower_upper": "I limiti inferiore e superiore non possono essere entrambi vuoti" + }, + "step": { + "init": { + "data": { + "entity_id": "Sensore di ingresso", + "hysteresis": "Isteresi", + "lower": "Limite inferiore", + "mode": "Modalit\u00e0 soglia", + "name": "Nome", + "upper": "Limite superiore" + }, + "description": "Solo limite inferiore configurato - si accende quando il valore del sensore di ingresso \u00e8 inferiore al limite inferiore.\nSolo limite superiore configurato - si accende quando il valore del sensore di ingresso \u00e8 maggiore del limite superiore.\nSia il limite inferiore che quello superiore configurati - si accende quando il valore del sensore di ingresso \u00e8 compreso nell'intervallo [limite inferiore .. limite superiore]." + } + } + }, + "title": "Sensore di soglia" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/ja.json b/homeassistant/components/threshold/translations/ja.json new file mode 100644 index 00000000000..7d5a6cc2ce7 --- /dev/null +++ b/homeassistant/components/threshold/translations/ja.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" + }, + "step": { + "user": { + "data": { + "entity_id": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "hysteresis": "\u30d2\u30b9\u30c6\u30ea\u30b7\u30b9", + "lower": "\u4e0b\u9650\u5024", + "mode": "\u3057\u304d\u3044\u5024\u30e2\u30fc\u30c9", + "name": "\u540d\u524d", + "upper": "\u4e0a\u9650\u5024" + }, + "description": "\u30bb\u30f3\u30b5\u30fc\u306e\u30aa\u30f3\u3068\u30aa\u30d5\u3092\u5207\u308a\u66ff\u3048\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\n\n\u4e0b\u9650\u306e\u307f\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c\u4e0b\u9650\u5024\u3088\u308a\u5c0f\u3055\u3044\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002\n\u4e0a\u9650\u306e\u307f\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c\u4e0a\u9650\u5024\u3088\u308a\u5927\u304d\u3044\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002\n\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c[\u4e0b\u9650..\u4e0a\u9650]\u306e\u7bc4\u56f2\u306b\u3042\u308b\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002", + "title": "\u65b0\u3057\u3044\u3057\u304d\u3044\u5024\u30bb\u30f3\u30b5\u30fc" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" + }, + "step": { + "init": { + "data": { + "entity_id": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "hysteresis": "\u30d2\u30b9\u30c6\u30ea\u30b7\u30b9", + "lower": "\u4e0b\u9650\u5024", + "mode": "\u3057\u304d\u3044\u5024\u30e2\u30fc\u30c9", + "name": "\u540d\u524d", + "upper": "\u4e0a\u9650\u5024" + }, + "description": "\u30bb\u30f3\u30b5\u30fc\u306e\u30aa\u30f3\u3068\u30aa\u30d5\u3092\u5207\u308a\u66ff\u3048\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\n\n\u4e0b\u9650\u306e\u307f\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c\u4e0b\u9650\u5024\u3088\u308a\u5c0f\u3055\u3044\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002\n\u4e0a\u9650\u306e\u307f\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c\u4e0a\u9650\u5024\u3088\u308a\u5927\u304d\u3044\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002\n\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u8a2d\u5b9a - \u5165\u529b\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u304c[\u4e0b\u9650..\u4e0a\u9650]\u306e\u7bc4\u56f2\u306b\u3042\u308b\u3068\u304d\u306b\u30aa\u30f3\u306b\u3057\u307e\u3059\u3002" + } + } + }, + "title": "\u3057\u304d\u3044\u5024\u30bb\u30f3\u30b5\u30fc" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/nl.json b/homeassistant/components/threshold/translations/nl.json new file mode 100644 index 00000000000..73af8fcc8b7 --- /dev/null +++ b/homeassistant/components/threshold/translations/nl.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Onder- en bovengrens kunnen niet beide leeg zijn" + }, + "step": { + "user": { + "data": { + "entity_id": "Invoer sensor", + "hysteresis": "Hyseterise", + "lower": "Ondergrens", + "mode": "Drempelmodus", + "name": "Naam", + "upper": "Bovengrens" + }, + "description": "Maak een binaire sensor die aan en uit gaat afhankelijk van de waarde van een sensor\n\nAlleen ondergrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor kleiner is dan de ondergrens.\nAlleen bovengrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor groter is dan de bovengrens.\nZowel ondergrens als bovengrens ingesteld - Schakelt in wanneer de waarde van de ingangssensor binnen het bereik [ondergrens ... bovengrens] ligt.", + "title": "Drempelsensor toevoegen" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Onder- en bovengrens kunnen niet beide leeg zijn" + }, + "step": { + "init": { + "data": { + "entity_id": "Invoer sensor", + "hysteresis": "Hyseterise", + "lower": "Ondergrens", + "mode": "Drempelmodus", + "name": "Naam", + "upper": "Bovengrens" + }, + "description": "Alleen ondergrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor lager is dan de ondergrens.\nAlleen bovengrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor groter is dan de bovengrens.\nZowel ondergrens als bovengrens ingesteld - Schakelt in wanneer de waarde van de ingangssensor binnen het bereik [ondergrens ... bovengrens] ligt." + } + } + }, + "title": "Drempelsensor" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/no.json b/homeassistant/components/threshold/translations/no.json new file mode 100644 index 00000000000..66c8461d8cc --- /dev/null +++ b/homeassistant/components/threshold/translations/no.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Nedre og \u00f8vre grense kan ikke begge v\u00e6re tomme" + }, + "step": { + "user": { + "data": { + "entity_id": "Inngangssensor", + "hysteresis": "Hysterese", + "lower": "Nedre grense", + "mode": "Terskelverdi-modus", + "name": "Navn", + "upper": "\u00d8vre grense" + }, + "description": "Lag en bin\u00e6r sensor som sl\u00e5s av og p\u00e5 avhengig av verdien til en sensor \n\n Kun nedre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er mindre enn den nedre grensen.\n Bare \u00f8vre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er st\u00f8rre enn den \u00f8vre grensen.\n B\u00e5de nedre og \u00f8vre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er innenfor omr\u00e5det [nedre grense .. \u00f8vre grense].", + "title": "Legg til terskelsensor" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Nedre og \u00f8vre grense kan ikke begge v\u00e6re tomme" + }, + "step": { + "init": { + "data": { + "entity_id": "Inngangssensor", + "hysteresis": "Hysterese", + "lower": "Nedre grense", + "mode": "Terskelverdi-modus", + "name": "Navn", + "upper": "\u00d8vre grense" + }, + "description": "Kun nedre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er mindre enn den nedre grensen.\n Bare \u00f8vre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er st\u00f8rre enn den \u00f8vre grensen.\n B\u00e5de nedre og \u00f8vre grense konfigurert - Sl\u00e5 p\u00e5 n\u00e5r inngangssensorens verdi er innenfor omr\u00e5det [nedre grense .. \u00f8vre grense]." + } + } + }, + "title": "Terskelsensor" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/pl.json b/homeassistant/components/threshold/translations/pl.json new file mode 100644 index 00000000000..5db231947c2 --- /dev/null +++ b/homeassistant/components/threshold/translations/pl.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Dolny i g\u00f3rny limit nie mog\u0105 by\u0107 jednocze\u015bnie puste" + }, + "step": { + "user": { + "data": { + "entity_id": "Sensor wej\u015bciowy", + "hysteresis": "Op\u00f3\u017anienie", + "lower": "Dolny limit", + "mode": "Tryb progowy", + "name": "Nazwa", + "upper": "G\u00f3rny limit" + }, + "description": "Utw\u00f3rz sensor binarny, kt\u00f3ry w\u0142\u0105cza si\u0119 lub wy\u0142\u0105cza w zale\u017cno\u015bci od warto\u015bci sensora.\n\nSkonfigurowano tylko dolny limit - W\u0142\u0105cza si\u0119, gdy warto\u015b\u0107 sensora wej\u015bciowego jest mniejsza ni\u017c dolny limit.\nSkonfigurowano tylko g\u00f3rny limit - W\u0142\u0105cza si\u0119, gdy warto\u015b\u0107 sensora wej\u015bciowego jest wi\u0119ksza ni\u017c g\u00f3rny limit.\nSkonfigurowano dolny i g\u00f3rny limit - W\u0142\u0105cza si\u0119, gdy warto\u015b\u0107 czujnika wej\u015bciowego znajduje si\u0119 w zakresie [dolny limit .. g\u00f3rny limit].", + "title": "Dodaj sensor progowy" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Dolny i g\u00f3rny limit nie mog\u0105 by\u0107 jednocze\u015bnie puste" + }, + "step": { + "init": { + "data": { + "entity_id": "Sensor wej\u015bciowy", + "hysteresis": "Op\u00f3\u017anienie", + "lower": "Dolny limit", + "mode": "Tryb progowy", + "name": "Nazwa", + "upper": "G\u00f3rny limit" + }, + "description": "Skonfigurowano tylko dolny limit - W\u0142\u0105cza si\u0119, gdy warto\u015b\u0107 sensora wej\u015bciowego jest mniejsza ni\u017c dolny limit.\nSkonfigurowano tylko g\u00f3rny limit - W\u0142\u0105cza, gdy warto\u015b\u0107 sensora wej\u015bciowego jest wi\u0119ksza ni\u017c g\u00f3rny limit.\nSkonfigurowano zar\u00f3wno dolny, jak i g\u00f3rny limit - W\u0142\u0105cza, gdy warto\u015b\u0107 sensora wej\u015bciowego znajduje si\u0119 w zakresie [dolny limit .. g\u00f3rny limit]." + } + } + }, + "title": "Sensor progowy" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/pt-BR.json b/homeassistant/components/threshold/translations/pt-BR.json new file mode 100644 index 00000000000..1aa7358086a --- /dev/null +++ b/homeassistant/components/threshold/translations/pt-BR.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Os limites inferior e superior n\u00e3o podem estar vazios" + }, + "step": { + "user": { + "data": { + "entity_id": "Sensor de entrada", + "hysteresis": "Histerese", + "lower": "Limite inferior", + "mode": "Modo Threshold", + "name": "Nome", + "upper": "Limite superior" + }, + "description": "Crie um sensor bin\u00e1rio que liga e desliga dependendo do valor de um sensor \n\n Somente limite inferior configurado - Liga quando o valor do sensor de entrada for menor que o limite inferior.\n Somente limite superior configurado - Liga quando o valor do sensor de entrada for maior que o limite superior.\n Ambos os limites inferior e superior configurados - Liga quando o valor do sensor de entrada est\u00e1 na faixa [limite inferior .. limite superior].", + "title": "Adicionar sensor Threshold" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Os limites inferior e superior n\u00e3o podem estar vazios" + }, + "step": { + "init": { + "data": { + "entity_id": "Sensor de entrada", + "hysteresis": "Histerese", + "lower": "Limite inferior", + "mode": "Modo Threshold", + "name": "Nome", + "upper": "Limite superior" + }, + "description": "Somente o limite inferior \u00e9 configurado - Liga quando o valor do sensor de entrada for menor que o limite inferior.\nSomente o limite superior \u00e9 configurado - Liga quando o valor do sensor de entrada for maior que o limite superior.\nAmbos os limites inferior e superior s\u00e3o configurados - Liga quando o valor do sensor de entrada est\u00e1 na faixa [limite inferior .. limite superior]." + } + } + }, + "title": "Sensor Threshold" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/ru.json b/homeassistant/components/threshold/translations/ru.json new file mode 100644 index 00000000000..5b8c4546823 --- /dev/null +++ b/homeassistant/components/threshold/translations/ru.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "\u041d\u0438\u0436\u043d\u0438\u0439 \u0438 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0443\u0441\u0442\u044b\u043c\u0438." + }, + "step": { + "user": { + "data": { + "entity_id": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "hysteresis": "\u0413\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", + "lower": "\u041d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b", + "mode": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "upper": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b" + }, + "description": "\u0411\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430. \n\n\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u043c\u0435\u043d\u044c\u0448\u0435 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0435\u043b\u0430.\n\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0435\u043b\u0430.\n\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u043a\u0430\u043a \u043d\u0438\u0436\u043d\u0438\u0439, \u0442\u0430\u043a \u0438 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 [\u043d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2026 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b].", + "title": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\u041d\u0438\u0436\u043d\u0438\u0439 \u0438 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0443\u0441\u0442\u044b\u043c\u0438." + }, + "step": { + "init": { + "data": { + "entity_id": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "hysteresis": "\u0413\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", + "lower": "\u041d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b", + "mode": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "upper": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b" + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u043c\u0435\u043d\u044c\u0448\u0435 \u043d\u0438\u0436\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0435\u043b\u0430.\n\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u043f\u0440\u0435\u0434\u0435\u043b\u0430.\n\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0438 \u043d\u0438\u0436\u043d\u0438\u0439 \u0438 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 [\u043d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b \u2026 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b]." + } + } + }, + "title": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/tr.json b/homeassistant/components/threshold/translations/tr.json new file mode 100644 index 00000000000..0cb338ef681 --- /dev/null +++ b/homeassistant/components/threshold/translations/tr.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "Alt ve \u00fcst limitlerin ikisi de bo\u015f olamaz" + }, + "step": { + "user": { + "data": { + "entity_id": "Giri\u015f sens\u00f6r\u00fc", + "hysteresis": "Histeresis", + "lower": "Alt s\u0131n\u0131r", + "mode": "E\u015fik modu", + "name": "Ad", + "upper": "\u00dcst s\u0131n\u0131r" + }, + "description": "Bir sens\u00f6r\u00fcn de\u011ferine ba\u011fl\u0131 olarak a\u00e7\u0131l\u0131p kapanan bir ikili sens\u00f6r olu\u015fturun \n\n Yaln\u0131zca alt limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri alt limitten d\u00fc\u015f\u00fck oldu\u011funda a\u00e7\u0131n.\n Yaln\u0131zca \u00fcst limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri \u00fcst limitten b\u00fcy\u00fck oldu\u011funda a\u00e7\u0131n.\n Hem alt hem de \u00fcst limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri [alt limit .. \u00fcst limit] aral\u0131\u011f\u0131nda oldu\u011funda a\u00e7\u0131n.", + "title": "E\u015fik Sens\u00f6r\u00fc Ekle" + } + } + }, + "options": { + "error": { + "need_lower_upper": "Alt ve \u00fcst limitlerin ikisi de bo\u015f olamaz" + }, + "step": { + "init": { + "data": { + "entity_id": "Giri\u015f sens\u00f6r\u00fc", + "hysteresis": "Histeresis", + "lower": "Alt s\u0131n\u0131r", + "mode": "E\u015fik modu", + "name": "Ad", + "upper": "\u00dcst s\u0131n\u0131r" + }, + "description": "Yaln\u0131zca alt limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri alt limitten d\u00fc\u015f\u00fck oldu\u011funda a\u00e7\u0131n.\n Yaln\u0131zca \u00fcst limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri \u00fcst limitten b\u00fcy\u00fck oldu\u011funda a\u00e7\u0131n.\n Hem alt hem de \u00fcst limit konfig\u00fcre edildi - Giri\u015f sens\u00f6r\u00fcn\u00fcn de\u011feri [alt limit .. \u00fcst limit] aral\u0131\u011f\u0131nda oldu\u011funda a\u00e7\u0131n." + } + } + }, + "title": "E\u015fik Sens\u00f6r\u00fc" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/zh-Hans.json b/homeassistant/components/threshold/translations/zh-Hans.json new file mode 100644 index 00000000000..35bc919f0d1 --- /dev/null +++ b/homeassistant/components/threshold/translations/zh-Hans.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "\u4e0b\u9650\u548c\u4e0a\u9650\u4e0d\u80fd\u540c\u65f6\u4e3a\u7a7a" + }, + "step": { + "user": { + "data": { + "entity_id": "\u8f93\u5165\u4f20\u611f\u5668", + "hysteresis": "\u9632\u6296\u8303\u56f4", + "lower": "\u4e0b\u9650", + "mode": "\u9608\u503c\u6a21\u5f0f", + "name": "\u540d\u79f0", + "upper": "\u4e0a\u9650" + }, + "description": "\u521b\u5efa\u6839\u636e\u5176\u4ed6\u4f20\u611f\u5668\u7684\u503c\u6539\u53d8\u5f00\u5173\u72b6\u6001\u7684\u4e8c\u5143\u4f20\u611f\u5668\u3002\n\n\u5982\u679c\u53ea\u914d\u7f6e\u4e0b\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u4f4e\u4e8e\u4e0b\u9650\u65f6\u4e3a\u5f00\u3002\n\u5982\u679c\u53ea\u914d\u7f6e\u4e0a\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u9ad8\u4e8e\u4e0a\u7ebf\u65f6\u4e3a\u5f00\u3002\n\u5982\u679c\u540c\u65f6\u914d\u7f6e\u4e0a\u9650\u548c\u4e0b\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u4ecb\u4e8e\u4e0a\u9650\u548c\u4e0b\u9650\u4e4b\u95f4\uff08\u542b\u4e0a\u9650\u548c\u4e0b\u9650\uff09\u65f6\u4e3a\u5f00\u3002", + "title": "\u6dfb\u52a0\u9608\u503c\u4f20\u611f\u5668" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\u4e0b\u9650\u548c\u4e0a\u9650\u4e0d\u80fd\u540c\u65f6\u4e3a\u7a7a" + }, + "step": { + "init": { + "data": { + "entity_id": "\u8f93\u5165\u4f20\u611f\u5668", + "hysteresis": "\u9632\u6296\u8303\u56f4", + "lower": "\u4e0b\u9650", + "mode": "\u9608\u503c\u6a21\u5f0f", + "name": "\u540d\u79f0", + "upper": "\u4e0a\u9650" + }, + "description": "\u5982\u679c\u53ea\u914d\u7f6e\u4e0b\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u4f4e\u4e8e\u4e0b\u9650\u65f6\u4e3a\u5f00\u3002\n\u5982\u679c\u53ea\u914d\u7f6e\u4e0a\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u9ad8\u4e8e\u4e0a\u9650\u65f6\u4e3a\u5f00\u3002\n\u5982\u679c\u540c\u65f6\u914d\u7f6e\u4e0a\u9650\u548c\u4e0b\u9650\uff0c\u5219\u5f53\u8f93\u5165\u4f20\u611f\u5668\u7684\u503c\u4ecb\u4e8e\u4e0a\u9650\u548c\u4e0b\u9650\u4e4b\u95f4\uff08\u542b\u4e0a\u9650\u548c\u4e0b\u9650\uff09\u65f6\u4e3a\u5f00\u3002" + } + } + }, + "title": "\u9608\u503c\u4f20\u611f\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/zh-Hant.json b/homeassistant/components/threshold/translations/zh-Hant.json new file mode 100644 index 00000000000..66bdfe6915a --- /dev/null +++ b/homeassistant/components/threshold/translations/zh-Hant.json @@ -0,0 +1,40 @@ +{ + "config": { + "error": { + "need_lower_upper": "\u4e0a\u4e0b\u9650\u4e0d\u53ef\u540c\u6642\u70ba\u7a7a\u767d" + }, + "step": { + "user": { + "data": { + "entity_id": "\u8f38\u5165\u611f\u6e2c\u5668", + "hysteresis": "\u9072\u6eef", + "lower": "\u4e0b\u9650", + "mode": "\u81e8\u754c\u9ede\u5f0f", + "name": "\u540d\u7a31", + "upper": "\u4e0a\u9650" + }, + "description": "\u65b0\u589e\u6839\u64da\u6578\u503c\u6c7a\u5b9a\u958b\u95dc\u4e4b\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u3002\n\n\u50c5\u8a2d\u5b9a\u4e0b\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u4f4e\u65bc\u4e0b\u9650\u6642\u3001\u958b\u555f\u3002\n\u50c5\u8a2d\u5b9a\u4e0a\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u9ad8\u65bc\u4e0a\u9650\u6642\u3001\u958b\u555f\u3002\n\u540c\u6642\u8a2d\u5b9a\u4e0a\u4e0b\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u65bc\u7bc4\u570d\u5167 [\u4e0b\u9650 .. \u4e0a\u9650] \u6642\u958b\u555f\u3002", + "title": "\u65b0\u589e\u81e8\u754c\u611f\u6e2c\u5668" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\u4e0a\u4e0b\u9650\u4e0d\u53ef\u540c\u6642\u70ba\u7a7a\u767d" + }, + "step": { + "init": { + "data": { + "entity_id": "\u8f38\u5165\u611f\u6e2c\u5668", + "hysteresis": "\u9072\u6eef", + "lower": "\u4e0b\u9650", + "mode": "\u81e8\u754c\u9ede\u5f0f", + "name": "\u540d\u7a31", + "upper": "\u4e0a\u9650" + }, + "description": "\u50c5\u8a2d\u5b9a\u4e0b\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u4f4e\u65bc\u4e0b\u9650\u6642\u3001\u958b\u555f\u3002\n\u50c5\u8a2d\u5b9a\u4e0a\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u9ad8\u65bc\u4e0a\u9650\u6642\u3001\u958b\u555f\u3002\n\u540c\u6642\u8a2d\u5b9a\u4e0a\u4e0b\u9650 - \u7576\u8f38\u5165\u611f\u6e2c\u5668\u6578\u503c\u65bc\u7bc4\u570d\u5167 [\u4e0b\u9650 .. \u4e0a\u9650] \u6642\u958b\u555f\u3002" + } + } + }, + "title": "\u81e8\u754c\u611f\u6e2c\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/bg.json b/homeassistant/components/tod/translations/bg.json new file mode 100644 index 00000000000..35cfa0ad1d7 --- /dev/null +++ b/homeassistant/components/tod/translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/ca.json b/homeassistant/components/tod/translations/ca.json new file mode 100644 index 00000000000..3908cb7761f --- /dev/null +++ b/homeassistant/components/tod/translations/ca.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Activa despr\u00e9s de", + "after_time": "Temps d'activaci\u00f3", + "before": "Desactiva despr\u00e9s de", + "before_time": "Temps de desactivaci\u00f3", + "name": "Nom" + }, + "description": "Crea un sensor binari que s'activa o es desactiva en funci\u00f3 de l'hora.", + "title": "Afegeix sensor temps del dia" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Activa despr\u00e9s de", + "after_time": "Temps d'activaci\u00f3", + "before": "Desactiva despr\u00e9s de", + "before_time": "Temps de desactivaci\u00f3" + }, + "description": "Crea un sensor binari que s'activa o es desactiva en funci\u00f3 de l'hora." + } + } + }, + "title": "Sensor temps del dia" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/de.json b/homeassistant/components/tod/translations/de.json new file mode 100644 index 00000000000..eeb3d4dd8f9 --- /dev/null +++ b/homeassistant/components/tod/translations/de.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Angeschaltet nach", + "after_time": "Einschaltzeit", + "before": "Ausgeschaltet nach", + "before_time": "Ausschaltzeit", + "name": "Name" + }, + "description": "Erstelle einen bin\u00e4ren Sensor, der sich je nach Uhrzeit ein- oder ausschaltet.", + "title": "Tageszeitsensor hinzuf\u00fcgen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Angeschaltet nach", + "after_time": "Einschaltzeit", + "before": "Ausgeschaltet nach", + "before_time": "Ausschaltzeit" + }, + "description": "Erstelle einen bin\u00e4ren Sensor, der sich je nach Uhrzeit ein- oder ausschaltet." + } + } + }, + "title": "Tageszeitensensor" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/el.json b/homeassistant/components/tod/translations/el.json new file mode 100644 index 00000000000..ffa426365a4 --- /dev/null +++ b/homeassistant/components/tod/translations/el.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", + "after_time": "\u0395\u03bd\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", + "before": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", + "before_time": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + }, + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c0\u03cc\u03c4\u03b5 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b8\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9.", + "title": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 New Times of the Day" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", + "after_time": "\u0395\u03bd\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", + "before": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", + "before_time": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" + }, + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c0\u03cc\u03c4\u03b5 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b8\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9." + } + } + }, + "title": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 Times of the Day" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/en.json b/homeassistant/components/tod/translations/en.json index 2ecb2c695c8..ced14151519 100644 --- a/homeassistant/components/tod/translations/en.json +++ b/homeassistant/components/tod/translations/en.json @@ -3,7 +3,9 @@ "step": { "user": { "data": { + "after": "On after", "after_time": "On time", + "before": "Off after", "before_time": "Off time", "name": "Name" }, @@ -16,9 +18,12 @@ "step": { "init": { "data": { + "after": "On after", "after_time": "On time", + "before": "Off after", "before_time": "Off time" - } + }, + "description": "Create a binary sensor that turns on or off depending on the time." } } }, diff --git a/homeassistant/components/tod/translations/et.json b/homeassistant/components/tod/translations/et.json new file mode 100644 index 00000000000..06b40e0ce72 --- /dev/null +++ b/homeassistant/components/tod/translations/et.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Kestus sissel\u00fclitumisest", + "after_time": "Seesoleku aeg", + "before": "Kestus v\u00e4ljal\u00fclitumisest", + "before_time": "V\u00e4ljasoleku aeg", + "name": "Nimi" + }, + "description": "Loo olekuandur mis l\u00fcltub sisse v\u00f5i v\u00e4lja vastavalt m\u00e4\u00e4ratud ajale.", + "title": "Lisa \u00f6\u00f6p\u00e4eva andur" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Kestus sissel\u00fclitumisest", + "after_time": "Seesoleku aeg", + "before": "Kestus v\u00e4ljal\u00fclitumisest", + "before_time": "V\u00e4ljasoleku aeg" + }, + "description": "Vali millal andur on sisse v\u00f5i v\u00e4lja l\u00fclitatud." + } + } + }, + "title": "\u00d6\u00f6p\u00e4eva andur" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/fr.json b/homeassistant/components/tod/translations/fr.json new file mode 100644 index 00000000000..799e286343b --- /dev/null +++ b/homeassistant/components/tod/translations/fr.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Activ\u00e9 apr\u00e8s", + "after_time": "Heure d'activation", + "before": "D\u00e9sactiv\u00e9 apr\u00e8s", + "before_time": "Heure de d\u00e9sactivation", + "name": "Nom" + }, + "description": "Cr\u00e9ez un capteur binaire qui s'active et se d\u00e9sactive en fonction de l'heure.", + "title": "Ajouter un capteur de moment de la journ\u00e9e" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Activ\u00e9 apr\u00e8s", + "after_time": "Heure d'activation", + "before": "D\u00e9sactiv\u00e9 apr\u00e8s", + "before_time": "Heure de d\u00e9sactivation" + }, + "description": "Cr\u00e9ez un capteur binaire qui s'active et se d\u00e9sactive en fonction de l'heure." + } + } + }, + "title": "Capteur de moment de la journ\u00e9e" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/he.json b/homeassistant/components/tod/translations/he.json new file mode 100644 index 00000000000..b10f9e2b1ca --- /dev/null +++ b/homeassistant/components/tod/translations/he.json @@ -0,0 +1,30 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u05d4\u05e4\u05e2\u05dc\u05d4 \u05dc\u05d0\u05d7\u05e8", + "after_time": "\u05d6\u05de\u05df \u05d4\u05e4\u05e2\u05dc\u05d4", + "before": "\u05db\u05d9\u05d1\u05d5\u05d9 \u05dc\u05d0\u05d7\u05e8", + "before_time": "\u05d6\u05de\u05df \u05db\u05d9\u05d1\u05d5\u05d9", + "name": "\u05e9\u05dd" + }, + "description": "\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d9\u05e9\u05df \u05d1\u05d9\u05e0\u05d0\u05e8\u05d9 \u05d4\u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05e0\u05db\u05d1\u05d4 \u05d1\u05d4\u05ea\u05d0\u05dd \u05dc\u05d6\u05de\u05df.", + "title": "\u05d4\u05d5\u05e1\u05e4\u05ea \u05d7\u05d9\u05d9\u05e9\u05df \u05e9\u05e2\u05d5\u05ea \u05d1\u05d9\u05d5\u05dd" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u05d4\u05e4\u05e2\u05dc\u05d4 \u05dc\u05d0\u05d7\u05e8", + "after_time": "\u05d6\u05de\u05df \u05d4\u05e4\u05e2\u05dc\u05d4", + "before": "\u05db\u05d9\u05d1\u05d5\u05d9 \u05dc\u05d0\u05d7\u05e8", + "before_time": "\u05d6\u05de\u05df \u05db\u05d9\u05d1\u05d5\u05d9" + }, + "description": "\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d9\u05e9\u05df \u05d1\u05d9\u05e0\u05d0\u05e8\u05d9 \u05d4\u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05e0\u05db\u05d1\u05d4 \u05d1\u05d4\u05ea\u05d0\u05dd \u05dc\u05d6\u05de\u05df." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/hu.json b/homeassistant/components/tod/translations/hu.json new file mode 100644 index 00000000000..28af029f230 --- /dev/null +++ b/homeassistant/components/tod/translations/hu.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "BE ut\u00e1n", + "after_time": "BE id\u0151pont", + "before": "KI ut\u00e1n", + "before_time": "KI id\u0151pont", + "name": "Elnevez\u00e9s" + }, + "description": "\u00c1ll\u00edtsa be, hogy az \u00e9rz\u00e9kel\u0151 mikor kapcsoljon be \u00e9s ki.", + "title": "I\u0151pontok \u00e9rz\u00e9kel\u0151 hozz\u00e1ad\u00e1sa" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "BE ut\u00e1n", + "after_time": "BE id\u0151pont", + "before": "KI ut\u00e1n", + "before_time": "KI id\u0151pont" + }, + "description": "\u00c1ll\u00edtsa be, hogy az \u00e9rz\u00e9kel\u0151 mikor kapcsoljon be \u00e9s ki." + } + } + }, + "title": "I\u0151pontok \u00e9rz\u00e9kel\u0151" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/id.json b/homeassistant/components/tod/translations/id.json new file mode 100644 index 00000000000..2ceef6c3a40 --- /dev/null +++ b/homeassistant/components/tod/translations/id.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Nyala setelah", + "after_time": "Nyala pada", + "before": "Mati setelah", + "before_time": "Mati pada", + "name": "Nama" + }, + "description": "Buat sensor biner yang nyala atau mati tergantung waktu.", + "title": "Tambahkan Sensor Waktu Pada Hari Ini" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Nyala setelah", + "after_time": "Nyala pada", + "before": "Mati setelah", + "before_time": "Mati pada" + }, + "description": "Buat sensor biner yang nyala atau mati tergantung waktu." + } + } + }, + "title": "Sensor Waktu Pada Hari Ini" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/it.json b/homeassistant/components/tod/translations/it.json new file mode 100644 index 00000000000..072cc80f5ff --- /dev/null +++ b/homeassistant/components/tod/translations/it.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Acceso dopo", + "after_time": "Ora di accensione", + "before": "Spento dopo", + "before_time": "Ora di spegnimento", + "name": "Nome" + }, + "description": "Crea un sensore binario che si accende o si spegne a seconda dell'ora.", + "title": "Aggiungi sensore delle ore del giorno" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Acceso dopo", + "after_time": "Ora di accensione", + "before": "Spento dopo", + "before_time": "Ora di spegnimento" + }, + "description": "Crea un sensore binario che si accende o si spegne a seconda dell'ora." + } + } + }, + "title": "Sensore ore del giorno" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/ja.json b/homeassistant/components/tod/translations/ja.json new file mode 100644 index 00000000000..a497f3e3c98 --- /dev/null +++ b/homeassistant/components/tod/translations/ja.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u5f8c\u306b\u30aa\u30f3(On after)", + "after_time": "\u30aa\u30f3\u30bf\u30a4\u30e0(On time)", + "before": "\u30aa\u30d5\u5f8c(Off after)", + "before_time": "\u30aa\u30d5\u30bf\u30a4\u30e0(Off time)", + "name": "\u540d\u524d" + }, + "description": "\u30bb\u30f3\u30b5\u30fc\u306e\u30aa\u30f3\u3068\u30aa\u30d5\u3092\u5207\u308a\u66ff\u3048\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u69cb\u6210\u3057\u307e\u3059\u3002", + "title": "\u65b0\u3057\u3044\u6642\u523b\u30bb\u30f3\u30b5\u30fc" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u5f8c\u306b\u30aa\u30f3(On after)", + "after_time": "\u30aa\u30f3\u30bf\u30a4\u30e0(On time)", + "before": "\u30aa\u30d5\u5f8c(Off after)", + "before_time": "\u30aa\u30d5\u30bf\u30a4\u30e0(Off time)" + }, + "description": "\u30bb\u30f3\u30b5\u30fc\u306e\u30aa\u30f3\u3068\u30aa\u30d5\u3092\u5207\u308a\u66ff\u3048\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u69cb\u6210\u3057\u307e\u3059\u3002" + } + } + }, + "title": "\u6642\u9593\u5e2f\u30bb\u30f3\u30b5\u30fc(Times of the Day Sensor)" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/nl.json b/homeassistant/components/tod/translations/nl.json new file mode 100644 index 00000000000..82f06ea3f4f --- /dev/null +++ b/homeassistant/components/tod/translations/nl.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Aan na", + "after_time": "Op tijd", + "before": "Uit na", + "before_time": "Uit tijd", + "name": "Naam\n" + }, + "description": "Maak een binaire sensor die afhankelijk van de tijd in- of uitgeschakeld wordt.", + "title": "Tijden van de dag sensor toevoegen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Aan na", + "after_time": "Op tijd", + "before": "Uit na", + "before_time": "Uit tijd" + }, + "description": "Maak een binaire sensor die afhankelijk van de tijd in- of uitgeschakeld wordt." + } + } + }, + "title": "Tijd van de dag Sensor" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/no.json b/homeassistant/components/tod/translations/no.json new file mode 100644 index 00000000000..a2cbf7e6427 --- /dev/null +++ b/homeassistant/components/tod/translations/no.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "P\u00e5 etter", + "after_time": "P\u00e5 tide", + "before": "Av etter", + "before_time": "Utenfor arbeidstid", + "name": "Navn" + }, + "description": "Lag en bin\u00e6r sensor som sl\u00e5s av eller p\u00e5 avhengig av tiden.", + "title": "Legg til klokkeslettsensor" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "P\u00e5 etter", + "after_time": "P\u00e5 tide", + "before": "Av etter", + "before_time": "Utenfor arbeidstid" + }, + "description": "Lag en bin\u00e6r sensor som sl\u00e5s av eller p\u00e5 avhengig av tiden." + } + } + }, + "title": "Tidssensor for dagen" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/pl.json b/homeassistant/components/tod/translations/pl.json new file mode 100644 index 00000000000..8b03a798f5c --- /dev/null +++ b/homeassistant/components/tod/translations/pl.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "W\u0142\u0105cz po", + "after_time": "Czas w\u0142\u0105czenia", + "before": "Wy\u0142\u0105cz po", + "before_time": "Czas wy\u0142\u0105czenia", + "name": "Nazwa" + }, + "description": "Utw\u00f3rz sensor binarny, kt\u00f3ry w\u0142\u0105cza si\u0119 lub wy\u0142\u0105cza w zale\u017cno\u015bci od czasu.", + "title": "Dodaj sensor p\u00f3r dnia" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "W\u0142\u0105cz po", + "after_time": "Czas w\u0142\u0105czenia", + "before": "Wy\u0142\u0105cz po", + "before_time": "Czas wy\u0142\u0105czenia" + }, + "description": "Utw\u00f3rz sensor binarny, kt\u00f3ry w\u0142\u0105cza si\u0119 lub wy\u0142\u0105cza w zale\u017cno\u015bci od czasu." + } + } + }, + "title": "Sensor p\u00f3r dnia" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/pt-BR.json b/homeassistant/components/tod/translations/pt-BR.json new file mode 100644 index 00000000000..e9784076fd7 --- /dev/null +++ b/homeassistant/components/tod/translations/pt-BR.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Ligado depois", + "after_time": "Ligado na hora", + "before": "Desligado depois", + "before_time": "Desligado na hora", + "name": "Nome" + }, + "description": "Crie um sensor bin\u00e1rio que liga ou desliga dependendo do tempo.", + "title": "Adicionar sensor de horas do dia" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Ligado depois", + "after_time": "Ligado na hora", + "before": "Desligado depois", + "before_time": "Desligado na hora" + }, + "description": "Crie um sensor bin\u00e1rio que liga ou desliga dependendo do tempo." + } + } + }, + "title": "Sensor de horas do dia" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/ru.json b/homeassistant/components/tod/translations/ru.json new file mode 100644 index 00000000000..eda3e4efd77 --- /dev/null +++ b/homeassistant/components/tod/translations/ru.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", + "after_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", + "before": "\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", + "before_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + }, + "description": "\u0411\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.", + "title": "\u0412\u0440\u0435\u043c\u044f \u0441\u0443\u0442\u043e\u043a" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", + "after_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", + "before": "\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", + "before_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" + }, + "description": "\u0411\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438." + } + } + }, + "title": "\u0412\u0440\u0435\u043c\u044f \u0441\u0443\u0442\u043e\u043a" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/tr.json b/homeassistant/components/tod/translations/tr.json new file mode 100644 index 00000000000..e2f04757369 --- /dev/null +++ b/homeassistant/components/tod/translations/tr.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "Daha sonra", + "after_time": "Vaktinde", + "before": "Sonra kapat", + "before_time": "Kapatma zaman\u0131", + "name": "Ad" + }, + "description": "Zamana ba\u011fl\u0131 olarak a\u00e7\u0131l\u0131p kapanan bir ikili sens\u00f6r olu\u015fturun.", + "title": "G\u00fcn\u00fcn Saatleri Sens\u00f6r\u00fc Ekleyin" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "Daha sonra", + "after_time": "Vaktinde", + "before": "Sonra kapat", + "before_time": "Kapatma zaman\u0131" + }, + "description": "Zamana ba\u011fl\u0131 olarak a\u00e7\u0131l\u0131p kapanan bir ikili sens\u00f6r olu\u015fturun." + } + } + }, + "title": "G\u00fcn\u00fcn Saatleri Sens\u00f6r\u00fc" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/zh-Hans.json b/homeassistant/components/tod/translations/zh-Hans.json new file mode 100644 index 00000000000..1c0b6ba1597 --- /dev/null +++ b/homeassistant/components/tod/translations/zh-Hans.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u5728\u6b64\u65f6\u95f4\u540e\u6253\u5f00", + "after_time": "\u6253\u5f00\u65f6\u95f4", + "before": "\u5728\u6b64\u65f6\u95f4\u540e\u5173\u95ed", + "before_time": "\u5173\u95ed\u65f6\u95f4", + "name": "\u540d\u79f0" + }, + "description": "\u521b\u5efa\u6839\u636e\u65f6\u95f4\u6539\u53d8\u5f00\u5173\u72b6\u6001\u7684\u4e8c\u5143\u4f20\u611f\u5668\u3002", + "title": "\u6dfb\u52a0\u65f6\u95f4\u8303\u56f4\u4f20\u611f\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u5728\u6b64\u65f6\u95f4\u540e\u6253\u5f00", + "after_time": "\u6253\u5f00\u65f6\u95f4", + "before": "\u5728\u6b64\u65f6\u95f4\u540e\u5173\u95ed", + "before_time": "\u5173\u95ed\u65f6\u95f4" + }, + "description": "\u521b\u5efa\u6839\u636e\u65f6\u95f4\u6539\u53d8\u5f00\u5173\u72b6\u6001\u7684\u4e8c\u5143\u4f20\u611f\u5668\u3002" + } + } + }, + "title": "\u65f6\u95f4\u8303\u56f4\u4f20\u611f\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/zh-Hant.json b/homeassistant/components/tod/translations/zh-Hant.json new file mode 100644 index 00000000000..0a47c33a318 --- /dev/null +++ b/homeassistant/components/tod/translations/zh-Hant.json @@ -0,0 +1,31 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after": "\u958b\u555f\u6642\u9577\u5f8c", + "after_time": "\u958b\u555f\u6642\u9593", + "before": "\u95dc\u9589\u6642\u9577\u5f8c", + "before_time": "\u95dc\u9589\u6642\u9593", + "name": "\u540d\u7a31" + }, + "description": "\u65b0\u589e\u6839\u64da\u6642\u9593\u6c7a\u5b9a\u958b\u95dc\u4e4b\u6642\u9593\u611f\u6e2c\u5668\u3002", + "title": "\u65b0\u589e\u6bcf\u65e5\u5b9a\u6642\u611f\u6e2c\u5668" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after": "\u958b\u555f\u6642\u9577\u5f8c", + "after_time": "\u958b\u555f\u6642\u9593", + "before": "\u95dc\u9589\u6642\u9577\u5f8c", + "before_time": "\u95dc\u9589\u6642\u9593" + }, + "description": "\u65b0\u589e\u6839\u64da\u6642\u9593\u6c7a\u5b9a\u958b\u95dc\u4e4b\u6642\u9593\u611f\u6e2c\u5668\u3002" + } + } + }, + "title": "\u6bcf\u65e5\u5b9a\u6642\u611f\u6e2c\u5668" +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/bg.json b/homeassistant/components/tomorrowio/translations/bg.json new file mode 100644 index 00000000000..783408dab84 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/bg.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", + "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", + "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/ca.json b/homeassistant/components/tomorrowio/translations/ca.json new file mode 100644 index 00000000000..fc351430ffb --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/ca.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_api_key": "Clau API inv\u00e0lida", + "rate_limited": "Freq\u00fc\u00e8ncia limitada temporalment, torna-ho a provar m\u00e9s tard.", + "unknown": "Error inesperat" + }, + "step": { + "user": { + "data": { + "api_key": "Clau API", + "latitude": "Latitud", + "location": "Ubicaci\u00f3", + "longitude": "Longitud", + "name": "Nom" + }, + "description": "Per obtenir una clau API, registra't a [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre previsions de NowCast" + }, + "description": "Si decideixes activar l'entitat de previsi\u00f3 `nowcast`, podr\u00e0s configurar l'interval en minuts entre cada previsi\u00f3. El nombre de previsions proporcionades dep\u00e8n d'aquest interval de minuts.", + "title": "Actualitza les opcions de Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/cs.json b/homeassistant/components/tomorrowio/translations/cs.json new file mode 100644 index 00000000000..6b50656cd00 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/cs.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "location": "Um\u00edst\u011bn\u00ed" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/de.json b/homeassistant/components/tomorrowio/translations/de.json new file mode 100644 index 00000000000..03739ce5c2f --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/de.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", + "rate_limited": "Aktuelle Aktualisierungsrate gedrosselt, bitte versuche es sp\u00e4ter erneut.", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "latitude": "Breitengrad", + "location": "Standort", + "longitude": "L\u00e4ngengrad", + "name": "Name" + }, + "description": "Um einen API-Schl\u00fcssel zu erhalten, melde dich bei [Tomorrow.io] (https://app.tomorrow.io/signup) an." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuten zwischen den NowCast Kurzvorhersagen" + }, + "description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.", + "title": "Tomorrow.io Optionen aktualisieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/el.json b/homeassistant/components/tomorrowio/translations/el.json new file mode 100644 index 00000000000..28e3f56c379 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/el.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", + "rate_limited": "\u0391\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae \u03b7 \u03c4\u03b9\u03bc\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1.", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2", + "location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1", + "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + }, + "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "\u0395\u03bb\u03ac\u03c7. \u039c\u03b5\u03c4\u03b1\u03be\u03cd NowCast Forecasts" + }, + "description": "\u0395\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c0\u03c1\u03cc\u03b2\u03bb\u03b5\u03c8\u03b7\u03c2 \"nowcast\", \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03ba\u03ac\u03b8\u03b5 \u03c0\u03c1\u03cc\u03b2\u03bb\u03b5\u03c8\u03b7\u03c2. \u039f \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03c9\u03bd \u03c0\u03c1\u03bf\u03b2\u03bb\u03ad\u03c8\u03b5\u03c9\u03bd \u03b5\u03be\u03b1\u03c1\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03b3\u03bf\u03bd\u03c4\u03b1\u03b9 \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03c0\u03c1\u03bf\u03b2\u03bb\u03ad\u03c8\u03b5\u03c9\u03bd.", + "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/en.json b/homeassistant/components/tomorrowio/translations/en.json index 7c653b00574..103f1c81679 100644 --- a/homeassistant/components/tomorrowio/translations/en.json +++ b/homeassistant/components/tomorrowio/translations/en.json @@ -11,6 +11,7 @@ "data": { "api_key": "API Key", "latitude": "Latitude", + "location": "Location", "longitude": "Longitude", "name": "Name" }, diff --git a/homeassistant/components/tomorrowio/translations/et.json b/homeassistant/components/tomorrowio/translations/et.json new file mode 100644 index 00000000000..7ac38239349 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/et.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_api_key": "Vigane API v\u00f5ti", + "rate_limited": "Hetkel on m\u00e4\u00e4r piiratud, proovi hiljem uuesti.", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "user": { + "data": { + "api_key": "API v\u00f5ti", + "latitude": "Laiuskraad", + "location": "Asukoht", + "longitude": "Pikkuskraad", + "name": "Nimi" + }, + "description": "API v\u00f5tme saamiseks registreeru aadressil [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Minuteid NowCasti prognooside vahel" + }, + "description": "Kui otsustad lubada olemi \"nowcast\", saad seadistada iga prognoosi vahelise minutite arvu. Esitatud prognooside arv s\u00f5ltub prognooside vahel valitud minutite arvust.", + "title": "V\u00e4rskenda Tomorrow.io valikuid" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/fr.json b/homeassistant/components/tomorrowio/translations/fr.json new file mode 100644 index 00000000000..bcb97eb3fc5 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/fr.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_api_key": "Cl\u00e9 d'API non valide", + "rate_limited": "Nombre maximal de tentatives de connexion d\u00e9pass\u00e9, veuillez r\u00e9essayer ult\u00e9rieurement.", + "unknown": "Erreur inattendue" + }, + "step": { + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "latitude": "Latitude", + "location": "Emplacement", + "longitude": "Longitude", + "name": "Nom" + }, + "description": "Pour obtenir une cl\u00e9 d'API, inscrivez-vous sur [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre les pr\u00e9visions NowCast" + }, + "description": "Si vous choisissez d'activer l'entit\u00e9 de pr\u00e9vision \u00ab\u00a0nowcast\u00a0\u00bb, vous pouvez configurer le nombre de minutes entre chaque pr\u00e9vision. Le nombre de pr\u00e9visions fournies d\u00e9pend du nombre de minutes choisies entre les pr\u00e9visions.", + "title": "Mettre \u00e0 jour les options de Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/he.json b/homeassistant/components/tomorrowio/translations/he.json new file mode 100644 index 00000000000..20b48520f18 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/he.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API", + "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", + "location": "\u05de\u05d9\u05e7\u05d5\u05dd", + "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", + "name": "\u05e9\u05dd" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/hu.json b/homeassistant/components/tomorrowio/translations/hu.json new file mode 100644 index 00000000000..8f90392234e --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/hu.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", + "rate_limited": "Jelenleg korl\u00e1tozott a hozz\u00e1f\u00e9r\u00e9s, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "user": { + "data": { + "api_key": "API kulcs", + "latitude": "Sz\u00e9less\u00e9g", + "location": "Elhelyezked\u00e9s", + "longitude": "Hossz\u00fas\u00e1g", + "name": "Elnevez\u00e9s" + }, + "description": "API-kulcs beszerz\u00e9s\u00e9hez regisztr\u00e1ljon a [Tomorrow.io] (https://app.tomorrow.io/signup) oldalon." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Percek NowCast el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt" + }, + "description": "Ha enged\u00e9lyezi a 'nowcast' id\u0151j\u00e1r\u00e1st, be\u00e1ll\u00edthatja az egyes el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt eltelt percek sz\u00e1m\u00e1t. Az el\u0151rejelz\u00e9sek sz\u00e1ma az el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt v\u00e1lasztott percek sz\u00e1m\u00e1t\u00f3l f\u00fcgg.", + "title": "Tomorrow.io be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/id.json b/homeassistant/components/tomorrowio/translations/id.json new file mode 100644 index 00000000000..b428648e799 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/id.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_api_key": "Kunci API tidak valid", + "rate_limited": "Saat ini tingkatnya dibatasi, coba lagi nanti.", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "user": { + "data": { + "api_key": "Kunci API", + "latitude": "Lintang", + "location": "Lokasi", + "longitude": "Bujur", + "name": "Nama" + }, + "description": "Untuk mendapatkan kunci API, daftar di [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Jarak Interval Prakiraan NowCast dalam Menit" + }, + "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan 'nowcast', Anda dapat mengonfigurasi jumlah menit antar-prakiraan. Jumlah prakiraan yang diberikan tergantung pada jumlah menit yang dipilih antar-prakiraan.", + "title": "Perbarui Opsi Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/it.json b/homeassistant/components/tomorrowio/translations/it.json new file mode 100644 index 00000000000..9a79896a3d8 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/it.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_api_key": "Chiave API non valida", + "rate_limited": "Attualmente la tariffa \u00e8 limitata, riprova pi\u00f9 tardi.", + "unknown": "Errore imprevisto" + }, + "step": { + "user": { + "data": { + "api_key": "Chiave API", + "latitude": "Latitudine", + "location": "Posizione", + "longitude": "Logitudine", + "name": "Nome" + }, + "description": "Per ottenere una chiave API, registrati su [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. tra le previsioni di NowCast" + }, + "description": "Se scegli di abilitare l'entit\u00e0 di previsione `nowcast`, puoi configurare il numero di minuti tra ciascuna previsione. Il numero di previsioni fornite dipende dal numero di minuti scelti tra le previsioni.", + "title": "Aggiorna le opzioni di Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/ja.json b/homeassistant/components/tomorrowio/translations/ja.json new file mode 100644 index 00000000000..17d31f74214 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/ja.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", + "rate_limited": "\u73fe\u5728\u30ec\u30fc\u30c8\u304c\u5236\u9650\u3055\u308c\u3066\u3044\u307e\u3059\u306e\u3067\u3001\u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "latitude": "\u7def\u5ea6", + "location": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3", + "longitude": "\u7d4c\u5ea6", + "name": "\u540d\u524d" + }, + "description": "API\u30ad\u30fc\u3092\u53d6\u5f97\u3059\u308b\u306b\u306f\u3001 [Tomorrow.io](https://app.tomorrow.io/signup) \u306b\u30b5\u30a4\u30f3\u30a2\u30c3\u30d7\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "\u6700\u5c0f: Between NowCast Forecasts" + }, + "description": "`nowcast` forecast(\u4e88\u6e2c) \u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u3092\u9078\u629e\u3057\u305f\u5834\u5408\u3001\u5404\u4e88\u6e2c\u9593\u306e\u5206\u6570\u3092\u8a2d\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u63d0\u4f9b\u3055\u308c\u308bforecast(\u4e88\u6e2c)\u306e\u6570\u306f\u3001forecast(\u4e88\u6e2c)\u306e\u9593\u306b\u9078\u629e\u3057\u305f\u5206\u6570\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002", + "title": "Tomorrow.io\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u66f4\u65b0" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/nl.json b/homeassistant/components/tomorrowio/translations/nl.json new file mode 100644 index 00000000000..d1efb7d75c3 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/nl.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_api_key": "Ongeldige API-sleutel", + "rate_limited": "Aantal aanvragen bereikt, probeer later opnieuw", + "unknown": "Onverwachte fout" + }, + "step": { + "user": { + "data": { + "api_key": "API-sleutel", + "latitude": "Breedtegraad", + "location": "Locatie", + "longitude": "Lengtegraad", + "name": "Naam" + }, + "description": "Om een API sleutel te krijgen, meld je aan bij [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Tussen NowCast Voorspellingen" + }, + "description": "Indien u ervoor kiest om de `nowcast` voorspellingsentiteit in te schakelen, kan u het aantal minuten tussen elke voorspelling configureren. Het aantal voorspellingen hangt af van het aantal gekozen minuten tussen de voorspellingen.", + "title": "Update Tomorrow.io Opties" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/no.json b/homeassistant/components/tomorrowio/translations/no.json new file mode 100644 index 00000000000..bf366895a70 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/no.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_api_key": "Ugyldig API-n\u00f8kkel", + "rate_limited": "For \u00f8yeblikket prisbegrenset, pr\u00f8v igjen senere.", + "unknown": "Uventet feil" + }, + "step": { + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "latitude": "Breddegrad", + "location": "Plassering", + "longitude": "Lengdegrad", + "name": "Navn" + }, + "description": "For \u00e5 f\u00e5 en API-n\u00f8kkel, registrer deg p\u00e5 [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Mellom NowCast-prognoser" + }, + "description": "Hvis du velger \u00e5 aktivere prognoseenheten \"nowcast\", kan du konfigurere antall minutter mellom hver prognose. Antallet prognoser som gis avhenger av antall minutter valgt mellom prognosene.", + "title": "Oppdater Tomorrow.io-alternativer" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/pl.json b/homeassistant/components/tomorrowio/translations/pl.json new file mode 100644 index 00000000000..4637a553aa4 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/pl.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_api_key": "Nieprawid\u0142owy klucz API", + "rate_limited": "Przekroczono limit, spr\u00f3buj ponownie p\u00f3\u017aniej.", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "api_key": "Klucz API", + "latitude": "Szeroko\u015b\u0107 geograficzna", + "location": "Lokalizacja", + "longitude": "D\u0142ugo\u015b\u0107 geograficzna", + "name": "Nazwa" + }, + "description": "Aby uzyska\u0107 klucz API, zarejestruj si\u0119 na [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Czas (min) mi\u0119dzy prognozami NowCast" + }, + "description": "Je\u015bli zdecydujesz si\u0119 w\u0142\u0105czy\u0107 encj\u0119 prognozy \u201enowcast\u201d, mo\u017cesz skonfigurowa\u0107 liczb\u0119 minut mi\u0119dzy ka\u017cd\u0105 prognoz\u0105. Liczba dostarczonych prognoz zale\u017cy od liczby minut wybranych mi\u0119dzy prognozami.", + "title": "Zaktualizuj opcje Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/pt-BR.json b/homeassistant/components/tomorrowio/translations/pt-BR.json new file mode 100644 index 00000000000..83f78e31b8e --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/pt-BR.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Falha ao conectar", + "invalid_api_key": "Chave de API inv\u00e1lida", + "rate_limited": "Taxa atualmente limitada. Tente novamente mais tarde.", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "api_key": "Chave da API", + "latitude": "Latitude", + "location": "Localiza\u00e7\u00e3o", + "longitude": "Longitude", + "name": "Nome" + }, + "description": "Para obter uma chave de API, inscreva-se em [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. Entre Previs\u00f5es NowCast" + }, + "description": "Se voc\u00ea optar por ativar a entidade de previs\u00e3o `nowcast`, poder\u00e1 configurar o n\u00famero de minutos entre cada previs\u00e3o. O n\u00famero de previs\u00f5es fornecidas depende do n\u00famero de minutos escolhidos entre as previs\u00f5es.", + "title": "Atualizar op\u00e7\u00f5es do Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/ru.json b/homeassistant/components/tomorrowio/translations/ru.json new file mode 100644 index 00000000000..08e24c5a47a --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/ru.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", + "rate_limited": "\u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", + "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + }, + "description": "\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435\u0441\u044c \u043d\u0430 [Tomorrow.io](https://app.tomorrow.io/signup)." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430\u043c\u0438 NowCast (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)" + }, + "description": "\u0415\u0441\u043b\u0438 \u0412\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 'nowcast', \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430. \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u043e\u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430\u043c\u0438.", + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Tomorrow.io" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.bg.json b/homeassistant/components/tomorrowio/translations/sensor.bg.json new file mode 100644 index 00000000000..1a5c2989702 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.bg.json @@ -0,0 +1,8 @@ +{ + "state": { + "tomorrowio__precipitation_type": { + "rain": "\u0414\u044a\u0436\u0434", + "snow": "\u0421\u043d\u044f\u0433" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.ca.json b/homeassistant/components/tomorrowio/translations/sensor.ca.json new file mode 100644 index 00000000000..161978d1919 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.ca.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Bo", + "hazardous": "Perill\u00f3s", + "moderate": "Moderat", + "unhealthy": "Poc saludable", + "unhealthy_for_sensitive_groups": "No saludable per a grups sensibles", + "very_unhealthy": "Gens saludable" + }, + "tomorrowio__pollen_index": { + "high": "Alt", + "low": "Baix", + "medium": "Mitj\u00e0", + "none": "Cap", + "very_high": "Molt alt", + "very_low": "Molt baix" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Pluja congelada", + "ice_pellets": "Gran\u00eds", + "none": "Cap", + "rain": "Pluja", + "snow": "Neu" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.de.json b/homeassistant/components/tomorrowio/translations/sensor.de.json new file mode 100644 index 00000000000..801833520f7 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.de.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Gut", + "hazardous": "Gef\u00e4hrlich", + "moderate": "M\u00e4\u00dfig", + "unhealthy": "Ungesund", + "unhealthy_for_sensitive_groups": "Ungesund f\u00fcr sensible Gruppen", + "very_unhealthy": "Sehr ungesund" + }, + "tomorrowio__pollen_index": { + "high": "Hoch", + "low": "Niedrig", + "medium": "Mittel", + "none": "Keiner", + "very_high": "Sehr hoch", + "very_low": "Sehr niedrig" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Gefrierender Regen", + "ice_pellets": "Graupel", + "none": "Keiner", + "rain": "Regen", + "snow": "Schnee" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.el.json b/homeassistant/components/tomorrowio/translations/sensor.el.json new file mode 100644 index 00000000000..fd4ca22f8d0 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.el.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "\u039a\u03b1\u03bb\u03cc", + "hazardous": "\u0395\u03c0\u03b9\u03ba\u03af\u03bd\u03b4\u03c5\u03bd\u03bf", + "moderate": "\u039c\u03ad\u03c4\u03c1\u03b9\u03bf", + "unhealthy": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc", + "unhealthy_for_sensitive_groups": "\u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc \u03b3\u03b9\u03b1 \u03b5\u03c5\u03b1\u03af\u03c3\u03b8\u03b7\u03c4\u03b5\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b5\u03c2", + "very_unhealthy": "\u03a0\u03bf\u03bb\u03cd \u0391\u03bd\u03b8\u03c5\u03b3\u03b9\u03b5\u03b9\u03bd\u03cc" + }, + "tomorrowio__pollen_index": { + "high": "\u03a5\u03c8\u03b7\u03bb\u03cc", + "low": "\u03a7\u03b1\u03bc\u03b7\u03bb\u03cc", + "medium": "\u039c\u03b5\u03c3\u03b1\u03af\u03bf", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "very_high": "\u03a0\u03bf\u03bb\u03cd \u03c5\u03c8\u03b7\u03bb\u03cc", + "very_low": "\u03a0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03cc" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "\u03a0\u03b1\u03b3\u03c9\u03bc\u03ad\u03bd\u03b7 \u03b2\u03c1\u03bf\u03c7\u03ae", + "ice_pellets": "\u03a7\u03b1\u03bb\u03ac\u03b6\u03b9", + "none": "\u03a4\u03af\u03c0\u03bf\u03c4\u03b1", + "rain": "\u0392\u03c1\u03bf\u03c7\u03ae", + "snow": "\u03a7\u03b9\u03cc\u03bd\u03b9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.et.json b/homeassistant/components/tomorrowio/translations/sensor.et.json new file mode 100644 index 00000000000..56482b21e12 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.et.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Hea", + "hazardous": "Ohtlik", + "moderate": "M\u00f5\u00f5dukas", + "unhealthy": "Ebatervislik", + "unhealthy_for_sensitive_groups": "Ebatervislik riskir\u00fchmale", + "very_unhealthy": "V\u00e4ga ebatervislik" + }, + "tomorrowio__pollen_index": { + "high": "K\u00f5rge", + "low": "Madal", + "medium": "Keskmine", + "none": "Saastet pole", + "very_high": "V\u00e4ga k\u00f5rge", + "very_low": "V\u00e4ga madal" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "J\u00e4\u00e4vihm", + "ice_pellets": "J\u00e4\u00e4kruubid", + "none": "Sademeid pole", + "rain": "Vihm", + "snow": "Lumi" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.fr.json b/homeassistant/components/tomorrowio/translations/sensor.fr.json new file mode 100644 index 00000000000..c0e2ee8747d --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.fr.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Bon", + "hazardous": "Dangereux", + "moderate": "Mod\u00e9r\u00e9", + "unhealthy": "Malsain", + "unhealthy_for_sensitive_groups": "Malsain pour les groupes sensibles", + "very_unhealthy": "Tr\u00e8s malsain" + }, + "tomorrowio__pollen_index": { + "high": "\u00c9lev\u00e9", + "low": "Faible", + "medium": "Moyen", + "none": "Nul", + "very_high": "Tr\u00e8s \u00e9lev\u00e9", + "very_low": "Tr\u00e8s faible" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Pluie vergla\u00e7ante", + "ice_pellets": "Gr\u00e9sil", + "none": "Aucune", + "rain": "Pluie", + "snow": "Neige" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.hu.json b/homeassistant/components/tomorrowio/translations/sensor.hu.json new file mode 100644 index 00000000000..3377ecb47dd --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.hu.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "J\u00f3", + "hazardous": "Vesz\u00e9lyes", + "moderate": "M\u00e9rs\u00e9kelt", + "unhealthy": "Eg\u00e9szs\u00e9gtelen", + "unhealthy_for_sensitive_groups": "Eg\u00e9szs\u00e9gtelen \u00e9rz\u00e9keny csoportok sz\u00e1m\u00e1ra", + "very_unhealthy": "Nagyon eg\u00e9szs\u00e9gtelen" + }, + "tomorrowio__pollen_index": { + "high": "Magas", + "low": "Alacsony", + "medium": "K\u00f6zepes", + "none": "Nincs", + "very_high": "Nagyon magas", + "very_low": "Nagyon alacsony" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Havas es\u0151", + "ice_pellets": "\u00d3nos es\u0151", + "none": "Nincs", + "rain": "Es\u0151", + "snow": "Havaz\u00e1s" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.id.json b/homeassistant/components/tomorrowio/translations/sensor.id.json new file mode 100644 index 00000000000..6323cf6beef --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.id.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Bagus", + "hazardous": "Berbahaya", + "moderate": "Sedang", + "unhealthy": "Tidak Sehat", + "unhealthy_for_sensitive_groups": "Tidak Sehat untuk Kelompok Sensitif", + "very_unhealthy": "Sangat Tidak Sehat" + }, + "tomorrowio__pollen_index": { + "high": "Tinggi", + "low": "Rendah", + "medium": "Sedang", + "none": "Tidak Ada", + "very_high": "Sangat Tinggi", + "very_low": "Sangat Rendah" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Hujan Beku", + "ice_pellets": "Hujan Es", + "none": "Tidak Ada", + "rain": "Hujan", + "snow": "Salju" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.it.json b/homeassistant/components/tomorrowio/translations/sensor.it.json new file mode 100644 index 00000000000..b8f281a462c --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.it.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Buono", + "hazardous": "Pericoloso", + "moderate": "Moderato", + "unhealthy": "Malsano", + "unhealthy_for_sensitive_groups": "Malsano per i gruppi sensibili", + "very_unhealthy": "Molto malsano" + }, + "tomorrowio__pollen_index": { + "high": "Alto", + "low": "Basso", + "medium": "Medio", + "none": "Nessuno", + "very_high": "Molto alto", + "very_low": "Molto basso" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Pioggia gelata", + "ice_pellets": "Grandine", + "none": "Nessuna", + "rain": "Pioggia", + "snow": "Neve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.ja.json b/homeassistant/components/tomorrowio/translations/sensor.ja.json new file mode 100644 index 00000000000..286be339435 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.ja.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "\u826f\u3044", + "hazardous": "\u5371\u967a", + "moderate": "\u9069\u5ea6", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_for_sensitive_groups": "\u654f\u611f\u306a\u30b0\u30eb\u30fc\u30d7\u306b\u3068\u3063\u3066\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u3068\u3066\u3082\u4e0d\u5065\u5eb7" + }, + "tomorrowio__pollen_index": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "none": "\u306a\u3057", + "very_high": "\u975e\u5e38\u306b\u9ad8\u3044", + "very_low": "\u3068\u3066\u3082\u4f4e\u3044" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "\u51cd\u3066\u3064\u304f\u96e8", + "ice_pellets": "\u51cd\u96e8", + "none": "\u306a\u3057", + "rain": "\u96e8", + "snow": "\u96ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.nl.json b/homeassistant/components/tomorrowio/translations/sensor.nl.json new file mode 100644 index 00000000000..5ca31721f94 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.nl.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Goed", + "hazardous": "Gevaarlijk", + "moderate": "Gematigd", + "unhealthy": "Ongezond", + "unhealthy_for_sensitive_groups": "Ongezond voor gevoelige groepen", + "very_unhealthy": "Zeer ongezond" + }, + "tomorrowio__pollen_index": { + "high": "Hoog", + "low": "Laag", + "medium": "Medium", + "none": "Geen", + "very_high": "Zeer Hoog", + "very_low": "Zeer Laag" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "IJzel", + "ice_pellets": "Hagel", + "none": "Geen", + "rain": "Regen", + "snow": "Sneeuw" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.no.json b/homeassistant/components/tomorrowio/translations/sensor.no.json new file mode 100644 index 00000000000..2eeabe5b569 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.no.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Bra", + "hazardous": "Farlig", + "moderate": "Moderat", + "unhealthy": "Usunt", + "unhealthy_for_sensitive_groups": "Usunt for sensitive grupper", + "very_unhealthy": "Veldig usunt" + }, + "tomorrowio__pollen_index": { + "high": "H\u00f8y", + "low": "Lav", + "medium": "Medium", + "none": "Ingen", + "very_high": "Veldig h\u00f8y", + "very_low": "Veldig lav" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Underkj\u00f8lt regn", + "ice_pellets": "Is tapper", + "none": "Ingen", + "rain": "Regn", + "snow": "Sn\u00f8" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.pl.json b/homeassistant/components/tomorrowio/translations/sensor.pl.json new file mode 100644 index 00000000000..7140524fc0b --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.pl.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "dobre", + "hazardous": "niebezpieczne", + "moderate": "umiarkowane", + "unhealthy": "niezdrowe", + "unhealthy_for_sensitive_groups": "niezdrowe dla wra\u017cliwych grup", + "very_unhealthy": "bardzo niezdrowe" + }, + "tomorrowio__pollen_index": { + "high": "wysoki", + "low": "niski", + "medium": "\u015bredni", + "none": "brak", + "very_high": "bardzo wysoki", + "very_low": "bardzo niski" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "marzn\u0105cy deszcz", + "ice_pellets": "granulki lodu", + "none": "brak", + "rain": "deszcz", + "snow": "\u015bnieg" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.pt-BR.json b/homeassistant/components/tomorrowio/translations/sensor.pt-BR.json new file mode 100644 index 00000000000..b63572d731d --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.pt-BR.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "Bom", + "hazardous": "Perigoso", + "moderate": "Moderado", + "unhealthy": "Insalubre", + "unhealthy_for_sensitive_groups": "Insalubre para grupos sens\u00edveis", + "very_unhealthy": "Muito Insalubre" + }, + "tomorrowio__pollen_index": { + "high": "Alto", + "low": "Baixo", + "medium": "M\u00e9dio", + "none": "Nenhum", + "very_high": "Muito alto", + "very_low": "Muito baixo" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Chuva congelante", + "ice_pellets": "Pelotas de Gelo", + "none": "Nenhum", + "rain": "Chuva", + "snow": "Neve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.ru.json b/homeassistant/components/tomorrowio/translations/sensor.ru.json new file mode 100644 index 00000000000..f9be80a4bad --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.ru.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "\u0425\u043e\u0440\u043e\u0448\u043e", + "hazardous": "\u041e\u043f\u0430\u0441\u043d\u043e", + "moderate": "\u0421\u0440\u0435\u0434\u043d\u0435", + "unhealthy": "\u0412\u0440\u0435\u0434\u043d\u043e", + "unhealthy_for_sensitive_groups": "\u0412\u0440\u0435\u0434\u043d\u043e \u0434\u043b\u044f \u0443\u044f\u0437\u0432\u0438\u043c\u044b\u0445 \u0433\u0440\u0443\u043f\u043f", + "very_unhealthy": "\u041e\u0447\u0435\u043d\u044c \u0432\u0440\u0435\u0434\u043d\u043e" + }, + "tomorrowio__pollen_index": { + "high": "\u0412\u044b\u0441\u043e\u043a\u0438\u0439", + "low": "\u041d\u0438\u0437\u043a\u0438\u0439", + "medium": "\u0421\u0440\u0435\u0434\u043d\u0438\u0439", + "none": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442", + "very_high": "\u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0438\u0439", + "very_low": "\u041e\u0447\u0435\u043d\u044c \u043d\u0438\u0437\u043a\u0438\u0439" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "\u041b\u0435\u0434\u044f\u043d\u043e\u0439 \u0434\u043e\u0436\u0434\u044c", + "ice_pellets": "\u041b\u0435\u0434\u044f\u043d\u0430\u044f \u043a\u0440\u0443\u043f\u0430", + "none": "\u0411\u0435\u0437 \u043e\u0441\u0430\u0434\u043a\u043e\u0432", + "rain": "\u0414\u043e\u0436\u0434\u044c", + "snow": "\u0421\u043d\u0435\u0433" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.tr.json b/homeassistant/components/tomorrowio/translations/sensor.tr.json new file mode 100644 index 00000000000..553d72f6178 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.tr.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "\u0130yi", + "hazardous": "Tehlikeli", + "moderate": "Il\u0131ml\u0131", + "unhealthy": "Sa\u011fl\u0131ks\u0131z", + "unhealthy_for_sensitive_groups": "Hassas Gruplar \u0130\u00e7in Sa\u011fl\u0131ks\u0131z", + "very_unhealthy": "\u00c7ok Sa\u011fl\u0131ks\u0131z" + }, + "tomorrowio__pollen_index": { + "high": "Y\u00fcksek", + "low": "D\u00fc\u015f\u00fck", + "medium": "Orta", + "none": "Hi\u00e7biri", + "very_high": "\u00c7ok Y\u00fcksek", + "very_low": "\u00c7ok D\u00fc\u015f\u00fck" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "Dondurucu Ya\u011fmur", + "ice_pellets": "Buz Peletleri", + "none": "Hi\u00e7biri", + "rain": "Ya\u011fmur", + "snow": "Kar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.zh-Hant.json b/homeassistant/components/tomorrowio/translations/sensor.zh-Hant.json new file mode 100644 index 00000000000..6cc5c1a42dd --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.zh-Hant.json @@ -0,0 +1,27 @@ +{ + "state": { + "tomorrowio__health_concern": { + "good": "\u826f\u597d", + "hazardous": "\u5371\u96aa", + "moderate": "\u4e2d\u7b49", + "unhealthy": "\u4e0d\u5065\u5eb7", + "unhealthy_for_sensitive_groups": "\u5c0d\u654f\u611f\u65cf\u7fa4\u4e0d\u5065\u5eb7", + "very_unhealthy": "\u975e\u5e38\u4e0d\u5065\u5eb7" + }, + "tomorrowio__pollen_index": { + "high": "\u9ad8", + "low": "\u4f4e", + "medium": "\u4e2d", + "none": "\u7121", + "very_high": "\u6975\u9ad8", + "very_low": "\u6975\u4f4e" + }, + "tomorrowio__precipitation_type": { + "freezing_rain": "\u51cd\u96e8", + "ice_pellets": "\u51b0\u73e0", + "none": "\u7121", + "rain": "\u4e0b\u96e8", + "snow": "\u4e0b\u96ea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/tr.json b/homeassistant/components/tomorrowio/translations/tr.json new file mode 100644 index 00000000000..4193428459c --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/tr.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", + "rate_limited": "\u015eu anda oran s\u0131n\u0131rl\u0131, l\u00fctfen daha sonra tekrar deneyin.", + "unknown": "Beklenmeyen hata" + }, + "step": { + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "latitude": "Enlem", + "location": "Konum", + "longitude": "Boylam", + "name": "Ad" + }, + "description": "API anahtar\u0131 almak i\u00e7in [Tomorrow.io](https://app.tomorrow.io/signup) adresinden kaydolun." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "NowCast Tahminleri Aras\u0131nda Min." + }, + "description": "\"\u015eimdi yay\u0131n\" tahmin varl\u0131\u011f\u0131n\u0131 etkinle\u015ftirmeyi se\u00e7erseniz, her tahmin aras\u0131ndaki dakika say\u0131s\u0131n\u0131 yap\u0131land\u0131rabilirsiniz. Sa\u011flanan tahminlerin say\u0131s\u0131, tahminler aras\u0131nda se\u00e7ilen dakika say\u0131s\u0131na ba\u011fl\u0131d\u0131r.", + "title": "Tomorrow.io Se\u00e7eneklerini G\u00fcncelleyin" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/zh-Hant.json b/homeassistant/components/tomorrowio/translations/zh-Hant.json new file mode 100644 index 00000000000..bf7043b0225 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/zh-Hant.json @@ -0,0 +1,33 @@ +{ + "config": { + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_api_key": "API \u91d1\u9470\u7121\u6548", + "rate_limited": "\u9054\u5230\u9650\u5236\u983b\u7387\u3001\u8acb\u7a0d\u5019\u518d\u8a66\u3002", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "latitude": "\u7def\u5ea6", + "location": "\u5ea7\u6a19", + "longitude": "\u7d93\u5ea6", + "name": "\u540d\u7a31" + }, + "description": "\u8acb\u53c3\u95b1\u7db2\u5740\u4ee5\u4e86\u89e3\u5982\u4f55\u53d6\u5f97 API \u91d1\u9470\uff1a[Tomorrow.io](https://app.tomorrow.io/signup)\u3002" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "NowCast \u9810\u5831\u9593\u9694\u5206\u9418" + }, + "description": "\u5047\u5982\u9078\u64c7\u958b\u555f `nowcast` \u9810\u5831\u5be6\u9ad4\u3001\u5c07\u53ef\u4ee5\u8a2d\u5b9a\u9810\u5831\u983b\u7387\u9593\u9694\u5206\u9418\u6578\u3002\u6839\u64da\u6240\u8f38\u5165\u7684\u9593\u9694\u6642\u9593\u5c07\u6c7a\u5b9a\u9810\u5831\u7684\u6578\u76ee\u3002", + "title": "\u66f4\u65b0 Tomorrow.io \u9078\u9805" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/translations/hu.json b/homeassistant/components/toon/translations/hu.json index b1a69144dd5..a9192ed013a 100644 --- a/homeassistant/components/toon/translations/hu.json +++ b/homeassistant/components/toon/translations/hu.json @@ -5,7 +5,7 @@ "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", "no_agreements": "Ennek a fi\u00f3knak nincsenek Toon kijelz\u0151i.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", "unknown_authorize_url_generation": "Ismeretlen hiba t\u00f6rt\u00e9nt a hiteles\u00edt\u00e9si link gener\u00e1l\u00e1sa sor\u00e1n." }, "step": { diff --git a/homeassistant/components/toon/translations/it.json b/homeassistant/components/toon/translations/it.json index 724c61dba3c..9125bac09db 100644 --- a/homeassistant/components/toon/translations/it.json +++ b/homeassistant/components/toon/translations/it.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "L'accordo selezionato \u00e8 gi\u00e0 configurato.", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_agreements": "Questo account non ha display Toon.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", "unknown_authorize_url_generation": "Errore sconosciuto durante la generazione di un URL di autorizzazione." diff --git a/homeassistant/components/tplink/translations/it.json b/homeassistant/components/tplink/translations/it.json index 3fd30d8d12c..6ab46615373 100644 --- a/homeassistant/components/tplink/translations/it.json +++ b/homeassistant/components/tplink/translations/it.json @@ -25,7 +25,7 @@ "data": { "host": "Host" }, - "description": "Se si lascia vuoto l'host, l'individuazione verr\u00e0 utilizzata per trovare i dispositivi." + "description": "Se si lascia vuoto l'host, l'individuazione sar\u00e0 utilizzata per trovare i dispositivi." } } } diff --git a/homeassistant/components/tradfri/translations/hu.json b/homeassistant/components/tradfri/translations/hu.json index 01bb51b0af3..03a8ddd8fa6 100644 --- a/homeassistant/components/tradfri/translations/hu.json +++ b/homeassistant/components/tradfri/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van" + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve" }, "error": { "cannot_authenticate": "Sikertelen hiteles\u00edt\u00e9s. A Gateway egy m\u00e1sik eszk\u00f6zzel van p\u00e1ros\u00edtva, mint p\u00e9ld\u00e1ul a Homekittel?", diff --git a/homeassistant/components/trafikverket_ferry/translations/ca.json b/homeassistant/components/trafikverket_ferry/translations/ca.json new file mode 100644 index 00000000000..b3983a45e2b --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/ca.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Clau API" + } + }, + "user": { + "data": { + "api_key": "Clau API", + "weekday": "Laborables" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/de.json b/homeassistant/components/trafikverket_ferry/translations/de.json new file mode 100644 index 00000000000..298a0e1711c --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/de.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "incorrect_api_key": "Ung\u00fcltiger API-Schl\u00fcssel f\u00fcr ausgew\u00e4hltes Konto", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "invalid_route": "Konnte keine Route mit den angegebenen Informationen finden" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-Schl\u00fcssel" + } + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "from": "Vom Hafen", + "time": "Zeit", + "to": "Zum Hafen", + "weekday": "Wochentage" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/el.json b/homeassistant/components/trafikverket_ferry/translations/el.json new file mode 100644 index 00000000000..552e8ad0160 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/el.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "incorrect_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "invalid_route": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2 \u03bc\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + } + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "from": "\u0391\u03c0\u03cc \u03c4\u03bf \u039b\u03b9\u03bc\u03ac\u03bd\u03b9", + "time": "\u038f\u03c1\u03b1", + "to": "\u03a0\u03c1\u03bf\u03c2 \u039b\u03b9\u03bc\u03ac\u03bd\u03b9", + "weekday": "\u0395\u03c1\u03b3\u03ac\u03c3\u03b9\u03bc\u03b5\u03c2" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/et.json b/homeassistant/components/trafikverket_ferry/translations/et.json new file mode 100644 index 00000000000..ec791013788 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/et.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Konto on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "incorrect_api_key": "Valitud konto API-v\u00f5ti on kehtetu", + "invalid_auth": "Tuvastamine nurjus", + "invalid_route": "Esitatud teabega marsruuti ei leitud" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API v\u00f5ti" + } + }, + "user": { + "data": { + "api_key": "API v\u00f5ti", + "from": "Sadamast", + "time": "Kellaaeg", + "to": "Sadamasse", + "weekday": "N\u00e4dalap\u00e4ev" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/fr.json b/homeassistant/components/trafikverket_ferry/translations/fr.json new file mode 100644 index 00000000000..4288f56908f --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/fr.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "incorrect_api_key": "Cl\u00e9 d'API non valide pour le compte s\u00e9lectionn\u00e9", + "invalid_auth": "Authentification non valide", + "invalid_route": "Impossible de trouver un itin\u00e9raire avec les informations fournies" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Cl\u00e9 d'API" + } + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "from": "Port de d\u00e9part", + "time": "Heure", + "to": "Port d'arriv\u00e9e", + "weekday": "Jours de la semaine" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/he.json b/homeassistant/components/trafikverket_ferry/translations/he.json new file mode 100644 index 00000000000..29f4fc720da --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/he.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + }, + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/hu.json b/homeassistant/components/trafikverket_ferry/translations/hu.json new file mode 100644 index 00000000000..88b2b2ebfa7 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/hu.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "incorrect_api_key": "\u00c9rv\u00e9nytelen API-kulcs a megadott fi\u00f3khoz", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "invalid_route": "Nem tal\u00e1lhat\u00f3 \u00fatvonal a megadott inform\u00e1ci\u00f3k alapj\u00e1n" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API kulcs" + } + }, + "user": { + "data": { + "api_key": "API kulcs", + "from": "Kik\u00f6t\u0151b\u0151l", + "time": "Id\u0151pont", + "to": "Kik\u00f6t\u0151be", + "weekday": "H\u00e9tk\u00f6znap" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/id.json b/homeassistant/components/trafikverket_ferry/translations/id.json new file mode 100644 index 00000000000..1d476f2c0e1 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/id.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "incorrect_api_key": "Kunci API tidak valid untuk akun yang dipilih", + "invalid_auth": "Autentikasi tidak valid", + "invalid_route": "Tidak dapat menemukan rute dengan informasi yang ditentukan" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Kunci API" + } + }, + "user": { + "data": { + "api_key": "Kunci API", + "from": "Dari Pelabuhan", + "time": "Waktu", + "to": "Ke Pelabuhan", + "weekday": "Hari kerja" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/it.json b/homeassistant/components/trafikverket_ferry/translations/it.json new file mode 100644 index 00000000000..160953d9f82 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/it.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "incorrect_api_key": "Chiave API non valida per l'account selezionato", + "invalid_auth": "Autenticazione non valida", + "invalid_route": "Impossibile trovare il percorso con le informazioni fornite" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chiave API" + } + }, + "user": { + "data": { + "api_key": "Chiave API", + "from": "Dal porto", + "time": "Orario", + "to": "Al porto", + "weekday": "Giorni della settimana" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/nl.json b/homeassistant/components/trafikverket_ferry/translations/nl.json new file mode 100644 index 00000000000..f84232839c8 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/nl.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "incorrect_api_key": "Ongeldige API-sleutel voor geselecteerde account", + "invalid_auth": "Ongeldige authenticatie", + "invalid_route": "Kon geen route vinden met verstrekte informatie" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-sleutel" + } + }, + "user": { + "data": { + "api_key": "API-sleutel", + "from": "Van de haven", + "time": "Tijd", + "to": "Naar de haven", + "weekday": "Weekdagen" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/no.json b/homeassistant/components/trafikverket_ferry/translations/no.json new file mode 100644 index 00000000000..4cc6286d78f --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/no.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "incorrect_api_key": "Ugyldig API-n\u00f8kkel for valgt konto", + "invalid_auth": "Ugyldig godkjenning", + "invalid_route": "Kunne ikke finne rute med oppgitt informasjon" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-n\u00f8kkel" + } + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "from": "Fra havnen", + "time": "Tid", + "to": "Til havn", + "weekday": "Hverdager" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/pl.json b/homeassistant/components/trafikverket_ferry/translations/pl.json new file mode 100644 index 00000000000..5e7d133b1e5 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/pl.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "incorrect_api_key": "Nieprawid\u0142owy klucz API dla wybranego konta", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_route": "Nie mo\u017cna znale\u017a\u0107 trasy dla podanych informacjami" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Klucz API" + } + }, + "user": { + "data": { + "api_key": "Klucz API", + "from": "Z portu", + "time": "Czas", + "to": "Do portu", + "weekday": "Dni powszednie" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/pt-BR.json b/homeassistant/components/trafikverket_ferry/translations/pt-BR.json new file mode 100644 index 00000000000..e2349c181c8 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/pt-BR.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "incorrect_api_key": "Chave de API inv\u00e1lida para a conta selecionada", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_route": "N\u00e3o foi poss\u00edvel encontrar a rota com as informa\u00e7\u00f5es fornecidas" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + } + }, + "user": { + "data": { + "api_key": "Chave da API", + "from": "Do porto", + "time": "Tempo", + "to": "Ao porto", + "weekday": "Dias \u00fateis" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/ru.json b/homeassistant/components/trafikverket_ferry/translations/ru.json new file mode 100644 index 00000000000..c87b6e941f3 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/ru.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "incorrect_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API \u0434\u043b\u044f \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "invalid_route": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439." + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + } + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "from": "\u0418\u0437 \u0433\u0430\u0432\u0430\u043d\u0438", + "time": "\u0412\u0440\u0435\u043c\u044f", + "to": "\u0412 \u0433\u0430\u0432\u0430\u043d\u044c", + "weekday": "\u0411\u0443\u0434\u043d\u0438\u0435 \u0434\u043d\u0438" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/zh-Hant.json b/homeassistant/components/trafikverket_ferry/translations/zh-Hant.json new file mode 100644 index 00000000000..9acd15fdfb1 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/zh-Hant.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "incorrect_api_key": "\u6240\u9078\u64c7\u5e33\u865f\u4e4b API \u91d1\u9470\u7121\u6548", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_route": "\u627e\u4e0d\u5230\u63d0\u4f9b\u8cc7\u8a0a\u4e4b\u8def\u7dda" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u91d1\u9470" + } + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "from": "\u81ea\u6d77\u6e2f", + "time": "\u6642\u9593", + "to": "\u81f3\u6d77\u6e2f", + "weekday": "\u5e73\u65e5" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/bg.json b/homeassistant/components/trafikverket_train/translations/bg.json new file mode 100644 index 00000000000..91bb9c04e35 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/bg.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "weekday": "\u0414\u043d\u0438" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/ca.json b/homeassistant/components/trafikverket_train/translations/ca.json new file mode 100644 index 00000000000..ed9a28841a2 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/ca.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "incorrect_api_key": "Clau API inv\u00e0lida per al compte seleccionat", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_station": "No s'ha trobat cap estaci\u00f3 amb el nom especificat", + "invalid_time": "Hora proporcionada inv\u00e0lida", + "more_stations": "S'han trobat m\u00faltiples estacions amb el nom especificat" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Clau API" + } + }, + "user": { + "data": { + "api_key": "Clau API", + "from": "Des de l'estaci\u00f3", + "time": "Hora (opcional)", + "to": "A l'estaci\u00f3", + "weekday": "Dies" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/de.json b/homeassistant/components/trafikverket_train/translations/de.json new file mode 100644 index 00000000000..057a416d70c --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/de.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "incorrect_api_key": "Ung\u00fcltiger API-Schl\u00fcssel f\u00fcr ausgew\u00e4hltes Konto", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "invalid_station": "Es konnte kein Bahnhof mit dem angegebenen Namen gefunden werden", + "invalid_time": "Ung\u00fcltige Zeitangabe", + "more_stations": "Mehrere Bahnh\u00f6fe mit dem angegebenen Namen gefunden" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-Schl\u00fcssel" + } + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "from": "Vom Bahnhof", + "time": "Zeit (optional)", + "to": "Zum Bahnhof", + "weekday": "Tage" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/el.json b/homeassistant/components/trafikverket_train/translations/el.json new file mode 100644 index 00000000000..1dea0a7a8da --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/el.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "incorrect_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "invalid_station": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd \u03bc\u03b5 \u03c4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1", + "invalid_time": "\u0394\u03cc\u03b8\u03b7\u03ba\u03b5 \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03ce\u03c1\u03b1", + "more_stations": "\u0392\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03bf\u03af \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03af \u03bc\u03b5 \u03c4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + } + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "from": "\u0391\u03c0\u03cc \u03c3\u03c4\u03b1\u03b8\u03bc\u03cc", + "time": "\u03a7\u03c1\u03cc\u03bd\u03bf\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", + "to": "\u03a0\u03c1\u03bf\u03c2 \u03c3\u03c4\u03b1\u03b8\u03bc\u03cc", + "weekday": "\u0397\u03bc\u03ad\u03c1\u03b5\u03c2" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/en.json b/homeassistant/components/trafikverket_train/translations/en.json index dc91c2097f3..86969fc7d96 100644 --- a/homeassistant/components/trafikverket_train/translations/en.json +++ b/homeassistant/components/trafikverket_train/translations/en.json @@ -6,26 +6,26 @@ }, "error": { "cannot_connect": "Failed to connect", + "incorrect_api_key": "Invalid API key for selected account", "invalid_auth": "Invalid authentication", "invalid_station": "Could not find a station with the specified name", - "more_stations": "Found multiple stations with the specified name", "invalid_time": "Invalid time provided", - "incorrect_api_key": "Invalid API key for selected account" + "more_stations": "Found multiple stations with the specified name" }, "step": { - "user": { - "data": { - "api_key": "API Key", - "to": "To station", - "from": "From station", - "time": "Time (optional)", - "weekday": "Days" - } - }, "reauth_confirm": { "data": { "api_key": "API Key" } + }, + "user": { + "data": { + "api_key": "API Key", + "from": "From station", + "time": "Time (optional)", + "to": "To station", + "weekday": "Days" + } } } } diff --git a/homeassistant/components/trafikverket_train/translations/et.json b/homeassistant/components/trafikverket_train/translations/et.json new file mode 100644 index 00000000000..9712adacce7 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/et.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Konto on juba seadistatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "incorrect_api_key": "Valitud konto API-v\u00f5ti on kehtetu", + "invalid_auth": "Tuvastamine nurjus", + "invalid_station": "M\u00e4\u00e4ratud nimega jaama ei leitud.", + "invalid_time": "Esitatud on kehtetu aeg", + "more_stations": "Leiti mitu m\u00e4\u00e4ratud nimega jaama" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API v\u00f5ti" + } + }, + "user": { + "data": { + "api_key": "API v\u00f5ti", + "from": "L\u00e4htejaam", + "time": "Aeg (valikuline)", + "to": "Sihtjaam", + "weekday": "N\u00e4dalap\u00e4evad" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/fr.json b/homeassistant/components/trafikverket_train/translations/fr.json new file mode 100644 index 00000000000..357edca46fa --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/fr.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "incorrect_api_key": "Cl\u00e9 d'API non valide pour le compte s\u00e9lectionn\u00e9", + "invalid_auth": "Authentification non valide", + "invalid_station": "Aucune gare portant le nom sp\u00e9cifi\u00e9 n'a \u00e9t\u00e9 trouv\u00e9e", + "invalid_time": "Heure fournie non valide", + "more_stations": "Plusieurs gares portant le nom sp\u00e9cifi\u00e9 ont \u00e9t\u00e9 trouv\u00e9es" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Cl\u00e9 d'API" + } + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "from": "Gare de d\u00e9part", + "time": "Heure (facultatif)", + "to": "Gare d'arriv\u00e9e", + "weekday": "Jours" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/he.json b/homeassistant/components/trafikverket_train/translations/he.json new file mode 100644 index 00000000000..29f4fc720da --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/he.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + }, + "user": { + "data": { + "api_key": "\u05de\u05e4\u05ea\u05d7 API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/hu.json b/homeassistant/components/trafikverket_train/translations/hu.json new file mode 100644 index 00000000000..ff4a4ee7142 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/hu.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "incorrect_api_key": "\u00c9rv\u00e9nytelen API-kulcs a megadott fi\u00f3khoz", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "invalid_station": "Nem tal\u00e1lhat\u00f3 megadott nev\u0171 \u00e1llom\u00e1s", + "invalid_time": "\u00c9rv\u00e9nytelen megadott id\u0151", + "more_stations": "T\u00f6bb \u00e1llom\u00e1s tal\u00e1lhat\u00f3 a megadott n\u00e9vvel" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API kulcs" + } + }, + "user": { + "data": { + "api_key": "API kulcs", + "from": "\u00c1llom\u00e1sr\u00f3l", + "time": "Id\u0151 (opcion\u00e1lis)", + "to": "\u00c1llom\u00e1sig", + "weekday": "Napok" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/id.json b/homeassistant/components/trafikverket_train/translations/id.json new file mode 100644 index 00000000000..4723183257c --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/id.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "incorrect_api_key": "Kunci API tidak valid untuk akun yang dipilih", + "invalid_auth": "Autentikasi tidak valid", + "invalid_station": "Tidak dapat menemukan SPBU dengan nama yang ditentukan", + "invalid_time": "Waktu yang disediakan tidak valid", + "more_stations": "Ditemukan beberapa SPBU dengan nama yang ditentukan" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Kunci API" + } + }, + "user": { + "data": { + "api_key": "Kunci API", + "from": "Dari stasiun", + "time": "Waktu (opsional)", + "to": "Ke stasiun", + "weekday": "Hari" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/it.json b/homeassistant/components/trafikverket_train/translations/it.json new file mode 100644 index 00000000000..fe9c37e8e17 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/it.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "incorrect_api_key": "Chiave API non valida per l'account selezionato", + "invalid_auth": "Autenticazione non valida", + "invalid_station": "Impossibile trovare una stazione con il nome specificato", + "invalid_time": "Tempo fornito non valido", + "more_stations": "Trovate pi\u00f9 stazioni con il nome specificato" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chiave API" + } + }, + "user": { + "data": { + "api_key": "Chiave API", + "from": "Dalla stazione", + "time": "Tempo (facoltativo)", + "to": "Alla stazione", + "weekday": "Tariffe supportate" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/ja.json b/homeassistant/components/trafikverket_train/translations/ja.json new file mode 100644 index 00000000000..ec4b5a4015a --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/ja.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "incorrect_api_key": "\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306eAPI\u30ad\u30fc\u304c\u7121\u52b9\u3067\u3059", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "invalid_station": "\u6307\u5b9a\u3055\u308c\u305f\u540d\u524d\u306e\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f", + "invalid_time": "\u7121\u52b9\u306a\u6642\u9593\u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f", + "more_stations": "\u6307\u5b9a\u3055\u308c\u305f\u540d\u524d\u306e\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3092\u8907\u6570\u767a\u898b" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API\u30ad\u30fc" + } + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "from": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u304b\u3089", + "time": "\u6642\u9593\uff08\u30aa\u30d7\u30b7\u30e7\u30f3\uff09", + "to": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u3078", + "weekday": "\u65e5" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/nl.json b/homeassistant/components/trafikverket_train/translations/nl.json new file mode 100644 index 00000000000..d076f8b2961 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/nl.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "incorrect_api_key": "Ongeldige API-sleutel voor geselecteerd account", + "invalid_auth": "Ongeldige authenticatie", + "invalid_station": "Kon geen station vinden met de opgegeven naam", + "invalid_time": "Ongeldige tijd opgegeven", + "more_stations": "Meerdere stations gevonden met de opgegeven naam" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-sleutel" + } + }, + "user": { + "data": { + "api_key": "API-sleutel", + "from": "Van station", + "time": "Tijd (optioneel)", + "to": "Naar station", + "weekday": "Dagen" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/no.json b/homeassistant/components/trafikverket_train/translations/no.json new file mode 100644 index 00000000000..12feb2f6abf --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/no.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "incorrect_api_key": "Ugyldig API-n\u00f8kkel for valgt konto", + "invalid_auth": "Ugyldig godkjenning", + "invalid_station": "Kunne ikke finne en stasjon med det angitte navnet", + "invalid_time": "Ugyldig tid oppgitt", + "more_stations": "Fant flere stasjoner med det angitte navnet" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API-n\u00f8kkel" + } + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "from": "Fra stasjon", + "time": "Tid (valgfritt)", + "to": "Til stasjon", + "weekday": "Dager" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/pl.json b/homeassistant/components/trafikverket_train/translations/pl.json new file mode 100644 index 00000000000..0ff0f49c701 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/pl.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "incorrect_api_key": "Nieprawid\u0142owy klucz API dla wybranego konta", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_station": "Nie mo\u017cna znale\u017a\u0107 stacji o podanej nazwie", + "invalid_time": "Podano nieprawid\u0142owy czas", + "more_stations": "Znaleziono wiele stacji o podanej nazwie" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Klucz API" + } + }, + "user": { + "data": { + "api_key": "Klucz API", + "from": "Ze stacji", + "time": "Czas (opcjonalnie)", + "to": "Do stacji", + "weekday": "Dni" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/pt-BR.json b/homeassistant/components/trafikverket_train/translations/pt-BR.json new file mode 100644 index 00000000000..43dcf558a18 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/pt-BR.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 foi configurada", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falha ao conectar", + "incorrect_api_key": "Chave de API inv\u00e1lida para a conta selecionada", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_station": "N\u00e3o foi poss\u00edvel encontrar uma esta\u00e7\u00e3o com o nome especificado", + "invalid_time": "Tempo inv\u00e1lido fornecido", + "more_stations": "Encontrado v\u00e1rias esta\u00e7\u00f5es com o nome especificado" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Chave da API" + } + }, + "user": { + "data": { + "api_key": "Chave da API", + "from": "Da esta\u00e7\u00e3o", + "time": "Tempo (opcional)", + "to": "Para esta\u00e7\u00e3o", + "weekday": "Dias" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/ru.json b/homeassistant/components/trafikverket_train/translations/ru.json new file mode 100644 index 00000000000..27a0b1d6393 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/ru.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "incorrect_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API \u0434\u043b\u044f \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "invalid_station": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0441\u0442\u0430\u043d\u0446\u0438\u044e \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c.", + "invalid_time": "\u0423\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f.", + "more_stations": "\u041d\u0430\u0439\u0434\u0435\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u043d\u0446\u0438\u0439 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c." + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + } + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "from": "\u041e\u0442 \u0441\u0442\u0430\u043d\u0446\u0438\u0438", + "time": "\u0412\u0440\u0435\u043c\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "to": "\u041d\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044e", + "weekday": "\u0414\u043d\u0438" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/tr.json b/homeassistant/components/trafikverket_train/translations/tr.json new file mode 100644 index 00000000000..9e7f21b5685 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/tr.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "incorrect_api_key": "Se\u00e7ilen hesap i\u00e7in ge\u00e7ersiz API anahtar\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "invalid_station": "Belirtilen ada sahip bir istasyon bulunamad\u0131", + "invalid_time": "Ge\u00e7ersiz s\u00fcre sa\u011fland\u0131", + "more_stations": "Belirtilen ada sahip birden fazla istasyon bulundu" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API Anahtar\u0131" + } + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "from": "\u0130stasyondan", + "time": "Zaman (iste\u011fe ba\u011fl\u0131)", + "to": "\u0130stasyona", + "weekday": "G\u00fcn" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/zh-Hant.json b/homeassistant/components/trafikverket_train/translations/zh-Hant.json new file mode 100644 index 00000000000..be3d51c19fd --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/zh-Hant.json @@ -0,0 +1,32 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "incorrect_api_key": "\u6240\u9078\u64c7\u5e33\u865f\u4e4b API \u91d1\u9470\u7121\u6548", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_station": "\u627e\u4e0d\u5230\u8a72\u6307\u5b9a\u540d\u7a31\u4e4b\u8eca\u7ad9", + "invalid_time": "\u63d0\u4f9b\u6642\u9593\u7121\u6548", + "more_stations": "\u6307\u5b9a\u540d\u7a31\u627e\u5230\u591a\u500b\u8eca\u7ad9" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u91d1\u9470" + } + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "from": "\u51fa\u767c\u52a0\u6cb9\u7ad9", + "time": "\u6642\u9593\uff08\u9078\u9805\uff09", + "to": "\u62b5\u9054\u52a0\u6cb9\u7ad9", + "weekday": "\u5929" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/transmission/translations/hu.json b/homeassistant/components/transmission/translations/hu.json index 5e3dcfd2b6c..79a60dc2b5b 100644 --- a/homeassistant/components/transmission/translations/hu.json +++ b/homeassistant/components/transmission/translations/hu.json @@ -12,7 +12,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" diff --git a/homeassistant/components/tuya/translations/select.he.json b/homeassistant/components/tuya/translations/select.he.json index f31e63515c8..5a29489bdcc 100644 --- a/homeassistant/components/tuya/translations/select.he.json +++ b/homeassistant/components/tuya/translations/select.he.json @@ -98,7 +98,7 @@ "power_on": "\u05de\u05d5\u05e4\u05e2\u05dc" }, "tuya__vacuum_cistern": { - "closed": "\u05e1\u05d2\u05d5\u05e8", + "closed": "\u05e0\u05e1\u05d2\u05e8", "high": "\u05d2\u05d1\u05d5\u05d4", "low": "\u05e0\u05de\u05d5\u05da", "middle": "\u05d0\u05de\u05e6\u05e2" diff --git a/homeassistant/components/tuya/translations/select.pt-BR.json b/homeassistant/components/tuya/translations/select.pt-BR.json index aed86dfe4ce..90ae768c418 100644 --- a/homeassistant/components/tuya/translations/select.pt-BR.json +++ b/homeassistant/components/tuya/translations/select.pt-BR.json @@ -109,19 +109,19 @@ "small": "Pequeno" }, "tuya__vacuum_mode": { - "bow": "", + "bow": "Bow", "chargego": "Retornar para Base", - "left_bow": "", - "left_spiral": "", + "left_bow": "Bow Left", + "left_spiral": "Spiral Left", "mop": "Esfregar (Mop)", "part": "Parcial", - "partial_bow": "", + "partial_bow": "Bow Partially", "pick_zone": "C\u00f4modos Selecionados", "point": "Ponto", "pose": "Ponto Definido", "random": "Aleat\u00f3rio", - "right_bow": "", - "right_spiral": "", + "right_bow": "Bow Right", + "right_spiral": "Spiral Right", "single": "Simples", "smart": "Autom\u00e1tica", "spiral": "Espiral", diff --git a/homeassistant/components/twentemilieu/translations/bg.json b/homeassistant/components/twentemilieu/translations/bg.json index 8844b6124e0..6ddc25a367e 100644 --- a/homeassistant/components/twentemilieu/translations/bg.json +++ b/homeassistant/components/twentemilieu/translations/bg.json @@ -10,8 +10,7 @@ "house_number": "\u041d\u043e\u043c\u0435\u0440 \u043d\u0430 \u043a\u044a\u0449\u0430", "post_code": "\u041f\u043e\u0449\u0435\u043d\u0441\u043a\u0438 \u043a\u043e\u0434" }, - "description": "\u0421\u044a\u0437\u0434\u0430\u0439\u0442\u0435 Twente Milieu, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u044f\u0449\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0441\u044a\u0431\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043e\u0442\u043f\u0430\u0434\u044a\u0446\u0438 \u043d\u0430 \u0432\u0430\u0448\u0438\u044f \u0430\u0434\u0440\u0435\u0441.", - "title": "Twente Milieu" + "description": "\u0421\u044a\u0437\u0434\u0430\u0439\u0442\u0435 Twente Milieu, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u044f\u0449\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0437\u0430 \u0441\u044a\u0431\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043e\u0442\u043f\u0430\u0434\u044a\u0446\u0438 \u043d\u0430 \u0432\u0430\u0448\u0438\u044f \u0430\u0434\u0440\u0435\u0441." } } } diff --git a/homeassistant/components/twentemilieu/translations/ca.json b/homeassistant/components/twentemilieu/translations/ca.json index 6c1469f16c1..c9a3b0bb6cb 100644 --- a/homeassistant/components/twentemilieu/translations/ca.json +++ b/homeassistant/components/twentemilieu/translations/ca.json @@ -14,8 +14,7 @@ "house_number": "N\u00famero de casa", "post_code": "Codi postal" }, - "description": "Configura Twente Milieu amb informaci\u00f3 de la recollida de residus a la teva adre\u00e7a.", - "title": "Twente Milieu" + "description": "Configura Twente Milieu amb informaci\u00f3 de la recollida de residus a la teva adre\u00e7a." } } } diff --git a/homeassistant/components/twentemilieu/translations/cs.json b/homeassistant/components/twentemilieu/translations/cs.json index 2eb6e267b2c..edcb6a22594 100644 --- a/homeassistant/components/twentemilieu/translations/cs.json +++ b/homeassistant/components/twentemilieu/translations/cs.json @@ -12,8 +12,7 @@ "house_letter": "Roz\u0161\u00ed\u0159en\u00ed ozna\u010den\u00ed \u010d\u00edsla domu", "house_number": "\u010c\u00edslo domu", "post_code": "PS\u010c" - }, - "title": "Twente Milieu" + } } } } diff --git a/homeassistant/components/twentemilieu/translations/da.json b/homeassistant/components/twentemilieu/translations/da.json index e1c9b6024ad..6930a37adb4 100644 --- a/homeassistant/components/twentemilieu/translations/da.json +++ b/homeassistant/components/twentemilieu/translations/da.json @@ -10,8 +10,7 @@ "house_number": "Husnummer", "post_code": "Postnummer" }, - "description": "Konfigurer Twente Milieu, der leverer oplysninger om indsamling af affald p\u00e5 din adresse.", - "title": "Twente Milieu" + "description": "Konfigurer Twente Milieu, der leverer oplysninger om indsamling af affald p\u00e5 din adresse." } } } diff --git a/homeassistant/components/twentemilieu/translations/de.json b/homeassistant/components/twentemilieu/translations/de.json index 4ce9ed23ea7..36ea2123bdb 100644 --- a/homeassistant/components/twentemilieu/translations/de.json +++ b/homeassistant/components/twentemilieu/translations/de.json @@ -14,8 +14,7 @@ "house_number": "Hausnummer", "post_code": "Postleitzahl" }, - "description": "Richte Twente Milieu mit Informationen zur Abfallsammlung unter deiner Adresse ein.", - "title": "Twente Milieu" + "description": "Richte Twente Milieu mit Informationen zur Abfallsammlung unter deiner Adresse ein." } } } diff --git a/homeassistant/components/twentemilieu/translations/el.json b/homeassistant/components/twentemilieu/translations/el.json index f4949d3832d..d8cdd9672bd 100644 --- a/homeassistant/components/twentemilieu/translations/el.json +++ b/homeassistant/components/twentemilieu/translations/el.json @@ -14,8 +14,7 @@ "house_number": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd", "post_code": "\u03a4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc\u03c2 \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2" }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf Twente Milieu \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bb\u03bb\u03bf\u03b3\u03ae \u03b1\u03c0\u03bf\u03b2\u03bb\u03ae\u03c4\u03c9\u03bd \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03ae \u03c3\u03b1\u03c2.", - "title": "Twente Milieu" + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf Twente Milieu \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03bb\u03bb\u03bf\u03b3\u03ae \u03b1\u03c0\u03bf\u03b2\u03bb\u03ae\u03c4\u03c9\u03bd \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03ae \u03c3\u03b1\u03c2." } } } diff --git a/homeassistant/components/twentemilieu/translations/en.json b/homeassistant/components/twentemilieu/translations/en.json index fdc4023f89b..ac5f9e54b2e 100644 --- a/homeassistant/components/twentemilieu/translations/en.json +++ b/homeassistant/components/twentemilieu/translations/en.json @@ -14,8 +14,7 @@ "house_number": "House number", "post_code": "Postal code" }, - "description": "Set up Twente Milieu providing waste collection information on your address.", - "title": "Twente Milieu" + "description": "Set up Twente Milieu providing waste collection information on your address." } } } diff --git a/homeassistant/components/twentemilieu/translations/es-419.json b/homeassistant/components/twentemilieu/translations/es-419.json index 8adddffd407..7eb541978d3 100644 --- a/homeassistant/components/twentemilieu/translations/es-419.json +++ b/homeassistant/components/twentemilieu/translations/es-419.json @@ -10,8 +10,7 @@ "house_number": "N\u00famero de casa", "post_code": "C\u00f3digo postal" }, - "description": "Configure Twente Milieu proporcionando informaci\u00f3n de recolecci\u00f3n de residuos en su direcci\u00f3n.", - "title": "Twente Milieu" + "description": "Configure Twente Milieu proporcionando informaci\u00f3n de recolecci\u00f3n de residuos en su direcci\u00f3n." } } } diff --git a/homeassistant/components/twentemilieu/translations/es.json b/homeassistant/components/twentemilieu/translations/es.json index cf8410c55ad..259d202a9c3 100644 --- a/homeassistant/components/twentemilieu/translations/es.json +++ b/homeassistant/components/twentemilieu/translations/es.json @@ -14,8 +14,7 @@ "house_number": "N\u00famero de casa", "post_code": "C\u00f3digo postal" }, - "description": "Configure Twente Milieu proporcionando informaci\u00f3n sobre la recolecci\u00f3n de residuos en su direcci\u00f3n.", - "title": "Twente Milieu" + "description": "Configure Twente Milieu proporcionando informaci\u00f3n sobre la recolecci\u00f3n de residuos en su direcci\u00f3n." } } } diff --git a/homeassistant/components/twentemilieu/translations/et.json b/homeassistant/components/twentemilieu/translations/et.json index a77657d19ff..31c8ea6ee95 100644 --- a/homeassistant/components/twentemilieu/translations/et.json +++ b/homeassistant/components/twentemilieu/translations/et.json @@ -14,8 +14,7 @@ "house_number": "Maja number", "post_code": "Sihtnumber" }, - "description": "Seadista Twente Milieu, mis pakub j\u00e4\u00e4tmekogumist teie aadressil.", - "title": "" + "description": "Seadista Twente Milieu, mis pakub j\u00e4\u00e4tmekogumist teie aadressil." } } } diff --git a/homeassistant/components/twentemilieu/translations/fr.json b/homeassistant/components/twentemilieu/translations/fr.json index 6530216a5a4..560fcb89c29 100644 --- a/homeassistant/components/twentemilieu/translations/fr.json +++ b/homeassistant/components/twentemilieu/translations/fr.json @@ -14,8 +14,7 @@ "house_number": "Num\u00e9ro de maison", "post_code": "Code postal" }, - "description": "Configurez Twente Milieu en fournissant des informations sur la collecte des d\u00e9chets sur votre adresse.", - "title": "Twente Milieu" + "description": "Configurez Twente Milieu en fournissant des informations sur la collecte des d\u00e9chets sur votre adresse." } } } diff --git a/homeassistant/components/twentemilieu/translations/hu.json b/homeassistant/components/twentemilieu/translations/hu.json index 637dadb5baf..97a0cc8f022 100644 --- a/homeassistant/components/twentemilieu/translations/hu.json +++ b/homeassistant/components/twentemilieu/translations/hu.json @@ -14,8 +14,7 @@ "house_number": "h\u00e1zsz\u00e1m", "post_code": "ir\u00e1ny\u00edt\u00f3sz\u00e1m" }, - "description": "\u00c1ll\u00edtsa be a Twente Milieu szolg\u00e1ltat\u00e1st, amely hullad\u00e9kgy\u0171jt\u00e9si inform\u00e1ci\u00f3kat biztos\u00edt a c\u00edm\u00e9re.", - "title": "Twente Milieu" + "description": "\u00c1ll\u00edtsa be a Twente Milieu szolg\u00e1ltat\u00e1st, amely hullad\u00e9kgy\u0171jt\u00e9si inform\u00e1ci\u00f3kat biztos\u00edt a c\u00edm\u00e9re." } } } diff --git a/homeassistant/components/twentemilieu/translations/id.json b/homeassistant/components/twentemilieu/translations/id.json index 38746dfd12f..60148dc396c 100644 --- a/homeassistant/components/twentemilieu/translations/id.json +++ b/homeassistant/components/twentemilieu/translations/id.json @@ -14,8 +14,7 @@ "house_number": "Nomor rumah", "post_code": "Kode pos" }, - "description": "Siapkan Twente Milieu untuk memberikan informasi pengumpulan sampah di alamat Anda.", - "title": "Twente Milieu" + "description": "Siapkan Twente Milieu untuk memberikan informasi pengumpulan sampah di alamat Anda." } } } diff --git a/homeassistant/components/twentemilieu/translations/it.json b/homeassistant/components/twentemilieu/translations/it.json index a374885e7aa..7648d24b5e6 100644 --- a/homeassistant/components/twentemilieu/translations/it.json +++ b/homeassistant/components/twentemilieu/translations/it.json @@ -14,8 +14,7 @@ "house_number": "Numero civico", "post_code": "CAP" }, - "description": "Imposta Twente Milieu fornendo le informazioni sulla raccolta dei rifiuti al tuo indirizzo.", - "title": "Twente Milieu" + "description": "Imposta Twente Milieu fornendo le informazioni sulla raccolta dei rifiuti al tuo indirizzo." } } } diff --git a/homeassistant/components/twentemilieu/translations/ja.json b/homeassistant/components/twentemilieu/translations/ja.json index 8ec65b24fb5..5cd78fbba3c 100644 --- a/homeassistant/components/twentemilieu/translations/ja.json +++ b/homeassistant/components/twentemilieu/translations/ja.json @@ -14,8 +14,7 @@ "house_number": "\u5bb6\u5c4b\u756a\u53f7", "post_code": "\u90f5\u4fbf\u756a\u53f7" }, - "description": "\u3042\u306a\u305f\u306e\u4f4f\u6240\u306eTwente Milieu providing waste collection(\u30b4\u30df\u53ce\u96c6\u60c5\u5831)\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7", - "title": "Twente Milieu" + "description": "\u3042\u306a\u305f\u306e\u4f4f\u6240\u306eTwente Milieu providing waste collection(\u30b4\u30df\u53ce\u96c6\u60c5\u5831)\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" } } } diff --git a/homeassistant/components/twentemilieu/translations/ko.json b/homeassistant/components/twentemilieu/translations/ko.json index a27df565f1b..985650ff15b 100644 --- a/homeassistant/components/twentemilieu/translations/ko.json +++ b/homeassistant/components/twentemilieu/translations/ko.json @@ -14,8 +14,7 @@ "house_number": "\uc9d1 \ubc88\ud638", "post_code": "\uc6b0\ud3b8\ubc88\ud638" }, - "description": "\uc8fc\uc18c\uc5d0 \uc4f0\ub808\uae30 \uc218\uac70 \uc815\ubcf4\ub97c \ub123\uc5b4 Twente Milieu \ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694.", - "title": "Twente Milieu" + "description": "\uc8fc\uc18c\uc5d0 \uc4f0\ub808\uae30 \uc218\uac70 \uc815\ubcf4\ub97c \ub123\uc5b4 Twente Milieu \ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/twentemilieu/translations/lb.json b/homeassistant/components/twentemilieu/translations/lb.json index 98454f62dbb..ca1591417e7 100644 --- a/homeassistant/components/twentemilieu/translations/lb.json +++ b/homeassistant/components/twentemilieu/translations/lb.json @@ -14,8 +14,7 @@ "house_number": "Haus Nummer", "post_code": "Postleitzuel" }, - "description": "Offallsammlung Informatiounen vun Twente Milieu zu \u00e4erer Adresse ariichten.", - "title": "Twente Milieu" + "description": "Offallsammlung Informatiounen vun Twente Milieu zu \u00e4erer Adresse ariichten." } } } diff --git a/homeassistant/components/twentemilieu/translations/nl.json b/homeassistant/components/twentemilieu/translations/nl.json index 54611aa9ab8..575c642a777 100644 --- a/homeassistant/components/twentemilieu/translations/nl.json +++ b/homeassistant/components/twentemilieu/translations/nl.json @@ -14,8 +14,7 @@ "house_number": "Huisnummer", "post_code": "Postcode" }, - "description": "Stel Twente Milieu in voor het inzamelen van afval op uw adres.", - "title": "Twente Milieu" + "description": "Stel Twente Milieu in voor het inzamelen van afval op uw adres." } } } diff --git a/homeassistant/components/twentemilieu/translations/nn.json b/homeassistant/components/twentemilieu/translations/nn.json deleted file mode 100644 index ed333bb9b51..00000000000 --- a/homeassistant/components/twentemilieu/translations/nn.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Twente Milieu" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/twentemilieu/translations/no.json b/homeassistant/components/twentemilieu/translations/no.json index ea7df786247..76a023e44e3 100644 --- a/homeassistant/components/twentemilieu/translations/no.json +++ b/homeassistant/components/twentemilieu/translations/no.json @@ -14,8 +14,7 @@ "house_number": "Husnummer", "post_code": "Postnummer" }, - "description": "Sett opp Twente Milieu som gir informasjon om innsamling av avfall p\u00e5 adressen din.", - "title": "" + "description": "Sett opp Twente Milieu som gir informasjon om innsamling av avfall p\u00e5 adressen din." } } } diff --git a/homeassistant/components/twentemilieu/translations/pl.json b/homeassistant/components/twentemilieu/translations/pl.json index 67f2d2b362b..d7f53bb9bf1 100644 --- a/homeassistant/components/twentemilieu/translations/pl.json +++ b/homeassistant/components/twentemilieu/translations/pl.json @@ -14,8 +14,7 @@ "house_number": "Numer domu", "post_code": "Kod pocztowy" }, - "description": "Skonfiguruj Twente Milieu, dostarczaj\u0105c informacji o zbieraniu odpad\u00f3w pod swoim adresem.", - "title": "Twente Milieu" + "description": "Skonfiguruj Twente Milieu, dostarczaj\u0105c informacji o zbieraniu odpad\u00f3w pod swoim adresem." } } } diff --git a/homeassistant/components/twentemilieu/translations/pt-BR.json b/homeassistant/components/twentemilieu/translations/pt-BR.json index 943b46849b6..8a7fa74b554 100644 --- a/homeassistant/components/twentemilieu/translations/pt-BR.json +++ b/homeassistant/components/twentemilieu/translations/pt-BR.json @@ -14,8 +14,7 @@ "house_number": "N\u00famero da casa", "post_code": "C\u00f3digo postal" }, - "description": "Configure o Twente Milieu, fornecendo informa\u00e7\u00f5es de coleta de lixo em seu endere\u00e7o.", - "title": "Twente Milieu" + "description": "Configure o Twente Milieu, fornecendo informa\u00e7\u00f5es de coleta de lixo em seu endere\u00e7o." } } } diff --git a/homeassistant/components/twentemilieu/translations/ru.json b/homeassistant/components/twentemilieu/translations/ru.json index f7da9b628fd..33748e9d16b 100644 --- a/homeassistant/components/twentemilieu/translations/ru.json +++ b/homeassistant/components/twentemilieu/translations/ru.json @@ -14,8 +14,7 @@ "house_number": "\u041d\u043e\u043c\u0435\u0440 \u0434\u043e\u043c\u0430", "post_code": "\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 Twente Milieu \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0432\u044b\u0432\u043e\u0437\u0435 \u043c\u0443\u0441\u043e\u0440\u0430 \u043f\u043e \u0412\u0430\u0448\u0435\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443.", - "title": "Twente Milieu" + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 Twente Milieu \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0432\u044b\u0432\u043e\u0437\u0435 \u043c\u0443\u0441\u043e\u0440\u0430 \u043f\u043e \u0412\u0430\u0448\u0435\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443." } } } diff --git a/homeassistant/components/twentemilieu/translations/sl.json b/homeassistant/components/twentemilieu/translations/sl.json index affe8269613..313a5ae5a87 100644 --- a/homeassistant/components/twentemilieu/translations/sl.json +++ b/homeassistant/components/twentemilieu/translations/sl.json @@ -10,8 +10,7 @@ "house_number": "Hi\u0161na \u0161tevilka", "post_code": "Po\u0161tna \u0161tevilka" }, - "description": "Nastavite Twente milieu, ki zagotavlja informacije o zbiranju odpadkov na va\u0161em naslovu.", - "title": "Twente Milieu" + "description": "Nastavite Twente milieu, ki zagotavlja informacije o zbiranju odpadkov na va\u0161em naslovu." } } } diff --git a/homeassistant/components/twentemilieu/translations/sv.json b/homeassistant/components/twentemilieu/translations/sv.json index fa1481f7e10..4e8bb592d05 100644 --- a/homeassistant/components/twentemilieu/translations/sv.json +++ b/homeassistant/components/twentemilieu/translations/sv.json @@ -10,8 +10,7 @@ "house_number": "Husnummer", "post_code": "Postnummer" }, - "description": "St\u00e4ll in Twente Milieu som ger information om avfallshantering p\u00e5 din adress.", - "title": "Twente Milieu" + "description": "St\u00e4ll in Twente Milieu som ger information om avfallshantering p\u00e5 din adress." } } } diff --git a/homeassistant/components/twentemilieu/translations/tr.json b/homeassistant/components/twentemilieu/translations/tr.json index 363128d9c1e..9f03d2c9189 100644 --- a/homeassistant/components/twentemilieu/translations/tr.json +++ b/homeassistant/components/twentemilieu/translations/tr.json @@ -14,8 +14,7 @@ "house_number": "Ev numaras\u0131", "post_code": "Posta kodu" }, - "description": "Adresinizde at\u0131k toplama bilgileri sa\u011flayan Twente Milieu'yu kurun.", - "title": "Twente Milieu" + "description": "Adresinizde at\u0131k toplama bilgileri sa\u011flayan Twente Milieu'yu kurun." } } } diff --git a/homeassistant/components/twentemilieu/translations/uk.json b/homeassistant/components/twentemilieu/translations/uk.json index 435bd79fb85..5fa562a9afa 100644 --- a/homeassistant/components/twentemilieu/translations/uk.json +++ b/homeassistant/components/twentemilieu/translations/uk.json @@ -14,8 +14,7 @@ "house_number": "\u041d\u043e\u043c\u0435\u0440 \u0431\u0443\u0434\u0438\u043d\u043a\u0443", "post_code": "\u041f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Twente Milieu \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u0432\u0438\u0432\u0435\u0437\u0435\u043d\u043d\u044f \u0441\u043c\u0456\u0442\u0442\u044f \u0437\u0430 \u0412\u0430\u0448\u043e\u044e \u0430\u0434\u0440\u0435\u0441\u043e\u044e.", - "title": "Twente Milieu" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Twente Milieu \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u0432\u0438\u0432\u0435\u0437\u0435\u043d\u043d\u044f \u0441\u043c\u0456\u0442\u0442\u044f \u0437\u0430 \u0412\u0430\u0448\u043e\u044e \u0430\u0434\u0440\u0435\u0441\u043e\u044e." } } } diff --git a/homeassistant/components/twentemilieu/translations/zh-Hant.json b/homeassistant/components/twentemilieu/translations/zh-Hant.json index 11cf62d2d60..51373552418 100644 --- a/homeassistant/components/twentemilieu/translations/zh-Hant.json +++ b/homeassistant/components/twentemilieu/translations/zh-Hant.json @@ -14,8 +14,7 @@ "house_number": "\u9580\u724c\u865f\u78bc", "post_code": "\u90f5\u905e\u5340\u865f" }, - "description": "\u8a2d\u5b9a Twente Milieu \u4ee5\u53d6\u5f97\u8a72\u5730\u5740\u5ee2\u68c4\u7269\u56de\u6536\u8cc7\u8a0a\u3002", - "title": "Twente Milieu" + "description": "\u8a2d\u5b9a Twente Milieu \u4ee5\u53d6\u5f97\u8a72\u5730\u5740\u5ee2\u68c4\u7269\u56de\u6536\u8cc7\u8a0a\u3002" } } } diff --git a/homeassistant/components/twilio/translations/cs.json b/homeassistant/components/twilio/translations/cs.json index f45c4bf881e..8572109edcd 100644 --- a/homeassistant/components/twilio/translations/cs.json +++ b/homeassistant/components/twilio/translations/cs.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "Nen\u00ed p\u0159ipojeno k Home Assistant Cloud.", "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.", "webhook_not_internet_accessible": "V\u00e1\u0161 Home Assistant mus\u00ed b\u00fdt p\u0159\u00edstupn\u00fd z internetu, aby mohl p\u0159ij\u00edmat zpr\u00e1vy webhook." }, diff --git a/homeassistant/components/twilio/translations/id.json b/homeassistant/components/twilio/translations/id.json index 06a77bc974e..6c447e22f63 100644 --- a/homeassistant/components/twilio/translations/id.json +++ b/homeassistant/components/twilio/translations/id.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Instans Home Assistant Anda harus dapat diakses dari internet untuk menerima pesan webhook." }, "create_entry": { - "default": "Untuk mengirim event ke Home Assistant, Anda harus menyiapkan [Webhooks dengan Twilio]({twilio_url}).\n\nIsikan info berikut:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content-Type: application/x-www-form-urlencoded\n\nBaca [dokumentasi]({docs_url}) tentang cara mengonfigurasi otomasi untuk menangani data masuk." + "default": "Untuk mengirim event ke Home Assistant, Anda harus menyiapkan [Webhooks dengan Twilio]({twilio_url}).\n\nIsi info berikut:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content-Type: application/x-www-form-urlencoded\n\nBaca [dokumentasi]({docs_url}) tentang cara mengonfigurasi otomasi untuk menangani data masuk." }, "step": { "user": { diff --git a/homeassistant/components/twilio/translations/ja.json b/homeassistant/components/twilio/translations/ja.json index 84ea72878e7..521fee184f2 100644 --- a/homeassistant/components/twilio/translations/ja.json +++ b/homeassistant/components/twilio/translations/ja.json @@ -6,7 +6,7 @@ "webhook_not_internet_accessible": "Webhook\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u53d7\u4fe1\u3059\u308b\u306b\u306f\u3001Home Assistant\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3001\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u304b\u3089\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002" }, "create_entry": { - "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[Webhooks with Twilio]({twilio_url})\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044:\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type: application/x-www-form-urlencoded\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + "default": "Home Assistant\u306b\u30a4\u30d9\u30f3\u30c8\u3092\u9001\u4fe1\u3059\u308b\u306b\u306f\u3001[Webhooks with Twilio]({twilio_url})\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u4ee5\u4e0b\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044:\n\n- URL: `{webhook_url}`\n- Method(\u65b9\u5f0f): POST\n- Content Type(\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e): application/x-www-form-urlencoded\n\n\u53d7\u4fe1\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8]({docs_url})\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "step": { "user": { diff --git a/homeassistant/components/twinkly/translations/ca.json b/homeassistant/components/twinkly/translations/ca.json index ecf2c4ed12c..4813f7736f9 100644 --- a/homeassistant/components/twinkly/translations/ca.json +++ b/homeassistant/components/twinkly/translations/ca.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Amfitri\u00f3" - }, - "description": "Configura la teva tira LED de Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/cs.json b/homeassistant/components/twinkly/translations/cs.json index be5b291744b..26afb737dce 100644 --- a/homeassistant/components/twinkly/translations/cs.json +++ b/homeassistant/components/twinkly/translations/cs.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Hostitel" - }, - "description": "Nastavte sv\u016fj LED p\u00e1sek Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/de.json b/homeassistant/components/twinkly/translations/de.json index d31b243f7b9..5d096c5b594 100644 --- a/homeassistant/components/twinkly/translations/de.json +++ b/homeassistant/components/twinkly/translations/de.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Einrichten deiner Twinkly-Led-Kette", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/el.json b/homeassistant/components/twinkly/translations/el.json index 8e0294b87b5..1210c6dc76a 100644 --- a/homeassistant/components/twinkly/translations/el.json +++ b/homeassistant/components/twinkly/translations/el.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf Twinkly led string \u03c3\u03b1\u03c2", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/en.json b/homeassistant/components/twinkly/translations/en.json index fbb63fbc3e8..720a258161f 100644 --- a/homeassistant/components/twinkly/translations/en.json +++ b/homeassistant/components/twinkly/translations/en.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Set up your Twinkly led string", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/es.json b/homeassistant/components/twinkly/translations/es.json index 691c0afcda6..e18d54adb9e 100644 --- a/homeassistant/components/twinkly/translations/es.json +++ b/homeassistant/components/twinkly/translations/es.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host (o direcci\u00f3n IP) de tu dispositivo Twinkly" - }, - "description": "Configura tu tira led Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/et.json b/homeassistant/components/twinkly/translations/et.json index bdb4ef3772c..bf902984d26 100644 --- a/homeassistant/components/twinkly/translations/et.json +++ b/homeassistant/components/twinkly/translations/et.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Seadista oma Twinkly LED riba", - "title": "" + } } } } diff --git a/homeassistant/components/twinkly/translations/fr.json b/homeassistant/components/twinkly/translations/fr.json index c5b01400457..cf6fe97ce88 100644 --- a/homeassistant/components/twinkly/translations/fr.json +++ b/homeassistant/components/twinkly/translations/fr.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "H\u00f4te" - }, - "description": "Configurer votre Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/hu.json b/homeassistant/components/twinkly/translations/hu.json index 94983ce4893..0ad6208ca16 100644 --- a/homeassistant/components/twinkly/translations/hu.json +++ b/homeassistant/components/twinkly/translations/hu.json @@ -12,10 +12,8 @@ }, "user": { "data": { - "host": "A Twinkly eszk\u00f6z c\u00edme" - }, - "description": "\u00c1ll\u00edtsa be a Twinkly led-karakterl\u00e1nc\u00e1t", - "title": "Twinkly" + "host": "C\u00edm" + } } } } diff --git a/homeassistant/components/twinkly/translations/id.json b/homeassistant/components/twinkly/translations/id.json index 330bcf6ce11..b37e20bf023 100644 --- a/homeassistant/components/twinkly/translations/id.json +++ b/homeassistant/components/twinkly/translations/id.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Siapkan string led Twinkly Anda", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/it.json b/homeassistant/components/twinkly/translations/it.json index a64ecaeb425..fec569379d4 100644 --- a/homeassistant/components/twinkly/translations/it.json +++ b/homeassistant/components/twinkly/translations/it.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Configura la tua stringa led Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/ja.json b/homeassistant/components/twinkly/translations/ja.json index e3955e4286f..2ec0d28e0bf 100644 --- a/homeassistant/components/twinkly/translations/ja.json +++ b/homeassistant/components/twinkly/translations/ja.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Twinkly device\u306e\u30db\u30b9\u30c8(\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9)" - }, - "description": "Twinkly led string\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/ka.json b/homeassistant/components/twinkly/translations/ka.json index d0d6b61f4cc..293552c37d4 100644 --- a/homeassistant/components/twinkly/translations/ka.json +++ b/homeassistant/components/twinkly/translations/ka.json @@ -10,9 +10,7 @@ "user": { "data": { "host": "\u10d7\u10e5\u10d5\u10d4\u10dc\u10d8 twinkly \u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10f0\u10dd\u10e1\u10e2\u10d8 (\u10d0\u10dc IP \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8)" - }, - "description": "\u10d7\u10e5\u10d5\u10d4\u10dc\u10d8 Twinkly \u10e8\u10e3\u10e5\u10d3\u10d8\u10dd\u10d3\u10d8\u10e1 \u10da\u10d4\u10dc\u10e2\u10d8\u10e1 \u10d3\u10d0\u10e7\u10d4\u10dc\u10d4\u10d1\u10d0", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/ko.json b/homeassistant/components/twinkly/translations/ko.json index b3b23991e3d..257be47f610 100644 --- a/homeassistant/components/twinkly/translations/ko.json +++ b/homeassistant/components/twinkly/translations/ko.json @@ -10,9 +10,7 @@ "user": { "data": { "host": "Twinkly \uae30\uae30\uc758 \ud638\uc2a4\ud2b8 (\ub610\ub294 IP \uc8fc\uc18c)" - }, - "description": "Twinkly LED \uc904 \uc870\uba85 \uc124\uc815\ud558\uae30", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/nl.json b/homeassistant/components/twinkly/translations/nl.json index 38331177895..b41baa0b403 100644 --- a/homeassistant/components/twinkly/translations/nl.json +++ b/homeassistant/components/twinkly/translations/nl.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Host" - }, - "description": "Uw Twinkly LED-string instellen", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/no.json b/homeassistant/components/twinkly/translations/no.json index af3b74a9686..72f88054aa7 100644 --- a/homeassistant/components/twinkly/translations/no.json +++ b/homeassistant/components/twinkly/translations/no.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Vert" - }, - "description": "Sett opp Twinkly-led-strengen", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/pl.json b/homeassistant/components/twinkly/translations/pl.json index 8036d6cc2f6..a9538177101 100644 --- a/homeassistant/components/twinkly/translations/pl.json +++ b/homeassistant/components/twinkly/translations/pl.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Nazwa hosta lub adres IP" - }, - "description": "Konfiguracja Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/pt-BR.json b/homeassistant/components/twinkly/translations/pt-BR.json index 3aff4eb867d..789802f44e9 100644 --- a/homeassistant/components/twinkly/translations/pt-BR.json +++ b/homeassistant/components/twinkly/translations/pt-BR.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Nome do host" - }, - "description": "Configure sua fita de led Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/ru.json b/homeassistant/components/twinkly/translations/ru.json index a7e0ff145cd..d1e06eee9cc 100644 --- a/homeassistant/components/twinkly/translations/ru.json +++ b/homeassistant/components/twinkly/translations/ru.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434\u043d\u043e\u0439 \u043b\u0435\u043d\u0442\u044b Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/tr.json b/homeassistant/components/twinkly/translations/tr.json index 0e78648d811..39bf9b32e55 100644 --- a/homeassistant/components/twinkly/translations/tr.json +++ b/homeassistant/components/twinkly/translations/tr.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "Sunucu" - }, - "description": "Twinkly led dizinizi ayarlay\u0131n", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/uk.json b/homeassistant/components/twinkly/translations/uk.json index bd256d31b03..8f3d6d0cbaa 100644 --- a/homeassistant/components/twinkly/translations/uk.json +++ b/homeassistant/components/twinkly/translations/uk.json @@ -10,9 +10,7 @@ "user": { "data": { "host": "\u0406\u043c'\u044f \u0445\u043e\u0441\u0442\u0430 (\u0430\u0431\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0430) \u0412\u0430\u0448\u043e\u0433\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e Twinkly" - }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0441\u0432\u0456\u0442\u043b\u043e\u0434\u0456\u043e\u0434\u043d\u043e\u0457 \u0441\u0442\u0440\u0456\u0447\u043a\u0438 Twinkly", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/twinkly/translations/zh-Hant.json b/homeassistant/components/twinkly/translations/zh-Hant.json index 77e0a69cf65..d016c286034 100644 --- a/homeassistant/components/twinkly/translations/zh-Hant.json +++ b/homeassistant/components/twinkly/translations/zh-Hant.json @@ -13,9 +13,7 @@ "user": { "data": { "host": "\u4e3b\u6a5f\u7aef" - }, - "description": "\u8a2d\u5b9a Twinkly LED \u71c8\u4e32", - "title": "Twinkly" + } } } } diff --git a/homeassistant/components/unifi/translations/hu.json b/homeassistant/components/unifi/translations/hu.json index 7692342bf89..ea4c7484d44 100644 --- a/homeassistant/components/unifi/translations/hu.json +++ b/homeassistant/components/unifi/translations/hu.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "A vez\u00e9rl\u0151 webhelye m\u00e1r konfigur\u00e1lva van", + "already_configured": "Az UniFi Network webhely m\u00e1r konfigur\u00e1lva van", "configuration_updated": "A konfigur\u00e1ci\u00f3 friss\u00edtve.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, @@ -39,7 +39,7 @@ "device_tracker": { "data": { "detection_time": "Id\u0151 m\u00e1sodpercben az utols\u00f3 l\u00e1t\u00e1st\u00f3l a t\u00e1vol tart\u00e1sig", - "ignore_wired_bug": "Az UniFi vezet\u00e9kes hibalogika letilt\u00e1sa", + "ignore_wired_bug": "Az UniFi Network vezet\u00e9kes hibalogika letilt\u00e1sa", "ssid_filter": "V\u00e1lassza ki az SSID -ket a vezet\u00e9k n\u00e9lk\u00fcli \u00fcgyfelek nyomon k\u00f6vet\u00e9s\u00e9hez", "track_clients": "K\u00f6vesse nyomon a h\u00e1l\u00f3zati \u00fcgyfeleket", "track_devices": "H\u00e1l\u00f3zati eszk\u00f6z\u00f6k nyomon k\u00f6vet\u00e9se (Ubiquiti eszk\u00f6z\u00f6k)", diff --git a/homeassistant/components/unifi/translations/it.json b/homeassistant/components/unifi/translations/it.json index eeda8a8c9bb..7ddc0fde4fe 100644 --- a/homeassistant/components/unifi/translations/it.json +++ b/homeassistant/components/unifi/translations/it.json @@ -50,8 +50,8 @@ }, "init": { "data": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" } }, "simple_options": { diff --git a/homeassistant/components/update/translations/ca.json b/homeassistant/components/update/translations/ca.json index 396e79c14c0..b008c0693c0 100644 --- a/homeassistant/components/update/translations/ca.json +++ b/homeassistant/components/update/translations/ca.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "La disponibilitat de l'actualitzaci\u00f3 de {entity_name} canvi\u00ef", + "turned_off": "{entity_name} s'actualitzi", + "turned_on": "{entity_name} tingui una actualitzaci\u00f3 disponible" + } + }, "title": "Actualitza" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/cs.json b/homeassistant/components/update/translations/cs.json new file mode 100644 index 00000000000..39380573af6 --- /dev/null +++ b/homeassistant/components/update/translations/cs.json @@ -0,0 +1,9 @@ +{ + "device_automation": { + "trigger_type": { + "changed_states": "Dostupnost aktualizace {entity_name} zm\u011bn\u011bna", + "turned_off": "{entity_name} se stal aktu\u00e1ln\u00edm", + "turned_on": "{entity_name} m\u00e1 k dispozici aktualizaci" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/update/translations/de.json b/homeassistant/components/update/translations/de.json index 18562d81eaf..bc3e9b19322 100644 --- a/homeassistant/components/update/translations/de.json +++ b/homeassistant/components/update/translations/de.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} Update Verf\u00fcgbarkeit ge\u00e4ndert", + "turned_off": "{entity_name} wurde auf den neuesten Stand gebracht", + "turned_on": "F\u00fcr {entity_name} ist ein Update verf\u00fcgbar" + } + }, "title": "Aktualisieren" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/el.json b/homeassistant/components/update/translations/el.json index d687d342ec3..0a4d6190150 100644 --- a/homeassistant/components/update/translations/el.json +++ b/homeassistant/components/update/translations/el.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "\u0397 \u03b4\u03b9\u03b1\u03b8\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2 {entity_name} \u03ac\u03bb\u03bb\u03b1\u03be\u03b5", + "turned_off": "{entity_name} \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5", + "turned_on": "{entity_name} \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03bc\u03b9\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7" + } + }, "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/es.json b/homeassistant/components/update/translations/es.json new file mode 100644 index 00000000000..79993c92b20 --- /dev/null +++ b/homeassistant/components/update/translations/es.json @@ -0,0 +1,3 @@ +{ + "title": "Actualizar" +} \ No newline at end of file diff --git a/homeassistant/components/update/translations/et.json b/homeassistant/components/update/translations/et.json index 7db9a98a507..e89acfbe30f 100644 --- a/homeassistant/components/update/translations/et.json +++ b/homeassistant/components/update/translations/et.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "Olemi {entity_name} v\u00e4rskenduse saadavus muutus", + "turned_off": "Olem {entity_name} muutus ajakohaseks", + "turned_on": "Olemile {entity_name} on saadaval v\u00e4rskendus" + } + }, "title": "Uuenda" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/fr.json b/homeassistant/components/update/translations/fr.json index 1a49a0cab9f..94d23e72a7a 100644 --- a/homeassistant/components/update/translations/fr.json +++ b/homeassistant/components/update/translations/fr.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "La disponibilit\u00e9 d'une mise \u00e0 jour pour {entity_name} a chang\u00e9", + "turned_off": "{entity_name} a \u00e9t\u00e9 mis \u00e0 jour", + "turned_on": "Une mise \u00e0 jour est disponible pour {entity_name}" + } + }, "title": "Mettre \u00e0 jour" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/he.json b/homeassistant/components/update/translations/he.json new file mode 100644 index 00000000000..6c81fac9447 --- /dev/null +++ b/homeassistant/components/update/translations/he.json @@ -0,0 +1,10 @@ +{ + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} \u05d6\u05de\u05d9\u05e0\u05d5\u05ea \u05d4\u05e2\u05d3\u05db\u05d5\u05df \u05d4\u05e9\u05ea\u05e0\u05ea\u05d4", + "turned_off": "{entity_name} \u05d4\u05e4\u05da \u05dc\u05de\u05e2\u05d5\u05d3\u05db\u05df", + "turned_on": "{entity_name} \u05e7\u05d9\u05d1\u05dc \u05e2\u05d3\u05db\u05d5\u05df \u05d6\u05de\u05d9\u05df" + } + }, + "title": "\u05e2\u05d3\u05db\u05d5\u05df" +} \ No newline at end of file diff --git a/homeassistant/components/update/translations/hu.json b/homeassistant/components/update/translations/hu.json index 1e2ec425a88..7732ee56d62 100644 --- a/homeassistant/components/update/translations/hu.json +++ b/homeassistant/components/update/translations/hu.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} friss\u00edt\u00e9s el\u00e9rhet\u0151s\u00e9ge megv\u00e1ltozott", + "turned_off": "{entity_name} naprak\u00e9sz lett", + "turned_on": "{entity_name} kapott egy el\u00e9rhet\u0151 friss\u00edt\u00e9st" + } + }, "title": "Friss\u00edt\u00e9s" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/id.json b/homeassistant/components/update/translations/id.json index 70f495575fa..c4e6c43ab5c 100644 --- a/homeassistant/components/update/translations/id.json +++ b/homeassistant/components/update/translations/id.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "Ketersediaan pembaruan {entity_name} berubah", + "turned_off": "{entity_name} menjadi yang terbaru", + "turned_on": "{entity_name} mendapat pembaruan yang tersedia" + } + }, "title": "Versi Baru" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/it.json b/homeassistant/components/update/translations/it.json index 539f0bb4294..97d2b4020df 100644 --- a/homeassistant/components/update/translations/it.json +++ b/homeassistant/components/update/translations/it.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "La disponibilit\u00e0 dell'aggiornamento di {entity_name} \u00e8 cambiata", + "turned_off": "{entity_name} \u00e8 stato aggiornato", + "turned_on": "{entity_name} ha un aggiornamento disponibile" + } + }, "title": "Aggiornamento" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/ja.json b/homeassistant/components/update/translations/ja.json index 4cb5e4959a1..63ac9a7ddf5 100644 --- a/homeassistant/components/update/translations/ja.json +++ b/homeassistant/components/update/translations/ja.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} \u66f4\u65b0\u306e\u53ef\u7528\u6027(availability)\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f", + "turned_off": "{entity_name} \u304c\u6700\u65b0\u306b\u306a\u308a\u307e\u3057\u305f", + "turned_on": "{entity_name} \u306f\u3001\u5229\u7528\u53ef\u80fd\u306a\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u53d6\u5f97\u3057\u307e\u3057\u305f\u3002" + } + }, "title": "\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/nl.json b/homeassistant/components/update/translations/nl.json index 95b82de3b4d..840fa03da8a 100644 --- a/homeassistant/components/update/translations/nl.json +++ b/homeassistant/components/update/translations/nl.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} update beschikbaarheid gewijzigd", + "turned_off": "{entity_name} werd up-to-date", + "turned_on": "{entity_name} heeft een update beschikbaar" + } + }, "title": "Update" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/no.json b/homeassistant/components/update/translations/no.json index e98d60ab4fc..8f292b0b141 100644 --- a/homeassistant/components/update/translations/no.json +++ b/homeassistant/components/update/translations/no.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} -oppdateringstilgjengeligheten er endret", + "turned_off": "{entity_name} ble oppdatert", + "turned_on": "{entity_name} har en oppdatering tilgjengelig" + } + }, "title": "Oppdater" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/pl.json b/homeassistant/components/update/translations/pl.json index eff0431a518..ee29b58f8c3 100644 --- a/homeassistant/components/update/translations/pl.json +++ b/homeassistant/components/update/translations/pl.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "zmieni si\u0119 dost\u0119pno\u015b\u0107 aktualizacji {entity_name}", + "turned_off": "wykonano aktualizacj\u0119 dla {entity_name}", + "turned_on": "{entity_name} ma dost\u0119pn\u0105 aktualizacj\u0119" + } + }, "title": "Aktualizacja" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/pt-BR.json b/homeassistant/components/update/translations/pt-BR.json index 4003445e2c3..dbdbae4fa88 100644 --- a/homeassistant/components/update/translations/pt-BR.json +++ b/homeassistant/components/update/translations/pt-BR.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} disponibilidade de atualiza\u00e7\u00e3o alterada", + "turned_off": "{entity_name} foi atualizado", + "turned_on": "{entity_name} tem uma atualiza\u00e7\u00e3o dispon\u00edvel" + } + }, "title": "Atualiza\u00e7\u00e3o" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/ru.json b/homeassistant/components/update/translations/ru.json index a2ee79efd15..f78b838708e 100644 --- a/homeassistant/components/update/translations/ru.json +++ b/homeassistant/components/update/translations/ru.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f {entity_name}", + "turned_off": "{entity_name} \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f", + "turned_on": "\u0421\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 {entity_name}" + } + }, "title": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/tr.json b/homeassistant/components/update/translations/tr.json index 30c3c90c437..11d0b3a2c92 100644 --- a/homeassistant/components/update/translations/tr.json +++ b/homeassistant/components/update/translations/tr.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} g\u00fcncellemesinin kullan\u0131labilirli\u011fi de\u011fi\u015fti", + "turned_off": "{entity_name} g\u00fcncellendi", + "turned_on": "{entity_name} bir g\u00fcncelleme ald\u0131" + } + }, "title": "G\u00fcncelle" } \ No newline at end of file diff --git a/homeassistant/components/update/translations/zh-Hans.json b/homeassistant/components/update/translations/zh-Hans.json new file mode 100644 index 00000000000..8b7c885cccb --- /dev/null +++ b/homeassistant/components/update/translations/zh-Hans.json @@ -0,0 +1,9 @@ +{ + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} \u7684\u66f4\u65b0\u53ef\u7528\u6027\u53d8\u5316", + "turned_off": "{entity_name} \u53d8\u4e3a\u6700\u65b0\u72b6\u6001", + "turned_on": "{entity_name} \u6536\u5230\u66f4\u65b0" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/update/translations/zh-Hant.json b/homeassistant/components/update/translations/zh-Hant.json index 46cdfb48f90..492af201404 100644 --- a/homeassistant/components/update/translations/zh-Hant.json +++ b/homeassistant/components/update/translations/zh-Hant.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} \u53ef\u7528\u66f4\u65b0\u5df2\u8b8a\u66f4", + "turned_off": "{entity_name} \u5df2\u6700\u65b0", + "turned_on": "{entity_name} \u6709\u66f4\u65b0" + } + }, "title": "\u66f4\u65b0" } \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/it.json b/homeassistant/components/upnp/translations/it.json index ce637bbad08..a57429ac78a 100644 --- a/homeassistant/components/upnp/translations/it.json +++ b/homeassistant/components/upnp/translations/it.json @@ -6,14 +6,14 @@ "no_devices_found": "Nessun dispositivo trovato sulla rete" }, "error": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "flow_title": "{name}", "step": { "init": { - "one": "Pi\u00f9", - "other": "Altri" + "one": "Vuoto", + "other": "Vuoti" }, "ssdp_confirm": { "description": "Vuoi configurare questo dispositivo UPnP/IGD?" diff --git a/homeassistant/components/uptime/translations/bg.json b/homeassistant/components/uptime/translations/bg.json new file mode 100644 index 00000000000..1290144ec04 --- /dev/null +++ b/homeassistant/components/uptime/translations/bg.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "step": { + "user": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/cs.json b/homeassistant/components/uptime/translations/cs.json new file mode 100644 index 00000000000..ce19e127348 --- /dev/null +++ b/homeassistant/components/uptime/translations/cs.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + }, + "step": { + "user": { + "description": "Chcete za\u010d\u00edt nastavovat?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/id.json b/homeassistant/components/uptime/translations/id.json new file mode 100644 index 00000000000..bf6ea606f2b --- /dev/null +++ b/homeassistant/components/uptime/translations/id.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + }, + "step": { + "user": { + "description": "Ingin memulai penyiapan?" + } + } + }, + "title": "Uptime" +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/it.json b/homeassistant/components/uptime/translations/it.json index cba8d0a264f..9913180a309 100644 --- a/homeassistant/components/uptime/translations/it.json +++ b/homeassistant/components/uptime/translations/it.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, "step": { "user": { "description": "Vuoi iniziare la configurazione?" diff --git a/homeassistant/components/uptime/translations/no.json b/homeassistant/components/uptime/translations/no.json new file mode 100644 index 00000000000..9ac16f0a20c --- /dev/null +++ b/homeassistant/components/uptime/translations/no.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + }, + "step": { + "user": { + "description": "Vil du starte oppsettet?" + } + } + }, + "title": "Oppetid" +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/pl.json b/homeassistant/components/uptime/translations/pl.json new file mode 100644 index 00000000000..bca14b2f14c --- /dev/null +++ b/homeassistant/components/uptime/translations/pl.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + }, + "step": { + "user": { + "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" + } + } + }, + "title": "Uptime" +} \ No newline at end of file diff --git a/homeassistant/components/uptime/translations/pt-BR.json b/homeassistant/components/uptime/translations/pt-BR.json index fdec46961b5..d3dddae8233 100644 --- a/homeassistant/components/uptime/translations/pt-BR.json +++ b/homeassistant/components/uptime/translations/pt-BR.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o poss\u00edvel." + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "step": { "user": { diff --git a/homeassistant/components/uptime/translations/tr.json b/homeassistant/components/uptime/translations/tr.json new file mode 100644 index 00000000000..ed090a38398 --- /dev/null +++ b/homeassistant/components/uptime/translations/tr.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "step": { + "user": { + "description": "Kuruluma ba\u015flamak ister misiniz?" + } + } + }, + "title": "\u00c7al\u0131\u015fma S\u00fcresi" +} \ No newline at end of file diff --git a/homeassistant/components/uptimerobot/translations/ca.json b/homeassistant/components/uptimerobot/translations/ca.json index 9ae97109fc1..a6ed19c11f0 100644 --- a/homeassistant/components/uptimerobot/translations/ca.json +++ b/homeassistant/components/uptimerobot/translations/ca.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_api_key": "Clau API inv\u00e0lida", + "not_main_key": "S'ha detectat un tipus de clau API incorrecta, utilitza la clau API 'principal'", "reauth_failed_matching_account": "La clau API proporcionada no correspon amb l'identificador del compte de la configuraci\u00f3 actual.", "unknown": "Error inesperat" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Clau API" }, - "description": "Has de proporcionar una nova clau API de nom\u00e9s lectura d'UptimeRobot", + "description": "Has de proporcionar una nova clau API 'principal' d'UptimeRobot", "title": "Reautenticaci\u00f3 de la integraci\u00f3" }, "user": { "data": { "api_key": "Clau API" }, - "description": "Has de proporcionar una clau API de nom\u00e9s lectura d'UptimeRobot" + "description": "Has de proporcionar la clau API 'principal' d'UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/de.json b/homeassistant/components/uptimerobot/translations/de.json index 3ac63f84de2..ee2af73ea20 100644 --- a/homeassistant/components/uptimerobot/translations/de.json +++ b/homeassistant/components/uptimerobot/translations/de.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", + "not_main_key": "Falscher API-Schl\u00fcsseltyp erkannt, verwende den API-Hauptschl\u00fcssel", "reauth_failed_matching_account": "Der von dir angegebene API-Schl\u00fcssel stimmt nicht mit der Konto-ID f\u00fcr die vorhandene Konfiguration \u00fcberein.", "unknown": "Unerwarteter Fehler" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API-Schl\u00fcssel" }, - "description": "Du musst einen neuen schreibgesch\u00fctzten API-Schl\u00fcssel von UptimeRobot bereitstellen", + "description": "Du musst einen neuen Haupt-API-Schl\u00fcssel von UptimeRobot bereitstellen", "title": "Integration erneut authentifizieren" }, "user": { "data": { "api_key": "API-Schl\u00fcssel" }, - "description": "Du musst einen schreibgesch\u00fctzten API-Schl\u00fcssel von UptimeRobot bereitstellen" + "description": "Du musst den Haupt-API-Schl\u00fcssel von UptimeRobot bereitstellen" } } } diff --git a/homeassistant/components/uptimerobot/translations/el.json b/homeassistant/components/uptimerobot/translations/el.json index 2f15a945ab0..98034c40eee 100644 --- a/homeassistant/components/uptimerobot/translations/el.json +++ b/homeassistant/components/uptimerobot/translations/el.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", + "not_main_key": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03bb\u03b1\u03bd\u03b8\u03b1\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c2 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af 'main' API", "reauth_failed_matching_account": "\u03a4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03c0\u03bf\u03c5 \u03b4\u03ce\u03c3\u03b1\u03c4\u03b5 \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03c4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03c3\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7.", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, diff --git a/homeassistant/components/uptimerobot/translations/et.json b/homeassistant/components/uptimerobot/translations/et.json index 9b00c2a5b44..ed2e46b7cc2 100644 --- a/homeassistant/components/uptimerobot/translations/et.json +++ b/homeassistant/components/uptimerobot/translations/et.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "invalid_api_key": "Vigane API v\u00f5ti", + "not_main_key": "Tuvastati vale API-v\u00f5tme t\u00fc\u00fcp, kasuta API peamist v\u00f5tit", "reauth_failed_matching_account": "Sisestatud API v\u00f5ti ei vasta olemasoleva konto ID s\u00e4tetele.", "unknown": "Ootamatu t\u00f5rge" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API v\u00f5ti" }, - "description": "Pead sisestama uue UptimeRoboti kirjutuskaitstud API-v\u00f5tme", + "description": "Pead sisestama uue UptimeRoboti peamise API-v\u00f5tme", "title": "Taastuvasta sidumine" }, "user": { "data": { "api_key": "API v\u00f5ti" }, - "description": "Pead sisestama UptimeRoboti kirjutuskaitstud API-v\u00f5tme" + "description": "Pead sisestama UptimeRoboti peamise API-v\u00f5tme" } } } diff --git a/homeassistant/components/uptimerobot/translations/fr.json b/homeassistant/components/uptimerobot/translations/fr.json index 674180a1d90..ebea3087fc7 100644 --- a/homeassistant/components/uptimerobot/translations/fr.json +++ b/homeassistant/components/uptimerobot/translations/fr.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u00c9chec de connexion", "invalid_api_key": "Cl\u00e9 d'API non valide", + "not_main_key": "Mauvais type de cl\u00e9 d'API d\u00e9tect\u00e9, utilisez la cl\u00e9 d'API \u00ab\u00a0principale\u00a0\u00bb", "reauth_failed_matching_account": "La cl\u00e9 API que vous avez fournie ne correspond pas \u00e0 l\u2019ID de compte pour la configuration existante.", "unknown": "Erreur inattendue" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Cl\u00e9 d'API" }, - "description": "Vous devez fournir une nouvelle cl\u00e9 API en lecture seule \u00e0 partir d'Uptime Robot", + "description": "Vous devez fournir une nouvelle cl\u00e9 d'API \u00ab\u00a0principale\u00a0\u00bb \u00e0 partir d'UptimeRobot", "title": "R\u00e9-authentifier l'int\u00e9gration" }, "user": { "data": { "api_key": "Cl\u00e9 d'API" }, - "description": "Vous devez fournir une cl\u00e9 API en lecture seule \u00e0 partir d'Uptime Robot" + "description": "Vous devez fournir la cl\u00e9 d'API \u00ab\u00a0principale\u00a0\u00bb \u00e0 partir d'UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/hu.json b/homeassistant/components/uptimerobot/translations/hu.json index 000851093a5..d17c9bf8ac1 100644 --- a/homeassistant/components/uptimerobot/translations/hu.json +++ b/homeassistant/components/uptimerobot/translations/hu.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Nem siker\u00fclt csatlakozni", "invalid_api_key": "\u00c9rv\u00e9nytelen API-kulcs", + "not_main_key": "Nem megfelel\u0151 API-kulcst\u00edpus, haszn\u00e1lja a \"main\" API-kulcsot", "reauth_failed_matching_account": "A megadott API -kulcs nem egyezik a megl\u00e9v\u0151 konfigur\u00e1ci\u00f3 fi\u00f3kazonos\u00edt\u00f3j\u00e1val.", "unknown": "V\u00e1ratlan hiba" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API kulcs" }, - "description": "Meg kell adnia egy \u00faj, csak olvashat\u00f3 API-kulcsot az Uptime Robot-t\u00f3l", + "description": "Meg kell adnia egy \u00faj \u201ef\u0151\u201d API-kulcsot az UptimeRobott\u00f3l", "title": "Integr\u00e1ci\u00f3 \u00fajb\u00f3li hiteles\u00edt\u00e9se" }, "user": { "data": { "api_key": "API kulcs" }, - "description": "Meg kell adnia egy csak olvashat\u00f3 API-kulcsot az Uptime Robot-t\u00f3l" + "description": "Meg kell adnia a \u201ef\u0151\u201d API-kulcsot az UptimeRobott\u00f3l" } } } diff --git a/homeassistant/components/uptimerobot/translations/id.json b/homeassistant/components/uptimerobot/translations/id.json index dac2e7e2814..b92f8f74027 100644 --- a/homeassistant/components/uptimerobot/translations/id.json +++ b/homeassistant/components/uptimerobot/translations/id.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Gagal terhubung", "invalid_api_key": "Kunci API tidak valid", + "not_main_key": "Jenis kunci API yang salah terdeteksi, gunakan kunci API 'main'", "reauth_failed_matching_account": "Kunci API yang Anda berikan tidak cocok dengan ID akun untuk konfigurasi yang ada.", "unknown": "Kesalahan yang tidak diharapkan" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Kunci API" }, - "description": "Anda perlu menyediakan kunci API hanya-baca yang baru dari UptimeRobot", + "description": "Anda perlu menyediakan kunci API 'main' yang baru dari UptimeRobot", "title": "Autentikasi Ulang Integrasi" }, "user": { "data": { "api_key": "Kunci API" }, - "description": "Anda perlu menyediakan kunci API hanya-baca dari UptimeRobot" + "description": "Anda perlu menyediakan kunci API 'main' dari UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/it.json b/homeassistant/components/uptimerobot/translations/it.json index 3a6c87c5ea9..4359ab682ee 100644 --- a/homeassistant/components/uptimerobot/translations/it.json +++ b/homeassistant/components/uptimerobot/translations/it.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Impossibile connettersi", "invalid_api_key": "Chiave API non valida", + "not_main_key": "Rilevato tipo di chiave API errato, utilizza la chiave API 'principale'.", "reauth_failed_matching_account": "La chiave API che hai fornito non corrisponde all'ID account per la configurazione esistente.", "unknown": "Errore imprevisto" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Chiave API" }, - "description": "Devi fornire una nuova chiave API di sola lettura da UptimeRobot", + "description": "Devi fornire una nuova chiave API 'principale' da UptimeRobot", "title": "Autentica nuovamente l'integrazione" }, "user": { "data": { "api_key": "Chiave API" }, - "description": "Devi fornire una chiave API di sola lettura da UptimeRobot" + "description": "Devi fornire la chiave API 'principale' da UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/ja.json b/homeassistant/components/uptimerobot/translations/ja.json index d890780eab9..e8c66b5088b 100644 --- a/homeassistant/components/uptimerobot/translations/ja.json +++ b/homeassistant/components/uptimerobot/translations/ja.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", + "not_main_key": "\u9593\u9055\u3063\u305fAPI\u30ad\u30fc\u30bf\u30a4\u30d7\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002 'main' API\u30ad\u30fc\u3092\u4f7f\u7528\u3057\u3066\u304f\u3060\u3055\u3044", "reauth_failed_matching_account": "\u6307\u5b9a\u3055\u308c\u305fAPI\u30ad\u30fc\u304c\u3001\u3059\u3067\u306b\u3042\u308b\u8a2d\u5b9a\u306e\u30a2\u30ab\u30a6\u30f3\u30c8ID\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, diff --git a/homeassistant/components/uptimerobot/translations/nl.json b/homeassistant/components/uptimerobot/translations/nl.json index 3431a9cbfa5..d3ee1f7515a 100644 --- a/homeassistant/components/uptimerobot/translations/nl.json +++ b/homeassistant/components/uptimerobot/translations/nl.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Kan geen verbinding maken", "invalid_api_key": "Ongeldige API-sleutel", + "not_main_key": "Verkeerde API sleutel gedetecteerd, gebruik de 'hoofd' API sleutel", "reauth_failed_matching_account": "De API sleutel die u heeft opgegeven komt niet overeen met de account ID voor de bestaande configuratie.", "unknown": "Onverwachte fout" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API-sleutel" }, - "description": "U moet een nieuwe alleen-lezen API-sleutel van UptimeRobot opgeven", + "description": "U moet een nieuwe 'hoofd'-API-sleutel van UptimeRobot opgeven", "title": "Verifieer de integratie opnieuw" }, "user": { "data": { "api_key": "API-sleutel" }, - "description": "U moet een alleen-lezen API-sleutel van UptimeRobot opgeven" + "description": "U moet de 'hoofd' API-sleutel van UptimeRobot opgeven" } } } diff --git a/homeassistant/components/uptimerobot/translations/no.json b/homeassistant/components/uptimerobot/translations/no.json index cbb5066a747..df7a7f8045a 100644 --- a/homeassistant/components/uptimerobot/translations/no.json +++ b/homeassistant/components/uptimerobot/translations/no.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "invalid_api_key": "Ugyldig API-n\u00f8kkel", + "not_main_key": "Feil API-n\u00f8kkeltype oppdaget, bruk 'hoved' API-n\u00f8kkelen", "reauth_failed_matching_account": "API-n\u00f8kkelen du oppgav, samsvarer ikke med konto-IDen for eksisterende konfigurasjon.", "unknown": "Uventet feil" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API-n\u00f8kkel" }, - "description": "Du m\u00e5 levere en ny skrivebeskyttet API-n\u00f8kkel fra UptimeRobot", + "description": "Du m\u00e5 oppgi en ny 'hoved' API-n\u00f8kkel fra UptimeRobot", "title": "Godkjenne integrering p\u00e5 nytt" }, "user": { "data": { "api_key": "API-n\u00f8kkel" }, - "description": "Du m\u00e5 levere en skrivebeskyttet API-n\u00f8kkel fra UptimeRobot" + "description": "Du m\u00e5 oppgi \"hoved\" API-n\u00f8kkelen fra UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/pl.json b/homeassistant/components/uptimerobot/translations/pl.json index 18c40afec1e..d5698192e6d 100644 --- a/homeassistant/components/uptimerobot/translations/pl.json +++ b/homeassistant/components/uptimerobot/translations/pl.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_api_key": "Nieprawid\u0142owy klucz API", + "not_main_key": "Wykryto nieprawid\u0142owy typ klucza API, u\u017cyj \"g\u0142\u00f3wnego\" klucza API", "reauth_failed_matching_account": "Podany klucz API nie jest zgodny z identyfikatorem konta istniej\u0105cej konfiguracji.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Klucz API" }, - "description": "Musisz poda\u0107 nowy, tylko do odczytu, klucz API od Uptime Robot", + "description": "Musisz poda\u0107 nowy \"g\u0142\u00f3wny\" klucz API od Uptime Robot", "title": "Ponownie uwierzytelnij integracj\u0119" }, "user": { "data": { "api_key": "Klucz API" }, - "description": "Musisz poda\u0107 klucz API (tylko do odczytu) od Uptime Robot" + "description": "Musisz poda\u0107 \"g\u0142\u00f3wny\" klucz API od Uptime Robot" } } } diff --git a/homeassistant/components/uptimerobot/translations/pt-BR.json b/homeassistant/components/uptimerobot/translations/pt-BR.json index 0d9bea96b12..4e905f67b31 100644 --- a/homeassistant/components/uptimerobot/translations/pt-BR.json +++ b/homeassistant/components/uptimerobot/translations/pt-BR.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Falha ao conectar", "invalid_api_key": "Chave de API inv\u00e1lida", + "not_main_key": "Tipo de chave de API incorreto detectado, use a chave de API 'principal'", "reauth_failed_matching_account": "A chave de API fornecida n\u00e3o corresponde ao ID da conta da configura\u00e7\u00e3o existente.", "unknown": "Erro inesperado" }, @@ -17,14 +18,14 @@ "data": { "api_key": "Chave da API" }, - "description": "Voc\u00ea precisa fornecer uma nova chave de API somente leitura do UptimeRobot", + "description": "Voc\u00ea precisa fornecer uma nova chave de API 'principal' do UptimeRobot", "title": "Reautenticar Integra\u00e7\u00e3o" }, "user": { "data": { "api_key": "Chave da API" }, - "description": "Voc\u00ea precisa fornecer uma chave de API somente leitura do UptimeRobot" + "description": "Voc\u00ea precisa fornecer a chave de API 'principal' do UptimeRobot" } } } diff --git a/homeassistant/components/uptimerobot/translations/ru.json b/homeassistant/components/uptimerobot/translations/ru.json index fd26dbf969a..4497dc5a362 100644 --- a/homeassistant/components/uptimerobot/translations/ru.json +++ b/homeassistant/components/uptimerobot/translations/ru.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", + "not_main_key": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0442\u0438\u043f \u043a\u043b\u044e\u0447\u0430 API, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043b\u044e\u0447 API 'main'.", "reauth_failed_matching_account": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, @@ -17,14 +18,14 @@ "data": { "api_key": "\u041a\u043b\u044e\u0447 API" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u043a\u043b\u044e\u0447 API UptimeRobot \u0441 \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f", + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 'main' \u043a\u043b\u044e\u0447 API UptimeRobot.", "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043b\u044e\u0447 API UptimeRobot \u0441 \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 'main' \u043a\u043b\u044e\u0447 API UptimeRobot." } } } diff --git a/homeassistant/components/uptimerobot/translations/tr.json b/homeassistant/components/uptimerobot/translations/tr.json index c209afdfd8e..8f7291d57fd 100644 --- a/homeassistant/components/uptimerobot/translations/tr.json +++ b/homeassistant/components/uptimerobot/translations/tr.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", + "not_main_key": "Yanl\u0131\u015f API anahtar\u0131 t\u00fcr\u00fc alg\u0131land\u0131, 'ana' API anahtar\u0131n\u0131 kullan\u0131n", "reauth_failed_matching_account": "Sa\u011flad\u0131\u011f\u0131n\u0131z API anahtar\u0131, mevcut yap\u0131land\u0131rman\u0131n hesap kimli\u011fiyle e\u015fle\u015fmiyor.", "unknown": "Beklenmeyen hata" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API Anahtar\u0131" }, - "description": "UptimeRobot'tan yeni bir salt okunur API anahtar\u0131 sa\u011flaman\u0131z gerekiyor", + "description": "UptimeRobot'tan yeni bir 'ana' API anahtar\u0131 sa\u011flaman\u0131z gerekiyor", "title": "Entegrasyonu Yeniden Do\u011frula" }, "user": { "data": { "api_key": "API Anahtar\u0131" }, - "description": "UptimeRobot'tan salt okunur bir API anahtar\u0131 sa\u011flaman\u0131z gerekiyor" + "description": "UptimeRobot'tan 'ana' API anahtar\u0131n\u0131 sa\u011flaman\u0131z gerekiyor" } } } diff --git a/homeassistant/components/uptimerobot/translations/zh-Hant.json b/homeassistant/components/uptimerobot/translations/zh-Hant.json index 8b01cab6d7c..e8edfbf1934 100644 --- a/homeassistant/components/uptimerobot/translations/zh-Hant.json +++ b/homeassistant/components/uptimerobot/translations/zh-Hant.json @@ -9,6 +9,7 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_api_key": "API \u91d1\u9470\u7121\u6548", + "not_main_key": "\u5075\u6e2c\u5230\u932f\u8aa4\u7684 API \u91d1\u9470\u985e\u578b\u3001\u4f7f\u7528 'main' API \u91d1\u9470", "reauth_failed_matching_account": "\u6240\u63d0\u4f9b\u7684\u91d1\u9470\u8207\u73fe\u6709\u8a2d\u5b9a\u5e33\u865f ID \u4e0d\u7b26\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, @@ -17,14 +18,14 @@ "data": { "api_key": "API \u91d1\u9470" }, - "description": "\u9700\u8981\u63d0\u4f9b\u7531 UptimeRobot \u53d6\u5f97\u4e00\u7d44\u65b0\u7684\u552f\u8b80 API \u91d1\u9470", + "description": "\u5fc5\u9808\u63d0\u4f9b\u7531 UptimeRobot \u53d6\u5f97\u4e00\u7d44\u65b0\u7684 'main' API \u91d1\u9470", "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" }, "user": { "data": { "api_key": "API \u91d1\u9470" }, - "description": "\u9700\u8981\u63d0\u4f9b\u7531 UptimeRobot \u53d6\u5f97\u552f\u8b80 API \u91d1\u9470" + "description": "\u5fc5\u9808\u63d0\u4f9b\u7531 UptimeRobot \u53d6\u5f97\u4e4b 'main' API \u91d1\u9470" } } } diff --git a/homeassistant/components/utility_meter/translations/bg.json b/homeassistant/components/utility_meter/translations/bg.json new file mode 100644 index 00000000000..35cfa0ad1d7 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/bg.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/ca.json b/homeassistant/components/utility_meter/translations/ca.json new file mode 100644 index 00000000000..6781fc9533c --- /dev/null +++ b/homeassistant/components/utility_meter/translations/ca.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Cicle de reinici del comptador", + "delta_values": "Valors delta", + "name": "Nom", + "net_consumption": "Consum net", + "offset": "Despla\u00e7ament del reinici del comptador", + "source": "Sensor d'entrada", + "tariffs": "Tarifes suportades" + }, + "data_description": { + "delta_values": "Activa-ho si els les lectures s\u00f3n valors delta (actual - anterior) en lloc de valors absoluts.", + "net_consumption": "Activa-ho si \u00e9s un comptador net, \u00e9s a dir, pot augmentar i disminuir.", + "offset": "Despla\u00e7a el dia de restabliment mensual del comptador.", + "tariffs": "Llista de tarifes admeses, deixa-la en blanc si utilitzes una \u00fanica tarifa." + }, + "description": "El sensor de comptador permet fer un seguiment dels consums de diversos serveis (per exemple, energia, gas, aigua o calefacci\u00f3) durant un per\u00edode de temps establert, normalment mensual. El sensor comptador tamb\u00e9 permet dividir el consum per tarifes; en aquest cas, es crear\u00e0 un sensor per a cada tarifa i una entitat de selecci\u00f3 que tria la tarifa actual.", + "title": "Afegeix comptador" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensor d'entrada" + } + } + } + }, + "title": "Comptador" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/cs.json b/homeassistant/components/utility_meter/translations/cs.json new file mode 100644 index 00000000000..552ce790742 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/cs.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Cyklus vynulov\u00e1n\u00ed m\u011b\u0159i\u010de", + "delta_values": "Rozd\u00edlov\u00e9 hodnoty", + "name": "Jm\u00e9no", + "net_consumption": "\u010cist\u00e1 spot\u0159eba", + "offset": "Posun vynulov\u00e1n\u00ed m\u011b\u0159i\u010de", + "source": "Vstupn\u00ed senzor", + "tariffs": "Podporovan\u00e9 tarify" + }, + "data_description": { + "delta_values": "Povolte, pokud jsou zdrojov\u00e9 hodnoty rozd\u00edlov\u00e9 hodnoty od posledn\u00edho ode\u010dtu nam\u00edsto absolutn\u00edch hodnot.", + "net_consumption": "Povolte, pokud je zdroj \u010dist\u00fdm m\u011b\u0159i\u010dem, co\u017e znamen\u00e1, \u017ee se m\u016f\u017ee zvy\u0161ovat i sni\u017eovat.", + "offset": "Posunut\u00ed dne m\u011bs\u00ed\u010dn\u00edho vynulov\u00e1n\u00ed m\u011b\u0159i\u010de.", + "tariffs": "Seznam podporovan\u00fdch tarif\u016f, pokud je pot\u0159eba pouze jeden tarif, nechte pr\u00e1zdn\u00e9." + }, + "description": "Vytvo\u0159\u00ed senzor, kter\u00fd sleduje spot\u0159ebu r\u016fzn\u00fdch zdroj\u016f (nap\u0159. energie, plynu, vody, vyt\u00e1p\u011bn\u00ed) za nastaven\u00e9 \u010dasov\u00e9 obdob\u00ed, obvykle m\u011bs\u00ed\u010dn\u011b. Senzor m\u011b\u0159i\u010de m\u00e9di\u00ed voliteln\u011b podporuje rozd\u011blen\u00ed spot\u0159eby podle tarif\u016f, v takov\u00e9m p\u0159\u00edpad\u011b se vytvo\u0159\u00ed jeden senzor pro ka\u017ed\u00fd tarif a tak\u00e9 v\u00fdb\u011brov\u00e1 entita pro v\u00fdb\u011br aktu\u00e1ln\u00edho tarifu.", + "title": "P\u0159idat m\u011b\u0159i\u010d" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Vstupn\u00ed senzor" + } + } + } + }, + "title": "M\u011b\u0159i\u010d" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/de.json b/homeassistant/components/utility_meter/translations/de.json new file mode 100644 index 00000000000..870772e0dbe --- /dev/null +++ b/homeassistant/components/utility_meter/translations/de.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Z\u00e4hler-Reset-Zyklus", + "delta_values": "Delta-Werte", + "name": "Name", + "net_consumption": "Netzverbrauch", + "offset": "Z\u00e4hler-Reset-Offset", + "source": "Eingangssensor", + "tariffs": "Unterst\u00fctzte Tarife" + }, + "data_description": { + "delta_values": "Aktiviere diese Option, wenn die Quellwerte Deltawerte seit dem letzten Lesen anstelle von absoluten Werten sind.", + "net_consumption": "Aktiviere diese Option, wenn die Quelle ein Nettoz\u00e4hler ist, was bedeutet, dass sie sowohl steigen als auch fallen kann.", + "offset": "Versetzen des Tages einer monatlichen Z\u00e4hlerr\u00fccksetzung.", + "tariffs": "Eine Liste der unterst\u00fctzten Tarife; leer lassen, wenn nur ein einziger Tarif ben\u00f6tigt wird." + }, + "description": "Erstelle einen Sensor, der den Verbrauch verschiedener Versorgungsleistungen (z. B. Energie, Gas, Wasser, Heizung) \u00fcber einen konfigurierten Zeitraum, in der Regel monatlich, erfasst. Der Sensor f\u00fcr den Verbrauchsz\u00e4hler unterst\u00fctzt optional die Aufteilung des Verbrauchs nach Tarifen. In diesem Fall wird ein Sensor f\u00fcr jeden Tarif sowie eine Auswahlm\u00f6glichkeit zur Auswahl des aktuellen Tarifs erstellt.", + "title": "Verbrauchsz\u00e4hler hinzuf\u00fcgen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Eingangssensor" + } + } + } + }, + "title": "Verbrauchsz\u00e4hler" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/el.json b/homeassistant/components/utility_meter/translations/el.json new file mode 100644 index 00000000000..3264503bab9 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/el.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae", + "delta_values": "\u03a4\u03b9\u03bc\u03ad\u03c2 \u0394\u03ad\u03bb\u03c4\u03b1", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", + "net_consumption": "\u039a\u03b1\u03b8\u03b1\u03c1\u03ae \u03ba\u03b1\u03c4\u03b1\u03bd\u03ac\u03bb\u03c9\u03c3\u03b7", + "offset": "\u039c\u03b5\u03c4\u03b1\u03c4\u03cc\u03c0\u03b9\u03c3\u03b7 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae", + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", + "tariffs": "\u03a5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03b1" + }, + "data_description": { + "delta_values": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03ac\u03bd \u03bf\u03b9 \u03c4\u03b9\u03bc\u03ad\u03c2 \u03c0\u03b7\u03b3\u03ae\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03c4\u03b9\u03bc\u03ad\u03c2 \u03b4\u03ad\u03bb\u03c4\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03c4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7 \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03b1\u03c0\u03cc\u03bb\u03c5\u03c4\u03b5\u03c2 \u03c4\u03b9\u03bc\u03ad\u03c2.", + "net_consumption": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03ac\u03bd \u03b7 \u03c0\u03b7\u03b3\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03b1\u03b8\u03b1\u03c1\u03cc\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae\u03c2, \u03b4\u03b7\u03bb\u03b1\u03b4\u03ae \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03c4\u03cc\u03c3\u03bf \u03bd\u03b1 \u03b1\u03c5\u03be\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03cc\u03c3\u03bf \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03bc\u03b5\u03b9\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9.", + "offset": "\u0391\u03bd\u03c4\u03b9\u03c3\u03c4\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b7\u03bc\u03ad\u03c1\u03b1\u03c2 \u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03b1\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c4\u03bf\u03c5 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae.", + "tariffs": "\u039c\u03b9\u03b1 \u03bb\u03af\u03c3\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b1 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03b1, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03ae \u03b5\u03ac\u03bd \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03cc\u03bd\u03bf \u03ad\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf." + }, + "description": "\u039f \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae \u03ba\u03bf\u03b9\u03bd\u03ae\u03c2 \u03c9\u03c6\u03ad\u03bb\u03b5\u03b9\u03b1\u03c2 \u03c0\u03b1\u03c1\u03ad\u03c7\u03b5\u03b9 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7\u03c2 \u03c4\u03c9\u03bd \u03ba\u03b1\u03c4\u03b1\u03bd\u03b1\u03bb\u03ce\u03c3\u03b5\u03c9\u03bd \u03b4\u03b9\u03b1\u03c6\u03cc\u03c1\u03c9\u03bd \u03b2\u03bf\u03b7\u03b8\u03b7\u03c4\u03b9\u03ba\u03ce\u03bd \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03b9\u03ce\u03bd (\u03c0.\u03c7. \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1, \u03c6\u03c5\u03c3\u03b9\u03ba\u03cc \u03b1\u03ad\u03c1\u03b9\u03bf, \u03bd\u03b5\u03c1\u03cc, \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7) \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03bc\u03ad\u03bd\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ae \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf, \u03c3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03b1. \u039f \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03c4\u03bf\u03c5 \u03b2\u03bf\u03b7\u03b8\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03b9 \u03b5\u03c0\u03af\u03c3\u03b7\u03c2 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03cc \u03c4\u03b7\u03c2 \u03ba\u03b1\u03c4\u03b1\u03bd\u03ac\u03bb\u03c9\u03c3\u03b7\u03c2 \u03b1\u03bd\u03ac \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03b1.\n \u0397 \u03bc\u03b5\u03c4\u03b1\u03c4\u03cc\u03c0\u03b9\u03c3\u03b7 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c0\u03b5\u03b9 \u03c4\u03b7 \u03bc\u03b5\u03c4\u03b1\u03c4\u03cc\u03c0\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b7\u03bc\u03ad\u03c1\u03b1\u03c2 \u03c4\u03b7\u03c2 \u03bc\u03b7\u03bd\u03b9\u03b1\u03af\u03b1\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c4\u03bf\u03c5 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae.\n \u03a4\u03b1 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03bb\u03af\u03c3\u03c4\u03b1 \u03bc\u03b5 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03b1 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b1 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1\u03c4\u03b1, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03ae \u03b5\u03ac\u03bd \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03cc\u03bd\u03bf \u03ad\u03bd\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf.", + "title": "\u039d\u03ad\u03bf\u03c2 \u03b2\u03bf\u03b7\u03b8\u03b7\u03c4\u03b9\u03ba\u03cc\u03c2 \u03bc\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae\u03c2" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5" + } + } + } + }, + "title": "\u039c\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae\u03c2 \u03ba\u03bf\u03b9\u03bd\u03ae\u03c2 \u03c9\u03c6\u03ad\u03bb\u03b5\u03b9\u03b1\u03c2" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/et.json b/homeassistant/components/utility_meter/translations/et.json new file mode 100644 index 00000000000..579933130fb --- /dev/null +++ b/homeassistant/components/utility_meter/translations/et.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "M\u00f5\u00f5turi l\u00e4htestamise ts\u00fckkel", + "delta_values": "Delta v\u00e4\u00e4rtused", + "name": "Nimi", + "net_consumption": "Netotarbimine", + "offset": "Arvesti l\u00e4htestamise nihe", + "source": "Sisendandur", + "tariffs": "Toetatud tariifid" + }, + "data_description": { + "delta_values": "Luba kui l\u00e4htev\u00e4\u00e4rtused on absoluutv\u00e4\u00e4rtuste asemel delta v\u00e4\u00e4rtused alates viimasest lugemisest.", + "net_consumption": "Luba, kui allikaks on netoarvesti, mis t\u00e4hendab, et see v\u00f5ib nii suureneda kui ka v\u00e4heneda.", + "offset": "Arvesti igakuise l\u00e4htestamise p\u00e4eva nihutamine.", + "tariffs": "Toetatud tariifide loend, j\u00e4ta t\u00fchjaks kui vajad ainult \u00fchte tariifi." + }, + "description": "Loo andur mis j\u00e4lgib erinevate kommunaalteenuste (nt energia, gaas, vesi, k\u00fcte) tarbimist seadistatud aja jooksul, tavaliselt kord kuus. Kommunaalarvesti andur toetab valikuliselt tarbimise jagamist tariifide kaupa, sellisel juhul luuakse iga tariifi kohta \u00fcks andur ning ka valitud olem kehtiva tariifi valimiseks.", + "title": "Lisa kommunaalm\u00f5\u00f5tja" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sisendandur" + } + } + } + }, + "title": "Kommunaalarvesti" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/fr.json b/homeassistant/components/utility_meter/translations/fr.json new file mode 100644 index 00000000000..9659cf413bb --- /dev/null +++ b/homeassistant/components/utility_meter/translations/fr.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Cycle de remise \u00e0 z\u00e9ro du compteur", + "delta_values": "Valeurs delta", + "name": "Nom", + "net_consumption": "Consommation nette", + "offset": "D\u00e9calage de la remise \u00e0 z\u00e9ro du compteur", + "source": "Capteur d'entr\u00e9e", + "tariffs": "Tarifs pris en charge" + }, + "data_description": { + "delta_values": "Activer si les valeurs de la source sont des valeurs delta depuis la derni\u00e8re lecture au lieu de valeurs absolues.", + "net_consumption": "Activer si la source est un compteur net, c'est-\u00e0-dire qu'il peut \u00e0 la fois augmenter et diminuer.", + "offset": "D\u00e9calage du jour de r\u00e9initialisation mensuelle du compteur.", + "tariffs": "Liste des tarifs pris en charge\u00a0; laisser vide si un seul tarif est n\u00e9cessaire." + }, + "description": "Cr\u00e9ez un capteur permettant de suivre les consommations de divers services publics (comme l'\u00e9nergie, le gaz, l'eau ou le chauffage) sur une p\u00e9riode configur\u00e9e, g\u00e9n\u00e9ralement mensuelle. Le capteur de compteur de services publics peut, si n\u00e9cessaire, r\u00e9partir la consommation par tarifs, auquel cas un capteur sera cr\u00e9\u00e9 pour chaque tarif ainsi qu'une entit\u00e9 de s\u00e9lection permettant de choisir le tarif actuel.", + "title": "Ajouter un compteur de services publics (eau, gaz, \u00e9lectricit\u00e9\u2026)" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Capteur d'entr\u00e9e" + } + } + } + }, + "title": "Compteur de services publics (eau, gaz, \u00e9lectricit\u00e9\u2026)" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/hu.json b/homeassistant/components/utility_meter/translations/hu.json new file mode 100644 index 00000000000..646517cfd24 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/hu.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "M\u00e9r\u0151 null\u00e1z\u00e1si ciklusa", + "delta_values": "Delta \u00e9rt\u00e9kek", + "name": "Elnevez\u00e9s", + "net_consumption": "Nett\u00f3 fogyaszt\u00e1s", + "offset": "M\u00e9r\u0151 null\u00e1z\u00e1s\u00e1nak eltol\u00e1sa", + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", + "tariffs": "T\u00e1mogatott tarif\u00e1k" + }, + "data_description": { + "delta_values": "Akkor kapcsolja be, ha az \u00e9rt\u00e9kek az utols\u00f3 olvasat \u00f3ta k\u00fcl\u00f6nb\u00f6zeti \u00e9rt\u00e9kek, nem pedig abszol\u00fat \u00e9rt\u00e9kek.", + "net_consumption": "Enged\u00e9lyezze, ha a forr\u00e1s nett\u00f3 m\u00e9r\u0151, ami azt jelenti, hogy n\u00f6vekedhet \u00e9s cs\u00f6kkenhet is.", + "offset": "A havi m\u00e9r\u0151-vissza\u00e1ll\u00edt\u00e1s napj\u00e1nak eltol\u00e1sa.", + "tariffs": "A t\u00e1mogatott tarif\u00e1k list\u00e1ja, hagyja \u00fcresen, ha csak egyetlen tarif\u00e1ra van sz\u00fcks\u00e9g." + }, + "description": "A k\u00f6z\u00fczemi fogyaszt\u00e1sm\u00e9r\u0151 \u00e9rz\u00e9kel\u0151 lehet\u0151s\u00e9get ad a k\u00fcl\u00f6nf\u00e9le k\u00f6zm\u0171vek (pl. energia, g\u00e1z, v\u00edz, f\u0171t\u00e9s) fogyaszt\u00e1s\u00e1nak nyomon k\u00f6vet\u00e9s\u00e9re egy konfigur\u00e1lt id\u0151tartamon kereszt\u00fcl, jellemz\u0151en havonta. Az \u00e9rz\u00e9kel\u0151 a fogyaszt\u00e1s tarif\u00e1k szerinti megoszt\u00e1s\u00e1t is t\u00e1mogatja.", + "title": "\u00daj k\u00f6z\u00fczemi m\u00e9r\u0151" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151" + } + } + } + }, + "title": "K\u00f6z\u00fczemi fogyaszt\u00e1sm\u00e9r\u0151" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/id.json b/homeassistant/components/utility_meter/translations/id.json new file mode 100644 index 00000000000..d2f9bab72c5 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/id.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Siklus setel ulang meter", + "delta_values": "Nilai delta", + "name": "Nama", + "net_consumption": "Konsumsi netto", + "offset": "Offset setel ulang meter", + "source": "Sensor input", + "tariffs": "Tarif yang didukung" + }, + "data_description": { + "delta_values": "Aktifkan jika nilai sumber adalah nilai delta sejak pembacaan terakhir, bukan nilai absolut.", + "net_consumption": "Aktifkan jika sumbernya adalah ukuran netto, artinya bisa bertambah dan berkurang.", + "offset": "Ofset hari setelah penyetelan ulang bulanan", + "tariffs": "Daftar tarif yang didukung, kosongkan jika hanya diperlukan satu tarif." + }, + "description": "Buat sensor yang melacak konsumsi berbagai utilitas (misalnya, energi, gas, air, pemanas) selama periode waktu yang dikonfigurasi, biasanya bulanan. Sensor meter utilitas secara opsional mendukung pemisahan konsumsi berdasarkan tarif, dalam hal ini satu sensor untuk setiap tarif dibuat serta entitas terpilih untuk memilih tarif saat ini.", + "title": "Tambahkan Meter Utilitas" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensor Input" + } + } + } + }, + "title": "Meter Utilitas" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/it.json b/homeassistant/components/utility_meter/translations/it.json new file mode 100644 index 00000000000..03512cc0b7a --- /dev/null +++ b/homeassistant/components/utility_meter/translations/it.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Ciclo di ripristino del contatore", + "delta_values": "Valori delta", + "name": "Nome", + "net_consumption": "Consumo netto", + "offset": "Offset azzeramento contatore", + "source": "Sensore di ingresso", + "tariffs": "Tariffe supportate" + }, + "data_description": { + "delta_values": "Abilita se i valori di origine sono valori delta dall'ultima lettura anzich\u00e9 valori assoluti.", + "net_consumption": "Abilita se la sorgente \u00e8 un contatore netto, il che significa che pu\u00f2 sia aumentare che diminuire.", + "offset": "Offset del giorno di un ripristino mensile del contatore.", + "tariffs": "Un elenco di tariffe supportate, lascia vuoto se \u00e8 necessaria una sola tariffa." + }, + "description": "Crea un sensore che tenga traccia del consumo di varie utenze (ad es. energia, gas, acqua, riscaldamento) in un periodo di tempo configurato, generalmente mensile. Il sensore del contatore di utenze supporta opzionalmente la suddivisione del consumo per tariffe, in tal caso viene creato un sensore per ciascuna tariffa e un'entit\u00e0 selezionata per scegliere la tariffa corrente.", + "title": "Aggiungi misuratore di utilit\u00e0" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensore di ingresso" + } + } + } + }, + "title": "Misuratore di utilit\u00e0" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/ja.json b/homeassistant/components/utility_meter/translations/ja.json new file mode 100644 index 00000000000..90c175f16ec --- /dev/null +++ b/homeassistant/components/utility_meter/translations/ja.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u30e1\u30fc\u30bf\u30fc\u30ea\u30bb\u30c3\u30c8\u306e\u30b5\u30a4\u30af\u30eb", + "delta_values": "\u30c7\u30eb\u30bf\u5024", + "name": "\u540d\u524d", + "net_consumption": "\u7d14\u6d88\u8cbb\u91cf", + "offset": "\u30e1\u30fc\u30bf\u30fc\u30ea\u30bb\u30c3\u30c8\u30aa\u30d5\u30bb\u30c3\u30c8", + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", + "tariffs": "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u95a2\u7a0e(Tariffs)" + }, + "data_description": { + "delta_values": "\u30bd\u30fc\u30b9\u5024\u304c\u7d76\u5bfe\u5024\u3067\u306f\u306a\u304f\u3001\u6700\u5f8c\u306e\u8aad\u307f\u53d6\u308a\u4ee5\u964d\u304c\u30c7\u30eb\u30bf\u5024\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u6709\u52b9\u306b\u3057\u307e\u3059\u3002", + "net_consumption": "\u30bd\u30fc\u30b9\u304c\u30cd\u30c3\u30c8\u30e1\u30fc\u30bf(net meter)\u3001\u3064\u307e\u308a\u5897\u52a0\u3082\u3059\u308b\u3057\u6e1b\u5c11\u3082\u3059\u308b\u5834\u5408\u306f\u3001\u6709\u52b9\u306b\u3057\u307e\u3059\u3002", + "offset": "\u6bce\u6708\u306e\u30e1\u30fc\u30bf\u30fc\u30ea\u30bb\u30c3\u30c8\u306e\u65e5\u3092\u30aa\u30d5\u30bb\u30c3\u30c8\u3057\u307e\u3059\u3002", + "tariffs": "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u6599\u91d1(Tariff)\u306e\u30ea\u30b9\u30c8\u3002\u5358\u4e00\u306e\u6599\u91d1\u8868\u306e\u307f\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" + }, + "description": "\u8a2d\u5b9a\u3055\u308c\u305f\u671f\u9593\u3001\u901a\u5e38\u306f\u6bce\u6708\u3001\u69d8\u3005\u306a\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3(\u30a8\u30cd\u30eb\u30ae\u30fc\u3001\u30ac\u30b9\u3001\u6c34\u3001\u6696\u623f\u306a\u3069)\u306e\u6d88\u8cbb\u91cf\u3092\u8ffd\u8de1\u3059\u308b\u30bb\u30f3\u30b5\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3068\u3057\u3066\u3001\u6599\u91d1\u8868\u306b\u3088\u308b\u6d88\u8cbb\u91cf\u306e\u5206\u5272\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u307e\u3059\u3002\u3053\u306e\u5834\u5408\u3001\u5404\u6599\u91d1\u8868\u306e\u305f\u3081\u306e1\u3064\u306e\u30bb\u30f3\u30b5\u30fc\u3068\u3001\u73fe\u5728\u306e\u6599\u91d1(Tariff)\u3092\u9078\u629e\u3059\u308b\u305f\u3081\u306e\u9078\u629e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u4f5c\u6210\u3055\u308c\u307e\u3059\u3002", + "title": "\u65b0\u3057\u3044\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30e1\u30fc\u30bf\u30fc" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc" + } + } + } + }, + "title": "\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30e1\u30fc\u30bf\u30fc" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/nl.json b/homeassistant/components/utility_meter/translations/nl.json new file mode 100644 index 00000000000..8e1d986ac6b --- /dev/null +++ b/homeassistant/components/utility_meter/translations/nl.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Meter reset cyclus", + "delta_values": "Deltawaarden", + "name": "Name", + "net_consumption": "Netto verbruik", + "offset": "Meter reset offset", + "source": "Invoer sensor", + "tariffs": "Ondersteunde tarieven" + }, + "data_description": { + "delta_values": "Schakel in als de bronwaarden deltawaarden zijn sinds de laatste meting in plaats van absolute waarden.", + "net_consumption": "Schakel in als de bron een nettometer is, wat betekent dat deze zowel kan toenemen als afnemen.", + "offset": "Compenseer de dag van een maandelijkse meterreset.", + "tariffs": "Een lijst met ondersteunde tarieven, laat leeg als er maar \u00e9\u00e9n tarief nodig is." + }, + "description": "Cre\u00eber een sensor die het verbruik van verschillende nutsvoorzieningen (bv. energie, gas, water, verwarming) over een geconfigureerde tijdsperiode bijhoudt, meestal maandelijks. De sensor voor de meter voor nutsvoorzieningen ondersteunt optioneel het opsplitsen van het verbruik in tarieven, in dat geval wordt een sensor voor elk tarief aangemaakt, evenals een selectie-entiteit om het huidige tarief te kiezen.", + "title": "Nutsmeter toevoegen" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Invoer sensor" + } + } + } + }, + "title": "Nutsmeter" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/no.json b/homeassistant/components/utility_meter/translations/no.json new file mode 100644 index 00000000000..05218c7010b --- /dev/null +++ b/homeassistant/components/utility_meter/translations/no.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Syklus for tilbakestilling av m\u00e5ler", + "delta_values": "Delta-verdier", + "name": "Navn", + "net_consumption": "Netto forbruk", + "offset": "Forskyvning for tilbakestilling av meter", + "source": "Inngangssensor", + "tariffs": "St\u00f8ttede tariffer" + }, + "data_description": { + "delta_values": "Aktiver hvis kildeverdiene er deltaverdier siden siste lesing i stedet for absolutte verdier.", + "net_consumption": "Aktiver hvis kilden er en nettom\u00e5ler, noe som betyr at den b\u00e5de kan \u00f8ke og redusere.", + "offset": "Forskyv dagen for en m\u00e5nedlig tilbakestilling av m\u00e5leren.", + "tariffs": "En liste over st\u00f8ttede tariffer, la st\u00e5 tom hvis bare en enkelt tariff er n\u00f8dvendig." + }, + "description": "Lag en sensor som sporer forbruket til ulike verkt\u00f8y (f.eks. energi, gass, vann, oppvarming) over en konfigurert tidsperiode, vanligvis m\u00e5nedlig. M\u00e5lersensoren st\u00f8tter valgfritt \u00e5 dele forbruket etter tariffer, i s\u00e5 fall opprettes en sensor for hver tariff samt en valgt enhet for \u00e5 velge gjeldende tariff.", + "title": "Legg til verkt\u00f8ym\u00e5ler" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Inngangssensor" + } + } + } + }, + "title": "Verkt\u00f8ym\u00e5ler" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/pl.json b/homeassistant/components/utility_meter/translations/pl.json new file mode 100644 index 00000000000..9be6f68384b --- /dev/null +++ b/homeassistant/components/utility_meter/translations/pl.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Cykl resetowania licznika", + "delta_values": "Warto\u015bci delta", + "name": "Nazwa", + "net_consumption": "Zu\u017cycie netto", + "offset": "Przesuni\u0119cie resetowania licznika", + "source": "Sensor wej\u015bciowy", + "tariffs": "Obs\u0142ugiwane taryfy" + }, + "data_description": { + "delta_values": "W\u0142\u0105cz, je\u015bli warto\u015bci \u017ar\u00f3d\u0142owe s\u0105 warto\u015bciami delta od ostatniego odczytu, a nie warto\u015bciami bezwzgl\u0119dnymi.", + "net_consumption": "W\u0142\u0105cz, je\u015bli \u017ar\u00f3d\u0142em jest licznik netto, co oznacza, \u017ce jego warto\u015bci mog\u0105 si\u0119 zar\u00f3wno zwi\u0119ksza\u0107 jak i zmniejsza\u0107.", + "offset": "Przesuni\u0119cie dnia miesi\u0119cznego zerowania licznika.", + "tariffs": "Lista obs\u0142ugiwanych taryf. Pozostaw puste, je\u015bli potrzebna jest tylko jedna taryfa." + }, + "description": "Utw\u00f3rz sensor, kt\u00f3ry \u015bledzi u\u017cycie r\u00f3\u017cnych medi\u00f3w (np. energii, gazu, wody, ogrzewania) przez skonfigurowany okres czasu, zwykle co miesi\u0105c. Sensor licznika medi\u00f3w opcjonalnie obs\u0142uguje podzia\u0142 u\u017cycia wed\u0142ug taryf, w takim przypadku tworzony jest jeden czujnik dla ka\u017cdej taryfy oraz encja do wyboru aktualnej taryfy.", + "title": "Dodaj licznik medi\u00f3w" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensor wej\u015bciowy" + } + } + } + }, + "title": "Licznik medi\u00f3w" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/pt-BR.json b/homeassistant/components/utility_meter/translations/pt-BR.json new file mode 100644 index 00000000000..856360b05b2 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/pt-BR.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Ciclo de reinicializa\u00e7\u00e3o do medidor", + "delta_values": "Valores delta", + "name": "Nome", + "net_consumption": "Consumo l\u00edquido", + "offset": "Compensa\u00e7\u00e3o do reajuste do medidor", + "source": "Sensor de entrada", + "tariffs": "Tarifas compat\u00edveis" + }, + "data_description": { + "delta_values": "Habilite se os valores de origem forem valores delta desde a \u00faltima leitura em vez de valores absolutos.", + "net_consumption": "Ative se a fonte for um medidor de rede, o que significa que pode aumentar e diminuir.", + "offset": "Desloque o dia de uma reinicializa\u00e7\u00e3o mensal do medidor.", + "tariffs": "Uma lista de tarifas suportadas, deixe em branco se apenas uma \u00fanica tarifa for necess\u00e1ria." + }, + "description": "Crie um sensor que rastreie o consumo de v\u00e1rias utilidades (por exemplo, energia, g\u00e1s, \u00e1gua, aquecimento) durante um per\u00edodo de tempo configurado, normalmente mensalmente. O sensor do contador da concession\u00e1ria suporta opcionalmente a divis\u00e3o do consumo por tarif\u00e1rio, neste caso \u00e9 criado um sensor para cada tarif\u00e1rio e uma entidade seleccionada para escolher o tarif\u00e1rio atual.", + "title": "Adicionar medidor de utilidades" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensor de entrada" + } + } + } + }, + "title": "Medidor de utilidade" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/ru.json b/homeassistant/components/utility_meter/translations/ru.json new file mode 100644 index 00000000000..3eda01a8116 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/ru.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u0426\u0438\u043a\u043b \u0441\u0431\u0440\u043e\u0441\u0430 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430", + "delta_values": "\u0414\u0435\u043b\u044c\u0442\u0430-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "net_consumption": "\u0427\u0438\u0441\u0442\u043e\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435", + "offset": "\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0441\u0431\u0440\u043e\u0441\u0430 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430", + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", + "tariffs": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u0442\u0430\u0440\u0438\u0444\u044b" + }, + "data_description": { + "delta_values": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0434\u0435\u043b\u044c\u0442\u0430-\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0441 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0447\u0442\u0435\u043d\u0438\u044f, \u0430 \u043d\u0435 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438.", + "net_consumption": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0442\u0442\u043e-\u0441\u0447\u0435\u0442\u0447\u0438\u043a\u043e\u043c, \u0442\u043e \u0435\u0441\u0442\u044c \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043a\u0430\u043a \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f, \u0442\u0430\u043a \u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0442\u044c\u0441\u044f.", + "offset": "\u0421\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0434\u043d\u044f \u0435\u0436\u0435\u043c\u0435\u0441\u044f\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u0443\u043b\u0435\u043d\u0438\u044f \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430.", + "tariffs": "\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u0442\u0430\u0440\u0438\u0444\u043e\u0432, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0442\u0430\u0440\u0438\u0444." + }, + "description": "\u0421\u0435\u043d\u0441\u043e\u0440 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043a\u043e\u043c\u043c\u0443\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u043b\u0443\u0433 (\u044d\u043d\u0435\u0440\u0433\u0438\u0438, \u0433\u0430\u0437\u0430, \u0432\u043e\u0434\u044b, \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f) \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0438\u043e\u0434\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0431\u044b\u0447\u043d\u043e \u0435\u0436\u0435\u043c\u0435\u0441\u044f\u0447\u043d\u043e. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u044f \u043f\u043e \u0442\u0430\u0440\u0438\u0444\u0430\u043c, \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0430\u0440\u0438\u0444\u0430, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0442\u0430\u0440\u0438\u0444\u0430.", + "title": "\u0421\u0447\u0435\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u043c\u0443\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u043b\u0443\u0433" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440" + } + } + } + }, + "title": "\u0421\u0447\u0435\u0442\u0447\u0438\u043a \u043a\u043e\u043c\u043c\u0443\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0443\u0441\u043b\u0443\u0433" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/tr.json b/homeassistant/components/utility_meter/translations/tr.json new file mode 100644 index 00000000000..0eedf15d93e --- /dev/null +++ b/homeassistant/components/utility_meter/translations/tr.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Saya\u00e7 s\u0131f\u0131rlama d\u00f6ng\u00fcs\u00fc", + "delta_values": "Delta de\u011ferleri", + "name": "Ad", + "net_consumption": "Net t\u00fcketim", + "offset": "Saya\u00e7 s\u0131f\u0131rlama uzakl\u0131\u011f\u0131", + "source": "Giri\u015f sens\u00f6r\u00fc", + "tariffs": "Desteklenen tarifeler" + }, + "data_description": { + "delta_values": "Kaynak de\u011ferler, mutlak de\u011ferler yerine son okumadan itibaren delta de\u011ferler ise etkinle\u015ftirin.", + "net_consumption": "Kaynak bir net saya\u00e7 ise etkinle\u015ftirin, yani hem artabilir hem de azalabilir.", + "offset": "Ayl\u0131k saya\u00e7 s\u0131f\u0131rlama g\u00fcn\u00fcn\u00fc dengeleyin.", + "tariffs": "Desteklenen tarifelerin listesi, yaln\u0131zca tek bir tarife gerekiyorsa bo\u015f b\u0131rak\u0131n." + }, + "description": "\u00c7e\u015fitli hizmetlerin (\u00f6rn. enerji, gaz, su, \u0131s\u0131tma) t\u00fcketimini, yap\u0131land\u0131r\u0131lm\u0131\u015f bir s\u00fcre boyunca, genellikle ayl\u0131k olarak izleyen bir sens\u00f6r olu\u015fturun. \u015eebeke sayac\u0131 sens\u00f6r\u00fc iste\u011fe ba\u011fl\u0131 olarak t\u00fcketimin tarifelere g\u00f6re b\u00f6l\u00fcnmesini destekler, bu durumda her tarife i\u00e7in bir sens\u00f6r ve mevcut tarifeyi se\u00e7mek i\u00e7in se\u00e7ilen bir varl\u0131k olu\u015fturulur.", + "title": "Kullan\u0131m Sayac\u0131 Ekle" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Giri\u015f sens\u00f6r\u00fc" + } + } + } + }, + "title": "Yard\u0131mc\u0131 Saya\u00e7" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/zh-Hans.json b/homeassistant/components/utility_meter/translations/zh-Hans.json new file mode 100644 index 00000000000..a1db639adfb --- /dev/null +++ b/homeassistant/components/utility_meter/translations/zh-Hans.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u8ba1\u91cf\u590d\u4f4d\u5468\u671f", + "delta_values": "\u8f93\u5165\u4e3a\u5dee\u503c", + "name": "\u540d\u79f0", + "net_consumption": "\u6570\u503c\u53ef\u56de\u9000", + "offset": "\u8ba1\u91cf\u590d\u4f4d\u504f\u79fb", + "source": "\u8f93\u5165\u4f20\u611f\u5668", + "tariffs": "\u652f\u6301\u7684\u8d39\u7387" + }, + "data_description": { + "delta_values": "\u5982\u679c\u6e90\u6570\u636e\u662f\u81ea\u4e0a\u6b21\u8bfb\u6570\u4ee5\u6765\u7684\u5dee\u503c\uff0c\u8bf7\u542f\u7528\u6b64\u9009\u9879\u3002", + "net_consumption": "\u5982\u679c\u6e90\u6570\u636e\u4e0d\u662f\u6301\u7eed\u589e\u957f\uff0c\u800c\u662f\u53ef\u4ee5\u964d\u4f4e\uff0c\u8bf7\u542f\u7528\u6b64\u9009\u9879\u3002", + "offset": "\u6309\u6708\u8ba1\u91cf\u65f6\u590d\u4f4d\u63a8\u8fdf\u7684\u5929\u6570\u3002", + "tariffs": "\u4ee5\u5217\u8868\u7684\u5f62\u5f0f\u5217\u51fa\u6240\u6709\u53ef\u80fd\u7684\u8d39\u7387\u3002\u5982\u679c\u8d39\u7387\u552f\u4e00\uff0c\u8bf7\u7559\u7a7a\u3002" + }, + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u5468\u671f\u6027\u5730\u76d1\u6d4b\u516c\u5171\u80fd\u6e90\uff08\u4f8b\u5982\u6c34\u3001\u7535\u3001\u71c3\u6c14\uff09\u7684\u6d88\u8017\u60c5\u51b5\u3002\u6b64\u7c7b\u4f20\u611f\u5668\u8fd8\u652f\u6301\u591a\u8d39\u7387\u5206\u522b\u7edf\u8ba1\uff08\u4f8b\u5982\u9636\u68af\u6c34\u4ef7\u3001\u5cf0\u8c37\u7535\u4ef7\uff09\uff0c\u4e3a\u6bcf\u79cd\u8d39\u7387\u5206\u522b\u521b\u5efa\u4e00\u4e2a\u4f20\u611f\u5668\uff0c\u5e76\u63d0\u4f9b\u4e00\u4e2a\u9009\u62e9\u5668\u5b9e\u4f53\u6765\u9009\u62e9\u5f53\u524d\u6267\u884c\u7684\u8d39\u7387\u3002", + "title": "\u6dfb\u52a0\u4eea\u8868\u7edf\u8ba1" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u8f93\u5165\u4f20\u611f\u5668" + } + } + } + }, + "title": "\u4eea\u8868\u7edf\u8ba1" +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/zh-Hant.json b/homeassistant/components/utility_meter/translations/zh-Hant.json new file mode 100644 index 00000000000..dc23f73a89e --- /dev/null +++ b/homeassistant/components/utility_meter/translations/zh-Hant.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\u529f\u8017\u8868\u91cd\u7f6e\u9031\u671f", + "delta_values": "\u589e\u91cf\u503c", + "name": "\u540d\u7a31", + "net_consumption": "\u6de8\u8017\u80fd", + "offset": "\u529f\u8017\u8868\u91cd\u7f6e\u504f\u79fb", + "source": "\u8f38\u5165\u611f\u6e2c\u5668", + "tariffs": "\u652f\u63f4\u5206\u5340\u9069\u7528\u8cbb\u7387" + }, + "data_description": { + "delta_values": "\u5047\u5982\u4f86\u6e90\u70ba\u4e0a\u6b21\u8b80\u53d6\u589e\u91cf\u503c\u3001\u800c\u975e\u7d55\u5c0d\u503c\u5247\u958b\u555f\u3002", + "net_consumption": "\u5047\u5982\u4f86\u6e90\u70ba\u6de8\u529f\u8017\u5247\u958b\u555f\u3001\u8868\u793a\u53ef\u540c\u6642\u589e\u52a0\u53ca\u6e1b\u5c11\u3002", + "offset": "\u6bcf\u6708\u529f\u8017\u91cd\u7f6e\u504f\u79fb\u5929\u6578\u3002", + "tariffs": "\u652f\u63f4\u5206\u5340\u9069\u7528\u8cbb\u7387\u5217\u8868\uff0c\u5047\u5982\u9700\u8981\u55ae\u4e00\u532f\u7387\u3001\u5247\u4fdd\u7559\u7a7a\u767d\u3002" + }, + "description": "\u65b0\u589e\u65bc\u8a2d\u5b9a\u9031\u671f\u6642\u9593\u5167\uff08\u901a\u5e38\u70ba\u6bcf\u6708\uff09\u8ddf\u8e2a\u5404\u7a2e\u516c\u7528\u4e8b\u696d\uff08\u4f8b\u5982\uff0c\u96fb\u529b\u3001\u5929\u7136\u6c23\u3001\u6c34\u3001\u4f9b\u6696\uff09\u8017\u80fd\u6578\u503c\u611f\u6e2c\u5668\u3002\u529f\u8017\u8868\u611f\u6e2c\u5668\u540c\u6642\u9078\u9805\u652f\u63f4\u5206\u5340\u9069\u7528\u8cbb\u7387\u8a08\u7b97\uff0c\u5728\u6b64\u60c5\u6cc1\u4e0b\u3001\u6bcf\u500b\u8cbb\u7387\u7686\u6703\u65b0\u589e\u611f\u6e2c\u5668\u3001\u53ca\u9078\u64c7\u5be6\u9ad4\u4ee5\u9078\u64c7\u76ee\u524d\u8cbb\u7387\u3002", + "title": "\u65b0\u589e\u529f\u8017\u8868" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\u8f38\u5165\u611f\u6e2c\u5668" + } + } + } + }, + "title": "\u529f\u8017\u8868" +} \ No newline at end of file diff --git a/homeassistant/components/vallox/translations/bg.json b/homeassistant/components/vallox/translations/bg.json index 1c2e0aee24c..ec27ad32d0b 100644 --- a/homeassistant/components/vallox/translations/bg.json +++ b/homeassistant/components/vallox/translations/bg.json @@ -14,10 +14,8 @@ "step": { "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u0418\u043c\u0435" - }, - "title": "Vallox" + "host": "\u0425\u043e\u0441\u0442" + } } } } diff --git a/homeassistant/components/vallox/translations/ca.json b/homeassistant/components/vallox/translations/ca.json index 7a92c24a558..ba8d6287ed4 100644 --- a/homeassistant/components/vallox/translations/ca.json +++ b/homeassistant/components/vallox/translations/ca.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Amfitri\u00f3", - "name": "Nom" - }, - "description": "Configura la integraci\u00f3 Vallox. Si tens problemes amb la configuraci\u00f3, v\u00e9s a {integration_docs_url}.", - "title": "Vallox" + "host": "Amfitri\u00f3" + } } } } diff --git a/homeassistant/components/vallox/translations/cs.json b/homeassistant/components/vallox/translations/cs.json index 2a364830dbf..ffef74e8f9c 100644 --- a/homeassistant/components/vallox/translations/cs.json +++ b/homeassistant/components/vallox/translations/cs.json @@ -8,13 +8,6 @@ }, "error": { "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, - "step": { - "user": { - "data": { - "name": "Jm\u00e9no" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/vallox/translations/de.json b/homeassistant/components/vallox/translations/de.json index 661f45bdfcf..711ef828ad3 100644 --- a/homeassistant/components/vallox/translations/de.json +++ b/homeassistant/components/vallox/translations/de.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Name" - }, - "description": "Richte die Vallox-Integration ein. Wenn du Probleme mit der Konfiguration hast, gehe zu {integration_docs_url} .", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/el.json b/homeassistant/components/vallox/translations/el.json index 4b15c926247..cab5c683ae5 100644 --- a/homeassistant/components/vallox/translations/el.json +++ b/homeassistant/components/vallox/translations/el.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Vallox. \u0395\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 {integration_docs_url}.", - "title": "Vallox" + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" + } } } } diff --git a/homeassistant/components/vallox/translations/en.json b/homeassistant/components/vallox/translations/en.json index efa32604646..3390fd34854 100644 --- a/homeassistant/components/vallox/translations/en.json +++ b/homeassistant/components/vallox/translations/en.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Name" - }, - "description": "Set up the Vallox integration. If you have problems with configuration go to {integration_docs_url}.", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/es.json b/homeassistant/components/vallox/translations/es.json index 3373f38a725..373ae333ec3 100644 --- a/homeassistant/components/vallox/translations/es.json +++ b/homeassistant/components/vallox/translations/es.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nombre" - }, - "description": "Configure la integraci\u00f3n de Vallox. Si tiene problemas con la configuraci\u00f3n, vaya a {integration_docs_url} .", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/et.json b/homeassistant/components/vallox/translations/et.json index 8f3a1c7cb3e..14f1854aded 100644 --- a/homeassistant/components/vallox/translations/et.json +++ b/homeassistant/components/vallox/translations/et.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nimi" - }, - "description": "Seadista Valloxi sidumine. Kui seadistamisega on probleeme, mine aadressile {integration_docs_url}.", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/fr.json b/homeassistant/components/vallox/translations/fr.json index c9823756185..c5db6b81e9e 100644 --- a/homeassistant/components/vallox/translations/fr.json +++ b/homeassistant/components/vallox/translations/fr.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "H\u00f4te", - "name": "Nom" - }, - "description": "Mettre en place l'int\u00e9gration Vallox. Si vous rencontrez des probl\u00e8mes de configuration, acc\u00e9dez \u00e0 {integration_docs_url} .", - "title": "Vallox" + "host": "H\u00f4te" + } } } } diff --git a/homeassistant/components/vallox/translations/he.json b/homeassistant/components/vallox/translations/he.json index ad8f603cb5a..067445268a8 100644 --- a/homeassistant/components/vallox/translations/he.json +++ b/homeassistant/components/vallox/translations/he.json @@ -14,8 +14,7 @@ "step": { "user": { "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "name": "\u05e9\u05dd" + "host": "\u05de\u05d0\u05e8\u05d7" } } } diff --git a/homeassistant/components/vallox/translations/hu.json b/homeassistant/components/vallox/translations/hu.json index 1b3366b5f0e..58404c9ad73 100644 --- a/homeassistant/components/vallox/translations/hu.json +++ b/homeassistant/components/vallox/translations/hu.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "C\u00edm", - "name": "N\u00e9v" - }, - "description": "A Vallox integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa. Ha probl\u00e9m\u00e1i vannak a konfigur\u00e1ci\u00f3val, l\u00e1togasson el a https://www.home-assistant.io/integrations/vallox oldalra.", - "title": "Vallox" + "host": "C\u00edm" + } } } } diff --git a/homeassistant/components/vallox/translations/id.json b/homeassistant/components/vallox/translations/id.json index 4f8fbbff6e9..90e7c60b49f 100644 --- a/homeassistant/components/vallox/translations/id.json +++ b/homeassistant/components/vallox/translations/id.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nama" - }, - "description": "Siapkan integrasi Vallox Jika Anda memiliki masalah dengan konfigurasi, buka {integration_docs_url}.", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/it.json b/homeassistant/components/vallox/translations/it.json index 9aa0e929c50..c82991be7e7 100644 --- a/homeassistant/components/vallox/translations/it.json +++ b/homeassistant/components/vallox/translations/it.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Nome" - }, - "description": "Configura l'integrazione Vallox. Se hai problemi con la configurazione vai su {integration_docs_url}.", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/ja.json b/homeassistant/components/vallox/translations/ja.json index bac8c6a7732..a5467460d22 100644 --- a/homeassistant/components/vallox/translations/ja.json +++ b/homeassistant/components/vallox/translations/ja.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "\u30db\u30b9\u30c8", - "name": "\u540d\u524d" - }, - "description": "Vallox\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/vallox \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Vallox" + "host": "\u30db\u30b9\u30c8" + } } } } diff --git a/homeassistant/components/vallox/translations/lv.json b/homeassistant/components/vallox/translations/lv.json deleted file mode 100644 index cee9047f155..00000000000 --- a/homeassistant/components/vallox/translations/lv.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Vallox" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/vallox/translations/nl.json b/homeassistant/components/vallox/translations/nl.json index 9cc6ea07a93..b8b5b6bdc57 100644 --- a/homeassistant/components/vallox/translations/nl.json +++ b/homeassistant/components/vallox/translations/nl.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Host", - "name": "Naam" - }, - "description": "Stel de Vallox integratie in. Als u problemen heeft met de configuratie ga dan naar {integration_docs_url}", - "title": "Vallox" + "host": "Host" + } } } } diff --git a/homeassistant/components/vallox/translations/no.json b/homeassistant/components/vallox/translations/no.json index df8fd8e4535..4cc8f8321cb 100644 --- a/homeassistant/components/vallox/translations/no.json +++ b/homeassistant/components/vallox/translations/no.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Vert", - "name": "Navn" - }, - "description": "Sett opp Vallox-integrasjonen. Hvis du har problemer med konfigurasjonen, g\u00e5 til {integration_docs_url} .", - "title": "Vallox" + "host": "Vert" + } } } } diff --git a/homeassistant/components/vallox/translations/pl.json b/homeassistant/components/vallox/translations/pl.json index f96481eba82..b608d4f8e0b 100644 --- a/homeassistant/components/vallox/translations/pl.json +++ b/homeassistant/components/vallox/translations/pl.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Nazwa hosta lub adres IP", - "name": "Nazwa" - }, - "description": "Konfiguracja integracji Vallox. Je\u015bli masz problemy z konfiguracj\u0105 przejd\u017a do {integration_docs_url} .", - "title": "Vallox" + "host": "Nazwa hosta lub adres IP" + } } } } diff --git a/homeassistant/components/vallox/translations/pt-BR.json b/homeassistant/components/vallox/translations/pt-BR.json index 729e5257db8..cccafd932c8 100644 --- a/homeassistant/components/vallox/translations/pt-BR.json +++ b/homeassistant/components/vallox/translations/pt-BR.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Nome do host", - "name": "Nome" - }, - "description": "Configure a integra\u00e7\u00e3o Vallox. Se voc\u00ea tiver problemas com a configura\u00e7\u00e3o, v\u00e1 para {integration_docs_url} .", - "title": "Vallox" + "host": "Nome do host" + } } } } diff --git a/homeassistant/components/vallox/translations/ru.json b/homeassistant/components/vallox/translations/ru.json index 70e9551ae11..a1165b287d1 100644 --- a/homeassistant/components/vallox/translations/ru.json +++ b/homeassistant/components/vallox/translations/ru.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Vallox. \u0415\u0441\u043b\u0438 \u0443 \u0412\u0430\u0441 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439: {integration_docs_url}.", - "title": "Vallox" + "host": "\u0425\u043e\u0441\u0442" + } } } } diff --git a/homeassistant/components/vallox/translations/tr.json b/homeassistant/components/vallox/translations/tr.json index 1958a0535b6..8d5780d2019 100644 --- a/homeassistant/components/vallox/translations/tr.json +++ b/homeassistant/components/vallox/translations/tr.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "Sunucu", - "name": "Ad" - }, - "description": "Vallox entegrasyonunu ayarlay\u0131n. Yap\u0131land\u0131rmayla ilgili sorunlar\u0131n\u0131z varsa {integration_docs_url} adresine gidin.", - "title": "Vallox" + "host": "Sunucu" + } } } } diff --git a/homeassistant/components/vallox/translations/zh-Hant.json b/homeassistant/components/vallox/translations/zh-Hant.json index c9f1d13fa08..d9e2150dce9 100644 --- a/homeassistant/components/vallox/translations/zh-Hant.json +++ b/homeassistant/components/vallox/translations/zh-Hant.json @@ -14,11 +14,8 @@ "step": { "user": { "data": { - "host": "\u4e3b\u6a5f\u7aef", - "name": "\u540d\u7a31" - }, - "description": "\u8a2d\u5b9a Vallox \u6574\u5408\u3002\u5047\u5982\u9700\u8981\u5354\u52a9\uff0c\u8acb\u53c3\u8003\uff1a{integration_docs_url}\u3002", - "title": "Vallox" + "host": "\u4e3b\u6a5f\u7aef" + } } } } diff --git a/homeassistant/components/venstar/translations/pl.json b/homeassistant/components/venstar/translations/pl.json index ad0e76435dc..fdaf30ead9f 100644 --- a/homeassistant/components/venstar/translations/pl.json +++ b/homeassistant/components/venstar/translations/pl.json @@ -16,7 +16,7 @@ "ssl": "Certyfikat SSL", "username": "Nazwa u\u017cytkownika" }, - "title": "Po\u0142\u0105cz z termostatem Venstar" + "title": "Po\u0142\u0105czenie z termostatem Venstar" } } } diff --git a/homeassistant/components/vera/translations/ca.json b/homeassistant/components/vera/translations/ca.json index 13a889cb7db..6aa7cd8292d 100644 --- a/homeassistant/components/vera/translations/ca.json +++ b/homeassistant/components/vera/translations/ca.json @@ -10,8 +10,9 @@ "lights": "Identificadors de dispositiu dels commutadors Vera a tractar com a llums a Home Assistant.", "vera_controller_url": "URL del controlador" }, - "description": "Proporciona un URL pel controlador Vera. Hauria de ser similar al seg\u00fcent: http://192.168.1.161:3480.", - "title": "Configuraci\u00f3 del controlador Vera" + "data_description": { + "vera_controller_url": "Hauria de tenir aquest aspecte: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/cs.json b/homeassistant/components/vera/translations/cs.json index df93575a945..33b7cbf6625 100644 --- a/homeassistant/components/vera/translations/cs.json +++ b/homeassistant/components/vera/translations/cs.json @@ -9,9 +9,7 @@ "exclude": "ID za\u0159\u00edzen\u00ed Vera, kter\u00e1 chcete vylou\u010dit z Home Assistant.", "lights": "ID za\u0159\u00edzen\u00ed Vera, se kter\u00fdmi m\u00e1 Home Assistant zach\u00e1zet jako se sv\u011btly.", "vera_controller_url": "URL adresa ovlada\u010de" - }, - "description": "N\u00ed\u017ee zadejte adresu URL odvlada\u010de Vera. M\u011blo by to vypadat takto: http://192.168.1.161:3480.", - "title": "Nastaven\u00ed ovlada\u010de Vera" + } } } }, diff --git a/homeassistant/components/vera/translations/de.json b/homeassistant/components/vera/translations/de.json index 999dfc8213f..f13e347ffd9 100644 --- a/homeassistant/components/vera/translations/de.json +++ b/homeassistant/components/vera/translations/de.json @@ -10,8 +10,9 @@ "lights": "Vera Switch-Ger\u00e4te-IDs, die im Home Assistant als Lichter behandelt werden sollen.", "vera_controller_url": "Controller-URL" }, - "description": "Stelle unten eine Vera-Controller-URL zur Verf\u00fcgung. Sie sollte wie folgt aussehen: http://192.168.1.161:3480.", - "title": "Richte den Vera-Controller ein" + "data_description": { + "vera_controller_url": "Sie sollte wie folgt aussehen: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/el.json b/homeassistant/components/vera/translations/el.json index 1039fb01ccd..bb667455132 100644 --- a/homeassistant/components/vera/translations/el.json +++ b/homeassistant/components/vera/translations/el.json @@ -10,8 +10,9 @@ "lights": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03ac \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03bc\u03b5\u03c4\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 Vera \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c9\u03c2 \u03c6\u03ce\u03c4\u03b1 \u03c3\u03c4\u03bf Home Assistant.", "vera_controller_url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b5\u03bb\u03b5\u03b3\u03ba\u03c4\u03ae" }, - "description": "\u0394\u03ce\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c4\u03bf\u03c5 \u03b5\u03bb\u03b5\u03b3\u03ba\u03c4\u03ae Vera \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9. \u0398\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03bc\u03bf\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc: http://192.168.1.161:3480.", - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03bb\u03b5\u03b3\u03ba\u03c4\u03ae Vera" + "data_description": { + "vera_controller_url": "\u0398\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03bc\u03bf\u03b9\u03ac\u03b6\u03b5\u03b9 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/en.json b/homeassistant/components/vera/translations/en.json index 94f490d71ee..53c60e39c01 100644 --- a/homeassistant/components/vera/translations/en.json +++ b/homeassistant/components/vera/translations/en.json @@ -10,8 +10,9 @@ "lights": "Vera switch device ids to treat as lights in Home Assistant.", "vera_controller_url": "Controller URL" }, - "description": "Provide a Vera controller URL below. It should look like this: http://192.168.1.161:3480.", - "title": "Setup Vera controller" + "data_description": { + "vera_controller_url": "It should look like this: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/es-419.json b/homeassistant/components/vera/translations/es-419.json index 834c74b7ed3..40b78726144 100644 --- a/homeassistant/components/vera/translations/es-419.json +++ b/homeassistant/components/vera/translations/es-419.json @@ -9,9 +9,7 @@ "exclude": "Identificadores de dispositivo de Vera para excluir de Home Assistant.", "lights": "Vera cambia los identificadores del dispositivo para tratarlos como luces en Home Assistant.", "vera_controller_url": "URL del controlador" - }, - "description": "Proporcione una URL del controlador Vera a continuaci\u00f3n. Deber\u00eda verse as\u00ed: http://192.168.1.161:3480.", - "title": "Configurar el controlador Vera" + } } } }, diff --git a/homeassistant/components/vera/translations/es.json b/homeassistant/components/vera/translations/es.json index d34e106e9eb..0cccacaa88f 100644 --- a/homeassistant/components/vera/translations/es.json +++ b/homeassistant/components/vera/translations/es.json @@ -9,9 +9,7 @@ "exclude": "Identificadores de dispositivos Vera a excluir de Home Assistant", "lights": "Identificadores de interruptores Vera que deben ser tratados como luces en Home Assistant", "vera_controller_url": "URL del controlador" - }, - "description": "Introduce una URL para el controlador Vera a continuaci\u00f3n. Ser\u00eda algo como: http://192.168.1.161:3480.", - "title": "Configurar el controlador Vera" + } } } }, diff --git a/homeassistant/components/vera/translations/et.json b/homeassistant/components/vera/translations/et.json index 4afb098caa6..36d2cae71bf 100644 --- a/homeassistant/components/vera/translations/et.json +++ b/homeassistant/components/vera/translations/et.json @@ -10,8 +10,9 @@ "lights": "Vera l\u00fclitite ID'd mida neid k\u00e4sitleda Home Assistantis tuledena.", "vera_controller_url": "Kontrolleri URL" }, - "description": "Esita allpool Vera kontrolleri URL. See peaks v\u00e4lja n\u00e4gema selline: http://192.168.1.161:3480.", - "title": "Seadista Vera kontroller" + "data_description": { + "vera_controller_url": "See peaks v\u00e4lja n\u00e4gema selline: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/fr.json b/homeassistant/components/vera/translations/fr.json index 133319c552d..f57145a9a24 100644 --- a/homeassistant/components/vera/translations/fr.json +++ b/homeassistant/components/vera/translations/fr.json @@ -10,8 +10,9 @@ "lights": "Identifiants des interrupteurs vera \u00e0 traiter comme des lumi\u00e8res dans Home Assistant", "vera_controller_url": "URL du contr\u00f4leur" }, - "description": "Fournissez une URL de contr\u00f4leur Vera ci-dessous. Cela devrait ressembler \u00e0 ceci : http://192.168.1.161:3480.", - "title": "Configurer le contr\u00f4leur Vera" + "data_description": { + "vera_controller_url": "Cela devrait ressembler \u00e0 ceci\u00a0: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/hu.json b/homeassistant/components/vera/translations/hu.json index d1d4910c97a..30cf7e5e7d1 100644 --- a/homeassistant/components/vera/translations/hu.json +++ b/homeassistant/components/vera/translations/hu.json @@ -10,8 +10,9 @@ "lights": "A Vera kapcsol\u00f3eszk\u00f6z-azonos\u00edt\u00f3k f\u00e9nyk\u00e9nt kezelhet\u0151k a Home Assistant alkalmaz\u00e1sban.", "vera_controller_url": "Vez\u00e9rl\u0151 URL" }, - "description": "Adja meg a Vera vez\u00e9rl\u0151 URL-j\u00e9t al\u00e1bb. Hasonl\u00f3k\u00e9ppen kell kin\u00e9znie: http://192.168.1.161:3480.", - "title": "Vera vez\u00e9rl\u0151 be\u00e1ll\u00edt\u00e1sa" + "data_description": { + "vera_controller_url": "\u00cdgy kell kin\u00e9znie: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/id.json b/homeassistant/components/vera/translations/id.json index 435fc722dba..99422f10be6 100644 --- a/homeassistant/components/vera/translations/id.json +++ b/homeassistant/components/vera/translations/id.json @@ -10,8 +10,9 @@ "lights": "ID perangkat sakelar Vera yang diperlakukan sebagai lampu di Home Assistant", "vera_controller_url": "URL Pengontrol" }, - "description": "Tentukan URL pengontrol Vera di bawah. Ini akan terlihat seperti ini: http://192.168.1.161:3480.", - "title": "Siapkan pengontrol Vera" + "data_description": { + "vera_controller_url": "Seharusnya terlihat seperti ini: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/it.json b/homeassistant/components/vera/translations/it.json index a3832914ac6..292e4048f0a 100644 --- a/homeassistant/components/vera/translations/it.json +++ b/homeassistant/components/vera/translations/it.json @@ -10,8 +10,9 @@ "lights": "Gli ID dei dispositivi switch Vera da trattare come luci in Home Assistant.", "vera_controller_url": "URL del controller" }, - "description": "Fornisci un URL controller Vera di seguito. Dovrebbe assomigliare a questo: http://192.168.1.161:3480.", - "title": "Configurazione controller Vera" + "data_description": { + "vera_controller_url": "Dovrebbe apparire cos\u00ec: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/ja.json b/homeassistant/components/vera/translations/ja.json index 2f800ee7b83..83ca62f4abf 100644 --- a/homeassistant/components/vera/translations/ja.json +++ b/homeassistant/components/vera/translations/ja.json @@ -10,8 +10,9 @@ "lights": "Vera\u306f\u3001Home Assistant\u3067\u30e9\u30a4\u30c8\u3068\u3057\u3066\u6271\u3046\u30c7\u30d0\u30a4\u30b9ID\u3092\u5207\u308a\u66ff\u3048\u307e\u3059\u3002", "vera_controller_url": "\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306eURL" }, - "description": "Vera\u306e\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306eURL\u3092\u4ee5\u4e0b\u306b\u793a\u3057\u307e\u3059: http://192.168.1.161:3480 \u306e\u3088\u3046\u306b\u306a\u3063\u3066\u3044\u308b\u306f\u305a\u3067\u3059\u3002", - "title": "Vera controller\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + "data_description": { + "vera_controller_url": "\u6b21\u306e\u3088\u3046\u306b\u306a\u308a\u307e\u3059: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/ko.json b/homeassistant/components/vera/translations/ko.json index 0990435cb4a..430af1be385 100644 --- a/homeassistant/components/vera/translations/ko.json +++ b/homeassistant/components/vera/translations/ko.json @@ -9,9 +9,7 @@ "exclude": "Home Assistant\uc5d0\uc11c \uc81c\uc678\ud560 Vera \uae30\uae30 ID.", "lights": "Vera \uc2a4\uc704\uce58 \uae30\uae30 ID \ub294 Home Assistant\uc5d0\uc11c \uc870\uba85\uc73c\ub85c \ucde8\uae09\ub429\ub2c8\ub2e4.", "vera_controller_url": "\ucee8\ud2b8\ub864\ub7ec URL" - }, - "description": "\uc544\ub798\uc5d0 Vera \ucee8\ud2b8\ub864\ub7ec URL \uc8fc\uc18c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. http://192.168.1.161:3480 \uacfc \uac19\uc740 \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4.", - "title": "Vera \ucee8\ud2b8\ub864\ub7ec \uc124\uc815\ud558\uae30" + } } } }, diff --git a/homeassistant/components/vera/translations/lb.json b/homeassistant/components/vera/translations/lb.json index 0f3c8acc028..307218b2b23 100644 --- a/homeassistant/components/vera/translations/lb.json +++ b/homeassistant/components/vera/translations/lb.json @@ -9,9 +9,7 @@ "exclude": "IDs vu Vera Apparater d\u00e9i vun Home Assistant ausgeschloss solle ginn.", "lights": "IDs vun Apparater vu Vera Schalter d\u00e9i als Luuchten am Home Assistant trait\u00e9iert ginn.", "vera_controller_url": "Kontroller URL" - }, - "description": "Vera Kontroller URL uginn: D\u00e9i sollt sou ausgesinn:\nhttp://192.168.1.161:3480.", - "title": "Vera Kontroller ariichten" + } } } }, diff --git a/homeassistant/components/vera/translations/nl.json b/homeassistant/components/vera/translations/nl.json index b4aa4a1d7a3..bc81fa3563e 100644 --- a/homeassistant/components/vera/translations/nl.json +++ b/homeassistant/components/vera/translations/nl.json @@ -10,8 +10,9 @@ "lights": "Vera-schakelapparaat id's behandelen als lichten in Home Assistant.", "vera_controller_url": "Controller-URL" }, - "description": "Geef hieronder een URL voor de Vera-controller op. Het zou er zo uit moeten zien: http://192.168.1.161:3480.", - "title": "Stel Vera controller in" + "data_description": { + "vera_controller_url": "Het zou er zo uit moeten zien: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/no.json b/homeassistant/components/vera/translations/no.json index f1454be6799..807b44dc84e 100644 --- a/homeassistant/components/vera/translations/no.json +++ b/homeassistant/components/vera/translations/no.json @@ -10,8 +10,9 @@ "lights": "Vera bytter enhets ID-er for \u00e5 behandle som lys i Home Assistant", "vera_controller_url": "URL-adresse for kontroller" }, - "description": "Gi en Vera-kontroller-URL nedenfor. Det skal se slik ut: http://192.168.1.161:3480.", - "title": "Oppsett Vera-kontroller" + "data_description": { + "vera_controller_url": "Det skal se slik ut: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/pl.json b/homeassistant/components/vera/translations/pl.json index 5b7b6886085..5be37d2a571 100644 --- a/homeassistant/components/vera/translations/pl.json +++ b/homeassistant/components/vera/translations/pl.json @@ -10,8 +10,9 @@ "lights": "Identyfikatory prze\u0142\u0105cznik\u00f3w Vera, kt\u00f3re maj\u0105 by\u0107 traktowane jako \u015bwiat\u0142a w Home Assistant.", "vera_controller_url": "Adres URL kontrolera" }, - "description": "Podaj adres URL kontrolera Vera. Powinien on wygl\u0105da\u0107 tak: http://192.168.1.161:3480.", - "title": "Konfiguracja kontrolera Vera" + "data_description": { + "vera_controller_url": "Powinno to wygl\u0105da\u0107 tak: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/pt-BR.json b/homeassistant/components/vera/translations/pt-BR.json index 361b0487617..b3c39d4cbd7 100644 --- a/homeassistant/components/vera/translations/pt-BR.json +++ b/homeassistant/components/vera/translations/pt-BR.json @@ -10,8 +10,9 @@ "lights": "Vera - alternar os IDs do dispositivo para tratar como luzes no Home Assistant.", "vera_controller_url": "URL do controlador" }, - "description": "Forne\u00e7a um URL do controlador Vera abaixo. Deve ficar assim: http://192.168.1.161:3480.", - "title": "Configurar controlador Vera" + "data_description": { + "vera_controller_url": "Deve ficar assim: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/ru.json b/homeassistant/components/vera/translations/ru.json index 857ffaa5bf5..43dc0cfd168 100644 --- a/homeassistant/components/vera/translations/ru.json +++ b/homeassistant/components/vera/translations/ru.json @@ -10,8 +10,9 @@ "lights": "ID \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u0435\u0439 Vera, \u0434\u043b\u044f \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u0432 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435", "vera_controller_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430" }, - "description": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 URL-\u0430\u0434\u0440\u0435\u0441 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 'address[:port]' (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: 'http://192.168.1.161:3480').", - "title": "Vera" + "data_description": { + "vera_controller_url": "\u0414\u043e\u043b\u0436\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/sl.json b/homeassistant/components/vera/translations/sl.json index 355c6353388..01b2f755ac5 100644 --- a/homeassistant/components/vera/translations/sl.json +++ b/homeassistant/components/vera/translations/sl.json @@ -9,9 +9,7 @@ "exclude": "ID-ji naprav Vera, ki jih \u017eelite izklju\u010diti iz programa Home Assistant.", "lights": "ID-ji stikal Vera, ki naj jih Home Assistant tretira kot lu\u010di.", "vera_controller_url": "URL krmilnika" - }, - "description": "Spodaj navedite URL krmilnika Vera. Izgledati bi moral takole: http://192.168.1.161:3480.", - "title": "Nastavite krmilnik Vera" + } } } }, diff --git a/homeassistant/components/vera/translations/tr.json b/homeassistant/components/vera/translations/tr.json index fa66c1c9806..465aed0cb90 100644 --- a/homeassistant/components/vera/translations/tr.json +++ b/homeassistant/components/vera/translations/tr.json @@ -10,8 +10,9 @@ "lights": "Vera, Home Assistant'ta \u0131\u015f\u0131k gibi davranmak i\u00e7in cihaz kimliklerini de\u011fi\u015ftirir.", "vera_controller_url": "Denetleyici URL'si" }, - "description": "A\u015fa\u011f\u0131da bir Vera denetleyici URL'si sa\u011flay\u0131n. \u015eu \u015fekilde g\u00f6r\u00fcnmelidir: http://192.168.1.161:3480.", - "title": "Vera denetleyicisini kurun" + "data_description": { + "vera_controller_url": "\u015eu \u015fekilde g\u00f6r\u00fcnmelidir: http://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/vera/translations/uk.json b/homeassistant/components/vera/translations/uk.json index 8c591a1cc10..5042d04c26a 100644 --- a/homeassistant/components/vera/translations/uk.json +++ b/homeassistant/components/vera/translations/uk.json @@ -9,9 +9,7 @@ "exclude": "ID \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432 Vera, \u0434\u043b\u044f \u0432\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0437 Home Assistant", "lights": "ID \u0432\u0438\u043c\u0438\u043a\u0430\u0447\u0456\u0432 Vera, \u0434\u043b\u044f \u0456\u043c\u043f\u043e\u0440\u0442\u0443 \u0432 \u043e\u0441\u0432\u0456\u0442\u043b\u0435\u043d\u043d\u044f", "vera_controller_url": "URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440\u0430" - }, - "description": "\u0412\u043a\u0430\u0436\u0456\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440\u0430 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0456 'address[:port]' (\u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434: 'http://192.168.1.161:3480').", - "title": "Vera" + } } } }, diff --git a/homeassistant/components/vera/translations/zh-Hant.json b/homeassistant/components/vera/translations/zh-Hant.json index b8d7031ee12..802ce7c97f1 100644 --- a/homeassistant/components/vera/translations/zh-Hant.json +++ b/homeassistant/components/vera/translations/zh-Hant.json @@ -10,8 +10,9 @@ "lights": "\u65bc Home Assistant \u4e2d\u8996\u70ba\u71c8\u5149\u7684 Vera \u958b\u95dc\u88dd\u7f6e ID\u3002", "vera_controller_url": "\u63a7\u5236\u5668 URL" }, - "description": "\u65bc\u4e0b\u65b9\u63d0\u4f9b Vera \u63a7\u5236\u5668 URL\u3002\u683c\u5f0f\u61c9\u8a72\u70ba\uff1ahttp://192.168.1.161:3480\u3002", - "title": "\u8a2d\u5b9a Vera \u63a7\u5236\u5668" + "data_description": { + "vera_controller_url": "\u770b\u8d77\u4f86\u61c9\u8a72\u985e\u4f3c\uff1ahttp://192.168.1.161:3480" + } } } }, diff --git a/homeassistant/components/verisure/translations/hu.json b/homeassistant/components/verisure/translations/hu.json index 91c0292c3b8..324033b666a 100644 --- a/homeassistant/components/verisure/translations/hu.json +++ b/homeassistant/components/verisure/translations/hu.json @@ -17,14 +17,14 @@ }, "reauth_confirm": { "data": { - "description": "Hiteles\u00edts \u00fajra a Verisure My Pages fi\u00f3koddal.", + "description": "Hiteles\u00edtsen \u00fajra a Verisure My Pages fi\u00f3kj\u00e1val.", "email": "E-mail", "password": "Jelsz\u00f3" } }, "user": { "data": { - "description": "Jelentkezz be a Verisure My Pages fi\u00f3koddal.", + "description": "Jelentkezzen be a Verisure My Pages fi\u00f3kj\u00e1val.", "email": "E-mail", "password": "Jelsz\u00f3" } diff --git a/homeassistant/components/version/translations/he.json b/homeassistant/components/version/translations/he.json index cdb921611c4..c5508040834 100644 --- a/homeassistant/components/version/translations/he.json +++ b/homeassistant/components/version/translations/he.json @@ -2,6 +2,17 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "step": { + "user": { + "title": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05e1\u05d5\u05d2 \u05d4\u05ea\u05e7\u05e0\u05d4" + }, + "version_source": { + "data": { + "beta": "\u05dc\u05db\u05dc\u05d5\u05dc \u05d2\u05e8\u05e1\u05d0\u05d5\u05ea \u05d1\u05d8\u05d0" + }, + "title": "\u05d4\u05d2\u05d3\u05e8\u05d4" + } } } } \ No newline at end of file diff --git a/homeassistant/components/vicare/translations/fr.json b/homeassistant/components/vicare/translations/fr.json index 260dbb25006..b3dce5ec826 100644 --- a/homeassistant/components/vicare/translations/fr.json +++ b/homeassistant/components/vicare/translations/fr.json @@ -18,7 +18,7 @@ "scan_interval": "Intervalle de balayage (secondes)", "username": "Courriel" }, - "description": "Configurer l'int\u00e9gration ViCare. Pour g\u00e9n\u00e9rer une cl\u00e9 API se rendre sur https://developer.viessmann.com", + "description": "Configurer l'int\u00e9gration ViCare. Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.viessmann.com", "title": "{name}" } } diff --git a/homeassistant/components/vicare/translations/hu.json b/homeassistant/components/vicare/translations/hu.json index 4dca080307d..0e69a395a90 100644 --- a/homeassistant/components/vicare/translations/hu.json +++ b/homeassistant/components/vicare/translations/hu.json @@ -13,7 +13,7 @@ "data": { "client_id": "API kulcs", "heating_type": "F\u0171t\u00e9s t\u00edpusa", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "scan_interval": "Beolvas\u00e1si id\u0151k\u00f6z (m\u00e1sodperc)", "username": "E-mail" diff --git a/homeassistant/components/vizio/translations/hu.json b/homeassistant/components/vizio/translations/hu.json index bb619e359c0..908dfad8f76 100644 --- a/homeassistant/components/vizio/translations/hu.json +++ b/homeassistant/components/vizio/translations/hu.json @@ -31,7 +31,7 @@ "access_token": "Hozz\u00e1f\u00e9r\u00e9si token", "device_class": "Eszk\u00f6zt\u00edpus", "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "A Hozz\u00e1f\u00e9r\u00e9si token csak t\u00e9v\u00e9khez sz\u00fcks\u00e9ges. Ha TV -t konfigur\u00e1l, \u00e9s m\u00e9g nincs Hozz\u00e1f\u00e9r\u00e9si token , hagyja \u00fcresen a p\u00e1ros\u00edt\u00e1si folyamathoz.", "title": "VIZIO SmartCast Eszk\u00f6z" diff --git a/homeassistant/components/vlc_telnet/translations/hu.json b/homeassistant/components/vlc_telnet/translations/hu.json index 0a76fc089ed..bac42b4d4c3 100644 --- a/homeassistant/components/vlc_telnet/translations/hu.json +++ b/homeassistant/components/vlc_telnet/translations/hu.json @@ -26,7 +26,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "port": "Port" } diff --git a/homeassistant/components/vulcan/translations/bg.json b/homeassistant/components/vulcan/translations/bg.json new file mode 100644 index 00000000000..27893871c14 --- /dev/null +++ b/homeassistant/components/vulcan/translations/bg.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435\u0442\u043e - \u043c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0432\u0430\u0448\u0430\u0442\u0430 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0432\u0440\u044a\u0437\u043a\u0430" + }, + "step": { + "select_saved_credentials": { + "data": { + "credentials": "\u0412\u0445\u043e\u0434" + } + } + } + }, + "options": { + "error": { + "error": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "init": { + "data": { + "message_notify": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043d\u043e\u0432\u043e \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u0435", + "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 (\u0432 \u043c\u0438\u043d\u0443\u0442\u0438)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ca.json b/homeassistant/components/vulcan/translations/ca.json new file mode 100644 index 00000000000..505bcb1d326 --- /dev/null +++ b/homeassistant/components/vulcan/translations/ca.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Ja s'han afegit tots els alumnes.", + "already_configured": "Ja s'ha afegit aquest alumne.", + "reauth_successful": "Re-autenticaci\u00f3 exitosa" + }, + "error": { + "cannot_connect": "Error de connexi\u00f3, comprova la teva connexi\u00f3 a Internet", + "expired_credentials": "Credencials caducades, crea'n de noves a la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil de Vulcan", + "expired_token": "Token caducat, genera un nou token", + "invalid_pin": "PIN inv\u00e0lid", + "invalid_symbol": "S\u00edmbol inv\u00e0lid", + "invalid_token": "Token inv\u00e0lid", + "unknown": "S'ha produ\u00eft un error desconegut" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Utilitza les credencials desades" + }, + "description": "Afegeix un altre alumne." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "S\u00edmbol", + "token": "Token" + }, + "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "S\u00edmbol", + "token": "Token" + }, + "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." + }, + "select_saved_credentials": { + "data": { + "credentials": "Inici de sessi\u00f3" + }, + "description": "Selecciona credencials desades." + }, + "select_student": { + "data": { + "student_name": "Selecciona l'alumne" + }, + "description": "Selecciona l'alumne, pots afegir m\u00e9s alumnes tornant a afegir la integraci\u00f3 de nou." + } + } + }, + "options": { + "error": { + "error": "S'ha produ\u00eft un error" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Mostra les notificacions sobre les darreres assist\u00e8ncies", + "grade_notify": "Mostra notificacions de les \u00faltimes qualificacions", + "message_notify": "Mostra notificacions quan es rebi un missatge nou", + "scan_interval": "Interval d'actualitzaci\u00f3 (minuts)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/de.json b/homeassistant/components/vulcan/translations/de.json new file mode 100644 index 00000000000..23d5032f5fa --- /dev/null +++ b/homeassistant/components/vulcan/translations/de.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Alle Sch\u00fcler wurden bereits hinzugef\u00fcgt.", + "already_configured": "Dieser Sch\u00fcler wurde bereits hinzugef\u00fcgt.", + "reauth_successful": "Reauth erfolgreich" + }, + "error": { + "cannot_connect": "Verbindungsfehler - Bitte \u00fcberpr\u00fcfe deine Internetverbindung", + "expired_credentials": "Abgelaufene Anmeldedaten - bitte erstelle neue auf der Registrierungsseite der Vulcan Mobile App", + "expired_token": "Abgelaufener Token - bitte generiere einen neuen Token", + "invalid_pin": "Ung\u00fcltige PIN", + "invalid_symbol": "Ung\u00fcltiges Symbol", + "invalid_token": "Ung\u00fcltiges Token", + "unknown": "Unbekannter Fehler ist aufgetreten" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Gespeicherte Anmeldeinformationen verwenden" + }, + "description": "F\u00fcge einen weiteren Sch\u00fcler hinzu." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "Symbol", + "token": "Token" + }, + "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "Symbol", + "token": "Token" + }, + "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." + }, + "select_saved_credentials": { + "data": { + "credentials": "Anmelden" + }, + "description": "W\u00e4hle gespeicherte Anmeldeinformationen aus." + }, + "select_student": { + "data": { + "student_name": "Sch\u00fcler ausw\u00e4hlen" + }, + "description": "W\u00e4hle Sch\u00fcler aus, Du kannst weitere Sch\u00fcler hinzuf\u00fcgen, indem du die Integration erneut hinzuf\u00fcgst." + } + } + }, + "options": { + "error": { + "error": "Ein Fehler ist aufgetreten" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Benachrichtigungen \u00fcber die neuesten Anwesenheitseintr\u00e4ge anzeigen", + "grade_notify": "Benachrichtigungen \u00fcber die neuesten Noten anzeigen", + "message_notify": "Benachrichtigungen anzeigen, wenn eine neue Nachricht eingegangen ist", + "scan_interval": "Aktualisierungsintervall (in Minuten)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/el.json b/homeassistant/components/vulcan/translations/el.json new file mode 100644 index 00000000000..bb77c88f8e5 --- /dev/null +++ b/homeassistant/components/vulcan/translations/el.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "\u038c\u03bb\u03bf\u03b9 \u03bf\u03b9 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ad\u03c2 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ae\u03b4\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b5\u03b8\u03b5\u03af.", + "already_configured": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b5\u03b8\u03b5\u03af.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "error": { + "cannot_connect": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 - \u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf \u0394\u03b9\u03b1\u03b4\u03af\u03ba\u03c4\u03c5\u03bf", + "expired_credentials": "\u039b\u03b7\u03b3\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 - \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03bd\u03ad\u03b1 \u03c3\u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac \u03c4\u03b7\u03c2 Vulcan", + "expired_token": "\u039b\u03b7\u03b3\u03bc\u03ad\u03bd\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc - \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03bd\u03ad\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc", + "invalid_pin": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf PIN", + "invalid_symbol": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c3\u03cd\u03bc\u03b2\u03bf\u03bb\u03bf", + "invalid_token": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc", + "unknown": "\u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ac\u03b3\u03bd\u03c9\u03c3\u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03c9\u03bd \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03b7\u03c1\u03af\u03c9\u03bd" + }, + "description": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03ac\u03bb\u03bb\u03bf\u03c5 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae." + }, + "auth": { + "data": { + "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", + "region": "\u03a3\u03cd\u03bc\u03b2\u03bf\u03bb\u03bf", + "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc" + }, + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." + }, + "reauth": { + "data": { + "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", + "region": "\u03a3\u03cd\u03bc\u03b2\u03bf\u03bb\u03bf", + "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc" + }, + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." + }, + "select_saved_credentials": { + "data": { + "credentials": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7" + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03c5\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1." + }, + "select_student": { + "data": { + "student_name": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae" + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae, \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03bf\u03c5\u03c2 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ad\u03c2 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c4\u03bf\u03bd\u03c4\u03b1\u03c2 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7." + } + } + }, + "options": { + "error": { + "error": "\u03a0\u03c1\u03bf\u03ad\u03ba\u03c5\u03c8\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "init": { + "data": { + "attendance_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b9\u03bf \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03b5\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", + "grade_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03c0\u03b9\u03bf \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03bf\u03c5\u03c2 \u03b2\u03b1\u03b8\u03bc\u03bf\u03cd\u03c2", + "message_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03ad\u03bf \u03bc\u03ae\u03bd\u03c5\u03bc\u03b1", + "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03c3\u03b5 \u03bb\u03b5\u03c0\u03c4\u03ac)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/en.json b/homeassistant/components/vulcan/translations/en.json index abb3dce7c7f..48c054d2c15 100644 --- a/homeassistant/components/vulcan/translations/en.json +++ b/homeassistant/components/vulcan/translations/en.json @@ -1,69 +1,69 @@ { - "config": { - "abort": { - "already_configured": "That student has already been added.", - "all_student_already_configured": "All students have already been added.", - "reauth_successful": "Reauth successful" + "config": { + "abort": { + "all_student_already_configured": "All students have already been added.", + "already_configured": "That student has already been added.", + "reauth_successful": "Reauth successful" + }, + "error": { + "cannot_connect": "Connection error - please check your internet connection", + "expired_credentials": "Expired credentials - please create new on Vulcan mobile app registration page", + "expired_token": "Expired token - please generate a new token", + "invalid_pin": "Invalid pin", + "invalid_symbol": "Invalid symbol", + "invalid_token": "Invalid token", + "unknown": "Unknown error occurred" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Use saved credentials" + }, + "description": "Add another student." + }, + "auth": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Login to your Vulcan Account using mobile app registration page." + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Login to your Vulcan Account using mobile app registration page." + }, + "select_saved_credentials": { + "data": { + "credentials": "Login" + }, + "description": "Select saved credentials." + }, + "select_student": { + "data": { + "student_name": "Select student" + }, + "description": "Select student, you can add more students by adding integration again." + } + } }, - "error": { - "unknown": "Unknown error occurred", - "invalid_token": "Invalid token", - "expired_token": "Expired token - please generate a new token", - "invalid_pin": "Invalid pin", - "invalid_symbol": "Invalid symbol", - "expired_credentials": "Expired credentials - please create new on Vulcan mobile app registration page", - "cannot_connect": "Connection error - please check your internet connection" - }, - "step": { - "auth": { - "description": "Login to your Vulcan Account using mobile app registration page.", - "data": { - "token": "Token", - "region": "Symbol", - "pin": "Pin" + "options": { + "error": { + "error": "Error occurred" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Show notifications about the latest attendance entries", + "grade_notify": "Show notifications about the latest grades", + "message_notify": "Show notifications when new message received", + "scan_interval": "Update interval (in minutes)" + } + } } - }, - "reauth": { - "description": "Login to your Vulcan Account using mobile app registration page.", - "data": { - "token": "Token", - "region": "Symbol", - "pin": "Pin" - } - }, - "select_student": { - "description": "Select student, you can add more students by adding integration again.", - "data": { - "student_name": "Select student" - } - }, - "select_saved_credentials": { - "description": "Select saved credentials.", - "data": { - "credentials": "Login" - } - }, - "add_next_config_entry": { - "description": "Add another student.", - "data": { - "use_saved_credentials": "Use saved credentials" - } - } } - }, - "options": { - "error": { - "error": "Error occurred" - }, - "step": { - "init": { - "data": { - "message_notify": "Show notifications when new message received", - "attendance_notify": "Show notifications about the latest attendance entries", - "grade_notify": "Show notifications about the latest grades", - "scan_interval": "Update interval (in minutes)" - } - } - } - } -} +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/et.json b/homeassistant/components/vulcan/translations/et.json new file mode 100644 index 00000000000..ab3156cc676 --- /dev/null +++ b/homeassistant/components/vulcan/translations/et.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "K\u00f5ik \u00f5pilased on juba lisatud.", + "already_configured": "See \u00f5pilane on juba lisatud.", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendusviga - kontrolli oma interneti\u00fchendust", + "expired_credentials": "Aegunud mandaat \u2013 loo Vulcani mobiilirakenduse registreerimislehel uued", + "expired_token": "Token on aegunud - loo uus token.", + "invalid_pin": "Vigane PIN kood", + "invalid_symbol": "Vigane s\u00fcmbol", + "invalid_token": "Vigane token", + "unknown": "Ilmnes ootamatu t\u00f5rge" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Salvestatud identimisteabe kasutamine" + }, + "description": "Lisa veel \u00fcks \u00f5pilane." + }, + "auth": { + "data": { + "pin": "PIN kood", + "region": "S\u00fcmbol", + "token": "Token" + }, + "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." + }, + "reauth": { + "data": { + "pin": "PIN kood", + "region": "S\u00fcmbol", + "token": "Token" + }, + "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." + }, + "select_saved_credentials": { + "data": { + "credentials": "Sisselogimine" + }, + "description": "Vali salvestatud identimisteave." + }, + "select_student": { + "data": { + "student_name": "Vali \u00f5pilane" + }, + "description": "Vali \u00f5pilane, saad lisada rohkem \u00f5pilasi lisades sidumise veelkord." + } + } + }, + "options": { + "error": { + "error": "Ilmnes t\u00f5rge" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Kuva m\u00e4rguanded viimaste osalemiskirjete kohta", + "grade_notify": "Kuva m\u00e4rguanded viimaste hinnete kohta", + "message_notify": "Kuva teated uue s\u00f5numi saabumisel", + "scan_interval": "V\u00e4rskendamise intervall (minutites)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/fr.json b/homeassistant/components/vulcan/translations/fr.json new file mode 100644 index 00000000000..812c069223e --- /dev/null +++ b/homeassistant/components/vulcan/translations/fr.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Tous les \u00e9tudiants ont d\u00e9j\u00e0 \u00e9t\u00e9 ajout\u00e9s.", + "already_configured": "Cet \u00e9tudiant a d\u00e9j\u00e0 \u00e9t\u00e9 ajout\u00e9.", + "reauth_successful": "R\u00e9-authentification r\u00e9ussie" + }, + "error": { + "cannot_connect": "Erreur de connexion\u00a0; veuillez v\u00e9rifier votre connexion Internet", + "expired_credentials": "Identifiants expir\u00e9s\u00a0; veuillez en cr\u00e9er des nouveaux sur la page d'inscription de l'application mobile Vulcan", + "expired_token": "Jeton expir\u00e9\u00a0; veuillez g\u00e9n\u00e9rer un nouveau jeton", + "invalid_pin": "PIN non valide", + "invalid_symbol": "Symbole non valide", + "invalid_token": "Jeton non valide", + "unknown": "Une erreur inconnue est survenue" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Utiliser les informations d'identification enregistr\u00e9es" + }, + "description": "Ajoutez un autre \u00e9tudiant." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "Symbole", + "token": "Jeton" + }, + "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "Symbole", + "token": "Jeton" + }, + "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." + }, + "select_saved_credentials": { + "data": { + "credentials": "Connexion" + }, + "description": "S\u00e9lectionnez les informations d'identification enregistr\u00e9es." + }, + "select_student": { + "data": { + "student_name": "S\u00e9lectionner un \u00e9tudiant" + }, + "description": "S\u00e9lectionnez l'\u00e9tudiant\u00a0; vous pouvez ajouter d'autres \u00e9tudiants en ajoutant \u00e0 nouveau l'int\u00e9gration." + } + } + }, + "options": { + "error": { + "error": "Une erreur est survenue" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Afficher les notifications au sujet des derni\u00e8res entr\u00e9es de pr\u00e9sence", + "grade_notify": "Afficher les notifications au sujet des derni\u00e8res notes", + "message_notify": "Afficher les notifications lors de la r\u00e9ception d'un nouveau message", + "scan_interval": "Intervalle de mise \u00e0 jour (en minutes)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/hu.json b/homeassistant/components/vulcan/translations/hu.json new file mode 100644 index 00000000000..9e138254ebe --- /dev/null +++ b/homeassistant/components/vulcan/translations/hu.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "M\u00e1r minden tanul\u00f3 hozz\u00e1adva.", + "already_configured": "Ezt a tanul\u00f3t m\u00e1r hozz\u00e1adt\u00e1k.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres" + }, + "error": { + "cannot_connect": "Csatlakoz\u00e1si hiba \u2013 ellen\u0151rizze az internetkapcsolatot", + "expired_credentials": "Lej\u00e1rt hiteles\u00edt\u0151 adatok - k\u00e9rj\u00fck, hozzon l\u00e9tre \u00fajakat a Vulcan mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n.", + "expired_token": "Lej\u00e1rt token \u2013 k\u00e9rj\u00fck, hozzon l\u00e9tre egy \u00fajat", + "invalid_pin": "\u00c9rv\u00e9nytelen PIN-k\u00f3d", + "invalid_symbol": "\u00c9rv\u00e9nytelen szimb\u00f3lum", + "invalid_token": "\u00c9rv\u00e9nytelen token", + "unknown": "Ismeretlen hiba t\u00f6rt\u00e9nt" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Mentett hiteles\u00edt\u0151 adatok haszn\u00e1lata" + }, + "description": "Adjon hozz\u00e1 egy m\u00e1sik tanul\u00f3t." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "Szimb\u00f3lum", + "token": "Token" + }, + "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "Szimb\u00f3lum", + "token": "Token" + }, + "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." + }, + "select_saved_credentials": { + "data": { + "credentials": "Bejelentkez\u00e9s" + }, + "description": "V\u00e1lassza ki a mentett hiteles\u00edt\u0151 adatokat." + }, + "select_student": { + "data": { + "student_name": "Tanul\u00f3 kiv\u00e1laszt\u00e1sa" + }, + "description": "V\u00e1lassza ki a tanul\u00f3t, tov\u00e1bbi tanul\u00f3kat vehet fel az integr\u00e1ci\u00f3 \u00fajb\u00f3li hozz\u00e1ad\u00e1s\u00e1val." + } + } + }, + "options": { + "error": { + "error": "Hiba t\u00f6rt\u00e9nt" + }, + "step": { + "init": { + "data": { + "attendance_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se a legut\u00f3bbi r\u00e9szv\u00e9teli bejegyz\u00e9sekr\u0151l", + "grade_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se a leg\u00fajabb oszt\u00e1lyzatokr\u00f3l", + "message_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se \u00faj \u00fczenet \u00e9rkez\u00e9sekor", + "scan_interval": "Friss\u00edt\u00e9si id\u0151k\u00f6z (percben)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/id.json b/homeassistant/components/vulcan/translations/id.json new file mode 100644 index 00000000000..a15ed1772d2 --- /dev/null +++ b/homeassistant/components/vulcan/translations/id.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Semua siswa telah ditambahkan.", + "already_configured": "Siswa tersebut telah ditambahkan.", + "reauth_successful": "Otorisasi ulang berhasil" + }, + "error": { + "cannot_connect": "Kesalahan koneksi - periksa koneksi internet Anda", + "expired_credentials": "Kredensial kedaluwarsa - buat baru di halaman pendaftaran aplikasi seluler Vulcan", + "expired_token": "Token kedaluwarsa - buat token baru", + "invalid_pin": "PIN tidak valid", + "invalid_symbol": "Simbol tidak valid", + "invalid_token": "Token tidak valid", + "unknown": "Kesalahan tidak dikenal terjadi." + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Menggunakan kredensial yang disimpan" + }, + "description": "Tambahkan siswa lain." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "Simbol", + "token": "Token" + }, + "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "Simbol", + "token": "Token" + }, + "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." + }, + "select_saved_credentials": { + "data": { + "credentials": "Masuk" + }, + "description": "Pilih kredensial yang disimpan." + }, + "select_student": { + "data": { + "student_name": "Pilih siswa" + }, + "description": "Pilih siswa, Anda dapat menambahkan lebih banyak siswa dengan menambahkan integrasi lagi." + } + } + }, + "options": { + "error": { + "error": "Terjadi kesalahan" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Tampilkan notifikasi tentang entri kehadiran terbaru", + "grade_notify": "Tampilkan notifikasi tentang nilai terbaru", + "message_notify": "Tampilkan saat pesan baru diterima", + "scan_interval": "Interval pembaruan (dalam menit)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/it.json b/homeassistant/components/vulcan/translations/it.json new file mode 100644 index 00000000000..72bb696a909 --- /dev/null +++ b/homeassistant/components/vulcan/translations/it.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Tutti gli studenti sono gi\u00e0 stati aggiunti.", + "already_configured": "Quello studente \u00e8 gi\u00e0 stato aggiunto.", + "reauth_successful": "Nuova autenticazione avvenuta" + }, + "error": { + "cannot_connect": "Errore di connessione: controlla la tua connessione Internet", + "expired_credentials": "Credenziali scadute: creane una nuova nella pagina di registrazione dell'applicazione mobile Vulcan", + "expired_token": "Token scaduto: genera un nuovo token", + "invalid_pin": "Pin non valido", + "invalid_symbol": "Simbolo non valido", + "invalid_token": "Token non valido", + "unknown": "Si \u00e8 verificato un errore sconosciuto" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Usa le credenziali salvate" + }, + "description": "Aggiungi un altro studente." + }, + "auth": { + "data": { + "pin": "PIN", + "region": "Simbolo", + "token": "Token" + }, + "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "Simbolo", + "token": "Token" + }, + "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." + }, + "select_saved_credentials": { + "data": { + "credentials": "Accesso" + }, + "description": "Seleziona le credenziali salvate." + }, + "select_student": { + "data": { + "student_name": "Seleziona studente" + }, + "description": "Seleziona studente, puoi aggiungere pi\u00f9 studenti aggiungendo nuovamente l'integrazione." + } + } + }, + "options": { + "error": { + "error": "Si \u00e8 verificato un errore" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Mostra le notifiche sulle ultime voci di partecipazione", + "grade_notify": "Mostra le notifiche sugli ultimi voti", + "message_notify": "Mostra le notifiche quando viene ricevuto un nuovo messaggio", + "scan_interval": "Intervallo di aggiornamento (in minuti)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ja.json b/homeassistant/components/vulcan/translations/ja.json new file mode 100644 index 00000000000..98363f12f13 --- /dev/null +++ b/homeassistant/components/vulcan/translations/ja.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "\u3059\u3079\u3066\u306e\u751f\u5f92\u306f\u3059\u3067\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002", + "already_configured": "\u305d\u306e\u5b66\u751f\u306f\u3059\u3067\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u30a8\u30e9\u30fc - \u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u63a5\u7d9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044", + "expired_credentials": "\u6709\u52b9\u671f\u9650\u304c\u5207\u308c\u305f\u8a8d\u8a3c\u60c5\u5831 - Vulcan\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u306e\u767b\u9332\u30da\u30fc\u30b8\u3067\u65b0\u898f\u4f5c\u6210\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "expired_token": "\u6709\u52b9\u671f\u9650\u5207\u308c\u306e\u30c8\u30fc\u30af\u30f3 - \u65b0\u3057\u3044\u30c8\u30fc\u30af\u30f3\u3092\u751f\u6210\u3057\u3066\u304f\u3060\u3055\u3044", + "invalid_pin": "\u7121\u52b9\u306a\u30d4\u30f3", + "invalid_symbol": "\u7121\u52b9\u306a\u30b7\u30f3\u30dc\u30eb", + "invalid_token": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3", + "unknown": "\u4e0d\u660e\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "\u4fdd\u5b58\u3055\u308c\u305f\u8cc7\u683c\u60c5\u5831\u3092\u4f7f\u7528\u3059\u308b" + }, + "description": "\u5225\u306e\u751f\u5f92\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002" + }, + "auth": { + "data": { + "pin": "\u30d4\u30f3", + "region": "\u30b7\u30f3\u30dc\u30eb", + "token": "\u30c8\u30fc\u30af\u30f3" + }, + "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" + }, + "reauth": { + "data": { + "pin": "\u30d4\u30f3", + "region": "\u30b7\u30f3\u30dc\u30eb", + "token": "\u30c8\u30fc\u30af\u30f3" + }, + "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" + }, + "select_saved_credentials": { + "data": { + "credentials": "\u30ed\u30b0\u30a4\u30f3" + }, + "description": "\u4fdd\u5b58\u3055\u308c\u305f\u8cc7\u683c\u60c5\u5831\u3092\u9078\u629e\u3057\u307e\u3059\u3002" + }, + "select_student": { + "data": { + "student_name": "\u751f\u5f92\u3092\u9078\u629e(Select student)" + }, + "description": "\u751f\u5f92\u3092\u9078\u629e\u3057\u307e\u3059(Select student)\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u591a\u304f\u306e\u751f\u5f92\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002" + } + } + }, + "options": { + "error": { + "error": "\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f" + }, + "step": { + "init": { + "data": { + "attendance_notify": "\u6700\u65b0\u306e\u51fa\u5e2d\u30a8\u30f3\u30c8\u30ea\u306b\u95a2\u3059\u308b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", + "grade_notify": "\u6700\u65b0\u306e\u6210\u7e3e\u306b\u95a2\u3059\u308b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", + "message_notify": "\u65b0\u3057\u3044\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u53d7\u4fe1\u6642\u306b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", + "scan_interval": "\u66f4\u65b0\u9593\u9694(\u5206\u5358\u4f4d)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/nl.json b/homeassistant/components/vulcan/translations/nl.json new file mode 100644 index 00000000000..b05b32937c0 --- /dev/null +++ b/homeassistant/components/vulcan/translations/nl.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Alle studenten zijn al toegevoegd.", + "already_configured": "Die student is al toegevoegd.", + "reauth_successful": "Opnieuw verifi\u00ebren gelukt" + }, + "error": { + "cannot_connect": "Verbindingsfout - controleer uw internetverbinding", + "expired_credentials": "Verlopen inloggegevens - maak een nieuwe aan op de Vulcan mobiele app registratiepagina", + "expired_token": "Verlopen token - genereer een nieuw token", + "invalid_pin": "Ongeldige pin", + "invalid_symbol": "Ongeldig symbool", + "invalid_token": "Ongeldige Token", + "unknown": "Onbekende fout opgetreden" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Gebruik opgeslagen referenties" + }, + "description": "Voeg nog een student toe." + }, + "auth": { + "data": { + "pin": "Pin", + "region": "Symbool", + "token": "Token" + }, + "description": "Log in op uw Vulcan-account via de registratiepagina voor mobiele apps." + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "Symbool", + "token": "Token" + }, + "description": "Log in op uw Vulcan-account via de registratiepagina voor mobiele apps." + }, + "select_saved_credentials": { + "data": { + "credentials": "Inloggen" + }, + "description": "Selecteer opgeslagen referenties." + }, + "select_student": { + "data": { + "student_name": "Selecteer student" + }, + "description": "Selecteer student, u kunt meer studenten toevoegen door integratie opnieuw toe te voegen." + } + } + }, + "options": { + "error": { + "error": "Er is een fout opgetreden." + }, + "step": { + "init": { + "data": { + "attendance_notify": "Toon meldingen over de laatste aanwezigheidsgegevens", + "grade_notify": "Toon meldingen over de laatste cijfers", + "message_notify": "Meldingen weergeven wanneer een nieuw bericht is ontvangen", + "scan_interval": "Update-interval (in minuten)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/no.json b/homeassistant/components/vulcan/translations/no.json new file mode 100644 index 00000000000..7cb8b68fa63 --- /dev/null +++ b/homeassistant/components/vulcan/translations/no.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Alle elever er allerede lagt til.", + "already_configured": "Den studenten er allerede lagt til.", + "reauth_successful": "Reauth vellykket" + }, + "error": { + "cannot_connect": "Tilkoblingsfeil - sjekk internettforbindelsen din", + "expired_credentials": "Utl\u00f8pt p\u00e5loggingsinformasjon - vennligst opprett ny p\u00e5 registreringssiden for Vulcan-mobilappen", + "expired_token": "Utl\u00f8pt token - generer et nytt token", + "invalid_pin": "Ugyldig pinkode", + "invalid_symbol": "Ugyldig symbol", + "invalid_token": "Ugyldig token", + "unknown": "Ukjent feil oppstod" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Bruk lagret legitimasjon" + }, + "description": "Legg til en annen student." + }, + "auth": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." + }, + "select_saved_credentials": { + "data": { + "credentials": "P\u00e5logging" + }, + "description": "Velg lagret legitimasjon." + }, + "select_student": { + "data": { + "student_name": "Velg elev" + }, + "description": "Velg student, du kan legge til flere studenter ved \u00e5 legge til integrering p\u00e5 nytt." + } + } + }, + "options": { + "error": { + "error": "Det oppstod en feil" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Vis varsler om de siste fremm\u00f8teoppf\u00f8ringene", + "grade_notify": "Vis varsler om de nyeste vurderingene", + "message_notify": "Vis varsler n\u00e5r ny melding mottas", + "scan_interval": "Oppdateringsintervall (i minutter)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/pl.json b/homeassistant/components/vulcan/translations/pl.json new file mode 100644 index 00000000000..acbc51c6754 --- /dev/null +++ b/homeassistant/components/vulcan/translations/pl.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Wszyscy uczniowie zostali ju\u017c dodani.", + "already_configured": "Ten ucze\u0144 zosta\u0142 ju\u017c dodany.", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "expired_credentials": "Dane uwierzytelniaj\u0105ce wygas\u0142y \u2014 utw\u00f3rz nowe na stronie rejestracji aplikacji mobilnej Vulcan", + "expired_token": "Token wygas\u0142, wygeneruj nowy", + "invalid_pin": "Nieprawid\u0142owy kod PIN", + "invalid_symbol": "Nieprawid\u0142owy symbol", + "invalid_token": "Nieprawid\u0142owy token", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "U\u017cyj zapisanych danych uwierzytelniaj\u0105cych" + }, + "description": "Dodaj kolejnego ucznia." + }, + "auth": { + "data": { + "pin": "Kod pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." + }, + "reauth": { + "data": { + "pin": "Kod pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." + }, + "select_saved_credentials": { + "data": { + "credentials": "Login" + }, + "description": "Wybierz zapisane dane uwierzytelniaj\u0105ce." + }, + "select_student": { + "data": { + "student_name": "Wybierz ucznia" + }, + "description": "Wybierz ucznia. Mo\u017cesz doda\u0107 wi\u0119cej uczni\u00f3w, ponownie dodaj\u0105c integracj\u0119." + } + } + }, + "options": { + "error": { + "error": "Wyst\u0105pi\u0142 b\u0142\u0105d" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Poka\u017c powiadomienia o najnowszych wpisach o obecno\u015bci", + "grade_notify": "Poka\u017c powiadomienia o najnowszych ocenach", + "message_notify": "Pokazuj powiadomienia po otrzymaniu nowej wiadomo\u015bci", + "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (w minutach)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/pt-BR.json b/homeassistant/components/vulcan/translations/pt-BR.json new file mode 100644 index 00000000000..ecef34ff96b --- /dev/null +++ b/homeassistant/components/vulcan/translations/pt-BR.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "Todos os alunos j\u00e1 foram adicionados.", + "already_configured": "Esse aluno j\u00e1 foi adicionado.", + "reauth_successful": "Autentica\u00e7\u00e3o bem-sucedida" + }, + "error": { + "cannot_connect": "Erro de conex\u00e3o - verifique sua conex\u00e3o com a Internet", + "expired_credentials": "Credenciais expiradas - crie uma nova na p\u00e1gina de registro do aplicativo m\u00f3vel Vulcan", + "expired_token": "Token expirado - gere um novo token", + "invalid_pin": "Pin inv\u00e1lido", + "invalid_symbol": "S\u00edmbolo inv\u00e1lido", + "invalid_token": "Token inv\u00e1lido", + "unknown": "Ocorreu um erro desconhecido" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Usar credenciais salvas" + }, + "description": "Adicione outro aluno." + }, + "auth": { + "data": { + "pin": "Pin", + "region": "S\u00edmbolo", + "token": "Token" + }, + "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo m\u00f3vel." + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "S\u00edmbolo", + "token": "Token" + }, + "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo m\u00f3vel." + }, + "select_saved_credentials": { + "data": { + "credentials": "Login" + }, + "description": "Selecione as credenciais salvas." + }, + "select_student": { + "data": { + "student_name": "Selecionar aluno" + }, + "description": "Selecione aluno, voc\u00ea pode adicionar mais alunos adicionando integra\u00e7\u00e3o novamente." + } + } + }, + "options": { + "error": { + "error": "Ocorreu um erro" + }, + "step": { + "init": { + "data": { + "attendance_notify": "Mostrar notifica\u00e7\u00f5es sobre as entradas de participa\u00e7\u00e3o mais recentes", + "grade_notify": "Mostrar notifica\u00e7\u00f5es sobre as notas mais recentes", + "message_notify": "Mostrar notifica\u00e7\u00f5es quando uma nova mensagem for recebida", + "scan_interval": "Intervalo de atualiza\u00e7\u00e3o (em minutos)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ru.json b/homeassistant/components/vulcan/translations/ru.json new file mode 100644 index 00000000000..88e64d9ca79 --- /dev/null +++ b/homeassistant/components/vulcan/translations/ru.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "\u0412\u0441\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u044b \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b.", + "already_configured": "\u042d\u0442\u043e\u0442 \u0441\u0442\u0443\u0434\u0435\u043d\u0442 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0443.", + "expired_credentials": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0438\u0441\u0442\u0435\u043a\u0448\u0438\u043c \u0441\u0440\u043e\u043a\u043e\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Vulcan.", + "expired_token": "\u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0441\u0442\u0435\u043a. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d.", + "invalid_pin": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 PIN-\u043a\u043e\u0434.", + "invalid_symbol": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u0438\u043c\u0432\u043e\u043b.", + "invalid_token": "\u041d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d.", + "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435" + }, + "description": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430." + }, + "auth": { + "data": { + "pin": "PIN-\u043a\u043e\u0434", + "region": "\u0421\u0438\u043c\u0432\u043e\u043b", + "token": "\u0422\u043e\u043a\u0435\u043d" + }, + "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." + }, + "reauth": { + "data": { + "pin": "PIN-\u043a\u043e\u0434", + "region": "\u0421\u0438\u043c\u0432\u043e\u043b", + "token": "\u0422\u043e\u043a\u0435\u043d" + }, + "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." + }, + "select_saved_credentials": { + "data": { + "credentials": "\u041b\u043e\u0433\u0438\u043d" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435." + }, + "select_student": { + "data": { + "student_name": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430" + }, + "description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u043e\u0432, \u0441\u043d\u043e\u0432\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e." + } + } + }, + "options": { + "error": { + "error": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "init": { + "data": { + "attendance_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u044f\u0445 \u043e \u043f\u043e\u0441\u0435\u0449\u0430\u0435\u043c\u043e\u0441\u0442\u0438", + "grade_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u043e\u0446\u0435\u043d\u043a\u0430\u0445", + "message_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f", + "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/tr.json b/homeassistant/components/vulcan/translations/tr.json new file mode 100644 index 00000000000..9f4324dfbcc --- /dev/null +++ b/homeassistant/components/vulcan/translations/tr.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "T\u00fcm \u00f6\u011frenciler zaten eklendi.", + "already_configured": "Bu \u00f6\u011frenci zaten eklendi.", + "reauth_successful": "Yeniden yetkilendirme ba\u015far\u0131l\u0131" + }, + "error": { + "cannot_connect": "Ba\u011flant\u0131 hatas\u0131 - l\u00fctfen internet ba\u011flant\u0131n\u0131z\u0131 kontrol edin", + "expired_credentials": "S\u00fcresi dolmu\u015f kimlik bilgileri - l\u00fctfen Vulcan mobil uygulama kay\u0131t sayfas\u0131nda yeni olu\u015fturun", + "expired_token": "S\u00fcresi dolmu\u015f anahtar - l\u00fctfen yeni bir anahtar olu\u015fturun", + "invalid_pin": "Ge\u00e7ersiz PIN", + "invalid_symbol": "Ge\u00e7ersiz sembol", + "invalid_token": "Ge\u00e7ersiz anahtar", + "unknown": "Bilinmeyen hata olu\u015ftu" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "Kaydedilmi\u015f kimlik bilgilerini kullan" + }, + "description": "Ba\u015fka bir \u00f6\u011frenci ekleyin." + }, + "auth": { + "data": { + "pin": "Pin", + "region": "Sembol", + "token": "Anahtar" + }, + "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "Sembol", + "token": "Anahtar" + }, + "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." + }, + "select_saved_credentials": { + "data": { + "credentials": "Oturum a\u00e7" + }, + "description": "Kaydedilmi\u015f kimlik bilgilerini se\u00e7in." + }, + "select_student": { + "data": { + "student_name": "\u00d6\u011frenci se\u00e7" + }, + "description": "\u00d6\u011frenciyi se\u00e7in, yeniden t\u00fcmle\u015ftirme ekleyerek daha fazla \u00f6\u011frenci ekleyebilirsiniz." + } + } + }, + "options": { + "error": { + "error": "Hata olu\u015ftu" + }, + "step": { + "init": { + "data": { + "attendance_notify": "En son kat\u0131l\u0131m giri\u015fleriyle ilgili bildirimleri g\u00f6ster", + "grade_notify": "En son notlarla ilgili bildirimleri g\u00f6ster", + "message_notify": "Yeni mesaj al\u0131nd\u0131\u011f\u0131nda bildirimleri g\u00f6ster", + "scan_interval": "G\u00fcncelleme aral\u0131\u011f\u0131 (dakika olarak)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/zh-Hant.json b/homeassistant/components/vulcan/translations/zh-Hant.json new file mode 100644 index 00000000000..3a1c06ce5ad --- /dev/null +++ b/homeassistant/components/vulcan/translations/zh-Hant.json @@ -0,0 +1,69 @@ +{ + "config": { + "abort": { + "all_student_already_configured": "\u6240\u6709\u5b78\u751f\u90fd\u5df2\u7d93\u65b0\u589e\u3002", + "already_configured": "\u8a72\u5b78\u751f\u5df2\u7d93\u65b0\u589e\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u932f\u8aa4 - \u8acb\u6aa2\u5bdf\u7db2\u8def\u9023\u7dda", + "expired_credentials": "\u6191\u8b49\u904e\u671f - \u8acb\u900f\u904e Vulcan \u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u91cd\u65b0\u65b0\u589e", + "expired_token": "\u6b0a\u6756\u5df2\u904e\u671f - \u8acb\u7522\u751f\u65b0\u6b0a\u6756", + "invalid_pin": "Pin \u7121\u6548", + "invalid_symbol": "\u7b26\u865f\u7121\u6548", + "invalid_token": "\u6b0a\u6756\u7121\u6548", + "unknown": "\u767c\u751f\u672a\u77e5\u932f\u8aa4" + }, + "step": { + "add_next_config_entry": { + "data": { + "use_saved_credentials": "\u4f7f\u7528\u5132\u5b58\u6191\u8b49" + }, + "description": "\u65b0\u589e\u5176\u4ed6\u5b78\u751f\u3002" + }, + "auth": { + "data": { + "pin": "Pin", + "region": "\u7b26\u865f", + "token": "\u6b0a\u6756" + }, + "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" + }, + "reauth": { + "data": { + "pin": "Pin", + "region": "\u7b26\u865f", + "token": "\u6b0a\u6756" + }, + "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" + }, + "select_saved_credentials": { + "data": { + "credentials": "\u767b\u5165" + }, + "description": "\u9078\u64c7\u5132\u5b58\u6191\u8b49\u3002" + }, + "select_student": { + "data": { + "student_name": "\u9078\u64c7\u5b78\u751f" + }, + "description": "\u9078\u64c7\u5b78\u751f\u3001\u53ef\u4ee5\u518d\u900f\u904e\u65b0\u589e\u6574\u5408\u65b0\u589e\u5176\u4ed6\u5b78\u751f\u3002" + } + } + }, + "options": { + "error": { + "error": "\u767c\u751f\u932f\u8aa4" + }, + "step": { + "init": { + "data": { + "attendance_notify": "\u95dc\u65bc\u6700\u65b0\u51fa\u52e4\u5be6\u9ad4\u986f\u793a\u901a\u77e5", + "grade_notify": "\u95dc\u65bc\u6700\u65b0\u6210\u7e3e\u986f\u793a\u901a\u77e5", + "message_notify": "\u7576\u6536\u5230\u65b0\u8a0a\u606f\u6642\u986f\u793a\u901a\u77e5", + "scan_interval": "\u66f4\u65b0\u983b\u7387\uff08\u5206\uff09" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/bg.json b/homeassistant/components/wallbox/translations/bg.json index 25f3bb50845..cc0ea3ece2d 100644 --- a/homeassistant/components/wallbox/translations/bg.json +++ b/homeassistant/components/wallbox/translations/bg.json @@ -24,6 +24,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/ca.json b/homeassistant/components/wallbox/translations/ca.json index b6a10e16e2a..6d76243a479 100644 --- a/homeassistant/components/wallbox/translations/ca.json +++ b/homeassistant/components/wallbox/translations/ca.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/de.json b/homeassistant/components/wallbox/translations/de.json index d415b4f9e7a..2aa206d1b7e 100644 --- a/homeassistant/components/wallbox/translations/de.json +++ b/homeassistant/components/wallbox/translations/de.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/el.json b/homeassistant/components/wallbox/translations/el.json index dd95266866f..48443b1b45f 100644 --- a/homeassistant/components/wallbox/translations/el.json +++ b/homeassistant/components/wallbox/translations/el.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/en.json b/homeassistant/components/wallbox/translations/en.json index 28ec5d08235..f32c7b7b481 100644 --- a/homeassistant/components/wallbox/translations/en.json +++ b/homeassistant/components/wallbox/translations/en.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/es.json b/homeassistant/components/wallbox/translations/es.json index 72c7b0587d6..1c5315d6745 100644 --- a/homeassistant/components/wallbox/translations/es.json +++ b/homeassistant/components/wallbox/translations/es.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/et.json b/homeassistant/components/wallbox/translations/et.json index f5d3b3aac73..cfe0720616a 100644 --- a/homeassistant/components/wallbox/translations/et.json +++ b/homeassistant/components/wallbox/translations/et.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/fr.json b/homeassistant/components/wallbox/translations/fr.json index b9499f72b15..e918573e13b 100644 --- a/homeassistant/components/wallbox/translations/fr.json +++ b/homeassistant/components/wallbox/translations/fr.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/he.json b/homeassistant/components/wallbox/translations/he.json index 6109bb22195..06a2f86efa9 100644 --- a/homeassistant/components/wallbox/translations/he.json +++ b/homeassistant/components/wallbox/translations/he.json @@ -23,6 +23,5 @@ } } } - }, - "title": "\u05ea\u05d9\u05d1\u05ea \u05e7\u05d9\u05e8" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/hu.json b/homeassistant/components/wallbox/translations/hu.json index 5579da0fe88..d6d0c4837d4 100644 --- a/homeassistant/components/wallbox/translations/hu.json +++ b/homeassistant/components/wallbox/translations/hu.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/id.json b/homeassistant/components/wallbox/translations/id.json index 08611ab3c2e..430a0eac114 100644 --- a/homeassistant/components/wallbox/translations/id.json +++ b/homeassistant/components/wallbox/translations/id.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/it.json b/homeassistant/components/wallbox/translations/it.json index ce4bdef9d95..726a77396e2 100644 --- a/homeassistant/components/wallbox/translations/it.json +++ b/homeassistant/components/wallbox/translations/it.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/ja.json b/homeassistant/components/wallbox/translations/ja.json index 8924bf891b9..4aa79afcb22 100644 --- a/homeassistant/components/wallbox/translations/ja.json +++ b/homeassistant/components/wallbox/translations/ja.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/nl.json b/homeassistant/components/wallbox/translations/nl.json index dbe8bd91f72..5cde7830b85 100644 --- a/homeassistant/components/wallbox/translations/nl.json +++ b/homeassistant/components/wallbox/translations/nl.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/no.json b/homeassistant/components/wallbox/translations/no.json index 74bbb1f39d5..498362fad1d 100644 --- a/homeassistant/components/wallbox/translations/no.json +++ b/homeassistant/components/wallbox/translations/no.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/pl.json b/homeassistant/components/wallbox/translations/pl.json index 51180d4c68e..369049c3812 100644 --- a/homeassistant/components/wallbox/translations/pl.json +++ b/homeassistant/components/wallbox/translations/pl.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/pt-BR.json b/homeassistant/components/wallbox/translations/pt-BR.json index 3fb6428603a..ea7b6c899ed 100644 --- a/homeassistant/components/wallbox/translations/pt-BR.json +++ b/homeassistant/components/wallbox/translations/pt-BR.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/ru.json b/homeassistant/components/wallbox/translations/ru.json index 426c07c9423..e35de176bf9 100644 --- a/homeassistant/components/wallbox/translations/ru.json +++ b/homeassistant/components/wallbox/translations/ru.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/tr.json b/homeassistant/components/wallbox/translations/tr.json index 1bee24a696d..a1c69aea28d 100644 --- a/homeassistant/components/wallbox/translations/tr.json +++ b/homeassistant/components/wallbox/translations/tr.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/wallbox/translations/zh-Hant.json b/homeassistant/components/wallbox/translations/zh-Hant.json index 3f282cda1f9..2688a050ce0 100644 --- a/homeassistant/components/wallbox/translations/zh-Hant.json +++ b/homeassistant/components/wallbox/translations/zh-Hant.json @@ -25,6 +25,5 @@ } } } - }, - "title": "Wallbox" + } } \ No newline at end of file diff --git a/homeassistant/components/waze_travel_time/translations/hu.json b/homeassistant/components/waze_travel_time/translations/hu.json index 401eb3c814c..75bc311d814 100644 --- a/homeassistant/components/waze_travel_time/translations/hu.json +++ b/homeassistant/components/waze_travel_time/translations/hu.json @@ -10,7 +10,7 @@ "user": { "data": { "destination": "\u00c9rkez\u00e9s helye", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "origin": "Indul\u00e1s helye", "region": "R\u00e9gi\u00f3" }, diff --git a/homeassistant/components/webostv/translations/hu.json b/homeassistant/components/webostv/translations/hu.json new file mode 100644 index 00000000000..de3f59d8c90 --- /dev/null +++ b/homeassistant/components/webostv/translations/hu.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "error_pairing": "Csatlakozva az LG webOS TV-hez, de a p\u00e1ros\u00edt\u00e1s nem siker\u00fclt" + }, + "error": { + "cannot_connect": "Nem siker\u00fclt csatlakozni, k\u00e9rj\u00fck, kapcsolja be a TV-t vagy ellen\u0151rizze az ip-c\u00edmet." + }, + "flow_title": "LG webOS Smart TV", + "step": { + "pairing": { + "description": "K\u00fcldje el a k\u00e9r\u00e9st, \u00e9s fogadja el a p\u00e1ros\u00edt\u00e1si k\u00e9relmet a t\u00e9v\u00e9n. \n\n ![Image](/static/images/config_webos.png)", + "title": "webOS TV p\u00e1ros\u00edt\u00e1s" + }, + "user": { + "data": { + "host": "C\u00edm", + "name": "Elnevez\u00e9s" + }, + "description": "Kapcsolja be a TV-t, t\u00f6ltse ki a k\u00f6vetkez\u0151 mez\u0151ket, ut\u00e1na folytassa", + "title": "Csatlakoz\u00e1s a webOS TV-hez" + } + } + }, + "device_automation": { + "trigger_type": { + "webostv.turn_on": "Az eszk\u00f6znek be kell lennie kapcsolva" + } + }, + "options": { + "error": { + "cannot_retrieve": "Nem siker\u00fclt lek\u00e9rni a forr\u00e1sok list\u00e1j\u00e1t. Gy\u0151z\u0151dj\u00f6n meg r\u00f3la, hogy a k\u00e9sz\u00fcl\u00e9k be van kapcsolva", + "script_not_found": "A szkript nem tal\u00e1lhat\u00f3" + }, + "step": { + "init": { + "data": { + "sources": "Forr\u00e1sok list\u00e1ja" + }, + "description": "Enged\u00e9lyezett forr\u00e1sok kiv\u00e1laszt\u00e1sa", + "title": "A webOS Smart TV be\u00e1ll\u00edt\u00e1sai" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wilight/translations/ca.json b/homeassistant/components/wilight/translations/ca.json index 0ace653e050..22044549b0c 100644 --- a/homeassistant/components/wilight/translations/ca.json +++ b/homeassistant/components/wilight/translations/ca.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Voleu configurar el WiLight {name}? \n\n Admet: {components}", + "description": "S'admeten els seg\u00fcents components: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/de.json b/homeassistant/components/wilight/translations/de.json index 546f8cec7b5..27d48a90e58 100644 --- a/homeassistant/components/wilight/translations/de.json +++ b/homeassistant/components/wilight/translations/de.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "M\u00f6chtest du WiLight {name} einrichten? \n\n Es unterst\u00fctzt: {components}", + "description": "Die folgenden Komponenten werden unterst\u00fctzt: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/en.json b/homeassistant/components/wilight/translations/en.json index 3d3a83a0270..1f0733d28f3 100644 --- a/homeassistant/components/wilight/translations/en.json +++ b/homeassistant/components/wilight/translations/en.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Do you want to set up WiLight {name}?\n\n It supports: {components}", + "description": "The following components are supported: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/et.json b/homeassistant/components/wilight/translations/et.json index 1be23313837..dae085d1084 100644 --- a/homeassistant/components/wilight/translations/et.json +++ b/homeassistant/components/wilight/translations/et.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Kas soovid seadistada WiLight'i {name} ?\n\n See toetab: {components}", + "description": "Toetatud on j\u00e4rgmised komponendid: {components}", "title": "" } } diff --git a/homeassistant/components/wilight/translations/fr.json b/homeassistant/components/wilight/translations/fr.json index 0a3851bb816..ddd85a5cc04 100644 --- a/homeassistant/components/wilight/translations/fr.json +++ b/homeassistant/components/wilight/translations/fr.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Voulez-vous configurer la WiLight {name}\u00a0?\n\n Elle prend en charge\u00a0: {components}", + "description": "Les composants suivants sont pris en charge\u00a0: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/hu.json b/homeassistant/components/wilight/translations/hu.json index 9ef669f1ed3..02db2f1b7df 100644 --- a/homeassistant/components/wilight/translations/hu.json +++ b/homeassistant/components/wilight/translations/hu.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani a WiLight {name}-t ? \n\nT\u00e1mogatja: {components}", + "description": "A k\u00f6vetkez\u0151 komponensek t\u00e1mogatottak: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/id.json b/homeassistant/components/wilight/translations/id.json index 06616b29e35..0489a0e96bb 100644 --- a/homeassistant/components/wilight/translations/id.json +++ b/homeassistant/components/wilight/translations/id.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Apakah Anda ingin menyiapkan WiLight {name}?\n\nIni mendukung: {components}", + "description": "Komponen berikut didukung: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/it.json b/homeassistant/components/wilight/translations/it.json index 84b323a0000..7b560b7bfdb 100644 --- a/homeassistant/components/wilight/translations/it.json +++ b/homeassistant/components/wilight/translations/it.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vuoi configurare WiLight {name}? \n\nSupporta: {components}", + "description": "Sono supportati i seguenti componenti: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/nl.json b/homeassistant/components/wilight/translations/nl.json index c2820f0ed6e..51cefe8ce28 100644 --- a/homeassistant/components/wilight/translations/nl.json +++ b/homeassistant/components/wilight/translations/nl.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Wil je WiLight {name} ? \n\n Het ondersteunt: {components}", + "description": "De volgende componenten worden ondersteund: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/no.json b/homeassistant/components/wilight/translations/no.json index 170739145da..582b2289a32 100644 --- a/homeassistant/components/wilight/translations/no.json +++ b/homeassistant/components/wilight/translations/no.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Vil du konfigurere WiLight {name} ? \n\n Den st\u00f8tter: {components}", + "description": "F\u00f8lgende komponenter st\u00f8ttes: {components}", "title": "" } } diff --git a/homeassistant/components/wilight/translations/pl.json b/homeassistant/components/wilight/translations/pl.json index 93957d016f5..c1f636b9d80 100644 --- a/homeassistant/components/wilight/translations/pl.json +++ b/homeassistant/components/wilight/translations/pl.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Czy chcesz skonfigurowa\u0107 WiLight {name}?\n\nObs\u0142uguje: {components}", + "description": "Obs\u0142ugiwane s\u0105 nast\u0119puj\u0105ce komponenty: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/pt-BR.json b/homeassistant/components/wilight/translations/pt-BR.json index 5da689b9b74..e70a286e812 100644 --- a/homeassistant/components/wilight/translations/pt-BR.json +++ b/homeassistant/components/wilight/translations/pt-BR.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Voc\u00ea deseja configurar WiLight {name}?\n\nEle suporta: {components}", + "description": "Os seguintes componentes s\u00e3o compat\u00edveis: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/ru.json b/homeassistant/components/wilight/translations/ru.json index 7d04a13518d..7873b8ca326 100644 --- a/homeassistant/components/wilight/translations/ru.json +++ b/homeassistant/components/wilight/translations/ru.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c WiLight {name}? \n\n\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442: {components}", + "description": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b: {components}.", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/tr.json b/homeassistant/components/wilight/translations/tr.json index 446504f49d7..4762a678254 100644 --- a/homeassistant/components/wilight/translations/tr.json +++ b/homeassistant/components/wilight/translations/tr.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "{name} kurmak istiyor musunuz? \n\n \u015eunlar\u0131 destekler: {components}", + "description": "A\u015fa\u011f\u0131daki bile\u015fenler desteklenir: {components}", "title": "WiLight" } } diff --git a/homeassistant/components/wilight/translations/zh-Hant.json b/homeassistant/components/wilight/translations/zh-Hant.json index fe6c36f21d2..70a9bff0550 100644 --- a/homeassistant/components/wilight/translations/zh-Hant.json +++ b/homeassistant/components/wilight/translations/zh-Hant.json @@ -8,7 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a WiLight {name}\uff1f\n\n\u652f\u63f4\uff1a{components}", + "description": "\u652f\u63f4\u4ee5\u4e0b\u5143\u4ef6\uff1a{components}", "title": "WiLight" } } diff --git a/homeassistant/components/withings/translations/hu.json b/homeassistant/components/withings/translations/hu.json index 1a64a95de5e..7504c36c58e 100644 --- a/homeassistant/components/withings/translations/hu.json +++ b/homeassistant/components/withings/translations/hu.json @@ -4,7 +4,7 @@ "already_configured": "A profil konfigur\u00e1ci\u00f3ja friss\u00edtve.", "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", - "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3t [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lsz." + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3." }, "create_entry": { "default": "A Withings sikeresen hiteles\u00edtett." @@ -15,7 +15,7 @@ "flow_title": "{profile}", "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" }, "profile": { "data": { diff --git a/homeassistant/components/withings/translations/it.json b/homeassistant/components/withings/translations/it.json index f97933593a0..079acb0b503 100644 --- a/homeassistant/components/withings/translations/it.json +++ b/homeassistant/components/withings/translations/it.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Configurazione aggiornata per il profilo.", "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})" }, "create_entry": { diff --git a/homeassistant/components/wiz/translations/hu.json b/homeassistant/components/wiz/translations/hu.json index 7c99571a9ae..63ae8479bb9 100644 --- a/homeassistant/components/wiz/translations/hu.json +++ b/homeassistant/components/wiz/translations/hu.json @@ -28,7 +28,7 @@ "user": { "data": { "host": "IP c\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" }, "description": "Ha az IP-c\u00edmet \u00fcresen hagyja, akkor az eszk\u00f6z\u00f6k keres\u00e9se a felder\u00edt\u00e9ssel t\u00f6rt\u00e9nik." } diff --git a/homeassistant/components/wolflink/translations/sensor.hu.json b/homeassistant/components/wolflink/translations/sensor.hu.json index 0a257e570cf..71e8f1e42ca 100644 --- a/homeassistant/components/wolflink/translations/sensor.hu.json +++ b/homeassistant/components/wolflink/translations/sensor.hu.json @@ -65,7 +65,7 @@ "sparen": "Gazdas\u00e1gos", "spreizung_hoch": "dT t\u00fal sz\u00e9les", "spreizung_kf": "Spread KF", - "stabilisierung": "Stabiliz\u00e1ci\u00f3", + "stabilisierung": "Stabiliz\u00e1l\u00e1s", "standby": "K\u00e9szenl\u00e9t", "start": "Indul\u00e1s", "storung": "Hiba", diff --git a/homeassistant/components/xbox/translations/hu.json b/homeassistant/components/xbox/translations/hu.json index 24c46bb8ab0..fc8428b3101 100644 --- a/homeassistant/components/xbox/translations/hu.json +++ b/homeassistant/components/xbox/translations/hu.json @@ -10,7 +10,7 @@ }, "step": { "pick_implementation": { - "title": "V\u00e1lassz hiteles\u00edt\u00e9si m\u00f3dszert" + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } } diff --git a/homeassistant/components/xbox/translations/it.json b/homeassistant/components/xbox/translations/it.json index 010baad571e..e60c37c9e5f 100644 --- a/homeassistant/components/xbox/translations/it.json +++ b/homeassistant/components/xbox/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", - "missing_configuration": "Il componente non \u00e8 configurato. Si prega di seguire la documentazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "create_entry": { diff --git a/homeassistant/components/xiaomi_aqara/translations/ca.json b/homeassistant/components/xiaomi_aqara/translations/ca.json index 46037fa5eea..78f5affd556 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ca.json +++ b/homeassistant/components/xiaomi_aqara/translations/ca.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Adre\u00e7a IP" }, - "description": "Torna a executar la configuraci\u00f3 si vols connectar passarel\u00b7les addicionals", + "description": "Selecciona la passarel\u00b7la Xiaomi Aqara a la qual connectar-te", "title": "Selecciona la passarel\u00b7la Xiaomi Aqara a la qual connectar-te" }, "settings": { @@ -27,7 +27,7 @@ "name": "Nom de la passarel\u00b7la" }, "description": "La clau (contrasenya) es pot obtenir mitjan\u00e7ant aquest tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Si no es proporciona la clau, nom\u00e9s seran accessibles els sensors", - "title": "Passarel\u00b7la Xiaomi Aqara, configuraci\u00f3 opcional" + "title": "Configuracions opcionals" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Interf\u00edcie de xarxa a utilitzar", "mac": "Adre\u00e7a MAC (opcional)" }, - "description": "Connecta't a la teva passarel\u00b7la Xiaomi Aqara, si les adreces IP i MAC es deixen buides s'utilitzar\u00e0 els descobriment autom\u00e0tic", + "description": "Si les adreces IP i MAC es deixen buides s'utilitzar\u00e0 el descobriment autom\u00e0tic", "title": "Passarel\u00b7la Xiaomi Aqara" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/de.json b/homeassistant/components/xiaomi_aqara/translations/de.json index 469fa14bcc1..170442fc856 100644 --- a/homeassistant/components/xiaomi_aqara/translations/de.json +++ b/homeassistant/components/xiaomi_aqara/translations/de.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP-Adresse" }, - "description": "F\u00fchre das Setup erneut aus, wenn du zus\u00e4tzliche Gateways verbinden m\u00f6chtest", + "description": "W\u00e4hle das Xiaomi Aqara Gateway, das du verbinden m\u00f6chtest", "title": "W\u00e4hle das Xiaomi Aqara Gateway, das du verbinden m\u00f6chtest" }, "settings": { @@ -27,7 +27,7 @@ "name": "Name des Gateways" }, "description": "Der Schl\u00fcssel (das Passwort) kann mithilfe dieser Anleitung abgerufen werden: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Wenn der Schl\u00fcssel nicht angegeben wird, sind nur die Sensoren zug\u00e4nglich", - "title": "Xiaomi Aqara Gateway, optionale Einstellungen" + "title": "Optionale Einstellungen" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Die zu verwendende Netzwerkschnittstelle", "mac": "MAC-Adresse (optional)" }, - "description": "Stelle eine Verbindung zu deinem Xiaomi Aqara Gateway her. Wenn die IP- und MAC-Adressen leer bleiben, wird die automatische Erkennung verwendet", + "description": "Wenn die IP- und MAC-Adressen leer gelassen werden, wird die automatische Erkennung verwendet", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/en.json b/homeassistant/components/xiaomi_aqara/translations/en.json index 53776111d37..72949820bc0 100644 --- a/homeassistant/components/xiaomi_aqara/translations/en.json +++ b/homeassistant/components/xiaomi_aqara/translations/en.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP Address" }, - "description": "Run the setup again if you want to connect additional gateways", + "description": "Select the Xiaomi Aqara Gateway that you wish to connect", "title": "Select the Xiaomi Aqara Gateway that you wish to connect" }, "settings": { @@ -27,7 +27,7 @@ "name": "Name of the Gateway" }, "description": "The key (password) can be retrieved using this tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. If the key is not provided only sensors will be accessible", - "title": "Xiaomi Aqara Gateway, optional settings" + "title": "Optional settings" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "The network interface to use", "mac": "Mac Address (optional)" }, - "description": "Connect to your Xiaomi Aqara Gateway, if the IP and MAC addresses are left empty, auto-discovery is used", + "description": "If the IP and MAC addresses are left empty, auto-discovery is used", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/et.json b/homeassistant/components/xiaomi_aqara/translations/et.json index cc94b0e9b95..087c59dd2e2 100644 --- a/homeassistant/components/xiaomi_aqara/translations/et.json +++ b/homeassistant/components/xiaomi_aqara/translations/et.json @@ -18,7 +18,7 @@ "data": { "select_ip": "L\u00fc\u00fcsi IP aadress" }, - "description": "K\u00e4ivita seadistamine uuesti kui soovid \u00fchendada t\u00e4iendavaid l\u00fc\u00fcse", + "description": "Vali Xiaomi Aqara Gateway mida soovid \u00fchendada.", "title": "Vali Xiaomi Aqara l\u00fc\u00fcs mida soovid \u00fchendada" }, "settings": { @@ -27,7 +27,7 @@ "name": "L\u00fc\u00fcsi nimi" }, "description": "V\u00f5tme (parooli) saab hankida selle \u00f5petuse abil: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Kui v\u00f5ti puudub on ligip\u00e4\u00e4s ainult anduritele", - "title": "Xiaomi Aqara Gateway, valikulised s\u00e4tted" + "title": "Valikulised s\u00e4tted" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Kasutatav v\u00f5rguliides", "mac": "MAC aadress (valikuline)" }, - "description": "\u00dchendu oma Xiaomi Aqara Gatewayga. Kui IP- ja MAC-aadressid j\u00e4etakse t\u00fchjaks, kasutatakse automaatset avastamist", + "description": "Kui IP- ja MAC-aadressid j\u00e4etakse t\u00fchjaks, kasutatakse automaatset avastamist", "title": "" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/fr.json b/homeassistant/components/xiaomi_aqara/translations/fr.json index 04be4e35e2f..ab042cb5f0e 100644 --- a/homeassistant/components/xiaomi_aqara/translations/fr.json +++ b/homeassistant/components/xiaomi_aqara/translations/fr.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Adresse IP" }, - "description": "Ex\u00e9cutez \u00e0 nouveau la configuration si vous souhaitez connecter des passerelles suppl\u00e9mentaires", + "description": "S\u00e9lectionnez la passerelle Xiaomi Aqara que vous souhaitez connecter", "title": "S\u00e9lectionnez la passerelle Xiaomi Aqara que vous souhaitez connecter" }, "settings": { @@ -27,7 +27,7 @@ "name": "Nom de la passerelle" }, "description": "La cl\u00e9 (mot de passe) peut \u00eatre r\u00e9cup\u00e9r\u00e9e \u00e0 l'aide de ce tutoriel: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Si la cl\u00e9 n'est pas fournie, seuls les capteurs seront accessibles", - "title": "Passerelle Xiaomi Aqara, param\u00e8tres optionnels" + "title": "Param\u00e8tres optionnels" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Interface r\u00e9seau \u00e0 utiliser", "mac": "Adresse MAC (facultatif)" }, - "description": "Connectez-vous \u00e0 votre passerelle Xiaomi Aqara, si les adresses IP et mac sont laiss\u00e9es vides, la d\u00e9tection automatique est utilis\u00e9e", + "description": "Si les adresses IP et MAC sont laiss\u00e9es vides, la d\u00e9couverte automatique sera utilis\u00e9e", "title": "Passerelle Xiaomi Aqara" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/he.json b/homeassistant/components/xiaomi_aqara/translations/he.json index 7450bbd463c..ea6cd8e278b 100644 --- a/homeassistant/components/xiaomi_aqara/translations/he.json +++ b/homeassistant/components/xiaomi_aqara/translations/he.json @@ -27,7 +27,7 @@ "name": "\u05e9\u05dd \u05d4\u05e9\u05e2\u05e8" }, "description": "\u05e0\u05d9\u05ea\u05df \u05dc\u05d0\u05d7\u05d6\u05e8 \u05d0\u05ea \u05d4\u05de\u05e4\u05ea\u05d7 (\u05e1\u05d9\u05e1\u05de\u05d4) \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d4\u05d3\u05e8\u05db\u05d4 \u05d6\u05d5: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. \u05d0\u05dd \u05d4\u05de\u05e4\u05ea\u05d7 \u05d0\u05d9\u05e0\u05d5 \u05de\u05e1\u05d5\u05e4\u05e7, \u05e8\u05e7 \u05d7\u05d9\u05d9\u05e9\u05e0\u05d9\u05dd \u05d9\u05d4\u05d9\u05d5 \u05e0\u05d2\u05d9\u05e9\u05d9\u05dd", - "title": "\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05d0\u05e7\u05d0\u05e8\u05d4, \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9\u05d5\u05ea" + "title": "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9\u05d5\u05ea" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "\u05de\u05de\u05e9\u05e7 \u05d4\u05e8\u05e9\u05ea \u05d1\u05d5 \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9", "mac": "\u05db\u05ea\u05d5\u05d1\u05ea Mac (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)" }, - "description": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05d0\u05e7\u05d0\u05e8\u05d4 \u05e9\u05dc\u05da, \u05d0\u05dd \u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d4-IP \u05d5\u05d4-MAC \u05d9\u05d5\u05d5\u05ea\u05e8\u05d5 \u05e8\u05d9\u05e7\u05d5\u05ea, \u05e0\u05e2\u05e9\u05d4 \u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9", + "description": "\u05d0\u05dd \u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d4-IP \u05d5\u05d4-MAC \u05e0\u05d5\u05ea\u05e8\u05d5\u05ea \u05e8\u05d9\u05e7\u05d5\u05ea, \u05e0\u05e2\u05e9\u05d4 \u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9", "title": "\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05d0\u05e7\u05d0\u05e8\u05d4" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/hu.json b/homeassistant/components/xiaomi_aqara/translations/hu.json index e38bebefe19..f0a1b076750 100644 --- a/homeassistant/components/xiaomi_aqara/translations/hu.json +++ b/homeassistant/components/xiaomi_aqara/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "not_xiaomi_aqara": "Nem egy Xiaomi Aqara Gateway, a felfedezett eszk\u00f6z nem egyezett az ismert \u00e1tj\u00e1r\u00f3kkal" }, "error": { @@ -18,7 +18,7 @@ "data": { "select_ip": "IP c\u00edm" }, - "description": "Futtassa \u00fajra a be\u00e1ll\u00edt\u00e1st, ha egy m\u00e1sik k\u00f6zponti egys\u00e9get szeretne csatlakoztatni", + "description": "V\u00e1lassza ki a csatlakoztatni k\u00edv\u00e1nt Xiaomi Aqara K\u00f6zponti egys\u00e9get", "title": "V\u00e1lassza ki a csatlakoztatni k\u00edv\u00e1nt Xiaomi Aqara K\u00f6zponti egys\u00e9get" }, "settings": { @@ -27,7 +27,7 @@ "name": "K\u00f6zponti egys\u00e9g neve" }, "description": "A kulcs (jelsz\u00f3) az al\u00e1bbi oktat\u00f3anyag seg\u00edts\u00e9g\u00e9vel t\u00f6lthet\u0151 le: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Ha a kulcs nincs megadva, csak az \u00e9rz\u00e9kel\u0151k lesznek hozz\u00e1f\u00e9rhet\u0151k", - "title": "Xiaomi Aqara k\u00f6zponti egys\u00e9g, opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok" + "title": "Opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "A haszn\u00e1lni k\u00edv\u00e1nt h\u00e1l\u00f3zati interf\u00e9sz", "mac": "Mac-c\u00edm (opcion\u00e1lis)" }, - "description": "Csatlakozzon a Xiaomi Aqara k\u00f6zponti egys\u00e9ghez, ha az IP- \u00e9s a MAC-c\u00edm \u00fcresen marad, akkor az automatikus felder\u00edt\u00e9st haszn\u00e1lja", + "description": "Ha az IP- \u00e9s MAC-c\u00edm mez\u0151ket \u00fcresen hagyja, akkor az automatikus felder\u00edt\u00e9s ker\u00fcl alkalmaz\u00e1sra.", "title": "Xiaomi Aqara k\u00f6zponti egys\u00e9g" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/id.json b/homeassistant/components/xiaomi_aqara/translations/id.json index eeab548f681..2b33af8237f 100644 --- a/homeassistant/components/xiaomi_aqara/translations/id.json +++ b/homeassistant/components/xiaomi_aqara/translations/id.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Alamat IP" }, - "description": "Jalankan penyiapan lagi jika Anda ingin menghubungkan gateway lainnya", + "description": "Pilih Gateway Xiaomi Aqara yang ingin disambungkan", "title": "Pilih Gateway Xiaomi Aqara yang ingin dihubungkan" }, "settings": { @@ -27,7 +27,7 @@ "name": "Nama Gateway" }, "description": "Kunci (kata sandi) dapat diambil menggunakan tutorial ini: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Jika kunci tidak disediakan, hanya sensor yang akan dapat diakses", - "title": "Xiaomi Aqara Gateway, pengaturan opsional" + "title": "Pengaturan opsional" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Antarmuka jaringan yang akan digunakan", "mac": "Alamat MAC (opsional)" }, - "description": "Hubungkan ke Xiaomi Aqara Gateway Anda, jika alamat IP dan MAC dibiarkan kosong, penemuan otomatis digunakan", + "description": "Jika alamat IP dan MAC dikosongkan, penemuan otomatis digunakan", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/it.json b/homeassistant/components/xiaomi_aqara/translations/it.json index 319a33f3964..ac5eff78190 100644 --- a/homeassistant/components/xiaomi_aqara/translations/it.json +++ b/homeassistant/components/xiaomi_aqara/translations/it.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Indirizzo IP" }, - "description": "Esegui di nuovo la configurazione se desideri connettere gateway aggiuntivi", + "description": "Seleziona lo Xiaomi Aqara Gateway che desideri connettere", "title": "Seleziona il Gateway Xiaomi Aqara che si desidera collegare" }, "settings": { @@ -27,7 +27,7 @@ "name": "Nome del Gateway" }, "description": "La chiave (password) pu\u00f2 essere recuperata utilizzando questo tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Se la chiave non viene fornita, saranno accessibili solo i sensori", - "title": "Xiaomi Aqara Gateway, impostazioni opzionali" + "title": "Impostazioni facoltative" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "L'interfaccia di rete da utilizzare", "mac": "Indirizzo Mac (opzionale)" }, - "description": "Connettiti al tuo Xiaomi Aqara Gateway, se gli indirizzi IP e MAC sono lasciati vuoti, sar\u00e0 utilizzato il rilevamento automatico", + "description": "Se gli indirizzi IP e MAC vengono lasciati vuoti, viene utilizzato il rilevamento automatico", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/nl.json b/homeassistant/components/xiaomi_aqara/translations/nl.json index 9ef660a8e7f..c2042850fdb 100644 --- a/homeassistant/components/xiaomi_aqara/translations/nl.json +++ b/homeassistant/components/xiaomi_aqara/translations/nl.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP-adres" }, - "description": "Voer de installatie opnieuw uit als u extra gateways wilt aansluiten", + "description": "Selecteer de Xiaomi Aqara Gateway die u wilt verbinden", "title": "Selecteer de Xiaomi Aqara Gateway waarmee u verbinding wilt maken" }, "settings": { @@ -27,7 +27,7 @@ "name": "Naam van de Gateway" }, "description": "De sleutel (wachtwoord) kan worden opgehaald met behulp van deze tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Als de sleutel niet wordt meegeleverd, zijn alleen sensoren toegankelijk", - "title": "Xiaomi Aqara Gateway, optionele instellingen" + "title": "Optionele instellingen" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "De netwerkinterface die moet worden gebruikt", "mac": "MAC-adres (optioneel)" }, - "description": "Maak verbinding met uw Xiaomi Aqara Gateway, als de IP- en mac-adressen leeg worden gelaten, wordt automatische detectie gebruikt", + "description": "Als de IP- en MAC-adressen leeg worden gelaten, wordt auto-discovery gebruikt", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/no.json b/homeassistant/components/xiaomi_aqara/translations/no.json index 081b0e5e990..c325886dd22 100644 --- a/homeassistant/components/xiaomi_aqara/translations/no.json +++ b/homeassistant/components/xiaomi_aqara/translations/no.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP adresse" }, - "description": "Kj\u00f8r oppsettet p\u00e5 nytt hvis du vil koble til flere gatewayer", + "description": "Velg Xiaomi Aqara Gateway som du \u00f8nsker \u00e5 koble til", "title": "Velg Xiaomi Aqara Gateway som du \u00f8nsker \u00e5 koble til" }, "settings": { @@ -27,7 +27,7 @@ "name": "Navnet p\u00e5 gatewayen" }, "description": "N\u00f8kkelen (passordet) kan hentes ved hjelp av denne veiviseren: [https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz](https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz). Hvis n\u00f8kkelen ikke oppgis, vil bare sensorer bli tilgjengelige", - "title": "Xiaomi Aqara Gateway, valgfrie innstillinger" + "title": "Valgfrie innstillinger" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Nettverksgrensesnittet som skal brukes", "mac": "MAC-adresse (valgfritt)" }, - "description": "Koble til Xiaomi Aqara Gateway, hvis IP- og MAC-adressene blir tomme, brukes automatisk oppdagelse", + "description": "Hvis IP- og MAC-adressene er tomme, brukes automatisk oppdagelse", "title": "" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/pl.json b/homeassistant/components/xiaomi_aqara/translations/pl.json index da8a803d81f..e680415320c 100644 --- a/homeassistant/components/xiaomi_aqara/translations/pl.json +++ b/homeassistant/components/xiaomi_aqara/translations/pl.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Adres IP" }, - "description": "Uruchom konfiguracj\u0119 ponownie, je\u015bli chcesz pod\u0142\u0105czy\u0107 dodatkowe bramki", + "description": "Wybierz bramk\u0119 Xiaomi Aqara, z kt\u00f3r\u0105 chcesz si\u0119 po\u0142\u0105czy\u0107", "title": "Wybierz bramk\u0119 Xiaomi Aqara, z kt\u00f3r\u0105 chcesz si\u0119 po\u0142\u0105czy\u0107" }, "settings": { @@ -27,7 +27,7 @@ "name": "Nazwa bramki" }, "description": "Klucz (has\u0142o) mo\u017cna uzyska\u0107 za pomoc\u0105 tej instrukcji: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Je\u015bli klucz nie zostanie podany, dost\u0119pne b\u0119d\u0105 tylko sensory.", - "title": "Brama Xiaomi Aqara, ustawienia opcjonalne" + "title": "Ustawienia opcjonalne" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Interfejs sieciowy", "mac": "Adres MAC (opcjonalnie)" }, - "description": "Po\u0142\u0105cz si\u0119 z bramk\u0105 Xiaomi Aqara, je\u015bli zostawisz Adres IP oraz MAC puste to bramka zostanie automatycznie wykryta.", + "description": "Je\u015bli zostawisz Adres IP oraz MAC puste to bramka zostanie automatycznie wykryta.", "title": "Bramka Xiaomi Aqara" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/pt-BR.json b/homeassistant/components/xiaomi_aqara/translations/pt-BR.json index 5e188e54565..62deb3ba97c 100644 --- a/homeassistant/components/xiaomi_aqara/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_aqara/translations/pt-BR.json @@ -18,7 +18,7 @@ "data": { "select_ip": "Endere\u00e7o IP" }, - "description": "Execute a configura\u00e7\u00e3o novamente se quiser conectar gateways adicionais", + "description": "Selecione o Xiaomi Aqara Gateway que voc\u00ea deseja conectar", "title": "Selecione o Xiaomi Aqara Gateway que voc\u00ea deseja conectar" }, "settings": { @@ -27,15 +27,15 @@ "name": "Nome do Gateway" }, "description": "A chave (senha) pode ser recuperada usando este tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Se a chave n\u00e3o for fornecida, apenas os sensores estar\u00e3o acess\u00edveis", - "title": "Xiaomi Aqara Gateway, configura\u00e7\u00f5es opcionais" + "title": "Configura\u00e7\u00f5es opcionais" }, "user": { "data": { - "host": "Endere\u00e7o IP", + "host": "Endere\u00e7o IP (opcional)", "interface": "A interface de rede a ser usada", "mac": "Endere\u00e7o Mac (opcional)" }, - "description": "Conecte-se ao seu Xiaomi Aqara Gateway, se os endere\u00e7os IP e MAC ficarem vazios, a descoberta autom\u00e1tica \u00e9 usada", + "description": "Se os endere\u00e7os IP e MAC forem deixados vazios, a descoberta autom\u00e1tica \u00e9 usada", "title": "Gateway Xiaomi Aqara" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/ru.json b/homeassistant/components/xiaomi_aqara/translations/ru.json index aa18808d222..499837889df 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ru.json +++ b/homeassistant/components/xiaomi_aqara/translations/ru.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP-\u0430\u0434\u0440\u0435\u0441" }, - "description": "\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0435\u0449\u0451 \u0440\u0430\u0437, \u0435\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u0448\u043b\u044e\u0437.", + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 Xiaomi Aqara.", "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 Xiaomi Aqara" }, "settings": { @@ -27,7 +27,7 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, "description": "\u041a\u043b\u044e\u0447 (\u043f\u0430\u0440\u043e\u043b\u044c) \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0430: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. \u0415\u0441\u043b\u0438 \u043a\u043b\u044e\u0447 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d, \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0438.", - "title": "\u0428\u043b\u044e\u0437 Xiaomi Aqara" + "title": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "\u0421\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441", "mac": "MAC-\u0430\u0434\u0440\u0435\u0441 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441\u043e \u0448\u043b\u044e\u0437\u043e\u043c Xiaomi Aqara. \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0438 MAC \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043f\u0443\u0441\u0442\u044b\u043c\u0438.", + "description": "\u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0438 MAC \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043f\u0443\u0441\u0442\u044b\u043c\u0438.", "title": "\u0428\u043b\u044e\u0437 Xiaomi Aqara" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/tr.json b/homeassistant/components/xiaomi_aqara/translations/tr.json index 6281b7a397e..0c961a34a3c 100644 --- a/homeassistant/components/xiaomi_aqara/translations/tr.json +++ b/homeassistant/components/xiaomi_aqara/translations/tr.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP Adresi" }, - "description": "Ek a\u011f ge\u00e7itlerini ba\u011flamak istiyorsan\u0131z kurulumu tekrar \u00e7al\u0131\u015ft\u0131r\u0131n.", + "description": "Ba\u011flamak istedi\u011finiz Xiaomi Aqara A\u011f Ge\u00e7idini se\u00e7in", "title": "Ba\u011flamak istedi\u011finiz Xiaomi Aqara A\u011f Ge\u00e7idini se\u00e7in" }, "settings": { @@ -27,7 +27,7 @@ "name": "A\u011f Ge\u00e7idinin Ad\u0131" }, "description": "Anahtar (parola) bu \u00f6\u011fretici kullan\u0131larak al\u0131nabilir: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Anahtar sa\u011flanmazsa, yaln\u0131zca sens\u00f6rlere eri\u015filebilir", - "title": "Xiaomi Aqara A\u011f Ge\u00e7idi, iste\u011fe ba\u011fl\u0131 ayarlar" + "title": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "Kullan\u0131lacak a\u011f aray\u00fcz\u00fc", "mac": "Mac Adresi (iste\u011fe ba\u011fl\u0131)" }, - "description": "Xiaomi Aqara Gateway'inize ba\u011flan\u0131n, IP ve MAC adresleri bo\u015f b\u0131rak\u0131l\u0131rsa otomatik ke\u015fif kullan\u0131l\u0131r", + "description": "IP ve MAC adresleri bo\u015f b\u0131rak\u0131l\u0131rsa otomatik ke\u015fif kullan\u0131l\u0131r", "title": "Xiaomi Aqara A\u011f Ge\u00e7idi" } } diff --git a/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json b/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json index 058efd39631..5ffe34d5d39 100644 --- a/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json @@ -18,7 +18,7 @@ "data": { "select_ip": "IP \u4f4d\u5740" }, - "description": "\u5982\u679c\u9084\u9700\u8981\u9023\u7dda\u81f3\u5176\u4ed6\u7db2\u95dc\uff0c\u8acb\u518d\u57f7\u884c\u4e00\u6b21\u8a2d\u5b9a", + "description": "\u5982\u679c\u6240\u8981\u9023\u7dda\u7684\u5c0f\u7c73 Aqara \u7db2\u95dc", "title": "\u9078\u64c7\u6240\u8981\u9023\u7dda\u7684\u5c0f\u7c73 Aqara \u7db2\u95dc" }, "settings": { @@ -27,7 +27,7 @@ "name": "\u7db2\u95dc\u540d\u7a31" }, "description": "\u91d1\u9470\uff08\u5bc6\u78bc\uff09\u53d6\u5f97\u8acb\u53c3\u8003\u4e0b\u65b9\u6559\u5b78\uff1ahttps://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz\u3002\u5047\u5982\u672a\u63d0\u4f9b\u91d1\u9470\u3001\u5247\u50c5\u6703\u6536\u5230\u611f\u6e2c\u5668\u88dd\u7f6e\u7684\u8cc7\u8a0a", - "title": "\u5c0f\u7c73 Aqara \u7db2\u95dc\u9078\u9805\u8a2d\u5b9a" + "title": "\u9078\u9805\u8a2d\u5b9a" }, "user": { "data": { @@ -35,7 +35,7 @@ "interface": "\u4f7f\u7528\u7684\u7db2\u8def\u4ecb\u9762", "mac": "Mac \u4f4d\u5740\uff08\u9078\u9805\uff09" }, - "description": "\u9023\u7dda\u81f3\u5c0f\u7c73 Aqara \u7db2\u95dc\uff0c\u5047\u5982 IP \u6216 Mac \u4f4d\u5740\u70ba\u7a7a\u767d\u3001\u5c07\u9032\u884c\u81ea\u52d5\u641c\u7d22", + "description": "\u5047\u5982 IP \u6216 Mac \u4f4d\u5740\u70ba\u7a7a\u767d\u3001\u5c07\u9032\u884c\u81ea\u52d5\u641c\u7d22", "title": "\u5c0f\u7c73 Aqara \u7db2\u95dc" } } diff --git a/homeassistant/components/xiaomi_miio/translations/he.json b/homeassistant/components/xiaomi_miio/translations/he.json index 4bb0251d6cb..db0cee16884 100644 --- a/homeassistant/components/xiaomi_miio/translations/he.json +++ b/homeassistant/components/xiaomi_miio/translations/he.json @@ -51,7 +51,7 @@ "name": "\u05e9\u05dd \u05d4\u05e9\u05e2\u05e8", "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" }, - "description": "\u05d0\u05ea\u05d4 \u05d6\u05e7\u05d5\u05e7 \u05dc-32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API , \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05db\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara.", + "description": "\u05e0\u05d3\u05e8\u05e9\u05d9\u05dd 32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API, \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05db\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara.", "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" }, "manual": { diff --git a/homeassistant/components/xiaomi_miio/translations/hu.json b/homeassistant/components/xiaomi_miio/translations/hu.json index 8d2340988dd..189e9906e24 100644 --- a/homeassistant/components/xiaomi_miio/translations/hu.json +++ b/homeassistant/components/xiaomi_miio/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "incomplete_info": "Az eszk\u00f6z be\u00e1ll\u00edt\u00e1s\u00e1hoz sz\u00fcks\u00e9ges inform\u00e1ci\u00f3k hi\u00e1nyosak, nincs megadva \u00e1llom\u00e1s vagy token.", "not_xiaomi_miio": "Az eszk\u00f6zt (m\u00e9g) nem t\u00e1mogatja a Xiaomi Miio integr\u00e1ci\u00f3.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." diff --git a/homeassistant/components/xiaomi_miio/translations/pt-BR.json b/homeassistant/components/xiaomi_miio/translations/pt-BR.json index 9e966a541d5..1116de258fa 100644 --- a/homeassistant/components/xiaomi_miio/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_miio/translations/pt-BR.json @@ -25,7 +25,7 @@ "cloud_username": "Usu\u00e1rio da Cloud", "manual": "Configurar manualmente (n\u00e3o recomendado)" }, - "description": "Fa\u00e7a login na Cloud Xiaomi Miio, consulte https://www.openhab.org/addons/bindings/miio/#country-servers para o servidor em cloud usar.", + "description": "Coloque o login da Cloud Xiaomi Miio e consulte https://www.openhab.org/addons/bindings/miio/#country-servers para saber qual servidor cloud usar.", "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" }, "connect": { diff --git a/homeassistant/components/yale_smart_alarm/translations/hu.json b/homeassistant/components/yale_smart_alarm/translations/hu.json index 028f2cd6f0f..a18480fcac0 100644 --- a/homeassistant/components/yale_smart_alarm/translations/hu.json +++ b/homeassistant/components/yale_smart_alarm/translations/hu.json @@ -12,7 +12,7 @@ "reauth_confirm": { "data": { "area_id": "Ter\u00fclet ID", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" } @@ -20,7 +20,7 @@ "user": { "data": { "area_id": "Ter\u00fclet ID", - "name": "N\u00e9v", + "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" } diff --git a/homeassistant/components/yeelight/translations/cs.json b/homeassistant/components/yeelight/translations/cs.json index adc42efddb7..2a3084fd3eb 100644 --- a/homeassistant/components/yeelight/translations/cs.json +++ b/homeassistant/components/yeelight/translations/cs.json @@ -27,7 +27,7 @@ "init": { "data": { "model": "Model (voliteln\u00fd)", - "nightlight_switch": "Pou\u017e\u00edt p\u0159ep\u00edna\u010d no\u010dn\u00edho osv\u011btlen\u00ed", + "nightlight_switch": "Pou\u017e\u00edt vyp\u00edna\u010d no\u010dn\u00edho osv\u011btlen\u00ed", "save_on_change": "Ulo\u017eit stav p\u0159i zm\u011bn\u011b", "transition": "\u010cas p\u0159echodu (v ms)", "use_music_mode": "Povolit hudebn\u00ed re\u017eim" diff --git a/homeassistant/components/yeelight/translations/fr.json b/homeassistant/components/yeelight/translations/fr.json index b2153e6aec0..a319f15e36a 100644 --- a/homeassistant/components/yeelight/translations/fr.json +++ b/homeassistant/components/yeelight/translations/fr.json @@ -32,7 +32,7 @@ "model": "Mod\u00e8le", "nightlight_switch": "Utiliser le commutateur de veilleuse", "save_on_change": "Enregistrer l'\u00e9tat lors d'un changement", - "transition": "Dur\u00e9e de transition (en millisecondes)", + "transition": "Dur\u00e9e de la transition (en millisecondes)", "use_music_mode": "Activer le mode musique" }, "description": "Si vous ne pr\u00e9cisez pas le mod\u00e8le, il sera automatiquement d\u00e9tect\u00e9." diff --git a/homeassistant/components/youless/translations/hu.json b/homeassistant/components/youless/translations/hu.json index 31913b7fa6f..415753be254 100644 --- a/homeassistant/components/youless/translations/hu.json +++ b/homeassistant/components/youless/translations/hu.json @@ -7,7 +7,7 @@ "user": { "data": { "host": "C\u00edm", - "name": "N\u00e9v" + "name": "Elnevez\u00e9s" } } } diff --git a/homeassistant/components/zha/translations/cs.json b/homeassistant/components/zha/translations/cs.json index 6a7d4aa17ea..de16e3bd387 100644 --- a/homeassistant/components/zha/translations/cs.json +++ b/homeassistant/components/zha/translations/cs.json @@ -35,7 +35,7 @@ "button_6": "\u0160est\u00e9 tla\u010d\u00edtko", "close": "Zav\u0159\u00edt", "dim_down": "ztmavit", - "dim_up": "ro\u017ehnout", + "dim_up": "rozjasnit", "face_1": "aktivov\u00e1no tv\u00e1\u0159\u00ed 1", "face_2": "aktivov\u00e1no tv\u00e1\u0159\u00ed 2", "face_3": "aktivov\u00e1no tv\u00e1\u0159\u00ed 3", diff --git a/homeassistant/components/zwave_js/translations/bg.json b/homeassistant/components/zwave_js/translations/bg.json index bdae86569ac..59fcf968740 100644 --- a/homeassistant/components/zwave_js/translations/bg.json +++ b/homeassistant/components/zwave_js/translations/bg.json @@ -17,6 +17,9 @@ "data": { "url": "URL" } + }, + "zeroconf_confirm": { + "title": "\u041e\u0442\u043a\u0440\u0438\u0442 Z-Wave JS \u0441\u044a\u0440\u0432\u044a\u0440" } } }, diff --git a/homeassistant/components/zwave_js/translations/ca.json b/homeassistant/components/zwave_js/translations/ca.json index 5e0f834ef19..6691894b79d 100644 --- a/homeassistant/components/zwave_js/translations/ca.json +++ b/homeassistant/components/zwave_js/translations/ca.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Vols configurar {name} amb el complement Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "Vols afegir el servidor Z-Wave JS amb ID {home_id} que es troba a {url} a Home Assistant?", + "title": "Servidor Z-Wave JS descobert" } } }, diff --git a/homeassistant/components/zwave_js/translations/de.json b/homeassistant/components/zwave_js/translations/de.json index 4900c9055d9..02418f44306 100644 --- a/homeassistant/components/zwave_js/translations/de.json +++ b/homeassistant/components/zwave_js/translations/de.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "M\u00f6chtest du {name} mit dem Z-Wave JS Add-on einrichten?" + }, + "zeroconf_confirm": { + "description": "M\u00f6chtest du den Z-Wave JS-Server mit der Home-ID {home_id} , gefunden unter {url} , zu Home Assistant hinzuf\u00fcgen?", + "title": "Z-Wave JS-Server entdeckt" } } }, diff --git a/homeassistant/components/zwave_js/translations/el.json b/homeassistant/components/zwave_js/translations/el.json index 2a4a519369a..66330206b60 100644 --- a/homeassistant/components/zwave_js/translations/el.json +++ b/homeassistant/components/zwave_js/translations/el.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} \u03bc\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Z-Wave JS;" + }, + "zeroconf_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Z-Wave JS Server \u03bc\u03b5 \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c3\u03c0\u03b9\u03c4\u03b9\u03bf\u03cd {home_id} \u03c0\u03bf\u03c5 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf {url} \u03c3\u03c4\u03bf Home Assistant;", + "title": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 Z-Wave JS" } } }, diff --git a/homeassistant/components/zwave_js/translations/en.json b/homeassistant/components/zwave_js/translations/en.json index 843f2aaf284..fa9794ed847 100644 --- a/homeassistant/components/zwave_js/translations/en.json +++ b/homeassistant/components/zwave_js/translations/en.json @@ -26,6 +26,7 @@ "step": { "configure_addon": { "data": { + "network_key": "Network Key", "s0_legacy_key": "S0 Key (Legacy)", "s2_access_control_key": "S2 Access Control Key", "s2_authenticated_key": "S2 Authenticated Key", @@ -116,6 +117,7 @@ "data": { "emulate_hardware": "Emulate Hardware", "log_level": "Log level", + "network_key": "Network Key", "s0_legacy_key": "S0 Key (Legacy)", "s2_access_control_key": "S2 Access Control Key", "s2_authenticated_key": "S2 Authenticated Key", @@ -144,5 +146,6 @@ "title": "The Z-Wave JS add-on is starting." } } - } + }, + "title": "Z-Wave JS" } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/et.json b/homeassistant/components/zwave_js/translations/et.json index c6c5db1ebd7..29f50ccb290 100644 --- a/homeassistant/components/zwave_js/translations/et.json +++ b/homeassistant/components/zwave_js/translations/et.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Kas seadistada Z-Wave JS lisandmoodul {name}?" + }, + "zeroconf_confirm": { + "description": "Kas lisada Z-Wave JS-server, mille ID {home_id} on leitud aadressil {url}, Home Assistant'ile?", + "title": "Avastatud Z-Wave JS Server" } } }, diff --git a/homeassistant/components/zwave_js/translations/fr.json b/homeassistant/components/zwave_js/translations/fr.json index 2645e573b6d..22e8ed19e32 100644 --- a/homeassistant/components/zwave_js/translations/fr.json +++ b/homeassistant/components/zwave_js/translations/fr.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Voulez-vous configurer {name} avec le plugin Z-Wave JS ?" + }, + "zeroconf_confirm": { + "description": "Voulez-vous ajouter le serveur Z-Wave\u00a0JS portant l'ID {home_id} et trouv\u00e9 sur {url} \u00e0 Home Assistant\u00a0?", + "title": "Serveur Z-Wave\u00a0JS d\u00e9couvert" } } }, diff --git a/homeassistant/components/zwave_js/translations/hu.json b/homeassistant/components/zwave_js/translations/hu.json index 1803cfa4a26..07dbb93b703 100644 --- a/homeassistant/components/zwave_js/translations/hu.json +++ b/homeassistant/components/zwave_js/translations/hu.json @@ -7,7 +7,7 @@ "addon_set_config_failed": "Nem siker\u00fclt be\u00e1ll\u00edtani a Z-Wave JS konfigur\u00e1ci\u00f3t.", "addon_start_failed": "Nem siker\u00fclt elind\u00edtani a Z-Wave JS b\u0151v\u00edtm\u00e9nyt.", "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "discovery_requires_supervisor": "A felfedez\u00e9shez a fel\u00fcgyel\u0151re van sz\u00fcks\u00e9g.", "not_zwave_device": "A felfedezett eszk\u00f6z nem Z-Wave eszk\u00f6z." @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {name} alkalmaz\u00e1st a Z-Wave JS b\u0151v\u00edtm\u00e9nnyel?" + }, + "zeroconf_confirm": { + "description": "Szeretn\u00e9 hozz\u00e1adni a {url} c\u00edmen tal\u00e1lhat\u00f3 {home_id} azonos\u00edt\u00f3val rendelkez\u0151 Z-Wave JS szervert a Home Assistanthoz?", + "title": "Felfedezett Z-Wave JS szerver" } } }, diff --git a/homeassistant/components/zwave_js/translations/id.json b/homeassistant/components/zwave_js/translations/id.json index 7ffb9d7b713..ef14a550210 100644 --- a/homeassistant/components/zwave_js/translations/id.json +++ b/homeassistant/components/zwave_js/translations/id.json @@ -10,7 +10,7 @@ "already_in_progress": "Alur konfigurasi sedang berlangsung", "cannot_connect": "Gagal terhubung", "discovery_requires_supervisor": "Fitur penemuan membutuhkan supervisor.", - "not_zwave_device": "Perangkat yang ditemukan bukanperangkat Z-Wave." + "not_zwave_device": "Perangkat yang ditemukan bukan perangkat Z-Wave." }, "error": { "addon_start_failed": "Gagal memulai add-on Z-Wave JS. Periksa konfigurasi.", @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Ingin menyiapkan {name} dengan add-on Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "Apakah Anda ingin menambahkan Server Z-Wave JS dengan ID rumah {home_id} di {url} ke Home Assistant?", + "title": "Server Z-Wave JS yang Ditemukan" } } }, diff --git a/homeassistant/components/zwave_js/translations/it.json b/homeassistant/components/zwave_js/translations/it.json index d455b7befe6..5922601921a 100644 --- a/homeassistant/components/zwave_js/translations/it.json +++ b/homeassistant/components/zwave_js/translations/it.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Vuoi configurare {name} con il componente aggiuntivo JS Z-Wave?" + }, + "zeroconf_confirm": { + "description": "Vuoi aggiungere il server Z-Wave JS con l'ID casa {home_id} trovato in {url} a Home Assistant?", + "title": "Server JS Z-Wave rilevato" } } }, diff --git a/homeassistant/components/zwave_js/translations/ja.json b/homeassistant/components/zwave_js/translations/ja.json index c85025c1394..6df591dd0ab 100644 --- a/homeassistant/components/zwave_js/translations/ja.json +++ b/homeassistant/components/zwave_js/translations/ja.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Z-Wave JS\u30a2\u30c9\u30aa\u30f3\u3067 {name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" + }, + "zeroconf_confirm": { + "description": "{url} \u306b\u3042\u308b\u30db\u30fc\u30e0ID {home_id} \u306eZ-Wave JS\u30b5\u30fc\u30d0\u30fc\u3092Home Assistant\u306b\u8ffd\u52a0\u3057\u307e\u3059\u304b\uff1f", + "title": "Z-Wave JS\u30b5\u30fc\u30d0\u30fc\u3092\u767a\u898b" } } }, diff --git a/homeassistant/components/zwave_js/translations/nl.json b/homeassistant/components/zwave_js/translations/nl.json index 1f99373c18f..2d3ce5421ed 100644 --- a/homeassistant/components/zwave_js/translations/nl.json +++ b/homeassistant/components/zwave_js/translations/nl.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Wilt u {name} instellen met de Z-Wave JS add-on?" + }, + "zeroconf_confirm": { + "description": "Wilt u de Z-Wave JS Server met home ID {home_id} gevonden op {url} toevoegen aan Home Assistant?", + "title": "Ontdekt Z-Wave JS Server" } } }, diff --git a/homeassistant/components/zwave_js/translations/no.json b/homeassistant/components/zwave_js/translations/no.json index 24efdf1c573..854bd307abc 100644 --- a/homeassistant/components/zwave_js/translations/no.json +++ b/homeassistant/components/zwave_js/translations/no.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Vil du konfigurere {name} med Z-Wave JS-tillegget?" + }, + "zeroconf_confirm": { + "description": "Vil du legge til Z-Wave JS Server med hjemme-ID {home_id} funnet p\u00e5 {url} til Home Assistant?", + "title": "Oppdaget Z-Wave JS Server" } } }, diff --git a/homeassistant/components/zwave_js/translations/pl.json b/homeassistant/components/zwave_js/translations/pl.json index 68dd6555552..2edb9fd8c1f 100644 --- a/homeassistant/components/zwave_js/translations/pl.json +++ b/homeassistant/components/zwave_js/translations/pl.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Czy chcesz skonfigurowa\u0107 {name} z dodatkiem Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "Czy chcesz doda\u0107 do Home Assistanta serwer Z-Wave JS z identyfikatorem {home_id} znalezionym pod {url}?", + "title": "Wykryty serwer Z-Wave JS" } } }, diff --git a/homeassistant/components/zwave_js/translations/pt-BR.json b/homeassistant/components/zwave_js/translations/pt-BR.json index b8fadd65089..5e1e8610fc7 100644 --- a/homeassistant/components/zwave_js/translations/pt-BR.json +++ b/homeassistant/components/zwave_js/translations/pt-BR.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "Deseja configurar o {name} com o add-on Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "Deseja adicionar o servidor Z-Wave JS com o ID inicial {home_id} encontrado em {url} ao Home Assistant?", + "title": "Servidor Z-Wave JS descoberto" } } }, diff --git a/homeassistant/components/zwave_js/translations/ru.json b/homeassistant/components/zwave_js/translations/ru.json index bc1d3e2cbd4..14011c78517 100644 --- a/homeassistant/components/zwave_js/translations/ru.json +++ b/homeassistant/components/zwave_js/translations/ru.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "\u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Z-Wave JS \u0441 home ID {home_id}, \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u043c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 {url}?", + "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d \u0441\u0435\u0440\u0432\u0435\u0440 Z-Wave JS" } } }, diff --git a/homeassistant/components/zwave_js/translations/tr.json b/homeassistant/components/zwave_js/translations/tr.json index efaa87cc48d..a3b5f6fb9f4 100644 --- a/homeassistant/components/zwave_js/translations/tr.json +++ b/homeassistant/components/zwave_js/translations/tr.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "{name} Z-Wave JS eklentisiyle kurmak istiyor musunuz?" + }, + "zeroconf_confirm": { + "description": "{url} adresinde bulunan {home_id} ev kimli\u011fine sahip Z-Wave JS Sunucusunu Home Assistant'a eklemek istiyor musunuz?", + "title": "Ke\u015ffedilen Z-Wave JS Sunucusu" } } }, diff --git a/homeassistant/components/zwave_js/translations/zh-Hant.json b/homeassistant/components/zwave_js/translations/zh-Hant.json index 664cd6b48d9..2ea728a591e 100644 --- a/homeassistant/components/zwave_js/translations/zh-Hant.json +++ b/homeassistant/components/zwave_js/translations/zh-Hant.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u540d\u70ba {name} \u7684 Z-Wave JS \u9644\u52a0\u5143\u4ef6\uff1f" + }, + "zeroconf_confirm": { + "description": "\u662f\u5426\u8981\u65b0\u589e\u65bc {url} \u6240\u627e\u5230\u4e4b ID \u70ba {home_id} \u4e4b Z-Wave JS \u4f3a\u670d\u5668\u81f3 Home Assistant\uff1f", + "title": "\u6240\u767c\u73fe\u7684 Z-Wave JS \u4f3a\u670d\u5668" } } }, diff --git a/homeassistant/components/zwave_me/translations/ca.json b/homeassistant/components/zwave_me/translations/ca.json index a382cef5494..abd616b1ed5 100644 --- a/homeassistant/components/zwave_me/translations/ca.json +++ b/homeassistant/components/zwave_me/translations/ca.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token d'API", "url": "URL" }, - "description": "Introdueix l'adre\u00e7a IP del servidor Z-Way i el 'token' d'acc\u00e9s Z-Way. Si s'utilitza HTTPS en lloc d'HTTP, l'adre\u00e7a IP es pot prefixar amb wss://. Per obtenir el 'token', v\u00e9s a la interf\u00edcie d'usuari de Z-Way > Men\u00fa > Configuraci\u00f3 > Usuari > Token API. Es recomana crear un nou usuari de Home Assistant i concedir-li acc\u00e9s als dispositius que necessitis controlar des de Home Assistant. Tamb\u00e9 \u00e9s possible utilitzar l'acc\u00e9s remot a trav\u00e9s de find.z-wave.me per connectar amb un Z-Way remot. Introdueix wss://find.z-wave.me al camp d'IP i copia el 'token' d'\u00e0mbit global (inicia sessi\u00f3 a Z-Way mitjan\u00e7ant find.z-wave.me)." + "description": "Introdueix l'adre\u00e7a IP del servidor Z-Way i el 'token' d'acc\u00e9s Z-Way. Per obtenir el 'token', v\u00e9s a la interf\u00edcie d'usuari de Z-Way Smart Home UI > Men\u00fa > Configuraci\u00f3 > Usuaris > Administrador > Token API.\n\nExemple de connexi\u00f3 a Z-Way en una xarxa local:\nURL: {local_url}\nToken: {local_token}\n\nExemple de connexi\u00f3 a Z-Way a trav\u00e9s de l'acc\u00e9s remot find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nExemple de connexi\u00f3 a Z-Way amb una adre\u00e7a IP p\u00fablica:\nURL: {remote_url}\nToken: {local_token}\n\nSi et connectes a trav\u00e9s de find.z-wave.me, has d'utilitzar un 'token' d'abast global (inicia sessi\u00f3 a Z-Way via find.z-wave.me per fer-ho)." } } } diff --git a/homeassistant/components/zwave_me/translations/de.json b/homeassistant/components/zwave_me/translations/de.json index 5afd788a3fb..747ccf0c9c8 100644 --- a/homeassistant/components/zwave_me/translations/de.json +++ b/homeassistant/components/zwave_me/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits eingerichtet", + "already_configured": "Ger\u00e4t ist bereits konfiguriert", "no_valid_uuid_set": "Keine g\u00fcltige UUID gesetzt" }, "error": { @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API-Token", "url": "URL" }, - "description": "Gib die IP-Adresse des Z-Way-Servers und das Z-Way-Zugangs-Token ein. Der IP-Adresse kann wss:// vorangestellt werden, wenn HTTPS anstelle von HTTP verwendet werden soll. Um das Token zu erhalten, gehe auf Z-Way-Benutzeroberfl\u00e4che > Men\u00fc > Einstellungen > Benutzer > API-Token. Es wird empfohlen, einen neuen Benutzer f\u00fcr Home Assistant zu erstellen und den Ger\u00e4ten, die du \u00fcber den Home Assistant steuern m\u00f6chtest, Zugriff zu gew\u00e4hren. Es ist auch m\u00f6glich, den Fernzugriff \u00fcber find.z-wave.me zu nutzen, um ein entferntes Z-Way zu verbinden. Gib wss://find.z-wave.me in das IP-Feld ein und kopiere das Token mit globalem Geltungsbereich (logge dich dazu \u00fcber find.z-wave.me bei Z-Way ein)." + "description": "Gib die IP-Adresse mit Port und Zugangs-Token des Z-Way-Servers ein. Um das Token zu erhalten, gehe zur Z-Way-Benutzeroberfl\u00e4che Smart Home UI > Men\u00fc > Einstellungen > Benutzer > Administrator > API-Token.\n\nBeispiel f\u00fcr die Verbindung zu Z-Way im lokalen Netzwerk:\nURL: {local_url}\nToken: {local_token}\n\nBeispiel f\u00fcr die Verbindung zu Z-Way \u00fcber den Fernzugriff find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nBeispiel f\u00fcr eine Verbindung zu Z-Way mit einer statischen \u00f6ffentlichen IP-Adresse:\nURL: {remote_url}\nToken: {local_token}\n\nWenn du dich \u00fcber find.z-wave.me verbindest, musst du ein Token mit globalem Geltungsbereich verwenden (logge dich dazu \u00fcber find.z-wave.me bei Z-Way ein)." } } } diff --git a/homeassistant/components/zwave_me/translations/en.json b/homeassistant/components/zwave_me/translations/en.json index 81d09d5c350..0ad88764806 100644 --- a/homeassistant/components/zwave_me/translations/en.json +++ b/homeassistant/components/zwave_me/translations/en.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API Token", "url": "URL" }, - "description": "Input IP address of Z-Way server and Z-Way access token. IP address can be prefixed with wss:// if HTTPS should be used instead of HTTP. To get the token go to the Z-Way user interface > Menu > Settings > User > API token. It is suggested to create a new user for Home Assistant and grant access to devices you need to control from Home Assistant. It is also possible to use remote access via find.z-wave.me to connect a remote Z-Way. Input wss://find.z-wave.me in IP field and copy the token with Global scope (log-in to Z-Way via find.z-wave.me for this)." + "description": "Input IP address with port and access token of Z-Way server. To get the token go to the Z-Way user interface Smart Home UI > Menu > Settings > Users > Administrator > API token.\n\nExample of connecting to Z-Way in the local network:\nURL: {local_url}\nToken: {local_token}\n\nExample of connecting to Z-Way via remote access find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nExample of connecting to Z-Way with a static public IP address:\nURL: {remote_url}\nToken: {local_token}\n\nWhen connecting via find.z-wave.me you need to use a token with a global scope (log-in to Z-Way via find.z-wave.me for this)." } } } diff --git a/homeassistant/components/zwave_me/translations/et.json b/homeassistant/components/zwave_me/translations/et.json index c07b4b2b5f8..1969e86fc4a 100644 --- a/homeassistant/components/zwave_me/translations/et.json +++ b/homeassistant/components/zwave_me/translations/et.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API v\u00f5ti", "url": "URL" }, - "description": "Sisesta Z-Way serveri IP-aadress ja Z-Way juurdep\u00e4\u00e4suluba. Kui HTTP asemel tuleks kasutada HTTPS-i, v\u00f5ib IP-aadressile lisada eesliite wss://. Tokeni hankimiseks mine Z-Way kasutajaliidesesse > Men\u00fc\u00fc > Seaded > Kasutaja > API tunnus. Soovitatav on luua Home Assistantile uus kasutaja ja anda juurdep\u00e4\u00e4s seadmetele mida pead Home Assistandi abil juhtima. Kaug-Z-Way \u00fchendamiseks on v\u00f5imalik kasutada ka kaugjuurdep\u00e4\u00e4su l\u00e4bi find.z-wave.me. Sisesta IP v\u00e4ljale wss://find.z-wave.me ja kopeeri globaalse ulatusega luba (selleks logi Z-Waysse sisse saidi find.z-wave.me kaudu)." + "description": "Sisesta IP-aadress koos pordi ja Z-Way serveri juurdep\u00e4\u00e4suv\u00f5tmega. Tokeni hankimiseks ava Z-Way kasutajaliides Smart Home UI > Men\u00fc\u00fc > Seaded > Kasutajad > Administraator > API luba. \n\n N\u00e4ide kohalikus v\u00f5rgus Z-Wayga \u00fchenduse loomisest:\n URL: {local_url}\n Token: {local_token} \n\n N\u00e4ide Z-Wayga \u00fchenduse loomisest kaugjuurdep\u00e4\u00e4su kaudu find.z-wave.me:\n URL: {find_url}\n Token: {find_token} \n\n N\u00e4ide Z-Wayga \u00fchenduse loomisest staatilise avaliku IP-aadressiga:\n URL: {remote_url}\n Token: {local_token} \n\n Kui \u00fchendud saidi find.z-wave.me kaudu, pead kasutama globaalse ulatusega luba (selleks logi Z-Waysse sisse saidi find.z-wave.me kaudu)." } } } diff --git a/homeassistant/components/zwave_me/translations/fr.json b/homeassistant/components/zwave_me/translations/fr.json index 1705796cf77..b53964d96ff 100644 --- a/homeassistant/components/zwave_me/translations/fr.json +++ b/homeassistant/components/zwave_me/translations/fr.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Jeton", + "token": "Jeton d'API", "url": "URL" }, - "description": "Entrez l'adresse IP du serveur Z-Way et le jeton d'acc\u00e8s Z-Way. L'adresse IP peut \u00eatre pr\u00e9c\u00e9d\u00e9e de wss:// si HTTPS doit \u00eatre utilis\u00e9 \u00e0 la place de HTTP. Pour obtenir le jeton, acc\u00e9dez \u00e0 l'interface utilisateur Z-Way > Menu > Param\u00e8tres > Utilisateur > Jeton API. Il est sugg\u00e9r\u00e9 de cr\u00e9er un nouvel utilisateur pour Home Assistant et d'accorder l'acc\u00e8s aux appareils que vous devez contr\u00f4ler \u00e0 partir de Home Assistant. Il est \u00e9galement possible d'utiliser l'acc\u00e8s \u00e0 distance via find.z-wave.me pour connecter un Z-Way distant. Entrez wss://find.z-wave.me dans le champ IP et copiez le jeton avec la port\u00e9e globale (connectez-vous \u00e0 Z-Way via find.z-wave.me pour cela)." + "description": "Saisissez l'adresse IP avec le port ainsi que le jeton d'acc\u00e8s au serveur Z-Way. Pour obtenir le jeton, acc\u00e9dez \u00e0 l'interface utilisateur Z-Way Smart Home UI > Menu > Param\u00e8tres > Utilisateurs > Administrateur > jeton d'API.\n\nExemple de connexion \u00e0 Z-Way sur le r\u00e9seau local\u00a0:\nURL\u00a0: {local_url}\nJeton\u00a0: {local_token}\n\nExemple de connexion \u00e0 Z-Way via l'acc\u00e8s \u00e0 distance find.z-wave.me\u00a0:\nURL\u00a0: {find_url}\nJeton\u00a0: {find_token}\n\nExemple de connexion \u00e0 Z-Way avec une adresse IP publique statique\u00a0:\nURL\u00a0: {remote_url}\nJeton\u00a0: {local_token}\n\nLorsque vous vous connectez via find.z-wave.me, vous devez utiliser un jeton avec une port\u00e9e globale (pour cela, connectez-vous \u00e0 Z-Way via find.z-wave.me)." } } } diff --git a/homeassistant/components/zwave_me/translations/he.json b/homeassistant/components/zwave_me/translations/he.json index 5689db627e2..ad8b69eaee6 100644 --- a/homeassistant/components/zwave_me/translations/he.json +++ b/homeassistant/components/zwave_me/translations/he.json @@ -6,7 +6,7 @@ "step": { "user": { "data": { - "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df", + "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API", "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8" } } diff --git a/homeassistant/components/zwave_me/translations/hu.json b/homeassistant/components/zwave_me/translations/hu.json index 679604d6cfa..21ed9026feb 100644 --- a/homeassistant/components/zwave_me/translations/hu.json +++ b/homeassistant/components/zwave_me/translations/hu.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API Token", "url": "URL" }, - "description": "Adja meg a Z-Way szerver IP-c\u00edm\u00e9t \u00e9s a Z-Way hozz\u00e1f\u00e9r\u00e9si tokent. Az IP-c\u00edm el\u00e9 wss:// el\u0151tagot lehet tenni, ha HTTP helyett HTTPS-t kell haszn\u00e1lni. A token megszerz\u00e9s\u00e9hez l\u00e9pjen a Z-Way felhaszn\u00e1l\u00f3i fel\u00fcletre > Men\u00fc > Be\u00e1ll\u00edt\u00e1sok > Felhaszn\u00e1l\u00f3 > API token men\u00fcpontba. Javasoljuk, hogy hozzon l\u00e9tre egy \u00faj felhaszn\u00e1l\u00f3t a Home Assistanthoz, \u00e9s adjon hozz\u00e1f\u00e9r\u00e9st azoknak az eszk\u00f6z\u00f6knek, amelyeket a Home Assistantb\u00f3l kell vez\u00e9relnie. A t\u00e1voli Z-Way csatlakoztat\u00e1s\u00e1hoz a find.z-wave.me oldalon kereszt\u00fcl t\u00e1voli hozz\u00e1f\u00e9r\u00e9st is haszn\u00e1lhat. \u00cdrja be az IP mez\u0151be a wss://find.z-wave.me c\u00edmet, \u00e9s m\u00e1solja be a tokent glob\u00e1lis hat\u00f3k\u00f6rrel (ehhez jelentkezzen be a Z-Way-be a find.z-wave.me oldalon kereszt\u00fcl)." + "description": "Adja meg a Z-Way szerver IP-c\u00edm\u00e9t, portj\u00e1t \u00e9s hozz\u00e1f\u00e9r\u00e9si jel\u00e9t. A token megszerz\u00e9s\u00e9hez n\u00e9zze meg a Z-Way felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n a Smart Home UI > Menu > Settings > Users > Administrator > API token men\u00fcpontot.\n\nP\u00e9lda a Z-Wayhez val\u00f3 csatlakoz\u00e1sra a helyi h\u00e1l\u00f3zaton:\nURL: {local_url}\nToken: {local_token}\n\nP\u00e9lda a Z-Wayhez t\u00e1voli el\u00e9r\u00e9ssel t\u00f6rt\u00e9n\u0151 csatlakoz\u00e1sra find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nP\u00e9lda a Z-Wayhez val\u00f3 csatlakoz\u00e1sra statikus nyilv\u00e1nos IP-c\u00edmmel:\nURL: {remote_url}\nToken: {local_token}\n\nA find.z-wave.me oldalon kereszt\u00fcl t\u00f6rt\u00e9n\u0151 csatlakoz\u00e1skor glob\u00e1lis hat\u00f3k\u00f6r\u0171 tokent kell haszn\u00e1lnia (ehhez a find.z-wave.me oldalon kereszt\u00fcl kell bejelentkeznie a Z-Way-be)." } } } diff --git a/homeassistant/components/zwave_me/translations/id.json b/homeassistant/components/zwave_me/translations/id.json index da9ddeb6d67..7e934a6c258 100644 --- a/homeassistant/components/zwave_me/translations/id.json +++ b/homeassistant/components/zwave_me/translations/id.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token API", "url": "URL" }, - "description": "Masukkan alamat IP server Z-Way dan token akses Z-Way. Alamat IP dapat diawali dengan wss:// jika HTTPS harus digunakan sebagai pengganti HTTP. Untuk mendapatkan token, buka antarmuka pengguna Z-Way > Menu > Pengaturan > Token API > Pengguna. Disarankan untuk membuat pengguna baru untuk Home Assistant dan memberikan akses ke perangkat yang perlu Anda kontrol dari Home Assistant. Dimungkinkan juga untuk menggunakan akses jarak jauh melalui find.z-wave.me untuk menghubungkan Z-Way jarak jauh. Masukkan wss://find.z-wave.me di bidang IP dan salin token dengan cakupan Global (masuk ke Z-Way melalui find.z-wave.me untuk ini)." + "description": "Masukkan alamat IP dengan port dan token akses server Z-Way. Untuk mendapatkan token, buka antarmuka pengguna Z-Way Smart Home UI > Menu > Settings > Users > Administrator > API token.\n\nContoh menghubungkan ke Z-Way di jaringan lokal:\nURL: {local_url}\nToken: {local_token}\n\nContoh menghubungkan ke Z-Way melalui akses jarak jauh find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nContoh menghubungkan ke Z-Way dengan alamat IP publik statis:\nURL: {remote_url}\nToken: {local_token}\n\nSaat terhubung melalui find.z-wave.me Anda perlu menggunakan token dengan cakupan global (masuk ke Z-Way melalui find.z-wave.me untuk hal ini)." } } } diff --git a/homeassistant/components/zwave_me/translations/it.json b/homeassistant/components/zwave_me/translations/it.json index d60ac60d899..048312af73e 100644 --- a/homeassistant/components/zwave_me/translations/it.json +++ b/homeassistant/components/zwave_me/translations/it.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token API", "url": "URL" }, - "description": "Digita l'indirizzo IP del server Z-Way e il token di accesso Z-Way. L'indirizzo IP pu\u00f2 essere preceduto da wss:// se si deve utilizzare HTTPS invece di HTTP. Per ottenere il token, vai all'interfaccia utente di Z-Way > Menu > Impostazioni > Utente > Token API. Si consiglia di creare un nuovo utente per Home Assistant e concedere l'accesso ai dispositivi che devi controllare da Home Assistant. \u00c8 anche possibile utilizzare l'accesso remoto tramite find.z-wave.me per connettere uno Z-Way remoto. Inserisci wss://find.z-wave.me nel campo IP e copia il token con ambito globale (accedi a Z-Way tramite find.z-wave.me per questo)." + "description": "Digita l'indirizzo IP con la porta e il token di accesso del server Z-Way. Per ottenere il token, vai all'interfaccia utente di Z-Way Smart Home UI > Menu > Impostazioni > Utenti > Amministratore > API token. \n\nEsempio di connessione a Z-Way nella rete locale:\nURL: {local_url}\nToken: {local_token} \n\nEsempio di connessione a Z-Way tramite accesso remoto find.z-wave.me:\nURL: {find_url}\nToken: {find_token} \n\nEsempio di connessione a Z-Way con un indirizzo IP pubblico statico:\nURL: {remote_url}\nToken: {local_token} \n\nQuando ti connetti tramite find.z-wave.me devi usare un token con un ambito globale (accedi a Z-Way tramite find.z-wave.me per questo)." } } } diff --git a/homeassistant/components/zwave_me/translations/nl.json b/homeassistant/components/zwave_me/translations/nl.json index a2356ed1035..04692559e81 100644 --- a/homeassistant/components/zwave_me/translations/nl.json +++ b/homeassistant/components/zwave_me/translations/nl.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API-token", "url": "URL" }, - "description": "Voer het IP-adres van de Z-Way server en het Z-Way toegangstoken in. Het IP adres kan voorafgegaan worden door wss:// indien HTTPS gebruikt moet worden in plaats van HTTP. Om het token te verkrijgen gaat u naar de Z-Way gebruikersinterface > Menu > Instellingen > Gebruiker > API token. Het is aanbevolen om een nieuwe gebruiker voor Home Assistant aan te maken en toegang te verlenen aan apparaten die u wilt bedienen vanuit Home Assistant. Het is ook mogelijk om toegang op afstand te gebruiken via find.z-wave.me om een Z-Way op afstand te verbinden. Voer wss://find.z-wave.me in het IP veld in en kopieer het token met Global scope (log hiervoor in op Z-Way via find.z-wave.me)." + "description": "Voer IP adres met poort en toegangstoken van Z-Way server in. Om het token te verkrijgen gaat u naar de Z-Way gebruikersinterface Smart Home UI > Menu > Instellingen > Gebruikers > Beheerder > API token.\n\nVoorbeeld van verbinding met Z-Way in het lokale netwerk:\nURL: {local_url}\nToken: {local_token}\n\nVoorbeeld van verbinding maken met Z-Way via remote access find.z-wave.me:\nURL: {find_url}\nToken: {find_token}\n\nVoorbeeld van een verbinding met Z-Way met een statisch publiek IP-adres:\nURL: {remote_url}\nToken: {local_token}\n\nWanneer je verbinding maakt via find.z-wave.me moet je een token gebruiken met een globale scope (log hiervoor in op Z-Way via find.z-wave.me)." } } } diff --git a/homeassistant/components/zwave_me/translations/no.json b/homeassistant/components/zwave_me/translations/no.json index 8a9a6928e93..266f74fdb61 100644 --- a/homeassistant/components/zwave_me/translations/no.json +++ b/homeassistant/components/zwave_me/translations/no.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "API-token", "url": "URL" }, - "description": "Skriv inn IP-adressen til Z-Way-serveren og Z-Way-tilgangstoken. IP-adressen kan settes foran med wss:// hvis HTTPS skal brukes i stedet for HTTP. For \u00e5 f\u00e5 tokenet, g\u00e5 til Z-Way-brukergrensesnittet > Meny > Innstillinger > Bruker > API-token. Det foresl\u00e5s \u00e5 opprette en ny bruker for Home Assistant og gi tilgang til enheter du m\u00e5 kontrollere fra Home Assistant. Det er ogs\u00e5 mulig \u00e5 bruke ekstern tilgang via find.z-wave.me for \u00e5 koble til en ekstern Z-Way. Skriv inn wss://find.z-wave.me i IP-feltet og kopier token med Global scope (logg inn p\u00e5 Z-Way via find.z-wave.me for dette)." + "description": "Skriv inn IP-adresse med port og tilgangstoken til Z-Way-serveren. For \u00e5 f\u00e5 tokenet, g\u00e5 til Z-Way-brukergrensesnittet Smart Home UI > Meny > Innstillinger > Brukere > Administrator > API-token. \n\n Eksempel p\u00e5 tilkobling til Z-Way i det lokale nettverket:\n URL: {local_url}\n Token: {local_token} \n\n Eksempel p\u00e5 tilkobling til Z-Way via fjerntilgang find.z-wave.me:\n URL: {find_url}\n Token: {find_token} \n\n Eksempel p\u00e5 tilkobling til Z-Way med en statisk offentlig IP-adresse:\n URL: {remote_url}\n Token: {local_token} \n\n N\u00e5r du kobler til via find.z-wave.me m\u00e5 du bruke en token med et globalt omfang (logg inn p\u00e5 Z-Way via find.z-wave.me for dette)." } } } diff --git a/homeassistant/components/zwave_me/translations/pl.json b/homeassistant/components/zwave_me/translations/pl.json index d75251367cd..bc9d43bed0d 100644 --- a/homeassistant/components/zwave_me/translations/pl.json +++ b/homeassistant/components/zwave_me/translations/pl.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token API", "url": "URL" }, - "description": "Wprowad\u017a adres IP serwera Z-Way i token dost\u0119pu Z-Way. Adres IP mo\u017ce by\u0107 poprzedzony wss://, je\u015bli zamiast HTTP powinien by\u0107 u\u017cywany HTTPS. Aby uzyska\u0107 token, przejd\u017a do interfejsu u\u017cytkownika Z-Way > Menu > Ustawienia > U\u017cytkownik > Token API. Sugeruje si\u0119 utworzenie nowego u\u017cytkownika Home Assistant i przyznanie dost\u0119pu do urz\u0105dze\u0144, kt\u00f3rymi chcesz sterowa\u0107 z Home Assistant. Mo\u017cliwe jest r\u00f3wnie\u017c u\u017cycie zdalnego dost\u0119pu za po\u015brednictwem find.z-wave.me, aby po\u0142\u0105czy\u0107 si\u0119 ze zdalnym Z-Way. Wpisz wss://find.z-wave.me w polu IP i skopiuj token z zakresem globalnym (w tym celu zaloguj si\u0119 do Z-Way przez find.z-wave.me)." + "description": "Wprowad\u017a adres IP z portem i tokenem dost\u0119pu serwera Z-Way. Aby uzyska\u0107 token, przejd\u017a do interfejsu u\u017cytkownika Z-Way Smart Home UI > Menu > Ustawienia > U\u017cytkownicy > Administrator > Token API. \n\nPrzyk\u0142ad po\u0142\u0105czenia z Z-Way w sieci lokalnej:\nURL: {local_url}\nToken: {local_token} \n\nPrzyk\u0142ad po\u0142\u0105czenia z Z-Way przez zdalny dost\u0119p find.z-wave.me:\nURL: {find_url}\nToken: {find_token} \n\nPrzyk\u0142ad po\u0142\u0105czenia z Z-Way za pomoc\u0105 statycznego publicznego adresu IP:\nURL: {remote_url}\nToken: {local_token} \n\n\u0141\u0105cz\u0105c si\u0119 przez find.z-wave.me, musisz u\u017cy\u0107 tokena o zasi\u0119gu globalnym (w tym celu zaloguj si\u0119 do Z-Way przez find.z-wave.me)." } } } diff --git a/homeassistant/components/zwave_me/translations/pt-BR.json b/homeassistant/components/zwave_me/translations/pt-BR.json index 4a7be43ddb7..23bdcd4ef9a 100644 --- a/homeassistant/components/zwave_me/translations/pt-BR.json +++ b/homeassistant/components/zwave_me/translations/pt-BR.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token da API", "url": "URL" }, - "description": "Insira o endere\u00e7o IP do servidor Z-Way e o token de acesso Z-Way. O endere\u00e7o IP pode ser prefixado com wss:// e o HTTPS deve ser usado em vez de HTTP. Para obter o token, v\u00e1 para a interface do usu\u00e1rio Z-Way > Menu > Configura\u00e7\u00f5es > Usu\u00e1rio > Token de API. Sugere-se criar um novo usu\u00e1rio para o Home Assistant e conceder acesso aos dispositivos que voc\u00ea quer controlar no Home Assistant. Tamb\u00e9m \u00e9 poss\u00edvel usar o acesso remoto via find.z-wave.me para conectar um Z-Way remoto. Insira wss://find.z-wave.me no campo IP e copie o token com escopo Global (fa\u00e7a login no Z-Way via find.z-wave.me para isso)." + "description": "Insira o endere\u00e7o IP com porta e token de acesso do servidor Z-Way. Para obter o token, v\u00e1 para a interface de usu\u00e1rio Z-Way Smart Home UI > Menu > Settings > Users > Administrator > API token. \n\n Exemplo de conex\u00e3o ao Z-Way na rede local:\n URL: {local_url}\n Token: {local_token} \n\n Exemplo de conex\u00e3o ao Z-Way via acesso remoto find.z-wave.me:\n URL: {find_url}\n Token: {find_token} \n\n Exemplo de conex\u00e3o ao Z-Way com um endere\u00e7o IP p\u00fablico est\u00e1tico:\n URL: {remote_url}\n Token: {local_token} \n\n Ao conectar via find.z-wave.me, voc\u00ea precisa usar um token com escopo global (fa\u00e7a login no Z-Way via find.z-wave.me para isso)." } } } diff --git a/homeassistant/components/zwave_me/translations/ru.json b/homeassistant/components/zwave_me/translations/ru.json index d24b2f7327a..f42a7fd20ca 100644 --- a/homeassistant/components/zwave_me/translations/ru.json +++ b/homeassistant/components/zwave_me/translations/ru.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "\u0422\u043e\u043a\u0435\u043d", + "token": "\u0422\u043e\u043a\u0435\u043d API", "url": "URL-\u0430\u0434\u0440\u0435\u0441" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Z-Way \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 Z-Way. \u0415\u0441\u043b\u0438 \u0432\u043c\u0435\u0441\u0442\u043e HTTP \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HTTPS, IP-\u0430\u0434\u0440\u0435\u0441 \u043d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0441 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c 'wss://'. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Z-Way > \u041c\u0435\u043d\u044e > \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 > \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c > \u0422\u043e\u043a\u0435\u043d API. \u0417\u0430\u0442\u0435\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f Home Assistant \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0412\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0437 Home Assistant. \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0447\u0435\u0440\u0435\u0437 find.z-wave.me \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e Z-Way. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 wss://find.z-wave.me \u0432 \u043f\u043e\u043b\u0435 IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0442\u043e\u043a\u0435\u043d \u0441 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f (\u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 Z-Way \u0447\u0435\u0440\u0435\u0437 find.z-wave.me)." + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 IP-\u0430\u0434\u0440\u0435\u0441, \u043f\u043e\u0440\u0442 \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 Z-Way. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Z-Way Smart Home UI > \u041c\u0435\u043d\u044e > \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 > \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 > \u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440 > \u0422\u043e\u043a\u0435\u043d API. \n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Z-Way \u0432 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438:\nURL-\u0430\u0434\u0440\u0435\u0441: {local_url}\n\u0422\u043e\u043a\u0435\u043d: {local_token} \n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Z-Way \u0447\u0435\u0440\u0435\u0437 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f find.z-wave.me:\nURL-\u0430\u0434\u0440\u0435\u0441: {find_url}\n\u0422\u043e\u043a\u0435\u043d: {find_token} \n\n\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a Z-Way \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u043c IP-\u0430\u0434\u0440\u0435\u0441\u043e\u043c:\nURL-\u0430\u0434\u0440\u0435\u0441: {remote_url}\n\u0422\u043e\u043a\u0435\u043d: {local_token} \n\n\u041f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 find.z-wave.me \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0441 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f (\u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0439\u0442\u0435\u0441\u044c \u0432 Z-Way \u0447\u0435\u0440\u0435\u0437 find.z-wave.me)." } } } diff --git a/homeassistant/components/zwave_me/translations/tr.json b/homeassistant/components/zwave_me/translations/tr.json index 39d25423fab..90edc6bf4ff 100644 --- a/homeassistant/components/zwave_me/translations/tr.json +++ b/homeassistant/components/zwave_me/translations/tr.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "Anahtar", + "token": "API Anahtar\u0131", "url": "URL" }, - "description": "Z-Way sunucusunun ve Z-Way eri\u015fim belirtecinin IP adresini girin. HTTP yerine HTTPS kullan\u0131lmas\u0131 gerekiyorsa, IP adresinin \u00f6n\u00fcne wss:// eklenebilir. Belirteci almak i\u00e7in Z-Way kullan\u0131c\u0131 aray\u00fcz\u00fc > Men\u00fc > Ayarlar > Kullan\u0131c\u0131 > API belirtecine gidin. Home Assistant i\u00e7in yeni bir kullan\u0131c\u0131 olu\u015fturman\u0131z ve Home Assistant'tan kontrol etmeniz gereken cihazlara eri\u015fim izni vermeniz \u00f6nerilir. Uzak bir Z-Way'i ba\u011flamak i\u00e7in find.z-wave.me arac\u0131l\u0131\u011f\u0131yla uzaktan eri\u015fimi kullanmak da m\u00fcmk\u00fcnd\u00fcr. IP alan\u0131na wss://find.z-wave.me yaz\u0131n ve belirteci Global kapsamla kopyalay\u0131n (bunun i\u00e7in find.z-wave.me arac\u0131l\u0131\u011f\u0131yla Z-Way'de oturum a\u00e7\u0131n)." + "description": "Z-Way sunucusunun ba\u011flant\u0131 noktas\u0131 ve eri\u015fim anahtar\u0131 ile IP adresini girin. Simgeyi almak i\u00e7in Z-Way kullan\u0131c\u0131 aray\u00fcz\u00fc Smart Home UI > Men\u00fc > Ayarlar > Kullan\u0131c\u0131lar > Y\u00f6netici > API anahtar\u0131na gidin. \n\n Yerel a\u011fda Z-Way'e ba\u011flanma \u00f6rne\u011fi:\n URL: {local_url}\n Belirte\u00e7: {local_token} \n\n Z-Way'e uzaktan eri\u015fim find.z-wave.me arac\u0131l\u0131\u011f\u0131yla ba\u011flanma \u00f6rne\u011fi:\n URL: {find_url}\n Belirte\u00e7: {find_token} \n\n Statik bir genel IP adresiyle Z-Way'e ba\u011flanma \u00f6rne\u011fi:\n URL: {remote_url}\n Belirte\u00e7: {local_token} \n\n find.z-wave.me arac\u0131l\u0131\u011f\u0131yla ba\u011flan\u0131rken, k\u00fcresel kapsaml\u0131 bir anahtar kullanman\u0131z gerekir (bunun i\u00e7in Z-Way'de find.z-wave.me arac\u0131l\u0131\u011f\u0131yla oturum a\u00e7\u0131n)." } } } diff --git a/homeassistant/components/zwave_me/translations/zh-Hant.json b/homeassistant/components/zwave_me/translations/zh-Hant.json index ee6eb1e9ae7..9c1178b4063 100644 --- a/homeassistant/components/zwave_me/translations/zh-Hant.json +++ b/homeassistant/components/zwave_me/translations/zh-Hant.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "token": "\u6b0a\u6756", + "token": "API \u6b0a\u6756", "url": "\u7db2\u5740" }, - "description": "\u8f38\u5165 Z-Way \u4f3a\u670d\u5668 IP \u4f4d\u5740\u8207 Z-Way \u5b58\u53d6\u6b0a\u6756\u3002\u5047\u5982\u4f7f\u7528 HTTPS \u800c\u975e HTTP\u3001IP \u4f4d\u5740\u524d\u7db4\u53ef\u80fd\u70ba wss://\u3002\u6b32\u53d6\u5f97\u6b0a\u6756\u3001\u8acb\u81f3 Z-Way \u4f7f\u7528\u8005\u4ecb\u9762 > \u9078\u55ae > \u8a2d\u5b9a > \u4f7f\u7528\u8005 > API \u6b0a\u6756\u7372\u5f97\u3002\u5efa\u8b70\u91dd\u5c0d Home Assistant \u65b0\u5275\u4f7f\u7528\u8005\u4e26\u7531 Home Assistant \u63a7\u5236\u53ef\u4ee5\u5b58\u53d6\u7684\u88dd\u7f6e\u3002\u53e6\u5916\u4e5f\u53ef\u4ee5\u900f\u904e find.z-wave.me \u9023\u7dda\u81f3\u9060\u7aef Z-Way \u4e26\u7372\u5f97\u9060\u7aef\u5b58\u53d6\u529f\u80fd\u3002\u65bc IP \u6b04\u4f4d\u8f38\u5165 wss://find.z-wave.me \u4e26\u7531 Global scope\uff08\u900f\u904e find.z-wave.me \u767b\u5165 Z-Way\uff09\u8907\u88fd\u6b0a\u6756\u3002" + "description": "\u8f38\u5165 Z-Way \u4f3a\u670d\u5668 IP \u4f4d\u5740\u901a\u4fe1\u57e0\u8207\u5b58\u53d6\u6b0a\u6756\u3002\u6b32\u53d6\u5f97\u6b0a\u6756\u3001\u8acb\u81f3 Z-Way \u4f7f\u7528\u8005 Smart Home \u4ecb\u9762 > \u9078\u55ae > \u8a2d\u5b9a > \u4f7f\u7528\u8005 > \u7ba1\u7406\u8005 > API \u6b0a\u6756\u7372\u5f97\u3002\n\n\u4ee5\u672c\u5730\u7aef\u9023\u7dda\u81f3 Z-Way \u7bc4\u4f8b\uff1a\nURL\uff1a{local_url}\n\u6b0a\u6756\uff1a{local_token}\n\n\u900f\u904e\u9060\u7aef\u5b58\u53d6 find.z-wave.me \u9023\u7dda\u81f3 Z-Way \u7bc4\u4f8b\uff1a\nURL\uff1a{find_url}\n\u6b0a\u6756\uff1a{find_token}\n\n\u4ee5\u975c\u614b\u516c\u773e IP \u4f4d\u5740\u9023\u7dda\u81f3 Z-Way \u7bc4\u4f8b\uff1a\nURL\uff1a{remote_url}\n\u6b0a\u6756\uff1a{local_token}\n\n\u7576\u900f\u904e find.z-wave.me \u9023\u7dda\u6642\u3001\u5c07\u9700\u8981\u4f7f\u7528\u4e00\u7d44\u5168\u7bc4\u570d\u91d1\u9470\uff08\u900f\u904e find.z-wave.me \u767b\u5165\u81f3 Z-Way \u4ee5\u53d6\u5f97\uff09\u3002" } } } From 37384f7eb3f99f08a335e5b5dd38fa23c0fa1097 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 28 Apr 2022 23:32:46 -0500 Subject: [PATCH 067/930] Bump sqlalchemy to 1.4.36 (#71039) --- homeassistant/components/recorder/manifest.json | 2 +- homeassistant/components/sql/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/manifest.json b/homeassistant/components/recorder/manifest.json index e0e4862c18c..0fb44f99ae2 100644 --- a/homeassistant/components/recorder/manifest.json +++ b/homeassistant/components/recorder/manifest.json @@ -2,7 +2,7 @@ "domain": "recorder", "name": "Recorder", "documentation": "https://www.home-assistant.io/integrations/recorder", - "requirements": ["sqlalchemy==1.4.35", "fnvhash==0.1.0", "lru-dict==1.1.7"], + "requirements": ["sqlalchemy==1.4.36", "fnvhash==0.1.0", "lru-dict==1.1.7"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", "iot_class": "local_push" diff --git a/homeassistant/components/sql/manifest.json b/homeassistant/components/sql/manifest.json index 272a5d1f685..c779e4567cd 100644 --- a/homeassistant/components/sql/manifest.json +++ b/homeassistant/components/sql/manifest.json @@ -2,7 +2,7 @@ "domain": "sql", "name": "SQL", "documentation": "https://www.home-assistant.io/integrations/sql", - "requirements": ["sqlalchemy==1.4.35"], + "requirements": ["sqlalchemy==1.4.36"], "codeowners": ["@dgomes", "@gjohansson-ST"], "config_flow": true, "iot_class": "local_polling" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 9d258ddbd4a..bf6ad12b287 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -29,7 +29,7 @@ pyudev==0.22.0 pyyaml==6.0 requests==2.27.1 scapy==2.4.5 -sqlalchemy==1.4.35 +sqlalchemy==1.4.36 typing-extensions>=3.10.0.2,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 diff --git a/requirements_all.txt b/requirements_all.txt index 7039bc0da94..5f0a54d77a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2208,7 +2208,7 @@ spotipy==2.19.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.35 +sqlalchemy==1.4.36 # homeassistant.components.srp_energy srpenergy==1.3.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7f4380e4220..c8470d51d4a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1444,7 +1444,7 @@ spotipy==2.19.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.35 +sqlalchemy==1.4.36 # homeassistant.components.srp_energy srpenergy==1.3.6 From c7d344692fbb7e63467501abe411d79c84658bce Mon Sep 17 00:00:00 2001 From: Raj Laud <50647620+rajlaud@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:39:58 -0400 Subject: [PATCH 068/930] Squeezebox grouping (#70962) --- .../components/squeezebox/media_player.py | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index a3cc2120751..bd1f29f4e69 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -238,6 +238,7 @@ class SqueezeBoxEntity(MediaPlayerEntity): | MediaPlayerEntityFeature.SHUFFLE_SET | MediaPlayerEntityFeature.CLEAR_PLAYLIST | MediaPlayerEntityFeature.STOP + | MediaPlayerEntityFeature.GROUPING ) def __init__(self, player): @@ -393,7 +394,7 @@ class SqueezeBoxEntity(MediaPlayerEntity): return self._player.shuffle == "song" @property - def sync_group(self): + def group_members(self): """List players we are synced with.""" player_ids = { p.unique_id: p.entity_id for p in self.hass.data[DOMAIN][KNOWN_PLAYERS] @@ -404,6 +405,11 @@ class SqueezeBoxEntity(MediaPlayerEntity): sync_group.append(player_ids[player]) return sync_group + @property + def sync_group(self): + """List players we are synced with. Deprecated.""" + return self.group_members + @property def query_result(self): """Return the result from the call_query service.""" @@ -560,9 +566,9 @@ class SqueezeBoxEntity(MediaPlayerEntity): self._query_result = await self._player.async_query(*all_params) _LOGGER.debug("call_query got result %s", self._query_result) - async def async_sync(self, other_player): + async def async_join_players(self, group_members): """ - Add another Squeezebox player to this player's sync group. + Add other Squeezebox players to this player's sync group. If the other player is a member of a sync group, it will leave the current sync group without asking. @@ -570,15 +576,33 @@ class SqueezeBoxEntity(MediaPlayerEntity): player_ids = { p.entity_id: p.unique_id for p in self.hass.data[DOMAIN][KNOWN_PLAYERS] } - if other_player_id := player_ids.get(other_player): - await self._player.async_sync(other_player_id) - else: - _LOGGER.info("Could not find player_id for %s. Not syncing", other_player) - async def async_unsync(self): + for other_player in group_members: + if other_player_id := player_ids.get(other_player): + await self._player.async_sync(other_player_id) + else: + _LOGGER.info( + "Could not find player_id for %s. Not syncing", other_player + ) + + async def async_sync(self, other_player): + """Sync this Squeezebox player to another. Deprecated.""" + _LOGGER.warning( + "Service squeezebox.sync is deprecated; use media_player.join_players instead" + ) + await self.async_join_players([other_player]) + + async def async_unjoin_player(self): """Unsync this Squeezebox player.""" await self._player.async_unsync() + async def async_unsync(self): + """Unsync this Squeezebox player. Deprecated.""" + _LOGGER.warning( + "Service squeezebox.unsync is deprecated; use media_player.unjoin_player instead" + ) + await self.async_unjoin_player() + async def async_browse_media(self, media_content_type=None, media_content_id=None): """Implement the websocket media browsing helper.""" _LOGGER.debug( From 2f9beb4a4a3bc4269a2c08801d6716756c11bdb3 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 29 Apr 2022 08:37:49 +0200 Subject: [PATCH 069/930] Deprecate white_value support in MQTT light (#71002) --- .../components/mqtt/light/schema_basic.py | 10 ++++++++++ homeassistant/components/mqtt/light/schema_json.py | 4 ++++ .../components/mqtt/light/schema_template.py | 14 ++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index b56c06a43e0..8ad553ddd74 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -221,12 +221,22 @@ _PLATFORM_SCHEMA_BASE = ( ) PLATFORM_SCHEMA_BASIC = vol.All( + # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_SCALE), + cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), _PLATFORM_SCHEMA_BASE, ) DISCOVERY_SCHEMA_BASIC = vol.All( # CONF_VALUE_TEMPLATE is no longer supported, support was removed in 2022.2 cv.removed(CONF_VALUE_TEMPLATE), + # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_SCALE), + cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 68cc7e8b36c..2433bdf1679 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -149,11 +149,15 @@ _PLATFORM_SCHEMA_BASE = ( ) PLATFORM_SCHEMA_JSON = vol.All( + # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE), _PLATFORM_SCHEMA_BASE, valid_color_configuration, ) DISCOVERY_SCHEMA_JSON = vol.All( + # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), valid_color_configuration, ) diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index 4f25bde928d..b82474db2a3 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -68,7 +68,7 @@ CONF_MIN_MIREDS = "min_mireds" CONF_RED_TEMPLATE = "red_template" CONF_WHITE_VALUE_TEMPLATE = "white_value_template" -PLATFORM_SCHEMA_TEMPLATE = ( +_PLATFORM_SCHEMA_BASE = ( mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( { vol.Optional(CONF_BLUE_TEMPLATE): cv.template, @@ -92,7 +92,17 @@ PLATFORM_SCHEMA_TEMPLATE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) -DISCOVERY_SCHEMA_TEMPLATE = PLATFORM_SCHEMA_TEMPLATE.extend({}, extra=vol.REMOVE_EXTRA) +PLATFORM_SCHEMA_TEMPLATE = vol.All( + # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), + _PLATFORM_SCHEMA_BASE, +) + +DISCOVERY_SCHEMA_TEMPLATE = vol.All( + # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), + _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), +) async def async_setup_entity_template( From a57136783d9b4cb14c52fbc63690487bb0c4e754 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 28 Apr 2022 23:45:57 -0700 Subject: [PATCH 070/930] Fix race causing google config pre-init access (#71042) --- homeassistant/components/cloud/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index ad34186b7df..c47544f9d99 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -118,14 +118,15 @@ class CloudClient(Interface): cloud_user = await self._prefs.get_cloud_user() - self._google_config = google_config.CloudGoogleConfig( + google_conf = google_config.CloudGoogleConfig( self._hass, self.google_user_config, cloud_user, self._prefs, self.cloud, ) - await self._google_config.async_initialize() + await google_conf.async_initialize() + self._google_config = google_conf return self._google_config From b9c7a89b47eabecef9775e2e71c65899b4b5af2d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 01:48:28 -0500 Subject: [PATCH 071/930] Prevent sqlalchemy Transparent SQL Compilation Caching from filling up during purge (#71015) --- homeassistant/components/recorder/purge.py | 261 +++++++++++++++++++-- 1 file changed, 246 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index e6f30b1c62d..d4061a69bab 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -3,12 +3,15 @@ from __future__ import annotations from collections.abc import Callable, Iterable from datetime import datetime +from itertools import zip_longest import logging from typing import TYPE_CHECKING -from sqlalchemy import column, func, select, union +from sqlalchemy import func, lambda_stmt, select, union_all from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import distinct +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select from homeassistant.const import EVENT_STATE_CHANGED @@ -112,6 +115,223 @@ def _select_event_state_and_attributes_ids_to_purge( return event_ids, state_ids, attributes_ids +def _state_attrs_exist(attr: int | None) -> Select: + """Check if a state attributes id exists in the states table.""" + return select(func.min(States.attributes_id)).where(States.attributes_id == attr) + + +def _generate_find_attr_lambda( + attr1: int, + attr2: int | None, + attr3: int | None, + attr4: int | None, + attr5: int | None, + attr6: int | None, + attr7: int | None, + attr8: int | None, + attr9: int | None, + attr10: int | None, + attr11: int | None, + attr12: int | None, + attr13: int | None, + attr14: int | None, + attr15: int | None, + attr16: int | None, + attr17: int | None, + attr18: int | None, + attr19: int | None, + attr20: int | None, + attr21: int | None, + attr22: int | None, + attr23: int | None, + attr24: int | None, + attr25: int | None, + attr26: int | None, + attr27: int | None, + attr28: int | None, + attr29: int | None, + attr30: int | None, + attr31: int | None, + attr32: int | None, + attr33: int | None, + attr34: int | None, + attr35: int | None, + attr36: int | None, + attr37: int | None, + attr38: int | None, + attr39: int | None, + attr40: int | None, + attr41: int | None, + attr42: int | None, + attr43: int | None, + attr44: int | None, + attr45: int | None, + attr46: int | None, + attr47: int | None, + attr48: int | None, + attr49: int | None, + attr50: int | None, + attr51: int | None, + attr52: int | None, + attr53: int | None, + attr54: int | None, + attr55: int | None, + attr56: int | None, + attr57: int | None, + attr58: int | None, + attr59: int | None, + attr60: int | None, + attr61: int | None, + attr62: int | None, + attr63: int | None, + attr64: int | None, + attr65: int | None, + attr66: int | None, + attr67: int | None, + attr68: int | None, + attr69: int | None, + attr70: int | None, + attr71: int | None, + attr72: int | None, + attr73: int | None, + attr74: int | None, + attr75: int | None, + attr76: int | None, + attr77: int | None, + attr78: int | None, + attr79: int | None, + attr80: int | None, + attr81: int | None, + attr82: int | None, + attr83: int | None, + attr84: int | None, + attr85: int | None, + attr86: int | None, + attr87: int | None, + attr88: int | None, + attr89: int | None, + attr90: int | None, + attr91: int | None, + attr92: int | None, + attr93: int | None, + attr94: int | None, + attr95: int | None, + attr96: int | None, + attr97: int | None, + attr98: int | None, + attr99: int | None, + attr100: int | None, +) -> StatementLambdaElement: + """Generate the find attributes select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _state_attrs_exist(attr1), + _state_attrs_exist(attr2), + _state_attrs_exist(attr3), + _state_attrs_exist(attr4), + _state_attrs_exist(attr5), + _state_attrs_exist(attr6), + _state_attrs_exist(attr7), + _state_attrs_exist(attr8), + _state_attrs_exist(attr9), + _state_attrs_exist(attr10), + _state_attrs_exist(attr11), + _state_attrs_exist(attr12), + _state_attrs_exist(attr13), + _state_attrs_exist(attr14), + _state_attrs_exist(attr15), + _state_attrs_exist(attr16), + _state_attrs_exist(attr17), + _state_attrs_exist(attr18), + _state_attrs_exist(attr19), + _state_attrs_exist(attr20), + _state_attrs_exist(attr21), + _state_attrs_exist(attr22), + _state_attrs_exist(attr23), + _state_attrs_exist(attr24), + _state_attrs_exist(attr25), + _state_attrs_exist(attr26), + _state_attrs_exist(attr27), + _state_attrs_exist(attr28), + _state_attrs_exist(attr29), + _state_attrs_exist(attr30), + _state_attrs_exist(attr31), + _state_attrs_exist(attr32), + _state_attrs_exist(attr33), + _state_attrs_exist(attr34), + _state_attrs_exist(attr35), + _state_attrs_exist(attr36), + _state_attrs_exist(attr37), + _state_attrs_exist(attr38), + _state_attrs_exist(attr39), + _state_attrs_exist(attr40), + _state_attrs_exist(attr41), + _state_attrs_exist(attr42), + _state_attrs_exist(attr43), + _state_attrs_exist(attr44), + _state_attrs_exist(attr45), + _state_attrs_exist(attr46), + _state_attrs_exist(attr47), + _state_attrs_exist(attr48), + _state_attrs_exist(attr49), + _state_attrs_exist(attr50), + _state_attrs_exist(attr51), + _state_attrs_exist(attr52), + _state_attrs_exist(attr53), + _state_attrs_exist(attr54), + _state_attrs_exist(attr55), + _state_attrs_exist(attr56), + _state_attrs_exist(attr57), + _state_attrs_exist(attr58), + _state_attrs_exist(attr59), + _state_attrs_exist(attr60), + _state_attrs_exist(attr61), + _state_attrs_exist(attr62), + _state_attrs_exist(attr63), + _state_attrs_exist(attr64), + _state_attrs_exist(attr65), + _state_attrs_exist(attr66), + _state_attrs_exist(attr67), + _state_attrs_exist(attr68), + _state_attrs_exist(attr69), + _state_attrs_exist(attr70), + _state_attrs_exist(attr71), + _state_attrs_exist(attr72), + _state_attrs_exist(attr73), + _state_attrs_exist(attr74), + _state_attrs_exist(attr75), + _state_attrs_exist(attr76), + _state_attrs_exist(attr77), + _state_attrs_exist(attr78), + _state_attrs_exist(attr79), + _state_attrs_exist(attr80), + _state_attrs_exist(attr81), + _state_attrs_exist(attr82), + _state_attrs_exist(attr83), + _state_attrs_exist(attr84), + _state_attrs_exist(attr85), + _state_attrs_exist(attr86), + _state_attrs_exist(attr87), + _state_attrs_exist(attr88), + _state_attrs_exist(attr89), + _state_attrs_exist(attr90), + _state_attrs_exist(attr91), + _state_attrs_exist(attr92), + _state_attrs_exist(attr93), + _state_attrs_exist(attr94), + _state_attrs_exist(attr95), + _state_attrs_exist(attr96), + _state_attrs_exist(attr97), + _state_attrs_exist(attr98), + _state_attrs_exist(attr99), + _state_attrs_exist(attr100), + ) + ) + + def _select_unused_attributes_ids( session: Session, attributes_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -132,9 +352,12 @@ def _select_unused_attributes_ids( # > explain select distinct attributes_id from states where attributes_id in (136723); # ...Using index # - id_query = session.query(distinct(States.attributes_id)).filter( - States.attributes_id.in_(attributes_ids) - ) + seen_ids = { + state[0] + for state in session.query(distinct(States.attributes_id)) + .filter(States.attributes_id.in_(attributes_ids)) + .all() + } else: # # This branch is for DBMS that cannot optimize the distinct query well and has to examine @@ -151,17 +374,25 @@ def _select_unused_attributes_ids( # > explain select min(attributes_id) from states where attributes_id = 136723; # ...Select tables optimized away # - id_query = session.query(column("id")).from_statement( - union( - *[ - select(func.min(States.attributes_id).label("id")).where( - States.attributes_id == attributes_id - ) - for attributes_id in attributes_ids - ] - ) - ) - to_remove = attributes_ids - {state[0] for state in id_query.all()} + # We used to generate a query based on how many attribute_ids to find but + # that meant sqlalchemy Transparent SQL Compilation Caching was working against + # us by cached up to MAX_ROWS_TO_PURGE different statements which could be + # up to 500MB for large database due to the complexity of the ORM objects. + # + # We now break the query into groups of 100 and use a lambda_stmt to ensure + # that the query is only cached once. + # + seen_ids = set() + groups = [iter(attributes_ids)] * 100 + for attr_ids in zip_longest(*groups, fillvalue=None): + seen_ids |= { + state[0] + for state in session.execute( + _generate_find_attr_lambda(*attr_ids) + ).all() + if state[0] is not None + } + to_remove = attributes_ids - seen_ids _LOGGER.debug( "Selected %s shared attributes to remove", len(to_remove), From aafe6ff0e295e870a4a0b344a6880c97109d31b3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 01:48:58 -0500 Subject: [PATCH 072/930] Fix history_stats for timezones with a positive offset from UTC (#71038) --- .../components/history_stats/data.py | 4 +- tests/components/history_stats/test_sensor.py | 81 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index c5d0ee0fb15..3f22f4cc32b 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -11,6 +11,8 @@ import homeassistant.util.dt as dt_util from .helpers import async_calculate_period, floored_timestamp +MIN_TIME_UTC = datetime.datetime.min.replace(tzinfo=dt_util.UTC) + @dataclass class HistoryStatsState: @@ -36,7 +38,7 @@ class HistoryStats: """Init the history stats manager.""" self.hass = hass self.entity_id = entity_id - self._period = (datetime.datetime.min, datetime.datetime.min) + self._period = (MIN_TIME_UTC, MIN_TIME_UTC) self._state: HistoryStatsState = HistoryStatsState(None, None, self._period) self._history_current_period: list[State] = [] self._previous_run_before_start = False diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 40a13116026..bfa0c8f415e 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1306,3 +1306,84 @@ async def test_measure_from_end_going_backwards(hass, recorder_mock): assert hass.states.get("sensor.sensor2").state == "0.83" assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "83.3" + + +async def test_measure_cet(hass, recorder_mock): + """Test the history statistics sensor measure with a non-UTC timezone.""" + hass.config.set_time_zone("Europe/Berlin") + start_time = dt_util.utcnow() - timedelta(minutes=60) + t0 = start_time + timedelta(minutes=20) + t1 = t0 + timedelta(minutes=10) + t2 = t1 + timedelta(minutes=10) + + # Start t0 t1 t2 End + # |--20min--|--20min--|--10min--|--10min--| + # |---off---|---on----|---off---|---on----| + + def _fake_states(*args, **kwargs): + return { + "binary_sensor.test_id": [ + ha.State("binary_sensor.test_id", "on", last_changed=t0), + ha.State("binary_sensor.test_id", "off", last_changed=t1), + ha.State("binary_sensor.test_id", "on", last_changed=t2), + ] + } + + await async_setup_component( + hass, + "sensor", + { + "sensor": [ + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor1", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "time", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor2", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "time", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor3", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "count", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor4", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "ratio", + }, + ] + }, + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.recorder.history.state_changes_during_period", + _fake_states, + ): + for i in range(1, 5): + await async_update_entity(hass, f"sensor.sensor{i}") + await hass.async_block_till_done() + + assert hass.states.get("sensor.sensor1").state == "0.83" + assert hass.states.get("sensor.sensor2").state == "0.83" + assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor4").state == "83.3" From 76d105d0ba2f6235acc9bebd21cc3cdf57073316 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 29 Apr 2022 09:46:02 +0200 Subject: [PATCH 073/930] Sort stuff in template light (#71045) --- homeassistant/components/template/light.py | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index d2260aa10dc..17513d61222 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -49,47 +49,47 @@ from .template_entity import ( _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"] -CONF_ON_ACTION = "turn_on" -CONF_OFF_ACTION = "turn_off" -CONF_LEVEL_ACTION = "set_level" -CONF_LEVEL_TEMPLATE = "level_template" -CONF_TEMPERATURE_TEMPLATE = "temperature_template" -CONF_TEMPERATURE_ACTION = "set_temperature" -CONF_COLOR_TEMPLATE = "color_template" CONF_COLOR_ACTION = "set_color" -CONF_WHITE_VALUE_TEMPLATE = "white_value_template" -CONF_WHITE_VALUE_ACTION = "set_white_value" +CONF_COLOR_TEMPLATE = "color_template" CONF_EFFECT_ACTION = "set_effect" CONF_EFFECT_LIST_TEMPLATE = "effect_list_template" CONF_EFFECT_TEMPLATE = "effect_template" +CONF_LEVEL_ACTION = "set_level" +CONF_LEVEL_TEMPLATE = "level_template" CONF_MAX_MIREDS_TEMPLATE = "max_mireds_template" CONF_MIN_MIREDS_TEMPLATE = "min_mireds_template" +CONF_OFF_ACTION = "turn_off" +CONF_ON_ACTION = "turn_on" CONF_SUPPORTS_TRANSITION = "supports_transition_template" +CONF_TEMPERATURE_ACTION = "set_temperature" +CONF_TEMPERATURE_TEMPLATE = "temperature_template" +CONF_WHITE_VALUE_ACTION = "set_white_value" +CONF_WHITE_VALUE_TEMPLATE = "white_value_template" LIGHT_SCHEMA = vol.All( cv.deprecated(CONF_ENTITY_ID), vol.Schema( { - vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA, - vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_LEVEL_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_LEVEL_TEMPLATE): cv.template, - vol.Optional(CONF_FRIENDLY_NAME): cv.string, - vol.Optional(CONF_ENTITY_ID): cv.entity_ids, - vol.Optional(CONF_TEMPERATURE_TEMPLATE): cv.template, - vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_COLOR_TEMPLATE): cv.template, vol.Optional(CONF_COLOR_ACTION): cv.SCRIPT_SCHEMA, - vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, - vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_COLOR_TEMPLATE): cv.template, + vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA, vol.Inclusive(CONF_EFFECT_LIST_TEMPLATE, "effect"): cv.template, vol.Inclusive(CONF_EFFECT_TEMPLATE, "effect"): cv.template, - vol.Inclusive(CONF_EFFECT_ACTION, "effect"): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_ENTITY_ID): cv.entity_ids, + vol.Optional(CONF_FRIENDLY_NAME): cv.string, + vol.Optional(CONF_LEVEL_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_LEVEL_TEMPLATE): cv.template, vol.Optional(CONF_MAX_MIREDS_TEMPLATE): cv.template, vol.Optional(CONF_MIN_MIREDS_TEMPLATE): cv.template, + vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, + vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA, vol.Optional(CONF_SUPPORTS_TRANSITION): cv.template, + vol.Optional(CONF_TEMPERATURE_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_TEMPERATURE_TEMPLATE): cv.template, vol.Optional(CONF_UNIQUE_ID): cv.string, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_WHITE_VALUE_ACTION): cv.SCRIPT_SCHEMA, + vol.Optional(CONF_WHITE_VALUE_TEMPLATE): cv.template, } ).extend(TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY.schema), ) From 682ac52a207eda15cedd5da6672e2f99191df47f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 29 Apr 2022 18:06:21 +0200 Subject: [PATCH 074/930] Support shorthand logical operators in script sequences (#71022) --- homeassistant/helpers/config_validation.py | 2 +- tests/helpers/test_script.py | 117 +++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index bbf8475c539..f459d96040b 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -1599,7 +1599,7 @@ def determine_script_action(action: dict[str, Any]) -> str: if CONF_WAIT_TEMPLATE in action: return SCRIPT_ACTION_WAIT_TEMPLATE - if CONF_CONDITION in action: + if any(key in action for key in (CONF_CONDITION, "and", "or", "not")): return SCRIPT_ACTION_CHECK_CONDITION if CONF_EVENT in action: diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 7f1b35e4e4a..fb3b021daec 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -4744,3 +4744,120 @@ async def test_disabled_actions( "3": [{"result": {"event": "test_event", "event_data": {}}}], }, ) + + +async def test_condition_and_shorthand(hass, caplog): + """Test if we can use the shorthand and conditions in a script.""" + events = async_capture_events(hass, "test_event") + sequence = cv.SCRIPT_SCHEMA( + [ + {"event": "test_event"}, + { + "alias": "shorthand and condition", + "and": [ + { + "condition": "template", + "value_template": "{{ states('test.entity') == 'hello' }}", + } + ], + }, + {"event": "test_event"}, + ] + ) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + hass.states.async_set("test.entity", "hello") + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + assert "Test condition shorthand and condition: True" in caplog.text + assert len(events) == 2 + + assert_action_trace( + { + "0": [{"result": {"event": "test_event", "event_data": {}}}], + "1": [{"result": {"result": True}}], + "1/conditions/0": [ + {"result": {"entities": ["test.entity"], "result": True}} + ], + "2": [{"result": {"event": "test_event", "event_data": {}}}], + } + ) + + +async def test_condition_or_shorthand(hass, caplog): + """Test if we can use the shorthand or conditions in a script.""" + events = async_capture_events(hass, "test_event") + sequence = cv.SCRIPT_SCHEMA( + [ + {"event": "test_event"}, + { + "alias": "shorthand or condition", + "or": [ + { + "condition": "template", + "value_template": "{{ states('test.entity') == 'hello' }}", + } + ], + }, + {"event": "test_event"}, + ] + ) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + hass.states.async_set("test.entity", "hello") + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + assert "Test condition shorthand or condition: True" in caplog.text + assert len(events) == 2 + + assert_action_trace( + { + "0": [{"result": {"event": "test_event", "event_data": {}}}], + "1": [{"result": {"result": True}}], + "1/conditions/0": [ + {"result": {"entities": ["test.entity"], "result": True}} + ], + "2": [{"result": {"event": "test_event", "event_data": {}}}], + } + ) + + +async def test_condition_not_shorthand(hass, caplog): + """Test if we can use the shorthand not conditions in a script.""" + events = async_capture_events(hass, "test_event") + sequence = cv.SCRIPT_SCHEMA( + [ + {"event": "test_event"}, + { + "alias": "shorthand not condition", + "not": [ + { + "condition": "template", + "value_template": "{{ states('test.entity') == 'hello' }}", + } + ], + }, + {"event": "test_event"}, + ] + ) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + hass.states.async_set("test.entity", "not hello") + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + assert "Test condition shorthand not condition: True" in caplog.text + assert len(events) == 2 + + assert_action_trace( + { + "0": [{"result": {"event": "test_event", "event_data": {}}}], + "1": [{"result": {"result": True}}], + "1/conditions/0": [ + {"result": {"entities": ["test.entity"], "result": False}} + ], + "2": [{"result": {"event": "test_event", "event_data": {}}}], + } + ) From 755020ff6342721881f7d1752e51deed56862a7f Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Apr 2022 12:44:59 -0400 Subject: [PATCH 075/930] Update ZHA switch entities to leverage Zigpy cache appropriately (#71062) --- .../components/zha/core/channels/general.py | 21 ++-- homeassistant/components/zha/switch.py | 118 +++++++++--------- 2 files changed, 67 insertions(+), 72 deletions(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index 81152bb8869..e6524c9aad1 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -6,6 +6,7 @@ from collections.abc import Coroutine from typing import Any import zigpy.exceptions +import zigpy.types as t from zigpy.zcl.clusters import general from zigpy.zcl.foundation import Status @@ -300,7 +301,6 @@ class OnOffChannel(ZigbeeChannel): ) -> None: """Initialize OnOffChannel.""" super().__init__(cluster, ch_pool) - self._state = None self._off_listener = None @property @@ -314,9 +314,9 @@ class OnOffChannel(ZigbeeChannel): cmd = parse_and_log_command(self, tsn, command_id, args) if cmd in ("off", "off_with_effect"): - self.attribute_updated(self.ON_OFF, False) + self.cluster.update_attribute(self.ON_OFF, t.Bool.false) elif cmd in ("on", "on_with_recall_global_scene"): - self.attribute_updated(self.ON_OFF, True) + self.cluster.update_attribute(self.ON_OFF, t.Bool.true) elif cmd == "on_with_timed_off": should_accept = args[0] on_time = args[1] @@ -325,7 +325,7 @@ class OnOffChannel(ZigbeeChannel): if self._off_listener is not None: self._off_listener() self._off_listener = None - self.attribute_updated(self.ON_OFF, True) + self.cluster.update_attribute(self.ON_OFF, t.Bool.true) if on_time > 0: self._off_listener = async_call_later( self._ch_pool.hass, @@ -333,13 +333,13 @@ class OnOffChannel(ZigbeeChannel): self.set_to_off, ) elif cmd == "toggle": - self.attribute_updated(self.ON_OFF, not bool(self._state)) + self.cluster.update_attribute(self.ON_OFF, not bool(self.on_off)) @callback def set_to_off(self, *_): """Set the state to off.""" self._off_listener = None - self.attribute_updated(self.ON_OFF, False) + self.cluster.update_attribute(self.ON_OFF, t.Bool.false) @callback def attribute_updated(self, attrid, value): @@ -348,11 +348,6 @@ class OnOffChannel(ZigbeeChannel): self.async_send_signal( f"{self.unique_id}_{SIGNAL_ATTR_UPDATED}", attrid, "on_off", value ) - self._state = bool(value) - - async def async_initialize_channel_specific(self, from_cache: bool) -> None: - """Initialize channel.""" - self._state = self.on_off async def async_update(self): """Initialize channel.""" @@ -360,9 +355,7 @@ class OnOffChannel(ZigbeeChannel): return from_cache = not self._ch_pool.is_mains_powered self.debug("attempting to update onoff state - from cache: %s", from_cache) - state = await self.get_attribute_value(self.ON_OFF, from_cache=from_cache) - if state is not None: - self._state = bool(state) + await self.get_attribute_value(self.ON_OFF, from_cache=from_cache) await super().async_update() diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 87d2407c2dc..254e3691da1 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -46,21 +46,73 @@ async def async_setup_entry( config_entry.async_on_unload(unsub) -class BaseSwitch(SwitchEntity): - """Common base class for zha switches.""" +@STRICT_MATCH(channel_names=CHANNEL_ON_OFF) +class Switch(ZhaEntity, SwitchEntity): + """ZHA switch.""" - def __init__(self, *args, **kwargs): + def __init__(self, unique_id, zha_device, channels, **kwargs): """Initialize the ZHA switch.""" - self._on_off_channel = None - self._state = None - super().__init__(*args, **kwargs) + super().__init__(unique_id, zha_device, channels, **kwargs) + self._on_off_channel = self.cluster_channels.get(CHANNEL_ON_OFF) @property def is_on(self) -> bool: """Return if the switch is on based on the statemachine.""" - if self._state is None: + if self._on_off_channel.on_off is None: return False - return self._state + return self._on_off_channel.on_off + + async def async_turn_on(self, **kwargs) -> None: + """Turn the entity on.""" + result = await self._on_off_channel.on() + if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + return + self.async_write_ha_state() + + async def async_turn_off(self, **kwargs) -> None: + """Turn the entity off.""" + result = await self._on_off_channel.off() + if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + return + self.async_write_ha_state() + + @callback + def async_set_state(self, attr_id: int, attr_name: str, value: Any): + """Handle state update from channel.""" + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Run when about to be added to hass.""" + await super().async_added_to_hass() + self.async_accept_signal( + self._on_off_channel, SIGNAL_ATTR_UPDATED, self.async_set_state + ) + + async def async_update(self) -> None: + """Attempt to retrieve on off state from the switch.""" + await super().async_update() + if self._on_off_channel: + await self._on_off_channel.get_attribute_value("on_off", from_cache=False) + + +@GROUP_MATCH() +class SwitchGroup(ZhaGroupEntity, SwitchEntity): + """Representation of a switch group.""" + + def __init__( + self, entity_ids: list[str], unique_id: str, group_id: int, zha_device, **kwargs + ) -> None: + """Initialize a switch group.""" + super().__init__(entity_ids, unique_id, group_id, zha_device, **kwargs) + self._available: bool + self._state: bool + group = self.zha_device.gateway.get_group(self._group_id) + self._on_off_channel = group.endpoint[OnOff.cluster_id] + + @property + def is_on(self) -> bool: + """Return if the switch is on based on the statemachine.""" + return bool(self._state) async def async_turn_on(self, **kwargs) -> None: """Turn the entity on.""" @@ -78,56 +130,6 @@ class BaseSwitch(SwitchEntity): self._state = False self.async_write_ha_state() - -@STRICT_MATCH(channel_names=CHANNEL_ON_OFF) -class Switch(BaseSwitch, ZhaEntity): - """ZHA switch.""" - - def __init__(self, unique_id, zha_device, channels, **kwargs): - """Initialize the ZHA switch.""" - super().__init__(unique_id, zha_device, channels, **kwargs) - self._on_off_channel = self.cluster_channels.get(CHANNEL_ON_OFF) - - @callback - def async_set_state(self, attr_id: int, attr_name: str, value: Any): - """Handle state update from channel.""" - self._state = bool(value) - self.async_write_ha_state() - - async def async_added_to_hass(self) -> None: - """Run when about to be added to hass.""" - await super().async_added_to_hass() - self.async_accept_signal( - self._on_off_channel, SIGNAL_ATTR_UPDATED, self.async_set_state - ) - - @callback - def async_restore_last_state(self, last_state) -> None: - """Restore previous state.""" - self._state = last_state.state == STATE_ON - - async def async_update(self) -> None: - """Attempt to retrieve on off state from the switch.""" - await super().async_update() - if self._on_off_channel: - state = await self._on_off_channel.get_attribute_value("on_off") - if state is not None: - self._state = state - - -@GROUP_MATCH() -class SwitchGroup(BaseSwitch, ZhaGroupEntity): - """Representation of a switch group.""" - - def __init__( - self, entity_ids: list[str], unique_id: str, group_id: int, zha_device, **kwargs - ) -> None: - """Initialize a switch group.""" - super().__init__(entity_ids, unique_id, group_id, zha_device, **kwargs) - self._available: bool = False - group = self.zha_device.gateway.get_group(self._group_id) - self._on_off_channel = group.endpoint[OnOff.cluster_id] - async def async_update(self) -> None: """Query all members and determine the light group state.""" all_states = [self.hass.states.get(x) for x in self._entity_ids] From c90eb4d6b45c458092847ac6b70b004913462973 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 29 Apr 2022 19:00:44 +0200 Subject: [PATCH 076/930] Fix sql integration issues 5.0 beta (#71063) Co-authored-by: Paulus Schoutsen --- homeassistant/components/sql/config_flow.py | 30 ++++++++----------- homeassistant/components/sql/strings.json | 4 +++ .../components/sql/translations/en.json | 4 +++ tests/components/sql/__init__.py | 12 +++++++- tests/components/sql/test_config_flow.py | 27 ++++++++++------- tests/components/sql/test_sensor.py | 2 +- 6 files changed, 48 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index 4bb9f6c724c..9150cb8f63d 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -23,17 +23,12 @@ _LOGGER = logging.getLogger(__name__) DATA_SCHEMA = vol.Schema( { - vol.Optional(CONF_DB_URL): selector.TextSelector(selector.TextSelectorConfig()), - vol.Required(CONF_COLUMN_NAME): selector.TextSelector( - selector.TextSelectorConfig() - ), - vol.Required(CONF_QUERY): selector.TextSelector(selector.TextSelectorConfig()), - vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector( - selector.TextSelectorConfig() - ), - vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector( - selector.TemplateSelectorConfig() - ), + vol.Required(CONF_NAME, default="Select SQL Query"): selector.TextSelector(), + vol.Optional(CONF_DB_URL): selector.TextSelector(), + vol.Required(CONF_COLUMN_NAME): selector.TextSelector(), + vol.Required(CONF_QUERY): selector.TextSelector(), + vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(), + vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector(), } ) @@ -109,8 +104,7 @@ class SQLConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): column = user_input[CONF_COLUMN_NAME] uom = user_input.get(CONF_UNIT_OF_MEASUREMENT) value_template = user_input.get(CONF_VALUE_TEMPLATE) - - name = f"Select {column} SQL query" + name = user_input[CONF_NAME] try: validate_sql_select(query) @@ -182,17 +176,17 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow): description={ "suggested_value": self.entry.options[CONF_DB_URL] }, - ): selector.selector({"text": {}}), + ): selector.TextSelector(), vol.Required( CONF_QUERY, description={"suggested_value": self.entry.options[CONF_QUERY]}, - ): selector.selector({"text": {}}), + ): selector.TextSelector(), vol.Required( CONF_COLUMN_NAME, description={ "suggested_value": self.entry.options[CONF_COLUMN_NAME] }, - ): selector.selector({"text": {}}), + ): selector.TextSelector(), vol.Optional( CONF_UNIT_OF_MEASUREMENT, description={ @@ -200,7 +194,7 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow): CONF_UNIT_OF_MEASUREMENT ) }, - ): selector.selector({"text": {}}), + ): selector.TextSelector(), vol.Optional( CONF_VALUE_TEMPLATE, description={ @@ -208,7 +202,7 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow): CONF_VALUE_TEMPLATE ) }, - ): selector.selector({"text": {}}), + ): selector.TemplateSelector(), } ), errors=errors, diff --git a/homeassistant/components/sql/strings.json b/homeassistant/components/sql/strings.json index 0d174060d1b..8d3a194ac3e 100644 --- a/homeassistant/components/sql/strings.json +++ b/homeassistant/components/sql/strings.json @@ -12,6 +12,7 @@ "user": { "data": { "db_url": "Database URL", + "name": "[%key:common::config_flow::data::name%]", "query": "Select Query", "column": "Column", "unit_of_measurement": "Unit of Measure", @@ -19,6 +20,7 @@ }, "data_description": { "db_url": "Database URL, leave empty to use default HA database", + "name": "Name that will be used for Config Entry and also the Sensor", "query": "Query to run, needs to start with 'SELECT'", "column": "Column for returned query to present as state", "unit_of_measurement": "Unit of Measure (optional)", @@ -32,6 +34,7 @@ "init": { "data": { "db_url": "[%key:component::sql::config::step::user::data::db_url%]", + "name": "[%key:component::sql::config::step::user::data::name%]", "query": "[%key:component::sql::config::step::user::data::query%]", "column": "[%key:component::sql::config::step::user::data::column%]", "unit_of_measurement": "[%key:component::sql::config::step::user::data::unit_of_measurement%]", @@ -39,6 +42,7 @@ }, "data_description": { "db_url": "[%key:component::sql::config::step::user::data_description::db_url%]", + "name": "[%key:component::sql::config::step::user::data_description::name%]", "query": "[%key:component::sql::config::step::user::data_description::query%]", "column": "[%key:component::sql::config::step::user::data_description::column%]", "unit_of_measurement": "[%key:component::sql::config::step::user::data_description::unit_of_measurement%]", diff --git a/homeassistant/components/sql/translations/en.json b/homeassistant/components/sql/translations/en.json index 3b1fc223c00..4b72d024df4 100644 --- a/homeassistant/components/sql/translations/en.json +++ b/homeassistant/components/sql/translations/en.json @@ -13,6 +13,7 @@ "data": { "column": "Column", "db_url": "Database URL", + "name": "Name", "query": "Select Query", "unit_of_measurement": "Unit of Measure", "value_template": "Value Template" @@ -20,6 +21,7 @@ "data_description": { "column": "Column for returned query to present as state", "db_url": "Database URL, leave empty to use default HA database", + "name": "Name that will be used for Config Entry and also the Sensor", "query": "Query to run, needs to start with 'SELECT'", "unit_of_measurement": "Unit of Measure (optional)", "value_template": "Value Template (optional)" @@ -38,6 +40,7 @@ "data": { "column": "Column", "db_url": "Database URL", + "name": "Name", "query": "Select Query", "unit_of_measurement": "Unit of Measure", "value_template": "Value Template" @@ -45,6 +48,7 @@ "data_description": { "column": "Column for returned query to present as state", "db_url": "Database URL, leave empty to use default HA database", + "name": "Name that will be used for Config Entry and also the Sensor", "query": "Query to run, needs to start with 'SELECT'", "unit_of_measurement": "Unit of Measure (optional)", "value_template": "Value Template (optional)" diff --git a/tests/components/sql/__init__.py b/tests/components/sql/__init__.py index 7138a86a5d6..db65034bd11 100644 --- a/tests/components/sql/__init__.py +++ b/tests/components/sql/__init__.py @@ -6,19 +6,28 @@ from typing import Any from homeassistant.components.recorder import CONF_DB_URL from homeassistant.components.sql.const import CONF_COLUMN_NAME, CONF_QUERY, DOMAIN from homeassistant.config_entries import SOURCE_USER -from homeassistant.const import CONF_UNIT_OF_MEASUREMENT +from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry ENTRY_CONFIG = { CONF_DB_URL: "sqlite://", + CONF_NAME: "Get Value", CONF_QUERY: "SELECT 5 as value", CONF_COLUMN_NAME: "value", CONF_UNIT_OF_MEASUREMENT: "MiB", } ENTRY_CONFIG_INVALID_QUERY = { + CONF_DB_URL: "sqlite://", + CONF_NAME: "Get Value", + CONF_QUERY: "UPDATE 5 as value", + CONF_COLUMN_NAME: "size", + CONF_UNIT_OF_MEASUREMENT: "MiB", +} + +ENTRY_CONFIG_INVALID_QUERY_OPT = { CONF_DB_URL: "sqlite://", CONF_QUERY: "UPDATE 5 as value", CONF_COLUMN_NAME: "size", @@ -27,6 +36,7 @@ ENTRY_CONFIG_INVALID_QUERY = { ENTRY_CONFIG_NO_RESULTS = { CONF_DB_URL: "sqlite://", + CONF_NAME: "Get Value", CONF_QUERY: "SELECT kalle as value from no_table;", CONF_COLUMN_NAME: "value", CONF_UNIT_OF_MEASUREMENT: "MiB", diff --git a/tests/components/sql/test_config_flow.py b/tests/components/sql/test_config_flow.py index d38abce5da7..ec851b491b7 100644 --- a/tests/components/sql/test_config_flow.py +++ b/tests/components/sql/test_config_flow.py @@ -14,7 +14,12 @@ from homeassistant.data_entry_flow import ( RESULT_TYPE_FORM, ) -from . import ENTRY_CONFIG, ENTRY_CONFIG_INVALID_QUERY, ENTRY_CONFIG_NO_RESULTS +from . import ( + ENTRY_CONFIG, + ENTRY_CONFIG_INVALID_QUERY, + ENTRY_CONFIG_INVALID_QUERY_OPT, + ENTRY_CONFIG_NO_RESULTS, +) from tests.common import MockConfigEntry @@ -40,14 +45,14 @@ async def test_form(hass: HomeAssistant) -> None: print(ENTRY_CONFIG) assert result2["type"] == RESULT_TYPE_CREATE_ENTRY - assert result2["title"] == "Select value SQL query" + assert result2["title"] == "Get Value" assert result2["options"] == { "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select value SQL query", } assert len(mock_setup_entry.mock_calls) == 1 @@ -67,14 +72,14 @@ async def test_import_flow_success(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert result2["type"] == RESULT_TYPE_CREATE_ENTRY - assert result2["title"] == "Select value SQL query" + assert result2["title"] == "Get Value" assert result2["options"] == { "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select value SQL query", } assert len(mock_setup_entry.mock_calls) == 1 @@ -158,14 +163,14 @@ async def test_flow_fails_invalid_query(hass: HomeAssistant) -> None: ) assert result5["type"] == RESULT_TYPE_CREATE_ENTRY - assert result5["title"] == "Select value SQL query" + assert result5["title"] == "Get Value" assert result5["options"] == { "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select value SQL query", } @@ -176,11 +181,11 @@ async def test_options_flow(hass: HomeAssistant) -> None: data={}, options={ "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select value SQL query", }, ) entry.add_to_hass(hass) @@ -223,11 +228,11 @@ async def test_options_flow_fails_db_url(hass: HomeAssistant) -> None: data={}, options={ "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select value SQL query", }, ) entry.add_to_hass(hass) @@ -267,11 +272,11 @@ async def test_options_flow_fails_invalid_query( data={}, options={ "db_url": "sqlite://", + "name": "Get Value", "query": "SELECT 5 as value", "column": "value", "unit_of_measurement": "MiB", "value_template": None, - "name": "Select size SQL query", }, ) entry.add_to_hass(hass) @@ -287,7 +292,7 @@ async def test_options_flow_fails_invalid_query( result2 = await hass.config_entries.options.async_configure( result["flow_id"], - user_input=ENTRY_CONFIG_INVALID_QUERY, + user_input=ENTRY_CONFIG_INVALID_QUERY_OPT, ) assert result2["type"] == RESULT_TYPE_FORM diff --git a/tests/components/sql/test_sensor.py b/tests/components/sql/test_sensor.py index 77717ec400c..0eb3bf70683 100644 --- a/tests/components/sql/test_sensor.py +++ b/tests/components/sql/test_sensor.py @@ -52,7 +52,7 @@ async def test_import_query(hass: HomeAssistant) -> None: assert hass.config_entries.async_entries(DOMAIN) options = hass.config_entries.async_entries(DOMAIN)[0].options - assert options[CONF_NAME] == "Select value SQL query" + assert options[CONF_NAME] == "count_tables" async def test_query_value_template(hass: HomeAssistant) -> None: From 11b91e44c81b2a30f55efac8e425791a8478e057 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 13:18:06 -0500 Subject: [PATCH 077/930] Fix unsafe websocket stop call in isy994 (#71071) Fixes ``` 2022-04-29 12:49:10 ERROR (MainThread) [homeassistant.config_entries] Error unloading entry Alexander (192.168.209.83) for isy994 Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/config_entries.py", line 474, in async_unload result = await component.async_unload_entry(hass, self) File "/Users/bdraco/home-assistant/homeassistant/components/isy994/__init__.py", line 294, in async_unload_entry await hass.async_add_executor_job(_stop_auto_update) File "/opt/homebrew/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/Users/bdraco/home-assistant/homeassistant/components/isy994/__init__.py", line 292, in _stop_auto_update isy.websocket.stop() File "/Users/bdraco/home-assistant/venv/lib/python3.9/site-packages/pyisy/events/websocket.py", line 110, in stop self.websocket_task.cancel() File "/opt/homebrew/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 753, in call_soon self._check_thread() File "/opt/homebrew/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 790, in _check_thread raise RuntimeError( RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one ``` --- homeassistant/components/isy994/__init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index ba152c5d840..ae48fc18b5b 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -288,14 +288,10 @@ async def async_unload_entry( hass_isy_data = hass.data[DOMAIN][entry.entry_id] - isy = hass_isy_data[ISY994_ISY] + isy: ISY = hass_isy_data[ISY994_ISY] - def _stop_auto_update() -> None: - """Stop the isy auto update.""" - _LOGGER.debug("ISY Stopping Event Stream and automatic updates") - isy.websocket.stop() - - await hass.async_add_executor_job(_stop_auto_update) + _LOGGER.debug("ISY Stopping Event Stream and automatic updates") + isy.websocket.stop() if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) From d90937182e754fc5c12e8d7b8a29129ccdf71b6b Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:29:06 +0200 Subject: [PATCH 078/930] Fix "station is open" binary sensor in Tankerkoenig (#70928) --- homeassistant/components/tankerkoenig/binary_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/tankerkoenig/binary_sensor.py b/homeassistant/components/tankerkoenig/binary_sensor.py index 4b58ea26703..9a2b048e0b8 100644 --- a/homeassistant/components/tankerkoenig/binary_sensor.py +++ b/homeassistant/components/tankerkoenig/binary_sensor.py @@ -74,5 +74,5 @@ class StationOpenBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): @property def is_on(self) -> bool | None: """Return true if the station is open.""" - data = self.coordinator.data[self._station_id] - return data is not None and "status" in data + data: dict = self.coordinator.data[self._station_id] + return data is not None and data.get("status") == "open" From 57d03908827443fd7f2eb40e1c84b3e0ac7dffee Mon Sep 17 00:00:00 2001 From: rappenze Date: Fri, 29 Apr 2022 21:39:45 +0200 Subject: [PATCH 079/930] Improve energy meter support of fibaro sensor (#71072) --- homeassistant/components/fibaro/sensor.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/fibaro/sensor.py b/homeassistant/components/fibaro/sensor.py index bd80a9d0181..acaa97ee2a2 100644 --- a/homeassistant/components/fibaro/sensor.py +++ b/homeassistant/components/fibaro/sensor.py @@ -33,27 +33,43 @@ SENSOR_TYPES = { None, None, SensorDeviceClass.TEMPERATURE, + SensorStateClass.MEASUREMENT, ], "com.fibaro.smokeSensor": [ "Smoke", CONCENTRATION_PARTS_PER_MILLION, "mdi:fire", None, + None, ], "CO2": [ "CO2", CONCENTRATION_PARTS_PER_MILLION, None, - None, SensorDeviceClass.CO2, + SensorStateClass.MEASUREMENT, ], "com.fibaro.humiditySensor": [ "Humidity", PERCENTAGE, None, SensorDeviceClass.HUMIDITY, + SensorStateClass.MEASUREMENT, + ], + "com.fibaro.lightSensor": [ + "Light", + LIGHT_LUX, + None, + SensorDeviceClass.ILLUMINANCE, + SensorStateClass.MEASUREMENT, + ], + "com.fibaro.energyMeter": [ + "Energy", + ENERGY_KILO_WATT_HOUR, + None, + SensorDeviceClass.ENERGY, + SensorStateClass.TOTAL_INCREASING, ], - "com.fibaro.lightSensor": ["Light", LIGHT_LUX, None, SensorDeviceClass.ILLUMINANCE], } @@ -66,7 +82,7 @@ async def async_setup_entry( entities: list[SensorEntity] = [] for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES][Platform.SENSOR]: entities.append(FibaroSensor(device)) - for platform in (Platform.COVER, Platform.LIGHT, Platform.SWITCH): + for platform in (Platform.COVER, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH): for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES][platform]: if "energy" in device.interfaces: entities.append(FibaroEnergySensor(device)) @@ -89,6 +105,7 @@ class FibaroSensor(FibaroDevice, SensorEntity): self._unit = SENSOR_TYPES[fibaro_device.type][1] self._icon = SENSOR_TYPES[fibaro_device.type][2] self._device_class = SENSOR_TYPES[fibaro_device.type][3] + self._attr_state_class = SENSOR_TYPES[fibaro_device.type][4] else: self._unit = None self._icon = None From 2fb16fd06c049def3f88d482530f28dfcf498360 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 29 Apr 2022 21:40:23 +0200 Subject: [PATCH 080/930] Deprecate white_value support in template light (#71044) --- homeassistant/components/template/light.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 17513d61222..efe123f36b5 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -94,8 +94,13 @@ LIGHT_SCHEMA = vol.All( ).extend(TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY.schema), ) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - {vol.Required(CONF_LIGHTS): cv.schema_with_slug_keys(LIGHT_SCHEMA)} +PLATFORM_SCHEMA = vol.All( + # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 + cv.deprecated(CONF_WHITE_VALUE_ACTION), + cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), + PLATFORM_SCHEMA.extend( + {vol.Required(CONF_LIGHTS): cv.schema_with_slug_keys(LIGHT_SCHEMA)} + ), ) From 6bb685eabac5a92a6f57a547d221c42ed85a2a3e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:42:45 +0200 Subject: [PATCH 081/930] Use LightEntityFeature enum in smartthings (#71057) --- homeassistant/components/smartthings/light.py | 9 ++++++--- tests/components/smartthings/test_light.py | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/smartthings/light.py b/homeassistant/components/smartthings/light.py index 794a902e941..1b1738a94d4 100644 --- a/homeassistant/components/smartthings/light.py +++ b/homeassistant/components/smartthings/light.py @@ -14,8 +14,8 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_TRANSITION, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -86,7 +86,7 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): features = 0 # Brightness and transition if Capability.switch_level in self._device.capabilities: - features |= SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + features |= SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION # Color Temperature if Capability.color_temperature in self._device.capabilities: features |= SUPPORT_COLOR_TEMP @@ -124,7 +124,10 @@ class SmartThingsLight(SmartThingsEntity, LightEntity): async def async_turn_off(self, **kwargs) -> None: """Turn the light off.""" # Switch/transition - if self._supported_features & SUPPORT_TRANSITION and ATTR_TRANSITION in kwargs: + if ( + self._supported_features & LightEntityFeature.TRANSITION + and ATTR_TRANSITION in kwargs + ): await self.async_set_level(0, int(kwargs[ATTR_TRANSITION])) else: await self._device.switch_off(set_status=True) diff --git a/tests/components/smartthings/test_light.py b/tests/components/smartthings/test_light.py index 166b0606b66..0fff1403985 100644 --- a/tests/components/smartthings/test_light.py +++ b/tests/components/smartthings/test_light.py @@ -16,7 +16,7 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_TRANSITION, + LightEntityFeature, ) from homeassistant.components.smartthings.const import DOMAIN, SIGNAL_SMARTTHINGS_UPDATE from homeassistant.config_entries import ConfigEntryState @@ -82,7 +82,7 @@ async def test_entity_state(hass, light_devices): assert state.state == "on" assert ( state.attributes[ATTR_SUPPORTED_FEATURES] - == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + == SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION ) assert isinstance(state.attributes[ATTR_BRIGHTNESS], int) assert state.attributes[ATTR_BRIGHTNESS] == 255 @@ -92,7 +92,7 @@ async def test_entity_state(hass, light_devices): assert state.state == "off" assert ( state.attributes[ATTR_SUPPORTED_FEATURES] - == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_COLOR + == SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION | SUPPORT_COLOR ) # Color Dimmer 2 @@ -100,7 +100,10 @@ async def test_entity_state(hass, light_devices): assert state.state == "on" assert ( state.attributes[ATTR_SUPPORTED_FEATURES] - == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_COLOR | SUPPORT_COLOR_TEMP + == SUPPORT_BRIGHTNESS + | LightEntityFeature.TRANSITION + | SUPPORT_COLOR + | SUPPORT_COLOR_TEMP ) assert state.attributes[ATTR_BRIGHTNESS] == 255 assert state.attributes[ATTR_HS_COLOR] == (273.6, 55.0) From e63aef79fec62865b99d638559e13988ded6e7b7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:43:33 +0200 Subject: [PATCH 082/930] Use LightEntityFeature enum in template (#71056) --- homeassistant/components/template/light.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index efe123f36b5..32ce215a41f 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -16,10 +16,9 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, LightEntity, + LightEntityFeature, ) from homeassistant.const import ( CONF_ENTITY_ID, @@ -254,9 +253,9 @@ class LightTemplate(TemplateEntity, LightEntity): if self._white_value_script is not None: supported_features |= SUPPORT_WHITE_VALUE if self._effect_script is not None: - supported_features |= SUPPORT_EFFECT + supported_features |= LightEntityFeature.EFFECT if self._supports_transition is True: - supported_features |= SUPPORT_TRANSITION + supported_features |= LightEntityFeature.TRANSITION return supported_features @property From f08615fc766e33a7c92ce35d468200a6153ef717 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:45:25 +0200 Subject: [PATCH 083/930] Use LightEntityFeature enum in zha (#71060) --- homeassistant/components/zha/light.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/zha/light.py b/homeassistant/components/zha/light.py index ef7205feb87..2ec507109a2 100644 --- a/homeassistant/components/zha/light.py +++ b/homeassistant/components/zha/light.py @@ -26,9 +26,7 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -91,10 +89,10 @@ SIGNAL_LIGHT_GROUP_STATE_CHANGED = "zha_light_group_state_changed" SUPPORT_GROUP_LIGHT = ( SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP - | SUPPORT_EFFECT - | SUPPORT_FLASH + | LightEntityFeature.EFFECT + | LightEntityFeature.FLASH | SUPPORT_COLOR - | SUPPORT_TRANSITION + | LightEntityFeature.TRANSITION ) @@ -290,7 +288,7 @@ class BaseLight(LogMixin, light.LightEntity): if ( effect == light.EFFECT_COLORLOOP - and self.supported_features & light.SUPPORT_EFFECT + and self.supported_features & light.LightEntityFeature.EFFECT ): result = await self._color_channel.color_loop_set( UPDATE_COLORLOOP_ACTION @@ -306,7 +304,7 @@ class BaseLight(LogMixin, light.LightEntity): elif ( self._effect == light.EFFECT_COLORLOOP and effect != light.EFFECT_COLORLOOP - and self.supported_features & light.SUPPORT_EFFECT + and self.supported_features & light.LightEntityFeature.EFFECT ): result = await self._color_channel.color_loop_set( UPDATE_COLORLOOP_ACTION, @@ -318,7 +316,10 @@ class BaseLight(LogMixin, light.LightEntity): t_log["color_loop_set"] = result self._effect = None - if flash is not None and self._supported_features & light.SUPPORT_FLASH: + if ( + flash is not None + and self._supported_features & light.LightEntityFeature.FLASH + ): result = await self._identify_channel.trigger_effect( FLASH_EFFECTS[flash], EFFECT_DEFAULT_VARIANT ) @@ -373,7 +374,7 @@ class Light(BaseLight, ZhaEntity): if self._level_channel: self._supported_features |= light.SUPPORT_BRIGHTNESS - self._supported_features |= light.SUPPORT_TRANSITION + self._supported_features |= light.LightEntityFeature.TRANSITION self._brightness = self._level_channel.current_level if self._color_channel: @@ -394,13 +395,13 @@ class Light(BaseLight, ZhaEntity): self._hs_color = (0, 0) if color_capabilities & CAPABILITIES_COLOR_LOOP: - self._supported_features |= light.SUPPORT_EFFECT + self._supported_features |= light.LightEntityFeature.EFFECT effect_list.append(light.EFFECT_COLORLOOP) if self._color_channel.color_loop_active == 1: self._effect = light.EFFECT_COLORLOOP if self._identify_channel: - self._supported_features |= light.SUPPORT_FLASH + self._supported_features |= light.LightEntityFeature.FLASH if effect_list: self._effect_list = effect_list From 09d61edd9ff0797724f2a7146bb2b92cd74d3d12 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:47:26 +0200 Subject: [PATCH 084/930] Use LightEntityFeature enum in mqtt (#71055) --- homeassistant/components/mqtt/light/schema_basic.py | 5 +++-- homeassistant/components/mqtt/light/schema_json.py | 12 ++++++------ .../components/mqtt/light/schema_template.py | 8 +++----- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 8ad553ddd74..497e8186fd0 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -23,10 +23,10 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, SUPPORT_WHITE_VALUE, ColorMode, LightEntity, + LightEntityFeature, valid_supported_color_modes, ) from homeassistant.const import ( @@ -799,7 +799,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): """Flag supported features.""" supported_features = 0 supported_features |= ( - self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None and SUPPORT_EFFECT + self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None + and LightEntityFeature.EFFECT ) if not self._legacy_mode: return supported_features diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 2433bdf1679..c1e0d7467e0 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -24,13 +24,11 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, VALID_COLOR_MODES, ColorMode, LightEntity, + LightEntityFeature, legacy_supported_features, valid_supported_color_modes, ) @@ -215,8 +213,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): for key in (CONF_FLASH_TIME_SHORT, CONF_FLASH_TIME_LONG) } - self._supported_features = SUPPORT_TRANSITION | SUPPORT_FLASH - self._supported_features |= config[CONF_EFFECT] and SUPPORT_EFFECT + self._supported_features = ( + LightEntityFeature.TRANSITION | LightEntityFeature.FLASH + ) + self._supported_features |= config[CONF_EFFECT] and LightEntityFeature.EFFECT if not self._config[CONF_COLOR_MODE]: self._supported_features |= config[CONF_BRIGHTNESS] and SUPPORT_BRIGHTNESS self._supported_features |= config[CONF_COLOR_TEMP] and SUPPORT_COLOR_TEMP @@ -355,7 +355,7 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except ValueError: _LOGGER.warning("Invalid color temp value received") - if self._supported_features and SUPPORT_EFFECT: + if self._supported_features and LightEntityFeature.EFFECT: with suppress(KeyError): self._effect = values["effect"] diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index b82474db2a3..a98f634642d 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -15,11 +15,9 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, LightEntity, + LightEntityFeature, ) from homeassistant.const import ( CONF_NAME, @@ -442,7 +440,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): @property def supported_features(self): """Flag supported features.""" - features = SUPPORT_FLASH | SUPPORT_TRANSITION + features = LightEntityFeature.FLASH | LightEntityFeature.TRANSITION if self._templates[CONF_BRIGHTNESS_TEMPLATE] is not None: features = features | SUPPORT_BRIGHTNESS if ( @@ -452,7 +450,7 @@ class MqttLightTemplate(MqttEntity, LightEntity, RestoreEntity): ): features = features | SUPPORT_COLOR | SUPPORT_BRIGHTNESS if self._config.get(CONF_EFFECT_LIST) is not None: - features = features | SUPPORT_EFFECT + features = features | LightEntityFeature.EFFECT if self._templates[CONF_COLOR_TEMP_TEMPLATE] is not None: features = features | SUPPORT_COLOR_TEMP if self._templates[CONF_WHITE_VALUE_TEMPLATE] is not None: From faffd809d51bff27715a7a833ca1918c7632fb49 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:48:55 +0200 Subject: [PATCH 085/930] Use LightEntityFeature enum in osramlightify (#71059) --- homeassistant/components/osramlightify/light.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index 20e50b8b7fd..a247497550f 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -18,9 +18,8 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_TRANSITION, LightEntity, + LightEntityFeature, ) from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant @@ -210,13 +209,18 @@ class Luminary(LightEntity): """Get list of supported features.""" features = 0 if "lum" in self._luminary.supported_features(): - features = features | SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + features = features | SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION if "temp" in self._luminary.supported_features(): - features = features | SUPPORT_COLOR_TEMP | SUPPORT_TRANSITION + features = features | SUPPORT_COLOR_TEMP | LightEntityFeature.TRANSITION if "rgb" in self._luminary.supported_features(): - features = features | SUPPORT_COLOR | SUPPORT_TRANSITION | SUPPORT_EFFECT + features = ( + features + | SUPPORT_COLOR + | LightEntityFeature.TRANSITION + | LightEntityFeature.EFFECT + ) return features @@ -416,7 +420,7 @@ class OsramLightifyGroup(Luminary): """Get list of supported features.""" features = super()._get_supported_features() if self._luminary.scenes(): - features = features | SUPPORT_EFFECT + features = features | LightEntityFeature.EFFECT return features From da8160a770be4bb76df0cb832814a8132ed0ec22 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:49:16 +0200 Subject: [PATCH 086/930] Use LightEntityFeature enum in wemo (#71058) --- homeassistant/components/wemo/light.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index b7c6aefa868..34ec7cab08d 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -14,8 +14,8 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_TRANSITION, LightEntity, + LightEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback @@ -30,7 +30,10 @@ from .entity import WemoBinaryStateEntity, WemoEntity from .wemo_device import DeviceCoordinator SUPPORT_WEMO = ( - SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_COLOR | SUPPORT_TRANSITION + SUPPORT_BRIGHTNESS + | SUPPORT_COLOR_TEMP + | SUPPORT_COLOR + | LightEntityFeature.TRANSITION ) # The WEMO_ constants below come from pywemo itself From e1f4b17971f0c555f54f2635f93f48a37f3a1ea8 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 29 Apr 2022 22:55:55 +0200 Subject: [PATCH 087/930] Pydeconz raise ResponseError when deCONZ Rest API Plugin is not yet ready (#71078) --- homeassistant/components/deconz/gateway.py | 2 +- tests/components/deconz/test_gateway.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index ef07a97ad8f..f54cd4076d3 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -289,6 +289,6 @@ async def get_deconz_session( LOGGER.warning("Invalid key for deCONZ at %s", config[CONF_HOST]) raise AuthenticationRequired from err - except (asyncio.TimeoutError, errors.RequestError) as err: + except (asyncio.TimeoutError, errors.RequestError, errors.ResponseError) as err: LOGGER.error("Error connecting to deCONZ gateway at %s", config[CONF_HOST]) raise CannotConnect from err diff --git a/tests/components/deconz/test_gateway.py b/tests/components/deconz/test_gateway.py index 3a4f6b907af..e6ded3981c4 100644 --- a/tests/components/deconz/test_gateway.py +++ b/tests/components/deconz/test_gateway.py @@ -286,6 +286,7 @@ async def test_get_deconz_session(hass): [ (asyncio.TimeoutError, CannotConnect), (pydeconz.RequestError, CannotConnect), + (pydeconz.ResponseError, CannotConnect), (pydeconz.Unauthorized, AuthenticationRequired), ], ) From 865c75b6310fbf70922d5a4ee42fa2519954b059 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 29 Apr 2022 22:57:38 +0200 Subject: [PATCH 088/930] Don't rely on deCONZ gateway object in config options flow (#71079) --- .../components/deconz/config_flow.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index fcfea60533f..806ac72c973 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -31,12 +31,15 @@ from .const import ( CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_ALLOW_NEW_DEVICES, + DEFAULT_ALLOW_CLIP_SENSOR, + DEFAULT_ALLOW_DECONZ_GROUPS, + DEFAULT_ALLOW_NEW_DEVICES, DEFAULT_PORT, DOMAIN, HASSIO_CONFIGURATION_URL, LOGGER, ) -from .gateway import DeconzGateway, get_gateway_from_config_entry +from .gateway import DeconzGateway DECONZ_MANUFACTURERURL = "http://www.dresden-elektronik.de" CONF_SERIAL = "serial" @@ -298,7 +301,6 @@ class DeconzOptionsFlowHandler(OptionsFlow): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Manage the deCONZ options.""" - self.gateway = get_gateway_from_config_entry(self.hass, self.config_entry) return await self.async_step_deconz_devices() async def async_step_deconz_devices( @@ -309,22 +311,20 @@ class DeconzOptionsFlowHandler(OptionsFlow): self.options.update(user_input) return self.async_create_entry(title="", data=self.options) + schema_options = {} + for option, default in ( + (CONF_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_CLIP_SENSOR), + (CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS), + (CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES), + ): + schema_options[ + vol.Optional( + option, + default=self.config_entry.options.get(option, default), + ) + ] = bool + return self.async_show_form( step_id="deconz_devices", - data_schema=vol.Schema( - { - vol.Optional( - CONF_ALLOW_CLIP_SENSOR, - default=self.gateway.option_allow_clip_sensor, - ): bool, - vol.Optional( - CONF_ALLOW_DECONZ_GROUPS, - default=self.gateway.option_allow_deconz_groups, - ): bool, - vol.Optional( - CONF_ALLOW_NEW_DEVICES, - default=self.gateway.option_allow_new_devices, - ): bool, - } - ), + data_schema=vol.Schema(schema_options), ) From 7f094a928bb9ed2887422574eeda1c2fe3d4e9a0 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 30 Apr 2022 00:34:33 +0200 Subject: [PATCH 089/930] Fix linking issue when deCONZ gateway is not unlocked (#71082) --- homeassistant/components/deconz/config_flow.py | 5 ++++- homeassistant/components/deconz/strings.json | 1 + .../components/deconz/translations/en.json | 1 + tests/components/deconz/test_config_flow.py | 16 +++++++++++++--- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 806ac72c973..af37ee96878 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -8,7 +8,7 @@ from typing import Any, cast from urllib.parse import urlparse import async_timeout -from pydeconz.errors import RequestError, ResponseError +from pydeconz.errors import LinkButtonNotPressed, RequestError, ResponseError from pydeconz.gateway import DeconzSession from pydeconz.utils import ( DiscoveredBridge, @@ -160,6 +160,9 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN): async with async_timeout.timeout(10): api_key = await deconz_session.get_api_key() + except LinkButtonNotPressed: + errors["base"] = "linking_not_possible" + except (ResponseError, RequestError, asyncio.TimeoutError): errors["base"] = "no_key" diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 0fa929f9e63..4098951d714 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -23,6 +23,7 @@ } }, "error": { + "linking_not_possible": "Couldn't link with the gateway", "no_key": "Couldn't get an API key" }, "abort": { diff --git a/homeassistant/components/deconz/translations/en.json b/homeassistant/components/deconz/translations/en.json index a8e09fd20d7..16034e12414 100644 --- a/homeassistant/components/deconz/translations/en.json +++ b/homeassistant/components/deconz/translations/en.json @@ -9,6 +9,7 @@ "updated_instance": "Updated deCONZ instance with new host address" }, "error": { + "linking_not_possible": "Couldn't link with the gateway", "no_key": "Couldn't get an API key" }, "flow_title": "{host}", diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 8ed1e348fee..97b2f7ee164 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -4,6 +4,7 @@ import asyncio from unittest.mock import patch import pydeconz +import pytest from homeassistant.components import ssdp from homeassistant.components.deconz.config_flow import ( @@ -341,7 +342,16 @@ async def test_manual_configuration_timeout_get_bridge(hass, aioclient_mock): assert result["reason"] == "no_bridges" -async def test_link_get_api_key_ResponseError(hass, aioclient_mock): +@pytest.mark.parametrize( + "raised_error, error_string", + [ + (pydeconz.errors.LinkButtonNotPressed, "linking_not_possible"), + (asyncio.TimeoutError, "no_key"), + (pydeconz.errors.ResponseError, "no_key"), + (pydeconz.errors.RequestError, "no_key"), + ], +) +async def test_link_step_fails(hass, aioclient_mock, raised_error, error_string): """Test config flow should abort if no API key was possible to retrieve.""" aioclient_mock.get( pydeconz.utils.URL_DISCOVER, @@ -360,7 +370,7 @@ async def test_link_get_api_key_ResponseError(hass, aioclient_mock): assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "link" - aioclient_mock.post("http://1.2.3.4:80/api", exc=pydeconz.errors.ResponseError) + aioclient_mock.post("http://1.2.3.4:80/api", exc=raised_error) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={} @@ -368,7 +378,7 @@ async def test_link_get_api_key_ResponseError(hass, aioclient_mock): assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "link" - assert result["errors"] == {"base": "no_key"} + assert result["errors"] == {"base": error_string} async def test_reauth_flow_update_configuration(hass, aioclient_mock): From f184f9e1427da0dfa9c2c374233c6fe0ef04d809 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 29 Apr 2022 18:35:53 -0400 Subject: [PATCH 090/930] Fix ZHA cover initial state (#71083) --- .../components/zha/core/channels/security.py | 12 ++++++------ homeassistant/components/zha/core/gateway.py | 4 ++-- homeassistant/components/zha/entity.py | 6 ++---- homeassistant/components/zha/select.py | 6 ------ 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/zha/core/channels/security.py b/homeassistant/components/zha/core/channels/security.py index 19be861178f..0e1e0f8e8a3 100644 --- a/homeassistant/components/zha/core/channels/security.py +++ b/homeassistant/components/zha/core/channels/security.py @@ -84,7 +84,7 @@ class IasAce(ZigbeeChannel): @callback def cluster_command(self, tsn, command_id, args) -> None: """Handle commands received to this cluster.""" - self.warning( + self.debug( "received command %s", self._cluster.server_commands[command_id].name ) self.command_map[command_id](*args) @@ -120,7 +120,7 @@ class IasAce(ZigbeeChannel): code != self.panel_code and self.armed_state != AceCluster.PanelStatus.Panel_Disarmed ): - self.warning("Invalid code supplied to IAS ACE") + self.debug("Invalid code supplied to IAS ACE") self.invalid_tries += 1 zigbee_reply = self.arm_response( AceCluster.ArmNotification.Invalid_Arm_Disarm_Code @@ -131,12 +131,12 @@ class IasAce(ZigbeeChannel): self.armed_state == AceCluster.PanelStatus.Panel_Disarmed and self.alarm_status == AceCluster.AlarmStatus.No_Alarm ): - self.warning("IAS ACE already disarmed") + self.debug("IAS ACE already disarmed") zigbee_reply = self.arm_response( AceCluster.ArmNotification.Already_Disarmed ) else: - self.warning("Disarming all IAS ACE zones") + self.debug("Disarming all IAS ACE zones") zigbee_reply = self.arm_response( AceCluster.ArmNotification.All_Zones_Disarmed ) @@ -177,12 +177,12 @@ class IasAce(ZigbeeChannel): ) -> None: """Arm the panel with the specified statuses.""" if self.code_required_arm_actions and code != self.panel_code: - self.warning("Invalid code supplied to IAS ACE") + self.debug("Invalid code supplied to IAS ACE") zigbee_reply = self.arm_response( AceCluster.ArmNotification.Invalid_Arm_Disarm_Code ) else: - self.warning("Arming all IAS ACE zones") + self.debug("Arming all IAS ACE zones") self.armed_state = panel_status zigbee_reply = self.arm_response(armed_type) return zigbee_reply diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 1280f3defe3..0ba375362f7 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -240,14 +240,14 @@ class ZHAGateway: async def async_initialize_devices_and_entities(self) -> None: """Initialize devices and load entities.""" - _LOGGER.warning("Loading all devices") + _LOGGER.debug("Loading all devices") await asyncio.gather( *(dev.async_initialize(from_cache=True) for dev in self.devices.values()) ) async def fetch_updated_state() -> None: """Fetch updated state for mains powered devices.""" - _LOGGER.warning("Fetching current state for mains powered devices") + _LOGGER.debug("Fetching current state for mains powered devices") await asyncio.gather( *( dev.async_initialize(from_cache=False) diff --git a/homeassistant/components/zha/entity.py b/homeassistant/components/zha/entity.py index 50ffb8f4fcd..e9ea9ee871a 100644 --- a/homeassistant/components/zha/entity.py +++ b/homeassistant/components/zha/entity.py @@ -206,10 +206,8 @@ class ZhaEntity(BaseZhaEntity, RestoreEntity): signal_override=True, ) - if not self.zha_device.is_mains_powered: - # mains powered devices will get real time state - if last_state := await self.async_get_last_state(): - self.async_restore_last_state(last_state) + if last_state := await self.async_get_last_state(): + self.async_restore_last_state(last_state) self.async_accept_signal( None, diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index b9237e24eef..afa2ee18da9 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -86,12 +86,6 @@ class ZHAEnumSelectEntity(ZhaEntity, SelectEntity): self._channel.data_cache[self._attr_name] = self._enum[option.replace(" ", "_")] self.async_write_ha_state() - async def async_added_to_hass(self) -> None: - """Run when about to be added to hass.""" - await super().async_added_to_hass() - if last_state := await self.async_get_last_state(): - self.async_restore_last_state(last_state) - @callback def async_restore_last_state(self, last_state) -> None: """Restore previous state.""" From 6635fc4e3111f72bfa6095c97b3f522429fa1a8b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sat, 30 Apr 2022 00:38:49 +0200 Subject: [PATCH 091/930] Use LightEntityFeature enum in limitlessled (#71061) --- .../components/limitlessled/light.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/limitlessled/light.py b/homeassistant/components/limitlessled/light.py index e45a4a2ced0..15b814775e6 100644 --- a/homeassistant/components/limitlessled/light.py +++ b/homeassistant/components/limitlessled/light.py @@ -27,10 +27,8 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, - SUPPORT_EFFECT, - SUPPORT_FLASH, - SUPPORT_TRANSITION, LightEntity, + LightEntityFeature, ) from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_TYPE, STATE_ON from homeassistant.core import HomeAssistant @@ -63,23 +61,26 @@ MIN_SATURATION = 10 WHITE = [0, 0] SUPPORT_LIMITLESSLED_WHITE = ( - SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP | SUPPORT_EFFECT | SUPPORT_TRANSITION + SUPPORT_BRIGHTNESS + | SUPPORT_COLOR_TEMP + | LightEntityFeature.EFFECT + | LightEntityFeature.TRANSITION ) -SUPPORT_LIMITLESSLED_DIMMER = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION +SUPPORT_LIMITLESSLED_DIMMER = SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION SUPPORT_LIMITLESSLED_RGB = ( SUPPORT_BRIGHTNESS - | SUPPORT_EFFECT - | SUPPORT_FLASH + | LightEntityFeature.EFFECT + | LightEntityFeature.FLASH | SUPPORT_COLOR - | SUPPORT_TRANSITION + | LightEntityFeature.TRANSITION ) SUPPORT_LIMITLESSLED_RGBWW = ( SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP - | SUPPORT_EFFECT - | SUPPORT_FLASH + | LightEntityFeature.EFFECT + | LightEntityFeature.FLASH | SUPPORT_COLOR - | SUPPORT_TRANSITION + | LightEntityFeature.TRANSITION ) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( @@ -369,7 +370,7 @@ class LimitlessLEDGroup(LightEntity, RestoreEntity): pipeline.transition(transition_time, **args) # Flash. - if ATTR_FLASH in kwargs and self._supported & SUPPORT_FLASH: + if ATTR_FLASH in kwargs and self._supported & LightEntityFeature.FLASH: duration = 0 if kwargs[ATTR_FLASH] == FLASH_LONG: duration = 1 From 4eca162a0aacbbe16693174290df8620b833e8ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 17:46:25 -0500 Subject: [PATCH 092/930] Add reauth support to ISY994 (#71069) --- homeassistant/components/isy994/__init__.py | 8 +-- .../components/isy994/config_flow.py | 54 ++++++++++++++- homeassistant/components/isy994/strings.json | 13 +++- .../components/isy994/translations/en.json | 13 +++- tests/components/isy994/test_config_flow.py | 69 ++++++++++++++++++- 5 files changed, 145 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index ae48fc18b5b..faa2f7cfb5d 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -17,7 +17,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, ) from homeassistant.core import Event, HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, config_validation as cv import homeassistant.helpers.device_registry as dr from homeassistant.helpers.typing import ConfigType @@ -181,11 +181,7 @@ async def async_setup_entry( f"Timed out initializing the ISY; device may be busy, trying again later: {err}" ) from err except ISYInvalidAuthError as err: - _LOGGER.error( - "Invalid credentials for the ISY, please adjust settings and try again: %s", - err, - ) - return False + raise ConfigEntryAuthFailed(f"Invalid credentials for the ISY: {err}") from err except ISYConnectionError as err: raise ConfigEntryNotReady( f"Failed to connect to the ISY, please adjust settings and try again: {err}" diff --git a/homeassistant/components/isy994/config_flow.py b/homeassistant/components/isy994/config_flow.py index dea4bce4eeb..34d4738db68 100644 --- a/homeassistant/components/isy994/config_flow.py +++ b/homeassistant/components/isy994/config_flow.py @@ -119,6 +119,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self) -> None: """Initialize the isy994 config flow.""" self.discovered_conf: dict[str, str] = {} + self._existing_entry: config_entries.ConfigEntry | None = None @staticmethod @callback @@ -142,7 +143,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except InvalidHost: errors["base"] = "invalid_host" except InvalidAuth: - errors["base"] = "invalid_auth" + errors[CONF_PASSWORD] = "invalid_auth" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" @@ -252,6 +253,57 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.context["title_placeholders"] = self.discovered_conf return await self.async_step_user() + async def async_step_reauth( + self, user_input: dict[str, Any] | None = None + ) -> data_entry_flow.FlowResult: + """Handle reauth.""" + self._existing_entry = await self.async_set_unique_id(self.context["unique_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> data_entry_flow.FlowResult: + """Handle reauth input.""" + errors = {} + assert self._existing_entry is not None + existing_entry = self._existing_entry + existing_data = existing_entry.data + if user_input is not None: + new_data = { + **existing_data, + CONF_USERNAME: user_input[CONF_USERNAME], + CONF_PASSWORD: user_input[CONF_PASSWORD], + } + try: + await validate_input(self.hass, new_data) + except CannotConnect: + errors["base"] = "cannot_connect" + except InvalidAuth: + errors[CONF_PASSWORD] = "invalid_auth" + else: + cfg_entries = self.hass.config_entries + cfg_entries.async_update_entry(existing_entry, data=new_data) + await cfg_entries.async_reload(existing_entry.entry_id) + return self.async_abort(reason="reauth_successful") + + self.context["title_placeholders"] = { + CONF_NAME: existing_entry.title, + CONF_HOST: existing_data[CONF_HOST], + } + return self.async_show_form( + description_placeholders={CONF_HOST: existing_data[CONF_HOST]}, + step_id="reauth_confirm", + data_schema=vol.Schema( + { + vol.Required( + CONF_USERNAME, default=existing_data[CONF_USERNAME] + ): str, + vol.Required(CONF_PASSWORD): str, + } + ), + errors=errors, + ) + class OptionsFlowHandler(config_entries.OptionsFlow): """Handle a option flow for isy994.""" diff --git a/homeassistant/components/isy994/strings.json b/homeassistant/components/isy994/strings.json index eaba5f7a9da..74b54f406a0 100644 --- a/homeassistant/components/isy994/strings.json +++ b/homeassistant/components/isy994/strings.json @@ -10,10 +10,19 @@ "tls": "The TLS version of the ISY controller." }, "description": "The host entry must be in full URL format, e.g., http://192.168.10.100:80", - "title": "Connect to your ISY994" + "title": "Connect to your ISY" + }, + "reauth_confirm": { + "description": "The credentials for {host} is no longer valid.", + "title": "Reauthenticate your ISY", + "data": { + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } } }, "error": { + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "unknown": "[%key:common::config_flow::error::unknown%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", @@ -26,7 +35,7 @@ "options": { "step": { "init": { - "title": "ISY994 Options", + "title": "ISY Options", "description": "Set the options for the ISY Integration: \n • Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n • Ignore String: Any device with 'Ignore String' in the name will be ignored. \n • Variable Sensor String: Any variable that contains 'Variable Sensor String' will be added as a sensor. \n • Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", "data": { "sensor_string": "Node Sensor String", diff --git a/homeassistant/components/isy994/translations/en.json b/homeassistant/components/isy994/translations/en.json index dbeaa75acf4..b221765cd99 100644 --- a/homeassistant/components/isy994/translations/en.json +++ b/homeassistant/components/isy994/translations/en.json @@ -7,10 +7,19 @@ "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", "invalid_host": "The host entry was not in full URL format, e.g., http://192.168.10.100:80", + "reauth_successful": "Re-authentication was successful", "unknown": "Unexpected error" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Password", + "username": "Username" + }, + "description": "The credentials for {host} is no longer valid.", + "title": "Reauthenticate your ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Username" }, "description": "The host entry must be in full URL format, e.g., http://192.168.10.100:80", - "title": "Connect to your ISY994" + "title": "Connect to your ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Variable Sensor String" }, "description": "Set the options for the ISY Integration: \n \u2022 Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n \u2022 Ignore String: Any device with 'Ignore String' in the name will be ignored. \n \u2022 Variable Sensor String: Any variable that contains 'Variable Sensor String' will be added as a sensor. \n \u2022 Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", - "title": "ISY994 Options" + "title": "ISY Options" } } }, diff --git a/tests/components/isy994/test_config_flow.py b/tests/components/isy994/test_config_flow.py index 60e9fd964b6..8458dc0dc67 100644 --- a/tests/components/isy994/test_config_flow.py +++ b/tests/components/isy994/test_config_flow.py @@ -173,7 +173,7 @@ async def test_form_invalid_auth(hass: HomeAssistant): ) assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result2["errors"] == {"base": "invalid_auth"} + assert result2["errors"] == {CONF_PASSWORD: "invalid_auth"} async def test_form_unknown_exeption(hass: HomeAssistant): @@ -679,3 +679,70 @@ async def test_form_dhcp_existing_ignored_entry(hass: HomeAssistant): assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_configured" + + +async def test_reauth(hass): + """Test we can reauth.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_USERNAME: "bob", + CONF_HOST: f"http://{MOCK_HOSTNAME}:1443{ISY_URL_POSTFIX}", + }, + unique_id=MOCK_UUID, + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_REAUTH, "unique_id": MOCK_UUID}, + ) + + assert result["type"] == "form" + assert result["step_id"] == "reauth_confirm" + + with patch( + PATCH_CONNECTION, + side_effect=ISYInvalidAuthError(), + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"password": "invalid_auth"} + + with patch( + PATCH_CONNECTION, + side_effect=ISYConnectionError(), + ): + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result3["type"] == "form" + assert result3["errors"] == {"base": "cannot_connect"} + + with patch(PATCH_CONNECTION, return_value=MOCK_CONFIG_RESPONSE), patch( + "homeassistant.components.isy994.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result4 = await hass.config_entries.flow.async_configure( + result3["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert mock_setup_entry.called + assert result4["type"] == "abort" + assert result4["reason"] == "reauth_successful" From 85dbfa75efdfa0471886fcd679380809c4d299ec Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Fri, 29 Apr 2022 18:09:08 -0500 Subject: [PATCH 093/930] Frontend bump 20220429.0 (#71085) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index c806b21e723..e57f06827a5 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220428.0"], + "requirements": ["home-assistant-frontend==20220429.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index bf6ad12b287..0cb893be99d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220428.0 +home-assistant-frontend==20220429.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index 5f0a54d77a1..5f535f1248a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220428.0 +home-assistant-frontend==20220429.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c8470d51d4a..61be95a6bc4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220428.0 +home-assistant-frontend==20220429.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 7662d588a990783a823d97c89412888cba0e1cb6 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Fri, 29 Apr 2022 19:09:21 -0400 Subject: [PATCH 094/930] Patch Insteon Hub connectivity issues (#71081) --- homeassistant/components/insteon/manifest.json | 11 +++++++++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index ad5736f4d53..fc8ade1ba4d 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -4,11 +4,18 @@ "documentation": "https://www.home-assistant.io/integrations/insteon", "dependencies": ["http", "websocket_api"], "requirements": [ - "pyinsteon==1.1.0b1", + "pyinsteon==1.1.0b3", "insteon-frontend-home-assistant==0.1.0" ], "codeowners": ["@teharris1"], - "dhcp": [{ "macaddress": "000EF3*" }, { "registered_devices": true }], + "dhcp": [ + { + "macaddress": "000EF3*" + }, + { + "registered_devices": true + } + ], "config_flow": true, "iot_class": "local_push", "loggers": ["pyinsteon", "pypubsub"], diff --git a/requirements_all.txt b/requirements_all.txt index 5f535f1248a..c9900b718a8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1547,7 +1547,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.1.0b1 +pyinsteon==1.1.0b3 # homeassistant.components.intesishome pyintesishome==1.7.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 61be95a6bc4..2a1ea95093d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1029,7 +1029,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.1.0b1 +pyinsteon==1.1.0b3 # homeassistant.components.ipma pyipma==2.0.5 From de26df0c6d8dd906e9a8abde6efa573d73472961 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 29 Apr 2022 16:09:38 -0700 Subject: [PATCH 095/930] Fix /config/server_control redirect (#71084) --- homeassistant/components/frontend/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 55d745eb309..0c540a477ef 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -360,13 +360,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if os.path.isdir(local): hass.http.register_static_path("/local", local, not is_dev) + # Can be removed in 2023 + hass.http.register_redirect("/config/server_control", "/developer-tools/yaml") + hass.http.app.router.register_resource(IndexView(repo_path, hass)) async_register_built_in_panel(hass, "profile") - # Can be removed in 2023 - hass.http.register_redirect("/config/server_control", "/developer-tools/yaml") - async_register_built_in_panel( hass, "developer-tools", From 852eaa062cf626a1834ca8814679a6f30bb6007f Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Sat, 30 Apr 2022 00:16:05 +0100 Subject: [PATCH 096/930] update unit_of_measurement even if unit_of_measurement is known (#69699) --- homeassistant/components/integration/sensor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/integration/sensor.py b/homeassistant/components/integration/sensor.py index 40229404288..8cf5da5bc7c 100644 --- a/homeassistant/components/integration/sensor.py +++ b/homeassistant/components/integration/sensor.py @@ -196,10 +196,11 @@ class IntegrationSensor(RestoreEntity, SensorEntity): # or device_class. update_state = False - if self._unit_of_measurement is None: - unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - if unit is not None: - self._unit_of_measurement = self._unit_template.format(unit) + unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) + if unit is not None: + new_unit_of_measurement = self._unit_template.format(unit) + if self._unit_of_measurement != new_unit_of_measurement: + self._unit_of_measurement = new_unit_of_measurement update_state = True if ( From cef5d6054c620a13176fb98f54ff5e17c6f3001f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Elio=20Petten=C3=B2?= Date: Sat, 30 Apr 2022 00:21:12 +0100 Subject: [PATCH 097/930] withings: don't store the webhook URL in the state. (#69191) The webhook url should be calculated at startup, not stored in the state as otherwise a change in network requires re-configuring the integration from scratch. This is particularly important as external network configuration might change when moving HA between networks, and it's not obvious it needs to be re-added just for this. --- homeassistant/components/withings/__init__.py | 4 ---- homeassistant/components/withings/common.py | 5 ++++- homeassistant/components/withings/const.py | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/withings/__init__.py b/homeassistant/components/withings/__init__.py index 701694e40e9..9d1fe1a600f 100644 --- a/homeassistant/components/withings/__init__.py +++ b/homeassistant/components/withings/__init__.py @@ -111,10 +111,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: const.CONF_USE_WEBHOOK ], CONF_WEBHOOK_ID: webhook_id, - const.CONF_WEBHOOK_URL: entry.data.get( - const.CONF_WEBHOOK_URL, - webhook.async_generate_url(hass, webhook_id), - ), }, } diff --git a/homeassistant/components/withings/common.py b/homeassistant/components/withings/common.py index f2fe7bfb1ec..c0dadb47924 100644 --- a/homeassistant/components/withings/common.py +++ b/homeassistant/components/withings/common.py @@ -27,6 +27,7 @@ from withings_api.common import ( query_measure_groups, ) +from homeassistant.components import webhook from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.http import HomeAssistantView from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN @@ -1046,7 +1047,9 @@ async def async_get_data_manager( config_entry.data["token"]["userid"], WebhookConfig( id=config_entry.data[CONF_WEBHOOK_ID], - url=config_entry.data[const.CONF_WEBHOOK_URL], + url=webhook.async_generate_url( + hass, config_entry.data[CONF_WEBHOOK_ID] + ), enabled=config_entry.data[const.CONF_USE_WEBHOOK], ), ) diff --git a/homeassistant/components/withings/const.py b/homeassistant/components/withings/const.py index dd744152bb1..a5a99c193ab 100644 --- a/homeassistant/components/withings/const.py +++ b/homeassistant/components/withings/const.py @@ -13,7 +13,6 @@ DOMAIN = "withings" LOG_NAMESPACE = "homeassistant.components.withings" PROFILE = "profile" PUSH_HANDLER = "push_handler" -CONF_WEBHOOK_URL = "webhook_url" class Measurement(Enum): From d01666f3a2a275a303c0dc3ba2ded89f653dea42 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 30 Apr 2022 00:25:25 +0000 Subject: [PATCH 098/930] [ci skip] Translation update --- homeassistant/components/deconz/translations/fr.json | 1 + .../components/deconz/translations/pt-BR.json | 1 + homeassistant/components/sabnzbd/translations/ca.json | 1 + .../components/simplisafe/translations/ca.json | 6 +++++- .../components/simplisafe/translations/de.json | 4 ++++ .../components/simplisafe/translations/et.json | 4 ++++ .../components/simplisafe/translations/hu.json | 4 ++++ .../components/simplisafe/translations/no.json | 4 ++++ .../components/simplisafe/translations/zh-Hant.json | 4 ++++ homeassistant/components/sql/translations/ca.json | 4 ++++ homeassistant/components/sql/translations/de.json | 4 ++++ homeassistant/components/sql/translations/el.json | 4 ++++ homeassistant/components/sql/translations/et.json | 4 ++++ homeassistant/components/sql/translations/fr.json | 4 ++++ homeassistant/components/sql/translations/nl.json | 2 ++ homeassistant/components/sql/translations/pt-BR.json | 4 ++++ .../components/steam_online/translations/ca.json | 10 ++++++++++ .../components/trafikverket_ferry/translations/ca.json | 2 ++ 18 files changed, 66 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/translations/fr.json b/homeassistant/components/deconz/translations/fr.json index 39a32fc2434..12f63765314 100644 --- a/homeassistant/components/deconz/translations/fr.json +++ b/homeassistant/components/deconz/translations/fr.json @@ -9,6 +9,7 @@ "updated_instance": "Instance deCONZ mise \u00e0 jour avec la nouvelle adresse d'h\u00f4te" }, "error": { + "linking_not_possible": "Impossible d'\u00e9tablir une liaison avec la passerelle", "no_key": "Impossible d'obtenir une cl\u00e9 d'API" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/pt-BR.json b/homeassistant/components/deconz/translations/pt-BR.json index 03004cae304..fbbd776cbe8 100644 --- a/homeassistant/components/deconz/translations/pt-BR.json +++ b/homeassistant/components/deconz/translations/pt-BR.json @@ -9,6 +9,7 @@ "updated_instance": "Atualiza\u00e7\u00e3o da inst\u00e2ncia deCONZ com novo endere\u00e7o de host" }, "error": { + "linking_not_possible": "N\u00e3o foi poss\u00edvel se conectar com o gateway", "no_key": "N\u00e3o foi poss\u00edvel obter uma chave de API" }, "flow_title": "{host}", diff --git a/homeassistant/components/sabnzbd/translations/ca.json b/homeassistant/components/sabnzbd/translations/ca.json index 6c980e23fa1..d1947bb05f5 100644 --- a/homeassistant/components/sabnzbd/translations/ca.json +++ b/homeassistant/components/sabnzbd/translations/ca.json @@ -9,6 +9,7 @@ "data": { "api_key": "Clau API", "name": "Nom", + "path": "Ruta", "url": "URL" } } diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index 4392a99ffea..a89b06bfcfc 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Aquest compte SimpliSafe ja est\u00e0 en \u00fas.", + "email_2fa_timed_out": "S'ha esgotat el temps d'espera de l'autenticaci\u00f3 de dos factors a trav\u00e9s de correu electr\u00f2nic.", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", "wrong_account": "Les credencials d'usuari proporcionades no coincideixen amb les d'aquest compte SimpliSafe." }, @@ -11,6 +12,9 @@ "still_awaiting_mfa": "Esperant clic de l'enlla\u00e7 del correu MFA", "unknown": "Error inesperat" }, + "progress": { + "email_2fa": "Introdueix el codi d'autenticaci\u00f3 de dos factors que s'ha enviat al teu correu." + }, "step": { "mfa": { "title": "Autenticaci\u00f3 multi-factor SimpliSafe" @@ -32,7 +36,7 @@ "auth_code": "Codi d'autoritzaci\u00f3", "code": "Codi (utilitzat a la UI de Home Assistant)", "password": "Contrasenya", - "username": "Correu electr\u00f2nic" + "username": "Nom d'usuari" }, "description": "SimpliSafe s'autentica amb Home Assistant a trav\u00e9s de la seva aplicaci\u00f3 web. A causa de les limitacions t\u00e8cniques, hi ha un pas manual al final d'aquest proc\u00e9s; assegura't de llegir la [documentaci\u00f3]({docs_url}) abans de comen\u00e7ar.\n\n1. Fes clic [aqu\u00ed]({url}) per obrir l'aplicaci\u00f3 web de SimpliSafe i introdueix les teves credencials.\n\n2. Quan el proc\u00e9s d'inici de sessi\u00f3 s'hagi completat, torna aqu\u00ed i introdueix a sota el codi d'autoritzaci\u00f3.", "title": "Introdueix la teva informaci\u00f3" diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index 51083565770..eb23adaabba 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Dieses SimpliSafe-Konto wird bereits verwendet.", + "email_2fa_timed_out": "Zeit\u00fcberschreitung beim Warten auf E-Mail-basierte Zwei-Faktor-Authentifizierung.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "wrong_account": "Die angegebenen Benutzeranmeldeinformationen stimmen nicht mit diesem SimpliSafe-Konto \u00fcberein." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Immernoch warten auf MFA-E-Mail-Klick", "unknown": "Unerwarteter Fehler" }, + "progress": { + "email_2fa": "Gib den Code f\u00fcr die Zwei-Faktor-Authentifizierung ein, den du per E-Mail erhalten hast." + }, "step": { "mfa": { "title": "SimpliSafe Multi-Faktor-Authentifizierung" diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index 83c441de9f9..8931c2250ec 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "See SimpliSafe'i konto on juba kasutusel.", + "email_2fa_timed_out": "Meilip\u00f5hise kahefaktorilise autentimise ajal\u00f5pp.", "reauth_successful": "Taastuvastamine \u00f5nnestus", "wrong_account": "Esitatud kasutaja mandaadid ei \u00fchti selle SimpliSafe kontoga." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Ootan endiselt MFA e-posti klikki", "unknown": "Tundmatu viga" }, + "progress": { + "email_2fa": "Sisesta e-kirjaga saadetud kahefaktoriline autentimiskood." + }, "step": { "mfa": { "title": "SimpliSafe mitmeastmeline autentimine" diff --git a/homeassistant/components/simplisafe/translations/hu.json b/homeassistant/components/simplisafe/translations/hu.json index e1b8769f4b6..6a0e6d33642 100644 --- a/homeassistant/components/simplisafe/translations/hu.json +++ b/homeassistant/components/simplisafe/translations/hu.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Ez a SimpliSafe-fi\u00f3k m\u00e1r haszn\u00e1latban van.", + "email_2fa_timed_out": "Az e-mail alap\u00fa k\u00e9tfaktoros hiteles\u00edt\u00e9sre val\u00f3 v\u00e1rakoz\u00e1s ideje lej\u00e1rt", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "wrong_account": "A megadott felhaszn\u00e1l\u00f3i hiteles\u00edt\u0151 adatok nem j\u00f3k ehhez a SimpliSafe fi\u00f3khoz." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "M\u00e9g v\u00e1r az MFA e-mail kattint\u00e1sra", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, + "progress": { + "email_2fa": "Adja meg a k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3dot amelyet e-mailben kapott." + }, "step": { "mfa": { "title": "SimpliSafe t\u00f6bbt\u00e9nyez\u0151s hiteles\u00edt\u00e9s" diff --git a/homeassistant/components/simplisafe/translations/no.json b/homeassistant/components/simplisafe/translations/no.json index 6527f2e4ae2..d0b743e5ff5 100644 --- a/homeassistant/components/simplisafe/translations/no.json +++ b/homeassistant/components/simplisafe/translations/no.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Denne SimpliSafe-kontoen er allerede i bruk.", + "email_2fa_timed_out": "Tidsavbrudd mens du ventet p\u00e5 e-postbasert tofaktorautentisering.", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", "wrong_account": "Brukerlegitimasjonen som er oppgitt, samsvarer ikke med denne SimpliSafe -kontoen." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Forventer fortsatt MFA-e-postklikk", "unknown": "Uventet feil" }, + "progress": { + "email_2fa": "Skriv inn tofaktorautentiseringskoden\n sendt til deg via e-post." + }, "step": { "mfa": { "title": "SimpliSafe flertrinnsbekreftelse" diff --git a/homeassistant/components/simplisafe/translations/zh-Hant.json b/homeassistant/components/simplisafe/translations/zh-Hant.json index 1b10153ab05..649378c1d2c 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hant.json +++ b/homeassistant/components/simplisafe/translations/zh-Hant.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u6b64 SimpliSafe \u5e33\u865f\u5df2\u88ab\u4f7f\u7528\u3002", + "email_2fa_timed_out": "\u7b49\u5f85\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u90f5\u4ef6\u903e\u6642\u3002", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", "wrong_account": "\u6240\u4ee5\u63d0\u4f9b\u7684\u6191\u8b49\u8207 Simplisafe \u5e33\u865f\u4e0d\u7b26\u3002" }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "\u4ecd\u5728\u7b49\u5019\u9ede\u64ca\u591a\u6b65\u9a5f\u8a8d\u8b49\u90f5\u4ef6", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, + "progress": { + "email_2fa": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230\u7684\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u3002" + }, "step": { "mfa": { "title": "SimpliSafe \u591a\u6b65\u9a5f\u9a57\u8b49" diff --git a/homeassistant/components/sql/translations/ca.json b/homeassistant/components/sql/translations/ca.json index d69f22ca855..a579253e8d7 100644 --- a/homeassistant/components/sql/translations/ca.json +++ b/homeassistant/components/sql/translations/ca.json @@ -13,6 +13,7 @@ "data": { "column": "Columna", "db_url": "URL de la base de dades", + "name": "Nom", "query": "Selecciona la consulta", "unit_of_measurement": "Unitat de mesura", "value_template": "Plantilla de valor" @@ -20,6 +21,7 @@ "data_description": { "column": "Columna de resposta de la consulta per presentar com a estat", "db_url": "URL de la base de dades, deixa-ho en blanc per utilitzar la predeterminada de HA", + "name": "Nom que s'utilitzar\u00e0 per a l'entrada de configuraci\u00f3 i tamb\u00e9 pel sensor", "query": "Consulta a executar, ha de comen\u00e7ar per 'SELECT'", "unit_of_measurement": "Unitat de mesura (opcional)", "value_template": "Plantilla de valor (opcional)" @@ -38,6 +40,7 @@ "data": { "column": "Columna", "db_url": "URL de la base de dades", + "name": "Nom", "query": "Selecciona la consulta", "unit_of_measurement": "Unitat de mesura", "value_template": "Plantilla de valor" @@ -45,6 +48,7 @@ "data_description": { "column": "Columna de resposta de la consulta per presentar com a estat", "db_url": "URL de la base de dades, deixa-ho en blanc per utilitzar la predeterminada de HA", + "name": "Nom que s'utilitzar\u00e0 per a l'entrada de configuraci\u00f3 i tamb\u00e9 pel sensor", "query": "Consulta a executar, ha de comen\u00e7ar per 'SELECT'", "unit_of_measurement": "Unitat de mesura (opcional)", "value_template": "Plantilla de valor (opcional)" diff --git a/homeassistant/components/sql/translations/de.json b/homeassistant/components/sql/translations/de.json index 2207997133d..53642f5bede 100644 --- a/homeassistant/components/sql/translations/de.json +++ b/homeassistant/components/sql/translations/de.json @@ -13,6 +13,7 @@ "data": { "column": "Spalte", "db_url": "Datenbank-URL", + "name": "Name", "query": "Abfrage ausw\u00e4hlen", "unit_of_measurement": "Ma\u00dfeinheit", "value_template": "Wertvorlage" @@ -20,6 +21,7 @@ "data_description": { "column": "Spalte f\u00fcr die zur\u00fcckgegebene Abfrage, die als Status angezeigt werden soll", "db_url": "Datenbank-URL, leer lassen, um die Standard-HA-Datenbank zu verwenden", + "name": "Name, der f\u00fcr den Konfigurationseintrag und auch f\u00fcr den Sensor verwendet wird", "query": "Auszuf\u00fchrende Abfrage, muss mit 'SELECT' beginnen", "unit_of_measurement": "Ma\u00dfeinheit (optional)", "value_template": "Wertvorlage (optional)" @@ -38,6 +40,7 @@ "data": { "column": "Spalte", "db_url": "Datenbank-URL", + "name": "Name", "query": "Abfrage ausw\u00e4hlen", "unit_of_measurement": "Ma\u00dfeinheit", "value_template": "Wertvorlage" @@ -45,6 +48,7 @@ "data_description": { "column": "Spalte f\u00fcr die zur\u00fcckgegebene Abfrage, die als Status angezeigt werden soll", "db_url": "Datenbank-URL, leer lassen, um die Standard-HA-Datenbank zu verwenden", + "name": "Name, der f\u00fcr den Konfigurationseintrag und auch f\u00fcr den Sensor verwendet wird", "query": "Auszuf\u00fchrende Abfrage, muss mit 'SELECT' beginnen", "unit_of_measurement": "Ma\u00dfeinheit (optional)", "value_template": "Wertvorlage (optional)" diff --git a/homeassistant/components/sql/translations/el.json b/homeassistant/components/sql/translations/el.json index 4f19401a8f3..85326cb886e 100644 --- a/homeassistant/components/sql/translations/el.json +++ b/homeassistant/components/sql/translations/el.json @@ -13,6 +13,7 @@ "data": { "column": "\u03a3\u03c4\u03ae\u03bb\u03b7", "db_url": "URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "query": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03c1\u03c9\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03b1\u03be\u03af\u03b1\u03c2" @@ -20,6 +21,7 @@ "data_description": { "column": "\u03a3\u03c4\u03ae\u03bb\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03b5\u03c6\u03cc\u03bc\u03b5\u03bd\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03b1\u03c3\u03c4\u03b5\u03af \u03c9\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", "db_url": "URL \u03c4\u03b7\u03c2 \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b2\u03ac\u03c3\u03b7 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd HA", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b9\u03c3\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03c0\u03af\u03c3\u03b7\u03c2 \u03c4\u03bf\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "query": "\u03a4\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03b5\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ac \u03bc\u03b5 'SELECT'", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" @@ -38,6 +40,7 @@ "data": { "column": "\u03a3\u03c4\u03ae\u03bb\u03b7", "db_url": "URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "query": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b5\u03c1\u03c9\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" @@ -45,6 +48,7 @@ "data_description": { "column": "\u03a3\u03c4\u03ae\u03bb\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03b5\u03c6\u03cc\u03bc\u03b5\u03bd\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03b1\u03c3\u03c4\u03b5\u03af \u03c9\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", "db_url": "URL \u03c4\u03b7\u03c2 \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd, \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03b2\u03ac\u03c3\u03b7 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd HA", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b9\u03c3\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03c0\u03af\u03c3\u03b7\u03c2 \u03c4\u03bf\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1", "query": "\u03a4\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b5\u03ba\u03c4\u03b5\u03bb\u03b5\u03c3\u03c4\u03b5\u03af \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ac \u03bc\u03b5 'SELECT'", "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", "value_template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2 (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" diff --git a/homeassistant/components/sql/translations/et.json b/homeassistant/components/sql/translations/et.json index b1f7339aba2..62bbd96b63e 100644 --- a/homeassistant/components/sql/translations/et.json +++ b/homeassistant/components/sql/translations/et.json @@ -13,6 +13,7 @@ "data": { "column": "Veerg", "db_url": "Andmebaasi URL", + "name": "Nimi", "query": "Vali p\u00e4ring", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "value_template": "V\u00e4\u00e4rtuse mall" @@ -20,6 +21,7 @@ "data_description": { "column": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks", "db_url": "Andmebaasi URL, j\u00e4ta t\u00fchjaks, et kasutada HA vaikeandmebaasi", + "name": "Nimi, mida kasutatakse Config Entry'i ja ka anduri jaoks", "query": "K\u00e4ivitatav p\u00e4ring peab algama 'SELECT'-iga.", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik (valikuline)", "value_template": "V\u00e4\u00e4rtuse mall (valikuline)" @@ -38,6 +40,7 @@ "data": { "column": "Veerg", "db_url": "Andmebaasi URL", + "name": "Nimi", "query": "Vali p\u00e4ring", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik", "value_template": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks" @@ -45,6 +48,7 @@ "data_description": { "column": "Tagastatud p\u00e4ringu veerg olekuna esitamiseks", "db_url": "Andmebaasi URL, j\u00e4ta t\u00fchjaks, et kasutada HA vaikeandmebaasi", + "name": "Nimi, mida kasutatakse Config Entry'i ja ka anduri jaoks", "query": "K\u00e4ivitatav p\u00e4ring peab algama 'SELECT'-iga.", "unit_of_measurement": "M\u00f5\u00f5t\u00fchik (valikuline)", "value_template": "V\u00e4\u00e4rtuse mall (valikuline)" diff --git a/homeassistant/components/sql/translations/fr.json b/homeassistant/components/sql/translations/fr.json index 078fdc44d24..f7607845119 100644 --- a/homeassistant/components/sql/translations/fr.json +++ b/homeassistant/components/sql/translations/fr.json @@ -13,6 +13,7 @@ "data": { "column": "Colonne", "db_url": "URL de la base de donn\u00e9es", + "name": "Nom", "query": "Requ\u00eate de s\u00e9lection", "unit_of_measurement": "Unit\u00e9 de mesure", "value_template": "Mod\u00e8le de valeur" @@ -20,6 +21,7 @@ "data_description": { "column": "La colonne de la r\u00e9ponse \u00e0 la requ\u00eate \u00e0 pr\u00e9senter en tant qu'\u00e9tat", "db_url": "L'URL de la base de donn\u00e9es, laisser vide pour utiliser la base de donn\u00e9es HA par d\u00e9faut", + "name": "Le nom qui sera utilis\u00e9 pour l'entr\u00e9e de configuration ainsi que pour le capteur", "query": "La requ\u00eate \u00e0 ex\u00e9cuter, doit commencer par \u00ab\u00a0SELECT\u00a0\u00bb", "unit_of_measurement": "Unit\u00e9 de mesure (facultatif)", "value_template": "Mod\u00e8le de valeur (facultatif)" @@ -38,6 +40,7 @@ "data": { "column": "Colonne", "db_url": "URL de la base de donn\u00e9es", + "name": "Nom", "query": "Requ\u00eate de s\u00e9lection", "unit_of_measurement": "Unit\u00e9 de mesure", "value_template": "Mod\u00e8le de valeur" @@ -45,6 +48,7 @@ "data_description": { "column": "La colonne de la r\u00e9ponse \u00e0 la requ\u00eate \u00e0 pr\u00e9senter en tant qu'\u00e9tat", "db_url": "L'URL de la base de donn\u00e9es, laisser vide pour utiliser la base de donn\u00e9es HA par d\u00e9faut", + "name": "Le nom qui sera utilis\u00e9 pour l'entr\u00e9e de configuration ainsi que pour le capteur", "query": "La requ\u00eate \u00e0 ex\u00e9cuter, doit commencer par \u00ab\u00a0SELECT\u00a0\u00bb", "unit_of_measurement": "Unit\u00e9 de mesure (facultatif)", "value_template": "Mod\u00e8le de valeur (facultatif)" diff --git a/homeassistant/components/sql/translations/nl.json b/homeassistant/components/sql/translations/nl.json index 947066307b5..c95d4b67701 100644 --- a/homeassistant/components/sql/translations/nl.json +++ b/homeassistant/components/sql/translations/nl.json @@ -13,6 +13,7 @@ "data": { "column": "Kolom", "db_url": "Database URL", + "name": "Naam", "query": "Selecteer Query", "unit_of_measurement": "Meeteenheid", "value_template": "Waarde Template" @@ -38,6 +39,7 @@ "data": { "column": "Kolom", "db_url": "Database URL", + "name": "Naam", "query": "Selecteer Query", "unit_of_measurement": "Meeteenheid", "value_template": "Waarde Template" diff --git a/homeassistant/components/sql/translations/pt-BR.json b/homeassistant/components/sql/translations/pt-BR.json index 10df43c7e1f..21a930488a6 100644 --- a/homeassistant/components/sql/translations/pt-BR.json +++ b/homeassistant/components/sql/translations/pt-BR.json @@ -13,6 +13,7 @@ "data": { "column": "Coluna", "db_url": "URL do banco de dados", + "name": "Nome", "query": "Selecionar consulta", "unit_of_measurement": "Unidade de medida", "value_template": "Modelo do valor" @@ -20,6 +21,7 @@ "data_description": { "column": "Coluna para a consulta retornada para apresentar como estado", "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", + "name": "Nome que ser\u00e1 usado para Config Entry e tamb\u00e9m para o Sensor", "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", "unit_of_measurement": "Unidade de medida (opcional)", "value_template": "Modelo do valor (opcional)" @@ -38,6 +40,7 @@ "data": { "column": "Coluna", "db_url": "URL do banco de dados", + "name": "Nome", "query": "Selecionar consulta", "unit_of_measurement": "Unidade de medida", "value_template": "Modelo do valor" @@ -45,6 +48,7 @@ "data_description": { "column": "Coluna para a consulta retornada para apresentar como estado", "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", + "name": "Nome que ser\u00e1 usado para Config Entry e tamb\u00e9m para o Sensor", "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", "unit_of_measurement": "Unidade de medida (opcional)", "value_template": "Modelo do valor (opcional)" diff --git a/homeassistant/components/steam_online/translations/ca.json b/homeassistant/components/steam_online/translations/ca.json index 22a8a83a896..4535fe6d31c 100644 --- a/homeassistant/components/steam_online/translations/ca.json +++ b/homeassistant/components/steam_online/translations/ca.json @@ -16,9 +16,19 @@ }, "user": { "data": { + "account": "ID del compte de Steam", "api_key": "Clau API" } } } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Noms dels comptes a controlar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/ca.json b/homeassistant/components/trafikverket_ferry/translations/ca.json index b3983a45e2b..8780b02ee37 100644 --- a/homeassistant/components/trafikverket_ferry/translations/ca.json +++ b/homeassistant/components/trafikverket_ferry/translations/ca.json @@ -17,6 +17,8 @@ "user": { "data": { "api_key": "Clau API", + "from": "Des del port", + "to": "Al port", "weekday": "Laborables" } } From 0360613dddc2a42874bd5faafcfc753d100df7a1 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Fri, 29 Apr 2022 21:17:03 -0400 Subject: [PATCH 099/930] Cleanup eight_sleep (#69171) * Cleanup eight_sleep * Only set data after checking for users --- .../components/eight_sleep/__init__.py | 204 +++++------------- .../components/eight_sleep/binary_sensor.py | 47 ++-- homeassistant/components/eight_sleep/const.py | 19 ++ .../components/eight_sleep/sensor.py | 117 +++++----- 4 files changed, 150 insertions(+), 237 deletions(-) create mode 100644 homeassistant/components/eight_sleep/const.py diff --git a/homeassistant/components/eight_sleep/__init__.py b/homeassistant/components/eight_sleep/__init__.py index 3495d1da28b..eece6e69ed5 100644 --- a/homeassistant/components/eight_sleep/__init__.py +++ b/homeassistant/components/eight_sleep/__init__.py @@ -3,20 +3,12 @@ from __future__ import annotations from datetime import timedelta import logging -from typing import Union from pyeight.eight import EightSleep from pyeight.user import EightUser import voluptuous as vol -from homeassistant.const import ( - ATTR_ENTITY_ID, - CONF_BINARY_SENSORS, - CONF_PASSWORD, - CONF_SENSORS, - CONF_USERNAME, - Platform, -) +from homeassistant.const import ATTR_ENTITY_ID, CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import discovery from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -27,44 +19,24 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) +from .const import ( + ATTR_HEAT_DURATION, + ATTR_TARGET_HEAT, + DATA_API, + DATA_HEAT, + DATA_USER, + DOMAIN, + NAME_MAP, + SERVICE_HEAT_SET, +) + _LOGGER = logging.getLogger(__name__) -DATA_EIGHT = "eight_sleep" -DATA_HEAT = "heat" -DATA_USER = "user" -DATA_API = "api" -DOMAIN = "eight_sleep" - -HEAT_ENTITY = "heat" -USER_ENTITY = "user" - +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] HEAT_SCAN_INTERVAL = timedelta(seconds=60) USER_SCAN_INTERVAL = timedelta(seconds=300) -NAME_MAP = { - "left_current_sleep": "Left Sleep Session", - "left_current_sleep_fitness": "Left Sleep Fitness", - "left_last_sleep": "Left Previous Sleep Session", - "right_current_sleep": "Right Sleep Session", - "right_current_sleep_fitness": "Right Sleep Fitness", - "right_last_sleep": "Right Previous Sleep Session", -} - -SENSORS = [ - "current_sleep", - "current_sleep_fitness", - "last_sleep", - "bed_state", - "bed_temperature", - "sleep_stage", -] - -SERVICE_HEAT_SET = "heat_set" - -ATTR_TARGET_HEAT = "target" -ATTR_HEAT_DURATION = "duration" - VALID_TARGET_HEAT = vol.All(vol.Coerce(int), vol.Clamp(min=-100, max=100)) VALID_DURATION = vol.All(vol.Coerce(int), vol.Clamp(min=0, max=28800)) @@ -107,15 +79,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: user = conf[CONF_USERNAME] password = conf[CONF_PASSWORD] - if hass.config.time_zone is None: - _LOGGER.error("Timezone is not set in Home Assistant") - return False + eight = EightSleep( + user, password, hass.config.time_zone, async_get_clientsession(hass) + ) - timezone = str(hass.config.time_zone) - - eight = EightSleep(user, password, timezone, async_get_clientsession(hass)) - - hass.data.setdefault(DATA_EIGHT, {})[DATA_API] = eight + hass.data.setdefault(DOMAIN, {}) # Authenticate, build sensors success = await eight.start() @@ -123,43 +91,37 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Authentication failed, cannot continue return False - heat_coordinator = hass.data[DOMAIN][DATA_HEAT] = EightSleepHeatDataCoordinator( - hass, eight + heat_coordinator: DataUpdateCoordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"{DOMAIN}_heat", + update_interval=HEAT_SCAN_INTERVAL, + update_method=eight.update_device_data, ) - user_coordinator = hass.data[DOMAIN][DATA_USER] = EightSleepUserDataCoordinator( - hass, eight + user_coordinator: DataUpdateCoordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"{DOMAIN}_user", + update_interval=USER_SCAN_INTERVAL, + update_method=eight.update_user_data, ) await heat_coordinator.async_config_entry_first_refresh() await user_coordinator.async_config_entry_first_refresh() - # Load sub components - sensors = [] - binary_sensors = [] - if eight.users: - for user, obj in eight.users.items(): - for sensor in SENSORS: - sensors.append((obj.side, sensor)) - binary_sensors.append((obj.side, "bed_presence")) - sensors.append((None, "room_temperature")) - else: + if not eight.users: # No users, cannot continue return False - hass.async_create_task( - discovery.async_load_platform( - hass, Platform.SENSOR, DOMAIN, {CONF_SENSORS: sensors}, config - ) - ) + hass.data[DOMAIN] = { + DATA_API: eight, + DATA_HEAT: heat_coordinator, + DATA_USER: user_coordinator, + } - hass.async_create_task( - discovery.async_load_platform( - hass, - Platform.BINARY_SENSOR, - DOMAIN, - {CONF_BINARY_SENSORS: binary_sensors}, - config, + for platform in PLATFORMS: + hass.async_create_task( + discovery.async_load_platform(hass, platform, DOMAIN, {}, config) ) - ) async def async_service_handler(service: ServiceCall) -> None: """Handle eight sleep service calls.""" @@ -185,88 +147,32 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -class EightSleepHeatDataCoordinator(DataUpdateCoordinator): - """Class to retrieve heat data from Eight Sleep.""" - - def __init__(self, hass: HomeAssistant, api: EightSleep) -> None: - """Initialize coordinator.""" - self.api = api - super().__init__( - hass, - _LOGGER, - name=f"{DOMAIN}_heat", - update_interval=HEAT_SCAN_INTERVAL, - ) - - async def _async_update_data(self) -> None: - await self.api.update_device_data() - - -class EightSleepUserDataCoordinator(DataUpdateCoordinator): - """Class to retrieve user data from Eight Sleep.""" - - def __init__(self, hass: HomeAssistant, api: EightSleep) -> None: - """Initialize coordinator.""" - self.api = api - super().__init__( - hass, - _LOGGER, - name=f"{DOMAIN}_user", - update_interval=USER_SCAN_INTERVAL, - ) - - async def _async_update_data(self) -> None: - await self.api.update_user_data() - - -class EightSleepBaseEntity( - CoordinatorEntity[ - Union[EightSleepUserDataCoordinator, EightSleepHeatDataCoordinator] - ] -): +class EightSleepBaseEntity(CoordinatorEntity[DataUpdateCoordinator]): """The base Eight Sleep entity class.""" def __init__( self, - name: str, - coordinator: EightSleepUserDataCoordinator | EightSleepHeatDataCoordinator, + coordinator: DataUpdateCoordinator, eight: EightSleep, - side: str | None, + user_id: str | None, sensor: str, + units: str | None = None, ) -> None: """Initialize the data object.""" super().__init__(coordinator) self._eight = eight - self._side = side + self._user_id = user_id self._sensor = sensor - self._usrobj: EightUser | None = None - if self._side: - self._usrobj = self._eight.users[self._eight.fetch_userid(self._side)] - full_sensor_name = self._sensor - if self._side is not None: - full_sensor_name = f"{self._side}_{full_sensor_name}" - mapped_name = NAME_MAP.get( - full_sensor_name, full_sensor_name.replace("_", " ").title() - ) - - self._attr_name = f"{name} {mapped_name}" - self._attr_unique_id = ( - f"{_get_device_unique_id(eight, self._usrobj)}.{self._sensor}" - ) - - -class EightSleepUserEntity(EightSleepBaseEntity): - """The Eight Sleep user entity.""" - - def __init__( - self, - name: str, - coordinator: EightSleepUserDataCoordinator, - eight: EightSleep, - side: str | None, - sensor: str, - units: str, - ) -> None: - """Initialize the data object.""" - super().__init__(name, coordinator, eight, side, sensor) self._units = units + self._user_obj: EightUser | None = None + if self._user_id: + self._user_obj = self._eight.users[user_id] + + mapped_name = NAME_MAP.get(sensor, sensor.replace("_", " ").title()) + if self._user_obj is not None: + mapped_name = f"{self._user_obj.side.title()} {mapped_name}" + + self._attr_name = f"Eight {mapped_name}" + self._attr_unique_id = ( + f"{_get_device_unique_id(eight, self._user_obj)}.{sensor}" + ) diff --git a/homeassistant/components/eight_sleep/binary_sensor.py b/homeassistant/components/eight_sleep/binary_sensor.py index a13a0fd840d..a63389ef3f9 100644 --- a/homeassistant/components/eight_sleep/binary_sensor.py +++ b/homeassistant/components/eight_sleep/binary_sensor.py @@ -12,15 +12,10 @@ from homeassistant.components.binary_sensor import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from . import ( - CONF_BINARY_SENSORS, - DATA_API, - DATA_EIGHT, - DATA_HEAT, - EightSleepBaseEntity, - EightSleepHeatDataCoordinator, -) +from . import EightSleepBaseEntity +from .const import DATA_API, DATA_HEAT, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -35,17 +30,16 @@ async def async_setup_platform( if discovery_info is None: return - name = "Eight" - sensors = discovery_info[CONF_BINARY_SENSORS] - eight: EightSleep = hass.data[DATA_EIGHT][DATA_API] - heat_coordinator: EightSleepHeatDataCoordinator = hass.data[DATA_EIGHT][DATA_HEAT] + eight: EightSleep = hass.data[DOMAIN][DATA_API] + heat_coordinator: DataUpdateCoordinator = hass.data[DOMAIN][DATA_HEAT] - all_sensors = [ - EightHeatSensor(name, heat_coordinator, eight, side, sensor) - for side, sensor in sensors - ] + entities = [] + for user in eight.users.values(): + entities.append( + EightHeatSensor(heat_coordinator, eight, user.userid, "bed_presence") + ) - async_add_entities(all_sensors) + async_add_entities(entities) class EightHeatSensor(EightSleepBaseEntity, BinarySensorEntity): @@ -53,25 +47,24 @@ class EightHeatSensor(EightSleepBaseEntity, BinarySensorEntity): def __init__( self, - name: str, - coordinator: EightSleepHeatDataCoordinator, + coordinator: DataUpdateCoordinator, eight: EightSleep, - side: str | None, + user_id: str | None, sensor: str, ) -> None: """Initialize the sensor.""" - super().__init__(name, coordinator, eight, side, sensor) + super().__init__(coordinator, eight, user_id, sensor) self._attr_device_class = BinarySensorDeviceClass.OCCUPANCY - assert self._usrobj + assert self._user_obj _LOGGER.debug( "Presence Sensor: %s, Side: %s, User: %s", - self._sensor, - self._side, - self._usrobj.userid, + sensor, + self._user_obj.side, + user_id, ) @property def is_on(self) -> bool: """Return true if the binary sensor is on.""" - assert self._usrobj - return bool(self._usrobj.bed_presence) + assert self._user_obj + return bool(self._user_obj.bed_presence) diff --git a/homeassistant/components/eight_sleep/const.py b/homeassistant/components/eight_sleep/const.py new file mode 100644 index 00000000000..42a9eea590e --- /dev/null +++ b/homeassistant/components/eight_sleep/const.py @@ -0,0 +1,19 @@ +"""Eight Sleep constants.""" +DATA_HEAT = "heat" +DATA_USER = "user" +DATA_API = "api" +DOMAIN = "eight_sleep" + +HEAT_ENTITY = "heat" +USER_ENTITY = "user" + +NAME_MAP = { + "current_sleep": "Sleep Session", + "current_sleep_fitness": "Sleep Fitness", + "last_sleep": "Previous Sleep Session", +} + +SERVICE_HEAT_SET = "heat_set" + +ATTR_TARGET_HEAT = "target" +ATTR_HEAT_DURATION = "duration" diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index 5be36c0024e..145292d6728 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -11,18 +11,10 @@ from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from . import ( - CONF_SENSORS, - DATA_API, - DATA_EIGHT, - DATA_HEAT, - DATA_USER, - EightSleepBaseEntity, - EightSleepHeatDataCoordinator, - EightSleepUserDataCoordinator, - EightSleepUserEntity, -) +from . import EightSleepBaseEntity +from .const import DATA_API, DATA_HEAT, DATA_USER, DOMAIN ATTR_ROOM_TEMP = "Room Temperature" ATTR_AVG_ROOM_TEMP = "Average Room Temperature" @@ -51,6 +43,16 @@ ATTR_FIT_WAKEUP_SCORE = "Fitness Wakeup Score" _LOGGER = logging.getLogger(__name__) +EIGHT_USER_SENSORS = [ + "current_sleep", + "current_sleep_fitness", + "last_sleep", + "bed_temperature", + "sleep_stage", +] +EIGHT_HEAT_SENSORS = ["bed_state"] +EIGHT_ROOM_SENSORS = ["room_temperature"] + async def async_setup_platform( hass: HomeAssistant, @@ -62,11 +64,9 @@ async def async_setup_platform( if discovery_info is None: return - name = "Eight" - sensors = discovery_info[CONF_SENSORS] - eight: EightSleep = hass.data[DATA_EIGHT][DATA_API] - heat_coordinator: EightSleepHeatDataCoordinator = hass.data[DATA_EIGHT][DATA_HEAT] - user_coordinator: EightSleepUserDataCoordinator = hass.data[DATA_EIGHT][DATA_USER] + eight: EightSleep = hass.data[DOMAIN][DATA_API] + heat_coordinator: DataUpdateCoordinator = hass.data[DOMAIN][DATA_HEAT] + user_coordinator: DataUpdateCoordinator = hass.data[DOMAIN][DATA_USER] if hass.config.units.is_metric: units = "si" @@ -75,19 +75,17 @@ async def async_setup_platform( all_sensors: list[SensorEntity] = [] - for side, sensor in sensors: - if sensor == "bed_state": + for obj in eight.users.values(): + for sensor in EIGHT_USER_SENSORS: all_sensors.append( - EightHeatSensor(name, heat_coordinator, eight, side, sensor) + EightUserSensor(user_coordinator, eight, obj.userid, sensor, units) ) - elif sensor == "room_temperature": + for sensor in EIGHT_HEAT_SENSORS: all_sensors.append( - EightRoomSensor(name, user_coordinator, eight, side, sensor, units) - ) - else: - all_sensors.append( - EightUserSensor(name, user_coordinator, eight, side, sensor, units) + EightHeatSensor(heat_coordinator, eight, obj.userid, sensor) ) + for sensor in EIGHT_ROOM_SENSORS: + all_sensors.append(EightRoomSensor(user_coordinator, eight, sensor, units)) async_add_entities(all_sensors) @@ -97,38 +95,37 @@ class EightHeatSensor(EightSleepBaseEntity, SensorEntity): def __init__( self, - name: str, - coordinator: EightSleepHeatDataCoordinator, + coordinator: DataUpdateCoordinator, eight: EightSleep, - side: str | None, + user_id: str, sensor: str, ) -> None: """Initialize the sensor.""" - super().__init__(name, coordinator, eight, side, sensor) + super().__init__(coordinator, eight, user_id, sensor) self._attr_native_unit_of_measurement = PERCENTAGE - assert self._usrobj + assert self._user_obj _LOGGER.debug( "Heat Sensor: %s, Side: %s, User: %s", self._sensor, - self._side, - self._usrobj.userid, + self._user_obj.side, + self._user_id, ) @property def native_value(self) -> int: """Return the state of the sensor.""" - assert self._usrobj - return self._usrobj.heating_level + assert self._user_obj + return self._user_obj.heating_level @property def extra_state_attributes(self) -> dict[str, Any]: """Return device state attributes.""" - assert self._usrobj + assert self._user_obj return { - ATTR_TARGET_HEAT: self._usrobj.target_heating_level, - ATTR_ACTIVE_HEAT: self._usrobj.now_heating, - ATTR_DURATION_HEAT: self._usrobj.heating_remaining, + ATTR_TARGET_HEAT: self._user_obj.target_heating_level, + ATTR_ACTIVE_HEAT: self._user_obj.now_heating, + ATTR_DURATION_HEAT: self._user_obj.heating_remaining, } @@ -142,20 +139,20 @@ def _get_breakdown_percent( return 0 -class EightUserSensor(EightSleepUserEntity, SensorEntity): +class EightUserSensor(EightSleepBaseEntity, SensorEntity): """Representation of an eight sleep user-based sensor.""" def __init__( self, - name: str, - coordinator: EightSleepUserDataCoordinator, + coordinator: DataUpdateCoordinator, eight: EightSleep, - side: str | None, + user_id: str, sensor: str, units: str, ) -> None: """Initialize the sensor.""" - super().__init__(name, coordinator, eight, side, sensor, units) + super().__init__(coordinator, eight, user_id, sensor, units) + assert self._user_obj if self._sensor == "bed_temperature": self._attr_icon = "mdi:thermometer" @@ -163,26 +160,26 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity): _LOGGER.debug( "User Sensor: %s, Side: %s, User: %s", self._sensor, - self._side, - self._usrobj.userid if self._usrobj else None, + self._user_obj.side, + self._user_id, ) @property def native_value(self) -> str | int | float | None: """Return the state of the sensor.""" - if not self._usrobj: + if not self._user_obj: return None if "current" in self._sensor: if "fitness" in self._sensor: - return self._usrobj.current_sleep_fitness_score - return self._usrobj.current_sleep_score + return self._user_obj.current_sleep_fitness_score + return self._user_obj.current_sleep_score if "last" in self._sensor: - return self._usrobj.last_sleep_score + return self._user_obj.last_sleep_score if self._sensor == "bed_temperature": - temp = self._usrobj.current_values["bed_temp"] + temp = self._user_obj.current_values["bed_temp"] try: if self._units == "si": return round(temp, 2) @@ -191,7 +188,7 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity): return None if self._sensor == "sleep_stage": - return self._usrobj.current_values["stage"] + return self._user_obj.current_values["stage"] return None @@ -221,13 +218,13 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity): def extra_state_attributes(self) -> dict[str, Any] | None: """Return device state attributes.""" attr = None - if "current" in self._sensor and self._usrobj: + if "current" in self._sensor and self._user_obj: if "fitness" in self._sensor: - attr = self._usrobj.current_fitness_values + attr = self._user_obj.current_fitness_values else: - attr = self._usrobj.current_values - elif "last" in self._sensor and self._usrobj: - attr = self._usrobj.last_values + attr = self._user_obj.current_values + elif "last" in self._sensor and self._user_obj: + attr = self._user_obj.last_values if attr is None: # Skip attributes if sensor type doesn't support @@ -284,20 +281,18 @@ class EightUserSensor(EightSleepUserEntity, SensorEntity): return state_attr -class EightRoomSensor(EightSleepUserEntity, SensorEntity): +class EightRoomSensor(EightSleepBaseEntity, SensorEntity): """Representation of an eight sleep room sensor.""" def __init__( self, - name: str, - coordinator: EightSleepUserDataCoordinator, + coordinator: DataUpdateCoordinator, eight: EightSleep, - side: str | None, sensor: str, units: str, ) -> None: """Initialize the sensor.""" - super().__init__(name, coordinator, eight, side, sensor, units) + super().__init__(coordinator, eight, None, sensor, units) self._attr_icon = "mdi:thermometer" self._attr_native_unit_of_measurement: str = ( From 69fa7b0d6143bab0fd4c54fe467d5691b3b176ba Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 23:24:26 -0500 Subject: [PATCH 100/930] Reduce calls to asyncio.iscoroutine (#71090) --- homeassistant/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 69e776a1dcd..7245dad7864 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -192,18 +192,15 @@ class HassJob(Generic[_R_co]): def __init__(self, target: Callable[..., _R_co]) -> None: """Create a job object.""" - if asyncio.iscoroutine(target): - raise ValueError("Coroutine not allowed to be passed to HassJob") - self.target = target - self.job_type = _get_callable_job_type(target) + self.job_type = _get_hassjob_callable_job_type(target) def __repr__(self) -> str: """Return the job.""" return f"" -def _get_callable_job_type(target: Callable[..., Any]) -> HassJobType: +def _get_hassjob_callable_job_type(target: Callable[..., Any]) -> HassJobType: """Determine the job type from the callable.""" # Check for partials to properly determine if coroutine function check_target = target @@ -214,6 +211,8 @@ def _get_callable_job_type(target: Callable[..., Any]) -> HassJobType: return HassJobType.Coroutinefunction if is_callback(check_target): return HassJobType.Callback + if asyncio.iscoroutine(check_target): + raise ValueError("Coroutine not allowed to be passed to HassJob") return HassJobType.Executor From 781ec87dff674b5f47f4138552d818cc490b5201 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Sat, 30 Apr 2022 00:26:27 -0400 Subject: [PATCH 101/930] Fix Insteon tests (#71092) --- .../insteon/fixtures/kpl_properties.json | 2 +- tests/components/insteon/mock_devices.py | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/components/insteon/fixtures/kpl_properties.json b/tests/components/insteon/fixtures/kpl_properties.json index 725432dbedc..db694e4a7eb 100644 --- a/tests/components/insteon/fixtures/kpl_properties.json +++ b/tests/components/insteon/fixtures/kpl_properties.json @@ -3,7 +3,7 @@ "program_lock_on": false, "blink_on_tx_on": false, "resume_dim_on": false, - "led_on": false, + "led_off": false, "key_beep_on": false, "rf_disable_on": false, "powerline_disable_on": false, diff --git a/tests/components/insteon/mock_devices.py b/tests/components/insteon/mock_devices.py index 6e6a8eccfcc..ef64b1e0969 100644 --- a/tests/components/insteon/mock_devices.py +++ b/tests/components/insteon/mock_devices.py @@ -1,7 +1,7 @@ """Mock devices object to test Insteon.""" import asyncio -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock, MagicMock, patch from pyinsteon.address import Address from pyinsteon.constants import ALDBStatus, ResponseStatus @@ -129,18 +129,20 @@ class MockDevices: def fill_properties(self, address, props_dict): """Fill the operating flags and extended properties of a device.""" + device = self._devices[Address(address)] operating_flags = props_dict.get("operating_flags", {}) properties = props_dict.get("properties", {}) - for flag in operating_flags: - value = operating_flags[flag] - if device.operating_flags.get(flag): - device.operating_flags[flag].load(value) - for flag in properties: - value = properties[flag] - if device.properties.get(flag): - device.properties[flag].load(value) + with patch("pyinsteon.subscriber_base.publish_topic", MagicMock()): + for flag in operating_flags: + value = operating_flags[flag] + if device.operating_flags.get(flag): + device.operating_flags[flag].load(value) + for flag in properties: + value = properties[flag] + if device.properties.get(flag): + device.properties[flag].load(value) async def async_add_device(self, address=None, multiple=False): """Mock the async_add_device method.""" From 7b682e299092cbe44956ab88e1522e8aa5d5a7d4 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sat, 30 Apr 2022 02:59:39 -0400 Subject: [PATCH 102/930] Bump zwave-js-server-python to 0.36.1 (#71096) --- homeassistant/components/zwave_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index c9edf28bd93..934c3f9a3f5 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave JS", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["zwave-js-server-python==0.36.0"], + "requirements": ["zwave-js-server-python==0.36.1"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["usb", "http", "websocket_api"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index c9900b718a8..a37f5e3dece 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2510,7 +2510,7 @@ zigpy==0.45.1 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.36.0 +zwave-js-server-python==0.36.1 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2a1ea95093d..46cab0ec1c9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1641,7 +1641,7 @@ zigpy-znp==0.7.0 zigpy==0.45.1 # homeassistant.components.zwave_js -zwave-js-server-python==0.36.0 +zwave-js-server-python==0.36.1 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 From 5e3740d5ed35e69f7413d79b02f920810d93fdfb Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sat, 30 Apr 2022 03:05:22 -0400 Subject: [PATCH 103/930] Add available property to kodi, with some code cleanup (#69115) * Add available property to kodi, with some code cleanup * Update homeassistant/components/kodi/media_player.py Co-authored-by: Shay Levy Co-authored-by: Shay Levy --- homeassistant/components/kodi/media_player.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index 0e6d51ba3cd..66bb582e6e0 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -9,7 +9,6 @@ import re from typing import Any, TypeVar import urllib.parse -import jsonrpc_base from jsonrpc_base.jsonrpc import ProtocolError, TransportError from pykodi import CannotConnectError from typing_extensions import Concatenate, ParamSpec @@ -258,10 +257,7 @@ def cmd( """Wrap all command methods.""" try: await func(obj, *args, **kwargs) - except ( - jsonrpc_base.jsonrpc.TransportError, - jsonrpc_base.jsonrpc.ProtocolError, - ) as exc: + except (TransportError, ProtocolError) as exc: # If Kodi is off, we expect calls to fail. if obj.state == STATE_OFF: log_function = _LOGGER.debug @@ -301,7 +297,6 @@ class KodiEntity(MediaPlayerEntity): """Initialize the Kodi entity.""" self._connection = connection self._kodi = kodi - self._name = name self._unique_id = uid self._players = None self._properties = {} @@ -311,6 +306,8 @@ class KodiEntity(MediaPlayerEntity): self._media_position = None self._connect_error = False + self._attr_name = name + def _reset_state(self, players=None): self._players = players self._properties = {} @@ -440,7 +437,7 @@ class KodiEntity(MediaPlayerEntity): try: await self._connection.connect() await self._on_ws_connected() - except (jsonrpc_base.jsonrpc.TransportError, CannotConnectError): + except (TransportError, CannotConnectError): if not self._connect_error: self._connect_error = True _LOGGER.warning("Unable to connect to Kodi via websocket") @@ -451,7 +448,7 @@ class KodiEntity(MediaPlayerEntity): async def _ping(self): try: await self._kodi.ping() - except (jsonrpc_base.jsonrpc.TransportError, CannotConnectError): + except (TransportError, CannotConnectError): if not self._connect_error: self._connect_error = True _LOGGER.warning("Unable to ping Kodi via websocket") @@ -528,11 +525,6 @@ class KodiEntity(MediaPlayerEntity): else: self._reset_state([]) - @property - def name(self): - """Return the name of the device.""" - return self._name - @property def should_poll(self): """Return True if entity has to be polled for state.""" @@ -644,6 +636,11 @@ class KodiEntity(MediaPlayerEntity): return None + @property + def available(self): + """Return True if entity is available.""" + return not self._connect_error + async def async_turn_on(self): """Turn the media player on.""" _LOGGER.debug("Firing event to turn on device") @@ -765,7 +762,7 @@ class KodiEntity(MediaPlayerEntity): try: result = await self._kodi.call_method(method, **kwargs) result_ok = True - except jsonrpc_base.jsonrpc.ProtocolError as exc: + except ProtocolError as exc: result = exc.args[2]["error"] _LOGGER.error( "Run API method %s.%s(%s) error: %s", @@ -774,7 +771,7 @@ class KodiEntity(MediaPlayerEntity): kwargs, result, ) - except jsonrpc_base.jsonrpc.TransportError: + except TransportError: result = None _LOGGER.warning( "TransportError trying to run API method %s.%s(%s)", From 1ede67e51f389bbe1442e3e956d1f48ce1a325ac Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Sat, 30 Apr 2022 03:43:13 -0600 Subject: [PATCH 104/930] Reflect unavailable state when litter robot hasn't been seen recently (#70810) --- .../components/litterrobot/vacuum.py | 31 +++++++++++-------- tests/components/litterrobot/conftest.py | 10 ++++++ tests/components/litterrobot/test_vacuum.py | 15 ++++++++- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/litterrobot/vacuum.py b/homeassistant/components/litterrobot/vacuum.py index fe20c6bfe50..96908c1fa9d 100644 --- a/homeassistant/components/litterrobot/vacuum.py +++ b/homeassistant/components/litterrobot/vacuum.py @@ -1,6 +1,7 @@ """Support for Litter-Robot "Vacuum".""" from __future__ import annotations +from datetime import datetime, timedelta, timezone import logging from typing import Any @@ -17,7 +18,7 @@ from homeassistant.components.vacuum import ( VacuumEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_OFF +from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -34,6 +35,19 @@ SERVICE_RESET_WASTE_DRAWER = "reset_waste_drawer" SERVICE_SET_SLEEP_MODE = "set_sleep_mode" SERVICE_SET_WAIT_TIME = "set_wait_time" +LITTER_BOX_STATUS_STATE_MAP = { + LitterBoxStatus.CLEAN_CYCLE: STATE_CLEANING, + LitterBoxStatus.EMPTY_CYCLE: STATE_CLEANING, + LitterBoxStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED, + LitterBoxStatus.CAT_SENSOR_TIMING: STATE_DOCKED, + LitterBoxStatus.DRAWER_FULL_1: STATE_DOCKED, + LitterBoxStatus.DRAWER_FULL_2: STATE_DOCKED, + LitterBoxStatus.READY: STATE_DOCKED, + LitterBoxStatus.CAT_SENSOR_INTERRUPTED: STATE_PAUSED, + LitterBoxStatus.OFF: STATE_OFF, +} +UNAVAILABLE_AFTER = timedelta(minutes=30) + async def async_setup_entry( hass: HomeAssistant, @@ -85,19 +99,10 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity): @property def state(self) -> str: """Return the state of the cleaner.""" - switcher = { - LitterBoxStatus.CLEAN_CYCLE: STATE_CLEANING, - LitterBoxStatus.EMPTY_CYCLE: STATE_CLEANING, - LitterBoxStatus.CLEAN_CYCLE_COMPLETE: STATE_DOCKED, - LitterBoxStatus.CAT_SENSOR_TIMING: STATE_DOCKED, - LitterBoxStatus.DRAWER_FULL_1: STATE_DOCKED, - LitterBoxStatus.DRAWER_FULL_2: STATE_DOCKED, - LitterBoxStatus.READY: STATE_DOCKED, - LitterBoxStatus.CAT_SENSOR_INTERRUPTED: STATE_PAUSED, - LitterBoxStatus.OFF: STATE_OFF, - } + if self.robot.last_seen < datetime.now(timezone.utc) - UNAVAILABLE_AFTER: + return STATE_UNAVAILABLE - return switcher.get(self.robot.status, STATE_ERROR) + return LITTER_BOX_STATUS_STATE_MAP.get(self.robot.status, STATE_ERROR) @property def status(self) -> str: diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index c5355a218af..839ffe2952d 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -1,6 +1,7 @@ """Configure pytest for Litter-Robot tests.""" from __future__ import annotations +from datetime import datetime from typing import Any from unittest.mock import AsyncMock, MagicMock, patch @@ -9,6 +10,7 @@ from pylitterbot.exceptions import InvalidCommandException import pytest from homeassistant.components import litterrobot +from homeassistant.components.litterrobot.vacuum import UNAVAILABLE_AFTER from homeassistant.core import HomeAssistant from .common import CONFIG, ROBOT_DATA @@ -65,6 +67,14 @@ def mock_account_with_sleeping_robot() -> MagicMock: return create_mock_account({"sleepModeActive": "102:00:00"}) +@pytest.fixture +def mock_account_with_robot_not_recently_seen() -> MagicMock: + """Mock a Litter-Robot account with a sleeping robot.""" + return create_mock_account( + {"lastSeen": (datetime.now() - UNAVAILABLE_AFTER).isoformat()} + ) + + @pytest.fixture def mock_account_with_error() -> MagicMock: """Mock a Litter-Robot account with error.""" diff --git a/tests/components/litterrobot/test_vacuum.py b/tests/components/litterrobot/test_vacuum.py index 89f8f077b55..3adf820d6aa 100644 --- a/tests/components/litterrobot/test_vacuum.py +++ b/tests/components/litterrobot/test_vacuum.py @@ -24,7 +24,7 @@ from homeassistant.components.vacuum import ( STATE_DOCKED, STATE_ERROR, ) -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.util.dt import utcnow @@ -62,6 +62,19 @@ async def test_vacuum_status_when_sleeping( assert vacuum.attributes.get(ATTR_STATUS) == "Ready (Sleeping)" +async def test_vacuum_state_when_not_recently_seen( + hass: HomeAssistant, mock_account_with_robot_not_recently_seen: MagicMock +) -> None: + """Tests the vacuum state when not seen recently.""" + await setup_integration( + hass, mock_account_with_robot_not_recently_seen, PLATFORM_DOMAIN + ) + + vacuum = hass.states.get(VACUUM_ENTITY_ID) + assert vacuum + assert vacuum.state == STATE_UNAVAILABLE + + async def test_no_robots( hass: HomeAssistant, mock_account_with_no_robots: MagicMock ) -> None: From 1cdc5f70f214002be1286d00edf0789b2bb039cc Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 30 Apr 2022 13:59:59 +0200 Subject: [PATCH 105/930] Bump pysensibo to 1.0.13 (#71105) --- homeassistant/components/sensibo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index 122fea51d6a..535824aaa19 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.12"], + "requirements": ["pysensibo==1.0.13"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index a37f5e3dece..3e0e4ef935b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1783,7 +1783,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.12 +pysensibo==1.0.13 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 46cab0ec1c9..93c76550825 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1196,7 +1196,7 @@ pyruckus==0.12 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.12 +pysensibo==1.0.13 # homeassistant.components.serial # homeassistant.components.zha From 72bc4c5ee9378f6003727be475f5941ed98bf508 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Sat, 30 Apr 2022 15:25:41 +0200 Subject: [PATCH 106/930] Update xknx to 0.21.0 (#71108) --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index 06ccba386d8..f5b1bdc6206 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.20.4"], + "requirements": ["xknx==0.21.0"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 3e0e4ef935b..67d4a7b45a7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2445,7 +2445,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.20.4 +xknx==0.21.0 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 93c76550825..4d5974047c9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1597,7 +1597,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.20.4 +xknx==0.21.0 # homeassistant.components.bluesound # homeassistant.components.fritz From 802c4c0d4223ab210b226722a2feec47b481dbec Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sat, 30 Apr 2022 16:42:22 +0200 Subject: [PATCH 107/930] Deprecate LightEntityFeature light constants (#69301) --- pylint/plugins/hass_imports.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index 5b1a76e94a5..ae9961b49dd 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -120,6 +120,10 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { reason="replaced by ColorMode enum", constant=re.compile(r"^COLOR_MODE_(\w*)$"), ), + ObsoleteImportMatch( + reason="replaced by LightEntityFeature enum", + constant=re.compile("^SUPPORT_(EFFECT|FLASH|TRANSITION)$"), + ), ], "homeassistant.components.media_player": [ ObsoleteImportMatch( From ae8604d4291f5ffde569ed604afc9b5dc81d0338 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 30 Apr 2022 17:46:27 +0300 Subject: [PATCH 108/930] Fix Litterrobot available property (#71102) --- homeassistant/components/litterrobot/vacuum.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/litterrobot/vacuum.py b/homeassistant/components/litterrobot/vacuum.py index 96908c1fa9d..0d9a2b1dc3d 100644 --- a/homeassistant/components/litterrobot/vacuum.py +++ b/homeassistant/components/litterrobot/vacuum.py @@ -18,7 +18,7 @@ from homeassistant.components.vacuum import ( VacuumEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_OFF, STATE_UNAVAILABLE +from homeassistant.const import STATE_OFF from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -96,12 +96,14 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity): | VacuumEntityFeature.TURN_ON ) + @property + def available(self) -> bool: + """Return True if the cleaner has been seen recently.""" + return self.robot.last_seen > datetime.now(timezone.utc) - UNAVAILABLE_AFTER + @property def state(self) -> str: """Return the state of the cleaner.""" - if self.robot.last_seen < datetime.now(timezone.utc) - UNAVAILABLE_AFTER: - return STATE_UNAVAILABLE - return LITTER_BOX_STATUS_STATE_MAP.get(self.robot.status, STATE_ERROR) @property From 00b5d30e24dccebcc61839be7cf6ca9d87b2a3de Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 30 Apr 2022 08:06:43 -0700 Subject: [PATCH 109/930] Add application credentials platform (#69148) * Initial developer credentials scaffolding - Support websocket list/add/delete - Add developer credentials protocol from yaml config - Handle OAuth credential registration and de-registration - Tests for websocket and integration based registration * Fix pydoc text * Remove translations and update owners * Update homeassistant/components/developer_credentials/__init__.py Co-authored-by: Paulus Schoutsen * Update homeassistant/components/developer_credentials/__init__.py Co-authored-by: Paulus Schoutsen * Remove _async_get_developer_credential * Rename to application credentials platform * Fix race condition and add import support * Increase code coverage (92%) * Increase test coverage 93% * Increase test coverage (94%) * Increase test coverage (97%) * Increase test covearge (98%) * Increase test coverage (99%) * Increase test coverage (100%) * Remove http router frozen comment * Remove auth domain override on import * Remove debug statement * Don't import the same client id multiple times * Add auth dependency for local oauth implementation * Revert older oauth2 changes from merge * Update homeassistant/components/application_credentials/__init__.py Co-authored-by: Martin Hjelmare * Move config credential import to its own fixture * Override the mock_application_credentials_integration fixture instead per test * Update application credentials * Add dictionary typing * Use f-strings as per feedback * Add additional structure needed for an MVP application credential Add additional structure needed for an MVP, including a target component Xbox * Add websocket to list supported integrations for frontend selector * Application credentials config * Import xbox credentials * Remove unnecessary async calls * Update script/hassfest/application_credentials.py Co-authored-by: Martin Hjelmare * Update script/hassfest/application_credentials.py Co-authored-by: Martin Hjelmare * Update script/hassfest/application_credentials.py Co-authored-by: Martin Hjelmare * Update script/hassfest/application_credentials.py Co-authored-by: Martin Hjelmare * Import credentials with a fixed auth domain Resolve an issue with compatibility of exisiting config entries when importing client credentials Co-authored-by: Paulus Schoutsen Co-authored-by: Martin Hjelmare --- CODEOWNERS | 2 + .../application_credentials/__init__.py | 242 +++++++ .../application_credentials/manifest.json | 9 + .../application_credentials/strings.json | 3 + .../components/cloud/account_link.py | 4 +- .../components/default_config/manifest.json | 1 + homeassistant/components/xbox/__init__.py | 17 +- .../xbox/application_credentials.py | 14 + homeassistant/components/xbox/manifest.json | 2 +- .../generated/application_credentials.py | 10 + .../helpers/config_entry_oauth2_flow.py | 9 +- script/hassfest/__main__.py | 2 + script/hassfest/application_credentials.py | 63 ++ script/hassfest/manifest.py | 1 + .../application_credentials/__init__.py | 1 + .../application_credentials/test_init.py | 623 ++++++++++++++++++ .../helpers/test_config_entry_oauth2_flow.py | 28 +- 17 files changed, 1006 insertions(+), 25 deletions(-) create mode 100644 homeassistant/components/application_credentials/__init__.py create mode 100644 homeassistant/components/application_credentials/manifest.json create mode 100644 homeassistant/components/application_credentials/strings.json create mode 100644 homeassistant/components/xbox/application_credentials.py create mode 100644 homeassistant/generated/application_credentials.py create mode 100644 script/hassfest/application_credentials.py create mode 100644 tests/components/application_credentials/__init__.py create mode 100644 tests/components/application_credentials/test_init.py diff --git a/CODEOWNERS b/CODEOWNERS index c3405001f23..b8d737cdfb7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -75,6 +75,8 @@ build.json @home-assistant/supervisor /tests/components/api/ @home-assistant/core /homeassistant/components/apple_tv/ @postlund /tests/components/apple_tv/ @postlund +/homeassistant/components/application_credentials/ @home-assistant/core +/tests/components/application_credentials/ @home-assistant/core /homeassistant/components/apprise/ @caronc /tests/components/apprise/ @caronc /homeassistant/components/aprs/ @PhilRW diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py new file mode 100644 index 00000000000..cc5ed5e44bb --- /dev/null +++ b/homeassistant/components/application_credentials/__init__.py @@ -0,0 +1,242 @@ +"""The Application Credentials integration. + +This integration provides APIs for managing local OAuth credentials on behalf +of other integrations. Integrations register an authorization server, and then +the APIs are used to add one or more client credentials. Integrations may also +provide credentials from yaml for backwards compatibility. +""" +from __future__ import annotations + +from dataclasses import dataclass +import logging +from typing import Any, Protocol + +import voluptuous as vol + +from homeassistant.components import websocket_api +from homeassistant.components.websocket_api.connection import ActiveConnection +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN, CONF_ID +from homeassistant.core import HomeAssistant, callback +from homeassistant.generated.application_credentials import APPLICATION_CREDENTIALS +from homeassistant.helpers import collection, config_entry_oauth2_flow +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.storage import Store +from homeassistant.helpers.typing import ConfigType +from homeassistant.loader import IntegrationNotFound, async_get_integration +from homeassistant.util import slugify + +__all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "application_credentials" + +STORAGE_KEY = DOMAIN +STORAGE_VERSION = 1 +DATA_STORAGE = "storage" +CONF_AUTH_DOMAIN = "auth_domain" + +CREATE_FIELDS = { + vol.Required(CONF_DOMAIN): cv.string, + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_AUTH_DOMAIN): cv.string, +} +UPDATE_FIELDS: dict = {} # Not supported + + +@dataclass +class ClientCredential: + """Represent an OAuth client credential.""" + + client_id: str + client_secret: str + + +@dataclass +class AuthorizationServer: + """Represent an OAuth2 Authorization Server.""" + + authorize_url: str + token_url: str + + +class ApplicationCredentialsStorageCollection(collection.StorageCollection): + """Application credential collection stored in storage.""" + + CREATE_SCHEMA = vol.Schema(CREATE_FIELDS) + + async def _process_create_data(self, data: dict[str, str]) -> dict[str, str]: + """Validate the config is valid.""" + result = self.CREATE_SCHEMA(data) + domain = result[CONF_DOMAIN] + if not await _get_platform(self.hass, domain): + raise ValueError(f"No application_credentials platform for {domain}") + return result + + @callback + def _get_suggested_id(self, info: dict[str, str]) -> str: + """Suggest an ID based on the config.""" + return f"{info[CONF_DOMAIN]}.{info[CONF_CLIENT_ID]}" + + async def _update_data( + self, data: dict[str, str], update_data: dict[str, str] + ) -> dict[str, str]: + """Return a new updated data object.""" + raise ValueError("Updates not supported") + + async def async_delete_item(self, item_id: str) -> None: + """Delete item, verifying credential is not in use.""" + if item_id not in self.data: + raise collection.ItemNotFound(item_id) + + # Cannot delete a credential currently in use by a ConfigEntry + current = self.data[item_id] + entries = self.hass.config_entries.async_entries(current[CONF_DOMAIN]) + for entry in entries: + if entry.data.get("auth_implementation") == item_id: + raise ValueError("Cannot delete credential in use by an integration") + + await super().async_delete_item(item_id) + + async def async_import_item(self, info: dict[str, str]) -> None: + """Import an yaml credential if it does not already exist.""" + suggested_id = self._get_suggested_id(info) + if self.id_manager.has_id(slugify(suggested_id)): + return + await self.async_create_item(info) + + def async_client_credentials(self, domain: str) -> dict[str, ClientCredential]: + """Return ClientCredentials in storage for the specified domain.""" + credentials = {} + for item in self.async_items(): + if item[CONF_DOMAIN] != domain: + continue + auth_domain = ( + item[CONF_AUTH_DOMAIN] if CONF_AUTH_DOMAIN in item else item[CONF_ID] + ) + credentials[auth_domain] = ClientCredential( + item[CONF_CLIENT_ID], item[CONF_CLIENT_SECRET] + ) + return credentials + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Application Credentials.""" + hass.data[DOMAIN] = {} + + id_manager = collection.IDManager() + storage_collection = ApplicationCredentialsStorageCollection( + Store(hass, STORAGE_VERSION, STORAGE_KEY), + logging.getLogger(f"{__name__}.storage_collection"), + id_manager, + ) + await storage_collection.async_load() + hass.data[DOMAIN][DATA_STORAGE] = storage_collection + + collection.StorageCollectionWebsocket( + storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS + ).async_setup(hass) + + websocket_api.async_register_command(hass, handle_integration_list) + + config_entry_oauth2_flow.async_add_implementation_provider( + hass, DOMAIN, _async_provide_implementation + ) + + return True + + +async def async_import_client_credential( + hass: HomeAssistant, domain: str, credential: ClientCredential +) -> None: + """Import an existing credential from configuration.yaml.""" + if DOMAIN not in hass.data: + raise ValueError("Integration 'application_credentials' not setup") + storage_collection = hass.data[DOMAIN][DATA_STORAGE] + item = { + CONF_DOMAIN: domain, + CONF_CLIENT_ID: credential.client_id, + CONF_CLIENT_SECRET: credential.client_secret, + CONF_AUTH_DOMAIN: domain, + } + await storage_collection.async_import_item(item) + + +class AuthImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation): + """Application Credentials local oauth2 implementation.""" + + @property + def name(self) -> str: + """Name of the implementation.""" + return self.client_id + + +async def _async_provide_implementation( + hass: HomeAssistant, domain: str +) -> list[config_entry_oauth2_flow.AbstractOAuth2Implementation]: + """Return registered OAuth implementations.""" + + platform = await _get_platform(hass, domain) + if not platform: + return [] + + authorization_server = await platform.async_get_authorization_server(hass) + storage_collection = hass.data[DOMAIN][DATA_STORAGE] + credentials = storage_collection.async_client_credentials(domain) + return [ + AuthImplementation( + hass, + auth_domain, + credential.client_id, + credential.client_secret, + authorization_server.authorize_url, + authorization_server.token_url, + ) + for auth_domain, credential in credentials.items() + ] + + +class ApplicationCredentialsProtocol(Protocol): + """Define the format that application_credentials platforms can have.""" + + async def async_get_authorization_server( + self, hass: HomeAssistant + ) -> AuthorizationServer: + """Return authorization server.""" + + +async def _get_platform( + hass: HomeAssistant, integration_domain: str +) -> ApplicationCredentialsProtocol | None: + """Register an application_credentials platform.""" + try: + integration = await async_get_integration(hass, integration_domain) + except IntegrationNotFound as err: + _LOGGER.debug("Integration '%s' does not exist: %s", integration_domain, err) + return None + try: + platform = integration.get_platform("application_credentials") + except ImportError as err: + _LOGGER.debug( + "Integration '%s' does not provide application_credentials: %s", + integration_domain, + err, + ) + return None + if not hasattr(platform, "async_get_authorization_server"): + raise ValueError( + f"Integration '{integration_domain}' platform application_credentials did not implement 'async_get_authorization_server'" + ) + return platform + + +@websocket_api.websocket_command( + {vol.Required("type"): "application_credentials/config"} +) +@callback +def handle_integration_list( + hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] +) -> None: + """Handle integrations command.""" + connection.send_result(msg["id"], {"domains": APPLICATION_CREDENTIALS}) diff --git a/homeassistant/components/application_credentials/manifest.json b/homeassistant/components/application_credentials/manifest.json new file mode 100644 index 00000000000..9a8abc16c36 --- /dev/null +++ b/homeassistant/components/application_credentials/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "application_credentials", + "name": "Application Credentials", + "config_flow": false, + "documentation": "https://www.home-assistant.io/integrations/application_credentials", + "dependencies": ["auth", "websocket_api"], + "codeowners": ["@home-assistant/core"], + "quality_scale": "internal" +} diff --git a/homeassistant/components/application_credentials/strings.json b/homeassistant/components/application_credentials/strings.json new file mode 100644 index 00000000000..48d74bc75e4 --- /dev/null +++ b/homeassistant/components/application_credentials/strings.json @@ -0,0 +1,3 @@ +{ + "title": "Application Credentials" +} diff --git a/homeassistant/components/cloud/account_link.py b/homeassistant/components/cloud/account_link.py index 6dc0da82512..5df16cb1724 100644 --- a/homeassistant/components/cloud/account_link.py +++ b/homeassistant/components/cloud/account_link.py @@ -34,9 +34,9 @@ async def async_provide_implementation(hass: HomeAssistant, domain: str): for service in services: if service["service"] == domain and CURRENT_VERSION >= service["min_version"]: - return CloudOAuth2Implementation(hass, domain) + return [CloudOAuth2Implementation(hass, domain)] - return + return [] async def _get_services(hass): diff --git a/homeassistant/components/default_config/manifest.json b/homeassistant/components/default_config/manifest.json index 1ab827529c6..1742092cc70 100644 --- a/homeassistant/components/default_config/manifest.json +++ b/homeassistant/components/default_config/manifest.json @@ -3,6 +3,7 @@ "name": "Default Config", "documentation": "https://www.home-assistant.io/integrations/default_config", "dependencies": [ + "application_credentials", "automation", "cloud", "counter", diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 0466d0191cf..2b5772dd0ba 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -20,6 +20,7 @@ from xbox.webapi.api.provider.smartglass.models import ( SmartglassConsoleStatus, ) +from homeassistant.components import application_credentials from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform from homeassistant.core import HomeAssistant @@ -31,8 +32,8 @@ from homeassistant.helpers import ( from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from . import api, config_flow -from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN +from . import api +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -63,15 +64,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in config: return True - config_flow.OAuth2FlowHandler.async_register_implementation( + await application_credentials.async_import_client_credential( hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, - config[DOMAIN][CONF_CLIENT_ID], - config[DOMAIN][CONF_CLIENT_SECRET], - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, + DOMAIN, + application_credentials.ClientCredential( + config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET] ), ) diff --git a/homeassistant/components/xbox/application_credentials.py b/homeassistant/components/xbox/application_credentials.py new file mode 100644 index 00000000000..2e3d7f8a6a0 --- /dev/null +++ b/homeassistant/components/xbox/application_credentials.py @@ -0,0 +1,14 @@ +"""Application credentials platform for xbox.""" + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + +from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ) diff --git a/homeassistant/components/xbox/manifest.json b/homeassistant/components/xbox/manifest.json index 432b3e84100..5adfa54a901 100644 --- a/homeassistant/components/xbox/manifest.json +++ b/homeassistant/components/xbox/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/xbox", "requirements": ["xbox-webapi==2.0.11"], - "dependencies": ["auth"], + "dependencies": ["auth", "application_credentials"], "codeowners": ["@hunterjm"], "iot_class": "cloud_polling" } diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py new file mode 100644 index 00000000000..ec6c1886e0a --- /dev/null +++ b/homeassistant/generated/application_credentials.py @@ -0,0 +1,10 @@ +"""Automatically generated by hassfest. + +To update, run python3 -m script.hassfest +""" + +# fmt: off + +APPLICATION_CREDENTIALS = [ + "xbox" +] diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index e2b21522d42..d0aaca71304 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -347,10 +347,9 @@ async def async_get_implementations( return registered registered = dict(registered) - - for provider_domain, get_impl in hass.data[DATA_PROVIDERS].items(): - if (implementation := await get_impl(hass, domain)) is not None: - registered[provider_domain] = implementation + for get_impl in list(hass.data[DATA_PROVIDERS].values()): + for impl in await get_impl(hass, domain): + registered[impl.domain] = impl return registered @@ -373,7 +372,7 @@ def async_add_implementation_provider( hass: HomeAssistant, provider_domain: str, async_provide_implementation: Callable[ - [HomeAssistant, str], Awaitable[AbstractOAuth2Implementation | None] + [HomeAssistant, str], Awaitable[list[AbstractOAuth2Implementation]] ], ) -> None: """Add an implementation provider. diff --git a/script/hassfest/__main__.py b/script/hassfest/__main__.py index c6a9799a502..889cad2a497 100644 --- a/script/hassfest/__main__.py +++ b/script/hassfest/__main__.py @@ -5,6 +5,7 @@ import sys from time import monotonic from . import ( + application_credentials, codeowners, config_flow, coverage, @@ -25,6 +26,7 @@ from . import ( from .model import Config, Integration INTEGRATION_PLUGINS = [ + application_credentials, codeowners, config_flow, dependencies, diff --git a/script/hassfest/application_credentials.py b/script/hassfest/application_credentials.py new file mode 100644 index 00000000000..87a277bb2b8 --- /dev/null +++ b/script/hassfest/application_credentials.py @@ -0,0 +1,63 @@ +"""Generate application_credentials data.""" +from __future__ import annotations + +import json + +from .model import Config, Integration + +BASE = """ +\"\"\"Automatically generated by hassfest. + +To update, run python3 -m script.hassfest +\"\"\" + +# fmt: off + +APPLICATION_CREDENTIALS = {} +""".strip() + + +def generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: + """Validate and generate config flow data.""" + + match_list = [] + + for domain in sorted(integrations): + integration = integrations[domain] + application_credentials_file = integration.path / "application_credentials.py" + if not application_credentials_file.is_file(): + continue + + match_list.append(domain) + + return BASE.format(json.dumps(match_list, indent=4)) + + +def validate(integrations: dict[str, Integration], config: Config) -> None: + """Validate application_credentials data.""" + application_credentials_path = ( + config.root / "homeassistant/generated/application_credentials.py" + ) + config.cache["application_credentials"] = content = generate_and_validate( + integrations, config + ) + + if config.specific_integrations: + return + + if application_credentials_path.read_text(encoding="utf-8").strip() != content: + config.add_error( + "application_credentials", + "File application_credentials.py is not up to date. Run python3 -m script.hassfest", + fixable=True, + ) + + +def generate(integrations: dict[str, Integration], config: Config): + """Generate application_credentials data.""" + application_credentials_path = ( + config.root / "homeassistant/generated/application_credentials.py" + ) + application_credentials_path.write_text( + f"{config.cache['application_credentials']}\n", encoding="utf-8" + ) diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index ca9acedd515..b66b33486cb 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -36,6 +36,7 @@ SUPPORTED_IOT_CLASSES = [ NO_IOT_CLASS = [ *{platform.value for platform in Platform}, "api", + "application_credentials", "auth", "automation", "blueprint", diff --git a/tests/components/application_credentials/__init__.py b/tests/components/application_credentials/__init__.py new file mode 100644 index 00000000000..36933b9ccfb --- /dev/null +++ b/tests/components/application_credentials/__init__.py @@ -0,0 +1 @@ +"""Tests for the Application Credentials integration.""" diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py new file mode 100644 index 00000000000..31cf45f2b54 --- /dev/null +++ b/tests/components/application_credentials/test_init.py @@ -0,0 +1,623 @@ +"""Test the Developer Credentials integration.""" + +from __future__ import annotations + +from collections.abc import Callable, Generator +import logging +from typing import Any +from unittest.mock import AsyncMock, Mock, patch + +from aiohttp import ClientWebSocketResponse +import pytest + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.application_credentials import ( + CONF_AUTH_DOMAIN, + DOMAIN, + AuthorizationServer, + ClientCredential, + async_import_client_credential, +) +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.setup import async_setup_component + +from tests.common import mock_platform + +CLIENT_ID = "some-client-id" +CLIENT_SECRET = "some-client-secret" +DEVELOPER_CREDENTIAL = ClientCredential(CLIENT_ID, CLIENT_SECRET) +ID = "fake_integration_some_client_id" +AUTHORIZE_URL = "https://example.com/auth" +TOKEN_URL = "https://example.com/oauth2/v4/token" +REFRESH_TOKEN = "mock-refresh-token" +ACCESS_TOKEN = "mock-access-token" + +TEST_DOMAIN = "fake_integration" + + +@pytest.fixture +async def authorization_server() -> AuthorizationServer: + """Fixture AuthorizationServer for mock application_credentials integration.""" + return AuthorizationServer(AUTHORIZE_URL, TOKEN_URL) + + +@pytest.fixture +async def config_credential() -> ClientCredential | None: + """Fixture ClientCredential for mock application_credentials integration.""" + return None + + +@pytest.fixture +async def import_config_credential( + hass: HomeAssistant, config_credential: ClientCredential +) -> None: + """Fixture to import the yaml based credential.""" + await async_import_client_credential(hass, TEST_DOMAIN, config_credential) + + +async def setup_application_credentials_integration( + hass: HomeAssistant, + domain: str, + authorization_server: AuthorizationServer, +) -> None: + """Set up a fake application_credentials integration.""" + hass.config.components.add(domain) + mock_platform( + hass, + f"{domain}.application_credentials", + Mock( + async_get_authorization_server=AsyncMock(return_value=authorization_server), + ), + ) + + +@pytest.fixture(autouse=True) +async def mock_application_credentials_integration( + hass: HomeAssistant, + authorization_server: AuthorizationServer, +): + """Mock a application_credentials integration.""" + assert await async_setup_component(hass, "application_credentials", {}) + await setup_application_credentials_integration( + hass, TEST_DOMAIN, authorization_server + ) + + +class FakeConfigFlow(config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN): + """Config flow used during tests.""" + + DOMAIN = TEST_DOMAIN + + @property + def logger(self) -> logging.Logger: + """Return logger.""" + return logging.getLogger(__name__) + + +@pytest.fixture(autouse=True) +def config_flow_handler( + hass: HomeAssistant, current_request_with_host: Any +) -> Generator[FakeConfigFlow, None, None]: + """Fixture for a test config flow.""" + mock_platform(hass, f"{TEST_DOMAIN}.config_flow") + with patch.dict(config_entries.HANDLERS, {TEST_DOMAIN: FakeConfigFlow}): + yield FakeConfigFlow + + +class OAuthFixture: + """Fixture to facilitate testing an OAuth flow.""" + + def __init__(self, hass, hass_client, aioclient_mock): + """Initialize OAuthFixture.""" + self.hass = hass + self.hass_client = hass_client + self.aioclient_mock = aioclient_mock + self.client_id = CLIENT_ID + + async def complete_external_step( + self, result: data_entry_flow.FlowResult + ) -> data_entry_flow.FlowResult: + """Fixture method to complete the OAuth flow and return the completed result.""" + client = await self.hass_client() + state = config_entry_oauth2_flow._encode_jwt( + self.hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + assert result["url"] == ( + f"{AUTHORIZE_URL}?response_type=code&client_id={self.client_id}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}" + ) + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + self.aioclient_mock.post( + TOKEN_URL, + json={ + "refresh_token": REFRESH_TOKEN, + "access_token": ACCESS_TOKEN, + "type": "bearer", + "expires_in": 60, + }, + ) + + result = await self.hass.config_entries.flow.async_configure(result["flow_id"]) + assert result.get("type") == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result.get("title") == self.client_id + assert "data" in result + assert "token" in result["data"] + return result + + +@pytest.fixture +async def oauth_fixture( + hass: HomeAssistant, hass_client_no_auth: Any, aioclient_mock: Any +) -> OAuthFixture: + """Fixture for testing the OAuth flow.""" + return OAuthFixture(hass, hass_client_no_auth, aioclient_mock) + + +class Client: + """Test client with helper methods for application credentials websocket.""" + + def __init__(self, client): + """Initialize Client.""" + self.client = client + self.id = 0 + + async def cmd(self, cmd: str, payload: dict[str, Any] = None) -> dict[str, Any]: + """Send a command and receive the json result.""" + self.id += 1 + await self.client.send_json( + { + "id": self.id, + "type": f"{DOMAIN}/{cmd}", + **(payload if payload is not None else {}), + } + ) + resp = await self.client.receive_json() + assert resp.get("id") == self.id + return resp + + async def cmd_result(self, cmd: str, payload: dict[str, Any] = None) -> Any: + """Send a command and parse the result.""" + resp = await self.cmd(cmd, payload) + assert resp.get("success") + assert resp.get("type") == "result" + return resp.get("result") + + +ClientFixture = Callable[[], Client] + + +@pytest.fixture +async def ws_client( + hass_ws_client: Callable[[...], ClientWebSocketResponse] +) -> ClientFixture: + """Fixture for creating the test websocket client.""" + + async def create_client() -> Client: + ws_client = await hass_ws_client() + return Client(ws_client) + + return create_client + + +async def test_websocket_list_empty(ws_client: ClientFixture): + """Test websocket list command.""" + client = await ws_client() + assert await client.cmd_result("list") == [] + + +async def test_websocket_create(ws_client: ClientFixture): + """Test websocket create command.""" + client = await ws_client() + result = await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert result == { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + + result = await client.cmd_result("list") + assert result == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + ] + + +async def test_websocket_create_invalid_domain(ws_client: ClientFixture): + """Test websocket create command.""" + client = await ws_client() + resp = await client.cmd( + "create", + { + CONF_DOMAIN: "other-domain", + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "invalid_format" + assert ( + resp["error"].get("message") + == "No application_credentials platform for other-domain" + ) + + +async def test_websocket_update_not_supported(ws_client: ClientFixture): + """Test websocket update command in unsupported.""" + client = await ws_client() + result = await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert result == { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + + resp = await client.cmd("update", {"application_credentials_id": ID}) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "invalid_format" + assert resp["error"].get("message") == "Updates not supported" + + +async def test_websocket_delete(ws_client: ClientFixture): + """Test websocket delete command.""" + client = await ws_client() + + await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert await client.cmd_result("list") == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + } + ] + + await client.cmd_result("delete", {"application_credentials_id": ID}) + assert await client.cmd_result("list") == [] + + +async def test_websocket_delete_item_not_found(ws_client: ClientFixture): + """Test websocket delete command.""" + client = await ws_client() + + resp = await client.cmd("delete", {"application_credentials_id": ID}) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "not_found" + assert ( + resp["error"].get("message") + == f"Unable to find application_credentials_id {ID}" + ) + + +@pytest.mark.parametrize("config_credential", [DEVELOPER_CREDENTIAL]) +async def test_websocket_import_config( + ws_client: ClientFixture, + config_credential: ClientCredential, + import_config_credential: Any, +): + """Test websocket list command for an imported credential.""" + client = await ws_client() + + # Imported creds returned from websocket + assert await client.cmd_result("list") == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + CONF_AUTH_DOMAIN: TEST_DOMAIN, + } + ] + + # Imported credential can be deleted + await client.cmd_result("delete", {"application_credentials_id": ID}) + assert await client.cmd_result("list") == [] + + +@pytest.mark.parametrize("config_credential", [DEVELOPER_CREDENTIAL]) +async def test_import_duplicate_credentials( + hass: HomeAssistant, + ws_client: ClientFixture, + config_credential: ClientCredential, + import_config_credential: Any, +): + """Exercise duplicate credentials are ignored.""" + + # Import the test credential again and verify it is not imported twice + await async_import_client_credential(hass, TEST_DOMAIN, DEVELOPER_CREDENTIAL) + client = await ws_client() + assert await client.cmd_result("list") == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + CONF_AUTH_DOMAIN: TEST_DOMAIN, + } + ] + + +async def test_config_flow_no_credentials(hass): + """Test config flow base case with no credentials registered.""" + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT + assert result.get("reason") == "missing_configuration" + + +async def test_config_flow_other_domain( + hass: HomeAssistant, + ws_client: ClientFixture, + authorization_server: AuthorizationServer, +): + """Test config flow ignores credentials for another domain.""" + await setup_application_credentials_integration( + hass, + "other_domain", + authorization_server, + ) + client = await ws_client() + await client.cmd_result( + "create", + { + CONF_DOMAIN: "other_domain", + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT + assert result.get("reason") == "missing_configuration" + + +async def test_config_flow( + hass: HomeAssistant, + ws_client: ClientFixture, + oauth_fixture: OAuthFixture, +): + """Test config flow with application credential registered.""" + client = await ws_client() + + await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + result = await oauth_fixture.complete_external_step(result) + assert ( + result["data"].get("auth_implementation") == "fake_integration_some_client_id" + ) + + # Verify it is not possible to delete an in-use config entry + resp = await client.cmd("delete", {"application_credentials_id": ID}) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "unknown_error" + + +async def test_config_flow_multiple_entries( + hass: HomeAssistant, + ws_client: ClientFixture, + oauth_fixture: OAuthFixture, +): + """Test config flow with multiple application credentials registered.""" + client = await ws_client() + + await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID + "2", + CONF_CLIENT_SECRET: CLIENT_SECRET + "2", + }, + ) + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_FORM + assert result.get("step_id") == "pick_implementation" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={"implementation": "fake_integration_some_client_id2"}, + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + oauth_fixture.client_id = CLIENT_ID + "2" + result = await oauth_fixture.complete_external_step(result) + assert ( + result["data"].get("auth_implementation") == "fake_integration_some_client_id2" + ) + + +async def test_config_flow_create_delete_credential( + hass: HomeAssistant, + ws_client: ClientFixture, + oauth_fixture: OAuthFixture, +): + """Test adding and deleting a credential unregisters from the config flow.""" + client = await ws_client() + + await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + await client.cmd("delete", {"application_credentials_id": ID}) + + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT + assert result.get("reason") == "missing_configuration" + + +@pytest.mark.parametrize("config_credential", [DEVELOPER_CREDENTIAL]) +async def test_config_flow_with_config_credential( + hass, + hass_client_no_auth, + aioclient_mock, + oauth_fixture, + config_credential, + import_config_credential, +): + """Test config flow with application credential registered.""" + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + result = await oauth_fixture.complete_external_step(result) + # Uses the imported auth domain for compatibility + assert result["data"].get("auth_implementation") == TEST_DOMAIN + + +@pytest.mark.parametrize("mock_application_credentials_integration", [None]) +async def test_import_without_setup(hass, config_credential): + """Test import of credentials without setting up the integration.""" + + with pytest.raises(ValueError): + await async_import_client_credential(hass, TEST_DOMAIN, config_credential) + + # Config flow does not have authentication + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT + assert result.get("reason") == "missing_configuration" + + +@pytest.mark.parametrize("mock_application_credentials_integration", [None]) +async def test_websocket_without_platform( + hass: HomeAssistant, ws_client: ClientFixture +): + """Test an integration without the application credential platform.""" + assert await async_setup_component(hass, "application_credentials", {}) + hass.config.components.add(TEST_DOMAIN) + + client = await ws_client() + resp = await client.cmd( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "invalid_format" + + # Config flow does not have authentication + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_ABORT + assert result.get("reason") == "missing_configuration" + + +@pytest.mark.parametrize("mock_application_credentials_integration", [None]) +async def test_websocket_without_authorization_server( + hass: HomeAssistant, ws_client: ClientFixture +): + """Test platform with incorrect implementation.""" + assert await async_setup_component(hass, "application_credentials", {}) + hass.config.components.add(TEST_DOMAIN) + + # Platform does not implemenent async_get_authorization_server + platform = Mock() + del platform.async_get_authorization_server + mock_platform( + hass, + f"{TEST_DOMAIN}.application_credentials", + platform, + ) + + client = await ws_client() + resp = await client.cmd( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + ) + assert not resp.get("success") + assert "error" in resp + assert resp["error"].get("code") == "invalid_format" + + # Config flow does not have authentication + with pytest.raises(ValueError): + await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + +async def test_websocket_integration_list(ws_client: ClientFixture): + """Test websocket integration list command.""" + client = await ws_client() + with patch( + "homeassistant.components.application_credentials.APPLICATION_CREDENTIALS", + ["example1", "example2"], + ): + assert await client.cmd_result("config") == { + "domains": ["example1", "example2"] + } diff --git a/tests/helpers/test_config_entry_oauth2_flow.py b/tests/helpers/test_config_entry_oauth2_flow.py index 2cd3184b44b..97e728d022d 100644 --- a/tests/helpers/test_config_entry_oauth2_flow.py +++ b/tests/helpers/test_config_entry_oauth2_flow.py @@ -537,11 +537,11 @@ async def test_implementation_provider(hass, local_impl): hass, mock_domain_with_impl ) == {TEST_DOMAIN: local_impl} - provider_source = {} + provider_source = [] async def async_provide_implementation(hass, domain): """Mock implementation provider.""" - return provider_source.get(domain) + return provider_source config_entry_oauth2_flow.async_add_implementation_provider( hass, "cloud", async_provide_implementation @@ -551,15 +551,29 @@ async def test_implementation_provider(hass, local_impl): hass, mock_domain_with_impl ) == {TEST_DOMAIN: local_impl} - provider_source[ - mock_domain_with_impl - ] = config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, "cloud", CLIENT_ID, CLIENT_SECRET, AUTHORIZE_URL, TOKEN_URL + provider_source.append( + config_entry_oauth2_flow.LocalOAuth2Implementation( + hass, "cloud", CLIENT_ID, CLIENT_SECRET, AUTHORIZE_URL, TOKEN_URL + ) ) assert await config_entry_oauth2_flow.async_get_implementations( hass, mock_domain_with_impl - ) == {TEST_DOMAIN: local_impl, "cloud": provider_source[mock_domain_with_impl]} + ) == {TEST_DOMAIN: local_impl, "cloud": provider_source[0]} + + provider_source.append( + config_entry_oauth2_flow.LocalOAuth2Implementation( + hass, "other", CLIENT_ID, CLIENT_SECRET, AUTHORIZE_URL, TOKEN_URL + ) + ) + + assert await config_entry_oauth2_flow.async_get_implementations( + hass, mock_domain_with_impl + ) == { + TEST_DOMAIN: local_impl, + "cloud": provider_source[0], + "other": provider_source[1], + } async def test_oauth_session_refresh_failure( From f14bc1cecefedb959d67d6e84c5f7e170c0a0169 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sat, 30 Apr 2022 11:40:57 -0400 Subject: [PATCH 110/930] Clean up Steam integration (#71091) * Clean up Steam * uno mas * uno mas * uno mas --- .../components/steam_online/config_flow.py | 79 ++++++++----------- .../components/steam_online/const.py | 9 ++- .../components/steam_online/coordinator.py | 9 +-- .../components/steam_online/sensor.py | 6 +- .../components/steam_online/strings.json | 4 +- .../steam_online/translations/en.json | 4 +- tests/components/steam_online/__init__.py | 31 ++------ .../steam_online/test_config_flow.py | 7 +- 8 files changed, 58 insertions(+), 91 deletions(-) diff --git a/homeassistant/components/steam_online/config_flow.py b/homeassistant/components/steam_online/config_flow.py index cfac20da11b..246d54c0bff 100644 --- a/homeassistant/components/steam_online/config_flow.py +++ b/homeassistant/components/steam_online/config_flow.py @@ -13,7 +13,14 @@ from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.typing import ConfigType -from .const import CONF_ACCOUNT, CONF_ACCOUNTS, DEFAULT_NAME, DOMAIN, LOGGER +from .const import ( + CONF_ACCOUNT, + CONF_ACCOUNTS, + DEFAULT_NAME, + DOMAIN, + LOGGER, + PLACEHOLDERS, +) def validate_input(user_input: dict[str, str | int]) -> list[dict[str, str | int]]: @@ -52,14 +59,14 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if res[0] is not None: name = str(res[0]["personaname"]) else: - errors = {"base": "invalid_account"} + errors["base"] = "invalid_account" except (steam.api.HTTPError, steam.api.HTTPTimeoutError) as ex: - errors = {"base": "cannot_connect"} + errors["base"] = "cannot_connect" if "403" in str(ex): - errors = {"base": "invalid_auth"} + errors["base"] = "invalid_auth" except Exception as ex: # pylint:disable=broad-except LOGGER.exception("Unknown exception: %s", ex) - errors = {"base": "unknown"} + errors["base"] = "unknown" if not errors: entry = await self.async_set_unique_id(user_input[CONF_ACCOUNT]) if entry and self.source == config_entries.SOURCE_REAUTH: @@ -70,20 +77,12 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if self.source == config_entries.SOURCE_IMPORT: accounts_data = { CONF_ACCOUNTS: { - acc["steamid"]: { - "name": acc["personaname"], - "enabled": True, - } - for acc in res + acc["steamid"]: acc["personaname"] for acc in res } } user_input.pop(CONF_ACCOUNTS) else: - accounts_data = { - CONF_ACCOUNTS: { - user_input[CONF_ACCOUNT]: {"name": name, "enabled": True} - } - } + accounts_data = {CONF_ACCOUNTS: {user_input[CONF_ACCOUNT]: name}} return self.async_create_entry( title=name or DEFAULT_NAME, data=user_input, @@ -103,6 +102,7 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): } ), errors=errors, + description_placeholders=PLACEHOLDERS, ) async def async_step_import(self, import_config: ConfigType) -> FlowResult: @@ -111,7 +111,7 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if entry.data[CONF_API_KEY] == import_config[CONF_API_KEY]: return self.async_abort(reason="already_configured") LOGGER.warning( - "Steam yaml config in now deprecated and has been imported. " + "Steam yaml config is now deprecated and has been imported. " "Please remove it from your config" ) import_config[CONF_ACCOUNT] = import_config[CONF_ACCOUNTS][0] @@ -131,7 +131,9 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return await self.async_step_user() self._set_confirm_only() - return self.async_show_form(step_id="reauth_confirm") + return self.async_show_form( + step_id="reauth_confirm", description_placeholders=PLACEHOLDERS + ) class SteamOptionsFlowHandler(config_entries.OptionsFlow): @@ -148,56 +150,38 @@ class SteamOptionsFlowHandler(config_entries.OptionsFlow): """Manage Steam options.""" if user_input is not None: await self.hass.config_entries.async_unload(self.entry.entry_id) - for k in self.options[CONF_ACCOUNTS]: - if ( - self.options[CONF_ACCOUNTS][k]["enabled"] - and k not in user_input[CONF_ACCOUNTS] - and ( - entity_id := er.async_get(self.hass).async_get_entity_id( - Platform.SENSOR, DOMAIN, f"sensor.steam_{k}" - ) + for _id in self.options[CONF_ACCOUNTS]: + if _id not in user_input[CONF_ACCOUNTS] and ( + entity_id := er.async_get(self.hass).async_get_entity_id( + Platform.SENSOR, DOMAIN, f"sensor.steam_{_id}" ) ): er.async_get(self.hass).async_remove(entity_id) channel_data = { CONF_ACCOUNTS: { - k: { - "name": v["name"], - "enabled": k in user_input[CONF_ACCOUNTS], - } - for k, v in self.options[CONF_ACCOUNTS].items() - if k in user_input[CONF_ACCOUNTS] + _id: name + for _id, name in self.options[CONF_ACCOUNTS].items() + if _id in user_input[CONF_ACCOUNTS] } } await self.hass.config_entries.async_reload(self.entry.entry_id) return self.async_create_entry(title="", data=channel_data) try: users = { - name["steamid"]: {"name": name["personaname"], "enabled": False} + name["steamid"]: name["personaname"] for name in await self.hass.async_add_executor_job(self.get_accounts) } except steam.api.HTTPTimeoutError: users = self.options[CONF_ACCOUNTS] - _users = users | self.options[CONF_ACCOUNTS] - self.options[CONF_ACCOUNTS] = { - k: v - for k, v in _users.items() - if k in users or self.options[CONF_ACCOUNTS][k]["enabled"] - } options = { vol.Required( CONF_ACCOUNTS, - default={ - k - for k in self.options[CONF_ACCOUNTS] - if self.options[CONF_ACCOUNTS][k]["enabled"] - }, - ): cv.multi_select( - {k: v["name"] for k, v in self.options[CONF_ACCOUNTS].items()} - ), + default=set(self.options[CONF_ACCOUNTS]), + ): cv.multi_select(users | self.options[CONF_ACCOUNTS]), } + self.options[CONF_ACCOUNTS] = users | self.options[CONF_ACCOUNTS] return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) @@ -205,7 +189,6 @@ class SteamOptionsFlowHandler(config_entries.OptionsFlow): """Get accounts.""" interface = steam.api.interface("ISteamUser") friends = interface.GetFriendList(steamid=self.entry.data[CONF_ACCOUNT]) - friends = friends["friendslist"]["friends"] - _users_str = [user["steamid"] for user in friends] + _users_str = [user["steamid"] for user in friends["friendslist"]["friends"]] names = interface.GetPlayerSummaries(steamids=_users_str) return names["response"]["players"]["player"] diff --git a/homeassistant/components/steam_online/const.py b/homeassistant/components/steam_online/const.py index 63206230073..01f4410a7c9 100644 --- a/homeassistant/components/steam_online/const.py +++ b/homeassistant/components/steam_online/const.py @@ -11,6 +11,11 @@ DOMAIN: Final = "steam_online" LOGGER = logging.getLogger(__package__) +PLACEHOLDERS = { + "api_key_url": "https://steamcommunity.com/dev/apikey", + "account_id_url": "https://steamid.io", +} + STATE_OFFLINE = "offline" STATE_ONLINE = "online" STATE_BUSY = "busy" @@ -30,6 +35,4 @@ STEAM_STATUSES = { STEAM_API_URL = "https://steamcdn-a.akamaihd.net/steam/apps/" STEAM_HEADER_IMAGE_FILE = "header.jpg" STEAM_MAIN_IMAGE_FILE = "capsule_616x353.jpg" -STEAM_ICON_URL = ( - "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/%d/%s.jpg" -) +STEAM_ICON_URL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/" diff --git a/homeassistant/components/steam_online/coordinator.py b/homeassistant/components/steam_online/coordinator.py index 8f535999247..78c850b0ac9 100644 --- a/homeassistant/components/steam_online/coordinator.py +++ b/homeassistant/components/steam_online/coordinator.py @@ -28,7 +28,7 @@ class SteamDataUpdateCoordinator(DataUpdateCoordinator): name=DOMAIN, update_interval=timedelta(seconds=30), ) - self.game_icons: dict = {} + self.game_icons: dict[int, str] = {} self.player_interface: INTMethod = None self.user_interface: INTMethod = None steam.api.key.set(self.config_entry.data[CONF_API_KEY]) @@ -36,7 +36,7 @@ class SteamDataUpdateCoordinator(DataUpdateCoordinator): def _update(self) -> dict[str, dict[str, str | int]]: """Fetch data from API endpoint.""" accounts = self.config_entry.options[CONF_ACCOUNTS] - _ids = [k for k in accounts if accounts[k]["enabled"]] + _ids = list(accounts) if not self.user_interface or not self.player_interface: self.user_interface = steam.api.interface("ISteamUser") self.player_interface = steam.api.interface("IPlayerService") @@ -46,7 +46,7 @@ class SteamDataUpdateCoordinator(DataUpdateCoordinator): steamid=_id, include_appinfo=1 )["response"] self.game_icons = self.game_icons | { - game["appid"]: game["img_icon_url"] for game in res.get("games", {}) + game["appid"]: game["img_icon_url"] for game in res.get("games", []) } response = self.user_interface.GetPlayerSummaries(steamids=_ids) players = { @@ -56,8 +56,7 @@ class SteamDataUpdateCoordinator(DataUpdateCoordinator): } for k in players: data = self.player_interface.GetSteamLevel(steamid=players[k]["steamid"]) - data = data["response"] - players[k]["level"] = data["player_level"] + players[k]["level"] = data["response"]["player_level"] return players async def _async_update_data(self) -> dict[str, dict[str, str | int]]: diff --git a/homeassistant/components/steam_online/sensor.py b/homeassistant/components/steam_online/sensor.py index 466bd46f38f..be175b41b66 100644 --- a/homeassistant/components/steam_online/sensor.py +++ b/homeassistant/components/steam_online/sensor.py @@ -65,7 +65,6 @@ async def async_setup_entry( async_add_entities( SteamSensor(hass.data[DOMAIN][entry.entry_id], account) for account in entry.options[CONF_ACCOUNTS] - if entry.options[CONF_ACCOUNTS][account]["enabled"] ) @@ -106,10 +105,7 @@ class SteamSensor(SteamEntity, SensorEntity): attrs["game_image_header"] = f"{game_url}{STEAM_HEADER_IMAGE_FILE}" attrs["game_image_main"] = f"{game_url}{STEAM_MAIN_IMAGE_FILE}" if info := self._get_game_icon(player): - attrs["game_icon"] = STEAM_ICON_URL % ( - game_id, - info, - ) + attrs["game_icon"] = f"{STEAM_ICON_URL}{game_id}/{info}.jpg" self._attr_name = player["personaname"] self._attr_entity_picture = player["avatarmedium"] if last_online := player.get("lastlogoff"): diff --git a/homeassistant/components/steam_online/strings.json b/homeassistant/components/steam_online/strings.json index 67484f0276b..6d80bb77f1b 100644 --- a/homeassistant/components/steam_online/strings.json +++ b/homeassistant/components/steam_online/strings.json @@ -2,7 +2,7 @@ "config": { "step": { "user": { - "description": "Use https://steamid.io to find your Steam account ID", + "description": "Use {account_id_url} to find your Steam account ID", "data": { "api_key": "[%key:common::config_flow::data::api_key%]", "account": "Steam account ID" @@ -10,7 +10,7 @@ }, "reauth_confirm": { "title": "[%key:common::config_flow::title::reauth%]", - "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: https://steamcommunity.com/dev/apikey" + "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: {api_key_url}" } }, "error": { diff --git a/homeassistant/components/steam_online/translations/en.json b/homeassistant/components/steam_online/translations/en.json index 8b34a85922d..990a33dbeff 100644 --- a/homeassistant/components/steam_online/translations/en.json +++ b/homeassistant/components/steam_online/translations/en.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: https://steamcommunity.com/dev/apikey", + "description": "The Steam integration needs to be manually re-authenticated\n\nYou can find your key here: {api_key_url}", "title": "Reauthenticate Integration" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam account ID", "api_key": "API Key" }, - "description": "Use https://steamid.io to find your Steam account ID" + "description": "Use {account_id_url} to find your Steam account ID" } } }, diff --git a/tests/components/steam_online/__init__.py b/tests/components/steam_online/__init__.py index 729877e58bc..27958b76576 100644 --- a/tests/components/steam_online/__init__.py +++ b/tests/components/steam_online/__init__.py @@ -3,7 +3,7 @@ from unittest.mock import patch from homeassistant.components.steam_online import DOMAIN from homeassistant.components.steam_online.const import CONF_ACCOUNT, CONF_ACCOUNTS -from homeassistant.const import CONF_API_KEY, CONF_NAME +from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -19,38 +19,19 @@ CONF_DATA = { CONF_ACCOUNT: ACCOUNT_1, } -CONF_OPTIONS = { - CONF_ACCOUNTS: { - ACCOUNT_1: { - CONF_NAME: ACCOUNT_NAME_1, - "enabled": True, - } - } -} +CONF_OPTIONS = {CONF_ACCOUNTS: {ACCOUNT_1: ACCOUNT_NAME_1}} CONF_OPTIONS_2 = { CONF_ACCOUNTS: { - ACCOUNT_1: { - CONF_NAME: ACCOUNT_NAME_1, - "enabled": True, - }, - ACCOUNT_2: { - CONF_NAME: ACCOUNT_NAME_2, - "enabled": True, - }, + ACCOUNT_1: ACCOUNT_NAME_1, + ACCOUNT_2: ACCOUNT_NAME_2, } } CONF_IMPORT_OPTIONS = { CONF_ACCOUNTS: { - ACCOUNT_1: { - CONF_NAME: ACCOUNT_NAME_1, - "enabled": True, - }, - ACCOUNT_2: { - CONF_NAME: ACCOUNT_NAME_2, - "enabled": True, - }, + ACCOUNT_1: ACCOUNT_NAME_1, + ACCOUNT_2: ACCOUNT_NAME_2, } } diff --git a/tests/components/steam_online/test_config_flow.py b/tests/components/steam_online/test_config_flow.py index 68b7612f049..6d8a16f35f7 100644 --- a/tests/components/steam_online/test_config_flow.py +++ b/tests/components/steam_online/test_config_flow.py @@ -1,4 +1,6 @@ """Test Steam config flow.""" +from unittest.mock import patch + import steam from homeassistant import data_entry_flow @@ -25,7 +27,10 @@ from . import ( async def test_flow_user(hass: HomeAssistant) -> None: """Test user initialized flow.""" - with patch_interface(): + with patch_interface(), patch( + "homeassistant.components.steam_online.async_setup_entry", + return_value=True, + ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, From 66a21e0bc3813249d9e4fe8dd420fca4dbfc0f32 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 30 Apr 2022 11:10:20 -0500 Subject: [PATCH 111/930] Add basic system health data to the recorder (#71086) --- .../components/recorder/run_history.py | 7 ++++ .../components/recorder/strings.json | 8 ++++ .../components/recorder/system_health.py | 24 ++++++++++++ .../components/recorder/translations/en.json | 8 ++++ .../components/recorder/test_system_health.py | 38 +++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 homeassistant/components/recorder/strings.json create mode 100644 homeassistant/components/recorder/system_health.py create mode 100644 homeassistant/components/recorder/translations/en.json create mode 100644 tests/components/recorder/test_system_health.py diff --git a/homeassistant/components/recorder/run_history.py b/homeassistant/components/recorder/run_history.py index 3a76eef3c83..783aff89c17 100644 --- a/homeassistant/components/recorder/run_history.py +++ b/homeassistant/components/recorder/run_history.py @@ -53,6 +53,13 @@ class RunHistory: """Return the time the recorder started recording states.""" return self._recording_start + @property + def first(self) -> RecorderRuns: + """Get the first run.""" + if runs_by_timestamp := self._run_history.runs_by_timestamp: + return next(iter(runs_by_timestamp.values())) + return self.current + @property def current(self) -> RecorderRuns: """Get the current run.""" diff --git a/homeassistant/components/recorder/strings.json b/homeassistant/components/recorder/strings.json new file mode 100644 index 00000000000..72fcf322c31 --- /dev/null +++ b/homeassistant/components/recorder/strings.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "oldest_recorder_run": "Oldest Run Start Time", + "current_recorder_run": "Current Run Start Time" + } + } +} diff --git a/homeassistant/components/recorder/system_health.py b/homeassistant/components/recorder/system_health.py new file mode 100644 index 00000000000..23fb760e898 --- /dev/null +++ b/homeassistant/components/recorder/system_health.py @@ -0,0 +1,24 @@ +"""Provide info to system health.""" + +from homeassistant.components import system_health +from homeassistant.core import HomeAssistant, callback + +from . import get_instance + + +@callback +def async_register( + hass: HomeAssistant, register: system_health.SystemHealthRegistration +) -> None: + """Register system health callbacks.""" + register.async_register_info(system_health_info) + + +async def system_health_info(hass: HomeAssistant): + """Get info for the info page.""" + instance = get_instance(hass) + run_history = instance.run_history + return { + "oldest_recorder_run": run_history.first.start, + "current_recorder_run": run_history.current.start, + } diff --git a/homeassistant/components/recorder/translations/en.json b/homeassistant/components/recorder/translations/en.json new file mode 100644 index 00000000000..a44ecd3c1d6 --- /dev/null +++ b/homeassistant/components/recorder/translations/en.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Current Run Start Time", + "oldest_recorder_run": "Oldest Run Start Time" + } + } +} \ No newline at end of file diff --git a/tests/components/recorder/test_system_health.py b/tests/components/recorder/test_system_health.py new file mode 100644 index 00000000000..75abc2b6ae1 --- /dev/null +++ b/tests/components/recorder/test_system_health.py @@ -0,0 +1,38 @@ +"""Test recorder system health.""" + +from unittest.mock import patch + +from homeassistant.components.recorder import get_instance +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from .common import async_wait_recording_done + +from tests.common import SetupRecorderInstanceT, get_system_health_info + + +async def test_recorder_system_health(hass, recorder_mock): + """Test recorder system health.""" + assert await async_setup_component(hass, "system_health", {}) + await async_wait_recording_done(hass) + info = await get_system_health_info(hass, "recorder") + instance = get_instance(hass) + assert info == { + "current_recorder_run": instance.run_history.current.start, + "oldest_recorder_run": instance.run_history.first.start, + } + + +async def test_recorder_system_health_crashed_recorder_runs_table( + hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT +): + """Test recorder system health with crashed recorder runs table.""" + with patch("homeassistant.components.recorder.run_history.RunHistory.load_from_db"): + assert await async_setup_component(hass, "system_health", {}) + instance = await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + info = await get_system_health_info(hass, "recorder") + assert info == { + "current_recorder_run": instance.run_history.current.start, + "oldest_recorder_run": instance.run_history.current.start, + } From 75debb7dece50744713a2822fe8f508ce633c818 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Sat, 30 Apr 2022 19:24:24 +0300 Subject: [PATCH 112/930] Add entity id to template error logging (#71107) * Add entity id to template error logging * Increase coverage --- .../template/alarm_control_panel.py | 3 +- homeassistant/components/template/cover.py | 3 +- homeassistant/components/template/fan.py | 30 ++++++++++++++----- homeassistant/components/template/light.py | 30 +++++++++++++------ homeassistant/components/template/vacuum.py | 13 +++++--- tests/components/template/test_light.py | 1 + 6 files changed, 57 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index 07610a59457..9d81dce28fe 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -207,8 +207,9 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity): return _LOGGER.error( - "Received invalid alarm panel state: %s. Expected: %s", + "Received invalid alarm panel state: %s for entity %s. Expected: %s", result, + self.entity_id, ", ".join(_VALID_STATES), ) self._state = None diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index 7a05f9445f9..e1df61bf4a2 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -222,8 +222,9 @@ class CoverTemplate(TemplateEntity, CoverEntity): self._is_closing = state == STATE_CLOSING else: _LOGGER.error( - "Received invalid cover is_on state: %s. Expected: %s", + "Received invalid cover is_on state: %s for entity %s. Expected: %s", state, + self.entity_id, ", ".join(_VALID_STATES), ) if not self._position_template: diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index 2ee63e7e318..6b0fdefc2f9 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -285,8 +285,9 @@ class TemplateFan(TemplateEntity, FanEntity): """Set the preset_mode of the fan.""" if self.preset_modes and preset_mode not in self.preset_modes: _LOGGER.error( - "Received invalid preset_mode: %s. Expected: %s", + "Received invalid preset_mode: %s for entity %s. Expected: %s", preset_mode, + self.entity_id, self.preset_modes, ) return @@ -322,8 +323,9 @@ class TemplateFan(TemplateEntity, FanEntity): ) else: _LOGGER.error( - "Received invalid direction: %s. Expected: %s", + "Received invalid direction: %s for entity %s. Expected: %s", direction, + self.entity_id, ", ".join(_VALID_DIRECTIONS), ) @@ -341,8 +343,9 @@ class TemplateFan(TemplateEntity, FanEntity): self._state = None else: _LOGGER.error( - "Received invalid fan is_on state: %s. Expected: %s", + "Received invalid fan is_on state: %s for entity %s. Expected: %s", result, + self.entity_id, ", ".join(_VALID_STATES), ) self._state = None @@ -390,7 +393,11 @@ class TemplateFan(TemplateEntity, FanEntity): try: percentage = int(float(percentage)) except ValueError: - _LOGGER.error("Received invalid percentage: %s", percentage) + _LOGGER.error( + "Received invalid percentage: %s for entity %s", + percentage, + self.entity_id, + ) self._percentage = 0 self._preset_mode = None return @@ -399,7 +406,11 @@ class TemplateFan(TemplateEntity, FanEntity): self._percentage = percentage self._preset_mode = None else: - _LOGGER.error("Received invalid percentage: %s", percentage) + _LOGGER.error( + "Received invalid percentage: %s for entity %s", + percentage, + self.entity_id, + ) self._percentage = 0 self._preset_mode = None @@ -416,8 +427,9 @@ class TemplateFan(TemplateEntity, FanEntity): self._preset_mode = None else: _LOGGER.error( - "Received invalid preset_mode: %s. Expected: %s", + "Received invalid preset_mode: %s for entity %s. Expected: %s", preset_mode, + self.entity_id, self.preset_mode, ) self._percentage = None @@ -434,8 +446,9 @@ class TemplateFan(TemplateEntity, FanEntity): self._oscillating = None else: _LOGGER.error( - "Received invalid oscillating: %s. Expected: True/False", + "Received invalid oscillating: %s for entity %s. Expected: True/False", oscillating, + self.entity_id, ) self._oscillating = None @@ -448,8 +461,9 @@ class TemplateFan(TemplateEntity, FanEntity): self._direction = None else: _LOGGER.error( - "Received invalid direction: %s. Expected: %s", + "Received invalid direction: %s for entity %s. Expected: %s", direction, + self.entity_id, ", ".join(_VALID_DIRECTIONS), ) self._direction = None diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 32ce215a41f..0ebdc3fb93a 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -397,8 +397,9 @@ class LightTemplate(TemplateEntity, LightEntity): effect = kwargs[ATTR_EFFECT] if effect not in self._effect_list: _LOGGER.error( - "Received invalid effect: %s. Expected one of: %s", + "Received invalid effect: %s for entity %s. Expected one of: %s", effect, + self.entity_id, self._effect_list, exc_info=True, ) @@ -447,7 +448,9 @@ class LightTemplate(TemplateEntity, LightEntity): self._brightness = int(brightness) else: _LOGGER.error( - "Received invalid brightness : %s. Expected: 0-255", brightness + "Received invalid brightness : %s for entity %s. Expected: 0-255", + brightness, + self.entity_id, ) self._brightness = None except ValueError: @@ -468,7 +471,9 @@ class LightTemplate(TemplateEntity, LightEntity): self._white_value = int(white_value) else: _LOGGER.error( - "Received invalid white value: %s. Expected: 0-255", white_value + "Received invalid white value: %s for entity %s. Expected: 0-255", + white_value, + self.entity_id, ) self._white_value = None except ValueError: @@ -487,8 +492,9 @@ class LightTemplate(TemplateEntity, LightEntity): if not isinstance(effect_list, list): _LOGGER.error( - "Received invalid effect list: %s. Expected list of strings", + "Received invalid effect list: %s for entity %s. Expected list of strings", effect_list, + self.entity_id, ) self._effect_list = None return @@ -508,8 +514,9 @@ class LightTemplate(TemplateEntity, LightEntity): if effect not in self._effect_list: _LOGGER.error( - "Received invalid effect: %s. Expected one of: %s", + "Received invalid effect: %s for entity %s. Expected one of: %s", effect, + self.entity_id, self._effect_list, ) self._effect = None @@ -537,8 +544,9 @@ class LightTemplate(TemplateEntity, LightEntity): return _LOGGER.error( - "Received invalid light is_on state: %s. Expected: %s", + "Received invalid light is_on state: %s for entity %s. Expected: %s", state, + self.entity_id, ", ".join(_VALID_STATES), ) self._state = None @@ -555,8 +563,9 @@ class LightTemplate(TemplateEntity, LightEntity): self._temperature = temperature else: _LOGGER.error( - "Received invalid color temperature : %s. Expected: %s-%s", + "Received invalid color temperature : %s for entity %s. Expected: %s-%s", temperature, + self.entity_id, self.min_mireds, self.max_mireds, ) @@ -595,13 +604,16 @@ class LightTemplate(TemplateEntity, LightEntity): self._color = (h_str, s_str) elif h_str is not None and s_str is not None: _LOGGER.error( - "Received invalid hs_color : (%s, %s). Expected: (0-360, 0-100)", + "Received invalid hs_color : (%s, %s) for entity %s. Expected: (0-360, 0-100)", h_str, s_str, + self.entity_id, ) self._color = None else: - _LOGGER.error("Received invalid hs_color : (%s)", render) + _LOGGER.error( + "Received invalid hs_color : (%s) for entity %s", render, self.entity_id + ) self._color = None @callback diff --git a/homeassistant/components/template/vacuum.py b/homeassistant/components/template/vacuum.py index 2a004eabc9a..1d350d120c7 100644 --- a/homeassistant/components/template/vacuum.py +++ b/homeassistant/components/template/vacuum.py @@ -253,8 +253,9 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): ) else: _LOGGER.error( - "Received invalid fan speed: %s. Expected: %s", + "Received invalid fan speed: %s for entity %s. Expected: %s", fan_speed, + self.entity_id, self._attr_fan_speed_list, ) @@ -298,8 +299,9 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): self._state = None else: _LOGGER.error( - "Received invalid vacuum state: %s. Expected: %s", + "Received invalid vacuum state: %s for entity %s. Expected: %s", result, + self.entity_id, ", ".join(_VALID_STATES), ) self._state = None @@ -312,7 +314,9 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): raise ValueError except ValueError: _LOGGER.error( - "Received invalid battery level: %s. Expected: 0-100", battery_level + "Received invalid battery level: %s for entity %s. Expected: 0-100", + battery_level, + self.entity_id, ) self._attr_battery_level = None return @@ -333,8 +337,9 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): self._attr_fan_speed = None else: _LOGGER.error( - "Received invalid fan speed: %s. Expected: %s", + "Received invalid fan speed: %s for entity %s. Expected: %s", fan_speed, + self.entity_id, self._attr_fan_speed_list, ) self._attr_fan_speed = None diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index 2b877c52c4f..642fa5601cf 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -1025,6 +1025,7 @@ async def test_color_action_no_template(hass, start_ha, calls): ((359.9, 99.9), {"replace6": '"{{(359.9, 99.9)}}"'}), (None, {"replace6": '"{{(361, 100)}}"'}), (None, {"replace6": '"{{(360, 101)}}"'}), + (None, {"replace6": '"[{{(360)}},{{null}}]"'}), (None, {"replace6": '"{{x - 12}}"'}), (None, {"replace6": '""'}), (None, {"replace6": '"{{ none }}"'}), From 1396213d78fe6403fa8d21f5de56dab37c170727 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 30 Apr 2022 18:26:14 +0200 Subject: [PATCH 113/930] Fix copy paste issue leaving one device trigger with a wrong subtype (#71121) --- homeassistant/components/deconz/device_trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index c76aaf481bf..c92ad7f46dc 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -403,7 +403,7 @@ AQARA_OPPLE_4_BUTTONS = { AQARA_OPPLE_6_BUTTONS_MODEL = "lumi.remote.b686opcn01" AQARA_OPPLE_6_BUTTONS = { **AQARA_OPPLE_4_BUTTONS, - (CONF_LONG_PRESS, CONF_DIM_DOWN): {CONF_EVENT: 5001}, + (CONF_LONG_PRESS, CONF_LEFT): {CONF_EVENT: 5001}, (CONF_SHORT_RELEASE, CONF_LEFT): {CONF_EVENT: 5002}, (CONF_LONG_RELEASE, CONF_LEFT): {CONF_EVENT: 5003}, (CONF_DOUBLE_PRESS, CONF_LEFT): {CONF_EVENT: 5004}, From 731a8ac74fc801f9c9cf215a5210bb3305ee7bdd Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 30 Apr 2022 18:28:47 +0200 Subject: [PATCH 114/930] Sensibo bugfix device on (#71106) Co-authored-by: J. Nick Koston --- homeassistant/components/sensibo/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 3ab6f06c4fb..907105b5384 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -45,7 +45,7 @@ HA_TO_SENSIBO = {value: key for key, value in SENSIBO_TO_HA.items()} AC_STATE_TO_DATA = { "targetTemperature": "target_temp", "fanLevel": "fan_mode", - "on": "on", + "on": "device_on", "mode": "hvac_mode", "swing": "swing_mode", } From c8ea264db852e779b450f0dc0ed2e80046143ebd Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 30 Apr 2022 09:33:30 -0700 Subject: [PATCH 115/930] Bump gcal_sync to 0.7.0 (#71116) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 0e9a2fe7ddb..46c5844819a 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["auth"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.6.3", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.7.0", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 67d4a7b45a7..6d2ef35b3eb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.6.3 +gcal-sync==0.7.0 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4d5974047c9..7fc6f066d0c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -486,7 +486,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.6.3 +gcal-sync==0.7.0 # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 From e3f224e1157b185e030a6611200e1bddb1bf6d0b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sat, 30 Apr 2022 18:57:05 +0200 Subject: [PATCH 116/930] Use shorthand attributes in neato vacuum (#70843) --- homeassistant/components/neato/vacuum.py | 47 +++++------------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/neato/vacuum.py b/homeassistant/components/neato/vacuum.py index 3a4096055bc..4d402fbb8bb 100644 --- a/homeassistant/components/neato/vacuum.py +++ b/homeassistant/components/neato/vacuum.py @@ -94,6 +94,7 @@ async def async_setup_entry( class NeatoConnectedVacuum(StateVacuumEntity): """Representation of a Neato Connected Vacuum.""" + _attr_icon = "mdi:robot-vacuum-variant" _attr_supported_features = ( VacuumEntityFeature.BATTERY | VacuumEntityFeature.PAUSE @@ -115,12 +116,13 @@ class NeatoConnectedVacuum(StateVacuumEntity): ) -> None: """Initialize the Neato Connected Vacuum.""" self.robot = robot - self._available: bool = neato is not None + self._attr_available: bool = neato is not None self._mapdata = mapdata - self._name: str = f"{self.robot.name}" + self._attr_name: str = self.robot.name self._robot_has_map: bool = self.robot.has_persistent_maps self._robot_maps = persistent_maps self._robot_serial: str = self.robot.serial + self._attr_unique_id: str = self.robot.serial self._status_state: str | None = None self._clean_state: str | None = None self._state: dict[str, Any] | None = None @@ -134,7 +136,6 @@ class NeatoConnectedVacuum(StateVacuumEntity): self._clean_pause_time: int | None = None self._clean_error_time: int | None = None self._launched_from: str | None = None - self._battery_level: int | None = None self._robot_boundaries: list = [] self._robot_stats: dict[str, Any] | None = None @@ -150,17 +151,17 @@ class NeatoConnectedVacuum(StateVacuumEntity): try: self._state = self.robot.state except NeatoRobotException as ex: - if self._available: # print only once when available + if self._attr_available: # print only once when available _LOGGER.error( "Neato vacuum connection error for '%s': %s", self.entity_id, ex ) self._state = None - self._available = False + self._attr_available = False return if self._state is None: return - self._available = True + self._attr_available = True _LOGGER.debug("self._state=%s", self._state) if "alert" in self._state: robot_alert = ALERTS.get(self._state["alert"]) @@ -205,7 +206,7 @@ class NeatoConnectedVacuum(StateVacuumEntity): self._clean_state = STATE_ERROR self._status_state = ERRORS.get(self._state["error"]) - self._battery_level = self._state["details"]["charge"] + self._attr_battery_level = self._state["details"]["charge"] if self._mapdata is None or not self._mapdata.get(self._robot_serial, {}).get( "maps", [] @@ -260,41 +261,11 @@ class NeatoConnectedVacuum(StateVacuumEntity): self._robot_boundaries, ) - @property - def name(self) -> str: - """Return the name of the device.""" - return self._name - - @property - def supported_features(self) -> int: - """Flag vacuum cleaner robot features that are supported.""" - return self._attr_supported_features - - @property - def battery_level(self) -> int | None: - """Return the battery level of the vacuum cleaner.""" - return self._battery_level - - @property - def available(self) -> bool: - """Return if the robot is available.""" - return self._available - - @property - def icon(self) -> str: - """Return neato specific icon.""" - return "mdi:robot-vacuum-variant" - @property def state(self) -> str | None: """Return the status of the vacuum cleaner.""" return self._clean_state - @property - def unique_id(self) -> str: - """Return a unique ID.""" - return self._robot_serial - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the vacuum cleaner.""" @@ -333,7 +304,7 @@ class NeatoConnectedVacuum(StateVacuumEntity): identifiers={(NEATO_DOMAIN, self._robot_serial)}, manufacturer=stats["battery"]["vendor"] if stats else None, model=stats["model"] if stats else None, - name=self._name, + name=self._attr_name, sw_version=stats["firmware"] if stats else None, ) From a0fe5b02476fc89ee20e5e3d9b0bcb9490c10f5f Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 30 Apr 2022 19:33:47 +0200 Subject: [PATCH 117/930] Make deCONZ SSDP discovery more strict by matching on manufacturerURL (#71124) --- homeassistant/components/deconz/config_flow.py | 6 ------ homeassistant/components/deconz/manifest.json | 3 ++- homeassistant/components/deconz/strings.json | 1 - homeassistant/generated/ssdp.py | 3 ++- tests/components/deconz/test_config_flow.py | 16 ---------------- 5 files changed, 4 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index af37ee96878..28205a7382d 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -215,12 +215,6 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN): async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult: """Handle a discovered deCONZ bridge.""" - if ( - discovery_info.upnp.get(ssdp.ATTR_UPNP_MANUFACTURER_URL) - != DECONZ_MANUFACTURERURL - ): - return self.async_abort(reason="not_deconz_bridge") - LOGGER.debug("deCONZ SSDP discovery %s", pformat(discovery_info)) self.bridge_id = normalize_bridge_id(discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL]) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index dee41435779..1ce3477db70 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -6,7 +6,8 @@ "requirements": ["pydeconz==91"], "ssdp": [ { - "manufacturer": "Royal Philips Electronics" + "manufacturer": "Royal Philips Electronics", + "manufacturerURL": "http://www.dresden-elektronik.de" } ], "codeowners": ["@Kane610"], diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 4098951d714..55bb86d03f6 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -31,7 +31,6 @@ "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "no_bridges": "No deCONZ bridges discovered", "no_hardware_available": "No radio hardware connected to deCONZ", - "not_deconz_bridge": "Not a deCONZ bridge", "updated_instance": "Updated deCONZ instance with new host address" } }, diff --git a/homeassistant/generated/ssdp.py b/homeassistant/generated/ssdp.py index 1c0876cd791..851d9b0fd10 100644 --- a/homeassistant/generated/ssdp.py +++ b/homeassistant/generated/ssdp.py @@ -24,7 +24,8 @@ SSDP = { ], "deconz": [ { - "manufacturer": "Royal Philips Electronics" + "manufacturer": "Royal Philips Electronics", + "manufacturerURL": "http://www.dresden-elektronik.de" } ], "denonavr": [ diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index 97b2f7ee164..1a7031a0fd6 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -459,22 +459,6 @@ async def test_flow_ssdp_discovery(hass, aioclient_mock): } -async def test_flow_ssdp_bad_discovery(hass, aioclient_mock): - """Test that SSDP discovery aborts if manufacturer URL is wrong.""" - result = await hass.config_entries.flow.async_init( - DECONZ_DOMAIN, - data=ssdp.SsdpServiceInfo( - ssdp_usn="mock_usn", - ssdp_st="mock_st", - upnp={ATTR_UPNP_MANUFACTURER_URL: "other"}, - ), - context={"source": SOURCE_SSDP}, - ) - - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "not_deconz_bridge" - - async def test_ssdp_discovery_update_configuration(hass, aioclient_mock): """Test if a discovered bridge is configured but updates with new attributes.""" config_entry = await setup_deconz_integration(hass, aioclient_mock) From 109b4d45ca721230cf52bc807a5b6013af5cce7e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 30 Apr 2022 14:17:18 -0500 Subject: [PATCH 118/930] Allow matching ssdp by manufacturerURL only (#71125) --- homeassistant/components/ssdp/__init__.py | 10 ++++- tests/components/ssdp/test_init.py | 54 +++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index b59dc0ce1ee..8e8663a9734 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -45,6 +45,8 @@ ATTR_SSDP_SERVER = "ssdp_server" ATTR_SSDP_BOOTID = "BOOTID.UPNP.ORG" ATTR_SSDP_NEXTBOOTID = "NEXTBOOTID.UPNP.ORG" # Attributes for accessing info from retrieved UPnP device description +ATTR_ST = "st" +ATTR_NT = "nt" ATTR_UPNP_DEVICE_TYPE = "deviceType" ATTR_UPNP_FRIENDLY_NAME = "friendlyName" ATTR_UPNP_MANUFACTURER = "manufacturer" @@ -61,7 +63,13 @@ ATTR_UPNP_PRESENTATION_URL = "presentationURL" # Attributes for accessing info added by Home Assistant ATTR_HA_MATCHING_DOMAINS = "x_homeassistant_matching_domains" -PRIMARY_MATCH_KEYS = [ATTR_UPNP_MANUFACTURER, "st", ATTR_UPNP_DEVICE_TYPE, "nt"] +PRIMARY_MATCH_KEYS = [ + ATTR_UPNP_MANUFACTURER, + ATTR_ST, + ATTR_UPNP_DEVICE_TYPE, + ATTR_NT, + ATTR_UPNP_MANUFACTURER_URL, +] _LOGGER = logging.getLogger(__name__) diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index b947ce4269a..5bbc90307b5 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -88,6 +88,60 @@ async def test_ssdp_flow_dispatched_on_st(mock_get_ssdp, hass, caplog, mock_flow # End compatibility checks +@patch( + "homeassistant.components.ssdp.async_get_ssdp", + return_value={"mock-domain": [{"manufacturerURL": "mock-url"}]}, +) +@pytest.mark.usefixtures("mock_get_source_ip") +async def test_ssdp_flow_dispatched_on_manufacturer_url( + mock_get_ssdp, hass, caplog, mock_flow_init +): + """Test matching based on manufacturerURL.""" + mock_ssdp_search_response = _ssdp_headers( + { + "st": "mock-st", + "manufacturerURL": "mock-url", + "location": "http://1.1.1.1", + "usn": "uuid:mock-udn::mock-st", + "server": "mock-server", + "ext": "", + } + ) + ssdp_listener = await init_ssdp_component(hass) + await ssdp_listener._on_search(mock_ssdp_search_response) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + assert len(mock_flow_init.mock_calls) == 1 + assert mock_flow_init.mock_calls[0][1][0] == "mock-domain" + assert mock_flow_init.mock_calls[0][2]["context"] == { + "source": config_entries.SOURCE_SSDP + } + mock_call_data: ssdp.SsdpServiceInfo = mock_flow_init.mock_calls[0][2]["data"] + assert mock_call_data.ssdp_st == "mock-st" + assert mock_call_data.ssdp_location == "http://1.1.1.1" + assert mock_call_data.ssdp_usn == "uuid:mock-udn::mock-st" + assert mock_call_data.ssdp_server == "mock-server" + assert mock_call_data.ssdp_ext == "" + assert mock_call_data.ssdp_udn == ANY + assert mock_call_data.ssdp_headers["_timestamp"] == ANY + assert mock_call_data.x_homeassistant_matching_domains == {"mock-domain"} + assert mock_call_data.upnp == {ssdp.ATTR_UPNP_UDN: "uuid:mock-udn"} + assert "Failed to fetch ssdp data" not in caplog.text + # Compatibility with old dict access (to be removed after 2022.6) + assert mock_call_data[ssdp.ATTR_SSDP_ST] == "mock-st" + assert mock_call_data[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" + assert mock_call_data[ssdp.ATTR_SSDP_USN] == "uuid:mock-udn::mock-st" + assert mock_call_data[ssdp.ATTR_SSDP_SERVER] == "mock-server" + assert mock_call_data[ssdp.ATTR_SSDP_EXT] == "" + assert mock_call_data[ssdp.ATTR_UPNP_UDN] == "uuid:mock-udn" + assert mock_call_data[ssdp.ATTR_SSDP_UDN] == ANY + assert mock_call_data["_timestamp"] == ANY + assert mock_call_data[ssdp.ATTR_HA_MATCHING_DOMAINS] == {"mock-domain"} + # End compatibility checks + + @pytest.mark.usefixtures("mock_get_source_ip") @patch( "homeassistant.components.ssdp.async_get_ssdp", From 7b2947bad7d41bc6ffc695929650dfcfd19998e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 30 Apr 2022 22:13:35 +0200 Subject: [PATCH 119/930] Add QNAP QSW diagnostics support (#70871) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * qnap_qsw: add diagnostics support Signed-off-by: Álvaro Fernández Rojas * qnap_qsw: diagnostics: several improvements - Provide full config_entry. - Reduce number of test asserts. Signed-off-by: Álvaro Fernández Rojas --- .../components/qnap_qsw/diagnostics.py | 37 ++++++ tests/components/qnap_qsw/test_diagnostics.py | 123 ++++++++++++++++++ tests/components/qnap_qsw/util.py | 10 +- 3 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/qnap_qsw/diagnostics.py create mode 100644 tests/components/qnap_qsw/test_diagnostics.py diff --git a/homeassistant/components/qnap_qsw/diagnostics.py b/homeassistant/components/qnap_qsw/diagnostics.py new file mode 100644 index 00000000000..3730bab24a8 --- /dev/null +++ b/homeassistant/components/qnap_qsw/diagnostics.py @@ -0,0 +1,37 @@ +"""Support for the QNAP QSW diagnostics.""" +from __future__ import annotations + +from typing import Any + +from aioqsw.const import QSD_MAC, QSD_SERIAL + +from homeassistant.components.diagnostics.util import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD, CONF_UNIQUE_ID, CONF_USERNAME +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .coordinator import QswUpdateCoordinator + +TO_REDACT_CONFIG = [ + CONF_USERNAME, + CONF_PASSWORD, + CONF_UNIQUE_ID, +] + +TO_REDACT_DATA = [ + QSD_MAC, + QSD_SERIAL, +] + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + coordinator: QswUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + return { + "config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT_CONFIG), + "coord_data": async_redact_data(coordinator.data, TO_REDACT_DATA), + } diff --git a/tests/components/qnap_qsw/test_diagnostics.py b/tests/components/qnap_qsw/test_diagnostics.py new file mode 100644 index 00000000000..a4b5c1658e1 --- /dev/null +++ b/tests/components/qnap_qsw/test_diagnostics.py @@ -0,0 +1,123 @@ +"""The diagnostics tests for the QNAP QSW platform.""" + +from aiohttp import ClientSession +from aioqsw.const import ( + API_ANOMALY, + API_BUILD_NUMBER, + API_FAN1_SPEED, + API_MAX_SWITCH_TEMP, + API_NUMBER, + API_PRODUCT, + API_RESULT, + API_SWITCH_TEMP, + API_UPTIME, + API_VERSION, + QSD_ANOMALY, + QSD_BUILD_NUMBER, + QSD_FAN1_SPEED, + QSD_FIRMWARE_CONDITION, + QSD_FIRMWARE_INFO, + QSD_MAC, + QSD_NUMBER, + QSD_PRODUCT, + QSD_SERIAL, + QSD_SYSTEM_BOARD, + QSD_SYSTEM_SENSOR, + QSD_SYSTEM_TIME, + QSD_TEMP, + QSD_TEMP_MAX, + QSD_UPTIME, + QSD_VERSION, +) + +from homeassistant.components.diagnostics.const import REDACTED +from homeassistant.components.qnap_qsw.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME +from homeassistant.core import HomeAssistant + +from .util import ( + CONFIG, + FIRMWARE_CONDITION_MOCK, + FIRMWARE_INFO_MOCK, + SYSTEM_BOARD_MOCK, + SYSTEM_SENSOR_MOCK, + SYSTEM_TIME_MOCK, + async_init_integration, +) + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_config_entry_diagnostics( + hass: HomeAssistant, hass_client: ClientSession +) -> None: + """Test config entry diagnostics.""" + await async_init_integration(hass) + assert hass.data[DOMAIN] + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + + assert ( + diag["config_entry"].items() + >= { + "data": { + CONF_PASSWORD: REDACTED, + CONF_URL: CONFIG[CONF_URL], + CONF_USERNAME: REDACTED, + }, + "domain": DOMAIN, + "unique_id": REDACTED, + }.items() + ) + + fw_cond_diag = diag["coord_data"][QSD_FIRMWARE_CONDITION] + fw_cond_mock = FIRMWARE_CONDITION_MOCK[API_RESULT] + assert ( + fw_cond_diag.items() + >= { + QSD_ANOMALY: fw_cond_mock[API_ANOMALY], + }.items() + ) + + fw_info_diag = diag["coord_data"][QSD_FIRMWARE_INFO] + fw_info_mock = FIRMWARE_INFO_MOCK[API_RESULT] + assert ( + fw_info_diag.items() + >= { + QSD_BUILD_NUMBER: fw_info_mock[API_BUILD_NUMBER], + QSD_NUMBER: fw_info_mock[API_NUMBER], + QSD_VERSION: fw_info_mock[API_VERSION], + }.items() + ) + + sys_board_diag = diag["coord_data"][QSD_SYSTEM_BOARD] + sys_board_mock = SYSTEM_BOARD_MOCK[API_RESULT] + assert ( + sys_board_diag.items() + >= { + QSD_MAC: REDACTED, + QSD_PRODUCT: sys_board_mock[API_PRODUCT], + QSD_SERIAL: REDACTED, + }.items() + ) + + sys_sensor_diag = diag["coord_data"][QSD_SYSTEM_SENSOR] + sys_sensor_mock = SYSTEM_SENSOR_MOCK[API_RESULT] + assert ( + sys_sensor_diag.items() + >= { + QSD_FAN1_SPEED: sys_sensor_mock[API_FAN1_SPEED], + QSD_TEMP: sys_sensor_mock[API_SWITCH_TEMP], + QSD_TEMP_MAX: sys_sensor_mock[API_MAX_SWITCH_TEMP], + }.items() + ) + + sys_time_diag = diag["coord_data"][QSD_SYSTEM_TIME] + sys_time_mock = SYSTEM_TIME_MOCK[API_RESULT] + assert ( + sys_time_diag.items() + >= { + QSD_UPTIME: sys_time_mock[API_UPTIME], + }.items() + ) diff --git a/tests/components/qnap_qsw/util.py b/tests/components/qnap_qsw/util.py index 501c31f55e9..57b7b61a59d 100644 --- a/tests/components/qnap_qsw/util.py +++ b/tests/components/qnap_qsw/util.py @@ -127,8 +127,12 @@ async def async_init_integration( ) -> None: """Set up the QNAP QSW integration in Home Assistant.""" - entry = MockConfigEntry(domain=DOMAIN, data=CONFIG) - entry.add_to_hass(hass) + config_entry = MockConfigEntry( + data=CONFIG, + domain=DOMAIN, + unique_id="qsw_unique_id", + ) + config_entry.add_to_hass(hass) with patch( "homeassistant.components.qnap_qsw.QnapQswApi.get_firmware_condition", @@ -149,5 +153,5 @@ async def async_init_integration( "homeassistant.components.qnap_qsw.QnapQswApi.post_users_login", return_value=USERS_LOGIN_MOCK, ): - await hass.config_entries.async_setup(entry.entry_id) + await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() From 285fdeb581c485047dd6beb9e9acd6b8798601b9 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 30 Apr 2022 17:21:30 -0700 Subject: [PATCH 120/930] Add calendar trigger offsets (#70963) * Add support for calendar trigger offsets * Add offset end test * Update homeassistant/components/calendar/trigger.py Co-authored-by: Martin Hjelmare * Always include offset in trigger data Co-authored-by: Martin Hjelmare --- homeassistant/components/calendar/trigger.py | 37 +++++++--- tests/components/calendar/test_trigger.py | 78 +++++++++++++++++++- 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/calendar/trigger.py b/homeassistant/components/calendar/trigger.py index 3540a9f5148..bb6e874b47f 100644 --- a/homeassistant/components/calendar/trigger.py +++ b/homeassistant/components/calendar/trigger.py @@ -11,7 +11,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.const import CONF_ENTITY_ID, CONF_EVENT, CONF_PLATFORM +from homeassistant.const import CONF_ENTITY_ID, CONF_EVENT, CONF_OFFSET, CONF_PLATFORM from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv @@ -36,6 +36,7 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( vol.Required(CONF_PLATFORM): DOMAIN, vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Optional(CONF_EVENT, default=EVENT_START): vol.In({EVENT_START, EVENT_END}), + vol.Optional(CONF_OFFSET, default=datetime.timedelta(0)): cv.time_period, } ) @@ -50,12 +51,14 @@ class CalendarEventListener: trigger_data: dict[str, Any], entity: CalendarEntity, event_type: str, + offset: datetime.timedelta, ) -> None: """Initialize CalendarEventListener.""" self._hass = hass self._job = job self._trigger_data = trigger_data self._entity = entity + self._offset = offset self._unsub_event: CALLBACK_TYPE | None = None self._unsub_refresh: CALLBACK_TYPE | None = None # Upcoming set of events with their trigger time @@ -81,10 +84,18 @@ class CalendarEventListener: async def _fetch_events(self, last_endtime: datetime.datetime) -> None: """Update the set of eligible events.""" + # Use a sliding window for selecting in scope events in the next interval. The event + # search range is offset, then the fire time of the returned events are offset again below. # Event time ranges are exclusive so the end time is expanded by 1sec - end_time = last_endtime + UPDATE_INTERVAL + datetime.timedelta(seconds=1) - _LOGGER.debug("Fetching events between %s, %s", last_endtime, end_time) - events = await self._entity.async_get_events(self._hass, last_endtime, end_time) + start_time = last_endtime - self._offset + end_time = start_time + UPDATE_INTERVAL + datetime.timedelta(seconds=1) + _LOGGER.debug( + "Fetching events between %s, %s (offset=%s)", + start_time, + end_time, + self._offset, + ) + events = await self._entity.async_get_events(self._hass, start_time, end_time) # Build list of events and the appropriate time to trigger an alarm. The # returned events may have already started but matched the start/end time @@ -92,13 +103,14 @@ class CalendarEventListener: # trigger time. event_list = [] for event in events: - event_time = ( + event_fire_time = ( event.start_datetime_local if self._event_type == EVENT_START else event.end_datetime_local ) - if event_time > last_endtime: - event_list.append((event_time, event)) + event_fire_time += self._offset + if event_fire_time > last_endtime: + event_list.append((event_fire_time, event)) event_list.sort(key=lambda x: x[0]) self._events = event_list _LOGGER.debug("Populated event list %s", self._events) @@ -109,12 +121,12 @@ class CalendarEventListener: if not self._events: return - (event_datetime, _event) = self._events[0] - _LOGGER.debug("Scheduling next event trigger @ %s", event_datetime) + (event_fire_time, _event) = self._events[0] + _LOGGER.debug("Scheduled alarm for %s", event_fire_time) self._unsub_event = async_track_point_in_utc_time( self._hass, self._handle_calendar_event, - event_datetime, + event_fire_time, ) def _clear_event_listener(self) -> None: @@ -160,6 +172,7 @@ async def async_attach_trigger( """Attach trigger for the specified calendar.""" entity_id = config[CONF_ENTITY_ID] event_type = config[CONF_EVENT] + offset = config[CONF_OFFSET] component: EntityComponent = hass.data[DOMAIN] if not (entity := component.get_entity(entity_id)) or not isinstance( @@ -173,10 +186,10 @@ async def async_attach_trigger( **automation_info["trigger_data"], "platform": DOMAIN, "event": event_type, + "offset": offset, } - listener = CalendarEventListener( - hass, HassJob(action), trigger_data, entity, event_type + hass, HassJob(action), trigger_data, entity, event_type, offset ) await listener.async_attach() return listener.async_detach diff --git a/tests/components/calendar/test_trigger.py b/tests/components/calendar/test_trigger.py index 65655bc283c..a3b5bacca59 100644 --- a/tests/components/calendar/test_trigger.py +++ b/tests/components/calendar/test_trigger.py @@ -126,13 +126,15 @@ async def setup_calendar(hass: HomeAssistant, fake_schedule: FakeSchedule) -> No await hass.async_block_till_done() -async def create_automation(hass: HomeAssistant, event_type: str) -> None: +async def create_automation(hass: HomeAssistant, event_type: str, offset=None) -> None: """Register an automation.""" trigger_data = { "platform": calendar.DOMAIN, "entity_id": CALENDAR_ENTITY_ID, "event": event_type, } + if offset: + trigger_data["offset"] = offset assert await async_setup_component( hass, automation.DOMAIN, @@ -178,7 +180,43 @@ async def test_event_start_trigger(hass, calls, fake_schedule): assert len(calls()) == 0 await fake_schedule.fire_until( - datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00") + datetime.datetime.fromisoformat("2022-04-19 11:15:00+00:00"), + ) + assert calls() == [ + { + "platform": "calendar", + "event": EVENT_START, + "calendar_event": event_data, + } + ] + + +@pytest.mark.parametrize( + "offset_str, offset_delta", + [ + ("-01:00", datetime.timedelta(hours=-1)), + ("+01:00", datetime.timedelta(hours=1)), + ], +) +async def test_event_start_trigger_with_offset( + hass, calls, fake_schedule, offset_str, offset_delta +): + """Test the a calendar trigger based on start time with an offset.""" + event_data = fake_schedule.create_event( + start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"), + end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"), + ) + await create_automation(hass, EVENT_START, offset=offset_str) + + # No calls yet + await fake_schedule.fire_until( + datetime.datetime.fromisoformat("2022-04-19 11:55:00+00:00") + offset_delta, + ) + assert len(calls()) == 0 + + # Event has started w/ offset + await fake_schedule.fire_until( + datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta, ) assert calls() == [ { @@ -216,6 +254,42 @@ async def test_event_end_trigger(hass, calls, fake_schedule): ] +@pytest.mark.parametrize( + "offset_str, offset_delta", + [ + ("-01:00", datetime.timedelta(hours=-1)), + ("+01:00", datetime.timedelta(hours=1)), + ], +) +async def test_event_end_trigger_with_offset( + hass, calls, fake_schedule, offset_str, offset_delta +): + """Test the a calendar trigger based on end time with an offset.""" + event_data = fake_schedule.create_event( + start=datetime.datetime.fromisoformat("2022-04-19 12:00:00+00:00"), + end=datetime.datetime.fromisoformat("2022-04-19 12:30:00+00:00"), + ) + await create_automation(hass, EVENT_END, offset=offset_str) + + # No calls yet + await fake_schedule.fire_until( + datetime.datetime.fromisoformat("2022-04-19 12:05:00+00:00") + offset_delta, + ) + assert len(calls()) == 0 + + # Event has started w/ offset + await fake_schedule.fire_until( + datetime.datetime.fromisoformat("2022-04-19 12:35:00+00:00") + offset_delta, + ) + assert calls() == [ + { + "platform": "calendar", + "event": EVENT_END, + "calendar_event": event_data, + } + ] + + async def test_calendar_trigger_with_no_events(hass, calls, fake_schedule): """Test a calendar trigger setup with no events.""" From ba386b58413a8f69d205d63976f4f7862e39485a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 1 May 2022 00:28:25 +0000 Subject: [PATCH 121/930] [ci skip] Translation update --- .../translations/ca.json | 3 +++ .../translations/de.json | 3 +++ .../translations/el.json | 3 +++ .../translations/en.json | 3 +++ .../translations/fr.json | 3 +++ .../translations/hu.json | 3 +++ .../translations/pt-BR.json | 3 +++ .../translations/ru.json | 3 +++ .../translations/sv.json | 3 +++ .../components/deconz/translations/bg.json | 1 + .../components/deconz/translations/ca.json | 1 + .../components/deconz/translations/de.json | 1 + .../components/deconz/translations/el.json | 1 + .../components/deconz/translations/hu.json | 1 + .../components/deconz/translations/nl.json | 1 + .../components/deconz/translations/pl.json | 1 + .../deconz/translations/zh-Hant.json | 1 + .../components/hue/translations/sv.json | 1 + .../components/isy994/translations/bg.json | 7 +++++++ .../components/isy994/translations/ca.json | 13 ++++++++++-- .../components/isy994/translations/de.json | 13 ++++++++++-- .../components/isy994/translations/el.json | 9 +++++++++ .../components/isy994/translations/fr.json | 13 ++++++++++-- .../components/isy994/translations/hu.json | 13 ++++++++++-- .../components/isy994/translations/nl.json | 13 ++++++++++-- .../components/isy994/translations/pl.json | 13 ++++++++++-- .../components/isy994/translations/pt-BR.json | 13 ++++++++++-- .../components/isy994/translations/ru.json | 9 +++++++++ .../isy994/translations/zh-Hant.json | 13 ++++++++++-- .../components/knx/translations/sv.json | 8 ++++++++ .../media_player/translations/bg.json | 1 + .../media_player/translations/sv.json | 5 +++++ .../rainmachine/translations/bg.json | 2 +- .../components/recorder/translations/ca.json | 8 ++++++++ .../components/recorder/translations/de.json | 8 ++++++++ .../components/recorder/translations/el.json | 8 ++++++++ .../components/recorder/translations/fr.json | 8 ++++++++ .../components/recorder/translations/hu.json | 8 ++++++++ .../recorder/translations/pt-BR.json | 8 ++++++++ .../components/recorder/translations/ru.json | 7 +++++++ .../components/sabnzbd/translations/bg.json | 6 ++++++ .../simplisafe/translations/bg.json | 5 +++++ .../simplisafe/translations/ca.json | 6 ++++-- .../simplisafe/translations/nl.json | 4 ++++ .../simplisafe/translations/pt-BR.json | 2 +- .../components/slimproto/translations/bg.json | 7 +++++++ .../components/sql/translations/bg.json | 20 +++++++++++++++++++ .../components/sql/translations/hu.json | 4 ++++ .../components/sql/translations/nl.json | 2 ++ .../components/sql/translations/pl.json | 4 ++++ .../components/sql/translations/pt-BR.json | 4 ++-- .../components/sql/translations/zh-Hant.json | 4 ++++ .../steam_online/translations/ca.json | 4 +++- .../steam_online/translations/de.json | 4 ++-- .../steam_online/translations/fr.json | 4 ++-- .../steam_online/translations/hu.json | 4 ++-- .../steam_online/translations/pt-BR.json | 4 ++-- .../components/tautulli/translations/ca.json | 7 +++++-- .../components/tautulli/translations/nl.json | 4 +++- .../trafikverket_ferry/translations/ca.json | 5 ++++- .../components/update/translations/sv.json | 9 +++++++++ 61 files changed, 314 insertions(+), 35 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/ca.json create mode 100644 homeassistant/components/application_credentials/translations/de.json create mode 100644 homeassistant/components/application_credentials/translations/el.json create mode 100644 homeassistant/components/application_credentials/translations/en.json create mode 100644 homeassistant/components/application_credentials/translations/fr.json create mode 100644 homeassistant/components/application_credentials/translations/hu.json create mode 100644 homeassistant/components/application_credentials/translations/pt-BR.json create mode 100644 homeassistant/components/application_credentials/translations/ru.json create mode 100644 homeassistant/components/application_credentials/translations/sv.json create mode 100644 homeassistant/components/recorder/translations/ca.json create mode 100644 homeassistant/components/recorder/translations/de.json create mode 100644 homeassistant/components/recorder/translations/el.json create mode 100644 homeassistant/components/recorder/translations/fr.json create mode 100644 homeassistant/components/recorder/translations/hu.json create mode 100644 homeassistant/components/recorder/translations/pt-BR.json create mode 100644 homeassistant/components/recorder/translations/ru.json create mode 100644 homeassistant/components/slimproto/translations/bg.json create mode 100644 homeassistant/components/sql/translations/bg.json create mode 100644 homeassistant/components/update/translations/sv.json diff --git a/homeassistant/components/application_credentials/translations/ca.json b/homeassistant/components/application_credentials/translations/ca.json new file mode 100644 index 00000000000..073fb242e72 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/ca.json @@ -0,0 +1,3 @@ +{ + "title": "Credencials de l'aplicaci\u00f3" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/de.json b/homeassistant/components/application_credentials/translations/de.json new file mode 100644 index 00000000000..95198ef66cc --- /dev/null +++ b/homeassistant/components/application_credentials/translations/de.json @@ -0,0 +1,3 @@ +{ + "title": "Anmeldeinformationen" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/el.json b/homeassistant/components/application_credentials/translations/el.json new file mode 100644 index 00000000000..79c30a8b969 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/el.json @@ -0,0 +1,3 @@ +{ + "title": "\u0394\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/en.json b/homeassistant/components/application_credentials/translations/en.json new file mode 100644 index 00000000000..2a8755ad9d8 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/en.json @@ -0,0 +1,3 @@ +{ + "title": "Application Credentials" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/fr.json b/homeassistant/components/application_credentials/translations/fr.json new file mode 100644 index 00000000000..859fa965023 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/fr.json @@ -0,0 +1,3 @@ +{ + "title": "Informations d'identification de l'application" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/hu.json b/homeassistant/components/application_credentials/translations/hu.json new file mode 100644 index 00000000000..d24ea488f08 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/hu.json @@ -0,0 +1,3 @@ +{ + "title": "Alkalmaz\u00e1s hiteles\u00edt\u0151 adatai" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/pt-BR.json b/homeassistant/components/application_credentials/translations/pt-BR.json new file mode 100644 index 00000000000..cc21106d6c5 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/pt-BR.json @@ -0,0 +1,3 @@ +{ + "title": "Credenciais do aplicativo" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/ru.json b/homeassistant/components/application_credentials/translations/ru.json new file mode 100644 index 00000000000..e0edbab4c74 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/ru.json @@ -0,0 +1,3 @@ +{ + "title": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/sv.json b/homeassistant/components/application_credentials/translations/sv.json new file mode 100644 index 00000000000..1127f807c9f --- /dev/null +++ b/homeassistant/components/application_credentials/translations/sv.json @@ -0,0 +1,3 @@ +{ + "title": "Autentiseringsuppgifter f\u00f6r applikationer" +} \ No newline at end of file diff --git a/homeassistant/components/deconz/translations/bg.json b/homeassistant/components/deconz/translations/bg.json index 3fe700efd3e..efba936a8d0 100644 --- a/homeassistant/components/deconz/translations/bg.json +++ b/homeassistant/components/deconz/translations/bg.json @@ -8,6 +8,7 @@ "updated_instance": "\u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 deCONZ \u0441 \u043d\u043e\u0432 \u0430\u0434\u0440\u0435\u0441" }, "error": { + "linking_not_possible": "\u041d\u0435 \u043c\u043e\u0436\u0430 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435 \u0441 \u0448\u043b\u044e\u0437\u0430", "no_key": "\u041d\u0435 \u043c\u043e\u0436\u0430 \u0434\u0430 \u0441\u0435 \u043f\u043e\u043b\u0443\u0447\u0438 API \u043a\u043b\u044e\u0447" }, "flow_title": "deCONZ Zigbee \u0448\u043b\u044e\u0437 ({host})", diff --git a/homeassistant/components/deconz/translations/ca.json b/homeassistant/components/deconz/translations/ca.json index 2f839b209eb..2d15801b590 100644 --- a/homeassistant/components/deconz/translations/ca.json +++ b/homeassistant/components/deconz/translations/ca.json @@ -9,6 +9,7 @@ "updated_instance": "S'ha actualitzat la inst\u00e0ncia de deCONZ amb una nova adre\u00e7a" }, "error": { + "linking_not_possible": "No s'ha pogut enlla\u00e7ar amb la passarel\u00b7la", "no_key": "No s'ha pogut obtenir una clau API" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/de.json b/homeassistant/components/deconz/translations/de.json index a24dbb44ad4..05fa6ce8b3f 100644 --- a/homeassistant/components/deconz/translations/de.json +++ b/homeassistant/components/deconz/translations/de.json @@ -9,6 +9,7 @@ "updated_instance": "deCONZ-Instanz mit neuer Host-Adresse aktualisiert" }, "error": { + "linking_not_possible": "Es konnte keine Verbindung mit dem Gateway hergestellt werden", "no_key": "Es konnte kein API-Schl\u00fcssel abgerufen werden" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/el.json b/homeassistant/components/deconz/translations/el.json index 273d939cc34..18eeaf700b6 100644 --- a/homeassistant/components/deconz/translations/el.json +++ b/homeassistant/components/deconz/translations/el.json @@ -9,6 +9,7 @@ "updated_instance": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03af\u03b1 deCONZ \u03bc\u03b5 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae" }, "error": { + "linking_not_possible": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7", "no_key": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03bb\u03ae\u03c8\u03b7 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/hu.json b/homeassistant/components/deconz/translations/hu.json index 47bb343247c..c92a5dd36cd 100644 --- a/homeassistant/components/deconz/translations/hu.json +++ b/homeassistant/components/deconz/translations/hu.json @@ -9,6 +9,7 @@ "updated_instance": "A deCONZ-p\u00e9ld\u00e1ny \u00faj \u00e1llom\u00e1sc\u00edmmel friss\u00edtve" }, "error": { + "linking_not_possible": "Nem tudott kapcsol\u00f3dni az \u00e1tj\u00e1r\u00f3hoz", "no_key": "API kulcs lek\u00e9r\u00e9se nem siker\u00fclt" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/nl.json b/homeassistant/components/deconz/translations/nl.json index 9c83fb806e8..6339ad0fd10 100644 --- a/homeassistant/components/deconz/translations/nl.json +++ b/homeassistant/components/deconz/translations/nl.json @@ -9,6 +9,7 @@ "updated_instance": "DeCONZ-instantie bijgewerkt met nieuw host-adres" }, "error": { + "linking_not_possible": "Kan geen koppeling maken met de gateway", "no_key": "Kon geen API-sleutel ophalen" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/pl.json b/homeassistant/components/deconz/translations/pl.json index 945830a5734..7f49a259304 100644 --- a/homeassistant/components/deconz/translations/pl.json +++ b/homeassistant/components/deconz/translations/pl.json @@ -9,6 +9,7 @@ "updated_instance": "Zaktualizowano instancj\u0119 deCONZ o nowy adres hosta" }, "error": { + "linking_not_possible": "Nie mo\u017cna po\u0142\u0105czy\u0107 si\u0119 z bramk\u0105", "no_key": "Nie mo\u017cna uzyska\u0107 klucza API" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/zh-Hant.json b/homeassistant/components/deconz/translations/zh-Hant.json index d6d4dfeba45..d2e37ffa710 100644 --- a/homeassistant/components/deconz/translations/zh-Hant.json +++ b/homeassistant/components/deconz/translations/zh-Hant.json @@ -9,6 +9,7 @@ "updated_instance": "\u4f7f\u7528\u65b0\u4e3b\u6a5f\u7aef\u4f4d\u5740\u66f4\u65b0 deCONZ \u88dd\u7f6e" }, "error": { + "linking_not_possible": "\u7121\u6cd5\u8207\u8def\u7531\u5668\u9023\u7dda", "no_key": "\u7121\u6cd5\u53d6\u5f97 API key" }, "flow_title": "{host}", diff --git a/homeassistant/components/hue/translations/sv.json b/homeassistant/components/hue/translations/sv.json index 80f7b179692..c4687726357 100644 --- a/homeassistant/components/hue/translations/sv.json +++ b/homeassistant/components/hue/translations/sv.json @@ -6,6 +6,7 @@ "already_in_progress": "Konfigurations fl\u00f6det f\u00f6r bryggan p\u00e5g\u00e5r redan.", "cannot_connect": "Det gick inte att ansluta till bryggan", "discover_timeout": "Det gick inte att uppt\u00e4cka n\u00e5gra Hue-bryggor", + "invalid_host": "Ogiltig v\u00e4rd", "no_bridges": "Inga Philips Hue-bryggor uppt\u00e4cktes", "not_hue_bridge": "Inte en Hue-brygga", "unknown": "Ett ok\u00e4nt fel intr\u00e4ffade" diff --git a/homeassistant/components/isy994/translations/bg.json b/homeassistant/components/isy994/translations/bg.json index 04a015b0d61..bba26a6cf89 100644 --- a/homeassistant/components/isy994/translations/bg.json +++ b/homeassistant/components/isy994/translations/bg.json @@ -6,10 +6,17 @@ "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + }, "user": { "data": { "host": "URL", diff --git a/homeassistant/components/isy994/translations/ca.json b/homeassistant/components/isy994/translations/ca.json index cd0a2d2a1de..69c9d02b1bb 100644 --- a/homeassistant/components/isy994/translations/ca.json +++ b/homeassistant/components/isy994/translations/ca.json @@ -7,10 +7,19 @@ "cannot_connect": "[%key::common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key::common::config_flow::error::invalid_auth%]", "invalid_host": "L'entrada de l'amfitri\u00f3 no t\u00e9 el fromat d'URL complet, ex: http://192.168.10.100:80", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", "unknown": "Error inesperat" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Contrasenya", + "username": "Nom d'usuari" + }, + "description": "Les credencials de {host} ja no s\u00f3n v\u00e0lides.", + "title": "Re-autenticaci\u00f3 d'ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "[%key::common::config_flow::data::username%]" }, "description": "L'entrada de l'amfitri\u00f3 ha de tenir el format d'URL complet, ex: http://192.168.10.100:80", - "title": "Connexi\u00f3 amb ISY994" + "title": "Connexi\u00f3 amb ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "String de Sensor Variable" }, "description": "Configuraci\u00f3 de les opcions per a la integraci\u00f3 ISY: \n \u2022 String de Sensor Node: qualsevol dispositiu o carpeta que contingui 'String de Sensor Node' dins el nom ser\u00e0 tractat com a un sensor o sensor binari. \n \u2022 String Ignora: qualsevol dispositiu amb 'String Ignora' dins el nom ser\u00e0 ignorat. \n \u2022 String de Sensor Variable: qualsevol variable que contingui 'String de Sensor Variable' s'afegir\u00e0 com a un sensor. \n \u2022 Restaura la brillantor de la llum: si est\u00e0 activat, en encendre un llum es restablir\u00e0 la brillantor anterior en lloc del valor integrat dins el dispositiu.", - "title": "Opcions d'ISY994" + "title": "Opcions d'ISY" } } }, diff --git a/homeassistant/components/isy994/translations/de.json b/homeassistant/components/isy994/translations/de.json index a6e9e0a1498..a183e6f7853 100644 --- a/homeassistant/components/isy994/translations/de.json +++ b/homeassistant/components/isy994/translations/de.json @@ -7,10 +7,19 @@ "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", "invalid_host": "Der Hosteintrag hatte nicht das vollst\u00e4ndige URL-Format, z. B. http://192.168.10.100:80", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "unknown": "Unerwarteter Fehler" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Passwort", + "username": "Benutzername" + }, + "description": "Die Anmeldedaten f\u00fcr {host} sind nicht mehr g\u00fcltig.", + "title": "Reauthentifizierung deines ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Benutzername" }, "description": "Der Hosteintrag muss im vollst\u00e4ndigen URL-Format vorliegen, z. B. http://192.168.10.100:80", - "title": "Stelle eine Verbindung zu deinem ISY994 her" + "title": "Verbindung zu deinem ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Variabler Sensor String" }, "description": "Stelle die Optionen f\u00fcr die ISY-Integration ein: \n - Node Sensor String: Jedes Ger\u00e4t oder jeder Ordner, der 'Node Sensor String' im Namen enth\u00e4lt, wird als Sensor oder bin\u00e4rer Sensor behandelt. \n - String ignorieren: Jedes Ger\u00e4t mit 'Ignore String' im Namen wird ignoriert. \n - Variable Sensor Zeichenfolge: Jede Variable, die 'Variable Sensor String' im Namen enth\u00e4lt, wird als Sensor hinzugef\u00fcgt. \n - Lichthelligkeit wiederherstellen: Wenn diese Option aktiviert ist, wird beim Einschalten eines Lichts die vorherige Helligkeit wiederhergestellt und nicht der integrierte Ein-Pegel des Ger\u00e4ts.", - "title": "ISY994 Optionen" + "title": "ISY-Optionen" } } }, diff --git a/homeassistant/components/isy994/translations/el.json b/homeassistant/components/isy994/translations/el.json index 470975e2117..ec32ea756bc 100644 --- a/homeassistant/components/isy994/translations/el.json +++ b/homeassistant/components/isy994/translations/el.json @@ -7,10 +7,19 @@ "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "v", "invalid_host": "\u0397 \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03b4\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03c3\u03b5 \u03c0\u03bb\u03ae\u03c1\u03b7 \u03bc\u03bf\u03c1\u03c6\u03ae URL, \u03c0.\u03c7. http://192.168.10.100:80", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "description": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03b3\u03b9\u03b1 \u03c4\u03bf {host} \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03bb\u03ad\u03bf\u03bd \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1.", + "title": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03bf ISY \u03c3\u03b1\u03c2" + }, "user": { "data": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", diff --git a/homeassistant/components/isy994/translations/fr.json b/homeassistant/components/isy994/translations/fr.json index c9e53845dfb..29a3c6e54fe 100644 --- a/homeassistant/components/isy994/translations/fr.json +++ b/homeassistant/components/isy994/translations/fr.json @@ -7,10 +7,19 @@ "cannot_connect": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", "invalid_host": "L'entr\u00e9e d'h\u00f4te n'\u00e9tait pas au format URL complet, par exemple http://192.168.10.100:80", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", "unknown": "Erreur inattendue" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Mot de passe", + "username": "Nom d'utilisateur" + }, + "description": "Les informations d'identification pour {host} ne sont plus valides.", + "title": "R\u00e9authentifiez votre ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Nom d'utilisateur" }, "description": "L'entr\u00e9e d'h\u00f4te doit \u00eatre au format URL complet, par exemple, http://192.168.10.100:80", - "title": "Connect\u00e9 \u00e0 votre ISY994" + "title": "Connexion \u00e0 votre ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Cha\u00eene de capteur variable" }, "description": "D\u00e9finir les options pour l'int\u00e9gration ISY: \n \u2022 Node Sensor String: tout p\u00e9riph\u00e9rique ou dossier contenant \u00abNode Sensor String\u00bb dans le nom sera trait\u00e9 comme un capteur ou un capteur binaire. \n \u2022 Ignore String : tout p\u00e9riph\u00e9rique avec \u00abIgnore String\u00bb dans le nom sera ignor\u00e9. \n \u2022 Variable Sensor String : toute variable contenant \u00abVariable Sensor String\u00bb sera ajout\u00e9e en tant que capteur. \n \u2022 Restaurer la luminosit\u00e9 : si cette option est activ\u00e9e, la luminosit\u00e9 pr\u00e9c\u00e9dente sera restaur\u00e9e lors de l'allumage d'une lumi\u00e8re au lieu de la fonction int\u00e9gr\u00e9e de l'appareil.", - "title": "Options ISY994" + "title": "Options ISY" } } }, diff --git a/homeassistant/components/isy994/translations/hu.json b/homeassistant/components/isy994/translations/hu.json index d9cce2fefcb..e5a5c4d6f98 100644 --- a/homeassistant/components/isy994/translations/hu.json +++ b/homeassistant/components/isy994/translations/hu.json @@ -7,10 +7,19 @@ "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "invalid_host": "A c\u00edm bejegyz\u00e9se nem volt teljes URL-form\u00e1tumban, p\u00e9ld\u00e1ul: http://192.168.10.100:80", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Jelsz\u00f3", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + }, + "description": "{host} hiteles\u00edt\u0151 adatai m\u00e1r nem \u00e9rv\u00e9nyesek.", + "title": "ISY \u00fajrahiteles\u00edt\u00e9se" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, "description": "A c\u00edm bejegyz\u00e9s\u00e9nek teljes URL form\u00e1tumban kell lennie, pl. Http://192.168.10.100:80", - "title": "Csatlakozzon az ISY994-hez" + "title": "Csatlakozzon az ISY-hez" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "V\u00e1ltoz\u00f3 \u00e9rz\u00e9kel\u0151 karakterl\u00e1nc" }, "description": "\u00c1ll\u00edtsa be az ISY integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sait:\n \u2022 Csom\u00f3pont -\u00e9rz\u00e9kel\u0151 karakterl\u00e1nc: B\u00e1rmely eszk\u00f6z vagy mappa, amelynek nev\u00e9ben \u201eNode Sensor String\u201d szerepel, \u00e9rz\u00e9kel\u0151k\u00e9nt vagy bin\u00e1ris \u00e9rz\u00e9kel\u0151k\u00e9nt fog kezelni.\n \u2022 Karakterl\u00e1nc figyelmen k\u00edv\u00fcl hagy\u00e1sa: Minden olyan eszk\u00f6z, amelynek a neve \u201eIgnore String\u201d, figyelmen k\u00edv\u00fcl marad.\n \u2022 V\u00e1ltoz\u00f3 \u00e9rz\u00e9kel\u0151 karakterl\u00e1nc: B\u00e1rmely v\u00e1ltoz\u00f3, amely tartalmazza a \u201eV\u00e1ltoz\u00f3 \u00e9rz\u00e9kel\u0151 karakterl\u00e1ncot\u201d, hozz\u00e1ad\u00f3dik \u00e9rz\u00e9kel\u0151k\u00e9nt.\n \u2022 F\u00e9nyer\u0151ss\u00e9g vissza\u00e1ll\u00edt\u00e1sa: Ha enged\u00e9lyezve van, akkor az el\u0151z\u0151 f\u00e9nyer\u0151 vissza\u00e1ll, amikor a k\u00e9sz\u00fcl\u00e9ket be\u00e9p\u00edtett On-Level helyett bekapcsolja.", - "title": "ISY994 Be\u00e1ll\u00edt\u00e1sok" + "title": "ISY be\u00e1ll\u00edt\u00e1sok" } } }, diff --git a/homeassistant/components/isy994/translations/nl.json b/homeassistant/components/isy994/translations/nl.json index c75cdf86473..580bd307a5c 100644 --- a/homeassistant/components/isy994/translations/nl.json +++ b/homeassistant/components/isy994/translations/nl.json @@ -7,10 +7,19 @@ "cannot_connect": "Kon niet verbinden", "invalid_auth": "Ongeldige authenticatie", "invalid_host": "De hostvermelding had de niet volledig URL-indeling, bijvoorbeeld http://192.168.10.100:80", + "reauth_successful": "Herauthenticatie was succesvol", "unknown": "Onverwachte fout" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Wachtwoord", + "username": "Gebruikersnaam" + }, + "description": "De inloggegevens voor {host} zijn niet langer geldig.", + "title": "Herautoriseer uw ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Gebruikersnaam" }, "description": "Het hostvermelding moet de volledige URL-indeling hebben, bijvoorbeeld http://192.168.10.100:80", - "title": "Maak verbinding met uw ISY994" + "title": "Maak verbinding met uw ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Variabele sensor string" }, "description": "Stel de opties in voor de ISY-integratie:\n \u2022 Node Sensor String: elk apparaat of elke map die 'Node Sensor String' in de naam bevat, wordt behandeld als een sensor of binaire sensor.\n \u2022 Ignore String: elk apparaat met 'Ignore String' in de naam wordt genegeerd.\n \u2022 Variabele sensorreeks: elke variabele die 'Variabele sensorreeks' bevat, wordt als sensor toegevoegd.\n \u2022 Lichthelderheid herstellen: indien ingeschakeld, wordt de vorige helderheid hersteld wanneer u een lamp inschakelt in plaats van het ingebouwde Aan-niveau van het apparaat.", - "title": "ISY994-opties" + "title": "ISY-opties" } } }, diff --git a/homeassistant/components/isy994/translations/pl.json b/homeassistant/components/isy994/translations/pl.json index 4deeefe391d..a1ca157bd14 100644 --- a/homeassistant/components/isy994/translations/pl.json +++ b/homeassistant/components/isy994/translations/pl.json @@ -7,10 +7,19 @@ "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", "invalid_host": "Wpis hosta nie by\u0142 w pe\u0142nym formacie URL, np. http://192.168.10.100:80", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Has\u0142o", + "username": "Nazwa u\u017cytkownika" + }, + "description": "Dane uwierzytelniaj\u0105ce dla {host} nie s\u0105 ju\u017c wa\u017cne.", + "title": "Ponownie uwierzytelnij ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Nazwa u\u017cytkownika" }, "description": "Wpis hosta musi by\u0107 w pe\u0142nym formacie URL, np. http://192.168.10.100:80.", - "title": "Po\u0142\u0105czenie z ISY994" + "title": "Po\u0142\u0105czenie z ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Ci\u0105g zmiennej sensora" }, "description": "Ustaw opcje dla integracji ISY: \n \u2022 Ci\u0105g sensora w\u0119z\u0142a: ka\u017cde urz\u0105dzenie lub folder, kt\u00f3ry zawiera w nazwie ci\u0105g sensora w\u0119z\u0142a, b\u0119dzie traktowane jako sensor lub sensor binarny. \n \u2022 Ci\u0105g ignorowania: ka\u017cde urz\u0105dzenie z 'ci\u0105giem ignorowania' w nazwie zostanie zignorowane. \n \u2022 Ci\u0105g sensora zmiennej: ka\u017cda zmienna zawieraj\u0105ca 'ci\u0105g sensora zmiennej' zostanie dodana jako sensor. \n \u2022 Przywr\u00f3\u0107 jasno\u015b\u0107 \u015bwiat\u0142a: je\u015bli ta opcja jest w\u0142\u0105czona, poprzednia warto\u015b\u0107 jasno\u015bci zostanie przywr\u00f3cona po w\u0142\u0105czeniu \u015bwiat\u0142a zamiast domy\u015blnej warto\u015bci jasno\u015bci dla urz\u0105dzenia.", - "title": "Opcje ISY994" + "title": "Opcje ISY" } } }, diff --git a/homeassistant/components/isy994/translations/pt-BR.json b/homeassistant/components/isy994/translations/pt-BR.json index b644b6c1bfc..7b1d8e796ba 100644 --- a/homeassistant/components/isy994/translations/pt-BR.json +++ b/homeassistant/components/isy994/translations/pt-BR.json @@ -7,10 +7,19 @@ "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "invalid_host": "A entrada do host n\u00e3o est\u00e1 no formato de URL completo, por exemplo, http://192.168.10.100:80", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", "unknown": "Erro inesperado" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Senha", + "username": "Usu\u00e1rio" + }, + "description": "As credenciais para {host} n\u00e3o s\u00e3o mais v\u00e1lidas.", + "title": "Reautentique seu ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Usu\u00e1rio" }, "description": "A entrada do endere\u00e7o deve estar no formato de URL completo, por exemplo, http://192.168.10.100:80", - "title": "Conecte-se ao seu ISY994" + "title": "Conecte-se seu ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Texto da vari\u00e1vel do sensor" }, "description": "Defina as op\u00e7\u00f5es para a Integra\u00e7\u00e3o ISY:\n \u2022 Cadeia de Sensores de N\u00f3: Qualquer dispositivo ou pasta que contenha 'Cadeia de Sensores de N\u00f3' no nome ser\u00e1 tratado como um sensor ou sensor bin\u00e1rio.\n \u2022 Ignore String: Qualquer dispositivo com 'Ignore String' no nome ser\u00e1 ignorado.\n \u2022 Variable Sensor String: Qualquer vari\u00e1vel que contenha 'Variable Sensor String' ser\u00e1 adicionada como sensor.\n \u2022 Restaurar o brilho da luz: Se ativado, o brilho anterior ser\u00e1 restaurado ao acender uma luz em vez do n\u00edvel integrado do dispositivo.", - "title": "ISY994 Op\u00e7\u00f5es" + "title": "Op\u00e7\u00f5es ISY" } } }, diff --git a/homeassistant/components/isy994/translations/ru.json b/homeassistant/components/isy994/translations/ru.json index a5dccc79ddd..61e2c79831a 100644 --- a/homeassistant/components/isy994/translations/ru.json +++ b/homeassistant/components/isy994/translations/ru.json @@ -7,10 +7,19 @@ "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "invalid_host": "URL-\u0430\u0434\u0440\u0435\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 'address[:port]' (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: 'http://192.168.10.100:80').", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "description": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f {host} \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0412\u0430\u0448\u0435\u0433\u043e ISY" + }, "user": { "data": { "host": "URL-\u0430\u0434\u0440\u0435\u0441", diff --git a/homeassistant/components/isy994/translations/zh-Hant.json b/homeassistant/components/isy994/translations/zh-Hant.json index 6370d7bbbf8..c22bbd1b58d 100644 --- a/homeassistant/components/isy994/translations/zh-Hant.json +++ b/homeassistant/components/isy994/translations/zh-Hant.json @@ -7,10 +7,19 @@ "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "invalid_host": "\u4e3b\u6a5f\u7aef\u4e26\u672a\u4ee5\u5b8c\u6574\u7db2\u5740\u683c\u5f0f\u8f38\u5165\uff0c\u4f8b\u5982\uff1ahttp://192.168.10.100:80", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u5bc6\u78bc", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + }, + "description": "{username} \u6191\u8b49\u4e0d\u518d\u6709\u6548\u3002", + "title": "\u91cd\u65b0\u8a8d\u8b49 ISY \u5e33\u865f" + }, "user": { "data": { "host": "\u7db2\u5740", @@ -19,7 +28,7 @@ "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, "description": "\u4e3b\u6a5f\u7aef\u5fc5\u9808\u4ee5\u5b8c\u6574\u7db2\u5740\u683c\u5f0f\u8f38\u5165\uff0c\u4f8b\u5982\uff1ahttp://192.168.10.100:80", - "title": "\u9023\u7dda\u81f3 ISY994" + "title": "\u9023\u7dda\u81f3 ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "\u53ef\u8b8a\u611f\u6e2c\u5668\u5b57\u4e32" }, "description": "ISY \u6574\u5408\u8a2d\u5b9a\u9078\u9805\uff1a \n \u2022 \u7bc0\u9ede\u611f\u6e2c\u5668\u5b57\u4e32\uff08Node Sensor String\uff09\uff1a\u4efb\u4f55\u540d\u7a31\u6216\u8cc7\u6599\u593e\u5305\u542b\u300cNode Sensor String\u300d\u7684\u88dd\u7f6e\u90fd\u6703\u88ab\u8996\u70ba\u611f\u6e2c\u5668\u6216\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u3002\n \u2022 \u5ffd\u7565\u5b57\u4e32\uff08Ignore String\uff09\uff1a\u4efb\u4f55\u540d\u7a31\u5305\u542b\u300cIgnore String\u300d\u7684\u88dd\u7f6e\u90fd\u6703\u88ab\u5ffd\u7565\u3002\n \u2022 \u53ef\u8b8a\u611f\u6e2c\u5668\u5b57\u4e32\uff08Variable Sensor String\uff09\uff1a\u4efb\u4f55\u5305\u542b\u300cVariable Sensor String\u300d\u7684\u8b8a\u6578\u90fd\u5c07\u65b0\u589e\u70ba\u611f\u6e2c\u5668\u3002 \n \u2022 \u56de\u5fa9\u4eae\u5ea6\uff08Restore Light Brightness\uff09\uff1a\u958b\u5553\u5f8c\u3001\u7576\u71c8\u5149\u958b\u555f\u6642\u6703\u56de\u5fa9\u5148\u524d\u7684\u4eae\u5ea6\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u88dd\u7f6e\u9810\u8a2d\u4eae\u5ea6\u3002", - "title": "ISY994 \u9078\u9805" + "title": "ISY \u9078\u9805" } } }, diff --git a/homeassistant/components/knx/translations/sv.json b/homeassistant/components/knx/translations/sv.json index b1be9557565..de5def120c9 100644 --- a/homeassistant/components/knx/translations/sv.json +++ b/homeassistant/components/knx/translations/sv.json @@ -1,5 +1,9 @@ { "config": { + "error": { + "invalid_individual_address": "V\u00e4rdet matchar inte m\u00f6nstret f\u00f6r en individuell adress i KNX.\n'area.line.device'", + "invalid_ip_address": "Ogiltig IPv4-adress." + }, "step": { "manual_tunnel": { "data": { @@ -13,6 +17,10 @@ "tunnel": { "data": { "tunneling_type": "KNX tunneltyp" + }, + "data_description": { + "host": "IP adress till KNX/IP tunnelingsenhet.", + "port": "Port p\u00e5 KNX/IP tunnelingsenhet." } } } diff --git a/homeassistant/components/media_player/translations/bg.json b/homeassistant/components/media_player/translations/bg.json index 408f3c1e682..ddc7ab9ee6b 100644 --- a/homeassistant/components/media_player/translations/bg.json +++ b/homeassistant/components/media_player/translations/bg.json @@ -10,6 +10,7 @@ }, "state": { "_": { + "buffering": "\u0411\u0443\u0444\u0435\u0440\u0438\u0440\u0430\u043d\u0435", "idle": "\u041d\u0435\u0440\u0430\u0431\u043e\u0442\u0435\u0449", "off": "\u0418\u0437\u043a\u043b\u044e\u0447\u0435\u043d", "on": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d", diff --git a/homeassistant/components/media_player/translations/sv.json b/homeassistant/components/media_player/translations/sv.json index 95f098f2f3e..6aae6d6b208 100644 --- a/homeassistant/components/media_player/translations/sv.json +++ b/homeassistant/components/media_player/translations/sv.json @@ -1,15 +1,20 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} buffrar", "is_idle": "{entity_name} \u00e4r inaktiv", "is_off": "{entity_name} \u00e4r avst\u00e4ngd", "is_on": "{entity_name} \u00e4r p\u00e5", "is_paused": "{entity_name} \u00e4r pausad", "is_playing": "{entity_name} spelar" + }, + "trigger_type": { + "buffering": "{entity_name} b\u00f6rjar buffra" } }, "state": { "_": { + "buffering": "Buffrar", "idle": "Inaktiv", "off": "Av", "on": "P\u00e5", diff --git a/homeassistant/components/rainmachine/translations/bg.json b/homeassistant/components/rainmachine/translations/bg.json index b54660f8e9f..1239915231b 100644 --- a/homeassistant/components/rainmachine/translations/bg.json +++ b/homeassistant/components/rainmachine/translations/bg.json @@ -4,7 +4,7 @@ "step": { "user": { "data": { - "ip_address": "\u0410\u0434\u0440\u0435\u0441", + "ip_address": "\u0418\u043c\u0435 \u043d\u0430 \u0445\u043e\u0441\u0442 \u0438\u043b\u0438 IP \u0430\u0434\u0440\u0435\u0441", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442" }, diff --git a/homeassistant/components/recorder/translations/ca.json b/homeassistant/components/recorder/translations/ca.json new file mode 100644 index 00000000000..b6e5a549ba7 --- /dev/null +++ b/homeassistant/components/recorder/translations/ca.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Hora d'inici de l'execuci\u00f3 actual", + "oldest_recorder_run": "Hora d'inici de l'execuci\u00f3 m\u00e9s antiga" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/de.json b/homeassistant/components/recorder/translations/de.json new file mode 100644 index 00000000000..823b4ae62bf --- /dev/null +++ b/homeassistant/components/recorder/translations/de.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Aktuelle Startzeit der Ausf\u00fchrung", + "oldest_recorder_run": "\u00c4lteste Startzeit der Ausf\u00fchrung" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/el.json b/homeassistant/components/recorder/translations/el.json new file mode 100644 index 00000000000..0ede908d151 --- /dev/null +++ b/homeassistant/components/recorder/translations/el.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03ce\u03c1\u03b1 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7\u03c2", + "oldest_recorder_run": "\u03a0\u03b1\u03bb\u03b1\u03b9\u03cc\u03c4\u03b5\u03c1\u03b7 \u03ce\u03c1\u03b1 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7\u03c2" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/fr.json b/homeassistant/components/recorder/translations/fr.json new file mode 100644 index 00000000000..31673d970f3 --- /dev/null +++ b/homeassistant/components/recorder/translations/fr.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution actuelle", + "oldest_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution la plus ancienne" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/hu.json b/homeassistant/components/recorder/translations/hu.json new file mode 100644 index 00000000000..4b0cd6067ad --- /dev/null +++ b/homeassistant/components/recorder/translations/hu.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Aktu\u00e1lis futtat\u00e1s kezd\u00e9si id\u0151pontja", + "oldest_recorder_run": "Legr\u00e9gebbi futtat\u00e1s kezd\u00e9si id\u0151pontja" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/pt-BR.json b/homeassistant/components/recorder/translations/pt-BR.json new file mode 100644 index 00000000000..78d43d14e52 --- /dev/null +++ b/homeassistant/components/recorder/translations/pt-BR.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Hora atual de in\u00edcio de execu\u00e7\u00e3o", + "oldest_recorder_run": "Hora antiga de in\u00edcio de execu\u00e7\u00e3o" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/ru.json b/homeassistant/components/recorder/translations/ru.json new file mode 100644 index 00000000000..6e5c928d77b --- /dev/null +++ b/homeassistant/components/recorder/translations/ru.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "oldest_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/bg.json b/homeassistant/components/sabnzbd/translations/bg.json index 02c83a6e916..d3fcfb0789d 100644 --- a/homeassistant/components/sabnzbd/translations/bg.json +++ b/homeassistant/components/sabnzbd/translations/bg.json @@ -1,8 +1,14 @@ { "config": { + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447" + }, "step": { "user": { "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "name": "\u0418\u043c\u0435", "url": "URL" } } diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index 9d32e18ae5c..56cf0e346f1 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -15,6 +15,11 @@ }, "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" }, + "sms_2fa": { + "data": { + "code": "\u041a\u043e\u0434" + } + }, "user": { "data": { "auth_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f", diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index a89b06bfcfc..827bd6dceec 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -7,6 +7,7 @@ "wrong_account": "Les credencials d'usuari proporcionades no coincideixen amb les d'aquest compte SimpliSafe." }, "error": { + "2fa_timed_out": "S'ha esgotat el temps d'espera m\u00e0xim durant l'autenticaci\u00f3 de dos factors", "identifier_exists": "Aquest compte ja est\u00e0 registrat", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "still_awaiting_mfa": "Esperant clic de l'enlla\u00e7 del correu MFA", @@ -29,7 +30,8 @@ "sms_2fa": { "data": { "code": "Codi" - } + }, + "description": "Introdueix el codi d'autenticaci\u00f3 de dos factors que s'ha enviat per SMS." }, "user": { "data": { @@ -38,7 +40,7 @@ "password": "Contrasenya", "username": "Nom d'usuari" }, - "description": "SimpliSafe s'autentica amb Home Assistant a trav\u00e9s de la seva aplicaci\u00f3 web. A causa de les limitacions t\u00e8cniques, hi ha un pas manual al final d'aquest proc\u00e9s; assegura't de llegir la [documentaci\u00f3]({docs_url}) abans de comen\u00e7ar.\n\n1. Fes clic [aqu\u00ed]({url}) per obrir l'aplicaci\u00f3 web de SimpliSafe i introdueix les teves credencials.\n\n2. Quan el proc\u00e9s d'inici de sessi\u00f3 s'hagi completat, torna aqu\u00ed i introdueix a sota el codi d'autoritzaci\u00f3.", + "description": "Introdueix el teu nom d'usuari i contrasenya.", "title": "Introdueix la teva informaci\u00f3" } } diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index 33bc10ac85d..5ed0292b2d0 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Dit SimpliSafe-account is al in gebruik.", + "email_2fa_timed_out": "Timed out tijdens het wachten op email-gebaseerde twee-factor authenticatie.", "reauth_successful": "Herauthenticatie was succesvol", "wrong_account": "De opgegeven gebruikersgegevens komen niet overeen met deze SimpliSafe-account." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Wacht nog steeds op MFA-e-mailklik", "unknown": "Onverwachte fout" }, + "progress": { + "email_2fa": "Voer de twee-factor authenticatie code in\ndie u per e-mail is toegezonden." + }, "step": { "mfa": { "title": "SimpliSafe Multi-Factor Authenticatie" diff --git a/homeassistant/components/simplisafe/translations/pt-BR.json b/homeassistant/components/simplisafe/translations/pt-BR.json index 596f1f441e6..5257950027b 100644 --- a/homeassistant/components/simplisafe/translations/pt-BR.json +++ b/homeassistant/components/simplisafe/translations/pt-BR.json @@ -24,7 +24,7 @@ "data": { "password": "Senha" }, - "description": "Por favor, digite novamente a senha para {username} .", + "description": "Por favor, digite novamente a senha para {username}.", "title": "Reautenticar Integra\u00e7\u00e3o" }, "sms_2fa": { diff --git a/homeassistant/components/slimproto/translations/bg.json b/homeassistant/components/slimproto/translations/bg.json new file mode 100644 index 00000000000..f49a3911eab --- /dev/null +++ b/homeassistant/components/slimproto/translations/bg.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/bg.json b/homeassistant/components/sql/translations/bg.json new file mode 100644 index 00000000000..b6c3577d1d0 --- /dev/null +++ b/homeassistant/components/sql/translations/bg.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u0418\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/hu.json b/homeassistant/components/sql/translations/hu.json index cf125215ae3..c7f8b02a2b6 100644 --- a/homeassistant/components/sql/translations/hu.json +++ b/homeassistant/components/sql/translations/hu.json @@ -13,6 +13,7 @@ "data": { "column": "Oszlop", "db_url": "Adatb\u00e1zis URL", + "name": "Elnevez\u00e9s", "query": "Lek\u00e9rdez\u00e9s kijel\u00f6l\u00e9se", "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", "value_template": "\u00c9rt\u00e9ksablon" @@ -20,6 +21,7 @@ "data_description": { "column": "\u00c1llapotk\u00e9nt megjelen\u00edtend\u0151, lek\u00e9rdezett oszlop", "db_url": "Adatb\u00e1zis URL-c\u00edme, hagyja \u00fcresen az alap\u00e9rtelmezett HA-adatb\u00e1zis haszn\u00e1lat\u00e1hoz", + "name": "A konfigur\u00e1ci\u00f3s bejegyz\u00e9shez \u00e9s az \u00e9rz\u00e9kel\u0151h\u00f6z haszn\u00e1lt n\u00e9v", "query": "A futtatand\u00f3 lek\u00e9rdez\u00e9snek 'SELECT'-el kell kezd\u0151dnie.", "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g (nem k\u00f6telez\u0151)", "value_template": "\u00c9rt\u00e9ksablon (nem k\u00f6telez\u0151)" @@ -38,6 +40,7 @@ "data": { "column": "Oszlop", "db_url": "Adatb\u00e1zis URL", + "name": "Elnevez\u00e9s", "query": "Lek\u00e9rdez\u00e9s kijel\u00f6l\u00e9se", "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g", "value_template": "\u00c9rt\u00e9ksablon" @@ -45,6 +48,7 @@ "data_description": { "column": "\u00c1llapotk\u00e9nt megjelen\u00edtend\u0151, lek\u00e9rdezett oszlop", "db_url": "Adatb\u00e1zis URL-c\u00edme, hagyja \u00fcresen az alap\u00e9rtelmezett HA-adatb\u00e1zis haszn\u00e1lat\u00e1hoz", + "name": "A konfigur\u00e1ci\u00f3s bejegyz\u00e9shez \u00e9s az \u00e9rz\u00e9kel\u0151h\u00f6z haszn\u00e1lt n\u00e9v", "query": "A futtatand\u00f3 lek\u00e9rdez\u00e9snek 'SELECT'-el kell kezd\u0151dnie.", "unit_of_measurement": "M\u00e9rt\u00e9kegys\u00e9g (nem k\u00f6telez\u0151)", "value_template": "\u00c9rt\u00e9ksablon (nem k\u00f6telez\u0151)" diff --git a/homeassistant/components/sql/translations/nl.json b/homeassistant/components/sql/translations/nl.json index c95d4b67701..68aa1230432 100644 --- a/homeassistant/components/sql/translations/nl.json +++ b/homeassistant/components/sql/translations/nl.json @@ -21,6 +21,7 @@ "data_description": { "column": "Kolom voor teruggestuurde query om als status te presenteren", "db_url": "Database URL, leeg laten om de standaard HA database te gebruiken", + "name": "Naam die zal worden gebruikt voor Config Entry en ook de Sensor", "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", "unit_of_measurement": "Meeteenheid (optioneel)", "value_template": "Waarde Template (optioneel)" @@ -47,6 +48,7 @@ "data_description": { "column": "Kolom voor teruggestuurde query om als status te presenteren", "db_url": "Database URL, leeg laten om de standaard HA database te gebruiken", + "name": "Naam die zal worden gebruikt voor Config Entry en ook de Sensor", "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", "unit_of_measurement": "Meeteenheid (optioneel)", "value_template": "Waarde Template (optioneel)" diff --git a/homeassistant/components/sql/translations/pl.json b/homeassistant/components/sql/translations/pl.json index 0e8cb47b148..2527a8239b3 100644 --- a/homeassistant/components/sql/translations/pl.json +++ b/homeassistant/components/sql/translations/pl.json @@ -13,6 +13,7 @@ "data": { "column": "Kolumna", "db_url": "Adres URL bazy danych", + "name": "Nazwa", "query": "Wybierz zapytanie", "unit_of_measurement": "Jednostka miary", "value_template": "Szablon warto\u015bci" @@ -20,6 +21,7 @@ "data_description": { "column": "Kolumna dla zwr\u00f3conego zapytania do przedstawienia jako stan", "db_url": "Adres URL bazy danych, pozostaw puste, aby u\u017cy\u0107 domy\u015blnej bazy danych HA", + "name": "Nazwa, kt\u00f3ra b\u0119dzie u\u017cywana do wprowadzania konfiguracji, a tak\u017ce do sensora", "query": "Zapytanie do uruchomienia, musi zaczyna\u0107 si\u0119 od \u201eSELECT\u201d", "unit_of_measurement": "Jednostka miary (opcjonalnie)", "value_template": "Szablon warto\u015bci (opcjonalnie)" @@ -38,6 +40,7 @@ "data": { "column": "Kolumna", "db_url": "Adres URL bazy danych", + "name": "Nazwa", "query": "Wybierz zapytanie", "unit_of_measurement": "Jednostka miary", "value_template": "Szablon warto\u015bci" @@ -45,6 +48,7 @@ "data_description": { "column": "Kolumna dla zwr\u00f3conego zapytania do przedstawienia jako stan", "db_url": "Adres URL bazy danych, pozostaw puste, aby u\u017cy\u0107 domy\u015blnej bazy danych HA", + "name": "Nazwa, kt\u00f3ra b\u0119dzie u\u017cywana do wprowadzania konfiguracji, a tak\u017ce do sensora", "query": "Zapytanie do uruchomienia, musi zaczyna\u0107 si\u0119 od \u201eSELECT\u201d", "unit_of_measurement": "Jednostka miary (opcjonalnie)", "value_template": "Szablon warto\u015bci (opcjonalnie)" diff --git a/homeassistant/components/sql/translations/pt-BR.json b/homeassistant/components/sql/translations/pt-BR.json index 21a930488a6..1846b8d5488 100644 --- a/homeassistant/components/sql/translations/pt-BR.json +++ b/homeassistant/components/sql/translations/pt-BR.json @@ -21,7 +21,7 @@ "data_description": { "column": "Coluna para a consulta retornada para apresentar como estado", "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", - "name": "Nome que ser\u00e1 usado para Config Entry e tamb\u00e9m para o Sensor", + "name": "Nome que ser\u00e1 usado para entrada de configura\u00e7\u00e3o e tamb\u00e9m para o Sensor", "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", "unit_of_measurement": "Unidade de medida (opcional)", "value_template": "Modelo do valor (opcional)" @@ -48,7 +48,7 @@ "data_description": { "column": "Coluna para a consulta retornada para apresentar como estado", "db_url": "URL do banco de dados, deixe em branco para usar o banco de dados padr\u00e3o", - "name": "Nome que ser\u00e1 usado para Config Entry e tamb\u00e9m para o Sensor", + "name": "Nome que ser\u00e1 usado para entrada de configura\u00e7\u00e3o e tamb\u00e9m para o Sensor", "query": "Consulte para ser executada, precisa come\u00e7ar com 'SELECT'", "unit_of_measurement": "Unidade de medida (opcional)", "value_template": "Modelo do valor (opcional)" diff --git a/homeassistant/components/sql/translations/zh-Hant.json b/homeassistant/components/sql/translations/zh-Hant.json index 801d973049d..43349c39295 100644 --- a/homeassistant/components/sql/translations/zh-Hant.json +++ b/homeassistant/components/sql/translations/zh-Hant.json @@ -13,6 +13,7 @@ "data": { "column": "\u6b04\u4f4d", "db_url": "\u8cc7\u6599\u5eab URL", + "name": "\u540d\u7a31", "query": "\u9078\u64c7\u67e5\u8a62", "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", "value_template": "\u6578\u503c\u6a21\u677f" @@ -20,6 +21,7 @@ "data_description": { "column": "\u67e5\u8a62\u56de\u8986\u6b04\u4f4d\u70ba\u72c0\u614b", "db_url": "\u8cc7\u6599\u5eab URL\u3001\u4fdd\u7559\u7a7a\u767d\u4ee5\u4f7f\u7528\u9810\u8a2d HA \u8cc7\u6599\u5eab", + "name": "\u540d\u7a31\u5c07\u6703\u7528\u65bc\u8a2d\u5b9a\u5be6\u9ad4\u8207\u611f\u6e2c\u5668", "query": "\u57f7\u884c\u7684\u67e5\u8a62\u3001\u9700\u8981\u4ee5 'SELECT' \u4f5c\u70ba\u958b\u982d", "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d\uff08\u9078\u9805\uff09", "value_template": "\u6578\u503c\u6a21\u677f\uff08\u9078\u9805\uff09" @@ -38,6 +40,7 @@ "data": { "column": "\u6b04\u4f4d", "db_url": "\u8cc7\u6599\u5eab URL", + "name": "\u540d\u7a31", "query": "\u9078\u64c7\u67e5\u8a62", "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d", "value_template": "\u6578\u503c\u6a21\u677f" @@ -45,6 +48,7 @@ "data_description": { "column": "\u67e5\u8a62\u56de\u8986\u6b04\u4f4d\u70ba\u72c0\u614b", "db_url": "\u8cc7\u6599\u5eab URL\u3001\u4fdd\u7559\u7a7a\u767d\u4ee5\u4f7f\u7528\u9810\u8a2d HA \u8cc7\u6599\u5eab", + "name": "\u540d\u7a31\u5c07\u6703\u7528\u65bc\u8a2d\u5b9a\u5be6\u9ad4\u8207\u611f\u6e2c\u5668", "query": "\u57f7\u884c\u7684\u67e5\u8a62\u3001\u9700\u8981\u4ee5 'SELECT' \u4f5c\u70ba\u958b\u982d", "unit_of_measurement": "\u6e2c\u91cf\u55ae\u4f4d\uff08\u9078\u9805\uff09", "value_template": "\u6578\u503c\u6a21\u677f\uff08\u9078\u9805\uff09" diff --git a/homeassistant/components/steam_online/translations/ca.json b/homeassistant/components/steam_online/translations/ca.json index 4535fe6d31c..699630c3732 100644 --- a/homeassistant/components/steam_online/translations/ca.json +++ b/homeassistant/components/steam_online/translations/ca.json @@ -12,13 +12,15 @@ }, "step": { "reauth_confirm": { + "description": "La integraci\u00f3 Steam ha de tornar a autenticar-se manualment\n\nPots trobar la teva clau a: {api_key_url}", "title": "Reautenticaci\u00f3 de la integraci\u00f3" }, "user": { "data": { "account": "ID del compte de Steam", "api_key": "Clau API" - } + }, + "description": "Utilitza {account_id_url} per trobar l'ID del teu compte de Steam" } } }, diff --git a/homeassistant/components/steam_online/translations/de.json b/homeassistant/components/steam_online/translations/de.json index 17950e14e24..fe73cf71448 100644 --- a/homeassistant/components/steam_online/translations/de.json +++ b/homeassistant/components/steam_online/translations/de.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Die Steam-Integration muss manuell erneut authentifiziert werden \n\nDeinen Schl\u00fcssel findest du hier: https://steamcommunity.com/dev/apikey", + "description": "Die Steam-Integration muss manuell erneut authentifiziert werden \nHier findest du deinen Schl\u00fcssel: {api_key_url}", "title": "Integration erneut authentifizieren" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam-Konto-ID", "api_key": "API-Schl\u00fcssel" }, - "description": "Verwende https://steamid.io, um deine Steam-Konto-ID zu finden" + "description": "Verwende {account_id_url}, um deine Steam-Konto-ID zu finden" } } }, diff --git a/homeassistant/components/steam_online/translations/fr.json b/homeassistant/components/steam_online/translations/fr.json index 5ee6892eef7..ae8055638f7 100644 --- a/homeassistant/components/steam_online/translations/fr.json +++ b/homeassistant/components/steam_online/translations/fr.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "L'int\u00e9gration Steam doit \u00eatre r\u00e9-authentifi\u00e9e manuellement\n\nVous pouvez trouver votre cl\u00e9 ici\u00a0: https://steamcommunity.com/dev/apikey", + "description": "L'int\u00e9gration Steam doit \u00eatre r\u00e9-authentifi\u00e9e manuellement\n\nVous pouvez trouver votre cl\u00e9 ici\u00a0: {api_key_url}", "title": "R\u00e9-authentifier l'int\u00e9gration" }, "user": { @@ -20,7 +20,7 @@ "account": "ID de compte Steam", "api_key": "Cl\u00e9 d'API" }, - "description": "Utilisez https://steamid.io pour trouver l'ID de votre compte Steam" + "description": "Utilisez {account_id_url} pour trouver l'ID de votre compte Steam" } } }, diff --git a/homeassistant/components/steam_online/translations/hu.json b/homeassistant/components/steam_online/translations/hu.json index 9bd0976345e..edff854ab52 100644 --- a/homeassistant/components/steam_online/translations/hu.json +++ b/homeassistant/components/steam_online/translations/hu.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "A Steam integr\u00e1ci\u00f3t manu\u00e1lisan \u00fajra kell hiteles\u00edteni .\n\nA kulcs itt tal\u00e1lhat\u00f3: https://steamcommunity.com/dev/apikey", + "description": "A Steam integr\u00e1ci\u00f3t manu\u00e1lisan \u00fajra kell hiteles\u00edteni .\n\nA kulcs itt tal\u00e1lhat\u00f3: {api_key_url}", "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam fi\u00f3k azonos\u00edt\u00f3ja", "api_key": "API kulcs" }, - "description": "A https://steamid.io haszn\u00e1lat\u00e1val kaphat\u00f3 meg a Steam fi\u00f3kazonos\u00edt\u00f3" + "description": "A {account_id_url} haszn\u00e1lat\u00e1val kaphat\u00f3 meg a Steam fi\u00f3kazonos\u00edt\u00f3" } } }, diff --git a/homeassistant/components/steam_online/translations/pt-BR.json b/homeassistant/components/steam_online/translations/pt-BR.json index 93ff3e00dc5..cca660e891e 100644 --- a/homeassistant/components/steam_online/translations/pt-BR.json +++ b/homeassistant/components/steam_online/translations/pt-BR.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "A integra\u00e7\u00e3o da Steam precisa ser autenticada manualmente\n\nVoc\u00ea pode encontrar sua chave aqui: https://steamcommunity.com/dev/apikey", + "description": "A integra\u00e7\u00e3o da Steam precisa ser autenticada manualmente \n\n Voc\u00ea pode encontrar sua chave aqui: {api_key_url}", "title": "Reautenticar Integra\u00e7\u00e3o" }, "user": { @@ -20,7 +20,7 @@ "account": "ID da conta Steam", "api_key": "Chave da API" }, - "description": "Use https://steamid.io para encontrar o ID da sua conta Steam" + "description": "Use {account_id_url} para encontrar o ID da sua conta Steam" } } }, diff --git a/homeassistant/components/tautulli/translations/ca.json b/homeassistant/components/tautulli/translations/ca.json index 15895636b8e..6e53dd83be5 100644 --- a/homeassistant/components/tautulli/translations/ca.json +++ b/homeassistant/components/tautulli/translations/ca.json @@ -13,14 +13,17 @@ "reauth_confirm": { "data": { "api_key": "Clau API" - } + }, + "description": "Per trobar la teva clau API, obre la web de Tautulli i v\u00e9s a Configuraci\u00f3 i despr\u00e9s a Interf\u00edcie web. La clau API estar\u00e0 a la part inferior d'aquesta p\u00e0gina.", + "title": "Re-autenticaci\u00f3 de Tautulli" }, "user": { "data": { "api_key": "Clau API", "url": "URL", "verify_ssl": "Verifica el certificat SSL" - } + }, + "description": "Per trobar la teva clau API, obre la web de Tautulli i v\u00e9s a Configuraci\u00f3 i despr\u00e9s a Interf\u00edcie web. La clau API estar\u00e0 a la part inferior d'aquesta p\u00e0gina.\n\nExemple de l'URL: ```http://192.168.0.10:8181``` on 8181 \u00e9s el port predeterminat." } } } diff --git a/homeassistant/components/tautulli/translations/nl.json b/homeassistant/components/tautulli/translations/nl.json index eeecbd56477..9f10f666030 100644 --- a/homeassistant/components/tautulli/translations/nl.json +++ b/homeassistant/components/tautulli/translations/nl.json @@ -14,6 +14,7 @@ "data": { "api_key": "API-sleutel" }, + "description": "Om uw API sleutel te vinden, opent u de Tautulli webpagina en navigeert u naar Instellingen en vervolgens naar Webinterface. De API sleutel vindt u onderaan de pagina.", "title": "Herauthenticeer Tautulli" }, "user": { @@ -21,7 +22,8 @@ "api_key": "API-sleutel", "url": "URL", "verify_ssl": "SSL-certificaat verifi\u00ebren" - } + }, + "description": "Om uw API sleutel te vinden, opent u de Tautulli webpagina en navigeert u naar Instellingen en vervolgens naar Webinterface. De API sleutel vindt u onderaan de pagina.\n\nVoorbeeld van de URL: ```http://192.168.0.10:8181`` met 8181 als standaard poort." } } } diff --git a/homeassistant/components/trafikverket_ferry/translations/ca.json b/homeassistant/components/trafikverket_ferry/translations/ca.json index 8780b02ee37..0af4ba34155 100644 --- a/homeassistant/components/trafikverket_ferry/translations/ca.json +++ b/homeassistant/components/trafikverket_ferry/translations/ca.json @@ -6,7 +6,9 @@ }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + "incorrect_api_key": "Clau API inv\u00e0lida per al compte seleccionat", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_route": "No s'ha pogut trobar la ruta amb la informaci\u00f3 proporcionada" }, "step": { "reauth_confirm": { @@ -18,6 +20,7 @@ "data": { "api_key": "Clau API", "from": "Des del port", + "time": "Hora", "to": "Al port", "weekday": "Laborables" } diff --git a/homeassistant/components/update/translations/sv.json b/homeassistant/components/update/translations/sv.json new file mode 100644 index 00000000000..53ecdb21377 --- /dev/null +++ b/homeassistant/components/update/translations/sv.json @@ -0,0 +1,9 @@ +{ + "device_automation": { + "trigger_type": { + "changed_states": "{entity_name} uppdateringstillg\u00e4nglighet \u00e4ndrad", + "turned_off": "{entity_name} blev uppdaterad", + "turned_on": "{entity_name} har en uppdatering tillg\u00e4nglig" + } + } +} \ No newline at end of file From ef09e8900671c729aacfea37c9274d4fc11bc18b Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Sun, 1 May 2022 11:00:37 +0200 Subject: [PATCH 122/930] Update xknx to 0.21.1 (#71144) --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index f5b1bdc6206..a000261ec3a 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.21.0"], + "requirements": ["xknx==0.21.1"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 6d2ef35b3eb..40800a0ff4a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2445,7 +2445,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.21.0 +xknx==0.21.1 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7fc6f066d0c..0283849e92e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1597,7 +1597,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.21.0 +xknx==0.21.1 # homeassistant.components.bluesound # homeassistant.components.fritz From 6e1b787ba6a7690340e2b1652a9831a8a9a570e9 Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Sun, 1 May 2022 13:21:27 +0200 Subject: [PATCH 123/930] Add missing type information for panel_custom (#71122) --- .../components/panel_custom/__init__.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/panel_custom/__init__.py b/homeassistant/components/panel_custom/__init__.py index d0ff0ad44a0..493a738c1ea 100644 --- a/homeassistant/components/panel_custom/__init__.py +++ b/homeassistant/components/panel_custom/__init__.py @@ -1,4 +1,6 @@ """Register a custom front end panel.""" +from __future__ import annotations + import logging import voluptuous as vol @@ -70,27 +72,27 @@ CONFIG_SCHEMA = vol.Schema( @bind_hass async def async_register_panel( - hass, + hass: HomeAssistant, # The url to serve the panel - frontend_url_path, + frontend_url_path: str, # The webcomponent name that loads your panel - webcomponent_name, + webcomponent_name: str, # Title/icon for sidebar - sidebar_title=None, - sidebar_icon=None, + sidebar_title: str | None = None, + sidebar_icon: str | None = None, # JS source of your panel - js_url=None, + js_url: str | None = None, # JS module of your panel - module_url=None, + module_url: str | None = None, # If your panel should be run inside an iframe - embed_iframe=DEFAULT_EMBED_IFRAME, + embed_iframe: bool = DEFAULT_EMBED_IFRAME, # Should user be asked for confirmation when loading external source - trust_external=DEFAULT_TRUST_EXTERNAL, + trust_external: bool = DEFAULT_TRUST_EXTERNAL, # Configuration to be passed to the panel - config=None, + config: ConfigType | None = None, # If your panel should only be shown to admin users - require_admin=False, -): + require_admin: bool = False, +) -> None: """Register a new custom panel.""" if js_url is None and module_url is None: raise ValueError("Either js_url, module_url or html_url is required.") From 4628b151ffa49387e2ff8e7b5dafb6e7cf7d85f5 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 1 May 2022 15:39:33 +0200 Subject: [PATCH 124/930] Bump pysensibo 1.0.14 (#71150) --- homeassistant/components/sensibo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index 535824aaa19..308d991e675 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.13"], + "requirements": ["pysensibo==1.0.14"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 40800a0ff4a..e0228c16571 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1783,7 +1783,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.13 +pysensibo==1.0.14 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0283849e92e..f9e059b2dad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1196,7 +1196,7 @@ pyruckus==0.12 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.13 +pysensibo==1.0.14 # homeassistant.components.serial # homeassistant.components.zha From 2d720973ee3f9a8a9bf777540b9a01fc6ff475fc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 11:53:47 -0500 Subject: [PATCH 125/930] Fix incomplete recorder typing (#71158) --- .strict-typing | 2 ++ .../components/recorder/system_health.py | 4 +++- mypy.ini | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.strict-typing b/.strict-typing index ea4124abde5..bf53662ee67 100644 --- a/.strict-typing +++ b/.strict-typing @@ -185,7 +185,9 @@ homeassistant.components.recorder.models homeassistant.components.recorder.pool homeassistant.components.recorder.purge homeassistant.components.recorder.repack +homeassistant.components.recorder.run_history homeassistant.components.recorder.statistics +homeassistant.components.recorder.system_health homeassistant.components.recorder.util homeassistant.components.recorder.websocket_api homeassistant.components.remote.* diff --git a/homeassistant/components/recorder/system_health.py b/homeassistant/components/recorder/system_health.py index 23fb760e898..2a9c536a2d6 100644 --- a/homeassistant/components/recorder/system_health.py +++ b/homeassistant/components/recorder/system_health.py @@ -1,5 +1,7 @@ """Provide info to system health.""" +from typing import Any + from homeassistant.components import system_health from homeassistant.core import HomeAssistant, callback @@ -14,7 +16,7 @@ def async_register( register.async_register_info(system_health_info) -async def system_health_info(hass: HomeAssistant): +async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: """Get info for the info page.""" instance = get_instance(hass) run_history = instance.run_history diff --git a/mypy.ini b/mypy.ini index 74ed61e5c57..c35e4d471c8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1798,6 +1798,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.recorder.run_history] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.recorder.statistics] check_untyped_defs = true disallow_incomplete_defs = true @@ -1809,6 +1820,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.recorder.system_health] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.recorder.util] check_untyped_defs = true disallow_incomplete_defs = true From c842672ed1c55d030533beb1592ac947bc57bc32 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 12:00:15 -0500 Subject: [PATCH 126/930] Fix missing device info in lutron_caseta (#71156) - There was a missing return due to a bad merge conflict resolution - Fixes #71154 --- .../components/lutron_caseta/__init__.py | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index d0561bc47ab..bb8f94f3abe 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -294,6 +294,8 @@ async def async_unload_entry( class LutronCasetaDevice(Entity): """Common base class for all Lutron Caseta devices.""" + _attr_should_poll = False + def __init__(self, device, bridge, bridge_device): """Set up the base class. @@ -304,6 +306,18 @@ class LutronCasetaDevice(Entity): self._device = device self._smartbridge = bridge self._bridge_device = bridge_device + info = DeviceInfo( + identifiers={(DOMAIN, self.serial)}, + manufacturer=MANUFACTURER, + model=f"{device['model']} ({device['type']})", + name=self.name, + via_device=(DOMAIN, self._bridge_device["serial"]), + configuration_url="https://device-login.lutron.com", + ) + area, _ = _area_and_name_from_name(device["name"]) + if area != UNASSIGNED_AREA: + info[ATTR_SUGGESTED_AREA] = area + self._attr_device_info = info async def async_added_to_hass(self): """Register callbacks.""" @@ -329,28 +343,7 @@ class LutronCasetaDevice(Entity): """Return the unique ID of the device (serial).""" return str(self.serial) - @property - def device_info(self) -> DeviceInfo: - """Return the device info.""" - device = self._device - info = DeviceInfo( - identifiers={(DOMAIN, self.serial)}, - manufacturer=MANUFACTURER, - model=f"{device['model']} ({device['type']})", - name=self.name, - via_device=(DOMAIN, self._bridge_device["serial"]), - configuration_url="https://device-login.lutron.com", - ) - area, _ = _area_and_name_from_name(device["name"]) - if area != UNASSIGNED_AREA: - info[ATTR_SUGGESTED_AREA] = area - @property def extra_state_attributes(self): """Return the state attributes.""" return {"device_id": self.device_id, "zone_id": self._device["zone"]} - - @property - def should_poll(self): - """No polling needed.""" - return False From 2cb9783cf5591851b146b8d378ad8fe1463fe9bb Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 1 May 2022 19:00:58 +0200 Subject: [PATCH 127/930] Small cleanup Sensibo (#71149) * Cleanup * fix temp * Modify set_temp * Apply suggestions from code review Co-authored-by: Shay Levy --- homeassistant/components/sensibo/__init__.py | 5 ++--- homeassistant/components/sensibo/climate.py | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/sensibo/__init__.py b/homeassistant/components/sensibo/__init__.py index ab8e4e85d39..dc02e9ee686 100644 --- a/homeassistant/components/sensibo/__init__.py +++ b/homeassistant/components/sensibo/__init__.py @@ -26,12 +26,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload Sensibo config entry.""" - if await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): del hass.data[DOMAIN][entry.entry_id] if not hass.data[DOMAIN]: del hass.data[DOMAIN] - return True - return False + return unload_ok async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/sensibo/climate.py b/homeassistant/components/sensibo/climate.py index 907105b5384..c1e690cd28a 100644 --- a/homeassistant/components/sensibo/climate.py +++ b/homeassistant/components/sensibo/climate.py @@ -178,7 +178,7 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): ) if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: - return + raise ValueError("No target temperature provided") if temperature == self.target_temperature: return @@ -192,7 +192,9 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity): temperature = self.device_data.temp_list[0] else: - return + raise ValueError( + f"Target temperature has to be one off {str(self.device_data.temp_list)}" + ) await self._async_set_ac_state_property("targetTemperature", int(temperature)) From 2e20ec21c49d090987dc885e0097d83fee5a4e6e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 12:49:17 -0500 Subject: [PATCH 128/930] Bump zeroconf to 0.38.5 (#71160) --- homeassistant/components/zeroconf/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index dc4c7c001ae..e1ea2c82b1f 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.38.4"], + "requirements": ["zeroconf==0.38.5"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0cb893be99d..6dd904c858f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -34,7 +34,7 @@ typing-extensions>=3.10.0.2,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 yarl==1.7.2 -zeroconf==0.38.4 +zeroconf==0.38.5 # Constrain pycryptodome to avoid vulnerability # see https://github.com/home-assistant/core/pull/16238 diff --git a/requirements_all.txt b/requirements_all.txt index e0228c16571..5fe40d3c7b5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2480,7 +2480,7 @@ youtube_dl==2021.12.17 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.38.4 +zeroconf==0.38.5 # homeassistant.components.zha zha-quirks==0.0.73 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f9e059b2dad..d24235fb1ff 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1620,7 +1620,7 @@ yeelight==0.7.10 youless-api==0.16 # homeassistant.components.zeroconf -zeroconf==0.38.4 +zeroconf==0.38.5 # homeassistant.components.zha zha-quirks==0.0.73 From 63e309506228c4cbefc0c053a3d069a504ada8ab Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 1 May 2022 21:00:38 +0200 Subject: [PATCH 129/930] Abort UniFi Network options flow if integration is not setup (#71128) --- homeassistant/components/unifi/config_flow.py | 2 ++ homeassistant/components/unifi/strings.json | 5 ++++- homeassistant/components/unifi/translations/en.json | 3 +++ tests/components/unifi/test_config_flow.py | 11 +++++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index 6e40798fb7a..d02f3f49a5e 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -256,6 +256,8 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): async def async_step_init(self, user_input=None): """Manage the UniFi Network options.""" + if self.config_entry.entry_id not in self.hass.data[UNIFI_DOMAIN]: + return self.async_abort(reason="integration_not_setup") self.controller = self.hass.data[UNIFI_DOMAIN][self.config_entry.entry_id] self.options[CONF_BLOCK_CLIENT] = self.controller.option_block_clients diff --git a/homeassistant/components/unifi/strings.json b/homeassistant/components/unifi/strings.json index 476a0bfdd61..8d6df90b704 100644 --- a/homeassistant/components/unifi/strings.json +++ b/homeassistant/components/unifi/strings.json @@ -21,11 +21,14 @@ }, "abort": { "already_configured": "UniFi Network site is already configured", - "configuration_updated": "Configuration updated.", + "configuration_updated": "Configuration updated", "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" } }, "options": { + "abort": { + "integration_not_setup": "UniFi integration is not setup" + }, "step": { "device_tracker": { "data": { diff --git a/homeassistant/components/unifi/translations/en.json b/homeassistant/components/unifi/translations/en.json index 5d08c8b816d..2a1c17cb6e3 100644 --- a/homeassistant/components/unifi/translations/en.json +++ b/homeassistant/components/unifi/translations/en.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi integration is not setup" + }, "step": { "client_control": { "data": { diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index 4c4ff7006fd..321cbdfd9e8 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -542,6 +542,17 @@ async def test_simple_option_flow(hass, aioclient_mock): } +async def test_option_flow_integration_not_setup(hass, aioclient_mock): + """Test advanced config flow options.""" + config_entry = await setup_unifi_integration(hass, aioclient_mock) + + hass.data[UNIFI_DOMAIN].pop(config_entry.entry_id) + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == "abort" + assert result["reason"] == "integration_not_setup" + + async def test_form_ssdp(hass): """Test we get the form with ssdp source.""" From 5b25b94a22f84143a6055d782fd2c6ac6bd8d238 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 1 May 2022 22:04:03 +0200 Subject: [PATCH 130/930] Fix template error in sql (#71169) --- homeassistant/components/sql/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index 2ae626c67fa..5e748bed55e 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -42,7 +42,7 @@ _QUERY_SCHEME = vol.Schema( vol.Required(CONF_NAME): cv.string, vol.Required(CONF_QUERY): cv.string, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, - vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_VALUE_TEMPLATE): cv.string, } ) From 40280cbd43ec3a5fa95465289fe86ba6ea57545f Mon Sep 17 00:00:00 2001 From: Matt Zimmerman Date: Sun, 1 May 2022 13:36:13 -0700 Subject: [PATCH 131/930] update python-smarttub to 0.0.32 (#71164) --- homeassistant/components/smarttub/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smarttub/manifest.json b/homeassistant/components/smarttub/manifest.json index 94e8185bb7d..1d7500b9185 100644 --- a/homeassistant/components/smarttub/manifest.json +++ b/homeassistant/components/smarttub/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/smarttub", "dependencies": [], "codeowners": ["@mdz"], - "requirements": ["python-smarttub==0.0.31"], + "requirements": ["python-smarttub==0.0.32"], "quality_scale": "platinum", "iot_class": "cloud_polling", "loggers": ["smarttub"] diff --git a/requirements_all.txt b/requirements_all.txt index 5fe40d3c7b5..ae3de70522a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1934,7 +1934,7 @@ python-qbittorrent==0.4.2 python-ripple-api==0.0.3 # homeassistant.components.smarttub -python-smarttub==0.0.31 +python-smarttub==0.0.32 # homeassistant.components.songpal python-songpal==0.14.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d24235fb1ff..8ec85ba314b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1269,7 +1269,7 @@ python-nest==4.2.0 python-picnic-api==1.1.0 # homeassistant.components.smarttub -python-smarttub==0.0.31 +python-smarttub==0.0.32 # homeassistant.components.songpal python-songpal==0.14.1 From b97ca8e26009baba53a038dba4559eadad5ddf7a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 1 May 2022 14:20:44 -0700 Subject: [PATCH 132/930] Bump gcal_sync to 0.7.1 to fix calendar API timezone handling (#71173) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 46c5844819a..2cf852fc6af 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["auth"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.7.0", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.7.1", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index ae3de70522a..a331efde6c2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.7.0 +gcal-sync==0.7.1 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8ec85ba314b..67bba2141d5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -486,7 +486,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.7.0 +gcal-sync==0.7.1 # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 From ff48720c6ad7314a52c76ebaba8d0ca1b581db3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 18:11:11 -0500 Subject: [PATCH 133/930] Remove db schema v23 stats migration tests (#71137) --- tests/components/recorder/test_statistics.py | 612 +------------------ 1 file changed, 2 insertions(+), 610 deletions(-) diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 8a73716e482..58f848edc5a 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -1,18 +1,13 @@ """The tests for sensor recorder platform.""" # pylint: disable=protected-access,invalid-name from datetime import timedelta -import importlib -import json -import sys from unittest.mock import patch, sentinel import pytest from pytest import approx -from sqlalchemy import create_engine -from sqlalchemy.orm import Session from homeassistant.components import recorder -from homeassistant.components.recorder import SQLITE_URL_PREFIX, history, statistics +from homeassistant.components.recorder import history, statistics from homeassistant.components.recorder.const import DATA_INSTANCE from homeassistant.components.recorder.models import ( StatisticsShortTerm, @@ -37,7 +32,7 @@ import homeassistant.util.dt as dt_util from .common import async_wait_recording_done -from tests.common import get_test_home_assistant, mock_registry +from tests.common import mock_registry from tests.components.recorder.common import wait_recording_done ORIG_TZ = dt_util.DEFAULT_TIME_ZONE @@ -740,609 +735,6 @@ def test_monthly_statistics(hass_recorder, caplog, timezone): dt_util.set_default_time_zone(dt_util.get_time_zone("UTC")) -def _create_engine_test(*args, **kwargs): - """Test version of create_engine that initializes with old schema. - - This simulates an existing db with the old schema. - """ - module = "tests.components.recorder.models_schema_23" - importlib.import_module(module) - old_models = sys.modules[module] - engine = create_engine(*args, **kwargs) - old_models.Base.metadata.create_all(engine) - with Session(engine) as session: - session.add(recorder.models.StatisticsRuns(start=statistics.get_start_time())) - session.add( - recorder.models.SchemaChanges(schema_version=old_models.SCHEMA_VERSION) - ) - session.commit() - return engine - - -def test_delete_duplicates(caplog, tmpdir): - """Test removal of duplicated statistics.""" - test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") - dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" - - module = "tests.components.recorder.models_schema_23" - importlib.import_module(module) - old_models = sys.modules[module] - - period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) - period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) - period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) - period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) - - external_energy_statistics_1 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 2, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 3, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 4, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - }, - ) - external_energy_metadata_1 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_1", - "unit_of_measurement": "kWh", - } - external_energy_statistics_2 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 20, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 30, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 40, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - ) - external_energy_metadata_2 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_2", - "unit_of_measurement": "kWh", - } - external_co2_statistics = ( - { - "start": period1, - "last_reset": None, - "mean": 10, - }, - { - "start": period2, - "last_reset": None, - "mean": 30, - }, - { - "start": period3, - "last_reset": None, - "mean": 60, - }, - { - "start": period4, - "last_reset": None, - "mean": 90, - }, - ) - external_co2_metadata = { - "has_mean": True, - "has_sum": False, - "name": "Fossil percentage", - "source": "test", - "statistic_id": "test:fossil_percentage", - "unit_of_measurement": "%", - } - - # Create some duplicated statistics with schema version 23 - with patch.object(recorder, "models", old_models), patch.object( - recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION - ), patch( - "homeassistant.components.recorder.create_engine", new=_create_engine_test - ): - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - wait_recording_done(hass) - wait_recording_done(hass) - - with session_scope(hass=hass) as session: - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) - ) - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) - ) - session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) - with session_scope(hass=hass) as session: - for stat in external_energy_statistics_1: - session.add(recorder.models.Statistics.from_stats(1, stat)) - for stat in external_energy_statistics_2: - session.add(recorder.models.Statistics.from_stats(2, stat)) - for stat in external_co2_statistics: - session.add(recorder.models.Statistics.from_stats(3, stat)) - - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - # Test that the duplicates are removed during migration from schema 23 - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - hass.start() - wait_recording_done(hass) - wait_recording_done(hass) - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - assert "Deleted 2 duplicated statistics rows" in caplog.text - assert "Found non identical" not in caplog.text - assert "Found duplicated" not in caplog.text - - -def test_delete_duplicates_many(caplog, tmpdir): - """Test removal of duplicated statistics.""" - test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") - dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" - - module = "tests.components.recorder.models_schema_23" - importlib.import_module(module) - old_models = sys.modules[module] - - period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) - period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) - period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) - period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) - - external_energy_statistics_1 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 2, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 3, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 4, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - }, - ) - external_energy_metadata_1 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_1", - "unit_of_measurement": "kWh", - } - external_energy_statistics_2 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 20, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 30, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 40, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - ) - external_energy_metadata_2 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_2", - "unit_of_measurement": "kWh", - } - external_co2_statistics = ( - { - "start": period1, - "last_reset": None, - "mean": 10, - }, - { - "start": period2, - "last_reset": None, - "mean": 30, - }, - { - "start": period3, - "last_reset": None, - "mean": 60, - }, - { - "start": period4, - "last_reset": None, - "mean": 90, - }, - ) - external_co2_metadata = { - "has_mean": True, - "has_sum": False, - "name": "Fossil percentage", - "source": "test", - "statistic_id": "test:fossil_percentage", - "unit_of_measurement": "%", - } - - # Create some duplicated statistics with schema version 23 - with patch.object(recorder, "models", old_models), patch.object( - recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION - ), patch( - "homeassistant.components.recorder.create_engine", new=_create_engine_test - ): - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - wait_recording_done(hass) - wait_recording_done(hass) - - with session_scope(hass=hass) as session: - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) - ) - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) - ) - session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) - with session_scope(hass=hass) as session: - for stat in external_energy_statistics_1: - session.add(recorder.models.Statistics.from_stats(1, stat)) - for _ in range(3000): - session.add( - recorder.models.Statistics.from_stats( - 1, external_energy_statistics_1[-1] - ) - ) - for stat in external_energy_statistics_2: - session.add(recorder.models.Statistics.from_stats(2, stat)) - for stat in external_co2_statistics: - session.add(recorder.models.Statistics.from_stats(3, stat)) - - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - # Test that the duplicates are removed during migration from schema 23 - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - hass.start() - wait_recording_done(hass) - wait_recording_done(hass) - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - assert "Deleted 3002 duplicated statistics rows" in caplog.text - assert "Found non identical" not in caplog.text - assert "Found duplicated" not in caplog.text - - -@pytest.mark.freeze_time("2021-08-01 00:00:00+00:00") -def test_delete_duplicates_non_identical(caplog, tmpdir): - """Test removal of duplicated statistics.""" - test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") - dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" - - module = "tests.components.recorder.models_schema_23" - importlib.import_module(module) - old_models = sys.modules[module] - - period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) - period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) - period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) - period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) - - external_energy_statistics_1 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 2, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 3, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 4, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 6, - }, - ) - external_energy_metadata_1 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_1", - "unit_of_measurement": "kWh", - } - external_energy_statistics_2 = ( - { - "start": period1, - "last_reset": None, - "state": 0, - "sum": 20, - }, - { - "start": period2, - "last_reset": None, - "state": 1, - "sum": 30, - }, - { - "start": period3, - "last_reset": None, - "state": 2, - "sum": 40, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 50, - }, - ) - external_energy_metadata_2 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_2", - "unit_of_measurement": "kWh", - } - - # Create some duplicated statistics with schema version 23 - with patch.object(recorder, "models", old_models), patch.object( - recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION - ), patch( - "homeassistant.components.recorder.create_engine", new=_create_engine_test - ): - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - wait_recording_done(hass) - wait_recording_done(hass) - - with session_scope(hass=hass) as session: - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) - ) - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) - ) - with session_scope(hass=hass) as session: - for stat in external_energy_statistics_1: - session.add(recorder.models.Statistics.from_stats(1, stat)) - for stat in external_energy_statistics_2: - session.add(recorder.models.Statistics.from_stats(2, stat)) - - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - # Test that the duplicates are removed during migration from schema 23 - hass = get_test_home_assistant() - hass.config.config_dir = tmpdir - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - hass.start() - wait_recording_done(hass) - wait_recording_done(hass) - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - assert "Deleted 2 duplicated statistics rows" in caplog.text - assert "Deleted 1 non identical" in caplog.text - assert "Found duplicated" not in caplog.text - - isotime = dt_util.utcnow().isoformat() - backup_file_name = f".storage/deleted_statistics.{isotime}.json" - - with open(hass.config.path(backup_file_name)) as backup_file: - backup = json.load(backup_file) - - assert backup == [ - { - "duplicate": { - "created": "2021-08-01T00:00:00", - "id": 4, - "last_reset": None, - "max": None, - "mean": None, - "metadata_id": 1, - "min": None, - "start": "2021-10-31T23:00:00", - "state": 3.0, - "sum": 5.0, - }, - "original": { - "created": "2021-08-01T00:00:00", - "id": 5, - "last_reset": None, - "max": None, - "mean": None, - "metadata_id": 1, - "min": None, - "start": "2021-10-31T23:00:00", - "state": 3.0, - "sum": 6.0, - }, - } - ] - - -def test_delete_duplicates_short_term(caplog, tmpdir): - """Test removal of duplicated statistics.""" - test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") - dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" - - module = "tests.components.recorder.models_schema_23" - importlib.import_module(module) - old_models = sys.modules[module] - - period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) - - external_energy_metadata_1 = { - "has_mean": False, - "has_sum": True, - "name": "Total imported energy", - "source": "test", - "statistic_id": "test:total_energy_import_tariff_1", - "unit_of_measurement": "kWh", - } - statistic_row = { - "start": period4, - "last_reset": None, - "state": 3, - "sum": 5, - } - - # Create some duplicated statistics with schema version 23 - with patch.object(recorder, "models", old_models), patch.object( - recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION - ), patch( - "homeassistant.components.recorder.create_engine", new=_create_engine_test - ): - hass = get_test_home_assistant() - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - wait_recording_done(hass) - wait_recording_done(hass) - - with session_scope(hass=hass) as session: - session.add( - recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) - ) - with session_scope(hass=hass) as session: - session.add( - recorder.models.StatisticsShortTerm.from_stats(1, statistic_row) - ) - session.add( - recorder.models.StatisticsShortTerm.from_stats(1, statistic_row) - ) - - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - # Test that the duplicates are removed during migration from schema 23 - hass = get_test_home_assistant() - hass.config.config_dir = tmpdir - setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) - hass.start() - wait_recording_done(hass) - wait_recording_done(hass) - hass.stop() - dt_util.DEFAULT_TIME_ZONE = ORIG_TZ - - assert "duplicated statistics rows" not in caplog.text - assert "Found non identical" not in caplog.text - assert "Deleted duplicated short term statistic" in caplog.text - - def test_delete_duplicates_no_duplicates(hass_recorder, caplog): """Test removal of duplicated statistics.""" hass = hass_recorder() From 26c6328b1ffde1071718b5442ebaffc7fddc73d4 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Mon, 2 May 2022 00:13:21 +0100 Subject: [PATCH 134/930] Generic camera handle template adjacent to portnumber (#71031) --- .../components/generic/config_flow.py | 30 ++++++++----- tests/components/generic/test_config_flow.py | 42 +++++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 4298b473681..086262aa0a1 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -130,10 +130,9 @@ async def async_test_still(hass, info) -> tuple[dict[str, str], str | None]: fmt = None if not (url := info.get(CONF_STILL_IMAGE_URL)): return {}, info.get(CONF_CONTENT_TYPE, "image/jpeg") - if not isinstance(url, template_helper.Template) and url: - url = cv.template(url) - url.hass = hass try: + if not isinstance(url, template_helper.Template): + url = template_helper.Template(url, hass) url = url.async_render(parse_result=False) except TemplateError as err: _LOGGER.warning("Problem rendering template %s: %s", url, err) @@ -168,11 +167,20 @@ async def async_test_still(hass, info) -> tuple[dict[str, str], str | None]: return {}, f"image/{fmt}" -def slug_url(url) -> str | None: +def slug(hass, template) -> str | None: """Convert a camera url into a string suitable for a camera name.""" - if not url: + if not template: return None - return slugify(yarl.URL(url).host) + if not isinstance(template, template_helper.Template): + template = template_helper.Template(template, hass) + try: + url = template.async_render(parse_result=False) + return slugify(yarl.URL(url).host) + except TemplateError as err: + _LOGGER.error("Syntax error in '%s': %s", template.template, err) + except (ValueError, TypeError) as err: + _LOGGER.error("Syntax error in '%s': %s", url, err) + return None async def async_test_stream(hass, info) -> dict[str, str]: @@ -252,6 +260,7 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle the start of the config flow.""" errors = {} + hass = self.hass if user_input: # Secondary validation because serialised vol can't seem to handle this complexity: if not user_input.get(CONF_STILL_IMAGE_URL) and not user_input.get( @@ -263,8 +272,7 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN): errors = errors | await async_test_stream(self.hass, user_input) still_url = user_input.get(CONF_STILL_IMAGE_URL) stream_url = user_input.get(CONF_STREAM_SOURCE) - name = slug_url(still_url) or slug_url(stream_url) or DEFAULT_NAME - + name = slug(hass, still_url) or slug(hass, stream_url) or DEFAULT_NAME if not errors: user_input[CONF_CONTENT_TYPE] = still_format user_input[CONF_LIMIT_REFETCH_TO_URL_CHANGE] = False @@ -295,7 +303,8 @@ class GenericIPCamConfigFlow(ConfigFlow, domain=DOMAIN): still_url = import_config.get(CONF_STILL_IMAGE_URL) stream_url = import_config.get(CONF_STREAM_SOURCE) name = import_config.get( - CONF_NAME, slug_url(still_url) or slug_url(stream_url) or DEFAULT_NAME + CONF_NAME, + slug(self.hass, still_url) or slug(self.hass, stream_url) or DEFAULT_NAME, ) if CONF_LIMIT_REFETCH_TO_URL_CHANGE not in import_config: import_config[CONF_LIMIT_REFETCH_TO_URL_CHANGE] = False @@ -318,6 +327,7 @@ class GenericOptionsFlowHandler(OptionsFlow): ) -> FlowResult: """Manage Generic IP Camera options.""" errors: dict[str, str] = {} + hass = self.hass if user_input is not None: errors, still_format = await async_test_still( @@ -327,7 +337,7 @@ class GenericOptionsFlowHandler(OptionsFlow): still_url = user_input.get(CONF_STILL_IMAGE_URL) stream_url = user_input.get(CONF_STREAM_SOURCE) if not errors: - title = slug_url(still_url) or slug_url(stream_url) or DEFAULT_NAME + title = slug(hass, still_url) or slug(hass, stream_url) or DEFAULT_NAME if still_url is None: # If user didn't specify a still image URL, # The automatically generated still image that stream generates diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index f5411ed3ea0..457cac26aa5 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -165,6 +165,48 @@ async def test_form_only_still_sample(hass, user_flow, image_file): assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY +@respx.mock +@pytest.mark.parametrize( + ("template", "url", "expected_result"), + [ + # Test we can handle templates in strange parts of the url, #70961. + ( + "http://localhost:812{{3}}/static/icons/favicon-apple-180x180.png", + "http://localhost:8123/static/icons/favicon-apple-180x180.png", + data_entry_flow.RESULT_TYPE_CREATE_ENTRY, + ), + ( + "{% if 1 %}https://bla{% else %}https://yo{% endif %}", + "https://bla/", + data_entry_flow.RESULT_TYPE_CREATE_ENTRY, + ), + ( + "http://{{example.org", + "http://example.org", + data_entry_flow.RESULT_TYPE_FORM, + ), + ( + "invalid1://invalid:4\\1", + "invalid1://invalid:4%5c1", + data_entry_flow.RESULT_TYPE_CREATE_ENTRY, + ), + ], +) +async def test_still_template( + hass, user_flow, fakeimgbytes_png, template, url, expected_result +) -> None: + """Test we can handle various templates.""" + respx.get(url).respond(stream=fakeimgbytes_png) + data = TESTDATA.copy() + data.pop(CONF_STREAM_SOURCE) + data[CONF_STILL_IMAGE_URL] = template + result2 = await hass.config_entries.flow.async_configure( + user_flow["flow_id"], + data, + ) + assert result2["type"] == expected_result + + @respx.mock async def test_form_rtsp_mode(hass, fakeimg_png, mock_av_open, user_flow): """Test we complete ok if the user enters a stream url.""" From d8ee9c1922b41188e04e2f33828363458c1a580c Mon Sep 17 00:00:00 2001 From: Kuba Wolanin Date: Mon, 2 May 2022 01:14:30 +0200 Subject: [PATCH 135/930] Add Show logs URL to integration errors notification (#71142) --- homeassistant/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/config.py b/homeassistant/config.py index d5f9eb91b62..5c870918231 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -951,8 +951,9 @@ def async_notify_setup_error( message = "The following integrations and platforms could not be set up:\n\n" for name, link in errors.items(): + show_logs = f"[Show logs](/config/logs?filter={name})" part = f"[{name}]({link})" if link else name - message += f" - {part}\n" + message += f" - {part} ({show_logs})\n" message += "\nPlease check your config and [logs](/config/logs)." From ae01ec02e28d4b83ef64636e36de2baf59c19874 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sun, 1 May 2022 19:26:22 -0400 Subject: [PATCH 136/930] Allow custom integrations to support application_credentials platform (#71129) --- .../application_credentials/__init__.py | 15 +++++--- homeassistant/loader.py | 15 ++++++++ script/hassfest/application_credentials.py | 2 +- script/hassfest/model.py | 4 +- .../application_credentials/test_init.py | 3 +- tests/test_loader.py | 37 +++++++++++++++++++ 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index cc5ed5e44bb..b5c828762c1 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -17,12 +17,15 @@ from homeassistant.components import websocket_api from homeassistant.components.websocket_api.connection import ActiveConnection from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN, CONF_ID from homeassistant.core import HomeAssistant, callback -from homeassistant.generated.application_credentials import APPLICATION_CREDENTIALS from homeassistant.helpers import collection, config_entry_oauth2_flow import homeassistant.helpers.config_validation as cv from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType -from homeassistant.loader import IntegrationNotFound, async_get_integration +from homeassistant.loader import ( + IntegrationNotFound, + async_get_application_credentials, + async_get_integration, +) from homeassistant.util import slugify __all__ = ["ClientCredential", "AuthorizationServer", "async_import_client_credential"] @@ -234,9 +237,11 @@ async def _get_platform( @websocket_api.websocket_command( {vol.Required("type"): "application_credentials/config"} ) -@callback -def handle_integration_list( +@websocket_api.async_response +async def handle_integration_list( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Handle integrations command.""" - connection.send_result(msg["id"], {"domains": APPLICATION_CREDENTIALS}) + connection.send_result( + msg["id"], {"domains": await async_get_application_credentials(hass)} + ) diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 364f212a1be..589f316532b 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -24,6 +24,7 @@ from awesomeversion import ( AwesomeVersionStrategy, ) +from .generated.application_credentials import APPLICATION_CREDENTIALS from .generated.dhcp import DHCP from .generated.mqtt import MQTT from .generated.ssdp import SSDP @@ -210,6 +211,20 @@ async def async_get_config_flows( return flows +async def async_get_application_credentials(hass: HomeAssistant) -> list[str]: + """Return cached list of application credentials.""" + integrations = await async_get_custom_components(hass) + + return [ + *APPLICATION_CREDENTIALS, + *[ + integration.domain + for integration in integrations.values() + if "application_credentials" in integration.dependencies + ], + ] + + def async_process_zeroconf_match_dict(entry: dict[str, Any]) -> dict[str, Any]: """Handle backwards compat with zeroconf matchers.""" entry_without_type: dict[str, Any] = entry.copy() diff --git a/script/hassfest/application_credentials.py b/script/hassfest/application_credentials.py index 87a277bb2b8..48d812dba02 100644 --- a/script/hassfest/application_credentials.py +++ b/script/hassfest/application_credentials.py @@ -18,7 +18,7 @@ APPLICATION_CREDENTIALS = {} def generate_and_validate(integrations: dict[str, Integration], config: Config) -> str: - """Validate and generate config flow data.""" + """Validate and generate application_credentials data.""" match_list = [] diff --git a/script/hassfest/model.py b/script/hassfest/model.py index 2a6ea9ca85f..fc38e1db592 100644 --- a/script/hassfest/model.py +++ b/script/hassfest/model.py @@ -98,9 +98,9 @@ class Integration: return self.manifest.get("quality_scale") @property - def config_flow(self) -> str: + def config_flow(self) -> bool: """Return if the integration has a config flow.""" - return self.manifest.get("config_flow") + return self.manifest.get("config_flow", False) @property def requirements(self) -> list[str]: diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index 31cf45f2b54..8929d8f9c54 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -615,8 +615,7 @@ async def test_websocket_integration_list(ws_client: ClientFixture): """Test websocket integration list command.""" client = await ws_client() with patch( - "homeassistant.components.application_credentials.APPLICATION_CREDENTIALS", - ["example1", "example2"], + "homeassistant.loader.APPLICATION_CREDENTIALS", ["example1", "example2"] ): assert await client.cmd_result("config") == { "domains": ["example1", "example2"] diff --git a/tests/test_loader.py b/tests/test_loader.py index 9f2aaff58b7..96694c43c7f 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -327,6 +327,26 @@ def _get_test_integration(hass, name, config_flow): ) +def _get_test_integration_with_application_credentials(hass, name): + """Return a generated test integration with application_credentials support.""" + return loader.Integration( + hass, + f"homeassistant.components.{name}", + None, + { + "name": name, + "domain": name, + "config_flow": True, + "dependencies": ["application_credentials"], + "requirements": [], + "zeroconf": [f"_{name}._tcp.local."], + "homekit": {"models": [name]}, + "ssdp": [{"manufacturer": name, "modelName": name}], + "mqtt": [f"{name}/discovery"], + }, + ) + + def _get_test_integration_with_zeroconf_matcher(hass, name, config_flow): """Return a generated test integration with a zeroconf matcher.""" return loader.Integration( @@ -479,6 +499,23 @@ async def test_get_zeroconf(hass): ] +async def test_get_application_credentials(hass): + """Verify that custom components with application_credentials are found.""" + test_1_integration = _get_test_integration(hass, "test_1", True) + test_2_integration = _get_test_integration_with_application_credentials( + hass, "test_2" + ) + + with patch("homeassistant.loader.async_get_custom_components") as mock_get: + mock_get.return_value = { + "test_1": test_1_integration, + "test_2": test_2_integration, + } + application_credentials = await loader.async_get_application_credentials(hass) + assert "test_2" in application_credentials + assert "test_1" not in application_credentials + + async def test_get_zeroconf_back_compat(hass): """Verify that custom components with zeroconf are found and legacy matchers are converted.""" test_1_integration = _get_test_integration(hass, "test_1", True) From 9b10658d0120a2d897a7a63c0cd01cf5e275b1e4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 2 May 2022 00:22:04 +0000 Subject: [PATCH 137/930] [ci skip] Translation update --- .../components/apple_tv/translations/it.json | 2 +- .../application_credentials/translations/et.json | 3 +++ .../application_credentials/translations/it.json | 3 +++ .../application_credentials/translations/pl.json | 3 +++ .../translations/zh-Hant.json | 3 +++ .../components/august/translations/it.json | 2 +- .../components/auth/translations/it.json | 2 +- .../components/deconz/translations/et.json | 1 + .../components/deconz/translations/it.json | 1 + .../components/deconz/translations/ru.json | 1 + .../components/elmax/translations/it.json | 2 +- homeassistant/components/fan/translations/it.json | 2 +- .../components/integration/translations/it.json | 2 +- .../components/isy994/translations/et.json | 13 +++++++++++-- .../components/isy994/translations/it.json | 15 ++++++++++++--- homeassistant/components/ps4/translations/it.json | 2 +- .../components/recorder/translations/et.json | 8 ++++++++ .../components/recorder/translations/it.json | 8 ++++++++ .../components/recorder/translations/pl.json | 8 ++++++++ .../components/recorder/translations/zh-Hant.json | 8 ++++++++ .../components/simplisafe/translations/it.json | 4 ++++ .../components/simplisafe/translations/ru.json | 4 ++++ homeassistant/components/sql/translations/it.json | 4 ++++ homeassistant/components/sql/translations/ru.json | 4 ++++ .../components/steam_online/translations/et.json | 4 ++-- .../components/steam_online/translations/it.json | 4 ++-- .../components/steam_online/translations/pl.json | 4 ++-- .../steam_online/translations/zh-Hant.json | 4 ++-- .../components/unifi/translations/de.json | 5 ++++- .../components/unifi/translations/en.json | 2 +- .../components/unifi/translations/fr.json | 5 ++++- .../components/unifi/translations/it.json | 5 ++++- .../components/unifi/translations/pt-BR.json | 5 ++++- .../components/vulcan/translations/it.json | 2 +- homeassistant/components/zha/translations/it.json | 2 +- 35 files changed, 120 insertions(+), 27 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/et.json create mode 100644 homeassistant/components/application_credentials/translations/it.json create mode 100644 homeassistant/components/application_credentials/translations/pl.json create mode 100644 homeassistant/components/application_credentials/translations/zh-Hant.json create mode 100644 homeassistant/components/recorder/translations/et.json create mode 100644 homeassistant/components/recorder/translations/it.json create mode 100644 homeassistant/components/recorder/translations/pl.json create mode 100644 homeassistant/components/recorder/translations/zh-Hant.json diff --git a/homeassistant/components/apple_tv/translations/it.json b/homeassistant/components/apple_tv/translations/it.json index b1e3a06440f..8b6e744ce53 100644 --- a/homeassistant/components/apple_tv/translations/it.json +++ b/homeassistant/components/apple_tv/translations/it.json @@ -19,7 +19,7 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "invalid_auth": "Autenticazione non valida", "no_devices_found": "Nessun dispositivo trovato sulla rete", - "no_usable_service": "\u00c8 stato trovato un dispositivo ma non \u00e8 stato possibile identificare alcun modo per stabilire una connessione ad esso. Se continui a vedere questo messaggio, prova a specificarne l'indirizzo IP o a riavviare l'Apple TV.", + "no_usable_service": "\u00c8 stato trovato un dispositivo, ma non \u00e8 stato possibile identificare alcun modo per stabilire una connessione con esso. Se continui a vedere questo messaggio, prova a specificarne l'indirizzo IP o a riavviare l'Apple TV.", "unknown": "Errore imprevisto" }, "flow_title": "{name} ({type})", diff --git a/homeassistant/components/application_credentials/translations/et.json b/homeassistant/components/application_credentials/translations/et.json new file mode 100644 index 00000000000..af619b0fb8d --- /dev/null +++ b/homeassistant/components/application_credentials/translations/et.json @@ -0,0 +1,3 @@ +{ + "title": "Rakenduse mandaadid" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/it.json b/homeassistant/components/application_credentials/translations/it.json new file mode 100644 index 00000000000..144f96c6342 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/it.json @@ -0,0 +1,3 @@ +{ + "title": "Credenziali dell'applicazione" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/pl.json b/homeassistant/components/application_credentials/translations/pl.json new file mode 100644 index 00000000000..93c2ddb7534 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/pl.json @@ -0,0 +1,3 @@ +{ + "title": "Po\u015bwiadczenia aplikacji" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/zh-Hant.json b/homeassistant/components/application_credentials/translations/zh-Hant.json new file mode 100644 index 00000000000..88ad70a7d10 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/zh-Hant.json @@ -0,0 +1,3 @@ +{ + "title": "\u61c9\u7528\u6191\u8b49" +} \ No newline at end of file diff --git a/homeassistant/components/august/translations/it.json b/homeassistant/components/august/translations/it.json index aebfc1b14cd..7a121915768 100644 --- a/homeassistant/components/august/translations/it.json +++ b/homeassistant/components/august/translations/it.json @@ -24,7 +24,7 @@ "username": "Nome utente" }, "description": "Se il metodo di accesso \u00e8 'email', il nome utente \u00e8 l'indirizzo email. Se il metodo di accesso \u00e8 'phone', il nome utente \u00e8 il numero di telefono nel formato '+NNNNNNNNNN'.", - "title": "Configura un account di August" + "title": "Configura un account August" }, "validation": { "data": { diff --git a/homeassistant/components/auth/translations/it.json b/homeassistant/components/auth/translations/it.json index e8091faf757..82d69b007ff 100644 --- a/homeassistant/components/auth/translations/it.json +++ b/homeassistant/components/auth/translations/it.json @@ -25,7 +25,7 @@ }, "step": { "init": { - "description": "Per attivare l'autenticazione a due fattori utilizzando le password monouso basate sul tempo, esegui la scansione del codice QR con l'app di autenticazione. Se non ne hai uno, ti consigliamo [Google Authenticator](https://support.google.com/accounts/answer/1066447) o [Authy](https://authy.com/). \n\n {qr_code} \n \nDopo la scansione, inserisci il codice a sei cifre dalla tua app per verificare la configurazione. Se riscontri problemi con la scansione del codice QR, esegui una configurazione manuale con il codice **`{code}`**.", + "description": "Per attivare l'autenticazione a due fattori utilizzando le password monouso basate sul tempo, esegui la scansione del codice QR con l'applicazione di autenticazione. Se non ne hai uno, ti consigliamo [Google Authenticator](https://support.google.com/accounts/answer/1066447) o [Authy](https://authy.com/). \n\n {qr_code} \n \nDopo la scansione, inserisci il codice a sei cifre dalla tua app per verificare la configurazione. Se riscontri problemi con la scansione del codice QR, esegui una configurazione manuale con il codice **`{code}`**.", "title": "Imposta l'autenticazione a due fattori usando TOTP" } }, diff --git a/homeassistant/components/deconz/translations/et.json b/homeassistant/components/deconz/translations/et.json index 6be6a7fbdc6..8519182878c 100644 --- a/homeassistant/components/deconz/translations/et.json +++ b/homeassistant/components/deconz/translations/et.json @@ -9,6 +9,7 @@ "updated_instance": "DeCONZ-i eksemplarile omistati uus hostiaadress" }, "error": { + "linking_not_possible": "\u00dchendus l\u00fc\u00fcsiga nurjus", "no_key": "API v\u00f5tit ei leitud" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/it.json b/homeassistant/components/deconz/translations/it.json index 2c3e42adcc8..44c204d7ed9 100644 --- a/homeassistant/components/deconz/translations/it.json +++ b/homeassistant/components/deconz/translations/it.json @@ -9,6 +9,7 @@ "updated_instance": "Istanza deCONZ aggiornata con nuovo indirizzo host" }, "error": { + "linking_not_possible": "Impossibile collegarsi al gateway", "no_key": "Impossibile ottenere una chiave API" }, "flow_title": "{host}", diff --git a/homeassistant/components/deconz/translations/ru.json b/homeassistant/components/deconz/translations/ru.json index 412c12198f3..f92ebd0e1c7 100644 --- a/homeassistant/components/deconz/translations/ru.json +++ b/homeassistant/components/deconz/translations/ru.json @@ -9,6 +9,7 @@ "updated_instance": "\u0410\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d." }, "error": { + "linking_not_possible": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0448\u043b\u044e\u0437\u0443.", "no_key": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API." }, "flow_title": "{host}", diff --git a/homeassistant/components/elmax/translations/it.json b/homeassistant/components/elmax/translations/it.json index 30098163498..07539c739bd 100644 --- a/homeassistant/components/elmax/translations/it.json +++ b/homeassistant/components/elmax/translations/it.json @@ -6,7 +6,7 @@ "error": { "bad_auth": "Autenticazione non valida", "invalid_auth": "Autenticazione non valida", - "invalid_pin": "Il pin fornito non \u00e8 valido", + "invalid_pin": "Il PIN fornito non \u00e8 valido", "network_error": "Si \u00e8 verificato un errore di rete", "no_panel_online": "Non \u00e8 stato trovato alcun pannello di controllo Elmax in linea.", "unknown": "Errore imprevisto", diff --git a/homeassistant/components/fan/translations/it.json b/homeassistant/components/fan/translations/it.json index ccde6f0a3a5..563b24b7c44 100644 --- a/homeassistant/components/fan/translations/it.json +++ b/homeassistant/components/fan/translations/it.json @@ -9,7 +9,7 @@ "is_on": "{entity_name} \u00e8 acceso" }, "trigger_type": { - "changed_states": "{entity_name} attivata o disattivat", + "changed_states": "{entity_name} attivata o disattivata", "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" diff --git a/homeassistant/components/integration/translations/it.json b/homeassistant/components/integration/translations/it.json index a1904f45cd1..d07b702962c 100644 --- a/homeassistant/components/integration/translations/it.json +++ b/homeassistant/components/integration/translations/it.json @@ -13,7 +13,7 @@ "data_description": { "round": "Controlla il numero di cifre decimali nell'output.", "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso della metrica selezionato.", - "unit_time": "L'output verr\u00e0 ridimensionato in base all'unit\u00e0 di tempo selezionata." + "unit_time": "L'output sar\u00e0 ridimensionato in base all'unit\u00e0 di tempo selezionata." }, "description": "Crea un sensore che calcoli una somma di Riemann per stimare l'integrale di un sensore.", "title": "Aggiungi il sensore integrale della somma di Riemann" diff --git a/homeassistant/components/isy994/translations/et.json b/homeassistant/components/isy994/translations/et.json index 233b3a2bfe8..0f7e0e45503 100644 --- a/homeassistant/components/isy994/translations/et.json +++ b/homeassistant/components/isy994/translations/et.json @@ -7,10 +7,19 @@ "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", "invalid_host": "Hostikirje ei olnud sobivas URL-vormingus, nt http://192.168.10.100:80", + "reauth_successful": "Taastuvastamine \u00f5nnestus", "unknown": "Tundmatu viga" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Salas\u00f5na", + "username": "Kasutajanimi" + }, + "description": "Kasutaja {host} mandaat ei kehti enam.", + "title": "Taastuvasta ISY" + }, "user": { "data": { "host": "", @@ -19,7 +28,7 @@ "username": "Kasutajanimi" }, "description": "Hostikirje peab olema t\u00e4ielikus URL-vormingus, nt http://192.168.10.100:80", - "title": "\u00dchendu oma ISY994-ga" + "title": "\u00dchendu oma ISY-ga" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Muutuja sensori string" }, "description": "Isy isidumisei suvandite m\u00e4\u00e4ramine: \n \u2022 S\u00f5lme sensor string: iga seade v\u00f5i kaust, mis sisaldab nimes \"Node Sensor String\" k\u00e4sitletakse sensorina v\u00f5i binaarandurina. \n \u2022 Ignoreeri stringi: iga seadet, mille nimes on \"Ignore String\", ignoreeritakse. \n \u2022 Muutuv sensorstring: andurina lisatakse k\u00f5ik muutujad, mis sisaldavad \"Variable Sensor String\". \n \u2022 Valguse heleduse taastamine: kui see on lubatud, taastatakse eelmine heledus, kui l\u00fclitate sisse seadme sisseehitatud taseme asemel valguse.", - "title": "ISY994 valikud" + "title": "ISY valikud" } } }, diff --git a/homeassistant/components/isy994/translations/it.json b/homeassistant/components/isy994/translations/it.json index b59e312717a..158d9f1d400 100644 --- a/homeassistant/components/isy994/translations/it.json +++ b/homeassistant/components/isy994/translations/it.json @@ -7,10 +7,19 @@ "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", "invalid_host": "La voce host non era nel formato URL completo, ad esempio http://192.168.10.100:80", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "unknown": "Errore imprevisto" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Password", + "username": "Nome utente" + }, + "description": "Le credenziali per {host} non sono pi\u00f9 valide.", + "title": "Autentica nuovamente il tuo ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Nome utente" }, "description": "La voce host deve essere nel formato URL completo, ad esempio, http://192.168.10.100:80", - "title": "Connettersi a ISY994" + "title": "Connettiti al tuo ISY" } } }, @@ -32,8 +41,8 @@ "sensor_string": "Stringa Nodo Sensore", "variable_sensor_string": "Stringa Variabile Sensore" }, - "description": "Imposta le opzioni per l'integrazione ISY: \n \u2022 Stringa Nodo Sensore: qualsiasi dispositivo o cartella che contiene \"Stringa Nodo Sensore\" nel nome verr\u00e0 trattato come un sensore o un sensore binario. \n \u2022 Ignora Stringa: qualsiasi dispositivo con \"Ignora Stringa\" nel nome verr\u00e0 ignorato. \n \u2022 Stringa Variabile Sensore: qualsiasi variabile che contiene \"Stringa Variabile Sensore\" verr\u00e0 aggiunta come sensore. \n \u2022 Ripristina Luminosit\u00e0 Luce: se abilitato, verr\u00e0 ripristinata la luminosit\u00e0 precedente quando si accende una luce al posto del livello incorporato nel dispositivo.", - "title": "Opzioni ISY994" + "description": "Imposta le opzioni per l'integrazione ISY: \n \u2022 Stringa nodo sensore: qualsiasi dispositivo o cartella che contiene \"Stringa nodo sensore\" nel nome verr\u00e0 trattato come un sensore o un sensore binario. \n \u2022 Ignora stringa: qualsiasi dispositivo con \"Ignora stringa\" nel nome verr\u00e0 ignorato. \n \u2022 Stringa variabile sensore: qualsiasi variabile che contiene \"Stringa variabile sensore\" verr\u00e0 aggiunta come sensore. \n \u2022 Ripristina luminosit\u00e0 luce: se abilitato, verr\u00e0 ripristinata la luminosit\u00e0 precedente quando si accende una luce al posto del livello incorporato nel dispositivo.", + "title": "Opzioni ISY" } } }, diff --git a/homeassistant/components/ps4/translations/it.json b/homeassistant/components/ps4/translations/it.json index 3faca963317..a8a97ad1144 100644 --- a/homeassistant/components/ps4/translations/it.json +++ b/homeassistant/components/ps4/translations/it.json @@ -15,7 +15,7 @@ }, "step": { "creds": { - "description": "Credenziali necessarie. Premi 'Invia' e poi, nella seconda schermata della App PS4, aggiorna i dispositivi e seleziona il dispositivo 'Home-Assistant' per continuare.", + "description": "Credenziali necessarie. Premi 'Invia' e poi, nell'applicazione PS4 Second Screen, aggiorna i dispositivi e seleziona il dispositivo 'Home-Assistant' per continuare.", "title": "PlayStation 4" }, "link": { diff --git a/homeassistant/components/recorder/translations/et.json b/homeassistant/components/recorder/translations/et.json new file mode 100644 index 00000000000..8455fb5ce7d --- /dev/null +++ b/homeassistant/components/recorder/translations/et.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Praegune k\u00e4ivitamise algusaeg", + "oldest_recorder_run": "Vanim k\u00e4ivitamise algusaeg" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/it.json b/homeassistant/components/recorder/translations/it.json new file mode 100644 index 00000000000..82f456981a8 --- /dev/null +++ b/homeassistant/components/recorder/translations/it.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Ora di inizio esecuzione corrente", + "oldest_recorder_run": "Ora di inizio esecuzione meno recente" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/pl.json b/homeassistant/components/recorder/translations/pl.json new file mode 100644 index 00000000000..1a31b3f7ec5 --- /dev/null +++ b/homeassistant/components/recorder/translations/pl.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Pocz\u0105tek aktualnej sesji rejestratora", + "oldest_recorder_run": "Pocz\u0105tek najstarszej sesji rejestratora" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/zh-Hant.json b/homeassistant/components/recorder/translations/zh-Hant.json new file mode 100644 index 00000000000..648bade5447 --- /dev/null +++ b/homeassistant/components/recorder/translations/zh-Hant.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "\u76ee\u524d\u57f7\u884c\u958b\u59cb\u6642\u9593", + "oldest_recorder_run": "\u6700\u65e9\u57f7\u884c\u958b\u59cb\u6642\u9593" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/it.json b/homeassistant/components/simplisafe/translations/it.json index ad9e491ab9a..fb92029795d 100644 --- a/homeassistant/components/simplisafe/translations/it.json +++ b/homeassistant/components/simplisafe/translations/it.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Questo account SimpliSafe \u00e8 gi\u00e0 in uso.", + "email_2fa_timed_out": "Timeout durante l'attesa dell'autenticazione a due fattori basata su email.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "wrong_account": "Le credenziali utente fornite non corrispondono a questo account SimpliSafe." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Ancora in attesa del clic sull'email MFA", "unknown": "Errore imprevisto" }, + "progress": { + "email_2fa": "Digita il codice di autenticazione a due fattori\n inviato tramite email." + }, "step": { "mfa": { "title": "Autenticazione a pi\u00f9 fattori (MFA) SimpliSafe " diff --git a/homeassistant/components/simplisafe/translations/ru.json b/homeassistant/components/simplisafe/translations/ru.json index 66bd439a0c1..1788a9ca206 100644 --- a/homeassistant/components/simplisafe/translations/ru.json +++ b/homeassistant/components/simplisafe/translations/ru.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "email_2fa_timed_out": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", "wrong_account": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u044d\u0442\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 SimpliSafe." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, + "progress": { + "email_2fa": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0412\u0430\u043c \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435." + }, "step": { "mfa": { "title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f SimpliSafe" diff --git a/homeassistant/components/sql/translations/it.json b/homeassistant/components/sql/translations/it.json index 01672eb5a51..3d7bee5454e 100644 --- a/homeassistant/components/sql/translations/it.json +++ b/homeassistant/components/sql/translations/it.json @@ -13,6 +13,7 @@ "data": { "column": "Colonna", "db_url": "URL del database", + "name": "Nome", "query": "Query Select", "unit_of_measurement": "Unit\u00e0 di misura", "value_template": "Modello di valore" @@ -20,6 +21,7 @@ "data_description": { "column": "Colonna per la query restituita da presentare come stato", "db_url": "URL del database, lascia vuoto per utilizzare il database predefinito di Home Assistant", + "name": "Nome che sar\u00e0 utilizzato per la voce di configurazione e anche per il sensore", "query": "Query da eseguire, deve iniziare con 'SELECT'", "unit_of_measurement": "Unit\u00e0 di misura (opzionale)", "value_template": "Modello di valore (opzionale)" @@ -38,6 +40,7 @@ "data": { "column": "Colonna", "db_url": "URL del database", + "name": "Nome", "query": "Query Select", "unit_of_measurement": "Unit\u00e0 di misura", "value_template": "Modello di valore" @@ -45,6 +48,7 @@ "data_description": { "column": "Colonna per la query restituita da presentare come stato", "db_url": "URL del database, lascia vuoto per utilizzare il database predefinito di Home Assistant", + "name": "Nome che sar\u00e0 utilizzato per la voce di configurazione e anche per il sensore", "query": "Query da eseguire, deve iniziare con 'SELECT'", "unit_of_measurement": "Unit\u00e0 di misura (opzionale)", "value_template": "Modello di valore (opzionale)" diff --git a/homeassistant/components/sql/translations/ru.json b/homeassistant/components/sql/translations/ru.json index 8f8e0741583..2d993776fa6 100644 --- a/homeassistant/components/sql/translations/ru.json +++ b/homeassistant/components/sql/translations/ru.json @@ -13,6 +13,7 @@ "data": { "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "query": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" @@ -20,6 +21,7 @@ "data_description": { "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 HA \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", "query": "\u0417\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 'SELECT'", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" @@ -38,6 +40,7 @@ "data": { "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446", "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "query": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" @@ -45,6 +48,7 @@ "data_description": { "column": "\u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", "db_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 HA \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", "query": "\u0417\u0430\u043f\u0440\u043e\u0441 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 'SELECT'", "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", "value_template": "\u0428\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" diff --git a/homeassistant/components/steam_online/translations/et.json b/homeassistant/components/steam_online/translations/et.json index 8d501ccf50d..7c09fa1de91 100644 --- a/homeassistant/components/steam_online/translations/et.json +++ b/homeassistant/components/steam_online/translations/et.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Steami sidumine tuleb uuesti autentida\n\nV\u00f5tme leiad siit: https://steamcommunity.com/dev/apikey", + "description": "Steami sidumine tuleb k\u00e4sitsi uuesti autentida \n\n Oma v\u00f5tme leiad siit: {api_key_url}", "title": "Taastuvasta sidumine" }, "user": { @@ -20,7 +20,7 @@ "account": "Steami konto ID", "api_key": "API v\u00f5ti" }, - "description": "Kasuta https://steamid.io, et leida oma Steam'i konto ID" + "description": "Steami konto ID leidmiseks kasuta {account_id_url}" } } }, diff --git a/homeassistant/components/steam_online/translations/it.json b/homeassistant/components/steam_online/translations/it.json index 71036870961..edb7878234d 100644 --- a/homeassistant/components/steam_online/translations/it.json +++ b/homeassistant/components/steam_online/translations/it.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "L'integrazione di Steam richiede una nuova autenticazione manuale \n\nPuoi trovare la tua chiave qui: https://steamcommunity.com/dev/apikey", + "description": "L'integrazione Steam richiede una nuova autenticazione manuale \n\nPuoi trovare la tua chiave qui: {api_key_url}", "title": "Autentica nuovamente l'integrazione" }, "user": { @@ -20,7 +20,7 @@ "account": "ID dell'account Steam", "api_key": "Chiave API" }, - "description": "Usa https://steamid.io per trovare l'ID del tuo account Steam" + "description": "Usa {account_id_url} per trovare l'ID del tuo account Steam" } } }, diff --git a/homeassistant/components/steam_online/translations/pl.json b/homeassistant/components/steam_online/translations/pl.json index 09c95bca9c4..370b997b3fc 100644 --- a/homeassistant/components/steam_online/translations/pl.json +++ b/homeassistant/components/steam_online/translations/pl.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Integracja Steam musi zosta\u0107 ponownie uwierzytelniona r\u0119cznie\n\nSw\u00f3j klucz znajdziesz tutaj: https://steamcommunity.com/dev/apikey", + "description": "Integracja Steam musi zosta\u0107 ponownie uwierzytelniona r\u0119cznie\n\nSw\u00f3j klucz znajdziesz tutaj: {api_key_url}", "title": "Ponownie uwierzytelnij integracj\u0119" }, "user": { @@ -20,7 +20,7 @@ "account": "Identyfikator konta Steam", "api_key": "Klucz API" }, - "description": "U\u017cyj https://steamid.io, aby znale\u017a\u0107 sw\u00f3j identyfikator konta Steam" + "description": "U\u017cyj {account_id_url}, aby znale\u017a\u0107 sw\u00f3j identyfikator konta Steam" } } }, diff --git a/homeassistant/components/steam_online/translations/zh-Hant.json b/homeassistant/components/steam_online/translations/zh-Hant.json index 775c9710592..17cff11185e 100644 --- a/homeassistant/components/steam_online/translations/zh-Hant.json +++ b/homeassistant/components/steam_online/translations/zh-Hant.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Steam \u6574\u5408\u9700\u8981\u624b\u52d5\u91cd\u65b0\u8a8d\u8b49\n\n\u53ef\u4ee5\u65bc\u5f8c\u65b9\u7db2\u5740\u627e\u5230\u91d1\u9470\uff1ahttps://steamcommunity.com/dev/apikey", + "description": "Steam \u6574\u5408\u9700\u8981\u624b\u52d5\u91cd\u65b0\u8a8d\u8b49\n\n\u53ef\u4ee5\u65bc\u5f8c\u65b9\u7db2\u5740\u627e\u5230\u91d1\u9470\uff1a{api_key_url}", "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam \u5e33\u865f ID", "api_key": "API \u91d1\u9470" }, - "description": "\u4f7f\u7528 https://steamid.io \u4ee5\u78ba\u8a8d\u60a8\u7684 Steam \u5e33\u865f ID" + "description": "\u4f7f\u7528 {account_id_url} \u4ee5\u78ba\u8a8d\u60a8\u7684 Steam \u5e33\u865f ID" } } }, diff --git a/homeassistant/components/unifi/translations/de.json b/homeassistant/components/unifi/translations/de.json index ce4047ced42..419bb3e8eba 100644 --- a/homeassistant/components/unifi/translations/de.json +++ b/homeassistant/components/unifi/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi-Netzwerkstandort ist bereits konfiguriert", - "configuration_updated": "Konfiguration aktualisiert.", + "configuration_updated": "Konfiguration aktualisiert", "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi-Integration ist nicht eingerichtet" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/en.json b/homeassistant/components/unifi/translations/en.json index 2a1c17cb6e3..6e36e517e16 100644 --- a/homeassistant/components/unifi/translations/en.json +++ b/homeassistant/components/unifi/translations/en.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi Network site is already configured", - "configuration_updated": "Configuration updated.", + "configuration_updated": "Configuration updated", "reauth_successful": "Re-authentication was successful" }, "error": { diff --git a/homeassistant/components/unifi/translations/fr.json b/homeassistant/components/unifi/translations/fr.json index d8f1bc0a84c..3da202d1cec 100644 --- a/homeassistant/components/unifi/translations/fr.json +++ b/homeassistant/components/unifi/translations/fr.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Le contr\u00f4leur est d\u00e9j\u00e0 configur\u00e9", - "configuration_updated": "Configuration mise \u00e0 jour.", + "configuration_updated": "Configuration mise \u00e0 jour", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "L'int\u00e9gration UniFi n'est pas configur\u00e9e" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/it.json b/homeassistant/components/unifi/translations/it.json index 7ddc0fde4fe..1ac0ff05798 100644 --- a/homeassistant/components/unifi/translations/it.json +++ b/homeassistant/components/unifi/translations/it.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Il sito della rete UniFi \u00e8 gi\u00e0 configurato", - "configuration_updated": "Configurazione aggiornata.", + "configuration_updated": "Configurazione aggiornata", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "L'integrazione UniFi non \u00e8 configurata" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/pt-BR.json b/homeassistant/components/unifi/translations/pt-BR.json index 0e5ba9af217..00dd0b08165 100644 --- a/homeassistant/components/unifi/translations/pt-BR.json +++ b/homeassistant/components/unifi/translations/pt-BR.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "A conta j\u00e1 foi configurada", - "configuration_updated": "Configura\u00e7\u00e3o atualizada.", + "configuration_updated": "Configura\u00e7\u00e3o atualizada", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "A integra\u00e7\u00e3o UniFi n\u00e3o est\u00e1 configurada" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/vulcan/translations/it.json b/homeassistant/components/vulcan/translations/it.json index 72bb696a909..895f4469eb9 100644 --- a/homeassistant/components/vulcan/translations/it.json +++ b/homeassistant/components/vulcan/translations/it.json @@ -9,7 +9,7 @@ "cannot_connect": "Errore di connessione: controlla la tua connessione Internet", "expired_credentials": "Credenziali scadute: creane una nuova nella pagina di registrazione dell'applicazione mobile Vulcan", "expired_token": "Token scaduto: genera un nuovo token", - "invalid_pin": "Pin non valido", + "invalid_pin": "PIN non valido", "invalid_symbol": "Simbolo non valido", "invalid_token": "Token non valido", "unknown": "Si \u00e8 verificato un errore sconosciuto" diff --git a/homeassistant/components/zha/translations/it.json b/homeassistant/components/zha/translations/it.json index 504ae7eadba..57be4c7acb6 100644 --- a/homeassistant/components/zha/translations/it.json +++ b/homeassistant/components/zha/translations/it.json @@ -86,7 +86,7 @@ "device_dropped": "Dispositivo caduto", "device_flipped": "Dispositivo capovolto \"{subtype}\"", "device_knocked": "Dispositivo bussato \"{subtype}\"", - "device_offline": "Dispositivo offline", + "device_offline": "Dispositivo non in linea", "device_rotated": "Dispositivo ruotato \" {subtype} \"", "device_shaken": "Dispositivo in vibrazione", "device_slid": "Dispositivo scivolato \"{subtype}\"", From b8442d9340b569d10e0593bb0576bdcdb9ea55e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 19:33:31 -0500 Subject: [PATCH 138/930] Add json decode caching to logbook (#71080) --- .strict-typing | 1 + homeassistant/components/logbook/__init__.py | 261 ++++++++++++------- homeassistant/scripts/benchmark/__init__.py | 2 +- mypy.ini | 11 + tests/components/logbook/test_init.py | 87 ++++++- 5 files changed, 261 insertions(+), 101 deletions(-) diff --git a/.strict-typing b/.strict-typing index bf53662ee67..df54a082119 100644 --- a/.strict-typing +++ b/.strict-typing @@ -139,6 +139,7 @@ homeassistant.components.lcn.* homeassistant.components.light.* homeassistant.components.local_ip.* homeassistant.components.lock.* +homeassistant.components.logbook.* homeassistant.components.lookin.* homeassistant.components.luftdaten.* homeassistant.components.mailbox.* diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 8b00311a9e7..b136c7330ac 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -1,16 +1,18 @@ """Event parser and human readable log generator.""" from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Callable, Generator, Iterable from contextlib import suppress from datetime import datetime as dt, timedelta from http import HTTPStatus from itertools import groupby import json import re -from typing import Any +from typing import Any, cast +from aiohttp import web import sqlalchemy +from sqlalchemy.engine.row import Row from sqlalchemy.orm import aliased from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session @@ -19,7 +21,10 @@ import voluptuous as vol from homeassistant.components import frontend from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED -from homeassistant.components.history import sqlalchemy_filter_from_include_exclude_conf +from homeassistant.components.history import ( + Filters, + sqlalchemy_filter_from_include_exclude_conf, +) from homeassistant.components.http import HomeAssistantView from homeassistant.components.recorder import get_instance from homeassistant.components.recorder.models import ( @@ -34,7 +39,6 @@ from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, - ATTR_ICON, ATTR_NAME, ATTR_SERVICE, EVENT_CALL_SERVICE, @@ -45,6 +49,8 @@ from homeassistant.const import ( ) from homeassistant.core import ( DOMAIN as HA_DOMAIN, + Context, + Event, HomeAssistant, ServiceCall, callback, @@ -54,6 +60,7 @@ from homeassistant.exceptions import InvalidEntityFormatError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, + EntityFilter, convert_include_exclude_filter, generate_filter, ) @@ -65,6 +72,7 @@ from homeassistant.loader import bind_hass import homeassistant.util.dt as dt_util ENTITY_ID_JSON_TEMPLATE = '%"entity_id":"{}"%' +FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"') @@ -111,7 +119,7 @@ EVENT_COLUMNS = [ Events.context_parent_id, ] -SCRIPT_AUTOMATION_EVENTS = [EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED] +SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} LOG_MESSAGE_SCHEMA = vol.Schema( { @@ -124,14 +132,28 @@ LOG_MESSAGE_SCHEMA = vol.Schema( @bind_hass -def log_entry(hass, name, message, domain=None, entity_id=None, context=None): +def log_entry( + hass: HomeAssistant, + name: str, + message: str, + domain: str | None = None, + entity_id: str | None = None, + context: Context | None = None, +) -> None: """Add an entry to the logbook.""" hass.add_job(async_log_entry, hass, name, message, domain, entity_id, context) @callback @bind_hass -def async_log_entry(hass, name, message, domain=None, entity_id=None, context=None): +def async_log_entry( + hass: HomeAssistant, + name: str, + message: str, + domain: str | None = None, + entity_id: str | None = None, + context: Context | None = None, +) -> None: """Add an entry to the logbook.""" data = {ATTR_NAME: name, ATTR_MESSAGE: message} @@ -184,11 +206,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -async def _process_logbook_platform(hass, domain, platform): +async def _process_logbook_platform( + hass: HomeAssistant, domain: str, platform: Any +) -> None: """Process a logbook platform.""" @callback - def _async_describe_event(domain, event_name, describe_callback): + def _async_describe_event( + domain: str, + event_name: str, + describe_callback: Callable[[Event], dict[str, Any]], + ) -> None: """Teach logbook how to describe a new event.""" hass.data[DOMAIN][event_name] = (domain, describe_callback) @@ -202,41 +230,51 @@ class LogbookView(HomeAssistantView): name = "api:logbook" extra_urls = ["/api/logbook/{datetime}"] - def __init__(self, config, filters, entities_filter): + def __init__( + self, + config: dict[str, Any], + filters: Filters | None, + entities_filter: EntityFilter | None, + ) -> None: """Initialize the logbook view.""" self.config = config self.filters = filters self.entities_filter = entities_filter - async def get(self, request, datetime=None): + async def get( + self, request: web.Request, datetime: str | None = None + ) -> web.Response: """Retrieve logbook entries.""" if datetime: - if (datetime := dt_util.parse_datetime(datetime)) is None: + if (datetime_dt := dt_util.parse_datetime(datetime)) is None: return self.json_message("Invalid datetime", HTTPStatus.BAD_REQUEST) else: - datetime = dt_util.start_of_local_day() + datetime_dt = dt_util.start_of_local_day() - if (period := request.query.get("period")) is None: - period = 1 + if (period_str := request.query.get("period")) is None: + period: int = 1 else: - period = int(period) + period = int(period_str) - if entity_ids := request.query.get("entity"): + if entity_ids_str := request.query.get("entity"): try: - entity_ids = cv.entity_ids(entity_ids) + entity_ids = cv.entity_ids(entity_ids_str) except vol.Invalid: raise InvalidEntityFormatError( - f"Invalid entity id(s) encountered: {entity_ids}. " + f"Invalid entity id(s) encountered: {entity_ids_str}. " "Format should be ." ) from vol.Invalid + else: + entity_ids = None - if (end_time := request.query.get("end_time")) is None: - start_day = dt_util.as_utc(datetime) - timedelta(days=period - 1) + if (end_time_str := request.query.get("end_time")) is None: + start_day = dt_util.as_utc(datetime_dt) - timedelta(days=period - 1) end_day = start_day + timedelta(days=period) else: - start_day = datetime - if (end_day := dt_util.parse_datetime(end_time)) is None: + start_day = datetime_dt + if (end_day_dt := dt_util.parse_datetime(end_time_str)) is None: return self.json_message("Invalid end_time", HTTPStatus.BAD_REQUEST) + end_day = end_day_dt hass = request.app["hass"] @@ -248,7 +286,7 @@ class LogbookView(HomeAssistantView): "Can't combine entity with context_id", HTTPStatus.BAD_REQUEST ) - def json_events(): + def json_events() -> web.Response: """Fetch events and generate JSON.""" return self.json( _get_events( @@ -263,10 +301,17 @@ class LogbookView(HomeAssistantView): ) ) - return await get_instance(hass).async_add_executor_job(json_events) + return cast( + web.Response, await get_instance(hass).async_add_executor_job(json_events) + ) -def humanify(hass, events, entity_attr_cache, context_lookup): +def humanify( + hass: HomeAssistant, + events: Generator[LazyEventPartialState, None, None], + entity_attr_cache: EntityAttributeCache, + context_lookup: dict[str | None, LazyEventPartialState | None], +) -> Generator[dict[str, Any], None, None]: """Generate a converted list of events into Entry objects. Will try to group events if possible: @@ -320,6 +365,7 @@ def humanify(hass, events, entity_attr_cache, context_lookup): # Skip all but the last sensor state continue + assert entity_id is not None data = { "when": event.time_fired_isoformat, "name": _entity_name_from_event( @@ -420,27 +466,28 @@ def humanify(hass, events, entity_attr_cache, context_lookup): def _get_events( - hass, - start_day, - end_day, - entity_ids=None, - filters=None, - entities_filter=None, - entity_matches_only=False, - context_id=None, -): + hass: HomeAssistant, + start_day: dt, + end_day: dt, + entity_ids: list[str] | None = None, + filters: Filters | None = None, + entities_filter: EntityFilter | Callable[[str], bool] | None = None, + entity_matches_only: bool = False, + context_id: str | None = None, +) -> list[dict[str, Any]]: """Get events for a period of time.""" assert not ( entity_ids and context_id ), "can't pass in both entity_ids and context_id" entity_attr_cache = EntityAttributeCache(hass) - context_lookup = {None: None} + event_data_cache: dict[str, dict[str, Any]] = {} + context_lookup: dict[str | None, LazyEventPartialState | None] = {None: None} - def yield_events(query): + def yield_events(query: Query) -> Generator[LazyEventPartialState, None, None]: """Yield Events that are not filtered away.""" for row in query.yield_per(1000): - event = LazyEventPartialState(row) + event = LazyEventPartialState(row, event_data_cache) context_lookup.setdefault(event.context_id, event) if event.event_type == EVENT_CALL_SERVICE: continue @@ -482,7 +529,7 @@ def _get_events( ) if filters: query = query.filter( - filters.entity_filter() | (Events.event_type != EVENT_STATE_CHANGED) + filters.entity_filter() | (Events.event_type != EVENT_STATE_CHANGED) # type: ignore[no-untyped-call] ) if context_id is not None: @@ -634,7 +681,11 @@ def _apply_event_entity_id_matchers( ) -def _keep_event(hass, event, entities_filter): +def _keep_event( + hass: HomeAssistant, + event: LazyEventPartialState, + entities_filter: EntityFilter | Callable[[str], bool] | None = None, +) -> bool: if event.event_type in HOMEASSISTANT_EVENTS: return entities_filter is None or entities_filter(HA_DOMAIN_ENTITY_ID) @@ -648,15 +699,21 @@ def _keep_event(hass, event, entities_filter): else: domain = event.data_domain - if domain is None: - return False - - return entities_filter is None or entities_filter(f"{domain}._") + return domain is not None and ( + entities_filter is None or entities_filter(f"{domain}._") + ) def _augment_data_with_context( - data, entity_id, event, context_lookup, entity_attr_cache, external_events -): + data: dict[str, Any], + entity_id: str | None, + event: LazyEventPartialState, + context_lookup: dict[str | None, LazyEventPartialState | None], + entity_attr_cache: EntityAttributeCache, + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ], +) -> None: if not (context_event := context_lookup.get(event.context_id)): return @@ -691,18 +748,14 @@ def _augment_data_with_context( data["context_event_type"] = event_type return - if not entity_id: + if not entity_id or context_event == event: return - attr_entity_id = event_data.get(ATTR_ENTITY_ID) - if not isinstance(attr_entity_id, str) or ( + if (attr_entity_id := context_event.data_entity_id) is None or ( event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id ): return - if context_event == event: - return - data["context_entity_id"] = attr_entity_id data["context_entity_id_name"] = _entity_name_from_event( attr_entity_id, context_event, entity_attr_cache @@ -716,7 +769,11 @@ def _augment_data_with_context( data["context_name"] = name -def _entity_name_from_event(entity_id, event, entity_attr_cache): +def _entity_name_from_event( + entity_id: str, + event: LazyEventPartialState, + entity_attr_cache: EntityAttributeCache, +) -> str: """Extract the entity name from the event using the cache if possible.""" return entity_attr_cache.get( entity_id, ATTR_FRIENDLY_NAME, event @@ -739,85 +796,87 @@ class LazyEventPartialState: "context_user_id", "context_parent_id", "time_fired_minute", + "_event_data_cache", ] - def __init__(self, row): + def __init__( + self, + row: Row, + event_data_cache: dict[str, dict[str, Any]], + ) -> None: """Init the lazy event.""" self._row = row - self._event_data = None - self._time_fired_isoformat = None - self._attributes = None - self._domain = None - self.event_type = self._row.event_type - self.entity_id = self._row.entity_id + self._event_data: dict[str, Any] | None = None + self._time_fired_isoformat: dt | None = None + self._domain: str | None = None + self.event_type: str = self._row.event_type + self.entity_id: str | None = self._row.entity_id self.state = self._row.state - self.context_id = self._row.context_id - self.context_user_id = self._row.context_user_id - self.context_parent_id = self._row.context_parent_id - self.time_fired_minute = self._row.time_fired.minute + self.context_id: str | None = self._row.context_id + self.context_user_id: str | None = self._row.context_user_id + self.context_parent_id: str | None = self._row.context_parent_id + self.time_fired_minute: int = self._row.time_fired.minute + self._event_data_cache = event_data_cache @property - def domain(self): + def domain(self) -> str | None: """Return the domain for the state.""" if self._domain is None: + assert self.entity_id is not None self._domain = split_entity_id(self.entity_id)[0] return self._domain @property - def attributes_icon(self): + def attributes_icon(self) -> str | None: """Extract the icon from the decoded attributes or json.""" - if self._attributes: - return self._attributes.get(ATTR_ICON) result = ICON_JSON_EXTRACT.search( - self._row.shared_attrs or self._row.attributes + self._row.shared_attrs or self._row.attributes or "" ) - return result and result.group(1) + return result.group(1) if result else None @property - def data_entity_id(self): + def data_entity_id(self) -> str | None: """Extract the entity id from the decoded data or json.""" if self._event_data: return self._event_data.get(ATTR_ENTITY_ID) result = ENTITY_ID_JSON_EXTRACT.search(self._row.event_data) - return result and result.group(1) + return result.group(1) if result else None @property - def data_domain(self): + def attributes_friendly_name(self) -> str | None: + """Extract the friendly name from the decoded attributes or json.""" + result = FRIENDLY_NAME_JSON_EXTRACT.search( + self._row.shared_attrs or self._row.attributes or "" + ) + return result.group(1) if result else None + + @property + def data_domain(self) -> str | None: """Extract the domain from the decoded data or json.""" - if self._event_data: - return self._event_data.get(ATTR_DOMAIN) - result = DOMAIN_JSON_EXTRACT.search(self._row.event_data) - return result and result.group(1) + return result.group(1) if result else None @property - def attributes(self): - """State attributes.""" - if self._attributes is None: - source = self._row.shared_attrs or self._row.attributes - if source == EMPTY_JSON_OBJECT or source is None: - self._attributes = {} - else: - self._attributes = json.loads(source) - return self._attributes - - @property - def data(self): + def data(self) -> dict[str, Any]: """Event data.""" if not self._event_data: - if self._row.event_data == EMPTY_JSON_OBJECT: - self._event_data = {} + source: str = self._row.event_data + if event_data := self._event_data_cache.get(source): + self._event_data = event_data else: - self._event_data = json.loads(self._row.event_data) + self._event_data = self._event_data_cache[source] = cast( + dict[str, Any], json.loads(source) + ) return self._event_data @property - def time_fired_isoformat(self): + def time_fired_isoformat(self) -> dt | None: """Time event was fired in utc isoformat.""" if not self._time_fired_isoformat: - self._time_fired_isoformat = process_timestamp_to_utc_isoformat( - self._row.time_fired or dt_util.utcnow() + self._time_fired_isoformat = ( + process_timestamp_to_utc_isoformat(self._row.time_fired) + or dt_util.utcnow() ) return self._time_fired_isoformat @@ -841,15 +900,19 @@ class EntityAttributeCache: if attribute in self._cache[entity_id]: return self._cache[entity_id][attribute] else: - self._cache[entity_id] = {} + cache = self._cache[entity_id] = {} if current_state := self._hass.states.get(entity_id): # Try the current state as its faster than decoding the # attributes - self._cache[entity_id][attribute] = current_state.attributes.get(attribute) + cache[attribute] = current_state.attributes.get(attribute) else: # If the entity has been removed, decode the attributes # instead - self._cache[entity_id][attribute] = event.attributes.get(attribute) + if attribute != ATTR_FRIENDLY_NAME: + raise ValueError( + f"{attribute} is not supported by {self.__class__.__name__}" + ) + cache[attribute] = event.attributes_friendly_name - return self._cache[entity_id][attribute] + return cache[attribute] diff --git a/homeassistant/scripts/benchmark/__init__.py b/homeassistant/scripts/benchmark/__init__.py index 35df75d5a1c..2062ef231b9 100644 --- a/homeassistant/scripts/benchmark/__init__.py +++ b/homeassistant/scripts/benchmark/__init__.py @@ -404,4 +404,4 @@ def _create_state_changed_event_from_old_new( # pylint: disable=import-outside-toplevel from homeassistant.components import logbook - return logbook.LazyEventPartialState(row) + return logbook.LazyEventPartialState(row, {}) diff --git a/mypy.ini b/mypy.ini index c35e4d471c8..a4f7ef80d30 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1292,6 +1292,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.logbook.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.lookin.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index e1975b52e3c..68b9169224e 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -113,6 +113,35 @@ async def test_service_call_create_logbook_entry(hass_): assert last_call.data.get(logbook.ATTR_DOMAIN) == "logbook" +async def test_service_call_create_logbook_entry_invalid_entity_id(hass, recorder_mock): + """Test if service call create log book entry with an invalid entity id.""" + await async_setup_component(hass, "logbook", {}) + await hass.async_block_till_done() + hass.bus.async_fire( + logbook.EVENT_LOGBOOK_ENTRY, + { + logbook.ATTR_NAME: "Alarm", + logbook.ATTR_MESSAGE: "is triggered", + logbook.ATTR_DOMAIN: "switch", + logbook.ATTR_ENTITY_ID: 1234, + }, + ) + await async_wait_recording_done(hass) + + events = list( + logbook._get_events( + hass, + dt_util.utcnow() - timedelta(hours=1), + dt_util.utcnow() + timedelta(hours=1), + ) + ) + assert len(events) == 1 + assert events[0][logbook.ATTR_DOMAIN] == "switch" + assert events[0][logbook.ATTR_NAME] == "Alarm" + assert events[0][logbook.ATTR_ENTITY_ID] == 1234 + assert events[0][logbook.ATTR_MESSAGE] == "is triggered" + + async def test_service_call_create_log_book_entry_no_message(hass_): """Test if service call create log book entry without message.""" calls = async_capture_events(hass_, logbook.EVENT_LOGBOOK_ENTRY) @@ -175,6 +204,14 @@ def test_home_assistant_start_stop_grouped(hass_): ) +def test_unsupported_attributes_in_cache_throws(hass): + """Test unsupported attributes in cache.""" + entity_attr_cache = logbook.EntityAttributeCache(hass) + event = MockLazyEventPartialState(EVENT_STATE_CHANGED) + with pytest.raises(ValueError): + entity_attr_cache.get("sensor.xyz", "not_supported", event) + + def test_home_assistant_start(hass_): """Test if HA start is not filtered or converted into a restart.""" entity_id = "switch.bla" @@ -295,7 +332,7 @@ def create_state_changed_event_from_old_new( row.context_parent_id = None row.old_state_id = old_state and 1 row.state_id = new_state and 1 - return logbook.LazyEventPartialState(row) + return logbook.LazyEventPartialState(row, {}) async def test_logbook_view(hass, hass_client, recorder_mock): @@ -307,6 +344,26 @@ async def test_logbook_view(hass, hass_client, recorder_mock): assert response.status == HTTPStatus.OK +async def test_logbook_view_invalid_start_date_time(hass, hass_client, recorder_mock): + """Test the logbook view with an invalid date time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + client = await hass_client() + response = await client.get("/api/logbook/INVALID") + assert response.status == HTTPStatus.BAD_REQUEST + + +async def test_logbook_view_invalid_end_date_time(hass, hass_client, recorder_mock): + """Test the logbook view.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + client = await hass_client() + response = await client.get( + f"/api/logbook/{dt_util.utcnow().isoformat()}?end_time=INVALID" + ) + assert response.status == HTTPStatus.BAD_REQUEST + + async def test_logbook_view_period_entity(hass, hass_client, recorder_mock, set_utc): """Test the logbook view with period and entity.""" await async_setup_component(hass, "logbook", {}) @@ -1437,6 +1494,34 @@ async def test_icon_and_state(hass, hass_client, recorder_mock): assert response_json[2]["state"] == STATE_OFF +async def test_fire_logbook_entries(hass, hass_client, recorder_mock): + """Test many logbook entry calls.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + for _ in range(10): + hass.bus.async_fire( + logbook.EVENT_LOGBOOK_ENTRY, + { + logbook.ATTR_NAME: "Alarm", + logbook.ATTR_MESSAGE: "is triggered", + logbook.ATTR_DOMAIN: "switch", + logbook.ATTR_ENTITY_ID: "sensor.xyz", + }, + ) + hass.bus.async_fire( + logbook.EVENT_LOGBOOK_ENTRY, + {}, + ) + await async_wait_recording_done(hass) + + client = await hass_client() + response_json = await _async_fetch_logbook(client) + + # The empty events should be skipped + assert len(response_json) == 10 + + async def test_exclude_events_domain(hass, hass_client, recorder_mock): """Test if events are filtered if domain is excluded in config.""" entity_id = "switch.bla" From c23866e5e59cfec3085faa37a0cc5a56135a7064 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 21:01:17 -0500 Subject: [PATCH 139/930] De-duplicate event data into a new event_data table (#71135) --- homeassistant/components/logbook/__init__.py | 59 ++-- homeassistant/components/recorder/__init__.py | 68 +++- .../components/recorder/migration.py | 6 +- homeassistant/components/recorder/models.py | 68 +++- homeassistant/components/recorder/purge.py | 322 +++++++++++++++++- tests/components/recorder/test_init.py | 53 ++- tests/components/recorder/test_models.py | 11 +- 7 files changed, 526 insertions(+), 61 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index b136c7330ac..96e6b975e32 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -28,6 +28,7 @@ from homeassistant.components.history import ( from homeassistant.components.http import HomeAssistantView from homeassistant.components.recorder import get_instance from homeassistant.components.recorder.models import ( + EventData, Events, StateAttributes, States, @@ -512,6 +513,7 @@ def _get_events( # When entity_matches_only is provided, contexts and events that do not # contain the entity_ids are not included in the logbook response. query = _apply_event_entity_id_matchers(query, entity_ids) + query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) query = query.union_all( _generate_states_query( @@ -535,6 +537,8 @@ def _get_events( if context_id is not None: query = query.filter(Events.context_id == context_id) + query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) + query = query.order_by(Events.time_fired) return list( @@ -545,6 +549,18 @@ def _get_events( def _generate_events_query(session: Session) -> Query: return session.query( *EVENT_COLUMNS, + EventData.shared_data, + States.state, + States.entity_id, + States.attributes, + StateAttributes.shared_attrs, + ) + + +def _generate_events_query_without_data(session: Session) -> Query: + return session.query( + *EVENT_COLUMNS, + literal(value=None, type_=sqlalchemy.Text).label("shared_data"), States.state, States.entity_id, States.attributes, @@ -555,6 +571,7 @@ def _generate_events_query(session: Session) -> Query: def _generate_events_query_without_states(session: Session) -> Query: return session.query( *EVENT_COLUMNS, + EventData.shared_data, literal(value=None, type_=sqlalchemy.String).label("state"), literal(value=None, type_=sqlalchemy.String).label("entity_id"), literal(value=None, type_=sqlalchemy.Text).label("attributes"), @@ -570,7 +587,7 @@ def _generate_states_query( entity_ids: Iterable[str], ) -> Query: return ( - _generate_events_query(session) + _generate_events_query_without_data(session) .outerjoin(Events, (States.event_id == Events.event_id)) .outerjoin(old_state, (States.old_state_id == old_state.state_id)) .filter(_missing_state_matcher(old_state)) @@ -671,14 +688,12 @@ def _apply_event_types_filter( def _apply_event_entity_id_matchers( events_query: Query, entity_ids: Iterable[str] ) -> Query: - return events_query.filter( - sqlalchemy.or_( - *( - Events.event_data.like(ENTITY_ID_JSON_TEMPLATE.format(entity_id)) - for entity_id in entity_ids - ) - ) - ) + ors = [] + for entity_id in entity_ids: + like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) + ors.append(Events.event_data.like(like)) + ors.append(EventData.shared_data.like(like)) + return events_query.filter(sqlalchemy.or_(*ors)) def _keep_event( @@ -840,7 +855,17 @@ class LazyEventPartialState: if self._event_data: return self._event_data.get(ATTR_ENTITY_ID) - result = ENTITY_ID_JSON_EXTRACT.search(self._row.event_data) + result = ENTITY_ID_JSON_EXTRACT.search( + self._row.shared_data or self._row.event_data or "" + ) + return result.group(1) if result else None + + @property + def data_domain(self) -> str | None: + """Extract the domain from the decoded data or json.""" + result = DOMAIN_JSON_EXTRACT.search( + self._row.shared_data or self._row.event_data or "" + ) return result.group(1) if result else None @property @@ -851,18 +876,14 @@ class LazyEventPartialState: ) return result.group(1) if result else None - @property - def data_domain(self) -> str | None: - """Extract the domain from the decoded data or json.""" - result = DOMAIN_JSON_EXTRACT.search(self._row.event_data) - return result.group(1) if result else None - @property def data(self) -> dict[str, Any]: """Event data.""" - if not self._event_data: - source: str = self._row.event_data - if event_data := self._event_data_cache.get(source): + if self._event_data is None: + source: str = self._row.shared_data or self._row.event_data + if not source: + self._event_data = {} + elif event_data := self._event_data_cache.get(source): self._event_data = event_data else: self._event_data = self._event_data_cache[source] = cast( diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index ef53721efd2..d190ebd0a99 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -80,6 +80,7 @@ from .const import ( from .executor import DBInterruptibleThreadPoolExecutor from .models import ( Base, + EventData, Events, StateAttributes, States, @@ -158,6 +159,7 @@ EXPIRE_AFTER_COMMITS = 120 # - How frequently states with overlapping attributes will change # - How much memory our low end hardware has STATE_ATTRIBUTES_ID_CACHE_SIZE = 2048 +EVENT_DATA_ID_CACHE_SIZE = 2048 SHUTDOWN_TASK = object() @@ -639,10 +641,13 @@ class Recorder(threading.Thread): self._commits_without_expire = 0 self._old_states: dict[str, States] = {} self._state_attributes_ids: LRU = LRU(STATE_ATTRIBUTES_ID_CACHE_SIZE) + self._event_data_ids: LRU = LRU(EVENT_DATA_ID_CACHE_SIZE) self._pending_state_attributes: dict[str, StateAttributes] = {} + self._pending_event_data: dict[str, EventData] = {} self._pending_expunge: list[States] = [] self._bakery = bakery self._find_shared_attr_query: Query | None = None + self._find_shared_data_query: Query | None = None self.event_session: Session | None = None self.get_session: Callable[[], Session] | None = None self._completed_first_database_setup: bool | None = None @@ -1162,17 +1167,61 @@ class Recorder(threading.Thread): return cast(int, attributes[0]) return None + def _find_shared_data_in_db(self, data_hash: int, shared_data: str) -> int | None: + """Find shared event data in the db from the hash and shared_attrs.""" + # + # Avoid the event session being flushed since it will + # commit all the pending events and states to the database. + # + # The lookup has already have checked to see if the data is cached + # or going to be written in the next commit so there is no + # need to flush before checking the database. + # + assert self.event_session is not None + if self._find_shared_data_query is None: + self._find_shared_data_query = self._bakery( + lambda session: session.query(EventData.data_id) + .filter(EventData.hash == bindparam("data_hash")) + .filter(EventData.shared_data == bindparam("shared_data")) + ) + with self.event_session.no_autoflush: + if ( + data_id := self._find_shared_data_query(self.event_session) + .params(data_hash=data_hash, shared_data=shared_data) + .first() + ): + return cast(int, data_id[0]) + return None + def _process_event_into_session(self, event: Event) -> None: assert self.event_session is not None + dbevent = Events.from_event(event) - try: - if event.event_type == EVENT_STATE_CHANGED: - dbevent = Events.from_event(event, event_data=None) + if event.event_type != EVENT_STATE_CHANGED and event.data: + try: + shared_data = EventData.shared_data_from_event(event) + except (TypeError, ValueError): + _LOGGER.warning("Event is not JSON serializable: %s", event) + return + + # Matching attributes found in the pending commit + if pending_event_data := self._pending_event_data.get(shared_data): + dbevent.event_data_rel = pending_event_data + # Matching attributes id found in the cache + elif data_id := self._event_data_ids.get(shared_data): + dbevent.data_id = data_id else: - dbevent = Events.from_event(event) - except (TypeError, ValueError): - _LOGGER.warning("Event is not JSON serializable: %s", event) - return + data_hash = EventData.hash_shared_data(shared_data) + # Matching attributes found in the database + if data_id := self._find_shared_data_in_db(data_hash, shared_data): + self._event_data_ids[shared_data] = dbevent.data_id = data_id + # No matching attributes found, save them in the DB + else: + dbevent_data = EventData(shared_data=shared_data, hash=data_hash) + dbevent.event_data_rel = self._pending_event_data[ + shared_data + ] = dbevent_data + self.event_session.add(dbevent_data) self.event_session.add(dbevent) if event.event_type != EVENT_STATE_CHANGED: @@ -1286,6 +1335,9 @@ class Recorder(threading.Thread): state_attr.shared_attrs ] = state_attr.attributes_id self._pending_state_attributes = {} + for event_data in self._pending_event_data.values(): + self._event_data_ids[event_data.shared_data] = event_data.data_id + self._pending_event_data = {} # Expire is an expensive operation (frequently more expensive # than the flush and commit itself) so we only @@ -1307,7 +1359,9 @@ class Recorder(threading.Thread): """Close the event session.""" self._old_states = {} self._state_attributes_ids = {} + self._event_data_ids = {} self._pending_state_attributes = {} + self._pending_event_data = {} if not self.event_session: return diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 94614fe8cff..2ef51824737 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -380,6 +380,8 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 """Perform operations to bring schema up to date.""" engine = instance.engine dialect = engine.dialect.name + big_int = "INTEGER(20)" if dialect == "mysql" else "INTEGER" + if new_version == 1: _create_index(instance, "events", "ix_events_time_fired") elif new_version == 2: @@ -643,11 +645,13 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 "ix_statistics_short_term_statistic_id_start", ) elif new_version == 25: - big_int = "INTEGER(20)" if dialect == "mysql" else "INTEGER" _add_columns(instance, "states", [f"attributes_id {big_int}"]) _create_index(instance, "states", "ix_states_attributes_id") elif new_version == 26: _create_index(instance, "statistics_runs", "ix_statistics_runs_start") + elif new_version == 27: + _add_columns(instance, "events", [f"data_id {big_int}"]) + _create_index(instance, "events", "ix_events_data_id") else: raise ValueError(f"No schema migration defined for version {new_version}") diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 5b402dec7a3..212c0c7e7d4 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -35,7 +35,6 @@ from homeassistant.const import ( MAX_LENGTH_STATE_STATE, ) from homeassistant.core import Context, Event, EventOrigin, State, split_entity_id -from homeassistant.helpers.typing import UNDEFINED, UndefinedType import homeassistant.util.dt as dt_util from .const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP @@ -44,13 +43,14 @@ from .const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP # pylint: disable=invalid-name Base = declarative_base() -SCHEMA_VERSION = 26 +SCHEMA_VERSION = 27 _LOGGER = logging.getLogger(__name__) DB_TIMEZONE = "+00:00" TABLE_EVENTS = "events" +TABLE_EVENT_DATA = "event_data" TABLE_STATES = "states" TABLE_STATE_ATTRIBUTES = "state_attributes" TABLE_RECORDER_RUNS = "recorder_runs" @@ -60,6 +60,9 @@ TABLE_STATISTICS_META = "statistics_meta" TABLE_STATISTICS_RUNS = "statistics_runs" TABLE_STATISTICS_SHORT_TERM = "statistics_short_term" +# Only add TABLE_STATE_ATTRIBUTES and TABLE_EVENT_DATA +# to the below list once we want to check for their +# instance in the sanity check. ALL_TABLES = [ TABLE_STATES, TABLE_EVENTS, @@ -103,24 +106,24 @@ class Events(Base): # type: ignore[misc,valid-type] context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + data_id = Column(Integer, ForeignKey("event_data.data_id"), index=True) + event_data_rel = relationship("EventData") def __repr__(self) -> str: """Return string representation of instance for debugging.""" return ( f"" + f", data_id={self.data_id})>" ) @staticmethod - def from_event( - event: Event, event_data: UndefinedType | None = UNDEFINED - ) -> Events: + def from_event(event: Event) -> Events: """Create an event database object from a native event.""" return Events( event_type=event.event_type, - event_data=JSON_DUMP(event.data) if event_data is UNDEFINED else event_data, + event_data=None, origin=str(event.origin.value), time_fired=event.time_fired, context_id=event.context.id, @@ -138,7 +141,7 @@ class Events(Base): # type: ignore[misc,valid-type] try: return Event( self.event_type, - json.loads(self.event_data), + json.loads(self.event_data) if self.event_data else {}, EventOrigin(self.origin), process_timestamp(self.time_fired), context=context, @@ -149,6 +152,53 @@ class Events(Base): # type: ignore[misc,valid-type] return None +class EventData(Base): # type: ignore[misc,valid-type] + """Event data history.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_EVENT_DATA + data_id = Column(Integer, Identity(), primary_key=True) + hash = Column(BigInteger, index=True) + # Note that this is not named attributes to avoid confusion with the states table + shared_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event: Event) -> EventData: + """Create object from an event.""" + shared_data = JSON_DUMP(event.data) + return EventData( + shared_data=shared_data, hash=EventData.hash_shared_data(shared_data) + ) + + @staticmethod + def shared_data_from_event(event: Event) -> str: + """Create shared_attrs from an event.""" + return JSON_DUMP(event.data) + + @staticmethod + def hash_shared_data(shared_data: str) -> int: + """Return the hash of json encoded shared data.""" + return cast(int, fnv1a_32(shared_data.encode("utf-8"))) + + def to_native(self) -> dict[str, Any]: + """Convert to an HA state object.""" + try: + return cast(dict[str, Any], json.loads(self.shared_data)) + except ValueError: + _LOGGER.exception("Error converting row to event data: %s", self) + return {} + + class States(Base): # type: ignore[misc,valid-type] """State change history.""" diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index d4061a69bab..4aad3a28a88 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -17,6 +17,7 @@ from homeassistant.const import EVENT_STATE_CHANGED from .const import MAX_ROWS_TO_PURGE from .models import ( + EventData, Events, RecorderRuns, StateAttributes, @@ -53,7 +54,8 @@ def purge_old_data( event_ids, state_ids, attributes_ids, - ) = _select_event_state_and_attributes_ids_to_purge(session, purge_before) + data_ids, + ) = _select_event_state_attributes_ids_data_ids_to_purge(session, purge_before) statistics_runs = _select_statistics_runs_to_purge(session, purge_before) short_term_statistics = _select_short_term_statistics_to_purge( session, purge_before @@ -70,6 +72,11 @@ def purge_old_data( if event_ids: _purge_event_ids(session, event_ids) + if unused_data_ids_set := _select_unused_event_data_ids( + session, data_ids, using_sqlite + ): + _purge_event_data_ids(instance, session, unused_data_ids_set) + if statistics_runs: _purge_statistics_runs(session, statistics_runs) @@ -91,12 +98,14 @@ def purge_old_data( return True -def _select_event_state_and_attributes_ids_to_purge( +def _select_event_state_attributes_ids_data_ids_to_purge( session: Session, purge_before: datetime -) -> tuple[set[int], set[int], set[int]]: +) -> tuple[set[int], set[int], set[int], set[int]]: """Return a list of event, state, and attribute ids to purge.""" events = ( - session.query(Events.event_id, States.state_id, States.attributes_id) + session.query( + Events.event_id, Events.data_id, States.state_id, States.attributes_id + ) .outerjoin(States, Events.event_id == States.event_id) .filter(Events.time_fired < purge_before) .limit(MAX_ROWS_TO_PURGE) @@ -106,13 +115,16 @@ def _select_event_state_and_attributes_ids_to_purge( event_ids = set() state_ids = set() attributes_ids = set() + data_ids = set() for event in events: event_ids.add(event.event_id) if event.state_id: state_ids.add(event.state_id) if event.attributes_id: attributes_ids.add(event.attributes_id) - return event_ids, state_ids, attributes_ids + if event.data_id: + data_ids.add(event.data_id) + return event_ids, state_ids, attributes_ids, data_ids def _state_attrs_exist(attr: int | None) -> Select: @@ -400,6 +412,255 @@ def _select_unused_attributes_ids( return to_remove +def _event_data_id_exist(data_id: int | None) -> Select: + """Check if a event data id exists in the events table.""" + return select(func.min(Events.data_id)).where(Events.data_id == data_id) + + +def _generate_find_data_id_lambda( + id1: int, + id2: int | None, + id3: int | None, + id4: int | None, + id5: int | None, + id6: int | None, + id7: int | None, + id8: int | None, + id9: int | None, + id10: int | None, + id11: int | None, + id12: int | None, + id13: int | None, + id14: int | None, + id15: int | None, + id16: int | None, + id17: int | None, + id18: int | None, + id19: int | None, + id20: int | None, + id21: int | None, + id22: int | None, + id23: int | None, + id24: int | None, + id25: int | None, + id26: int | None, + id27: int | None, + id28: int | None, + id29: int | None, + id30: int | None, + id31: int | None, + id32: int | None, + id33: int | None, + id34: int | None, + id35: int | None, + id36: int | None, + id37: int | None, + id38: int | None, + id39: int | None, + id40: int | None, + id41: int | None, + id42: int | None, + id43: int | None, + id44: int | None, + id45: int | None, + id46: int | None, + id47: int | None, + id48: int | None, + id49: int | None, + id50: int | None, + id51: int | None, + id52: int | None, + id53: int | None, + id54: int | None, + id55: int | None, + id56: int | None, + id57: int | None, + id58: int | None, + id59: int | None, + id60: int | None, + id61: int | None, + id62: int | None, + id63: int | None, + id64: int | None, + id65: int | None, + id66: int | None, + id67: int | None, + id68: int | None, + id69: int | None, + id70: int | None, + id71: int | None, + id72: int | None, + id73: int | None, + id74: int | None, + id75: int | None, + id76: int | None, + id77: int | None, + id78: int | None, + id79: int | None, + id80: int | None, + id81: int | None, + id82: int | None, + id83: int | None, + id84: int | None, + id85: int | None, + id86: int | None, + id87: int | None, + id88: int | None, + id89: int | None, + id90: int | None, + id91: int | None, + id92: int | None, + id93: int | None, + id94: int | None, + id95: int | None, + id96: int | None, + id97: int | None, + id98: int | None, + id99: int | None, + id100: int | None, +) -> StatementLambdaElement: + """Generate the find event data select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _event_data_id_exist(id1), + _event_data_id_exist(id2), + _event_data_id_exist(id3), + _event_data_id_exist(id4), + _event_data_id_exist(id5), + _event_data_id_exist(id6), + _event_data_id_exist(id7), + _event_data_id_exist(id8), + _event_data_id_exist(id9), + _event_data_id_exist(id10), + _event_data_id_exist(id11), + _event_data_id_exist(id12), + _event_data_id_exist(id13), + _event_data_id_exist(id14), + _event_data_id_exist(id15), + _event_data_id_exist(id16), + _event_data_id_exist(id17), + _event_data_id_exist(id18), + _event_data_id_exist(id19), + _event_data_id_exist(id20), + _event_data_id_exist(id21), + _event_data_id_exist(id22), + _event_data_id_exist(id23), + _event_data_id_exist(id24), + _event_data_id_exist(id25), + _event_data_id_exist(id26), + _event_data_id_exist(id27), + _event_data_id_exist(id28), + _event_data_id_exist(id29), + _event_data_id_exist(id30), + _event_data_id_exist(id31), + _event_data_id_exist(id32), + _event_data_id_exist(id33), + _event_data_id_exist(id34), + _event_data_id_exist(id35), + _event_data_id_exist(id36), + _event_data_id_exist(id37), + _event_data_id_exist(id38), + _event_data_id_exist(id39), + _event_data_id_exist(id40), + _event_data_id_exist(id41), + _event_data_id_exist(id42), + _event_data_id_exist(id43), + _event_data_id_exist(id44), + _event_data_id_exist(id45), + _event_data_id_exist(id46), + _event_data_id_exist(id47), + _event_data_id_exist(id48), + _event_data_id_exist(id49), + _event_data_id_exist(id50), + _event_data_id_exist(id51), + _event_data_id_exist(id52), + _event_data_id_exist(id53), + _event_data_id_exist(id54), + _event_data_id_exist(id55), + _event_data_id_exist(id56), + _event_data_id_exist(id57), + _event_data_id_exist(id58), + _event_data_id_exist(id59), + _event_data_id_exist(id60), + _event_data_id_exist(id61), + _event_data_id_exist(id62), + _event_data_id_exist(id63), + _event_data_id_exist(id64), + _event_data_id_exist(id65), + _event_data_id_exist(id66), + _event_data_id_exist(id67), + _event_data_id_exist(id68), + _event_data_id_exist(id69), + _event_data_id_exist(id70), + _event_data_id_exist(id71), + _event_data_id_exist(id72), + _event_data_id_exist(id73), + _event_data_id_exist(id74), + _event_data_id_exist(id75), + _event_data_id_exist(id76), + _event_data_id_exist(id77), + _event_data_id_exist(id78), + _event_data_id_exist(id79), + _event_data_id_exist(id80), + _event_data_id_exist(id81), + _event_data_id_exist(id82), + _event_data_id_exist(id83), + _event_data_id_exist(id84), + _event_data_id_exist(id85), + _event_data_id_exist(id86), + _event_data_id_exist(id87), + _event_data_id_exist(id88), + _event_data_id_exist(id89), + _event_data_id_exist(id90), + _event_data_id_exist(id91), + _event_data_id_exist(id92), + _event_data_id_exist(id93), + _event_data_id_exist(id94), + _event_data_id_exist(id95), + _event_data_id_exist(id96), + _event_data_id_exist(id97), + _event_data_id_exist(id98), + _event_data_id_exist(id99), + _event_data_id_exist(id100), + ) + ) + + +def _select_unused_event_data_ids( + session: Session, data_ids: set[int], using_sqlite: bool +) -> set[int]: + """Return a set of event data ids that are not used by any events in the database.""" + if not data_ids: + return set() + + # See _select_unused_attributes_ids for why this function + # branches for non-sqlite databases. + if using_sqlite: + seen_ids = { + state[0] + for state in session.query(distinct(Events.data_id)) + .filter(Events.data_id.in_(data_ids)) + .all() + } + else: + seen_ids = set() + groups = [iter(data_ids)] * 100 + for data_ids_group in zip_longest(*groups, fillvalue=None): + seen_ids |= { + state[0] + for state in session.execute( + _generate_find_data_id_lambda(*data_ids_group) + ).all() + if state[0] is not None + } + to_remove = data_ids - seen_ids + _LOGGER.debug("Selected %s shared event data to remove", len(to_remove)) + return to_remove + + def _select_statistics_runs_to_purge( session: Session, purge_before: datetime ) -> list[int]: @@ -477,6 +738,21 @@ def _evict_purged_states_from_old_states_cache( old_states.pop(old_state_reversed[purged_state_id], None) +def _evict_purged_data_from_data_cache( + instance: Recorder, purged_data_ids: set[int] +) -> None: + """Evict purged data ids from the data ids cache.""" + # Make a map from data_id to the data json + event_data_ids = instance._event_data_ids # pylint: disable=protected-access + event_data_ids_reversed = { + data_id: data for data, data_id in event_data_ids.items() + } + + # Evict any purged data from the event_data_ids cache + for purged_attribute_id in purged_data_ids.intersection(event_data_ids_reversed): + event_data_ids.pop(event_data_ids_reversed[purged_attribute_id], None) + + def _evict_purged_attributes_from_attributes_cache( instance: Recorder, purged_attributes_ids: set[int] ) -> None: @@ -515,6 +791,22 @@ def _purge_attributes_ids( _evict_purged_attributes_from_attributes_cache(instance, attributes_ids) +def _purge_event_data_ids( + instance: Recorder, session: Session, data_ids: set[int] +) -> None: + """Delete old event data ids.""" + + deleted_rows = ( + session.query(EventData) + .filter(EventData.data_id.in_(data_ids)) + .delete(synchronize_session=False) + ) + _LOGGER.debug("Deleted %s data events", deleted_rows) + + # Evict any entries in the event_data_ids cache referring to a purged state + _evict_purged_data_from_data_cache(instance, data_ids) + + def _purge_statistics_runs(session: Session, statistics_runs: list[int]) -> None: """Delete by run_id.""" deleted_rows = ( @@ -623,15 +915,15 @@ def _purge_filtered_events( instance: Recorder, session: Session, excluded_event_types: list[str] ) -> None: """Remove filtered events and linked states.""" - events: list[Events] = ( - session.query(Events.event_id) - .filter(Events.event_type.in_(excluded_event_types)) - .limit(MAX_ROWS_TO_PURGE) - .all() + using_sqlite = instance.using_sqlite() + event_ids, data_ids = zip( + *( + session.query(Events.event_id, Events.data_id) + .filter(Events.event_type.in_(excluded_event_types)) + .limit(MAX_ROWS_TO_PURGE) + .all() + ) ) - event_ids: list[int] = [ - event.event_id for event in events if event.event_id is not None - ] _LOGGER.debug( "Selected %s event_ids to remove that should be filtered", len(event_ids) ) @@ -641,6 +933,10 @@ def _purge_filtered_events( state_ids: set[int] = {state.state_id for state in states} _purge_state_ids(instance, session, state_ids) _purge_event_ids(session, event_ids) + if unused_data_ids_set := _select_unused_event_data_ids( + session, set(data_ids), using_sqlite + ): + _purge_event_data_ids(instance, session, unused_data_ids_set) if EVENT_STATE_CHANGED in excluded_event_types: session.query(StateAttributes).delete(synchronize_session=False) instance._state_attributes_ids = {} # pylint: disable=protected-access diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 77bd3992e72..c3d22255ccc 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -6,6 +6,7 @@ import asyncio from datetime import datetime, timedelta import sqlite3 import threading +from typing import cast from unittest.mock import Mock, patch import pytest @@ -31,6 +32,7 @@ from homeassistant.components.recorder import ( ) from homeassistant.components.recorder.const import DATA_INSTANCE from homeassistant.components.recorder.models import ( + EventData, Events, RecorderRuns, StateAttributes, @@ -47,7 +49,7 @@ from homeassistant.const import ( STATE_LOCKED, STATE_UNLOCKED, ) -from homeassistant.core import Context, CoreState, HomeAssistant, callback +from homeassistant.core import Context, CoreState, Event, HomeAssistant, callback from homeassistant.setup import async_setup_component, setup_component from homeassistant.util import dt as dt_util @@ -363,14 +365,25 @@ def test_saving_event(hass, hass_recorder): wait_recording_done(hass) assert len(events) == 1 - event = events[0] + event: Event = events[0] hass.data[DATA_INSTANCE].block_till_done() + events: list[Event] = [] with session_scope(hass=hass) as session: - db_events = list(session.query(Events).filter_by(event_type=event_type)) - assert len(db_events) == 1 - db_event = db_events[0].to_native() + for select_event, event_data in ( + session.query(Events, EventData) + .filter_by(event_type=event_type) + .outerjoin(EventData, Events.data_id == EventData.data_id) + ): + select_event = cast(Events, select_event) + event_data = cast(EventData, event_data) + + native_event = select_event.to_native() + native_event.data = event_data.to_native() + events.append(native_event) + + db_event = events[0] assert event.event_type == db_event.event_type assert event.data == db_event.data @@ -427,7 +440,18 @@ def _add_events(hass, events): wait_recording_done(hass) with session_scope(hass=hass) as session: - return [ev.to_native() for ev in session.query(Events)] + events = [] + for event, event_data in session.query(Events, EventData).outerjoin( + EventData, Events.data_id == EventData.data_id + ): + event = cast(Events, event) + event_data = cast(EventData, event_data) + + native_event = event.to_native() + if event_data: + native_event.data = event_data.to_native() + events.append(native_event) + return events def _state_empty_context(hass, entity_id): @@ -1073,11 +1097,22 @@ def test_service_disable_events_not_recording(hass, hass_recorder): assert events[0] != events[1] assert events[0].data != events[1].data + db_events = [] with session_scope(hass=hass) as session: - db_events = list(session.query(Events).filter_by(event_type=event_type)) - assert len(db_events) == 1 - db_event = db_events[0].to_native() + for select_event, event_data in ( + session.query(Events, EventData) + .filter_by(event_type=event_type) + .outerjoin(EventData, Events.data_id == EventData.data_id) + ): + select_event = cast(Events, select_event) + event_data = cast(EventData, event_data) + native_event = select_event.to_native() + native_event.data = event_data.to_native() + db_events.append(native_event) + + assert len(db_events) == 1 + db_event = db_events[0] event = events[1] assert event.event_type == db_event.event_type diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index 8382afe4353..3bdb7992c7c 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -8,6 +8,7 @@ from sqlalchemy.orm import scoped_session, sessionmaker from homeassistant.components.recorder.models import ( Base, + EventData, Events, LazyState, RecorderRuns, @@ -25,7 +26,9 @@ from homeassistant.util import dt, dt as dt_util def test_from_event_to_db_event(): """Test converting event to db event.""" event = ha.Event("test_event", {"some_data": 15}) - assert event == Events.from_event(event).to_native() + db_event = Events.from_event(event) + db_event.event_data = EventData.from_event(event).shared_data + assert event == db_event.to_native() def test_from_event_to_db_state(): @@ -231,10 +234,12 @@ async def test_event_to_db_model(): event = ha.Event( "state_changed", {"some": "attr"}, ha.EventOrigin.local, dt_util.utcnow() ) - native = Events.from_event(event).to_native() + db_event = Events.from_event(event) + db_event.event_data = EventData.from_event(event).shared_data + native = db_event.to_native() assert native == event - native = Events.from_event(event, event_data="{}").to_native() + native = Events.from_event(event).to_native() event.data = {} assert native == event From b770ca319e640e2af24221dfe96d9af530c85083 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 21:04:05 -0500 Subject: [PATCH 140/930] Improve scrape performance by using lxml parser (#71087) * Improve scape performance by using lxml parser * load it * tweak * tweak * ensure libxml2 is installed in dev container --- Dockerfile.dev | 1 + homeassistant/components/scrape/manifest.json | 2 +- homeassistant/components/scrape/sensor.py | 2 +- requirements_all.txt | 3 +++ requirements_test_all.txt | 3 +++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Dockerfile.dev b/Dockerfile.dev index dc04efe56fb..322c63f53dd 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -18,6 +18,7 @@ RUN \ libavfilter-dev \ libpcap-dev \ libturbojpeg0 \ + libxml2 \ git \ cmake \ && apt-get clean \ diff --git a/homeassistant/components/scrape/manifest.json b/homeassistant/components/scrape/manifest.json index bf5865206e4..b1ccbb354a9 100644 --- a/homeassistant/components/scrape/manifest.json +++ b/homeassistant/components/scrape/manifest.json @@ -2,7 +2,7 @@ "domain": "scrape", "name": "Scrape", "documentation": "https://www.home-assistant.io/integrations/scrape", - "requirements": ["beautifulsoup4==4.11.1"], + "requirements": ["beautifulsoup4==4.11.1", "lxml==4.8.0"], "after_dependencies": ["rest"], "codeowners": ["@fabaff"], "iot_class": "cloud_polling" diff --git a/homeassistant/components/scrape/sensor.py b/homeassistant/components/scrape/sensor.py index 8f2a672ef06..e15f7c5ba97 100644 --- a/homeassistant/components/scrape/sensor.py +++ b/homeassistant/components/scrape/sensor.py @@ -154,7 +154,7 @@ class ScrapeSensor(SensorEntity): def _extract_value(self) -> Any: """Parse the html extraction in the executor.""" - raw_data = BeautifulSoup(self.rest.data, "html.parser") + raw_data = BeautifulSoup(self.rest.data, "lxml") _LOGGER.debug(raw_data) try: diff --git a/requirements_all.txt b/requirements_all.txt index a331efde6c2..dd808efa0bd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -974,6 +974,9 @@ lupupy==0.0.24 # homeassistant.components.lw12wifi lw12==0.9.2 +# homeassistant.components.scrape +lxml==4.8.0 + # homeassistant.components.nmap_tracker mac-vendor-lookup==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 67bba2141d5..26f1c86d1af 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -663,6 +663,9 @@ lru-dict==1.1.7 # homeassistant.components.luftdaten luftdaten==0.7.2 +# homeassistant.components.scrape +lxml==4.8.0 + # homeassistant.components.nmap_tracker mac-vendor-lookup==0.1.11 From 2a9f043039dc60fc25bc14bab724419bedf746bb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 May 2022 23:44:54 -0500 Subject: [PATCH 141/930] Use ULID short format for context ids (#71119) --- homeassistant/core.py | 2 +- homeassistant/util/ulid.py | 63 +++++++++++++++++++++++++++++++++----- tests/util/test_ulid.py | 7 ++++- 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 7245dad7864..d6b1ac85d8b 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -695,7 +695,7 @@ class Context: user_id: str | None = attr.ib(default=None) parent_id: str | None = attr.ib(default=None) - id: str = attr.ib(factory=ulid_util.ulid_hex) + id: str = attr.ib(factory=ulid_util.ulid) def as_dict(self) -> dict[str, str | None]: """Return a dictionary representation of the context.""" diff --git a/homeassistant/util/ulid.py b/homeassistant/util/ulid.py index 888b5b2074b..c38d95bd169 100644 --- a/homeassistant/util/ulid.py +++ b/homeassistant/util/ulid.py @@ -4,11 +4,21 @@ from random import getrandbits import time -# In the future once we require python 3.10 and above, we can -# create a new function that uses base64.b32encodehex to shorten -# these to 26 characters. def ulid_hex() -> str: - """Generate a ULID in hex that will work for a UUID. + """Generate a ULID in lowercase hex that will work for a UUID. + + This ulid should not be used for cryptographically secure + operations. + + This string can be converted with https://github.com/ahawker/ulid + + ulid.from_uuid(uuid.UUID(ulid_hex)) + """ + return f"{int(time.time()*1000):012x}{getrandbits(80):020x}" + + +def ulid() -> str: + """Generate a ULID. This ulid should not be used for cryptographically secure operations. @@ -18,8 +28,47 @@ def ulid_hex() -> str: Timestamp Randomness 48bits 80bits - This string can be converted with https://github.com/ahawker/ulid + This string can be loaded directly with https://github.com/ahawker/ulid - ulid.from_uuid(uuid.UUID(value)) + import homeassistant.util.ulid as ulid_util + import ulid + ulid.parse(ulid_util.ulid()) """ - return f"{int(time.time()*1000):012x}{getrandbits(80):020x}" + ulid_bytes = int(time.time() * 1000).to_bytes(6, byteorder="big") + int( + getrandbits(80) + ).to_bytes(10, byteorder="big") + + # This is base32 crockford encoding with the loop unrolled for performance + # + # This code is adapted from: + # https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102 + # + enc = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" + return ( + enc[(ulid_bytes[0] & 224) >> 5] + + enc[ulid_bytes[0] & 31] + + enc[(ulid_bytes[1] & 248) >> 3] + + enc[((ulid_bytes[1] & 7) << 2) | ((ulid_bytes[2] & 192) >> 6)] + + enc[((ulid_bytes[2] & 62) >> 1)] + + enc[((ulid_bytes[2] & 1) << 4) | ((ulid_bytes[3] & 240) >> 4)] + + enc[((ulid_bytes[3] & 15) << 1) | ((ulid_bytes[4] & 128) >> 7)] + + enc[(ulid_bytes[4] & 124) >> 2] + + enc[((ulid_bytes[4] & 3) << 3) | ((ulid_bytes[5] & 224) >> 5)] + + enc[ulid_bytes[5] & 31] + + enc[(ulid_bytes[6] & 248) >> 3] + + enc[((ulid_bytes[6] & 7) << 2) | ((ulid_bytes[7] & 192) >> 6)] + + enc[(ulid_bytes[7] & 62) >> 1] + + enc[((ulid_bytes[7] & 1) << 4) | ((ulid_bytes[8] & 240) >> 4)] + + enc[((ulid_bytes[8] & 15) << 1) | ((ulid_bytes[9] & 128) >> 7)] + + enc[(ulid_bytes[9] & 124) >> 2] + + enc[((ulid_bytes[9] & 3) << 3) | ((ulid_bytes[10] & 224) >> 5)] + + enc[ulid_bytes[10] & 31] + + enc[(ulid_bytes[11] & 248) >> 3] + + enc[((ulid_bytes[11] & 7) << 2) | ((ulid_bytes[12] & 192) >> 6)] + + enc[(ulid_bytes[12] & 62) >> 1] + + enc[((ulid_bytes[12] & 1) << 4) | ((ulid_bytes[13] & 240) >> 4)] + + enc[((ulid_bytes[13] & 15) << 1) | ((ulid_bytes[14] & 128) >> 7)] + + enc[(ulid_bytes[14] & 124) >> 2] + + enc[((ulid_bytes[14] & 3) << 3) | ((ulid_bytes[15] & 224) >> 5)] + + enc[ulid_bytes[15] & 31] + ) diff --git a/tests/util/test_ulid.py b/tests/util/test_ulid.py index fc5d2af8c87..9f297bf373e 100644 --- a/tests/util/test_ulid.py +++ b/tests/util/test_ulid.py @@ -6,6 +6,11 @@ import homeassistant.util.ulid as ulid_util async def test_ulid_util_uuid_hex(): - """Verify we can generate a ulid.""" + """Verify we can generate a ulid in hex.""" assert len(ulid_util.ulid_hex()) == 32 assert uuid.UUID(ulid_util.ulid_hex()) + + +async def test_ulid_util_uuid(): + """Verify we can generate a ulid.""" + assert len(ulid_util.ulid()) == 26 From 63679d3d2927f9e6b1029bca994a3fe138480faa Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 2 May 2022 06:49:50 +0200 Subject: [PATCH 142/930] Fix missing device & entity references in automations (#71103) --- .../components/automation/__init__.py | 33 +++++++--- tests/components/automation/test_init.py | 60 ++++++++++++++++--- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 857d2d4b49f..c743e1f83fd 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -17,6 +17,7 @@ from homeassistant.const import ( CONF_CONDITION, CONF_DEVICE_ID, CONF_ENTITY_ID, + CONF_EVENT_DATA, CONF_ID, CONF_MODE, CONF_PLATFORM, @@ -360,9 +361,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity): referenced |= condition.async_extract_devices(conf) for conf in self._trigger_config: - device = _trigger_extract_device(conf) - if device is not None: - referenced.add(device) + referenced |= set(_trigger_extract_device(conf)) self._referenced_devices = referenced return referenced @@ -764,12 +763,22 @@ async def _async_process_if(hass, name, config, p_config): @callback -def _trigger_extract_device(trigger_conf: dict) -> str | None: +def _trigger_extract_device(trigger_conf: dict) -> list[str]: """Extract devices from a trigger config.""" - if trigger_conf[CONF_PLATFORM] != "device": - return None + if trigger_conf[CONF_PLATFORM] == "device": + return [trigger_conf[CONF_DEVICE_ID]] - return trigger_conf[CONF_DEVICE_ID] + if ( + trigger_conf[CONF_PLATFORM] == "event" + and CONF_EVENT_DATA in trigger_conf + and CONF_DEVICE_ID in trigger_conf[CONF_EVENT_DATA] + ): + return [trigger_conf[CONF_EVENT_DATA][CONF_DEVICE_ID]] + + if trigger_conf[CONF_PLATFORM] == "tag" and CONF_DEVICE_ID in trigger_conf: + return trigger_conf[CONF_DEVICE_ID] + + return [] @callback @@ -778,6 +787,9 @@ def _trigger_extract_entities(trigger_conf: dict) -> list[str]: if trigger_conf[CONF_PLATFORM] in ("state", "numeric_state"): return trigger_conf[CONF_ENTITY_ID] + if trigger_conf[CONF_PLATFORM] == "calendar": + return [trigger_conf[CONF_ENTITY_ID]] + if trigger_conf[CONF_PLATFORM] == "zone": return trigger_conf[CONF_ENTITY_ID] + [trigger_conf[CONF_ZONE]] @@ -787,4 +799,11 @@ def _trigger_extract_entities(trigger_conf: dict) -> list[str]: if trigger_conf[CONF_PLATFORM] == "sun": return ["sun.sun"] + if ( + trigger_conf[CONF_PLATFORM] == "event" + and CONF_EVENT_DATA in trigger_conf + and CONF_ENTITY_ID in trigger_conf[CONF_EVENT_DATA] + ): + return [trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID]] + return [] diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 1c90abe72ca..ccb508c6acc 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1079,6 +1079,7 @@ async def test_automation_restore_last_triggered_with_initial_state(hass): async def test_extraction_functions(hass): """Test extraction functions.""" + await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}}) assert await async_setup_component( hass, DOMAIN, @@ -1086,7 +1087,24 @@ async def test_extraction_functions(hass): DOMAIN: [ { "alias": "test1", - "trigger": {"platform": "state", "entity_id": "sensor.trigger_1"}, + "trigger": [ + {"platform": "state", "entity_id": "sensor.trigger_state"}, + { + "platform": "numeric_state", + "entity_id": "sensor.trigger_numeric_state", + "above": 10, + }, + { + "platform": "calendar", + "entity_id": "calendar.trigger_calendar", + "event": "start", + }, + { + "platform": "event", + "event_type": "state_changed", + "event_data": {"entity_id": "sensor.trigger_event"}, + }, + ], "condition": { "condition": "state", "entity_id": "light.condition_state", @@ -1111,13 +1129,30 @@ async def test_extraction_functions(hass): }, { "alias": "test2", - "trigger": { - "platform": "device", - "domain": "light", - "type": "turned_on", - "entity_id": "light.trigger_2", - "device_id": "trigger-device-2", - }, + "trigger": [ + { + "platform": "device", + "domain": "light", + "type": "turned_on", + "entity_id": "light.trigger_2", + "device_id": "trigger-device-2", + }, + { + "platform": "tag", + "tag_id": "1234", + "device_id": "device-trigger-tag1", + }, + { + "platform": "tag", + "tag_id": "1234", + "device_id": ["device-trigger-tag2", "device-trigger-tag3"], + }, + { + "platform": "event", + "event_type": "esphome.button_pressed", + "event_data": {"device_id": "device-trigger-event"}, + }, + ], "condition": { "condition": "device", "device_id": "condition-device", @@ -1159,7 +1194,10 @@ async def test_extraction_functions(hass): "automation.test2", } assert set(automation.entities_in_automation(hass, "automation.test1")) == { - "sensor.trigger_1", + "calendar.trigger_calendar", + "sensor.trigger_state", + "sensor.trigger_numeric_state", + "sensor.trigger_event", "light.condition_state", "light.in_both", "light.in_first", @@ -1173,6 +1211,10 @@ async def test_extraction_functions(hass): "condition-device", "device-in-both", "device-in-last", + "device-trigger-event", + "device-trigger-tag1", + "device-trigger-tag2", + "device-trigger-tag3", } From 802adaf43c0d9ed344363b9b6bb6ee2e6234f7b4 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 1 May 2022 22:50:39 -0600 Subject: [PATCH 143/930] Fix issues with SimpliSafe email-based 2FA (#71180) * FIx issues with email-based SimpliSafe 2FA * Bump --- .../components/simplisafe/manifest.json | 2 +- homeassistant/components/simplisafe/strings.json | 2 +- .../components/simplisafe/translations/en.json | 16 +++------------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 8e4c1f5d82b..804523b3390 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,7 +3,7 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==2022.04.1"], + "requirements": ["simplisafe-python==2022.05.0"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "dhcp": [ diff --git a/homeassistant/components/simplisafe/strings.json b/homeassistant/components/simplisafe/strings.json index 45e0ef84f5a..85e579fd455 100644 --- a/homeassistant/components/simplisafe/strings.json +++ b/homeassistant/components/simplisafe/strings.json @@ -32,7 +32,7 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" }, "progress": { - "email_2fa": "Input the two-factor authentication code\nsent to you via email." + "email_2fa": "Check your email for a verification link from Simplisafe." } }, "options": { diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index a3bf0049681..0da6f6442e4 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "This SimpliSafe account is already in use.", "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", - "reauth_successful": "Re-authentication was successful", - "wrong_account": "The user credentials provided do not match this SimpliSafe account." + "reauth_successful": "Re-authentication was successful" }, "error": { - "2fa_timed_out": "Timed out while waiting for two-factor authentication", - "identifier_exists": "Account already registered", "invalid_auth": "Invalid authentication", - "still_awaiting_mfa": "Still awaiting MFA email click", "unknown": "Unexpected error" }, "progress": { - "email_2fa": "Input the two-factor authentication code\nsent to you via email." + "email_2fa": "Check your email for a verification link from Simplisafe." }, "step": { - "mfa": { - "title": "SimpliSafe Multi-Factor Authentication" - }, "reauth_confirm": { "data": { "password": "Password" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Authorization Code", - "code": "Code (used in Home Assistant UI)", "password": "Password", "username": "Username" }, - "description": "Input your username and password.", - "title": "Fill in your information." + "description": "Input your username and password." } } }, diff --git a/requirements_all.txt b/requirements_all.txt index dd808efa0bd..6b89774f4e7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2156,7 +2156,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==2022.04.1 +simplisafe-python==2022.05.0 # homeassistant.components.sisyphus sisyphus-control==3.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 26f1c86d1af..fed48ff4175 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1407,7 +1407,7 @@ sharkiq==0.0.1 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==2022.04.1 +simplisafe-python==2022.05.0 # homeassistant.components.slack slackclient==2.5.0 From 7026e5dd110220402f61ca3c3825adca3aa62bfa Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 2 May 2022 08:12:32 +0200 Subject: [PATCH 144/930] Fix Renault diagnostics (#71186) --- homeassistant/components/renault/diagnostics.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/renault/diagnostics.py b/homeassistant/components/renault/diagnostics.py index f8b0a0d926e..2029ef989d6 100644 --- a/homeassistant/components/renault/diagnostics.py +++ b/homeassistant/components/renault/diagnostics.py @@ -59,7 +59,9 @@ def _get_vehicle_diagnostics(vehicle: RenaultVehicleProxy) -> dict[str, Any]: return { "details": async_redact_data(vehicle.details.raw_data, TO_REDACT), "data": { - key: async_redact_data(coordinator.data.raw_data, TO_REDACT) + key: async_redact_data( + coordinator.data.raw_data if coordinator.data else None, TO_REDACT + ) for key, coordinator in vehicle.coordinators.items() }, } From 5db014666c5f988ad4ce42ee5ce94b6547c086de Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 02:10:34 -0500 Subject: [PATCH 145/930] Avoid recording state_changed events in the events table (#71165) * squash fix mypy * Update homeassistant/components/recorder/models.py Co-authored-by: Paulus Schoutsen * pass all columns * fix commented out code * reduce logbook query complexity * merge * comment Co-authored-by: Paulus Schoutsen --- homeassistant/components/logbook/__init__.py | 135 ++++++++---------- homeassistant/components/recorder/__init__.py | 3 +- .../components/recorder/migration.py | 20 ++- homeassistant/components/recorder/models.py | 45 ++++-- homeassistant/components/recorder/purge.py | 22 +-- tests/components/recorder/test_init.py | 48 +++---- tests/components/recorder/test_models.py | 3 - tests/components/recorder/test_purge.py | 9 +- 8 files changed, 151 insertions(+), 134 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 96e6b975e32..fdf553775b4 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -111,13 +111,28 @@ ALL_EVENT_TYPES = [ *ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, ] + EVENT_COLUMNS = [ - Events.event_type, - Events.event_data, - Events.time_fired, - Events.context_id, - Events.context_user_id, - Events.context_parent_id, + Events.event_type.label("event_type"), + Events.event_data.label("event_data"), + Events.time_fired.label("time_fired"), + Events.context_id.label("context_id"), + Events.context_user_id.label("context_user_id"), + Events.context_parent_id.label("context_parent_id"), +] + +STATE_COLUMNS = [ + States.state.label("state"), + States.entity_id.label("entity_id"), + States.attributes.label("attributes"), + StateAttributes.shared_attrs.label("shared_attrs"), +] + +EMPTY_STATE_COLUMNS = [ + literal(value=None, type_=sqlalchemy.String).label("state"), + literal(value=None, type_=sqlalchemy.String).label("entity_id"), + literal(value=None, type_=sqlalchemy.Text).label("attributes"), + literal(value=None, type_=sqlalchemy.Text).label("shared_attrs"), ] SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} @@ -502,43 +517,47 @@ def _get_events( with session_scope(hass=hass) as session: old_state = aliased(States, name="old_state") + query: Query + query = _generate_events_query_without_states(session) + query = _apply_event_time_filter(query, start_day, end_day) + query = _apply_event_types_filter( + hass, query, ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED + ) if entity_ids is not None: - query = _generate_events_query_without_states(session) - query = _apply_event_time_filter(query, start_day, end_day) - query = _apply_event_types_filter( - hass, query, ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED - ) if entity_matches_only: # When entity_matches_only is provided, contexts and events that do not # contain the entity_ids are not included in the logbook response. query = _apply_event_entity_id_matchers(query, entity_ids) query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) - query = query.union_all( _generate_states_query( session, start_day, end_day, old_state, entity_ids ) ) else: - query = _generate_events_query(session) - query = _apply_event_time_filter(query, start_day, end_day) - query = _apply_events_types_and_states_filter( - hass, query, old_state - ).filter( - (States.last_updated == States.last_changed) - | (Events.event_type != EVENT_STATE_CHANGED) - ) - if filters: - query = query.filter( - filters.entity_filter() | (Events.event_type != EVENT_STATE_CHANGED) # type: ignore[no-untyped-call] - ) - if context_id is not None: query = query.filter(Events.context_id == context_id) - query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) + states_query = _generate_states_query( + session, start_day, end_day, old_state, entity_ids + ) + if context_id is not None: + # Once all the old `state_changed` events + # are gone from the database this query can + # be simplified to filter only on States.context_id == context_id + states_query = states_query.outerjoin( + Events, (States.event_id == Events.event_id) + ) + states_query = states_query.filter( + (States.context_id == context_id) + | (States.context_id.is_(None) & (Events.context_id == context_id)) + ) + if filters: + states_query = states_query.filter(filters.entity_filter()) # type: ignore[no-untyped-call] + query = query.union_all(states_query) + query = query.order_by(Events.time_fired) return list( @@ -546,36 +565,22 @@ def _get_events( ) -def _generate_events_query(session: Session) -> Query: - return session.query( - *EVENT_COLUMNS, - EventData.shared_data, - States.state, - States.entity_id, - States.attributes, - StateAttributes.shared_attrs, - ) - - def _generate_events_query_without_data(session: Session) -> Query: return session.query( - *EVENT_COLUMNS, + literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label("event_type"), + literal(value=None, type_=sqlalchemy.Text).label("event_data"), + States.last_changed.label("time_fired"), + States.context_id.label("context_id"), + States.context_user_id.label("context_user_id"), + States.context_parent_id.label("context_parent_id"), literal(value=None, type_=sqlalchemy.Text).label("shared_data"), - States.state, - States.entity_id, - States.attributes, - StateAttributes.shared_attrs, + *STATE_COLUMNS, ) def _generate_events_query_without_states(session: Session) -> Query: return session.query( - *EVENT_COLUMNS, - EventData.shared_data, - literal(value=None, type_=sqlalchemy.String).label("state"), - literal(value=None, type_=sqlalchemy.String).label("entity_id"), - literal(value=None, type_=sqlalchemy.Text).label("attributes"), - literal(value=None, type_=sqlalchemy.Text).label("shared_attrs"), + *EVENT_COLUMNS, EventData.shared_data.label("shared_data"), *EMPTY_STATE_COLUMNS ) @@ -584,41 +589,19 @@ def _generate_states_query( start_day: dt, end_day: dt, old_state: States, - entity_ids: Iterable[str], + entity_ids: Iterable[str] | None, ) -> Query: - return ( + query = ( _generate_events_query_without_data(session) - .outerjoin(Events, (States.event_id == Events.event_id)) .outerjoin(old_state, (States.old_state_id == old_state.state_id)) .filter(_missing_state_matcher(old_state)) .filter(_not_continuous_entity_matcher()) .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .filter( - (States.last_updated == States.last_changed) - & States.entity_id.in_(entity_ids) - ) - .outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) + .filter(States.last_updated == States.last_changed) ) - - -def _apply_events_types_and_states_filter( - hass: HomeAssistant, query: Query, old_state: States -) -> Query: - events_query = ( - query.outerjoin(States, (Events.event_id == States.event_id)) - .outerjoin(old_state, (States.old_state_id == old_state.state_id)) - .filter( - (Events.event_type != EVENT_STATE_CHANGED) - | _missing_state_matcher(old_state) - ) - .filter( - (Events.event_type != EVENT_STATE_CHANGED) - | _not_continuous_entity_matcher() - ) - ) - return _apply_event_types_filter(hass, events_query, ALL_EVENT_TYPES).outerjoin( + if entity_ids: + query = query.filter(States.entity_id.in_(entity_ids)) + return query.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index d190ebd0a99..486866fc799 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -1223,8 +1223,8 @@ class Recorder(threading.Thread): ] = dbevent_data self.event_session.add(dbevent_data) - self.event_session.add(dbevent) if event.event_type != EVENT_STATE_CHANGED: + self.event_session.add(dbevent) return try: @@ -1272,7 +1272,6 @@ class Recorder(threading.Thread): self._pending_expunge.append(dbstate) else: dbstate.state = None - dbstate.event = dbevent self.event_session.add(dbstate) def _handle_database_error(self, err: Exception) -> bool: diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 2ef51824737..7835f5320b9 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -442,7 +442,7 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 # and we would have to move to something like # sqlalchemy alembic to make that work # - _drop_index(instance, "states", "ix_states_context_id") + # no longer dropping ix_states_context_id since its recreated in 28 _drop_index(instance, "states", "ix_states_context_user_id") # This index won't be there if they were not running # nightly but we don't treat that as a critical issue @@ -652,6 +652,24 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 elif new_version == 27: _add_columns(instance, "events", [f"data_id {big_int}"]) _create_index(instance, "events", "ix_events_data_id") + elif new_version == 28: + _add_columns(instance, "events", ["origin_idx INTEGER"]) + # We never use the user_id or parent_id index + _drop_index(instance, "events", "ix_events_context_user_id") + _drop_index(instance, "events", "ix_events_context_parent_id") + _add_columns( + instance, + "states", + [ + "origin_idx INTEGER", + "context_id VARCHAR(36)", + "context_user_id VARCHAR(36)", + "context_parent_id VARCHAR(36)", + ], + ) + _create_index(instance, "states", "ix_states_context_id") + # Once there are no longer any state_changed events + # in the events table we can drop the index on states.event_id else: raise ValueError(f"No schema migration defined for version {new_version}") diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 212c0c7e7d4..c528093198d 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -17,6 +17,7 @@ from sqlalchemy import ( Identity, Index, Integer, + SmallInteger, String, Text, distinct, @@ -43,7 +44,7 @@ from .const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP # pylint: disable=invalid-name Base = declarative_base() -SCHEMA_VERSION = 27 +SCHEMA_VERSION = 28 _LOGGER = logging.getLogger(__name__) @@ -86,6 +87,8 @@ DOUBLE_TYPE = ( .with_variant(oracle.DOUBLE_PRECISION(), "oracle") .with_variant(postgresql.DOUBLE_PRECISION(), "postgresql") ) +EVENT_ORIGIN_ORDER = [EventOrigin.local, EventOrigin.remote] +EVENT_ORIGIN_TO_IDX = {origin: idx for idx, origin in enumerate(EVENT_ORIGIN_ORDER)} class Events(Base): # type: ignore[misc,valid-type] @@ -98,14 +101,15 @@ class Events(Base): # type: ignore[misc,valid-type] {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, ) __tablename__ = TABLE_EVENTS - event_id = Column(Integer, Identity(), primary_key=True) + event_id = Column(Integer, Identity(), primary_key=True) # no longer used event_type = Column(String(MAX_LENGTH_EVENT_EVENT_TYPE)) event_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) - origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) + origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) # no longer used + origin_idx = Column(SmallInteger) time_fired = Column(DATETIME_TYPE, index=True) context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) - context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) - context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) data_id = Column(Integer, ForeignKey("event_data.data_id"), index=True) event_data_rel = relationship("EventData") @@ -114,7 +118,7 @@ class Events(Base): # type: ignore[misc,valid-type] return ( f"" ) @@ -124,7 +128,7 @@ class Events(Base): # type: ignore[misc,valid-type] return Events( event_type=event.event_type, event_data=None, - origin=str(event.origin.value), + origin_idx=EVENT_ORIGIN_TO_IDX.get(event.origin), time_fired=event.time_fired, context_id=event.context.id, context_user_id=event.context.user_id, @@ -142,7 +146,9 @@ class Events(Base): # type: ignore[misc,valid-type] return Event( self.event_type, json.loads(self.event_data) if self.event_data else {}, - EventOrigin(self.origin), + EventOrigin(self.origin) + if self.origin + else EVENT_ORIGIN_ORDER[self.origin_idx], process_timestamp(self.time_fired), context=context, ) @@ -222,7 +228,10 @@ class States(Base): # type: ignore[misc,valid-type] attributes_id = Column( Integer, ForeignKey("state_attributes.attributes_id"), index=True ) - event = relationship("Events", uselist=False) + context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + origin_idx = Column(SmallInteger) # 0 is local, 1 is remote old_state = relationship("States", remote_side=[state_id]) state_attributes = relationship("StateAttributes") @@ -242,7 +251,14 @@ class States(Base): # type: ignore[misc,valid-type] """Create object from a state_changed event.""" entity_id = event.data["entity_id"] state: State | None = event.data.get("new_state") - dbstate = States(entity_id=entity_id, attributes=None) + dbstate = States( + entity_id=entity_id, + attributes=None, + context_id=event.context.id, + context_user_id=event.context.user_id, + context_parent_id=event.context.parent_id, + origin_idx=EVENT_ORIGIN_TO_IDX.get(event.origin), + ) # None state means the state was removed from the state machine if state is None: @@ -258,6 +274,11 @@ class States(Base): # type: ignore[misc,valid-type] def to_native(self, validate_entity_id: bool = True) -> State | None: """Convert to an HA state object.""" + context = Context( + id=self.context_id, + user_id=self.context_user_id, + parent_id=self.context_parent_id, + ) try: return State( self.entity_id, @@ -267,9 +288,7 @@ class States(Base): # type: ignore[misc,valid-type] json.loads(self.attributes) if self.attributes else {}, process_timestamp(self.last_changed), process_timestamp(self.last_updated), - # Join the events table on event_id to get the context instead - # as it will always be there for state_changed events - context=Context(id=None), # type: ignore[arg-type] + context=context, validate_entity_id=validate_entity_id, ) except ValueError: diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index 4aad3a28a88..dcc535c8177 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -83,7 +83,7 @@ def purge_old_data( if short_term_statistics: _purge_short_term_statistics(session, short_term_statistics) - if event_ids or statistics_runs or short_term_statistics: + if state_ids or event_ids or statistics_runs or short_term_statistics: # Return false, as we might not be done yet. _LOGGER.debug("Purging hasn't fully completed yet") return False @@ -103,27 +103,31 @@ def _select_event_state_attributes_ids_data_ids_to_purge( ) -> tuple[set[int], set[int], set[int], set[int]]: """Return a list of event, state, and attribute ids to purge.""" events = ( - session.query( - Events.event_id, Events.data_id, States.state_id, States.attributes_id - ) - .outerjoin(States, Events.event_id == States.event_id) + session.query(Events.event_id, Events.data_id) .filter(Events.time_fired < purge_before) .limit(MAX_ROWS_TO_PURGE) .all() ) _LOGGER.debug("Selected %s event ids to remove", len(events)) + states = ( + session.query(States.state_id, States.attributes_id) + .filter(States.last_updated < purge_before) + .limit(MAX_ROWS_TO_PURGE) + .all() + ) + _LOGGER.debug("Selected %s state ids to remove", len(states)) event_ids = set() state_ids = set() attributes_ids = set() data_ids = set() for event in events: event_ids.add(event.event_id) - if event.state_id: - state_ids.add(event.state_id) - if event.attributes_id: - attributes_ids.add(event.attributes_id) if event.data_id: data_ids.add(event.data_id) + for state in states: + state_ids.add(state.state_id) + if state.attributes_id: + attributes_ids.add(state.attributes_id) return event_ids, state_ids, attributes_ids, data_ids diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index c3d22255ccc..423e8eb2f48 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -49,7 +49,7 @@ from homeassistant.const import ( STATE_LOCKED, STATE_UNLOCKED, ) -from homeassistant.core import Context, CoreState, Event, HomeAssistant, callback +from homeassistant.core import CoreState, Event, HomeAssistant, callback from homeassistant.setup import async_setup_component, setup_component from homeassistant.util import dt as dt_util @@ -162,7 +162,7 @@ async def test_state_gets_saved_when_set_before_start_event( with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 1 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None async def test_saving_state(hass: HomeAssistant, recorder_mock): @@ -182,9 +182,9 @@ async def test_saving_state(hass: HomeAssistant, recorder_mock): state = db_state.to_native() state.attributes = db_state_attributes.to_native() assert len(db_states) == 1 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None - assert state == _state_empty_context(hass, entity_id) + assert state == _state_with_context(hass, entity_id) async def test_saving_many_states( @@ -210,7 +210,7 @@ async def test_saving_many_states( with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 6 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None async def test_saving_state_with_intermixed_time_changes( @@ -234,7 +234,7 @@ async def test_saving_state_with_intermixed_time_changes( with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 2 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None def test_saving_state_with_exception(hass, hass_recorder, caplog): @@ -411,7 +411,7 @@ def test_saving_state_with_commit_interval_zero(hass_recorder): with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 1 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None def _add_entities(hass, entity_ids): @@ -454,12 +454,10 @@ def _add_events(hass, events): return events -def _state_empty_context(hass, entity_id): +def _state_with_context(hass, entity_id): # We don't restore context unless we need it by joining the # events table on the event_id for state_changed events - state = hass.states.get(entity_id) - state.context = Context(id=None) - return state + return hass.states.get(entity_id) # pylint: disable=redefined-outer-name,invalid-name @@ -468,7 +466,7 @@ def test_saving_state_include_domains(hass_recorder): hass = hass_recorder({"include": {"domains": "test2"}}) states = _add_entities(hass, ["test.recorder", "test2.recorder"]) assert len(states) == 1 - assert _state_empty_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test2.recorder") == states[0] def test_saving_state_include_domains_globs(hass_recorder): @@ -480,8 +478,8 @@ def test_saving_state_include_domains_globs(hass_recorder): hass, ["test.recorder", "test2.recorder", "test3.included_entity"] ) assert len(states) == 2 - assert _state_empty_context(hass, "test2.recorder") == states[0] - assert _state_empty_context(hass, "test3.included_entity") == states[1] + assert _state_with_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test3.included_entity") == states[1] def test_saving_state_incl_entities(hass_recorder): @@ -489,7 +487,7 @@ def test_saving_state_incl_entities(hass_recorder): hass = hass_recorder({"include": {"entities": "test2.recorder"}}) states = _add_entities(hass, ["test.recorder", "test2.recorder"]) assert len(states) == 1 - assert _state_empty_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test2.recorder") == states[0] def test_saving_event_exclude_event_type(hass_recorder): @@ -518,7 +516,7 @@ def test_saving_state_exclude_domains(hass_recorder): hass = hass_recorder({"exclude": {"domains": "test"}}) states = _add_entities(hass, ["test.recorder", "test2.recorder"]) assert len(states) == 1 - assert _state_empty_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test2.recorder") == states[0] def test_saving_state_exclude_domains_globs(hass_recorder): @@ -530,7 +528,7 @@ def test_saving_state_exclude_domains_globs(hass_recorder): hass, ["test.recorder", "test2.recorder", "test2.excluded_entity"] ) assert len(states) == 1 - assert _state_empty_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test2.recorder") == states[0] def test_saving_state_exclude_entities(hass_recorder): @@ -538,7 +536,7 @@ def test_saving_state_exclude_entities(hass_recorder): hass = hass_recorder({"exclude": {"entities": "test.recorder"}}) states = _add_entities(hass, ["test.recorder", "test2.recorder"]) assert len(states) == 1 - assert _state_empty_context(hass, "test2.recorder") == states[0] + assert _state_with_context(hass, "test2.recorder") == states[0] def test_saving_state_exclude_domain_include_entity(hass_recorder): @@ -571,8 +569,8 @@ def test_saving_state_include_domain_exclude_entity(hass_recorder): ) states = _add_entities(hass, ["test.recorder", "test2.recorder", "test.ok"]) assert len(states) == 1 - assert _state_empty_context(hass, "test.ok") == states[0] - assert _state_empty_context(hass, "test.ok").state == "state2" + assert _state_with_context(hass, "test.ok") == states[0] + assert _state_with_context(hass, "test.ok").state == "state2" def test_saving_state_include_domain_glob_exclude_entity(hass_recorder): @@ -587,8 +585,8 @@ def test_saving_state_include_domain_glob_exclude_entity(hass_recorder): hass, ["test.recorder", "test2.recorder", "test.ok", "test2.included_entity"] ) assert len(states) == 1 - assert _state_empty_context(hass, "test.ok") == states[0] - assert _state_empty_context(hass, "test.ok").state == "state2" + assert _state_with_context(hass, "test.ok") == states[0] + assert _state_with_context(hass, "test.ok").state == "state2" def test_saving_state_and_removing_entity(hass, hass_recorder): @@ -1153,8 +1151,8 @@ def test_service_disable_states_not_recording(hass, hass_recorder): with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 1 - assert db_states[0].event_id > 0 - assert db_states[0].to_native() == _state_empty_context(hass, "test.two") + assert db_states[0].event_id is None + assert db_states[0].to_native() == _state_with_context(hass, "test.two") def test_service_disable_run_information_recorded(tmpdir): @@ -1257,7 +1255,7 @@ async def test_database_corruption_while_running(hass, tmpdir, caplog): with session_scope(hass=hass) as session: db_states = list(session.query(States)) assert len(db_states) == 1 - assert db_states[0].event_id > 0 + assert db_states[0].event_id is None return db_states[0].to_native() state = await hass.async_add_executor_job(_get_last_state) diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index 3bdb7992c7c..a68d137eb0f 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -39,9 +39,6 @@ def test_from_event_to_db_state(): {"entity_id": "sensor.temperature", "old_state": None, "new_state": state}, context=state.context, ) - # We don't restore context unless we need it by joining the - # events table on the event_id for state_changed events - state.context = ha.Context(id=None) assert state == States.from_event(event).to_native() diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 1f0f3c87ea5..d946d1e2a14 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -64,7 +64,7 @@ async def test_purge_old_states( assert state_attributes.count() == 3 events = session.query(Events).filter(Events.event_type == "state_changed") - assert events.count() == 6 + assert events.count() == 0 assert "test.recorder2" in instance._old_states purge_before = dt_util.utcnow() - timedelta(days=4) @@ -108,7 +108,7 @@ async def test_purge_old_states( assert states[5].old_state_id == states[4].state_id events = session.query(Events).filter(Events.event_type == "state_changed") - assert events.count() == 6 + assert events.count() == 0 assert "test.recorder2" in instance._old_states state_attributes = session.query(StateAttributes) @@ -793,7 +793,6 @@ async def test_purge_filtered_states( assert session.query(StateAttributes).count() == 11 - # Finally make sure we can delete them all except for the ones missing an event_id service_data = {"keep_days": 0} await hass.services.async_call( recorder.DOMAIN, recorder.SERVICE_PURGE, service_data @@ -805,8 +804,8 @@ async def test_purge_filtered_states( remaining = list(session.query(States)) for state in remaining: assert state.event_id is None - assert len(remaining) == 3 - assert session.query(StateAttributes).count() == 1 + assert len(remaining) == 0 + assert session.query(StateAttributes).count() == 0 @pytest.mark.parametrize("use_sqlite", (True, False), indirect=True) From 37b59dfcc0dd2823305a2a9e8ec75bab30a17d8c Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 2 May 2022 09:51:19 +0200 Subject: [PATCH 146/930] Make sure sensor state value is not None prior to trying to used the scaled value (#71189) --- homeassistant/components/deconz/sensor.py | 8 +++--- tests/components/deconz/test_sensor.py | 30 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index a4dcaed8f18..d3a20fad522 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -112,7 +112,7 @@ ENTITY_DESCRIPTIONS = { DeconzSensorDescription( key="consumption", value_fn=lambda device: device.scaled_consumption - if isinstance(device, Consumption) + if isinstance(device, Consumption) and isinstance(device.consumption, int) else None, update_key="consumption", device_class=SensorDeviceClass.ENERGY, @@ -144,7 +144,7 @@ ENTITY_DESCRIPTIONS = { DeconzSensorDescription( key="humidity", value_fn=lambda device: device.scaled_humidity - if isinstance(device, Humidity) + if isinstance(device, Humidity) and isinstance(device.humidity, int) else None, update_key="humidity", device_class=SensorDeviceClass.HUMIDITY, @@ -156,7 +156,7 @@ ENTITY_DESCRIPTIONS = { DeconzSensorDescription( key="light_level", value_fn=lambda device: device.scaled_light_level - if isinstance(device, LightLevel) + if isinstance(device, LightLevel) and isinstance(device.light_level, int) else None, update_key="lightlevel", device_class=SensorDeviceClass.ILLUMINANCE, @@ -189,7 +189,7 @@ ENTITY_DESCRIPTIONS = { DeconzSensorDescription( key="temperature", value_fn=lambda device: device.scaled_temperature - if isinstance(device, Temperature) + if isinstance(device, Temperature) and isinstance(device.temperature, int) else None, update_key="temperature", device_class=SensorDeviceClass.TEMPERATURE, diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index bd51bab44e4..590ccee25d5 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -785,6 +785,36 @@ async def test_add_new_sensor(hass, aioclient_mock, mock_deconz_websocket): assert hass.states.get("sensor.light_level_sensor").state == "999.8" +BAD_SENSOR_DATA = [ + ("ZHAConsumption", "consumption"), + ("ZHAHumidity", "humidity"), + ("ZHALightLevel", "lightlevel"), + ("ZHATemperature", "temperature"), +] + + +@pytest.mark.parametrize("sensor_type, sensor_property", BAD_SENSOR_DATA) +async def test_dont_add_sensor_if_state_is_none( + hass, aioclient_mock, sensor_type, sensor_property +): + """Test sensor with scaled data is not created if state is None.""" + data = { + "sensors": { + "1": { + "name": "Sensor 1", + "type": sensor_type, + "state": {sensor_property: None}, + "config": {}, + "uniqueid": "00:00:00:00:00:00:00:00-00", + } + } + } + with patch.dict(DECONZ_WEB_REQUEST, data): + await setup_deconz_integration(hass, aioclient_mock) + + assert len(hass.states.async_all()) == 0 + + async def test_add_battery_later(hass, aioclient_mock, mock_deconz_websocket): """Test that a sensor without an initial battery state creates a battery sensor once state exist.""" data = { From 738701a2d628c9d9b32f0fa7d5cbc0c375e0a5e1 Mon Sep 17 00:00:00 2001 From: stegm Date: Mon, 2 May 2022 11:42:18 +0200 Subject: [PATCH 147/930] Handle missing kostal plenticore battery option (#65237) Co-authored-by: Martin Hjelmare --- .../components/kostal_plenticore/select.py | 45 +++++++++----- .../components/kostal_plenticore/conftest.py | 61 ++++++++----------- .../kostal_plenticore/test_diagnostics.py | 31 +++++++++- .../kostal_plenticore/test_select.py | 45 ++++++++++++++ 4 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 tests/components/kostal_plenticore/test_select.py diff --git a/homeassistant/components/kostal_plenticore/select.py b/homeassistant/components/kostal_plenticore/select.py index 1844f0894a5..7ac06f2ebef 100644 --- a/homeassistant/components/kostal_plenticore/select.py +++ b/homeassistant/components/kostal_plenticore/select.py @@ -24,6 +24,8 @@ async def async_setup_entry( ) -> None: """Add kostal plenticore Select widget.""" plenticore: Plenticore = hass.data[DOMAIN][entry.entry_id] + + available_settings_data = await plenticore.client.get_settings() select_data_update_coordinator = SelectDataUpdateCoordinator( hass, _LOGGER, @@ -32,23 +34,34 @@ async def async_setup_entry( plenticore, ) - async_add_entities( - PlenticoreDataSelect( - select_data_update_coordinator, - entry_id=entry.entry_id, - platform_name=entry.title, - device_class="kostal_plenticore__battery", - module_id=select.module_id, - data_id=select.data_id, - name=select.name, - current_option="None", - options=select.options, - is_on=select.is_on, - device_info=plenticore.device_info, - unique_id=f"{entry.entry_id}_{select.module_id}", + entities = [] + for select in SELECT_SETTINGS_DATA: + if select.module_id not in available_settings_data: + continue + needed_data_ids = {data_id for data_id in select.options if data_id != "None"} + available_data_ids = { + setting.id for setting in available_settings_data[select.module_id] + } + if not needed_data_ids <= available_data_ids: + continue + entities.append( + PlenticoreDataSelect( + select_data_update_coordinator, + entry_id=entry.entry_id, + platform_name=entry.title, + device_class="kostal_plenticore__battery", + module_id=select.module_id, + data_id=select.data_id, + name=select.name, + current_option="None", + options=select.options, + is_on=select.is_on, + device_info=plenticore.device_info, + unique_id=f"{entry.entry_id}_{select.module_id}", + ) ) - for select in SELECT_SETTINGS_DATA - ) + + async_add_entities(entities) class PlenticoreDataSelect(CoordinatorEntity, SelectEntity, ABC): diff --git a/tests/components/kostal_plenticore/conftest.py b/tests/components/kostal_plenticore/conftest.py index c3ed1b45592..4e789a34198 100644 --- a/tests/components/kostal_plenticore/conftest.py +++ b/tests/components/kostal_plenticore/conftest.py @@ -4,9 +4,10 @@ from __future__ import annotations from collections.abc import Generator from unittest.mock import AsyncMock, MagicMock, patch -from kostal.plenticore import MeData, SettingsData, VersionData +from kostal.plenticore import MeData, VersionData import pytest +from homeassistant.components.kostal_plenticore.helper import Plenticore from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo @@ -14,10 +15,19 @@ from tests.common import MockConfigEntry @pytest.fixture -async def init_integration( - hass: HomeAssistant, -) -> Generator[None, MockConfigEntry, None]: - """Set up Kostal Plenticore integration for testing.""" +def mock_config_entry() -> MockConfigEntry: + """Return a mocked ConfigEntry for testing.""" + return MockConfigEntry( + entry_id="2ab8dd92a62787ddfe213a67e09406bd", + title="scb", + domain="kostal_plenticore", + data={"host": "192.168.1.2", "password": "SecretPassword"}, + ) + + +@pytest.fixture +def mock_plenticore() -> Generator[Plenticore, None, None]: + """Set up a Plenticore mock with some default values.""" with patch( "homeassistant.components.kostal_plenticore.Plenticore", autospec=True ) as mock_api_class: @@ -60,37 +70,20 @@ async def init_integration( ) plenticore.client.get_process_data = AsyncMock() - plenticore.client.get_process_data.return_value = { - "devices:local": ["HomeGrid_P", "HomePv_P"] - } - plenticore.client.get_settings = AsyncMock() - plenticore.client.get_settings.return_value = { - "devices:local": [ - SettingsData( - { - "id": "Battery:MinSoc", - "unit": "%", - "default": "None", - "min": 5, - "max": 100, - "type": "byte", - "access": "readwrite", - } - ) - ] - } - mock_config_entry = MockConfigEntry( - entry_id="2ab8dd92a62787ddfe213a67e09406bd", - title="scb", - domain="kostal_plenticore", - data={"host": "192.168.1.2", "password": "SecretPassword"}, - ) + yield plenticore - mock_config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() +@pytest.fixture +async def init_integration( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +) -> MockConfigEntry: + """Set up Kostal Plenticore integration for testing.""" - yield mock_config_entry + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + return mock_config_entry diff --git a/tests/components/kostal_plenticore/test_diagnostics.py b/tests/components/kostal_plenticore/test_diagnostics.py index 56af8bafe06..1f249aa3798 100644 --- a/tests/components/kostal_plenticore/test_diagnostics.py +++ b/tests/components/kostal_plenticore/test_diagnostics.py @@ -1,7 +1,9 @@ """Test Kostal Plenticore diagnostics.""" from aiohttp import ClientSession +from kostal.plenticore import SettingsData from homeassistant.components.diagnostics import REDACTED +from homeassistant.components.kostal_plenticore.helper import Plenticore from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -9,9 +11,34 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry async def test_entry_diagnostics( - hass: HomeAssistant, hass_client: ClientSession, init_integration: MockConfigEntry -): + hass: HomeAssistant, + hass_client: ClientSession, + mock_plenticore: Plenticore, + init_integration: MockConfigEntry, +) -> None: """Test config entry diagnostics.""" + + # set some test process and settings data for the diagnostics output + mock_plenticore.client.get_process_data.return_value = { + "devices:local": ["HomeGrid_P", "HomePv_P"] + } + + mock_plenticore.client.get_settings.return_value = { + "devices:local": [ + SettingsData( + { + "id": "Battery:MinSoc", + "unit": "%", + "default": "None", + "min": 5, + "max": 100, + "type": "byte", + "access": "readwrite", + } + ) + ] + } + assert await get_diagnostics_for_config_entry( hass, hass_client, init_integration ) == { diff --git a/tests/components/kostal_plenticore/test_select.py b/tests/components/kostal_plenticore/test_select.py new file mode 100644 index 00000000000..6023b015483 --- /dev/null +++ b/tests/components/kostal_plenticore/test_select.py @@ -0,0 +1,45 @@ +"""Test the Kostal Plenticore Solar Inverter select platform.""" +from kostal.plenticore import SettingsData + +from homeassistant.components.kostal_plenticore.helper import Plenticore +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry + +from tests.common import MockConfigEntry + + +async def test_select_battery_charging_usage_available( + hass: HomeAssistant, mock_plenticore: Plenticore, mock_config_entry: MockConfigEntry +) -> None: + """Test that the battery charging usage select entity is added if the settings are available.""" + + mock_plenticore.client.get_settings.return_value = { + "devices:local": [ + SettingsData({"id": "Battery:SmartBatteryControl:Enable"}), + SettingsData({"id": "Battery:TimeControl:Enable"}), + ] + } + + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert entity_registry.async_get(hass).async_is_registered( + "select.battery_charging_usage_mode" + ) + + +async def test_select_battery_charging_usage_not_available( + hass: HomeAssistant, mock_plenticore: Plenticore, mock_config_entry: MockConfigEntry +) -> None: + """Test that the battery charging usage select entity is not added if the settings are unavailable.""" + + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert not entity_registry.async_get(hass).async_is_registered( + "select.battery_charging_usage_mode" + ) From 546ba8169d3de57513480e4ac9444b2009879b04 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 2 May 2022 13:15:19 +0200 Subject: [PATCH 148/930] Remove entity registry entries when script is removed (#71193) --- homeassistant/components/config/automation.py | 2 +- homeassistant/components/config/scene.py | 2 +- homeassistant/components/config/script.py | 16 ++++++++++++++-- tests/components/config/test_script.py | 19 +++++++++++++++++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/config/automation.py b/homeassistant/components/config/automation.py index f1a2d9aab84..5a39b786e27 100644 --- a/homeassistant/components/config/automation.py +++ b/homeassistant/components/config/automation.py @@ -23,7 +23,7 @@ async def async_setup(hass): if action != ACTION_DELETE: return - ent_reg = await entity_registry.async_get_registry(hass) + ent_reg = entity_registry.async_get(hass) entity_id = ent_reg.async_get_entity_id(DOMAIN, DOMAIN, config_key) diff --git a/homeassistant/components/config/scene.py b/homeassistant/components/config/scene.py index 6523ff84158..862c8c46f4d 100644 --- a/homeassistant/components/config/scene.py +++ b/homeassistant/components/config/scene.py @@ -20,7 +20,7 @@ async def async_setup(hass): if action != ACTION_DELETE: return - ent_reg = await entity_registry.async_get_registry(hass) + ent_reg = entity_registry.async_get(hass) entity_id = ent_reg.async_get_entity_id(DOMAIN, HA_DOMAIN, config_key) diff --git a/homeassistant/components/config/script.py b/homeassistant/components/config/script.py index 7adc766a1ab..45a7a6dc227 100644 --- a/homeassistant/components/config/script.py +++ b/homeassistant/components/config/script.py @@ -6,9 +6,9 @@ from homeassistant.components.script.config import ( ) from homeassistant.config import SCRIPT_CONFIG_PATH from homeassistant.const import SERVICE_RELOAD -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er -from . import EditKeyBasedConfigView +from . import ACTION_DELETE, EditKeyBasedConfigView async def async_setup(hass): @@ -18,6 +18,18 @@ async def async_setup(hass): """post_write_hook for Config View that reloads scripts.""" await hass.services.async_call(DOMAIN, SERVICE_RELOAD) + if action != ACTION_DELETE: + return + + ent_reg = er.async_get(hass) + + entity_id = ent_reg.async_get_entity_id(DOMAIN, DOMAIN, config_key) + + if entity_id is None: + return + + ent_reg.async_remove(entity_id) + hass.http.register_view( EditScriptConfigView( DOMAIN, diff --git a/tests/components/config/test_script.py b/tests/components/config/test_script.py index dca9a8aa8a7..4b6ca1bdc8f 100644 --- a/tests/components/config/test_script.py +++ b/tests/components/config/test_script.py @@ -6,21 +6,34 @@ import pytest from homeassistant.bootstrap import async_setup_component from homeassistant.components import config +from homeassistant.helpers import entity_registry as er from tests.components.blueprint.conftest import stub_blueprint_populate # noqa: F401 @pytest.fixture(autouse=True) -async def setup_script(hass, stub_blueprint_populate): # noqa: F811 +async def setup_script(hass, script_config, stub_blueprint_populate): # noqa: F811 """Set up script integration.""" - assert await async_setup_component(hass, "script", {}) + assert await async_setup_component(hass, "script", {"script": script_config}) +@pytest.mark.parametrize( + "script_config", + ( + { + "one": {"alias": "Light on", "sequence": []}, + "two": {"alias": "Light off", "sequence": []}, + }, + ), +) async def test_delete_script(hass, hass_client): """Test deleting a script.""" with patch.object(config, "SECTIONS", ["script"]): await async_setup_component(hass, "config", {}) + ent_reg = er.async_get(hass) + assert len(ent_reg.entities) == 2 + client = await hass_client() orig_data = {"one": {}, "two": {}} @@ -46,3 +59,5 @@ async def test_delete_script(hass, hass_client): assert len(written) == 1 assert written[0] == {"one": {}} + + assert len(ent_reg.entities) == 1 From 3f7c6a1ba733c28e9bc73654c994430c6857e0a5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 May 2022 04:35:19 -0700 Subject: [PATCH 149/930] Offer visit device for Squeezelite32 devices (#71181) --- homeassistant/components/slimproto/media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/slimproto/media_player.py b/homeassistant/components/slimproto/media_player.py index af3eb693478..d41d10432db 100644 --- a/homeassistant/components/slimproto/media_player.py +++ b/homeassistant/components/slimproto/media_player.py @@ -100,8 +100,8 @@ class SlimProtoPlayer(MediaPlayerEntity): name=self.player.name, hw_version=self.player.firmware, ) - # PiCore player has web interface - if "-pCP" in self.player.firmware: + # PiCore + SqueezeESP32 player has web interface + if "-pCP" in self.player.firmware or self.player.device_model == "SqueezeESP32": self._attr_device_info[ "configuration_url" ] = f"http://{self.player.device_address}" From f6c2fb088c8f0c9f9d358e6c81025778dc36129a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 2 May 2022 14:59:58 +0200 Subject: [PATCH 150/930] Stop script if sub-script stops or aborts (#71195) --- homeassistant/helpers/script.py | 10 ++++-- tests/helpers/test_script.py | 61 ++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 70f73f065ab..d988b0edd81 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -395,8 +395,14 @@ class _ScriptRun: script_execution_set("finished") except _StopScript: script_execution_set("finished") + # Let the _StopScript bubble up if this is a sub-script + if not self._script.top_level: + raise except _AbortScript: script_execution_set("aborted") + # Let the _AbortScript bubble up if this is a sub-script + if not self._script.top_level: + raise except Exception: script_execution_set("error") raise @@ -1143,7 +1149,7 @@ class Script: hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STOP, partial(_async_stop_scripts_at_shutdown, hass) ) - self._top_level = top_level + self.top_level = top_level if top_level: all_scripts.append( {"instance": self, "started_before_shutdown": not hass.is_stopping} @@ -1431,7 +1437,7 @@ class Script: # If this is a top level Script then make a copy of the variables in case they # are read-only, but more importantly, so as not to leak any variables created # during the run back to the caller. - if self._top_level: + if self.top_level: if self.variables: try: variables = self.variables.async_render( diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index fb3b021daec..b9c968838c9 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -94,7 +94,7 @@ def assert_element(trace_element, expected_element, path): # Check for unexpected items in trace_element assert not set(trace_element._result or {}) - set(expected_result) - if "error_type" in expected_element: + if "error_type" in expected_element and expected_element["error_type"] is not None: assert isinstance(trace_element._error, expected_element["error_type"]) else: assert trace_element._error is None @@ -4485,6 +4485,65 @@ async def test_stop_action(hass, caplog): ) +@pytest.mark.parametrize( + "error,error_type,logmsg,script_execution", + ( + (True, script._AbortScript, "Error", "aborted"), + (False, None, "Stop", "finished"), + ), +) +async def test_stop_action_subscript( + hass, caplog, error, error_type, logmsg, script_execution +): + """Test if automation stops on calling the stop action from a sub-script.""" + event = "test_event" + events = async_capture_events(hass, event) + + alias = "stop step" + sequence = cv.SCRIPT_SCHEMA( + [ + {"event": event}, + { + "if": { + "alias": "if condition", + "condition": "template", + "value_template": "{{ 1 == 1 }}", + }, + "then": { + "alias": alias, + "stop": "In the name of love", + "error": error, + }, + }, + {"event": event}, + ] + ) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + assert f"{logmsg} script sequence: In the name of love" in caplog.text + caplog.clear() + assert len(events) == 1 + + assert_action_trace( + { + "0": [{"result": {"event": "test_event", "event_data": {}}}], + "1": [{"error_type": error_type, "result": {"choice": "then"}}], + "1/if": [{"result": {"result": True}}], + "1/if/condition/0": [{"result": {"result": True, "entities": []}}], + "1/then/0": [ + { + "error_type": error_type, + "result": {"stop": "In the name of love", "error": error}, + } + ], + }, + expected_script_execution=script_execution, + ) + + async def test_stop_action_with_error(hass, caplog): """Test if automation fails on calling the error action.""" event = "test_event" From 56de00272704ead14c911da2325cad82920aa291 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 May 2022 06:09:49 -0700 Subject: [PATCH 151/930] Add media source support to AppleTV (#71185) --- .../components/apple_tv/browse_media.py | 2 +- .../components/apple_tv/media_player.py | 54 +++++++++++++++++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/apple_tv/browse_media.py b/homeassistant/components/apple_tv/browse_media.py index 3c0eee8b6ad..8d0a94ca858 100644 --- a/homeassistant/components/apple_tv/browse_media.py +++ b/homeassistant/components/apple_tv/browse_media.py @@ -18,7 +18,7 @@ def build_app_list(app_list): return BrowseMedia( media_class=MEDIA_CLASS_DIRECTORY, - media_content_id=None, + media_content_id="apps", media_content_type=MEDIA_TYPE_APPS, title="Apps", can_play=True, diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index cadf84e8b31..02919043bb2 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -13,11 +13,15 @@ from pyatv.const import ( ) from pyatv.helpers import is_streamable +from homeassistant.components import media_source from homeassistant.components.media_player import ( BrowseMedia, MediaPlayerEntity, MediaPlayerEntityFeature, ) +from homeassistant.components.media_player.browse_media import ( + async_process_play_media_url, +) from homeassistant.components.media_player.const import ( MEDIA_TYPE_APP, MEDIA_TYPE_MUSIC, @@ -73,7 +77,8 @@ SUPPORT_APPLE_TV = ( # Map features in pyatv to Home Assistant SUPPORT_FEATURE_MAPPING = { - FeatureName.PlayUrl: MediaPlayerEntityFeature.PLAY_MEDIA, + FeatureName.PlayUrl: MediaPlayerEntityFeature.BROWSE_MEDIA + | MediaPlayerEntityFeature.PLAY_MEDIA, FeatureName.StreamFile: MediaPlayerEntityFeature.PLAY_MEDIA, FeatureName.Pause: MediaPlayerEntityFeature.PAUSE, FeatureName.Play: MediaPlayerEntityFeature.PLAY, @@ -276,12 +281,24 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): # RAOP. Otherwise try to play it with regular AirPlay. if media_type == MEDIA_TYPE_APP: await self.atv.apps.launch_app(media_id) - elif self._is_feature_available(FeatureName.StreamFile) and ( - await is_streamable(media_id) or media_type == MEDIA_TYPE_MUSIC + + is_media_source_id = media_source.is_media_source_id(media_id) + + if ( + not is_media_source_id + and self._is_feature_available(FeatureName.StreamFile) + and (await is_streamable(media_id) or media_type == MEDIA_TYPE_MUSIC) ): _LOGGER.debug("Streaming %s via RAOP", media_id) await self.atv.stream.stream_file(media_id) - elif self._is_feature_available(FeatureName.PlayUrl): + + if self._is_feature_available(FeatureName.PlayUrl): + if is_media_source_id: + play_item = await media_source.async_resolve_media(self.hass, media_id) + media_id = play_item.url + + media_id = async_process_play_media_url(self.hass, media_id) + _LOGGER.debug("Playing %s via AirPlay", media_id) await self.atv.stream.play_url(media_id) else: @@ -380,7 +397,34 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): media_content_id=None, ) -> BrowseMedia: """Implement the websocket media browsing helper.""" - return build_app_list(self._app_list) + # If we can't stream URLs, we can't browse media. + # In that case the `BROWSE_MEDIA` feature was added because of AppList/LaunchApp + if not self._is_feature_available(FeatureName.PlayUrl): + return build_app_list(self._app_list) + + if self._app_list: + kwargs = {} + else: + # If it has no apps, assume it has no display + kwargs = { + "content_filter": lambda item: item.media_content_type.startswith( + "audio/" + ), + } + + cur_item = await media_source.async_browse_media( + self.hass, media_content_id, **kwargs + ) + + # If media content id is not None, we're browsing into a media source + if media_content_id is not None: + return cur_item + + # Add app item if we have one + if self._app_list and cur_item.children: + cur_item.children.insert(0, build_app_list(self._app_list)) + + return cur_item async def async_turn_on(self): """Turn the media player on.""" From d6617eba7c4699520b63013ef78377b672621ce9 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 2 May 2022 09:23:17 -0400 Subject: [PATCH 152/930] Fix bad ZHA _attr definitions (#71198) --- homeassistant/components/zha/sensor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 0a5fe204648..249034ef068 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -345,7 +345,7 @@ class ElectricalMeasurementFrequency(ElectricalMeasurement, id_suffix="ac_freque """Frequency measurement.""" SENSOR_ATTR = "ac_frequency" - _device_class: SensorDeviceClass = SensorDeviceClass.FREQUENCY + _attr_device_class: SensorDeviceClass = SensorDeviceClass.FREQUENCY _unit = FREQUENCY_HERTZ _div_mul_prefix = "ac_frequency" @@ -360,7 +360,7 @@ class ElectricalMeasurementPowerFactor(ElectricalMeasurement, id_suffix="power_f """Frequency measurement.""" SENSOR_ATTR = "power_factor" - _device_class: SensorDeviceClass = SensorDeviceClass.POWER_FACTOR + _attr_device_class: SensorDeviceClass = SensorDeviceClass.POWER_FACTOR _unit = PERCENTAGE @property @@ -718,8 +718,8 @@ class SinopeHVACAction(ThermostatHVACAction): class RSSISensor(Sensor, id_suffix="rssi"): """RSSI sensor for a device.""" - _state_class: SensorStateClass = SensorStateClass.MEASUREMENT - _device_class: SensorDeviceClass = SensorDeviceClass.SIGNAL_STRENGTH + _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT + _attr_device_class: SensorDeviceClass = SensorDeviceClass.SIGNAL_STRENGTH _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_registry_enabled_default = False From 1e18307a6678b6f77bf53567c4aa22d005cf190b Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 2 May 2022 15:50:13 +0200 Subject: [PATCH 153/930] Add reauth flow to Meater (#69895) --- homeassistant/components/meater/__init__.py | 9 +-- .../components/meater/config_flow.py | 58 ++++++++++++++++--- homeassistant/components/meater/strings.json | 9 +++ .../components/meater/translations/en.json | 9 +++ tests/components/meater/test_config_flow.py | 52 ++++++++++++++--- 5 files changed, 119 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/meater/__init__.py b/homeassistant/components/meater/__init__.py index a722928a13f..904d9e412c0 100644 --- a/homeassistant/components/meater/__init__.py +++ b/homeassistant/components/meater/__init__.py @@ -14,7 +14,7 @@ from meater.MeaterApi import MeaterProbe from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -40,8 +40,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (ServiceUnavailableError, TooManyRequestsError) as err: raise ConfigEntryNotReady from err except AuthenticationError as err: - _LOGGER.error("Unable to authenticate with the Meater API: %s", err) - return False + raise ConfigEntryAuthFailed( + f"Unable to authenticate with the Meater API: {err}" + ) from err async def async_update_data() -> dict[str, MeaterProbe]: """Fetch data from API endpoint.""" @@ -51,7 +52,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async with async_timeout.timeout(10): devices: list[MeaterProbe] = await meater_api.get_all_devices() except AuthenticationError as err: - raise UpdateFailed("The API call wasn't authenticated") from err + raise ConfigEntryAuthFailed("The API call wasn't authenticated") from err except TooManyRequestsError as err: raise UpdateFailed( "Too many requests have been made to the API, rate limiting is in place" diff --git a/homeassistant/components/meater/config_flow.py b/homeassistant/components/meater/config_flow.py index 1b1a8a0eca4..07dbd4bd4a5 100644 --- a/homeassistant/components/meater/config_flow.py +++ b/homeassistant/components/meater/config_flow.py @@ -1,14 +1,18 @@ """Config flow for Meater.""" +from __future__ import annotations + from meater import AuthenticationError, MeaterApi, ServiceUnavailableError import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import aiohttp_client from .const import DOMAIN -FLOW_SCHEMA = vol.Schema( +REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) +USER_SCHEMA = vol.Schema( {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} ) @@ -16,12 +20,17 @@ FLOW_SCHEMA = vol.Schema( class MeaterConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Meater Config Flow.""" - async def async_step_user(self, user_input=None): + _data_schema = USER_SCHEMA + _username: str + + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Define the login user step.""" if user_input is None: return self.async_show_form( step_id="user", - data_schema=FLOW_SCHEMA, + data_schema=self._data_schema, ) username: str = user_input[CONF_USERNAME] @@ -31,13 +40,41 @@ class MeaterConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): username = user_input[CONF_USERNAME] password = user_input[CONF_PASSWORD] + return await self._try_connect_meater("user", None, username, password) + + async def async_step_reauth(self, data: dict[str, str]) -> FlowResult: + """Handle configuration by re-auth.""" + self._data_schema = REAUTH_SCHEMA + self._username = data[CONF_USERNAME] + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: + """Handle re-auth completion.""" + placeholders = {"username": self._username} + if not user_input: + return self.async_show_form( + step_id="reauth_confirm", + data_schema=self._data_schema, + description_placeholders=placeholders, + ) + + password = user_input[CONF_PASSWORD] + return await self._try_connect_meater( + "reauth_confirm", placeholders, self._username, password + ) + + async def _try_connect_meater( + self, step_id, placeholders: dict[str, str] | None, username: str, password: str + ) -> FlowResult: session = aiohttp_client.async_get_clientsession(self.hass) api = MeaterApi(session) errors = {} try: - await api.authenticate(user_input[CONF_USERNAME], user_input[CONF_PASSWORD]) + await api.authenticate(username, password) except AuthenticationError: errors["base"] = "invalid_auth" except ServiceUnavailableError: @@ -45,13 +82,20 @@ class MeaterConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): except Exception: # pylint: disable=broad-except errors["base"] = "unknown_auth_error" else: + data = {"username": username, "password": password} + existing_entry = await self.async_set_unique_id(username.lower()) + if existing_entry: + self.hass.config_entries.async_update_entry(existing_entry, data=data) + await self.hass.config_entries.async_reload(existing_entry.entry_id) + return self.async_abort(reason="reauth_successful") return self.async_create_entry( title="Meater", - data={"username": username, "password": password}, + data=data, ) return self.async_show_form( - step_id="user", - data_schema=FLOW_SCHEMA, + step_id=step_id, + data_schema=self._data_schema, + description_placeholders=placeholders, errors=errors, ) diff --git a/homeassistant/components/meater/strings.json b/homeassistant/components/meater/strings.json index 772e6afd080..635c71d324c 100644 --- a/homeassistant/components/meater/strings.json +++ b/homeassistant/components/meater/strings.json @@ -6,6 +6,15 @@ "data": { "password": "[%key:common::config_flow::data::password%]", "username": "[%key:common::config_flow::data::username%]" + }, + "data_description": { + "username": "Meater Cloud username, typically an email address." + } + }, + "reauth_confirm": { + "description": "Confirm the password for Meater Cloud account {username}.", + "data": { + "password": "[%key:common::config_flow::data::password%]" } } }, diff --git a/homeassistant/components/meater/translations/en.json b/homeassistant/components/meater/translations/en.json index 3ceb94bcef0..707c6dc6ed6 100644 --- a/homeassistant/components/meater/translations/en.json +++ b/homeassistant/components/meater/translations/en.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Unexpected error" }, "step": { + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "Confirm the password for Meater Cloud account {username}." + }, "user": { "data": { "password": "Password", "username": "Username" }, + "data_description": { + "username": "Meater Cloud username, typically an email address." + }, "description": "Set up your Meater Cloud account." } } diff --git a/tests/components/meater/test_config_flow.py b/tests/components/meater/test_config_flow.py index 597b72c354a..11312111311 100644 --- a/tests/components/meater/test_config_flow.py +++ b/tests/components/meater/test_config_flow.py @@ -4,9 +4,8 @@ from unittest.mock import AsyncMock, patch from meater import AuthenticationError, ServiceUnavailableError import pytest -from homeassistant import data_entry_flow +from homeassistant import config_entries, data_entry_flow from homeassistant.components.meater import DOMAIN -from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from tests.common import MockConfigEntry @@ -35,7 +34,7 @@ async def test_duplicate_error(hass): ) result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=conf + DOMAIN, context={"source": config_entries.SOURCE_USER}, data=conf ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT @@ -48,7 +47,7 @@ async def test_unknown_auth_error(hass, mock_meater): conf = {CONF_USERNAME: "user@host.com", CONF_PASSWORD: "password123"} result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=conf + DOMAIN, context={"source": config_entries.SOURCE_USER}, data=conf ) assert result["errors"] == {"base": "unknown_auth_error"} @@ -59,7 +58,7 @@ async def test_invalid_credentials(hass, mock_meater): conf = {CONF_USERNAME: "user@host.com", CONF_PASSWORD: "password123"} result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=conf + DOMAIN, context={"source": config_entries.SOURCE_USER}, data=conf ) assert result["errors"] == {"base": "invalid_auth"} @@ -72,7 +71,7 @@ async def test_service_unavailable(hass, mock_meater): conf = {CONF_USERNAME: "user@host.com", CONF_PASSWORD: "password123"} result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=conf + DOMAIN, context={"source": config_entries.SOURCE_USER}, data=conf ) assert result["errors"] == {"base": "service_unavailable_error"} @@ -82,7 +81,7 @@ async def test_user_flow(hass, mock_meater): conf = {CONF_USERNAME: "user@host.com", CONF_PASSWORD: "password123"} result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=None + DOMAIN, context={"source": config_entries.SOURCE_USER}, data=None ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" @@ -106,3 +105,42 @@ async def test_user_flow(hass, mock_meater): CONF_USERNAME: "user@host.com", CONF_PASSWORD: "password123", } + + +async def test_reauth_flow(hass, mock_meater): + """Test that the reauth flow works.""" + data = { + CONF_USERNAME: "user@host.com", + CONF_PASSWORD: "password123", + } + mock_config = MockConfigEntry( + domain=DOMAIN, + unique_id="user@host.com", + data=data, + ) + mock_config.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_REAUTH}, + data=data, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "reauth_confirm" + assert result["errors"] is None + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"password": "passwordabc"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result2["reason"] == "reauth_successful" + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + assert config_entry.data == { + CONF_USERNAME: "user@host.com", + CONF_PASSWORD: "passwordabc", + } From f35e7d1129ccde6994a0969137efdca63a4b0a1e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 2 May 2022 16:41:14 +0200 Subject: [PATCH 154/930] Allow cancelling async_at_start helper (#71196) --- homeassistant/helpers/start.py | 15 +++++++-- tests/helpers/test_start.py | 61 +++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/start.py b/homeassistant/helpers/start.py index 7f919f5351d..6c17ae5be3a 100644 --- a/homeassistant/helpers/start.py +++ b/homeassistant/helpers/start.py @@ -20,8 +20,19 @@ def async_at_start( hass.async_run_hass_job(at_start_job, hass) return lambda: None - async def _matched_event(event: Event) -> None: + unsub: None | CALLBACK_TYPE = None + + @callback + def _matched_event(event: Event) -> None: """Call the callback when Home Assistant started.""" hass.async_run_hass_job(at_start_job, hass) + nonlocal unsub + unsub = None - return hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _matched_event) + @callback + def cancel() -> None: + if unsub: + unsub() + + unsub = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _matched_event) + return cancel diff --git a/tests/helpers/test_start.py b/tests/helpers/test_start.py index 55f98cf60eb..bc32ffa35fd 100644 --- a/tests/helpers/test_start.py +++ b/tests/helpers/test_start.py @@ -27,7 +27,7 @@ async def test_at_start_when_running_awaitable(hass): assert len(calls) == 2 -async def test_at_start_when_running_callback(hass): +async def test_at_start_when_running_callback(hass, caplog): """Test at start when already running.""" assert hass.state == core.CoreState.running assert hass.is_running @@ -39,15 +39,19 @@ async def test_at_start_when_running_callback(hass): """Home Assistant is started.""" calls.append(1) - start.async_at_start(hass, cb_at_start) + start.async_at_start(hass, cb_at_start)() assert len(calls) == 1 hass.state = core.CoreState.starting assert hass.is_running - start.async_at_start(hass, cb_at_start) + start.async_at_start(hass, cb_at_start)() assert len(calls) == 2 + # Check the unnecessary cancel did not generate warnings or errors + for record in caplog.records: + assert record.levelname in ("DEBUG", "INFO") + async def test_at_start_when_starting_awaitable(hass): """Test at start when yet to start.""" @@ -69,7 +73,7 @@ async def test_at_start_when_starting_awaitable(hass): assert len(calls) == 1 -async def test_at_start_when_starting_callback(hass): +async def test_at_start_when_starting_callback(hass, caplog): """Test at start when yet to start.""" hass.state = core.CoreState.not_running assert not hass.is_running @@ -81,10 +85,57 @@ async def test_at_start_when_starting_callback(hass): """Home Assistant is started.""" calls.append(1) - start.async_at_start(hass, cb_at_start) + cancel = start.async_at_start(hass, cb_at_start) await hass.async_block_till_done() assert len(calls) == 0 hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() assert len(calls) == 1 + + cancel() + + # Check the unnecessary cancel did not generate warnings or errors + for record in caplog.records: + assert record.levelname in ("DEBUG", "INFO") + + +async def test_cancelling_when_running(hass, caplog): + """Test cancelling at start when already running.""" + assert hass.state == core.CoreState.running + assert hass.is_running + + calls = [] + + async def cb_at_start(hass): + """Home Assistant is started.""" + calls.append(1) + + start.async_at_start(hass, cb_at_start)() + await hass.async_block_till_done() + assert len(calls) == 1 + + # Check the unnecessary cancel did not generate warnings or errors + for record in caplog.records: + assert record.levelname in ("DEBUG", "INFO") + + +async def test_cancelling_when_starting(hass): + """Test cancelling at start when yet to start.""" + hass.state = core.CoreState.not_running + assert not hass.is_running + + calls = [] + + @core.callback + def cb_at_start(hass): + """Home Assistant is started.""" + calls.append(1) + + start.async_at_start(hass, cb_at_start)() + await hass.async_block_till_done() + assert len(calls) == 0 + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + assert len(calls) == 0 From a74f035ae7f16ae62d5d757f7b825ee174b039a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20T=C3=B3th?= <42900504+toth2zoltan@users.noreply.github.com> Date: Mon, 2 May 2022 16:42:23 +0200 Subject: [PATCH 155/930] Fix SAJ Solar inverter RecursionError (#71157) --- homeassistant/components/saj/sensor.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/saj/sensor.py b/homeassistant/components/saj/sensor.py index 2fb3729d0a8..818ce7ea57a 100644 --- a/homeassistant/components/saj/sensor.py +++ b/homeassistant/components/saj/sensor.py @@ -209,14 +209,11 @@ class SAJsensor(SensorEntity): @property def device_class(self): """Return the device class the sensor belongs to.""" - if self.unit_of_measurement == POWER_WATT: + if self.native_unit_of_measurement == POWER_WATT: return SensorDeviceClass.POWER - if self.unit_of_measurement == ENERGY_KILO_WATT_HOUR: + if self.native_unit_of_measurement == ENERGY_KILO_WATT_HOUR: return SensorDeviceClass.ENERGY - if ( - self.unit_of_measurement == TEMP_CELSIUS - or self._sensor.unit == TEMP_FAHRENHEIT - ): + if self.native_unit_of_measurement in (TEMP_CELSIUS, TEMP_FAHRENHEIT): return SensorDeviceClass.TEMPERATURE @property From 5e4e7ed1521c67ba8b07b76279212defd925bb93 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Mon, 2 May 2022 10:42:47 -0400 Subject: [PATCH 156/930] Fix Insteon thermostats and reduce logging (#71179) * Bump pyinsteon to 1.1.0 * Load modem aldb if read write mode is unkwown * Correct reference to read_write_mode --- homeassistant/components/insteon/__init__.py | 4 +++- homeassistant/components/insteon/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/insteon/__init__.py b/homeassistant/components/insteon/__init__.py index 15181cb827c..94c18df9a33 100644 --- a/homeassistant/components/insteon/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -4,6 +4,7 @@ from contextlib import suppress import logging from pyinsteon import async_close, async_connect, devices +from pyinsteon.constants import ReadWriteMode from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_PLATFORM, EVENT_HOMEASSISTANT_STOP @@ -48,7 +49,8 @@ async def async_get_device_config(hass, config_entry): with suppress(AttributeError): await devices[address].async_status() - await devices.async_load(id_devices=1) + load_aldb = devices.modem.aldb.read_write_mode == ReadWriteMode.UNKNOWN + await devices.async_load(id_devices=1, load_modem_aldb=load_aldb) for addr in devices: device = devices[addr] flags = True diff --git a/homeassistant/components/insteon/manifest.json b/homeassistant/components/insteon/manifest.json index fc8ade1ba4d..c69f4f2cdf5 100644 --- a/homeassistant/components/insteon/manifest.json +++ b/homeassistant/components/insteon/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/insteon", "dependencies": ["http", "websocket_api"], "requirements": [ - "pyinsteon==1.1.0b3", + "pyinsteon==1.1.0", "insteon-frontend-home-assistant==0.1.0" ], "codeowners": ["@teharris1"], diff --git a/requirements_all.txt b/requirements_all.txt index 6b89774f4e7..16177793510 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1550,7 +1550,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.1.0b3 +pyinsteon==1.1.0 # homeassistant.components.intesishome pyintesishome==1.7.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fed48ff4175..fde9af6058b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1032,7 +1032,7 @@ pyialarm==1.9.0 pyicloud==1.0.0 # homeassistant.components.insteon -pyinsteon==1.1.0b3 +pyinsteon==1.1.0 # homeassistant.components.ipma pyipma==2.0.5 From a4682ae6e104134b3efc0bc25b0aee3368895dab Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 2 May 2022 17:35:37 +0200 Subject: [PATCH 157/930] Adjust version number in template default deprecation warning (#71203) --- homeassistant/helpers/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index bded5ab933c..dbc82ce6902 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1331,7 +1331,7 @@ def warn_no_default(function, value, default): ( "Template warning: '%s' got invalid input '%s' when %s template '%s' " "but no default was specified. Currently '%s' will return '%s', however this template will fail " - "to render in Home Assistant core 2022.1" + "to render in Home Assistant core 2022.6" ), function, value, From 1aaf78ef9944ded259298afbdbedcc07c90b80b0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 2 May 2022 18:33:16 +0200 Subject: [PATCH 158/930] Remove entity category system in favor of hidden_by (#68550) --- homeassistant/const.py | 2 -- homeassistant/helpers/entity.py | 3 -- homeassistant/helpers/entity_platform.py | 2 +- tests/components/alexa/test_entities.py | 10 +----- tests/components/cloud/test_alexa_config.py | 12 +------ tests/components/cloud/test_google_config.py | 17 ++-------- tests/components/energy/test_sensor.py | 34 +++++++++++++++++++ .../google_assistant/test_google_assistant.py | 10 +----- tests/helpers/test_entity.py | 3 +- 9 files changed, 42 insertions(+), 51 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2f9a90032ef..74fe6adfdfd 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -764,11 +764,9 @@ CLOUD_NEVER_EXPOSED_ENTITIES: Final[list[str]] = ["group.all_locks"] # use the EntityCategory enum instead. ENTITY_CATEGORY_CONFIG: Final = "config" ENTITY_CATEGORY_DIAGNOSTIC: Final = "diagnostic" -ENTITY_CATEGORY_SYSTEM: Final = "system" ENTITY_CATEGORIES: Final[list[str]] = [ ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_DIAGNOSTIC, - ENTITY_CATEGORY_SYSTEM, ] # The ID of the Home Assistant Media Player Cast App diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index f07f4c1c7b8..c8faad53b0e 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -200,9 +200,6 @@ class EntityCategory(StrEnum): # Diagnostic: An entity exposing some configuration parameter or diagnostics of a device DIAGNOSTIC = "diagnostic" - # System: An entity which is not useful for the user to interact with - SYSTEM = "system" - ENTITY_CATEGORIES_SCHEMA: Final = vol.Coerce(EntityCategory) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 6972dbf7c16..3d57a9c3dc0 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -519,8 +519,8 @@ class EntityPlatform: config_entry=self.config_entry, device_id=device_id, disabled_by=disabled_by, - hidden_by=hidden_by, entity_category=entity.entity_category, + hidden_by=hidden_by, known_object_ids=self.entities.keys(), original_device_class=entity.device_class, original_icon=entity.icon, diff --git a/tests/components/alexa/test_entities.py b/tests/components/alexa/test_entities.py index 5f64879b535..fb364dbf14e 100644 --- a/tests/components/alexa/test_entities.py +++ b/tests/components/alexa/test_entities.py @@ -43,20 +43,13 @@ async def test_categorized_hidden_entities(hass): entity_category=EntityCategory.DIAGNOSTIC, ) entity_entry3 = entity_registry.async_get_or_create( - "switch", - "test", - "switch_system_id", - suggested_object_id="system_switch", - entity_category=EntityCategory.SYSTEM, - ) - entity_entry4 = entity_registry.async_get_or_create( "switch", "test", "switch_hidden_integration_id", suggested_object_id="hidden_integration_switch", hidden_by=er.RegistryEntryHider.INTEGRATION, ) - entity_entry5 = entity_registry.async_get_or_create( + entity_entry4 = entity_registry.async_get_or_create( "switch", "test", "switch_hidden_user_id", @@ -69,7 +62,6 @@ async def test_categorized_hidden_entities(hass): hass.states.async_set(entity_entry2.entity_id, "something_else") hass.states.async_set(entity_entry3.entity_id, "blah") hass.states.async_set(entity_entry4.entity_id, "foo") - hass.states.async_set(entity_entry5.entity_id, "bar") msg = await smart_home.async_handle_message(hass, get_default_config(hass), request) diff --git a/tests/components/cloud/test_alexa_config.py b/tests/components/cloud/test_alexa_config.py index 115d39d3aeb..465ff7dd3d4 100644 --- a/tests/components/cloud/test_alexa_config.py +++ b/tests/components/cloud/test_alexa_config.py @@ -39,20 +39,13 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): entity_category=EntityCategory.DIAGNOSTIC, ) entity_entry3 = entity_registry.async_get_or_create( - "light", - "test", - "light_system_id", - suggested_object_id="system_light", - entity_category=EntityCategory.SYSTEM, - ) - entity_entry4 = entity_registry.async_get_or_create( "light", "test", "light_hidden_integration_id", suggested_object_id="hidden_integration_light", hidden_by=er.RegistryEntryHider.INTEGRATION, ) - entity_entry5 = entity_registry.async_get_or_create( + entity_entry4 = entity_registry.async_get_or_create( "light", "test", "light_hidden_user_id", @@ -77,7 +70,6 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): assert not conf.should_expose(entity_entry2.entity_id) assert not conf.should_expose(entity_entry3.entity_id) assert not conf.should_expose(entity_entry4.entity_id) - assert not conf.should_expose(entity_entry5.entity_id) entity_conf["should_expose"] = True assert conf.should_expose("light.kitchen") @@ -86,7 +78,6 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): assert not conf.should_expose(entity_entry2.entity_id) assert not conf.should_expose(entity_entry3.entity_id) assert not conf.should_expose(entity_entry4.entity_id) - assert not conf.should_expose(entity_entry5.entity_id) entity_conf["should_expose"] = None assert conf.should_expose("light.kitchen") @@ -95,7 +86,6 @@ async def test_alexa_config_expose_entity_prefs(hass, cloud_prefs, cloud_stub): assert not conf.should_expose(entity_entry2.entity_id) assert not conf.should_expose(entity_entry3.entity_id) assert not conf.should_expose(entity_entry4.entity_id) - assert not conf.should_expose(entity_entry5.entity_id) assert "alexa" not in hass.config.components await cloud_prefs.async_update( diff --git a/tests/components/cloud/test_google_config.py b/tests/components/cloud/test_google_config.py index 95746eb67ae..a86f1f3cf8a 100644 --- a/tests/components/cloud/test_google_config.py +++ b/tests/components/cloud/test_google_config.py @@ -298,20 +298,13 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): entity_category=EntityCategory.DIAGNOSTIC, ) entity_entry3 = entity_registry.async_get_or_create( - "light", - "test", - "light_system_id", - suggested_object_id="system_light", - entity_category=EntityCategory.SYSTEM, - ) - entity_entry4 = entity_registry.async_get_or_create( "light", "test", "light_hidden_integration_id", suggested_object_id="hidden_integration_light", hidden_by=er.RegistryEntryHider.INTEGRATION, ) - entity_entry5 = entity_registry.async_get_or_create( + entity_entry4 = entity_registry.async_get_or_create( "light", "test", "light_hidden_user_id", @@ -328,14 +321,12 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): state = State("light.kitchen", "on") state_config = State(entity_entry1.entity_id, "on") state_diagnostic = State(entity_entry2.entity_id, "on") - state_system = State(entity_entry3.entity_id, "on") - state_hidden_integration = State(entity_entry4.entity_id, "on") - state_hidden_user = State(entity_entry5.entity_id, "on") + state_hidden_integration = State(entity_entry3.entity_id, "on") + state_hidden_user = State(entity_entry4.entity_id, "on") assert not mock_conf.should_expose(state) assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) - assert not mock_conf.should_expose(state_system) assert not mock_conf.should_expose(state_hidden_integration) assert not mock_conf.should_expose(state_hidden_user) @@ -344,7 +335,6 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): # categorized and hidden entities should not be exposed assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) - assert not mock_conf.should_expose(state_system) assert not mock_conf.should_expose(state_hidden_integration) assert not mock_conf.should_expose(state_hidden_user) @@ -353,7 +343,6 @@ async def test_google_config_expose_entity_prefs(hass, mock_conf, cloud_prefs): # categorized and hidden entities should not be exposed assert not mock_conf.should_expose(state_config) assert not mock_conf.should_expose(state_diagnostic) - assert not mock_conf.should_expose(state_system) assert not mock_conf.should_expose(state_hidden_integration) assert not mock_conf.should_expose(state_hidden_user) diff --git a/tests/components/energy/test_sensor.py b/tests/components/energy/test_sensor.py index 913a2f44d93..997f60a8899 100644 --- a/tests/components/energy/test_sensor.py +++ b/tests/components/energy/test_sensor.py @@ -75,6 +75,40 @@ async def test_cost_sensor_no_states(hass, hass_storage, setup_integration) -> N # TODO: No states, should the cost entity refuse to setup? +async def test_cost_sensor_attributes(hass, hass_storage, setup_integration) -> None: + """Test sensor attributes.""" + energy_data = data.EnergyManager.default_preferences() + energy_data["energy_sources"].append( + { + "type": "grid", + "flow_from": [ + { + "stat_energy_from": "sensor.energy_consumption", + "entity_energy_from": "sensor.energy_consumption", + "stat_cost": None, + "entity_energy_price": None, + "number_energy_price": 1, + } + ], + "flow_to": [], + "cost_adjustment_day": 0, + } + ) + + hass_storage[data.STORAGE_KEY] = { + "version": 1, + "data": energy_data, + } + await setup_integration(hass) + + registry = er.async_get(hass) + cost_sensor_entity_id = "sensor.energy_consumption_cost" + entry = registry.async_get(cost_sensor_entity_id) + assert entry.entity_category is None + assert entry.disabled_by is None + assert entry.hidden_by == er.RegistryEntryHider.INTEGRATION + + @pytest.mark.parametrize("initial_energy,initial_cost", [(0, "0.0"), (None, "unknown")]) @pytest.mark.parametrize( "price_entity,fixed_price", [("sensor.energy_price", None), (None, 1)] diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 642ef15451a..8bf0e5573b2 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -147,20 +147,13 @@ async def test_sync_request(hass_fixture, assistant_client, auth_header): entity_category=EntityCategory.DIAGNOSTIC, ) entity_entry3 = entity_registry.async_get_or_create( - "switch", - "test", - "switch_system_id", - suggested_object_id="system_switch", - entity_category=EntityCategory.SYSTEM, - ) - entity_entry4 = entity_registry.async_get_or_create( "switch", "test", "switch_hidden_integration_id", suggested_object_id="hidden_integration_switch", hidden_by=er.RegistryEntryHider.INTEGRATION, ) - entity_entry5 = entity_registry.async_get_or_create( + entity_entry4 = entity_registry.async_get_or_create( "switch", "test", "switch_hidden_user_id", @@ -173,7 +166,6 @@ async def test_sync_request(hass_fixture, assistant_client, auth_header): hass_fixture.states.async_set(entity_entry2.entity_id, "something_else") hass_fixture.states.async_set(entity_entry3.entity_id, "blah") hass_fixture.states.async_set(entity_entry4.entity_id, "foo") - hass_fixture.states.async_set(entity_entry5.entity_id, "bar") reqid = "5711642932632160983" data = {"requestId": reqid, "inputs": [{"intent": "action.devices.SYNC"}]} diff --git a/tests/helpers/test_entity.py b/tests/helpers/test_entity.py index 34cb68403f4..e345d7d7258 100644 --- a/tests/helpers/test_entity.py +++ b/tests/helpers/test_entity.py @@ -913,7 +913,6 @@ async def test_entity_category_property(hass): ( ("config", entity.EntityCategory.CONFIG), ("diagnostic", entity.EntityCategory.DIAGNOSTIC), - ("system", entity.EntityCategory.SYSTEM), ), ) def test_entity_category_schema(value, expected): @@ -930,7 +929,7 @@ def test_entity_category_schema_error(value): schema = vol.Schema(entity.ENTITY_CATEGORIES_SCHEMA) with pytest.raises( vol.Invalid, - match=r"expected EntityCategory or one of 'config', 'diagnostic', 'system'", + match=r"expected EntityCategory or one of 'config', 'diagnostic'", ): schema(value) From 0cdcdec80915a0f88f9229304446c2f53f33cea7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 11:34:24 -0500 Subject: [PATCH 159/930] Speed up and isolate legacy logbook context_id query (#71201) --- homeassistant/components/logbook/__init__.py | 50 ++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index fdf553775b4..7bacbd4cdd7 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -543,20 +543,24 @@ def _get_events( states_query = _generate_states_query( session, start_day, end_day, old_state, entity_ids ) + unions: list[Query] = [] if context_id is not None: # Once all the old `state_changed` events - # are gone from the database this query can - # be simplified to filter only on States.context_id == context_id + # are gone from the database remove the + # _generate_legacy_events_context_id_query + unions.append( + _generate_legacy_events_context_id_query( + session, context_id, start_day, end_day + ) + ) states_query = states_query.outerjoin( Events, (States.event_id == Events.event_id) ) - states_query = states_query.filter( - (States.context_id == context_id) - | (States.context_id.is_(None) & (Events.context_id == context_id)) - ) - if filters: + states_query = states_query.filter(States.context_id == context_id) + elif filters: states_query = states_query.filter(filters.entity_filter()) # type: ignore[no-untyped-call] - query = query.union_all(states_query) + unions.append(states_query) + query = query.union_all(*unions) query = query.order_by(Events.time_fired) @@ -578,6 +582,36 @@ def _generate_events_query_without_data(session: Session) -> Query: ) +def _generate_legacy_events_context_id_query( + session: Session, + context_id: str, + start_day: dt, + end_day: dt, +) -> Query: + """Generate a legacy events context id query that also joins states.""" + # This can be removed once we no longer have event_ids in the states table + legacy_context_id_query = session.query( + *EVENT_COLUMNS, + literal(value=None, type_=sqlalchemy.String).label("shared_data"), + States.state, + States.entity_id, + States.attributes, + StateAttributes.shared_attrs, + ) + legacy_context_id_query = _apply_event_time_filter( + legacy_context_id_query, start_day, end_day + ) + return ( + legacy_context_id_query.filter(Events.context_id == context_id) + .outerjoin(States, (Events.event_id == States.event_id)) + .filter(States.last_updated == States.last_changed) + .filter(_not_continuous_entity_matcher()) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + ) + + def _generate_events_query_without_states(session: Session) -> Query: return session.query( *EVENT_COLUMNS, EventData.shared_data.label("shared_data"), *EMPTY_STATE_COLUMNS From 0926470ef0f7bf7fd11da09a9d101ea17a4b4c00 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 May 2022 10:51:13 -0700 Subject: [PATCH 160/930] Skip signing URL that we know requires no auth (#71208) --- .../components/media_player/browse_media.py | 7 +++++++ tests/components/media_player/test_browse_media.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/homeassistant/components/media_player/browse_media.py b/homeassistant/components/media_player/browse_media.py index 60234cd1b38..9327bf68f9f 100644 --- a/homeassistant/components/media_player/browse_media.py +++ b/homeassistant/components/media_player/browse_media.py @@ -20,6 +20,9 @@ from homeassistant.helpers.network import ( from .const import CONTENT_AUTH_EXPIRY_TIME, MEDIA_CLASS_DIRECTORY +# Paths that we don't need to sign +PATHS_WITHOUT_AUTH = ("/api/tts_proxy/",) + @callback def async_process_play_media_url( @@ -46,6 +49,10 @@ def async_process_play_media_url( logging.getLogger(__name__).debug( "Not signing path for content with query param" ) + elif parsed.path.startswith(PATHS_WITHOUT_AUTH): + # We don't sign this path if it doesn't need auth. Although signing itself can't hurt, + # some devices are unable to handle long URLs and the auth signature might push it over. + pass else: signed_path = async_sign_path( hass, diff --git a/tests/components/media_player/test_browse_media.py b/tests/components/media_player/test_browse_media.py index ea1e3b4fc36..58081c7b3b1 100644 --- a/tests/components/media_player/test_browse_media.py +++ b/tests/components/media_player/test_browse_media.py @@ -73,6 +73,18 @@ async def test_process_play_media_url(hass, mock_sign_path): == "http://192.168.123.123:8123/path?hello=world" ) + # Test skip signing URLs if they are known to require no auth + assert ( + async_process_play_media_url(hass, "/api/tts_proxy/bla") + == "http://example.local:8123/api/tts_proxy/bla" + ) + assert ( + async_process_play_media_url( + hass, "http://example.local:8123/api/tts_proxy/bla" + ) + == "http://example.local:8123/api/tts_proxy/bla" + ) + with pytest.raises(ValueError): async_process_play_media_url(hass, "hello") From 7c46eb5952146812c32d39bcfe77c4be1e2c6d99 Mon Sep 17 00:00:00 2001 From: Yuval Aboulafia Date: Mon, 2 May 2022 20:52:00 +0300 Subject: [PATCH 161/930] Add test for incorrect config for Jewish Calendar (#71163) * Loop load Jewish Calendar platforms * Address review * Add test for incorrect config * add test to sensor platform --- .../jewish_calendar/test_binary_sensor.py | 13 +++++++++++++ tests/components/jewish_calendar/test_sensor.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/tests/components/jewish_calendar/test_binary_sensor.py b/tests/components/jewish_calendar/test_binary_sensor.py index 31cc0095af1..3ecac44b59c 100644 --- a/tests/components/jewish_calendar/test_binary_sensor.py +++ b/tests/components/jewish_calendar/test_binary_sensor.py @@ -4,6 +4,7 @@ from datetime import datetime as dt, timedelta import pytest from homeassistant.components import jewish_calendar +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component @@ -302,3 +303,15 @@ async def test_issur_melacha_sensor_update( hass.states.get("binary_sensor.test_issur_melacha_in_effect").state == result[1] ) + + +async def test_no_discovery_info(hass, caplog): + """Test setup without discovery info.""" + assert BINARY_SENSOR_DOMAIN not in hass.config.components + assert await async_setup_component( + hass, + BINARY_SENSOR_DOMAIN, + {BINARY_SENSOR_DOMAIN: {"platform": jewish_calendar.DOMAIN}}, + ) + await hass.async_block_till_done() + assert BINARY_SENSOR_DOMAIN in hass.config.components diff --git a/tests/components/jewish_calendar/test_sensor.py b/tests/components/jewish_calendar/test_sensor.py index d7d62a7ac97..a5dae8b4ce7 100644 --- a/tests/components/jewish_calendar/test_sensor.py +++ b/tests/components/jewish_calendar/test_sensor.py @@ -4,6 +4,7 @@ from datetime import datetime as dt, timedelta import pytest from homeassistant.components import jewish_calendar +from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -639,3 +640,15 @@ async def test_dafyomi_sensor(hass, test_time, result): await hass.async_block_till_done() assert hass.states.get("sensor.test_daf_yomi").state == result + + +async def test_no_discovery_info(hass, caplog): + """Test setup without discovery info.""" + assert SENSOR_DOMAIN not in hass.config.components + assert await async_setup_component( + hass, + SENSOR_DOMAIN, + {SENSOR_DOMAIN: {"platform": jewish_calendar.DOMAIN}}, + ) + await hass.async_block_till_done() + assert SENSOR_DOMAIN in hass.config.components From 40cf75844a99794763fbdc3d6915b559bc2e493e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 May 2022 11:02:20 -0700 Subject: [PATCH 162/930] Add media source support to Bose Soundtouch (#71209) --- .../components/soundtouch/media_player.py | 20 +++++++++++++++++++ .../soundtouch/test_media_player.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py index 7081f4a3a0f..3172eb4aed6 100644 --- a/homeassistant/components/soundtouch/media_player.py +++ b/homeassistant/components/soundtouch/media_player.py @@ -1,6 +1,7 @@ """Support for interface with a Bose Soundtouch.""" from __future__ import annotations +from functools import partial import logging import re @@ -8,11 +9,15 @@ from libsoundtouch import soundtouch_device from libsoundtouch.utils import Source import voluptuous as vol +from homeassistant.components import media_source from homeassistant.components.media_player import ( PLATFORM_SCHEMA, MediaPlayerEntity, MediaPlayerEntityFeature, ) +from homeassistant.components.media_player.browse_media import ( + async_process_play_media_url, +) from homeassistant.const import ( CONF_HOST, CONF_NAME, @@ -190,6 +195,7 @@ class SoundTouchDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.SELECT_SOURCE + | MediaPlayerEntityFeature.BROWSE_MEDIA ) def __init__(self, name, config): @@ -348,6 +354,16 @@ class SoundTouchDevice(MediaPlayerEntity): EVENT_HOMEASSISTANT_START, async_update_on_start ) + async def async_play_media(self, media_type, media_id, **kwargs): + """Play a piece of media.""" + if media_source.is_media_source_id(media_id): + play_item = await media_source.async_resolve_media(self.hass, media_id) + media_id = async_process_play_media_url(self.hass, play_item.url) + + await self.hass.async_add_executor_job( + partial(self.play_media, media_type, media_id, **kwargs) + ) + def play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" _LOGGER.debug("Starting media with media_id: %s", media_id) @@ -450,6 +466,10 @@ class SoundTouchDevice(MediaPlayerEntity): return attributes + async def async_browse_media(self, media_content_type=None, media_content_id=None): + """Implement the websocket media browsing helper.""" + return await media_source.async_browse_media(self.hass, media_content_id) + def get_zone_info(self): """Return the current zone info.""" zone_status = self._device.zone_status() diff --git a/tests/components/soundtouch/test_media_player.py b/tests/components/soundtouch/test_media_player.py index 4ed8a648c77..797b5b440d1 100644 --- a/tests/components/soundtouch/test_media_player.py +++ b/tests/components/soundtouch/test_media_player.py @@ -488,7 +488,7 @@ async def test_media_commands(mocked_status, mocked_volume, hass, one_device): assert mocked_volume.call_count == 2 entity_1_state = hass.states.get("media_player.soundtouch_1") - assert entity_1_state.attributes["supported_features"] == 20413 + assert entity_1_state.attributes["supported_features"] == 151485 @patch("libsoundtouch.device.SoundTouchDevice.power_off") From 188040b8bb7ae1d0776e686504bae6945f8d55c2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 17:17:21 -0500 Subject: [PATCH 163/930] Use lambda_stmt for recorder queries and migrate them to queries module (#71219) --- homeassistant/components/recorder/__init__.py | 47 +- homeassistant/components/recorder/purge.py | 455 +---------------- homeassistant/components/recorder/queries.py | 462 ++++++++++++++++++ tests/components/recorder/test_init.py | 2 - 4 files changed, 481 insertions(+), 485 deletions(-) create mode 100644 homeassistant/components/recorder/queries.py diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 486866fc799..35a664cac65 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -14,19 +14,10 @@ import time from typing import Any, TypeVar, cast from lru import LRU # pylint: disable=no-name-in-module -from sqlalchemy import ( - bindparam, - create_engine, - event as sqlalchemy_event, - exc, - func, - select, -) +from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select from sqlalchemy.engine import Engine from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.ext import baked from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session import voluptuous as vol @@ -90,6 +81,7 @@ from .models import ( process_timestamp, ) from .pool import POOL_SIZE, MutexPool, RecorderPool +from .queries import find_shared_attributes_id, find_shared_data_id from .run_history import RunHistory from .util import ( dburl_to_path, @@ -292,7 +284,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: entity_filter=entity_filter, exclude_t=exclude_t, exclude_attributes_by_domain=exclude_attributes_by_domain, - bakery=baked.bakery(), ) instance.async_initialize() instance.async_register() @@ -614,7 +605,6 @@ class Recorder(threading.Thread): entity_filter: Callable[[str], bool], exclude_t: list[str], exclude_attributes_by_domain: dict[str, set[str]], - bakery: baked.bakery, ) -> None: """Initialize the recorder.""" threading.Thread.__init__(self, name="Recorder") @@ -645,9 +635,6 @@ class Recorder(threading.Thread): self._pending_state_attributes: dict[str, StateAttributes] = {} self._pending_event_data: dict[str, EventData] = {} self._pending_expunge: list[States] = [] - self._bakery = bakery - self._find_shared_attr_query: Query | None = None - self._find_shared_data_query: Query | None = None self.event_session: Session | None = None self.get_session: Callable[[], Session] | None = None self._completed_first_database_setup: bool | None = None @@ -1152,19 +1139,11 @@ class Recorder(threading.Thread): # need to flush before checking the database. # assert self.event_session is not None - if self._find_shared_attr_query is None: - self._find_shared_attr_query = self._bakery( - lambda session: session.query(StateAttributes.attributes_id) - .filter(StateAttributes.hash == bindparam("attr_hash")) - .filter(StateAttributes.shared_attrs == bindparam("shared_attrs")) - ) with self.event_session.no_autoflush: - if ( - attributes := self._find_shared_attr_query(self.event_session) - .params(attr_hash=attr_hash, shared_attrs=shared_attrs) - .first() - ): - return cast(int, attributes[0]) + if attributes_id := self.event_session.execute( + find_shared_attributes_id(attr_hash, shared_attrs) + ).first(): + return cast(int, attributes_id[0]) return None def _find_shared_data_in_db(self, data_hash: int, shared_data: str) -> int | None: @@ -1178,18 +1157,10 @@ class Recorder(threading.Thread): # need to flush before checking the database. # assert self.event_session is not None - if self._find_shared_data_query is None: - self._find_shared_data_query = self._bakery( - lambda session: session.query(EventData.data_id) - .filter(EventData.hash == bindparam("data_hash")) - .filter(EventData.shared_data == bindparam("shared_data")) - ) with self.event_session.no_autoflush: - if ( - data_id := self._find_shared_data_query(self.event_session) - .params(data_hash=data_hash, shared_data=shared_data) - .first() - ): + if data_id := self.event_session.execute( + find_shared_data_id(data_hash, shared_data) + ).first(): return cast(int, data_id[0]) return None diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index dcc535c8177..3a0e2e6e141 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -7,11 +7,9 @@ from itertools import zip_longest import logging from typing import TYPE_CHECKING -from sqlalchemy import func, lambda_stmt, select, union_all +from sqlalchemy import func from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import distinct -from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select from homeassistant.const import EVENT_STATE_CHANGED @@ -25,6 +23,7 @@ from .models import ( StatisticsRuns, StatisticsShortTerm, ) +from .queries import attributes_ids_exist_in_states, data_ids_exist_in_events from .repack import repack_database from .util import retryable_database_job, session_scope @@ -131,223 +130,6 @@ def _select_event_state_attributes_ids_data_ids_to_purge( return event_ids, state_ids, attributes_ids, data_ids -def _state_attrs_exist(attr: int | None) -> Select: - """Check if a state attributes id exists in the states table.""" - return select(func.min(States.attributes_id)).where(States.attributes_id == attr) - - -def _generate_find_attr_lambda( - attr1: int, - attr2: int | None, - attr3: int | None, - attr4: int | None, - attr5: int | None, - attr6: int | None, - attr7: int | None, - attr8: int | None, - attr9: int | None, - attr10: int | None, - attr11: int | None, - attr12: int | None, - attr13: int | None, - attr14: int | None, - attr15: int | None, - attr16: int | None, - attr17: int | None, - attr18: int | None, - attr19: int | None, - attr20: int | None, - attr21: int | None, - attr22: int | None, - attr23: int | None, - attr24: int | None, - attr25: int | None, - attr26: int | None, - attr27: int | None, - attr28: int | None, - attr29: int | None, - attr30: int | None, - attr31: int | None, - attr32: int | None, - attr33: int | None, - attr34: int | None, - attr35: int | None, - attr36: int | None, - attr37: int | None, - attr38: int | None, - attr39: int | None, - attr40: int | None, - attr41: int | None, - attr42: int | None, - attr43: int | None, - attr44: int | None, - attr45: int | None, - attr46: int | None, - attr47: int | None, - attr48: int | None, - attr49: int | None, - attr50: int | None, - attr51: int | None, - attr52: int | None, - attr53: int | None, - attr54: int | None, - attr55: int | None, - attr56: int | None, - attr57: int | None, - attr58: int | None, - attr59: int | None, - attr60: int | None, - attr61: int | None, - attr62: int | None, - attr63: int | None, - attr64: int | None, - attr65: int | None, - attr66: int | None, - attr67: int | None, - attr68: int | None, - attr69: int | None, - attr70: int | None, - attr71: int | None, - attr72: int | None, - attr73: int | None, - attr74: int | None, - attr75: int | None, - attr76: int | None, - attr77: int | None, - attr78: int | None, - attr79: int | None, - attr80: int | None, - attr81: int | None, - attr82: int | None, - attr83: int | None, - attr84: int | None, - attr85: int | None, - attr86: int | None, - attr87: int | None, - attr88: int | None, - attr89: int | None, - attr90: int | None, - attr91: int | None, - attr92: int | None, - attr93: int | None, - attr94: int | None, - attr95: int | None, - attr96: int | None, - attr97: int | None, - attr98: int | None, - attr99: int | None, - attr100: int | None, -) -> StatementLambdaElement: - """Generate the find attributes select only once. - - https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas - """ - return lambda_stmt( - lambda: union_all( - _state_attrs_exist(attr1), - _state_attrs_exist(attr2), - _state_attrs_exist(attr3), - _state_attrs_exist(attr4), - _state_attrs_exist(attr5), - _state_attrs_exist(attr6), - _state_attrs_exist(attr7), - _state_attrs_exist(attr8), - _state_attrs_exist(attr9), - _state_attrs_exist(attr10), - _state_attrs_exist(attr11), - _state_attrs_exist(attr12), - _state_attrs_exist(attr13), - _state_attrs_exist(attr14), - _state_attrs_exist(attr15), - _state_attrs_exist(attr16), - _state_attrs_exist(attr17), - _state_attrs_exist(attr18), - _state_attrs_exist(attr19), - _state_attrs_exist(attr20), - _state_attrs_exist(attr21), - _state_attrs_exist(attr22), - _state_attrs_exist(attr23), - _state_attrs_exist(attr24), - _state_attrs_exist(attr25), - _state_attrs_exist(attr26), - _state_attrs_exist(attr27), - _state_attrs_exist(attr28), - _state_attrs_exist(attr29), - _state_attrs_exist(attr30), - _state_attrs_exist(attr31), - _state_attrs_exist(attr32), - _state_attrs_exist(attr33), - _state_attrs_exist(attr34), - _state_attrs_exist(attr35), - _state_attrs_exist(attr36), - _state_attrs_exist(attr37), - _state_attrs_exist(attr38), - _state_attrs_exist(attr39), - _state_attrs_exist(attr40), - _state_attrs_exist(attr41), - _state_attrs_exist(attr42), - _state_attrs_exist(attr43), - _state_attrs_exist(attr44), - _state_attrs_exist(attr45), - _state_attrs_exist(attr46), - _state_attrs_exist(attr47), - _state_attrs_exist(attr48), - _state_attrs_exist(attr49), - _state_attrs_exist(attr50), - _state_attrs_exist(attr51), - _state_attrs_exist(attr52), - _state_attrs_exist(attr53), - _state_attrs_exist(attr54), - _state_attrs_exist(attr55), - _state_attrs_exist(attr56), - _state_attrs_exist(attr57), - _state_attrs_exist(attr58), - _state_attrs_exist(attr59), - _state_attrs_exist(attr60), - _state_attrs_exist(attr61), - _state_attrs_exist(attr62), - _state_attrs_exist(attr63), - _state_attrs_exist(attr64), - _state_attrs_exist(attr65), - _state_attrs_exist(attr66), - _state_attrs_exist(attr67), - _state_attrs_exist(attr68), - _state_attrs_exist(attr69), - _state_attrs_exist(attr70), - _state_attrs_exist(attr71), - _state_attrs_exist(attr72), - _state_attrs_exist(attr73), - _state_attrs_exist(attr74), - _state_attrs_exist(attr75), - _state_attrs_exist(attr76), - _state_attrs_exist(attr77), - _state_attrs_exist(attr78), - _state_attrs_exist(attr79), - _state_attrs_exist(attr80), - _state_attrs_exist(attr81), - _state_attrs_exist(attr82), - _state_attrs_exist(attr83), - _state_attrs_exist(attr84), - _state_attrs_exist(attr85), - _state_attrs_exist(attr86), - _state_attrs_exist(attr87), - _state_attrs_exist(attr88), - _state_attrs_exist(attr89), - _state_attrs_exist(attr90), - _state_attrs_exist(attr91), - _state_attrs_exist(attr92), - _state_attrs_exist(attr93), - _state_attrs_exist(attr94), - _state_attrs_exist(attr95), - _state_attrs_exist(attr96), - _state_attrs_exist(attr97), - _state_attrs_exist(attr98), - _state_attrs_exist(attr99), - _state_attrs_exist(attr100), - ) - ) - - def _select_unused_attributes_ids( session: Session, attributes_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -402,11 +184,11 @@ def _select_unused_attributes_ids( groups = [iter(attributes_ids)] * 100 for attr_ids in zip_longest(*groups, fillvalue=None): seen_ids |= { - state[0] - for state in session.execute( - _generate_find_attr_lambda(*attr_ids) + attrs_id[0] + for attrs_id in session.execute( + attributes_ids_exist_in_states(*attr_ids) ).all() - if state[0] is not None + if attrs_id[0] is not None } to_remove = attributes_ids - seen_ids _LOGGER.debug( @@ -416,223 +198,6 @@ def _select_unused_attributes_ids( return to_remove -def _event_data_id_exist(data_id: int | None) -> Select: - """Check if a event data id exists in the events table.""" - return select(func.min(Events.data_id)).where(Events.data_id == data_id) - - -def _generate_find_data_id_lambda( - id1: int, - id2: int | None, - id3: int | None, - id4: int | None, - id5: int | None, - id6: int | None, - id7: int | None, - id8: int | None, - id9: int | None, - id10: int | None, - id11: int | None, - id12: int | None, - id13: int | None, - id14: int | None, - id15: int | None, - id16: int | None, - id17: int | None, - id18: int | None, - id19: int | None, - id20: int | None, - id21: int | None, - id22: int | None, - id23: int | None, - id24: int | None, - id25: int | None, - id26: int | None, - id27: int | None, - id28: int | None, - id29: int | None, - id30: int | None, - id31: int | None, - id32: int | None, - id33: int | None, - id34: int | None, - id35: int | None, - id36: int | None, - id37: int | None, - id38: int | None, - id39: int | None, - id40: int | None, - id41: int | None, - id42: int | None, - id43: int | None, - id44: int | None, - id45: int | None, - id46: int | None, - id47: int | None, - id48: int | None, - id49: int | None, - id50: int | None, - id51: int | None, - id52: int | None, - id53: int | None, - id54: int | None, - id55: int | None, - id56: int | None, - id57: int | None, - id58: int | None, - id59: int | None, - id60: int | None, - id61: int | None, - id62: int | None, - id63: int | None, - id64: int | None, - id65: int | None, - id66: int | None, - id67: int | None, - id68: int | None, - id69: int | None, - id70: int | None, - id71: int | None, - id72: int | None, - id73: int | None, - id74: int | None, - id75: int | None, - id76: int | None, - id77: int | None, - id78: int | None, - id79: int | None, - id80: int | None, - id81: int | None, - id82: int | None, - id83: int | None, - id84: int | None, - id85: int | None, - id86: int | None, - id87: int | None, - id88: int | None, - id89: int | None, - id90: int | None, - id91: int | None, - id92: int | None, - id93: int | None, - id94: int | None, - id95: int | None, - id96: int | None, - id97: int | None, - id98: int | None, - id99: int | None, - id100: int | None, -) -> StatementLambdaElement: - """Generate the find event data select only once. - - https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas - """ - return lambda_stmt( - lambda: union_all( - _event_data_id_exist(id1), - _event_data_id_exist(id2), - _event_data_id_exist(id3), - _event_data_id_exist(id4), - _event_data_id_exist(id5), - _event_data_id_exist(id6), - _event_data_id_exist(id7), - _event_data_id_exist(id8), - _event_data_id_exist(id9), - _event_data_id_exist(id10), - _event_data_id_exist(id11), - _event_data_id_exist(id12), - _event_data_id_exist(id13), - _event_data_id_exist(id14), - _event_data_id_exist(id15), - _event_data_id_exist(id16), - _event_data_id_exist(id17), - _event_data_id_exist(id18), - _event_data_id_exist(id19), - _event_data_id_exist(id20), - _event_data_id_exist(id21), - _event_data_id_exist(id22), - _event_data_id_exist(id23), - _event_data_id_exist(id24), - _event_data_id_exist(id25), - _event_data_id_exist(id26), - _event_data_id_exist(id27), - _event_data_id_exist(id28), - _event_data_id_exist(id29), - _event_data_id_exist(id30), - _event_data_id_exist(id31), - _event_data_id_exist(id32), - _event_data_id_exist(id33), - _event_data_id_exist(id34), - _event_data_id_exist(id35), - _event_data_id_exist(id36), - _event_data_id_exist(id37), - _event_data_id_exist(id38), - _event_data_id_exist(id39), - _event_data_id_exist(id40), - _event_data_id_exist(id41), - _event_data_id_exist(id42), - _event_data_id_exist(id43), - _event_data_id_exist(id44), - _event_data_id_exist(id45), - _event_data_id_exist(id46), - _event_data_id_exist(id47), - _event_data_id_exist(id48), - _event_data_id_exist(id49), - _event_data_id_exist(id50), - _event_data_id_exist(id51), - _event_data_id_exist(id52), - _event_data_id_exist(id53), - _event_data_id_exist(id54), - _event_data_id_exist(id55), - _event_data_id_exist(id56), - _event_data_id_exist(id57), - _event_data_id_exist(id58), - _event_data_id_exist(id59), - _event_data_id_exist(id60), - _event_data_id_exist(id61), - _event_data_id_exist(id62), - _event_data_id_exist(id63), - _event_data_id_exist(id64), - _event_data_id_exist(id65), - _event_data_id_exist(id66), - _event_data_id_exist(id67), - _event_data_id_exist(id68), - _event_data_id_exist(id69), - _event_data_id_exist(id70), - _event_data_id_exist(id71), - _event_data_id_exist(id72), - _event_data_id_exist(id73), - _event_data_id_exist(id74), - _event_data_id_exist(id75), - _event_data_id_exist(id76), - _event_data_id_exist(id77), - _event_data_id_exist(id78), - _event_data_id_exist(id79), - _event_data_id_exist(id80), - _event_data_id_exist(id81), - _event_data_id_exist(id82), - _event_data_id_exist(id83), - _event_data_id_exist(id84), - _event_data_id_exist(id85), - _event_data_id_exist(id86), - _event_data_id_exist(id87), - _event_data_id_exist(id88), - _event_data_id_exist(id89), - _event_data_id_exist(id90), - _event_data_id_exist(id91), - _event_data_id_exist(id92), - _event_data_id_exist(id93), - _event_data_id_exist(id94), - _event_data_id_exist(id95), - _event_data_id_exist(id96), - _event_data_id_exist(id97), - _event_data_id_exist(id98), - _event_data_id_exist(id99), - _event_data_id_exist(id100), - ) - ) - - def _select_unused_event_data_ids( session: Session, data_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -654,11 +219,11 @@ def _select_unused_event_data_ids( groups = [iter(data_ids)] * 100 for data_ids_group in zip_longest(*groups, fillvalue=None): seen_ids |= { - state[0] - for state in session.execute( - _generate_find_data_id_lambda(*data_ids_group) + data_id[0] + for data_id in session.execute( + data_ids_exist_in_events(*data_ids_group) ).all() - if state[0] is not None + if data_id[0] is not None } to_remove = data_ids - seen_ids _LOGGER.debug("Selected %s shared event data to remove", len(to_remove)) diff --git a/homeassistant/components/recorder/queries.py b/homeassistant/components/recorder/queries.py new file mode 100644 index 00000000000..ff4662e27b1 --- /dev/null +++ b/homeassistant/components/recorder/queries.py @@ -0,0 +1,462 @@ +"""Queries for the recorder.""" +from __future__ import annotations + +from sqlalchemy import func, lambda_stmt, select, union_all +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from .models import EventData, Events, StateAttributes, States + + +def find_shared_attributes_id( + data_hash: int, shared_attrs: str +) -> StatementLambdaElement: + """Find an attributes_id by hash and shared_attrs.""" + return lambda_stmt( + lambda: select(StateAttributes.attributes_id) + .filter(StateAttributes.hash == data_hash) + .filter(StateAttributes.shared_attrs == shared_attrs) + ) + + +def find_shared_data_id(attr_hash: int, shared_data: str) -> StatementLambdaElement: + """Find a data_id by hash and shared_data.""" + return lambda_stmt( + lambda: select(EventData.data_id) + .filter(EventData.hash == attr_hash) + .filter(EventData.shared_data == shared_data) + ) + + +def _state_attrs_exist(attr: int | None) -> Select: + """Check if a state attributes id exists in the states table.""" + return select(func.min(States.attributes_id)).where(States.attributes_id == attr) + + +def attributes_ids_exist_in_states( + attr1: int, + attr2: int | None, + attr3: int | None, + attr4: int | None, + attr5: int | None, + attr6: int | None, + attr7: int | None, + attr8: int | None, + attr9: int | None, + attr10: int | None, + attr11: int | None, + attr12: int | None, + attr13: int | None, + attr14: int | None, + attr15: int | None, + attr16: int | None, + attr17: int | None, + attr18: int | None, + attr19: int | None, + attr20: int | None, + attr21: int | None, + attr22: int | None, + attr23: int | None, + attr24: int | None, + attr25: int | None, + attr26: int | None, + attr27: int | None, + attr28: int | None, + attr29: int | None, + attr30: int | None, + attr31: int | None, + attr32: int | None, + attr33: int | None, + attr34: int | None, + attr35: int | None, + attr36: int | None, + attr37: int | None, + attr38: int | None, + attr39: int | None, + attr40: int | None, + attr41: int | None, + attr42: int | None, + attr43: int | None, + attr44: int | None, + attr45: int | None, + attr46: int | None, + attr47: int | None, + attr48: int | None, + attr49: int | None, + attr50: int | None, + attr51: int | None, + attr52: int | None, + attr53: int | None, + attr54: int | None, + attr55: int | None, + attr56: int | None, + attr57: int | None, + attr58: int | None, + attr59: int | None, + attr60: int | None, + attr61: int | None, + attr62: int | None, + attr63: int | None, + attr64: int | None, + attr65: int | None, + attr66: int | None, + attr67: int | None, + attr68: int | None, + attr69: int | None, + attr70: int | None, + attr71: int | None, + attr72: int | None, + attr73: int | None, + attr74: int | None, + attr75: int | None, + attr76: int | None, + attr77: int | None, + attr78: int | None, + attr79: int | None, + attr80: int | None, + attr81: int | None, + attr82: int | None, + attr83: int | None, + attr84: int | None, + attr85: int | None, + attr86: int | None, + attr87: int | None, + attr88: int | None, + attr89: int | None, + attr90: int | None, + attr91: int | None, + attr92: int | None, + attr93: int | None, + attr94: int | None, + attr95: int | None, + attr96: int | None, + attr97: int | None, + attr98: int | None, + attr99: int | None, + attr100: int | None, +) -> StatementLambdaElement: + """Generate the find attributes select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _state_attrs_exist(attr1), + _state_attrs_exist(attr2), + _state_attrs_exist(attr3), + _state_attrs_exist(attr4), + _state_attrs_exist(attr5), + _state_attrs_exist(attr6), + _state_attrs_exist(attr7), + _state_attrs_exist(attr8), + _state_attrs_exist(attr9), + _state_attrs_exist(attr10), + _state_attrs_exist(attr11), + _state_attrs_exist(attr12), + _state_attrs_exist(attr13), + _state_attrs_exist(attr14), + _state_attrs_exist(attr15), + _state_attrs_exist(attr16), + _state_attrs_exist(attr17), + _state_attrs_exist(attr18), + _state_attrs_exist(attr19), + _state_attrs_exist(attr20), + _state_attrs_exist(attr21), + _state_attrs_exist(attr22), + _state_attrs_exist(attr23), + _state_attrs_exist(attr24), + _state_attrs_exist(attr25), + _state_attrs_exist(attr26), + _state_attrs_exist(attr27), + _state_attrs_exist(attr28), + _state_attrs_exist(attr29), + _state_attrs_exist(attr30), + _state_attrs_exist(attr31), + _state_attrs_exist(attr32), + _state_attrs_exist(attr33), + _state_attrs_exist(attr34), + _state_attrs_exist(attr35), + _state_attrs_exist(attr36), + _state_attrs_exist(attr37), + _state_attrs_exist(attr38), + _state_attrs_exist(attr39), + _state_attrs_exist(attr40), + _state_attrs_exist(attr41), + _state_attrs_exist(attr42), + _state_attrs_exist(attr43), + _state_attrs_exist(attr44), + _state_attrs_exist(attr45), + _state_attrs_exist(attr46), + _state_attrs_exist(attr47), + _state_attrs_exist(attr48), + _state_attrs_exist(attr49), + _state_attrs_exist(attr50), + _state_attrs_exist(attr51), + _state_attrs_exist(attr52), + _state_attrs_exist(attr53), + _state_attrs_exist(attr54), + _state_attrs_exist(attr55), + _state_attrs_exist(attr56), + _state_attrs_exist(attr57), + _state_attrs_exist(attr58), + _state_attrs_exist(attr59), + _state_attrs_exist(attr60), + _state_attrs_exist(attr61), + _state_attrs_exist(attr62), + _state_attrs_exist(attr63), + _state_attrs_exist(attr64), + _state_attrs_exist(attr65), + _state_attrs_exist(attr66), + _state_attrs_exist(attr67), + _state_attrs_exist(attr68), + _state_attrs_exist(attr69), + _state_attrs_exist(attr70), + _state_attrs_exist(attr71), + _state_attrs_exist(attr72), + _state_attrs_exist(attr73), + _state_attrs_exist(attr74), + _state_attrs_exist(attr75), + _state_attrs_exist(attr76), + _state_attrs_exist(attr77), + _state_attrs_exist(attr78), + _state_attrs_exist(attr79), + _state_attrs_exist(attr80), + _state_attrs_exist(attr81), + _state_attrs_exist(attr82), + _state_attrs_exist(attr83), + _state_attrs_exist(attr84), + _state_attrs_exist(attr85), + _state_attrs_exist(attr86), + _state_attrs_exist(attr87), + _state_attrs_exist(attr88), + _state_attrs_exist(attr89), + _state_attrs_exist(attr90), + _state_attrs_exist(attr91), + _state_attrs_exist(attr92), + _state_attrs_exist(attr93), + _state_attrs_exist(attr94), + _state_attrs_exist(attr95), + _state_attrs_exist(attr96), + _state_attrs_exist(attr97), + _state_attrs_exist(attr98), + _state_attrs_exist(attr99), + _state_attrs_exist(attr100), + ) + ) + + +def _event_data_id_exist(data_id: int | None) -> Select: + """Check if a event data id exists in the events table.""" + return select(func.min(Events.data_id)).where(Events.data_id == data_id) + + +def data_ids_exist_in_events( + id1: int, + id2: int | None, + id3: int | None, + id4: int | None, + id5: int | None, + id6: int | None, + id7: int | None, + id8: int | None, + id9: int | None, + id10: int | None, + id11: int | None, + id12: int | None, + id13: int | None, + id14: int | None, + id15: int | None, + id16: int | None, + id17: int | None, + id18: int | None, + id19: int | None, + id20: int | None, + id21: int | None, + id22: int | None, + id23: int | None, + id24: int | None, + id25: int | None, + id26: int | None, + id27: int | None, + id28: int | None, + id29: int | None, + id30: int | None, + id31: int | None, + id32: int | None, + id33: int | None, + id34: int | None, + id35: int | None, + id36: int | None, + id37: int | None, + id38: int | None, + id39: int | None, + id40: int | None, + id41: int | None, + id42: int | None, + id43: int | None, + id44: int | None, + id45: int | None, + id46: int | None, + id47: int | None, + id48: int | None, + id49: int | None, + id50: int | None, + id51: int | None, + id52: int | None, + id53: int | None, + id54: int | None, + id55: int | None, + id56: int | None, + id57: int | None, + id58: int | None, + id59: int | None, + id60: int | None, + id61: int | None, + id62: int | None, + id63: int | None, + id64: int | None, + id65: int | None, + id66: int | None, + id67: int | None, + id68: int | None, + id69: int | None, + id70: int | None, + id71: int | None, + id72: int | None, + id73: int | None, + id74: int | None, + id75: int | None, + id76: int | None, + id77: int | None, + id78: int | None, + id79: int | None, + id80: int | None, + id81: int | None, + id82: int | None, + id83: int | None, + id84: int | None, + id85: int | None, + id86: int | None, + id87: int | None, + id88: int | None, + id89: int | None, + id90: int | None, + id91: int | None, + id92: int | None, + id93: int | None, + id94: int | None, + id95: int | None, + id96: int | None, + id97: int | None, + id98: int | None, + id99: int | None, + id100: int | None, +) -> StatementLambdaElement: + """Generate the find event data select only once. + + https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas + """ + return lambda_stmt( + lambda: union_all( + _event_data_id_exist(id1), + _event_data_id_exist(id2), + _event_data_id_exist(id3), + _event_data_id_exist(id4), + _event_data_id_exist(id5), + _event_data_id_exist(id6), + _event_data_id_exist(id7), + _event_data_id_exist(id8), + _event_data_id_exist(id9), + _event_data_id_exist(id10), + _event_data_id_exist(id11), + _event_data_id_exist(id12), + _event_data_id_exist(id13), + _event_data_id_exist(id14), + _event_data_id_exist(id15), + _event_data_id_exist(id16), + _event_data_id_exist(id17), + _event_data_id_exist(id18), + _event_data_id_exist(id19), + _event_data_id_exist(id20), + _event_data_id_exist(id21), + _event_data_id_exist(id22), + _event_data_id_exist(id23), + _event_data_id_exist(id24), + _event_data_id_exist(id25), + _event_data_id_exist(id26), + _event_data_id_exist(id27), + _event_data_id_exist(id28), + _event_data_id_exist(id29), + _event_data_id_exist(id30), + _event_data_id_exist(id31), + _event_data_id_exist(id32), + _event_data_id_exist(id33), + _event_data_id_exist(id34), + _event_data_id_exist(id35), + _event_data_id_exist(id36), + _event_data_id_exist(id37), + _event_data_id_exist(id38), + _event_data_id_exist(id39), + _event_data_id_exist(id40), + _event_data_id_exist(id41), + _event_data_id_exist(id42), + _event_data_id_exist(id43), + _event_data_id_exist(id44), + _event_data_id_exist(id45), + _event_data_id_exist(id46), + _event_data_id_exist(id47), + _event_data_id_exist(id48), + _event_data_id_exist(id49), + _event_data_id_exist(id50), + _event_data_id_exist(id51), + _event_data_id_exist(id52), + _event_data_id_exist(id53), + _event_data_id_exist(id54), + _event_data_id_exist(id55), + _event_data_id_exist(id56), + _event_data_id_exist(id57), + _event_data_id_exist(id58), + _event_data_id_exist(id59), + _event_data_id_exist(id60), + _event_data_id_exist(id61), + _event_data_id_exist(id62), + _event_data_id_exist(id63), + _event_data_id_exist(id64), + _event_data_id_exist(id65), + _event_data_id_exist(id66), + _event_data_id_exist(id67), + _event_data_id_exist(id68), + _event_data_id_exist(id69), + _event_data_id_exist(id70), + _event_data_id_exist(id71), + _event_data_id_exist(id72), + _event_data_id_exist(id73), + _event_data_id_exist(id74), + _event_data_id_exist(id75), + _event_data_id_exist(id76), + _event_data_id_exist(id77), + _event_data_id_exist(id78), + _event_data_id_exist(id79), + _event_data_id_exist(id80), + _event_data_id_exist(id81), + _event_data_id_exist(id82), + _event_data_id_exist(id83), + _event_data_id_exist(id84), + _event_data_id_exist(id85), + _event_data_id_exist(id86), + _event_data_id_exist(id87), + _event_data_id_exist(id88), + _event_data_id_exist(id89), + _event_data_id_exist(id90), + _event_data_id_exist(id91), + _event_data_id_exist(id92), + _event_data_id_exist(id93), + _event_data_id_exist(id94), + _event_data_id_exist(id95), + _event_data_id_exist(id96), + _event_data_id_exist(id97), + _event_data_id_exist(id98), + _event_data_id_exist(id99), + _event_data_id_exist(id100), + ) + ) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 423e8eb2f48..0b03283547e 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -11,7 +11,6 @@ from unittest.mock import Mock, patch import pytest from sqlalchemy.exc import DatabaseError, OperationalError, SQLAlchemyError -from sqlalchemy.ext import baked from homeassistant.components import recorder from homeassistant.components.recorder import ( @@ -82,7 +81,6 @@ def _default_recorder(hass): entity_filter=CONFIG_SCHEMA({DOMAIN: {}}), exclude_t=[], exclude_attributes_by_domain={}, - bakery=baked.bakery(), ) From ea456893f94c7dc88b0cc28f92dadf240fbb1fe7 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Tue, 3 May 2022 00:18:38 +0200 Subject: [PATCH 164/930] Review AndroidTV tests for media player entity (#71168) --- .coveragerc | 1 - tests/components/androidtv/patchers.py | 84 +- .../components/androidtv/test_config_flow.py | 12 +- .../components/androidtv/test_media_player.py | 743 ++++++++---------- 4 files changed, 372 insertions(+), 468 deletions(-) diff --git a/.coveragerc b/.coveragerc index b7433ecf58a..3464b0df1be 100644 --- a/.coveragerc +++ b/.coveragerc @@ -58,7 +58,6 @@ omit = homeassistant/components/amcrest/* homeassistant/components/ampio/* homeassistant/components/android_ip_webcam/* - homeassistant/components/androidtv/__init__.py homeassistant/components/androidtv/diagnostics.py homeassistant/components/anel_pwrctrl/switch.py homeassistant/components/anthemav/media_player.py diff --git a/tests/components/androidtv/patchers.py b/tests/components/androidtv/patchers.py index 7cc14bbd7b5..31e9a9c82c3 100644 --- a/tests/components/androidtv/patchers.py +++ b/tests/components/androidtv/patchers.py @@ -1,8 +1,15 @@ """Define patches used for androidtv tests.""" -from unittest.mock import mock_open, patch +from unittest.mock import patch from androidtv.constants import CMD_DEVICE_PROPERTIES, CMD_MAC_ETH0, CMD_MAC_WLAN0 +from homeassistant.components.androidtv.const import ( + DEFAULT_ADB_SERVER_PORT, + DEVICE_ANDROIDTV, + DEVICE_FIRETV, +) + +ADB_SERVER_HOST = "127.0.0.1" KEY_PYTHON = "python" KEY_SERVER = "server" @@ -36,7 +43,7 @@ class AdbDeviceTcpAsyncFake: class ClientAsyncFakeSuccess: """A fake of the `ClientAsync` class when the connection and shell commands succeed.""" - def __init__(self, host="127.0.0.1", port=5037): + def __init__(self, host=ADB_SERVER_HOST, port=DEFAULT_ADB_SERVER_PORT): """Initialize a `ClientAsyncFakeSuccess` instance.""" self._devices = [] @@ -50,7 +57,7 @@ class ClientAsyncFakeSuccess: class ClientAsyncFakeFail: """A fake of the `ClientAsync` class when the connection and shell commands fail.""" - def __init__(self, host="127.0.0.1", port=5037): + def __init__(self, host=ADB_SERVER_HOST, port=DEFAULT_ADB_SERVER_PORT): """Initialize a `ClientAsyncFakeFail` instance.""" self._devices = [] @@ -143,17 +150,34 @@ def patch_shell(response=None, error=False, mac_eth=False): } -PATCH_ADB_DEVICE_TCP = patch( - "androidtv.adb_manager.adb_manager_async.AdbDeviceTcpAsync", AdbDeviceTcpAsyncFake -) -PATCH_ANDROIDTV_OPEN = patch( - "homeassistant.components.androidtv.media_player.open", mock_open() -) -PATCH_KEYGEN = patch("homeassistant.components.androidtv.keygen") -PATCH_SIGNER = patch( - "homeassistant.components.androidtv.ADBPythonSync.load_adbkey", - return_value="signer for testing", -) +def patch_androidtv_update( + state, + current_app, + running_apps, + device, + is_volume_muted, + volume_level, + hdmi_input, +): + """Patch the `AndroidTV.update()` method.""" + return { + DEVICE_ANDROIDTV: patch( + "androidtv.androidtv.androidtv_async.AndroidTVAsync.update", + return_value=( + state, + current_app, + running_apps, + device, + is_volume_muted, + volume_level, + hdmi_input, + ), + ), + DEVICE_FIRETV: patch( + "androidtv.firetv.firetv_async.FireTVAsync.update", + return_value=(state, current_app, running_apps, hdmi_input), + ), + } def isfile(filepath): @@ -161,32 +185,12 @@ def isfile(filepath): return filepath.endswith("adbkey") -def patch_firetv_update(state, current_app, running_apps, hdmi_input): - """Patch the `FireTV.update()` method.""" - return patch( - "androidtv.firetv.firetv_async.FireTVAsync.update", - return_value=(state, current_app, running_apps, hdmi_input), - ) - - -def patch_androidtv_update( - state, current_app, running_apps, device, is_volume_muted, volume_level, hdmi_input -): - """Patch the `AndroidTV.update()` method.""" - return patch( - "androidtv.androidtv.androidtv_async.AndroidTVAsync.update", - return_value=( - state, - current_app, - running_apps, - device, - is_volume_muted, - volume_level, - hdmi_input, - ), - ) - - +PATCH_SETUP_ENTRY = patch( + "homeassistant.components.androidtv.async_setup_entry", + return_value=True, +) +PATCH_ACCESS = patch("homeassistant.components.androidtv.os.access", return_value=True) +PATCH_ISFILE = patch("homeassistant.components.androidtv.os.path.isfile", isfile) PATCH_LAUNCH_APP = patch("androidtv.basetv.basetv_async.BaseTVAsync.launch_app") PATCH_STOP_APP = patch("androidtv.basetv.basetv_async.BaseTVAsync.stop_app") diff --git a/tests/components/androidtv/test_config_flow.py b/tests/components/androidtv/test_config_flow.py index aca308fd0e5..d5301f7ada3 100644 --- a/tests/components/androidtv/test_config_flow.py +++ b/tests/components/androidtv/test_config_flow.py @@ -36,7 +36,7 @@ from homeassistant.components.androidtv.const import ( from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT -from .patchers import isfile +from .patchers import PATCH_ACCESS, PATCH_ISFILE, PATCH_SETUP_ENTRY from tests.common import MockConfigEntry @@ -66,16 +66,6 @@ CONFIG_ADB_SERVER = { CONNECT_METHOD = ( "homeassistant.components.androidtv.config_flow.async_connect_androidtv" ) -PATCH_ACCESS = patch( - "homeassistant.components.androidtv.config_flow.os.access", return_value=True -) -PATCH_ISFILE = patch( - "homeassistant.components.androidtv.config_flow.os.path.isfile", isfile -) -PATCH_SETUP_ENTRY = patch( - "homeassistant.components.androidtv.async_setup_entry", - return_value=True, -) class MockConfigDevice: diff --git a/tests/components/androidtv/test_media_player.py b/tests/components/androidtv/test_media_player.py index aa27ee8f564..73f8de55cc9 100644 --- a/tests/components/androidtv/test_media_player.py +++ b/tests/components/androidtv/test_media_player.py @@ -1,8 +1,7 @@ """The tests for the androidtv platform.""" import base64 -import copy import logging -from unittest.mock import patch +from unittest.mock import Mock, patch from androidtv.constants import APPS as ANDROIDTV_APPS, KEYS from androidtv.exceptions import LockNotAcquiredException @@ -14,6 +13,8 @@ from homeassistant.components.androidtv.const import ( CONF_ADBKEY, CONF_APPS, CONF_EXCLUDE_UNNAMED_APPS, + CONF_SCREENCAP, + CONF_STATE_DETECTION_RULES, CONF_TURN_OFF_COMMAND, CONF_TURN_ON_COMMAND, DEFAULT_ADB_SERVER_PORT, @@ -52,6 +53,7 @@ from homeassistant.components.media_player import ( SERVICE_VOLUME_UP, ) from homeassistant.components.websocket_api.const import TYPE_RESULT +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( ATTR_COMMAND, ATTR_ENTITY_ID, @@ -72,16 +74,15 @@ from . import patchers from tests.common import MockConfigEntry -CONF_OPTIONS = "options" HOST = "127.0.0.1" ADB_PATCH_KEY = "patch_key" TEST_ENTITY_NAME = "entity_name" -PATCH_ACCESS = patch("homeassistant.components.androidtv.os.access", return_value=True) -PATCH_ISFILE = patch( - "homeassistant.components.androidtv.os.path.isfile", patchers.isfile -) +MSG_RECONNECT = { + patchers.KEY_PYTHON: f"ADB connection to {HOST}:{DEFAULT_PORT} successfully established", + patchers.KEY_SERVER: f"ADB connection to {HOST}:{DEFAULT_PORT} via ADB server {patchers.ADB_SERVER_HOST}:{DEFAULT_ADB_SERVER_PORT} successfully established", +} SHELL_RESPONSE_OFF = "" SHELL_RESPONSE_STANDBY = "1" @@ -107,6 +108,16 @@ CONFIG_ANDROIDTV_PYTHON_ADB_YAML = { }, } +# Android TV device with Python ADB implementation with custom adbkey +CONFIG_ANDROIDTV_PYTHON_ADB_KEY = { + ADB_PATCH_KEY: patchers.KEY_PYTHON, + TEST_ENTITY_NAME: CONFIG_ANDROIDTV_PYTHON_ADB[TEST_ENTITY_NAME], + DOMAIN: { + **CONFIG_ANDROIDTV_PYTHON_ADB[DOMAIN], + CONF_ADBKEY: "user_provided_adbkey", + }, +} + # Android TV device with ADB server CONFIG_ANDROIDTV_ADB_SERVER = { ADB_PATCH_KEY: patchers.KEY_SERVER, @@ -115,7 +126,7 @@ CONFIG_ANDROIDTV_ADB_SERVER = { CONF_HOST: HOST, CONF_PORT: DEFAULT_PORT, CONF_DEVICE_CLASS: DEVICE_ANDROIDTV, - CONF_ADB_SERVER_IP: HOST, + CONF_ADB_SERVER_IP: patchers.ADB_SERVER_HOST, CONF_ADB_SERVER_PORT: DEFAULT_ADB_SERVER_PORT, }, } @@ -139,11 +150,44 @@ CONFIG_FIRETV_ADB_SERVER = { CONF_HOST: HOST, CONF_PORT: DEFAULT_PORT, CONF_DEVICE_CLASS: DEVICE_FIRETV, - CONF_ADB_SERVER_IP: HOST, + CONF_ADB_SERVER_IP: patchers.ADB_SERVER_HOST, CONF_ADB_SERVER_PORT: DEFAULT_ADB_SERVER_PORT, }, } +CONFIG_ANDROIDTV_DEFAULT = CONFIG_ANDROIDTV_PYTHON_ADB +CONFIG_FIRETV_DEFAULT = CONFIG_FIRETV_PYTHON_ADB + + +@pytest.fixture(autouse=True) +def adb_device_tcp_fixture() -> None: + """Patch ADB Device TCP.""" + with patch( + "androidtv.adb_manager.adb_manager_async.AdbDeviceTcpAsync", + patchers.AdbDeviceTcpAsyncFake, + ): + yield + + +@pytest.fixture(autouse=True) +def load_adbkey_fixture() -> None: + """Patch load_adbkey.""" + with patch( + "homeassistant.components.androidtv.ADBPythonSync.load_adbkey", + return_value="signer for testing", + ): + yield + + +@pytest.fixture(autouse=True) +def keygen_fixture() -> None: + """Patch keygen.""" + with patch( + "homeassistant.components.androidtv.keygen", + return_value=Mock(), + ): + yield + def _setup(config): """Perform common setup tasks for the tests.""" @@ -153,7 +197,6 @@ def _setup(config): domain=DOMAIN, data=config[DOMAIN], unique_id="a1:b1:c1:d1:e1:f1", - options=config[DOMAIN].get(CONF_OPTIONS), ) return patch_key, entity_id, config_entry @@ -180,11 +223,9 @@ async def test_reconnect(hass, caplog, config): patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[ - patch_key - ], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -198,7 +239,7 @@ async def test_reconnect(hass, caplog, config): with patchers.patch_connect(False)[patch_key], patchers.patch_shell(error=True)[ patch_key - ], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + ]: for _ in range(5): await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) @@ -212,23 +253,13 @@ async def test_reconnect(hass, caplog, config): caplog.set_level(logging.DEBUG) with patchers.patch_connect(True)[patch_key], patchers.patch_shell( SHELL_RESPONSE_STANDBY - )[patch_key], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + )[patch_key]: await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) assert state is not None assert state.state == STATE_STANDBY - - if patch_key == "python": - assert ( - "ADB connection to 127.0.0.1:5555 successfully established" - in caplog.record_tuples[2] - ) - else: - assert ( - "ADB connection to 127.0.0.1:5555 via ADB server 127.0.0.1:5037 successfully established" - in caplog.record_tuples[2] - ) + assert MSG_RECONNECT[patch_key] in caplog.record_tuples[2] @pytest.mark.parametrize( @@ -248,11 +279,9 @@ async def test_adb_shell_returns_none(hass, config): patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[ - patch_key - ], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -263,7 +292,7 @@ async def test_adb_shell_returns_none(hass, config): with patchers.patch_shell(None)[patch_key], patchers.patch_shell(error=True)[ patch_key - ], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + ]: await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) assert state is not None @@ -272,16 +301,12 @@ async def test_adb_shell_returns_none(hass, config): async def test_setup_with_adbkey(hass): """Test that setup succeeds when using an ADB key.""" - config = copy.deepcopy(CONFIG_ANDROIDTV_PYTHON_ADB) - config[DOMAIN][CONF_ADBKEY] = hass.config.path("user_provided_adbkey") - patch_key, entity_id, config_entry = _setup(config) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_PYTHON_ADB_KEY) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[ - patch_key - ], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER, PATCH_ISFILE, PATCH_ACCESS: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key], patchers.PATCH_ISFILE: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -292,30 +317,26 @@ async def test_setup_with_adbkey(hass): @pytest.mark.parametrize( - "config0", + "config", [ - CONFIG_ANDROIDTV_ADB_SERVER, - CONFIG_FIRETV_ADB_SERVER, + CONFIG_ANDROIDTV_DEFAULT, + CONFIG_FIRETV_DEFAULT, ], ) -async def test_sources(hass, config0): +async def test_sources(hass, config): """Test that sources (i.e., apps) are handled correctly for Android TV and Fire TV devices.""" - config = copy.deepcopy(config0) - config[DOMAIN].setdefault(CONF_OPTIONS, {}).update( - { - CONF_APPS: { - "com.app.test1": "TEST 1", - "com.app.test3": None, - "com.app.test4": SHELL_RESPONSE_OFF, - } - } - ) + conf_apps = { + "com.app.test1": "TEST 1", + "com.app.test3": None, + "com.app.test4": SHELL_RESPONSE_OFF, + } patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry(config_entry, options={CONF_APPS: conf_apps}) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -324,25 +345,17 @@ async def test_sources(hass, config0): assert state is not None assert state.state == STATE_OFF - if config[DOMAIN].get(CONF_DEVICE_CLASS) != DEVICE_FIRETV: - patch_update = patchers.patch_androidtv_update( - "playing", - "com.app.test1", - ["com.app.test1", "com.app.test2", "com.app.test3", "com.app.test4"], - "hdmi", - False, - 1, - "HW5", - ) - else: - patch_update = patchers.patch_firetv_update( - "playing", - "com.app.test1", - ["com.app.test1", "com.app.test2", "com.app.test3", "com.app.test4"], - "HW5", - ) + patch_update = patchers.patch_androidtv_update( + "playing", + "com.app.test1", + ["com.app.test1", "com.app.test2", "com.app.test3", "com.app.test4"], + "hdmi", + False, + 1, + "HW5", + ) - with patch_update: + with patch_update[config[DOMAIN][CONF_DEVICE_CLASS]]: await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) assert state is not None @@ -350,25 +363,17 @@ async def test_sources(hass, config0): assert state.attributes["source"] == "TEST 1" assert sorted(state.attributes["source_list"]) == ["TEST 1", "com.app.test2"] - if config[DOMAIN].get(CONF_DEVICE_CLASS) != DEVICE_FIRETV: - patch_update = patchers.patch_androidtv_update( - "playing", - "com.app.test2", - ["com.app.test2", "com.app.test1", "com.app.test3", "com.app.test4"], - "hdmi", - True, - 0, - "HW5", - ) - else: - patch_update = patchers.patch_firetv_update( - "playing", - "com.app.test2", - ["com.app.test2", "com.app.test1", "com.app.test3", "com.app.test4"], - "HW5", - ) + patch_update = patchers.patch_androidtv_update( + "playing", + "com.app.test2", + ["com.app.test2", "com.app.test1", "com.app.test3", "com.app.test4"], + "hdmi", + True, + 0, + "HW5", + ) - with patch_update: + with patch_update[config[DOMAIN][CONF_DEVICE_CLASS]]: await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) assert state is not None @@ -377,24 +382,29 @@ async def test_sources(hass, config0): assert sorted(state.attributes["source_list"]) == ["TEST 1", "com.app.test2"] -async def _test_exclude_sources(hass, config0, expected_sources): +@pytest.mark.parametrize( + ["config", "expected_sources"], + [ + (CONFIG_ANDROIDTV_DEFAULT, ["TEST 1"]), + (CONFIG_FIRETV_DEFAULT, ["TEST 1"]), + ], +) +async def test_exclude_sources(hass, config, expected_sources): """Test that sources (i.e., apps) are handled correctly when the `exclude_unnamed_apps` config parameter is provided.""" - config = copy.deepcopy(config0) - config[DOMAIN].setdefault(CONF_OPTIONS, {}).update( - { - CONF_APPS: { - "com.app.test1": "TEST 1", - "com.app.test3": None, - "com.app.test4": SHELL_RESPONSE_OFF, - } - } - ) + conf_apps = { + "com.app.test1": "TEST 1", + "com.app.test3": None, + "com.app.test4": SHELL_RESPONSE_OFF, + } patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry( + config_entry, options={CONF_EXCLUDE_UNNAMED_APPS: True, CONF_APPS: conf_apps} + ) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -403,37 +413,23 @@ async def _test_exclude_sources(hass, config0, expected_sources): assert state is not None assert state.state == STATE_OFF - if config[DOMAIN].get(CONF_DEVICE_CLASS) != DEVICE_FIRETV: - patch_update = patchers.patch_androidtv_update( - "playing", + patch_update = patchers.patch_androidtv_update( + "playing", + "com.app.test1", + [ "com.app.test1", - [ - "com.app.test1", - "com.app.test2", - "com.app.test3", - "com.app.test4", - "com.app.test5", - ], - "hdmi", - False, - 1, - "HW5", - ) - else: - patch_update = patchers.patch_firetv_update( - "playing", - "com.app.test1", - [ - "com.app.test1", - "com.app.test2", - "com.app.test3", - "com.app.test4", - "com.app.test5", - ], - "HW5", - ) + "com.app.test2", + "com.app.test3", + "com.app.test4", + "com.app.test5", + ], + "hdmi", + False, + 1, + "HW5", + ) - with patch_update: + with patch_update[config[DOMAIN][CONF_DEVICE_CLASS]]: await async_update_entity(hass, entity_id) state = hass.states.get(entity_id) assert state is not None @@ -441,41 +437,18 @@ async def _test_exclude_sources(hass, config0, expected_sources): assert state.attributes["source"] == "TEST 1" assert sorted(state.attributes["source_list"]) == expected_sources - return True - -async def test_androidtv_exclude_sources(hass): - """Test that sources (i.e., apps) are handled correctly for Android TV devices when the `exclude_unnamed_apps` config parameter is provided as true.""" - config = copy.deepcopy(CONFIG_ANDROIDTV_ADB_SERVER) - config[DOMAIN][CONF_OPTIONS] = {CONF_EXCLUDE_UNNAMED_APPS: True} - assert await _test_exclude_sources(hass, config, ["TEST 1"]) - - -async def test_firetv_exclude_sources(hass): - """Test that sources (i.e., apps) are handled correctly for Fire TV devices when the `exclude_unnamed_apps` config parameter is provided as true.""" - config = copy.deepcopy(CONFIG_FIRETV_ADB_SERVER) - config[DOMAIN][CONF_OPTIONS] = {CONF_EXCLUDE_UNNAMED_APPS: True} - assert await _test_exclude_sources(hass, config, ["TEST 1"]) - - -async def _test_select_source(hass, config0, source, expected_arg, method_patch): +async def _test_select_source( + hass, config, conf_apps, source, expected_arg, method_patch +): """Test that the methods for launching and stopping apps are called correctly when selecting a source.""" - config = copy.deepcopy(config0) - config[DOMAIN].setdefault(CONF_OPTIONS, {}).update( - { - CONF_APPS: { - "com.app.test1": "TEST 1", - "com.app.test3": None, - "com.youtube.test": "YouTube", - } - } - ) patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry(config_entry, options={CONF_APPS: conf_apps}) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -484,213 +457,87 @@ async def _test_select_source(hass, config0, source, expected_arg, method_patch) assert state is not None assert state.state == STATE_OFF - with method_patch as method_patch_: + with method_patch as method_patch_used: await hass.services.async_call( MP_DOMAIN, SERVICE_SELECT_SOURCE, {ATTR_ENTITY_ID: entity_id, ATTR_INPUT_SOURCE: source}, blocking=True, ) - method_patch_.assert_called_with(expected_arg) - - return True + method_patch_used.assert_called_with(expected_arg) -async def test_androidtv_select_source_launch_app_id(hass): - """Test that an app can be launched using its app ID.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "com.app.test1", - "com.app.test1", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_androidtv_select_source_launch_app_name(hass): - """Test that an app can be launched using its friendly name.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "TEST 1", - "com.app.test1", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_androidtv_select_source_launch_app_id_no_name(hass): - """Test that an app can be launched using its app ID when it has no friendly name.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "com.app.test2", - "com.app.test2", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_androidtv_select_source_launch_app_hidden(hass): - """Test that an app can be launched using its app ID when it is hidden from the sources list.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "com.app.test3", - "com.app.test3", - patchers.PATCH_LAUNCH_APP, +@pytest.mark.parametrize( + ["source", "expected_arg", "method_patch"], + [ + ("com.app.test1", "com.app.test1", patchers.PATCH_LAUNCH_APP), + ("TEST 1", "com.app.test1", patchers.PATCH_LAUNCH_APP), + ("com.app.test2", "com.app.test2", patchers.PATCH_LAUNCH_APP), + ("com.app.test3", "com.app.test3", patchers.PATCH_LAUNCH_APP), + ("!com.app.test1", "com.app.test1", patchers.PATCH_STOP_APP), + ("!TEST 1", "com.app.test1", patchers.PATCH_STOP_APP), + ("!com.app.test2", "com.app.test2", patchers.PATCH_STOP_APP), + ("!com.app.test3", "com.app.test3", patchers.PATCH_STOP_APP), + ], +) +async def test_select_source_androidtv(hass, source, expected_arg, method_patch): + """Test that an app can be launched for AndroidTV.""" + conf_apps = { + "com.app.test1": "TEST 1", + "com.app.test3": None, + } + await _test_select_source( + hass, CONFIG_ANDROIDTV_DEFAULT, conf_apps, source, expected_arg, method_patch ) async def test_androidtv_select_source_overridden_app_name(hass): """Test that when an app name is overridden via the `apps` configuration parameter, the app is launched correctly.""" # Evidence that the default YouTube app ID will be overridden + conf_apps = { + "com.youtube.test": "YouTube", + } assert "YouTube" in ANDROIDTV_APPS.values() assert "com.youtube.test" not in ANDROIDTV_APPS - assert await _test_select_source( + await _test_select_source( hass, - CONFIG_ANDROIDTV_ADB_SERVER, + CONFIG_ANDROIDTV_PYTHON_ADB, + conf_apps, "YouTube", "com.youtube.test", patchers.PATCH_LAUNCH_APP, ) -async def test_androidtv_select_source_stop_app_id(hass): - """Test that an app can be stopped using its app ID.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "!com.app.test1", - "com.app.test1", - patchers.PATCH_STOP_APP, - ) - - -async def test_androidtv_select_source_stop_app_name(hass): - """Test that an app can be stopped using its friendly name.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "!TEST 1", - "com.app.test1", - patchers.PATCH_STOP_APP, - ) - - -async def test_androidtv_select_source_stop_app_id_no_name(hass): - """Test that an app can be stopped using its app ID when it has no friendly name.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "!com.app.test2", - "com.app.test2", - patchers.PATCH_STOP_APP, - ) - - -async def test_androidtv_select_source_stop_app_hidden(hass): - """Test that an app can be stopped using its app ID when it is hidden from the sources list.""" - assert await _test_select_source( - hass, - CONFIG_ANDROIDTV_ADB_SERVER, - "!com.app.test3", - "com.app.test3", - patchers.PATCH_STOP_APP, - ) - - -async def test_firetv_select_source_launch_app_id(hass): - """Test that an app can be launched using its app ID.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "com.app.test1", - "com.app.test1", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_firetv_select_source_launch_app_name(hass): - """Test that an app can be launched using its friendly name.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "TEST 1", - "com.app.test1", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_firetv_select_source_launch_app_id_no_name(hass): - """Test that an app can be launched using its app ID when it has no friendly name.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "com.app.test2", - "com.app.test2", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_firetv_select_source_launch_app_hidden(hass): - """Test that an app can be launched using its app ID when it is hidden from the sources list.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "com.app.test3", - "com.app.test3", - patchers.PATCH_LAUNCH_APP, - ) - - -async def test_firetv_select_source_stop_app_id(hass): - """Test that an app can be stopped using its app ID.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "!com.app.test1", - "com.app.test1", - patchers.PATCH_STOP_APP, - ) - - -async def test_firetv_select_source_stop_app_name(hass): - """Test that an app can be stopped using its friendly name.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "!TEST 1", - "com.app.test1", - patchers.PATCH_STOP_APP, - ) - - -async def test_firetv_select_source_stop_app_id_no_name(hass): - """Test that an app can be stopped using its app ID when it has no friendly name.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "!com.app.test2", - "com.app.test2", - patchers.PATCH_STOP_APP, - ) - - -async def test_firetv_select_source_stop_hidden(hass): - """Test that an app can be stopped using its app ID when it is hidden from the sources list.""" - assert await _test_select_source( - hass, - CONFIG_FIRETV_ADB_SERVER, - "!com.app.test3", - "com.app.test3", - patchers.PATCH_STOP_APP, +@pytest.mark.parametrize( + ["source", "expected_arg", "method_patch"], + [ + ("com.app.test1", "com.app.test1", patchers.PATCH_LAUNCH_APP), + ("TEST 1", "com.app.test1", patchers.PATCH_LAUNCH_APP), + ("com.app.test2", "com.app.test2", patchers.PATCH_LAUNCH_APP), + ("com.app.test3", "com.app.test3", patchers.PATCH_LAUNCH_APP), + ("!com.app.test1", "com.app.test1", patchers.PATCH_STOP_APP), + ("!TEST 1", "com.app.test1", patchers.PATCH_STOP_APP), + ("!com.app.test2", "com.app.test2", patchers.PATCH_STOP_APP), + ("!com.app.test3", "com.app.test3", patchers.PATCH_STOP_APP), + ], +) +async def test_select_source_firetv(hass, source, expected_arg, method_patch): + """Test that an app can be launched for FireTV.""" + conf_apps = { + "com.app.test1": "TEST 1", + "com.app.test3": None, + } + await _test_select_source( + hass, CONFIG_FIRETV_DEFAULT, conf_apps, source, expected_arg, method_patch ) @pytest.mark.parametrize( "config", [ - CONFIG_ANDROIDTV_PYTHON_ADB, - CONFIG_FIRETV_PYTHON_ADB, + CONFIG_ANDROIDTV_DEFAULT, + CONFIG_FIRETV_DEFAULT, ], ) async def test_setup_fail(hass, config): @@ -698,11 +545,9 @@ async def test_setup_fail(hass, config): patch_key, entity_id, config_entry = _setup(config) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(False)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[ - patch_key - ], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + with patchers.patch_connect(False)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) is False await hass.async_block_till_done() @@ -713,14 +558,14 @@ async def test_setup_fail(hass, config): async def test_adb_command(hass): """Test sending a command via the `androidtv.adb_command` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) command = "test command" response = "test response" - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -742,14 +587,14 @@ async def test_adb_command(hass): async def test_adb_command_unicode_decode_error(hass): """Test sending a command via the `androidtv.adb_command` service that raises a UnicodeDecodeError exception.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) command = "test command" response = b"test response" - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -771,14 +616,14 @@ async def test_adb_command_unicode_decode_error(hass): async def test_adb_command_key(hass): """Test sending a key command via the `androidtv.adb_command` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) command = "HOME" response = None - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -800,14 +645,14 @@ async def test_adb_command_key(hass): async def test_adb_command_get_properties(hass): """Test sending the "GET_PROPERTIES" command via the `androidtv.adb_command` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) command = "GET_PROPERTIES" response = {"test key": "test value"} - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -830,13 +675,13 @@ async def test_adb_command_get_properties(hass): async def test_learn_sendevent(hass): """Test the `androidtv.learn_sendevent` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) response = "sendevent 1 2 3 4" - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -859,12 +704,12 @@ async def test_learn_sendevent(hass): async def test_update_lock_not_acquired(hass): """Test that the state does not get updated when a `LockNotAcquiredException` is raised.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -892,14 +737,14 @@ async def test_update_lock_not_acquired(hass): async def test_download(hass): """Test the `androidtv.download` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) device_path = "device/path" local_path = "local/path" - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -938,14 +783,14 @@ async def test_download(hass): async def test_upload(hass): """Test the `androidtv.upload` service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) device_path = "device/path" local_path = "local/path" - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -984,12 +829,12 @@ async def test_upload(hass): async def test_androidtv_volume_set(hass): """Test setting the volume for an Android TV device.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1011,12 +856,12 @@ async def test_get_image(hass, hass_ws_client): This is based on `test_get_image` in tests/components/media_player/test_init.py. """ - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1042,7 +887,7 @@ async def test_get_image(hass, hass_ws_client): with patch( "androidtv.basetv.basetv_async.BaseTVAsync.adb_screencap", - side_effect=RuntimeError, + side_effect=ConnectionResetError, ): await client.send_json( {"id": 6, "type": "media_player_thumbnail", "entity_id": entity_id} @@ -1056,6 +901,39 @@ async def test_get_image(hass, hass_ws_client): assert state.state == STATE_UNAVAILABLE +async def test_get_image_disabled(hass, hass_ws_client): + """Test taking a screen capture with screencap option disabled. + + This is based on `test_get_image` in tests/components/media_player/test_init.py. + """ + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) + config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry( + config_entry, options={CONF_SCREENCAP: False} + ) + + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + with patchers.patch_shell("11")[patch_key]: + await async_update_entity(hass, entity_id) + + client = await hass_ws_client(hass) + + with patch( + "androidtv.basetv.basetv_async.BaseTVAsync.adb_screencap", return_value=b"image" + ) as screen_cap: + await client.send_json( + {"id": 5, "type": "media_player_thumbnail", "entity_id": entity_id} + ) + + await client.receive_json() + assert not screen_cap.called + + async def _test_service( hass, entity_id, @@ -1088,10 +966,10 @@ async def _test_service( async def test_services_androidtv(hass): """Test media player services for an Android TV device.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[patch_key]: + with patchers.patch_connect(True)[patch_key]: with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1129,15 +1007,17 @@ async def test_services_androidtv(hass): async def test_services_firetv(hass): """Test media player services for a Fire TV device.""" - config = copy.deepcopy(CONFIG_FIRETV_ADB_SERVER) - config[DOMAIN][CONF_OPTIONS] = { - CONF_TURN_OFF_COMMAND: "test off", - CONF_TURN_ON_COMMAND: "test on", - } - patch_key, entity_id, config_entry = _setup(config) + patch_key, entity_id, config_entry = _setup(CONFIG_FIRETV_DEFAULT) config_entry.add_to_hass(hass) + hass.config_entries.async_update_entry( + config_entry, + options={ + CONF_TURN_OFF_COMMAND: "test off", + CONF_TURN_ON_COMMAND: "test on", + }, + ) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[patch_key]: + with patchers.patch_connect(True)[patch_key]: with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1150,10 +1030,10 @@ async def test_services_firetv(hass): async def test_volume_mute(hass): """Test the volume mute service.""" - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[patch_key]: + with patchers.patch_connect(True)[patch_key]: with patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1193,18 +1073,16 @@ async def test_volume_mute(hass): async def test_connection_closed_on_ha_stop(hass): """Test that the ADB socket connection is closed when HA stops.""" - patch_key, _, config_entry = _setup(CONFIG_ANDROIDTV_ADB_SERVER) + patch_key, _, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[patch_key]: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - with patch( - "androidtv.androidtv.androidtv_async.AndroidTVAsync.adb_close" - ) as adb_close: + with patch("androidtv.basetv.basetv_async.BaseTVAsync.adb_close") as adb_close: hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) await hass.async_block_till_done() assert adb_close.called @@ -1215,14 +1093,12 @@ async def test_exception(hass): HA will attempt to reconnect on the next update. """ - patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_PYTHON_ADB) + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) config_entry.add_to_hass(hass) - with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ - patch_key - ], patchers.patch_shell(SHELL_RESPONSE_OFF)[ - patch_key - ], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER: + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -1243,3 +1119,38 @@ async def test_exception(hass): state = hass.states.get(entity_id) assert state is not None assert state.state == STATE_OFF + + +async def test_options_reload(hass): + """Test changing an option that will cause integration reload.""" + patch_key, entity_id, config_entry = _setup(CONFIG_ANDROIDTV_DEFAULT) + config_entry.add_to_hass(hass) + + with patchers.patch_connect(True)[patch_key], patchers.patch_shell( + SHELL_RESPONSE_OFF + )[patch_key]: + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + await async_update_entity(hass, entity_id) + state = hass.states.get(entity_id) + assert state is not None + assert state.state == STATE_OFF + + with patchers.PATCH_SETUP_ENTRY as setup_entry_call: + # change an option that not require integration reload + hass.config_entries.async_update_entry( + config_entry, options={CONF_SCREENCAP: False} + ) + await hass.async_block_till_done() + + assert not setup_entry_call.called + + # change an option that require integration reload + hass.config_entries.async_update_entry( + config_entry, options={CONF_STATE_DETECTION_RULES: {}} + ) + await hass.async_block_till_done() + + assert setup_entry_call.called + assert config_entry.state is ConfigEntryState.LOADED From 75026f9fedcb7c43d166fd4025ec4649179ead8c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 17:22:53 -0500 Subject: [PATCH 165/930] Separate recorder logic for state_changed and non-state_changed events (#71204) --- homeassistant/components/recorder/__init__.py | 67 ++++++++++--------- tests/components/recorder/test_init.py | 60 +++++++++++++++++ 2 files changed, 97 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 35a664cac65..1d81495c3a5 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -1123,7 +1123,10 @@ class Recorder(threading.Thread): def _process_one_event(self, event: Event) -> None: if not self.enabled: return - self._process_event_into_session(event) + if event.event_type == EVENT_STATE_CHANGED: + self._process_state_changed_event_into_session(event) + else: + self._process_non_state_changed_event_into_session(event) # Commit if the commit interval is zero if not self.commit_interval: self._commit_event_session_or_retry() @@ -1164,40 +1167,44 @@ class Recorder(threading.Thread): return cast(int, data_id[0]) return None - def _process_event_into_session(self, event: Event) -> None: + def _process_non_state_changed_event_into_session(self, event: Event) -> None: + """Process any event into the session except state changed.""" assert self.event_session is not None dbevent = Events.from_event(event) - - if event.event_type != EVENT_STATE_CHANGED and event.data: - try: - shared_data = EventData.shared_data_from_event(event) - except (TypeError, ValueError): - _LOGGER.warning("Event is not JSON serializable: %s", event) - return - - # Matching attributes found in the pending commit - if pending_event_data := self._pending_event_data.get(shared_data): - dbevent.event_data_rel = pending_event_data - # Matching attributes id found in the cache - elif data_id := self._event_data_ids.get(shared_data): - dbevent.data_id = data_id - else: - data_hash = EventData.hash_shared_data(shared_data) - # Matching attributes found in the database - if data_id := self._find_shared_data_in_db(data_hash, shared_data): - self._event_data_ids[shared_data] = dbevent.data_id = data_id - # No matching attributes found, save them in the DB - else: - dbevent_data = EventData(shared_data=shared_data, hash=data_hash) - dbevent.event_data_rel = self._pending_event_data[ - shared_data - ] = dbevent_data - self.event_session.add(dbevent_data) - - if event.event_type != EVENT_STATE_CHANGED: + if not event.data: self.event_session.add(dbevent) return + try: + shared_data = EventData.shared_data_from_event(event) + except (TypeError, ValueError) as ex: + _LOGGER.warning("Event is not JSON serializable: %s: %s", event, ex) + return + + # Matching attributes found in the pending commit + if pending_event_data := self._pending_event_data.get(shared_data): + dbevent.event_data_rel = pending_event_data + # Matching attributes id found in the cache + elif data_id := self._event_data_ids.get(shared_data): + dbevent.data_id = data_id + else: + data_hash = EventData.hash_shared_data(shared_data) + # Matching attributes found in the database + if data_id := self._find_shared_data_in_db(data_hash, shared_data): + self._event_data_ids[shared_data] = dbevent.data_id = data_id + # No matching attributes found, save them in the DB + else: + dbevent_data = EventData(shared_data=shared_data, hash=data_hash) + dbevent.event_data_rel = self._pending_event_data[ + shared_data + ] = dbevent_data + self.event_session.add(dbevent_data) + + self.event_session.add(dbevent) + + def _process_state_changed_event_into_session(self, event: Event) -> None: + """Process a state_changed event into the session.""" + assert self.event_session is not None try: dbstate = States.from_event(event) shared_attrs = StateAttributes.shared_attrs_from_event( diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 0b03283547e..6d4f2e1106a 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -1456,3 +1456,63 @@ async def test_database_connection_keep_alive_disabled_on_sqlite( ) await async_wait_recording_done(hass) assert "Sending keepalive" not in caplog.text + + +def test_deduplication_event_data_inside_commit_interval(hass_recorder, caplog): + """Test deduplication of event data inside the commit interval.""" + hass = hass_recorder() + + for _ in range(10): + hass.bus.fire("this_event", {"de": "dupe"}) + wait_recording_done(hass) + for _ in range(10): + hass.bus.fire("this_event", {"de": "dupe"}) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + events = list( + session.query(Events) + .filter(Events.event_type == "this_event") + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + assert len(events) == 20 + first_data_id = events[0].data_id + assert all(event.data_id == first_data_id for event in events) + + +# Patch STATE_ATTRIBUTES_ID_CACHE_SIZE since otherwise +# the CI can fail because the test takes too long to run +@patch("homeassistant.components.recorder.STATE_ATTRIBUTES_ID_CACHE_SIZE", 5) +def test_deduplication_state_attributes_inside_commit_interval(hass_recorder, caplog): + """Test deduplication of state attributes inside the commit interval.""" + hass = hass_recorder() + + entity_id = "test.recorder" + attributes = {"test_attr": 5, "test_attr_10": "nice"} + + hass.states.set(entity_id, "on", attributes) + hass.states.set(entity_id, "off", attributes) + + # Now exaust the cache to ensure we go back to the db + for attr_id in range(5): + hass.states.set(entity_id, "on", {"test_attr": attr_id}) + hass.states.set(entity_id, "off", {"test_attr": attr_id}) + + wait_recording_done(hass) + for _ in range(5): + hass.states.set(entity_id, "on", attributes) + hass.states.set(entity_id, "off", attributes) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + states = list( + session.query(States) + .filter(States.entity_id == entity_id) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + ) + assert len(states) == 22 + first_attributes_id = states[0].attributes_id + last_attributes_id = states[-1].attributes_id + assert first_attributes_id == last_attributes_id From fbc048f07b64a4158763ae14ee1989b8231ee73c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 May 2022 15:39:05 -0700 Subject: [PATCH 166/930] Bump frontend to 20220502.0 (#71221) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index e57f06827a5..18e2fc6ed8f 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220429.0"], + "requirements": ["home-assistant-frontend==20220502.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 6dd904c858f..f328394b9c1 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220429.0 +home-assistant-frontend==20220502.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index 16177793510..82b69db90f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220429.0 +home-assistant-frontend==20220502.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fde9af6058b..a107e19bdf6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220429.0 +home-assistant-frontend==20220502.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 496260561355c9b4f6d7c9fc78638dda88506b8e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 01:46:39 +0200 Subject: [PATCH 167/930] Fix enumeration of zwave-js device triggers (#71225) * Fix enumeration of zwave-js device triggers * Address review comments --- .../components/zwave_js/device_trigger.py | 6 ++++- homeassistant/components/zwave_js/helpers.py | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zwave_js/device_trigger.py b/homeassistant/components/zwave_js/device_trigger.py index 89379f9a953..1e76f9e73ec 100644 --- a/homeassistant/components/zwave_js/device_trigger.py +++ b/homeassistant/components/zwave_js/device_trigger.py @@ -266,7 +266,11 @@ async def async_get_triggers( entity_id = async_get_node_status_sensor_entity_id( hass, device_id, ent_reg, dev_reg ) - if (entity := ent_reg.async_get(entity_id)) is not None and not entity.disabled: + if ( + entity_id + and (entity := ent_reg.async_get(entity_id)) is not None + and not entity.disabled + ): triggers.append( {**base_trigger, CONF_TYPE: NODE_STATUS, CONF_ENTITY_ID: entity_id} ) diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index a5a2da23206..17515f9aa99 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -329,13 +329,22 @@ def get_zwave_value_from_config(node: ZwaveNode, config: ConfigType) -> ZwaveVal return node.values[value_id] +def _zwave_js_config_entry(hass: HomeAssistant, device: dr.DeviceEntry) -> str | None: + """Find zwave_js config entry from a device.""" + for entry_id in device.config_entries: + entry = hass.config_entries.async_get_entry(entry_id) + if entry and entry.domain == DOMAIN: + return entry_id + return None + + @callback def async_get_node_status_sensor_entity_id( hass: HomeAssistant, device_id: str, ent_reg: er.EntityRegistry | None = None, dev_reg: dr.DeviceRegistry | None = None, -) -> str: +) -> str | None: """Get the node status sensor entity ID for a given Z-Wave JS device.""" if not ent_reg: ent_reg = er.async_get(hass) @@ -344,20 +353,16 @@ def async_get_node_status_sensor_entity_id( if not (device := dev_reg.async_get(device_id)): raise HomeAssistantError("Invalid Device ID provided") - entry_id = next(entry_id for entry_id in device.config_entries) + if not (entry_id := _zwave_js_config_entry(hass, device)): + return None + client = hass.data[DOMAIN][entry_id][DATA_CLIENT] node = async_get_node_from_device_id(hass, device_id, dev_reg) - entity_id = ent_reg.async_get_entity_id( + return ent_reg.async_get_entity_id( SENSOR_DOMAIN, DOMAIN, f"{client.driver.controller.home_id}.{node.node_id}.node_status", ) - if not entity_id: - raise HomeAssistantError( - "Node status sensor entity not found. Device may not be a zwave_js device" - ) - - return entity_id def remove_keys_with_empty_values(config: ConfigType) -> ConfigType: From c594de25f7967bfe65b97f600f789eb675372d74 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 3 May 2022 00:23:56 +0000 Subject: [PATCH 168/930] [ci skip] Translation update --- .../application_credentials/translations/no.json | 3 +++ .../components/deconz/translations/no.json | 1 + .../components/dialogflow/translations/fr.json | 2 +- .../components/geofency/translations/fr.json | 2 +- .../components/gpslogger/translations/fr.json | 2 +- .../components/ifttt/translations/fr.json | 2 +- .../components/isy994/translations/no.json | 13 +++++++++++-- .../components/locative/translations/da.json | 2 +- .../components/mailgun/translations/fr.json | 2 +- .../components/meater/translations/de.json | 9 +++++++++ .../components/meater/translations/el.json | 9 +++++++++ .../components/meater/translations/fr.json | 9 +++++++++ .../components/meater/translations/hu.json | 9 +++++++++ .../components/meater/translations/nl.json | 5 +++++ .../components/meater/translations/pl.json | 9 +++++++++ .../components/meater/translations/pt-BR.json | 9 +++++++++ .../components/meater/translations/ru.json | 9 +++++++++ .../components/meater/translations/zh-Hant.json | 9 +++++++++ .../components/owntracks/translations/fr.json | 2 +- .../components/recorder/translations/no.json | 8 ++++++++ .../components/simplisafe/translations/de.json | 2 +- .../components/simplisafe/translations/en.json | 14 ++++++++++++-- .../components/simplisafe/translations/fr.json | 2 +- .../components/simplisafe/translations/no.json | 2 +- .../components/simplisafe/translations/pl.json | 2 +- .../components/simplisafe/translations/pt-BR.json | 2 +- .../simplisafe/translations/zh-Hant.json | 2 +- homeassistant/components/sql/translations/no.json | 4 ++++ .../components/steam_online/translations/no.json | 4 ++-- .../components/traccar/translations/fr.json | 2 +- .../components/unifi/translations/el.json | 3 +++ .../components/unifi/translations/hu.json | 3 +++ .../components/unifi/translations/nl.json | 3 +++ .../components/unifi/translations/no.json | 5 ++++- .../components/unifi/translations/pl.json | 3 +++ .../components/unifi/translations/ru.json | 3 +++ .../components/unifi/translations/zh-Hant.json | 5 ++++- 37 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/no.json create mode 100644 homeassistant/components/recorder/translations/no.json diff --git a/homeassistant/components/application_credentials/translations/no.json b/homeassistant/components/application_credentials/translations/no.json new file mode 100644 index 00000000000..5e55d3d6538 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/no.json @@ -0,0 +1,3 @@ +{ + "title": "S\u00f8knadslegitimasjon" +} \ No newline at end of file diff --git a/homeassistant/components/deconz/translations/no.json b/homeassistant/components/deconz/translations/no.json index 06c03b8b585..7682cb15a5d 100644 --- a/homeassistant/components/deconz/translations/no.json +++ b/homeassistant/components/deconz/translations/no.json @@ -9,6 +9,7 @@ "updated_instance": "Oppdatert deCONZ forekomst med ny vertsadresse" }, "error": { + "linking_not_possible": "Kunne ikke koble til gatewayen", "no_key": "Kunne ikke f\u00e5 en API-n\u00f8kkel" }, "flow_title": "{host}", diff --git a/homeassistant/components/dialogflow/translations/fr.json b/homeassistant/components/dialogflow/translations/fr.json index 661d86566b0..56d754d7f0a 100644 --- a/homeassistant/components/dialogflow/translations/fr.json +++ b/homeassistant/components/dialogflow/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer Dialogflow?", + "description": "Voulez-vous vraiment configurer Dialogflow\u00a0?", "title": "Configurer le Webhook Dialogflow" } } diff --git a/homeassistant/components/geofency/translations/fr.json b/homeassistant/components/geofency/translations/fr.json index 84da031d50d..0e268eae673 100644 --- a/homeassistant/components/geofency/translations/fr.json +++ b/homeassistant/components/geofency/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer le Webhook Geofency ?", + "description": "Voulez-vous vraiment configurer le webhook Geofency\u00a0?", "title": "Configurer le Webhook Geofency" } } diff --git a/homeassistant/components/gpslogger/translations/fr.json b/homeassistant/components/gpslogger/translations/fr.json index 4e207dddfcd..21896884391 100644 --- a/homeassistant/components/gpslogger/translations/fr.json +++ b/homeassistant/components/gpslogger/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer le Webhook GPSLogger ?", + "description": "Voulez-vous vraiment configurer le webhook GPSLogger\u00a0?", "title": "Configurer le Webhook GPSLogger" } } diff --git a/homeassistant/components/ifttt/translations/fr.json b/homeassistant/components/ifttt/translations/fr.json index 371bbd65ee0..280c031ea39 100644 --- a/homeassistant/components/ifttt/translations/fr.json +++ b/homeassistant/components/ifttt/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer IFTTT?", + "description": "Voulez-vous vraiment configurer IFTTT\u00a0?", "title": "Configurer l'applet IFTTT Webhook" } } diff --git a/homeassistant/components/isy994/translations/no.json b/homeassistant/components/isy994/translations/no.json index 7da3fa4c6fe..e18666d7fc4 100644 --- a/homeassistant/components/isy994/translations/no.json +++ b/homeassistant/components/isy994/translations/no.json @@ -7,10 +7,19 @@ "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", "invalid_host": "Vertsoppf\u00f8ringen var ikke i fullstendig URL-format, for eksempel http://192.168.10.100:80", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", "unknown": "Uventet feil" }, "flow_title": "{name} ( {host} )", "step": { + "reauth_confirm": { + "data": { + "password": "Passord", + "username": "Brukernavn" + }, + "description": "P\u00e5loggingsinformasjonen for {host} er ikke lenger gyldig.", + "title": "Autentiser din ISY p\u00e5 nytt" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Brukernavn" }, "description": "Vertsoppf\u00f8ringen m\u00e5 v\u00e6re i fullstendig URL-format, for eksempel http://192.168.10.100:80", - "title": "Koble til ISY994" + "title": "Koble til din ISY" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "Variabel sensorstreng" }, "description": "Angi alternativene for ISY-integrering: \n \u2022 Nodesensorstreng: Alle enheter eller mapper som inneholder NodeSensor String i navnet, behandles som en sensor eller bin\u00e6r sensor. \n \u2022 Ignorer streng: Alle enheter med 'Ignorer streng' i navnet ignoreres. \n \u2022 Variabel sensorstreng: Alle variabler som inneholder \"Variabel sensorstreng\" vil bli lagt til som en sensor. \n \u2022 Gjenopprett lyslysstyrke: Hvis den er aktivert, gjenopprettes den forrige lysstyrken n\u00e5r du sl\u00e5r p\u00e5 et lys i stedet for enhetens innebygde p\u00e5-niv\u00e5.", - "title": "ISY994 Alternativer" + "title": "ISY-alternativer" } } }, diff --git a/homeassistant/components/locative/translations/da.json b/homeassistant/components/locative/translations/da.json index 2401428eff4..3d4395b89dc 100644 --- a/homeassistant/components/locative/translations/da.json +++ b/homeassistant/components/locative/translations/da.json @@ -1,7 +1,7 @@ { "config": { "create_entry": { - "default": "For at sende lokationer til Home Assistant skal du konfigurere webhook funktionen i Locative applicationen.\n\n Udfyld f\u00f8lgende oplysninger: \n\n - URL: `{webhook_url}`\n - Metode: POST\n \n Se [dokumentationen]({docs_url}) for yderligere oplysninger." + "default": "For at sende lokaliteter til Home Assistant skal du konfigurere webhook-funktionen i Locative-applicationen.\n\n Udfyld f\u00f8lgende oplysninger: \n\n - URL: `{webhook_url}`\n - Metode: POST\n \n Se [dokumentationen]({docs_url}) for yderligere oplysninger." }, "step": { "user": { diff --git a/homeassistant/components/mailgun/translations/fr.json b/homeassistant/components/mailgun/translations/fr.json index 80c961babba..9d114cec058 100644 --- a/homeassistant/components/mailgun/translations/fr.json +++ b/homeassistant/components/mailgun/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer Mailgun?", + "description": "Voulez-vous vraiment configurer Mailgun\u00a0?", "title": "Configurer le Webhook Mailgun" } } diff --git a/homeassistant/components/meater/translations/de.json b/homeassistant/components/meater/translations/de.json index ed143e26b4c..9e85c5e56f8 100644 --- a/homeassistant/components/meater/translations/de.json +++ b/homeassistant/components/meater/translations/de.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Unerwarteter Fehler" }, "step": { + "reauth_confirm": { + "data": { + "password": "Passwort" + }, + "description": "Best\u00e4tige das Passwort f\u00fcr das Meater Cloud-Konto {username} ." + }, "user": { "data": { "password": "Passwort", "username": "Benutzername" }, + "data_description": { + "username": "Meater Cloud-Benutzername, normalerweise eine E-Mail-Adresse." + }, "description": "Richte dein Meater Cloud-Konto ein." } } diff --git a/homeassistant/components/meater/translations/el.json b/homeassistant/components/meater/translations/el.json index 2d02110dd49..3114eb17286 100644 --- a/homeassistant/components/meater/translations/el.json +++ b/homeassistant/components/meater/translations/el.json @@ -6,11 +6,20 @@ "unknown_auth_error": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { + "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, + "description": "\u0395\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc Meater Cloud {username}." + }, "user": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, + "data_description": { + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 Meater Cloud, \u03c3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 email." + }, "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/fr.json b/homeassistant/components/meater/translations/fr.json index 9940cb6e24b..af9b76d722a 100644 --- a/homeassistant/components/meater/translations/fr.json +++ b/homeassistant/components/meater/translations/fr.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Erreur inattendue" }, "step": { + "reauth_confirm": { + "data": { + "password": "Mot de passe" + }, + "description": "Confirmez le mot de passe du compte Meater Cloud {username}." + }, "user": { "data": { "password": "Mot de passe", "username": "Nom d'utilisateur" }, + "data_description": { + "username": "Nom d'utilisateur Meater Cloud, g\u00e9n\u00e9ralement une adresse \u00e9lectronique." + }, "description": "Configurez votre compte Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/hu.json b/homeassistant/components/meater/translations/hu.json index fd55bae3bdd..964c240b4e3 100644 --- a/homeassistant/components/meater/translations/hu.json +++ b/homeassistant/components/meater/translations/hu.json @@ -6,11 +6,20 @@ "unknown_auth_error": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { + "reauth_confirm": { + "data": { + "password": "Jelsz\u00f3" + }, + "description": "Er\u0151s\u00edtse meg a Meater Cloud-fi\u00f3k {username} jelszav\u00e1t." + }, "user": { "data": { "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, + "data_description": { + "username": "Meater Cloud felhaszn\u00e1l\u00f3n\u00e9v, \u00e1ltal\u00e1ban egy e-mail c\u00edm." + }, "description": "\u00c1ll\u00edtsa be a Meater Cloud fi\u00f3kj\u00e1t." } } diff --git a/homeassistant/components/meater/translations/nl.json b/homeassistant/components/meater/translations/nl.json index a87175c7574..3261272c7f9 100644 --- a/homeassistant/components/meater/translations/nl.json +++ b/homeassistant/components/meater/translations/nl.json @@ -6,6 +6,11 @@ "unknown_auth_error": "Onverwachte fout" }, "step": { + "reauth_confirm": { + "data": { + "password": "Wachtwoord" + } + }, "user": { "data": { "password": "Wachtwoord", diff --git a/homeassistant/components/meater/translations/pl.json b/homeassistant/components/meater/translations/pl.json index 1816069dd34..1afaffb4bb7 100644 --- a/homeassistant/components/meater/translations/pl.json +++ b/homeassistant/components/meater/translations/pl.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Nieoczekiwany b\u0142\u0105d" }, "step": { + "reauth_confirm": { + "data": { + "password": "Has\u0142o" + }, + "description": "Potwierd\u017a has\u0142o do konta Meater Cloud {username}." + }, "user": { "data": { "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, + "data_description": { + "username": "Nazwa u\u017cytkownika Meater Cloud, zazwyczaj adres e-mail." + }, "description": "Skonfiguruj swoje konto w Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/pt-BR.json b/homeassistant/components/meater/translations/pt-BR.json index 103f3f76986..4e379482baa 100644 --- a/homeassistant/components/meater/translations/pt-BR.json +++ b/homeassistant/components/meater/translations/pt-BR.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Erro inesperado" }, "step": { + "reauth_confirm": { + "data": { + "password": "Senha" + }, + "description": "Confirme a senha da conta do Meeater Cloud {username}." + }, "user": { "data": { "password": "Senha", "username": "Usu\u00e1rio" }, + "data_description": { + "username": "Nome de usu\u00e1rio do Meeater Cloud, normalmente um endere\u00e7o de e-mail." + }, "description": "Configure sua conta Meeater Cloud." } } diff --git a/homeassistant/components/meater/translations/ru.json b/homeassistant/components/meater/translations/ru.json index a56f2f8ebbe..182e2d03e33 100644 --- a/homeassistant/components/meater/translations/ru.json +++ b/homeassistant/components/meater/translations/ru.json @@ -6,11 +6,20 @@ "unknown_auth_error": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Meater Cloud {username}." + }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, + "data_description": { + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f Meater Cloud, \u043e\u0431\u044b\u0447\u043d\u043e \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b." + }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/zh-Hant.json b/homeassistant/components/meater/translations/zh-Hant.json index b04f4a54076..1033f5c993a 100644 --- a/homeassistant/components/meater/translations/zh-Hant.json +++ b/homeassistant/components/meater/translations/zh-Hant.json @@ -6,11 +6,20 @@ "unknown_auth_error": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\u5bc6\u78bc" + }, + "description": "\u78ba\u8a8d Meater Cloud \u5e33\u865f {username} \u5bc6\u78bc\u3002" + }, "user": { "data": { "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, + "data_description": { + "username": "Meater Cloud \u4f7f\u7528\u8005\u540d\u7a31\u3001\u901a\u5e38\u70ba\u96fb\u5b50\u90f5\u4ef6\u4f4d\u5740\u3002" + }, "description": "\u8a2d\u5b9a Meater Cloud \u5e33\u865f\u3002" } } diff --git a/homeassistant/components/owntracks/translations/fr.json b/homeassistant/components/owntracks/translations/fr.json index 9d651c66e94..e2e440cafa5 100644 --- a/homeassistant/components/owntracks/translations/fr.json +++ b/homeassistant/components/owntracks/translations/fr.json @@ -9,7 +9,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer OwnTracks?", + "description": "Voulez-vous vraiment configurer OwnTracks\u00a0?", "title": "Configurer OwnTracks" } } diff --git a/homeassistant/components/recorder/translations/no.json b/homeassistant/components/recorder/translations/no.json new file mode 100644 index 00000000000..b1474c13466 --- /dev/null +++ b/homeassistant/components/recorder/translations/no.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Gjeldende starttid for kj\u00f8ring", + "oldest_recorder_run": "Eldste Run Start Time" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index eb23adaabba..1f7cd74043b 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -14,7 +14,7 @@ "unknown": "Unerwarteter Fehler" }, "progress": { - "email_2fa": "Gib den Code f\u00fcr die Zwei-Faktor-Authentifizierung ein, den du per E-Mail erhalten hast." + "email_2fa": "\u00dcberpr\u00fcfe deine E-Mails auf einen Best\u00e4tigungslink von Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index 0da6f6442e4..0aa7cf4cec1 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -3,16 +3,23 @@ "abort": { "already_configured": "This SimpliSafe account is already in use.", "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", - "reauth_successful": "Re-authentication was successful" + "reauth_successful": "Re-authentication was successful", + "wrong_account": "The user credentials provided do not match this SimpliSafe account." }, "error": { + "2fa_timed_out": "Timed out while waiting for two-factor authentication", + "identifier_exists": "Account already registered", "invalid_auth": "Invalid authentication", + "still_awaiting_mfa": "Still awaiting MFA email click", "unknown": "Unexpected error" }, "progress": { "email_2fa": "Check your email for a verification link from Simplisafe." }, "step": { + "mfa": { + "title": "SimpliSafe Multi-Factor Authentication" + }, "reauth_confirm": { "data": { "password": "Password" @@ -28,10 +35,13 @@ }, "user": { "data": { + "auth_code": "Authorization Code", + "code": "Code (used in Home Assistant UI)", "password": "Password", "username": "Username" }, - "description": "Input your username and password." + "description": "Input your username and password.", + "title": "Fill in your information." } } }, diff --git a/homeassistant/components/simplisafe/translations/fr.json b/homeassistant/components/simplisafe/translations/fr.json index b3456fc35ef..1abfb1c4638 100644 --- a/homeassistant/components/simplisafe/translations/fr.json +++ b/homeassistant/components/simplisafe/translations/fr.json @@ -14,7 +14,7 @@ "unknown": "Erreur inattendue" }, "progress": { - "email_2fa": "Saisissez le code d'authentification \u00e0 deux facteurs qui vous a \u00e9t\u00e9 envoy\u00e9 par courriel." + "email_2fa": "Vous devriez recevoir un lien de v\u00e9rification par courriel envoy\u00e9 par Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/no.json b/homeassistant/components/simplisafe/translations/no.json index d0b743e5ff5..06a7260fd0e 100644 --- a/homeassistant/components/simplisafe/translations/no.json +++ b/homeassistant/components/simplisafe/translations/no.json @@ -14,7 +14,7 @@ "unknown": "Uventet feil" }, "progress": { - "email_2fa": "Skriv inn tofaktorautentiseringskoden\n sendt til deg via e-post." + "email_2fa": "Sjekk e-posten din for en bekreftelseslenke fra Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/pl.json b/homeassistant/components/simplisafe/translations/pl.json index f4c8f5096cf..ec574217c58 100644 --- a/homeassistant/components/simplisafe/translations/pl.json +++ b/homeassistant/components/simplisafe/translations/pl.json @@ -14,7 +14,7 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "progress": { - "email_2fa": "Wprowad\u017a kod uwierzytelniania dwusk\u0142adnikowego\nwys\u0142any do Ciebie e-mailem." + "email_2fa": "Sprawd\u017a e-mail z linkiem weryfikacyjnym od Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/pt-BR.json b/homeassistant/components/simplisafe/translations/pt-BR.json index 5257950027b..3cc1703e11c 100644 --- a/homeassistant/components/simplisafe/translations/pt-BR.json +++ b/homeassistant/components/simplisafe/translations/pt-BR.json @@ -14,7 +14,7 @@ "unknown": "Erro inesperado" }, "progress": { - "email_2fa": "Insira o c\u00f3digo de autentica\u00e7\u00e3o de dois fatores\n enviado para voc\u00ea por e-mail." + "email_2fa": "Verifique seu e-mail para obter um link de verifica\u00e7\u00e3o do Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/zh-Hant.json b/homeassistant/components/simplisafe/translations/zh-Hant.json index 649378c1d2c..e3cc230962c 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hant.json +++ b/homeassistant/components/simplisafe/translations/zh-Hant.json @@ -14,7 +14,7 @@ "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "progress": { - "email_2fa": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230\u7684\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u3002" + "email_2fa": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230 \u7684Simplisafe \u9a57\u8b49\u9023\u7d50\u3002" }, "step": { "mfa": { diff --git a/homeassistant/components/sql/translations/no.json b/homeassistant/components/sql/translations/no.json index 292486d3c0f..ab2554c4fb9 100644 --- a/homeassistant/components/sql/translations/no.json +++ b/homeassistant/components/sql/translations/no.json @@ -13,6 +13,7 @@ "data": { "column": "Kolonne", "db_url": "Database URL", + "name": "Navn", "query": "Velg Sp\u00f8rring", "unit_of_measurement": "M\u00e5leenhet", "value_template": "Verdimal" @@ -20,6 +21,7 @@ "data_description": { "column": "Kolonne for returnert foresp\u00f8rsel for \u00e5 presentere som tilstand", "db_url": "Database-URL, la st\u00e5 tom for \u00e5 bruke standard HA-database", + "name": "Navn som vil bli brukt for Config Entry og ogs\u00e5 sensoren", "query": "Sp\u00f8rsm\u00e5let skal kj\u00f8res, m\u00e5 starte med \"SELECT\"", "unit_of_measurement": "M\u00e5leenhet (valgfritt)", "value_template": "Verdimal (valgfritt)" @@ -38,6 +40,7 @@ "data": { "column": "Kolonne", "db_url": "Database URL", + "name": "Navn", "query": "Velg Sp\u00f8rring", "unit_of_measurement": "M\u00e5leenhet", "value_template": "Verdimal" @@ -45,6 +48,7 @@ "data_description": { "column": "Kolonne for returnert foresp\u00f8rsel for \u00e5 presentere som tilstand", "db_url": "Database-URL, la st\u00e5 tom for \u00e5 bruke standard HA-database", + "name": "Navn som vil bli brukt for Config Entry og ogs\u00e5 sensoren", "query": "Sp\u00f8rsm\u00e5let skal kj\u00f8res, m\u00e5 starte med \"SELECT\"", "unit_of_measurement": "M\u00e5leenhet (valgfritt)", "value_template": "Verdimal (valgfritt)" diff --git a/homeassistant/components/steam_online/translations/no.json b/homeassistant/components/steam_online/translations/no.json index 7e0122dfe18..021218823f5 100644 --- a/homeassistant/components/steam_online/translations/no.json +++ b/homeassistant/components/steam_online/translations/no.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Steam-integrasjonen m\u00e5 re-autentiseres manuelt \n\n Du finner n\u00f8kkelen din her: https://steamcommunity.com/dev/apikey", + "description": "Steam-integrasjonen m\u00e5 re-autentiseres manuelt \n\n Du finner n\u00f8kkelen din her: {api_key_url}", "title": "Godkjenne integrering p\u00e5 nytt" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam-konto-ID", "api_key": "API-n\u00f8kkel" }, - "description": "Bruk https://steamid.io for \u00e5 finne din Steam-konto-ID" + "description": "Bruk {account_id_url} for \u00e5 finne Steam-konto-ID-en din" } } }, diff --git a/homeassistant/components/traccar/translations/fr.json b/homeassistant/components/traccar/translations/fr.json index ab5d254dd98..79e881b674b 100644 --- a/homeassistant/components/traccar/translations/fr.json +++ b/homeassistant/components/traccar/translations/fr.json @@ -10,7 +10,7 @@ }, "step": { "user": { - "description": "\u00cates-vous s\u00fbr de vouloir configurer Traccar?", + "description": "Voulez-vous vraiment configurer Traccar\u00a0?", "title": "Configurer Traccar" } } diff --git a/homeassistant/components/unifi/translations/el.json b/homeassistant/components/unifi/translations/el.json index b017910c279..6c150fe8133 100644 --- a/homeassistant/components/unifi/translations/el.json +++ b/homeassistant/components/unifi/translations/el.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 UniFi \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/hu.json b/homeassistant/components/unifi/translations/hu.json index ea4c7484d44..f06c30d5343 100644 --- a/homeassistant/components/unifi/translations/hu.json +++ b/homeassistant/components/unifi/translations/hu.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "Az UniFi integr\u00e1ci\u00f3 nincs be\u00e1ll\u00edtva" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/nl.json b/homeassistant/components/unifi/translations/nl.json index 5cadc27117c..8b67bec0364 100644 --- a/homeassistant/components/unifi/translations/nl.json +++ b/homeassistant/components/unifi/translations/nl.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi-integratie is niet ingesteld" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/no.json b/homeassistant/components/unifi/translations/no.json index ec43a8f295b..2253bbd5023 100644 --- a/homeassistant/components/unifi/translations/no.json +++ b/homeassistant/components/unifi/translations/no.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi Network-nettstedet er allerede konfigurert", - "configuration_updated": "Konfigurasjonen er oppdatert.", + "configuration_updated": "Konfigurasjonen er oppdatert", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi-integrasjon er ikke konfigurert" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/pl.json b/homeassistant/components/unifi/translations/pl.json index a52660ac262..40bbc9e47a4 100644 --- a/homeassistant/components/unifi/translations/pl.json +++ b/homeassistant/components/unifi/translations/pl.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "Integracja UniFi nie jest skonfigurowana" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/ru.json b/homeassistant/components/unifi/translations/ru.json index 4d9726ab751..68247ef09e1 100644 --- a/homeassistant/components/unifi/translations/ru.json +++ b/homeassistant/components/unifi/translations/ru.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f UniFi \u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430." + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/zh-Hant.json b/homeassistant/components/unifi/translations/zh-Hant.json index 1b394bae317..c6cb4724697 100644 --- a/homeassistant/components/unifi/translations/zh-Hant.json +++ b/homeassistant/components/unifi/translations/zh-Hant.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi \u7db2\u8def\u5df2\u7d93\u8a2d\u5b9a", - "configuration_updated": "\u8a2d\u5b9a\u5df2\u66f4\u65b0\u3002", + "configuration_updated": "\u8a2d\u5b9a\u5df2\u66f4\u65b0", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi \u6574\u5408\u5c1a\u672a\u8a2d\u5b9a" + }, "step": { "client_control": { "data": { From 29bda196b5e0a90a2bea7e1797742236114afc1c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 2 May 2022 23:53:56 -0500 Subject: [PATCH 169/930] Break apart recorder into tasks and core modules (#71222) --- .strict-typing | 2 + homeassistant/components/recorder/__init__.py | 1304 +---------------- homeassistant/components/recorder/const.py | 9 + homeassistant/components/recorder/core.py | 1082 ++++++++++++++ homeassistant/components/recorder/tasks.py | 250 ++++ mypy.ini | 22 + tests/components/history/test_init.py | 25 +- tests/components/recorder/test_history.py | 83 +- tests/components/recorder/test_init.py | 50 +- tests/components/recorder/test_migrate.py | 34 +- tests/components/recorder/test_purge.py | 33 +- tests/components/recorder/test_statistics.py | 12 +- tests/components/recorder/test_util.py | 6 +- .../components/recorder/test_websocket_api.py | 7 +- tests/components/sensor/test_recorder.py | 68 +- 15 files changed, 1583 insertions(+), 1404 deletions(-) create mode 100644 homeassistant/components/recorder/core.py create mode 100644 homeassistant/components/recorder/tasks.py diff --git a/.strict-typing b/.strict-typing index df54a082119..2915f398953 100644 --- a/.strict-typing +++ b/.strict-typing @@ -179,6 +179,7 @@ homeassistant.components.rdw.* homeassistant.components.recollect_waste.* homeassistant.components.recorder homeassistant.components.recorder.const +homeassistant.components.recorder.core homeassistant.components.recorder.backup homeassistant.components.recorder.executor homeassistant.components.recorder.history @@ -189,6 +190,7 @@ homeassistant.components.recorder.repack homeassistant.components.recorder.run_history homeassistant.components.recorder.statistics homeassistant.components.recorder.system_health +homeassistant.components.recorder.tasks homeassistant.components.recorder.util homeassistant.components.recorder.websocket_api homeassistant.components.remote.* diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 1d81495c3a5..f9d462abeea 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -1,55 +1,18 @@ """Support for recording details.""" from __future__ import annotations -import abc -import asyncio -from collections.abc import Callable, Iterable -from dataclasses import dataclass -from datetime import datetime, timedelta import logging -import queue -import sqlite3 -import threading -import time -from typing import Any, TypeVar, cast +from typing import Any -from lru import LRU # pylint: disable=no-name-in-module -from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select -from sqlalchemy.engine import Engine -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.orm.session import Session import voluptuous as vol -from homeassistant.components import persistent_notification -from homeassistant.const import ( - ATTR_ENTITY_ID, - CONF_EXCLUDE, - EVENT_HOMEASSISTANT_FINAL_WRITE, - EVENT_HOMEASSISTANT_STARTED, - EVENT_HOMEASSISTANT_STOP, - EVENT_STATE_CHANGED, - MATCH_ALL, -) -from homeassistant.core import ( - CALLBACK_TYPE, - CoreState, - Event, - HomeAssistant, - ServiceCall, - callback, -) +from homeassistant.const import CONF_EXCLUDE, EVENT_STATE_CHANGED +from homeassistant.core import HomeAssistant, ServiceCall, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER, convert_include_exclude_filter, - generate_filter, -) -from homeassistant.helpers.event import ( - async_track_time_change, - async_track_time_interval, - async_track_utc_time_change, ) from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, @@ -57,58 +20,29 @@ from homeassistant.helpers.integration_platform import ( from homeassistant.helpers.service import async_extract_entity_ids from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -import homeassistant.util.dt as dt_util -from . import history, migration, purge, statistics, websocket_api +from . import history, statistics, websocket_api from .const import ( + ATTR_APPLY_FILTER, + ATTR_KEEP_DAYS, + ATTR_REPACK, CONF_DB_INTEGRITY_CHECK, DATA_INSTANCE, - DB_WORKER_PREFIX, DOMAIN, - MAX_QUEUE_BACKLOG, + EXCLUDE_ATTRIBUTES, SQLITE_URL_PREFIX, ) -from .executor import DBInterruptibleThreadPoolExecutor -from .models import ( - Base, - EventData, - Events, - StateAttributes, - States, - StatisticData, - StatisticMetaData, - StatisticsRuns, - process_timestamp, -) -from .pool import POOL_SIZE, MutexPool, RecorderPool -from .queries import find_shared_attributes_id, find_shared_data_id -from .run_history import RunHistory -from .util import ( - dburl_to_path, - end_incomplete_runs, - is_second_sunday, - move_away_broken_database, - periodic_db_cleanups, - session_scope, - setup_connection_for_dialect, - validate_or_move_away_sqlite_database, - write_lock_db_sqlite, -) +from .core import Recorder +from .tasks import AddRecorderPlatformTask _LOGGER = logging.getLogger(__name__) -T = TypeVar("T") - -EXCLUDE_ATTRIBUTES = f"{DOMAIN}_exclude_attributes_by_domain" SERVICE_PURGE = "purge" SERVICE_PURGE_ENTITIES = "purge_entities" SERVICE_ENABLE = "enable" SERVICE_DISABLE = "disable" -ATTR_KEEP_DAYS = "keep_days" -ATTR_REPACK = "repack" -ATTR_APPLY_FILTER = "apply_filter" SERVICE_PURGE_SCHEMA = vol.Schema( { @@ -138,26 +72,6 @@ DEFAULT_DB_INTEGRITY_CHECK = True DEFAULT_DB_MAX_RETRIES = 10 DEFAULT_DB_RETRY_WAIT = 3 DEFAULT_COMMIT_INTERVAL = 1 -KEEPALIVE_TIME = 30 - -# Controls how often we clean up -# States and Events objects -EXPIRE_AFTER_COMMITS = 120 - -# The number of attribute ids to cache in memory -# -# Based on: -# - The number of overlapping attributes -# - How frequently states with overlapping attributes will change -# - How much memory our low end hardware has -STATE_ATTRIBUTES_ID_CACHE_SIZE = 2048 -EVENT_DATA_ID_CACHE_SIZE = 2048 - -SHUTDOWN_TASK = object() - - -DB_LOCK_TIMEOUT = 30 -DB_LOCK_QUEUE_CHECK_TIMEOUT = 1 CONF_AUTO_PURGE = "auto_purge" CONF_AUTO_REPACK = "auto_repack" @@ -169,8 +83,6 @@ CONF_PURGE_INTERVAL = "purge_interval" CONF_EVENT_TYPES = "event_types" CONF_COMMIT_INTERVAL = "commit_interval" -INVALIDATED_ERR = "Database connection invalidated" -CONNECTIVITY_ERR = "Error in database connectivity during commit" EXCLUDE_SCHEMA = INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER.extend( {vol.Optional(CONF_EVENT_TYPES): vol.All(cv.ensure_list, [cv.string])} @@ -227,10 +139,6 @@ CONFIG_SCHEMA = vol.Schema( ) -# Pool size must accommodate Recorder thread + All db executors -MAX_DB_EXECUTOR_WORKERS = POOL_SIZE - 1 - - def get_instance(hass: HomeAssistant) -> Recorder: """Get the recorder instance.""" instance: Recorder = hass.data[DATA_INSTANCE] @@ -351,1195 +259,3 @@ def _async_register_services(hass: HomeAssistant, instance: Recorder) -> None: async_handle_disable_service, schema=SERVICE_DISABLE_SCHEMA, ) - - -class RecorderTask(abc.ABC): - """ABC for recorder tasks.""" - - commit_before = True - - @abc.abstractmethod - def run(self, instance: Recorder) -> None: - """Handle the task.""" - - -@dataclass -class ClearStatisticsTask(RecorderTask): - """Object to store statistics_ids which for which to remove statistics.""" - - statistic_ids: list[str] - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - statistics.clear_statistics(instance, self.statistic_ids) - - -@dataclass -class UpdateStatisticsMetadataTask(RecorderTask): - """Object to store statistics_id and unit for update of statistics metadata.""" - - statistic_id: str - unit_of_measurement: str | None - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - statistics.update_statistics_metadata( - instance, self.statistic_id, self.unit_of_measurement - ) - - -@dataclass -class PurgeTask(RecorderTask): - """Object to store information about purge task.""" - - purge_before: datetime - repack: bool - apply_filter: bool - - def run(self, instance: Recorder) -> None: - """Purge the database.""" - assert instance.get_session is not None - - if purge.purge_old_data( - instance, self.purge_before, self.repack, self.apply_filter - ): - with instance.get_session() as session: - instance.run_history.load_from_db(session) - # We always need to do the db cleanups after a purge - # is finished to ensure the WAL checkpoint and other - # tasks happen after a vacuum. - periodic_db_cleanups(instance) - return - # Schedule a new purge task if this one didn't finish - instance.queue.put(PurgeTask(self.purge_before, self.repack, self.apply_filter)) - - -@dataclass -class PurgeEntitiesTask(RecorderTask): - """Object to store entity information about purge task.""" - - entity_filter: Callable[[str], bool] - - def run(self, instance: Recorder) -> None: - """Purge entities from the database.""" - if purge.purge_entity_data(instance, self.entity_filter): - return - # Schedule a new purge task if this one didn't finish - instance.queue.put(PurgeEntitiesTask(self.entity_filter)) - - -@dataclass -class PerodicCleanupTask(RecorderTask): - """An object to insert into the recorder to trigger cleanup tasks when auto purge is disabled.""" - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - periodic_db_cleanups(instance) - - -@dataclass -class StatisticsTask(RecorderTask): - """An object to insert into the recorder queue to run a statistics task.""" - - start: datetime - - def run(self, instance: Recorder) -> None: - """Run statistics task.""" - if statistics.compile_statistics(instance, self.start): - return - # Schedule a new statistics task if this one didn't finish - instance.queue.put(StatisticsTask(self.start)) - - -@dataclass -class ExternalStatisticsTask(RecorderTask): - """An object to insert into the recorder queue to run an external statistics task.""" - - metadata: StatisticMetaData - statistics: Iterable[StatisticData] - - def run(self, instance: Recorder) -> None: - """Run statistics task.""" - if statistics.add_external_statistics(instance, self.metadata, self.statistics): - return - # Schedule a new statistics task if this one didn't finish - instance.queue.put(ExternalStatisticsTask(self.metadata, self.statistics)) - - -@dataclass -class AdjustStatisticsTask(RecorderTask): - """An object to insert into the recorder queue to run an adjust statistics task.""" - - statistic_id: str - start_time: datetime - sum_adjustment: float - - def run(self, instance: Recorder) -> None: - """Run statistics task.""" - if statistics.adjust_statistics( - instance, - self.statistic_id, - self.start_time, - self.sum_adjustment, - ): - return - # Schedule a new adjust statistics task if this one didn't finish - instance.queue.put( - AdjustStatisticsTask( - self.statistic_id, self.start_time, self.sum_adjustment - ) - ) - - -@dataclass -class WaitTask(RecorderTask): - """An object to insert into the recorder queue to tell it set the _queue_watch event.""" - - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - instance._queue_watch.set() # pylint: disable=[protected-access] - - -@dataclass -class DatabaseLockTask(RecorderTask): - """An object to insert into the recorder queue to prevent writes to the database.""" - - database_locked: asyncio.Event - database_unlock: threading.Event - queue_overflow: bool - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - instance._lock_database(self) # pylint: disable=[protected-access] - - -@dataclass -class StopTask(RecorderTask): - """An object to insert into the recorder queue to stop the event handler.""" - - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - instance.stop_requested = True - - -@dataclass -class EventTask(RecorderTask): - """An event to be processed.""" - - event: Event - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - # pylint: disable-next=[protected-access] - instance._process_one_event(self.event) - - -@dataclass -class KeepAliveTask(RecorderTask): - """A keep alive to be sent.""" - - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - # pylint: disable-next=[protected-access] - instance._send_keep_alive() - - -@dataclass -class CommitTask(RecorderTask): - """Commit the event session.""" - - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - # pylint: disable-next=[protected-access] - instance._commit_event_session_or_retry() - - -@dataclass -class AddRecorderPlatformTask(RecorderTask): - """Add a recorder platform.""" - - domain: str - platform: Any - commit_before = False - - def run(self, instance: Recorder) -> None: - """Handle the task.""" - hass = instance.hass - domain = self.domain - platform = self.platform - - platforms: dict[str, Any] = hass.data[DOMAIN] - platforms[domain] = platform - if hasattr(self.platform, "exclude_attributes"): - hass.data[EXCLUDE_ATTRIBUTES][domain] = platform.exclude_attributes(hass) - - -COMMIT_TASK = CommitTask() -KEEP_ALIVE_TASK = KeepAliveTask() - - -class Recorder(threading.Thread): - """A threaded recorder class.""" - - stop_requested: bool - - def __init__( - self, - hass: HomeAssistant, - auto_purge: bool, - auto_repack: bool, - keep_days: int, - commit_interval: int, - uri: str, - db_max_retries: int, - db_retry_wait: int, - entity_filter: Callable[[str], bool], - exclude_t: list[str], - exclude_attributes_by_domain: dict[str, set[str]], - ) -> None: - """Initialize the recorder.""" - threading.Thread.__init__(self, name="Recorder") - - self.hass = hass - self.auto_purge = auto_purge - self.auto_repack = auto_repack - self.keep_days = keep_days - self._hass_started: asyncio.Future[object] = asyncio.Future() - self.commit_interval = commit_interval - self.queue: queue.SimpleQueue[RecorderTask] = queue.SimpleQueue() - self.db_url = uri - self.db_max_retries = db_max_retries - self.db_retry_wait = db_retry_wait - self.async_db_ready: asyncio.Future[bool] = asyncio.Future() - self.async_recorder_ready = asyncio.Event() - self._queue_watch = threading.Event() - self.engine: Engine | None = None - self.run_history = RunHistory() - - self.entity_filter = entity_filter - self.exclude_t = exclude_t - - self._commits_without_expire = 0 - self._old_states: dict[str, States] = {} - self._state_attributes_ids: LRU = LRU(STATE_ATTRIBUTES_ID_CACHE_SIZE) - self._event_data_ids: LRU = LRU(EVENT_DATA_ID_CACHE_SIZE) - self._pending_state_attributes: dict[str, StateAttributes] = {} - self._pending_event_data: dict[str, EventData] = {} - self._pending_expunge: list[States] = [] - self.event_session: Session | None = None - self.get_session: Callable[[], Session] | None = None - self._completed_first_database_setup: bool | None = None - self._event_listener: CALLBACK_TYPE | None = None - self.async_migration_event = asyncio.Event() - self.migration_in_progress = False - self._queue_watcher: CALLBACK_TYPE | None = None - self._db_supports_row_number = True - self._database_lock_task: DatabaseLockTask | None = None - self._db_executor: DBInterruptibleThreadPoolExecutor | None = None - self._exclude_attributes_by_domain = exclude_attributes_by_domain - - self._keep_alive_listener: CALLBACK_TYPE | None = None - self._commit_listener: CALLBACK_TYPE | None = None - self._periodic_listener: CALLBACK_TYPE | None = None - self._nightly_listener: CALLBACK_TYPE | None = None - self.enabled = True - - def set_enable(self, enable: bool) -> None: - """Enable or disable recording events and states.""" - self.enabled = enable - - @callback - def async_start_executor(self) -> None: - """Start the executor.""" - self._db_executor = DBInterruptibleThreadPoolExecutor( - thread_name_prefix=DB_WORKER_PREFIX, - max_workers=MAX_DB_EXECUTOR_WORKERS, - shutdown_hook=self._shutdown_pool, - ) - - def _shutdown_pool(self) -> None: - """Close the dbpool connections in the current thread.""" - if self.engine and hasattr(self.engine.pool, "shutdown"): - self.engine.pool.shutdown() - - @callback - def async_initialize(self) -> None: - """Initialize the recorder.""" - self._event_listener = self.hass.bus.async_listen( - MATCH_ALL, self.event_listener, event_filter=self._async_event_filter - ) - self._queue_watcher = async_track_time_interval( - self.hass, self._async_check_queue, timedelta(minutes=10) - ) - - @callback - def _async_keep_alive(self, now: datetime) -> None: - """Queue a keep alive.""" - if self._event_listener: - self.queue.put(KEEP_ALIVE_TASK) - - @callback - def _async_commit(self, now: datetime) -> None: - """Queue a commit.""" - if ( - self._event_listener - and not self._database_lock_task - and self._event_session_has_pending_writes() - ): - self.queue.put(COMMIT_TASK) - - @callback - def async_add_executor_job( - self, target: Callable[..., T], *args: Any - ) -> asyncio.Future[T]: - """Add an executor job from within the event loop.""" - return self.hass.loop.run_in_executor(self._db_executor, target, *args) - - def _stop_executor(self) -> None: - """Stop the executor.""" - assert self._db_executor is not None - self._db_executor.shutdown() - self._db_executor = None - - @callback - def _async_check_queue(self, *_: Any) -> None: - """Periodic check of the queue size to ensure we do not exaust memory. - - The queue grows during migraton or if something really goes wrong. - """ - size = self.queue.qsize() - _LOGGER.debug("Recorder queue size is: %s", size) - if size <= MAX_QUEUE_BACKLOG: - return - _LOGGER.error( - "The recorder backlog queue reached the maximum size of %s events; " - "usually, the system is CPU bound, I/O bound, or the database " - "is corrupt due to a disk problem; The recorder will stop " - "recording events to avoid running out of memory", - MAX_QUEUE_BACKLOG, - ) - self._async_stop_queue_watcher_and_event_listener() - - @callback - def _async_stop_queue_watcher_and_event_listener(self) -> None: - """Stop watching the queue and listening for events.""" - if self._queue_watcher: - self._queue_watcher() - self._queue_watcher = None - if self._event_listener: - self._event_listener() - self._event_listener = None - - @callback - def _async_stop_listeners(self) -> None: - """Stop listeners.""" - self._async_stop_queue_watcher_and_event_listener() - if self._keep_alive_listener: - self._keep_alive_listener() - self._keep_alive_listener = None - if self._commit_listener: - self._commit_listener() - self._commit_listener = None - if self._nightly_listener: - self._nightly_listener() - self._nightly_listener = None - if self._periodic_listener: - self._periodic_listener() - self._periodic_listener = None - - @callback - def _async_event_filter(self, event: Event) -> bool: - """Filter events.""" - if event.event_type in self.exclude_t: - return False - - if (entity_id := event.data.get(ATTR_ENTITY_ID)) is None: - return True - - if isinstance(entity_id, str): - return self.entity_filter(entity_id) - - if isinstance(entity_id, list): - for eid in entity_id: - if self.entity_filter(eid): - return True - return False - - # Unknown what it is. - return True - - def do_adhoc_purge(self, **kwargs: Any) -> None: - """Trigger an adhoc purge retaining keep_days worth of data.""" - keep_days = kwargs.get(ATTR_KEEP_DAYS, self.keep_days) - repack = cast(bool, kwargs[ATTR_REPACK]) - apply_filter = cast(bool, kwargs[ATTR_APPLY_FILTER]) - - purge_before = dt_util.utcnow() - timedelta(days=keep_days) - self.queue.put(PurgeTask(purge_before, repack, apply_filter)) - - def do_adhoc_purge_entities( - self, entity_ids: set[str], domains: list[str], entity_globs: list[str] - ) -> None: - """Trigger an adhoc purge of requested entities.""" - entity_filter = generate_filter(domains, list(entity_ids), [], [], entity_globs) - self.queue.put(PurgeEntitiesTask(entity_filter)) - - def do_adhoc_statistics(self, **kwargs: Any) -> None: - """Trigger an adhoc statistics run.""" - if not (start := kwargs.get("start")): - start = statistics.get_start_time() - self.queue.put(StatisticsTask(start)) - - @callback - def async_register(self) -> None: - """Post connection initialize.""" - - def _empty_queue(event: Event) -> None: - """Empty the queue if its still present at final write.""" - - # If the queue is full of events to be processed because - # the database is so broken that every event results in a retry - # we will never be able to get though the events to shutdown in time. - # - # We drain all the events in the queue and then insert - # an empty one to ensure the next thing the recorder sees - # is a request to shutdown. - while True: - try: - self.queue.get_nowait() - except queue.Empty: - break - self.queue.put(StopTask()) - - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_FINAL_WRITE, _empty_queue) - - async def _async_shutdown(event: Event) -> None: - """Shut down the Recorder.""" - if not self._hass_started.done(): - self._hass_started.set_result(SHUTDOWN_TASK) - self.queue.put(StopTask()) - self._async_stop_listeners() - await self.hass.async_add_executor_job(self.join) - - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_shutdown) - - if self.hass.state == CoreState.running: - self._hass_started.set_result(None) - return - - @callback - def _async_hass_started(event: Event) -> None: - """Notify that hass has started.""" - self._hass_started.set_result(None) - - self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STARTED, _async_hass_started - ) - - @callback - def async_connection_failed(self) -> None: - """Connect failed tasks.""" - self.async_db_ready.set_result(False) - persistent_notification.async_create( - self.hass, - "The recorder could not start, check [the logs](/config/logs)", - "Recorder", - ) - self._async_stop_listeners() - - @callback - def async_connection_success(self) -> None: - """Connect success tasks.""" - self.async_db_ready.set_result(True) - self.async_start_executor() - - @callback - def _async_recorder_ready(self) -> None: - """Finish start and mark recorder ready.""" - self._async_setup_periodic_tasks() - self.async_recorder_ready.set() - - @callback - def async_nightly_tasks(self, now: datetime) -> None: - """Trigger the purge.""" - if self.auto_purge: - # Purge will schedule the periodic cleanups - # after it completes to ensure it does not happen - # until after the database is vacuumed - repack = self.auto_repack and is_second_sunday(now) - purge_before = dt_util.utcnow() - timedelta(days=self.keep_days) - self.queue.put(PurgeTask(purge_before, repack=repack, apply_filter=False)) - else: - self.queue.put(PerodicCleanupTask()) - - @callback - def async_periodic_statistics(self, now: datetime) -> None: - """Trigger the statistics run. - - Short term statistics run every 5 minutes - """ - start = statistics.get_start_time() - self.queue.put(StatisticsTask(start)) - - @callback - def async_adjust_statistics( - self, statistic_id: str, start_time: datetime, sum_adjustment: float - ) -> None: - """Adjust statistics.""" - self.queue.put(AdjustStatisticsTask(statistic_id, start_time, sum_adjustment)) - - @callback - def async_clear_statistics(self, statistic_ids: list[str]) -> None: - """Clear statistics for a list of statistic_ids.""" - self.queue.put(ClearStatisticsTask(statistic_ids)) - - @callback - def async_update_statistics_metadata( - self, statistic_id: str, unit_of_measurement: str | None - ) -> None: - """Update statistics metadata for a statistic_id.""" - self.queue.put(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) - - @callback - def async_external_statistics( - self, metadata: StatisticMetaData, stats: Iterable[StatisticData] - ) -> None: - """Schedule external statistics.""" - self.queue.put(ExternalStatisticsTask(metadata, stats)) - - @callback - def using_sqlite(self) -> bool: - """Return if recorder uses sqlite as the engine.""" - return bool(self.engine and self.engine.dialect.name == "sqlite") - - @callback - def _async_setup_periodic_tasks(self) -> None: - """Prepare periodic tasks.""" - if self.hass.is_stopping or not self.get_session: - # Home Assistant is shutting down - return - - # If the db is using a socket connection, we need to keep alive - # to prevent errors from unexpected disconnects - if not self.using_sqlite(): - self._keep_alive_listener = async_track_time_interval( - self.hass, self._async_keep_alive, timedelta(seconds=KEEPALIVE_TIME) - ) - - # If the commit interval is not 0, we need to commit periodically - if self.commit_interval: - self._commit_listener = async_track_time_interval( - self.hass, self._async_commit, timedelta(seconds=self.commit_interval) - ) - - # Run nightly tasks at 4:12am - self._nightly_listener = async_track_time_change( - self.hass, self.async_nightly_tasks, hour=4, minute=12, second=0 - ) - - # Compile short term statistics every 5 minutes - self._periodic_listener = async_track_utc_time_change( - self.hass, self.async_periodic_statistics, minute=range(0, 60, 5), second=10 - ) - - async def _async_wait_for_started(self) -> object | None: - """Wait for the hass started future.""" - return await self._hass_started - - def _wait_startup_or_shutdown(self) -> object | None: - """Wait for startup or shutdown before starting.""" - return asyncio.run_coroutine_threadsafe( - self._async_wait_for_started(), self.hass.loop - ).result() - - def run(self) -> None: - """Start processing events to save.""" - current_version = self._setup_recorder() - - if current_version is None: - self.hass.add_job(self.async_connection_failed) - return - - schema_is_current = migration.schema_is_current(current_version) - if schema_is_current: - self._setup_run() - else: - self.migration_in_progress = True - - self.hass.add_job(self.async_connection_success) - - # If shutdown happened before Home Assistant finished starting - if self._wait_startup_or_shutdown() is SHUTDOWN_TASK: - self.migration_in_progress = False - # Make sure we cleanly close the run if - # we restart before startup finishes - self._shutdown() - return - - # We wait to start the migration until startup has finished - # since it can be cpu intensive and we do not want it to compete - # with startup which is also cpu intensive - if not schema_is_current: - if self._migrate_schema_and_setup_run(current_version): - if not self._event_listener: - # If the schema migration takes so long that the end - # queue watcher safety kicks in because MAX_QUEUE_BACKLOG - # is reached, we need to reinitialize the listener. - self.hass.add_job(self.async_initialize) - else: - persistent_notification.create( - self.hass, - "The database migration failed, check [the logs](/config/logs)." - "Database Migration Failed", - "recorder_database_migration", - ) - self._shutdown() - return - - _LOGGER.debug("Recorder processing the queue") - self.hass.add_job(self._async_recorder_ready) - self._run_event_loop() - - def _run_event_loop(self) -> None: - """Run the event loop for the recorder.""" - # Use a session for the event read loop - # with a commit every time the event time - # has changed. This reduces the disk io. - self.stop_requested = False - while not self.stop_requested: - task = self.queue.get() - _LOGGER.debug("Processing task: %s", task) - try: - self._process_one_task_or_recover(task) - except Exception as err: # pylint: disable=broad-except - _LOGGER.exception("Error while processing event %s: %s", task, err) - - self._shutdown() - - def _process_one_task_or_recover(self, task: RecorderTask) -> None: - """Process an event, reconnect, or recover a malformed database.""" - try: - # If its not an event, commit everything - # that is pending before running the task - if task.commit_before: - self._commit_event_session_or_retry() - return task.run(self) - except exc.DatabaseError as err: - if self._handle_database_error(err): - return - _LOGGER.exception( - "Unhandled database error while processing task %s: %s", task, err - ) - except SQLAlchemyError as err: - _LOGGER.exception("SQLAlchemyError error processing task %s: %s", task, err) - - # Reset the session if an SQLAlchemyError (including DatabaseError) - # happens to rollback and recover - self._reopen_event_session() - - def _setup_recorder(self) -> None | int: - """Create connect to the database and get the schema version.""" - tries = 1 - - while tries <= self.db_max_retries: - try: - self._setup_connection() - return migration.get_schema_version(self) - except Exception as err: # pylint: disable=broad-except - _LOGGER.exception( - "Error during connection setup: %s (retrying in %s seconds)", - err, - self.db_retry_wait, - ) - tries += 1 - time.sleep(self.db_retry_wait) - - return None - - @callback - def _async_migration_started(self) -> None: - """Set the migration started event.""" - self.async_migration_event.set() - - def _migrate_schema_and_setup_run(self, current_version: int) -> bool: - """Migrate schema to the latest version.""" - persistent_notification.create( - self.hass, - "System performance will temporarily degrade during the database upgrade. Do not power down or restart the system until the upgrade completes. Integrations that read the database, such as logbook and history, may return inconsistent results until the upgrade completes.", - "Database upgrade in progress", - "recorder_database_migration", - ) - self.hass.add_job(self._async_migration_started) - - try: - migration.migrate_schema(self, current_version) - except exc.DatabaseError as err: - if self._handle_database_error(err): - return True - _LOGGER.exception("Database error during schema migration") - return False - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Error during schema migration") - return False - else: - self._setup_run() - return True - finally: - self.migration_in_progress = False - persistent_notification.dismiss(self.hass, "recorder_database_migration") - - def _lock_database(self, task: DatabaseLockTask) -> None: - @callback - def _async_set_database_locked(task: DatabaseLockTask) -> None: - task.database_locked.set() - - with write_lock_db_sqlite(self): - # Notify that lock is being held, wait until database can be used again. - self.hass.add_job(_async_set_database_locked, task) - while not task.database_unlock.wait(timeout=DB_LOCK_QUEUE_CHECK_TIMEOUT): - if self.queue.qsize() > MAX_QUEUE_BACKLOG * 0.9: - _LOGGER.warning( - "Database queue backlog reached more than 90% of maximum queue " - "length while waiting for backup to finish; recorder will now " - "resume writing to database. The backup can not be trusted and " - "must be restarted" - ) - task.queue_overflow = True - break - _LOGGER.info( - "Database queue backlog reached %d entries during backup", - self.queue.qsize(), - ) - - def _process_one_event(self, event: Event) -> None: - if not self.enabled: - return - if event.event_type == EVENT_STATE_CHANGED: - self._process_state_changed_event_into_session(event) - else: - self._process_non_state_changed_event_into_session(event) - # Commit if the commit interval is zero - if not self.commit_interval: - self._commit_event_session_or_retry() - - def _find_shared_attr_in_db(self, attr_hash: int, shared_attrs: str) -> int | None: - """Find shared attributes in the db from the hash and shared_attrs.""" - # - # Avoid the event session being flushed since it will - # commit all the pending events and states to the database. - # - # The lookup has already have checked to see if the data is cached - # or going to be written in the next commit so there is no - # need to flush before checking the database. - # - assert self.event_session is not None - with self.event_session.no_autoflush: - if attributes_id := self.event_session.execute( - find_shared_attributes_id(attr_hash, shared_attrs) - ).first(): - return cast(int, attributes_id[0]) - return None - - def _find_shared_data_in_db(self, data_hash: int, shared_data: str) -> int | None: - """Find shared event data in the db from the hash and shared_attrs.""" - # - # Avoid the event session being flushed since it will - # commit all the pending events and states to the database. - # - # The lookup has already have checked to see if the data is cached - # or going to be written in the next commit so there is no - # need to flush before checking the database. - # - assert self.event_session is not None - with self.event_session.no_autoflush: - if data_id := self.event_session.execute( - find_shared_data_id(data_hash, shared_data) - ).first(): - return cast(int, data_id[0]) - return None - - def _process_non_state_changed_event_into_session(self, event: Event) -> None: - """Process any event into the session except state changed.""" - assert self.event_session is not None - dbevent = Events.from_event(event) - if not event.data: - self.event_session.add(dbevent) - return - - try: - shared_data = EventData.shared_data_from_event(event) - except (TypeError, ValueError) as ex: - _LOGGER.warning("Event is not JSON serializable: %s: %s", event, ex) - return - - # Matching attributes found in the pending commit - if pending_event_data := self._pending_event_data.get(shared_data): - dbevent.event_data_rel = pending_event_data - # Matching attributes id found in the cache - elif data_id := self._event_data_ids.get(shared_data): - dbevent.data_id = data_id - else: - data_hash = EventData.hash_shared_data(shared_data) - # Matching attributes found in the database - if data_id := self._find_shared_data_in_db(data_hash, shared_data): - self._event_data_ids[shared_data] = dbevent.data_id = data_id - # No matching attributes found, save them in the DB - else: - dbevent_data = EventData(shared_data=shared_data, hash=data_hash) - dbevent.event_data_rel = self._pending_event_data[ - shared_data - ] = dbevent_data - self.event_session.add(dbevent_data) - - self.event_session.add(dbevent) - - def _process_state_changed_event_into_session(self, event: Event) -> None: - """Process a state_changed event into the session.""" - assert self.event_session is not None - try: - dbstate = States.from_event(event) - shared_attrs = StateAttributes.shared_attrs_from_event( - event, self._exclude_attributes_by_domain - ) - except (TypeError, ValueError) as ex: - _LOGGER.warning( - "State is not JSON serializable: %s: %s", - event.data.get("new_state"), - ex, - ) - return - - dbstate.attributes = None - # Matching attributes found in the pending commit - if pending_attributes := self._pending_state_attributes.get(shared_attrs): - dbstate.state_attributes = pending_attributes - # Matching attributes id found in the cache - elif attributes_id := self._state_attributes_ids.get(shared_attrs): - dbstate.attributes_id = attributes_id - else: - attr_hash = StateAttributes.hash_shared_attrs(shared_attrs) - # Matching attributes found in the database - if attributes_id := self._find_shared_attr_in_db(attr_hash, shared_attrs): - dbstate.attributes_id = attributes_id - self._state_attributes_ids[shared_attrs] = attributes_id - # No matching attributes found, save them in the DB - else: - dbstate_attributes = StateAttributes( - shared_attrs=shared_attrs, hash=attr_hash - ) - dbstate.state_attributes = dbstate_attributes - self._pending_state_attributes[shared_attrs] = dbstate_attributes - self.event_session.add(dbstate_attributes) - - if old_state := self._old_states.pop(dbstate.entity_id, None): - if old_state.state_id: - dbstate.old_state_id = old_state.state_id - else: - dbstate.old_state = old_state - if event.data.get("new_state"): - self._old_states[dbstate.entity_id] = dbstate - self._pending_expunge.append(dbstate) - else: - dbstate.state = None - self.event_session.add(dbstate) - - def _handle_database_error(self, err: Exception) -> bool: - """Handle a database error that may result in moving away the corrupt db.""" - if isinstance(err.__cause__, sqlite3.DatabaseError): - _LOGGER.exception( - "Unrecoverable sqlite3 database corruption detected: %s", err - ) - self._handle_sqlite_corruption() - return True - return False - - def _event_session_has_pending_writes(self) -> bool: - return bool( - self.event_session and (self.event_session.new or self.event_session.dirty) - ) - - def _commit_event_session_or_retry(self) -> None: - """Commit the event session if there is work to do.""" - if not self._event_session_has_pending_writes(): - return - tries = 1 - while tries <= self.db_max_retries: - try: - self._commit_event_session() - return - except (exc.InternalError, exc.OperationalError) as err: - _LOGGER.error( - "%s: Error executing query: %s. (retrying in %s seconds)", - INVALIDATED_ERR if err.connection_invalidated else CONNECTIVITY_ERR, - err, - self.db_retry_wait, - ) - if tries == self.db_max_retries: - raise - - tries += 1 - time.sleep(self.db_retry_wait) - - def _commit_event_session(self) -> None: - assert self.event_session is not None - self._commits_without_expire += 1 - - if self._pending_expunge: - self.event_session.flush() - for dbstate in self._pending_expunge: - # Expunge the state so its not expired - # until we use it later for dbstate.old_state - if dbstate in self.event_session: - self.event_session.expunge(dbstate) - self._pending_expunge = [] - self.event_session.commit() - - # We just committed the state attributes to the database - # and we now know the attributes_ids. We can save - # many selects for matching attributes by loading them - # into the LRU cache now. - for state_attr in self._pending_state_attributes.values(): - self._state_attributes_ids[ - state_attr.shared_attrs - ] = state_attr.attributes_id - self._pending_state_attributes = {} - for event_data in self._pending_event_data.values(): - self._event_data_ids[event_data.shared_data] = event_data.data_id - self._pending_event_data = {} - - # Expire is an expensive operation (frequently more expensive - # than the flush and commit itself) so we only - # do it after EXPIRE_AFTER_COMMITS commits - if self._commits_without_expire >= EXPIRE_AFTER_COMMITS: - self._commits_without_expire = 0 - self.event_session.expire_all() - - def _handle_sqlite_corruption(self) -> None: - """Handle the sqlite3 database being corrupt.""" - self._close_event_session() - self._close_connection() - move_away_broken_database(dburl_to_path(self.db_url)) - self.run_history.reset() - self._setup_recorder() - self._setup_run() - - def _close_event_session(self) -> None: - """Close the event session.""" - self._old_states = {} - self._state_attributes_ids = {} - self._event_data_ids = {} - self._pending_state_attributes = {} - self._pending_event_data = {} - - if not self.event_session: - return - - try: - self.event_session.rollback() - self.event_session.close() - except SQLAlchemyError as err: - _LOGGER.exception( - "Error while rolling back and closing the event session: %s", err - ) - - def _reopen_event_session(self) -> None: - """Rollback the event session and reopen it after a failure.""" - self._close_event_session() - self._open_event_session() - - def _open_event_session(self) -> None: - """Open the event session.""" - assert self.get_session is not None - self.event_session = self.get_session() - self.event_session.expire_on_commit = False - - def _send_keep_alive(self) -> None: - """Send a keep alive to keep the db connection open.""" - assert self.event_session is not None - _LOGGER.debug("Sending keepalive") - self.event_session.connection().scalar(select([1])) - - @callback - def event_listener(self, event: Event) -> None: - """Listen for new events and put them in the process queue.""" - self.queue.put(EventTask(event)) - - def block_till_done(self) -> None: - """Block till all events processed. - - This is only called in tests. - - This only blocks until the queue is empty - which does not mean the recorder is done. - - Call tests.common's wait_recording_done - after calling this to ensure the data - is in the database. - """ - self._queue_watch.clear() - self.queue.put(WaitTask()) - self._queue_watch.wait() - - async def lock_database(self) -> bool: - """Lock database so it can be backed up safely.""" - if not self.using_sqlite(): - _LOGGER.debug( - "Not a SQLite database or not connected, locking not necessary" - ) - return True - - if self._database_lock_task: - _LOGGER.warning("Database already locked") - return False - - database_locked = asyncio.Event() - task = DatabaseLockTask(database_locked, threading.Event(), False) - self.queue.put(task) - try: - await asyncio.wait_for(database_locked.wait(), timeout=DB_LOCK_TIMEOUT) - except asyncio.TimeoutError as err: - task.database_unlock.set() - raise TimeoutError( - f"Could not lock database within {DB_LOCK_TIMEOUT} seconds." - ) from err - self._database_lock_task = task - return True - - @callback - def unlock_database(self) -> bool: - """Unlock database. - - Returns true if database lock has been held throughout the process. - """ - if not self.using_sqlite(): - _LOGGER.debug( - "Not a SQLite database or not connected, unlocking not necessary" - ) - return True - - if not self._database_lock_task: - _LOGGER.warning("Database currently not locked") - return False - - self._database_lock_task.database_unlock.set() - success = not self._database_lock_task.queue_overflow - - self._database_lock_task = None - - return success - - def _setup_connection(self) -> None: - """Ensure database is ready to fly.""" - kwargs: dict[str, Any] = {} - self._completed_first_database_setup = False - - def setup_recorder_connection( - dbapi_connection: Any, connection_record: Any - ) -> None: - """Dbapi specific connection settings.""" - assert self.engine is not None - setup_connection_for_dialect( - self, - self.engine.dialect.name, - dbapi_connection, - not self._completed_first_database_setup, - ) - self._completed_first_database_setup = True - - if self.db_url == SQLITE_URL_PREFIX or ":memory:" in self.db_url: - kwargs["connect_args"] = {"check_same_thread": False} - kwargs["poolclass"] = MutexPool - MutexPool.pool_lock = threading.RLock() - kwargs["pool_reset_on_return"] = None - elif self.db_url.startswith(SQLITE_URL_PREFIX): - kwargs["poolclass"] = RecorderPool - else: - kwargs["echo"] = False - - if self._using_file_sqlite: - validate_or_move_away_sqlite_database(self.db_url) - - self.engine = create_engine(self.db_url, **kwargs, future=True) - - sqlalchemy_event.listen(self.engine, "connect", setup_recorder_connection) - - Base.metadata.create_all(self.engine) - self.get_session = scoped_session(sessionmaker(bind=self.engine, future=True)) - _LOGGER.debug("Connected to recorder database") - - @property - def _using_file_sqlite(self) -> bool: - """Short version to check if we are using sqlite3 as a file.""" - return self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith( - SQLITE_URL_PREFIX - ) - - def _close_connection(self) -> None: - """Close the connection.""" - assert self.engine is not None - self.engine.dispose() - self.engine = None - self.get_session = None - - def _setup_run(self) -> None: - """Log the start of the current run and schedule any needed jobs.""" - assert self.get_session is not None - with session_scope(session=self.get_session()) as session: - end_incomplete_runs(session, self.run_history.recording_start) - self.run_history.start(session) - self._schedule_compile_missing_statistics(session) - - self._open_event_session() - - def _schedule_compile_missing_statistics(self, session: Session) -> None: - """Add tasks for missing statistics runs.""" - now = dt_util.utcnow() - last_period_minutes = now.minute - now.minute % 5 - last_period = now.replace(minute=last_period_minutes, second=0, microsecond=0) - start = now - timedelta(days=self.keep_days) - start = start.replace(minute=0, second=0, microsecond=0) - - # Find the newest statistics run, if any - if last_run := session.query(func.max(StatisticsRuns.start)).scalar(): - start = max(start, process_timestamp(last_run) + timedelta(minutes=5)) - - # Add tasks - while start < last_period: - end = start + timedelta(minutes=5) - _LOGGER.debug("Compiling missing statistics for %s-%s", start, end) - self.queue.put(StatisticsTask(start)) - start = end - - def _end_session(self) -> None: - """End the recorder session.""" - if self.event_session is None: - return - try: - self.run_history.end(self.event_session) - self._commit_event_session_or_retry() - self.event_session.close() - except Exception as err: # pylint: disable=broad-except - _LOGGER.exception("Error saving the event session during shutdown: %s", err) - - self.run_history.clear() - - def _shutdown(self) -> None: - """Save end time for current run.""" - self.hass.add_job(self._async_stop_listeners) - self._stop_executor() - self._end_session() - self._close_connection() - - @property - def recording(self) -> bool: - """Return if the recorder is recording.""" - return self._event_listener is not None diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 593710a10dd..5b623ee5e04 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -28,3 +28,12 @@ DB_WORKER_PREFIX = "DbWorker" JSON_DUMP: Final = partial(json.dumps, cls=JSONEncoder, separators=(",", ":")) ALL_DOMAIN_EXCLUDE_ATTRS = {ATTR_ATTRIBUTION, ATTR_RESTORED, ATTR_SUPPORTED_FEATURES} + +ATTR_KEEP_DAYS = "keep_days" +ATTR_REPACK = "repack" +ATTR_APPLY_FILTER = "apply_filter" + +KEEPALIVE_TIME = 30 + + +EXCLUDE_ATTRIBUTES = f"{DOMAIN}_exclude_attributes_by_domain" diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py new file mode 100644 index 00000000000..b96eee2c67f --- /dev/null +++ b/homeassistant/components/recorder/core.py @@ -0,0 +1,1082 @@ +"""Support for recording details.""" +from __future__ import annotations + +import asyncio +from collections.abc import Callable, Iterable +from datetime import datetime, timedelta +import logging +import queue +import sqlite3 +import threading +import time +from typing import Any, TypeVar, cast + +from lru import LRU # pylint: disable=no-name-in-module +from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select +from sqlalchemy.engine import Engine +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import scoped_session, sessionmaker +from sqlalchemy.orm.session import Session + +from homeassistant.components import persistent_notification +from homeassistant.const import ( + ATTR_ENTITY_ID, + EVENT_HOMEASSISTANT_FINAL_WRITE, + EVENT_HOMEASSISTANT_STARTED, + EVENT_HOMEASSISTANT_STOP, + EVENT_STATE_CHANGED, + MATCH_ALL, +) +from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback +from homeassistant.helpers.entityfilter import generate_filter +from homeassistant.helpers.event import ( + async_track_time_change, + async_track_time_interval, + async_track_utc_time_change, +) +import homeassistant.util.dt as dt_util + +from . import migration, statistics +from .const import ( + ATTR_APPLY_FILTER, + ATTR_KEEP_DAYS, + ATTR_REPACK, + DB_WORKER_PREFIX, + KEEPALIVE_TIME, + MAX_QUEUE_BACKLOG, + SQLITE_URL_PREFIX, +) +from .executor import DBInterruptibleThreadPoolExecutor +from .models import ( + Base, + EventData, + Events, + StateAttributes, + States, + StatisticData, + StatisticMetaData, + StatisticsRuns, + process_timestamp, +) +from .pool import POOL_SIZE, MutexPool, RecorderPool +from .queries import find_shared_attributes_id, find_shared_data_id +from .run_history import RunHistory +from .tasks import ( + AdjustStatisticsTask, + ClearStatisticsTask, + CommitTask, + DatabaseLockTask, + EventTask, + ExternalStatisticsTask, + KeepAliveTask, + PerodicCleanupTask, + PurgeEntitiesTask, + PurgeTask, + RecorderTask, + StatisticsTask, + StopTask, + UpdateStatisticsMetadataTask, + WaitTask, +) +from .util import ( + dburl_to_path, + end_incomplete_runs, + is_second_sunday, + move_away_broken_database, + session_scope, + setup_connection_for_dialect, + validate_or_move_away_sqlite_database, + write_lock_db_sqlite, +) + +_LOGGER = logging.getLogger(__name__) + +T = TypeVar("T") + +DEFAULT_URL = "sqlite:///{hass_config_path}" + +# Controls how often we clean up +# States and Events objects +EXPIRE_AFTER_COMMITS = 120 + +# The number of attribute ids to cache in memory +# +# Based on: +# - The number of overlapping attributes +# - How frequently states with overlapping attributes will change +# - How much memory our low end hardware has +STATE_ATTRIBUTES_ID_CACHE_SIZE = 2048 +EVENT_DATA_ID_CACHE_SIZE = 2048 + +SHUTDOWN_TASK = object() + +COMMIT_TASK = CommitTask() +KEEP_ALIVE_TASK = KeepAliveTask() + +DB_LOCK_TIMEOUT = 30 +DB_LOCK_QUEUE_CHECK_TIMEOUT = 1 + + +INVALIDATED_ERR = "Database connection invalidated" +CONNECTIVITY_ERR = "Error in database connectivity during commit" + +# Pool size must accommodate Recorder thread + All db executors +MAX_DB_EXECUTOR_WORKERS = POOL_SIZE - 1 + + +class Recorder(threading.Thread): + """A threaded recorder class.""" + + stop_requested: bool + + def __init__( + self, + hass: HomeAssistant, + auto_purge: bool, + auto_repack: bool, + keep_days: int, + commit_interval: int, + uri: str, + db_max_retries: int, + db_retry_wait: int, + entity_filter: Callable[[str], bool], + exclude_t: list[str], + exclude_attributes_by_domain: dict[str, set[str]], + ) -> None: + """Initialize the recorder.""" + threading.Thread.__init__(self, name="Recorder") + + self.hass = hass + self.auto_purge = auto_purge + self.auto_repack = auto_repack + self.keep_days = keep_days + self._hass_started: asyncio.Future[object] = asyncio.Future() + self.commit_interval = commit_interval + self.queue: queue.SimpleQueue[RecorderTask] = queue.SimpleQueue() + self.db_url = uri + self.db_max_retries = db_max_retries + self.db_retry_wait = db_retry_wait + self.async_db_ready: asyncio.Future[bool] = asyncio.Future() + self.async_recorder_ready = asyncio.Event() + self._queue_watch = threading.Event() + self.engine: Engine | None = None + self.run_history = RunHistory() + + self.entity_filter = entity_filter + self.exclude_t = exclude_t + + self._commits_without_expire = 0 + self._old_states: dict[str, States] = {} + self._state_attributes_ids: LRU = LRU(STATE_ATTRIBUTES_ID_CACHE_SIZE) + self._event_data_ids: LRU = LRU(EVENT_DATA_ID_CACHE_SIZE) + self._pending_state_attributes: dict[str, StateAttributes] = {} + self._pending_event_data: dict[str, EventData] = {} + self._pending_expunge: list[States] = [] + self.event_session: Session | None = None + self.get_session: Callable[[], Session] | None = None + self._completed_first_database_setup: bool | None = None + self._event_listener: CALLBACK_TYPE | None = None + self.async_migration_event = asyncio.Event() + self.migration_in_progress = False + self._queue_watcher: CALLBACK_TYPE | None = None + self._db_supports_row_number = True + self._database_lock_task: DatabaseLockTask | None = None + self._db_executor: DBInterruptibleThreadPoolExecutor | None = None + self._exclude_attributes_by_domain = exclude_attributes_by_domain + + self._keep_alive_listener: CALLBACK_TYPE | None = None + self._commit_listener: CALLBACK_TYPE | None = None + self._periodic_listener: CALLBACK_TYPE | None = None + self._nightly_listener: CALLBACK_TYPE | None = None + self.enabled = True + + def set_enable(self, enable: bool) -> None: + """Enable or disable recording events and states.""" + self.enabled = enable + + @callback + def async_start_executor(self) -> None: + """Start the executor.""" + self._db_executor = DBInterruptibleThreadPoolExecutor( + thread_name_prefix=DB_WORKER_PREFIX, + max_workers=MAX_DB_EXECUTOR_WORKERS, + shutdown_hook=self._shutdown_pool, + ) + + def _shutdown_pool(self) -> None: + """Close the dbpool connections in the current thread.""" + if self.engine and hasattr(self.engine.pool, "shutdown"): + self.engine.pool.shutdown() + + @callback + def async_initialize(self) -> None: + """Initialize the recorder.""" + self._event_listener = self.hass.bus.async_listen( + MATCH_ALL, self.event_listener, event_filter=self._async_event_filter + ) + self._queue_watcher = async_track_time_interval( + self.hass, self._async_check_queue, timedelta(minutes=10) + ) + + @callback + def _async_keep_alive(self, now: datetime) -> None: + """Queue a keep alive.""" + if self._event_listener: + self.queue.put(KEEP_ALIVE_TASK) + + @callback + def _async_commit(self, now: datetime) -> None: + """Queue a commit.""" + if ( + self._event_listener + and not self._database_lock_task + and self._event_session_has_pending_writes() + ): + self.queue.put(COMMIT_TASK) + + @callback + def async_add_executor_job( + self, target: Callable[..., T], *args: Any + ) -> asyncio.Future[T]: + """Add an executor job from within the event loop.""" + return self.hass.loop.run_in_executor(self._db_executor, target, *args) + + def _stop_executor(self) -> None: + """Stop the executor.""" + assert self._db_executor is not None + self._db_executor.shutdown() + self._db_executor = None + + @callback + def _async_check_queue(self, *_: Any) -> None: + """Periodic check of the queue size to ensure we do not exaust memory. + + The queue grows during migraton or if something really goes wrong. + """ + size = self.queue.qsize() + _LOGGER.debug("Recorder queue size is: %s", size) + if size <= MAX_QUEUE_BACKLOG: + return + _LOGGER.error( + "The recorder backlog queue reached the maximum size of %s events; " + "usually, the system is CPU bound, I/O bound, or the database " + "is corrupt due to a disk problem; The recorder will stop " + "recording events to avoid running out of memory", + MAX_QUEUE_BACKLOG, + ) + self._async_stop_queue_watcher_and_event_listener() + + @callback + def _async_stop_queue_watcher_and_event_listener(self) -> None: + """Stop watching the queue and listening for events.""" + if self._queue_watcher: + self._queue_watcher() + self._queue_watcher = None + if self._event_listener: + self._event_listener() + self._event_listener = None + + @callback + def _async_stop_listeners(self) -> None: + """Stop listeners.""" + self._async_stop_queue_watcher_and_event_listener() + if self._keep_alive_listener: + self._keep_alive_listener() + self._keep_alive_listener = None + if self._commit_listener: + self._commit_listener() + self._commit_listener = None + if self._nightly_listener: + self._nightly_listener() + self._nightly_listener = None + if self._periodic_listener: + self._periodic_listener() + self._periodic_listener = None + + @callback + def _async_event_filter(self, event: Event) -> bool: + """Filter events.""" + if event.event_type in self.exclude_t: + return False + + if (entity_id := event.data.get(ATTR_ENTITY_ID)) is None: + return True + + if isinstance(entity_id, str): + return self.entity_filter(entity_id) + + if isinstance(entity_id, list): + for eid in entity_id: + if self.entity_filter(eid): + return True + return False + + # Unknown what it is. + return True + + def do_adhoc_purge(self, **kwargs: Any) -> None: + """Trigger an adhoc purge retaining keep_days worth of data.""" + keep_days = kwargs.get(ATTR_KEEP_DAYS, self.keep_days) + repack = cast(bool, kwargs[ATTR_REPACK]) + apply_filter = cast(bool, kwargs[ATTR_APPLY_FILTER]) + + purge_before = dt_util.utcnow() - timedelta(days=keep_days) + self.queue.put(PurgeTask(purge_before, repack, apply_filter)) + + def do_adhoc_purge_entities( + self, entity_ids: set[str], domains: list[str], entity_globs: list[str] + ) -> None: + """Trigger an adhoc purge of requested entities.""" + entity_filter = generate_filter(domains, list(entity_ids), [], [], entity_globs) + self.queue.put(PurgeEntitiesTask(entity_filter)) + + def do_adhoc_statistics(self, **kwargs: Any) -> None: + """Trigger an adhoc statistics run.""" + if not (start := kwargs.get("start")): + start = statistics.get_start_time() + self.queue.put(StatisticsTask(start)) + + @callback + def async_register(self) -> None: + """Post connection initialize.""" + + def _empty_queue(event: Event) -> None: + """Empty the queue if its still present at final write.""" + + # If the queue is full of events to be processed because + # the database is so broken that every event results in a retry + # we will never be able to get though the events to shutdown in time. + # + # We drain all the events in the queue and then insert + # an empty one to ensure the next thing the recorder sees + # is a request to shutdown. + while True: + try: + self.queue.get_nowait() + except queue.Empty: + break + self.queue.put(StopTask()) + + self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_FINAL_WRITE, _empty_queue) + + async def _async_shutdown(event: Event) -> None: + """Shut down the Recorder.""" + if not self._hass_started.done(): + self._hass_started.set_result(SHUTDOWN_TASK) + self.queue.put(StopTask()) + self._async_stop_listeners() + await self.hass.async_add_executor_job(self.join) + + self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_shutdown) + + if self.hass.state == CoreState.running: + self._hass_started.set_result(None) + return + + @callback + def _async_hass_started(event: Event) -> None: + """Notify that hass has started.""" + self._hass_started.set_result(None) + + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STARTED, _async_hass_started + ) + + @callback + def async_connection_failed(self) -> None: + """Connect failed tasks.""" + self.async_db_ready.set_result(False) + persistent_notification.async_create( + self.hass, + "The recorder could not start, check [the logs](/config/logs)", + "Recorder", + ) + self._async_stop_listeners() + + @callback + def async_connection_success(self) -> None: + """Connect success tasks.""" + self.async_db_ready.set_result(True) + self.async_start_executor() + + @callback + def _async_recorder_ready(self) -> None: + """Finish start and mark recorder ready.""" + self._async_setup_periodic_tasks() + self.async_recorder_ready.set() + + @callback + def async_nightly_tasks(self, now: datetime) -> None: + """Trigger the purge.""" + if self.auto_purge: + # Purge will schedule the periodic cleanups + # after it completes to ensure it does not happen + # until after the database is vacuumed + repack = self.auto_repack and is_second_sunday(now) + purge_before = dt_util.utcnow() - timedelta(days=self.keep_days) + self.queue.put(PurgeTask(purge_before, repack=repack, apply_filter=False)) + else: + self.queue.put(PerodicCleanupTask()) + + @callback + def async_periodic_statistics(self, now: datetime) -> None: + """Trigger the statistics run. + + Short term statistics run every 5 minutes + """ + start = statistics.get_start_time() + self.queue.put(StatisticsTask(start)) + + @callback + def async_adjust_statistics( + self, statistic_id: str, start_time: datetime, sum_adjustment: float + ) -> None: + """Adjust statistics.""" + self.queue.put(AdjustStatisticsTask(statistic_id, start_time, sum_adjustment)) + + @callback + def async_clear_statistics(self, statistic_ids: list[str]) -> None: + """Clear statistics for a list of statistic_ids.""" + self.queue.put(ClearStatisticsTask(statistic_ids)) + + @callback + def async_update_statistics_metadata( + self, statistic_id: str, unit_of_measurement: str | None + ) -> None: + """Update statistics metadata for a statistic_id.""" + self.queue.put(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) + + @callback + def async_external_statistics( + self, metadata: StatisticMetaData, stats: Iterable[StatisticData] + ) -> None: + """Schedule external statistics.""" + self.queue.put(ExternalStatisticsTask(metadata, stats)) + + @callback + def using_sqlite(self) -> bool: + """Return if recorder uses sqlite as the engine.""" + return bool(self.engine and self.engine.dialect.name == "sqlite") + + @callback + def _async_setup_periodic_tasks(self) -> None: + """Prepare periodic tasks.""" + if self.hass.is_stopping or not self.get_session: + # Home Assistant is shutting down + return + + # If the db is using a socket connection, we need to keep alive + # to prevent errors from unexpected disconnects + if not self.using_sqlite(): + self._keep_alive_listener = async_track_time_interval( + self.hass, self._async_keep_alive, timedelta(seconds=KEEPALIVE_TIME) + ) + + # If the commit interval is not 0, we need to commit periodically + if self.commit_interval: + self._commit_listener = async_track_time_interval( + self.hass, self._async_commit, timedelta(seconds=self.commit_interval) + ) + + # Run nightly tasks at 4:12am + self._nightly_listener = async_track_time_change( + self.hass, self.async_nightly_tasks, hour=4, minute=12, second=0 + ) + + # Compile short term statistics every 5 minutes + self._periodic_listener = async_track_utc_time_change( + self.hass, self.async_periodic_statistics, minute=range(0, 60, 5), second=10 + ) + + async def _async_wait_for_started(self) -> object | None: + """Wait for the hass started future.""" + return await self._hass_started + + def _wait_startup_or_shutdown(self) -> object | None: + """Wait for startup or shutdown before starting.""" + return asyncio.run_coroutine_threadsafe( + self._async_wait_for_started(), self.hass.loop + ).result() + + def run(self) -> None: + """Start processing events to save.""" + current_version = self._setup_recorder() + + if current_version is None: + self.hass.add_job(self.async_connection_failed) + return + + schema_is_current = migration.schema_is_current(current_version) + if schema_is_current: + self._setup_run() + else: + self.migration_in_progress = True + + self.hass.add_job(self.async_connection_success) + + # If shutdown happened before Home Assistant finished starting + if self._wait_startup_or_shutdown() is SHUTDOWN_TASK: + self.migration_in_progress = False + # Make sure we cleanly close the run if + # we restart before startup finishes + self._shutdown() + return + + # We wait to start the migration until startup has finished + # since it can be cpu intensive and we do not want it to compete + # with startup which is also cpu intensive + if not schema_is_current: + if self._migrate_schema_and_setup_run(current_version): + if not self._event_listener: + # If the schema migration takes so long that the end + # queue watcher safety kicks in because MAX_QUEUE_BACKLOG + # is reached, we need to reinitialize the listener. + self.hass.add_job(self.async_initialize) + else: + persistent_notification.create( + self.hass, + "The database migration failed, check [the logs](/config/logs)." + "Database Migration Failed", + "recorder_database_migration", + ) + self._shutdown() + return + + _LOGGER.debug("Recorder processing the queue") + self.hass.add_job(self._async_recorder_ready) + self._run_event_loop() + + def _run_event_loop(self) -> None: + """Run the event loop for the recorder.""" + # Use a session for the event read loop + # with a commit every time the event time + # has changed. This reduces the disk io. + self.stop_requested = False + while not self.stop_requested: + task = self.queue.get() + _LOGGER.debug("Processing task: %s", task) + try: + self._process_one_task_or_recover(task) + except Exception as err: # pylint: disable=broad-except + _LOGGER.exception("Error while processing event %s: %s", task, err) + + self._shutdown() + + def _process_one_task_or_recover(self, task: RecorderTask) -> None: + """Process an event, reconnect, or recover a malformed database.""" + try: + # If its not an event, commit everything + # that is pending before running the task + if task.commit_before: + self._commit_event_session_or_retry() + return task.run(self) + except exc.DatabaseError as err: + if self._handle_database_error(err): + return + _LOGGER.exception( + "Unhandled database error while processing task %s: %s", task, err + ) + except SQLAlchemyError as err: + _LOGGER.exception("SQLAlchemyError error processing task %s: %s", task, err) + + # Reset the session if an SQLAlchemyError (including DatabaseError) + # happens to rollback and recover + self._reopen_event_session() + + def _setup_recorder(self) -> None | int: + """Create connect to the database and get the schema version.""" + tries = 1 + + while tries <= self.db_max_retries: + try: + self._setup_connection() + return migration.get_schema_version(self) + except Exception as err: # pylint: disable=broad-except + _LOGGER.exception( + "Error during connection setup: %s (retrying in %s seconds)", + err, + self.db_retry_wait, + ) + tries += 1 + time.sleep(self.db_retry_wait) + + return None + + @callback + def _async_migration_started(self) -> None: + """Set the migration started event.""" + self.async_migration_event.set() + + def _migrate_schema_and_setup_run(self, current_version: int) -> bool: + """Migrate schema to the latest version.""" + persistent_notification.create( + self.hass, + "System performance will temporarily degrade during the database upgrade. Do not power down or restart the system until the upgrade completes. Integrations that read the database, such as logbook and history, may return inconsistent results until the upgrade completes.", + "Database upgrade in progress", + "recorder_database_migration", + ) + self.hass.add_job(self._async_migration_started) + + try: + migration.migrate_schema(self, current_version) + except exc.DatabaseError as err: + if self._handle_database_error(err): + return True + _LOGGER.exception("Database error during schema migration") + return False + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Error during schema migration") + return False + else: + self._setup_run() + return True + finally: + self.migration_in_progress = False + persistent_notification.dismiss(self.hass, "recorder_database_migration") + + def _lock_database(self, task: DatabaseLockTask) -> None: + @callback + def _async_set_database_locked(task: DatabaseLockTask) -> None: + task.database_locked.set() + + with write_lock_db_sqlite(self): + # Notify that lock is being held, wait until database can be used again. + self.hass.add_job(_async_set_database_locked, task) + while not task.database_unlock.wait(timeout=DB_LOCK_QUEUE_CHECK_TIMEOUT): + if self.queue.qsize() > MAX_QUEUE_BACKLOG * 0.9: + _LOGGER.warning( + "Database queue backlog reached more than 90% of maximum queue " + "length while waiting for backup to finish; recorder will now " + "resume writing to database. The backup can not be trusted and " + "must be restarted" + ) + task.queue_overflow = True + break + _LOGGER.info( + "Database queue backlog reached %d entries during backup", + self.queue.qsize(), + ) + + def _process_one_event(self, event: Event) -> None: + if not self.enabled: + return + if event.event_type == EVENT_STATE_CHANGED: + self._process_state_changed_event_into_session(event) + else: + self._process_non_state_changed_event_into_session(event) + # Commit if the commit interval is zero + if not self.commit_interval: + self._commit_event_session_or_retry() + + def _find_shared_attr_in_db(self, attr_hash: int, shared_attrs: str) -> int | None: + """Find shared attributes in the db from the hash and shared_attrs.""" + # + # Avoid the event session being flushed since it will + # commit all the pending events and states to the database. + # + # The lookup has already have checked to see if the data is cached + # or going to be written in the next commit so there is no + # need to flush before checking the database. + # + assert self.event_session is not None + with self.event_session.no_autoflush: + if attributes_id := self.event_session.execute( + find_shared_attributes_id(attr_hash, shared_attrs) + ).first(): + return cast(int, attributes_id[0]) + return None + + def _find_shared_data_in_db(self, data_hash: int, shared_data: str) -> int | None: + """Find shared event data in the db from the hash and shared_attrs.""" + # + # Avoid the event session being flushed since it will + # commit all the pending events and states to the database. + # + # The lookup has already have checked to see if the data is cached + # or going to be written in the next commit so there is no + # need to flush before checking the database. + # + assert self.event_session is not None + with self.event_session.no_autoflush: + if data_id := self.event_session.execute( + find_shared_data_id(data_hash, shared_data) + ).first(): + return cast(int, data_id[0]) + return None + + def _process_non_state_changed_event_into_session(self, event: Event) -> None: + """Process any event into the session except state changed.""" + assert self.event_session is not None + dbevent = Events.from_event(event) + if not event.data: + self.event_session.add(dbevent) + return + + try: + shared_data = EventData.shared_data_from_event(event) + except (TypeError, ValueError) as ex: + _LOGGER.warning("Event is not JSON serializable: %s: %s", event, ex) + return + + # Matching attributes found in the pending commit + if pending_event_data := self._pending_event_data.get(shared_data): + dbevent.event_data_rel = pending_event_data + # Matching attributes id found in the cache + elif data_id := self._event_data_ids.get(shared_data): + dbevent.data_id = data_id + else: + data_hash = EventData.hash_shared_data(shared_data) + # Matching attributes found in the database + if data_id := self._find_shared_data_in_db(data_hash, shared_data): + self._event_data_ids[shared_data] = dbevent.data_id = data_id + # No matching attributes found, save them in the DB + else: + dbevent_data = EventData(shared_data=shared_data, hash=data_hash) + dbevent.event_data_rel = self._pending_event_data[ + shared_data + ] = dbevent_data + self.event_session.add(dbevent_data) + + self.event_session.add(dbevent) + + def _process_state_changed_event_into_session(self, event: Event) -> None: + """Process a state_changed event into the session.""" + assert self.event_session is not None + try: + dbstate = States.from_event(event) + shared_attrs = StateAttributes.shared_attrs_from_event( + event, self._exclude_attributes_by_domain + ) + except (TypeError, ValueError) as ex: + _LOGGER.warning( + "State is not JSON serializable: %s: %s", + event.data.get("new_state"), + ex, + ) + return + + dbstate.attributes = None + # Matching attributes found in the pending commit + if pending_attributes := self._pending_state_attributes.get(shared_attrs): + dbstate.state_attributes = pending_attributes + # Matching attributes id found in the cache + elif attributes_id := self._state_attributes_ids.get(shared_attrs): + dbstate.attributes_id = attributes_id + else: + attr_hash = StateAttributes.hash_shared_attrs(shared_attrs) + # Matching attributes found in the database + if attributes_id := self._find_shared_attr_in_db(attr_hash, shared_attrs): + dbstate.attributes_id = attributes_id + self._state_attributes_ids[shared_attrs] = attributes_id + # No matching attributes found, save them in the DB + else: + dbstate_attributes = StateAttributes( + shared_attrs=shared_attrs, hash=attr_hash + ) + dbstate.state_attributes = dbstate_attributes + self._pending_state_attributes[shared_attrs] = dbstate_attributes + self.event_session.add(dbstate_attributes) + + if old_state := self._old_states.pop(dbstate.entity_id, None): + if old_state.state_id: + dbstate.old_state_id = old_state.state_id + else: + dbstate.old_state = old_state + if event.data.get("new_state"): + self._old_states[dbstate.entity_id] = dbstate + self._pending_expunge.append(dbstate) + else: + dbstate.state = None + self.event_session.add(dbstate) + + def _handle_database_error(self, err: Exception) -> bool: + """Handle a database error that may result in moving away the corrupt db.""" + if isinstance(err.__cause__, sqlite3.DatabaseError): + _LOGGER.exception( + "Unrecoverable sqlite3 database corruption detected: %s", err + ) + self._handle_sqlite_corruption() + return True + return False + + def _event_session_has_pending_writes(self) -> bool: + return bool( + self.event_session and (self.event_session.new or self.event_session.dirty) + ) + + def _commit_event_session_or_retry(self) -> None: + """Commit the event session if there is work to do.""" + if not self._event_session_has_pending_writes(): + return + tries = 1 + while tries <= self.db_max_retries: + try: + self._commit_event_session() + return + except (exc.InternalError, exc.OperationalError) as err: + _LOGGER.error( + "%s: Error executing query: %s. (retrying in %s seconds)", + INVALIDATED_ERR if err.connection_invalidated else CONNECTIVITY_ERR, + err, + self.db_retry_wait, + ) + if tries == self.db_max_retries: + raise + + tries += 1 + time.sleep(self.db_retry_wait) + + def _commit_event_session(self) -> None: + assert self.event_session is not None + self._commits_without_expire += 1 + + if self._pending_expunge: + self.event_session.flush() + for dbstate in self._pending_expunge: + # Expunge the state so its not expired + # until we use it later for dbstate.old_state + if dbstate in self.event_session: + self.event_session.expunge(dbstate) + self._pending_expunge = [] + self.event_session.commit() + + # We just committed the state attributes to the database + # and we now know the attributes_ids. We can save + # many selects for matching attributes by loading them + # into the LRU cache now. + for state_attr in self._pending_state_attributes.values(): + self._state_attributes_ids[ + state_attr.shared_attrs + ] = state_attr.attributes_id + self._pending_state_attributes = {} + for event_data in self._pending_event_data.values(): + self._event_data_ids[event_data.shared_data] = event_data.data_id + self._pending_event_data = {} + + # Expire is an expensive operation (frequently more expensive + # than the flush and commit itself) so we only + # do it after EXPIRE_AFTER_COMMITS commits + if self._commits_without_expire >= EXPIRE_AFTER_COMMITS: + self._commits_without_expire = 0 + self.event_session.expire_all() + + def _handle_sqlite_corruption(self) -> None: + """Handle the sqlite3 database being corrupt.""" + self._close_event_session() + self._close_connection() + move_away_broken_database(dburl_to_path(self.db_url)) + self.run_history.reset() + self._setup_recorder() + self._setup_run() + + def _close_event_session(self) -> None: + """Close the event session.""" + self._old_states = {} + self._state_attributes_ids = {} + self._event_data_ids = {} + self._pending_state_attributes = {} + self._pending_event_data = {} + + if not self.event_session: + return + + try: + self.event_session.rollback() + self.event_session.close() + except SQLAlchemyError as err: + _LOGGER.exception( + "Error while rolling back and closing the event session: %s", err + ) + + def _reopen_event_session(self) -> None: + """Rollback the event session and reopen it after a failure.""" + self._close_event_session() + self._open_event_session() + + def _open_event_session(self) -> None: + """Open the event session.""" + assert self.get_session is not None + self.event_session = self.get_session() + self.event_session.expire_on_commit = False + + def _send_keep_alive(self) -> None: + """Send a keep alive to keep the db connection open.""" + assert self.event_session is not None + _LOGGER.debug("Sending keepalive") + self.event_session.connection().scalar(select([1])) + + @callback + def event_listener(self, event: Event) -> None: + """Listen for new events and put them in the process queue.""" + self.queue.put(EventTask(event)) + + def block_till_done(self) -> None: + """Block till all events processed. + + This is only called in tests. + + This only blocks until the queue is empty + which does not mean the recorder is done. + + Call tests.common's wait_recording_done + after calling this to ensure the data + is in the database. + """ + self._queue_watch.clear() + self.queue.put(WaitTask()) + self._queue_watch.wait() + + async def lock_database(self) -> bool: + """Lock database so it can be backed up safely.""" + if not self.using_sqlite(): + _LOGGER.debug( + "Not a SQLite database or not connected, locking not necessary" + ) + return True + + if self._database_lock_task: + _LOGGER.warning("Database already locked") + return False + + database_locked = asyncio.Event() + task = DatabaseLockTask(database_locked, threading.Event(), False) + self.queue.put(task) + try: + await asyncio.wait_for(database_locked.wait(), timeout=DB_LOCK_TIMEOUT) + except asyncio.TimeoutError as err: + task.database_unlock.set() + raise TimeoutError( + f"Could not lock database within {DB_LOCK_TIMEOUT} seconds." + ) from err + self._database_lock_task = task + return True + + @callback + def unlock_database(self) -> bool: + """Unlock database. + + Returns true if database lock has been held throughout the process. + """ + if not self.using_sqlite(): + _LOGGER.debug( + "Not a SQLite database or not connected, unlocking not necessary" + ) + return True + + if not self._database_lock_task: + _LOGGER.warning("Database currently not locked") + return False + + self._database_lock_task.database_unlock.set() + success = not self._database_lock_task.queue_overflow + + self._database_lock_task = None + + return success + + def _setup_connection(self) -> None: + """Ensure database is ready to fly.""" + kwargs: dict[str, Any] = {} + self._completed_first_database_setup = False + + def setup_recorder_connection( + dbapi_connection: Any, connection_record: Any + ) -> None: + """Dbapi specific connection settings.""" + assert self.engine is not None + setup_connection_for_dialect( + self, + self.engine.dialect.name, + dbapi_connection, + not self._completed_first_database_setup, + ) + self._completed_first_database_setup = True + + if self.db_url == SQLITE_URL_PREFIX or ":memory:" in self.db_url: + kwargs["connect_args"] = {"check_same_thread": False} + kwargs["poolclass"] = MutexPool + MutexPool.pool_lock = threading.RLock() + kwargs["pool_reset_on_return"] = None + elif self.db_url.startswith(SQLITE_URL_PREFIX): + kwargs["poolclass"] = RecorderPool + else: + kwargs["echo"] = False + + if self._using_file_sqlite: + validate_or_move_away_sqlite_database(self.db_url) + + self.engine = create_engine(self.db_url, **kwargs, future=True) + + sqlalchemy_event.listen(self.engine, "connect", setup_recorder_connection) + + Base.metadata.create_all(self.engine) + self.get_session = scoped_session(sessionmaker(bind=self.engine, future=True)) + _LOGGER.debug("Connected to recorder database") + + @property + def _using_file_sqlite(self) -> bool: + """Short version to check if we are using sqlite3 as a file.""" + return self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith( + SQLITE_URL_PREFIX + ) + + def _close_connection(self) -> None: + """Close the connection.""" + assert self.engine is not None + self.engine.dispose() + self.engine = None + self.get_session = None + + def _setup_run(self) -> None: + """Log the start of the current run and schedule any needed jobs.""" + assert self.get_session is not None + with session_scope(session=self.get_session()) as session: + end_incomplete_runs(session, self.run_history.recording_start) + self.run_history.start(session) + self._schedule_compile_missing_statistics(session) + + self._open_event_session() + + def _schedule_compile_missing_statistics(self, session: Session) -> None: + """Add tasks for missing statistics runs.""" + now = dt_util.utcnow() + last_period_minutes = now.minute - now.minute % 5 + last_period = now.replace(minute=last_period_minutes, second=0, microsecond=0) + start = now - timedelta(days=self.keep_days) + start = start.replace(minute=0, second=0, microsecond=0) + + # Find the newest statistics run, if any + if last_run := session.query(func.max(StatisticsRuns.start)).scalar(): + start = max(start, process_timestamp(last_run) + timedelta(minutes=5)) + + # Add tasks + while start < last_period: + end = start + timedelta(minutes=5) + _LOGGER.debug("Compiling missing statistics for %s-%s", start, end) + self.queue.put(StatisticsTask(start)) + start = end + + def _end_session(self) -> None: + """End the recorder session.""" + if self.event_session is None: + return + try: + self.run_history.end(self.event_session) + self._commit_event_session_or_retry() + self.event_session.close() + except Exception as err: # pylint: disable=broad-except + _LOGGER.exception("Error saving the event session during shutdown: %s", err) + + self.run_history.clear() + + def _shutdown(self) -> None: + """Save end time for current run.""" + self.hass.add_job(self._async_stop_listeners) + self._stop_executor() + self._end_session() + self._close_connection() + + @property + def recording(self) -> bool: + """Return if the recorder is recording.""" + return self._event_listener is not None diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py new file mode 100644 index 00000000000..fdffa63bcd3 --- /dev/null +++ b/homeassistant/components/recorder/tasks.py @@ -0,0 +1,250 @@ +"""Support for recording details.""" +from __future__ import annotations + +import abc +import asyncio +from collections.abc import Callable, Iterable +from dataclasses import dataclass +from datetime import datetime +import threading +from typing import TYPE_CHECKING, Any + +from homeassistant.core import Event + +from . import purge, statistics +from .const import DOMAIN, EXCLUDE_ATTRIBUTES +from .models import StatisticData, StatisticMetaData +from .util import periodic_db_cleanups + +if TYPE_CHECKING: + from .core import Recorder + + +class RecorderTask(abc.ABC): + """ABC for recorder tasks.""" + + commit_before = True + + @abc.abstractmethod + def run(self, instance: Recorder) -> None: + """Handle the task.""" + + +@dataclass +class ClearStatisticsTask(RecorderTask): + """Object to store statistics_ids which for which to remove statistics.""" + + statistic_ids: list[str] + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + statistics.clear_statistics(instance, self.statistic_ids) + + +@dataclass +class UpdateStatisticsMetadataTask(RecorderTask): + """Object to store statistics_id and unit for update of statistics metadata.""" + + statistic_id: str + unit_of_measurement: str | None + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + statistics.update_statistics_metadata( + instance, self.statistic_id, self.unit_of_measurement + ) + + +@dataclass +class PurgeTask(RecorderTask): + """Object to store information about purge task.""" + + purge_before: datetime + repack: bool + apply_filter: bool + + def run(self, instance: Recorder) -> None: + """Purge the database.""" + assert instance.get_session is not None + + if purge.purge_old_data( + instance, self.purge_before, self.repack, self.apply_filter + ): + with instance.get_session() as session: + instance.run_history.load_from_db(session) + # We always need to do the db cleanups after a purge + # is finished to ensure the WAL checkpoint and other + # tasks happen after a vacuum. + periodic_db_cleanups(instance) + return + # Schedule a new purge task if this one didn't finish + instance.queue.put(PurgeTask(self.purge_before, self.repack, self.apply_filter)) + + +@dataclass +class PurgeEntitiesTask(RecorderTask): + """Object to store entity information about purge task.""" + + entity_filter: Callable[[str], bool] + + def run(self, instance: Recorder) -> None: + """Purge entities from the database.""" + if purge.purge_entity_data(instance, self.entity_filter): + return + # Schedule a new purge task if this one didn't finish + instance.queue.put(PurgeEntitiesTask(self.entity_filter)) + + +@dataclass +class PerodicCleanupTask(RecorderTask): + """An object to insert into the recorder to trigger cleanup tasks when auto purge is disabled.""" + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + periodic_db_cleanups(instance) + + +@dataclass +class StatisticsTask(RecorderTask): + """An object to insert into the recorder queue to run a statistics task.""" + + start: datetime + + def run(self, instance: Recorder) -> None: + """Run statistics task.""" + if statistics.compile_statistics(instance, self.start): + return + # Schedule a new statistics task if this one didn't finish + instance.queue.put(StatisticsTask(self.start)) + + +@dataclass +class ExternalStatisticsTask(RecorderTask): + """An object to insert into the recorder queue to run an external statistics task.""" + + metadata: StatisticMetaData + statistics: Iterable[StatisticData] + + def run(self, instance: Recorder) -> None: + """Run statistics task.""" + if statistics.add_external_statistics(instance, self.metadata, self.statistics): + return + # Schedule a new statistics task if this one didn't finish + instance.queue.put(ExternalStatisticsTask(self.metadata, self.statistics)) + + +@dataclass +class AdjustStatisticsTask(RecorderTask): + """An object to insert into the recorder queue to run an adjust statistics task.""" + + statistic_id: str + start_time: datetime + sum_adjustment: float + + def run(self, instance: Recorder) -> None: + """Run statistics task.""" + if statistics.adjust_statistics( + instance, + self.statistic_id, + self.start_time, + self.sum_adjustment, + ): + return + # Schedule a new adjust statistics task if this one didn't finish + instance.queue.put( + AdjustStatisticsTask( + self.statistic_id, self.start_time, self.sum_adjustment + ) + ) + + +@dataclass +class WaitTask(RecorderTask): + """An object to insert into the recorder queue to tell it set the _queue_watch event.""" + + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + instance._queue_watch.set() # pylint: disable=[protected-access] + + +@dataclass +class DatabaseLockTask(RecorderTask): + """An object to insert into the recorder queue to prevent writes to the database.""" + + database_locked: asyncio.Event + database_unlock: threading.Event + queue_overflow: bool + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + instance._lock_database(self) # pylint: disable=[protected-access] + + +@dataclass +class StopTask(RecorderTask): + """An object to insert into the recorder queue to stop the event handler.""" + + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + instance.stop_requested = True + + +@dataclass +class EventTask(RecorderTask): + """An event to be processed.""" + + event: Event + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + # pylint: disable-next=[protected-access] + instance._process_one_event(self.event) + + +@dataclass +class KeepAliveTask(RecorderTask): + """A keep alive to be sent.""" + + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + # pylint: disable-next=[protected-access] + instance._send_keep_alive() + + +@dataclass +class CommitTask(RecorderTask): + """Commit the event session.""" + + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + # pylint: disable-next=[protected-access] + instance._commit_event_session_or_retry() + + +@dataclass +class AddRecorderPlatformTask(RecorderTask): + """Add a recorder platform.""" + + domain: str + platform: Any + commit_before = False + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + hass = instance.hass + domain = self.domain + platform = self.platform + + platforms: dict[str, Any] = hass.data[DOMAIN] + platforms[domain] = platform + if hasattr(self.platform, "exclude_attributes"): + hass.data[EXCLUDE_ATTRIBUTES][domain] = platform.exclude_attributes(hass) diff --git a/mypy.ini b/mypy.ini index a4f7ef80d30..b55d5e74998 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1732,6 +1732,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.recorder.core] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.recorder.backup] check_untyped_defs = true disallow_incomplete_defs = true @@ -1842,6 +1853,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.recorder.tasks] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.recorder.util] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 4ae1354464d..630d0b84889 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -459,23 +459,28 @@ def test_get_significant_states_only(hass_history): points.append(start + timedelta(minutes=i)) states = [] - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("123", attributes={"attribute": 10.64}) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[0] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[0], ): # Attributes are different, state not states.append(set_state("123", attributes={"attribute": 21.42})) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[1] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[1], ): # state is different, attributes not states.append(set_state("32", attributes={"attribute": 21.42})) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[2] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[2], ): # everything is different states.append(set_state("412", attributes={"attribute": 54.23})) @@ -536,7 +541,9 @@ def record_states(hass): four = three + timedelta(seconds=1) states = {therm: [], therm2: [], mp: [], mp2: [], mp3: [], script_c: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[mp].append( set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) ) @@ -553,7 +560,9 @@ def record_states(hass): set_state(therm, 20, attributes={"current_temperature": 19.5}) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): # This state will be skipped only different in time set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt3)}) # This state will be skipped because domain is excluded @@ -568,7 +577,9 @@ def record_states(hass): set_state(therm2, 20, attributes={"current_temperature": 19}) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[mp].append( set_state(mp, "Netflix", attributes={"media_title": str(sentinel.mt4)}) ) diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index 8f22447cd8e..5e29fb092b1 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -52,7 +52,7 @@ async def _async_get_states( def _add_db_entries( hass: ha.HomeAssistant, point: datetime, entity_ids: list[str] ) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: for idx, entity_id in enumerate(entity_ids): session.add( Events( @@ -87,7 +87,9 @@ def _setup_get_states(hass): """Set up for testing get_states.""" states = [] now = dt_util.utcnow() - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=now): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=now + ): for i in range(5): state = ha.State( f"test.point_in_time_{i % 5}", @@ -102,7 +104,9 @@ def _setup_get_states(hass): wait_recording_done(hass) future = now + timedelta(seconds=1) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=future): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=future + ): for i in range(5): state = ha.State( f"test.point_in_time_{i % 5}", @@ -122,7 +126,7 @@ def test_get_full_significant_states_with_session_entity_no_matches(hass_recorde hass = hass_recorder() now = dt_util.utcnow() time_before_recorder_ran = now - timedelta(days=1000) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: assert ( history.get_full_significant_states_with_session( hass, session, time_before_recorder_ran, now, entity_ids=["demo.id"] @@ -148,7 +152,7 @@ def test_significant_states_with_session_entity_minimal_response_no_matches( hass = hass_recorder() now = dt_util.utcnow() time_before_recorder_ran = now - timedelta(days=1000) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: assert ( history.get_significant_states_with_session( hass, @@ -197,11 +201,15 @@ def test_state_changes_during_period(hass_recorder, attributes, no_attributes, l point = start + timedelta(seconds=1) end = point + timedelta(seconds=1) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("idle") set_state("YouTube") - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=point): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=point + ): states = [ set_state("idle"), set_state("Netflix"), @@ -209,7 +217,9 @@ def test_state_changes_during_period(hass_recorder, attributes, no_attributes, l set_state("YouTube"), ] - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=end): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=end + ): set_state("Netflix") set_state("Plex") @@ -235,11 +245,15 @@ def test_state_changes_during_period_descending(hass_recorder): point = start + timedelta(seconds=1) end = point + timedelta(seconds=1) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("idle") set_state("YouTube") - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=point): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=point + ): states = [ set_state("idle"), set_state("Netflix"), @@ -247,7 +261,9 @@ def test_state_changes_during_period_descending(hass_recorder): set_state("YouTube"), ] - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=end): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=end + ): set_state("Netflix") set_state("Plex") @@ -277,14 +293,20 @@ def test_get_last_state_changes(hass_recorder): point = start + timedelta(minutes=1) point2 = point + timedelta(minutes=1) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("1") states = [] - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=point): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=point + ): states.append(set_state("2")) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=point2): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=point2 + ): states.append(set_state("3")) hist = history.get_last_state_changes(hass, 2, entity_id) @@ -310,10 +332,14 @@ def test_ensure_state_can_be_copied(hass_recorder): start = dt_util.utcnow() - timedelta(minutes=2) point = start + timedelta(minutes=1) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("1") - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=point): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=point + ): set_state("2") hist = history.get_last_state_changes(hass, 2, entity_id) @@ -486,23 +512,28 @@ def test_get_significant_states_only(hass_recorder): points.append(start + timedelta(minutes=i)) states = [] - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=start): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=start + ): set_state("123", attributes={"attribute": 10.64}) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[0] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[0], ): # Attributes are different, state not states.append(set_state("123", attributes={"attribute": 21.42})) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[1] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[1], ): # state is different, attributes not states.append(set_state("32", attributes={"attribute": 21.42})) with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=points[2] + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=points[2], ): # everything is different states.append(set_state("412", attributes={"attribute": 54.23})) @@ -547,7 +578,9 @@ def record_states(hass): four = three + timedelta(seconds=1) states = {therm: [], therm2: [], mp: [], mp2: [], mp3: [], script_c: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[mp].append( set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) ) @@ -564,7 +597,9 @@ def record_states(hass): set_state(therm, 20, attributes={"current_temperature": 19.5}) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): # This state will be skipped only different in time set_state(mp, "YouTube", attributes={"media_title": str(sentinel.mt3)}) # This state will be skipped because domain is excluded @@ -579,7 +614,9 @@ def record_states(hass): set_state(therm2, 20, attributes={"current_temperature": 19}) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[mp].append( set_state(mp, "Netflix", attributes={"media_title": str(sentinel.mt4)}) ) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 6d4f2e1106a..fc2c03e0039 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -20,7 +20,6 @@ from homeassistant.components.recorder import ( CONF_DB_URL, CONFIG_SCHEMA, DOMAIN, - KEEPALIVE_TIME, SERVICE_DISABLE, SERVICE_ENABLE, SERVICE_PURGE, @@ -29,7 +28,7 @@ from homeassistant.components.recorder import ( Recorder, get_instance, ) -from homeassistant.components.recorder.const import DATA_INSTANCE +from homeassistant.components.recorder.const import DATA_INSTANCE, KEEPALIVE_TIME from homeassistant.components.recorder.models import ( EventData, Events, @@ -196,7 +195,7 @@ async def test_saving_many_states( with patch.object( hass.data[DATA_INSTANCE].event_session, "expire_all" - ) as expire_all, patch.object(recorder, "EXPIRE_AFTER_COMMITS", 2): + ) as expire_all, patch.object(recorder.core, "EXPIRE_AFTER_COMMITS", 2): for _ in range(3): hass.states.async_set(entity_id, "on", attributes) await async_wait_recording_done(hass) @@ -611,7 +610,7 @@ def test_saving_state_and_removing_entity(hass, hass_recorder): def test_recorder_setup_failure(hass): """Test some exceptions.""" with patch.object(Recorder, "_setup_connection") as setup, patch( - "homeassistant.components.recorder.time.sleep" + "homeassistant.components.recorder.core.time.sleep" ): setup.side_effect = ImportError("driver not found") rec = _default_recorder(hass) @@ -625,7 +624,7 @@ def test_recorder_setup_failure(hass): def test_recorder_setup_failure_without_event_listener(hass): """Test recorder setup failure when the event listener is not setup.""" with patch.object(Recorder, "_setup_connection") as setup, patch( - "homeassistant.components.recorder.time.sleep" + "homeassistant.components.recorder.core.time.sleep" ): setup.side_effect = ImportError("driver not found") rec = _default_recorder(hass) @@ -685,7 +684,7 @@ def test_auto_purge(hass_recorder): with patch( "homeassistant.components.recorder.purge.purge_old_data", return_value=True ) as purge_old_data, patch( - "homeassistant.components.recorder.periodic_db_cleanups" + "homeassistant.components.recorder.tasks.periodic_db_cleanups" ) as periodic_db_cleanups: # Advance one day, and the purge task should run test_time = test_time + timedelta(days=1) @@ -741,11 +740,11 @@ def test_auto_purge_auto_repack_on_second_sunday(hass_recorder): run_tasks_at_time(hass, test_time) with patch( - "homeassistant.components.recorder.is_second_sunday", return_value=True + "homeassistant.components.recorder.core.is_second_sunday", return_value=True ), patch( "homeassistant.components.recorder.purge.purge_old_data", return_value=True ) as purge_old_data, patch( - "homeassistant.components.recorder.periodic_db_cleanups" + "homeassistant.components.recorder.tasks.periodic_db_cleanups" ) as periodic_db_cleanups: # Advance one day, and the purge task should run test_time = test_time + timedelta(days=1) @@ -779,11 +778,11 @@ def test_auto_purge_auto_repack_disabled_on_second_sunday(hass_recorder): run_tasks_at_time(hass, test_time) with patch( - "homeassistant.components.recorder.is_second_sunday", return_value=True + "homeassistant.components.recorder.core.is_second_sunday", return_value=True ), patch( "homeassistant.components.recorder.purge.purge_old_data", return_value=True ) as purge_old_data, patch( - "homeassistant.components.recorder.periodic_db_cleanups" + "homeassistant.components.recorder.tasks.periodic_db_cleanups" ) as periodic_db_cleanups: # Advance one day, and the purge task should run test_time = test_time + timedelta(days=1) @@ -817,11 +816,12 @@ def test_auto_purge_no_auto_repack_on_not_second_sunday(hass_recorder): run_tasks_at_time(hass, test_time) with patch( - "homeassistant.components.recorder.is_second_sunday", return_value=False + "homeassistant.components.recorder.core.is_second_sunday", + return_value=False, ), patch( "homeassistant.components.recorder.purge.purge_old_data", return_value=True ) as purge_old_data, patch( - "homeassistant.components.recorder.periodic_db_cleanups" + "homeassistant.components.recorder.tasks.periodic_db_cleanups" ) as periodic_db_cleanups: # Advance one day, and the purge task should run test_time = test_time + timedelta(days=1) @@ -856,7 +856,7 @@ def test_auto_purge_disabled(hass_recorder): with patch( "homeassistant.components.recorder.purge.purge_old_data", return_value=True ) as purge_old_data, patch( - "homeassistant.components.recorder.periodic_db_cleanups" + "homeassistant.components.recorder.tasks.periodic_db_cleanups" ) as periodic_db_cleanups: # Advance one day, and the purge task should run test_time = test_time + timedelta(days=1) @@ -924,7 +924,9 @@ def test_auto_statistics(hass_recorder): def test_statistics_runs_initiated(hass_recorder): """Test statistics_runs is initiated when DB is created.""" now = dt_util.utcnow() - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=now): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=now + ): hass = hass_recorder() wait_recording_done(hass) @@ -944,7 +946,9 @@ def test_compile_missing_statistics(tmpdir): test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=now): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=now + ): hass = get_test_home_assistant() setup_component(hass, DOMAIN, {DOMAIN: {CONF_DB_URL: dburl}}) @@ -963,7 +967,7 @@ def test_compile_missing_statistics(tmpdir): hass.stop() with patch( - "homeassistant.components.recorder.dt_util.utcnow", + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=now + timedelta(hours=1), ): @@ -1356,8 +1360,8 @@ async def test_database_lock_and_overflow( instance: Recorder = hass.data[DATA_INSTANCE] - with patch.object(recorder, "MAX_QUEUE_BACKLOG", 1), patch.object( - recorder, "DB_LOCK_QUEUE_CHECK_TIMEOUT", 0.1 + with patch.object(recorder.core, "MAX_QUEUE_BACKLOG", 1), patch.object( + recorder.core, "DB_LOCK_QUEUE_CHECK_TIMEOUT", 0.1 ): await instance.lock_database() @@ -1382,7 +1386,7 @@ async def test_database_lock_timeout(hass, recorder_mock): instance: Recorder = hass.data[DATA_INSTANCE] - class BlockQueue(recorder.RecorderTask): + class BlockQueue(recorder.tasks.RecorderTask): event: threading.Event = threading.Event() def run(self, instance: Recorder) -> None: @@ -1390,7 +1394,7 @@ async def test_database_lock_timeout(hass, recorder_mock): block_task = BlockQueue() instance.queue.put(block_task) - with patch.object(recorder, "DB_LOCK_TIMEOUT", 0.1): + with patch.object(recorder.core, "DB_LOCK_TIMEOUT", 0.1): try: with pytest.raises(TimeoutError): await instance.lock_database() @@ -1435,7 +1439,7 @@ async def test_database_connection_keep_alive( await instance.async_recorder_ready.wait() async_fire_time_changed( - hass, dt_util.utcnow() + timedelta(seconds=recorder.KEEPALIVE_TIME) + hass, dt_util.utcnow() + timedelta(seconds=recorder.core.KEEPALIVE_TIME) ) await async_wait_recording_done(hass) assert "Sending keepalive" in caplog.text @@ -1452,7 +1456,7 @@ async def test_database_connection_keep_alive_disabled_on_sqlite( await instance.async_recorder_ready.wait() async_fire_time_changed( - hass, dt_util.utcnow() + timedelta(seconds=recorder.KEEPALIVE_TIME) + hass, dt_util.utcnow() + timedelta(seconds=recorder.core.KEEPALIVE_TIME) ) await async_wait_recording_done(hass) assert "Sending keepalive" not in caplog.text @@ -1482,7 +1486,7 @@ def test_deduplication_event_data_inside_commit_interval(hass_recorder, caplog): # Patch STATE_ATTRIBUTES_ID_CACHE_SIZE since otherwise # the CI can fail because the test takes too long to run -@patch("homeassistant.components.recorder.STATE_ATTRIBUTES_ID_CACHE_SIZE", 5) +@patch("homeassistant.components.recorder.core.STATE_ATTRIBUTES_ID_CACHE_SIZE", 5) def test_deduplication_state_attributes_inside_commit_interval(hass_recorder, caplog): """Test deduplication of state attributes inside the commit interval.""" hass = hass_recorder() diff --git a/tests/components/recorder/test_migrate.py b/tests/components/recorder/test_migrate.py index 6b963941263..1b84eb5d171 100644 --- a/tests/components/recorder/test_migrate.py +++ b/tests/components/recorder/test_migrate.py @@ -44,7 +44,8 @@ async def test_schema_update_calls(hass): assert recorder.util.async_migration_in_progress(hass) is False with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( - "homeassistant.components.recorder.create_engine", new=create_engine_test + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, ), patch( "homeassistant.components.recorder.migration._apply_update", wraps=migration._apply_update, @@ -67,10 +68,10 @@ async def test_migration_in_progress(hass): """Test that we can check for migration in progress.""" assert recorder.util.async_migration_in_progress(hass) is False - with patch( - "homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", - True, - ), patch("homeassistant.components.recorder.create_engine", new=create_engine_test): + with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True,), patch( + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, + ): await async_setup_component( hass, "recorder", {"recorder": {"db_url": "sqlite://"}} ) @@ -86,7 +87,8 @@ async def test_database_migration_failed(hass): assert recorder.util.async_migration_in_progress(hass) is False with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( - "homeassistant.components.recorder.create_engine", new=create_engine_test + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, ), patch( "homeassistant.components.recorder.migration._apply_update", side_effect=ValueError, @@ -125,7 +127,7 @@ async def test_database_migration_encounters_corruption(hass): "homeassistant.components.recorder.migration.migrate_schema", side_effect=sqlite3_exception, ), patch( - "homeassistant.components.recorder.move_away_broken_database" + "homeassistant.components.recorder.core.move_away_broken_database" ) as move_away: await async_setup_component( hass, "recorder", {"recorder": {"db_url": "sqlite://"}} @@ -149,7 +151,7 @@ async def test_database_migration_encounters_corruption_not_sqlite(hass): "homeassistant.components.recorder.migration.migrate_schema", side_effect=DatabaseError("statement", {}, []), ), patch( - "homeassistant.components.recorder.move_away_broken_database" + "homeassistant.components.recorder.core.move_away_broken_database" ) as move_away, patch( "homeassistant.components.persistent_notification.create", side_effect=pn.create ) as mock_create, patch( @@ -176,10 +178,10 @@ async def test_events_during_migration_are_queued(hass): assert recorder.util.async_migration_in_progress(hass) is False - with patch( - "homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", - True, - ), patch("homeassistant.components.recorder.create_engine", new=create_engine_test): + with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True,), patch( + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, + ): await async_setup_component( hass, "recorder", @@ -207,8 +209,9 @@ async def test_events_during_migration_queue_exhausted(hass): assert recorder.util.async_migration_in_progress(hass) is False with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( - "homeassistant.components.recorder.create_engine", new=create_engine_test - ), patch.object(recorder, "MAX_QUEUE_BACKLOG", 1): + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, + ), patch.object(recorder.core, "MAX_QUEUE_BACKLOG", 1): await async_setup_component( hass, "recorder", @@ -296,7 +299,8 @@ async def test_schema_migrate(hass, start_version): migration_done.set() with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( - "homeassistant.components.recorder.create_engine", new=_create_engine_test + "homeassistant.components.recorder.core.create_engine", + new=_create_engine_test, ), patch( "homeassistant.components.recorder.Recorder._setup_run", side_effect=_mock_setup_run, diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index d946d1e2a14..8ac2f3a783c 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -9,7 +9,6 @@ from sqlalchemy.exc import DatabaseError, OperationalError from sqlalchemy.orm.session import Session from homeassistant.components import recorder -from homeassistant.components.recorder import PurgeTask from homeassistant.components.recorder.const import MAX_ROWS_TO_PURGE from homeassistant.components.recorder.models import ( Events, @@ -20,6 +19,7 @@ from homeassistant.components.recorder.models import ( StatisticsShortTerm, ) from homeassistant.components.recorder.purge import purge_old_data +from homeassistant.components.recorder.tasks import PurgeTask from homeassistant.components.recorder.util import session_scope from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON from homeassistant.core import HomeAssistant @@ -128,7 +128,7 @@ async def test_purge_old_states_encouters_database_corruption( sqlite3_exception.__cause__ = sqlite3.DatabaseError() with patch( - "homeassistant.components.recorder.move_away_broken_database" + "homeassistant.components.recorder.core.move_away_broken_database" ) as move_away, patch( "homeassistant.components.recorder.purge.purge_old_data", side_effect=sqlite3_exception, @@ -406,7 +406,7 @@ async def test_purge_edge_case( """Test states and events are purged even if they occurred shortly before purge_before.""" async def _add_db_entries(hass: HomeAssistant, timestamp: datetime) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: session.add( Events( event_id=1001, @@ -477,7 +477,7 @@ async def test_purge_cutoff_date( timestamp_keep = cutoff timestamp_purge = cutoff - timedelta(microseconds=1) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: session.add( Events( event_id=1000, @@ -626,7 +626,7 @@ async def test_purge_filtered_states( assert instance.entity_filter("sensor.excluded") is False def _add_db_entries(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) @@ -820,7 +820,7 @@ async def test_purge_filtered_states_to_empty( assert instance.entity_filter("sensor.excluded") is False def _add_db_entries(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) @@ -877,7 +877,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty( assert instance.entity_filter("sensor.old_format") is False def _add_db_entries(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be purged # in the legacy format timestamp = dt_util.utcnow() - timedelta(days=5) @@ -944,7 +944,7 @@ async def test_purge_filtered_events( await async_setup_recorder_instance(hass, config) def _add_db_entries(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) @@ -1038,7 +1038,7 @@ async def test_purge_filtered_events_state_changed( assert instance.entity_filter("sensor.excluded") is True def _add_db_entries(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) @@ -1154,7 +1154,7 @@ async def test_purge_entities( await async_wait_purge_done(hass) def _add_purge_records(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) @@ -1186,7 +1186,7 @@ async def test_purge_entities( ) def _add_keep_records(hass: HomeAssistant) -> None: - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: # Add states and state_changed events that should be kept timestamp = dt_util.utcnow() - timedelta(days=2) for event_id in range(200, 210): @@ -1289,7 +1289,8 @@ async def _add_test_states(hass: HomeAssistant): attributes = {"dontpurgeme": True, **base_attributes} with patch( - "homeassistant.components.recorder.dt_util.utcnow", return_value=timestamp + "homeassistant.components.recorder.core.dt_util.utcnow", + return_value=timestamp, ): await set_state("test.recorder2", state, attributes=attributes) @@ -1304,7 +1305,7 @@ async def _add_test_events(hass: HomeAssistant): await hass.async_block_till_done() await async_wait_recording_done(hass) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago @@ -1335,7 +1336,7 @@ async def _add_test_statistics(hass: HomeAssistant): await hass.async_block_till_done() await async_wait_recording_done(hass) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago @@ -1364,7 +1365,7 @@ async def _add_test_recorder_runs(hass: HomeAssistant): await hass.async_block_till_done() await async_wait_recording_done(hass) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: for rec_id in range(6): if rec_id < 2: timestamp = eleven_days_ago @@ -1391,7 +1392,7 @@ async def _add_test_statistics_runs(hass: HomeAssistant): await hass.async_block_till_done() await async_wait_recording_done(hass) - with recorder.session_scope(hass=hass) as session: + with session_scope(hass=hass) as session: for rec_id in range(6): if rec_id < 2: timestamp = eleven_days_ago diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 58f848edc5a..dc13f2abb6a 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -839,7 +839,9 @@ def record_states(hass): four = three + timedelta(seconds=15 * 5) states = {mp: [], sns1: [], sns2: [], sns3: [], sns4: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[mp].append( set_state(mp, "idle", attributes={"media_title": str(sentinel.mt1)}) ) @@ -851,13 +853,17 @@ def record_states(hass): states[sns3].append(set_state(sns3, "10", attributes=sns3_attr)) states[sns4].append(set_state(sns4, "10", attributes=sns4_attr)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): states[sns1].append(set_state(sns1, "15", attributes=sns1_attr)) states[sns2].append(set_state(sns2, "15", attributes=sns2_attr)) states[sns3].append(set_state(sns3, "15", attributes=sns3_attr)) states[sns4].append(set_state(sns4, "15", attributes=sns4_attr)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[sns1].append(set_state(sns1, "20", attributes=sns1_attr)) states[sns2].append(set_state(sns2, "20", attributes=sns2_attr)) states[sns3].append(set_state(sns3, "20", attributes=sns3_attr)) diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index f6d2fa0a99d..07334fef1c3 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -45,7 +45,7 @@ def test_recorder_bad_commit(hass_recorder): session.execute(text("select * from notthere")) with patch( - "homeassistant.components.recorder.time.sleep" + "homeassistant.components.recorder.core.time.sleep" ) as e_mock, util.session_scope(hass=hass) as session: res = util.commit(session, work) assert res is False @@ -66,7 +66,7 @@ def test_recorder_bad_execute(hass_recorder): mck1.to_native = to_native with pytest.raises(SQLAlchemyError), patch( - "homeassistant.components.recorder.time.sleep" + "homeassistant.components.recorder.core.time.sleep" ) as e_mock: util.execute((mck1,), to_native=True) @@ -148,7 +148,7 @@ async def test_last_run_was_recently_clean( "homeassistant.components.recorder.util.last_run_was_recently_clean", wraps=_last_run_was_recently_clean, ) as last_run_was_recently_clean_mock, patch( - "homeassistant.components.recorder.dt_util.utcnow", + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=thirty_min_future_time, ): hass = await async_test_home_assistant(None) diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 08427e60911..fe197cb72e6 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -323,9 +323,10 @@ async def test_recorder_info_migration_queue_exhausted(hass, hass_ws_client): with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( "homeassistant.components.recorder.Recorder.async_periodic_statistics" ), patch( - "homeassistant.components.recorder.create_engine", new=create_engine_test + "homeassistant.components.recorder.core.create_engine", + new=create_engine_test, ), patch.object( - recorder, "MAX_QUEUE_BACKLOG", 1 + recorder.core, "MAX_QUEUE_BACKLOG", 1 ), patch( "homeassistant.components.recorder.migration.migrate_schema", wraps=stalled_migration, @@ -384,7 +385,7 @@ async def test_backup_start_timeout( # Ensure there are no queued events await async_wait_recording_done(hass) - with patch.object(recorder, "DB_LOCK_TIMEOUT", 0): + with patch.object(recorder.core, "DB_LOCK_TIMEOUT", 0): try: await client.send_json({"id": 1, "type": "backup/start"}) response = await client.receive_json() diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 58a991c5f53..b4abf4d9ec3 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -172,7 +172,9 @@ def test_compile_hourly_statistics_purged_state_changes( mean = min = max = float(hist["sensor.test1"][-1].state) # Purge all states from the database - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=four): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=four + ): hass.services.call("recorder", "purge", {"keep_days": 0}) hass.block_till_done() wait_recording_done(hass) @@ -2747,17 +2749,23 @@ def record_states(hass, zero, entity_id, attributes, seq=None): four = three + timedelta(seconds=10 * 5) states = {entity_id: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[entity_id].append( set_state(entity_id, str(seq[0]), attributes=attributes) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): states[entity_id].append( set_state(entity_id, str(seq[1]), attributes=attributes) ) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[entity_id].append( set_state(entity_id, str(seq[2]), attributes=attributes) ) @@ -3339,35 +3347,53 @@ def record_meter_states(hass, zero, entity_id, _attributes, seq): attributes["last_reset"] = zero.isoformat() states = {entity_id: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=zero): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=zero + ): states[entity_id].append(set_state(entity_id, seq[0], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[entity_id].append(set_state(entity_id, seq[1], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): states[entity_id].append(set_state(entity_id, seq[2], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[entity_id].append(set_state(entity_id, seq[3], attributes=attributes)) attributes = dict(_attributes) if "last_reset" in _attributes: attributes["last_reset"] = four.isoformat() - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=four): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=four + ): states[entity_id].append(set_state(entity_id, seq[4], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=five): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=five + ): states[entity_id].append(set_state(entity_id, seq[5], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=six): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=six + ): states[entity_id].append(set_state(entity_id, seq[6], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=seven): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=seven + ): states[entity_id].append(set_state(entity_id, seq[7], attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=eight): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=eight + ): states[entity_id].append(set_state(entity_id, seq[8], attributes=attributes)) return four, eight, states @@ -3386,7 +3412,9 @@ def record_meter_state(hass, zero, entity_id, attributes, seq): return hass.states.get(entity_id) states = {entity_id: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=zero): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=zero + ): states[entity_id].append(set_state(entity_id, seq[0], attributes=attributes)) return states @@ -3410,13 +3438,19 @@ def record_states_partially_unavailable(hass, zero, entity_id, attributes): four = three + timedelta(seconds=15 * 5) states = {entity_id: []} - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=one): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=one + ): states[entity_id].append(set_state(entity_id, "10", attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=two): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=two + ): states[entity_id].append(set_state(entity_id, "25", attributes=attributes)) - with patch("homeassistant.components.recorder.dt_util.utcnow", return_value=three): + with patch( + "homeassistant.components.recorder.core.dt_util.utcnow", return_value=three + ): states[entity_id].append( set_state(entity_id, STATE_UNAVAILABLE, attributes=attributes) ) From 9b03ef4829bd6591af8f87224573bd57b35b5ac5 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 3 May 2022 01:01:19 -0500 Subject: [PATCH 170/930] Improve Sonos terminology for inclusiveness (#71206) * Improve Sonos group terminology * Deprecate Sonos-specific grouping services * Push deprecation back one version * Revert deprecation notice --- homeassistant/components/sonos/speaker.py | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 4e4661b389b..a57bb4d3206 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -749,18 +749,18 @@ class SonosSpeaker: def _get_soco_group() -> list[str]: """Ask SoCo cache for existing topology.""" coordinator_uid = self.soco.uid - slave_uids = [] + joined_uids = [] with contextlib.suppress(OSError, SoCoException): if self.soco.group and self.soco.group.coordinator: coordinator_uid = self.soco.group.coordinator.uid - slave_uids = [ + joined_uids = [ p.uid for p in self.soco.group.members if p.uid != coordinator_uid and p.is_visible ] - return [coordinator_uid] + slave_uids + return [coordinator_uid] + joined_uids async def _async_extract_group(event: SonosEvent | None) -> list[str]: """Extract group layout from a topology event.""" @@ -814,13 +814,13 @@ class SonosSpeaker: self.sonos_group_entities = sonos_group_entities self.async_write_entity_states() - for slave_uid in group[1:]: - slave = self.hass.data[DATA_SONOS].discovered.get(slave_uid) - if slave: - slave.coordinator = self - slave.sonos_group = sonos_group - slave.sonos_group_entities = sonos_group_entities - slave.async_write_entity_states() + for joined_uid in group[1:]: + joined_speaker = self.hass.data[DATA_SONOS].discovered.get(joined_uid) + if joined_speaker: + joined_speaker.coordinator = self + joined_speaker.sonos_group = sonos_group + joined_speaker.sonos_group_entities = sonos_group_entities + joined_speaker.async_write_entity_states() _LOGGER.debug("Regrouped %s: %s", self.zone_name, self.sonos_group_entities) @@ -838,7 +838,7 @@ class SonosSpeaker: return _async_handle_group_event(event) @soco_error() - def join(self, slaves: list[SonosSpeaker]) -> list[SonosSpeaker]: + def join(self, speakers: list[SonosSpeaker]) -> list[SonosSpeaker]: """Form a group with other players.""" if self.coordinator: self.unjoin() @@ -846,12 +846,12 @@ class SonosSpeaker: else: group = self.sonos_group.copy() - for slave in slaves: - if slave.soco.uid != self.soco.uid: - slave.soco.join(self.soco) - slave.coordinator = self - if slave not in group: - group.append(slave) + for speaker in speakers: + if speaker.soco.uid != self.soco.uid: + speaker.soco.join(self.soco) + speaker.coordinator = self + if speaker not in group: + group.append(speaker) return group @@ -880,11 +880,11 @@ class SonosSpeaker: def _unjoin_all(speakers: list[SonosSpeaker]) -> None: """Sync helper.""" - # Unjoin slaves first to prevent inheritance of queues + # Detach all joined speakers first to prevent inheritance of queues coordinators = [s for s in speakers if s.is_coordinator] - slaves = [s for s in speakers if not s.is_coordinator] + joined_speakers = [s for s in speakers if not s.is_coordinator] - for speaker in slaves + coordinators: + for speaker in joined_speakers + coordinators: speaker.unjoin() async with hass.data[DATA_SONOS].topology_condition: @@ -928,7 +928,7 @@ class SonosSpeaker: assert self.soco_snapshot is not None self.soco_snapshot.restore() except (TypeError, AssertionError, AttributeError, SoCoException) as ex: - # Can happen if restoring a coordinator onto a current slave + # Can happen if restoring a coordinator onto a current group member _LOGGER.warning("Error on restore %s: %s", self.zone_name, ex) self.soco_snapshot = None @@ -1036,7 +1036,7 @@ class SonosSpeaker: if coordinator != current_group[0]: return False - # Test that slaves match + # Test that joined members match if set(group[1:]) != set(current_group[1:]): return False From bbe807c655edcfe2c8b354dd3685221e4a2755a7 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 3 May 2022 01:01:44 -0500 Subject: [PATCH 171/930] Deprecate legacy Sonos grouping services (#71226) --- homeassistant/components/sonos/media_player.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index f7f5e2722ff..95834938953 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -127,6 +127,9 @@ async def async_setup_entry( speakers.append(entity.speaker) if service_call.service == SERVICE_JOIN: + _LOGGER.warning( + "Service 'sonos.join' is deprecated and will be removed in 2022.8, please use 'media_player.join'" + ) master = platform.entities.get(service_call.data[ATTR_MASTER]) if master: await SonosSpeaker.join_multi(hass, master.speaker, speakers) # type: ignore[arg-type] @@ -136,6 +139,9 @@ async def async_setup_entry( service_call.data[ATTR_MASTER], ) elif service_call.service == SERVICE_UNJOIN: + _LOGGER.warning( + "Service 'sonos.unjoin' is deprecated and will be removed in 2022.8, please use 'media_player.unjoin'" + ) await SonosSpeaker.unjoin_multi(hass, speakers) # type: ignore[arg-type] elif service_call.service == SERVICE_SNAPSHOT: await SonosSpeaker.snapshot_multi( From a87578998776265d711e9222f15d8beb8010ffbd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 May 2022 00:34:04 -0700 Subject: [PATCH 172/930] Bump aioslimproto to 1.0.2 (#71231) --- homeassistant/components/slimproto/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/slimproto/manifest.json b/homeassistant/components/slimproto/manifest.json index b8e00eb3f99..557428919a4 100644 --- a/homeassistant/components/slimproto/manifest.json +++ b/homeassistant/components/slimproto/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "iot_class": "local_push", "documentation": "https://www.home-assistant.io/integrations/slimproto", - "requirements": ["aioslimproto==1.0.0"], + "requirements": ["aioslimproto==1.0.2"], "codeowners": ["@marcelveldt"], "after_dependencies": ["media_source"] } diff --git a/requirements_all.txt b/requirements_all.txt index 82b69db90f8..14154c718b1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -244,7 +244,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==1.0.0 +aioslimproto==1.0.2 # homeassistant.components.steamist aiosteamist==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a107e19bdf6..9929fe508d4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -210,7 +210,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==1.0.0 +aioslimproto==1.0.2 # homeassistant.components.steamist aiosteamist==0.3.1 From 1ef060700a02a46ce6de554aaf5e9d4be3ecbd00 Mon Sep 17 00:00:00 2001 From: prokon <67952017+prokon@users.noreply.github.com> Date: Tue, 3 May 2022 09:49:12 +0200 Subject: [PATCH 173/930] Add verisure lock method attribute (#70375) Co-authored-by: Martin Hjelmare --- homeassistant/components/verisure/lock.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/verisure/lock.py b/homeassistant/components/verisure/lock.py index 0e28298b2e8..8f9556643f8 100644 --- a/homeassistant/components/verisure/lock.py +++ b/homeassistant/components/verisure/lock.py @@ -99,6 +99,11 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt """Last change triggered by.""" return self.coordinator.data["locks"][self.serial_number].get("userString") + @property + def changed_method(self) -> str: + """Last change method.""" + return self.coordinator.data["locks"][self.serial_number]["method"] + @property def code_format(self) -> str: """Return the required six digit code.""" @@ -112,6 +117,11 @@ class VerisureDoorlock(CoordinatorEntity[VerisureDataUpdateCoordinator], LockEnt == "LOCKED" ) + @property + def extra_state_attributes(self): + """Return the state attributes.""" + return {"method": self.changed_method} + async def async_unlock(self, **kwargs) -> None: """Send unlock command.""" code = kwargs.get( From 1931600eac9d62b00efa2897acb735bfafb73851 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 10:36:58 +0200 Subject: [PATCH 174/930] Isolate parallel subscripts (#71233) --- homeassistant/helpers/script.py | 18 ++-- tests/helpers/test_script.py | 146 +++++++++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index d988b0edd81..3d43e03ddce 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -1133,13 +1133,14 @@ class Script: domain: str, *, # Used in "Running " log message - running_description: str | None = None, change_listener: Callable[..., Any] | None = None, - script_mode: str = DEFAULT_SCRIPT_MODE, - max_runs: int = DEFAULT_MAX, - max_exceeded: str = DEFAULT_MAX_EXCEEDED, - logger: logging.Logger | None = None, + copy_variables: bool = False, log_exceptions: bool = True, + logger: logging.Logger | None = None, + max_exceeded: str = DEFAULT_MAX_EXCEEDED, + max_runs: int = DEFAULT_MAX, + running_description: str | None = None, + script_mode: str = DEFAULT_SCRIPT_MODE, top_level: bool = True, variables: ScriptVariables | None = None, ) -> None: @@ -1192,6 +1193,7 @@ class Script: self._variables_dynamic = template.is_complex(variables) if self._variables_dynamic: template.attach(hass, variables) + self._copy_variables_on_run = copy_variables @property def change_listener(self) -> Callable[..., Any] | None: @@ -1454,7 +1456,10 @@ class Script: variables["context"] = context else: - variables = cast(dict, run_variables) + if self._copy_variables_on_run: + variables = cast(dict, copy(run_variables)) + else: + variables = cast(dict, run_variables) # Prevent non-allowed recursive calls which will cause deadlocks when we try to # stop (restart) or wait for (queued) our own script run. @@ -1671,6 +1676,7 @@ class Script: max_runs=self.max_runs, logger=self._logger, top_level=False, + copy_variables=True, ) parallel_script.change_listener = partial( self._chain_change_listener, parallel_script diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index b9c968838c9..4791dd84cc4 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -3027,7 +3027,7 @@ async def test_parallel(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) - ], "0/parallel/1/sequence/0": [ { - "variables": {"wait": {"remaining": None}}, + "variables": {}, "result": { "event": "test_event", "event_data": {"hello": "from action 2", "what": "world"}, @@ -3047,6 +3047,150 @@ async def test_parallel(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) - assert_action_trace(expected_trace) +async def test_parallel_loop( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test parallel loops do not affect each other.""" + events_loop1 = async_capture_events(hass, "loop1") + events_loop2 = async_capture_events(hass, "loop2") + hass.states.async_set("switch.trigger", "off") + + sequence = cv.SCRIPT_SCHEMA( + { + "parallel": [ + { + "alias": "Loop1", + "sequence": [ + { + "repeat": { + "for_each": ["loop1_a", "loop1_b", "loop1_c"], + "sequence": [ + { + "event": "loop1", + "event_data": {"hello1": "{{ repeat.item }}"}, + } + ], + }, + }, + ], + }, + { + "alias": "Loop2", + "sequence": [ + { + "repeat": { + "for_each": ["loop2_a", "loop2_b", "loop2_c"], + "sequence": [ + { + "event": "loop2", + "event_data": {"hello2": "{{ repeat.item }}"}, + } + ], + }, + }, + ], + }, + ] + } + ) + + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + hass.async_create_task( + script_obj.async_run(MappingProxyType({"what": "world"}), Context()) + ) + await hass.async_block_till_done() + + assert len(events_loop1) == 3 + assert events_loop1[0].data["hello1"] == "loop1_a" + assert events_loop1[1].data["hello1"] == "loop1_b" + assert events_loop1[2].data["hello1"] == "loop1_c" + assert events_loop2[0].data["hello2"] == "loop2_a" + assert events_loop2[1].data["hello2"] == "loop2_b" + assert events_loop2[2].data["hello2"] == "loop2_c" + + expected_trace = { + "0": [{"result": {}}], + "0/parallel/0/sequence/0": [{"result": {}}], + "0/parallel/1/sequence/0": [ + { + "result": {}, + } + ], + "0/parallel/0/sequence/0/repeat/sequence/0": [ + { + "variables": { + "repeat": { + "first": True, + "index": 1, + "last": False, + "item": "loop1_a", + } + }, + "result": {"event": "loop1", "event_data": {"hello1": "loop1_a"}}, + }, + { + "variables": { + "repeat": { + "first": False, + "index": 2, + "last": False, + "item": "loop1_b", + } + }, + "result": {"event": "loop1", "event_data": {"hello1": "loop1_b"}}, + }, + { + "variables": { + "repeat": { + "first": False, + "index": 3, + "last": True, + "item": "loop1_c", + } + }, + "result": {"event": "loop1", "event_data": {"hello1": "loop1_c"}}, + }, + ], + "0/parallel/1/sequence/0/repeat/sequence/0": [ + { + "variables": { + "repeat": { + "first": True, + "index": 1, + "last": False, + "item": "loop2_a", + } + }, + "result": {"event": "loop2", "event_data": {"hello2": "loop2_a"}}, + }, + { + "variables": { + "repeat": { + "first": False, + "index": 2, + "last": False, + "item": "loop2_b", + } + }, + "result": {"event": "loop2", "event_data": {"hello2": "loop2_b"}}, + }, + { + "variables": { + "repeat": { + "first": False, + "index": 3, + "last": True, + "item": "loop2_c", + } + }, + "result": {"event": "loop2", "event_data": {"hello2": "loop2_c"}}, + }, + ], + } + assert_action_trace(expected_trace) + + async def test_parallel_error( hass: HomeAssistant, caplog: pytest.LogCaptureFixture ) -> None: From 92f1855bcfaad514aaa9614a91a426c2e52402ce Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 11:28:08 +0200 Subject: [PATCH 175/930] Fix script conditions (#71235) --- homeassistant/helpers/script.py | 32 +++++++++++++++++++++++--------- tests/helpers/test_script.py | 5 +---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 3d43e03ddce..0b755ae0032 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -205,6 +205,10 @@ async def trace_action(hass, script_run, stop, variables): except _AbortScript as ex: trace_element.set_error(ex.__cause__ or ex) raise ex + except _ConditionFail as ex: + # Clear errors which may have been set when evaluating the condition + trace_element.set_error(None) + raise ex except _StopScript as ex: raise ex except Exception as ex: @@ -325,11 +329,19 @@ async def async_validate_action_config( return config -class _AbortScript(Exception): +class _HaltScript(Exception): + """Throw if script needs to stop executing.""" + + +class _AbortScript(_HaltScript): """Throw if script needs to abort because of an unexpected error.""" -class _StopScript(Exception): +class _ConditionFail(_HaltScript): + """Throw if script needs to stop because a condition evaluated to False.""" + + +class _StopScript(_HaltScript): """Throw if script needs to stop.""" @@ -393,16 +405,18 @@ class _ScriptRun: await self._async_step(log_exceptions=False) else: script_execution_set("finished") - except _StopScript: - script_execution_set("finished") - # Let the _StopScript bubble up if this is a sub-script - if not self._script.top_level: - raise except _AbortScript: script_execution_set("aborted") # Let the _AbortScript bubble up if this is a sub-script if not self._script.top_level: raise + except _ConditionFail: + script_execution_set("aborted") + except _StopScript: + script_execution_set("finished") + # Let the _StopScript bubble up if this is a sub-script + if not self._script.top_level: + raise except Exception: script_execution_set("error") raise @@ -450,7 +464,7 @@ class _ScriptRun: def _handle_exception( self, exception: Exception, continue_on_error: bool, log_exceptions: bool ) -> None: - if not isinstance(exception, (_AbortScript, _StopScript)) and log_exceptions: + if not isinstance(exception, _HaltScript) and log_exceptions: self._log_exception(exception) if not continue_on_error: @@ -726,7 +740,7 @@ class _ScriptRun: self._log("Test condition %s: %s", self._script.last_action, check) trace_update_result(result=check) if not check: - raise _AbortScript + raise _ConditionFail def _test_conditions(self, conditions, name, condition_path=None): if condition_path is None: diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 4791dd84cc4..c01f086a12a 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -1445,7 +1445,7 @@ async def test_condition_warning(hass, caplog): assert_action_trace( { "0": [{"result": {"event": "test_event", "event_data": {}}}], - "1": [{"error_type": script._AbortScript, "result": {"result": False}}], + "1": [{"result": {"result": False}}], "1/entity_id/0": [{"error_type": ConditionError}], }, expected_script_execution="aborted", @@ -1499,7 +1499,6 @@ async def test_condition_basic(hass, caplog): "0": [{"result": {"event": "test_event", "event_data": {}}}], "1": [ { - "error_type": script._AbortScript, "result": {"entities": ["test.entity"], "result": False}, } ], @@ -1590,7 +1589,6 @@ async def test_shorthand_template_condition(hass, caplog): "0": [{"result": {"event": "test_event", "event_data": {}}}], "1": [ { - "error_type": script._AbortScript, "result": {"entities": ["test.entity"], "result": False}, } ], @@ -1656,7 +1654,6 @@ async def test_condition_validation(hass, caplog): "0": [{"result": {"event": "test_event", "event_data": {}}}], "1": [ { - "error_type": script._AbortScript, "result": {"result": False}, } ], From 71248bcbcef25290afdc727a4182670aa41e1ada Mon Sep 17 00:00:00 2001 From: Sven <85389871+wrt54g@users.noreply.github.com> Date: Tue, 3 May 2022 12:34:57 +0200 Subject: [PATCH 176/930] Update images (#71215) --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index cf8323d2e81..05e13695a58 100644 --- a/README.rst +++ b/README.rst @@ -22,7 +22,7 @@ of a component, check the `Home Assistant help section Date: Tue, 3 May 2022 13:04:59 +0200 Subject: [PATCH 177/930] Indicate disabled steps in script trace (#71237) --- homeassistant/helpers/script.py | 1 + tests/helpers/test_script.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 0b755ae0032..54ae4f456ab 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -439,6 +439,7 @@ class _ScriptRun: self._log( "Skipped disabled step %s", self._action.get(CONF_ALIAS, action) ) + trace_set_result(enabled=False) return try: diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index c01f086a12a..136729e62fc 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -4939,8 +4939,8 @@ async def test_disabled_actions( assert_action_trace( { "0": [{"result": {"event": "test_event", "event_data": {}}}], - "1": [{}], - "2": [{}], + "1": [{"result": {"enabled": False}}], + "2": [{"result": {"enabled": False}}], "3": [{"result": {"event": "test_event", "event_data": {}}}], }, ) From eb10654e011621b4a7c3c5355f44587cf13270ca Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 13:06:13 +0200 Subject: [PATCH 178/930] Add test for failing conditions in sub scripts (#71238) --- tests/helpers/test_script.py | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index 136729e62fc..519498f2e08 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -1507,6 +1507,55 @@ async def test_condition_basic(hass, caplog): ) +async def test_condition_subscript(hass, caplog): + """Test failing conditions in a subscript don't stop the parent script.""" + event = "test_event" + events = async_capture_events(hass, event) + sequence = cv.SCRIPT_SCHEMA( + [ + {"event": event}, + { + "repeat": { + "until": "{{ 1 == 1 }}", + "sequence": [ + {"condition": "{{ 1 == 2 }}"}, + ], + } + }, + {"event": event}, + ] + ) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + hass.states.async_set("test.entity", "hello") + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + caplog.clear() + assert len(events) == 2 + + assert_action_trace( + { + "0": [{"result": {"event": "test_event", "event_data": {}}}], + "1": [{"result": {}}], + "1/repeat/sequence/0": [ + { + "variables": {"repeat": {"first": True, "index": 1}}, + "result": {"entities": [], "result": False}, + } + ], + "1/repeat": [ + { + "variables": {"repeat": {"first": True, "index": 1}}, + "result": {"result": True}, + } + ], + "1/repeat/until/0": [{"result": {"entities": [], "result": True}}], + "2": [{"result": {"event": "test_event", "event_data": {}}}], + } + ) + + async def test_and_default_condition(hass, caplog): """Test that a list of conditions evaluates as AND.""" alias = "condition step" From b562416eb218c5d96b8ba5f462f70bc9308aa905 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 09:33:50 -0500 Subject: [PATCH 179/930] Remove humidify_supported and dehumidify_supported attributes from nexia (#71248) These non-standard attributes can already be infered from the dehumidify_setpoint or humidify_setpoint and took up space in the database every time any of the values changes --- homeassistant/components/nexia/climate.py | 9 --------- homeassistant/components/nexia/const.py | 2 -- tests/components/nexia/test_climate.py | 4 ---- 3 files changed, 15 deletions(-) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index fc85f72e503..7eeb04c5676 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -38,9 +38,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( ATTR_AIRCLEANER_MODE, ATTR_DEHUMIDIFY_SETPOINT, - ATTR_DEHUMIDIFY_SUPPORTED, ATTR_HUMIDIFY_SETPOINT, - ATTR_HUMIDIFY_SUPPORTED, ATTR_RUN_MODE, ATTR_ZONE_STATUS, DOMAIN, @@ -356,13 +354,6 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): if not self._has_relative_humidity: return data - data.update( - { - ATTR_DEHUMIDIFY_SUPPORTED: self._has_dehumidify_support, - ATTR_HUMIDIFY_SUPPORTED: self._has_humidify_support, - } - ) - if self._has_dehumidify_support: dehumdify_setpoint = percent_conv( self._thermostat.get_dehumidify_setpoint() diff --git a/homeassistant/components/nexia/const.py b/homeassistant/components/nexia/const.py index 4fa3cb022f8..5d84e8a3075 100644 --- a/homeassistant/components/nexia/const.py +++ b/homeassistant/components/nexia/const.py @@ -28,8 +28,6 @@ ATTR_AIRCLEANER_MODE = "aircleaner_mode" ATTR_RUN_MODE = "run_mode" ATTR_ZONE_STATUS = "zone_status" -ATTR_HUMIDIFY_SUPPORTED = "humidify_supported" -ATTR_DEHUMIDIFY_SUPPORTED = "dehumidify_supported" ATTR_HUMIDIFY_SETPOINT = "humidify_setpoint" ATTR_DEHUMIDIFY_SETPOINT = "dehumidify_setpoint" diff --git a/tests/components/nexia/test_climate.py b/tests/components/nexia/test_climate.py index edd5d56e79a..312d78ac256 100644 --- a/tests/components/nexia/test_climate.py +++ b/tests/components/nexia/test_climate.py @@ -16,11 +16,9 @@ async def test_climate_zones(hass): "current_humidity": 52.0, "current_temperature": 22.8, "dehumidify_setpoint": 45.0, - "dehumidify_supported": True, "fan_mode": "Auto", "fan_modes": ["Auto", "On", "Circulate"], "friendly_name": "Nick Office", - "humidify_supported": False, "humidity": 45.0, "hvac_action": "cooling", "hvac_modes": ["off", "auto", "heat_cool", "heat", "cool"], @@ -51,11 +49,9 @@ async def test_climate_zones(hass): "current_humidity": 36.0, "current_temperature": 25.0, "dehumidify_setpoint": 50.0, - "dehumidify_supported": True, "fan_mode": "Auto", "fan_modes": ["Auto", "On", "Circulate"], "friendly_name": "Kitchen", - "humidify_supported": False, "humidity": 50.0, "hvac_action": "idle", "hvac_modes": ["off", "auto", "heat_cool", "heat", "cool"], From 08b683dafd0bc354880881581666efa981655658 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 May 2022 07:42:06 -0700 Subject: [PATCH 180/930] Fix homepod streaming and browsing apps (#71230) --- .../components/apple_tv/media_player.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index 02919043bb2..6c1b35f70f8 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -79,7 +79,8 @@ SUPPORT_APPLE_TV = ( SUPPORT_FEATURE_MAPPING = { FeatureName.PlayUrl: MediaPlayerEntityFeature.BROWSE_MEDIA | MediaPlayerEntityFeature.PLAY_MEDIA, - FeatureName.StreamFile: MediaPlayerEntityFeature.PLAY_MEDIA, + FeatureName.StreamFile: MediaPlayerEntityFeature.BROWSE_MEDIA + | MediaPlayerEntityFeature.PLAY_MEDIA, FeatureName.Pause: MediaPlayerEntityFeature.PAUSE, FeatureName.Play: MediaPlayerEntityFeature.PLAY, FeatureName.SetPosition: MediaPlayerEntityFeature.SEEK, @@ -282,23 +283,20 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): if media_type == MEDIA_TYPE_APP: await self.atv.apps.launch_app(media_id) - is_media_source_id = media_source.is_media_source_id(media_id) + if media_source.is_media_source_id(media_id): + play_item = await media_source.async_resolve_media(self.hass, media_id) + media_id = play_item.url + media_type = MEDIA_TYPE_MUSIC - if ( - not is_media_source_id - and self._is_feature_available(FeatureName.StreamFile) - and (await is_streamable(media_id) or media_type == MEDIA_TYPE_MUSIC) + media_id = async_process_play_media_url(self.hass, media_id) + + if self._is_feature_available(FeatureName.StreamFile) and ( + media_type == MEDIA_TYPE_MUSIC or await is_streamable(media_id) ): _LOGGER.debug("Streaming %s via RAOP", media_id) await self.atv.stream.stream_file(media_id) if self._is_feature_available(FeatureName.PlayUrl): - if is_media_source_id: - play_item = await media_source.async_resolve_media(self.hass, media_id) - media_id = play_item.url - - media_id = async_process_play_media_url(self.hass, media_id) - _LOGGER.debug("Playing %s via AirPlay", media_id) await self.atv.stream.play_url(media_id) else: @@ -397,9 +395,12 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): media_content_id=None, ) -> BrowseMedia: """Implement the websocket media browsing helper.""" - # If we can't stream URLs, we can't browse media. - # In that case the `BROWSE_MEDIA` feature was added because of AppList/LaunchApp - if not self._is_feature_available(FeatureName.PlayUrl): + if media_content_id == "apps" or ( + # If we can't stream files or URLs, we can't browse media. + # In that case the `BROWSE_MEDIA` feature was added because of AppList/LaunchApp + not self._is_feature_available(FeatureName.PlayUrl) + and not self._is_feature_available(FeatureName.StreamFile) + ): return build_app_list(self._app_list) if self._app_list: From eba125b0939eb2c9acfde97d9ac1aca206d3aac8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 16:43:44 +0200 Subject: [PATCH 181/930] Ensure 'this' variable is always defined for template entities (#70911) --- .../components/template/template_entity.py | 30 ++++- homeassistant/helpers/template.py | 3 +- tests/components/template/test_sensor.py | 114 ++++++++++++++++++ 3 files changed, 140 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/template/template_entity.py b/homeassistant/components/template/template_entity.py index d7d6ab46c62..a6d1cba78e1 100644 --- a/homeassistant/components/template/template_entity.py +++ b/homeassistant/components/template/template_entity.py @@ -17,8 +17,9 @@ from homeassistant.const import ( CONF_ICON_TEMPLATE, CONF_NAME, EVENT_HOMEASSISTANT_START, + STATE_UNKNOWN, ) -from homeassistant.core import CoreState, Event, callback +from homeassistant.core import CoreState, Event, State, callback from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity @@ -251,13 +252,28 @@ class TemplateEntity(Entity): self._entity_picture_template = config.get(CONF_PICTURE) self._friendly_name_template = config.get(CONF_NAME) + class DummyState(State): + """None-state for template entities not yet added to the state machine.""" + + def __init__(self) -> None: + """Initialize a new state.""" + super().__init__("unknown.unknown", STATE_UNKNOWN) + self.entity_id = None # type: ignore[assignment] + + @property + def name(self) -> str: + """Name of this state.""" + return "" + + variables = {"this": DummyState()} + # Try to render the name as it can influence the entity ID self._attr_name = fallback_name if self._friendly_name_template: self._friendly_name_template.hass = hass with contextlib.suppress(TemplateError): self._attr_name = self._friendly_name_template.async_render( - parse_result=False + variables=variables, parse_result=False ) # Templates will not render while the entity is unavailable, try to render the @@ -266,13 +282,15 @@ class TemplateEntity(Entity): self._entity_picture_template.hass = hass with contextlib.suppress(TemplateError): self._attr_entity_picture = self._entity_picture_template.async_render( - parse_result=False + variables=variables, parse_result=False ) if self._icon_template: self._icon_template.hass = hass with contextlib.suppress(TemplateError): - self._attr_icon = self._icon_template.async_render(parse_result=False) + self._attr_icon = self._icon_template.async_render( + variables=variables, parse_result=False + ) @callback def _update_available(self, result): @@ -373,10 +391,10 @@ class TemplateEntity(Entity): template_var_tups: list[TrackTemplate] = [] has_availability_template = False - values = {"this": TemplateStateFromEntityId(self.hass, self.entity_id)} + variables = {"this": TemplateStateFromEntityId(self.hass, self.entity_id)} for template, attributes in self._template_attrs.items(): - template_var_tup = TrackTemplate(template, values) + template_var_tup = TrackTemplate(template, variables) is_availability_template = False for attribute in attributes: # pylint: disable-next=protected-access diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index dbc82ce6902..3f084674a1b 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -850,7 +850,8 @@ class TemplateStateFromEntityId(TemplateStateBase): @property def _state(self) -> State: # type: ignore[override] # mypy issue 4125 state = self._hass.states.get(self._entity_id) - assert state + if not state: + state = State(self._entity_id, STATE_UNKNOWN) return state def __repr__(self) -> str: diff --git a/tests/components/template/test_sensor.py b/tests/components/template/test_sensor.py index 297008e77bb..ddf13c2015b 100644 --- a/tests/components/template/test_sensor.py +++ b/tests/components/template/test_sensor.py @@ -653,6 +653,120 @@ async def test_this_variable(hass, start_ha): assert hass.states.get(TEST_NAME).state == "It Works: " + TEST_NAME +@pytest.mark.parametrize("count,domain", [(1, "template")]) +@pytest.mark.parametrize( + "config", + [ + { + "template": { + "sensor": { + "state": "{{ this.attributes.get('test', 'no-test!') }}: {{ this.entity_id }}", + "icon": "mdi:{% if this.entity_id in states and 'friendly_name' in this.attributes %} {{this.attributes['friendly_name']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "name": "{% if this.entity_id in states and 'friendly_name' in this.attributes %} {{this.attributes['friendly_name']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "picture": "{% if this.entity_id in states and 'entity_picture' in this.attributes %} {{this.attributes['entity_picture']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "attributes": {"test": "{{ this.entity_id }}"}, + }, + }, + }, + ], +) +async def test_this_variable_early_hass_not_running(hass, config, count, domain): + """Test referencing 'this' variable before the entity is in the state machine. + + Hass is not yet started when the entity is added. + Icon, name and picture templates are rendered once in the constructor. + """ + entity_id = "sensor.none_false" + + hass.state = CoreState.not_running + + # Setup template + with assert_setup_component(count, domain): + assert await async_setup_component( + hass, + domain, + config, + ) + await hass.async_block_till_done() + await hass.async_block_till_done() + + # Sensor state not rendered, icon, name and picture + # templates rendered in constructor with entity_id set to None + state = hass.states.get(entity_id) + assert state.state == "unknown" + assert state.attributes == { + "entity_picture": "None:False", + "friendly_name": "None:False", + "icon": "mdi:None:False", + } + + # Signal hass started + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + # Re-render icon, name, pciture + other templates now rendered + state = hass.states.get(entity_id) + assert state.state == "sensor.none_false: sensor.none_false" + assert state.attributes == { + "entity_picture": "sensor.none_false:False", + "friendly_name": "sensor.none_false:False", + "icon": "mdi:sensor.none_false:False", + "test": "sensor.none_false", + } + + +@pytest.mark.parametrize("count,domain", [(1, "template")]) +@pytest.mark.parametrize( + "config", + [ + { + "template": { + "sensor": { + "state": "{{ this.attributes.get('test', 'no-test!') }}: {{ this.entity_id }}", + "icon": "mdi:{% if this.entity_id in states and 'friendly_name' in this.attributes %} {{this.attributes['friendly_name']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "name": "{% if this.entity_id in states and 'friendly_name' in this.attributes %} {{this.attributes['friendly_name']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "picture": "{% if this.entity_id in states and 'entity_picture' in this.attributes %} {{this.attributes['entity_picture']}} {% else %}{{this.entity_id}}:{{this.entity_id in states}}{% endif %}", + "attributes": {"test": "{{ this.entity_id }}"}, + }, + }, + }, + ], +) +async def test_this_variable_early_hass_running(hass, config, count, domain): + """Test referencing 'this' variable before the entity is in the state machine. + + Hass is already started when the entity is added. + Icon, name and picture templates are rendered in the constructor, and again + before the entity is added to hass. + """ + + # Start hass + assert hass.state == CoreState.running + await hass.async_start() + await hass.async_block_till_done() + + # Setup template + with assert_setup_component(count, domain): + assert await async_setup_component( + hass, + domain, + config, + ) + await hass.async_block_till_done() + await hass.async_block_till_done() + + entity_id = "sensor.none_false" + # All templated rendered + state = hass.states.get(entity_id) + assert state.state == "sensor.none_false: sensor.none_false" + assert state.attributes == { + "entity_picture": "sensor.none_false:False", + "friendly_name": "sensor.none_false:False", + "icon": "mdi:sensor.none_false:False", + "test": "sensor.none_false", + } + + @pytest.mark.parametrize("count,domain", [(1, sensor.DOMAIN)]) @pytest.mark.parametrize( "config", From 60bfcc6be4d7d3944995182a038075c126221af6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 11:47:13 -0500 Subject: [PATCH 182/930] Allow hidden entities to be selected in homekit include mode (#71250) --- .../components/homekit/config_flow.py | 10 ++++--- tests/components/homekit/test_config_flow.py | 28 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/homekit/config_flow.py b/homeassistant/components/homekit/config_flow.py index b7a00bc3ade..5f142fdb0fe 100644 --- a/homeassistant/components/homekit/config_flow.py +++ b/homeassistant/components/homekit/config_flow.py @@ -467,7 +467,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): entity_filter = self.hk_options.get(CONF_FILTER, {}) entities = entity_filter.get(CONF_INCLUDE_ENTITIES, []) all_supported_entities = _async_get_matching_entities( - self.hass, domains, include_entity_category=True + self.hass, domains, include_entity_category=True, include_hidden=True ) # In accessory mode we can only have one default_value = next( @@ -508,7 +508,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): entities = entity_filter.get(CONF_INCLUDE_ENTITIES, []) all_supported_entities = _async_get_matching_entities( - self.hass, domains, include_entity_category=True + self.hass, domains, include_entity_category=True, include_hidden=True ) if not entities: entities = entity_filter.get(CONF_EXCLUDE_ENTITIES, []) @@ -646,12 +646,13 @@ def _exclude_by_entity_registry( ent_reg: entity_registry.EntityRegistry, entity_id: str, include_entity_category: bool, + include_hidden: bool, ) -> bool: """Filter out hidden entities and ones with entity category (unless specified).""" return bool( (entry := ent_reg.async_get(entity_id)) and ( - entry.hidden_by is not None + (not include_hidden and entry.hidden_by is not None) or (not include_entity_category and entry.entity_category is not None) ) ) @@ -661,6 +662,7 @@ def _async_get_matching_entities( hass: HomeAssistant, domains: list[str] | None = None, include_entity_category: bool = False, + include_hidden: bool = False, ) -> dict[str, str]: """Fetch all entities or entities in the given domains.""" ent_reg = entity_registry.async_get(hass) @@ -671,7 +673,7 @@ def _async_get_matching_entities( key=lambda item: item.entity_id, ) if not _exclude_by_entity_registry( - ent_reg, state.entity_id, include_entity_category + ent_reg, state.entity_id, include_entity_category, include_hidden ) } diff --git a/tests/components/homekit/test_config_flow.py b/tests/components/homekit/test_config_flow.py index 42ce6779528..ce0bed0ff52 100644 --- a/tests/components/homekit/test_config_flow.py +++ b/tests/components/homekit/test_config_flow.py @@ -1504,7 +1504,7 @@ async def test_options_flow_exclude_mode_skips_hidden_entities( @patch(f"{PATH_HOMEKIT}.async_port_is_available", return_value=True) -async def test_options_flow_include_mode_skips_hidden_entities( +async def test_options_flow_include_mode_allows_hidden_entities( port_mock, hass, mock_get_source_ip, hk_driver, mock_async_zeroconf, entity_reg ): """Ensure include mode does not offer hidden entities.""" @@ -1558,24 +1558,28 @@ async def test_options_flow_include_mode_skips_hidden_entities( assert _get_schema_default(result2["data_schema"].schema, "entities") == [] # sonos_hidden_switch.entity_id is a hidden entity - # so it should not be selectable since it will always be excluded - with pytest.raises(voluptuous.error.MultipleInvalid): - await hass.config_entries.options.async_configure( - result2["flow_id"], - user_input={"entities": [sonos_hidden_switch.entity_id]}, - ) - - result4 = await hass.config_entries.options.async_configure( + # we allow it to be selected in include mode only + result3 = await hass.config_entries.options.async_configure( result2["flow_id"], - user_input={"entities": ["media_player.tv", "switch.other"]}, + user_input={ + "entities": [ + sonos_hidden_switch.entity_id, + "media_player.tv", + "switch.other", + ] + }, ) - assert result4["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result3["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert config_entry.options == { "mode": "bridge", "filter": { "exclude_domains": [], "exclude_entities": [], "include_domains": [], - "include_entities": ["media_player.tv", "switch.other"], + "include_entities": [ + sonos_hidden_switch.entity_id, + "media_player.tv", + "switch.other", + ], }, } From 8d40d9df8511e90e73a3a105a4fd1b32b63197c6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 11:49:52 -0500 Subject: [PATCH 183/930] Create ISY auxiliary sensors as sensor entities instead of attributes (#71254) --- homeassistant/components/isy994/__init__.py | 3 +- homeassistant/components/isy994/const.py | 2 + homeassistant/components/isy994/entity.py | 19 +++-- homeassistant/components/isy994/helpers.py | 5 ++ homeassistant/components/isy994/sensor.py | 93 +++++++++++++++++++-- 5 files changed, 104 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index faa2f7cfb5d..e94b8215746 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -41,6 +41,7 @@ from .const import ( MANUFACTURER, PLATFORMS, PROGRAM_PLATFORMS, + SENSOR_AUX, ) from .helpers import _categorize_nodes, _categorize_programs, _categorize_variables from .services import async_setup_services, async_unload_services @@ -120,7 +121,7 @@ async def async_setup_entry( hass.data[DOMAIN][entry.entry_id] = {} hass_isy_data = hass.data[DOMAIN][entry.entry_id] - hass_isy_data[ISY994_NODES] = {} + hass_isy_data[ISY994_NODES] = {SENSOR_AUX: []} for platform in PLATFORMS: hass_isy_data[ISY994_NODES][platform] = [] diff --git a/homeassistant/components/isy994/const.py b/homeassistant/components/isy994/const.py index bc463655f27..ddabe1b9680 100644 --- a/homeassistant/components/isy994/const.py +++ b/homeassistant/components/isy994/const.py @@ -191,6 +191,8 @@ UOM_INDEX = "25" UOM_ON_OFF = "2" UOM_PERCENTAGE = "51" +SENSOR_AUX = "sensor_aux" + # Do not use the Home Assistant consts for the states here - we're matching exact API # responses, not using them for Home Assistant states # Insteon Types: https://www.universal-devices.com/developers/wsdk/5.0.4/1_fam.xml diff --git a/homeassistant/components/isy994/entity.py b/homeassistant/components/isy994/entity.py index e5db8de5872..54ee9a2ded5 100644 --- a/homeassistant/components/isy994/entity.py +++ b/homeassistant/components/isy994/entity.py @@ -8,6 +8,7 @@ from pyisy.constants import ( EMPTY_TIME, EVENT_PROPS_IGNORED, PROTO_GROUP, + PROTO_INSTEON, PROTO_ZWAVE, ) from pyisy.helpers import EventListener, NodeProperty @@ -35,6 +36,7 @@ class ISYEntity(Entity): """Representation of an ISY994 device.""" _name: str | None = None + _attr_should_poll = False def __init__(self, node: Node) -> None: """Initialize the insteon device.""" @@ -86,7 +88,7 @@ class ISYEntity(Entity): node = self._node url = _async_isy_to_configuration_url(isy) - basename = self.name + basename = self._name or str(self._node.name) if hasattr(self._node, "parent_node") and self._node.parent_node is not None: # This is not the parent node, get the parent node. @@ -151,11 +153,6 @@ class ISYEntity(Entity): """Get the name of the device.""" return self._name or str(self._node.name) - @property - def should_poll(self) -> bool: - """No polling required since we're using the subscription.""" - return False - class ISYNodeEntity(ISYEntity): """Representation of a ISY Nodebase (Node/Group) entity.""" @@ -169,9 +166,13 @@ class ISYNodeEntity(ISYEntity): the combined result are returned as the device state attributes. """ attr = {} - if hasattr(self._node, "aux_properties"): - # Cast as list due to RuntimeError if a new property is added while running. - for name, value in list(self._node.aux_properties.items()): + node = self._node + # Insteon aux_properties are now their own sensors + if ( + hasattr(self._node, "aux_properties") + and getattr(node, "protocol", None) != PROTO_INSTEON + ): + for name, value in self._node.aux_properties.items(): attr_name = COMMAND_FRIENDLY_NAME.get(name, name) attr[attr_name] = str(value.formatted).lower() diff --git a/homeassistant/components/isy994/helpers.py b/homeassistant/components/isy994/helpers.py index 6d0a1d303bb..7efbb3bade7 100644 --- a/homeassistant/components/isy994/helpers.py +++ b/homeassistant/components/isy994/helpers.py @@ -44,6 +44,7 @@ from .const import ( NODE_FILTERS, PLATFORMS, PROGRAM_PLATFORMS, + SENSOR_AUX, SUBNODE_CLIMATE_COOL, SUBNODE_CLIMATE_HEAT, SUBNODE_EZIO2X4_SENSORS, @@ -295,6 +296,10 @@ def _categorize_nodes( hass_isy_data[ISY994_NODES][ISY_GROUP_PLATFORM].append(node) continue + if getattr(node, "protocol", None) == PROTO_INSTEON: + for control in node.aux_properties: + hass_isy_data[ISY994_NODES][SENSOR_AUX].append((node, control)) + if sensor_identifier in path or sensor_identifier in node.name: # User has specified to treat this as a sensor. First we need to # determine if it should be a binary_sensor. diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index d9751fd707b..2dee990c249 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -3,9 +3,15 @@ from __future__ import annotations from typing import Any, cast -from pyisy.constants import ISY_VALUE_UNKNOWN +from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN +from pyisy.helpers import NodeProperty +from pyisy.nodes import Node -from homeassistant.components.sensor import DOMAIN as SENSOR, SensorEntity +from homeassistant.components.sensor import ( + DOMAIN as SENSOR, + SensorDeviceClass, + SensorEntity, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant @@ -16,6 +22,7 @@ from .const import ( DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_VARIABLES, + SENSOR_AUX, UOM_DOUBLE_TEMP, UOM_FRIENDLY_NAME, UOM_INDEX, @@ -25,6 +32,20 @@ from .const import ( from .entity import ISYEntity, ISYNodeEntity from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids +# Disable general purpose and redundant sensors by default +AUX_DISABLED_BY_DEFAULT = ["ERR", "GV", "CLIEMD", "CLIHCS", "DO", "OL", "RR", "ST"] + +ISY_CONTROL_TO_DEVICE_CLASS = { + "BARPRES": SensorDeviceClass.PRESSURE, + "BATLVL": SensorDeviceClass.BATTERY, + "CLIHUM": SensorDeviceClass.HUMIDITY, + "CLITEMP": SensorDeviceClass.TEMPERATURE, + "CO2LVL": SensorDeviceClass.CO2, + "CV": SensorDeviceClass.VOLTAGE, + "LUMIN": SensorDeviceClass.ILLUMINANCE, + "PF": SensorDeviceClass.POWER_FACTOR, +} + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -37,6 +58,13 @@ async def async_setup_entry( _LOGGER.debug("Loading %s", node.name) entities.append(ISYSensorEntity(node)) + for node, control in hass_isy_data[ISY994_NODES][SENSOR_AUX]: + _LOGGER.debug("Loading %s %s", node.name, node.aux_properties[control]) + enabled_default = not any( + control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT + ) + entities.append(ISYAuxSensorEntity(node, control, enabled_default)) + for vname, vobj in hass_isy_data[ISY994_VARIABLES]: entities.append(ISYSensorVariableEntity(vname, vobj)) @@ -47,10 +75,20 @@ async def async_setup_entry( class ISYSensorEntity(ISYNodeEntity, SensorEntity): """Representation of an ISY994 sensor device.""" + @property + def target(self) -> Node | NodeProperty: + """Return target for the sensor.""" + return self._node + + @property + def target_value(self) -> Any: + """Return the target value.""" + return self._node.status + @property def raw_unit_of_measurement(self) -> dict | str | None: """Get the raw unit of measurement for the ISY994 sensor device.""" - uom = self._node.uom + uom = self.target.uom # Backwards compatibility for ISYv4 Firmware: if isinstance(uom, list): @@ -69,7 +107,7 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): @property def native_value(self) -> float | int | str | None: """Get the state of the ISY994 sensor device.""" - if (value := self._node.status) == ISY_VALUE_UNKNOWN: + if (value := self.target_value) == ISY_VALUE_UNKNOWN: return None # Get the translated ISY Unit of Measurement @@ -80,14 +118,14 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): return uom.get(value, value) if uom in (UOM_INDEX, UOM_ON_OFF): - return cast(str, self._node.formatted) + return cast(str, self.target.formatted) # Check if this is an index type and get formatted value - if uom == UOM_INDEX and hasattr(self._node, "formatted"): - return cast(str, self._node.formatted) + if uom == UOM_INDEX and hasattr(self.target, "formatted"): + return cast(str, self.target.formatted) # Handle ISY precision and rounding - value = convert_isy_value_to_hass(value, uom, self._node.prec) + value = convert_isy_value_to_hass(value, uom, self.target.prec) # Convert temperatures to Home Assistant's unit if uom in (TEMP_CELSIUS, TEMP_FAHRENHEIT): @@ -111,6 +149,45 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): return raw_units +class ISYAuxSensorEntity(ISYSensorEntity): + """Representation of an ISY994 aux sensor device.""" + + def __init__(self, node: Node, control: str, enabled_default: bool) -> None: + """Initialize the ISY994 aux sensor.""" + super().__init__(node) + self._control = control + self._attr_entity_registry_enabled_default = enabled_default + + @property + def device_class(self) -> SensorDeviceClass | str | None: + """Return the device class for the sensor.""" + return ISY_CONTROL_TO_DEVICE_CLASS.get(self._control, super().device_class) + + @property + def target(self) -> Node | NodeProperty: + """Return target for the sensor.""" + return cast(NodeProperty, self._node.aux_properties[self._control]) + + @property + def target_value(self) -> Any: + """Return the target value.""" + return self.target.value + + @property + def unique_id(self) -> str | None: + """Get the unique identifier of the device and aux sensor.""" + if not hasattr(self._node, "address"): + return None + return f"{self._node.isy.configuration['uuid']}_{self._node.address}_{self._control}" + + @property + def name(self) -> str: + """Get the name of the device and aux sensor.""" + base_name = self._name or str(self._node.name) + name = COMMAND_FRIENDLY_NAME.get(self._control, self._control) + return f"{base_name} {name.replace('_', ' ').title()}" + + class ISYSensorVariableEntity(ISYEntity, SensorEntity): """Representation of an ISY994 variable as a sensor device.""" From 0580803b7d2c4de0533edc330ed30d36804b1df3 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 3 May 2022 19:38:20 +0200 Subject: [PATCH 184/930] Prevent Netgear SSDP from updating host (#71240) --- homeassistant/components/netgear/config_flow.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/netgear/config_flow.py b/homeassistant/components/netgear/config_flow.py index 85c206ce463..79053c712fc 100644 --- a/homeassistant/components/netgear/config_flow.py +++ b/homeassistant/components/netgear/config_flow.py @@ -19,6 +19,7 @@ from homeassistant.const import ( ) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.util.network import is_ipv4_address from .const import ( CONF_CONSIDER_HOME, @@ -129,6 +130,9 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): hostname = cast(str, hostname) updated_data[CONF_HOST] = hostname + if not is_ipv4_address(str(hostname)): + return self.async_abort(reason="not_ipv4_address") + _LOGGER.debug("Netgear ssdp discovery info: %s", discovery_info) await self.async_set_unique_id(discovery_info.upnp[ssdp.ATTR_UPNP_SERIAL]) From e1be6dd34f3258bb568b630a67d869b7e98cd438 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 12:38:44 -0500 Subject: [PATCH 185/930] Move recorder services to services.py (#71249) --- .strict-typing | 1 + homeassistant/components/recorder/__init__.py | 85 +------------- homeassistant/components/recorder/services.py | 106 ++++++++++++++++++ mypy.ini | 11 ++ tests/components/recorder/test_init.py | 10 +- tests/components/recorder/test_purge.py | 66 ++++------- 6 files changed, 147 insertions(+), 132 deletions(-) create mode 100644 homeassistant/components/recorder/services.py diff --git a/.strict-typing b/.strict-typing index 2915f398953..f42bd4a4ab1 100644 --- a/.strict-typing +++ b/.strict-typing @@ -188,6 +188,7 @@ homeassistant.components.recorder.pool homeassistant.components.recorder.purge homeassistant.components.recorder.repack homeassistant.components.recorder.run_history +homeassistant.components.recorder.services homeassistant.components.recorder.statistics homeassistant.components.recorder.system_health homeassistant.components.recorder.tasks diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index f9d462abeea..00f12710c18 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -7,7 +7,7 @@ from typing import Any import voluptuous as vol from homeassistant.const import CONF_EXCLUDE, EVENT_STATE_CHANGED -from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, @@ -17,15 +17,11 @@ from homeassistant.helpers.entityfilter import ( from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, ) -from homeassistant.helpers.service import async_extract_entity_ids from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from . import history, statistics, websocket_api from .const import ( - ATTR_APPLY_FILTER, - ATTR_KEEP_DAYS, - ATTR_REPACK, CONF_DB_INTEGRITY_CHECK, DATA_INSTANCE, DOMAIN, @@ -33,39 +29,12 @@ from .const import ( SQLITE_URL_PREFIX, ) from .core import Recorder +from .services import async_register_services from .tasks import AddRecorderPlatformTask _LOGGER = logging.getLogger(__name__) -SERVICE_PURGE = "purge" -SERVICE_PURGE_ENTITIES = "purge_entities" -SERVICE_ENABLE = "enable" -SERVICE_DISABLE = "disable" - - -SERVICE_PURGE_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_KEEP_DAYS): cv.positive_int, - vol.Optional(ATTR_REPACK, default=False): cv.boolean, - vol.Optional(ATTR_APPLY_FILTER, default=False): cv.boolean, - } -) - -ATTR_DOMAINS = "domains" -ATTR_ENTITY_GLOBS = "entity_globs" - -SERVICE_PURGE_ENTITIES_SCHEMA = vol.Schema( - { - vol.Optional(ATTR_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]), - vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All( - cv.ensure_list, [cv.string] - ), - } -).extend(cv.ENTITY_SERVICE_FIELDS) -SERVICE_ENABLE_SCHEMA = vol.Schema({}) -SERVICE_DISABLE_SCHEMA = vol.Schema({}) - DEFAULT_URL = "sqlite:///{hass_config_path}" DEFAULT_DB_FILE = "home-assistant_v2.db" DEFAULT_DB_INTEGRITY_CHECK = True @@ -196,7 +165,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: instance.async_initialize() instance.async_register() instance.start() - _async_register_services(hass, instance) + async_register_services(hass, instance) history.async_setup(hass) statistics.async_setup(hass) websocket_api.async_setup(hass) @@ -211,51 +180,3 @@ async def _process_recorder_platform( """Process a recorder platform.""" instance: Recorder = hass.data[DATA_INSTANCE] instance.queue.put(AddRecorderPlatformTask(domain, platform)) - - -@callback -def _async_register_services(hass: HomeAssistant, instance: Recorder) -> None: - """Register recorder services.""" - - async def async_handle_purge_service(service: ServiceCall) -> None: - """Handle calls to the purge service.""" - instance.do_adhoc_purge(**service.data) - - hass.services.async_register( - DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA - ) - - async def async_handle_purge_entities_service(service: ServiceCall) -> None: - """Handle calls to the purge entities service.""" - entity_ids = await async_extract_entity_ids(hass, service) - domains = service.data.get(ATTR_DOMAINS, []) - entity_globs = service.data.get(ATTR_ENTITY_GLOBS, []) - - instance.do_adhoc_purge_entities(entity_ids, domains, entity_globs) - - hass.services.async_register( - DOMAIN, - SERVICE_PURGE_ENTITIES, - async_handle_purge_entities_service, - schema=SERVICE_PURGE_ENTITIES_SCHEMA, - ) - - async def async_handle_enable_service(service: ServiceCall) -> None: - instance.set_enable(True) - - hass.services.async_register( - DOMAIN, - SERVICE_ENABLE, - async_handle_enable_service, - schema=SERVICE_ENABLE_SCHEMA, - ) - - async def async_handle_disable_service(service: ServiceCall) -> None: - instance.set_enable(False) - - hass.services.async_register( - DOMAIN, - SERVICE_DISABLE, - async_handle_disable_service, - schema=SERVICE_DISABLE_SCHEMA, - ) diff --git a/homeassistant/components/recorder/services.py b/homeassistant/components/recorder/services.py new file mode 100644 index 00000000000..3da2c63a27c --- /dev/null +++ b/homeassistant/components/recorder/services.py @@ -0,0 +1,106 @@ +"""Support for recorder services.""" +from __future__ import annotations + +import voluptuous as vol + +from homeassistant.core import HomeAssistant, ServiceCall, callback +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.service import async_extract_entity_ids + +from .const import ATTR_APPLY_FILTER, ATTR_KEEP_DAYS, ATTR_REPACK, DOMAIN +from .core import Recorder + +SERVICE_PURGE = "purge" +SERVICE_PURGE_ENTITIES = "purge_entities" +SERVICE_ENABLE = "enable" +SERVICE_DISABLE = "disable" + + +SERVICE_PURGE_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_KEEP_DAYS): cv.positive_int, + vol.Optional(ATTR_REPACK, default=False): cv.boolean, + vol.Optional(ATTR_APPLY_FILTER, default=False): cv.boolean, + } +) + +ATTR_DOMAINS = "domains" +ATTR_ENTITY_GLOBS = "entity_globs" + +SERVICE_PURGE_ENTITIES_SCHEMA = vol.Schema( + { + vol.Optional(ATTR_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All( + cv.ensure_list, [cv.string] + ), + } +).extend(cv.ENTITY_SERVICE_FIELDS) + +SERVICE_ENABLE_SCHEMA = vol.Schema({}) +SERVICE_DISABLE_SCHEMA = vol.Schema({}) + + +@callback +def _async_register_purge_service(hass: HomeAssistant, instance: Recorder) -> None: + async def async_handle_purge_service(service: ServiceCall) -> None: + """Handle calls to the purge service.""" + instance.do_adhoc_purge(**service.data) + + hass.services.async_register( + DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA + ) + + +@callback +def _async_register_purge_entities_service( + hass: HomeAssistant, instance: Recorder +) -> None: + async def async_handle_purge_entities_service(service: ServiceCall) -> None: + """Handle calls to the purge entities service.""" + entity_ids = await async_extract_entity_ids(hass, service) + domains = service.data.get(ATTR_DOMAINS, []) + entity_globs = service.data.get(ATTR_ENTITY_GLOBS, []) + + instance.do_adhoc_purge_entities(entity_ids, domains, entity_globs) + + hass.services.async_register( + DOMAIN, + SERVICE_PURGE_ENTITIES, + async_handle_purge_entities_service, + schema=SERVICE_PURGE_ENTITIES_SCHEMA, + ) + + +@callback +def _async_register_enable_service(hass: HomeAssistant, instance: Recorder) -> None: + async def async_handle_enable_service(service: ServiceCall) -> None: + instance.set_enable(True) + + hass.services.async_register( + DOMAIN, + SERVICE_ENABLE, + async_handle_enable_service, + schema=SERVICE_ENABLE_SCHEMA, + ) + + +@callback +def _async_register_disable_service(hass: HomeAssistant, instance: Recorder) -> None: + async def async_handle_disable_service(service: ServiceCall) -> None: + instance.set_enable(False) + + hass.services.async_register( + DOMAIN, + SERVICE_DISABLE, + async_handle_disable_service, + schema=SERVICE_DISABLE_SCHEMA, + ) + + +@callback +def async_register_services(hass: HomeAssistant, instance: Recorder) -> None: + """Register recorder services.""" + _async_register_purge_service(hass, instance) + _async_register_purge_entities_service(hass, instance) + _async_register_enable_service(hass, instance) + _async_register_disable_service(hass, instance) diff --git a/mypy.ini b/mypy.ini index b55d5e74998..3d97a716955 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1831,6 +1831,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.recorder.services] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.recorder.statistics] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index fc2c03e0039..1e8ff89f20d 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -20,10 +20,6 @@ from homeassistant.components.recorder import ( CONF_DB_URL, CONFIG_SCHEMA, DOMAIN, - SERVICE_DISABLE, - SERVICE_ENABLE, - SERVICE_PURGE, - SERVICE_PURGE_ENTITIES, SQLITE_URL_PREFIX, Recorder, get_instance, @@ -38,6 +34,12 @@ from homeassistant.components.recorder.models import ( StatisticsRuns, process_timestamp, ) +from homeassistant.components.recorder.services import ( + SERVICE_DISABLE, + SERVICE_ENABLE, + SERVICE_PURGE, + SERVICE_PURGE_ENTITIES, +) from homeassistant.components.recorder.util import session_scope from homeassistant.const import ( EVENT_HOMEASSISTANT_FINAL_WRITE, diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 8ac2f3a783c..7c3a2adcdc1 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -19,6 +19,10 @@ from homeassistant.components.recorder.models import ( StatisticsShortTerm, ) from homeassistant.components.recorder.purge import purge_old_data +from homeassistant.components.recorder.services import ( + SERVICE_PURGE, + SERVICE_PURGE_ENTITIES, +) from homeassistant.components.recorder.tasks import PurgeTask from homeassistant.components.recorder.util import session_scope from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON @@ -133,9 +137,7 @@ async def test_purge_old_states_encouters_database_corruption( "homeassistant.components.recorder.purge.purge_old_data", side_effect=sqlite3_exception, ): - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0} - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0}) await hass.async_block_till_done() await async_wait_recording_done(hass) @@ -169,9 +171,7 @@ async def test_purge_old_states_encounters_temporary_mysql_error( ), patch.object( instance.engine.dialect, "name", "mysql" ): - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0} - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0}) await hass.async_block_till_done() await async_wait_recording_done(hass) await async_wait_recording_done(hass) @@ -197,9 +197,7 @@ async def test_purge_old_states_encounters_operational_error( "homeassistant.components.recorder.purge._purge_old_recorder_runs", side_effect=exception, ): - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0} - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0}) await hass.async_block_till_done() await async_wait_recording_done(hass) await async_wait_recording_done(hass) @@ -452,9 +450,7 @@ async def test_purge_edge_case( events = session.query(Events).filter(Events.event_type == "EVENT_TEST_PURGE") assert events.count() == 1 - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -722,9 +718,7 @@ async def test_purge_filtered_states( assert events_keep.count() == 1 # Normal purge doesn't remove excluded entities - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -742,9 +736,7 @@ async def test_purge_filtered_states( # Test with 'apply_filter' = True service_data["apply_filter"] = True - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -780,9 +772,7 @@ async def test_purge_filtered_states( assert session.query(StateAttributes).count() == 11 # Do it again to make sure nothing changes - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -794,9 +784,7 @@ async def test_purge_filtered_states( assert session.query(StateAttributes).count() == 11 service_data = {"keep_days": 0} - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -844,9 +832,7 @@ async def test_purge_filtered_states_to_empty( # Test with 'apply_filter' = True service_data["apply_filter"] = True - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -858,9 +844,7 @@ async def test_purge_filtered_states_to_empty( # Do it again to make sure nothing changes # Why do we do this? Should we check the end result? - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -914,9 +898,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty( # Test with 'apply_filter' = True service_data["apply_filter"] = True - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -928,9 +910,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty( # Do it again to make sure nothing changes # Why do we do this? Should we check the end result? - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -985,9 +965,7 @@ async def test_purge_filtered_events( assert states.count() == 10 # Normal purge doesn't remove excluded events - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -1005,9 +983,7 @@ async def test_purge_filtered_events( # Test with 'apply_filter' = True service_data["apply_filter"] = True - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -1105,9 +1081,7 @@ async def test_purge_filtered_events_state_changed( assert events_purge.count() == 60 assert states.count() == 63 - await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE, service_data - ) + await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data) await hass.async_block_till_done() await async_recorder_block_till_done(hass) @@ -1146,7 +1120,7 @@ async def test_purge_entities( } await hass.services.async_call( - recorder.DOMAIN, recorder.SERVICE_PURGE_ENTITIES, service_data + recorder.DOMAIN, SERVICE_PURGE_ENTITIES, service_data ) await hass.async_block_till_done() From 236d8aa277354870e59c4496fd85adb488ef40bf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 13:22:49 -0500 Subject: [PATCH 186/930] Avoid recording static attributes for group entities (#71256) --- homeassistant/components/group/__init__.py | 3 ++ homeassistant/components/group/recorder.py | 16 ++++++ tests/components/group/test_recorder.py | 61 ++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 homeassistant/components/group/recorder.py create mode 100644 tests/components/group/test_recorder.py diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 9627ad86734..e6d7e91f035 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -33,6 +33,7 @@ from homeassistant.helpers.entity import Entity, async_generate_entity_id from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.integration_platform import ( + async_process_integration_platform_for_component, async_process_integration_platforms, ) from homeassistant.helpers.reload import async_reload_integration_platforms @@ -265,6 +266,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in hass.data: hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass) + await async_process_integration_platform_for_component(hass, DOMAIN) + component: EntityComponent = hass.data[DOMAIN] hass.data[REG_KEY] = GroupIntegrationRegistry() diff --git a/homeassistant/components/group/recorder.py b/homeassistant/components/group/recorder.py new file mode 100644 index 00000000000..9138b4ef348 --- /dev/null +++ b/homeassistant/components/group/recorder.py @@ -0,0 +1,16 @@ +"""Integration platform for recorder.""" +from __future__ import annotations + +from homeassistant.core import HomeAssistant, callback + +from . import ATTR_AUTO, ATTR_ENTITY_ID, ATTR_ORDER + + +@callback +def exclude_attributes(hass: HomeAssistant) -> set[str]: + """Exclude static attributes from being recorded in the database.""" + return { + ATTR_ENTITY_ID, + ATTR_ORDER, + ATTR_AUTO, + } diff --git a/tests/components/group/test_recorder.py b/tests/components/group/test_recorder.py new file mode 100644 index 00000000000..fb68d9d3d43 --- /dev/null +++ b/tests/components/group/test_recorder.py @@ -0,0 +1,61 @@ +"""The tests for group recorder.""" +from __future__ import annotations + +from datetime import timedelta + +from homeassistant.components import group +from homeassistant.components.group import ATTR_AUTO, ATTR_ENTITY_ID, ATTR_ORDER +from homeassistant.components.recorder.models import StateAttributes, States +from homeassistant.components.recorder.util import session_scope +from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_ON +from homeassistant.core import State +from homeassistant.setup import async_setup_component +from homeassistant.util import dt as dt_util + +from tests.common import async_fire_time_changed +from tests.components.recorder.common import async_wait_recording_done + + +async def test_exclude_attributes(hass, recorder_mock): + """Test number registered attributes to be excluded.""" + hass.states.async_set("light.bowl", STATE_ON) + + assert await async_setup_component(hass, "light", {}) + assert await async_setup_component( + hass, + group.DOMAIN, + { + group.DOMAIN: { + "group_zero": {"entities": "light.Bowl", "icon": "mdi:work"}, + "group_one": {"entities": "light.Bowl", "icon": "mdi:work"}, + "group_two": {"entities": "light.Bowl", "icon": "mdi:work"}, + } + }, + ) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5)) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + + def _fetch_states() -> list[State]: + with session_scope(hass=hass) as session: + native_states = [] + attr_ids = {} + for db_state_attributes in session.query(StateAttributes): + attr_ids[ + db_state_attributes.attributes_id + ] = db_state_attributes.to_native() + for db_state, db_state_attributes in session.query(States, StateAttributes): + state = db_state.to_native() + state.attributes = attr_ids[db_state.attributes_id] + native_states.append(state) + return native_states + + states: list[State] = await hass.async_add_executor_job(_fetch_states) + assert len(states) > 1 + for state in states: + if state.domain == group.DOMAIN: + assert ATTR_AUTO not in state.attributes + assert ATTR_ENTITY_ID not in state.attributes + assert ATTR_ORDER not in state.attributes + assert ATTR_FRIENDLY_NAME in state.attributes From 3aeda6b18ee79b127f29711a3e9cec84154647f4 Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Tue, 3 May 2022 14:34:20 -0400 Subject: [PATCH 187/930] Load Insteon modem database on startup if needed (#71261) --- homeassistant/components/insteon/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/insteon/__init__.py b/homeassistant/components/insteon/__init__.py index 94c18df9a33..e2bebf0bb7f 100644 --- a/homeassistant/components/insteon/__init__.py +++ b/homeassistant/components/insteon/__init__.py @@ -49,7 +49,7 @@ async def async_get_device_config(hass, config_entry): with suppress(AttributeError): await devices[address].async_status() - load_aldb = devices.modem.aldb.read_write_mode == ReadWriteMode.UNKNOWN + load_aldb = 2 if devices.modem.aldb.read_write_mode == ReadWriteMode.UNKNOWN else 1 await devices.async_load(id_devices=1, load_modem_aldb=load_aldb) for addr in devices: device = devices[addr] From 92a30a69a351a66b5f9ea0ea631d86dc0614dde3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 13:35:38 -0500 Subject: [PATCH 188/930] Fix oncue not logging back in when the session expires (#71258) --- homeassistant/components/oncue/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/oncue/manifest.json b/homeassistant/components/oncue/manifest.json index 9991d35ed18..e0533129d94 100644 --- a/homeassistant/components/oncue/manifest.json +++ b/homeassistant/components/oncue/manifest.json @@ -9,7 +9,7 @@ } ], "documentation": "https://www.home-assistant.io/integrations/oncue", - "requirements": ["aiooncue==0.3.2"], + "requirements": ["aiooncue==0.3.4"], "codeowners": ["@bdraco"], "iot_class": "cloud_polling", "loggers": ["aiooncue"] diff --git a/requirements_all.txt b/requirements_all.txt index 14154c718b1..e9c0844e2d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -211,7 +211,7 @@ aionotify==0.2.0 aionotion==3.0.2 # homeassistant.components.oncue -aiooncue==0.3.2 +aiooncue==0.3.4 # homeassistant.components.acmeda aiopulse==0.4.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9929fe508d4..400a60ec180 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -177,7 +177,7 @@ aionanoleaf==0.2.0 aionotion==3.0.2 # homeassistant.components.oncue -aiooncue==0.3.2 +aiooncue==0.3.4 # homeassistant.components.acmeda aiopulse==0.4.3 From c9d49ac9e4915343f53746e0403648ec7861ac9e Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 3 May 2022 13:51:07 -0500 Subject: [PATCH 189/930] Bump frontend to 20220503.0 (#71262) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 18e2fc6ed8f..89dd75fa96c 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220502.0"], + "requirements": ["home-assistant-frontend==20220503.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f328394b9c1..fcb63a9426c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220502.0 +home-assistant-frontend==20220503.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index e9c0844e2d3..e212a63b559 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220502.0 +home-assistant-frontend==20220503.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 400a60ec180..c6e1065a52a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220502.0 +home-assistant-frontend==20220503.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 5934167e15c20efec617a40ccaaa83e91ea54f0b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 May 2022 12:16:57 -0700 Subject: [PATCH 190/930] Bump aioslimproto to 2.0.0 (#71265) --- homeassistant/components/slimproto/manifest.json | 2 +- homeassistant/components/slimproto/media_player.py | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/slimproto/manifest.json b/homeassistant/components/slimproto/manifest.json index 557428919a4..eb4ab00f18d 100644 --- a/homeassistant/components/slimproto/manifest.json +++ b/homeassistant/components/slimproto/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "iot_class": "local_push", "documentation": "https://www.home-assistant.io/integrations/slimproto", - "requirements": ["aioslimproto==1.0.2"], + "requirements": ["aioslimproto==2.0.0"], "codeowners": ["@marcelveldt"], "after_dependencies": ["media_source"] } diff --git a/homeassistant/components/slimproto/media_player.py b/homeassistant/components/slimproto/media_player.py index d41d10432db..6b1989830e2 100644 --- a/homeassistant/components/slimproto/media_player.py +++ b/homeassistant/components/slimproto/media_player.py @@ -118,7 +118,7 @@ class SlimProtoPlayer(MediaPlayerEntity): EventType.PLAYER_CONNECTED, EventType.PLAYER_DISCONNECTED, EventType.PLAYER_NAME_RECEIVED, - EventType.PLAYER_RPC_EVENT, + EventType.PLAYER_CLI_EVENT, ), player_filter=self.player.player_id, ) @@ -205,7 +205,7 @@ class SlimProtoPlayer(MediaPlayerEntity): if event.type == EventType.PLAYER_CONNECTED: # player reconnected, update our player object self.player = self.slimserver.get_player(event.player_id) - if event.type == EventType.PLAYER_RPC_EVENT: + if event.type == EventType.PLAYER_CLI_EVENT: # rpc event from player such as a button press, # forward on the eventbus for others to handle dev_id = self.registry_entry.device_id if self.registry_entry else None diff --git a/requirements_all.txt b/requirements_all.txt index e212a63b559..438c951d214 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -244,7 +244,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==1.0.2 +aioslimproto==2.0.0 # homeassistant.components.steamist aiosteamist==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c6e1065a52a..b012f795c9e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -210,7 +210,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==1.0.2 +aioslimproto==2.0.0 # homeassistant.components.steamist aiosteamist==0.3.1 From 3717ec8811e41288cd50353e317a1bc411eedb41 Mon Sep 17 00:00:00 2001 From: James Szalay Date: Tue, 3 May 2022 15:17:27 -0400 Subject: [PATCH 191/930] Updated vesync component fans list to handle alt ids for models. (#71259) * Updated vesync component fans list to handle alt ids for models. * Lint Co-authored-by: Paulus Schoutsen --- homeassistant/components/vesync/fan.py | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 41ed6109be2..e37a6c8893e 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -20,10 +20,20 @@ _LOGGER = logging.getLogger(__name__) DEV_TYPE_TO_HA = { "LV-PUR131S": "fan", + "LV-RH131S": "fan", # Alt ID Model LV-PUR131S "Core200S": "fan", + "LAP-C201S-AUSR": "fan", # Alt ID Model Core200S + "LAP-C202S-WUSR": "fan", # Alt ID Model Core200S "Core300S": "fan", + "LAP-C301S-WJP": "fan", # Alt ID Model Core300S "Core400S": "fan", + "LAP-C401S-WJP": "fan", # Alt ID Model Core400S + "LAP-C401S-WUSR": "fan", # Alt ID Model Core400S + "LAP-C401S-WAAA": "fan", # Alt ID Model Core400S "Core600S": "fan", + "LAP-C601S-WUS": "fan", # Alt ID Model Core600S + "LAP-C601S-WUSR": "fan", # Alt ID Model Core600S + "LAP-C601S-WEU": "fan", # Alt ID Model Core600S } FAN_MODE_AUTO = "auto" @@ -31,17 +41,37 @@ FAN_MODE_SLEEP = "sleep" PRESET_MODES = { "LV-PUR131S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], + "LV-RH131S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model LV-PUR131S "Core200S": [FAN_MODE_SLEEP], + "LAP-C201S-AUSR": [FAN_MODE_SLEEP], # Alt ID Model Core200S + "LAP-C202S-WUSR": [FAN_MODE_SLEEP], # Alt ID Model Core200S "Core300S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], + "LAP-C301S-WJP": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core300S "Core400S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], + "LAP-C401S-WJP": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core400S + "LAP-C401S-WUSR": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core400S + "LAP-C401S-WAAA": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core400S "Core600S": [FAN_MODE_AUTO, FAN_MODE_SLEEP], + "LAP-C601S-WUS": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core600S + "LAP-C601S-WUSR": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core600S + "LAP-C601S-WEU": [FAN_MODE_AUTO, FAN_MODE_SLEEP], # Alt ID Model Core600S } SPEED_RANGE = { # off is not included "LV-PUR131S": (1, 3), + "LV-RH131S": (1, 3), # ALt ID Model LV-PUR131S "Core200S": (1, 3), + "LAP-C201S-AUSR": (1, 3), # ALt ID Model Core200S + "LAP-C202S-WUSR": (1, 3), # ALt ID Model Core200S "Core300S": (1, 3), + "LAP-C301S-WJP": (1, 3), # ALt ID Model Core300S "Core400S": (1, 4), + "LAP-C401S-WJP": (1, 4), # ALt ID Model Core400S + "LAP-C401S-WUSR": (1, 4), # ALt ID Model Core400S + "LAP-C401S-WAAA": (1, 4), # ALt ID Model Core400S "Core600S": (1, 4), + "LAP-C601S-WUS": (1, 4), # ALt ID Model Core600S + "LAP-C601S-WUSR": (1, 4), # ALt ID Model Core600S + "LAP-C601S-WEU": (1, 4), # ALt ID Model Core600S } From e9abfad3611a8f08b211eb39141e26aefb485609 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 3 May 2022 21:19:43 +0200 Subject: [PATCH 192/930] Reject MQTT topics which include control- or non-characters (#71263) --- homeassistant/components/mqtt/util.py | 9 +++++++++ tests/components/mqtt/test_init.py | 28 ++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index b8fca50a153..66eec1bdfe8 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -31,6 +31,15 @@ def valid_topic(value: Any) -> str: ) if "\0" in value: raise vol.Invalid("MQTT topic name/filter must not contain null character.") + if any(char <= "\u001F" for char in value): + raise vol.Invalid("MQTT topic name/filter must not contain control characters.") + if any("\u007f" <= char <= "\u009F" for char in value): + raise vol.Invalid("MQTT topic name/filter must not contain control characters.") + if any("\ufdd0" <= char <= "\ufdef" for char in value): + raise vol.Invalid("MQTT topic name/filter must not contain non-characters.") + if any((ord(char) & 0xFFFF) in (0xFFFE, 0xFFFF) for char in value): + raise vol.Invalid("MQTT topic name/filter must not contain noncharacters.") + return value diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 03874ed331e..763cbfc1b71 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -522,11 +522,29 @@ def test_validate_topic(): # Topics "SHOULD NOT" include these special characters # (not MUST NOT, RFC2119). The receiver MAY close the connection. - mqtt.util.valid_topic("\u0001") - mqtt.util.valid_topic("\u001F") - mqtt.util.valid_topic("\u009F") - mqtt.util.valid_topic("\u009F") - mqtt.util.valid_topic("\uffff") + # We enforce this because mosquitto does: https://github.com/eclipse/mosquitto/commit/94fdc9cb44c829ff79c74e1daa6f7d04283dfffd + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\u0001") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\u001F") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\u007F") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\u009F") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\ufdd0") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\ufdef") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\ufffe") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\ufffe") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\uffff") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\U0001fffe") + with pytest.raises(vol.Invalid): + mqtt.util.valid_topic("\U0001ffff") def test_validate_subscribe_topic(): From e30940ef2adc2a2f4b006e85092f53bcbcb68bfb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 15:56:22 -0500 Subject: [PATCH 193/930] Move processing of recorder service call arguments into services.py (#71260) --- homeassistant/components/recorder/__init__.py | 2 +- homeassistant/components/recorder/core.py | 170 ++++++++---------- homeassistant/components/recorder/services.py | 18 +- homeassistant/components/recorder/tasks.py | 12 +- .../components/recorder/websocket_api.py | 2 +- tests/components/recorder/test_init.py | 2 +- tests/components/recorder/test_purge.py | 6 +- 7 files changed, 104 insertions(+), 108 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 00f12710c18..ca1ecd8c71a 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -179,4 +179,4 @@ async def _process_recorder_platform( ) -> None: """Process a recorder platform.""" instance: Recorder = hass.data[DATA_INSTANCE] - instance.queue.put(AddRecorderPlatformTask(domain, platform)) + instance.queue_task(AddRecorderPlatformTask(domain, platform)) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index b96eee2c67f..7f07a4483cb 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -28,7 +28,6 @@ from homeassistant.const import ( MATCH_ALL, ) from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback -from homeassistant.helpers.entityfilter import generate_filter from homeassistant.helpers.event import ( async_track_time_change, async_track_time_interval, @@ -38,9 +37,6 @@ import homeassistant.util.dt as dt_util from . import migration, statistics from .const import ( - ATTR_APPLY_FILTER, - ATTR_KEEP_DAYS, - ATTR_REPACK, DB_WORKER_PREFIX, KEEPALIVE_TIME, MAX_QUEUE_BACKLOG, @@ -70,7 +66,6 @@ from .tasks import ( ExternalStatisticsTask, KeepAliveTask, PerodicCleanupTask, - PurgeEntitiesTask, PurgeTask, RecorderTask, StatisticsTask, @@ -112,6 +107,7 @@ SHUTDOWN_TASK = object() COMMIT_TASK = CommitTask() KEEP_ALIVE_TASK = KeepAliveTask() +WAIT_TASK = WaitTask() DB_LOCK_TIMEOUT = 30 DB_LOCK_QUEUE_CHECK_TIMEOUT = 1 @@ -152,7 +148,7 @@ class Recorder(threading.Thread): self.keep_days = keep_days self._hass_started: asyncio.Future[object] = asyncio.Future() self.commit_interval = commit_interval - self.queue: queue.SimpleQueue[RecorderTask] = queue.SimpleQueue() + self._queue: queue.SimpleQueue[RecorderTask] = queue.SimpleQueue() self.db_url = uri self.db_max_retries = db_max_retries self.db_retry_wait = db_retry_wait @@ -175,21 +171,42 @@ class Recorder(threading.Thread): self.event_session: Session | None = None self.get_session: Callable[[], Session] | None = None self._completed_first_database_setup: bool | None = None - self._event_listener: CALLBACK_TYPE | None = None self.async_migration_event = asyncio.Event() self.migration_in_progress = False - self._queue_watcher: CALLBACK_TYPE | None = None self._db_supports_row_number = True self._database_lock_task: DatabaseLockTask | None = None self._db_executor: DBInterruptibleThreadPoolExecutor | None = None self._exclude_attributes_by_domain = exclude_attributes_by_domain + self._event_listener: CALLBACK_TYPE | None = None + self._queue_watcher: CALLBACK_TYPE | None = None self._keep_alive_listener: CALLBACK_TYPE | None = None self._commit_listener: CALLBACK_TYPE | None = None self._periodic_listener: CALLBACK_TYPE | None = None self._nightly_listener: CALLBACK_TYPE | None = None self.enabled = True + @property + def backlog(self) -> int: + """Return the number of items in the recorder backlog.""" + return self._queue.qsize() + + @property + def _using_file_sqlite(self) -> bool: + """Short version to check if we are using sqlite3 as a file.""" + return self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith( + SQLITE_URL_PREFIX + ) + + @property + def recording(self) -> bool: + """Return if the recorder is recording.""" + return self._event_listener is not None + + def queue_task(self, task: RecorderTask) -> None: + """Add a task to the recorder queue.""" + self._queue.put(task) + def set_enable(self, enable: bool) -> None: """Enable or disable recording events and states.""" self.enabled = enable @@ -222,7 +239,7 @@ class Recorder(threading.Thread): def _async_keep_alive(self, now: datetime) -> None: """Queue a keep alive.""" if self._event_listener: - self.queue.put(KEEP_ALIVE_TASK) + self.queue_task(KEEP_ALIVE_TASK) @callback def _async_commit(self, now: datetime) -> None: @@ -232,7 +249,7 @@ class Recorder(threading.Thread): and not self._database_lock_task and self._event_session_has_pending_writes() ): - self.queue.put(COMMIT_TASK) + self.queue_task(COMMIT_TASK) @callback def async_add_executor_job( @@ -253,7 +270,7 @@ class Recorder(threading.Thread): The queue grows during migraton or if something really goes wrong. """ - size = self.queue.qsize() + size = self.backlog _LOGGER.debug("Recorder queue size is: %s", size) if size <= MAX_QUEUE_BACKLOG: return @@ -314,73 +331,52 @@ class Recorder(threading.Thread): # Unknown what it is. return True - def do_adhoc_purge(self, **kwargs: Any) -> None: - """Trigger an adhoc purge retaining keep_days worth of data.""" - keep_days = kwargs.get(ATTR_KEEP_DAYS, self.keep_days) - repack = cast(bool, kwargs[ATTR_REPACK]) - apply_filter = cast(bool, kwargs[ATTR_APPLY_FILTER]) - - purge_before = dt_util.utcnow() - timedelta(days=keep_days) - self.queue.put(PurgeTask(purge_before, repack, apply_filter)) - - def do_adhoc_purge_entities( - self, entity_ids: set[str], domains: list[str], entity_globs: list[str] - ) -> None: - """Trigger an adhoc purge of requested entities.""" - entity_filter = generate_filter(domains, list(entity_ids), [], [], entity_globs) - self.queue.put(PurgeEntitiesTask(entity_filter)) - def do_adhoc_statistics(self, **kwargs: Any) -> None: """Trigger an adhoc statistics run.""" if not (start := kwargs.get("start")): start = statistics.get_start_time() - self.queue.put(StatisticsTask(start)) + self.queue_task(StatisticsTask(start)) + + def _empty_queue(self, event: Event) -> None: + """Empty the queue if its still present at final write.""" + + # If the queue is full of events to be processed because + # the database is so broken that every event results in a retry + # we will never be able to get though the events to shutdown in time. + # + # We drain all the events in the queue and then insert + # an empty one to ensure the next thing the recorder sees + # is a request to shutdown. + while True: + try: + self._queue.get_nowait() + except queue.Empty: + break + self.queue_task(StopTask()) + + async def _async_shutdown(self, event: Event) -> None: + """Shut down the Recorder.""" + if not self._hass_started.done(): + self._hass_started.set_result(SHUTDOWN_TASK) + self.queue_task(StopTask()) + self._async_stop_listeners() + await self.hass.async_add_executor_job(self.join) + + @callback + def _async_hass_started(self, event: Event) -> None: + """Notify that hass has started.""" + self._hass_started.set_result(None) @callback def async_register(self) -> None: """Post connection initialize.""" - - def _empty_queue(event: Event) -> None: - """Empty the queue if its still present at final write.""" - - # If the queue is full of events to be processed because - # the database is so broken that every event results in a retry - # we will never be able to get though the events to shutdown in time. - # - # We drain all the events in the queue and then insert - # an empty one to ensure the next thing the recorder sees - # is a request to shutdown. - while True: - try: - self.queue.get_nowait() - except queue.Empty: - break - self.queue.put(StopTask()) - - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_FINAL_WRITE, _empty_queue) - - async def _async_shutdown(event: Event) -> None: - """Shut down the Recorder.""" - if not self._hass_started.done(): - self._hass_started.set_result(SHUTDOWN_TASK) - self.queue.put(StopTask()) - self._async_stop_listeners() - await self.hass.async_add_executor_job(self.join) - - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_shutdown) - + bus = self.hass.bus + bus.async_listen_once(EVENT_HOMEASSISTANT_FINAL_WRITE, self._empty_queue) + bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._async_shutdown) if self.hass.state == CoreState.running: self._hass_started.set_result(None) return - - @callback - def _async_hass_started(event: Event) -> None: - """Notify that hass has started.""" - self._hass_started.set_result(None) - - self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STARTED, _async_hass_started - ) + bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, self._async_hass_started) @callback def async_connection_failed(self) -> None: @@ -414,9 +410,9 @@ class Recorder(threading.Thread): # until after the database is vacuumed repack = self.auto_repack and is_second_sunday(now) purge_before = dt_util.utcnow() - timedelta(days=self.keep_days) - self.queue.put(PurgeTask(purge_before, repack=repack, apply_filter=False)) + self.queue_task(PurgeTask(purge_before, repack=repack, apply_filter=False)) else: - self.queue.put(PerodicCleanupTask()) + self.queue_task(PerodicCleanupTask()) @callback def async_periodic_statistics(self, now: datetime) -> None: @@ -425,33 +421,33 @@ class Recorder(threading.Thread): Short term statistics run every 5 minutes """ start = statistics.get_start_time() - self.queue.put(StatisticsTask(start)) + self.queue_task(StatisticsTask(start)) @callback def async_adjust_statistics( self, statistic_id: str, start_time: datetime, sum_adjustment: float ) -> None: """Adjust statistics.""" - self.queue.put(AdjustStatisticsTask(statistic_id, start_time, sum_adjustment)) + self.queue_task(AdjustStatisticsTask(statistic_id, start_time, sum_adjustment)) @callback def async_clear_statistics(self, statistic_ids: list[str]) -> None: """Clear statistics for a list of statistic_ids.""" - self.queue.put(ClearStatisticsTask(statistic_ids)) + self.queue_task(ClearStatisticsTask(statistic_ids)) @callback def async_update_statistics_metadata( self, statistic_id: str, unit_of_measurement: str | None ) -> None: """Update statistics metadata for a statistic_id.""" - self.queue.put(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) + self.queue_task(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) @callback def async_external_statistics( self, metadata: StatisticMetaData, stats: Iterable[StatisticData] ) -> None: """Schedule external statistics.""" - self.queue.put(ExternalStatisticsTask(metadata, stats)) + self.queue_task(ExternalStatisticsTask(metadata, stats)) @callback def using_sqlite(self) -> bool: @@ -553,7 +549,7 @@ class Recorder(threading.Thread): # has changed. This reduces the disk io. self.stop_requested = False while not self.stop_requested: - task = self.queue.get() + task = self._queue.get() _LOGGER.debug("Processing task: %s", task) try: self._process_one_task_or_recover(task) @@ -643,7 +639,7 @@ class Recorder(threading.Thread): # Notify that lock is being held, wait until database can be used again. self.hass.add_job(_async_set_database_locked, task) while not task.database_unlock.wait(timeout=DB_LOCK_QUEUE_CHECK_TIMEOUT): - if self.queue.qsize() > MAX_QUEUE_BACKLOG * 0.9: + if self.backlog > MAX_QUEUE_BACKLOG * 0.9: _LOGGER.warning( "Database queue backlog reached more than 90% of maximum queue " "length while waiting for backup to finish; recorder will now " @@ -654,7 +650,7 @@ class Recorder(threading.Thread): break _LOGGER.info( "Database queue backlog reached %d entries during backup", - self.queue.qsize(), + self.backlog, ) def _process_one_event(self, event: Event) -> None: @@ -908,7 +904,7 @@ class Recorder(threading.Thread): @callback def event_listener(self, event: Event) -> None: """Listen for new events and put them in the process queue.""" - self.queue.put(EventTask(event)) + self.queue_task(EventTask(event)) def block_till_done(self) -> None: """Block till all events processed. @@ -923,7 +919,7 @@ class Recorder(threading.Thread): is in the database. """ self._queue_watch.clear() - self.queue.put(WaitTask()) + self.queue_task(WAIT_TASK) self._queue_watch.wait() async def lock_database(self) -> bool: @@ -940,7 +936,7 @@ class Recorder(threading.Thread): database_locked = asyncio.Event() task = DatabaseLockTask(database_locked, threading.Event(), False) - self.queue.put(task) + self.queue_task(task) try: await asyncio.wait_for(database_locked.wait(), timeout=DB_LOCK_TIMEOUT) except asyncio.TimeoutError as err: @@ -1013,13 +1009,6 @@ class Recorder(threading.Thread): self.get_session = scoped_session(sessionmaker(bind=self.engine, future=True)) _LOGGER.debug("Connected to recorder database") - @property - def _using_file_sqlite(self) -> bool: - """Short version to check if we are using sqlite3 as a file.""" - return self.db_url != SQLITE_URL_PREFIX and self.db_url.startswith( - SQLITE_URL_PREFIX - ) - def _close_connection(self) -> None: """Close the connection.""" assert self.engine is not None @@ -1053,7 +1042,7 @@ class Recorder(threading.Thread): while start < last_period: end = start + timedelta(minutes=5) _LOGGER.debug("Compiling missing statistics for %s-%s", start, end) - self.queue.put(StatisticsTask(start)) + self.queue_task(StatisticsTask(start)) start = end def _end_session(self) -> None: @@ -1075,8 +1064,3 @@ class Recorder(threading.Thread): self._stop_executor() self._end_session() self._close_connection() - - @property - def recording(self) -> bool: - """Return if the recorder is recording.""" - return self._event_listener is not None diff --git a/homeassistant/components/recorder/services.py b/homeassistant/components/recorder/services.py index 3da2c63a27c..14337290c9b 100644 --- a/homeassistant/components/recorder/services.py +++ b/homeassistant/components/recorder/services.py @@ -1,21 +1,26 @@ """Support for recorder services.""" from __future__ import annotations +from datetime import timedelta +from typing import cast + import voluptuous as vol from homeassistant.core import HomeAssistant, ServiceCall, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entityfilter import generate_filter from homeassistant.helpers.service import async_extract_entity_ids +import homeassistant.util.dt as dt_util from .const import ATTR_APPLY_FILTER, ATTR_KEEP_DAYS, ATTR_REPACK, DOMAIN from .core import Recorder +from .tasks import PurgeEntitiesTask, PurgeTask SERVICE_PURGE = "purge" SERVICE_PURGE_ENTITIES = "purge_entities" SERVICE_ENABLE = "enable" SERVICE_DISABLE = "disable" - SERVICE_PURGE_SCHEMA = vol.Schema( { vol.Optional(ATTR_KEEP_DAYS): cv.positive_int, @@ -44,7 +49,12 @@ SERVICE_DISABLE_SCHEMA = vol.Schema({}) def _async_register_purge_service(hass: HomeAssistant, instance: Recorder) -> None: async def async_handle_purge_service(service: ServiceCall) -> None: """Handle calls to the purge service.""" - instance.do_adhoc_purge(**service.data) + kwargs = service.data + keep_days = kwargs.get(ATTR_KEEP_DAYS, instance.keep_days) + repack = cast(bool, kwargs[ATTR_REPACK]) + apply_filter = cast(bool, kwargs[ATTR_APPLY_FILTER]) + purge_before = dt_util.utcnow() - timedelta(days=keep_days) + instance.queue_task(PurgeTask(purge_before, repack, apply_filter)) hass.services.async_register( DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA @@ -60,8 +70,8 @@ def _async_register_purge_entities_service( entity_ids = await async_extract_entity_ids(hass, service) domains = service.data.get(ATTR_DOMAINS, []) entity_globs = service.data.get(ATTR_ENTITY_GLOBS, []) - - instance.do_adhoc_purge_entities(entity_ids, domains, entity_globs) + entity_filter = generate_filter(domains, list(entity_ids), [], [], entity_globs) + instance.queue_task(PurgeEntitiesTask(entity_filter)) hass.services.async_register( DOMAIN, diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py index fdffa63bcd3..bed49e36f16 100644 --- a/homeassistant/components/recorder/tasks.py +++ b/homeassistant/components/recorder/tasks.py @@ -78,7 +78,9 @@ class PurgeTask(RecorderTask): periodic_db_cleanups(instance) return # Schedule a new purge task if this one didn't finish - instance.queue.put(PurgeTask(self.purge_before, self.repack, self.apply_filter)) + instance.queue_task( + PurgeTask(self.purge_before, self.repack, self.apply_filter) + ) @dataclass @@ -92,7 +94,7 @@ class PurgeEntitiesTask(RecorderTask): if purge.purge_entity_data(instance, self.entity_filter): return # Schedule a new purge task if this one didn't finish - instance.queue.put(PurgeEntitiesTask(self.entity_filter)) + instance.queue_task(PurgeEntitiesTask(self.entity_filter)) @dataclass @@ -115,7 +117,7 @@ class StatisticsTask(RecorderTask): if statistics.compile_statistics(instance, self.start): return # Schedule a new statistics task if this one didn't finish - instance.queue.put(StatisticsTask(self.start)) + instance.queue_task(StatisticsTask(self.start)) @dataclass @@ -130,7 +132,7 @@ class ExternalStatisticsTask(RecorderTask): if statistics.add_external_statistics(instance, self.metadata, self.statistics): return # Schedule a new statistics task if this one didn't finish - instance.queue.put(ExternalStatisticsTask(self.metadata, self.statistics)) + instance.queue_task(ExternalStatisticsTask(self.metadata, self.statistics)) @dataclass @@ -151,7 +153,7 @@ class AdjustStatisticsTask(RecorderTask): ): return # Schedule a new adjust statistics task if this one didn't finish - instance.queue.put( + instance.queue_task( AdjustStatisticsTask( self.statistic_id, self.start_time, self.sum_adjustment ) diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index 585641665af..a851d2681f4 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -148,7 +148,7 @@ def ws_info( """Return status of the recorder.""" instance: Recorder = hass.data[DATA_INSTANCE] - backlog = instance.queue.qsize() if instance and instance.queue else None + backlog = instance.backlog if instance else None migration_in_progress = async_migration_in_progress(hass) recording = instance.recording if instance else False thread_alive = instance.is_alive() if instance else False diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 1e8ff89f20d..9bfca76394b 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -1395,7 +1395,7 @@ async def test_database_lock_timeout(hass, recorder_mock): self.event.wait() block_task = BlockQueue() - instance.queue.put(block_task) + instance.queue_task(block_task) with patch.object(recorder.core, "DB_LOCK_TIMEOUT", 0.1): try: with pytest.raises(TimeoutError): diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 7c3a2adcdc1..2d711f17cf3 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -557,7 +557,7 @@ async def test_purge_cutoff_date( assert events.filter(Events.event_type == "PURGE").count() == rows - 1 assert events.filter(Events.event_type == "KEEP").count() == 1 - instance.queue.put(PurgeTask(cutoff, repack=False, apply_filter=False)) + instance.queue_task(PurgeTask(cutoff, repack=False, apply_filter=False)) await hass.async_block_till_done() await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -588,7 +588,7 @@ async def test_purge_cutoff_date( assert events.filter(Events.event_type == "KEEP").count() == 1 # Make sure we can purge everything - instance.queue.put(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)) + instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) @@ -599,7 +599,7 @@ async def test_purge_cutoff_date( assert state_attributes.count() == 0 # Make sure we can purge everything when the db is already empty - instance.queue.put(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)) + instance.queue_task(PurgeTask(dt_util.utcnow(), repack=False, apply_filter=False)) await async_recorder_block_till_done(hass) await async_wait_purge_done(hass) From 93153b379047c1ebe654a40de796f8e5b729bef0 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Tue, 3 May 2022 22:56:57 +0200 Subject: [PATCH 194/930] Add UniqueID to AsusWRT config entry (#70478) --- .../components/asuswrt/config_flow.py | 59 ++++-- .../components/asuswrt/diagnostics.py | 3 +- homeassistant/components/asuswrt/router.py | 19 +- homeassistant/components/asuswrt/sensor.py | 8 +- homeassistant/components/asuswrt/strings.json | 3 +- .../components/asuswrt/translations/en.json | 3 +- tests/components/asuswrt/test_config_flow.py | 187 +++++++++++------- tests/components/asuswrt/test_sensor.py | 19 +- 8 files changed, 201 insertions(+), 100 deletions(-) diff --git a/homeassistant/components/asuswrt/config_flow.py b/homeassistant/components/asuswrt/config_flow.py index ab62b879f75..e1b34cca15b 100644 --- a/homeassistant/components/asuswrt/config_flow.py +++ b/homeassistant/components/asuswrt/config_flow.py @@ -25,6 +25,7 @@ from homeassistant.const import ( from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.device_registry import format_mac from .const import ( CONF_DNSMASQ, @@ -41,11 +42,13 @@ from .const import ( PROTOCOL_SSH, PROTOCOL_TELNET, ) -from .router import get_api +from .router import get_api, get_nvram_info + +LABEL_MAC = "LABEL_MAC" RESULT_CONN_ERROR = "cannot_connect" -RESULT_UNKNOWN = "unknown" RESULT_SUCCESS = "success" +RESULT_UNKNOWN = "unknown" _LOGGER = logging.getLogger(__name__) @@ -107,7 +110,9 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): ) @staticmethod - async def _async_check_connection(user_input: dict[str, Any]) -> str: + async def _async_check_connection( + user_input: dict[str, Any] + ) -> tuple[str, str | None]: """Attempt to connect the AsusWrt router.""" host: str = user_input[CONF_HOST] @@ -117,29 +122,37 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): except OSError: _LOGGER.error("Error connecting to the AsusWrt router at %s", host) - return RESULT_CONN_ERROR + return RESULT_CONN_ERROR, None except Exception: # pylint: disable=broad-except _LOGGER.exception( "Unknown error connecting with AsusWrt router at %s", host ) - return RESULT_UNKNOWN + return RESULT_UNKNOWN, None if not api.is_connected: _LOGGER.error("Error connecting to the AsusWrt router at %s", host) - return RESULT_CONN_ERROR + return RESULT_CONN_ERROR, None + label_mac = await get_nvram_info(api, LABEL_MAC) conf_protocol = user_input[CONF_PROTOCOL] if conf_protocol == PROTOCOL_TELNET: api.connection.disconnect() - return RESULT_SUCCESS + + unique_id = None + if label_mac and "label_mac" in label_mac: + unique_id = format_mac(label_mac["label_mac"]) + return RESULT_SUCCESS, unique_id async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initiated by the user.""" - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") + + # if exist one entry without unique ID, we abort config flow + for unique_id in self._async_current_ids(): + if unique_id is None: + return self.async_abort(reason="not_unique_id_exist") if user_input is None: return self._show_setup_form(user_input) @@ -166,17 +179,27 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): errors["base"] = "invalid_host" if not errors: - result = await self._async_check_connection(user_input) - if result != RESULT_SUCCESS: - errors["base"] = result + result, unique_id = await self._async_check_connection(user_input) + if result == RESULT_SUCCESS: + if unique_id: + await self.async_set_unique_id(unique_id) + # we allow configure a single instance without unique id + elif self._async_current_entries(): + return self.async_abort(reason="invalid_unique_id") + else: + _LOGGER.warning( + "This device do not provide a valid Unique ID." + " Configuration of multiple instance will not be possible" + ) - if errors: - return self._show_setup_form(user_input, errors) + return self.async_create_entry( + title=host, + data=user_input, + ) - return self.async_create_entry( - title=host, - data=user_input, - ) + errors["base"] = result + + return self._show_setup_form(user_input, errors) @staticmethod @callback diff --git a/homeassistant/components/asuswrt/diagnostics.py b/homeassistant/components/asuswrt/diagnostics.py index 16f5468f87d..61de4c866db 100644 --- a/homeassistant/components/asuswrt/diagnostics.py +++ b/homeassistant/components/asuswrt/diagnostics.py @@ -11,6 +11,7 @@ from homeassistant.const import ( ATTR_CONNECTIONS, ATTR_IDENTIFIERS, CONF_PASSWORD, + CONF_UNIQUE_ID, CONF_USERNAME, ) from homeassistant.core import HomeAssistant @@ -19,7 +20,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er from .const import DATA_ASUSWRT, DOMAIN from .router import AsusWrtRouter -TO_REDACT = {CONF_PASSWORD, CONF_USERNAME} +TO_REDACT = {CONF_PASSWORD, CONF_UNIQUE_ID, CONF_USERNAME} TO_REDACT_DEV = {ATTR_CONNECTIONS, ATTR_IDENTIFIERS} diff --git a/homeassistant/components/asuswrt/router.py b/homeassistant/components/asuswrt/router.py index 2484b9880c3..f68c77a2a66 100644 --- a/homeassistant/components/asuswrt/router.py +++ b/homeassistant/components/asuswrt/router.py @@ -51,6 +51,7 @@ from .const import ( ) CONF_REQ_RELOAD = [CONF_DNSMASQ, CONF_INTERFACE, CONF_REQUIRE_IP] +DEFAULT_NAME = "Asuswrt" KEY_COORDINATOR = "coordinator" KEY_SENSORS = "sensors" @@ -260,10 +261,10 @@ class AsusWrtRouter: raise ConfigEntryNotReady # System - model = await _get_nvram_info(self._api, "MODEL") + model = await get_nvram_info(self._api, "MODEL") if model and "model" in model: self._model = model["model"] - firmware = await _get_nvram_info(self._api, "FIRMWARE") + firmware = await get_nvram_info(self._api, "FIRMWARE") if firmware and "firmver" in firmware and "buildno" in firmware: self._sw_v = f"{firmware['firmver']} (build {firmware['buildno']})" @@ -441,7 +442,7 @@ class AsusWrtRouter: def device_info(self) -> DeviceInfo: """Return the device information.""" return DeviceInfo( - identifiers={(DOMAIN, "AsusWRT")}, + identifiers={(DOMAIN, self.unique_id or "AsusWRT")}, name=self._host, model=self._model, manufacturer="Asus", @@ -464,6 +465,16 @@ class AsusWrtRouter: """Return router hostname.""" return self._host + @property + def unique_id(self) -> str | None: + """Return router unique id.""" + return self._entry.unique_id + + @property + def name(self) -> str: + """Return router name.""" + return self._host if self.unique_id else DEFAULT_NAME + @property def devices(self) -> dict[str, AsusWrtDevInfo]: """Return devices.""" @@ -475,7 +486,7 @@ class AsusWrtRouter: return self._sensors_coordinator -async def _get_nvram_info(api: AsusWrt, info_type: str) -> dict[str, Any]: +async def get_nvram_info(api: AsusWrt, info_type: str) -> dict[str, Any]: """Get AsusWrt router info from nvram.""" info = {} try: diff --git a/homeassistant/components/asuswrt/sensor.py b/homeassistant/components/asuswrt/sensor.py index 5c46b3e693c..e66874e4137 100644 --- a/homeassistant/components/asuswrt/sensor.py +++ b/homeassistant/components/asuswrt/sensor.py @@ -43,7 +43,6 @@ class AsusWrtSensorEntityDescription(SensorEntityDescription): precision: int = 2 -DEFAULT_PREFIX = "Asuswrt" UNIT_DEVICES = "Devices" CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = ( @@ -190,8 +189,11 @@ class AsusWrtSensor(CoordinatorEntity, SensorEntity): super().__init__(coordinator) self.entity_description: AsusWrtSensorEntityDescription = description - self._attr_name = f"{DEFAULT_PREFIX} {description.name}" - self._attr_unique_id = f"{DOMAIN} {self.name}" + self._attr_name = f"{router.name} {description.name}" + if router.unique_id: + self._attr_unique_id = f"{DOMAIN} {router.unique_id} {description.name}" + else: + self._attr_unique_id = f"{DOMAIN} {self.name}" self._attr_device_info = router.device_info self._attr_extra_state_attributes = {"hostname": router.host} diff --git a/homeassistant/components/asuswrt/strings.json b/homeassistant/components/asuswrt/strings.json index 56f3c659c0b..9023f5aa604 100644 --- a/homeassistant/components/asuswrt/strings.json +++ b/homeassistant/components/asuswrt/strings.json @@ -25,7 +25,8 @@ "unknown": "[%key:common::config_flow::error::unknown%]" }, "abort": { - "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" + "invalid_unique_id": "Impossible to determine a valid unique id for the device", + "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible" } }, "options": { diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index 4bef898ff35..494156eae45 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "single_instance_allowed": "Already configured. Only a single configuration possible." + "invalid_unique_id": "Impossible to determine a valid unique id for the device", + "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible" }, "error": { "cannot_connect": "Failed to connect", diff --git a/tests/components/asuswrt/test_config_flow.py b/tests/components/asuswrt/test_config_flow.py index a1a37d25460..09c484ff69d 100644 --- a/tests/components/asuswrt/test_config_flow.py +++ b/tests/components/asuswrt/test_config_flow.py @@ -28,6 +28,7 @@ from tests.common import MockConfigEntry HOST = "myrouter.asuswrt.com" IP_ADDRESS = "192.168.1.1" +MAC_ADDR = "a1:b1:c1:d1:e1:f1" SSH_KEY = "1234" CONFIG_DATA = { @@ -39,19 +40,44 @@ CONFIG_DATA = { CONF_MODE: "ap", } +PATCH_GET_HOST = patch( + "homeassistant.components.asuswrt.config_flow.socket.gethostbyname", + return_value=IP_ADDRESS, +) + +PATCH_SETUP_ENTRY = patch( + "homeassistant.components.asuswrt.async_setup_entry", + return_value=True, +) + + +@pytest.fixture(name="mock_unique_id") +def mock_unique_id_fixture(): + """Mock returned unique id.""" + return {} + @pytest.fixture(name="connect") -def mock_controller_connect(): +def mock_controller_connect(mock_unique_id): """Mock a successful connection.""" with patch("homeassistant.components.asuswrt.router.AsusWrt") as service_mock: service_mock.return_value.connection.async_connect = AsyncMock() service_mock.return_value.is_connected = True service_mock.return_value.connection.disconnect = Mock() + service_mock.return_value.async_get_nvram = AsyncMock( + return_value=mock_unique_id + ) yield service_mock -async def test_user(hass, connect): +@pytest.mark.usefixtures("connect") +@pytest.mark.parametrize( + "unique_id", + [{}, {"label_mac": MAC_ADDR}], +) +async def test_user(hass, mock_unique_id, unique_id): """Test user config.""" + mock_unique_id.update(unique_id) flow_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER, "show_advanced_options": True} ) @@ -59,15 +85,10 @@ async def test_user(hass, connect): assert flow_result["step_id"] == "user" # test with all provided - with patch( - "homeassistant.components.asuswrt.async_setup_entry", - return_value=True, - ) as mock_setup_entry, patch( - "homeassistant.components.asuswrt.config_flow.socket.gethostbyname", - return_value=IP_ADDRESS, - ): + with PATCH_GET_HOST, PATCH_SETUP_ENTRY as mock_setup_entry: result = await hass.config_entries.flow.async_configure( - flow_result["flow_id"], user_input=CONFIG_DATA + flow_result["flow_id"], + user_input=CONFIG_DATA, ) await hass.async_block_till_done() @@ -78,10 +99,17 @@ async def test_user(hass, connect): assert len(mock_setup_entry.mock_calls) == 1 -async def test_error_no_password_ssh(hass): - """Test we abort if component is already setup.""" +@pytest.mark.parametrize( + ["config", "error"], + [ + ({CONF_PASSWORD: None}, "pwd_or_ssh"), + ({CONF_SSH_KEY: SSH_KEY}, "pwd_and_ssh"), + ], +) +async def test_error_wrong_password_ssh(hass, config, error): + """Test we abort for wrong password and ssh file combination.""" config_data = CONFIG_DATA.copy() - config_data.pop(CONF_PASSWORD) + config_data.update(config) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER, "show_advanced_options": True}, @@ -89,36 +117,27 @@ async def test_error_no_password_ssh(hass): ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "pwd_or_ssh"} - - -async def test_error_both_password_ssh(hass): - """Test we abort if component is already setup.""" - config_data = CONFIG_DATA.copy() - config_data[CONF_SSH_KEY] = SSH_KEY - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER, "show_advanced_options": True}, - data=config_data, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "pwd_and_ssh"} + assert result["errors"] == {"base": error} async def test_error_invalid_ssh(hass): - """Test we abort if component is already setup.""" + """Test we abort if invalid ssh file is provided.""" config_data = CONFIG_DATA.copy() config_data.pop(CONF_PASSWORD) config_data[CONF_SSH_KEY] = SSH_KEY - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_USER, "show_advanced_options": True}, - data=config_data, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "ssh_not_file"} + with patch( + "homeassistant.components.asuswrt.config_flow.os.path.isfile", + return_value=False, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER, "show_advanced_options": True}, + data=config_data, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["errors"] == {"base": "ssh_not_file"} async def test_error_invalid_host(hass): @@ -137,60 +156,96 @@ async def test_error_invalid_host(hass): assert result["errors"] == {"base": "invalid_host"} -async def test_abort_if_already_setup(hass): - """Test we abort if component is already setup.""" +async def test_abort_if_not_unique_id_setup(hass): + """Test we abort if component without uniqueid is already setup.""" MockConfigEntry( domain=DOMAIN, data=CONFIG_DATA, ).add_to_hass(hass) - with patch( - "homeassistant.components.asuswrt.config_flow.socket.gethostbyname", - return_value=IP_ADDRESS, - ): - # Should fail, same HOST (flow) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + data=CONFIG_DATA, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "not_unique_id_exist" + + +@pytest.mark.usefixtures("connect") +async def test_update_uniqueid_exist(hass, mock_unique_id): + """Test we update entry if uniqueid is already configured.""" + mock_unique_id.update({"label_mac": MAC_ADDR}) + existing_entry = MockConfigEntry( + domain=DOMAIN, + data={**CONFIG_DATA, CONF_HOST: "10.10.10.10"}, + unique_id=MAC_ADDR, + ) + existing_entry.add_to_hass(hass) + + # test with all provided + with PATCH_GET_HOST, PATCH_SETUP_ENTRY: + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER, "show_advanced_options": True}, + data=CONFIG_DATA, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == HOST + assert result["data"] == CONFIG_DATA + prev_entry = hass.config_entries.async_get_entry(existing_entry.entry_id) + assert not prev_entry + + +@pytest.mark.usefixtures("connect") +async def test_abort_invalid_unique_id(hass): + """Test we abort if uniqueid not available.""" + MockConfigEntry( + domain=DOMAIN, + data=CONFIG_DATA, + unique_id=MAC_ADDR, + ).add_to_hass(hass) + + with PATCH_GET_HOST: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=CONFIG_DATA, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "single_instance_allowed" + assert result["reason"] == "invalid_unique_id" -async def test_on_connect_failed(hass): +@pytest.mark.parametrize( + ["side_effect", "error"], + [ + (OSError, "cannot_connect"), + (TypeError, "unknown"), + (None, "cannot_connect"), + ], +) +async def test_on_connect_failed(hass, side_effect, error): """Test when we have errors connecting the router.""" flow_result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER, "show_advanced_options": True}, ) - with patch("homeassistant.components.asuswrt.router.AsusWrt") as asus_wrt: - asus_wrt.return_value.connection.async_connect = AsyncMock() - asus_wrt.return_value.is_connected = False - result = await hass.config_entries.flow.async_configure( - flow_result["flow_id"], user_input=CONFIG_DATA - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "cannot_connect"} - - with patch("homeassistant.components.asuswrt.router.AsusWrt") as asus_wrt: - asus_wrt.return_value.connection.async_connect = AsyncMock(side_effect=OSError) - result = await hass.config_entries.flow.async_configure( - flow_result["flow_id"], user_input=CONFIG_DATA - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "cannot_connect"} - - with patch("homeassistant.components.asuswrt.router.AsusWrt") as asus_wrt: + with PATCH_GET_HOST, patch( + "homeassistant.components.asuswrt.router.AsusWrt" + ) as asus_wrt: asus_wrt.return_value.connection.async_connect = AsyncMock( - side_effect=TypeError + side_effect=side_effect ) + asus_wrt.return_value.is_connected = False + result = await hass.config_entries.flow.async_configure( flow_result["flow_id"], user_input=CONFIG_DATA ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["errors"] == {"base": "unknown"} + assert result["errors"] == {"base": error} async def test_options_flow(hass): @@ -202,7 +257,7 @@ async def test_options_flow(hass): ) config_entry.add_to_hass(hass) - with patch("homeassistant.components.asuswrt.async_setup_entry", return_value=True): + with PATCH_SETUP_ENTRY: await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() result = await hass.config_entries.options.async_init(config_entry.entry_id) diff --git a/tests/components/asuswrt/test_sensor.py b/tests/components/asuswrt/test_sensor.py index 3e0c01071c1..483592302bf 100644 --- a/tests/components/asuswrt/test_sensor.py +++ b/tests/components/asuswrt/test_sensor.py @@ -7,7 +7,7 @@ import pytest from homeassistant.components import device_tracker, sensor from homeassistant.components.asuswrt.const import CONF_INTERFACE, DOMAIN -from homeassistant.components.asuswrt.sensor import DEFAULT_PREFIX +from homeassistant.components.asuswrt.router import DEFAULT_NAME from homeassistant.components.device_tracker.const import CONF_CONSIDER_HOME from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( @@ -39,6 +39,8 @@ CONFIG_DATA = { CONF_MODE: "router", } +MAC_ADDR = "a1:b2:c3:d4:e5:f6" + MOCK_BYTES_TOTAL = [60000000000, 50000000000] MOCK_CURRENT_TRANSFER_RATES = [20000000, 10000000] MOCK_LOAD_AVG = [1.1, 1.2, 1.3] @@ -157,7 +159,7 @@ def mock_controller_connect_sens_fail(): yield service_mock -def _setup_entry(hass): +def _setup_entry(hass, unique_id=None): """Create mock config entry.""" entity_reg = er.async_get(hass) @@ -166,11 +168,11 @@ def _setup_entry(hass): domain=DOMAIN, data=CONFIG_DATA, options={CONF_CONSIDER_HOME: 60}, + unique_id=unique_id, ) # init variable - unique_id = DOMAIN - obj_prefix = slugify(DEFAULT_PREFIX) + obj_prefix = slugify(HOST if unique_id else DEFAULT_NAME) sensor_prefix = f"{sensor.DOMAIN}.{obj_prefix}" # Pre-enable the status sensor @@ -179,7 +181,7 @@ def _setup_entry(hass): entity_reg.async_get_or_create( sensor.DOMAIN, DOMAIN, - f"{unique_id} {DEFAULT_PREFIX} {sensor_name}", + f"{DOMAIN} {unique_id or DEFAULT_NAME} {sensor_name}", suggested_object_id=f"{obj_prefix}_{sensor_id}", disabled_by=None, ) @@ -202,15 +204,20 @@ def _setup_entry(hass): return config_entry, sensor_prefix +@pytest.mark.parametrize( + "entry_unique_id", + [None, MAC_ADDR], +) async def test_sensors( hass, connect, mock_devices, mock_available_temps, create_device_registry_devices, + entry_unique_id, ): """Test creating an AsusWRT sensor.""" - config_entry, sensor_prefix = _setup_entry(hass) + config_entry, sensor_prefix = _setup_entry(hass, entry_unique_id) config_entry.add_to_hass(hass) # initial devices setup From 6e675d6f078088befb2c954d2b9dbd08d78d3119 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 3 May 2022 14:09:35 -0700 Subject: [PATCH 195/930] Fix homekit tests in beta (#71268) --- tests/components/homekit/test_accessories.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/components/homekit/test_accessories.py b/tests/components/homekit/test_accessories.py index 704bb368d64..6d7de6eb696 100644 --- a/tests/components/homekit/test_accessories.py +++ b/tests/components/homekit/test_accessories.py @@ -30,6 +30,7 @@ from homeassistant.components.homekit.const import ( MANUFACTURER, SERV_ACCESSORY_INFO, ) +from homeassistant.components.homekit.util import format_version from homeassistant.const import ( ATTR_BATTERY_CHARGING, ATTR_BATTERY_LEVEL, @@ -165,7 +166,7 @@ async def test_home_accessory(hass, hk_driver): serv.get_characteristic(CHAR_SERIAL_NUMBER).value == "light.accessory_that_exceeds_the_maximum_maximum_maximum_maximum" ) - assert hass_version.startswith( + assert format_version(hass_version).startswith( serv.get_characteristic(CHAR_FIRMWARE_REVISION).value ) @@ -217,7 +218,7 @@ async def test_accessory_with_missing_basic_service_info(hass, hk_driver): assert serv.get_characteristic(CHAR_MANUFACTURER).value == "Home Assistant Sensor" assert serv.get_characteristic(CHAR_MODEL).value == "Sensor" assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == entity_id - assert hass_version.startswith( + assert format_version(hass_version).startswith( serv.get_characteristic(CHAR_FIRMWARE_REVISION).value ) assert isinstance(acc.to_HAP(), dict) @@ -247,7 +248,7 @@ async def test_accessory_with_hardware_revision(hass, hk_driver): assert serv.get_characteristic(CHAR_MANUFACTURER).value == "Home Assistant Sensor" assert serv.get_characteristic(CHAR_MODEL).value == "Sensor" assert serv.get_characteristic(CHAR_SERIAL_NUMBER).value == entity_id - assert hass_version.startswith( + assert format_version(hass_version).startswith( serv.get_characteristic(CHAR_FIRMWARE_REVISION).value ) assert serv.get_characteristic(CHAR_HARDWARE_REVISION).value == "1.2.3" @@ -692,7 +693,7 @@ def test_home_bridge(hk_driver): serv = bridge.services[0] # SERV_ACCESSORY_INFO assert serv.display_name == SERV_ACCESSORY_INFO assert serv.get_characteristic(CHAR_NAME).value == BRIDGE_NAME - assert hass_version.startswith( + assert format_version(hass_version).startswith( serv.get_characteristic(CHAR_FIRMWARE_REVISION).value ) assert serv.get_characteristic(CHAR_MANUFACTURER).value == MANUFACTURER From 509dd657b8c452060bcf70b23996c6df61c3f2d1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 4 May 2022 00:22:09 +0000 Subject: [PATCH 196/930] [ci skip] Translation update --- .../components/asuswrt/translations/en.json | 3 ++- .../components/asuswrt/translations/fr.json | 2 ++ .../asuswrt/translations/pt-BR.json | 2 ++ .../components/hue/translations/bg.json | 1 + .../components/insteon/translations/bg.json | 4 +++ .../intellifire/translations/bg.json | 14 ++++++++-- .../components/meater/translations/bg.json | 5 ++++ .../components/meater/translations/ca.json | 9 +++++++ .../components/meater/translations/et.json | 9 +++++++ .../components/meater/translations/it.json | 6 +++++ .../components/meater/translations/no.json | 9 +++++++ .../components/qnap_qsw/translations/bg.json | 20 ++++++++++++++ .../components/roon/translations/bg.json | 6 +++++ .../components/senz/translations/bg.json | 3 ++- .../simplisafe/translations/ca.json | 2 +- .../simplisafe/translations/et.json | 2 +- .../components/sql/translations/bg.json | 17 ++++++++++-- .../steam_online/translations/bg.json | 23 ++++++++++++++++ .../components/tautulli/translations/bg.json | 26 +++++++++++++++++++ .../trafikverket_ferry/translations/bg.json | 25 ++++++++++++++++++ .../components/unifi/translations/bg.json | 3 +++ .../components/unifi/translations/ca.json | 5 +++- .../components/unifi/translations/et.json | 5 +++- 23 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 homeassistant/components/qnap_qsw/translations/bg.json create mode 100644 homeassistant/components/steam_online/translations/bg.json create mode 100644 homeassistant/components/tautulli/translations/bg.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/bg.json diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index 494156eae45..6d8dc09aec7 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -2,7 +2,8 @@ "config": { "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible" + "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible", + "single_instance_allowed": "Already configured. Only a single configuration possible." }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/asuswrt/translations/fr.json b/homeassistant/components/asuswrt/translations/fr.json index 5c8882d5813..69a7705789e 100644 --- a/homeassistant/components/asuswrt/translations/fr.json +++ b/homeassistant/components/asuswrt/translations/fr.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Impossible de d\u00e9terminer un identifiant unique valide pour l'appareil", + "not_unique_id_exist": "Un appareil sans UniqueID valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/pt-BR.json b/homeassistant/components/asuswrt/translations/pt-BR.json index 88999128895..74e48b53ba9 100644 --- a/homeassistant/components/asuswrt/translations/pt-BR.json +++ b/homeassistant/components/asuswrt/translations/pt-BR.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Imposs\u00edvel determinar um ID exclusivo v\u00e1lido para o dispositivo", + "not_unique_id_exist": "Um dispositivo sem um ID exclusivo v\u00e1lido j\u00e1 est\u00e1 configurado. A configura\u00e7\u00e3o de v\u00e1rias inst\u00e2ncias n\u00e3o \u00e9 poss\u00edvel", "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "error": { diff --git a/homeassistant/components/hue/translations/bg.json b/homeassistant/components/hue/translations/bg.json index babf0089389..68da5279508 100644 --- a/homeassistant/components/hue/translations/bg.json +++ b/homeassistant/components/hue/translations/bg.json @@ -6,6 +6,7 @@ "already_in_progress": "\u0412 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f.", "cannot_connect": "\u041d\u0435 \u043c\u043e\u0436\u0430 \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0436\u0435 \u0441 \u0431\u0430\u0437\u043e\u0432\u0430\u0442\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f", "discover_timeout": "\u041d\u0435\u0432\u044a\u0437\u043c\u043e\u0436\u043d\u043e \u0435 \u0434\u0430 \u0431\u044a\u0434\u0430\u0442 \u043e\u0442\u043a\u0440\u0438\u0442\u0438 \u0431\u0430\u0437\u043e\u0432\u0438 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 Philips Hue", + "invalid_host": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u0445\u043e\u0441\u0442", "no_bridges": "\u041d\u0435 \u0441\u0430 \u043e\u0442\u043a\u0440\u0438\u0442\u0438 \u0431\u0430\u0437\u043e\u0432\u0438 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 Philips Hue", "not_hue_bridge": "\u041d\u0435 \u0435 Hue \u0431\u0430\u0437\u043e\u0432\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f", "unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" diff --git a/homeassistant/components/insteon/translations/bg.json b/homeassistant/components/insteon/translations/bg.json index 65bf4bad59a..e1adb8657da 100644 --- a/homeassistant/components/insteon/translations/bg.json +++ b/homeassistant/components/insteon/translations/bg.json @@ -8,7 +8,11 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", "select_single": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0435\u0434\u043d\u0430 \u043e\u043f\u0446\u0438\u044f." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name}?" + }, "hubv1": { "data": { "host": "IP \u0430\u0434\u0440\u0435\u0441", diff --git a/homeassistant/components/intellifire/translations/bg.json b/homeassistant/components/intellifire/translations/bg.json index 1a8dd460097..9926e7bb6a6 100644 --- a/homeassistant/components/intellifire/translations/bg.json +++ b/homeassistant/components/intellifire/translations/bg.json @@ -1,14 +1,23 @@ { "config": { "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { + "api_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u043b\u0438\u0437\u0430\u043d\u0435", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "iftapi_connect": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u0441 iftapi.net", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "flow_title": "{serial} ({host})", "step": { + "api_config": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "username": "Email" + } + }, "dhcp_confirm": { "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {host}\n\u0421\u0435\u0440\u0438\u0435\u043d: {serial}?" }, @@ -21,7 +30,8 @@ "pick_device": { "data": { "host": "\u0425\u043e\u0441\u0442" - } + }, + "title": "\u0418\u0437\u0431\u043e\u0440 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" }, "user": { "data": { diff --git a/homeassistant/components/meater/translations/bg.json b/homeassistant/components/meater/translations/bg.json index e5396fbc999..cb1a84abf51 100644 --- a/homeassistant/components/meater/translations/bg.json +++ b/homeassistant/components/meater/translations/bg.json @@ -5,6 +5,11 @@ "unknown_auth_error": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + } + }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430", diff --git a/homeassistant/components/meater/translations/ca.json b/homeassistant/components/meater/translations/ca.json index 0174767bc52..cd2c626ef8a 100644 --- a/homeassistant/components/meater/translations/ca.json +++ b/homeassistant/components/meater/translations/ca.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Error inesperat" }, "step": { + "reauth_confirm": { + "data": { + "password": "Contrasenya" + }, + "description": "Confirma la contrasenya del compte de Meater Cloud {username}." + }, "user": { "data": { "password": "Contrasenya", "username": "Nom d'usuari" }, + "data_description": { + "username": "Nom d'usuari de Meater Cloud, normalment una adre\u00e7a de correu electr\u00f2nic." + }, "description": "Configura el teu compte de Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/et.json b/homeassistant/components/meater/translations/et.json index 55328467d3a..dc5f6f9102f 100644 --- a/homeassistant/components/meater/translations/et.json +++ b/homeassistant/components/meater/translations/et.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Ootamatu t\u00f5rge" }, "step": { + "reauth_confirm": { + "data": { + "password": "Salas\u00f5na" + }, + "description": "Kinnita Meater Cloudi konto {username} salas\u00f5na." + }, "user": { "data": { "password": "Salas\u00f5na", "username": "Kasutajanimi" }, + "data_description": { + "username": "Meater Cloudi kasutajanimi, tavaliselt e-posti aadress." + }, "description": "Seadista Meater Cloudi konto." } } diff --git a/homeassistant/components/meater/translations/it.json b/homeassistant/components/meater/translations/it.json index fe3bc189ef6..090de20f280 100644 --- a/homeassistant/components/meater/translations/it.json +++ b/homeassistant/components/meater/translations/it.json @@ -6,11 +6,17 @@ "unknown_auth_error": "Errore imprevisto" }, "step": { + "reauth_confirm": { + "description": "Conferma la password per l'account Meater Cloud {username}." + }, "user": { "data": { "password": "Password", "username": "Nome utente" }, + "data_description": { + "username": "Nome utente di Meater Cloud, tipicamente un indirizzo email" + }, "description": "Configura il tuo account Meater Cloud." } } diff --git a/homeassistant/components/meater/translations/no.json b/homeassistant/components/meater/translations/no.json index 60bec4e6864..f97f8ce5f8f 100644 --- a/homeassistant/components/meater/translations/no.json +++ b/homeassistant/components/meater/translations/no.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Uventet feil" }, "step": { + "reauth_confirm": { + "data": { + "password": "Passord" + }, + "description": "Bekreft passordet for Meater Cloud-kontoen {username} ." + }, "user": { "data": { "password": "Passord", "username": "Brukernavn" }, + "data_description": { + "username": "Meater Cloud brukernavn, vanligvis en e-postadresse." + }, "description": "Sett opp din Meater Cloud-konto." } } diff --git a/homeassistant/components/qnap_qsw/translations/bg.json b/homeassistant/components/qnap_qsw/translations/bg.json new file mode 100644 index 00000000000..33ce2a4028f --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/bg.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "url": "URL", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/roon/translations/bg.json b/homeassistant/components/roon/translations/bg.json index 36b9a0b4bff..5738012f715 100644 --- a/homeassistant/components/roon/translations/bg.json +++ b/homeassistant/components/roon/translations/bg.json @@ -8,6 +8,12 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { + "fallback": { + "data": { + "host": "\u0425\u043e\u0441\u0442", + "port": "\u041f\u043e\u0440\u0442" + } + }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" diff --git a/homeassistant/components/senz/translations/bg.json b/homeassistant/components/senz/translations/bg.json index ddd6040e9cf..916271d14d0 100644 --- a/homeassistant/components/senz/translations/bg.json +++ b/homeassistant/components/senz/translations/bg.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430." } } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index 827bd6dceec..82ac0eabbff 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -14,7 +14,7 @@ "unknown": "Error inesperat" }, "progress": { - "email_2fa": "Introdueix el codi d'autenticaci\u00f3 de dos factors que s'ha enviat al teu correu." + "email_2fa": "Mira el correu electr\u00f2nic on hauries de trobar l'enlla\u00e7 de verificaci\u00f3 de Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index 8931c2250ec..75c908e60c0 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -14,7 +14,7 @@ "unknown": "Tundmatu viga" }, "progress": { - "email_2fa": "Sisesta e-kirjaga saadetud kahefaktoriline autentimiskood." + "email_2fa": "Kontrolli oma meili Simplisafe'i kinnituslingi saamiseks." }, "step": { "mfa": { diff --git a/homeassistant/components/sql/translations/bg.json b/homeassistant/components/sql/translations/bg.json index b6c3577d1d0..5d2b776b0da 100644 --- a/homeassistant/components/sql/translations/bg.json +++ b/homeassistant/components/sql/translations/bg.json @@ -1,9 +1,17 @@ { "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" + }, "step": { "user": { "data": { - "name": "\u0418\u043c\u0435" + "column": "\u041a\u043e\u043b\u043e\u043d\u0430", + "name": "\u0418\u043c\u0435", + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430" + }, + "data_description": { + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430 (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)" } } } @@ -12,7 +20,12 @@ "step": { "init": { "data": { - "name": "\u0418\u043c\u0435" + "column": "\u041a\u043e\u043b\u043e\u043d\u0430", + "name": "\u0418\u043c\u0435", + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430" + }, + "data_description": { + "unit_of_measurement": "\u041c\u0435\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430 (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)" } } } diff --git a/homeassistant/components/steam_online/translations/bg.json b/homeassistant/components/steam_online/translations/bg.json new file mode 100644 index 00000000000..66ae6cc081f --- /dev/null +++ b/homeassistant/components/steam_online/translations/bg.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "reauth_confirm": { + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/bg.json b/homeassistant/components/tautulli/translations/bg.json new file mode 100644 index 00000000000..fb4e836f98d --- /dev/null +++ b/homeassistant/components/tautulli/translations/bg.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", + "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/bg.json b/homeassistant/components/trafikverket_ferry/translations/bg.json new file mode 100644 index 00000000000..4e93f090b87 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/bg.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "weekday": "\u0420\u0430\u0431\u043e\u0442\u043d\u0438 \u0434\u043d\u0438" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/bg.json b/homeassistant/components/unifi/translations/bg.json index afe2db0cc5b..c1bf205abfe 100644 --- a/homeassistant/components/unifi/translations/bg.json +++ b/homeassistant/components/unifi/translations/bg.json @@ -22,6 +22,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043d\u0435 \u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430" + }, "step": { "device_tracker": { "data": { diff --git a/homeassistant/components/unifi/translations/ca.json b/homeassistant/components/unifi/translations/ca.json index 4bb01d82ef6..74129c3c2af 100644 --- a/homeassistant/components/unifi/translations/ca.json +++ b/homeassistant/components/unifi/translations/ca.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El lloc web d'UniFi Network ja est\u00e0 configurat", - "configuration_updated": "S'ha actualitzat la configuraci\u00f3.", + "configuration_updated": "S'ha actualitzat la configuraci\u00f3", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "La integraci\u00f3 UniFi no est\u00e0 configurada" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifi/translations/et.json b/homeassistant/components/unifi/translations/et.json index 76d93c95d97..6db9276562e 100644 --- a/homeassistant/components/unifi/translations/et.json +++ b/homeassistant/components/unifi/translations/et.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi Network on juba seadistatud", - "configuration_updated": "Seaded on v\u00e4rskendatud.", + "configuration_updated": "Seaded on v\u00e4rskendatud", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi sidumine pole seadistatud" + }, "step": { "client_control": { "data": { From c949f010c129bcad73cfa1f02646039ebb733ce1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 20:43:58 -0500 Subject: [PATCH 197/930] Set entity category for isy auxiliary sensors (#71266) --- homeassistant/components/isy994/sensor.py | 76 ++++++++++++++++++----- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 2dee990c249..b02050f6f7b 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -3,7 +3,20 @@ from __future__ import annotations from typing import Any, cast -from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN +from pyisy.constants import ( + COMMAND_FRIENDLY_NAME, + ISY_VALUE_UNKNOWN, + PROP_BATTERY_LEVEL, + PROP_BUSY, + PROP_COMMS_ERROR, + PROP_ENERGY_MODE, + PROP_HEAT_COOL_STATE, + PROP_HUMIDITY, + PROP_ON_LEVEL, + PROP_RAMP_RATE, + PROP_STATUS, + PROP_TEMPERATURE, +) from pyisy.helpers import NodeProperty from pyisy.nodes import Node @@ -11,10 +24,12 @@ from homeassistant.components.sensor import ( DOMAIN as SENSOR, SensorDeviceClass, SensorEntity, + SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( @@ -33,18 +48,34 @@ from .entity import ISYEntity, ISYNodeEntity from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids # Disable general purpose and redundant sensors by default -AUX_DISABLED_BY_DEFAULT = ["ERR", "GV", "CLIEMD", "CLIHCS", "DO", "OL", "RR", "ST"] +AUX_DISABLED_BY_DEFAULT_MATCH = ["GV", "DO"] +AUX_DISABLED_BY_DEFAULT_EXACT = { + PROP_ENERGY_MODE, + PROP_HEAT_COOL_STATE, + PROP_ON_LEVEL, + PROP_RAMP_RATE, + PROP_STATUS, +} +SKIP_AUX_PROPERTIES = {PROP_BUSY, PROP_COMMS_ERROR, PROP_STATUS} ISY_CONTROL_TO_DEVICE_CLASS = { + PROP_BATTERY_LEVEL: SensorDeviceClass.BATTERY, + PROP_HUMIDITY: SensorDeviceClass.HUMIDITY, + PROP_TEMPERATURE: SensorDeviceClass.TEMPERATURE, "BARPRES": SensorDeviceClass.PRESSURE, - "BATLVL": SensorDeviceClass.BATTERY, - "CLIHUM": SensorDeviceClass.HUMIDITY, - "CLITEMP": SensorDeviceClass.TEMPERATURE, "CO2LVL": SensorDeviceClass.CO2, "CV": SensorDeviceClass.VOLTAGE, "LUMIN": SensorDeviceClass.ILLUMINANCE, "PF": SensorDeviceClass.POWER_FACTOR, } +ISY_CONTROL_TO_STATE_CLASS = { + control: SensorStateClass.MEASUREMENT for control in ISY_CONTROL_TO_DEVICE_CLASS +} +ISY_CONTROL_TO_ENTITY_CATEGORY = { + PROP_RAMP_RATE: EntityCategory.CONFIG, + PROP_ON_LEVEL: EntityCategory.CONFIG, + PROP_COMMS_ERROR: EntityCategory.DIAGNOSTIC, +} async def async_setup_entry( @@ -58,13 +89,21 @@ async def async_setup_entry( _LOGGER.debug("Loading %s", node.name) entities.append(ISYSensorEntity(node)) + aux_nodes = set() for node, control in hass_isy_data[ISY994_NODES][SENSOR_AUX]: + aux_nodes.add(node) + if control in SKIP_AUX_PROPERTIES: + continue _LOGGER.debug("Loading %s %s", node.name, node.aux_properties[control]) - enabled_default = not any( - control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT + enabled_default = control not in AUX_DISABLED_BY_DEFAULT_EXACT and not any( + control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT_MATCH ) entities.append(ISYAuxSensorEntity(node, control, enabled_default)) + for node in aux_nodes: + # Any node in SENSOR_AUX can potentially have communication errors + entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False)) + for vname, vobj in hass_isy_data[ISY994_VARIABLES]: entities.append(ISYSensorVariableEntity(vname, vobj)) @@ -76,7 +115,7 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): """Representation of an ISY994 sensor device.""" @property - def target(self) -> Node | NodeProperty: + def target(self) -> Node | NodeProperty | None: """Return target for the sensor.""" return self._node @@ -88,6 +127,9 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): @property def raw_unit_of_measurement(self) -> dict | str | None: """Get the raw unit of measurement for the ISY994 sensor device.""" + if self.target is None: + return None + uom = self.target.uom # Backwards compatibility for ISYv4 Firmware: @@ -107,6 +149,9 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): @property def native_value(self) -> float | int | str | None: """Get the state of the ISY994 sensor device.""" + if self.target is None: + return None + if (value := self.target_value) == ISY_VALUE_UNKNOWN: return None @@ -157,21 +202,22 @@ class ISYAuxSensorEntity(ISYSensorEntity): super().__init__(node) self._control = control self._attr_entity_registry_enabled_default = enabled_default + self._attr_entity_category = ISY_CONTROL_TO_ENTITY_CATEGORY.get(control) + self._attr_device_class = ISY_CONTROL_TO_DEVICE_CLASS.get(control) + self._attr_state_class = ISY_CONTROL_TO_STATE_CLASS.get(control) @property - def device_class(self) -> SensorDeviceClass | str | None: - """Return the device class for the sensor.""" - return ISY_CONTROL_TO_DEVICE_CLASS.get(self._control, super().device_class) - - @property - def target(self) -> Node | NodeProperty: + def target(self) -> Node | NodeProperty | None: """Return target for the sensor.""" + if self._control not in self._node.aux_properties: + # Property not yet set (i.e. no errors) + return None return cast(NodeProperty, self._node.aux_properties[self._control]) @property def target_value(self) -> Any: """Return the target value.""" - return self.target.value + return None if self.target is None else self.target.value @property def unique_id(self) -> str | None: From 4408ad82eb26f0b64b775d9ab60c00fd53820067 Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Wed, 4 May 2022 06:14:56 +0200 Subject: [PATCH 198/930] Update xknx to 0.21.2 (#71271) --- homeassistant/components/knx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index a000261ec3a..00b4c6cdc5f 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.21.1"], + "requirements": ["xknx==0.21.2"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 438c951d214..8360f4af154 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2448,7 +2448,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.21.1 +xknx==0.21.2 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b012f795c9e..4ca5f854780 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1600,7 +1600,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.21.1 +xknx==0.21.2 # homeassistant.components.bluesound # homeassistant.components.fritz From 2c1d2c323db72d623b58ccbddb54f525f5c627ba Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Wed, 4 May 2022 09:22:30 +0200 Subject: [PATCH 199/930] Bump pynetgear to 0.10.0 (#71251) --- homeassistant/components/netgear/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/netgear/manifest.json b/homeassistant/components/netgear/manifest.json index a5374f4c315..ae0824c82a5 100644 --- a/homeassistant/components/netgear/manifest.json +++ b/homeassistant/components/netgear/manifest.json @@ -2,7 +2,7 @@ "domain": "netgear", "name": "NETGEAR", "documentation": "https://www.home-assistant.io/integrations/netgear", - "requirements": ["pynetgear==0.9.4"], + "requirements": ["pynetgear==0.10.0"], "codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"], "iot_class": "local_polling", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 8360f4af154..998f1c8791f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1667,7 +1667,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.9.4 +pynetgear==0.10.0 # homeassistant.components.netio pynetio==0.1.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4ca5f854780..53981018ac1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1119,7 +1119,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.9.4 +pynetgear==0.10.0 # homeassistant.components.nina pynina==0.1.8 From 9e2f0b3af1545ede2e93dca284e8c0635361535b Mon Sep 17 00:00:00 2001 From: Tomasz Date: Wed, 4 May 2022 10:17:43 +0200 Subject: [PATCH 200/930] Rename UniqueID to unique id in asuswrt (#71279) * Rename UniqueID to unique id * Update en.json --- homeassistant/components/asuswrt/strings.json | 2 +- homeassistant/components/asuswrt/translations/en.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/asuswrt/strings.json b/homeassistant/components/asuswrt/strings.json index 9023f5aa604..73887fcc4af 100644 --- a/homeassistant/components/asuswrt/strings.json +++ b/homeassistant/components/asuswrt/strings.json @@ -26,7 +26,7 @@ }, "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible" + "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" } }, "options": { diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index 6d8dc09aec7..4d4ea4d0a30 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -2,7 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "not_unique_id_exist": "A device without a valid UniqueID is already configured. Configuration of multiple instance is not possible", + "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", "single_instance_allowed": "Already configured. Only a single configuration possible." }, "error": { @@ -44,4 +44,4 @@ } } } -} \ No newline at end of file +} From ee8eac10c9d8d890acf32c9b02992df5833cfb07 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Wed, 4 May 2022 11:39:55 +0200 Subject: [PATCH 201/930] Address late review of AsusWRT unique id PR (#71281) --- homeassistant/components/asuswrt/config_flow.py | 6 +++--- homeassistant/components/asuswrt/strings.json | 2 +- homeassistant/components/asuswrt/translations/en.json | 5 ++--- tests/components/asuswrt/test_config_flow.py | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/asuswrt/config_flow.py b/homeassistant/components/asuswrt/config_flow.py index e1b34cca15b..ec5cccb9a71 100644 --- a/homeassistant/components/asuswrt/config_flow.py +++ b/homeassistant/components/asuswrt/config_flow.py @@ -149,10 +149,10 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle a flow initiated by the user.""" - # if exist one entry without unique ID, we abort config flow + # if there's one entry without unique ID, we abort config flow for unique_id in self._async_current_ids(): if unique_id is None: - return self.async_abort(reason="not_unique_id_exist") + return self.async_abort(reason="no_unique_id") if user_input is None: return self._show_setup_form(user_input) @@ -188,7 +188,7 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN): return self.async_abort(reason="invalid_unique_id") else: _LOGGER.warning( - "This device do not provide a valid Unique ID." + "This device does not provide a valid Unique ID." " Configuration of multiple instance will not be possible" ) diff --git a/homeassistant/components/asuswrt/strings.json b/homeassistant/components/asuswrt/strings.json index 73887fcc4af..bd0c706e74a 100644 --- a/homeassistant/components/asuswrt/strings.json +++ b/homeassistant/components/asuswrt/strings.json @@ -26,7 +26,7 @@ }, "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" + "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" } }, "options": { diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index 4d4ea4d0a30..f93eae80e6d 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -2,8 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", - "single_instance_allowed": "Already configured. Only a single configuration possible." + "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" }, "error": { "cannot_connect": "Failed to connect", @@ -44,4 +43,4 @@ } } } -} +} \ No newline at end of file diff --git a/tests/components/asuswrt/test_config_flow.py b/tests/components/asuswrt/test_config_flow.py index 09c484ff69d..8475bd48f9a 100644 --- a/tests/components/asuswrt/test_config_flow.py +++ b/tests/components/asuswrt/test_config_flow.py @@ -169,7 +169,7 @@ async def test_abort_if_not_unique_id_setup(hass): data=CONFIG_DATA, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "not_unique_id_exist" + assert result["reason"] == "no_unique_id" @pytest.mark.usefixtures("connect") From 6eef3c16f2ed74e3ec1f89a51d88a1ba5c01c6ff Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 May 2022 12:14:24 +0200 Subject: [PATCH 202/930] Update pylint to 2.13.8 (#71280) --- homeassistant/components/buienradar/util.py | 4 ++-- homeassistant/components/doorbird/__init__.py | 2 +- homeassistant/components/owntracks/messages.py | 2 +- homeassistant/components/smtp/notify.py | 2 +- homeassistant/components/template/cover.py | 2 +- homeassistant/components/vasttrafik/sensor.py | 2 +- homeassistant/components/zwave_js/services.py | 1 - requirements_test.txt | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/buienradar/util.py b/homeassistant/components/buienradar/util.py index 3686e2bd3c9..06cd1b32cf5 100644 --- a/homeassistant/components/buienradar/util.py +++ b/homeassistant/components/buienradar/util.py @@ -115,7 +115,7 @@ class BrData: self.load_error_count += 1 threshold_log( self.load_error_count, - "Unable to retrieve json data from Buienradar" "(Msg: %s, status: %s,)", + "Unable to retrieve json data from Buienradar (Msg: %s, status: %s)", content.get(MESSAGE), content.get(STATUS_CODE), ) @@ -135,7 +135,7 @@ class BrData: # unable to get the data threshold_log( self.rain_error_count, - "Unable to retrieve rain data from Buienradar" "(Msg: %s, status: %s)", + "Unable to retrieve rain data from Buienradar (Msg: %s, status: %s)", raincontent.get(MESSAGE), raincontent.get(STATUS_CODE), ) diff --git a/homeassistant/components/doorbird/__init__.py b/homeassistant/components/doorbird/__init__.py index 8a2c06c7157..133c7612a89 100644 --- a/homeassistant/components/doorbird/__init__.py +++ b/homeassistant/components/doorbird/__init__.py @@ -287,7 +287,7 @@ class ConfiguredDoorBird: self.device.change_favorite("http", f"Home Assistant ({event})", url) if not self.webhook_is_registered(url): _LOGGER.warning( - 'Unable to set favorite URL "%s". ' 'Event "%s" will not fire', + 'Unable to set favorite URL "%s". Event "%s" will not fire', url, event, ) diff --git a/homeassistant/components/owntracks/messages.py b/homeassistant/components/owntracks/messages.py index b85a37dadf9..4f17c8a5375 100644 --- a/homeassistant/components/owntracks/messages.py +++ b/homeassistant/components/owntracks/messages.py @@ -159,7 +159,7 @@ def encrypt_message(secret, topic, message): if key is None: _LOGGER.warning( - "Unable to encrypt payload because no decryption key known " "for topic %s", + "Unable to encrypt payload because no decryption key known for topic %s", topic, ) return None diff --git a/homeassistant/components/smtp/notify.py b/homeassistant/components/smtp/notify.py index ef743912bc9..7b8e2dad1ed 100644 --- a/homeassistant/components/smtp/notify.py +++ b/homeassistant/components/smtp/notify.py @@ -235,7 +235,7 @@ def _attach_file(atch_name, content_id): attachment = MIMEImage(file_bytes) except TypeError: _LOGGER.warning( - "Attachment %s has an unknown MIME type. " "Falling back to file", + "Attachment %s has an unknown MIME type. Falling back to file", atch_name, ) attachment = MIMEApplication(file_bytes, Name=atch_name) diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index e1df61bf4a2..7a20398d0b7 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -242,7 +242,7 @@ class CoverTemplate(TemplateEntity, CoverEntity): if state < 0 or state > 100: self._position = None _LOGGER.error( - "Cover position value must be" " between 0 and 100." " Value was: %.2f", + "Cover position value must be between 0 and 100. Value was: %.2f", state, ) else: diff --git a/homeassistant/components/vasttrafik/sensor.py b/homeassistant/components/vasttrafik/sensor.py index 5e446106312..bec7959f9c8 100644 --- a/homeassistant/components/vasttrafik/sensor.py +++ b/homeassistant/components/vasttrafik/sensor.py @@ -139,7 +139,7 @@ class VasttrafikDepartureSensor(SensorEntity): if not self._departureboard: _LOGGER.debug( - "No departures from departure station %s " "to destination station %s", + "No departures from departure station %s to destination station %s", self._departure["station_name"], self._heading["station_name"] if self._heading else "ANY", ) diff --git a/homeassistant/components/zwave_js/services.py b/homeassistant/components/zwave_js/services.py index 70099859d39..8577e48e7a3 100644 --- a/homeassistant/components/zwave_js/services.py +++ b/homeassistant/components/zwave_js/services.py @@ -540,7 +540,6 @@ class ZWaveServices: async def async_ping(self, service: ServiceCall) -> None: """Ping node(s).""" - # pylint: disable=no-self-use _LOGGER.warning( "This service is deprecated in favor of the ping button entity. Service " "calls will still work for now but the service will be removed in a " diff --git a/requirements_test.txt b/requirements_test.txt index 744ca7bebcf..0ef6063a1ca 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -13,7 +13,7 @@ freezegun==1.2.1 mock-open==1.4.0 mypy==0.950 pre-commit==2.17.0 -pylint==2.13.7 +pylint==2.13.8 pipdeptree==2.2.1 pylint-strict-informational==0.1 pytest-aiohttp==0.3.0 From 309d8d70b1d431078d4e1ef03d7deeadc96be90e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 4 May 2022 14:19:16 +0200 Subject: [PATCH 203/930] Update frontend to 20220504.0 (#71284) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 89dd75fa96c..b51219c4f19 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220503.0"], + "requirements": ["home-assistant-frontend==20220504.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index fcb63a9426c..a3dec49ddb9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220503.0 +home-assistant-frontend==20220504.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index 998f1c8791f..ce4a5971ecd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220503.0 +home-assistant-frontend==20220504.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 53981018ac1..3772fd7b38a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220503.0 +home-assistant-frontend==20220504.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 03ab9d07a836af6dc3af3ccb745bfc9a025a1c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 4 May 2022 15:47:24 +0200 Subject: [PATCH 204/930] Remove more info links for hassio system health (#71286) --- homeassistant/components/hassio/system_health.py | 3 --- tests/components/hassio/test_system_health.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/homeassistant/components/hassio/system_health.py b/homeassistant/components/hassio/system_health.py index 4be94f89218..5ce38b0e121 100644 --- a/homeassistant/components/hassio/system_health.py +++ b/homeassistant/components/hassio/system_health.py @@ -30,7 +30,6 @@ async def system_health_info(hass: HomeAssistant): healthy = { "type": "failed", "error": "Unhealthy", - "more_info": "/hassio/system", } if supervisor_info.get("supported"): @@ -39,7 +38,6 @@ async def system_health_info(hass: HomeAssistant): supported = { "type": "failed", "error": "Unsupported", - "more_info": "/hassio/system", } information = { @@ -63,7 +61,6 @@ async def system_health_info(hass: HomeAssistant): information["version_api"] = system_health.async_check_can_reach_url( hass, f"https://version.home-assistant.io/{info.get('channel')}.json", - "/hassio/system", ) information["installed_addons"] = ", ".join( diff --git a/tests/components/hassio/test_system_health.py b/tests/components/hassio/test_system_health.py index 8fa610b5442..dcf18f7bc13 100644 --- a/tests/components/hassio/test_system_health.py +++ b/tests/components/hassio/test_system_health.py @@ -98,16 +98,13 @@ async def test_hassio_system_health_with_issues(hass, aioclient_mock): assert info["healthy"] == { "error": "Unhealthy", - "more_info": "/hassio/system", "type": "failed", } assert info["supported"] == { "error": "Unsupported", - "more_info": "/hassio/system", "type": "failed", } assert info["version_api"] == { "error": "unreachable", - "more_info": "/hassio/system", "type": "failed", } From fdee8800a0f3de4a45f8d559fa0871def5fdc9f4 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 4 May 2022 15:51:21 +0200 Subject: [PATCH 205/930] Handle empty zeroconf properties in devolo_home_network (#71288) * Handle empty zeroconf properties in devolo_home_network * Change approach * Restore test data --- homeassistant/components/devolo_home_network/manifest.json | 4 +++- homeassistant/generated/zeroconf.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/devolo_home_network/manifest.json b/homeassistant/components/devolo_home_network/manifest.json index a514606a322..445a383ea14 100644 --- a/homeassistant/components/devolo_home_network/manifest.json +++ b/homeassistant/components/devolo_home_network/manifest.json @@ -4,7 +4,9 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/devolo_home_network", "requirements": ["devolo-plc-api==0.7.1"], - "zeroconf": ["_dvl-deviceapi._tcp.local."], + "zeroconf": [ + { "type": "_dvl-deviceapi._tcp.local.", "properties": { "MT": "*" } } + ], "codeowners": ["@2Fake", "@Shutgun"], "quality_scale": "platinum", "iot_class": "local_polling", diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index 34e24e51fc9..b93d7249211 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -103,7 +103,10 @@ ZEROCONF = { "domain": "devolo_home_control" }, { - "domain": "devolo_home_network" + "domain": "devolo_home_network", + "properties": { + "MT": "*" + } } ], "_easylink._tcp.local.": [ From 1df99badcf04593bb98348c8669e0d066277ae27 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 4 May 2022 15:54:37 +0200 Subject: [PATCH 206/930] Allow scripts to turn themselves on (#71289) --- homeassistant/components/script/__init__.py | 9 ++- tests/components/automation/test_init.py | 6 -- tests/components/script/test_init.py | 89 ++++++++++++++++++++- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/script/__init__.py b/homeassistant/components/script/__init__.py index 660e7233b33..efad242fbd0 100644 --- a/homeassistant/components/script/__init__.py +++ b/homeassistant/components/script/__init__.py @@ -42,6 +42,7 @@ from homeassistant.helpers.script import ( CONF_MAX, CONF_MAX_EXCEEDED, Script, + script_stack_cv, ) from homeassistant.helpers.service import async_set_service_schema from homeassistant.helpers.trace import trace_get, trace_path @@ -398,10 +399,14 @@ class ScriptEntity(ToggleEntity, RestoreEntity): return # Caller does not want to wait for called script to finish so let script run in - # separate Task. However, wait for first state change so we can guarantee that - # it is written to the State Machine before we return. + # separate Task. Make a new empty script stack; scripts are allowed to + # recursively turn themselves on when not waiting. + script_stack_cv.set([]) + self._changed.clear() self.hass.async_create_task(coro) + # Wait for first state change so we can guarantee that + # it is written to the State Machine before we return. await self._changed.wait() async def _async_run(self, variables, context): diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index ccb508c6acc..dbc8f0fc346 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1791,18 +1791,12 @@ async def test_recursive_automation(hass: HomeAssistant, automation_mode, caplog ) service_called = asyncio.Event() - service_called_late = [] async def async_service_handler(service): if service.service == "automation_done": service_called.set() - if service.service == "automation_started_late": - service_called_late.append(service) hass.services.async_register("test", "automation_done", async_service_handler) - hass.services.async_register( - "test", "automation_started_late", async_service_handler - ) hass.bus.async_fire("trigger_automation") await asyncio.wait_for(service_called.wait(), 1) diff --git a/tests/components/script/test_init.py b/tests/components/script/test_init.py index 8a5297786f5..ca0cdb97592 100644 --- a/tests/components/script/test_init.py +++ b/tests/components/script/test_init.py @@ -1,6 +1,7 @@ """The tests for the Script component.""" # pylint: disable=protected-access import asyncio +from datetime import timedelta from unittest.mock import Mock, patch import pytest @@ -33,12 +34,13 @@ from homeassistant.helpers.script import ( SCRIPT_MODE_QUEUED, SCRIPT_MODE_RESTART, SCRIPT_MODE_SINGLE, + _async_stop_scripts_at_shutdown, ) from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import async_mock_service, mock_restore_cache +from tests.common import async_fire_time_changed, async_mock_service, mock_restore_cache from tests.components.logbook.test_init import MockLazyEventPartialState ENTITY_ID = "script.test" @@ -919,6 +921,91 @@ async def test_recursive_script_indirect(hass, script_mode, warning_msg, caplog) assert warning_msg in caplog.text +@pytest.mark.parametrize( + "script_mode", [SCRIPT_MODE_PARALLEL, SCRIPT_MODE_QUEUED, SCRIPT_MODE_RESTART] +) +async def test_recursive_script_turn_on(hass: HomeAssistant, script_mode, caplog): + """Test script turning itself on. + + - Illegal recursion detection should not be triggered + - Home Assistant should not hang on shut down + - SCRIPT_MODE_SINGLE is not relevant because suca script can't turn itself on + """ + # Make sure we cover all script modes + assert SCRIPT_MODE_CHOICES == [ + SCRIPT_MODE_PARALLEL, + SCRIPT_MODE_QUEUED, + SCRIPT_MODE_RESTART, + SCRIPT_MODE_SINGLE, + ] + stop_scripts_at_shutdown_called = asyncio.Event() + real_stop_scripts_at_shutdown = _async_stop_scripts_at_shutdown + + async def stop_scripts_at_shutdown(*args): + await real_stop_scripts_at_shutdown(*args) + stop_scripts_at_shutdown_called.set() + + with patch( + "homeassistant.helpers.script._async_stop_scripts_at_shutdown", + wraps=stop_scripts_at_shutdown, + ): + assert await async_setup_component( + hass, + script.DOMAIN, + { + script.DOMAIN: { + "script1": { + "mode": script_mode, + "sequence": [ + { + "choose": { + "conditions": { + "condition": "template", + "value_template": "{{ request == 'step_2' }}", + }, + "sequence": {"service": "test.script_done"}, + }, + "default": { + "service": "script.turn_on", + "data": { + "entity_id": "script.script1", + "variables": {"request": "step_2"}, + }, + }, + }, + { + "service": "script.turn_on", + "data": {"entity_id": "script.script1"}, + }, + ], + } + } + }, + ) + + service_called = asyncio.Event() + + async def async_service_handler(service): + if service.service == "script_done": + service_called.set() + + hass.services.async_register("test", "script_done", async_service_handler) + + await hass.services.async_call("script", "script1") + await asyncio.wait_for(service_called.wait(), 1) + + # Trigger 1st stage script shutdown + hass.state = CoreState.stopping + hass.bus.async_fire("homeassistant_stop") + await asyncio.wait_for(stop_scripts_at_shutdown_called.wait(), 1) + + # Trigger 2nd stage script shutdown + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=90)) + await hass.async_block_till_done() + + assert "Disallowed recursion detected" not in caplog.text + + async def test_setup_with_duplicate_scripts( hass: HomeAssistant, caplog: pytest.LogCaptureFixture ) -> None: From f2d6a06a6a5eb28d045830e3d6da91b6b0755c16 Mon Sep 17 00:00:00 2001 From: Thomas Dietrich Date: Wed, 4 May 2022 15:57:56 +0200 Subject: [PATCH 207/930] Add additional characteristics to the statistics integration (#62631) * Improve config checking, add device_class timestamp * Improve warning message --- homeassistant/components/statistics/sensor.py | 44 ++++++++++++++++- tests/components/statistics/test_sensor.py | 48 +++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index bc6acb2732a..ac62b63e8ca 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -64,8 +64,12 @@ STAT_CHANGE = "change" STAT_CHANGE_SAMPLE = "change_sample" STAT_CHANGE_SECOND = "change_second" STAT_COUNT = "count" +STAT_COUNT_BINARY_ON = "count_on" +STAT_COUNT_BINARY_OFF = "count_off" STAT_DATETIME_NEWEST = "datetime_newest" STAT_DATETIME_OLDEST = "datetime_oldest" +STAT_DATETIME_VALUE_MAX = "datetime_value_max" +STAT_DATETIME_VALUE_MIN = "datetime_value_min" STAT_DISTANCE_95P = "distance_95_percent_of_values" STAT_DISTANCE_99P = "distance_99_percent_of_values" STAT_DISTANCE_ABSOLUTE = "distance_absolute" @@ -99,6 +103,8 @@ STATS_NUMERIC_SUPPORT = { STAT_COUNT, STAT_DATETIME_NEWEST, STAT_DATETIME_OLDEST, + STAT_DATETIME_VALUE_MAX, + STAT_DATETIME_VALUE_MIN, STAT_DISTANCE_95P, STAT_DISTANCE_99P, STAT_DISTANCE_ABSOLUTE, @@ -118,18 +124,26 @@ STATS_BINARY_SUPPORT = { STAT_AVERAGE_STEP, STAT_AVERAGE_TIMELESS, STAT_COUNT, + STAT_COUNT_BINARY_ON, + STAT_COUNT_BINARY_OFF, + STAT_DATETIME_NEWEST, + STAT_DATETIME_OLDEST, STAT_MEAN, } STATS_NOT_A_NUMBER = { STAT_DATETIME_NEWEST, STAT_DATETIME_OLDEST, + STAT_DATETIME_VALUE_MAX, + STAT_DATETIME_VALUE_MIN, STAT_QUANTILES, } STATS_DATETIME = { STAT_DATETIME_NEWEST, STAT_DATETIME_OLDEST, + STAT_DATETIME_VALUE_MAX, + STAT_DATETIME_VALUE_MIN, } # Statistics which retain the unit of the source entity @@ -351,7 +365,7 @@ class StatisticsSensor(SensorEntity): except ValueError: self.attributes[STAT_SOURCE_VALUE_VALID] = False _LOGGER.error( - "%s: parsing error, expected number and received %s", + "%s: parsing error. Expected number or binary state, but received '%s'", self.entity_id, new_state.state, ) @@ -370,7 +384,11 @@ class StatisticsSensor(SensorEntity): unit = base_unit elif self._state_characteristic in STATS_NOT_A_NUMBER: unit = None - elif self._state_characteristic == STAT_COUNT: + elif self._state_characteristic in ( + STAT_COUNT, + STAT_COUNT_BINARY_ON, + STAT_COUNT_BINARY_OFF, + ): unit = None elif self._state_characteristic == STAT_VARIANCE: unit = base_unit + "²" @@ -614,6 +632,16 @@ class StatisticsSensor(SensorEntity): return self.ages[0] return None + def _stat_datetime_value_max(self) -> datetime | None: + if len(self.states) > 0: + return self.ages[self.states.index(max(self.states))] + return None + + def _stat_datetime_value_min(self) -> datetime | None: + if len(self.states) > 0: + return self.ages[self.states.index(min(self.states))] + return None + def _stat_distance_95_percent_of_values(self) -> StateType: if len(self.states) >= 2: return 2 * 1.96 * cast(float, self._stat_standard_deviation()) @@ -704,6 +732,18 @@ class StatisticsSensor(SensorEntity): def _stat_binary_count(self) -> StateType: return len(self.states) + def _stat_binary_count_on(self) -> StateType: + return self.states.count(True) + + def _stat_binary_count_off(self) -> StateType: + return self.states.count(False) + + def _stat_binary_datetime_newest(self) -> datetime | None: + return self._stat_datetime_newest() + + def _stat_binary_datetime_oldest(self) -> datetime | None: + return self._stat_datetime_oldest() + def _stat_binary_mean(self) -> StateType: if len(self.states) > 0: return 100.0 / len(self.states) * self.states.count(True) diff --git a/tests/components/statistics/test_sensor.py b/tests/components/statistics/test_sensor.py index dbcb3b1b8e7..56255216f52 100644 --- a/tests/components/statistics/test_sensor.py +++ b/tests/components/statistics/test_sensor.py @@ -687,6 +687,22 @@ async def test_state_characteristics(hass: HomeAssistant): "value_9": (start_datetime + timedelta(minutes=1)).isoformat(), "unit": None, }, + { + "source_sensor_domain": "sensor", + "name": "datetime_value_max", + "value_0": STATE_UNKNOWN, + "value_1": (start_datetime + timedelta(minutes=9)).isoformat(), + "value_9": (start_datetime + timedelta(minutes=2)).isoformat(), + "unit": None, + }, + { + "source_sensor_domain": "sensor", + "name": "datetime_value_min", + "value_0": STATE_UNKNOWN, + "value_1": (start_datetime + timedelta(minutes=9)).isoformat(), + "value_9": (start_datetime + timedelta(minutes=5)).isoformat(), + "unit": None, + }, { "source_sensor_domain": "sensor", "name": "distance_95_percent_of_values", @@ -811,6 +827,38 @@ async def test_state_characteristics(hass: HomeAssistant): "value_9": len(VALUES_BINARY), "unit": None, }, + { + "source_sensor_domain": "binary_sensor", + "name": "count_on", + "value_0": 0, + "value_1": 1, + "value_9": VALUES_BINARY.count("on"), + "unit": None, + }, + { + "source_sensor_domain": "binary_sensor", + "name": "count_off", + "value_0": 0, + "value_1": 0, + "value_9": VALUES_BINARY.count("off"), + "unit": None, + }, + { + "source_sensor_domain": "binary_sensor", + "name": "datetime_newest", + "value_0": STATE_UNKNOWN, + "value_1": (start_datetime + timedelta(minutes=9)).isoformat(), + "value_9": (start_datetime + timedelta(minutes=9)).isoformat(), + "unit": None, + }, + { + "source_sensor_domain": "binary_sensor", + "name": "datetime_oldest", + "value_0": STATE_UNKNOWN, + "value_1": (start_datetime + timedelta(minutes=9)).isoformat(), + "value_9": (start_datetime + timedelta(minutes=1)).isoformat(), + "unit": None, + }, { "source_sensor_domain": "binary_sensor", "name": "mean", From 12bd5fae1a198e8f1c70039c6345fd7d0e21c072 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 4 May 2022 16:13:09 +0200 Subject: [PATCH 208/930] Fix meater sensor (#71283) * Fix meater sensor * Cleanup MeaterEntityDescription * Apply suggestions from code review Co-authored-by: Martin Hjelmare * Update sensor.py Co-authored-by: Martin Hjelmare --- homeassistant/components/meater/sensor.py | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index 70582f39d9e..17e8db9e473 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -27,15 +27,18 @@ from .const import DOMAIN @dataclass -class MeaterSensorEntityDescription(SensorEntityDescription): - """Describes meater sensor entity.""" +class MeaterSensorEntityDescriptionMixin: + """Mixin for MeaterSensorEntityDescription.""" - available: Callable[ - [MeaterProbe | None], bool | type[NotImplementedError] - ] = lambda x: NotImplementedError - value: Callable[ - [MeaterProbe], datetime | float | str | None | type[NotImplementedError] - ] = lambda x: NotImplementedError + available: Callable[[MeaterProbe | None], bool] + value: Callable[[MeaterProbe], datetime | float | str | None] + + +@dataclass +class MeaterSensorEntityDescription( + SensorEntityDescription, MeaterSensorEntityDescriptionMixin +): + """Describes meater sensor entity.""" def _elapsed_time_to_timestamp(probe: MeaterProbe) -> datetime | None: @@ -108,7 +111,8 @@ SENSOR_TYPES = ( available=lambda probe: probe is not None and probe.cook is not None, value=lambda probe: probe.cook.peak_temperature if probe.cook else None, ), - # Time since the start of cook in seconds. Default: 0. + # Remaining time in seconds. When unknown/calculating default is used. Default: -1 + # Exposed as a TIMESTAMP sensor where the timestamp is current time + remaining time. MeaterSensorEntityDescription( key="cook_time_remaining", device_class=SensorDeviceClass.TIMESTAMP, @@ -116,7 +120,8 @@ SENSOR_TYPES = ( available=lambda probe: probe is not None and probe.cook is not None, value=_remaining_time_to_timestamp, ), - # Remaining time in seconds. When unknown/calculating default is used. Default: -1 + # Time since the start of cook in seconds. Default: 0. Exposed as a TIMESTAMP sensor + # where the timestamp is current time - elapsed time. MeaterSensorEntityDescription( key="cook_time_elapsed", device_class=SensorDeviceClass.TIMESTAMP, @@ -147,7 +152,7 @@ async def async_setup_entry( # Add entities for temperature probes which we've not yet seen for dev in devices: - if dev in known_probes: + if dev.id in known_probes: continue entities.extend( @@ -156,7 +161,7 @@ async def async_setup_entry( for sensor_description in SENSOR_TYPES ] ) - known_probes.add(dev) + known_probes.add(dev.id) async_add_entities(entities) From 3704b5cf5e535c217ce1fa3c2686d7606ae81b76 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 4 May 2022 16:38:11 +0200 Subject: [PATCH 209/930] Bump aioslimproto to 2.0.1 (#71285) --- homeassistant/components/slimproto/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/slimproto/manifest.json b/homeassistant/components/slimproto/manifest.json index eb4ab00f18d..23b3198d7e4 100644 --- a/homeassistant/components/slimproto/manifest.json +++ b/homeassistant/components/slimproto/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "iot_class": "local_push", "documentation": "https://www.home-assistant.io/integrations/slimproto", - "requirements": ["aioslimproto==2.0.0"], + "requirements": ["aioslimproto==2.0.1"], "codeowners": ["@marcelveldt"], "after_dependencies": ["media_source"] } diff --git a/requirements_all.txt b/requirements_all.txt index ce4a5971ecd..16778889248 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -244,7 +244,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==2.0.0 +aioslimproto==2.0.1 # homeassistant.components.steamist aiosteamist==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3772fd7b38a..6d1a1de2cf6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -210,7 +210,7 @@ aiosenz==1.0.0 aioshelly==2.0.0 # homeassistant.components.slimproto -aioslimproto==2.0.0 +aioslimproto==2.0.1 # homeassistant.components.steamist aiosteamist==0.3.1 From d95113c8f26dbdcabe0c801da86b4f68daa89e54 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 4 May 2022 16:56:29 +0200 Subject: [PATCH 210/930] Pin grpcio-status to 1.45.0 (#71293) --- homeassistant/package_constraints.txt | 1 + script/gen_requirements_all.py | 1 + 2 files changed, 2 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a3dec49ddb9..0a2846a44ad 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -51,6 +51,7 @@ httplib2>=0.19.0 # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. grpcio==1.45.0 +grpcio-status==1.45.0 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index ef2b67f3657..1e93b8bba35 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -68,6 +68,7 @@ httplib2>=0.19.0 # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. grpcio==1.45.0 +grpcio-status==1.45.0 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, From 08770d015b5413bd56251f7376561148142ba5c2 Mon Sep 17 00:00:00 2001 From: Sean Vig Date: Wed, 4 May 2022 11:15:52 -0400 Subject: [PATCH 211/930] Change Amcrest event monitor to non-async (#69640) --- homeassistant/components/amcrest/__init__.py | 128 ++++++++++++------- 1 file changed, 84 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index f2472575259..e3f48263e8b 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -7,6 +7,7 @@ from contextlib import asynccontextmanager, suppress from dataclasses import dataclass from datetime import datetime, timedelta import logging +import threading from typing import Any import aiohttp @@ -30,15 +31,14 @@ from homeassistant.const import ( CONF_USERNAME, ENTITY_MATCH_ALL, ENTITY_MATCH_NONE, - EVENT_HOMEASSISTANT_STOP, HTTP_BASIC_AUTHENTICATION, Platform, ) -from homeassistant.core import Event, HomeAssistant, ServiceCall, callback +from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.exceptions import Unauthorized, UnknownUser from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import async_dispatcher_send, dispatcher_send from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.service import async_extract_entity_ids from homeassistant.helpers.typing import ConfigType @@ -144,10 +144,13 @@ class AmcrestChecker(ApiWrapper): self._hass = hass self._wrap_name = name self._wrap_errors = 0 - self._wrap_lock = asyncio.Lock() + self._wrap_lock = threading.Lock() + self._async_wrap_lock = asyncio.Lock() self._wrap_login_err = False - self._wrap_event_flag = asyncio.Event() + self._wrap_event_flag = threading.Event() self._wrap_event_flag.set() + self._async_wrap_event_flag = asyncio.Event() + self._async_wrap_event_flag.set() self._unsub_recheck: Callable[[], None] | None = None super().__init__( host, @@ -164,12 +167,18 @@ class AmcrestChecker(ApiWrapper): return self._wrap_errors <= MAX_ERRORS and not self._wrap_login_err @property - def available_flag(self) -> asyncio.Event: + def available_flag(self) -> threading.Event: """Return event flag that indicates if camera's API is responding.""" return self._wrap_event_flag + @property + def async_available_flag(self) -> asyncio.Event: + """Return event flag that indicates if camera's API is responding.""" + return self._async_wrap_event_flag + def _start_recovery(self) -> None: - self._wrap_event_flag.clear() + self.available_flag.clear() + self.async_available_flag.clear() async_dispatcher_send( self._hass, service_signal(SERVICE_UPDATE, self._wrap_name) ) @@ -177,9 +186,22 @@ class AmcrestChecker(ApiWrapper): self._hass, self._wrap_test_online, RECHECK_INTERVAL ) + def command(self, *args: Any, **kwargs: Any) -> Any: + """amcrest.ApiWrapper.command wrapper to catch errors.""" + try: + ret = super().command(*args, **kwargs) + except LoginError as ex: + self._handle_offline(ex) + raise + except AmcrestError: + self._handle_error() + raise + self._set_online() + return ret + async def async_command(self, *args: Any, **kwargs: Any) -> httpx.Response: """amcrest.ApiWrapper.command wrapper to catch errors.""" - async with self._command_wrapper(): + async with self._async_command_wrapper(): ret = await super().async_command(*args, **kwargs) return ret @@ -188,35 +210,47 @@ class AmcrestChecker(ApiWrapper): self, *args: Any, **kwargs: Any ) -> AsyncIterator[httpx.Response]: """amcrest.ApiWrapper.command wrapper to catch errors.""" - async with self._command_wrapper(): + async with self._async_command_wrapper(): async with super().async_stream_command(*args, **kwargs) as ret: yield ret @asynccontextmanager - async def _command_wrapper(self) -> AsyncIterator[None]: + async def _async_command_wrapper(self) -> AsyncIterator[None]: try: yield except LoginError as ex: - async with self._wrap_lock: - was_online = self.available - was_login_err = self._wrap_login_err - self._wrap_login_err = True - if not was_login_err: - _LOGGER.error("%s camera offline: Login error: %s", self._wrap_name, ex) - if was_online: - self._start_recovery() + async with self._async_wrap_lock: + self._handle_offline(ex) raise except AmcrestError: - async with self._wrap_lock: - was_online = self.available - errs = self._wrap_errors = self._wrap_errors + 1 - offline = not self.available - _LOGGER.debug("%s camera errs: %i", self._wrap_name, errs) - if was_online and offline: - _LOGGER.error("%s camera offline: Too many errors", self._wrap_name) - self._start_recovery() + async with self._async_wrap_lock: + self._handle_error() raise - async with self._wrap_lock: + async with self._async_wrap_lock: + self._set_online() + + def _handle_offline(self, ex: Exception) -> None: + with self._wrap_lock: + was_online = self.available + was_login_err = self._wrap_login_err + self._wrap_login_err = True + if not was_login_err: + _LOGGER.error("%s camera offline: Login error: %s", self._wrap_name, ex) + if was_online: + self._start_recovery() + + def _handle_error(self) -> None: + with self._wrap_lock: + was_online = self.available + errs = self._wrap_errors = self._wrap_errors + 1 + offline = not self.available + _LOGGER.debug("%s camera errs: %i", self._wrap_name, errs) + if was_online and offline: + _LOGGER.error("%s camera offline: Too many errors", self._wrap_name) + self._start_recovery() + + def _set_online(self) -> None: + with self._wrap_lock: was_offline = not self.available self._wrap_errors = 0 self._wrap_login_err = False @@ -225,7 +259,8 @@ class AmcrestChecker(ApiWrapper): self._unsub_recheck() self._unsub_recheck = None _LOGGER.error("%s camera back online", self._wrap_name) - self._wrap_event_flag.set() + self.available_flag.set() + self.async_available_flag.set() async_dispatcher_send( self._hass, service_signal(SERVICE_UPDATE, self._wrap_name) ) @@ -237,18 +272,18 @@ class AmcrestChecker(ApiWrapper): await self.async_current_time -async def _monitor_events( +def _monitor_events( hass: HomeAssistant, name: str, api: AmcrestChecker, event_codes: set[str], ) -> None: while True: - await api.available_flag.wait() + api.available_flag.wait() try: - async for code, payload in api.async_event_actions("All"): + for code, payload in api.event_actions("All"): event_data = {"camera": name, "event": code, "payload": payload} - hass.bus.async_fire("amcrest", event_data) + hass.bus.fire("amcrest", event_data) if code in event_codes: signal = service_signal(SERVICE_EVENT, name, code) start = any( @@ -256,18 +291,32 @@ async def _monitor_events( for key, val in payload.items() ) _LOGGER.debug("Sending signal: '%s': %s", signal, start) - async_dispatcher_send(hass, signal, start) + dispatcher_send(hass, signal, start) except AmcrestError as error: _LOGGER.warning( "Error while processing events from %s camera: %r", name, error ) +def _start_event_monitor( + hass: HomeAssistant, + name: str, + api: AmcrestChecker, + event_codes: set[str], +) -> None: + thread = threading.Thread( + target=_monitor_events, + name=f"Amcrest {name}", + args=(hass, name, api, event_codes), + daemon=True, + ) + thread.start() + + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Amcrest IP Camera component.""" hass.data.setdefault(DATA_AMCREST, {DEVICES: {}, CAMERAS: []}) - monitor_tasks = [] for device in config[DOMAIN]: name: str = device[CONF_NAME] username: str = device[CONF_USERNAME] @@ -328,9 +377,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: and sensor.event_code is not None } - monitor_tasks.append( - asyncio.create_task(_monitor_events(hass, name, api, event_codes)) - ) + _start_event_monitor(hass, name, api, event_codes) if sensors: hass.async_create_task( @@ -354,13 +401,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: ) ) - @callback - def cancel_monitors(event: Event) -> None: - for monitor_task in monitor_tasks: - monitor_task.cancel() - - hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cancel_monitors) - if not hass.data[DATA_AMCREST][DEVICES]: return False From 13ce0a7d6ac7aed9dc50a1bfd7e12339621bc3c5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 4 May 2022 10:56:50 -0500 Subject: [PATCH 212/930] Fix history using pre v25 queries during v26 migration (#71295) --- homeassistant/components/recorder/core.py | 5 +++++ homeassistant/components/recorder/history.py | 4 ++-- tests/components/recorder/test_history.py | 6 +++--- tests/components/recorder/test_init.py | 7 +++++++ tests/components/recorder/test_migrate.py | 7 ++++++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 7f07a4483cb..84509a1bd53 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -44,6 +44,7 @@ from .const import ( ) from .executor import DBInterruptibleThreadPoolExecutor from .models import ( + SCHEMA_VERSION, Base, EventData, Events, @@ -161,6 +162,7 @@ class Recorder(threading.Thread): self.entity_filter = entity_filter self.exclude_t = exclude_t + self.schema_version = 0 self._commits_without_expire = 0 self._old_states: dict[str, States] = {} self._state_attributes_ids: LRU = LRU(STATE_ATTRIBUTES_ID_CACHE_SIZE) @@ -502,6 +504,8 @@ class Recorder(threading.Thread): self.hass.add_job(self.async_connection_failed) return + self.schema_version = current_version + schema_is_current = migration.schema_is_current(current_version) if schema_is_current: self._setup_run() @@ -523,6 +527,7 @@ class Recorder(threading.Thread): # with startup which is also cpu intensive if not schema_is_current: if self._migrate_schema_and_setup_run(current_version): + self.schema_version = SCHEMA_VERSION if not self._event_listener: # If the schema migration takes so long that the end # queue watcher safety kicks in because MAX_QUEUE_BACKLOG diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 24fe21f101c..d221ced3a84 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -116,7 +116,7 @@ def query_and_join_attributes( # If we in the process of migrating schema we do # not want to join the state_attributes table as we # do not know if it will be there yet - if recorder.get_instance(hass).migration_in_progress: + if recorder.get_instance(hass).schema_version < 25: return QUERY_STATES_PRE_SCHEMA_25, False # Finally if no migration is in progress and no_attributes # was not requested, we query both attributes columns and @@ -146,7 +146,7 @@ def bake_query_and_join_attributes( # If we in the process of migrating schema we do # not want to join the state_attributes table as we # do not know if it will be there yet - if recorder.get_instance(hass).migration_in_progress: + if recorder.get_instance(hass).schema_version < 25: if include_last_updated: return ( bakery(lambda session: session.query(*QUERY_STATES_PRE_SCHEMA_25)), diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index 5e29fb092b1..20b60c3c96d 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -665,7 +665,7 @@ async def test_state_changes_during_period_query_during_migration_to_schema_25( conn.execute(text("drop table state_attributes;")) conn.commit() - with patch.object(instance, "migration_in_progress", True): + with patch.object(instance, "schema_version", 24): no_attributes = True hist = history.state_changes_during_period( hass, start, end, entity_id, no_attributes, include_start_time_state=False @@ -711,7 +711,7 @@ async def test_get_states_query_during_migration_to_schema_25( conn.execute(text("drop table state_attributes;")) conn.commit() - with patch.object(instance, "migration_in_progress", True): + with patch.object(instance, "schema_version", 24): no_attributes = True hist = await _async_get_states( hass, end, [entity_id], no_attributes=no_attributes @@ -760,7 +760,7 @@ async def test_get_states_query_during_migration_to_schema_25_multiple_entities( conn.execute(text("drop table state_attributes;")) conn.commit() - with patch.object(instance, "migration_in_progress", True): + with patch.object(instance, "schema_version", 24): no_attributes = True hist = await _async_get_states( hass, end, entity_ids, no_attributes=no_attributes diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 9bfca76394b..18b74df0189 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -26,6 +26,7 @@ from homeassistant.components.recorder import ( ) from homeassistant.components.recorder.const import DATA_INSTANCE, KEEPALIVE_TIME from homeassistant.components.recorder.models import ( + SCHEMA_VERSION, EventData, Events, RecorderRuns, @@ -459,6 +460,12 @@ def _state_with_context(hass, entity_id): return hass.states.get(entity_id) +def test_setup_without_migration(hass_recorder): + """Verify the schema version without a migration.""" + hass = hass_recorder() + assert recorder.get_instance(hass).schema_version == SCHEMA_VERSION + + # pylint: disable=redefined-outer-name,invalid-name def test_saving_state_include_domains(hass_recorder): """Test saving and restoring a state.""" diff --git a/tests/components/recorder/test_migrate.py b/tests/components/recorder/test_migrate.py index 1b84eb5d171..0a95d174d66 100644 --- a/tests/components/recorder/test_migrate.py +++ b/tests/components/recorder/test_migrate.py @@ -22,7 +22,11 @@ from homeassistant.bootstrap import async_setup_component from homeassistant.components import persistent_notification as pn, recorder from homeassistant.components.recorder import migration, models from homeassistant.components.recorder.const import DATA_INSTANCE -from homeassistant.components.recorder.models import RecorderRuns, States +from homeassistant.components.recorder.models import ( + SCHEMA_VERSION, + RecorderRuns, + States, +) from homeassistant.components.recorder.util import session_scope import homeassistant.util.dt as dt_util @@ -80,6 +84,7 @@ async def test_migration_in_progress(hass): await async_wait_recording_done(hass) assert recorder.util.async_migration_in_progress(hass) is False + assert recorder.get_instance(hass).schema_version == SCHEMA_VERSION async def test_database_migration_failed(hass): From eb77f8db8559dba95e5e36c8a9314f89e1ae82b1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 4 May 2022 12:22:50 -0500 Subject: [PATCH 213/930] Complete strict typing for recorder (#71274) * Complete strict typing for recorder * update tests * Update tests/components/recorder/test_migrate.py Co-authored-by: Martin Hjelmare * Update tests/components/recorder/test_migrate.py Co-authored-by: Martin Hjelmare * Remove the asserts * remove ignore comments Co-authored-by: Martin Hjelmare --- .strict-typing | 18 +- homeassistant/components/recorder/core.py | 22 +- .../components/recorder/migration.py | 208 ++++++++++-------- homeassistant/components/recorder/purge.py | 4 +- .../components/recorder/statistics.py | 16 +- homeassistant/components/recorder/tasks.py | 2 - mypy.ini | 178 +-------------- tests/components/recorder/test_init.py | 2 + tests/components/recorder/test_migrate.py | 23 +- tests/components/recorder/test_statistics.py | 2 +- 10 files changed, 166 insertions(+), 309 deletions(-) diff --git a/.strict-typing b/.strict-typing index f42bd4a4ab1..67efdfa7953 100644 --- a/.strict-typing +++ b/.strict-typing @@ -177,23 +177,7 @@ homeassistant.components.pure_energie.* homeassistant.components.rainmachine.* homeassistant.components.rdw.* homeassistant.components.recollect_waste.* -homeassistant.components.recorder -homeassistant.components.recorder.const -homeassistant.components.recorder.core -homeassistant.components.recorder.backup -homeassistant.components.recorder.executor -homeassistant.components.recorder.history -homeassistant.components.recorder.models -homeassistant.components.recorder.pool -homeassistant.components.recorder.purge -homeassistant.components.recorder.repack -homeassistant.components.recorder.run_history -homeassistant.components.recorder.services -homeassistant.components.recorder.statistics -homeassistant.components.recorder.system_health -homeassistant.components.recorder.tasks -homeassistant.components.recorder.util -homeassistant.components.recorder.websocket_api +homeassistant.components.recorder.* homeassistant.components.remote.* homeassistant.components.renault.* homeassistant.components.ridwell.* diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 84509a1bd53..af368b909b7 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -171,7 +171,7 @@ class Recorder(threading.Thread): self._pending_event_data: dict[str, EventData] = {} self._pending_expunge: list[States] = [] self.event_session: Session | None = None - self.get_session: Callable[[], Session] | None = None + self._get_session: Callable[[], Session] | None = None self._completed_first_database_setup: bool | None = None self.async_migration_event = asyncio.Event() self.migration_in_progress = False @@ -205,6 +205,12 @@ class Recorder(threading.Thread): """Return if the recorder is recording.""" return self._event_listener is not None + def get_session(self) -> Session: + """Get a new sqlalchemy session.""" + if self._get_session is None: + raise RuntimeError("The database connection has not been established") + return self._get_session() + def queue_task(self, task: RecorderTask) -> None: """Add a task to the recorder queue.""" self._queue.put(task) @@ -459,7 +465,7 @@ class Recorder(threading.Thread): @callback def _async_setup_periodic_tasks(self) -> None: """Prepare periodic tasks.""" - if self.hass.is_stopping or not self.get_session: + if self.hass.is_stopping or not self._get_session: # Home Assistant is shutting down return @@ -591,7 +597,7 @@ class Recorder(threading.Thread): while tries <= self.db_max_retries: try: self._setup_connection() - return migration.get_schema_version(self) + return migration.get_schema_version(self.get_session) except Exception as err: # pylint: disable=broad-except _LOGGER.exception( "Error during connection setup: %s (retrying in %s seconds)", @@ -619,7 +625,9 @@ class Recorder(threading.Thread): self.hass.add_job(self._async_migration_started) try: - migration.migrate_schema(self, current_version) + migration.migrate_schema( + self.hass, self.engine, self.get_session, current_version + ) except exc.DatabaseError as err: if self._handle_database_error(err): return True @@ -896,7 +904,6 @@ class Recorder(threading.Thread): def _open_event_session(self) -> None: """Open the event session.""" - assert self.get_session is not None self.event_session = self.get_session() self.event_session.expire_on_commit = False @@ -1011,7 +1018,7 @@ class Recorder(threading.Thread): sqlalchemy_event.listen(self.engine, "connect", setup_recorder_connection) Base.metadata.create_all(self.engine) - self.get_session = scoped_session(sessionmaker(bind=self.engine, future=True)) + self._get_session = scoped_session(sessionmaker(bind=self.engine, future=True)) _LOGGER.debug("Connected to recorder database") def _close_connection(self) -> None: @@ -1019,11 +1026,10 @@ class Recorder(threading.Thread): assert self.engine is not None self.engine.dispose() self.engine = None - self.get_session = None + self._get_session = None def _setup_run(self) -> None: """Log the start of the current run and schedule any needed jobs.""" - assert self.get_session is not None with session_scope(session=self.get_session()) as session: end_incomplete_runs(session, self.run_history.recording_start) self.run_history.start(session) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 7835f5320b9..b38bb89b5b9 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -1,11 +1,13 @@ """Schema migration helpers.""" +from collections.abc import Callable, Iterable import contextlib from datetime import timedelta import logging -from typing import Any +from typing import cast import sqlalchemy from sqlalchemy import ForeignKeyConstraint, MetaData, Table, func, text +from sqlalchemy.engine import Engine from sqlalchemy.exc import ( DatabaseError, InternalError, @@ -13,9 +15,12 @@ from sqlalchemy.exc import ( ProgrammingError, SQLAlchemyError, ) +from sqlalchemy.orm.session import Session from sqlalchemy.schema import AddConstraint, DropConstraint from sqlalchemy.sql.expression import true +from homeassistant.core import HomeAssistant + from .models import ( SCHEMA_VERSION, TABLE_STATES, @@ -33,7 +38,7 @@ from .util import session_scope _LOGGER = logging.getLogger(__name__) -def raise_if_exception_missing_str(ex, match_substrs): +def raise_if_exception_missing_str(ex: Exception, match_substrs: Iterable[str]) -> None: """Raise an exception if the exception and cause do not contain the match substrs.""" lower_ex_strs = [str(ex).lower(), str(ex.__cause__).lower()] for str_sub in match_substrs: @@ -44,10 +49,9 @@ def raise_if_exception_missing_str(ex, match_substrs): raise ex -def get_schema_version(instance: Any) -> int: +def get_schema_version(session_maker: Callable[[], Session]) -> int: """Get the schema version.""" - assert instance.get_session is not None - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: res = ( session.query(SchemaChanges) .order_by(SchemaChanges.change_id.desc()) @@ -61,7 +65,7 @@ def get_schema_version(instance: Any) -> int: "No schema version found. Inspected version: %s", current_version ) - return current_version + return cast(int, current_version) def schema_is_current(current_version: int) -> bool: @@ -69,21 +73,27 @@ def schema_is_current(current_version: int) -> bool: return current_version == SCHEMA_VERSION -def migrate_schema(instance: Any, current_version: int) -> None: +def migrate_schema( + hass: HomeAssistant, + engine: Engine, + session_maker: Callable[[], Session], + current_version: int, +) -> None: """Check if the schema needs to be upgraded.""" - assert instance.get_session is not None _LOGGER.warning("Database is about to upgrade. Schema version: %s", current_version) for version in range(current_version, SCHEMA_VERSION): new_version = version + 1 _LOGGER.info("Upgrading recorder db schema to version %s", new_version) - _apply_update(instance, new_version, current_version) - with session_scope(session=instance.get_session()) as session: + _apply_update(hass, engine, session_maker, new_version, current_version) + with session_scope(session=session_maker()) as session: session.add(SchemaChanges(schema_version=new_version)) _LOGGER.info("Upgrade to version %s done", new_version) -def _create_index(instance, table_name, index_name): +def _create_index( + session_maker: Callable[[], Session], table_name: str, index_name: str +) -> None: """Create an index for the specified table. The index name should match the name given for the index @@ -104,7 +114,7 @@ def _create_index(instance, table_name, index_name): "be patient!", index_name, ) - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() index.create(connection) @@ -117,7 +127,9 @@ def _create_index(instance, table_name, index_name): _LOGGER.debug("Finished creating %s", index_name) -def _drop_index(instance, table_name, index_name): +def _drop_index( + session_maker: Callable[[], Session], table_name: str, index_name: str +) -> None: """Drop an index from a specified table. There is no universal way to do something like `DROP INDEX IF EXISTS` @@ -132,7 +144,7 @@ def _drop_index(instance, table_name, index_name): success = False # Engines like DB2/Oracle - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute(text(f"DROP INDEX {index_name}")) @@ -143,7 +155,7 @@ def _drop_index(instance, table_name, index_name): # Engines like SQLite, SQL Server if not success: - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -160,7 +172,7 @@ def _drop_index(instance, table_name, index_name): if not success: # Engines like MySQL, MS Access - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -194,7 +206,9 @@ def _drop_index(instance, table_name, index_name): ) -def _add_columns(instance, table_name, columns_def): +def _add_columns( + session_maker: Callable[[], Session], table_name: str, columns_def: list[str] +) -> None: """Add columns to a table.""" _LOGGER.warning( "Adding columns %s to table %s. Note: this can take several " @@ -206,7 +220,7 @@ def _add_columns(instance, table_name, columns_def): columns_def = [f"ADD {col_def}" for col_def in columns_def] - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -223,7 +237,7 @@ def _add_columns(instance, table_name, columns_def): _LOGGER.info("Unable to use quick column add. Adding 1 by 1") for column_def in columns_def: - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -242,7 +256,12 @@ def _add_columns(instance, table_name, columns_def): ) -def _modify_columns(instance, engine, table_name, columns_def): +def _modify_columns( + session_maker: Callable[[], Session], + engine: Engine, + table_name: str, + columns_def: list[str], +) -> None: """Modify columns in a table.""" if engine.dialect.name == "sqlite": _LOGGER.debug( @@ -274,7 +293,7 @@ def _modify_columns(instance, engine, table_name, columns_def): else: columns_def = [f"MODIFY {col_def}" for col_def in columns_def] - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -289,7 +308,7 @@ def _modify_columns(instance, engine, table_name, columns_def): _LOGGER.info("Unable to use quick column modify. Modifying 1 by 1") for column_def in columns_def: - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute( @@ -305,7 +324,9 @@ def _modify_columns(instance, engine, table_name, columns_def): ) -def _update_states_table_with_foreign_key_options(instance, engine): +def _update_states_table_with_foreign_key_options( + session_maker: Callable[[], Session], engine: Engine +) -> None: """Add the options to foreign key constraints.""" inspector = sqlalchemy.inspect(engine) alters = [] @@ -333,7 +354,7 @@ def _update_states_table_with_foreign_key_options(instance, engine): ) for alter in alters: - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute(DropConstraint(alter["old_fk"])) @@ -346,7 +367,9 @@ def _update_states_table_with_foreign_key_options(instance, engine): ) -def _drop_foreign_key_constraints(instance, engine, table, columns): +def _drop_foreign_key_constraints( + session_maker: Callable[[], Session], engine: Engine, table: str, columns: list[str] +) -> None: """Drop foreign key constraints for a table on specific columns.""" inspector = sqlalchemy.inspect(engine) drops = [] @@ -364,7 +387,7 @@ def _drop_foreign_key_constraints(instance, engine, table, columns): ) for drop in drops: - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: try: connection = session.connection() connection.execute(DropConstraint(drop)) @@ -376,19 +399,24 @@ def _drop_foreign_key_constraints(instance, engine, table, columns): ) -def _apply_update(instance, new_version, old_version): # noqa: C901 +def _apply_update( # noqa: C901 + hass: HomeAssistant, + engine: Engine, + session_maker: Callable[[], Session], + new_version: int, + old_version: int, +) -> None: """Perform operations to bring schema up to date.""" - engine = instance.engine dialect = engine.dialect.name big_int = "INTEGER(20)" if dialect == "mysql" else "INTEGER" if new_version == 1: - _create_index(instance, "events", "ix_events_time_fired") + _create_index(session_maker, "events", "ix_events_time_fired") elif new_version == 2: # Create compound start/end index for recorder_runs - _create_index(instance, "recorder_runs", "ix_recorder_runs_start_end") + _create_index(session_maker, "recorder_runs", "ix_recorder_runs_start_end") # Create indexes for states - _create_index(instance, "states", "ix_states_last_updated") + _create_index(session_maker, "states", "ix_states_last_updated") elif new_version == 3: # There used to be a new index here, but it was removed in version 4. pass @@ -398,41 +426,41 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 if old_version == 3: # Remove index that was added in version 3 - _drop_index(instance, "states", "ix_states_created_domain") + _drop_index(session_maker, "states", "ix_states_created_domain") if old_version == 2: # Remove index that was added in version 2 - _drop_index(instance, "states", "ix_states_entity_id_created") + _drop_index(session_maker, "states", "ix_states_entity_id_created") # Remove indexes that were added in version 0 - _drop_index(instance, "states", "states__state_changes") - _drop_index(instance, "states", "states__significant_changes") - _drop_index(instance, "states", "ix_states_entity_id_created") + _drop_index(session_maker, "states", "states__state_changes") + _drop_index(session_maker, "states", "states__significant_changes") + _drop_index(session_maker, "states", "ix_states_entity_id_created") - _create_index(instance, "states", "ix_states_entity_id_last_updated") + _create_index(session_maker, "states", "ix_states_entity_id_last_updated") elif new_version == 5: # Create supporting index for States.event_id foreign key - _create_index(instance, "states", "ix_states_event_id") + _create_index(session_maker, "states", "ix_states_event_id") elif new_version == 6: _add_columns( - instance, + session_maker, "events", ["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"], ) - _create_index(instance, "events", "ix_events_context_id") - _create_index(instance, "events", "ix_events_context_user_id") + _create_index(session_maker, "events", "ix_events_context_id") + _create_index(session_maker, "events", "ix_events_context_user_id") _add_columns( - instance, + session_maker, "states", ["context_id CHARACTER(36)", "context_user_id CHARACTER(36)"], ) - _create_index(instance, "states", "ix_states_context_id") - _create_index(instance, "states", "ix_states_context_user_id") + _create_index(session_maker, "states", "ix_states_context_id") + _create_index(session_maker, "states", "ix_states_context_user_id") elif new_version == 7: - _create_index(instance, "states", "ix_states_entity_id") + _create_index(session_maker, "states", "ix_states_entity_id") elif new_version == 8: - _add_columns(instance, "events", ["context_parent_id CHARACTER(36)"]) - _add_columns(instance, "states", ["old_state_id INTEGER"]) - _create_index(instance, "events", "ix_events_context_parent_id") + _add_columns(session_maker, "events", ["context_parent_id CHARACTER(36)"]) + _add_columns(session_maker, "states", ["old_state_id INTEGER"]) + _create_index(session_maker, "events", "ix_events_context_parent_id") elif new_version == 9: # We now get the context from events with a join # since its always there on state_changed events @@ -443,35 +471,35 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 # sqlalchemy alembic to make that work # # no longer dropping ix_states_context_id since its recreated in 28 - _drop_index(instance, "states", "ix_states_context_user_id") + _drop_index(session_maker, "states", "ix_states_context_user_id") # This index won't be there if they were not running # nightly but we don't treat that as a critical issue - _drop_index(instance, "states", "ix_states_context_parent_id") + _drop_index(session_maker, "states", "ix_states_context_parent_id") # Redundant keys on composite index: # We already have ix_states_entity_id_last_updated - _drop_index(instance, "states", "ix_states_entity_id") - _create_index(instance, "events", "ix_events_event_type_time_fired") - _drop_index(instance, "events", "ix_events_event_type") + _drop_index(session_maker, "states", "ix_states_entity_id") + _create_index(session_maker, "events", "ix_events_event_type_time_fired") + _drop_index(session_maker, "events", "ix_events_event_type") elif new_version == 10: # Now done in step 11 pass elif new_version == 11: - _create_index(instance, "states", "ix_states_old_state_id") - _update_states_table_with_foreign_key_options(instance, engine) + _create_index(session_maker, "states", "ix_states_old_state_id") + _update_states_table_with_foreign_key_options(session_maker, engine) elif new_version == 12: if engine.dialect.name == "mysql": - _modify_columns(instance, engine, "events", ["event_data LONGTEXT"]) - _modify_columns(instance, engine, "states", ["attributes LONGTEXT"]) + _modify_columns(session_maker, engine, "events", ["event_data LONGTEXT"]) + _modify_columns(session_maker, engine, "states", ["attributes LONGTEXT"]) elif new_version == 13: if engine.dialect.name == "mysql": _modify_columns( - instance, + session_maker, engine, "events", ["time_fired DATETIME(6)", "created DATETIME(6)"], ) _modify_columns( - instance, + session_maker, engine, "states", [ @@ -481,12 +509,14 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 ], ) elif new_version == 14: - _modify_columns(instance, engine, "events", ["event_type VARCHAR(64)"]) + _modify_columns(session_maker, engine, "events", ["event_type VARCHAR(64)"]) elif new_version == 15: # This dropped the statistics table, done again in version 18. pass elif new_version == 16: - _drop_foreign_key_constraints(instance, engine, TABLE_STATES, ["old_state_id"]) + _drop_foreign_key_constraints( + session_maker, engine, TABLE_STATES, ["old_state_id"] + ) elif new_version == 17: # This dropped the statistics table, done again in version 18. pass @@ -511,13 +541,13 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 elif new_version == 19: # This adds the statistic runs table, insert a fake run to prevent duplicating # statistics. - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: session.add(StatisticsRuns(start=get_start_time())) elif new_version == 20: # This changed the precision of statistics from float to double if engine.dialect.name in ["mysql", "postgresql"]: _modify_columns( - instance, + session_maker, engine, "statistics", [ @@ -539,7 +569,7 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 table, ) with contextlib.suppress(SQLAlchemyError): - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: connection = session.connection() connection.execute( # Using LOCK=EXCLUSIVE to prevent the database from corrupting @@ -574,7 +604,7 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 # Block 5-minute statistics for one hour from the last run, or it will overlap # with existing hourly statistics. Don't block on a database with no existing # statistics. - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: if session.query(Statistics.id).count() and ( last_run_string := session.query( func.max(StatisticsRuns.start) @@ -590,7 +620,7 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 # When querying the database, be careful to only explicitly query for columns # which were present in schema version 21. If querying the table, SQLAlchemy # will refer to future columns. - with session_scope(session=instance.get_session()) as session: + with session_scope(session=session_maker()) as session: for sum_statistic in session.query(StatisticsMeta.id).filter_by( has_sum=true() ): @@ -617,48 +647,52 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 ) elif new_version == 23: # Add name column to StatisticsMeta - _add_columns(instance, "statistics_meta", ["name VARCHAR(255)"]) + _add_columns(session_maker, "statistics_meta", ["name VARCHAR(255)"]) elif new_version == 24: # Recreate statistics indices to block duplicated statistics - _drop_index(instance, "statistics", "ix_statistics_statistic_id_start") + _drop_index(session_maker, "statistics", "ix_statistics_statistic_id_start") _drop_index( - instance, + session_maker, "statistics_short_term", "ix_statistics_short_term_statistic_id_start", ) try: - _create_index(instance, "statistics", "ix_statistics_statistic_id_start") _create_index( - instance, + session_maker, "statistics", "ix_statistics_statistic_id_start" + ) + _create_index( + session_maker, "statistics_short_term", "ix_statistics_short_term_statistic_id_start", ) except DatabaseError: # There may be duplicated statistics entries, delete duplicated statistics # and try again - with session_scope(session=instance.get_session()) as session: - delete_duplicates(instance, session) - _create_index(instance, "statistics", "ix_statistics_statistic_id_start") + with session_scope(session=session_maker()) as session: + delete_duplicates(hass, session) _create_index( - instance, + session_maker, "statistics", "ix_statistics_statistic_id_start" + ) + _create_index( + session_maker, "statistics_short_term", "ix_statistics_short_term_statistic_id_start", ) elif new_version == 25: - _add_columns(instance, "states", [f"attributes_id {big_int}"]) - _create_index(instance, "states", "ix_states_attributes_id") + _add_columns(session_maker, "states", [f"attributes_id {big_int}"]) + _create_index(session_maker, "states", "ix_states_attributes_id") elif new_version == 26: - _create_index(instance, "statistics_runs", "ix_statistics_runs_start") + _create_index(session_maker, "statistics_runs", "ix_statistics_runs_start") elif new_version == 27: - _add_columns(instance, "events", [f"data_id {big_int}"]) - _create_index(instance, "events", "ix_events_data_id") + _add_columns(session_maker, "events", [f"data_id {big_int}"]) + _create_index(session_maker, "events", "ix_events_data_id") elif new_version == 28: - _add_columns(instance, "events", ["origin_idx INTEGER"]) + _add_columns(session_maker, "events", ["origin_idx INTEGER"]) # We never use the user_id or parent_id index - _drop_index(instance, "events", "ix_events_context_user_id") - _drop_index(instance, "events", "ix_events_context_parent_id") + _drop_index(session_maker, "events", "ix_events_context_user_id") + _drop_index(session_maker, "events", "ix_events_context_parent_id") _add_columns( - instance, + session_maker, "states", [ "origin_idx INTEGER", @@ -667,14 +701,14 @@ def _apply_update(instance, new_version, old_version): # noqa: C901 "context_parent_id VARCHAR(36)", ], ) - _create_index(instance, "states", "ix_states_context_id") + _create_index(session_maker, "states", "ix_states_context_id") # Once there are no longer any state_changed events # in the events table we can drop the index on states.event_id else: raise ValueError(f"No schema migration defined for version {new_version}") -def _inspect_schema_version(session): +def _inspect_schema_version(session: Session) -> int: """Determine the schema version by inspecting the db structure. When the schema version is not present in the db, either db was just @@ -696,4 +730,4 @@ def _inspect_schema_version(session): # Version 1 schema changes not found, this db needs to be migrated. current_version = SchemaChanges(schema_version=0) session.add(current_version) - return current_version.schema_version + return cast(int, current_version.schema_version) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index 3a0e2e6e141..b2547d13e45 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -47,7 +47,7 @@ def purge_old_data( ) using_sqlite = instance.using_sqlite() - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: # Purge a max of MAX_ROWS_TO_PURGE, based on the oldest states or events record ( event_ids, @@ -515,7 +515,7 @@ def _purge_filtered_events( def purge_entity_data(instance: Recorder, entity_filter: Callable[[str], bool]) -> bool: """Purge states and events of specified entities.""" using_sqlite = instance.using_sqlite() - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: selected_entity_ids: list[str] = [ entity_id for (entity_id,) in session.query(distinct(States.entity_id)).all() diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 1c993b32bb6..9104fb7e234 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -377,7 +377,7 @@ def _delete_duplicates_from_table( return (total_deleted_rows, all_non_identical_duplicates) -def delete_duplicates(instance: Recorder, session: Session) -> None: +def delete_duplicates(hass: HomeAssistant, session: Session) -> None: """Identify and delete duplicated statistics. A backup will be made of duplicated statistics before it is deleted. @@ -391,7 +391,7 @@ def delete_duplicates(instance: Recorder, session: Session) -> None: if non_identical_duplicates: isotime = dt_util.utcnow().isoformat() backup_file_name = f"deleted_statistics.{isotime}.json" - backup_path = instance.hass.config.path(STORAGE_DIR, backup_file_name) + backup_path = hass.config.path(STORAGE_DIR, backup_file_name) os.makedirs(os.path.dirname(backup_path), exist_ok=True) with open(backup_path, "w", encoding="utf8") as backup_file: @@ -551,7 +551,7 @@ def compile_statistics(instance: Recorder, start: datetime) -> bool: end = start + timedelta(minutes=5) # Return if we already have 5-minute statistics for the requested period - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: if session.query(StatisticsRuns).filter_by(start=start).first(): _LOGGER.debug("Statistics already compiled for %s-%s", start, end) return True @@ -578,7 +578,7 @@ def compile_statistics(instance: Recorder, start: datetime) -> bool: # Insert collected statistics in the database with session_scope( - session=instance.get_session(), # type: ignore[misc] + session=instance.get_session(), exception_filter=_filter_unique_constraint_integrity_error(instance), ) as session: for stats in platform_stats: @@ -768,7 +768,7 @@ def _configured_unit(unit: str | None, units: UnitSystem) -> str | None: def clear_statistics(instance: Recorder, statistic_ids: list[str]) -> None: """Clear statistics for a list of statistic_ids.""" - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: session.query(StatisticsMeta).filter( StatisticsMeta.statistic_id.in_(statistic_ids) ).delete(synchronize_session=False) @@ -778,7 +778,7 @@ def update_statistics_metadata( instance: Recorder, statistic_id: str, unit_of_measurement: str | None ) -> None: """Update statistics metadata for a statistic_id.""" - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: session.query(StatisticsMeta).filter( StatisticsMeta.statistic_id == statistic_id ).update({StatisticsMeta.unit_of_measurement: unit_of_measurement}) @@ -1376,7 +1376,7 @@ def add_external_statistics( """Process an add_external_statistics job.""" with session_scope( - session=instance.get_session(), # type: ignore[misc] + session=instance.get_session(), exception_filter=_filter_unique_constraint_integrity_error(instance), ) as session: old_metadata_dict = get_metadata_with_session( @@ -1403,7 +1403,7 @@ def adjust_statistics( ) -> bool: """Process an add_statistics job.""" - with session_scope(session=instance.get_session()) as session: # type: ignore[misc] + with session_scope(session=instance.get_session()) as session: metadata = get_metadata_with_session( instance.hass, session, statistic_ids=(statistic_id,) ) diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py index bed49e36f16..e12526b316a 100644 --- a/homeassistant/components/recorder/tasks.py +++ b/homeassistant/components/recorder/tasks.py @@ -65,8 +65,6 @@ class PurgeTask(RecorderTask): def run(self, instance: Recorder) -> None: """Purge the database.""" - assert instance.get_session is not None - if purge.purge_old_data( instance, self.purge_before, self.repack, self.apply_filter ): diff --git a/mypy.ini b/mypy.ini index 3d97a716955..81677b8d8ff 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1710,183 +1710,7 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true -[mypy-homeassistant.components.recorder] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.const] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.core] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.backup] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.executor] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.history] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.models] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.pool] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.purge] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.repack] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.run_history] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.services] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.statistics] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.system_health] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.tasks] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.util] -check_untyped_defs = true -disallow_incomplete_defs = true -disallow_subclassing_any = true -disallow_untyped_calls = true -disallow_untyped_decorators = true -disallow_untyped_defs = true -no_implicit_optional = true -warn_return_any = true -warn_unreachable = true - -[mypy-homeassistant.components.recorder.websocket_api] +[mypy-homeassistant.components.recorder.*] check_untyped_defs = true disallow_incomplete_defs = true disallow_subclassing_any = true diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 18b74df0189..17287151bc1 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -138,6 +138,8 @@ async def test_shutdown_closes_connections(hass, recorder_mock): await hass.async_block_till_done() assert len(pool.shutdown.mock_calls) == 1 + with pytest.raises(RuntimeError): + assert instance.get_session() async def test_state_gets_saved_when_set_before_start_event( diff --git a/tests/components/recorder/test_migrate.py b/tests/components/recorder/test_migrate.py index 0a95d174d66..fcc35938088 100644 --- a/tests/components/recorder/test_migrate.py +++ b/tests/components/recorder/test_migrate.py @@ -60,9 +60,12 @@ async def test_schema_update_calls(hass): await async_wait_recording_done(hass) assert recorder.util.async_migration_in_progress(hass) is False + instance = recorder.get_instance(hass) + engine = instance.engine + session_maker = instance.get_session update.assert_has_calls( [ - call(hass.data[DATA_INSTANCE], version + 1, 0) + call(hass, engine, session_maker, version + 1, 0) for version in range(0, models.SCHEMA_VERSION) ] ) @@ -327,10 +330,10 @@ async def test_schema_migrate(hass, start_version): assert recorder.util.async_migration_in_progress(hass) is not True -def test_invalid_update(): +def test_invalid_update(hass): """Test that an invalid new version raises an exception.""" with pytest.raises(ValueError): - migration._apply_update(Mock(), -1, 0) + migration._apply_update(hass, Mock(), Mock(), -1, 0) @pytest.mark.parametrize( @@ -351,7 +354,9 @@ def test_modify_column(engine_type, substr): instance.get_session = Mock(return_value=session) engine = Mock() engine.dialect.name = engine_type - migration._modify_columns(instance, engine, "events", ["event_type VARCHAR(64)"]) + migration._modify_columns( + instance.get_session, engine, "events", ["event_type VARCHAR(64)"] + ) if substr: assert substr in connection.execute.call_args[0][0].text else: @@ -365,8 +370,12 @@ def test_forgiving_add_column(): session.execute(text("CREATE TABLE hello (id int)")) instance = Mock() instance.get_session = Mock(return_value=session) - migration._add_columns(instance, "hello", ["context_id CHARACTER(36)"]) - migration._add_columns(instance, "hello", ["context_id CHARACTER(36)"]) + migration._add_columns( + instance.get_session, "hello", ["context_id CHARACTER(36)"] + ) + migration._add_columns( + instance.get_session, "hello", ["context_id CHARACTER(36)"] + ) def test_forgiving_add_index(): @@ -376,7 +385,7 @@ def test_forgiving_add_index(): with Session(engine) as session: instance = Mock() instance.get_session = Mock(return_value=session) - migration._create_index(instance, "states", "ix_states_context_id") + migration._create_index(instance.get_session, "states", "ix_states_context_id") @pytest.mark.parametrize( diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index dc13f2abb6a..765364a7487 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -740,7 +740,7 @@ def test_delete_duplicates_no_duplicates(hass_recorder, caplog): hass = hass_recorder() wait_recording_done(hass) with session_scope(hass=hass) as session: - delete_duplicates(hass.data[DATA_INSTANCE], session) + delete_duplicates(hass, session) assert "duplicated statistics rows" not in caplog.text assert "Found non identical" not in caplog.text assert "Found duplicated" not in caplog.text From 2b3fc97020460705a9c9164bd9b9e3fccb382d1a Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 5 May 2022 00:22:23 +0000 Subject: [PATCH 214/930] [ci skip] Translation update --- .../application_credentials/translations/id.json | 3 +++ .../application_credentials/translations/nl.json | 3 +++ .../components/asuswrt/translations/ca.json | 3 +++ .../components/asuswrt/translations/de.json | 3 +++ .../components/asuswrt/translations/el.json | 3 +++ .../components/asuswrt/translations/en.json | 4 +++- .../components/asuswrt/translations/et.json | 3 +++ .../components/asuswrt/translations/fr.json | 3 ++- .../components/asuswrt/translations/hu.json | 2 ++ .../components/asuswrt/translations/id.json | 3 +++ .../components/asuswrt/translations/nl.json | 2 ++ .../components/asuswrt/translations/no.json | 2 ++ .../components/asuswrt/translations/pl.json | 2 ++ .../components/asuswrt/translations/pt-BR.json | 1 + .../components/asuswrt/translations/ru.json | 3 +++ .../components/asuswrt/translations/zh-Hant.json | 2 ++ .../components/deconz/translations/id.json | 1 + .../components/isy994/translations/id.json | 13 +++++++++++-- .../components/meater/translations/id.json | 9 +++++++++ .../components/meater/translations/nl.json | 6 +++++- homeassistant/components/ps4/translations/de.json | 2 +- .../components/recorder/translations/id.json | 8 ++++++++ .../components/recorder/translations/pt-BR.json | 4 ++-- homeassistant/components/senz/translations/bg.json | 5 +++++ homeassistant/components/senz/translations/de.json | 6 +++--- .../components/simplisafe/translations/bg.json | 3 +++ .../components/simplisafe/translations/id.json | 4 ++++ .../components/simplisafe/translations/nl.json | 2 +- .../components/smappee/translations/de.json | 2 +- homeassistant/components/sql/translations/id.json | 4 ++++ .../components/steam_online/translations/id.json | 4 ++-- .../trafikverket_ferry/translations/bg.json | 1 + homeassistant/components/unifi/translations/id.json | 5 ++++- 33 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/id.json create mode 100644 homeassistant/components/application_credentials/translations/nl.json create mode 100644 homeassistant/components/recorder/translations/id.json diff --git a/homeassistant/components/application_credentials/translations/id.json b/homeassistant/components/application_credentials/translations/id.json new file mode 100644 index 00000000000..b4ce4f295fd --- /dev/null +++ b/homeassistant/components/application_credentials/translations/id.json @@ -0,0 +1,3 @@ +{ + "title": "Kredensial Aplikasi" +} \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/nl.json b/homeassistant/components/application_credentials/translations/nl.json new file mode 100644 index 00000000000..c186d95ce27 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/nl.json @@ -0,0 +1,3 @@ +{ + "title": "Applicatiegegevens" +} \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/ca.json b/homeassistant/components/asuswrt/translations/ca.json index 9149cd1ae7a..93821419fc4 100644 --- a/homeassistant/components/asuswrt/translations/ca.json +++ b/homeassistant/components/asuswrt/translations/ca.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "No s'ha pogut determinar cap identificador \u00fanic v\u00e0lid del dispositiu", + "no_unique_id": "Ja s'ha configurat un dispositiu sense un identificador \u00fanic v\u00e0lid. La configuraci\u00f3 de diverses inst\u00e0ncies no \u00e9s possible", + "not_unique_id_exist": "Ja s'ha configurat un dispositiu sense un identificador \u00fanic v\u00e0lid. La configuraci\u00f3 de diverses inst\u00e0ncies no \u00e9s possible", "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/de.json b/homeassistant/components/asuswrt/translations/de.json index af4beb984a4..1a0dc26903d 100644 --- a/homeassistant/components/asuswrt/translations/de.json +++ b/homeassistant/components/asuswrt/translations/de.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Unm\u00f6glich, eine g\u00fcltige eindeutige Kennung f\u00fcr das Ger\u00e4t zu ermitteln", + "no_unique_id": "Ein Ger\u00e4t ohne g\u00fcltige, eindeutige ID ist bereits konfiguriert. Die Konfiguration mehrerer Instanzen ist nicht m\u00f6glich", + "not_unique_id_exist": "Ein Ger\u00e4t ohne g\u00fcltige, eindeutige ID ist bereits konfiguriert. Die Konfiguration mehrerer Instanzen ist nicht m\u00f6glich", "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/el.json b/homeassistant/components/asuswrt/translations/el.json index ea926f10bd2..bb18890091c 100644 --- a/homeassistant/components/asuswrt/translations/el.json +++ b/homeassistant/components/asuswrt/translations/el.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "\u0391\u03b4\u03cd\u03bd\u03b1\u03c4\u03bf\u03c2 \u03bf \u03c0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c5 \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03bf\u03cd \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", + "no_unique_id": "\u039c\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", + "not_unique_id_exist": "\u039c\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index f93eae80e6d..93e8f8869b0 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -2,7 +2,9 @@ "config": { "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" + "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", + "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", + "single_instance_allowed": "Already configured. Only a single configuration possible." }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/asuswrt/translations/et.json b/homeassistant/components/asuswrt/translations/et.json index 7b7a0869061..03dc665fcf9 100644 --- a/homeassistant/components/asuswrt/translations/et.json +++ b/homeassistant/components/asuswrt/translations/et.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Seadme kehtivat kordumatut ID-d on v\u00f5imatu m\u00e4\u00e4rata", + "no_unique_id": "Seade, millel puudub kehtiv kordumatu ID, on juba seadistatud. Mitme eksemplari seadistamine pole v\u00f5imalik", + "not_unique_id_exist": "Seade, millel puudub kehtiv kordumatu ID, on juba seadistatud. Mitme eksemplari seadistamine pole v\u00f5imalik", "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/fr.json b/homeassistant/components/asuswrt/translations/fr.json index 69a7705789e..9cb849632b3 100644 --- a/homeassistant/components/asuswrt/translations/fr.json +++ b/homeassistant/components/asuswrt/translations/fr.json @@ -2,7 +2,8 @@ "config": { "abort": { "invalid_unique_id": "Impossible de d\u00e9terminer un identifiant unique valide pour l'appareil", - "not_unique_id_exist": "Un appareil sans UniqueID valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", + "no_unique_id": "Un appareil sans identifiant unique valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", + "not_unique_id_exist": "Un appareil sans identifiant unique valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/hu.json b/homeassistant/components/asuswrt/translations/hu.json index d8133061380..8cec2c31736 100644 --- a/homeassistant/components/asuswrt/translations/hu.json +++ b/homeassistant/components/asuswrt/translations/hu.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Lehetetlen \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3t meghat\u00e1rozni az eszk\u00f6zh\u00f6z", + "not_unique_id_exist": "Egy \u00e9rv\u00e9nyes UniqueID azonos\u00edt\u00f3val nem rendelkez\u0151 eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/id.json b/homeassistant/components/asuswrt/translations/id.json index 83ade7b6462..249744a1ebe 100644 --- a/homeassistant/components/asuswrt/translations/id.json +++ b/homeassistant/components/asuswrt/translations/id.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Penentuan ID unik yang valid untuk perangkat tidak dimungkinkan", + "no_unique_id": "Perangkat tanpa ID unik yang valid sudah dikonfigurasi. Konfigurasi beberapa instans tidak dimungkinkan", + "not_unique_id_exist": "Perangkat tanpa ID unik yang valid sudah dikonfigurasi. Konfigurasi beberapa instans tidak dimungkinkan", "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/nl.json b/homeassistant/components/asuswrt/translations/nl.json index e6db5fae621..26d95e16461 100644 --- a/homeassistant/components/asuswrt/translations/nl.json +++ b/homeassistant/components/asuswrt/translations/nl.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Onmogelijk om een geldige unieke id voor het apparaat te bepalen", + "not_unique_id_exist": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk", "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/no.json b/homeassistant/components/asuswrt/translations/no.json index 84ece53ed42..b68e771646f 100644 --- a/homeassistant/components/asuswrt/translations/no.json +++ b/homeassistant/components/asuswrt/translations/no.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Umulig \u00e5 bestemme en gyldig unik ID for enheten", + "not_unique_id_exist": "En enhet uten en gyldig UniqueID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/pl.json b/homeassistant/components/asuswrt/translations/pl.json index 76d8f30d950..ce0fc53accf 100644 --- a/homeassistant/components/asuswrt/translations/pl.json +++ b/homeassistant/components/asuswrt/translations/pl.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "Nie mo\u017cna okre\u015bli\u0107 prawid\u0142owego unikalnego identyfikatora urz\u0105dzenia", + "not_unique_id_exist": "Urz\u0105dzenie bez prawid\u0142owego unikatowego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa", "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/pt-BR.json b/homeassistant/components/asuswrt/translations/pt-BR.json index 74e48b53ba9..24f2ab51305 100644 --- a/homeassistant/components/asuswrt/translations/pt-BR.json +++ b/homeassistant/components/asuswrt/translations/pt-BR.json @@ -2,6 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Imposs\u00edvel determinar um ID exclusivo v\u00e1lido para o dispositivo", + "no_unique_id": "Um dispositivo sem um ID exclusivo v\u00e1lido j\u00e1 est\u00e1 configurado. A configura\u00e7\u00e3o de v\u00e1rias inst\u00e2ncias n\u00e3o \u00e9 poss\u00edvel", "not_unique_id_exist": "Um dispositivo sem um ID exclusivo v\u00e1lido j\u00e1 est\u00e1 configurado. A configura\u00e7\u00e3o de v\u00e1rias inst\u00e2ncias n\u00e3o \u00e9 poss\u00edvel", "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, diff --git a/homeassistant/components/asuswrt/translations/ru.json b/homeassistant/components/asuswrt/translations/ru.json index 8edc9786f5b..923faf174a5 100644 --- a/homeassistant/components/asuswrt/translations/ru.json +++ b/homeassistant/components/asuswrt/translations/ru.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", + "no_unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0435\u0437 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430.", + "not_unique_id_exist": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0435\u0437 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430.", "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/zh-Hant.json b/homeassistant/components/asuswrt/translations/zh-Hant.json index 17c5cd698cd..f78a0466a5c 100644 --- a/homeassistant/components/asuswrt/translations/zh-Hant.json +++ b/homeassistant/components/asuswrt/translations/zh-Hant.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "invalid_unique_id": "\u7121\u6cd5\u78ba\u8a8d\u88dd\u7f6e\u6709\u6548\u552f\u4e00 ID", + "not_unique_id_exist": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b", "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, "error": { diff --git a/homeassistant/components/deconz/translations/id.json b/homeassistant/components/deconz/translations/id.json index f63261e6e87..e76e8b98d55 100644 --- a/homeassistant/components/deconz/translations/id.json +++ b/homeassistant/components/deconz/translations/id.json @@ -9,6 +9,7 @@ "updated_instance": "Instans deCONZ yang diperbarui dengan alamat host baru" }, "error": { + "linking_not_possible": "Tidak dapat terhubung dengan gateway", "no_key": "Tidak bisa mendapatkan kunci API" }, "flow_title": "{host}", diff --git a/homeassistant/components/isy994/translations/id.json b/homeassistant/components/isy994/translations/id.json index aeea471514c..f27a8c41f5c 100644 --- a/homeassistant/components/isy994/translations/id.json +++ b/homeassistant/components/isy994/translations/id.json @@ -7,10 +7,19 @@ "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", "invalid_host": "Entri host tidak dalam format URL lengkap, misalnya, http://192.168.10.100:80", + "reauth_successful": "Autentikasi ulang berhasil", "unknown": "Kesalahan yang tidak diharapkan" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Kata Sandi", + "username": "Nama Pengguna" + }, + "description": "Kredensial untuk {host} tidak lagi valid.", + "title": "Autentikasi ulang ISY Anda" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Nama Pengguna" }, "description": "Entri host harus dalam format URL lengkap, misalnya, http://192.168.10.100:80", - "title": "Hubungkan ke ISY994 Anda" + "title": "Hubungkan ke ISY Anda" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "String Sensor Variabel" }, "description": "Mengatur opsi untuk Integrasi ISY: \n \u2022 String Sensor Node: Setiap perangkat atau folder yang berisi 'String Sensor Node' dalam nama akan diperlakukan sebagai sensor atau sensor biner. \n \u2022 Abaikan String: Setiap perangkat dengan 'Abaikan String' dalam nama akan diabaikan. \n \u2022 String Sensor Variabel: Variabel apa pun yang berisi 'String Sensor Variabel' akan ditambahkan sebagai sensor. \n \u2022 Pulihkan Kecerahan Cahaya: Jika diaktifkan, kecerahan sebelumnya akan dipulihkan saat menyalakan lampu alih-alih bawaan perangkat On-Level.", - "title": "Opsi ISY994" + "title": "Opsi ISY" } } }, diff --git a/homeassistant/components/meater/translations/id.json b/homeassistant/components/meater/translations/id.json index 5d9e28c583c..e27bc97fce5 100644 --- a/homeassistant/components/meater/translations/id.json +++ b/homeassistant/components/meater/translations/id.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Kesalahan yang tidak diharapkan" }, "step": { + "reauth_confirm": { + "data": { + "password": "Kata Sandi" + }, + "description": "Konfirmasikan kata sandi untuk akun Meater Cloud {username}." + }, "user": { "data": { "password": "Kata Sandi", "username": "Nama Pengguna" }, + "data_description": { + "username": "Nama pengguna Meater Cloud, biasanya berupa alamat email." + }, "description": "Siapkan akun Meater Cloud Anda." } } diff --git a/homeassistant/components/meater/translations/nl.json b/homeassistant/components/meater/translations/nl.json index 3261272c7f9..05165dbaa8a 100644 --- a/homeassistant/components/meater/translations/nl.json +++ b/homeassistant/components/meater/translations/nl.json @@ -9,13 +9,17 @@ "reauth_confirm": { "data": { "password": "Wachtwoord" - } + }, + "description": "Bevestig het wachtwoord voor Meater Cloud account {username}." }, "user": { "data": { "password": "Wachtwoord", "username": "Gebruikersnaam" }, + "data_description": { + "username": "Meater Cloud gebruikersnaam, meestal een e-mailadres." + }, "description": "Stel uw Meater Cloud-account in." } } diff --git a/homeassistant/components/ps4/translations/de.json b/homeassistant/components/ps4/translations/de.json index 22a50cdf3e3..94012575c06 100644 --- a/homeassistant/components/ps4/translations/de.json +++ b/homeassistant/components/ps4/translations/de.json @@ -15,7 +15,7 @@ }, "step": { "creds": { - "description": "Anmeldeinformationen ben\u00f6tigt. Klicke auf \"Senden\" und dann in der PS4 Second Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\"-Ger\u00e4t aus, um fortzufahren.", + "description": "Anmeldeinformationen ben\u00f6tigt. Klicke auf \"Senden\" und dann in der PS4 2nd Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\"-Ger\u00e4t aus, um fortzufahren.", "title": "PlayStation 4" }, "link": { diff --git a/homeassistant/components/recorder/translations/id.json b/homeassistant/components/recorder/translations/id.json new file mode 100644 index 00000000000..9d3b4c333ef --- /dev/null +++ b/homeassistant/components/recorder/translations/id.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Waktu Mulai Jalankan Saat Ini", + "oldest_recorder_run": "Waktu Mulai Lari Terlama" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/pt-BR.json b/homeassistant/components/recorder/translations/pt-BR.json index 78d43d14e52..c44ad73c28b 100644 --- a/homeassistant/components/recorder/translations/pt-BR.json +++ b/homeassistant/components/recorder/translations/pt-BR.json @@ -1,8 +1,8 @@ { "system_health": { "info": { - "current_recorder_run": "Hora atual de in\u00edcio de execu\u00e7\u00e3o", - "oldest_recorder_run": "Hora antiga de in\u00edcio de execu\u00e7\u00e3o" + "current_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o atual", + "oldest_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o mais antiga" } } } \ No newline at end of file diff --git a/homeassistant/components/senz/translations/bg.json b/homeassistant/components/senz/translations/bg.json index 916271d14d0..a99746433a0 100644 --- a/homeassistant/components/senz/translations/bg.json +++ b/homeassistant/components/senz/translations/bg.json @@ -3,6 +3,11 @@ "abort": { "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430." + }, + "step": { + "pick_implementation": { + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + } } } } \ No newline at end of file diff --git a/homeassistant/components/senz/translations/de.json b/homeassistant/components/senz/translations/de.json index 16c6d8a883d..ffbc7bb458f 100644 --- a/homeassistant/components/senz/translations/de.json +++ b/homeassistant/components/senz/translations/de.json @@ -4,16 +4,16 @@ "already_configured": "Konto wurde bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", - "missing_configuration": "Diese Komponente ist nicht konfiguriert. Bitte in der Anleitung nachlesen.", + "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", "oauth_error": "Ung\u00fcltige Token-Daten empfangen." }, "create_entry": { - "default": "Anmeldung erfolgreich" + "default": "Erfolgreich authentifiziert" }, "step": { "pick_implementation": { - "title": "Art der Anmeldung ausw\u00e4hlen" + "title": "W\u00e4hle die Authentifizierungsmethode" } } } diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index 56cf0e346f1..d193393c534 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -8,6 +8,9 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, + "progress": { + "email_2fa": "\u041f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u0442\u0430 \u0441\u0438 \u043f\u043e\u0449\u0430 \u0437\u0430 \u0432\u0440\u044a\u0437\u043a\u0430 \u0437\u0430 \u043f\u043e\u0442\u0432\u044a\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043e\u0442 Simplisafe." + }, "step": { "reauth_confirm": { "data": { diff --git a/homeassistant/components/simplisafe/translations/id.json b/homeassistant/components/simplisafe/translations/id.json index ad0ad01be63..737831195f1 100644 --- a/homeassistant/components/simplisafe/translations/id.json +++ b/homeassistant/components/simplisafe/translations/id.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Akun SimpliSafe ini sudah digunakan.", + "email_2fa_timed_out": "Tenggang waktu habis ketika menunggu autentikasi dua faktor berbasis email.", "reauth_successful": "Autentikasi ulang berhasil", "wrong_account": "Kredensial pengguna yang diberikan tidak cocok dengan akun SimpliSafe ini." }, @@ -12,6 +13,9 @@ "still_awaiting_mfa": "Masih menunggu pengeklikan dari email MFA", "unknown": "Kesalahan yang tidak diharapkan" }, + "progress": { + "email_2fa": "Periksa email Anda untuk tautan verifikasi dari Simplisafe." + }, "step": { "mfa": { "title": "Autentikasi Multi-Faktor SimpliSafe" diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index 5ed0292b2d0..ff282832e4c 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -14,7 +14,7 @@ "unknown": "Onverwachte fout" }, "progress": { - "email_2fa": "Voer de twee-factor authenticatie code in\ndie u per e-mail is toegezonden." + "email_2fa": "Controleer uw e-mail voor een verificatielink van Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/smappee/translations/de.json b/homeassistant/components/smappee/translations/de.json index 127c0f4d725..121b74e9627 100644 --- a/homeassistant/components/smappee/translations/de.json +++ b/homeassistant/components/smappee/translations/de.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured_device": "Ger\u00e4t ist bereits konfiguriert", - "already_configured_local_device": "Lokale(s) Ger\u00e4t(e) ist / sind bereits konfiguriert. Bitte entferne diese zuerst, bevor du ein Cloud-Ger\u00e4t konfigurierst.", + "already_configured_local_device": "Lokale(s) Ger\u00e4t(e) ist/sind bereits konfiguriert. Bitte entferne diese zuerst, bevor du ein Cloud-Ger\u00e4t konfigurierst.", "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", "cannot_connect": "Verbindung fehlgeschlagen", "invalid_mdns": "Nicht unterst\u00fctztes Ger\u00e4t f\u00fcr die Smappee-Integration.", diff --git a/homeassistant/components/sql/translations/id.json b/homeassistant/components/sql/translations/id.json index 9cd3574c6c9..8680824e467 100644 --- a/homeassistant/components/sql/translations/id.json +++ b/homeassistant/components/sql/translations/id.json @@ -13,6 +13,7 @@ "data": { "column": "Kolom", "db_url": "URL Database", + "name": "Nama", "query": "Kueri Select", "unit_of_measurement": "Satuan Ukuran", "value_template": "Templat Nilai" @@ -20,6 +21,7 @@ "data_description": { "column": "Kolom pada kueri yang dikembalikan untuk ditampilkan sebagai status", "db_url": "URL database, kosongkan untuk menggunakan database HA default", + "name": "Nama yang akan digunakan untuk Entri Konfigurasi dan juga Sensor", "query": "Kueri untuk dijalankan, perlu dimulai dengan 'SELECT'", "unit_of_measurement": "Satuan Ukuran (opsional)", "value_template": "Template Nilai (opsional)" @@ -38,6 +40,7 @@ "data": { "column": "Kolom", "db_url": "URL Database", + "name": "Nama", "query": "Kueri Select", "unit_of_measurement": "Satuan Ukuran", "value_template": "Templat Nilai" @@ -45,6 +48,7 @@ "data_description": { "column": "Kolom pada kueri yang dikembalikan untuk ditampilkan sebagai status", "db_url": "URL database, kosongkan untuk menggunakan database HA default", + "name": "Nama yang akan digunakan untuk Entri Konfigurasi dan juga Sensor", "query": "Kueri untuk dijalankan, perlu dimulai dengan 'SELECT'", "unit_of_measurement": "Satuan Ukuran (opsional)", "value_template": "Template Nilai (opsional)" diff --git a/homeassistant/components/steam_online/translations/id.json b/homeassistant/components/steam_online/translations/id.json index 07d708067fc..abc61d42194 100644 --- a/homeassistant/components/steam_online/translations/id.json +++ b/homeassistant/components/steam_online/translations/id.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "Integrasi Steam perlu diautentikasi ulang secara manual \n\n Anda dapat menemukan kunci Anda di sini: https://steamcommunity.com/dev/apikey", + "description": "Integrasi Steam perlu diautentikasi ulang secara manual \n\nAnda dapat menemukan kunci Anda di sini: {api_key_url}", "title": "Autentikasi Ulang Integrasi" }, "user": { @@ -20,7 +20,7 @@ "account": "ID akun Steam", "api_key": "Kunci API" }, - "description": "Gunakan https://steamid.io untuk menemukan ID akun Steam Anda" + "description": "Gunakan {account_id_url} untuk menemukan ID akun Steam Anda" } } }, diff --git a/homeassistant/components/trafikverket_ferry/translations/bg.json b/homeassistant/components/trafikverket_ferry/translations/bg.json index 4e93f090b87..72b2aa2cf7b 100644 --- a/homeassistant/components/trafikverket_ferry/translations/bg.json +++ b/homeassistant/components/trafikverket_ferry/translations/bg.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "incorrect_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447 \u0437\u0430 \u0438\u0437\u0431\u0440\u0430\u043d\u0438\u044f \u0430\u043a\u0430\u0443\u043d\u0442", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "step": { diff --git a/homeassistant/components/unifi/translations/id.json b/homeassistant/components/unifi/translations/id.json index 4c78c96155c..f3618a31c02 100644 --- a/homeassistant/components/unifi/translations/id.json +++ b/homeassistant/components/unifi/translations/id.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Situs Jaringan UniFi sudah dikonfigurasi", - "configuration_updated": "Konfigurasi diperbarui.", + "configuration_updated": "Konfigurasi diperbarui", "reauth_successful": "Autentikasi ulang berhasil" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "Integrasi UniFi tidak disiapkan" + }, "step": { "client_control": { "data": { From d67f19f8a404dffb5c053a7c9232a931ef134647 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 01:10:27 -0400 Subject: [PATCH 215/930] Fix lutron caseta occupancy sensors (#71309) * Fix lutron_caseta occupancy sensors * Fix lutron_caseta occupancy sensors * Make as service since its a group * merge * Revert "merge" This reverts commit 69d19dc0088bd1b3483cfc481ed2f72e49599cf8. * model and type not present --- .../components/lutron_caseta/__init__.py | 5 ++- .../components/lutron_caseta/binary_sensor.py | 34 ++++++++++++------- .../components/lutron_caseta/const.py | 2 ++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index bb8f94f3abe..3d9e07519a8 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -38,6 +38,7 @@ from .const import ( CONF_CA_CERTS, CONF_CERTFILE, CONF_KEYFILE, + CONFIG_URL, DOMAIN, LUTRON_CASETA_BUTTON_EVENT, MANUFACTURER, @@ -306,13 +307,15 @@ class LutronCasetaDevice(Entity): self._device = device self._smartbridge = bridge self._bridge_device = bridge_device + if "serial" not in self._device: + return info = DeviceInfo( identifiers={(DOMAIN, self.serial)}, manufacturer=MANUFACTURER, model=f"{device['model']} ({device['type']})", name=self.name, via_device=(DOMAIN, self._bridge_device["serial"]), - configuration_url="https://device-login.lutron.com", + configuration_url=CONFIG_URL, ) area, _ = _area_and_name_from_name(device["name"]) if area != UNASSIGNED_AREA: diff --git a/homeassistant/components/lutron_caseta/binary_sensor.py b/homeassistant/components/lutron_caseta/binary_sensor.py index 788702f9353..f61e644a331 100644 --- a/homeassistant/components/lutron_caseta/binary_sensor.py +++ b/homeassistant/components/lutron_caseta/binary_sensor.py @@ -6,11 +6,14 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntity, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_SUGGESTED_AREA from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice -from .const import BRIDGE_DEVICE, BRIDGE_LEAP +from . import DOMAIN as CASETA_DOMAIN, LutronCasetaDevice, _area_and_name_from_name +from .const import BRIDGE_DEVICE, BRIDGE_LEAP, CONFIG_URL, MANUFACTURER, UNASSIGNED_AREA async def async_setup_entry( @@ -39,6 +42,23 @@ async def async_setup_entry( class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity): """Representation of a Lutron occupancy group.""" + def __init__(self, device, bridge, bridge_device): + """Init an occupancy sensor.""" + super().__init__(device, bridge, bridge_device) + info = DeviceInfo( + identifiers={(CASETA_DOMAIN, self.unique_id)}, + manufacturer=MANUFACTURER, + model="Lutron Occupancy", + name=self.name, + via_device=(CASETA_DOMAIN, self._bridge_device["serial"]), + configuration_url=CONFIG_URL, + entry_type=DeviceEntryType.SERVICE, + ) + area, _ = _area_and_name_from_name(device["name"]) + if area != UNASSIGNED_AREA: + info[ATTR_SUGGESTED_AREA] = area + self._attr_device_info = info + @property def device_class(self): """Flag supported features.""" @@ -65,16 +85,6 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity): """Return a unique identifier.""" return f"occupancygroup_{self.device_id}" - @property - def device_info(self): - """Return the device info. - - Sensor entities are aggregated from one or more physical - sensors by each room. Therefore, there shouldn't be devices - related to any sensor entities. - """ - return None - @property def extra_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/lutron_caseta/const.py b/homeassistant/components/lutron_caseta/const.py index 56a3821dd64..71d686ba2c8 100644 --- a/homeassistant/components/lutron_caseta/const.py +++ b/homeassistant/components/lutron_caseta/const.py @@ -35,3 +35,5 @@ CONF_SUBTYPE = "subtype" BRIDGE_TIMEOUT = 35 UNASSIGNED_AREA = "Unassigned" + +CONFIG_URL = "https://device-login.lutron.com" From 1da3b5048b4d18f77b6a112636c2cb1871060491 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 01:13:23 -0400 Subject: [PATCH 216/930] Ensure rachio retries setup later when cloud service is broken (#71300) --- homeassistant/components/rachio/__init__.py | 6 +++++- homeassistant/components/rachio/device.py | 19 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rachio/__init__.py b/homeassistant/components/rachio/__init__.py index ea8b8fe59cb..e75d7117d73 100644 --- a/homeassistant/components/rachio/__init__.py +++ b/homeassistant/components/rachio/__init__.py @@ -9,7 +9,7 @@ from homeassistant.components import cloud from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv from .const import CONF_CLOUDHOOK_URL, CONF_MANUAL_RUN_MINS, CONF_WEBHOOK_ID, DOMAIN @@ -73,6 +73,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Get the API user try: await person.async_setup(hass) + except ConfigEntryAuthFailed as error: + # Reauth is not yet implemented + _LOGGER.error("Authentication failed: %s", error) + return False except ConnectTimeout as error: _LOGGER.error("Could not reach the Rachio API: %s", error) raise ConfigEntryNotReady from error diff --git a/homeassistant/components/rachio/device.py b/homeassistant/components/rachio/device.py index ff7c0535295..911049883d9 100644 --- a/homeassistant/components/rachio/device.py +++ b/homeassistant/components/rachio/device.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import ServiceCall +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv from .const import ( @@ -125,12 +126,18 @@ class RachioPerson: rachio = self.rachio response = rachio.person.info() - assert int(response[0][KEY_STATUS]) == HTTPStatus.OK, "API key error" + if is_invalid_auth_code(int(response[0][KEY_STATUS])): + raise ConfigEntryAuthFailed(f"API key error: {response}") + if int(response[0][KEY_STATUS]) != HTTPStatus.OK: + raise ConfigEntryNotReady(f"API Error: {response}") self._id = response[1][KEY_ID] # Use user ID to get user data data = rachio.person.get(self._id) - assert int(data[0][KEY_STATUS]) == HTTPStatus.OK, "User ID error" + if is_invalid_auth_code(int(data[0][KEY_STATUS])): + raise ConfigEntryAuthFailed(f"User ID error: {data}") + if int(data[0][KEY_STATUS]) != HTTPStatus.OK: + raise ConfigEntryNotReady(f"API Error: {data}") self.username = data[1][KEY_USERNAME] devices = data[1][KEY_DEVICES] for controller in devices: @@ -297,3 +304,11 @@ class RachioIro: """Resume paused watering on this controller.""" self.rachio.device.resume_zone_run(self.controller_id) _LOGGER.debug("Resuming watering on %s", self) + + +def is_invalid_auth_code(http_status_code): + """HTTP status codes that mean invalid auth.""" + if http_status_code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN): + return True + + return False From db08c04da6f08dbaa29b57e7f8a03bb692ace7b0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 5 May 2022 07:15:24 +0200 Subject: [PATCH 217/930] Only test for EncryptedBridge in Samsung J/H models (#71291) --- .../components/samsungtv/__init__.py | 14 +++++----- homeassistant/components/samsungtv/bridge.py | 27 ++++++++++++------- tests/components/samsungtv/conftest.py | 4 +-- .../components/samsungtv/test_config_flow.py | 24 ++++++++--------- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py index dae4033ad4c..a7b8f7d1aec 100644 --- a/homeassistant/components/samsungtv/__init__.py +++ b/homeassistant/components/samsungtv/__init__.py @@ -30,7 +30,12 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.typing import ConfigType -from .bridge import SamsungTVBridge, async_get_device_info, mac_from_device_info +from .bridge import ( + SamsungTVBridge, + async_get_device_info, + mac_from_device_info, + model_requires_encryption, +) from .const import ( CONF_ON_ACTION, CONF_SESSION_ID, @@ -214,11 +219,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True -def _model_requires_encryption(model: str | None) -> bool: - """H and J models need pairing with PIN.""" - return model is not None and len(model) > 4 and model[4] in ("H", "J") - - async def _async_create_bridge_with_updated_data( hass: HomeAssistant, entry: ConfigEntry ) -> SamsungTVBridge: @@ -279,7 +279,7 @@ async def _async_create_bridge_with_updated_data( LOGGER.info("Updated model to %s for %s", model, host) updated_data[CONF_MODEL] = model - if _model_requires_encryption(model) and method != METHOD_ENCRYPTED_WEBSOCKET: + if model_requires_encryption(model) and method != METHOD_ENCRYPTED_WEBSOCKET: LOGGER.info( "Detected model %s for %s. Some televisions from H and J series use " "an encrypted protocol but you are using %s which may not be supported", diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index 52ab86337dd..c3201a493eb 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -85,6 +85,11 @@ def mac_from_device_info(info: dict[str, Any]) -> str | None: return None +def model_requires_encryption(model: str | None) -> bool: + """H and J models need pairing with PIN.""" + return model is not None and len(model) > 4 and model[4] in ("H", "J") + + async def async_get_device_info( hass: HomeAssistant, host: str, @@ -99,17 +104,19 @@ async def async_get_device_info( port, info, ) - encrypted_bridge = SamsungTVEncryptedBridge( - hass, METHOD_ENCRYPTED_WEBSOCKET, host, ENCRYPTED_WEBSOCKET_PORT - ) - result = await encrypted_bridge.async_try_connect() - if result != RESULT_CANNOT_CONNECT: - return ( - result, - ENCRYPTED_WEBSOCKET_PORT, - METHOD_ENCRYPTED_WEBSOCKET, - info, + # Check the encrypted port if the model requires encryption + if model_requires_encryption(info.get("device", {}).get("modelName")): + encrypted_bridge = SamsungTVEncryptedBridge( + hass, METHOD_ENCRYPTED_WEBSOCKET, host, ENCRYPTED_WEBSOCKET_PORT ) + result = await encrypted_bridge.async_try_connect() + if result != RESULT_CANNOT_CONNECT: + return ( + result, + ENCRYPTED_WEBSOCKET_PORT, + METHOD_ENCRYPTED_WEBSOCKET, + info, + ) return RESULT_SUCCESS, port, METHOD_WEBSOCKET, info # Try legacy port diff --git a/tests/components/samsungtv/conftest.py b/tests/components/samsungtv/conftest.py index d7f8ed0d1a1..764022f3501 100644 --- a/tests/components/samsungtv/conftest.py +++ b/tests/components/samsungtv/conftest.py @@ -22,7 +22,7 @@ from samsungtvws.remote import ChannelEmitCommand from homeassistant.components.samsungtv.const import WEBSOCKET_SSL_PORT import homeassistant.util.dt as dt_util -from .const import SAMPLE_DEVICE_INFO_WIFI +from .const import SAMPLE_DEVICE_INFO_UE48JU6400, SAMPLE_DEVICE_INFO_WIFI @pytest.fixture(autouse=True) @@ -177,7 +177,7 @@ def rest_api_fixture_non_ssl_only() -> Mock: """Mock rest_device_info to fail for ssl and work for non-ssl.""" if self.port == WEBSOCKET_SSL_PORT: raise ResponseError - return SAMPLE_DEVICE_INFO_WIFI + return SAMPLE_DEVICE_INFO_UE48JU6400 with patch( "homeassistant.components.samsungtv.bridge.SamsungTVAsyncRest", diff --git a/tests/components/samsungtv/test_config_flow.py b/tests/components/samsungtv/test_config_flow.py index d2a9d10caf2..40397a68d7d 100644 --- a/tests/components/samsungtv/test_config_flow.py +++ b/tests/components/samsungtv/test_config_flow.py @@ -340,16 +340,16 @@ async def test_user_encrypted_websocket( ) assert result4["type"] == "create_entry" - assert result4["title"] == "Living Room (82GXARRS)" + assert result4["title"] == "TV-UE48JU6470 (UE48JU6400)" assert result4["data"][CONF_HOST] == "fake_host" - assert result4["data"][CONF_NAME] == "Living Room" + assert result4["data"][CONF_NAME] == "TV-UE48JU6470" assert result4["data"][CONF_MAC] == "aa:bb:ww:ii:ff:ii" assert result4["data"][CONF_MANUFACTURER] == "Samsung" - assert result4["data"][CONF_MODEL] == "82GXARRS" + assert result4["data"][CONF_MODEL] == "UE48JU6400" assert result4["data"][CONF_SSDP_RENDERING_CONTROL_LOCATION] is None assert result4["data"][CONF_TOKEN] == "037739871315caef138547b03e348b72" assert result4["data"][CONF_SESSION_ID] == "1" - assert result4["result"].unique_id == "be9554b9-c9fb-41f4-8920-22da015376a4" + assert result4["result"].unique_id == "223da676-497a-4e06-9507-5e27ec4f0fb3" @pytest.mark.usefixtures("rest_api_failing") @@ -714,19 +714,19 @@ async def test_ssdp_encrypted_websocket_success_populates_mac_address_and_ssdp_l ) assert result4["type"] == "create_entry" - assert result4["title"] == "Living Room (82GXARRS)" + assert result4["title"] == "TV-UE48JU6470 (UE48JU6400)" assert result4["data"][CONF_HOST] == "fake_host" - assert result4["data"][CONF_NAME] == "Living Room" + assert result4["data"][CONF_NAME] == "TV-UE48JU6470" assert result4["data"][CONF_MAC] == "aa:bb:ww:ii:ff:ii" assert result4["data"][CONF_MANUFACTURER] == "Samsung fake_manufacturer" - assert result4["data"][CONF_MODEL] == "82GXARRS" + assert result4["data"][CONF_MODEL] == "UE48JU6400" assert ( result4["data"][CONF_SSDP_RENDERING_CONTROL_LOCATION] == "https://fake_host:12345/test" ) assert result4["data"][CONF_TOKEN] == "037739871315caef138547b03e348b72" assert result4["data"][CONF_SESSION_ID] == "1" - assert result4["result"].unique_id == "be9554b9-c9fb-41f4-8920-22da015376a4" + assert result4["result"].unique_id == "223da676-497a-4e06-9507-5e27ec4f0fb3" @pytest.mark.usefixtures("rest_api_non_ssl_only") @@ -1036,13 +1036,13 @@ async def test_dhcp_wireless(hass: HomeAssistant) -> None: result["flow_id"], user_input="whatever" ) assert result["type"] == "create_entry" - assert result["title"] == "Living Room (82GXARRS)" + assert result["title"] == "TV-UE48JU6470 (UE48JU6400)" assert result["data"][CONF_HOST] == "fake_host" - assert result["data"][CONF_NAME] == "Living Room" + assert result["data"][CONF_NAME] == "TV-UE48JU6470" assert result["data"][CONF_MAC] == "aa:bb:ww:ii:ff:ii" assert result["data"][CONF_MANUFACTURER] == "Samsung" - assert result["data"][CONF_MODEL] == "82GXARRS" - assert result["result"].unique_id == "be9554b9-c9fb-41f4-8920-22da015376a4" + assert result["data"][CONF_MODEL] == "UE48JU6400" + assert result["result"].unique_id == "223da676-497a-4e06-9507-5e27ec4f0fb3" @pytest.mark.usefixtures("remotews", "rest_api", "remoteencws_failing") From eebf3acb93507f8f706f8043d57fdfd09942a750 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Thu, 5 May 2022 15:22:15 +1000 Subject: [PATCH 218/930] Relax dlna_dmr filtering when browsing media (#69576) * Fix incorrect types of test data structures * Loosen MIME-type filtering for async_browse_media * Add option to not filter results when browsing media Some devices do not report all that they support, and in this case filtering will hide media that's actually playable. Most devices are OK, though, and it's better to hide what they can't play. Add an option, off by default, to show all media. * Fix linting issues --- .../components/dlna_dmr/config_flow.py | 21 ++-- homeassistant/components/dlna_dmr/const.py | 1 + .../components/dlna_dmr/media_player.py | 39 +++++-- .../components/dlna_dmr/strings.json | 3 +- .../components/dlna_dmr/translations/en.json | 1 + tests/components/dlna_dmr/test_config_flow.py | 14 ++- .../components/dlna_dmr/test_media_player.py | 102 ++++++++++++++++++ 7 files changed, 157 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/dlna_dmr/config_flow.py b/homeassistant/components/dlna_dmr/config_flow.py index ac7e3c83253..c7b37537e2b 100644 --- a/homeassistant/components/dlna_dmr/config_flow.py +++ b/homeassistant/components/dlna_dmr/config_flow.py @@ -21,6 +21,7 @@ from homeassistant.exceptions import IntegrationError import homeassistant.helpers.config_validation as cv from .const import ( + CONF_BROWSE_UNFILTERED, CONF_CALLBACK_URL_OVERRIDE, CONF_LISTEN_PORT, CONF_POLL_AVAILABILITY, @@ -328,6 +329,7 @@ class DlnaDmrOptionsFlowHandler(config_entries.OptionsFlow): options[CONF_LISTEN_PORT] = listen_port options[CONF_CALLBACK_URL_OVERRIDE] = callback_url_override options[CONF_POLL_AVAILABILITY] = user_input[CONF_POLL_AVAILABILITY] + options[CONF_BROWSE_UNFILTERED] = user_input[CONF_BROWSE_UNFILTERED] # Save if there's no errors, else fall through and show the form again if not errors: @@ -335,9 +337,14 @@ class DlnaDmrOptionsFlowHandler(config_entries.OptionsFlow): fields = {} - def _add_with_suggestion(key: str, validator: Callable) -> None: - """Add a field to with a suggested, not default, value.""" - if (suggested_value := options.get(key)) is None: + def _add_with_suggestion(key: str, validator: Callable | type[bool]) -> None: + """Add a field to with a suggested value. + + For bools, use the existing value as default, or fallback to False. + """ + if validator is bool: + fields[vol.Required(key, default=options.get(key, False))] = validator + elif (suggested_value := options.get(key)) is None: fields[vol.Optional(key)] = validator else: fields[ @@ -347,12 +354,8 @@ class DlnaDmrOptionsFlowHandler(config_entries.OptionsFlow): # listen_port can be blank or 0 for "bind any free port" _add_with_suggestion(CONF_LISTEN_PORT, cv.port) _add_with_suggestion(CONF_CALLBACK_URL_OVERRIDE, str) - fields[ - vol.Required( - CONF_POLL_AVAILABILITY, - default=options.get(CONF_POLL_AVAILABILITY, False), - ) - ] = bool + _add_with_suggestion(CONF_POLL_AVAILABILITY, bool) + _add_with_suggestion(CONF_BROWSE_UNFILTERED, bool) return self.async_show_form( step_id="init", diff --git a/homeassistant/components/dlna_dmr/const.py b/homeassistant/components/dlna_dmr/const.py index a4118a0ce78..4f9982061fb 100644 --- a/homeassistant/components/dlna_dmr/const.py +++ b/homeassistant/components/dlna_dmr/const.py @@ -16,6 +16,7 @@ DOMAIN: Final = "dlna_dmr" CONF_LISTEN_PORT: Final = "listen_port" CONF_CALLBACK_URL_OVERRIDE: Final = "callback_url_override" CONF_POLL_AVAILABILITY: Final = "poll_availability" +CONF_BROWSE_UNFILTERED: Final = "browse_unfiltered" DEFAULT_NAME: Final = "DLNA Digital Media Renderer" diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index 9a7eef4bf89..fd1fc9b2bab 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -45,6 +45,7 @@ from homeassistant.helpers import device_registry, entity_registry from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( + CONF_BROWSE_UNFILTERED, CONF_CALLBACK_URL_OVERRIDE, CONF_LISTEN_PORT, CONF_POLL_AVAILABILITY, @@ -108,6 +109,7 @@ async def async_setup_entry( event_callback_url=entry.options.get(CONF_CALLBACK_URL_OVERRIDE), poll_availability=entry.options.get(CONF_POLL_AVAILABILITY, False), location=entry.data[CONF_URL], + browse_unfiltered=entry.options.get(CONF_BROWSE_UNFILTERED, False), ) async_add_entities([entity]) @@ -124,6 +126,8 @@ class DlnaDmrEntity(MediaPlayerEntity): # Last known URL for the device, used when adding this entity to hass to try # to connect before SSDP has rediscovered it, or when SSDP discovery fails. location: str + # Should the async_browse_media function *not* filter out incompatible media? + browse_unfiltered: bool _device_lock: asyncio.Lock # Held when connecting or disconnecting the device _device: DmrDevice | None = None @@ -146,6 +150,7 @@ class DlnaDmrEntity(MediaPlayerEntity): event_callback_url: str | None, poll_availability: bool, location: str, + browse_unfiltered: bool, ) -> None: """Initialize DLNA DMR entity.""" self.udn = udn @@ -154,6 +159,7 @@ class DlnaDmrEntity(MediaPlayerEntity): self._event_addr = EventListenAddr(None, event_port, event_callback_url) self.poll_availability = poll_availability self.location = location + self.browse_unfiltered = browse_unfiltered self._device_lock = asyncio.Lock() async def async_added_to_hass(self) -> None: @@ -275,6 +281,7 @@ class DlnaDmrEntity(MediaPlayerEntity): ) self.location = entry.data[CONF_URL] self.poll_availability = entry.options.get(CONF_POLL_AVAILABILITY, False) + self.browse_unfiltered = entry.options.get(CONF_BROWSE_UNFILTERED, False) new_port = entry.options.get(CONF_LISTEN_PORT) or 0 new_callback_url = entry.options.get(CONF_CALLBACK_URL_OVERRIDE) @@ -762,14 +769,21 @@ class DlnaDmrEntity(MediaPlayerEntity): # media_content_type is ignored; it's the content_type of the current # media_content_id, not the desired content_type of whomever is calling. - content_filter = self._get_content_filter() + if self.browse_unfiltered: + content_filter = None + else: + content_filter = self._get_content_filter() return await media_source.async_browse_media( self.hass, media_content_id, content_filter=content_filter ) def _get_content_filter(self) -> Callable[[BrowseMedia], bool]: - """Return a function that filters media based on what the renderer can play.""" + """Return a function that filters media based on what the renderer can play. + + The filtering is pretty loose; it's better to show something that can't + be played than hide something that can. + """ if not self._device or not self._device.sink_protocol_info: # Nothing is specified by the renderer, so show everything _LOGGER.debug("Get content filter with no device or sink protocol info") @@ -780,18 +794,25 @@ class DlnaDmrEntity(MediaPlayerEntity): # Renderer claims it can handle everything, so show everything return lambda _: True - # Convert list of things like "http-get:*:audio/mpeg:*" to just "audio/mpeg" - content_types: list[str] = [] + # Convert list of things like "http-get:*:audio/mpeg;codecs=mp3:*" + # to just "audio/mpeg" + content_types = set[str]() for protocol_info in self._device.sink_protocol_info: protocol, _, content_format, _ = protocol_info.split(":", 3) + # Transform content_format for better generic matching + content_format = content_format.lower().replace("/x-", "/", 1) + content_format = content_format.partition(";")[0] + if protocol in STREAMABLE_PROTOCOLS: - content_types.append(content_format) + content_types.add(content_format) - def _content_type_filter(item: BrowseMedia) -> bool: - """Filter media items by their content_type.""" - return item.media_content_type in content_types + def _content_filter(item: BrowseMedia) -> bool: + """Filter media items by their media_content_type.""" + content_type = item.media_content_type + content_type = content_type.lower().replace("/x-", "/", 1).partition(";")[0] + return content_type in content_types - return _content_type_filter + return _content_filter @property def media_title(self) -> str | None: diff --git a/homeassistant/components/dlna_dmr/strings.json b/homeassistant/components/dlna_dmr/strings.json index ac77009e0cb..d646f20f7a1 100644 --- a/homeassistant/components/dlna_dmr/strings.json +++ b/homeassistant/components/dlna_dmr/strings.json @@ -44,7 +44,8 @@ "data": { "listen_port": "Event listener port (random if not set)", "callback_url_override": "Event listener callback URL", - "poll_availability": "Poll for device availability" + "poll_availability": "Poll for device availability", + "browse_unfiltered": "Show incompatible media when browsing" } } }, diff --git a/homeassistant/components/dlna_dmr/translations/en.json b/homeassistant/components/dlna_dmr/translations/en.json index 512dfe7f11c..51cbd875211 100644 --- a/homeassistant/components/dlna_dmr/translations/en.json +++ b/homeassistant/components/dlna_dmr/translations/en.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Show incompatible media when browsing", "callback_url_override": "Event listener callback URL", "listen_port": "Event listener port (random if not set)", "poll_availability": "Poll for device availability" diff --git a/tests/components/dlna_dmr/test_config_flow.py b/tests/components/dlna_dmr/test_config_flow.py index 6112c7c5ed5..7ec25906f99 100644 --- a/tests/components/dlna_dmr/test_config_flow.py +++ b/tests/components/dlna_dmr/test_config_flow.py @@ -11,6 +11,7 @@ import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components import ssdp from homeassistant.components.dlna_dmr.const import ( + CONF_BROWSE_UNFILTERED, CONF_CALLBACK_URL_OVERRIDE, CONF_LISTEN_PORT, CONF_POLL_AVAILABILITY, @@ -74,7 +75,7 @@ MOCK_DISCOVERY = ssdp.SsdpServiceInfo( ] }, }, - x_homeassistant_matching_domains=(DLNA_DOMAIN,), + x_homeassistant_matching_domains={DLNA_DOMAIN}, ) @@ -390,7 +391,7 @@ async def test_ssdp_missing_services(hass: HomeAssistant) -> None: """Test SSDP ignores devices that are missing required services.""" # No service list at all discovery = dataclasses.replace(MOCK_DISCOVERY) - discovery.upnp = discovery.upnp.copy() + discovery.upnp = dict(discovery.upnp) del discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST] result = await hass.config_entries.flow.async_init( DLNA_DOMAIN, @@ -414,7 +415,7 @@ async def test_ssdp_missing_services(hass: HomeAssistant) -> None: # AVTransport service is missing discovery = dataclasses.replace(MOCK_DISCOVERY) - discovery.upnp = discovery.upnp.copy() + discovery.upnp = dict(discovery.upnp) discovery.upnp[ssdp.ATTR_UPNP_SERVICE_LIST] = { "service": [ service @@ -465,7 +466,7 @@ async def test_ssdp_ignore_device(hass: HomeAssistant) -> None: assert result["reason"] == "alternative_integration" discovery = dataclasses.replace(MOCK_DISCOVERY) - discovery.upnp = discovery.upnp.copy() + discovery.upnp = dict(discovery.upnp) discovery.upnp[ ssdp.ATTR_UPNP_DEVICE_TYPE ] = "urn:schemas-upnp-org:device:ZonePlayer:1" @@ -484,7 +485,7 @@ async def test_ssdp_ignore_device(hass: HomeAssistant) -> None: ("Royal Philips Electronics", "Philips TV DMR"), ]: discovery = dataclasses.replace(MOCK_DISCOVERY) - discovery.upnp = discovery.upnp.copy() + discovery.upnp = dict(discovery.upnp) discovery.upnp[ssdp.ATTR_UPNP_MANUFACTURER] = manufacturer discovery.upnp[ssdp.ATTR_UPNP_MODEL_NAME] = model result = await hass.config_entries.flow.async_init( @@ -592,6 +593,7 @@ async def test_options_flow( user_input={ CONF_CALLBACK_URL_OVERRIDE: "Bad url", CONF_POLL_AVAILABILITY: False, + CONF_BROWSE_UNFILTERED: False, }, ) @@ -606,6 +608,7 @@ async def test_options_flow( CONF_LISTEN_PORT: 2222, CONF_CALLBACK_URL_OVERRIDE: "http://override/callback", CONF_POLL_AVAILABILITY: True, + CONF_BROWSE_UNFILTERED: True, }, ) @@ -614,4 +617,5 @@ async def test_options_flow( CONF_LISTEN_PORT: 2222, CONF_CALLBACK_URL_OVERRIDE: "http://override/callback", CONF_POLL_AVAILABILITY: True, + CONF_BROWSE_UNFILTERED: True, } diff --git a/tests/components/dlna_dmr/test_media_player.py b/tests/components/dlna_dmr/test_media_player.py index bed89f3db9d..eef5d936396 100644 --- a/tests/components/dlna_dmr/test_media_player.py +++ b/tests/components/dlna_dmr/test_media_player.py @@ -23,6 +23,7 @@ from homeassistant import const as ha_const from homeassistant.components import ssdp from homeassistant.components.dlna_dmr import media_player from homeassistant.components.dlna_dmr.const import ( + CONF_BROWSE_UNFILTERED, CONF_CALLBACK_URL_OVERRIDE, CONF_LISTEN_PORT, CONF_POLL_AVAILABILITY, @@ -997,6 +998,26 @@ async def test_browse_media( # Audio file should appear assert expected_child_audio in response["result"]["children"] + # Device specifies extra parameters in MIME type, uses non-standard "x-" + # prefix, and capitilizes things, all of which should be ignored + dmr_device_mock.sink_protocol_info = [ + "http-get:*:audio/X-MPEG;codecs=mp3:*", + ] + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "media_player/browse_media", + "entity_id": mock_entity_id, + } + ) + response = await client.receive_json() + assert response["success"] + # Video file should not be shown + assert expected_child_video not in response["result"]["children"] + # Audio file should appear + assert expected_child_audio in response["result"]["children"] + # Device does not specify what it can play dmr_device_mock.sink_protocol_info = [] client = await hass_ws_client() @@ -1014,6 +1035,87 @@ async def test_browse_media( assert expected_child_audio in response["result"]["children"] +async def test_browse_media_unfiltered( + hass: HomeAssistant, + hass_ws_client, + config_entry_mock: MockConfigEntry, + dmr_device_mock: Mock, + mock_entity_id: str, +) -> None: + """Test the async_browse_media method with filtering turned off and on.""" + # Based on cast's test_entity_browse_media + await async_setup_component(hass, MS_DOMAIN, {MS_DOMAIN: {}}) + await hass.async_block_till_done() + + expected_child_video = { + "title": "Epic Sax Guy 10 Hours.mp4", + "media_class": "video", + "media_content_type": "video/mp4", + "media_content_id": "media-source://media_source/local/Epic Sax Guy 10 Hours.mp4", + "can_play": True, + "can_expand": False, + "thumbnail": None, + "children_media_class": None, + } + expected_child_audio = { + "title": "test.mp3", + "media_class": "music", + "media_content_type": "audio/mpeg", + "media_content_id": "media-source://media_source/local/test.mp3", + "can_play": True, + "can_expand": False, + "thumbnail": None, + "children_media_class": None, + } + + # Device can only play MIME type audio/mpeg and audio/vorbis + dmr_device_mock.sink_protocol_info = [ + "http-get:*:audio/mpeg:*", + "http-get:*:audio/vorbis:*", + ] + + # Filtering turned on by default + assert CONF_BROWSE_UNFILTERED not in config_entry_mock.options + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "media_player/browse_media", + "entity_id": mock_entity_id, + } + ) + response = await client.receive_json() + assert response["success"] + # Video file should not be shown + assert expected_child_video not in response["result"]["children"] + # Audio file should appear + assert expected_child_audio in response["result"]["children"] + + # Filtering turned off via config entry + hass.config_entries.async_update_entry( + config_entry_mock, + options={ + CONF_BROWSE_UNFILTERED: True, + }, + ) + await hass.async_block_till_done() + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "media_player/browse_media", + "entity_id": mock_entity_id, + } + ) + response = await client.receive_json() + assert response["success"] + # All files should be returned + assert expected_child_video in response["result"]["children"] + assert expected_child_audio in response["result"]["children"] + + async def test_playback_update_state( hass: HomeAssistant, dmr_device_mock: Mock, mock_entity_id: str ) -> None: From a0474633fd944a79731945a0f62ac36ebbd4740b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 4 May 2022 23:52:00 -0700 Subject: [PATCH 219/930] Fix apple tv warning (#71321) --- homeassistant/components/apple_tv/media_player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index 6c1b35f70f8..5a7298dcbee 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -296,7 +296,7 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): _LOGGER.debug("Streaming %s via RAOP", media_id) await self.atv.stream.stream_file(media_id) - if self._is_feature_available(FeatureName.PlayUrl): + elif self._is_feature_available(FeatureName.PlayUrl): _LOGGER.debug("Playing %s via AirPlay", media_id) await self.atv.stream.play_url(media_id) else: From e8f0d80fc988d8e43bb1eff27cb45288d446f1c1 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 5 May 2022 08:52:20 +0200 Subject: [PATCH 220/930] Fix Meater (#71324) --- homeassistant/components/meater/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index 17e8db9e473..8c719d588d8 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -146,13 +146,13 @@ async def async_setup_entry( if not coordinator.last_update_success: return - devices = coordinator.data + devices: dict[str, MeaterProbe] = coordinator.data entities = [] known_probes: set = hass.data[DOMAIN]["known_probes"] # Add entities for temperature probes which we've not yet seen for dev in devices: - if dev.id in known_probes: + if dev in known_probes: continue entities.extend( @@ -161,7 +161,7 @@ async def async_setup_entry( for sensor_description in SENSOR_TYPES ] ) - known_probes.add(dev.id) + known_probes.add(dev) async_add_entities(entities) From 6a35c3f2ab0fbebbdb94a31d26a8babcd133afef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 5 May 2022 09:06:23 +0200 Subject: [PATCH 221/930] Update aioairzone to v0.4.3 (#71312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update aioairzone to v0.4.3 Fixes exception on older local API. Signed-off-by: Álvaro Fernández Rojas * airzone: switch to set_hvac_parameters function Fixes failing airzone tests. Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/climate.py | 2 +- homeassistant/components/airzone/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/airzone/test_climate.py | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/airzone/climate.py b/homeassistant/components/airzone/climate.py index 1ec7dfabcfa..9cff1ff393d 100644 --- a/homeassistant/components/airzone/climate.py +++ b/homeassistant/components/airzone/climate.py @@ -123,7 +123,7 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity): } _LOGGER.debug("update_hvac_params=%s", _params) try: - await self.coordinator.airzone.put_hvac(_params) + await self.coordinator.airzone.set_hvac_parameters(_params) except AirzoneError as error: raise HomeAssistantError( f"Failed to set zone {self.name}: {error}" diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index a6ea814fe9c..7a04b3a78b3 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -3,7 +3,7 @@ "name": "Airzone", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airzone", - "requirements": ["aioairzone==0.4.2"], + "requirements": ["aioairzone==0.4.3"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioairzone"] diff --git a/requirements_all.txt b/requirements_all.txt index 16778889248..02b21b18c4b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -110,7 +110,7 @@ aio_geojson_nsw_rfs_incidents==0.4 aio_georss_gdacs==0.7 # homeassistant.components.airzone -aioairzone==0.4.2 +aioairzone==0.4.3 # homeassistant.components.ambient_station aioambient==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6d1a1de2cf6..2e71bda5cf1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -94,7 +94,7 @@ aio_geojson_nsw_rfs_incidents==0.4 aio_georss_gdacs==0.7 # homeassistant.components.airzone -aioairzone==0.4.2 +aioairzone==0.4.3 # homeassistant.components.ambient_station aioambient==2021.11.0 diff --git a/tests/components/airzone/test_climate.py b/tests/components/airzone/test_climate.py index 2128d2818e7..dcb493351ba 100644 --- a/tests/components/airzone/test_climate.py +++ b/tests/components/airzone/test_climate.py @@ -147,7 +147,7 @@ async def test_airzone_climate_turn_on_off(hass: HomeAssistant) -> None: ] } with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK, ): await hass.services.async_call( @@ -172,7 +172,7 @@ async def test_airzone_climate_turn_on_off(hass: HomeAssistant) -> None: ] } with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK, ): await hass.services.async_call( @@ -204,7 +204,7 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: ] } with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK, ): await hass.services.async_call( @@ -230,7 +230,7 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: ] } with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK_2, ): await hass.services.async_call( @@ -263,7 +263,7 @@ async def test_airzone_climate_set_hvac_slave_error(hass: HomeAssistant) -> None await async_init_integration(hass) with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK, ), pytest.raises(HomeAssistantError): await hass.services.async_call( @@ -296,7 +296,7 @@ async def test_airzone_climate_set_temp(hass: HomeAssistant) -> None: await async_init_integration(hass) with patch( - "homeassistant.components.airzone.AirzoneLocalApi.http_request", + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", return_value=HVAC_MOCK, ): await hass.services.async_call( From e145d3c65bb3401f96903d492fb24e5600fdfedb Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 5 May 2022 10:12:39 +0200 Subject: [PATCH 222/930] Bump numpy to 1.21.6 (#71325) --- homeassistant/components/compensation/manifest.json | 2 +- homeassistant/components/iqvia/manifest.json | 2 +- homeassistant/components/opencv/manifest.json | 2 +- homeassistant/components/tensorflow/manifest.json | 2 +- homeassistant/components/trend/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/compensation/manifest.json b/homeassistant/components/compensation/manifest.json index 315d1b705df..213e8888e23 100644 --- a/homeassistant/components/compensation/manifest.json +++ b/homeassistant/components/compensation/manifest.json @@ -2,7 +2,7 @@ "domain": "compensation", "name": "Compensation", "documentation": "https://www.home-assistant.io/integrations/compensation", - "requirements": ["numpy==1.21.4"], + "requirements": ["numpy==1.21.6"], "codeowners": ["@Petro31"], "iot_class": "calculated" } diff --git a/homeassistant/components/iqvia/manifest.json b/homeassistant/components/iqvia/manifest.json index 50ddeb3bba7..9bb07157b54 100644 --- a/homeassistant/components/iqvia/manifest.json +++ b/homeassistant/components/iqvia/manifest.json @@ -3,7 +3,7 @@ "name": "IQVIA", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/iqvia", - "requirements": ["numpy==1.21.4", "pyiqvia==2022.04.0"], + "requirements": ["numpy==1.21.6", "pyiqvia==2022.04.0"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["pyiqvia"] diff --git a/homeassistant/components/opencv/manifest.json b/homeassistant/components/opencv/manifest.json index 9c1f51c4933..504b83bdaf9 100644 --- a/homeassistant/components/opencv/manifest.json +++ b/homeassistant/components/opencv/manifest.json @@ -2,7 +2,7 @@ "domain": "opencv", "name": "OpenCV", "documentation": "https://www.home-assistant.io/integrations/opencv", - "requirements": ["numpy==1.21.4", "opencv-python-headless==4.5.2.54"], + "requirements": ["numpy==1.21.6", "opencv-python-headless==4.5.2.54"], "codeowners": [], "iot_class": "local_push" } diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index 5f1ac406b70..0f53dd61cb4 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -6,7 +6,7 @@ "tensorflow==2.5.0", "tf-models-official==2.5.0", "pycocotools==2.0.1", - "numpy==1.21.4", + "numpy==1.21.6", "pillow==9.1.0" ], "codeowners": [], diff --git a/homeassistant/components/trend/manifest.json b/homeassistant/components/trend/manifest.json index 831e97aed3d..aaae8f7cc54 100644 --- a/homeassistant/components/trend/manifest.json +++ b/homeassistant/components/trend/manifest.json @@ -2,7 +2,7 @@ "domain": "trend", "name": "Trend", "documentation": "https://www.home-assistant.io/integrations/trend", - "requirements": ["numpy==1.21.4"], + "requirements": ["numpy==1.21.6"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 02b21b18c4b..7faa9dad813 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1114,7 +1114,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.21.4 +numpy==1.21.6 # homeassistant.components.oasa_telematics oasatelematics==0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2e71bda5cf1..89192ad4f2f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -758,7 +758,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.21.4 +numpy==1.21.6 # homeassistant.components.google oauth2client==4.1.3 From 22d258759324a3b16aae7a2f4f4a14ed3dbeedc5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 5 May 2022 11:24:43 +0200 Subject: [PATCH 223/930] Tweak Meater typing and variable naming (#71333) --- homeassistant/components/meater/sensor.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index 8c719d588d8..8a6c07bcbc4 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -136,9 +136,9 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the entry.""" - coordinator: DataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id][ - "coordinator" - ] + coordinator: DataUpdateCoordinator[dict[str, MeaterProbe]] = hass.data[DOMAIN][ + entry.entry_id + ]["coordinator"] @callback def async_update_data(): @@ -146,22 +146,22 @@ async def async_setup_entry( if not coordinator.last_update_success: return - devices: dict[str, MeaterProbe] = coordinator.data + devices = coordinator.data entities = [] known_probes: set = hass.data[DOMAIN]["known_probes"] # Add entities for temperature probes which we've not yet seen - for dev in devices: - if dev in known_probes: + for device_id in devices: + if device_id in known_probes: continue entities.extend( [ - MeaterProbeTemperature(coordinator, dev, sensor_description) + MeaterProbeTemperature(coordinator, device_id, sensor_description) for sensor_description in SENSOR_TYPES ] ) - known_probes.add(dev) + known_probes.add(device_id) async_add_entities(entities) From 248f01f41f58682abc75a26f8ae16bd1516fba97 Mon Sep 17 00:00:00 2001 From: Markus Bong Date: Thu, 5 May 2022 11:36:00 +0200 Subject: [PATCH 224/930] fix reading of battery messages (#70659) --- .../components/devolo_home_control/devolo_device.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/devolo_home_control/devolo_device.py b/homeassistant/components/devolo_home_control/devolo_device.py index b3cb68098e1..6087e07799d 100644 --- a/homeassistant/components/devolo_home_control/devolo_device.py +++ b/homeassistant/components/devolo_home_control/devolo_device.py @@ -7,6 +7,7 @@ from urllib.parse import urlparse from devolo_home_control_api.devices.zwave import Zwave from devolo_home_control_api.homecontrol import HomeControl +from homeassistant.components.sensor import SensorDeviceClass from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN @@ -71,7 +72,11 @@ class DevoloDeviceEntity(Entity): def _generic_message(self, message: tuple) -> None: """Handle generic messages.""" - if len(message) == 3 and message[2] == "battery_level": + if ( + len(message) == 3 + and message[2] == "battery_level" + and self.device_class == SensorDeviceClass.BATTERY + ): self._value = message[1] elif len(message) == 3 and message[2] == "status": # Maybe the API wants to tell us, that the device went on- or offline. From 191230f535b053ec76ae1164035de8879dfc2750 Mon Sep 17 00:00:00 2001 From: Antoni Czaplicki <56671347+Antoni-Czaplicki@users.noreply.github.com> Date: Thu, 5 May 2022 14:32:36 +0200 Subject: [PATCH 225/930] Refactor vulcan integration (#71175) --- homeassistant/components/vulcan/__init__.py | 52 +-- homeassistant/components/vulcan/calendar.py | 167 +++------ .../components/vulcan/config_flow.py | 223 +++++------- homeassistant/components/vulcan/const.py | 1 - homeassistant/components/vulcan/fetch_data.py | 1 + homeassistant/components/vulcan/manifest.json | 5 +- homeassistant/components/vulcan/register.py | 5 +- homeassistant/components/vulcan/strings.json | 20 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/vulcan/test_config_flow.py | 344 +++++++----------- 11 files changed, 293 insertions(+), 529 deletions(-) diff --git a/homeassistant/components/vulcan/__init__.py b/homeassistant/components/vulcan/__init__.py index 430819c8f4c..50f525d9a68 100644 --- a/homeassistant/components/vulcan/__init__.py +++ b/homeassistant/components/vulcan/__init__.py @@ -1,18 +1,15 @@ """The Vulcan component.""" -import logging from aiohttp import ClientConnectorError -from vulcan import Account, Keystore, Vulcan -from vulcan._utils import VulcanAPIException +from vulcan import Account, Keystore, UnauthorizedCertificateException, Vulcan from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - PLATFORMS = ["calendar"] @@ -22,54 +19,29 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: keystore = Keystore.load(entry.data["keystore"]) account = Account.load(entry.data["account"]) - client = Vulcan(keystore, account) + client = Vulcan(keystore, account, async_get_clientsession(hass)) await client.select_student() students = await client.get_students() for student in students: if str(student.pupil.id) == str(entry.data["student_id"]): client.student = student break - except VulcanAPIException as err: - if str(err) == "The certificate is not authorized.": - _LOGGER.error( - "The certificate is not authorized, please authorize integration again" - ) - raise ConfigEntryAuthFailed from err - _LOGGER.error("Vulcan API error: %s", err) - return False + except UnauthorizedCertificateException as err: + raise ConfigEntryAuthFailed("The certificate is not authorized.") from err except ClientConnectorError as err: - if "connection_error" not in hass.data[DOMAIN]: - _LOGGER.error( - "Connection error - please check your internet connection: %s", err - ) - hass.data[DOMAIN]["connection_error"] = True - await client.close() - raise ConfigEntryNotReady from err - hass.data[DOMAIN]["students_number"] = len( - hass.config_entries.async_entries(DOMAIN) - ) + raise ConfigEntryNotReady( + f"Connection error - please check your internet connection: {err}" + ) from err hass.data[DOMAIN][entry.entry_id] = client - if not entry.update_listeners: - entry.add_update_listener(_async_update_options) - - for platform in PLATFORMS: - hass.async_create_task( - hass.config_entries.async_forward_entry_setup(entry, platform) - ) + hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - await hass.data[DOMAIN][entry.entry_id].close() - for platform in PLATFORMS: - await hass.config_entries.async_forward_entry_unload(entry, platform) + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) - return True - - -async def _async_update_options(hass, entry): - """Update options.""" - await hass.config_entries.async_reload(entry.entry_id) + return unload_ok diff --git a/homeassistant/components/vulcan/calendar.py b/homeassistant/components/vulcan/calendar.py index 2e9e79063f9..ac302940c72 100644 --- a/homeassistant/components/vulcan/calendar.py +++ b/homeassistant/components/vulcan/calendar.py @@ -1,24 +1,25 @@ """Support for Vulcan Calendar platform.""" -import copy +from __future__ import annotations + from datetime import date, datetime, timedelta import logging from aiohttp import ClientConnectorError -from vulcan._utils import VulcanAPIException +from vulcan import UnauthorizedCertificateException -from homeassistant.components.calendar import ENTITY_ID_FORMAT, CalendarEventDevice +from homeassistant.components.calendar import ( + ENTITY_ID_FORMAT, + CalendarEntity, + CalendarEvent, +) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.template import DATE_STR_FORMAT -from homeassistant.util import Throttle, dt from . import DOMAIN -from .const import DEFAULT_SCAN_INTERVAL from .fetch_data import get_lessons, get_student_info _LOGGER = logging.getLogger(__name__) @@ -29,20 +30,16 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the calendar platform for event devices.""" - VulcanCalendarData.MIN_TIME_BETWEEN_UPDATES = timedelta( - minutes=config_entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL) - ) + """Set up the calendar platform for entity.""" client = hass.data[DOMAIN][config_entry.entry_id] data = { "student_info": await get_student_info( client, config_entry.data.get("student_id") ), - "students_number": hass.data[DOMAIN]["students_number"], } async_add_entities( [ - VulcanCalendarEventDevice( + VulcanCalendarEntity( client, data, generate_entity_id( @@ -55,80 +52,33 @@ async def async_setup_entry( ) -class VulcanCalendarEventDevice(CalendarEventDevice): - """A calendar event device.""" +class VulcanCalendarEntity(CalendarEntity): + """A calendar entity.""" - def __init__(self, client, data, entity_id): - """Create the Calendar event device.""" + def __init__(self, client, data, entity_id) -> None: + """Create the Calendar entity.""" self.student_info = data["student_info"] - self.data = VulcanCalendarData( - client, - self.student_info, - self.hass, - ) - self._event = None + self._event: CalendarEvent | None = None + self.client = client self.entity_id = entity_id self._unique_id = f"vulcan_calendar_{self.student_info['id']}" - - if data["students_number"] == 1: - self._attr_name = "Vulcan calendar" - self.device_name = "Calendar" - else: - self._attr_name = f"Vulcan calendar - {self.student_info['full_name']}" - self.device_name = f"{self.student_info['full_name']}: Calendar" + self._attr_name = f"Vulcan calendar - {self.student_info['full_name']}" self._attr_unique_id = f"vulcan_calendar_{self.student_info['id']}" self._attr_device_info = { "identifiers": {(DOMAIN, f"calendar_{self.student_info['id']}")}, "entry_type": DeviceEntryType.SERVICE, - "name": self.device_name, + "name": f"{self.student_info['full_name']}: Calendar", "model": f"{self.student_info['full_name']} - {self.student_info['class']} {self.student_info['school']}", "manufacturer": "Uonet +", "configuration_url": f"https://uonetplus.vulcan.net.pl/{self.student_info['symbol']}", } @property - def event(self): + def event(self) -> CalendarEvent | None: """Return the next upcoming event.""" return self._event - async def async_get_events(self, hass, start_date, end_date): - """Get all events in a specific time frame.""" - return await self.data.async_get_events(hass, start_date, end_date) - - async def async_update(self): - """Update event data.""" - await self.data.async_update() - event = copy.deepcopy(self.data.event) - if event is None: - self._event = event - return - event["start"] = { - "dateTime": datetime.combine(event["date"], event["time"].from_) - .astimezone(dt.DEFAULT_TIME_ZONE) - .isoformat() - } - event["end"] = { - "dateTime": datetime.combine(event["date"], event["time"].to) - .astimezone(dt.DEFAULT_TIME_ZONE) - .isoformat() - } - self._event = event - - -class VulcanCalendarData: - """Class to utilize calendar service object to get next event.""" - - MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=DEFAULT_SCAN_INTERVAL) - - def __init__(self, client, student_info, hass): - """Set up how we are going to search the Vulcan calendar.""" - self.client = client - self.event = None - self.hass = hass - self.student_info = student_info - self._available = True - - async def async_get_events(self, hass, start_date, end_date): + async def async_get_events(self, hass, start_date, end_date) -> list[CalendarEvent]: """Get all events in a specific time frame.""" try: events = await get_lessons( @@ -136,16 +86,12 @@ class VulcanCalendarData: date_from=start_date, date_to=end_date, ) - except VulcanAPIException as err: - if str(err) == "The certificate is not authorized.": - _LOGGER.error( - "The certificate is not authorized, please authorize integration again" - ) - raise ConfigEntryAuthFailed from err - _LOGGER.error("An API error has occurred: %s", err) - events = [] + except UnauthorizedCertificateException as err: + raise ConfigEntryAuthFailed( + "The certificate is not authorized, please authorize integration again" + ) from err except ClientConnectorError as err: - if self._available: + if self.available: _LOGGER.warning( "Connection error - please check your internet connection: %s", err ) @@ -153,37 +99,27 @@ class VulcanCalendarData: event_list = [] for item in events: - event = { - "uid": item["id"], - "start": { - "dateTime": datetime.combine( - item["date"], item["time"].from_ - ).strftime(DATE_STR_FORMAT) - }, - "end": { - "dateTime": datetime.combine( - item["date"], item["time"].to - ).strftime(DATE_STR_FORMAT) - }, - "summary": item["lesson"], - "location": item["room"], - "description": item["teacher"], - } + event = CalendarEvent( + start=datetime.combine(item["date"], item["time"].from_), + end=datetime.combine(item["date"], item["time"].to), + summary=item["lesson"], + location=item["room"], + description=item["teacher"], + ) event_list.append(event) return event_list - @Throttle(MIN_TIME_BETWEEN_UPDATES) - async def async_update(self): + async def async_update(self) -> None: """Get the latest data.""" try: events = await get_lessons(self.client) - if not self._available: + if not self.available: _LOGGER.info("Restored connection with API") - self._available = True + self._attr_available = True if events == []: events = await get_lessons( @@ -191,22 +127,18 @@ class VulcanCalendarData: date_to=date.today() + timedelta(days=7), ) if events == []: - self.event = None + self._event = None return - except VulcanAPIException as err: - if str(err) == "The certificate is not authorized.": - _LOGGER.error( - "The certificate is not authorized, please authorize integration again" - ) - raise ConfigEntryAuthFailed from err - _LOGGER.error("An API error has occurred: %s", err) - return + except UnauthorizedCertificateException as err: + raise ConfigEntryAuthFailed( + "The certificate is not authorized, please authorize integration again" + ) from err except ClientConnectorError as err: - if self._available: + if self.available: _LOGGER.warning( "Connection error - please check your internet connection: %s", err ) - self._available = False + self._attr_available = False return new_event = min( @@ -216,11 +148,10 @@ class VulcanCalendarData: abs(datetime.combine(d["date"], d["time"].to) - datetime.now()), ), ) - self.event = { - "uid": new_event["id"], - "date": new_event["date"], - "time": new_event["time"], - "summary": new_event["lesson"], - "location": new_event["room"], - "description": new_event["teacher"], - } + self._event = CalendarEvent( + start=datetime.combine(new_event["date"], new_event["time"].from_), + end=datetime.combine(new_event["date"], new_event["time"].to), + summary=new_event["lesson"], + location=new_event["room"], + description=new_event["teacher"], + ) diff --git a/homeassistant/components/vulcan/config_flow.py b/homeassistant/components/vulcan/config_flow.py index ef700560d73..09acb13ea27 100644 --- a/homeassistant/components/vulcan/config_flow.py +++ b/homeassistant/components/vulcan/config_flow.py @@ -3,16 +3,22 @@ import logging from aiohttp import ClientConnectionError import voluptuous as vol -from vulcan import Account, Keystore, Vulcan -from vulcan._utils import VulcanAPIException +from vulcan import ( + Account, + ExpiredTokenException, + InvalidPINException, + InvalidSymbolException, + InvalidTokenException, + Keystore, + UnauthorizedCertificateException, + Vulcan, +) from homeassistant import config_entries -from homeassistant.const import CONF_PIN, CONF_REGION, CONF_SCAN_INTERVAL, CONF_TOKEN -from homeassistant.core import callback -import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_PIN, CONF_REGION, CONF_TOKEN +from homeassistant.helpers.aiohttp_client import async_get_clientsession from . import DOMAIN -from .const import DEFAULT_SCAN_INTERVAL from .register import register _LOGGER = logging.getLogger(__name__) @@ -29,11 +35,11 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - @staticmethod - @callback - def async_get_options_flow(config_entry): - """Get the options flow for this handler.""" - return VulcanOptionsFlowHandler(config_entry) + def __init__(self): + """Initialize config flow.""" + self.account = None + self.keystore = None + self.students = None async def async_step_user(self, user_input=None): """Handle config flow.""" @@ -53,22 +59,14 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_input[CONF_REGION], user_input[CONF_PIN], ) - except VulcanAPIException as err: - if str(err) == "Invalid token!" or str(err) == "Invalid token.": - errors = {"base": "invalid_token"} - elif str(err) == "Expired token.": - errors = {"base": "expired_token"} - elif str(err) == "Invalid PIN.": - errors = {"base": "invalid_pin"} - else: - errors = {"base": "unknown"} - _LOGGER.error(err) - except RuntimeError as err: - if str(err) == "Internal Server Error (ArgumentException)": - errors = {"base": "invalid_symbol"} - else: - errors = {"base": "unknown"} - _LOGGER.error(err) + except InvalidSymbolException: + errors = {"base": "invalid_symbol"} + except InvalidTokenException: + errors = {"base": "invalid_token"} + except InvalidPINException: + errors = {"base": "invalid_pin"} + except ExpiredTokenException: + errors = {"base": "expired_token"} except ClientConnectionError as err: errors = {"base": "cannot_connect"} _LOGGER.error("Connection error: %s", err) @@ -78,12 +76,10 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not errors: account = credentials["account"] keystore = credentials["keystore"] - client = Vulcan(keystore, account) + client = Vulcan(keystore, account, async_get_clientsession(self.hass)) students = await client.get_students() - await client.close() if len(students) > 1: - # pylint:disable=attribute-defined-outside-init self.account = account self.keystore = keystore self.students = students @@ -109,10 +105,10 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_select_student(self, user_input=None): """Allow user to select student.""" errors = {} - students_list = {} + students = {} if self.students is not None: for student in self.students: - students_list[ + students[ str(student.pupil.id) ] = f"{student.pupil.first_name} {student.pupil.last_name}" if user_input is not None: @@ -120,7 +116,7 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(str(student_id)) self._abort_if_unique_id_configured() return self.async_create_entry( - title=students_list[student_id], + title=students[student_id], data={ "student_id": str(student_id), "keystore": self.keystore.as_dict, @@ -128,37 +124,30 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): }, ) - data_schema = { - vol.Required( - "student", - ): vol.In(students_list), - } return self.async_show_form( step_id="select_student", - data_schema=vol.Schema(data_schema), + data_schema=vol.Schema({vol.Required("student"): vol.In(students)}), errors=errors, ) async def async_step_select_saved_credentials(self, user_input=None, errors=None): """Allow user to select saved credentials.""" - credentials_list = {} + + credentials = {} for entry in self.hass.config_entries.async_entries(DOMAIN): - credentials_list[entry.entry_id] = entry.data["account"]["UserName"] + credentials[entry.entry_id] = entry.data["account"]["UserName"] if user_input is not None: entry = self.hass.config_entries.async_get_entry(user_input["credentials"]) keystore = Keystore.load(entry.data["keystore"]) account = Account.load(entry.data["account"]) - client = Vulcan(keystore, account) + client = Vulcan(keystore, account, async_get_clientsession(self.hass)) try: students = await client.get_students() - except VulcanAPIException as err: - if str(err) == "The certificate is not authorized.": - return await self.async_step_auth( - errors={"base": "expired_credentials"} - ) - _LOGGER.error(err) - return await self.async_step_auth(errors={"base": "unknown"}) + except UnauthorizedCertificateException: + return await self.async_step_auth( + errors={"base": "expired_credentials"} + ) except ClientConnectionError as err: _LOGGER.error("Connection error: %s", err) return await self.async_step_select_saved_credentials( @@ -167,8 +156,6 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") return await self.async_step_auth(errors={"base": "unknown"}) - finally: - await client.close() if len(students) == 1: student = students[0] await self.async_set_unique_id(str(student.pupil.id)) @@ -181,7 +168,6 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): "account": account.as_dict, }, ) - # pylint:disable=attribute-defined-outside-init self.account = account self.keystore = keystore self.students = students @@ -190,7 +176,7 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data_schema = { vol.Required( "credentials", - ): vol.In(credentials_list), + ): vol.In(credentials), } return self.async_show_form( step_id="select_saved_credentials", @@ -200,46 +186,46 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_add_next_config_entry(self, user_input=None): """Flow initialized when user is adding next entry of that integration.""" + existing_entries = [] for entry in self.hass.config_entries.async_entries(DOMAIN): existing_entries.append(entry) errors = {} + if user_input is not None: - if user_input["use_saved_credentials"]: - if len(existing_entries) == 1: - keystore = Keystore.load(existing_entries[0].data["keystore"]) - account = Account.load(existing_entries[0].data["account"]) - client = Vulcan(keystore, account) - students = await client.get_students() - await client.close() - new_students = [] - existing_entry_ids = [] - for entry in self.hass.config_entries.async_entries(DOMAIN): - existing_entry_ids.append(entry.data["student_id"]) - for student in students: - if str(student.pupil.id) not in existing_entry_ids: - new_students.append(student) - if not new_students: - return self.async_abort(reason="all_student_already_configured") - if len(new_students) == 1: - await self.async_set_unique_id(str(new_students[0].pupil.id)) - self._abort_if_unique_id_configured() - return self.async_create_entry( - title=f"{new_students[0].pupil.first_name} {new_students[0].pupil.last_name}", - data={ - "student_id": str(new_students[0].pupil.id), - "keystore": keystore.as_dict, - "account": account.as_dict, - }, - ) - # pylint:disable=attribute-defined-outside-init - self.account = account - self.keystore = keystore - self.students = new_students - return await self.async_step_select_student() + if not user_input["use_saved_credentials"]: + return await self.async_step_auth() + if len(existing_entries) > 1: return await self.async_step_select_saved_credentials() - return await self.async_step_auth() + keystore = Keystore.load(existing_entries[0].data["keystore"]) + account = Account.load(existing_entries[0].data["account"]) + client = Vulcan(keystore, account, async_get_clientsession(self.hass)) + students = await client.get_students() + new_students = [] + existing_entry_ids = [] + for entry in self.hass.config_entries.async_entries(DOMAIN): + existing_entry_ids.append(entry.data["student_id"]) + for student in students: + if str(student.pupil.id) not in existing_entry_ids: + new_students.append(student) + if not new_students: + return self.async_abort(reason="all_student_already_configured") + if len(new_students) == 1: + await self.async_set_unique_id(str(new_students[0].pupil.id)) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=f"{new_students[0].pupil.first_name} {new_students[0].pupil.last_name}", + data={ + "student_id": str(new_students[0].pupil.id), + "keystore": keystore.as_dict, + "account": account.as_dict, + }, + ) + self.account = account + self.keystore = keystore + self.students = new_students + return await self.async_step_select_student() data_schema = { vol.Required("use_saved_credentials", default=True): bool, @@ -251,6 +237,10 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) async def async_step_reauth(self, user_input=None): + """Perform reauth upon an API authentication error.""" + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm(self, user_input=None): """Reauthorize integration.""" errors = {} if user_input is not None: @@ -261,22 +251,14 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_input[CONF_REGION], user_input[CONF_PIN], ) - except VulcanAPIException as err: - if str(err) == "Invalid token!" or str(err) == "Invalid token.": - errors["base"] = "invalid_token" - elif str(err) == "Expired token.": - errors["base"] = "expired_token" - elif str(err) == "Invalid PIN.": - errors["base"] = "invalid_pin" - else: - errors["base"] = "unknown" - _LOGGER.error(err) - except RuntimeError as err: - if str(err) == "Internal Server Error (ArgumentException)": - errors["base"] = "invalid_symbol" - else: - errors["base"] = "unknown" - _LOGGER.error(err) + except InvalidSymbolException: + errors = {"base": "invalid_symbol"} + except InvalidTokenException: + errors = {"base": "invalid_token"} + except InvalidPINException: + errors = {"base": "invalid_pin"} + except ExpiredTokenException: + errors = {"base": "expired_token"} except ClientConnectionError as err: errors["base"] = "cannot_connect" _LOGGER.error("Connection error: %s", err) @@ -286,12 +268,12 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if not errors: account = credentials["account"] keystore = credentials["keystore"] - client = Vulcan(keystore, account) + client = Vulcan(keystore, account, async_get_clientsession(self.hass)) students = await client.get_students() - await client.close() existing_entries = [] for entry in self.hass.config_entries.async_entries(DOMAIN): existing_entries.append(entry) + matching_entries = False for student in students: for entry in existing_entries: if str(student.pupil.id) == str(entry.data["student_id"]): @@ -305,38 +287,13 @@ class VulcanFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): }, ) await self.hass.config_entries.async_reload(entry.entry_id) + matching_entries = True + if not matching_entries: + return self.async_abort(reason="no_matching_entries") return self.async_abort(reason="reauth_successful") return self.async_show_form( - step_id="reauth", + step_id="reauth_confirm", data_schema=vol.Schema(LOGIN_SCHEMA), errors=errors, ) - - -class VulcanOptionsFlowHandler(config_entries.OptionsFlow): - """Config flow options for Uonet+ Vulcan.""" - - def __init__(self, config_entry): - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Manage the options.""" - errors = {} - - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - options = { - vol.Optional( - CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), - ): cv.positive_int, - } - - return self.async_show_form( - step_id="init", data_schema=vol.Schema(options), errors=errors - ) diff --git a/homeassistant/components/vulcan/const.py b/homeassistant/components/vulcan/const.py index 938cc4df8cd..4f17d43c342 100644 --- a/homeassistant/components/vulcan/const.py +++ b/homeassistant/components/vulcan/const.py @@ -1,4 +1,3 @@ """Constants for the Vulcan integration.""" DOMAIN = "vulcan" -DEFAULT_SCAN_INTERVAL = 5 diff --git a/homeassistant/components/vulcan/fetch_data.py b/homeassistant/components/vulcan/fetch_data.py index 04da8d125d7..c706bfa805f 100644 --- a/homeassistant/components/vulcan/fetch_data.py +++ b/homeassistant/components/vulcan/fetch_data.py @@ -94,4 +94,5 @@ async def get_student_info(client, student_id): student_info["class"] = student.class_ student_info["school"] = student.school.name student_info["symbol"] = student.symbol + break return student_info diff --git a/homeassistant/components/vulcan/manifest.json b/homeassistant/components/vulcan/manifest.json index 6449f07a3fb..b7f1f5fe4d6 100644 --- a/homeassistant/components/vulcan/manifest.json +++ b/homeassistant/components/vulcan/manifest.json @@ -3,9 +3,8 @@ "name": "Uonet+ Vulcan", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/vulcan", - "requirements": ["vulcan-api==2.0.3"], - "dependencies": [], + "requirements": ["vulcan-api==2.1.1"], "codeowners": ["@Antoni-Czaplicki"], "iot_class": "cloud_polling", - "quality_scale": "platinum" + "quality_scale": "silver" } diff --git a/homeassistant/components/vulcan/register.py b/homeassistant/components/vulcan/register.py index 802805d1db8..67cceb8d7b8 100644 --- a/homeassistant/components/vulcan/register.py +++ b/homeassistant/components/vulcan/register.py @@ -1,13 +1,10 @@ """Support for register Vulcan account.""" -from functools import partial from vulcan import Account, Keystore async def register(hass, token, symbol, pin): """Register integration and save credentials.""" - keystore = await hass.async_add_executor_job( - partial(Keystore.create, device_model="Home Assistant") - ) + keystore = await Keystore.create(device_model="Home Assistant") account = await Account.register(keystore, token, symbol, pin) return {"account": account, "keystore": keystore} diff --git a/homeassistant/components/vulcan/strings.json b/homeassistant/components/vulcan/strings.json index abb3dce7c7f..bb9e1d4d848 100644 --- a/homeassistant/components/vulcan/strings.json +++ b/homeassistant/components/vulcan/strings.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "That student has already been added.", "all_student_already_configured": "All students have already been added.", - "reauth_successful": "Reauth successful" + "reauth_successful": "Reauth successful", + "no_matching_entries": "No matching entries found, please use different account or remove integration with outdated student.." }, "error": { "unknown": "Unknown error occurred", @@ -23,7 +24,7 @@ "pin": "Pin" } }, - "reauth": { + "reauth_confirm": { "description": "Login to your Vulcan Account using mobile app registration page.", "data": { "token": "Token", @@ -50,20 +51,5 @@ } } } - }, - "options": { - "error": { - "error": "Error occurred" - }, - "step": { - "init": { - "data": { - "message_notify": "Show notifications when new message received", - "attendance_notify": "Show notifications about the latest attendance entries", - "grade_notify": "Show notifications about the latest grades", - "scan_interval": "Update interval (in minutes)" - } - } - } } } diff --git a/requirements_all.txt b/requirements_all.txt index 7faa9dad813..53712397c4c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2396,7 +2396,7 @@ vsure==1.7.3 vtjp==0.1.14 # homeassistant.components.vulcan -vulcan-api==2.0.3 +vulcan-api==2.1.1 # homeassistant.components.vultr vultr==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 89192ad4f2f..78f7da53d35 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1563,7 +1563,7 @@ vilfo-api-client==0.3.2 vsure==1.7.3 # homeassistant.components.vulcan -vulcan-api==2.0.3 +vulcan-api==2.1.1 # homeassistant.components.vultr vultr==0.1.2 diff --git a/tests/components/vulcan/test_config_flow.py b/tests/components/vulcan/test_config_flow.py index 91f0dd769c8..20f030bb99f 100644 --- a/tests/components/vulcan/test_config_flow.py +++ b/tests/components/vulcan/test_config_flow.py @@ -3,17 +3,20 @@ import json from unittest import mock from unittest.mock import patch -from vulcan import Account +from vulcan import ( + Account, + ExpiredTokenException, + InvalidPINException, + InvalidSymbolException, + InvalidTokenException, + UnauthorizedCertificateException, +) from vulcan.model import Student from homeassistant import config_entries, data_entry_flow from homeassistant.components.vulcan import config_flow, const, register -from homeassistant.components.vulcan.config_flow import ( - ClientConnectionError, - Keystore, - VulcanAPIException, -) -from homeassistant.const import CONF_PIN, CONF_REGION, CONF_SCAN_INTERVAL, CONF_TOKEN +from homeassistant.components.vulcan.config_flow import ClientConnectionError, Keystore +from homeassistant.const import CONF_PIN, CONF_REGION, CONF_TOKEN from tests.common import MockConfigEntry, load_fixture @@ -56,13 +59,20 @@ async def test_config_flow_auth_success( assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "auth" assert result["errors"] is None - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, - ) + + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, + ) + await hass.async_block_till_done() assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 1 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -96,13 +106,18 @@ async def test_config_flow_auth_success_with_multiple_students( assert result["step_id"] == "select_student" assert result["errors"] == {} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"student": "0"}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"student": "0"}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 1 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -120,14 +135,53 @@ async def test_config_flow_reauth_success( MockConfigEntry( domain=const.DOMAIN, unique_id="0", - data={"student_id": "0", "login": "example@example.com"}, + data={"student_id": "0"}, ).add_to_hass(hass) result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_REAUTH} ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" + assert result["errors"] == {} + + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "reauth_successful" + assert len(mock_setup_entry.mock_calls) == 1 + + +@mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") +@mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") +@mock.patch("homeassistant.components.vulcan.config_flow.Account.register") +async def test_config_flow_reauth_without_matching_entries( + mock_account, mock_keystore, mock_student, hass +): + """Test a aborted config flow reauth caused by leak of matching entries.""" + mock_keystore.return_value = fake_keystore + mock_account.return_value = fake_account + mock_student.return_value = [ + Student.load(load_fixture("fake_student_1.json", "vulcan")) + ] + MockConfigEntry( + domain=const.DOMAIN, + unique_id="0", + data={"student_id": "1"}, + ).add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": config_entries.SOURCE_REAUTH} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {} result = await hass.config_entries.flow.async_configure( @@ -136,7 +190,7 @@ async def test_config_flow_reauth_success( ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "reauth_successful" + assert result["reason"] == "no_matching_entries" @mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") @@ -149,11 +203,11 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) const.DOMAIN, context={"source": config_entries.SOURCE_REAUTH} ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {} with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Invalid token."), + side_effect=InvalidTokenException, ): result = await hass.config_entries.flow.async_configure( @@ -162,12 +216,12 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "invalid_token"} with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Expired token."), + side_effect=ExpiredTokenException, ): result = await hass.config_entries.flow.async_configure( @@ -176,12 +230,12 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "expired_token"} with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Invalid PIN."), + side_effect=InvalidPINException, ): result = await hass.config_entries.flow.async_configure( @@ -190,12 +244,12 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "invalid_pin"} with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Unknown error"), + side_effect=InvalidSymbolException, ): result = await hass.config_entries.flow.async_configure( @@ -204,37 +258,9 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" - assert result["errors"] == {"base": "unknown"} - - with patch( - "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=RuntimeError("Internal Server Error (ArgumentException)"), - ): - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "invalid_symbol"} - with patch( - "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=RuntimeError("Unknown error"), - ): - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" - assert result["errors"] == {"base": "unknown"} - with patch( "homeassistant.components.vulcan.config_flow.Account.register", side_effect=ClientConnectionError, @@ -246,7 +272,7 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "cannot_connect"} with patch( @@ -260,7 +286,7 @@ async def test_config_flow_reauth_with_errors(mock_account, mock_keystore, hass) ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "reauth" + assert result["step_id"] == "reauth_confirm" assert result["errors"] == {"base": "unknown"} @@ -297,13 +323,18 @@ async def test_multiple_config_entries(mock_account, mock_keystore, mock_student assert result["step_id"] == "auth" assert result["errors"] is None - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_TOKEN: "token", CONF_REGION: "region", CONF_PIN: "000000"}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 2 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -326,13 +357,18 @@ async def test_multiple_config_entries_using_saved_credentials(mock_student, has assert result["step_id"] == "add_next_config_entry" assert result["errors"] == {} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"use_saved_credentials": True}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"use_saved_credentials": True}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 2 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -364,13 +400,18 @@ async def test_multiple_config_entries_using_saved_credentials_2(mock_student, h assert result["step_id"] == "select_student" assert result["errors"] == {} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"student": "0"}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"student": "0"}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 2 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -410,13 +451,18 @@ async def test_multiple_config_entries_using_saved_credentials_3(mock_student, h assert result["step_id"] == "select_saved_credentials" assert result["errors"] is None - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"credentials": "123"}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"credentials": "123"}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 3 @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @@ -465,13 +511,18 @@ async def test_multiple_config_entries_using_saved_credentials_4(mock_student, h assert result["step_id"] == "select_student" assert result["errors"] == {} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"student": "0"}, - ) + with patch( + "homeassistant.components.vulcan.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"student": "0"}, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == "Jan Kowalski" + assert len(mock_setup_entry.mock_calls) == 3 async def test_multiple_config_entries_without_valid_saved_credentials(hass): @@ -504,7 +555,7 @@ async def test_multiple_config_entries_without_valid_saved_credentials(hass): ) with patch( "homeassistant.components.vulcan.config_flow.Vulcan.get_students", - side_effect=VulcanAPIException("The certificate is not authorized."), + side_effect=UnauthorizedCertificateException, ): assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "select_saved_credentials" @@ -614,54 +665,6 @@ async def test_multiple_config_entries_using_saved_credentials_with_unknown_erro assert result["errors"] == {"base": "unknown"} -async def test_multiple_config_entries_using_saved_credentials_with_unknown_api_error( - hass, -): - """Test a unsuccessful config flow for multiple config entries without valid saved credentials.""" - MockConfigEntry( - entry_id="456", - domain=const.DOMAIN, - unique_id="234567", - data=json.loads(load_fixture("fake_config_entry_data.json", "vulcan")) - | {"student_id": "456"}, - ).add_to_hass(hass) - MockConfigEntry( - entry_id="123", - domain=const.DOMAIN, - unique_id="123456", - data=json.loads(load_fixture("fake_config_entry_data.json", "vulcan")), - ).add_to_hass(hass) - - result = await hass.config_entries.flow.async_init( - const.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "add_next_config_entry" - assert result["errors"] == {} - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"use_saved_credentials": True}, - ) - with patch( - "homeassistant.components.vulcan.config_flow.Vulcan.get_students", - side_effect=VulcanAPIException("Unknown error"), - ): - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "select_saved_credentials" - assert result["errors"] is None - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"credentials": "123"}, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "auth" - assert result["errors"] == {"base": "unknown"} - - @mock.patch("homeassistant.components.vulcan.config_flow.Vulcan.get_students") @mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") @mock.patch("homeassistant.components.vulcan.config_flow.Account.register") @@ -704,7 +707,7 @@ async def test_config_flow_auth_invalid_token(mock_keystore, hass): mock_keystore.return_value = fake_keystore with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Invalid token."), + side_effect=InvalidTokenException, ): result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -730,7 +733,7 @@ async def test_config_flow_auth_invalid_region(mock_keystore, hass): mock_keystore.return_value = fake_keystore with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=RuntimeError("Internal Server Error (ArgumentException)"), + side_effect=InvalidSymbolException, ): result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -756,7 +759,7 @@ async def test_config_flow_auth_invalid_pin(mock_keystore, hass): mock_keystore.return_value = fake_keystore with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Invalid PIN."), + side_effect=InvalidPINException, ): result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -782,7 +785,7 @@ async def test_config_flow_auth_expired_token(mock_keystore, hass): mock_keystore.return_value = fake_keystore with patch( "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Expired token."), + side_effect=ExpiredTokenException, ): result = await hass.config_entries.flow.async_init( const.DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -802,58 +805,6 @@ async def test_config_flow_auth_expired_token(mock_keystore, hass): assert result["errors"] == {"base": "expired_token"} -@mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") -async def test_config_flow_auth_api_unknown_error(mock_keystore, hass): - """Test a config flow with unknown API error.""" - mock_keystore.return_value = fake_keystore - with patch( - "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=VulcanAPIException("Unknown error"), - ): - result = await hass.config_entries.flow.async_init( - const.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "auth" - assert result["errors"] is None - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "3S10000", CONF_REGION: "region", CONF_PIN: "000000"}, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "auth" - assert result["errors"] == {"base": "unknown"} - - -@mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") -async def test_config_flow_auth_api_unknown_runtime_error(mock_keystore, hass): - """Test a config flow with runtime error.""" - mock_keystore.return_value = fake_keystore - with patch( - "homeassistant.components.vulcan.config_flow.Account.register", - side_effect=RuntimeError("Unknown error"), - ): - result = await hass.config_entries.flow.async_init( - const.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "auth" - assert result["errors"] is None - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_TOKEN: "3S10000", CONF_REGION: "region", CONF_PIN: "000000"}, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "auth" - assert result["errors"] == {"base": "unknown"} - - @mock.patch("homeassistant.components.vulcan.config_flow.Keystore.create") async def test_config_flow_auth_connection_error(mock_keystore, hass): """Test a config flow with connection error.""" @@ -904,32 +855,3 @@ async def test_config_flow_auth_unknown_error(mock_keystore, hass): assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "auth" assert result["errors"] == {"base": "unknown"} - - -@mock.patch("homeassistant.components.vulcan.Vulcan.get_students") -async def test_options_flow(mock_student, hass): - """Test config flow options.""" - mock_student.return_value = [ - Student.load(load_fixture("fake_student_1.json", "vulcan")) - ] - config_entry = MockConfigEntry( - domain=const.DOMAIN, - unique_id="0", - data=json.loads(load_fixture("fake_config_entry_data.json", "vulcan")), - ) - config_entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - result = await hass.config_entries.options.async_init(config_entry.entry_id) - - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "init" - - result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_SCAN_INTERVAL: 2137} - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert config_entry.options == {CONF_SCAN_INTERVAL: 2137} From 8c89f68d2c4695dd963d31f9b7b1943059694166 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Thu, 5 May 2022 17:40:56 +0200 Subject: [PATCH 226/930] Bump library version (#71349) --- homeassistant/components/nam/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nam/manifest.json b/homeassistant/components/nam/manifest.json index 2b62500f23b..16231ef0b88 100644 --- a/homeassistant/components/nam/manifest.json +++ b/homeassistant/components/nam/manifest.json @@ -3,7 +3,7 @@ "name": "Nettigo Air Monitor", "documentation": "https://www.home-assistant.io/integrations/nam", "codeowners": ["@bieniu"], - "requirements": ["nettigo-air-monitor==1.2.2"], + "requirements": ["nettigo-air-monitor==1.2.3"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/requirements_all.txt b/requirements_all.txt index 53712397c4c..4a7f5e25e46 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1068,7 +1068,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.2.2 +nettigo-air-monitor==1.2.3 # homeassistant.components.neurio_energy neurio==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 78f7da53d35..8d1f5da4bbb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -730,7 +730,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.2.2 +nettigo-air-monitor==1.2.3 # homeassistant.components.nexia nexia==0.9.13 From e3433008a25d59e057e1d75679babc4ec9932ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 5 May 2022 19:37:32 +0300 Subject: [PATCH 227/930] Upgrade huawei-lte-api to 1.6.0, adapt to it (#71041) * Upgrade huawei-lte-api to 1.6.0, adapt to it https://github.com/Salamek/huawei-lte-api/releases/tag/1.5 https://github.com/Salamek/huawei-lte-api/releases/tag/1.5.1 https://github.com/Salamek/huawei-lte-api/releases/tag/1.5.2 https://github.com/Salamek/huawei-lte-api/releases/tag/1.5.3 https://github.com/Salamek/huawei-lte-api/releases/tag/1.5.4 https://github.com/Salamek/huawei-lte-api/releases/tag/1.6 * Fix logout on config flow Co-authored-by: Antonino Piazza --- homeassistant/components/huawei_lte/__init__.py | 16 +++++++++------- .../components/huawei_lte/config_flow.py | 12 +++++------- .../components/huawei_lte/manifest.json | 2 +- homeassistant/components/huawei_lte/utils.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/huawei_lte/test_config_flow.py | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index ca15731641d..d8afbd752a4 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -10,9 +10,9 @@ import logging import time from typing import Any, NamedTuple, cast -from huawei_lte_api.AuthorizedConnection import AuthorizedConnection from huawei_lte_api.Client import Client from huawei_lte_api.Connection import Connection +from huawei_lte_api.enums.device import ControlModeEnum from huawei_lte_api.exceptions import ( ResponseErrorException, ResponseErrorLoginRequiredException, @@ -186,9 +186,12 @@ class Router: try: self.data[key] = func() except ResponseErrorLoginRequiredException: - if isinstance(self.connection, AuthorizedConnection): + if not self.config_entry.options.get(CONF_UNAUTHENTICATED_MODE): _LOGGER.debug("Trying to authorize again") - if self.connection.enforce_authorized_connection(): + if self.client.user.login( + self.config_entry.data.get(CONF_USERNAME, ""), + self.config_entry.data.get(CONF_PASSWORD, ""), + ): _LOGGER.debug( "success, %s will be updated by a future periodic run", key, @@ -276,8 +279,6 @@ class Router: def logout(self) -> None: """Log out router session.""" - if not isinstance(self.connection, AuthorizedConnection): - return try: self.client.user.logout() except ResponseErrorNotSupportedException: @@ -293,6 +294,7 @@ class Router: self.subscriptions.clear() self.logout() + self.connection.requests_session.close() class HuaweiLteData(NamedTuple): @@ -315,7 +317,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Connecting in authenticated mode, full feature set") username = entry.data.get(CONF_USERNAME) or "" password = entry.data.get(CONF_PASSWORD) or "" - connection = AuthorizedConnection( + connection = Connection( url, username=username, password=password, timeout=CONNECTION_TIMEOUT ) return connection @@ -515,7 +517,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if router.suspended: _LOGGER.debug("%s: ignored, integration suspended", service.service) return - result = router.client.device.reboot() + result = router.client.device.set_control(ControlModeEnum.REBOOT) _LOGGER.debug("%s: %s", service.service, result) elif service.service == SERVICE_RESUME_INTEGRATION: # Login will be handled automatically on demand diff --git a/homeassistant/components/huawei_lte/config_flow.py b/homeassistant/components/huawei_lte/config_flow.py index f4ea7cd86f7..3dfd38d6304 100644 --- a/homeassistant/components/huawei_lte/config_flow.py +++ b/homeassistant/components/huawei_lte/config_flow.py @@ -5,9 +5,9 @@ import logging from typing import TYPE_CHECKING, Any from urllib.parse import urlparse -from huawei_lte_api.AuthorizedConnection import AuthorizedConnection from huawei_lte_api.Client import Client -from huawei_lte_api.Connection import GetResponseType +from huawei_lte_api.Connection import Connection +from huawei_lte_api.Session import GetResponseType from huawei_lte_api.exceptions import ( LoginErrorPasswordWrongException, LoginErrorUsernamePasswordOverrunException, @@ -108,19 +108,17 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_input=user_input, errors=errors ) - conn: AuthorizedConnection - def logout() -> None: try: - conn.user.logout() + conn.user_session.user.logout() # type: ignore[union-attr] except Exception: # pylint: disable=broad-except _LOGGER.debug("Could not logout", exc_info=True) - def try_connect(user_input: dict[str, Any]) -> AuthorizedConnection: + def try_connect(user_input: dict[str, Any]) -> Connection: """Try connecting with given credentials.""" username = user_input.get(CONF_USERNAME) or "" password = user_input.get(CONF_PASSWORD) or "" - conn = AuthorizedConnection( + conn = Connection( user_input[CONF_URL], username=username, password=password, diff --git a/homeassistant/components/huawei_lte/manifest.json b/homeassistant/components/huawei_lte/manifest.json index 3e7ebf24b16..dd0382d5f55 100644 --- a/homeassistant/components/huawei_lte/manifest.json +++ b/homeassistant/components/huawei_lte/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/huawei_lte", "requirements": [ - "huawei-lte-api==1.4.18", + "huawei-lte-api==1.6.0", "stringcase==1.2.0", "url-normalize==1.4.1" ], diff --git a/homeassistant/components/huawei_lte/utils.py b/homeassistant/components/huawei_lte/utils.py index 69b346a58f4..37f9f1a5542 100644 --- a/homeassistant/components/huawei_lte/utils.py +++ b/homeassistant/components/huawei_lte/utils.py @@ -1,7 +1,7 @@ """Utilities for the Huawei LTE integration.""" from __future__ import annotations -from huawei_lte_api.Connection import GetResponseType +from huawei_lte_api.Session import GetResponseType from homeassistant.helpers.device_registry import format_mac diff --git a/requirements_all.txt b/requirements_all.txt index 4a7f5e25e46..eb0b504bdaa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -837,7 +837,7 @@ horimote==0.4.1 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.4.18 +huawei-lte-api==1.6.0 # homeassistant.components.huisbaasje huisbaasje-client==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8d1f5da4bbb..01f4a2d339a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -595,7 +595,7 @@ homepluscontrol==0.0.5 httplib2==0.20.4 # homeassistant.components.huawei_lte -huawei-lte-api==1.4.18 +huawei-lte-api==1.6.0 # homeassistant.components.huisbaasje huisbaasje-client==0.1.0 diff --git a/tests/components/huawei_lte/test_config_flow.py b/tests/components/huawei_lte/test_config_flow.py index 52b46c57b98..b363836cc4f 100644 --- a/tests/components/huawei_lte/test_config_flow.py +++ b/tests/components/huawei_lte/test_config_flow.py @@ -127,7 +127,7 @@ def login_requests_mock(requests_mock): LoginErrorEnum.USERNAME_PWD_WRONG, {CONF_USERNAME: "invalid_auth"}, ), - (LoginErrorEnum.USERNAME_PWD_ORERRUN, {"base": "login_attempts_exceeded"}), + (LoginErrorEnum.USERNAME_PWD_OVERRUN, {"base": "login_attempts_exceeded"}), (ResponseCodeEnum.ERROR_SYSTEM_UNKNOWN, {"base": "response_error"}), ), ) From 8a41370950f951910c89f83089c3ce5285f3b845 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 13:13:55 -0400 Subject: [PATCH 228/930] Add cache to split_entity_id (#71345) --- homeassistant/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/core.py b/homeassistant/core.py index d6b1ac85d8b..b181e4c4106 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -133,9 +133,12 @@ SOURCE_YAML = ConfigSource.YAML.value # How long to wait until things that run on startup have to finish. TIMEOUT_EVENT_START = 15 +MAX_EXPECTED_ENTITY_IDS = 16384 + _LOGGER = logging.getLogger(__name__) +@functools.lru_cache(MAX_EXPECTED_ENTITY_IDS) def split_entity_id(entity_id: str) -> tuple[str, str]: """Split a state entity ID into domain and object ID.""" domain, _, object_id = entity_id.partition(".") From 203bebe668cd2cf0b4130c225527691977c9f932 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 5 May 2022 19:16:36 +0200 Subject: [PATCH 229/930] Include all non-numeric sensor events in logbook (#71331) --- homeassistant/components/logbook/__init__.py | 48 ++++++++--- tests/components/logbook/test_init.py | 85 +++++++++++++------- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 7bacbd4cdd7..1c930cf3004 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -26,6 +26,7 @@ from homeassistant.components.history import ( sqlalchemy_filter_from_include_exclude_conf, ) from homeassistant.components.http import HomeAssistantView +from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder import get_instance from homeassistant.components.recorder.models import ( EventData, @@ -36,6 +37,7 @@ from homeassistant.components.recorder.models import ( ) from homeassistant.components.recorder.util import session_scope from homeassistant.components.script import EVENT_SCRIPT_STARTED +from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, @@ -58,7 +60,7 @@ from homeassistant.core import ( split_entity_id, ) from homeassistant.exceptions import InvalidEntityFormatError -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, EntityFilter, @@ -79,7 +81,7 @@ DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"') ATTR_MESSAGE = "message" -CONTINUOUS_DOMAINS = {"proximity", "sensor"} +CONTINUOUS_DOMAINS = {PROXIMITY_DOMAIN, SENSOR_DOMAIN} CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] DOMAIN = "logbook" @@ -331,7 +333,6 @@ def humanify( """Generate a converted list of events into Entry objects. Will try to group events if possible: - - if 2+ sensor updates in GROUP_BY_MINUTES, show last - if Home Assistant stop and start happen in same minute call it restarted """ external_events = hass.data.get(DOMAIN, {}) @@ -343,8 +344,8 @@ def humanify( events_batch = list(g_events) - # Keep track of last sensor states - last_sensor_event = {} + # Continuous sensors, will be excluded from the logbook + continuous_sensors = {} # Group HA start/stop events # Maps minute of event to 1: stop, 2: stop + start @@ -353,8 +354,13 @@ def humanify( # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: - if event.domain in CONTINUOUS_DOMAINS: - last_sensor_event[event.entity_id] = event + if event.domain != SENSOR_DOMAIN: + continue + entity_id = event.entity_id + if entity_id in continuous_sensors: + continue + assert entity_id is not None + continuous_sensors[entity_id] = _is_sensor_continuous(hass, entity_id) elif event.event_type == EVENT_HOMEASSISTANT_STOP: if event.time_fired_minute in start_stop_events: @@ -373,15 +379,12 @@ def humanify( if event.event_type == EVENT_STATE_CHANGED: entity_id = event.entity_id domain = event.domain + assert entity_id is not None - if ( - domain in CONTINUOUS_DOMAINS - and event != last_sensor_event[entity_id] - ): - # Skip all but the last sensor state + if domain == SENSOR_DOMAIN and continuous_sensors[entity_id]: + # Skip continuous sensors continue - assert entity_id is not None data = { "when": event.time_fired_isoformat, "name": _entity_name_from_event( @@ -812,6 +815,25 @@ def _entity_name_from_event( ) or split_entity_id(entity_id)[1].replace("_", " ") +def _is_sensor_continuous( + hass: HomeAssistant, + entity_id: str, +) -> bool: + """Determine if a sensor is continuous by checking its state class. + + Sensors with a unit_of_measurement are also considered continuous, but are filtered + already by the SQL query generated by _get_events + """ + registry = er.async_get(hass) + if not (entry := registry.async_get(entity_id)): + # Entity not registered, so can't have a state class + return False + return ( + entry.capabilities is not None + and entry.capabilities.get(ATTR_STATE_CLASS) is not None + ) + + class LazyEventPartialState: """A lazy version of core Event with limited State joined in.""" diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 68b9169224e..2612765584f 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -14,12 +14,14 @@ from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat from homeassistant.components.script import EVENT_SCRIPT_STARTED +from homeassistant.components.sensor import SensorStateClass from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_NAME, ATTR_SERVICE, + ATTR_UNIT_OF_MEASUREMENT, CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, @@ -33,6 +35,7 @@ from homeassistant.const import ( STATE_ON, ) import homeassistant.core as ha +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entityfilter import CONF_ENTITY_GLOBS from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component @@ -157,27 +160,51 @@ async def test_service_call_create_log_book_entry_no_message(hass_): assert len(calls) == 0 -def test_humanify_filter_sensor(hass_): - """Test humanify filter too frequent sensor values.""" - entity_id = "sensor.bla" +async def test_filter_sensor(hass_: ha.HomeAssistant, hass_client): + """Test numeric sensors are filtered.""" - pointA = dt_util.utcnow().replace(minute=2) - pointB = pointA.replace(minute=5) - pointC = pointA + timedelta(minutes=logbook.GROUP_BY_MINUTES) - entity_attr_cache = logbook.EntityAttributeCache(hass_) + registry = er.async_get(hass_) - eventA = create_state_changed_event(pointA, entity_id, 10) - eventB = create_state_changed_event(pointB, entity_id, 20) - eventC = create_state_changed_event(pointC, entity_id, 30) + # Unregistered sensor without a unit of measurement - should be in logbook + entity_id1 = "sensor.bla" + attributes_1 = None + # Unregistered sensor with a unit of measurement - should be excluded from logbook + entity_id2 = "sensor.blu" + attributes_2 = {ATTR_UNIT_OF_MEASUREMENT: "cats"} + # Registered sensor with state class - should be excluded from logbook + entity_id3 = registry.async_get_or_create( + "sensor", + "test", + "unique_3", + suggested_object_id="bli", + capabilities={"state_class": SensorStateClass.MEASUREMENT}, + ).entity_id + attributes_3 = None + # Registered sensor without state class or unit - should be in logbook + entity_id4 = registry.async_get_or_create( + "sensor", "test", "unique_4", suggested_object_id="ble" + ).entity_id + attributes_4 = None - entries = list( - logbook.humanify(hass_, (eventA, eventB, eventC), entity_attr_cache, {}) - ) + hass_.states.async_set(entity_id1, None, attributes_1) # Excluded + hass_.states.async_set(entity_id1, 10, attributes_1) # Included + hass_.states.async_set(entity_id2, None, attributes_2) # Excluded + hass_.states.async_set(entity_id2, 10, attributes_2) # Excluded + hass_.states.async_set(entity_id3, None, attributes_3) # Excluded + hass_.states.async_set(entity_id3, 10, attributes_3) # Excluded + hass_.states.async_set(entity_id1, 20, attributes_1) # Included + hass_.states.async_set(entity_id2, 20, attributes_2) # Excluded + hass_.states.async_set(entity_id4, None, attributes_4) # Excluded + hass_.states.async_set(entity_id4, 10, attributes_4) # Included - assert len(entries) == 2 - assert_entry(entries[0], pointB, "bla", entity_id=entity_id) + await async_wait_recording_done(hass_) + client = await hass_client() + entries = await _async_fetch_logbook(client) - assert_entry(entries[1], pointC, "bla", entity_id=entity_id) + assert len(entries) == 3 + _assert_entry(entries[0], name="bla", entity_id=entity_id1, state="10") + _assert_entry(entries[1], name="bla", entity_id=entity_id1, state="20") + _assert_entry(entries[2], name="ble", entity_id=entity_id4, state="10") def test_home_assistant_start_stop_grouped(hass_): @@ -1790,12 +1817,13 @@ async def test_include_exclude_events(hass, hass_client, recorder_mock): client = await hass_client() entries = await _async_fetch_logbook(client) - assert len(entries) == 3 + assert len(entries) == 4 _assert_entry( entries[0], name="Home Assistant", message="started", domain=ha.DOMAIN ) - _assert_entry(entries[1], name="blu", entity_id=entity_id2) - _assert_entry(entries[2], name="keep", entity_id=entity_id4) + _assert_entry(entries[1], name="blu", entity_id=entity_id2, state="10") + _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="20") + _assert_entry(entries[3], name="keep", entity_id=entity_id4, state="10") async def test_include_exclude_events_with_glob_filters( @@ -1849,12 +1877,13 @@ async def test_include_exclude_events_with_glob_filters( client = await hass_client() entries = await _async_fetch_logbook(client) - assert len(entries) == 3 + assert len(entries) == 4 _assert_entry( entries[0], name="Home Assistant", message="started", domain=ha.DOMAIN ) - _assert_entry(entries[1], name="blu", entity_id=entity_id2) - _assert_entry(entries[2], name="included", entity_id=entity_id4) + _assert_entry(entries[1], name="blu", entity_id=entity_id2, state="10") + _assert_entry(entries[2], name="blu", entity_id=entity_id2, state="20") + _assert_entry(entries[3], name="included", entity_id=entity_id4, state="30") async def test_empty_config(hass, hass_client, recorder_mock): @@ -1939,22 +1968,22 @@ def _assert_entry( entry, when=None, name=None, message=None, domain=None, entity_id=None, state=None ): """Assert an entry is what is expected.""" - if when: + if when is not None: assert when.isoformat() == entry["when"] - if name: + if name is not None: assert name == entry["name"] - if message: + if message is not None: assert message == entry["message"] - if domain: + if domain is not None: assert domain == entry["domain"] - if entity_id: + if entity_id is not None: assert entity_id == entry["entity_id"] - if state: + if state is not None: assert state == entry["state"] From d9a7c4a4839ba1468e018ef96861b03c3512a274 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 5 May 2022 20:04:00 +0200 Subject: [PATCH 230/930] Only lookup unknown Google Cast models once (#71348) Co-authored-by: Paulus Schoutsen --- homeassistant/components/cast/__init__.py | 4 +- homeassistant/components/cast/discovery.py | 2 +- homeassistant/components/cast/helpers.py | 47 +++++++- homeassistant/components/cast/manifest.json | 2 +- homeassistant/components/cast/media_player.py | 6 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cast/conftest.py | 11 ++ tests/components/cast/test_media_player.py | 109 +++++++++++++++++- 9 files changed, 166 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/cast/__init__.py b/homeassistant/components/cast/__init__.py index d0b7cfc4158..63f0693b01a 100644 --- a/homeassistant/components/cast/__init__.py +++ b/homeassistant/components/cast/__init__.py @@ -58,7 +58,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Cast from a config entry.""" await home_assistant_cast.async_setup_ha_cast(hass, entry) hass.config_entries.async_setup_platforms(entry, PLATFORMS) - hass.data[DOMAIN] = {} + hass.data[DOMAIN] = {"cast_platform": {}, "unknown_models": {}} await async_process_integration_platforms(hass, DOMAIN, _register_cast_platform) return True @@ -107,7 +107,7 @@ async def _register_cast_platform( or not hasattr(platform, "async_play_media") ): raise HomeAssistantError(f"Invalid cast platform {platform}") - hass.data[DOMAIN][integration_domain] = platform + hass.data[DOMAIN]["cast_platform"][integration_domain] = platform async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: diff --git a/homeassistant/components/cast/discovery.py b/homeassistant/components/cast/discovery.py index bc0f1435f8b..485d2888a41 100644 --- a/homeassistant/components/cast/discovery.py +++ b/homeassistant/components/cast/discovery.py @@ -34,7 +34,7 @@ def discover_chromecast( _LOGGER.error("Discovered chromecast without uuid %s", info) return - info = info.fill_out_missing_chromecast_info() + info = info.fill_out_missing_chromecast_info(hass) _LOGGER.debug("Discovered new or updated chromecast %s", info) dispatcher_send(hass, SIGNAL_CAST_DISCOVERED, info) diff --git a/homeassistant/components/cast/helpers.py b/homeassistant/components/cast/helpers.py index 5c6f0fee62a..dd98a2bc051 100644 --- a/homeassistant/components/cast/helpers.py +++ b/homeassistant/components/cast/helpers.py @@ -15,8 +15,11 @@ from pychromecast import dial from pychromecast.const import CAST_TYPE_GROUP from pychromecast.models import CastInfo +from homeassistant.core import HomeAssistant from homeassistant.helpers import aiohttp_client +from .const import DOMAIN + _LOGGER = logging.getLogger(__name__) _PLS_SECTION_PLAYLIST = "playlist" @@ -47,18 +50,50 @@ class ChromecastInfo: """Return the UUID.""" return self.cast_info.uuid - def fill_out_missing_chromecast_info(self) -> ChromecastInfo: + def fill_out_missing_chromecast_info(self, hass: HomeAssistant) -> ChromecastInfo: """Return a new ChromecastInfo object with missing attributes filled in. Uses blocking HTTP / HTTPS. """ cast_info = self.cast_info if self.cast_info.cast_type is None or self.cast_info.manufacturer is None: - # Manufacturer and cast type is not available in mDNS data, get it over http - cast_info = dial.get_cast_type( - cast_info, - zconf=ChromeCastZeroconf.get_zeroconf(), - ) + unknown_models = hass.data[DOMAIN]["unknown_models"] + if self.cast_info.model_name not in unknown_models: + # Manufacturer and cast type is not available in mDNS data, get it over http + cast_info = dial.get_cast_type( + cast_info, + zconf=ChromeCastZeroconf.get_zeroconf(), + ) + unknown_models[self.cast_info.model_name] = ( + cast_info.cast_type, + cast_info.manufacturer, + ) + + report_issue = ( + "create a bug report at " + "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue" + "+label%3A%22integration%3A+cast%22" + ) + + _LOGGER.info( + "Fetched cast details for unknown model '%s' manufacturer: '%s', type: '%s'. Please %s", + cast_info.model_name, + cast_info.manufacturer, + cast_info.cast_type, + report_issue, + ) + else: + cast_type, manufacturer = unknown_models[self.cast_info.model_name] + cast_info = CastInfo( + cast_info.services, + cast_info.uuid, + cast_info.model_name, + cast_info.friendly_name, + cast_info.host, + cast_info.port, + cast_type, + manufacturer, + ) if not self.is_audio_group or self.is_dynamic_group is not None: # We have all information, no need to check HTTP API. diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index 389b837f200..7144a3872f5 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==12.0.0"], + "requirements": ["pychromecast==12.1.0"], "after_dependencies": [ "cloud", "http", diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index e63fedd3598..b64c3372c15 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -535,7 +535,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): """Generate root node.""" children = [] # Add media browsers - for platform in self.hass.data[CAST_DOMAIN].values(): + for platform in self.hass.data[CAST_DOMAIN]["cast_platform"].values(): children.extend( await platform.async_get_media_browser_root_object( self.hass, self._chromecast.cast_type @@ -587,7 +587,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): if media_content_id is None: return await self._async_root_payload(content_filter) - for platform in self.hass.data[CAST_DOMAIN].values(): + for platform in self.hass.data[CAST_DOMAIN]["cast_platform"].values(): browse_media = await platform.async_browse_media( self.hass, media_content_type, @@ -646,7 +646,7 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): return # Try the cast platforms - for platform in self.hass.data[CAST_DOMAIN].values(): + for platform in self.hass.data[CAST_DOMAIN]["cast_platform"].values(): result = await platform.async_play_media( self.hass, self.entity_id, self._chromecast, media_type, media_id ) diff --git a/requirements_all.txt b/requirements_all.txt index eb0b504bdaa..777cd79af74 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1402,7 +1402,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==12.0.0 +pychromecast==12.1.0 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 01f4a2d339a..205ac163595 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -941,7 +941,7 @@ pybotvac==0.0.23 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==12.0.0 +pychromecast==12.1.0 # homeassistant.components.climacell pyclimacell==0.18.2 diff --git a/tests/components/cast/conftest.py b/tests/components/cast/conftest.py index 3b96f378906..52152b4a718 100644 --- a/tests/components/cast/conftest.py +++ b/tests/components/cast/conftest.py @@ -14,6 +14,13 @@ def get_multizone_status_mock(): return mock +@pytest.fixture() +def get_cast_type_mock(): + """Mock pychromecast dial.""" + mock = MagicMock(spec_set=pychromecast.dial.get_cast_type) + return mock + + @pytest.fixture() def castbrowser_mock(): """Mock pychromecast CastBrowser.""" @@ -43,6 +50,7 @@ def cast_mock( mz_mock, quick_play_mock, castbrowser_mock, + get_cast_type_mock, get_chromecast_mock, get_multizone_status_mock, ): @@ -52,6 +60,9 @@ def cast_mock( with patch( "homeassistant.components.cast.discovery.pychromecast.discovery.CastBrowser", castbrowser_mock, + ), patch( + "homeassistant.components.cast.helpers.dial.get_cast_type", + get_cast_type_mock, ), patch( "homeassistant.components.cast.helpers.dial.get_multizone_status", get_multizone_status_mock, diff --git a/tests/components/cast/test_media_player.py b/tests/components/cast/test_media_player.py index 88cf281c8a5..e4df84f6443 100644 --- a/tests/components/cast/test_media_player.py +++ b/tests/components/cast/test_media_player.py @@ -64,6 +64,8 @@ FAKE_MDNS_SERVICE = pychromecast.discovery.ServiceInfo( pychromecast.const.SERVICE_TYPE_MDNS, "the-service" ) +UNDEFINED = object() + def get_fake_chromecast(info: ChromecastInfo): """Generate a Fake Chromecast object with the specified arguments.""" @@ -74,7 +76,14 @@ def get_fake_chromecast(info: ChromecastInfo): def get_fake_chromecast_info( - host="192.168.178.42", port=8009, service=None, uuid: UUID | None = FakeUUID + *, + host="192.168.178.42", + port=8009, + service=None, + uuid: UUID | None = FakeUUID, + cast_type=UNDEFINED, + manufacturer=UNDEFINED, + model_name=UNDEFINED, ): """Generate a Fake ChromecastInfo with the specified arguments.""" @@ -82,16 +91,22 @@ def get_fake_chromecast_info( service = pychromecast.discovery.ServiceInfo( pychromecast.const.SERVICE_TYPE_HOST, (host, port) ) + if cast_type is UNDEFINED: + cast_type = CAST_TYPE_GROUP if port != 8009 else CAST_TYPE_CHROMECAST + if manufacturer is UNDEFINED: + manufacturer = "Nabu Casa" + if model_name is UNDEFINED: + model_name = "Chromecast" return ChromecastInfo( cast_info=pychromecast.models.CastInfo( services={service}, uuid=uuid, - model_name="Chromecast", + model_name=model_name, friendly_name="Speaker", host=host, port=port, - cast_type=CAST_TYPE_GROUP if port != 8009 else CAST_TYPE_CHROMECAST, - manufacturer="Nabu Casa", + cast_type=cast_type, + manufacturer=manufacturer, ) ) @@ -342,6 +357,92 @@ async def test_internal_discovery_callback_fill_out_group( get_multizone_status_mock.assert_called_once() +async def test_internal_discovery_callback_fill_out_cast_type_manufacturer( + hass, get_cast_type_mock, caplog +): + """Test internal discovery automatically filling out information.""" + discover_cast, _, _ = await async_setup_cast_internal_discovery(hass) + info = get_fake_chromecast_info( + host="host1", + port=8009, + service=FAKE_MDNS_SERVICE, + cast_type=None, + manufacturer=None, + ) + info2 = get_fake_chromecast_info( + host="host1", + port=8009, + service=FAKE_MDNS_SERVICE, + cast_type=None, + manufacturer=None, + model_name="Model 101", + ) + zconf = get_fake_zconf(host="host1", port=8009) + full_info = attr.evolve( + info, + cast_info=pychromecast.discovery.CastInfo( + services=info.cast_info.services, + uuid=FakeUUID, + model_name="Chromecast", + friendly_name="Speaker", + host=info.cast_info.host, + port=info.cast_info.port, + cast_type="audio", + manufacturer="TrollTech", + ), + is_dynamic_group=None, + ) + full_info2 = attr.evolve( + info2, + cast_info=pychromecast.discovery.CastInfo( + services=info.cast_info.services, + uuid=FakeUUID, + model_name="Model 101", + friendly_name="Speaker", + host=info.cast_info.host, + port=info.cast_info.port, + cast_type="cast", + manufacturer="Cyberdyne Systems", + ), + is_dynamic_group=None, + ) + + get_cast_type_mock.assert_not_called() + get_cast_type_mock.return_value = full_info.cast_info + + with patch( + "homeassistant.components.cast.discovery.ChromeCastZeroconf.get_zeroconf", + return_value=zconf, + ): + signal = MagicMock() + + async_dispatcher_connect(hass, "cast_discovered", signal) + discover_cast(FAKE_MDNS_SERVICE, info) + await hass.async_block_till_done() + + # when called with incomplete info, it should use HTTP to get missing + get_cast_type_mock.assert_called_once() + assert get_cast_type_mock.call_count == 1 + discover = signal.mock_calls[0][1][0] + assert discover == full_info + assert "Fetched cast details for unknown model 'Chromecast'" in caplog.text + + # Call again, the model name should be fetched from cache + discover_cast(FAKE_MDNS_SERVICE, info) + await hass.async_block_till_done() + assert get_cast_type_mock.call_count == 1 # No additional calls + discover = signal.mock_calls[1][1][0] + assert discover == full_info + + # Call for another model, need to call HTTP again + get_cast_type_mock.return_value = full_info2.cast_info + discover_cast(FAKE_MDNS_SERVICE, info2) + await hass.async_block_till_done() + assert get_cast_type_mock.call_count == 2 + discover = signal.mock_calls[2][1][0] + assert discover == full_info2 + + async def test_stop_discovery_called_on_stop(hass, castbrowser_mock): """Test pychromecast.stop_discovery called on shutdown.""" # start_discovery should be called with empty config From 8f5e61ee8083492c1a6bae307b370976744cf820 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 14:48:45 -0400 Subject: [PATCH 231/930] Remove logbook split_entity_id caching (#71359) --- homeassistant/components/logbook/__init__.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 1c930cf3004..f29276489a2 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -354,10 +354,11 @@ def humanify( # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: - if event.domain != SENSOR_DOMAIN: - continue entity_id = event.entity_id - if entity_id in continuous_sensors: + if ( + entity_id in continuous_sensors + or split_entity_id(entity_id)[0] != SENSOR_DOMAIN + ): continue assert entity_id is not None continuous_sensors[entity_id] = _is_sensor_continuous(hass, entity_id) @@ -378,10 +379,9 @@ def humanify( for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: entity_id = event.entity_id - domain = event.domain assert entity_id is not None - if domain == SENSOR_DOMAIN and continuous_sensors[entity_id]: + if continuous_sensors.get(entity_id): # Skip continuous sensors continue @@ -872,14 +872,6 @@ class LazyEventPartialState: self.time_fired_minute: int = self._row.time_fired.minute self._event_data_cache = event_data_cache - @property - def domain(self) -> str | None: - """Return the domain for the state.""" - if self._domain is None: - assert self.entity_id is not None - self._domain = split_entity_id(self.entity_id)[0] - return self._domain - @property def attributes_icon(self) -> str | None: """Extract the icon from the decoded attributes or json.""" From d2c9fa40755cf26e45a0d0ce0192f4ac545acd8e Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 5 May 2022 20:55:52 +0200 Subject: [PATCH 232/930] Bump pytrafikverket to 0.2.0.1 (#71131) * Bump pytrafikverket to 0.2.0.1 * Use system timezone * Minor review changes * current time * Adjustments timezone --- .../trafikverket_ferry/coordinator.py | 21 ++++++++------ .../trafikverket_ferry/manifest.json | 2 +- .../components/trafikverket_ferry/sensor.py | 15 +++++----- .../trafikverket_train/manifest.json | 2 +- .../components/trafikverket_train/sensor.py | 29 ++++++++----------- .../trafikverket_weatherstation/manifest.json | 2 +- .../trafikverket_weatherstation/sensor.py | 10 +++---- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 9 files changed, 40 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/trafikverket_ferry/coordinator.py b/homeassistant/components/trafikverket_ferry/coordinator.py index 052341cd12e..96a1e58c6c9 100644 --- a/homeassistant/components/trafikverket_ferry/coordinator.py +++ b/homeassistant/components/trafikverket_ferry/coordinator.py @@ -13,7 +13,7 @@ from homeassistant.const import CONF_API_KEY, CONF_WEEKDAY, WEEKDAYS from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from homeassistant.util.dt import UTC, as_utc, parse_time +from homeassistant.util import dt from .const import CONF_FROM, CONF_TIME, CONF_TO, DOMAIN @@ -58,21 +58,23 @@ class TVDataUpdateCoordinator(DataUpdateCoordinator): ) self._from: str = entry.data[CONF_FROM] self._to: str = entry.data[CONF_TO] - self._time: time | None = parse_time(entry.data[CONF_TIME]) + self._time: time | None = dt.parse_time(entry.data[CONF_TIME]) self._weekdays: list[str] = entry.data[CONF_WEEKDAY] async def _async_update_data(self) -> dict[str, Any]: """Fetch data from Trafikverket.""" departure_day = next_departuredate(self._weekdays) - currenttime = datetime.now() + current_time = dt.now() when = ( - datetime.combine(departure_day, self._time) + datetime.combine( + departure_day, self._time, dt.get_time_zone(self.hass.config.time_zone) + ) if self._time - else datetime.now() + else dt.now() ) - if currenttime > when: - when = currenttime + if current_time > when: + when = current_time try: routedata: FerryStop = await self._ferry_api.async_get_next_ferry_stop( @@ -84,10 +86,11 @@ class TVDataUpdateCoordinator(DataUpdateCoordinator): ) from error states = { - "departure_time": routedata.departure_time.replace(tzinfo=UTC), + "departure_time": routedata.departure_time, "departure_from": routedata.from_harbor_name, "departure_to": routedata.to_harbor_name, - "departure_modified": as_utc(routedata.modified_time.replace(tzinfo=UTC)), + "departure_modified": routedata.modified_time, "departure_information": routedata.other_information, } + _LOGGER.debug("States: %s", states) return states diff --git a/homeassistant/components/trafikverket_ferry/manifest.json b/homeassistant/components/trafikverket_ferry/manifest.json index 90864c7e358..d333473f169 100644 --- a/homeassistant/components/trafikverket_ferry/manifest.json +++ b/homeassistant/components/trafikverket_ferry/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_ferry", "name": "Trafikverket Ferry", "documentation": "https://www.home-assistant.io/integrations/trafikverket_ferry", - "requirements": ["pytrafikverket==0.1.6.2"], + "requirements": ["pytrafikverket==0.2.0.1"], "codeowners": ["@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/trafikverket_ferry/sensor.py b/homeassistant/components/trafikverket_ferry/sensor.py index 682c2073f76..d93aecd38c1 100644 --- a/homeassistant/components/trafikverket_ferry/sensor.py +++ b/homeassistant/components/trafikverket_ferry/sensor.py @@ -3,8 +3,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from datetime import timedelta -import logging +from datetime import datetime, timedelta from typing import Any from homeassistant.components.sensor import ( @@ -20,12 +19,11 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util.dt import as_utc from .const import ATTRIBUTION, DOMAIN from .coordinator import TVDataUpdateCoordinator -_LOGGER = logging.getLogger(__name__) - ATTR_FROM = "from_harbour" ATTR_TO = "to_harbour" ATTR_MODIFIED_TIME = "modified_time" @@ -39,7 +37,7 @@ SCAN_INTERVAL = timedelta(minutes=5) class TrafikverketRequiredKeysMixin: """Mixin for required keys.""" - value_fn: Callable[[dict[str, Any]], StateType] + value_fn: Callable[[dict[str, Any]], StateType | datetime] info_fn: Callable[[dict[str, Any]], StateType | list] @@ -56,7 +54,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( name="Departure Time", icon="mdi:clock", device_class=SensorDeviceClass.TIMESTAMP, - value_fn=lambda data: data["departure_time"], + value_fn=lambda data: as_utc(data["departure_time"]), info_fn=lambda data: data["departure_information"], ), TrafikverketSensorEntityDescription( @@ -78,7 +76,7 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( name="Departure Modified", icon="mdi:clock", device_class=SensorDeviceClass.TIMESTAMP, - value_fn=lambda data: data["departure_modified"], + value_fn=lambda data: as_utc(data["departure_modified"]), info_fn=lambda data: data["departure_information"], entity_registry_enabled_default=False, ), @@ -122,7 +120,7 @@ class FerrySensor(CoordinatorEntity[TVDataUpdateCoordinator], SensorEntity): entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, entry_id)}, manufacturer="Trafikverket", - model="v1.2", + model="v2.0", name=name, configuration_url="https://api.trafikinfo.trafikverket.se/", ) @@ -133,6 +131,7 @@ class FerrySensor(CoordinatorEntity[TVDataUpdateCoordinator], SensorEntity): self._attr_native_value = self.entity_description.value_fn( self.coordinator.data ) + self._attr_extra_state_attributes = { "other_information": self.entity_description.info_fn(self.coordinator.data), } diff --git a/homeassistant/components/trafikverket_train/manifest.json b/homeassistant/components/trafikverket_train/manifest.json index b3bf23ce5c4..0432670f15c 100644 --- a/homeassistant/components/trafikverket_train/manifest.json +++ b/homeassistant/components/trafikverket_train/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_train", "name": "Trafikverket Train", "documentation": "https://www.home-assistant.io/integrations/trafikverket_train", - "requirements": ["pytrafikverket==0.1.6.2"], + "requirements": ["pytrafikverket==0.2.0.1"], "codeowners": ["@endor-force", "@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/trafikverket_train/sensor.py b/homeassistant/components/trafikverket_train/sensor.py index 7bcd796bb16..6ae6af7d363 100644 --- a/homeassistant/components/trafikverket_train/sensor.py +++ b/homeassistant/components/trafikverket_train/sensor.py @@ -24,7 +24,7 @@ from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.util.dt import as_utc, get_time_zone, parse_time +from homeassistant.util import dt from .const import CONF_FROM, CONF_TIME, CONF_TO, CONF_TRAINS, DOMAIN from .util import create_unique_id @@ -42,7 +42,6 @@ ATTR_DEVIATIONS = "deviations" ICON = "mdi:train" SCAN_INTERVAL = timedelta(minutes=5) -STOCKHOLM_TIMEZONE = get_time_zone("Europe/Stockholm") PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { @@ -111,7 +110,9 @@ async def async_setup_entry( ) from error train_time = ( - parse_time(entry.data.get(CONF_TIME, "")) if entry.data.get(CONF_TIME) else None + dt.parse_time(entry.data.get(CONF_TIME, "")) + if entry.data.get(CONF_TIME) + else None ) async_add_entities( @@ -153,7 +154,7 @@ def next_departuredate(departure: list[str]) -> date: def _to_iso_format(traintime: datetime) -> str: """Return isoformatted utc time.""" - return as_utc(traintime.replace(tzinfo=STOCKHOLM_TIMEZONE)).isoformat() + return dt.as_utc(traintime).isoformat() class TrainSensor(SensorEntity): @@ -183,7 +184,7 @@ class TrainSensor(SensorEntity): entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, entry_id)}, manufacturer="Trafikverket", - model="v1.2", + model="v2.0", name=name, configuration_url="https://api.trafikinfo.trafikverket.se/", ) @@ -193,12 +194,12 @@ class TrainSensor(SensorEntity): async def async_update(self) -> None: """Retrieve latest state.""" - when = datetime.now() + when = dt.now() _state: TrainStop | None = None if self._time: departure_day = next_departuredate(self._weekday) - when = datetime.combine(departure_day, self._time).replace( - tzinfo=STOCKHOLM_TIMEZONE + when = datetime.combine( + departure_day, self._time, dt.get_time_zone(self.hass.config.time_zone) ) try: if self._time: @@ -222,17 +223,11 @@ class TrainSensor(SensorEntity): self._attr_available = True # The original datetime doesn't provide a timezone so therefore attaching it here. - self._attr_native_value = _state.advertised_time_at_location.replace( - tzinfo=STOCKHOLM_TIMEZONE - ) + self._attr_native_value = dt.as_utc(_state.advertised_time_at_location) if _state.time_at_location: - self._attr_native_value = _state.time_at_location.replace( - tzinfo=STOCKHOLM_TIMEZONE - ) + self._attr_native_value = dt.as_utc(_state.time_at_location) if _state.estimated_time_at_location: - self._attr_native_value = _state.estimated_time_at_location.replace( - tzinfo=STOCKHOLM_TIMEZONE - ) + self._attr_native_value = dt.as_utc(_state.estimated_time_at_location) self._update_attributes(_state) diff --git a/homeassistant/components/trafikverket_weatherstation/manifest.json b/homeassistant/components/trafikverket_weatherstation/manifest.json index 4001856b703..e7efca9b24a 100644 --- a/homeassistant/components/trafikverket_weatherstation/manifest.json +++ b/homeassistant/components/trafikverket_weatherstation/manifest.json @@ -2,7 +2,7 @@ "domain": "trafikverket_weatherstation", "name": "Trafikverket Weather Station", "documentation": "https://www.home-assistant.io/integrations/trafikverket_weatherstation", - "requirements": ["pytrafikverket==0.1.6.2"], + "requirements": ["pytrafikverket==0.2.0.1"], "codeowners": ["@endor-force", "@gjohansson-ST"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/trafikverket_weatherstation/sensor.py b/homeassistant/components/trafikverket_weatherstation/sensor.py index e659e42f82b..c54c9f67388 100644 --- a/homeassistant/components/trafikverket_weatherstation/sensor.py +++ b/homeassistant/components/trafikverket_weatherstation/sensor.py @@ -24,13 +24,11 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from homeassistant.util.dt import as_utc, get_time_zone +from homeassistant.util.dt import as_utc from .const import ATTRIBUTION, CONF_STATION, DOMAIN, NONE_IS_ZERO_SENSORS from .coordinator import TVDataUpdateCoordinator -STOCKHOLM_TIMEZONE = get_time_zone("Europe/Stockholm") - @dataclass class TrafikverketRequiredKeysMixin: @@ -156,8 +154,8 @@ async def async_setup_entry( def _to_datetime(measuretime: str) -> datetime: """Return isoformatted utc time.""" - time_obj = datetime.strptime(measuretime, "%Y-%m-%dT%H:%M:%S") - return as_utc(time_obj.replace(tzinfo=STOCKHOLM_TIMEZONE)) + time_obj = datetime.strptime(measuretime, "%Y-%m-%dT%H:%M:%S.%f%z") + return as_utc(time_obj) class TrafikverketWeatherStation( @@ -184,7 +182,7 @@ class TrafikverketWeatherStation( entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, entry_id)}, manufacturer="Trafikverket", - model="v1.2", + model="v2.0", name=sensor_station, configuration_url="https://api.trafikinfo.trafikverket.se/", ) diff --git a/requirements_all.txt b/requirements_all.txt index 777cd79af74..eae4a2c3920 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1978,7 +1978,7 @@ pytradfri[async]==9.0.0 # homeassistant.components.trafikverket_ferry # homeassistant.components.trafikverket_train # homeassistant.components.trafikverket_weatherstation -pytrafikverket==0.1.6.2 +pytrafikverket==0.2.0.1 # homeassistant.components.usb pyudev==0.22.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 205ac163595..0cb0cf2b69f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1301,7 +1301,7 @@ pytradfri[async]==9.0.0 # homeassistant.components.trafikverket_ferry # homeassistant.components.trafikverket_train # homeassistant.components.trafikverket_weatherstation -pytrafikverket==0.1.6.2 +pytrafikverket==0.2.0.1 # homeassistant.components.usb pyudev==0.22.0 From aadfcc9a6e711f14f3285d6b4763a8d43bc60140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Thu, 5 May 2022 20:57:01 +0200 Subject: [PATCH 233/930] Lower Airzone unique id migration log to debug (#71362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address late @MartinHjelmare PR comments. Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/airzone/__init__.py b/homeassistant/components/airzone/__init__.py index 39f1fc978c3..2882ea18143 100644 --- a/homeassistant/components/airzone/__init__.py +++ b/homeassistant/components/airzone/__init__.py @@ -99,7 +99,7 @@ async def _async_migrate_unique_ids( if entity_unique_id.startswith(entry_id): new_unique_id = f"{unique_id}{entity_unique_id[len(entry_id):]}" - _LOGGER.info( + _LOGGER.debug( "Migrating unique_id from [%s] to [%s]", entity_unique_id, new_unique_id, From 353cc0b8c279ad8d40c78bcd31eddfcf57450ece Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 5 May 2022 14:33:17 -0700 Subject: [PATCH 234/930] Fix importing blueprints (#71365) Co-authored-by: Shay Levy --- homeassistant/helpers/selector.py | 23 ++++++++++++++++++- tests/components/blueprint/test_importer.py | 2 +- .../blueprint/test_websocket_api.py | 12 +++++++--- tests/helpers/test_config_validation.py | 13 ++++++++++- tests/helpers/test_selector.py | 18 ++++++++++----- .../automation/test_event_service.yaml | 2 ++ 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index eecc66c8332..afe74a4d7af 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -5,11 +5,13 @@ from collections.abc import Callable, Sequence from typing import Any, TypedDict, cast import voluptuous as vol +import yaml from homeassistant.backports.enum import StrEnum from homeassistant.const import CONF_MODE, CONF_UNIT_OF_MEASUREMENT from homeassistant.core import split_entity_id, valid_entity_id from homeassistant.util import decorator +from homeassistant.util.yaml.dumper import represent_odict from . import config_validation as cv @@ -71,7 +73,11 @@ class Selector: def serialize(self) -> Any: """Serialize Selector for voluptuous_serialize.""" - return {"selector": {self.selector_type: self.config}} + return {"selector": {self.selector_type: self.serialize_config()}} + + def serialize_config(self) -> Any: + """Serialize config.""" + return self.config SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA = vol.Schema( @@ -623,6 +629,13 @@ class NumberSelector(Selector): """Instantiate a selector.""" super().__init__(config) + def serialize_config(self) -> Any: + """Serialize the selector config.""" + return { + **self.config, + "mode": self.config["mode"].value, + } + def __call__(self, data: Any) -> float: """Validate the passed selection.""" value: float = vol.Coerce(float)(data) @@ -881,3 +894,11 @@ class TimeSelector(Selector): """Validate the passed selection.""" cv.time(data) return cast(str, data) + + +yaml.SafeDumper.add_representer( + Selector, + lambda dumper, value: represent_odict( + dumper, "tag:yaml.org,2002:map", value.serialize() + ), +) diff --git a/tests/components/blueprint/test_importer.py b/tests/components/blueprint/test_importer.py index 0e1e66405e6..806cdb2cb8d 100644 --- a/tests/components/blueprint/test_importer.py +++ b/tests/components/blueprint/test_importer.py @@ -198,7 +198,7 @@ async def test_fetch_blueprint_from_github_url(hass, aioclient_mock, url): assert imported_blueprint.blueprint.domain == "automation" assert imported_blueprint.blueprint.inputs == { "service_to_call": None, - "trigger_event": None, + "trigger_event": {"selector": {"text": {}}}, } assert imported_blueprint.suggested_filename == "balloob/motion_light" assert imported_blueprint.blueprint.metadata["source_url"] == url diff --git a/tests/components/blueprint/test_websocket_api.py b/tests/components/blueprint/test_websocket_api.py index 51b8184354a..40f24d98016 100644 --- a/tests/components/blueprint/test_websocket_api.py +++ b/tests/components/blueprint/test_websocket_api.py @@ -30,7 +30,10 @@ async def test_list_blueprints(hass, hass_ws_client): "test_event_service.yaml": { "metadata": { "domain": "automation", - "input": {"service_to_call": None, "trigger_event": None}, + "input": { + "service_to_call": None, + "trigger_event": {"selector": {"text": {}}}, + }, "name": "Call service based on event", }, }, @@ -89,7 +92,10 @@ async def test_import_blueprint(hass, aioclient_mock, hass_ws_client): "blueprint": { "metadata": { "domain": "automation", - "input": {"service_to_call": None, "trigger_event": None}, + "input": { + "service_to_call": None, + "trigger_event": {"selector": {"text": {}}}, + }, "name": "Call service based on event", "source_url": "https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml", }, @@ -123,7 +129,7 @@ async def test_save_blueprint(hass, aioclient_mock, hass_ws_client): assert msg["success"] assert write_mock.mock_calls assert write_mock.call_args[0] == ( - "blueprint:\n name: Call service based on event\n domain: automation\n input:\n trigger_event:\n service_to_call:\n source_url: https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml\ntrigger:\n platform: event\n event_type: !input 'trigger_event'\naction:\n service: !input 'service_to_call'\n entity_id: light.kitchen\n", + "blueprint:\n name: Call service based on event\n domain: automation\n input:\n trigger_event:\n selector:\n text: {}\n service_to_call:\n source_url: https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml\ntrigger:\n platform: event\n event_type: !input 'trigger_event'\naction:\n service: !input 'service_to_call'\n entity_id: light.kitchen\n", ) diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 64c838e6c02..a5d2223a3d2 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -11,7 +11,7 @@ import pytest import voluptuous as vol import homeassistant -from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers import config_validation as cv, selector, template def test_boolean(): @@ -720,6 +720,17 @@ def test_string_in_serializer(): } +def test_selector_in_serializer(): + """Test selector with custom_serializer.""" + assert cv.custom_serializer(selector.selector({"text": {}})) == { + "selector": { + "text": { + "multiline": False, + } + } + } + + def test_positive_time_period_dict_in_serializer(): """Test positive_time_period_dict with custom_serializer.""" assert cv.custom_serializer(cv.positive_time_period_dict) == { diff --git a/tests/helpers/test_selector.py b/tests/helpers/test_selector.py index cb0ad95eb6b..8c94e3d3c56 100644 --- a/tests/helpers/test_selector.py +++ b/tests/helpers/test_selector.py @@ -2,7 +2,8 @@ import pytest import voluptuous as vol -from homeassistant.helpers import config_validation as cv, selector +from homeassistant.helpers import selector +from homeassistant.util import yaml FAKE_UUID = "a266a680b608c32770e6c45bfe6b8411" @@ -48,10 +49,12 @@ def _test_selector( converter = default_converter # Validate selector configuration - selector.validate_selector({selector_type: schema}) + config = {selector_type: schema} + selector.validate_selector(config) + selector_instance = selector.selector(config) # Use selector in schema and validate - vol_schema = vol.Schema({"selection": selector.selector({selector_type: schema})}) + vol_schema = vol.Schema({"selection": selector_instance}) for selection in valid_selections: assert vol_schema({"selection": selection}) == { "selection": converter(selection) @@ -62,9 +65,12 @@ def _test_selector( # Serialize selector selector_instance = selector.selector({selector_type: schema}) - assert cv.custom_serializer(selector_instance) == { - "selector": {selector_type: selector_instance.config} - } + assert ( + selector.selector(selector_instance.serialize()["selector"]).config + == selector_instance.config + ) + # Test serialized selector can be dumped to YAML + yaml.dump(selector_instance.serialize()) @pytest.mark.parametrize( diff --git a/tests/testing_config/blueprints/automation/test_event_service.yaml b/tests/testing_config/blueprints/automation/test_event_service.yaml index ab067b004ac..648cef39b96 100644 --- a/tests/testing_config/blueprints/automation/test_event_service.yaml +++ b/tests/testing_config/blueprints/automation/test_event_service.yaml @@ -3,6 +3,8 @@ blueprint: domain: automation input: trigger_event: + selector: + text: service_to_call: trigger: platform: event From c8f95b7dfc357480bdf6295b05cf3aba545699ad Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 5 May 2022 14:33:37 -0700 Subject: [PATCH 235/930] Ignore loading system entity category (#71361) --- homeassistant/helpers/entity_registry.py | 4 ++++ tests/helpers/test_entity_registry.py | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index b4dd0820d8c..64ab0323f6c 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -711,6 +711,10 @@ class EntityRegistry: if not valid_entity_id(entity["entity_id"]): continue + # We removed this in 2022.5. Remove this check in 2023.1. + if entity["entity_category"] == "system": + entity["entity_category"] = None + entities[entity["entity_id"]] = RegistryEntry( area_id=entity["area_id"], capabilities=entity["capabilities"], diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index 41ac7412d9c..21d29736bd0 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -297,6 +297,12 @@ async def test_loading_extra_values(hass, hass_storage): "unique_id": "invalid-hass", "disabled_by": er.RegistryEntryDisabler.HASS, }, + { + "entity_id": "test.system_entity", + "platform": "super_platform", + "unique_id": "system-entity", + "entity_category": "system", + }, ] }, } @@ -304,7 +310,7 @@ async def test_loading_extra_values(hass, hass_storage): await er.async_load(hass) registry = er.async_get(hass) - assert len(registry.entities) == 4 + assert len(registry.entities) == 5 entry_with_name = registry.async_get_or_create( "test", "super_platform", "with-name" @@ -327,6 +333,11 @@ async def test_loading_extra_values(hass, hass_storage): assert entry_disabled_user.disabled assert entry_disabled_user.disabled_by is er.RegistryEntryDisabler.USER + entry_system_category = registry.async_get_or_create( + "test", "system_entity", "system-entity" + ) + assert entry_system_category.entity_category is None + def test_async_get_entity_id(registry): """Test that entity_id is returned.""" From afe4892ae6e8a714c312efc3bfb702a65cdb6d45 Mon Sep 17 00:00:00 2001 From: Shai Ungar Date: Fri, 6 May 2022 00:34:30 +0300 Subject: [PATCH 236/930] Add unique ids to sensors (#71367) --- homeassistant/components/sabnzbd/manifest.json | 2 -- homeassistant/components/sabnzbd/sensor.py | 11 ++++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sabnzbd/manifest.json b/homeassistant/components/sabnzbd/manifest.json index 0702446d217..f112893b5e1 100644 --- a/homeassistant/components/sabnzbd/manifest.json +++ b/homeassistant/components/sabnzbd/manifest.json @@ -3,8 +3,6 @@ "name": "SABnzbd", "documentation": "https://www.home-assistant.io/integrations/sabnzbd", "requirements": ["pysabnzbd==1.1.1"], - "dependencies": ["configurator"], - "after_dependencies": ["discovery"], "codeowners": ["@shaiu"], "iot_class": "local_polling", "config_flow": true, diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index 1d661d90848..dee80945e9d 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -14,8 +14,10 @@ from . import DOMAIN, SIGNAL_SABNZBD_UPDATED from ...config_entries import ConfigEntry from ...const import DATA_GIGABYTES, DATA_MEGABYTES, DATA_RATE_MEGABYTES_PER_SECOND from ...core import HomeAssistant +from ...helpers.device_registry import DeviceEntryType +from ...helpers.entity import DeviceInfo from ...helpers.entity_platform import AddEntitiesCallback -from .const import KEY_API_DATA, KEY_NAME +from .const import DEFAULT_NAME, KEY_API_DATA, KEY_NAME @dataclass @@ -127,9 +129,16 @@ class SabnzbdSensor(SensorEntity): self, sabnzbd_api_data, client_name, description: SabnzbdSensorEntityDescription ): """Initialize the sensor.""" + unique_id = description.key + self._attr_unique_id = unique_id self.entity_description = description self._sabnzbd_api = sabnzbd_api_data self._attr_name = f"{client_name} {description.name}" + self._attr_device_info = DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, DOMAIN)}, + name=DEFAULT_NAME, + ) async def async_added_to_hass(self): """Call when entity about to be added to hass.""" From 1d95a37eab2907494d1679d3e2d720e524b0420e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 5 May 2022 15:01:25 -0700 Subject: [PATCH 237/930] Bump pychromecast to 12.1.1 (#71377) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index 7144a3872f5..cee46913937 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==12.1.0"], + "requirements": ["pychromecast==12.1.1"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index eae4a2c3920..1f68cb43b04 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1402,7 +1402,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==12.1.0 +pychromecast==12.1.1 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0cb0cf2b69f..911f726effd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -941,7 +941,7 @@ pybotvac==0.0.23 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==12.1.0 +pychromecast==12.1.1 # homeassistant.components.climacell pyclimacell==0.18.2 From 6722d060dd038aad2c9adcd4efa3437b714d9689 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 18:49:30 -0400 Subject: [PATCH 238/930] Bump yalexs to 1.1.24 (#71372) --- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index 329522bef28..c1156c9aea9 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.23"], + "requirements": ["yalexs==1.1.24"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 1f68cb43b04..f47df3386ad 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2465,7 +2465,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.23 +yalexs==1.1.24 # homeassistant.components.yeelight yeelight==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 911f726effd..4d7b846f10a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1614,7 +1614,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.23 +yalexs==1.1.24 # homeassistant.components.yeelight yeelight==0.7.10 From c22cf3b3d2e4d079499473500abf8a73e5b80163 Mon Sep 17 00:00:00 2001 From: Graham Arthur Blair Date: Thu, 5 May 2022 16:12:51 -0700 Subject: [PATCH 239/930] Add buttons to Ring chime devices to play ding and motion chimes (#71370) Co-authored-by: Paulus Schoutsen --- homeassistant/components/ring/__init__.py | 1 + homeassistant/components/ring/button.py | 65 +++++++++++++++++++++++ tests/components/ring/test_button.py | 55 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 homeassistant/components/ring/button.py create mode 100644 tests/components/ring/test_button.py diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 5d834976370..2a922a7bb3e 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -32,6 +32,7 @@ DEFAULT_ENTITY_NAMESPACE = "ring" PLATFORMS = [ Platform.BINARY_SENSOR, + Platform.BUTTON, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH, diff --git a/homeassistant/components/ring/button.py b/homeassistant/components/ring/button.py new file mode 100644 index 00000000000..04f9f7950f8 --- /dev/null +++ b/homeassistant/components/ring/button.py @@ -0,0 +1,65 @@ +"""This component provides HA button support for Ring Chimes.""" +import logging + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import DOMAIN +from .entity import RingEntityMixin + +_LOGGER = logging.getLogger(__name__) + +BELL_ICON = "mdi:bell-ring" + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Create the buttons for the Ring devices.""" + devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] + buttons = [] + + # add one button for each test chime type (ding, motion) + for device in devices["chimes"]: + buttons.append(ChimeButton(config_entry.entry_id, device, "ding")) + buttons.append(ChimeButton(config_entry.entry_id, device, "motion")) + + async_add_entities(buttons) + + +class BaseRingButton(RingEntityMixin, ButtonEntity): + """Represents a Button for controlling an aspect of a ring device.""" + + def __init__(self, config_entry_id, device, button_identifier, button_name): + """Initialize the switch.""" + super().__init__(config_entry_id, device) + self._button_identifier = button_identifier + self._button_name = button_name + self._attr_unique_id = f"{self._device.id}-{self._button_identifier}" + + @property + def name(self): + """Name of the device.""" + return f"{self._device.name} {self._button_name}" + + +class ChimeButton(BaseRingButton): + """Creates a button to play the test chime of a Chime device.""" + + _attr_icon = BELL_ICON + + def __init__(self, config_entry_id, device, kind): + """Initialize the button for a device with a chime.""" + super().__init__( + config_entry_id, device, f"play-chime-{kind}", f"Play chime: {kind}" + ) + self.kind = kind + + def press(self) -> None: + """Send the test chime request.""" + if not self._device.test_sound(kind=self.kind): + _LOGGER.error("Failed to ring chime sound on %s", self.name) diff --git a/tests/components/ring/test_button.py b/tests/components/ring/test_button.py new file mode 100644 index 00000000000..62b2fcd8a78 --- /dev/null +++ b/tests/components/ring/test_button.py @@ -0,0 +1,55 @@ +"""The tests for the Ring button platform.""" + +from homeassistant.const import Platform +from homeassistant.helpers import entity_registry as er + +from .common import setup_platform + + +async def test_entity_registry(hass, requests_mock): + """Tests that the devices are registered in the entity registry.""" + await setup_platform(hass, Platform.BUTTON) + entity_registry = er.async_get(hass) + + entry = entity_registry.async_get("button.downstairs_play_chime_ding") + assert entry.unique_id == "123456-play-chime-ding" + + entry = entity_registry.async_get("button.downstairs_play_chime_motion") + assert entry.unique_id == "123456-play-chime-motion" + + +async def test_play_chime_buttons_report_correctly(hass, requests_mock): + """Tests that the initial state of a device that should be on is correct.""" + await setup_platform(hass, Platform.BUTTON) + + state = hass.states.get("button.downstairs_play_chime_ding") + assert state.attributes.get("friendly_name") == "Downstairs Play chime: ding" + assert state.attributes.get("icon") == "mdi:bell-ring" + + state = hass.states.get("button.downstairs_play_chime_motion") + assert state.attributes.get("friendly_name") == "Downstairs Play chime: motion" + assert state.attributes.get("icon") == "mdi:bell-ring" + + +async def test_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.BUTTON) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "button", + "press", + {"entity_id": "button.downstairs_play_chime_ding"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url From 07706fa62ac4a34fcfa0dd2b5cf5e84ea8b4882f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 6 May 2022 00:22:16 +0000 Subject: [PATCH 240/930] [ci skip] Translation update --- .../translations/tr.json | 3 ++ .../components/asuswrt/translations/hu.json | 1 + .../components/asuswrt/translations/no.json | 1 + .../components/asuswrt/translations/sv.json | 3 ++ .../components/asuswrt/translations/tr.json | 3 ++ .../asuswrt/translations/zh-Hant.json | 1 + .../components/axis/translations/sv.json | 2 +- .../components/deconz/translations/sv.json | 1 + .../components/deconz/translations/tr.json | 1 + .../components/dlna_dmr/translations/ca.json | 1 + .../components/dlna_dmr/translations/de.json | 1 + .../components/dlna_dmr/translations/el.json | 1 + .../components/dlna_dmr/translations/et.json | 1 + .../components/dlna_dmr/translations/fr.json | 1 + .../components/dlna_dmr/translations/hu.json | 1 + .../components/dlna_dmr/translations/id.json | 1 + .../components/dlna_dmr/translations/no.json | 1 + .../dlna_dmr/translations/pt-BR.json | 1 + .../components/dlna_dmr/translations/sv.json | 11 ++++++ .../components/dlna_dmr/translations/tr.json | 1 + .../dlna_dmr/translations/zh-Hant.json | 1 + .../components/esphome/translations/sv.json | 1 + .../components/generic/translations/sv.json | 7 ++++ .../components/hassio/translations/ca.json | 4 +-- .../homekit_controller/translations/sv.json | 2 +- .../components/insteon/translations/sv.json | 7 ++++ .../intellifire/translations/tr.json | 2 +- .../components/isy994/translations/sv.json | 11 ++++++ .../components/isy994/translations/tr.json | 13 +++++-- .../components/lovelace/translations/sv.json | 7 ++++ .../components/meater/translations/sv.json | 17 +++++++++ .../components/meater/translations/tr.json | 9 +++++ .../media_player/translations/tr.json | 3 ++ .../components/onewire/translations/sv.json | 9 +++++ .../components/qnap_qsw/translations/sv.json | 8 +++++ .../components/qnap_qsw/translations/tr.json | 21 +++++++++++ .../qnap_qsw/translations/zh-Hans.json | 21 +++++++++++ .../components/recorder/translations/sv.json | 8 +++++ .../components/recorder/translations/tr.json | 8 +++++ .../components/sabnzbd/translations/sv.json | 17 +++++++++ .../components/sabnzbd/translations/tr.json | 18 ++++++++++ .../simplisafe/translations/sv.json | 10 ++++++ .../simplisafe/translations/tr.json | 17 +++++++-- .../components/slimproto/translations/sv.json | 13 +++++++ .../components/slimproto/translations/tr.json | 7 ++++ .../components/sql/translations/sv.json | 23 ++++++++++++ .../components/sql/translations/tr.json | 4 +++ .../steam_online/translations/sv.json | 24 +++++++++++++ .../steam_online/translations/tr.json | 36 +++++++++++++++++++ .../steam_online/translations/zh-Hans.json | 27 ++++++++++++++ .../components/tautulli/translations/sv.json | 9 +++++ .../components/tautulli/translations/tr.json | 30 ++++++++++++++++ .../trafikverket_ferry/translations/tr.json | 30 ++++++++++++++++ .../components/unifi/translations/sv.json | 3 ++ .../components/unifi/translations/tr.json | 5 ++- .../utility_meter/translations/sv.json | 3 ++ .../components/vulcan/translations/ca.json | 9 +++++ .../components/vulcan/translations/el.json | 9 +++++ .../components/vulcan/translations/en.json | 9 +++++ .../components/vulcan/translations/fr.json | 9 +++++ .../components/vulcan/translations/hu.json | 9 +++++ .../components/vulcan/translations/pt-BR.json | 9 +++++ .../components/vulcan/translations/tr.json | 9 +++++ 63 files changed, 524 insertions(+), 11 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/tr.json create mode 100644 homeassistant/components/dlna_dmr/translations/sv.json create mode 100644 homeassistant/components/generic/translations/sv.json create mode 100644 homeassistant/components/insteon/translations/sv.json create mode 100644 homeassistant/components/lovelace/translations/sv.json create mode 100644 homeassistant/components/meater/translations/sv.json create mode 100644 homeassistant/components/qnap_qsw/translations/sv.json create mode 100644 homeassistant/components/qnap_qsw/translations/tr.json create mode 100644 homeassistant/components/qnap_qsw/translations/zh-Hans.json create mode 100644 homeassistant/components/recorder/translations/sv.json create mode 100644 homeassistant/components/recorder/translations/tr.json create mode 100644 homeassistant/components/sabnzbd/translations/sv.json create mode 100644 homeassistant/components/sabnzbd/translations/tr.json create mode 100644 homeassistant/components/slimproto/translations/sv.json create mode 100644 homeassistant/components/slimproto/translations/tr.json create mode 100644 homeassistant/components/sql/translations/sv.json create mode 100644 homeassistant/components/steam_online/translations/sv.json create mode 100644 homeassistant/components/steam_online/translations/tr.json create mode 100644 homeassistant/components/steam_online/translations/zh-Hans.json create mode 100644 homeassistant/components/tautulli/translations/sv.json create mode 100644 homeassistant/components/tautulli/translations/tr.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/tr.json create mode 100644 homeassistant/components/utility_meter/translations/sv.json diff --git a/homeassistant/components/application_credentials/translations/tr.json b/homeassistant/components/application_credentials/translations/tr.json new file mode 100644 index 00000000000..70da28ed814 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/tr.json @@ -0,0 +1,3 @@ +{ + "title": "Uygulama Kimlik Bilgileri" +} \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/hu.json b/homeassistant/components/asuswrt/translations/hu.json index 8cec2c31736..86339d7274c 100644 --- a/homeassistant/components/asuswrt/translations/hu.json +++ b/homeassistant/components/asuswrt/translations/hu.json @@ -2,6 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Lehetetlen \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3t meghat\u00e1rozni az eszk\u00f6zh\u00f6z", + "no_unique_id": "Az \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3 n\u00e9lk\u00fcli eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges", "not_unique_id_exist": "Egy \u00e9rv\u00e9nyes UniqueID azonos\u00edt\u00f3val nem rendelkez\u0151 eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges", "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, diff --git a/homeassistant/components/asuswrt/translations/no.json b/homeassistant/components/asuswrt/translations/no.json index b68e771646f..5aa48649989 100644 --- a/homeassistant/components/asuswrt/translations/no.json +++ b/homeassistant/components/asuswrt/translations/no.json @@ -2,6 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Umulig \u00e5 bestemme en gyldig unik ID for enheten", + "no_unique_id": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", "not_unique_id_exist": "En enhet uten en gyldig UniqueID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, diff --git a/homeassistant/components/asuswrt/translations/sv.json b/homeassistant/components/asuswrt/translations/sv.json index 9189aba6208..ba461c681e3 100644 --- a/homeassistant/components/asuswrt/translations/sv.json +++ b/homeassistant/components/asuswrt/translations/sv.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Om\u00f6jligt att fastst\u00e4lla ett giltigt unikt ID f\u00f6r enheten", + "no_unique_id": "En enhet utan ett giltigt unikt ID \u00e4r redan konfigurerad. Konfiguration av flera instanser \u00e4r inte m\u00f6jlig", + "not_unique_id_exist": "En enhet utan ett giltigt unikt ID \u00e4r redan konfigurerad. Konfiguration av flera instanser \u00e4r inte m\u00f6jlig", "single_instance_allowed": "Redan konfigurerad. Bara en konfiguration \u00e4r m\u00f6jlig." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/tr.json b/homeassistant/components/asuswrt/translations/tr.json index 3225c78b40a..10be601fd10 100644 --- a/homeassistant/components/asuswrt/translations/tr.json +++ b/homeassistant/components/asuswrt/translations/tr.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Cihaz i\u00e7in ge\u00e7erli bir benzersiz kimlik belirlemek imkans\u0131z", + "no_unique_id": "Ge\u00e7erli bir benzersiz kimli\u011fi olmayan bir cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Birden \u00e7ok \u00f6rne\u011fin yap\u0131land\u0131r\u0131lmas\u0131 m\u00fcmk\u00fcn de\u011fil", + "not_unique_id_exist": "Ge\u00e7erli bir benzersiz kimli\u011fi olmayan bir cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Birden \u00e7ok \u00f6rne\u011fin yap\u0131land\u0131r\u0131lmas\u0131 m\u00fcmk\u00fcn de\u011fil", "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/zh-Hant.json b/homeassistant/components/asuswrt/translations/zh-Hant.json index f78a0466a5c..d5955313b93 100644 --- a/homeassistant/components/asuswrt/translations/zh-Hant.json +++ b/homeassistant/components/asuswrt/translations/zh-Hant.json @@ -2,6 +2,7 @@ "config": { "abort": { "invalid_unique_id": "\u7121\u6cd5\u78ba\u8a8d\u88dd\u7f6e\u6709\u6548\u552f\u4e00 ID", + "no_unique_id": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b", "not_unique_id_exist": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b", "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, diff --git a/homeassistant/components/axis/translations/sv.json b/homeassistant/components/axis/translations/sv.json index 22e9344b64d..e04267cb5d7 100644 --- a/homeassistant/components/axis/translations/sv.json +++ b/homeassistant/components/axis/translations/sv.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Enheten \u00e4r redan konfigurerad", - "link_local_address": "Link local addresses are not supported", + "link_local_address": "Lokala l\u00e4nkadresser st\u00f6ds inte", "not_axis_device": "Uppt\u00e4ckte enhet som inte \u00e4r en Axis enhet" }, "error": { diff --git a/homeassistant/components/deconz/translations/sv.json b/homeassistant/components/deconz/translations/sv.json index d7ec321ff36..1a867d0c7fb 100644 --- a/homeassistant/components/deconz/translations/sv.json +++ b/homeassistant/components/deconz/translations/sv.json @@ -8,6 +8,7 @@ "updated_instance": "Uppdaterad deCONZ-instans med ny v\u00e4rdadress" }, "error": { + "linking_not_possible": "Det gick inte att l\u00e4nka till gatewayen", "no_key": "Det gick inte att ta emot en API-nyckel" }, "flow_title": "deCONZ Zigbee gateway ({host})", diff --git a/homeassistant/components/deconz/translations/tr.json b/homeassistant/components/deconz/translations/tr.json index 021c4845989..77045776c98 100644 --- a/homeassistant/components/deconz/translations/tr.json +++ b/homeassistant/components/deconz/translations/tr.json @@ -9,6 +9,7 @@ "updated_instance": "DeCONZ yeni ana bilgisayar adresiyle g\u00fcncelle\u015ftirildi" }, "error": { + "linking_not_possible": "A\u011f ge\u00e7idi ile ba\u011flant\u0131 kurulamad\u0131", "no_key": "API anahtar\u0131 al\u0131namad\u0131" }, "flow_title": "{host}", diff --git a/homeassistant/components/dlna_dmr/translations/ca.json b/homeassistant/components/dlna_dmr/translations/ca.json index 944af3bfebc..7c528d5ad29 100644 --- a/homeassistant/components/dlna_dmr/translations/ca.json +++ b/homeassistant/components/dlna_dmr/translations/ca.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Mostra fitxers multim\u00e8dia incompatibles mentre navegui", "callback_url_override": "URL de crida de l'oient d'esdeveniments", "listen_port": "Port de l'oient d'esdeveniments (aleatori si no es defineix)", "poll_availability": "Sondeja per saber la disponibilitat del dispositiu" diff --git a/homeassistant/components/dlna_dmr/translations/de.json b/homeassistant/components/dlna_dmr/translations/de.json index e37786c401c..baf1a53efca 100644 --- a/homeassistant/components/dlna_dmr/translations/de.json +++ b/homeassistant/components/dlna_dmr/translations/de.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Inkompatible Medien beim Durchsuchen anzeigen", "callback_url_override": "R\u00fcckruf-URL des Ereignis-Listeners", "listen_port": "Port des Ereignis-Listeners (zuf\u00e4llig, wenn nicht festgelegt)", "poll_availability": "Abfrage der Ger\u00e4teverf\u00fcgbarkeit" diff --git a/homeassistant/components/dlna_dmr/translations/el.json b/homeassistant/components/dlna_dmr/translations/el.json index 91a1d353bcc..5dc56ef3e37 100644 --- a/homeassistant/components/dlna_dmr/translations/el.json +++ b/homeassistant/components/dlna_dmr/translations/el.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03bc\u03b7 \u03c3\u03c5\u03bc\u03b2\u03b1\u03c4\u03ce\u03bd \u03bc\u03ad\u03c3\u03c9\u03bd \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03b5\u03c1\u03b9\u03ae\u03b3\u03b7\u03c3\u03b7", "callback_url_override": "URL \u03ba\u03bb\u03ae\u03c3\u03b7\u03c2 \u03b1\u03ba\u03c1\u03bf\u03b1\u03c4\u03ae \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2", "listen_port": "\u0398\u03cd\u03c1\u03b1 \u03b1\u03ba\u03c1\u03cc\u03b1\u03c3\u03b7\u03c2 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03c9\u03bd (\u03c4\u03c5\u03c7\u03b1\u03af\u03b1 \u03b1\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af)", "poll_availability": "\u0388\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03b8\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" diff --git a/homeassistant/components/dlna_dmr/translations/et.json b/homeassistant/components/dlna_dmr/translations/et.json index b5019f5f9a1..1981b6eefde 100644 --- a/homeassistant/components/dlna_dmr/translations/et.json +++ b/homeassistant/components/dlna_dmr/translations/et.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Kuva sirvimisel \u00fchildumatu meedia", "callback_url_override": "S\u00fcndmuse kuulaja URL", "listen_port": "S\u00fcndmuste kuulaja port (juhuslik kui pole m\u00e4\u00e4ratud)", "poll_availability": "K\u00fcsitle seadme saadavuse kohta" diff --git a/homeassistant/components/dlna_dmr/translations/fr.json b/homeassistant/components/dlna_dmr/translations/fr.json index 6554a3994d6..87686251c4d 100644 --- a/homeassistant/components/dlna_dmr/translations/fr.json +++ b/homeassistant/components/dlna_dmr/translations/fr.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Afficher les fichiers multim\u00e9dias incompatibles lors de la navigation", "callback_url_override": "URL de rappel de l'\u00e9couteur d'\u00e9v\u00e9nement", "listen_port": "Port d'\u00e9coute d'\u00e9v\u00e9nement (al\u00e9atoire s'il n'est pas d\u00e9fini)", "poll_availability": "Sondage pour la disponibilit\u00e9 de l'appareil" diff --git a/homeassistant/components/dlna_dmr/translations/hu.json b/homeassistant/components/dlna_dmr/translations/hu.json index 5e2ba148602..2773cf2bae4 100644 --- a/homeassistant/components/dlna_dmr/translations/hu.json +++ b/homeassistant/components/dlna_dmr/translations/hu.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Inkompatibilis m\u00e9dia megjelen\u00edt\u00e9se b\u00f6ng\u00e9sz\u00e9s k\u00f6zben", "callback_url_override": "Esem\u00e9nyfigyel\u0151 visszah\u00edv\u00e1si URL (callback)", "listen_port": "Esem\u00e9nyfigyel\u0151 port (v\u00e9letlenszer\u0171, ha nincs be\u00e1ll\u00edtva)", "poll_availability": "Eszk\u00f6z el\u00e9r\u00e9s\u00e9nek tesztel\u00e9se lek\u00e9rdez\u00e9ssel" diff --git a/homeassistant/components/dlna_dmr/translations/id.json b/homeassistant/components/dlna_dmr/translations/id.json index 152c4f73a56..415ab7dc653 100644 --- a/homeassistant/components/dlna_dmr/translations/id.json +++ b/homeassistant/components/dlna_dmr/translations/id.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Tampilkan media yang tidak kompatibel saat menjelajah", "callback_url_override": "URL panggilan balik pendengar peristiwa", "listen_port": "Port pendengar peristiwa (acak jika tidak diatur)", "poll_availability": "Polling untuk ketersediaan perangkat" diff --git a/homeassistant/components/dlna_dmr/translations/no.json b/homeassistant/components/dlna_dmr/translations/no.json index a1ce1fdce32..04fed89ca78 100644 --- a/homeassistant/components/dlna_dmr/translations/no.json +++ b/homeassistant/components/dlna_dmr/translations/no.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Vis inkompatible medier n\u00e5r du surfer", "callback_url_override": "URL for tilbakeringing av hendelseslytter", "listen_port": "Hendelseslytterport (tilfeldig hvis den ikke er angitt)", "poll_availability": "Avstemning for tilgjengelighet av enheter" diff --git a/homeassistant/components/dlna_dmr/translations/pt-BR.json b/homeassistant/components/dlna_dmr/translations/pt-BR.json index 0df5a60e565..5caf64af4b1 100644 --- a/homeassistant/components/dlna_dmr/translations/pt-BR.json +++ b/homeassistant/components/dlna_dmr/translations/pt-BR.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Mostrar m\u00eddia incompat\u00edvel ao navegar", "callback_url_override": "URL de retorno do ouvinte de eventos", "listen_port": "Porta do ouvinte de eventos (aleat\u00f3rio se n\u00e3o estiver definido)", "poll_availability": "Pesquisa de disponibilidade do dispositivo" diff --git a/homeassistant/components/dlna_dmr/translations/sv.json b/homeassistant/components/dlna_dmr/translations/sv.json new file mode 100644 index 00000000000..8f95dda75db --- /dev/null +++ b/homeassistant/components/dlna_dmr/translations/sv.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "browse_unfiltered": "Visa inkompatibla media n\u00e4r du surfar" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/tr.json b/homeassistant/components/dlna_dmr/translations/tr.json index 59c5221b870..bee4a922a75 100644 --- a/homeassistant/components/dlna_dmr/translations/tr.json +++ b/homeassistant/components/dlna_dmr/translations/tr.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Tarama s\u0131ras\u0131nda uyumsuz medyay\u0131 g\u00f6ster", "callback_url_override": "Olay dinleyici geri \u00e7a\u011f\u0131rma URL'si", "listen_port": "Olay dinleyici ba\u011flant\u0131 noktas\u0131 (ayarlanmam\u0131\u015fsa rastgele)", "poll_availability": "Cihaz kullan\u0131labilirli\u011fi i\u00e7in anket" diff --git a/homeassistant/components/dlna_dmr/translations/zh-Hant.json b/homeassistant/components/dlna_dmr/translations/zh-Hant.json index f085767565d..c75334df1ae 100644 --- a/homeassistant/components/dlna_dmr/translations/zh-Hant.json +++ b/homeassistant/components/dlna_dmr/translations/zh-Hant.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "\u7576\u700f\u89bd\u6642\u986f\u793a\u4e0d\u76f8\u5bb9\u5a92\u9ad4", "callback_url_override": "\u4e8b\u4ef6\u76e3\u807d\u56de\u547c URL", "listen_port": "\u4e8b\u4ef6\u76e3\u807d\u901a\u8a0a\u57e0\uff08\u672a\u8a2d\u7f6e\u5247\u70ba\u96a8\u6a5f\uff09", "poll_availability": "\u67e5\u8a62\u88dd\u7f6e\u53ef\u7528\u6027" diff --git a/homeassistant/components/esphome/translations/sv.json b/homeassistant/components/esphome/translations/sv.json index 40fbbf86bc6..88a9ca92377 100644 --- a/homeassistant/components/esphome/translations/sv.json +++ b/homeassistant/components/esphome/translations/sv.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "ESP \u00e4r redan konfigurerad", + "already_in_progress": "Konfigurationsfl\u00f6det p\u00e5g\u00e5r redan", "reauth_successful": "\u00c5terautentisering lyckades" }, "error": { diff --git a/homeassistant/components/generic/translations/sv.json b/homeassistant/components/generic/translations/sv.json new file mode 100644 index 00000000000..78033d17c6e --- /dev/null +++ b/homeassistant/components/generic/translations/sv.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "Inga enheter hittades i n\u00e4tverket" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 7813d970d0e..6a6874662b2 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -2,7 +2,7 @@ "system_health": { "info": { "board": "Placa", - "disk_total": "Total disc", + "disk_total": "Total del disc", "disk_used": "Emmagatzematge utilitzat", "docker_version": "Versi\u00f3 de Docker", "healthy": "Saludable", @@ -11,7 +11,7 @@ "supervisor_api": "API del Supervisor", "supervisor_version": "Versi\u00f3 del Supervisor", "supported": "Compatible", - "update_channel": "Canal d'actualitzaci\u00f3", + "update_channel": "Canal d'actualitzacions", "version_api": "Versi\u00f3 d'APIs" } } diff --git a/homeassistant/components/homekit_controller/translations/sv.json b/homeassistant/components/homekit_controller/translations/sv.json index 71425e0f208..348c0305e03 100644 --- a/homeassistant/components/homekit_controller/translations/sv.json +++ b/homeassistant/components/homekit_controller/translations/sv.json @@ -22,7 +22,7 @@ "data": { "pairing_code": "Parningskod" }, - "description": "Ange din HomeKit-parningskod (i formatet XXX-XX-XXX) f\u00f6r att anv\u00e4nda det h\u00e4r tillbeh\u00f6ret", + "description": "HomeKit Controller kommunicerar med {name} \u00f6ver det lokala n\u00e4tverket med hj\u00e4lp av en s\u00e4ker krypterad anslutning utan en separat HomeKit-kontroller eller iCloud. Ange din HomeKit-kopplingskod (i formatet XXX-XX-XXX) f\u00f6r att anv\u00e4nda detta tillbeh\u00f6r. Denna kod finns vanligtvis p\u00e5 sj\u00e4lva enheten eller i f\u00f6rpackningen.", "title": "Para HomeKit-tillbeh\u00f6r" }, "user": { diff --git a/homeassistant/components/insteon/translations/sv.json b/homeassistant/components/insteon/translations/sv.json new file mode 100644 index 00000000000..b8b6834022c --- /dev/null +++ b/homeassistant/components/insteon/translations/sv.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "not_insteon_device": "Uppt\u00e4ckt enhet \u00e4r inte en Insteon-enhet" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/tr.json b/homeassistant/components/intellifire/translations/tr.json index 3a22e7c3680..b5be24edd2f 100644 --- a/homeassistant/components/intellifire/translations/tr.json +++ b/homeassistant/components/intellifire/translations/tr.json @@ -24,7 +24,7 @@ }, "manual_device_entry": { "data": { - "host": "Sunucu (IP Adresi)" + "host": "Ana Bilgisayar (IP Adresi)" }, "description": "Yerel Yap\u0131land\u0131rma" }, diff --git a/homeassistant/components/isy994/translations/sv.json b/homeassistant/components/isy994/translations/sv.json index 23c825f256f..bd14c77dd60 100644 --- a/homeassistant/components/isy994/translations/sv.json +++ b/homeassistant/components/isy994/translations/sv.json @@ -1,6 +1,17 @@ { "config": { + "error": { + "reauth_successful": "\u00c5terautentiseringen lyckades" + }, "step": { + "reauth_confirm": { + "data": { + "password": "L\u00f6senord", + "username": "Anv\u00e4ndarnamn" + }, + "description": "Inloggningsuppgifterna f\u00f6r {host} \u00e4r inte l\u00e4ngre giltiga.", + "title": "Autentisera din ISY igen" + }, "user": { "data": { "username": "Anv\u00e4ndarnamn" diff --git a/homeassistant/components/isy994/translations/tr.json b/homeassistant/components/isy994/translations/tr.json index 78fc66196f1..3999ee16163 100644 --- a/homeassistant/components/isy994/translations/tr.json +++ b/homeassistant/components/isy994/translations/tr.json @@ -7,10 +7,19 @@ "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "invalid_host": "Ana bilgisayar giri\u015fi tam URL bi\u00e7iminde de\u011fildi, \u00f6r. http://192.168.10.100:80", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", "unknown": "Beklenmeyen hata" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Parola", + "username": "Kullan\u0131c\u0131 Ad\u0131" + }, + "description": "{host} kimlik bilgileri art\u0131k ge\u00e7erli de\u011fil.", + "title": "ISY'nizi yeniden do\u011frulay\u0131n" + }, "user": { "data": { "host": "URL", @@ -19,7 +28,7 @@ "username": "Kullan\u0131c\u0131 Ad\u0131" }, "description": "Ana bilgisayar giri\u015fi tam URL bi\u00e7iminde olmal\u0131d\u0131r, \u00f6r. http://192.168.10.100:80", - "title": "ISY994'\u00fcn\u00fcze ba\u011flan\u0131n" + "title": "ISY'nize ba\u011flan\u0131n" } } }, @@ -33,7 +42,7 @@ "variable_sensor_string": "De\u011fi\u015fken Sens\u00f6r Dizesi" }, "description": "ISY Entegrasyonu i\u00e7in se\u00e7enekleri ayarlay\u0131n:\n \u2022 D\u00fc\u011f\u00fcm Sens\u00f6r\u00fc Dizisi: Ad\u0131nda 'D\u00fc\u011f\u00fcm Sens\u00f6r\u00fc Dizisi' i\u00e7eren herhangi bir cihaz veya klas\u00f6r, bir sens\u00f6r veya ikili sens\u00f6r olarak ele al\u0131nacakt\u0131r.\n \u2022 Ignore String: Ad\u0131nda 'Ignore String' olan herhangi bir cihaz yoksay\u0131lacakt\u0131r.\n \u2022 De\u011fi\u015fken Sens\u00f6r Dizisi: 'De\u011fi\u015fken Sens\u00f6r Dizisi' i\u00e7eren herhangi bir de\u011fi\u015fken sens\u00f6r olarak eklenecektir.\n \u2022 I\u015f\u0131k Parlakl\u0131\u011f\u0131n\u0131 Geri Y\u00fckle: Etkinle\u015ftirilirse, bir \u0131\u015f\u0131k a\u00e7\u0131ld\u0131\u011f\u0131nda cihaz\u0131n yerle\u015fik On-Level yerine \u00f6nceki parlakl\u0131k geri y\u00fcklenir.", - "title": "ISY994 Se\u00e7enekleri" + "title": "ISY Se\u00e7enekleri" } } }, diff --git a/homeassistant/components/lovelace/translations/sv.json b/homeassistant/components/lovelace/translations/sv.json new file mode 100644 index 00000000000..b622e036a0f --- /dev/null +++ b/homeassistant/components/lovelace/translations/sv.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "dashboards": "Kontrollpaneler" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/sv.json b/homeassistant/components/meater/translations/sv.json new file mode 100644 index 00000000000..383fbbeb5a6 --- /dev/null +++ b/homeassistant/components/meater/translations/sv.json @@ -0,0 +1,17 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "L\u00f6senord" + }, + "description": "Bekr\u00e4fta l\u00f6senordet f\u00f6r Meater Cloud-kontot {username}." + }, + "user": { + "data_description": { + "username": "Meater Cloud anv\u00e4ndarnamn, vanligtvis en e-postadress." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/tr.json b/homeassistant/components/meater/translations/tr.json index 03f77d2ed51..14618f6c955 100644 --- a/homeassistant/components/meater/translations/tr.json +++ b/homeassistant/components/meater/translations/tr.json @@ -6,11 +6,20 @@ "unknown_auth_error": "Beklenmeyen hata" }, "step": { + "reauth_confirm": { + "data": { + "password": "Parola" + }, + "description": "Meater Bulut hesab\u0131 {username} i\u00e7in parolay\u0131 onaylay\u0131n." + }, "user": { "data": { "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" }, + "data_description": { + "username": "Meater Cloud kullan\u0131c\u0131 ad\u0131, genellikle bir e-posta adresi." + }, "description": "Meater Cloud hesab\u0131n\u0131z\u0131 kurun." } } diff --git a/homeassistant/components/media_player/translations/tr.json b/homeassistant/components/media_player/translations/tr.json index 5782730f6bf..62958c54fea 100644 --- a/homeassistant/components/media_player/translations/tr.json +++ b/homeassistant/components/media_player/translations/tr.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} arabelle\u011fe al\u0131yor", "is_idle": "{entity_name} bo\u015fta", "is_off": "{entity_name} kapal\u0131", "is_on": "{entity_name} a\u00e7\u0131k", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} oynat\u0131l\u0131yor" }, "trigger_type": { + "buffering": "{entity_name} arabelle\u011fe almaya ba\u015flar", "changed_states": "{entity_name} durumlar\u0131 de\u011fi\u015ftirdi", "idle": "{entity_name} bo\u015fta", "paused": "{entity_name} duraklat\u0131ld\u0131", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u00d6n belle\u011fe al\u0131n\u0131yor", "idle": "Bo\u015fta", "off": "Kapal\u0131", "on": "A\u00e7\u0131k", diff --git a/homeassistant/components/onewire/translations/sv.json b/homeassistant/components/onewire/translations/sv.json index e5717348568..3265a54aeaf 100644 --- a/homeassistant/components/onewire/translations/sv.json +++ b/homeassistant/components/onewire/translations/sv.json @@ -7,5 +7,14 @@ } } } + }, + "options": { + "step": { + "device_selection": { + "data": { + "device_selection": "V\u00e4lj enheter att konfigurera" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/sv.json b/homeassistant/components/qnap_qsw/translations/sv.json new file mode 100644 index 00000000000..416ef964cf3 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/sv.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten \u00e4r redan konfigurerad", + "invalid_id": "Enheten returnerade ett ogiltigt unikt ID" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/tr.json b/homeassistant/components/qnap_qsw/translations/tr.json new file mode 100644 index 00000000000..309f2da3a90 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/tr.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "invalid_id": "Cihaz ge\u00e7ersiz bir benzersiz kimlik d\u00f6nd\u00fcrd\u00fc" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" + }, + "step": { + "user": { + "data": { + "password": "Parola", + "url": "URL", + "username": "Kullan\u0131c\u0131 Ad\u0131" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/zh-Hans.json b/homeassistant/components/qnap_qsw/translations/zh-Hans.json new file mode 100644 index 00000000000..84c6b9f4026 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/zh-Hans.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "\u8bbe\u5907\u5df2\u88ab\u914d\u7f6e", + "invalid_id": "\u8bbe\u5907\u8fd4\u56de\u4e86\u65e0\u6548\u7684\u552f\u4e00 ID" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_auth": "\u51ed\u8bc1\u65e0\u6548" + }, + "step": { + "user": { + "data": { + "password": "\u5bc6\u7801", + "url": "\u4e3b\u673a\u5730\u5740", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/sv.json b/homeassistant/components/recorder/translations/sv.json new file mode 100644 index 00000000000..bf4d6ccf0a5 --- /dev/null +++ b/homeassistant/components/recorder/translations/sv.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Aktuell starttid", + "oldest_recorder_run": "\u00c4ldsta starttid" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/tr.json b/homeassistant/components/recorder/translations/tr.json new file mode 100644 index 00000000000..80fd4fd45be --- /dev/null +++ b/homeassistant/components/recorder/translations/tr.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Mevcut \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131", + "oldest_recorder_run": "En Eski \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/sv.json b/homeassistant/components/sabnzbd/translations/sv.json new file mode 100644 index 00000000000..61847ff0eb4 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/sv.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "Det gick inte att ansluta", + "invalid_api_key": "Ogiltig API-nyckel" + }, + "step": { + "user": { + "data": { + "api_key": "API Nyckel", + "name": "Namn", + "path": "S\u00f6kv\u00e4g" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/tr.json b/homeassistant/components/sabnzbd/translations/tr.json new file mode 100644 index 00000000000..f238072b6da --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/tr.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131" + }, + "step": { + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "name": "Ad", + "path": "Yol", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/sv.json b/homeassistant/components/simplisafe/translations/sv.json index a4e8e052073..760d092e113 100644 --- a/homeassistant/components/simplisafe/translations/sv.json +++ b/homeassistant/components/simplisafe/translations/sv.json @@ -4,9 +4,19 @@ "already_configured": "Det h\u00e4r SimpliSafe-kontot har redan konfigurerats." }, "error": { + "2fa_timed_out": "Tidsgr\u00e4nsen tog slut i v\u00e4ntan p\u00e5 tv\u00e5faktorsautentisering", "identifier_exists": "Kontot \u00e4r redan registrerat" }, + "progress": { + "email_2fa": "Kontrollera din e-post f\u00f6r en verifieringsl\u00e4nk fr\u00e5n Simplisafe." + }, "step": { + "sms_2fa": { + "data": { + "code": "Kod" + }, + "description": "Ange tv\u00e5faktorsautentiseringskoden som skickats till dig via SMS." + }, "user": { "data": { "password": "L\u00f6senord", diff --git a/homeassistant/components/simplisafe/translations/tr.json b/homeassistant/components/simplisafe/translations/tr.json index 255207833c7..156b3575756 100644 --- a/homeassistant/components/simplisafe/translations/tr.json +++ b/homeassistant/components/simplisafe/translations/tr.json @@ -2,15 +2,20 @@ "config": { "abort": { "already_configured": "Bu SimpliSafe hesab\u0131 zaten kullan\u0131mda.", + "email_2fa_timed_out": "E-posta tabanl\u0131 iki fakt\u00f6rl\u00fc kimlik do\u011frulama i\u00e7in beklerken zaman a\u015f\u0131m\u0131na u\u011frad\u0131.", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", "wrong_account": "Sa\u011flanan kullan\u0131c\u0131 kimlik bilgileri bu SimpliSafe hesab\u0131yla e\u015fle\u015fmiyor." }, "error": { + "2fa_timed_out": "\u0130ki fakt\u00f6rl\u00fc kimlik do\u011frulama i\u00e7in beklerken zaman a\u015f\u0131m\u0131na u\u011frad\u0131", "identifier_exists": "Hesap zaten kay\u0131tl\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "still_awaiting_mfa": "Hala MFA e-posta t\u0131klamas\u0131 bekleniyor", "unknown": "Beklenmeyen hata" }, + "progress": { + "email_2fa": "Simplisafe'den bir do\u011frulama ba\u011flant\u0131s\u0131 i\u00e7in e-postan\u0131z\u0131 kontrol edin." + }, "step": { "mfa": { "title": "SimpliSafe \u00c7ok Fakt\u00f6rl\u00fc Kimlik Do\u011frulama" @@ -19,17 +24,23 @@ "data": { "password": "Parola" }, - "description": "Eri\u015fim kodunuzun s\u00fcresi doldu veya iptal edildi. Hesab\u0131n\u0131z\u0131 yeniden ba\u011flamak i\u00e7in parolan\u0131z\u0131 girin.", + "description": "L\u00fctfen {username} \u015fifresini tekrar girin.", "title": "Entegrasyonu Yeniden Do\u011frula" }, + "sms_2fa": { + "data": { + "code": "Kod" + }, + "description": "Size SMS ile g\u00f6nderilen iki fakt\u00f6rl\u00fc do\u011frulama kodunu girin." + }, "user": { "data": { "auth_code": "Yetkilendirme Kodu", "code": "Kod (Home Assistant kullan\u0131c\u0131 aray\u00fcz\u00fcnde kullan\u0131l\u0131r)", "password": "Parola", - "username": "E-posta" + "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "SimpliSafe, SimpliSafe web uygulamas\u0131 arac\u0131l\u0131\u011f\u0131yla Home Assistant ile kimlik do\u011frulamas\u0131 yapar. Teknik s\u0131n\u0131rlamalar nedeniyle bu i\u015flemin sonunda manuel bir ad\u0131m vard\u0131r; l\u00fctfen ba\u015flamadan \u00f6nce [belgeleri]( {docs_url} \n\n 1. SimpliSafe web uygulamas\u0131n\u0131 a\u00e7mak ve kimlik bilgilerinizi girmek i\u00e7in [buraya]( {url} \n\n 2. Oturum a\u00e7ma i\u015flemi tamamland\u0131\u011f\u0131nda buraya d\u00f6n\u00fcn ve yetkilendirme kodunu a\u015fa\u011f\u0131ya girin.", + "description": "Kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 ve \u015fifrenizi girin.", "title": "Bilgilerinizi doldurun." } } diff --git a/homeassistant/components/slimproto/translations/sv.json b/homeassistant/components/slimproto/translations/sv.json new file mode 100644 index 00000000000..8ca6afbf92f --- /dev/null +++ b/homeassistant/components/slimproto/translations/sv.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig." + }, + "step": { + "user": { + "one": "Tom", + "other": "Tom" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/tr.json b/homeassistant/components/slimproto/translations/tr.json new file mode 100644 index 00000000000..a152eb19468 --- /dev/null +++ b/homeassistant/components/slimproto/translations/tr.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/sv.json b/homeassistant/components/sql/translations/sv.json new file mode 100644 index 00000000000..25bd828223f --- /dev/null +++ b/homeassistant/components/sql/translations/sv.json @@ -0,0 +1,23 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Namn" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Namn" + }, + "data_description": { + "name": "Namn som kommer att anv\u00e4ndas f\u00f6r Config Entry och \u00e4ven sensorn" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/tr.json b/homeassistant/components/sql/translations/tr.json index edbce58d65a..5757f50cdfb 100644 --- a/homeassistant/components/sql/translations/tr.json +++ b/homeassistant/components/sql/translations/tr.json @@ -13,6 +13,7 @@ "data": { "column": "S\u00fctun", "db_url": "Veritaban\u0131 URL'si", + "name": "Ad", "query": "Sorgu Se\u00e7", "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", "value_template": "De\u011fer \u015eablonu" @@ -20,6 +21,7 @@ "data_description": { "column": "D\u00f6nd\u00fcr\u00fclen sorgunun durum olarak sunulmas\u0131 i\u00e7in s\u00fctun", "db_url": "Veritaban\u0131 URL'si, varsay\u0131lan HA veritaban\u0131n\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n", + "name": "Yap\u0131land\u0131rma Giri\u015fi ve ayr\u0131ca Sens\u00f6r i\u00e7in kullan\u0131lacak ad", "query": "\u00c7al\u0131\u015ft\u0131r\u0131lacak sorgu, 'SE\u00c7' ile ba\u015flamal\u0131d\u0131r", "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi (iste\u011fe ba\u011fl\u0131)", "value_template": "De\u011fer \u015eablonu (iste\u011fe ba\u011fl\u0131)" @@ -38,6 +40,7 @@ "data": { "column": "S\u00fctun", "db_url": "Veritaban\u0131 URL'si", + "name": "Ad", "query": "Sorgu Se\u00e7", "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi", "value_template": "De\u011fer \u015eablonu" @@ -45,6 +48,7 @@ "data_description": { "column": "D\u00f6nd\u00fcr\u00fclen sorgunun durum olarak sunulmas\u0131 i\u00e7in s\u00fctun", "db_url": "Veritaban\u0131 URL'si, varsay\u0131lan HA veritaban\u0131n\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n", + "name": "Yap\u0131land\u0131rma Giri\u015fi ve ayr\u0131ca Sens\u00f6r i\u00e7in kullan\u0131lacak ad", "query": "\u00c7al\u0131\u015ft\u0131r\u0131lacak sorgu, 'SE\u00c7' ile ba\u015flamal\u0131d\u0131r", "unit_of_measurement": "\u00d6l\u00e7\u00fc Birimi (iste\u011fe ba\u011fl\u0131)", "value_template": "De\u011fer \u015eablonu (iste\u011fe ba\u011fl\u0131)" diff --git a/homeassistant/components/steam_online/translations/sv.json b/homeassistant/components/steam_online/translations/sv.json new file mode 100644 index 00000000000..51c39c12b35 --- /dev/null +++ b/homeassistant/components/steam_online/translations/sv.json @@ -0,0 +1,24 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "description": "Steam-integrationen m\u00e5ste autentiseras p\u00e5 nytt manuellt\n\nDu hittar din nyckel h\u00e4r: {api_key_url}" + }, + "user": { + "data": { + "api_key": "API Nyckel" + }, + "description": "Anv\u00e4nd {account_id_url} f\u00f6r att hitta ditt Steam-konto-ID" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Namn p\u00e5 konton som ska \u00f6vervakas" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/tr.json b/homeassistant/components/steam_online/translations/tr.json new file mode 100644 index 00000000000..c378c90855e --- /dev/null +++ b/homeassistant/components/steam_online/translations/tr.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_account": "Ge\u00e7ersiz hesap kimli\u011fi", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "reauth_confirm": { + "description": "Steam entegrasyonunun manuel olarak yeniden do\u011frulanmas\u0131 gerekiyor \n\n Anahtar\u0131n\u0131z\u0131 burada bulabilirsiniz: {api_key_url}", + "title": "Entegrasyonu Yeniden Do\u011frula" + }, + "user": { + "data": { + "account": "Steam hesap kimli\u011fi", + "api_key": "API Anahtar\u0131" + }, + "description": "Steam hesap kimli\u011finizi bulmak i\u00e7in {account_id_url} kullan\u0131n" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\u0130zlenecek hesaplar\u0131n adlar\u0131" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/zh-Hans.json b/homeassistant/components/steam_online/translations/zh-Hans.json new file mode 100644 index 00000000000..bf7b93740a0 --- /dev/null +++ b/homeassistant/components/steam_online/translations/zh-Hans.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52a1\u5df2\u88ab\u914d\u7f6e", + "reauth_successful": "\u91cd\u9a8c\u8bc1\u6210\u529f" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_account": "\u5e10\u6237 ID \u65e0\u6548", + "invalid_auth": "\u51ed\u8bc1\u65e0\u6548", + "unknown": "\u672a\u77e5\u9519\u8bef" + }, + "step": { + "reauth_confirm": { + "description": "Steam \u96c6\u6210\u9700\u8981\u624b\u52a8\u91cd\u65b0\u8ba4\u8bc1\uff1a\n\n\u60a8\u53ef\u4ee5\u5728\u6b64\u5904\u627e\u5230\u60a8\u7684\u5bc6\u94a5\uff1a{api_key_url}", + "title": "\u91cd\u9a8c\u8bc1\u96c6\u6210" + }, + "user": { + "data": { + "account": "Steam \u5e10\u6237 ID", + "api_key": "API \u5bc6\u94a5" + }, + "description": "\u4f7f\u7528 {account_id_url} \u4ee5\u67e5\u627e\u60a8\u7684 Steam \u5e10\u6237 ID" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/sv.json b/homeassistant/components/tautulli/translations/sv.json new file mode 100644 index 00000000000..3354c6053dc --- /dev/null +++ b/homeassistant/components/tautulli/translations/sv.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "description": "F\u00f6r att hitta din API-nyckel, \u00f6ppna Tautullis webbsida och navigera till Inst\u00e4llningar och sedan till webbgr\u00e4nssnitt. API-nyckeln finns l\u00e4ngst ner p\u00e5 sidan." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/tr.json b/homeassistant/components/tautulli/translations/tr.json new file mode 100644 index 00000000000..b52d2a7abad --- /dev/null +++ b/homeassistant/components/tautulli/translations/tr.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API Anahtar\u0131" + }, + "description": "API anahtar\u0131n\u0131z\u0131 bulmak i\u00e7in Tautulli web sayfas\u0131n\u0131 a\u00e7\u0131n ve Ayarlar'a ve ard\u0131ndan Web aray\u00fcz\u00fcne gidin. API anahtar\u0131 o sayfan\u0131n alt\u0131nda olacakt\u0131r.", + "title": "Tautulli'yi yeniden do\u011frulay\u0131n" + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "url": "URL", + "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" + }, + "description": "API anahtar\u0131n\u0131z\u0131 bulmak i\u00e7in Tautulli web sayfas\u0131n\u0131 a\u00e7\u0131n ve Ayarlar'a ve ard\u0131ndan Web aray\u00fcz\u00fcne gidin. API anahtar\u0131 o sayfan\u0131n alt\u0131nda olacakt\u0131r. \n\n URL \u00f6rne\u011fi: ```http://192.168.0.10:8181``` 8181 varsay\u0131lan ba\u011flant\u0131 noktas\u0131d\u0131r." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/tr.json b/homeassistant/components/trafikverket_ferry/translations/tr.json new file mode 100644 index 00000000000..341de06c8a5 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/tr.json @@ -0,0 +1,30 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "incorrect_api_key": "Se\u00e7ilen hesap i\u00e7in ge\u00e7ersiz API anahtar\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "invalid_route": "Sa\u011flanan bilgilerle rota bulunamad\u0131" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API Anahtar\u0131" + } + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "from": "\u0130stasyondan", + "time": "Zaman", + "to": "\u0130stasyona", + "weekday": "Hafta i\u00e7i" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/sv.json b/homeassistant/components/unifi/translations/sv.json index 2e4851e70ed..a84558a6b58 100644 --- a/homeassistant/components/unifi/translations/sv.json +++ b/homeassistant/components/unifi/translations/sv.json @@ -23,6 +23,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi-integration \u00e4r inte konfigurerad" + }, "step": { "client_control": { "title": "UniFi-inst\u00e4llningar 2/3" diff --git a/homeassistant/components/unifi/translations/tr.json b/homeassistant/components/unifi/translations/tr.json index 9b53e712fa5..9e9ff5034d8 100644 --- a/homeassistant/components/unifi/translations/tr.json +++ b/homeassistant/components/unifi/translations/tr.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "UniFi Network sitesi zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "configuration_updated": "Yap\u0131land\u0131rma g\u00fcncellendi.", + "configuration_updated": "Yap\u0131land\u0131rma g\u00fcncellendi", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi entegrasyonu kurulmad\u0131" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/utility_meter/translations/sv.json b/homeassistant/components/utility_meter/translations/sv.json new file mode 100644 index 00000000000..38d433e7b42 --- /dev/null +++ b/homeassistant/components/utility_meter/translations/sv.json @@ -0,0 +1,3 @@ +{ + "title": "Tj\u00e4nsten \u00e4r redan konfigurerad" +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ca.json b/homeassistant/components/vulcan/translations/ca.json index 505bcb1d326..a9bfe95310b 100644 --- a/homeassistant/components/vulcan/translations/ca.json +++ b/homeassistant/components/vulcan/translations/ca.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Ja s'han afegit tots els alumnes.", "already_configured": "Ja s'ha afegit aquest alumne.", + "no_matching_entries": "No s'han trobat entrades coincidents, utilitza un compte diferent o elimina la integraci\u00f3 amb l'estudiant obsolet.", "reauth_successful": "Re-autenticaci\u00f3 exitosa" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "S\u00edmbol", + "token": "Token" + }, + "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." + }, "select_saved_credentials": { "data": { "credentials": "Inici de sessi\u00f3" diff --git a/homeassistant/components/vulcan/translations/el.json b/homeassistant/components/vulcan/translations/el.json index bb77c88f8e5..abd3a264463 100644 --- a/homeassistant/components/vulcan/translations/el.json +++ b/homeassistant/components/vulcan/translations/el.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "\u038c\u03bb\u03bf\u03b9 \u03bf\u03b9 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ad\u03c2 \u03ad\u03c7\u03bf\u03c5\u03bd \u03ae\u03b4\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b5\u03b8\u03b5\u03af.", "already_configured": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c0\u03c1\u03bf\u03c3\u03c4\u03b5\u03b8\u03b5\u03af.", + "no_matching_entries": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c0\u03bf\u03c5 \u03bd\u03b1 \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03bf\u03c5\u03bd, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03ae \u03ba\u03b1\u03c4\u03b1\u03c1\u03b3\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03bc\u03b5 \u03c0\u03b1\u03bb\u03b9\u03cc \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae..", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { @@ -37,6 +38,14 @@ }, "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." }, + "reauth_confirm": { + "data": { + "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", + "region": "\u03a3\u03cd\u03bc\u03b2\u03bf\u03bb\u03bf", + "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc" + }, + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." + }, "select_saved_credentials": { "data": { "credentials": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7" diff --git a/homeassistant/components/vulcan/translations/en.json b/homeassistant/components/vulcan/translations/en.json index 48c054d2c15..8ea9fc59964 100644 --- a/homeassistant/components/vulcan/translations/en.json +++ b/homeassistant/components/vulcan/translations/en.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "All students have already been added.", "already_configured": "That student has already been added.", + "no_matching_entries": "No matching entries found, please use different account or remove integration with outdated student..", "reauth_successful": "Reauth successful" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Login to your Vulcan Account using mobile app registration page." }, + "reauth_confirm": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Login to your Vulcan Account using mobile app registration page." + }, "select_saved_credentials": { "data": { "credentials": "Login" diff --git a/homeassistant/components/vulcan/translations/fr.json b/homeassistant/components/vulcan/translations/fr.json index 812c069223e..9a850d85796 100644 --- a/homeassistant/components/vulcan/translations/fr.json +++ b/homeassistant/components/vulcan/translations/fr.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Tous les \u00e9tudiants ont d\u00e9j\u00e0 \u00e9t\u00e9 ajout\u00e9s.", "already_configured": "Cet \u00e9tudiant a d\u00e9j\u00e0 \u00e9t\u00e9 ajout\u00e9.", + "no_matching_entries": "Aucune entr\u00e9e correspondante n'a \u00e9t\u00e9 trouv\u00e9e, veuillez utiliser un autre compte ou retirer l'int\u00e9gration incluant un \u00e9tudiant obsol\u00e8te.", "reauth_successful": "R\u00e9-authentification r\u00e9ussie" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "Symbole", + "token": "Jeton" + }, + "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." + }, "select_saved_credentials": { "data": { "credentials": "Connexion" diff --git a/homeassistant/components/vulcan/translations/hu.json b/homeassistant/components/vulcan/translations/hu.json index 9e138254ebe..90566b69fe8 100644 --- a/homeassistant/components/vulcan/translations/hu.json +++ b/homeassistant/components/vulcan/translations/hu.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "M\u00e1r minden tanul\u00f3 hozz\u00e1adva.", "already_configured": "Ezt a tanul\u00f3t m\u00e1r hozz\u00e1adt\u00e1k.", + "no_matching_entries": "Nem tal\u00e1ltunk megfelel\u0151 bejegyz\u00e9st, k\u00e9rj\u00fck, haszn\u00e1ljon m\u00e1sik fi\u00f3kot, vagy t\u00e1vol\u00edtsa el az elavult di\u00e1kkal val\u00f3 integr\u00e1ci\u00f3t...", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "Szimb\u00f3lum", + "token": "Token" + }, + "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." + }, "select_saved_credentials": { "data": { "credentials": "Bejelentkez\u00e9s" diff --git a/homeassistant/components/vulcan/translations/pt-BR.json b/homeassistant/components/vulcan/translations/pt-BR.json index ecef34ff96b..98aaec3c33f 100644 --- a/homeassistant/components/vulcan/translations/pt-BR.json +++ b/homeassistant/components/vulcan/translations/pt-BR.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Todos os alunos j\u00e1 foram adicionados.", "already_configured": "Esse aluno j\u00e1 foi adicionado.", + "no_matching_entries": "Nenhuma entrada correspondente encontrada, use uma conta diferente ou remova a integra\u00e7\u00e3o com o aluno desatualizado.", "reauth_successful": "Autentica\u00e7\u00e3o bem-sucedida" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo m\u00f3vel." }, + "reauth_confirm": { + "data": { + "pin": "C\u00f3digo PIN", + "region": "S\u00edmbolo", + "token": "Token" + }, + "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo." + }, "select_saved_credentials": { "data": { "credentials": "Login" diff --git a/homeassistant/components/vulcan/translations/tr.json b/homeassistant/components/vulcan/translations/tr.json index 9f4324dfbcc..e103d8626ab 100644 --- a/homeassistant/components/vulcan/translations/tr.json +++ b/homeassistant/components/vulcan/translations/tr.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "T\u00fcm \u00f6\u011frenciler zaten eklendi.", "already_configured": "Bu \u00f6\u011frenci zaten eklendi.", + "no_matching_entries": "E\u015fle\u015fen giri\u015f bulunamad\u0131, l\u00fctfen farkl\u0131 bir hesap kullan\u0131n veya eski \u00f6\u011frenci ile entegrasyonu kald\u0131r\u0131n..", "reauth_successful": "Yeniden yetkilendirme ba\u015far\u0131l\u0131" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." }, + "reauth_confirm": { + "data": { + "pin": "Pin", + "region": "Sembol", + "token": "Anahtar" + }, + "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." + }, "select_saved_credentials": { "data": { "credentials": "Oturum a\u00e7" From d612b9e0b4d1013e49d8489a3857e110f174cb14 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 5 May 2022 23:09:10 -0400 Subject: [PATCH 241/930] Reduce event loop overhead for listeners that already queue (#71364) Co-authored-by: Paulus Schoutsen --- homeassistant/components/recorder/core.py | 7 ++++-- .../components/websocket_api/auth.py | 2 +- .../components/websocket_api/commands.py | 16 +++++++++----- .../components/websocket_api/connection.py | 2 +- .../components/websocket_api/http.py | 10 ++++++--- homeassistant/core.py | 22 +++++++++++++++---- tests/test_core.py | 18 +++++++++++++++ 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index af368b909b7..49be0aac705 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -237,7 +237,9 @@ class Recorder(threading.Thread): def async_initialize(self) -> None: """Initialize the recorder.""" self._event_listener = self.hass.bus.async_listen( - MATCH_ALL, self.event_listener, event_filter=self._async_event_filter + MATCH_ALL, + self.event_listener, + run_immediately=True, ) self._queue_watcher = async_track_time_interval( self.hass, self._async_check_queue, timedelta(minutes=10) @@ -916,7 +918,8 @@ class Recorder(threading.Thread): @callback def event_listener(self, event: Event) -> None: """Listen for new events and put them in the process queue.""" - self.queue_task(EventTask(event)) + if self._async_event_filter(event): + self.queue_task(EventTask(event)) def block_till_done(self) -> None: """Block till all events processed. diff --git a/homeassistant/components/websocket_api/auth.py b/homeassistant/components/websocket_api/auth.py index 794dae77153..9c074588a17 100644 --- a/homeassistant/components/websocket_api/auth.py +++ b/homeassistant/components/websocket_api/auth.py @@ -56,7 +56,7 @@ class AuthPhase: self, logger: WebSocketAdapter, hass: HomeAssistant, - send_message: Callable[[str | dict[str, Any]], None], + send_message: Callable[[str | dict[str, Any] | Callable[[], str]], None], cancel_ws: CALLBACK_TYPE, request: Request, ) -> None: diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index 414913be002..edec628fd2c 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -105,17 +105,21 @@ def handle_subscribe_events( ): return - connection.send_message(messages.cached_event_message(msg["id"], event)) + connection.send_message( + lambda: messages.cached_event_message(msg["id"], event) + ) else: @callback def forward_events(event: Event) -> None: """Forward events to websocket.""" - connection.send_message(messages.cached_event_message(msg["id"], event)) + connection.send_message( + lambda: messages.cached_event_message(msg["id"], event) + ) connection.subscriptions[msg["id"]] = hass.bus.async_listen( - event_type, forward_events + event_type, forward_events, run_immediately=True ) connection.send_result(msg["id"]) @@ -286,14 +290,16 @@ def handle_subscribe_entities( if entity_ids and event.data["entity_id"] not in entity_ids: return - connection.send_message(messages.cached_state_diff_message(msg["id"], event)) + connection.send_message( + lambda: messages.cached_state_diff_message(msg["id"], event) + ) # We must never await between sending the states and listening for # state changed events or we will introduce a race condition # where some states are missed states = _async_get_allowed_states(hass, connection) connection.subscriptions[msg["id"]] = hass.bus.async_listen( - "state_changed", forward_entity_changes + EVENT_STATE_CHANGED, forward_entity_changes, run_immediately=True ) connection.send_result(msg["id"]) data: dict[str, dict[str, dict]] = { diff --git a/homeassistant/components/websocket_api/connection.py b/homeassistant/components/websocket_api/connection.py index 9b53f358b85..cdcee408070 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -30,7 +30,7 @@ class ActiveConnection: self, logger: WebSocketAdapter, hass: HomeAssistant, - send_message: Callable[[str | dict[str, Any]], None], + send_message: Callable[[str | dict[str, Any] | Callable[[], str]], None], user: User, refresh_token: RefreshToken, ) -> None: diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index 44b2aa8579c..a913b81c384 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -72,9 +72,13 @@ class WebSocketHandler: # Exceptions if Socket disconnected or cancelled by connection handler with suppress(RuntimeError, ConnectionResetError, *CANCELLATION_ERRORS): while not self.wsock.closed: - if (message := await self._to_write.get()) is None: + if (process := await self._to_write.get()) is None: break + if not isinstance(process, str): + message: str = process() + else: + message = process self._logger.debug("Sending %s", message) await self.wsock.send_str(message) @@ -84,14 +88,14 @@ class WebSocketHandler: self._peak_checker_unsub = None @callback - def _send_message(self, message: str | dict[str, Any]) -> None: + def _send_message(self, message: str | dict[str, Any] | Callable[[], str]) -> None: """Send a message to the client. Closes connection if the client is not reading the messages. Async friendly. """ - if not isinstance(message, str): + if isinstance(message, dict): message = message_to_json(message) try: diff --git a/homeassistant/core.py b/homeassistant/core.py index b181e4c4106..916dd0c6f72 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -778,6 +778,7 @@ class _FilterableJob(NamedTuple): job: HassJob[None | Awaitable[None]] event_filter: Callable[[Event], bool] | None + run_immediately: bool class EventBus: @@ -845,7 +846,7 @@ class EventBus: if not listeners: return - for job, event_filter in listeners: + for job, event_filter, run_immediately in listeners: if event_filter is not None: try: if not event_filter(event): @@ -853,7 +854,13 @@ class EventBus: except Exception: # pylint: disable=broad-except _LOGGER.exception("Error in event filter") continue - self._hass.async_add_hass_job(job, event) + if run_immediately: + try: + job.target(event) + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Error running job: %s", job) + else: + self._hass.async_add_hass_job(job, event) def listen( self, @@ -881,6 +888,7 @@ class EventBus: event_type: str, listener: Callable[[Event], None | Awaitable[None]], event_filter: Callable[[Event], bool] | None = None, + run_immediately: bool = False, ) -> CALLBACK_TYPE: """Listen for all events or events of a specific type. @@ -891,12 +899,18 @@ class EventBus: @callback that returns a boolean value, determines if the listener callable should run. + If run_immediately is passed, the callback will be run + right away instead of using call_soon. Only use this if + the callback results in scheduling another task. + This method must be run in the event loop. """ if event_filter is not None and not is_callback(event_filter): raise HomeAssistantError(f"Event filter {event_filter} is not a callback") + if run_immediately and not is_callback(listener): + raise HomeAssistantError(f"Event listener {listener} is not a callback") return self._async_listen_filterable_job( - event_type, _FilterableJob(HassJob(listener), event_filter) + event_type, _FilterableJob(HassJob(listener), event_filter, run_immediately) ) @callback @@ -966,7 +980,7 @@ class EventBus: _onetime_listener, listener, ("__name__", "__qualname__", "__module__"), [] ) - filterable_job = _FilterableJob(HassJob(_onetime_listener), None) + filterable_job = _FilterableJob(HassJob(_onetime_listener), None, False) return self._async_listen_filterable_job(event_type, filterable_job) diff --git a/tests/test_core.py b/tests/test_core.py index 6885063a79a..c870605fc01 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -442,6 +442,24 @@ async def test_eventbus_filtered_listener(hass): unsub() +async def test_eventbus_run_immediately(hass): + """Test we can call events immediately.""" + calls = [] + + @ha.callback + def listener(event): + """Mock listener.""" + calls.append(event) + + unsub = hass.bus.async_listen("test", listener, run_immediately=True) + + hass.bus.async_fire("test", {"event": True}) + # No async_block_till_done here + assert len(calls) == 1 + + unsub() + + async def test_eventbus_unsubscribe_listener(hass): """Test unsubscribe listener from returned function.""" calls = [] From 5931f6598a98af915c7c4435709a76de4b7093f5 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 6 May 2022 09:05:15 +0200 Subject: [PATCH 242/930] Add tests for Sensibo (#71148) * Initial commit * Check temperature missing * fix temp is none * Fix parallell * Commit to save * Fix tests * Fix test_init * assert 25 * Adjustments tests * Small removal * Cleanup * no hass.data * Adjustment test_coordinator * Minor change test_coordinator --- .coveragerc | 6 - tests/components/sensibo/__init__.py | 5 + tests/components/sensibo/conftest.py | 45 ++ tests/components/sensibo/response.py | 400 ++++++++++++ .../components/sensibo/test_binary_sensor.py | 52 ++ tests/components/sensibo/test_climate.py | 601 ++++++++++++++++++ tests/components/sensibo/test_coordinator.py | 91 +++ tests/components/sensibo/test_diagnostics.py | 48 ++ tests/components/sensibo/test_entity.py | 132 ++++ tests/components/sensibo/test_init.py | 132 ++++ 10 files changed, 1506 insertions(+), 6 deletions(-) create mode 100644 tests/components/sensibo/conftest.py create mode 100644 tests/components/sensibo/response.py create mode 100644 tests/components/sensibo/test_binary_sensor.py create mode 100644 tests/components/sensibo/test_climate.py create mode 100644 tests/components/sensibo/test_coordinator.py create mode 100644 tests/components/sensibo/test_diagnostics.py create mode 100644 tests/components/sensibo/test_entity.py create mode 100644 tests/components/sensibo/test_init.py diff --git a/.coveragerc b/.coveragerc index 3464b0df1be..353d2f07d06 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1017,12 +1017,6 @@ omit = homeassistant/components/senseme/fan.py homeassistant/components/senseme/light.py homeassistant/components/senseme/switch.py - homeassistant/components/sensibo/__init__.py - homeassistant/components/sensibo/binary_sensor.py - homeassistant/components/sensibo/climate.py - homeassistant/components/sensibo/coordinator.py - homeassistant/components/sensibo/diagnostics.py - homeassistant/components/sensibo/entity.py homeassistant/components/sensibo/number.py homeassistant/components/sensibo/select.py homeassistant/components/sensibo/sensor.py diff --git a/tests/components/sensibo/__init__.py b/tests/components/sensibo/__init__.py index 8dd2ed661bc..da585f8d1e8 100644 --- a/tests/components/sensibo/__init__.py +++ b/tests/components/sensibo/__init__.py @@ -1 +1,6 @@ """Tests for the Sensibo integration.""" +from __future__ import annotations + +from homeassistant.const import CONF_API_KEY + +ENTRY_CONFIG = {CONF_API_KEY: "1234567890"} diff --git a/tests/components/sensibo/conftest.py b/tests/components/sensibo/conftest.py new file mode 100644 index 00000000000..9380ee6ab6b --- /dev/null +++ b/tests/components/sensibo/conftest.py @@ -0,0 +1,45 @@ +"""Fixtures for the Sensibo integration.""" +from __future__ import annotations + +from unittest.mock import patch + +import pytest + +from homeassistant.components.sensibo.const import DOMAIN +from homeassistant.config_entries import SOURCE_USER +from homeassistant.core import HomeAssistant + +from . import ENTRY_CONFIG +from .response import DATA_FROM_API + +from tests.common import MockConfigEntry + + +@pytest.fixture +async def load_int(hass: HomeAssistant) -> MockConfigEntry: + """Set up the Sensibo integration in Home Assistant.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="username", + version=2, + ) + + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {"username": "username"}}, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry diff --git a/tests/components/sensibo/response.py b/tests/components/sensibo/response.py new file mode 100644 index 00000000000..e6b39b81881 --- /dev/null +++ b/tests/components/sensibo/response.py @@ -0,0 +1,400 @@ +"""Test api response for the Sensibo integration.""" +from __future__ import annotations + +from pysensibo.model import MotionSensor, SensiboData, SensiboDevice + +DATA_FROM_API = SensiboData( + raw={ + "status": "success", + "result": [ + { + "id": "ABC999111", + "qrId": "AAAAAAAAAA", + "room": {"uid": "99TT99TT", "name": "Hallway", "icon": "Lounge"}, + "acState": { + "timestamp": { + "time": "2022-04-30T19:58:15.544787Z", + "secondsAgo": 0, + }, + "on": False, + "mode": "fan", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + "location": { + "id": "ZZZZZZZZZZZZ", + "name": "Home", + "latLon": [58.9806976, 20.5864297], + "address": ["Sealand 99", "Some county"], + "country": "United Country", + "createTime": { + "time": "2020-03-21T15:44:15Z", + "secondsAgo": 66543240, + }, + "updateTime": None, + "features": [], + "geofenceTriggerRadius": 200, + "subscription": None, + "technician": None, + "shareAnalytics": False, + "occupancy": "n/a", + }, + "accessPoint": {"ssid": "SENSIBO-I-99999", "password": None}, + "macAddress": "00:02:00:B6:00:00", + "autoOffMinutes": None, + "autoOffEnabled": False, + "antiMoldTimer": None, + "antiMoldConfig": None, + } + ], + }, + parsed={ + "ABC999111": SensiboDevice( + id="ABC999111", + mac="00:02:00:B6:00:00", + name="Hallway", + ac_states={ + "timestamp": {"time": "2022-04-30T19:58:15.544787Z", "secondsAgo": 0}, + "on": False, + "mode": "heat", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + temp=22.4, + humidity=38, + target_temp=25, + hvac_mode="heat", + device_on=True, + fan_mode="high", + swing_mode="stopped", + horizontal_swing_mode="stopped", + light_mode="on", + available=True, + hvac_modes=["cool", "heat", "dry", "auto", "fan", "off"], + fan_modes=["quiet", "low", "medium"], + swing_modes=[ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + horizontal_swing_modes=[ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + light_modes=["on", "off"], + temp_unit="C", + temp_list=[18, 19, 20], + temp_step=1, + active_features=[ + "timestamp", + "on", + "mode", + "fanLevel", + "swing", + "targetTemperature", + "horizontalSwing", + "light", + ], + full_features={ + "targetTemperature", + "fanLevel", + "swing", + "horizontalSwing", + "light", + }, + state="heat", + fw_ver="SKY30046", + fw_ver_available="SKY30046", + fw_type="esp8266ex", + model="skyv2", + calibration_temp=0.1, + calibration_hum=0.1, + full_capabilities={ + "modes": { + "cool": { + "temperatures": { + "F": { + "isNative": False, + "values": [ + 64, + 66, + 68, + ], + }, + "C": { + "isNative": True, + "values": [ + 18, + 19, + 20, + ], + }, + }, + "fanLevels": [ + "quiet", + "low", + "medium", + ], + "swing": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "horizontalSwing": [ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + "light": ["on", "off"], + }, + "heat": { + "temperatures": { + "F": { + "isNative": False, + "values": [ + 63, + 64, + 66, + ], + }, + "C": { + "isNative": True, + "values": [ + 17, + 18, + 19, + ], + }, + }, + "fanLevels": ["quiet", "low", "medium"], + "swing": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "horizontalSwing": [ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + "light": ["on", "off"], + }, + "dry": { + "temperatures": { + "F": { + "isNative": False, + "values": [ + 64, + 66, + 68, + ], + }, + "C": { + "isNative": True, + "values": [ + 18, + 19, + 20, + ], + }, + }, + "swing": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "horizontalSwing": [ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + "light": ["on", "off"], + }, + "auto": { + "temperatures": { + "F": { + "isNative": False, + "values": [ + 64, + 66, + 68, + ], + }, + "C": { + "isNative": True, + "values": [ + 18, + 19, + 20, + ], + }, + }, + "fanLevels": [ + "quiet", + "low", + "medium", + ], + "swing": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "horizontalSwing": [ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + "light": ["on", "off"], + }, + "fan": { + "temperatures": {}, + "fanLevels": [ + "quiet", + "low", + ], + "swing": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "horizontalSwing": [ + "stopped", + "fixedLeft", + "fixedCenterLeft", + ], + "light": ["on", "off"], + }, + } + }, + motion_sensors={ + "AABBCC": MotionSensor( + id="AABBCC", + alive=True, + motion=True, + fw_ver="V17", + fw_type="nrf52", + is_main_sensor=True, + battery_voltage=3000, + humidity=57, + temperature=23.9, + model="motion_sensor", + rssi=-72, + ) + }, + pm25=None, + room_occupied=True, + update_available=False, + schedules={}, + pure_boost_enabled=None, + pure_sensitivity=None, + pure_ac_integration=None, + pure_geo_integration=None, + pure_measure_integration=None, + timer_on=False, + timer_id=None, + timer_state_on=None, + timer_time=None, + smart_on=False, + smart_type="temperature", + smart_low_temp_threshold=0.0, + smart_high_temp_threshold=27.5, + smart_low_state={ + "on": True, + "targetTemperature": 21, + "temperatureUnit": "C", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + smart_high_state={ + "on": True, + "targetTemperature": 21, + "temperatureUnit": "C", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + filter_clean=False, + filter_last_reset="2022-03-12T15:24:26Z", + ), + "AAZZAAZZ": SensiboDevice( + id="AAZZAAZZ", + mac="00:01:00:01:00:01", + name="Kitchen", + ac_states={ + "timestamp": {"time": "2022-04-30T19:58:15.568753Z", "secondsAgo": 0}, + "on": False, + "mode": "fan", + "fanLevel": "low", + "light": "on", + }, + temp=None, + humidity=None, + target_temp=None, + hvac_mode="off", + device_on=False, + fan_mode="low", + swing_mode=None, + horizontal_swing_mode=None, + light_mode="on", + available=True, + hvac_modes=["fan", "off"], + fan_modes=["low", "high"], + swing_modes=None, + horizontal_swing_modes=None, + light_modes=["on", "dim", "off"], + temp_unit="C", + temp_list=[0, 1], + temp_step=1, + active_features=["timestamp", "on", "mode", "fanLevel", "light"], + full_features={"light", "targetTemperature", "fanLevel"}, + state="off", + fw_ver="PUR00111", + fw_ver_available="PUR00111", + fw_type="pure-esp32", + model="pure", + calibration_temp=0.0, + calibration_hum=0.0, + full_capabilities={ + "modes": { + "fan": { + "temperatures": {}, + "fanLevels": ["low", "high"], + "light": ["on", "dim", "off"], + } + } + }, + motion_sensors={}, + pm25=1, + room_occupied=None, + update_available=False, + schedules={}, + pure_boost_enabled=False, + pure_sensitivity="N", + pure_ac_integration=False, + pure_geo_integration=False, + pure_measure_integration=True, + timer_on=None, + timer_id=None, + timer_state_on=None, + timer_time=None, + smart_on=None, + smart_type=None, + smart_low_temp_threshold=None, + smart_high_temp_threshold=None, + smart_low_state=None, + smart_high_state=None, + filter_clean=False, + filter_last_reset="2022-04-23T15:58:45Z", + ), + }, +) diff --git a/tests/components/sensibo/test_binary_sensor.py b/tests/components/sensibo/test_binary_sensor.py new file mode 100644 index 00000000000..cbf38ff27b0 --- /dev/null +++ b/tests/components/sensibo/test_binary_sensor.py @@ -0,0 +1,52 @@ +"""The test for the sensibo binary sensor platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from pytest import MonkeyPatch + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.util import dt + +from .response import DATA_FROM_API + +from tests.common import async_fire_time_changed + + +async def test_binary_sensor( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: MonkeyPatch +) -> None: + """Test the Sensibo binary sensor.""" + + state1 = hass.states.get("binary_sensor.hallway_motion_sensor_alive") + state2 = hass.states.get("binary_sensor.hallway_motion_sensor_main_sensor") + state3 = hass.states.get("binary_sensor.hallway_motion_sensor_motion") + state4 = hass.states.get("binary_sensor.hallway_room_occupied") + assert state1.state == "on" + assert state2.state == "on" + assert state3.state == "on" + assert state4.state == "on" + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"].motion_sensors["AABBCC"], "alive", False + ) + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"].motion_sensors["AABBCC"], "motion", False + ) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("binary_sensor.hallway_motion_sensor_alive") + state3 = hass.states.get("binary_sensor.hallway_motion_sensor_motion") + assert state1.state == "off" + assert state3.state == "off" diff --git a/tests/components/sensibo/test_climate.py b/tests/components/sensibo/test_climate.py new file mode 100644 index 00000000000..8b087da5d95 --- /dev/null +++ b/tests/components/sensibo/test_climate.py @@ -0,0 +1,601 @@ +"""The test for the sensibo binary sensor platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +import pytest +from voluptuous import MultipleInvalid + +from homeassistant.components.climate.const import ( + ATTR_FAN_MODE, + ATTR_HVAC_MODE, + ATTR_SWING_MODE, + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_SWING_MODE, + SERVICE_SET_TEMPERATURE, +) +from homeassistant.components.sensibo.climate import SERVICE_ASSUME_STATE +from homeassistant.components.sensibo.const import DOMAIN +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_STATE, + ATTR_TEMPERATURE, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, +) +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util import dt + +from .response import DATA_FROM_API + +from tests.common import async_fire_time_changed + + +async def test_climate(hass: HomeAssistant, load_int: ConfigEntry) -> None: + """Test the Sensibo climate.""" + + state1 = hass.states.get("climate.hallway") + state2 = hass.states.get("climate.kitchen") + + assert state1.state == "heat" + assert state1.attributes == { + "hvac_modes": [ + "cool", + "heat", + "dry", + "heat_cool", + "fan_only", + "off", + ], + "min_temp": 18, + "max_temp": 20, + "target_temp_step": 1, + "fan_modes": ["quiet", "low", "medium"], + "swing_modes": [ + "stopped", + "fixedTop", + "fixedMiddleTop", + ], + "current_temperature": 22.4, + "temperature": 25, + "current_humidity": 38, + "fan_mode": "high", + "swing_mode": "stopped", + "friendly_name": "Hallway", + "supported_features": 41, + } + + assert state2.state == "off" + + +async def test_climate_fan( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate fan service.""" + + state1 = hass.states.get("climate.hallway") + assert state1.attributes["fan_mode"] == "high" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["fan_mode"] == "low" + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "swing", + "targetTemperature", + "horizontalSwing", + "light", + ], + ) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) + await hass.async_block_till_done() + + state3 = hass.states.get("climate.hallway") + assert state3.attributes["fan_mode"] == "low" + + +async def test_climate_swing( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate swing service.""" + + state1 = hass.states.get("climate.hallway") + assert state1.attributes["swing_mode"] == "stopped" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_SWING_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "fixedTop"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["swing_mode"] == "fixedTop" + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "targetTemperature", + "horizontalSwing", + "light", + ], + ) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_SWING_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "fixedTop"}, + blocking=True, + ) + await hass.async_block_till_done() + + state3 = hass.states.get("climate.hallway") + assert state3.attributes["swing_mode"] == "fixedTop" + + +async def test_climate_temperatures( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate temperature service.""" + + state1 = hass.states.get("climate.hallway") + assert state1.attributes["temperature"] == 25 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 20 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 15}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 18 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + with pytest.raises(ValueError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 18.5}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 18 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 24}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 20 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 20 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + with pytest.raises(MultipleInvalid): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 20 + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "swing", + "horizontalSwing", + "light", + ], + ) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 20 + + +async def test_climate_temperature_is_none( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate temperature service no temperature provided.""" + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "fanLevel", + "targetTemperature", + "swing", + "horizontalSwing", + "light", + ], + ) + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "target_temp", + 25, + ) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("climate.hallway") + assert state1.attributes["temperature"] == 25 + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + ): + with pytest.raises(ValueError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: state1.entity_id, + ATTR_TARGET_TEMP_HIGH: 30, + ATTR_TARGET_TEMP_LOW: 20, + }, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.attributes["temperature"] == 25 + + +async def test_climate_hvac_mode( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate hvac mode service.""" + + monkeypatch.setattr( + DATA_FROM_API.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "fanLevel", + "targetTemperature", + "swing", + "horizontalSwing", + "light", + ], + ) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("climate.hallway") + assert state1.state == "heat" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_HVAC_MODE: "off"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "off" + + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", False) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_HVAC_MODE: "heat"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "heat" + + +async def test_climate_on_off( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate on/off service.""" + + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("climate.hallway") + assert state1.state == "heat" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: state1.entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "off" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: state1.entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "heat" + + +async def test_climate_service_failed( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate service failed.""" + + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("climate.hallway") + assert state1.state == "heat" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Error", "failureReason": "Did not work"}}, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: state1.entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "heat" + + +async def test_climate_assumed_state( + hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo climate assumed state service.""" + + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("climate.hallway") + assert state1.state == "heat" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + DOMAIN, + SERVICE_ASSUME_STATE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_STATE: "off"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("climate.hallway") + assert state2.state == "off" diff --git a/tests/components/sensibo/test_coordinator.py b/tests/components/sensibo/test_coordinator.py new file mode 100644 index 00000000000..703eb8b184b --- /dev/null +++ b/tests/components/sensibo/test_coordinator.py @@ -0,0 +1,91 @@ +"""The test for the sensibo coordinator.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from pysensibo.exceptions import AuthenticationError, SensiboError +from pysensibo.model import SensiboData +import pytest + +from homeassistant.components.sensibo.const import DOMAIN +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.util import dt + +from . import ENTRY_CONFIG +from .response import DATA_FROM_API + +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def test_coordinator( + hass: HomeAssistant, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test the Sensibo coordinator with errors.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="username", + version=2, + ) + + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + ) as mock_data, patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {"username": "username"}}, + ): + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + mock_data.return_value = DATA_FROM_API + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == "heat" + mock_data.reset_mock() + + mock_data.side_effect = SensiboError("info") + async_fire_time_changed(hass, dt.utcnow() + timedelta(minutes=1)) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE + mock_data.reset_mock() + + mock_data.return_value = SensiboData(raw={}, parsed={}) + mock_data.side_effect = None + async_fire_time_changed(hass, dt.utcnow() + timedelta(minutes=3)) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE + mock_data.reset_mock() + + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + + mock_data.return_value = DATA_FROM_API + mock_data.side_effect = None + async_fire_time_changed(hass, dt.utcnow() + timedelta(minutes=5)) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == "heat" + mock_data.reset_mock() + + mock_data.side_effect = AuthenticationError("info") + async_fire_time_changed(hass, dt.utcnow() + timedelta(minutes=7)) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE diff --git a/tests/components/sensibo/test_diagnostics.py b/tests/components/sensibo/test_diagnostics.py new file mode 100644 index 00000000000..b4e85dad2b4 --- /dev/null +++ b/tests/components/sensibo/test_diagnostics.py @@ -0,0 +1,48 @@ +"""Test Sensibo diagnostics.""" +from __future__ import annotations + +import aiohttp + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, hass_client: aiohttp.client, load_int: ConfigEntry +): + """Test generating diagnostics for a config entry.""" + entry = load_int + + diag = await get_diagnostics_for_config_entry(hass, hass_client, entry) + + assert diag == { + "status": "success", + "result": [ + { + "id": "**REDACTED**", + "qrId": "**REDACTED**", + "room": {"uid": "**REDACTED**", "name": "Hallway", "icon": "Lounge"}, + "acState": { + "timestamp": { + "time": "2022-04-30T19:58:15.544787Z", + "secondsAgo": 0, + }, + "on": False, + "mode": "fan", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + "location": "**REDACTED**", + "accessPoint": {"ssid": "**REDACTED**", "password": None}, + "macAddress": "**REDACTED**", + "autoOffMinutes": None, + "autoOffEnabled": False, + "antiMoldTimer": None, + "antiMoldConfig": None, + } + ], + } diff --git a/tests/components/sensibo/test_entity.py b/tests/components/sensibo/test_entity.py new file mode 100644 index 00000000000..7df70c7d45e --- /dev/null +++ b/tests/components/sensibo/test_entity.py @@ -0,0 +1,132 @@ +"""The test for the sensibo entity.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +import pytest + +from homeassistant.components.climate.const import ( + ATTR_FAN_MODE, + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, +) +from homeassistant.components.number.const import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.components.sensibo.const import SENSIBO_ERRORS +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.util import dt + +from .response import DATA_FROM_API + +from tests.common import async_fire_time_changed + + +async def test_entity(hass: HomeAssistant, load_int: ConfigEntry) -> None: + """Test the Sensibo climate.""" + + state1 = hass.states.get("climate.hallway") + assert state1 + + dr_reg = dr.async_get(hass) + dr_entries = dr.async_entries_for_config_entry(dr_reg, load_int.entry_id) + dr_entry: dr.DeviceEntry + for dr_entry in dr_entries: + if dr_entry.name == "Hallway": + assert dr_entry.identifiers == {("sensibo", "ABC999111")} + device_id = dr_entry.id + + er_reg = er.async_get(hass) + er_entries = er.async_entries_for_device( + er_reg, device_id, include_disabled_entities=True + ) + er_entry: er.RegistryEntry + for er_entry in er_entries: + if er_entry.name == "Hallway": + assert er_entry.unique_id == "Hallway" + + +@pytest.mark.parametrize("p_error", SENSIBO_ERRORS) +async def test_entity_send_command( + hass: HomeAssistant, p_error: Exception, load_int: ConfigEntry +) -> None: + """Test the Sensibo send command with error.""" + + state = hass.states.get("climate.hallway") + assert state + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) + await hass.async_block_till_done() + + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "low" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + side_effect=p_error, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "low" + + +async def test_entity_send_command_calibration( + hass: HomeAssistant, load_int: ConfigEntry +) -> None: + """Test the Sensibo send command for calibration.""" + + registry = er.async_get(hass) + registry.async_update_entity( + "number.hallway_temperature_calibration", disabled_by=None + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.1" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_calibration", + return_value={"status": "success"}, + ): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_VALUE: 0.2}, + blocking=True, + ) + + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.2" diff --git a/tests/components/sensibo/test_init.py b/tests/components/sensibo/test_init.py new file mode 100644 index 00000000000..b9ad56eaf07 --- /dev/null +++ b/tests/components/sensibo/test_init.py @@ -0,0 +1,132 @@ +"""Test for Sensibo component Init.""" +from __future__ import annotations + +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.sensibo.const import DOMAIN +from homeassistant.components.sensibo.util import NoUsernameError +from homeassistant.config_entries import SOURCE_USER +from homeassistant.core import HomeAssistant + +from . import ENTRY_CONFIG +from .response import DATA_FROM_API + +from tests.common import MockConfigEntry + + +async def test_setup_entry(hass: HomeAssistant) -> None: + """Test setup entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="12", + version=2, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {"username": "username"}}, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == config_entries.ConfigEntryState.LOADED + + +async def test_migrate_entry(hass: HomeAssistant) -> None: + """Test migrate entry unique id.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="12", + version=1, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {"username": "username"}}, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == config_entries.ConfigEntryState.LOADED + assert entry.version == 2 + assert entry.unique_id == "username" + + +async def test_migrate_entry_fails(hass: HomeAssistant) -> None: + """Test migrate entry unique id.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="12", + version=1, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + side_effect=NoUsernameError("No username returned"), + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == config_entries.ConfigEntryState.MIGRATION_ERROR + assert entry.version == 1 + assert entry.unique_id == "12" + + +async def test_unload_entry(hass: HomeAssistant) -> None: + """Test unload an entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + source=SOURCE_USER, + data=ENTRY_CONFIG, + entry_id="1", + unique_id="12", + version="2", + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=DATA_FROM_API, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {"username": "username"}}, + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state == config_entries.ConfigEntryState.LOADED + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + assert entry.state is config_entries.ConfigEntryState.NOT_LOADED From 118bae6cb40468e4c351d92b49eef434f7befb39 Mon Sep 17 00:00:00 2001 From: Alessandro Di Felice Date: Fri, 6 May 2022 02:27:46 -0500 Subject: [PATCH 243/930] Upgrade glances_api to 0.3.5 (#71243) --- homeassistant/components/glances/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/glances/manifest.json b/homeassistant/components/glances/manifest.json index 73f4d2f333f..3c2906f9fd6 100644 --- a/homeassistant/components/glances/manifest.json +++ b/homeassistant/components/glances/manifest.json @@ -3,7 +3,7 @@ "name": "Glances", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/glances", - "requirements": ["glances_api==0.3.4"], + "requirements": ["glances_api==0.3.5"], "codeowners": ["@engrbm87"], "iot_class": "local_polling", "loggers": ["glances_api"] diff --git a/requirements_all.txt b/requirements_all.txt index f47df3386ad..38566ceed22 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -723,7 +723,7 @@ gios==2.1.0 gitterpy==0.1.7 # homeassistant.components.glances -glances_api==0.3.4 +glances_api==0.3.5 # homeassistant.components.goalzero goalzero==0.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4d7b846f10a..953aeebc8de 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -514,7 +514,7 @@ getmac==0.8.2 gios==2.1.0 # homeassistant.components.glances -glances_api==0.3.4 +glances_api==0.3.5 # homeassistant.components.goalzero goalzero==0.2.1 From 225d41f82abf55f2671e4ca15358e75b7732213c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 6 May 2022 12:07:02 +0200 Subject: [PATCH 244/930] Bump numpy to v1.22.3 (#71393) * Bump numpy to v1.22.3 * Fix mypy --- homeassistant/components/compensation/manifest.json | 2 +- homeassistant/components/iqvia/manifest.json | 2 +- homeassistant/components/iqvia/sensor.py | 2 +- homeassistant/components/opencv/manifest.json | 2 +- homeassistant/components/tensorflow/manifest.json | 2 +- homeassistant/components/trend/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/compensation/manifest.json b/homeassistant/components/compensation/manifest.json index 213e8888e23..1f96ae25953 100644 --- a/homeassistant/components/compensation/manifest.json +++ b/homeassistant/components/compensation/manifest.json @@ -2,7 +2,7 @@ "domain": "compensation", "name": "Compensation", "documentation": "https://www.home-assistant.io/integrations/compensation", - "requirements": ["numpy==1.21.6"], + "requirements": ["numpy==1.22.3"], "codeowners": ["@Petro31"], "iot_class": "calculated" } diff --git a/homeassistant/components/iqvia/manifest.json b/homeassistant/components/iqvia/manifest.json index 9bb07157b54..f363d7aa91c 100644 --- a/homeassistant/components/iqvia/manifest.json +++ b/homeassistant/components/iqvia/manifest.json @@ -3,7 +3,7 @@ "name": "IQVIA", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/iqvia", - "requirements": ["numpy==1.21.6", "pyiqvia==2022.04.0"], + "requirements": ["numpy==1.22.3", "pyiqvia==2022.04.0"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["pyiqvia"] diff --git a/homeassistant/components/iqvia/sensor.py b/homeassistant/components/iqvia/sensor.py index 51f2969e9fe..d8c7ea317c8 100644 --- a/homeassistant/components/iqvia/sensor.py +++ b/homeassistant/components/iqvia/sensor.py @@ -161,7 +161,7 @@ def calculate_trend(indices: list[float]) -> str: """Calculate the "moving average" of a set of indices.""" index_range = np.arange(0, len(indices)) index_array = np.array(indices) - linear_fit = np.polyfit(index_range, index_array, 1) # type: ignore[no-untyped-call] + linear_fit = np.polyfit(index_range, index_array, 1) slope = round(linear_fit[0], 2) if slope > 0: diff --git a/homeassistant/components/opencv/manifest.json b/homeassistant/components/opencv/manifest.json index 504b83bdaf9..deba05b4065 100644 --- a/homeassistant/components/opencv/manifest.json +++ b/homeassistant/components/opencv/manifest.json @@ -2,7 +2,7 @@ "domain": "opencv", "name": "OpenCV", "documentation": "https://www.home-assistant.io/integrations/opencv", - "requirements": ["numpy==1.21.6", "opencv-python-headless==4.5.2.54"], + "requirements": ["numpy==1.22.3", "opencv-python-headless==4.5.2.54"], "codeowners": [], "iot_class": "local_push" } diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index 0f53dd61cb4..cdc3659153d 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -6,7 +6,7 @@ "tensorflow==2.5.0", "tf-models-official==2.5.0", "pycocotools==2.0.1", - "numpy==1.21.6", + "numpy==1.22.3", "pillow==9.1.0" ], "codeowners": [], diff --git a/homeassistant/components/trend/manifest.json b/homeassistant/components/trend/manifest.json index aaae8f7cc54..d50455aa9ab 100644 --- a/homeassistant/components/trend/manifest.json +++ b/homeassistant/components/trend/manifest.json @@ -2,7 +2,7 @@ "domain": "trend", "name": "Trend", "documentation": "https://www.home-assistant.io/integrations/trend", - "requirements": ["numpy==1.21.6"], + "requirements": ["numpy==1.22.3"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 38566ceed22..33da79b3234 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1114,7 +1114,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.21.6 +numpy==1.22.3 # homeassistant.components.oasa_telematics oasatelematics==0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 953aeebc8de..398cbb62d92 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -758,7 +758,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.21.6 +numpy==1.22.3 # homeassistant.components.google oauth2client==4.1.3 From f1733236bbffa1f828013f5a4cc89648e53e98dd Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 6 May 2022 12:51:19 +0200 Subject: [PATCH 245/930] Revert "Bump numpy to v1.22.3 (#71393)" (#71407) This reverts commit 225d41f82abf55f2671e4ca15358e75b7732213c. --- homeassistant/components/compensation/manifest.json | 2 +- homeassistant/components/iqvia/manifest.json | 2 +- homeassistant/components/iqvia/sensor.py | 2 +- homeassistant/components/opencv/manifest.json | 2 +- homeassistant/components/tensorflow/manifest.json | 2 +- homeassistant/components/trend/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/compensation/manifest.json b/homeassistant/components/compensation/manifest.json index 1f96ae25953..213e8888e23 100644 --- a/homeassistant/components/compensation/manifest.json +++ b/homeassistant/components/compensation/manifest.json @@ -2,7 +2,7 @@ "domain": "compensation", "name": "Compensation", "documentation": "https://www.home-assistant.io/integrations/compensation", - "requirements": ["numpy==1.22.3"], + "requirements": ["numpy==1.21.6"], "codeowners": ["@Petro31"], "iot_class": "calculated" } diff --git a/homeassistant/components/iqvia/manifest.json b/homeassistant/components/iqvia/manifest.json index f363d7aa91c..9bb07157b54 100644 --- a/homeassistant/components/iqvia/manifest.json +++ b/homeassistant/components/iqvia/manifest.json @@ -3,7 +3,7 @@ "name": "IQVIA", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/iqvia", - "requirements": ["numpy==1.22.3", "pyiqvia==2022.04.0"], + "requirements": ["numpy==1.21.6", "pyiqvia==2022.04.0"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "loggers": ["pyiqvia"] diff --git a/homeassistant/components/iqvia/sensor.py b/homeassistant/components/iqvia/sensor.py index d8c7ea317c8..51f2969e9fe 100644 --- a/homeassistant/components/iqvia/sensor.py +++ b/homeassistant/components/iqvia/sensor.py @@ -161,7 +161,7 @@ def calculate_trend(indices: list[float]) -> str: """Calculate the "moving average" of a set of indices.""" index_range = np.arange(0, len(indices)) index_array = np.array(indices) - linear_fit = np.polyfit(index_range, index_array, 1) + linear_fit = np.polyfit(index_range, index_array, 1) # type: ignore[no-untyped-call] slope = round(linear_fit[0], 2) if slope > 0: diff --git a/homeassistant/components/opencv/manifest.json b/homeassistant/components/opencv/manifest.json index deba05b4065..504b83bdaf9 100644 --- a/homeassistant/components/opencv/manifest.json +++ b/homeassistant/components/opencv/manifest.json @@ -2,7 +2,7 @@ "domain": "opencv", "name": "OpenCV", "documentation": "https://www.home-assistant.io/integrations/opencv", - "requirements": ["numpy==1.22.3", "opencv-python-headless==4.5.2.54"], + "requirements": ["numpy==1.21.6", "opencv-python-headless==4.5.2.54"], "codeowners": [], "iot_class": "local_push" } diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index cdc3659153d..0f53dd61cb4 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -6,7 +6,7 @@ "tensorflow==2.5.0", "tf-models-official==2.5.0", "pycocotools==2.0.1", - "numpy==1.22.3", + "numpy==1.21.6", "pillow==9.1.0" ], "codeowners": [], diff --git a/homeassistant/components/trend/manifest.json b/homeassistant/components/trend/manifest.json index d50455aa9ab..aaae8f7cc54 100644 --- a/homeassistant/components/trend/manifest.json +++ b/homeassistant/components/trend/manifest.json @@ -2,7 +2,7 @@ "domain": "trend", "name": "Trend", "documentation": "https://www.home-assistant.io/integrations/trend", - "requirements": ["numpy==1.22.3"], + "requirements": ["numpy==1.21.6"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 33da79b3234..38566ceed22 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1114,7 +1114,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.22.3 +numpy==1.21.6 # homeassistant.components.oasa_telematics oasatelematics==0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 398cbb62d92..953aeebc8de 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -758,7 +758,7 @@ numato-gpio==0.10.0 # homeassistant.components.opencv # homeassistant.components.tensorflow # homeassistant.components.trend -numpy==1.22.3 +numpy==1.21.6 # homeassistant.components.google oauth2client==4.1.3 From e5619f4af1e1afe28c3f614a79d6756eb4da8729 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 6 May 2022 13:27:12 +0200 Subject: [PATCH 246/930] Remove yaml import trafikverket_train (#71410) --- .../trafikverket_train/config_flow.py | 5 -- .../components/trafikverket_train/const.py | 1 - .../components/trafikverket_train/sensor.py | 60 +-------------- .../trafikverket_train/test_config_flow.py | 75 ------------------- 4 files changed, 3 insertions(+), 138 deletions(-) diff --git a/homeassistant/components/trafikverket_train/config_flow.py b/homeassistant/components/trafikverket_train/config_flow.py index 79c5978de2c..823b393f7b1 100644 --- a/homeassistant/components/trafikverket_train/config_flow.py +++ b/homeassistant/components/trafikverket_train/config_flow.py @@ -102,11 +102,6 @@ class TVTrainConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_import(self, config: dict[str, Any] | None) -> FlowResult: - """Import a configuration from config.yaml.""" - - return await self.async_step_user(user_input=config) - async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> FlowResult: diff --git a/homeassistant/components/trafikverket_train/const.py b/homeassistant/components/trafikverket_train/const.py index f0a6a1d6a18..253383b4b5a 100644 --- a/homeassistant/components/trafikverket_train/const.py +++ b/homeassistant/components/trafikverket_train/const.py @@ -5,7 +5,6 @@ DOMAIN = "trafikverket_train" PLATFORMS = [Platform.SENSOR] ATTRIBUTION = "Data provided by Trafikverket" -CONF_TRAINS = "trains" CONF_FROM = "from" CONF_TO = "to" CONF_TIME = "time" diff --git a/homeassistant/components/trafikverket_train/sensor.py b/homeassistant/components/trafikverket_train/sensor.py index 6ae6af7d363..4a419ff3b33 100644 --- a/homeassistant/components/trafikverket_train/sensor.py +++ b/homeassistant/components/trafikverket_train/sensor.py @@ -7,26 +7,19 @@ from typing import Any from pytrafikverket import TrafikverketTrain from pytrafikverket.trafikverket_train import StationInfo, TrainStop -import voluptuous as vol -from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, - SensorDeviceClass, - SensorEntity, -) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_WEEKDAY, WEEKDAYS from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt -from .const import CONF_FROM, CONF_TIME, CONF_TO, CONF_TRAINS, DOMAIN +from .const import CONF_FROM, CONF_TIME, CONF_TO, DOMAIN from .util import create_unique_id _LOGGER = logging.getLogger(__name__) @@ -43,53 +36,6 @@ ATTR_DEVIATIONS = "deviations" ICON = "mdi:train" SCAN_INTERVAL = timedelta(minutes=5) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Required(CONF_TRAINS): [ - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_TO): cv.string, - vol.Required(CONF_FROM): cv.string, - vol.Optional(CONF_TIME): cv.time, - vol.Optional(CONF_WEEKDAY, default=WEEKDAYS): vol.All( - cv.ensure_list, [vol.In(WEEKDAYS)] - ), - } - ], - } -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Import Trafikverket Train configuration from YAML.""" - _LOGGER.warning( - # Config flow added in Home Assistant Core 2022.3, remove import flow in 2022.7 - "Loading Trafikverket Train via platform setup is deprecated; Please remove it from your configuration" - ) - - for train in config[CONF_TRAINS]: - - new_config = { - CONF_API_KEY: config[CONF_API_KEY], - CONF_FROM: train[CONF_FROM], - CONF_TO: train[CONF_TO], - CONF_TIME: str(train.get(CONF_TIME)), - CONF_WEEKDAY: train.get(CONF_WEEKDAY, WEEKDAYS), - } - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=new_config, - ) - ) - async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback diff --git a/tests/components/trafikverket_train/test_config_flow.py b/tests/components/trafikverket_train/test_config_flow.py index b8e3548af2e..09539584cbc 100644 --- a/tests/components/trafikverket_train/test_config_flow.py +++ b/tests/components/trafikverket_train/test_config_flow.py @@ -66,81 +66,6 @@ async def test_form(hass: HomeAssistant) -> None: ) -async def test_import_flow_success(hass: HomeAssistant) -> None: - """Test a successful import of yaml.""" - - with patch( - "homeassistant.components.trafikverket_train.config_flow.TrafikverketTrain.async_get_train_station", - ), patch( - "homeassistant.components.trafikverket_train.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data={ - CONF_API_KEY: "1234567890", - CONF_FROM: "Stockholm C", - CONF_TO: "Uppsala C", - CONF_TIME: "10:00", - CONF_WEEKDAY: ["mon", "fri"], - }, - ) - await hass.async_block_till_done() - - assert result2["type"] == RESULT_TYPE_CREATE_ENTRY - assert result2["title"] == "Stockholm C to Uppsala C at 10:00" - assert result2["data"] == { - "api_key": "1234567890", - "name": "Stockholm C to Uppsala C at 10:00", - "from": "Stockholm C", - "to": "Uppsala C", - "time": "10:00", - "weekday": ["mon", "fri"], - } - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_import_flow_already_exist(hass: HomeAssistant) -> None: - """Test import of yaml already exist.""" - - MockConfigEntry( - domain=DOMAIN, - data={ - CONF_API_KEY: "1234567890", - CONF_NAME: "Stockholm C to Uppsala C", - CONF_FROM: "Stockholm C", - CONF_TO: "Uppsala C", - CONF_TIME: "10:00", - CONF_WEEKDAY: WEEKDAYS, - }, - unique_id=f"stockholmc-uppsalac-10:00-{WEEKDAYS}", - ).add_to_hass(hass) - - with patch( - "homeassistant.components.trafikverket_train.async_setup_entry", - return_value=True, - ), patch( - "homeassistant.components.trafikverket_train.config_flow.TrafikverketTrain.async_get_train_station", - ): - result3 = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data={ - CONF_NAME: "Stockholm C to Uppsala C", - CONF_API_KEY: "1234567890", - CONF_FROM: "Stockholm C", - CONF_TO: "Uppsala C", - CONF_TIME: "10:00", - CONF_WEEKDAY: WEEKDAYS, - }, - ) - await hass.async_block_till_done() - - assert result3["type"] == RESULT_TYPE_ABORT - assert result3["reason"] == "already_configured" - - @pytest.mark.parametrize( "error_message,base_error", [ From 173f14379b98b108d4d8c05fecde57e59b2dc7a9 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Fri, 6 May 2022 10:24:08 -0400 Subject: [PATCH 247/930] Update Zigpy attribute cache for switch devices that do not report state (#71417) * fix devices that do not report state * whoops --- .../components/zha/core/channels/general.py | 16 ++++++++++++++++ homeassistant/components/zha/switch.py | 8 ++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index e6524c9aad1..2e6093dd4f7 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -308,6 +308,22 @@ class OnOffChannel(ZigbeeChannel): """Return cached value of on/off attribute.""" return self.cluster.get("on_off") + async def turn_on(self) -> bool: + """Turn the on off cluster on.""" + result = await self.on() + if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + return False + self.cluster.update_attribute(self.ON_OFF, t.Bool.true) + return True + + async def turn_off(self) -> bool: + """Turn the on off cluster off.""" + result = await self.off() + if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + return False + self.cluster.update_attribute(self.ON_OFF, t.Bool.false) + return True + @callback def cluster_command(self, tsn, command_id, args): """Handle commands received to this cluster.""" diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 254e3691da1..76c41093ed6 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -64,15 +64,15 @@ class Switch(ZhaEntity, SwitchEntity): async def async_turn_on(self, **kwargs) -> None: """Turn the entity on.""" - result = await self._on_off_channel.on() - if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + result = await self._on_off_channel.turn_on() + if not result: return self.async_write_ha_state() async def async_turn_off(self, **kwargs) -> None: """Turn the entity off.""" - result = await self._on_off_channel.off() - if isinstance(result, Exception) or result[1] is not Status.SUCCESS: + result = await self._on_off_channel.turn_off() + if not result: return self.async_write_ha_state() From 6984c56cc6c34e5522823b801bfc8cf6568b164f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 6 May 2022 17:00:01 +0200 Subject: [PATCH 248/930] Freeze numpy on wheel build (#71408) --- .github/workflows/wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 950338cdd13..e43c397f3bc 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -96,7 +96,7 @@ jobs: wheels-user: wheels env-file: true apk: "build-base;cmake;git;linux-headers;bluez-dev;libffi-dev;openssl-dev;glib-dev;eudev-dev;libxml2-dev;libxslt-dev;cargo" - pip: "Cython;numpy" + pip: "Cython;numpy==1.21.6" skip-binary: aiohttp constraints: "homeassistant/package_constraints.txt" requirements-diff: "requirements_diff.txt" From 1a00bb9fc4a12b7cacfd1371892b33d06a08ecd8 Mon Sep 17 00:00:00 2001 From: 0bmay <57501269+0bmay@users.noreply.github.com> Date: Fri, 6 May 2022 08:00:48 -0700 Subject: [PATCH 249/930] Fix Canary camera stream blocking call (#71369) * fix: Canary stream camera, fix blocker fixes a "detected blocking call to putrequest inside the event loop. This is causing stability issues. Please report issue for canary doing blocking calls at homeassistant/components/canary/camera.py, line 149: self._live_stream_session.live_stream_url, extra_cmd=self._ffmpeg_arguments" from log file. * refactor: black formatting changes tsia --- homeassistant/components/canary/camera.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/canary/camera.py b/homeassistant/components/canary/camera.py index 46826d80291..5caae6f5cc5 100644 --- a/homeassistant/components/canary/camera.py +++ b/homeassistant/components/canary/camera.py @@ -144,10 +144,11 @@ class CanaryCamera(CoordinatorEntity[CanaryDataUpdateCoordinator], Camera): if self._live_stream_session is None: return None - stream = CameraMjpeg(self._ffmpeg.binary) - await stream.open_camera( - self._live_stream_session.live_stream_url, extra_cmd=self._ffmpeg_arguments + live_stream_url = await self.hass.async_add_executor_job( + getattr, self._live_stream_session, "live_stream_url" ) + stream = CameraMjpeg(self._ffmpeg.binary) + await stream.open_camera(live_stream_url, extra_cmd=self._ffmpeg_arguments) try: stream_reader = await stream.get_reader() From b1a04302b55dd511f3416cf2ad21a21e78d23bed Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 6 May 2022 11:25:01 -0700 Subject: [PATCH 250/930] Stringify enums in selectors (#71441) --- homeassistant/helpers/selector.py | 17 +++-------------- tests/components/automation/test_init.py | 2 ++ tests/components/blueprint/test_importer.py | 1 + .../components/blueprint/test_websocket_api.py | 4 +++- tests/components/trace/test_websocket_api.py | 1 + tests/helpers/test_selector.py | 4 ++++ .../automation/test_event_service.yaml | 4 ++++ 7 files changed, 18 insertions(+), 15 deletions(-) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index afe74a4d7af..1ae0082c06f 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -73,11 +73,7 @@ class Selector: def serialize(self) -> Any: """Serialize Selector for voluptuous_serialize.""" - return {"selector": {self.selector_type: self.serialize_config()}} - - def serialize_config(self) -> Any: - """Serialize config.""" - return self.config + return {"selector": {self.selector_type: self.config}} SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA = vol.Schema( @@ -617,8 +613,8 @@ class NumberSelector(Selector): vol.Coerce(float), vol.Range(min=1e-3) ), vol.Optional(CONF_UNIT_OF_MEASUREMENT): str, - vol.Optional(CONF_MODE, default=NumberSelectorMode.SLIDER): vol.Coerce( - NumberSelectorMode + vol.Optional(CONF_MODE, default=NumberSelectorMode.SLIDER): vol.All( + vol.Coerce(NumberSelectorMode), lambda val: val.value ), } ), @@ -629,13 +625,6 @@ class NumberSelector(Selector): """Instantiate a selector.""" super().__init__(config) - def serialize_config(self) -> Any: - """Serialize the selector config.""" - return { - **self.config, - "mode": self.config["mode"].value, - } - def __call__(self, data: Any) -> float: """Validate the passed selection.""" value: float = vol.Coerce(float)(data) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index dbc8f0fc346..45f719a326f 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1474,6 +1474,7 @@ async def test_blueprint_automation(hass, calls): "input": { "trigger_event": "blueprint_event", "service_to_call": "test.automation", + "a_number": 5, }, } } @@ -1499,6 +1500,7 @@ async def test_blueprint_automation_bad_config(hass, caplog): "input": { "trigger_event": "blueprint_event", "service_to_call": {"dict": "not allowed"}, + "a_number": 5, }, } } diff --git a/tests/components/blueprint/test_importer.py b/tests/components/blueprint/test_importer.py index 806cdb2cb8d..46a98840a80 100644 --- a/tests/components/blueprint/test_importer.py +++ b/tests/components/blueprint/test_importer.py @@ -199,6 +199,7 @@ async def test_fetch_blueprint_from_github_url(hass, aioclient_mock, url): assert imported_blueprint.blueprint.inputs == { "service_to_call": None, "trigger_event": {"selector": {"text": {}}}, + "a_number": {"selector": {"number": {"mode": "box", "step": 1.0}}}, } assert imported_blueprint.suggested_filename == "balloob/motion_light" assert imported_blueprint.blueprint.metadata["source_url"] == url diff --git a/tests/components/blueprint/test_websocket_api.py b/tests/components/blueprint/test_websocket_api.py index 40f24d98016..9376710abee 100644 --- a/tests/components/blueprint/test_websocket_api.py +++ b/tests/components/blueprint/test_websocket_api.py @@ -33,6 +33,7 @@ async def test_list_blueprints(hass, hass_ws_client): "input": { "service_to_call": None, "trigger_event": {"selector": {"text": {}}}, + "a_number": {"selector": {"number": {"mode": "box", "step": 1.0}}}, }, "name": "Call service based on event", }, @@ -95,6 +96,7 @@ async def test_import_blueprint(hass, aioclient_mock, hass_ws_client): "input": { "service_to_call": None, "trigger_event": {"selector": {"text": {}}}, + "a_number": {"selector": {"number": {"mode": "box", "step": 1.0}}}, }, "name": "Call service based on event", "source_url": "https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml", @@ -129,7 +131,7 @@ async def test_save_blueprint(hass, aioclient_mock, hass_ws_client): assert msg["success"] assert write_mock.mock_calls assert write_mock.call_args[0] == ( - "blueprint:\n name: Call service based on event\n domain: automation\n input:\n trigger_event:\n selector:\n text: {}\n service_to_call:\n source_url: https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml\ntrigger:\n platform: event\n event_type: !input 'trigger_event'\naction:\n service: !input 'service_to_call'\n entity_id: light.kitchen\n", + "blueprint:\n name: Call service based on event\n domain: automation\n input:\n trigger_event:\n selector:\n text: {}\n service_to_call:\n a_number:\n selector:\n number:\n mode: box\n step: 1.0\n source_url: https://github.com/balloob/home-assistant-config/blob/main/blueprints/automation/motion_light.yaml\ntrigger:\n platform: event\n event_type: !input 'trigger_event'\naction:\n service: !input 'service_to_call'\n entity_id: light.kitchen\n", ) diff --git a/tests/components/trace/test_websocket_api.py b/tests/components/trace/test_websocket_api.py index f55999a1e48..21eefb14c1b 100644 --- a/tests/components/trace/test_websocket_api.py +++ b/tests/components/trace/test_websocket_api.py @@ -1539,6 +1539,7 @@ async def test_trace_blueprint_automation( "input": { "trigger_event": "blueprint_event", "service_to_call": "test.automation", + "a_number": 5, }, }, } diff --git a/tests/helpers/test_selector.py b/tests/helpers/test_selector.py index 8c94e3d3c56..ed831026065 100644 --- a/tests/helpers/test_selector.py +++ b/tests/helpers/test_selector.py @@ -1,4 +1,6 @@ """Test selectors.""" +from enum import Enum + import pytest import voluptuous as vol @@ -52,6 +54,8 @@ def _test_selector( config = {selector_type: schema} selector.validate_selector(config) selector_instance = selector.selector(config) + # We do not allow enums in the config, as they cannot serialize + assert not any(isinstance(val, Enum) for val in selector_instance.config.values()) # Use selector in schema and validate vol_schema = vol.Schema({"selection": selector_instance}) diff --git a/tests/testing_config/blueprints/automation/test_event_service.yaml b/tests/testing_config/blueprints/automation/test_event_service.yaml index 648cef39b96..ba7462ed2e0 100644 --- a/tests/testing_config/blueprints/automation/test_event_service.yaml +++ b/tests/testing_config/blueprints/automation/test_event_service.yaml @@ -6,6 +6,10 @@ blueprint: selector: text: service_to_call: + a_number: + selector: + number: + mode: "box" trigger: platform: event event_type: !input trigger_event From 92f46a48c389ab89d069fbc35ef15586de0dbfa8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 May 2022 16:01:05 -0500 Subject: [PATCH 251/930] Bump zeroconf to 0.38.6 (#71447) --- homeassistant/components/zeroconf/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index e1ea2c82b1f..8cfc0698dc4 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.38.5"], + "requirements": ["zeroconf==0.38.6"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 0a2846a44ad..5c3ef6712b0 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -34,7 +34,7 @@ typing-extensions>=3.10.0.2,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 yarl==1.7.2 -zeroconf==0.38.5 +zeroconf==0.38.6 # Constrain pycryptodome to avoid vulnerability # see https://github.com/home-assistant/core/pull/16238 diff --git a/requirements_all.txt b/requirements_all.txt index 38566ceed22..48f71c37f93 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2483,7 +2483,7 @@ youtube_dl==2021.12.17 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.38.5 +zeroconf==0.38.6 # homeassistant.components.zha zha-quirks==0.0.73 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 953aeebc8de..8adb336e488 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1623,7 +1623,7 @@ yeelight==0.7.10 youless-api==0.16 # homeassistant.components.zeroconf -zeroconf==0.38.5 +zeroconf==0.38.6 # homeassistant.components.zha zha-quirks==0.0.73 From 44d8f2f7730f7fb6977e96d0e591323b5682e23e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 7 May 2022 00:20:47 +0000 Subject: [PATCH 252/930] [ci skip] Translation update --- homeassistant/components/asuswrt/translations/it.json | 3 +++ homeassistant/components/asuswrt/translations/nl.json | 1 + homeassistant/components/asuswrt/translations/no.json | 2 +- homeassistant/components/asuswrt/translations/pl.json | 3 ++- homeassistant/components/deluge/translations/it.json | 2 +- homeassistant/components/dlna_dmr/translations/it.json | 1 + homeassistant/components/dlna_dmr/translations/nl.json | 1 + homeassistant/components/dlna_dmr/translations/pl.json | 1 + homeassistant/components/dlna_dmr/translations/ru.json | 1 + homeassistant/components/flux_led/translations/it.json | 2 +- homeassistant/components/isy994/translations/it.json | 2 +- homeassistant/components/meater/translations/it.json | 3 +++ homeassistant/components/recorder/translations/nl.json | 8 ++++++++ homeassistant/components/simplisafe/translations/it.json | 2 +- homeassistant/components/vulcan/translations/de.json | 9 +++++++++ homeassistant/components/vulcan/translations/et.json | 9 +++++++++ homeassistant/components/vulcan/translations/it.json | 9 +++++++++ homeassistant/components/vulcan/translations/nl.json | 9 +++++++++ homeassistant/components/vulcan/translations/no.json | 9 +++++++++ homeassistant/components/vulcan/translations/pl.json | 9 +++++++++ homeassistant/components/vulcan/translations/ru.json | 9 +++++++++ .../components/vulcan/translations/zh-Hant.json | 9 +++++++++ 22 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/recorder/translations/nl.json diff --git a/homeassistant/components/asuswrt/translations/it.json b/homeassistant/components/asuswrt/translations/it.json index 0cf06679d83..d2152c85357 100644 --- a/homeassistant/components/asuswrt/translations/it.json +++ b/homeassistant/components/asuswrt/translations/it.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "Impossibile determinare un ID univoco valido per il dispositivo", + "no_unique_id": "Un dispositivo senza un ID univoco valido \u00e8 gi\u00e0 configurato. La configurazione di pi\u00f9 istanze non \u00e8 possibile", + "not_unique_id_exist": "Un dispositivo senza un ID univoco valido \u00e8 gi\u00e0 configurato. La configurazione di pi\u00f9 istanze non \u00e8 possibile", "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/nl.json b/homeassistant/components/asuswrt/translations/nl.json index 26d95e16461..4f3611289e5 100644 --- a/homeassistant/components/asuswrt/translations/nl.json +++ b/homeassistant/components/asuswrt/translations/nl.json @@ -2,6 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Onmogelijk om een geldige unieke id voor het apparaat te bepalen", + "no_unique_id": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk", "not_unique_id_exist": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk", "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." }, diff --git a/homeassistant/components/asuswrt/translations/no.json b/homeassistant/components/asuswrt/translations/no.json index 5aa48649989..de540484cac 100644 --- a/homeassistant/components/asuswrt/translations/no.json +++ b/homeassistant/components/asuswrt/translations/no.json @@ -3,7 +3,7 @@ "abort": { "invalid_unique_id": "Umulig \u00e5 bestemme en gyldig unik ID for enheten", "no_unique_id": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", - "not_unique_id_exist": "En enhet uten en gyldig UniqueID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", + "not_unique_id_exist": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "error": { diff --git a/homeassistant/components/asuswrt/translations/pl.json b/homeassistant/components/asuswrt/translations/pl.json index ce0fc53accf..d30a1e9e778 100644 --- a/homeassistant/components/asuswrt/translations/pl.json +++ b/homeassistant/components/asuswrt/translations/pl.json @@ -2,7 +2,8 @@ "config": { "abort": { "invalid_unique_id": "Nie mo\u017cna okre\u015bli\u0107 prawid\u0142owego unikalnego identyfikatora urz\u0105dzenia", - "not_unique_id_exist": "Urz\u0105dzenie bez prawid\u0142owego unikatowego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa", + "no_unique_id": "Urz\u0105dzenie bez prawid\u0142owego unikalnego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa.", + "not_unique_id_exist": "Urz\u0105dzenie bez prawid\u0142owego unikalnego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa.", "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "error": { diff --git a/homeassistant/components/deluge/translations/it.json b/homeassistant/components/deluge/translations/it.json index d9407f0c29f..52cfb4a09ae 100644 --- a/homeassistant/components/deluge/translations/it.json +++ b/homeassistant/components/deluge/translations/it.json @@ -16,7 +16,7 @@ "username": "Nome utente", "web_port": "Porta web (per il servizio di visita)" }, - "description": "Per poter utilizzare questa integrazione, devi abilitare la seguente opzione nelle impostazioni di diluvio: Demone > Consenti controlli " + "description": "Per poter utilizzare questa integrazione, devi abilitare la seguente opzione nelle impostazioni di diluvio: Demone > Consenti controlli remoti" } } } diff --git a/homeassistant/components/dlna_dmr/translations/it.json b/homeassistant/components/dlna_dmr/translations/it.json index 545d3cadbcb..f9e3e11d5f0 100644 --- a/homeassistant/components/dlna_dmr/translations/it.json +++ b/homeassistant/components/dlna_dmr/translations/it.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Mostra file multimediali incompatibili durante la navigazione", "callback_url_override": "URL di richiamata dell'ascoltatore di eventi", "listen_port": "Porta dell'ascoltatore di eventi (casuale se non impostata)", "poll_availability": "Interrogazione per la disponibilit\u00e0 del dispositivo" diff --git a/homeassistant/components/dlna_dmr/translations/nl.json b/homeassistant/components/dlna_dmr/translations/nl.json index 5331f3340dd..6147fac2007 100644 --- a/homeassistant/components/dlna_dmr/translations/nl.json +++ b/homeassistant/components/dlna_dmr/translations/nl.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Incompatibele media weergeven tijdens browsen", "callback_url_override": "Event listener callback URL", "listen_port": "Poort om naar gebeurtenissen te luisteren (willekeurige poort indien niet ingesteld)", "poll_availability": "Pollen voor apparaat beschikbaarheid" diff --git a/homeassistant/components/dlna_dmr/translations/pl.json b/homeassistant/components/dlna_dmr/translations/pl.json index 7f831c92f99..23f46c84c38 100644 --- a/homeassistant/components/dlna_dmr/translations/pl.json +++ b/homeassistant/components/dlna_dmr/translations/pl.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Poka\u017c niezgodne multimedia podczas przegl\u0105dania", "callback_url_override": "Adres Callback URL dla detektora zdarze\u0144", "listen_port": "Port detektora zdarze\u0144 (losowy, je\u015bli nie jest ustawiony)", "poll_availability": "Sondowanie na dost\u0119pno\u015b\u0107 urz\u0105dze\u0144" diff --git a/homeassistant/components/dlna_dmr/translations/ru.json b/homeassistant/components/dlna_dmr/translations/ru.json index d8931e268a0..09c98173b3b 100644 --- a/homeassistant/components/dlna_dmr/translations/ru.json +++ b/homeassistant/components/dlna_dmr/translations/ru.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0435 \u043c\u0435\u0434\u0438\u0430", "callback_url_override": "Callback URL \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439", "listen_port": "\u041f\u043e\u0440\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 (\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d)", "poll_availability": "\u041e\u043f\u0440\u043e\u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" diff --git a/homeassistant/components/flux_led/translations/it.json b/homeassistant/components/flux_led/translations/it.json index 13b522906ba..f2d20bd45df 100644 --- a/homeassistant/components/flux_led/translations/it.json +++ b/homeassistant/components/flux_led/translations/it.json @@ -27,7 +27,7 @@ "data": { "custom_effect_colors": "Effetto personalizzato: Lista da 1 a 16 colori [R,G,B]. Esempio: [255,0,255],[60,128,0]", "custom_effect_speed_pct": "Effetto personalizzato: Velocit\u00e0 in percentuale per l'effetto che cambia colore.", - "custom_effect_transition": "Effetto personalizzato: Tipo di transizione tra i colori.", + "custom_effect_transition": "Effetto personalizzato: tipo di transizione tra i colori.", "mode": "La modalit\u00e0 di luminosit\u00e0 scelta." } } diff --git a/homeassistant/components/isy994/translations/it.json b/homeassistant/components/isy994/translations/it.json index 158d9f1d400..dae46ed8275 100644 --- a/homeassistant/components/isy994/translations/it.json +++ b/homeassistant/components/isy994/translations/it.json @@ -41,7 +41,7 @@ "sensor_string": "Stringa Nodo Sensore", "variable_sensor_string": "Stringa Variabile Sensore" }, - "description": "Imposta le opzioni per l'integrazione ISY: \n \u2022 Stringa nodo sensore: qualsiasi dispositivo o cartella che contiene \"Stringa nodo sensore\" nel nome verr\u00e0 trattato come un sensore o un sensore binario. \n \u2022 Ignora stringa: qualsiasi dispositivo con \"Ignora stringa\" nel nome verr\u00e0 ignorato. \n \u2022 Stringa variabile sensore: qualsiasi variabile che contiene \"Stringa variabile sensore\" verr\u00e0 aggiunta come sensore. \n \u2022 Ripristina luminosit\u00e0 luce: se abilitato, verr\u00e0 ripristinata la luminosit\u00e0 precedente quando si accende una luce al posto del livello incorporato nel dispositivo.", + "description": "Imposta le opzioni per l'integrazione ISY: \n \u2022 Stringa nodo sensore: qualsiasi dispositivo o cartella che contiene \"Stringa nodo sensore\" nel nome verr\u00e0 trattato come un sensore o un sensore binario. \n \u2022 Ignora stringa: qualsiasi dispositivo con \"Ignora stringa\" nel nome verr\u00e0 ignorato. \n \u2022 Stringa variabile sensore: qualsiasi variabile che contenga \"Stringa variabile sensore\" sar\u00e0 aggiunta come sensore. \n \u2022 Ripristina luminosit\u00e0 luce: se abilitato, verr\u00e0 ripristinata la luminosit\u00e0 precedente quando si accende una luce al posto del livello incorporato nel dispositivo.", "title": "Opzioni ISY" } } diff --git a/homeassistant/components/meater/translations/it.json b/homeassistant/components/meater/translations/it.json index 090de20f280..d006b2f7e64 100644 --- a/homeassistant/components/meater/translations/it.json +++ b/homeassistant/components/meater/translations/it.json @@ -7,6 +7,9 @@ }, "step": { "reauth_confirm": { + "data": { + "password": "Password" + }, "description": "Conferma la password per l'account Meater Cloud {username}." }, "user": { diff --git a/homeassistant/components/recorder/translations/nl.json b/homeassistant/components/recorder/translations/nl.json new file mode 100644 index 00000000000..5bffcef1bd2 --- /dev/null +++ b/homeassistant/components/recorder/translations/nl.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Starttijd huidige run", + "oldest_recorder_run": "Starttijd oudste run" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/it.json b/homeassistant/components/simplisafe/translations/it.json index fb92029795d..17a9a2ceb47 100644 --- a/homeassistant/components/simplisafe/translations/it.json +++ b/homeassistant/components/simplisafe/translations/it.json @@ -14,7 +14,7 @@ "unknown": "Errore imprevisto" }, "progress": { - "email_2fa": "Digita il codice di autenticazione a due fattori\n inviato tramite email." + "email_2fa": "Verifica la presenza nella tua email di un collegamento di verifica da Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/vulcan/translations/de.json b/homeassistant/components/vulcan/translations/de.json index 23d5032f5fa..94f25858119 100644 --- a/homeassistant/components/vulcan/translations/de.json +++ b/homeassistant/components/vulcan/translations/de.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Alle Sch\u00fcler wurden bereits hinzugef\u00fcgt.", "already_configured": "Dieser Sch\u00fcler wurde bereits hinzugef\u00fcgt.", + "no_matching_entries": "Keine \u00fcbereinstimmenden Eintr\u00e4ge gefunden, bitte verwende ein anderes Konto oder entferne die Integration mit veraltetem Sch\u00fcler.", "reauth_successful": "Reauth erfolgreich" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "Symbol", + "token": "Token" + }, + "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." + }, "select_saved_credentials": { "data": { "credentials": "Anmelden" diff --git a/homeassistant/components/vulcan/translations/et.json b/homeassistant/components/vulcan/translations/et.json index ab3156cc676..80c97d1c4ed 100644 --- a/homeassistant/components/vulcan/translations/et.json +++ b/homeassistant/components/vulcan/translations/et.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "K\u00f5ik \u00f5pilased on juba lisatud.", "already_configured": "See \u00f5pilane on juba lisatud.", + "no_matching_entries": "Sobivaid kirjeid ei leitud, kasuta teist kontot v\u00f5i eemalda muudetud \u00f5pilasega sidumine...", "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." }, + "reauth_confirm": { + "data": { + "pin": "PIN kood", + "region": "S\u00fcmbol", + "token": "Token" + }, + "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." + }, "select_saved_credentials": { "data": { "credentials": "Sisselogimine" diff --git a/homeassistant/components/vulcan/translations/it.json b/homeassistant/components/vulcan/translations/it.json index 895f4469eb9..8fe26ddfba6 100644 --- a/homeassistant/components/vulcan/translations/it.json +++ b/homeassistant/components/vulcan/translations/it.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Tutti gli studenti sono gi\u00e0 stati aggiunti.", "already_configured": "Quello studente \u00e8 gi\u00e0 stato aggiunto.", + "no_matching_entries": "Nessuna voce corrispondente trovata, utilizza un account diverso o rimuovi l'integrazione con lo studente obsoleto.", "reauth_successful": "Nuova autenticazione avvenuta" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "Simbolo", + "token": "Token" + }, + "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." + }, "select_saved_credentials": { "data": { "credentials": "Accesso" diff --git a/homeassistant/components/vulcan/translations/nl.json b/homeassistant/components/vulcan/translations/nl.json index b05b32937c0..df1990ce1a0 100644 --- a/homeassistant/components/vulcan/translations/nl.json +++ b/homeassistant/components/vulcan/translations/nl.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Alle studenten zijn al toegevoegd.", "already_configured": "Die student is al toegevoegd.", + "no_matching_entries": "Geen overeenkomende vermeldingen gevonden, gebruik een ander account of verwijder integratie met verouderde student..", "reauth_successful": "Opnieuw verifi\u00ebren gelukt" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Log in op uw Vulcan-account via de registratiepagina voor mobiele apps." }, + "reauth_confirm": { + "data": { + "pin": "Pincode", + "region": "Symbool", + "token": "Token" + }, + "description": "Log in op je Vulcan Account met behulp van de registratie pagina in de mobiele app." + }, "select_saved_credentials": { "data": { "credentials": "Inloggen" diff --git a/homeassistant/components/vulcan/translations/no.json b/homeassistant/components/vulcan/translations/no.json index 7cb8b68fa63..78444957ff1 100644 --- a/homeassistant/components/vulcan/translations/no.json +++ b/homeassistant/components/vulcan/translations/no.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Alle elever er allerede lagt til.", "already_configured": "Den studenten er allerede lagt til.", + "no_matching_entries": "Ingen samsvarende oppf\u00f8ringer funnet, vennligst bruk en annen konto eller fjern integrasjon med utdatert student..", "reauth_successful": "Reauth vellykket" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." }, + "reauth_confirm": { + "data": { + "pin": "Pin", + "region": "Symbol", + "token": "Token" + }, + "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." + }, "select_saved_credentials": { "data": { "credentials": "P\u00e5logging" diff --git a/homeassistant/components/vulcan/translations/pl.json b/homeassistant/components/vulcan/translations/pl.json index acbc51c6754..220b05b6972 100644 --- a/homeassistant/components/vulcan/translations/pl.json +++ b/homeassistant/components/vulcan/translations/pl.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Wszyscy uczniowie zostali ju\u017c dodani.", "already_configured": "Ten ucze\u0144 zosta\u0142 ju\u017c dodany.", + "no_matching_entries": "Nie znaleziono pasuj\u0105cych wpis\u00f3w. U\u017cyj innego konta lub usu\u0144 integracj\u0119 z nieaktualnym uczniem.", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." }, + "reauth_confirm": { + "data": { + "pin": "Kod PIN", + "region": "Symbol", + "token": "Token" + }, + "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." + }, "select_saved_credentials": { "data": { "credentials": "Login" diff --git a/homeassistant/components/vulcan/translations/ru.json b/homeassistant/components/vulcan/translations/ru.json index 88e64d9ca79..5ce94bd932f 100644 --- a/homeassistant/components/vulcan/translations/ru.json +++ b/homeassistant/components/vulcan/translations/ru.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "\u0412\u0441\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u044b \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b.", "already_configured": "\u042d\u0442\u043e\u0442 \u0441\u0442\u0443\u0434\u0435\u043d\u0442 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d.", + "no_matching_entries": "\u041f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0434\u0440\u0443\u0433\u0443\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { @@ -37,6 +38,14 @@ }, "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." }, + "reauth_confirm": { + "data": { + "pin": "PIN-\u043a\u043e\u0434", + "region": "\u0421\u0438\u043c\u0432\u043e\u043b", + "token": "\u0422\u043e\u043a\u0435\u043d" + }, + "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." + }, "select_saved_credentials": { "data": { "credentials": "\u041b\u043e\u0433\u0438\u043d" diff --git a/homeassistant/components/vulcan/translations/zh-Hant.json b/homeassistant/components/vulcan/translations/zh-Hant.json index 3a1c06ce5ad..b26ef1f5cc5 100644 --- a/homeassistant/components/vulcan/translations/zh-Hant.json +++ b/homeassistant/components/vulcan/translations/zh-Hant.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "\u6240\u6709\u5b78\u751f\u90fd\u5df2\u7d93\u65b0\u589e\u3002", "already_configured": "\u8a72\u5b78\u751f\u5df2\u7d93\u65b0\u589e\u3002", + "no_matching_entries": "\u627e\u4e0d\u5230\u76f8\u7b26\u7684\u5be6\u9ad4\uff0c\u8acb\u4f7f\u7528\u5176\u4ed6\u5e33\u865f\u6216\u79fb\u9664\u5305\u542b\u904e\u671f\u5b78\u751f\u4e4b\u6574\u5408..", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { @@ -37,6 +38,14 @@ }, "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" }, + "reauth_confirm": { + "data": { + "pin": "Pin", + "region": "\u7b26\u865f", + "token": "\u6b0a\u6756" + }, + "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" + }, "select_saved_credentials": { "data": { "credentials": "\u767b\u5165" From b86f508ac872d832dc79cb4df53ffc493a381e95 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 May 2022 08:42:49 -0500 Subject: [PATCH 253/930] Use DataUpdateCoordinator generic for data (#71479) --- homeassistant/components/history_stats/coordinator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/history_stats/coordinator.py b/homeassistant/components/history_stats/coordinator.py index 2a2cc392bb5..7d44da9f5f6 100644 --- a/homeassistant/components/history_stats/coordinator.py +++ b/homeassistant/components/history_stats/coordinator.py @@ -19,11 +19,9 @@ _LOGGER = logging.getLogger(__name__) UPDATE_INTERVAL = timedelta(minutes=1) -class HistoryStatsUpdateCoordinator(DataUpdateCoordinator): +class HistoryStatsUpdateCoordinator(DataUpdateCoordinator[HistoryStatsState]): """DataUpdateCoordinator to gather data for a specific TPLink device.""" - data: HistoryStatsState - def __init__( self, hass: HomeAssistant, From 6866cca929b733a35b4884e4ce156a2e88565093 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 7 May 2022 22:08:20 +0200 Subject: [PATCH 254/930] Add timeout (#71499) --- homeassistant/components/brother/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index ce715e991b0..96e2ad069ce 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import timedelta import logging +import async_timeout from brother import Brother, DictToObj, SnmpError, UnsupportedModel import pysnmp.hlapi.asyncio as SnmpEngine @@ -76,7 +77,8 @@ class BrotherDataUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> DictToObj: """Update data via library.""" try: - data = await self.brother.async_update() + async with async_timeout.timeout(20): + data = await self.brother.async_update() except (ConnectionError, SnmpError, UnsupportedModel) as error: raise UpdateFailed(error) from error return data From 21cee3b1c4a844d721b3d3cb3a645de62a822f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 7 May 2022 22:13:12 +0200 Subject: [PATCH 255/930] airzone: improve diagnostics (#71488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Fernández Rojas --- .../components/airzone/diagnostics.py | 18 +++- tests/components/airzone/test_diagnostics.py | 82 +++++++++++++------ 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/airzone/diagnostics.py b/homeassistant/components/airzone/diagnostics.py index 8baa106beb1..f56a5106b25 100644 --- a/homeassistant/components/airzone/diagnostics.py +++ b/homeassistant/components/airzone/diagnostics.py @@ -3,16 +3,25 @@ from __future__ import annotations from typing import Any -from aioairzone.const import AZD_MAC +from aioairzone.const import API_MAC, AZD_MAC from homeassistant.components.diagnostics.util import async_redact_data from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_UNIQUE_ID from homeassistant.core import HomeAssistant from .const import DOMAIN from .coordinator import AirzoneUpdateCoordinator -TO_REDACT = [ +TO_REDACT_API = [ + API_MAC, +] + +TO_REDACT_CONFIG = [ + CONF_UNIQUE_ID, +] + +TO_REDACT_COORD = [ AZD_MAC, ] @@ -24,6 +33,7 @@ async def async_get_config_entry_diagnostics( coordinator: AirzoneUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] return { - "info": async_redact_data(config_entry.data, TO_REDACT), - "data": async_redact_data(coordinator.data, TO_REDACT), + "api_data": async_redact_data(coordinator.airzone.raw_data(), TO_REDACT_API), + "config_entry": async_redact_data(config_entry.as_dict(), TO_REDACT_CONFIG), + "coord_data": async_redact_data(coordinator.data, TO_REDACT_COORD), } diff --git a/tests/components/airzone/test_diagnostics.py b/tests/components/airzone/test_diagnostics.py index 7425677876f..4f7e2f61a48 100644 --- a/tests/components/airzone/test_diagnostics.py +++ b/tests/components/airzone/test_diagnostics.py @@ -1,20 +1,30 @@ """The diagnostics tests for the Airzone platform.""" +from unittest.mock import patch + from aioairzone.const import ( + API_DATA, + API_MAC, + API_SYSTEM_ID, + API_SYSTEMS, + API_WIFI_RSSI, AZD_ID, AZD_MASTER, AZD_SYSTEM, AZD_SYSTEMS, AZD_ZONES, AZD_ZONES_NUM, + RAW_HVAC, + RAW_WEBSERVER, ) from aiohttp import ClientSession from homeassistant.components.airzone.const import DOMAIN +from homeassistant.components.diagnostics.const import REDACTED from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant -from .util import CONFIG, async_init_integration +from .util import CONFIG, HVAC_MOCK, HVAC_WEBSERVER_MOCK, async_init_integration from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -27,31 +37,55 @@ async def test_config_entry_diagnostics( assert hass.data[DOMAIN] config_entry = hass.config_entries.async_entries(DOMAIN)[0] + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.raw_data", + return_value={ + RAW_HVAC: HVAC_MOCK, + RAW_WEBSERVER: HVAC_WEBSERVER_MOCK, + }, + ): + diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) - diag = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + assert ( + diag["api_data"][RAW_HVAC][API_SYSTEMS][0][API_DATA][0].items() + >= { + API_SYSTEM_ID: HVAC_MOCK[API_SYSTEMS][0][API_DATA][0][API_SYSTEM_ID], + }.items() + ) - assert diag["info"][CONF_HOST] == CONFIG[CONF_HOST] - assert diag["info"][CONF_PORT] == CONFIG[CONF_PORT] + assert ( + diag["api_data"][RAW_WEBSERVER].items() + >= { + API_MAC: REDACTED, + API_WIFI_RSSI: HVAC_WEBSERVER_MOCK[API_WIFI_RSSI], + }.items() + ) - assert diag["data"][AZD_SYSTEMS]["1"][AZD_ID] == 1 - assert diag["data"][AZD_SYSTEMS]["1"][AZD_ZONES_NUM] == 5 + assert ( + diag["config_entry"].items() + >= { + "data": { + CONF_HOST: CONFIG[CONF_HOST], + CONF_PORT: CONFIG[CONF_PORT], + }, + "domain": DOMAIN, + "unique_id": REDACTED, + }.items() + ) - assert diag["data"][AZD_ZONES]["1:1"][AZD_ID] == 1 - assert diag["data"][AZD_ZONES]["1:1"][AZD_MASTER] == 1 - assert diag["data"][AZD_ZONES]["1:1"][AZD_SYSTEM] == 1 + assert ( + diag["coord_data"][AZD_SYSTEMS]["1"].items() + >= { + AZD_ID: 1, + AZD_ZONES_NUM: 5, + }.items() + ) - assert diag["data"][AZD_ZONES]["1:2"][AZD_ID] == 2 - assert diag["data"][AZD_ZONES]["1:2"][AZD_MASTER] == 0 - assert diag["data"][AZD_ZONES]["1:2"][AZD_SYSTEM] == 1 - - assert diag["data"][AZD_ZONES]["1:3"][AZD_ID] == 3 - assert diag["data"][AZD_ZONES]["1:3"][AZD_MASTER] == 0 - assert diag["data"][AZD_ZONES]["1:3"][AZD_SYSTEM] == 1 - - assert diag["data"][AZD_ZONES]["1:4"][AZD_ID] == 4 - assert diag["data"][AZD_ZONES]["1:4"][AZD_MASTER] == 0 - assert diag["data"][AZD_ZONES]["1:4"][AZD_SYSTEM] == 1 - - assert diag["data"][AZD_ZONES]["1:5"][AZD_ID] == 5 - assert diag["data"][AZD_ZONES]["1:5"][AZD_MASTER] == 0 - assert diag["data"][AZD_ZONES]["1:5"][AZD_SYSTEM] == 1 + assert ( + diag["coord_data"][AZD_ZONES]["1:1"].items() + >= { + AZD_ID: 1, + AZD_MASTER: True, + AZD_SYSTEM: 1, + }.items() + ) From 50f4c5d347a8d6a95f4fa36d1c95228a56ceefe5 Mon Sep 17 00:00:00 2001 From: Shai Ungar Date: Sat, 7 May 2022 23:16:51 +0300 Subject: [PATCH 256/930] fix speed sensor wrong number (#71502) --- homeassistant/components/sabnzbd/sensor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index dee80945e9d..539eaa4f097 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -32,13 +32,15 @@ class SabnzbdSensorEntityDescription(SensorEntityDescription, SabnzbdRequiredKey """Describes Sabnzbd sensor entity.""" +SPEED_KEY = "kbpersec" + SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = ( SabnzbdSensorEntityDescription( key="status", name="Status", ), SabnzbdSensorEntityDescription( - key="kbpersec", + key=SPEED_KEY, name="Speed", native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, @@ -154,7 +156,7 @@ class SabnzbdSensor(SensorEntity): self.entity_description.key ) - if self.entity_description.key == "speed": + if self.entity_description.key == SPEED_KEY: self._attr_native_value = round(float(self._attr_native_value) / 1024, 1) elif "size" in self.entity_description.key: self._attr_native_value = round(float(self._attr_native_value), 2) From 6442c5949b132595bdfc02dcba4fd530d093e795 Mon Sep 17 00:00:00 2001 From: rappenze Date: Sat, 7 May 2022 22:20:30 +0200 Subject: [PATCH 257/930] Revert usage of Fibaro Client V5 as it has too many errors (#71477) --- homeassistant/components/fibaro/__init__.py | 23 +++++++-------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index d5a8f94970d..c9b0cb345e1 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -10,10 +10,7 @@ from fiblary3.client.v4.client import ( Client as FibaroClientV4, StateHandler as StateHandlerV4, ) -from fiblary3.client.v5.client import ( - Client as FibaroClientV5, - StateHandler as StateHandlerV5, -) +from fiblary3.client.v5.client import StateHandler as StateHandlerV5 from fiblary3.common.exceptions import HTTPException import voluptuous as vol @@ -141,18 +138,12 @@ class FibaroController: should do that only when you use the FibaroController for login test as only the login and info API's are equal throughout the different versions. """ - if ( - serial_number is None - or serial_number.upper().startswith("HC2") - or serial_number.upper().startswith("HCL") - ): - self._client = FibaroClientV4( - config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD] - ) - else: - self._client = FibaroClientV5( - config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD] - ) + + # Only use V4 API as it works better even for HC3, after the library is fixed, we should + # add here support for the newer library version V5 again. + self._client = FibaroClientV4( + config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD] + ) self._scene_map = None # Whether to import devices from plugins From fe7564813afdc0538348e9a47065adf4e7249abc Mon Sep 17 00:00:00 2001 From: rappenze Date: Sat, 7 May 2022 22:22:41 +0200 Subject: [PATCH 258/930] Fix rgb conversion in fibaro light (#71476) --- homeassistant/components/fibaro/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index ad41b2aaed6..9d0309bc4ee 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -185,6 +185,6 @@ class FibaroLight(FibaroDevice, LightEntity): rgbw_list = [int(i) for i in rgbw_s.split(",")][:4] if self._attr_color_mode == ColorMode.RGB: - self._attr_rgb_color = tuple(*rgbw_list[:3]) + self._attr_rgb_color = tuple(rgbw_list[:3]) else: self._attr_rgbw_color = tuple(rgbw_list) From b8c76a416b7c570c514a299e7ef39e40b04899d1 Mon Sep 17 00:00:00 2001 From: 0bmay <57501269+0bmay@users.noreply.github.com> Date: Sat, 7 May 2022 13:28:05 -0700 Subject: [PATCH 259/930] Update py-canary to 0.5.2 (#71489) Update py-canary from 0.5.1 to 0.5.2 Github issue #71052 Github Issue #44830 --- homeassistant/components/canary/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/canary/manifest.json b/homeassistant/components/canary/manifest.json index 12b4d54b391..fdae5c83d7b 100644 --- a/homeassistant/components/canary/manifest.json +++ b/homeassistant/components/canary/manifest.json @@ -2,7 +2,7 @@ "domain": "canary", "name": "Canary", "documentation": "https://www.home-assistant.io/integrations/canary", - "requirements": ["py-canary==0.5.1"], + "requirements": ["py-canary==0.5.2"], "dependencies": ["ffmpeg"], "codeowners": [], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 48f71c37f93..a32812c3fd4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1290,7 +1290,7 @@ pushover_complete==1.1.1 pvo==0.2.2 # homeassistant.components.canary -py-canary==0.5.1 +py-canary==0.5.2 # homeassistant.components.cpuspeed py-cpuinfo==8.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8adb336e488..ab43ebd64c8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -868,7 +868,7 @@ pushbullet.py==0.11.0 pvo==0.2.2 # homeassistant.components.canary -py-canary==0.5.1 +py-canary==0.5.2 # homeassistant.components.cpuspeed py-cpuinfo==8.0.0 From a01444b6dd0e4fb711da2bc32a8bf71b883183fc Mon Sep 17 00:00:00 2001 From: Austin Mroczek Date: Sat, 7 May 2022 13:30:36 -0700 Subject: [PATCH 260/930] bump total_connect_client to 2022.5 (#71493) --- homeassistant/components/totalconnect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/totalconnect/manifest.json b/homeassistant/components/totalconnect/manifest.json index bc62a5b17af..461bff0cfd0 100644 --- a/homeassistant/components/totalconnect/manifest.json +++ b/homeassistant/components/totalconnect/manifest.json @@ -2,7 +2,7 @@ "domain": "totalconnect", "name": "Total Connect", "documentation": "https://www.home-assistant.io/integrations/totalconnect", - "requirements": ["total_connect_client==2022.3"], + "requirements": ["total_connect_client==2022.5"], "dependencies": [], "codeowners": ["@austinmroczek"], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index a32812c3fd4..dd253665613 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2319,7 +2319,7 @@ tololib==0.1.0b3 toonapi==0.2.1 # homeassistant.components.totalconnect -total_connect_client==2022.3 +total_connect_client==2022.5 # homeassistant.components.tplink_lte tp-connected==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ab43ebd64c8..510f001e729 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1504,7 +1504,7 @@ tololib==0.1.0b3 toonapi==0.2.1 # homeassistant.components.totalconnect -total_connect_client==2022.3 +total_connect_client==2022.5 # homeassistant.components.transmission transmissionrpc==0.11 From 523828c81e98e81cd2f9c2b10de7b80cfa1a4086 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 May 2022 16:18:40 -0500 Subject: [PATCH 261/930] Ensure sql sensors keep working after using the options flow (#71453) * Ensure sql sensors keep working after using the options flow Fixes ``` 2022-05-06 16:17:57 ERROR (MainThread) [homeassistant.components.sensor] Error while setting up sql platform for sensor Traceback (most recent call last): File "/Users/bdraco/home-assistant/homeassistant/helpers/entity_platform.py", line 249, in _async_setup_platform await asyncio.shield(task) File "/Users/bdraco/home-assistant/homeassistant/components/sql/sensor.py", line 97, in async_setup_entry name: str = entry.options[CONF_NAME] KeyError: name ``` * ensure saving the options flow fixes the broken config entry * ensure options changes take effect right away * Add cover to validate the reload --- homeassistant/components/sql/__init__.py | 6 +++ homeassistant/components/sql/config_flow.py | 9 +++- tests/components/sql/test_config_flow.py | 55 +++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sql/__init__.py b/homeassistant/components/sql/__init__.py index 3c83b01b284..2917056b5d4 100644 --- a/homeassistant/components/sql/__init__.py +++ b/homeassistant/components/sql/__init__.py @@ -7,8 +7,14 @@ from homeassistant.core import HomeAssistant from .const import PLATFORMS +async def async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Update listener for options.""" + await hass.config_entries.async_reload(entry.entry_id) + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up SQL from a config entry.""" + entry.async_on_unload(entry.add_update_listener(async_update_listener)) hass.config_entries.async_setup_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index 9150cb8f63d..07ff610087b 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -165,7 +165,14 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow): except ValueError: errors["query"] = "query_invalid" else: - return self.async_create_entry(title="", data=user_input) + return self.async_create_entry( + title="", + data={ + CONF_NAME: self.entry.title, + **self.entry.options, + **user_input, + }, + ) return self.async_show_form( step_id="init", diff --git a/tests/components/sql/test_config_flow.py b/tests/components/sql/test_config_flow.py index ec851b491b7..3e065df0ebd 100644 --- a/tests/components/sql/test_config_flow.py +++ b/tests/components/sql/test_config_flow.py @@ -214,9 +214,62 @@ async def test_options_flow(hass: HomeAssistant) -> None: assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["data"] == { + "name": "Get Value", "db_url": "sqlite://", "query": "SELECT 5 as size", "column": "size", + "value_template": None, + "unit_of_measurement": "MiB", + } + + +async def test_options_flow_name_previously_removed(hass: HomeAssistant) -> None: + """Test options config flow where the name was missing.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={}, + options={ + "db_url": "sqlite://", + "query": "SELECT 5 as value", + "column": "value", + "unit_of_measurement": "MiB", + "value_template": None, + }, + title="Get Value Title", + ) + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(entry.entry_id) + + assert result["type"] == RESULT_TYPE_FORM + assert result["step_id"] == "init" + + with patch( + "homeassistant.components.sql.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "db_url": "sqlite://", + "query": "SELECT 5 as size", + "column": "size", + "unit_of_measurement": "MiB", + }, + ) + await hass.async_block_till_done() + + assert len(mock_setup_entry.mock_calls) == 1 + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["data"] == { + "name": "Get Value Title", + "db_url": "sqlite://", + "query": "SELECT 5 as size", + "column": "size", + "value_template": None, "unit_of_measurement": "MiB", } @@ -312,6 +365,8 @@ async def test_options_flow_fails_invalid_query( assert result4["type"] == RESULT_TYPE_CREATE_ENTRY assert result4["data"] == { + "name": "Get Value", + "value_template": None, "db_url": "sqlite://", "query": "SELECT 5 as size", "column": "size", From 3883bad70a4d45fd800792d56a77e689741cb0fb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 May 2022 16:19:01 -0500 Subject: [PATCH 262/930] Fix display of multiline queries in sql config flow (#71450) --- homeassistant/components/sql/config_flow.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index 07ff610087b..ba9a5f7e4dd 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -26,7 +26,9 @@ DATA_SCHEMA = vol.Schema( vol.Required(CONF_NAME, default="Select SQL Query"): selector.TextSelector(), vol.Optional(CONF_DB_URL): selector.TextSelector(), vol.Required(CONF_COLUMN_NAME): selector.TextSelector(), - vol.Required(CONF_QUERY): selector.TextSelector(), + vol.Required(CONF_QUERY): selector.TextSelector( + selector.TextSelectorConfig(multiline=True) + ), vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(), vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector(), } @@ -187,7 +189,9 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow): vol.Required( CONF_QUERY, description={"suggested_value": self.entry.options[CONF_QUERY]}, - ): selector.TextSelector(), + ): selector.TextSelector( + selector.TextSelectorConfig(multiline=True) + ), vol.Required( CONF_COLUMN_NAME, description={ From 55cb35046ec3b2e9072d4bebfbd7259ab2938253 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 7 May 2022 14:19:23 -0700 Subject: [PATCH 263/930] Move flexit climate to HVAC action (#71443) --- homeassistant/components/flexit/climate.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/flexit/climate.py b/homeassistant/components/flexit/climate.py index c24acf78221..1c9f752c15b 100644 --- a/homeassistant/components/flexit/climate.py +++ b/homeassistant/components/flexit/climate.py @@ -6,7 +6,11 @@ import logging import voluptuous as vol from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity -from homeassistant.components.climate.const import ClimateEntityFeature, HVACMode +from homeassistant.components.climate.const import ( + ClimateEntityFeature, + HVACAction, + HVACMode, +) from homeassistant.components.modbus import get_hub from homeassistant.components.modbus.const import ( CALL_TYPE_REGISTER_HOLDING, @@ -69,9 +73,7 @@ class Flexit(ClimateEntity): self._target_temperature = None self._current_temperature = None self._current_fan_mode = None - self._current_operation = None self._fan_modes = ["Off", "Low", "Medium", "High"] - self._current_operation = None self._filter_hours = None self._filter_alarm = None self._heat_recovery = None @@ -124,15 +126,15 @@ class Flexit(ClimateEntity): ) if self._heating: - self._current_operation = "Heating" + self._attr_hvac_action = HVACAction.HEATING elif self._cooling: - self._current_operation = "Cooling" + self._attr_hvac_action = HVACAction.COOLING elif self._heat_recovery: - self._current_operation = "Recovering" + self._attr_hvac_action = HVACAction.IDLE elif actual_air_speed: - self._current_operation = "Fan Only" + self._attr_hvac_action = HVACAction.FAN else: - self._current_operation = "Off" + self._attr_hvac_action = HVACAction.OFF @property def extra_state_attributes(self): @@ -175,7 +177,7 @@ class Flexit(ClimateEntity): @property def hvac_mode(self): """Return current operation ie. heat, cool, idle.""" - return self._current_operation + return HVACMode.COOL @property def hvac_modes(self) -> list[str]: From 12065ad58ec710d79eac7eec1c8865d25b21b3e9 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 8 May 2022 00:21:41 +0000 Subject: [PATCH 264/930] [ci skip] Translation update --- .../components/isy994/translations/he.json | 7 +++++++ .../components/meater/translations/he.json | 5 +++++ .../components/sql/translations/he.json | 16 ++++++++++++++++ .../components/switch_as_x/translations/pl.json | 4 ++-- .../components/vulcan/translations/he.json | 12 ++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 homeassistant/components/vulcan/translations/he.json diff --git a/homeassistant/components/isy994/translations/he.json b/homeassistant/components/isy994/translations/he.json index b1874d2675d..e72724785cc 100644 --- a/homeassistant/components/isy994/translations/he.json +++ b/homeassistant/components/isy994/translations/he.json @@ -7,10 +7,17 @@ "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "invalid_host": "\u05e2\u05e8\u05da \u05d4\u05beHost \u05dc\u05d0 \u05d4\u05d9\u05d4 \u05d1\u05e4\u05d5\u05e8\u05de\u05d8 URL \u05de\u05dc\u05d0, \u05dc\u05de\u05e9\u05dc, http://192.168.10.100:80", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05dc\u05d0 \u05e6\u05e4\u05d5\u05d9\u05d9\u05d4" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + }, "user": { "data": { "host": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", diff --git a/homeassistant/components/meater/translations/he.json b/homeassistant/components/meater/translations/he.json index f1376b2cf0d..59aa348444f 100644 --- a/homeassistant/components/meater/translations/he.json +++ b/homeassistant/components/meater/translations/he.json @@ -5,6 +5,11 @@ "unknown_auth_error": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4" + } + }, "user": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4", diff --git a/homeassistant/components/sql/translations/he.json b/homeassistant/components/sql/translations/he.json index 937391f9327..9b9bda1ed3f 100644 --- a/homeassistant/components/sql/translations/he.json +++ b/homeassistant/components/sql/translations/he.json @@ -2,6 +2,22 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "step": { + "user": { + "data": { + "name": "\u05e9\u05dd" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u05e9\u05dd" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/pl.json b/homeassistant/components/switch_as_x/translations/pl.json index 7491ef0d7fc..53a81fec1a5 100644 --- a/homeassistant/components/switch_as_x/translations/pl.json +++ b/homeassistant/components/switch_as_x/translations/pl.json @@ -12,9 +12,9 @@ "target_domain": "Nowy rodzaj" }, "description": "Wybierz prze\u0142\u0105cznik, kt\u00f3ry chcesz pokaza\u0107 w Home Assistant jako \u015bwiat\u0142o, rolet\u0119 lub cokolwiek innego. Oryginalny prze\u0142\u0105cznik zostanie ukryty.", - "title": "Zmiana typu urz\u0105dzenia prze\u0142\u0105czaj\u0105cego" + "title": "Zmiana typu urz\u0105dzenia prze\u0142\u0105cznika" } } }, - "title": "Zmiana typu urz\u0105dzenia w prze\u0142\u0105czniku" + "title": "Zmiana typu urz\u0105dzenia prze\u0142\u0105cznika" } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/he.json b/homeassistant/components/vulcan/translations/he.json new file mode 100644 index 00000000000..605830b683e --- /dev/null +++ b/homeassistant/components/vulcan/translations/he.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "region": "\u05e1\u05de\u05dc", + "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df" + } + } + } + } +} \ No newline at end of file From 00291fb1a6bf0a8cfbd71acebbc01298cb337f2d Mon Sep 17 00:00:00 2001 From: screenagerbe <82099983+screenagerbe@users.noreply.github.com> Date: Sun, 8 May 2022 02:41:09 +0200 Subject: [PATCH 265/930] update to caldav v0.9.0 (#71406) --- homeassistant/components/caldav/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/caldav/manifest.json b/homeassistant/components/caldav/manifest.json index 91f563107ed..e6945effca4 100644 --- a/homeassistant/components/caldav/manifest.json +++ b/homeassistant/components/caldav/manifest.json @@ -2,7 +2,7 @@ "domain": "caldav", "name": "CalDAV", "documentation": "https://www.home-assistant.io/integrations/caldav", - "requirements": ["caldav==0.8.2"], + "requirements": ["caldav==0.9.0"], "codeowners": [], "iot_class": "cloud_polling", "loggers": ["caldav", "vobject"] diff --git a/requirements_all.txt b/requirements_all.txt index dd253665613..13bb140e1a5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -460,7 +460,7 @@ btsmarthub_devicelist==0.2.0 buienradar==1.0.5 # homeassistant.components.caldav -caldav==0.8.2 +caldav==0.9.0 # homeassistant.components.circuit circuit-webhook==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 510f001e729..792d6287e3e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -339,7 +339,7 @@ bsblan==0.5.0 buienradar==1.0.5 # homeassistant.components.caldav -caldav==0.8.2 +caldav==0.9.0 # homeassistant.components.co2signal co2signal==0.4.2 From e35a5a1a28bee57b2ea962759c40d9715318d49b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 7 May 2022 20:57:48 -0700 Subject: [PATCH 266/930] Fix other enums in helpers (#71505) --- homeassistant/helpers/selector.py | 8 ++++++-- tests/helpers/test_selector.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index 1ae0082c06f..87574949f4e 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -704,7 +704,9 @@ class SelectSelector(Selector): vol.Required("options"): vol.All(vol.Any([str], [select_option])), vol.Optional("multiple", default=False): cv.boolean, vol.Optional("custom_value", default=False): cv.boolean, - vol.Optional("mode"): vol.Coerce(SelectSelectorMode), + vol.Optional("mode"): vol.All( + vol.Coerce(SelectSelectorMode), lambda val: val.value + ), } ) @@ -827,7 +829,9 @@ class TextSelector(Selector): vol.Optional("suffix"): str, # The "type" controls the input field in the browser, the resulting # data can be any string so we don't validate it. - vol.Optional("type"): vol.Coerce(TextSelectorType), + vol.Optional("type"): vol.All( + vol.Coerce(TextSelectorType), lambda val: val.value + ), } ) diff --git a/tests/helpers/test_selector.py b/tests/helpers/test_selector.py index ed831026065..4cb924a520e 100644 --- a/tests/helpers/test_selector.py +++ b/tests/helpers/test_selector.py @@ -351,7 +351,7 @@ def test_object_selector_schema(schema, valid_selections, invalid_selections): ( ({}, ("abc123",), (None,)), ({"multiline": True}, (), ()), - ({"multiline": False}, (), ()), + ({"multiline": False, "type": "email"}, (), ()), ), ) def test_text_selector_schema(schema, valid_selections, invalid_selections): @@ -402,7 +402,7 @@ def test_text_selector_schema(schema, valid_selections, invalid_selections): (0, None, ["red"]), ), ( - {"options": [], "custom_value": True, "multiple": True}, + {"options": [], "custom_value": True, "multiple": True, "mode": "list"}, (["red"], ["green", "blue"], []), (0, None, "red"), ), From 49d13b9981a2b92ae09362411e3fcd9b87a235c6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 7 May 2022 20:57:57 -0700 Subject: [PATCH 267/930] Bump frontend to 20220504.1 (#71504) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index b51219c4f19..8c475d51abb 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220504.0"], + "requirements": ["home-assistant-frontend==20220504.1"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 5c3ef6712b0..129774d989a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220504.0 +home-assistant-frontend==20220504.1 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.1 diff --git a/requirements_all.txt b/requirements_all.txt index 13bb140e1a5..3c915905d18 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -819,7 +819,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220504.0 +home-assistant-frontend==20220504.1 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 792d6287e3e..9a16053f48f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -580,7 +580,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220504.0 +home-assistant-frontend==20220504.1 # homeassistant.components.home_connect homeconnect==0.7.0 From a8aa0e1cca486ce5f8baf8e09b8c9bd24c47cfa1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 7 May 2022 23:02:54 -0500 Subject: [PATCH 268/930] Add Estimated Database Size to the recorder system health (#71463) --- homeassistant/components/recorder/const.py | 9 +++ homeassistant/components/recorder/core.py | 20 ++++--- .../components/recorder/migration.py | 15 ++--- homeassistant/components/recorder/purge.py | 10 ++-- homeassistant/components/recorder/repack.py | 8 ++- .../components/recorder/statistics.py | 9 ++- .../components/recorder/strings.json | 3 +- .../components/recorder/system_health.py | 26 -------- .../recorder/system_health/__init__.py | 60 +++++++++++++++++++ .../recorder/system_health/mysql.py | 19 ++++++ .../recorder/system_health/postgresql.py | 15 +++++ .../recorder/system_health/sqlite.py | 17 ++++++ .../components/recorder/translations/en.json | 3 +- homeassistant/components/recorder/util.py | 12 ++-- tests/components/recorder/test_init.py | 4 +- tests/components/recorder/test_purge.py | 8 ++- .../components/recorder/test_system_health.py | 29 ++++++++- 17 files changed, 200 insertions(+), 67 deletions(-) delete mode 100644 homeassistant/components/recorder/system_health.py create mode 100644 homeassistant/components/recorder/system_health/__init__.py create mode 100644 homeassistant/components/recorder/system_health/mysql.py create mode 100644 homeassistant/components/recorder/system_health/postgresql.py create mode 100644 homeassistant/components/recorder/system_health/sqlite.py diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 5b623ee5e04..5d650ec83e2 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -4,6 +4,7 @@ from functools import partial import json from typing import Final +from homeassistant.backports.enum import StrEnum from homeassistant.const import ATTR_ATTRIBUTION, ATTR_RESTORED, ATTR_SUPPORTED_FEATURES from homeassistant.helpers.json import JSONEncoder @@ -37,3 +38,11 @@ KEEPALIVE_TIME = 30 EXCLUDE_ATTRIBUTES = f"{DOMAIN}_exclude_attributes_by_domain" + + +class SupportedDialect(StrEnum): + """Supported dialects.""" + + SQLITE = "sqlite" + MYSQL = "mysql" + POSTGRESQL = "postgresql" diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 49be0aac705..a008ae6767b 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Iterable +import contextlib from datetime import datetime, timedelta import logging import queue @@ -41,6 +42,7 @@ from .const import ( KEEPALIVE_TIME, MAX_QUEUE_BACKLOG, SQLITE_URL_PREFIX, + SupportedDialect, ) from .executor import DBInterruptibleThreadPoolExecutor from .models import ( @@ -193,6 +195,13 @@ class Recorder(threading.Thread): """Return the number of items in the recorder backlog.""" return self._queue.qsize() + @property + def dialect_name(self) -> SupportedDialect | None: + """Return the dialect the recorder uses.""" + with contextlib.suppress(ValueError): + return SupportedDialect(self.engine.dialect.name) if self.engine else None + return None + @property def _using_file_sqlite(self) -> bool: """Short version to check if we are using sqlite3 as a file.""" @@ -459,11 +468,6 @@ class Recorder(threading.Thread): """Schedule external statistics.""" self.queue_task(ExternalStatisticsTask(metadata, stats)) - @callback - def using_sqlite(self) -> bool: - """Return if recorder uses sqlite as the engine.""" - return bool(self.engine and self.engine.dialect.name == "sqlite") - @callback def _async_setup_periodic_tasks(self) -> None: """Prepare periodic tasks.""" @@ -473,7 +477,7 @@ class Recorder(threading.Thread): # If the db is using a socket connection, we need to keep alive # to prevent errors from unexpected disconnects - if not self.using_sqlite(): + if self.dialect_name != SupportedDialect.SQLITE: self._keep_alive_listener = async_track_time_interval( self.hass, self._async_keep_alive, timedelta(seconds=KEEPALIVE_TIME) ) @@ -939,7 +943,7 @@ class Recorder(threading.Thread): async def lock_database(self) -> bool: """Lock database so it can be backed up safely.""" - if not self.using_sqlite(): + if self.dialect_name != SupportedDialect.SQLITE: _LOGGER.debug( "Not a SQLite database or not connected, locking not necessary" ) @@ -968,7 +972,7 @@ class Recorder(threading.Thread): Returns true if database lock has been held throughout the process. """ - if not self.using_sqlite(): + if self.dialect_name != SupportedDialect.SQLITE: _LOGGER.debug( "Not a SQLite database or not connected, unlocking not necessary" ) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index b38bb89b5b9..5da31f18781 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -21,6 +21,7 @@ from sqlalchemy.sql.expression import true from homeassistant.core import HomeAssistant +from .const import SupportedDialect from .models import ( SCHEMA_VERSION, TABLE_STATES, @@ -263,7 +264,7 @@ def _modify_columns( columns_def: list[str], ) -> None: """Modify columns in a table.""" - if engine.dialect.name == "sqlite": + if engine.dialect.name == SupportedDialect.SQLITE: _LOGGER.debug( "Skipping to modify columns %s in table %s; " "Modifying column length in SQLite is unnecessary, " @@ -281,7 +282,7 @@ def _modify_columns( table_name, ) - if engine.dialect.name == "postgresql": + if engine.dialect.name == SupportedDialect.POSTGRESQL: columns_def = [ "ALTER {column} TYPE {type}".format( **dict(zip(["column", "type"], col_def.split(" ", 1))) @@ -408,7 +409,7 @@ def _apply_update( # noqa: C901 ) -> None: """Perform operations to bring schema up to date.""" dialect = engine.dialect.name - big_int = "INTEGER(20)" if dialect == "mysql" else "INTEGER" + big_int = "INTEGER(20)" if dialect == SupportedDialect.MYSQL else "INTEGER" if new_version == 1: _create_index(session_maker, "events", "ix_events_time_fired") @@ -487,11 +488,11 @@ def _apply_update( # noqa: C901 _create_index(session_maker, "states", "ix_states_old_state_id") _update_states_table_with_foreign_key_options(session_maker, engine) elif new_version == 12: - if engine.dialect.name == "mysql": + if engine.dialect.name == SupportedDialect.MYSQL: _modify_columns(session_maker, engine, "events", ["event_data LONGTEXT"]) _modify_columns(session_maker, engine, "states", ["attributes LONGTEXT"]) elif new_version == 13: - if engine.dialect.name == "mysql": + if engine.dialect.name == SupportedDialect.MYSQL: _modify_columns( session_maker, engine, @@ -545,7 +546,7 @@ def _apply_update( # noqa: C901 session.add(StatisticsRuns(start=get_start_time())) elif new_version == 20: # This changed the precision of statistics from float to double - if engine.dialect.name in ["mysql", "postgresql"]: + if engine.dialect.name in [SupportedDialect.MYSQL, SupportedDialect.POSTGRESQL]: _modify_columns( session_maker, engine, @@ -560,7 +561,7 @@ def _apply_update( # noqa: C901 ) elif new_version == 21: # Try to change the character set of the statistic_meta table - if engine.dialect.name == "mysql": + if engine.dialect.name == SupportedDialect.MYSQL: for table in ("events", "states", "statistics_meta"): _LOGGER.warning( "Updating character set and collation of table %s to utf8mb4. " diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index b2547d13e45..2c6211ca2cd 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -13,7 +13,7 @@ from sqlalchemy.sql.expression import distinct from homeassistant.const import EVENT_STATE_CHANGED -from .const import MAX_ROWS_TO_PURGE +from .const import MAX_ROWS_TO_PURGE, SupportedDialect from .models import ( EventData, Events, @@ -45,7 +45,7 @@ def purge_old_data( "Purging states and events before target %s", purge_before.isoformat(sep=" ", timespec="seconds"), ) - using_sqlite = instance.using_sqlite() + using_sqlite = instance.dialect_name == SupportedDialect.SQLITE with session_scope(session=instance.get_session()) as session: # Purge a max of MAX_ROWS_TO_PURGE, based on the oldest states or events record @@ -425,7 +425,7 @@ def _purge_old_recorder_runs( def _purge_filtered_data(instance: Recorder, session: Session) -> bool: """Remove filtered states and events that shouldn't be in the database.""" _LOGGER.debug("Cleanup filtered data") - using_sqlite = instance.using_sqlite() + using_sqlite = instance.dialect_name == SupportedDialect.SQLITE # Check if excluded entity_ids are in database excluded_entity_ids: list[str] = [ @@ -484,7 +484,7 @@ def _purge_filtered_events( instance: Recorder, session: Session, excluded_event_types: list[str] ) -> None: """Remove filtered events and linked states.""" - using_sqlite = instance.using_sqlite() + using_sqlite = instance.dialect_name == SupportedDialect.SQLITE event_ids, data_ids = zip( *( session.query(Events.event_id, Events.data_id) @@ -514,7 +514,7 @@ def _purge_filtered_events( @retryable_database_job("purge") def purge_entity_data(instance: Recorder, entity_filter: Callable[[str], bool]) -> bool: """Purge states and events of specified entities.""" - using_sqlite = instance.using_sqlite() + using_sqlite = instance.dialect_name == SupportedDialect.SQLITE with session_scope(session=instance.get_session()) as session: selected_entity_ids: list[str] = [ entity_id diff --git a/homeassistant/components/recorder/repack.py b/homeassistant/components/recorder/repack.py index c272f2827a0..c05b8390724 100644 --- a/homeassistant/components/recorder/repack.py +++ b/homeassistant/components/recorder/repack.py @@ -6,6 +6,8 @@ from typing import TYPE_CHECKING from sqlalchemy import text +from .const import SupportedDialect + if TYPE_CHECKING: from . import Recorder @@ -18,7 +20,7 @@ def repack_database(instance: Recorder) -> None: dialect_name = instance.engine.dialect.name # Execute sqlite command to free up space on disk - if dialect_name == "sqlite": + if dialect_name == SupportedDialect.SQLITE: _LOGGER.debug("Vacuuming SQL DB to free space") with instance.engine.connect() as conn: conn.execute(text("VACUUM")) @@ -26,7 +28,7 @@ def repack_database(instance: Recorder) -> None: return # Execute postgresql vacuum command to free up space on disk - if dialect_name == "postgresql": + if dialect_name == SupportedDialect.POSTGRESQL: _LOGGER.debug("Vacuuming SQL DB to free space") with instance.engine.connect().execution_options( isolation_level="AUTOCOMMIT" @@ -36,7 +38,7 @@ def repack_database(instance: Recorder) -> None: return # Optimize mysql / mariadb tables to free up space on disk - if dialect_name == "mysql": + if dialect_name == SupportedDialect.MYSQL: _LOGGER.debug("Optimizing SQL DB to free space") with instance.engine.connect() as conn: conn.execute(text("OPTIMIZE TABLE states, events, recorder_runs")) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 9104fb7e234..d8ba415cc7a 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -38,7 +38,7 @@ import homeassistant.util.temperature as temperature_util from homeassistant.util.unit_system import UnitSystem import homeassistant.util.volume as volume_util -from .const import DATA_INSTANCE, DOMAIN, MAX_ROWS_TO_PURGE +from .const import DATA_INSTANCE, DOMAIN, MAX_ROWS_TO_PURGE, SupportedDialect from .models import ( StatisticData, StatisticMetaData, @@ -1342,10 +1342,13 @@ def _filter_unique_constraint_integrity_error( dialect_name = instance.engine.dialect.name ignore = False - if dialect_name == "sqlite" and "UNIQUE constraint failed" in str(err): + if ( + dialect_name == SupportedDialect.SQLITE + and "UNIQUE constraint failed" in str(err) + ): ignore = True if ( - dialect_name == "postgresql" + dialect_name == SupportedDialect.POSTGRESQL and hasattr(err.orig, "pgcode") and err.orig.pgcode == "23505" ): diff --git a/homeassistant/components/recorder/strings.json b/homeassistant/components/recorder/strings.json index 72fcf322c31..b475b29a16a 100644 --- a/homeassistant/components/recorder/strings.json +++ b/homeassistant/components/recorder/strings.json @@ -2,7 +2,8 @@ "system_health": { "info": { "oldest_recorder_run": "Oldest Run Start Time", - "current_recorder_run": "Current Run Start Time" + "current_recorder_run": "Current Run Start Time", + "estimated_db_size": "Estimated Database Size (MiB)" } } } diff --git a/homeassistant/components/recorder/system_health.py b/homeassistant/components/recorder/system_health.py deleted file mode 100644 index 2a9c536a2d6..00000000000 --- a/homeassistant/components/recorder/system_health.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Provide info to system health.""" - -from typing import Any - -from homeassistant.components import system_health -from homeassistant.core import HomeAssistant, callback - -from . import get_instance - - -@callback -def async_register( - hass: HomeAssistant, register: system_health.SystemHealthRegistration -) -> None: - """Register system health callbacks.""" - register.async_register_info(system_health_info) - - -async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: - """Get info for the info page.""" - instance = get_instance(hass) - run_history = instance.run_history - return { - "oldest_recorder_run": run_history.first.start, - "current_recorder_run": run_history.current.start, - } diff --git a/homeassistant/components/recorder/system_health/__init__.py b/homeassistant/components/recorder/system_health/__init__.py new file mode 100644 index 00000000000..3250aecf356 --- /dev/null +++ b/homeassistant/components/recorder/system_health/__init__.py @@ -0,0 +1,60 @@ +"""Provide info to system health.""" +from __future__ import annotations + +from typing import Any + +from yarl import URL + +from homeassistant.components import system_health +from homeassistant.components.recorder.core import Recorder +from homeassistant.components.recorder.util import session_scope +from homeassistant.core import HomeAssistant, callback + +from .. import get_instance +from ..const import SupportedDialect +from .mysql import db_size_bytes as mysql_db_size_bytes +from .postgresql import db_size_bytes as postgresql_db_size_bytes +from .sqlite import db_size_bytes as sqlite_db_size_bytes + +DIALECT_TO_GET_SIZE = { + SupportedDialect.SQLITE: sqlite_db_size_bytes, + SupportedDialect.MYSQL: mysql_db_size_bytes, + SupportedDialect.POSTGRESQL: postgresql_db_size_bytes, +} + + +@callback +def async_register( + hass: HomeAssistant, register: system_health.SystemHealthRegistration +) -> None: + """Register system health callbacks.""" + register.async_register_info(system_health_info) + + +def _get_db_stats(instance: Recorder, database_name: str) -> dict[str, Any]: + """Get the stats about the database.""" + db_stats: dict[str, Any] = {} + with session_scope(session=instance.get_session()) as session: + if ( + (dialect_name := instance.dialect_name) + and (get_size := DIALECT_TO_GET_SIZE.get(dialect_name)) + and (db_bytes := get_size(session, database_name)) + ): + db_stats["estimated_db_size"] = f"{db_bytes/1024/1024:.2f} MiB" + return db_stats + + +async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: + """Get info for the info page.""" + instance = get_instance(hass) + run_history = instance.run_history + database_name = URL(instance.db_url).path.lstrip("/") + db_stats: dict[str, Any] = {} + if instance.async_db_ready.done(): + db_stats = await instance.async_add_executor_job( + _get_db_stats, instance, database_name + ) + return { + "oldest_recorder_run": run_history.first.start, + "current_recorder_run": run_history.current.start, + } | db_stats diff --git a/homeassistant/components/recorder/system_health/mysql.py b/homeassistant/components/recorder/system_health/mysql.py new file mode 100644 index 00000000000..52ea06f61c3 --- /dev/null +++ b/homeassistant/components/recorder/system_health/mysql.py @@ -0,0 +1,19 @@ +"""Provide info to system health for mysql.""" +from __future__ import annotations + +from sqlalchemy import text +from sqlalchemy.orm.session import Session + + +def db_size_bytes(session: Session, database_name: str) -> float: + """Get the mysql database size.""" + return float( + session.execute( + text( + "SELECT ROUND(SUM(DATA_LENGTH + INDEX_LENGTH), 2) " + "FROM information_schema.TABLES WHERE " + "TABLE_SCHEMA=:database_name" + ), + {"database_name": database_name}, + ).first()[0] + ) diff --git a/homeassistant/components/recorder/system_health/postgresql.py b/homeassistant/components/recorder/system_health/postgresql.py new file mode 100644 index 00000000000..3e0667b1f4f --- /dev/null +++ b/homeassistant/components/recorder/system_health/postgresql.py @@ -0,0 +1,15 @@ +"""Provide info to system health for postgresql.""" +from __future__ import annotations + +from sqlalchemy import text +from sqlalchemy.orm.session import Session + + +def db_size_bytes(session: Session, database_name: str) -> float: + """Get the mysql database size.""" + return float( + session.execute( + text("select pg_database_size(:database_name);"), + {"database_name": database_name}, + ).first()[0] + ) diff --git a/homeassistant/components/recorder/system_health/sqlite.py b/homeassistant/components/recorder/system_health/sqlite.py new file mode 100644 index 00000000000..5a5901d2cb3 --- /dev/null +++ b/homeassistant/components/recorder/system_health/sqlite.py @@ -0,0 +1,17 @@ +"""Provide info to system health for sqlite.""" +from __future__ import annotations + +from sqlalchemy import text +from sqlalchemy.orm.session import Session + + +def db_size_bytes(session: Session, database_name: str) -> float: + """Get the mysql database size.""" + return float( + session.execute( + text( + "SELECT page_count * page_size as size " + "FROM pragma_page_count(), pragma_page_size();" + ) + ).first()[0] + ) diff --git a/homeassistant/components/recorder/translations/en.json b/homeassistant/components/recorder/translations/en.json index a44ecd3c1d6..32d78085999 100644 --- a/homeassistant/components/recorder/translations/en.json +++ b/homeassistant/components/recorder/translations/en.json @@ -2,7 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Current Run Start Time", - "oldest_recorder_run": "Oldest Run Start Time" + "oldest_recorder_run": "Oldest Run Start Time", + "estimated_db_size": "Estimated Database Size" } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index b63bbe740bc..3d9fa7d29e1 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -25,7 +25,7 @@ from typing_extensions import Concatenate, ParamSpec from homeassistant.core import HomeAssistant import homeassistant.util.dt as dt_util -from .const import DATA_INSTANCE, SQLITE_URL_PREFIX +from .const import DATA_INSTANCE, SQLITE_URL_PREFIX, SupportedDialect from .models import ( ALL_TABLES, TABLE_RECORDER_RUNS, @@ -353,7 +353,7 @@ def setup_connection_for_dialect( # Returns False if the the connection needs to be setup # on the next connection, returns True if the connection # never needs to be setup again. - if dialect_name == "sqlite": + if dialect_name == SupportedDialect.SQLITE: if first_connection: old_isolation = dbapi_connection.isolation_level dbapi_connection.isolation_level = None @@ -381,7 +381,7 @@ def setup_connection_for_dialect( # enable support for foreign keys execute_on_connection(dbapi_connection, "PRAGMA foreign_keys=ON") - elif dialect_name == "mysql": + elif dialect_name == SupportedDialect.MYSQL: execute_on_connection(dbapi_connection, "SET session wait_timeout=28800") if first_connection: result = query_on_connection(dbapi_connection, "SELECT VERSION()") @@ -408,7 +408,7 @@ def setup_connection_for_dialect( version or version_string, "MySQL", MIN_VERSION_MYSQL ) - elif dialect_name == "postgresql": + elif dialect_name == SupportedDialect.POSTGRESQL: if first_connection: # server_version_num was added in 2006 result = query_on_connection(dbapi_connection, "SHOW server_version") @@ -455,7 +455,7 @@ def retryable_database_job( except OperationalError as err: assert instance.engine is not None if ( - instance.engine.dialect.name == "mysql" + instance.engine.dialect.name == SupportedDialect.MYSQL and err.orig.args[0] in RETRYABLE_MYSQL_ERRORS ): _LOGGER.info( @@ -481,7 +481,7 @@ def periodic_db_cleanups(instance: Recorder) -> None: These cleanups will happen nightly or after any purge. """ assert instance.engine is not None - if instance.engine.dialect.name == "sqlite": + if instance.engine.dialect.name == SupportedDialect.SQLITE: # Execute sqlite to create a wal checkpoint and free up disk space _LOGGER.debug("WAL checkpoint") with instance.engine.connect() as connection: diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 17287151bc1..0b2b7b2dcb8 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -1440,9 +1440,7 @@ async def test_database_connection_keep_alive( caplog: pytest.LogCaptureFixture, ): """Test we keep alive socket based dialects.""" - with patch( - "homeassistant.components.recorder.Recorder.using_sqlite", return_value=False - ): + with patch("homeassistant.components.recorder.Recorder.dialect_name"): instance = await async_setup_recorder_instance(hass) # We have to mock this since we don't have a mock # MySQL server available in tests. diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 2d711f17cf3..de9db9d5014 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -9,7 +9,7 @@ from sqlalchemy.exc import DatabaseError, OperationalError from sqlalchemy.orm.session import Session from homeassistant.components import recorder -from homeassistant.components.recorder.const import MAX_ROWS_TO_PURGE +from homeassistant.components.recorder.const import MAX_ROWS_TO_PURGE, SupportedDialect from homeassistant.components.recorder.models import ( Events, RecorderRuns, @@ -43,8 +43,10 @@ from tests.common import SetupRecorderInstanceT def mock_use_sqlite(request): """Pytest fixture to switch purge method.""" with patch( - "homeassistant.components.recorder.Recorder.using_sqlite", - return_value=request.param, + "homeassistant.components.recorder.core.Recorder.dialect_name", + return_value=SupportedDialect.SQLITE + if request.param + else SupportedDialect.MYSQL, ): yield diff --git a/tests/components/recorder/test_system_health.py b/tests/components/recorder/test_system_health.py index 75abc2b6ae1..8a8d9ea4e80 100644 --- a/tests/components/recorder/test_system_health.py +++ b/tests/components/recorder/test_system_health.py @@ -1,8 +1,11 @@ """Test recorder system health.""" -from unittest.mock import patch +from unittest.mock import ANY, Mock, patch + +import pytest from homeassistant.components.recorder import get_instance +from homeassistant.components.recorder.const import SupportedDialect from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component @@ -20,6 +23,29 @@ async def test_recorder_system_health(hass, recorder_mock): assert info == { "current_recorder_run": instance.run_history.current.start, "oldest_recorder_run": instance.run_history.first.start, + "estimated_db_size": ANY, + } + + +@pytest.mark.parametrize( + "dialect_name", [SupportedDialect.MYSQL, SupportedDialect.POSTGRESQL] +) +async def test_recorder_system_health_alternate_dbms(hass, recorder_mock, dialect_name): + """Test recorder system health.""" + assert await async_setup_component(hass, "system_health", {}) + await async_wait_recording_done(hass) + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", dialect_name + ), patch( + "sqlalchemy.orm.session.Session.execute", + return_value=Mock(first=Mock(return_value=("1048576",))), + ): + info = await get_system_health_info(hass, "recorder") + instance = get_instance(hass) + assert info == { + "current_recorder_run": instance.run_history.current.start, + "oldest_recorder_run": instance.run_history.first.start, + "estimated_db_size": "1.00 MiB", } @@ -35,4 +61,5 @@ async def test_recorder_system_health_crashed_recorder_runs_table( assert info == { "current_recorder_run": instance.run_history.current.start, "oldest_recorder_run": instance.run_history.current.start, + "estimated_db_size": ANY, } From d3e2d2eb40d09c816ba2f5438c08658d74bfa476 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 8 May 2022 11:31:03 +0200 Subject: [PATCH 269/930] Remove myself from shiftr codeowners (#71517) --- CODEOWNERS | 1 - homeassistant/components/shiftr/manifest.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index b8d737cdfb7..e1f9e82551e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -899,7 +899,6 @@ build.json @home-assistant/supervisor /tests/components/shell_command/ @home-assistant/core /homeassistant/components/shelly/ @balloob @bieniu @thecode @chemelli74 /tests/components/shelly/ @balloob @bieniu @thecode @chemelli74 -/homeassistant/components/shiftr/ @fabaff /homeassistant/components/shodan/ @fabaff /homeassistant/components/sia/ @eavanvalkenburg /tests/components/sia/ @eavanvalkenburg diff --git a/homeassistant/components/shiftr/manifest.json b/homeassistant/components/shiftr/manifest.json index e3d27b6b4fc..932571a977e 100644 --- a/homeassistant/components/shiftr/manifest.json +++ b/homeassistant/components/shiftr/manifest.json @@ -3,7 +3,7 @@ "name": "shiftr.io", "documentation": "https://www.home-assistant.io/integrations/shiftr", "requirements": ["paho-mqtt==1.6.1"], - "codeowners": ["@fabaff"], + "codeowners": [], "iot_class": "cloud_push", "loggers": ["paho"] } From 71e84dfcd1539d984d2bf31c85e0b2bc366502b3 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 8 May 2022 11:31:33 +0200 Subject: [PATCH 270/930] Remove myself from volkszaehler codeowners (#71515) --- CODEOWNERS | 1 - homeassistant/components/volkszaehler/manifest.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index e1f9e82551e..dcc98dd29cf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1122,7 +1122,6 @@ build.json @home-assistant/supervisor /tests/components/vizio/ @raman325 /homeassistant/components/vlc_telnet/ @rodripf @MartinHjelmare /tests/components/vlc_telnet/ @rodripf @MartinHjelmare -/homeassistant/components/volkszaehler/ @fabaff /homeassistant/components/volumio/ @OnFreund /tests/components/volumio/ @OnFreund /homeassistant/components/volvooncall/ @molobrakos @decompil3d diff --git a/homeassistant/components/volkszaehler/manifest.json b/homeassistant/components/volkszaehler/manifest.json index 5212593da45..baa9e4a8f14 100644 --- a/homeassistant/components/volkszaehler/manifest.json +++ b/homeassistant/components/volkszaehler/manifest.json @@ -3,7 +3,7 @@ "name": "Volkszaehler", "documentation": "https://www.home-assistant.io/integrations/volkszaehler", "requirements": ["volkszaehler==0.3.2"], - "codeowners": ["@fabaff"], + "codeowners": [], "iot_class": "local_polling", "loggers": ["volkszaehler"] } From d52137cc1a1518bba5b07a08f32182ac7ec260b6 Mon Sep 17 00:00:00 2001 From: Jelte Zeilstra Date: Sun, 8 May 2022 15:54:43 +0200 Subject: [PATCH 271/930] Add state class measurement to deCONZ LightLevel sensors (#71516) --- homeassistant/components/deconz/sensor.py | 1 + tests/components/deconz/test_sensor.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index d3a20fad522..bf29e8db478 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -160,6 +160,7 @@ ENTITY_DESCRIPTIONS = { else None, update_key="lightlevel", device_class=SensorDeviceClass.ILLUMINANCE, + state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=LIGHT_LUX, ) ], diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index 590ccee25d5..658e11da906 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -337,7 +337,7 @@ TEST_DATA = [ "state": "5.0", "entity_category": None, "device_class": SensorDeviceClass.ILLUMINANCE, - "state_class": None, + "state_class": SensorStateClass.MEASUREMENT, "attributes": { "on": True, "dark": True, @@ -345,6 +345,7 @@ TEST_DATA = [ "unit_of_measurement": "lx", "device_class": "illuminance", "friendly_name": "Motion sensor 4", + "state_class": "measurement", }, "websocket_event": {"state": {"lightlevel": 1000}}, "next_state": "1.3", From 2eaaa525f4779ca841ec2f5a5ba10ca6d6ac73d9 Mon Sep 17 00:00:00 2001 From: Paul Annekov Date: Sun, 8 May 2022 18:22:20 +0300 Subject: [PATCH 272/930] Add Ukraine Alarm integration (#71501) Co-authored-by: J. Nick Koston Co-authored-by: Paulus Schoutsen Co-authored-by: Martin Hjelmare Co-authored-by: Paulus Schoutsen --- .coveragerc | 3 + CODEOWNERS | 2 + .../components/ukraine_alarm/__init__.py | 79 ++++ .../components/ukraine_alarm/binary_sensor.py | 106 ++++++ .../components/ukraine_alarm/config_flow.py | 154 ++++++++ .../components/ukraine_alarm/const.py | 19 + .../components/ukraine_alarm/manifest.json | 9 + .../components/ukraine_alarm/strings.json | 39 ++ .../ukraine_alarm/translations/en.json | 28 ++ .../ukraine_alarm/translations/ru.json | 28 ++ .../ukraine_alarm/translations/uk.json | 28 ++ homeassistant/generated/config_flows.py | 1 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/ukraine_alarm/__init__.py | 1 + .../ukraine_alarm/test_config_flow.py | 354 ++++++++++++++++++ 16 files changed, 857 insertions(+) create mode 100644 homeassistant/components/ukraine_alarm/__init__.py create mode 100644 homeassistant/components/ukraine_alarm/binary_sensor.py create mode 100644 homeassistant/components/ukraine_alarm/config_flow.py create mode 100644 homeassistant/components/ukraine_alarm/const.py create mode 100644 homeassistant/components/ukraine_alarm/manifest.json create mode 100644 homeassistant/components/ukraine_alarm/strings.json create mode 100644 homeassistant/components/ukraine_alarm/translations/en.json create mode 100644 homeassistant/components/ukraine_alarm/translations/ru.json create mode 100644 homeassistant/components/ukraine_alarm/translations/uk.json create mode 100644 tests/components/ukraine_alarm/__init__.py create mode 100644 tests/components/ukraine_alarm/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 353d2f07d06..706122d0a07 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1313,6 +1313,9 @@ omit = homeassistant/components/twitter/notify.py homeassistant/components/ubus/device_tracker.py homeassistant/components/ue_smart_radio/media_player.py + homeassistant/components/ukraine_alarm/__init__.py + homeassistant/components/ukraine_alarm/const.py + homeassistant/components/ukraine_alarm/binary_sensor.py homeassistant/components/unifiled/* homeassistant/components/upb/__init__.py homeassistant/components/upb/const.py diff --git a/CODEOWNERS b/CODEOWNERS index dcc98dd29cf..fe5a460e9ee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1071,6 +1071,8 @@ build.json @home-assistant/supervisor /tests/components/twentemilieu/ @frenck /homeassistant/components/twinkly/ @dr1rrb @Robbie1221 /tests/components/twinkly/ @dr1rrb @Robbie1221 +/homeassistant/components/ukraine_alarm/ @PaulAnnekov +/tests/components/ukraine_alarm/ @PaulAnnekov /homeassistant/components/unifi/ @Kane610 /tests/components/unifi/ @Kane610 /homeassistant/components/unifiled/ @florisvdk diff --git a/homeassistant/components/ukraine_alarm/__init__.py b/homeassistant/components/ukraine_alarm/__init__.py new file mode 100644 index 00000000000..b2b2ff4162f --- /dev/null +++ b/homeassistant/components/ukraine_alarm/__init__.py @@ -0,0 +1,79 @@ +"""The ukraine_alarm component.""" +from __future__ import annotations + +from datetime import timedelta +import logging +from typing import Any + +import aiohttp +from aiohttp import ClientSession +from ukrainealarm.client import Client + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_REGION +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import ALERT_TYPES, DOMAIN, PLATFORMS + +_LOGGER = logging.getLogger(__name__) + +UPDATE_INTERVAL = timedelta(seconds=10) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Ukraine Alarm as config entry.""" + api_key = entry.data[CONF_API_KEY] + region_id = entry.data[CONF_REGION] + + websession = async_get_clientsession(hass) + + coordinator = UkraineAlarmDataUpdateCoordinator( + hass, websession, api_key, region_id + ) + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok + + +class UkraineAlarmDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): + """Class to manage fetching Ukraine Alarm API.""" + + def __init__( + self, + hass: HomeAssistant, + session: ClientSession, + api_key: str, + region_id: str, + ) -> None: + """Initialize.""" + self.region_id = region_id + self.ukrainealarm = Client(session, api_key) + + super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) + + async def _async_update_data(self) -> dict[str, Any]: + """Update data via library.""" + try: + res = await self.ukrainealarm.get_alerts(self.region_id) + except aiohttp.ClientError as error: + raise UpdateFailed(f"Error fetching alerts from API: {error}") from error + + current = {alert_type: False for alert_type in ALERT_TYPES} + for alert in res[0]["activeAlerts"]: + current[alert["type"]] = True + + return current diff --git a/homeassistant/components/ukraine_alarm/binary_sensor.py b/homeassistant/components/ukraine_alarm/binary_sensor.py new file mode 100644 index 00000000000..b98add95e03 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/binary_sensor.py @@ -0,0 +1,106 @@ +"""binary sensors for Ukraine Alarm integration.""" +from __future__ import annotations + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import UkraineAlarmDataUpdateCoordinator +from .const import ( + ALERT_TYPE_AIR, + ALERT_TYPE_ARTILLERY, + ALERT_TYPE_UNKNOWN, + ALERT_TYPE_URBAN_FIGHTS, + ATTRIBUTION, + DOMAIN, + MANUFACTURER, +) + +BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = ( + BinarySensorEntityDescription( + key=ALERT_TYPE_UNKNOWN, + name="Unknown", + device_class=BinarySensorDeviceClass.SAFETY, + ), + BinarySensorEntityDescription( + key=ALERT_TYPE_AIR, + name="Air", + device_class=BinarySensorDeviceClass.SAFETY, + icon="mdi:cloud", + ), + BinarySensorEntityDescription( + key=ALERT_TYPE_URBAN_FIGHTS, + name="Urban Fights", + device_class=BinarySensorDeviceClass.SAFETY, + icon="mdi:pistol", + ), + BinarySensorEntityDescription( + key=ALERT_TYPE_ARTILLERY, + name="Artillery", + device_class=BinarySensorDeviceClass.SAFETY, + icon="mdi:tank", + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up Ukraine Alarm binary sensor entities based on a config entry.""" + name = config_entry.data[CONF_NAME] + coordinator = hass.data[DOMAIN][config_entry.entry_id] + + async_add_entities( + UkraineAlarmSensor( + name, + config_entry.unique_id, + description, + coordinator, + ) + for description in BINARY_SENSOR_TYPES + ) + + +class UkraineAlarmSensor( + CoordinatorEntity[UkraineAlarmDataUpdateCoordinator], BinarySensorEntity +): + """Class for a Ukraine Alarm binary sensor.""" + + _attr_attribution = ATTRIBUTION + + def __init__( + self, + name, + unique_id, + description: BinarySensorEntityDescription, + coordinator: UkraineAlarmDataUpdateCoordinator, + ) -> None: + """Initialize the sensor.""" + super().__init__(coordinator) + + self.entity_description = description + + self._attr_name = f"{name} {description.name}" + self._attr_unique_id = f"{unique_id}-{description.key}".lower() + self._attr_device_info = DeviceInfo( + entry_type=DeviceEntryType.SERVICE, + identifiers={(DOMAIN, unique_id)}, + manufacturer=MANUFACTURER, + name=name, + ) + + @property + def is_on(self) -> bool | None: + """Return true if the binary sensor is on.""" + return self.coordinator.data.get(self.entity_description.key, None) diff --git a/homeassistant/components/ukraine_alarm/config_flow.py b/homeassistant/components/ukraine_alarm/config_flow.py new file mode 100644 index 00000000000..dcf41658dfb --- /dev/null +++ b/homeassistant/components/ukraine_alarm/config_flow.py @@ -0,0 +1,154 @@ +"""Config flow for Ukraine Alarm.""" +from __future__ import annotations + +import asyncio + +import aiohttp +from ukrainealarm.client import Client +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_REGION +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DOMAIN + + +class UkraineAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Config flow for Ukraine Alarm.""" + + VERSION = 1 + + def __init__(self): + """Initialize a new UkraineAlarmConfigFlow.""" + self.api_key = None + self.states = None + self.selected_region = None + + async def async_step_user(self, user_input=None): + """Handle a flow initialized by the user.""" + errors = {} + + if user_input is not None: + websession = async_get_clientsession(self.hass) + try: + regions = await Client( + websession, user_input[CONF_API_KEY] + ).get_regions() + except aiohttp.ClientResponseError as ex: + errors["base"] = "invalid_api_key" if ex.status == 401 else "unknown" + except aiohttp.ClientConnectionError: + errors["base"] = "cannot_connect" + except aiohttp.ClientError: + errors["base"] = "unknown" + except asyncio.TimeoutError: + errors["base"] = "timeout" + + if not errors and not regions: + errors["base"] = "unknown" + + if not errors: + self.api_key = user_input[CONF_API_KEY] + self.states = regions["states"] + return await self.async_step_state() + + schema = vol.Schema( + { + vol.Required(CONF_API_KEY): str, + } + ) + + return self.async_show_form( + step_id="user", + data_schema=schema, + description_placeholders={"api_url": "https://api.ukrainealarm.com/"}, + errors=errors, + last_step=False, + ) + + async def async_step_state(self, user_input=None): + """Handle user-chosen state.""" + return await self._handle_pick_region("state", "district", user_input) + + async def async_step_district(self, user_input=None): + """Handle user-chosen district.""" + return await self._handle_pick_region("district", "community", user_input) + + async def async_step_community(self, user_input=None): + """Handle user-chosen community.""" + return await self._handle_pick_region("community", None, user_input, True) + + async def _handle_pick_region( + self, step_id: str, next_step: str | None, user_input, last_step=False + ): + """Handle picking a (sub)region.""" + if self.selected_region: + source = self.selected_region["regionChildIds"] + else: + source = self.states + + if user_input is not None: + # Only offer to browse subchildren if picked region wasn't the previously picked one + if ( + not self.selected_region + or user_input[CONF_REGION] != self.selected_region["regionId"] + ): + self.selected_region = _find(source, user_input[CONF_REGION]) + + if next_step and self.selected_region["regionChildIds"]: + return await getattr(self, f"async_step_{next_step}")() + + return await self._async_finish_flow() + + regions = {} + if self.selected_region: + regions[self.selected_region["regionId"]] = self.selected_region[ + "regionName" + ] + + regions.update(_make_regions_object(source)) + + schema = vol.Schema( + { + vol.Required(CONF_REGION): vol.In(regions), + } + ) + + return self.async_show_form( + step_id=step_id, data_schema=schema, last_step=last_step + ) + + async def _async_finish_flow(self): + """Finish the setup.""" + await self.async_set_unique_id(self.selected_region["regionId"]) + self._abort_if_unique_id_configured() + + return self.async_create_entry( + title=self.selected_region["regionName"], + data={ + CONF_API_KEY: self.api_key, + CONF_REGION: self.selected_region["regionId"], + CONF_NAME: self.selected_region["regionName"], + }, + ) + + +def _find(regions, region_id): + return next((region for region in regions if region["regionId"] == region_id), None) + + +def _make_regions_object(regions): + regions_list = [] + for region in regions: + regions_list.append( + { + "id": region["regionId"], + "name": region["regionName"], + } + ) + regions_list = sorted(regions_list, key=lambda region: region["name"].lower()) + regions_object = {} + for region in regions_list: + regions_object[region["id"]] = region["name"] + + return regions_object diff --git a/homeassistant/components/ukraine_alarm/const.py b/homeassistant/components/ukraine_alarm/const.py new file mode 100644 index 00000000000..cc1ae352967 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/const.py @@ -0,0 +1,19 @@ +"""Consts for the Ukraine Alarm.""" +from __future__ import annotations + +from homeassistant.const import Platform + +DOMAIN = "ukraine_alarm" +ATTRIBUTION = "Data provided by Ukraine Alarm" +MANUFACTURER = "Ukraine Alarm" +ALERT_TYPE_UNKNOWN = "UNKNOWN" +ALERT_TYPE_AIR = "AIR" +ALERT_TYPE_ARTILLERY = "ARTILLERY" +ALERT_TYPE_URBAN_FIGHTS = "URBAN_FIGHTS" +ALERT_TYPES = { + ALERT_TYPE_UNKNOWN, + ALERT_TYPE_AIR, + ALERT_TYPE_ARTILLERY, + ALERT_TYPE_URBAN_FIGHTS, +} +PLATFORMS = [Platform.BINARY_SENSOR] diff --git a/homeassistant/components/ukraine_alarm/manifest.json b/homeassistant/components/ukraine_alarm/manifest.json new file mode 100644 index 00000000000..08dad9960b5 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "ukraine_alarm", + "name": "Ukraine Alarm", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/ukraine_alarm", + "requirements": ["ukrainealarm==0.0.1"], + "codeowners": ["@PaulAnnekov"], + "iot_class": "cloud_polling" +} diff --git a/homeassistant/components/ukraine_alarm/strings.json b/homeassistant/components/ukraine_alarm/strings.json new file mode 100644 index 00000000000..79f81e71b08 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/strings.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_location%]" + }, + "error": { + "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]", + "timeout": "[%key:common::config_flow::error::timeout_connect%]" + }, + "step": { + "user": { + "data": { + "api_key": "[%key:common::config_flow::data::api_key%]" + }, + "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}" + }, + "state": { + "data": { + "region": "Region" + }, + "description": "Choose state to monitor" + }, + "district": { + "data": { + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + }, + "description": "If you want to monitor not only state, choose its specific district" + }, + "community": { + "data": { + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + }, + "description": "If you want to monitor not only state and district, choose its specific community" + } + } + } +} diff --git a/homeassistant/components/ukraine_alarm/translations/en.json b/homeassistant/components/ukraine_alarm/translations/en.json new file mode 100644 index 00000000000..2c39945cb87 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/en.json @@ -0,0 +1,28 @@ +{ + "config": { + "step": { + "user": { + "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}", + "title": "Ukraine Alarm" + }, + "state": { + "data": { + "region": "Region" + }, + "description": "Choose state to monitor" + }, + "district": { + "data": { + "region": "Region" + }, + "description": "If you want to monitor not only state, choose its specific district" + }, + "community": { + "data": { + "region": "Region" + }, + "description": "If you want to monitor not only state and district, choose its specific community" + } + } + } +} diff --git a/homeassistant/components/ukraine_alarm/translations/ru.json b/homeassistant/components/ukraine_alarm/translations/ru.json new file mode 100644 index 00000000000..89c9eb1670a --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/ru.json @@ -0,0 +1,28 @@ +{ + "config": { + "step": { + "user": { + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f\u0020\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438\u0020\u0441 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435\u0020\u043d\u0430 {api_url}.", + "title": "Ukraine Alarm" + }, + "state": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + }, + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430" + }, + "district": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + }, + "description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0440\u0430\u0439\u043e\u043d" + }, + "community": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + }, + "description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0438\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443" + } + } + } +} diff --git a/homeassistant/components/ukraine_alarm/translations/uk.json b/homeassistant/components/ukraine_alarm/translations/uk.json new file mode 100644 index 00000000000..2eed983f34f --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/uk.json @@ -0,0 +1,28 @@ +{ + "config": { + "step": { + "user": { + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f\u0020\u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457\u0020\u0437 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c\u0020\u043d\u0430 {api_url}.", + "title": "Ukraine Alarm" + }, + "state": { + "data": { + "region": "\u0420\u0435\u0433\u0456\u043e\u043d" + }, + "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u043d\u0433\u0443" + }, + "district": { + "data": { + "region": "\u0420\u0435\u0433\u0456\u043e\u043d" + }, + "description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u043b\u0438\u0448\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0440\u0430\u0439\u043e\u043d" + }, + "community": { + "data": { + "region": "\u0420\u0435\u0433\u0456\u043e\u043d" + }, + "description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u0442\u0456\u043b\u044c\u043a\u0438\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0442\u0430\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443" + } + } + } +} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 710d97f3c34..510adc74e61 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -366,6 +366,7 @@ FLOWS = { "twentemilieu", "twilio", "twinkly", + "ukraine_alarm", "unifi", "unifiprotect", "upb", diff --git a/requirements_all.txt b/requirements_all.txt index 3c915905d18..09a6a61bd11 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2345,6 +2345,9 @@ twitchAPI==2.5.2 # homeassistant.components.rainforest_eagle uEagle==0.0.2 +# homeassistant.components.ukraine_alarm +ukrainealarm==0.0.1 + # homeassistant.components.unifiprotect unifi-discovery==1.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9a16053f48f..46daf2c2744 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1527,6 +1527,9 @@ twitchAPI==2.5.2 # homeassistant.components.rainforest_eagle uEagle==0.0.2 +# homeassistant.components.ukraine_alarm +ukrainealarm==0.0.1 + # homeassistant.components.unifiprotect unifi-discovery==1.1.2 diff --git a/tests/components/ukraine_alarm/__init__.py b/tests/components/ukraine_alarm/__init__.py new file mode 100644 index 00000000000..228594b3d0c --- /dev/null +++ b/tests/components/ukraine_alarm/__init__.py @@ -0,0 +1 @@ +"""Tests for the Ukraine Alarm integration.""" diff --git a/tests/components/ukraine_alarm/test_config_flow.py b/tests/components/ukraine_alarm/test_config_flow.py new file mode 100644 index 00000000000..3832e6a9fb6 --- /dev/null +++ b/tests/components/ukraine_alarm/test_config_flow.py @@ -0,0 +1,354 @@ +"""Test the Ukraine Alarm config flow.""" +import asyncio +from collections.abc import Generator +from unittest.mock import AsyncMock, patch + +from aiohttp import ClientConnectionError, ClientError, ClientResponseError +import pytest + +from homeassistant import config_entries +from homeassistant.components.ukraine_alarm.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM + +MOCK_API_KEY = "mock-api-key" + + +def _region(rid, recurse=0, depth=0): + if depth == 0: + name_prefix = "State" + elif depth == 1: + name_prefix = "District" + else: + name_prefix = "Community" + + name = f"{name_prefix} {rid}" + region = {"regionId": rid, "regionName": name, "regionChildIds": []} + + if not recurse: + return region + + for i in range(1, 4): + region["regionChildIds"].append(_region(f"{rid}.{i}", recurse - 1, depth + 1)) + + return region + + +REGIONS = { + "states": [_region(f"{i}", i - 1) for i in range(1, 4)], +} + + +@pytest.fixture(autouse=True) +def mock_get_regions() -> Generator[None, AsyncMock, None]: + """Mock the get_regions method.""" + + with patch( + "homeassistant.components.ukraine_alarm.config_flow.Client.get_regions", + return_value=REGIONS, + ) as mock_get: + yield mock_get + + +async def test_state(hass: HomeAssistant) -> None: + """Test we can create entry for state.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + + with patch( + "homeassistant.components.ukraine_alarm.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "1", + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == RESULT_TYPE_CREATE_ENTRY + assert result3["title"] == "State 1" + assert result3["data"] == { + "api_key": MOCK_API_KEY, + "region": "1", + "name": result3["title"], + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_state_district(hass: HomeAssistant) -> None: + """Test we can create entry for state + district.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "2", + }, + ) + assert result3["type"] == RESULT_TYPE_FORM + + with patch( + "homeassistant.components.ukraine_alarm.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result4 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "2.2", + }, + ) + await hass.async_block_till_done() + + assert result4["type"] == RESULT_TYPE_CREATE_ENTRY + assert result4["title"] == "District 2.2" + assert result4["data"] == { + "api_key": MOCK_API_KEY, + "region": "2.2", + "name": result4["title"], + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_state_district_pick_region(hass: HomeAssistant) -> None: + """Test we can create entry for region which has districts.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "2", + }, + ) + assert result3["type"] == RESULT_TYPE_FORM + + with patch( + "homeassistant.components.ukraine_alarm.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result4 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "2", + }, + ) + await hass.async_block_till_done() + + assert result4["type"] == RESULT_TYPE_CREATE_ENTRY + assert result4["title"] == "State 2" + assert result4["data"] == { + "api_key": MOCK_API_KEY, + "region": "2", + "name": result4["title"], + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_state_district_community(hass: HomeAssistant) -> None: + """Test we can create entry for state + district + community.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "3", + }, + ) + assert result3["type"] == RESULT_TYPE_FORM + + result4 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "3.2", + }, + ) + assert result4["type"] == RESULT_TYPE_FORM + + with patch( + "homeassistant.components.ukraine_alarm.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result5 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "region": "3.2.1", + }, + ) + await hass.async_block_till_done() + + assert result5["type"] == RESULT_TYPE_CREATE_ENTRY + assert result5["title"] == "Community 3.2.1" + assert result5["data"] == { + "api_key": MOCK_API_KEY, + "region": "3.2.1", + "name": result5["title"], + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_invalid_api(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.side_effect = ClientResponseError(None, None, status=401) + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "invalid_api_key"} + + +async def test_server_error(hass: HomeAssistant, mock_get_regions) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.side_effect = ClientResponseError(None, None, status=500) + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "unknown"} + + +async def test_cannot_connect(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.side_effect = ClientConnectionError + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_unknown_client_error( + hass: HomeAssistant, mock_get_regions: AsyncMock +) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.side_effect = ClientError + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "unknown"} + + +async def test_timeout_error(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.side_effect = asyncio.TimeoutError + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "timeout"} + + +async def test_no_regions_returned( + hass: HomeAssistant, mock_get_regions: AsyncMock +) -> None: + """Test we can create entry for just region.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + + mock_get_regions.return_value = {} + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "api_key": MOCK_API_KEY, + }, + ) + assert result2["type"] == RESULT_TYPE_FORM + assert result2["step_id"] == "user" + assert result2["errors"] == {"base": "unknown"} From 6922209ddb894d96450c4eb0c8cbcdfa4bba6685 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 14:15:06 -0500 Subject: [PATCH 273/930] Ensure all mysql tables get optimized (#71538) --- homeassistant/components/recorder/models.py | 13 ++++++++++--- homeassistant/components/recorder/repack.py | 3 ++- homeassistant/components/recorder/util.py | 16 ++-------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index c528093198d..9cc94b3019f 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -61,12 +61,11 @@ TABLE_STATISTICS_META = "statistics_meta" TABLE_STATISTICS_RUNS = "statistics_runs" TABLE_STATISTICS_SHORT_TERM = "statistics_short_term" -# Only add TABLE_STATE_ATTRIBUTES and TABLE_EVENT_DATA -# to the below list once we want to check for their -# instance in the sanity check. ALL_TABLES = [ TABLE_STATES, + TABLE_STATE_ATTRIBUTES, TABLE_EVENTS, + TABLE_EVENT_DATA, TABLE_RECORDER_RUNS, TABLE_SCHEMA_CHANGES, TABLE_STATISTICS, @@ -75,6 +74,14 @@ ALL_TABLES = [ TABLE_STATISTICS_SHORT_TERM, ] +TABLES_TO_CHECK = [ + TABLE_STATES, + TABLE_EVENTS, + TABLE_RECORDER_RUNS, + TABLE_SCHEMA_CHANGES, +] + + EMPTY_JSON_OBJECT = "{}" diff --git a/homeassistant/components/recorder/repack.py b/homeassistant/components/recorder/repack.py index c05b8390724..1b1d59df37e 100644 --- a/homeassistant/components/recorder/repack.py +++ b/homeassistant/components/recorder/repack.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING from sqlalchemy import text from .const import SupportedDialect +from .models import ALL_TABLES if TYPE_CHECKING: from . import Recorder @@ -41,6 +42,6 @@ def repack_database(instance: Recorder) -> None: if dialect_name == SupportedDialect.MYSQL: _LOGGER.debug("Optimizing SQL DB to free space") with instance.engine.connect() as conn: - conn.execute(text("OPTIMIZE TABLE states, events, recorder_runs")) + conn.execute(text(f"OPTIMIZE TABLE {','.join(ALL_TABLES)}")) conn.commit() return diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index 3d9fa7d29e1..9f6bef86a29 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -27,13 +27,9 @@ import homeassistant.util.dt as dt_util from .const import DATA_INSTANCE, SQLITE_URL_PREFIX, SupportedDialect from .models import ( - ALL_TABLES, TABLE_RECORDER_RUNS, TABLE_SCHEMA_CHANGES, - TABLE_STATISTICS, - TABLE_STATISTICS_META, - TABLE_STATISTICS_RUNS, - TABLE_STATISTICS_SHORT_TERM, + TABLES_TO_CHECK, RecorderRuns, process_timestamp, ) @@ -213,15 +209,7 @@ def last_run_was_recently_clean(cursor: CursorFetchStrategy) -> bool: def basic_sanity_check(cursor: CursorFetchStrategy) -> bool: """Check tables to make sure select does not fail.""" - for table in ALL_TABLES: - # The statistics tables may not be present in old databases - if table in [ - TABLE_STATISTICS, - TABLE_STATISTICS_META, - TABLE_STATISTICS_RUNS, - TABLE_STATISTICS_SHORT_TERM, - ]: - continue + for table in TABLES_TO_CHECK: if table in (TABLE_RECORDER_RUNS, TABLE_SCHEMA_CHANGES): cursor.execute(f"SELECT * FROM {table};") # nosec # not injection else: From e46310ac0b6415f7e370cc5b5d649acdba5f65bd Mon Sep 17 00:00:00 2001 From: Brandon Rothweiler Date: Sun, 8 May 2022 15:29:58 -0400 Subject: [PATCH 274/930] Add device class for Mazda pressure sensors (#71539) --- homeassistant/components/mazda/sensor.py | 4 ++++ tests/components/mazda/test_sensor.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/homeassistant/components/mazda/sensor.py b/homeassistant/components/mazda/sensor.py index 99f8c74d64d..7e8b45e0ca1 100644 --- a/homeassistant/components/mazda/sensor.py +++ b/homeassistant/components/mazda/sensor.py @@ -188,6 +188,7 @@ SENSOR_ENTITIES = [ key="front_left_tire_pressure", name_suffix="Front Left Tire Pressure", icon="mdi:car-tire-alert", + device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=PRESSURE_PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_front_left_tire_pressure_supported, @@ -197,6 +198,7 @@ SENSOR_ENTITIES = [ key="front_right_tire_pressure", name_suffix="Front Right Tire Pressure", icon="mdi:car-tire-alert", + device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=PRESSURE_PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_front_right_tire_pressure_supported, @@ -206,6 +208,7 @@ SENSOR_ENTITIES = [ key="rear_left_tire_pressure", name_suffix="Rear Left Tire Pressure", icon="mdi:car-tire-alert", + device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=PRESSURE_PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_rear_left_tire_pressure_supported, @@ -215,6 +218,7 @@ SENSOR_ENTITIES = [ key="rear_right_tire_pressure", name_suffix="Rear Right Tire Pressure", icon="mdi:car-tire-alert", + device_class=SensorDeviceClass.PRESSURE, native_unit_of_measurement=PRESSURE_PSI, state_class=SensorStateClass.MEASUREMENT, is_supported=_rear_right_tire_pressure_supported, diff --git a/tests/components/mazda/test_sensor.py b/tests/components/mazda/test_sensor.py index f7fb379da51..f2e9039397b 100644 --- a/tests/components/mazda/test_sensor.py +++ b/tests/components/mazda/test_sensor.py @@ -75,6 +75,7 @@ async def test_sensors(hass): state.attributes.get(ATTR_FRIENDLY_NAME) == "My Mazda3 Front Left Tire Pressure" ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.state == "35" @@ -90,6 +91,7 @@ async def test_sensors(hass): == "My Mazda3 Front Right Tire Pressure" ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.state == "35" @@ -104,6 +106,7 @@ async def test_sensors(hass): state.attributes.get(ATTR_FRIENDLY_NAME) == "My Mazda3 Rear Left Tire Pressure" ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.state == "33" @@ -118,6 +121,7 @@ async def test_sensors(hass): state.attributes.get(ATTR_FRIENDLY_NAME) == "My Mazda3 Rear Right Tire Pressure" ) assert state.attributes.get(ATTR_ICON) == "mdi:car-tire-alert" + assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.PRESSURE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PRESSURE_PSI assert state.attributes.get(ATTR_STATE_CLASS) == SensorStateClass.MEASUREMENT assert state.state == "33" From 7c9c0e911a48e2b99ff15068d9bfbbed5b2cedd2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 14:45:57 -0500 Subject: [PATCH 275/930] Move do_adhoc_statistics to recorder test helpers (#71544) --- tests/components/history/test_init.py | 7 +- tests/components/recorder/common.py | 11 +- tests/components/recorder/test_statistics.py | 19 ++- .../components/recorder/test_websocket_api.py | 7 +- tests/components/sensor/test_recorder.py | 136 +++++++----------- 5 files changed, 79 insertions(+), 101 deletions(-) diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 630d0b84889..143b0c55fab 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -8,7 +8,7 @@ from unittest.mock import patch, sentinel import pytest from pytest import approx -from homeassistant.components import history, recorder +from homeassistant.components import history from homeassistant.components.recorder.history import get_significant_states from homeassistant.components.recorder.models import process_timestamp import homeassistant.core as ha @@ -20,6 +20,7 @@ from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, + do_adhoc_statistics, wait_recording_done, ) @@ -879,7 +880,7 @@ async def test_statistics_during_period( hass.states.async_set("sensor.test", state, attributes=attributes) await async_wait_recording_done(hass) - hass.data[recorder.DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_wait_recording_done(hass) client = await hass_ws_client() @@ -1021,7 +1022,7 @@ async def test_list_statistic_ids( } ] - hass.data[recorder.DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) # Remove the state, statistics will now be fetched from the database hass.states.async_remove("sensor.test") diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index a7472a6289e..b36547309cd 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -2,14 +2,16 @@ from __future__ import annotations from datetime import datetime, timedelta -from typing import cast +from typing import Any, cast from sqlalchemy import create_engine from sqlalchemy.orm.session import Session from homeassistant import core as ha from homeassistant.components import recorder +from homeassistant.components.recorder import get_instance, statistics from homeassistant.components.recorder.models import RecorderRuns +from homeassistant.components.recorder.tasks import StatisticsTask from homeassistant.core import HomeAssistant from homeassistant.util import dt as dt_util @@ -19,6 +21,13 @@ from tests.components.recorder import models_schema_0 DEFAULT_PURGE_TASKS = 3 +def do_adhoc_statistics(hass: HomeAssistant, **kwargs: Any) -> None: + """Trigger an adhoc statistics run.""" + if not (start := kwargs.get("start")): + start = statistics.get_start_time() + get_instance(hass).queue_task(StatisticsTask(start)) + + def wait_recording_done(hass: HomeAssistant) -> None: """Block till recording is done.""" hass.block_till_done() diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 765364a7487..28eba51a4f3 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -30,7 +30,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.setup import setup_component import homeassistant.util.dt as dt_util -from .common import async_wait_recording_done +from .common import async_wait_recording_done, do_adhoc_statistics from tests.common import mock_registry from tests.components.recorder.common import wait_recording_done @@ -57,8 +57,8 @@ def test_compile_hourly_statistics(hass_recorder): stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) assert stats == {} - recorder.do_adhoc_statistics(start=zero) - recorder.do_adhoc_statistics(start=four) + do_adhoc_statistics(hass, start=zero) + do_adhoc_statistics(hass, start=four) wait_recording_done(hass) expected_1 = { "statistic_id": "sensor.test1", @@ -197,12 +197,11 @@ def test_compile_periodic_statistics_exception( """Test exception handling when compiling periodic statistics.""" hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) now = dt_util.utcnow() - recorder.do_adhoc_statistics(start=now) - recorder.do_adhoc_statistics(start=now + timedelta(minutes=5)) + do_adhoc_statistics(hass, start=now) + do_adhoc_statistics(hass, start=now + timedelta(minutes=5)) wait_recording_done(hass) expected_1 = { "statistic_id": "sensor.test1", @@ -249,7 +248,6 @@ def test_compile_periodic_statistics_exception( def test_rename_entity(hass_recorder): """Test statistics is migrated when entity_id is changed.""" hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) entity_reg = mock_registry(hass) @@ -277,7 +275,7 @@ def test_rename_entity(hass_recorder): stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) assert stats == {} - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) expected_1 = { "statistic_id": "sensor.test1", @@ -317,7 +315,6 @@ def test_rename_entity(hass_recorder): def test_statistics_duplicated(hass_recorder, caplog): """Test statistics with same start time is not compiled.""" hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) zero, four, states = record_states(hass) hist = history.get_significant_states(hass, zero, four) @@ -331,7 +328,7 @@ def test_statistics_duplicated(hass_recorder, caplog): "homeassistant.components.sensor.recorder.compile_statistics", return_value=statistics.PlatformCompiledStatistics([], {}), ) as compile_statistics: - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert compile_statistics.called compile_statistics.reset_mock() @@ -339,7 +336,7 @@ def test_statistics_duplicated(hass_recorder, caplog): assert "Statistics already compiled" not in caplog.text caplog.clear() - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert not compile_statistics.called compile_statistics.reset_mock() diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index fe197cb72e6..55bbb13898e 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -18,6 +18,7 @@ from .common import ( async_recorder_block_till_done, async_wait_recording_done, create_engine_test, + do_adhoc_statistics, ) from tests.common import async_fire_time_changed @@ -84,7 +85,7 @@ async def test_clear_statistics(hass, hass_ws_client, recorder_mock): hass.states.async_set("sensor.test3", state * 3, attributes=attributes) await async_wait_recording_done(hass) - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) client = await hass_ws_client() @@ -208,7 +209,7 @@ async def test_update_statistics_metadata( hass.states.async_set("sensor.test", state, attributes=attributes) await async_wait_recording_done(hass) - hass.data[DATA_INSTANCE].do_adhoc_statistics(period="hourly", start=now) + do_adhoc_statistics(hass, period="hourly", start=now) await async_recorder_block_till_done(hass) client = await hass_ws_client() @@ -521,7 +522,7 @@ async def test_get_statistics_metadata( } ] - hass.data[recorder.DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) # Remove the state, statistics will now be fetched from the database hass.states.async_remove("sensor.test") diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index b4abf4d9ec3..9165fbc4354 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -1,7 +1,6 @@ """The tests for sensor recorder platform.""" # pylint: disable=protected-access,invalid-name from datetime import timedelta -from functools import partial import math from statistics import mean from unittest.mock import patch @@ -30,6 +29,7 @@ from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, + do_adhoc_statistics, wait_recording_done, ) @@ -101,7 +101,6 @@ def test_compile_hourly_statistics( """Test compiling hourly statistics.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -113,7 +112,7 @@ def test_compile_hourly_statistics( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -157,7 +156,6 @@ def test_compile_hourly_statistics_purged_state_changes( """Test compiling hourly statistics.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -181,7 +179,7 @@ def test_compile_hourly_statistics_purged_state_changes( hist = history.get_significant_states(hass, zero, four) assert not hist - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -218,7 +216,6 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes """Test compiling hourly statistics for unsupported sensor.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added four, states = record_states(hass, zero, "sensor.test1", attributes) @@ -250,7 +247,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -361,7 +358,6 @@ async def test_compile_hourly_sum_statistics_amount( period2_end = period0 + timedelta(minutes=15) client = await hass_ws_client() hass.config.units = units - recorder = hass.data[DATA_INSTANCE] await async_setup_component(hass, "sensor", {}) # Wait for the sensor recorder platform to be added await async_recorder_block_till_done(hass) @@ -382,17 +378,11 @@ async def test_compile_hourly_sum_statistics_amount( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - await hass.async_add_executor_job( - partial(recorder.do_adhoc_statistics, start=period0) - ) + do_adhoc_statistics(hass, start=period0) await async_wait_recording_done(hass) - await hass.async_add_executor_job( - partial(recorder.do_adhoc_statistics, start=period1) - ) + do_adhoc_statistics(hass, start=period1) await async_wait_recording_done(hass) - await hass.async_add_executor_job( - partial(recorder.do_adhoc_statistics, start=period2) - ) + do_adhoc_statistics(hass, start=period2) await async_wait_recording_done(hass) statistic_ids = await hass.async_add_executor_job(list_statistic_ids, hass) assert statistic_ids == [ @@ -525,7 +515,6 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change( """Test compiling hourly statistics.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -573,8 +562,8 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=zero) - recorder.do_adhoc_statistics(start=zero + timedelta(minutes=5)) + do_adhoc_statistics(hass, start=zero) + do_adhoc_statistics(hass, start=zero + timedelta(minutes=5)) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -632,7 +621,6 @@ def test_compile_hourly_sum_statistics_amount_invalid_last_reset( """Test compiling hourly statistics.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -666,7 +654,7 @@ def test_compile_hourly_sum_statistics_amount_invalid_last_reset( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -712,7 +700,6 @@ def test_compile_hourly_sum_statistics_nan_inf_state( """Test compiling hourly statistics with nan and inf states.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -742,7 +729,7 @@ def test_compile_hourly_sum_statistics_nan_inf_state( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -817,7 +804,6 @@ def test_compile_hourly_sum_statistics_negative_state( zero = dt_util.utcnow() hass = hass_recorder() hass.data.pop(loader.DATA_CUSTOM_COMPONENTS) - recorder = hass.data[DATA_INSTANCE] platform = getattr(hass.components, "test.sensor") platform.init(empty=True) @@ -855,7 +841,7 @@ def test_compile_hourly_sum_statistics_negative_state( ) assert dict(states)[entity_id] == dict(hist)[entity_id] - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert { @@ -911,7 +897,6 @@ def test_compile_hourly_sum_statistics_total_no_reset( period1_end = period2 = period0 + timedelta(minutes=10) period2_end = period0 + timedelta(minutes=15) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -930,11 +915,11 @@ def test_compile_hourly_sum_statistics_total_no_reset( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period2) + do_adhoc_statistics(hass, start=period2) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -1006,7 +991,6 @@ def test_compile_hourly_sum_statistics_total_increasing( period1_end = period2 = period0 + timedelta(minutes=10) period2_end = period0 + timedelta(minutes=15) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1025,11 +1009,11 @@ def test_compile_hourly_sum_statistics_total_increasing( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period2) + do_adhoc_statistics(hass, start=period2) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -1099,7 +1083,6 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( period1_end = period2 = period0 + timedelta(minutes=10) period2_end = period0 + timedelta(minutes=15) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1118,15 +1101,15 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip( ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) assert ( "Entity sensor.test1 has state class total_increasing, but its state is not " "strictly increasing." ) not in caplog.text - recorder.do_adhoc_statistics(start=period2) + do_adhoc_statistics(hass, start=period2) wait_recording_done(hass) state = states["sensor.test1"][6].state previous_state = float(states["sensor.test1"][5].state) @@ -1196,7 +1179,6 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog): period1_end = period2 = period0 + timedelta(minutes=10) period2_end = period0 + timedelta(minutes=15) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added sns1_attr = { @@ -1225,11 +1207,11 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog): ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period2) + do_adhoc_statistics(hass, start=period2) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -1290,7 +1272,6 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): period1_end = period2 = period0 + timedelta(minutes=10) period2_end = period0 + timedelta(minutes=15) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added sns1_attr = {**ENERGY_SENSOR_ATTRIBUTES, "last_reset": None} @@ -1317,11 +1298,11 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): ) assert dict(states)["sensor.test1"] == dict(hist)["sensor.test1"] - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) - recorder.do_adhoc_statistics(start=period2) + do_adhoc_statistics(hass, start=period2) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -1483,7 +1464,6 @@ def test_compile_hourly_statistics_unchanged( """Test compiling hourly statistics, with no changes during the hour.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1495,7 +1475,7 @@ def test_compile_hourly_statistics_unchanged( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=four) + do_adhoc_statistics(hass, start=four) wait_recording_done(hass) stats = statistics_during_period(hass, four, period="5minute") assert stats == { @@ -1520,7 +1500,6 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog): """Test compiling hourly statistics, with the sensor being partially unavailable.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added four, states = record_states_partially_unavailable( @@ -1529,7 +1508,7 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog): hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) stats = statistics_during_period(hass, zero, period="5minute") assert stats == { @@ -1572,7 +1551,6 @@ def test_compile_hourly_statistics_unavailable( """Test compiling hourly statistics, with the sensor being unavailable.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1588,7 +1566,7 @@ def test_compile_hourly_statistics_unavailable( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=four) + do_adhoc_statistics(hass, start=four) wait_recording_done(hass) stats = statistics_during_period(hass, four, period="5minute") assert stats == { @@ -1613,14 +1591,13 @@ def test_compile_hourly_statistics_fails(hass_recorder, caplog): """Test compiling hourly statistics throws.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added with patch( "homeassistant.components.sensor.recorder.compile_statistics", side_effect=Exception, ): - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert "Error while processing event StatisticsTask" in caplog.text @@ -1737,7 +1714,6 @@ def test_compile_hourly_statistics_changing_units_1( """Test compiling hourly statistics where units change from one hour to the next.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1758,7 +1734,7 @@ def test_compile_hourly_statistics_changing_units_1( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert "does not match the unit of already compiled" not in caplog.text statistic_ids = list_statistic_ids(hass) @@ -1789,7 +1765,7 @@ def test_compile_hourly_statistics_changing_units_1( ] } - recorder.do_adhoc_statistics(start=zero + timedelta(minutes=10)) + do_adhoc_statistics(hass, start=zero + timedelta(minutes=10)) wait_recording_done(hass) assert ( "The unit of sensor.test1 (cats) does not match the unit of already compiled " @@ -1840,7 +1816,6 @@ def test_compile_hourly_statistics_changing_units_2( """Test compiling hourly statistics where units change during an hour.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1857,7 +1832,7 @@ def test_compile_hourly_statistics_changing_units_2( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero + timedelta(seconds=30 * 5)) + do_adhoc_statistics(hass, start=zero + timedelta(seconds=30 * 5)) wait_recording_done(hass) assert "The unit of sensor.test1 is changing" in caplog.text assert "and matches the unit of already compiled statistics" not in caplog.text @@ -1893,7 +1868,6 @@ def test_compile_hourly_statistics_changing_units_3( """Test compiling hourly statistics where units change from one hour to the next.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -1914,7 +1888,7 @@ def test_compile_hourly_statistics_changing_units_3( hist = history.get_significant_states(hass, zero, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert "does not match the unit of already compiled" not in caplog.text statistic_ids = list_statistic_ids(hass) @@ -1945,7 +1919,7 @@ def test_compile_hourly_statistics_changing_units_3( ] } - recorder.do_adhoc_statistics(start=zero + timedelta(minutes=10)) + do_adhoc_statistics(hass, start=zero + timedelta(minutes=10)) wait_recording_done(hass) assert "The unit of sensor.test1 is changing" in caplog.text assert f"matches the unit of already compiled statistics ({unit})" in caplog.text @@ -1991,7 +1965,6 @@ def test_compile_hourly_statistics_changing_device_class_1( """Test compiling hourly statistics where device class changes from one hour to the next.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added @@ -2002,7 +1975,7 @@ def test_compile_hourly_statistics_changing_device_class_1( } four, states = record_states(hass, zero, "sensor.test1", attributes) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert "does not match the unit of already compiled" not in caplog.text statistic_ids = list_statistic_ids(hass) @@ -2047,7 +2020,7 @@ def test_compile_hourly_statistics_changing_device_class_1( assert dict(states) == dict(hist) # Run statistics again, we get a warning, and no additional statistics is generated - recorder.do_adhoc_statistics(start=zero + timedelta(minutes=10)) + do_adhoc_statistics(hass, start=zero + timedelta(minutes=10)) wait_recording_done(hass) assert ( f"The normalized unit of sensor.test1 ({statistic_unit}) does not match the " @@ -2095,7 +2068,6 @@ def test_compile_hourly_statistics_changing_device_class_2( """Test compiling hourly statistics where device class changes from one hour to the next.""" zero = dt_util.utcnow() hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added @@ -2107,7 +2079,7 @@ def test_compile_hourly_statistics_changing_device_class_2( } four, states = record_states(hass, zero, "sensor.test1", attributes) - recorder.do_adhoc_statistics(start=zero) + do_adhoc_statistics(hass, start=zero) wait_recording_done(hass) assert "does not match the unit of already compiled" not in caplog.text statistic_ids = list_statistic_ids(hass) @@ -2152,7 +2124,7 @@ def test_compile_hourly_statistics_changing_device_class_2( assert dict(states) == dict(hist) # Run statistics again, we get a warning, and no additional statistics is generated - recorder.do_adhoc_statistics(start=zero + timedelta(minutes=10)) + do_adhoc_statistics(hass, start=zero + timedelta(minutes=10)) wait_recording_done(hass) assert ( f"The unit of sensor.test1 ({state_unit}) does not match the " @@ -2202,7 +2174,6 @@ def test_compile_hourly_statistics_changing_statistics( period0_end = period1 = period0 + timedelta(minutes=5) period1_end = period0 + timedelta(minutes=10) hass = hass_recorder() - recorder = hass.data[DATA_INSTANCE] setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes_1 = { @@ -2216,7 +2187,7 @@ def test_compile_hourly_statistics_changing_statistics( "unit_of_measurement": unit, } four, states = record_states(hass, period0, "sensor.test1", attributes_1) - recorder.do_adhoc_statistics(start=period0) + do_adhoc_statistics(hass, start=period0) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -2250,7 +2221,7 @@ def test_compile_hourly_statistics_changing_statistics( hist = history.get_significant_states(hass, period0, four) assert dict(states) == dict(hist) - recorder.do_adhoc_statistics(start=period1) + do_adhoc_statistics(hass, start=period1) wait_recording_done(hass) statistic_ids = list_statistic_ids(hass) assert statistic_ids == [ @@ -2447,7 +2418,7 @@ def test_compile_statistics_hourly_daily_monthly_summary( # Generate 5-minute statistics for two hours start = zero for i in range(24): - recorder.do_adhoc_statistics(start=start) + do_adhoc_statistics(hass, start=start) wait_recording_done(hass) start += timedelta(minutes=5) @@ -2841,7 +2812,7 @@ async def test_validate_statistics_supported_device_class( # Statistics has run, invalid state - expect error await async_recorder_block_till_done(hass) - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) hass.states.async_set( "sensor.test", 12, attributes={**attributes, **{"unit_of_measurement": "dogs"}} ) @@ -2856,7 +2827,7 @@ async def test_validate_statistics_supported_device_class( await assert_validation_result(client, {}) # Valid state, statistic runs again - empty response - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, {}) @@ -2915,7 +2886,7 @@ async def test_validate_statistics_supported_device_class_2( await assert_validation_result(client, {}) # Statistics has run, device class set - expect error - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) hass.states.async_set("sensor.test", 12, attributes=attributes) await hass.async_block_till_done() @@ -3004,7 +2975,7 @@ async def test_validate_statistics_unsupported_state_class( await assert_validation_result(client, {}) # Statistics has run, empty response - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, {}) @@ -3068,7 +3039,7 @@ async def test_validate_statistics_sensor_no_longer_recorded( await assert_validation_result(client, {}) # Statistics has run, empty response - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, {}) @@ -3141,7 +3112,7 @@ async def test_validate_statistics_sensor_not_recorded( await assert_validation_result(client, expected) # Statistics has run, expect same error - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, expected) @@ -3187,7 +3158,7 @@ async def test_validate_statistics_sensor_removed( await assert_validation_result(client, {}) # Statistics has run, empty response - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, {}) @@ -3243,7 +3214,6 @@ async def test_validate_statistics_unsupported_device_class( await async_setup_component(hass, "sensor", {}) await async_recorder_block_till_done(hass) client = await hass_ws_client() - rec = hass.data[DATA_INSTANCE] # No statistics, no state - empty response await assert_validation_result(client, {}) @@ -3260,7 +3230,7 @@ async def test_validate_statistics_unsupported_device_class( # Run statistics, no statistics will be generated because of conflicting units await async_recorder_block_till_done(hass) - rec.do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_statistic_ids([]) @@ -3272,7 +3242,7 @@ async def test_validate_statistics_unsupported_device_class( # Run statistics one hour later, only the "dogs" state will be considered await async_recorder_block_till_done(hass) - rec.do_adhoc_statistics(start=now + timedelta(hours=1)) + do_adhoc_statistics(hass, start=now + timedelta(hours=1)) await async_recorder_block_till_done(hass) await assert_statistic_ids( [{"statistic_id": "sensor.test", "unit_of_measurement": "dogs"}] @@ -3305,7 +3275,7 @@ async def test_validate_statistics_unsupported_device_class( # Valid state, statistic runs again - empty response await async_recorder_block_till_done(hass) - hass.data[DATA_INSTANCE].do_adhoc_statistics(start=now) + do_adhoc_statistics(hass, start=now) await async_recorder_block_till_done(hass) await assert_validation_result(client, {}) From 896bf986ebfc5b58344f4e9af02b1b6386caed90 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 14:47:12 -0500 Subject: [PATCH 276/930] Speed up nightly database purges with lambda_stmt (#71537) --- homeassistant/components/recorder/purge.py | 123 +++++--------- homeassistant/components/recorder/queries.py | 165 ++++++++++++++++++- 2 files changed, 205 insertions(+), 83 deletions(-) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index 2c6211ca2cd..f94f3d3d641 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -7,23 +7,32 @@ from itertools import zip_longest import logging from typing import TYPE_CHECKING -from sqlalchemy import func from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import distinct from homeassistant.const import EVENT_STATE_CHANGED from .const import MAX_ROWS_TO_PURGE, SupportedDialect -from .models import ( - EventData, - Events, - RecorderRuns, - StateAttributes, - States, - StatisticsRuns, - StatisticsShortTerm, +from .models import Events, StateAttributes, States +from .queries import ( + attributes_ids_exist_in_states, + attributes_ids_exist_in_states_sqlite, + data_ids_exist_in_events, + data_ids_exist_in_events_sqlite, + delete_event_data_rows, + delete_event_rows, + delete_recorder_runs_rows, + delete_states_attributes_rows, + delete_states_rows, + delete_statistics_runs_rows, + delete_statistics_short_term_rows, + disconnect_states_rows, + find_events_to_purge, + find_latest_statistics_runs_run_id, + find_short_term_statistics_to_purge, + find_states_to_purge, + find_statistics_runs_to_purge, ) -from .queries import attributes_ids_exist_in_states, data_ids_exist_in_events from .repack import repack_database from .util import retryable_database_job, session_scope @@ -101,19 +110,9 @@ def _select_event_state_attributes_ids_data_ids_to_purge( session: Session, purge_before: datetime ) -> tuple[set[int], set[int], set[int], set[int]]: """Return a list of event, state, and attribute ids to purge.""" - events = ( - session.query(Events.event_id, Events.data_id) - .filter(Events.time_fired < purge_before) - .limit(MAX_ROWS_TO_PURGE) - .all() - ) + events = session.execute(find_events_to_purge(purge_before)).all() _LOGGER.debug("Selected %s event ids to remove", len(events)) - states = ( - session.query(States.state_id, States.attributes_id) - .filter(States.last_updated < purge_before) - .limit(MAX_ROWS_TO_PURGE) - .all() - ) + states = session.execute(find_states_to_purge(purge_before)).all() _LOGGER.debug("Selected %s state ids to remove", len(states)) event_ids = set() state_ids = set() @@ -152,9 +151,9 @@ def _select_unused_attributes_ids( # seen_ids = { state[0] - for state in session.query(distinct(States.attributes_id)) - .filter(States.attributes_id.in_(attributes_ids)) - .all() + for state in session.execute( + attributes_ids_exist_in_states_sqlite(attributes_ids) + ).all() } else: # @@ -210,9 +209,9 @@ def _select_unused_event_data_ids( if using_sqlite: seen_ids = { state[0] - for state in session.query(distinct(Events.data_id)) - .filter(Events.data_id.in_(data_ids)) - .all() + for state in session.execute( + data_ids_exist_in_events_sqlite(data_ids) + ).all() } else: seen_ids = set() @@ -234,16 +233,11 @@ def _select_statistics_runs_to_purge( session: Session, purge_before: datetime ) -> list[int]: """Return a list of statistic runs to purge, but take care to keep the newest run.""" - statistic_runs = ( - session.query(StatisticsRuns.run_id) - .filter(StatisticsRuns.start < purge_before) - .limit(MAX_ROWS_TO_PURGE) - .all() - ) + statistic_runs = session.execute(find_statistics_runs_to_purge(purge_before)).all() statistic_runs_list = [run.run_id for run in statistic_runs] # Exclude the newest statistics run if ( - last_run := session.query(func.max(StatisticsRuns.run_id)).scalar() + last_run := session.execute(find_latest_statistics_runs_run_id()).scalar() ) and last_run in statistic_runs_list: statistic_runs_list.remove(last_run) @@ -255,12 +249,9 @@ def _select_short_term_statistics_to_purge( session: Session, purge_before: datetime ) -> list[int]: """Return a list of short term statistics to purge.""" - statistics = ( - session.query(StatisticsShortTerm.id) - .filter(StatisticsShortTerm.start < purge_before) - .limit(MAX_ROWS_TO_PURGE) - .all() - ) + statistics = session.execute( + find_short_term_statistics_to_purge(purge_before) + ).all() _LOGGER.debug("Selected %s short term statistics to remove", len(statistics)) return [statistic.id for statistic in statistics] @@ -272,18 +263,10 @@ def _purge_state_ids(instance: Recorder, session: Session, state_ids: set[int]) # the delete does not fail due to a foreign key constraint # since some databases (MSSQL) cannot do the ON DELETE SET NULL # for us. - disconnected_rows = ( - session.query(States) - .filter(States.old_state_id.in_(state_ids)) - .update({"old_state_id": None}, synchronize_session=False) - ) + disconnected_rows = session.execute(disconnect_states_rows(state_ids)) _LOGGER.debug("Updated %s states to remove old_state_id", disconnected_rows) - deleted_rows = ( - session.query(States) - .filter(States.state_id.in_(state_ids)) - .delete(synchronize_session=False) - ) + deleted_rows = session.execute(delete_states_rows(state_ids)) _LOGGER.debug("Deleted %s states", deleted_rows) # Evict eny entries in the old_states cache referring to a purged state @@ -348,12 +331,7 @@ def _purge_attributes_ids( instance: Recorder, session: Session, attributes_ids: set[int] ) -> None: """Delete old attributes ids.""" - - deleted_rows = ( - session.query(StateAttributes) - .filter(StateAttributes.attributes_id.in_(attributes_ids)) - .delete(synchronize_session=False) - ) + deleted_rows = session.execute(delete_states_attributes_rows(attributes_ids)) _LOGGER.debug("Deleted %s attribute states", deleted_rows) # Evict any entries in the state_attributes_ids cache referring to a purged state @@ -365,11 +343,7 @@ def _purge_event_data_ids( ) -> None: """Delete old event data ids.""" - deleted_rows = ( - session.query(EventData) - .filter(EventData.data_id.in_(data_ids)) - .delete(synchronize_session=False) - ) + deleted_rows = session.execute(delete_event_data_rows(data_ids)) _LOGGER.debug("Deleted %s data events", deleted_rows) # Evict any entries in the event_data_ids cache referring to a purged state @@ -378,11 +352,7 @@ def _purge_event_data_ids( def _purge_statistics_runs(session: Session, statistics_runs: list[int]) -> None: """Delete by run_id.""" - deleted_rows = ( - session.query(StatisticsRuns) - .filter(StatisticsRuns.run_id.in_(statistics_runs)) - .delete(synchronize_session=False) - ) + deleted_rows = session.execute(delete_statistics_runs_rows(statistics_runs)) _LOGGER.debug("Deleted %s statistic runs", deleted_rows) @@ -390,21 +360,15 @@ def _purge_short_term_statistics( session: Session, short_term_statistics: list[int] ) -> None: """Delete by id.""" - deleted_rows = ( - session.query(StatisticsShortTerm) - .filter(StatisticsShortTerm.id.in_(short_term_statistics)) - .delete(synchronize_session=False) + deleted_rows = session.execute( + delete_statistics_short_term_rows(short_term_statistics) ) _LOGGER.debug("Deleted %s short term statistics", deleted_rows) def _purge_event_ids(session: Session, event_ids: Iterable[int]) -> None: """Delete by event id.""" - deleted_rows = ( - session.query(Events) - .filter(Events.event_id.in_(event_ids)) - .delete(synchronize_session=False) - ) + deleted_rows = session.execute(delete_event_rows(event_ids)) _LOGGER.debug("Deleted %s events", deleted_rows) @@ -413,11 +377,8 @@ def _purge_old_recorder_runs( ) -> None: """Purge all old recorder runs.""" # Recorder runs is small, no need to batch run it - deleted_rows = ( - session.query(RecorderRuns) - .filter(RecorderRuns.start < purge_before) - .filter(RecorderRuns.run_id != instance.run_history.current.run_id) - .delete(synchronize_session=False) + deleted_rows = session.execute( + delete_recorder_runs_rows(purge_before, instance.run_history.current.run_id) ) _LOGGER.debug("Deleted %s recorder_runs", deleted_rows) diff --git a/homeassistant/components/recorder/queries.py b/homeassistant/components/recorder/queries.py index ff4662e27b1..76098663bd7 100644 --- a/homeassistant/components/recorder/queries.py +++ b/homeassistant/components/recorder/queries.py @@ -1,11 +1,23 @@ """Queries for the recorder.""" from __future__ import annotations -from sqlalchemy import func, lambda_stmt, select, union_all +from collections.abc import Iterable +from datetime import datetime + +from sqlalchemy import delete, distinct, func, lambda_stmt, select, union_all, update from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import Select -from .models import EventData, Events, StateAttributes, States +from .const import MAX_ROWS_TO_PURGE +from .models import ( + EventData, + Events, + RecorderRuns, + StateAttributes, + States, + StatisticsRuns, + StatisticsShortTerm, +) def find_shared_attributes_id( @@ -33,6 +45,17 @@ def _state_attrs_exist(attr: int | None) -> Select: return select(func.min(States.attributes_id)).where(States.attributes_id == attr) +def attributes_ids_exist_in_states_sqlite( + attributes_ids: Iterable[int], +) -> StatementLambdaElement: + """Find attributes ids that exist in the states table.""" + return lambda_stmt( + lambda: select(distinct(States.attributes_id)).filter( + States.attributes_id.in_(attributes_ids) + ) + ) + + def attributes_ids_exist_in_states( attr1: int, attr2: int | None, @@ -245,6 +268,15 @@ def attributes_ids_exist_in_states( ) +def data_ids_exist_in_events_sqlite( + data_ids: Iterable[int], +) -> StatementLambdaElement: + """Find data ids that exist in the events table.""" + return lambda_stmt( + lambda: select(distinct(Events.data_id)).filter(Events.data_id.in_(data_ids)) + ) + + def _event_data_id_exist(data_id: int | None) -> Select: """Check if a event data id exists in the events table.""" return select(func.min(Events.data_id)).where(Events.data_id == data_id) @@ -460,3 +492,132 @@ def data_ids_exist_in_events( _event_data_id_exist(id100), ) ) + + +def disconnect_states_rows(state_ids: Iterable[int]) -> StatementLambdaElement: + """Disconnect states rows.""" + return lambda_stmt( + lambda: update(States) + .where(States.old_state_id.in_(state_ids)) + .values(old_state_id=None) + .execution_options(synchronize_session=False) + ) + + +def delete_states_rows(state_ids: Iterable[int]) -> StatementLambdaElement: + """Delete states rows.""" + return lambda_stmt( + lambda: delete(States) + .where(States.state_id.in_(state_ids)) + .execution_options(synchronize_session=False) + ) + + +def delete_event_data_rows(data_ids: Iterable[int]) -> StatementLambdaElement: + """Delete event_data rows.""" + return lambda_stmt( + lambda: delete(EventData) + .where(EventData.data_id.in_(data_ids)) + .execution_options(synchronize_session=False) + ) + + +def delete_states_attributes_rows( + attributes_ids: Iterable[int], +) -> StatementLambdaElement: + """Delete states_attributes rows.""" + return lambda_stmt( + lambda: delete(StateAttributes) + .where(StateAttributes.attributes_id.in_(attributes_ids)) + .execution_options(synchronize_session=False) + ) + + +def delete_statistics_runs_rows( + statistics_runs: Iterable[int], +) -> StatementLambdaElement: + """Delete statistics_runs rows.""" + return lambda_stmt( + lambda: delete(StatisticsRuns) + .where(StatisticsRuns.run_id.in_(statistics_runs)) + .execution_options(synchronize_session=False) + ) + + +def delete_statistics_short_term_rows( + short_term_statistics: Iterable[int], +) -> StatementLambdaElement: + """Delete statistics_short_term rows.""" + return lambda_stmt( + lambda: delete(StatisticsShortTerm) + .where(StatisticsShortTerm.id.in_(short_term_statistics)) + .execution_options(synchronize_session=False) + ) + + +def delete_event_rows( + event_ids: Iterable[int], +) -> StatementLambdaElement: + """Delete statistics_short_term rows.""" + return lambda_stmt( + lambda: delete(Events) + .where(Events.event_id.in_(event_ids)) + .execution_options(synchronize_session=False) + ) + + +def delete_recorder_runs_rows( + purge_before: datetime, current_run_id: int +) -> StatementLambdaElement: + """Delete recorder_runs rows.""" + return lambda_stmt( + lambda: delete(RecorderRuns) + .filter(RecorderRuns.start < purge_before) + .filter(RecorderRuns.run_id != current_run_id) + .execution_options(synchronize_session=False) + ) + + +def find_events_to_purge(purge_before: datetime) -> StatementLambdaElement: + """Find events to purge.""" + return lambda_stmt( + lambda: select(Events.event_id, Events.data_id) + .filter(Events.time_fired < purge_before) + .limit(MAX_ROWS_TO_PURGE) + ) + + +def find_states_to_purge(purge_before: datetime) -> StatementLambdaElement: + """Find states to purge.""" + return lambda_stmt( + lambda: select(States.state_id, States.attributes_id) + .filter(States.last_updated < purge_before) + .limit(MAX_ROWS_TO_PURGE) + ) + + +def find_short_term_statistics_to_purge( + purge_before: datetime, +) -> StatementLambdaElement: + """Find short term statistics to purge.""" + return lambda_stmt( + lambda: select(StatisticsShortTerm.id) + .filter(StatisticsShortTerm.start < purge_before) + .limit(MAX_ROWS_TO_PURGE) + ) + + +def find_statistics_runs_to_purge( + purge_before: datetime, +) -> StatementLambdaElement: + """Find statistics_runs to purge.""" + return lambda_stmt( + lambda: select(StatisticsRuns.run_id) + .filter(StatisticsRuns.start < purge_before) + .limit(MAX_ROWS_TO_PURGE) + ) + + +def find_latest_statistics_runs_run_id() -> StatementLambdaElement: + """Find the latest statistics_runs run_id.""" + return lambda_stmt(lambda: select(func.max(StatisticsRuns.run_id))) From 0b25b44820c6c1ee3a9ff22d46daa6a8e6e77245 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 8 May 2022 15:21:18 -0600 Subject: [PATCH 277/930] Bump simplisafe-python to 2022.05.1 (#71545) * Bump simplisafe-python to 2022.05.1 * Trigger Build --- homeassistant/components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index 804523b3390..cb1b02e37ae 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,7 +3,7 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==2022.05.0"], + "requirements": ["simplisafe-python==2022.05.1"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 09a6a61bd11..1eb22ec11d8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2156,7 +2156,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==2022.05.0 +simplisafe-python==2022.05.1 # homeassistant.components.sisyphus sisyphus-control==3.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 46daf2c2744..8d2061a6187 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1407,7 +1407,7 @@ sharkiq==0.0.1 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==2022.05.0 +simplisafe-python==2022.05.1 # homeassistant.components.slack slackclient==2.5.0 From 5e737bfe4fbc5a724f5fdf04ea9319c2224cb114 Mon Sep 17 00:00:00 2001 From: Shawn Saenger Date: Sun, 8 May 2022 15:52:39 -0600 Subject: [PATCH 278/930] Add ws66i core integration (#56094) * Add ws66i core integration * Remove all ws66i translations * Update ws66i unit tests to meet minimum code coverage * Update ws66i based on @bdraco review * General improvements after 2nd PR review * Disable entities if amp shutoff, set default source names, set 30sec polling * Add _attr_ and change async_on_unload * Improve entity generation * Implement coordinator * Made options fields required, retry connection on failed attempts, use ZoneStatus for attributes * Refactor WS66i entity properties, raise HomeAssistantError on restore service if no snapshot * Update to pyws66i v1.1 * Add quality scale of silver to manifest * Update config_flow test --- CODEOWNERS | 2 + homeassistant/components/ws66i/__init__.py | 124 ++++ homeassistant/components/ws66i/config_flow.py | 146 ++++ homeassistant/components/ws66i/const.py | 24 + homeassistant/components/ws66i/coordinator.py | 53 ++ homeassistant/components/ws66i/manifest.json | 10 + .../components/ws66i/media_player.py | 213 ++++++ homeassistant/components/ws66i/models.py | 30 + homeassistant/components/ws66i/services.yaml | 15 + homeassistant/components/ws66i/strings.json | 34 + .../components/ws66i/translations/en.json | 35 + homeassistant/generated/config_flows.py | 1 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/ws66i/__init__.py | 1 + tests/components/ws66i/test_config_flow.py | 152 ++++ tests/components/ws66i/test_media_player.py | 692 ++++++++++++++++++ 17 files changed, 1538 insertions(+) create mode 100644 homeassistant/components/ws66i/__init__.py create mode 100644 homeassistant/components/ws66i/config_flow.py create mode 100644 homeassistant/components/ws66i/const.py create mode 100644 homeassistant/components/ws66i/coordinator.py create mode 100644 homeassistant/components/ws66i/manifest.json create mode 100644 homeassistant/components/ws66i/media_player.py create mode 100644 homeassistant/components/ws66i/models.py create mode 100644 homeassistant/components/ws66i/services.yaml create mode 100644 homeassistant/components/ws66i/strings.json create mode 100644 homeassistant/components/ws66i/translations/en.json create mode 100644 tests/components/ws66i/__init__.py create mode 100644 tests/components/ws66i/test_config_flow.py create mode 100644 tests/components/ws66i/test_media_player.py diff --git a/CODEOWNERS b/CODEOWNERS index fe5a460e9ee..ecb9115480f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1172,6 +1172,8 @@ build.json @home-assistant/supervisor /tests/components/workday/ @fabaff /homeassistant/components/worldclock/ @fabaff /tests/components/worldclock/ @fabaff +/homeassistant/components/ws66i/ @ssaenger +/tests/components/ws66i/ @ssaenger /homeassistant/components/xbox/ @hunterjm /tests/components/xbox/ @hunterjm /homeassistant/components/xbox_live/ @MartinHjelmare diff --git a/homeassistant/components/ws66i/__init__.py b/homeassistant/components/ws66i/__init__.py new file mode 100644 index 00000000000..232c4390f19 --- /dev/null +++ b/homeassistant/components/ws66i/__init__.py @@ -0,0 +1,124 @@ +"""The Soundavo WS66i 6-Zone Amplifier integration.""" +from __future__ import annotations + +import logging + +from pyws66i import WS66i, get_ws66i + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_IP_ADDRESS, EVENT_HOMEASSISTANT_STOP +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import ConfigEntryNotReady + +from .const import CONF_SOURCES, DOMAIN +from .coordinator import Ws66iDataUpdateCoordinator +from .models import SourceRep, Ws66iData + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS = ["media_player"] + + +@callback +def _get_sources_from_dict(data) -> SourceRep: + sources_config = data[CONF_SOURCES] + + # Dict index to custom name + source_id_name = {int(index): name for index, name in sources_config.items()} + + # Dict custom name to index + source_name_id = {v: k for k, v in source_id_name.items()} + + # List of custom names + source_names = sorted(source_name_id.keys(), key=lambda v: source_name_id[v]) + + return SourceRep(source_id_name, source_name_id, source_names) + + +def _find_zones(hass: HomeAssistant, ws66i: WS66i) -> list[int]: + """Generate zones list by searching for presence of zones.""" + # Zones 11 - 16 are the master amp + # Zones 21,31 - 26,36 are the daisy-chained amps + zone_list = [] + for amp_num in range(1, 4): + + if amp_num > 1: + # Don't add entities that aren't present + status = ws66i.zone_status(amp_num * 10 + 1) + if status is None: + break + + for zone_num in range(1, 7): + zone_id = (amp_num * 10) + zone_num + zone_list.append(zone_id) + + _LOGGER.info("Detected %d amp(s)", amp_num - 1) + return zone_list + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Soundavo WS66i 6-Zone Amplifier from a config entry.""" + # Get the source names from the options flow + options: dict[str, dict[str, str]] + options = {CONF_SOURCES: entry.options[CONF_SOURCES]} + # Get the WS66i object and open up a connection to it + ws66i = get_ws66i(entry.data[CONF_IP_ADDRESS]) + try: + await hass.async_add_executor_job(ws66i.open) + except ConnectionError as err: + # Amplifier is probably turned off + raise ConfigEntryNotReady("Could not connect to WS66i Amp. Is it off?") from err + + # Create the zone Representation dataclass + source_rep: SourceRep = _get_sources_from_dict(options) + + # Create a list of discovered zones + zones = await hass.async_add_executor_job(_find_zones, hass, ws66i) + + # Create the coordinator for the WS66i + coordinator: Ws66iDataUpdateCoordinator = Ws66iDataUpdateCoordinator( + hass, + ws66i, + zones, + ) + + # Fetch initial data, retry on failed poll + await coordinator.async_config_entry_first_refresh() + + # Create the Ws66iData data class save it to hass + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = Ws66iData( + host_ip=entry.data[CONF_IP_ADDRESS], + device=ws66i, + sources=source_rep, + coordinator=coordinator, + zones=zones, + ) + + def shutdown(event): + """Close the WS66i connection to the amplifier and save snapshots.""" + ws66i.close() + + entry.async_on_unload(entry.add_update_listener(_update_listener)) + entry.async_on_unload( + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shutdown) + ) + + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload_ok: + ws66i: WS66i = hass.data[DOMAIN][entry.entry_id].device + ws66i.close() + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok + + +async def _update_listener(hass: HomeAssistant, entry: ConfigEntry): + """Handle options update.""" + await hass.config_entries.async_reload(entry.entry_id) diff --git a/homeassistant/components/ws66i/config_flow.py b/homeassistant/components/ws66i/config_flow.py new file mode 100644 index 00000000000..a8f098faadd --- /dev/null +++ b/homeassistant/components/ws66i/config_flow.py @@ -0,0 +1,146 @@ +"""Config flow for WS66i 6-Zone Amplifier integration.""" +import logging + +from pyws66i import WS66i, get_ws66i +import voluptuous as vol + +from homeassistant import config_entries, core, exceptions +from homeassistant.const import CONF_IP_ADDRESS + +from .const import ( + CONF_SOURCE_1, + CONF_SOURCE_2, + CONF_SOURCE_3, + CONF_SOURCE_4, + CONF_SOURCE_5, + CONF_SOURCE_6, + CONF_SOURCES, + DOMAIN, + INIT_OPTIONS_DEFAULT, +) + +_LOGGER = logging.getLogger(__name__) + +SOURCES = [ + CONF_SOURCE_1, + CONF_SOURCE_2, + CONF_SOURCE_3, + CONF_SOURCE_4, + CONF_SOURCE_5, + CONF_SOURCE_6, +] + +OPTIONS_SCHEMA = {vol.Optional(source): str for source in SOURCES} + +DATA_SCHEMA = vol.Schema({vol.Required(CONF_IP_ADDRESS): str}) + +FIRST_ZONE = 11 + + +@core.callback +def _sources_from_config(data): + sources_config = { + str(idx + 1): data.get(source) for idx, source in enumerate(SOURCES) + } + + return { + index: name.strip() + for index, name in sources_config.items() + if (name is not None and name.strip() != "") + } + + +async def validate_input(hass: core.HomeAssistant, input_data): + """Validate the user input allows us to connect. + + Data has the keys from DATA_SCHEMA with values provided by the user. + """ + ws66i: WS66i = get_ws66i(input_data[CONF_IP_ADDRESS]) + await hass.async_add_executor_job(ws66i.open) + # No exception. run a simple test to make sure we opened correct port + # Test on FIRST_ZONE because this zone will always be valid + ret_val = await hass.async_add_executor_job(ws66i.zone_status, FIRST_ZONE) + if ret_val is None: + ws66i.close() + raise ConnectionError("Not a valid WS66i connection") + + # Validation done. No issues. Close the connection + ws66i.close() + + # Return info that you want to store in the config entry. + return {CONF_IP_ADDRESS: input_data[CONF_IP_ADDRESS]} + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for WS66i 6-Zone Amplifier.""" + + VERSION = 1 + + async def async_step_user(self, user_input=None): + """Handle the initial step.""" + errors = {} + if user_input is not None: + try: + info = await validate_input(self.hass, user_input) + # Data is valid. Add default values for options flow. + return self.async_create_entry( + title="WS66i Amp", + data=info, + options={CONF_SOURCES: INIT_OPTIONS_DEFAULT}, + ) + except ConnectionError: + errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + + return self.async_show_form( + step_id="user", data_schema=DATA_SCHEMA, errors=errors + ) + + @staticmethod + @core.callback + def async_get_options_flow(config_entry): + """Define the config flow to handle options.""" + return Ws66iOptionsFlowHandler(config_entry) + + +@core.callback +def _key_for_source(index, source, previous_sources): + key = vol.Required( + source, description={"suggested_value": previous_sources[str(index)]} + ) + + return key + + +class Ws66iOptionsFlowHandler(config_entries.OptionsFlow): + """Handle a WS66i options flow.""" + + def __init__(self, config_entry): + """Initialize.""" + self.config_entry = config_entry + + async def async_step_init(self, user_input=None): + """Manage the options.""" + if user_input is not None: + return self.async_create_entry( + title="Source Names", + data={CONF_SOURCES: _sources_from_config(user_input)}, + ) + + # Fill form with previous source names + previous_sources = self.config_entry.options[CONF_SOURCES] + options = { + _key_for_source(idx + 1, source, previous_sources): str + for idx, source in enumerate(SOURCES) + } + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema(options), + ) + + +class CannotConnect(exceptions.HomeAssistantError): + """Error to indicate we cannot connect.""" diff --git a/homeassistant/components/ws66i/const.py b/homeassistant/components/ws66i/const.py new file mode 100644 index 00000000000..ec4439a690d --- /dev/null +++ b/homeassistant/components/ws66i/const.py @@ -0,0 +1,24 @@ +"""Constants for the Soundavo WS66i 6-Zone Amplifier Media Player component.""" + +DOMAIN = "ws66i" + +CONF_SOURCES = "sources" + +CONF_SOURCE_1 = "source_1" +CONF_SOURCE_2 = "source_2" +CONF_SOURCE_3 = "source_3" +CONF_SOURCE_4 = "source_4" +CONF_SOURCE_5 = "source_5" +CONF_SOURCE_6 = "source_6" + +INIT_OPTIONS_DEFAULT = { + "1": "Source 1", + "2": "Source 2", + "3": "Source 3", + "4": "Source 4", + "5": "Source 5", + "6": "Source 6", +} + +SERVICE_SNAPSHOT = "snapshot" +SERVICE_RESTORE = "restore" diff --git a/homeassistant/components/ws66i/coordinator.py b/homeassistant/components/ws66i/coordinator.py new file mode 100644 index 00000000000..a9a274756b5 --- /dev/null +++ b/homeassistant/components/ws66i/coordinator.py @@ -0,0 +1,53 @@ +"""Coordinator for WS66i.""" +from __future__ import annotations + +from datetime import timedelta +import logging + +from pyws66i import WS66i, ZoneStatus + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +_LOGGER = logging.getLogger(__name__) + +POLL_INTERVAL = timedelta(seconds=30) + + +class Ws66iDataUpdateCoordinator(DataUpdateCoordinator): + """DataUpdateCoordinator to gather data for WS66i Zones.""" + + def __init__( + self, + hass: HomeAssistant, + my_api: WS66i, + zones: list[int], + ) -> None: + """Initialize DataUpdateCoordinator to gather data for specific zones.""" + super().__init__( + hass, + _LOGGER, + name="WS66i", + update_interval=POLL_INTERVAL, + ) + self._ws66i = my_api + self._zones = zones + + def _update_all_zones(self) -> list[ZoneStatus]: + """Fetch data for each of the zones.""" + data = [] + for zone_id in self._zones: + data_zone = self._ws66i.zone_status(zone_id) + if data_zone is None: + raise UpdateFailed(f"Failed to update zone {zone_id}") + + data.append(data_zone) + + # HA will call my entity's _handle_coordinator_update() + return data + + async def _async_update_data(self) -> list[ZoneStatus]: + """Fetch data for each of the zones.""" + # HA will call my entity's _handle_coordinator_update() + # The data I pass back here can be accessed through coordinator.data. + return await self.hass.async_add_executor_job(self._update_all_zones) diff --git a/homeassistant/components/ws66i/manifest.json b/homeassistant/components/ws66i/manifest.json new file mode 100644 index 00000000000..abd943eb26b --- /dev/null +++ b/homeassistant/components/ws66i/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ws66i", + "name": "Soundavo WS66i 6-Zone Amplifier", + "documentation": "https://www.home-assistant.io/integrations/ws66i", + "requirements": ["pyws66i==1.1"], + "codeowners": ["@ssaenger"], + "config_flow": true, + "quality_scale": "silver", + "iot_class": "local_polling" +} diff --git a/homeassistant/components/ws66i/media_player.py b/homeassistant/components/ws66i/media_player.py new file mode 100644 index 00000000000..2f485748c02 --- /dev/null +++ b/homeassistant/components/ws66i/media_player.py @@ -0,0 +1,213 @@ +"""Support for interfacing with WS66i 6 zone home audio controller.""" +from copy import deepcopy +import logging + +from pyws66i import WS66i, ZoneStatus + +from homeassistant.components.media_player import MediaPlayerEntity +from homeassistant.components.media_player.const import ( + SUPPORT_SELECT_SOURCE, + SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, + SUPPORT_VOLUME_MUTE, + SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_STEP, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, + async_get_current_platform, +) +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, SERVICE_RESTORE, SERVICE_SNAPSHOT +from .coordinator import Ws66iDataUpdateCoordinator +from .models import Ws66iData + +_LOGGER = logging.getLogger(__name__) + +PARALLEL_UPDATES = 1 + +SUPPORT_WS66I = ( + SUPPORT_VOLUME_MUTE + | SUPPORT_VOLUME_SET + | SUPPORT_VOLUME_STEP + | SUPPORT_TURN_ON + | SUPPORT_TURN_OFF + | SUPPORT_SELECT_SOURCE +) + +MAX_VOL = 38 + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the WS66i 6-zone amplifier platform from a config entry.""" + ws66i_data: Ws66iData = hass.data[DOMAIN][config_entry.entry_id] + + # Build and add the entities from the data class + async_add_entities( + Ws66iZone( + device=ws66i_data.device, + ws66i_data=ws66i_data, + entry_id=config_entry.entry_id, + zone_id=zone_id, + data_idx=idx, + coordinator=ws66i_data.coordinator, + ) + for idx, zone_id in enumerate(ws66i_data.zones) + ) + + # Set up services + platform = async_get_current_platform() + + platform.async_register_entity_service( + SERVICE_SNAPSHOT, + {}, + "snapshot", + ) + + platform.async_register_entity_service( + SERVICE_RESTORE, + {}, + "async_restore", + ) + + +class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): + """Representation of a WS66i amplifier zone.""" + + def __init__( + self, + device: WS66i, + ws66i_data: Ws66iData, + entry_id: str, + zone_id: int, + data_idx: int, + coordinator: Ws66iDataUpdateCoordinator, + ) -> None: + """Initialize a zone entity.""" + super().__init__(coordinator) + self._ws66i: WS66i = device + self._ws66i_data: Ws66iData = ws66i_data + self._zone_id: int = zone_id + self._zone_id_idx: int = data_idx + self._coordinator = coordinator + self._snapshot: ZoneStatus = None + self._status: ZoneStatus = coordinator.data[data_idx] + self._attr_source_list = ws66i_data.sources.name_list + self._attr_unique_id = f"{entry_id}_{self._zone_id}" + self._attr_name = f"Zone {self._zone_id}" + self._attr_supported_features = SUPPORT_WS66I + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, str(self.unique_id))}, + name=self.name, + manufacturer="Soundavo", + model="WS66i 6-Zone Amplifier", + ) + self._set_attrs_from_status() + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + # This will be called for each of the entities after the coordinator + # finishes executing _async_update_data() + + # Save a reference to the zone status that this entity represents + self._status = self.coordinator.data[self._zone_id_idx] + self._set_attrs_from_status() + + # Parent will notify HA of the update + super()._handle_coordinator_update() + + @callback + def _set_attrs_from_status(self) -> None: + status = self._status + sources = self._ws66i_data.sources.id_name + self._attr_state = STATE_ON if status.power else STATE_OFF + self._attr_volume_level = status.volume / float(MAX_VOL) + self._attr_is_volume_muted = status.mute + self._attr_source = self._attr_media_title = sources[status.source] + + @callback + def _async_update_attrs_write_ha_state(self) -> None: + self._set_attrs_from_status() + self.async_write_ha_state() + + @callback + def snapshot(self): + """Save zone's current state.""" + self._snapshot = deepcopy(self._status) + + async def async_restore(self): + """Restore saved state.""" + if not self._snapshot: + raise HomeAssistantError("There is no snapshot to restore") + + await self.hass.async_add_executor_job(self._ws66i.restore_zone, self._snapshot) + self._status = self._snapshot + self._async_update_attrs_write_ha_state() + + async def async_select_source(self, source): + """Set input source.""" + idx = self._ws66i_data.sources.name_id[source] + await self.hass.async_add_executor_job( + self._ws66i.set_source, self._zone_id, idx + ) + self._status.source = idx + self._async_update_attrs_write_ha_state() + + async def async_turn_on(self): + """Turn the media player on.""" + await self.hass.async_add_executor_job( + self._ws66i.set_power, self._zone_id, True + ) + self._status.power = True + self._async_update_attrs_write_ha_state() + + async def async_turn_off(self): + """Turn the media player off.""" + await self.hass.async_add_executor_job( + self._ws66i.set_power, self._zone_id, False + ) + self._status.power = False + self._async_update_attrs_write_ha_state() + + async def async_mute_volume(self, mute): + """Mute (true) or unmute (false) media player.""" + await self.hass.async_add_executor_job( + self._ws66i.set_mute, self._zone_id, mute + ) + self._status.mute = bool(mute) + self._async_update_attrs_write_ha_state() + + async def async_set_volume_level(self, volume): + """Set volume level, range 0..1.""" + await self.hass.async_add_executor_job( + self._ws66i.set_volume, self._zone_id, int(volume * MAX_VOL) + ) + self._status.volume = int(volume * MAX_VOL) + self._async_update_attrs_write_ha_state() + + async def async_volume_up(self): + """Volume up the media player.""" + await self.hass.async_add_executor_job( + self._ws66i.set_volume, self._zone_id, min(self._status.volume + 1, MAX_VOL) + ) + self._status.volume = min(self._status.volume + 1, MAX_VOL) + self._async_update_attrs_write_ha_state() + + async def async_volume_down(self): + """Volume down media player.""" + await self.hass.async_add_executor_job( + self._ws66i.set_volume, self._zone_id, max(self._status.volume - 1, 0) + ) + self._status.volume = max(self._status.volume - 1, 0) + self._async_update_attrs_write_ha_state() diff --git a/homeassistant/components/ws66i/models.py b/homeassistant/components/ws66i/models.py new file mode 100644 index 00000000000..d84ee56a4a1 --- /dev/null +++ b/homeassistant/components/ws66i/models.py @@ -0,0 +1,30 @@ +"""The ws66i integration models.""" +from __future__ import annotations + +from dataclasses import dataclass + +from pyws66i import WS66i + +from .coordinator import Ws66iDataUpdateCoordinator + +# A dataclass is basically a struct in C/C++ + + +@dataclass +class SourceRep: + """Different representations of the amp sources.""" + + id_name: dict[int, str] + name_id: dict[str, int] + name_list: list[str] + + +@dataclass +class Ws66iData: + """Data for the ws66i integration.""" + + host_ip: str + device: WS66i + sources: SourceRep + coordinator: Ws66iDataUpdateCoordinator + zones: list[int] diff --git a/homeassistant/components/ws66i/services.yaml b/homeassistant/components/ws66i/services.yaml new file mode 100644 index 00000000000..cedd1d3546a --- /dev/null +++ b/homeassistant/components/ws66i/services.yaml @@ -0,0 +1,15 @@ +snapshot: + name: Snapshot + description: Take a snapshot of the media player zone. + target: + entity: + integration: ws66i + domain: media_player + +restore: + name: Restore + description: Restore a snapshot of the media player zone. + target: + entity: + integration: ws66i + domain: media_player diff --git a/homeassistant/components/ws66i/strings.json b/homeassistant/components/ws66i/strings.json new file mode 100644 index 00000000000..fcfa64d7e22 --- /dev/null +++ b/homeassistant/components/ws66i/strings.json @@ -0,0 +1,34 @@ +{ + "config": { + "step": { + "user": { + "title": "Connect to the device", + "data": { + "ip_address": "[%key:common::config_flow::data::ip%]" + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + }, + "options": { + "step": { + "init": { + "title": "Configure sources", + "data": { + "source_1": "Name of source #1", + "source_2": "Name of source #2", + "source_3": "Name of source #3", + "source_4": "Name of source #4", + "source_5": "Name of source #5", + "source_6": "Name of source #6" + } + } + } + } +} diff --git a/homeassistant/components/ws66i/translations/en.json b/homeassistant/components/ws66i/translations/en.json new file mode 100644 index 00000000000..7f6a04c1ea3 --- /dev/null +++ b/homeassistant/components/ws66i/translations/en.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "ip_address": "IP Address" + }, + "title": "Connect to the device" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Name of source #1", + "source_2": "Name of source #2", + "source_3": "Name of source #3", + "source_4": "Name of source #4", + "source_5": "Name of source #5", + "source_6": "Name of source #6" + }, + "title": "Configure sources" + } + } + } +} + diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 510adc74e61..4bcfa0dbbef 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -400,6 +400,7 @@ FLOWS = { "wiz", "wled", "wolflink", + "ws66i", "xbox", "xiaomi_aqara", "xiaomi_miio", diff --git a/requirements_all.txt b/requirements_all.txt index 1eb22ec11d8..9dbb2751aac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2022,6 +2022,9 @@ pywilight==0.0.70 # homeassistant.components.wiz pywizlight==0.5.13 +# homeassistant.components.ws66i +pyws66i==1.1 + # homeassistant.components.xeoma pyxeoma==1.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8d2061a6187..8bdfa3f3484 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1336,6 +1336,9 @@ pywilight==0.0.70 # homeassistant.components.wiz pywizlight==0.5.13 +# homeassistant.components.ws66i +pyws66i==1.1 + # homeassistant.components.zerproc pyzerproc==0.4.8 diff --git a/tests/components/ws66i/__init__.py b/tests/components/ws66i/__init__.py new file mode 100644 index 00000000000..3106b858d0c --- /dev/null +++ b/tests/components/ws66i/__init__.py @@ -0,0 +1 @@ +"""Tests for the ws66i component.""" diff --git a/tests/components/ws66i/test_config_flow.py b/tests/components/ws66i/test_config_flow.py new file mode 100644 index 00000000000..d426e62c012 --- /dev/null +++ b/tests/components/ws66i/test_config_flow.py @@ -0,0 +1,152 @@ +"""Test the WS66i 6-Zone Amplifier config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries, data_entry_flow, setup +from homeassistant.components.ws66i.const import ( + CONF_SOURCE_1, + CONF_SOURCE_2, + CONF_SOURCE_3, + CONF_SOURCE_4, + CONF_SOURCE_5, + CONF_SOURCE_6, + CONF_SOURCES, + DOMAIN, + INIT_OPTIONS_DEFAULT, +) +from homeassistant.const import CONF_IP_ADDRESS + +from tests.common import MockConfigEntry +from tests.components.ws66i.test_media_player import AttrDict + +CONFIG = {CONF_IP_ADDRESS: "1.1.1.1"} + + +async def test_form(hass): + """Test we get the form.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.ws66i.config_flow.get_ws66i", + ) as mock_ws66i, patch( + "homeassistant.components.ws66i.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + + ws66i_instance = mock_ws66i.return_value + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], CONFIG + ) + await hass.async_block_till_done() + + ws66i_instance.open.assert_called_once() + ws66i_instance.close.assert_called_once() + + assert result2["type"] == "create_entry" + assert result2["title"] == "WS66i Amp" + assert result2["data"] == {CONF_IP_ADDRESS: CONFIG[CONF_IP_ADDRESS]} + + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_cannot_connect(hass): + """Test cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("homeassistant.components.ws66i.config_flow.get_ws66i") as mock_ws66i: + ws66i_instance = mock_ws66i.return_value + ws66i_instance.open.side_effect = ConnectionError + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], CONFIG + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_form_wrong_ip(hass): + """Test cannot connect error with bad IP.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("homeassistant.components.ws66i.config_flow.get_ws66i") as mock_ws66i: + ws66i_instance = mock_ws66i.return_value + ws66i_instance.zone_status.return_value = None + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], CONFIG + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_generic_exception(hass): + """Test generic exception.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("homeassistant.components.ws66i.config_flow.get_ws66i") as mock_ws66i: + ws66i_instance = mock_ws66i.return_value + ws66i_instance.open.side_effect = Exception + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], CONFIG + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "unknown"} + + +async def test_options_flow(hass): + """Test config flow options.""" + conf = {CONF_IP_ADDRESS: "1.1.1.1", CONF_SOURCES: INIT_OPTIONS_DEFAULT} + + config_entry = MockConfigEntry( + domain=DOMAIN, + data=conf, + options={CONF_SOURCES: INIT_OPTIONS_DEFAULT}, + ) + config_entry.add_to_hass(hass) + + with patch("homeassistant.components.ws66i.get_ws66i") as mock_ws66i: + ws66i_instance = mock_ws66i.return_value + ws66i_instance.zone_status.return_value = AttrDict( + power=True, volume=0, mute=True, source=1, treble=0, bass=0, balance=10 + ) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_SOURCE_1: "one", + CONF_SOURCE_2: "too", + CONF_SOURCE_3: "tree", + CONF_SOURCE_4: "for", + CONF_SOURCE_5: "feeve", + CONF_SOURCE_6: "roku", + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert config_entry.options[CONF_SOURCES] == { + "1": "one", + "2": "too", + "3": "tree", + "4": "for", + "5": "feeve", + "6": "roku", + } diff --git a/tests/components/ws66i/test_media_player.py b/tests/components/ws66i/test_media_player.py new file mode 100644 index 00000000000..6fc1e00d827 --- /dev/null +++ b/tests/components/ws66i/test_media_player.py @@ -0,0 +1,692 @@ +"""The tests for WS66i Media player platform.""" +from collections import defaultdict +from unittest.mock import patch + +import pytest + +from homeassistant.components.media_player.const import ( + ATTR_INPUT_SOURCE, + ATTR_INPUT_SOURCE_LIST, + ATTR_MEDIA_VOLUME_LEVEL, + DOMAIN as MEDIA_PLAYER_DOMAIN, + SERVICE_SELECT_SOURCE, + SUPPORT_SELECT_SOURCE, + SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, + SUPPORT_VOLUME_MUTE, + SUPPORT_VOLUME_SET, + SUPPORT_VOLUME_STEP, +) +from homeassistant.components.ws66i.const import ( + CONF_SOURCES, + DOMAIN, + INIT_OPTIONS_DEFAULT, + SERVICE_RESTORE, + SERVICE_SNAPSHOT, +) +from homeassistant.const import ( + CONF_IP_ADDRESS, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + SERVICE_VOLUME_DOWN, + SERVICE_VOLUME_MUTE, + SERVICE_VOLUME_SET, + SERVICE_VOLUME_UP, + STATE_ON, + STATE_UNAVAILABLE, +) +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry + +MOCK_SOURCE_DIC = { + "1": "one", + "2": "two", + "3": "three", + "4": "four", + "5": "five", + "6": "six", +} +MOCK_CONFIG = {CONF_IP_ADDRESS: "fake ip"} +MOCK_OPTIONS = {CONF_SOURCES: MOCK_SOURCE_DIC} +MOCK_DEFAULT_OPTIONS = {CONF_SOURCES: INIT_OPTIONS_DEFAULT} + +ZONE_1_ID = "media_player.zone_11" +ZONE_2_ID = "media_player.zone_12" +ZONE_7_ID = "media_player.zone_21" + + +class AttrDict(dict): + """Helper class for mocking attributes.""" + + def __setattr__(self, name, value): + """Set attribute.""" + self[name] = value + + def __getattr__(self, item): + """Get attribute.""" + try: + return self[item] + except KeyError as err: + # The reason for doing this is because of the deepcopy in my code + raise AttributeError(item) from err + + +class MockWs66i: + """Mock for pyws66i object.""" + + def __init__(self, fail_open=False, fail_zone_check=None): + """Init mock object.""" + self.zones = defaultdict( + lambda: AttrDict( + power=True, volume=0, mute=True, source=1, treble=0, bass=0, balance=10 + ) + ) + self.fail_open = fail_open + self.fail_zone_check = fail_zone_check + + def open(self): + """Open socket. Do nothing.""" + if self.fail_open is True: + raise ConnectionError() + + def close(self): + """Close socket. Do nothing.""" + + def zone_status(self, zone_id): + """Get zone status.""" + if self.fail_zone_check is not None and zone_id in self.fail_zone_check: + return None + status = self.zones[zone_id] + status.zone = zone_id + return AttrDict(status) + + def set_source(self, zone_id, source_idx): + """Set source for zone.""" + self.zones[zone_id].source = source_idx + + def set_power(self, zone_id, power): + """Turn zone on/off.""" + self.zones[zone_id].power = power + + def set_mute(self, zone_id, mute): + """Mute/unmute zone.""" + self.zones[zone_id].mute = mute + + def set_volume(self, zone_id, volume): + """Set volume for zone.""" + self.zones[zone_id].volume = volume + + def restore_zone(self, zone): + """Restore zone status.""" + self.zones[zone.zone] = AttrDict(zone) + + +async def test_setup_success(hass): + """Test connection success.""" + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: MockWs66i(), + ): + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert hass.states.get(ZONE_1_ID) is not None + + +async def _setup_ws66i(hass, ws66i) -> MockConfigEntry: + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: ws66i, + ): + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_DEFAULT_OPTIONS + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry + + +async def _setup_ws66i_with_options(hass, ws66i) -> MockConfigEntry: + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: ws66i, + ): + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + return config_entry + + +async def _call_media_player_service(hass, name, data): + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, name, service_data=data, blocking=True + ) + + +async def _call_ws66i_service(hass, name, data): + await hass.services.async_call(DOMAIN, name, service_data=data, blocking=True) + + +async def test_cannot_connect(hass): + """Test connection error.""" + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: MockWs66i(fail_open=True), + ): + config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert hass.states.get(ZONE_1_ID) is None + + +async def test_cannot_connect_2(hass): + """Test connection error pt 2.""" + # Another way to test same case as test_cannot_connect + ws66i = MockWs66i() + + with patch.object(MockWs66i, "open", side_effect=ConnectionError): + await _setup_ws66i(hass, ws66i) + assert hass.states.get(ZONE_1_ID) is None + + +async def test_service_calls_with_entity_id(hass): + """Test snapshot save/restore service calls.""" + _ = await _setup_ws66i_with_options(hass, MockWs66i()) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + # Saving existing values + await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": ZONE_1_ID}) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} + ) + await hass.async_block_till_done() + + # Restoring other media player to its previous state + # The zone should not be restored + with pytest.raises(HomeAssistantError): + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_2_ID}) + await hass.async_block_till_done() + + # Checking that values were not (!) restored + state = hass.states.get(ZONE_1_ID) + + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "three" + + # Restoring media player to its previous state + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) + await hass.async_block_till_done() + + state = hass.states.get(ZONE_1_ID) + + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "one" + + +async def test_service_calls_with_all_entities(hass): + """Test snapshot save/restore service calls with entity id all.""" + _ = await _setup_ws66i_with_options(hass, MockWs66i()) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + # Saving existing values + await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": "all"}) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} + ) + + # await coordinator.async_refresh() + # await hass.async_block_till_done() + + # Restoring media player to its previous state + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": "all"}) + await hass.async_block_till_done() + + state = hass.states.get(ZONE_1_ID) + + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "one" + + +async def test_service_calls_without_relevant_entities(hass): + """Test snapshot save/restore service calls with bad entity id.""" + config_entry = await _setup_ws66i_with_options(hass, MockWs66i()) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + await coordinator.async_refresh() + await hass.async_block_till_done() + + # Saving existing values + await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": "all"}) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} + ) + + await coordinator.async_refresh() + await hass.async_block_till_done() + + # Restoring media player to its previous state + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": "light.demo"}) + await hass.async_block_till_done() + + state = hass.states.get(ZONE_1_ID) + + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "three" + + +async def test_restore_without_snapshot(hass): + """Test restore when snapshot wasn't called.""" + await _setup_ws66i(hass, MockWs66i()) + + with patch.object(MockWs66i, "restore_zone") as method_call: + with pytest.raises(HomeAssistantError): + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) + await hass.async_block_till_done() + + assert not method_call.called + + +async def test_update(hass): + """Test updating values from ws66i.""" + ws66i = MockWs66i() + config_entry = await _setup_ws66i_with_options(hass, ws66i) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + ws66i.set_source(11, 3) + ws66i.set_volume(11, 38) + + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + + with patch.object(MockWs66i, "open") as method_call: + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert not method_call.called + + state = hass.states.get(ZONE_1_ID) + + assert hass.states.is_state(ZONE_1_ID, STATE_ON) + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "three" + + +async def test_failed_update(hass): + """Test updating failure from ws66i.""" + ws66i = MockWs66i() + config_entry = await _setup_ws66i_with_options(hass, ws66i) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + ws66i.set_source(11, 3) + ws66i.set_volume(11, 38) + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + await coordinator.async_refresh() + await hass.async_block_till_done() + + # Failed update, close called + with patch.object(MockWs66i, "zone_status", return_value=None): + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert hass.states.is_state(ZONE_1_ID, STATE_UNAVAILABLE) + + # A connection re-attempt fails + with patch.object(MockWs66i, "zone_status", return_value=None): + await coordinator.async_refresh() + await hass.async_block_till_done() + + # A connection re-attempt succeeds + await coordinator.async_refresh() + await hass.async_block_till_done() + + # confirm entity is back on + state = hass.states.get(ZONE_1_ID) + + assert hass.states.is_state(ZONE_1_ID, STATE_ON) + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "three" + + +async def test_supported_features(hass): + """Test supported features property.""" + await _setup_ws66i(hass, MockWs66i()) + + state = hass.states.get(ZONE_1_ID) + assert ( + SUPPORT_VOLUME_MUTE + | SUPPORT_VOLUME_SET + | SUPPORT_VOLUME_STEP + | SUPPORT_TURN_ON + | SUPPORT_TURN_OFF + | SUPPORT_SELECT_SOURCE + == state.attributes["supported_features"] + ) + + +async def test_source_list(hass): + """Test source list property.""" + await _setup_ws66i(hass, MockWs66i()) + + state = hass.states.get(ZONE_1_ID) + # Note, the list is sorted! + assert state.attributes[ATTR_INPUT_SOURCE_LIST] == list( + INIT_OPTIONS_DEFAULT.values() + ) + + +async def test_source_list_with_options(hass): + """Test source list property.""" + await _setup_ws66i_with_options(hass, MockWs66i()) + + state = hass.states.get(ZONE_1_ID) + # Note, the list is sorted! + assert state.attributes[ATTR_INPUT_SOURCE_LIST] == list(MOCK_SOURCE_DIC.values()) + + +async def test_select_source(hass): + """Test source selection methods.""" + ws66i = MockWs66i() + await _setup_ws66i_with_options(hass, ws66i) + + await _call_media_player_service( + hass, + SERVICE_SELECT_SOURCE, + {"entity_id": ZONE_1_ID, ATTR_INPUT_SOURCE: "three"}, + ) + assert ws66i.zones[11].source == 3 + + +async def test_source_select(hass): + """Test behavior when device has unknown source.""" + ws66i = MockWs66i() + config_entry = await _setup_ws66i_with_options(hass, ws66i) + + ws66i.set_source(11, 5) + + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + await coordinator.async_refresh() + await hass.async_block_till_done() + + state = hass.states.get(ZONE_1_ID) + + assert state.attributes.get(ATTR_INPUT_SOURCE) == "five" + + +async def test_turn_on_off(hass): + """Test turning on the zone.""" + ws66i = MockWs66i() + await _setup_ws66i(hass, ws66i) + + await _call_media_player_service(hass, SERVICE_TURN_OFF, {"entity_id": ZONE_1_ID}) + assert not ws66i.zones[11].power + + await _call_media_player_service(hass, SERVICE_TURN_ON, {"entity_id": ZONE_1_ID}) + assert ws66i.zones[11].power + + +async def test_mute_volume(hass): + """Test mute functionality.""" + ws66i = MockWs66i() + await _setup_ws66i(hass, ws66i) + + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.5} + ) + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": False} + ) + assert not ws66i.zones[11].mute + + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": True} + ) + assert ws66i.zones[11].mute + + +async def test_volume_up_down(hass): + """Test increasing volume by one.""" + ws66i = MockWs66i() + config_entry = await _setup_ws66i(hass, ws66i) + + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + assert ws66i.zones[11].volume == 0 + + await _call_media_player_service( + hass, SERVICE_VOLUME_DOWN, {"entity_id": ZONE_1_ID} + ) + await coordinator.async_refresh() + await hass.async_block_till_done() + # should not go below zero + assert ws66i.zones[11].volume == 0 + + await _call_media_player_service(hass, SERVICE_VOLUME_UP, {"entity_id": ZONE_1_ID}) + await coordinator.async_refresh() + await hass.async_block_till_done() + assert ws66i.zones[11].volume == 1 + + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + await coordinator.async_refresh() + await hass.async_block_till_done() + assert ws66i.zones[11].volume == 38 + + await _call_media_player_service(hass, SERVICE_VOLUME_UP, {"entity_id": ZONE_1_ID}) + + await coordinator.async_refresh() + await hass.async_block_till_done() + # should not go above 38 + assert ws66i.zones[11].volume == 38 + + await _call_media_player_service( + hass, SERVICE_VOLUME_DOWN, {"entity_id": ZONE_1_ID} + ) + assert ws66i.zones[11].volume == 37 + + +async def test_first_run_with_available_zones(hass): + """Test first run with all zones available.""" + ws66i = MockWs66i() + await _setup_ws66i(hass, ws66i) + + registry = er.async_get(hass) + + entry = registry.async_get(ZONE_7_ID) + assert not entry.disabled + + +async def test_first_run_with_failing_zones(hass): + """Test first run with failed zones.""" + ws66i = MockWs66i() + + with patch.object(MockWs66i, "zone_status", return_value=None): + await _setup_ws66i(hass, ws66i) + + registry = er.async_get(hass) + + entry = registry.async_get(ZONE_1_ID) + assert entry is None + + entry = registry.async_get(ZONE_7_ID) + assert entry is None + + +async def test_register_all_entities(hass): + """Test run with all entities registered.""" + ws66i = MockWs66i() + await _setup_ws66i(hass, ws66i) + + registry = er.async_get(hass) + + entry = registry.async_get(ZONE_1_ID) + assert not entry.disabled + + entry = registry.async_get(ZONE_7_ID) + assert not entry.disabled + + +async def test_register_entities_in_1_amp_only(hass): + """Test run with only zones 11-16 registered.""" + ws66i = MockWs66i(fail_zone_check=[21]) + await _setup_ws66i(hass, ws66i) + + registry = er.async_get(hass) + + entry = registry.async_get(ZONE_1_ID) + assert not entry.disabled + + entry = registry.async_get(ZONE_2_ID) + assert not entry.disabled + + entry = registry.async_get(ZONE_7_ID) + assert entry is None + + +async def test_unload_config_entry(hass): + """Test unloading config entry.""" + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: MockWs66i(), + ): + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.data[DOMAIN][config_entry.entry_id] + + with patch.object(MockWs66i, "close") as method_call: + await config_entry.async_unload(hass) + await hass.async_block_till_done() + + assert method_call.called + + assert not hass.data[DOMAIN] + + +async def test_restore_snapshot_on_reconnect(hass): + """Test restoring a saved snapshot when reconnecting to amp.""" + ws66i = MockWs66i() + config_entry = await _setup_ws66i_with_options(hass, ws66i) + + # Changing media player to new state + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} + ) + + # Save a snapshot + await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": ZONE_1_ID}) + + ws66i_data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = ws66i_data.coordinator + + # Failed update, + with patch.object(MockWs66i, "zone_status", return_value=None): + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert hass.states.is_state(ZONE_1_ID, STATE_UNAVAILABLE) + + # A connection re-attempt succeeds + await coordinator.async_refresh() + await hass.async_block_till_done() + + # confirm entity is back on + state = hass.states.get(ZONE_1_ID) + + assert hass.states.is_state(ZONE_1_ID, STATE_ON) + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "one" + + # Change states + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + await _call_media_player_service( + hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "six"} + ) + + # Now confirm that the snapshot before the disconnect works + await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) + await hass.async_block_till_done() + + state = hass.states.get(ZONE_1_ID) + + assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 + assert state.attributes[ATTR_INPUT_SOURCE] == "one" From 9a5e0281db476b598cadd421bfd1e6fc5b0582e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 9 May 2022 01:20:18 +0200 Subject: [PATCH 279/930] Add missing AEMET weather units (#70165) --- CODEOWNERS | 2 ++ homeassistant/components/aemet/manifest.json | 2 +- homeassistant/components/aemet/weather.py | 8 +++++--- tests/components/aemet/test_weather.py | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index ecb9115480f..2e2a8c0584f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -32,6 +32,8 @@ build.json @home-assistant/supervisor /tests/components/adguard/ @frenck /homeassistant/components/advantage_air/ @Bre77 /tests/components/advantage_air/ @Bre77 +/homeassistant/components/aemet/ @Noltari +/tests/components/aemet/ @Noltari /homeassistant/components/agent_dvr/ @ispysoftware /tests/components/agent_dvr/ @ispysoftware /homeassistant/components/air_quality/ @home-assistant/core diff --git a/homeassistant/components/aemet/manifest.json b/homeassistant/components/aemet/manifest.json index 087d5c38820..e530489476e 100644 --- a/homeassistant/components/aemet/manifest.json +++ b/homeassistant/components/aemet/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/aemet", "requirements": ["AEMET-OpenData==0.2.1"], - "codeowners": [], + "codeowners": ["@Noltari"], "iot_class": "cloud_polling", "loggers": ["aemet_opendata"] } diff --git a/homeassistant/components/aemet/weather.py b/homeassistant/components/aemet/weather.py index 2bbb4d0da55..d05442b621e 100644 --- a/homeassistant/components/aemet/weather.py +++ b/homeassistant/components/aemet/weather.py @@ -1,7 +1,7 @@ """Support for the AEMET OpenData service.""" from homeassistant.components.weather import WeatherEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS +from homeassistant.const import PRESSURE_HPA, SPEED_KILOMETERS_PER_HOUR, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -48,6 +48,8 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity): _attr_attribution = ATTRIBUTION _attr_temperature_unit = TEMP_CELSIUS + _attr_pressure_unit = PRESSURE_HPA + _attr_wind_speed_unit = SPEED_KILOMETERS_PER_HOUR def __init__( self, @@ -92,10 +94,10 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity): @property def wind_bearing(self): - """Return the temperature.""" + """Return the wind bearing.""" return self.coordinator.data[ATTR_API_WIND_BEARING] @property def wind_speed(self): - """Return the temperature.""" + """Return the wind speed.""" return self.coordinator.data[ATTR_API_WIND_SPEED] diff --git a/tests/components/aemet/test_weather.py b/tests/components/aemet/test_weather.py index d1f1889c807..809b61e0bda 100644 --- a/tests/components/aemet/test_weather.py +++ b/tests/components/aemet/test_weather.py @@ -42,10 +42,10 @@ async def test_aemet_weather(hass): assert state.state == ATTR_CONDITION_SNOWY assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION assert state.attributes.get(ATTR_WEATHER_HUMIDITY) == 99.0 - assert state.attributes.get(ATTR_WEATHER_PRESSURE) == 1004.4 + assert state.attributes.get(ATTR_WEATHER_PRESSURE) == 100440.0 assert state.attributes.get(ATTR_WEATHER_TEMPERATURE) == -0.7 assert state.attributes.get(ATTR_WEATHER_WIND_BEARING) == 90.0 - assert state.attributes.get(ATTR_WEATHER_WIND_SPEED) == 15 + assert state.attributes.get(ATTR_WEATHER_WIND_SPEED) == 4.17 forecast = state.attributes.get(ATTR_FORECAST)[0] assert forecast.get(ATTR_FORECAST_CONDITION) == ATTR_CONDITION_PARTLYCLOUDY assert forecast.get(ATTR_FORECAST_PRECIPITATION) is None @@ -57,7 +57,7 @@ async def test_aemet_weather(hass): == dt_util.parse_datetime("2021-01-10 00:00:00+00:00").isoformat() ) assert forecast.get(ATTR_FORECAST_WIND_BEARING) == 45.0 - assert forecast.get(ATTR_FORECAST_WIND_SPEED) == 20 + assert forecast.get(ATTR_FORECAST_WIND_SPEED) == 5.56 state = hass.states.get("weather.aemet_hourly") assert state is None From 1d63c2069ea94ea4ddec38967bc678d8fd11ca5b Mon Sep 17 00:00:00 2001 From: Fairesoimeme Date: Mon, 9 May 2022 01:27:09 +0200 Subject: [PATCH 280/930] Add ZiGate device on automatic integration USB and ZEROCONF (#68577) Co-authored-by: J. Nick Koston --- homeassistant/components/zha/config_flow.py | 17 +++- homeassistant/components/zha/manifest.json | 16 +++ homeassistant/generated/usb.py | 12 +++ homeassistant/generated/zeroconf.py | 6 ++ tests/components/zha/test_config_flow.py | 103 +++++++++++++++++++- 5 files changed, 148 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/zha/config_flow.py b/homeassistant/components/zha/config_flow.py index f8f696c56e3..e116954cdcb 100644 --- a/homeassistant/components/zha/config_flow.py +++ b/homeassistant/components/zha/config_flow.py @@ -164,9 +164,15 @@ class ZhaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle zeroconf discovery.""" # Hostname is format: livingroom.local. local_name = discovery_info.hostname[:-1] + radio_type = discovery_info.properties.get("radio_type") or local_name node_name = local_name[: -len(".local")] host = discovery_info.host - device_path = f"socket://{host}:6638" + if local_name.startswith("tube") or "efr32" in local_name: + # This is hard coded to work with legacy devices + port = 6638 + else: + port = discovery_info.port + device_path = f"socket://{host}:{port}" if current_entry := await self.async_set_unique_id(node_name): self._abort_if_unique_id_configured( @@ -187,9 +193,12 @@ class ZhaFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): } self._device_path = device_path - self._radio_type = ( - RadioType.ezsp.name if "efr32" in local_name else RadioType.znp.name - ) + if "efr32" in radio_type: + self._radio_type = RadioType.ezsp.name + elif "zigate" in radio_type: + self._radio_type = RadioType.zigate.name + else: + self._radio_type = RadioType.znp.name return await self.async_step_port_config() diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 8befa039382..d24e29a8ab7 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -57,6 +57,18 @@ "description": "*zigbee*", "known_devices": ["Nortek HUSBZB-1"] }, + { + "vid": "0403", + "pid": "6015", + "description": "*zigate*", + "known_devices": ["ZiGate+"] + }, + { + "vid": "10C4", + "pid": "EA60", + "description": "*zigate*", + "known_devices": ["ZiGate"] + }, { "vid": "10C4", "pid": "8B34", @@ -69,6 +81,10 @@ { "type": "_esphomelib._tcp.local.", "name": "tube*" + }, + { + "type": "_zigate-zigbee-gateway._tcp.local.", + "name": "*zigate*" } ], "after_dependencies": ["usb", "zeroconf"], diff --git a/homeassistant/generated/usb.py b/homeassistant/generated/usb.py index 2e5104ce66d..ef75fbef4d1 100644 --- a/homeassistant/generated/usb.py +++ b/homeassistant/generated/usb.py @@ -77,6 +77,18 @@ USB = [ "pid": "8A2A", "description": "*zigbee*" }, + { + "domain": "zha", + "vid": "0403", + "pid": "6015", + "description": "*zigate*" + }, + { + "domain": "zha", + "vid": "10C4", + "pid": "EA60", + "description": "*zigate*" + }, { "domain": "zha", "vid": "10C4", diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index b93d7249211..d1a15356ddc 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -381,6 +381,12 @@ ZEROCONF = { "domain": "kodi" } ], + "_zigate-zigbee-gateway._tcp.local.": [ + { + "domain": "zha", + "name": "*zigate*" + } + ], "_zwave-js-server._tcp.local.": [ { "domain": "zwave_js" diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index 0c51ecffe9b..dee04165c1e 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -52,8 +52,8 @@ async def test_discovery(detect_mock, hass): service_info = zeroconf.ZeroconfServiceInfo( host="192.168.1.200", addresses=["192.168.1.200"], - hostname="_tube_zb_gw._tcp.local.", - name="mock_name", + hostname="tube._tube_zb_gw._tcp.local.", + name="tube", port=6053, properties={"name": "tube_123456"}, type="mock_type", @@ -77,6 +77,68 @@ async def test_discovery(detect_mock, hass): } +@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) +@patch("zigpy_zigate.zigbee.application.ControllerApplication.probe") +async def test_zigate_via_zeroconf(probe_mock, hass): + """Test zeroconf flow -- zigate radio detected.""" + service_info = zeroconf.ZeroconfServiceInfo( + host="192.168.1.200", + addresses=["192.168.1.200"], + hostname="_zigate-zigbee-gateway._tcp.local.", + name="any", + port=1234, + properties={"radio_type": "zigate"}, + type="mock_type", + ) + flow = await hass.config_entries.flow.async_init( + "zha", context={"source": SOURCE_ZEROCONF}, data=service_info + ) + result = await hass.config_entries.flow.async_configure( + flow["flow_id"], user_input={} + ) + + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "socket://192.168.1.200:1234" + assert result["data"] == { + CONF_DEVICE: { + CONF_DEVICE_PATH: "socket://192.168.1.200:1234", + }, + CONF_RADIO_TYPE: "zigate", + } + + +@patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) +@patch("bellows.zigbee.application.ControllerApplication.probe", return_value=True) +async def test_efr32_via_zeroconf(probe_mock, hass): + """Test zeroconf flow -- efr32 radio detected.""" + service_info = zeroconf.ZeroconfServiceInfo( + host="192.168.1.200", + addresses=["192.168.1.200"], + hostname="efr32._esphomelib._tcp.local.", + name="efr32", + port=1234, + properties={}, + type="mock_type", + ) + flow = await hass.config_entries.flow.async_init( + "zha", context={"source": SOURCE_ZEROCONF}, data=service_info + ) + result = await hass.config_entries.flow.async_configure( + flow["flow_id"], user_input={"baudrate": 115200} + ) + + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "socket://192.168.1.200:6638" + assert result["data"] == { + CONF_DEVICE: { + CONF_DEVICE_PATH: "socket://192.168.1.200:6638", + CONF_BAUDRATE: 115200, + CONF_FLOWCONTROL: "software", + }, + CONF_RADIO_TYPE: "ezsp", + } + + @patch("homeassistant.components.zha.async_setup_entry", AsyncMock(return_value=True)) @patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=True) async def test_discovery_via_zeroconf_ip_change(detect_mock, hass): @@ -183,6 +245,43 @@ async def test_discovery_via_usb(detect_mock, hass): } +@patch("zigpy_zigate.zigbee.application.ControllerApplication.probe") +async def test_zigate_discovery_via_usb(detect_mock, hass): + """Test zigate usb flow -- radio detected.""" + discovery_info = usb.UsbServiceInfo( + device="/dev/ttyZIGBEE", + pid="0403", + vid="6015", + serial_number="1234", + description="zigate radio", + manufacturer="test", + ) + result = await hass.config_entries.flow.async_init( + "zha", context={"source": SOURCE_USB}, data=discovery_info + ) + await hass.async_block_till_done() + assert result["type"] == RESULT_TYPE_FORM + assert result["step_id"] == "confirm" + + with patch("homeassistant.components.zha.async_setup_entry"): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert ( + "zigate radio - /dev/ttyZIGBEE, s/n: 1234 - test - 6015:0403" + in result2["title"] + ) + assert result2["data"] == { + "device": { + "path": "/dev/ttyZIGBEE", + }, + CONF_RADIO_TYPE: "zigate", + } + + @patch("zigpy_znp.zigbee.application.ControllerApplication.probe", return_value=False) async def test_discovery_via_usb_no_radio(detect_mock, hass): """Test usb flow -- no radio detected.""" From e1fa285640013d0f2ad300d03715e71a8ec59015 Mon Sep 17 00:00:00 2001 From: RenierM26 <66512715+RenierM26@users.noreply.github.com> Date: Mon, 9 May 2022 01:28:33 +0200 Subject: [PATCH 281/930] Ezviz dependency bump to 0.2.0.8 (#71512) --- homeassistant/components/ezviz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ezviz/manifest.json b/homeassistant/components/ezviz/manifest.json index 211e500cc7d..cfdfd6e441d 100644 --- a/homeassistant/components/ezviz/manifest.json +++ b/homeassistant/components/ezviz/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/ezviz", "dependencies": ["ffmpeg"], "codeowners": ["@RenierM26", "@baqs"], - "requirements": ["pyezviz==0.2.0.6"], + "requirements": ["pyezviz==0.2.0.8"], "config_flow": true, "iot_class": "cloud_polling", "loggers": ["paho_mqtt", "pyezviz"] diff --git a/requirements_all.txt b/requirements_all.txt index 9dbb2751aac..1d68e4bbfdf 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1480,7 +1480,7 @@ pyeverlights==0.1.0 pyevilgenius==2.0.0 # homeassistant.components.ezviz -pyezviz==0.2.0.6 +pyezviz==0.2.0.8 # homeassistant.components.fido pyfido==2.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8bdfa3f3484..c747ad3587d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -974,7 +974,7 @@ pyeverlights==0.1.0 pyevilgenius==2.0.0 # homeassistant.components.ezviz -pyezviz==0.2.0.6 +pyezviz==0.2.0.8 # homeassistant.components.fido pyfido==2.1.1 From f1dd3b7f89a2990626abc2a4423d46472b002013 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 9 May 2022 00:21:41 +0000 Subject: [PATCH 282/930] [ci skip] Translation update --- .../components/monoprice/translations/fr.json | 24 ++++++------ .../components/recorder/translations/ca.json | 1 + .../components/recorder/translations/de.json | 1 + .../components/recorder/translations/el.json | 1 + .../components/recorder/translations/en.json | 4 +- .../components/recorder/translations/et.json | 1 + .../components/recorder/translations/fr.json | 1 + .../components/recorder/translations/hu.json | 1 + .../recorder/translations/pt-BR.json | 1 + .../components/recorder/translations/ru.json | 1 + .../recorder/translations/zh-Hant.json | 1 + .../ukraine_alarm/translations/de.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/el.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/en.json | 29 +++++++++----- .../ukraine_alarm/translations/fr.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/hu.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/ru.json | 31 ++++++++++----- .../ukraine_alarm/translations/uk.json | 28 ------------- .../components/ws66i/translations/en.json | 3 +- .../components/ws66i/translations/fr.json | 34 ++++++++++++++++ 20 files changed, 255 insertions(+), 63 deletions(-) create mode 100644 homeassistant/components/ukraine_alarm/translations/de.json create mode 100644 homeassistant/components/ukraine_alarm/translations/el.json create mode 100644 homeassistant/components/ukraine_alarm/translations/fr.json create mode 100644 homeassistant/components/ukraine_alarm/translations/hu.json delete mode 100644 homeassistant/components/ukraine_alarm/translations/uk.json create mode 100644 homeassistant/components/ws66i/translations/fr.json diff --git a/homeassistant/components/monoprice/translations/fr.json b/homeassistant/components/monoprice/translations/fr.json index 5489fb4e0e6..7f1a2a7a72e 100644 --- a/homeassistant/components/monoprice/translations/fr.json +++ b/homeassistant/components/monoprice/translations/fr.json @@ -11,12 +11,12 @@ "user": { "data": { "port": "Port", - "source_1": "Nom de la source #1", - "source_2": "Nom de la source #2", - "source_3": "Nom de la source #3", - "source_4": "Nom de la source #4", - "source_5": "Nom de la source #5", - "source_6": "Nom de la source #6" + "source_1": "Nom de la source n\u00ba\u00a01", + "source_2": "Nom de la source n\u00ba\u00a02", + "source_3": "Nom de la source n\u00ba\u00a03", + "source_4": "Nom de la source n\u00ba\u00a04", + "source_5": "Nom de la source n\u00ba\u00a05", + "source_6": "Nom de la source n\u00ba\u00a06" }, "title": "Se connecter \u00e0 l'appareil" } @@ -26,12 +26,12 @@ "step": { "init": { "data": { - "source_1": "Nom de la source #1", - "source_2": "Nom de la source #2", - "source_3": "Nom de la source #3", - "source_4": "Nom de la source #4", - "source_5": "Nom de la source #5", - "source_6": "Nom de la source #6" + "source_1": "Nom de la source n\u00ba\u00a01", + "source_2": "Nom de la source n\u00ba\u00a02", + "source_3": "Nom de la source n\u00ba\u00a03", + "source_4": "Nom de la source n\u00ba\u00a04", + "source_5": "Nom de la source n\u00ba\u00a05", + "source_6": "Nom de la source n\u00ba\u00a06" }, "title": "Configurer les sources" } diff --git a/homeassistant/components/recorder/translations/ca.json b/homeassistant/components/recorder/translations/ca.json index b6e5a549ba7..ee9efb50b21 100644 --- a/homeassistant/components/recorder/translations/ca.json +++ b/homeassistant/components/recorder/translations/ca.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Hora d'inici de l'execuci\u00f3 actual", + "estimated_db_size": "Mida estimada de la base de dades (MiB)", "oldest_recorder_run": "Hora d'inici de l'execuci\u00f3 m\u00e9s antiga" } } diff --git a/homeassistant/components/recorder/translations/de.json b/homeassistant/components/recorder/translations/de.json index 823b4ae62bf..27ba9f1da85 100644 --- a/homeassistant/components/recorder/translations/de.json +++ b/homeassistant/components/recorder/translations/de.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Aktuelle Startzeit der Ausf\u00fchrung", + "estimated_db_size": "Gesch\u00e4tzte Datenbankgr\u00f6\u00dfe (MiB)", "oldest_recorder_run": "\u00c4lteste Startzeit der Ausf\u00fchrung" } } diff --git a/homeassistant/components/recorder/translations/el.json b/homeassistant/components/recorder/translations/el.json index 0ede908d151..6d541820c55 100644 --- a/homeassistant/components/recorder/translations/el.json +++ b/homeassistant/components/recorder/translations/el.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "\u03a4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1 \u03ce\u03c1\u03b1 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7\u03c2", + "estimated_db_size": "\u0395\u03ba\u03c4\u03b9\u03bc\u03ce\u03bc\u03b5\u03bd\u03bf \u03bc\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2 \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd (MiB)", "oldest_recorder_run": "\u03a0\u03b1\u03bb\u03b1\u03b9\u03cc\u03c4\u03b5\u03c1\u03b7 \u03ce\u03c1\u03b1 \u03ad\u03bd\u03b1\u03c1\u03be\u03b7\u03c2 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7\u03c2" } } diff --git a/homeassistant/components/recorder/translations/en.json b/homeassistant/components/recorder/translations/en.json index 32d78085999..fb802ff7ff9 100644 --- a/homeassistant/components/recorder/translations/en.json +++ b/homeassistant/components/recorder/translations/en.json @@ -2,8 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Current Run Start Time", - "oldest_recorder_run": "Oldest Run Start Time", - "estimated_db_size": "Estimated Database Size" + "estimated_db_size": "Estimated Database Size (MiB)", + "oldest_recorder_run": "Oldest Run Start Time" } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/et.json b/homeassistant/components/recorder/translations/et.json index 8455fb5ce7d..0dc6ae3ec62 100644 --- a/homeassistant/components/recorder/translations/et.json +++ b/homeassistant/components/recorder/translations/et.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Praegune k\u00e4ivitamise algusaeg", + "estimated_db_size": "Andmebaasi hinnanguline suurus (MB)", "oldest_recorder_run": "Vanim k\u00e4ivitamise algusaeg" } } diff --git a/homeassistant/components/recorder/translations/fr.json b/homeassistant/components/recorder/translations/fr.json index 31673d970f3..036fbdbb16c 100644 --- a/homeassistant/components/recorder/translations/fr.json +++ b/homeassistant/components/recorder/translations/fr.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution actuelle", + "estimated_db_size": "Taille estim\u00e9e de la base de donn\u00e9es (en Mio)", "oldest_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution la plus ancienne" } } diff --git a/homeassistant/components/recorder/translations/hu.json b/homeassistant/components/recorder/translations/hu.json index 4b0cd6067ad..96bb02c9c20 100644 --- a/homeassistant/components/recorder/translations/hu.json +++ b/homeassistant/components/recorder/translations/hu.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Aktu\u00e1lis futtat\u00e1s kezd\u00e9si id\u0151pontja", + "estimated_db_size": "Az adatb\u00e1zis becs\u00fclt m\u00e9rete (MiB)", "oldest_recorder_run": "Legr\u00e9gebbi futtat\u00e1s kezd\u00e9si id\u0151pontja" } } diff --git a/homeassistant/components/recorder/translations/pt-BR.json b/homeassistant/components/recorder/translations/pt-BR.json index c44ad73c28b..059dcc76779 100644 --- a/homeassistant/components/recorder/translations/pt-BR.json +++ b/homeassistant/components/recorder/translations/pt-BR.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o atual", + "estimated_db_size": "Tamanho estimado do banco de dados (MiB)", "oldest_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o mais antiga" } } diff --git a/homeassistant/components/recorder/translations/ru.json b/homeassistant/components/recorder/translations/ru.json index 6e5c928d77b..bdac6c52a3b 100644 --- a/homeassistant/components/recorder/translations/ru.json +++ b/homeassistant/components/recorder/translations/ru.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "estimated_db_size": "\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (MiB)", "oldest_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430" } } diff --git a/homeassistant/components/recorder/translations/zh-Hant.json b/homeassistant/components/recorder/translations/zh-Hant.json index 648bade5447..d9525c0826d 100644 --- a/homeassistant/components/recorder/translations/zh-Hant.json +++ b/homeassistant/components/recorder/translations/zh-Hant.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "\u76ee\u524d\u57f7\u884c\u958b\u59cb\u6642\u9593", + "estimated_db_size": "\u9810\u4f30\u8cc7\u6599\u5eab\u6a94\u6848\u5927\u5c0f(MiB)", "oldest_recorder_run": "\u6700\u65e9\u57f7\u884c\u958b\u59cb\u6642\u9593" } } diff --git a/homeassistant/components/ukraine_alarm/translations/de.json b/homeassistant/components/ukraine_alarm/translations/de.json new file mode 100644 index 00000000000..75dc7a7856a --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/de.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "Standort ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", + "timeout": "Zeit\u00fcberschreitung beim Verbindungsaufbau", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "community": { + "data": { + "region": "Region" + }, + "description": "Wenn du nicht nur Bundesland und Bezirk \u00fcberwachen m\u00f6chtest, w\u00e4hle die jeweilige Gemeinde aus" + }, + "district": { + "data": { + "region": "Region" + }, + "description": "Wenn du nicht nur das Bundesland \u00fcberwachen m\u00f6chtest, w\u00e4hle seinen bestimmten Bezirk" + }, + "state": { + "data": { + "region": "Region" + }, + "description": "Zu \u00fcberwachendes Bundesland ausw\u00e4hlen" + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel" + }, + "description": "Richte die Ukraine Alarm-Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe zu {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/el.json b/homeassistant/components/ukraine_alarm/translations/el.json new file mode 100644 index 00000000000..57e40095d37 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/el.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "community": { + "data": { + "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" + }, + "description": "\u0395\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c7\u03b9 \u03bc\u03cc\u03bd\u03bf \u03c4\u03b7\u03bd \u03c0\u03bf\u03bb\u03b9\u03c4\u03b5\u03af\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7\u03bd \u03c0\u03b5\u03c1\u03b9\u03c6\u03ad\u03c1\u03b5\u03b9\u03b1, \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03ba\u03bf\u03b9\u03bd\u03cc\u03c4\u03b7\u03c4\u03ac \u03c4\u03b7\u03c2" + }, + "district": { + "data": { + "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" + }, + "description": "\u0391\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c7\u03b9 \u03bc\u03cc\u03bd\u03bf \u03c4\u03b7\u03bd \u03c0\u03bf\u03bb\u03b9\u03c4\u03b5\u03af\u03b1, \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae \u03c4\u03b7\u03c2." + }, + "state": { + "data": { + "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" + }, + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + }, + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd \u03c4\u03b7\u03c2 \u039f\u03c5\u03ba\u03c1\u03b1\u03bd\u03af\u03b1\u03c2. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/en.json b/homeassistant/components/ukraine_alarm/translations/en.json index 2c39945cb87..e7b2a9edcdc 100644 --- a/homeassistant/components/ukraine_alarm/translations/en.json +++ b/homeassistant/components/ukraine_alarm/translations/en.json @@ -1,15 +1,20 @@ { "config": { + "abort": { + "already_configured": "Location is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_api_key": "Invalid API key", + "timeout": "Timeout establishing connection", + "unknown": "Unexpected error" + }, "step": { - "user": { - "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}", - "title": "Ukraine Alarm" - }, - "state": { + "community": { "data": { "region": "Region" }, - "description": "Choose state to monitor" + "description": "If you want to monitor not only state and district, choose its specific community" }, "district": { "data": { @@ -17,12 +22,18 @@ }, "description": "If you want to monitor not only state, choose its specific district" }, - "community": { + "state": { "data": { "region": "Region" }, - "description": "If you want to monitor not only state and district, choose its specific community" + "description": "Choose state to monitor" + }, + "user": { + "data": { + "api_key": "API Key" + }, + "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}" } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/fr.json b/homeassistant/components/ukraine_alarm/translations/fr.json new file mode 100644 index 00000000000..51706dfee5f --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/fr.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_api_key": "Cl\u00e9 d'API non valide", + "timeout": "D\u00e9lai d'attente pour \u00e9tablir la connexion expir\u00e9", + "unknown": "Erreur inattendue" + }, + "step": { + "community": { + "data": { + "region": "R\u00e9gion" + }, + "description": "Si vous ne d\u00e9sirez pas uniquement surveiller l'\u00c9tat et son district, s\u00e9lectionnez sa communaut\u00e9 sp\u00e9cifique" + }, + "district": { + "data": { + "region": "R\u00e9gion" + }, + "description": "Si vous ne d\u00e9sirez pas uniquement surveiller l'\u00c9tat, s\u00e9lectionnez son district sp\u00e9cifique" + }, + "state": { + "data": { + "region": "R\u00e9gion" + }, + "description": "Choisissez l'\u00c9tat \u00e0 surveiller" + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API" + }, + "description": "Configurez l'int\u00e9gration Ukraine Alarm. Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/hu.json b/homeassistant/components/ukraine_alarm/translations/hu.json new file mode 100644 index 00000000000..fb00a32a507 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/hu.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "A hely m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a kapcsolat l\u00e9trehoz\u00e1sa sor\u00e1n", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "community": { + "data": { + "region": "R\u00e9gi\u00f3" + }, + "description": "Ha nem csak a megy\u00e9t \u00e9s a ker\u00fcletet szeretn\u00e9 figyelni, v\u00e1lassza ki az adott k\u00f6z\u00f6ss\u00e9get" + }, + "district": { + "data": { + "region": "R\u00e9gi\u00f3" + }, + "description": "Ha nem csak megy\u00e9t szeretne figyelni, v\u00e1lassza ki az adott ker\u00fcletet" + }, + "state": { + "data": { + "region": "R\u00e9gi\u00f3" + }, + "description": "V\u00e1lassza ki a megfigyelni k\u00edv\u00e1nt megy\u00e9t" + }, + "user": { + "data": { + "api_key": "API kulcs" + }, + "description": "\u00c1ll\u00edtsa be az Ukraine Alarm integr\u00e1ci\u00f3t. API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/ru.json b/homeassistant/components/ukraine_alarm/translations/ru.json index 89c9eb1670a..cd852ad0745 100644 --- a/homeassistant/components/ukraine_alarm/translations/ru.json +++ b/homeassistant/components/ukraine_alarm/translations/ru.json @@ -1,28 +1,39 @@ { "config": { + "abort": { + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", + "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, "step": { - "user": { - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f\u0020\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438\u0020\u0441 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435\u0020\u043d\u0430 {api_url}.", - "title": "Ukraine Alarm" - }, - "state": { + "community": { "data": { "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430" + "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0448\u0442\u0430\u0442\u043e\u043c \u0438 \u043e\u043a\u0440\u0443\u0433\u043e\u043c, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e." }, "district": { "data": { "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, - "description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0440\u0430\u0439\u043e\u043d" + "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0448\u0442\u0430\u0442\u043e\u043c, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0440\u0430\u0439\u043e\u043d." }, - "community": { + "state": { "data": { "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, - "description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0438\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u0442\u0430\u0442 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430." + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API" + }, + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 {api_url}" } } } -} +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/uk.json b/homeassistant/components/ukraine_alarm/translations/uk.json deleted file mode 100644 index 2eed983f34f..00000000000 --- a/homeassistant/components/ukraine_alarm/translations/uk.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "config": { - "step": { - "user": { - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f\u0020\u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457\u0020\u0437 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c\u0020\u043d\u0430 {api_url}.", - "title": "Ukraine Alarm" - }, - "state": { - "data": { - "region": "\u0420\u0435\u0433\u0456\u043e\u043d" - }, - "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u043d\u0433\u0443" - }, - "district": { - "data": { - "region": "\u0420\u0435\u0433\u0456\u043e\u043d" - }, - "description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u043b\u0438\u0448\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0440\u0430\u0439\u043e\u043d" - }, - "community": { - "data": { - "region": "\u0420\u0435\u0433\u0456\u043e\u043d" - }, - "description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u0442\u0456\u043b\u044c\u043a\u0438\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0442\u0430\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443" - } - } - } -} diff --git a/homeassistant/components/ws66i/translations/en.json b/homeassistant/components/ws66i/translations/en.json index 7f6a04c1ea3..30ef1e4205a 100644 --- a/homeassistant/components/ws66i/translations/en.json +++ b/homeassistant/components/ws66i/translations/en.json @@ -31,5 +31,4 @@ } } } -} - +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/fr.json b/homeassistant/components/ws66i/translations/fr.json new file mode 100644 index 00000000000..d4d3f6e7350 --- /dev/null +++ b/homeassistant/components/ws66i/translations/fr.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "unknown": "Erreur inattendue" + }, + "step": { + "user": { + "data": { + "ip_address": "Adresse IP" + }, + "title": "Se connecter \u00e0 l'appareil" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nom de la source n\u00ba\u00a01", + "source_2": "Nom de la source n\u00ba\u00a02", + "source_3": "Nom de la source n\u00ba\u00a03", + "source_4": "Nom de la source n\u00ba\u00a04", + "source_5": "Nom de la source n\u00ba\u00a05", + "source_6": "Nom de la source n\u00ba\u00a06" + }, + "title": "Configurer les sources" + } + } + } +} \ No newline at end of file From 0b8f87169bb2019896069e958aa0f15f42f613cf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 20:05:19 -0500 Subject: [PATCH 283/930] Pretty zha manifest.json (#71556) --- homeassistant/components/zha/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index d24e29a8ab7..d4e71562b07 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -63,7 +63,7 @@ "description": "*zigate*", "known_devices": ["ZiGate+"] }, - { + { "vid": "10C4", "pid": "EA60", "description": "*zigate*", From 6da889326bd51e13752b1511f2f1d46d7c78ab67 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sun, 8 May 2022 22:07:12 -0400 Subject: [PATCH 284/930] Fix typer/click incompatibilty for unifiprotect (#71555) Co-authored-by: J. Nick Koston --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index d5dbb51ffc1..3c3b461ed4f 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Protect", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==3.4.0", "unifi-discovery==1.1.2"], + "requirements": ["pyunifiprotect==3.4.1", "unifi-discovery==1.1.2"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index 1d68e4bbfdf..07772173bd8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1984,7 +1984,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.4.0 +pyunifiprotect==3.4.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c747ad3587d..9cfa8a6945b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1307,7 +1307,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.4.0 +pyunifiprotect==3.4.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 From 9ef5c23f1ca90ca8242dffc60922fe2a4f0b9b87 Mon Sep 17 00:00:00 2001 From: Andrew Bullock Date: Mon, 9 May 2022 03:10:25 +0100 Subject: [PATCH 285/930] Add support to Hunter Douglas for Silhouette Type 23 Tilting (#70775) Co-authored-by: J. Nick Koston --- CODEOWNERS | 4 +- .../hunterdouglas_powerview/cover.py | 170 ++++++++++++++++-- .../hunterdouglas_powerview/manifest.json | 2 +- 3 files changed, 154 insertions(+), 22 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 2e2a8c0584f..19450d86bba 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -455,8 +455,8 @@ build.json @home-assistant/supervisor /tests/components/huisbaasje/ @dennisschroer /homeassistant/components/humidifier/ @home-assistant/core @Shulyaka /tests/components/humidifier/ @home-assistant/core @Shulyaka -/homeassistant/components/hunterdouglas_powerview/ @bdraco -/tests/components/hunterdouglas_powerview/ @bdraco +/homeassistant/components/hunterdouglas_powerview/ @bdraco @trullock +/tests/components/hunterdouglas_powerview/ @bdraco @trullock /homeassistant/components/hvv_departures/ @vigonotion /tests/components/hvv_departures/ @vigonotion /homeassistant/components/hydrawise/ @ptcryan diff --git a/homeassistant/components/hunterdouglas_powerview/cover.py b/homeassistant/components/hunterdouglas_powerview/cover.py index 2ef1cc46adf..493b5d53639 100644 --- a/homeassistant/components/hunterdouglas_powerview/cover.py +++ b/homeassistant/components/hunterdouglas_powerview/cover.py @@ -1,4 +1,5 @@ """Support for hunter douglas shades.""" +from abc import abstractmethod import asyncio from contextlib import suppress import logging @@ -8,12 +9,14 @@ from aiopvapi.resources.shade import ( ATTR_POSKIND1, MAX_POSITION, MIN_POSITION, + Silhouette, factory as PvShade, ) import async_timeout from homeassistant.components.cover import ( ATTR_POSITION, + ATTR_TILT_POSITION, CoverDeviceClass, CoverEntity, CoverEntityFeature, @@ -49,6 +52,12 @@ PARALLEL_UPDATES = 1 RESYNC_DELAY = 60 +POSKIND_NONE = 0 +POSKIND_PRIMARY = 1 +POSKIND_SECONDARY = 2 +POSKIND_VANE = 3 +POSKIND_ERROR = 4 + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -82,24 +91,39 @@ async def async_setup_entry( room_id = shade.raw_data.get(ROOM_ID_IN_SHADE) room_name = room_data.get(room_id, {}).get(ROOM_NAME_UNICODE, "") entities.append( - PowerViewShade( + create_powerview_shade_entity( coordinator, device_info, room_name, shade, name_before_refresh ) ) async_add_entities(entities) -def hd_position_to_hass(hd_position): +def create_powerview_shade_entity( + coordinator, device_info, room_name, shade, name_before_refresh +): + """Create a PowerViewShade entity.""" + + if isinstance(shade, Silhouette): + return PowerViewShadeSilhouette( + coordinator, device_info, room_name, shade, name_before_refresh + ) + + return PowerViewShade( + coordinator, device_info, room_name, shade, name_before_refresh + ) + + +def hd_position_to_hass(hd_position, max_val): """Convert hunter douglas position to hass position.""" - return round((hd_position / MAX_POSITION) * 100) + return round((hd_position / max_val) * 100) -def hass_position_to_hd(hass_position): +def hass_position_to_hd(hass_position, max_val): """Convert hass position to hunter douglas position.""" - return int(hass_position / 100 * MAX_POSITION) + return int(hass_position / 100 * max_val) -class PowerViewShade(ShadeEntity, CoverEntity): +class PowerViewShadeBase(ShadeEntity, CoverEntity): """Representation of a powerview shade.""" # The hub frequently reports stale states @@ -113,12 +137,7 @@ class PowerViewShade(ShadeEntity, CoverEntity): self._is_closing = False self._last_action_timestamp = 0 self._scheduled_transition_update = None - self._current_cover_position = MIN_POSITION - self._attr_supported_features = ( - CoverEntityFeature.OPEN - | CoverEntityFeature.CLOSE - | CoverEntityFeature.SET_POSITION - ) + self._current_hd_cover_position = MIN_POSITION if self._device_info[DEVICE_MODEL] != LEGACY_DEVICE_MODEL: self._attr_supported_features |= CoverEntityFeature.STOP self._forced_resync = None @@ -131,7 +150,7 @@ class PowerViewShade(ShadeEntity, CoverEntity): @property def is_closed(self): """Return if the cover is closed.""" - return self._current_cover_position == MIN_POSITION + return self._current_hd_cover_position == MIN_POSITION @property def is_opening(self): @@ -146,7 +165,7 @@ class PowerViewShade(ShadeEntity, CoverEntity): @property def current_cover_position(self): """Return the current position of cover.""" - return hd_position_to_hass(self._current_cover_position) + return hd_position_to_hass(self._current_hd_cover_position, MAX_POSITION) @property def device_class(self): @@ -181,14 +200,18 @@ class PowerViewShade(ShadeEntity, CoverEntity): async def _async_move(self, target_hass_position): """Move the shade to a position.""" - current_hass_position = hd_position_to_hass(self._current_cover_position) + current_hass_position = hd_position_to_hass( + self._current_hd_cover_position, MAX_POSITION + ) steps_to_move = abs(current_hass_position - target_hass_position) self._async_schedule_update_for_transition(steps_to_move) self._async_update_from_command( await self._shade.move( { - ATTR_POSITION1: hass_position_to_hd(target_hass_position), - ATTR_POSKIND1: 1, + ATTR_POSITION1: hass_position_to_hd( + target_hass_position, MAX_POSITION + ), + ATTR_POSKIND1: POSKIND_PRIMARY, } ) ) @@ -218,11 +241,15 @@ class PowerViewShade(ShadeEntity, CoverEntity): """Update the current cover position from the data.""" _LOGGER.debug("Raw data update: %s", self._shade.raw_data) position_data = self._shade.raw_data.get(ATTR_POSITION_DATA, {}) - if ATTR_POSITION1 in position_data: - self._current_cover_position = int(position_data[ATTR_POSITION1]) + self._async_process_updated_position_data(position_data) self._is_opening = False self._is_closing = False + @callback + @abstractmethod + def _async_process_updated_position_data(self, position_data): + """Process position data.""" + @callback def _async_cancel_scheduled_transition_update(self): """Cancel any previous updates.""" @@ -299,3 +326,108 @@ class PowerViewShade(ShadeEntity, CoverEntity): return self._async_process_new_shade_data(self.coordinator.data[self._shade.id]) self.async_write_ha_state() + + +class PowerViewShade(PowerViewShadeBase): + """Represent a standard shade.""" + + _attr_supported_features = ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + ) + + @callback + def _async_process_updated_position_data(self, position_data): + """Process position data.""" + if ATTR_POSITION1 in position_data: + self._current_hd_cover_position = int(position_data[ATTR_POSITION1]) + + +class PowerViewShadeWithTilt(PowerViewShade): + """Representation of a PowerView shade with tilt capabilities.""" + + _attr_supported_features = ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.SET_POSITION + | CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.STOP_TILT + | CoverEntityFeature.SET_TILT_POSITION + ) + + _max_tilt = MAX_POSITION + _tilt_steps = 10 + + def __init__(self, coordinator, device_info, room_name, shade, name): + """Initialize the shade.""" + super().__init__(coordinator, device_info, room_name, shade, name) + self._attr_current_cover_tilt_position = 0 + + async def async_open_cover_tilt(self, **kwargs): + """Open the cover tilt.""" + current_hass_position = hd_position_to_hass( + self._current_hd_cover_position, MAX_POSITION + ) + steps_to_move = current_hass_position + self._tilt_steps + self._async_schedule_update_for_transition(steps_to_move) + self._async_update_from_command(await self._shade.tilt_open()) + + async def async_close_cover_tilt(self, **kwargs): + """Close the cover tilt.""" + current_hass_position = hd_position_to_hass( + self._current_hd_cover_position, MAX_POSITION + ) + steps_to_move = current_hass_position + self._tilt_steps + self._async_schedule_update_for_transition(steps_to_move) + self._async_update_from_command(await self._shade.tilt_close()) + + async def async_set_cover_tilt_position(self, **kwargs): + """Move the cover tilt to a specific position.""" + target_hass_tilt_position = kwargs[ATTR_TILT_POSITION] + current_hass_position = hd_position_to_hass( + self._current_hd_cover_position, MAX_POSITION + ) + steps_to_move = current_hass_position + self._tilt_steps + + self._async_schedule_update_for_transition(steps_to_move) + self._async_update_from_command( + await self._shade.move( + { + ATTR_POSITION1: hass_position_to_hd( + target_hass_tilt_position, self._max_tilt + ), + ATTR_POSKIND1: POSKIND_VANE, + } + ) + ) + + async def async_stop_cover_tilt(self, **kwargs): + """Stop the cover tilting.""" + # Cancel any previous updates + await self.async_stop_cover() + + @callback + def _async_process_updated_position_data(self, position_data): + """Process position data.""" + if ATTR_POSKIND1 not in position_data: + return + if int(position_data[ATTR_POSKIND1]) == POSKIND_PRIMARY: + self._current_hd_cover_position = int(position_data[ATTR_POSITION1]) + self._attr_current_cover_tilt_position = 0 + if int(position_data[ATTR_POSKIND1]) == POSKIND_VANE: + self._current_hd_cover_position = MIN_POSITION + self._attr_current_cover_tilt_position = hd_position_to_hass( + int(position_data[ATTR_POSITION1]), self._max_tilt + ) + + +class PowerViewShadeSilhouette(PowerViewShadeWithTilt): + """Representation of a Silhouette PowerView shade.""" + + def __init__(self, coordinator, device_info, room_name, shade, name): + """Initialize the shade.""" + super().__init__(coordinator, device_info, room_name, shade, name) + self._max_tilt = 32767 + self._tilt_steps = 4 diff --git a/homeassistant/components/hunterdouglas_powerview/manifest.json b/homeassistant/components/hunterdouglas_powerview/manifest.json index c34f53f47b4..af6aea17de3 100644 --- a/homeassistant/components/hunterdouglas_powerview/manifest.json +++ b/homeassistant/components/hunterdouglas_powerview/manifest.json @@ -3,7 +3,7 @@ "name": "Hunter Douglas PowerView", "documentation": "https://www.home-assistant.io/integrations/hunterdouglas_powerview", "requirements": ["aiopvapi==1.6.19"], - "codeowners": ["@bdraco"], + "codeowners": ["@bdraco", "@trullock"], "config_flow": true, "homekit": { "models": ["PowerView"] From cec7e533022202cfa15f591d7be2468c0e607940 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 21:15:18 -0500 Subject: [PATCH 286/930] Fix zeroconf tests (#71557) --- tests/components/zeroconf/test_init.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/components/zeroconf/test_init.py b/tests/components/zeroconf/test_init.py index 3487b57e482..900dbeb8431 100644 --- a/tests/components/zeroconf/test_init.py +++ b/tests/components/zeroconf/test_init.py @@ -147,7 +147,17 @@ def get_zeroconf_info_mock_model(model): async def test_setup(hass, mock_async_zeroconf): """Test configured options for a device are loaded via config entry.""" - with patch.object( + mock_zc = { + "_http._tcp.local.": [ + { + "domain": "shelly", + "name": "shelly*", + "properties": {"macaddress": "ffaadd*"}, + } + ], + "_Volumio._tcp.local.": [{"domain": "volumio"}], + } + with patch.dict(zc_gen.ZEROCONF, mock_zc, clear=True,), patch.object( hass.config_entries.flow, "async_init" ) as mock_config_flow, patch.object( zeroconf, "HaAsyncServiceBrowser", side_effect=service_update_mock @@ -161,7 +171,7 @@ async def test_setup(hass, mock_async_zeroconf): assert len(mock_service_browser.mock_calls) == 1 expected_flow_calls = 0 - for matching_components in zc_gen.ZEROCONF.values(): + for matching_components in mock_zc.values(): domains = set() for component in matching_components: if len(component) == 1: From 2b30bda6c8371bbf546eeaa4b34393cbe58f9f86 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Mon, 9 May 2022 04:33:20 +0200 Subject: [PATCH 287/930] Add binary sensor platform to devolo Home Network (#60301) Co-authored-by: J. Nick Koston --- .../devolo_home_network/binary_sensor.py | 99 +++++++++++++++++++ .../components/devolo_home_network/const.py | 3 +- .../components/devolo_home_network/entity.py | 17 ++-- .../devolo_home_network/__init__.py | 1 + tests/components/devolo_home_network/const.py | 19 +++- .../devolo_home_network/test_binary_sensor.py | 83 ++++++++++++++++ 6 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 homeassistant/components/devolo_home_network/binary_sensor.py create mode 100644 tests/components/devolo_home_network/test_binary_sensor.py diff --git a/homeassistant/components/devolo_home_network/binary_sensor.py b/homeassistant/components/devolo_home_network/binary_sensor.py new file mode 100644 index 00000000000..6bc02d802f5 --- /dev/null +++ b/homeassistant/components/devolo_home_network/binary_sensor.py @@ -0,0 +1,99 @@ +"""Platform for binary sensor integration.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from devolo_plc_api.device import Device + +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_PLUG, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import CONNECTED_PLC_DEVICES, CONNECTED_TO_ROUTER, DOMAIN +from .entity import DevoloEntity + + +def _is_connected_to_router(entity: DevoloBinarySensorEntity) -> bool: + """Check, if device is attached to the router.""" + return all( + device["attached_to_router"] + for device in entity.coordinator.data["network"]["devices"] + if device["mac_address"] == entity.device.mac + ) + + +@dataclass +class DevoloBinarySensorRequiredKeysMixin: + """Mixin for required keys.""" + + value_func: Callable[[DevoloBinarySensorEntity], bool] + + +@dataclass +class DevoloBinarySensorEntityDescription( + BinarySensorEntityDescription, DevoloBinarySensorRequiredKeysMixin +): + """Describes devolo sensor entity.""" + + +SENSOR_TYPES: dict[str, DevoloBinarySensorEntityDescription] = { + CONNECTED_TO_ROUTER: DevoloBinarySensorEntityDescription( + key=CONNECTED_TO_ROUTER, + device_class=DEVICE_CLASS_PLUG, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + icon="mdi:router-network", + name="Connected to router", + value_func=_is_connected_to_router, + ), +} + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Get all devices and sensors and setup them via config entry.""" + device: Device = hass.data[DOMAIN][entry.entry_id]["device"] + coordinators: dict[str, DataUpdateCoordinator] = hass.data[DOMAIN][entry.entry_id][ + "coordinators" + ] + + entities: list[BinarySensorEntity] = [] + if device.plcnet: + entities.append( + DevoloBinarySensorEntity( + coordinators[CONNECTED_PLC_DEVICES], + SENSOR_TYPES[CONNECTED_TO_ROUTER], + device, + entry.title, + ) + ) + async_add_entities(entities) + + +class DevoloBinarySensorEntity(DevoloEntity, BinarySensorEntity): + """Representation of a devolo binary sensor.""" + + def __init__( + self, + coordinator: DataUpdateCoordinator, + description: DevoloBinarySensorEntityDescription, + device: Device, + device_name: str, + ) -> None: + """Initialize entity.""" + self.entity_description: DevoloBinarySensorEntityDescription = description + super().__init__(coordinator, device, device_name) + + @property + def is_on(self) -> bool: + """State of the binary sensor.""" + return self.entity_description.value_func(self) diff --git a/homeassistant/components/devolo_home_network/const.py b/homeassistant/components/devolo_home_network/const.py index bd7170bfde5..2dfdd3c1d9a 100644 --- a/homeassistant/components/devolo_home_network/const.py +++ b/homeassistant/components/devolo_home_network/const.py @@ -5,7 +5,7 @@ from datetime import timedelta from homeassistant.const import Platform DOMAIN = "devolo_home_network" -PLATFORMS = [Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] PRODUCT = "product" SERIAL_NUMBER = "serial_number" @@ -15,5 +15,6 @@ LONG_UPDATE_INTERVAL = timedelta(minutes=5) SHORT_UPDATE_INTERVAL = timedelta(seconds=15) CONNECTED_PLC_DEVICES = "connected_plc_devices" +CONNECTED_TO_ROUTER = "connected_to_router" CONNECTED_WIFI_CLIENTS = "connected_wifi_clients" NEIGHBORING_WIFI_NETWORKS = "neighboring_wifi_networks" diff --git a/homeassistant/components/devolo_home_network/entity.py b/homeassistant/components/devolo_home_network/entity.py index dbfe0e4035a..dd26324bc2c 100644 --- a/homeassistant/components/devolo_home_network/entity.py +++ b/homeassistant/components/devolo_home_network/entity.py @@ -21,17 +21,14 @@ class DevoloEntity(CoordinatorEntity): """Initialize a devolo home network device.""" super().__init__(coordinator) - self._device = device - self._device_name = device_name + self.device = device self._attr_device_info = DeviceInfo( - configuration_url=f"http://{self._device.ip}", - identifiers={(DOMAIN, str(self._device.serial_number))}, + configuration_url=f"http://{device.ip}", + identifiers={(DOMAIN, str(device.serial_number))}, manufacturer="devolo", - model=self._device.product, - name=self._device_name, - sw_version=self._device.firmware_version, - ) - self._attr_unique_id = ( - f"{self._device.serial_number}_{self.entity_description.key}" + model=device.product, + name=device_name, + sw_version=device.firmware_version, ) + self._attr_unique_id = f"{device.serial_number}_{self.entity_description.key}" diff --git a/tests/components/devolo_home_network/__init__.py b/tests/components/devolo_home_network/__init__.py index 913193be3f7..1c10d7a59ef 100644 --- a/tests/components/devolo_home_network/__init__.py +++ b/tests/components/devolo_home_network/__init__.py @@ -28,5 +28,6 @@ def configure_integration(hass: HomeAssistant) -> MockConfigEntry: async def async_connect(self, session_instance: Any = None): """Give a mocked device the needed properties.""" + self.mac = DISCOVERY_INFO.properties["PlcMacAddress"] self.plcnet = PlcNetApi(IP, None, dataclasses.asdict(DISCOVERY_INFO)) self.device = DeviceApi(IP, None, dataclasses.asdict(DISCOVERY_INFO)) diff --git a/tests/components/devolo_home_network/const.py b/tests/components/devolo_home_network/const.py index 0e48833a78b..516a19f3421 100644 --- a/tests/components/devolo_home_network/const.py +++ b/tests/components/devolo_home_network/const.py @@ -62,6 +62,12 @@ NEIGHBOR_ACCESS_POINTS = { PLCNET = { "network": { + "devices": [ + { + "mac_address": "AA:BB:CC:DD:EE:FF", + "attached_to_router": False, + } + ], "data_rates": [ { "mac_address_from": "AA:BB:CC:DD:EE:FF", @@ -70,6 +76,17 @@ PLCNET = { "tx_rate": 0.0, }, ], - "devices": [], + } +} + +PLCNET_ATTACHED = { + "network": { + "devices": [ + { + "mac_address": "AA:BB:CC:DD:EE:FF", + "attached_to_router": True, + } + ], + "data_rates": [], } } diff --git a/tests/components/devolo_home_network/test_binary_sensor.py b/tests/components/devolo_home_network/test_binary_sensor.py new file mode 100644 index 00000000000..3d181da6106 --- /dev/null +++ b/tests/components/devolo_home_network/test_binary_sensor.py @@ -0,0 +1,83 @@ +"""Tests for the devolo Home Network sensors.""" +from unittest.mock import AsyncMock, patch + +from devolo_plc_api.exceptions.device import DeviceUnavailable +import pytest + +from homeassistant.components.binary_sensor import DOMAIN +from homeassistant.components.devolo_home_network.const import ( + CONNECTED_TO_ROUTER, + LONG_UPDATE_INTERVAL, +) +from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry +from homeassistant.helpers.entity import EntityCategory +from homeassistant.util import dt + +from . import configure_integration +from .const import PLCNET_ATTACHED + +from tests.common import async_fire_time_changed + + +@pytest.mark.usefixtures("mock_device", "mock_zeroconf") +async def test_binary_sensor_setup(hass: HomeAssistant): + """Test default setup of the binary sensor component.""" + entry = configure_integration(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(f"{DOMAIN}.{CONNECTED_TO_ROUTER}") is None + + await hass.config_entries.async_unload(entry.entry_id) + + +@pytest.mark.usefixtures("mock_device", "mock_zeroconf") +async def test_update_attached_to_router(hass: HomeAssistant): + """Test state change of a attached_to_router binary sensor device.""" + state_key = f"{DOMAIN}.{CONNECTED_TO_ROUTER}" + entry = configure_integration(hass) + + er = entity_registry.async_get(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + # Enable entity + er.async_update_entity(state_key, disabled_by=None) + await hass.async_block_till_done() + async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_OFF + + assert er.async_get(state_key).entity_category == EntityCategory.DIAGNOSTIC + + # Emulate device failure + with patch( + "devolo_plc_api.plcnet_api.plcnetapi.PlcNetApi.async_get_network_overview", + side_effect=DeviceUnavailable, + ): + async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_UNAVAILABLE + + # Emulate state change + with patch( + "devolo_plc_api.plcnet_api.plcnetapi.PlcNetApi.async_get_network_overview", + new=AsyncMock(return_value=PLCNET_ATTACHED), + ): + async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == STATE_ON + + await hass.config_entries.async_unload(entry.entry_id) From 15a5878a39a70802a87737d2274c32b1adf75952 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 22:38:22 -0500 Subject: [PATCH 288/930] Use MediaPlayerEntityFeature in ws66i (#71553) --- .../components/ws66i/media_player.py | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/ws66i/media_player.py b/homeassistant/components/ws66i/media_player.py index 2f485748c02..c0e62fe773c 100644 --- a/homeassistant/components/ws66i/media_player.py +++ b/homeassistant/components/ws66i/media_player.py @@ -1,17 +1,11 @@ """Support for interfacing with WS66i 6 zone home audio controller.""" from copy import deepcopy -import logging from pyws66i import WS66i, ZoneStatus -from homeassistant.components.media_player import MediaPlayerEntity -from homeassistant.components.media_player.const import ( - SUPPORT_SELECT_SOURCE, - SUPPORT_TURN_OFF, - SUPPORT_TURN_ON, - SUPPORT_VOLUME_MUTE, - SUPPORT_VOLUME_SET, - SUPPORT_VOLUME_STEP, +from homeassistant.components.media_player import ( + MediaPlayerEntity, + MediaPlayerEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_OFF, STATE_ON @@ -28,19 +22,8 @@ from .const import DOMAIN, SERVICE_RESTORE, SERVICE_SNAPSHOT from .coordinator import Ws66iDataUpdateCoordinator from .models import Ws66iData -_LOGGER = logging.getLogger(__name__) - PARALLEL_UPDATES = 1 -SUPPORT_WS66I = ( - SUPPORT_VOLUME_MUTE - | SUPPORT_VOLUME_SET - | SUPPORT_VOLUME_STEP - | SUPPORT_TURN_ON - | SUPPORT_TURN_OFF - | SUPPORT_SELECT_SOURCE -) - MAX_VOL = 38 @@ -105,7 +88,14 @@ class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): self._attr_source_list = ws66i_data.sources.name_list self._attr_unique_id = f"{entry_id}_{self._zone_id}" self._attr_name = f"Zone {self._zone_id}" - self._attr_supported_features = SUPPORT_WS66I + self._attr_supported_features = ( + MediaPlayerEntityFeature.VOLUME_MUTE + | MediaPlayerEntityFeature.VOLUME_SET + | MediaPlayerEntityFeature.VOLUME_STEP + | MediaPlayerEntityFeature.TURN_ON + | MediaPlayerEntityFeature.TURN_OFF + | MediaPlayerEntityFeature.SELECT_SOURCE + ) self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, str(self.unique_id))}, name=self.name, From 24d7a464e1b09e09b98e8d727adca71825c3ee5f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 May 2022 23:47:26 -0500 Subject: [PATCH 289/930] Refactor logbook to reduce overhead and complexity (#71509) --- homeassistant/components/logbook/__init__.py | 477 ++++++++---------- homeassistant/scripts/benchmark/__init__.py | 52 -- tests/components/alexa/test_init.py | 64 ++- tests/components/automation/test_init.py | 40 +- tests/components/automation/test_logbook.py | 51 +- tests/components/deconz/test_logbook.py | 149 +++--- .../google_assistant/test_logbook.py | 72 ++- tests/components/homekit/test_init.py | 50 +- tests/components/logbook/common.py | 58 +++ tests/components/logbook/test_init.py | 106 ++-- tests/components/script/test_init.py | 33 +- tests/components/shelly/test_logbook.py | 101 ++-- 12 files changed, 573 insertions(+), 680 deletions(-) create mode 100644 tests/components/logbook/common.py diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index f29276489a2..5ec03b3aaa4 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -326,9 +326,10 @@ class LogbookView(HomeAssistantView): def humanify( hass: HomeAssistant, - events: Generator[LazyEventPartialState, None, None], - entity_attr_cache: EntityAttributeCache, - context_lookup: dict[str | None, LazyEventPartialState | None], + rows: Generator[Row, None, None], + entity_name_cache: EntityNameCache, + event_cache: EventCache, + context_augmenter: ContextAugmenter, ) -> Generator[dict[str, Any], None, None]: """Generate a converted list of events into Entry objects. @@ -336,25 +337,24 @@ def humanify( - if Home Assistant stop and start happen in same minute call it restarted """ external_events = hass.data.get(DOMAIN, {}) + # Continuous sensors, will be excluded from the logbook + continuous_sensors = {} # Group events in batches of GROUP_BY_MINUTES - for _, g_events in groupby( - events, lambda event: event.time_fired_minute // GROUP_BY_MINUTES + for _, g_rows in groupby( + rows, lambda row: row.time_fired.minute // GROUP_BY_MINUTES # type: ignore[no-any-return] ): - events_batch = list(g_events) - - # Continuous sensors, will be excluded from the logbook - continuous_sensors = {} + rows_batch = list(g_rows) # Group HA start/stop events # Maps minute of event to 1: stop, 2: stop + start start_stop_events = {} # Process events - for event in events_batch: - if event.event_type == EVENT_STATE_CHANGED: - entity_id = event.entity_id + for row in rows_batch: + if row.event_type == EVENT_STATE_CHANGED: + entity_id = row.entity_id if ( entity_id in continuous_sensors or split_entity_id(entity_id)[0] != SENSOR_DOMAIN @@ -363,22 +363,22 @@ def humanify( assert entity_id is not None continuous_sensors[entity_id] = _is_sensor_continuous(hass, entity_id) - elif event.event_type == EVENT_HOMEASSISTANT_STOP: - if event.time_fired_minute in start_stop_events: + elif row.event_type == EVENT_HOMEASSISTANT_STOP: + if row.time_fired.minute in start_stop_events: continue - start_stop_events[event.time_fired_minute] = 1 + start_stop_events[row.time_fired.minute] = 1 - elif event.event_type == EVENT_HOMEASSISTANT_START: - if event.time_fired_minute not in start_stop_events: + elif row.event_type == EVENT_HOMEASSISTANT_START: + if row.time_fired.minute not in start_stop_events: continue - start_stop_events[event.time_fired_minute] = 2 + start_stop_events[row.time_fired.minute] = 2 # Yield entries - for event in events_batch: - if event.event_type == EVENT_STATE_CHANGED: - entity_id = event.entity_id + for row in rows_batch: + if row.event_type == EVENT_STATE_CHANGED: + entity_id = row.entity_id assert entity_id is not None if continuous_sensors.get(entity_id): @@ -386,74 +386,59 @@ def humanify( continue data = { - "when": event.time_fired_isoformat, - "name": _entity_name_from_event( - entity_id, event, entity_attr_cache - ), - "state": event.state, + "when": _row_time_fired_isoformat(row), + "name": entity_name_cache.get(entity_id, row), + "state": row.state, "entity_id": entity_id, } - if icon := event.attributes_icon: + if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): data["icon"] = icon - if event.context_user_id: - data["context_user_id"] = event.context_user_id + if row.context_user_id: + data["context_user_id"] = row.context_user_id - _augment_data_with_context( - data, - entity_id, - event, - context_lookup, - entity_attr_cache, - external_events, - ) + context_augmenter.augment(data, entity_id, row) yield data - elif event.event_type in external_events: - domain, describe_event = external_events[event.event_type] - data = describe_event(event) - data["when"] = event.time_fired_isoformat + elif row.event_type in external_events: + domain, describe_event = external_events[row.event_type] + data = describe_event(event_cache.get(row)) + data["when"] = _row_time_fired_isoformat(row) data["domain"] = domain - if event.context_user_id: - data["context_user_id"] = event.context_user_id + if row.context_user_id: + data["context_user_id"] = row.context_user_id - _augment_data_with_context( - data, - data.get(ATTR_ENTITY_ID), - event, - context_lookup, - entity_attr_cache, - external_events, - ) + entity_id = data.get(ATTR_ENTITY_ID) + context_augmenter.augment(data, entity_id, row) yield data - elif event.event_type == EVENT_HOMEASSISTANT_START: - if start_stop_events.get(event.time_fired_minute) == 2: + elif row.event_type == EVENT_HOMEASSISTANT_START: + if start_stop_events.get(row.time_fired.minute) == 2: continue - yield { - "when": event.time_fired_isoformat, + "when": _row_time_fired_isoformat(row), "name": "Home Assistant", "message": "started", "domain": HA_DOMAIN, } - elif event.event_type == EVENT_HOMEASSISTANT_STOP: - if start_stop_events.get(event.time_fired_minute) == 2: + elif row.event_type == EVENT_HOMEASSISTANT_STOP: + if start_stop_events.get(row.time_fired.minute) == 2: action = "restarted" else: action = "stopped" yield { - "when": event.time_fired_isoformat, + "when": _row_time_fired_isoformat(row), "name": "Home Assistant", "message": action, "domain": HA_DOMAIN, } - elif event.event_type == EVENT_LOGBOOK_ENTRY: + elif row.event_type == EVENT_LOGBOOK_ENTRY: + event = event_cache.get(row) event_data = event.data domain = event_data.get(ATTR_DOMAIN) entity_id = event_data.get(ATTR_ENTITY_ID) @@ -462,25 +447,17 @@ def humanify( domain = split_entity_id(str(entity_id))[0] data = { - "when": event.time_fired_isoformat, + "when": _row_time_fired_isoformat(row), "name": event_data.get(ATTR_NAME), "message": event_data.get(ATTR_MESSAGE), "domain": domain, "entity_id": entity_id, } - if event.context_user_id: - data["context_user_id"] = event.context_user_id - - _augment_data_with_context( - data, - entity_id, - event, - context_lookup, - entity_attr_cache, - external_events, - ) + if row.context_user_id: + data["context_user_id"] = row.context_user_id + context_augmenter.augment(data, entity_id, row) yield data @@ -499,21 +476,24 @@ def _get_events( entity_ids and context_id ), "can't pass in both entity_ids and context_id" - entity_attr_cache = EntityAttributeCache(hass) + entity_name_cache = EntityNameCache(hass) event_data_cache: dict[str, dict[str, Any]] = {} - context_lookup: dict[str | None, LazyEventPartialState | None] = {None: None} + context_lookup: dict[str | None, Row | None] = {None: None} + event_cache = EventCache(event_data_cache) + external_events = hass.data.get(DOMAIN, {}) + context_augmenter = ContextAugmenter( + context_lookup, entity_name_cache, external_events, event_cache + ) - def yield_events(query: Query) -> Generator[LazyEventPartialState, None, None]: + def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" for row in query.yield_per(1000): - event = LazyEventPartialState(row, event_data_cache) - context_lookup.setdefault(event.context_id, event) - if event.event_type == EVENT_CALL_SERVICE: - continue - if event.event_type == EVENT_STATE_CHANGED or _keep_event( - hass, event, entities_filter + context_lookup.setdefault(row.context_id, row) + if row.event_type != EVENT_CALL_SERVICE and ( + row.event_type == EVENT_STATE_CHANGED + or _keep_row(hass, row, entities_filter) ): - yield event + yield row if entity_ids is not None: entities_filter = generate_filter([], entity_ids, [], []) @@ -568,7 +548,13 @@ def _get_events( query = query.order_by(Events.time_fired) return list( - humanify(hass, yield_events(query), entity_attr_cache, context_lookup) + humanify( + hass, + yield_rows(query), + entity_name_cache, + event_cache, + context_augmenter, + ) ) @@ -716,103 +702,108 @@ def _apply_event_entity_id_matchers( return events_query.filter(sqlalchemy.or_(*ors)) -def _keep_event( +def _keep_row( hass: HomeAssistant, - event: LazyEventPartialState, + row: Row, entities_filter: EntityFilter | Callable[[str], bool] | None = None, ) -> bool: - if event.event_type in HOMEASSISTANT_EVENTS: + event_type = row.event_type + if event_type in HOMEASSISTANT_EVENTS: return entities_filter is None or entities_filter(HA_DOMAIN_ENTITY_ID) - if entity_id := event.data_entity_id: + if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): return entities_filter is None or entities_filter(entity_id) - if event.event_type in hass.data[DOMAIN]: + if event_type in hass.data[DOMAIN]: # If the entity_id isn't described, use the domain that describes # the event for filtering. - domain = hass.data[DOMAIN][event.event_type][0] + domain = hass.data[DOMAIN][event_type][0] else: - domain = event.data_domain + domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) return domain is not None and ( entities_filter is None or entities_filter(f"{domain}._") ) -def _augment_data_with_context( - data: dict[str, Any], - entity_id: str | None, - event: LazyEventPartialState, - context_lookup: dict[str | None, LazyEventPartialState | None], - entity_attr_cache: EntityAttributeCache, - external_events: dict[ - str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] - ], -) -> None: - if not (context_event := context_lookup.get(event.context_id)): - return +class ContextAugmenter: + """Augment data with context trace.""" - if event == context_event: - # This is the first event with the given ID. Was it directly caused by - # a parent event? - if event.context_parent_id: - context_event = context_lookup.get(event.context_parent_id) - # Ensure the (parent) context_event exists and is not the root cause of - # this log entry. - if not context_event or event == context_event: + def __init__( + self, + context_lookup: dict[str | None, Row | None], + entity_name_cache: EntityNameCache, + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ], + event_cache: EventCache, + ) -> None: + """Init the augmenter.""" + self.context_lookup = context_lookup + self.entity_name_cache = entity_name_cache + self.external_events = external_events + self.event_cache = event_cache + + def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None: + """Augment data from the row and cache.""" + if not (context_row := self.context_lookup.get(row.context_id)): return - event_type = context_event.event_type + if _rows_match(row, context_row): + # This is the first event with the given ID. Was it directly caused by + # a parent event? + if ( + not row.context_parent_id + or (context_row := self.context_lookup.get(row.context_parent_id)) + is None + ): + return + # Ensure the (parent) context_event exists and is not the root cause of + # this log entry. + if _rows_match(row, context_row): + return - # State change - if context_entity_id := context_event.entity_id: - data["context_entity_id"] = context_entity_id - data["context_entity_id_name"] = _entity_name_from_event( - context_entity_id, context_event, entity_attr_cache + event_type = context_row.event_type + + # State change + if context_entity_id := context_row.entity_id: + data["context_entity_id"] = context_entity_id + data["context_entity_id_name"] = self.entity_name_cache.get( + context_entity_id, context_row + ) + data["context_event_type"] = event_type + return + + # Call service + if event_type == EVENT_CALL_SERVICE: + event = self.event_cache.get(context_row) + event_data = event.data + data["context_domain"] = event_data.get(ATTR_DOMAIN) + data["context_service"] = event_data.get(ATTR_SERVICE) + data["context_event_type"] = event_type + return + + if not entity_id: + return + + attr_entity_id = _row_event_data_extract(context_row, ENTITY_ID_JSON_EXTRACT) + if attr_entity_id is None or ( + event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id + ): + return + + data["context_entity_id"] = attr_entity_id + data["context_entity_id_name"] = self.entity_name_cache.get( + attr_entity_id, context_row ) data["context_event_type"] = event_type - return - event_data = context_event.data - - # Call service - if event_type == EVENT_CALL_SERVICE: - event_data = context_event.data - data["context_domain"] = event_data.get(ATTR_DOMAIN) - data["context_service"] = event_data.get(ATTR_SERVICE) - data["context_event_type"] = event_type - return - - if not entity_id or context_event == event: - return - - if (attr_entity_id := context_event.data_entity_id) is None or ( - event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id - ): - return - - data["context_entity_id"] = attr_entity_id - data["context_entity_id_name"] = _entity_name_from_event( - attr_entity_id, context_event, entity_attr_cache - ) - data["context_event_type"] = event_type - - if event_type in external_events: - domain, describe_event = external_events[event_type] - data["context_domain"] = domain - if name := describe_event(context_event).get(ATTR_NAME): - data["context_name"] = name - - -def _entity_name_from_event( - entity_id: str, - event: LazyEventPartialState, - entity_attr_cache: EntityAttributeCache, -) -> str: - """Extract the entity name from the event using the cache if possible.""" - return entity_attr_cache.get( - entity_id, ATTR_FRIENDLY_NAME, event - ) or split_entity_id(entity_id)[1].replace("_", " ") + if event_type in self.external_events: + domain, describe_event = self.external_events[event_type] + data["context_domain"] = domain + event = self.event_cache.get(context_row) + if name := describe_event(event).get(ATTR_NAME): + data["context_name"] = name def _is_sensor_continuous( @@ -834,23 +825,46 @@ def _is_sensor_continuous( ) +def _rows_match(row: Row, other_row: Row) -> bool: + """Check of rows match by using the same method as Events __hash__.""" + return bool( + row.event_type == other_row.event_type + and row.context_id == other_row.context_id + and row.time_fired == other_row.time_fired + ) + + +def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None: + """Extract from event_data row.""" + result = extractor.search(row.shared_data or row.event_data or "") + return result.group(1) if result else None + + +def _row_attributes_extract(row: Row, extractor: re.Pattern) -> str | None: + """Extract from attributes row.""" + result = extractor.search(row.shared_attrs or row.attributes or "") + return result.group(1) if result else None + + +def _row_time_fired_isoformat(row: Row) -> dt | None: + """Convert the row timed_fired to isoformat.""" + return process_timestamp_to_utc_isoformat(row.time_fired) or dt_util.utcnow() + + class LazyEventPartialState: """A lazy version of core Event with limited State joined in.""" __slots__ = [ - "_row", + "row", "_event_data", - "_time_fired_isoformat", - "_attributes", + "_event_data_cache", "event_type", "entity_id", "state", - "_domain", "context_id", "context_user_id", "context_parent_id", - "time_fired_minute", - "_event_data_cache", + "data", ] def __init__( @@ -859,83 +873,28 @@ class LazyEventPartialState: event_data_cache: dict[str, dict[str, Any]], ) -> None: """Init the lazy event.""" - self._row = row + self.row = row self._event_data: dict[str, Any] | None = None - self._time_fired_isoformat: dt | None = None - self._domain: str | None = None - self.event_type: str = self._row.event_type - self.entity_id: str | None = self._row.entity_id - self.state = self._row.state - self.context_id: str | None = self._row.context_id - self.context_user_id: str | None = self._row.context_user_id - self.context_parent_id: str | None = self._row.context_parent_id - self.time_fired_minute: int = self._row.time_fired.minute self._event_data_cache = event_data_cache - - @property - def attributes_icon(self) -> str | None: - """Extract the icon from the decoded attributes or json.""" - result = ICON_JSON_EXTRACT.search( - self._row.shared_attrs or self._row.attributes or "" - ) - return result.group(1) if result else None - - @property - def data_entity_id(self) -> str | None: - """Extract the entity id from the decoded data or json.""" - if self._event_data: - return self._event_data.get(ATTR_ENTITY_ID) - - result = ENTITY_ID_JSON_EXTRACT.search( - self._row.shared_data or self._row.event_data or "" - ) - return result.group(1) if result else None - - @property - def data_domain(self) -> str | None: - """Extract the domain from the decoded data or json.""" - result = DOMAIN_JSON_EXTRACT.search( - self._row.shared_data or self._row.event_data or "" - ) - return result.group(1) if result else None - - @property - def attributes_friendly_name(self) -> str | None: - """Extract the friendly name from the decoded attributes or json.""" - result = FRIENDLY_NAME_JSON_EXTRACT.search( - self._row.shared_attrs or self._row.attributes or "" - ) - return result.group(1) if result else None - - @property - def data(self) -> dict[str, Any]: - """Event data.""" - if self._event_data is None: - source: str = self._row.shared_data or self._row.event_data - if not source: - self._event_data = {} - elif event_data := self._event_data_cache.get(source): - self._event_data = event_data - else: - self._event_data = self._event_data_cache[source] = cast( - dict[str, Any], json.loads(source) - ) - return self._event_data - - @property - def time_fired_isoformat(self) -> dt | None: - """Time event was fired in utc isoformat.""" - if not self._time_fired_isoformat: - self._time_fired_isoformat = ( - process_timestamp_to_utc_isoformat(self._row.time_fired) - or dt_util.utcnow() + self.event_type: str = self.row.event_type + self.entity_id: str | None = self.row.entity_id + self.state = self.row.state + self.context_id: str | None = self.row.context_id + self.context_user_id: str | None = self.row.context_user_id + self.context_parent_id: str | None = self.row.context_parent_id + source: str = self.row.shared_data or self.row.event_data + if not source: + self.data = {} + elif event_data := self._event_data_cache.get(source): + self.data = event_data + else: + self.data = self._event_data_cache[source] = cast( + dict[str, Any], json.loads(source) ) - return self._time_fired_isoformat - -class EntityAttributeCache: - """A cache to lookup static entity_id attribute. +class EntityNameCache: + """A cache to lookup the name for an entity. This class should not be used to lookup attributes that are expected to change state. @@ -944,27 +903,37 @@ class EntityAttributeCache: def __init__(self, hass: HomeAssistant) -> None: """Init the cache.""" self._hass = hass - self._cache: dict[str, dict[str, Any]] = {} + self._names: dict[str, str] = {} - def get(self, entity_id: str, attribute: str, event: LazyEventPartialState) -> Any: - """Lookup an attribute for an entity or get it from the cache.""" - if entity_id in self._cache: - if attribute in self._cache[entity_id]: - return self._cache[entity_id][attribute] + def get(self, entity_id: str, row: Row) -> str: + """Lookup an the friendly name.""" + if entity_id in self._names: + return self._names[entity_id] + if (current_state := self._hass.states.get(entity_id)) and ( + friendly_name := current_state.attributes.get(ATTR_FRIENDLY_NAME) + ): + self._names[entity_id] = friendly_name + elif extracted_name := _row_attributes_extract(row, FRIENDLY_NAME_JSON_EXTRACT): + self._names[entity_id] = extracted_name else: - cache = self._cache[entity_id] = {} + return split_entity_id(entity_id)[1].replace("_", " ") - if current_state := self._hass.states.get(entity_id): - # Try the current state as its faster than decoding the - # attributes - cache[attribute] = current_state.attributes.get(attribute) - else: - # If the entity has been removed, decode the attributes - # instead - if attribute != ATTR_FRIENDLY_NAME: - raise ValueError( - f"{attribute} is not supported by {self.__class__.__name__}" - ) - cache[attribute] = event.attributes_friendly_name + return self._names[entity_id] - return cache[attribute] + +class EventCache: + """Cache LazyEventPartialState by row.""" + + def __init__(self, event_data_cache: dict[str, dict[str, Any]]) -> None: + """Init the cache.""" + self._event_data_cache = event_data_cache + self.event_cache: dict[Row, LazyEventPartialState] = {} + + def get(self, row: Row) -> LazyEventPartialState: + """Get the event from the row.""" + if event := self.event_cache.get(row): + return event + event = self.event_cache[row] = LazyEventPartialState( + row, self._event_data_cache + ) + return event diff --git a/homeassistant/scripts/benchmark/__init__.py b/homeassistant/scripts/benchmark/__init__.py index 2062ef231b9..8f95f7db66b 100644 --- a/homeassistant/scripts/benchmark/__init__.py +++ b/homeassistant/scripts/benchmark/__init__.py @@ -16,7 +16,6 @@ from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import EVENT_STATE_CHANGED from homeassistant.helpers.entityfilter import convert_include_exclude_filter from homeassistant.helpers.json import JSONEncoder -from homeassistant.util import dt as dt_util # mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs # mypy: no-warn-return-any @@ -224,57 +223,6 @@ async def state_changed_event_filter_helper(hass): return timer() - start -@benchmark -async def logbook_filtering_state(hass): - """Filter state changes.""" - return await _logbook_filtering(hass, 1, 1) - - -@benchmark -async def logbook_filtering_attributes(hass): - """Filter attribute changes.""" - return await _logbook_filtering(hass, 1, 2) - - -@benchmark -async def _logbook_filtering(hass, last_changed, last_updated): - # pylint: disable=import-outside-toplevel - from homeassistant.components import logbook - - entity_id = "test.entity" - - old_state = {"entity_id": entity_id, "state": "off"} - - new_state = { - "entity_id": entity_id, - "state": "on", - "last_updated": last_updated, - "last_changed": last_changed, - } - - event = _create_state_changed_event_from_old_new( - entity_id, dt_util.utcnow(), old_state, new_state - ) - - entity_attr_cache = logbook.EntityAttributeCache(hass) - - entities_filter = convert_include_exclude_filter( - logbook.INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA({}) - ) - - def yield_events(event): - for _ in range(10**5): - # pylint: disable=protected-access - if logbook._keep_event(hass, event, entities_filter): - yield event - - start = timer() - - list(logbook.humanify(hass, yield_events(event), entity_attr_cache, {})) - - return timer() - start - - @benchmark async def filtering_entity_id(hass): """Run a 100k state changes through entity filter.""" diff --git a/tests/components/alexa/test_init.py b/tests/components/alexa/test_init.py index eac4b32e5ba..9dc47da6256 100644 --- a/tests/components/alexa/test_init.py +++ b/tests/components/alexa/test_init.py @@ -1,9 +1,8 @@ """Tests for alexa.""" -from homeassistant.components import logbook from homeassistant.components.alexa.const import EVENT_ALEXA_SMART_HOME from homeassistant.setup import async_setup_component -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanify_alexa_event(hass): @@ -12,40 +11,35 @@ async def test_humanify_alexa_event(hass): await async_setup_component(hass, "alexa", {}) await async_setup_component(hass, "logbook", {}) hass.states.async_set("light.kitchen", "on", {"friendly_name": "Kitchen Light"}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - results = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_ALEXA_SMART_HOME, - {"request": {"namespace": "Alexa.Discovery", "name": "Discover"}}, - ), - MockLazyEventPartialState( - EVENT_ALEXA_SMART_HOME, - { - "request": { - "namespace": "Alexa.PowerController", - "name": "TurnOn", - "entity_id": "light.kitchen", - } - }, - ), - MockLazyEventPartialState( - EVENT_ALEXA_SMART_HOME, - { - "request": { - "namespace": "Alexa.PowerController", - "name": "TurnOn", - "entity_id": "light.non_existing", - } - }, - ), - ], - entity_attr_cache, - {}, - ) + results = mock_humanify( + hass, + [ + MockRow( + EVENT_ALEXA_SMART_HOME, + {"request": {"namespace": "Alexa.Discovery", "name": "Discover"}}, + ), + MockRow( + EVENT_ALEXA_SMART_HOME, + { + "request": { + "namespace": "Alexa.PowerController", + "name": "TurnOn", + "entity_id": "light.kitchen", + } + }, + ), + MockRow( + EVENT_ALEXA_SMART_HOME, + { + "request": { + "namespace": "Alexa.PowerController", + "name": "TurnOn", + "entity_id": "light.non_existing", + } + }, + ), + ], ) event1, event2, event3 = results diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 45f719a326f..af88adc00b7 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -6,7 +6,6 @@ from unittest.mock import Mock, patch import pytest -from homeassistant.components import logbook import homeassistant.components.automation as automation from homeassistant.components.automation import ( ATTR_SOURCE, @@ -53,7 +52,7 @@ from tests.common import ( async_mock_service, mock_restore_cache, ) -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify @pytest.fixture @@ -1223,28 +1222,23 @@ async def test_logbook_humanify_automation_triggered_event(hass): hass.config.components.add("recorder") await async_setup_component(hass, automation.DOMAIN, {}) await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_AUTOMATION_TRIGGERED, - {ATTR_ENTITY_ID: "automation.hello", ATTR_NAME: "Hello Automation"}, - ), - MockLazyEventPartialState( - EVENT_AUTOMATION_TRIGGERED, - { - ATTR_ENTITY_ID: "automation.bye", - ATTR_NAME: "Bye Automation", - ATTR_SOURCE: "source of trigger", - }, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_ENTITY_ID: "automation.hello", ATTR_NAME: "Hello Automation"}, + ), + MockRow( + EVENT_AUTOMATION_TRIGGERED, + { + ATTR_ENTITY_ID: "automation.bye", + ATTR_NAME: "Bye Automation", + ATTR_SOURCE: "source of trigger", + }, + ), + ], ) assert event1["name"] == "Hello Automation" diff --git a/tests/components/automation/test_logbook.py b/tests/components/automation/test_logbook.py index e13ebdc17a1..a3299d806ce 100644 --- a/tests/components/automation/test_logbook.py +++ b/tests/components/automation/test_logbook.py @@ -1,9 +1,9 @@ """Test automation logbook.""" -from homeassistant.components import automation, logbook +from homeassistant.components import automation from homeassistant.core import Context from homeassistant.setup import async_setup_component -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanify_automation_trigger_event(hass): @@ -11,34 +11,29 @@ async def test_humanify_automation_trigger_event(hass): hass.config.components.add("recorder") assert await async_setup_component(hass, "automation", {}) assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) context = Context() - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - automation.EVENT_AUTOMATION_TRIGGERED, - { - "name": "Bla", - "entity_id": "automation.bla", - "source": "state change of input_boolean.yo", - }, - context=context, - ), - MockLazyEventPartialState( - automation.EVENT_AUTOMATION_TRIGGERED, - { - "name": "Bla", - "entity_id": "automation.bla", - }, - context=context, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + automation.EVENT_AUTOMATION_TRIGGERED, + { + "name": "Bla", + "entity_id": "automation.bla", + "source": "state change of input_boolean.yo", + }, + context=context, + ), + MockRow( + automation.EVENT_AUTOMATION_TRIGGERED, + { + "name": "Bla", + "entity_id": "automation.bla", + }, + context=context, + ), + ], ) assert event1["name"] == "Bla" diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index bfd5126c9db..98245a0df20 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -2,7 +2,6 @@ from unittest.mock import patch -from homeassistant.components import logbook from homeassistant.components.deconz.const import CONF_GESTURE, DOMAIN as DECONZ_DOMAIN from homeassistant.components.deconz.deconz_event import ( CONF_DECONZ_ALARM_EVENT, @@ -21,7 +20,7 @@ from homeassistant.util import slugify from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): @@ -67,26 +66,21 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - events = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - CONF_DECONZ_ALARM_EVENT, - { - CONF_CODE: 1234, - CONF_DEVICE_ID: keypad_entry.id, - CONF_EVENT: STATE_ALARM_ARMED_AWAY, - CONF_ID: keypad_event_id, - CONF_UNIQUE_ID: keypad_serial, - }, - ), - ], - entity_attr_cache, - {}, - ) + events = mock_humanify( + hass, + [ + MockRow( + CONF_DECONZ_ALARM_EVENT, + { + CONF_CODE: 1234, + CONF_DEVICE_ID: keypad_entry.id, + CONF_EVENT: STATE_ALARM_ARMED_AWAY, + CONF_ID: keypad_event_id, + CONF_UNIQUE_ID: keypad_serial, + }, + ), + ], ) assert events[0]["name"] == "Keypad" @@ -161,66 +155,61 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - events = list( - logbook.humanify( - hass, - [ - # Event without matching device trigger - MockLazyEventPartialState( - CONF_DECONZ_EVENT, - { - CONF_DEVICE_ID: switch_entry.id, - CONF_EVENT: 2000, - CONF_ID: switch_event_id, - CONF_UNIQUE_ID: switch_serial, - }, - ), - # Event with matching device trigger - MockLazyEventPartialState( - CONF_DECONZ_EVENT, - { - CONF_DEVICE_ID: hue_remote_entry.id, - CONF_EVENT: 2001, - CONF_ID: hue_remote_event_id, - CONF_UNIQUE_ID: hue_remote_serial, - }, - ), - # Gesture with matching device trigger - MockLazyEventPartialState( - CONF_DECONZ_EVENT, - { - CONF_DEVICE_ID: xiaomi_cube_entry.id, - CONF_GESTURE: 1, - CONF_ID: xiaomi_cube_event_id, - CONF_UNIQUE_ID: xiaomi_cube_serial, - }, - ), - # Unsupported device trigger - MockLazyEventPartialState( - CONF_DECONZ_EVENT, - { - CONF_DEVICE_ID: xiaomi_cube_entry.id, - CONF_GESTURE: "unsupported_gesture", - CONF_ID: xiaomi_cube_event_id, - CONF_UNIQUE_ID: xiaomi_cube_serial, - }, - ), - # Unknown event - MockLazyEventPartialState( - CONF_DECONZ_EVENT, - { - CONF_DEVICE_ID: faulty_entry.id, - "unknown_event": None, - CONF_ID: faulty_event_id, - CONF_UNIQUE_ID: faulty_serial, - }, - ), - ], - entity_attr_cache, - {}, - ) + events = mock_humanify( + hass, + [ + # Event without matching device trigger + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: switch_entry.id, + CONF_EVENT: 2000, + CONF_ID: switch_event_id, + CONF_UNIQUE_ID: switch_serial, + }, + ), + # Event with matching device trigger + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: hue_remote_entry.id, + CONF_EVENT: 2001, + CONF_ID: hue_remote_event_id, + CONF_UNIQUE_ID: hue_remote_serial, + }, + ), + # Gesture with matching device trigger + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: xiaomi_cube_entry.id, + CONF_GESTURE: 1, + CONF_ID: xiaomi_cube_event_id, + CONF_UNIQUE_ID: xiaomi_cube_serial, + }, + ), + # Unsupported device trigger + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: xiaomi_cube_entry.id, + CONF_GESTURE: "unsupported_gesture", + CONF_ID: xiaomi_cube_event_id, + CONF_UNIQUE_ID: xiaomi_cube_serial, + }, + ), + # Unknown event + MockRow( + CONF_DECONZ_EVENT, + { + CONF_DEVICE_ID: faulty_entry.id, + "unknown_event": None, + CONF_ID: faulty_event_id, + CONF_UNIQUE_ID: faulty_serial, + }, + ), + ], ) assert events[0]["name"] == "Switch 1" diff --git a/tests/components/google_assistant/test_logbook.py b/tests/components/google_assistant/test_logbook.py index 09d0b12e417..5875d44adc7 100644 --- a/tests/components/google_assistant/test_logbook.py +++ b/tests/components/google_assistant/test_logbook.py @@ -1,5 +1,4 @@ """The tests for Google Assistant logbook.""" -from homeassistant.components import logbook from homeassistant.components.google_assistant.const import ( DOMAIN, EVENT_COMMAND_RECEIVED, @@ -9,7 +8,7 @@ from homeassistant.components.google_assistant.const import ( from homeassistant.const import ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME from homeassistant.setup import async_setup_component -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanify_command_received(hass): @@ -18,48 +17,43 @@ async def test_humanify_command_received(hass): hass.config.components.add("frontend") hass.config.components.add("google_assistant") assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) hass.states.async_set( "light.kitchen", "on", {ATTR_FRIENDLY_NAME: "The Kitchen Lights"} ) - events = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_COMMAND_RECEIVED, - { - "request_id": "abcd", - ATTR_ENTITY_ID: ["light.kitchen"], - "execution": [ - { - "command": "action.devices.commands.OnOff", - "params": {"on": True}, - } - ], - "source": SOURCE_LOCAL, - }, - ), - MockLazyEventPartialState( - EVENT_COMMAND_RECEIVED, - { - "request_id": "abcd", - ATTR_ENTITY_ID: ["light.non_existing"], - "execution": [ - { - "command": "action.devices.commands.OnOff", - "params": {"on": False}, - } - ], - "source": SOURCE_CLOUD, - }, - ), - ], - entity_attr_cache, - {}, - ) + events = mock_humanify( + hass, + [ + MockRow( + EVENT_COMMAND_RECEIVED, + { + "request_id": "abcd", + ATTR_ENTITY_ID: ["light.kitchen"], + "execution": [ + { + "command": "action.devices.commands.OnOff", + "params": {"on": True}, + } + ], + "source": SOURCE_LOCAL, + }, + ), + MockRow( + EVENT_COMMAND_RECEIVED, + { + "request_id": "abcd", + ATTR_ENTITY_ID: ["light.non_existing"], + "execution": [ + { + "command": "action.devices.commands.OnOff", + "params": {"on": False}, + } + ], + "source": SOURCE_CLOUD, + }, + ), + ], ) assert len(events) == 2 diff --git a/tests/components/homekit/test_init.py b/tests/components/homekit/test_init.py index 8652f8b032a..17933616fc4 100644 --- a/tests/components/homekit/test_init.py +++ b/tests/components/homekit/test_init.py @@ -1,7 +1,6 @@ """Test HomeKit initialization.""" from unittest.mock import patch -from homeassistant.components import logbook from homeassistant.components.homekit.const import ( ATTR_DISPLAY_NAME, ATTR_VALUE, @@ -11,7 +10,7 @@ from homeassistant.components.homekit.const import ( from homeassistant.const import ATTR_ENTITY_ID, ATTR_SERVICE from homeassistant.setup import async_setup_component -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanify_homekit_changed_event(hass, hk_driver, mock_get_source_ip): @@ -20,33 +19,28 @@ async def test_humanify_homekit_changed_event(hass, hk_driver, mock_get_source_i with patch("homeassistant.components.homekit.HomeKit"): assert await async_setup_component(hass, "homekit", {"homekit": {}}) assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_HOMEKIT_CHANGED, - { - ATTR_ENTITY_ID: "lock.front_door", - ATTR_DISPLAY_NAME: "Front Door", - ATTR_SERVICE: "lock", - }, - ), - MockLazyEventPartialState( - EVENT_HOMEKIT_CHANGED, - { - ATTR_ENTITY_ID: "cover.window", - ATTR_DISPLAY_NAME: "Window", - ATTR_SERVICE: "set_cover_position", - ATTR_VALUE: 75, - }, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + EVENT_HOMEKIT_CHANGED, + { + ATTR_ENTITY_ID: "lock.front_door", + ATTR_DISPLAY_NAME: "Front Door", + ATTR_SERVICE: "lock", + }, + ), + MockRow( + EVENT_HOMEKIT_CHANGED, + { + ATTR_ENTITY_ID: "cover.window", + ATTR_DISPLAY_NAME: "Window", + ATTR_SERVICE: "set_cover_position", + ATTR_VALUE: 75, + }, + ), + ], ) assert event1["name"] == "HomeKit" diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py new file mode 100644 index 00000000000..c5b8c8239e1 --- /dev/null +++ b/tests/components/logbook/common.py @@ -0,0 +1,58 @@ +"""Tests for the logbook component.""" +from __future__ import annotations + +import json +from typing import Any + +from homeassistant.components import logbook +from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat +from homeassistant.core import Context +from homeassistant.helpers.json import JSONEncoder +import homeassistant.util.dt as dt_util + + +class MockRow: + """Minimal row mock.""" + + def __init__( + self, + event_type: str, + data: dict[str, Any] | None = None, + context: Context | None = None, + ): + """Init the fake row.""" + self.event_type = event_type + self.shared_data = json.dumps(data, cls=JSONEncoder) + self.data = data + self.time_fired = dt_util.utcnow() + self.context_parent_id = context.parent_id if context else None + self.context_user_id = context.user_id if context else None + self.context_id = context.id if context else None + self.state = None + self.entity_id = None + + @property + def time_fired_minute(self): + """Minute the event was fired.""" + return self.time_fired.minute + + @property + def time_fired_isoformat(self): + """Time event was fired in utc isoformat.""" + return process_timestamp_to_utc_isoformat(self.time_fired) + + +def mock_humanify(hass_, rows): + """Wrap humanify with mocked logbook objects.""" + event_data_cache = {} + context_lookup = {} + entity_name_cache = logbook.EntityNameCache(hass_) + event_cache = logbook.EventCache(event_data_cache) + context_augmenter = logbook.ContextAugmenter( + context_lookup, entity_name_cache, {}, event_cache + ) + return list( + logbook.humanify( + hass_, rows, entity_name_cache, event_cache, context_augmenter + ), + ) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 2612765584f..ad5423286d9 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -4,6 +4,7 @@ import collections from datetime import datetime, timedelta from http import HTTPStatus import json +from typing import Any from unittest.mock import Mock, patch import pytest @@ -41,6 +42,8 @@ from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util +from .common import mock_humanify + from tests.common import async_capture_events, mock_platform from tests.components.recorder.common import ( async_recorder_block_till_done, @@ -212,16 +215,11 @@ def test_home_assistant_start_stop_grouped(hass_): Events that are occurring in the same minute. """ - entity_attr_cache = logbook.EntityAttributeCache(hass_) - entries = list( - logbook.humanify( - hass_, - ( - MockLazyEventPartialState(EVENT_HOMEASSISTANT_STOP), - MockLazyEventPartialState(EVENT_HOMEASSISTANT_START), - ), - entity_attr_cache, - {}, + entries = mock_humanify( + hass_, + ( + MockRow(EVENT_HOMEASSISTANT_STOP), + MockRow(EVENT_HOMEASSISTANT_START), ), ) @@ -231,30 +229,17 @@ def test_home_assistant_start_stop_grouped(hass_): ) -def test_unsupported_attributes_in_cache_throws(hass): - """Test unsupported attributes in cache.""" - entity_attr_cache = logbook.EntityAttributeCache(hass) - event = MockLazyEventPartialState(EVENT_STATE_CHANGED) - with pytest.raises(ValueError): - entity_attr_cache.get("sensor.xyz", "not_supported", event) - - def test_home_assistant_start(hass_): """Test if HA start is not filtered or converted into a restart.""" entity_id = "switch.bla" pointA = dt_util.utcnow() - entity_attr_cache = logbook.EntityAttributeCache(hass_) - entries = list( - logbook.humanify( - hass_, - ( - MockLazyEventPartialState(EVENT_HOMEASSISTANT_START), - create_state_changed_event(pointA, entity_id, 10), - ), - entity_attr_cache, - {}, - ) + entries = mock_humanify( + hass_, + ( + MockRow(EVENT_HOMEASSISTANT_START), + create_state_changed_event(pointA, entity_id, 10).row, + ), ) assert len(entries) == 2 @@ -267,24 +252,19 @@ def test_process_custom_logbook_entries(hass_): name = "Nice name" message = "has a custom entry" entity_id = "sun.sun" - entity_attr_cache = logbook.EntityAttributeCache(hass_) - entries = list( - logbook.humanify( - hass_, - ( - MockLazyEventPartialState( - logbook.EVENT_LOGBOOK_ENTRY, - { - logbook.ATTR_NAME: name, - logbook.ATTR_MESSAGE: message, - logbook.ATTR_ENTITY_ID: entity_id, - }, - ), + entries = mock_humanify( + hass_, + ( + MockRow( + logbook.EVENT_LOGBOOK_ENTRY, + { + logbook.ATTR_NAME: name, + logbook.ATTR_MESSAGE: message, + logbook.ATTR_ENTITY_ID: entity_id, + }, ), - entity_attr_cache, - {}, - ) + ), ) assert len(entries) == 1 @@ -343,11 +323,13 @@ def create_state_changed_event_from_old_new( "state_id", "old_state_id", "shared_attrs", + "shared_data", ], ) row.event_type = EVENT_STATE_CHANGED row.event_data = "{}" + row.shared_data = "{}" row.attributes = attributes_json row.shared_attrs = attributes_json row.time_fired = event_time_fired @@ -1987,34 +1969,26 @@ def _assert_entry( assert state == entry["state"] -class MockLazyEventPartialState(ha.Event): - """Minimal mock of a Lazy event.""" +class MockRow: + """Minimal row mock.""" - @property - def data_entity_id(self): - """Lookup entity id.""" - return self.data.get(ATTR_ENTITY_ID) - - @property - def data_domain(self): - """Lookup domain.""" - return self.data.get(ATTR_DOMAIN) + def __init__(self, event_type: str, data: dict[str, Any] = None): + """Init the fake row.""" + self.event_type = event_type + self.shared_data = json.dumps(data, cls=JSONEncoder) + self.data = data + self.time_fired = dt_util.utcnow() + self.context_parent_id = None + self.context_user_id = None + self.context_id = None + self.state = None + self.entity_id = None @property def time_fired_minute(self): """Minute the event was fired.""" return self.time_fired.minute - @property - def context_user_id(self): - """Context user id of event.""" - return self.context.user_id - - @property - def context_id(self): - """Context id of event.""" - return self.context.id - @property def time_fired_isoformat(self): """Time event was fired in utc isoformat.""" diff --git a/tests/components/script/test_init.py b/tests/components/script/test_init.py index ca0cdb97592..480bbcbc4f0 100644 --- a/tests/components/script/test_init.py +++ b/tests/components/script/test_init.py @@ -6,7 +6,7 @@ from unittest.mock import Mock, patch import pytest -from homeassistant.components import logbook, script +from homeassistant.components import script from homeassistant.components.script import DOMAIN, EVENT_SCRIPT_STARTED from homeassistant.const import ( ATTR_ENTITY_ID, @@ -41,7 +41,7 @@ from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util from tests.common import async_fire_time_changed, async_mock_service, mock_restore_cache -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify ENTITY_ID = "script.test" @@ -526,24 +526,19 @@ async def test_logbook_humanify_script_started_event(hass): hass.config.components.add("recorder") await async_setup_component(hass, DOMAIN, {}) await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_SCRIPT_STARTED, - {ATTR_ENTITY_ID: "script.hello", ATTR_NAME: "Hello Script"}, - ), - MockLazyEventPartialState( - EVENT_SCRIPT_STARTED, - {ATTR_ENTITY_ID: "script.bye", ATTR_NAME: "Bye Script"}, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + EVENT_SCRIPT_STARTED, + {ATTR_ENTITY_ID: "script.hello", ATTR_NAME: "Hello Script"}, + ), + MockRow( + EVENT_SCRIPT_STARTED, + {ATTR_ENTITY_ID: "script.bye", ATTR_NAME: "Bye Script"}, + ), + ], ) assert event1["name"] == "Hello Script" diff --git a/tests/components/shelly/test_logbook.py b/tests/components/shelly/test_logbook.py index 9ece9590cbb..1ba7ea7ed16 100644 --- a/tests/components/shelly/test_logbook.py +++ b/tests/components/shelly/test_logbook.py @@ -1,5 +1,4 @@ """The tests for Shelly logbook.""" -from homeassistant.components import logbook from homeassistant.components.shelly.const import ( ATTR_CHANNEL, ATTR_CLICK_TYPE, @@ -10,7 +9,7 @@ from homeassistant.components.shelly.const import ( from homeassistant.const import ATTR_DEVICE_ID from homeassistant.setup import async_setup_component -from tests.components.logbook.test_init import MockLazyEventPartialState +from tests.components.logbook.common import MockRow, mock_humanify async def test_humanify_shelly_click_event_block_device(hass, coap_wrapper): @@ -18,34 +17,29 @@ async def test_humanify_shelly_click_event_block_device(hass, coap_wrapper): assert coap_wrapper hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_SHELLY_CLICK, - { - ATTR_DEVICE_ID: coap_wrapper.device_id, - ATTR_DEVICE: "shellyix3-12345678", - ATTR_CLICK_TYPE: "single", - ATTR_CHANNEL: 1, - }, - ), - MockLazyEventPartialState( - EVENT_SHELLY_CLICK, - { - ATTR_DEVICE_ID: "no_device_id", - ATTR_DEVICE: "shellyswitch25-12345678", - ATTR_CLICK_TYPE: "long", - ATTR_CHANNEL: 2, - }, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + EVENT_SHELLY_CLICK, + { + ATTR_DEVICE_ID: coap_wrapper.device_id, + ATTR_DEVICE: "shellyix3-12345678", + ATTR_CLICK_TYPE: "single", + ATTR_CHANNEL: 1, + }, + ), + MockRow( + EVENT_SHELLY_CLICK, + { + ATTR_DEVICE_ID: "no_device_id", + ATTR_DEVICE: "shellyswitch25-12345678", + ATTR_CLICK_TYPE: "long", + ATTR_CHANNEL: 2, + }, + ), + ], ) assert event1["name"] == "Shelly" @@ -68,34 +62,29 @@ async def test_humanify_shelly_click_event_rpc_device(hass, rpc_wrapper): assert rpc_wrapper hass.config.components.add("recorder") assert await async_setup_component(hass, "logbook", {}) - entity_attr_cache = logbook.EntityAttributeCache(hass) - event1, event2 = list( - logbook.humanify( - hass, - [ - MockLazyEventPartialState( - EVENT_SHELLY_CLICK, - { - ATTR_DEVICE_ID: rpc_wrapper.device_id, - ATTR_DEVICE: "shellyplus1pm-12345678", - ATTR_CLICK_TYPE: "single_push", - ATTR_CHANNEL: 1, - }, - ), - MockLazyEventPartialState( - EVENT_SHELLY_CLICK, - { - ATTR_DEVICE_ID: "no_device_id", - ATTR_DEVICE: "shellypro4pm-12345678", - ATTR_CLICK_TYPE: "btn_down", - ATTR_CHANNEL: 2, - }, - ), - ], - entity_attr_cache, - {}, - ) + event1, event2 = mock_humanify( + hass, + [ + MockRow( + EVENT_SHELLY_CLICK, + { + ATTR_DEVICE_ID: rpc_wrapper.device_id, + ATTR_DEVICE: "shellyplus1pm-12345678", + ATTR_CLICK_TYPE: "single_push", + ATTR_CHANNEL: 1, + }, + ), + MockRow( + EVENT_SHELLY_CLICK, + { + ATTR_DEVICE_ID: "no_device_id", + ATTR_DEVICE: "shellypro4pm-12345678", + ATTR_CLICK_TYPE: "btn_down", + ATTR_CHANNEL: 2, + }, + ), + ], ) assert event1["name"] == "Shelly" From 1be2438ef67c7f523654bdb849cbed5f4c865365 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 9 May 2022 08:15:11 +0200 Subject: [PATCH 290/930] Use climate enums in mqtt (#70696) --- homeassistant/components/mqtt/climate.py | 45 +++++++++++------------- tests/components/mqtt/test_climate.py | 39 ++++++++------------ 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index 5c53380bb71..deb1021b9d7 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -10,7 +10,6 @@ from homeassistant.components import climate from homeassistant.components.climate import ( PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA, ClimateEntity, - ClimateEntityFeature, ) from homeassistant.components.climate.const import ( ATTR_HVAC_MODE, @@ -23,14 +22,13 @@ from homeassistant.components.climate.const import ( FAN_HIGH, FAN_LOW, FAN_MEDIUM, - HVAC_MODE_AUTO, - HVAC_MODE_COOL, - HVAC_MODE_DRY, - HVAC_MODE_FAN_ONLY, - HVAC_MODE_HEAT, - HVAC_MODE_OFF, PRESET_AWAY, PRESET_NONE, + SWING_OFF, + SWING_ON, + ClimateEntityFeature, + HVACAction, + HVACMode, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -43,7 +41,6 @@ from homeassistant.const import ( PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE, - STATE_ON, ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv @@ -272,12 +269,12 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend( vol.Optional( CONF_MODE_LIST, default=[ - HVAC_MODE_AUTO, - HVAC_MODE_OFF, - HVAC_MODE_COOL, - HVAC_MODE_HEAT, - HVAC_MODE_DRY, - HVAC_MODE_FAN_ONLY, + HVACMode.AUTO, + HVACMode.OFF, + HVACMode.COOL, + HVACMode.HEAT, + HVACMode.DRY, + HVACMode.FAN_ONLY, ], ): cv.ensure_list, vol.Optional(CONF_MODE_STATE_TEMPLATE): cv.template, @@ -309,7 +306,7 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend( vol.Optional(CONF_SWING_MODE_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional( - CONF_SWING_MODE_LIST, default=[STATE_ON, HVAC_MODE_OFF] + CONF_SWING_MODE_LIST, default=[SWING_ON, SWING_OFF] ): cv.ensure_list, vol.Optional(CONF_SWING_MODE_STATE_TEMPLATE): cv.template, vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic, @@ -460,9 +457,9 @@ class MqttClimate(MqttEntity, ClimateEntity): if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None: self._current_fan_mode = FAN_LOW if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None: - self._current_swing_mode = HVAC_MODE_OFF + self._current_swing_mode = SWING_OFF if self._topic[CONF_MODE_STATE_TOPIC] is None: - self._current_operation = HVAC_MODE_OFF + self._current_operation = HVACMode.OFF self._feature_preset_mode = CONF_PRESET_MODE_COMMAND_TOPIC in config if self._feature_preset_mode: self._preset_modes = config[CONF_PRESET_MODES_LIST] @@ -773,17 +770,17 @@ class MqttClimate(MqttEntity, ClimateEntity): return self._target_temp_high @property - def hvac_action(self): + def hvac_action(self) -> HVACAction | None: """Return the current running hvac operation if supported.""" return self._action @property - def hvac_mode(self): + def hvac_mode(self) -> HVACMode: """Return current operation ie. heat, cool, idle.""" return self._current_operation @property - def hvac_modes(self): + def hvac_modes(self) -> list[HVACMode]: """Return the list of available operation modes.""" return self._config[CONF_MODE_LIST] @@ -859,7 +856,7 @@ class MqttClimate(MqttEntity, ClimateEntity): setattr(self, attr, temp) # CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9 - if self._send_if_off or self._current_operation != HVAC_MODE_OFF: + if self._send_if_off or self._current_operation != HVACMode.OFF: payload = self._command_templates[cmnd_template](temp) await self._publish(cmnd_topic, payload) @@ -899,7 +896,7 @@ class MqttClimate(MqttEntity, ClimateEntity): async def async_set_swing_mode(self, swing_mode): """Set new swing mode.""" # CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9 - if self._send_if_off or self._current_operation != HVAC_MODE_OFF: + if self._send_if_off or self._current_operation != HVACMode.OFF: payload = self._command_templates[CONF_SWING_MODE_COMMAND_TEMPLATE]( swing_mode ) @@ -912,7 +909,7 @@ class MqttClimate(MqttEntity, ClimateEntity): async def async_set_fan_mode(self, fan_mode): """Set new target temperature.""" # CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9 - if self._send_if_off or self._current_operation != HVAC_MODE_OFF: + if self._send_if_off or self._current_operation != HVACMode.OFF: payload = self._command_templates[CONF_FAN_MODE_COMMAND_TEMPLATE](fan_mode) await self._publish(CONF_FAN_MODE_COMMAND_TOPIC, payload) @@ -922,7 +919,7 @@ class MqttClimate(MqttEntity, ClimateEntity): async def async_set_hvac_mode(self, hvac_mode) -> None: """Set new operation mode.""" - if hvac_mode == HVAC_MODE_OFF: + if hvac_mode == HVACMode.OFF: await self._publish( CONF_POWER_COMMAND_TOPIC, self._config[CONF_PAYLOAD_OFF] ) diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index 0c1db16d6fe..af6d65ac490 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -19,23 +19,14 @@ from homeassistant.components.climate.const import ( ATTR_TARGET_TEMP_LOW, CURRENT_HVAC_ACTIONS, DOMAIN as CLIMATE_DOMAIN, - HVAC_MODE_AUTO, - HVAC_MODE_COOL, - HVAC_MODE_DRY, - HVAC_MODE_FAN_ONLY, - HVAC_MODE_HEAT, PRESET_AWAY, PRESET_ECO, PRESET_NONE, - SUPPORT_AUX_HEAT, - SUPPORT_FAN_MODE, - SUPPORT_PRESET_MODE, - SUPPORT_SWING_MODE, - SUPPORT_TARGET_TEMPERATURE, - SUPPORT_TARGET_TEMPERATURE_RANGE, + ClimateEntityFeature, + HVACMode, ) from homeassistant.components.mqtt.climate import MQTT_CLIMATE_ATTRIBUTES_BLOCKED -from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF +from homeassistant.const import ATTR_TEMPERATURE from homeassistant.setup import async_setup_component from .test_common import ( @@ -171,12 +162,12 @@ async def test_supported_features(hass, mqtt_mock): state = hass.states.get(ENTITY_CLIMATE) support = ( - SUPPORT_TARGET_TEMPERATURE - | SUPPORT_SWING_MODE - | SUPPORT_FAN_MODE - | SUPPORT_PRESET_MODE - | SUPPORT_AUX_HEAT - | SUPPORT_TARGET_TEMPERATURE_RANGE + ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.SWING_MODE + | ClimateEntityFeature.FAN_MODE + | ClimateEntityFeature.PRESET_MODE + | ClimateEntityFeature.AUX_HEAT + | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE ) assert state.attributes.get("supported_features") == support @@ -190,12 +181,12 @@ async def test_get_hvac_modes(hass, mqtt_mock): state = hass.states.get(ENTITY_CLIMATE) modes = state.attributes.get("hvac_modes") assert [ - HVAC_MODE_AUTO, - STATE_OFF, - HVAC_MODE_COOL, - HVAC_MODE_HEAT, - HVAC_MODE_DRY, - HVAC_MODE_FAN_ONLY, + HVACMode.AUTO, + HVACMode.OFF, + HVACMode.COOL, + HVACMode.HEAT, + HVACMode.DRY, + HVACMode.FAN_ONLY, ] == modes From f50681e3d388f952d53bdd35277a6fdcc8cb01ca Mon Sep 17 00:00:00 2001 From: Shai Ungar Date: Mon, 9 May 2022 10:27:23 +0300 Subject: [PATCH 291/930] Migrate sabnzbd sensors unique ids (#71455) * Migrate sensors unique ids 1. migrate sensors to have unique id constructed also from entry_id 2. add migration flow in init 3. bump config flow to version 2 4. add tests for migration * move migrate to async_setup_entry * 1. Use the entity registry api in tests 2. Set up the config entry and not use integration directly 3. remove patch for entity registry * fix too many lines * Update tests/components/sabnzbd/test_init.py Co-authored-by: Martin Hjelmare * Update tests/components/sabnzbd/test_init.py Co-authored-by: Martin Hjelmare * Update tests/components/sabnzbd/test_init.py Co-authored-by: Martin Hjelmare * Update tests/components/sabnzbd/test_init.py Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- homeassistant/components/sabnzbd/__init__.py | 56 +++++++++++++ homeassistant/components/sabnzbd/sensor.py | 35 +++++--- tests/components/sabnzbd/test_config_flow.py | 1 - tests/components/sabnzbd/test_init.py | 85 ++++++++++++++++++++ 4 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 tests/components/sabnzbd/test_init.py diff --git a/homeassistant/components/sabnzbd/__init__.py b/homeassistant/components/sabnzbd/__init__.py index aca50e404a2..3fa5054a5f0 100644 --- a/homeassistant/components/sabnzbd/__init__.py +++ b/homeassistant/components/sabnzbd/__init__.py @@ -1,4 +1,6 @@ """Support for monitoring an SABnzbd NZB client.""" +from __future__ import annotations + from collections.abc import Callable import logging @@ -19,7 +21,9 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import async_get from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_entries from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType @@ -123,8 +127,56 @@ def async_get_entry_id_for_service_call(hass: HomeAssistant, call: ServiceCall) raise ValueError(f"No api for API key: {call_data_api_key}") +def update_device_identifiers(hass: HomeAssistant, entry: ConfigEntry): + """Update device identifiers to new identifiers.""" + device_registry = async_get(hass) + device_entry = device_registry.async_get_device({(DOMAIN, DOMAIN)}) + if device_entry and entry.entry_id in device_entry.config_entries: + new_identifiers = {(DOMAIN, entry.entry_id)} + _LOGGER.debug( + "Updating device id <%s> with new identifiers <%s>", + device_entry.id, + new_identifiers, + ) + device_registry.async_update_device( + device_entry.id, new_identifiers=new_identifiers + ) + + +async def migrate_unique_id(hass: HomeAssistant, entry: ConfigEntry): + """Migrate entities to new unique ids (with entry_id).""" + + @callback + def async_migrate_callback(entity_entry: RegistryEntry) -> dict | None: + """ + Define a callback to migrate appropriate SabnzbdSensor entities to new unique IDs. + + Old: description.key + New: {entry_id}_description.key + """ + entry_id = entity_entry.config_entry_id + if entry_id is None: + return None + if entity_entry.unique_id.startswith(entry_id): + return None + + new_unique_id = f"{entry_id}_{entity_entry.unique_id}" + + _LOGGER.debug( + "Migrating entity %s from old unique ID '%s' to new unique ID '%s'", + entity_entry.entity_id, + entity_entry.unique_id, + new_unique_id, + ) + + return {"new_unique_id": new_unique_id} + + await async_migrate_entries(hass, entry.entry_id, async_migrate_callback) + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the SabNzbd Component.""" + sab_api = await get_client(hass, entry.data) if not sab_api: raise ConfigEntryNotReady @@ -137,6 +189,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: KEY_NAME: entry.data[CONF_NAME], } + await migrate_unique_id(hass, entry) + update_device_identifiers(hass, entry) + @callback def extract_api(func: Callable) -> Callable: """Define a decorator to get the correct api for a service call.""" @@ -188,6 +243,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.error(err) async_track_time_interval(hass, async_update_sabnzbd, UPDATE_INTERVAL) + hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index 539eaa4f097..ebdb9190ed9 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -113,11 +113,16 @@ async def async_setup_entry( ) -> None: """Set up a Sabnzbd sensor entry.""" - sab_api_data = hass.data[DOMAIN][config_entry.entry_id][KEY_API_DATA] - client_name = hass.data[DOMAIN][config_entry.entry_id][KEY_NAME] + entry_id = config_entry.entry_id + + sab_api_data = hass.data[DOMAIN][entry_id][KEY_API_DATA] + client_name = hass.data[DOMAIN][entry_id][KEY_NAME] async_add_entities( - [SabnzbdSensor(sab_api_data, client_name, sensor) for sensor in SENSOR_TYPES] + [ + SabnzbdSensor(sab_api_data, client_name, sensor, entry_id) + for sensor in SENSOR_TYPES + ] ) @@ -128,17 +133,21 @@ class SabnzbdSensor(SensorEntity): _attr_should_poll = False def __init__( - self, sabnzbd_api_data, client_name, description: SabnzbdSensorEntityDescription + self, + sabnzbd_api_data, + client_name, + description: SabnzbdSensorEntityDescription, + entry_id, ): """Initialize the sensor.""" - unique_id = description.key - self._attr_unique_id = unique_id + + self._attr_unique_id = f"{entry_id}_{description.key}" self.entity_description = description self._sabnzbd_api = sabnzbd_api_data self._attr_name = f"{client_name} {description.name}" self._attr_device_info = DeviceInfo( entry_type=DeviceEntryType.SERVICE, - identifiers={(DOMAIN, DOMAIN)}, + identifiers={(DOMAIN, entry_id)}, name=DEFAULT_NAME, ) @@ -156,9 +165,11 @@ class SabnzbdSensor(SensorEntity): self.entity_description.key ) - if self.entity_description.key == SPEED_KEY: - self._attr_native_value = round(float(self._attr_native_value) / 1024, 1) - elif "size" in self.entity_description.key: - self._attr_native_value = round(float(self._attr_native_value), 2) - + if self._attr_native_value is not None: + if self.entity_description.key == SPEED_KEY: + self._attr_native_value = round( + float(self._attr_native_value) / 1024, 1 + ) + elif "size" in self.entity_description.key: + self._attr_native_value = round(float(self._attr_native_value), 2) self.schedule_update_ha_state() diff --git a/tests/components/sabnzbd/test_config_flow.py b/tests/components/sabnzbd/test_config_flow.py index d04c5b18ab1..bc72dff2535 100644 --- a/tests/components/sabnzbd/test_config_flow.py +++ b/tests/components/sabnzbd/test_config_flow.py @@ -87,7 +87,6 @@ async def test_import_flow(hass) -> None: "homeassistant.components.sabnzbd.sab.SabnzbdApi.check_available", return_value=True, ): - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, diff --git a/tests/components/sabnzbd/test_init.py b/tests/components/sabnzbd/test_init.py new file mode 100644 index 00000000000..9bdef4119d0 --- /dev/null +++ b/tests/components/sabnzbd/test_init.py @@ -0,0 +1,85 @@ +"""Tests for the SABnzbd Integration.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.sabnzbd import DEFAULT_NAME, DOMAIN, SENSOR_KEYS +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_URL +from homeassistant.helpers.device_registry import DeviceEntryType + +from tests.common import MockConfigEntry, mock_device_registry, mock_registry + +MOCK_ENTRY_ID = "mock_entry_id" + +MOCK_UNIQUE_ID = "someuniqueid" + +MOCK_DEVICE_ID = "somedeviceid" + +MOCK_DATA_VERSION_1 = { + CONF_API_KEY: "api_key", + CONF_URL: "http://127.0.0.1:8080", + CONF_NAME: "name", +} + +MOCK_ENTRY_VERSION_1 = MockConfigEntry( + domain=DOMAIN, data=MOCK_DATA_VERSION_1, entry_id=MOCK_ENTRY_ID, version=1 +) + + +@pytest.fixture +def device_registry(hass): + """Return an empty, loaded, registry.""" + return mock_device_registry(hass) + + +@pytest.fixture +def entity_registry(hass): + """Return an empty, loaded, registry.""" + return mock_registry(hass) + + +async def test_unique_id_migrate(hass, device_registry, entity_registry): + """Test that config flow entry is migrated correctly.""" + # Start with the config entry at Version 1. + mock_entry = MOCK_ENTRY_VERSION_1 + mock_entry.add_to_hass(hass) + + mock_d_entry = device_registry.async_get_or_create( + config_entry_id=mock_entry.entry_id, + identifiers={(DOMAIN, DOMAIN)}, + name=DEFAULT_NAME, + entry_type=DeviceEntryType.SERVICE, + ) + + entity_id_sensor_key = [] + + for sensor_key in SENSOR_KEYS: + mock_entity_id = f"{SENSOR_DOMAIN}.{DOMAIN}_{sensor_key}" + entity_registry.async_get_or_create( + SENSOR_DOMAIN, + DOMAIN, + unique_id=sensor_key, + config_entry=mock_entry, + device_id=mock_d_entry.id, + ) + entity = entity_registry.async_get(mock_entity_id) + assert entity.entity_id == mock_entity_id + assert entity.unique_id == sensor_key + entity_id_sensor_key.append((mock_entity_id, sensor_key)) + + with patch( + "homeassistant.components.sabnzbd.sab.SabnzbdApi.check_available", + return_value=True, + ): + await hass.config_entries.async_setup(mock_entry.entry_id) + + await hass.async_block_till_done() + + for mock_entity_id, sensor_key in entity_id_sensor_key: + entity = entity_registry.async_get(mock_entity_id) + assert entity.unique_id == f"{MOCK_ENTRY_ID}_{sensor_key}" + + assert device_registry.async_get(mock_d_entry.id).identifiers == { + (DOMAIN, MOCK_ENTRY_ID) + } From bb4a5ccc2c05a9e17074ef9509d06dd66f3e12f5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 11:03:32 +0200 Subject: [PATCH 292/930] Correct device class for meater cook sensors (#71565) --- homeassistant/components/meater/sensor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index 8a6c07bcbc4..f6d06dc2b25 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -176,8 +176,6 @@ class MeaterProbeTemperature( ): """Meater Temperature Sensor Entity.""" - _attr_device_class = SensorDeviceClass.TEMPERATURE - _attr_native_unit_of_measurement = TEMP_CELSIUS entity_description: MeaterSensorEntityDescription def __init__( From 5d9dc8252b5d08fffac3e8f9cdd54246defb1301 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 9 May 2022 11:52:08 +0200 Subject: [PATCH 293/930] Use helper for testing an event change (#71579) --- tests/components/mqtt/test_discovery.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index a1ef6ea477a..9215ab651b2 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -24,6 +24,7 @@ from homeassistant.setup import async_setup_component from tests.common import ( MockConfigEntry, + async_capture_events, async_fire_mqtt_message, mock_device_registry, mock_entity_platform, @@ -438,14 +439,7 @@ async def test_rediscover(hass, mqtt_mock, caplog): async def test_rapid_rediscover(hass, mqtt_mock, caplog): """Test immediate rediscover of removed component.""" - events = [] - - @ha.callback - def callback(event): - """Verify event got called.""" - events.append(event) - - hass.bus.async_listen(EVENT_STATE_CHANGED, callback) + events = async_capture_events(hass, EVENT_STATE_CHANGED) async_fire_mqtt_message( hass, From 1cc9800a939327c59b21da849daa5214cb58af57 Mon Sep 17 00:00:00 2001 From: Oscar Calvo <2091582+ocalvo@users.noreply.github.com> Date: Mon, 9 May 2022 03:06:29 -0700 Subject: [PATCH 294/930] Support custom baud speed (#68320) Co-authored-by: Franck Nijhof --- homeassistant/components/sms/__init__.py | 24 ++++++++++++++++++--- homeassistant/components/sms/config_flow.py | 20 +++++++++++++---- homeassistant/components/sms/const.py | 24 +++++++++++++++++++++ homeassistant/components/sms/gateway.py | 1 + homeassistant/components/sms/strings.json | 5 ++++- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sms/__init__.py b/homeassistant/components/sms/__init__.py index 9b091942556..b1c2703409c 100644 --- a/homeassistant/components/sms/__init__.py +++ b/homeassistant/components/sms/__init__.py @@ -1,4 +1,6 @@ """The sms component.""" +import logging + import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry @@ -7,13 +9,24 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import DOMAIN, SMS_GATEWAY +from .const import CONF_BAUD_SPEED, DEFAULT_BAUD_SPEED, DOMAIN, SMS_GATEWAY from .gateway import create_sms_gateway +_LOGGER = logging.getLogger(__name__) + PLATFORMS = [Platform.SENSOR] +SMS_CONFIG_SCHEMA = {vol.Required(CONF_DEVICE): cv.isdevice} + CONFIG_SCHEMA = vol.Schema( - {DOMAIN: vol.Schema({vol.Required(CONF_DEVICE): cv.isdevice})}, + { + DOMAIN: vol.Schema( + vol.All( + cv.deprecated(CONF_DEVICE), + SMS_CONFIG_SCHEMA, + ), + ) + }, extra=vol.ALLOW_EXTRA, ) @@ -39,7 +52,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Configure Gammu state machine.""" device = entry.data[CONF_DEVICE] - config = {"Device": device, "Connection": "at"} + connection_mode = "at" + baud_speed = entry.data.get(CONF_BAUD_SPEED, DEFAULT_BAUD_SPEED) + if baud_speed != DEFAULT_BAUD_SPEED: + connection_mode += baud_speed + config = {"Device": device, "Connection": connection_mode} + _LOGGER.debug("Connecting mode:%s", connection_mode) gateway = await create_sms_gateway(config, hass) if not gateway: return False diff --git a/homeassistant/components/sms/config_flow.py b/homeassistant/components/sms/config_flow.py index 37b78ee3ea3..acc33075397 100644 --- a/homeassistant/components/sms/config_flow.py +++ b/homeassistant/components/sms/config_flow.py @@ -6,13 +6,21 @@ import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import CONF_DEVICE +from homeassistant.helpers import selector -from .const import DOMAIN +from .const import CONF_BAUD_SPEED, DEFAULT_BAUD_SPEED, DEFAULT_BAUD_SPEEDS, DOMAIN from .gateway import create_sms_gateway _LOGGER = logging.getLogger(__name__) -DATA_SCHEMA = vol.Schema({vol.Required(CONF_DEVICE): str}) +DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_DEVICE): str, + vol.Optional(CONF_BAUD_SPEED, default=DEFAULT_BAUD_SPEED): selector.selector( + {"select": {"options": DEFAULT_BAUD_SPEEDS}} + ), + } +) async def get_imei_from_config(hass: core.HomeAssistant, data): @@ -21,13 +29,17 @@ async def get_imei_from_config(hass: core.HomeAssistant, data): Data has the keys from DATA_SCHEMA with values provided by the user. """ device = data[CONF_DEVICE] - config = {"Device": device, "Connection": "at"} + connection_mode = "at" + baud_speed = data.get(CONF_BAUD_SPEED, DEFAULT_BAUD_SPEED) + if baud_speed != DEFAULT_BAUD_SPEED: + connection_mode += baud_speed + config = {"Device": device, "Connection": connection_mode} gateway = await create_sms_gateway(config, hass) if not gateway: raise CannotConnect try: imei = await gateway.get_imei_async() - except gammu.GSMError as err: + except gammu.GSMError as err: # pylint: disable=no-member raise CannotConnect from err finally: await gateway.terminate_async() diff --git a/homeassistant/components/sms/const.py b/homeassistant/components/sms/const.py index ab2c15a0c49..7c40a04073c 100644 --- a/homeassistant/components/sms/const.py +++ b/homeassistant/components/sms/const.py @@ -3,3 +3,27 @@ DOMAIN = "sms" SMS_GATEWAY = "SMS_GATEWAY" SMS_STATE_UNREAD = "UnRead" +CONF_BAUD_SPEED = "baud_speed" +DEFAULT_BAUD_SPEED = "0" +DEFAULT_BAUD_SPEEDS = [ + {"value": DEFAULT_BAUD_SPEED, "label": "Auto"}, + {"value": "50", "label": "50"}, + {"value": "75", "label": "75"}, + {"value": "110", "label": "110"}, + {"value": "134", "label": "134"}, + {"value": "150", "label": "150"}, + {"value": "200", "label": "200"}, + {"value": "300", "label": "300"}, + {"value": "600", "label": "600"}, + {"value": "1200", "label": "1200"}, + {"value": "1800", "label": "1800"}, + {"value": "2400", "label": "2400"}, + {"value": "4800", "label": "4800"}, + {"value": "9600", "label": "9600"}, + {"value": "19200", "label": "19200"}, + {"value": "28800", "label": "28800"}, + {"value": "38400", "label": "38400"}, + {"value": "57600", "label": "57600"}, + {"value": "76800", "label": "76800"}, + {"value": "115200", "label": "115200"}, +] diff --git a/homeassistant/components/sms/gateway.py b/homeassistant/components/sms/gateway.py index bd8d2f365c9..09992600943 100644 --- a/homeassistant/components/sms/gateway.py +++ b/homeassistant/components/sms/gateway.py @@ -16,6 +16,7 @@ class Gateway: def __init__(self, config, hass): """Initialize the sms gateway.""" + _LOGGER.debug("Init with connection mode:%s", config["Connection"]) self._worker = GammuAsyncWorker(self.sms_pull) self._worker.configure(config) self._hass = hass diff --git a/homeassistant/components/sms/strings.json b/homeassistant/components/sms/strings.json index 872cb17cbea..b4a9279845d 100644 --- a/homeassistant/components/sms/strings.json +++ b/homeassistant/components/sms/strings.json @@ -3,7 +3,10 @@ "step": { "user": { "title": "Connect to the modem", - "data": { "device": "Device" } + "data": { + "device": "Device", + "baud_speed": "Baud Speed" + } } }, "error": { From 20d9f2d3b7dc512948bcf5971127231a61c7a47d Mon Sep 17 00:00:00 2001 From: Hans Oischinger Date: Mon, 9 May 2022 12:11:09 +0200 Subject: [PATCH 295/930] Vicare Gas & Power consumption summary sensors (#66458) --- homeassistant/components/vicare/sensor.py | 141 ++++++++++++++++++++-- 1 file changed, 131 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py index 249cadaee86..60a39b454a2 100644 --- a/homeassistant/components/vicare/sensor.py +++ b/homeassistant/components/vicare/sensor.py @@ -27,6 +27,7 @@ from homeassistant.const import ( POWER_WATT, TEMP_CELSIUS, TIME_HOURS, + VOLUME_CUBIC_METERS, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -132,6 +133,126 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( unit_getter=lambda api: api.getGasConsumptionHeatingUnit(), state_class=SensorStateClass.TOTAL_INCREASING, ), + ViCareSensorEntityDescription( + key="gas_summary_consumption_heating_currentday", + name="Heating gas consumption current day", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentDay(), + unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="gas_summary_consumption_heating_currentmonth", + name="Heating gas consumption current month", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentMonth(), + unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="gas_summary_consumption_heating_currentyear", + name="Heating gas consumption current year", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionHeatingCurrentYear(), + unit_getter=lambda api: api.getGasSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="hotwater_gas_summary_consumption_heating_currentday", + name="Hot water gas consumption current day", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentDay(), + unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="hotwater_gas_summary_consumption_heating_currentmonth", + name="Hot water gas consumption current month", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentMonth(), + unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="hotwater_gas_summary_consumption_heating_currentyear", + name="Hot water gas consumption current year", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterCurrentYear(), + unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="hotwater_gas_summary_consumption_heating_lastsevendays", + name="Hot water gas consumption last seven days", + native_unit_of_measurement=VOLUME_CUBIC_METERS, + value_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterLastSevenDays(), + unit_getter=lambda api: api.getGasSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_summary_consumption_heating_currentday", + name="Energy consumption of gas heating current day", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentDay(), + unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_summary_consumption_heating_currentmonth", + name="Energy consumption of gas heating current month", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentMonth(), + unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_summary_consumption_heating_currentyear", + name="Energy consumption of gas heating current year", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionHeatingCurrentYear(), + unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_summary_consumption_heating_lastsevendays", + name="Energy consumption of gas heating last seven days", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionHeatingLastSevenDays(), + unit_getter=lambda api: api.getPowerSummaryConsumptionHeatingUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_dhw_summary_consumption_heating_currentday", + name="Energy consumption of hot water gas heating current day", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentDay(), + unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_dhw_summary_consumption_heating_currentmonth", + name="Energy consumption of hot water gas heating current month", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentMonth(), + unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_dhw_summary_consumption_heating_currentyear", + name="Energy consumption of hot water gas heating current year", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterCurrentYear(), + unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), + ViCareSensorEntityDescription( + key="energy_summary_dhw_consumption_heating_lastsevendays", + name="Energy consumption of hot water gas heating last seven days", + native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, + value_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterLastSevenDays(), + unit_getter=lambda api: api.getPowerSummaryConsumptionDomesticHotWaterUnit(), + state_class=SensorStateClass.TOTAL_INCREASING, + ), ViCareSensorEntityDescription( key="power_production_current", name="Power production current", @@ -142,7 +263,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power_production_today", - name="Power production today", + name="Energy production today", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionToday(), device_class=SensorDeviceClass.ENERGY, @@ -158,7 +279,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power_production_this_month", - name="Power production this month", + name="Energy production this month", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionThisMonth(), device_class=SensorDeviceClass.ENERGY, @@ -166,7 +287,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power_production_this_year", - name="Power production this year", + name="Energy production this year", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerProductionThisYear(), device_class=SensorDeviceClass.ENERGY, @@ -190,7 +311,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="solar power production today", - name="Solar power production today", + name="Solar energy production today", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionToday(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), @@ -199,7 +320,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="solar power production this week", - name="Solar power production this week", + name="Solar energy production this week", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisWeek(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), @@ -208,7 +329,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="solar power production this month", - name="Solar power production this month", + name="Solar energy production this month", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisMonth(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), @@ -217,7 +338,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="solar power production this year", - name="Solar power production this year", + name="Solar energy production this year", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getSolarPowerProductionThisYear(), unit_getter=lambda api: api.getSolarPowerProductionUnit(), @@ -226,7 +347,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power consumption today", - name="Power consumption today", + name="Energy consumption today", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionToday(), unit_getter=lambda api: api.getPowerConsumptionUnit(), @@ -244,7 +365,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power consumption this month", - name="Power consumption this month", + name="Energy consumption this month", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionThisMonth(), unit_getter=lambda api: api.getPowerConsumptionUnit(), @@ -253,7 +374,7 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = ( ), ViCareSensorEntityDescription( key="power consumption this year", - name="Power consumption this year", + name="Energy consumption this year", native_unit_of_measurement=ENERGY_KILO_WATT_HOUR, value_getter=lambda api: api.getPowerConsumptionThisYear(), unit_getter=lambda api: api.getPowerConsumptionUnit(), From 587a29c72327227e9e0169a916571a1ce6e31016 Mon Sep 17 00:00:00 2001 From: Shai Ungar Date: Mon, 9 May 2022 13:34:16 +0300 Subject: [PATCH 296/930] Fix SABnzbd config check (#71549) --- homeassistant/components/sabnzbd/__init__.py | 6 ++---- homeassistant/components/sabnzbd/sensor.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/sabnzbd/__init__.py b/homeassistant/components/sabnzbd/__init__.py index 3fa5054a5f0..c03d27fefe5 100644 --- a/homeassistant/components/sabnzbd/__init__.py +++ b/homeassistant/components/sabnzbd/__init__.py @@ -16,7 +16,6 @@ from homeassistant.const import ( CONF_PORT, CONF_SENSORS, CONF_SSL, - CONF_URL, ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError @@ -46,7 +45,7 @@ from .const import ( UPDATE_INTERVAL, ) from .sab import get_client -from .sensor import SENSOR_KEYS +from .sensor import OLD_SENSOR_KEYS PLATFORMS = ["sensor"] _LOGGER = logging.getLogger(__name__) @@ -80,12 +79,11 @@ CONFIG_SCHEMA = vol.Schema( { vol.Required(CONF_API_KEY): str, vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, - vol.Required(CONF_URL): str, vol.Optional(CONF_PATH): str, vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_SENSORS): vol.All( - cv.ensure_list, [vol.In(SENSOR_KEYS)] + cv.ensure_list, [vol.In(OLD_SENSOR_KEYS)] ), vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, }, diff --git a/homeassistant/components/sabnzbd/sensor.py b/homeassistant/components/sabnzbd/sensor.py index ebdb9190ed9..043a344ec7b 100644 --- a/homeassistant/components/sabnzbd/sensor.py +++ b/homeassistant/components/sabnzbd/sensor.py @@ -103,7 +103,19 @@ SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = ( ), ) -SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] +OLD_SENSOR_KEYS = [ + "current_status", + "speed", + "queue_size", + "queue_remaining", + "disk_size", + "disk_free", + "queue_count", + "day_size", + "week_size", + "month_size", + "total_size", +] async def async_setup_entry( From 2dbe910e312dad4c4d7f808850b642f41877f552 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Mon, 9 May 2022 12:37:24 +0200 Subject: [PATCH 297/930] Adjust warning for missing entites (#71343) --- homeassistant/helpers/service.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 9d446f10913..4cd38aa9768 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -127,7 +127,10 @@ class SelectedEntities: if not parts: return - _LOGGER.warning("Unable to find referenced %s", ", ".join(parts)) + _LOGGER.warning( + "Unable to find referenced %s or it is/they are currently not available", + ", ".join(parts), + ) @bind_hass From 894d0e35377fb993f2fcf536ff78570163f721dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 12:38:46 +0200 Subject: [PATCH 298/930] Bump docker/login-action from 1.14.1 to 2.0.0 (#71385) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/builder.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 2a83ed47de0..48c0782dafa 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -122,13 +122,13 @@ jobs: echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE - name: Login to DockerHub - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -187,13 +187,13 @@ jobs: fi - name: Login to DockerHub - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -259,14 +259,14 @@ jobs: - name: Login to DockerHub if: matrix.registry == 'homeassistant' - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry if: matrix.registry == 'ghcr.io/home-assistant' - uses: docker/login-action@v1.14.1 + uses: docker/login-action@v2.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} From 30fdfc454f9cd6a1a0abc356a46105042a462cdd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 05:48:38 -0500 Subject: [PATCH 299/930] Avoid lowercasing entities after template ratelimit recovery (#71415) --- homeassistant/helpers/event.py | 28 ++++++++++--- tests/helpers/test_event.py | 77 ++++++++++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 686fde89fbb..c1229dc3e7c 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -68,8 +68,8 @@ class TrackStates: """Class for keeping track of states being tracked. all_states: All states on the system are being tracked - entities: Entities to track - domains: Domains to track + entities: Lowercased entities to track + domains: Lowercased domains to track """ all_states: bool @@ -248,7 +248,16 @@ def async_track_state_change_event( """ if not (entity_ids := _async_string_to_lower_list(entity_ids)): return _remove_empty_listener + return _async_track_state_change_event(hass, entity_ids, action) + +@bind_hass +def _async_track_state_change_event( + hass: HomeAssistant, + entity_ids: str | Iterable[str], + action: Callable[[Event], Any], +) -> CALLBACK_TYPE: + """async_track_state_change_event without lowercasing.""" entity_callbacks = hass.data.setdefault(TRACK_STATE_CHANGE_CALLBACKS, {}) if TRACK_STATE_CHANGE_LISTENER not in hass.data: @@ -419,7 +428,16 @@ def async_track_state_added_domain( """Track state change events when an entity is added to domains.""" if not (domains := _async_string_to_lower_list(domains)): return _remove_empty_listener + return _async_track_state_added_domain(hass, domains, action) + +@bind_hass +def _async_track_state_added_domain( + hass: HomeAssistant, + domains: str | Iterable[str], + action: Callable[[Event], Any], +) -> CALLBACK_TYPE: + """async_track_state_added_domain without lowercasing.""" domain_callbacks = hass.data.setdefault(TRACK_STATE_ADDED_DOMAIN_CALLBACKS, {}) if TRACK_STATE_ADDED_DOMAIN_LISTENER not in hass.data: @@ -626,7 +644,7 @@ class _TrackStateChangeFiltered: if not entities: return - self._listeners[_ENTITIES_LISTENER] = async_track_state_change_event( + self._listeners[_ENTITIES_LISTENER] = _async_track_state_change_event( self.hass, entities, self._action ) @@ -643,7 +661,7 @@ class _TrackStateChangeFiltered: if not domains: return - self._listeners[_DOMAINS_LISTENER] = async_track_state_added_domain( + self._listeners[_DOMAINS_LISTENER] = _async_track_state_added_domain( self.hass, domains, self._state_added ) @@ -1217,7 +1235,7 @@ def async_track_same_state( else: async_remove_state_for_cancel = async_track_state_change_event( hass, - [entity_ids] if isinstance(entity_ids, str) else entity_ids, + entity_ids, state_for_cancel_listener, ) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 7644abaa558..9b0a3e1abd5 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -1983,6 +1983,69 @@ async def test_track_template_result_and_conditional(hass): assert specific_runs[2] == "on" +async def test_track_template_result_and_conditional_upper_case(hass): + """Test tracking template with an and conditional with an upper case template.""" + specific_runs = [] + hass.states.async_set("light.a", "off") + hass.states.async_set("light.b", "off") + template_str = '{% if states.light.A.state == "on" and states.light.B.state == "on" %}on{% else %}off{% endif %}' + + template = Template(template_str, hass) + + def specific_run_callback(event, updates): + specific_runs.append(updates.pop().result) + + info = async_track_template_result( + hass, [TrackTemplate(template, None)], specific_run_callback + ) + await hass.async_block_till_done() + assert info.listeners == { + "all": False, + "domains": set(), + "entities": {"light.a"}, + "time": False, + } + + hass.states.async_set("light.b", "on") + await hass.async_block_till_done() + assert len(specific_runs) == 0 + + hass.states.async_set("light.a", "on") + await hass.async_block_till_done() + assert len(specific_runs) == 1 + assert specific_runs[0] == "on" + assert info.listeners == { + "all": False, + "domains": set(), + "entities": {"light.a", "light.b"}, + "time": False, + } + + hass.states.async_set("light.b", "off") + await hass.async_block_till_done() + assert len(specific_runs) == 2 + assert specific_runs[1] == "off" + assert info.listeners == { + "all": False, + "domains": set(), + "entities": {"light.a", "light.b"}, + "time": False, + } + + hass.states.async_set("light.a", "off") + await hass.async_block_till_done() + assert len(specific_runs) == 2 + + hass.states.async_set("light.b", "on") + await hass.async_block_till_done() + assert len(specific_runs) == 2 + + hass.states.async_set("light.a", "on") + await hass.async_block_till_done() + assert len(specific_runs) == 3 + assert specific_runs[2] == "on" + + async def test_track_template_result_iterator(hass): """Test tracking template.""" iterator_runs = [] @@ -2187,7 +2250,7 @@ async def test_track_template_rate_limit(hass): assert refresh_runs == [0] info.async_refresh() assert refresh_runs == [0, 1] - hass.states.async_set("sensor.two", "any") + hass.states.async_set("sensor.TWO", "any") await hass.async_block_till_done() assert refresh_runs == [0, 1] next_time = dt_util.utcnow() + timedelta(seconds=0.125) @@ -2200,7 +2263,7 @@ async def test_track_template_rate_limit(hass): hass.states.async_set("sensor.three", "any") await hass.async_block_till_done() assert refresh_runs == [0, 1, 2] - hass.states.async_set("sensor.four", "any") + hass.states.async_set("sensor.fOuR", "any") await hass.async_block_till_done() assert refresh_runs == [0, 1, 2] next_time = dt_util.utcnow() + timedelta(seconds=0.125 * 2) @@ -2385,7 +2448,7 @@ async def test_track_template_rate_limit_super_3(hass): await hass.async_block_till_done() assert refresh_runs == [] - hass.states.async_set("sensor.one", "any") + hass.states.async_set("sensor.ONE", "any") await hass.async_block_till_done() assert refresh_runs == [] info.async_refresh() @@ -2408,7 +2471,7 @@ async def test_track_template_rate_limit_super_3(hass): hass.states.async_set("sensor.four", "any") await hass.async_block_till_done() assert refresh_runs == [1, 2] - hass.states.async_set("sensor.five", "any") + hass.states.async_set("sensor.FIVE", "any") await hass.async_block_till_done() assert refresh_runs == [1, 2] next_time = dt_util.utcnow() + timedelta(seconds=0.125 * 2) @@ -2453,7 +2516,7 @@ async def test_track_template_rate_limit_suppress_listener(hass): await hass.async_block_till_done() assert refresh_runs == [0] - hass.states.async_set("sensor.one", "any") + hass.states.async_set("sensor.oNe", "any") await hass.async_block_till_done() assert refresh_runs == [0] info.async_refresh() @@ -2482,7 +2545,7 @@ async def test_track_template_rate_limit_suppress_listener(hass): "time": False, } assert refresh_runs == [0, 1, 2] - hass.states.async_set("sensor.three", "any") + hass.states.async_set("sensor.Three", "any") await hass.async_block_till_done() assert refresh_runs == [0, 1, 2] hass.states.async_set("sensor.four", "any") @@ -2509,7 +2572,7 @@ async def test_track_template_rate_limit_suppress_listener(hass): "time": False, } assert refresh_runs == [0, 1, 2, 4] - hass.states.async_set("sensor.five", "any") + hass.states.async_set("sensor.Five", "any") await hass.async_block_till_done() # Rate limit hit and the all listener is shut off assert info.listeners == { From 08856cfab06173bf8957ac1e6334514f062e2bb0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 9 May 2022 13:16:23 +0200 Subject: [PATCH 300/930] Remove 1-Wire SysBus (ADR-0019) (#71232) --- .../components/onewire/binary_sensor.py | 24 +- .../components/onewire/config_flow.py | 117 ++-------- homeassistant/components/onewire/const.py | 13 +- .../components/onewire/manifest.json | 4 +- homeassistant/components/onewire/model.py | 16 +- .../components/onewire/onewire_entities.py | 39 +--- .../components/onewire/onewirehub.py | 113 ++-------- homeassistant/components/onewire/sensor.py | 207 ++++-------------- homeassistant/components/onewire/strings.json | 18 +- homeassistant/components/onewire/switch.py | 29 +-- requirements_all.txt | 3 - requirements_test_all.txt | 3 - tests/components/onewire/__init__.py | 29 --- tests/components/onewire/conftest.py | 38 +--- tests/components/onewire/const.py | 140 ------------ .../components/onewire/test_binary_sensor.py | 4 +- tests/components/onewire/test_config_flow.py | 130 +---------- tests/components/onewire/test_diagnostics.py | 1 - tests/components/onewire/test_init.py | 36 +-- tests/components/onewire/test_options_flow.py | 30 +-- tests/components/onewire/test_sensor.py | 53 +---- tests/components/onewire/test_switch.py | 4 +- 22 files changed, 142 insertions(+), 909 deletions(-) diff --git a/homeassistant/components/onewire/binary_sensor.py b/homeassistant/components/onewire/binary_sensor.py index 0235b46baa6..307f38ceea0 100644 --- a/homeassistant/components/onewire/binary_sensor.py +++ b/homeassistant/components/onewire/binary_sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations from dataclasses import dataclass import os -from typing import TYPE_CHECKING from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -11,21 +10,18 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( - CONF_TYPE_OWSERVER, DEVICE_KEYS_0_3, DEVICE_KEYS_0_7, DEVICE_KEYS_A_B, DOMAIN, READ_MODE_BOOL, ) -from .model import OWServerDeviceDescription -from .onewire_entities import OneWireEntityDescription, OneWireProxyEntity +from .onewire_entities import OneWireEntity, OneWireEntityDescription from .onewirehub import OneWireHub @@ -98,23 +94,19 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up 1-Wire platform.""" - # Only OWServer implementation works with binary sensors - if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER: - onewirehub = hass.data[DOMAIN][config_entry.entry_id] + onewirehub = hass.data[DOMAIN][config_entry.entry_id] - entities = await hass.async_add_executor_job(get_entities, onewirehub) - async_add_entities(entities, True) + entities = await hass.async_add_executor_job(get_entities, onewirehub) + async_add_entities(entities, True) -def get_entities(onewirehub: OneWireHub) -> list[BinarySensorEntity]: +def get_entities(onewirehub: OneWireHub) -> list[OneWireBinarySensor]: """Get a list of entities.""" if not onewirehub.devices: return [] - entities: list[BinarySensorEntity] = [] + entities: list[OneWireBinarySensor] = [] for device in onewirehub.devices: - if TYPE_CHECKING: - assert isinstance(device, OWServerDeviceDescription) family = device.family device_id = device.id device_type = device.type @@ -130,7 +122,7 @@ def get_entities(onewirehub: OneWireHub) -> list[BinarySensorEntity]: device_file = os.path.join(os.path.split(device.path)[0], description.key) name = f"{device_id} {description.name}" entities.append( - OneWireProxyBinarySensor( + OneWireBinarySensor( description=description, device_id=device_id, device_file=device_file, @@ -143,7 +135,7 @@ def get_entities(onewirehub: OneWireHub) -> list[BinarySensorEntity]: return entities -class OneWireProxyBinarySensor(OneWireProxyEntity, BinarySensorEntity): +class OneWireBinarySensor(OneWireEntity, BinarySensorEntity): """Implementation of a 1-Wire binary sensor.""" entity_description: OneWireBinarySensorEntityDescription diff --git a/homeassistant/components/onewire/config_flow.py b/homeassistant/components/onewire/config_flow.py index 25604473bcf..5e944c51e0a 100644 --- a/homeassistant/components/onewire/config_flow.py +++ b/homeassistant/components/onewire/config_flow.py @@ -6,19 +6,15 @@ from typing import Any import voluptuous as vol from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.device_registry import DeviceRegistry from .const import ( - CONF_MOUNT_DIR, - CONF_TYPE_OWSERVER, - CONF_TYPE_SYSBUS, - DEFAULT_OWSERVER_HOST, - DEFAULT_OWSERVER_PORT, - DEFAULT_SYSBUS_MOUNT_DIR, + DEFAULT_HOST, + DEFAULT_PORT, DEVICE_SUPPORT_OPTIONS, DOMAIN, INPUT_ENTRY_CLEAR_OPTIONS, @@ -27,31 +23,21 @@ from .const import ( OPTION_ENTRY_SENSOR_PRECISION, PRECISION_MAPPING_FAMILY_28, ) -from .model import OWServerDeviceDescription -from .onewirehub import CannotConnect, InvalidPath, OneWireHub +from .model import OWDeviceDescription +from .onewirehub import CannotConnect, OneWireHub -DATA_SCHEMA_USER = vol.Schema( - {vol.Required(CONF_TYPE): vol.In([CONF_TYPE_OWSERVER, CONF_TYPE_SYSBUS])} -) -DATA_SCHEMA_OWSERVER = vol.Schema( +DATA_SCHEMA = vol.Schema( { - vol.Required(CONF_HOST, default=DEFAULT_OWSERVER_HOST): str, - vol.Required(CONF_PORT, default=DEFAULT_OWSERVER_PORT): int, - } -) -DATA_SCHEMA_MOUNTDIR = vol.Schema( - { - vol.Required(CONF_MOUNT_DIR, default=DEFAULT_SYSBUS_MOUNT_DIR): str, + vol.Required(CONF_HOST, default=DEFAULT_HOST): str, + vol.Required(CONF_PORT, default=DEFAULT_PORT): int, } ) -async def validate_input_owserver( - hass: HomeAssistant, data: dict[str, Any] -) -> dict[str, str]: +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, str]: """Validate the user input allows us to connect. - Data has the keys from DATA_SCHEMA_OWSERVER with values provided by the user. + Data has the keys from DATA_SCHEMA with values provided by the user. """ hub = OneWireHub(hass) @@ -65,24 +51,6 @@ async def validate_input_owserver( return {"title": host} -async def validate_input_mount_dir( - hass: HomeAssistant, data: dict[str, Any] -) -> dict[str, str]: - """Validate the user input allows us to connect. - - Data has the keys from DATA_SCHEMA_MOUNTDIR with values provided by the user. - """ - hub = OneWireHub(hass) - - mount_dir = data[CONF_MOUNT_DIR] - - # Raises InvalidDir exception on failure - await hub.check_mount_dir(mount_dir) - - # Return info that you want to store in the config entry. - return {"title": mount_dir} - - class OneWireFlowHandler(ConfigFlow, domain=DOMAIN): """Handle 1-Wire config flow.""" @@ -100,29 +68,10 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN): Let user manually input configuration. """ errors: dict[str, str] = {} - if user_input is not None: - self.onewire_config.update(user_input) - if CONF_TYPE_OWSERVER == user_input[CONF_TYPE]: - return await self.async_step_owserver() - if CONF_TYPE_SYSBUS == user_input[CONF_TYPE]: - return await self.async_step_mount_dir() - - return self.async_show_form( - step_id="user", - data_schema=DATA_SCHEMA_USER, - errors=errors, - ) - - async def async_step_owserver( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle OWServer configuration.""" - errors = {} if user_input: # Prevent duplicate entries self._async_abort_entries_match( { - CONF_TYPE: CONF_TYPE_OWSERVER, CONF_HOST: user_input[CONF_HOST], CONF_PORT: user_input[CONF_PORT], } @@ -131,7 +80,7 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN): self.onewire_config.update(user_input) try: - info = await validate_input_owserver(self.hass, user_input) + info = await validate_input(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" else: @@ -140,37 +89,8 @@ class OneWireFlowHandler(ConfigFlow, domain=DOMAIN): ) return self.async_show_form( - step_id="owserver", - data_schema=DATA_SCHEMA_OWSERVER, - errors=errors, - ) - - async def async_step_mount_dir( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: - """Handle SysBus configuration.""" - errors = {} - if user_input: - # Prevent duplicate entries - await self.async_set_unique_id( - f"{CONF_TYPE_SYSBUS}:{user_input[CONF_MOUNT_DIR]}" - ) - self._abort_if_unique_id_configured() - - self.onewire_config.update(user_input) - - try: - info = await validate_input_mount_dir(self.hass, user_input) - except InvalidPath: - errors["base"] = "invalid_path" - else: - return self.async_create_entry( - title=info["title"], data=self.onewire_config - ) - - return self.async_show_form( - step_id="mount_dir", - data_schema=DATA_SCHEMA_MOUNTDIR, + step_id="user", + data_schema=DATA_SCHEMA, errors=errors, ) @@ -188,8 +108,8 @@ class OnewireOptionsFlowHandler(OptionsFlow): """Initialize OneWire Network options flow.""" self.entry_id = config_entry.entry_id self.options = dict(config_entry.options) - self.configurable_devices: dict[str, OWServerDeviceDescription] = {} - self.devices_to_configure: dict[str, OWServerDeviceDescription] = {} + self.configurable_devices: dict[str, OWDeviceDescription] = {} + self.devices_to_configure: dict[str, OWDeviceDescription] = {} self.current_device: str = "" async def async_step_init( @@ -197,12 +117,7 @@ class OnewireOptionsFlowHandler(OptionsFlow): ) -> FlowResult: """Manage the options.""" controller: OneWireHub = self.hass.data[DOMAIN][self.entry_id] - if controller.type == CONF_TYPE_SYSBUS: - return self.async_abort( - reason="SysBus setup does not have any config options." - ) - - all_devices: list[OWServerDeviceDescription] = controller.devices # type: ignore[assignment] + all_devices: list[OWDeviceDescription] = controller.devices # type: ignore[assignment] if not all_devices: return self.async_abort(reason="No configurable devices found.") diff --git a/homeassistant/components/onewire/const.py b/homeassistant/components/onewire/const.py index 7fce90cc012..ed744caee17 100644 --- a/homeassistant/components/onewire/const.py +++ b/homeassistant/components/onewire/const.py @@ -3,14 +3,8 @@ from __future__ import annotations from homeassistant.const import Platform -CONF_MOUNT_DIR = "mount_dir" - -CONF_TYPE_OWSERVER = "OWServer" -CONF_TYPE_SYSBUS = "SysBus" - -DEFAULT_OWSERVER_HOST = "localhost" -DEFAULT_OWSERVER_PORT = 4304 -DEFAULT_SYSBUS_MOUNT_DIR = "/sys/bus/w1/devices/" +DEFAULT_HOST = "localhost" +DEFAULT_PORT = 4304 DOMAIN = "onewire" @@ -18,7 +12,7 @@ DEVICE_KEYS_0_3 = range(4) DEVICE_KEYS_0_7 = range(8) DEVICE_KEYS_A_B = ("A", "B") -DEVICE_SUPPORT_OWSERVER = { +DEVICE_SUPPORT = { "05": (), "10": (), "12": (), @@ -35,7 +29,6 @@ DEVICE_SUPPORT_OWSERVER = { "7E": ("EDS0066", "EDS0068"), "EF": ("HB_HUB", "HB_MOISTURE_METER", "HobbyBoards_EF"), } -DEVICE_SUPPORT_SYSBUS = ["10", "22", "28", "3B", "42"] DEVICE_SUPPORT_OPTIONS = ["28"] diff --git a/homeassistant/components/onewire/manifest.json b/homeassistant/components/onewire/manifest.json index d7b301f9c23..87adc23ae18 100644 --- a/homeassistant/components/onewire/manifest.json +++ b/homeassistant/components/onewire/manifest.json @@ -3,8 +3,8 @@ "name": "1-Wire", "documentation": "https://www.home-assistant.io/integrations/onewire", "config_flow": true, - "requirements": ["pyownet==0.10.0.post1", "pi1wire==0.1.0"], + "requirements": ["pyownet==0.10.0.post1"], "codeowners": ["@garbled1", "@epenet"], "iot_class": "local_polling", - "loggers": ["pi1wire", "pyownet"] + "loggers": ["pyownet"] } diff --git a/homeassistant/components/onewire/model.py b/homeassistant/components/onewire/model.py index 370b26c2530..d3fb2f22f14 100644 --- a/homeassistant/components/onewire/model.py +++ b/homeassistant/components/onewire/model.py @@ -3,29 +3,15 @@ from __future__ import annotations from dataclasses import dataclass -from pi1wire import OneWireInterface - from homeassistant.helpers.entity import DeviceInfo @dataclass class OWDeviceDescription: - """OWDeviceDescription device description class.""" + """1-Wire device description class.""" device_info: DeviceInfo - -@dataclass -class OWDirectDeviceDescription(OWDeviceDescription): - """SysBus device description class.""" - - interface: OneWireInterface - - -@dataclass -class OWServerDeviceDescription(OWDeviceDescription): - """OWServer device description class.""" - family: str id: str path: str diff --git a/homeassistant/components/onewire/onewire_entities.py b/homeassistant/components/onewire/onewire_entities.py index e00733ae387..98287c01ce1 100644 --- a/homeassistant/components/onewire/onewire_entities.py +++ b/homeassistant/components/onewire/onewire_entities.py @@ -23,7 +23,7 @@ class OneWireEntityDescription(EntityDescription): _LOGGER = logging.getLogger(__name__) -class OneWireBaseEntity(Entity): +class OneWireEntity(Entity): """Implementation of a 1-Wire entity.""" entity_description: OneWireEntityDescription @@ -35,6 +35,7 @@ class OneWireBaseEntity(Entity): device_info: DeviceInfo, device_file: str, name: str, + owproxy: protocol._Proxy, ) -> None: """Initialize the entity.""" self.entity_description = description @@ -44,6 +45,7 @@ class OneWireBaseEntity(Entity): self._device_file = device_file self._state: StateType = None self._value_raw: float | None = None + self._owproxy = owproxy @property def extra_state_attributes(self) -> dict[str, Any] | None: @@ -53,44 +55,21 @@ class OneWireBaseEntity(Entity): "raw_value": self._value_raw, } - -class OneWireProxyEntity(OneWireBaseEntity): - """Implementation of a 1-Wire entity connected through owserver.""" - - def __init__( - self, - description: OneWireEntityDescription, - device_id: str, - device_info: DeviceInfo, - device_file: str, - name: str, - owproxy: protocol._Proxy, - ) -> None: - """Initialize the sensor.""" - super().__init__( - description=description, - device_id=device_id, - device_info=device_info, - device_file=device_file, - name=name, - ) - self._owproxy = owproxy - - def _read_value_ownet(self) -> str: - """Read a value from the owserver.""" + def _read_value(self) -> str: + """Read a value from the server.""" read_bytes: bytes = self._owproxy.read(self._device_file) return read_bytes.decode().lstrip() - def _write_value_ownet(self, value: bytes) -> None: - """Write a value to the owserver.""" + def _write_value(self, value: bytes) -> None: + """Write a value to the server.""" self._owproxy.write(self._device_file, value) def update(self) -> None: """Get the latest data from the device.""" try: - self._value_raw = float(self._read_value_ownet()) + self._value_raw = float(self._read_value()) except protocol.Error as exc: - _LOGGER.error("Owserver failure in read(), got: %s", exc) + _LOGGER.error("Failure to read server value, got: %s", exc) self._state = None else: if self.entity_description.read_mode == READ_MODE_INT: diff --git a/homeassistant/components/onewire/onewirehub.py b/homeassistant/components/onewire/onewirehub.py index 63ed3e8cef9..a412f87deaa 100644 --- a/homeassistant/components/onewire/onewirehub.py +++ b/homeassistant/components/onewire/onewirehub.py @@ -5,7 +5,6 @@ import logging import os from typing import TYPE_CHECKING -from pi1wire import Pi1Wire from pyownet import protocol from homeassistant.config_entries import ConfigEntry @@ -17,7 +16,6 @@ from homeassistant.const import ( ATTR_VIA_DEVICE, CONF_HOST, CONF_PORT, - CONF_TYPE, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -25,21 +23,13 @@ from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import DeviceInfo from .const import ( - CONF_MOUNT_DIR, - CONF_TYPE_OWSERVER, - CONF_TYPE_SYSBUS, - DEVICE_SUPPORT_OWSERVER, - DEVICE_SUPPORT_SYSBUS, + DEVICE_SUPPORT, DOMAIN, MANUFACTURER_EDS, MANUFACTURER_HOBBYBOARDS, MANUFACTURER_MAXIM, ) -from .model import ( - OWDeviceDescription, - OWDirectDeviceDescription, - OWServerDeviceDescription, -) +from .model import OWDeviceDescription DEVICE_COUPLERS = { # Family : [branches] @@ -54,26 +44,24 @@ DEVICE_MANUFACTURER = { _LOGGER = logging.getLogger(__name__) -def _is_known_owserver_device(device_family: str, device_type: str) -> bool: +def _is_known_device(device_family: str, device_type: str) -> bool: """Check if device family/type is known to the library.""" if device_family in ("7E", "EF"): # EDS or HobbyBoard - return device_type in DEVICE_SUPPORT_OWSERVER[device_family] - return device_family in DEVICE_SUPPORT_OWSERVER + return device_type in DEVICE_SUPPORT[device_family] + return device_family in DEVICE_SUPPORT class OneWireHub: - """Hub to communicate with SysBus or OWServer.""" + """Hub to communicate with server.""" def __init__(self, hass: HomeAssistant) -> None: """Initialize.""" self.hass = hass - self.type: str | None = None - self.pi1proxy: Pi1Wire | None = None self.owproxy: protocol._Proxy | None = None self.devices: list[OWDeviceDescription] | None = None async def connect(self, host: str, port: int) -> None: - """Connect to the owserver host.""" + """Connect to the server.""" try: self.owproxy = await self.hass.async_add_executor_job( protocol.proxy, host, port @@ -81,32 +69,12 @@ class OneWireHub: except protocol.ConnError as exc: raise CannotConnect from exc - async def check_mount_dir(self, mount_dir: str) -> None: - """Test that the mount_dir is a valid path.""" - if not await self.hass.async_add_executor_job(os.path.isdir, mount_dir): - raise InvalidPath - self.pi1proxy = Pi1Wire(mount_dir) - async def initialize(self, config_entry: ConfigEntry) -> None: """Initialize a config entry.""" - self.type = config_entry.data[CONF_TYPE] - if self.type == CONF_TYPE_SYSBUS: - mount_dir = config_entry.data[CONF_MOUNT_DIR] - _LOGGER.debug("Initializing using SysBus %s", mount_dir) - _LOGGER.warning( - "Using the 1-Wire integration via SysBus is deprecated and will be removed " - "in Home Assistant Core 2022.6; this integration is being adjusted to comply " - "with Architectural Decision Record 0019, more information can be found here: " - "https://github.com/home-assistant/architecture/blob/master/adr/0019-GPIO.md " - "Access via OWServer is still supported" - ) - - await self.check_mount_dir(mount_dir) - elif self.type == CONF_TYPE_OWSERVER: - host = config_entry.data[CONF_HOST] - port = config_entry.data[CONF_PORT] - _LOGGER.debug("Initializing using OWServer %s:%s", host, port) - await self.connect(host, port) + host = config_entry.data[CONF_HOST] + port = config_entry.data[CONF_PORT] + _LOGGER.debug("Initializing connection to %s:%s", host, port) + await self.connect(host, port) await self.discover_devices() if TYPE_CHECKING: assert self.devices @@ -126,63 +94,22 @@ class OneWireHub: async def discover_devices(self) -> None: """Discover all devices.""" if self.devices is None: - if self.type == CONF_TYPE_SYSBUS: - self.devices = await self.hass.async_add_executor_job( - self._discover_devices_sysbus - ) - if self.type == CONF_TYPE_OWSERVER: - self.devices = await self.hass.async_add_executor_job( - self._discover_devices_owserver - ) - - def _discover_devices_sysbus(self) -> list[OWDeviceDescription]: - """Discover all sysbus devices.""" - devices: list[OWDeviceDescription] = [] - assert self.pi1proxy - all_sensors = self.pi1proxy.find_all_sensors() - if not all_sensors: - _LOGGER.error( - "No onewire sensor found. Check if dtoverlay=w1-gpio " - "is in your /boot/config.txt. " - "Check the mount_dir parameter if it's defined" + self.devices = await self.hass.async_add_executor_job( + self._discover_devices ) - for interface in all_sensors: - device_family = interface.mac_address[:2] - device_id = f"{device_family}-{interface.mac_address[2:]}" - if device_family not in DEVICE_SUPPORT_SYSBUS: - _LOGGER.warning( - "Ignoring unknown device family (%s) found for device %s", - device_family, - device_id, - ) - continue - device_info: DeviceInfo = { - ATTR_IDENTIFIERS: {(DOMAIN, device_id)}, - ATTR_MANUFACTURER: DEVICE_MANUFACTURER.get( - device_family, MANUFACTURER_MAXIM - ), - ATTR_MODEL: device_family, - ATTR_NAME: device_id, - } - device = OWDirectDeviceDescription( - device_info=device_info, - interface=interface, - ) - devices.append(device) - return devices - def _discover_devices_owserver( + def _discover_devices( self, path: str = "/", parent_id: str | None = None ) -> list[OWDeviceDescription]: - """Discover all owserver devices.""" + """Discover all server devices.""" devices: list[OWDeviceDescription] = [] assert self.owproxy for device_path in self.owproxy.dir(path): device_id = os.path.split(os.path.split(device_path)[0])[1] device_family = self.owproxy.read(f"{device_path}family").decode() _LOGGER.debug("read `%sfamily`: %s", device_path, device_family) - device_type = self._get_device_type_owserver(device_path) - if not _is_known_owserver_device(device_family, device_type): + device_type = self._get_device_type(device_path) + if not _is_known_device(device_family, device_type): _LOGGER.warning( "Ignoring unknown device family/type (%s/%s) found for device %s", device_family, @@ -200,7 +127,7 @@ class OneWireHub: } if parent_id: device_info[ATTR_VIA_DEVICE] = (DOMAIN, parent_id) - device = OWServerDeviceDescription( + device = OWDeviceDescription( device_info=device_info, id=device_id, family=device_family, @@ -210,13 +137,13 @@ class OneWireHub: devices.append(device) if device_branches := DEVICE_COUPLERS.get(device_family): for branch in device_branches: - devices += self._discover_devices_owserver( + devices += self._discover_devices( f"{device_path}{branch}", device_id ) return devices - def _get_device_type_owserver(self, device_path: str) -> str: + def _get_device_type(self, device_path: str) -> str: """Get device model.""" if TYPE_CHECKING: assert self.owproxy diff --git a/homeassistant/components/onewire/sensor.py b/homeassistant/components/onewire/sensor.py index 759b1f9eccf..9f376a6df7a 100644 --- a/homeassistant/components/onewire/sensor.py +++ b/homeassistant/components/onewire/sensor.py @@ -1,16 +1,13 @@ """Support for 1-Wire environment sensors.""" from __future__ import annotations -import asyncio from collections.abc import Callable, Mapping import copy from dataclasses import dataclass import logging import os from types import MappingProxyType -from typing import TYPE_CHECKING, Any - -from pi1wire import InvalidCRCException, OneWireInterface, UnsupportResponseException +from typing import Any from homeassistant.components.sensor import ( SensorDeviceClass, @@ -20,7 +17,6 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - CONF_TYPE, ELECTRIC_POTENTIAL_VOLT, LIGHT_LUX, PERCENTAGE, @@ -29,13 +25,10 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from .const import ( - CONF_TYPE_OWSERVER, - CONF_TYPE_SYSBUS, DEVICE_KEYS_0_3, DEVICE_KEYS_A_B, DOMAIN, @@ -45,12 +38,7 @@ from .const import ( READ_MODE_FLOAT, READ_MODE_INT, ) -from .model import OWDirectDeviceDescription, OWServerDeviceDescription -from .onewire_entities import ( - OneWireBaseEntity, - OneWireEntityDescription, - OneWireProxyEntity, -) +from .onewire_entities import OneWireEntity, OneWireEntityDescription from .onewirehub import OneWireHub @@ -263,8 +251,6 @@ DEVICE_SENSORS: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { } # EF sensors are usually hobbyboards specialized sensors. -# These can only be read by OWFS. Currently this driver only supports them -# via owserver (network protocol) HOBBYBOARD_EF: dict[str, tuple[OneWireSensorEntityDescription, ...]] = { "HobbyBoards_EF": ( @@ -383,109 +369,72 @@ async def async_setup_entry( """Set up 1-Wire platform.""" onewirehub = hass.data[DOMAIN][config_entry.entry_id] entities = await hass.async_add_executor_job( - get_entities, onewirehub, config_entry.data, config_entry.options + get_entities, onewirehub, config_entry.options ) async_add_entities(entities, True) def get_entities( - onewirehub: OneWireHub, - config: MappingProxyType[str, Any], - options: MappingProxyType[str, Any], -) -> list[SensorEntity]: + onewirehub: OneWireHub, options: MappingProxyType[str, Any] +) -> list[OneWireSensor]: """Get a list of entities.""" if not onewirehub.devices: return [] - entities: list[SensorEntity] = [] - conf_type = config[CONF_TYPE] - # We have an owserver on a remote(or local) host/port - if conf_type == CONF_TYPE_OWSERVER: - assert onewirehub.owproxy - for device in onewirehub.devices: - if TYPE_CHECKING: - assert isinstance(device, OWServerDeviceDescription) - family = device.family - device_type = device.type - device_id = device.id - device_info = device.device_info - device_sub_type = "std" - device_path = device.path - if "EF" in family: - device_sub_type = "HobbyBoard" - family = device_type - elif "7E" in family: - device_sub_type = "EDS" - family = device_type + entities: list[OneWireSensor] = [] + assert onewirehub.owproxy + for device in onewirehub.devices: + family = device.family + device_type = device.type + device_id = device.id + device_info = device.device_info + device_sub_type = "std" + device_path = device.path + if "EF" in family: + device_sub_type = "HobbyBoard" + family = device_type + elif "7E" in family: + device_sub_type = "EDS" + family = device_type - if family not in get_sensor_types(device_sub_type): - continue - for description in get_sensor_types(device_sub_type)[family]: - if description.key.startswith("moisture/"): - s_id = description.key.split(".")[1] - is_leaf = int( - onewirehub.owproxy.read( - f"{device_path}moisture/is_leaf.{s_id}" - ).decode() - ) - if is_leaf: - description = copy.deepcopy(description) - description.device_class = SensorDeviceClass.HUMIDITY - description.native_unit_of_measurement = PERCENTAGE - description.name = f"Wetness {s_id}" - override_key = None - if description.override_key: - override_key = description.override_key(device_id, options) - device_file = os.path.join( - os.path.split(device.path)[0], - override_key or description.key, + if family not in get_sensor_types(device_sub_type): + continue + for description in get_sensor_types(device_sub_type)[family]: + if description.key.startswith("moisture/"): + s_id = description.key.split(".")[1] + is_leaf = int( + onewirehub.owproxy.read( + f"{device_path}moisture/is_leaf.{s_id}" + ).decode() ) - name = f"{device_id} {description.name}" - entities.append( - OneWireProxySensor( - description=description, - device_id=device_id, - device_file=device_file, - device_info=device_info, - name=name, - owproxy=onewirehub.owproxy, - ) - ) - - # We have a raw GPIO ow sensor on a Pi - elif conf_type == CONF_TYPE_SYSBUS: - for device in onewirehub.devices: - if TYPE_CHECKING: - assert isinstance(device, OWDirectDeviceDescription) - p1sensor: OneWireInterface = device.interface - family = p1sensor.mac_address[:2] - device_id = f"{family}-{p1sensor.mac_address[2:]}" - device_info = device.device_info - description = SIMPLE_TEMPERATURE_SENSOR_DESCRIPTION - device_file = f"/sys/bus/w1/devices/{device_id}/w1_slave" + if is_leaf: + description = copy.deepcopy(description) + description.device_class = SensorDeviceClass.HUMIDITY + description.native_unit_of_measurement = PERCENTAGE + description.name = f"Wetness {s_id}" + override_key = None + if description.override_key: + override_key = description.override_key(device_id, options) + device_file = os.path.join( + os.path.split(device.path)[0], + override_key or description.key, + ) name = f"{device_id} {description.name}" entities.append( - OneWireDirectSensor( + OneWireSensor( description=description, device_id=device_id, device_file=device_file, device_info=device_info, name=name, - owsensor=p1sensor, + owproxy=onewirehub.owproxy, ) ) - return entities -class OneWireSensor(OneWireBaseEntity, SensorEntity): - """Mixin for sensor specific attributes.""" - - entity_description: OneWireSensorEntityDescription - - -class OneWireProxySensor(OneWireProxyEntity, OneWireSensor): - """Implementation of a 1-Wire sensor connected through owserver.""" +class OneWireSensor(OneWireEntity, SensorEntity): + """Implementation of a 1-Wire sensor.""" entity_description: OneWireSensorEntityDescription @@ -493,69 +442,3 @@ class OneWireProxySensor(OneWireProxyEntity, OneWireSensor): def native_value(self) -> StateType: """Return the state of the entity.""" return self._state - - -class OneWireDirectSensor(OneWireSensor): - """Implementation of a 1-Wire sensor directly connected to RPI GPIO.""" - - def __init__( - self, - description: OneWireSensorEntityDescription, - device_id: str, - device_info: DeviceInfo, - device_file: str, - name: str, - owsensor: OneWireInterface, - ) -> None: - """Initialize the sensor.""" - super().__init__( - description=description, - device_id=device_id, - device_info=device_info, - device_file=device_file, - name=name, - ) - self._attr_unique_id = device_file - self._owsensor = owsensor - - @property - def native_value(self) -> StateType: - """Return the state of the entity.""" - return self._state - - async def get_temperature(self) -> float: - """Get the latest data from the device.""" - attempts = 1 - while True: - try: - return await self.hass.async_add_executor_job( - self._owsensor.get_temperature - ) - except UnsupportResponseException as ex: - _LOGGER.debug( - "Cannot read from sensor %s (retry attempt %s): %s", - self._device_file, - attempts, - ex, - ) - await asyncio.sleep(0.2) - attempts += 1 - if attempts > 10: - raise - - async def async_update(self) -> None: - """Get the latest data from the device.""" - try: - self._value_raw = await self.get_temperature() - self._state = round(self._value_raw, 1) - except ( - FileNotFoundError, - InvalidCRCException, - UnsupportResponseException, - ) as ex: - _LOGGER.warning( - "Cannot read from sensor %s: %s", - self._device_file, - ex, - ) - self._state = None diff --git a/homeassistant/components/onewire/strings.json b/homeassistant/components/onewire/strings.json index b685479d359..734971cb2a1 100644 --- a/homeassistant/components/onewire/strings.json +++ b/homeassistant/components/onewire/strings.json @@ -4,22 +4,15 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" }, "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "invalid_path": "Directory not found." + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" }, "step": { - "owserver": { + "user": { "data": { "host": "[%key:common::config_flow::data::host%]", "port": "[%key:common::config_flow::data::port%]" }, - "title": "Set owserver details" - }, - "user": { - "data": { - "type": "Connection type" - }, - "title": "Set up 1-Wire" + "title": "Set server details" } } }, @@ -28,11 +21,6 @@ "device_not_selected": "Select devices to configure" }, "step": { - "ack_no_options": { - "data": {}, - "description": "There are no options for the SysBus implementation", - "title": "OneWire SysBus Options" - }, "device_selection": { "data": { "clear_device_options": "Clear all device configurations", diff --git a/homeassistant/components/onewire/switch.py b/homeassistant/components/onewire/switch.py index a05757936f9..764cc403681 100644 --- a/homeassistant/components/onewire/switch.py +++ b/homeassistant/components/onewire/switch.py @@ -3,25 +3,22 @@ from __future__ import annotations from dataclasses import dataclass import os -from typing import TYPE_CHECKING, Any +from typing import Any from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( - CONF_TYPE_OWSERVER, DEVICE_KEYS_0_3, DEVICE_KEYS_0_7, DEVICE_KEYS_A_B, DOMAIN, READ_MODE_BOOL, ) -from .model import OWServerDeviceDescription -from .onewire_entities import OneWireEntityDescription, OneWireProxyEntity +from .onewire_entities import OneWireEntity, OneWireEntityDescription from .onewirehub import OneWireHub @@ -153,24 +150,20 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up 1-Wire platform.""" - # Only OWServer implementation works with switches - if config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER: - onewirehub = hass.data[DOMAIN][config_entry.entry_id] + onewirehub = hass.data[DOMAIN][config_entry.entry_id] - entities = await hass.async_add_executor_job(get_entities, onewirehub) - async_add_entities(entities, True) + entities = await hass.async_add_executor_job(get_entities, onewirehub) + async_add_entities(entities, True) -def get_entities(onewirehub: OneWireHub) -> list[SwitchEntity]: +def get_entities(onewirehub: OneWireHub) -> list[OneWireSwitch]: """Get a list of entities.""" if not onewirehub.devices: return [] - entities: list[SwitchEntity] = [] + entities: list[OneWireSwitch] = [] for device in onewirehub.devices: - if TYPE_CHECKING: - assert isinstance(device, OWServerDeviceDescription) family = device.family device_type = device.type device_id = device.id @@ -186,7 +179,7 @@ def get_entities(onewirehub: OneWireHub) -> list[SwitchEntity]: device_file = os.path.join(os.path.split(device.path)[0], description.key) name = f"{device_id} {description.name}" entities.append( - OneWireProxySwitch( + OneWireSwitch( description=description, device_id=device_id, device_file=device_file, @@ -199,7 +192,7 @@ def get_entities(onewirehub: OneWireHub) -> list[SwitchEntity]: return entities -class OneWireProxySwitch(OneWireProxyEntity, SwitchEntity): +class OneWireSwitch(OneWireEntity, SwitchEntity): """Implementation of a 1-Wire switch.""" entity_description: OneWireSwitchEntityDescription @@ -211,8 +204,8 @@ class OneWireProxySwitch(OneWireProxyEntity, SwitchEntity): def turn_on(self, **kwargs: Any) -> None: """Turn the entity on.""" - self._write_value_ownet(b"1") + self._write_value(b"1") def turn_off(self, **kwargs: Any) -> None: """Turn the entity off.""" - self._write_value_ownet(b"0") + self._write_value(b"0") diff --git a/requirements_all.txt b/requirements_all.txt index 07772173bd8..644b5c2b74b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1210,9 +1210,6 @@ pexpect==4.6.0 # homeassistant.components.modem_callerid phone_modem==0.1.1 -# homeassistant.components.onewire -pi1wire==0.1.0 - # homeassistant.components.remote_rpi_gpio pigpio==1.78 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9cfa8a6945b..a7859ff3e1e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -812,9 +812,6 @@ pexpect==4.6.0 # homeassistant.components.modem_callerid phone_modem==0.1.1 -# homeassistant.components.onewire -pi1wire==0.1.0 - # homeassistant.components.pilight pilight==0.1.1 diff --git a/tests/components/onewire/__init__.py b/tests/components/onewire/__init__.py index 0ab8b8f6917..d189db8af1a 100644 --- a/tests/components/onewire/__init__.py +++ b/tests/components/onewire/__init__.py @@ -7,7 +7,6 @@ from unittest.mock import MagicMock from pyownet.protocol import ProtocolError -from homeassistant.components.onewire.const import DEFAULT_SYSBUS_MOUNT_DIR from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_IDENTIFIERS, @@ -29,7 +28,6 @@ from .const import ( ATTR_UNIQUE_ID, FIXED_ATTRIBUTES, MOCK_OWPROXY_DEVICES, - MOCK_SYSBUS_DEVICES, ) @@ -181,30 +179,3 @@ def _setup_owproxy_mock_device_reads( device_sensors = mock_device.get(platform, []) for expected_sensor in device_sensors: sub_read_side_effect.append(expected_sensor[ATTR_INJECT_READS]) - - -def setup_sysbus_mock_devices( - platform: str, device_ids: list[str] -) -> tuple[list[str], list[Any]]: - """Set up mock for sysbus.""" - glob_result = [] - read_side_effect = [] - - for device_id in device_ids: - mock_device = MOCK_SYSBUS_DEVICES[device_id] - - # Setup directory listing - glob_result += [f"/{DEFAULT_SYSBUS_MOUNT_DIR}/{device_id}"] - - # Setup sub-device reads - device_sensors = mock_device.get(platform, []) - for expected_sensor in device_sensors: - if isinstance(expected_sensor[ATTR_INJECT_READS], list): - read_side_effect += expected_sensor[ATTR_INJECT_READS] - else: - read_side_effect.append(expected_sensor[ATTR_INJECT_READS]) - - # Ensure enough read side effect - read_side_effect.extend([FileNotFoundError("Missing injected value")] * 20) - - return (glob_result, read_side_effect) diff --git a/tests/components/onewire/conftest.py b/tests/components/onewire/conftest.py index ab456c7d7df..9b8b53859ea 100644 --- a/tests/components/onewire/conftest.py +++ b/tests/components/onewire/conftest.py @@ -4,15 +4,9 @@ from unittest.mock import MagicMock, patch from pyownet.protocol import ConnError import pytest -from homeassistant.components.onewire.const import ( - CONF_MOUNT_DIR, - CONF_TYPE_OWSERVER, - CONF_TYPE_SYSBUS, - DEFAULT_SYSBUS_MOUNT_DIR, - DOMAIN, -) +from homeassistant.components.onewire.const import DOMAIN from homeassistant.config_entries import SOURCE_USER, ConfigEntry -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from .const import MOCK_OWPROXY_DEVICES @@ -33,7 +27,6 @@ def get_config_entry(hass: HomeAssistant) -> ConfigEntry: domain=DOMAIN, source=SOURCE_USER, data={ - CONF_TYPE: CONF_TYPE_OWSERVER, CONF_HOST: "1.2.3.4", CONF_PORT: 1234, }, @@ -49,24 +42,6 @@ def get_config_entry(hass: HomeAssistant) -> ConfigEntry: return config_entry -@pytest.fixture(name="sysbus_config_entry") -def get_sysbus_config_entry(hass: HomeAssistant) -> ConfigEntry: - """Create and register mock config entry.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - source=SOURCE_USER, - data={ - CONF_TYPE: CONF_TYPE_SYSBUS, - CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR, - }, - unique_id=f"{CONF_TYPE_SYSBUS}:{DEFAULT_SYSBUS_MOUNT_DIR}", - options={}, - entry_id="3", - ) - config_entry.add_to_hass(hass) - return config_entry - - @pytest.fixture(name="owproxy") def get_owproxy() -> MagicMock: """Mock owproxy.""" @@ -82,12 +57,3 @@ def get_owproxy_with_connerror() -> MagicMock: side_effect=ConnError, ) as owproxy: yield owproxy - - -@pytest.fixture(name="sysbus") -def get_sysbus() -> MagicMock: - """Mock sysbus.""" - with patch( - "homeassistant.components.onewire.onewirehub.os.path.isdir", return_value=True - ): - yield diff --git a/tests/components/onewire/const.py b/tests/components/onewire/const.py index d77b374c7c4..c28ec017a9b 100644 --- a/tests/components/onewire/const.py +++ b/tests/components/onewire/const.py @@ -1,5 +1,4 @@ """Constants for 1-Wire integration.""" -from pi1wire import InvalidCRCException, UnsupportResponseException from pyownet.protocol import Error as ProtocolError from homeassistant.components.binary_sensor import BinarySensorDeviceClass @@ -1131,142 +1130,3 @@ MOCK_OWPROXY_DEVICES = { ], }, } - -MOCK_SYSBUS_DEVICES = { - "00-111111111111": { - ATTR_UNKNOWN_DEVICE: True, - }, - "10-111111111111": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "10-111111111111")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "10", - ATTR_NAME: "10-111111111111", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.10_111111111111_temperature", - ATTR_INJECT_READS: 25.123, - ATTR_STATE: "25.1", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/10-111111111111/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "22-111111111111": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "22-111111111111")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "22", - ATTR_NAME: "22-111111111111", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.22_111111111111_temperature", - ATTR_INJECT_READS: FileNotFoundError, - ATTR_STATE: STATE_UNKNOWN, - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/22-111111111111/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "28-111111111111": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "28-111111111111")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "28", - ATTR_NAME: "28-111111111111", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.28_111111111111_temperature", - ATTR_INJECT_READS: InvalidCRCException, - ATTR_STATE: STATE_UNKNOWN, - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/28-111111111111/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "3B-111111111111": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "3B-111111111111")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "3B", - ATTR_NAME: "3B-111111111111", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.3b_111111111111_temperature", - ATTR_INJECT_READS: 29.993, - ATTR_STATE: "30.0", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/3B-111111111111/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "42-111111111111": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "42-111111111111")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "42", - ATTR_NAME: "42-111111111111", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.42_111111111111_temperature", - ATTR_INJECT_READS: UnsupportResponseException, - ATTR_STATE: STATE_UNKNOWN, - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/42-111111111111/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "42-111111111112": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "42-111111111112")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "42", - ATTR_NAME: "42-111111111112", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.42_111111111112_temperature", - ATTR_INJECT_READS: [UnsupportResponseException] * 9 + [27.993], - ATTR_STATE: "28.0", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/42-111111111112/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, - "42-111111111113": { - ATTR_DEVICE_INFO: { - ATTR_IDENTIFIERS: {(DOMAIN, "42-111111111113")}, - ATTR_MANUFACTURER: MANUFACTURER_MAXIM, - ATTR_MODEL: "42", - ATTR_NAME: "42-111111111113", - }, - Platform.SENSOR: [ - { - ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE, - ATTR_ENTITY_ID: "sensor.42_111111111113_temperature", - ATTR_INJECT_READS: [UnsupportResponseException] * 10 + [27.993], - ATTR_STATE: STATE_UNKNOWN, - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, - ATTR_UNIQUE_ID: "/sys/bus/w1/devices/42-111111111113/w1_slave", - ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS, - }, - ], - }, -} diff --git a/tests/components/onewire/test_binary_sensor.py b/tests/components/onewire/test_binary_sensor.py index ee55a550cea..636228af143 100644 --- a/tests/components/onewire/test_binary_sensor.py +++ b/tests/components/onewire/test_binary_sensor.py @@ -1,4 +1,4 @@ -"""Tests for 1-Wire devices connected on OWServer.""" +"""Tests for 1-Wire binary sensors.""" import logging from unittest.mock import MagicMock, patch @@ -27,7 +27,7 @@ def override_platforms(): yield -async def test_owserver_binary_sensor( +async def test_binary_sensors( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, diff --git a/tests/components/onewire/test_config_flow.py b/tests/components/onewire/test_config_flow.py index 7bf7b58ebfa..d599c10ea90 100644 --- a/tests/components/onewire/test_config_flow.py +++ b/tests/components/onewire/test_config_flow.py @@ -4,15 +4,9 @@ from unittest.mock import AsyncMock, patch from pyownet import protocol import pytest -from homeassistant.components.onewire.const import ( - CONF_MOUNT_DIR, - CONF_TYPE_OWSERVER, - CONF_TYPE_SYSBUS, - DEFAULT_SYSBUS_MOUNT_DIR, - DOMAIN, -) +from homeassistant.components.onewire.const import DOMAIN from homeassistant.config_entries import SOURCE_USER, ConfigEntry -from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import ( RESULT_TYPE_ABORT, @@ -30,23 +24,14 @@ def override_async_setup_entry() -> AsyncMock: yield mock_setup_entry -async def test_user_owserver(hass: HomeAssistant, mock_setup_entry: AsyncMock): - """Test OWServer user flow.""" +async def test_user_flow(hass: HomeAssistant, mock_setup_entry: AsyncMock): + """Test user flow.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] == RESULT_TYPE_FORM assert not result["errors"] - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_TYPE: CONF_TYPE_OWSERVER}, - ) - - assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "owserver" - assert not result["errors"] - # Invalid server with patch( "homeassistant.components.onewire.onewirehub.protocol.proxy", @@ -58,7 +43,7 @@ async def test_user_owserver(hass: HomeAssistant, mock_setup_entry: AsyncMock): ) assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "owserver" + assert result["step_id"] == "user" assert result["errors"] == {"base": "cannot_connect"} # Valid server @@ -73,7 +58,6 @@ async def test_user_owserver(hass: HomeAssistant, mock_setup_entry: AsyncMock): assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["title"] == "1.2.3.4" assert result["data"] == { - CONF_TYPE: CONF_TYPE_OWSERVER, CONF_HOST: "1.2.3.4", CONF_PORT: 1234, } @@ -81,10 +65,10 @@ async def test_user_owserver(hass: HomeAssistant, mock_setup_entry: AsyncMock): assert len(mock_setup_entry.mock_calls) == 1 -async def test_user_owserver_duplicate( +async def test_user_duplicate( hass: HomeAssistant, config_entry: ConfigEntry, mock_setup_entry: AsyncMock ): - """Test OWServer flow.""" + """Test user duplicate flow.""" await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() assert len(hass.config_entries.async_entries(DOMAIN)) == 1 @@ -93,15 +77,7 @@ async def test_user_owserver_duplicate( DOMAIN, context={"source": SOURCE_USER} ) assert result["type"] == RESULT_TYPE_FORM - assert not result["errors"] - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_TYPE: CONF_TYPE_OWSERVER}, - ) - - assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "owserver" + assert result["step_id"] == "user" assert not result["errors"] # Duplicate server @@ -113,93 +89,3 @@ async def test_user_owserver_duplicate( assert result["reason"] == "already_configured" await hass.async_block_till_done() assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_user_sysbus(hass: HomeAssistant, mock_setup_entry: AsyncMock): - """Test SysBus flow.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER} - ) - assert result["type"] == RESULT_TYPE_FORM - assert not result["errors"] - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_TYPE: CONF_TYPE_SYSBUS}, - ) - - assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "mount_dir" - assert not result["errors"] - - # Invalid path - with patch( - "homeassistant.components.onewire.onewirehub.os.path.isdir", - return_value=False, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_MOUNT_DIR: "/sys/bus/invalid_directory"}, - ) - - assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "mount_dir" - assert result["errors"] == {"base": "invalid_path"} - - # Valid path - with patch( - "homeassistant.components.onewire.onewirehub.os.path.isdir", - return_value=True, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_MOUNT_DIR: "/sys/bus/directory"}, - ) - - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "/sys/bus/directory" - assert result["data"] == { - CONF_TYPE: CONF_TYPE_SYSBUS, - CONF_MOUNT_DIR: "/sys/bus/directory", - } - await hass.async_block_till_done() - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_user_sysbus_duplicate( - hass: HomeAssistant, sysbus_config_entry: ConfigEntry, mock_setup_entry: AsyncMock -): - """Test SysBus duplicate flow.""" - await hass.config_entries.async_setup(sysbus_config_entry.entry_id) - await hass.async_block_till_done() - assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER} - ) - assert result["type"] == RESULT_TYPE_FORM - assert not result["errors"] - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_TYPE: CONF_TYPE_SYSBUS}, - ) - - assert result["type"] == RESULT_TYPE_FORM - assert result["step_id"] == "mount_dir" - assert not result["errors"] - - # Valid path - with patch( - "homeassistant.components.onewire.onewirehub.os.path.isdir", - return_value=True, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR}, - ) - - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "already_configured" - await hass.async_block_till_done() - assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/onewire/test_diagnostics.py b/tests/components/onewire/test_diagnostics.py index ded4811a586..e279e3a2633 100644 --- a/tests/components/onewire/test_diagnostics.py +++ b/tests/components/onewire/test_diagnostics.py @@ -52,7 +52,6 @@ async def test_entry_diagnostics( "data": { "host": REDACTED, "port": 1234, - "type": "OWServer", }, "options": { "device_options": { diff --git a/tests/components/onewire/test_init.py b/tests/components/onewire/test_init.py index 763cdc9c071..fecade521a8 100644 --- a/tests/components/onewire/test_init.py +++ b/tests/components/onewire/test_init.py @@ -1,5 +1,4 @@ """Tests for 1-Wire config flow.""" -import logging from unittest.mock import MagicMock from pyownet import protocol @@ -11,7 +10,7 @@ from homeassistant.core import HomeAssistant @pytest.mark.usefixtures("owproxy_with_connerror") -async def test_owserver_connect_failure(hass: HomeAssistant, config_entry: ConfigEntry): +async def test_connect_failure(hass: HomeAssistant, config_entry: ConfigEntry): """Test connection failure raises ConfigEntryNotReady.""" await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() @@ -21,7 +20,7 @@ async def test_owserver_connect_failure(hass: HomeAssistant, config_entry: Confi assert not hass.data.get(DOMAIN) -async def test_owserver_listing_failure( +async def test_listing_failure( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock ): """Test listing failure raises ConfigEntryNotReady.""" @@ -49,34 +48,3 @@ async def test_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): assert config_entry.state is ConfigEntryState.NOT_LOADED assert not hass.data.get(DOMAIN) - - -@pytest.mark.usefixtures("sysbus") -async def test_warning_no_devices( - hass: HomeAssistant, - sysbus_config_entry: ConfigEntry, - caplog: pytest.LogCaptureFixture, -): - """Test warning is generated when no sysbus devices found.""" - with caplog.at_level(logging.WARNING, logger="homeassistant.components.onewire"): - await hass.config_entries.async_setup(sysbus_config_entry.entry_id) - await hass.async_block_till_done() - assert "No onewire sensor found. Check if dtoverlay=w1-gpio" in caplog.text - - -@pytest.mark.usefixtures("sysbus") -async def test_unload_sysbus_entry( - hass: HomeAssistant, sysbus_config_entry: ConfigEntry -): - """Test being able to unload an entry.""" - await hass.config_entries.async_setup(sysbus_config_entry.entry_id) - await hass.async_block_till_done() - - assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert sysbus_config_entry.state is ConfigEntryState.LOADED - - assert await hass.config_entries.async_unload(sysbus_config_entry.entry_id) - await hass.async_block_till_done() - - assert sysbus_config_entry.state is ConfigEntryState.NOT_LOADED - assert not hass.data.get(DOMAIN) diff --git a/tests/components/onewire/test_options_flow.py b/tests/components/onewire/test_options_flow.py index 2edb9da7ffc..e27b5a368d9 100644 --- a/tests/components/onewire/test_options_flow.py +++ b/tests/components/onewire/test_options_flow.py @@ -2,8 +2,6 @@ from unittest.mock import MagicMock, patch from homeassistant.components.onewire.const import ( - CONF_TYPE_SYSBUS, - DOMAIN, INPUT_ENTRY_CLEAR_OPTIONS, INPUT_ENTRY_DEVICE_SELECTION, ) @@ -26,13 +24,7 @@ class FakeDevice: name_by_user = "Given Name" -class FakeOWHubSysBus: - """Mock Class for mocking onewire hub.""" - - type = CONF_TYPE_SYSBUS - - -async def test_user_owserver_options_clear( +async def test_user_options_clear( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -61,7 +53,7 @@ async def test_user_owserver_options_clear( assert result["data"] == {} -async def test_user_owserver_options_empty_selection( +async def test_user_options_empty_selection( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -91,7 +83,7 @@ async def test_user_owserver_options_empty_selection( assert result["errors"] == {"base": "device_not_selected"} -async def test_user_owserver_options_set_single( +async def test_user_options_set_single( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -134,7 +126,7 @@ async def test_user_owserver_options_set_single( ) -async def test_user_owserver_options_set_multiple( +async def test_user_options_set_multiple( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -208,7 +200,7 @@ async def test_user_owserver_options_set_multiple( ) -async def test_user_owserver_options_no_devices( +async def test_user_options_no_devices( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -223,15 +215,3 @@ async def test_user_owserver_options_no_devices( await hass.async_block_till_done() assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "No configurable devices found." - - -async def test_user_sysbus_options( - hass: HomeAssistant, - config_entry: ConfigEntry, -): - """Test that SysBus options flow aborts on init.""" - hass.data[DOMAIN] = {config_entry.entry_id: FakeOWHubSysBus()} - result = await hass.config_entries.options.async_init(config_entry.entry_id) - await hass.async_block_till_done() - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "SysBus setup does not have any config options." diff --git a/tests/components/onewire/test_sensor.py b/tests/components/onewire/test_sensor.py index 6bfc68d85c8..a20714fedfc 100644 --- a/tests/components/onewire/test_sensor.py +++ b/tests/components/onewire/test_sensor.py @@ -1,4 +1,4 @@ -"""Tests for 1-Wire sensor platform.""" +"""Tests for 1-Wire sensors.""" import logging from unittest.mock import MagicMock, patch @@ -14,14 +14,8 @@ from . import ( check_device_registry, check_entities, setup_owproxy_mock_devices, - setup_sysbus_mock_devices, -) -from .const import ( - ATTR_DEVICE_INFO, - ATTR_UNKNOWN_DEVICE, - MOCK_OWPROXY_DEVICES, - MOCK_SYSBUS_DEVICES, ) +from .const import ATTR_DEVICE_INFO, ATTR_UNKNOWN_DEVICE, MOCK_OWPROXY_DEVICES from tests.common import mock_device_registry, mock_registry @@ -33,7 +27,7 @@ def override_platforms(): yield -async def test_owserver_sensor( +async def test_sensors( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, @@ -73,44 +67,3 @@ async def test_owserver_sensor( await hass.async_block_till_done() check_entities(hass, entity_registry, expected_entities) - - -@pytest.mark.usefixtures("sysbus") -@pytest.mark.parametrize("device_id", MOCK_SYSBUS_DEVICES.keys(), indirect=True) -async def test_onewiredirect_setup_valid_device( - hass: HomeAssistant, - sysbus_config_entry: ConfigEntry, - device_id: str, - caplog: pytest.LogCaptureFixture, -): - """Test that sysbus config entry works correctly.""" - device_registry = mock_device_registry(hass) - entity_registry = mock_registry(hass) - - glob_result, read_side_effect = setup_sysbus_mock_devices( - Platform.SENSOR, [device_id] - ) - - mock_device = MOCK_SYSBUS_DEVICES[device_id] - expected_entities = mock_device.get(Platform.SENSOR, []) - expected_devices = ensure_list(mock_device.get(ATTR_DEVICE_INFO)) - - with patch("pi1wire._finder.glob.glob", return_value=glob_result,), patch( - "pi1wire.OneWire.get_temperature", - side_effect=read_side_effect, - ), caplog.at_level( - logging.WARNING, logger="homeassistant.components.onewire" - ), patch( - "homeassistant.components.onewire.sensor.asyncio.sleep" - ): - await hass.config_entries.async_setup(sysbus_config_entry.entry_id) - await hass.async_block_till_done() - assert "No onewire sensor found. Check if dtoverlay=w1-gpio" not in caplog.text - if mock_device.get(ATTR_UNKNOWN_DEVICE): - assert "Ignoring unknown device family" in caplog.text - else: - assert "Ignoring unknown device family" not in caplog.text - - check_device_registry(device_registry, expected_devices) - assert len(entity_registry.entities) == len(expected_entities) - check_entities(hass, entity_registry, expected_entities) diff --git a/tests/components/onewire/test_switch.py b/tests/components/onewire/test_switch.py index 32212f84b34..8b60232330f 100644 --- a/tests/components/onewire/test_switch.py +++ b/tests/components/onewire/test_switch.py @@ -1,4 +1,4 @@ -"""Tests for 1-Wire devices connected on OWServer.""" +"""Tests for 1-Wire switches.""" import logging from unittest.mock import MagicMock, patch @@ -35,7 +35,7 @@ def override_platforms(): yield -async def test_owserver_switch( +async def test_switches( hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock, From 4db289ad6e2656aada241f4b70d422f28ebcde84 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Mon, 9 May 2022 07:22:51 -0400 Subject: [PATCH 301/930] Remove deprecated yaml config from Deluge (#71487) --- .../components/deluge/config_flow.py | 24 +-------- homeassistant/components/deluge/sensor.py | 52 ++----------------- homeassistant/components/deluge/switch.py | 44 ++-------------- tests/components/deluge/__init__.py | 18 +------ tests/components/deluge/test_config_flow.py | 31 +---------- 5 files changed, 12 insertions(+), 157 deletions(-) diff --git a/homeassistant/components/deluge/config_flow.py b/homeassistant/components/deluge/config_flow.py index fc0dd5ff300..2f38d4d447d 100644 --- a/homeassistant/components/deluge/config_flow.py +++ b/homeassistant/components/deluge/config_flow.py @@ -1,7 +1,6 @@ """Config flow for the Deluge integration.""" from __future__ import annotations -import logging import socket from ssl import SSLError from typing import Any @@ -12,8 +11,6 @@ import voluptuous as vol from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow from homeassistant.const import ( CONF_HOST, - CONF_MONITORED_VARIABLES, - CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SOURCE, @@ -30,8 +27,6 @@ from .const import ( DOMAIN, ) -_LOGGER = logging.getLogger(__name__) - class DelugeFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a config flow for Deluge.""" @@ -41,11 +36,8 @@ class DelugeFlowHandler(ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle a flow initiated by the user.""" errors = {} - title = None if user_input is not None: - if CONF_NAME in user_input: - title = user_input.pop(CONF_NAME) if (error := await self.validate_input(user_input)) is None: for entry in self._async_current_entries(): if ( @@ -60,7 +52,7 @@ class DelugeFlowHandler(ConfigFlow, domain=DOMAIN): return self.async_abort(reason="reauth_successful") return self.async_abort(reason="already_configured") return self.async_create_entry( - title=title or DEFAULT_NAME, + title=DEFAULT_NAME, data=user_input, ) errors["base"] = error @@ -87,20 +79,6 @@ class DelugeFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a reauthorization flow request.""" return await self.async_step_user() - async def async_step_import(self, config: dict[str, Any]) -> FlowResult: - """Import a config entry from configuration.yaml.""" - if CONF_MONITORED_VARIABLES in config: - config.pop(CONF_MONITORED_VARIABLES) - config[CONF_WEB_PORT] = DEFAULT_WEB_PORT - - for entry in self._async_current_entries(): - if entry.data[CONF_HOST] == config[CONF_HOST]: - _LOGGER.warning( - "Deluge yaml config has been imported. Please remove it" - ) - return self.async_abort(reason="already_configured") - return await self.async_step_user(config) - async def validate_input(self, user_input: dict[str, Any]) -> str | None: """Handle common flow input validation.""" host = user_input[CONF_HOST] diff --git a/homeassistant/components/deluge/sensor.py b/homeassistant/components/deluge/sensor.py index 99e63e6ef17..bad535def96 100644 --- a/homeassistant/components/deluge/sensor.py +++ b/homeassistant/components/deluge/sensor.py @@ -1,33 +1,19 @@ """Support for monitoring the Deluge BitTorrent client API.""" from __future__ import annotations -import voluptuous as vol - from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, SensorEntity, SensorEntityDescription, SensorStateClass, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_MONITORED_VARIABLES, - CONF_NAME, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, - DATA_RATE_KILOBYTES_PER_SECOND, - STATE_IDLE, - Platform, -) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import DATA_RATE_KILOBYTES_PER_SECOND, STATE_IDLE, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType +from homeassistant.helpers.typing import StateType from . import DelugeEntity -from .const import DEFAULT_NAME, DEFAULT_RPC_PORT, DOMAIN +from .const import DOMAIN from .coordinator import DelugeDataUpdateCoordinator SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( @@ -49,36 +35,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( ), ) -SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES] - -# Deprecated in Home Assistant 2022.3 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_RPC_PORT): cv.port, - vol.Required(CONF_USERNAME): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_MONITORED_VARIABLES, default=[]): vol.All( - cv.ensure_list, [vol.In(SENSOR_KEYS)] - ), - } -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: entity_platform.AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Deluge sensor component.""" - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=config - ) - ) - async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/deluge/switch.py b/homeassistant/components/deluge/switch.py index d438d236e5c..cc11fcaf86c 100644 --- a/homeassistant/components/deluge/switch.py +++ b/homeassistant/components/deluge/switch.py @@ -3,52 +3,16 @@ from __future__ import annotations from typing import Any -import voluptuous as vol - -from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_NAME, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, - Platform, -) +from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import DelugeEntity -from .const import DEFAULT_RPC_PORT, DOMAIN +from .const import DOMAIN from .coordinator import DelugeDataUpdateCoordinator -# Deprecated in Home Assistant 2022.3 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_RPC_PORT): cv.port, - vol.Required(CONF_USERNAME): cv.string, - vol.Optional(CONF_NAME, default="Deluge Switch"): cv.string, - } -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: entity_platform.AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Deluge sensor component.""" - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=config - ) - ) - async def async_setup_entry( hass: HomeAssistant, diff --git a/tests/components/deluge/__init__.py b/tests/components/deluge/__init__.py index 47339f8dfd5..4efbe04cf52 100644 --- a/tests/components/deluge/__init__.py +++ b/tests/components/deluge/__init__.py @@ -5,14 +5,7 @@ from homeassistant.components.deluge.const import ( DEFAULT_RPC_PORT, DEFAULT_WEB_PORT, ) -from homeassistant.const import ( - CONF_HOST, - CONF_MONITORED_VARIABLES, - CONF_NAME, - CONF_PASSWORD, - CONF_PORT, - CONF_USERNAME, -) +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME CONF_DATA = { CONF_HOST: "1.2.3.4", @@ -21,12 +14,3 @@ CONF_DATA = { CONF_PORT: DEFAULT_RPC_PORT, CONF_WEB_PORT: DEFAULT_WEB_PORT, } - -IMPORT_DATA = { - CONF_HOST: "1.2.3.4", - CONF_NAME: "Deluge Torrent", - CONF_MONITORED_VARIABLES: ["current_status", "download_speed", "upload_speed"], - CONF_USERNAME: "user", - CONF_PASSWORD: "password", - CONF_PORT: DEFAULT_RPC_PORT, -} diff --git a/tests/components/deluge/test_config_flow.py b/tests/components/deluge/test_config_flow.py index 56dbac55674..b56c717e635 100644 --- a/tests/components/deluge/test_config_flow.py +++ b/tests/components/deluge/test_config_flow.py @@ -4,7 +4,7 @@ from unittest.mock import patch import pytest from homeassistant.components.deluge.const import DEFAULT_NAME, DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_REAUTH, SOURCE_USER +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import CONF_SOURCE from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import ( @@ -13,7 +13,7 @@ from homeassistant.data_entry_flow import ( RESULT_TYPE_FORM, ) -from . import CONF_DATA, IMPORT_DATA +from . import CONF_DATA from tests.common import MockConfigEntry @@ -102,33 +102,6 @@ async def test_flow_user_unknown_error(hass: HomeAssistant, unknown_error): assert result["errors"] == {"base": "unknown"} -async def test_flow_import(hass: HomeAssistant, api): - """Test import step.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA - ) - - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "Deluge Torrent" - assert result["data"] == CONF_DATA - - -async def test_flow_import_already_configured(hass: HomeAssistant, api): - """Test import step already configured.""" - entry = MockConfigEntry( - domain=DOMAIN, - data=CONF_DATA, - ) - - entry.add_to_hass(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=IMPORT_DATA - ) - - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "already_configured" - - async def test_flow_reauth(hass: HomeAssistant, api): """Test reauth step.""" entry = MockConfigEntry( From 524920dd2e69de4030b93a3c90c88a8f0310f56f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 13:49:35 +0200 Subject: [PATCH 302/930] Add 'toggle' device action to fans (#71570) --- homeassistant/components/fan/device_action.py | 50 +++---------------- tests/components/fan/test_device_action.py | 25 +++++++++- 2 files changed, 29 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/fan/device_action.py b/homeassistant/components/fan/device_action.py index 0482c31b929..fd38c69c3da 100644 --- a/homeassistant/components/fan/device_action.py +++ b/homeassistant/components/fan/device_action.py @@ -3,64 +3,26 @@ from __future__ import annotations import voluptuous as vol -from homeassistant.const import ( - ATTR_ENTITY_ID, - CONF_DEVICE_ID, - CONF_DOMAIN, - CONF_ENTITY_ID, - CONF_TYPE, - SERVICE_TURN_OFF, - SERVICE_TURN_ON, -) +from homeassistant.components.device_automation import toggle_entity +from homeassistant.const import CONF_DOMAIN from homeassistant.core import Context, HomeAssistant -from homeassistant.helpers import entity_registry -import homeassistant.helpers.config_validation as cv from . import DOMAIN -ACTION_TYPES = {"turn_on", "turn_off"} - -ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( - { - vol.Required(CONF_TYPE): vol.In(ACTION_TYPES), - vol.Required(CONF_ENTITY_ID): cv.entity_domain(DOMAIN), - } -) +ACTION_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN}) async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Fan devices.""" - registry = await entity_registry.async_get_registry(hass) - actions = [] - - # Get all the integrations entities for this device - for entry in entity_registry.async_entries_for_device(registry, device_id): - if entry.domain != DOMAIN: - continue - - base_action = { - CONF_DEVICE_ID: device_id, - CONF_DOMAIN: DOMAIN, - CONF_ENTITY_ID: entry.entity_id, - } - actions += [{**base_action, CONF_TYPE: action} for action in ACTION_TYPES] - - return actions + return await toggle_entity.async_get_actions(hass, device_id, DOMAIN) async def async_call_action_from_config( hass: HomeAssistant, config: dict, variables: dict, context: Context | None ) -> None: """Execute a device action.""" - service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} - - if config[CONF_TYPE] == "turn_on": - service = SERVICE_TURN_ON - elif config[CONF_TYPE] == "turn_off": - service = SERVICE_TURN_OFF - - await hass.services.async_call( - DOMAIN, service, service_data, blocking=True, context=context + await toggle_entity.async_call_action_from_config( + hass, config, variables, context, DOMAIN ) diff --git a/tests/components/fan/test_device_action.py b/tests/components/fan/test_device_action.py index ee300aad39f..8800e61675e 100644 --- a/tests/components/fan/test_device_action.py +++ b/tests/components/fan/test_device_action.py @@ -50,7 +50,7 @@ async def test_get_actions(hass, device_reg, entity_reg): "entity_id": f"{DOMAIN}.test_5678", "metadata": {"secondary": False}, } - for action in ["turn_on", "turn_off"] + for action in ["turn_on", "turn_off", "toggle"] ] actions = await async_get_device_automations( hass, DeviceAutomationType.ACTION, device_entry.id @@ -98,7 +98,7 @@ async def test_get_actions_hidden_auxiliary( "entity_id": f"{DOMAIN}.test_5678", "metadata": {"secondary": True}, } - for action in ["turn_on", "turn_off"] + for action in ["turn_on", "turn_off", "toggle"] ] actions = await async_get_device_automations( hass, DeviceAutomationType.ACTION, device_entry.id @@ -137,19 +137,40 @@ async def test_action(hass): "type": "turn_on", }, }, + { + "trigger": { + "platform": "event", + "event_type": "test_event_toggle", + }, + "action": { + "domain": DOMAIN, + "device_id": "abcdefgh", + "entity_id": "fan.entity", + "type": "toggle", + }, + }, ] }, ) turn_off_calls = async_mock_service(hass, "fan", "turn_off") turn_on_calls = async_mock_service(hass, "fan", "turn_on") + toggle_calls = async_mock_service(hass, "fan", "toggle") hass.bus.async_fire("test_event_turn_off") await hass.async_block_till_done() assert len(turn_off_calls) == 1 assert len(turn_on_calls) == 0 + assert len(toggle_calls) == 0 hass.bus.async_fire("test_event_turn_on") await hass.async_block_till_done() assert len(turn_off_calls) == 1 assert len(turn_on_calls) == 1 + assert len(toggle_calls) == 0 + + hass.bus.async_fire("test_event_toggle") + await hass.async_block_till_done() + assert len(turn_off_calls) == 1 + assert len(turn_on_calls) == 1 + assert len(toggle_calls) == 1 From d284e579bb95955c031db2c41d08d6a36b792c0d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 13:57:32 +0200 Subject: [PATCH 303/930] Improve Google Cast detection of HLS playlists (#71564) --- homeassistant/components/cast/helpers.py | 17 ++++++++++++++--- tests/components/cast/test_helpers.py | 21 ++++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/cast/helpers.py b/homeassistant/components/cast/helpers.py index dd98a2bc051..dfeb9fce25b 100644 --- a/homeassistant/components/cast/helpers.py +++ b/homeassistant/components/cast/helpers.py @@ -237,12 +237,14 @@ def _is_url(url): return all([result.scheme, result.netloc]) -async def _fetch_playlist(hass, url): +async def _fetch_playlist(hass, url, supported_content_types): """Fetch a playlist from the given url.""" try: session = aiohttp_client.async_get_clientsession(hass, verify_ssl=False) async with session.get(url, timeout=5) as resp: charset = resp.charset or "utf-8" + if resp.content_type in supported_content_types: + raise PlaylistSupported try: playlist_data = (await resp.content.read(64 * 1024)).decode(charset) except ValueError as err: @@ -260,7 +262,16 @@ async def parse_m3u(hass, url): Based on https://github.com/dvndrsn/M3uParser/blob/master/m3uparser.py """ - m3u_data = await _fetch_playlist(hass, url) + # From Mozilla gecko source: https://github.com/mozilla/gecko-dev/blob/c4c1adbae87bf2d128c39832d72498550ee1b4b8/dom/media/DecoderTraits.cpp#L47-L52 + hls_content_types = ( + # https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10 + "application/vnd.apple.mpegurl", + # Some sites serve these as the informal HLS m3u type. + "application/x-mpegurl", + "audio/mpegurl", + "audio/x-mpegurl", + ) + m3u_data = await _fetch_playlist(hass, url, hls_content_types) m3u_lines = m3u_data.splitlines() playlist = [] @@ -301,7 +312,7 @@ async def parse_pls(hass, url): Based on https://github.com/mariob/plsparser/blob/master/src/plsparser.py """ - pls_data = await _fetch_playlist(hass, url) + pls_data = await _fetch_playlist(hass, url, ()) pls_parser = configparser.ConfigParser() try: diff --git a/tests/components/cast/test_helpers.py b/tests/components/cast/test_helpers.py index 0d7a3b1ff14..d729d36a225 100644 --- a/tests/components/cast/test_helpers.py +++ b/tests/components/cast/test_helpers.py @@ -14,10 +14,25 @@ from homeassistant.components.cast.helpers import ( from tests.common import load_fixture -async def test_hls_playlist_supported(hass, aioclient_mock): +@pytest.mark.parametrize( + "url,fixture,content_type", + ( + ( + "http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_low/ak/bbc_radio_fourfm.m3u8", + "bbc_radio_fourfm.m3u8", + None, + ), + ( + "https://rthkaudio2-lh.akamaihd.net/i/radio2_1@355865/master.m3u8", + "rthkaudio2.m3u8", + "application/vnd.apple.mpegurl", + ), + ), +) +async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, content_type): """Test playlist parsing of HLS playlist.""" - url = "http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_low/ak/bbc_radio_fourfm.m3u8" - aioclient_mock.get(url, text=load_fixture("bbc_radio_fourfm.m3u8", "cast")) + headers = {"content-type": content_type} + aioclient_mock.get(url, text=load_fixture(fixture, "cast"), headers=headers) with pytest.raises(PlaylistSupported): await parse_playlist(hass, url) From 0842c291090a4d2ed6414cf4075107816396b866 Mon Sep 17 00:00:00 2001 From: Evan Bruhn Date: Mon, 9 May 2022 21:58:26 +1000 Subject: [PATCH 304/930] Bump logi_circle to 0.2.3 (#71578) --- homeassistant/components/logi_circle/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/logi_circle/manifest.json b/homeassistant/components/logi_circle/manifest.json index 94c040f3b75..2d8495df576 100644 --- a/homeassistant/components/logi_circle/manifest.json +++ b/homeassistant/components/logi_circle/manifest.json @@ -3,7 +3,7 @@ "name": "Logi Circle", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/logi_circle", - "requirements": ["logi_circle==0.2.2"], + "requirements": ["logi_circle==0.2.3"], "dependencies": ["ffmpeg", "http"], "codeowners": ["@evanjd"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 644b5c2b74b..c6d6b10ad64 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -957,7 +957,7 @@ lmnotify==0.0.4 locationsharinglib==4.1.5 # homeassistant.components.logi_circle -logi_circle==0.2.2 +logi_circle==0.2.3 # homeassistant.components.london_underground london-tube-status==0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7859ff3e1e..ecd8fc1f457 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -655,7 +655,7 @@ librouteros==3.2.0 libsoundtouch==0.8 # homeassistant.components.logi_circle -logi_circle==0.2.2 +logi_circle==0.2.3 # homeassistant.components.recorder lru-dict==1.1.7 From ddd22398f2c49d106930a84ec7e3c90b7838502f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 13:59:13 +0200 Subject: [PATCH 305/930] Bump pychromecast to 12.1.2 (#71567) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index cee46913937..10edc81e0fc 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==12.1.1"], + "requirements": ["pychromecast==12.1.2"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index c6d6b10ad64..fc76ea2dc35 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1399,7 +1399,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==12.1.1 +pychromecast==12.1.2 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ecd8fc1f457..cf3e5a1217b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -938,7 +938,7 @@ pybotvac==0.0.23 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==12.1.1 +pychromecast==12.1.2 # homeassistant.components.climacell pyclimacell==0.18.2 From 141688e210fd91910e055a27dee4f8e204c60d50 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 9 May 2022 14:17:48 +0200 Subject: [PATCH 306/930] Cleanup ServiceInfo compatibility (#60540) Co-authored-by: epenet Co-authored-by: J. Nick Koston --- homeassistant/components/dhcp/__init__.py | 33 ---- homeassistant/components/mqtt/__init__.py | 15 -- homeassistant/components/ssdp/__init__.py | 58 ------- homeassistant/components/usb/__init__.py | 17 +- homeassistant/components/zeroconf/__init__.py | 31 ---- tests/components/dhcp/test_init.py | 25 --- tests/components/mqtt/test_init.py | 20 --- tests/components/ssdp/test_init.py | 162 ------------------ tests/components/usb/test_init.py | 21 --- tests/components/zeroconf/test_init.py | 31 ---- 10 files changed, 1 insertion(+), 412 deletions(-) diff --git a/homeassistant/components/dhcp/__init__.py b/homeassistant/components/dhcp/__init__.py index 1756f620f46..8581a5f2241 100644 --- a/homeassistant/components/dhcp/__init__.py +++ b/homeassistant/components/dhcp/__init__.py @@ -52,14 +52,11 @@ from homeassistant.helpers.event import ( async_track_state_added_domain, async_track_time_interval, ) -from homeassistant.helpers.frame import report from homeassistant.helpers.typing import ConfigType from homeassistant.loader import DHCPMatcher, async_get_dhcp from homeassistant.util.async_ import run_callback_threadsafe from homeassistant.util.network import is_invalid, is_link_local, is_loopback -from .const import DOMAIN - if TYPE_CHECKING: from scapy.packet import Packet from scapy.sendrecv import AsyncSniffer @@ -86,36 +83,6 @@ class DhcpServiceInfo(BaseServiceInfo): hostname: str macaddress: str - def __getitem__(self, name: str) -> Any: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info['{name}'] instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - return getattr(self, name) - - def get(self, name: str, default: Any = None) -> Any: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - if hasattr(self, name): - return getattr(self, name) - return default - async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the dhcp component.""" diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 7c16da7f2aa..162e344f852 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -54,7 +54,6 @@ from homeassistant.helpers import config_validation as cv, event, template from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from homeassistant.helpers.entity import Entity -from homeassistant.helpers.frame import report from homeassistant.helpers.typing import ConfigType, TemplateVarsType from homeassistant.loader import bind_hass from homeassistant.util import dt as dt_util @@ -419,20 +418,6 @@ class MqttServiceInfo(BaseServiceInfo): subscribed_topic: str timestamp: dt.datetime - def __getitem__(self, name: str) -> Any: - """ - Allow property access by name for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info['{name}'] instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - return getattr(self, name) - def publish( hass: HomeAssistant, diff --git a/homeassistant/components/ssdp/__init__.py b/homeassistant/components/ssdp/__init__.py index 8e8663a9734..e854472f21f 100644 --- a/homeassistant/components/ssdp/__init__.py +++ b/homeassistant/components/ssdp/__init__.py @@ -25,7 +25,6 @@ from homeassistant.data_entry_flow import BaseServiceInfo from homeassistant.helpers import discovery_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.frame import report from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_get_ssdp, bind_hass @@ -111,63 +110,6 @@ class SsdpServiceInfo( ): """Prepared info from ssdp/upnp entries.""" - def __getitem__(self, name: str) -> Any: - """ - Allow property access by name for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info['{name}'] instead of discovery_info.{name}, " - f"discovery_info.upnp['{name}'] " - f"or discovery_info.ssdp_headers['{name}']; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - # Use a property if it is available, fallback to upnp data - if hasattr(self, name): - return getattr(self, name) - if name in self.ssdp_headers and name not in self.upnp: - return self.ssdp_headers.get(name) - return self.upnp[name] - - def get(self, name: str, default: Any = None) -> Any: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info.get('{name}') instead of discovery_info.{name}, " - f"discovery_info.upnp.get('{name}') " - f"or discovery_info.ssdp_headers.get('{name}'); " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - if hasattr(self, name): - return getattr(self, name) - return self.upnp.get(name, self.ssdp_headers.get(name, default)) - - def __contains__(self, name: str) -> bool: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info.__contains__('{name}') " - f"instead of discovery_info.upnp.__contains__('{name}') " - f"or discovery_info.ssdp_headers.__contains__('{name}'); " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - if hasattr(self, name): - return getattr(self, name) is not None - return name in self.upnp or name in self.ssdp_headers - SsdpChange = Enum("SsdpChange", "ALIVE BYEBYE UPDATE") SsdpCallback = Callable[[SsdpServiceInfo, SsdpChange], Awaitable] diff --git a/homeassistant/components/usb/__init__.py b/homeassistant/components/usb/__init__.py index 5ac390ad168..7a56a659d07 100644 --- a/homeassistant/components/usb/__init__.py +++ b/homeassistant/components/usb/__init__.py @@ -6,7 +6,7 @@ import fnmatch import logging import os import sys -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from serial.tools.list_ports import comports from serial.tools.list_ports_common import ListPortInfo @@ -20,7 +20,6 @@ from homeassistant.core import Event, HomeAssistant, callback from homeassistant.data_entry_flow import BaseServiceInfo from homeassistant.helpers import discovery_flow, system_info from homeassistant.helpers.debounce import Debouncer -from homeassistant.helpers.frame import report from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_get_usb @@ -47,20 +46,6 @@ class UsbServiceInfo(BaseServiceInfo): manufacturer: str | None description: str | None - def __getitem__(self, name: str) -> Any: - """ - Allow property access by name for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info['{name}'] instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - return getattr(self, name) - def human_readable_device_name( device: str, diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index ffe4140a434..2c16868f9de 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -30,7 +30,6 @@ from homeassistant.core import Event, HomeAssistant, callback from homeassistant.data_entry_flow import BaseServiceInfo from homeassistant.helpers import discovery_flow import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.frame import report from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.typing import ConfigType from homeassistant.loader import ( @@ -108,36 +107,6 @@ class ZeroconfServiceInfo(BaseServiceInfo): name: str properties: dict[str, Any] - def __getitem__(self, name: str) -> Any: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info['{name}'] instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - return getattr(self, name) - - def get(self, name: str, default: Any = None) -> Any: - """ - Enable method for compatibility reason. - - Deprecated, and will be removed in version 2022.6. - """ - report( - f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; " - "this will fail in version 2022.6", - exclude_integrations={DOMAIN}, - error_if_core=False, - ) - if hasattr(self, name): - return getattr(self, name) - return default - @bind_hass async def async_get_instance(hass: HomeAssistant) -> HaZeroconf: diff --git a/tests/components/dhcp/test_init.py b/tests/components/dhcp/test_init.py index a809d6eb5ab..c6d889eab72 100644 --- a/tests/components/dhcp/test_init.py +++ b/tests/components/dhcp/test_init.py @@ -3,7 +3,6 @@ import datetime import threading from unittest.mock import MagicMock, patch -import pytest from scapy import arch # pylint: disable=unused-import # noqa: F401 from scapy.error import Scapy_Exception from scapy.layers.dhcp import DHCP @@ -973,27 +972,3 @@ async def test_aiodiscover_finds_new_hosts_after_interval(hass): hostname="connect", macaddress="b8b7f16db533", ) - - -@pytest.mark.usefixtures("mock_integration_frame") -async def test_service_info_compatibility(hass, caplog): - """Test compatibility with old-style dict. - - To be removed in 2022.6 - """ - discovery_info = dhcp.DhcpServiceInfo( - ip="192.168.210.56", - hostname="connect", - macaddress="b8b7f16db533", - ) - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info["ip"] == "192.168.210.56" - assert "Detected integration that accessed discovery_info['ip']" in caplog.text - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info.get("ip") == "192.168.210.56" - assert "Detected integration that accessed discovery_info.get('ip')" in caplog.text - - assert discovery_info.get("ip", "fallback_host") == "192.168.210.56" - assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host" diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 763cbfc1b71..543174653ec 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -2380,26 +2380,6 @@ async def test_publish_json_from_template(hass, mqtt_mock): assert mqtt_mock.async_publish.call_args[0][1] == test_str -@pytest.mark.usefixtures("mock_integration_frame") -async def test_service_info_compatibility(hass, caplog): - """Test compatibility with old-style dict. - - To be removed in 2022.6 - """ - discovery_info = mqtt.MqttServiceInfo( - topic="tasmota/discovery/DC4F220848A2/config", - payload="", - qos=0, - retain=False, - subscribed_topic="tasmota/discovery/#", - timestamp=None, - ) - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info["topic"] == "tasmota/discovery/DC4F220848A2/config" - assert "Detected integration that accessed discovery_info['topic']" in caplog.text - - async def test_subscribe_connection_status(hass, mqtt_mock, mqtt_client_mock): """Test connextion status subscription.""" mqtt_connected_calls = [] diff --git a/tests/components/ssdp/test_init.py b/tests/components/ssdp/test_init.py index 5bbc90307b5..bf88f45acf9 100644 --- a/tests/components/ssdp/test_init.py +++ b/tests/components/ssdp/test_init.py @@ -75,17 +75,6 @@ async def test_ssdp_flow_dispatched_on_st(mock_get_ssdp, hass, caplog, mock_flow assert mock_call_data.x_homeassistant_matching_domains == {"mock-domain"} assert mock_call_data.upnp == {ssdp.ATTR_UPNP_UDN: "uuid:mock-udn"} assert "Failed to fetch ssdp data" not in caplog.text - # Compatibility with old dict access (to be removed after 2022.6) - assert mock_call_data[ssdp.ATTR_SSDP_ST] == "mock-st" - assert mock_call_data[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert mock_call_data[ssdp.ATTR_SSDP_USN] == "uuid:mock-udn::mock-st" - assert mock_call_data[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert mock_call_data[ssdp.ATTR_SSDP_EXT] == "" - assert mock_call_data[ssdp.ATTR_UPNP_UDN] == "uuid:mock-udn" - assert mock_call_data[ssdp.ATTR_SSDP_UDN] == ANY - assert mock_call_data["_timestamp"] == ANY - assert mock_call_data[ssdp.ATTR_HA_MATCHING_DOMAINS] == {"mock-domain"} - # End compatibility checks @patch( @@ -129,17 +118,6 @@ async def test_ssdp_flow_dispatched_on_manufacturer_url( assert mock_call_data.x_homeassistant_matching_domains == {"mock-domain"} assert mock_call_data.upnp == {ssdp.ATTR_UPNP_UDN: "uuid:mock-udn"} assert "Failed to fetch ssdp data" not in caplog.text - # Compatibility with old dict access (to be removed after 2022.6) - assert mock_call_data[ssdp.ATTR_SSDP_ST] == "mock-st" - assert mock_call_data[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert mock_call_data[ssdp.ATTR_SSDP_USN] == "uuid:mock-udn::mock-st" - assert mock_call_data[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert mock_call_data[ssdp.ATTR_SSDP_EXT] == "" - assert mock_call_data[ssdp.ATTR_UPNP_UDN] == "uuid:mock-udn" - assert mock_call_data[ssdp.ATTR_SSDP_UDN] == ANY - assert mock_call_data["_timestamp"] == ANY - assert mock_call_data[ssdp.ATTR_HA_MATCHING_DOMAINS] == {"mock-domain"} - # End compatibility checks @pytest.mark.usefixtures("mock_get_source_ip") @@ -426,18 +404,6 @@ async def test_discovery_from_advertisement_sets_ssdp_st( ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_UPNP_UDN: "uuid:mock-udn", } - # Compatibility with old dict access (to be removed after 2022.6) - assert discovery_info[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert discovery_info[ssdp.ATTR_SSDP_NT] == "mock-st" - # Set by ssdp component, not in original advertisement. - assert discovery_info[ssdp.ATTR_SSDP_ST] == "mock-st" - assert discovery_info[ssdp.ATTR_SSDP_USN] == "uuid:mock-udn::mock-st" - assert discovery_info[ssdp.ATTR_UPNP_UDN] == "uuid:mock-udn" - assert discovery_info[ssdp.ATTR_UPNP_DEVICE_TYPE] == "Paulus" - assert discovery_info[ssdp.ATTR_SSDP_UDN] == ANY - assert discovery_info["nts"] == "ssdp:alive" - assert discovery_info["_timestamp"] == ANY - # End compatibility checks @patch( @@ -547,25 +513,6 @@ async def test_scan_with_registered_callback( ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", } - # Compatibility with old dict access (to be removed after 2022.6) - assert mock_call_data[ssdp.ATTR_UPNP_DEVICE_TYPE] == "Paulus" - assert mock_call_data[ssdp.ATTR_SSDP_EXT] == "" - assert mock_call_data[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert mock_call_data[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert mock_call_data[ssdp.ATTR_SSDP_ST] == "mock-st" - assert ( - mock_call_data[ssdp.ATTR_SSDP_USN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::mock-st" - ) - assert ( - mock_call_data[ssdp.ATTR_UPNP_UDN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL" - ) - assert mock_call_data["x-rincon-bootseq"] == "55" - assert mock_call_data[ssdp.ATTR_SSDP_UDN] == ANY - assert mock_call_data["_timestamp"] == ANY - assert mock_call_data[ssdp.ATTR_HA_MATCHING_DOMAINS] == set() - # End of compatibility checks assert "Failed to callback info" in caplog.text async_integration_callback_from_cache = AsyncMock() @@ -622,23 +569,6 @@ async def test_getting_existing_headers( ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", } - # Compatibility with old dict access (to be removed after 2022.6) - assert discovery_info_by_st[ssdp.ATTR_SSDP_EXT] == "" - assert discovery_info_by_st[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert discovery_info_by_st[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert discovery_info_by_st[ssdp.ATTR_SSDP_ST] == "mock-st" - assert ( - discovery_info_by_st[ssdp.ATTR_SSDP_USN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3" - ) - assert ( - discovery_info_by_st[ssdp.ATTR_UPNP_UDN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL" - ) - assert discovery_info_by_st[ssdp.ATTR_UPNP_DEVICE_TYPE] == "Paulus" - assert discovery_info_by_st[ssdp.ATTR_SSDP_UDN] == ANY - assert discovery_info_by_st["_timestamp"] == ANY - # End of compatibility checks discovery_info_by_udn = await ssdp.async_get_discovery_info_by_udn( hass, "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL" @@ -658,23 +588,6 @@ async def test_getting_existing_headers( ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", } - # Compatibility with old dict access (to be removed after 2022.6) - assert discovery_info_by_udn[ssdp.ATTR_SSDP_EXT] == "" - assert discovery_info_by_udn[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert discovery_info_by_udn[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert discovery_info_by_udn[ssdp.ATTR_SSDP_ST] == "mock-st" - assert ( - discovery_info_by_udn[ssdp.ATTR_SSDP_USN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3" - ) - assert ( - discovery_info_by_udn[ssdp.ATTR_UPNP_UDN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL" - ) - assert discovery_info_by_udn[ssdp.ATTR_UPNP_DEVICE_TYPE] == "Paulus" - assert discovery_info_by_udn[ssdp.ATTR_SSDP_UDN] == ANY - assert discovery_info_by_udn["_timestamp"] == ANY - # End of compatibility checks discovery_info_by_udn_st = await ssdp.async_get_discovery_info_by_udn_st( hass, "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", "mock-st" @@ -693,23 +606,6 @@ async def test_getting_existing_headers( ssdp.ATTR_UPNP_DEVICE_TYPE: "Paulus", ssdp.ATTR_UPNP_UDN: "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL", } - # Compatibility with old dict access (to be removed after 2022.6) - assert discovery_info_by_udn_st[ssdp.ATTR_SSDP_EXT] == "" - assert discovery_info_by_udn_st[ssdp.ATTR_SSDP_LOCATION] == "http://1.1.1.1" - assert discovery_info_by_udn_st[ssdp.ATTR_SSDP_SERVER] == "mock-server" - assert discovery_info_by_udn_st[ssdp.ATTR_SSDP_ST] == "mock-st" - assert ( - discovery_info_by_udn_st[ssdp.ATTR_SSDP_USN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL::urn:mdx-netflix-com:service:target:3" - ) - assert ( - discovery_info_by_udn_st[ssdp.ATTR_UPNP_UDN] - == "uuid:TIVRTLSR7ANF-D6E-1557809135086-RETAIL" - ) - assert discovery_info_by_udn_st[ssdp.ATTR_UPNP_DEVICE_TYPE] == "Paulus" - assert discovery_info_by_udn_st[ssdp.ATTR_SSDP_UDN] == ANY - assert discovery_info_by_udn_st["_timestamp"] == ANY - # End of compatibility checks assert ( await ssdp.async_get_discovery_info_by_udn_st(hass, "wrong", "mock-st") is None @@ -845,61 +741,3 @@ async def test_ipv4_does_additional_search_for_sonos( ), ) assert ssdp_listener.async_search.call_args[1] == {} - - -@pytest.mark.usefixtures("mock_integration_frame") -async def test_service_info_compatibility(hass, caplog): - """Test compatibility with old-style dict. - - To be removed in 2022.6 - """ - discovery_info = ssdp.SsdpServiceInfo( - ssdp_st="mock-st", - ssdp_location="http://1.1.1.1", - ssdp_usn="uuid:mock-udn::mock-st", - ssdp_server="mock-server", - ssdp_ext="", - ssdp_headers=_ssdp_headers( - { - "st": "mock-st", - "location": "http://1.1.1.1", - "usn": "uuid:mock-udn::mock-st", - "server": "mock-server", - "ext": "", - } - ), - upnp={ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC"}, - ) - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info["ssdp_st"] == "mock-st" - assert "Detected integration that accessed discovery_info['ssdp_st']" in caplog.text - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info.get("ssdp_location") == "http://1.1.1.1" - assert ( - "Detected integration that accessed discovery_info.get('ssdp_location')" - in caplog.text - ) - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert "ssdp_usn" in discovery_info - assert ( - "Detected integration that accessed discovery_info.__contains__('ssdp_usn')" - in caplog.text - ) - - # Root item - assert discovery_info["ssdp_usn"] == "uuid:mock-udn::mock-st" - assert discovery_info.get("ssdp_usn") == "uuid:mock-udn::mock-st" - assert "ssdp_usn" in discovery_info - - # SSDP header - assert discovery_info["st"] == "mock-st" - assert discovery_info.get("st") == "mock-st" - assert "st" in discovery_info - - # UPnP item - assert discovery_info[ssdp.ATTR_UPNP_DEVICE_TYPE] == "ABC" - assert discovery_info.get(ssdp.ATTR_UPNP_DEVICE_TYPE) == "ABC" - assert ssdp.ATTR_UPNP_DEVICE_TYPE in discovery_info diff --git a/tests/components/usb/test_init.py b/tests/components/usb/test_init.py index f1cf67bfb78..f4245a0e0d6 100644 --- a/tests/components/usb/test_init.py +++ b/tests/components/usb/test_init.py @@ -833,24 +833,3 @@ def test_human_readable_device_name(): assert "Silicon Labs" in name assert "10C4" in name assert "8A2A" in name - - -@pytest.mark.usefixtures("mock_integration_frame") -async def test_service_info_compatibility(hass, caplog): - """Test compatibility with old-style dict. - - To be removed in 2022.6 - """ - discovery_info = usb.UsbServiceInfo( - device=slae_sh_device.device, - vid=12345, - pid=12345, - serial_number=slae_sh_device.serial_number, - manufacturer=slae_sh_device.manufacturer, - description=slae_sh_device.description, - ) - - # Ensure first call get logged - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info["vid"] == 12345 - assert "Detected integration that accessed discovery_info['vid']" in caplog.text diff --git a/tests/components/zeroconf/test_init.py b/tests/components/zeroconf/test_init.py index 900dbeb8431..88dceb9d464 100644 --- a/tests/components/zeroconf/test_init.py +++ b/tests/components/zeroconf/test_init.py @@ -3,7 +3,6 @@ from ipaddress import ip_address from typing import Any from unittest.mock import call, patch -import pytest from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange from zeroconf.asyncio import AsyncServiceInfo @@ -1116,33 +1115,3 @@ async def test_no_name(hass, mock_async_zeroconf): register_call = mock_async_zeroconf.async_register_service.mock_calls[-1] info = register_call.args[0] assert info.name == "Home._home-assistant._tcp.local." - - -@pytest.mark.usefixtures("mock_integration_frame") -async def test_service_info_compatibility(hass, caplog): - """Test compatibility with old-style dict. - - To be removed in 2022.6 - """ - discovery_info = zeroconf.ZeroconfServiceInfo( - host="mock_host", - addresses=["mock_host"], - port=None, - hostname="mock_hostname", - type="mock_type", - name="mock_name", - properties={}, - ) - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info["host"] == "mock_host" - assert "Detected integration that accessed discovery_info['host']" in caplog.text - - with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()): - assert discovery_info.get("host") == "mock_host" - assert ( - "Detected integration that accessed discovery_info.get('host')" in caplog.text - ) - - assert discovery_info.get("host", "fallback_host") == "mock_host" - assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host" From a599325c2dd3a812f4c344a311c951b538f15e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 9 May 2022 14:44:14 +0200 Subject: [PATCH 307/930] Add QNAP QSW Binary Sensor platform (#70868) --- homeassistant/components/qnap_qsw/__init__.py | 51 +--------- .../components/qnap_qsw/binary_sensor.py | 88 ++++++++++++++++++ homeassistant/components/qnap_qsw/const.py | 1 + homeassistant/components/qnap_qsw/entity.py | 93 +++++++++++++++++++ homeassistant/components/qnap_qsw/sensor.py | 26 +----- .../components/qnap_qsw/test_binary_sensor.py | 17 ++++ 6 files changed, 206 insertions(+), 70 deletions(-) create mode 100644 homeassistant/components/qnap_qsw/binary_sensor.py create mode 100644 homeassistant/components/qnap_qsw/entity.py create mode 100644 tests/components/qnap_qsw/test_binary_sensor.py diff --git a/homeassistant/components/qnap_qsw/__init__.py b/homeassistant/components/qnap_qsw/__init__.py index c1b96a3298e..838a567199d 100644 --- a/homeassistant/components/qnap_qsw/__init__.py +++ b/homeassistant/components/qnap_qsw/__init__.py @@ -1,64 +1,17 @@ """The QNAP QSW integration.""" from __future__ import annotations -from typing import Any - -from aioqsw.const import ( - QSD_FIRMWARE, - QSD_FIRMWARE_INFO, - QSD_MAC, - QSD_PRODUCT, - QSD_SYSTEM_BOARD, -) from aioqsw.localapi import ConnectionOptions, QnapQswApi from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import aiohttp_client -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC -from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, MANUFACTURER +from .const import DOMAIN from .coordinator import QswUpdateCoordinator -PLATFORMS: list[Platform] = [Platform.SENSOR] - - -class QswEntity(CoordinatorEntity[QswUpdateCoordinator]): - """Define an QNAP QSW entity.""" - - def __init__( - self, - coordinator: QswUpdateCoordinator, - entry: ConfigEntry, - ) -> None: - """Initialize.""" - super().__init__(coordinator) - - self._attr_device_info = DeviceInfo( - configuration_url=entry.data[CONF_URL], - connections={ - ( - CONNECTION_NETWORK_MAC, - self.get_device_value(QSD_SYSTEM_BOARD, QSD_MAC), - ) - }, - manufacturer=MANUFACTURER, - model=self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT), - name=self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT), - sw_version=self.get_device_value(QSD_FIRMWARE_INFO, QSD_FIRMWARE), - ) - - def get_device_value(self, key: str, subkey: str) -> Any: - """Return device value by key.""" - value = None - if key in self.coordinator.data: - data = self.coordinator.data[key] - if subkey in data: - value = data[subkey] - return value +PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/qnap_qsw/binary_sensor.py b/homeassistant/components/qnap_qsw/binary_sensor.py new file mode 100644 index 00000000000..467a3314070 --- /dev/null +++ b/homeassistant/components/qnap_qsw/binary_sensor.py @@ -0,0 +1,88 @@ +"""Support for the QNAP QSW binary sensors.""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Final + +from aioqsw.const import QSD_ANOMALY, QSD_FIRMWARE_CONDITION, QSD_MESSAGE + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_MESSAGE, DOMAIN +from .coordinator import QswUpdateCoordinator +from .entity import QswEntityDescription, QswSensorEntity + + +@dataclass +class QswBinarySensorEntityDescription( + BinarySensorEntityDescription, QswEntityDescription +): + """A class that describes QNAP QSW binary sensor entities.""" + + attributes: dict[str, list[str]] | None = None + + +BINARY_SENSOR_TYPES: Final[tuple[QswBinarySensorEntityDescription, ...]] = ( + QswBinarySensorEntityDescription( + attributes={ + ATTR_MESSAGE: [QSD_FIRMWARE_CONDITION, QSD_MESSAGE], + }, + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + key=QSD_FIRMWARE_CONDITION, + name="Anomaly", + subkey=QSD_ANOMALY, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Add QNAP QSW binary sensors from a config_entry.""" + coordinator: QswUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + QswBinarySensor(coordinator, description, entry) + for description in BINARY_SENSOR_TYPES + if ( + description.key in coordinator.data + and description.subkey in coordinator.data[description.key] + ) + ) + + +class QswBinarySensor(QswSensorEntity, BinarySensorEntity): + """Define a QNAP QSW binary sensor.""" + + entity_description: QswBinarySensorEntityDescription + + def __init__( + self, + coordinator: QswUpdateCoordinator, + description: QswBinarySensorEntityDescription, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry) + self._attr_name = f"{self.product} {description.name}" + self._attr_unique_id = ( + f"{entry.unique_id}_{description.key}_{description.subkey}" + ) + self.entity_description = description + self._async_update_attrs() + + @callback + def _async_update_attrs(self) -> None: + """Update binary sensor attributes.""" + self._attr_is_on = self.get_device_value( + self.entity_description.key, self.entity_description.subkey + ) + super()._async_update_attrs() diff --git a/homeassistant/components/qnap_qsw/const.py b/homeassistant/components/qnap_qsw/const.py index b55a817927f..a6cacfd1c40 100644 --- a/homeassistant/components/qnap_qsw/const.py +++ b/homeassistant/components/qnap_qsw/const.py @@ -3,6 +3,7 @@ from typing import Final ATTR_MAX: Final = "max" +ATTR_MESSAGE: Final = "message" DOMAIN: Final = "qnap_qsw" MANUFACTURER: Final = "QNAP" diff --git a/homeassistant/components/qnap_qsw/entity.py b/homeassistant/components/qnap_qsw/entity.py new file mode 100644 index 00000000000..c3550610d83 --- /dev/null +++ b/homeassistant/components/qnap_qsw/entity.py @@ -0,0 +1,93 @@ +"""Entity classes for the QNAP QSW integration.""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from aioqsw.const import ( + QSD_FIRMWARE, + QSD_FIRMWARE_INFO, + QSD_MAC, + QSD_PRODUCT, + QSD_SYSTEM_BOARD, +) + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_URL +from homeassistant.core import callback +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers.entity import DeviceInfo, EntityDescription +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import MANUFACTURER +from .coordinator import QswUpdateCoordinator + + +class QswEntity(CoordinatorEntity[QswUpdateCoordinator]): + """Define an QNAP QSW entity.""" + + def __init__( + self, + coordinator: QswUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + self.product = self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT) + self._attr_device_info = DeviceInfo( + configuration_url=entry.data[CONF_URL], + connections={ + ( + CONNECTION_NETWORK_MAC, + self.get_device_value(QSD_SYSTEM_BOARD, QSD_MAC), + ) + }, + manufacturer=MANUFACTURER, + model=self.product, + name=self.product, + sw_version=self.get_device_value(QSD_FIRMWARE_INFO, QSD_FIRMWARE), + ) + + def get_device_value(self, key: str, subkey: str) -> Any: + """Return device value by key.""" + value = None + if key in self.coordinator.data: + data = self.coordinator.data[key] + if subkey in data: + value = data[subkey] + return value + + +@dataclass +class QswEntityDescriptionMixin: + """Mixin to describe a QSW entity.""" + + subkey: str + + +class QswEntityDescription(EntityDescription, QswEntityDescriptionMixin): + """Class to describe a QSW entity.""" + + attributes: dict[str, list[str]] | None = None + + +class QswSensorEntity(QswEntity): + """Base class for QSW sensor entities.""" + + entity_description: QswEntityDescription + + @callback + def _handle_coordinator_update(self) -> None: + """Update attributes when the coordinator updates.""" + self._async_update_attrs() + super()._handle_coordinator_update() + + @callback + def _async_update_attrs(self) -> None: + """Update attributes.""" + if self.entity_description.attributes: + self._attr_extra_state_attributes = { + key: self.get_device_value(val[0], val[1]) + for key, val in self.entity_description.attributes.items() + } diff --git a/homeassistant/components/qnap_qsw/sensor.py b/homeassistant/components/qnap_qsw/sensor.py index 8453232ce6f..0de8ec4a39e 100644 --- a/homeassistant/components/qnap_qsw/sensor.py +++ b/homeassistant/components/qnap_qsw/sensor.py @@ -7,8 +7,6 @@ from typing import Final from aioqsw.const import ( QSD_FAN1_SPEED, QSD_FAN2_SPEED, - QSD_PRODUCT, - QSD_SYSTEM_BOARD, QSD_SYSTEM_SENSOR, QSD_SYSTEM_TIME, QSD_TEMP, @@ -28,17 +26,16 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import QswEntity from .const import ATTR_MAX, DOMAIN, RPM from .coordinator import QswUpdateCoordinator +from .entity import QswEntityDescription, QswSensorEntity @dataclass -class QswSensorEntityDescription(SensorEntityDescription): +class QswSensorEntityDescription(SensorEntityDescription, QswEntityDescription): """A class that describes QNAP QSW sensor entities.""" attributes: dict[str, list[str]] | None = None - subkey: str = "" SENSOR_TYPES: Final[tuple[QswSensorEntityDescription, ...]] = ( @@ -96,7 +93,7 @@ async def async_setup_entry( ) -class QswSensor(QswEntity, SensorEntity): +class QswSensor(QswSensorEntity, SensorEntity): """Define a QNAP QSW sensor.""" entity_description: QswSensorEntityDescription @@ -109,30 +106,17 @@ class QswSensor(QswEntity, SensorEntity): ) -> None: """Initialize.""" super().__init__(coordinator, entry) - self._attr_name = ( - f"{self.get_device_value(QSD_SYSTEM_BOARD, QSD_PRODUCT)} {description.name}" - ) + self._attr_name = f"{self.product} {description.name}" self._attr_unique_id = ( f"{entry.unique_id}_{description.key}_{description.subkey}" ) self.entity_description = description self._async_update_attrs() - @callback - def _handle_coordinator_update(self) -> None: - """Update attributes when the coordinator updates.""" - self._async_update_attrs() - super()._handle_coordinator_update() - @callback def _async_update_attrs(self) -> None: """Update sensor attributes.""" self._attr_native_value = self.get_device_value( self.entity_description.key, self.entity_description.subkey ) - - if self.entity_description.attributes: - self._attr_extra_state_attributes = { - key: self.get_device_value(val[0], val[1]) - for key, val in self.entity_description.attributes.items() - } + super()._async_update_attrs() diff --git a/tests/components/qnap_qsw/test_binary_sensor.py b/tests/components/qnap_qsw/test_binary_sensor.py new file mode 100644 index 00000000000..a36a34f02be --- /dev/null +++ b/tests/components/qnap_qsw/test_binary_sensor.py @@ -0,0 +1,17 @@ +"""The binary sensor tests for the QNAP QSW platform.""" + +from homeassistant.components.qnap_qsw.const import ATTR_MESSAGE +from homeassistant.const import STATE_OFF +from homeassistant.core import HomeAssistant + +from .util import async_init_integration + + +async def test_qnap_qsw_create_binary_sensors(hass: HomeAssistant) -> None: + """Test creation of binary sensors.""" + + await async_init_integration(hass) + + state = hass.states.get("binary_sensor.qsw_m408_4c_anomaly") + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_MESSAGE) is None From 539ce7ff0e9d9bc59cd8f028f245c09f802c89cb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 9 May 2022 06:05:31 -0700 Subject: [PATCH 308/930] Allow mobile app to disable entities by default (#71562) --- .../components/mobile_app/binary_sensor.py | 12 +----- homeassistant/components/mobile_app/const.py | 1 + homeassistant/components/mobile_app/entity.py | 43 ++++++------------- homeassistant/components/mobile_app/sensor.py | 17 +------- .../components/mobile_app/webhook.py | 27 ++++++++++-- tests/components/mobile_app/test_sensor.py | 33 ++++++++++++++ 6 files changed, 74 insertions(+), 59 deletions(-) diff --git a/homeassistant/components/mobile_app/binary_sensor.py b/homeassistant/components/mobile_app/binary_sensor.py index 4d40e42a47e..fd8545b1f98 100644 --- a/homeassistant/components/mobile_app/binary_sensor.py +++ b/homeassistant/components/mobile_app/binary_sensor.py @@ -3,14 +3,13 @@ from typing import Any from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_NAME, CONF_UNIQUE_ID, CONF_WEBHOOK_ID, STATE_ON +from homeassistant.const import CONF_WEBHOOK_ID, STATE_ON from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( - ATTR_DEVICE_NAME, ATTR_SENSOR_ATTRIBUTES, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, @@ -22,7 +21,7 @@ from .const import ( ATTR_SENSOR_UNIQUE_ID, DOMAIN, ) -from .entity import MobileAppEntity, unique_id +from .entity import MobileAppEntity async def async_setup_entry( @@ -59,13 +58,6 @@ async def async_setup_entry( if data[CONF_WEBHOOK_ID] != webhook_id: return - data[CONF_UNIQUE_ID] = unique_id( - data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID] - ) - data[ - CONF_NAME - ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" - async_add_entities([MobileAppBinarySensor(data, config_entry)]) async_dispatcher_connect( diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index ba81a0484cf..e6a26430f11 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,6 +67,7 @@ ERR_INVALID_FORMAT = "invalid_format" ATTR_SENSOR_ATTRIBUTES = "attributes" ATTR_SENSOR_DEVICE_CLASS = "device_class" +ATTR_SENSOR_DEFAULT_DISABLED = "default_disabled" ATTR_SENSOR_ENTITY_CATEGORY = "entity_category" ATTR_SENSOR_ICON = "icon" ATTR_SENSOR_NAME = "name" diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index 0cb6cfc6fcc..b38774d56d6 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -2,46 +2,35 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_ICON, - CONF_NAME, - CONF_UNIQUE_ID, - CONF_WEBHOOK_ID, - STATE_UNAVAILABLE, -) +from homeassistant.const import ATTR_ICON, CONF_NAME, CONF_UNIQUE_ID, STATE_UNAVAILABLE from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.restore_state import RestoreEntity from .const import ( ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, ATTR_SENSOR_STATE, - ATTR_SENSOR_TYPE, - ATTR_SENSOR_UNIQUE_ID, SIGNAL_SENSOR_UPDATE, ) from .helpers import device_info -def unique_id(webhook_id, sensor_unique_id): - """Return a unique sensor ID.""" - return f"{webhook_id}_{sensor_unique_id}" - - class MobileAppEntity(RestoreEntity): """Representation of an mobile app entity.""" + _attr_should_poll = False + def __init__(self, config: dict, entry: ConfigEntry) -> None: """Initialize the entity.""" self._config = config self._entry = entry self._registration = entry.data - self._unique_id = config[CONF_UNIQUE_ID] - self._entity_type = config[ATTR_SENSOR_TYPE] - self._name = config[CONF_NAME] + self._attr_unique_id = config[CONF_UNIQUE_ID] + self._name = self._config[CONF_NAME] async def async_added_to_hass(self): """Register callbacks.""" @@ -67,16 +56,16 @@ class MobileAppEntity(RestoreEntity): if ATTR_ICON in last_state.attributes: self._config[ATTR_SENSOR_ICON] = last_state.attributes[ATTR_ICON] - @property - def should_poll(self) -> bool: - """Declare that this entity pushes its state to HA.""" - return False - @property def name(self): """Return the name of the mobile app sensor.""" return self._name + @property + def entity_registry_enabled_default(self) -> bool: + """Return if entity should be enabled by default.""" + return not self._config.get(ATTR_SENSOR_DEFAULT_DISABLED) + @property def device_class(self): """Return the device class.""" @@ -97,11 +86,6 @@ class MobileAppEntity(RestoreEntity): """Return the entity category, if any.""" return self._config.get(ATTR_SENSOR_ENTITY_CATEGORY) - @property - def unique_id(self): - """Return the unique ID of this sensor.""" - return self._unique_id - @property def device_info(self): """Return device registry information for this entity.""" @@ -113,10 +97,9 @@ class MobileAppEntity(RestoreEntity): return self._config.get(ATTR_SENSOR_STATE) != STATE_UNAVAILABLE @callback - def _handle_update(self, data): + def _handle_update(self, incoming_id, data): """Handle async event updates.""" - incoming_id = unique_id(data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID]) - if incoming_id != self._unique_id: + if incoming_id != self._attr_unique_id: return self._config = {**self._config, **data} diff --git a/homeassistant/components/mobile_app/sensor.py b/homeassistant/components/mobile_app/sensor.py index 45bc4acd6a2..d7cfc9545f6 100644 --- a/homeassistant/components/mobile_app/sensor.py +++ b/homeassistant/components/mobile_app/sensor.py @@ -5,12 +5,7 @@ from typing import Any from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_NAME, - CONF_UNIQUE_ID, - CONF_WEBHOOK_ID, - STATE_UNKNOWN, -) +from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -18,7 +13,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import dt as dt_util from .const import ( - ATTR_DEVICE_NAME, ATTR_SENSOR_ATTRIBUTES, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, @@ -32,7 +26,7 @@ from .const import ( ATTR_SENSOR_UOM, DOMAIN, ) -from .entity import MobileAppEntity, unique_id +from .entity import MobileAppEntity async def async_setup_entry( @@ -70,13 +64,6 @@ async def async_setup_entry( if data[CONF_WEBHOOK_ID] != webhook_id: return - data[CONF_UNIQUE_ID] = unique_id( - data[CONF_WEBHOOK_ID], data[ATTR_SENSOR_UNIQUE_ID] - ) - data[ - CONF_NAME - ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" - async_add_entities([MobileAppSensor(data, config_entry)]) async_dispatcher_connect( diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index f0c7c628976..bc67076b005 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -34,6 +34,8 @@ from homeassistant.const import ( ATTR_SERVICE, ATTR_SERVICE_DATA, ATTR_SUPPORTED_FEATURES, + CONF_NAME, + CONF_UNIQUE_ID, CONF_WEBHOOK_ID, ) from homeassistant.core import EventOrigin, HomeAssistant @@ -62,6 +64,7 @@ from .const import ( ATTR_NO_LEGACY_ENCRYPTION, ATTR_OS_VERSION, ATTR_SENSOR_ATTRIBUTES, + ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, @@ -431,6 +434,11 @@ def _validate_state_class_sensor(value: dict): return value +def _gen_unique_id(webhook_id, sensor_unique_id): + """Return a unique sensor ID.""" + return f"{webhook_id}_{sensor_unique_id}" + + @WEBHOOK_COMMANDS.register("register_sensor") @validate_schema( vol.All( @@ -449,6 +457,7 @@ def _validate_state_class_sensor(value: dict): vol.Optional(ATTR_SENSOR_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon, vol.Optional(ATTR_SENSOR_STATE_CLASS): vol.In(SENSOSR_STATE_CLASSES), + vol.Optional(ATTR_SENSOR_DEFAULT_DISABLED): bool, }, _validate_state_class_sensor, ) @@ -459,7 +468,7 @@ async def webhook_register_sensor(hass, config_entry, data): unique_id = data[ATTR_SENSOR_UNIQUE_ID] device_name = config_entry.data[ATTR_DEVICE_NAME] - unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}" + unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) entity_registry = er.async_get(hass) existing_sensor = entity_registry.async_get_entity_id( entity_type, DOMAIN, unique_store_key @@ -493,8 +502,13 @@ async def webhook_register_sensor(hass, config_entry, data): if changes: entity_registry.async_update_entity(existing_sensor, **changes) - async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, data) + async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, unique_store_key, data) else: + data[CONF_UNIQUE_ID] = unique_store_key + data[ + CONF_NAME + ] = f"{config_entry.data[ATTR_DEVICE_NAME]} {data[ATTR_SENSOR_NAME]}" + register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register" async_dispatcher_send(hass, register_signal, data) @@ -543,7 +557,7 @@ async def webhook_update_sensor_states(hass, config_entry, data): unique_id = sensor[ATTR_SENSOR_UNIQUE_ID] - unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}" + unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) entity_registry = er.async_get(hass) if not entity_registry.async_get_entity_id( @@ -578,7 +592,12 @@ async def webhook_update_sensor_states(hass, config_entry, data): continue sensor[CONF_WEBHOOK_ID] = config_entry.data[CONF_WEBHOOK_ID] - async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, sensor) + async_dispatcher_send( + hass, + SIGNAL_SENSOR_UPDATE, + unique_store_key, + sensor, + ) resp[unique_id] = {"success": True} diff --git a/tests/components/mobile_app/test_sensor.py b/tests/components/mobile_app/test_sensor.py index 7eb99df8d8f..301c49381f7 100644 --- a/tests/components/mobile_app/test_sensor.py +++ b/tests/components/mobile_app/test_sensor.py @@ -340,3 +340,36 @@ async def test_sensor_datetime( assert entity.attributes["device_class"] == device_class assert entity.domain == "sensor" assert entity.state == state_value + + +async def test_default_disabling_entity(hass, create_registrations, webhook_client): + """Test that sensors can be disabled by default upon registration.""" + webhook_id = create_registrations[1]["webhook_id"] + webhook_url = f"/api/webhook/{webhook_id}" + + reg_resp = await webhook_client.post( + webhook_url, + json={ + "type": "register_sensor", + "data": { + "name": "Battery State", + "type": "sensor", + "unique_id": "battery_state", + "default_disabled": True, + }, + }, + ) + + assert reg_resp.status == HTTPStatus.CREATED + + json = await reg_resp.json() + assert json == {"success": True} + await hass.async_block_till_done() + + entity = hass.states.get("sensor.test_1_battery_state") + assert entity is None + + assert ( + er.async_get(hass).async_get("sensor.test_1_battery_state").disabled_by + == er.RegistryEntryDisabler.INTEGRATION + ) From 88c2c5c36cfe1ac89a5b26836d0bdd34a0d72021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 9 May 2022 16:07:11 +0200 Subject: [PATCH 309/930] Add Binary Sensors for Airzone Systems (#69736) --- homeassistant/components/airzone/__init__.py | 62 +---------- .../components/airzone/binary_sensor.py | 47 +++++++- homeassistant/components/airzone/climate.py | 2 +- homeassistant/components/airzone/entity.py | 105 ++++++++++++++++++ homeassistant/components/airzone/sensor.py | 2 +- .../components/airzone/test_binary_sensor.py | 5 + tests/components/airzone/util.py | 18 ++- 7 files changed, 176 insertions(+), 65 deletions(-) create mode 100644 homeassistant/components/airzone/entity.py diff --git a/homeassistant/components/airzone/__init__.py b/homeassistant/components/airzone/__init__.py index 2882ea18143..cf57a28a5e1 100644 --- a/homeassistant/components/airzone/__init__.py +++ b/homeassistant/components/airzone/__init__.py @@ -4,17 +4,7 @@ from __future__ import annotations import logging from typing import Any -from aioairzone.const import ( - AZD_ID, - AZD_MAC, - AZD_NAME, - AZD_SYSTEM, - AZD_THERMOSTAT_FW, - AZD_THERMOSTAT_MODEL, - AZD_WEBSERVER, - AZD_ZONES, - DEFAULT_SYSTEM_ID, -) +from aioairzone.const import AZD_MAC, AZD_WEBSERVER, DEFAULT_SYSTEM_ID from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions from homeassistant.config_entries import ConfigEntry @@ -25,10 +15,8 @@ from homeassistant.helpers import ( device_registry as dr, entity_registry as er, ) -from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, MANUFACTURER +from .const import DOMAIN from .coordinator import AirzoneUpdateCoordinator PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.SENSOR] @@ -36,52 +24,6 @@ PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform. _LOGGER = logging.getLogger(__name__) -class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator]): - """Define an Airzone entity.""" - - def get_airzone_value(self, key) -> Any: - """Return Airzone entity value by key.""" - raise NotImplementedError() - - -class AirzoneZoneEntity(AirzoneEntity): - """Define an Airzone Zone entity.""" - - def __init__( - self, - coordinator: AirzoneUpdateCoordinator, - entry: ConfigEntry, - system_zone_id: str, - zone_data: dict[str, Any], - ) -> None: - """Initialize.""" - super().__init__(coordinator) - - self.system_id = zone_data[AZD_SYSTEM] - self.system_zone_id = system_zone_id - self.zone_id = zone_data[AZD_ID] - - self._attr_device_info: DeviceInfo = { - "identifiers": {(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, - "manufacturer": MANUFACTURER, - "model": self.get_airzone_value(AZD_THERMOSTAT_MODEL), - "name": f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", - "sw_version": self.get_airzone_value(AZD_THERMOSTAT_FW), - } - self._attr_unique_id = ( - entry.entry_id if entry.unique_id is None else entry.unique_id - ) - - def get_airzone_value(self, key) -> Any: - """Return zone value by key.""" - value = None - if self.system_zone_id in self.coordinator.data[AZD_ZONES]: - zone = self.coordinator.data[AZD_ZONES][self.system_zone_id] - if key in zone: - value = zone[key] - return value - - async def _async_migrate_unique_ids( hass: HomeAssistant, entry: ConfigEntry, diff --git a/homeassistant/components/airzone/binary_sensor.py b/homeassistant/components/airzone/binary_sensor.py index dd0e4a5b768..4dbfc764664 100644 --- a/homeassistant/components/airzone/binary_sensor.py +++ b/homeassistant/components/airzone/binary_sensor.py @@ -12,6 +12,7 @@ from aioairzone.const import ( AZD_FLOOR_DEMAND, AZD_NAME, AZD_PROBLEMS, + AZD_SYSTEMS, AZD_ZONES, ) @@ -25,9 +26,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import AirzoneEntity, AirzoneZoneEntity from .const import DOMAIN from .coordinator import AirzoneUpdateCoordinator +from .entity import AirzoneEntity, AirzoneSystemEntity, AirzoneZoneEntity @dataclass @@ -37,6 +38,18 @@ class AirzoneBinarySensorEntityDescription(BinarySensorEntityDescription): attributes: dict[str, str] | None = None +SYSTEM_BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...]] = ( + AirzoneBinarySensorEntityDescription( + attributes={ + "errors": AZD_ERRORS, + }, + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + key=AZD_PROBLEMS, + name="Problem", + ), +) + ZONE_BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...]] = ( AirzoneBinarySensorEntityDescription( device_class=BinarySensorDeviceClass.RUNNING, @@ -72,6 +85,20 @@ async def async_setup_entry( coordinator = hass.data[DOMAIN][entry.entry_id] binary_sensors: list[AirzoneBinarySensor] = [] + + for system_id, system_data in coordinator.data[AZD_SYSTEMS].items(): + for description in SYSTEM_BINARY_SENSOR_TYPES: + if description.key in system_data: + binary_sensors.append( + AirzoneSystemBinarySensor( + coordinator, + description, + entry, + system_id, + system_data, + ) + ) + for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items(): for description in ZONE_BINARY_SENSOR_TYPES: if description.key in zone_data: @@ -109,6 +136,24 @@ class AirzoneBinarySensor(AirzoneEntity, BinarySensorEntity): return self.get_airzone_value(self.entity_description.key) +class AirzoneSystemBinarySensor(AirzoneSystemEntity, AirzoneBinarySensor): + """Define an Airzone System binary sensor.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + description: AirzoneBinarySensorEntityDescription, + entry: ConfigEntry, + system_id: str, + system_data: dict[str, Any], + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry, system_data) + self._attr_name = f"System {system_id} {description.name}" + self._attr_unique_id = f"{self._attr_unique_id}_{system_id}_{description.key}" + self.entity_description = description + + class AirzoneZoneBinarySensor(AirzoneZoneEntity, AirzoneBinarySensor): """Define an Airzone Zone binary sensor.""" diff --git a/homeassistant/components/airzone/climate.py b/homeassistant/components/airzone/climate.py index 9cff1ff393d..21bb08f08c9 100644 --- a/homeassistant/components/airzone/climate.py +++ b/homeassistant/components/airzone/climate.py @@ -39,9 +39,9 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import AirzoneZoneEntity from .const import API_TEMPERATURE_STEP, DOMAIN, TEMP_UNIT_LIB_TO_HASS from .coordinator import AirzoneUpdateCoordinator +from .entity import AirzoneZoneEntity _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/airzone/entity.py b/homeassistant/components/airzone/entity.py new file mode 100644 index 00000000000..bf1512f3801 --- /dev/null +++ b/homeassistant/components/airzone/entity.py @@ -0,0 +1,105 @@ +"""Entity classes for the Airzone integration.""" +from __future__ import annotations + +from typing import Any + +from aioairzone.const import ( + AZD_FIRMWARE, + AZD_FULL_NAME, + AZD_ID, + AZD_MODEL, + AZD_NAME, + AZD_SYSTEM, + AZD_SYSTEMS, + AZD_THERMOSTAT_FW, + AZD_THERMOSTAT_MODEL, + AZD_ZONES, +) + +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, MANUFACTURER +from .coordinator import AirzoneUpdateCoordinator + + +class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator]): + """Define an Airzone entity.""" + + def get_airzone_value(self, key) -> Any: + """Return Airzone entity value by key.""" + raise NotImplementedError() + + +class AirzoneSystemEntity(AirzoneEntity): + """Define an Airzone System entity.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + entry: ConfigEntry, + system_data: dict[str, Any], + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + self.system_id = system_data[AZD_ID] + + self._attr_device_info: DeviceInfo = { + "identifiers": {(DOMAIN, f"{entry.entry_id}_{self.system_id}")}, + "manufacturer": MANUFACTURER, + "model": self.get_airzone_value(AZD_MODEL), + "name": self.get_airzone_value(AZD_FULL_NAME), + "sw_version": self.get_airzone_value(AZD_FIRMWARE), + "via_device": (DOMAIN, f"{entry.entry_id}_ws"), + } + self._attr_unique_id = ( + entry.entry_id if entry.unique_id is None else entry.unique_id + ) + + def get_airzone_value(self, key) -> Any: + """Return system value by key.""" + value = None + if system := self.coordinator.data[AZD_SYSTEMS].get(self.system_id): + if key in system: + value = system[key] + return value + + +class AirzoneZoneEntity(AirzoneEntity): + """Define an Airzone Zone entity.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + entry: ConfigEntry, + system_zone_id: str, + zone_data: dict[str, Any], + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + self.system_id = zone_data[AZD_SYSTEM] + self.system_zone_id = system_zone_id + self.zone_id = zone_data[AZD_ID] + + self._attr_device_info: DeviceInfo = { + "identifiers": {(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, + "manufacturer": MANUFACTURER, + "model": self.get_airzone_value(AZD_THERMOSTAT_MODEL), + "name": f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", + "sw_version": self.get_airzone_value(AZD_THERMOSTAT_FW), + "via_device": (DOMAIN, f"{entry.entry_id}_{self.system_id}"), + } + self._attr_unique_id = ( + entry.entry_id if entry.unique_id is None else entry.unique_id + ) + + def get_airzone_value(self, key) -> Any: + """Return zone value by key.""" + value = None + if zone := self.coordinator.data[AZD_ZONES].get(self.system_zone_id): + if key in zone: + value = zone[key] + return value diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index f41add5053a..81dcea5098e 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -16,9 +16,9 @@ from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import AirzoneEntity, AirzoneZoneEntity from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS from .coordinator import AirzoneUpdateCoordinator +from .entity import AirzoneEntity, AirzoneZoneEntity ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( diff --git a/tests/components/airzone/test_binary_sensor.py b/tests/components/airzone/test_binary_sensor.py index d6a3fdcb115..138801d1dc0 100644 --- a/tests/components/airzone/test_binary_sensor.py +++ b/tests/components/airzone/test_binary_sensor.py @@ -13,6 +13,11 @@ async def test_airzone_create_binary_sensors(hass: HomeAssistant) -> None: await async_init_integration(hass) + # Systems + state = hass.states.get("binary_sensor.system_1_problem") + assert state.state == STATE_OFF + + # Zones state = hass.states.get("binary_sensor.despacho_air_demand") assert state.state == STATE_OFF diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index 52f15dd1476..ede4bcc3b53 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -19,9 +19,12 @@ from aioairzone.const import ( API_MODES, API_NAME, API_ON, + API_POWER, API_ROOM_TEMP, API_SET_POINT, + API_SYSTEM_FIRMWARE, API_SYSTEM_ID, + API_SYSTEM_TYPE, API_SYSTEMS, API_THERMOS_FIRMWARE, API_THERMOS_RADIO, @@ -31,7 +34,7 @@ from aioairzone.const import ( API_WIFI_RSSI, API_ZONE_ID, ) -from aioairzone.exceptions import InvalidMethod, SystemOutOfRange +from aioairzone.exceptions import InvalidMethod from homeassistant.components.airzone import DOMAIN from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT @@ -178,6 +181,17 @@ HVAC_MOCK = { ] } +HVAC_SYSTEMS_MOCK = { + API_SYSTEMS: [ + { + API_SYSTEM_ID: 1, + API_POWER: 0, + API_SYSTEM_FIRMWARE: "3.31", + API_SYSTEM_TYPE: 1, + } + ] +} + HVAC_WEBSERVER_MOCK = { API_MAC: "11:22:33:44:55:66", API_WIFI_CHANNEL: 6, @@ -202,7 +216,7 @@ async def async_init_integration( return_value=HVAC_MOCK, ), patch( "homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems", - side_effect=SystemOutOfRange, + return_value=HVAC_SYSTEMS_MOCK, ), patch( "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", side_effect=InvalidMethod, From e5870c65ee928b37fbc07bf7ea1ab4aaa527678e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 18:41:25 +0200 Subject: [PATCH 310/930] Add missing cast test fixture (#71595) --- tests/components/cast/fixtures/rthkaudio2.m3u8 | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/components/cast/fixtures/rthkaudio2.m3u8 diff --git a/tests/components/cast/fixtures/rthkaudio2.m3u8 b/tests/components/cast/fixtures/rthkaudio2.m3u8 new file mode 100644 index 00000000000..388c115635f --- /dev/null +++ b/tests/components/cast/fixtures/rthkaudio2.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=54000,CODECS="mp4a.40.2" +https://rthkaudio2-lh.akamaihd.net/i/radio2_1@355865/index_56_a-p.m3u8?sd=10&rebase=on +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=54000,CODECS="mp4a.40.2" +https://rthkaudio2-lh.akamaihd.net/i/radio2_1@355865/index_56_a-b.m3u8?sd=10&rebase=on From 75ce66e8bdbc4b968c9609b0d4f31d863b0ff932 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 9 May 2022 18:42:18 +0200 Subject: [PATCH 311/930] Migrate wemo light to ColorMode (#70857) --- homeassistant/components/wemo/light.py | 60 +++++++++++++--------- tests/components/wemo/test_light_bridge.py | 11 +++- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index 34ec7cab08d..87ebbd9e2c3 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -11,9 +11,7 @@ from homeassistant.components.light import ( ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_TRANSITION, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, + ColorMode, LightEntity, LightEntityFeature, ) @@ -29,13 +27,6 @@ from .const import DOMAIN as WEMO_DOMAIN from .entity import WemoBinaryStateEntity, WemoEntity from .wemo_device import DeviceCoordinator -SUPPORT_WEMO = ( - SUPPORT_BRIGHTNESS - | SUPPORT_COLOR_TEMP - | SUPPORT_COLOR - | LightEntityFeature.TRANSITION -) - # The WEMO_ constants below come from pywemo itself WEMO_OFF = 0 @@ -94,6 +85,8 @@ def async_setup_bridge( class WemoLight(WemoEntity, LightEntity): """Representation of a WeMo light.""" + _attr_supported_features = LightEntityFeature.TRANSITION + def __init__(self, coordinator: DeviceCoordinator, light: bridge.Light) -> None: """Initialize the WeMo light.""" super().__init__(coordinator) @@ -133,27 +126,48 @@ class WemoLight(WemoEntity, LightEntity): return cast(int, self.light.state.get("level", 255)) @property - def hs_color(self) -> tuple[float, float] | None: - """Return the hs color values of this light.""" - if xy_color := self.light.state.get("color_xy"): - return color_util.color_xy_to_hs(*xy_color) - return None + def xy_color(self) -> tuple[float, float] | None: + """Return the xy color value [float, float].""" + return self.light.state.get("color_xy") # type:ignore[no-any-return] @property def color_temp(self) -> int | None: """Return the color temperature of this light in mireds.""" return cast(Optional[int], self.light.state.get("temperature_mireds")) + @property + def color_mode(self) -> ColorMode: + """Return the color mode of the light.""" + if ( + "colorcontrol" in self.light.capabilities + and self.light.state.get("color_xy") is not None + ): + return ColorMode.XY + if "colortemperature" in self.light.capabilities: + return ColorMode.COLOR_TEMP + if "levelcontrol" in self.light.capabilities: + return ColorMode.BRIGHTNESS + return ColorMode.ONOFF + + @property + def supported_color_modes(self) -> set[ColorMode]: + """Flag supported color modes.""" + modes: set[ColorMode] = set() + if "colorcontrol" in self.light.capabilities: + modes.add(ColorMode.XY) + if "colortemperature" in self.light.capabilities: + modes.add(ColorMode.COLOR_TEMP) + if "levelcontrol" in self.light.capabilities and not modes: + modes.add(ColorMode.BRIGHTNESS) + if not modes: + modes.add(ColorMode.ONOFF) + return modes + @property def is_on(self) -> bool: """Return true if device is on.""" return cast(int, self.light.state.get("onoff")) != WEMO_OFF - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_WEMO - def turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" xy_color = None @@ -194,10 +208,8 @@ class WemoLight(WemoEntity, LightEntity): class WemoDimmer(WemoBinaryStateEntity, LightEntity): """Representation of a WeMo dimmer.""" - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_BRIGHTNESS + _attr_supported_color_modes = {ColorMode.BRIGHTNESS} + _attr_color_mode = ColorMode.BRIGHTNESS @property def brightness(self) -> int: diff --git a/tests/components/wemo/test_light_bridge.py b/tests/components/wemo/test_light_bridge.py index 3184335b173..afcdb37cba9 100644 --- a/tests/components/wemo/test_light_bridge.py +++ b/tests/components/wemo/test_light_bridge.py @@ -8,7 +8,13 @@ from homeassistant.components.homeassistant import ( DOMAIN as HA_DOMAIN, SERVICE_UPDATE_ENTITY, ) -from homeassistant.components.light import ATTR_COLOR_TEMP, DOMAIN as LIGHT_DOMAIN +from homeassistant.components.light import ( + ATTR_COLOR_MODE, + ATTR_COLOR_TEMP, + ATTR_SUPPORTED_COLOR_MODES, + DOMAIN as LIGHT_DOMAIN, + ColorMode, +) from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.setup import async_setup_component @@ -32,6 +38,7 @@ def pywemo_bridge_light_fixture(pywemo_device): light.name = pywemo_device.name light.bridge = pywemo_device light.state = {"onoff": 0, "available": True} + light.capabilities = ["onoff", "levelcontrol", "colortemperature"] pywemo_device.Lights = {pywemo_device.serialnumber: light} return light @@ -102,6 +109,8 @@ async def test_light_update_entity( ) state = hass.states.get(wemo_entity.entity_id) assert state.attributes.get(ATTR_COLOR_TEMP) == 432 + assert state.attributes.get(ATTR_SUPPORTED_COLOR_MODES) == [ColorMode.COLOR_TEMP] + assert state.attributes.get(ATTR_COLOR_MODE) == ColorMode.COLOR_TEMP assert state.state == STATE_ON # Off state. From b9b83c05e9d53963891bb50ec9ed14c2bb07f4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 9 May 2022 19:56:59 +0200 Subject: [PATCH 312/930] Add Airzone to strict typing (#71604) --- .strict-typing | 1 + .../components/airzone/binary_sensor.py | 28 ++++++++----------- homeassistant/components/airzone/climate.py | 2 +- .../components/airzone/coordinator.py | 8 ++++-- homeassistant/components/airzone/entity.py | 6 ++-- homeassistant/components/airzone/sensor.py | 12 ++++---- mypy.ini | 11 ++++++++ 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/.strict-typing b/.strict-typing index 67efdfa7953..2aa336afb9e 100644 --- a/.strict-typing +++ b/.strict-typing @@ -43,6 +43,7 @@ homeassistant.components.aftership.* homeassistant.components.air_quality.* homeassistant.components.airly.* homeassistant.components.airvisual.* +homeassistant.components.airzone.* homeassistant.components.aladdin_connect.* homeassistant.components.alarm_control_panel.* homeassistant.components.amazon_polly.* diff --git a/homeassistant/components/airzone/binary_sensor.py b/homeassistant/components/airzone/binary_sensor.py index 4dbfc764664..d1885e0edf9 100644 --- a/homeassistant/components/airzone/binary_sensor.py +++ b/homeassistant/components/airzone/binary_sensor.py @@ -1,7 +1,6 @@ """Support for the Airzone sensors.""" from __future__ import annotations -from collections.abc import Mapping from dataclasses import dataclass from typing import Any, Final @@ -22,7 +21,7 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -120,20 +119,15 @@ class AirzoneBinarySensor(AirzoneEntity, BinarySensorEntity): entity_description: AirzoneBinarySensorEntityDescription - @property - def extra_state_attributes(self) -> Mapping[str, Any] | None: - """Return state attributes.""" - if not self.entity_description.attributes: - return None - return { - key: self.get_airzone_value(val) - for key, val in self.entity_description.attributes.items() - } - - @property - def is_on(self) -> bool | None: - """Return true if the binary sensor is on.""" - return self.get_airzone_value(self.entity_description.key) + @callback + def _async_update_attrs(self) -> None: + """Update binary sensor attributes.""" + self._attr_is_on = self.get_airzone_value(self.entity_description.key) + if self.entity_description.attributes: + self._attr_extra_state_attributes = { + key: self.get_airzone_value(val) + for key, val in self.entity_description.attributes.items() + } class AirzoneSystemBinarySensor(AirzoneSystemEntity, AirzoneBinarySensor): @@ -152,6 +146,7 @@ class AirzoneSystemBinarySensor(AirzoneSystemEntity, AirzoneBinarySensor): self._attr_name = f"System {system_id} {description.name}" self._attr_unique_id = f"{self._attr_unique_id}_{system_id}_{description.key}" self.entity_description = description + self._async_update_attrs() class AirzoneZoneBinarySensor(AirzoneZoneEntity, AirzoneBinarySensor): @@ -173,3 +168,4 @@ class AirzoneZoneBinarySensor(AirzoneZoneEntity, AirzoneBinarySensor): f"{self._attr_unique_id}_{system_zone_id}_{description.key}" ) self.entity_description = description + self._async_update_attrs() diff --git a/homeassistant/components/airzone/climate.py b/homeassistant/components/airzone/climate.py index 21bb08f08c9..e264df18e0c 100644 --- a/homeassistant/components/airzone/climate.py +++ b/homeassistant/components/airzone/climate.py @@ -162,7 +162,7 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity): params[API_ON] = 1 await self._async_update_hvac_params(params) - async def async_set_temperature(self, **kwargs) -> None: + async def async_set_temperature(self, **kwargs: dict[str, Any]) -> None: """Set new target temperature.""" params = { API_SET_POINT: kwargs.get(ATTR_TEMPERATURE), diff --git a/homeassistant/components/airzone/coordinator.py b/homeassistant/components/airzone/coordinator.py index 9e1dc44bb6c..f9f7322a3eb 100644 --- a/homeassistant/components/airzone/coordinator.py +++ b/homeassistant/components/airzone/coordinator.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import timedelta import logging +from typing import Any, cast from aioairzone.exceptions import AirzoneError from aioairzone.localapi import AirzoneLocalApi @@ -18,7 +19,7 @@ SCAN_INTERVAL = timedelta(seconds=60) _LOGGER = logging.getLogger(__name__) -class AirzoneUpdateCoordinator(DataUpdateCoordinator): +class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): """Class to manage fetching data from the Airzone device.""" def __init__(self, hass: HomeAssistant, airzone: AirzoneLocalApi) -> None: @@ -30,13 +31,14 @@ class AirzoneUpdateCoordinator(DataUpdateCoordinator): _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL, + update_method=self._async_update, ) - async def _async_update_data(self): + async def _async_update(self) -> dict[str, Any]: """Update data via library.""" async with async_timeout.timeout(AIOAIRZONE_DEVICE_TIMEOUT_SEC): try: await self.airzone.update() except AirzoneError as error: raise UpdateFailed(error) from error - return self.airzone.data() + return cast(dict[str, Any], self.airzone.data()) diff --git a/homeassistant/components/airzone/entity.py b/homeassistant/components/airzone/entity.py index bf1512f3801..687d7873ece 100644 --- a/homeassistant/components/airzone/entity.py +++ b/homeassistant/components/airzone/entity.py @@ -27,7 +27,7 @@ from .coordinator import AirzoneUpdateCoordinator class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator]): """Define an Airzone entity.""" - def get_airzone_value(self, key) -> Any: + def get_airzone_value(self, key: str) -> Any: """Return Airzone entity value by key.""" raise NotImplementedError() @@ -58,7 +58,7 @@ class AirzoneSystemEntity(AirzoneEntity): entry.entry_id if entry.unique_id is None else entry.unique_id ) - def get_airzone_value(self, key) -> Any: + def get_airzone_value(self, key: str) -> Any: """Return system value by key.""" value = None if system := self.coordinator.data[AZD_SYSTEMS].get(self.system_id): @@ -96,7 +96,7 @@ class AirzoneZoneEntity(AirzoneEntity): entry.entry_id if entry.unique_id is None else entry.unique_id ) - def get_airzone_value(self, key) -> Any: + def get_airzone_value(self, key: str) -> Any: """Return zone value by key.""" value = None if zone := self.coordinator.data[AZD_ZONES].get(self.system_zone_id): diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index 81dcea5098e..3e81f5df094 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -13,7 +13,7 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, TEMP_CELSIUS -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS @@ -64,10 +64,10 @@ async def async_setup_entry( class AirzoneSensor(AirzoneEntity, SensorEntity): """Define an Airzone sensor.""" - @property - def native_value(self): - """Return the state.""" - return self.get_airzone_value(self.entity_description.key) + @callback + def _async_update_attrs(self) -> None: + """Update sensor attributes.""" + self._attr_native_value = self.get_airzone_value(self.entity_description.key) class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor): @@ -94,3 +94,5 @@ class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor): self._attr_native_unit_of_measurement = TEMP_UNIT_LIB_TO_HASS.get( self.get_airzone_value(AZD_TEMP_UNIT) ) + + self._async_update_attrs() diff --git a/mypy.ini b/mypy.ini index 81677b8d8ff..0232e1eec15 100644 --- a/mypy.ini +++ b/mypy.ini @@ -236,6 +236,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.airzone.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.aladdin_connect.*] check_untyped_defs = true disallow_incomplete_defs = true From d8e4f6d6e6965159d6fdad9f059e8c367a2e9b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 9 May 2022 19:57:27 +0200 Subject: [PATCH 313/930] Add QNAP QSW to strict typing (#71603) --- .strict-typing | 1 + homeassistant/components/qnap_qsw/coordinator.py | 8 +++++--- mypy.ini | 11 +++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.strict-typing b/.strict-typing index 2aa336afb9e..f7264591c83 100644 --- a/.strict-typing +++ b/.strict-typing @@ -175,6 +175,7 @@ homeassistant.components.powerwall.* homeassistant.components.proximity.* homeassistant.components.pvoutput.* homeassistant.components.pure_energie.* +homeassistant.components.qnap_qsw.* homeassistant.components.rainmachine.* homeassistant.components.rdw.* homeassistant.components.recollect_waste.* diff --git a/homeassistant/components/qnap_qsw/coordinator.py b/homeassistant/components/qnap_qsw/coordinator.py index 064953b1446..e3ca95076d0 100644 --- a/homeassistant/components/qnap_qsw/coordinator.py +++ b/homeassistant/components/qnap_qsw/coordinator.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import timedelta import logging +from typing import Any, cast from aioqsw.exceptions import QswError from aioqsw.localapi import QnapQswApi @@ -18,7 +19,7 @@ SCAN_INTERVAL = timedelta(seconds=60) _LOGGER = logging.getLogger(__name__) -class QswUpdateCoordinator(DataUpdateCoordinator): +class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): """Class to manage fetching data from the QNAP QSW device.""" def __init__(self, hass: HomeAssistant, qsw: QnapQswApi) -> None: @@ -30,13 +31,14 @@ class QswUpdateCoordinator(DataUpdateCoordinator): _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL, + update_method=self._async_update, ) - async def _async_update_data(self): + async def _async_update(self) -> dict[str, Any]: """Update data via library.""" async with async_timeout.timeout(QSW_TIMEOUT_SEC): try: await self.qsw.update() except QswError as error: raise UpdateFailed(error) from error - return self.qsw.data() + return cast(dict[str, Any], self.qsw.data()) diff --git a/mypy.ini b/mypy.ini index 0232e1eec15..ab4ba77c98b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1688,6 +1688,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.qnap_qsw.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.rainmachine.*] check_untyped_defs = true disallow_incomplete_defs = true From 37c4318d336cc19db333ffbba8dfd24d8119df9f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 12:58:42 -0500 Subject: [PATCH 314/930] Fix merge conflict with master to dev in sabnzbd (CI fix) (#71605) --- tests/components/sabnzbd/test_init.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/sabnzbd/test_init.py b/tests/components/sabnzbd/test_init.py index 9bdef4119d0..f140c332778 100644 --- a/tests/components/sabnzbd/test_init.py +++ b/tests/components/sabnzbd/test_init.py @@ -3,7 +3,7 @@ from unittest.mock import patch import pytest -from homeassistant.components.sabnzbd import DEFAULT_NAME, DOMAIN, SENSOR_KEYS +from homeassistant.components.sabnzbd import DEFAULT_NAME, DOMAIN, OLD_SENSOR_KEYS from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_URL from homeassistant.helpers.device_registry import DeviceEntryType @@ -54,7 +54,7 @@ async def test_unique_id_migrate(hass, device_registry, entity_registry): entity_id_sensor_key = [] - for sensor_key in SENSOR_KEYS: + for sensor_key in OLD_SENSOR_KEYS: mock_entity_id = f"{SENSOR_DOMAIN}.{DOMAIN}_{sensor_key}" entity_registry.async_get_or_create( SENSOR_DOMAIN, From 1a45e543240470db952f4f1dc9d632e6c4dc2d8e Mon Sep 17 00:00:00 2001 From: Tom Harris Date: Mon, 9 May 2022 14:20:45 -0400 Subject: [PATCH 315/930] Fix Insteon issue with dimmer default on level (#71426) --- homeassistant/components/insteon/insteon_entity.py | 5 ++--- homeassistant/components/insteon/light.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/insteon/insteon_entity.py b/homeassistant/components/insteon/insteon_entity.py index 60935f3f951..67d30ba8cad 100644 --- a/homeassistant/components/insteon/insteon_entity.py +++ b/homeassistant/components/insteon/insteon_entity.py @@ -153,10 +153,9 @@ class InsteonEntity(Entity): def get_device_property(self, name: str): """Get a single Insteon device property value (raw).""" - value = None if (prop := self._insteon_device.properties.get(name)) is not None: - value = prop.value if prop.new_value is None else prop.new_value - return value + return prop.value + return None def _get_label(self): """Get the device label for grouped devices.""" diff --git a/homeassistant/components/insteon/light.py b/homeassistant/components/insteon/light.py index 05ad9794042..bf8b693b103 100644 --- a/homeassistant/components/insteon/light.py +++ b/homeassistant/components/insteon/light.py @@ -58,9 +58,9 @@ class InsteonDimmerEntity(InsteonEntity, LightEntity): """Turn light on.""" if ATTR_BRIGHTNESS in kwargs: brightness = int(kwargs[ATTR_BRIGHTNESS]) - else: + elif self._insteon_device_group.group == 1: brightness = self.get_device_property(ON_LEVEL) - if brightness is not None: + if brightness: await self._insteon_device.async_on( on_level=brightness, group=self._insteon_device_group.group ) From 0fcdca2d71578b257df2938cf350e9b07c17a176 Mon Sep 17 00:00:00 2001 From: TheHolyRoger <39387497+TheHolyRoger@users.noreply.github.com> Date: Mon, 9 May 2022 20:40:15 +0100 Subject: [PATCH 316/930] Add optional base64 decoding of mqtt camera image (#71223) Add unittest for b64 decoding of camera, fix linting --- homeassistant/components/mqtt/camera.py | 8 +++++-- tests/components/mqtt/test_camera.py | 29 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 0e387023a39..176e8e86b6b 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -1,6 +1,7 @@ """Camera that loads a picture from an MQTT topic.""" from __future__ import annotations +from base64 import b64decode import functools import voluptuous as vol @@ -16,7 +17,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import subscription from .. import mqtt -from .const import CONF_QOS, CONF_TOPIC +from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, @@ -102,7 +103,10 @@ class MqttCamera(MqttEntity, Camera): @log_messages(self.hass, self.entity_id) def message_received(msg): """Handle new MQTT messages.""" - self._last_image = msg.payload + if self._config[CONF_ENCODING] == "b64": + self._last_image = b64decode(msg.payload) + else: + self._last_image = msg.payload self._sub_state = subscription.async_prepare_subscribe_topics( self.hass, diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 07fd7dc2c14..48f73a504bf 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -1,4 +1,5 @@ """The tests for mqtt camera component.""" +from base64 import b64encode from http import HTTPStatus import json from unittest.mock import patch @@ -64,6 +65,34 @@ async def test_run_camera_setup(hass, hass_client_no_auth, mqtt_mock): assert body == "beer" +async def test_run_camera_b64_encoded(hass, hass_client_no_auth, mqtt_mock): + """Test that it fetches the given encoded payload.""" + topic = "test/camera" + await async_setup_component( + hass, + "camera", + { + "camera": { + "platform": "mqtt", + "topic": topic, + "name": "Test Camera", + "encoding": "b64", + } + }, + ) + await hass.async_block_till_done() + + url = hass.states.get("camera.test_camera").attributes["entity_picture"] + + async_fire_mqtt_message(hass, topic, b64encode(b"grass")) + + client = await hass_client_no_auth() + resp = await client.get(url) + assert resp.status == HTTPStatus.OK + body = await resp.text() + assert body == "grass" + + async def test_availability_when_connection_lost(hass, mqtt_mock): """Test availability after MQTT disconnection.""" await help_test_availability_when_connection_lost( From 287bc5e3dcfea2fc04ed30bdc8719dd580f1b4a1 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Mon, 9 May 2022 22:07:47 +0200 Subject: [PATCH 317/930] Add release url to Fritz!Tools update entity (#71606) * add release url to update entity * fix test --- homeassistant/components/fritz/common.py | 28 ++++++++++++++++++------ homeassistant/components/fritz/update.py | 5 +++++ tests/components/fritz/const.py | 5 ++++- tests/components/fritz/test_update.py | 12 +++++++--- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index d7b8d916803..4a01367cc20 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -174,6 +174,7 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): self._current_firmware: str | None = None self._latest_firmware: str | None = None self._update_available: bool = False + self._release_url: str | None = None async def async_setup( self, options: MappingProxyType[str, Any] | None = None @@ -224,7 +225,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): self._model = info.get("NewModelName") self._current_firmware = info.get("NewSoftwareVersion") - self._update_available, self._latest_firmware = self._update_device_info() + ( + self._update_available, + self._latest_firmware, + self._release_url, + ) = self._update_device_info() if "Layer3Forwarding1" in self.connection.services: if connection_type := self.connection.call_action( "Layer3Forwarding1", "GetDefaultConnectionService" @@ -274,6 +279,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): """Return if new SW version is available.""" return self._update_available + @property + def release_url(self) -> str | None: + """Return the info URL for latest firmware.""" + return self._release_url + @property def mac(self) -> str: """Return device Mac address.""" @@ -305,12 +315,12 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): raise HomeAssistantError("Error refreshing hosts info") from ex return [] - def _update_device_info(self) -> tuple[bool, str | None]: + def _update_device_info(self) -> tuple[bool, str | None, str | None]: """Retrieve latest device information from the FRITZ!Box.""" - version = self.connection.call_action("UserInterface1", "GetInfo").get( - "NewX_AVM-DE_Version" - ) - return bool(version), version + info = self.connection.call_action("UserInterface1", "GetInfo") + version = info.get("NewX_AVM-DE_Version") + release_url = info.get("NewX_AVM-DE_InfoURL") + return bool(version), version, release_url def _get_wan_access(self, ip_address: str) -> bool | None: """Get WAN access rule for given IP address.""" @@ -361,7 +371,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): return _LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host) - self._update_available, self._latest_firmware = self._update_device_info() + ( + self._update_available, + self._latest_firmware, + self._release_url, + ) = self._update_device_info() _LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() diff --git a/homeassistant/components/fritz/update.py b/homeassistant/components/fritz/update.py index 620b932999e..03cffc3cae6 100644 --- a/homeassistant/components/fritz/update.py +++ b/homeassistant/components/fritz/update.py @@ -55,6 +55,11 @@ class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity): return self._avm_wrapper.latest_firmware return self._avm_wrapper.current_firmware + @property + def release_url(self) -> str | None: + """URL to the full release notes of the latest version available.""" + return self._avm_wrapper.release_url + async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: diff --git a/tests/components/fritz/const.py b/tests/components/fritz/const.py index 39533d07a93..f8f6f8370d7 100644 --- a/tests/components/fritz/const.py +++ b/tests/components/fritz/const.py @@ -30,8 +30,11 @@ MOCK_IPS = {"fritz.box": "192.168.178.1", "printer": "192.168.178.2"} MOCK_MODELNAME = "FRITZ!Box 7530 AX" MOCK_FIRMWARE = "256.07.29" MOCK_FIRMWARE_AVAILABLE = "256.07.50" +MOCK_FIRMWARE_RELEASE_URL = ( + "http://download.avm.de/fritzbox/fritzbox-7530-ax/deutschland/fritz.os/info_de.txt" +) MOCK_SERIAL_NUMBER = "fake_serial_number" -MOCK_FIRMWARE_INFO = [True, "1.1.1"] +MOCK_FIRMWARE_INFO = [True, "1.1.1", "some-release-url"] MOCK_MESH_SSID = "TestSSID" MOCK_MESH_MASTER_MAC = "1C:ED:6F:12:34:11" MOCK_MESH_MASTER_WIFI1_MAC = "1C:ED:6F:12:34:12" diff --git a/tests/components/fritz/test_update.py b/tests/components/fritz/test_update.py index f43b35755e8..2261ef3ef9b 100644 --- a/tests/components/fritz/test_update.py +++ b/tests/components/fritz/test_update.py @@ -10,7 +10,12 @@ from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from .const import MOCK_FIRMWARE, MOCK_FIRMWARE_AVAILABLE, MOCK_USER_DATA +from .const import ( + MOCK_FIRMWARE, + MOCK_FIRMWARE_AVAILABLE, + MOCK_FIRMWARE_RELEASE_URL, + MOCK_USER_DATA, +) from tests.common import MockConfigEntry @@ -38,7 +43,7 @@ async def test_update_available( with patch( "homeassistant.components.fritz.common.FritzBoxTools._update_device_info", - return_value=(True, MOCK_FIRMWARE_AVAILABLE), + return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL), ): entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA) entry.add_to_hass(hass) @@ -52,6 +57,7 @@ async def test_update_available( assert update.state == "on" assert update.attributes.get("installed_version") == MOCK_FIRMWARE assert update.attributes.get("latest_version") == MOCK_FIRMWARE_AVAILABLE + assert update.attributes.get("release_url") == MOCK_FIRMWARE_RELEASE_URL async def test_no_update_available( @@ -80,7 +86,7 @@ async def test_available_update_can_be_installed( with patch( "homeassistant.components.fritz.common.FritzBoxTools._update_device_info", - return_value=(True, MOCK_FIRMWARE_AVAILABLE), + return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL), ), patch( "homeassistant.components.fritz.common.FritzBoxTools.async_trigger_firmware_update", return_value=True, From bb052679df609f1f0c0382e0d3328d791b022479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hjelseth=20H=C3=B8yer?= Date: Mon, 9 May 2022 22:08:22 +0200 Subject: [PATCH 318/930] Update adax lib (#71609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Hjelseth Høyer --- homeassistant/components/adax/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/adax/manifest.json b/homeassistant/components/adax/manifest.json index b5c19269f09..408c099b8ac 100644 --- a/homeassistant/components/adax/manifest.json +++ b/homeassistant/components/adax/manifest.json @@ -3,7 +3,7 @@ "name": "Adax", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/adax", - "requirements": ["adax==0.2.0", "Adax-local==0.1.3"], + "requirements": ["adax==0.2.0", "Adax-local==0.1.4"], "codeowners": ["@danielhiversen"], "iot_class": "local_polling", "loggers": ["adax", "adax_local"] diff --git a/requirements_all.txt b/requirements_all.txt index fc76ea2dc35..72c2d54e44c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -5,7 +5,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.adax -Adax-local==0.1.3 +Adax-local==0.1.4 # homeassistant.components.homekit HAP-python==4.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cf3e5a1217b..0f67c0adfa7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -7,7 +7,7 @@ AEMET-OpenData==0.2.1 # homeassistant.components.adax -Adax-local==0.1.3 +Adax-local==0.1.4 # homeassistant.components.homekit HAP-python==4.4.0 From bec3c6e66ae3376affd0047d61c35ca6e50d7009 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 22:09:43 +0200 Subject: [PATCH 319/930] Add 'toggle' device action translation to fan (#71590) --- homeassistant/components/fan/strings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fan/strings.json b/homeassistant/components/fan/strings.json index 403a63f99d5..fdd95a822de 100644 --- a/homeassistant/components/fan/strings.json +++ b/homeassistant/components/fan/strings.json @@ -11,6 +11,7 @@ "turned_off": "{entity_name} turned off" }, "action_type": { + "toggle": "Toggle {entity_name}", "turn_on": "Turn on {entity_name}", "turn_off": "Turn off {entity_name}" } From 347193055e2d78716a6fc8b45d3e776a7b1d7c81 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 22:10:26 +0200 Subject: [PATCH 320/930] Rename logbook humanify function (#71597) Co-authored-by: J. Nick Koston --- homeassistant/components/logbook/__init__.py | 4 ++-- tests/components/logbook/common.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 5ec03b3aaa4..d3ede5303fb 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -324,7 +324,7 @@ class LogbookView(HomeAssistantView): ) -def humanify( +def _humanify( hass: HomeAssistant, rows: Generator[Row, None, None], entity_name_cache: EntityNameCache, @@ -548,7 +548,7 @@ def _get_events( query = query.order_by(Events.time_fired) return list( - humanify( + _humanify( hass, yield_rows(query), entity_name_cache, diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py index c5b8c8239e1..896add3104e 100644 --- a/tests/components/logbook/common.py +++ b/tests/components/logbook/common.py @@ -52,7 +52,7 @@ def mock_humanify(hass_, rows): context_lookup, entity_name_cache, {}, event_cache ) return list( - logbook.humanify( + logbook._humanify( hass_, rows, entity_name_cache, event_cache, context_augmenter ), ) From 1c841590aac3cd5fbef4a04d66c3b729de717b08 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 9 May 2022 13:12:48 -0700 Subject: [PATCH 321/930] Reduce mobile app error to debug (#71601) --- homeassistant/components/mobile_app/webhook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index bc67076b005..cdf6fc874d9 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -563,7 +563,7 @@ async def webhook_update_sensor_states(hass, config_entry, data): if not entity_registry.async_get_entity_id( entity_type, DOMAIN, unique_store_key ): - _LOGGER.error( + _LOGGER.debug( "Refusing to update %s non-registered sensor: %s", device_name, unique_store_key, From f859cb6b2ed638f45a53063cce907ad7ece576c2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 9 May 2022 22:19:00 +0200 Subject: [PATCH 322/930] Remove unneeded bind_hass from device_automation code (#71599) --- homeassistant/components/device_automation/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/device_automation/__init__.py b/homeassistant/components/device_automation/__init__.py index 73f28c54657..d7b75d4fcd4 100644 --- a/homeassistant/components/device_automation/__init__.py +++ b/homeassistant/components/device_automation/__init__.py @@ -26,7 +26,7 @@ from homeassistant.helpers import ( entity_registry as er, ) from homeassistant.helpers.typing import ConfigType -from homeassistant.loader import IntegrationNotFound, bind_hass +from homeassistant.loader import IntegrationNotFound from homeassistant.requirements import async_get_integration_with_requirements from .exceptions import DeviceNotFound, InvalidDeviceAutomationConfig @@ -212,7 +212,6 @@ async def _async_get_device_automations_from_domain( ) -@bind_hass async def async_get_device_automations( hass: HomeAssistant, automation_type: DeviceAutomationType, From 5430b513589a50d48fba33ea14b13e0d45060599 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 9 May 2022 22:19:22 +0200 Subject: [PATCH 323/930] Update pre-commit to 2.19.0 (#71592) Co-authored-by: Shay Levy --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 0ef6063a1ca..b6fb5272abd 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -12,7 +12,7 @@ coverage==6.3.2 freezegun==1.2.1 mock-open==1.4.0 mypy==0.950 -pre-commit==2.17.0 +pre-commit==2.19.0 pylint==2.13.8 pipdeptree==2.2.1 pylint-strict-informational==0.1 From 2560d35f1c4de7eaa9ff9623b1060a2f9f9fa8ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 15:21:21 -0500 Subject: [PATCH 324/930] Always show the start and stop event in logbook (#71600) --- homeassistant/components/logbook/__init__.py | 181 +++++++------------ tests/components/logbook/test_init.py | 14 +- 2 files changed, 71 insertions(+), 124 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index d3ede5303fb..53063d36fc0 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -5,7 +5,6 @@ from collections.abc import Callable, Generator, Iterable from contextlib import suppress from datetime import datetime as dt, timedelta from http import HTTPStatus -from itertools import groupby import json import re from typing import Any, cast @@ -338,127 +337,76 @@ def _humanify( """ external_events = hass.data.get(DOMAIN, {}) # Continuous sensors, will be excluded from the logbook - continuous_sensors = {} + continuous_sensors: dict[str, bool] = {} - # Group events in batches of GROUP_BY_MINUTES - for _, g_rows in groupby( - rows, lambda row: row.time_fired.minute // GROUP_BY_MINUTES # type: ignore[no-any-return] - ): + # Process events + for row in rows: + event_type = row.event_type + if event_type == EVENT_STATE_CHANGED: + entity_id = row.entity_id + assert entity_id is not None + # Skip continuous sensors + if ( + is_continuous := continuous_sensors.get(entity_id) + ) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN: + is_continuous = _is_sensor_continuous(hass, entity_id) + continuous_sensors[entity_id] = is_continuous + if is_continuous: + continue - rows_batch = list(g_rows) + data = { + "when": _row_time_fired_isoformat(row), + "name": entity_name_cache.get(entity_id, row), + "state": row.state, + "entity_id": entity_id, + } + if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): + data["icon"] = icon - # Group HA start/stop events - # Maps minute of event to 1: stop, 2: stop + start - start_stop_events = {} + context_augmenter.augment(data, entity_id, row) + yield data - # Process events - for row in rows_batch: - if row.event_type == EVENT_STATE_CHANGED: - entity_id = row.entity_id - if ( - entity_id in continuous_sensors - or split_entity_id(entity_id)[0] != SENSOR_DOMAIN - ): - continue - assert entity_id is not None - continuous_sensors[entity_id] = _is_sensor_continuous(hass, entity_id) + elif event_type in external_events: + domain, describe_event = external_events[event_type] + data = describe_event(event_cache.get(row)) + data["when"] = _row_time_fired_isoformat(row) + data["domain"] = domain + context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row) + yield data - elif row.event_type == EVENT_HOMEASSISTANT_STOP: - if row.time_fired.minute in start_stop_events: - continue + elif event_type == EVENT_HOMEASSISTANT_START: + yield { + "when": _row_time_fired_isoformat(row), + "name": "Home Assistant", + "message": "started", + "domain": HA_DOMAIN, + } + elif event_type == EVENT_HOMEASSISTANT_STOP: + yield { + "when": _row_time_fired_isoformat(row), + "name": "Home Assistant", + "message": "stopped", + "domain": HA_DOMAIN, + } - start_stop_events[row.time_fired.minute] = 1 + elif event_type == EVENT_LOGBOOK_ENTRY: + event = event_cache.get(row) + event_data = event.data + domain = event_data.get(ATTR_DOMAIN) + entity_id = event_data.get(ATTR_ENTITY_ID) + if domain is None and entity_id is not None: + with suppress(IndexError): + domain = split_entity_id(str(entity_id))[0] - elif row.event_type == EVENT_HOMEASSISTANT_START: - if row.time_fired.minute not in start_stop_events: - continue - - start_stop_events[row.time_fired.minute] = 2 - - # Yield entries - for row in rows_batch: - if row.event_type == EVENT_STATE_CHANGED: - entity_id = row.entity_id - assert entity_id is not None - - if continuous_sensors.get(entity_id): - # Skip continuous sensors - continue - - data = { - "when": _row_time_fired_isoformat(row), - "name": entity_name_cache.get(entity_id, row), - "state": row.state, - "entity_id": entity_id, - } - - if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): - data["icon"] = icon - - if row.context_user_id: - data["context_user_id"] = row.context_user_id - - context_augmenter.augment(data, entity_id, row) - - yield data - - elif row.event_type in external_events: - domain, describe_event = external_events[row.event_type] - data = describe_event(event_cache.get(row)) - data["when"] = _row_time_fired_isoformat(row) - data["domain"] = domain - if row.context_user_id: - data["context_user_id"] = row.context_user_id - - entity_id = data.get(ATTR_ENTITY_ID) - context_augmenter.augment(data, entity_id, row) - yield data - - elif row.event_type == EVENT_HOMEASSISTANT_START: - if start_stop_events.get(row.time_fired.minute) == 2: - continue - yield { - "when": _row_time_fired_isoformat(row), - "name": "Home Assistant", - "message": "started", - "domain": HA_DOMAIN, - } - - elif row.event_type == EVENT_HOMEASSISTANT_STOP: - if start_stop_events.get(row.time_fired.minute) == 2: - action = "restarted" - else: - action = "stopped" - - yield { - "when": _row_time_fired_isoformat(row), - "name": "Home Assistant", - "message": action, - "domain": HA_DOMAIN, - } - - elif row.event_type == EVENT_LOGBOOK_ENTRY: - event = event_cache.get(row) - event_data = event.data - domain = event_data.get(ATTR_DOMAIN) - entity_id = event_data.get(ATTR_ENTITY_ID) - if domain is None and entity_id is not None: - with suppress(IndexError): - domain = split_entity_id(str(entity_id))[0] - - data = { - "when": _row_time_fired_isoformat(row), - "name": event_data.get(ATTR_NAME), - "message": event_data.get(ATTR_MESSAGE), - "domain": domain, - "entity_id": entity_id, - } - - if row.context_user_id: - data["context_user_id"] = row.context_user_id - - context_augmenter.augment(data, entity_id, row) - yield data + data = { + "when": _row_time_fired_isoformat(row), + "name": event_data.get(ATTR_NAME), + "message": event_data.get(ATTR_MESSAGE), + "domain": domain, + "entity_id": entity_id, + } + context_augmenter.augment(data, entity_id, row) + yield data def _get_events( @@ -746,6 +694,9 @@ class ContextAugmenter: def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None: """Augment data from the row and cache.""" + if context_user_id := row.context_user_id: + data["context_user_id"] = context_user_id + if not (context_row := self.context_lookup.get(row.context_id)): return diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index ad5423286d9..e55fca1133b 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -210,11 +210,8 @@ async def test_filter_sensor(hass_: ha.HomeAssistant, hass_client): _assert_entry(entries[2], name="ble", entity_id=entity_id4, state="10") -def test_home_assistant_start_stop_grouped(hass_): - """Test if HA start and stop events are grouped. - - Events that are occurring in the same minute. - """ +def test_home_assistant_start_stop_not_grouped(hass_): + """Test if HA start and stop events are no longer grouped.""" entries = mock_humanify( hass_, ( @@ -223,10 +220,9 @@ def test_home_assistant_start_stop_grouped(hass_): ), ) - assert len(entries) == 1 - assert_entry( - entries[0], name="Home Assistant", message="restarted", domain=ha.DOMAIN - ) + assert len(entries) == 2 + assert_entry(entries[0], name="Home Assistant", message="stopped", domain=ha.DOMAIN) + assert_entry(entries[1], name="Home Assistant", message="started", domain=ha.DOMAIN) def test_home_assistant_start(hass_): From 222baa53dd9adb24a352e9a3974e69c19d2d1466 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 15:22:08 -0500 Subject: [PATCH 325/930] Make database access in the eventloop raise an exception (#71547) --- homeassistant/components/recorder/pool.py | 15 +++++++++++- homeassistant/util/async_.py | 13 +++++++--- tests/common.py | 7 +++--- tests/components/recorder/test_init.py | 30 ++++++++++++++--------- tests/components/recorder/test_pool.py | 20 +++++++++------ tests/components/recorder/test_util.py | 6 ++++- tests/conftest.py | 7 +++--- 7 files changed, 66 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/recorder/pool.py b/homeassistant/components/recorder/pool.py index 027b9bfbc25..f2f952121af 100644 --- a/homeassistant/components/recorder/pool.py +++ b/homeassistant/components/recorder/pool.py @@ -8,6 +8,7 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.pool import NullPool, SingletonThreadPool, StaticPool from homeassistant.helpers.frame import report +from homeassistant.util.async_ import check_loop from .const import DB_WORKER_PREFIX @@ -19,6 +20,10 @@ DEBUG_MUTEX_POOL_TRACE = False POOL_SIZE = 5 +ADVISE_MSG = ( + "Use homeassistant.components.recorder.get_instance(hass).async_add_executor_job()" +) + class RecorderPool(SingletonThreadPool, NullPool): # type: ignore[misc] """A hybrid of NullPool and SingletonThreadPool. @@ -62,9 +67,17 @@ class RecorderPool(SingletonThreadPool, NullPool): # type: ignore[misc] def _do_get(self) -> Any: if self.recorder_or_dbworker: return super()._do_get() + check_loop( + self._do_get_db_connection_protected, + strict=True, + advise_msg=ADVISE_MSG, + ) + return self._do_get_db_connection_protected() + + def _do_get_db_connection_protected(self) -> Any: report( "accesses the database without the database executor; " - "Use homeassistant.components.recorder.get_instance(hass).async_add_executor_job() " + f"{ADVISE_MSG} " "for faster database operations", exclude_integrations={"recorder"}, error_if_core=False, diff --git a/homeassistant/util/async_.py b/homeassistant/util/async_.py index 4e7d05f7c2e..8b96e85664d 100644 --- a/homeassistant/util/async_.py +++ b/homeassistant/util/async_.py @@ -94,8 +94,14 @@ def run_callback_threadsafe( return future -def check_loop(func: Callable[..., Any], strict: bool = True) -> None: - """Warn if called inside the event loop. Raise if `strict` is True.""" +def check_loop( + func: Callable[..., Any], strict: bool = True, advise_msg: str | None = None +) -> None: + """Warn if called inside the event loop. Raise if `strict` is True. + + The default advisory message is 'Use `await hass.async_add_executor_job()' + Set `advise_msg` to an alternate message if the the solution differs. + """ try: get_running_loop() in_loop = True @@ -134,6 +140,7 @@ def check_loop(func: Callable[..., Any], strict: bool = True) -> None: if found_frame is None: raise RuntimeError( f"Detected blocking call to {func.__name__} inside the event loop. " + f"{advise_msg or 'Use `await hass.async_add_executor_job()`'}; " "This is causing stability issues. Please report issue" ) @@ -160,7 +167,7 @@ def check_loop(func: Callable[..., Any], strict: bool = True) -> None: if strict: raise RuntimeError( "Blocking calls must be done in the executor or a separate thread; " - "Use `await hass.async_add_executor_job()` " + f"{advise_msg or 'Use `await hass.async_add_executor_job()`'}; " f"at {found_frame.filename[index:]}, line {found_frame.lineno}: {(found_frame.line or '?').strip()}" ) diff --git a/tests/common.py b/tests/common.py index 73625bcfe07..bd0b828737b 100644 --- a/tests/common.py +++ b/tests/common.py @@ -905,10 +905,9 @@ def init_recorder_component(hass, add_config=None): if recorder.CONF_COMMIT_INTERVAL not in config: config[recorder.CONF_COMMIT_INTERVAL] = 0 - with patch( - "homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", - True, - ), patch("homeassistant.components.recorder.migration.migrate_schema"): + with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( + "homeassistant.components.recorder.migration.migrate_schema" + ): assert setup_component(hass, recorder.DOMAIN, {recorder.DOMAIN: config}) assert recorder.DOMAIN in hass.config.components _LOGGER.info( diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 0b2b7b2dcb8..49133dd67bd 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -1319,7 +1319,9 @@ def test_entity_id_filter(hass_recorder): async def test_database_lock_and_unlock( - hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT, tmp_path + hass: HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, + tmp_path, ): """Test writing events during lock getting written after unlocking.""" # Use file DB, in memory DB cannot do write locks. @@ -1330,6 +1332,10 @@ async def test_database_lock_and_unlock( await async_setup_recorder_instance(hass, config) await hass.async_block_till_done() + def _get_db_events(): + with session_scope(hass=hass) as session: + return list(session.query(Events).filter_by(event_type=event_type)) + instance: Recorder = hass.data[DATA_INSTANCE] assert await instance.lock_database() @@ -1344,21 +1350,20 @@ async def test_database_lock_and_unlock( # Recording can't be finished while lock is held with pytest.raises(asyncio.TimeoutError): await asyncio.wait_for(asyncio.shield(task), timeout=1) - - with session_scope(hass=hass) as session: - db_events = list(session.query(Events).filter_by(event_type=event_type)) + db_events = await hass.async_add_executor_job(_get_db_events) assert len(db_events) == 0 assert instance.unlock_database() await task - with session_scope(hass=hass) as session: - db_events = list(session.query(Events).filter_by(event_type=event_type)) - assert len(db_events) == 1 + db_events = await hass.async_add_executor_job(_get_db_events) + assert len(db_events) == 1 async def test_database_lock_and_overflow( - hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT, tmp_path + hass: HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, + tmp_path, ): """Test writing events during lock leading to overflow the queue causes the database to unlock.""" # Use file DB, in memory DB cannot do write locks. @@ -1369,6 +1374,10 @@ async def test_database_lock_and_overflow( await async_setup_recorder_instance(hass, config) await hass.async_block_till_done() + def _get_db_events(): + with session_scope(hass=hass) as session: + return list(session.query(Events).filter_by(event_type=event_type)) + instance: Recorder = hass.data[DATA_INSTANCE] with patch.object(recorder.core, "MAX_QUEUE_BACKLOG", 1), patch.object( @@ -1384,9 +1393,8 @@ async def test_database_lock_and_overflow( # even before unlocking. await async_wait_recording_done(hass) - with session_scope(hass=hass) as session: - db_events = list(session.query(Events).filter_by(event_type=event_type)) - assert len(db_events) == 1 + db_events = await hass.async_add_executor_job(_get_db_events) + assert len(db_events) == 1 assert not instance.unlock_database() diff --git a/tests/components/recorder/test_pool.py b/tests/components/recorder/test_pool.py index ca6a88d84a7..aa47ce5eb3c 100644 --- a/tests/components/recorder/test_pool.py +++ b/tests/components/recorder/test_pool.py @@ -1,6 +1,7 @@ """Test pool.""" import threading +import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -8,6 +9,13 @@ from homeassistant.components.recorder.const import DB_WORKER_PREFIX from homeassistant.components.recorder.pool import RecorderPool +async def test_recorder_pool_called_from_event_loop(): + """Test we raise an exception when calling from the event loop.""" + engine = create_engine("sqlite://", poolclass=RecorderPool) + with pytest.raises(RuntimeError): + sessionmaker(bind=engine)().connection() + + def test_recorder_pool(caplog): """Test RecorderPool gives the same connection in the creating thread.""" @@ -28,30 +36,26 @@ def test_recorder_pool(caplog): connections.append(session.connection().connection.connection) session.close() - _get_connection_twice() - assert "accesses the database without the database executor" in caplog.text - assert connections[0] != connections[1] - caplog.clear() new_thread = threading.Thread(target=_get_connection_twice) new_thread.start() new_thread.join() assert "accesses the database without the database executor" in caplog.text - assert connections[2] != connections[3] + assert connections[0] != connections[1] caplog.clear() new_thread = threading.Thread(target=_get_connection_twice, name=DB_WORKER_PREFIX) new_thread.start() new_thread.join() assert "accesses the database without the database executor" not in caplog.text - assert connections[4] == connections[5] + assert connections[2] == connections[3] caplog.clear() new_thread = threading.Thread(target=_get_connection_twice, name="Recorder") new_thread.start() new_thread.join() assert "accesses the database without the database executor" not in caplog.text - assert connections[6] == connections[7] + assert connections[4] == connections[5] shutdown = True caplog.clear() @@ -59,4 +63,4 @@ def test_recorder_pool(caplog): new_thread.start() new_thread.join() assert "accesses the database without the database executor" not in caplog.text - assert connections[8] != connections[9] + assert connections[6] != connections[7] diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index 07334fef1c3..6b1093ee038 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -597,8 +597,12 @@ def test_periodic_db_cleanups(hass_recorder): assert str(text_obj) == "PRAGMA wal_checkpoint(TRUNCATE);" +@patch("homeassistant.components.recorder.pool.check_loop") async def test_write_lock_db( - hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT, tmp_path + skip_check_loop, + hass: HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, + tmp_path, ): """Test database write lock.""" from sqlalchemy.exc import OperationalError diff --git a/tests/conftest.py b/tests/conftest.py index 0155a965fe6..37bdb05faf7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -692,10 +692,9 @@ async def _async_init_recorder_component(hass, add_config=None): if recorder.CONF_COMMIT_INTERVAL not in config: config[recorder.CONF_COMMIT_INTERVAL] = 0 - with patch( - "homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", - True, - ), patch("homeassistant.components.recorder.migration.migrate_schema"): + with patch("homeassistant.components.recorder.ALLOW_IN_MEMORY_DB", True), patch( + "homeassistant.components.recorder.migration.migrate_schema" + ): assert await async_setup_component( hass, recorder.DOMAIN, {recorder.DOMAIN: config} ) From d8336a521652b7fe7c0c0a78bced558b1d35dd73 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 15:22:27 -0500 Subject: [PATCH 326/930] Fix missing context_id in script logbook entries (#71602) --- homeassistant/components/script/logbook.py | 1 + tests/components/logbook/test_init.py | 62 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/homeassistant/components/script/logbook.py b/homeassistant/components/script/logbook.py index f75584540d3..250b7231b32 100644 --- a/homeassistant/components/script/logbook.py +++ b/homeassistant/components/script/logbook.py @@ -17,6 +17,7 @@ def async_describe_events(hass, async_describe_event): "name": data.get(ATTR_NAME), "message": "started", "entity_id": data.get(ATTR_ENTITY_ID), + "context_id": event.context_id, } async_describe_event(DOMAIN, EVENT_SCRIPT_STARTED, async_describe_logbook_event) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index e55fca1133b..4f961dcd2c1 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -1002,6 +1002,68 @@ async def test_logbook_entity_context_id(hass, recorder_mock, hass_client): assert json_dict[7]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" +async def test_logbook_context_id_automation_script_started_manually( + hass, recorder_mock, hass_client +): + """Test the logbook populates context_ids for scripts and automations started manually.""" + await async_setup_component(hass, "logbook", {}) + await async_setup_component(hass, "automation", {}) + await async_setup_component(hass, "script", {}) + + await async_recorder_block_till_done(hass) + + # An Automation + automation_entity_id_test = "automation.alarm" + automation_context = ha.Context( + id="fc5bd62de45711eaaeb351041eec8dd9", + user_id="f400facee45711eaa9308bfd3d19e474", + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: automation_entity_id_test}, + context=automation_context, + ) + script_context = ha.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + hass.bus.async_fire( + EVENT_SCRIPT_STARTED, + {ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"}, + context=script_context, + ) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + await async_wait_recording_done(hass) + + client = await hass_client() + + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert json_dict[0]["entity_id"] == "automation.alarm" + assert "context_entity_id" not in json_dict[0] + assert json_dict[0]["context_user_id"] == "f400facee45711eaa9308bfd3d19e474" + assert json_dict[0]["context_id"] == "fc5bd62de45711eaaeb351041eec8dd9" + + assert json_dict[1]["entity_id"] == "script.mock_script" + assert "context_entity_id" not in json_dict[1] + assert json_dict[1]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + assert json_dict[1]["context_id"] == "ac5bd62de45711eaaeb351041eec8dd9" + + assert json_dict[2]["domain"] == "homeassistant" + + async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock): """Test the logbook view links events via context parent_id.""" await async_setup_component(hass, "logbook", {}) From 64636a4310f4765c7477e3588b025b85a8d4586a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 9 May 2022 14:45:53 -0700 Subject: [PATCH 327/930] Add service entity context (#71558) Co-authored-by: Shay Levy --- homeassistant/helpers/service.py | 11 +++++++++++ tests/helpers/test_service.py | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 4cd38aa9768..975b05067b2 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Awaitable, Callable, Iterable +from contextvars import ContextVar import dataclasses from functools import partial, wraps import logging @@ -63,6 +64,15 @@ _LOGGER = logging.getLogger(__name__) SERVICE_DESCRIPTION_CACHE = "service_description_cache" +_current_entity: ContextVar[str | None] = ContextVar("current_entity", default=None) + + +@callback +def async_get_current_entity() -> str | None: + """Get the current entity on which the service is called.""" + return _current_entity.get() + + class ServiceParams(TypedDict): """Type for service call parameters.""" @@ -706,6 +716,7 @@ async def _handle_entity_call( ) -> None: """Handle calling service method.""" entity.async_set_context(context) + _current_entity.set(entity.entity_id) if isinstance(func, str): result = hass.async_run_job(partial(getattr(entity, func), **data)) # type: ignore[arg-type] diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 1b8de6ca6e2..cf87377dd8f 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -19,12 +19,12 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.helpers import ( + config_validation as cv, device_registry as dev_reg, entity_registry as ent_reg, service, template, ) -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import EntityCategory from homeassistant.setup import async_setup_component @@ -1205,3 +1205,17 @@ async def test_async_extract_config_entry_ids(hass): ) assert await service.async_extract_config_entry_ids(hass, call) == {"abc"} + + +async def test_current_entity_context(hass, mock_entities): + """Test we set the current entity context var.""" + + async def mock_service(entity, call): + assert entity.entity_id == service.async_get_current_entity() + + await service.entity_service_call( + hass, + [Mock(entities=mock_entities)], + mock_service, + ha.ServiceCall("test_domain", "test_service", {"entity_id": "light.kitchen"}), + ) From 3a00c95113eb86c0b58c04509a9ff32bc6688836 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 10 May 2022 00:00:31 +0200 Subject: [PATCH 328/930] Add device_info and entity_category to Vallox (#67353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add device_info and entity_category to Vallox * Fix DeviceInfo * Address review comments 1 * vallox suggested changes Co-authored-by: Sebastian Lövdahl Co-authored-by: J. Nick Koston --- homeassistant/components/vallox/__init__.py | 56 ++++++++++++++++--- .../components/vallox/binary_sensor.py | 15 ++--- homeassistant/components/vallox/fan.py | 10 ++-- homeassistant/components/vallox/sensor.py | 13 ++--- tests/components/vallox/conftest.py | 26 ++++++++- tests/components/vallox/test_sensor.py | 10 ++-- 6 files changed, 93 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index 23e5cb53f97..a69542596c8 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -5,14 +5,16 @@ from dataclasses import dataclass, field from datetime import date import ipaddress import logging -from typing import Any, NamedTuple +from typing import Any, NamedTuple, cast from uuid import UUID from vallox_websocket_api import PROFILE as VALLOX_PROFILE, Vallox from vallox_websocket_api.exceptions import ValloxApiException from vallox_websocket_api.vallox import ( - get_next_filter_change_date as calculate_next_filter_change_date, - get_uuid as calculate_uuid, + get_model as _api_get_model, + get_next_filter_change_date as _api_get_next_filter_change_date, + get_sw_version as _api_get_sw_version, + get_uuid as _api_get_uuid, ) import voluptuous as vol @@ -20,8 +22,13 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_HOST, CONF_NAME, Platform from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.typing import ConfigType, StateType -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, + UpdateFailed, +) from .const import ( DEFAULT_FAN_SPEED_AWAY, @@ -114,16 +121,32 @@ class ValloxState: return value - def get_uuid(self) -> UUID | None: + @property + def model(self) -> str | None: + """Return the model, if any.""" + model = cast(str, _api_get_model(self.metric_cache)) + + if model == "Unknown": + return None + + return model + + @property + def sw_version(self) -> str: + """Return the SW version.""" + return cast(str, _api_get_sw_version(self.metric_cache)) + + @property + def uuid(self) -> UUID | None: """Return cached UUID value.""" - uuid = calculate_uuid(self.metric_cache) + uuid = _api_get_uuid(self.metric_cache) if not isinstance(uuid, UUID): raise ValueError return uuid def get_next_filter_change_date(self) -> date | None: """Return the next filter change date.""" - next_filter_change_date = calculate_next_filter_change_date(self.metric_cache) + next_filter_change_date = _api_get_next_filter_change_date(self.metric_cache) if not isinstance(next_filter_change_date, date): return None @@ -291,3 +314,22 @@ class ValloxServiceHandler: # be observed by all parties involved. if result: await self._coordinator.async_request_refresh() + + +class ValloxEntity(CoordinatorEntity[ValloxDataUpdateCoordinator]): + """Representation of a Vallox entity.""" + + def __init__(self, name: str, coordinator: ValloxDataUpdateCoordinator) -> None: + """Initialize a Vallox entity.""" + super().__init__(coordinator) + + self._device_uuid = self.coordinator.data.uuid + assert self.coordinator.config_entry is not None + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, str(self._device_uuid))}, + manufacturer=DEFAULT_NAME, + model=self.coordinator.data.model, + name=name, + sw_version=self.coordinator.data.sw_version, + configuration_url=f"http://{self.coordinator.config_entry.data[CONF_HOST]}", + ) diff --git a/homeassistant/components/vallox/binary_sensor.py b/homeassistant/components/vallox/binary_sensor.py index 348bad97158..762b63c0c1d 100644 --- a/homeassistant/components/vallox/binary_sensor.py +++ b/homeassistant/components/vallox/binary_sensor.py @@ -9,19 +9,18 @@ from homeassistant.components.binary_sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import DOMAIN -class ValloxBinarySensor( - CoordinatorEntity[ValloxDataUpdateCoordinator], BinarySensorEntity -): +class ValloxBinarySensor(ValloxEntity, BinarySensorEntity): """Representation of a Vallox binary sensor.""" entity_description: ValloxBinarySensorEntityDescription + _attr_entity_category = EntityCategory.DIAGNOSTIC def __init__( self, @@ -30,14 +29,12 @@ class ValloxBinarySensor( description: ValloxBinarySensorEntityDescription, ) -> None: """Initialize the Vallox binary sensor.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self.entity_description = description self._attr_name = f"{name} {description.name}" - - uuid = self.coordinator.data.get_uuid() - self._attr_unique_id = f"{uuid}-{description.key}" + self._attr_unique_id = f"{self._device_uuid}-{description.key}" @property def is_on(self) -> bool | None: diff --git a/homeassistant/components/vallox/fan.py b/homeassistant/components/vallox/fan.py index 85b3f501c85..6872acbb5b7 100644 --- a/homeassistant/components/vallox/fan.py +++ b/homeassistant/components/vallox/fan.py @@ -17,9 +17,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import ( DOMAIN, METRIC_KEY_MODE, @@ -80,7 +79,7 @@ async def async_setup_entry( async_add_entities([device]) -class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity): +class ValloxFan(ValloxEntity, FanEntity): """Representation of the fan.""" _attr_supported_features = FanEntityFeature.PRESET_MODE @@ -92,13 +91,12 @@ class ValloxFan(CoordinatorEntity[ValloxDataUpdateCoordinator], FanEntity): coordinator: ValloxDataUpdateCoordinator, ) -> None: """Initialize the fan.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self._client = client self._attr_name = name - - self._attr_unique_id = str(self.coordinator.data.get_uuid()) + self._attr_unique_id = str(self._device_uuid) @property def preset_modes(self) -> list[str]: diff --git a/homeassistant/components/vallox/sensor.py b/homeassistant/components/vallox/sensor.py index 92f0bc32e76..54a010c3e3d 100644 --- a/homeassistant/components/vallox/sensor.py +++ b/homeassistant/components/vallox/sensor.py @@ -17,12 +17,12 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt -from . import ValloxDataUpdateCoordinator +from . import ValloxDataUpdateCoordinator, ValloxEntity from .const import ( DOMAIN, METRIC_KEY_MODE, @@ -32,10 +32,11 @@ from .const import ( ) -class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity): +class ValloxSensor(ValloxEntity, SensorEntity): """Representation of a Vallox sensor.""" entity_description: ValloxSensorEntityDescription + _attr_entity_category = EntityCategory.DIAGNOSTIC def __init__( self, @@ -44,14 +45,12 @@ class ValloxSensor(CoordinatorEntity[ValloxDataUpdateCoordinator], SensorEntity) description: ValloxSensorEntityDescription, ) -> None: """Initialize the Vallox sensor.""" - super().__init__(coordinator) + super().__init__(name, coordinator) self.entity_description = description self._attr_name = f"{name} {description.name}" - - uuid = self.coordinator.data.get_uuid() - self._attr_unique_id = f"{uuid}-{description.key}" + self._attr_unique_id = f"{self._device_uuid}-{description.key}" @property def native_value(self) -> StateType | datetime: diff --git a/tests/components/vallox/conftest.py b/tests/components/vallox/conftest.py index e7ea6ee6d6e..ef9fd2a0e4b 100644 --- a/tests/components/vallox/conftest.py +++ b/tests/components/vallox/conftest.py @@ -50,10 +50,30 @@ def patch_profile_home(): @pytest.fixture(autouse=True) -def patch_uuid(): - """Patch the Vallox entity UUID.""" +def patch_model(): + """Patch the Vallox model response.""" with patch( - "homeassistant.components.vallox.calculate_uuid", + "homeassistant.components.vallox._api_get_model", + return_value="Vallox Testmodel", + ): + yield + + +@pytest.fixture(autouse=True) +def patch_sw_version(): + """Patch the Vallox SW version response.""" + with patch( + "homeassistant.components.vallox._api_get_sw_version", + return_value="0.1.2", + ): + yield + + +@pytest.fixture(autouse=True) +def patch_uuid(): + """Patch the Vallox UUID response.""" + with patch( + "homeassistant.components.vallox._api_get_uuid", return_value=_random_uuid(), ): yield diff --git a/tests/components/vallox/test_sensor.py b/tests/components/vallox/test_sensor.py index dea3f79fbd8..9c96f6f0a68 100644 --- a/tests/components/vallox/test_sensor.py +++ b/tests/components/vallox/test_sensor.py @@ -51,7 +51,7 @@ async def test_remaining_filter_returns_timestamp( """Test that the remaining time for filter sensor returns a timestamp.""" # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=dt.now().date(), ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) @@ -68,7 +68,7 @@ async def test_remaining_time_for_filter_none_returned_from_vallox( """Test that the remaining time for filter sensor returns 'unknown' when Vallox returns None.""" # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=None, ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) @@ -98,7 +98,7 @@ async def test_remaining_time_for_filter_in_the_future( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=mocked_filter_end_date, ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) @@ -122,7 +122,7 @@ async def test_remaining_time_for_filter_today( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=mocked_filter_end_date, ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) @@ -146,7 +146,7 @@ async def test_remaining_time_for_filter_in_the_past( # Act with patch( - "homeassistant.components.vallox.calculate_next_filter_change_date", + "homeassistant.components.vallox._api_get_next_filter_change_date", return_value=mocked_filter_end_date, ), patch_metrics(metrics={}): await hass.config_entries.async_setup(mock_entry.entry_id) From b731b7b69c64fff6f8ec89e2bf961f342d29a4ef Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 10 May 2022 00:03:03 +0200 Subject: [PATCH 329/930] Bump pydeconz to v92 (#71613) --- homeassistant/components/deconz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index 1ce3477db70..2a4a5ccf253 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -3,7 +3,7 @@ "name": "deCONZ", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/deconz", - "requirements": ["pydeconz==91"], + "requirements": ["pydeconz==92"], "ssdp": [ { "manufacturer": "Royal Philips Electronics", diff --git a/requirements_all.txt b/requirements_all.txt index 72c2d54e44c..c8ee7b0f29c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1432,7 +1432,7 @@ pydaikin==2.7.0 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==91 +pydeconz==92 # homeassistant.components.delijn pydelijn==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0f67c0adfa7..930a6ff4b29 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -953,7 +953,7 @@ pycoolmasternet-async==0.1.2 pydaikin==2.7.0 # homeassistant.components.deconz -pydeconz==91 +pydeconz==92 # homeassistant.components.dexcom pydexcom==0.2.3 From 7243f787f97bb6c6d2dece8e751721eba67b1735 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Tue, 10 May 2022 00:11:50 +0200 Subject: [PATCH 330/930] Bump nam backend library to version 1.2.4 (#71584) --- homeassistant/components/nam/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nam/manifest.json b/homeassistant/components/nam/manifest.json index 16231ef0b88..a842af46f84 100644 --- a/homeassistant/components/nam/manifest.json +++ b/homeassistant/components/nam/manifest.json @@ -3,7 +3,7 @@ "name": "Nettigo Air Monitor", "documentation": "https://www.home-assistant.io/integrations/nam", "codeowners": ["@bieniu"], - "requirements": ["nettigo-air-monitor==1.2.3"], + "requirements": ["nettigo-air-monitor==1.2.4"], "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/requirements_all.txt b/requirements_all.txt index c8ee7b0f29c..731e7e1ee2d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1068,7 +1068,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.2.3 +nettigo-air-monitor==1.2.4 # homeassistant.components.neurio_energy neurio==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 930a6ff4b29..ca41a5ca264 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -730,7 +730,7 @@ netdisco==3.0.0 netmap==0.7.0.2 # homeassistant.components.nam -nettigo-air-monitor==1.2.3 +nettigo-air-monitor==1.2.4 # homeassistant.components.nexia nexia==0.9.13 From 3de7ffde54103e51e92ea2d812e7ae972d0c1160 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 10 May 2022 00:23:19 +0000 Subject: [PATCH 331/930] [ci skip] Translation update --- .../components/airthings/translations/ko.json | 11 ++++++ .../binary_sensor/translations/ko.json | 3 ++ .../crownstone/translations/ko.json | 9 +++++ .../components/deconz/translations/ja.json | 1 + .../components/deluge/translations/it.json | 2 +- .../components/fan/translations/el.json | 1 + .../components/fan/translations/en.json | 1 + .../components/fan/translations/pt-BR.json | 1 + .../fjaraskupan/translations/ko.json | 7 ++++ .../components/fronius/translations/ko.json | 9 +++++ .../components/goodwe/translations/ko.json | 9 +++++ .../components/iotawatt/translations/ko.json | 15 +++++++ .../components/isy994/translations/ja.json | 7 ++++ .../components/isy994/translations/ru.json | 2 +- .../components/knx/translations/ko.json | 11 ++++++ .../components/meater/translations/ja.json | 5 +++ .../media_player/translations/ja.json | 3 ++ .../components/mill/translations/ko.json | 3 ++ .../components/nam/translations/ko.json | 12 ++++++ .../components/nanoleaf/translations/ko.json | 14 +++++++ .../components/nest/translations/ko.json | 4 ++ .../components/notion/translations/ko.json | 3 ++ .../components/onewire/translations/ca.json | 4 +- .../components/onewire/translations/de.json | 4 +- .../components/onewire/translations/el.json | 2 + .../components/onewire/translations/en.json | 4 +- .../components/onewire/translations/fr.json | 4 +- .../components/onewire/translations/hu.json | 2 + .../components/onewire/translations/id.json | 4 +- .../components/onewire/translations/it.json | 4 +- .../components/onewire/translations/ja.json | 2 + .../components/onewire/translations/nl.json | 4 +- .../components/onewire/translations/pl.json | 4 +- .../onewire/translations/pt-BR.json | 4 +- .../onewire/translations/zh-Hant.json | 4 +- .../opengarage/translations/ko.json | 11 ++++++ .../p1_monitor/translations/ko.json | 8 ++++ .../components/qnap_qsw/translations/ja.json | 13 +++++++ .../rainforest_eagle/translations/ko.json | 10 +++++ .../components/recorder/translations/id.json | 1 + .../components/recorder/translations/it.json | 1 + .../components/recorder/translations/nl.json | 1 + .../components/recorder/translations/no.json | 1 + .../components/recorder/translations/pl.json | 1 + .../simplisafe/translations/ru.json | 2 +- .../components/sms/translations/ca.json | 1 + .../components/sms/translations/de.json | 1 + .../components/sms/translations/el.json | 1 + .../components/sms/translations/en.json | 1 + .../components/sms/translations/et.json | 1 + .../components/sms/translations/fr.json | 1 + .../components/sms/translations/hu.json | 1 + .../components/sms/translations/id.json | 1 + .../components/sms/translations/it.json | 1 + .../components/sms/translations/ja.json | 1 + .../components/sms/translations/nl.json | 1 + .../components/sms/translations/no.json | 1 + .../components/sms/translations/pl.json | 1 + .../components/sms/translations/pt-BR.json | 1 + .../components/sms/translations/zh-Hant.json | 1 + .../components/sql/translations/ja.json | 39 +++++++++++++++++++ .../steam_online/translations/ja.json | 11 ++++++ .../steam_online/translations/nl.json | 4 +- .../steam_online/translations/ru.json | 4 +- .../components/tailscale/translations/ko.json | 14 +++++++ .../components/tautulli/translations/ja.json | 27 +++++++++++++ .../totalconnect/translations/ko.json | 1 + .../components/tplink/translations/ko.json | 3 ++ .../tractive/translations/sensor.ko.json | 9 +++++ .../trafikverket_ferry/translations/ja.json | 26 +++++++++++++ .../translations/ko.json | 15 +++++++ .../tuya/translations/select.ko.json | 12 ++++++ .../ukraine_alarm/translations/ca.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/et.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/id.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/it.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/ja.json | 35 +++++++++++++++++ .../ukraine_alarm/translations/nl.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/no.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/pl.json | 35 +++++++++++++++++ .../ukraine_alarm/translations/pt-BR.json | 39 +++++++++++++++++++ .../ukraine_alarm/translations/zh-Hant.json | 39 +++++++++++++++++++ .../components/unifi/translations/nl.json | 2 +- .../components/vulcan/translations/id.json | 9 +++++ .../components/vulcan/translations/ja.json | 7 ++++ .../components/wallbox/translations/ko.json | 7 ++++ .../components/ws66i/translations/ca.json | 34 ++++++++++++++++ .../components/ws66i/translations/de.json | 34 ++++++++++++++++ .../components/ws66i/translations/el.json | 34 ++++++++++++++++ .../components/ws66i/translations/et.json | 34 ++++++++++++++++ .../components/ws66i/translations/hu.json | 34 ++++++++++++++++ .../components/ws66i/translations/id.json | 34 ++++++++++++++++ .../components/ws66i/translations/it.json | 34 ++++++++++++++++ .../components/ws66i/translations/ja.json | 18 +++++++++ .../components/ws66i/translations/nl.json | 34 ++++++++++++++++ .../components/ws66i/translations/no.json | 34 ++++++++++++++++ .../components/ws66i/translations/pl.json | 34 ++++++++++++++++ .../components/ws66i/translations/pt-BR.json | 34 ++++++++++++++++ .../components/ws66i/translations/ru.json | 34 ++++++++++++++++ .../ws66i/translations/zh-Hant.json | 34 ++++++++++++++++ .../yale_smart_alarm/translations/ko.json | 11 ++++++ .../components/zwave_js/translations/ko.json | 7 ++++ 102 files changed, 1275 insertions(+), 18 deletions(-) create mode 100644 homeassistant/components/airthings/translations/ko.json create mode 100644 homeassistant/components/crownstone/translations/ko.json create mode 100644 homeassistant/components/fjaraskupan/translations/ko.json create mode 100644 homeassistant/components/fronius/translations/ko.json create mode 100644 homeassistant/components/goodwe/translations/ko.json create mode 100644 homeassistant/components/iotawatt/translations/ko.json create mode 100644 homeassistant/components/knx/translations/ko.json create mode 100644 homeassistant/components/nam/translations/ko.json create mode 100644 homeassistant/components/nanoleaf/translations/ko.json create mode 100644 homeassistant/components/opengarage/translations/ko.json create mode 100644 homeassistant/components/p1_monitor/translations/ko.json create mode 100644 homeassistant/components/qnap_qsw/translations/ja.json create mode 100644 homeassistant/components/rainforest_eagle/translations/ko.json create mode 100644 homeassistant/components/sql/translations/ja.json create mode 100644 homeassistant/components/steam_online/translations/ja.json create mode 100644 homeassistant/components/tailscale/translations/ko.json create mode 100644 homeassistant/components/tautulli/translations/ja.json create mode 100644 homeassistant/components/tractive/translations/sensor.ko.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/ja.json create mode 100644 homeassistant/components/trafikverket_weatherstation/translations/ko.json create mode 100644 homeassistant/components/tuya/translations/select.ko.json create mode 100644 homeassistant/components/ukraine_alarm/translations/ca.json create mode 100644 homeassistant/components/ukraine_alarm/translations/et.json create mode 100644 homeassistant/components/ukraine_alarm/translations/id.json create mode 100644 homeassistant/components/ukraine_alarm/translations/it.json create mode 100644 homeassistant/components/ukraine_alarm/translations/ja.json create mode 100644 homeassistant/components/ukraine_alarm/translations/nl.json create mode 100644 homeassistant/components/ukraine_alarm/translations/no.json create mode 100644 homeassistant/components/ukraine_alarm/translations/pl.json create mode 100644 homeassistant/components/ukraine_alarm/translations/pt-BR.json create mode 100644 homeassistant/components/ukraine_alarm/translations/zh-Hant.json create mode 100644 homeassistant/components/wallbox/translations/ko.json create mode 100644 homeassistant/components/ws66i/translations/ca.json create mode 100644 homeassistant/components/ws66i/translations/de.json create mode 100644 homeassistant/components/ws66i/translations/el.json create mode 100644 homeassistant/components/ws66i/translations/et.json create mode 100644 homeassistant/components/ws66i/translations/hu.json create mode 100644 homeassistant/components/ws66i/translations/id.json create mode 100644 homeassistant/components/ws66i/translations/it.json create mode 100644 homeassistant/components/ws66i/translations/ja.json create mode 100644 homeassistant/components/ws66i/translations/nl.json create mode 100644 homeassistant/components/ws66i/translations/no.json create mode 100644 homeassistant/components/ws66i/translations/pl.json create mode 100644 homeassistant/components/ws66i/translations/pt-BR.json create mode 100644 homeassistant/components/ws66i/translations/ru.json create mode 100644 homeassistant/components/ws66i/translations/zh-Hant.json create mode 100644 homeassistant/components/yale_smart_alarm/translations/ko.json diff --git a/homeassistant/components/airthings/translations/ko.json b/homeassistant/components/airthings/translations/ko.json new file mode 100644 index 00000000000..27863f2e0e4 --- /dev/null +++ b/homeassistant/components/airthings/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "secret": "\ubcf4\uc548\ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/ko.json b/homeassistant/components/binary_sensor/translations/ko.json index 7a725fc6719..3d26bdd0193 100644 --- a/homeassistant/components/binary_sensor/translations/ko.json +++ b/homeassistant/components/binary_sensor/translations/ko.json @@ -89,6 +89,9 @@ "vibration": "{entity_name}\uc774(\uac00) \uc9c4\ub3d9\uc744 \uac10\uc9c0\ud558\uae30 \uc2dc\uc791\ud588\uc744 \ub54c" } }, + "device_class": { + "co": "\uc77c\uc0b0\ud654\ud0c4\uc18c" + }, "state": { "_": { "off": "\uaebc\uc9d0", diff --git a/homeassistant/components/crownstone/translations/ko.json b/homeassistant/components/crownstone/translations/ko.json new file mode 100644 index 00000000000..22a5729e256 --- /dev/null +++ b/homeassistant/components/crownstone/translations/ko.json @@ -0,0 +1,9 @@ +{ + "options": { + "step": { + "usb_sphere_config": { + "title": "\ud06c\ub77c\uc6b4\uc2a4\ud1a4 USB \uc2a4\ud53c\uc5b4" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/translations/ja.json b/homeassistant/components/deconz/translations/ja.json index a074be8d8cd..ce1cab4dea6 100644 --- a/homeassistant/components/deconz/translations/ja.json +++ b/homeassistant/components/deconz/translations/ja.json @@ -9,6 +9,7 @@ "updated_instance": "\u65b0\u3057\u3044\u30db\u30b9\u30c8\u30a2\u30c9\u30ec\u30b9\u3067deCONZ\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f" }, "error": { + "linking_not_possible": "\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3068\u30ea\u30f3\u30af\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f", "no_key": "API\u30ad\u30fc\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f" }, "flow_title": "{host}", diff --git a/homeassistant/components/deluge/translations/it.json b/homeassistant/components/deluge/translations/it.json index 52cfb4a09ae..a7b03fc75ed 100644 --- a/homeassistant/components/deluge/translations/it.json +++ b/homeassistant/components/deluge/translations/it.json @@ -16,7 +16,7 @@ "username": "Nome utente", "web_port": "Porta web (per il servizio di visita)" }, - "description": "Per poter utilizzare questa integrazione, devi abilitare la seguente opzione nelle impostazioni di diluvio: Demone > Consenti controlli remoti" + "description": "Per poter utilizzare questa integrazione, devi abilitare la seguente opzione nelle impostazioni di diluvio: Daemon > Consenti controlli remoti" } } } diff --git a/homeassistant/components/fan/translations/el.json b/homeassistant/components/fan/translations/el.json index b9c4962a7a6..b0ff671f737 100644 --- a/homeassistant/components/fan/translations/el.json +++ b/homeassistant/components/fan/translations/el.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "\u0395\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae {entity_name}", "turn_off": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 {entity_name}", "turn_on": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 {entity_name}" }, diff --git a/homeassistant/components/fan/translations/en.json b/homeassistant/components/fan/translations/en.json index f4f27e8fd2d..55feb3dad9f 100644 --- a/homeassistant/components/fan/translations/en.json +++ b/homeassistant/components/fan/translations/en.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Toggle {entity_name}", "turn_off": "Turn off {entity_name}", "turn_on": "Turn on {entity_name}" }, diff --git a/homeassistant/components/fan/translations/pt-BR.json b/homeassistant/components/fan/translations/pt-BR.json index 97145dd8a60..14eafd36f40 100644 --- a/homeassistant/components/fan/translations/pt-BR.json +++ b/homeassistant/components/fan/translations/pt-BR.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Alternar {entity_name}", "turn_off": "Desligar {entity_name}", "turn_on": "Ligar {entity_name}" }, diff --git a/homeassistant/components/fjaraskupan/translations/ko.json b/homeassistant/components/fjaraskupan/translations/ko.json new file mode 100644 index 00000000000..0a4087d0200 --- /dev/null +++ b/homeassistant/components/fjaraskupan/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c \uc548\uc5d0 \ubc1c\uacac\ub41c \ub514\ubc14\uc774\uc2a4 \uc5c6\uc74c" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fronius/translations/ko.json b/homeassistant/components/fronius/translations/ko.json new file mode 100644 index 00000000000..5e3238d1be1 --- /dev/null +++ b/homeassistant/components/fronius/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm_discovery": { + "description": "{device} \ub97c \ud648\uc5b4\uc2dc\uc2a4\ud134\ud2b8\uc5d0 \ucd94\uac00\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/goodwe/translations/ko.json b/homeassistant/components/goodwe/translations/ko.json new file mode 100644 index 00000000000..9aaa9eacf20 --- /dev/null +++ b/homeassistant/components/goodwe/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc778\ubc84\ud130\uc5d0 \uc5f0\uacb0" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/iotawatt/translations/ko.json b/homeassistant/components/iotawatt/translations/ko.json new file mode 100644 index 00000000000..f3fac4e1e74 --- /dev/null +++ b/homeassistant/components/iotawatt/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + }, + "step": { + "auth": { + "data": { + "password": "\uc554\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/isy994/translations/ja.json b/homeassistant/components/isy994/translations/ja.json index 271cba9e0c4..51c56f035d7 100644 --- a/homeassistant/components/isy994/translations/ja.json +++ b/homeassistant/components/isy994/translations/ja.json @@ -7,10 +7,17 @@ "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "invalid_host": "\u30db\u30b9\u30c8\u30a8\u30f3\u30c8\u30ea\u306f\u304d\u3061\u3093\u3068\u3057\u305fURL\u5f62\u5f0f\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f \u4f8b: http://192.168.10.100:80", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + } + }, "user": { "data": { "host": "URL", diff --git a/homeassistant/components/isy994/translations/ru.json b/homeassistant/components/isy994/translations/ru.json index 61e2c79831a..ea7d5c6e36c 100644 --- a/homeassistant/components/isy994/translations/ru.json +++ b/homeassistant/components/isy994/translations/ru.json @@ -42,7 +42,7 @@ "variable_sensor_string": "\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440" }, "description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:\n \u2022 \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0437\u0435\u043b \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440: \u043b\u044e\u0431\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0438\u043b\u0438 \u043f\u0430\u043f\u043a\u0430, \u0432 \u0438\u043c\u0435\u043d\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430, \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440 \u0438\u043b\u0438 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440.\n \u2022 \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440: \u043b\u044e\u0431\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443, \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u043a\u0430\u043a \u0441\u0435\u043d\u0441\u043e\u0440.\n \u2022 \u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c: \u043b\u044e\u0431\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u0432 \u0438\u043c\u0435\u043d\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430, \u0431\u0443\u0434\u0435\u0442 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f.\n \u2022 \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u044f\u0440\u043a\u043e\u0441\u0442\u044c \u0441\u0432\u0435\u0442\u0430: \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u044f\u0440\u043a\u043e\u0441\u0442\u0438, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u0434\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ISY994" + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ISY" } } }, diff --git a/homeassistant/components/knx/translations/ko.json b/homeassistant/components/knx/translations/ko.json new file mode 100644 index 00000000000..676037b15e4 --- /dev/null +++ b/homeassistant/components/knx/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "manual_tunnel": { + "data": { + "route_back": "\ub77c\uc6b0\ud2b8 \ubc31 / NAT \ubaa8\ub4dc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ja.json b/homeassistant/components/meater/translations/ja.json index db4dd2e9c6b..e24fc9199cc 100644 --- a/homeassistant/components/meater/translations/ja.json +++ b/homeassistant/components/meater/translations/ja.json @@ -6,6 +6,11 @@ "unknown_auth_error": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { + "reauth_confirm": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" + } + }, "user": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", diff --git a/homeassistant/components/media_player/translations/ja.json b/homeassistant/components/media_player/translations/ja.json index c553308a3e7..a11bc516b38 100644 --- a/homeassistant/components/media_player/translations/ja.json +++ b/homeassistant/components/media_player/translations/ja.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} \u306f\u3001\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u4e2d\u3067\u3059", "is_idle": "{entity_name} \u306f\u3001\u30a2\u30a4\u30c9\u30eb\u72b6\u614b\u3067\u3059", "is_off": "{entity_name} \u306f\u30aa\u30d5\u3067\u3059", "is_on": "{entity_name} \u304c\u30aa\u30f3\u3067\u3059", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} \u304c\u518d\u751f\u3055\u308c\u3066\u3044\u307e\u3059" }, "trigger_type": { + "buffering": "{entity_name} \u306e\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u958b\u59cb\u3057\u307e\u3059", "changed_states": "{entity_name} \u306e\u72b6\u614b\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f", "idle": "{entity_name} \u304c\u30a2\u30a4\u30c9\u30eb\u72b6\u614b\u306b\u306a\u308a\u307e\u3059", "paused": "{entity_name} \u306f\u3001\u4e00\u6642\u505c\u6b62\u3057\u3066\u3044\u307e\u3059", @@ -18,6 +20,7 @@ }, "state": { "_": { + "buffering": "\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0", "idle": "\u30a2\u30a4\u30c9\u30eb", "off": "\u30aa\u30d5", "on": "\u30aa\u30f3", diff --git a/homeassistant/components/mill/translations/ko.json b/homeassistant/components/mill/translations/ko.json index 48c8cdc6eaa..1041727d84a 100644 --- a/homeassistant/components/mill/translations/ko.json +++ b/homeassistant/components/mill/translations/ko.json @@ -7,6 +7,9 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "local": { + "description": "\ub85c\uceec IP \uc8fc\uc18c" + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/nam/translations/ko.json b/homeassistant/components/nam/translations/ko.json new file mode 100644 index 00000000000..a4cc4c01810 --- /dev/null +++ b/homeassistant/components/nam/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "abort": { + "reauth_unsuccessful": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uad6c\uc131\uc744 \uc81c\uac70\ud558\uace0 \ub2e4\uc2dc \uc124\uc815\ud558\uc2ed\uc2dc\uc624." + }, + "step": { + "reauth_confirm": { + "description": "\ud638\uc2a4\ud2b8\uc5d0 \ub300\ud55c \uc62c\ubc14\ub978 \uc0ac\uc6a9\uc790 \uc774\ub984\uacfc \uc554\ud638\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624: {host}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/ko.json b/homeassistant/components/nanoleaf/translations/ko.json new file mode 100644 index 00000000000..e540d903647 --- /dev/null +++ b/homeassistant/components/nanoleaf/translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "\ub514\ubc14\uc774\uc2a4\uac00 \uc774\ubbf8 \uc124\uc815\ub418\uc5b4 \uc788\uc74c", + "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", + "invalid_token": "\uc798\ubabb\ub41c \uc811\uc18d \ud1a0\ud070", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/nest/translations/ko.json b/homeassistant/components/nest/translations/ko.json index 048a0c7d100..1b4563c800f 100644 --- a/homeassistant/components/nest/translations/ko.json +++ b/homeassistant/components/nest/translations/ko.json @@ -35,6 +35,10 @@ "pick_implementation": { "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" }, + "pubsub": { + "description": "[Cloud Console]( {url} )\uc744 \ubc29\ubb38\ud558\uc5ec Google Cloud \ud504\ub85c\uc81d\ud2b8 ID\ub97c \ucc3e\uc73c\uc138\uc694.", + "title": "\uad6c\uae00 \ud074\ub77c\uc6b0\ub4dc \uad6c\uc131" + }, "reauth_confirm": { "description": "Nest \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4", "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" diff --git a/homeassistant/components/notion/translations/ko.json b/homeassistant/components/notion/translations/ko.json index b5c7cadbe9b..c81805446f9 100644 --- a/homeassistant/components/notion/translations/ko.json +++ b/homeassistant/components/notion/translations/ko.json @@ -8,6 +8,9 @@ "no_devices": "\uacc4\uc815\uc5d0 \ub4f1\ub85d\ub41c \uae30\uae30\uac00 \uc874\uc7ac\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\ub97c \ub2e4\uc2dc \uc785\ub825\ud558\uc138\uc694." + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/onewire/translations/ca.json b/homeassistant/components/onewire/translations/ca.json index b50e3abc1f6..a8f4da41b6b 100644 --- a/homeassistant/components/onewire/translations/ca.json +++ b/homeassistant/components/onewire/translations/ca.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Amfitri\u00f3", + "port": "Port", "type": "Tipus de connexi\u00f3" }, - "title": "Configuraci\u00f3 d'1-Wire" + "title": "Detalls del servidor" } } }, diff --git a/homeassistant/components/onewire/translations/de.json b/homeassistant/components/onewire/translations/de.json index a6347b12d75..14f321c8004 100644 --- a/homeassistant/components/onewire/translations/de.json +++ b/homeassistant/components/onewire/translations/de.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Port", "type": "Verbindungstyp" }, - "title": "1-Wire einrichten" + "title": "Serverdetails festlegen" } } }, diff --git a/homeassistant/components/onewire/translations/el.json b/homeassistant/components/onewire/translations/el.json index 3d358decfe5..b0d37d679bc 100644 --- a/homeassistant/components/onewire/translations/el.json +++ b/homeassistant/components/onewire/translations/el.json @@ -17,6 +17,8 @@ }, "user": { "data": { + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", + "port": "\u0398\u03cd\u03c1\u03b1", "type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 1-Wire" diff --git a/homeassistant/components/onewire/translations/en.json b/homeassistant/components/onewire/translations/en.json index df61b436f65..d8e2c157e0f 100644 --- a/homeassistant/components/onewire/translations/en.json +++ b/homeassistant/components/onewire/translations/en.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Port", "type": "Connection type" }, - "title": "Set up 1-Wire" + "title": "Set server details" } } }, diff --git a/homeassistant/components/onewire/translations/fr.json b/homeassistant/components/onewire/translations/fr.json index 08e3d3cf18b..3d9e01916c3 100644 --- a/homeassistant/components/onewire/translations/fr.json +++ b/homeassistant/components/onewire/translations/fr.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "H\u00f4te", + "port": "Port", "type": "Type de connexion" }, - "title": "Configurer 1-Wire" + "title": "Renseigner les informations du serveur" } } }, diff --git a/homeassistant/components/onewire/translations/hu.json b/homeassistant/components/onewire/translations/hu.json index 7034f2eaa29..e26721758cd 100644 --- a/homeassistant/components/onewire/translations/hu.json +++ b/homeassistant/components/onewire/translations/hu.json @@ -17,6 +17,8 @@ }, "user": { "data": { + "host": "C\u00edm", + "port": "Port", "type": "Kapcsolat t\u00edpusa" }, "title": "A 1-Wire be\u00e1ll\u00edt\u00e1sa" diff --git a/homeassistant/components/onewire/translations/id.json b/homeassistant/components/onewire/translations/id.json index a1cde1b35bd..203a38d1a4c 100644 --- a/homeassistant/components/onewire/translations/id.json +++ b/homeassistant/components/onewire/translations/id.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Port", "type": "Jenis koneksi" }, - "title": "Siapkan 1-Wire" + "title": "Setel detail server" } } }, diff --git a/homeassistant/components/onewire/translations/it.json b/homeassistant/components/onewire/translations/it.json index ec2edddc275..77f64381ee7 100644 --- a/homeassistant/components/onewire/translations/it.json +++ b/homeassistant/components/onewire/translations/it.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Porta", "type": "Tipo di connessione" }, - "title": "Configurazione 1-Wire" + "title": "Imposta i dettagli del server" } } }, diff --git a/homeassistant/components/onewire/translations/ja.json b/homeassistant/components/onewire/translations/ja.json index c948a9567b0..b4acd54cd41 100644 --- a/homeassistant/components/onewire/translations/ja.json +++ b/homeassistant/components/onewire/translations/ja.json @@ -17,6 +17,8 @@ }, "user": { "data": { + "host": "\u30db\u30b9\u30c8", + "port": "\u30dd\u30fc\u30c8", "type": "\u63a5\u7d9a\u30bf\u30a4\u30d7" }, "title": "1-Wire\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" diff --git a/homeassistant/components/onewire/translations/nl.json b/homeassistant/components/onewire/translations/nl.json index 3ea1fd8e13c..e6af2cebef3 100644 --- a/homeassistant/components/onewire/translations/nl.json +++ b/homeassistant/components/onewire/translations/nl.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Poort", "type": "Verbindingstype" }, - "title": "Stel 1-Wire in" + "title": "Serverdetails instellen" } } }, diff --git a/homeassistant/components/onewire/translations/pl.json b/homeassistant/components/onewire/translations/pl.json index fe0e1d54eb5..743a1b28522 100644 --- a/homeassistant/components/onewire/translations/pl.json +++ b/homeassistant/components/onewire/translations/pl.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Nazwa hosta lub adres IP", + "port": "Port", "type": "Rodzaj po\u0142\u0105czenia" }, - "title": "Konfiguracja 1-Wire" + "title": "Konfiguracja serwera" } } }, diff --git a/homeassistant/components/onewire/translations/pt-BR.json b/homeassistant/components/onewire/translations/pt-BR.json index 61405f5089a..74fe2918222 100644 --- a/homeassistant/components/onewire/translations/pt-BR.json +++ b/homeassistant/components/onewire/translations/pt-BR.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Porta", "type": "Tipo de conex\u00e3o" }, - "title": "Configurar 1-Wire" + "title": "Definir detalhes do servidor" } } }, diff --git a/homeassistant/components/onewire/translations/zh-Hant.json b/homeassistant/components/onewire/translations/zh-Hant.json index 8b11692a3f5..a77b86ea416 100644 --- a/homeassistant/components/onewire/translations/zh-Hant.json +++ b/homeassistant/components/onewire/translations/zh-Hant.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "\u4e3b\u6a5f\u7aef", + "port": "\u901a\u8a0a\u57e0", "type": "\u9023\u7dda\u985e\u5225" }, - "title": "\u8a2d\u5b9a 1-Wire" + "title": "\u8a2d\u5b9a\u4f3a\u670d\u5668\u8a73\u7d30\u8cc7\u8a0a" } } }, diff --git a/homeassistant/components/opengarage/translations/ko.json b/homeassistant/components/opengarage/translations/ko.json new file mode 100644 index 00000000000..b297ac33af6 --- /dev/null +++ b/homeassistant/components/opengarage/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "device_key": "\uc7a5\uce58 \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/p1_monitor/translations/ko.json b/homeassistant/components/p1_monitor/translations/ko.json new file mode 100644 index 00000000000..40f9f3b7152 --- /dev/null +++ b/homeassistant/components/p1_monitor/translations/ko.json @@ -0,0 +1,8 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/qnap_qsw/translations/ja.json b/homeassistant/components/qnap_qsw/translations/ja.json new file mode 100644 index 00000000000..38e13174ac8 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/ja.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "url": "URL", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rainforest_eagle/translations/ko.json b/homeassistant/components/rainforest_eagle/translations/ko.json new file mode 100644 index 00000000000..2da0f2c8ee1 --- /dev/null +++ b/homeassistant/components/rainforest_eagle/translations/ko.json @@ -0,0 +1,10 @@ +{ + "config": { + "abort": { + "already_configured": "\ub514\ubc14\uc774\uc2a4\uac00 \uc774\ubbf8 \uc124\uc815\ub418\uc5b4 \uc788\uc74c" + }, + "error": { + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/id.json b/homeassistant/components/recorder/translations/id.json index 9d3b4c333ef..ec9e87b39ab 100644 --- a/homeassistant/components/recorder/translations/id.json +++ b/homeassistant/components/recorder/translations/id.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Waktu Mulai Jalankan Saat Ini", + "estimated_db_size": "Perkiraan Ukuran Basis Data (MiB)", "oldest_recorder_run": "Waktu Mulai Lari Terlama" } } diff --git a/homeassistant/components/recorder/translations/it.json b/homeassistant/components/recorder/translations/it.json index 82f456981a8..ab0ac13772a 100644 --- a/homeassistant/components/recorder/translations/it.json +++ b/homeassistant/components/recorder/translations/it.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Ora di inizio esecuzione corrente", + "estimated_db_size": "Dimensione stimata del database (MiB)", "oldest_recorder_run": "Ora di inizio esecuzione meno recente" } } diff --git a/homeassistant/components/recorder/translations/nl.json b/homeassistant/components/recorder/translations/nl.json index 5bffcef1bd2..bb28428cc48 100644 --- a/homeassistant/components/recorder/translations/nl.json +++ b/homeassistant/components/recorder/translations/nl.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Starttijd huidige run", + "estimated_db_size": "Geschatte databasegrootte (MiB)", "oldest_recorder_run": "Starttijd oudste run" } } diff --git a/homeassistant/components/recorder/translations/no.json b/homeassistant/components/recorder/translations/no.json index b1474c13466..982354a6e34 100644 --- a/homeassistant/components/recorder/translations/no.json +++ b/homeassistant/components/recorder/translations/no.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Gjeldende starttid for kj\u00f8ring", + "estimated_db_size": "Estimert databasest\u00f8rrelse (MiB)", "oldest_recorder_run": "Eldste Run Start Time" } } diff --git a/homeassistant/components/recorder/translations/pl.json b/homeassistant/components/recorder/translations/pl.json index 1a31b3f7ec5..a91f92207f2 100644 --- a/homeassistant/components/recorder/translations/pl.json +++ b/homeassistant/components/recorder/translations/pl.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Pocz\u0105tek aktualnej sesji rejestratora", + "estimated_db_size": "Szacowany rozmiar bazy danych (MiB)", "oldest_recorder_run": "Pocz\u0105tek najstarszej sesji rejestratora" } } diff --git a/homeassistant/components/simplisafe/translations/ru.json b/homeassistant/components/simplisafe/translations/ru.json index 1788a9ca206..0d2a36085e4 100644 --- a/homeassistant/components/simplisafe/translations/ru.json +++ b/homeassistant/components/simplisafe/translations/ru.json @@ -14,7 +14,7 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "progress": { - "email_2fa": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0412\u0430\u043c \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435." + "email_2fa": "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0441\u0432\u043e\u044e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443 \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043e\u0442 Simplisafe." }, "step": { "mfa": { diff --git a/homeassistant/components/sms/translations/ca.json b/homeassistant/components/sms/translations/ca.json index f7640befed4..f6491d0dd46 100644 --- a/homeassistant/components/sms/translations/ca.json +++ b/homeassistant/components/sms/translations/ca.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Velocitat en Baudis", "device": "Dispositiu" }, "title": "Connexi\u00f3 al m\u00f2dem" diff --git a/homeassistant/components/sms/translations/de.json b/homeassistant/components/sms/translations/de.json index 3c5cc3c0490..f17e30789c2 100644 --- a/homeassistant/components/sms/translations/de.json +++ b/homeassistant/components/sms/translations/de.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Baud-Geschwindigkeit", "device": "Ger\u00e4t" }, "title": "Verbinden mit dem Modem" diff --git a/homeassistant/components/sms/translations/el.json b/homeassistant/components/sms/translations/el.json index 372cdfed20f..6484f847523 100644 --- a/homeassistant/components/sms/translations/el.json +++ b/homeassistant/components/sms/translations/el.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "\u03a4\u03b1\u03c7\u03cd\u03c4\u03b7\u03c4\u03b1 Baud", "device": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" }, "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc" diff --git a/homeassistant/components/sms/translations/en.json b/homeassistant/components/sms/translations/en.json index dbbac1871c7..12d4e82c648 100644 --- a/homeassistant/components/sms/translations/en.json +++ b/homeassistant/components/sms/translations/en.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Baud Speed", "device": "Device" }, "title": "Connect to the modem" diff --git a/homeassistant/components/sms/translations/et.json b/homeassistant/components/sms/translations/et.json index 70a378d591e..e3f67f1ac1a 100644 --- a/homeassistant/components/sms/translations/et.json +++ b/homeassistant/components/sms/translations/et.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Pordi kiirus", "device": "Seade" }, "title": "Modemiga \u00fchenduse loomine" diff --git a/homeassistant/components/sms/translations/fr.json b/homeassistant/components/sms/translations/fr.json index ebfa3c1da08..3caec67daaa 100644 --- a/homeassistant/components/sms/translations/fr.json +++ b/homeassistant/components/sms/translations/fr.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Vitesse en bauds", "device": "Appareil" }, "title": "Se connecter au modem" diff --git a/homeassistant/components/sms/translations/hu.json b/homeassistant/components/sms/translations/hu.json index 6fa524b18ab..3ebf7fbd948 100644 --- a/homeassistant/components/sms/translations/hu.json +++ b/homeassistant/components/sms/translations/hu.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Baud sebess\u00e9g", "device": "Eszk\u00f6z" }, "title": "Csatlakoz\u00e1s a modemhez" diff --git a/homeassistant/components/sms/translations/id.json b/homeassistant/components/sms/translations/id.json index 63ebb088521..d2b190eb3d0 100644 --- a/homeassistant/components/sms/translations/id.json +++ b/homeassistant/components/sms/translations/id.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Kecepatan Baud", "device": "Perangkat" }, "title": "Hubungkan ke modem" diff --git a/homeassistant/components/sms/translations/it.json b/homeassistant/components/sms/translations/it.json index 9d2ac87d833..48524b72f24 100644 --- a/homeassistant/components/sms/translations/it.json +++ b/homeassistant/components/sms/translations/it.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Velocit\u00e0 Baud", "device": "Dispositivo" }, "title": "Connettiti al modem" diff --git a/homeassistant/components/sms/translations/ja.json b/homeassistant/components/sms/translations/ja.json index 248427ad9bf..ddfb644d90f 100644 --- a/homeassistant/components/sms/translations/ja.json +++ b/homeassistant/components/sms/translations/ja.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "\u30dc\u30fc\u30ec\u30fc\u30c8", "device": "\u30c7\u30d0\u30a4\u30b9" }, "title": "\u30e2\u30c7\u30e0\u306b\u63a5\u7d9a" diff --git a/homeassistant/components/sms/translations/nl.json b/homeassistant/components/sms/translations/nl.json index ddcc54d239f..7c2bcb0568c 100644 --- a/homeassistant/components/sms/translations/nl.json +++ b/homeassistant/components/sms/translations/nl.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Baud snelheid", "device": "Apparaat" }, "title": "Maak verbinding met de modem" diff --git a/homeassistant/components/sms/translations/no.json b/homeassistant/components/sms/translations/no.json index ab692ff8fa6..e8abd1ab220 100644 --- a/homeassistant/components/sms/translations/no.json +++ b/homeassistant/components/sms/translations/no.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Overf\u00f8ringshastighet", "device": "Enhet" }, "title": "Koble til modemet" diff --git a/homeassistant/components/sms/translations/pl.json b/homeassistant/components/sms/translations/pl.json index 615f08e3e06..c3aae889b7e 100644 --- a/homeassistant/components/sms/translations/pl.json +++ b/homeassistant/components/sms/translations/pl.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Szybko\u015b\u0107 transmisji (Baud)", "device": "Urz\u0105dzenie" }, "title": "Po\u0142\u0105czenie z modemem" diff --git a/homeassistant/components/sms/translations/pt-BR.json b/homeassistant/components/sms/translations/pt-BR.json index 05e211760f2..708d64e33de 100644 --- a/homeassistant/components/sms/translations/pt-BR.json +++ b/homeassistant/components/sms/translations/pt-BR.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Velocidade de transmiss\u00e3o", "device": "Dispositivo" }, "title": "Conectado ao modem" diff --git a/homeassistant/components/sms/translations/zh-Hant.json b/homeassistant/components/sms/translations/zh-Hant.json index b6e08ffa7ec..0b84e7910f7 100644 --- a/homeassistant/components/sms/translations/zh-Hant.json +++ b/homeassistant/components/sms/translations/zh-Hant.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "\u9b91\u7387", "device": "\u88dd\u7f6e" }, "title": "\u9023\u7dda\u81f3\u6578\u64da\u6a5f" diff --git a/homeassistant/components/sql/translations/ja.json b/homeassistant/components/sql/translations/ja.json new file mode 100644 index 00000000000..cf03616fbb9 --- /dev/null +++ b/homeassistant/components/sql/translations/ja.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "db_url_invalid": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL\u304c\u7121\u52b9\u3067\u3059", + "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059", + "value_template_invalid": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u7121\u52b9\u3067\u3059" + }, + "step": { + "user": { + "data": { + "column": "\u30b3\u30e9\u30e0", + "db_url": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL", + "name": "\u540d\u524d", + "query": "\u30af\u30a8\u30ea\u3092\u9078\u629e", + "unit_of_measurement": "\u6e2c\u5b9a\u5358\u4f4d", + "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8" + }, + "data_description": { + "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\u540d\u524d" + }, + "data_description": { + "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/ja.json b/homeassistant/components/steam_online/translations/ja.json new file mode 100644 index 00000000000..07c813bb0b1 --- /dev/null +++ b/homeassistant/components/steam_online/translations/ja.json @@ -0,0 +1,11 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json index 20600463f6a..d9281038e76 100644 --- a/homeassistant/components/steam_online/translations/nl.json +++ b/homeassistant/components/steam_online/translations/nl.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "De Steam-integratie moet handmatig opnieuw worden geauthenticeerd.\n\nU vind uw API-sleutel hier: https://steamcommunity.com/dev/apikey", + "description": "De Steam integratie moet handmatig opnieuw geauthenticeerd worden\n\nU kunt uw sleutel hier vinden: {api_key_url}", "title": "Verifieer de integratie opnieuw" }, "user": { @@ -20,7 +20,7 @@ "account": "Steam Account-ID", "api_key": "API-sleutel" }, - "description": "Gebruik https://steamid.io om je Steam Account-ID te vinden." + "description": "Gebruik {account_id_url} om uw Steam account ID te vinden" } } }, diff --git a/homeassistant/components/steam_online/translations/ru.json b/homeassistant/components/steam_online/translations/ru.json index 8a6ea715455..0da65eda473 100644 --- a/homeassistant/components/steam_online/translations/ru.json +++ b/homeassistant/components/steam_online/translations/ru.json @@ -12,7 +12,7 @@ }, "step": { "reauth_confirm": { - "description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0441\u0432\u043e\u0439 \u043a\u043b\u044e\u0447 \u0437\u0434\u0435\u0441\u044c: https://steamcommunity.com/dev/apikey", + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f. \n\n\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u0441\u0432\u043e\u0439 \u043a\u043b\u044e\u0447 \u0437\u0434\u0435\u0441\u044c: {api_key_url}", "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" }, "user": { @@ -20,7 +20,7 @@ "account": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Steam", "api_key": "\u041a\u043b\u044e\u0447 API" }, - "description": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 https://steamid.io, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0432\u043e\u0435\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Steam." + "description": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 {account_id_url}, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0439\u0442\u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0432\u043e\u0435\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 Steam." } } }, diff --git a/homeassistant/components/tailscale/translations/ko.json b/homeassistant/components/tailscale/translations/ko.json new file mode 100644 index 00000000000..cd273d60ca0 --- /dev/null +++ b/homeassistant/components/tailscale/translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "description": "Tailscale API \ud1a0\ud070\uc740 90\uc77c \ub3d9\uc548 \uc720\ud6a8\ud569\ub2c8\ub2e4. https://login.tailscale.com/admin/settings/authkeys\uc5d0\uc11c \uc0c8\ub85c\uc6b4 Tailscale API \ud0a4\ub97c \uc0dd\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + }, + "user": { + "data": { + "tailnet": "\ud14c\uc77c\ub137" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/ja.json b/homeassistant/components/tautulli/translations/ja.json new file mode 100644 index 00000000000..854dc593a55 --- /dev/null +++ b/homeassistant/components/tautulli/translations/ja.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API\u30ad\u30fc" + } + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "url": "URL", + "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/totalconnect/translations/ko.json b/homeassistant/components/totalconnect/translations/ko.json index 354522154b5..246b62d5500 100644 --- a/homeassistant/components/totalconnect/translations/ko.json +++ b/homeassistant/components/totalconnect/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "no_locations": "\uc774 \uc0ac\uc6a9\uc790\uac00 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \uc704\uce58\uac00 \uc5c6\uc2b5\ub2c8\ub2e4. TotalConnect \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694.", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { diff --git a/homeassistant/components/tplink/translations/ko.json b/homeassistant/components/tplink/translations/ko.json index a1bfb59ca07..01ae48031e4 100644 --- a/homeassistant/components/tplink/translations/ko.json +++ b/homeassistant/components/tplink/translations/ko.json @@ -7,6 +7,9 @@ "step": { "confirm": { "description": "TP-Link \uc2a4\ub9c8\ud2b8 \uae30\uae30\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "discovery_confirm": { + "description": "{name} {model} ( {host} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" } } } diff --git a/homeassistant/components/tractive/translations/sensor.ko.json b/homeassistant/components/tractive/translations/sensor.ko.json new file mode 100644 index 00000000000..459ec08bec5 --- /dev/null +++ b/homeassistant/components/tractive/translations/sensor.ko.json @@ -0,0 +1,9 @@ +{ + "state": { + "tractive__tracker_state": { + "operational": "\uc6b4\uc601", + "system_shutdown_user": "\uc2dc\uc2a4\ud15c \uc885\ub8cc \uc0ac\uc6a9\uc790", + "system_startup": "\uc2dc\uc2a4\ud15c \uc2dc\uc791" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_ferry/translations/ja.json b/homeassistant/components/trafikverket_ferry/translations/ja.json new file mode 100644 index 00000000000..2377e33a60c --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/ja.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API\u30ad\u30fc" + } + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "time": "\u6642\u9593", + "weekday": "\u5e73\u65e5" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_weatherstation/translations/ko.json b/homeassistant/components/trafikverket_weatherstation/translations/ko.json new file mode 100644 index 00000000000..97d9717113c --- /dev/null +++ b/homeassistant/components/trafikverket_weatherstation/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "invalid_station": "\uc9c0\uc815\ub41c \uc774\ub984\uc758 \uae30\uc0c1 \uad00\uce21\uc18c\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", + "more_stations": "\uc9c0\uc815\ub41c \uc774\ub984\uc744 \uac00\uc9c4 \uc5ec\ub7ec \uae30\uc0c1 \uad00\uce21\uc18c\ub97c \ucc3e\uc558\uc2b5\ub2c8\ub2e4." + }, + "step": { + "user": { + "data": { + "station": "\uc2a4\ud14c\uc774\uc158" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/select.ko.json b/homeassistant/components/tuya/translations/select.ko.json new file mode 100644 index 00000000000..022505e76e5 --- /dev/null +++ b/homeassistant/components/tuya/translations/select.ko.json @@ -0,0 +1,12 @@ +{ + "state": { + "tuya__basic_anti_flickr": { + "1": "50Hz", + "2": "60Hz" + }, + "tuya__fingerbot_mode": { + "click": "\ud478\uc2dc", + "switch": "\uc2a4\uc704\uce58" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/ca.json b/homeassistant/components/ukraine_alarm/translations/ca.json new file mode 100644 index 00000000000..bee49c8e4a1 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/ca.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_api_key": "Clau API inv\u00e0lida", + "timeout": "Temps m\u00e0xim d'espera per establir la connexi\u00f3 esgotat", + "unknown": "Error inesperat" + }, + "step": { + "community": { + "data": { + "region": "Regi\u00f3" + }, + "description": "Si vols monitorar no nom\u00e9s l'estat i el districte, tria la comunitat espec\u00edfica" + }, + "district": { + "data": { + "region": "Regi\u00f3" + }, + "description": "Si vols monitorar no nom\u00e9s l'estat, tria el districte espec\u00edfic" + }, + "state": { + "data": { + "region": "Regi\u00f3" + }, + "description": "Escull l'estat a monitorar" + }, + "user": { + "data": { + "api_key": "Clau API" + }, + "description": "Configura la integraci\u00f3 d'Alarma Ucraina. Per generar la clau API, v\u00e9s a {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/et.json b/homeassistant/components/ukraine_alarm/translations/et.json new file mode 100644 index 00000000000..45649336837 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/et.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "Asukoht on juba m\u00e4\u00e4ratud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_api_key": "Vigane API v\u00f5ti", + "timeout": "\u00dchendamise ajal\u00f5pp", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "community": { + "data": { + "region": "Piirkond" + }, + "description": "Kui soovid j\u00e4lgida mitte ainult oblastit ja piirkonda vali konkreetne asukoht" + }, + "district": { + "data": { + "region": "Piirkond" + }, + "description": "Kui soovid j\u00e4lgida mitte ainult oblastit vali konkreetne piirkond" + }, + "state": { + "data": { + "region": "Piirkond" + }, + "description": "Vali j\u00e4lgitav oblast" + }, + "user": { + "data": { + "api_key": "API v\u00f5ti" + }, + "description": "Loo Ukraina h\u00e4ire integreerimine. API-v\u00f5tme loomiseks ava {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/id.json b/homeassistant/components/ukraine_alarm/translations/id.json new file mode 100644 index 00000000000..6c10857bc80 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/id.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "Lokasi sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_api_key": "Kunci API tidak valid", + "timeout": "Tenggang waktu membuat koneksi habis", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "community": { + "data": { + "region": "Wilayah" + }, + "description": "Jika ingin memantau tidak hanya negara bagian dan distrik, pilih komunitas tertentu" + }, + "district": { + "data": { + "region": "Wilayah" + }, + "description": "Jika ingin memantau tidak hanya negara bagian, pilih distrik tertentu" + }, + "state": { + "data": { + "region": "Wilayah" + }, + "description": "Pilih negara bagian untuk dipantau" + }, + "user": { + "data": { + "api_key": "Kunci API" + }, + "description": "Siapkan integrasi Alarm Ukraina. Untuk membuat kunci API, buka {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/it.json b/homeassistant/components/ukraine_alarm/translations/it.json new file mode 100644 index 00000000000..23d0f2a12a5 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/it.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "La posizione \u00e8 gi\u00e0 configurata" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_api_key": "Chiave API non valida", + "timeout": "Tempo scaduto per stabile la connessione.", + "unknown": "Errore imprevisto" + }, + "step": { + "community": { + "data": { + "region": "Regione" + }, + "description": "Se vuoi monitorare non solo stato e distretto, scegli la sua comunit\u00e0 specifica" + }, + "district": { + "data": { + "region": "Regione" + }, + "description": "Se vuoi monitorare non solo lo stato, scegli il suo distretto specifico" + }, + "state": { + "data": { + "region": "Regione" + }, + "description": "Scegli lo stato da monitorare" + }, + "user": { + "data": { + "api_key": "Chiave API" + }, + "description": "Imposta l'integrazione di Ukraine Alarm. Per generare una chiave API vai su {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/ja.json b/homeassistant/components/ukraine_alarm/translations/ja.json new file mode 100644 index 00000000000..519bf3df072 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/ja.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "already_configured": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", + "timeout": "\u63a5\u7d9a\u78ba\u7acb\u6642\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "community": { + "data": { + "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" + } + }, + "district": { + "data": { + "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" + } + }, + "state": { + "data": { + "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" + } + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/nl.json b/homeassistant/components/ukraine_alarm/translations/nl.json new file mode 100644 index 00000000000..6ec93b0526d --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/nl.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "Locatie is al geconfigureerd." + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_api_key": "Ongeldige API-sleutel", + "timeout": "Time-out bij het maken van verbinding", + "unknown": "Onverwachte fout" + }, + "step": { + "community": { + "data": { + "region": "Regio" + }, + "description": "Als u niet alleen de staat en het district wilt volgen, kiest u de specifieke gemeenschap ervan" + }, + "district": { + "data": { + "region": "Regio" + }, + "description": "Als u niet alleen de staat wilt controleren, kies dan het specifieke district" + }, + "state": { + "data": { + "region": "Regio" + }, + "description": "Kies staat om te monitoren" + }, + "user": { + "data": { + "api_key": "API-sleutel" + }, + "description": "Stel de Oekra\u00efne Alarm integratie in. Om een API sleutel te genereren ga naar {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/no.json b/homeassistant/components/ukraine_alarm/translations/no.json new file mode 100644 index 00000000000..b7ee3275920 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/no.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "Plasseringen er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_api_key": "Ugyldig API-n\u00f8kkel", + "timeout": "Tidsavbrudd oppretter forbindelse", + "unknown": "Uventet feil" + }, + "step": { + "community": { + "data": { + "region": "Region" + }, + "description": "Hvis du ikke bare vil overv\u00e5ke stat og distrikt, velg dets spesifikke fellesskap" + }, + "district": { + "data": { + "region": "Region" + }, + "description": "Hvis du ikke bare vil overv\u00e5ke staten, velg dens spesifikke distrikt" + }, + "state": { + "data": { + "region": "Region" + }, + "description": "Velg tilstand \u00e5 overv\u00e5ke" + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel" + }, + "description": "Sett opp Ukraina Alarm-integrasjonen. For \u00e5 generere en API-n\u00f8kkel, g\u00e5 til {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/pl.json b/homeassistant/components/ukraine_alarm/translations/pl.json new file mode 100644 index 00000000000..1cccde50083 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/pl.json @@ -0,0 +1,35 @@ +{ + "config": { + "abort": { + "already_configured": "Lokalizacja jest ju\u017c skonfigurowana" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_api_key": "Nieprawid\u0142owy klucz API", + "timeout": "Limit czasu na nawi\u0105zanie po\u0142\u0105czenia", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "community": { + "data": { + "region": "Region" + } + }, + "district": { + "data": { + "region": "Region" + } + }, + "state": { + "data": { + "region": "Region" + } + }, + "user": { + "data": { + "api_key": "Klucz API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/pt-BR.json b/homeassistant/components/ukraine_alarm/translations/pt-BR.json new file mode 100644 index 00000000000..92c5eb20b6d --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/pt-BR.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "O local j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_api_key": "Chave de API inv\u00e1lida", + "timeout": "Tempo limite estabelecendo conex\u00e3o", + "unknown": "Erro inesperado" + }, + "step": { + "community": { + "data": { + "region": "Regi\u00e3o" + }, + "description": "Se voc\u00ea deseja monitorar n\u00e3o apenas estado e distrito, escolha sua comunidade espec\u00edfica" + }, + "district": { + "data": { + "region": "Regi\u00e3o" + }, + "description": "Se voc\u00ea deseja monitorar n\u00e3o apenas o estado, escolha seu distrito espec\u00edfico" + }, + "state": { + "data": { + "region": "Regi\u00e3o" + }, + "description": "Escolha o estado para monitorar" + }, + "user": { + "data": { + "api_key": "Chave de API" + }, + "description": "Configure a integra\u00e7\u00e3o do Alarme da Ucr\u00e2nia. Para gerar uma chave de API, acesse {api_url}" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/zh-Hant.json b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json new file mode 100644 index 00000000000..0704a45dd56 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json @@ -0,0 +1,39 @@ +{ + "config": { + "abort": { + "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_api_key": "API \u91d1\u9470\u7121\u6548", + "timeout": "\u5efa\u7acb\u9023\u7dda\u903e\u6642", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "community": { + "data": { + "region": "\u5340\u57df" + }, + "description": "\u5047\u5982\u4f60\u4e0d\u53ea\u8981\u76e3\u770b\u72c0\u614b\u8207\u5340\u57df\u3001\u8acb\u9078\u64c7\u7279\u5b9a\u793e\u5340" + }, + "district": { + "data": { + "region": "\u5340\u57df" + }, + "description": "\u5047\u5982\u4f60\u4e0d\u53ea\u8981\u76e3\u770b\u72c0\u614b\u3001\u8acb\u9078\u64c7\u7279\u5b9a\u5340\u57df" + }, + "state": { + "data": { + "region": "\u5340\u57df" + }, + "description": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684\u72c0\u614b" + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470" + }, + "description": "\u8a2d\u5b9a\u70cf\u514b\u862d\u8b66\u5831\u6574\u5408\uff0c\u8acb\u9023\u7dda\u81f3 {api_url} \u4ee5\u53d6\u5f97 API \u91d1\u9470\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/nl.json b/homeassistant/components/unifi/translations/nl.json index 8b67bec0364..637dbd480f3 100644 --- a/homeassistant/components/unifi/translations/nl.json +++ b/homeassistant/components/unifi/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Unifi Network site is al geconfigureerd", - "configuration_updated": "Configuratie bijgewerkt.", + "configuration_updated": "Configuratie bijgewerkt", "reauth_successful": "Herauthenticatie was succesvol" }, "error": { diff --git a/homeassistant/components/vulcan/translations/id.json b/homeassistant/components/vulcan/translations/id.json index a15ed1772d2..c372ca0ebe5 100644 --- a/homeassistant/components/vulcan/translations/id.json +++ b/homeassistant/components/vulcan/translations/id.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "Semua siswa telah ditambahkan.", "already_configured": "Siswa tersebut telah ditambahkan.", + "no_matching_entries": "Tidak ditemukan entri yang cocok, harap gunakan akun lain atau hapus integrasi dengan siswa yang ketinggalan zaman..", "reauth_successful": "Otorisasi ulang berhasil" }, "error": { @@ -37,6 +38,14 @@ }, "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." }, + "reauth_confirm": { + "data": { + "pin": "PIN", + "region": "Simbol", + "token": "Token" + }, + "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." + }, "select_saved_credentials": { "data": { "credentials": "Masuk" diff --git a/homeassistant/components/vulcan/translations/ja.json b/homeassistant/components/vulcan/translations/ja.json index 98363f12f13..1f97968e9ae 100644 --- a/homeassistant/components/vulcan/translations/ja.json +++ b/homeassistant/components/vulcan/translations/ja.json @@ -37,6 +37,13 @@ }, "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" }, + "reauth_confirm": { + "data": { + "pin": "\u30d4\u30f3", + "region": "\u30b7\u30f3\u30dc\u30eb", + "token": "\u30c8\u30fc\u30af\u30f3" + } + }, "select_saved_credentials": { "data": { "credentials": "\u30ed\u30b0\u30a4\u30f3" diff --git a/homeassistant/components/wallbox/translations/ko.json b/homeassistant/components/wallbox/translations/ko.json new file mode 100644 index 00000000000..7c532be8890 --- /dev/null +++ b/homeassistant/components/wallbox/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "reauth_invalid": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ubc88\ud638\uac00 \uc6d0\ubcf8\uacfc \uc77c\uce58\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/ca.json b/homeassistant/components/ws66i/translations/ca.json new file mode 100644 index 00000000000..789edb86fb2 --- /dev/null +++ b/homeassistant/components/ws66i/translations/ca.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "unknown": "Error inesperat" + }, + "step": { + "user": { + "data": { + "ip_address": "Adre\u00e7a IP" + }, + "title": "Connexi\u00f3 amb el dispositiu" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nom de la font #1", + "source_2": "Nom de la font #2", + "source_3": "Nom de la font #3", + "source_4": "Nom de la font #4", + "source_5": "Nom de la font #5", + "source_6": "Nom de la font #6" + }, + "title": "Configuraci\u00f3 de les fonts" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/de.json b/homeassistant/components/ws66i/translations/de.json new file mode 100644 index 00000000000..cab3e062d8e --- /dev/null +++ b/homeassistant/components/ws66i/translations/de.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "user": { + "data": { + "ip_address": "IP-Adresse" + }, + "title": "Verbinden mit dem Ger\u00e4t" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Name der Quelle #1", + "source_2": "Name der Quelle #2", + "source_3": "Name der Quelle #3", + "source_4": "Name der Quelle #4", + "source_5": "Name der Quelle #5", + "source_6": "Name der Quelle #6" + }, + "title": "Quellen konfigurieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/el.json b/homeassistant/components/ws66i/translations/el.json new file mode 100644 index 00000000000..4a1365c3a77 --- /dev/null +++ b/homeassistant/components/ws66i/translations/el.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "user": { + "data": { + "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" + }, + "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #1", + "source_2": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #2", + "source_3": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #3", + "source_4": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #4", + "source_5": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #5", + "source_6": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03b7\u03b3\u03ae\u03c2 #6" + }, + "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03b7\u03b3\u03ce\u03bd" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/et.json b/homeassistant/components/ws66i/translations/et.json new file mode 100644 index 00000000000..83b238d74f5 --- /dev/null +++ b/homeassistant/components/ws66i/translations/et.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "user": { + "data": { + "ip_address": "IP aadress" + }, + "title": "\u00dchendu seadmega" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Sisendi nr 1 nimi", + "source_2": "Sisendi nr 2 nimi", + "source_3": "Sisendi nr 3 nimi", + "source_4": "Sisendi nr 4 nimi", + "source_5": "Sisendi nr 5 nimi", + "source_6": "Sisendi nr 6 nimi" + }, + "title": "Seadista sisendid" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/hu.json b/homeassistant/components/ws66i/translations/hu.json new file mode 100644 index 00000000000..9d6585e8f6b --- /dev/null +++ b/homeassistant/components/ws66i/translations/hu.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "user": { + "data": { + "ip_address": "IP c\u00edm" + }, + "title": "Csatlakoz\u00e1s az eszk\u00f6zh\u00f6z" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Forr\u00e1s neve #1", + "source_2": "Forr\u00e1s neve #2", + "source_3": "Forr\u00e1s neve #3", + "source_4": "Forr\u00e1s neve #4", + "source_5": "Forr\u00e1s neve #5", + "source_6": "Forr\u00e1s neve #6" + }, + "title": "Forr\u00e1sok konfigur\u00e1l\u00e1sa" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/id.json b/homeassistant/components/ws66i/translations/id.json new file mode 100644 index 00000000000..54cb3043a11 --- /dev/null +++ b/homeassistant/components/ws66i/translations/id.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "user": { + "data": { + "ip_address": "Alamat IP" + }, + "title": "Hubungkan ke perangkat" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nama sumber #1", + "source_2": "Nama sumber #2", + "source_3": "Nama sumber #3", + "source_4": "Nama sumber #4", + "source_5": "Nama sumber #5", + "source_6": "Nama sumber #6" + }, + "title": "Konfigurasikan sumber" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/it.json b/homeassistant/components/ws66i/translations/it.json new file mode 100644 index 00000000000..c98714c98ad --- /dev/null +++ b/homeassistant/components/ws66i/translations/it.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "unknown": "Errore imprevisto" + }, + "step": { + "user": { + "data": { + "ip_address": "Indirizzo IP" + }, + "title": "Connettiti al dispositivo" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nome della fonte n. 1", + "source_2": "Nome della fonte n. 2", + "source_3": "Nome della fonte n. 3", + "source_4": "Nome della fonte n. 4", + "source_5": "Nome della fonte n. 5", + "source_6": "Nome della fonte n. 6" + }, + "title": "Configura le fonti" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/ja.json b/homeassistant/components/ws66i/translations/ja.json new file mode 100644 index 00000000000..2f93c93954b --- /dev/null +++ b/homeassistant/components/ws66i/translations/ja.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "user": { + "data": { + "ip_address": "IP\u30a2\u30c9\u30ec\u30b9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/nl.json b/homeassistant/components/ws66i/translations/nl.json new file mode 100644 index 00000000000..0ff24636b88 --- /dev/null +++ b/homeassistant/components/ws66i/translations/nl.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "unknown": "Onverwachte fout" + }, + "step": { + "user": { + "data": { + "ip_address": "IP-adres" + }, + "title": "Verbinding maken met het apparaat" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Naam van bron #1", + "source_2": "Naam van bron #2", + "source_3": "Naam van bron #3", + "source_4": "Naam van bron #4", + "source_5": "Naam van bron #5", + "source_6": "Naam van bron #6" + }, + "title": "Configureer bronnen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/no.json b/homeassistant/components/ws66i/translations/no.json new file mode 100644 index 00000000000..fa132ab681a --- /dev/null +++ b/homeassistant/components/ws66i/translations/no.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "unknown": "Uventet feil" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresse" + }, + "title": "Koble til enheten" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Navn p\u00e5 kilde #1", + "source_2": "Navn p\u00e5 kilde #2", + "source_3": "Navn p\u00e5 kilde #3", + "source_4": "Navn p\u00e5 kilde #4", + "source_5": "Navn p\u00e5 kilde #5", + "source_6": "Navn p\u00e5 kilde #6" + }, + "title": "Konfigurer kilder" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/pl.json b/homeassistant/components/ws66i/translations/pl.json new file mode 100644 index 00000000000..1fc8c8b1cc9 --- /dev/null +++ b/homeassistant/components/ws66i/translations/pl.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "ip_address": "Adres IP" + }, + "title": "Po\u0142\u0105czenie z urz\u0105dzeniem" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nazwa \u017ar\u00f3d\u0142a #1", + "source_2": "Nazwa \u017ar\u00f3d\u0142a #2", + "source_3": "Nazwa \u017ar\u00f3d\u0142a #3", + "source_4": "Nazwa \u017ar\u00f3d\u0142a #4", + "source_5": "Nazwa \u017ar\u00f3d\u0142a #5", + "source_6": "Nazwa \u017ar\u00f3d\u0142a #6" + }, + "title": "Konfiguracja \u017ar\u00f3de\u0142" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/pt-BR.json b/homeassistant/components/ws66i/translations/pt-BR.json new file mode 100644 index 00000000000..d440aab3aa4 --- /dev/null +++ b/homeassistant/components/ws66i/translations/pt-BR.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "ip_address": "Endere\u00e7o IP" + }, + "title": "Conecte-se ao dispositivo" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nome da fonte #1", + "source_2": "Nome da fonte #2", + "source_3": "Nome da fonte #3", + "source_4": "Nome da fonte #4", + "source_5": "Nome da fonte #5", + "source_6": "Nome da fonte #6" + }, + "title": "Configurar fontes" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/ru.json b/homeassistant/components/ws66i/translations/ru.json new file mode 100644 index 00000000000..b7e244cf2b0 --- /dev/null +++ b/homeassistant/components/ws66i/translations/ru.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "user": { + "data": { + "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441" + }, + "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21161", + "source_2": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21162", + "source_3": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21163", + "source_4": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21164", + "source_5": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21165", + "source_6": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u21166" + }, + "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/zh-Hant.json b/homeassistant/components/ws66i/translations/zh-Hant.json new file mode 100644 index 00000000000..a583ac2217f --- /dev/null +++ b/homeassistant/components/ws66i/translations/zh-Hant.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \u4f4d\u5740" + }, + "title": "\u9023\u7dda\u81f3\u88dd\u7f6e" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "\u4f86\u6e90 #1 \u540d\u7a31", + "source_2": "\u4f86\u6e90 #2 \u540d\u7a31", + "source_3": "\u4f86\u6e90 #3 \u540d\u7a31", + "source_4": "\u4f86\u6e90 #4 \u540d\u7a31", + "source_5": "\u4f86\u6e90 #5 \u540d\u7a31", + "source_6": "\u4f86\u6e90 #6 \u540d\u7a31" + }, + "title": "\u8a2d\u5b9a\u4f86\u6e90" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yale_smart_alarm/translations/ko.json b/homeassistant/components/yale_smart_alarm/translations/ko.json new file mode 100644 index 00000000000..b213fdda32c --- /dev/null +++ b/homeassistant/components/yale_smart_alarm/translations/ko.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "lock_code_digits": "\ud540\ucf54\ub4dc \uc22b\uc790 \uac1c\uc218" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/ko.json b/homeassistant/components/zwave_js/translations/ko.json index 08e3dc8c7d7..2c14397177e 100644 --- a/homeassistant/components/zwave_js/translations/ko.json +++ b/homeassistant/components/zwave_js/translations/ko.json @@ -51,5 +51,12 @@ } } }, + "device_automation": { + "action_type": { + "set_config_parameter": "\uad6c\uc131 \ub9e4\uac1c\ubcc0\uc218 {subtype} \uc758 \uac12 \uc124\uc815", + "set_lock_usercode": "{entity_name} \uc5d0 \uc0ac\uc6a9\uc790 \ucf54\ub4dc \uc124\uc815", + "set_value": "Z-Wave Value\uc758 \uc124\uc815\uac12" + } + }, "title": "Z-Wave JS" } \ No newline at end of file From bf77c000eaf4b3e8ae6e607481f6a5a75cad9376 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 9 May 2022 23:00:19 -0500 Subject: [PATCH 332/930] Complete baked query conversion for recorder.history (#71618) --- homeassistant/components/recorder/history.py | 246 ++++++++++--------- 1 file changed, 131 insertions(+), 115 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index d221ced3a84..a061bcd1329 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -11,6 +11,8 @@ from typing import Any, cast from sqlalchemy import Column, Text, and_, bindparam, func, or_ from sqlalchemy.ext import baked +from sqlalchemy.ext.baked import BakedQuery +from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal @@ -104,26 +106,6 @@ QUERY_STATES_NO_LAST_UPDATED = [ HISTORY_BAKERY = "recorder_history_bakery" -def query_and_join_attributes( - hass: HomeAssistant, no_attributes: bool -) -> tuple[list[Column], bool]: - """Return the query keys and if StateAttributes should be joined.""" - # If no_attributes was requested we do the query - # without the attributes fields and do not join the - # state_attributes table - if no_attributes: - return QUERY_STATE_NO_ATTR, False - # If we in the process of migrating schema we do - # not want to join the state_attributes table as we - # do not know if it will be there yet - if recorder.get_instance(hass).schema_version < 25: - return QUERY_STATES_PRE_SCHEMA_25, False - # Finally if no migration is in progress and no_attributes - # was not requested, we query both attributes columns and - # join state_attributes - return QUERY_STATES, True - - def bake_query_and_join_attributes( hass: HomeAssistant, no_attributes: bool, include_last_updated: bool = True ) -> tuple[Any, bool]: @@ -138,9 +120,9 @@ def bake_query_and_join_attributes( # state_attributes table if no_attributes: if include_last_updated: - return bakery(lambda session: session.query(*QUERY_STATE_NO_ATTR)), False + return bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR)), False return ( - bakery(lambda session: session.query(*QUERY_STATE_NO_ATTR_NO_LAST_UPDATED)), + bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR_NO_LAST_UPDATED)), False, ) # If we in the process of migrating schema we do @@ -149,23 +131,19 @@ def bake_query_and_join_attributes( if recorder.get_instance(hass).schema_version < 25: if include_last_updated: return ( - bakery(lambda session: session.query(*QUERY_STATES_PRE_SCHEMA_25)), + bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25)), False, ) return ( - bakery( - lambda session: session.query( - *QUERY_STATES_PRE_SCHEMA_25_NO_LAST_UPDATED - ) - ), + bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25_NO_LAST_UPDATED)), False, ) # Finally if no migration is in progress and no_attributes # was not requested, we query both attributes columns and # join state_attributes if include_last_updated: - return bakery(lambda session: session.query(*QUERY_STATES)), True - return bakery(lambda session: session.query(*QUERY_STATES_NO_LAST_UPDATED)), True + return bakery(lambda s: s.query(*QUERY_STATES)), True + return bakery(lambda s: s.query(*QUERY_STATES_NO_LAST_UPDATED)), True def async_setup(hass: HomeAssistant) -> None: @@ -200,6 +178,18 @@ def get_significant_states( ) +def _ignore_domains_filter(query: Query) -> Query: + """Add a filter to ignore domains we do not fetch history for.""" + return query.filter( + and_( + *[ + ~States.entity_id.like(entity_domain) + for entity_domain in IGNORE_DOMAINS_ENTITY_ID_LIKE + ] + ) + ) + + def _query_significant_states_with_session( hass: HomeAssistant, session: Session, @@ -243,14 +233,7 @@ def _query_significant_states_with_session( States.entity_id.in_(bindparam("entity_ids", expanding=True)) ) else: - baked_query += lambda q: q.filter( - and_( - *[ - ~States.entity_id.like(entity_domain) - for entity_domain in IGNORE_DOMAINS_ENTITY_ID_LIKE - ] - ) - ) + baked_query += _ignore_domains_filter if filters: filters.bake(baked_query) @@ -470,6 +453,94 @@ def get_last_state_changes( ) +def _most_recent_state_ids_entities_subquery(query: Query) -> Query: + """Query to find the most recent state id for specific entities.""" + # We got an include-list of entities, accelerate the query by filtering already + # in the inner query. + most_recent_state_ids = ( + query.session.query(func.max(States.state_id).label("max_state_id")) + .filter( + (States.last_updated >= bindparam("run_start")) + & (States.last_updated < bindparam("utc_point_in_time")) + ) + .filter(States.entity_id.in_(bindparam("entity_ids", expanding=True))) + .group_by(States.entity_id) + .subquery() + ) + return query.join( + most_recent_state_ids, + States.state_id == most_recent_state_ids.c.max_state_id, + ) + + +def _get_states_baked_query_for_entites( + hass: HomeAssistant, + no_attributes: bool = False, +) -> BakedQuery: + """Baked query to get states for specific entities.""" + baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query += _most_recent_state_ids_entities_subquery + if join_attributes: + baked_query += lambda q: q.outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + return baked_query + + +def _most_recent_state_ids_subquery(query: Query) -> Query: + """Find the most recent state ids for all entiites.""" + # We did not get an include-list of entities, query all states in the inner + # query, then filter out unwanted domains as well as applying the custom filter. + # This filtering can't be done in the inner query because the domain column is + # not indexed and we can't control what's in the custom filter. + most_recent_states_by_date = ( + query.session.query( + States.entity_id.label("max_entity_id"), + func.max(States.last_updated).label("max_last_updated"), + ) + .filter( + (States.last_updated >= bindparam("run_start")) + & (States.last_updated < bindparam("utc_point_in_time")) + ) + .group_by(States.entity_id) + .subquery() + ) + most_recent_state_ids = ( + query.session.query(func.max(States.state_id).label("max_state_id")) + .join( + most_recent_states_by_date, + and_( + States.entity_id == most_recent_states_by_date.c.max_entity_id, + States.last_updated == most_recent_states_by_date.c.max_last_updated, + ), + ) + .group_by(States.entity_id) + .subquery() + ) + return query.join( + most_recent_state_ids, + States.state_id == most_recent_state_ids.c.max_state_id, + ) + + +def _get_states_baked_query_for_all( + hass: HomeAssistant, + filters: Any | None = None, + no_attributes: bool = False, +) -> BakedQuery: + """Baked query to get states for all entities.""" + baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query += _most_recent_state_ids_subquery + baked_query += _ignore_domains_filter + if filters: + filters.bake(baked_query) + if join_attributes: + baked_query += lambda q: q.outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + return baked_query + + def _get_states_with_session( hass: HomeAssistant, session: Session, @@ -494,77 +565,22 @@ def _get_states_with_session( # We have more than one entity to look at so we need to do a query on states # since the last recorder run started. - query_keys, join_attributes = query_and_join_attributes(hass, no_attributes) - query = session.query(*query_keys) - if entity_ids: - # We got an include-list of entities, accelerate the query by filtering already - # in the inner query. - most_recent_state_ids = ( - session.query( - func.max(States.state_id).label("max_state_id"), - ) - .filter( - (States.last_updated >= run.start) - & (States.last_updated < utc_point_in_time) - ) - .filter(States.entity_id.in_(entity_ids)) - ) - most_recent_state_ids = most_recent_state_ids.group_by(States.entity_id) - most_recent_state_ids = most_recent_state_ids.subquery() - query = query.join( - most_recent_state_ids, - States.state_id == most_recent_state_ids.c.max_state_id, - ) - if join_attributes: - query = query.outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) + baked_query = _get_states_baked_query_for_entites(hass, no_attributes) else: - # We did not get an include-list of entities, query all states in the inner - # query, then filter out unwanted domains as well as applying the custom filter. - # This filtering can't be done in the inner query because the domain column is - # not indexed and we can't control what's in the custom filter. - most_recent_states_by_date = ( - session.query( - States.entity_id.label("max_entity_id"), - func.max(States.last_updated).label("max_last_updated"), - ) - .filter( - (States.last_updated >= run.start) - & (States.last_updated < utc_point_in_time) - ) - .group_by(States.entity_id) - .subquery() - ) - most_recent_state_ids = ( - session.query(func.max(States.state_id).label("max_state_id")) - .join( - most_recent_states_by_date, - and_( - States.entity_id == most_recent_states_by_date.c.max_entity_id, - States.last_updated - == most_recent_states_by_date.c.max_last_updated, - ), - ) - .group_by(States.entity_id) - .subquery() - ) - query = query.join( - most_recent_state_ids, - States.state_id == most_recent_state_ids.c.max_state_id, - ) - for entity_domain in IGNORE_DOMAINS_ENTITY_ID_LIKE: - query = query.filter(~States.entity_id.like(entity_domain)) - if filters: - query = filters.apply(query) - if join_attributes: - query = query.outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) + baked_query = _get_states_baked_query_for_all(hass, filters, no_attributes) attr_cache: dict[str, dict[str, Any]] = {} - return [LazyState(row, attr_cache) for row in execute(query)] + return [ + LazyState(row, attr_cache) + for row in execute( + baked_query(session).params( + run_start=run.start, + utc_point_in_time=utc_point_in_time, + entity_ids=entity_ids, + ) + ) + ] def _get_single_entity_states_with_session( @@ -585,8 +601,7 @@ def _get_single_entity_states_with_session( baked_query += lambda q: q.outerjoin( StateAttributes, States.attributes_id == StateAttributes.attributes_id ) - baked_query += lambda q: q.order_by(States.last_updated.desc()) - baked_query += lambda q: q.limit(1) + baked_query += lambda q: q.order_by(States.last_updated.desc()).limit(1) query = baked_query(session).params( utc_point_in_time=utc_point_in_time, entity_id=entity_id @@ -634,8 +649,8 @@ def _sorted_states_to_dict( filters=filters, no_attributes=no_attributes, ): - state.last_changed = start_time state.last_updated = start_time + state.last_changed = start_time result[state.entity_id].append(state) if _LOGGER.isEnabledFor(logging.DEBUG): @@ -671,31 +686,32 @@ def _sorted_states_to_dict( continue ent_results.append(LazyState(first_state, attr_cache)) - prev_state = ent_results[-1] - assert isinstance(prev_state, LazyState) + assert isinstance(ent_results[-1], State) + prev_state: Column | str = ent_results[-1].state initial_state_count = len(ent_results) + db_state = None for db_state in group: # With minimal response we do not care about attribute # changes so we can filter out duplicate states - if db_state.state == prev_state.state: + if (state := db_state.state) == prev_state: continue ent_results.append( { - STATE_KEY: db_state.state, + STATE_KEY: state, LAST_CHANGED_KEY: _process_timestamp_to_utc_isoformat( db_state.last_changed ), } ) - prev_state = db_state + prev_state = state - if prev_state and len(ent_results) != initial_state_count: + if db_state and len(ent_results) != initial_state_count: # There was at least one state change # replace the last minimal state with # a full state - ent_results[-1] = LazyState(prev_state, attr_cache) + ent_results[-1] = LazyState(db_state, attr_cache) # Filter out the empty lists if some states had 0 results. return {key: val for key, val in result.items() if val} From 0f41f569985944a6972d06da626fcace70ff8bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 10 May 2022 09:59:10 +0200 Subject: [PATCH 333/930] Address late QNAP QSW strict typing comments (#71628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/qnap_qsw/coordinator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/qnap_qsw/coordinator.py b/homeassistant/components/qnap_qsw/coordinator.py index e3ca95076d0..7aa1b6f0a85 100644 --- a/homeassistant/components/qnap_qsw/coordinator.py +++ b/homeassistant/components/qnap_qsw/coordinator.py @@ -31,10 +31,9 @@ class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL, - update_method=self._async_update, ) - async def _async_update(self) -> dict[str, Any]: + async def _async_update_data(self) -> dict[str, Any]: """Update data via library.""" async with async_timeout.timeout(QSW_TIMEOUT_SEC): try: From 46becd40235fe2077f4493c6c0511a1a2176448c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 10 May 2022 10:00:38 +0200 Subject: [PATCH 334/930] Address late Airzone strict typing comments (#71627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/climate.py | 2 +- homeassistant/components/airzone/coordinator.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/airzone/climate.py b/homeassistant/components/airzone/climate.py index e264df18e0c..ce67142547f 100644 --- a/homeassistant/components/airzone/climate.py +++ b/homeassistant/components/airzone/climate.py @@ -162,7 +162,7 @@ class AirzoneClimate(AirzoneZoneEntity, ClimateEntity): params[API_ON] = 1 await self._async_update_hvac_params(params) - async def async_set_temperature(self, **kwargs: dict[str, Any]) -> None: + async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" params = { API_SET_POINT: kwargs.get(ATTR_TEMPERATURE), diff --git a/homeassistant/components/airzone/coordinator.py b/homeassistant/components/airzone/coordinator.py index f9f7322a3eb..5ce9dcc45ef 100644 --- a/homeassistant/components/airzone/coordinator.py +++ b/homeassistant/components/airzone/coordinator.py @@ -31,10 +31,9 @@ class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL, - update_method=self._async_update, ) - async def _async_update(self) -> dict[str, Any]: + async def _async_update_data(self) -> dict[str, Any]: """Update data via library.""" async with async_timeout.timeout(AIOAIRZONE_DEVICE_TIMEOUT_SEC): try: From 275a90a2e84c07687f8f0fa7a1550b23bdd90aea Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 10 May 2022 12:46:02 +0200 Subject: [PATCH 335/930] Check state attributes in template light tests (#71608) --- tests/components/template/test_light.py | 388 ++++++++++++++++++++---- 1 file changed, 322 insertions(+), 66 deletions(-) diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index 642fa5601cf..0888511c583 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -1,5 +1,4 @@ """The tests for the Template light platform.""" -import logging import pytest @@ -11,6 +10,12 @@ from homeassistant.components.light import ( ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_WHITE_VALUE, + SUPPORT_BRIGHTNESS, + SUPPORT_COLOR, + SUPPORT_COLOR_TEMP, + SUPPORT_TRANSITION, + SUPPORT_WHITE_VALUE, + ColorMode, LightEntityFeature, ) from homeassistant.const import ( @@ -22,13 +27,15 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) -_LOGGER = logging.getLogger(__name__) - # Represent for light's availability _STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state" @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -59,12 +66,22 @@ _STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state" }, ], ) -async def test_template_state_invalid(hass, start_ha): +async def test_template_state_invalid( + hass, supported_features, supported_color_modes, start_ha +): """Test template state with render error.""" - assert hass.states.get("light.test_template_light").state == STATE_OFF + state = hass.states.get("light.test_template_light") + assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -95,20 +112,39 @@ async def test_template_state_invalid(hass, start_ha): }, ], ) -async def test_template_state_text(hass, start_ha): +async def test_template_state_text( + hass, supported_features, supported_color_modes, start_ha +): """Test the state text of a template.""" - for set_state in [STATE_ON, STATE_OFF]: - hass.states.async_set("light.test_state", set_state) - await hass.async_block_till_done() - assert hass.states.get("light.test_template_light").state == set_state + set_state = STATE_ON + hass.states.async_set("light.test_state", set_state) + await hass.async_block_till_done() + state = hass.states.get("light.test_template_light") + assert state.state == set_state + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + set_state = STATE_OFF + hass.states.async_set("light.test_state", set_state) + await hass.async_block_till_done() + state = hass.states.get("light.test_template_light") + assert state.state == set_state + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( - "config_addon,expected_state", + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) +@pytest.mark.parametrize( + "config_addon,expected_state,expected_color_mode", [ - ({"replace1": '"{{ 1 == 1 }}"'}, STATE_ON), - ({"replace1": '"{{ 1 == 2 }}"'}, STATE_OFF), + ({"replace1": '"{{ 1 == 1 }}"'}, STATE_ON, ColorMode.UNKNOWN), + ({"replace1": '"{{ 1 == 2 }}"'}, STATE_OFF, None), ], ) @pytest.mark.parametrize( @@ -141,9 +177,20 @@ async def test_template_state_text(hass, start_ha): }""", ], ) -async def test_templatex_state_boolean(hass, expected_state, start_ha): +async def test_templatex_state_boolean( + hass, + expected_color_mode, + expected_state, + supported_features, + supported_color_modes, + start_ha, +): """Test the setting of the state with boolean on.""" - assert hass.states.get("light.test_template_light").state == expected_state + state = hass.states.get("light.test_template_light") + assert state.state == expected_state + assert state.attributes.get("color_mode") == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(0, light.DOMAIN)]) @@ -247,6 +294,10 @@ async def test_missing_key(hass, count, start_ha): @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -274,13 +325,18 @@ async def test_missing_key(hass, count, start_ha): }, ], ) -async def test_on_action(hass, start_ha, calls): +async def test_on_action( + hass, start_ha, calls, supported_features, supported_color_modes +): """Test on action.""" hass.states.async_set("light.test_state", STATE_OFF) await hass.async_block_till_done() state = hass.states.get("light.test_template_light") assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -291,8 +347,17 @@ async def test_on_action(hass, start_ha, calls): assert len(calls) == 1 + assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -327,13 +392,18 @@ async def test_on_action(hass, start_ha, calls): }, ], ) -async def test_on_action_with_transition(hass, start_ha, calls): +async def test_on_action_with_transition( + hass, start_ha, calls, supported_features, supported_color_modes +): """Test on action with transition.""" hass.states.async_set("light.test_state", STATE_OFF) await hass.async_block_till_done() state = hass.states.get("light.test_template_light") assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -345,8 +415,17 @@ async def test_on_action_with_transition(hass, start_ha, calls): assert len(calls) == 1 assert calls[0].data["transition"] == 5 + assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes,expected_color_mode", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS], ColorMode.BRIGHTNESS)], +) @pytest.mark.parametrize( "config", [ @@ -373,13 +452,23 @@ async def test_on_action_with_transition(hass, start_ha, calls): }, ], ) -async def test_on_action_optimistic(hass, start_ha, calls): +async def test_on_action_optimistic( + hass, + start_ha, + calls, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test on action with optimistic state.""" hass.states.async_set("light.test_state", STATE_OFF) await hass.async_block_till_done() state = hass.states.get("light.test_template_light") assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -391,9 +480,30 @@ async def test_on_action_optimistic(hass, start_ha, calls): state = hass.states.get("light.test_template_light") assert len(calls) == 1 assert state.state == STATE_ON + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + await hass.services.async_call( + light.DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_template_light", ATTR_BRIGHTNESS: 100}, + blocking=True, + ) + + state = hass.states.get("light.test_template_light") + assert len(calls) == 1 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -423,13 +533,18 @@ async def test_on_action_optimistic(hass, start_ha, calls): }, ], ) -async def test_off_action(hass, start_ha, calls): +async def test_off_action( + hass, start_ha, calls, supported_features, supported_color_modes +): """Test off action.""" hass.states.async_set("light.test_state", STATE_ON) await hass.async_block_till_done() state = hass.states.get("light.test_template_light") assert state.state == STATE_ON + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -439,9 +554,17 @@ async def test_off_action(hass, start_ha, calls): ) assert len(calls) == 1 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -476,13 +599,18 @@ async def test_off_action(hass, start_ha, calls): }, ], ) -async def test_off_action_with_transition(hass, start_ha, calls): +async def test_off_action_with_transition( + hass, start_ha, calls, supported_features, supported_color_modes +): """Test off action with transition.""" hass.states.async_set("light.test_state", STATE_ON) await hass.async_block_till_done() state = hass.states.get("light.test_template_light") assert state.state == STATE_ON + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -493,9 +621,17 @@ async def test_off_action_with_transition(hass, start_ha, calls): assert len(calls) == 1 assert calls[0].data["transition"] == 2 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -522,10 +658,15 @@ async def test_off_action_with_transition(hass, start_ha, calls): }, ], ) -async def test_off_action_optimistic(hass, start_ha, calls): +async def test_off_action_optimistic( + hass, start_ha, calls, supported_features, supported_color_modes +): """Test off action with optimistic state.""" state = hass.states.get("light.test_template_light") assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features await hass.services.async_call( light.DOMAIN, @@ -537,9 +678,16 @@ async def test_off_action_optimistic(hass, start_ha, calls): assert len(calls) == 1 state = hass.states.get("light.test_template_light") assert state.state == STATE_OFF + assert "color_mode" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes,expected_color_mode", + [(SUPPORT_WHITE_VALUE, [ColorMode.RGBW], ColorMode.UNKNOWN)], +) @pytest.mark.parametrize( "config", [ @@ -570,7 +718,14 @@ async def test_off_action_optimistic(hass, start_ha, calls): }, ], ) -async def test_white_value_action_no_template(hass, start_ha, calls): +async def test_white_value_action_no_template( + hass, + start_ha, + calls, + supported_color_modes, + supported_features, + expected_color_mode, +): """Test setting white value with optimistic template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("white_value") is None @@ -586,8 +741,11 @@ async def test_white_value_action_no_template(hass, start_ha, calls): assert calls[0].data["white_value"] == 124 state = hass.states.get("light.test_template_light") - assert state is not None assert state.attributes.get("white_value") == 124 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode # hs_color is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize( @@ -601,6 +759,10 @@ async def test_white_value_action_no_template(hass, start_ha, calls): ], ) @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes,expected_color_mode", + [(SUPPORT_WHITE_VALUE, [ColorMode.RGBW], ColorMode.UNKNOWN)], +) @pytest.mark.parametrize( "config", [ @@ -617,14 +779,29 @@ async def test_white_value_action_no_template(hass, start_ha, calls): }}}}""", ], ) -async def test_white_value_template(hass, expected_white_value, start_ha): +async def test_white_value_template( + hass, + expected_white_value, + start_ha, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test the template for the white value.""" state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("white_value") == expected_white_value + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode # hs_color is None + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes,expected_color_mode", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS], ColorMode.BRIGHTNESS)], +) @pytest.mark.parametrize( "config", [ @@ -655,7 +832,14 @@ async def test_white_value_template(hass, expected_white_value, start_ha): }, ], ) -async def test_level_action_no_template(hass, start_ha, calls): +async def test_level_action_no_template( + hass, + start_ha, + calls, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test setting brightness with optimistic template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("brightness") is None @@ -671,23 +855,33 @@ async def test_level_action_no_template(hass, start_ha, calls): assert calls[0].data["brightness"] == 124 state = hass.states.get("light.test_template_light") - _LOGGER.info(str(state.attributes)) - assert state is not None - assert state.attributes.get("brightness") == 124 + assert state.state == STATE_ON + assert state.attributes["brightness"] == 124 + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( - "expected_level,config_addon", + "expected_level,config_addon,expected_color_mode", [ - (255, {"replace4": '"{{255}}"'}), - (None, {"replace4": '"{{256}}"'}), - (None, {"replace4": '"{{x - 12}}"'}), - (None, {"replace4": '"{{ none }}"'}), - (None, {"replace4": '""'}), - (None, {"replace4": "\"{{ state_attr('light.nolight', 'brightness') }}\""}), + (255, {"replace4": '"{{255}}"'}, ColorMode.BRIGHTNESS), + (None, {"replace4": '"{{256}}"'}, ColorMode.UNKNOWN), + (None, {"replace4": '"{{x - 12}}"'}, ColorMode.UNKNOWN), + (None, {"replace4": '"{{ none }}"'}, ColorMode.UNKNOWN), + (None, {"replace4": '""'}, ColorMode.UNKNOWN), + ( + None, + {"replace4": "\"{{ state_attr('light.nolight', 'brightness') }}\""}, + ColorMode.UNKNOWN, + ), ], ) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], +) @pytest.mark.parametrize( "config", [ @@ -702,25 +896,39 @@ async def test_level_action_no_template(hass, start_ha, calls): }}}}""", ], ) -async def test_level_template(hass, expected_level, start_ha): +async def test_level_template( + hass, + expected_level, + start_ha, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test the template for the level.""" state = hass.states.get("light.test_template_light") - assert state is not None assert state.attributes.get("brightness") == expected_level + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( - "expected_temp,config_addon", + "expected_temp,config_addon,expected_color_mode", [ - (500, {"replace5": '"{{500}}"'}), - (None, {"replace5": '"{{501}}"'}), - (None, {"replace5": '"{{x - 12}}"'}), - (None, {"replace5": '"None"'}), - (None, {"replace5": '"{{ none }}"'}), - (None, {"replace5": '""'}), + (500, {"replace5": '"{{500}}"'}, ColorMode.COLOR_TEMP), + (None, {"replace5": '"{{501}}"'}, ColorMode.UNKNOWN), + (None, {"replace5": '"{{x - 12}}"'}, ColorMode.UNKNOWN), + (None, {"replace5": '"None"'}, ColorMode.UNKNOWN), + (None, {"replace5": '"{{ none }}"'}, ColorMode.UNKNOWN), + (None, {"replace5": '""'}, ColorMode.UNKNOWN), ], ) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP])], +) @pytest.mark.parametrize( "config", [ @@ -752,14 +960,28 @@ async def test_level_template(hass, expected_level, start_ha): }""" ], ) -async def test_temperature_template(hass, expected_temp, start_ha): +async def test_temperature_template( + hass, + expected_temp, + start_ha, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test the template for the temperature.""" state = hass.states.get("light.test_template_light") - assert state is not None assert state.attributes.get("color_temp") == expected_temp + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes,expected_color_mode", + [(SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP], ColorMode.COLOR_TEMP)], +) @pytest.mark.parametrize( "config", [ @@ -790,7 +1012,14 @@ async def test_temperature_template(hass, expected_temp, start_ha): }, ], ) -async def test_temperature_action_no_template(hass, start_ha, calls): +async def test_temperature_action_no_template( + hass, + start_ha, + calls, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test setting temperature with optimistic template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("color_template") is None @@ -806,9 +1035,12 @@ async def test_temperature_action_no_template(hass, start_ha, calls): assert calls[0].data["color_temp"] == 345 state = hass.states.get("light.test_template_light") - _LOGGER.info(str(state.attributes)) assert state is not None assert state.attributes.get("color_temp") == 345 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @@ -949,6 +1181,10 @@ async def test_entity_picture_template(hass, start_ha): @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes, expected_color_mode", + [(SUPPORT_COLOR, [ColorMode.HS], ColorMode.UNKNOWN)], +) @pytest.mark.parametrize( "config", [ @@ -990,7 +1226,14 @@ async def test_entity_picture_template(hass, start_ha): }, ], ) -async def test_color_action_no_template(hass, start_ha, calls): +async def test_color_action_no_template( + hass, + start_ha, + calls, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test setting color with optimistic template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("hs_color") is None @@ -1009,28 +1252,31 @@ async def test_color_action_no_template(hass, start_ha, calls): assert calls[1].data["s"] == 50 state = hass.states.get("light.test_template_light") - _LOGGER.info(str(state.attributes)) - assert state is not None - assert calls[0].data["h"] == 40 - assert calls[0].data["s"] == 50 - assert calls[1].data["h"] == 40 - assert calls[1].data["s"] == 50 + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode # hs_color is None + assert "hs_color" not in state.attributes + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( - "expected_hs,config_addon", + "expected_hs,config_addon,expected_color_mode", [ - ((360, 100), {"replace6": '"{{(360, 100)}}"'}), - ((359.9, 99.9), {"replace6": '"{{(359.9, 99.9)}}"'}), - (None, {"replace6": '"{{(361, 100)}}"'}), - (None, {"replace6": '"{{(360, 101)}}"'}), - (None, {"replace6": '"[{{(360)}},{{null}}]"'}), - (None, {"replace6": '"{{x - 12}}"'}), - (None, {"replace6": '""'}), - (None, {"replace6": '"{{ none }}"'}), + ((360, 100), {"replace6": '"{{(360, 100)}}"'}, ColorMode.HS), + ((359.9, 99.9), {"replace6": '"{{(359.9, 99.9)}}"'}, ColorMode.HS), + (None, {"replace6": '"{{(361, 100)}}"'}, ColorMode.UNKNOWN), + (None, {"replace6": '"{{(360, 101)}}"'}, ColorMode.UNKNOWN), + (None, {"replace6": '"[{{(360)}},{{null}}]"'}, ColorMode.UNKNOWN), + (None, {"replace6": '"{{x - 12}}"'}, ColorMode.UNKNOWN), + (None, {"replace6": '""'}, ColorMode.UNKNOWN), + (None, {"replace6": '"{{ none }}"'}, ColorMode.UNKNOWN), ], ) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_COLOR, [ColorMode.HS])], +) @pytest.mark.parametrize( "config", [ @@ -1045,11 +1291,21 @@ async def test_color_action_no_template(hass, start_ha, calls): }}}}""" ], ) -async def test_color_template(hass, expected_hs, start_ha): +async def test_color_template( + hass, + expected_hs, + start_ha, + supported_features, + supported_color_modes, + expected_color_mode, +): """Test the template for the color.""" state = hass.states.get("light.test_template_light") - assert state is not None assert state.attributes.get("hs_color") == expected_hs + assert state.state == STATE_ON + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) From 054ea77b45cbcb58db842b49f0629d493073cc1a Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Tue, 10 May 2022 13:00:00 +0200 Subject: [PATCH 336/930] Bump devolo-plc-api to 0.8.0 (#71633) --- homeassistant/components/devolo_home_network/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/devolo_home_network/manifest.json b/homeassistant/components/devolo_home_network/manifest.json index 445a383ea14..94f26c8a615 100644 --- a/homeassistant/components/devolo_home_network/manifest.json +++ b/homeassistant/components/devolo_home_network/manifest.json @@ -3,7 +3,7 @@ "name": "devolo Home Network", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/devolo_home_network", - "requirements": ["devolo-plc-api==0.7.1"], + "requirements": ["devolo-plc-api==0.8.0"], "zeroconf": [ { "type": "_dvl-deviceapi._tcp.local.", "properties": { "MT": "*" } } ], diff --git a/requirements_all.txt b/requirements_all.txt index 731e7e1ee2d..69fed66566e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -542,7 +542,7 @@ denonavr==0.10.11 devolo-home-control-api==0.18.1 # homeassistant.components.devolo_home_network -devolo-plc-api==0.7.1 +devolo-plc-api==0.8.0 # homeassistant.components.directv directv==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ca41a5ca264..6bc7b31420d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -397,7 +397,7 @@ denonavr==0.10.11 devolo-home-control-api==0.18.1 # homeassistant.components.devolo_home_network -devolo-plc-api==0.7.1 +devolo-plc-api==0.8.0 # homeassistant.components.directv directv==0.4.0 From 68c2b63ca1e92b0d041a2b0f53eeaf441f8ee83d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 10 May 2022 15:22:12 +0200 Subject: [PATCH 337/930] Fix issue creation links in log messages (#71638) --- homeassistant/components/edl21/sensor.py | 4 ++-- homeassistant/components/recorder/statistics.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/edl21/sensor.py b/homeassistant/components/edl21/sensor.py index 278ac004121..7614dc54ac4 100644 --- a/homeassistant/components/edl21/sensor.py +++ b/homeassistant/components/edl21/sensor.py @@ -329,9 +329,9 @@ class EDL21: self._registered_obis.add((electricity_id, obis)) elif obis not in self._OBIS_BLACKLIST: _LOGGER.warning( - "Unhandled sensor %s detected. Please report at " - 'https://github.com/home-assistant/core/issues?q=is%%3Aissue+label%%3A"integration%%3A+edl21"+', + "Unhandled sensor %s detected. Please report at %s", obis, + "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+edl21%22", ) self._OBIS_BLACKLIST.add(obis) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index d8ba415cc7a..732c042d9c2 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -415,8 +415,8 @@ def delete_duplicates(hass: HomeAssistant, session: Session) -> None: ) if deleted_short_term_statistics_rows: _LOGGER.warning( - "Deleted duplicated short term statistic rows, please report at " - 'https://github.com/home-assistant/core/issues?q=is%%3Aissue+label%%3A"integration%%3A+recorder"+' + "Deleted duplicated short term statistic rows, please report at %s", + "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+recorder%22", ) @@ -1360,8 +1360,8 @@ def _filter_unique_constraint_integrity_error( if ignore: _LOGGER.warning( - "Blocked attempt to insert duplicated statistic rows, please report at " - 'https://github.com/home-assistant/core/issues?q=is%%3Aissue+label%%3A"integration%%3A+recorder"+', + "Blocked attempt to insert duplicated statistic rows, please report at %s", + "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+recorder%22", exc_info=err, ) From 26177bd080b4eb6d11cfd9fbdd158be36f4983d4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 10 May 2022 08:23:13 -0500 Subject: [PATCH 338/930] Convert logbook to use lambda_stmt (#71624) --- homeassistant/components/logbook/__init__.py | 247 ++++++++++--------- tests/components/logbook/test_init.py | 53 ++++ 2 files changed, 179 insertions(+), 121 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 53063d36fc0..a877cba4ff2 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -11,11 +11,13 @@ from typing import Any, cast from aiohttp import web import sqlalchemy +from sqlalchemy import lambda_stmt, select from sqlalchemy.engine.row import Row from sqlalchemy.orm import aliased from sqlalchemy.orm.query import Query -from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select import voluptuous as vol from homeassistant.components import frontend @@ -85,8 +87,6 @@ CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] DOMAIN = "logbook" -GROUP_BY_MINUTES = 15 - EMPTY_JSON_OBJECT = "{}" UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" @@ -435,70 +435,43 @@ def _get_events( def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" - for row in query.yield_per(1000): + if entity_ids or context_id: + rows = query.all() + else: + rows = query.yield_per(1000) + for row in rows: context_lookup.setdefault(row.context_id, row) - if row.event_type != EVENT_CALL_SERVICE and ( - row.event_type == EVENT_STATE_CHANGED - or _keep_row(hass, row, entities_filter) + event_type = row.event_type + if event_type != EVENT_CALL_SERVICE and ( + event_type == EVENT_STATE_CHANGED + or _keep_row(hass, event_type, row, entities_filter) ): yield row if entity_ids is not None: entities_filter = generate_filter([], entity_ids, [], []) + event_types = [ + *ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, + *hass.data.get(DOMAIN, {}), + ] + entity_filter = None + if entity_ids is None and filters: + entity_filter = filters.entity_filter() # type: ignore[no-untyped-call] + stmt = _generate_logbook_query( + start_day, + end_day, + event_types, + entity_ids, + entity_filter, + entity_matches_only, + context_id, + ) with session_scope(hass=hass) as session: - old_state = aliased(States, name="old_state") - query: Query - query = _generate_events_query_without_states(session) - query = _apply_event_time_filter(query, start_day, end_day) - query = _apply_event_types_filter( - hass, query, ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED - ) - - if entity_ids is not None: - if entity_matches_only: - # When entity_matches_only is provided, contexts and events that do not - # contain the entity_ids are not included in the logbook response. - query = _apply_event_entity_id_matchers(query, entity_ids) - query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) - query = query.union_all( - _generate_states_query( - session, start_day, end_day, old_state, entity_ids - ) - ) - else: - if context_id is not None: - query = query.filter(Events.context_id == context_id) - query = query.outerjoin(EventData, (Events.data_id == EventData.data_id)) - - states_query = _generate_states_query( - session, start_day, end_day, old_state, entity_ids - ) - unions: list[Query] = [] - if context_id is not None: - # Once all the old `state_changed` events - # are gone from the database remove the - # _generate_legacy_events_context_id_query - unions.append( - _generate_legacy_events_context_id_query( - session, context_id, start_day, end_day - ) - ) - states_query = states_query.outerjoin( - Events, (States.event_id == Events.event_id) - ) - states_query = states_query.filter(States.context_id == context_id) - elif filters: - states_query = states_query.filter(filters.entity_filter()) # type: ignore[no-untyped-call] - unions.append(states_query) - query = query.union_all(*unions) - - query = query.order_by(Events.time_fired) - return list( _humanify( hass, - yield_rows(query), + yield_rows(session.execute(stmt)), entity_name_cache, event_cache, context_augmenter, @@ -506,8 +479,72 @@ def _get_events( ) -def _generate_events_query_without_data(session: Session) -> Query: - return session.query( +def _generate_logbook_query( + start_day: dt, + end_day: dt, + event_types: list[str], + entity_ids: list[str] | None = None, + entity_filter: Any | None = None, + entity_matches_only: bool = False, + context_id: str | None = None, +) -> StatementLambdaElement: + """Generate a logbook query lambda_stmt.""" + stmt = lambda_stmt( + lambda: _generate_events_query_without_states() + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.event_type.in_(event_types)) + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + if entity_ids is not None: + if entity_matches_only: + # When entity_matches_only is provided, contexts and events that do not + # contain the entity_ids are not included in the logbook response. + stmt.add_criteria( + lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)), + track_on=entity_ids, + ) + stmt += lambda s: s.union_all( + _generate_states_query() + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)) + ) + else: + if context_id is not None: + # Once all the old `state_changed` events + # are gone from the database remove the + # union_all(_generate_legacy_events_context_id_query()....) + stmt += lambda s: s.where(Events.context_id == context_id).union_all( + _generate_legacy_events_context_id_query() + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.context_id == context_id), + _generate_states_query() + .where( + (States.last_updated > start_day) & (States.last_updated < end_day) + ) + .outerjoin(Events, (States.event_id == Events.event_id)) + .where(States.context_id == context_id), + ) + elif entity_filter is not None: + stmt += lambda s: s.union_all( + _generate_states_query() + .where( + (States.last_updated > start_day) & (States.last_updated < end_day) + ) + .where(entity_filter) + ) + else: + stmt += lambda s: s.union_all( + _generate_states_query().where( + (States.last_updated > start_day) & (States.last_updated < end_day) + ) + ) + + stmt += lambda s: s.order_by(Events.time_fired) + return stmt + + +def _generate_events_query_without_data() -> Select: + return select( literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label("event_type"), literal(value=None, type_=sqlalchemy.Text).label("event_data"), States.last_changed.label("time_fired"), @@ -519,65 +556,48 @@ def _generate_events_query_without_data(session: Session) -> Query: ) -def _generate_legacy_events_context_id_query( - session: Session, - context_id: str, - start_day: dt, - end_day: dt, -) -> Query: +def _generate_legacy_events_context_id_query() -> Select: """Generate a legacy events context id query that also joins states.""" # This can be removed once we no longer have event_ids in the states table - legacy_context_id_query = session.query( - *EVENT_COLUMNS, - literal(value=None, type_=sqlalchemy.String).label("shared_data"), - States.state, - States.entity_id, - States.attributes, - StateAttributes.shared_attrs, - ) - legacy_context_id_query = _apply_event_time_filter( - legacy_context_id_query, start_day, end_day - ) return ( - legacy_context_id_query.filter(Events.context_id == context_id) + select( + *EVENT_COLUMNS, + literal(value=None, type_=sqlalchemy.String).label("shared_data"), + States.state, + States.entity_id, + States.attributes, + StateAttributes.shared_attrs, + ) .outerjoin(States, (Events.event_id == States.event_id)) - .filter(States.last_updated == States.last_changed) - .filter(_not_continuous_entity_matcher()) + .where(States.last_updated == States.last_changed) + .where(_not_continuous_entity_matcher()) .outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) ) -def _generate_events_query_without_states(session: Session) -> Query: - return session.query( +def _generate_events_query_without_states() -> Select: + return select( *EVENT_COLUMNS, EventData.shared_data.label("shared_data"), *EMPTY_STATE_COLUMNS ) -def _generate_states_query( - session: Session, - start_day: dt, - end_day: dt, - old_state: States, - entity_ids: Iterable[str] | None, -) -> Query: - query = ( - _generate_events_query_without_data(session) +def _generate_states_query() -> Select: + old_state = aliased(States, name="old_state") + return ( + _generate_events_query_without_data() .outerjoin(old_state, (States.old_state_id == old_state.state_id)) - .filter(_missing_state_matcher(old_state)) - .filter(_not_continuous_entity_matcher()) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .filter(States.last_updated == States.last_changed) - ) - if entity_ids: - query = query.filter(States.entity_id.in_(entity_ids)) - return query.outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + .where(_missing_state_matcher(old_state)) + .where(_not_continuous_entity_matcher()) + .where(States.last_updated == States.last_changed) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) ) -def _missing_state_matcher(old_state: States) -> Any: +def _missing_state_matcher(old_state: States) -> sqlalchemy.and_: # The below removes state change events that do not have # and old_state or the old_state is missing (newly added entities) # or the new_state is missing (removed entities) @@ -588,7 +608,7 @@ def _missing_state_matcher(old_state: States) -> Any: ) -def _not_continuous_entity_matcher() -> Any: +def _not_continuous_entity_matcher() -> sqlalchemy.or_: """Match non continuous entities.""" return sqlalchemy.or_( _not_continuous_domain_matcher(), @@ -598,7 +618,7 @@ def _not_continuous_entity_matcher() -> Any: ) -def _not_continuous_domain_matcher() -> Any: +def _not_continuous_domain_matcher() -> sqlalchemy.and_: """Match not continuous domains.""" return sqlalchemy.and_( *[ @@ -608,7 +628,7 @@ def _not_continuous_domain_matcher() -> Any: ).self_group() -def _continuous_domain_matcher() -> Any: +def _continuous_domain_matcher() -> sqlalchemy.or_: """Match continuous domains.""" return sqlalchemy.or_( *[ @@ -625,37 +645,22 @@ def _not_uom_attributes_matcher() -> Any: ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) -def _apply_event_time_filter(events_query: Query, start_day: dt, end_day: dt) -> Query: - return events_query.filter( - (Events.time_fired > start_day) & (Events.time_fired < end_day) - ) - - -def _apply_event_types_filter( - hass: HomeAssistant, query: Query, event_types: list[str] -) -> Query: - return query.filter( - Events.event_type.in_(event_types + list(hass.data.get(DOMAIN, {}))) - ) - - -def _apply_event_entity_id_matchers( - events_query: Query, entity_ids: Iterable[str] -) -> Query: +def _apply_event_entity_id_matchers(entity_ids: Iterable[str]) -> sqlalchemy.or_: + """Create matchers for the entity_id in the event_data.""" ors = [] for entity_id in entity_ids: like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) ors.append(Events.event_data.like(like)) ors.append(EventData.shared_data.like(like)) - return events_query.filter(sqlalchemy.or_(*ors)) + return sqlalchemy.or_(*ors) def _keep_row( hass: HomeAssistant, + event_type: str, row: Row, entities_filter: EntityFilter | Callable[[str], bool] | None = None, ) -> bool: - event_type = row.event_type if event_type in HOMEASSISTANT_EVENTS: return entities_filter is None or entities_filter(HA_DOMAIN_ENTITY_ID) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 4f961dcd2c1..76319ba5a6e 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -1390,6 +1390,59 @@ async def test_logbook_entity_matches_only(hass, hass_client, recorder_mock): assert json_dict[1]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" +async def test_logbook_entity_matches_only_multiple_calls( + hass, hass_client, recorder_mock +): + """Test the logbook view with a single entity and entity_matches_only called multiple times.""" + await async_setup_component(hass, "logbook", {}) + await async_setup_component(hass, "automation", {}) + + await async_recorder_block_till_done(hass) + await hass.async_block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + for automation_id in range(5): + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + { + ATTR_NAME: f"Mock automation {automation_id}", + ATTR_ENTITY_ID: f"automation.mock_{automation_id}_automation", + }, + ) + await async_wait_recording_done(hass) + client = await hass_client() + + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + end_time = start + timedelta(hours=24) + + for automation_id in range(5): + # Test today entries with filter by end_time + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_{automation_id}_automation&entity_matches_only" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert len(json_dict) == 1 + assert ( + json_dict[0]["entity_id"] == f"automation.mock_{automation_id}_automation" + ) + + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_0_automation,automation.mock_1_automation,automation.mock_2_automation&entity_matches_only" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert len(json_dict) == 3 + assert json_dict[0]["entity_id"] == "automation.mock_0_automation" + assert json_dict[1]["entity_id"] == "automation.mock_1_automation" + assert json_dict[2]["entity_id"] == "automation.mock_2_automation" + + async def test_custom_log_entry_discoverable_via_entity_matches_only( hass, hass_client, recorder_mock ): From ef16e6c129485f654a765fb3dbad27a605e4f0c4 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 10 May 2022 15:28:44 +0200 Subject: [PATCH 339/930] Fix Plugwise recovering from aiohttp client error (#71642) --- homeassistant/components/plugwise/gateway.py | 3 ++- tests/components/plugwise/test_init.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/plugwise/gateway.py b/homeassistant/components/plugwise/gateway.py index f540c6d0b9c..648765155e4 100644 --- a/homeassistant/components/plugwise/gateway.py +++ b/homeassistant/components/plugwise/gateway.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio from typing import Any +from aiohttp import ClientConnectionError from plugwise.exceptions import InvalidAuthentication, PlugwiseException from plugwise.smile import Smile @@ -44,7 +45,7 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool: except InvalidAuthentication: LOGGER.error("Invalid username or Smile ID") return False - except PlugwiseException as err: + except (ClientConnectionError, PlugwiseException) as err: raise ConfigEntryNotReady( f"Error while communicating to device {api.smile_name}" ) from err diff --git a/tests/components/plugwise/test_init.py b/tests/components/plugwise/test_init.py index 811018db7e2..557680cb060 100644 --- a/tests/components/plugwise/test_init.py +++ b/tests/components/plugwise/test_init.py @@ -2,6 +2,7 @@ import asyncio from unittest.mock import MagicMock +import aiohttp from plugwise.exceptions import ( ConnectionFailedError, PlugwiseException, @@ -49,6 +50,7 @@ async def test_load_unload_config_entry( (PlugwiseException), (XMLDataMissingError), (asyncio.TimeoutError), + (aiohttp.ClientConnectionError), ], ) async def test_config_entry_not_ready( From c994d06967975a63a9b43ef0f1dc30cfd49432c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 10 May 2022 16:48:26 +0200 Subject: [PATCH 340/930] Update aioqsw to 0.0.8 (#71640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements PEP 561, which allows to remove coordinator cast. Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/qnap_qsw/coordinator.py | 4 ++-- homeassistant/components/qnap_qsw/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/qnap_qsw/coordinator.py b/homeassistant/components/qnap_qsw/coordinator.py index 7aa1b6f0a85..c018c1f3848 100644 --- a/homeassistant/components/qnap_qsw/coordinator.py +++ b/homeassistant/components/qnap_qsw/coordinator.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import timedelta import logging -from typing import Any, cast +from typing import Any from aioqsw.exceptions import QswError from aioqsw.localapi import QnapQswApi @@ -40,4 +40,4 @@ class QswUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): await self.qsw.update() except QswError as error: raise UpdateFailed(error) from error - return cast(dict[str, Any], self.qsw.data()) + return self.qsw.data() diff --git a/homeassistant/components/qnap_qsw/manifest.json b/homeassistant/components/qnap_qsw/manifest.json index 0624edd000c..9331a7df468 100644 --- a/homeassistant/components/qnap_qsw/manifest.json +++ b/homeassistant/components/qnap_qsw/manifest.json @@ -3,7 +3,7 @@ "name": "QNAP QSW", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/qnap_qsw", - "requirements": ["aioqsw==0.0.7"], + "requirements": ["aioqsw==0.0.8"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioqsw"] diff --git a/requirements_all.txt b/requirements_all.txt index 69fed66566e..9f3f4b00e33 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -226,7 +226,7 @@ aiopvpc==3.0.0 aiopyarr==22.2.2 # homeassistant.components.qnap_qsw -aioqsw==0.0.7 +aioqsw==0.0.8 # homeassistant.components.recollect_waste aiorecollect==1.0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6bc7b31420d..8104911a6ec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -192,7 +192,7 @@ aiopvpc==3.0.0 aiopyarr==22.2.2 # homeassistant.components.qnap_qsw -aioqsw==0.0.7 +aioqsw==0.0.8 # homeassistant.components.recollect_waste aiorecollect==1.0.8 From 45290c4c0920e64a2faca5adb05d8440d215d44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 10 May 2022 16:49:40 +0200 Subject: [PATCH 341/930] Update aioairzone to 0.4.4 (#71641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements PEP 561, which allows to remove coordinator cast for strict typing. Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/coordinator.py | 4 ++-- homeassistant/components/airzone/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/airzone/coordinator.py b/homeassistant/components/airzone/coordinator.py index 5ce9dcc45ef..ba0296557a1 100644 --- a/homeassistant/components/airzone/coordinator.py +++ b/homeassistant/components/airzone/coordinator.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import timedelta import logging -from typing import Any, cast +from typing import Any from aioairzone.exceptions import AirzoneError from aioairzone.localapi import AirzoneLocalApi @@ -40,4 +40,4 @@ class AirzoneUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): await self.airzone.update() except AirzoneError as error: raise UpdateFailed(error) from error - return cast(dict[str, Any], self.airzone.data()) + return self.airzone.data() diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index 7a04b3a78b3..e0d3bd6df09 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -3,7 +3,7 @@ "name": "Airzone", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/airzone", - "requirements": ["aioairzone==0.4.3"], + "requirements": ["aioairzone==0.4.4"], "codeowners": ["@Noltari"], "iot_class": "local_polling", "loggers": ["aioairzone"] diff --git a/requirements_all.txt b/requirements_all.txt index 9f3f4b00e33..8f80e5026f2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -110,7 +110,7 @@ aio_geojson_nsw_rfs_incidents==0.4 aio_georss_gdacs==0.7 # homeassistant.components.airzone -aioairzone==0.4.3 +aioairzone==0.4.4 # homeassistant.components.ambient_station aioambient==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8104911a6ec..6849524c94c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -94,7 +94,7 @@ aio_geojson_nsw_rfs_incidents==0.4 aio_georss_gdacs==0.7 # homeassistant.components.airzone -aioairzone==0.4.3 +aioairzone==0.4.4 # homeassistant.components.ambient_station aioambient==2021.11.0 From 220589877168e8b0c59f8cd8d7fdcd55072184a8 Mon Sep 17 00:00:00 2001 From: rappenze Date: Tue, 10 May 2022 22:33:40 +0200 Subject: [PATCH 342/930] Fix wrong brightness level change visible in UI (#71655) --- homeassistant/components/fibaro/light.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index 9d0309bc4ee..08a9e651668 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -112,6 +112,7 @@ class FibaroLight(FibaroDevice, LightEntity): if ATTR_BRIGHTNESS in kwargs: self._attr_brightness = kwargs[ATTR_BRIGHTNESS] self.set_level(scaleto99(self._attr_brightness)) + return if ATTR_RGB_COLOR in kwargs: # Update based on parameters From 945eba9aa384f2348e082ddb981f897a9465040f Mon Sep 17 00:00:00 2001 From: Graham Arthur Blair Date: Tue, 10 May 2022 13:49:38 -0700 Subject: [PATCH 343/930] Change Ring Chime play sound Buttons to a Siren (#71449) --- homeassistant/components/ring/__init__.py | 2 +- homeassistant/components/ring/button.py | 65 ----------- homeassistant/components/ring/siren.py | 51 +++++++++ tests/components/ring/test_button.py | 55 --------- tests/components/ring/test_siren.py | 132 ++++++++++++++++++++++ 5 files changed, 184 insertions(+), 121 deletions(-) delete mode 100644 homeassistant/components/ring/button.py create mode 100644 homeassistant/components/ring/siren.py delete mode 100644 tests/components/ring/test_button.py create mode 100644 tests/components/ring/test_siren.py diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 2a922a7bb3e..a1ed1ac017b 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -32,11 +32,11 @@ DEFAULT_ENTITY_NAMESPACE = "ring" PLATFORMS = [ Platform.BINARY_SENSOR, - Platform.BUTTON, Platform.LIGHT, Platform.SENSOR, Platform.SWITCH, Platform.CAMERA, + Platform.SIREN, ] diff --git a/homeassistant/components/ring/button.py b/homeassistant/components/ring/button.py deleted file mode 100644 index 04f9f7950f8..00000000000 --- a/homeassistant/components/ring/button.py +++ /dev/null @@ -1,65 +0,0 @@ -"""This component provides HA button support for Ring Chimes.""" -import logging - -from homeassistant.components.button import ButtonEntity -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddEntitiesCallback - -from . import DOMAIN -from .entity import RingEntityMixin - -_LOGGER = logging.getLogger(__name__) - -BELL_ICON = "mdi:bell-ring" - - -async def async_setup_entry( - hass: HomeAssistant, - config_entry: ConfigEntry, - async_add_entities: AddEntitiesCallback, -) -> None: - """Create the buttons for the Ring devices.""" - devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] - buttons = [] - - # add one button for each test chime type (ding, motion) - for device in devices["chimes"]: - buttons.append(ChimeButton(config_entry.entry_id, device, "ding")) - buttons.append(ChimeButton(config_entry.entry_id, device, "motion")) - - async_add_entities(buttons) - - -class BaseRingButton(RingEntityMixin, ButtonEntity): - """Represents a Button for controlling an aspect of a ring device.""" - - def __init__(self, config_entry_id, device, button_identifier, button_name): - """Initialize the switch.""" - super().__init__(config_entry_id, device) - self._button_identifier = button_identifier - self._button_name = button_name - self._attr_unique_id = f"{self._device.id}-{self._button_identifier}" - - @property - def name(self): - """Name of the device.""" - return f"{self._device.name} {self._button_name}" - - -class ChimeButton(BaseRingButton): - """Creates a button to play the test chime of a Chime device.""" - - _attr_icon = BELL_ICON - - def __init__(self, config_entry_id, device, kind): - """Initialize the button for a device with a chime.""" - super().__init__( - config_entry_id, device, f"play-chime-{kind}", f"Play chime: {kind}" - ) - self.kind = kind - - def press(self) -> None: - """Send the test chime request.""" - if not self._device.test_sound(kind=self.kind): - _LOGGER.error("Failed to ring chime sound on %s", self.name) diff --git a/homeassistant/components/ring/siren.py b/homeassistant/components/ring/siren.py new file mode 100644 index 00000000000..b83d3e7b2ae --- /dev/null +++ b/homeassistant/components/ring/siren.py @@ -0,0 +1,51 @@ +"""This component provides HA Siren support for Ring Chimes.""" +import logging +from typing import Any + +from ring_doorbell.const import CHIME_TEST_SOUND_KINDS, KIND_DING + +from homeassistant.components.siren import ATTR_TONE, SirenEntity, SirenEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import DOMAIN +from .entity import RingEntityMixin + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Create the sirens for the Ring devices.""" + devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] + sirens = [] + + for device in devices["chimes"]: + sirens.append(RingChimeSiren(config_entry, device)) + + async_add_entities(sirens) + + +class RingChimeSiren(RingEntityMixin, SirenEntity): + """Creates a siren to play the test chimes of a Chime device.""" + + def __init__(self, config_entry: ConfigEntry, device) -> None: + """Initialize a Ring Chime siren.""" + super().__init__(config_entry.entry_id, device) + # Entity class attributes + self._attr_name = f"{self._device.name} Siren" + self._attr_unique_id = f"{self._device.id}-siren" + self._attr_available_tones = CHIME_TEST_SOUND_KINDS + self._attr_supported_features = ( + SirenEntityFeature.TURN_ON | SirenEntityFeature.TONES + ) + + def turn_on(self, **kwargs: Any) -> None: + """Play the test sound on a Ring Chime device.""" + tone = kwargs.get(ATTR_TONE) or KIND_DING + + self._device.test_sound(kind=tone) diff --git a/tests/components/ring/test_button.py b/tests/components/ring/test_button.py deleted file mode 100644 index 62b2fcd8a78..00000000000 --- a/tests/components/ring/test_button.py +++ /dev/null @@ -1,55 +0,0 @@ -"""The tests for the Ring button platform.""" - -from homeassistant.const import Platform -from homeassistant.helpers import entity_registry as er - -from .common import setup_platform - - -async def test_entity_registry(hass, requests_mock): - """Tests that the devices are registered in the entity registry.""" - await setup_platform(hass, Platform.BUTTON) - entity_registry = er.async_get(hass) - - entry = entity_registry.async_get("button.downstairs_play_chime_ding") - assert entry.unique_id == "123456-play-chime-ding" - - entry = entity_registry.async_get("button.downstairs_play_chime_motion") - assert entry.unique_id == "123456-play-chime-motion" - - -async def test_play_chime_buttons_report_correctly(hass, requests_mock): - """Tests that the initial state of a device that should be on is correct.""" - await setup_platform(hass, Platform.BUTTON) - - state = hass.states.get("button.downstairs_play_chime_ding") - assert state.attributes.get("friendly_name") == "Downstairs Play chime: ding" - assert state.attributes.get("icon") == "mdi:bell-ring" - - state = hass.states.get("button.downstairs_play_chime_motion") - assert state.attributes.get("friendly_name") == "Downstairs Play chime: motion" - assert state.attributes.get("icon") == "mdi:bell-ring" - - -async def test_chime_can_be_played(hass, requests_mock): - """Tests the play chime request is sent correctly.""" - await setup_platform(hass, Platform.BUTTON) - - # Mocks the response for playing a test sound - requests_mock.post( - "https://api.ring.com/clients_api/chimes/123456/play_sound", - text="SUCCESS", - ) - await hass.services.async_call( - "button", - "press", - {"entity_id": "button.downstairs_play_chime_ding"}, - blocking=True, - ) - - await hass.async_block_till_done() - - assert requests_mock.request_history[-1].url.startswith( - "https://api.ring.com/clients_api/chimes/123456/play_sound?" - ) - assert "kind=ding" in requests_mock.request_history[-1].url diff --git a/tests/components/ring/test_siren.py b/tests/components/ring/test_siren.py new file mode 100644 index 00000000000..bb282769e57 --- /dev/null +++ b/tests/components/ring/test_siren.py @@ -0,0 +1,132 @@ +"""The tests for the Ring button platform.""" + +from homeassistant.const import Platform +from homeassistant.helpers import entity_registry as er + +from .common import setup_platform + + +async def test_entity_registry(hass, requests_mock): + """Tests that the devices are registered in the entity registry.""" + await setup_platform(hass, Platform.SIREN) + entity_registry = er.async_get(hass) + + entry = entity_registry.async_get("siren.downstairs_siren") + assert entry.unique_id == "123456-siren" + + +async def test_sirens_report_correctly(hass, requests_mock): + """Tests that the initial state of a device that should be on is correct.""" + await setup_platform(hass, Platform.SIREN) + + state = hass.states.get("siren.downstairs_siren") + assert state.attributes.get("friendly_name") == "Downstairs Siren" + assert state.state == "unknown" + + +async def test_default_ding_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_toggle_plays_default_chime(hass, requests_mock): + """Tests the play chime request is sent correctly when toggled.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "toggle", + {"entity_id": "siren.downstairs_siren"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_explicit_ding_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren", "tone": "ding"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=ding" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" + + +async def test_motion_chime_can_be_played(hass, requests_mock): + """Tests the play chime request is sent correctly.""" + await setup_platform(hass, Platform.SIREN) + + # Mocks the response for playing a test sound + requests_mock.post( + "https://api.ring.com/clients_api/chimes/123456/play_sound", + text="SUCCESS", + ) + await hass.services.async_call( + "siren", + "turn_on", + {"entity_id": "siren.downstairs_siren", "tone": "motion"}, + blocking=True, + ) + + await hass.async_block_till_done() + + assert requests_mock.request_history[-1].url.startswith( + "https://api.ring.com/clients_api/chimes/123456/play_sound?" + ) + assert "kind=motion" in requests_mock.request_history[-1].url + + state = hass.states.get("siren.downstairs_siren") + assert state.state == "unknown" From dcbac86fc04c864e3b6b4bb592134d192e5ccbf5 Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Tue, 10 May 2022 19:21:46 -0400 Subject: [PATCH 344/930] Bump up ZHA dependencies (#71663) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index d4e71562b07..8ea9e9d8d4a 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ - "bellows==0.29.0", + "bellows==0.30.0", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.73", diff --git a/requirements_all.txt b/requirements_all.txt index 8f80e5026f2..f3204e93502 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -394,7 +394,7 @@ beautifulsoup4==4.11.1 # beewi_smartclim==0.0.10 # homeassistant.components.zha -bellows==0.29.0 +bellows==0.30.0 # homeassistant.components.bmw_connected_drive bimmer_connected==0.8.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6849524c94c..f4b109b6b07 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -303,7 +303,7 @@ base36==0.1.1 beautifulsoup4==4.11.1 # homeassistant.components.zha -bellows==0.29.0 +bellows==0.30.0 # homeassistant.components.bmw_connected_drive bimmer_connected==0.8.12 From 3d2b0a17cedbac417ac29c3e2f0f3110f7d83044 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 11 May 2022 00:22:31 +0000 Subject: [PATCH 345/930] [ci skip] Translation update --- .../components/dlna_dmr/translations/ja.json | 1 + .../components/fan/translations/ca.json | 1 + .../components/fan/translations/de.json | 1 + .../components/fan/translations/et.json | 1 + .../components/fan/translations/fr.json | 1 + .../components/fan/translations/hu.json | 1 + .../components/fan/translations/id.json | 1 + .../components/fan/translations/ja.json | 1 + .../components/fan/translations/nl.json | 1 + .../components/fan/translations/pl.json | 1 + .../components/fan/translations/ru.json | 1 + .../components/fan/translations/zh-Hant.json | 1 + .../components/hue/translations/ja.json | 1 + .../humidifier/translations/fr.json | 2 +- .../components/insteon/translations/ja.json | 1 + .../components/onewire/translations/bg.json | 2 ++ .../components/onewire/translations/et.json | 4 ++- .../components/onewire/translations/ru.json | 2 ++ .../components/qnap_qsw/translations/ja.json | 8 +++++ .../components/recorder/translations/ja.json | 7 ++++ .../components/sabnzbd/translations/ja.json | 18 ++++++++++ .../components/slimproto/translations/ja.json | 7 ++++ .../components/sms/translations/ru.json | 1 + .../components/sql/translations/ja.json | 11 +++++- .../steam_online/translations/ja.json | 16 ++++++++- .../components/tautulli/translations/ja.json | 3 +- .../trafikverket_ferry/translations/ja.json | 3 ++ .../ukraine_alarm/translations/bg.json | 34 +++++++++++++++++++ .../ukraine_alarm/translations/ca.json | 2 +- .../ukraine_alarm/translations/ja.json | 3 +- .../components/ws66i/translations/bg.json | 18 ++++++++++ .../components/ws66i/translations/ja.json | 18 +++++++++- .../translations/select.fr.json | 2 +- 33 files changed, 166 insertions(+), 9 deletions(-) create mode 100644 homeassistant/components/recorder/translations/ja.json create mode 100644 homeassistant/components/sabnzbd/translations/ja.json create mode 100644 homeassistant/components/slimproto/translations/ja.json create mode 100644 homeassistant/components/ukraine_alarm/translations/bg.json create mode 100644 homeassistant/components/ws66i/translations/bg.json diff --git a/homeassistant/components/dlna_dmr/translations/ja.json b/homeassistant/components/dlna_dmr/translations/ja.json index 9edb9156534..4c71d57b468 100644 --- a/homeassistant/components/dlna_dmr/translations/ja.json +++ b/homeassistant/components/dlna_dmr/translations/ja.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "\u30d6\u30e9\u30a6\u30ba\u6642\u306b\u4e92\u63db\u6027\u306e\u306a\u3044\u30e1\u30c7\u30a3\u30a2\u3092\u8868\u793a\u3059\u308b", "callback_url_override": "\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u306e\u30b3\u30fc\u30eb\u30d0\u30c3\u30afURL", "listen_port": "\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u30dd\u30fc\u30c8(\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u30e9\u30f3\u30c0\u30e0)", "poll_availability": "\u30c7\u30d0\u30a4\u30b9\u306e\u53ef\u7528\u6027\u3092\u30dd\u30fc\u30ea\u30f3\u30b0" diff --git a/homeassistant/components/fan/translations/ca.json b/homeassistant/components/fan/translations/ca.json index 274ddddbbaf..206933b1c9a 100644 --- a/homeassistant/components/fan/translations/ca.json +++ b/homeassistant/components/fan/translations/ca.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Commuta {entity_name}", "turn_off": "Apaga {entity_name}", "turn_on": "Enc\u00e9n {entity_name}" }, diff --git a/homeassistant/components/fan/translations/de.json b/homeassistant/components/fan/translations/de.json index 43a7a42f808..5048a576bee 100644 --- a/homeassistant/components/fan/translations/de.json +++ b/homeassistant/components/fan/translations/de.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "{entity_name} umschalten", "turn_off": "Schalte {entity_name} aus.", "turn_on": "Schalte {entity_name} ein." }, diff --git a/homeassistant/components/fan/translations/et.json b/homeassistant/components/fan/translations/et.json index 4a46384ae3a..d168ca75158 100644 --- a/homeassistant/components/fan/translations/et.json +++ b/homeassistant/components/fan/translations/et.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Muuda {entity_name} olekut", "turn_off": "L\u00fclita {entity_name} v\u00e4lja", "turn_on": "L\u00fclita {entity_name} sisse" }, diff --git a/homeassistant/components/fan/translations/fr.json b/homeassistant/components/fan/translations/fr.json index e9cf666b725..1ff0ed5eea9 100644 --- a/homeassistant/components/fan/translations/fr.json +++ b/homeassistant/components/fan/translations/fr.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Basculer {entity_name}", "turn_off": "\u00c9teindre {entity_name}", "turn_on": "Allumer {entity_name}" }, diff --git a/homeassistant/components/fan/translations/hu.json b/homeassistant/components/fan/translations/hu.json index 50e659b001a..e7ef69d8800 100644 --- a/homeassistant/components/fan/translations/hu.json +++ b/homeassistant/components/fan/translations/hu.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "{entity_name} kapcsol\u00e1sa", "turn_off": "{entity_name} kikapcsol\u00e1sa", "turn_on": "{entity_name} bekapcsol\u00e1sa" }, diff --git a/homeassistant/components/fan/translations/id.json b/homeassistant/components/fan/translations/id.json index c21b90a495a..85fa77539c3 100644 --- a/homeassistant/components/fan/translations/id.json +++ b/homeassistant/components/fan/translations/id.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Nyala/matikan {entity_name}", "turn_off": "Matikan {entity_name}", "turn_on": "Nyalakan {entity_name}" }, diff --git a/homeassistant/components/fan/translations/ja.json b/homeassistant/components/fan/translations/ja.json index 62b37a0b5df..75d528404a2 100644 --- a/homeassistant/components/fan/translations/ja.json +++ b/homeassistant/components/fan/translations/ja.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "\u30c8\u30b0\u30eb {entity_name}", "turn_off": "\u30aa\u30d5\u306b\u3059\u308b {entity_name}", "turn_on": "\u30aa\u30f3\u306b\u3059\u308b {entity_name}" }, diff --git a/homeassistant/components/fan/translations/nl.json b/homeassistant/components/fan/translations/nl.json index aa4474a782f..bdc2f64195b 100644 --- a/homeassistant/components/fan/translations/nl.json +++ b/homeassistant/components/fan/translations/nl.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Schakel {entity_name}", "turn_off": "Schakel {entity_name} uit", "turn_on": "Schakel {entity_name} in" }, diff --git a/homeassistant/components/fan/translations/pl.json b/homeassistant/components/fan/translations/pl.json index 9e4f6b45341..9cfc6fa9b28 100644 --- a/homeassistant/components/fan/translations/pl.json +++ b/homeassistant/components/fan/translations/pl.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "prze\u0142\u0105cz {entity_name}", "turn_off": "wy\u0142\u0105cz {entity_name}", "turn_on": "w\u0142\u0105cz {entity_name}" }, diff --git a/homeassistant/components/fan/translations/ru.json b/homeassistant/components/fan/translations/ru.json index ec667b78ee1..5a4571f2dad 100644 --- a/homeassistant/components/fan/translations/ru.json +++ b/homeassistant/components/fan/translations/ru.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "{entity_name}: \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c", "turn_off": "{entity_name}: \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c", "turn_on": "{entity_name}: \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c" }, diff --git a/homeassistant/components/fan/translations/zh-Hant.json b/homeassistant/components/fan/translations/zh-Hant.json index 571040c820a..ba68f7c5158 100644 --- a/homeassistant/components/fan/translations/zh-Hant.json +++ b/homeassistant/components/fan/translations/zh-Hant.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "\u5207\u63db{entity_name}", "turn_off": "\u95dc\u9589{entity_name}", "turn_on": "\u958b\u555f{entity_name}" }, diff --git a/homeassistant/components/hue/translations/ja.json b/homeassistant/components/hue/translations/ja.json index 7a99db7e498..ec88173adca 100644 --- a/homeassistant/components/hue/translations/ja.json +++ b/homeassistant/components/hue/translations/ja.json @@ -6,6 +6,7 @@ "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "discover_timeout": "Hue bridge\u3092\u767a\u898b(\u63a2\u308a\u5f53\u3066)\u3067\u304d\u307e\u305b\u3093", + "invalid_host": "\u7121\u52b9\u306a\u30db\u30b9\u30c8", "no_bridges": "Hue bridge\u306f\u767a\u898b\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f", "not_hue_bridge": "Hue bridge\u3067\u306f\u3042\u308a\u307e\u305b\u3093", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" diff --git a/homeassistant/components/humidifier/translations/fr.json b/homeassistant/components/humidifier/translations/fr.json index 84b24b96904..4caf57d81b5 100644 --- a/homeassistant/components/humidifier/translations/fr.json +++ b/homeassistant/components/humidifier/translations/fr.json @@ -3,7 +3,7 @@ "action_type": { "set_humidity": "R\u00e9gler l'humidit\u00e9 pour {nom_entit\u00e9}", "set_mode": "Changer le mode sur {nom_entit\u00e9}.", - "toggle": "Inverser {nom_entit\u00e9}", + "toggle": "Basculer {entity_name}", "turn_off": "\u00c9teindre {entity_name}", "turn_on": "Allumer {entity_name}" }, diff --git a/homeassistant/components/insteon/translations/ja.json b/homeassistant/components/insteon/translations/ja.json index 0b8d93518bd..81b7f246e41 100644 --- a/homeassistant/components/insteon/translations/ja.json +++ b/homeassistant/components/insteon/translations/ja.json @@ -2,6 +2,7 @@ "config": { "abort": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "not_insteon_device": "\u691c\u51fa\u3055\u308c\u305f\u30c7\u30d0\u30a4\u30b9\u306f\u3001Insteon\u30c7\u30d0\u30a4\u30b9\u3067\u306f\u306a\u3044", "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" }, "error": { diff --git a/homeassistant/components/onewire/translations/bg.json b/homeassistant/components/onewire/translations/bg.json index e45a2db7197..26dd6f15686 100644 --- a/homeassistant/components/onewire/translations/bg.json +++ b/homeassistant/components/onewire/translations/bg.json @@ -15,6 +15,8 @@ }, "user": { "data": { + "host": "\u0425\u043e\u0441\u0442", + "port": "\u041f\u043e\u0440\u0442", "type": "\u0412\u0438\u0434 \u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0430\u0442\u0430" } } diff --git a/homeassistant/components/onewire/translations/et.json b/homeassistant/components/onewire/translations/et.json index bb00ce8fbbc..7e57911127e 100644 --- a/homeassistant/components/onewire/translations/et.json +++ b/homeassistant/components/onewire/translations/et.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Host", + "port": "Port", "type": "\u00dchenduse t\u00fc\u00fcp" }, - "title": "Seadista 1-wire sidumine" + "title": "M\u00e4\u00e4ra serveri \u00fcksikasjad" } } }, diff --git a/homeassistant/components/onewire/translations/ru.json b/homeassistant/components/onewire/translations/ru.json index 4bc24f85f19..d0ab8b8a282 100644 --- a/homeassistant/components/onewire/translations/ru.json +++ b/homeassistant/components/onewire/translations/ru.json @@ -17,6 +17,8 @@ }, "user": { "data": { + "host": "\u0425\u043e\u0441\u0442", + "port": "\u041f\u043e\u0440\u0442", "type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" }, "title": "1-Wire" diff --git a/homeassistant/components/qnap_qsw/translations/ja.json b/homeassistant/components/qnap_qsw/translations/ja.json index 38e13174ac8..0658405fd3f 100644 --- a/homeassistant/components/qnap_qsw/translations/ja.json +++ b/homeassistant/components/qnap_qsw/translations/ja.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "invalid_id": "\u30c7\u30d0\u30a4\u30b9\u304c\u7121\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u8fd4\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/recorder/translations/ja.json b/homeassistant/components/recorder/translations/ja.json new file mode 100644 index 00000000000..2aadb11ac33 --- /dev/null +++ b/homeassistant/components/recorder/translations/ja.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "estimated_db_size": "\u63a8\u5b9a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30a4\u30ba(MiB)" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/ja.json b/homeassistant/components/sabnzbd/translations/ja.json new file mode 100644 index 00000000000..737bbfe4140 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/ja.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc" + }, + "step": { + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "name": "\u540d\u524d", + "path": "\u30d1\u30b9", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/ja.json b/homeassistant/components/slimproto/translations/ja.json new file mode 100644 index 00000000000..4b8d0691b68 --- /dev/null +++ b/homeassistant/components/slimproto/translations/ja.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sms/translations/ru.json b/homeassistant/components/sms/translations/ru.json index 2200b582123..e5daa2005c0 100644 --- a/homeassistant/components/sms/translations/ru.json +++ b/homeassistant/components/sms/translations/ru.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445", "device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" }, "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" diff --git a/homeassistant/components/sql/translations/ja.json b/homeassistant/components/sql/translations/ja.json index cf03616fbb9..6fa037efc5f 100644 --- a/homeassistant/components/sql/translations/ja.json +++ b/homeassistant/components/sql/translations/ja.json @@ -19,16 +19,25 @@ "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8" }, "data_description": { + "column": "\u8fd4\u3055\u308c\u305f\u30af\u30a8\u30ea\u3092\u72b6\u614b\u3068\u3057\u3066\u8868\u793a\u3059\u308b\u305f\u3081\u306e\u30ab\u30e9\u30e0", "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" } } } }, "options": { + "error": { + "db_url_invalid": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL\u304c\u7121\u52b9\u3067\u3059", + "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059", + "value_template_invalid": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u7121\u52b9\u3067\u3059" + }, "step": { "init": { "data": { - "name": "\u540d\u524d" + "column": "\u30b3\u30e9\u30e0", + "db_url": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL", + "name": "\u540d\u524d", + "query": "\u30af\u30a8\u30ea\u3092\u9078\u629e" }, "data_description": { "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" diff --git a/homeassistant/components/steam_online/translations/ja.json b/homeassistant/components/steam_online/translations/ja.json index 07c813bb0b1..5e3b98684e8 100644 --- a/homeassistant/components/steam_online/translations/ja.json +++ b/homeassistant/components/steam_online/translations/ja.json @@ -5,7 +5,21 @@ "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_account": "\u30a2\u30ab\u30a6\u30f3\u30c8ID\u304c\u7121\u52b9", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "reauth_confirm": { + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + }, + "user": { + "data": { + "account": "Steam\u30a2\u30ab\u30a6\u30f3\u30c8ID", + "api_key": "API\u30ad\u30fc" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/ja.json b/homeassistant/components/tautulli/translations/ja.json index 854dc593a55..f6bec08752a 100644 --- a/homeassistant/components/tautulli/translations/ja.json +++ b/homeassistant/components/tautulli/translations/ja.json @@ -13,7 +13,8 @@ "reauth_confirm": { "data": { "api_key": "API\u30ad\u30fc" - } + }, + "title": "Tautulli\u3092\u518d\u8a8d\u8a3c\u3057\u307e\u3059" }, "user": { "data": { diff --git a/homeassistant/components/trafikverket_ferry/translations/ja.json b/homeassistant/components/trafikverket_ferry/translations/ja.json index 2377e33a60c..8081c3a9198 100644 --- a/homeassistant/components/trafikverket_ferry/translations/ja.json +++ b/homeassistant/components/trafikverket_ferry/translations/ja.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "incorrect_api_key": "\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306eAPI\u30ad\u30fc\u304c\u7121\u52b9\u3067\u3059", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" }, "step": { @@ -17,7 +18,9 @@ "user": { "data": { "api_key": "API\u30ad\u30fc", + "from": "\u6e2f\u304b\u3089", "time": "\u6642\u9593", + "to": "\u6e2f\u3078", "weekday": "\u5e73\u65e5" } } diff --git a/homeassistant/components/ukraine_alarm/translations/bg.json b/homeassistant/components/ukraine_alarm/translations/bg.json new file mode 100644 index 00000000000..fbedf14b7cd --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/bg.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "community": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + } + }, + "district": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + } + }, + "state": { + "data": { + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + } + }, + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/ca.json b/homeassistant/components/ukraine_alarm/translations/ca.json index bee49c8e4a1..cd5cda1cc8b 100644 --- a/homeassistant/components/ukraine_alarm/translations/ca.json +++ b/homeassistant/components/ukraine_alarm/translations/ca.json @@ -32,7 +32,7 @@ "data": { "api_key": "Clau API" }, - "description": "Configura la integraci\u00f3 d'Alarma Ucraina. Per generar la clau API, v\u00e9s a {api_url}" + "description": "Configura la integraci\u00f3 d'Alarma Ucra\u00efna. Per generar la clau API, v\u00e9s a {api_url}" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/ja.json b/homeassistant/components/ukraine_alarm/translations/ja.json index 519bf3df072..602d63a1de7 100644 --- a/homeassistant/components/ukraine_alarm/translations/ja.json +++ b/homeassistant/components/ukraine_alarm/translations/ja.json @@ -23,7 +23,8 @@ "state": { "data": { "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" - } + }, + "description": "\u30e2\u30cb\u30bf\u30fc\u3059\u308b\u72b6\u614b\u3092\u9078\u629e" }, "user": { "data": { diff --git a/homeassistant/components/ws66i/translations/bg.json b/homeassistant/components/ws66i/translations/bg.json new file mode 100644 index 00000000000..ba078ae5f11 --- /dev/null +++ b/homeassistant/components/ws66i/translations/bg.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \u0430\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/ja.json b/homeassistant/components/ws66i/translations/ja.json index 2f93c93954b..2ae21b3916c 100644 --- a/homeassistant/components/ws66i/translations/ja.json +++ b/homeassistant/components/ws66i/translations/ja.json @@ -11,7 +11,23 @@ "user": { "data": { "ip_address": "IP\u30a2\u30c9\u30ec\u30b9" - } + }, + "title": "\u30c7\u30d0\u30a4\u30b9\u306b\u63a5\u7d9a" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "\u30bd\u30fc\u30b9#1\u306e\u540d\u524d", + "source_2": "\u30bd\u30fc\u30b9#2\u306e\u540d\u524d", + "source_3": "\u30bd\u30fc\u30b9#3\u306e\u540d\u524d", + "source_4": "\u30bd\u30fc\u30b9#4\u306e\u540d\u524d", + "source_5": "\u30bd\u30fc\u30b9#5\u306e\u540d\u524d", + "source_6": "\u30bd\u30fc\u30b9#6\u306e\u540d\u524d" + }, + "title": "\u30bd\u30fc\u30b9\u306e\u8a2d\u5b9a" } } } diff --git a/homeassistant/components/yamaha_musiccast/translations/select.fr.json b/homeassistant/components/yamaha_musiccast/translations/select.fr.json index f8ce2da31a7..a12e6c3f26c 100644 --- a/homeassistant/components/yamaha_musiccast/translations/select.fr.json +++ b/homeassistant/components/yamaha_musiccast/translations/select.fr.json @@ -41,7 +41,7 @@ "dts_neo6_cinema": "DTS Neo:6 Cin\u00e9ma", "dts_neo6_music": "DTS Neo:6 Musique", "dts_neural_x": "DTS Neural:X", - "toggle": "Permuter" + "toggle": "Basculer" }, "yamaha_musiccast__zone_tone_control_mode": { "auto": "Auto", From f18d7942934c95ef3fec7af00f7828344621a911 Mon Sep 17 00:00:00 2001 From: dacwe Date: Wed, 11 May 2022 02:34:03 +0200 Subject: [PATCH 346/930] Bump yalexs to 1.1.25 for handling locks in "secure locked mode" as locked (#71666) --- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index c1156c9aea9..34bd4843f32 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.24"], + "requirements": ["yalexs==1.1.25"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index f3204e93502..94dfcbd4aa0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2468,7 +2468,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.24 +yalexs==1.1.25 # homeassistant.components.yeelight yeelight==0.7.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f4b109b6b07..e3b856b10d9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1617,7 +1617,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.8 # homeassistant.components.august -yalexs==1.1.24 +yalexs==1.1.25 # homeassistant.components.yeelight yeelight==0.7.10 From fb6cdb5a38463809104ac91edc55bca395ed4e15 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 11 May 2022 09:57:54 +0200 Subject: [PATCH 347/930] Optimistically set hs_color in template light (#71629) * Optimistically set hs_color in template light * Update light.py * Update test --- homeassistant/components/template/light.py | 18 ++- tests/components/template/test_light.py | 133 ++++++++++++++++++++- 2 files changed, 145 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 0ebdc3fb93a..1988e77d223 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -352,25 +352,37 @@ class LightTemplate(TemplateEntity, LightEntity): optimistic_set = True if self._level_template is None and ATTR_BRIGHTNESS in kwargs: - _LOGGER.info( + _LOGGER.debug( "Optimistically setting brightness to %s", kwargs[ATTR_BRIGHTNESS] ) self._brightness = kwargs[ATTR_BRIGHTNESS] optimistic_set = True if self._white_value_template is None and ATTR_WHITE_VALUE in kwargs: - _LOGGER.info( + _LOGGER.debug( "Optimistically setting white value to %s", kwargs[ATTR_WHITE_VALUE] ) self._white_value = kwargs[ATTR_WHITE_VALUE] optimistic_set = True if self._temperature_template is None and ATTR_COLOR_TEMP in kwargs: - _LOGGER.info( + _LOGGER.debug( "Optimistically setting color temperature to %s", kwargs[ATTR_COLOR_TEMP], ) self._temperature = kwargs[ATTR_COLOR_TEMP] + if self._color_template is None: + self._color = None + optimistic_set = True + + if self._color_template is None and ATTR_HS_COLOR in kwargs: + _LOGGER.debug( + "Optimistically setting color to %s", + kwargs[ATTR_HS_COLOR], + ) + self._color = kwargs[ATTR_HS_COLOR] + if self._temperature_template is None: + self._temperature = None optimistic_set = True common_params = {} diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index 0888511c583..924a5c25538 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -1183,7 +1183,7 @@ async def test_entity_picture_template(hass, start_ha): @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( "supported_features,supported_color_modes, expected_color_mode", - [(SUPPORT_COLOR, [ColorMode.HS], ColorMode.UNKNOWN)], + [(SUPPORT_COLOR, [ColorMode.HS], ColorMode.HS)], ) @pytest.mark.parametrize( "config", @@ -1253,8 +1253,8 @@ async def test_color_action_no_template( state = hass.states.get("light.test_template_light") assert state.state == STATE_ON - assert state.attributes["color_mode"] == expected_color_mode # hs_color is None - assert "hs_color" not in state.attributes + assert state.attributes["color_mode"] == expected_color_mode + assert state.attributes.get("hs_color") == (40, 50) assert state.attributes["supported_color_modes"] == supported_color_modes assert state.attributes["supported_features"] == supported_features @@ -1308,6 +1308,133 @@ async def test_color_template( assert state.attributes["supported_features"] == supported_features +@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize( + "supported_features,supported_color_modes", + [(SUPPORT_COLOR | SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP, ColorMode.HS])], +) +@pytest.mark.parametrize( + "config", + [ + { + "light": { + "platform": "template", + "lights": { + "test_template_light": { + "value_template": "{{1 == 1}}", + "turn_on": { + "service": "light.turn_on", + "entity_id": "light.test_state", + }, + "turn_off": { + "service": "light.turn_off", + "entity_id": "light.test_state", + }, + "set_color": [ + { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "h": "{{h}}", + "s": "{{s}}", + }, + }, + ], + "set_temperature": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "color_temp": "{{color_temp}}", + }, + }, + } + }, + } + }, + ], +) +async def test_color_and_temperature_actions_no_template( + hass, start_ha, calls, supported_features, supported_color_modes +): + """Test setting color and color temperature with optimistic template.""" + state = hass.states.get("light.test_template_light") + assert state.attributes.get("hs_color") is None + + # Optimistically set color, light should be in hs_color mode + await hass.services.async_call( + light.DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_template_light", ATTR_HS_COLOR: (40, 50)}, + blocking=True, + ) + + assert len(calls) == 1 + assert calls[-1].data["h"] == 40 + assert calls[-1].data["s"] == 50 + + state = hass.states.get("light.test_template_light") + assert state.attributes["color_mode"] == ColorMode.HS + assert "color_temp" not in state.attributes + assert state.attributes["hs_color"] == (40, 50) + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + # Optimistically set color temp, light should be in color temp mode + await hass.services.async_call( + light.DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_template_light", ATTR_COLOR_TEMP: 123}, + blocking=True, + ) + + assert len(calls) == 2 + assert calls[-1].data["color_temp"] == 123 + + state = hass.states.get("light.test_template_light") + assert state.attributes["color_mode"] == ColorMode.COLOR_TEMP + assert state.attributes["color_temp"] == 123 + assert "hs_color" in state.attributes # Color temp represented as hs_color + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + # Optimistically set color, light should again be in hs_color mode + await hass.services.async_call( + light.DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_template_light", ATTR_HS_COLOR: (10, 20)}, + blocking=True, + ) + + assert len(calls) == 3 + assert calls[-1].data["h"] == 10 + assert calls[-1].data["s"] == 20 + + state = hass.states.get("light.test_template_light") + assert state.attributes["color_mode"] == ColorMode.HS + assert "color_temp" not in state.attributes + assert state.attributes["hs_color"] == (10, 20) + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + # Optimistically set color temp, light should again be in color temp mode + await hass.services.async_call( + light.DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "light.test_template_light", ATTR_COLOR_TEMP: 234}, + blocking=True, + ) + + assert len(calls) == 4 + assert calls[-1].data["color_temp"] == 234 + + state = hass.states.get("light.test_template_light") + assert state.attributes["color_mode"] == ColorMode.COLOR_TEMP + assert state.attributes["color_temp"] == 234 + assert "hs_color" in state.attributes # Color temp represented as hs_color + assert state.attributes["supported_color_modes"] == supported_color_modes + assert state.attributes["supported_features"] == supported_features + + @pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) @pytest.mark.parametrize( "config", From 2d5a82d10e23d87106081fade29763822c737f5f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Wed, 11 May 2022 10:48:13 +0200 Subject: [PATCH 348/930] Refactor Plugwise select and add regulation_mode selector (#69210) Co-authored-by: Franck Nijhof --- homeassistant/components/plugwise/select.py | 99 +++++++++++++++------ 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/plugwise/select.py b/homeassistant/components/plugwise/select.py index b3cfb366889..cac9c3e5637 100644 --- a/homeassistant/components/plugwise/select.py +++ b/homeassistant/components/plugwise/select.py @@ -1,64 +1,113 @@ """Plugwise Select component for Home Assistant.""" from __future__ import annotations -from homeassistant.components.select import SelectEntity +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Any + +from plugwise import Smile + +from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_ON from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN, THERMOSTAT_CLASSES +from .const import DOMAIN from .coordinator import PlugwiseDataUpdateCoordinator from .entity import PlugwiseEntity +@dataclass +class PlugwiseSelectDescriptionMixin: + """Mixin values for Plugwise Select entities.""" + + command: Callable[[Smile, str, str], Awaitable[Any]] + current_option: str + options: str + + +@dataclass +class PlugwiseSelectEntityDescription( + SelectEntityDescription, PlugwiseSelectDescriptionMixin +): + """Class describing Plugwise Number entities.""" + + +SELECT_TYPES = ( + PlugwiseSelectEntityDescription( + key="select_schedule", + name="Thermostat Schedule", + icon="mdi:calendar-clock", + command=lambda api, loc, opt: api.set_schedule_state(loc, opt, STATE_ON), + current_option="selected_schedule", + options="available_schedules", + ), + PlugwiseSelectEntityDescription( + key="select_regulation_mode", + name="Regulation Mode", + icon="mdi:hvac", + entity_category=EntityCategory.CONFIG, + command=lambda api, loc, opt: api.set_regulation_mode(opt), + current_option="regulation_mode", + options="regulation_modes", + ), +) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Smile selector from a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id] - async_add_entities( - PlugwiseSelectEntity(coordinator, device_id) - for device_id, device in coordinator.data.devices.items() - if device["class"] in THERMOSTAT_CLASSES - and len(device.get("available_schedules")) > 1 - ) + coordinator: PlugwiseDataUpdateCoordinator = hass.data[DOMAIN][ + config_entry.entry_id + ] + + entities: list[PlugwiseSelectEntity] = [] + for device_id, device in coordinator.data.devices.items(): + for description in SELECT_TYPES: + if description.options in device and len(device[description.options]) > 1: + entities.append( + PlugwiseSelectEntity(coordinator, device_id, description) + ) + + async_add_entities(entities) class PlugwiseSelectEntity(PlugwiseEntity, SelectEntity): """Represent Smile selector.""" + entity_description: PlugwiseSelectEntityDescription + def __init__( self, coordinator: PlugwiseDataUpdateCoordinator, device_id: str, + entity_description: PlugwiseSelectEntityDescription, ) -> None: """Initialise the selector.""" super().__init__(coordinator, device_id) - self._attr_unique_id = f"{device_id}-select_schedule" - self._attr_name = (f"{self.device.get('name', '')} Select Schedule").lstrip() + self.entity_description = entity_description + self._attr_unique_id = f"{device_id}-{entity_description.key}" + self._attr_name = (f"{self.device['name']} {entity_description.name}").lstrip() @property - def current_option(self) -> str | None: + def current_option(self) -> str: """Return the selected entity option to represent the entity state.""" - return self.device.get("selected_schedule") + return self.device[self.entity_description.current_option] @property def options(self) -> list[str]: - """Return a set of selectable options.""" - return self.device.get("available_schedules", []) + """Return the selectable entity options.""" + return self.device[self.entity_description.options] async def async_select_option(self, option: str) -> None: - """Change the selected option.""" - if not ( - await self.coordinator.api.set_schedule_state( - self.device.get("location"), - option, - STATE_ON, - ) - ): - raise HomeAssistantError(f"Failed to change to schedule {option}") + """Change to the selected entity option.""" + await self.entity_description.command( + self.coordinator.api, self.device["location"], option + ) + await self.coordinator.async_request_refresh() From 1304808f8963e58dccda62fd176d8d38d3dbac1a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 06:09:55 -0500 Subject: [PATCH 349/930] Add additional test cover for history_stats (#71648) --- tests/components/history_stats/test_sensor.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index bfa0c8f415e..297e3a5a363 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1387,3 +1387,85 @@ async def test_measure_cet(hass, recorder_mock): assert hass.states.get("sensor.sensor2").state == "0.83" assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "83.3" + + +async def test_end_time_with_microseconds_zeroed(hass, recorder_mock): + """Test the history statistics sensor that has the end time microseconds zeroed out.""" + hass.config.set_time_zone("Europe/Berlin") + start_of_today = dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0) + start_time = start_of_today + timedelta(minutes=60) + t0 = start_time + timedelta(minutes=20) + t1 = t0 + timedelta(minutes=10) + t2 = t1 + timedelta(minutes=10) + time_200 = start_of_today + timedelta(hours=2) + + def _fake_states(*args, **kwargs): + return { + "binary_sensor.heatpump_compressor_state": [ + ha.State( + "binary_sensor.heatpump_compressor_state", "on", last_changed=t0 + ), + ha.State( + "binary_sensor.heatpump_compressor_state", + "off", + last_changed=t1, + ), + ha.State( + "binary_sensor.heatpump_compressor_state", "on", last_changed=t2 + ), + ] + } + + with freeze_time(time_200), patch( + "homeassistant.components.recorder.history.state_changes_during_period", + _fake_states, + ): + await async_setup_component( + hass, + "sensor", + { + "sensor": [ + { + "platform": "history_stats", + "entity_id": "binary_sensor.heatpump_compressor_state", + "name": "heatpump_compressor_today", + "state": "on", + "start": "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}", + "end": "{{ now().replace(microsecond=0) }}", + "type": "time", + }, + ] + }, + ) + await hass.async_block_till_done() + await async_update_entity(hass, "sensor.heatpump_compressor_today") + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + async_fire_time_changed(hass, time_200) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + hass.states.async_set("binary_sensor.heatpump_compressor_state", "off") + await hass.async_block_till_done() + + time_400 = start_of_today + timedelta(hours=4) + with freeze_time(time_400): + async_fire_time_changed(hass, time_400) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "1.83" + hass.states.async_set("binary_sensor.heatpump_compressor_state", "on") + await hass.async_block_till_done() + time_600 = start_of_today + timedelta(hours=6) + with freeze_time(time_600): + async_fire_time_changed(hass, time_600) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "3.83" + rolled_to_next_day = start_of_today + timedelta(days=1) + assert rolled_to_next_day.hour == 0 + assert rolled_to_next_day.minute == 0 + assert rolled_to_next_day.second == 0 + assert rolled_to_next_day.microsecond == 0 + + with freeze_time(rolled_to_next_day): + async_fire_time_changed(hass, rolled_to_next_day) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "0.0" From 557cba118f8ee68cdd2f1f6ca29c30a6facbf3a6 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 11 May 2022 13:10:34 +0200 Subject: [PATCH 350/930] Bump devolo_home_control (#71639) --- homeassistant/components/devolo_home_control/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/devolo_home_control/manifest.json b/homeassistant/components/devolo_home_control/manifest.json index 75cca12d99e..0de3cd7f07d 100644 --- a/homeassistant/components/devolo_home_control/manifest.json +++ b/homeassistant/components/devolo_home_control/manifest.json @@ -2,7 +2,7 @@ "domain": "devolo_home_control", "name": "devolo Home Control", "documentation": "https://www.home-assistant.io/integrations/devolo_home_control", - "requirements": ["devolo-home-control-api==0.18.1"], + "requirements": ["devolo-home-control-api==0.18.2"], "after_dependencies": ["zeroconf"], "config_flow": true, "codeowners": ["@2Fake", "@Shutgun"], diff --git a/requirements_all.txt b/requirements_all.txt index 94dfcbd4aa0..44d44e0602c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -539,7 +539,7 @@ deluge-client==1.7.1 denonavr==0.10.11 # homeassistant.components.devolo_home_control -devolo-home-control-api==0.18.1 +devolo-home-control-api==0.18.2 # homeassistant.components.devolo_home_network devolo-plc-api==0.8.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e3b856b10d9..cfe9ad40cda 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -394,7 +394,7 @@ deluge-client==1.7.1 denonavr==0.10.11 # homeassistant.components.devolo_home_control -devolo-home-control-api==0.18.1 +devolo-home-control-api==0.18.2 # homeassistant.components.devolo_home_network devolo-plc-api==0.8.0 From 34d4eb7c151e962fdcb5821d37598353c7e18436 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 11 May 2022 07:12:44 -0400 Subject: [PATCH 351/930] Improve eight sleep code quality and fix bug (#71622) --- .../components/eight_sleep/__init__.py | 2 - .../components/eight_sleep/binary_sensor.py | 3 +- .../components/eight_sleep/sensor.py | 100 ++++++------------ 3 files changed, 33 insertions(+), 72 deletions(-) diff --git a/homeassistant/components/eight_sleep/__init__.py b/homeassistant/components/eight_sleep/__init__.py index eece6e69ed5..e986a7f6d60 100644 --- a/homeassistant/components/eight_sleep/__init__.py +++ b/homeassistant/components/eight_sleep/__init__.py @@ -156,14 +156,12 @@ class EightSleepBaseEntity(CoordinatorEntity[DataUpdateCoordinator]): eight: EightSleep, user_id: str | None, sensor: str, - units: str | None = None, ) -> None: """Initialize the data object.""" super().__init__(coordinator) self._eight = eight self._user_id = user_id self._sensor = sensor - self._units = units self._user_obj: EightUser | None = None if self._user_id: self._user_obj = self._eight.users[user_id] diff --git a/homeassistant/components/eight_sleep/binary_sensor.py b/homeassistant/components/eight_sleep/binary_sensor.py index a63389ef3f9..868a5177cfe 100644 --- a/homeassistant/components/eight_sleep/binary_sensor.py +++ b/homeassistant/components/eight_sleep/binary_sensor.py @@ -45,6 +45,8 @@ async def async_setup_platform( class EightHeatSensor(EightSleepBaseEntity, BinarySensorEntity): """Representation of a Eight Sleep heat-based sensor.""" + _attr_device_class = BinarySensorDeviceClass.OCCUPANCY + def __init__( self, coordinator: DataUpdateCoordinator, @@ -54,7 +56,6 @@ class EightHeatSensor(EightSleepBaseEntity, BinarySensorEntity): ) -> None: """Initialize the sensor.""" super().__init__(coordinator, eight, user_id, sensor) - self._attr_device_class = BinarySensorDeviceClass.OCCUPANCY assert self._user_obj _LOGGER.debug( "Presence Sensor: %s, Side: %s, User: %s", diff --git a/homeassistant/components/eight_sleep/sensor.py b/homeassistant/components/eight_sleep/sensor.py index 145292d6728..b405617e276 100644 --- a/homeassistant/components/eight_sleep/sensor.py +++ b/homeassistant/components/eight_sleep/sensor.py @@ -6,8 +6,8 @@ from typing import Any from pyeight.eight import EightSleep -from homeassistant.components.sensor import SensorEntity -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -68,24 +68,19 @@ async def async_setup_platform( heat_coordinator: DataUpdateCoordinator = hass.data[DOMAIN][DATA_HEAT] user_coordinator: DataUpdateCoordinator = hass.data[DOMAIN][DATA_USER] - if hass.config.units.is_metric: - units = "si" - else: - units = "us" - all_sensors: list[SensorEntity] = [] for obj in eight.users.values(): for sensor in EIGHT_USER_SENSORS: all_sensors.append( - EightUserSensor(user_coordinator, eight, obj.userid, sensor, units) + EightUserSensor(user_coordinator, eight, obj.userid, sensor) ) for sensor in EIGHT_HEAT_SENSORS: all_sensors.append( EightHeatSensor(heat_coordinator, eight, obj.userid, sensor) ) for sensor in EIGHT_ROOM_SENSORS: - all_sensors.append(EightRoomSensor(user_coordinator, eight, sensor, units)) + all_sensors.append(EightRoomSensor(user_coordinator, eight, sensor)) async_add_entities(all_sensors) @@ -93,6 +88,8 @@ async def async_setup_platform( class EightHeatSensor(EightSleepBaseEntity, SensorEntity): """Representation of an eight sleep heat-based sensor.""" + _attr_native_unit_of_measurement = PERCENTAGE + def __init__( self, coordinator: DataUpdateCoordinator, @@ -102,7 +99,6 @@ class EightHeatSensor(EightSleepBaseEntity, SensorEntity): ) -> None: """Initialize the sensor.""" super().__init__(coordinator, eight, user_id, sensor) - self._attr_native_unit_of_measurement = PERCENTAGE assert self._user_obj _LOGGER.debug( @@ -139,6 +135,13 @@ def _get_breakdown_percent( return 0 +def _get_rounded_value(attr: dict[str, Any], key: str) -> int | float | None: + """Get rounded value for given key.""" + if (val := attr.get(key)) is None: + return None + return round(val, 2) + + class EightUserSensor(EightSleepBaseEntity, SensorEntity): """Representation of an eight sleep user-based sensor.""" @@ -148,14 +151,17 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): eight: EightSleep, user_id: str, sensor: str, - units: str, ) -> None: """Initialize the sensor.""" - super().__init__(coordinator, eight, user_id, sensor, units) + super().__init__(coordinator, eight, user_id, sensor) assert self._user_obj if self._sensor == "bed_temperature": self._attr_icon = "mdi:thermometer" + self._attr_device_class = SensorDeviceClass.TEMPERATURE + self._attr_native_unit_of_measurement = TEMP_CELSIUS + elif self._sensor in ("current_sleep", "last_sleep", "current_sleep_fitness"): + self._attr_native_unit_of_measurement = "Score" _LOGGER.debug( "User Sensor: %s, Side: %s, User: %s", @@ -179,41 +185,13 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): return self._user_obj.last_sleep_score if self._sensor == "bed_temperature": - temp = self._user_obj.current_values["bed_temp"] - try: - if self._units == "si": - return round(temp, 2) - return round((temp * 1.8) + 32, 2) - except TypeError: - return None + return self._user_obj.current_values["bed_temp"] if self._sensor == "sleep_stage": return self._user_obj.current_values["stage"] return None - @property - def native_unit_of_measurement(self) -> str | None: - """Return the unit the value is expressed in.""" - if self._sensor in ("current_sleep", "last_sleep", "current_sleep_fitness"): - return "Score" - if self._sensor == "bed_temperature": - if self._units == "si": - return TEMP_CELSIUS - return TEMP_FAHRENHEIT - return None - - def _get_rounded_value( - self, attr: dict[str, Any], key: str, use_units: bool = True - ) -> int | float | None: - """Get rounded value based on units for given key.""" - try: - if self._units == "si" or not use_units: - return round(attr["room_temp"], 2) - return round((attr["room_temp"] * 1.8) + 32, 2) - except TypeError: - return None - @property def extra_state_attributes(self) -> dict[str, Any] | None: """Return device state attributes.""" @@ -255,26 +233,18 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): ) state_attr[ATTR_REM_PERC] = _get_breakdown_percent(attr, "rem", sleep_time) - room_temp = self._get_rounded_value(attr, "room_temp") - bed_temp = self._get_rounded_value(attr, "bed_temp") + room_temp = _get_rounded_value(attr, "room_temp") + bed_temp = _get_rounded_value(attr, "bed_temp") if "current" in self._sensor: - state_attr[ATTR_RESP_RATE] = self._get_rounded_value( - attr, "resp_rate", False - ) - state_attr[ATTR_HEART_RATE] = self._get_rounded_value( - attr, "heart_rate", False - ) + state_attr[ATTR_RESP_RATE] = _get_rounded_value(attr, "resp_rate") + state_attr[ATTR_HEART_RATE] = _get_rounded_value(attr, "heart_rate") state_attr[ATTR_SLEEP_STAGE] = attr["stage"] state_attr[ATTR_ROOM_TEMP] = room_temp state_attr[ATTR_BED_TEMP] = bed_temp elif "last" in self._sensor: - state_attr[ATTR_AVG_RESP_RATE] = self._get_rounded_value( - attr, "resp_rate", False - ) - state_attr[ATTR_AVG_HEART_RATE] = self._get_rounded_value( - attr, "heart_rate", False - ) + state_attr[ATTR_AVG_RESP_RATE] = _get_rounded_value(attr, "resp_rate") + state_attr[ATTR_AVG_HEART_RATE] = _get_rounded_value(attr, "heart_rate") state_attr[ATTR_AVG_ROOM_TEMP] = room_temp state_attr[ATTR_AVG_BED_TEMP] = bed_temp @@ -284,28 +254,20 @@ class EightUserSensor(EightSleepBaseEntity, SensorEntity): class EightRoomSensor(EightSleepBaseEntity, SensorEntity): """Representation of an eight sleep room sensor.""" + _attr_icon = "mdi:thermometer" + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = TEMP_CELSIUS + def __init__( self, coordinator: DataUpdateCoordinator, eight: EightSleep, sensor: str, - units: str, ) -> None: """Initialize the sensor.""" - super().__init__(coordinator, eight, None, sensor, units) - - self._attr_icon = "mdi:thermometer" - self._attr_native_unit_of_measurement: str = ( - TEMP_CELSIUS if self._units == "si" else TEMP_FAHRENHEIT - ) + super().__init__(coordinator, eight, None, sensor) @property def native_value(self) -> int | float | None: """Return the state of the sensor.""" - temp = self._eight.room_temperature() - try: - if self._units == "si": - return round(temp, 2) - return round((temp * 1.8) + 32, 2) - except TypeError: - return None + return self._eight.room_temperature() From 04e3cee6c363f9c580e11c0b9b2d5b9a35cfa06e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 06:14:11 -0500 Subject: [PATCH 352/930] Bump flux_led to 0.28.29 (#71665) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index e28b55869c7..d2eb4e1e2e0 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.28.28"], + "requirements": ["flux_led==0.28.29"], "quality_scale": "platinum", "codeowners": ["@icemanch", "@bdraco"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 44d44e0602c..4f8bf564915 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -657,7 +657,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.2 # homeassistant.components.flux_led -flux_led==0.28.28 +flux_led==0.28.29 # homeassistant.components.homekit # homeassistant.components.recorder diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cfe9ad40cda..e274844449b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -460,7 +460,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.2 # homeassistant.components.flux_led -flux_led==0.28.28 +flux_led==0.28.29 # homeassistant.components.homekit # homeassistant.components.recorder From b9f3f1af6c1d10d16189dab04c4e68969568f381 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 11 May 2022 13:18:20 +0200 Subject: [PATCH 353/930] Streamline setup of deCONZ fan platform (#71658) --- homeassistant/components/deconz/fan.py | 30 +++++++------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/deconz/fan.py b/homeassistant/components/deconz/fan.py index 8fcc7c14f9d..93daae55ccf 100644 --- a/homeassistant/components/deconz/fan.py +++ b/homeassistant/components/deconz/fan.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any, Literal +from pydeconz.models.event import EventType from pydeconz.models.light.fan import ( FAN_SPEED_25_PERCENT, FAN_SPEED_50_PERCENT, @@ -15,7 +16,6 @@ from pydeconz.models.light.fan import ( from homeassistant.components.fan import DOMAIN, FanEntity, FanEntityFeature from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.percentage import ( ordered_list_item_to_percentage, @@ -43,33 +43,19 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_fan(lights: list[Fan] | None = None) -> None: + def async_add_fan(_: EventType, fan_id: str) -> None: """Add fan from deCONZ.""" - entities = [] - - if lights is None: - lights = list(gateway.api.lights.fans.values()) - - for light in lights: - - if ( - isinstance(light, Fan) - and light.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzFan(light, gateway)) - - if entities: - async_add_entities(entities) + fan = gateway.api.lights.fans[fan_id] + async_add_entities([DeconzFan(fan, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_light, + gateway.api.lights.fans.subscribe( async_add_fan, + EventType.ADDED, ) ) - - async_add_fan() + for fan_id in gateway.api.lights.fans: + async_add_fan(EventType.ADDED, fan_id) class DeconzFan(DeconzDevice, FanEntity): From 920c0d166732e2059027239f09a924f992151844 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 11 May 2022 13:19:28 +0200 Subject: [PATCH 354/930] Streamline setup of deCONZ cover platform (#71656) --- homeassistant/components/deconz/cover.py | 29 +++++++----------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index d5656d31bee..3d5dabb73c1 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any, cast +from pydeconz.models.event import EventType from pydeconz.models.light.cover import Cover from homeassistant.components.cover import ( @@ -15,7 +16,6 @@ from homeassistant.components.cover import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .deconz_device import DeconzDevice @@ -38,32 +38,19 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_cover(lights: list[Cover] | None = None) -> None: + def async_add_cover(_: EventType, cover_id: str) -> None: """Add cover from deCONZ.""" - entities = [] - - if lights is None: - lights = list(gateway.api.lights.covers.values()) - - for light in lights: - if ( - isinstance(light, Cover) - and light.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzCover(light, gateway)) - - if entities: - async_add_entities(entities) + cover = gateway.api.lights.covers[cover_id] + async_add_entities([DeconzCover(cover, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_light, + gateway.api.lights.covers.subscribe( async_add_cover, + EventType.ADDED, ) ) - - async_add_cover() + for cover_id in gateway.api.lights.covers: + async_add_cover(EventType.ADDED, cover_id) class DeconzCover(DeconzDevice, CoverEntity): From db17d7aecfcecb82f16d2f5cd615ddb0a845cbb6 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 11 May 2022 13:20:09 +0200 Subject: [PATCH 355/930] Streamline setup of deCONZ lock from light platform (#71659) --- homeassistant/components/deconz/lock.py | 28 +++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index 51c2d4fc0fc..1ecc1802b74 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any +from pydeconz.models.event import EventType from pydeconz.models.light.lock import Lock from pydeconz.models.sensor.door_lock import DoorLock @@ -27,31 +28,19 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_lock_from_light(lights: list[Lock] | None = None) -> None: + def async_add_lock_from_light(_: EventType, lock_id: str) -> None: """Add lock from deCONZ.""" - entities = [] - - if lights is None: - lights = list(gateway.api.lights.locks.values()) - - for light in lights: - - if ( - isinstance(light, Lock) - and light.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzLock(light, gateway)) - - if entities: - async_add_entities(entities) + lock = gateway.api.lights.locks[lock_id] + async_add_entities([DeconzLock(lock, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_light, + gateway.api.lights.locks.subscribe( async_add_lock_from_light, + EventType.ADDED, ) ) + for lock_id in gateway.api.lights.locks: + async_add_lock_from_light(EventType.ADDED, lock_id) @callback def async_add_lock_from_sensor(sensors: list[DoorLock] | None = None) -> None: @@ -80,7 +69,6 @@ async def async_setup_entry( ) ) - async_add_lock_from_light() async_add_lock_from_sensor() From 44f8c555a6ff94cc82d587a7ce274d2e44538340 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 11 May 2022 13:30:16 +0200 Subject: [PATCH 356/930] Streamline setup of deCONZ siren platform (#71660) --- homeassistant/components/deconz/siren.py | 30 +++++++----------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/deconz/siren.py b/homeassistant/components/deconz/siren.py index c279afae696..2a11cd5346a 100644 --- a/homeassistant/components/deconz/siren.py +++ b/homeassistant/components/deconz/siren.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any +from pydeconz.models.event import EventType from pydeconz.models.light.siren import Siren from homeassistant.components.siren import ( @@ -13,7 +14,6 @@ from homeassistant.components.siren import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .deconz_device import DeconzDevice @@ -30,33 +30,19 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_siren(lights: list[Siren] | None = None) -> None: + def async_add_siren(_: EventType, siren_id: str) -> None: """Add siren from deCONZ.""" - entities = [] - - if lights is None: - lights = list(gateway.api.lights.sirens.values()) - - for light in lights: - - if ( - isinstance(light, Siren) - and light.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzSiren(light, gateway)) - - if entities: - async_add_entities(entities) + siren = gateway.api.lights.sirens[siren_id] + async_add_entities([DeconzSiren(siren, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_light, + gateway.api.lights.sirens.subscribe( async_add_siren, + EventType.ADDED, ) ) - - async_add_siren() + for siren_id in gateway.api.lights.sirens: + async_add_siren(EventType.ADDED, siren_id) class DeconzSiren(DeconzDevice, SirenEntity): From 554f079b0244a582e4244ec6ad7654c617ffbcb3 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 11 May 2022 13:31:16 +0200 Subject: [PATCH 357/930] Streamline setup of deCONZ switch platform (#71661) --- homeassistant/components/deconz/switch.py | 32 +++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index f1ff95183dd..6789b20b3c7 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -4,12 +4,12 @@ from __future__ import annotations from typing import Any +from pydeconz.models.event import EventType from pydeconz.models.light.light import Light from homeassistant.components.switch import DOMAIN, SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import POWER_PLUGS @@ -30,33 +30,21 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_switch(lights: list[Light] | None = None) -> None: + def async_add_switch(_: EventType, light_id: str) -> None: """Add switch from deCONZ.""" - entities = [] - - if lights is None: - lights = list(gateway.api.lights.lights.values()) - - for light in lights: - - if ( - light.type in POWER_PLUGS - and light.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzPowerPlug(light, gateway)) - - if entities: - async_add_entities(entities) + light = gateway.api.lights.lights[light_id] + if light.type not in POWER_PLUGS: + return + async_add_entities([DeconzPowerPlug(light, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_light, + gateway.api.lights.lights.subscribe( async_add_switch, + EventType.ADDED, ) ) - - async_add_switch() + for light_id in gateway.api.lights.lights: + async_add_switch(EventType.ADDED, light_id) class DeconzPowerPlug(DeconzDevice, SwitchEntity): From 49491bcda55125369319fe1f0b6036ddfc4a2a2a Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 11 May 2022 19:36:30 +0200 Subject: [PATCH 358/930] Sensibo test data adjustment from late review (#71482) * Sensibo test data * Separate pytest fixture for json load * Scope to session --- tests/components/sensibo/conftest.py | 34 +- tests/components/sensibo/fixtures/data.json | 551 ++++++++++++++++++ .../components/sensibo/test_binary_sensor.py | 14 +- tests/components/sensibo/test_climate.py | 107 ++-- tests/components/sensibo/test_coordinator.py | 15 +- tests/components/sensibo/test_diagnostics.py | 36 +- tests/components/sensibo/test_entity.py | 16 +- tests/components/sensibo/test_init.py | 17 +- 8 files changed, 688 insertions(+), 102 deletions(-) create mode 100644 tests/components/sensibo/fixtures/data.json diff --git a/tests/components/sensibo/conftest.py b/tests/components/sensibo/conftest.py index 9380ee6ab6b..48c9317a5cb 100644 --- a/tests/components/sensibo/conftest.py +++ b/tests/components/sensibo/conftest.py @@ -1,8 +1,12 @@ """Fixtures for the Sensibo integration.""" from __future__ import annotations +import json +from typing import Any from unittest.mock import patch +from pysensibo import SensiboClient +from pysensibo.model import SensiboData import pytest from homeassistant.components.sensibo.const import DOMAIN @@ -10,13 +14,13 @@ from homeassistant.config_entries import SOURCE_USER from homeassistant.core import HomeAssistant from . import ENTRY_CONFIG -from .response import DATA_FROM_API -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_fixture +from tests.test_util.aiohttp import AiohttpClientMocker @pytest.fixture -async def load_int(hass: HomeAssistant) -> MockConfigEntry: +async def load_int(hass: HomeAssistant, get_data: SensiboData) -> MockConfigEntry: """Set up the Sensibo integration in Home Assistant.""" config_entry = MockConfigEntry( domain=DOMAIN, @@ -31,7 +35,7 @@ async def load_int(hass: HomeAssistant) -> MockConfigEntry: with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, @@ -43,3 +47,25 @@ async def load_int(hass: HomeAssistant) -> MockConfigEntry: await hass.async_block_till_done() return config_entry + + +@pytest.fixture(name="get_data") +async def get_data_from_library( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, load_json: dict[str, Any] +) -> SensiboData: + """Retrieve data from upstream Sensibo library.""" + + client = SensiboClient("123467890", aioclient_mock.create_session(hass.loop)) + with patch("pysensibo.SensiboClient.async_get_devices", return_value=load_json): + output = await client.async_get_devices_data() + await client._session.close() # pylint: disable=protected-access + return output + + +@pytest.fixture(name="load_json", scope="session") +def load_json_from_fixture() -> SensiboData: + """Load fixture with json data and return.""" + + data_fixture = load_fixture("data.json", "sensibo") + json_data: dict[str, Any] = json.loads(data_fixture) + return json_data diff --git a/tests/components/sensibo/fixtures/data.json b/tests/components/sensibo/fixtures/data.json new file mode 100644 index 00000000000..c75423fe464 --- /dev/null +++ b/tests/components/sensibo/fixtures/data.json @@ -0,0 +1,551 @@ +{ + "status": "success", + "result": [ + { + "isGeofenceOnEnterEnabledForThisUser": false, + "isClimateReactGeofenceOnEnterEnabledForThisUser": false, + "isMotionGeofenceOnEnterEnabled": false, + "isOwner": true, + "id": "ABC999111", + "qrId": "AAAAAAAAAA", + "temperatureUnit": "C", + "room": { + "uid": "99TT99TT", + "name": "Hallway", + "icon": "Lounge" + }, + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.019722Z", + "secondsAgo": -1 + }, + "on": true, + "mode": "heat", + "fanLevel": "high", + "targetTemperature": 25, + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on" + }, + "lastStateChange": { + "time": "2022-04-30T11:21:30Z", + "secondsAgo": 119 + }, + "lastStateChangeToOn": { + "time": "2022-04-30T11:20:28Z", + "secondsAgo": 181 + }, + "lastStateChangeToOff": { + "time": "2022-04-30T11:21:30Z", + "secondsAgo": 119 + }, + "location": { + "id": "ZZZZZZZZZZZZ", + "name": "Home", + "latLon": [58.9806976, 20.5864297], + "address": ["Sealand 99", "Some county"], + "country": "United Country", + "createTime": { + "time": "2020-03-21T15:44:15Z", + "secondsAgo": 66543240 + } + }, + "connectionStatus": { + "isAlive": true, + "lastSeen": { + "time": "2022-04-30T11:22:57.894846Z", + "secondsAgo": 32 + } + }, + "firmwareVersion": "SKY30046", + "firmwareType": "esp8266ex", + "productModel": "skyv2", + "configGroup": "stable", + "currentlyAvailableFirmwareVersion": "SKY30046", + "cleanFiltersNotificationEnabled": false, + "shouldShowFilterCleaningNotification": false, + "isGeofenceOnExitEnabled": false, + "isClimateReactGeofenceOnExitEnabled": false, + "isMotionGeofenceOnExitEnabled": false, + "serial": "1234567890", + "sensorsCalibration": { + "temperature": 0.1, + "humidity": 0.0 + }, + "motionSensors": [ + { + "configGroup": "stable", + "connectionStatus": { + "isAlive": true, + "lastSeen": { + "secondsAgo": 86, + "time": "2022-01-01T18:59:48.665878Z" + } + }, + "firmwareType": "nrf52", + "firmwareVersion": "V17", + "id": "AABBCC", + "isMainSensor": true, + "macAddress": "00:04:00:B6:00:00", + "measurements": { + "batteryVoltage": 3000, + "humidity": 57, + "motion": true, + "rssi": -72, + "temperature": 23.9, + "time": { + "secondsAgo": 86, + "time": "2022-01-01T18:59:48.665878Z" + } + }, + "parentDeviceUid": "ABC999111", + "productModel": "motion_sensor", + "qrId": "AAZZAAZZ", + "serial": "12123434" + } + ], + "tags": [], + "timer": null, + "schedules": [ + { + "id": "11", + "isEnabled": false, + "acState": { + "on": false, + "targetTemperature": 21, + "temperatureUnit": "C", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "extra": { + "scheduler": { + "climate_react": null, + "motion": null, + "on": false, + "climate_react_settings": null, + "pure_boost": null + } + }, + "horizontalSwing": "stopped", + "light": "on" + }, + "createTime": "2022-04-17T15:41:05", + "createTimeSecondsAgo": 1107745, + "recurringDays": ["Wednesday", "Thursday"], + "targetTimeLocal": "17:40", + "timezone": "Europe/Stockholm", + "podUid": "ABC999111", + "nextTime": "2022-05-04T15:40:00", + "nextTimeSecondsFromNow": 360989 + } + ], + "motionConfig": { + "enabled": true, + "onEnterACChange": false, + "onEnterACState": null, + "onEnterCRChange": true, + "onExitACChange": true, + "onExitACState": null, + "onExitCRChange": true, + "onExitDelayMinutes": 20 + }, + "filtersCleaning": { + "acOnSecondsSinceLastFiltersClean": 667991, + "filtersCleanSecondsThreshold": 1080000, + "lastFiltersCleanTime": { + "time": "2022-03-12T15:24:26Z", + "secondsAgo": 4219143 + }, + "shouldCleanFilters": false + }, + "serviceSubscriptions": [], + "roomIsOccupied": true, + "mainMeasurementsSensor": { + "configGroup": "stable", + "connectionStatus": { + "isAlive": true, + "lastSeen": { + "secondsAgo": 86, + "time": "2022-01-01T18:59:48.665878Z" + } + }, + "firmwareType": "nrf52", + "firmwareVersion": "V17", + "id": "AABBCC", + "isMainSensor": true, + "macAddress": "00:04:00:B6:00:00", + "measurements": { + "batteryVoltage": 3000, + "humidity": 32.9, + "motion": false, + "rssi": -72, + "temperature": 21.2, + "time": { + "secondsAgo": 86, + "time": "2022-01-01T18:59:48.665878Z" + } + }, + "parentDeviceUid": "ABC999111", + "productModel": "motion_sensor", + "qrId": "AAZZAAZZ", + "serial": "12123434" + }, + "pureBoostConfig": null, + "warrantyEligible": "no", + "warrantyEligibleUntil": { + "time": "2020-04-18T15:43:08Z", + "secondsAgo": 64093221 + }, + "features": ["softShowPlus", "optimusTrial"], + "runningHealthcheck": null, + "lastHealthcheck": null, + "lastACStateChange": { + "id": "11", + "time": { + "time": "2022-04-30T11:21:29Z", + "secondsAgo": 120 + }, + "status": "Success", + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.062645Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on" + }, + "resultingAcState": { + "timestamp": { + "time": "2022-04-30T11:23:30.062688Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on" + }, + "changedProperties": ["on"], + "reason": "UserRequest", + "failureReason": null, + "resolveTime": { + "time": "2022-04-30T11:21:30Z", + "secondsAgo": 119 + }, + "causedByScheduleId": null, + "causedByScheduleType": null + }, + "homekitSupported": false, + "remoteCapabilities": { + "modes": { + "cool": { + "temperatures": { + "F": { + "isNative": false, + "values": [64, 66, 68] + }, + "C": { + "isNative": true, + "values": [18, 19, 20] + } + }, + "fanLevels": ["quiet", "low", "medium"], + "swing": ["stopped", "fixedTop", "fixedMiddleTop"], + "horizontalSwing": ["stopped", "fixedLeft", "fixedCenterLeft"], + "light": ["on", "off"] + }, + "heat": { + "temperatures": { + "F": { + "isNative": false, + "values": [63, 64, 66] + }, + "C": { + "isNative": true, + "values": [17, 18, 19, 20] + } + }, + "fanLevels": ["quiet", "low", "medium"], + "swing": ["stopped", "fixedTop", "fixedMiddleTop"], + "horizontalSwing": ["stopped", "fixedLeft", "fixedCenterLeft"], + "light": ["on", "off"] + }, + "dry": { + "temperatures": { + "F": { + "isNative": false, + "values": [64, 66, 68] + }, + "C": { + "isNative": true, + "values": [18, 19, 20] + } + }, + "swing": ["stopped", "fixedTop", "fixedMiddleTop"], + "horizontalSwing": ["stopped", "fixedLeft", "fixedCenterLeft"], + "light": ["on", "off"] + }, + "auto": { + "temperatures": { + "F": { + "isNative": false, + "values": [64, 66, 68] + }, + "C": { + "isNative": true, + "values": [18, 19, 20, 21] + } + }, + "fanLevels": ["quiet", "low", "medium"], + "swing": ["stopped", "fixedTop", "fixedMiddleTop"], + "horizontalSwing": ["stopped", "fixedLeft", "fixedCenterLeft"], + "light": ["on", "off"] + }, + "fan": { + "temperatures": {}, + "fanLevels": ["quiet", "low", "medium"], + "swing": ["stopped", "fixedTop", "fixedMiddleTop"], + "horizontalSwing": ["stopped", "fixedLeft", "fixedCenterLeft"], + "light": ["on", "off"] + } + } + }, + "remote": { + "toggle": false, + "window": false + }, + "remoteFlavor": "Curious Sea Cucumber", + "remoteAlternatives": ["_mitsubishi2_night_heat"], + "smartMode": { + "enabled": false, + "type": "temperature", + "deviceUid": "ABC999111", + "lowTemperatureThreshold": 0.0, + "highTemperatureThreshold": 27.5, + "lowTemperatureState": { + "on": true, + "targetTemperature": 21, + "temperatureUnit": "C", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on" + }, + "highTemperatureState": { + "on": true, + "targetTemperature": 21, + "temperatureUnit": "C", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on" + }, + "lowTemperatureWebhook": null, + "highTemperatureWebhook": null + }, + "measurements": { + "time": { + "time": "2022-04-30T11:22:57.894846Z", + "secondsAgo": 32 + }, + "temperature": 21.2, + "humidity": 32.9, + "motion": true, + "roomIsOccupied": true, + "feelsLike": 21.2, + "rssi": -45 + }, + "accessPoint": { + "ssid": "Sensibo-1234567890", + "password": null + }, + "macAddress": "00:02:00:B6:00:00", + "autoOffMinutes": null, + "autoOffEnabled": false, + "antiMoldTimer": null, + "antiMoldConfig": null + }, + { + "isGeofenceOnEnterEnabledForThisUser": false, + "isClimateReactGeofenceOnEnterEnabledForThisUser": false, + "isMotionGeofenceOnEnterEnabled": false, + "isOwner": true, + "id": "AAZZAAZZ", + "qrId": "AAAAAAAABB", + "temperatureUnit": "C", + "room": { + "uid": "99TT99ZZ", + "name": "Kitchen", + "icon": "Diningroom" + }, + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.067312Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "low", + "light": "on" + }, + "lastStateChange": { + "time": "2022-04-30T11:21:41Z", + "secondsAgo": 108 + }, + "lastStateChangeToOn": { + "time": "2022-04-30T09:43:26Z", + "secondsAgo": 6003 + }, + "lastStateChangeToOff": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "location": { + "id": "ZZZZZZZZZZZZ", + "name": "Home", + "latLon": [58.9806976, 20.5864297], + "address": ["Sealand 99", "Some county"], + "country": "United Country", + "createTime": { + "time": "2020-03-21T15:44:15Z", + "secondsAgo": 66543240 + } + }, + "connectionStatus": { + "isAlive": true, + "lastSeen": { + "time": "2022-04-30T11:23:20.642798Z", + "secondsAgo": 9 + } + }, + "firmwareVersion": "PUR00111", + "firmwareType": "pure-esp32", + "productModel": "pure", + "configGroup": "stable", + "currentlyAvailableFirmwareVersion": "PUR00111", + "cleanFiltersNotificationEnabled": false, + "shouldShowFilterCleaningNotification": false, + "isGeofenceOnExitEnabled": false, + "isClimateReactGeofenceOnExitEnabled": false, + "isMotionGeofenceOnExitEnabled": false, + "serial": "0987654321", + "sensorsCalibration": { + "temperature": 0.0, + "humidity": 0.0 + }, + "motionSensors": [], + "tags": [], + "timer": null, + "schedules": [], + "motionConfig": null, + "filtersCleaning": { + "acOnSecondsSinceLastFiltersClean": 415560, + "filtersCleanSecondsThreshold": 14256000, + "lastFiltersCleanTime": { + "time": "2022-04-23T15:58:45Z", + "secondsAgo": 588284 + }, + "shouldCleanFilters": false + }, + "serviceSubscriptions": [], + "roomIsOccupied": null, + "mainMeasurementsSensor": null, + "pureBoostConfig": { + "enabled": false, + "sensitivity": "N", + "measurements_integration": true, + "ac_integration": false, + "geo_integration": false, + "prime_integration": false + }, + "warrantyEligible": "no", + "warrantyEligibleUntil": { + "time": "2022-04-10T09:58:58Z", + "secondsAgo": 1733071 + }, + "features": ["optimusTrial", "softShowPlus"], + "runningHealthcheck": null, + "lastHealthcheck": null, + "lastACStateChange": { + "id": "AA22", + "time": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "status": "Success", + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.090144Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "low", + "light": "on" + }, + "resultingAcState": { + "timestamp": { + "time": "2022-04-30T11:23:30.090185Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "low", + "light": "on" + }, + "changedProperties": ["on"], + "reason": "UserRequest", + "failureReason": null, + "resolveTime": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "causedByScheduleId": null, + "causedByScheduleType": null + }, + "homekitSupported": true, + "remoteCapabilities": { + "modes": { + "fan": { + "temperatures": {}, + "fanLevels": ["low", "high"], + "light": ["on", "dim", "off"] + } + } + }, + "remote": { + "toggle": false, + "window": false + }, + "remoteFlavor": "Eccentric Eagle", + "remoteAlternatives": [], + "smartMode": null, + "measurements": { + "time": { + "time": "2022-04-30T11:23:20.642798Z", + "secondsAgo": 9 + }, + "rssi": -58, + "pm25": 1, + "motion": false, + "roomIsOccupied": null + }, + "accessPoint": { + "ssid": "Sensibo-09876", + "password": null + }, + "macAddress": "00:01:00:01:00:01", + "autoOffMinutes": null, + "autoOffEnabled": false, + "antiMoldTimer": null, + "antiMoldConfig": null + } + ] +} diff --git a/tests/components/sensibo/test_binary_sensor.py b/tests/components/sensibo/test_binary_sensor.py index cbf38ff27b0..3a84dc99ca5 100644 --- a/tests/components/sensibo/test_binary_sensor.py +++ b/tests/components/sensibo/test_binary_sensor.py @@ -4,19 +4,21 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import patch +from pysensibo.model import SensiboData from pytest import MonkeyPatch from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.util import dt -from .response import DATA_FROM_API - from tests.common import async_fire_time_changed async def test_binary_sensor( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo binary sensor.""" @@ -30,15 +32,15 @@ async def test_binary_sensor( assert state4.state == "on" monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"].motion_sensors["AABBCC"], "alive", False + get_data.parsed["ABC999111"].motion_sensors["AABBCC"], "alive", False ) monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"].motion_sensors["AABBCC"], "motion", False + get_data.parsed["ABC999111"].motion_sensors["AABBCC"], "motion", False ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, diff --git a/tests/components/sensibo/test_climate.py b/tests/components/sensibo/test_climate.py index 8b087da5d95..0b6c043240c 100644 --- a/tests/components/sensibo/test_climate.py +++ b/tests/components/sensibo/test_climate.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import patch +from pysensibo.model import SensiboData import pytest from voluptuous import MultipleInvalid @@ -33,12 +34,12 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.util import dt -from .response import DATA_FROM_API - from tests.common import async_fire_time_changed -async def test_climate(hass: HomeAssistant, load_int: ConfigEntry) -> None: +async def test_climate( + hass: HomeAssistant, load_int: ConfigEntry, get_data: SensiboData +) -> None: """Test the Sensibo climate.""" state1 = hass.states.get("climate.hallway") @@ -54,7 +55,7 @@ async def test_climate(hass: HomeAssistant, load_int: ConfigEntry) -> None: "fan_only", "off", ], - "min_temp": 18, + "min_temp": 17, "max_temp": 20, "target_temp_step": 1, "fan_modes": ["quiet", "low", "medium"], @@ -63,9 +64,9 @@ async def test_climate(hass: HomeAssistant, load_int: ConfigEntry) -> None: "fixedTop", "fixedMiddleTop", ], - "current_temperature": 22.4, + "current_temperature": 21.2, "temperature": 25, - "current_humidity": 38, + "current_humidity": 32.9, "fan_mode": "high", "swing_mode": "stopped", "friendly_name": "Hallway", @@ -76,7 +77,10 @@ async def test_climate(hass: HomeAssistant, load_int: ConfigEntry) -> None: async def test_climate_fan( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate fan service.""" @@ -99,7 +103,7 @@ async def test_climate_fan( assert state2.attributes["fan_mode"] == "low" monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "active_features", [ "timestamp", @@ -113,7 +117,7 @@ async def test_climate_fan( ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -138,7 +142,10 @@ async def test_climate_fan( async def test_climate_swing( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate swing service.""" @@ -161,7 +168,7 @@ async def test_climate_swing( assert state2.attributes["swing_mode"] == "fixedTop" monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "active_features", [ "timestamp", @@ -174,7 +181,7 @@ async def test_climate_swing( ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -199,7 +206,10 @@ async def test_climate_swing( async def test_climate_temperatures( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate temperature service.""" @@ -234,7 +244,7 @@ async def test_climate_temperatures( await hass.async_block_till_done() state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 18 + assert state2.attributes["temperature"] == 17 with patch( "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", @@ -250,7 +260,7 @@ async def test_climate_temperatures( await hass.async_block_till_done() state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 18 + assert state2.attributes["temperature"] == 17 with patch( "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", @@ -299,7 +309,7 @@ async def test_climate_temperatures( assert state2.attributes["temperature"] == 20 monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "active_features", [ "timestamp", @@ -312,7 +322,7 @@ async def test_climate_temperatures( ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -338,12 +348,15 @@ async def test_climate_temperatures( async def test_climate_temperature_is_none( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate temperature service no temperature provided.""" monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "active_features", [ "timestamp", @@ -357,13 +370,13 @@ async def test_climate_temperature_is_none( ], ) monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "target_temp", 25, ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -395,12 +408,15 @@ async def test_climate_temperature_is_none( async def test_climate_hvac_mode( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate hvac mode service.""" monkeypatch.setattr( - DATA_FROM_API.parsed["ABC999111"], + get_data.parsed["ABC999111"], "active_features", [ "timestamp", @@ -415,7 +431,7 @@ async def test_climate_hvac_mode( ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -428,7 +444,7 @@ async def test_climate_hvac_mode( with patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", return_value={"result": {"status": "Success"}}, @@ -444,10 +460,10 @@ async def test_climate_hvac_mode( state2 = hass.states.get("climate.hallway") assert state2.state == "off" - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", False) + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", False) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -457,7 +473,7 @@ async def test_climate_hvac_mode( with patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", return_value={"result": {"status": "Success"}}, @@ -475,15 +491,18 @@ async def test_climate_hvac_mode( async def test_climate_on_off( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate on/off service.""" - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -526,15 +545,18 @@ async def test_climate_on_off( async def test_climate_service_failed( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate service failed.""" - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -563,15 +585,18 @@ async def test_climate_service_failed( async def test_climate_assumed_state( - hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: pytest.MonkeyPatch, + get_data: SensiboData, ) -> None: """Test the Sensibo climate assumed state service.""" - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, @@ -584,7 +609,7 @@ async def test_climate_assumed_state( with patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", return_value={"result": {"status": "Success"}}, diff --git a/tests/components/sensibo/test_coordinator.py b/tests/components/sensibo/test_coordinator.py index 703eb8b184b..f0b9d60f112 100644 --- a/tests/components/sensibo/test_coordinator.py +++ b/tests/components/sensibo/test_coordinator.py @@ -15,13 +15,12 @@ from homeassistant.core import HomeAssistant from homeassistant.util import dt from . import ENTRY_CONFIG -from .response import DATA_FROM_API from tests.common import MockConfigEntry, async_fire_time_changed async def test_coordinator( - hass: HomeAssistant, monkeypatch: pytest.MonkeyPatch + hass: HomeAssistant, monkeypatch: pytest.MonkeyPatch, get_data: SensiboData ) -> None: """Test the Sensibo coordinator with errors.""" config_entry = MockConfigEntry( @@ -44,9 +43,9 @@ async def test_coordinator( "homeassistant.components.sensibo.util.SensiboClient.async_get_me", return_value={"result": {"username": "username"}}, ): - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) - mock_data.return_value = DATA_FROM_API + monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) + mock_data.return_value = get_data await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() mock_data.assert_called_once() @@ -71,10 +70,10 @@ async def test_coordinator( assert state.state == STATE_UNAVAILABLE mock_data.reset_mock() - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(DATA_FROM_API.parsed["ABC999111"], "device_on", True) + monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - mock_data.return_value = DATA_FROM_API + mock_data.return_value = get_data mock_data.side_effect = None async_fire_time_changed(hass, dt.utcnow() + timedelta(minutes=5)) await hass.async_block_till_done() diff --git a/tests/components/sensibo/test_diagnostics.py b/tests/components/sensibo/test_diagnostics.py index b4e85dad2b4..636fefa5054 100644 --- a/tests/components/sensibo/test_diagnostics.py +++ b/tests/components/sensibo/test_diagnostics.py @@ -17,32 +17,10 @@ async def test_diagnostics( diag = await get_diagnostics_for_config_entry(hass, hass_client, entry) - assert diag == { - "status": "success", - "result": [ - { - "id": "**REDACTED**", - "qrId": "**REDACTED**", - "room": {"uid": "**REDACTED**", "name": "Hallway", "icon": "Lounge"}, - "acState": { - "timestamp": { - "time": "2022-04-30T19:58:15.544787Z", - "secondsAgo": 0, - }, - "on": False, - "mode": "fan", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "location": "**REDACTED**", - "accessPoint": {"ssid": "**REDACTED**", "password": None}, - "macAddress": "**REDACTED**", - "autoOffMinutes": None, - "autoOffEnabled": False, - "antiMoldTimer": None, - "antiMoldConfig": None, - } - ], - } + assert diag["status"] == "success" + for device in diag["result"]: + assert device["id"] == "**REDACTED**" + assert device["qrId"] == "**REDACTED**" + assert device["macAddress"] == "**REDACTED**" + assert device["location"] == "**REDACTED**" + assert device["productModel"] in ["skyv2", "pure"] diff --git a/tests/components/sensibo/test_entity.py b/tests/components/sensibo/test_entity.py index 7df70c7d45e..27e0f4df772 100644 --- a/tests/components/sensibo/test_entity.py +++ b/tests/components/sensibo/test_entity.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import patch +from pysensibo.model import SensiboData import pytest from homeassistant.components.climate.const import ( @@ -24,12 +25,12 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.util import dt -from .response import DATA_FROM_API - from tests.common import async_fire_time_changed -async def test_entity(hass: HomeAssistant, load_int: ConfigEntry) -> None: +async def test_entity( + hass: HomeAssistant, load_int: ConfigEntry, get_data: SensiboData +) -> None: """Test the Sensibo climate.""" state1 = hass.states.get("climate.hallway") @@ -55,7 +56,10 @@ async def test_entity(hass: HomeAssistant, load_int: ConfigEntry) -> None: @pytest.mark.parametrize("p_error", SENSIBO_ERRORS) async def test_entity_send_command( - hass: HomeAssistant, p_error: Exception, load_int: ConfigEntry + hass: HomeAssistant, + p_error: Exception, + load_int: ConfigEntry, + get_data: SensiboData, ) -> None: """Test the Sensibo send command with error.""" @@ -94,7 +98,7 @@ async def test_entity_send_command( async def test_entity_send_command_calibration( - hass: HomeAssistant, load_int: ConfigEntry + hass: HomeAssistant, load_int: ConfigEntry, get_data: SensiboData ) -> None: """Test the Sensibo send command for calibration.""" @@ -106,7 +110,7 @@ async def test_entity_send_command_calibration( with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ): async_fire_time_changed( hass, diff --git a/tests/components/sensibo/test_init.py b/tests/components/sensibo/test_init.py index b9ad56eaf07..505816e3f41 100644 --- a/tests/components/sensibo/test_init.py +++ b/tests/components/sensibo/test_init.py @@ -3,6 +3,8 @@ from __future__ import annotations from unittest.mock import patch +from pysensibo.model import SensiboData + from homeassistant import config_entries from homeassistant.components.sensibo.const import DOMAIN from homeassistant.components.sensibo.util import NoUsernameError @@ -10,12 +12,11 @@ from homeassistant.config_entries import SOURCE_USER from homeassistant.core import HomeAssistant from . import ENTRY_CONFIG -from .response import DATA_FROM_API from tests.common import MockConfigEntry -async def test_setup_entry(hass: HomeAssistant) -> None: +async def test_setup_entry(hass: HomeAssistant, get_data: SensiboData) -> None: """Test setup entry.""" entry = MockConfigEntry( domain=DOMAIN, @@ -29,7 +30,7 @@ async def test_setup_entry(hass: HomeAssistant) -> None: with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, @@ -43,7 +44,7 @@ async def test_setup_entry(hass: HomeAssistant) -> None: assert entry.state == config_entries.ConfigEntryState.LOADED -async def test_migrate_entry(hass: HomeAssistant) -> None: +async def test_migrate_entry(hass: HomeAssistant, get_data: SensiboData) -> None: """Test migrate entry unique id.""" entry = MockConfigEntry( domain=DOMAIN, @@ -57,7 +58,7 @@ async def test_migrate_entry(hass: HomeAssistant) -> None: with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, @@ -73,7 +74,7 @@ async def test_migrate_entry(hass: HomeAssistant) -> None: assert entry.unique_id == "username" -async def test_migrate_entry_fails(hass: HomeAssistant) -> None: +async def test_migrate_entry_fails(hass: HomeAssistant, get_data: SensiboData) -> None: """Test migrate entry unique id.""" entry = MockConfigEntry( domain=DOMAIN, @@ -101,7 +102,7 @@ async def test_migrate_entry_fails(hass: HomeAssistant) -> None: assert entry.unique_id == "12" -async def test_unload_entry(hass: HomeAssistant) -> None: +async def test_unload_entry(hass: HomeAssistant, get_data: SensiboData) -> None: """Test unload an entry.""" entry = MockConfigEntry( domain=DOMAIN, @@ -115,7 +116,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=DATA_FROM_API, + return_value=get_data, ), patch( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, From 074dfd5a21de0e205a723f892238be7b00657398 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Wed, 11 May 2022 23:05:42 +0100 Subject: [PATCH 359/930] Allow RTMP sources for RTSPtoWeb (#71695) --- homeassistant/components/camera/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 2079e962459..57019f7c68d 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -103,7 +103,7 @@ class CameraEntityFeature(IntEnum): SUPPORT_ON_OFF: Final = 1 SUPPORT_STREAM: Final = 2 -RTSP_PREFIXES = {"rtsp://", "rtsps://"} +RTSP_PREFIXES = {"rtsp://", "rtsps://", "rtmp://"} DEFAULT_CONTENT_TYPE: Final = "image/jpeg" ENTITY_IMAGE_URL: Final = "/api/camera_proxy/{0}?token={1}" From 81e8d2ab862b78b7936a0ba8b12a3643e76ccd94 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 17:27:02 -0500 Subject: [PATCH 360/930] Significantly improve logbook performance when selecting entities (#71657) --- homeassistant/components/logbook/__init__.py | 277 ++------------ homeassistant/components/logbook/queries.py | 377 +++++++++++++++++++ tests/components/logbook/test_init.py | 96 ++++- 3 files changed, 480 insertions(+), 270 deletions(-) create mode 100644 homeassistant/components/logbook/queries.py diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index a877cba4ff2..9cf3ad99e57 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -1,23 +1,18 @@ """Event parser and human readable log generator.""" from __future__ import annotations -from collections.abc import Callable, Generator, Iterable +from collections.abc import Callable, Generator from contextlib import suppress from datetime import datetime as dt, timedelta from http import HTTPStatus import json +import logging import re from typing import Any, cast from aiohttp import web -import sqlalchemy -from sqlalchemy import lambda_stmt, select from sqlalchemy.engine.row import Row -from sqlalchemy.orm import aliased from sqlalchemy.orm.query import Query -from sqlalchemy.sql.expression import literal -from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select import voluptuous as vol from homeassistant.components import frontend @@ -27,15 +22,8 @@ from homeassistant.components.history import ( sqlalchemy_filter_from_include_exclude_conf, ) from homeassistant.components.http import HomeAssistantView -from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder import get_instance -from homeassistant.components.recorder.models import ( - EventData, - Events, - StateAttributes, - States, - process_timestamp_to_utc_isoformat, -) +from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat from homeassistant.components.recorder.util import session_scope from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN @@ -75,66 +63,32 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass import homeassistant.util.dt as dt_util -ENTITY_ID_JSON_TEMPLATE = '%"entity_id":"{}"%' +from .queries import statement_for_request + +_LOGGER = logging.getLogger(__name__) + + FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"') ATTR_MESSAGE = "message" -CONTINUOUS_DOMAINS = {PROXIMITY_DOMAIN, SENSOR_DOMAIN} -CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] - DOMAIN = "logbook" -EMPTY_JSON_OBJECT = "{}" -UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' -UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" HA_DOMAIN_ENTITY_ID = f"{HA_DOMAIN}._" CONFIG_SCHEMA = vol.Schema( {DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA ) -HOMEASSISTANT_EVENTS = [ - EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, -] +HOMEASSISTANT_EVENTS = {EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP} -ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = [ +ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = ( EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE, *HOMEASSISTANT_EVENTS, -] - -ALL_EVENT_TYPES = [ - EVENT_STATE_CHANGED, - *ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, -] - - -EVENT_COLUMNS = [ - Events.event_type.label("event_type"), - Events.event_data.label("event_data"), - Events.time_fired.label("time_fired"), - Events.context_id.label("context_id"), - Events.context_user_id.label("context_user_id"), - Events.context_parent_id.label("context_parent_id"), -] - -STATE_COLUMNS = [ - States.state.label("state"), - States.entity_id.label("entity_id"), - States.attributes.label("attributes"), - StateAttributes.shared_attrs.label("shared_attrs"), -] - -EMPTY_STATE_COLUMNS = [ - literal(value=None, type_=sqlalchemy.String).label("state"), - literal(value=None, type_=sqlalchemy.String).label("entity_id"), - literal(value=None, type_=sqlalchemy.Text).label("attributes"), - literal(value=None, type_=sqlalchemy.Text).label("shared_attrs"), -] +) SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} @@ -295,7 +249,6 @@ class LogbookView(HomeAssistantView): hass = request.app["hass"] - entity_matches_only = "entity_matches_only" in request.query context_id = request.query.get("context_id") if entity_ids and context_id: @@ -313,7 +266,6 @@ class LogbookView(HomeAssistantView): entity_ids, self.filters, self.entities_filter, - entity_matches_only, context_id, ) ) @@ -416,7 +368,6 @@ def _get_events( entity_ids: list[str] | None = None, filters: Filters | None = None, entities_filter: EntityFilter | Callable[[str], bool] | None = None, - entity_matches_only: bool = False, context_id: str | None = None, ) -> list[dict[str, Any]]: """Get events for a period of time.""" @@ -428,10 +379,13 @@ def _get_events( event_data_cache: dict[str, dict[str, Any]] = {} context_lookup: dict[str | None, Row | None] = {None: None} event_cache = EventCache(event_data_cache) - external_events = hass.data.get(DOMAIN, {}) + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ] = hass.data.get(DOMAIN, {}) context_augmenter = ContextAugmenter( context_lookup, entity_name_cache, external_events, event_cache ) + event_types = (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" @@ -441,6 +395,8 @@ def _get_events( rows = query.yield_per(1000) for row in rows: context_lookup.setdefault(row.context_id, row) + if row.context_only: + continue event_type = row.event_type if event_type != EVENT_CALL_SERVICE and ( event_type == EVENT_STATE_CHANGED @@ -451,22 +407,15 @@ def _get_events( if entity_ids is not None: entities_filter = generate_filter([], entity_ids, [], []) - event_types = [ - *ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, - *hass.data.get(DOMAIN, {}), - ] - entity_filter = None - if entity_ids is None and filters: - entity_filter = filters.entity_filter() # type: ignore[no-untyped-call] - stmt = _generate_logbook_query( - start_day, - end_day, - event_types, - entity_ids, - entity_filter, - entity_matches_only, - context_id, + stmt = statement_for_request( + start_day, end_day, event_types, entity_ids, filters, context_id ) + if _LOGGER.isEnabledFor(logging.DEBUG): + _LOGGER.debug( + "Literal statement: %s", + stmt.compile(compile_kwargs={"literal_binds": True}), + ) + with session_scope(hass=hass) as session: return list( _humanify( @@ -479,182 +428,6 @@ def _get_events( ) -def _generate_logbook_query( - start_day: dt, - end_day: dt, - event_types: list[str], - entity_ids: list[str] | None = None, - entity_filter: Any | None = None, - entity_matches_only: bool = False, - context_id: str | None = None, -) -> StatementLambdaElement: - """Generate a logbook query lambda_stmt.""" - stmt = lambda_stmt( - lambda: _generate_events_query_without_states() - .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) - .where(Events.event_type.in_(event_types)) - .outerjoin(EventData, (Events.data_id == EventData.data_id)) - ) - if entity_ids is not None: - if entity_matches_only: - # When entity_matches_only is provided, contexts and events that do not - # contain the entity_ids are not included in the logbook response. - stmt.add_criteria( - lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)), - track_on=entity_ids, - ) - stmt += lambda s: s.union_all( - _generate_states_query() - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id.in_(entity_ids)) - ) - else: - if context_id is not None: - # Once all the old `state_changed` events - # are gone from the database remove the - # union_all(_generate_legacy_events_context_id_query()....) - stmt += lambda s: s.where(Events.context_id == context_id).union_all( - _generate_legacy_events_context_id_query() - .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) - .where(Events.context_id == context_id), - _generate_states_query() - .where( - (States.last_updated > start_day) & (States.last_updated < end_day) - ) - .outerjoin(Events, (States.event_id == Events.event_id)) - .where(States.context_id == context_id), - ) - elif entity_filter is not None: - stmt += lambda s: s.union_all( - _generate_states_query() - .where( - (States.last_updated > start_day) & (States.last_updated < end_day) - ) - .where(entity_filter) - ) - else: - stmt += lambda s: s.union_all( - _generate_states_query().where( - (States.last_updated > start_day) & (States.last_updated < end_day) - ) - ) - - stmt += lambda s: s.order_by(Events.time_fired) - return stmt - - -def _generate_events_query_without_data() -> Select: - return select( - literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label("event_type"), - literal(value=None, type_=sqlalchemy.Text).label("event_data"), - States.last_changed.label("time_fired"), - States.context_id.label("context_id"), - States.context_user_id.label("context_user_id"), - States.context_parent_id.label("context_parent_id"), - literal(value=None, type_=sqlalchemy.Text).label("shared_data"), - *STATE_COLUMNS, - ) - - -def _generate_legacy_events_context_id_query() -> Select: - """Generate a legacy events context id query that also joins states.""" - # This can be removed once we no longer have event_ids in the states table - return ( - select( - *EVENT_COLUMNS, - literal(value=None, type_=sqlalchemy.String).label("shared_data"), - States.state, - States.entity_id, - States.attributes, - StateAttributes.shared_attrs, - ) - .outerjoin(States, (Events.event_id == States.event_id)) - .where(States.last_updated == States.last_changed) - .where(_not_continuous_entity_matcher()) - .outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) - ) - - -def _generate_events_query_without_states() -> Select: - return select( - *EVENT_COLUMNS, EventData.shared_data.label("shared_data"), *EMPTY_STATE_COLUMNS - ) - - -def _generate_states_query() -> Select: - old_state = aliased(States, name="old_state") - return ( - _generate_events_query_without_data() - .outerjoin(old_state, (States.old_state_id == old_state.state_id)) - .where(_missing_state_matcher(old_state)) - .where(_not_continuous_entity_matcher()) - .where(States.last_updated == States.last_changed) - .outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) - ) - - -def _missing_state_matcher(old_state: States) -> sqlalchemy.and_: - # The below removes state change events that do not have - # and old_state or the old_state is missing (newly added entities) - # or the new_state is missing (removed entities) - return sqlalchemy.and_( - old_state.state_id.isnot(None), - (States.state != old_state.state), - States.state.isnot(None), - ) - - -def _not_continuous_entity_matcher() -> sqlalchemy.or_: - """Match non continuous entities.""" - return sqlalchemy.or_( - _not_continuous_domain_matcher(), - sqlalchemy.and_( - _continuous_domain_matcher, _not_uom_attributes_matcher() - ).self_group(), - ) - - -def _not_continuous_domain_matcher() -> sqlalchemy.and_: - """Match not continuous domains.""" - return sqlalchemy.and_( - *[ - ~States.entity_id.like(entity_domain) - for entity_domain in CONTINUOUS_ENTITY_ID_LIKE - ], - ).self_group() - - -def _continuous_domain_matcher() -> sqlalchemy.or_: - """Match continuous domains.""" - return sqlalchemy.or_( - *[ - States.entity_id.like(entity_domain) - for entity_domain in CONTINUOUS_ENTITY_ID_LIKE - ], - ).self_group() - - -def _not_uom_attributes_matcher() -> Any: - """Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql.""" - return ~StateAttributes.shared_attrs.like( - UNIT_OF_MEASUREMENT_JSON_LIKE - ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) - - -def _apply_event_entity_id_matchers(entity_ids: Iterable[str]) -> sqlalchemy.or_: - """Create matchers for the entity_id in the event_data.""" - ors = [] - for entity_id in entity_ids: - like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) - ors.append(Events.event_data.like(like)) - ors.append(EventData.shared_data.like(like)) - return sqlalchemy.or_(*ors) - - def _keep_row( hass: HomeAssistant, event_type: str, diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py new file mode 100644 index 00000000000..8c13719a1cf --- /dev/null +++ b/homeassistant/components/logbook/queries.py @@ -0,0 +1,377 @@ +"""Queries for logbook.""" +from __future__ import annotations + +from collections.abc import Iterable +from datetime import datetime as dt +from typing import Any + +import sqlalchemy +from sqlalchemy import lambda_stmt, select +from sqlalchemy.orm import aliased +from sqlalchemy.sql.expression import literal +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from homeassistant.components.history import Filters +from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN +from homeassistant.components.recorder.models import ( + EventData, + Events, + StateAttributes, + States, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import EVENT_STATE_CHANGED + +ENTITY_ID_JSON_TEMPLATE = '%"entity_id":"{}"%' + +CONTINUOUS_DOMAINS = {PROXIMITY_DOMAIN, SENSOR_DOMAIN} +CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] + +UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' +UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" + + +EVENT_COLUMNS = ( + Events.event_type.label("event_type"), + Events.event_data.label("event_data"), + Events.time_fired.label("time_fired"), + Events.context_id.label("context_id"), + Events.context_user_id.label("context_user_id"), + Events.context_parent_id.label("context_parent_id"), +) + +STATE_COLUMNS = ( + States.state.label("state"), + States.entity_id.label("entity_id"), + States.attributes.label("attributes"), + StateAttributes.shared_attrs.label("shared_attrs"), +) + +EMPTY_STATE_COLUMNS = ( + literal(value=None, type_=sqlalchemy.String).label("state"), + literal(value=None, type_=sqlalchemy.String).label("entity_id"), + literal(value=None, type_=sqlalchemy.Text).label("attributes"), + literal(value=None, type_=sqlalchemy.Text).label("shared_attrs"), +) + +EVENT_ROWS_NO_STATES = ( + *EVENT_COLUMNS, + EventData.shared_data.label("shared_data"), + *EMPTY_STATE_COLUMNS, +) + +# Virtual column to tell logbook if it should avoid processing +# the event as its only used to link contexts +CONTEXT_ONLY = literal("1").label("context_only") +NOT_CONTEXT_ONLY = literal(None).label("context_only") + + +def statement_for_request( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str] | None = None, + filters: Filters | None = None, + context_id: str | None = None, +) -> StatementLambdaElement: + """Generate the logbook statement for a logbook request.""" + + # No entities: logbook sends everything for the timeframe + # limited by the context_id and the yaml configured filter + if not entity_ids: + entity_filter = filters.entity_filter() if filters else None # type: ignore[no-untyped-call] + return _all_stmt(start_day, end_day, event_types, entity_filter, context_id) + + # Multiple entities: logbook sends everything for the timeframe for the entities + # + # This is the least efficient query because we use + # like matching which means part of the query has to be built each + # time when the entity_ids are not in the cache + if len(entity_ids) > 1: + return _entities_stmt(start_day, end_day, event_types, entity_ids) + + # Single entity: logbook sends everything for the timeframe for the entity + entity_id = entity_ids[0] + entity_like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) + return _single_entity_stmt(start_day, end_day, event_types, entity_id, entity_like) + + +def _select_events_context_id_subquery( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], +) -> Select: + """Generate the select for a context_id subquery.""" + return ( + select(Events.context_id) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.event_type.in_(event_types)) + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + + +def _select_entities_context_ids_sub_query( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], +) -> Select: + """Generate a subquery to find context ids for multiple entities.""" + return ( + _select_events_context_id_subquery(start_day, end_day, event_types) + .where(_apply_event_entity_id_matchers(entity_ids)) + .union_all( + select(States.context_id) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)) + ) + .subquery() + ) + + +def _select_events_context_only() -> Select: + """Generate an events query that mark them as for context_only. + + By marking them as context_only we know they are only for + linking context ids and we can avoid processing them. + """ + return select(*EVENT_ROWS_NO_STATES, CONTEXT_ONLY).outerjoin( + EventData, (Events.data_id == EventData.data_id) + ) + + +def _entities_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], +) -> StatementLambdaElement: + """Generate a logbook query for multiple entities.""" + stmt = lambda_stmt( + lambda: _select_events_without_states(start_day, end_day, event_types) + ) + stmt = stmt.add_criteria( + lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)).union_all( + _select_states(start_day, end_day).where(States.entity_id.in_(entity_ids)), + _select_events_context_only().where( + Events.context_id.in_( + _select_entities_context_ids_sub_query( + start_day, + end_day, + event_types, + entity_ids, + ) + ) + ), + ), + # Since _apply_event_entity_id_matchers generates multiple + # like statements we need to use the entity_ids in the + # the cache key since the sql can change based on the + # likes. + track_on=(str(entity_ids),), + ) + stmt += lambda s: s.order_by(Events.time_fired) + return stmt + + +def _select_entity_context_ids_sub_query( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_id: str, + entity_id_like: str, +) -> Select: + """Generate a subquery to find context ids for a single entity.""" + return ( + _select_events_context_id_subquery(start_day, end_day, event_types) + .where( + Events.event_data.like(entity_id_like) + | EventData.shared_data.like(entity_id_like) + ) + .union_all( + select(States.context_id) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id == entity_id) + ) + .subquery() + ) + + +def _single_entity_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_id: str, + entity_id_like: str, +) -> StatementLambdaElement: + """Generate a logbook query for a single entity.""" + stmt = lambda_stmt( + lambda: _select_events_without_states(start_day, end_day, event_types) + .where( + Events.event_data.like(entity_id_like) + | EventData.shared_data.like(entity_id_like) + ) + .union_all( + _select_states(start_day, end_day).where(States.entity_id == entity_id), + _select_events_context_only().where( + Events.context_id.in_( + _select_entity_context_ids_sub_query( + start_day, end_day, event_types, entity_id, entity_id_like + ) + ) + ), + ) + .order_by(Events.time_fired) + ) + return stmt + + +def _all_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_filter: Any | None = None, + context_id: str | None = None, +) -> StatementLambdaElement: + """Generate a logbook query for all entities.""" + stmt = lambda_stmt( + lambda: _select_events_without_states(start_day, end_day, event_types) + ) + if context_id is not None: + # Once all the old `state_changed` events + # are gone from the database remove the + # _legacy_select_events_context_id() + stmt += lambda s: s.where(Events.context_id == context_id).union_all( + _select_states(start_day, end_day).where(States.context_id == context_id), + _legacy_select_events_context_id(start_day, end_day, context_id), + ) + elif entity_filter is not None: + stmt += lambda s: s.union_all( + _select_states(start_day, end_day).where(entity_filter) + ) + else: + stmt += lambda s: s.union_all(_select_states(start_day, end_day)) + stmt += lambda s: s.order_by(Events.time_fired) + return stmt + + +def _legacy_select_events_context_id( + start_day: dt, end_day: dt, context_id: str +) -> Select: + """Generate a legacy events context id select that also joins states.""" + # This can be removed once we no longer have event_ids in the states table + return ( + select( + *EVENT_COLUMNS, + literal(value=None, type_=sqlalchemy.String).label("shared_data"), + *STATE_COLUMNS, + NOT_CONTEXT_ONLY, + ) + .outerjoin(States, (Events.event_id == States.event_id)) + .where(States.last_updated == States.last_changed) + .where(_not_continuous_entity_matcher()) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.context_id == context_id) + ) + + +def _select_events_without_states( + start_day: dt, end_day: dt, event_types: tuple[str, ...] +) -> Select: + """Generate an events select that does not join states.""" + return ( + select(*EVENT_ROWS_NO_STATES, NOT_CONTEXT_ONLY) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.event_type.in_(event_types)) + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + + +def _select_states(start_day: dt, end_day: dt) -> Select: + """Generate a states select that formats the states table as event rows.""" + old_state = aliased(States, name="old_state") + return ( + select( + literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( + "event_type" + ), + literal(value=None, type_=sqlalchemy.Text).label("event_data"), + States.last_changed.label("time_fired"), + States.context_id.label("context_id"), + States.context_user_id.label("context_user_id"), + States.context_parent_id.label("context_parent_id"), + literal(value=None, type_=sqlalchemy.Text).label("shared_data"), + *STATE_COLUMNS, + NOT_CONTEXT_ONLY, + ) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .outerjoin(old_state, (States.old_state_id == old_state.state_id)) + .where(_missing_state_matcher(old_state)) + .where(_not_continuous_entity_matcher()) + .where(States.last_updated == States.last_changed) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + ) + + +def _missing_state_matcher(old_state: States) -> sqlalchemy.and_: + # The below removes state change events that do not have + # and old_state or the old_state is missing (newly added entities) + # or the new_state is missing (removed entities) + return sqlalchemy.and_( + old_state.state_id.isnot(None), + (States.state != old_state.state), + States.state.isnot(None), + ) + + +def _not_continuous_entity_matcher() -> sqlalchemy.or_: + """Match non continuous entities.""" + return sqlalchemy.or_( + _not_continuous_domain_matcher(), + sqlalchemy.and_( + _continuous_domain_matcher, _not_uom_attributes_matcher() + ).self_group(), + ) + + +def _not_continuous_domain_matcher() -> sqlalchemy.and_: + """Match not continuous domains.""" + return sqlalchemy.and_( + *[ + ~States.entity_id.like(entity_domain) + for entity_domain in CONTINUOUS_ENTITY_ID_LIKE + ], + ).self_group() + + +def _continuous_domain_matcher() -> sqlalchemy.or_: + """Match continuous domains.""" + return sqlalchemy.or_( + *[ + States.entity_id.like(entity_domain) + for entity_domain in CONTINUOUS_ENTITY_ID_LIKE + ], + ).self_group() + + +def _not_uom_attributes_matcher() -> Any: + """Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql.""" + return ~StateAttributes.shared_attrs.like( + UNIT_OF_MEASUREMENT_JSON_LIKE + ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) + + +def _apply_event_entity_id_matchers(entity_ids: Iterable[str]) -> sqlalchemy.or_: + """Create matchers for the entity_id in the event_data.""" + ors = [] + for entity_id in entity_ids: + like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) + ors.append(Events.event_data.like(like)) + ors.append(EventData.shared_data.like(like)) + return sqlalchemy.or_(*ors) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 76319ba5a6e..cc10346fc07 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -1321,8 +1321,8 @@ async def test_logbook_context_from_template(hass, hass_client, recorder_mock): assert json_dict[5]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" -async def test_logbook_entity_matches_only(hass, hass_client, recorder_mock): - """Test the logbook view with a single entity and entity_matches_only.""" +async def test_logbook_(hass, hass_client, recorder_mock): + """Test the logbook view with a single entity and .""" await async_setup_component(hass, "logbook", {}) assert await async_setup_component( hass, @@ -1377,7 +1377,7 @@ async def test_logbook_entity_matches_only(hass, hass_client, recorder_mock): # Test today entries with filter by end_time end_time = start + timedelta(hours=24) response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=switch.test_state&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=switch.test_state" ) assert response.status == HTTPStatus.OK json_dict = await response.json() @@ -1390,10 +1390,8 @@ async def test_logbook_entity_matches_only(hass, hass_client, recorder_mock): assert json_dict[1]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" -async def test_logbook_entity_matches_only_multiple_calls( - hass, hass_client, recorder_mock -): - """Test the logbook view with a single entity and entity_matches_only called multiple times.""" +async def test_logbook_many_entities_multiple_calls(hass, hass_client, recorder_mock): + """Test the logbook view with a many entities called multiple times.""" await async_setup_component(hass, "logbook", {}) await async_setup_component(hass, "automation", {}) @@ -1421,7 +1419,7 @@ async def test_logbook_entity_matches_only_multiple_calls( for automation_id in range(5): # Test today entries with filter by end_time response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_{automation_id}_automation&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_{automation_id}_automation" ) assert response.status == HTTPStatus.OK json_dict = await response.json() @@ -1432,7 +1430,7 @@ async def test_logbook_entity_matches_only_multiple_calls( ) response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_0_automation,automation.mock_1_automation,automation.mock_2_automation&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_0_automation,automation.mock_1_automation,automation.mock_2_automation" ) assert response.status == HTTPStatus.OK json_dict = await response.json() @@ -1442,11 +1440,28 @@ async def test_logbook_entity_matches_only_multiple_calls( assert json_dict[1]["entity_id"] == "automation.mock_1_automation" assert json_dict[2]["entity_id"] == "automation.mock_2_automation" + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=automation.mock_4_automation,automation.mock_2_automation,automation.mock_0_automation,automation.mock_1_automation" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() -async def test_custom_log_entry_discoverable_via_entity_matches_only( - hass, hass_client, recorder_mock -): - """Test if a custom log entry is later discoverable via entity_matches_only.""" + assert len(json_dict) == 4 + assert json_dict[0]["entity_id"] == "automation.mock_0_automation" + assert json_dict[1]["entity_id"] == "automation.mock_1_automation" + assert json_dict[2]["entity_id"] == "automation.mock_2_automation" + assert json_dict[3]["entity_id"] == "automation.mock_4_automation" + + response = await client.get( + f"/api/logbook/{end_time.isoformat()}?end_time={end_time}&entity=automation.mock_4_automation,automation.mock_2_automation,automation.mock_0_automation,automation.mock_1_automation" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + assert len(json_dict) == 0 + + +async def test_custom_log_entry_discoverable_via_(hass, hass_client, recorder_mock): + """Test if a custom log entry is later discoverable via .""" await async_setup_component(hass, "logbook", {}) await async_recorder_block_till_done(hass) @@ -1468,7 +1483,7 @@ async def test_custom_log_entry_discoverable_via_entity_matches_only( # Test today entries with filter by end_time end_time = start + timedelta(hours=24) response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time.isoformat()}&entity=switch.test_switch&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time.isoformat()}&entity=switch.test_switch" ) assert response.status == HTTPStatus.OK json_dict = await response.json() @@ -1480,8 +1495,8 @@ async def test_custom_log_entry_discoverable_via_entity_matches_only( assert json_dict[0]["entity_id"] == "switch.test_switch" -async def test_logbook_entity_matches_only_multiple(hass, hass_client, recorder_mock): - """Test the logbook view with a multiple entities and entity_matches_only.""" +async def test_logbook_multiple_entities(hass, hass_client, recorder_mock): + """Test the logbook view with a multiple entities.""" await async_setup_component(hass, "logbook", {}) assert await async_setup_component( hass, @@ -1513,12 +1528,14 @@ async def test_logbook_entity_matches_only_multiple(hass, hass_client, recorder_ # Entity added (should not be logged) hass.states.async_set("switch.test_state", STATE_ON) hass.states.async_set("light.test_state", STATE_ON) + hass.states.async_set("binary_sensor.test_state", STATE_ON) await hass.async_block_till_done() # First state change (should be logged) hass.states.async_set("switch.test_state", STATE_OFF) hass.states.async_set("light.test_state", STATE_OFF) + hass.states.async_set("binary_sensor.test_state", STATE_OFF) await hass.async_block_till_done() @@ -1530,6 +1547,9 @@ async def test_logbook_entity_matches_only_multiple(hass, hass_client, recorder_ "switch.test_state", STATE_ON, context=switch_turn_off_context ) hass.states.async_set("light.test_state", STATE_ON, context=switch_turn_off_context) + hass.states.async_set( + "binary_sensor.test_state", STATE_ON, context=switch_turn_off_context + ) await async_wait_recording_done(hass) client = await hass_client() @@ -1541,7 +1561,7 @@ async def test_logbook_entity_matches_only_multiple(hass, hass_client, recorder_ # Test today entries with filter by end_time end_time = start + timedelta(hours=24) response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=switch.test_state,light.test_state&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=switch.test_state,light.test_state" ) assert response.status == HTTPStatus.OK json_dict = await response.json() @@ -1558,6 +1578,46 @@ async def test_logbook_entity_matches_only_multiple(hass, hass_client, recorder_ assert json_dict[3]["entity_id"] == "light.test_state" assert json_dict[3]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=binary_sensor.test_state,light.test_state" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert len(json_dict) == 4 + + assert json_dict[0]["entity_id"] == "light.test_state" + + assert json_dict[1]["entity_id"] == "binary_sensor.test_state" + + assert json_dict[2]["entity_id"] == "light.test_state" + assert json_dict[2]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + + assert json_dict[3]["entity_id"] == "binary_sensor.test_state" + assert json_dict[3]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=light.test_state,binary_sensor.test_state" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert len(json_dict) == 4 + + assert json_dict[0]["entity_id"] == "light.test_state" + + assert json_dict[1]["entity_id"] == "binary_sensor.test_state" + + assert json_dict[2]["entity_id"] == "light.test_state" + assert json_dict[2]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + + assert json_dict[3]["entity_id"] == "binary_sensor.test_state" + assert json_dict[3]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + async def test_logbook_invalid_entity(hass, hass_client, recorder_mock): """Test the logbook view with requesting an invalid entity.""" @@ -1572,7 +1632,7 @@ async def test_logbook_invalid_entity(hass, hass_client, recorder_mock): # Test today entries with filter by end_time end_time = start + timedelta(hours=24) response = await client.get( - f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=invalid&entity_matches_only" + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity=invalid" ) assert response.status == HTTPStatus.INTERNAL_SERVER_ERROR From e2cef55162793dfacd06559dc994bc868861eb0f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 17:52:22 -0500 Subject: [PATCH 361/930] Add history/history_during_period websocket endpoint (#71688) --- homeassistant/components/history/__init__.py | 85 +++- homeassistant/components/recorder/history.py | 117 +++-- homeassistant/components/recorder/models.py | 91 ++-- .../components/websocket_api/const.py | 6 + .../components/websocket_api/messages.py | 13 +- tests/components/history/test_init.py | 406 ++++++++++++++++++ tests/components/recorder/test_history.py | 17 +- tests/components/recorder/test_models.py | 8 +- 8 files changed, 655 insertions(+), 88 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index 8740f352dee..a0d1f2fa76b 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -1,12 +1,12 @@ """Provide pre-made queries on top of the recorder component.""" from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Iterable, MutableMapping from datetime import datetime as dt, timedelta from http import HTTPStatus import logging import time -from typing import cast +from typing import Any, cast from aiohttp import web from sqlalchemy import not_, or_ @@ -25,7 +25,7 @@ from homeassistant.components.recorder.statistics import ( ) from homeassistant.components.recorder.util import session_scope from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State import homeassistant.helpers.config_validation as cv from homeassistant.helpers.deprecation import deprecated_class, deprecated_function from homeassistant.helpers.entityfilter import ( @@ -40,6 +40,7 @@ import homeassistant.util.dt as dt_util _LOGGER = logging.getLogger(__name__) DOMAIN = "history" +HISTORY_FILTERS = "history_filters" CONF_ORDER = "use_include_order" GLOB_TO_SQL_CHARS = { @@ -83,7 +84,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the history hooks.""" conf = config.get(DOMAIN, {}) - filters = sqlalchemy_filter_from_include_exclude_conf(conf) + hass.data[HISTORY_FILTERS] = filters = sqlalchemy_filter_from_include_exclude_conf( + conf + ) use_include_order = conf.get(CONF_ORDER) @@ -91,6 +94,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: frontend.async_register_built_in_panel(hass, "history", "history", "hass:chart-box") websocket_api.async_register_command(hass, ws_get_statistics_during_period) websocket_api.async_register_command(hass, ws_get_list_statistic_ids) + websocket_api.async_register_command(hass, ws_get_history_during_period) return True @@ -163,6 +167,79 @@ async def ws_get_list_statistic_ids( connection.send_result(msg["id"], statistic_ids) +@websocket_api.websocket_command( + { + vol.Required("type"): "history/history_during_period", + vol.Required("start_time"): str, + vol.Optional("end_time"): str, + vol.Optional("entity_ids"): [str], + vol.Optional("include_start_time_state", default=True): bool, + vol.Optional("significant_changes_only", default=True): bool, + vol.Optional("minimal_response", default=False): bool, + vol.Optional("no_attributes", default=False): bool, + } +) +@websocket_api.async_response +async def ws_get_history_during_period( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict +) -> None: + """Handle history during period websocket command.""" + start_time_str = msg["start_time"] + end_time_str = msg.get("end_time") + + if start_time := dt_util.parse_datetime(start_time_str): + start_time = dt_util.as_utc(start_time) + else: + connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") + return + + if end_time_str: + if end_time := dt_util.parse_datetime(end_time_str): + end_time = dt_util.as_utc(end_time) + else: + connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") + return + else: + end_time = None + + if start_time > dt_util.utcnow(): + connection.send_result(msg["id"], {}) + return + + entity_ids = msg.get("entity_ids") + include_start_time_state = msg["include_start_time_state"] + + if ( + not include_start_time_state + and entity_ids + and not _entities_may_have_state_changes_after(hass, entity_ids, start_time) + ): + connection.send_result(msg["id"], {}) + return + + significant_changes_only = msg["significant_changes_only"] + no_attributes = msg["no_attributes"] + minimal_response = msg["minimal_response"] + compressed_state_format = True + + history_during_period: MutableMapping[ + str, list[State | dict[str, Any]] + ] = await get_instance(hass).async_add_executor_job( + history.get_significant_states, + hass, + start_time, + end_time, + entity_ids, + hass.data[HISTORY_FILTERS], + include_start_time_state, + significant_changes_only, + minimal_response, + no_attributes, + compressed_state_format, + ) + connection.send_result(msg["id"], history_during_period) + + class HistoryPeriodView(HomeAssistantView): """Handle history period requests.""" diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index a061bcd1329..179d25b9f5b 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections import defaultdict -from collections.abc import Iterable, Iterator, MutableMapping +from collections.abc import Callable, Iterable, Iterator, MutableMapping from datetime import datetime from itertools import groupby import logging @@ -10,6 +10,7 @@ import time from typing import Any, cast from sqlalchemy import Column, Text, and_, bindparam, func, or_ +from sqlalchemy.engine.row import Row from sqlalchemy.ext import baked from sqlalchemy.ext.baked import BakedQuery from sqlalchemy.orm.query import Query @@ -17,6 +18,10 @@ from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal from homeassistant.components import recorder +from homeassistant.components.websocket_api.const import ( + COMPRESSED_STATE_LAST_CHANGED, + COMPRESSED_STATE_STATE, +) from homeassistant.core import HomeAssistant, State, split_entity_id import homeassistant.util.dt as dt_util @@ -25,8 +30,10 @@ from .models import ( RecorderRuns, StateAttributes, States, + process_datetime_to_timestamp, process_timestamp, process_timestamp_to_utc_isoformat, + row_to_compressed_state, ) from .util import execute, session_scope @@ -161,6 +168,7 @@ def get_significant_states( significant_changes_only: bool = True, minimal_response: bool = False, no_attributes: bool = False, + compressed_state_format: bool = False, ) -> MutableMapping[str, list[State | dict[str, Any]]]: """Wrap get_significant_states_with_session with an sql session.""" with session_scope(hass=hass) as session: @@ -175,6 +183,7 @@ def get_significant_states( significant_changes_only, minimal_response, no_attributes, + compressed_state_format, ) @@ -199,7 +208,7 @@ def _query_significant_states_with_session( filters: Any = None, significant_changes_only: bool = True, no_attributes: bool = False, -) -> list[States]: +) -> list[Row]: """Query the database for significant state changes.""" if _LOGGER.isEnabledFor(logging.DEBUG): timer_start = time.perf_counter() @@ -271,6 +280,7 @@ def get_significant_states_with_session( significant_changes_only: bool = True, minimal_response: bool = False, no_attributes: bool = False, + compressed_state_format: bool = False, ) -> MutableMapping[str, list[State | dict[str, Any]]]: """ Return states changes during UTC period start_time - end_time. @@ -304,6 +314,7 @@ def get_significant_states_with_session( include_start_time_state, minimal_response, no_attributes, + compressed_state_format, ) @@ -541,7 +552,7 @@ def _get_states_baked_query_for_all( return baked_query -def _get_states_with_session( +def _get_rows_with_session( hass: HomeAssistant, session: Session, utc_point_in_time: datetime, @@ -549,7 +560,7 @@ def _get_states_with_session( run: RecorderRuns | None = None, filters: Any | None = None, no_attributes: bool = False, -) -> list[State]: +) -> list[Row]: """Return the states at a specific point in time.""" if entity_ids and len(entity_ids) == 1: return _get_single_entity_states_with_session( @@ -570,17 +581,13 @@ def _get_states_with_session( else: baked_query = _get_states_baked_query_for_all(hass, filters, no_attributes) - attr_cache: dict[str, dict[str, Any]] = {} - return [ - LazyState(row, attr_cache) - for row in execute( - baked_query(session).params( - run_start=run.start, - utc_point_in_time=utc_point_in_time, - entity_ids=entity_ids, - ) + return execute( + baked_query(session).params( + run_start=run.start, + utc_point_in_time=utc_point_in_time, + entity_ids=entity_ids, ) - ] + ) def _get_single_entity_states_with_session( @@ -589,7 +596,7 @@ def _get_single_entity_states_with_session( utc_point_in_time: datetime, entity_id: str, no_attributes: bool = False, -) -> list[State]: +) -> list[Row]: # Use an entirely different (and extremely fast) query if we only # have a single entity id baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) @@ -607,19 +614,20 @@ def _get_single_entity_states_with_session( utc_point_in_time=utc_point_in_time, entity_id=entity_id ) - return [LazyState(row) for row in execute(query)] + return execute(query) def _sorted_states_to_dict( hass: HomeAssistant, session: Session, - states: Iterable[States], + states: Iterable[Row], start_time: datetime, entity_ids: list[str] | None, filters: Any = None, include_start_time_state: bool = True, minimal_response: bool = False, no_attributes: bool = False, + compressed_state_format: bool = False, ) -> MutableMapping[str, list[State | dict[str, Any]]]: """Convert SQL results into JSON friendly data structure. @@ -632,6 +640,19 @@ def _sorted_states_to_dict( each list of states, otherwise our graphs won't start on the Y axis correctly. """ + if compressed_state_format: + state_class = row_to_compressed_state + _process_timestamp: Callable[ + [datetime], float | str + ] = process_datetime_to_timestamp + attr_last_changed = COMPRESSED_STATE_LAST_CHANGED + attr_state = COMPRESSED_STATE_STATE + else: + state_class = LazyState # type: ignore[assignment] + _process_timestamp = process_timestamp_to_utc_isoformat + attr_last_changed = LAST_CHANGED_KEY + attr_state = STATE_KEY + result: dict[str, list[State | dict[str, Any]]] = defaultdict(list) # Set all entity IDs to empty lists in result set to maintain the order if entity_ids is not None: @@ -640,27 +661,24 @@ def _sorted_states_to_dict( # Get the states at the start time timer_start = time.perf_counter() + initial_states: dict[str, Row] = {} if include_start_time_state: - for state in _get_states_with_session( - hass, - session, - start_time, - entity_ids, - filters=filters, - no_attributes=no_attributes, - ): - state.last_updated = start_time - state.last_changed = start_time - result[state.entity_id].append(state) + initial_states = { + row.entity_id: row + for row in _get_rows_with_session( + hass, + session, + start_time, + entity_ids, + filters=filters, + no_attributes=no_attributes, + ) + } if _LOGGER.isEnabledFor(logging.DEBUG): elapsed = time.perf_counter() - timer_start _LOGGER.debug("getting %d first datapoints took %fs", len(result), elapsed) - # Called in a tight loop so cache the function - # here - _process_timestamp_to_utc_isoformat = process_timestamp_to_utc_isoformat - if entity_ids and len(entity_ids) == 1: states_iter: Iterable[tuple[str | Column, Iterator[States]]] = ( (entity_ids[0], iter(states)), @@ -670,11 +688,15 @@ def _sorted_states_to_dict( # Append all changes to it for ent_id, group in states_iter: - ent_results = result[ent_id] attr_cache: dict[str, dict[str, Any]] = {} + prev_state: Column | str + ent_results = result[ent_id] + if row := initial_states.pop(ent_id, None): + prev_state = row.state + ent_results.append(state_class(row, attr_cache, start_time)) if not minimal_response or split_entity_id(ent_id)[0] in NEED_ATTRIBUTE_DOMAINS: - ent_results.extend(LazyState(db_state, attr_cache) for db_state in group) + ent_results.extend(state_class(db_state, attr_cache) for db_state in group) continue # With minimal response we only provide a native @@ -684,34 +706,35 @@ def _sorted_states_to_dict( if not ent_results: if (first_state := next(group, None)) is None: continue - ent_results.append(LazyState(first_state, attr_cache)) + prev_state = first_state.state + ent_results.append(state_class(first_state, attr_cache)) - assert isinstance(ent_results[-1], State) - prev_state: Column | str = ent_results[-1].state initial_state_count = len(ent_results) - - db_state = None - for db_state in group: + row = None + for row in group: # With minimal response we do not care about attribute # changes so we can filter out duplicate states - if (state := db_state.state) == prev_state: + if (state := row.state) == prev_state: continue ent_results.append( { - STATE_KEY: state, - LAST_CHANGED_KEY: _process_timestamp_to_utc_isoformat( - db_state.last_changed - ), + attr_state: state, + attr_last_changed: _process_timestamp(row.last_changed), } ) prev_state = state - if db_state and len(ent_results) != initial_state_count: + if row and len(ent_results) != initial_state_count: # There was at least one state change # replace the last minimal state with # a full state - ent_results[-1] = LazyState(db_state, attr_cache) + ent_results[-1] = state_class(row, attr_cache) + + # If there are no states beyond the initial state, + # the state a was never popped from initial_states + for ent_id, row in initial_states.items(): + result[ent_id].append(state_class(row, {}, start_time)) # Filter out the empty lists if some states had 0 results. return {key: val for key, val in result.items() if val} diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 9cc94b3019f..38d6b3319aa 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -28,6 +28,12 @@ from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import declarative_base, relationship from sqlalchemy.orm.session import Session +from homeassistant.components.websocket_api.const import ( + COMPRESSED_STATE_ATTRIBUTES, + COMPRESSED_STATE_LAST_CHANGED, + COMPRESSED_STATE_LAST_UPDATED, + COMPRESSED_STATE_STATE, +) from homeassistant.const import ( MAX_LENGTH_EVENT_CONTEXT_ID, MAX_LENGTH_EVENT_EVENT_TYPE, @@ -612,6 +618,13 @@ def process_timestamp_to_utc_isoformat(ts: datetime | None) -> str | None: return ts.astimezone(dt_util.UTC).isoformat() +def process_datetime_to_timestamp(ts: datetime) -> float: + """Process a timestamp into a unix timestamp.""" + if ts.tzinfo == dt_util.UTC: + return ts.timestamp() + return ts.replace(tzinfo=dt_util.UTC).timestamp() + + class LazyState(State): """A lazy version of core State.""" @@ -621,45 +634,30 @@ class LazyState(State): "_last_changed", "_last_updated", "_context", - "_attr_cache", + "attr_cache", ] def __init__( # pylint: disable=super-init-not-called - self, row: Row, attr_cache: dict[str, dict[str, Any]] | None = None + self, + row: Row, + attr_cache: dict[str, dict[str, Any]], + start_time: datetime | None = None, ) -> None: """Init the lazy state.""" self._row = row self.entity_id: str = self._row.entity_id self.state = self._row.state or "" self._attributes: dict[str, Any] | None = None - self._last_changed: datetime | None = None - self._last_updated: datetime | None = None + self._last_changed: datetime | None = start_time + self._last_updated: datetime | None = start_time self._context: Context | None = None - self._attr_cache = attr_cache + self.attr_cache = attr_cache @property # type: ignore[override] def attributes(self) -> dict[str, Any]: # type: ignore[override] """State attributes.""" if self._attributes is None: - source = self._row.shared_attrs or self._row.attributes - if self._attr_cache is not None and ( - attributes := self._attr_cache.get(source) - ): - self._attributes = attributes - return attributes - if source == EMPTY_JSON_OBJECT or source is None: - self._attributes = {} - return self._attributes - try: - self._attributes = json.loads(source) - except ValueError: - # When json.loads fails - _LOGGER.exception( - "Error converting row to state attributes: %s", self._row - ) - self._attributes = {} - if self._attr_cache is not None: - self._attr_cache[source] = self._attributes + self._attributes = decode_attributes_from_row(self._row, self.attr_cache) return self._attributes @attributes.setter @@ -748,3 +746,48 @@ class LazyState(State): and self.state == other.state and self.attributes == other.attributes ) + + +def decode_attributes_from_row( + row: Row, attr_cache: dict[str, dict[str, Any]] +) -> dict[str, Any]: + """Decode attributes from a database row.""" + source: str = row.shared_attrs or row.attributes + if (attributes := attr_cache.get(source)) is not None: + return attributes + if not source or source == EMPTY_JSON_OBJECT: + return {} + try: + attr_cache[source] = attributes = json.loads(source) + except ValueError: + _LOGGER.exception("Error converting row to state attributes: %s", source) + attr_cache[source] = attributes = {} + return attributes + + +def row_to_compressed_state( + row: Row, + attr_cache: dict[str, dict[str, Any]], + start_time: datetime | None = None, +) -> dict[str, Any]: + """Convert a database row to a compressed state.""" + if start_time: + last_changed = last_updated = start_time.timestamp() + else: + row_changed_changed: datetime = row.last_changed + if ( + not (row_last_updated := row.last_updated) + or row_last_updated == row_changed_changed + ): + last_changed = last_updated = process_datetime_to_timestamp( + row_changed_changed + ) + else: + last_changed = process_datetime_to_timestamp(row_changed_changed) + last_updated = process_datetime_to_timestamp(row_last_updated) + return { + COMPRESSED_STATE_STATE: row.state, + COMPRESSED_STATE_ATTRIBUTES: decode_attributes_from_row(row, attr_cache), + COMPRESSED_STATE_LAST_CHANGED: last_changed, + COMPRESSED_STATE_LAST_UPDATED: last_updated, + } diff --git a/homeassistant/components/websocket_api/const.py b/homeassistant/components/websocket_api/const.py index 6c5615ad253..107cf6d0270 100644 --- a/homeassistant/components/websocket_api/const.py +++ b/homeassistant/components/websocket_api/const.py @@ -56,3 +56,9 @@ DATA_CONNECTIONS: Final = f"{DOMAIN}.connections" JSON_DUMP: Final = partial( json.dumps, cls=JSONEncoder, allow_nan=False, separators=(",", ":") ) + +COMPRESSED_STATE_STATE = "s" +COMPRESSED_STATE_ATTRIBUTES = "a" +COMPRESSED_STATE_CONTEXT = "c" +COMPRESSED_STATE_LAST_CHANGED = "lc" +COMPRESSED_STATE_LAST_UPDATED = "lu" diff --git a/homeassistant/components/websocket_api/messages.py b/homeassistant/components/websocket_api/messages.py index eac40c9510b..1c09bc1d567 100644 --- a/homeassistant/components/websocket_api/messages.py +++ b/homeassistant/components/websocket_api/messages.py @@ -16,6 +16,13 @@ from homeassistant.util.json import ( from homeassistant.util.yaml.loader import JSON_TYPE from . import const +from .const import ( + COMPRESSED_STATE_ATTRIBUTES, + COMPRESSED_STATE_CONTEXT, + COMPRESSED_STATE_LAST_CHANGED, + COMPRESSED_STATE_LAST_UPDATED, + COMPRESSED_STATE_STATE, +) _LOGGER: Final = logging.getLogger(__name__) @@ -31,12 +38,6 @@ BASE_COMMAND_MESSAGE_SCHEMA: Final = vol.Schema({vol.Required("id"): cv.positive IDEN_TEMPLATE: Final = "__IDEN__" IDEN_JSON_TEMPLATE: Final = '"__IDEN__"' -COMPRESSED_STATE_STATE = "s" -COMPRESSED_STATE_ATTRIBUTES = "a" -COMPRESSED_STATE_CONTEXT = "c" -COMPRESSED_STATE_LAST_CHANGED = "lc" -COMPRESSED_STATE_LAST_UPDATED = "lu" - STATE_DIFF_ADDITIONS = "+" STATE_DIFF_REMOVALS = "-" diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 143b0c55fab..1dc18e5cc73 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -1070,3 +1070,409 @@ async def test_list_statistic_ids( response = await client.receive_json() assert response["success"] assert response["result"] == [] + + +async def test_history_during_period(hass, hass_ws_client, recorder_mock): + """Test history_during_period.""" + now = dt_util.utcnow() + + await async_setup_component(hass, "history", {}) + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "on", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "changed"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "again"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "on", attributes={"any": "attr"}) + await async_wait_recording_done(hass) + + do_adhoc_statistics(hass, start=now) + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "end_time": now.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == {} + + await client.send_json( + { + "id": 2, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": True, + "minimal_response": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + + sensor_test_history = response["result"]["sensor.test"] + assert len(sensor_test_history) == 3 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert "a" not in sensor_test_history[1] + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + + assert sensor_test_history[2]["s"] == "on" + assert sensor_test_history[2]["a"] == {} + + await client.send_json( + { + "id": 3, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": False, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + sensor_test_history = response["result"]["sensor.test"] + + assert len(sensor_test_history) == 5 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {"any": "attr"} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + assert sensor_test_history[1]["a"] == {"any": "attr"} + + assert sensor_test_history[4]["s"] == "on" + assert sensor_test_history[4]["a"] == {"any": "attr"} + + await client.send_json( + { + "id": 4, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": True, + "significant_changes_only": True, + "no_attributes": False, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 4 + sensor_test_history = response["result"]["sensor.test"] + + assert len(sensor_test_history) == 3 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {"any": "attr"} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + assert sensor_test_history[1]["a"] == {"any": "attr"} + + assert sensor_test_history[2]["s"] == "on" + assert sensor_test_history[2]["a"] == {"any": "attr"} + + +async def test_history_during_period_impossible_conditions( + hass, hass_ws_client, recorder_mock +): + """Test history_during_period returns when condition cannot be true.""" + now = dt_util.utcnow() + + await async_setup_component(hass, "history", {}) + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "on", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "changed"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "off", attributes={"any": "again"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.test", "on", attributes={"any": "attr"}) + await async_wait_recording_done(hass) + + do_adhoc_statistics(hass, start=now) + await async_wait_recording_done(hass) + + after = dt_util.utcnow() + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": after.isoformat(), + "end_time": after.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": False, + "significant_changes_only": False, + "no_attributes": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + assert response["result"] == {} + + future = dt_util.utcnow() + timedelta(hours=10) + + await client.send_json( + { + "id": 2, + "type": "history/history_during_period", + "start_time": future.isoformat(), + "entity_ids": ["sensor.test"], + "include_start_time_state": True, + "significant_changes_only": True, + "no_attributes": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + assert response["result"] == {} + + +@pytest.mark.parametrize( + "time_zone", ["UTC", "Europe/Berlin", "America/Chicago", "US/Hawaii"] +) +async def test_history_during_period_significant_domain( + time_zone, hass, hass_ws_client, recorder_mock +): + """Test history_during_period with climate domain.""" + hass.config.set_time_zone(time_zone) + now = dt_util.utcnow() + + await async_setup_component(hass, "history", {}) + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + hass.states.async_set("climate.test", "on", attributes={"temperature": "1"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("climate.test", "off", attributes={"temperature": "2"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("climate.test", "off", attributes={"temperature": "3"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("climate.test", "off", attributes={"temperature": "4"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("climate.test", "on", attributes={"temperature": "5"}) + await async_wait_recording_done(hass) + + do_adhoc_statistics(hass, start=now) + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "end_time": now.isoformat(), + "entity_ids": ["climate.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == {} + + await client.send_json( + { + "id": 2, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["climate.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": True, + "minimal_response": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + + sensor_test_history = response["result"]["climate.test"] + assert len(sensor_test_history) == 5 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert "a" in sensor_test_history[1] + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + + assert sensor_test_history[4]["s"] == "on" + assert sensor_test_history[4]["a"] == {} + + await client.send_json( + { + "id": 3, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["climate.test"], + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": False, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + sensor_test_history = response["result"]["climate.test"] + + assert len(sensor_test_history) == 5 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {"temperature": "1"} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + assert sensor_test_history[1]["a"] == {"temperature": "2"} + + assert sensor_test_history[4]["s"] == "on" + assert sensor_test_history[4]["a"] == {"temperature": "5"} + + await client.send_json( + { + "id": 4, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "entity_ids": ["climate.test"], + "include_start_time_state": True, + "significant_changes_only": True, + "no_attributes": False, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 4 + sensor_test_history = response["result"]["climate.test"] + + assert len(sensor_test_history) == 5 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {"temperature": "1"} + assert isinstance(sensor_test_history[0]["lu"], float) + assert isinstance(sensor_test_history[0]["lc"], float) + + assert sensor_test_history[1]["s"] == "off" + assert isinstance(sensor_test_history[1]["lc"], float) + assert sensor_test_history[1]["a"] == {"temperature": "2"} + + assert sensor_test_history[2]["s"] == "off" + assert sensor_test_history[2]["a"] == {"temperature": "3"} + + assert sensor_test_history[3]["s"] == "off" + assert sensor_test_history[3]["a"] == {"temperature": "4"} + + assert sensor_test_history[4]["s"] == "on" + assert sensor_test_history[4]["a"] == {"temperature": "5"} + + # Test we impute the state time state + later = dt_util.utcnow() + await client.send_json( + { + "id": 5, + "type": "history/history_during_period", + "start_time": later.isoformat(), + "entity_ids": ["climate.test"], + "include_start_time_state": True, + "significant_changes_only": True, + "no_attributes": False, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 5 + sensor_test_history = response["result"]["climate.test"] + + assert len(sensor_test_history) == 1 + + assert sensor_test_history[0]["s"] == "on" + assert sensor_test_history[0]["a"] == {"temperature": "5"} + assert sensor_test_history[0]["lu"] == later.timestamp() + assert sensor_test_history[0]["lc"] == later.timestamp() + + +async def test_history_during_period_bad_start_time( + hass, hass_ws_client, recorder_mock +): + """Test history_during_period bad state time.""" + await async_setup_component( + hass, + "history", + {"history": {}}, + ) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": "cats", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_start_time" + + +async def test_history_during_period_bad_end_time(hass, hass_ws_client, recorder_mock): + """Test history_during_period bad end time.""" + now = dt_util.utcnow() + + await async_setup_component( + hass, + "history", + {"history": {}}, + ) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "end_time": "dogs", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_end_time" diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index 20b60c3c96d..a98712ef282 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -14,6 +14,7 @@ from homeassistant.components import recorder from homeassistant.components.recorder import history from homeassistant.components.recorder.models import ( Events, + LazyState, RecorderRuns, StateAttributes, States, @@ -40,9 +41,19 @@ async def _async_get_states( def _get_states_with_session(): with session_scope(hass=hass) as session: - return history._get_states_with_session( - hass, session, utc_point_in_time, entity_ids, run, None, no_attributes - ) + attr_cache = {} + return [ + LazyState(row, attr_cache) + for row in history._get_rows_with_session( + hass, + session, + utc_point_in_time, + entity_ids, + run, + None, + no_attributes, + ) + ] return await recorder.get_instance(hass).async_add_executor_job( _get_states_with_session diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index a68d137eb0f..874d84ef2ad 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -247,7 +247,7 @@ async def test_lazy_state_handles_include_json(caplog): entity_id="sensor.invalid", shared_attrs="{INVALID_JSON}", ) - assert LazyState(row).attributes == {} + assert LazyState(row, {}).attributes == {} assert "Error converting row to state attributes" in caplog.text @@ -258,7 +258,7 @@ async def test_lazy_state_prefers_shared_attrs_over_attrs(caplog): shared_attrs='{"shared":true}', attributes='{"shared":false}', ) - assert LazyState(row).attributes == {"shared": True} + assert LazyState(row, {}).attributes == {"shared": True} async def test_lazy_state_handles_different_last_updated_and_last_changed(caplog): @@ -271,7 +271,7 @@ async def test_lazy_state_handles_different_last_updated_and_last_changed(caplog last_updated=now, last_changed=now - timedelta(seconds=60), ) - lstate = LazyState(row) + lstate = LazyState(row, {}) assert lstate.as_dict() == { "attributes": {"shared": True}, "entity_id": "sensor.valid", @@ -300,7 +300,7 @@ async def test_lazy_state_handles_same_last_updated_and_last_changed(caplog): last_updated=now, last_changed=now, ) - lstate = LazyState(row) + lstate = LazyState(row, {}) assert lstate.as_dict() == { "attributes": {"shared": True}, "entity_id": "sensor.valid", From 69a8232b4560e028525928c00b1d650e9ff3b7d7 Mon Sep 17 00:00:00 2001 From: Nic Jansma Date: Wed, 11 May 2022 20:22:54 -0400 Subject: [PATCH 362/930] Add missing Coinbase RATEs (#65101) --- homeassistant/components/coinbase/const.py | 95 +++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/coinbase/const.py b/homeassistant/components/coinbase/const.py index 85535613851..08773745f09 100644 --- a/homeassistant/components/coinbase/const.py +++ b/homeassistant/components/coinbase/const.py @@ -278,21 +278,31 @@ WALLETS = { RATES = { "1INCH": "1INCH", "AAVE": "AAVE", + "ACH": "ACH", "ADA": "ADA", "AED": "AED", "AFN": "AFN", + "AGLD": "AGLD", + "ALCX": "ALCX", "ALGO": "ALGO", "ALL": "ALL", "AMD": "AMD", + "AMP": "AMP", "ANG": "ANG", "ANKR": "ANKR", "AOA": "AOA", + "API3": "API3", + "ARPA": "ARPA", "ARS": "ARS", + "ASM": "ASM", "ATOM": "ATOM", "AUCTION": "AUCTION", "AUD": "AUD", + "AVAX": "AVAX", "AWG": "AWG", + "AXS": "AXS", "AZN": "AZN", + "BADGER": "BADGER", "BAL": "BAL", "BAM": "BAM", "BAND": "BAND", @@ -302,16 +312,20 @@ RATES = { "BDT": "BDT", "BGN": "BGN", "BHD": "BHD", + "BICO": "BICO", "BIF": "BIF", + "BLZ": "BLZ", "BMD": "BMD", "BND": "BND", "BNT": "BNT", "BOB": "BOB", + "BOND": "BOND", "BRL": "BRL", "BSD": "BSD", "BSV": "BSV", "BTC": "BTC", "BTN": "BTN", + "BTRST": "BTRST", "BWP": "BWP", "BYN": "BYN", "BYR": "BYR", @@ -320,6 +334,7 @@ RATES = { "CDF": "CDF", "CGLD": "CGLD", "CHF": "CHF", + "CHZ": "CHZ", "CLF": "CLF", "CLP": "CLP", "CLV": "CLV", @@ -327,21 +342,32 @@ RATES = { "CNY": "CNY", "COMP": "COMP", "COP": "COP", + "COTI": "COTI", + "COVAL": "COVAL", "CRC": "CRC", + "CRO": "CRO", "CRV": "CRV", + "CTSI": "CTSI", + "CTX": "CTX", "CUC": "CUC", "CVC": "CVC", "CVE": "CVE", "CZK": "CZK", "DAI": "DAI", "DASH": "DASH", + "DDX": "DDX", + "DESO": "DESO", + "DIA": "DIA", "DJF": "DJF", "DKK": "DKK", "DNT": "DNT", + "DOGE": "DOGE", "DOP": "DOP", + "DOT": "DOT", "DZD": "DZD", "EGP": "EGP", "ENJ": "ENJ", + "ENS": "ENS", "EOS": "EOS", "ERN": "ERN", "ETB": "ETB", @@ -349,50 +375,69 @@ RATES = { "ETH": "ETH", "ETH2": "ETH2", "EUR": "EUR", + "FARM": "FARM", "FET": "FET", "FIL": "FIL", "FJD": "FJD", "FKP": "FKP", "FORTH": "FORTH", + "FOX": "FOX", + "FX": "FX", + "GALA": "GALA", "GBP": "GBP", "GBX": "GBX", "GEL": "GEL", + "GFI": "GFI", "GGP": "GGP", "GHS": "GHS", "GIP": "GIP", "GMD": "GMD", "GNF": "GNF", + "GODS": "GODS", "GRT": "GRT", + "GTC": "GTC", "GTQ": "GTQ", "GYD": "GYD", + "GYEN": "GYEN", "HKD": "HKD", "HNL": "HNL", "HRK": "HRK", "HTG": "HTG", "HUF": "HUF", + "ICP": "ICP", + "IDEX": "IDEX", "IDR": "IDR", "ILS": "ILS", "IMP": "IMP", + "IMX": "IMX", "INR": "INR", + "INV": "INV", + "IOTX": "IOTX", "IQD": "IQD", "ISK": "ISK", + "JASMY": "JASMY", "JEP": "JEP", "JMD": "JMD", "JOD": "JOD", "JPY": "JPY", + "KEEP": "KEEP", "KES": "KES", "KGS": "KGS", "KHR": "KHR", "KMF": "KMF", "KNC": "KNC", + "KRL": "KRL", "KRW": "KRW", "KWD": "KWD", "KYD": "KYD", "KZT": "KZT", "LAK": "LAK", "LBP": "LBP", + "LCX": "LCX", "LINK": "LINK", "LKR": "LKR", + "LPT": "LPT", + "LQTY": "LQTY", "LRC": "LRC", "LRD": "LRD", "LSL": "LSL", @@ -400,23 +445,31 @@ RATES = { "LYD": "LYD", "MAD": "MAD", "MANA": "MANA", + "MASK": "MASK", "MATIC": "MATIC", + "MCO2": "MCO2", "MDL": "MDL", + "MDT": "MDT", "MGA": "MGA", + "MIR": "MIR", "MKD": "MKD", "MKR": "MKR", + "MLN": "MLN", "MMK": "MMK", "MNT": "MNT", "MOP": "MOP", + "MPL": "MPL", "MRO": "MRO", "MTL": "MTL", "MUR": "MUR", + "MUSD": "MUSD", "MVR": "MVR", "MWK": "MWK", "MXN": "MXN", "MYR": "MYR", "MZN": "MZN", "NAD": "NAD", + "NCT": "NCT", "NGN": "NGN", "NIO": "NIO", "NKN": "NKN", @@ -428,19 +481,37 @@ RATES = { "OGN": "OGN", "OMG": "OMG", "OMR": "OMR", + "ORN": "ORN", "OXT": "OXT", "PAB": "PAB", + "PAX": "PAX", "PEN": "PEN", + "PERP": "PERP", "PGK": "PGK", "PHP": "PHP", "PKR": "PKR", + "PLA": "PLA", "PLN": "PLN", + "PLU": "PLU", + "POLS": "POLS", "POLY": "POLY", + "POWR": "POWR", + "PRO": "PRO", "PYG": "PYG", "QAR": "QAR", - "RLY": "RLY", + "QNT": "QNT", + "QUICK": "QUICK", + "RAD": "RAD", + "RAI": "RAI", + "RARI": "RARI", + "RBN": "RBN", "REN": "REN", "REP": "REP", + "REPV2": "REPV2", + "REQ": "REQ", + "RGT": "RGT", + "RLC": "RLC", + "RLY": "RLY", "RON": "RON", "RSD": "RSD", "RUB": "RUB", @@ -452,22 +523,34 @@ RATES = { "SGD": "SGD", "SHIB": "SHIB", "SHP": "SHP", + "SHPING": "SHPING", + "SKK": "SKK", "SKL": "SKL", "SLL": "SLL", "SNX": "SNX", + "SOL": "SOL", "SOS": "SOS", + "SPELL": "SPELL", "SRD": "SRD", "SSP": "SSP", "STD": "STD", "STORJ": "STORJ", + "STX": "STX", + "SUKU": "SUKU", + "SUPER": "SUPER", "SUSHI": "SUSHI", "SVC": "SVC", "SZL": "SZL", "THB": "THB", "TJS": "TJS", + "TMM": "TMM", "TMT": "TMT", "TND": "TND", "TOP": "TOP", + "TRAC": "TRAC", + "TRB": "TRB", + "TRIBE": "TRIBE", + "TRU": "TRU", "TRY": "TRY", "TTD": "TTD", "TWD": "TWD", @@ -475,15 +558,21 @@ RATES = { "UAH": "UAH", "UGX": "UGX", "UMA": "UMA", + "UNFI": "UNFI", "UNI": "UNI", "USD": "USD", "USDC": "USDC", + "USDT": "USDT", + "UST": "UST", "UYU": "UYU", "UZS": "UZS", "VES": "VES", + "VGX": "VGX", "VND": "VND", "VUV": "VUV", "WBTC": "WBTC", + "WCFG": "WCFG", + "WLUNA": "WLUNA", "WST": "WST", "XAF": "XAF", "XAG": "XAG", @@ -495,11 +584,15 @@ RATES = { "XPD": "XPD", "XPF": "XPF", "XPT": "XPT", + "XRP": "XRP", "XTZ": "XTZ", + "XYO": "XYO", "YER": "YER", "YFI": "YFI", + "YFII": "YFII", "ZAR": "ZAR", "ZEC": "ZEC", + "ZEN": "ZEN", "ZMW": "ZMW", "ZRX": "ZRX", "ZWL": "ZWL", From 08851d8366e0860619fc605637267ee4a99a652d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 May 2022 02:32:02 +0200 Subject: [PATCH 363/930] Remove YAML configuration from International Space Station (ISS) (#71693) --- homeassistant/components/iss/binary_sensor.py | 45 ++----------------- homeassistant/components/iss/config_flow.py | 9 ---- tests/components/iss/test_config_flow.py | 20 +-------- 3 files changed, 5 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/iss/binary_sensor.py b/homeassistant/components/iss/binary_sensor.py index 12a8a7514b2..e2034fb48f9 100644 --- a/homeassistant/components/iss/binary_sensor.py +++ b/homeassistant/components/iss/binary_sensor.py @@ -7,24 +7,14 @@ import logging import pyiss import requests from requests.exceptions import HTTPError -import voluptuous as vol -from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - ATTR_LATITUDE, - ATTR_LONGITUDE, - CONF_NAME, - CONF_SHOW_ON_MAP, -) +from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, CONF_SHOW_ON_MAP from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import Throttle -from .const import DOMAIN - _LOGGER = logging.getLogger(__name__) ATTR_ISS_NEXT_RISE = "next_rise" @@ -35,35 +25,6 @@ DEFAULT_DEVICE_CLASS = "visible" MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_SHOW_ON_MAP, default=False): cv.boolean, - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Import ISS configuration from yaml.""" - _LOGGER.warning( - "Configuration of the iss platform in YAML is deprecated and will be " - "removed in Home Assistant 2022.5; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" - ) - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=config, - ) - ) - async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/iss/config_flow.py b/homeassistant/components/iss/config_flow.py index dc80126bd14..b43949daadc 100644 --- a/homeassistant/components/iss/config_flow.py +++ b/homeassistant/components/iss/config_flow.py @@ -43,15 +43,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form(step_id="user") - async def async_step_import(self, conf: dict) -> FlowResult: - """Import a configuration from configuration.yaml.""" - return await self.async_step_user( - user_input={ - CONF_NAME: conf[CONF_NAME], - CONF_SHOW_ON_MAP: conf[CONF_SHOW_ON_MAP], - } - ) - class OptionsFlowHandler(config_entries.OptionsFlow): """Config flow options handler for iss.""" diff --git a/tests/components/iss/test_config_flow.py b/tests/components/iss/test_config_flow.py index 09f7f391b89..3260b76432f 100644 --- a/tests/components/iss/test_config_flow.py +++ b/tests/components/iss/test_config_flow.py @@ -2,31 +2,15 @@ from unittest.mock import patch from homeassistant import data_entry_flow -from homeassistant.components.iss.binary_sensor import DEFAULT_NAME from homeassistant.components.iss.const import DOMAIN from homeassistant.config import async_process_ha_core_config -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER -from homeassistant.const import CONF_NAME, CONF_SHOW_ON_MAP +from homeassistant.config_entries import SOURCE_USER +from homeassistant.const import CONF_SHOW_ON_MAP from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry -async def test_import(hass: HomeAssistant): - """Test entry will be imported.""" - - imported_config = {CONF_NAME: DEFAULT_NAME, CONF_SHOW_ON_MAP: False} - - with patch("homeassistant.components.iss.async_setup_entry", return_value=True): - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=imported_config - ) - assert result.get("type") == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result.get("result").title == DEFAULT_NAME - assert result.get("result").options == {CONF_SHOW_ON_MAP: False} - - async def test_create_entry(hass: HomeAssistant): """Test we can finish a config flow.""" From 19168227ebac3861ac38ecffd93379ff8bf006c6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 21:30:36 -0500 Subject: [PATCH 364/930] Fix sqlalchemy warning about logbook query being converted from subquery (#71710) --- homeassistant/components/logbook/queries.py | 34 ++++++++++----------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 8c13719a1cf..08f6d759d25 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -6,7 +6,7 @@ from datetime import datetime as dt from typing import Any import sqlalchemy -from sqlalchemy import lambda_stmt, select +from sqlalchemy import lambda_stmt, select, union_all from sqlalchemy.orm import aliased from sqlalchemy.sql.expression import literal from sqlalchemy.sql.lambdas import StatementLambdaElement @@ -118,15 +118,15 @@ def _select_entities_context_ids_sub_query( entity_ids: list[str], ) -> Select: """Generate a subquery to find context ids for multiple entities.""" - return ( - _select_events_context_id_subquery(start_day, end_day, event_types) - .where(_apply_event_entity_id_matchers(entity_ids)) - .union_all( + return select( + union_all( + _select_events_context_id_subquery(start_day, end_day, event_types).where( + _apply_event_entity_id_matchers(entity_ids) + ), select(States.context_id) .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id.in_(entity_ids)) - ) - .subquery() + .where(States.entity_id.in_(entity_ids)), + ).c.context_id ) @@ -183,18 +183,16 @@ def _select_entity_context_ids_sub_query( entity_id_like: str, ) -> Select: """Generate a subquery to find context ids for a single entity.""" - return ( - _select_events_context_id_subquery(start_day, end_day, event_types) - .where( - Events.event_data.like(entity_id_like) - | EventData.shared_data.like(entity_id_like) - ) - .union_all( + return select( + union_all( + _select_events_context_id_subquery(start_day, end_day, event_types).where( + Events.event_data.like(entity_id_like) + | EventData.shared_data.like(entity_id_like) + ), select(States.context_id) .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id == entity_id) - ) - .subquery() + .where(States.entity_id == entity_id), + ).c.context_id ) From a03a4b0d131ddb6842b7ca09b6bd436c2fd7b983 Mon Sep 17 00:00:00 2001 From: Glenn Waters Date: Wed, 11 May 2022 22:32:19 -0400 Subject: [PATCH 365/930] ElkM1 integration updates for new version of base library (#71508) Co-authored-by: J. Nick Koston --- .../components/elkm1/alarm_control_panel.py | 28 ++++---- homeassistant/components/elkm1/climate.py | 67 +++++++++--------- homeassistant/components/elkm1/manifest.json | 2 +- homeassistant/components/elkm1/sensor.py | 69 ++++++++----------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 82 insertions(+), 88 deletions(-) diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index 49aa6f68a09..6b6a5b44d55 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -205,18 +205,18 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity): def _element_changed(self, element: Element, changeset: dict[str, Any]) -> None: elk_state_to_hass_state = { - ArmedStatus.DISARMED.value: STATE_ALARM_DISARMED, - ArmedStatus.ARMED_AWAY.value: STATE_ALARM_ARMED_AWAY, - ArmedStatus.ARMED_STAY.value: STATE_ALARM_ARMED_HOME, - ArmedStatus.ARMED_STAY_INSTANT.value: STATE_ALARM_ARMED_HOME, - ArmedStatus.ARMED_TO_NIGHT.value: STATE_ALARM_ARMED_NIGHT, - ArmedStatus.ARMED_TO_NIGHT_INSTANT.value: STATE_ALARM_ARMED_NIGHT, - ArmedStatus.ARMED_TO_VACATION.value: STATE_ALARM_ARMED_AWAY, + ArmedStatus.DISARMED: STATE_ALARM_DISARMED, + ArmedStatus.ARMED_AWAY: STATE_ALARM_ARMED_AWAY, + ArmedStatus.ARMED_STAY: STATE_ALARM_ARMED_HOME, + ArmedStatus.ARMED_STAY_INSTANT: STATE_ALARM_ARMED_HOME, + ArmedStatus.ARMED_TO_NIGHT: STATE_ALARM_ARMED_NIGHT, + ArmedStatus.ARMED_TO_NIGHT_INSTANT: STATE_ALARM_ARMED_NIGHT, + ArmedStatus.ARMED_TO_VACATION: STATE_ALARM_ARMED_AWAY, } if self._element.alarm_state is None: self._state = None - elif self._element.alarm_state >= AlarmState.FIRE_ALARM.value: + elif self._element.in_alarm_state(): # Area is in alarm state self._state = STATE_ALARM_TRIGGERED elif self._entry_exit_timer_is_running(): @@ -239,32 +239,32 @@ class ElkArea(ElkAttachedEntity, AlarmControlPanelEntity, RestoreEntity): async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_STAY.value, int(code)) + self._element.arm(ArmLevel.ARMED_STAY, int(code)) async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_AWAY.value, int(code)) + self._element.arm(ArmLevel.ARMED_AWAY, int(code)) async def async_alarm_arm_night(self, code: str | None = None) -> None: """Send arm night command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code)) + self._element.arm(ArmLevel.ARMED_NIGHT, int(code)) async def async_alarm_arm_home_instant(self, code: str | None = None) -> None: """Send arm stay instant command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_STAY_INSTANT.value, int(code)) + self._element.arm(ArmLevel.ARMED_STAY_INSTANT, int(code)) async def async_alarm_arm_night_instant(self, code: str | None = None) -> None: """Send arm night instant command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT.value, int(code)) + self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT, int(code)) async def async_alarm_arm_vacation(self, code: str | None = None) -> None: """Send arm vacation command.""" if code is not None: - self._element.arm(ArmLevel.ARMED_VACATION.value, int(code)) + self._element.arm(ArmLevel.ARMED_VACATION, int(code)) async def async_display_message( self, clear: int, beep: bool, timeout: int, line1: str, line2: str diff --git a/homeassistant/components/elkm1/climate.py b/homeassistant/components/elkm1/climate.py index 7b6de739d4f..9f6dc359f6f 100644 --- a/homeassistant/components/elkm1/climate.py +++ b/homeassistant/components/elkm1/climate.py @@ -33,26 +33,26 @@ SUPPORT_HVAC = [ HVACMode.FAN_ONLY, ] HASS_TO_ELK_HVAC_MODES = { - HVACMode.OFF: (ThermostatMode.OFF.value, ThermostatFan.AUTO.value), - HVACMode.HEAT: (ThermostatMode.HEAT.value, None), - HVACMode.COOL: (ThermostatMode.COOL.value, None), - HVACMode.HEAT_COOL: (ThermostatMode.AUTO.value, None), - HVACMode.FAN_ONLY: (ThermostatMode.OFF.value, ThermostatFan.ON.value), + HVACMode.OFF: (ThermostatMode.OFF, ThermostatFan.AUTO), + HVACMode.HEAT: (ThermostatMode.HEAT, None), + HVACMode.COOL: (ThermostatMode.COOL, None), + HVACMode.HEAT_COOL: (ThermostatMode.AUTO, None), + HVACMode.FAN_ONLY: (ThermostatMode.OFF, ThermostatFan.ON), } ELK_TO_HASS_HVAC_MODES = { - ThermostatMode.OFF.value: HVACMode.OFF, - ThermostatMode.COOL.value: HVACMode.COOL, - ThermostatMode.HEAT.value: HVACMode.HEAT, - ThermostatMode.EMERGENCY_HEAT.value: HVACMode.HEAT, - ThermostatMode.AUTO.value: HVACMode.HEAT_COOL, + ThermostatMode.OFF: HVACMode.OFF, + ThermostatMode.COOL: HVACMode.COOL, + ThermostatMode.HEAT: HVACMode.HEAT, + ThermostatMode.EMERGENCY_HEAT: HVACMode.HEAT, + ThermostatMode.AUTO: HVACMode.HEAT_COOL, } HASS_TO_ELK_FAN_MODES = { - FAN_AUTO: (None, ThermostatFan.AUTO.value), - FAN_ON: (None, ThermostatFan.ON.value), + FAN_AUTO: (None, ThermostatFan.AUTO), + FAN_ON: (None, ThermostatFan.ON), } ELK_TO_HASS_FAN_MODES = { - ThermostatFan.AUTO.value: FAN_AUTO, - ThermostatFan.ON.value: FAN_ON, + ThermostatFan.AUTO: FAN_AUTO, + ThermostatFan.ON: FAN_ON, } @@ -84,7 +84,7 @@ class ElkThermostat(ElkEntity, ClimateEntity): def __init__(self, element: Element, elk: Elk, elk_data: dict[str, Any]) -> None: """Initialize climate entity.""" super().__init__(element, elk, elk_data) - self._state: str = HVACMode.OFF + self._state: str | None = None @property def temperature_unit(self) -> str: @@ -100,11 +100,11 @@ class ElkThermostat(ElkEntity, ClimateEntity): def target_temperature(self) -> float | None: """Return the temperature we are trying to reach.""" if self._element.mode in ( - ThermostatMode.HEAT.value, - ThermostatMode.EMERGENCY_HEAT.value, + ThermostatMode.HEAT, + ThermostatMode.EMERGENCY_HEAT, ): return self._element.heat_setpoint - if self._element.mode == ThermostatMode.COOL.value: + if self._element.mode == ThermostatMode.COOL: return self._element.cool_setpoint return None @@ -129,7 +129,7 @@ class ElkThermostat(ElkEntity, ClimateEntity): return self._element.humidity @property - def hvac_mode(self) -> str: + def hvac_mode(self) -> str | None: """Return current operation ie. heat, cool, idle.""" return self._state @@ -146,7 +146,7 @@ class ElkThermostat(ElkEntity, ClimateEntity): @property def is_aux_heat(self) -> bool: """Return if aux heater is on.""" - return self._element.mode == ThermostatMode.EMERGENCY_HEAT.value + return self._element.mode == ThermostatMode.EMERGENCY_HEAT @property def min_temp(self) -> float: @@ -159,15 +159,17 @@ class ElkThermostat(ElkEntity, ClimateEntity): return 99 @property - def fan_mode(self) -> str: + def fan_mode(self) -> str | None: """Return the fan setting.""" + if self._element.fan is None: + return None return ELK_TO_HASS_FAN_MODES[self._element.fan] - def _elk_set(self, mode: int | None, fan: int | None) -> None: + def _elk_set(self, mode: ThermostatMode | None, fan: ThermostatFan | None) -> None: if mode is not None: - self._element.set(ThermostatSetting.MODE.value, mode) + self._element.set(ThermostatSetting.MODE, mode) if fan is not None: - self._element.set(ThermostatSetting.FAN.value, fan) + self._element.set(ThermostatSetting.FAN, fan) async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set thermostat operation mode.""" @@ -176,11 +178,11 @@ class ElkThermostat(ElkEntity, ClimateEntity): async def async_turn_aux_heat_on(self) -> None: """Turn auxiliary heater on.""" - self._elk_set(ThermostatMode.EMERGENCY_HEAT.value, None) + self._elk_set(ThermostatMode.EMERGENCY_HEAT, None) async def async_turn_aux_heat_off(self) -> None: """Turn auxiliary heater off.""" - self._elk_set(ThermostatMode.HEAT.value, None) + self._elk_set(ThermostatMode.HEAT, None) @property def fan_modes(self) -> list[str]: @@ -197,11 +199,14 @@ class ElkThermostat(ElkEntity, ClimateEntity): low_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) if low_temp is not None: - self._element.set(ThermostatSetting.HEAT_SETPOINT.value, round(low_temp)) + self._element.set(ThermostatSetting.HEAT_SETPOINT, round(low_temp)) if high_temp is not None: - self._element.set(ThermostatSetting.COOL_SETPOINT.value, round(high_temp)) + self._element.set(ThermostatSetting.COOL_SETPOINT, round(high_temp)) def _element_changed(self, element: Element, changeset: Any) -> None: - self._state = ELK_TO_HASS_HVAC_MODES[self._element.mode] - if self._state == HVACMode.OFF and self._element.fan == ThermostatFan.ON.value: - self._state = HVACMode.FAN_ONLY + if self._element.mode is None: + self._state = None + else: + self._state = ELK_TO_HASS_HVAC_MODES[self._element.mode] + if self._state == HVACMode.OFF and self._element.fan == ThermostatFan.ON: + self._state = HVACMode.FAN_ONLY diff --git a/homeassistant/components/elkm1/manifest.json b/homeassistant/components/elkm1/manifest.json index ad5634c1e9f..13f8ba8401f 100644 --- a/homeassistant/components/elkm1/manifest.json +++ b/homeassistant/components/elkm1/manifest.json @@ -2,7 +2,7 @@ "domain": "elkm1", "name": "Elk-M1 Control", "documentation": "https://www.home-assistant.io/integrations/elkm1", - "requirements": ["elkm1-lib==1.3.5"], + "requirements": ["elkm1-lib==2.0.0"], "dhcp": [{ "registered_devices": true }, { "macaddress": "00409D*" }], "codeowners": ["@gwww", "@bdraco"], "dependencies": ["network"], diff --git a/homeassistant/components/elkm1/sensor.py b/homeassistant/components/elkm1/sensor.py index e41d09e2e76..57f989d5cb5 100644 --- a/homeassistant/components/elkm1/sensor.py +++ b/homeassistant/components/elkm1/sensor.py @@ -3,12 +3,7 @@ from __future__ import annotations from typing import Any -from elkm1_lib.const import ( - SettingFormat, - ZoneLogicalStatus, - ZonePhysicalStatus, - ZoneType, -) +from elkm1_lib.const import SettingFormat, ZoneType from elkm1_lib.counters import Counter from elkm1_lib.elements import Element from elkm1_lib.elk import Elk @@ -237,25 +232,25 @@ class ElkZone(ElkSensor): def icon(self) -> str: """Icon to use in the frontend.""" zone_icons = { - ZoneType.FIRE_ALARM.value: "fire", - ZoneType.FIRE_VERIFIED.value: "fire", - ZoneType.FIRE_SUPERVISORY.value: "fire", - ZoneType.KEYFOB.value: "key", - ZoneType.NON_ALARM.value: "alarm-off", - ZoneType.MEDICAL_ALARM.value: "medical-bag", - ZoneType.POLICE_ALARM.value: "alarm-light", - ZoneType.POLICE_NO_INDICATION.value: "alarm-light", - ZoneType.KEY_MOMENTARY_ARM_DISARM.value: "power", - ZoneType.KEY_MOMENTARY_ARM_AWAY.value: "power", - ZoneType.KEY_MOMENTARY_ARM_STAY.value: "power", - ZoneType.KEY_MOMENTARY_DISARM.value: "power", - ZoneType.KEY_ON_OFF.value: "toggle-switch", - ZoneType.MUTE_AUDIBLES.value: "volume-mute", - ZoneType.POWER_SUPERVISORY.value: "power-plug", - ZoneType.TEMPERATURE.value: "thermometer-lines", - ZoneType.ANALOG_ZONE.value: "speedometer", - ZoneType.PHONE_KEY.value: "phone-classic", - ZoneType.INTERCOM_KEY.value: "deskphone", + ZoneType.FIRE_ALARM: "fire", + ZoneType.FIRE_VERIFIED: "fire", + ZoneType.FIRE_SUPERVISORY: "fire", + ZoneType.KEYFOB: "key", + ZoneType.NON_ALARM: "alarm-off", + ZoneType.MEDICAL_ALARM: "medical-bag", + ZoneType.POLICE_ALARM: "alarm-light", + ZoneType.POLICE_NO_INDICATION: "alarm-light", + ZoneType.KEY_MOMENTARY_ARM_DISARM: "power", + ZoneType.KEY_MOMENTARY_ARM_AWAY: "power", + ZoneType.KEY_MOMENTARY_ARM_STAY: "power", + ZoneType.KEY_MOMENTARY_DISARM: "power", + ZoneType.KEY_ON_OFF: "toggle-switch", + ZoneType.MUTE_AUDIBLES: "volume-mute", + ZoneType.POWER_SUPERVISORY: "power-plug", + ZoneType.TEMPERATURE: "thermometer-lines", + ZoneType.ANALOG_ZONE: "speedometer", + ZoneType.PHONE_KEY: "phone-classic", + ZoneType.INTERCOM_KEY: "deskphone", } return f"mdi:{zone_icons.get(self._element.definition, 'alarm-bell')}" @@ -263,13 +258,9 @@ class ElkZone(ElkSensor): def extra_state_attributes(self) -> dict[str, Any]: """Attributes of the sensor.""" attrs: dict[str, Any] = self.initial_attrs() - attrs["physical_status"] = ZonePhysicalStatus( - self._element.physical_status - ).name.lower() - attrs["logical_status"] = ZoneLogicalStatus( - self._element.logical_status - ).name.lower() - attrs["definition"] = ZoneType(self._element.definition).name.lower() + attrs["physical_status"] = self._element.physical_status.name.lower() + attrs["logical_status"] = self._element.logical_status.name.lower() + attrs["definition"] = self._element.definition.name.lower() attrs["area"] = self._element.area + 1 attrs["triggered_alarm"] = self._element.triggered_alarm return attrs @@ -277,27 +268,25 @@ class ElkZone(ElkSensor): @property def temperature_unit(self) -> str | None: """Return the temperature unit.""" - if self._element.definition == ZoneType.TEMPERATURE.value: + if self._element.definition == ZoneType.TEMPERATURE: return self._temperature_unit return None @property def native_unit_of_measurement(self) -> str | None: """Return the unit of measurement.""" - if self._element.definition == ZoneType.TEMPERATURE.value: + if self._element.definition == ZoneType.TEMPERATURE: return self._temperature_unit - if self._element.definition == ZoneType.ANALOG_ZONE.value: + if self._element.definition == ZoneType.ANALOG_ZONE: return ELECTRIC_POTENTIAL_VOLT return None def _element_changed(self, _: Element, changeset: Any) -> None: - if self._element.definition == ZoneType.TEMPERATURE.value: + if self._element.definition == ZoneType.TEMPERATURE: self._state = temperature_to_state( self._element.temperature, UNDEFINED_TEMPERATURE ) - elif self._element.definition == ZoneType.ANALOG_ZONE.value: + elif self._element.definition == ZoneType.ANALOG_ZONE: self._state = f"{self._element.voltage}" else: - self._state = pretty_const( - ZoneLogicalStatus(self._element.logical_status).name - ) + self._state = pretty_const(self._element.logical_status.name) diff --git a/requirements_all.txt b/requirements_all.txt index 4f8bf564915..c2fc83e847b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -584,7 +584,7 @@ elgato==3.0.0 eliqonline==1.2.2 # homeassistant.components.elkm1 -elkm1-lib==1.3.5 +elkm1-lib==2.0.0 # homeassistant.components.elmax elmax_api==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e274844449b..2fe34d6a33e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -418,7 +418,7 @@ dynalite_devices==0.1.46 elgato==3.0.0 # homeassistant.components.elkm1 -elkm1-lib==1.3.5 +elkm1-lib==2.0.0 # homeassistant.components.elmax elmax_api==0.0.2 From b9f7d1f54c091943348ef2bf7d0993b9e2280e94 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 11 May 2022 22:55:12 -0400 Subject: [PATCH 366/930] Fix zwave_js device automation bug (#71715) --- .../zwave_js/device_automation_helpers.py | 24 +++++++++++++++++++ .../components/zwave_js/device_condition.py | 6 ++--- .../components/zwave_js/device_trigger.py | 6 ++--- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/zwave_js/device_automation_helpers.py b/homeassistant/components/zwave_js/device_automation_helpers.py index 906efb2c4f9..f17ddccf03c 100644 --- a/homeassistant/components/zwave_js/device_automation_helpers.py +++ b/homeassistant/components/zwave_js/device_automation_helpers.py @@ -8,6 +8,12 @@ from zwave_js_server.const import ConfigurationValueType from zwave_js_server.model.node import Node from zwave_js_server.model.value import ConfigurationValue +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr + +from .const import DOMAIN + NODE_STATUSES = ["asleep", "awake", "dead", "alive"] CONF_SUBTYPE = "subtype" @@ -41,3 +47,21 @@ def generate_config_parameter_subtype(config_value: ConfigurationValue) -> str: parameter = f"{parameter}[{hex(config_value.property_key)}]" return f"{parameter} ({config_value.property_name})" + + +@callback +def async_bypass_dynamic_config_validation(hass: HomeAssistant, device_id: str) -> bool: + """Return whether device's config entries are not loaded.""" + dev_reg = dr.async_get(hass) + if (device := dev_reg.async_get(device_id)) is None: + raise ValueError(f"Device {device_id} not found") + entry = next( + ( + config_entry + for config_entry in hass.config_entries.async_entries(DOMAIN) + if config_entry.entry_id in device.config_entries + and config_entry.state == ConfigEntryState.LOADED + ), + None, + ) + return not entry diff --git a/homeassistant/components/zwave_js/device_condition.py b/homeassistant/components/zwave_js/device_condition.py index c70371d6f8a..549319d23f4 100644 --- a/homeassistant/components/zwave_js/device_condition.py +++ b/homeassistant/components/zwave_js/device_condition.py @@ -29,12 +29,12 @@ from .device_automation_helpers import ( CONF_SUBTYPE, CONF_VALUE_ID, NODE_STATUSES, + async_bypass_dynamic_config_validation, generate_config_parameter_subtype, get_config_parameter_value_schema, ) from .helpers import ( async_get_node_from_device_id, - async_is_device_config_entry_not_loaded, check_type_schema_map, get_zwave_value_from_config, remove_keys_with_empty_values, @@ -101,7 +101,7 @@ async def async_validate_condition_config( # We return early if the config entry for this device is not ready because we can't # validate the value without knowing the state of the device try: - device_config_entry_not_loaded = async_is_device_config_entry_not_loaded( + bypass_dynamic_config_validation = async_bypass_dynamic_config_validation( hass, config[CONF_DEVICE_ID] ) except ValueError as err: @@ -109,7 +109,7 @@ async def async_validate_condition_config( f"Device {config[CONF_DEVICE_ID]} not found" ) from err - if device_config_entry_not_loaded: + if bypass_dynamic_config_validation: return config if config[CONF_TYPE] == VALUE_TYPE: diff --git a/homeassistant/components/zwave_js/device_trigger.py b/homeassistant/components/zwave_js/device_trigger.py index 1e76f9e73ec..75a9647a5ca 100644 --- a/homeassistant/components/zwave_js/device_trigger.py +++ b/homeassistant/components/zwave_js/device_trigger.py @@ -53,12 +53,12 @@ from .const import ( from .device_automation_helpers import ( CONF_SUBTYPE, NODE_STATUSES, + async_bypass_dynamic_config_validation, generate_config_parameter_subtype, ) from .helpers import ( async_get_node_from_device_id, async_get_node_status_sensor_entity_id, - async_is_device_config_entry_not_loaded, check_type_schema_map, copy_available_params, get_value_state_schema, @@ -215,7 +215,7 @@ async def async_validate_trigger_config( # We return early if the config entry for this device is not ready because we can't # validate the value without knowing the state of the device try: - device_config_entry_not_loaded = async_is_device_config_entry_not_loaded( + bypass_dynamic_config_validation = async_bypass_dynamic_config_validation( hass, config[CONF_DEVICE_ID] ) except ValueError as err: @@ -223,7 +223,7 @@ async def async_validate_trigger_config( f"Device {config[CONF_DEVICE_ID]} not found" ) from err - if device_config_entry_not_loaded: + if bypass_dynamic_config_validation: return config trigger_type = config[CONF_TYPE] From 04af9698d3783c84ba8f6684badb40b96d63b13d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 22:28:06 -0500 Subject: [PATCH 367/930] Add logbook/get_events websocket endpoint (#71706) Co-authored-by: Paulus Schoutsen --- homeassistant/components/logbook/__init__.py | 94 ++++++++- tests/components/logbook/common.py | 7 +- tests/components/logbook/test_init.py | 193 ++++++++++++++++--- 3 files changed, 260 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 9cf3ad99e57..df8143d39fb 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -15,7 +15,7 @@ from sqlalchemy.engine.row import Row from sqlalchemy.orm.query import Query import voluptuous as vol -from homeassistant.components import frontend +from homeassistant.components import frontend, websocket_api from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED from homeassistant.components.history import ( Filters, @@ -23,7 +23,10 @@ from homeassistant.components.history import ( ) from homeassistant.components.http import HomeAssistantView from homeassistant.components.recorder import get_instance -from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat +from homeassistant.components.recorder.models import ( + process_datetime_to_timestamp, + process_timestamp_to_utc_isoformat, +) from homeassistant.components.recorder.util import session_scope from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN @@ -102,6 +105,10 @@ LOG_MESSAGE_SCHEMA = vol.Schema( ) +LOGBOOK_FILTERS = "logbook_filters" +LOGBOOK_ENTITIES_FILTER = "entities_filter" + + @bind_hass def log_entry( hass: HomeAssistant, @@ -168,7 +175,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: filters = None entities_filter = None + hass.data[LOGBOOK_FILTERS] = filters + hass.data[LOGBOOK_ENTITIES_FILTER] = entities_filter + hass.http.register_view(LogbookView(conf, filters, entities_filter)) + websocket_api.async_register_command(hass, ws_get_events) hass.services.async_register(DOMAIN, "log", log_message, schema=LOG_MESSAGE_SCHEMA) @@ -194,6 +205,61 @@ async def _process_logbook_platform( platform.async_describe_events(hass, _async_describe_event) +@websocket_api.websocket_command( + { + vol.Required("type"): "logbook/get_events", + vol.Required("start_time"): str, + vol.Optional("end_time"): str, + vol.Optional("entity_ids"): [str], + vol.Optional("context_id"): str, + } +) +@websocket_api.async_response +async def ws_get_events( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict +) -> None: + """Handle logbook get events websocket command.""" + start_time_str = msg["start_time"] + end_time_str = msg.get("end_time") + utc_now = dt_util.utcnow() + + if start_time := dt_util.parse_datetime(start_time_str): + start_time = dt_util.as_utc(start_time) + else: + connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") + return + + if not end_time_str: + end_time = utc_now + elif parsed_end_time := dt_util.parse_datetime(end_time_str): + end_time = dt_util.as_utc(parsed_end_time) + else: + connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") + return + + if start_time > utc_now: + connection.send_result(msg["id"], {}) + return + + entity_ids = msg.get("entity_ids") + context_id = msg.get("context_id") + + logbook_events: list[dict[str, Any]] = await get_instance( + hass + ).async_add_executor_job( + _get_events, + hass, + start_time, + end_time, + entity_ids, + hass.data[LOGBOOK_FILTERS], + hass.data[LOGBOOK_ENTITIES_FILTER], + context_id, + True, + ) + connection.send_result(msg["id"], logbook_events) + + class LogbookView(HomeAssistantView): """Handle logbook view requests.""" @@ -267,6 +333,7 @@ class LogbookView(HomeAssistantView): self.filters, self.entities_filter, context_id, + False, ) ) @@ -281,6 +348,7 @@ def _humanify( entity_name_cache: EntityNameCache, event_cache: EventCache, context_augmenter: ContextAugmenter, + format_time: Callable[[Row], Any], ) -> Generator[dict[str, Any], None, None]: """Generate a converted list of events into Entry objects. @@ -307,7 +375,7 @@ def _humanify( continue data = { - "when": _row_time_fired_isoformat(row), + "when": format_time(row), "name": entity_name_cache.get(entity_id, row), "state": row.state, "entity_id": entity_id, @@ -321,21 +389,21 @@ def _humanify( elif event_type in external_events: domain, describe_event = external_events[event_type] data = describe_event(event_cache.get(row)) - data["when"] = _row_time_fired_isoformat(row) + data["when"] = format_time(row) data["domain"] = domain context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row) yield data elif event_type == EVENT_HOMEASSISTANT_START: yield { - "when": _row_time_fired_isoformat(row), + "when": format_time(row), "name": "Home Assistant", "message": "started", "domain": HA_DOMAIN, } elif event_type == EVENT_HOMEASSISTANT_STOP: yield { - "when": _row_time_fired_isoformat(row), + "when": format_time(row), "name": "Home Assistant", "message": "stopped", "domain": HA_DOMAIN, @@ -351,7 +419,7 @@ def _humanify( domain = split_entity_id(str(entity_id))[0] data = { - "when": _row_time_fired_isoformat(row), + "when": format_time(row), "name": event_data.get(ATTR_NAME), "message": event_data.get(ATTR_MESSAGE), "domain": domain, @@ -369,6 +437,7 @@ def _get_events( filters: Filters | None = None, entities_filter: EntityFilter | Callable[[str], bool] | None = None, context_id: str | None = None, + timestamp: bool = False, ) -> list[dict[str, Any]]: """Get events for a period of time.""" assert not ( @@ -386,6 +455,7 @@ def _get_events( context_lookup, entity_name_cache, external_events, event_cache ) event_types = (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) + format_time = _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" @@ -424,6 +494,7 @@ def _get_events( entity_name_cache, event_cache, context_augmenter, + format_time, ) ) @@ -575,9 +646,14 @@ def _row_attributes_extract(row: Row, extractor: re.Pattern) -> str | None: return result.group(1) if result else None -def _row_time_fired_isoformat(row: Row) -> dt | None: +def _row_time_fired_isoformat(row: Row) -> str: """Convert the row timed_fired to isoformat.""" - return process_timestamp_to_utc_isoformat(row.time_fired) or dt_util.utcnow() + return process_timestamp_to_utc_isoformat(row.time_fired or dt_util.utcnow()) + + +def _row_time_fired_timestamp(row: Row) -> float: + """Convert the row timed_fired to timestamp.""" + return process_datetime_to_timestamp(row.time_fired or dt_util.utcnow()) class LazyEventPartialState: diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py index 896add3104e..32273f04c05 100644 --- a/tests/components/logbook/common.py +++ b/tests/components/logbook/common.py @@ -53,6 +53,11 @@ def mock_humanify(hass_, rows): ) return list( logbook._humanify( - hass_, rows, entity_name_cache, event_cache, context_augmenter + hass_, + rows, + entity_name_cache, + event_cache, + context_augmenter, + logbook._row_time_fired_isoformat, ), ) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index cc10346fc07..d382506a3db 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -4,7 +4,6 @@ import collections from datetime import datetime, timedelta from http import HTTPStatus import json -from typing import Any from unittest.mock import Mock, patch import pytest @@ -13,7 +12,6 @@ import voluptuous as vol from homeassistant.components import logbook from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED -from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.sensor import SensorStateClass from homeassistant.const import ( @@ -42,7 +40,7 @@ from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from .common import mock_humanify +from .common import MockRow, mock_humanify from tests.common import async_capture_events, mock_platform from tests.components.recorder.common import ( @@ -2140,27 +2138,174 @@ def _assert_entry( assert state == entry["state"] -class MockRow: - """Minimal row mock.""" +async def test_get_events(hass, hass_ws_client, recorder_mock): + """Test logbook get_events.""" + now = dt_util.utcnow() + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) - def __init__(self, event_type: str, data: dict[str, Any] = None): - """Init the fake row.""" - self.event_type = event_type - self.shared_data = json.dumps(data, cls=JSONEncoder) - self.data = data - self.time_fired = dt_util.utcnow() - self.context_parent_id = None - self.context_user_id = None - self.context_id = None - self.state = None - self.entity_id = None + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - @property - def time_fired_minute(self): - """Minute the event was fired.""" - return self.time_fired.minute + hass.states.async_set("light.kitchen", STATE_OFF) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 100}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 200}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 300}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 400}) + await hass.async_block_till_done() + context = ha.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) - @property - def time_fired_isoformat(self): - """Time event was fired in utc isoformat.""" - return process_timestamp_to_utc_isoformat(self.time_fired) + hass.states.async_set("light.kitchen", STATE_OFF, context=context) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "end_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [] + + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["sensor.test"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + assert response["result"] == [] + + await client.send_json( + { + "id": 3, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + + results = response["result"] + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "on" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "off" + + await client.send_json( + { + "id": 4, + "type": "logbook/get_events", + "start_time": now.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 4 + + results = response["result"] + assert len(results) == 3 + assert results[0]["message"] == "started" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "on" + assert isinstance(results[1]["when"], float) + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "off" + assert isinstance(results[2]["when"], float) + + await client.send_json( + { + "id": 5, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 5 + + results = response["result"] + assert len(results) == 1 + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "off" + assert isinstance(results[0]["when"], float) + + +async def test_get_events_future_start_time(hass, hass_ws_client, recorder_mock): + """Test get_events with a future start time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + future = dt_util.utcnow() + timedelta(hours=10) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": future.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + results = response["result"] + assert len(results) == 0 + + +async def test_get_events_bad_start_time(hass, hass_ws_client, recorder_mock): + """Test get_events bad start time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": "cats", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_start_time" + + +async def test_get_events_bad_end_time(hass, hass_ws_client, recorder_mock): + """Test get_events bad end time.""" + now = dt_util.utcnow() + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "end_time": "dogs", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_end_time" From 1dc15bb7c806c496ba6e848b31818d7b58fd900b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 22:44:35 -0500 Subject: [PATCH 368/930] Prevent history_stats from rejecting states when microseconds differ (#71704) --- .../components/history_stats/data.py | 6 +++- tests/components/history_stats/test_sensor.py | 36 +++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index 3f22f4cc32b..3d21cca6b6d 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -96,7 +96,11 @@ class HistoryStats: new_data = False if event and event.data["new_state"] is not None: new_state: State = event.data["new_state"] - if current_period_start <= new_state.last_changed <= current_period_end: + if ( + current_period_start_timestamp + <= floored_timestamp(new_state.last_changed) + <= current_period_end_timestamp + ): self._history_current_period.append(new_state) new_data = True if not new_data and current_period_end_timestamp < now_timestamp: diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 297e3a5a363..4f56edaa291 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1389,9 +1389,10 @@ async def test_measure_cet(hass, recorder_mock): assert hass.states.get("sensor.sensor4").state == "83.3" -async def test_end_time_with_microseconds_zeroed(hass, recorder_mock): +@pytest.mark.parametrize("time_zone", ["Europe/Berlin", "America/Chicago", "US/Hawaii"]) +async def test_end_time_with_microseconds_zeroed(time_zone, hass, recorder_mock): """Test the history statistics sensor that has the end time microseconds zeroed out.""" - hass.config.set_time_zone("Europe/Berlin") + hass.config.set_time_zone(time_zone) start_of_today = dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0) start_time = start_of_today + timedelta(minutes=60) t0 = start_time + timedelta(minutes=20) @@ -1459,6 +1460,7 @@ async def test_end_time_with_microseconds_zeroed(hass, recorder_mock): async_fire_time_changed(hass, time_600) await hass.async_block_till_done() assert hass.states.get("sensor.heatpump_compressor_today").state == "3.83" + rolled_to_next_day = start_of_today + timedelta(days=1) assert rolled_to_next_day.hour == 0 assert rolled_to_next_day.minute == 0 @@ -1469,3 +1471,33 @@ async def test_end_time_with_microseconds_zeroed(hass, recorder_mock): async_fire_time_changed(hass, rolled_to_next_day) await hass.async_block_till_done() assert hass.states.get("sensor.heatpump_compressor_today").state == "0.0" + + rolled_to_next_day_plus_12 = start_of_today + timedelta( + days=1, hours=12, microseconds=0 + ) + with freeze_time(rolled_to_next_day_plus_12): + async_fire_time_changed(hass, rolled_to_next_day_plus_12) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "12.0" + + rolled_to_next_day_plus_14 = start_of_today + timedelta( + days=1, hours=14, microseconds=0 + ) + with freeze_time(rolled_to_next_day_plus_14): + async_fire_time_changed(hass, rolled_to_next_day_plus_14) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "14.0" + + rolled_to_next_day_plus_16_860000 = start_of_today + timedelta( + days=1, hours=16, microseconds=860000 + ) + with freeze_time(rolled_to_next_day_plus_16_860000): + hass.states.async_set("binary_sensor.heatpump_compressor_state", "off") + async_fire_time_changed(hass, rolled_to_next_day_plus_16_860000) + await hass.async_block_till_done() + + rolled_to_next_day_plus_18 = start_of_today + timedelta(days=1, hours=18) + with freeze_time(rolled_to_next_day_plus_18): + async_fire_time_changed(hass, rolled_to_next_day_plus_18) + await hass.async_block_till_done() + assert hass.states.get("sensor.heatpump_compressor_today").state == "16.0" From 9cd81db5b31165130ec99227230fd9cd753e608f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 22:45:16 -0500 Subject: [PATCH 369/930] Add device_id and logbook descriptions to lutron_caseta (#71713) --- .../components/lutron_caseta/__init__.py | 5 +- .../components/lutron_caseta/logbook.py | 42 +++++++++++ .../components/lutron_caseta/test_logbook.py | 73 +++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/lutron_caseta/logbook.py create mode 100644 tests/components/lutron_caseta/test_logbook.py diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 3d9e07519a8..e9ada82bb54 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -12,7 +12,7 @@ from pylutron_caseta.smartbridge import Smartbridge import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import ATTR_SUGGESTED_AREA, CONF_HOST, Platform +from homeassistant.const import ATTR_DEVICE_ID, ATTR_SUGGESTED_AREA, CONF_HOST, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr @@ -229,6 +229,7 @@ def _async_subscribe_pico_remote_events( button_devices_by_id: dict[int, dict], ): """Subscribe to lutron events.""" + dev_reg = dr.async_get(hass) @callback def _async_button_event(button_id, event_type): @@ -257,6 +258,7 @@ def _async_subscribe_pico_remote_events( ) return lip_button_number = sub_type_to_lip_button[sub_type] + hass_device = dev_reg.async_get_device({(DOMAIN, device["serial"])}) hass.bus.async_fire( LUTRON_CASETA_BUTTON_EVENT, @@ -266,6 +268,7 @@ def _async_subscribe_pico_remote_events( ATTR_BUTTON_NUMBER: lip_button_number, ATTR_LEAP_BUTTON_NUMBER: button_number, ATTR_DEVICE_NAME: name, + ATTR_DEVICE_ID: hass_device.id, ATTR_AREA_NAME: area, ATTR_ACTION: action, }, diff --git a/homeassistant/components/lutron_caseta/logbook.py b/homeassistant/components/lutron_caseta/logbook.py new file mode 100644 index 00000000000..28342090a21 --- /dev/null +++ b/homeassistant/components/lutron_caseta/logbook.py @@ -0,0 +1,42 @@ +"""Describe lutron_caseta logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.core import Event, HomeAssistant, callback + +from .const import ( + ATTR_ACTION, + ATTR_AREA_NAME, + ATTR_DEVICE_NAME, + ATTR_LEAP_BUTTON_NUMBER, + ATTR_TYPE, + DOMAIN, + LUTRON_CASETA_BUTTON_EVENT, +) +from .device_trigger import LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + + @callback + def async_describe_button_event(event: Event) -> dict[str, str]: + """Describe lutron_caseta_button_event logbook event.""" + data = event.data + device_type = data[ATTR_TYPE] + leap_button_number = data[ATTR_LEAP_BUTTON_NUMBER] + button_map = LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP[device_type] + button_description = button_map[leap_button_number] + return { + "name": f"{data[ATTR_AREA_NAME]} {data[ATTR_DEVICE_NAME]}", + "message": f"{data[ATTR_ACTION]} {button_description}", + } + + async_describe_event( + DOMAIN, LUTRON_CASETA_BUTTON_EVENT, async_describe_button_event + ) diff --git a/tests/components/lutron_caseta/test_logbook.py b/tests/components/lutron_caseta/test_logbook.py new file mode 100644 index 00000000000..3a202eadf58 --- /dev/null +++ b/tests/components/lutron_caseta/test_logbook.py @@ -0,0 +1,73 @@ +"""The tests for lutron caseta logbook.""" +from unittest.mock import patch + +from homeassistant.components.lutron_caseta.const import ( + ATTR_ACTION, + ATTR_AREA_NAME, + ATTR_BUTTON_NUMBER, + ATTR_DEVICE_NAME, + ATTR_LEAP_BUTTON_NUMBER, + ATTR_SERIAL, + ATTR_TYPE, + CONF_CA_CERTS, + CONF_CERTFILE, + CONF_KEYFILE, + DOMAIN, + LUTRON_CASETA_BUTTON_EVENT, +) +from homeassistant.const import ATTR_DEVICE_ID, CONF_HOST +from homeassistant.setup import async_setup_component + +from . import MockBridge + +from tests.common import MockConfigEntry +from tests.components.logbook.common import MockRow, mock_humanify + + +async def test_humanify_lutron_caseta_button_event(hass): + """Test humanifying lutron_caseta_button_events.""" + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + config_entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_HOST: "1.1.1.1", + CONF_KEYFILE: "", + CONF_CERTFILE: "", + CONF_CA_CERTS: "", + }, + unique_id="abc", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.lutron_caseta.Smartbridge.create_tls", + return_value=MockBridge(can_connect=True), + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + await hass.async_block_till_done() + + (event1,) = mock_humanify( + hass, + [ + MockRow( + LUTRON_CASETA_BUTTON_EVENT, + { + ATTR_SERIAL: "123", + ATTR_DEVICE_ID: "1234", + ATTR_TYPE: "Pico3ButtonRaiseLower", + ATTR_LEAP_BUTTON_NUMBER: 3, + ATTR_BUTTON_NUMBER: 3, + ATTR_DEVICE_NAME: "Pico", + ATTR_AREA_NAME: "Living Room", + ATTR_ACTION: "press", + }, + ), + ], + ) + + assert event1["name"] == "Living Room Pico" + assert event1["domain"] == DOMAIN + assert event1["message"] == "press raise" From 18bdc70185cd9751a0a2bdf83729ea0c717897c2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 May 2022 22:45:47 -0500 Subject: [PATCH 370/930] Update sql to prepare for sqlalchemy 2.0 (#71532) * Update sql to prepare for sqlalchemy 2.0 * config flow as well --- homeassistant/components/sql/config_flow.py | 6 +++--- homeassistant/components/sql/sensor.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index ba9a5f7e4dd..9a6013e1d62 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -45,15 +45,15 @@ def validate_sql_select(value: str) -> str | None: def validate_query(db_url: str, query: str, column: str) -> bool: """Validate SQL query.""" try: - engine = sqlalchemy.create_engine(db_url) - sessmaker = scoped_session(sessionmaker(bind=engine)) + engine = sqlalchemy.create_engine(db_url, future=True) + sessmaker = scoped_session(sessionmaker(bind=engine, future=True)) except SQLAlchemyError as error: raise error sess: scoped_session = sessmaker() try: - result: Result = sess.execute(query) + result: Result = sess.execute(sqlalchemy.text(query)) for res in result.mappings(): data = res[column] _LOGGER.debug("Return value from query: %s", data) diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index 5e748bed55e..4e3f9d15846 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -111,8 +111,8 @@ async def async_setup_entry( value_template.hass = hass try: - engine = sqlalchemy.create_engine(db_url) - sessmaker = scoped_session(sessionmaker(bind=engine)) + engine = sqlalchemy.create_engine(db_url, future=True) + sessmaker = scoped_session(sessionmaker(bind=engine, future=True)) except SQLAlchemyError as err: _LOGGER.error("Can not open database %s", {redact_credentials(str(err))}) return @@ -179,7 +179,7 @@ class SQLSensor(SensorEntity): self._attr_extra_state_attributes = {} sess: scoped_session = self.sessionmaker() try: - result = sess.execute(self._query) + result = sess.execute(sqlalchemy.text(self._query)) except SQLAlchemyError as err: _LOGGER.error( "Error executing query %s: %s", From 684fe242d9c2afdd0b5d21d7b459f86b300a9ee6 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 11 May 2022 23:51:10 -0400 Subject: [PATCH 371/930] Set PARALLEL_UPDATES to 0 for all zwave_js platforms (#71626) --- homeassistant/components/zwave_js/binary_sensor.py | 2 ++ homeassistant/components/zwave_js/button.py | 2 ++ homeassistant/components/zwave_js/climate.py | 2 ++ homeassistant/components/zwave_js/cover.py | 2 ++ homeassistant/components/zwave_js/fan.py | 2 ++ homeassistant/components/zwave_js/humidifier.py | 2 ++ homeassistant/components/zwave_js/light.py | 2 ++ homeassistant/components/zwave_js/lock.py | 2 ++ homeassistant/components/zwave_js/number.py | 2 ++ homeassistant/components/zwave_js/select.py | 2 ++ homeassistant/components/zwave_js/sensor.py | 2 ++ homeassistant/components/zwave_js/siren.py | 2 ++ homeassistant/components/zwave_js/switch.py | 2 ++ 13 files changed, 26 insertions(+) diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py index 88ab7221600..8a86898239a 100644 --- a/homeassistant/components/zwave_js/binary_sensor.py +++ b/homeassistant/components/zwave_js/binary_sensor.py @@ -27,6 +27,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zwave_js/button.py b/homeassistant/components/zwave_js/button.py index ea987b56258..49c2a76cccf 100644 --- a/homeassistant/components/zwave_js/button.py +++ b/homeassistant/components/zwave_js/button.py @@ -14,6 +14,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DATA_CLIENT, DOMAIN, LOGGER from .helpers import get_device_id, get_valueless_base_unique_id +PARALLEL_UPDATES = 0 + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 188e627bb5e..67def698fc2 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -52,6 +52,8 @@ from .discovery_data_template import DynamicCurrentTempClimateDataTemplate from .entity import ZWaveBaseEntity from .helpers import get_value_of_zwave_value +PARALLEL_UPDATES = 0 + # Map Z-Wave HVAC Mode to Home Assistant value # Note: We treat "auto" as "heat_cool" as most Z-Wave devices # report auto_changeover as auto without schedule support. diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index 9281f0bc21c..c7ba50ee7e7 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -35,6 +35,8 @@ from .discovery import ZwaveDiscoveryInfo from .discovery_data_template import CoverTiltDataTemplate from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index 623ee072f3a..f571884ac80 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -35,6 +35,8 @@ from .discovery_data_template import FanValueMapping, FanValueMappingDataTemplat from .entity import ZWaveBaseEntity from .helpers import get_value_of_zwave_value +PARALLEL_UPDATES = 0 + DEFAULT_SPEED_RANGE = (1, 99) # off is not included ATTR_FAN_STATE = "fan_state" diff --git a/homeassistant/components/zwave_js/humidifier.py b/homeassistant/components/zwave_js/humidifier.py index 44d7bc19bbb..8cf0b6aec7c 100644 --- a/homeassistant/components/zwave_js/humidifier.py +++ b/homeassistant/components/zwave_js/humidifier.py @@ -32,6 +32,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + @dataclass class ZwaveHumidifierEntityDescriptionRequiredKeys: diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index ca51bc83986..534a86f1c86 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -45,6 +45,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) MULTI_COLOR_MAP = { diff --git a/homeassistant/components/zwave_js/lock.py b/homeassistant/components/zwave_js/lock.py index d70b6ef2009..3781821e4c7 100644 --- a/homeassistant/components/zwave_js/lock.py +++ b/homeassistant/components/zwave_js/lock.py @@ -34,6 +34,8 @@ from .const import ( from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) STATE_TO_ZWAVE_MAP: dict[int, dict[str, int | bool]] = { diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py index 16434b51108..fb9266ac071 100644 --- a/homeassistant/components/zwave_js/number.py +++ b/homeassistant/components/zwave_js/number.py @@ -14,6 +14,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/zwave_js/select.py b/homeassistant/components/zwave_js/select.py index c6bc11a4804..085c694fc0e 100644 --- a/homeassistant/components/zwave_js/select.py +++ b/homeassistant/components/zwave_js/select.py @@ -18,6 +18,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index e60a0793608..71e6bc48952 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -63,6 +63,8 @@ from .discovery_data_template import ( from .entity import ZWaveBaseEntity from .helpers import get_device_id, get_valueless_base_unique_id +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) STATUS_ICON: dict[NodeStatus, str] = { diff --git a/homeassistant/components/zwave_js/siren.py b/homeassistant/components/zwave_js/siren.py index e7443f33dae..e686c5446ca 100644 --- a/homeassistant/components/zwave_js/siren.py +++ b/homeassistant/components/zwave_js/siren.py @@ -21,6 +21,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py index a680a8fb04a..115f90b8e11 100644 --- a/homeassistant/components/zwave_js/switch.py +++ b/homeassistant/components/zwave_js/switch.py @@ -20,6 +20,8 @@ from .const import DATA_CLIENT, DOMAIN from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity +PARALLEL_UPDATES = 0 + LOGGER = logging.getLogger(__name__) From ca52aa47aaf1af3299c18dd69a24839266938e06 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 12 May 2022 03:05:35 -0400 Subject: [PATCH 372/930] Enable sentry reporting for zwave_js (#71719) * Enable sentry reporting for zwave_js * Fix test --- homeassistant/components/zwave_js/helpers.py | 1 + tests/components/zwave_js/test_api.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 17515f9aa99..c4eb0396287 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -95,6 +95,7 @@ def get_value_of_zwave_value(value: ZwaveValue | None) -> Any | None: async def async_enable_statistics(client: ZwaveClient) -> None: """Enable statistics on the driver.""" await client.driver.async_enable_statistics("Home Assistant", HA_VERSION) + await client.driver.async_enable_error_reporting() @callback diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 7ec7d98217b..f4ffa0876d9 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -3172,10 +3172,12 @@ async def test_data_collection(hass, client, integration, hass_ws_client): result = msg["result"] assert result is None - assert len(client.async_send_command.call_args_list) == 1 - args = client.async_send_command.call_args[0][0] + assert len(client.async_send_command.call_args_list) == 2 + args = client.async_send_command.call_args_list[0][0][0] assert args["command"] == "driver.enable_statistics" assert args["applicationName"] == "Home Assistant" + args = client.async_send_command.call_args_list[1][0][0] + assert args["command"] == "driver.enable_error_reporting" assert entry.data[CONF_DATA_COLLECTION_OPTED_IN] client.async_send_command.reset_mock() From 0d69adb4046d6487dabe07277b93e7af81205cfc Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 12 May 2022 03:07:11 -0400 Subject: [PATCH 373/930] Send initial message for certain zwave_js ws subscriptions (#71723) * Send initial message for certain zwave_js ws subscriptions * Be consistent * fix tests and bugs --- homeassistant/components/zwave_js/api.py | 39 ++++++++++++++++++++---- tests/components/zwave_js/test_api.py | 24 +++++++++++++-- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 5608679fe90..7d1c4622d50 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -1848,9 +1848,17 @@ async def websocket_subscribe_firmware_update_status( connection.subscriptions[msg["id"]] = async_cleanup progress = node.firmware_update_progress - connection.send_result( - msg[ID], _get_firmware_update_progress_dict(progress) if progress else None - ) + connection.send_result(msg[ID]) + if progress: + connection.send_message( + websocket_api.event_message( + msg[ID], + { + "event": "firmware update progress", + **_get_firmware_update_progress_dict(progress), + }, + ) + ) class FirmwareUploadView(HomeAssistantView): @@ -2011,8 +2019,16 @@ async def websocket_subscribe_controller_statistics( ] connection.subscriptions[msg["id"]] = async_cleanup - connection.send_result( - msg[ID], _get_controller_statistics_dict(controller.statistics) + connection.send_result(msg[ID]) + connection.send_message( + websocket_api.event_message( + msg[ID], + { + "event": "statistics updated", + "source": "controller", + **_get_controller_statistics_dict(controller.statistics), + }, + ) ) @@ -2069,7 +2085,18 @@ async def websocket_subscribe_node_statistics( msg[DATA_UNSUBSCRIBE] = unsubs = [node.on("statistics updated", forward_stats)] connection.subscriptions[msg["id"]] = async_cleanup - connection.send_result(msg[ID], _get_node_statistics_dict(node.statistics)) + connection.send_result(msg[ID]) + connection.send_message( + websocket_api.event_message( + msg[ID], + { + "event": "statistics updated", + "source": "node", + "nodeId": node.node_id, + **_get_node_statistics_dict(node.statistics), + }, + ) + ) @websocket_api.require_admin diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index f4ffa0876d9..d8a36fc6509 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -3479,7 +3479,14 @@ async def test_subscribe_firmware_update_status_initial_value( msg = await ws_client.receive_json() assert msg["success"] - assert msg["result"] == {"sent_fragments": 1, "total_fragments": 10} + assert msg["result"] is None + + msg = await ws_client.receive_json() + assert msg["event"] == { + "event": "firmware update progress", + "sent_fragments": 1, + "total_fragments": 10, + } async def test_subscribe_firmware_update_status_failures( @@ -3688,7 +3695,12 @@ async def test_subscribe_controller_statistics( msg = await ws_client.receive_json() assert msg["success"] - assert msg["result"] == { + assert msg["result"] is None + + msg = await ws_client.receive_json() + assert msg["event"] == { + "event": "statistics updated", + "source": "controller", "messages_tx": 0, "messages_rx": 0, "messages_dropped_tx": 0, @@ -3783,7 +3795,13 @@ async def test_subscribe_node_statistics( msg = await ws_client.receive_json() assert msg["success"] - assert msg["result"] == { + assert msg["result"] is None + + msg = await ws_client.receive_json() + assert msg["event"] == { + "source": "node", + "event": "statistics updated", + "nodeId": multisensor_6.node_id, "commands_tx": 0, "commands_rx": 0, "commands_dropped_tx": 0, From 533257021cddf56312092cff3ff71947c7086f77 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 12 May 2022 03:07:58 -0400 Subject: [PATCH 374/930] Parallelize zwave_js service calls (#71662) * Parallelize zwave_js service calls to speed them up and handle exceptions properly * Fix bug * Add tests * Fix comments * Additional comment fixes Co-authored-by: Martin Hjelmare --- homeassistant/components/zwave_js/services.py | 149 ++++++++++----- tests/components/zwave_js/test_services.py | 169 +++++++++++++++++- 2 files changed, 274 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/zwave_js/services.py b/homeassistant/components/zwave_js/services.py index 8577e48e7a3..af8f3c4c4e7 100644 --- a/homeassistant/components/zwave_js/services.py +++ b/homeassistant/components/zwave_js/services.py @@ -2,13 +2,14 @@ from __future__ import annotations import asyncio +from collections.abc import Generator import logging -from typing import Any, cast +from typing import Any import voluptuous as vol from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import CommandClass, CommandStatus -from zwave_js_server.exceptions import FailedCommand, SetValueFailed +from zwave_js_server.exceptions import SetValueFailed from zwave_js_server.model.endpoint import Endpoint from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import get_value_id @@ -38,6 +39,12 @@ from .helpers import ( _LOGGER = logging.getLogger(__name__) +SET_VALUE_FAILED_EXC = SetValueFailed( + "Unable to set value, refer to " + "https://zwave-js.github.io/node-zwave-js/#/api/node?id=setvalue for " + "possible reasons" +) + def parameter_name_does_not_need_bitmask( val: dict[str, int | str | list[str]] @@ -64,6 +71,33 @@ def broadcast_command(val: dict[str, Any]) -> dict[str, Any]: ) +def get_valid_responses_from_results( + zwave_objects: set[ZwaveNode | Endpoint], results: tuple[Any, ...] +) -> Generator[tuple[ZwaveNode | Endpoint, Any], None, None]: + """Return valid responses from a list of results.""" + for zwave_object, result in zip(zwave_objects, results): + if not isinstance(result, Exception): + yield zwave_object, result + + +def raise_exceptions_from_results( + zwave_objects: set[ZwaveNode | Endpoint] | tuple[ZwaveNode | str, ...], + results: tuple[Any, ...], +) -> None: + """Raise list of exceptions from a list of results.""" + if errors := [ + tup for tup in zip(zwave_objects, results) if isinstance(tup[1], Exception) + ]: + lines = ( + f"{len(errors)} error(s):", + *( + f"{zwave_object} - {error.__class__.__name__}: {error.args[0]}" + for zwave_object, error in errors + ), + ) + raise HomeAssistantError("\n".join(lines)) + + class ZWaveServices: """Class that holds our services (Zwave Commands) that should be published to hass.""" @@ -371,14 +405,21 @@ class ZWaveServices: property_key = service.data.get(const.ATTR_CONFIG_PARAMETER_BITMASK) new_value = service.data[const.ATTR_CONFIG_VALUE] - for node in nodes: - zwave_value, cmd_status = await async_set_config_parameter( - node, - new_value, - property_or_property_name, - property_key=property_key, - ) - + results = await asyncio.gather( + *( + async_set_config_parameter( + node, + new_value, + property_or_property_name, + property_key=property_key, + ) + for node in nodes + ), + return_exceptions=True, + ) + for node, result in get_valid_responses_from_results(nodes, results): + zwave_value = result[0] + cmd_status = result[1] if cmd_status == CommandStatus.ACCEPTED: msg = "Set configuration parameter %s on Node %s with value %s" else: @@ -386,8 +427,8 @@ class ZWaveServices: "Added command to queue to set configuration parameter %s on Node " "%s with value %s. Parameter will be set when the device wakes up" ) - _LOGGER.info(msg, zwave_value, node, new_value) + raise_exceptions_from_results(nodes, results) async def async_bulk_set_partial_config_parameters( self, service: ServiceCall @@ -397,23 +438,31 @@ class ZWaveServices: property_ = service.data[const.ATTR_CONFIG_PARAMETER] new_value = service.data[const.ATTR_CONFIG_VALUE] - for node in nodes: - cmd_status = await async_bulk_set_partial_config_parameters( - node, - property_, - new_value, - ) + results = await asyncio.gather( + *( + async_bulk_set_partial_config_parameters( + node, + property_, + new_value, + ) + for node in nodes + ), + return_exceptions=True, + ) + for node, cmd_status in get_valid_responses_from_results(nodes, results): if cmd_status == CommandStatus.ACCEPTED: msg = "Bulk set partials for configuration parameter %s on Node %s" else: msg = ( - "Added command to queue to bulk set partials for configuration " - "parameter %s on Node %s" + "Queued command to bulk set partials for configuration parameter " + "%s on Node %s" ) _LOGGER.info(msg, property_, node) + raise_exceptions_from_results(nodes, results) + async def async_poll_value(self, service: ServiceCall) -> None: """Poll value on a node.""" for entity_id in service.data[ATTR_ENTITY_ID]: @@ -436,6 +485,7 @@ class ZWaveServices: wait_for_result = service.data.get(const.ATTR_WAIT_FOR_RESULT) options = service.data.get(const.ATTR_OPTIONS) + coros = [] for node in nodes: value_id = get_value_id( node, @@ -455,19 +505,29 @@ class ZWaveServices: new_value_ = str(new_value) else: new_value_ = new_value - success = await node.async_set_value( - value_id, - new_value_, - options=options, - wait_for_result=wait_for_result, + coros.append( + node.async_set_value( + value_id, + new_value_, + options=options, + wait_for_result=wait_for_result, + ) ) + results = await asyncio.gather(*coros, return_exceptions=True) + # multiple set_values my fail so we will track the entire list + set_value_failed_nodes_list = [] + for node, success in get_valid_responses_from_results(nodes, results): if success is False: - raise HomeAssistantError( - "Unable to set value, refer to " - "https://zwave-js.github.io/node-zwave-js/#/api/node?id=setvalue " - "for possible reasons" - ) from SetValueFailed + # If we failed to set a value, add node to SetValueFailed exception list + set_value_failed_nodes_list.append(node) + + # Add the SetValueFailed exception to the results and the nodes to the node + # list. No-op if there are no SetValueFailed exceptions + raise_exceptions_from_results( + (*nodes, *set_value_failed_nodes_list), + (*results, *([SET_VALUE_FAILED_EXC] * len(set_value_failed_nodes_list))), + ) async def async_multicast_set_value(self, service: ServiceCall) -> None: """Set a value via multicast to multiple nodes.""" @@ -556,24 +616,29 @@ class ZWaveServices: async def _async_invoke_cc_api(endpoints: set[Endpoint]) -> None: """Invoke the CC API on a node endpoint.""" - errors: list[str] = [] - for endpoint in endpoints: + results = await asyncio.gather( + *( + endpoint.async_invoke_cc_api( + command_class, method_name, *parameters + ) + for endpoint in endpoints + ), + return_exceptions=True, + ) + for endpoint, result in get_valid_responses_from_results( + endpoints, results + ): _LOGGER.info( - "Invoking %s CC API method %s on endpoint %s", + ( + "Invoked %s CC API method %s on endpoint %s with the following " + "result: %s" + ), command_class.name, method_name, endpoint, + result, ) - try: - await endpoint.async_invoke_cc_api( - command_class, method_name, *parameters - ) - except FailedCommand as err: - errors.append(cast(str, err.args[0])) - if errors: - raise HomeAssistantError( - "\n".join([f"{len(errors)} error(s):", *errors]) - ) + raise_exceptions_from_results(endpoints, results) # If an endpoint is provided, we assume the user wants to call the CC API on # that endpoint for all target nodes diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index 67f3320b5f7..e04ec569c9f 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -518,6 +518,71 @@ async def test_set_config_parameter(hass, client, multisensor_6, integration): ) +async def test_set_config_parameter_gather( + hass, + client, + multisensor_6, + climate_radio_thermostat_ct100_plus_different_endpoints, + integration, +): + """Test the set_config_parameter service gather functionality.""" + # Test setting config parameter by property and validate that the first node + # which triggers an error doesn't prevent the second one to be called. + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + DOMAIN, + SERVICE_SET_CONFIG_PARAMETER, + { + ATTR_ENTITY_ID: [ + AIR_TEMPERATURE_SENSOR, + CLIMATE_RADIO_THERMOSTAT_ENTITY, + ], + ATTR_CONFIG_PARAMETER: 1, + ATTR_CONFIG_VALUE: 1, + }, + blocking=True, + ) + + assert len(client.async_send_command_no_wait.call_args_list) == 0 + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 26 + assert args["valueId"] == { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 1, + "propertyName": "Temperature Reporting Threshold", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": True, + "writeable": True, + "description": "Reporting threshold for changes in the ambient temperature", + "label": "Temperature Reporting Threshold", + "default": 2, + "min": 0, + "max": 4, + "states": { + "0": "Disabled", + "1": "0.5\u00b0 F", + "2": "1.0\u00b0 F", + "3": "1.5\u00b0 F", + "4": "2.0\u00b0 F", + }, + "valueSize": 1, + "format": 0, + "allowManualEntry": False, + "isFromConfig": True, + }, + "value": 1, + } + assert args["value"] == 1 + + client.async_send_command.reset_mock() + + async def test_bulk_set_config_parameters(hass, client, multisensor_6, integration): """Test the bulk_set_partial_config_parameters service.""" dev_reg = async_get_dev_reg(hass) @@ -726,6 +791,45 @@ async def test_bulk_set_config_parameters(hass, client, multisensor_6, integrati client.async_send_command.reset_mock() +async def test_bulk_set_config_parameters_gather( + hass, + client, + multisensor_6, + climate_radio_thermostat_ct100_plus_different_endpoints, + integration, +): + """Test the bulk_set_partial_config_parameters service gather functionality.""" + # Test bulk setting config parameter by property and validate that the first node + # which triggers an error doesn't prevent the second one to be called. + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + DOMAIN, + SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS, + { + ATTR_ENTITY_ID: [ + CLIMATE_RADIO_THERMOSTAT_ENTITY, + AIR_TEMPERATURE_SENSOR, + ], + ATTR_CONFIG_PARAMETER: 102, + ATTR_CONFIG_VALUE: 241, + }, + blocking=True, + ) + + assert len(client.async_send_command.call_args_list) == 0 + assert len(client.async_send_command_no_wait.call_args_list) == 1 + args = client.async_send_command_no_wait.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 52 + assert args["valueId"] == { + "commandClass": 112, + "property": 102, + } + assert args["value"] == 241 + + client.async_send_command_no_wait.reset_mock() + + async def test_refresh_value( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, integration ): @@ -1126,6 +1230,66 @@ async def test_set_value_options(hass, client, aeon_smart_switch_6, integration) client.async_send_command.reset_mock() +async def test_set_value_gather( + hass, + client, + multisensor_6, + climate_radio_thermostat_ct100_plus_different_endpoints, + integration, +): + """Test the set_value service gather functionality.""" + # Test setting value by property and validate that the first node + # which triggers an error doesn't prevent the second one to be called. + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: [ + CLIMATE_RADIO_THERMOSTAT_ENTITY, + AIR_TEMPERATURE_SENSOR, + ], + ATTR_COMMAND_CLASS: 112, + ATTR_PROPERTY: 102, + ATTR_PROPERTY_KEY: 1, + ATTR_VALUE: 1, + }, + blocking=True, + ) + + assert len(client.async_send_command.call_args_list) == 0 + assert len(client.async_send_command_no_wait.call_args_list) == 1 + args = client.async_send_command_no_wait.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 52 + assert args["valueId"] == { + "commandClassName": "Configuration", + "commandClass": 112, + "endpoint": 0, + "property": 102, + "propertyKey": 1, + "propertyName": "Group 2: Send battery reports", + "metadata": { + "type": "number", + "readable": True, + "writeable": True, + "valueSize": 4, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": True, + "label": "Group 2: Send battery reports", + "description": "Include battery information in periodic reports to Group 2", + "isFromConfig": True, + }, + "value": 0, + } + assert args["value"] == 1 + + client.async_send_command_no_wait.reset_mock() + + async def test_multicast_set_value( hass, client, @@ -1728,7 +1892,8 @@ async def test_invoke_cc_api( client.async_send_command.reset_mock() client.async_send_command_no_wait.reset_mock() - # Test failed invoke_cc_api call on one node + # Test failed invoke_cc_api call on one node. We return the error on + # the first node in the call to make sure that gather works as expected client.async_send_command.return_value = {"response": True} client.async_send_command_no_wait.side_effect = FailedZWaveCommand( "test", 12, "test" @@ -1740,8 +1905,8 @@ async def test_invoke_cc_api( SERVICE_INVOKE_CC_API, { ATTR_DEVICE_ID: [ - device_radio_thermostat.id, device_danfoss.id, + device_radio_thermostat.id, ], ATTR_COMMAND_CLASS: 132, ATTR_ENDPOINT: 0, From 3ce19cd6f870b91f162c69d027a52f2f795f899d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 May 2022 09:39:49 +0200 Subject: [PATCH 375/930] Remove YAML configuration from DuneHD (#71694) --- .../components/dunehd/config_flow.py | 28 +----------- .../components/dunehd/media_player.py | 39 +--------------- tests/components/dunehd/test_config_flow.py | 45 +------------------ 3 files changed, 4 insertions(+), 108 deletions(-) diff --git a/homeassistant/components/dunehd/config_flow.py b/homeassistant/components/dunehd/config_flow.py index 434bbc7bd84..b5a656716b0 100644 --- a/homeassistant/components/dunehd/config_flow.py +++ b/homeassistant/components/dunehd/config_flow.py @@ -2,9 +2,8 @@ from __future__ import annotations import ipaddress -import logging import re -from typing import Any, Final +from typing import Any from pdunehd import DuneHDPlayer import voluptuous as vol @@ -15,8 +14,6 @@ from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN -_LOGGER: Final = logging.getLogger(__name__) - def host_valid(host: str) -> bool: """Return True if hostname or IP address is valid.""" @@ -72,29 +69,6 @@ class DuneHDConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_import( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle configuration by yaml file.""" - _LOGGER.warning( - "Configuration of the Dune HD integration in YAML is deprecated and will be " - "removed in Home Assistant 2022.6; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" - ) - assert user_input is not None - host: str = user_input[CONF_HOST] - - self._async_abort_entries_match({CONF_HOST: host}) - - try: - await self.init_device(host) - except CannotConnect: - _LOGGER.error("Import aborted, cannot connect to %s", host) - return self.async_abort(reason="cannot_connect") - else: - return self.async_create_entry(title=host, data=user_input) - def host_already_configured(self, host: str) -> bool: """See if we already have a dunehd entry matching user input configured.""" existing_hosts = { diff --git a/homeassistant/components/dunehd/media_player.py b/homeassistant/components/dunehd/media_player.py index d39d148e5a5..f6d90b7b1d9 100644 --- a/homeassistant/components/dunehd/media_player.py +++ b/homeassistant/components/dunehd/media_player.py @@ -4,40 +4,21 @@ from __future__ import annotations from typing import Any, Final from pdunehd import DuneHDPlayer -import voluptuous as vol from homeassistant.components.media_player import ( - PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, MediaPlayerEntity, MediaPlayerEntityFeature, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_NAME, - STATE_OFF, - STATE_ON, - STATE_PAUSED, - STATE_PLAYING, -) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import ATTR_MANUFACTURER, DEFAULT_NAME, DOMAIN CONF_SOURCES: Final = "sources" -PLATFORM_SCHEMA: Final = PARENT_PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_SOURCES): vol.Schema({cv.string: cv.string}), - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - } -) - DUNEHD_PLAYER_SUPPORT: Final[int] = ( MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.TURN_ON @@ -48,22 +29,6 @@ DUNEHD_PLAYER_SUPPORT: Final[int] = ( ) -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Dune HD media player platform.""" - host: str = config[CONF_HOST] - - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data={CONF_HOST: host} - ) - ) - - async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: diff --git a/tests/components/dunehd/test_config_flow.py b/tests/components/dunehd/test_config_flow.py index 73732236077..76585ac73a1 100644 --- a/tests/components/dunehd/test_config_flow.py +++ b/tests/components/dunehd/test_config_flow.py @@ -3,7 +3,7 @@ from unittest.mock import patch from homeassistant import data_entry_flow from homeassistant.components.dunehd.const import DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_HOST from tests.common import MockConfigEntry @@ -14,49 +14,6 @@ CONFIG_IP = {CONF_HOST: "10.10.10.12"} DUNEHD_STATE = {"protocol_version": "4", "player_state": "navigator"} -async def test_import(hass): - """Test that the import works.""" - with patch("homeassistant.components.dunehd.async_setup_entry"), patch( - "pdunehd.DuneHDPlayer.update_state", return_value=DUNEHD_STATE - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=CONFIG_HOSTNAME - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "dunehd-host" - assert result["data"] == {CONF_HOST: "dunehd-host"} - - -async def test_import_cannot_connect(hass): - """Test that errors are shown when cannot connect to the host during import.""" - with patch("pdunehd.DuneHDPlayer.update_state", return_value={}): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=CONFIG_HOSTNAME - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "cannot_connect" - - -async def test_import_duplicate_error(hass): - """Test that errors are shown when duplicates are added during import.""" - config_entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "dunehd-host"}, - title="dunehd-host", - ) - config_entry.add_to_hass(hass) - - with patch("pdunehd.DuneHDPlayer.update_state", return_value=DUNEHD_STATE): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=CONFIG_HOSTNAME - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "already_configured" - - async def test_user_invalid_host(hass): """Test that errors are shown when the host is invalid.""" result = await hass.config_entries.flow.async_init( From 135326a4a6c152d2d2a3d96b3781cb21d2bdb921 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 May 2022 10:31:42 +0200 Subject: [PATCH 376/930] Remove YAML configuration from filesize (#71692) --- homeassistant/components/filesize/__init__.py | 3 -- .../components/filesize/config_flow.py | 4 -- homeassistant/components/filesize/const.py | 2 - homeassistant/components/filesize/sensor.py | 37 +------------- tests/components/filesize/test_config_flow.py | 23 +-------- tests/components/filesize/test_init.py | 48 +------------------ tests/components/filesize/test_sensor.py | 22 --------- 7 files changed, 6 insertions(+), 133 deletions(-) diff --git a/homeassistant/components/filesize/__init__.py b/homeassistant/components/filesize/__init__.py index 8f5b098d221..b3292ed9c9f 100644 --- a/homeassistant/components/filesize/__init__.py +++ b/homeassistant/components/filesize/__init__.py @@ -1,7 +1,6 @@ """The filesize component.""" from __future__ import annotations -import logging import pathlib from homeassistant.config_entries import ConfigEntry @@ -11,8 +10,6 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import PLATFORMS -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up from a config entry.""" diff --git a/homeassistant/components/filesize/config_flow.py b/homeassistant/components/filesize/config_flow.py index 7838353fa12..ed2d4ab0940 100644 --- a/homeassistant/components/filesize/config_flow.py +++ b/homeassistant/components/filesize/config_flow.py @@ -67,10 +67,6 @@ class FilesizeConfigFlow(ConfigFlow, domain=DOMAIN): step_id="user", data_schema=DATA_SCHEMA, errors=errors ) - async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult: - """Handle import from configuration.yaml.""" - return await self.async_step_user(user_input) - class NotValidError(Exception): """Path is not valid error.""" diff --git a/homeassistant/components/filesize/const.py b/homeassistant/components/filesize/const.py index a47f1f99d38..a3180f762c0 100644 --- a/homeassistant/components/filesize/const.py +++ b/homeassistant/components/filesize/const.py @@ -4,5 +4,3 @@ from homeassistant.const import Platform DOMAIN = "filesize" PLATFORMS = [Platform.SENSOR] - -CONF_FILE_PATHS = "file_paths" diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 22b8cd60d79..70896bcacad 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -6,23 +6,18 @@ import logging import os import pathlib -import voluptuous as vol - from homeassistant.components.sensor import ( - PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA, SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_FILE_PATH, DATA_BYTES, DATA_MEGABYTES from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -30,7 +25,7 @@ from homeassistant.helpers.update_coordinator import ( ) import homeassistant.util.dt as dt_util -from .const import CONF_FILE_PATHS, DOMAIN +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -64,34 +59,6 @@ SENSOR_TYPES = ( ), ) -PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( - {vol.Required(CONF_FILE_PATHS): vol.All(cv.ensure_list, [cv.isfile])} -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigType, - async_add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the file size sensor.""" - _LOGGER.warning( - # Filesize config flow added in 2022.4 and should be removed in 2022.6 - "Configuration of the Filesize sensor platform in YAML is deprecated and " - "will be removed in Home Assistant 2022.6; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" - ) - for path in config[CONF_FILE_PATHS]: - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data={CONF_FILE_PATH: path}, - ) - ) - async def async_setup_entry( hass: HomeAssistant, diff --git a/tests/components/filesize/test_config_flow.py b/tests/components/filesize/test_config_flow.py index 0209444ec42..f6873e64128 100644 --- a/tests/components/filesize/test_config_flow.py +++ b/tests/components/filesize/test_config_flow.py @@ -1,10 +1,8 @@ """Tests for the Filesize config flow.""" from unittest.mock import patch -import pytest - from homeassistant.components.filesize.const import DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_FILE_PATH from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import ( @@ -40,39 +38,22 @@ async def test_full_user_flow(hass: HomeAssistant) -> None: assert result2.get("data") == {CONF_FILE_PATH: TEST_FILE} -@pytest.mark.parametrize("source", [SOURCE_USER, SOURCE_IMPORT]) async def test_unique_path( hass: HomeAssistant, mock_config_entry: MockConfigEntry, - source: str, ) -> None: """Test we abort if already setup.""" hass.config.allowlist_external_dirs = {TEST_DIR} mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": source}, data={CONF_FILE_PATH: TEST_FILE} + DOMAIN, context={"source": SOURCE_USER}, data={CONF_FILE_PATH: TEST_FILE} ) assert result.get("type") == RESULT_TYPE_ABORT assert result.get("reason") == "already_configured" -async def test_import_flow(hass: HomeAssistant) -> None: - """Test the import configuration flow.""" - create_file(TEST_FILE) - hass.config.allowlist_external_dirs = {TEST_DIR} - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data={CONF_FILE_PATH: TEST_FILE}, - ) - - assert result.get("type") == RESULT_TYPE_CREATE_ENTRY - assert result.get("title") == TEST_FILE_NAME - assert result.get("data") == {CONF_FILE_PATH: TEST_FILE} - - async def test_flow_fails_on_validation(hass: HomeAssistant) -> None: """Test config flow errors.""" create_file(TEST_FILE) diff --git a/tests/components/filesize/test_init.py b/tests/components/filesize/test_init.py index fecd3033c91..99285e56b6f 100644 --- a/tests/components/filesize/test_init.py +++ b/tests/components/filesize/test_init.py @@ -1,21 +1,10 @@ """Tests for the Filesize integration.""" -from unittest.mock import AsyncMock - -from homeassistant.components.filesize.const import CONF_FILE_PATHS, DOMAIN -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.filesize.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_FILE_PATH from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component -from . import ( - TEST_DIR, - TEST_FILE, - TEST_FILE2, - TEST_FILE_NAME, - TEST_FILE_NAME2, - create_file, -) +from . import create_file from tests.common import MockConfigEntry @@ -75,36 +64,3 @@ async def test_not_valid_path_to_file( await hass.async_block_till_done() assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY - - -async def test_import_config( - hass: HomeAssistant, - mock_setup_entry: AsyncMock, -) -> None: - """Test Filesize being set up from config via import.""" - create_file(TEST_FILE) - create_file(TEST_FILE2) - hass.config.allowlist_external_dirs = {TEST_DIR} - assert await async_setup_component( - hass, - SENSOR_DOMAIN, - { - SENSOR_DOMAIN: { - "platform": DOMAIN, - CONF_FILE_PATHS: [TEST_FILE, TEST_FILE2], - } - }, - ) - await hass.async_block_till_done() - - config_entries = hass.config_entries.async_entries(DOMAIN) - assert len(config_entries) == 2 - - entry = config_entries[0] - assert entry.title == TEST_FILE_NAME - assert entry.unique_id == TEST_FILE - assert entry.data == {CONF_FILE_PATH: TEST_FILE} - entry2 = config_entries[1] - assert entry2.title == TEST_FILE_NAME2 - assert entry2.unique_id == TEST_FILE2 - assert entry2.data == {CONF_FILE_PATH: TEST_FILE2} diff --git a/tests/components/filesize/test_sensor.py b/tests/components/filesize/test_sensor.py index 6f21119f95f..803aa96610d 100644 --- a/tests/components/filesize/test_sensor.py +++ b/tests/components/filesize/test_sensor.py @@ -1,11 +1,9 @@ """The tests for the filesize sensor.""" import os -from homeassistant.components.filesize.const import DOMAIN from homeassistant.const import CONF_FILE_PATH, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_component import async_update_entity -from homeassistant.setup import async_setup_component from . import TEST_FILE, TEST_FILE_NAME, create_file @@ -71,23 +69,3 @@ async def test_state_unavailable( state = hass.states.get("sensor.file_txt_size") assert state.state == STATE_UNAVAILABLE - - -async def test_import_query(hass: HomeAssistant, tmpdir: str) -> None: - """Test import from yaml.""" - testfile = f"{tmpdir}/file.txt" - create_file(testfile) - hass.config.allowlist_external_dirs = {tmpdir} - config = { - "sensor": { - "platform": "filesize", - "file_paths": [testfile], - } - } - - assert await async_setup_component(hass, "sensor", config) - await hass.async_block_till_done() - - assert hass.config_entries.async_entries(DOMAIN) - data = hass.config_entries.async_entries(DOMAIN)[0].data - assert data[CONF_FILE_PATH] == testfile From 577b8cd97638124f93f8f4dc18809214d0240bdd Mon Sep 17 00:00:00 2001 From: Rudolf Offereins Date: Thu, 12 May 2022 12:12:47 +0200 Subject: [PATCH 377/930] Add Geocaching integration (#50284) Co-authored-by: Paulus Schoutsen Co-authored-by: Reinder Reinders Co-authored-by: Franck Nijhof --- .coveragerc | 5 + .strict-typing | 1 + CODEOWNERS | 2 + .../components/geocaching/__init__.py | 81 ++++++ .../components/geocaching/config_flow.py | 56 ++++ homeassistant/components/geocaching/const.py | 27 ++ .../components/geocaching/coordinator.py | 47 ++++ .../components/geocaching/manifest.json | 10 + homeassistant/components/geocaching/models.py | 9 + homeassistant/components/geocaching/oauth.py | 77 ++++++ homeassistant/components/geocaching/sensor.py | 126 +++++++++ .../components/geocaching/strings.json | 25 ++ .../geocaching/translations/en.json | 25 ++ homeassistant/generated/config_flows.py | 1 + mypy.ini | 11 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/geocaching/__init__.py | 5 + tests/components/geocaching/conftest.py | 50 ++++ .../components/geocaching/test_config_flow.py | 256 ++++++++++++++++++ 20 files changed, 820 insertions(+) create mode 100644 homeassistant/components/geocaching/__init__.py create mode 100644 homeassistant/components/geocaching/config_flow.py create mode 100644 homeassistant/components/geocaching/const.py create mode 100644 homeassistant/components/geocaching/coordinator.py create mode 100644 homeassistant/components/geocaching/manifest.json create mode 100644 homeassistant/components/geocaching/models.py create mode 100644 homeassistant/components/geocaching/oauth.py create mode 100644 homeassistant/components/geocaching/sensor.py create mode 100644 homeassistant/components/geocaching/strings.json create mode 100644 homeassistant/components/geocaching/translations/en.json create mode 100644 tests/components/geocaching/__init__.py create mode 100644 tests/components/geocaching/conftest.py create mode 100644 tests/components/geocaching/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 706122d0a07..78f63d30cbe 100644 --- a/.coveragerc +++ b/.coveragerc @@ -411,6 +411,11 @@ omit = homeassistant/components/garages_amsterdam/sensor.py homeassistant/components/gc100/* homeassistant/components/geniushub/* + homeassistant/components/geocaching/__init__.py + homeassistant/components/geocaching/const.py + homeassistant/components/geocaching/coordinator.py + homeassistant/components/geocaching/oauth.py + homeassistant/components/geocaching/sensor.py homeassistant/components/github/__init__.py homeassistant/components/github/coordinator.py homeassistant/components/github/sensor.py diff --git a/.strict-typing b/.strict-typing index f7264591c83..8c580dfa1aa 100644 --- a/.strict-typing +++ b/.strict-typing @@ -97,6 +97,7 @@ homeassistant.components.fronius.* homeassistant.components.frontend.* homeassistant.components.fritz.* homeassistant.components.geo_location.* +homeassistant.components.geocaching.* homeassistant.components.gios.* homeassistant.components.goalzero.* homeassistant.components.greeneye_monitor.* diff --git a/CODEOWNERS b/CODEOWNERS index 19450d86bba..8be3146b432 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -374,6 +374,8 @@ build.json @home-assistant/supervisor /tests/components/geo_location/ @home-assistant/core /homeassistant/components/geo_rss_events/ @exxamalte /tests/components/geo_rss_events/ @exxamalte +/homeassistant/components/geocaching/ @Sholofly @reinder83 +/tests/components/geocaching/ @Sholofly @reinder83 /homeassistant/components/geonetnz_quakes/ @exxamalte /tests/components/geonetnz_quakes/ @exxamalte /homeassistant/components/geonetnz_volcano/ @exxamalte diff --git a/homeassistant/components/geocaching/__init__.py b/homeassistant/components/geocaching/__init__.py new file mode 100644 index 00000000000..f04fbbd608f --- /dev/null +++ b/homeassistant/components/geocaching/__init__.py @@ -0,0 +1,81 @@ +"""The Geocaching integration.""" +import voluptuous as vol + +from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.config_entry_oauth2_flow import ( + OAuth2Session, + async_get_config_entry_implementation, +) +from homeassistant.helpers.typing import ConfigType + +from .config_flow import GeocachingFlowHandler +from .const import DOMAIN +from .coordinator import GeocachingDataUpdateCoordinator +from .oauth import GeocachingOAuth2Implementation + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + extra=vol.ALLOW_EXTRA, +) + +PLATFORMS = [Platform.SENSOR] + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the Geocaching component.""" + if DOMAIN not in config: + return True + + GeocachingFlowHandler.async_register_implementation( + hass, + GeocachingOAuth2Implementation( + hass, + client_id=config[DOMAIN][CONF_CLIENT_ID], + client_secret=config[DOMAIN][CONF_CLIENT_SECRET], + name="Geocaching", + ), + ) + + # When manual configuration is done, discover the integration. + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_INTEGRATION_DISCOVERY} + ) + ) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Geocaching from a config entry.""" + implementation = await async_get_config_entry_implementation(hass, entry) + + oauth_session = OAuth2Session(hass, entry, implementation) + coordinator = GeocachingDataUpdateCoordinator( + hass, entry=entry, session=oauth_session + ) + + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][entry.entry_id] = coordinator + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + del hass.data[DOMAIN][entry.entry_id] + return unload_ok diff --git a/homeassistant/components/geocaching/config_flow.py b/homeassistant/components/geocaching/config_flow.py new file mode 100644 index 00000000000..83c9ed17586 --- /dev/null +++ b/homeassistant/components/geocaching/config_flow.py @@ -0,0 +1,56 @@ +"""Config flow for Geocaching.""" +from __future__ import annotations + +import logging +from typing import Any + +from geocachingapi.geocachingapi import GeocachingApi + +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler + +from .const import DOMAIN, ENVIRONMENT + + +class GeocachingFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN): + """Config flow to handle Geocaching OAuth2 authentication.""" + + DOMAIN = DOMAIN + VERSION = 1 + + @property + def logger(self) -> logging.Logger: + """Return logger.""" + return logging.getLogger(__name__) + + async def async_step_reauth(self, user_input: dict[str, Any]) -> FlowResult: + """Perform reauth upon an API authentication error.""" + return await self.async_step_reauth_confirm(user_input=user_input) + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Dialog that informs the user that reauth is required.""" + if user_input is None: + return self.async_show_form(step_id="reauth_confirm") + return await self.async_step_user() + + async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult: + """Create an oauth config entry or update existing entry for reauth.""" + api = GeocachingApi( + environment=ENVIRONMENT, + token=data["token"]["access_token"], + session=async_get_clientsession(self.hass), + ) + status = await api.update() + if not status.user or not status.user.username: + return self.async_abort(reason="oauth_error") + + if existing_entry := await self.async_set_unique_id( + status.user.username.lower() + ): + self.hass.config_entries.async_update_entry(existing_entry, data=data) + await self.hass.config_entries.async_reload(existing_entry.entry_id) + return self.async_abort(reason="reauth_successful") + return self.async_create_entry(title=status.user.username, data=data) diff --git a/homeassistant/components/geocaching/const.py b/homeassistant/components/geocaching/const.py new file mode 100644 index 00000000000..13b42b318c0 --- /dev/null +++ b/homeassistant/components/geocaching/const.py @@ -0,0 +1,27 @@ +"""Constants for the Geocaching integration.""" +from __future__ import annotations + +from datetime import timedelta +import logging +from typing import Final + +from geocachingapi.models import GeocachingApiEnvironment + +from .models import GeocachingOAuthApiUrls + +DOMAIN: Final = "geocaching" +LOGGER = logging.getLogger(__package__) +UPDATE_INTERVAL = timedelta(hours=1) + +ENVIRONMENT_URLS = { + GeocachingApiEnvironment.Staging: GeocachingOAuthApiUrls( + authorize_url="https://staging.geocaching.com/oauth/authorize.aspx", + token_url="https://oauth-staging.geocaching.com/token", + ), + GeocachingApiEnvironment.Production: GeocachingOAuthApiUrls( + authorize_url="https://www.geocaching.com/oauth/authorize.aspx", + token_url="https://oauth.geocaching.com/token", + ), +} + +ENVIRONMENT = GeocachingApiEnvironment.Production diff --git a/homeassistant/components/geocaching/coordinator.py b/homeassistant/components/geocaching/coordinator.py new file mode 100644 index 00000000000..f02cccf544b --- /dev/null +++ b/homeassistant/components/geocaching/coordinator.py @@ -0,0 +1,47 @@ +"""Provides the Geocaching DataUpdateCoordinator.""" +from __future__ import annotations + +from geocachingapi.exceptions import GeocachingApiError +from geocachingapi.geocachingapi import GeocachingApi +from geocachingapi.models import GeocachingStatus + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DOMAIN, ENVIRONMENT, LOGGER, UPDATE_INTERVAL + + +class GeocachingDataUpdateCoordinator(DataUpdateCoordinator[GeocachingStatus]): + """Class to manage fetching Geocaching data from single endpoint.""" + + def __init__( + self, hass: HomeAssistant, *, entry: ConfigEntry, session: OAuth2Session + ) -> None: + """Initialize global Geocaching data updater.""" + self.session = session + self.entry = entry + + async def async_token_refresh() -> str: + await session.async_ensure_token_valid() + token = session.token["access_token"] + LOGGER.debug(str(token)) + return str(token) + + client_session = async_get_clientsession(hass) + self.geocaching = GeocachingApi( + environment=ENVIRONMENT, + token=session.token["access_token"], + session=client_session, + token_refresh_method=async_token_refresh, + ) + + super().__init__(hass, LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) + + async def _async_update_data(self) -> GeocachingStatus: + try: + return await self.geocaching.update() + except GeocachingApiError as error: + raise UpdateFailed(f"Invalid response from API: {error}") from error diff --git a/homeassistant/components/geocaching/manifest.json b/homeassistant/components/geocaching/manifest.json new file mode 100644 index 00000000000..683a23d474a --- /dev/null +++ b/homeassistant/components/geocaching/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "geocaching", + "name": "Geocaching", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/geocaching", + "requirements": ["geocachingapi==0.2.1"], + "dependencies": ["auth"], + "codeowners": ["@Sholofly", "@reinder83"], + "iot_class": "cloud_polling" +} diff --git a/homeassistant/components/geocaching/models.py b/homeassistant/components/geocaching/models.py new file mode 100644 index 00000000000..60ee4e05978 --- /dev/null +++ b/homeassistant/components/geocaching/models.py @@ -0,0 +1,9 @@ +"""Models for the Geocaching integration.""" +from typing import TypedDict + + +class GeocachingOAuthApiUrls(TypedDict): + """oAuth2 urls for a single environment.""" + + authorize_url: str + token_url: str diff --git a/homeassistant/components/geocaching/oauth.py b/homeassistant/components/geocaching/oauth.py new file mode 100644 index 00000000000..29371eb793c --- /dev/null +++ b/homeassistant/components/geocaching/oauth.py @@ -0,0 +1,77 @@ +"""oAuth2 functions and classes for Geocaching API integration.""" +from __future__ import annotations + +from typing import Any, cast + +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DOMAIN, ENVIRONMENT, ENVIRONMENT_URLS + + +class GeocachingOAuth2Implementation( + config_entry_oauth2_flow.LocalOAuth2Implementation +): + """Local OAuth2 implementation for Geocaching.""" + + def __init__( + self, hass: HomeAssistant, client_id: str, client_secret: str, name: str + ) -> None: + """Local Geocaching Oauth Implementation.""" + self._name = name + super().__init__( + hass=hass, + client_id=client_id, + client_secret=client_secret, + domain=DOMAIN, + authorize_url=ENVIRONMENT_URLS[ENVIRONMENT]["authorize_url"], + token_url=ENVIRONMENT_URLS[ENVIRONMENT]["token_url"], + ) + + @property + def name(self) -> str: + """Name of the implementation.""" + return f"{self._name}" + + @property + def extra_authorize_data(self) -> dict: + """Extra data that needs to be appended to the authorize url.""" + return {"scope": "*", "response_type": "code"} + + async def async_resolve_external_data(self, external_data: Any) -> dict: + """Initialize local Geocaching API auth implementation.""" + redirect_uri = external_data["state"]["redirect_uri"] + data = { + "grant_type": "authorization_code", + "code": external_data["code"], + "redirect_uri": redirect_uri, + } + token = await self._token_request(data) + # Store the redirect_uri (Needed for refreshing token, but not according to oAuth2 spec!) + token["redirect_uri"] = redirect_uri + return token + + async def _async_refresh_token(self, token: dict) -> dict: + """Refresh tokens.""" + data = { + "client_id": self.client_id, + "client_secret": self.client_secret, + "grant_type": "refresh_token", + "refresh_token": token["refresh_token"], + # Add previously stored redirect_uri (Mandatory, but not according to oAuth2 spec!) + "redirect_uri": token["redirect_uri"], + } + + new_token = await self._token_request(data) + return {**token, **new_token} + + async def _token_request(self, data: dict) -> dict: + """Make a token request.""" + data["client_id"] = self.client_id + if self.client_secret is not None: + data["client_secret"] = self.client_secret + session = async_get_clientsession(self.hass) + resp = await session.post(ENVIRONMENT_URLS[ENVIRONMENT]["token_url"], data=data) + resp.raise_for_status() + return cast(dict, await resp.json()) diff --git a/homeassistant/components/geocaching/sensor.py b/homeassistant/components/geocaching/sensor.py new file mode 100644 index 00000000000..82353a81484 --- /dev/null +++ b/homeassistant/components/geocaching/sensor.py @@ -0,0 +1,126 @@ +"""Platform for sensor integration.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import cast + +from geocachingapi.models import GeocachingStatus + +from homeassistant.components.sensor import SensorEntity, SensorEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo, EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN +from .coordinator import GeocachingDataUpdateCoordinator + + +@dataclass +class GeocachingRequiredKeysMixin: + """Mixin for required keys.""" + + value_fn: Callable[[GeocachingStatus], str | int | None] + + +@dataclass +class GeocachingSensorEntityDescription( + SensorEntityDescription, GeocachingRequiredKeysMixin +): + """Define Sensor entity description class.""" + + +SENSORS: tuple[GeocachingSensorEntityDescription, ...] = ( + GeocachingSensorEntityDescription( + key="username", + name="username", + icon="mdi:account", + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda status: status.user.username, + ), + GeocachingSensorEntityDescription( + key="find_count", + name="Total finds", + icon="mdi:notebook-edit-outline", + native_unit_of_measurement="caches", + value_fn=lambda status: status.user.find_count, + ), + GeocachingSensorEntityDescription( + key="hide_count", + name="Total hides", + icon="mdi:eye-off-outline", + native_unit_of_measurement="caches", + entity_registry_visible_default=False, + value_fn=lambda status: status.user.hide_count, + ), + GeocachingSensorEntityDescription( + key="favorite_points", + name="Favorite points", + icon="mdi:heart-outline", + native_unit_of_measurement="points", + entity_registry_visible_default=False, + value_fn=lambda status: status.user.favorite_points, + ), + GeocachingSensorEntityDescription( + key="souvenir_count", + name="Total souvenirs", + icon="mdi:license", + native_unit_of_measurement="souvenirs", + value_fn=lambda status: status.user.souvenir_count, + ), + GeocachingSensorEntityDescription( + key="awarded_favorite_points", + name="Awarded favorite points", + icon="mdi:heart", + native_unit_of_measurement="points", + entity_registry_visible_default=False, + value_fn=lambda status: status.user.awarded_favorite_points, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up a Geocaching sensor entry.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + GeocachingSensor(coordinator, description) for description in SENSORS + ) + + +class GeocachingSensor( + CoordinatorEntity[GeocachingDataUpdateCoordinator], SensorEntity +): + """Representation of a Sensor.""" + + entity_description: GeocachingSensorEntityDescription + + def __init__( + self, + coordinator: GeocachingDataUpdateCoordinator, + description: GeocachingSensorEntityDescription, + ) -> None: + """Initialize the Geocaching sensor.""" + super().__init__(coordinator) + self.entity_description = description + self._attr_name = ( + f"Geocaching {coordinator.data.user.username} {description.name}" + ) + self._attr_unique_id = ( + f"{coordinator.data.user.reference_code}_{description.key}" + ) + self._attr_device_info = DeviceInfo( + name=f"Geocaching {coordinator.data.user.username}", + identifiers={(DOMAIN, cast(str, coordinator.data.user.reference_code))}, + entry_type=DeviceEntryType.SERVICE, + manufacturer="Groundspeak, Inc.", + ) + + @property + def native_value(self) -> str | int | None: + """Return the state of the sensor.""" + return self.entity_description.value_fn(self.coordinator.data) diff --git a/homeassistant/components/geocaching/strings.json b/homeassistant/components/geocaching/strings.json new file mode 100644 index 00000000000..7c8547805d1 --- /dev/null +++ b/homeassistant/components/geocaching/strings.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "pick_implementation": { + "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" + }, + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "The Geocaching integration needs to re-authenticate your account" + } + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", + "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", + "oauth_error": "[%key:common::config_flow::abort::oauth2_error%]", + "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", + "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", + "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + }, + "create_entry": { + "default": "[%key:common::config_flow::create_entry::authenticated%]" + } + } +} diff --git a/homeassistant/components/geocaching/translations/en.json b/homeassistant/components/geocaching/translations/en.json new file mode 100644 index 00000000000..7f76bce4f3d --- /dev/null +++ b/homeassistant/components/geocaching/translations/en.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Account is already configured", + "already_in_progress": "Configuration flow is already in progress", + "authorize_url_timeout": "Timeout generating authorize URL.", + "missing_configuration": "The component is not configured. Please follow the documentation.", + "no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", + "oauth_error": "Received invalid token data.", + "reauth_successful": "Re-authentication was successful" + }, + "create_entry": { + "default": "Successfully authenticated" + }, + "step": { + "pick_implementation": { + "title": "Pick Authentication Method" + }, + "reauth_confirm": { + "description": "The Geocaching integration needs to re-authenticate your account", + "title": "Reauthenticate Integration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 4bcfa0dbbef..70451b22001 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -121,6 +121,7 @@ FLOWS = { "garages_amsterdam", "gdacs", "generic", + "geocaching", "geofency", "geonetnz_quakes", "geonetnz_volcano", diff --git a/mypy.ini b/mypy.ini index ab4ba77c98b..532c4526372 100644 --- a/mypy.ini +++ b/mypy.ini @@ -830,6 +830,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.geocaching.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.gios.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index c2fc83e847b..3ecdceca859 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -694,6 +694,9 @@ gcal-sync==0.7.1 # homeassistant.components.geniushub geniushub-client==0.6.30 +# homeassistant.components.geocaching +geocachingapi==0.2.1 + # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2fe34d6a33e..c23b9d58593 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -488,6 +488,9 @@ garages-amsterdam==3.0.0 # homeassistant.components.google gcal-sync==0.7.1 +# homeassistant.components.geocaching +geocachingapi==0.2.1 + # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 diff --git a/tests/components/geocaching/__init__.py b/tests/components/geocaching/__init__.py new file mode 100644 index 00000000000..8bc72aa3799 --- /dev/null +++ b/tests/components/geocaching/__init__.py @@ -0,0 +1,5 @@ +"""Tests for the Geocaching integration.""" + +CLIENT_ID = "1234" +CLIENT_SECRET = "5678" +REDIRECT_URI = "https://example.com/auth/external/callback" diff --git a/tests/components/geocaching/conftest.py b/tests/components/geocaching/conftest.py new file mode 100644 index 00000000000..f59f428118e --- /dev/null +++ b/tests/components/geocaching/conftest.py @@ -0,0 +1,50 @@ +"""Fixtures for the Geocaching integration tests.""" +from __future__ import annotations + +from collections.abc import Generator +from unittest.mock import AsyncMock, MagicMock, patch + +from geocachingapi import GeocachingStatus +import pytest + +from homeassistant.components.geocaching.const import DOMAIN + +from tests.common import MockConfigEntry + + +@pytest.fixture +def mock_config_entry() -> MockConfigEntry: + """Return the default mocked config entry.""" + return MockConfigEntry( + title="1234AB 1", + domain=DOMAIN, + data={ + "id": "mock_user", + "auth_implementation": DOMAIN, + }, + unique_id="mock_user", + ) + + +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock, None, None]: + """Mock setting up a config entry.""" + with patch( + "homeassistant.components.geocaching.async_setup_entry", return_value=True + ) as mock_setup: + yield mock_setup + + +@pytest.fixture +def mock_geocaching_config_flow() -> Generator[None, MagicMock, None]: + """Return a mocked Geocaching API client.""" + + mock_status = GeocachingStatus() + mock_status.user.username = "mock_user" + + with patch( + "homeassistant.components.geocaching.config_flow.GeocachingApi", autospec=True + ) as geocaching_mock: + geocachingapi = geocaching_mock.return_value + geocachingapi.update.return_value = mock_status + yield geocachingapi diff --git a/tests/components/geocaching/test_config_flow.py b/tests/components/geocaching/test_config_flow.py new file mode 100644 index 00000000000..0f5d182b2db --- /dev/null +++ b/tests/components/geocaching/test_config_flow.py @@ -0,0 +1,256 @@ +"""Test the Geocaching config flow.""" +from collections.abc import Awaitable, Callable +from http import HTTPStatus +from unittest.mock import MagicMock + +from aiohttp.test_utils import TestClient + +from homeassistant.components.geocaching.const import ( + DOMAIN, + ENVIRONMENT, + ENVIRONMENT_URLS, +) +from homeassistant.config_entries import ( + DEFAULT_DISCOVERY_UNIQUE_ID, + SOURCE_INTEGRATION_DISCOVERY, + SOURCE_REAUTH, + SOURCE_USER, +) +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_EXTERNAL_STEP +from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.setup import async_setup_component + +from . import CLIENT_ID, CLIENT_SECRET, REDIRECT_URI + +from tests.common import MockConfigEntry +from tests.test_util.aiohttp import AiohttpClientMocker + +CURRENT_ENVIRONMENT_URLS = ENVIRONMENT_URLS[ENVIRONMENT] + + +async def setup_geocaching_component(hass: HomeAssistant) -> bool: + """Set up the Geocaching component.""" + return await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: { + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + }, + }, + ) + + +async def test_full_flow( + hass: HomeAssistant, + hass_client_no_auth: Callable[[], Awaitable[TestClient]], + aioclient_mock: AiohttpClientMocker, + current_request_with_host: None, + mock_geocaching_config_flow: MagicMock, + mock_setup_entry: MagicMock, +) -> None: + """Check full flow.""" + assert await setup_geocaching_component(hass) + + # Ensure integration is discovered when manual implementation is configured + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + assert "context" in flows[0] + assert flows[0]["context"]["source"] == SOURCE_INTEGRATION_DISCOVERY + assert flows[0]["context"]["unique_id"] == DEFAULT_DISCOVERY_UNIQUE_ID + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert "flow_id" in result + + # pylint: disable=protected-access + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URI, + }, + ) + + assert result.get("type") == RESULT_TYPE_EXTERNAL_STEP + assert result.get("step_id") == "auth" + assert result.get("url") == ( + f"{CURRENT_ENVIRONMENT_URLS['authorize_url']}?response_type=code&client_id={CLIENT_ID}" + f"&redirect_uri={REDIRECT_URI}" + f"&state={state}&scope=*" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == HTTPStatus.OK + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + CURRENT_ENVIRONMENT_URLS["token_url"], + json={ + "access_token": "mock-access-token", + "token_type": "bearer", + "expires_in": 3599, + "refresh_token": "mock-refresh_token", + }, + ) + + await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_existing_entry( + hass: HomeAssistant, + hass_client_no_auth: Callable[[], Awaitable[TestClient]], + aioclient_mock: AiohttpClientMocker, + current_request_with_host: None, + mock_geocaching_config_flow: MagicMock, + mock_setup_entry: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Check existing entry.""" + assert await setup_geocaching_component(hass) + mock_config_entry.add_to_hass(hass) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert "flow_id" in result + # pylint: disable=protected-access + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URI, + }, + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == HTTPStatus.OK + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + CURRENT_ENVIRONMENT_URLS["token_url"], + json={ + "access_token": "mock-access-token", + "token_type": "bearer", + "expires_in": 3599, + "refresh_token": "mock-refresh_token", + }, + ) + + await hass.config_entries.flow.async_configure(result["flow_id"]) + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + +async def test_oauth_error( + hass: HomeAssistant, + hass_client_no_auth: Callable[[], Awaitable[TestClient]], + aioclient_mock: AiohttpClientMocker, + current_request_with_host: None, + mock_geocaching_config_flow: MagicMock, + mock_setup_entry: MagicMock, +) -> None: + """Check if aborted when oauth error occurs.""" + assert await setup_geocaching_component(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert "flow_id" in result + + # pylint: disable=protected-access + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": REDIRECT_URI, + }, + ) + assert result.get("type") == RESULT_TYPE_EXTERNAL_STEP + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == HTTPStatus.OK + + # No user information is returned from API + mock_geocaching_config_flow.update.return_value.user = None + + aioclient_mock.post( + CURRENT_ENVIRONMENT_URLS["token_url"], + json={ + "access_token": "mock-access-token", + "token_type": "bearer", + "expires_in": 3599, + "refresh_token": "mock-refresh_token", + }, + ) + + result2 = await hass.config_entries.flow.async_configure(result["flow_id"]) + assert result2.get("type") == RESULT_TYPE_ABORT + assert result2.get("reason") == "oauth_error" + + assert len(hass.config_entries.async_entries(DOMAIN)) == 0 + assert len(mock_setup_entry.mock_calls) == 0 + + +async def test_reauthentication( + hass: HomeAssistant, + hass_client_no_auth: Callable[[], Awaitable[TestClient]], + aioclient_mock: AiohttpClientMocker, + current_request_with_host: None, + mock_geocaching_config_flow: MagicMock, + mock_setup_entry: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test Geocaching reauthentication.""" + mock_config_entry.add_to_hass(hass) + assert await setup_geocaching_component(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_REAUTH} + ) + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + assert "flow_id" in flows[0] + + result = await hass.config_entries.flow.async_configure(flows[0]["flow_id"], {}) + assert "flow_id" in result + + # pylint: disable=protected-access + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == HTTPStatus.OK + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + CURRENT_ENVIRONMENT_URLS["token_url"], + json={ + "access_token": "mock-access-token", + "token_type": "bearer", + "expires_in": 3599, + "refresh_token": "mock-refresh_token", + }, + ) + + await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert len(mock_setup_entry.mock_calls) == 1 From bf314970ea8f89f32c25872f381530544e61d4f0 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 12 May 2022 13:14:33 +0200 Subject: [PATCH 378/930] Remove username entity from Geocaching (#71728) --- homeassistant/components/geocaching/sensor.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/homeassistant/components/geocaching/sensor.py b/homeassistant/components/geocaching/sensor.py index 82353a81484..0c719c463a4 100644 --- a/homeassistant/components/geocaching/sensor.py +++ b/homeassistant/components/geocaching/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import SensorEntity, SensorEntityDescriptio from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType -from homeassistant.helpers.entity import DeviceInfo, EntityCategory +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -34,13 +34,6 @@ class GeocachingSensorEntityDescription( SENSORS: tuple[GeocachingSensorEntityDescription, ...] = ( - GeocachingSensorEntityDescription( - key="username", - name="username", - icon="mdi:account", - entity_category=EntityCategory.DIAGNOSTIC, - value_fn=lambda status: status.user.username, - ), GeocachingSensorEntityDescription( key="find_count", name="Total finds", From 39313057c4f8384cc44bc95a8262a49c986777c1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 May 2022 23:14:52 +1200 Subject: [PATCH 379/930] Add amperage limit number to JuiceNet (#71716) * Add amperage limit number to JuiceNet * coverage exception * Use mixin dataclass --- .coveragerc | 1 + homeassistant/components/juicenet/__init__.py | 2 +- homeassistant/components/juicenet/entity.py | 15 ++- homeassistant/components/juicenet/number.py | 98 +++++++++++++++++++ 4 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/juicenet/number.py diff --git a/.coveragerc b/.coveragerc index 78f63d30cbe..922c13e550c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -579,6 +579,7 @@ omit = homeassistant/components/juicenet/const.py homeassistant/components/juicenet/device.py homeassistant/components/juicenet/entity.py + homeassistant/components/juicenet/number.py homeassistant/components/juicenet/sensor.py homeassistant/components/juicenet/switch.py homeassistant/components/kaiterra/* diff --git a/homeassistant/components/juicenet/__init__.py b/homeassistant/components/juicenet/__init__.py index 92d8de8bc0a..60377d7e85b 100644 --- a/homeassistant/components/juicenet/__init__.py +++ b/homeassistant/components/juicenet/__init__.py @@ -20,7 +20,7 @@ from .device import JuiceNetApi _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.NUMBER] CONFIG_SCHEMA = vol.Schema( vol.All( diff --git a/homeassistant/components/juicenet/entity.py b/homeassistant/components/juicenet/entity.py index 4b4e5764a5e..c0151a0cd00 100644 --- a/homeassistant/components/juicenet/entity.py +++ b/homeassistant/components/juicenet/entity.py @@ -1,7 +1,12 @@ """Adapter to wrap the pyjuicenet api for home assistant.""" +from pyjuicenet import Charger + from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) from .const import DOMAIN @@ -9,16 +14,18 @@ from .const import DOMAIN class JuiceNetDevice(CoordinatorEntity): """Represent a base JuiceNet device.""" - def __init__(self, device, sensor_type, coordinator): + def __init__( + self, device: Charger, key: str, coordinator: DataUpdateCoordinator + ) -> None: """Initialise the sensor.""" super().__init__(coordinator) self.device = device - self.type = sensor_type + self.key = key @property def unique_id(self): """Return a unique ID.""" - return f"{self.device.id}-{self.type}" + return f"{self.device.id}-{self.key}" @property def device_info(self) -> DeviceInfo: diff --git a/homeassistant/components/juicenet/number.py b/homeassistant/components/juicenet/number.py new file mode 100644 index 00000000000..24b0ba4f42b --- /dev/null +++ b/homeassistant/components/juicenet/number.py @@ -0,0 +1,98 @@ +"""Support for controlling juicenet/juicepoint/juicebox based EVSE numbers.""" +from __future__ import annotations + +from dataclasses import dataclass + +from pyjuicenet import Api, Charger + +from homeassistant.components.number import NumberEntity, NumberEntityDescription +from homeassistant.components.number.const import DEFAULT_MAX_VALUE +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import DOMAIN, JUICENET_API, JUICENET_COORDINATOR +from .entity import JuiceNetDevice + + +@dataclass +class JuiceNetNumberEntityDescriptionMixin: + """Mixin for required keys.""" + + setter_key: str + + +@dataclass +class JuiceNetNumberEntityDescription( + NumberEntityDescription, JuiceNetNumberEntityDescriptionMixin +): + """An entity description for a JuiceNetNumber.""" + + max_value_key: str | None = None + + +NUMBER_TYPES: tuple[JuiceNetNumberEntityDescription, ...] = ( + JuiceNetNumberEntityDescription( + name="Amperage Limit", + key="current_charging_amperage_limit", + min_value=6, + max_value_key="max_charging_amperage", + step=1, + setter_key="set_charging_amperage_limit", + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the JuiceNet Numbers.""" + juicenet_data = hass.data[DOMAIN][config_entry.entry_id] + api: Api = juicenet_data[JUICENET_API] + coordinator = juicenet_data[JUICENET_COORDINATOR] + + entities = [ + JuiceNetNumber(device, description, coordinator) + for device in api.devices + for description in NUMBER_TYPES + ] + async_add_entities(entities) + + +class JuiceNetNumber(JuiceNetDevice, NumberEntity): + """Implementation of a JuiceNet number.""" + + entity_description: JuiceNetNumberEntityDescription + + def __init__( + self, + device: Charger, + description: JuiceNetNumberEntityDescription, + coordinator: DataUpdateCoordinator, + ) -> None: + """Initialise the number.""" + super().__init__(device, description.key, coordinator) + self.entity_description = description + + self._attr_name = f"{self.device.name} {description.name}" + + @property + def value(self) -> float | None: + """Return the value of the entity.""" + return getattr(self.device, self.entity_description.key, None) + + @property + def max_value(self) -> float: + """Return the maximum value.""" + if self.entity_description.max_value_key is not None: + return getattr(self.device, self.entity_description.max_value_key) + if self.entity_description.max_value is not None: + return self.entity_description.max_value + return DEFAULT_MAX_VALUE + + async def async_set_value(self, value: float) -> None: + """Update the current value.""" + await getattr(self.device, self.entity_description.setter_key)(value) From 8f50a70ff584e0e68425afac9e0ddb6f9c21c02f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 14:09:43 +0200 Subject: [PATCH 380/930] Tweak template alarm tests (#71730) --- .../template/test_alarm_control_panel.py | 351 +++++------------- 1 file changed, 93 insertions(+), 258 deletions(-) diff --git a/tests/components/template/test_alarm_control_panel.py b/tests/components/template/test_alarm_control_panel.py index cd29794db8d..669effca3e9 100644 --- a/tests/components/template/test_alarm_control_panel.py +++ b/tests/components/template/test_alarm_control_panel.py @@ -1,7 +1,12 @@ """The tests for the Template alarm control panel platform.""" import pytest +from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN from homeassistant.const import ( + ATTR_DOMAIN, + ATTR_ENTITY_ID, + ATTR_SERVICE_DATA, + EVENT_CALL_SERVICE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, @@ -10,13 +15,62 @@ from homeassistant.const import ( STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, ) - -from tests.components.alarm_control_panel import common +from homeassistant.core import callback TEMPLATE_NAME = "alarm_control_panel.test_template_panel" PANEL_NAME = "alarm_control_panel.test" +@pytest.fixture +def service_calls(hass): + """Track service call events for alarm_control_panel.test.""" + events = [] + entity_id = "alarm_control_panel.test" + + @callback + def capture_events(event): + print(event.data) + if event.data[ATTR_DOMAIN] != ALARM_DOMAIN: + return + if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: + return + events.append(event) + + hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) + + return events + + +OPTIMISTIC_TEMPLATE_ALARM_CONFIG = { + "arm_away": { + "service": "alarm_control_panel.alarm_arm_away", + "entity_id": "alarm_control_panel.test", + "data": {"code": "1234"}, + }, + "arm_home": { + "service": "alarm_control_panel.alarm_arm_home", + "entity_id": "alarm_control_panel.test", + "data": {"code": "1234"}, + }, + "arm_night": { + "service": "alarm_control_panel.alarm_arm_night", + "entity_id": "alarm_control_panel.test", + "data": {"code": "1234"}, + }, + "disarm": { + "service": "alarm_control_panel.alarm_disarm", + "entity_id": "alarm_control_panel.test", + "data": {"code": "1234"}, + }, +} + + +TEMPLATE_ALARM_CONFIG = { + "value_template": "{{ states('alarm_control_panel.test') }}", + **OPTIMISTIC_TEMPLATE_ALARM_CONFIG, +} + + @pytest.mark.parametrize("count,domain", [(1, "alarm_control_panel")]) @pytest.mark.parametrize( "config", @@ -24,31 +78,7 @@ PANEL_NAME = "alarm_control_panel.test" { "alarm_control_panel": { "platform": "template", - "panels": { - "test_template_panel": { - "value_template": "{{ states('alarm_control_panel.test') }}", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, + "panels": {"test_template_panel": TEMPLATE_ALARM_CONFIG}, } }, ], @@ -83,30 +113,7 @@ async def test_template_state_text(hass, start_ha): { "alarm_control_panel": { "platform": "template", - "panels": { - "test_template_panel": { - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, + "panels": {"test_template_panel": OPTIMISTIC_TEMPLATE_ALARM_CONFIG}, } }, ], @@ -118,13 +125,15 @@ async def test_optimistic_states(hass, start_ha): await hass.async_block_till_done() assert state.state == "unknown" - for func, set_state in [ - (common.async_alarm_arm_away, STATE_ALARM_ARMED_AWAY), - (common.async_alarm_arm_home, STATE_ALARM_ARMED_HOME), - (common.async_alarm_arm_night, STATE_ALARM_ARMED_NIGHT), - (common.async_alarm_disarm, STATE_ALARM_DISARMED), + for service, set_state in [ + ("alarm_arm_away", STATE_ALARM_ARMED_AWAY), + ("alarm_arm_home", STATE_ALARM_ARMED_HOME), + ("alarm_arm_night", STATE_ALARM_ARMED_NIGHT), + ("alarm_disarm", STATE_ALARM_DISARMED), ]: - await func(hass, entity_id=TEMPLATE_NAME) + await hass.services.async_call( + ALARM_DOMAIN, service, {"entity_id": TEMPLATE_NAME}, blocking=True + ) await hass.async_block_till_done() assert hass.states.get(TEMPLATE_NAME).state == set_state @@ -140,26 +149,7 @@ async def test_optimistic_states(hass, start_ha): "panels": { "test_template_panel": { "value_template": "{% if blah %}", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, + **OPTIMISTIC_TEMPLATE_ALARM_CONFIG, } }, } @@ -173,26 +163,7 @@ async def test_optimistic_states(hass, start_ha): "panels": { "bad name here": { "value_template": "disarmed", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, + **OPTIMISTIC_TEMPLATE_ALARM_CONFIG, } }, } @@ -221,26 +192,7 @@ async def test_optimistic_states(hass, start_ha): "panels": { "test_template_panel": { "value_template": "disarmed", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, + **OPTIMISTIC_TEMPLATE_ALARM_CONFIG, "code_format": "bad_format", } }, @@ -267,26 +219,7 @@ async def test_template_syntax_error(hass, msg, start_ha, caplog_setup_text): "test_template_panel": { "name": "Template Alarm Panel", "value_template": "disarmed", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_away", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_night", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, + **OPTIMISTIC_TEMPLATE_ALARM_CONFIG, } }, } @@ -302,131 +235,33 @@ async def test_name(hass, start_ha): @pytest.mark.parametrize("count,domain", [(1, "alarm_control_panel")]) @pytest.mark.parametrize( - "config,func", + "config", [ - ( - { - "alarm_control_panel": { - "platform": "template", - "panels": { - "test_template_panel": { - "value_template": "{{ states('alarm_control_panel.test') }}", - "arm_away": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_home": {"service": "test.automation"}, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, - } - }, - common.async_alarm_arm_home, - ), - ( - { - "alarm_control_panel": { - "platform": "template", - "panels": { - "test_template_panel": { - "value_template": "{{ states('alarm_control_panel.test') }}", - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_away": {"service": "test.automation"}, - "arm_night": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, - }, - }, - common.async_alarm_arm_away, - ), - ( - { - "alarm_control_panel": { - "platform": "template", - "panels": { - "test_template_panel": { - "value_template": "{{ states('alarm_control_panel.test') }}", - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": {"service": "test.automation"}, - "arm_away": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, - } - }, - common.async_alarm_arm_night, - ), - ( - { - "alarm_control_panel": { - "platform": "template", - "panels": { - "test_template_panel": { - "value_template": "{{ states('alarm_control_panel.test') }}", - "arm_home": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "disarm": {"service": "test.automation"}, - "arm_away": { - "service": "alarm_control_panel.alarm_arm_home", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - "arm_night": { - "service": "alarm_control_panel.alarm_disarm", - "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, - }, - } - }, - } - }, - common.async_alarm_disarm, - ), + { + "alarm_control_panel": { + "platform": "template", + "panels": {"test_template_panel": TEMPLATE_ALARM_CONFIG}, + } + }, ], ) -async def test_arm_home_action(hass, func, start_ha, calls): - """Test arm home action.""" - await func(hass, entity_id=TEMPLATE_NAME) +@pytest.mark.parametrize( + "service", + [ + "alarm_arm_home", + "alarm_arm_away", + "alarm_arm_night", + "alarm_disarm", + ], +) +async def test_actions(hass, service, start_ha, service_calls): + """Test alarm actions.""" + await hass.services.async_call( + ALARM_DOMAIN, service, {"entity_id": TEMPLATE_NAME}, blocking=True + ) await hass.async_block_till_done() - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[0].data["service"] == service @pytest.mark.parametrize("count,domain", [(1, "alarm_control_panel")]) From a0f741778aa561c30962648206db09e292be8261 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 12 May 2022 14:12:21 +0200 Subject: [PATCH 381/930] Use HVACAction in mqtt (#71726) --- homeassistant/components/mqtt/climate.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index deb1021b9d7..c095054ae9b 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -15,7 +15,6 @@ from homeassistant.components.climate.const import ( ATTR_HVAC_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, - CURRENT_HVAC_ACTIONS, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, FAN_AUTO, @@ -529,21 +528,23 @@ class MqttClimate(MqttEntity, ClimateEntity): def handle_action_received(msg): """Handle receiving action via MQTT.""" payload = render_template(msg, CONF_ACTION_TEMPLATE) - if payload in CURRENT_HVAC_ACTIONS: - self._action = payload - self.async_write_ha_state() - elif not payload or payload == PAYLOAD_NONE: + if not payload or payload == PAYLOAD_NONE: _LOGGER.debug( "Invalid %s action: %s, ignoring", - CURRENT_HVAC_ACTIONS, + [e.value for e in HVACAction], payload, ) - else: + return + try: + self._action = HVACAction(payload) + except ValueError: _LOGGER.warning( "Invalid %s action: %s", - CURRENT_HVAC_ACTIONS, + [e.value for e in HVACAction], payload, ) + return + self.async_write_ha_state() add_subscription(topics, CONF_ACTION_TOPIC, handle_action_received) From 5a7624bfd49798a6e316f3e7215f4457c449da7b Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Thu, 12 May 2022 15:19:50 +0200 Subject: [PATCH 382/930] Bump plugwise to v0.18.2 (#71731) --- homeassistant/components/plugwise/climate.py | 6 +- homeassistant/components/plugwise/const.py | 44 ++-- homeassistant/components/plugwise/entity.py | 4 +- homeassistant/components/plugwise/gateway.py | 2 +- .../components/plugwise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../all_data.json | 193 +++++++----------- .../fixtures/anna_heatpump/all_data.json | 27 +-- .../fixtures/p1v3_full_option/all_data.json | 6 +- .../fixtures/stretch_v31/all_data.json | 71 +++---- tests/components/plugwise/test_diagnostics.py | 193 +++++++----------- 12 files changed, 210 insertions(+), 342 deletions(-) diff --git a/homeassistant/components/plugwise/climate.py b/homeassistant/components/plugwise/climate.py index 91a56d09ee5..09f5181090c 100644 --- a/homeassistant/components/plugwise/climate.py +++ b/homeassistant/components/plugwise/climate.py @@ -31,7 +31,7 @@ async def async_setup_entry( async_add_entities( PlugwiseClimateEntity(coordinator, device_id) for device_id, device in coordinator.data.devices.items() - if device["class"] in THERMOSTAT_CLASSES + if device["dev_class"] in THERMOSTAT_CLASSES ) @@ -53,9 +53,9 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity): # Determine preset modes self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE - if presets := self.device.get("presets"): + if presets := self.device.get("preset_modes"): self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE - self._attr_preset_modes = list(presets) + self._attr_preset_modes = presets # Determine hvac modes and current hvac mode self._attr_hvac_modes = [HVACMode.HEAT] diff --git a/homeassistant/components/plugwise/const.py b/homeassistant/components/plugwise/const.py index 3bcad0b88aa..63bd2a6d8f1 100644 --- a/homeassistant/components/plugwise/const.py +++ b/homeassistant/components/plugwise/const.py @@ -1,51 +1,53 @@ """Constants for Plugwise component.""" +from __future__ import annotations + from datetime import timedelta import logging +from typing import Final from homeassistant.const import Platform -DOMAIN = "plugwise" +DOMAIN: Final = "plugwise" LOGGER = logging.getLogger(__package__) -API = "api" -FLOW_SMILE = "smile (Adam/Anna/P1)" -FLOW_STRETCH = "stretch (Stretch)" -FLOW_TYPE = "flow_type" -GATEWAY = "gateway" -PW_TYPE = "plugwise_type" -SMILE = "smile" -STRETCH = "stretch" -STRETCH_USERNAME = "stretch" -UNIT_LUMEN = "lm" +API: Final = "api" +FLOW_SMILE: Final = "smile (Adam/Anna/P1)" +FLOW_STRETCH: Final = "stretch (Stretch)" +FLOW_TYPE: Final = "flow_type" +GATEWAY: Final = "gateway" +PW_TYPE: Final = "plugwise_type" +SMILE: Final = "smile" +STRETCH: Final = "stretch" +STRETCH_USERNAME: Final = "stretch" +UNIT_LUMEN: Final = "lm" -PLATFORMS_GATEWAY = [ +PLATFORMS_GATEWAY: Final[list[str]] = [ Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.SENSOR, - Platform.SWITCH, Platform.SELECT, + Platform.SWITCH, ] -ZEROCONF_MAP = { +ZEROCONF_MAP: Final[dict[str, str]] = { "smile": "P1", "smile_thermo": "Anna", "smile_open_therm": "Adam", "stretch": "Stretch", } - # Default directives -DEFAULT_MAX_TEMP = 30 -DEFAULT_MIN_TEMP = 4 -DEFAULT_PORT = 80 -DEFAULT_SCAN_INTERVAL = { +DEFAULT_MAX_TEMP: Final = 30 +DEFAULT_MIN_TEMP: Final = 4 +DEFAULT_PORT: Final = 80 +DEFAULT_SCAN_INTERVAL: Final[dict[str, timedelta]] = { "power": timedelta(seconds=10), "stretch": timedelta(seconds=60), "thermostat": timedelta(seconds=60), } -DEFAULT_USERNAME = "smile" +DEFAULT_USERNAME: Final = "smile" -THERMOSTAT_CLASSES = [ +THERMOSTAT_CLASSES: Final[list[str]] = [ "thermostat", "thermostatic_radiator_valve", "zone_thermometer", diff --git a/homeassistant/components/plugwise/entity.py b/homeassistant/components/plugwise/entity.py index b0896c3cd6d..491eb7c7db8 100644 --- a/homeassistant/components/plugwise/entity.py +++ b/homeassistant/components/plugwise/entity.py @@ -45,8 +45,8 @@ class PlugwiseEntity(CoordinatorEntity[PlugwiseDataUpdateCoordinator]): manufacturer=data.get("vendor"), model=data.get("model"), name=f"Smile {coordinator.data.gateway['smile_name']}", - sw_version=data.get("fw"), - hw_version=data.get("hw"), + sw_version=data.get("firmware"), + hw_version=data.get("hardware"), ) if device_id != coordinator.data.gateway["gateway_id"]: diff --git a/homeassistant/components/plugwise/gateway.py b/homeassistant/components/plugwise/gateway.py index 648765155e4..7eb2b1371d5 100644 --- a/homeassistant/components/plugwise/gateway.py +++ b/homeassistant/components/plugwise/gateway.py @@ -113,7 +113,7 @@ def migrate_sensor_entities( # Migrating opentherm_outdoor_temperature to opentherm_outdoor_air_temperature sensor for device_id, device in coordinator.data.devices.items(): - if device["class"] != "heater_central": + if device["dev_class"] != "heater_central": continue old_unique_id = f"{device_id}-outdoor_temperature" diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index 68ed6d65647..abf6f27b3fa 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.17.3"], + "requirements": ["plugwise==0.18.2"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 3ecdceca859..ac6727a95aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1242,7 +1242,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.17.3 +plugwise==0.18.2 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c23b9d58593..3289356d23a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -838,7 +838,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.17.3 +plugwise==0.18.2 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json b/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json index 4c4776f4063..85e5abb0b17 100644 --- a/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json +++ b/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json @@ -12,26 +12,19 @@ }, { "df4a4a8169904cdb9c03d61a21f42140": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "12493538af164a409c6a1c79e38afe1c", - "mac_address": null, "model": "Lisa", "name": "Zone Lisa Bios", + "zigbee_mac_address": "ABCD012345670A06", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "away", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0] - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -41,7 +34,7 @@ ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, + "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 16.5, @@ -50,13 +43,13 @@ } }, "b310b72a0e354bfab43089919b9a88bf": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": null, "model": "Tom/Floor", "name": "Floor kraan", + "zigbee_mac_address": "ABCD012345670A02", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -69,13 +62,13 @@ } }, "a2c3583e0a6349358998b760cea82d2a": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "12493538af164a409c6a1c79e38afe1c", - "mac_address": null, "model": "Tom/Floor", "name": "Bios Cv Thermostatic Radiator ", + "zigbee_mac_address": "ABCD012345670A09", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -89,26 +82,19 @@ } }, "b59bcebaf94b499ea7d46e4a66fb62d8": { - "class": "zone_thermostat", - "fw": "2016-08-02T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-08-02T02:00:00+02:00", + "hardware": "255", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": null, "model": "Lisa", "name": "Zone Lisa WK", + "zigbee_mac_address": "ABCD012345670A07", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "home", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0] - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -118,7 +104,7 @@ ], "selected_schedule": "GF7 Woonkamer", "last_used": "GF7 Woonkamer", - "schedule_temperature": 20.0, + "schedule_temperature": 15.0, "mode": "auto", "sensors": { "temperature": 20.9, @@ -127,15 +113,15 @@ } }, "fe799307f1624099878210aa0b9f1475": { - "class": "gateway", - "fw": "3.0.15", - "hw": "AME Smile 2.0 board", + "dev_class": "gateway", + "firmware": "3.0.15", + "hardware": "AME Smile 2.0 board", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d", "mac_address": "012345670001", "model": "Adam", "name": "Adam", - "vendor": "Plugwise B.V.", "zigbee_mac_address": "ABCD012345670101", + "vendor": "Plugwise B.V.", "regulation_mode": "heating", "regulation_modes": [], "binary_sensors": { @@ -146,13 +132,13 @@ } }, "d3da73bde12a47d5a6b8f9dad971f2ec": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "82fa13f017d240daa0d0ea1775420f24", - "mac_address": null, "model": "Tom/Floor", "name": "Thermostatic Radiator Jessie", + "zigbee_mac_address": "ABCD012345670A10", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -166,15 +152,13 @@ } }, "21f2b542c49845e6bb416884c55778d6": { - "class": "game_console", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "game_console", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "Playstation Smart Plug", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A12", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 82.6, "electricity_consumed_interval": 8.6, @@ -187,15 +171,13 @@ } }, "78d1126fc4c743db81b61c20e88342a7": { - "class": "central_heating_pump", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "central_heating_pump", + "firmware": "2019-06-21T02:00:00+02:00", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": null, "model": "Plug", "name": "CV Pomp", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A05", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 35.6, "electricity_consumed_interval": 7.37, @@ -207,14 +189,10 @@ } }, "90986d591dcd426cae3ec3e8111ff730": { - "class": "heater_central", - "fw": null, - "hw": null, + "dev_class": "heater_central", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d", - "mac_address": null, "model": "Unknown", "name": "OnOff", - "vendor": null, "binary_sensors": { "heating_state": true }, @@ -225,15 +203,13 @@ } }, "cd0ddb54ef694e11ac18ed1cbce5dbbd": { - "class": "vcr", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "vcr", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "NAS", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A14", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 16.5, "electricity_consumed_interval": 0.5, @@ -246,15 +222,13 @@ } }, "4a810418d5394b3f82727340b91ba740": { - "class": "router", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "router", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "USG Smart Plug", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A16", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 8.5, "electricity_consumed_interval": 0.0, @@ -267,15 +241,13 @@ } }, "02cf28bfec924855854c544690a609ef": { - "class": "vcr", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "vcr", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "NVR", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A15", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 34.0, "electricity_consumed_interval": 9.15, @@ -288,15 +260,13 @@ } }, "a28f588dc4a049a483fd03a30361ad3a": { - "class": "settop", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "settop", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "Fibaro HC2", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A13", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 12.5, "electricity_consumed_interval": 3.8, @@ -309,26 +279,19 @@ } }, "6a3bf693d05e48e0b460c815a4fdd09d": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "82fa13f017d240daa0d0ea1775420f24", - "mac_address": null, "model": "Lisa", "name": "Zone Thermostat Jessie", + "zigbee_mac_address": "ABCD012345670A03", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "asleep", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0] - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -347,13 +310,13 @@ } }, "680423ff840043738f42cc7f1ff97a36": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "08963fec7c53423ca5680aa4cb502c63", - "mac_address": null, "model": "Tom/Floor", "name": "Thermostatic Radiator Badkamer", + "zigbee_mac_address": "ABCD012345670A17", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -367,26 +330,19 @@ } }, "f1fee6043d3642a9b0a65297455f008e": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "08963fec7c53423ca5680aa4cb502c63", - "mac_address": null, "model": "Lisa", "name": "Zone Thermostat Badkamer", + "zigbee_mac_address": "ABCD012345670A08", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "away", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0] - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -405,15 +361,13 @@ } }, "675416a629f343c495449970e2ca37b5": { - "class": "router", - "fw": "2019-06-21T02:00:00+02:00", - "hw": null, + "dev_class": "router", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": null, "model": "Plug", "name": "Ziggo Modem", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A01", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 12.2, "electricity_consumed_interval": 2.97, @@ -426,26 +380,19 @@ } }, "e7693eb9582644e5b865dba8d4447cf1": { - "class": "thermostatic_radiator_valve", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermostatic_radiator_valve", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "446ac08dd04d4eff8ac57489757b7314", - "mac_address": null, "model": "Tom/Floor", "name": "CV Kraan Garage", + "zigbee_mac_address": "ABCD012345670A11", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "no_frost", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0] - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -455,7 +402,7 @@ ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, + "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 15.6, diff --git a/tests/components/plugwise/fixtures/anna_heatpump/all_data.json b/tests/components/plugwise/fixtures/anna_heatpump/all_data.json index efc95494cad..cc3f8d8f385 100644 --- a/tests/components/plugwise/fixtures/anna_heatpump/all_data.json +++ b/tests/components/plugwise/fixtures/anna_heatpump/all_data.json @@ -8,19 +8,16 @@ }, { "1cbf783bb11e4a7c8a6843dee3a86927": { - "class": "heater_central", - "fw": null, - "hw": null, + "dev_class": "heater_central", "location": "a57efe5f145f498c9be62a9b63626fbf", - "mac_address": null, "model": "Generic heater", "name": "OpenTherm", "vendor": "Techneco", "maximum_boiler_temperature": 60.0, - "compressor_state": true, "binary_sensors": { "dhw_state": false, "heating_state": true, + "compressor_state": true, "cooling_state": false, "slave_boiler_state": false, "flame_state": false @@ -38,9 +35,9 @@ } }, "015ae9ea3f964e668e490fa39da3870b": { - "class": "gateway", - "fw": "4.0.15", - "hw": "AME Smile 2.0 board", + "dev_class": "gateway", + "firmware": "4.0.15", + "hardware": "AME Smile 2.0 board", "location": "a57efe5f145f498c9be62a9b63626fbf", "mac_address": "012345670001", "model": "Anna", @@ -54,11 +51,10 @@ } }, "3cb70739631c4d17a86b8b12e8a5161b": { - "class": "thermostat", - "fw": "2018-02-08T11:15:53+01:00", - "hw": "6539-1301-5002", + "dev_class": "thermostat", + "firmware": "2018-02-08T11:15:53+01:00", + "hardware": "6539-1301-5002", "location": "c784ee9fdab44e1395b8dee7d7a497d5", - "mac_address": null, "model": "Anna", "name": "Anna", "vendor": "Plugwise", @@ -67,13 +63,6 @@ "resolution": 0.1, "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "active_preset": "home", - "presets": { - "no_frost": [10.0, 30.0], - "home": [21.0, 22.0], - "away": [20.0, 25.0], - "asleep": [20.5, 24.0], - "vacation": [17.0, 28.0] - }, "available_schedules": ["None"], "selected_schedule": "None", "last_used": null, diff --git a/tests/components/plugwise/fixtures/p1v3_full_option/all_data.json b/tests/components/plugwise/fixtures/p1v3_full_option/all_data.json index f186335fcc9..fbf5aa63a5f 100644 --- a/tests/components/plugwise/fixtures/p1v3_full_option/all_data.json +++ b/tests/components/plugwise/fixtures/p1v3_full_option/all_data.json @@ -6,9 +6,9 @@ }, { "e950c7d5e1ee407a858e2a8b5016c8b3": { - "class": "gateway", - "fw": "3.3.9", - "hw": "AME Smile 2.0 board", + "dev_class": "gateway", + "firmware": "3.3.9", + "hardware": "AME Smile 2.0 board", "location": "cd3e822288064775a7c4afcdd70bdda2", "mac_address": "012345670001", "model": "P1", diff --git a/tests/components/plugwise/fixtures/stretch_v31/all_data.json b/tests/components/plugwise/fixtures/stretch_v31/all_data.json index 08ebab7b148..1ff62e9e619 100644 --- a/tests/components/plugwise/fixtures/stretch_v31/all_data.json +++ b/tests/components/plugwise/fixtures/stretch_v31/all_data.json @@ -6,26 +6,24 @@ }, { "0000aaaa0000aaaa0000aaaa0000aa00": { - "class": "gateway", - "fw": "3.1.11", - "hw": null, - "mac_address": "01:23:45:67:89:AB", + "dev_class": "gateway", + "firmware": "3.1.11", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "vendor": "Plugwise B.V.", + "mac_address": "01:23:45:67:89:AB", "model": "Stretch", "name": "Stretch", + "vendor": "Plugwise B.V.", "zigbee_mac_address": "ABCD012345670101" }, "5871317346d045bc9f6b987ef25ee638": { - "class": "water_heater_vessel", - "fw": "2011-06-27T10:52:18+02:00", - "hw": "6539-0701-4028", + "dev_class": "water_heater_vessel", + "firmware": "2011-06-27T10:52:18+02:00", + "hardware": "6539-0701-4028", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "mac_address": null, "model": "Circle type F", "name": "Boiler (1EB31)", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A07", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 1.19, "electricity_consumed_interval": 0.0, @@ -37,13 +35,13 @@ } }, "e1c884e7dede431dadee09506ec4f859": { - "class": "refrigerator", - "fw": "2011-06-27T10:47:37+02:00", - "hw": "6539-0700-7330", + "dev_class": "refrigerator", + "firmware": "2011-06-27T10:47:37+02:00", + "hardware": "6539-0700-7330", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "mac_address": null, "model": "Circle+ type F", "name": "Koelkast (92C4A)", + "zigbee_mac_address": "0123456789AB", "vendor": "Plugwise", "sensors": { "electricity_consumed": 50.5, @@ -56,15 +54,14 @@ } }, "aac7b735042c4832ac9ff33aae4f453b": { - "class": "dishwasher", - "fw": "2011-06-27T10:52:18+02:00", - "hw": "6539-0701-4022", + "dev_class": "dishwasher", + "firmware": "2011-06-27T10:52:18+02:00", + "hardware": "6539-0701-4022", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "mac_address": null, "model": "Circle type F", "name": "Vaatwasser (2a1ab)", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A02", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 0.0, "electricity_consumed_interval": 0.71, @@ -76,15 +73,14 @@ } }, "cfe95cf3de1948c0b8955125bf754614": { - "class": "dryer", - "fw": "2011-06-27T10:52:18+02:00", - "hw": "0000-0440-0107", + "dev_class": "dryer", + "firmware": "2011-06-27T10:52:18+02:00", + "hardware": "0000-0440-0107", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "mac_address": null, "model": "Circle type F", "name": "Droger (52559)", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A04", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 0.0, "electricity_consumed_interval": 0.0, @@ -96,15 +92,14 @@ } }, "059e4d03c7a34d278add5c7a4a781d19": { - "class": "washingmachine", - "fw": "2011-06-27T10:52:18+02:00", - "hw": "0000-0440-0107", + "dev_class": "washingmachine", + "firmware": "2011-06-27T10:52:18+02:00", + "hardware": "0000-0440-0107", "location": "0000aaaa0000aaaa0000aaaa0000aa00", - "mac_address": null, "model": "Circle type F", "name": "Wasmachine (52AC1)", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A01", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 0.0, "electricity_consumed_interval": 0.0, @@ -116,22 +111,16 @@ } }, "71e1944f2a944b26ad73323e399efef0": { - "class": "switching", - "fw": null, - "location": null, + "dev_class": "switching", "model": "Switchgroup", "name": "Test", "members": ["5ca521ac179d468e91d772eeeb8a2117"], - "types": ["switch_group"], - "vendor": null, "switches": { "relay": true } }, "d950b314e9d8499f968e6db8d82ef78c": { - "class": "report", - "fw": null, - "location": null, + "dev_class": "report", "model": "Switchgroup", "name": "Stroomvreters", "members": [ @@ -141,24 +130,18 @@ "cfe95cf3de1948c0b8955125bf754614", "e1c884e7dede431dadee09506ec4f859" ], - "types": ["switch_group"], - "vendor": null, "switches": { "relay": true } }, "d03738edfcc947f7b8f4573571d90d2d": { - "class": "switching", - "fw": null, - "location": null, + "dev_class": "switching", "model": "Switchgroup", "name": "Schakel", "members": [ "059e4d03c7a34d278add5c7a4a781d19", "cfe95cf3de1948c0b8955125bf754614" ], - "types": ["switch_group"], - "vendor": null, "switches": { "relay": true } diff --git a/tests/components/plugwise/test_diagnostics.py b/tests/components/plugwise/test_diagnostics.py index d6d1eebaf72..c2bb91746ae 100644 --- a/tests/components/plugwise/test_diagnostics.py +++ b/tests/components/plugwise/test_diagnostics.py @@ -32,26 +32,19 @@ async def test_diagnostics( }, "devices": { "df4a4a8169904cdb9c03d61a21f42140": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "12493538af164a409c6a1c79e38afe1c", - "mac_address": None, "model": "Lisa", "name": "Zone Lisa Bios", + "zigbee_mac_address": "ABCD012345670A06", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "away", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0], - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -61,18 +54,18 @@ async def test_diagnostics( ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, + "schedule_temperature": 0.0, "mode": "heat", "sensors": {"temperature": 16.5, "setpoint": 13.0, "battery": 67}, }, "b310b72a0e354bfab43089919b9a88bf": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": None, "model": "Tom/Floor", "name": "Floor kraan", + "zigbee_mac_address": "ABCD012345670A02", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -85,13 +78,13 @@ async def test_diagnostics( }, }, "a2c3583e0a6349358998b760cea82d2a": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "12493538af164a409c6a1c79e38afe1c", - "mac_address": None, "model": "Tom/Floor", "name": "Bios Cv Thermostatic Radiator ", + "zigbee_mac_address": "ABCD012345670A09", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -105,26 +98,19 @@ async def test_diagnostics( }, }, "b59bcebaf94b499ea7d46e4a66fb62d8": { - "class": "zone_thermostat", - "fw": "2016-08-02T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-08-02T02:00:00+02:00", + "hardware": "255", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": None, "model": "Lisa", "name": "Zone Lisa WK", + "zigbee_mac_address": "ABCD012345670A07", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "home", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0], - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -134,33 +120,33 @@ async def test_diagnostics( ], "selected_schedule": "GF7 Woonkamer", "last_used": "GF7 Woonkamer", - "schedule_temperature": 20.0, + "schedule_temperature": 15.0, "mode": "auto", "sensors": {"temperature": 20.9, "setpoint": 21.5, "battery": 34}, }, "fe799307f1624099878210aa0b9f1475": { - "class": "gateway", - "fw": "3.0.15", - "hw": "AME Smile 2.0 board", + "dev_class": "gateway", + "firmware": "3.0.15", + "hardware": "AME Smile 2.0 board", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d", "mac_address": "012345670001", "model": "Adam", "name": "Adam", - "vendor": "Plugwise B.V.", "zigbee_mac_address": "ABCD012345670101", + "vendor": "Plugwise B.V.", "regulation_mode": "heating", "regulation_modes": [], "binary_sensors": {"plugwise_notification": True}, "sensors": {"outdoor_temperature": 7.81}, }, "d3da73bde12a47d5a6b8f9dad971f2ec": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "82fa13f017d240daa0d0ea1775420f24", - "mac_address": None, "model": "Tom/Floor", "name": "Thermostatic Radiator Jessie", + "zigbee_mac_address": "ABCD012345670A10", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -174,15 +160,13 @@ async def test_diagnostics( }, }, "21f2b542c49845e6bb416884c55778d6": { - "class": "game_console", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "game_console", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "Playstation Smart Plug", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A12", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 82.6, "electricity_consumed_interval": 8.6, @@ -192,15 +176,13 @@ async def test_diagnostics( "switches": {"relay": True, "lock": False}, }, "78d1126fc4c743db81b61c20e88342a7": { - "class": "central_heating_pump", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "central_heating_pump", + "firmware": "2019-06-21T02:00:00+02:00", "location": "c50f167537524366a5af7aa3942feb1e", - "mac_address": None, "model": "Plug", "name": "CV Pomp", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A05", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 35.6, "electricity_consumed_interval": 7.37, @@ -210,14 +192,10 @@ async def test_diagnostics( "switches": {"relay": True}, }, "90986d591dcd426cae3ec3e8111ff730": { - "class": "heater_central", - "fw": None, - "hw": None, + "dev_class": "heater_central", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d", - "mac_address": None, "model": "Unknown", "name": "OnOff", - "vendor": None, "binary_sensors": {"heating_state": True}, "sensors": { "water_temperature": 70.0, @@ -226,15 +204,13 @@ async def test_diagnostics( }, }, "cd0ddb54ef694e11ac18ed1cbce5dbbd": { - "class": "vcr", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "vcr", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "NAS", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A14", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 16.5, "electricity_consumed_interval": 0.5, @@ -244,15 +220,13 @@ async def test_diagnostics( "switches": {"relay": True, "lock": True}, }, "4a810418d5394b3f82727340b91ba740": { - "class": "router", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "router", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "USG Smart Plug", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A16", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 8.5, "electricity_consumed_interval": 0.0, @@ -262,15 +236,13 @@ async def test_diagnostics( "switches": {"relay": True, "lock": True}, }, "02cf28bfec924855854c544690a609ef": { - "class": "vcr", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "vcr", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "NVR", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A15", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 34.0, "electricity_consumed_interval": 9.15, @@ -280,15 +252,13 @@ async def test_diagnostics( "switches": {"relay": True, "lock": True}, }, "a28f588dc4a049a483fd03a30361ad3a": { - "class": "settop", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "settop", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "Fibaro HC2", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A13", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 12.5, "electricity_consumed_interval": 3.8, @@ -298,26 +268,19 @@ async def test_diagnostics( "switches": {"relay": True, "lock": True}, }, "6a3bf693d05e48e0b460c815a4fdd09d": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "82fa13f017d240daa0d0ea1775420f24", - "mac_address": None, "model": "Lisa", "name": "Zone Thermostat Jessie", + "zigbee_mac_address": "ABCD012345670A03", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "asleep", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0], - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -332,13 +295,13 @@ async def test_diagnostics( "sensors": {"temperature": 17.2, "setpoint": 15.0, "battery": 37}, }, "680423ff840043738f42cc7f1ff97a36": { - "class": "thermo_sensor", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermo_sensor", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "08963fec7c53423ca5680aa4cb502c63", - "mac_address": None, "model": "Tom/Floor", "name": "Thermostatic Radiator Badkamer", + "zigbee_mac_address": "ABCD012345670A17", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, @@ -352,26 +315,19 @@ async def test_diagnostics( }, }, "f1fee6043d3642a9b0a65297455f008e": { - "class": "zone_thermostat", - "fw": "2016-10-27T02:00:00+02:00", - "hw": "255", + "dev_class": "zone_thermostat", + "firmware": "2016-10-27T02:00:00+02:00", + "hardware": "255", "location": "08963fec7c53423ca5680aa4cb502c63", - "mac_address": None, "model": "Lisa", "name": "Zone Thermostat Badkamer", + "zigbee_mac_address": "ABCD012345670A08", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 99.9, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "away", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0], - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -386,15 +342,13 @@ async def test_diagnostics( "sensors": {"temperature": 18.9, "setpoint": 14.0, "battery": 92}, }, "675416a629f343c495449970e2ca37b5": { - "class": "router", - "fw": "2019-06-21T02:00:00+02:00", - "hw": None, + "dev_class": "router", + "firmware": "2019-06-21T02:00:00+02:00", "location": "cd143c07248f491493cea0533bc3d669", - "mac_address": None, "model": "Plug", "name": "Ziggo Modem", - "vendor": "Plugwise", "zigbee_mac_address": "ABCD012345670A01", + "vendor": "Plugwise", "sensors": { "electricity_consumed": 12.2, "electricity_consumed_interval": 2.97, @@ -404,26 +358,19 @@ async def test_diagnostics( "switches": {"relay": True, "lock": True}, }, "e7693eb9582644e5b865dba8d4447cf1": { - "class": "thermostatic_radiator_valve", - "fw": "2019-03-27T01:00:00+01:00", - "hw": "1", + "dev_class": "thermostatic_radiator_valve", + "firmware": "2019-03-27T01:00:00+01:00", + "hardware": "1", "location": "446ac08dd04d4eff8ac57489757b7314", - "mac_address": None, "model": "Tom/Floor", "name": "CV Kraan Garage", + "zigbee_mac_address": "ABCD012345670A11", "vendor": "Plugwise", "lower_bound": 0.0, "upper_bound": 100.0, "resolution": 0.01, - "preset_modes": ["home", "asleep", "away", "vacation", "no_frost"], + "preset_modes": ["home", "asleep", "away", "no_frost"], "active_preset": "no_frost", - "presets": { - "home": [20.0, 22.0], - "asleep": [17.0, 24.0], - "away": [15.0, 25.0], - "vacation": [15.0, 28.0], - "no_frost": [10.0, 30.0], - }, "available_schedules": [ "CV Roan", "Bios Schema met Film Avond", @@ -433,7 +380,7 @@ async def test_diagnostics( ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, + "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 15.6, From 1ef3800844236a389df93c481b4e5724becf4c2f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 15:22:57 +0200 Subject: [PATCH 383/930] Tweak template cover tests (#71732) --- tests/components/template/test_cover.py | 269 +++++++++--------------- 1 file changed, 98 insertions(+), 171 deletions(-) diff --git a/tests/components/template/test_cover.py b/tests/components/template/test_cover.py index b4bc00ee6a2..92842dc5bfe 100644 --- a/tests/components/template/test_cover.py +++ b/tests/components/template/test_cover.py @@ -4,7 +4,10 @@ import pytest from homeassistant import setup from homeassistant.components.cover import ATTR_POSITION, ATTR_TILT_POSITION, DOMAIN from homeassistant.const import ( + ATTR_DOMAIN, ATTR_ENTITY_ID, + ATTR_SERVICE_DATA, + EVENT_CALL_SERVICE, SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, @@ -22,12 +25,45 @@ from homeassistant.const import ( STATE_OPENING, STATE_UNAVAILABLE, ) +from homeassistant.core import callback from tests.common import assert_setup_component ENTITY_COVER = "cover.test_template_cover" +@pytest.fixture +def service_calls(hass): + """Track service call events for cover.test_state.""" + events = [] + entity_id = "cover.test_state" + + @callback + def capture_events(event): + print(event.data) + if event.data[ATTR_DOMAIN] != DOMAIN: + return + if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: + return + events.append(event) + + hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) + + return events + + +OPEN_CLOSE_COVER_CONFIG = { + "open_cover": { + "service": "cover.open_cover", + "entity_id": "cover.test_state", + }, + "close_cover": { + "service": "cover.close_cover", + "entity_id": "cover.test_state", + }, +} + + @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @pytest.mark.parametrize( "config, states", @@ -38,15 +74,8 @@ ENTITY_COVER = "cover.test_template_cover" "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ states.cover.test_state.state }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -90,16 +119,9 @@ ENTITY_COVER = "cover.test_template_cover" "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ states.cover.test.attributes.position }}", "value_template": "{{ states.cover.test_state.state }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -148,15 +170,8 @@ async def test_template_state_text(hass, states, start_ha, caplog): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ 1 == 1 }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -178,15 +193,8 @@ async def test_template_state_boolean(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ states.cover.test.attributes.position }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test", - }, } }, } @@ -202,11 +210,8 @@ async def test_template_position(hass, start_ha): (STATE_CLOSED, 42, STATE_OPEN), (STATE_OPEN, 0.0, STATE_CLOSED), ]: - state = hass.states.async_set("cover.test", set_state) - await hass.async_block_till_done() - entity = hass.states.get("cover.test") attrs["position"] = pos - hass.states.async_set(entity.entity_id, entity.state, attributes=attrs) + hass.states.async_set("cover.test", set_state, attributes=attrs) await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == pos @@ -222,16 +227,9 @@ async def test_template_position(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ 1 == 1 }}", "tilt_template": "{{ 42 }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -253,16 +251,9 @@ async def test_template_tilt(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ -1 }}", "tilt_template": "{{ 110 }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -272,20 +263,13 @@ async def test_template_tilt(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ on }}", "tilt_template": "{% if states.cover.test_state.state %}" "on" "{% else %}" "off" "{% endif %}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, }, }, } @@ -340,19 +324,15 @@ async def test_template_open_or_position(hass, start_ha, caplog_setup_text): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ 0 }}", - "open_cover": {"service": "test.automation"}, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } }, ], ) -async def test_open_action(hass, start_ha, calls): +async def test_open_action(hass, start_ha, service_calls): """Test the open_cover command.""" state = hass.states.get("cover.test_template_cover") assert state.state == STATE_CLOSED @@ -362,7 +342,8 @@ async def test_open_action(hass, start_ha, calls): ) await hass.async_block_till_done() - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[0].data["service"] == "open_cover" @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @@ -374,20 +355,19 @@ async def test_open_action(hass, start_ha, calls): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ 100 }}", - "open_cover": { - "service": "cover.open_cover", + "stop_cover": { + "service": "cover.stop_cover", "entity_id": "cover.test_state", }, - "close_cover": {"service": "test.automation"}, - "stop_cover": {"service": "test.automation"}, } }, } }, ], ) -async def test_close_stop_action(hass, start_ha, calls): +async def test_close_stop_action(hass, start_ha, service_calls): """Test the close-cover and stop_cover commands.""" state = hass.states.get("cover.test_template_cover") assert state.state == STATE_OPEN @@ -402,7 +382,9 @@ async def test_close_stop_action(hass, start_ha, calls): ) await hass.async_block_till_done() - assert len(calls) == 2 + assert len(service_calls) == 2 + assert service_calls[0].data["service"] == "close_cover" + assert service_calls[1].data["service"] == "stop_cover" @pytest.mark.parametrize("count,domain", [(1, "input_number")]) @@ -412,7 +394,7 @@ async def test_close_stop_action(hass, start_ha, calls): {"input_number": {"test": {"min": "0", "max": "100", "initial": "42"}}}, ], ) -async def test_set_position(hass, start_ha, calls): +async def test_set_position(hass, start_ha, service_calls): """Test the set_position command.""" with assert_setup_component(1, "cover"): assert await setup.async_setup_component( @@ -423,11 +405,10 @@ async def test_set_position(hass, start_ha, calls): "platform": "template", "covers": { "test_template_cover": { - "position_template": "{{ states.input_number.test.state | int }}", "set_cover_position": { - "service": "input_number.set_value", - "entity_id": "input_number.test", - "data_template": {"value": "{{ position }}"}, + "service": "cover.set_cover_position", + "entity_id": "cover.test_state", + "data_template": {"position": "{{ position }}"}, }, } }, @@ -450,6 +431,9 @@ async def test_set_position(hass, start_ha, calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 100.0 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "set_cover_position" + assert service_calls[-1].data["service_data"]["position"] == 100 await hass.services.async_call( DOMAIN, SERVICE_CLOSE_COVER, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -457,6 +441,9 @@ async def test_set_position(hass, start_ha, calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 0.0 + assert len(service_calls) == 2 + assert service_calls[-1].data["service"] == "set_cover_position" + assert service_calls[-1].data["service_data"]["position"] == 0 await hass.services.async_call( DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -464,6 +451,9 @@ async def test_set_position(hass, start_ha, calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 100.0 + assert len(service_calls) == 3 + assert service_calls[-1].data["service"] == "set_cover_position" + assert service_calls[-1].data["service_data"]["position"] == 100 await hass.services.async_call( DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -471,6 +461,9 @@ async def test_set_position(hass, start_ha, calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 0.0 + assert len(service_calls) == 4 + assert service_calls[-1].data["service"] == "set_cover_position" + assert service_calls[-1].data["service_data"]["position"] == 0 await hass.services.async_call( DOMAIN, @@ -481,6 +474,9 @@ async def test_set_position(hass, start_ha, calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 25.0 + assert len(service_calls) == 5 + assert service_calls[-1].data["service"] == "set_cover_position" + assert service_calls[-1].data["service_data"]["position"] == 25 @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @@ -492,16 +488,12 @@ async def test_set_position(hass, start_ha, calls): "platform": "template", "covers": { "test_template_cover": { - "position_template": "{{ 100 }}", - "open_cover": { - "service": "cover.open_cover", + **OPEN_CLOSE_COVER_CONFIG, + "set_cover_tilt_position": { + "service": "cover.set_cover_tilt_position", "entity_id": "cover.test_state", + "data_template": {"tilt_position": "{{ tilt }}"}, }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, - "set_cover_tilt_position": {"service": "test.automation"}, } }, } @@ -509,17 +501,20 @@ async def test_set_position(hass, start_ha, calls): ], ) @pytest.mark.parametrize( - "service,attr", + "service,attr,tilt_position", [ ( SERVICE_SET_COVER_TILT_POSITION, {ATTR_ENTITY_ID: ENTITY_COVER, ATTR_TILT_POSITION: 42}, + 42, ), - (SERVICE_OPEN_COVER_TILT, {ATTR_ENTITY_ID: ENTITY_COVER}), - (SERVICE_CLOSE_COVER_TILT, {ATTR_ENTITY_ID: ENTITY_COVER}), + (SERVICE_OPEN_COVER_TILT, {ATTR_ENTITY_ID: ENTITY_COVER}, 100), + (SERVICE_CLOSE_COVER_TILT, {ATTR_ENTITY_ID: ENTITY_COVER}, 0), ], ) -async def test_set_tilt_position(hass, service, attr, start_ha, calls): +async def test_set_tilt_position( + hass, service, attr, start_ha, service_calls, tilt_position +): """Test the set_tilt_position command.""" await hass.services.async_call( DOMAIN, @@ -529,7 +524,9 @@ async def test_set_tilt_position(hass, service, attr, start_ha, calls): ) await hass.async_block_till_done() - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "set_cover_tilt_position" + assert service_calls[-1].data["service_data"]["tilt_position"] == tilt_position @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @@ -633,15 +630,8 @@ async def test_set_tilt_position_optimistic(hass, start_ha, calls): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ states.cover.test_state.state }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, "icon_template": "{% if states.cover.test_state.state %}" "mdi:check" "{% endif %}", @@ -673,15 +663,8 @@ async def test_icon_template(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ states.cover.test_state.state }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, "entity_picture_template": "{% if states.cover.test_state.state %}" "/local/cover.png" "{% endif %}", @@ -713,15 +696,8 @@ async def test_entity_picture_template(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "open", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, "availability_template": "{{ is_state('availability_state.state','on') }}", } }, @@ -751,15 +727,8 @@ async def test_availability_template(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "open", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -781,16 +750,9 @@ async def test_availability_without_availability_template(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "availability_template": "{{ x - 12 }}", "value_template": "open", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -814,16 +776,9 @@ async def test_invalid_availability_template_keeps_component_available( "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ states.cover.test_state.state }}", "device_class": "door", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -845,16 +800,9 @@ async def test_device_class(hass, start_ha): "platform": "template", "covers": { "test_template_cover": { + **OPEN_CLOSE_COVER_CONFIG, "value_template": "{{ states.cover.test_state.state }}", "device_class": "barnacle_bill", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, } }, } @@ -876,28 +824,14 @@ async def test_invalid_device_class(hass, start_ha): "platform": "template", "covers": { "test_template_cover_01": { + **OPEN_CLOSE_COVER_CONFIG, "unique_id": "not-so-unique-anymore", "value_template": "{{ true }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, }, "test_template_cover_02": { + **OPEN_CLOSE_COVER_CONFIG, "unique_id": "not-so-unique-anymore", "value_template": "{{ false }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, }, }, } @@ -918,16 +852,9 @@ async def test_unique_id(hass, start_ha): "platform": "template", "covers": { "garage_door": { + **OPEN_CLOSE_COVER_CONFIG, "friendly_name": "Garage Door", "value_template": "{{ is_state('binary_sensor.garage_door_sensor', 'off') }}", - "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", - }, - "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", - }, }, }, } From b70e97e949ca73fe57849625c0b0c51f0b8796f7 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 16:04:01 +0200 Subject: [PATCH 384/930] Remove unused calls fixture from template tests (#71735) --- tests/components/template/test_button.py | 18 +++++------------- tests/components/template/test_fan.py | 2 +- tests/components/template/test_number.py | 21 +++++---------------- tests/components/template/test_select.py | 24 ++++++------------------ 4 files changed, 17 insertions(+), 48 deletions(-) diff --git a/tests/components/template/test_button.py b/tests/components/template/test_button.py index aa671bebd89..25bced16ed2 100644 --- a/tests/components/template/test_button.py +++ b/tests/components/template/test_button.py @@ -2,8 +2,6 @@ import datetime as dt from unittest.mock import patch -import pytest - from homeassistant import setup from homeassistant.components.button.const import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.components.template.button import DEFAULT_NAME @@ -16,19 +14,13 @@ from homeassistant.const import ( ) from homeassistant.helpers.entity_registry import async_get -from tests.common import assert_setup_component, async_mock_service +from tests.common import assert_setup_component _TEST_BUTTON = "button.template_button" _TEST_OPTIONS_BUTTON = "button.test" -@pytest.fixture -def calls(hass): - """Track calls to a mock service.""" - return async_mock_service(hass, "test", "automation") - - -async def test_missing_optional_config(hass, calls): +async def test_missing_optional_config(hass): """Test: missing optional template is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -50,7 +42,7 @@ async def test_missing_optional_config(hass, calls): _verify(hass, STATE_UNKNOWN) -async def test_missing_required_keys(hass, calls): +async def test_missing_required_keys(hass): """Test: missing required fields will fail.""" with assert_setup_component(0, "template"): assert await setup.async_setup_component( @@ -128,7 +120,7 @@ async def test_all_optional_config(hass, calls): assert er.async_get_entity_id("button", "template", "test-test") -async def test_name_template(hass, calls): +async def test_name_template(hass): """Test: name template.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -158,7 +150,7 @@ async def test_name_template(hass, calls): ) -async def test_unique_id(hass, calls): +async def test_unique_id(hass): """Test: unique id is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( diff --git a/tests/components/template/test_fan.py b/tests/components/template/test_fan.py index ccd273571b5..30d5222024d 100644 --- a/tests/components/template/test_fan.py +++ b/tests/components/template/test_fan.py @@ -396,7 +396,7 @@ async def test_on_off(hass): _verify(hass, state, 0, None, None, None) -async def test_set_invalid_direction_from_initial_stage(hass, calls): +async def test_set_invalid_direction_from_initial_stage(hass): """Test set invalid direction when fan is in initial state.""" await _register_components(hass) diff --git a/tests/components/template/test_number.py b/tests/components/template/test_number.py index 98460335047..6c47fdd2abc 100644 --- a/tests/components/template/test_number.py +++ b/tests/components/template/test_number.py @@ -1,5 +1,4 @@ """The tests for the Template number platform.""" -import pytest from homeassistant import setup from homeassistant.components.input_number import ( @@ -19,11 +18,7 @@ from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import Context from homeassistant.helpers.entity_registry import async_get -from tests.common import ( - assert_setup_component, - async_capture_events, - async_mock_service, -) +from tests.common import assert_setup_component, async_capture_events _TEST_NUMBER = "number.template_number" # Represent for number's value @@ -47,13 +42,7 @@ _VALUE_INPUT_NUMBER_CONFIG = { } -@pytest.fixture -def calls(hass): - """Track calls to a mock service.""" - return async_mock_service(hass, "test", "automation") - - -async def test_missing_optional_config(hass, calls): +async def test_missing_optional_config(hass): """Test: missing optional template is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -77,7 +66,7 @@ async def test_missing_optional_config(hass, calls): _verify(hass, 4, 1, 0.0, 100.0) -async def test_missing_required_keys(hass, calls): +async def test_missing_required_keys(hass): """Test: missing required fields will fail.""" with assert_setup_component(0, "template"): assert await setup.async_setup_component( @@ -112,7 +101,7 @@ async def test_missing_required_keys(hass, calls): assert hass.states.async_all("number") == [] -async def test_all_optional_config(hass, calls): +async def test_all_optional_config(hass): """Test: including all optional templates is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -138,7 +127,7 @@ async def test_all_optional_config(hass, calls): _verify(hass, 4, 1, 3, 5) -async def test_templates_with_entities(hass, calls): +async def test_templates_with_entities(hass): """Test templates with values from other entities.""" with assert_setup_component(4, "input_number"): assert await setup.async_setup_component( diff --git a/tests/components/template/test_select.py b/tests/components/template/test_select.py index 66f67d93754..de41ccedbb1 100644 --- a/tests/components/template/test_select.py +++ b/tests/components/template/test_select.py @@ -1,6 +1,4 @@ """The tests for the Template select platform.""" -import pytest - from homeassistant import setup from homeassistant.components.input_select import ( ATTR_OPTION as INPUT_SELECT_ATTR_OPTION, @@ -19,24 +17,14 @@ from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import Context from homeassistant.helpers.entity_registry import async_get -from tests.common import ( - assert_setup_component, - async_capture_events, - async_mock_service, -) +from tests.common import assert_setup_component, async_capture_events _TEST_SELECT = "select.template_select" # Represent for select's current_option _OPTION_INPUT_SELECT = "input_select.option" -@pytest.fixture -def calls(hass): - """Track calls to a mock service.""" - return async_mock_service(hass, "test", "automation") - - -async def test_missing_optional_config(hass, calls): +async def test_missing_optional_config(hass): """Test: missing optional template is ok.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -60,7 +48,7 @@ async def test_missing_optional_config(hass, calls): _verify(hass, "a", ["a", "b"]) -async def test_multiple_configs(hass, calls): +async def test_multiple_configs(hass): """Test: multiple select entities get created.""" with assert_setup_component(1, "template"): assert await setup.async_setup_component( @@ -92,7 +80,7 @@ async def test_multiple_configs(hass, calls): _verify(hass, "a", ["a", "b"], f"{_TEST_SELECT}_2") -async def test_missing_required_keys(hass, calls): +async def test_missing_required_keys(hass): """Test: missing required fields will fail.""" with assert_setup_component(0, "template"): assert await setup.async_setup_component( @@ -143,7 +131,7 @@ async def test_missing_required_keys(hass, calls): assert hass.states.async_all("select") == [] -async def test_templates_with_entities(hass, calls): +async def test_templates_with_entities(hass): """Test templates with values from other entities.""" with assert_setup_component(1, "input_select"): assert await setup.async_setup_component( @@ -290,7 +278,7 @@ def _verify(hass, expected_current_option, expected_options, entity_name=_TEST_S assert attributes.get(SELECT_ATTR_OPTIONS) == expected_options -async def test_template_icon_with_entities(hass, calls): +async def test_template_icon_with_entities(hass): """Test templates with values from other entities.""" with assert_setup_component(1, "input_select"): assert await setup.async_setup_component( From 1cb00cbb79a69a2ca44849226ced5275cc695847 Mon Sep 17 00:00:00 2001 From: Marvin ROGER Date: Thu, 12 May 2022 16:23:18 +0200 Subject: [PATCH 385/930] Fix timezone issue on onvif integration (#70473) --- homeassistant/components/onvif/device.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/onvif/device.py b/homeassistant/components/onvif/device.py index f45362c6e6c..d376b7fe258 100644 --- a/homeassistant/components/onvif/device.py +++ b/homeassistant/components/onvif/device.py @@ -165,17 +165,13 @@ class ONVIFDevice: ) return + tzone = dt_util.DEFAULT_TIME_ZONE + cdate = device_time.LocalDateTime if device_time.UTCDateTime: tzone = dt_util.UTC cdate = device_time.UTCDateTime - else: - tzone = ( - dt_util.get_time_zone( - device_time.TimeZone or str(dt_util.DEFAULT_TIME_ZONE) - ) - or dt_util.DEFAULT_TIME_ZONE - ) - cdate = device_time.LocalDateTime + elif device_time.TimeZone: + tzone = dt_util.get_time_zone(device_time.TimeZone.TZ) or tzone if cdate is None: LOGGER.warning("Could not retrieve date/time on this camera") From 35e4f11e0bc7c57f7da10d99c26bba3901969b89 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 16:29:48 +0200 Subject: [PATCH 386/930] Tweak template lock tests (#71734) --- tests/components/template/test_lock.py | 143 +++++++++++-------------- 1 file changed, 62 insertions(+), 81 deletions(-) diff --git a/tests/components/template/test_lock.py b/tests/components/template/test_lock.py index 80c83e0885a..e53f6660162 100644 --- a/tests/components/template/test_lock.py +++ b/tests/components/template/test_lock.py @@ -3,7 +3,48 @@ import pytest from homeassistant import setup from homeassistant.components import lock -from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.const import ( + ATTR_DOMAIN, + ATTR_ENTITY_ID, + ATTR_SERVICE_DATA, + EVENT_CALL_SERVICE, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) +from homeassistant.core import callback + + +@pytest.fixture +def service_calls(hass): + """Track service call events for switch.test_state.""" + events = [] + entity_id = "switch.test_state" + + @callback + def capture_events(event): + if event.data[ATTR_DOMAIN] != "switch": + return + if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: + return + events.append(event) + + hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) + + return events + + +OPTIMISTIC_LOCK_CONFIG = { + "platform": "template", + "lock": { + "service": "switch.turn_on", + "entity_id": "switch.test_state", + }, + "unlock": { + "service": "switch.turn_off", + "entity_id": "switch.test_state", + }, +} @pytest.mark.parametrize("count,domain", [(1, lock.DOMAIN)]) @@ -12,17 +53,9 @@ from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVA [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "name": "Test template lock", "value_template": "{{ states.switch.test_state.state }}", - "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -48,16 +81,8 @@ async def test_template_state(hass, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ 1 == 1 }}", - "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -74,16 +99,8 @@ async def test_template_state_boolean_on(hass, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ 1 == 2 }}", - "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -155,16 +172,8 @@ async def test_template_syntax_error(hass, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ 1 + 1 }}", - "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -186,19 +195,15 @@ async def test_template_static(hass, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "lock": {"service": "test.automation"}, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], ) -async def test_lock_action(hass, start_ha, calls): +async def test_lock_action(hass, start_ha, service_calls): """Test lock action.""" + await setup.async_setup_component(hass, "switch", {}) hass.states.async_set("switch.test_state", STATE_OFF) await hass.async_block_till_done() @@ -210,7 +215,8 @@ async def test_lock_action(hass, start_ha, calls): ) await hass.async_block_till_done() - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_on" @pytest.mark.parametrize("count,domain", [(1, lock.DOMAIN)]) @@ -219,19 +225,15 @@ async def test_lock_action(hass, start_ha, calls): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "unlock": {"service": "test.automation"}, } }, ], ) -async def test_unlock_action(hass, start_ha, calls): +async def test_unlock_action(hass, start_ha, service_calls): """Test unlock action.""" + await setup.async_setup_component(hass, "switch", {}) hass.states.async_set("switch.test_state", STATE_ON) await hass.async_block_till_done() @@ -243,7 +245,8 @@ async def test_unlock_action(hass, start_ha, calls): ) await hass.async_block_till_done() - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_off" @pytest.mark.parametrize("count,domain", [(1, lock.DOMAIN)]) @@ -252,10 +255,8 @@ async def test_unlock_action(hass, start_ha, calls): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ states.input_select.test_state.state }}", - "lock": {"service": "test.automation"}, - "unlock": {"service": "test.automation"}, } }, ], @@ -264,7 +265,7 @@ async def test_unlock_action(hass, start_ha, calls): "test_state", [lock.STATE_UNLOCKING, lock.STATE_LOCKING, lock.STATE_JAMMED] ) async def test_lock_state(hass, test_state, start_ha): - """Test unlocking.""" + """Test value template.""" hass.states.async_set("input_select.test_state", test_state) await hass.async_block_till_done() @@ -278,13 +279,8 @@ async def test_lock_state(hass, test_state, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ states('switch.test_state') }}", - "lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"}, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, "availability_template": "{{ is_state('availability_state.state', 'on') }}", } }, @@ -313,14 +309,9 @@ async def test_available_template_with_entities(hass, start_ha): [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "value_template": "{{ 1 + 1 }}", "availability_template": "{{ x - 12 }}", - "lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"}, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -339,15 +330,10 @@ async def test_invalid_availability_template_keeps_component_available( [ { lock.DOMAIN: { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "name": "test_template_lock_01", "unique_id": "not-so-unique-anymore", "value_template": "{{ true }}", - "lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"}, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, ], @@ -359,15 +345,10 @@ async def test_unique_id(hass, start_ha): lock.DOMAIN, { "lock": { - "platform": "template", + **OPTIMISTIC_LOCK_CONFIG, "name": "test_template_lock_02", "unique_id": "not-so-unique-anymore", "value_template": "{{ false }}", - "lock": {"service": "switch.turn_on", "entity_id": "switch.test_state"}, - "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, }, }, ) From 11cc1feb853bcfd9633ebfc44eae142c10a7f983 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 17:08:21 +0200 Subject: [PATCH 387/930] Tweak template switch tests (#71738) --- tests/components/template/test_switch.py | 194 ++++++++--------------- 1 file changed, 64 insertions(+), 130 deletions(-) diff --git a/tests/components/template/test_switch.py b/tests/components/template/test_switch.py index 2628b0afa49..93e0f8540bf 100644 --- a/tests/components/template/test_switch.py +++ b/tests/components/template/test_switch.py @@ -5,28 +5,51 @@ import pytest from homeassistant import setup from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( + ATTR_DOMAIN, ATTR_ENTITY_ID, + ATTR_SERVICE_DATA, + EVENT_CALL_SERVICE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, ) -from homeassistant.core import CoreState, State +from homeassistant.core import CoreState, State, callback from homeassistant.setup import async_setup_component -from tests.common import ( - assert_setup_component, - async_mock_service, - mock_component, - mock_restore_cache, -) +from tests.common import assert_setup_component, mock_component, mock_restore_cache @pytest.fixture -def calls(hass): - """Track calls to a mock service.""" - return async_mock_service(hass, "test", "automation") +def service_calls(hass): + """Track service call events for switch.test_state.""" + events = [] + entity_id = "switch.test_state" + + @callback + def capture_events(event): + if event.data[ATTR_DOMAIN] != "switch": + return + if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: + return + events.append(event) + + hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) + + return events + + +OPTIMISTIC_SWITCH_CONFIG = { + "turn_on": { + "service": "switch.turn_on", + "entity_id": "switch.test_state", + }, + "turn_off": { + "service": "switch.turn_off", + "entity_id": "switch.test_state", + }, +} async def test_template_state_text(hass): @@ -40,15 +63,8 @@ async def test_template_state_text(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -83,15 +99,8 @@ async def test_template_state_boolean_on(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -117,15 +126,8 @@ async def test_template_state_boolean_off(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ 1 == 2 }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -151,15 +153,8 @@ async def test_icon_template(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, "icon_template": "{% if states.switch.test_state.state %}" "mdi:check" "{% endif %}", @@ -194,15 +189,8 @@ async def test_entity_picture_template(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, "entity_picture_template": "{% if states.switch.test_state.state %}" "/local/switch.png" "{% endif %}", @@ -237,15 +225,8 @@ async def test_template_syntax_error(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{% if rubbish %}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -270,15 +251,8 @@ async def test_invalid_name_does_not_create(hass): "platform": "template", "switches": { "test INVALID switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ rubbish }", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -393,7 +367,7 @@ async def test_missing_off_does_not_create(hass): assert hass.states.async_all("switch") == [] -async def test_on_action(hass, calls): +async def test_on_action(hass, service_calls): """Test on action.""" assert await async_setup_component( hass, @@ -403,12 +377,8 @@ async def test_on_action(hass, calls): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, } }, } @@ -432,10 +402,11 @@ async def test_on_action(hass, calls): blocking=True, ) - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_on" -async def test_on_action_optimistic(hass, calls): +async def test_on_action_optimistic(hass, service_calls): """Test on action in optimistic mode.""" assert await async_setup_component( hass, @@ -445,11 +416,7 @@ async def test_on_action_optimistic(hass, calls): "platform": "template", "switches": { "test_template_switch": { - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, + **OPTIMISTIC_SWITCH_CONFIG, } }, } @@ -473,11 +440,13 @@ async def test_on_action_optimistic(hass, calls): ) state = hass.states.get("switch.test_template_switch") - assert len(calls) == 1 assert state.state == STATE_ON + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_on" -async def test_off_action(hass, calls): + +async def test_off_action(hass, service_calls): """Test off action.""" assert await async_setup_component( hass, @@ -487,12 +456,8 @@ async def test_off_action(hass, calls): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ states.switch.test_state.state }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": {"service": "test.automation"}, } }, } @@ -516,10 +481,11 @@ async def test_off_action(hass, calls): blocking=True, ) - assert len(calls) == 1 + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_off" -async def test_off_action_optimistic(hass, calls): +async def test_off_action_optimistic(hass, service_calls): """Test off action in optimistic mode.""" assert await async_setup_component( hass, @@ -529,11 +495,7 @@ async def test_off_action_optimistic(hass, calls): "platform": "template", "switches": { "test_template_switch": { - "turn_off": {"service": "test.automation"}, - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, + **OPTIMISTIC_SWITCH_CONFIG, } }, } @@ -557,9 +519,11 @@ async def test_off_action_optimistic(hass, calls): ) state = hass.states.get("switch.test_template_switch") - assert len(calls) == 1 assert state.state == STATE_OFF + assert len(service_calls) == 1 + assert service_calls[-1].data["service"] == "turn_off" + async def test_restore_state(hass): """Test state restoration.""" @@ -582,12 +546,10 @@ async def test_restore_state(hass): "platform": "template", "switches": { "s1": { - "turn_on": {"service": "test.automation"}, - "turn_off": {"service": "test.automation"}, + **OPTIMISTIC_SWITCH_CONFIG, }, "s2": { - "turn_on": {"service": "test.automation"}, - "turn_off": {"service": "test.automation"}, + **OPTIMISTIC_SWITCH_CONFIG, }, }, } @@ -614,15 +576,8 @@ async def test_available_template_with_entities(hass): "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, "availability_template": "{{ is_state('availability_state.state', 'on') }}", } }, @@ -655,15 +610,8 @@ async def test_invalid_availability_template_keeps_component_available(hass, cap "platform": "template", "switches": { "test_template_switch": { + **OPTIMISTIC_SWITCH_CONFIG, "value_template": "{{ true }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, "availability_template": "{{ x - 12 }}", } }, @@ -689,28 +637,14 @@ async def test_unique_id(hass): "platform": "template", "switches": { "test_template_switch_01": { + **OPTIMISTIC_SWITCH_CONFIG, "unique_id": "not-so-unique-anymore", "value_template": "{{ true }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, }, "test_template_switch_02": { + **OPTIMISTIC_SWITCH_CONFIG, "unique_id": "not-so-unique-anymore", "value_template": "{{ false }}", - "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", - }, - "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", - }, }, }, } From 3332c853c4dcbea146a9c52c02bb0df219718f00 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 12 May 2022 18:34:26 +0200 Subject: [PATCH 388/930] Remove prints from template tests (#71746) --- tests/components/template/test_alarm_control_panel.py | 1 - tests/components/template/test_cover.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/components/template/test_alarm_control_panel.py b/tests/components/template/test_alarm_control_panel.py index 669effca3e9..9ec93073911 100644 --- a/tests/components/template/test_alarm_control_panel.py +++ b/tests/components/template/test_alarm_control_panel.py @@ -29,7 +29,6 @@ def service_calls(hass): @callback def capture_events(event): - print(event.data) if event.data[ATTR_DOMAIN] != ALARM_DOMAIN: return if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: diff --git a/tests/components/template/test_cover.py b/tests/components/template/test_cover.py index 92842dc5bfe..18db5482141 100644 --- a/tests/components/template/test_cover.py +++ b/tests/components/template/test_cover.py @@ -40,7 +40,6 @@ def service_calls(hass): @callback def capture_events(event): - print(event.data) if event.data[ATTR_DOMAIN] != DOMAIN: return if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: From ae89a1243a563ea757a8251310101c6956c19976 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Thu, 12 May 2022 12:15:59 -0600 Subject: [PATCH 389/930] Refactor litterrobot to use SensorEntityDescription (#71224) --- .../components/litterrobot/sensor.py | 113 ++++++++++-------- tests/components/litterrobot/conftest.py | 6 + tests/components/litterrobot/test_sensor.py | 32 ++--- 3 files changed, 84 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/litterrobot/sensor.py b/homeassistant/components/litterrobot/sensor.py index de3126986d1..751d16551c3 100644 --- a/homeassistant/components/litterrobot/sensor.py +++ b/homeassistant/components/litterrobot/sensor.py @@ -1,11 +1,19 @@ """Support for Litter-Robot sensors.""" from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass from datetime import datetime +from typing import Any from pylitterbot.robot import Robot -from homeassistant.components.sensor import SensorDeviceClass, SensorEntity, StateType +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + StateType, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE from homeassistant.core import HomeAssistant @@ -27,56 +35,64 @@ def icon_for_gauge_level(gauge_level: int | None = None, offset: int = 0) -> str return "mdi:gauge-low" -class LitterRobotPropertySensor(LitterRobotEntity, SensorEntity): - """Litter-Robot property sensor.""" +@dataclass +class LitterRobotSensorEntityDescription(SensorEntityDescription): + """A class that describes Litter-Robot sensor entities.""" + + icon_fn: Callable[[Any], str | None] = lambda _: None + should_report: Callable[[Robot], bool] = lambda _: True + + +class LitterRobotSensorEntity(LitterRobotEntity, SensorEntity): + """Litter-Robot sensor entity.""" + + entity_description: LitterRobotSensorEntityDescription def __init__( - self, robot: Robot, entity_type: str, hub: LitterRobotHub, sensor_attribute: str + self, + robot: Robot, + hub: LitterRobotHub, + description: LitterRobotSensorEntityDescription, ) -> None: - """Pass robot, entity_type and hub to LitterRobotEntity.""" - super().__init__(robot, entity_type, hub) - self.sensor_attribute = sensor_attribute + """Initialize a Litter-Robot sensor entity.""" + assert description.name + super().__init__(robot, description.name, hub) + self.entity_description = description @property def native_value(self) -> StateType | datetime: """Return the state.""" - return getattr(self.robot, self.sensor_attribute) - - -class LitterRobotWasteSensor(LitterRobotPropertySensor): - """Litter-Robot waste sensor.""" - - @property - def native_unit_of_measurement(self) -> str: - """Return unit of measurement.""" - return PERCENTAGE - - @property - def icon(self) -> str: - """Return the icon to use in the frontend, if any.""" - return icon_for_gauge_level(self.state, 10) - - -class LitterRobotSleepTimeSensor(LitterRobotPropertySensor): - """Litter-Robot sleep time sensor.""" - - @property - def native_value(self) -> StateType | datetime: - """Return the state.""" - if self.robot.sleep_mode_enabled: - return super().native_value + if self.entity_description.should_report(self.robot): + return getattr(self.robot, self.entity_description.key) return None @property - def device_class(self) -> str: - """Return the device class, if any.""" - return SensorDeviceClass.TIMESTAMP + def icon(self) -> str | None: + """Return the icon to use in the frontend, if any.""" + if (icon := self.entity_description.icon_fn(self.state)) is not None: + return icon + return super().icon -ROBOT_SENSORS: list[tuple[type[LitterRobotPropertySensor], str, str]] = [ - (LitterRobotWasteSensor, "Waste Drawer", "waste_drawer_level"), - (LitterRobotSleepTimeSensor, "Sleep Mode Start Time", "sleep_mode_start_time"), - (LitterRobotSleepTimeSensor, "Sleep Mode End Time", "sleep_mode_end_time"), +ROBOT_SENSORS = [ + LitterRobotSensorEntityDescription( + name="Waste Drawer", + key="waste_drawer_level", + native_unit_of_measurement=PERCENTAGE, + icon_fn=lambda state: icon_for_gauge_level(state, 10), + ), + LitterRobotSensorEntityDescription( + name="Sleep Mode Start Time", + key="sleep_mode_start_time", + device_class=SensorDeviceClass.TIMESTAMP, + should_report=lambda robot: robot.sleep_mode_enabled, + ), + LitterRobotSensorEntityDescription( + name="Sleep Mode End Time", + key="sleep_mode_end_time", + device_class=SensorDeviceClass.TIMESTAMP, + should_report=lambda robot: robot.sleep_mode_enabled, + ), ] @@ -87,17 +103,8 @@ async def async_setup_entry( ) -> None: """Set up Litter-Robot sensors using config entry.""" hub: LitterRobotHub = hass.data[DOMAIN][entry.entry_id] - - entities = [] - for robot in hub.account.robots: - for (sensor_class, entity_type, sensor_attribute) in ROBOT_SENSORS: - entities.append( - sensor_class( - robot=robot, - entity_type=entity_type, - hub=hub, - sensor_attribute=sensor_attribute, - ) - ) - - async_add_entities(entities) + async_add_entities( + LitterRobotSensorEntity(robot=robot, hub=hub, description=description) + for description in ROBOT_SENSORS + for robot in hub.account.robots + ) diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index 839ffe2952d..e8ec5324ae6 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -67,6 +67,12 @@ def mock_account_with_sleeping_robot() -> MagicMock: return create_mock_account({"sleepModeActive": "102:00:00"}) +@pytest.fixture +def mock_account_with_sleep_disabled_robot() -> MagicMock: + """Mock a Litter-Robot account with a robot that has sleep mode disabled.""" + return create_mock_account({"sleepModeActive": "0"}) + + @pytest.fixture def mock_account_with_robot_not_recently_seen() -> MagicMock: """Mock a Litter-Robot account with a sleeping robot.""" diff --git a/tests/components/litterrobot/test_sensor.py b/tests/components/litterrobot/test_sensor.py index e3e62d5f5e4..ce91541f0d7 100644 --- a/tests/components/litterrobot/test_sensor.py +++ b/tests/components/litterrobot/test_sensor.py @@ -1,16 +1,19 @@ """Test the Litter-Robot sensor entity.""" -from unittest.mock import Mock +from unittest.mock import MagicMock -from homeassistant.components.litterrobot.sensor import LitterRobotSleepTimeSensor from homeassistant.components.sensor import DOMAIN as PLATFORM_DOMAIN, SensorDeviceClass -from homeassistant.const import PERCENTAGE +from homeassistant.const import PERCENTAGE, STATE_UNKNOWN +from homeassistant.core import HomeAssistant -from .conftest import create_mock_robot, setup_integration +from .conftest import setup_integration WASTE_DRAWER_ENTITY_ID = "sensor.test_waste_drawer" +SLEEP_START_TIME_ENTITY_ID = "sensor.test_sleep_mode_start_time" -async def test_waste_drawer_sensor(hass, mock_account): +async def test_waste_drawer_sensor( + hass: HomeAssistant, mock_account: MagicMock +) -> None: """Tests the waste drawer sensor entity was set up.""" await setup_integration(hass, mock_account, PLATFORM_DOMAIN) @@ -20,20 +23,21 @@ async def test_waste_drawer_sensor(hass, mock_account): assert sensor.attributes["unit_of_measurement"] == PERCENTAGE -async def test_sleep_time_sensor_with_none_state(hass): - """Tests the sleep mode start time sensor where sleep mode is inactive.""" - robot = create_mock_robot({"sleepModeActive": "0"}) - sensor = LitterRobotSleepTimeSensor( - robot, "Sleep Mode Start Time", Mock(), "sleep_mode_start_time" +async def test_sleep_time_sensor_with_sleep_disabled( + hass: HomeAssistant, mock_account_with_sleep_disabled_robot: MagicMock +) -> None: + """Tests the sleep mode start time sensor where sleep mode is disabled.""" + await setup_integration( + hass, mock_account_with_sleep_disabled_robot, PLATFORM_DOMAIN ) - sensor.hass = hass + sensor = hass.states.get(SLEEP_START_TIME_ENTITY_ID) assert sensor - assert sensor.state is None - assert sensor.device_class is SensorDeviceClass.TIMESTAMP + assert sensor.state == STATE_UNKNOWN + assert sensor.attributes["device_class"] == SensorDeviceClass.TIMESTAMP -async def test_gauge_icon(): +async def test_gauge_icon() -> None: """Test icon generator for gauge sensor.""" from homeassistant.components.litterrobot.sensor import icon_for_gauge_level From a746d7c1d7b8f1944e9caf923cd95b89bf9e9696 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 13 May 2022 01:40:00 +0200 Subject: [PATCH 390/930] Improve code quality in sql integration (#71705) --- homeassistant/components/sql/config_flow.py | 20 ++++++--------- homeassistant/components/sql/sensor.py | 22 ++++++++-------- homeassistant/components/sql/strings.json | 6 ++--- .../components/sql/translations/en.json | 6 ++--- tests/components/sql/test_config_flow.py | 1 - tests/components/sql/test_sensor.py | 25 ++++++++++++++++++- 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/sql/config_flow.py b/homeassistant/components/sql/config_flow.py index 9a6013e1d62..dc3a839ef1d 100644 --- a/homeassistant/components/sql/config_flow.py +++ b/homeassistant/components/sql/config_flow.py @@ -13,7 +13,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.components.recorder import CONF_DB_URL, DEFAULT_DB_FILE, DEFAULT_URL from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE_TEMPLATE -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import selector @@ -44,24 +44,23 @@ def validate_sql_select(value: str) -> str | None: def validate_query(db_url: str, query: str, column: str) -> bool: """Validate SQL query.""" - try: - engine = sqlalchemy.create_engine(db_url, future=True) - sessmaker = scoped_session(sessionmaker(bind=engine, future=True)) - except SQLAlchemyError as error: - raise error + engine = sqlalchemy.create_engine(db_url, future=True) + sessmaker = scoped_session(sessionmaker(bind=engine, future=True)) sess: scoped_session = sessmaker() try: result: Result = sess.execute(sqlalchemy.text(query)) - for res in result.mappings(): - data = res[column] - _LOGGER.debug("Return value from query: %s", data) except SQLAlchemyError as error: + _LOGGER.debug("Execution error %s", error) if sess: sess.close() raise ValueError(error) from error + for res in result.mappings(): + data = res[column] + _LOGGER.debug("Return value from query: %s", data) + if sess: sess.close() @@ -73,9 +72,6 @@ class SQLConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - entry: config_entries.ConfigEntry - hass: HomeAssistant - @staticmethod @callback def async_get_options_flow( diff --git a/homeassistant/components/sql/sensor.py b/homeassistant/components/sql/sensor.py index 4e3f9d15846..33ddafe2c0a 100644 --- a/homeassistant/components/sql/sensor.py +++ b/homeassistant/components/sql/sensor.py @@ -6,6 +6,7 @@ import decimal import logging import sqlalchemy +from sqlalchemy.engine import Result from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import scoped_session, sessionmaker import voluptuous as vol @@ -73,11 +74,11 @@ async def async_setup_platform( for query in config[CONF_QUERIES]: new_config = { CONF_DB_URL: config.get(CONF_DB_URL, default_db_url), - CONF_NAME: query.get(CONF_NAME), - CONF_QUERY: query.get(CONF_QUERY), + CONF_NAME: query[CONF_NAME], + CONF_QUERY: query[CONF_QUERY], CONF_UNIT_OF_MEASUREMENT: query.get(CONF_UNIT_OF_MEASUREMENT), CONF_VALUE_TEMPLATE: query.get(CONF_VALUE_TEMPLATE), - CONF_COLUMN_NAME: query.get(CONF_COLUMN_NAME), + CONF_COLUMN_NAME: query[CONF_COLUMN_NAME], } hass.async_create_task( hass.config_entries.flow.async_init( @@ -119,11 +120,10 @@ async def async_setup_entry( # MSSQL uses TOP and not LIMIT if not ("LIMIT" in query_str.upper() or "SELECT TOP" in query_str.upper()): - query_str = ( - query_str.replace("SELECT", "SELECT TOP 1") - if "mssql" in db_url - else query_str.replace(";", " LIMIT 1;") - ) + if "mssql" in db_url: + query_str = query_str.upper().replace("SELECT", "SELECT TOP 1") + else: + query_str = query_str.replace(";", "") + " LIMIT 1;" async_add_entities( [ @@ -179,7 +179,7 @@ class SQLSensor(SensorEntity): self._attr_extra_state_attributes = {} sess: scoped_session = self.sessionmaker() try: - result = sess.execute(sqlalchemy.text(self._query)) + result: Result = sess.execute(sqlalchemy.text(self._query)) except SQLAlchemyError as err: _LOGGER.error( "Error executing query %s: %s", @@ -188,10 +188,8 @@ class SQLSensor(SensorEntity): ) return - _LOGGER.debug("Result %s, ResultMapping %s", result, result.mappings()) - for res in result.mappings(): - _LOGGER.debug("result = %s", res.items()) + _LOGGER.debug("Query %s result in %s", self._query, res.items()) data = res[self._column_name] for key, value in res.items(): if isinstance(value, decimal.Decimal): diff --git a/homeassistant/components/sql/strings.json b/homeassistant/components/sql/strings.json index 8d3a194ac3e..2a300f75b3e 100644 --- a/homeassistant/components/sql/strings.json +++ b/homeassistant/components/sql/strings.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid", - "value_template_invalid": "Value Template invalid" + "query_invalid": "SQL Query invalid" }, "step": { "user": { @@ -52,8 +51,7 @@ }, "error": { "db_url_invalid": "[%key:component::sql::config::error::db_url_invalid%]", - "query_invalid": "[%key:component::sql::config::error::query_invalid%]", - "value_template_invalid": "[%key:component::sql::config::error::value_template_invalid%]" + "query_invalid": "[%key:component::sql::config::error::query_invalid%]" } } } diff --git a/homeassistant/components/sql/translations/en.json b/homeassistant/components/sql/translations/en.json index 4b72d024df4..9d05f771853 100644 --- a/homeassistant/components/sql/translations/en.json +++ b/homeassistant/components/sql/translations/en.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid", - "value_template_invalid": "Value Template invalid" + "query_invalid": "SQL Query invalid" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid", - "value_template_invalid": "Value Template invalid" + "query_invalid": "SQL Query invalid" }, "step": { "init": { diff --git a/tests/components/sql/test_config_flow.py b/tests/components/sql/test_config_flow.py index 3e065df0ebd..47957ead98e 100644 --- a/tests/components/sql/test_config_flow.py +++ b/tests/components/sql/test_config_flow.py @@ -42,7 +42,6 @@ async def test_form(hass: HomeAssistant) -> None: ENTRY_CONFIG, ) await hass.async_block_till_done() - print(ENTRY_CONFIG) assert result2["type"] == RESULT_TYPE_CREATE_ENTRY assert result2["title"] == "Get Value" diff --git a/tests/components/sql/test_sensor.py b/tests/components/sql/test_sensor.py index 0eb3bf70683..588e1c824b7 100644 --- a/tests/components/sql/test_sensor.py +++ b/tests/components/sql/test_sensor.py @@ -115,7 +115,30 @@ async def test_query_no_value( state = hass.states.get("sensor.count_tables") assert state.state == STATE_UNKNOWN - text = "SELECT 5 as value where 1=2 returned no results" + text = "SELECT 5 as value where 1=2 LIMIT 1; returned no results" + assert text in caplog.text + + +async def test_query_mssql_no_result( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test the SQL sensor with a query that returns no value.""" + config = { + "db_url": "mssql://", + "query": "SELECT 5 as value where 1=2", + "column": "value", + "name": "count_tables", + } + with patch("homeassistant.components.sql.sensor.sqlalchemy"), patch( + "homeassistant.components.sql.sensor.sqlalchemy.text", + return_value="SELECT TOP 1 5 as value where 1=2", + ): + await init_integration(hass, config) + + state = hass.states.get("sensor.count_tables") + assert state.state == STATE_UNKNOWN + + text = "SELECT TOP 1 5 AS VALUE WHERE 1=2 returned no results" assert text in caplog.text From 7e49ae64105aeb7d422fe8c999f86cddfa1c653c Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Fri, 13 May 2022 07:43:24 +0800 Subject: [PATCH 391/930] Add use_wallclock_as_timestamps option to generic (#71245) Co-authored-by: Paulus Schoutsen Co-authored-by: Paulus Schoutsen --- homeassistant/components/generic/camera.py | 5 ++++ .../components/generic/config_flow.py | 20 ++++++++++++- homeassistant/components/generic/const.py | 6 +++- homeassistant/components/generic/strings.json | 4 +++ .../components/generic/translations/en.json | 6 ++-- tests/components/generic/test_config_flow.py | 30 +++++++++++++++++++ 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index d20032e2607..197890efad3 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -37,6 +37,7 @@ from .const import ( CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DEFAULT_NAME, FFMPEG_OPTION_MAP, GET_IMAGE_TIMEOUT, @@ -160,6 +161,10 @@ class GenericCamera(Camera): CONF_RTSP_TRANSPORT ] self._auth = generate_auth(device_info) + if device_info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): + self.stream_options[ + FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] + ] = "1" self._last_url = None self._last_image = None diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 086262aa0a1..0a49393d9cc 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -41,6 +41,7 @@ from .const import ( CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DEFAULT_NAME, DOMAIN, FFMPEG_OPTION_MAP, @@ -64,6 +65,7 @@ SUPPORTED_IMAGE_TYPES = {"png", "jpeg", "gif", "svg+xml", "webp"} def build_schema( user_input: dict[str, Any] | MappingProxyType[str, Any], is_options_flow: bool = False, + show_advanced_options=False, ): """Create schema for camera config setup.""" spec = { @@ -106,6 +108,13 @@ def build_schema( default=user_input.get(CONF_LIMIT_REFETCH_TO_URL_CHANGE, False), ) ] = bool + if show_advanced_options: + spec[ + vol.Required( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + default=user_input.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS, False), + ) + ] = bool return vol.Schema(spec) @@ -199,6 +208,8 @@ async def async_test_stream(hass, info) -> dict[str, str]: } if rtsp_transport := info.get(CONF_RTSP_TRANSPORT): stream_options[FFMPEG_OPTION_MAP[CONF_RTSP_TRANSPORT]] = rtsp_transport + if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): + stream_options[FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS]] = "1" _LOGGER.debug("Attempting to open stream %s", stream_source) container = await hass.async_add_executor_job( partial( @@ -356,6 +367,9 @@ class GenericOptionsFlowHandler(OptionsFlow): ], CONF_FRAMERATE: user_input[CONF_FRAMERATE], CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL], + CONF_USE_WALLCLOCK_AS_TIMESTAMPS: user_input.get( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS + ), } return self.async_create_entry( title=title, @@ -363,6 +377,10 @@ class GenericOptionsFlowHandler(OptionsFlow): ) return self.async_show_form( step_id="init", - data_schema=build_schema(user_input or self.config_entry.options, True), + data_schema=build_schema( + user_input or self.config_entry.options, + True, + self.show_advanced_options, + ), errors=errors, ) diff --git a/homeassistant/components/generic/const.py b/homeassistant/components/generic/const.py index 60b4cec61a6..8ae5f16c4c4 100644 --- a/homeassistant/components/generic/const.py +++ b/homeassistant/components/generic/const.py @@ -8,7 +8,11 @@ CONF_STILL_IMAGE_URL = "still_image_url" CONF_STREAM_SOURCE = "stream_source" CONF_FRAMERATE = "framerate" CONF_RTSP_TRANSPORT = "rtsp_transport" -FFMPEG_OPTION_MAP = {CONF_RTSP_TRANSPORT: "rtsp_transport"} +CONF_USE_WALLCLOCK_AS_TIMESTAMPS = "use_wallclock_as_timestamps" +FFMPEG_OPTION_MAP = { + CONF_RTSP_TRANSPORT: "rtsp_transport", + CONF_USE_WALLCLOCK_AS_TIMESTAMPS: "use_wallclock_as_timestamps", +} RTSP_TRANSPORTS = { "tcp": "TCP", "udp": "UDP", diff --git a/homeassistant/components/generic/strings.json b/homeassistant/components/generic/strings.json index 01b1fe48a82..0954656f71d 100644 --- a/homeassistant/components/generic/strings.json +++ b/homeassistant/components/generic/strings.json @@ -55,9 +55,13 @@ "authentication": "[%key:component::generic::config::step::user::data::authentication%]", "limit_refetch_to_url_change": "[%key:component::generic::config::step::user::data::limit_refetch_to_url_change%]", "password": "[%key:common::config_flow::data::password%]", + "use_wallclock_as_timestamps": "Use wallclock as timestamps", "username": "[%key:common::config_flow::data::username%]", "framerate": "[%key:component::generic::config::step::user::data::framerate%]", "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" + }, + "data_description": { + "use_wallclock_as_timestamps": "This option may correct segmenting or crashing issues arising from buggy timestamp implementations on some cameras" } }, "content_type": { diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index b158488f178..b552c780d29 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Authentication", - "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", @@ -72,15 +71,18 @@ "init": { "data": { "authentication": "Authentication", - "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", "rtsp_transport": "RTSP transport protocol", "still_image_url": "Still Image URL (e.g. http://...)", "stream_source": "Stream Source URL (e.g. rtsp://...)", + "use_wallclock_as_timestamps": "Use wallclock as timestamps", "username": "Username", "verify_ssl": "Verify SSL certificate" + }, + "data_description": { + "use_wallclock_as_timestamps": "This option may correct segmenting or crashing issues arising from buggy timestamp implementations on some cameras" } } } diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index 457cac26aa5..dd53cb8548e 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -18,6 +18,7 @@ from homeassistant.components.generic.const import ( CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DOMAIN, ) from homeassistant.const import ( @@ -653,3 +654,32 @@ async def test_migrate_existing_ids(hass) -> None: entity_entry = registry.async_get(entity_id) assert entity_entry.unique_id == new_unique_id + + +@respx.mock +async def test_use_wallclock_as_timestamps_option(hass, fakeimg_png, mock_av_open): + """Test the use_wallclock_as_timestamps option flow.""" + + mock_entry = MockConfigEntry( + title="Test Camera", + domain=DOMAIN, + data={}, + options=TESTDATA, + ) + + with mock_av_open: + mock_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init( + mock_entry.entry_id, context={"show_advanced_options": True} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True, **TESTDATA}, + ) + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY From d76ff7d5a272419ad5329211c621f4d6218f1c63 Mon Sep 17 00:00:00 2001 From: Paul Annekov Date: Fri, 13 May 2022 02:45:39 +0300 Subject: [PATCH 392/930] Changed API for Ukraine Alarm (#71754) --- .../components/ukraine_alarm/__init__.py | 14 +- .../components/ukraine_alarm/config_flow.py | 67 ++++---- .../components/ukraine_alarm/manifest.json | 2 +- .../components/ukraine_alarm/strings.json | 17 +- .../ukraine_alarm/translations/en.json | 15 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../ukraine_alarm/test_config_flow.py | 161 ++++++------------ 8 files changed, 99 insertions(+), 181 deletions(-) diff --git a/homeassistant/components/ukraine_alarm/__init__.py b/homeassistant/components/ukraine_alarm/__init__.py index b2b2ff4162f..587854a3a7e 100644 --- a/homeassistant/components/ukraine_alarm/__init__.py +++ b/homeassistant/components/ukraine_alarm/__init__.py @@ -7,10 +7,10 @@ from typing import Any import aiohttp from aiohttp import ClientSession -from ukrainealarm.client import Client +from uasiren.client import Client from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_KEY, CONF_REGION +from homeassistant.const import CONF_REGION from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -24,14 +24,11 @@ UPDATE_INTERVAL = timedelta(seconds=10) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Ukraine Alarm as config entry.""" - api_key = entry.data[CONF_API_KEY] region_id = entry.data[CONF_REGION] websession = async_get_clientsession(hass) - coordinator = UkraineAlarmDataUpdateCoordinator( - hass, websession, api_key, region_id - ) + coordinator = UkraineAlarmDataUpdateCoordinator(hass, websession, region_id) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator @@ -56,19 +53,18 @@ class UkraineAlarmDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): self, hass: HomeAssistant, session: ClientSession, - api_key: str, region_id: str, ) -> None: """Initialize.""" self.region_id = region_id - self.ukrainealarm = Client(session, api_key) + self.uasiren = Client(session) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) async def _async_update_data(self) -> dict[str, Any]: """Update data via library.""" try: - res = await self.ukrainealarm.get_alerts(self.region_id) + res = await self.uasiren.get_alerts(self.region_id) except aiohttp.ClientError as error: raise UpdateFailed(f"Error fetching alerts from API: {error}") from error diff --git a/homeassistant/components/ukraine_alarm/config_flow.py b/homeassistant/components/ukraine_alarm/config_flow.py index dcf41658dfb..4f1e1c5cf23 100644 --- a/homeassistant/components/ukraine_alarm/config_flow.py +++ b/homeassistant/components/ukraine_alarm/config_flow.py @@ -2,17 +2,20 @@ from __future__ import annotations import asyncio +import logging import aiohttp -from ukrainealarm.client import Client +from uasiren.client import Client import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_REGION +from homeassistant.const import CONF_NAME, CONF_REGION from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DOMAIN +_LOGGER = logging.getLogger(__name__) + class UkraineAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Ukraine Alarm.""" @@ -21,54 +24,47 @@ class UkraineAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize a new UkraineAlarmConfigFlow.""" - self.api_key = None self.states = None self.selected_region = None async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" - errors = {} - if user_input is not None: + if len(self._async_current_entries()) == 5: + return self.async_abort(reason="max_regions") + + if not self.states: websession = async_get_clientsession(self.hass) + reason = None + unknown_err_msg = None try: - regions = await Client( - websession, user_input[CONF_API_KEY] - ).get_regions() + regions = await Client(websession).get_regions() except aiohttp.ClientResponseError as ex: - errors["base"] = "invalid_api_key" if ex.status == 401 else "unknown" + if ex.status == 429: + reason = "rate_limit" + else: + reason = "unknown" + unknown_err_msg = str(ex) except aiohttp.ClientConnectionError: - errors["base"] = "cannot_connect" - except aiohttp.ClientError: - errors["base"] = "unknown" + reason = "cannot_connect" + except aiohttp.ClientError as ex: + reason = "unknown" + unknown_err_msg = str(ex) except asyncio.TimeoutError: - errors["base"] = "timeout" + reason = "timeout" - if not errors and not regions: - errors["base"] = "unknown" + if not reason and not regions: + reason = "unknown" + unknown_err_msg = "no regions returned" - if not errors: - self.api_key = user_input[CONF_API_KEY] - self.states = regions["states"] - return await self.async_step_state() + if unknown_err_msg: + _LOGGER.error("Failed to connect to the service: %s", unknown_err_msg) - schema = vol.Schema( - { - vol.Required(CONF_API_KEY): str, - } - ) + if reason: + return self.async_abort(reason=reason) + self.states = regions["states"] - return self.async_show_form( - step_id="user", - data_schema=schema, - description_placeholders={"api_url": "https://api.ukrainealarm.com/"}, - errors=errors, - last_step=False, - ) - - async def async_step_state(self, user_input=None): - """Handle user-chosen state.""" - return await self._handle_pick_region("state", "district", user_input) + return await self._handle_pick_region("user", "district", user_input) async def async_step_district(self, user_input=None): """Handle user-chosen district.""" @@ -126,7 +122,6 @@ class UkraineAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_create_entry( title=self.selected_region["regionName"], data={ - CONF_API_KEY: self.api_key, CONF_REGION: self.selected_region["regionId"], CONF_NAME: self.selected_region["regionName"], }, diff --git a/homeassistant/components/ukraine_alarm/manifest.json b/homeassistant/components/ukraine_alarm/manifest.json index 08dad9960b5..5592ac774a4 100644 --- a/homeassistant/components/ukraine_alarm/manifest.json +++ b/homeassistant/components/ukraine_alarm/manifest.json @@ -3,7 +3,7 @@ "name": "Ukraine Alarm", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ukraine_alarm", - "requirements": ["ukrainealarm==0.0.1"], + "requirements": ["uasiren==0.0.1"], "codeowners": ["@PaulAnnekov"], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/ukraine_alarm/strings.json b/homeassistant/components/ukraine_alarm/strings.json index 79f81e71b08..6831d66adb3 100644 --- a/homeassistant/components/ukraine_alarm/strings.json +++ b/homeassistant/components/ukraine_alarm/strings.json @@ -1,22 +1,15 @@ { "config": { "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_location%]" - }, - "error": { - "invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]", + "max_regions": "Max 5 regions can be configured", + "already_configured": "[%key:common::config_flow::abort::already_configured_location%]", + "rate_limit": "Too much requests", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "unknown": "[%key:common::config_flow::error::unknown%]", "timeout": "[%key:common::config_flow::error::timeout_connect%]" }, "step": { "user": { - "data": { - "api_key": "[%key:common::config_flow::data::api_key%]" - }, - "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}" - }, - "state": { "data": { "region": "Region" }, @@ -24,13 +17,13 @@ }, "district": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "[%key:component::ukraine_alarm::config::step::user::data::region%]" }, "description": "If you want to monitor not only state, choose its specific district" }, "community": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "[%key:component::ukraine_alarm::config::step::user::data::region%]" }, "description": "If you want to monitor not only state and district, choose its specific community" } diff --git a/homeassistant/components/ukraine_alarm/translations/en.json b/homeassistant/components/ukraine_alarm/translations/en.json index e7b2a9edcdc..857311ea3e7 100644 --- a/homeassistant/components/ukraine_alarm/translations/en.json +++ b/homeassistant/components/ukraine_alarm/translations/en.json @@ -1,11 +1,10 @@ { "config": { "abort": { - "already_configured": "Location is already configured" - }, - "error": { + "already_configured": "Location is already configured", "cannot_connect": "Failed to connect", - "invalid_api_key": "Invalid API key", + "max_regions": "Max 5 regions can be configured", + "rate_limit": "Too much requests", "timeout": "Timeout establishing connection", "unknown": "Unexpected error" }, @@ -22,17 +21,11 @@ }, "description": "If you want to monitor not only state, choose its specific district" }, - "state": { + "user": { "data": { "region": "Region" }, "description": "Choose state to monitor" - }, - "user": { - "data": { - "api_key": "API Key" - }, - "description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}" } } } diff --git a/requirements_all.txt b/requirements_all.txt index ac6727a95aa..e0c864fea9d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2349,7 +2349,7 @@ twitchAPI==2.5.2 uEagle==0.0.2 # homeassistant.components.ukraine_alarm -ukrainealarm==0.0.1 +uasiren==0.0.1 # homeassistant.components.unifiprotect unifi-discovery==1.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3289356d23a..e0f5459d43a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1531,7 +1531,7 @@ twitchAPI==2.5.2 uEagle==0.0.2 # homeassistant.components.ukraine_alarm -ukrainealarm==0.0.1 +uasiren==0.0.1 # homeassistant.components.unifiprotect unifi-discovery==1.1.2 diff --git a/tests/components/ukraine_alarm/test_config_flow.py b/tests/components/ukraine_alarm/test_config_flow.py index 3832e6a9fb6..7369816fdc7 100644 --- a/tests/components/ukraine_alarm/test_config_flow.py +++ b/tests/components/ukraine_alarm/test_config_flow.py @@ -3,15 +3,20 @@ import asyncio from collections.abc import Generator from unittest.mock import AsyncMock, patch -from aiohttp import ClientConnectionError, ClientError, ClientResponseError +from aiohttp import ClientConnectionError, ClientError, ClientResponseError, RequestInfo import pytest +from yarl import URL from homeassistant import config_entries from homeassistant.components.ukraine_alarm.const import DOMAIN from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) -MOCK_API_KEY = "mock-api-key" +from tests.common import MockConfigEntry def _region(rid, recurse=0, depth=0): @@ -57,12 +62,7 @@ async def test_state(hass: HomeAssistant) -> None: ) assert result["type"] == RESULT_TYPE_FORM - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result2["type"] == RESULT_TYPE_FORM with patch( @@ -80,7 +80,6 @@ async def test_state(hass: HomeAssistant) -> None: assert result3["type"] == RESULT_TYPE_CREATE_ENTRY assert result3["title"] == "State 1" assert result3["data"] == { - "api_key": MOCK_API_KEY, "region": "1", "name": result3["title"], } @@ -94,12 +93,7 @@ async def test_state_district(hass: HomeAssistant) -> None: ) assert result["type"] == RESULT_TYPE_FORM - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result2["type"] == RESULT_TYPE_FORM result3 = await hass.config_entries.flow.async_configure( @@ -125,7 +119,6 @@ async def test_state_district(hass: HomeAssistant) -> None: assert result4["type"] == RESULT_TYPE_CREATE_ENTRY assert result4["title"] == "District 2.2" assert result4["data"] == { - "api_key": MOCK_API_KEY, "region": "2.2", "name": result4["title"], } @@ -139,12 +132,7 @@ async def test_state_district_pick_region(hass: HomeAssistant) -> None: ) assert result["type"] == RESULT_TYPE_FORM - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) + result2 = await hass.config_entries.flow.async_configure(result["flow_id"]) assert result2["type"] == RESULT_TYPE_FORM result3 = await hass.config_entries.flow.async_configure( @@ -170,7 +158,6 @@ async def test_state_district_pick_region(hass: HomeAssistant) -> None: assert result4["type"] == RESULT_TYPE_CREATE_ENTRY assert result4["title"] == "State 2" assert result4["data"] == { - "api_key": MOCK_API_KEY, "region": "2", "name": result4["title"], } @@ -186,9 +173,6 @@ async def test_state_district_community(hass: HomeAssistant) -> None: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, ) assert result2["type"] == RESULT_TYPE_FORM @@ -223,132 +207,89 @@ async def test_state_district_community(hass: HomeAssistant) -> None: assert result5["type"] == RESULT_TYPE_CREATE_ENTRY assert result5["title"] == "Community 3.2.1" assert result5["data"] == { - "api_key": MOCK_API_KEY, "region": "3.2.1", "name": result5["title"], } assert len(mock_setup_entry.mock_calls) == 1 -async def test_invalid_api(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: - """Test we can create entry for just region.""" +async def test_max_regions(hass: HomeAssistant) -> None: + """Test max regions config.""" + for i in range(5): + MockConfigEntry( + domain=DOMAIN, + unique_id=i, + ).add_to_hass(hass) + result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - mock_get_regions.side_effect = ClientResponseError(None, None, status=401) + assert result["type"] == "abort" + assert result["reason"] == "max_regions" - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, + +async def test_rate_limit(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: + """Test rate limit error.""" + mock_get_regions.side_effect = ClientResponseError(None, None, status=429) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "invalid_api_key"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "rate_limit" async def test_server_error(hass: HomeAssistant, mock_get_regions) -> None: - """Test we can create entry for just region.""" + """Test server error.""" + mock_get_regions.side_effect = ClientResponseError( + RequestInfo(None, None, None, real_url=URL("/regions")), None, status=500 + ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - - mock_get_regions.side_effect = ClientResponseError(None, None, status=500) - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "unknown"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "unknown" async def test_cannot_connect(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: - """Test we can create entry for just region.""" + """Test connection error.""" + mock_get_regions.side_effect = ClientConnectionError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - - mock_get_regions.side_effect = ClientConnectionError - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "cannot_connect"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "cannot_connect" async def test_unknown_client_error( hass: HomeAssistant, mock_get_regions: AsyncMock ) -> None: - """Test we can create entry for just region.""" + """Test client error.""" + mock_get_regions.side_effect = ClientError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - - mock_get_regions.side_effect = ClientError - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "unknown"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "unknown" async def test_timeout_error(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None: - """Test we can create entry for just region.""" + """Test timeout error.""" + mock_get_regions.side_effect = asyncio.TimeoutError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - - mock_get_regions.side_effect = asyncio.TimeoutError - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "timeout"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "timeout" async def test_no_regions_returned( hass: HomeAssistant, mock_get_regions: AsyncMock ) -> None: - """Test we can create entry for just region.""" + """Test regions not returned.""" + mock_get_regions.return_value = {} result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == RESULT_TYPE_FORM - - mock_get_regions.return_value = {} - - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "api_key": MOCK_API_KEY, - }, - ) - assert result2["type"] == RESULT_TYPE_FORM - assert result2["step_id"] == "user" - assert result2["errors"] == {"base": "unknown"} + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "unknown" From 8ab27f26b90bbb12898ecb10ff08e7225898b5e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 May 2022 20:11:43 -0400 Subject: [PATCH 393/930] Use ciso8601 for parsing datetimes with sqlalchemy sqlite dialect (#71766) --- homeassistant/components/recorder/models.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 38d6b3319aa..6edee252ea3 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -6,6 +6,7 @@ import json import logging from typing import Any, TypedDict, cast, overload +import ciso8601 from fnvhash import fnv1a_32 from sqlalchemy import ( BigInteger, @@ -22,7 +23,7 @@ from sqlalchemy import ( Text, distinct, ) -from sqlalchemy.dialects import mysql, oracle, postgresql +from sqlalchemy.dialects import mysql, oracle, postgresql, sqlite from sqlalchemy.engine.row import Row from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import declarative_base, relationship @@ -91,8 +92,18 @@ TABLES_TO_CHECK = [ EMPTY_JSON_OBJECT = "{}" -DATETIME_TYPE = DateTime(timezone=True).with_variant( - mysql.DATETIME(timezone=True, fsp=6), "mysql" +class FAST_PYSQLITE_DATETIME(sqlite.DATETIME): # type: ignore[misc] + """Use ciso8601 to parse datetimes instead of sqlalchemy built-in regex.""" + + def result_processor(self, dialect, coltype): # type: ignore[no-untyped-def] + """Offload the datetime parsing to ciso8601.""" + return lambda value: None if value is None else ciso8601.parse_datetime(value) + + +DATETIME_TYPE = ( + DateTime(timezone=True) + .with_variant(mysql.DATETIME(timezone=True, fsp=6), "mysql") + .with_variant(FAST_PYSQLITE_DATETIME(), "sqlite") ) DOUBLE_TYPE = ( Float() From 1d9fb4bca871f97109684419f0f9526a0c151f2d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 May 2022 20:12:50 -0400 Subject: [PATCH 394/930] Fix process_datetime_to_timestamp and add test coverage (#71755) --- homeassistant/components/recorder/models.py | 24 +++++-- tests/components/recorder/test_models.py | 72 +++++++++++++++++++++ 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 6edee252ea3..38b03eb824e 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -55,6 +55,10 @@ SCHEMA_VERSION = 28 _LOGGER = logging.getLogger(__name__) +# EPOCHORDINAL is not exposed as a constant +# https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L12 +EPOCHORDINAL = datetime(1970, 1, 1).toordinal() + DB_TIMEZONE = "+00:00" TABLE_EVENTS = "events" @@ -630,10 +634,22 @@ def process_timestamp_to_utc_isoformat(ts: datetime | None) -> str | None: def process_datetime_to_timestamp(ts: datetime) -> float: - """Process a timestamp into a unix timestamp.""" - if ts.tzinfo == dt_util.UTC: - return ts.timestamp() - return ts.replace(tzinfo=dt_util.UTC).timestamp() + """Process a datebase datetime to epoch. + + Mirrors the behavior of process_timestamp_to_utc_isoformat + except it returns the epoch time. + """ + if ts.tzinfo is None: + # Taken from + # https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L185 + return ( + (ts.toordinal() - EPOCHORDINAL) * 86400 + + ts.hour * 3600 + + ts.minute * 60 + + ts.second + + (ts.microsecond / 1000000) + ) + return ts.timestamp() class LazyState(State): diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index 874d84ef2ad..ca68d5951d8 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -2,6 +2,7 @@ from datetime import datetime, timedelta from unittest.mock import PropertyMock +from freezegun import freeze_time import pytest from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker @@ -14,6 +15,7 @@ from homeassistant.components.recorder.models import ( RecorderRuns, StateAttributes, States, + process_datetime_to_timestamp, process_timestamp, process_timestamp_to_utc_isoformat, ) @@ -333,3 +335,73 @@ async def test_lazy_state_handles_same_last_updated_and_last_changed(caplog): "last_updated": "2020-06-12T03:04:01.000323+00:00", "state": "off", } + + +@pytest.mark.parametrize( + "time_zone", ["Europe/Berlin", "America/Chicago", "US/Hawaii", "UTC"] +) +def test_process_datetime_to_timestamp(time_zone, hass): + """Test we can handle processing database datatimes to timestamps.""" + hass.config.set_time_zone(time_zone) + utc_now = dt_util.utcnow() + assert process_datetime_to_timestamp(utc_now) == utc_now.timestamp() + now = dt_util.now() + assert process_datetime_to_timestamp(now) == now.timestamp() + + +@pytest.mark.parametrize( + "time_zone", ["Europe/Berlin", "America/Chicago", "US/Hawaii", "UTC"] +) +def test_process_datetime_to_timestamp_freeze_time(time_zone, hass): + """Test we can handle processing database datatimes to timestamps. + + This test freezes time to make sure everything matches. + """ + hass.config.set_time_zone(time_zone) + utc_now = dt_util.utcnow() + with freeze_time(utc_now): + epoch = utc_now.timestamp() + assert process_datetime_to_timestamp(dt_util.utcnow()) == epoch + now = dt_util.now() + assert process_datetime_to_timestamp(now) == epoch + + +@pytest.mark.parametrize( + "time_zone", ["Europe/Berlin", "America/Chicago", "US/Hawaii", "UTC"] +) +async def test_process_datetime_to_timestamp_mirrors_utc_isoformat_behavior( + time_zone, hass +): + """Test process_datetime_to_timestamp mirrors process_timestamp_to_utc_isoformat.""" + hass.config.set_time_zone(time_zone) + datetime_with_tzinfo = datetime(2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC) + datetime_without_tzinfo = datetime(2016, 7, 9, 11, 0, 0) + est = dt_util.get_time_zone("US/Eastern") + datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est) + est = dt_util.get_time_zone("US/Eastern") + datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est) + nst = dt_util.get_time_zone("Canada/Newfoundland") + datetime_nst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=nst) + hst = dt_util.get_time_zone("US/Hawaii") + datetime_hst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=hst) + + assert ( + process_datetime_to_timestamp(datetime_with_tzinfo) + == dt_util.parse_datetime("2016-07-09T11:00:00+00:00").timestamp() + ) + assert ( + process_datetime_to_timestamp(datetime_without_tzinfo) + == dt_util.parse_datetime("2016-07-09T11:00:00+00:00").timestamp() + ) + assert ( + process_datetime_to_timestamp(datetime_est_timezone) + == dt_util.parse_datetime("2016-07-09T15:00:00+00:00").timestamp() + ) + assert ( + process_datetime_to_timestamp(datetime_nst_timezone) + == dt_util.parse_datetime("2016-07-09T13:30:00+00:00").timestamp() + ) + assert ( + process_datetime_to_timestamp(datetime_hst_timezone) + == dt_util.parse_datetime("2016-07-09T21:00:00+00:00").timestamp() + ) From 24a0007785e65e1043e0bdd55e7524ef0a7ccdad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 May 2022 20:21:14 -0400 Subject: [PATCH 395/930] Add additional context data to logbook events (#71721) --- .../components/automation/logbook.py | 2 +- homeassistant/components/deconz/logbook.py | 10 +- homeassistant/components/doorbird/logbook.py | 2 +- .../components/homeassistant/logbook.py | 39 +++++ homeassistant/components/logbook/__init__.py | 135 +++++++-------- homeassistant/components/logbook/queries.py | 4 + homeassistant/components/shelly/logbook.py | 2 +- tests/components/automation/test_init.py | 4 +- tests/components/automation/test_logbook.py | 4 +- tests/components/deconz/test_logbook.py | 12 +- tests/components/logbook/test_init.py | 156 ++++++++++++++---- tests/components/shelly/test_logbook.py | 8 +- 12 files changed, 254 insertions(+), 124 deletions(-) create mode 100644 homeassistant/components/homeassistant/logbook.py diff --git a/homeassistant/components/automation/logbook.py b/homeassistant/components/automation/logbook.py index 86fb797ea31..97a859d25b0 100644 --- a/homeassistant/components/automation/logbook.py +++ b/homeassistant/components/automation/logbook.py @@ -15,7 +15,7 @@ def async_describe_events(hass: HomeAssistant, async_describe_event): # type: i def async_describe_logbook_event(event: LazyEventPartialState): # type: ignore[no-untyped-def] """Describe a logbook event.""" data = event.data - message = "has been triggered" + message = "triggered" if ATTR_SOURCE in data: message = f"{message} by {data[ATTR_SOURCE]}" diff --git a/homeassistant/components/deconz/logbook.py b/homeassistant/components/deconz/logbook.py index 3dedeb4bfac..e67e07d2222 100644 --- a/homeassistant/components/deconz/logbook.py +++ b/homeassistant/components/deconz/logbook.py @@ -136,7 +136,7 @@ def async_describe_events( return { "name": f"{deconz_alarm_event.device.name}", - "message": f"fired event '{data}'.", + "message": f"fired event '{data}'", } @callback @@ -158,26 +158,26 @@ def async_describe_events( if not data: return { "name": f"{deconz_event.device.name}", - "message": "fired an unknown event.", + "message": "fired an unknown event", } # No device event match if not action: return { "name": f"{deconz_event.device.name}", - "message": f"fired event '{data}'.", + "message": f"fired event '{data}'", } # Gesture event if not interface: return { "name": f"{deconz_event.device.name}", - "message": f"fired event '{ACTIONS[action]}'.", + "message": f"fired event '{ACTIONS[action]}'", } return { "name": f"{deconz_event.device.name}", - "message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired.", + "message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired", } async_describe_event( diff --git a/homeassistant/components/doorbird/logbook.py b/homeassistant/components/doorbird/logbook.py index a4889360d81..fbd6c670a8d 100644 --- a/homeassistant/components/doorbird/logbook.py +++ b/homeassistant/components/doorbird/logbook.py @@ -17,7 +17,7 @@ def async_describe_events(hass, async_describe_event): return { "name": "Doorbird", - "message": f"Event {event.event_type} was fired.", + "message": f"Event {event.event_type} was fired", "entity_id": hass.data[DOMAIN][DOOR_STATION_EVENT_ENTITY_IDS].get( doorbird_event, event.data.get(ATTR_ENTITY_ID) ), diff --git a/homeassistant/components/homeassistant/logbook.py b/homeassistant/components/homeassistant/logbook.py new file mode 100644 index 00000000000..229fb24cb27 --- /dev/null +++ b/homeassistant/components/homeassistant/logbook.py @@ -0,0 +1,39 @@ +"""Describe homeassistant logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.components.logbook import ( + LOGBOOK_ENTRY_ICON, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) +from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP +from homeassistant.core import Event, HomeAssistant, callback + +from . import DOMAIN + +EVENT_TO_NAME = { + EVENT_HOMEASSISTANT_STOP: "stopped", + EVENT_HOMEASSISTANT_START: "started", +} + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + + @callback + def async_describe_hass_event(event: Event) -> dict[str, str]: + """Describe homeassisant logbook event.""" + return { + LOGBOOK_ENTRY_NAME: "Home Assistant", + LOGBOOK_ENTRY_MESSAGE: EVENT_TO_NAME[event.event_type], + LOGBOOK_ENTRY_ICON: "mdi:home-assistant", + } + + async_describe_event(DOMAIN, EVENT_HOMEASSISTANT_STOP, async_describe_hass_event) + async_describe_event(DOMAIN, EVENT_HOMEASSISTANT_START, async_describe_hass_event) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index df8143d39fb..80748768c55 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -37,13 +37,10 @@ from homeassistant.const import ( ATTR_NAME, ATTR_SERVICE, EVENT_CALL_SERVICE, - EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, EVENT_LOGBOOK_ENTRY, EVENT_STATE_CHANGED, ) from homeassistant.core import ( - DOMAIN as HA_DOMAIN, Context, Event, HomeAssistant, @@ -70,7 +67,6 @@ from .queries import statement_for_request _LOGGER = logging.getLogger(__name__) - FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') @@ -79,19 +75,28 @@ ATTR_MESSAGE = "message" DOMAIN = "logbook" -HA_DOMAIN_ENTITY_ID = f"{HA_DOMAIN}._" - CONFIG_SCHEMA = vol.Schema( {DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA ) -HOMEASSISTANT_EVENTS = {EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP} +CONTEXT_USER_ID = "context_user_id" +CONTEXT_ENTITY_ID = "context_entity_id" +CONTEXT_ENTITY_ID_NAME = "context_entity_id_name" +CONTEXT_EVENT_TYPE = "context_event_type" +CONTEXT_DOMAIN = "context_domain" +CONTEXT_SERVICE = "context_service" +CONTEXT_NAME = "context_name" +CONTEXT_MESSAGE = "context_message" -ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = ( - EVENT_LOGBOOK_ENTRY, - EVENT_CALL_SERVICE, - *HOMEASSISTANT_EVENTS, -) +LOGBOOK_ENTRY_DOMAIN = "domain" +LOGBOOK_ENTRY_ENTITY_ID = "entity_id" +LOGBOOK_ENTRY_ICON = "icon" +LOGBOOK_ENTRY_MESSAGE = "message" +LOGBOOK_ENTRY_NAME = "name" +LOGBOOK_ENTRY_STATE = "state" +LOGBOOK_ENTRY_WHEN = "when" + +ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE} SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} @@ -133,12 +138,12 @@ def async_log_entry( context: Context | None = None, ) -> None: """Add an entry to the logbook.""" - data = {ATTR_NAME: name, ATTR_MESSAGE: message} + data = {LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: message} if domain is not None: - data[ATTR_DOMAIN] = domain + data[LOGBOOK_ENTRY_DOMAIN] = domain if entity_id is not None: - data[ATTR_ENTITY_ID] = entity_id + data[LOGBOOK_ENTRY_ENTITY_ID] = entity_id hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data, context=context) @@ -162,7 +167,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: message.hass = hass message = message.async_render(parse_result=False) - async_log_entry(hass, name, message, domain, entity_id) + async_log_entry(hass, name, message, domain, entity_id, service.context) frontend.async_register_built_in_panel( hass, "logbook", "logbook", "hass:format-list-bulleted-type" @@ -375,13 +380,13 @@ def _humanify( continue data = { - "when": format_time(row), - "name": entity_name_cache.get(entity_id, row), - "state": row.state, - "entity_id": entity_id, + LOGBOOK_ENTRY_WHEN: format_time(row), + LOGBOOK_ENTRY_NAME: entity_name_cache.get(entity_id, row), + LOGBOOK_ENTRY_STATE: row.state, + LOGBOOK_ENTRY_ENTITY_ID: entity_id, } if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): - data["icon"] = icon + data[LOGBOOK_ENTRY_ICON] = icon context_augmenter.augment(data, entity_id, row) yield data @@ -389,26 +394,11 @@ def _humanify( elif event_type in external_events: domain, describe_event = external_events[event_type] data = describe_event(event_cache.get(row)) - data["when"] = format_time(row) - data["domain"] = domain + data[LOGBOOK_ENTRY_WHEN] = format_time(row) + data[LOGBOOK_ENTRY_DOMAIN] = domain context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row) yield data - elif event_type == EVENT_HOMEASSISTANT_START: - yield { - "when": format_time(row), - "name": "Home Assistant", - "message": "started", - "domain": HA_DOMAIN, - } - elif event_type == EVENT_HOMEASSISTANT_STOP: - yield { - "when": format_time(row), - "name": "Home Assistant", - "message": "stopped", - "domain": HA_DOMAIN, - } - elif event_type == EVENT_LOGBOOK_ENTRY: event = event_cache.get(row) event_data = event.data @@ -419,11 +409,11 @@ def _humanify( domain = split_entity_id(str(entity_id))[0] data = { - "when": format_time(row), - "name": event_data.get(ATTR_NAME), - "message": event_data.get(ATTR_MESSAGE), - "domain": domain, - "entity_id": entity_id, + LOGBOOK_ENTRY_WHEN: format_time(row), + LOGBOOK_ENTRY_NAME: event_data.get(ATTR_NAME), + LOGBOOK_ENTRY_MESSAGE: event_data.get(ATTR_MESSAGE), + LOGBOOK_ENTRY_DOMAIN: domain, + LOGBOOK_ENTRY_ENTITY_ID: entity_id, } context_augmenter.augment(data, entity_id, row) yield data @@ -505,9 +495,6 @@ def _keep_row( row: Row, entities_filter: EntityFilter | Callable[[str], bool] | None = None, ) -> bool: - if event_type in HOMEASSISTANT_EVENTS: - return entities_filter is None or entities_filter(HA_DOMAIN_ENTITY_ID) - if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): return entities_filter is None or entities_filter(entity_id) @@ -544,7 +531,7 @@ class ContextAugmenter: def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None: """Augment data from the row and cache.""" if context_user_id := row.context_user_id: - data["context_user_id"] = context_user_id + data[CONTEXT_USER_ID] = context_user_id if not (context_row := self.context_lookup.get(row.context_id)): return @@ -567,43 +554,40 @@ class ContextAugmenter: # State change if context_entity_id := context_row.entity_id: - data["context_entity_id"] = context_entity_id - data["context_entity_id_name"] = self.entity_name_cache.get( + data[CONTEXT_ENTITY_ID] = context_entity_id + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( context_entity_id, context_row ) - data["context_event_type"] = event_type + data[CONTEXT_EVENT_TYPE] = event_type return # Call service if event_type == EVENT_CALL_SERVICE: event = self.event_cache.get(context_row) event_data = event.data - data["context_domain"] = event_data.get(ATTR_DOMAIN) - data["context_service"] = event_data.get(ATTR_SERVICE) - data["context_event_type"] = event_type + data[CONTEXT_DOMAIN] = event_data.get(ATTR_DOMAIN) + data[CONTEXT_SERVICE] = event_data.get(ATTR_SERVICE) + data[CONTEXT_EVENT_TYPE] = event_type return - if not entity_id: + if event_type not in self.external_events: return - attr_entity_id = _row_event_data_extract(context_row, ENTITY_ID_JSON_EXTRACT) - if attr_entity_id is None or ( - event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id - ): + domain, describe_event = self.external_events[event_type] + data[CONTEXT_EVENT_TYPE] = event_type + data[CONTEXT_DOMAIN] = domain + event = self.event_cache.get(context_row) + described = describe_event(event) + if name := described.get(ATTR_NAME): + data[CONTEXT_NAME] = name + if message := described.get(ATTR_MESSAGE): + data[CONTEXT_MESSAGE] = message + if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): return - - data["context_entity_id"] = attr_entity_id - data["context_entity_id_name"] = self.entity_name_cache.get( + data[CONTEXT_ENTITY_ID] = attr_entity_id + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( attr_entity_id, context_row ) - data["context_event_type"] = event_type - - if event_type in self.external_events: - domain, describe_event = self.external_events[event_type] - data["context_domain"] = domain - event = self.event_cache.get(context_row) - if name := describe_event(event).get(ATTR_NAME): - data["context_name"] = name def _is_sensor_continuous( @@ -627,11 +611,14 @@ def _is_sensor_continuous( def _rows_match(row: Row, other_row: Row) -> bool: """Check of rows match by using the same method as Events __hash__.""" - return bool( - row.event_type == other_row.event_type - and row.context_id == other_row.context_id - and row.time_fired == other_row.time_fired - ) + if ( + (state_id := row.state_id) is not None + and state_id == other_row.state_id + or (event_id := row.event_id) is not None + and event_id == other_row.event_id + ): + return True + return False def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None: diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 08f6d759d25..2456c73fe22 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -33,6 +33,7 @@ UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" EVENT_COLUMNS = ( + Events.event_id.label("event_id"), Events.event_type.label("event_type"), Events.event_data.label("event_data"), Events.time_fired.label("time_fired"), @@ -42,6 +43,7 @@ EVENT_COLUMNS = ( ) STATE_COLUMNS = ( + States.state_id.label("state_id"), States.state.label("state"), States.entity_id.label("entity_id"), States.attributes.label("attributes"), @@ -49,6 +51,7 @@ STATE_COLUMNS = ( ) EMPTY_STATE_COLUMNS = ( + literal(value=None, type_=sqlalchemy.String).label("state_id"), literal(value=None, type_=sqlalchemy.String).label("state"), literal(value=None, type_=sqlalchemy.String).label("entity_id"), literal(value=None, type_=sqlalchemy.Text).label("attributes"), @@ -294,6 +297,7 @@ def _select_states(start_day: dt, end_day: dt) -> Select: old_state = aliased(States, name="old_state") return ( select( + literal(value=None, type_=sqlalchemy.Text).label("event_id"), literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( "event_type" ), diff --git a/homeassistant/components/shelly/logbook.py b/homeassistant/components/shelly/logbook.py index d4278e3e98e..504dfe90791 100644 --- a/homeassistant/components/shelly/logbook.py +++ b/homeassistant/components/shelly/logbook.py @@ -49,7 +49,7 @@ def async_describe_events( return { "name": "Shelly", - "message": f"'{click_type}' click event for {input_name} Input was fired.", + "message": f"'{click_type}' click event for {input_name} Input was fired", } async_describe_event(DOMAIN, EVENT_SHELLY_CLICK, async_describe_shelly_click_event) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index af88adc00b7..bcbcf382892 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1243,12 +1243,12 @@ async def test_logbook_humanify_automation_triggered_event(hass): assert event1["name"] == "Hello Automation" assert event1["domain"] == "automation" - assert event1["message"] == "has been triggered" + assert event1["message"] == "triggered" assert event1["entity_id"] == "automation.hello" assert event2["name"] == "Bye Automation" assert event2["domain"] == "automation" - assert event2["message"] == "has been triggered by source of trigger" + assert event2["message"] == "triggered by source of trigger" assert event2["entity_id"] == "automation.bye" diff --git a/tests/components/automation/test_logbook.py b/tests/components/automation/test_logbook.py index a3299d806ce..1f726f478bd 100644 --- a/tests/components/automation/test_logbook.py +++ b/tests/components/automation/test_logbook.py @@ -37,13 +37,13 @@ async def test_humanify_automation_trigger_event(hass): ) assert event1["name"] == "Bla" - assert event1["message"] == "has been triggered by state change of input_boolean.yo" + assert event1["message"] == "triggered by state change of input_boolean.yo" assert event1["source"] == "state change of input_boolean.yo" assert event1["context_id"] == context.id assert event1["entity_id"] == "automation.bla" assert event2["name"] == "Bla" - assert event2["message"] == "has been triggered" + assert event2["message"] == "triggered" assert event2["source"] is None assert event2["context_id"] == context.id assert event2["entity_id"] == "automation.bla" diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index 98245a0df20..a78e795573a 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -85,7 +85,7 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): assert events[0]["name"] == "Keypad" assert events[0]["domain"] == "deconz" - assert events[0]["message"] == "fired event 'armed_away'." + assert events[0]["message"] == "fired event 'armed_away'" async def test_humanifying_deconz_event(hass, aioclient_mock): @@ -214,20 +214,20 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): assert events[0]["name"] == "Switch 1" assert events[0]["domain"] == "deconz" - assert events[0]["message"] == "fired event '2000'." + assert events[0]["message"] == "fired event '2000'" assert events[1]["name"] == "Hue remote" assert events[1]["domain"] == "deconz" - assert events[1]["message"] == "'Long press' event for 'Dim up' was fired." + assert events[1]["message"] == "'Long press' event for 'Dim up' was fired" assert events[2]["name"] == "Xiaomi cube" assert events[2]["domain"] == "deconz" - assert events[2]["message"] == "fired event 'Shake'." + assert events[2]["message"] == "fired event 'Shake'" assert events[3]["name"] == "Xiaomi cube" assert events[3]["domain"] == "deconz" - assert events[3]["message"] == "fired event 'unsupported_gesture'." + assert events[3]["message"] == "fired event 'unsupported_gesture'" assert events[4]["name"] == "Faulty event" assert events[4]["domain"] == "deconz" - assert events[4]["message"] == "fired an unknown event." + assert events[4]["message"] == "fired an unknown event" diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index d382506a3db..82857e6735c 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -1,5 +1,6 @@ """The tests for the logbook component.""" # pylint: disable=protected-access,invalid-name +import asyncio import collections from datetime import datetime, timedelta from http import HTTPStatus @@ -208,8 +209,10 @@ async def test_filter_sensor(hass_: ha.HomeAssistant, hass_client): _assert_entry(entries[2], name="ble", entity_id=entity_id4, state="10") -def test_home_assistant_start_stop_not_grouped(hass_): +async def test_home_assistant_start_stop_not_grouped(hass_): """Test if HA start and stop events are no longer grouped.""" + await async_setup_component(hass_, "homeassistant", {}) + await hass_.async_block_till_done() entries = mock_humanify( hass_, ( @@ -223,8 +226,10 @@ def test_home_assistant_start_stop_not_grouped(hass_): assert_entry(entries[1], name="Home Assistant", message="started", domain=ha.DOMAIN) -def test_home_assistant_start(hass_): +async def test_home_assistant_start(hass_): """Test if HA start is not filtered or converted into a restart.""" + await async_setup_component(hass_, "homeassistant", {}) + await hass_.async_block_till_done() entity_id = "switch.bla" pointA = dt_util.utcnow() @@ -604,9 +609,12 @@ async def test_logbook_view_end_time_entity(hass, hass_client, recorder_mock): async def test_logbook_entity_filter_with_automations(hass, hass_client, recorder_mock): """Test the logbook view with end_time and entity with automations and scripts.""" - await async_setup_component(hass, "logbook", {}) - await async_setup_component(hass, "automation", {}) - await async_setup_component(hass, "script", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) await async_recorder_block_till_done(hass) @@ -749,7 +757,12 @@ async def test_filter_continuous_sensor_values( async def test_exclude_new_entities(hass, hass_client, recorder_mock, set_utc): """Test if events are excluded on first update.""" - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) await async_recorder_block_till_done(hass) entity_id = "climate.bla" @@ -781,7 +794,12 @@ async def test_exclude_new_entities(hass, hass_client, recorder_mock, set_utc): async def test_exclude_removed_entities(hass, hass_client, recorder_mock, set_utc): """Test if events are excluded on last update.""" - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) await async_recorder_block_till_done(hass) entity_id = "climate.bla" @@ -820,7 +838,12 @@ async def test_exclude_removed_entities(hass, hass_client, recorder_mock, set_ut async def test_exclude_attribute_changes(hass, hass_client, recorder_mock, set_utc): """Test if events of attribute changes are filtered.""" - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -855,9 +878,12 @@ async def test_exclude_attribute_changes(hass, hass_client, recorder_mock, set_u async def test_logbook_entity_context_id(hass, recorder_mock, hass_client): """Test the logbook view with end_time and entity with automations and scripts.""" - await async_setup_component(hass, "logbook", {}) - await async_setup_component(hass, "automation", {}) - await async_setup_component(hass, "script", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) await async_recorder_block_till_done(hass) @@ -1004,9 +1030,12 @@ async def test_logbook_context_id_automation_script_started_manually( hass, recorder_mock, hass_client ): """Test the logbook populates context_ids for scripts and automations started manually.""" - await async_setup_component(hass, "logbook", {}) - await async_setup_component(hass, "automation", {}) - await async_setup_component(hass, "script", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) await async_recorder_block_till_done(hass) @@ -1032,6 +1061,19 @@ async def test_logbook_context_id_automation_script_started_manually( ) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + + script_2_context = ha.Context( + id="1234", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + hass.bus.async_fire( + EVENT_SCRIPT_STARTED, + {ATTR_NAME: "Mock script"}, + context=script_2_context, + ) + hass.states.async_set("switch.new", STATE_ON, context=script_2_context) + hass.states.async_set("switch.new", STATE_OFF, context=script_2_context) + await hass.async_block_till_done() await async_wait_recording_done(hass) @@ -1061,12 +1103,28 @@ async def test_logbook_context_id_automation_script_started_manually( assert json_dict[2]["domain"] == "homeassistant" + assert json_dict[3]["entity_id"] is None + assert json_dict[3]["name"] == "Mock script" + assert "context_entity_id" not in json_dict[1] + assert json_dict[3]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + assert json_dict[3]["context_id"] == "1234" + + assert json_dict[4]["entity_id"] == "switch.new" + assert json_dict[4]["state"] == "off" + assert "context_entity_id" not in json_dict[1] + assert json_dict[4]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + assert json_dict[4]["context_event_type"] == "script_started" + assert json_dict[4]["context_domain"] == "script" + async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock): """Test the logbook view links events via context parent_id.""" - await async_setup_component(hass, "logbook", {}) - await async_setup_component(hass, "automation", {}) - await async_setup_component(hass, "script", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) await async_recorder_block_till_done(hass) @@ -1240,7 +1298,13 @@ async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock async def test_logbook_context_from_template(hass, hass_client, recorder_mock): """Test the logbook view with end_time and entity with automations and scripts.""" - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + assert await async_setup_component( hass, "switch", @@ -1637,7 +1701,13 @@ async def test_logbook_invalid_entity(hass, hass_client, recorder_mock): async def test_icon_and_state(hass, hass_client, recorder_mock): """Test to ensure state and custom icons are returned.""" - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1705,6 +1775,7 @@ async def test_exclude_events_domain(hass, hass_client, recorder_mock): entity_id = "switch.bla" entity_id2 = "sensor.blu" + await async_setup_component(hass, "homeassistant", {}) config = logbook.CONFIG_SCHEMA( { ha.DOMAIN: {}, @@ -1750,7 +1821,10 @@ async def test_exclude_events_domain_glob(hass, hass_client, recorder_mock): }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1789,7 +1863,10 @@ async def test_include_events_entity(hass, hass_client, recorder_mock): }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1821,7 +1898,10 @@ async def test_exclude_events_entity(hass, hass_client, recorder_mock): logbook.DOMAIN: {CONF_EXCLUDE: {CONF_ENTITIES: [entity_id]}}, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1854,7 +1934,10 @@ async def test_include_events_domain(hass, hass_client, recorder_mock): }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1897,7 +1980,10 @@ async def test_include_events_domain_glob(hass, hass_client, recorder_mock): }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -1948,7 +2034,10 @@ async def test_include_exclude_events(hass, hass_client, recorder_mock): }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -2004,7 +2093,10 @@ async def test_include_exclude_events_with_glob_filters( }, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -2047,7 +2139,10 @@ async def test_empty_config(hass, hass_client, recorder_mock): logbook.DOMAIN: {}, } ) - await async_setup_component(hass, "logbook", config) + await asyncio.gather( + async_setup_component(hass, "homeassistant", {}), + async_setup_component(hass, "logbook", config), + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -2141,7 +2236,12 @@ def _assert_entry( async def test_get_events(hass, hass_ws_client, recorder_mock): """Test logbook get_events.""" now = dt_util.utcnow() - await async_setup_component(hass, "logbook", {}) + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) await async_recorder_block_till_done(hass) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) diff --git a/tests/components/shelly/test_logbook.py b/tests/components/shelly/test_logbook.py index 1ba7ea7ed16..5e267dcfd8f 100644 --- a/tests/components/shelly/test_logbook.py +++ b/tests/components/shelly/test_logbook.py @@ -46,14 +46,14 @@ async def test_humanify_shelly_click_event_block_device(hass, coap_wrapper): assert event1["domain"] == DOMAIN assert ( event1["message"] - == "'single' click event for Test name channel 1 Input was fired." + == "'single' click event for Test name channel 1 Input was fired" ) assert event2["name"] == "Shelly" assert event2["domain"] == DOMAIN assert ( event2["message"] - == "'long' click event for shellyswitch25-12345678 channel 2 Input was fired." + == "'long' click event for shellyswitch25-12345678 channel 2 Input was fired" ) @@ -91,12 +91,12 @@ async def test_humanify_shelly_click_event_rpc_device(hass, rpc_wrapper): assert event1["domain"] == DOMAIN assert ( event1["message"] - == "'single_push' click event for test switch_0 Input was fired." + == "'single_push' click event for test switch_0 Input was fired" ) assert event2["name"] == "Shelly" assert event2["domain"] == DOMAIN assert ( event2["message"] - == "'btn_down' click event for shellypro4pm-12345678 channel 2 Input was fired." + == "'btn_down' click event for shellypro4pm-12345678 channel 2 Input was fired" ) From c7e8428daa00da507752721ef45b233724958b96 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 13 May 2022 00:26:47 +0000 Subject: [PATCH 396/930] [ci skip] Translation update --- .../components/androidtv/translations/ja.json | 4 +-- .../translations/ja.json | 3 +++ .../components/asuswrt/translations/ja.json | 3 +++ .../derivative/translations/ru.json | 14 ++++++++--- .../components/fan/translations/it.json | 1 + .../components/fan/translations/no.json | 1 + .../components/flux_led/translations/ja.json | 2 +- .../components/generic/translations/en.json | 2 ++ .../geocaching/translations/ca.json | 25 +++++++++++++++++++ .../geocaching/translations/de.json | 25 +++++++++++++++++++ .../geocaching/translations/el.json | 25 +++++++++++++++++++ .../geocaching/translations/et.json | 25 +++++++++++++++++++ .../geocaching/translations/fr.json | 25 +++++++++++++++++++ .../geocaching/translations/hu.json | 25 +++++++++++++++++++ .../geocaching/translations/id.json | 25 +++++++++++++++++++ .../geocaching/translations/it.json | 25 +++++++++++++++++++ .../geocaching/translations/pt-BR.json | 25 +++++++++++++++++++ .../geocaching/translations/zh-Hant.json | 25 +++++++++++++++++++ .../components/gios/translations/hu.json | 2 +- .../components/ipp/translations/bg.json | 1 + .../components/isy994/translations/ja.json | 3 ++- .../components/knx/translations/ja.json | 8 +++--- .../components/kodi/translations/ja.json | 2 +- .../components/mysensors/translations/ja.json | 6 ++--- .../components/onewire/translations/bg.json | 3 ++- .../components/onewire/translations/it.json | 2 +- .../components/onewire/translations/no.json | 4 ++- .../components/ps4/translations/ja.json | 2 +- .../components/recorder/translations/ja.json | 4 ++- .../components/recorder/translations/ru.json | 1 + .../components/rfxtrx/translations/bg.json | 3 +++ .../components/sabnzbd/translations/bg.json | 1 + .../simplisafe/translations/ja.json | 5 ++++ .../components/sonarr/translations/bg.json | 1 + .../components/sql/translations/en.json | 6 +++-- .../components/sql/translations/ja.json | 17 ++++++++++--- .../components/steamist/translations/ja.json | 2 +- .../components/threshold/translations/ja.json | 4 +-- .../components/tplink/translations/ja.json | 2 +- .../components/tuya/translations/ja.json | 2 +- .../tuya/translations/select.ru.json | 10 ++++++++ .../tuya/translations/sensor.ru.json | 3 ++- .../ukraine_alarm/translations/bg.json | 3 ++- .../ukraine_alarm/translations/en.json | 13 ++++++++++ .../ukraine_alarm/translations/ja.json | 9 ++++--- .../ukraine_alarm/translations/pl.json | 12 ++++++--- .../components/unifi/translations/ja.json | 3 +++ .../utility_meter/translations/ja.json | 2 +- .../xiaomi_aqara/translations/ja.json | 2 +- .../components/yeelight/translations/ja.json | 4 +-- .../components/zwave_js/translations/ja.json | 4 +-- 51 files changed, 379 insertions(+), 47 deletions(-) create mode 100644 homeassistant/components/application_credentials/translations/ja.json create mode 100644 homeassistant/components/geocaching/translations/ca.json create mode 100644 homeassistant/components/geocaching/translations/de.json create mode 100644 homeassistant/components/geocaching/translations/el.json create mode 100644 homeassistant/components/geocaching/translations/et.json create mode 100644 homeassistant/components/geocaching/translations/fr.json create mode 100644 homeassistant/components/geocaching/translations/hu.json create mode 100644 homeassistant/components/geocaching/translations/id.json create mode 100644 homeassistant/components/geocaching/translations/it.json create mode 100644 homeassistant/components/geocaching/translations/pt-BR.json create mode 100644 homeassistant/components/geocaching/translations/zh-Hant.json diff --git a/homeassistant/components/androidtv/translations/ja.json b/homeassistant/components/androidtv/translations/ja.json index f3139df9819..365a6078366 100644 --- a/homeassistant/components/androidtv/translations/ja.json +++ b/homeassistant/components/androidtv/translations/ja.json @@ -14,9 +14,9 @@ "step": { "user": { "data": { - "adb_server_ip": "ADB\u30b5\u30fc\u30d0\u30fc\u306eIP\u30a2\u30c9\u30ec\u30b9(\u4f7f\u7528\u3057\u306a\u3044\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", + "adb_server_ip": "ADB\u30b5\u30fc\u30d0\u30fc\u306eIP\u30a2\u30c9\u30ec\u30b9(\u4f7f\u7528\u3057\u306a\u3044\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "adb_server_port": "ADB\u30b5\u30fc\u30d0\u30fc\u306e\u30dd\u30fc\u30c8", - "adbkey": "ADB\u30ad\u30fc\u30d5\u30a1\u30a4\u30eb\u3078\u306e\u30d1\u30b9(\u7a7a\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", + "adbkey": "ADB\u30ad\u30fc\u30d5\u30a1\u30a4\u30eb\u3078\u306e\u30d1\u30b9(\u7a7a\u767d\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", "device_class": "\u30c7\u30d0\u30a4\u30b9\u306e\u7a2e\u985e", "host": "\u30db\u30b9\u30c8", "port": "\u30dd\u30fc\u30c8" diff --git a/homeassistant/components/application_credentials/translations/ja.json b/homeassistant/components/application_credentials/translations/ja.json new file mode 100644 index 00000000000..ee195408093 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/ja.json @@ -0,0 +1,3 @@ +{ + "title": "\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8a8d\u8a3c" +} \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/ja.json b/homeassistant/components/asuswrt/translations/ja.json index ab253e324ce..0ae15e8b86b 100644 --- a/homeassistant/components/asuswrt/translations/ja.json +++ b/homeassistant/components/asuswrt/translations/ja.json @@ -1,6 +1,9 @@ { "config": { "abort": { + "invalid_unique_id": "\u30c7\u30d0\u30a4\u30b9\u306e\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6c7a\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093", + "no_unique_id": "\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6301\u305f\u306a\u3044\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8907\u6570\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093", + "not_unique_id_exist": "\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6301\u305f\u306a\u3044\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8907\u6570\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093", "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" }, "error": { diff --git a/homeassistant/components/derivative/translations/ru.json b/homeassistant/components/derivative/translations/ru.json index d7c62f070e1..f3ff7cf0e83 100644 --- a/homeassistant/components/derivative/translations/ru.json +++ b/homeassistant/components/derivative/translations/ru.json @@ -11,8 +11,11 @@ "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" }, "data_description": { - "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." + "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", + "time_window": "\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u043e \u0432\u0437\u0432\u0435\u0448\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u043c\u0443 \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", + "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439." }, + "description": "\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0443\u044e \u0441\u0435\u043d\u0441\u043e\u0440\u0430.", "title": "\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0430\u044f" } } @@ -30,7 +33,8 @@ }, "data_description": { "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", - "unit_prefix": "." + "time_window": "\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u043e \u0432\u0437\u0432\u0435\u0448\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u043c\u0443 \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", + "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439.." } }, "options": { @@ -44,8 +48,10 @@ }, "data_description": { "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", - "unit_prefix": "." - } + "time_window": "\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u043e \u0432\u0437\u0432\u0435\u0448\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u043c\u0443 \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", + "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439.." + }, + "description": "\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0443\u044e \u0441\u0435\u043d\u0441\u043e\u0440\u0430." } } }, diff --git a/homeassistant/components/fan/translations/it.json b/homeassistant/components/fan/translations/it.json index 563b24b7c44..c74f477ea73 100644 --- a/homeassistant/components/fan/translations/it.json +++ b/homeassistant/components/fan/translations/it.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Attiva/disattiva {entity_name}", "turn_off": "Spegnere {entity_name}", "turn_on": "Accendere {entity_name}" }, diff --git a/homeassistant/components/fan/translations/no.json b/homeassistant/components/fan/translations/no.json index b576e2aca7d..d9fdf8c5ea5 100644 --- a/homeassistant/components/fan/translations/no.json +++ b/homeassistant/components/fan/translations/no.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Veksle {entity_name}", "turn_off": "Sl\u00e5 av {entity_name}", "turn_on": "Sl\u00e5 p\u00e5 {entity_name}" }, diff --git a/homeassistant/components/flux_led/translations/ja.json b/homeassistant/components/flux_led/translations/ja.json index 3b6a34d7e5b..248df586ee9 100644 --- a/homeassistant/components/flux_led/translations/ja.json +++ b/homeassistant/components/flux_led/translations/ja.json @@ -17,7 +17,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" + "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index b552c780d29..334f00fab6f 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -32,6 +32,7 @@ "user": { "data": { "authentication": "Authentication", + "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", @@ -71,6 +72,7 @@ "init": { "data": { "authentication": "Authentication", + "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", diff --git a/homeassistant/components/geocaching/translations/ca.json b/homeassistant/components/geocaching/translations/ca.json new file mode 100644 index 00000000000..09e241d8f2a --- /dev/null +++ b/homeassistant/components/geocaching/translations/ca.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "authorize_url_timeout": "Temps d'espera esgotat durant la generaci\u00f3 de l'URL d'autoritzaci\u00f3.", + "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", + "no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})", + "oauth_error": "S'han rebut dades token inv\u00e0lides.", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, + "step": { + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" + }, + "reauth_confirm": { + "description": "La integraci\u00f3 Geocaching ha de tornar a autenticar-se amb el teu compte", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/de.json b/homeassistant/components/geocaching/translations/de.json new file mode 100644 index 00000000000..1712153fbce --- /dev/null +++ b/homeassistant/components/geocaching/translations/de.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", + "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", + "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", + "oauth_error": "Ung\u00fcltige Token-Daten empfangen.", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "create_entry": { + "default": "Erfolgreich authentifiziert" + }, + "step": { + "pick_implementation": { + "title": "W\u00e4hle die Authentifizierungsmethode" + }, + "reauth_confirm": { + "description": "Die Geocaching-Integration muss dein Konto erneut authentifizieren", + "title": "Integration erneut authentifizieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/el.json b/homeassistant/components/geocaching/translations/el.json new file mode 100644 index 00000000000..e07b121ceee --- /dev/null +++ b/homeassistant/components/geocaching/translations/el.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "authorize_url_timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2.", + "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", + "oauth_error": "\u039b\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03bf\u03cd.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "create_entry": { + "default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "pick_implementation": { + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Geocaching \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/et.json b/homeassistant/components/geocaching/translations/et.json new file mode 100644 index 00000000000..075e41d1f24 --- /dev/null +++ b/homeassistant/components/geocaching/translations/et.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Kasutaja on juba seadistatud", + "already_in_progress": "Seadistamine on juba k\u00e4imas", + "authorize_url_timeout": "Kinnitus-URLi loomise ajal\u00f5pp", + "missing_configuration": "Komponent pole seadistatud. Palun loe dokumentatsiooni.", + "no_url_available": "URL pole saadaval. Selle t\u00f5rke kohta teabe saamiseks vaata [spikrijaotis]({docs_url})", + "oauth_error": "Saadi sobimatud loaandmed.", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "create_entry": { + "default": "Tuvastamine \u00f5nnestus" + }, + "step": { + "pick_implementation": { + "title": "Vali tuvastusmeetod" + }, + "reauth_confirm": { + "description": "Geocachingu sidumine peab konto taastuvastama", + "title": "Taastuvasta sidumine" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/fr.json b/homeassistant/components/geocaching/translations/fr.json new file mode 100644 index 00000000000..33bd549c762 --- /dev/null +++ b/homeassistant/components/geocaching/translations/fr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification expir\u00e9.", + "missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.", + "no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})", + "oauth_error": "Des donn\u00e9es de jeton non valides ont \u00e9t\u00e9 re\u00e7ues.", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "create_entry": { + "default": "Authentification r\u00e9ussie" + }, + "step": { + "pick_implementation": { + "title": "S\u00e9lectionner une m\u00e9thode d'authentification" + }, + "reauth_confirm": { + "description": "L'int\u00e9gration Geocaching doit r\u00e9-authentifier votre compte", + "title": "R\u00e9-authentifier l'int\u00e9gration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/hu.json b/homeassistant/components/geocaching/translations/hu.json new file mode 100644 index 00000000000..b7bbe55e397 --- /dev/null +++ b/homeassistant/components/geocaching/translations/hu.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", + "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", + "oauth_error": "\u00c9rv\u00e9nytelen token adatok \u00e9rkeztek.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "create_entry": { + "default": "Sikeres hiteles\u00edt\u00e9s" + }, + "step": { + "pick_implementation": { + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" + }, + "reauth_confirm": { + "description": "A Geocaching integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie a fi\u00f3kj\u00e1t.", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/id.json b/homeassistant/components/geocaching/translations/id.json new file mode 100644 index 00000000000..c52366419d5 --- /dev/null +++ b/homeassistant/components/geocaching/translations/id.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "authorize_url_timeout": "Tenggang waktu pembuatan URL otorisasi habis.", + "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", + "no_url_available": "Tidak ada URL yang tersedia. Untuk informasi tentang kesalahan ini, [lihat bagian bantuan]({docs_url})", + "oauth_error": "Menerima respons token yang tidak valid.", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "create_entry": { + "default": "Berhasil diautentikasi" + }, + "step": { + "pick_implementation": { + "title": "Pilih Metode Autentikasi" + }, + "reauth_confirm": { + "description": "Integrasi Geocaching perlu mengautentikasi ulang akun Anda", + "title": "Autentikasi Ulang Integrasi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/it.json b/homeassistant/components/geocaching/translations/it.json new file mode 100644 index 00000000000..2758f190756 --- /dev/null +++ b/homeassistant/components/geocaching/translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", + "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", + "oauth_error": "Ricevuti dati token non validi.", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "create_entry": { + "default": "Autenticazione riuscita" + }, + "step": { + "pick_implementation": { + "title": "Scegli il metodo di autenticazione" + }, + "reauth_confirm": { + "description": "L'integrazione Geocaching deve autenticare nuovamente il tuo account", + "title": "Autentica nuovamente l'integrazione" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/pt-BR.json b/homeassistant/components/geocaching/translations/pt-BR.json new file mode 100644 index 00000000000..4a6c20919d0 --- /dev/null +++ b/homeassistant/components/geocaching/translations/pt-BR.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 est\u00e1 configurada", + "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "authorize_url_timeout": "Tempo limite gerando URL de autoriza\u00e7\u00e3o.", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "no_url_available": "Nenhuma URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre este erro, [verifique a se\u00e7\u00e3o de ajuda]({docs_url})", + "oauth_error": "Dados de token inv\u00e1lidos recebidos.", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "create_entry": { + "default": "Autenticado com sucesso" + }, + "step": { + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o Geocaching precisa reautenticar sua conta", + "title": "Reautenticar Integra\u00e7\u00e3o" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/zh-Hant.json b/homeassistant/components/geocaching/translations/zh-Hant.json new file mode 100644 index 00000000000..80c501be734 --- /dev/null +++ b/homeassistant/components/geocaching/translations/zh-Hant.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "authorize_url_timeout": "\u7522\u751f\u8a8d\u8b49 URL \u6642\u903e\u6642\u3002", + "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", + "no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})", + "oauth_error": "\u6536\u5230\u7121\u6548\u7684\u6b0a\u6756\u8cc7\u6599\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, + "step": { + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" + }, + "reauth_confirm": { + "description": "Geocaching \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gios/translations/hu.json b/homeassistant/components/gios/translations/hu.json index 1de37cee96a..9ad0e244eaf 100644 --- a/homeassistant/components/gios/translations/hu.json +++ b/homeassistant/components/gios/translations/hu.json @@ -14,7 +14,7 @@ "name": "Elnevez\u00e9s", "station_id": "A m\u00e9r\u0151\u00e1llom\u00e1s azonos\u00edt\u00f3ja" }, - "description": "A GIO\u015a (lengyel k\u00f6rnyezetv\u00e9delmi f\u0151fel\u00fcgyel\u0151) leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ged a konfigur\u00e1ci\u00f3val kapcsolatban, l\u00e1togass ide: https://www.home-assistant.io/integrations/gios", + "description": "A GIO\u015a (lengyel k\u00f6rnyezetv\u00e9delmi f\u0151fel\u00fcgyel\u0151) leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ge a konfigur\u00e1ci\u00f3val kapcsolatban, l\u00e1togasson ide: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Lengyel K\u00f6rnyezetv\u00e9delmi F\u0151fel\u00fcgyel\u0151s\u00e9g)" } } diff --git a/homeassistant/components/ipp/translations/bg.json b/homeassistant/components/ipp/translations/bg.json index 4983c9a14b2..d454fe68170 100644 --- a/homeassistant/components/ipp/translations/bg.json +++ b/homeassistant/components/ipp/translations/bg.json @@ -3,6 +3,7 @@ "step": { "user": { "data": { + "base_path": "\u041e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u0435\u043d \u043f\u044a\u0442 \u0434\u043e \u043f\u0440\u0438\u043d\u0442\u0435\u0440\u0430", "port": "\u041f\u043e\u0440\u0442" } } diff --git a/homeassistant/components/isy994/translations/ja.json b/homeassistant/components/isy994/translations/ja.json index 51c56f035d7..0245baf01a0 100644 --- a/homeassistant/components/isy994/translations/ja.json +++ b/homeassistant/components/isy994/translations/ja.json @@ -16,7 +16,8 @@ "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - } + }, + "title": "ISY\u306e\u518d\u8a8d\u8a3c" }, "user": { "data": { diff --git a/homeassistant/components/knx/translations/ja.json b/homeassistant/components/knx/translations/ja.json index 52eed8780b8..475958af9a0 100644 --- a/homeassistant/components/knx/translations/ja.json +++ b/homeassistant/components/knx/translations/ja.json @@ -16,7 +16,7 @@ "data": { "host": "\u30db\u30b9\u30c8", "individual_address": "\u63a5\u7d9a\u7528\u306e\u500b\u5225\u30a2\u30c9\u30ec\u30b9", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", + "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "port": "\u30dd\u30fc\u30c8", "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" @@ -31,7 +31,7 @@ "routing": { "data": { "individual_address": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u63a5\u7d9a\u306e\u500b\u5225\u306e\u30a2\u30c9\u30ec\u30b9", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", + "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30b0\u30eb\u30fc\u30d7", "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30dd\u30fc\u30c8" }, @@ -92,7 +92,7 @@ "data": { "connection_type": "KNX\u63a5\u7d9a\u30bf\u30a4\u30d7", "individual_address": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u500b\u5225\u30a2\u30c9\u30ec\u30b9", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", + "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "multicast_group": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa(discovery)\u306b\u4f7f\u7528\u3055\u308c\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30b0\u30eb\u30fc\u30d7", "multicast_port": "\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u691c\u51fa(discovery)\u306b\u4f7f\u7528\u3055\u308c\u308b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8\u30dd\u30fc\u30c8", "rate_limit": "1 \u79d2\u3042\u305f\u308a\u306e\u6700\u5927\u9001\u4fe1\u96fb\u5831(telegrams )\u6570", @@ -110,7 +110,7 @@ "tunnel": { "data": { "host": "\u30db\u30b9\u30c8", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", + "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "port": "\u30dd\u30fc\u30c8", "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" diff --git a/homeassistant/components/kodi/translations/ja.json b/homeassistant/components/kodi/translations/ja.json index c7cf892bb2b..88855ff4d77 100644 --- a/homeassistant/components/kodi/translations/ja.json +++ b/homeassistant/components/kodi/translations/ja.json @@ -37,7 +37,7 @@ "data": { "ws_port": "\u30dd\u30fc\u30c8" }, - "description": "WebSocket\u30dd\u30fc\u30c8(Kodi\u3067\u306fTCP\u30dd\u30fc\u30c8\u3068\u547c\u3070\u308c\u308b\u3053\u3068\u3082\u3042\u308a\u307e\u3059)\u3002WebSocket\u3092\u4ecb\u3057\u3066\u63a5\u7d9a\u3059\u308b\u306b\u306f\u3001\u30b7\u30b9\u30c6\u30e0/\u8a2d\u5b9a/\u30cd\u30c3\u30c8\u30ef\u30fc\u30af/\u30b5\u30fc\u30d3\u30b9\u306b\u3042\u308b \"\u30d7\u30ed\u30b0\u30e9\u30e0\u306bKodi\u306e\u5236\u5fa1\u3092\u8a31\u53ef\u3059\u308b\" \u3092\u6709\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002WebSocket\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u30dd\u30fc\u30c8\u3092\u524a\u9664\u3057\u3066\u7a7a\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" + "description": "WebSocket\u30dd\u30fc\u30c8(Kodi\u3067\u306fTCP\u30dd\u30fc\u30c8\u3068\u547c\u3070\u308c\u308b\u3053\u3068\u3082\u3042\u308a\u307e\u3059)\u3002WebSocket\u3092\u4ecb\u3057\u3066\u63a5\u7d9a\u3059\u308b\u306b\u306f\u3001\u30b7\u30b9\u30c6\u30e0/\u8a2d\u5b9a/\u30cd\u30c3\u30c8\u30ef\u30fc\u30af/\u30b5\u30fc\u30d3\u30b9\u306b\u3042\u308b \"\u30d7\u30ed\u30b0\u30e9\u30e0\u306bKodi\u306e\u5236\u5fa1\u3092\u8a31\u53ef\u3059\u308b\" \u3092\u6709\u52b9\u306b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002WebSocket\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u30dd\u30fc\u30c8\u3092\u524a\u9664\u3057\u3066\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/mysensors/translations/ja.json b/homeassistant/components/mysensors/translations/ja.json index 9842d241ad1..b178d136009 100644 --- a/homeassistant/components/mysensors/translations/ja.json +++ b/homeassistant/components/mysensors/translations/ja.json @@ -42,7 +42,7 @@ "step": { "gw_mqtt": { "data": { - "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", + "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u767d\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", "retain": "mqtt retain(\u4fdd\u6301)", "topic_in_prefix": "\u30a4\u30f3\u30d7\u30c3\u30c8 \u30c8\u30d4\u30c3\u30af\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9(topic_in_prefix)", "topic_out_prefix": "\u30a2\u30a6\u30c8\u30d7\u30c3\u30c8 \u30c8\u30d4\u30c3\u30af\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9(topic_out_prefix)", @@ -54,7 +54,7 @@ "data": { "baud_rate": "\u30dc\u30fc\u30ec\u30fc\u30c8", "device": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8", - "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", + "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u767d\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", "version": "MySensors\u306e\u30d0\u30fc\u30b8\u30e7\u30f3" }, "description": "\u30b7\u30ea\u30a2\u30eb\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" @@ -62,7 +62,7 @@ "gw_tcp": { "data": { "device": "\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u306eIP\u30a2\u30c9\u30ec\u30b9", - "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", + "persistence_file": "\u6c38\u7d9a(persistence)\u30d5\u30a1\u30a4\u30eb(\u7a7a\u767d\u306b\u3059\u308b\u3068\u81ea\u52d5\u751f\u6210\u3055\u308c\u307e\u3059)", "tcp_port": "\u30dd\u30fc\u30c8", "version": "MySensors\u306e\u30d0\u30fc\u30b8\u30e7\u30f3" }, diff --git a/homeassistant/components/onewire/translations/bg.json b/homeassistant/components/onewire/translations/bg.json index 26dd6f15686..a14f7fa00a2 100644 --- a/homeassistant/components/onewire/translations/bg.json +++ b/homeassistant/components/onewire/translations/bg.json @@ -4,7 +4,8 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_path": "\u0414\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f\u0442\u0430 \u043d\u0435 \u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0430." }, "step": { "owserver": { diff --git a/homeassistant/components/onewire/translations/it.json b/homeassistant/components/onewire/translations/it.json index 77f64381ee7..a6bffe1e5fc 100644 --- a/homeassistant/components/onewire/translations/it.json +++ b/homeassistant/components/onewire/translations/it.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "Impossibile connettersi", - "invalid_path": "Directory non trovata." + "invalid_path": "Cartella non trovata." }, "step": { "owserver": { diff --git a/homeassistant/components/onewire/translations/no.json b/homeassistant/components/onewire/translations/no.json index 62bd0ac4634..89529aa7810 100644 --- a/homeassistant/components/onewire/translations/no.json +++ b/homeassistant/components/onewire/translations/no.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Vert", + "port": "Port", "type": "Tilkoblingstype" }, - "title": "Sett opp 1-Wire" + "title": "Angi serverdetaljer" } } }, diff --git a/homeassistant/components/ps4/translations/ja.json b/homeassistant/components/ps4/translations/ja.json index 92be62f7229..40ac55264a9 100644 --- a/homeassistant/components/ps4/translations/ja.json +++ b/homeassistant/components/ps4/translations/ja.json @@ -33,7 +33,7 @@ }, "mode": { "data": { - "ip_address": "IP\u30a2\u30c9\u30ec\u30b9(\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u307e\u3059)", + "ip_address": "IP\u30a2\u30c9\u30ec\u30b9(\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059)", "mode": "\u30b3\u30f3\u30d5\u30a3\u30b0\u30e2\u30fc\u30c9" }, "data_description": { diff --git a/homeassistant/components/recorder/translations/ja.json b/homeassistant/components/recorder/translations/ja.json index 2aadb11ac33..8b54e4f544f 100644 --- a/homeassistant/components/recorder/translations/ja.json +++ b/homeassistant/components/recorder/translations/ja.json @@ -1,7 +1,9 @@ { "system_health": { "info": { - "estimated_db_size": "\u63a8\u5b9a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30a4\u30ba(MiB)" + "current_recorder_run": "\u73fe\u5728\u306e\u5b9f\u884c\u958b\u59cb\u6642\u9593", + "estimated_db_size": "\u63a8\u5b9a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30a4\u30ba(MiB)", + "oldest_recorder_run": "\u6700\u3082\u53e4\u3044\u5b9f\u884c\u958b\u59cb\u6642\u9593" } } } \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/ru.json b/homeassistant/components/recorder/translations/ru.json index bdac6c52a3b..a637d37f720 100644 --- a/homeassistant/components/recorder/translations/ru.json +++ b/homeassistant/components/recorder/translations/ru.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "current_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0446\u0438\u043a\u043b\u0430", "estimated_db_size": "\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (MiB)", "oldest_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430" } diff --git a/homeassistant/components/rfxtrx/translations/bg.json b/homeassistant/components/rfxtrx/translations/bg.json index 9a7983491d6..c03ec99553e 100644 --- a/homeassistant/components/rfxtrx/translations/bg.json +++ b/homeassistant/components/rfxtrx/translations/bg.json @@ -19,6 +19,9 @@ "device": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" }, "title": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + }, + "setup_serial_manual_path": { + "title": "\u041f\u044a\u0442" } } }, diff --git a/homeassistant/components/sabnzbd/translations/bg.json b/homeassistant/components/sabnzbd/translations/bg.json index d3fcfb0789d..a9d07e52b39 100644 --- a/homeassistant/components/sabnzbd/translations/bg.json +++ b/homeassistant/components/sabnzbd/translations/bg.json @@ -9,6 +9,7 @@ "data": { "api_key": "API \u043a\u043b\u044e\u0447", "name": "\u0418\u043c\u0435", + "path": "\u041f\u044a\u0442", "url": "URL" } } diff --git a/homeassistant/components/simplisafe/translations/ja.json b/homeassistant/components/simplisafe/translations/ja.json index 74b0b20c65e..2d3f53b0710 100644 --- a/homeassistant/components/simplisafe/translations/ja.json +++ b/homeassistant/components/simplisafe/translations/ja.json @@ -22,6 +22,11 @@ "description": "\u30a2\u30af\u30bb\u30b9\u306e\u6709\u52b9\u671f\u9650\u304c\u5207\u308c\u3066\u3044\u308b\u304b\u3001\u53d6\u308a\u6d88\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u30ea\u30f3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" }, + "sms_2fa": { + "data": { + "code": "\u30b3\u30fc\u30c9" + } + }, "user": { "data": { "auth_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9", diff --git a/homeassistant/components/sonarr/translations/bg.json b/homeassistant/components/sonarr/translations/bg.json index c6ab1b205e9..d866b1d1813 100644 --- a/homeassistant/components/sonarr/translations/bg.json +++ b/homeassistant/components/sonarr/translations/bg.json @@ -17,6 +17,7 @@ "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447", + "base_path": "\u041f\u044a\u0442 \u0434\u043e API", "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442", "url": "URL" diff --git a/homeassistant/components/sql/translations/en.json b/homeassistant/components/sql/translations/en.json index 9d05f771853..4b72d024df4 100644 --- a/homeassistant/components/sql/translations/en.json +++ b/homeassistant/components/sql/translations/en.json @@ -5,7 +5,8 @@ }, "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid" + "query_invalid": "SQL Query invalid", + "value_template_invalid": "Value Template invalid" }, "step": { "user": { @@ -31,7 +32,8 @@ "options": { "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid" + "query_invalid": "SQL Query invalid", + "value_template_invalid": "Value Template invalid" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/ja.json b/homeassistant/components/sql/translations/ja.json index 6fa037efc5f..3b9fdfc8eaa 100644 --- a/homeassistant/components/sql/translations/ja.json +++ b/homeassistant/components/sql/translations/ja.json @@ -20,7 +20,11 @@ }, "data_description": { "column": "\u8fd4\u3055\u308c\u305f\u30af\u30a8\u30ea\u3092\u72b6\u614b\u3068\u3057\u3066\u8868\u793a\u3059\u308b\u305f\u3081\u306e\u30ab\u30e9\u30e0", - "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" + "db_url": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9URL\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306eHA\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059", + "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d", + "query": "\u5b9f\u884c\u3059\u308b\u30af\u30a8\u30ea\u306f\u3001 'SELECT' \u3067\u59cb\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "unit_of_measurement": "\u6e2c\u5b9a\u5358\u4f4d(\u30aa\u30d7\u30b7\u30e7\u30f3)", + "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8(\u30aa\u30d7\u30b7\u30e7\u30f3)" } } } @@ -37,10 +41,17 @@ "column": "\u30b3\u30e9\u30e0", "db_url": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL", "name": "\u540d\u524d", - "query": "\u30af\u30a8\u30ea\u3092\u9078\u629e" + "query": "\u30af\u30a8\u30ea\u3092\u9078\u629e", + "unit_of_measurement": "\u6e2c\u5b9a\u5358\u4f4d", + "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8" }, "data_description": { - "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d" + "column": "\u8fd4\u3055\u308c\u305f\u30af\u30a8\u30ea\u3092\u72b6\u614b\u3068\u3057\u3066\u8868\u793a\u3059\u308b\u305f\u3081\u306e\u30ab\u30e9\u30e0", + "db_url": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9URL\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u306eHA\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059", + "name": "\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u3068\u30bb\u30f3\u30b5\u30fc\u306b\u4f7f\u7528\u3055\u308c\u308b\u540d\u524d", + "query": "\u5b9f\u884c\u3059\u308b\u30af\u30a8\u30ea\u306f\u3001 'SELECT' \u3067\u59cb\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "unit_of_measurement": "\u6e2c\u5b9a\u5358\u4f4d(\u30aa\u30d7\u30b7\u30e7\u30f3)", + "value_template": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8(\u30aa\u30d7\u30b7\u30e7\u30f3)" } } } diff --git a/homeassistant/components/steamist/translations/ja.json b/homeassistant/components/steamist/translations/ja.json index 36658f87577..b37135e9c44 100644 --- a/homeassistant/components/steamist/translations/ja.json +++ b/homeassistant/components/steamist/translations/ja.json @@ -25,7 +25,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc(discovery)\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" + "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc(discovery)\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/threshold/translations/ja.json b/homeassistant/components/threshold/translations/ja.json index 7d5a6cc2ce7..75330ac830c 100644 --- a/homeassistant/components/threshold/translations/ja.json +++ b/homeassistant/components/threshold/translations/ja.json @@ -1,7 +1,7 @@ { "config": { "error": { - "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" + "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u767d\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" }, "step": { "user": { @@ -20,7 +20,7 @@ }, "options": { "error": { - "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" + "need_lower_upper": "\u4e0b\u9650\u3068\u4e0a\u9650\u306e\u4e21\u65b9\u3092\u7a7a\u767d\u306b\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" }, "step": { "init": { diff --git a/homeassistant/components/tplink/translations/ja.json b/homeassistant/components/tplink/translations/ja.json index f78e4adec9c..45e9854b431 100644 --- a/homeassistant/components/tplink/translations/ja.json +++ b/homeassistant/components/tplink/translations/ja.json @@ -25,7 +25,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc(discovery)\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" + "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc(discovery)\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/tuya/translations/ja.json b/homeassistant/components/tuya/translations/ja.json index 6733e70b472..1b5d29584cc 100644 --- a/homeassistant/components/tuya/translations/ja.json +++ b/homeassistant/components/tuya/translations/ja.json @@ -71,7 +71,7 @@ "init": { "data": { "discovery_interval": "\u30c7\u30d0\u30a4\u30b9\u691c\u51fa\u306e\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694(\u79d2\u5358\u4f4d)", - "list_devices": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b\u304b\u3001\u7a7a\u6b04\u306e\u307e\u307e\u306b\u3057\u3066\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3059", + "list_devices": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b\u304b\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3059", "query_device": "\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u66f4\u65b0\u3092\u9ad8\u901f\u5316\u3059\u308b\u305f\u3081\u306b\u30af\u30a8\u30ea\u306e\u65b9\u6cd5(query method)\u3092\u4f7f\u7528\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u307e\u3059", "query_interval": "\u30af\u30a8\u30ea\u30c7\u30d0\u30a4\u30b9\u306e\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694(\u79d2)" }, diff --git a/homeassistant/components/tuya/translations/select.ru.json b/homeassistant/components/tuya/translations/select.ru.json index 99f7f02771d..c2138e2490b 100644 --- a/homeassistant/components/tuya/translations/select.ru.json +++ b/homeassistant/components/tuya/translations/select.ru.json @@ -52,8 +52,17 @@ "level_8": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 8", "level_9": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c 9" }, + "tuya__humidifier_moodlighting": { + "1": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 1", + "2": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 2", + "3": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 3", + "4": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 4", + "5": "\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 5" + }, "tuya__humidifier_spray_mode": { "auto": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438", + "health": "\u0421\u0442\u0430\u0442\u0443\u0441", + "humidity": "\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c", "sleep": "\u0421\u043e\u043d", "work": "\u0420\u0430\u0431\u043e\u0442\u0430" }, @@ -108,6 +117,7 @@ "part": "\u0427\u0430\u0441\u0442\u044c", "pick_zone": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0437\u043e\u043d\u0443", "point": "\u0422\u043e\u0447\u043a\u0430", + "pose": "\u041f\u043e\u0437\u0438\u0446\u0438\u044f", "random": "\u0421\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439", "right_bow": "\u041e\u0433\u0438\u0431\u0430\u0442\u044c \u0441\u043f\u0440\u0430\u0432\u0430", "right_spiral": "\u0421\u043f\u0438\u0440\u0430\u043b\u044c \u0432\u043f\u0440\u0430\u0432\u043e", diff --git a/homeassistant/components/tuya/translations/sensor.ru.json b/homeassistant/components/tuya/translations/sensor.ru.json index 9ec7eac14c0..8b356e13db0 100644 --- a/homeassistant/components/tuya/translations/sensor.ru.json +++ b/homeassistant/components/tuya/translations/sensor.ru.json @@ -3,7 +3,8 @@ "tuya__air_quality": { "good": "\u0425\u043e\u0440\u043e\u0448\u0435\u0435", "great": "\u041e\u0442\u043b\u0438\u0447\u043d\u043e\u0435", - "mild": "\u0423\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0435" + "mild": "\u0423\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0435", + "severe": "\u0421\u0435\u0440\u044c\u0435\u0437\u043d\u044b\u0439" }, "tuya__status": { "boiling_temp": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043a\u0438\u043f\u0435\u043d\u0438\u044f", diff --git a/homeassistant/components/ukraine_alarm/translations/bg.json b/homeassistant/components/ukraine_alarm/translations/bg.json index fbedf14b7cd..68cd63e86b1 100644 --- a/homeassistant/components/ukraine_alarm/translations/bg.json +++ b/homeassistant/components/ukraine_alarm/translations/bg.json @@ -27,7 +27,8 @@ "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447" - } + }, + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 Ukraine Alarm. \u0417\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447, \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/en.json b/homeassistant/components/ukraine_alarm/translations/en.json index 857311ea3e7..6397f652aee 100644 --- a/homeassistant/components/ukraine_alarm/translations/en.json +++ b/homeassistant/components/ukraine_alarm/translations/en.json @@ -8,6 +8,12 @@ "timeout": "Timeout establishing connection", "unknown": "Unexpected error" }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_api_key": "Invalid API key", + "timeout": "Timeout establishing connection", + "unknown": "Unexpected error" + }, "step": { "community": { "data": { @@ -21,8 +27,15 @@ }, "description": "If you want to monitor not only state, choose its specific district" }, + "state": { + "data": { + "region": "Region" + }, + "description": "Choose state to monitor" + }, "user": { "data": { + "api_key": "API Key", "region": "Region" }, "description": "Choose state to monitor" diff --git a/homeassistant/components/ukraine_alarm/translations/ja.json b/homeassistant/components/ukraine_alarm/translations/ja.json index 602d63a1de7..303ac7df051 100644 --- a/homeassistant/components/ukraine_alarm/translations/ja.json +++ b/homeassistant/components/ukraine_alarm/translations/ja.json @@ -13,12 +13,14 @@ "community": { "data": { "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" - } + }, + "description": "\u5dde\u3084\u5730\u533a\u3060\u3051\u3067\u306a\u304f\u3001\u7279\u5b9a\u306e\u5730\u57df\u3092\u76e3\u8996\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u305d\u306e\u5730\u57df\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" }, "district": { "data": { "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" - } + }, + "description": "\u5dde\u3060\u3051\u3067\u306a\u304f\u3001\u7279\u5b9a\u306e\u5730\u533a\u3092\u76e3\u8996\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u305d\u306e\u5730\u533a\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" }, "state": { "data": { @@ -29,7 +31,8 @@ "user": { "data": { "api_key": "API\u30ad\u30fc" - } + }, + "description": "\u30a6\u30af\u30e9\u30a4\u30ca\u306e\u8b66\u5831\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001 {api_url} \u306b\u79fb\u52d5\u3057\u3066\u304f\u3060\u3055\u3044" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/pl.json b/homeassistant/components/ukraine_alarm/translations/pl.json index 1cccde50083..9ed67385f75 100644 --- a/homeassistant/components/ukraine_alarm/translations/pl.json +++ b/homeassistant/components/ukraine_alarm/translations/pl.json @@ -13,22 +13,26 @@ "community": { "data": { "region": "Region" - } + }, + "description": "Je\u015bli nie chcesz monitorowa\u0107 tylko rejonu i dystryktu, wybierz jego konkretn\u0105 spo\u0142eczno\u015b\u0107" }, "district": { "data": { "region": "Region" - } + }, + "description": "Je\u015bli nie chcesz monitorowa\u0107 tylko rejonu, wybierz jego konkretny dystrykt" }, "state": { "data": { "region": "Region" - } + }, + "description": "Wybierz rejon do monitorowania" }, "user": { "data": { "api_key": "Klucz API" - } + }, + "description": "Skonfiguruj integracj\u0119 Alarmy w Ukrainie. Aby wygenerowa\u0107 klucz API, przejd\u017a do {api_url}." } } } diff --git a/homeassistant/components/unifi/translations/ja.json b/homeassistant/components/unifi/translations/ja.json index 1f77f15d519..fba092385c1 100644 --- a/homeassistant/components/unifi/translations/ja.json +++ b/homeassistant/components/unifi/translations/ja.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u304c\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3055\u308c\u3066\u3044\u307e\u305b\u3093" + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/utility_meter/translations/ja.json b/homeassistant/components/utility_meter/translations/ja.json index 90c175f16ec..ec12f2310be 100644 --- a/homeassistant/components/utility_meter/translations/ja.json +++ b/homeassistant/components/utility_meter/translations/ja.json @@ -15,7 +15,7 @@ "delta_values": "\u30bd\u30fc\u30b9\u5024\u304c\u7d76\u5bfe\u5024\u3067\u306f\u306a\u304f\u3001\u6700\u5f8c\u306e\u8aad\u307f\u53d6\u308a\u4ee5\u964d\u304c\u30c7\u30eb\u30bf\u5024\u3067\u3042\u308b\u5834\u5408\u306f\u3001\u6709\u52b9\u306b\u3057\u307e\u3059\u3002", "net_consumption": "\u30bd\u30fc\u30b9\u304c\u30cd\u30c3\u30c8\u30e1\u30fc\u30bf(net meter)\u3001\u3064\u307e\u308a\u5897\u52a0\u3082\u3059\u308b\u3057\u6e1b\u5c11\u3082\u3059\u308b\u5834\u5408\u306f\u3001\u6709\u52b9\u306b\u3057\u307e\u3059\u3002", "offset": "\u6bce\u6708\u306e\u30e1\u30fc\u30bf\u30fc\u30ea\u30bb\u30c3\u30c8\u306e\u65e5\u3092\u30aa\u30d5\u30bb\u30c3\u30c8\u3057\u307e\u3059\u3002", - "tariffs": "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u6599\u91d1(Tariff)\u306e\u30ea\u30b9\u30c8\u3002\u5358\u4e00\u306e\u6599\u91d1\u8868\u306e\u307f\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u7a7a\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" + "tariffs": "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u6599\u91d1(Tariff)\u306e\u30ea\u30b9\u30c8\u3002\u5358\u4e00\u306e\u6599\u91d1\u8868\u306e\u307f\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" }, "description": "\u8a2d\u5b9a\u3055\u308c\u305f\u671f\u9593\u3001\u901a\u5e38\u306f\u6bce\u6708\u3001\u69d8\u3005\u306a\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3(\u30a8\u30cd\u30eb\u30ae\u30fc\u3001\u30ac\u30b9\u3001\u6c34\u3001\u6696\u623f\u306a\u3069)\u306e\u6d88\u8cbb\u91cf\u3092\u8ffd\u8de1\u3059\u308b\u30bb\u30f3\u30b5\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3068\u3057\u3066\u3001\u6599\u91d1\u8868\u306b\u3088\u308b\u6d88\u8cbb\u91cf\u306e\u5206\u5272\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u307e\u3059\u3002\u3053\u306e\u5834\u5408\u3001\u5404\u6599\u91d1\u8868\u306e\u305f\u3081\u306e1\u3064\u306e\u30bb\u30f3\u30b5\u30fc\u3068\u3001\u73fe\u5728\u306e\u6599\u91d1(Tariff)\u3092\u9078\u629e\u3059\u308b\u305f\u3081\u306e\u9078\u629e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u304c\u4f5c\u6210\u3055\u308c\u307e\u3059\u3002", "title": "\u65b0\u3057\u3044\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u30e1\u30fc\u30bf\u30fc" diff --git a/homeassistant/components/xiaomi_aqara/translations/ja.json b/homeassistant/components/xiaomi_aqara/translations/ja.json index 87e757a2c5d..65e1870efe2 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ja.json +++ b/homeassistant/components/xiaomi_aqara/translations/ja.json @@ -35,7 +35,7 @@ "interface": "\u4f7f\u7528\u3059\u308b\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30a4\u30b9", "mac": "Mac\u30a2\u30c9\u30ec\u30b9 (\u30aa\u30d7\u30b7\u30e7\u30f3)" }, - "description": "Xiaomi Aqara Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u3068MAC\u30a2\u30c9\u30ec\u30b9\u304c\u7a7a\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", + "description": "Xiaomi Aqara Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u3068MAC\u30a2\u30c9\u30ec\u30b9\u304c\u7a7a\u767d\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", "title": "Xiaomi Aqara Gateway" } } diff --git a/homeassistant/components/yeelight/translations/ja.json b/homeassistant/components/yeelight/translations/ja.json index e032979dcee..001cde41977 100644 --- a/homeassistant/components/yeelight/translations/ja.json +++ b/homeassistant/components/yeelight/translations/ja.json @@ -21,7 +21,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" + "description": "\u30db\u30b9\u30c8\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u30c7\u30a3\u30b9\u30ab\u30d0\u30ea\u30fc\u3092\u4f7f\u3063\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u691c\u7d22\u3057\u307e\u3059\u3002" } } }, @@ -35,7 +35,7 @@ "transition": "\u9077\u79fb\u6642\u9593(Transition Time)(ms)", "use_music_mode": "\u97f3\u697d\u30e2\u30fc\u30c9\u3092\u6709\u52b9\u306b\u3059\u308b" }, - "description": "\u30e2\u30c7\u30eb\u3092\u7a7a\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u81ea\u52d5\u7684\u306b\u691c\u51fa\u3055\u308c\u307e\u3059\u3002" + "description": "\u30e2\u30c7\u30eb\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u81ea\u52d5\u7684\u306b\u691c\u51fa\u3055\u308c\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/zwave_js/translations/ja.json b/homeassistant/components/zwave_js/translations/ja.json index 6df591dd0ab..9b00f062993 100644 --- a/homeassistant/components/zwave_js/translations/ja.json +++ b/homeassistant/components/zwave_js/translations/ja.json @@ -33,7 +33,7 @@ "s2_unauthenticated_key": "S2\u8a8d\u8a3c\u3055\u308c\u3066\u3044\u306a\u3044\u30ad\u30fc", "usb_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" }, - "description": "\u3053\u308c\u3089\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7a7a\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u30a2\u30c9\u30aa\u30f3\u306f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ad\u30fc\u3092\u751f\u6210\u3057\u307e\u3059\u3002", + "description": "\u3053\u308c\u3089\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7a7a\u767d\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u30a2\u30c9\u30aa\u30f3\u306f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ad\u30fc\u3092\u751f\u6210\u3057\u307e\u3059\u3002", "title": "Z-Wave JS\u30a2\u30c9\u30aa\u30f3\u306e\u8a2d\u5b9a\u3092\u5165\u529b" }, "hassio_confirm": { @@ -124,7 +124,7 @@ "s2_unauthenticated_key": "S2\u8a8d\u8a3c\u3055\u308c\u3066\u3044\u306a\u3044\u30ad\u30fc", "usb_path": "USB\u30c7\u30d0\u30a4\u30b9\u306e\u30d1\u30b9" }, - "description": "\u3053\u308c\u3089\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7a7a\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u30a2\u30c9\u30aa\u30f3\u306f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ad\u30fc\u3092\u751f\u6210\u3057\u307e\u3059\u3002", + "description": "\u3053\u308c\u3089\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u304c\u7a7a\u767d\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u30a2\u30c9\u30aa\u30f3\u306f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ad\u30fc\u3092\u751f\u6210\u3057\u307e\u3059\u3002", "title": "Z-Wave JS\u306e\u30a2\u30c9\u30aa\u30f3\u304c\u59cb\u307e\u308a\u307e\u3059\u3002" }, "install_addon": { From 7ab4960b1e9311e515e2a7771deb68d6cbc95adb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 12 May 2022 21:14:02 -0400 Subject: [PATCH 397/930] Restore v23 stats migration tests (#71743) --- .../models_schema_23_with_newer_columns.py | 613 +++++++++++++++++ .../recorder/test_statistics_v23_migration.py | 618 ++++++++++++++++++ 2 files changed, 1231 insertions(+) create mode 100644 tests/components/recorder/models_schema_23_with_newer_columns.py create mode 100644 tests/components/recorder/test_statistics_v23_migration.py diff --git a/tests/components/recorder/models_schema_23_with_newer_columns.py b/tests/components/recorder/models_schema_23_with_newer_columns.py new file mode 100644 index 00000000000..a086aa588d4 --- /dev/null +++ b/tests/components/recorder/models_schema_23_with_newer_columns.py @@ -0,0 +1,613 @@ +"""Models for SQLAlchemy. + +This file contains the model definitions for schema version 23 +used by Home Assistant Core 2021.11.0, which adds the name column +to statistics_meta. + +The v23 schema as been slightly modified to add the EventData table to +allow the recorder to startup successfully. + +It is used to test the schema migration logic. +""" +from __future__ import annotations + +from datetime import datetime, timedelta +import json +import logging +from typing import TypedDict, overload + +from sqlalchemy import ( + BigInteger, + Boolean, + Column, + DateTime, + Float, + ForeignKey, + Identity, + Index, + Integer, + SmallInteger, + String, + Text, + distinct, +) +from sqlalchemy.dialects import mysql, oracle, postgresql +from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm.session import Session + +from homeassistant.const import ( + MAX_LENGTH_EVENT_CONTEXT_ID, + MAX_LENGTH_EVENT_EVENT_TYPE, + MAX_LENGTH_EVENT_ORIGIN, + MAX_LENGTH_STATE_DOMAIN, + MAX_LENGTH_STATE_ENTITY_ID, + MAX_LENGTH_STATE_STATE, +) +from homeassistant.core import Context, Event, EventOrigin, State, split_entity_id +from homeassistant.helpers.json import JSONEncoder +import homeassistant.util.dt as dt_util + +# SQLAlchemy Schema +# pylint: disable=invalid-name +Base = declarative_base() + +SCHEMA_VERSION = 23 + +_LOGGER = logging.getLogger(__name__) + +DB_TIMEZONE = "+00:00" + +TABLE_EVENTS = "events" +TABLE_STATES = "states" +TABLE_RECORDER_RUNS = "recorder_runs" +TABLE_SCHEMA_CHANGES = "schema_changes" +TABLE_STATISTICS = "statistics" +TABLE_STATISTICS_META = "statistics_meta" +TABLE_STATISTICS_RUNS = "statistics_runs" +TABLE_STATISTICS_SHORT_TERM = "statistics_short_term" +TABLE_EVENT_DATA = "event_data" + +ALL_TABLES = [ + TABLE_STATES, + TABLE_EVENTS, + TABLE_RECORDER_RUNS, + TABLE_SCHEMA_CHANGES, + TABLE_STATISTICS, + TABLE_STATISTICS_META, + TABLE_STATISTICS_RUNS, + TABLE_STATISTICS_SHORT_TERM, +] + +DATETIME_TYPE = DateTime(timezone=True).with_variant( + mysql.DATETIME(timezone=True, fsp=6), "mysql" +) +DOUBLE_TYPE = ( + Float() + .with_variant(mysql.DOUBLE(asdecimal=False), "mysql") + .with_variant(oracle.DOUBLE_PRECISION(), "oracle") + .with_variant(postgresql.DOUBLE_PRECISION(), "postgresql") +) + + +class Events(Base): # type: ignore + """Event history data.""" + + __table_args__ = ( + # Used for fetching events at a specific time + # see logbook + Index("ix_events_event_type_time_fired", "event_type", "time_fired"), + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_EVENTS + event_id = Column(Integer, Identity(), primary_key=True) + event_type = Column(String(MAX_LENGTH_EVENT_EVENT_TYPE)) + event_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) + origin_idx = Column( + SmallInteger + ) # *** Not originally in v23, only added for recorder to startup ok + time_fired = Column(DATETIME_TYPE, index=True) + created = Column(DATETIME_TYPE, default=dt_util.utcnow) + context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + data_id = Column( + Integer, ForeignKey("event_data.data_id"), index=True + ) # *** Not originally in v23, only added for recorder to startup ok + event_data_rel = relationship( + "EventData" + ) # *** Not originally in v23, only added for recorder to startup ok + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event, event_data=None): + """Create an event database object from a native event.""" + return Events( + event_type=event.event_type, + event_data=event_data + or json.dumps(event.data, cls=JSONEncoder, separators=(",", ":")), + origin=str(event.origin.value), + time_fired=event.time_fired, + context_id=event.context.id, + context_user_id=event.context.user_id, + context_parent_id=event.context.parent_id, + ) + + def to_native(self, validate_entity_id=True): + """Convert to a native HA Event.""" + context = Context( + id=self.context_id, + user_id=self.context_user_id, + parent_id=self.context_parent_id, + ) + try: + return Event( + self.event_type, + json.loads(self.event_data), + EventOrigin(self.origin), + process_timestamp(self.time_fired), + context=context, + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting to event: %s", self) + return None + + +# *** Not originally in v23, only added for recorder to startup ok +# This is not being tested by the v23 statistics migration tests +class EventData(Base): # type: ignore[misc,valid-type] + """Event data history.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_EVENT_DATA + data_id = Column(Integer, Identity(), primary_key=True) + hash = Column(BigInteger, index=True) + # Note that this is not named attributes to avoid confusion with the states table + shared_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + + +class States(Base): # type: ignore + """State change history.""" + + __table_args__ = ( + # Used for fetching the state of entities at a specific time + # (get_states in history.py) + Index("ix_states_entity_id_last_updated", "entity_id", "last_updated"), + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_STATES + state_id = Column(Integer, Identity(), primary_key=True) + domain = Column(String(MAX_LENGTH_STATE_DOMAIN)) + entity_id = Column(String(MAX_LENGTH_STATE_ENTITY_ID)) + state = Column(String(MAX_LENGTH_STATE_STATE)) + attributes = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + event_id = Column( + Integer, ForeignKey("events.event_id", ondelete="CASCADE"), index=True + ) + last_changed = Column(DATETIME_TYPE, default=dt_util.utcnow) + last_updated = Column(DATETIME_TYPE, default=dt_util.utcnow, index=True) + created = Column(DATETIME_TYPE, default=dt_util.utcnow) + old_state_id = Column(Integer, ForeignKey("states.state_id"), index=True) + event = relationship("Events", uselist=False) + old_state = relationship("States", remote_side=[state_id]) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event): + """Create object from a state_changed event.""" + entity_id = event.data["entity_id"] + state = event.data.get("new_state") + + dbstate = States(entity_id=entity_id) + + # State got deleted + if state is None: + dbstate.state = "" + dbstate.domain = split_entity_id(entity_id)[0] + dbstate.attributes = "{}" + dbstate.last_changed = event.time_fired + dbstate.last_updated = event.time_fired + else: + dbstate.domain = state.domain + dbstate.state = state.state + dbstate.attributes = json.dumps( + dict(state.attributes), cls=JSONEncoder, separators=(",", ":") + ) + dbstate.last_changed = state.last_changed + dbstate.last_updated = state.last_updated + + return dbstate + + def to_native(self, validate_entity_id=True): + """Convert to an HA state object.""" + try: + return State( + self.entity_id, + self.state, + json.loads(self.attributes), + process_timestamp(self.last_changed), + process_timestamp(self.last_updated), + # Join the events table on event_id to get the context instead + # as it will always be there for state_changed events + context=Context(id=None), + validate_entity_id=validate_entity_id, + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting row to state: %s", self) + return None + + +class StatisticResult(TypedDict): + """Statistic result data class. + + Allows multiple datapoints for the same statistic_id. + """ + + meta: StatisticMetaData + stat: StatisticData + + +class StatisticDataBase(TypedDict): + """Mandatory fields for statistic data class.""" + + start: datetime + + +class StatisticData(StatisticDataBase, total=False): + """Statistic data class.""" + + mean: float + min: float + max: float + last_reset: datetime | None + state: float + sum: float + + +class StatisticsBase: + """Statistics base class.""" + + id = Column(Integer, Identity(), primary_key=True) + created = Column(DATETIME_TYPE, default=dt_util.utcnow) + + @declared_attr + def metadata_id(self): + """Define the metadata_id column for sub classes.""" + return Column( + Integer, + ForeignKey(f"{TABLE_STATISTICS_META}.id", ondelete="CASCADE"), + index=True, + ) + + start = Column(DATETIME_TYPE, index=True) + mean = Column(DOUBLE_TYPE) + min = Column(DOUBLE_TYPE) + max = Column(DOUBLE_TYPE) + last_reset = Column(DATETIME_TYPE) + state = Column(DOUBLE_TYPE) + sum = Column(DOUBLE_TYPE) + + @classmethod + def from_stats(cls, metadata_id: int, stats: StatisticData): + """Create object from a statistics.""" + return cls( # type: ignore + metadata_id=metadata_id, + **stats, + ) + + +class Statistics(Base, StatisticsBase): # type: ignore + """Long term statistics.""" + + duration = timedelta(hours=1) + + __table_args__ = ( + # Used for fetching statistics for a certain entity at a specific time + Index("ix_statistics_statistic_id_start", "metadata_id", "start"), + ) + __tablename__ = TABLE_STATISTICS + + +class StatisticsShortTerm(Base, StatisticsBase): # type: ignore + """Short term statistics.""" + + duration = timedelta(minutes=5) + + __table_args__ = ( + # Used for fetching statistics for a certain entity at a specific time + Index("ix_statistics_short_term_statistic_id_start", "metadata_id", "start"), + ) + __tablename__ = TABLE_STATISTICS_SHORT_TERM + + +class StatisticMetaData(TypedDict): + """Statistic meta data class.""" + + has_mean: bool + has_sum: bool + name: str | None + source: str + statistic_id: str + unit_of_measurement: str | None + + +class StatisticsMeta(Base): # type: ignore + """Statistics meta data.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_STATISTICS_META + id = Column(Integer, Identity(), primary_key=True) + statistic_id = Column(String(255), index=True) + source = Column(String(32)) + unit_of_measurement = Column(String(255)) + has_mean = Column(Boolean) + has_sum = Column(Boolean) + name = Column(String(255)) + + @staticmethod + def from_meta(meta: StatisticMetaData) -> StatisticsMeta: + """Create object from meta data.""" + return StatisticsMeta(**meta) + + +class RecorderRuns(Base): # type: ignore + """Representation of recorder run.""" + + __table_args__ = (Index("ix_recorder_runs_start_end", "start", "end"),) + __tablename__ = TABLE_RECORDER_RUNS + run_id = Column(Integer, Identity(), primary_key=True) + start = Column(DateTime(timezone=True), default=dt_util.utcnow) + end = Column(DateTime(timezone=True)) + closed_incorrect = Column(Boolean, default=False) + created = Column(DateTime(timezone=True), default=dt_util.utcnow) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + end = ( + f"'{self.end.isoformat(sep=' ', timespec='seconds')}'" if self.end else None + ) + return ( + f"" + ) + + def entity_ids(self, point_in_time=None): + """Return the entity ids that existed in this run. + + Specify point_in_time if you want to know which existed at that point + in time inside the run. + """ + session = Session.object_session(self) + + assert session is not None, "RecorderRuns need to be persisted" + + query = session.query(distinct(States.entity_id)).filter( + States.last_updated >= self.start + ) + + if point_in_time is not None: + query = query.filter(States.last_updated < point_in_time) + elif self.end is not None: + query = query.filter(States.last_updated < self.end) + + return [row[0] for row in query] + + def to_native(self, validate_entity_id=True): + """Return self, native format is this model.""" + return self + + +class SchemaChanges(Base): # type: ignore + """Representation of schema version changes.""" + + __tablename__ = TABLE_SCHEMA_CHANGES + change_id = Column(Integer, Identity(), primary_key=True) + schema_version = Column(Integer) + changed = Column(DateTime(timezone=True), default=dt_util.utcnow) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + +class StatisticsRuns(Base): # type: ignore + """Representation of statistics run.""" + + __tablename__ = TABLE_STATISTICS_RUNS + run_id = Column(Integer, Identity(), primary_key=True) + start = Column(DateTime(timezone=True)) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + +@overload +def process_timestamp(ts: None) -> None: + ... + + +@overload +def process_timestamp(ts: datetime) -> datetime: + ... + + +def process_timestamp(ts: datetime | None) -> datetime | None: + """Process a timestamp into datetime object.""" + if ts is None: + return None + if ts.tzinfo is None: + return ts.replace(tzinfo=dt_util.UTC) + + return dt_util.as_utc(ts) + + +@overload +def process_timestamp_to_utc_isoformat(ts: None) -> None: + ... + + +@overload +def process_timestamp_to_utc_isoformat(ts: datetime) -> str: + ... + + +def process_timestamp_to_utc_isoformat(ts: datetime | None) -> str | None: + """Process a timestamp into UTC isotime.""" + if ts is None: + return None + if ts.tzinfo == dt_util.UTC: + return ts.isoformat() + if ts.tzinfo is None: + return f"{ts.isoformat()}{DB_TIMEZONE}" + return ts.astimezone(dt_util.UTC).isoformat() + + +class LazyState(State): + """A lazy version of core State.""" + + __slots__ = [ + "_row", + "entity_id", + "state", + "_attributes", + "_last_changed", + "_last_updated", + "_context", + ] + + def __init__(self, row): # pylint: disable=super-init-not-called + """Init the lazy state.""" + self._row = row + self.entity_id = self._row.entity_id + self.state = self._row.state or "" + self._attributes = None + self._last_changed = None + self._last_updated = None + self._context = None + + @property # type: ignore + def attributes(self): + """State attributes.""" + if not self._attributes: + try: + self._attributes = json.loads(self._row.attributes) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting row to state: %s", self._row) + self._attributes = {} + return self._attributes + + @attributes.setter + def attributes(self, value): + """Set attributes.""" + self._attributes = value + + @property # type: ignore + def context(self): + """State context.""" + if not self._context: + self._context = Context(id=None) + return self._context + + @context.setter + def context(self, value): + """Set context.""" + self._context = value + + @property # type: ignore + def last_changed(self): + """Last changed datetime.""" + if not self._last_changed: + self._last_changed = process_timestamp(self._row.last_changed) + return self._last_changed + + @last_changed.setter + def last_changed(self, value): + """Set last changed datetime.""" + self._last_changed = value + + @property # type: ignore + def last_updated(self): + """Last updated datetime.""" + if not self._last_updated: + self._last_updated = process_timestamp(self._row.last_updated) + return self._last_updated + + @last_updated.setter + def last_updated(self, value): + """Set last updated datetime.""" + self._last_updated = value + + def as_dict(self): + """Return a dict representation of the LazyState. + + Async friendly. + + To be used for JSON serialization. + """ + if self._last_changed: + last_changed_isoformat = self._last_changed.isoformat() + else: + last_changed_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_changed + ) + if self._last_updated: + last_updated_isoformat = self._last_updated.isoformat() + else: + last_updated_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_updated + ) + return { + "entity_id": self.entity_id, + "state": self.state, + "attributes": self._attributes or self.attributes, + "last_changed": last_changed_isoformat, + "last_updated": last_updated_isoformat, + } + + def __eq__(self, other): + """Return the comparison.""" + return ( + other.__class__ in [self.__class__, State] + and self.entity_id == other.entity_id + and self.state == other.state + and self.attributes == other.attributes + ) diff --git a/tests/components/recorder/test_statistics_v23_migration.py b/tests/components/recorder/test_statistics_v23_migration.py new file mode 100644 index 00000000000..d487743a87f --- /dev/null +++ b/tests/components/recorder/test_statistics_v23_migration.py @@ -0,0 +1,618 @@ +"""The tests for sensor recorder platform migrating statistics from v23. + +The v23 schema used for these tests has been slightly modified to add the +EventData table to allow the recorder to startup successfully. +""" +# pylint: disable=protected-access,invalid-name +import importlib +import json +import sys +from unittest.mock import patch + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session + +from homeassistant.components import recorder +from homeassistant.components.recorder import SQLITE_URL_PREFIX, statistics +from homeassistant.components.recorder.util import session_scope +from homeassistant.setup import setup_component +import homeassistant.util.dt as dt_util + +from tests.common import get_test_home_assistant +from tests.components.recorder.common import wait_recording_done + +ORIG_TZ = dt_util.DEFAULT_TIME_ZONE + +CREATE_ENGINE_TARGET = "homeassistant.components.recorder.core.create_engine" +SCHEMA_MODULE = "tests.components.recorder.models_schema_23_with_newer_columns" + + +def _create_engine_test(*args, **kwargs): + """Test version of create_engine that initializes with old schema. + + This simulates an existing db with the old schema. + """ + importlib.import_module(SCHEMA_MODULE) + old_models = sys.modules[SCHEMA_MODULE] + engine = create_engine(*args, **kwargs) + old_models.Base.metadata.create_all(engine) + with Session(engine) as session: + session.add(recorder.models.StatisticsRuns(start=statistics.get_start_time())) + session.add( + recorder.models.SchemaChanges(schema_version=old_models.SCHEMA_VERSION) + ) + session.commit() + return engine + + +def test_delete_duplicates(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + importlib.import_module(SCHEMA_MODULE) + old_models = sys.modules[SCHEMA_MODULE] + + period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) + period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) + period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) + period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) + + external_energy_statistics_1 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 2, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 3, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 4, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + }, + ) + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_energy_statistics_2 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 20, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 30, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 40, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + ) + external_energy_metadata_2 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_2", + "unit_of_measurement": "kWh", + } + external_co2_statistics = ( + { + "start": period1, + "last_reset": None, + "mean": 10, + }, + { + "start": period2, + "last_reset": None, + "mean": 30, + }, + { + "start": period3, + "last_reset": None, + "mean": 60, + }, + { + "start": period4, + "last_reset": None, + "mean": 90, + }, + ) + external_co2_metadata = { + "has_mean": True, + "has_sum": False, + "name": "Fossil percentage", + "source": "test", + "statistic_id": "test:fossil_percentage", + "unit_of_measurement": "%", + } + + # Create some duplicated statistics with schema version 23 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch(CREATE_ENGINE_TARGET, new=_create_engine_test): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) + with session_scope(hass=hass) as session: + for stat in external_energy_statistics_1: + session.add(recorder.models.Statistics.from_stats(1, stat)) + for stat in external_energy_statistics_2: + session.add(recorder.models.Statistics.from_stats(2, stat)) + for stat in external_co2_statistics: + session.add(recorder.models.Statistics.from_stats(3, stat)) + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 23 + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + assert "Deleted 2 duplicated statistics rows" in caplog.text + assert "Found non identical" not in caplog.text + assert "Found duplicated" not in caplog.text + + +def test_delete_duplicates_many(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + importlib.import_module(SCHEMA_MODULE) + old_models = sys.modules[SCHEMA_MODULE] + + period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) + period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) + period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) + period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) + + external_energy_statistics_1 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 2, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 3, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 4, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + }, + ) + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_energy_statistics_2 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 20, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 30, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 40, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + ) + external_energy_metadata_2 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_2", + "unit_of_measurement": "kWh", + } + external_co2_statistics = ( + { + "start": period1, + "last_reset": None, + "mean": 10, + }, + { + "start": period2, + "last_reset": None, + "mean": 30, + }, + { + "start": period3, + "last_reset": None, + "mean": 60, + }, + { + "start": period4, + "last_reset": None, + "mean": 90, + }, + ) + external_co2_metadata = { + "has_mean": True, + "has_sum": False, + "name": "Fossil percentage", + "source": "test", + "statistic_id": "test:fossil_percentage", + "unit_of_measurement": "%", + } + + # Create some duplicated statistics with schema version 23 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch(CREATE_ENGINE_TARGET, new=_create_engine_test): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) + with session_scope(hass=hass) as session: + for stat in external_energy_statistics_1: + session.add(recorder.models.Statistics.from_stats(1, stat)) + for _ in range(3000): + session.add( + recorder.models.Statistics.from_stats( + 1, external_energy_statistics_1[-1] + ) + ) + for stat in external_energy_statistics_2: + session.add(recorder.models.Statistics.from_stats(2, stat)) + for stat in external_co2_statistics: + session.add(recorder.models.Statistics.from_stats(3, stat)) + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 23 + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + assert "Deleted 3002 duplicated statistics rows" in caplog.text + assert "Found non identical" not in caplog.text + assert "Found duplicated" not in caplog.text + + +@pytest.mark.freeze_time("2021-08-01 00:00:00+00:00") +def test_delete_duplicates_non_identical(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + importlib.import_module(SCHEMA_MODULE) + old_models = sys.modules[SCHEMA_MODULE] + + period1 = dt_util.as_utc(dt_util.parse_datetime("2021-09-01 00:00:00")) + period2 = dt_util.as_utc(dt_util.parse_datetime("2021-09-30 23:00:00")) + period3 = dt_util.as_utc(dt_util.parse_datetime("2021-10-01 00:00:00")) + period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) + + external_energy_statistics_1 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 2, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 3, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 4, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 6, + }, + ) + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_energy_statistics_2 = ( + { + "start": period1, + "last_reset": None, + "state": 0, + "sum": 20, + }, + { + "start": period2, + "last_reset": None, + "state": 1, + "sum": 30, + }, + { + "start": period3, + "last_reset": None, + "state": 2, + "sum": 40, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 50, + }, + ) + external_energy_metadata_2 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_2", + "unit_of_measurement": "kWh", + } + + # Create some duplicated statistics with schema version 23 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch(CREATE_ENGINE_TARGET, new=_create_engine_test): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + with session_scope(hass=hass) as session: + for stat in external_energy_statistics_1: + session.add(recorder.models.Statistics.from_stats(1, stat)) + for stat in external_energy_statistics_2: + session.add(recorder.models.Statistics.from_stats(2, stat)) + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 23 + hass = get_test_home_assistant() + hass.config.config_dir = tmpdir + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + assert "Deleted 2 duplicated statistics rows" in caplog.text + assert "Deleted 1 non identical" in caplog.text + assert "Found duplicated" not in caplog.text + + isotime = dt_util.utcnow().isoformat() + backup_file_name = f".storage/deleted_statistics.{isotime}.json" + + with open(hass.config.path(backup_file_name)) as backup_file: + backup = json.load(backup_file) + + assert backup == [ + { + "duplicate": { + "created": "2021-08-01T00:00:00", + "id": 4, + "last_reset": None, + "max": None, + "mean": None, + "metadata_id": 1, + "min": None, + "start": "2021-10-31T23:00:00", + "state": 3.0, + "sum": 5.0, + }, + "original": { + "created": "2021-08-01T00:00:00", + "id": 5, + "last_reset": None, + "max": None, + "mean": None, + "metadata_id": 1, + "min": None, + "start": "2021-10-31T23:00:00", + "state": 3.0, + "sum": 6.0, + }, + } + ] + + +def test_delete_duplicates_short_term(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + importlib.import_module(SCHEMA_MODULE) + old_models = sys.modules[SCHEMA_MODULE] + + period4 = dt_util.as_utc(dt_util.parse_datetime("2021-10-31 23:00:00")) + + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + statistic_row = { + "start": period4, + "last_reset": None, + "state": 3, + "sum": 5, + } + + # Create some duplicated statistics with schema version 23 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch(CREATE_ENGINE_TARGET, new=_create_engine_test): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsShortTerm.from_stats(1, statistic_row) + ) + session.add( + recorder.models.StatisticsShortTerm.from_stats(1, statistic_row) + ) + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 23 + hass = get_test_home_assistant() + hass.config.config_dir = tmpdir + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + assert "duplicated statistics rows" not in caplog.text + assert "Found non identical" not in caplog.text + assert "Deleted duplicated short term statistic" in caplog.text From 32e4046435428567cba74c8dbc615e339f33cf70 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 12 May 2022 19:33:52 -0700 Subject: [PATCH 398/930] Prepare google calendar integration for Application Credentials (#71748) * Prepare google calendar integration for Application Credentials Update google calendar integration to have fewer dependencies on yaml configuration data to prepare for supporting application credentials, which means setup can happen without configuration.yaml at all. This pre-factoring will allow the following PR adding application credentials support to be more focused. * Add test coverage for device auth checks --- homeassistant/components/google/__init__.py | 33 ++++++++++------- homeassistant/components/google/api.py | 35 ++++++++++++++----- .../components/google/config_flow.py | 28 ++++++++++++--- homeassistant/components/google/const.py | 3 ++ tests/components/google/test_config_flow.py | 29 ++++++++++++++- 5 files changed, 103 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 98eadead101..c5f62c0034f 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -40,7 +40,7 @@ from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.typing import ConfigType from . import config_flow -from .api import ApiAuthImpl, DeviceAuth +from .api import ApiAuthImpl, DeviceAuth, get_feature_access from .const import ( CONF_CALENDAR_ACCESS, DATA_CONFIG, @@ -172,7 +172,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # a ConfigEntry managed by home assistant. storage = Storage(hass.config.path(TOKEN_FILE)) creds = await hass.async_add_executor_job(storage.get) - if creds and conf[CONF_CALENDAR_ACCESS].scope in creds.scopes: + if creds and get_feature_access(hass).scope in creds.scopes: _LOGGER.debug("Importing configuration entry with credentials") hass.async_create_task( hass.config_entries.flow.async_init( @@ -210,8 +210,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except aiohttp.ClientError as err: raise ConfigEntryNotReady from err - required_scope = hass.data[DOMAIN][DATA_CONFIG][CONF_CALENDAR_ACCESS].scope - if required_scope not in session.token.get("scope", []): + access = get_feature_access(hass) + if access.scope not in session.token.get("scope", []): raise ConfigEntryAuthFailed( "Required scopes are not available, reauth required" ) @@ -220,7 +220,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) hass.data[DOMAIN][DATA_SERVICE] = calendar_service - await async_setup_services(hass, hass.data[DOMAIN][DATA_CONFIG], calendar_service) + track_new = hass.data[DOMAIN][DATA_CONFIG].get(CONF_TRACK_NEW, True) + await async_setup_services(hass, track_new, calendar_service) + # Only expose the add event service if we have the correct permissions + if access is FeatureAccess.read_write: + await async_setup_add_event_service(hass, calendar_service) hass.config_entries.async_setup_platforms(entry, PLATFORMS) @@ -234,7 +238,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_services( hass: HomeAssistant, - config: ConfigType, + track_new: bool, calendar_service: GoogleCalendarService, ) -> None: """Set up the service listeners.""" @@ -274,7 +278,7 @@ async def async_setup_services( tasks = [] for calendar_item in result.items: calendar = calendar_item.dict(exclude_unset=True) - calendar[CONF_TRACK] = config[CONF_TRACK_NEW] + calendar[CONF_TRACK] = track_new tasks.append( hass.services.async_call(DOMAIN, SERVICE_FOUND_CALENDARS, calendar) ) @@ -282,6 +286,13 @@ async def async_setup_services( hass.services.async_register(DOMAIN, SERVICE_SCAN_CALENDARS, _scan_for_calendars) + +async def async_setup_add_event_service( + hass: HomeAssistant, + calendar_service: GoogleCalendarService, +) -> None: + """Add the service to add events.""" + async def _add_event(call: ServiceCall) -> None: """Add a new event to calendar.""" start: DateOrDatetime | None = None @@ -333,11 +344,9 @@ async def async_setup_services( ), ) - # Only expose the add event service if we have the correct permissions - if config.get(CONF_CALENDAR_ACCESS) is FeatureAccess.read_write: - hass.services.async_register( - DOMAIN, SERVICE_ADD_EVENT, _add_event, schema=ADD_EVENT_SERVICE_SCHEMA - ) + hass.services.async_register( + DOMAIN, SERVICE_ADD_EVENT, _add_event, schema=ADD_EVENT_SERVICE_SCHEMA + ) def get_calendar_info( diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index 5de563393e1..bb32d46f0e4 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -19,18 +19,25 @@ from oauth2client.client import ( OAuth2WebServerFlow, ) -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.event import async_track_time_interval from homeassistant.util import dt -from .const import CONF_CALENDAR_ACCESS, DATA_CONFIG, DEVICE_AUTH_IMPL, DOMAIN +from .const import ( + CONF_CALENDAR_ACCESS, + DATA_CONFIG, + DEFAULT_FEATURE_ACCESS, + DEVICE_AUTH_IMPL, + DOMAIN, + FeatureAccess, +) _LOGGER = logging.getLogger(__name__) EVENT_PAGE_SIZE = 100 EXCHANGE_TIMEOUT_SECONDS = 60 +DEVICE_AUTH_CREDS = "creds" class OAuthError(Exception): @@ -53,7 +60,7 @@ class DeviceAuth(config_entry_oauth2_flow.LocalOAuth2Implementation): async def async_resolve_external_data(self, external_data: Any) -> dict: """Resolve a Google API Credentials object to Home Assistant token.""" - creds: Credentials = external_data["creds"] + creds: Credentials = external_data[DEVICE_AUTH_CREDS] return { "access_token": creds.access_token, "refresh_token": creds.refresh_token, @@ -132,13 +139,25 @@ class DeviceFlow: ) -async def async_create_device_flow(hass: HomeAssistant) -> DeviceFlow: +def get_feature_access(hass: HomeAssistant) -> FeatureAccess: + """Return the desired calendar feature access.""" + # This may be called during config entry setup without integration setup running when there + # is no google entry in configuration.yaml + return ( + hass.data.get(DOMAIN, {}) + .get(DATA_CONFIG, {}) + .get(CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS) + ) + + +async def async_create_device_flow( + hass: HomeAssistant, client_id: str, client_secret: str, access: FeatureAccess +) -> DeviceFlow: """Create a new Device flow.""" - conf = hass.data[DOMAIN][DATA_CONFIG] oauth_flow = OAuth2WebServerFlow( - client_id=conf[CONF_CLIENT_ID], - client_secret=conf[CONF_CLIENT_SECRET], - scope=conf[CONF_CALENDAR_ACCESS].scope, + client_id=client_id, + client_secret=client_secret, + scope=access.scope, redirect_uri="", ) try: diff --git a/homeassistant/components/google/config_flow.py b/homeassistant/components/google/config_flow.py index 8bbd2a6c2b1..f5e567a5272 100644 --- a/homeassistant/components/google/config_flow.py +++ b/homeassistant/components/google/config_flow.py @@ -9,7 +9,14 @@ from oauth2client.client import Credentials from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_entry_oauth2_flow -from .api import DeviceFlow, OAuthError, async_create_device_flow +from .api import ( + DEVICE_AUTH_CREDS, + DeviceAuth, + DeviceFlow, + OAuthError, + async_create_device_flow, + get_feature_access, +) from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -67,15 +74,28 @@ class OAuth2FlowHandler( if not self._device_flow: _LOGGER.debug("Creating DeviceAuth flow") + if not isinstance(self.flow_impl, DeviceAuth): + _LOGGER.error( + "Unexpected OAuth implementation does not support device auth: %s", + self.flow_impl, + ) + return self.async_abort(reason="oauth_error") try: - device_flow = await async_create_device_flow(self.hass) + device_flow = await async_create_device_flow( + self.hass, + self.flow_impl.client_id, + self.flow_impl.client_secret, + get_feature_access(self.hass), + ) except OAuthError as err: _LOGGER.error("Error initializing device flow: %s", str(err)) return self.async_abort(reason="oauth_error") self._device_flow = device_flow async def _exchange_finished(creds: Credentials | None) -> None: - self.external_data = {"creds": creds} # is None on timeout/expiration + self.external_data = { + DEVICE_AUTH_CREDS: creds + } # is None on timeout/expiration self.hass.async_create_task( self.hass.config_entries.flow.async_configure( flow_id=self.flow_id, user_input={} @@ -97,7 +117,7 @@ class OAuth2FlowHandler( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle external yaml configuration.""" - if self.external_data.get("creds") is None: + if self.external_data.get(DEVICE_AUTH_CREDS) is None: return self.async_abort(reason="code_expired") return await super().async_step_creation(user_input) diff --git a/homeassistant/components/google/const.py b/homeassistant/components/google/const.py index d5cdabb0638..c01ff1ea48b 100644 --- a/homeassistant/components/google/const.py +++ b/homeassistant/components/google/const.py @@ -28,3 +28,6 @@ class FeatureAccess(Enum): def scope(self) -> str: """Google calendar scope for the feature.""" return self._scope + + +DEFAULT_FEATURE_ACCESS = FeatureAccess.read_write diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index e96a4c3fd5f..0991f4e5194 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -13,6 +13,7 @@ import pytest from homeassistant import config_entries from homeassistant.components.google.const import DOMAIN from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.util.dt import utcnow from .conftest import ComponentSetup, YieldFixture @@ -254,7 +255,7 @@ async def test_existing_config_entry( async def test_missing_configuration( hass: HomeAssistant, ) -> None: - """Test can't configure when config entry already exists.""" + """Test can't configure when no authentication source is available.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -262,6 +263,32 @@ async def test_missing_configuration( assert result.get("reason") == "missing_configuration" +async def test_wrong_configuration( + hass: HomeAssistant, +) -> None: + """Test can't use the wrong type of authentication.""" + + # Google calendar flow currently only supports device auth + config_entry_oauth2_flow.async_register_implementation( + hass, + DOMAIN, + config_entry_oauth2_flow.LocalOAuth2Implementation( + hass, + DOMAIN, + "client-id", + "client-secret", + "http://example/authorize", + "http://example/token", + ), + ) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == "abort" + assert result.get("reason") == "oauth_error" + + async def test_import_config_entry_from_existing_token( hass: HomeAssistant, mock_token_read: None, From 75058e63a4a9dbe5facf66532134684bcc9e082e Mon Sep 17 00:00:00 2001 From: Jelte Zeilstra Date: Fri, 13 May 2022 09:17:41 +0200 Subject: [PATCH 399/930] Create Update entities for Ubiquiti network devices (#71700) Co-authored-by: Robert Svensson --- homeassistant/components/unifi/controller.py | 2 +- .../components/unifi/device_tracker.py | 10 ++ homeassistant/components/unifi/update.py | 128 ++++++++++++++ tests/components/unifi/test_device_tracker.py | 1 + tests/components/unifi/test_diagnostics.py | 4 + tests/components/unifi/test_update.py | 167 ++++++++++++++++++ 6 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/unifi/update.py create mode 100644 tests/components/unifi/test_update.py diff --git a/homeassistant/components/unifi/controller.py b/homeassistant/components/unifi/controller.py index be59a25f69f..fa92568b477 100644 --- a/homeassistant/components/unifi/controller.py +++ b/homeassistant/components/unifi/controller.py @@ -74,7 +74,7 @@ from .switch import BLOCK_SWITCH, POE_SWITCH RETRY_TIMER = 15 CHECK_HEARTBEAT_INTERVAL = timedelta(seconds=1) -PLATFORMS = [Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH, Platform.UPDATE] CLIENT_CONNECTED = ( WIRED_CLIENT_CONNECTED, diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 2ae8eb2e32b..947e4523531 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -429,6 +429,16 @@ class UniFiDeviceTracker(UniFiBase, ScannerEntity): return attributes + @property + def ip_address(self) -> str: + """Return the primary ip address of the device.""" + return self.device.ip + + @property + def mac_address(self) -> str: + """Return the mac address of the device.""" + return self.device.mac + async def options_updated(self) -> None: """Config entry options are updated, remove entity if option is disabled.""" if not self.controller.option_track_devices: diff --git a/homeassistant/components/unifi/update.py b/homeassistant/components/unifi/update.py new file mode 100644 index 00000000000..09720f15f84 --- /dev/null +++ b/homeassistant/components/unifi/update.py @@ -0,0 +1,128 @@ +"""Update entities for Ubiquiti network devices.""" +from __future__ import annotations + +import logging + +from homeassistant.components.update import ( + DOMAIN, + UpdateDeviceClass, + UpdateEntity, + UpdateEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_NAME +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN +from .unifi_entity_base import UniFiBase + +LOGGER = logging.getLogger(__name__) + +DEVICE_UPDATE = "device_update" + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up update entities for UniFi Network integration.""" + controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + controller.entities[DOMAIN] = {DEVICE_UPDATE: set()} + + @callback + def items_added( + clients: set = controller.api.clients, devices: set = controller.api.devices + ) -> None: + """Add device update entities.""" + add_device_update_entities(controller, async_add_entities, devices) + + for signal in (controller.signal_update, controller.signal_options_update): + config_entry.async_on_unload( + async_dispatcher_connect(hass, signal, items_added) + ) + + items_added() + + +@callback +def add_device_update_entities(controller, async_add_entities, devices): + """Add new device update entities from the controller.""" + entities = [] + + for mac in devices: + if mac in controller.entities[DOMAIN][UniFiDeviceUpdateEntity.TYPE]: + continue + + device = controller.api.devices[mac] + entities.append(UniFiDeviceUpdateEntity(device, controller)) + + if entities: + async_add_entities(entities) + + +class UniFiDeviceUpdateEntity(UniFiBase, UpdateEntity): + """Update entity for a UniFi network infrastructure device.""" + + DOMAIN = DOMAIN + TYPE = DEVICE_UPDATE + _attr_device_class = UpdateDeviceClass.FIRMWARE + _attr_supported_features = UpdateEntityFeature.PROGRESS + + def __init__(self, device, controller): + """Set up device update entity.""" + super().__init__(device, controller) + + self.device = self._item + + @property + def name(self) -> str: + """Return the name of the device.""" + return self.device.name or self.device.model + + @property + def unique_id(self) -> str: + """Return a unique identifier for this device.""" + return f"{self.TYPE}-{self.device.mac}" + + @property + def available(self) -> bool: + """Return if controller is available.""" + return not self.device.disabled and self.controller.available + + @property + def in_progress(self) -> bool: + """Update installation in progress.""" + return self.device.state == 4 + + @property + def installed_version(self) -> str | None: + """Version currently in use.""" + return self.device.version + + @property + def latest_version(self) -> str | None: + """Latest version available for install.""" + return self.device.upgrade_to_firmware or self.device.version + + @property + def device_info(self) -> DeviceInfo: + """Return a device description for device registry.""" + info = DeviceInfo( + connections={(CONNECTION_NETWORK_MAC, self.device.mac)}, + manufacturer=ATTR_MANUFACTURER, + model=self.device.model, + sw_version=self.device.version, + ) + + if self.device.name: + info[ATTR_NAME] = self.device.name + + return info + + async def options_updated(self) -> None: + """No action needed.""" diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index 532f19c35ae..736e85d1b8c 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -499,6 +499,7 @@ async def test_option_track_devices(hass, aioclient_mock, mock_device_registry): "board_rev": 3, "device_id": "mock-id", "last_seen": 1562600145, + "ip": "10.0.1.1", "mac": "00:00:00:00:01:01", "model": "US16P150", "name": "Device", diff --git a/tests/components/unifi/test_diagnostics.py b/tests/components/unifi/test_diagnostics.py index ccec7fcb48a..6584b947293 100644 --- a/tests/components/unifi/test_diagnostics.py +++ b/tests/components/unifi/test_diagnostics.py @@ -14,6 +14,7 @@ from homeassistant.components.unifi.switch import ( OUTLET_SWITCH, POE_SWITCH, ) +from homeassistant.components.unifi.update import DEVICE_UPDATE from homeassistant.const import Platform from .test_controller import setup_unifi_integration @@ -161,6 +162,9 @@ async def test_entry_diagnostics(hass, hass_client, aioclient_mock): POE_SWITCH: ["00:00:00:00:00:00"], OUTLET_SWITCH: [], }, + str(Platform.UPDATE): { + DEVICE_UPDATE: ["00:00:00:00:00:01"], + }, }, "clients": { "00:00:00:00:00:00": { diff --git a/tests/components/unifi/test_update.py b/tests/components/unifi/test_update.py new file mode 100644 index 00000000000..b5eb9c1d02e --- /dev/null +++ b/tests/components/unifi/test_update.py @@ -0,0 +1,167 @@ +"""The tests for the UniFi Network update platform.""" + +from aiounifi.controller import MESSAGE_DEVICE +from aiounifi.websocket import STATE_DISCONNECTED, STATE_RUNNING + +from homeassistant.components.update import ( + ATTR_IN_PROGRESS, + ATTR_INSTALLED_VERSION, + ATTR_LATEST_VERSION, + DOMAIN as UPDATE_DOMAIN, + UpdateDeviceClass, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, +) + +from .test_controller import setup_unifi_integration + + +async def test_no_entities(hass, aioclient_mock): + """Test the update_clients function when no clients are found.""" + await setup_unifi_integration(hass, aioclient_mock) + + assert len(hass.states.async_entity_ids(UPDATE_DOMAIN)) == 0 + + +async def test_device_updates( + hass, aioclient_mock, mock_unifi_websocket, mock_device_registry +): + """Test the update_items function with some devices.""" + device_1 = { + "board_rev": 3, + "device_id": "mock-id", + "has_fan": True, + "fan_level": 0, + "ip": "10.0.1.1", + "last_seen": 1562600145, + "mac": "00:00:00:00:01:01", + "model": "US16P150", + "name": "Device 1", + "next_interval": 20, + "overheating": True, + "state": 1, + "type": "usw", + "upgradable": True, + "version": "4.0.42.10433", + "upgrade_to_firmware": "4.3.17.11279", + } + device_2 = { + "board_rev": 3, + "device_id": "mock-id", + "has_fan": True, + "ip": "10.0.1.2", + "mac": "00:00:00:00:01:02", + "model": "US16P150", + "name": "Device 2", + "next_interval": 20, + "state": 0, + "type": "usw", + "version": "4.0.42.10433", + } + await setup_unifi_integration( + hass, + aioclient_mock, + devices_response=[device_1, device_2], + ) + + assert len(hass.states.async_entity_ids(UPDATE_DOMAIN)) == 2 + + device_1_state = hass.states.get("update.device_1") + assert device_1_state.state == STATE_ON + assert device_1_state.attributes[ATTR_INSTALLED_VERSION] == "4.0.42.10433" + assert device_1_state.attributes[ATTR_LATEST_VERSION] == "4.3.17.11279" + assert device_1_state.attributes[ATTR_IN_PROGRESS] is False + assert device_1_state.attributes[ATTR_DEVICE_CLASS] == UpdateDeviceClass.FIRMWARE + + device_2_state = hass.states.get("update.device_2") + assert device_2_state.state == STATE_OFF + assert device_2_state.attributes[ATTR_INSTALLED_VERSION] == "4.0.42.10433" + assert device_2_state.attributes[ATTR_LATEST_VERSION] == "4.0.42.10433" + assert device_2_state.attributes[ATTR_IN_PROGRESS] is False + assert device_2_state.attributes[ATTR_DEVICE_CLASS] == UpdateDeviceClass.FIRMWARE + + # Simulate start of update + + device_1["state"] = 4 + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_DEVICE}, + "data": [device_1], + } + ) + await hass.async_block_till_done() + + device_1_state = hass.states.get("update.device_1") + assert device_1_state.state == STATE_ON + assert device_1_state.attributes[ATTR_INSTALLED_VERSION] == "4.0.42.10433" + assert device_1_state.attributes[ATTR_LATEST_VERSION] == "4.3.17.11279" + assert device_1_state.attributes[ATTR_IN_PROGRESS] is True + + # Simulate update finished + + device_1["state"] = "0" + device_1["version"] = "4.3.17.11279" + device_1["upgradable"] = False + del device_1["upgrade_to_firmware"] + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_DEVICE}, + "data": [device_1], + } + ) + await hass.async_block_till_done() + + device_1_state = hass.states.get("update.device_1") + assert device_1_state.state == STATE_OFF + assert device_1_state.attributes[ATTR_INSTALLED_VERSION] == "4.3.17.11279" + assert device_1_state.attributes[ATTR_LATEST_VERSION] == "4.3.17.11279" + assert device_1_state.attributes[ATTR_IN_PROGRESS] is False + + +async def test_controller_state_change( + hass, aioclient_mock, mock_unifi_websocket, mock_device_registry +): + """Verify entities state reflect on controller becoming unavailable.""" + device = { + "board_rev": 3, + "device_id": "mock-id", + "has_fan": True, + "fan_level": 0, + "ip": "10.0.1.1", + "last_seen": 1562600145, + "mac": "00:00:00:00:01:01", + "model": "US16P150", + "name": "Device", + "next_interval": 20, + "overheating": True, + "state": 1, + "type": "usw", + "upgradable": True, + "version": "4.0.42.10433", + "upgrade_to_firmware": "4.3.17.11279", + } + + await setup_unifi_integration( + hass, + aioclient_mock, + devices_response=[device], + ) + + assert len(hass.states.async_entity_ids(UPDATE_DOMAIN)) == 1 + assert hass.states.get("update.device").state == STATE_ON + + # Controller unavailable + mock_unifi_websocket(state=STATE_DISCONNECTED) + await hass.async_block_till_done() + + assert hass.states.get("update.device").state == STATE_UNAVAILABLE + + # Controller available + mock_unifi_websocket(state=STATE_RUNNING) + await hass.async_block_till_done() + + assert hass.states.get("update.device").state == STATE_ON From bed2d1e37b967f857aecb4ca54b5da9be09d8b93 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 13 May 2022 09:51:19 +0200 Subject: [PATCH 400/930] Streamline setup of deCONZ climate platform (#71708) --- homeassistant/components/deconz/climate.py | 50 +++++++++++----------- homeassistant/components/deconz/gateway.py | 2 + 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index aaedc7bae90..bdc366ff7db 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any +from pydeconz.models.event import EventType from pydeconz.models.sensor.thermostat import ( THERMOSTAT_FAN_MODE_AUTO, THERMOSTAT_FAN_MODE_HIGH, @@ -91,45 +92,42 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the deCONZ climate devices. - - Thermostats are based on the same device class as sensors in deCONZ. - """ + """Set up the deCONZ climate devices.""" gateway = get_gateway_from_config_entry(hass, config_entry) gateway.entities[DOMAIN] = set() @callback - def async_add_climate(sensors: list[Thermostat] | None = None) -> None: - """Add climate devices from deCONZ.""" - entities: list[DeconzThermostat] = [] + def async_add_climate(_: EventType, climate_id: str) -> None: + """Add climate from deCONZ.""" + climate = gateway.api.sensors.thermostat[climate_id] + if not gateway.option_allow_clip_sensor and climate.type.startswith("CLIP"): + return + async_add_entities([DeconzThermostat(climate, gateway)]) - if sensors is None: - sensors = list(gateway.api.sensors.thermostat.values()) + config_entry.async_on_unload( + gateway.api.sensors.thermostat.subscribe( + async_add_climate, + EventType.ADDED, + ) + ) + for climate_id in gateway.api.sensors.thermostat: + async_add_climate(EventType.ADDED, climate_id) - for sensor in sensors: - - if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): - continue - - if ( - isinstance(sensor, Thermostat) - and sensor.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzThermostat(sensor, gateway)) - - if entities: - async_add_entities(entities) + @callback + def async_reload_clip_sensors() -> None: + """Load clip climate sensors from deCONZ.""" + for climate_id, climate in gateway.api.sensors.thermostat.items(): + if climate.type.startswith("CLIP"): + async_add_climate(EventType.ADDED, climate_id) config_entry.async_on_unload( async_dispatcher_connect( hass, - gateway.signal_new_sensor, - async_add_climate, + gateway.signal_reload_clip_sensors, + async_reload_clip_sensors, ) ) - async_add_climate() - class DeconzThermostat(DeconzDevice, ClimateEntity): """Representation of a deCONZ thermostat.""" diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index f54cd4076d3..0e880fa22c1 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -63,6 +63,7 @@ class DeconzGateway: self.signal_reachable = f"deconz-reachable-{config_entry.entry_id}" self.signal_reload_groups = f"deconz_reload_group_{config_entry.entry_id}" + self.signal_reload_clip_sensors = f"deconz_reload_clip_{config_entry.entry_id}" self.signal_new_light = f"deconz_new_light_{config_entry.entry_id}" self.signal_new_sensor = f"deconz_new_sensor_{config_entry.entry_id}" @@ -212,6 +213,7 @@ class DeconzGateway: if self.option_allow_clip_sensor: self.async_add_device_callback(ResourceGroup.SENSOR.value) + async_dispatcher_send(self.hass, self.signal_reload_clip_sensors) else: deconz_ids += [ From 2e568771a92f27c4e97c3bbcf2c09ebdb69a3cfc Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 13 May 2022 10:16:56 +0200 Subject: [PATCH 401/930] Update coverage to 6.3.3 (#71772) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index b6fb5272abd..49615d104a7 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -8,7 +8,7 @@ -c homeassistant/package_constraints.txt -r requirements_test_pre_commit.txt codecov==2.1.12 -coverage==6.3.2 +coverage==6.3.3 freezegun==1.2.1 mock-open==1.4.0 mypy==0.950 From e6d7170fd8c227342cd217c89c997cd957ad0264 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 13 May 2022 10:18:16 +0200 Subject: [PATCH 402/930] Remove deprecated WLED update button (#71775) --- homeassistant/components/wled/button.py | 73 +------------- tests/components/wled/test_button.py | 127 +----------------------- 2 files changed, 3 insertions(+), 197 deletions(-) diff --git a/homeassistant/components/wled/button.py b/homeassistant/components/wled/button.py index 2967067ef44..97877053163 100644 --- a/homeassistant/components/wled/button.py +++ b/homeassistant/components/wled/button.py @@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN, LOGGER +from .const import DOMAIN from .coordinator import WLEDDataUpdateCoordinator from .helpers import wled_exception_handler from .models import WLEDEntity @@ -20,12 +20,7 @@ async def async_setup_entry( ) -> None: """Set up WLED button based on a config entry.""" coordinator: WLEDDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - async_add_entities( - [ - WLEDRestartButton(coordinator), - WLEDUpdateButton(coordinator), - ] - ) + async_add_entities([WLEDRestartButton(coordinator)]) class WLEDRestartButton(WLEDEntity, ButtonEntity): @@ -44,67 +39,3 @@ class WLEDRestartButton(WLEDEntity, ButtonEntity): async def async_press(self) -> None: """Send out a restart command.""" await self.coordinator.wled.reset() - - -class WLEDUpdateButton(WLEDEntity, ButtonEntity): - """Defines a WLED update button.""" - - _attr_device_class = ButtonDeviceClass.UPDATE - _attr_entity_category = EntityCategory.CONFIG - - # Disabled by default, as this entity is deprecated. - _attr_entity_registry_enabled_default = False - - def __init__(self, coordinator: WLEDDataUpdateCoordinator) -> None: - """Initialize the button entity.""" - super().__init__(coordinator=coordinator) - self._attr_name = f"{coordinator.data.info.name} Update" - self._attr_unique_id = f"{coordinator.data.info.mac_address}_update" - - @property - def available(self) -> bool: - """Return if the entity and an update is available.""" - current = self.coordinator.data.info.version - beta = self.coordinator.data.info.version_latest_beta - stable = self.coordinator.data.info.version_latest_stable - - # If we already run a pre-release, allow upgrading to a newer - # pre-release offer a normal upgrade otherwise. - return ( - super().available - and current is not None - and ( - (stable is not None and stable > current) - or ( - beta is not None - and (current.alpha or current.beta or current.release_candidate) - and beta > current - ) - ) - ) - - @wled_exception_handler - async def async_press(self) -> None: - """Send out a update command.""" - LOGGER.warning( - "The WLED update button '%s' is deprecated, please " - "use the new update entity as a replacement", - self.entity_id, - ) - current = self.coordinator.data.info.version - beta = self.coordinator.data.info.version_latest_beta - stable = self.coordinator.data.info.version_latest_stable - - # If we already run a pre-release, allow update to a newer - # pre-release or newer stable, otherwise, offer a normal stable updates. - version = stable - if ( - current is not None - and beta is not None - and (current.alpha or current.beta or current.release_candidate) - and beta > current - and beta > stable - ): - version = beta - - await self.coordinator.wled.upgrade(version=str(version)) diff --git a/tests/components/wled/test_button.py b/tests/components/wled/test_button.py index a09c7e2aaa3..3cfa4f762ad 100644 --- a/tests/components/wled/test_button.py +++ b/tests/components/wled/test_button.py @@ -1,5 +1,5 @@ """Tests for the WLED button platform.""" -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import MagicMock from freezegun import freeze_time import pytest @@ -95,128 +95,3 @@ async def test_button_connection_error( assert state assert state.state == STATE_UNAVAILABLE assert "Error communicating with API" in caplog.text - - -async def test_button_update_stay_stable( - hass: HomeAssistant, - entity_registry_enabled_by_default: AsyncMock, - init_integration: MockConfigEntry, - mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the update button. - - There is both an update for beta and stable available, however, the device - is currently running a stable version. Therefore, the update button should - update the the next stable (even though beta is newer). - """ - entity_registry = er.async_get(hass) - - entry = entity_registry.async_get("button.wled_rgb_light_update") - assert entry - assert entry.unique_id == "aabbccddeeff_update" - assert entry.entity_category is EntityCategory.CONFIG - - state = hass.states.get("button.wled_rgb_light_update") - assert state - assert state.state == STATE_UNKNOWN - assert state.attributes[ATTR_DEVICE_CLASS] == ButtonDeviceClass.UPDATE - - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.wled_rgb_light_update"}, - blocking=True, - ) - await hass.async_block_till_done() - assert mock_wled.upgrade.call_count == 1 - mock_wled.upgrade.assert_called_with(version="0.12.0") - assert ( - "The WLED update button 'button.wled_rgb_light_update' is deprecated" - in caplog.text - ) - - -@pytest.mark.parametrize("mock_wled", ["wled/rgbw.json"], indirect=True) -async def test_button_update_beta_to_stable( - hass: HomeAssistant, - entity_registry_enabled_by_default: AsyncMock, - init_integration: MockConfigEntry, - mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the update button. - - There is both an update for beta and stable available the device - is currently a beta, however, a newer stable is available. Therefore, the - update button should update to the next stable. - """ - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.wled_rgbw_light_update"}, - blocking=True, - ) - await hass.async_block_till_done() - assert mock_wled.upgrade.call_count == 1 - mock_wled.upgrade.assert_called_with(version="0.8.6") - assert ( - "The WLED update button 'button.wled_rgbw_light_update' is deprecated" - in caplog.text - ) - - -@pytest.mark.parametrize("mock_wled", ["wled/rgb_single_segment.json"], indirect=True) -async def test_button_update_stay_beta( - hass: HomeAssistant, - entity_registry_enabled_by_default: AsyncMock, - init_integration: MockConfigEntry, - mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, -) -> None: - """Test the update button. - - There is an update for beta and the device is currently a beta. Therefore, - the update button should update to the next beta. - """ - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.wled_rgb_light_update"}, - blocking=True, - ) - await hass.async_block_till_done() - assert mock_wled.upgrade.call_count == 1 - mock_wled.upgrade.assert_called_with(version="0.8.6b2") - assert ( - "The WLED update button 'button.wled_rgb_light_update' is deprecated" - in caplog.text - ) - - -@pytest.mark.parametrize("mock_wled", ["wled/rgb_websocket.json"], indirect=True) -async def test_button_no_update_available( - hass: HomeAssistant, - entity_registry_enabled_by_default: AsyncMock, - init_integration: MockConfigEntry, - mock_wled: MagicMock, -) -> None: - """Test the update button. There is no update available.""" - state = hass.states.get("button.wled_websocket_update") - assert state - assert state.state == STATE_UNAVAILABLE - - -async def test_disabled_by_default( - hass: HomeAssistant, init_integration: MockConfigEntry -) -> None: - """Test that the update button is disabled by default.""" - registry = er.async_get(hass) - - state = hass.states.get("button.wled_rgb_light_update") - assert state is None - - entry = registry.async_get("button.wled_rgb_light_update") - assert entry - assert entry.disabled - assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION From 3d05a9d31fcb39a55f73e0c2388d999f24750275 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 13 May 2022 10:38:36 +0200 Subject: [PATCH 403/930] Streamline setup of deCONZ lock from sensor platform (#71707) --- homeassistant/components/deconz/lock.py | 29 ++++++------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index 1ecc1802b74..5b8c9d87e67 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -11,7 +11,6 @@ from pydeconz.models.sensor.door_lock import DoorLock from homeassistant.components.lock import DOMAIN, LockEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback from .deconz_device import DeconzDevice @@ -43,33 +42,19 @@ async def async_setup_entry( async_add_lock_from_light(EventType.ADDED, lock_id) @callback - def async_add_lock_from_sensor(sensors: list[DoorLock] | None = None) -> None: + def async_add_lock_from_sensor(_: EventType, lock_id: str) -> None: """Add lock from deCONZ.""" - entities = [] - - if sensors is None: - sensors = list(gateway.api.sensors.door_lock.values()) - - for sensor in sensors: - - if ( - isinstance(sensor, DoorLock) - and sensor.unique_id not in gateway.entities[DOMAIN] - ): - entities.append(DeconzLock(sensor, gateway)) - - if entities: - async_add_entities(entities) + lock = gateway.api.sensors.door_lock[lock_id] + async_add_entities([DeconzLock(lock, gateway)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_sensor, + gateway.api.sensors.door_lock.subscribe( async_add_lock_from_sensor, + EventType.ADDED, ) ) - - async_add_lock_from_sensor() + for lock_id in gateway.api.sensors.door_lock: + async_add_lock_from_sensor(EventType.ADDED, lock_id) class DeconzLock(DeconzDevice, LockEntity): From d3c25bf4507e166ee98d283598c63b26afec64b4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 13 May 2022 10:40:14 +0200 Subject: [PATCH 404/930] Adjust pylint plugin for climate HVACAction (#70760) --- pylint/plugins/hass_imports.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index ae9961b49dd..afbd930ec30 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -68,6 +68,10 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { ), ], "homeassistant.components.climate.const": [ + ObsoleteImportMatch( + reason="replaced by HVACAction enum", + constant=re.compile(r"^CURRENT_HVAC_(\w*)$"), + ), ObsoleteImportMatch( reason="replaced by ClimateEntityFeature enum", constant=re.compile(r"^SUPPORT_(\w*)$"), From 3adaad73816a7df12b18c13fbf4de9aab2a85142 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 11:40:22 +0200 Subject: [PATCH 405/930] Migrate limitlessled light to color_mode (#69430) --- .../components/limitlessled/light.py | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/limitlessled/light.py b/homeassistant/components/limitlessled/light.py index 15b814775e6..41668470dfb 100644 --- a/homeassistant/components/limitlessled/light.py +++ b/homeassistant/components/limitlessled/light.py @@ -24,9 +24,7 @@ from homeassistant.components.light import ( EFFECT_WHITE, FLASH_LONG, PLATFORM_SCHEMA, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, + ColorMode, LightEntity, LightEntityFeature, ) @@ -60,27 +58,17 @@ MIN_SATURATION = 10 WHITE = [0, 0] -SUPPORT_LIMITLESSLED_WHITE = ( - SUPPORT_BRIGHTNESS - | SUPPORT_COLOR_TEMP - | LightEntityFeature.EFFECT - | LightEntityFeature.TRANSITION -) -SUPPORT_LIMITLESSLED_DIMMER = SUPPORT_BRIGHTNESS | LightEntityFeature.TRANSITION +COLOR_MODES_LIMITLESS_WHITE = {ColorMode.COLOR_TEMP} +SUPPORT_LIMITLESSLED_WHITE = LightEntityFeature.EFFECT | LightEntityFeature.TRANSITION +COLOR_MODES_LIMITLESS_DIMMER = {ColorMode.BRIGHTNESS} +SUPPORT_LIMITLESSLED_DIMMER = LightEntityFeature.TRANSITION +COLOR_MODES_LIMITLESS_RGB = {ColorMode.HS} SUPPORT_LIMITLESSLED_RGB = ( - SUPPORT_BRIGHTNESS - | LightEntityFeature.EFFECT - | LightEntityFeature.FLASH - | SUPPORT_COLOR - | LightEntityFeature.TRANSITION + LightEntityFeature.EFFECT | LightEntityFeature.FLASH | LightEntityFeature.TRANSITION ) +COLOR_MODES_LIMITLESS_RGBWW = {ColorMode.COLOR_TEMP, ColorMode.HS} SUPPORT_LIMITLESSLED_RGBWW = ( - SUPPORT_BRIGHTNESS - | SUPPORT_COLOR_TEMP - | LightEntityFeature.EFFECT - | LightEntityFeature.FLASH - | SUPPORT_COLOR - | LightEntityFeature.TRANSITION + LightEntityFeature.EFFECT | LightEntityFeature.FLASH | LightEntityFeature.TRANSITION ) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( @@ -219,18 +207,31 @@ class LimitlessLEDGroup(LightEntity, RestoreEntity): """Initialize a group.""" if isinstance(group, WhiteGroup): - self._supported = SUPPORT_LIMITLESSLED_WHITE + self._attr_supported_color_modes = COLOR_MODES_LIMITLESS_WHITE + self._attr_supported_features = SUPPORT_LIMITLESSLED_WHITE self._effect_list = [EFFECT_NIGHT] elif isinstance(group, DimmerGroup): - self._supported = SUPPORT_LIMITLESSLED_DIMMER + self._attr_supported_color_modes = COLOR_MODES_LIMITLESS_DIMMER + self._attr_supported_features = SUPPORT_LIMITLESSLED_DIMMER self._effect_list = [] elif isinstance(group, RgbwGroup): - self._supported = SUPPORT_LIMITLESSLED_RGB + self._attr_supported_color_modes = COLOR_MODES_LIMITLESS_RGB + self._attr_supported_features = SUPPORT_LIMITLESSLED_RGB self._effect_list = [EFFECT_COLORLOOP, EFFECT_NIGHT, EFFECT_WHITE] elif isinstance(group, RgbwwGroup): - self._supported = SUPPORT_LIMITLESSLED_RGBWW + self._attr_supported_color_modes = COLOR_MODES_LIMITLESS_RGBWW + self._attr_supported_features = SUPPORT_LIMITLESSLED_RGBWW self._effect_list = [EFFECT_COLORLOOP, EFFECT_NIGHT, EFFECT_WHITE] + self._fixed_color_mode = None + if len(self._attr_supported_color_modes) == 1: + self._fixed_color_mode = next(iter(self._attr_supported_color_modes)) + else: + assert self._attr_supported_color_modes == { + ColorMode.COLOR_TEMP, + ColorMode.HS, + } + self.group = group self.config = config self._is_on = False @@ -286,29 +287,27 @@ class LimitlessLEDGroup(LightEntity, RestoreEntity): """Return the warmest color_temp that this light supports.""" return 370 + @property + def color_mode(self) -> str | None: + """Return the color mode of the light.""" + if self._fixed_color_mode: + return self._fixed_color_mode + + # The light supports both hs and white with adjustable color temperature + if self._effect == EFFECT_NIGHT or self._color is None or self._color[1] == 0: + return ColorMode.COLOR_TEMP + return ColorMode.HS + @property def color_temp(self): """Return the temperature property.""" - if self.hs_color is not None: - return None return self._temperature @property def hs_color(self): """Return the color property.""" - if self._effect == EFFECT_NIGHT: - return None - - if self._color is None or self._color[1] == 0: - return None - return self._color - @property - def supported_features(self): - """Flag supported features.""" - return self._supported - @property def effect(self): """Return the current effect for this light.""" @@ -349,7 +348,7 @@ class LimitlessLEDGroup(LightEntity, RestoreEntity): self._brightness = kwargs[ATTR_BRIGHTNESS] args["brightness"] = self.limitlessled_brightness() - if ATTR_HS_COLOR in kwargs and self._supported & SUPPORT_COLOR: + if ATTR_HS_COLOR in kwargs: self._color = kwargs[ATTR_HS_COLOR] # White is a special case. if self._color[1] < MIN_SATURATION: @@ -359,18 +358,17 @@ class LimitlessLEDGroup(LightEntity, RestoreEntity): args["color"] = self.limitlessled_color() if ATTR_COLOR_TEMP in kwargs: - if self._supported & SUPPORT_COLOR: + if ColorMode.HS in self.supported_color_modes: pipeline.white() self._color = WHITE - if self._supported & SUPPORT_COLOR_TEMP: - self._temperature = kwargs[ATTR_COLOR_TEMP] - args["temperature"] = self.limitlessled_temperature() + self._temperature = kwargs[ATTR_COLOR_TEMP] + args["temperature"] = self.limitlessled_temperature() if args: pipeline.transition(transition_time, **args) # Flash. - if ATTR_FLASH in kwargs and self._supported & LightEntityFeature.FLASH: + if ATTR_FLASH in kwargs and self.supported_features & LightEntityFeature.FLASH: duration = 0 if kwargs[ATTR_FLASH] == FLASH_LONG: duration = 1 From c8d171c4754efa0c08ae2df8f43f1ac283bc660f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 13 May 2022 12:13:26 +0200 Subject: [PATCH 406/930] Remove deprecated Raspberry Pi GPIO integration (#71777) --- .coveragerc | 1 - .github/workflows/wheels.yml | 1 - homeassistant/components/rpi_gpio/__init__.py | 68 --------- .../components/rpi_gpio/binary_sensor.py | 109 -------------- homeassistant/components/rpi_gpio/cover.py | 139 ------------------ .../components/rpi_gpio/manifest.json | 9 -- .../components/rpi_gpio/services.yaml | 3 - homeassistant/components/rpi_gpio/switch.py | 88 ----------- machine/raspberrypi | 6 +- machine/raspberrypi2 | 6 +- machine/raspberrypi3 | 3 +- machine/raspberrypi3-64 | 3 +- machine/raspberrypi4 | 3 +- machine/raspberrypi4-64 | 3 +- requirements_all.txt | 3 - script/gen_requirements_all.py | 1 - 16 files changed, 6 insertions(+), 440 deletions(-) delete mode 100644 homeassistant/components/rpi_gpio/__init__.py delete mode 100644 homeassistant/components/rpi_gpio/binary_sensor.py delete mode 100644 homeassistant/components/rpi_gpio/cover.py delete mode 100644 homeassistant/components/rpi_gpio/manifest.json delete mode 100644 homeassistant/components/rpi_gpio/services.yaml delete mode 100644 homeassistant/components/rpi_gpio/switch.py diff --git a/.coveragerc b/.coveragerc index 922c13e550c..ef7cd7b847c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -993,7 +993,6 @@ omit = homeassistant/components/route53/* homeassistant/components/rova/sensor.py homeassistant/components/rpi_camera/* - homeassistant/components/rpi_gpio/* homeassistant/components/rtorrent/sensor.py homeassistant/components/russound_rio/media_player.py homeassistant/components/russound_rnet/media_player.py diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index e43c397f3bc..70b6534df5e 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -134,7 +134,6 @@ jobs: sed -i "s|# pybluez|pybluez|g" ${requirement_file} sed -i "s|# bluepy|bluepy|g" ${requirement_file} sed -i "s|# beacontools|beacontools|g" ${requirement_file} - sed -i "s|# RPi.GPIO|RPi.GPIO|g" ${requirement_file} sed -i "s|# fritzconnection|fritzconnection|g" ${requirement_file} sed -i "s|# pyuserinput|pyuserinput|g" ${requirement_file} sed -i "s|# evdev|evdev|g" ${requirement_file} diff --git a/homeassistant/components/rpi_gpio/__init__.py b/homeassistant/components/rpi_gpio/__init__.py deleted file mode 100644 index 95e3ded1c64..00000000000 --- a/homeassistant/components/rpi_gpio/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Support for controlling GPIO pins of a Raspberry Pi.""" -import logging - -from RPi import GPIO # pylint: disable=import-error - -from homeassistant.const import ( - EVENT_HOMEASSISTANT_START, - EVENT_HOMEASSISTANT_STOP, - Platform, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers.typing import ConfigType - -DOMAIN = "rpi_gpio" -PLATFORMS = [ - Platform.BINARY_SENSOR, - Platform.COVER, - Platform.SWITCH, -] - -_LOGGER = logging.getLogger(__name__) - - -def setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Raspberry PI GPIO component.""" - _LOGGER.warning( - "The Raspberry Pi GPIO integration is deprecated and will be removed " - "in Home Assistant Core 2022.6; this integration is removed under " - "Architectural Decision Record 0019, more information can be found here: " - "https://github.com/home-assistant/architecture/blob/master/adr/0019-GPIO.md" - ) - - def cleanup_gpio(event): - """Stuff to do before stopping.""" - GPIO.cleanup() - - def prepare_gpio(event): - """Stuff to do when Home Assistant starts.""" - hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio) - - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio) - GPIO.setmode(GPIO.BCM) - return True - - -def setup_output(port): - """Set up a GPIO as output.""" - GPIO.setup(port, GPIO.OUT) - - -def setup_input(port, pull_mode): - """Set up a GPIO as input.""" - GPIO.setup(port, GPIO.IN, GPIO.PUD_DOWN if pull_mode == "DOWN" else GPIO.PUD_UP) - - -def write_output(port, value): - """Write a value to a GPIO.""" - GPIO.output(port, value) - - -def read_input(port): - """Read a value from a GPIO.""" - return GPIO.input(port) - - -def edge_detect(port, event_callback, bounce): - """Add detection for RISING and FALLING events.""" - GPIO.add_event_detect(port, GPIO.BOTH, callback=event_callback, bouncetime=bounce) diff --git a/homeassistant/components/rpi_gpio/binary_sensor.py b/homeassistant/components/rpi_gpio/binary_sensor.py deleted file mode 100644 index e183b463e45..00000000000 --- a/homeassistant/components/rpi_gpio/binary_sensor.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Support for binary sensor using RPi GPIO.""" -from __future__ import annotations - -import asyncio - -import voluptuous as vol - -from homeassistant.components import rpi_gpio -from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity -from homeassistant.const import DEVICE_DEFAULT_NAME -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import setup_reload_service -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import DOMAIN, PLATFORMS - -CONF_BOUNCETIME = "bouncetime" -CONF_INVERT_LOGIC = "invert_logic" -CONF_PORTS = "ports" -CONF_PULL_MODE = "pull_mode" - -DEFAULT_BOUNCETIME = 50 -DEFAULT_INVERT_LOGIC = False -DEFAULT_PULL_MODE = "UP" - -_SENSORS_SCHEMA = vol.Schema({cv.positive_int: cv.string}) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_PORTS): _SENSORS_SCHEMA, - vol.Optional(CONF_BOUNCETIME, default=DEFAULT_BOUNCETIME): cv.positive_int, - vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, - vol.Optional(CONF_PULL_MODE, default=DEFAULT_PULL_MODE): cv.string, - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Raspberry PI GPIO devices.""" - setup_reload_service(hass, DOMAIN, PLATFORMS) - - pull_mode = config[CONF_PULL_MODE] - bouncetime = config[CONF_BOUNCETIME] - invert_logic = config[CONF_INVERT_LOGIC] - - binary_sensors = [] - ports = config[CONF_PORTS] - for port_num, port_name in ports.items(): - binary_sensors.append( - RPiGPIOBinarySensor( - port_name, port_num, pull_mode, bouncetime, invert_logic - ) - ) - add_entities(binary_sensors, True) - - -class RPiGPIOBinarySensor(BinarySensorEntity): - """Represent a binary sensor that uses Raspberry Pi GPIO.""" - - async def async_read_gpio(self): - """Read state from GPIO.""" - await asyncio.sleep(float(self._bouncetime) / 1000) - self._state = await self.hass.async_add_executor_job( - rpi_gpio.read_input, self._port - ) - self.async_write_ha_state() - - def __init__(self, name, port, pull_mode, bouncetime, invert_logic): - """Initialize the RPi binary sensor.""" - self._name = name or DEVICE_DEFAULT_NAME - self._port = port - self._pull_mode = pull_mode - self._bouncetime = bouncetime - self._invert_logic = invert_logic - self._state = None - - rpi_gpio.setup_input(self._port, self._pull_mode) - - def edge_detected(port): - """Edge detection handler.""" - self.hass.add_job(self.async_read_gpio) - - rpi_gpio.edge_detect(self._port, edge_detected, self._bouncetime) - - @property - def should_poll(self): - """No polling needed.""" - return False - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def is_on(self): - """Return the state of the entity.""" - return self._state != self._invert_logic - - def update(self): - """Update the GPIO state.""" - self._state = rpi_gpio.read_input(self._port) diff --git a/homeassistant/components/rpi_gpio/cover.py b/homeassistant/components/rpi_gpio/cover.py deleted file mode 100644 index e4b07d3c577..00000000000 --- a/homeassistant/components/rpi_gpio/cover.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Support for controlling a Raspberry Pi cover.""" -from __future__ import annotations - -from time import sleep - -import voluptuous as vol - -from homeassistant.components import rpi_gpio -from homeassistant.components.cover import PLATFORM_SCHEMA, CoverEntity -from homeassistant.const import CONF_COVERS, CONF_NAME -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import setup_reload_service -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import DOMAIN, PLATFORMS - -CONF_RELAY_PIN = "relay_pin" -CONF_RELAY_TIME = "relay_time" -CONF_STATE_PIN = "state_pin" -CONF_STATE_PULL_MODE = "state_pull_mode" -CONF_INVERT_STATE = "invert_state" -CONF_INVERT_RELAY = "invert_relay" - -DEFAULT_RELAY_TIME = 0.2 -DEFAULT_STATE_PULL_MODE = "UP" -DEFAULT_INVERT_STATE = False -DEFAULT_INVERT_RELAY = False -_COVERS_SCHEMA = vol.All( - cv.ensure_list, - [ - vol.Schema( - { - CONF_NAME: cv.string, - CONF_RELAY_PIN: cv.positive_int, - CONF_STATE_PIN: cv.positive_int, - } - ) - ], -) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_COVERS): _COVERS_SCHEMA, - vol.Optional(CONF_STATE_PULL_MODE, default=DEFAULT_STATE_PULL_MODE): cv.string, - vol.Optional(CONF_RELAY_TIME, default=DEFAULT_RELAY_TIME): cv.positive_int, - vol.Optional(CONF_INVERT_STATE, default=DEFAULT_INVERT_STATE): cv.boolean, - vol.Optional(CONF_INVERT_RELAY, default=DEFAULT_INVERT_RELAY): cv.boolean, - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the RPi cover platform.""" - setup_reload_service(hass, DOMAIN, PLATFORMS) - - relay_time = config[CONF_RELAY_TIME] - state_pull_mode = config[CONF_STATE_PULL_MODE] - invert_state = config[CONF_INVERT_STATE] - invert_relay = config[CONF_INVERT_RELAY] - covers = [] - covers_conf = config[CONF_COVERS] - - for cover in covers_conf: - covers.append( - RPiGPIOCover( - cover[CONF_NAME], - cover[CONF_RELAY_PIN], - cover[CONF_STATE_PIN], - state_pull_mode, - relay_time, - invert_state, - invert_relay, - ) - ) - add_entities(covers) - - -class RPiGPIOCover(CoverEntity): - """Representation of a Raspberry GPIO cover.""" - - def __init__( - self, - name, - relay_pin, - state_pin, - state_pull_mode, - relay_time, - invert_state, - invert_relay, - ): - """Initialize the cover.""" - self._name = name - self._state = False - self._relay_pin = relay_pin - self._state_pin = state_pin - self._state_pull_mode = state_pull_mode - self._relay_time = relay_time - self._invert_state = invert_state - self._invert_relay = invert_relay - rpi_gpio.setup_output(self._relay_pin) - rpi_gpio.setup_input(self._state_pin, self._state_pull_mode) - rpi_gpio.write_output(self._relay_pin, 0 if self._invert_relay else 1) - - @property - def name(self): - """Return the name of the cover if any.""" - return self._name - - def update(self): - """Update the state of the cover.""" - self._state = rpi_gpio.read_input(self._state_pin) - - @property - def is_closed(self): - """Return true if cover is closed.""" - return self._state != self._invert_state - - def _trigger(self): - """Trigger the cover.""" - rpi_gpio.write_output(self._relay_pin, 1 if self._invert_relay else 0) - sleep(self._relay_time) - rpi_gpio.write_output(self._relay_pin, 0 if self._invert_relay else 1) - - def close_cover(self, **kwargs): - """Close the cover.""" - if not self.is_closed: - self._trigger() - - def open_cover(self, **kwargs): - """Open the cover.""" - if self.is_closed: - self._trigger() diff --git a/homeassistant/components/rpi_gpio/manifest.json b/homeassistant/components/rpi_gpio/manifest.json deleted file mode 100644 index f8db41b1a31..00000000000 --- a/homeassistant/components/rpi_gpio/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "rpi_gpio", - "name": "Raspberry Pi GPIO", - "documentation": "https://www.home-assistant.io/integrations/rpi_gpio", - "requirements": ["RPi.GPIO==0.7.1a4"], - "codeowners": [], - "iot_class": "local_push", - "loggers": ["RPi"] -} diff --git a/homeassistant/components/rpi_gpio/services.yaml b/homeassistant/components/rpi_gpio/services.yaml deleted file mode 100644 index 1858c5a9fa2..00000000000 --- a/homeassistant/components/rpi_gpio/services.yaml +++ /dev/null @@ -1,3 +0,0 @@ -reload: - name: Reload - description: Reload all rpi_gpio entities. diff --git a/homeassistant/components/rpi_gpio/switch.py b/homeassistant/components/rpi_gpio/switch.py deleted file mode 100644 index 040edd912c5..00000000000 --- a/homeassistant/components/rpi_gpio/switch.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Allows to configure a switch using RPi GPIO.""" -from __future__ import annotations - -import voluptuous as vol - -from homeassistant.components import rpi_gpio -from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity -from homeassistant.const import DEVICE_DEFAULT_NAME -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.reload import setup_reload_service -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType - -from . import DOMAIN, PLATFORMS - -CONF_PULL_MODE = "pull_mode" -CONF_PORTS = "ports" -CONF_INVERT_LOGIC = "invert_logic" - -DEFAULT_INVERT_LOGIC = False - -_SWITCHES_SCHEMA = vol.Schema({cv.positive_int: cv.string}) - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_PORTS): _SWITCHES_SCHEMA, - vol.Optional(CONF_INVERT_LOGIC, default=DEFAULT_INVERT_LOGIC): cv.boolean, - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Raspberry PI GPIO devices.""" - setup_reload_service(hass, DOMAIN, PLATFORMS) - - invert_logic = config[CONF_INVERT_LOGIC] - - switches = [] - ports = config[CONF_PORTS] - for port, name in ports.items(): - switches.append(RPiGPIOSwitch(name, port, invert_logic)) - add_entities(switches) - - -class RPiGPIOSwitch(SwitchEntity): - """Representation of a Raspberry Pi GPIO.""" - - def __init__(self, name, port, invert_logic): - """Initialize the pin.""" - self._name = name or DEVICE_DEFAULT_NAME - self._port = port - self._invert_logic = invert_logic - self._state = False - rpi_gpio.setup_output(self._port) - rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0) - - @property - def name(self): - """Return the name of the switch.""" - return self._name - - @property - def should_poll(self): - """No polling needed.""" - return False - - @property - def is_on(self): - """Return true if device is on.""" - return self._state - - def turn_on(self, **kwargs): - """Turn the device on.""" - rpi_gpio.write_output(self._port, 0 if self._invert_logic else 1) - self._state = True - self.schedule_update_ha_state() - - def turn_off(self, **kwargs): - """Turn the device off.""" - rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0) - self._state = False - self.schedule_update_ha_state() diff --git a/machine/raspberrypi b/machine/raspberrypi index 960e343792d..8ea2d08bba7 100644 --- a/machine/raspberrypi +++ b/machine/raspberrypi @@ -4,11 +4,7 @@ FROM homeassistant/armhf-homeassistant:$BUILD_VERSION RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ - usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ - && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO -c /usr/src/homeassistant/requirements_all.txt \ - --use-deprecated=legacy-resolver + usbutils ## # Set symlinks for raspberry pi camera binaries. diff --git a/machine/raspberrypi2 b/machine/raspberrypi2 index 225c45423a1..45f7a9c80d1 100644 --- a/machine/raspberrypi2 +++ b/machine/raspberrypi2 @@ -4,11 +4,7 @@ FROM homeassistant/armv7-homeassistant:$BUILD_VERSION RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ - usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ - && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO -c /usr/src/homeassistant/requirements_all.txt \ - --use-deprecated=legacy-resolver + usbutils ## # Set symlinks for raspberry pi binaries. diff --git a/machine/raspberrypi3 b/machine/raspberrypi3 index 6315cc3e885..9985b4a3b7a 100644 --- a/machine/raspberrypi3 +++ b/machine/raspberrypi3 @@ -5,9 +5,8 @@ RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ + bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ --use-deprecated=legacy-resolver ## diff --git a/machine/raspberrypi3-64 b/machine/raspberrypi3-64 index 51f41d68320..35c6eec77de 100644 --- a/machine/raspberrypi3-64 +++ b/machine/raspberrypi3-64 @@ -5,9 +5,8 @@ RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ + bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ --use-deprecated=legacy-resolver ## diff --git a/machine/raspberrypi4 b/machine/raspberrypi4 index 6315cc3e885..9985b4a3b7a 100644 --- a/machine/raspberrypi4 +++ b/machine/raspberrypi4 @@ -5,9 +5,8 @@ RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ + bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ --use-deprecated=legacy-resolver ## diff --git a/machine/raspberrypi4-64 b/machine/raspberrypi4-64 index 51f41d68320..35c6eec77de 100644 --- a/machine/raspberrypi4-64 +++ b/machine/raspberrypi4-64 @@ -5,9 +5,8 @@ RUN apk --no-cache add \ raspberrypi \ raspberrypi-libs \ usbutils \ - && sed -i "s|# RPi.GPIO|RPi.GPIO|g" /usr/src/homeassistant/requirements_all.txt \ && pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \ - RPi.GPIO bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ + bluepy pybluez -c /usr/src/homeassistant/requirements_all.txt \ --use-deprecated=legacy-resolver ## diff --git a/requirements_all.txt b/requirements_all.txt index e0c864fea9d..b572dca4f6f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -49,9 +49,6 @@ PyViCare==2.16.1 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.13.4 -# homeassistant.components.rpi_gpio -# RPi.GPIO==0.7.1a4 - # homeassistant.components.remember_the_milk RtmAPI==0.7.2 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 1e93b8bba35..be94fdac22b 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -32,7 +32,6 @@ COMMENT_REQUIREMENTS = ( "python-gammu", "python-lirc", "pyuserinput", - "RPi.GPIO", "tensorflow", "tf-models-official", ) From f301de98e47bc45548f41c0a01d172d5d2a34f64 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Fri, 13 May 2022 12:15:49 +0200 Subject: [PATCH 407/930] Add deprecation warning to Somfy integration (#71653) --- homeassistant/components/somfy/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/homeassistant/components/somfy/__init__.py b/homeassistant/components/somfy/__init__.py index ae12c4ea266..ed6c58bc0a0 100644 --- a/homeassistant/components/somfy/__init__.py +++ b/homeassistant/components/somfy/__init__.py @@ -77,6 +77,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Somfy from a config entry.""" + + _LOGGER.warning( + "The Somfy integration is deprecated and will be removed " + "in Home Assistant Core 2022.7; due to the Somfy Open API deprecation." + "The Somfy Open API will shutdown June 21st 2022, migrate to the " + "Overkiz integration to control your Somfy devices" + ) + # Backwards compat if "auth_implementation" not in entry.data: hass.config_entries.async_update_entry( From dba2f5ab1c5cd67b98dbfce27939115018f503c3 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 12:17:40 +0200 Subject: [PATCH 408/930] Support this variable in template alarm actions (#71744) --- .../template/alarm_control_panel.py | 4 +++- .../components/template/template_entity.py | 21 ++++++++++++++++++- .../template/test_alarm_control_panel.py | 9 ++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index 9d81dce28fe..a8a88c57bd3 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -230,7 +230,9 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity): self._state = state optimistic_set = True - await script.async_run({ATTR_CODE: code}, context=self._context) + await self.async_run_script( + script, run_variables={ATTR_CODE: code}, context=self._context + ) if optimistic_set: self.async_write_ha_state() diff --git a/homeassistant/components/template/template_entity.py b/homeassistant/components/template/template_entity.py index a6d1cba78e1..6e0b7f6f48f 100644 --- a/homeassistant/components/template/template_entity.py +++ b/homeassistant/components/template/template_entity.py @@ -19,7 +19,7 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, ) -from homeassistant.core import CoreState, Event, State, callback +from homeassistant.core import Context, CoreState, Event, State, callback from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity @@ -28,6 +28,7 @@ from homeassistant.helpers.event import ( TrackTemplateResult, async_track_template_result, ) +from homeassistant.helpers.script import Script, _VarsType from homeassistant.helpers.template import ( Template, TemplateStateFromEntityId, @@ -455,3 +456,21 @@ class TemplateEntity(Entity): async def async_update(self) -> None: """Call for forced update.""" self._async_update() + + async def async_run_script( + self, + script: Script, + *, + run_variables: _VarsType | None = None, + context: Context | None = None, + ) -> None: + """Run an action script.""" + if run_variables is None: + run_variables = {} + return await script.async_run( + run_variables={ + "this": TemplateStateFromEntityId(self.hass, self.entity_id), + **run_variables, + }, + context=context, + ) diff --git a/tests/components/template/test_alarm_control_panel.py b/tests/components/template/test_alarm_control_panel.py index 9ec93073911..8f9ab39f7c0 100644 --- a/tests/components/template/test_alarm_control_panel.py +++ b/tests/components/template/test_alarm_control_panel.py @@ -44,22 +44,22 @@ OPTIMISTIC_TEMPLATE_ALARM_CONFIG = { "arm_away": { "service": "alarm_control_panel.alarm_arm_away", "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, + "data": {"code": "{{ this.entity_id }}"}, }, "arm_home": { "service": "alarm_control_panel.alarm_arm_home", "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, + "data": {"code": "{{ this.entity_id }}"}, }, "arm_night": { "service": "alarm_control_panel.alarm_arm_night", "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, + "data": {"code": "{{ this.entity_id }}"}, }, "disarm": { "service": "alarm_control_panel.alarm_disarm", "entity_id": "alarm_control_panel.test", - "data": {"code": "1234"}, + "data": {"code": "{{ this.entity_id }}"}, }, } @@ -261,6 +261,7 @@ async def test_actions(hass, service, start_ha, service_calls): await hass.async_block_till_done() assert len(service_calls) == 1 assert service_calls[0].data["service"] == service + assert service_calls[0].data["service_data"]["code"] == TEMPLATE_NAME @pytest.mark.parametrize("count,domain", [(1, "alarm_control_panel")]) From 6cff2f8571005ac795540839a69ba6ef1446f863 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 12:22:34 +0200 Subject: [PATCH 409/930] Tweak template light tests (#71729) --- tests/components/template/conftest.py | 14 +- tests/components/template/test_light.py | 1491 +++++++++-------------- 2 files changed, 592 insertions(+), 913 deletions(-) diff --git a/tests/components/template/conftest.py b/tests/components/template/conftest.py index 410ce21e4c6..5ccc9e6479a 100644 --- a/tests/components/template/conftest.py +++ b/tests/components/template/conftest.py @@ -1,6 +1,4 @@ """template conftest.""" -import json - import pytest from homeassistant.setup import async_setup_component @@ -15,18 +13,8 @@ def calls(hass): @pytest.fixture -def config_addon(): - """Add entra configuration items.""" - return None - - -@pytest.fixture -async def start_ha(hass, count, domain, config_addon, config, caplog): +async def start_ha(hass, count, domain, config, caplog): """Do setup of integration.""" - if config_addon: - for key, value in config_addon.items(): - config = config.replace(key, value) - config = json.loads(config) with assert_setup_component(count, domain): assert await async_setup_component( hass, diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index 924a5c25538..394eb1cb9ba 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -26,48 +26,90 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.setup import async_setup_component + +from tests.common import assert_setup_component # Represent for light's availability _STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state" -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +OPTIMISTIC_ON_OFF_LIGHT_CONFIG = { + "turn_on": { + "service": "light.turn_on", + "entity_id": "light.test_state", + }, + "turn_off": { + "service": "light.turn_off", + "entity_id": "light.test_state", + }, +} + + +OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG = { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + }, + }, +} + + +OPTIMISTIC_WHITE_VALUE_LIGHT_CONFIG = { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "set_white_value": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "white_value": "{{white_value}}", + }, + }, +} + + +async def async_setup_light(hass, count, light_config): + """Do setup of light integration.""" + config = {"light": {"platform": "template", "lights": light_config}} + + with assert_setup_component(count, light.DOMAIN): + assert await async_setup_component( + hass, + light.DOMAIN, + config, + ) + + await hass.async_block_till_done() + await hass.async_start() + await hass.async_block_till_done() + + +@pytest.fixture +async def setup_light(hass, count, light_config): + """Do setup of light integration.""" + await async_setup_light(hass, count, light_config) + + +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{states.test['big.fat...']}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{{states.test['big.fat...']}}", } }, ], ) async def test_template_state_invalid( - hass, supported_features, supported_color_modes, start_ha + hass, supported_features, supported_color_modes, setup_light ): """Test template state with render error.""" state = hass.states.get("light.test_template_light") @@ -77,43 +119,24 @@ async def test_template_state_invalid( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{ states.light.test_state.state }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{{ states.light.test_state.state }}", } }, ], ) async def test_template_state_text( - hass, supported_features, supported_color_modes, start_ha + hass, supported_features, supported_color_modes, setup_light ): """Test the state text of a template.""" set_state = STATE_ON @@ -135,46 +158,24 @@ async def test_template_state_text( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config_addon,expected_state,expected_color_mode", + "value_template,expected_state,expected_color_mode", [ - ({"replace1": '"{{ 1 == 1 }}"'}, STATE_ON, ColorMode.UNKNOWN), - ({"replace1": '"{{ 1 == 2 }}"'}, STATE_OFF, None), - ], -) -@pytest.mark.parametrize( - "config", - [ - """{ - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": replace1, - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state" - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state" - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}" - } - } - } - } - } - }""", + ( + "{{ 1 == 1 }}", + STATE_ON, + ColorMode.UNKNOWN, + ), + ( + "{{ 1 == 2 }}", + STATE_OFF, + None, + ), ], ) async def test_templatex_state_boolean( @@ -183,9 +184,17 @@ async def test_templatex_state_boolean( expected_state, supported_features, supported_color_modes, - start_ha, + count, + value_template, ): """Test the setting of the state with boolean on.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": value_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state.state == expected_state assert state.attributes.get("color_mode") == expected_color_mode @@ -193,99 +202,55 @@ async def test_templatex_state_boolean( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(0, light.DOMAIN)]) +@pytest.mark.parametrize("count", [0]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{%- if false -%}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{%- if false -%}", } }, { - "light": { - "platform": "template", - "lights": { - "bad name here": { - "value_template": "{{ 1== 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, - } - }, - { - "light": { - "platform": "template", - "switches": {"test_template_light": "Invalid"}, + "bad name here": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{{ 1== 1}}", } }, + {"test_template_light": "Invalid"}, ], ) -async def test_template_syntax_error(hass, start_ha): +async def test_template_syntax_error(hass, setup_light): """Test templating syntax error.""" assert hass.states.async_all("light") == [] -SET_VAL1 = '"value_template": "{{ 1== 1}}",' -SET_VAL2 = '"turn_on": {"service": "light.turn_on","entity_id": "light.test_state"},' -SET_VAL3 = '"turn_off": {"service": "light.turn_off","entity_id": "light.test_state"},' - - -@pytest.mark.parametrize("domain", [light.DOMAIN]) @pytest.mark.parametrize( - "config_addon, count", + "light_config, count", [ - ({"replace2": f"{SET_VAL2}{SET_VAL3}"}, 1), - ({"replace2": f"{SET_VAL1}{SET_VAL2}"}, 0), - ({"replace2": f"{SET_VAL2}{SET_VAL3}"}, 1), + ( + { + "light_one": { + "value_template": "{{ 1== 1}}", + "turn_on": { + "service": "light.turn_on", + "entity_id": "light.test_state", + }, + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + }, + }, + } + }, + 0, + ), ], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template", "lights": { - "light_one": { - replace2 - "set_level": {"service": "light.turn_on", - "data_template": {"entity_id": "light.test_state","brightness": "{{brightness}}" - }}}}}}""" - ], -) -async def test_missing_key(hass, count, start_ha): +async def test_missing_key(hass, count, setup_light): """Test missing template.""" if count: assert hass.states.async_all("light") != [] @@ -293,40 +258,25 @@ async def test_missing_key(hass, count, start_ha): assert hass.states.async_all("light") == [] -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{states.light.test_state.state}}", - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{{states.light.test_state.state}}", + "turn_on": {"service": "test.automation"}, } }, ], ) async def test_on_action( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test on action.""" hass.states.async_set("light.test_state", STATE_OFF) @@ -353,47 +303,42 @@ async def test_on_action( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{states.light.test_state.state}}", - "turn_on": { - "service": "test.automation", - "data_template": { - "transition": "{{transition}}", - }, - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "supports_transition_template": "{{true}}", - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - "transition": "{{transition}}", - }, - }, - } + "test_template_light": { + "value_template": "{{states.light.test_state.state}}", + "turn_on": { + "service": "test.automation", + "data_template": { + "transition": "{{transition}}", + }, + }, + "turn_off": { + "service": "light.turn_off", + "entity_id": "light.test_state", + }, + "supports_transition_template": "{{true}}", + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + "transition": "{{transition}}", + }, }, } }, ], ) async def test_on_action_with_transition( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test on action with transition.""" hass.states.async_set("light.test_state", STATE_OFF) @@ -421,40 +366,25 @@ async def test_on_action_with_transition( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes,expected_color_mode", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS], ColorMode.BRIGHTNESS)], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "turn_on": {"service": "test.automation"}, } }, ], ) async def test_on_action_optimistic( hass, - start_ha, + setup_light, calls, supported_features, supported_color_modes, @@ -499,42 +429,25 @@ async def test_on_action_optimistic( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{states.light.test_state.state}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "test.automation", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "value_template": "{{states.light.test_state.state}}", + "turn_off": {"service": "test.automation"}, } }, ], ) async def test_off_action( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test off action.""" hass.states.async_set("light.test_state", STATE_ON) @@ -560,47 +473,42 @@ async def test_off_action( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [(1)]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{states.light.test_state.state}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "test.automation", - "data_template": { - "transition": "{{transition}}", - }, - }, - "supports_transition_template": "{{true}}", - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - "transition": "{{transition}}", - }, - }, - } + "test_template_light": { + "value_template": "{{states.light.test_state.state}}", + "turn_on": { + "service": "light.turn_on", + "entity_id": "light.test_state", + }, + "turn_off": { + "service": "test.automation", + "data_template": { + "transition": "{{transition}}", + }, + }, + "supports_transition_template": "{{true}}", + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + "transition": "{{transition}}", + }, }, } }, ], ) async def test_off_action_with_transition( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test off action with transition.""" hass.states.async_set("light.test_state", STATE_ON) @@ -627,39 +535,24 @@ async def test_off_action_with_transition( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": {"service": "test.automation"}, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "turn_off": {"service": "test.automation"}, } }, ], ) async def test_off_action_optimistic( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test off action with optimistic state.""" state = hass.states.get("light.test_template_light") @@ -683,36 +576,24 @@ async def test_off_action_optimistic( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes,expected_color_mode", [(SUPPORT_WHITE_VALUE, [ColorMode.RGBW], ColorMode.UNKNOWN)], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{1 == 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_white_value": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "white_value": "{{white_value}}", - }, - }, - } + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{1 == 1}}", + "set_white_value": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "white_value": "{{white_value}}", + }, }, } }, @@ -720,7 +601,7 @@ async def test_off_action_optimistic( ) async def test_white_value_action_no_template( hass, - start_ha, + setup_light, calls, supported_color_modes, supported_features, @@ -748,46 +629,40 @@ async def test_white_value_action_no_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize( - "expected_white_value,config_addon", - [ - (255, {"replace3": "{{255}}"}), - (None, {"replace3": "{{256}}"}), - (None, {"replace3": "{{x - 12}}"}), - (None, {"replace3": "{{ none }}"}), - (None, {"replace3": ""}), - ], -) -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes,expected_color_mode", [(SUPPORT_WHITE_VALUE, [ColorMode.RGBW], ColorMode.UNKNOWN)], ) @pytest.mark.parametrize( - "config", + "expected_white_value,white_value_template", [ - """{ - "light": {"platform": "template","lights": { - "test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_white_value": {"service": "light.turn_on", - "data_template": {"entity_id": "light.test_state", - "white_value": "{{white_value}}"}}, - "white_value_template": "replace3" - }}}}""", + (255, "{{255}}"), + (None, "{{256}}"), + (None, "{{x-12}}"), + (None, "{{ none }}"), + (None, ""), ], ) async def test_white_value_template( hass, expected_white_value, - start_ha, supported_features, supported_color_modes, expected_color_mode, + count, + white_value_template, ): """Test the template for the white value.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_WHITE_VALUE_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "white_value_template": white_value_template, + } + } + await async_setup_light(hass, count, light_config) + state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("white_value") == expected_white_value @@ -797,36 +672,24 @@ async def test_white_value_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes,expected_color_mode", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS], ColorMode.BRIGHTNESS)], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{1 == 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "brightness": "{{brightness}}", - }, - }, - } + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{1 == 1}}", + "set_level": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "brightness": "{{brightness}}", + }, }, } }, @@ -834,7 +697,7 @@ async def test_white_value_template( ) async def test_level_action_no_template( hass, - start_ha, + setup_light, calls, supported_features, supported_color_modes, @@ -862,18 +725,18 @@ async def test_level_action_no_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_level,config_addon,expected_color_mode", + "expected_level,level_template,expected_color_mode", [ - (255, {"replace4": '"{{255}}"'}, ColorMode.BRIGHTNESS), - (None, {"replace4": '"{{256}}"'}, ColorMode.UNKNOWN), - (None, {"replace4": '"{{x - 12}}"'}, ColorMode.UNKNOWN), - (None, {"replace4": '"{{ none }}"'}, ColorMode.UNKNOWN), - (None, {"replace4": '""'}, ColorMode.UNKNOWN), + (255, "{{255}}", ColorMode.BRIGHTNESS), + (None, "{{256}}", ColorMode.UNKNOWN), + (None, "{{x - 12}}", ColorMode.UNKNOWN), + (None, "{{ none }}", ColorMode.UNKNOWN), + (None, "", ColorMode.UNKNOWN), ( None, - {"replace4": "\"{{ state_attr('light.nolight', 'brightness') }}\""}, + "{{ state_attr('light.nolight', 'brightness') }}", ColorMode.UNKNOWN, ), ], @@ -882,29 +745,31 @@ async def test_level_action_no_template( "supported_features,supported_color_modes", [(SUPPORT_BRIGHTNESS, [ColorMode.BRIGHTNESS])], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template", "lights": { - "test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_level": {"service": "light.turn_on","data_template": { - "entity_id": "light.test_state","brightness": "{{brightness}}"}}, - "level_template": replace4 - }}}}""", - ], -) async def test_level_template( hass, expected_level, - start_ha, supported_features, supported_color_modes, expected_color_mode, + count, + level_template, ): """Test the template for the level.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + }, + }, + "level_template": level_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state.attributes.get("brightness") == expected_level assert state.state == STATE_ON @@ -913,62 +778,47 @@ async def test_level_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_temp,config_addon,expected_color_mode", + "expected_temp,temperature_template,expected_color_mode", [ - (500, {"replace5": '"{{500}}"'}, ColorMode.COLOR_TEMP), - (None, {"replace5": '"{{501}}"'}, ColorMode.UNKNOWN), - (None, {"replace5": '"{{x - 12}}"'}, ColorMode.UNKNOWN), - (None, {"replace5": '"None"'}, ColorMode.UNKNOWN), - (None, {"replace5": '"{{ none }}"'}, ColorMode.UNKNOWN), - (None, {"replace5": '""'}, ColorMode.UNKNOWN), + (500, "{{500}}", ColorMode.COLOR_TEMP), + (None, "{{501}}", ColorMode.UNKNOWN), + (None, "{{x - 12}}", ColorMode.UNKNOWN), + (None, "None", ColorMode.UNKNOWN), + (None, "{{ none }}", ColorMode.UNKNOWN), + (None, "", ColorMode.UNKNOWN), ], ) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP])], ) -@pytest.mark.parametrize( - "config", - [ - """{ - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state" - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state" - }, - "set_temperature": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "color_temp": "{{color_temp}}" - } - }, - "temperature_template": replace5 - } - } - } - }""" - ], -) async def test_temperature_template( hass, expected_temp, - start_ha, supported_features, supported_color_modes, expected_color_mode, + count, + temperature_template, ): """Test the template for the temperature.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_temperature": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "color_temp": "{{color_temp}}", + }, + }, + "temperature_template": temperature_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state.attributes.get("color_temp") == expected_temp assert state.state == STATE_ON @@ -977,36 +827,24 @@ async def test_temperature_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes,expected_color_mode", [(SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP], ColorMode.COLOR_TEMP)], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{1 == 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_temperature": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "color_temp": "{{color_temp}}", - }, - }, - } + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{1 == 1}}", + "set_temperature": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "color_temp": "{{color_temp}}", + }, }, } }, @@ -1014,7 +852,7 @@ async def test_temperature_template( ) async def test_temperature_action_no_template( hass, - start_ha, + setup_light, calls, supported_features, supported_color_modes, @@ -1043,39 +881,20 @@ async def test_temperature_action_no_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "friendly_name": "Template light", - "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "friendly_name": "Template light", + "value_template": "{{ 1 == 1 }}", } }, ], ) -async def test_friendly_name(hass, start_ha): +async def test_friendly_name(hass, setup_light): """Test the accessibility of the friendly_name attribute.""" state = hass.states.get("light.test_template_light") @@ -1084,42 +903,23 @@ async def test_friendly_name(hass, start_ha): assert state.attributes.get("friendly_name") == "Template light" -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "friendly_name": "Template light", - "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - "icon_template": "{% if states.light.test_state.state %}" - "mdi:check" - "{% endif %}", - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "friendly_name": "Template light", + "value_template": "{{ 1 == 1 }}", + "icon_template": "{% if states.light.test_state.state %}" + "mdi:check" + "{% endif %}", } }, ], ) -async def test_icon_template(hass, start_ha): +async def test_icon_template(hass, setup_light): """Test icon template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("icon") == "" @@ -1132,42 +932,23 @@ async def test_icon_template(hass, start_ha): assert state.attributes["icon"] == "mdi:check" -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "friendly_name": "Template light", - "value_template": "{{ 1 == 1 }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - "entity_picture_template": "{% if states.light.test_state.state %}" - "/local/light.png" - "{% endif %}", - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "friendly_name": "Template light", + "value_template": "{{ 1 == 1 }}", + "entity_picture_template": "{% if states.light.test_state.state %}" + "/local/light.png" + "{% endif %}", } }, ], ) -async def test_entity_picture_template(hass, start_ha): +async def test_entity_picture_template(hass, setup_light): """Test entity_picture template.""" state = hass.states.get("light.test_template_light") assert state.attributes.get("entity_picture") == "" @@ -1180,55 +961,35 @@ async def test_entity_picture_template(hass, start_ha): assert state.attributes["entity_picture"] == "/local/light.png" -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes, expected_color_mode", [(SUPPORT_COLOR, [ColorMode.HS], ColorMode.HS)], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{1 == 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{1 == 1}}", + "set_color": [ + { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "s": "{{s}}", + "h": "{{h}}", }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_color": [ - { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "h": "{{h}}", - "s": "{{s}}", - }, - }, - { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "s": "{{s}}", - "h": "{{h}}", - }, - }, - ], - } - }, + }, + ], } }, ], ) async def test_color_action_no_template( hass, - start_ha, + setup_light, calls, supported_features, supported_color_modes, @@ -1245,11 +1006,9 @@ async def test_color_action_no_template( blocking=True, ) - assert len(calls) == 2 + assert len(calls) == 1 assert calls[0].data["h"] == 40 assert calls[0].data["s"] == 50 - assert calls[1].data["h"] == 40 - assert calls[1].data["s"] == 50 state = hass.states.get("light.test_template_light") assert state.state == STATE_ON @@ -1259,47 +1018,51 @@ async def test_color_action_no_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_hs,config_addon,expected_color_mode", + "expected_hs,color_template,expected_color_mode", [ - ((360, 100), {"replace6": '"{{(360, 100)}}"'}, ColorMode.HS), - ((359.9, 99.9), {"replace6": '"{{(359.9, 99.9)}}"'}, ColorMode.HS), - (None, {"replace6": '"{{(361, 100)}}"'}, ColorMode.UNKNOWN), - (None, {"replace6": '"{{(360, 101)}}"'}, ColorMode.UNKNOWN), - (None, {"replace6": '"[{{(360)}},{{null}}]"'}, ColorMode.UNKNOWN), - (None, {"replace6": '"{{x - 12}}"'}, ColorMode.UNKNOWN), - (None, {"replace6": '""'}, ColorMode.UNKNOWN), - (None, {"replace6": '"{{ none }}"'}, ColorMode.UNKNOWN), + ((360, 100), "{{(360, 100)}}", ColorMode.HS), + ((359.9, 99.9), "{{(359.9, 99.9)}}", ColorMode.HS), + (None, "{{(361, 100)}}", ColorMode.UNKNOWN), + (None, "{{(360, 101)}}", ColorMode.UNKNOWN), + (None, "[{{(360)}},{{null}}]", ColorMode.UNKNOWN), + (None, "{{x - 12}}", ColorMode.UNKNOWN), + (None, "", ColorMode.UNKNOWN), + (None, "{{ none }}", ColorMode.UNKNOWN), ], ) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_COLOR, [ColorMode.HS])], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_color": [{"service": "input_number.set_value", - "data_template": {"entity_id": "input_number.h","color_temp": "{{h}}" - }}], - "color_template": replace6 - }}}}""" - ], -) async def test_color_template( hass, expected_hs, - start_ha, supported_features, supported_color_modes, expected_color_mode, + count, + color_template, ): """Test the template for the color.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_color": [ + { + "service": "input_number.set_value", + "data_template": { + "entity_id": "input_number.h", + "color_temp": "{{h}}", + }, + } + ], + "color_template": color_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state.attributes.get("hs_color") == expected_hs assert state.state == STATE_ON @@ -1308,53 +1071,41 @@ async def test_color_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( "supported_features,supported_color_modes", [(SUPPORT_COLOR | SUPPORT_COLOR_TEMP, [ColorMode.COLOR_TEMP, ColorMode.HS])], ) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{1 == 1}}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{1 == 1}}", + "set_color": [ + { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "h": "{{h}}", + "s": "{{s}}", }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_color": [ - { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "h": "{{h}}", - "s": "{{s}}", - }, - }, - ], - "set_temperature": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "color_temp": "{{color_temp}}", - }, - }, - } + }, + ], + "set_temperature": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "color_temp": "{{color_temp}}", + }, }, } }, ], ) async def test_color_and_temperature_actions_no_template( - hass, start_ha, calls, supported_features, supported_color_modes + hass, setup_light, calls, supported_features, supported_color_modes ): """Test setting color and color temperature with optimistic template.""" state = hass.states.get("light.test_template_light") @@ -1435,44 +1186,35 @@ async def test_color_and_temperature_actions_no_template( assert state.attributes["supported_features"] == supported_features -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{true}}", - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - "set_effect": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "effect": "{{effect}}", - }, - }, - "effect_list_template": "{{ ['Disco', 'Police'] }}", - "effect_template": "{{ 'Disco' }}", - } + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{true}}", + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + }, }, + "set_effect": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "effect": "{{effect}}", + }, + }, + "effect_list_template": "{{ ['Disco', 'Police'] }}", + "effect_template": "{{ 'Disco' }}", } }, ], ) -async def test_effect_action_valid_effect(hass, start_ha, calls): +async def test_effect_action_valid_effect(hass, setup_light, calls): """Test setting valid effect with template.""" state = hass.states.get("light.test_template_light") assert state is not None @@ -1492,44 +1234,35 @@ async def test_effect_action_valid_effect(hass, start_ha, calls): assert state.attributes.get("effect") == "Disco" -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "value_template": "{{true}}", - "turn_on": {"service": "test.automation"}, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - "set_effect": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "effect": "{{effect}}", - }, - }, - "effect_list_template": "{{ ['Disco', 'Police'] }}", - "effect_template": "{{ None }}", - } + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{true}}", + "set_level": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "brightness": "{{brightness}}", + }, }, + "set_effect": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "effect": "{{effect}}", + }, + }, + "effect_list_template": "{{ ['Disco', 'Police'] }}", + "effect_template": "{{ None }}", } }, ], ) -async def test_effect_action_invalid_effect(hass, start_ha, calls): +async def test_effect_action_invalid_effect(hass, setup_light, calls): """Test setting invalid effect with template.""" state = hass.states.get("light.test_template_light") assert state is not None @@ -1549,176 +1282,191 @@ async def test_effect_action_invalid_effect(hass, start_ha, calls): assert state.attributes.get("effect") is None -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_effect_list,config_addon", + "expected_effect_list,effect_list_template", [ ( ["Strobe color", "Police", "Christmas", "RGB", "Random Loop"], - { - "replace7": "\"{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}\"" - }, + "{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}", ), ( ["Police", "RGB", "Random Loop"], - {"replace7": "\"{{ ['Police', 'RGB', 'Random Loop'] }}\""}, + "{{ ['Police', 'RGB', 'Random Loop'] }}", ), - (None, {"replace7": '"{{ [] }}"'}), - (None, {"replace7": "\"{{ '[]' }}\""}), - (None, {"replace7": '"{{ 124 }}"'}), - (None, {"replace7": "\"{{ '124' }}\""}), - (None, {"replace7": '"{{ none }}"'}), - (None, {"replace7": '""'}), + (None, "{{ [] }}"), + (None, "{{ '[]' }}"), + (None, "{{ 124 }}"), + (None, "{{ '124' }}"), + (None, "{{ none }}"), + (None, ""), ], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_effect": {"service": "test.automation", - "data_template": {"entity_id": "test.test_state","effect": "{{effect}}"}}, - "effect_template": "{{ None }}", - "effect_list_template": replace7 - }}}}""", - ], -) -async def test_effect_list_template(hass, expected_effect_list, start_ha): +async def test_effect_list_template( + hass, expected_effect_list, count, effect_list_template +): """Test the template for the effect list.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_effect": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "effect": "{{effect}}", + }, + }, + "effect_template": "{{ None }}", + "effect_list_template": effect_list_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("effect_list") == expected_effect_list -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_effect,config_addon", + "expected_effect,effect_template", [ - (None, {"replace8": '"Disco"'}), - (None, {"replace8": '"None"'}), - (None, {"replace8": '"{{ None }}"'}), - ("Police", {"replace8": '"Police"'}), - ("Strobe color", {"replace8": "\"{{ 'Strobe color' }}\""}), + (None, "Disco"), + (None, "None"), + (None, "{{ None }}"), + ("Police", "Police"), + ("Strobe color", "{{ 'Strobe color' }}"), ], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_effect": {"service": "test.automation","data_template": { - "entity_id": "test.test_state","effect": "{{effect}}"}}, - "effect_list_template": "{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}", - "effect_template": replace8 - }}}}""", - ], -) -async def test_effect_template(hass, expected_effect, start_ha): +async def test_effect_template(hass, expected_effect, count, effect_template): """Test the template for the effect.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_effect": { + "service": "test.automation", + "data_template": { + "entity_id": "test.test_state", + "effect": "{{effect}}", + }, + }, + "effect_list_template": "{{ ['Strobe color', 'Police', 'Christmas', 'RGB', 'Random Loop'] }}", + "effect_template": effect_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("effect") == expected_effect -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_min_mireds,config_addon", + "expected_min_mireds,min_mireds_template", [ - (118, {"replace9": '"{{118}}"'}), - (153, {"replace9": '"{{x - 12}}"'}), - (153, {"replace9": '"None"'}), - (153, {"replace9": '"{{ none }}"'}), - (153, {"replace9": '""'}), - (153, {"replace9": "\"{{ 'a' }}\""}), + (118, "{{118}}"), + (153, "{{x - 12}}"), + (153, "None"), + (153, "{{ none }}"), + (153, ""), + (153, "{{ 'a' }}"), ], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_temperature": {"service": "light.turn_on","data_template": { - "entity_id": "light.test_state","color_temp": "{{color_temp}}"}}, - "temperature_template": "{{200}}", - "min_mireds_template": replace9 - }}}}""", - ], -) -async def test_min_mireds_template(hass, expected_min_mireds, start_ha): +async def test_min_mireds_template( + hass, expected_min_mireds, count, min_mireds_template +): """Test the template for the min mireds.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_temperature": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "color_temp": "{{color_temp}}", + }, + }, + "temperature_template": "{{200}}", + "min_mireds_template": min_mireds_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("min_mireds") == expected_min_mireds -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_max_mireds,config_addon", + "expected_max_mireds,max_mireds_template", [ - (488, {"template1": '"{{488}}"'}), - (500, {"template1": '"{{x - 12}}"'}), - (500, {"template1": '"None"'}), - (500, {"template1": '"{{ none }}"'}), - (500, {"template1": '""'}), - (500, {"template1": "\"{{ 'a' }}\""}), + (488, "{{488}}"), + (500, "{{x - 12}}"), + (500, "None"), + (500, "{{ none }}"), + (500, ""), + (500, "{{ 'a' }}"), ], ) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_temperature": {"service": "light.turn_on","data_template": { - "entity_id": "light.test_state","color_temp": "{{color_temp}}"}}, - "temperature_template": "{{200}}", - "max_mireds_template": template1 - }}}}""", - ], -) -async def test_max_mireds_template(hass, expected_max_mireds, start_ha): +async def test_max_mireds_template( + hass, expected_max_mireds, count, max_mireds_template +): """Test the template for the max mireds.""" + light_config = { + "test_template_light": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "value_template": "{{ 1 == 1 }}", + "set_temperature": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "color_temp": "{{color_temp}}", + }, + }, + "temperature_template": "{{200}}", + "max_mireds_template": max_mireds_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") assert state is not None assert state.attributes.get("max_mireds") == expected_max_mireds -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "expected_supports_transition,config_addon", + "expected_supports_transition,supports_transition_template", [ - (True, {"template2": '"{{true}}"'}), - (True, {"template2": '"{{1 == 1}}"'}), - (False, {"template2": '"{{false}}"'}), - (False, {"template2": '"{{ none }}"'}), - (False, {"template2": '""'}), - (False, {"template2": '"None"'}), - ], -) -@pytest.mark.parametrize( - "config", - [ - """{"light": {"platform": "template","lights": {"test_template_light": { - "value_template": "{{ 1 == 1 }}", - "turn_on": {"service": "light.turn_on","entity_id": "light.test_state"}, - "turn_off": {"service": "light.turn_off","entity_id": "light.test_state"}, - "set_temperature": {"service": "light.turn_on","data_template": { - "entity_id": "light.test_state","color_temp": "{{color_temp}}"}}, - "supports_transition_template": template2 - }}}}""", + (True, "{{true}}"), + (True, "{{1 == 1}}"), + (False, "{{false}}"), + (False, "{{ none }}"), + (False, ""), + (False, "None"), ], ) async def test_supports_transition_template( - hass, expected_supports_transition, start_ha + hass, expected_supports_transition, count, supports_transition_template ): """Test the template for the supports transition.""" + light_config = { + "test_template_light": { + "value_template": "{{ 1 == 1 }}", + "turn_on": {"service": "light.turn_on", "entity_id": "light.test_state"}, + "turn_off": {"service": "light.turn_off", "entity_id": "light.test_state"}, + "set_temperature": { + "service": "light.turn_on", + "data_template": { + "entity_id": "light.test_state", + "color_temp": "{{color_temp}}", + }, + }, + "supports_transition_template": supports_transition_template, + } + } + await async_setup_light(hass, count, light_config) state = hass.states.get("light.test_template_light") expected_value = 1 @@ -1732,38 +1480,19 @@ async def test_supports_transition_template( ) != expected_value -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "availability_template": "{{ is_state('availability_boolean.state', 'on') }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "availability_template": "{{ is_state('availability_boolean.state', 'on') }}", } }, ], ) -async def test_available_template_with_entities(hass, start_ha): +async def test_available_template_with_entities(hass, setup_light): """Test availability templates with values from other entities.""" # When template returns true.. hass.states.async_set(_STATE_AVAILABILITY_BOOLEAN, STATE_ON) @@ -1780,80 +1509,42 @@ async def test_available_template_with_entities(hass, start_ha): assert hass.states.get("light.test_template_light").state == STATE_UNAVAILABLE -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light": { - "availability_template": "{{ x - 12 }}", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, - } - }, + "test_template_light": { + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, + "availability_template": "{{ x - 12 }}", } }, ], ) async def test_invalid_availability_template_keeps_component_available( - hass, start_ha, caplog_setup_text + hass, setup_light, caplog_setup_text ): """Test that an invalid availability keeps the device available.""" assert hass.states.get("light.test_template_light").state != STATE_UNAVAILABLE assert ("UndefinedError: 'x' is undefined") in caplog_setup_text -@pytest.mark.parametrize("count,domain", [(1, light.DOMAIN)]) +@pytest.mark.parametrize("count", [1]) @pytest.mark.parametrize( - "config", + "light_config", [ { - "light": { - "platform": "template", - "lights": { - "test_template_light_01": { - "unique_id": "not-so-unique-anymore", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - }, - "test_template_light_02": { - "unique_id": "not-so-unique-anymore", - "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", - }, - "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", - }, - }, - }, - } + "test_template_light_01": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "unique_id": "not-so-unique-anymore", + }, + "test_template_light_02": { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "unique_id": "not-so-unique-anymore", + }, }, ], ) -async def test_unique_id(hass, start_ha): +async def test_unique_id(hass, setup_light): """Test unique_id option only creates one light per id.""" assert len(hass.states.async_all("light")) == 1 From 80d332ddf1ccddea42cbab38a1ee85812a835134 Mon Sep 17 00:00:00 2001 From: Thibaut Date: Fri, 13 May 2022 12:36:08 +0200 Subject: [PATCH 410/930] =?UTF-8?q?Don=E2=80=99t=20send=20None=20value=20w?= =?UTF-8?q?ithin=20Command=20parameter=20value=20in=20Overkiz=20integratio?= =?UTF-8?q?n=20(#71582)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homeassistant/components/overkiz/executor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/overkiz/executor.py b/homeassistant/components/overkiz/executor.py index 127851db36a..9bf7ef43b02 100644 --- a/homeassistant/components/overkiz/executor.py +++ b/homeassistant/components/overkiz/executor.py @@ -68,17 +68,18 @@ class OverkizExecutor: async def async_execute_command(self, command_name: str, *args: Any) -> None: """Execute device command in async context.""" + parameters = [arg for arg in args if arg is not None] # Set the execution duration to 0 seconds for RTS devices on supported commands # Default execution duration is 30 seconds and will block consecutive commands if ( self.device.protocol == Protocol.RTS and command_name not in COMMANDS_WITHOUT_DELAY ): - args = args + (0,) + parameters.append(0) exec_id = await self.coordinator.client.execute_command( self.device.device_url, - Command(command_name, list(args)), + Command(command_name, parameters), "Home Assistant", ) From f487c04e02d3c08bbb507dd28cd845bf80c5f6a0 Mon Sep 17 00:00:00 2001 From: Avi Miller Date: Fri, 13 May 2022 21:30:44 +1000 Subject: [PATCH 411/930] Remove LIFX bulb discovery from the inflight list if it fails to connect (#71673) Remove the bulb discovery from the inflight list if it fails to connect Signed-off-by: Avi Miller --- homeassistant/components/lifx/light.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 188959adc76..31e973874d9 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -416,6 +416,8 @@ class LIFXManager: if color_resp is None or version_resp is None: _LOGGER.error("Failed to connect to %s", bulb.ip_addr) bulb.registered = False + if bulb.mac_addr in self.discoveries_inflight: + self.discoveries_inflight.pop(bulb.mac_addr) else: bulb.timeout = MESSAGE_TIMEOUT bulb.retry_count = MESSAGE_RETRIES From 0a9a86f973c6eff07c1c6c21f5afc95a1b603a0f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 13 May 2022 13:38:20 +0200 Subject: [PATCH 412/930] Update jinja2 to 3.1.2 (#71780) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 129774d989a..9c9354c584b 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -18,7 +18,7 @@ hass-nabucasa==0.54.0 home-assistant-frontend==20220504.1 httpx==0.22.0 ifaddr==0.1.7 -jinja2==3.1.1 +jinja2==3.1.2 lru-dict==1.1.7 paho-mqtt==1.6.1 pillow==9.1.0 diff --git a/requirements.txt b/requirements.txt index ed129d0d944..2c96c00fdb3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ certifi>=2021.5.30 ciso8601==2.2.0 httpx==0.22.0 ifaddr==0.1.7 -jinja2==3.1.1 +jinja2==3.1.2 PyJWT==2.3.0 cryptography==36.0.2 pip>=21.0,<22.1 diff --git a/setup.cfg b/setup.cfg index 51226c30924..47bf617755a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,7 @@ install_requires = # httpcore, anyio, and h11 in gen_requirements_all httpx==0.22.0 ifaddr==0.1.7 - jinja2==3.1.1 + jinja2==3.1.2 PyJWT==2.3.0 # PyJWT has loose dependency. We want the latest one. cryptography==36.0.2 From 184421dae68c8f97d7a9f796e2a26137089de562 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 16:44:27 +0200 Subject: [PATCH 413/930] Support this variable in template switch actions (#71799) --- homeassistant/components/template/switch.py | 4 +- tests/components/template/test_switch.py | 68 ++++++++------------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/template/switch.py b/homeassistant/components/template/switch.py index 8a3955db007..ac01bc66812 100644 --- a/homeassistant/components/template/switch.py +++ b/homeassistant/components/template/switch.py @@ -156,14 +156,14 @@ class SwitchTemplate(TemplateEntity, SwitchEntity, RestoreEntity): async def async_turn_on(self, **kwargs): """Fire the on action.""" - await self._on_script.async_run(context=self._context) + await self.async_run_script(self._on_script, context=self._context) if self._template is None: self._state = True self.async_write_ha_state() async def async_turn_off(self, **kwargs): """Fire the off action.""" - await self._off_script.async_run(context=self._context) + await self.async_run_script(self._off_script, context=self._context) if self._template is None: self._state = False self.async_write_ha_state() diff --git a/tests/components/template/test_switch.py b/tests/components/template/test_switch.py index 93e0f8540bf..3c9c22154c9 100644 --- a/tests/components/template/test_switch.py +++ b/tests/components/template/test_switch.py @@ -1,53 +1,35 @@ """The tests for the Template switch platform.""" -import pytest from homeassistant import setup from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( - ATTR_DOMAIN, ATTR_ENTITY_ID, - ATTR_SERVICE_DATA, - EVENT_CALL_SERVICE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, ) -from homeassistant.core import CoreState, State, callback +from homeassistant.core import CoreState, State from homeassistant.setup import async_setup_component from tests.common import assert_setup_component, mock_component, mock_restore_cache - -@pytest.fixture -def service_calls(hass): - """Track service call events for switch.test_state.""" - events = [] - entity_id = "switch.test_state" - - @callback - def capture_events(event): - if event.data[ATTR_DOMAIN] != "switch": - return - if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: - return - events.append(event) - - hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) - - return events - - OPTIMISTIC_SWITCH_CONFIG = { "turn_on": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", + "service": "test.automation", + "data_template": { + "action": "turn_on", + "caller": "{{ this.entity_id }}", + }, }, "turn_off": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", + "service": "test.automation", + "data_template": { + "action": "turn_off", + "caller": "{{ this.entity_id }}", + }, }, } @@ -367,7 +349,7 @@ async def test_missing_off_does_not_create(hass): assert hass.states.async_all("switch") == [] -async def test_on_action(hass, service_calls): +async def test_on_action(hass, calls): """Test on action.""" assert await async_setup_component( hass, @@ -402,11 +384,12 @@ async def test_on_action(hass, service_calls): blocking=True, ) - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_on" + assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_on" + assert calls[-1].data["caller"] == "switch.test_template_switch" -async def test_on_action_optimistic(hass, service_calls): +async def test_on_action_optimistic(hass, calls): """Test on action in optimistic mode.""" assert await async_setup_component( hass, @@ -442,11 +425,12 @@ async def test_on_action_optimistic(hass, service_calls): state = hass.states.get("switch.test_template_switch") assert state.state == STATE_ON - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_on" + assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_on" + assert calls[-1].data["caller"] == "switch.test_template_switch" -async def test_off_action(hass, service_calls): +async def test_off_action(hass, calls): """Test off action.""" assert await async_setup_component( hass, @@ -481,11 +465,12 @@ async def test_off_action(hass, service_calls): blocking=True, ) - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_off" + assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_off" + assert calls[-1].data["caller"] == "switch.test_template_switch" -async def test_off_action_optimistic(hass, service_calls): +async def test_off_action_optimistic(hass, calls): """Test off action in optimistic mode.""" assert await async_setup_component( hass, @@ -521,8 +506,9 @@ async def test_off_action_optimistic(hass, service_calls): state = hass.states.get("switch.test_template_switch") assert state.state == STATE_OFF - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_off" + assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_off" + assert calls[-1].data["caller"] == "switch.test_template_switch" async def test_restore_state(hass): From 042321be6019366fc5449e3015f6234ff6513424 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 17:28:36 +0200 Subject: [PATCH 414/930] Support this variable in template button actions (#71792) --- homeassistant/components/template/button.py | 2 +- tests/components/template/test_button.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/template/button.py b/homeassistant/components/template/button.py index d6f41649734..ac83f76ca91 100644 --- a/homeassistant/components/template/button.py +++ b/homeassistant/components/template/button.py @@ -93,4 +93,4 @@ class TemplateButtonEntity(TemplateEntity, ButtonEntity): async def async_press(self) -> None: """Press the button.""" - await self._command_press.async_run(context=self._context) + await self.async_run_script(self._command_press, context=self._context) diff --git a/tests/components/template/test_button.py b/tests/components/template/test_button.py index 25bced16ed2..48bedd8e928 100644 --- a/tests/components/template/test_button.py +++ b/tests/components/template/test_button.py @@ -68,7 +68,10 @@ async def test_all_optional_config(hass, calls): "template": { "unique_id": "test", "button": { - "press": {"service": "test.automation"}, + "press": { + "service": "test.automation", + "data_template": {"caller": "{{ this.entity_id }}"}, + }, "device_class": "restart", "unique_id": "test", "name": "test", @@ -104,6 +107,7 @@ async def test_all_optional_config(hass, calls): ) assert len(calls) == 1 + assert calls[0].data["caller"] == _TEST_OPTIONS_BUTTON _verify( hass, From 28560e76e9f952f005315c84e2171c90558a53fd Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Fri, 13 May 2022 17:29:56 +0200 Subject: [PATCH 415/930] Fix mixing string references in Motion Blinds translations (#71806) --- homeassistant/components/motion_blinds/strings.json | 2 +- homeassistant/components/motion_blinds/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/motion_blinds/strings.json b/homeassistant/components/motion_blinds/strings.json index 13a4d117344..0b1482883aa 100644 --- a/homeassistant/components/motion_blinds/strings.json +++ b/homeassistant/components/motion_blinds/strings.json @@ -26,7 +26,7 @@ "discovery_error": "Failed to discover a Motion Gateway" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%], connection settings are updated", + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", "connection_error": "[%key:common::config_flow::error::cannot_connect%]" } diff --git a/homeassistant/components/motion_blinds/translations/en.json b/homeassistant/components/motion_blinds/translations/en.json index 92931ee27ab..7a6d6763fdc 100644 --- a/homeassistant/components/motion_blinds/translations/en.json +++ b/homeassistant/components/motion_blinds/translations/en.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Device is already configured, connection settings are updated", + "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "connection_error": "Failed to connect" }, From e7e45209ec0c8a245dc2388d50b546a9426bbe5c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 12:07:26 -0400 Subject: [PATCH 416/930] Update stale docstring in logbook (#71814) --- homeassistant/components/logbook/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 80748768c55..d850df847a0 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -355,11 +355,7 @@ def _humanify( context_augmenter: ContextAugmenter, format_time: Callable[[Row], Any], ) -> Generator[dict[str, Any], None, None]: - """Generate a converted list of events into Entry objects. - - Will try to group events if possible: - - if Home Assistant stop and start happen in same minute call it restarted - """ + """Generate a converted list of events into entries.""" external_events = hass.data.get(DOMAIN, {}) # Continuous sensors, will be excluded from the logbook continuous_sensors: dict[str, bool] = {} From d215cdc563d21b74b26c38ec9f95d63341f09347 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 12:10:43 -0400 Subject: [PATCH 417/930] Avoid buffering logbook rows unless we are selecting less than a days worth (#71809) --- homeassistant/components/logbook/__init__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index d850df847a0..592831badf1 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -445,10 +445,22 @@ def _get_events( def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" - if entity_ids or context_id: + # end_day - start_day intentionally checks .days and not .total_seconds() + # since we don't want to switch over to buffered if they go + # over one day by a few hours since the UI makes it so easy to do that. + if entity_ids or context_id or (end_day - start_day).days <= 1: rows = query.all() else: - rows = query.yield_per(1000) + # Only buffer rows to reduce memory pressure + # if we expect the result set is going to be very large. + # What is considered very large is going to differ + # based on the hardware Home Assistant is running on. + # + # sqlalchemy suggests that is at least 10k, but for + # even and RPi3 that number seems higher in testing + # so we don't switch over until we request > 1 day+ of data. + # + rows = query.yield_per(1024) for row in rows: context_lookup.setdefault(row.context_id, row) if row.context_only: From 9eca91afc97be0f9a58953dce31fd1876a5e8152 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:31:37 +0200 Subject: [PATCH 418/930] Support this variable in template light actions (#71805) --- homeassistant/components/template/light.py | 37 +++-- tests/components/template/test_light.py | 176 ++++++++++----------- 2 files changed, 104 insertions(+), 109 deletions(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 1988e77d223..eafb0b3f4d0 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -396,14 +396,18 @@ class LightTemplate(TemplateEntity, LightEntity): if ATTR_COLOR_TEMP in kwargs and self._temperature_script: common_params["color_temp"] = kwargs[ATTR_COLOR_TEMP] - await self._temperature_script.async_run( - common_params, context=self._context + await self.async_run_script( + self._temperature_script, + run_variables=common_params, + context=self._context, ) elif ATTR_WHITE_VALUE in kwargs and self._white_value_script: common_params["white_value"] = kwargs[ATTR_WHITE_VALUE] - await self._white_value_script.async_run( - common_params, context=self._context + await self.async_run_script( + self._white_value_script, + run_variables=common_params, + context=self._context, ) elif ATTR_EFFECT in kwargs and self._effect_script: effect = kwargs[ATTR_EFFECT] @@ -418,21 +422,26 @@ class LightTemplate(TemplateEntity, LightEntity): common_params["effect"] = effect - await self._effect_script.async_run(common_params, context=self._context) + await self.async_run_script( + self._effect_script, run_variables=common_params, context=self._context + ) elif ATTR_HS_COLOR in kwargs and self._color_script: hs_value = kwargs[ATTR_HS_COLOR] common_params["hs"] = hs_value common_params["h"] = int(hs_value[0]) common_params["s"] = int(hs_value[1]) - await self._color_script.async_run( - common_params, - context=self._context, + await self.async_run_script( + self._color_script, run_variables=common_params, context=self._context ) elif ATTR_BRIGHTNESS in kwargs and self._level_script: - await self._level_script.async_run(common_params, context=self._context) + await self.async_run_script( + self._level_script, run_variables=common_params, context=self._context + ) else: - await self._on_script.async_run(common_params, context=self._context) + await self.async_run_script( + self._on_script, run_variables=common_params, context=self._context + ) if optimistic_set: self.async_write_ha_state() @@ -440,11 +449,13 @@ class LightTemplate(TemplateEntity, LightEntity): async def async_turn_off(self, **kwargs): """Turn the light off.""" if ATTR_TRANSITION in kwargs and self._supports_transition is True: - await self._off_script.async_run( - {"transition": kwargs[ATTR_TRANSITION]}, context=self._context + await self.async_run_script( + self._off_script, + run_variables={"transition": kwargs[ATTR_TRANSITION]}, + context=self._context, ) else: - await self._off_script.async_run(context=self._context) + await self.async_run_script(self._off_script, context=self._context) if self._template is None: self._state = False self.async_write_ha_state() diff --git a/tests/components/template/test_light.py b/tests/components/template/test_light.py index 394eb1cb9ba..ca03c6f5d82 100644 --- a/tests/components/template/test_light.py +++ b/tests/components/template/test_light.py @@ -36,12 +36,18 @@ _STATE_AVAILABILITY_BOOLEAN = "availability_boolean.state" OPTIMISTIC_ON_OFF_LIGHT_CONFIG = { "turn_on": { - "service": "light.turn_on", - "entity_id": "light.test_state", + "service": "test.automation", + "data_template": { + "action": "turn_on", + "caller": "{{ this.entity_id }}", + }, }, "turn_off": { - "service": "light.turn_off", - "entity_id": "light.test_state", + "service": "test.automation", + "data_template": { + "action": "turn_off", + "caller": "{{ this.entity_id }}", + }, }, } @@ -49,10 +55,38 @@ OPTIMISTIC_ON_OFF_LIGHT_CONFIG = { OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG = { **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, "set_level": { - "service": "light.turn_on", + "service": "test.automation", "data_template": { - "entity_id": "light.test_state", + "action": "set_level", "brightness": "{{brightness}}", + "caller": "{{ this.entity_id }}", + }, + }, +} + + +OPTIMISTIC_COLOR_TEMP_LIGHT_CONFIG = { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "set_temperature": { + "service": "test.automation", + "data_template": { + "action": "set_temperature", + "caller": "{{ this.entity_id }}", + "color_temp": "{{color_temp}}", + }, + }, +} + + +OPTIMISTIC_HS_COLOR_LIGHT_CONFIG = { + **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + "set_color": { + "service": "test.automation", + "data_template": { + "action": "set_color", + "caller": "{{ this.entity_id }}", + "s": "{{s}}", + "h": "{{h}}", }, }, } @@ -61,9 +95,10 @@ OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG = { OPTIMISTIC_WHITE_VALUE_LIGHT_CONFIG = { **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, "set_white_value": { - "service": "light.turn_on", + "service": "test.automation", "data_template": { - "entity_id": "light.test_state", + "action": "set_white_value", + "caller": "{{ this.entity_id }}", "white_value": "{{white_value}}", }, }, @@ -270,7 +305,6 @@ async def test_missing_key(hass, count, setup_light): "test_template_light": { **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{states.light.test_state.state}}", - "turn_on": {"service": "test.automation"}, } }, ], @@ -296,6 +330,8 @@ async def test_on_action( ) assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_on" + assert calls[-1].data["caller"] == "light.test_template_light" assert state.state == STATE_OFF assert "color_mode" not in state.attributes @@ -377,7 +413,6 @@ async def test_on_action_with_transition( { "test_template_light": { **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, - "turn_on": {"service": "test.automation"}, } }, ], @@ -409,6 +444,8 @@ async def test_on_action_optimistic( state = hass.states.get("light.test_template_light") assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_on" + assert calls[-1].data["caller"] == "light.test_template_light" assert state.state == STATE_ON assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None assert state.attributes["supported_color_modes"] == supported_color_modes @@ -422,7 +459,10 @@ async def test_on_action_optimistic( ) state = hass.states.get("light.test_template_light") - assert len(calls) == 1 + assert len(calls) == 2 + assert calls[-1].data["action"] == "set_level" + assert calls[-1].data["brightness"] == 100 + assert calls[-1].data["caller"] == "light.test_template_light" assert state.state == STATE_ON assert state.attributes["color_mode"] == expected_color_mode assert state.attributes["supported_color_modes"] == supported_color_modes @@ -441,7 +481,6 @@ async def test_on_action_optimistic( "test_template_light": { **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{states.light.test_state.state}}", - "turn_off": {"service": "test.automation"}, } }, ], @@ -467,6 +506,8 @@ async def test_off_action( ) assert len(calls) == 1 + assert calls[-1].data["action"] == "turn_off" + assert calls[-1].data["caller"] == "light.test_template_light" assert state.state == STATE_ON assert state.attributes["color_mode"] == ColorMode.UNKNOWN # Brightness is None assert state.attributes["supported_color_modes"] == supported_color_modes @@ -546,7 +587,6 @@ async def test_off_action_with_transition( { "test_template_light": { **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, - "turn_off": {"service": "test.automation"}, } }, ], @@ -586,15 +626,8 @@ async def test_off_action_optimistic( [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_WHITE_VALUE_LIGHT_CONFIG, "value_template": "{{1 == 1}}", - "set_white_value": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "white_value": "{{white_value}}", - }, - }, } }, ], @@ -619,7 +652,9 @@ async def test_white_value_action_no_template( ) assert len(calls) == 1 - assert calls[0].data["white_value"] == 124 + assert calls[-1].data["action"] == "set_white_value" + assert calls[-1].data["caller"] == "light.test_template_light" + assert calls[-1].data["white_value"] == 124 state = hass.states.get("light.test_template_light") assert state.attributes.get("white_value") == 124 @@ -682,15 +717,8 @@ async def test_white_value_template( [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{1 == 1}}", - "set_level": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "brightness": "{{brightness}}", - }, - }, } }, ], @@ -715,7 +743,9 @@ async def test_level_action_no_template( ) assert len(calls) == 1 - assert calls[0].data["brightness"] == 124 + assert calls[-1].data["action"] == "set_level" + assert calls[-1].data["brightness"] == 124 + assert calls[-1].data["caller"] == "light.test_template_light" state = hass.states.get("light.test_template_light") assert state.state == STATE_ON @@ -757,15 +787,8 @@ async def test_level_template( """Test the template for the level.""" light_config = { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{ 1 == 1 }}", - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, "level_template": level_template, } } @@ -806,15 +829,8 @@ async def test_temperature_template( """Test the template for the temperature.""" light_config = { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_COLOR_TEMP_LIGHT_CONFIG, "value_template": "{{ 1 == 1 }}", - "set_temperature": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "color_temp": "{{color_temp}}", - }, - }, "temperature_template": temperature_template, } } @@ -837,15 +853,8 @@ async def test_temperature_template( [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_COLOR_TEMP_LIGHT_CONFIG, "value_template": "{{1 == 1}}", - "set_temperature": { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "color_temp": "{{color_temp}}", - }, - }, } }, ], @@ -870,7 +879,9 @@ async def test_temperature_action_no_template( ) assert len(calls) == 1 - assert calls[0].data["color_temp"] == 345 + assert calls[-1].data["action"] == "set_temperature" + assert calls[-1].data["caller"] == "light.test_template_light" + assert calls[-1].data["color_temp"] == 345 state = hass.states.get("light.test_template_light") assert state is not None @@ -971,18 +982,8 @@ async def test_entity_picture_template(hass, setup_light): [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_HS_COLOR_LIGHT_CONFIG, "value_template": "{{1 == 1}}", - "set_color": [ - { - "service": "test.automation", - "data_template": { - "entity_id": "test.test_state", - "s": "{{s}}", - "h": "{{h}}", - }, - }, - ], } }, ], @@ -1007,8 +1008,10 @@ async def test_color_action_no_template( ) assert len(calls) == 1 - assert calls[0].data["h"] == 40 - assert calls[0].data["s"] == 50 + assert calls[-1].data["action"] == "set_color" + assert calls[-1].data["caller"] == "light.test_template_light" + assert calls[-1].data["h"] == 40 + assert calls[-1].data["s"] == 50 state = hass.states.get("light.test_template_light") assert state.state == STATE_ON @@ -1048,17 +1051,8 @@ async def test_color_template( """Test the template for the color.""" light_config = { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_HS_COLOR_LIGHT_CONFIG, "value_template": "{{ 1 == 1 }}", - "set_color": [ - { - "service": "input_number.set_value", - "data_template": { - "entity_id": "input_number.h", - "color_temp": "{{h}}", - }, - } - ], "color_template": color_template, } } @@ -1192,18 +1186,13 @@ async def test_color_and_temperature_actions_no_template( [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{true}}", - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, "set_effect": { "service": "test.automation", "data_template": { + "action": "set_effect", + "caller": "{{ this.entity_id }}", "entity_id": "test.test_state", "effect": "{{effect}}", }, @@ -1227,7 +1216,9 @@ async def test_effect_action_valid_effect(hass, setup_light, calls): ) assert len(calls) == 1 - assert calls[0].data["effect"] == "Disco" + assert calls[-1].data["action"] == "set_effect" + assert calls[-1].data["caller"] == "light.test_template_light" + assert calls[-1].data["effect"] == "Disco" state = hass.states.get("light.test_template_light") assert state is not None @@ -1240,15 +1231,8 @@ async def test_effect_action_valid_effect(hass, setup_light, calls): [ { "test_template_light": { - **OPTIMISTIC_ON_OFF_LIGHT_CONFIG, + **OPTIMISTIC_BRIGHTNESS_LIGHT_CONFIG, "value_template": "{{true}}", - "set_level": { - "service": "light.turn_on", - "data_template": { - "entity_id": "light.test_state", - "brightness": "{{brightness}}", - }, - }, "set_effect": { "service": "test.automation", "data_template": { From 6f7a4653471909aa6e47ad58a3a5d651b37f35ad Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:32:19 +0200 Subject: [PATCH 419/930] Support this variable in template vacuum actions (#71800) --- homeassistant/components/template/vacuum.py | 18 ++- tests/components/template/test_vacuum.py | 166 +++++++++++++++----- 2 files changed, 141 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/template/vacuum.py b/homeassistant/components/template/vacuum.py index 1d350d120c7..4b278ef6aec 100644 --- a/homeassistant/components/template/vacuum.py +++ b/homeassistant/components/template/vacuum.py @@ -204,42 +204,42 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): async def async_start(self): """Start or resume the cleaning task.""" - await self._start_script.async_run(context=self._context) + await self.async_run_script(self._start_script, context=self._context) async def async_pause(self): """Pause the cleaning task.""" if self._pause_script is None: return - await self._pause_script.async_run(context=self._context) + await self.async_run_script(self._pause_script, context=self._context) async def async_stop(self, **kwargs): """Stop the cleaning task.""" if self._stop_script is None: return - await self._stop_script.async_run(context=self._context) + await self.async_run_script(self._stop_script, context=self._context) async def async_return_to_base(self, **kwargs): """Set the vacuum cleaner to return to the dock.""" if self._return_to_base_script is None: return - await self._return_to_base_script.async_run(context=self._context) + await self.async_run_script(self._return_to_base_script, context=self._context) async def async_clean_spot(self, **kwargs): """Perform a spot clean-up.""" if self._clean_spot_script is None: return - await self._clean_spot_script.async_run(context=self._context) + await self.async_run_script(self._clean_spot_script, context=self._context) async def async_locate(self, **kwargs): """Locate the vacuum cleaner.""" if self._locate_script is None: return - await self._locate_script.async_run(context=self._context) + await self.async_run_script(self._locate_script, context=self._context) async def async_set_fan_speed(self, fan_speed, **kwargs): """Set fan speed.""" @@ -248,8 +248,10 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity): if fan_speed in self._attr_fan_speed_list: self._attr_fan_speed = fan_speed - await self._set_fan_speed_script.async_run( - {ATTR_FAN_SPEED: fan_speed}, context=self._context + await self.async_run_script( + self._set_fan_speed_script, + run_variables={ATTR_FAN_SPEED: fan_speed}, + context=self._context, ) else: _LOGGER.error( diff --git a/tests/components/template/test_vacuum.py b/tests/components/template/test_vacuum.py index 1fd875f2df8..e454696d12a 100644 --- a/tests/components/template/test_vacuum.py +++ b/tests/components/template/test_vacuum.py @@ -342,7 +342,7 @@ async def test_unused_services(hass): _verify(hass, STATE_UNKNOWN, None) -async def test_state_services(hass): +async def test_state_services(hass, calls): """Test state services.""" await _register_components(hass) @@ -353,6 +353,9 @@ async def test_state_services(hass): # verify assert hass.states.get(_STATE_INPUT_SELECT).state == STATE_CLEANING _verify(hass, STATE_CLEANING, None) + assert len(calls) == 1 + assert calls[-1].data["action"] == "start" + assert calls[-1].data["caller"] == _TEST_VACUUM # Pause vacuum await common.async_pause(hass, _TEST_VACUUM) @@ -361,6 +364,9 @@ async def test_state_services(hass): # verify assert hass.states.get(_STATE_INPUT_SELECT).state == STATE_PAUSED _verify(hass, STATE_PAUSED, None) + assert len(calls) == 2 + assert calls[-1].data["action"] == "pause" + assert calls[-1].data["caller"] == _TEST_VACUUM # Stop vacuum await common.async_stop(hass, _TEST_VACUUM) @@ -369,6 +375,9 @@ async def test_state_services(hass): # verify assert hass.states.get(_STATE_INPUT_SELECT).state == STATE_IDLE _verify(hass, STATE_IDLE, None) + assert len(calls) == 3 + assert calls[-1].data["action"] == "stop" + assert calls[-1].data["caller"] == _TEST_VACUUM # Return vacuum to base await common.async_return_to_base(hass, _TEST_VACUUM) @@ -377,9 +386,12 @@ async def test_state_services(hass): # verify assert hass.states.get(_STATE_INPUT_SELECT).state == STATE_RETURNING _verify(hass, STATE_RETURNING, None) + assert len(calls) == 4 + assert calls[-1].data["action"] == "return_to_base" + assert calls[-1].data["caller"] == _TEST_VACUUM -async def test_clean_spot_service(hass): +async def test_clean_spot_service(hass, calls): """Test clean spot service.""" await _register_components(hass) @@ -389,9 +401,12 @@ async def test_clean_spot_service(hass): # verify assert hass.states.get(_SPOT_CLEANING_INPUT_BOOLEAN).state == STATE_ON + assert len(calls) == 1 + assert calls[-1].data["action"] == "clean_spot" + assert calls[-1].data["caller"] == _TEST_VACUUM -async def test_locate_service(hass): +async def test_locate_service(hass, calls): """Test locate service.""" await _register_components(hass) @@ -401,9 +416,12 @@ async def test_locate_service(hass): # verify assert hass.states.get(_LOCATING_INPUT_BOOLEAN).state == STATE_ON + assert len(calls) == 1 + assert calls[-1].data["action"] == "locate" + assert calls[-1].data["caller"] == _TEST_VACUUM -async def test_set_fan_speed(hass): +async def test_set_fan_speed(hass, calls): """Test set valid fan speed.""" await _register_components(hass) @@ -413,6 +431,10 @@ async def test_set_fan_speed(hass): # verify assert hass.states.get(_FAN_SPEED_INPUT_SELECT).state == "high" + assert len(calls) == 1 + assert calls[-1].data["action"] == "set_fan_speed" + assert calls[-1].data["caller"] == _TEST_VACUUM + assert calls[-1].data["option"] == "high" # Set fan's speed to medium await common.async_set_fan_speed(hass, "medium", _TEST_VACUUM) @@ -420,9 +442,13 @@ async def test_set_fan_speed(hass): # verify assert hass.states.get(_FAN_SPEED_INPUT_SELECT).state == "medium" + assert len(calls) == 2 + assert calls[-1].data["action"] == "set_fan_speed" + assert calls[-1].data["caller"] == _TEST_VACUUM + assert calls[-1].data["option"] == "medium" -async def test_set_invalid_fan_speed(hass): +async def test_set_invalid_fan_speed(hass, calls): """Test set invalid fan speed when fan has valid speed.""" await _register_components(hass) @@ -522,37 +548,107 @@ async def _register_components(hass): test_vacuum_config = { "value_template": "{{ states('input_select.state') }}", "fan_speed_template": "{{ states('input_select.fan_speed') }}", - "start": { - "service": "input_select.select_option", - "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_CLEANING}, - }, - "pause": { - "service": "input_select.select_option", - "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_PAUSED}, - }, - "stop": { - "service": "input_select.select_option", - "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_IDLE}, - }, - "return_to_base": { - "service": "input_select.select_option", - "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_RETURNING}, - }, - "clean_spot": { - "service": "input_boolean.turn_on", - "entity_id": _SPOT_CLEANING_INPUT_BOOLEAN, - }, - "locate": { - "service": "input_boolean.turn_on", - "entity_id": _LOCATING_INPUT_BOOLEAN, - }, - "set_fan_speed": { - "service": "input_select.select_option", - "data_template": { - "entity_id": _FAN_SPEED_INPUT_SELECT, - "option": "{{ fan_speed }}", + "start": [ + { + "service": "input_select.select_option", + "data": { + "entity_id": _STATE_INPUT_SELECT, + "option": STATE_CLEANING, + }, }, - }, + { + "service": "test.automation", + "data_template": { + "action": "start", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "pause": [ + { + "service": "input_select.select_option", + "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_PAUSED}, + }, + { + "service": "test.automation", + "data_template": { + "action": "pause", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "stop": [ + { + "service": "input_select.select_option", + "data": {"entity_id": _STATE_INPUT_SELECT, "option": STATE_IDLE}, + }, + { + "service": "test.automation", + "data_template": { + "action": "stop", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "return_to_base": [ + { + "service": "input_select.select_option", + "data": { + "entity_id": _STATE_INPUT_SELECT, + "option": STATE_RETURNING, + }, + }, + { + "service": "test.automation", + "data_template": { + "action": "return_to_base", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "clean_spot": [ + { + "service": "input_boolean.turn_on", + "entity_id": _SPOT_CLEANING_INPUT_BOOLEAN, + }, + { + "service": "test.automation", + "data_template": { + "action": "clean_spot", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "locate": [ + { + "service": "input_boolean.turn_on", + "entity_id": _LOCATING_INPUT_BOOLEAN, + }, + { + "service": "test.automation", + "data_template": { + "action": "locate", + "caller": "{{ this.entity_id }}", + }, + }, + ], + "set_fan_speed": [ + { + "service": "input_select.select_option", + "data_template": { + "entity_id": _FAN_SPEED_INPUT_SELECT, + "option": "{{ fan_speed }}", + }, + }, + { + "service": "test.automation", + "data_template": { + "action": "set_fan_speed", + "caller": "{{ this.entity_id }}", + "option": "{{ fan_speed }}", + }, + }, + ], "fan_speeds": ["low", "medium", "high"], "attribute_templates": { "test_attribute": "It {{ states.sensor.test_state.state }}." From adde9130a1d7db0ad9cc1c6054f07616bc1dc288 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:32:45 +0200 Subject: [PATCH 420/930] Support this variable in template select actions (#71798) --- homeassistant/components/template/select.py | 6 +++-- tests/components/template/test_select.py | 30 ++++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/template/select.py b/homeassistant/components/template/select.py index fa2668c1e81..19f17096178 100644 --- a/homeassistant/components/template/select.py +++ b/homeassistant/components/template/select.py @@ -133,8 +133,10 @@ class TemplateSelect(TemplateEntity, SelectEntity): if self._optimistic: self._attr_current_option = option self.async_write_ha_state() - await self._command_select_option.async_run( - {ATTR_OPTION: option}, context=self._context + await self.async_run_script( + self._command_select_option, + run_variables={ATTR_OPTION: option}, + context=self._context, ) diff --git a/tests/components/template/test_select.py b/tests/components/template/test_select.py index de41ccedbb1..a58de63f186 100644 --- a/tests/components/template/test_select.py +++ b/tests/components/template/test_select.py @@ -131,7 +131,7 @@ async def test_missing_required_keys(hass): assert hass.states.async_all("select") == [] -async def test_templates_with_entities(hass): +async def test_templates_with_entities(hass, calls): """Test templates with values from other entities.""" with assert_setup_component(1, "input_select"): assert await setup.async_setup_component( @@ -158,13 +158,23 @@ async def test_templates_with_entities(hass): "select": { "state": f"{{{{ states('{_OPTION_INPUT_SELECT}') }}}}", "options": f"{{{{ state_attr('{_OPTION_INPUT_SELECT}', '{INPUT_SELECT_ATTR_OPTIONS}') }}}}", - "select_option": { - "service": "input_select.select_option", - "data_template": { - "entity_id": _OPTION_INPUT_SELECT, - "option": "{{ option }}", + "select_option": [ + { + "service": "input_select.select_option", + "data_template": { + "entity_id": _OPTION_INPUT_SELECT, + "option": "{{ option }}", + }, }, - }, + { + "service": "test.automation", + "data_template": { + "action": "select_option", + "caller": "{{ this.entity_id }}", + "option": "{{ option }}", + }, + }, + ], "optimistic": True, "unique_id": "a", }, @@ -212,6 +222,12 @@ async def test_templates_with_entities(hass): ) _verify(hass, "c", ["a", "b", "c"]) + # Check this variable can be used in set_value script + assert len(calls) == 1 + assert calls[-1].data["action"] == "select_option" + assert calls[-1].data["caller"] == _TEST_SELECT + assert calls[-1].data["option"] == "c" + async def test_trigger_select(hass): """Test trigger based template select.""" From 66ec4564f4b5c07debaac4e164d5bcae3a7bfe81 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:33:00 +0200 Subject: [PATCH 421/930] Support this variable in template number actions (#71797) --- homeassistant/components/template/number.py | 6 +++-- tests/components/template/test_number.py | 30 ++++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/template/number.py b/homeassistant/components/template/number.py index 89adb45a6d0..54131990a26 100644 --- a/homeassistant/components/template/number.py +++ b/homeassistant/components/template/number.py @@ -157,8 +157,10 @@ class TemplateNumber(TemplateEntity, NumberEntity): if self._optimistic: self._attr_value = value self.async_write_ha_state() - await self._command_set_value.async_run( - {ATTR_VALUE: value}, context=self._context + await self.async_run_script( + self._command_set_value, + run_variables={ATTR_VALUE: value}, + context=self._context, ) diff --git a/tests/components/template/test_number.py b/tests/components/template/test_number.py index 6c47fdd2abc..ea29bb303df 100644 --- a/tests/components/template/test_number.py +++ b/tests/components/template/test_number.py @@ -127,7 +127,7 @@ async def test_all_optional_config(hass): _verify(hass, 4, 1, 3, 5) -async def test_templates_with_entities(hass): +async def test_templates_with_entities(hass, calls): """Test templates with values from other entities.""" with assert_setup_component(4, "input_number"): assert await setup.async_setup_component( @@ -173,13 +173,23 @@ async def test_templates_with_entities(hass): "step": f"{{{{ states('{_STEP_INPUT_NUMBER}') }}}}", "min": f"{{{{ states('{_MINIMUM_INPUT_NUMBER}') }}}}", "max": f"{{{{ states('{_MAXIMUM_INPUT_NUMBER}') }}}}", - "set_value": { - "service": "input_number.set_value", - "data_template": { - "entity_id": _VALUE_INPUT_NUMBER, - "value": "{{ value }}", + "set_value": [ + { + "service": "input_number.set_value", + "data_template": { + "entity_id": _VALUE_INPUT_NUMBER, + "value": "{{ value }}", + }, }, - }, + { + "service": "test.automation", + "data_template": { + "action": "set_value", + "caller": "{{ this.entity_id }}", + "value": "{{ value }}", + }, + }, + ], "optimistic": True, "unique_id": "a", }, @@ -247,6 +257,12 @@ async def test_templates_with_entities(hass): ) _verify(hass, 2, 2, 2, 6) + # Check this variable can be used in set_value script + assert len(calls) == 1 + assert calls[-1].data["action"] == "set_value" + assert calls[-1].data["caller"] == _TEST_NUMBER + assert calls[-1].data["value"] == 2 + async def test_trigger_number(hass): """Test trigger based template number.""" From 8b412acc9851e6197b333086f919d22b5e1782c3 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:33:25 +0200 Subject: [PATCH 422/930] Support this variable in template lock actions (#71796) --- homeassistant/components/template/lock.py | 4 +- tests/components/template/test_lock.py | 59 ++++++++--------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/template/lock.py b/homeassistant/components/template/lock.py index 78141c0d25e..1d94194be63 100644 --- a/homeassistant/components/template/lock.py +++ b/homeassistant/components/template/lock.py @@ -141,11 +141,11 @@ class TemplateLock(TemplateEntity, LockEntity): if self._optimistic: self._state = True self.async_write_ha_state() - await self._command_lock.async_run(context=self._context) + await self.async_run_script(self._command_lock, context=self._context) async def async_unlock(self, **kwargs): """Unlock the device.""" if self._optimistic: self._state = False self.async_write_ha_state() - await self._command_unlock.async_run(context=self._context) + await self.async_run_script(self._command_unlock, context=self._context) diff --git a/tests/components/template/test_lock.py b/tests/components/template/test_lock.py index e53f6660162..1ed5296f681 100644 --- a/tests/components/template/test_lock.py +++ b/tests/components/template/test_lock.py @@ -3,46 +3,23 @@ import pytest from homeassistant import setup from homeassistant.components import lock -from homeassistant.const import ( - ATTR_DOMAIN, - ATTR_ENTITY_ID, - ATTR_SERVICE_DATA, - EVENT_CALL_SERVICE, - STATE_OFF, - STATE_ON, - STATE_UNAVAILABLE, -) -from homeassistant.core import callback - - -@pytest.fixture -def service_calls(hass): - """Track service call events for switch.test_state.""" - events = [] - entity_id = "switch.test_state" - - @callback - def capture_events(event): - if event.data[ATTR_DOMAIN] != "switch": - return - if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: - return - events.append(event) - - hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) - - return events - +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON, STATE_UNAVAILABLE OPTIMISTIC_LOCK_CONFIG = { "platform": "template", "lock": { - "service": "switch.turn_on", - "entity_id": "switch.test_state", + "service": "test.automation", + "data_template": { + "action": "lock", + "caller": "{{ this.entity_id }}", + }, }, "unlock": { - "service": "switch.turn_off", - "entity_id": "switch.test_state", + "service": "test.automation", + "data_template": { + "action": "unlock", + "caller": "{{ this.entity_id }}", + }, }, } @@ -201,7 +178,7 @@ async def test_template_static(hass, start_ha): }, ], ) -async def test_lock_action(hass, start_ha, service_calls): +async def test_lock_action(hass, start_ha, calls): """Test lock action.""" await setup.async_setup_component(hass, "switch", {}) hass.states.async_set("switch.test_state", STATE_OFF) @@ -215,8 +192,9 @@ async def test_lock_action(hass, start_ha, service_calls): ) await hass.async_block_till_done() - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_on" + assert len(calls) == 1 + assert calls[0].data["action"] == "lock" + assert calls[0].data["caller"] == "lock.template_lock" @pytest.mark.parametrize("count,domain", [(1, lock.DOMAIN)]) @@ -231,7 +209,7 @@ async def test_lock_action(hass, start_ha, service_calls): }, ], ) -async def test_unlock_action(hass, start_ha, service_calls): +async def test_unlock_action(hass, start_ha, calls): """Test unlock action.""" await setup.async_setup_component(hass, "switch", {}) hass.states.async_set("switch.test_state", STATE_ON) @@ -245,8 +223,9 @@ async def test_unlock_action(hass, start_ha, service_calls): ) await hass.async_block_till_done() - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "turn_off" + assert len(calls) == 1 + assert calls[0].data["action"] == "unlock" + assert calls[0].data["caller"] == "lock.template_lock" @pytest.mark.parametrize("count,domain", [(1, lock.DOMAIN)]) From a17fa6d6d530cdb74dd8d9eceee7054e17ef61ad Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:33:44 +0200 Subject: [PATCH 423/930] Support this variable in template fan actions (#71795) --- homeassistant/components/template/fan.py | 31 ++-- tests/components/template/test_fan.py | 178 +++++++++++++++++------ 2 files changed, 152 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index 6b0fdefc2f9..2c8d7247967 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -251,8 +251,9 @@ class TemplateFan(TemplateEntity, FanEntity): **kwargs, ) -> None: """Turn on the fan.""" - await self._on_script.async_run( - { + await self.async_run_script( + self._on_script, + run_variables={ ATTR_PERCENTAGE: percentage, ATTR_PRESET_MODE: preset_mode, }, @@ -267,7 +268,7 @@ class TemplateFan(TemplateEntity, FanEntity): async def async_turn_off(self, **kwargs: Any) -> None: """Turn off the fan.""" - await self._off_script.async_run(context=self._context) + await self.async_run_script(self._off_script, context=self._context) self._state = STATE_OFF async def async_set_percentage(self, percentage: int) -> None: @@ -277,8 +278,10 @@ class TemplateFan(TemplateEntity, FanEntity): self._preset_mode = None if self._set_percentage_script: - await self._set_percentage_script.async_run( - {ATTR_PERCENTAGE: self._percentage}, context=self._context + await self.async_run_script( + self._set_percentage_script, + run_variables={ATTR_PERCENTAGE: self._percentage}, + context=self._context, ) async def async_set_preset_mode(self, preset_mode: str) -> None: @@ -297,8 +300,10 @@ class TemplateFan(TemplateEntity, FanEntity): self._percentage = None if self._set_preset_mode_script: - await self._set_preset_mode_script.async_run( - {ATTR_PRESET_MODE: self._preset_mode}, context=self._context + await self.async_run_script( + self._set_preset_mode_script, + run_variables={ATTR_PRESET_MODE: self._preset_mode}, + context=self._context, ) async def async_oscillate(self, oscillating: bool) -> None: @@ -307,8 +312,10 @@ class TemplateFan(TemplateEntity, FanEntity): return self._oscillating = oscillating - await self._set_oscillating_script.async_run( - {ATTR_OSCILLATING: oscillating}, context=self._context + await self.async_run_script( + self._set_oscillating_script, + run_variables={ATTR_OSCILLATING: self.oscillating}, + context=self._context, ) async def async_set_direction(self, direction: str) -> None: @@ -318,8 +325,10 @@ class TemplateFan(TemplateEntity, FanEntity): if direction in _VALID_DIRECTIONS: self._direction = direction - await self._set_direction_script.async_run( - {ATTR_DIRECTION: direction}, context=self._context + await self.async_run_script( + self._set_direction_script, + run_variables={ATTR_DIRECTION: direction}, + context=self._context, ) else: _LOGGER.error( diff --git a/tests/components/template/test_fan.py b/tests/components/template/test_fan.py index 30d5222024d..f7805ae41d6 100644 --- a/tests/components/template/test_fan.py +++ b/tests/components/template/test_fan.py @@ -383,20 +383,25 @@ async def test_invalid_availability_template_keeps_component_available( assert "x" in caplog_setup_text -async def test_on_off(hass): +async def test_on_off(hass, calls): """Test turn on and turn off.""" await _register_components(hass) + expected_calls = 0 - for func, state in [ - (common.async_turn_on, STATE_ON), - (common.async_turn_off, STATE_OFF), + for func, state, action in [ + (common.async_turn_on, STATE_ON, "turn_on"), + (common.async_turn_off, STATE_OFF, "turn_off"), ]: await func(hass, _TEST_FAN) assert hass.states.get(_STATE_INPUT_BOOLEAN).state == state _verify(hass, state, 0, None, None, None) + expected_calls += 1 + assert len(calls) == expected_calls + assert calls[-1].data["action"] == action + assert calls[-1].data["caller"] == _TEST_FAN -async def test_set_invalid_direction_from_initial_stage(hass): +async def test_set_invalid_direction_from_initial_stage(hass, calls): """Test set invalid direction when fan is in initial state.""" await _register_components(hass) @@ -407,29 +412,43 @@ async def test_set_invalid_direction_from_initial_stage(hass): _verify(hass, STATE_ON, 0, None, None, None) -async def test_set_osc(hass): +async def test_set_osc(hass, calls): """Test set oscillating.""" await _register_components(hass) + expected_calls = 0 await common.async_turn_on(hass, _TEST_FAN) + expected_calls += 1 for state in [True, False]: await common.async_oscillate(hass, _TEST_FAN, state) assert hass.states.get(_OSC_INPUT).state == str(state) _verify(hass, STATE_ON, 0, state, None, None) + expected_calls += 1 + assert len(calls) == expected_calls + assert calls[-1].data["action"] == "set_oscillating" + assert calls[-1].data["caller"] == _TEST_FAN + assert calls[-1].data["option"] == state -async def test_set_direction(hass): +async def test_set_direction(hass, calls): """Test set valid direction.""" await _register_components(hass) + expected_calls = 0 await common.async_turn_on(hass, _TEST_FAN) + expected_calls += 1 for cmd in [DIRECTION_FORWARD, DIRECTION_REVERSE]: await common.async_set_direction(hass, _TEST_FAN, cmd) assert hass.states.get(_DIRECTION_INPUT_SELECT).state == cmd _verify(hass, STATE_ON, 0, None, cmd, None) + expected_calls += 1 + assert len(calls) == expected_calls + assert calls[-1].data["action"] == "set_direction" + assert calls[-1].data["caller"] == _TEST_FAN + assert calls[-1].data["option"] == cmd -async def test_set_invalid_direction(hass): +async def test_set_invalid_direction(hass, calls): """Test set invalid direction when fan has valid direction.""" await _register_components(hass) @@ -440,30 +459,36 @@ async def test_set_invalid_direction(hass): _verify(hass, STATE_ON, 0, None, DIRECTION_FORWARD, None) -async def test_preset_modes(hass): +async def test_preset_modes(hass, calls): """Test preset_modes.""" await _register_components( hass, ["off", "low", "medium", "high", "auto", "smart"], ["auto", "smart"] ) await common.async_turn_on(hass, _TEST_FAN) - for extra, state in [ - ("auto", "auto"), - ("smart", "smart"), - ("invalid", "smart"), + for extra, state, expected_calls in [ + ("auto", "auto", 2), + ("smart", "smart", 3), + ("invalid", "smart", 3), ]: await common.async_set_preset_mode(hass, _TEST_FAN, extra) assert hass.states.get(_PRESET_MODE_INPUT_SELECT).state == state + assert len(calls) == expected_calls + assert calls[-1].data["action"] == "set_preset_mode" + assert calls[-1].data["caller"] == _TEST_FAN + assert calls[-1].data["option"] == state await common.async_turn_on(hass, _TEST_FAN, preset_mode="auto") assert hass.states.get(_PRESET_MODE_INPUT_SELECT).state == "auto" -async def test_set_percentage(hass): +async def test_set_percentage(hass, calls): """Test set valid speed percentage.""" await _register_components(hass) + expected_calls = 0 await common.async_turn_on(hass, _TEST_FAN) + expected_calls += 1 for state, value in [ (STATE_ON, 100), (STATE_ON, 66), @@ -472,13 +497,18 @@ async def test_set_percentage(hass): await common.async_set_percentage(hass, _TEST_FAN, value) assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == value _verify(hass, state, value, None, None, None) + expected_calls += 1 + assert len(calls) == expected_calls + assert calls[-1].data["action"] == "set_value" + assert calls[-1].data["caller"] == _TEST_FAN + assert calls[-1].data["value"] == value await common.async_turn_on(hass, _TEST_FAN, percentage=50) assert int(float(hass.states.get(_PERCENTAGE_INPUT_NUMBER).state)) == 50 _verify(hass, STATE_ON, 50, None, None, None) -async def test_increase_decrease_speed(hass): +async def test_increase_decrease_speed(hass, calls): """Test set valid increase and decrease speed.""" await _register_components(hass, speed_count=3) @@ -495,7 +525,7 @@ async def test_increase_decrease_speed(hass): _verify(hass, state, value, None, None, None) -async def test_increase_decrease_speed_default_speed_count(hass): +async def test_increase_decrease_speed_default_speed_count(hass, calls): """Test set valid increase and decrease speed.""" await _register_components(hass) @@ -512,7 +542,7 @@ async def test_increase_decrease_speed_default_speed_count(hass): _verify(hass, state, value, None, None, None) -async def test_set_invalid_osc_from_initial_state(hass): +async def test_set_invalid_osc_from_initial_state(hass, calls): """Test set invalid oscillating when fan is in initial state.""" await _register_components(hass) @@ -523,7 +553,7 @@ async def test_set_invalid_osc_from_initial_state(hass): _verify(hass, STATE_ON, 0, None, None, None) -async def test_set_invalid_osc(hass): +async def test_set_invalid_osc(hass, calls): """Test set invalid oscillating when fan has valid osc.""" await _register_components(hass) @@ -616,10 +646,19 @@ async def _register_components( "percentage_template": "{{ states('input_number.percentage') }}", "oscillating_template": "{{ states('input_select.osc') }}", "direction_template": "{{ states('input_select.direction') }}", - "turn_on": { - "service": "input_boolean.turn_on", - "entity_id": _STATE_INPUT_BOOLEAN, - }, + "turn_on": [ + { + "service": "input_boolean.turn_on", + "entity_id": _STATE_INPUT_BOOLEAN, + }, + { + "service": "test.automation", + "data_template": { + "action": "turn_on", + "caller": "{{ this.entity_id }}", + }, + }, + ], "turn_off": [ { "service": "input_boolean.turn_off", @@ -632,35 +671,82 @@ async def _register_components( "value": 0, }, }, + { + "service": "test.automation", + "data_template": { + "action": "turn_off", + "caller": "{{ this.entity_id }}", + }, + }, ], - "set_preset_mode": { - "service": "input_select.select_option", - "data_template": { - "entity_id": _PRESET_MODE_INPUT_SELECT, - "option": "{{ preset_mode }}", + "set_preset_mode": [ + { + "service": "input_select.select_option", + "data_template": { + "entity_id": _PRESET_MODE_INPUT_SELECT, + "option": "{{ preset_mode }}", + }, }, - }, - "set_percentage": { - "service": "input_number.set_value", - "data_template": { - "entity_id": _PERCENTAGE_INPUT_NUMBER, - "value": "{{ percentage }}", + { + "service": "test.automation", + "data_template": { + "action": "set_preset_mode", + "caller": "{{ this.entity_id }}", + "option": "{{ preset_mode }}", + }, }, - }, - "set_oscillating": { - "service": "input_select.select_option", - "data_template": { - "entity_id": _OSC_INPUT, - "option": "{{ oscillating }}", + ], + "set_percentage": [ + { + "service": "input_number.set_value", + "data_template": { + "entity_id": _PERCENTAGE_INPUT_NUMBER, + "value": "{{ percentage }}", + }, }, - }, - "set_direction": { - "service": "input_select.select_option", - "data_template": { - "entity_id": _DIRECTION_INPUT_SELECT, - "option": "{{ direction }}", + { + "service": "test.automation", + "data_template": { + "action": "set_value", + "caller": "{{ this.entity_id }}", + "value": "{{ percentage }}", + }, }, - }, + ], + "set_oscillating": [ + { + "service": "input_select.select_option", + "data_template": { + "entity_id": _OSC_INPUT, + "option": "{{ oscillating }}", + }, + }, + { + "service": "test.automation", + "data_template": { + "action": "set_oscillating", + "caller": "{{ this.entity_id }}", + "option": "{{ oscillating }}", + }, + }, + ], + "set_direction": [ + { + "service": "input_select.select_option", + "data_template": { + "entity_id": _DIRECTION_INPUT_SELECT, + "option": "{{ direction }}", + }, + }, + { + "service": "test.automation", + "data_template": { + "action": "set_direction", + "caller": "{{ this.entity_id }}", + "option": "{{ direction }}", + }, + }, + ], } if preset_modes: From 83080dbba8c87f43871d1c8f77166588c56f0663 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:34:00 +0200 Subject: [PATCH 424/930] Support this variable in template cover actions (#71793) --- homeassistant/components/template/cover.py | 42 ++++--- tests/components/template/test_cover.py | 134 +++++++++++---------- 2 files changed, 95 insertions(+), 81 deletions(-) diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index 7a20398d0b7..82c5cc2578c 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -323,10 +323,12 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_open_cover(self, **kwargs): """Move the cover up.""" if self._open_script: - await self._open_script.async_run(context=self._context) + await self.async_run_script(self._open_script, context=self._context) elif self._position_script: - await self._position_script.async_run( - {"position": 100}, context=self._context + await self.async_run_script( + self._position_script, + run_variables={"position": 100}, + context=self._context, ) if self._optimistic: self._position = 100 @@ -335,10 +337,12 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_close_cover(self, **kwargs): """Move the cover down.""" if self._close_script: - await self._close_script.async_run(context=self._context) + await self.async_run_script(self._close_script, context=self._context) elif self._position_script: - await self._position_script.async_run( - {"position": 0}, context=self._context + await self.async_run_script( + self._position_script, + run_variables={"position": 0}, + context=self._context, ) if self._optimistic: self._position = 0 @@ -347,13 +351,15 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_stop_cover(self, **kwargs): """Fire the stop action.""" if self._stop_script: - await self._stop_script.async_run(context=self._context) + await self.async_run_script(self._stop_script, context=self._context) async def async_set_cover_position(self, **kwargs): """Set cover position.""" self._position = kwargs[ATTR_POSITION] - await self._position_script.async_run( - {"position": self._position}, context=self._context + await self.async_run_script( + self._position_script, + run_variables={"position": self._position}, + context=self._context, ) if self._optimistic: self.async_write_ha_state() @@ -361,8 +367,10 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_open_cover_tilt(self, **kwargs): """Tilt the cover open.""" self._tilt_value = 100 - await self._tilt_script.async_run( - {"tilt": self._tilt_value}, context=self._context + await self.async_run_script( + self._tilt_script, + run_variables={"tilt": self._tilt_value}, + context=self._context, ) if self._tilt_optimistic: self.async_write_ha_state() @@ -370,8 +378,10 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_close_cover_tilt(self, **kwargs): """Tilt the cover closed.""" self._tilt_value = 0 - await self._tilt_script.async_run( - {"tilt": self._tilt_value}, context=self._context + await self.async_run_script( + self._tilt_script, + run_variables={"tilt": self._tilt_value}, + context=self._context, ) if self._tilt_optimistic: self.async_write_ha_state() @@ -379,8 +389,10 @@ class CoverTemplate(TemplateEntity, CoverEntity): async def async_set_cover_tilt_position(self, **kwargs): """Move the cover tilt to a specific position.""" self._tilt_value = kwargs[ATTR_TILT_POSITION] - await self._tilt_script.async_run( - {"tilt": self._tilt_value}, context=self._context + await self.async_run_script( + self._tilt_script, + run_variables={"tilt": self._tilt_value}, + context=self._context, ) if self._tilt_optimistic: self.async_write_ha_state() diff --git a/tests/components/template/test_cover.py b/tests/components/template/test_cover.py index 18db5482141..d08e8587703 100644 --- a/tests/components/template/test_cover.py +++ b/tests/components/template/test_cover.py @@ -4,10 +4,7 @@ import pytest from homeassistant import setup from homeassistant.components.cover import ATTR_POSITION, ATTR_TILT_POSITION, DOMAIN from homeassistant.const import ( - ATTR_DOMAIN, ATTR_ENTITY_ID, - ATTR_SERVICE_DATA, - EVENT_CALL_SERVICE, SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, @@ -25,40 +22,26 @@ from homeassistant.const import ( STATE_OPENING, STATE_UNAVAILABLE, ) -from homeassistant.core import callback from tests.common import assert_setup_component ENTITY_COVER = "cover.test_template_cover" -@pytest.fixture -def service_calls(hass): - """Track service call events for cover.test_state.""" - events = [] - entity_id = "cover.test_state" - - @callback - def capture_events(event): - if event.data[ATTR_DOMAIN] != DOMAIN: - return - if event.data[ATTR_SERVICE_DATA][ATTR_ENTITY_ID] != [entity_id]: - return - events.append(event) - - hass.bus.async_listen(EVENT_CALL_SERVICE, capture_events) - - return events - - OPEN_CLOSE_COVER_CONFIG = { "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", + "service": "test.automation", + "data_template": { + "action": "open_cover", + "caller": "{{ this.entity_id }}", + }, }, "close_cover": { - "service": "cover.close_cover", - "entity_id": "cover.test_state", + "service": "test.automation", + "data_template": { + "action": "close_cover", + "caller": "{{ this.entity_id }}", + }, }, } @@ -299,8 +282,11 @@ async def test_template_out_of_bounds(hass, start_ha): "test_template_cover": { "value_template": "{{ 1 == 1 }}", "open_cover": { - "service": "cover.open_cover", - "entity_id": "cover.test_state", + "service": "test.automation", + "data_template": { + "action": "open_cover", + "caller": "{{ this.entity_id }}", + }, }, } }, @@ -331,7 +317,7 @@ async def test_template_open_or_position(hass, start_ha, caplog_setup_text): }, ], ) -async def test_open_action(hass, start_ha, service_calls): +async def test_open_action(hass, start_ha, calls): """Test the open_cover command.""" state = hass.states.get("cover.test_template_cover") assert state.state == STATE_CLOSED @@ -341,8 +327,9 @@ async def test_open_action(hass, start_ha, service_calls): ) await hass.async_block_till_done() - assert len(service_calls) == 1 - assert service_calls[0].data["service"] == "open_cover" + assert len(calls) == 1 + assert calls[0].data["action"] == "open_cover" + assert calls[0].data["caller"] == "cover.test_template_cover" @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @@ -357,8 +344,11 @@ async def test_open_action(hass, start_ha, service_calls): **OPEN_CLOSE_COVER_CONFIG, "position_template": "{{ 100 }}", "stop_cover": { - "service": "cover.stop_cover", - "entity_id": "cover.test_state", + "service": "test.automation", + "data_template": { + "action": "stop_cover", + "caller": "{{ this.entity_id }}", + }, }, } }, @@ -366,7 +356,7 @@ async def test_open_action(hass, start_ha, service_calls): }, ], ) -async def test_close_stop_action(hass, start_ha, service_calls): +async def test_close_stop_action(hass, start_ha, calls): """Test the close-cover and stop_cover commands.""" state = hass.states.get("cover.test_template_cover") assert state.state == STATE_OPEN @@ -381,9 +371,11 @@ async def test_close_stop_action(hass, start_ha, service_calls): ) await hass.async_block_till_done() - assert len(service_calls) == 2 - assert service_calls[0].data["service"] == "close_cover" - assert service_calls[1].data["service"] == "stop_cover" + assert len(calls) == 2 + assert calls[0].data["action"] == "close_cover" + assert calls[0].data["caller"] == "cover.test_template_cover" + assert calls[1].data["action"] == "stop_cover" + assert calls[1].data["caller"] == "cover.test_template_cover" @pytest.mark.parametrize("count,domain", [(1, "input_number")]) @@ -393,7 +385,7 @@ async def test_close_stop_action(hass, start_ha, service_calls): {"input_number": {"test": {"min": "0", "max": "100", "initial": "42"}}}, ], ) -async def test_set_position(hass, start_ha, service_calls): +async def test_set_position(hass, start_ha, calls): """Test the set_position command.""" with assert_setup_component(1, "cover"): assert await setup.async_setup_component( @@ -405,9 +397,12 @@ async def test_set_position(hass, start_ha, service_calls): "covers": { "test_template_cover": { "set_cover_position": { - "service": "cover.set_cover_position", - "entity_id": "cover.test_state", - "data_template": {"position": "{{ position }}"}, + "service": "test.automation", + "data_template": { + "action": "set_cover_position", + "caller": "{{ this.entity_id }}", + "position": "{{ position }}", + }, }, } }, @@ -430,9 +425,10 @@ async def test_set_position(hass, start_ha, service_calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 100.0 - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "set_cover_position" - assert service_calls[-1].data["service_data"]["position"] == 100 + assert len(calls) == 1 + assert calls[-1].data["action"] == "set_cover_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["position"] == 100 await hass.services.async_call( DOMAIN, SERVICE_CLOSE_COVER, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -440,9 +436,10 @@ async def test_set_position(hass, start_ha, service_calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 0.0 - assert len(service_calls) == 2 - assert service_calls[-1].data["service"] == "set_cover_position" - assert service_calls[-1].data["service_data"]["position"] == 0 + assert len(calls) == 2 + assert calls[-1].data["action"] == "set_cover_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["position"] == 0 await hass.services.async_call( DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -450,9 +447,10 @@ async def test_set_position(hass, start_ha, service_calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 100.0 - assert len(service_calls) == 3 - assert service_calls[-1].data["service"] == "set_cover_position" - assert service_calls[-1].data["service_data"]["position"] == 100 + assert len(calls) == 3 + assert calls[-1].data["action"] == "set_cover_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["position"] == 100 await hass.services.async_call( DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: ENTITY_COVER}, blocking=True @@ -460,9 +458,10 @@ async def test_set_position(hass, start_ha, service_calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 0.0 - assert len(service_calls) == 4 - assert service_calls[-1].data["service"] == "set_cover_position" - assert service_calls[-1].data["service_data"]["position"] == 0 + assert len(calls) == 4 + assert calls[-1].data["action"] == "set_cover_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["position"] == 0 await hass.services.async_call( DOMAIN, @@ -473,9 +472,10 @@ async def test_set_position(hass, start_ha, service_calls): await hass.async_block_till_done() state = hass.states.get("cover.test_template_cover") assert state.attributes.get("current_position") == 25.0 - assert len(service_calls) == 5 - assert service_calls[-1].data["service"] == "set_cover_position" - assert service_calls[-1].data["service_data"]["position"] == 25 + assert len(calls) == 5 + assert calls[-1].data["action"] == "set_cover_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["position"] == 25 @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) @@ -489,9 +489,12 @@ async def test_set_position(hass, start_ha, service_calls): "test_template_cover": { **OPEN_CLOSE_COVER_CONFIG, "set_cover_tilt_position": { - "service": "cover.set_cover_tilt_position", - "entity_id": "cover.test_state", - "data_template": {"tilt_position": "{{ tilt }}"}, + "service": "test.automation", + "data_template": { + "action": "set_cover_tilt_position", + "caller": "{{ this.entity_id }}", + "tilt_position": "{{ tilt }}", + }, }, } }, @@ -511,9 +514,7 @@ async def test_set_position(hass, start_ha, service_calls): (SERVICE_CLOSE_COVER_TILT, {ATTR_ENTITY_ID: ENTITY_COVER}, 0), ], ) -async def test_set_tilt_position( - hass, service, attr, start_ha, service_calls, tilt_position -): +async def test_set_tilt_position(hass, service, attr, start_ha, calls, tilt_position): """Test the set_tilt_position command.""" await hass.services.async_call( DOMAIN, @@ -523,9 +524,10 @@ async def test_set_tilt_position( ) await hass.async_block_till_done() - assert len(service_calls) == 1 - assert service_calls[-1].data["service"] == "set_cover_tilt_position" - assert service_calls[-1].data["service_data"]["tilt_position"] == tilt_position + assert len(calls) == 1 + assert calls[-1].data["action"] == "set_cover_tilt_position" + assert calls[-1].data["caller"] == "cover.test_template_cover" + assert calls[-1].data["tilt_position"] == tilt_position @pytest.mark.parametrize("count,domain", [(1, DOMAIN)]) From 4885331509eeffe50f42d76b234996467b06170f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 18:46:49 +0200 Subject: [PATCH 425/930] Fail template functions when no default specified (#71687) --- homeassistant/helpers/template.py | 90 +++---- .../fixtures/broken_configuration.yaml | 4 +- .../fixtures/sensor_configuration.yaml | 4 +- tests/helpers/test_event.py | 2 +- tests/helpers/test_template.py | 226 ++++++++++++++---- 5 files changed, 213 insertions(+), 113 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 3f084674a1b..d1ed9d06db9 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1325,21 +1325,12 @@ def utcnow(hass: HomeAssistant) -> datetime: return dt_util.utcnow() -def warn_no_default(function, value, default): +def raise_no_default(function, value): """Log warning if no default is specified.""" template, action = template_cv.get() or ("", "rendering or compiling") - _LOGGER.warning( - ( - "Template warning: '%s' got invalid input '%s' when %s template '%s' " - "but no default was specified. Currently '%s' will return '%s', however this template will fail " - "to render in Home Assistant core 2022.6" - ), - function, - value, - action, - template, - function, - default, + raise ValueError( + f"Template error: {function} got invalid input '{value}' when {action} template '{template}' " + "but no default was specified" ) @@ -1361,8 +1352,7 @@ def forgiving_round(value, precision=0, method="common", default=_SENTINEL): except (ValueError, TypeError): # If value can't be converted to float if default is _SENTINEL: - warn_no_default("round", value, value) - return value + raise_no_default("round", value) return default @@ -1373,20 +1363,25 @@ def multiply(value, amount, default=_SENTINEL): except (ValueError, TypeError): # If value can't be converted to float if default is _SENTINEL: - warn_no_default("multiply", value, value) - return value + raise_no_default("multiply", value) return default def logarithm(value, base=math.e, default=_SENTINEL): """Filter and function to get logarithm of the value with a specific base.""" try: - return math.log(float(value), float(base)) + value_float = float(value) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("log", value, value) - return value + raise_no_default("log", value) return default + try: + base_float = float(base) + except (ValueError, TypeError): + if default is _SENTINEL: + raise_no_default("log", base) + return default + return math.log(value_float, base_float) def sine(value, default=_SENTINEL): @@ -1395,8 +1390,7 @@ def sine(value, default=_SENTINEL): return math.sin(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("sin", value, value) - return value + raise_no_default("sin", value) return default @@ -1406,8 +1400,7 @@ def cosine(value, default=_SENTINEL): return math.cos(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("cos", value, value) - return value + raise_no_default("cos", value) return default @@ -1417,8 +1410,7 @@ def tangent(value, default=_SENTINEL): return math.tan(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("tan", value, value) - return value + raise_no_default("tan", value) return default @@ -1428,8 +1420,7 @@ def arc_sine(value, default=_SENTINEL): return math.asin(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("asin", value, value) - return value + raise_no_default("asin", value) return default @@ -1439,8 +1430,7 @@ def arc_cosine(value, default=_SENTINEL): return math.acos(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("acos", value, value) - return value + raise_no_default("acos", value) return default @@ -1450,8 +1440,7 @@ def arc_tangent(value, default=_SENTINEL): return math.atan(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("atan", value, value) - return value + raise_no_default("atan", value) return default @@ -1474,8 +1463,7 @@ def arc_tangent2(*args, default=_SENTINEL): return math.atan2(float(args[0]), float(args[1])) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("atan2", args, args) - return args + raise_no_default("atan2", args) return default @@ -1485,8 +1473,7 @@ def square_root(value, default=_SENTINEL): return math.sqrt(float(value)) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("sqrt", value, value) - return value + raise_no_default("sqrt", value) return default @@ -1502,8 +1489,7 @@ def timestamp_custom(value, date_format=DATE_STR_FORMAT, local=True, default=_SE except (ValueError, TypeError): # If timestamp can't be converted if default is _SENTINEL: - warn_no_default("timestamp_custom", value, value) - return value + raise_no_default("timestamp_custom", value) return default @@ -1514,8 +1500,7 @@ def timestamp_local(value, default=_SENTINEL): except (ValueError, TypeError): # If timestamp can't be converted if default is _SENTINEL: - warn_no_default("timestamp_local", value, value) - return value + raise_no_default("timestamp_local", value) return default @@ -1526,8 +1511,7 @@ def timestamp_utc(value, default=_SENTINEL): except (ValueError, TypeError): # If timestamp can't be converted if default is _SENTINEL: - warn_no_default("timestamp_utc", value, value) - return value + raise_no_default("timestamp_utc", value) return default @@ -1537,8 +1521,7 @@ def forgiving_as_timestamp(value, default=_SENTINEL): return dt_util.as_timestamp(value) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("as_timestamp", value, None) - return None + raise_no_default("as_timestamp", value) return default @@ -1558,8 +1541,7 @@ def strptime(string, fmt, default=_SENTINEL): return datetime.strptime(string, fmt) except (ValueError, AttributeError, TypeError): if default is _SENTINEL: - warn_no_default("strptime", string, string) - return string + raise_no_default("strptime", string) return default @@ -1618,8 +1600,7 @@ def forgiving_float(value, default=_SENTINEL): return float(value) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("float", value, value) - return value + raise_no_default("float", value) return default @@ -1629,26 +1610,23 @@ def forgiving_float_filter(value, default=_SENTINEL): return float(value) except (ValueError, TypeError): if default is _SENTINEL: - warn_no_default("float", value, 0) - return 0 + raise_no_default("float", value) return default def forgiving_int(value, default=_SENTINEL, base=10): - """Try to convert value to an int, and warn if it fails.""" + """Try to convert value to an int, and raise if it fails.""" result = jinja2.filters.do_int(value, default=default, base=base) if result is _SENTINEL: - warn_no_default("int", value, value) - return value + raise_no_default("int", value) return result def forgiving_int_filter(value, default=_SENTINEL, base=10): - """Try to convert value to an int, and warn if it fails.""" + """Try to convert value to an int, and raise if it fails.""" result = jinja2.filters.do_int(value, default=default, base=base) if result is _SENTINEL: - warn_no_default("int", value, 0) - return 0 + raise_no_default("int", value) return result diff --git a/tests/components/template/fixtures/broken_configuration.yaml b/tests/components/template/fixtures/broken_configuration.yaml index de0c8aebd9d..00f773e2fd3 100644 --- a/tests/components/template/fixtures/broken_configuration.yaml +++ b/tests/components/template/fixtures/broken_configuration.yaml @@ -7,8 +7,8 @@ sensor: friendly_name: Combined Sense Energy Usage unit_of_measurement: kW value_template: - "{{ ((states('sensor.energy_usage') | float) + (states('sensor.energy_usage_2') - | float)) / 1000 }}" + "{{ ((states('sensor.energy_usage') | float(default=0)) + (states('sensor.energy_usage_2') + | float(default=0))) / 1000 }}" watching_tv_in_master_bedroom: friendly_name: Watching TV in Master Bedroom value_template: diff --git a/tests/components/template/fixtures/sensor_configuration.yaml b/tests/components/template/fixtures/sensor_configuration.yaml index 02c8cc373f4..f1afef26b87 100644 --- a/tests/components/template/fixtures/sensor_configuration.yaml +++ b/tests/components/template/fixtures/sensor_configuration.yaml @@ -14,8 +14,8 @@ sensor: friendly_name: Combined Sense Energy Usage unit_of_measurement: kW value_template: - "{{ ((states('sensor.energy_usage') | float) + (states('sensor.energy_usage_2') - | float)) / 1000 }}" + "{{ ((states('sensor.energy_usage') | float(default=0)) + (states('sensor.energy_usage_2') + | float(default=0))) / 1000 }}" watching_tv_in_master_bedroom: friendly_name: Watching TV in Master Bedroom value_template: diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 9b0a3e1abd5..d0355bba5a8 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -1026,7 +1026,7 @@ async def test_track_template_result_none(hass): template_condition = Template("{{state_attr('sensor.test', 'battery')}}", hass) template_condition_var = Template( - "{{(state_attr('sensor.test', 'battery')|int) + test }}", hass + "{{(state_attr('sensor.test', 'battery')|int(default=0)) + test }}", hass ) def specific_run_callback(event, updates): diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index d3a63460ec1..b2448bc1b5c 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -233,11 +233,11 @@ def test_float_function(hass): is True ) - assert ( + # Test handling of invalid input + with pytest.raises(TemplateError): template.Template("{{ float('forgiving') }}", hass).async_render() - == "forgiving" - ) + # Test handling of default return value assert render(hass, "{{ float('bad', 1) }}") == 1 assert render(hass, "{{ float('bad', default=1) }}") == 1 @@ -248,7 +248,12 @@ def test_float_filter(hass): assert render(hass, "{{ states.sensor.temperature.state | float }}") == 12.0 assert render(hass, "{{ states.sensor.temperature.state | float > 11 }}") is True - assert render(hass, "{{ 'bad' | float }}") == 0 + + # Test handling of invalid input + with pytest.raises(TemplateError): + render(hass, "{{ 'bad' | float }}") + + # Test handling of default return value assert render(hass, "{{ 'bad' | float(1) }}") == 1 assert render(hass, "{{ 'bad' | float(default=1) }}") == 1 @@ -262,7 +267,11 @@ def test_int_filter(hass): hass.states.async_set("sensor.temperature", "0x10") assert render(hass, "{{ states.sensor.temperature.state | int(base=16) }}") == 16 - assert render(hass, "{{ 'bad' | int }}") == 0 + # Test handling of invalid input + with pytest.raises(TemplateError): + render(hass, "{{ 'bad' | int }}") + + # Test handling of default return value assert render(hass, "{{ 'bad' | int(1) }}") == 1 assert render(hass, "{{ 'bad' | int(default=1) }}") == 1 @@ -276,7 +285,11 @@ def test_int_function(hass): hass.states.async_set("sensor.temperature", "0x10") assert render(hass, "{{ int(states.sensor.temperature.state, base=16) }}") == 16 - assert render(hass, "{{ int('bad') }}") == "bad" + # Test handling of invalid input + with pytest.raises(TemplateError): + render(hass, "{{ int('bad') }}") + + # Test handling of default return value assert render(hass, "{{ int('bad', 1) }}") == 1 assert render(hass, "{{ int('bad', default=1) }}") == 1 @@ -364,12 +377,12 @@ def test_rounding_value(hass): def test_rounding_value_on_error(hass): """Test rounding value handling of error.""" - assert template.Template("{{ None | round }}", hass).async_render() is None + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ None | round }}", hass).async_render() - assert ( + with pytest.raises(TemplateError): template.Template('{{ "no_number" | round }}', hass).async_render() - == "no_number" - ) # Test handling of default return value assert render(hass, "{{ 'no_number' | round(default=1) }}") == 1 @@ -377,7 +390,7 @@ def test_rounding_value_on_error(hass): def test_multiply(hass): """Test multiply.""" - tests = {None: None, 10: 100, '"abcd"': "abcd"} + tests = {10: 100} for inp, out in tests.items(): assert ( @@ -387,6 +400,10 @@ def test_multiply(hass): == out ) + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ abcd | multiply(10) }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | multiply(10, 1) }}") == 1 assert render(hass, "{{ 'no_number' | multiply(10, default=1) }}") == 1 @@ -397,9 +414,7 @@ def test_logarithm(hass): tests = [ (4, 2, 2.0), (1000, 10, 3.0), - (math.e, "", 1.0), - ('"invalid"', "_", "invalid"), - (10, '"invalid"', 10.0), + (math.e, "", 1.0), # The "" means the default base (e) will be used ] for value, base, expected in tests: @@ -417,6 +432,16 @@ def test_logarithm(hass): == expected ) + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ invalid | log(_) }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ log(invalid, _) }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ 10 | log(invalid) }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ log(10, invalid) }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | log(10, 1) }}") == 1 assert render(hass, "{{ 'no_number' | log(10, default=1) }}") == 1 @@ -432,7 +457,6 @@ def test_sine(hass): (math.pi, 0.0), (math.pi * 1.5, -1.0), (math.pi / 10, 0.309), - ('"duck"', "duck"), ] for value, expected in tests: @@ -442,6 +466,12 @@ def test_sine(hass): ) assert render(hass, f"{{{{ sin({value}) | round(3) }}}}") == expected + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ 'duck' | sin }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ invalid | sin('duck') }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | sin(1) }}") == 1 assert render(hass, "{{ 'no_number' | sin(default=1) }}") == 1 @@ -457,7 +487,6 @@ def test_cos(hass): (math.pi, -1.0), (math.pi * 1.5, -0.0), (math.pi / 10, 0.951), - ("'error'", "error"), ] for value, expected in tests: @@ -467,11 +496,17 @@ def test_cos(hass): ) assert render(hass, f"{{{{ cos({value}) | round(3) }}}}") == expected + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ 'error' | cos }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ invalid | cos('error') }}", hass).async_render() + # Test handling of default return value - assert render(hass, "{{ 'no_number' | sin(1) }}") == 1 - assert render(hass, "{{ 'no_number' | sin(default=1) }}") == 1 - assert render(hass, "{{ sin('no_number', 1) }}") == 1 - assert render(hass, "{{ sin('no_number', default=1) }}") == 1 + assert render(hass, "{{ 'no_number' | cos(1) }}") == 1 + assert render(hass, "{{ 'no_number' | cos(default=1) }}") == 1 + assert render(hass, "{{ cos('no_number', 1) }}") == 1 + assert render(hass, "{{ cos('no_number', default=1) }}") == 1 def test_tan(hass): @@ -482,7 +517,6 @@ def test_tan(hass): (math.pi / 180 * 45, 1.0), (math.pi / 180 * 90, "1.633123935319537e+16"), (math.pi / 180 * 135, -1.0), - ("'error'", "error"), ] for value, expected in tests: @@ -492,6 +526,12 @@ def test_tan(hass): ) assert render(hass, f"{{{{ tan({value}) | round(3) }}}}") == expected + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ 'error' | tan }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ invalid | tan('error') }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | tan(1) }}") == 1 assert render(hass, "{{ 'no_number' | tan(default=1) }}") == 1 @@ -507,7 +547,6 @@ def test_sqrt(hass): (2, 1.414), (10, 3.162), (100, 10.0), - ("'error'", "error"), ] for value, expected in tests: @@ -517,6 +556,12 @@ def test_sqrt(hass): ) assert render(hass, f"{{{{ sqrt({value}) | round(3) }}}}") == expected + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ 'error' | sqrt }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ invalid | sqrt('error') }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | sqrt(1) }}") == 1 assert render(hass, "{{ 'no_number' | sqrt(default=1) }}") == 1 @@ -527,14 +572,11 @@ def test_sqrt(hass): def test_arc_sine(hass): """Test arcus sine.""" tests = [ - (-2.0, -2.0), # value error (-1.0, -1.571), (-0.5, -0.524), (0.0, 0.0), (0.5, 0.524), (1.0, 1.571), - (2.0, 2.0), # value error - ('"error"', "error"), ] for value, expected in tests: @@ -544,6 +586,19 @@ def test_arc_sine(hass): ) assert render(hass, f"{{{{ asin({value}) | round(3) }}}}") == expected + # Test handling of invalid input + invalid_tests = [ + -2.0, # value error + 2.0, # value error + '"error"', + ] + + for value in invalid_tests: + with pytest.raises(TemplateError): + template.Template("{{ %s | asin | round(3) }}" % value, hass).async_render() + with pytest.raises(TemplateError): + assert render(hass, f"{{{{ asin({value}) | round(3) }}}}") + # Test handling of default return value assert render(hass, "{{ 'no_number' | asin(1) }}") == 1 assert render(hass, "{{ 'no_number' | asin(default=1) }}") == 1 @@ -554,14 +609,11 @@ def test_arc_sine(hass): def test_arc_cos(hass): """Test arcus cosine.""" tests = [ - (-2.0, -2.0), # value error (-1.0, 3.142), (-0.5, 2.094), (0.0, 1.571), (0.5, 1.047), (1.0, 0.0), - (2.0, 2.0), # value error - ('"error"', "error"), ] for value, expected in tests: @@ -571,6 +623,19 @@ def test_arc_cos(hass): ) assert render(hass, f"{{{{ acos({value}) | round(3) }}}}") == expected + # Test handling of invalid input + invalid_tests = [ + -2.0, # value error + 2.0, # value error + '"error"', + ] + + for value in invalid_tests: + with pytest.raises(TemplateError): + template.Template("{{ %s | acos | round(3) }}" % value, hass).async_render() + with pytest.raises(TemplateError): + assert render(hass, f"{{{{ acos({value}) | round(3) }}}}") + # Test handling of default return value assert render(hass, "{{ 'no_number' | acos(1) }}") == 1 assert render(hass, "{{ 'no_number' | acos(default=1) }}") == 1 @@ -590,7 +655,6 @@ def test_arc_tan(hass): (1.0, 0.785), (2.0, 1.107), (10.0, 1.471), - ('"error"', "error"), ] for value, expected in tests: @@ -600,6 +664,12 @@ def test_arc_tan(hass): ) assert render(hass, f"{{{{ atan({value}) | round(3) }}}}") == expected + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ 'error' | atan }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ invalid | atan('error') }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ 'no_number' | atan(1) }}") == 1 assert render(hass, "{{ 'no_number' | atan(default=1) }}") == 1 @@ -622,7 +692,6 @@ def test_arc_tan2(hass): (-4.0, 3.0, -0.927), (-1.0, 2.0, -0.464), (2.0, 1.0, 1.107), - ('"duck"', '"goose"', ("duck", "goose")), ] for y, x, expected in tests: @@ -639,6 +708,12 @@ def test_arc_tan2(hass): == expected ) + # Test handling of invalid input + with pytest.raises(TemplateError): + template.Template("{{ ('duck', 'goose') | atan2 }}", hass).async_render() + with pytest.raises(TemplateError): + template.Template("{{ atan2('duck', 'goose') }}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ ('duck', 'goose') | atan2(1) }}") == 1 assert render(hass, "{{ ('duck', 'goose') | atan2(default=1) }}") == 1 @@ -655,8 +730,6 @@ def test_strptime(hass): ("2016-10-19", "%Y-%m-%d", None), ("2016", "%Y", None), ("15:22:05", "%H:%M:%S", None), - ("1469119144", "%Y", 1469119144), - ("invalid", "%Y", "invalid"), ] for inp, fmt, expected in tests: @@ -667,6 +740,18 @@ def test_strptime(hass): assert template.Template(temp, hass).async_render() == expected + # Test handling of invalid input + invalid_tests = [ + ("1469119144", "%Y"), + ("invalid", "%Y"), + ] + + for inp, fmt in invalid_tests: + temp = f"{{{{ strptime('{inp}', '{fmt}') }}}}" + + with pytest.raises(TemplateError): + template.Template(temp, hass).async_render() + # Test handling of default return value assert render(hass, "{{ strptime('invalid', '%Y', 1) }}") == 1 assert render(hass, "{{ strptime('invalid', '%Y', default=1) }}") == 1 @@ -677,7 +762,6 @@ def test_timestamp_custom(hass): hass.config.set_time_zone("UTC") now = dt_util.utcnow() tests = [ - (None, None, None, None), (1469119144, None, True, "2016-07-21 16:39:04"), (1469119144, "%Y", True, 2016), (1469119144, "invalid", True, "invalid"), @@ -694,6 +778,22 @@ def test_timestamp_custom(hass): assert template.Template(f"{{{{ {inp} | {fil} }}}}", hass).async_render() == out + # Test handling of invalid input + invalid_tests = [ + (None, None, None), + ] + + for inp, fmt, local in invalid_tests: + if fmt: + fil = f"timestamp_custom('{fmt}')" + elif fmt and local: + fil = f"timestamp_custom('{fmt}', {local})" + else: + fil = "timestamp_custom" + + with pytest.raises(TemplateError): + template.Template(f"{{{{ {inp} | {fil} }}}}", hass).async_render() + # Test handling of default return value assert render(hass, "{{ None | timestamp_custom('invalid', True, 1) }}") == 1 assert render(hass, "{{ None | timestamp_custom(default=1) }}") == 1 @@ -702,14 +802,25 @@ def test_timestamp_custom(hass): def test_timestamp_local(hass): """Test the timestamps to local filter.""" hass.config.set_time_zone("UTC") - tests = {None: None, 1469119144: "2016-07-21T16:39:04+00:00"} + tests = [ + (1469119144, "2016-07-21T16:39:04+00:00"), + ] - for inp, out in tests.items(): + for inp, out in tests: assert ( template.Template("{{ %s | timestamp_local }}" % inp, hass).async_render() == out ) + # Test handling of invalid input + invalid_tests = [ + None, + ] + + for inp in invalid_tests: + with pytest.raises(TemplateError): + template.Template("{{ %s | timestamp_local }}" % inp, hass).async_render() + # Test handling of default return value assert render(hass, "{{ None | timestamp_local(1) }}") == 1 assert render(hass, "{{ None | timestamp_local(default=1) }}") == 1 @@ -1002,18 +1113,26 @@ def test_ordinal(hass): def test_timestamp_utc(hass): """Test the timestamps to local filter.""" now = dt_util.utcnow() - tests = { - None: None, - 1469119144: "2016-07-21T16:39:04+00:00", - dt_util.as_timestamp(now): now.isoformat(), - } + tests = [ + (1469119144, "2016-07-21T16:39:04+00:00"), + (dt_util.as_timestamp(now), now.isoformat()), + ] - for inp, out in tests.items(): + for inp, out in tests: assert ( template.Template("{{ %s | timestamp_utc }}" % inp, hass).async_render() == out ) + # Test handling of invalid input + invalid_tests = [ + None, + ] + + for inp in invalid_tests: + with pytest.raises(TemplateError): + template.Template("{{ %s | timestamp_utc }}" % inp, hass).async_render() + # Test handling of default return value assert render(hass, "{{ None | timestamp_utc(1) }}") == 1 assert render(hass, "{{ None | timestamp_utc(default=1) }}") == 1 @@ -1021,14 +1140,12 @@ def test_timestamp_utc(hass): def test_as_timestamp(hass): """Test the as_timestamp function.""" - assert ( - template.Template('{{ as_timestamp("invalid") }}', hass).async_render() is None - ) - hass.mock = None - assert ( - template.Template("{{ as_timestamp(states.mock) }}", hass).async_render() - is None - ) + with pytest.raises(TemplateError): + template.Template('{{ as_timestamp("invalid") }}', hass).async_render() + + hass.states.async_set("test.object", None) + with pytest.raises(TemplateError): + template.Template("{{ as_timestamp(states.test.object) }}", hass).async_render() tpl = ( '{{ as_timestamp(strptime("2024-02-03T09:10:24+0000", ' @@ -1822,7 +1939,8 @@ def test_distance_function_return_none_if_invalid_state(hass): """Test distance function return None if invalid state.""" hass.states.async_set("test.object_2", "happy", {"latitude": 10}) tpl = template.Template("{{ distance(states.test.object_2) | round }}", hass) - assert tpl.async_render() is None + with pytest.raises(TemplateError): + tpl.async_render() def test_distance_function_return_none_if_invalid_coord(hass): @@ -3359,7 +3477,11 @@ async def test_protected_blocked(hass): async def test_demo_template(hass): """Test the demo template works as expected.""" - hass.states.async_set("sun.sun", "above", {"elevation": 50, "next_rising": "later"}) + hass.states.async_set( + "sun.sun", + "above", + {"elevation": 50, "next_rising": "2022-05-12T03:00:08.503651+00:00"}, + ) for i in range(2): hass.states.async_set(f"sensor.sensor{i}", "on") @@ -3375,7 +3497,7 @@ The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}. {% if is_state("sun.sun", "above_horizon") -%} The sun rose {{ relative_time(states.sun.sun.last_changed) }} ago. {%- else -%} - The sun will rise at {{ as_timestamp(strptime(state_attr("sun.sun", "next_rising"), "")) | timestamp_local }}. + The sun will rise at {{ as_timestamp(state_attr("sun.sun", "next_rising")) | timestamp_local }}. {%- endif %} For loop example getting 3 entity values: From 9bd508c0bfc00405cb6c4e17f3378f8edce112ec Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 13:17:54 -0400 Subject: [PATCH 426/930] Generate json for history and logbook websocket responses in the executor (#71813) --- homeassistant/components/history/__init__.py | 129 ++++++++++++++----- homeassistant/components/logbook/__init__.py | 55 ++++++-- 2 files changed, 138 insertions(+), 46 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index a0d1f2fa76b..17b983e11c1 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -1,12 +1,12 @@ """Provide pre-made queries on top of the recorder component.""" from __future__ import annotations -from collections.abc import Iterable, MutableMapping +from collections.abc import Iterable from datetime import datetime as dt, timedelta from http import HTTPStatus import logging import time -from typing import Any, cast +from typing import Any, Literal, cast from aiohttp import web from sqlalchemy import not_, or_ @@ -24,8 +24,10 @@ from homeassistant.components.recorder.statistics import ( statistics_during_period, ) from homeassistant.components.recorder.util import session_scope +from homeassistant.components.websocket_api import messages +from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE -from homeassistant.core import HomeAssistant, State +from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.deprecation import deprecated_class, deprecated_function from homeassistant.helpers.entityfilter import ( @@ -104,6 +106,23 @@ class LazyState(history_models.LazyState): """A lazy version of core State.""" +def _ws_get_statistics_during_period( + hass: HomeAssistant, + msg_id: int, + start_time: dt, + end_time: dt | None = None, + statistic_ids: list[str] | None = None, + period: Literal["5minute", "day", "hour", "month"] = "hour", +) -> str: + """Fetch statistics and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message( + msg_id, + statistics_during_period(hass, start_time, end_time, statistic_ids, period), + ) + ) + + @websocket_api.websocket_command( { vol.Required("type"): "history/statistics_during_period", @@ -136,15 +155,28 @@ async def ws_get_statistics_during_period( else: end_time = None - statistics = await get_instance(hass).async_add_executor_job( - statistics_during_period, - hass, - start_time, - end_time, - msg.get("statistic_ids"), - msg.get("period"), + connection.send_message( + await get_instance(hass).async_add_executor_job( + _ws_get_statistics_during_period, + hass, + msg["id"], + start_time, + end_time, + msg.get("statistic_ids"), + msg.get("period"), + ) + ) + + +def _ws_get_list_statistic_ids( + hass: HomeAssistant, + msg_id: int, + statistic_type: Literal["mean"] | Literal["sum"] | None = None, +) -> str: + """Fetch a list of available statistic_id and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message(msg_id, list_statistic_ids(hass, None, statistic_type)) ) - connection.send_result(msg["id"], statistics) @websocket_api.websocket_command( @@ -158,13 +190,46 @@ async def ws_get_list_statistic_ids( hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict ) -> None: """Fetch a list of available statistic_id.""" - statistic_ids = await get_instance(hass).async_add_executor_job( - list_statistic_ids, - hass, - None, - msg.get("statistic_type"), + connection.send_message( + await get_instance(hass).async_add_executor_job( + _ws_get_list_statistic_ids, + hass, + msg["id"], + msg.get("statistic_type"), + ) + ) + + +def _ws_get_significant_states( + hass: HomeAssistant, + msg_id: int, + start_time: dt, + end_time: dt | None = None, + entity_ids: list[str] | None = None, + filters: Any | None = None, + include_start_time_state: bool = True, + significant_changes_only: bool = True, + minimal_response: bool = False, + no_attributes: bool = False, +) -> str: + """Fetch history significant_states and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message( + msg_id, + history.get_significant_states( + hass, + start_time, + end_time, + entity_ids, + filters, + include_start_time_state, + significant_changes_only, + minimal_response, + no_attributes, + True, + ), + ) ) - connection.send_result(msg["id"], statistic_ids) @websocket_api.websocket_command( @@ -220,24 +285,22 @@ async def ws_get_history_during_period( significant_changes_only = msg["significant_changes_only"] no_attributes = msg["no_attributes"] minimal_response = msg["minimal_response"] - compressed_state_format = True - history_during_period: MutableMapping[ - str, list[State | dict[str, Any]] - ] = await get_instance(hass).async_add_executor_job( - history.get_significant_states, - hass, - start_time, - end_time, - entity_ids, - hass.data[HISTORY_FILTERS], - include_start_time_state, - significant_changes_only, - minimal_response, - no_attributes, - compressed_state_format, + connection.send_message( + await get_instance(hass).async_add_executor_job( + _ws_get_significant_states, + hass, + msg["id"], + start_time, + end_time, + entity_ids, + hass.data[HISTORY_FILTERS], + include_start_time_state, + significant_changes_only, + minimal_response, + no_attributes, + ) ) - connection.send_result(msg["id"], history_during_period) class HistoryPeriodView(HomeAssistantView): diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 592831badf1..4b40e398f6e 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -30,6 +30,8 @@ from homeassistant.components.recorder.models import ( from homeassistant.components.recorder.util import session_scope from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN +from homeassistant.components.websocket_api import messages +from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, @@ -210,6 +212,34 @@ async def _process_logbook_platform( platform.async_describe_events(hass, _async_describe_event) +def _ws_formatted_get_events( + hass: HomeAssistant, + msg_id: int, + start_day: dt, + end_day: dt, + entity_ids: list[str] | None = None, + filters: Filters | None = None, + entities_filter: EntityFilter | Callable[[str], bool] | None = None, + context_id: str | None = None, +) -> str: + """Fetch events and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message( + msg_id, + _get_events( + hass, + start_day, + end_day, + entity_ids, + filters, + entities_filter, + context_id, + True, + ), + ) + ) + + @websocket_api.websocket_command( { vol.Required("type"): "logbook/get_events", @@ -249,20 +279,19 @@ async def ws_get_events( entity_ids = msg.get("entity_ids") context_id = msg.get("context_id") - logbook_events: list[dict[str, Any]] = await get_instance( - hass - ).async_add_executor_job( - _get_events, - hass, - start_time, - end_time, - entity_ids, - hass.data[LOGBOOK_FILTERS], - hass.data[LOGBOOK_ENTITIES_FILTER], - context_id, - True, + connection.send_message( + await get_instance(hass).async_add_executor_job( + _ws_formatted_get_events, + hass, + msg["id"], + start_time, + end_time, + entity_ids, + hass.data[LOGBOOK_FILTERS], + hass.data[LOGBOOK_ENTITIES_FILTER], + context_id, + ) ) - connection.send_result(msg["id"], logbook_events) class LogbookView(HomeAssistantView): From 807df530bc9713099f6eb015255f4591dd45fa74 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Fri, 13 May 2022 18:27:08 +0100 Subject: [PATCH 427/930] Add diagnostics file export to generic camera (#71492) --- .../components/generic/diagnostics.py | 49 +++++++++++++++++++ tests/components/generic/conftest.py | 35 +++++++++++++ tests/components/generic/test_diagnostics.py | 24 +++++++++ 3 files changed, 108 insertions(+) create mode 100644 homeassistant/components/generic/diagnostics.py create mode 100644 tests/components/generic/test_diagnostics.py diff --git a/homeassistant/components/generic/diagnostics.py b/homeassistant/components/generic/diagnostics.py new file mode 100644 index 00000000000..00be287f053 --- /dev/null +++ b/homeassistant/components/generic/diagnostics.py @@ -0,0 +1,49 @@ +"""Diagnostics support for generic (IP camera).""" +from __future__ import annotations + +from typing import Any + +import yarl + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant + +from .const import CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE + +TO_REDACT = { + CONF_PASSWORD, + CONF_USERNAME, +} + + +# A very similar redact function is in components.sql. Possible to be made common. +def redact_url(data: str) -> str: + """Redact credentials from string url.""" + url_in = yarl.URL(data) + if url_in.user: + url = url_in.with_user("****") + if url_in.password: + url = url.with_password("****") + if url_in.path: + url = url.with_path("****") + if url_in.query_string: + url = url.with_query("****=****") + return str(url) + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + options = async_redact_data(entry.options, TO_REDACT) + for key in (CONF_STREAM_SOURCE, CONF_STILL_IMAGE_URL): + if (value := options.get(key)) is not None: + options[key] = redact_url(value) + + return { + "title": entry.title, + "data": async_redact_data(entry.data, TO_REDACT), + "options": options, + } diff --git a/tests/components/generic/conftest.py b/tests/components/generic/conftest.py index 9daa3574e6e..dc5c545869b 100644 --- a/tests/components/generic/conftest.py +++ b/tests/components/generic/conftest.py @@ -10,6 +10,8 @@ import respx from homeassistant import config_entries, setup from homeassistant.components.generic.const import DOMAIN +from tests.common import MockConfigEntry + @pytest.fixture(scope="package") def fakeimgbytes_png(): @@ -79,3 +81,36 @@ async def user_flow(hass): assert result["errors"] == {} return result + + +@pytest.fixture(name="config_entry") +def config_entry_fixture(hass): + """Define a config entry fixture.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Test Camera", + unique_id="abc123", + data={}, + options={ + "still_image_url": "http://joebloggs:letmein1@example.com/secret1/file.jpg?pw=qwerty", + "stream_source": "http://janebloggs:letmein2@example.com/stream", + "username": "johnbloggs", + "password": "letmein123", + "limit_refetch_to_url_change": False, + "authentication": "basic", + "framerate": 2.0, + "verify_ssl": True, + "content_type": "image/jpeg", + }, + version=1, + ) + entry.add_to_hass(hass) + return entry + + +@pytest.fixture +async def setup_entry(hass, config_entry): + """Set up a config entry ready to be used in tests.""" + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + return config_entry diff --git a/tests/components/generic/test_diagnostics.py b/tests/components/generic/test_diagnostics.py new file mode 100644 index 00000000000..2d4e4c536d8 --- /dev/null +++ b/tests/components/generic/test_diagnostics.py @@ -0,0 +1,24 @@ +"""Test generic (IP camera) diagnostics.""" +from homeassistant.components.diagnostics import REDACTED + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, hass_client, setup_entry): + """Test config entry diagnostics.""" + + assert await get_diagnostics_for_config_entry(hass, hass_client, setup_entry) == { + "title": "Test Camera", + "data": {}, + "options": { + "still_image_url": "http://****:****@example.com/****?****=****", + "stream_source": "http://****:****@example.com/****", + "username": REDACTED, + "password": REDACTED, + "limit_refetch_to_url_change": False, + "authentication": "basic", + "framerate": 2.0, + "verify_ssl": True, + "content_type": "image/jpeg", + }, + } From 08ee276277234e0fa155e4392b3c104a51b11942 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 13 May 2022 21:03:21 +0200 Subject: [PATCH 428/930] Add tilt support to Tasmota covers (#71789) * Add tilt support to Tasmota covers * Bump hatasmota to 0.5.0 --- homeassistant/components/tasmota/cover.py | 48 ++++++- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_cover.py | 136 ++++++++++++++---- 5 files changed, 157 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/tasmota/cover.py b/homeassistant/components/tasmota/cover.py index 172e460c29b..4b851a86406 100644 --- a/homeassistant/components/tasmota/cover.py +++ b/homeassistant/components/tasmota/cover.py @@ -9,6 +9,7 @@ from hatasmota.models import DiscoveryHashType from homeassistant.components.cover import ( ATTR_POSITION, + ATTR_TILT_POSITION, DOMAIN as COVER_DOMAIN, CoverEntity, CoverEntityFeature, @@ -55,23 +56,32 @@ class TasmotaCover( ): """Representation of a Tasmota cover.""" - _attr_supported_features = ( - CoverEntityFeature.OPEN - | CoverEntityFeature.CLOSE - | CoverEntityFeature.STOP - | CoverEntityFeature.SET_POSITION - ) _tasmota_entity: tasmota_shutter.TasmotaShutter def __init__(self, **kwds: Any) -> None: """Initialize the Tasmota cover.""" self._direction: int | None = None self._position: int | None = None + self._tilt_position: int | None = None super().__init__( **kwds, ) + self._attr_supported_features = ( + CoverEntityFeature.OPEN + | CoverEntityFeature.CLOSE + | CoverEntityFeature.STOP + | CoverEntityFeature.SET_POSITION + ) + if self._tasmota_entity.supports_tilt: + self._attr_supported_features |= ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.STOP_TILT + | CoverEntityFeature.SET_TILT_POSITION + ) + async def async_added_to_hass(self) -> None: """Subscribe to MQTT events.""" self._tasmota_entity.set_on_state_callback(self.cover_state_updated) @@ -82,6 +92,7 @@ class TasmotaCover( """Handle state updates.""" self._direction = kwargs["direction"] self._position = kwargs["position"] + self._tilt_position = kwargs["tilt"] self.async_write_ha_state() @property @@ -92,6 +103,14 @@ class TasmotaCover( """ return self._position + @property + def current_cover_tilt_position(self) -> int | None: + """Return current tilt position of cover. + + None is unknown, 0 is closed, 100 is fully open. + """ + return self._tilt_position + @property def is_opening(self) -> bool: """Return if the cover is opening or not.""" @@ -125,3 +144,20 @@ class TasmotaCover( async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" await self._tasmota_entity.stop() + + async def async_open_cover_tilt(self, **kwargs: Any) -> None: + """Open the cover tilt.""" + await self._tasmota_entity.open_tilt() + + async def async_close_cover_tilt(self, **kwargs: Any) -> None: + """Close cover tilt.""" + await self._tasmota_entity.close_tilt() + + async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: + """Move the cover tilt to a specific position.""" + tilt = kwargs[ATTR_TILT_POSITION] + await self._tasmota_entity.set_tilt_position(tilt) + + async def async_stop_cover_tilt(self, **kwargs: Any) -> None: + """Stop the cover tilt.""" + await self._tasmota_entity.stop() diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 6a743683d94..772105043fe 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.4.1"], + "requirements": ["hatasmota==0.5.0"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"], diff --git a/requirements_all.txt b/requirements_all.txt index b572dca4f6f..79ee89d3cb7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -792,7 +792,7 @@ hass-nabucasa==0.54.0 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.4.1 +hatasmota==0.5.0 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e0f5459d43a..85077aa295a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -565,7 +565,7 @@ hangups==0.4.18 hass-nabucasa==0.54.0 # homeassistant.components.tasmota -hatasmota==0.4.1 +hatasmota==0.5.0 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/tests/components/tasmota/test_cover.py b/tests/components/tasmota/test_cover.py index e88df08a80c..843fd72ecf1 100644 --- a/tests/components/tasmota/test_cover.py +++ b/tests/components/tasmota/test_cover.py @@ -30,6 +30,19 @@ from .test_common import ( from tests.common import async_fire_mqtt_message +COVER_SUPPORT = ( + cover.SUPPORT_OPEN + | cover.SUPPORT_CLOSE + | cover.SUPPORT_STOP + | cover.SUPPORT_SET_POSITION +) +TILT_SUPPORT = ( + cover.SUPPORT_OPEN_TILT + | cover.SUPPORT_CLOSE_TILT + | cover.SUPPORT_STOP_TILT + | cover.SUPPORT_SET_TILT_POSITION +) + async def test_missing_relay(hass, mqtt_mock, setup_tasmota): """Test no cover is discovered if relays are missing.""" @@ -64,11 +77,46 @@ async def test_multiple_covers( assert len(hass.states.async_all("cover")) == num_covers +async def test_tilt_support(hass, mqtt_mock, setup_tasmota): + """Test tilt support detection.""" + config = copy.deepcopy(DEFAULT_CONFIG) + config["rl"] = [3, 3, 3, 3, 3, 3, 3, 3] + config["sht"] = [ + [0, 0, 0], # Default settings, no tilt + [-90, 90, 24], # Tilt configured + [-90, 90, 0], # Duration 0, no tilt + [-90, -90, 24], # min+max same, no tilt + ] + mac = config["mac"] + + async_fire_mqtt_message( + hass, + f"{DEFAULT_PREFIX}/{mac}/config", + json.dumps(config), + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all("cover")) == 4 + + state = hass.states.get("cover.tasmota_cover_1") + assert state.attributes["supported_features"] == COVER_SUPPORT + + state = hass.states.get("cover.tasmota_cover_2") + assert state.attributes["supported_features"] == COVER_SUPPORT | TILT_SUPPORT + + state = hass.states.get("cover.tasmota_cover_3") + assert state.attributes["supported_features"] == COVER_SUPPORT + + state = hass.states.get("cover.tasmota_cover_4") + assert state.attributes["supported_features"] == COVER_SUPPORT + + async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 3 config["rl"][1] = 3 + config["sht"] = [[-90, 90, 24]] mac = config["mac"] async_fire_mqtt_message( @@ -86,40 +134,39 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): await hass.async_block_till_done() state = hass.states.get("cover.tasmota_cover_1") assert state.state == STATE_UNKNOWN - assert ( - state.attributes["supported_features"] - == cover.SUPPORT_OPEN - | cover.SUPPORT_CLOSE - | cover.SUPPORT_STOP - | cover.SUPPORT_SET_POSITION - ) + assert state.attributes["supported_features"] == COVER_SUPPORT | TILT_SUPPORT assert not state.attributes.get(ATTR_ASSUMED_STATE) # Periodic updates async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", - '{"Shutter1":{"Position":54,"Direction":-1}}', + '{"Shutter1":{"Position":54,"Direction":-1,"Tilt":-90}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" assert state.attributes["current_position"] == 54 + assert state.attributes["current_tilt_position"] == 0 async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", - '{"Shutter1":{"Position":100,"Direction":1}}', + '{"Shutter1":{"Position":100,"Direction":1,"Tilt":90}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" assert state.attributes["current_position"] == 100 + assert state.attributes["current_tilt_position"] == 100 async_fire_mqtt_message( - hass, "tasmota_49A3BC/tele/SENSOR", '{"Shutter1":{"Position":0,"Direction":0}}' + hass, + "tasmota_49A3BC/tele/SENSOR", + '{"Shutter1":{"Position":0,"Direction":0,"Tilt":0}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" assert state.attributes["current_position"] == 0 + assert state.attributes["current_tilt_position"] == 50 async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", '{"Shutter1":{"Position":1,"Direction":0}}' @@ -141,29 +188,32 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":54,"Direction":-1}}}', + '{"StatusSNS":{"Shutter1":{"Position":54,"Direction":-1,"Tilt":-90}}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" assert state.attributes["current_position"] == 54 + assert state.attributes["current_tilt_position"] == 0 async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":1}}}', + '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":1,"Tilt":90}}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" assert state.attributes["current_position"] == 100 + assert state.attributes["current_tilt_position"] == 100 async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":0,"Direction":0}}}', + '{"StatusSNS":{"Shutter1":{"Position":0,"Direction":0,"Tilt":0}}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" assert state.attributes["current_position"] == 0 + assert state.attributes["current_tilt_position"] == 50 async_fire_mqtt_message( hass, @@ -187,27 +237,32 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", - '{"Shutter1":{"Position":54,"Direction":-1}}', + '{"Shutter1":{"Position":54,"Direction":-1,"Tilt":-90}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" assert state.attributes["current_position"] == 54 + assert state.attributes["current_tilt_position"] == 0 async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", - '{"Shutter1":{"Position":100,"Direction":1}}', + '{"Shutter1":{"Position":100,"Direction":1,"Tilt":90}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" assert state.attributes["current_position"] == 100 + assert state.attributes["current_tilt_position"] == 100 async_fire_mqtt_message( - hass, "tasmota_49A3BC/stat/RESULT", '{"Shutter1":{"Position":0,"Direction":0}}' + hass, + "tasmota_49A3BC/stat/RESULT", + '{"Shutter1":{"Position":0,"Direction":0,"Tilt":0}}', ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" assert state.attributes["current_position"] == 0 + assert state.attributes["current_tilt_position"] == 50 async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", '{"Shutter1":{"Position":1,"Direction":0}}' @@ -249,14 +304,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot await hass.async_block_till_done() state = hass.states.get("cover.tasmota_cover_1") assert state.state == STATE_UNKNOWN - assert ( - state.attributes["supported_features"] - == cover.SUPPORT_OPEN - | cover.SUPPORT_CLOSE - | cover.SUPPORT_STOP - | cover.SUPPORT_SET_POSITION - ) - assert not state.attributes.get(ATTR_ASSUMED_STATE) + assert state.attributes["supported_features"] == COVER_SUPPORT # Periodic updates async_fire_mqtt_message( @@ -405,6 +453,7 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): config["dn"] = "Test" config["rl"][0] = 3 config["rl"][1] = 3 + config["sht"] = [[-90, 90, 24]] mac = config["mac"] async_fire_mqtt_message( @@ -461,6 +510,45 @@ async def test_sending_mqtt_commands(hass, mqtt_mock, setup_tasmota): ) mqtt_mock.async_publish.reset_mock() + # Close the cover tilt and verify MQTT message is sent + await call_service(hass, "cover.test_cover_1", "close_cover_tilt") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/ShutterTilt1", "CLOSE", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Open the cover tilt and verify MQTT message is sent + await call_service(hass, "cover.test_cover_1", "open_cover_tilt") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/ShutterTilt1", "OPEN", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Stop the cover tilt and verify MQTT message is sent + await call_service(hass, "cover.test_cover_1", "stop_cover_tilt") + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/ShutterStop1", "", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Set tilt position and verify MQTT message is sent + await call_service( + hass, "cover.test_cover_1", "set_cover_tilt_position", tilt_position=0 + ) + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/ShutterTilt1", "-90", 0, False + ) + mqtt_mock.async_publish.reset_mock() + + # Set tilt position and verify MQTT message is sent + await call_service( + hass, "cover.test_cover_1", "set_cover_tilt_position", tilt_position=100 + ) + mqtt_mock.async_publish.assert_called_once_with( + "tasmota_49A3BC/cmnd/ShutterTilt1", "90", 0, False + ) + mqtt_mock.async_publish.reset_mock() + async def test_sending_mqtt_commands_inverted(hass, mqtt_mock, setup_tasmota): """Test the sending MQTT commands.""" From 2a2a7a62c525e1feda6557c48a881c997133eef7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 16:16:33 -0400 Subject: [PATCH 429/930] Avoid matching entity_id/domain attributes in logbook when there is no entities_filter (#71825) --- homeassistant/components/logbook/__init__.py | 57 ++++++++++---------- tests/components/logbook/test_init.py | 19 ++++++- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 4b40e398f6e..fae979445fd 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -426,7 +426,8 @@ def _humanify( elif event_type == EVENT_LOGBOOK_ENTRY: event = event_cache.get(row) - event_data = event.data + if not (event_data := event.data): + continue domain = event_data.get(ATTR_DOMAIN) entity_id = event_data.get(ATTR_ENTITY_ID) if domain is None and entity_id is not None: @@ -474,6 +475,22 @@ def _get_events( def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield Events that are not filtered away.""" + + def _keep_row(row: Row, event_type: str) -> bool: + """Check if the entity_filter rejects a row.""" + assert entities_filter is not None + if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): + return entities_filter(entity_id) + + if event_type in external_events: + # If the entity_id isn't described, use the domain that describes + # the event for filtering. + domain: str | None = external_events[event_type][0] + else: + domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) + + return domain is not None and entities_filter(f"{domain}._") + # end_day - start_day intentionally checks .days and not .total_seconds() # since we don't want to switch over to buffered if they go # over one day by a few hours since the UI makes it so easy to do that. @@ -490,16 +507,17 @@ def _get_events( # so we don't switch over until we request > 1 day+ of data. # rows = query.yield_per(1024) + for row in rows: context_lookup.setdefault(row.context_id, row) - if row.context_only: - continue - event_type = row.event_type - if event_type != EVENT_CALL_SERVICE and ( - event_type == EVENT_STATE_CHANGED - or _keep_row(hass, event_type, row, entities_filter) - ): - yield row + if not row.context_only: + event_type = row.event_type + if event_type != EVENT_CALL_SERVICE and ( + entities_filter is None + or event_type == EVENT_STATE_CHANGED + or _keep_row(row, event_type) + ): + yield row if entity_ids is not None: entities_filter = generate_filter([], entity_ids, [], []) @@ -526,27 +544,6 @@ def _get_events( ) -def _keep_row( - hass: HomeAssistant, - event_type: str, - row: Row, - entities_filter: EntityFilter | Callable[[str], bool] | None = None, -) -> bool: - if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): - return entities_filter is None or entities_filter(entity_id) - - if event_type in hass.data[DOMAIN]: - # If the entity_id isn't described, use the domain that describes - # the event for filtering. - domain = hass.data[DOMAIN][event_type][0] - else: - domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) - - return domain is not None and ( - entities_filter is None or entities_filter(f"{domain}._") - ) - - class ContextAugmenter: """Augment data with context trace.""" diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 82857e6735c..eb58a61835e 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -1761,13 +1761,21 @@ async def test_fire_logbook_entries(hass, hass_client, recorder_mock): logbook.EVENT_LOGBOOK_ENTRY, {}, ) + hass.bus.async_fire( + logbook.EVENT_LOGBOOK_ENTRY, + { + logbook.ATTR_NAME: "Alarm", + logbook.ATTR_MESSAGE: "is triggered", + logbook.ATTR_DOMAIN: "switch", + }, + ) await async_wait_recording_done(hass) client = await hass_client() response_json = await _async_fetch_logbook(client) # The empty events should be skipped - assert len(response_json) == 10 + assert len(response_json) == 11 async def test_exclude_events_domain(hass, hass_client, recorder_mock): @@ -1986,6 +1994,15 @@ async def test_include_events_domain_glob(hass, hass_client, recorder_mock): ) await async_recorder_block_till_done(hass) + # Should get excluded by domain + hass.bus.async_fire( + logbook.EVENT_LOGBOOK_ENTRY, + { + logbook.ATTR_NAME: "Alarm", + logbook.ATTR_MESSAGE: "is triggered", + logbook.ATTR_DOMAIN: "switch", + }, + ) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) hass.bus.async_fire( From 535ae56fe77f72d8950f1cba7632d459721fe9c7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 16:17:41 -0400 Subject: [PATCH 430/930] Remove unused entity_id argument in logbook context augmenter (#71829) --- homeassistant/components/logbook/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index fae979445fd..351b121a166 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -413,7 +413,7 @@ def _humanify( if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): data[LOGBOOK_ENTRY_ICON] = icon - context_augmenter.augment(data, entity_id, row) + context_augmenter.augment(data, row) yield data elif event_type in external_events: @@ -421,7 +421,7 @@ def _humanify( data = describe_event(event_cache.get(row)) data[LOGBOOK_ENTRY_WHEN] = format_time(row) data[LOGBOOK_ENTRY_DOMAIN] = domain - context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row) + context_augmenter.augment(data, row) yield data elif event_type == EVENT_LOGBOOK_ENTRY: @@ -441,7 +441,7 @@ def _humanify( LOGBOOK_ENTRY_DOMAIN: domain, LOGBOOK_ENTRY_ENTITY_ID: entity_id, } - context_augmenter.augment(data, entity_id, row) + context_augmenter.augment(data, row) yield data @@ -562,7 +562,7 @@ class ContextAugmenter: self.external_events = external_events self.event_cache = event_cache - def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None: + def augment(self, data: dict[str, Any], row: Row) -> None: """Augment data from the row and cache.""" if context_user_id := row.context_user_id: data[CONTEXT_USER_ID] = context_user_id From e06ea5e03e2cf4b97d19c9ea08e3d0cd40716dec Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 16:19:26 -0400 Subject: [PATCH 431/930] Remove deprecated history function entry points (#71815) --- homeassistant/components/history/__init__.py | 28 -------------------- 1 file changed, 28 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index 17b983e11c1..e5b6c99eb2a 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -29,7 +29,6 @@ from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.deprecation import deprecated_class, deprecated_function from homeassistant.helpers.entityfilter import ( CONF_ENTITY_GLOBS, INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, @@ -60,28 +59,6 @@ CONFIG_SCHEMA = vol.Schema( ) -@deprecated_function("homeassistant.components.recorder.history.get_significant_states") -def get_significant_states(hass, *args, **kwargs): - """Wrap get_significant_states_with_session with an sql session.""" - return history.get_significant_states(hass, *args, **kwargs) - - -@deprecated_function( - "homeassistant.components.recorder.history.state_changes_during_period" -) -def state_changes_during_period(hass, start_time, end_time=None, entity_id=None): - """Return states changes during UTC period start_time - end_time.""" - return history.state_changes_during_period( - hass, start_time, end_time=None, entity_id=None - ) - - -@deprecated_function("homeassistant.components.recorder.history.get_last_state_changes") -def get_last_state_changes(hass, number_of_states, entity_id): - """Return the last number_of_states.""" - return history.get_last_state_changes(hass, number_of_states, entity_id) - - async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the history hooks.""" conf = config.get(DOMAIN, {}) @@ -101,11 +78,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True -@deprecated_class("homeassistant.components.recorder.models.LazyState") -class LazyState(history_models.LazyState): - """A lazy version of core State.""" - - def _ws_get_statistics_during_period( hass: HomeAssistant, msg_id: int, From 663f6f83409f0c415380086b6fefd976327adbb8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 17:56:16 -0400 Subject: [PATCH 432/930] Complete refactoring of logbook humanify (#71830) --- homeassistant/components/logbook/__init__.py | 148 +++++++++---------- tests/components/logbook/common.py | 20 +-- tests/components/logbook/test_init.py | 2 + 3 files changed, 85 insertions(+), 85 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 351b121a166..809f13ca598 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -377,21 +377,54 @@ class LogbookView(HomeAssistantView): def _humanify( - hass: HomeAssistant, rows: Generator[Row, None, None], + entities_filter: EntityFilter | Callable[[str], bool] | None, + ent_reg: er.EntityRegistry, + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ], entity_name_cache: EntityNameCache, - event_cache: EventCache, - context_augmenter: ContextAugmenter, format_time: Callable[[Row], Any], ) -> Generator[dict[str, Any], None, None]: """Generate a converted list of events into entries.""" - external_events = hass.data.get(DOMAIN, {}) # Continuous sensors, will be excluded from the logbook continuous_sensors: dict[str, bool] = {} + event_data_cache: dict[str, dict[str, Any]] = {} + context_lookup: dict[str | None, Row | None] = {None: None} + event_cache = EventCache(event_data_cache) + context_augmenter = ContextAugmenter( + context_lookup, entity_name_cache, external_events, event_cache + ) - # Process events + def _keep_row(row: Row, event_type: str) -> bool: + """Check if the entity_filter rejects a row.""" + assert entities_filter is not None + if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): + return entities_filter(entity_id) + + if event_type in external_events: + # If the entity_id isn't described, use the domain that describes + # the event for filtering. + domain: str | None = external_events[event_type][0] + else: + domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) + + return domain is not None and entities_filter(f"{domain}._") + + # Process rows for row in rows: + context_id = row.context_id + context_lookup.setdefault(context_id, row) + if row.context_only: + continue event_type = row.event_type + if event_type == EVENT_CALL_SERVICE or ( + event_type != EVENT_STATE_CHANGED + and entities_filter is not None + and not _keep_row(row, event_type) + ): + continue + if event_type == EVENT_STATE_CHANGED: entity_id = row.entity_id assert entity_id is not None @@ -399,7 +432,7 @@ def _humanify( if ( is_continuous := continuous_sensors.get(entity_id) ) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN: - is_continuous = _is_sensor_continuous(hass, entity_id) + is_continuous = _is_sensor_continuous(ent_reg, entity_id) continuous_sensors[entity_id] = is_continuous if is_continuous: continue @@ -413,7 +446,7 @@ def _humanify( if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): data[LOGBOOK_ENTRY_ICON] = icon - context_augmenter.augment(data, row) + context_augmenter.augment(data, row, context_id) yield data elif event_type in external_events: @@ -421,27 +454,27 @@ def _humanify( data = describe_event(event_cache.get(row)) data[LOGBOOK_ENTRY_WHEN] = format_time(row) data[LOGBOOK_ENTRY_DOMAIN] = domain - context_augmenter.augment(data, row) + context_augmenter.augment(data, row, context_id) yield data elif event_type == EVENT_LOGBOOK_ENTRY: event = event_cache.get(row) if not (event_data := event.data): continue - domain = event_data.get(ATTR_DOMAIN) - entity_id = event_data.get(ATTR_ENTITY_ID) - if domain is None and entity_id is not None: + entry_domain = event_data.get(ATTR_DOMAIN) + entry_entity_id = event_data.get(ATTR_ENTITY_ID) + if entry_domain is None and entry_entity_id is not None: with suppress(IndexError): - domain = split_entity_id(str(entity_id))[0] + entry_domain = split_entity_id(str(entry_entity_id))[0] data = { LOGBOOK_ENTRY_WHEN: format_time(row), LOGBOOK_ENTRY_NAME: event_data.get(ATTR_NAME), LOGBOOK_ENTRY_MESSAGE: event_data.get(ATTR_MESSAGE), - LOGBOOK_ENTRY_DOMAIN: domain, - LOGBOOK_ENTRY_ENTITY_ID: entity_id, + LOGBOOK_ENTRY_DOMAIN: entry_domain, + LOGBOOK_ENTRY_ENTITY_ID: entry_entity_id, } - context_augmenter.augment(data, row) + context_augmenter.augment(data, row, context_id) yield data @@ -460,67 +493,34 @@ def _get_events( entity_ids and context_id ), "can't pass in both entity_ids and context_id" - entity_name_cache = EntityNameCache(hass) - event_data_cache: dict[str, dict[str, Any]] = {} - context_lookup: dict[str | None, Row | None] = {None: None} - event_cache = EventCache(event_data_cache) external_events: dict[ str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] ] = hass.data.get(DOMAIN, {}) - context_augmenter = ContextAugmenter( - context_lookup, entity_name_cache, external_events, event_cache - ) event_types = (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) format_time = _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat + entity_name_cache = EntityNameCache(hass) + ent_reg = er.async_get(hass) + + if entity_ids is not None: + entities_filter = generate_filter([], entity_ids, [], []) def yield_rows(query: Query) -> Generator[Row, None, None]: - """Yield Events that are not filtered away.""" - - def _keep_row(row: Row, event_type: str) -> bool: - """Check if the entity_filter rejects a row.""" - assert entities_filter is not None - if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): - return entities_filter(entity_id) - - if event_type in external_events: - # If the entity_id isn't described, use the domain that describes - # the event for filtering. - domain: str | None = external_events[event_type][0] - else: - domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) - - return domain is not None and entities_filter(f"{domain}._") - + """Yield rows from the database.""" # end_day - start_day intentionally checks .days and not .total_seconds() # since we don't want to switch over to buffered if they go # over one day by a few hours since the UI makes it so easy to do that. if entity_ids or context_id or (end_day - start_day).days <= 1: - rows = query.all() - else: - # Only buffer rows to reduce memory pressure - # if we expect the result set is going to be very large. - # What is considered very large is going to differ - # based on the hardware Home Assistant is running on. - # - # sqlalchemy suggests that is at least 10k, but for - # even and RPi3 that number seems higher in testing - # so we don't switch over until we request > 1 day+ of data. - # - rows = query.yield_per(1024) - - for row in rows: - context_lookup.setdefault(row.context_id, row) - if not row.context_only: - event_type = row.event_type - if event_type != EVENT_CALL_SERVICE and ( - entities_filter is None - or event_type == EVENT_STATE_CHANGED - or _keep_row(row, event_type) - ): - yield row - - if entity_ids is not None: - entities_filter = generate_filter([], entity_ids, [], []) + return query.all() # type: ignore[no-any-return] + # Only buffer rows to reduce memory pressure + # if we expect the result set is going to be very large. + # What is considered very large is going to differ + # based on the hardware Home Assistant is running on. + # + # sqlalchemy suggests that is at least 10k, but for + # even and RPi3 that number seems higher in testing + # so we don't switch over until we request > 1 day+ of data. + # + return query.yield_per(1024) # type: ignore[no-any-return] stmt = statement_for_request( start_day, end_day, event_types, entity_ids, filters, context_id @@ -534,11 +534,11 @@ def _get_events( with session_scope(hass=hass) as session: return list( _humanify( - hass, yield_rows(session.execute(stmt)), + entities_filter, + ent_reg, + external_events, entity_name_cache, - event_cache, - context_augmenter, format_time, ) ) @@ -562,12 +562,12 @@ class ContextAugmenter: self.external_events = external_events self.event_cache = event_cache - def augment(self, data: dict[str, Any], row: Row) -> None: + def augment(self, data: dict[str, Any], row: Row, context_id: str) -> None: """Augment data from the row and cache.""" if context_user_id := row.context_user_id: data[CONTEXT_USER_ID] = context_user_id - if not (context_row := self.context_lookup.get(row.context_id)): + if not (context_row := self.context_lookup.get(context_id)): return if _rows_match(row, context_row): @@ -624,17 +624,13 @@ class ContextAugmenter: ) -def _is_sensor_continuous( - hass: HomeAssistant, - entity_id: str, -) -> bool: +def _is_sensor_continuous(ent_reg: er.EntityRegistry, entity_id: str) -> bool: """Determine if a sensor is continuous by checking its state class. Sensors with a unit_of_measurement are also considered continuous, but are filtered already by the SQL query generated by _get_events """ - registry = er.async_get(hass) - if not (entry := registry.async_get(entity_id)): + if not (entry := ent_reg.async_get(entity_id)): # Entity not registered, so can't have a state class return False return ( diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py index 32273f04c05..9d6f35eb702 100644 --- a/tests/components/logbook/common.py +++ b/tests/components/logbook/common.py @@ -7,6 +7,7 @@ from typing import Any from homeassistant.components import logbook from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat from homeassistant.core import Context +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.json import JSONEncoder import homeassistant.util.dt as dt_util @@ -30,6 +31,11 @@ class MockRow: self.context_id = context.id if context else None self.state = None self.entity_id = None + self.state_id = None + self.event_id = None + self.shared_attrs = None + self.attributes = None + self.context_only = False @property def time_fired_minute(self): @@ -44,20 +50,16 @@ class MockRow: def mock_humanify(hass_, rows): """Wrap humanify with mocked logbook objects.""" - event_data_cache = {} - context_lookup = {} entity_name_cache = logbook.EntityNameCache(hass_) - event_cache = logbook.EventCache(event_data_cache) - context_augmenter = logbook.ContextAugmenter( - context_lookup, entity_name_cache, {}, event_cache - ) + ent_reg = er.async_get(hass_) + external_events = hass_.data.get(logbook.DOMAIN, {}) return list( logbook._humanify( - hass_, rows, + None, + ent_reg, + external_events, entity_name_cache, - event_cache, - context_augmenter, logbook._row_time_fired_isoformat, ), ) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index eb58a61835e..a515afdf16e 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -323,6 +323,7 @@ def create_state_changed_event_from_old_new( "old_state_id", "shared_attrs", "shared_data", + "context_only", ], ) @@ -335,6 +336,7 @@ def create_state_changed_event_from_old_new( row.state = new_state and new_state.get("state") row.entity_id = entity_id row.domain = entity_id and ha.split_entity_id(entity_id)[0] + row.context_only = False row.context_id = None row.context_user_id = None row.context_parent_id = None From f6600bbc209130cffee3b8b115e8309488eafc73 Mon Sep 17 00:00:00 2001 From: mkmer Date: Fri, 13 May 2022 18:41:01 -0400 Subject: [PATCH 433/930] Add Aladdin connect config flow (#68304) * Adding flow and async * Fixes to init * Lint and type * Fixed coveragerc file * Added Test Coverage * Added Update Listener and removed unused code * Wrong integration name in init. * Nothing * Added yaml import flow * Added YAML import functionality * Added back aladdin_connect files to coverage rc * Removed commented code * Clean up error message * Update homeassistant/components/aladdin_connect/__init__.py Co-authored-by: G Johansson * Update homeassistant/components/aladdin_connect/__init__.py Co-authored-by: G Johansson * Update homeassistant/components/aladdin_connect/config_flow.py Co-authored-by: G Johansson * Updated Documentation errors * recommended change broke cover.py - backed out * Cleaned up unused defenitions * implimented recommended changes from gjohansson * Dev environment cleanup * Raised errors for better recovery, replaced removed update files, utilized PLATFORM vars to init platform * Added back removal * Added Code Owner * Fixed more comment errors and import duplicates * Added test coverage and formated code * Added test coverage for model and init * Added test_cover for full testing coverage * Added await to async call * Added missing asserts to failure tests * Updated tranlsation * Fixed wording in yaml import function, white space in const.py, return from validate_input. * Update homeassistant/components/aladdin_connect/config_flow.py Co-authored-by: Robert Svensson * "too much" whitespace * Added back mising strings.json errors * Added ConfigFlowReconfig and tests * Finished up reauth config flow and associated tests * Added reauth to strings, removed username from reauth * recommended changes, ran script.translations, added auth test to reauth * put back self.entry.data unpack. * Cleanup for error message, fixed missing "asserts" in tests * Added yaml import assertions * Fixed documentation errors in test_cover. * remove unused string. * revised tests and wording for yaml import * Documentation cleanup. * Changed sideeffect names Co-authored-by: G Johansson Co-authored-by: Robert Svensson --- .coveragerc | 2 - CODEOWNERS | 2 + .../components/aladdin_connect/__init__.py | 37 ++ .../components/aladdin_connect/config_flow.py | 134 ++++++++ .../components/aladdin_connect/const.py | 1 + .../components/aladdin_connect/cover.py | 58 ++-- .../components/aladdin_connect/manifest.json | 5 +- .../components/aladdin_connect/strings.json | 29 ++ .../aladdin_connect/translations/en.json | 27 ++ homeassistant/generated/config_flows.py | 1 + requirements_test_all.txt | 3 + tests/components/aladdin_connect/__init__.py | 1 + .../aladdin_connect/test_config_flow.py | 323 ++++++++++++++++++ .../components/aladdin_connect/test_cover.py | 272 +++++++++++++++ tests/components/aladdin_connect/test_init.py | 76 +++++ .../components/aladdin_connect/test_model.py | 18 + 16 files changed, 962 insertions(+), 27 deletions(-) create mode 100644 homeassistant/components/aladdin_connect/config_flow.py create mode 100644 homeassistant/components/aladdin_connect/strings.json create mode 100644 homeassistant/components/aladdin_connect/translations/en.json create mode 100644 tests/components/aladdin_connect/__init__.py create mode 100644 tests/components/aladdin_connect/test_config_flow.py create mode 100644 tests/components/aladdin_connect/test_cover.py create mode 100644 tests/components/aladdin_connect/test_init.py create mode 100644 tests/components/aladdin_connect/test_model.py diff --git a/.coveragerc b/.coveragerc index ef7cd7b847c..7ce0e5ee299 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,5 @@ [run] source = homeassistant - omit = homeassistant/__main__.py homeassistant/helpers/signal.py @@ -42,7 +41,6 @@ omit = homeassistant/components/airtouch4/const.py homeassistant/components/airvisual/__init__.py homeassistant/components/airvisual/sensor.py - homeassistant/components/aladdin_connect/* homeassistant/components/alarmdecoder/__init__.py homeassistant/components/alarmdecoder/alarm_control_panel.py homeassistant/components/alarmdecoder/binary_sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 8be3146b432..d07ce028ba9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -50,6 +50,8 @@ build.json @home-assistant/supervisor /tests/components/airvisual/ @bachya /homeassistant/components/airzone/ @Noltari /tests/components/airzone/ @Noltari +/homeassistant/components/aladdin_connect/ @mkmer +/tests/components/aladdin_connect/ @mkmer /homeassistant/components/alarm_control_panel/ @home-assistant/core /tests/components/alarm_control_panel/ @home-assistant/core /homeassistant/components/alert/ @home-assistant/core diff --git a/homeassistant/components/aladdin_connect/__init__.py b/homeassistant/components/aladdin_connect/__init__.py index 90196616dc5..cbd4a195a3a 100644 --- a/homeassistant/components/aladdin_connect/__init__.py +++ b/homeassistant/components/aladdin_connect/__init__.py @@ -1 +1,38 @@ """The aladdin_connect component.""" +import logging +from typing import Final + +from aladdin_connect import AladdinConnectClient + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady + +from .const import DOMAIN + +_LOGGER: Final = logging.getLogger(__name__) + +PLATFORMS: list[Platform] = [Platform.COVER] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up platform from a ConfigEntry.""" + username = entry.data[CONF_USERNAME] + password = entry.data[CONF_PASSWORD] + acc = AladdinConnectClient(username, password) + try: + if not await hass.async_add_executor_job(acc.login): + raise ConfigEntryAuthFailed("Incorrect Password") + except (TypeError, KeyError, NameError, ValueError) as ex: + _LOGGER.error("%s", ex) + raise ConfigEntryNotReady from ex + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = acc + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/aladdin_connect/config_flow.py b/homeassistant/components/aladdin_connect/config_flow.py new file mode 100644 index 00000000000..f912d36a3f0 --- /dev/null +++ b/homeassistant/components/aladdin_connect/config_flow.py @@ -0,0 +1,134 @@ +"""Config flow for Aladdin Connect cover integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from aladdin_connect import AladdinConnectClient +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import HomeAssistantError + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +STEP_USER_DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + } +) + +REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) + + +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: + """Validate the user input allows us to connect. + + Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. + """ + acc = AladdinConnectClient(data[CONF_USERNAME], data[CONF_PASSWORD]) + try: + login = await hass.async_add_executor_job(acc.login) + except (TypeError, KeyError, NameError, ValueError) as ex: + raise ConnectionError from ex + else: + if not login: + raise InvalidAuth + + +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Aladdin Connect.""" + + VERSION = 1 + entry: config_entries.ConfigEntry | None + + async def async_step_reauth( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle re-authentication with Aladdin Connect.""" + + self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm re-authentication with Aladdin Connect.""" + errors: dict[str, str] = {} + + if user_input: + assert self.entry is not None + password = user_input[CONF_PASSWORD] + data = { + CONF_USERNAME: self.entry.data[CONF_USERNAME], + CONF_PASSWORD: password, + } + + try: + await validate_input(self.hass, data) + except ConnectionError: + errors["base"] = "cannot_connect" + except InvalidAuth: + errors["base"] = "invalid_auth" + else: + + self.hass.config_entries.async_update_entry( + self.entry, + data={ + **self.entry.data, + CONF_PASSWORD: password, + }, + ) + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth_confirm", + data_schema=REAUTH_SCHEMA, + errors=errors, + ) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if user_input is None: + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA + ) + + errors = {} + + try: + await validate_input(self.hass, user_input) + except ConnectionError: + errors["base"] = "cannot_connect" + except InvalidAuth: + errors["base"] = "invalid_auth" + + else: + await self.async_set_unique_id( + user_input["username"].lower(), raise_on_progress=False + ) + self._abort_if_unique_id_configured() + return self.async_create_entry(title="Aladdin Connect", data=user_input) + + return self.async_show_form( + step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors + ) + + async def async_step_import( + self, import_data: dict[str, Any] | None = None + ) -> FlowResult: + """Import Aladin Connect config from configuration.yaml.""" + return await self.async_step_user(import_data) + + +class InvalidAuth(HomeAssistantError): + """Error to indicate there is invalid auth.""" diff --git a/homeassistant/components/aladdin_connect/const.py b/homeassistant/components/aladdin_connect/const.py index 3069680c753..7a11cf63a9e 100644 --- a/homeassistant/components/aladdin_connect/const.py +++ b/homeassistant/components/aladdin_connect/const.py @@ -16,4 +16,5 @@ STATES_MAP: Final[dict[str, str]] = { "closing": STATE_CLOSING, } +DOMAIN = "aladdin_connect" SUPPORTED_FEATURES: Final = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 05c24fc9a37..9e18abe21f6 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -7,12 +7,12 @@ from typing import Any, Final from aladdin_connect import AladdinConnectClient import voluptuous as vol -from homeassistant.components import persistent_notification from homeassistant.components.cover import ( PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA, CoverDeviceClass, CoverEntity, ) +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( CONF_PASSWORD, CONF_USERNAME, @@ -21,11 +21,12 @@ from homeassistant.const import ( STATE_OPENING, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import NOTIFICATION_ID, NOTIFICATION_TITLE, STATES_MAP, SUPPORTED_FEATURES +from .const import DOMAIN, STATES_MAP, SUPPORTED_FEATURES from .model import DoorDevice _LOGGER: Final = logging.getLogger(__name__) @@ -35,33 +36,44 @@ PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend( ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up the Aladdin Connect platform.""" - - username: str = config[CONF_USERNAME] - password: str = config[CONF_PASSWORD] - acc = AladdinConnectClient(username, password) - - try: - if not acc.login(): - raise ValueError("Username or Password is incorrect") - add_entities( - (AladdinDevice(acc, door) for door in acc.get_doors()), - update_before_add=True, + """Set up Aladdin Connect devices yaml depreciated.""" + _LOGGER.warning( + "Configuring Aladdin Connect through yaml is deprecated" + "Please remove it from your configuration as it has already been imported to a config entry" + ) + await hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, ) + ) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the Aladdin Connect platform.""" + acc = hass.data[DOMAIN][config_entry.entry_id] + try: + doors = await hass.async_add_executor_job(acc.get_doors) + except (TypeError, KeyError, NameError, ValueError) as ex: _LOGGER.error("%s", ex) - persistent_notification.create( - hass, - f"Error: {ex}
You will need to restart hass after fixing.", - title=NOTIFICATION_TITLE, - notification_id=NOTIFICATION_ID, - ) + raise ConfigEntryNotReady from ex + + async_add_entities( + (AladdinDevice(acc, door) for door in doors), + update_before_add=True, + ) class AladdinDevice(CoverEntity): @@ -71,7 +83,7 @@ class AladdinDevice(CoverEntity): _attr_supported_features = SUPPORTED_FEATURES def __init__(self, acc: AladdinConnectClient, device: DoorDevice) -> None: - """Initialize the cover.""" + """Initialize the Aladdin Connect cover.""" self._acc = acc self._device_id = device["device_id"] self._number = device["door_number"] diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json index e28b2adff42..b9ea214d996 100644 --- a/homeassistant/components/aladdin_connect/manifest.json +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -3,7 +3,8 @@ "name": "Aladdin Connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect", "requirements": ["aladdin_connect==0.4"], - "codeowners": [], + "codeowners": ["@mkmer"], "iot_class": "cloud_polling", - "loggers": ["aladdin_connect"] + "loggers": ["aladdin_connect"], + "config_flow": true } diff --git a/homeassistant/components/aladdin_connect/strings.json b/homeassistant/components/aladdin_connect/strings.json new file mode 100644 index 00000000000..ff42ca14bc3 --- /dev/null +++ b/homeassistant/components/aladdin_connect/strings.json @@ -0,0 +1,29 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } + }, + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "The Aladdin Connect integration needs to re-authenticate your account", + "data": { + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]" + }, + + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + } + } +} diff --git a/homeassistant/components/aladdin_connect/translations/en.json b/homeassistant/components/aladdin_connect/translations/en.json new file mode 100644 index 00000000000..959e88fb2ae --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/en.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured", + "reauth_successful": "Re-authentication was successful" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "The Aladdin Connect integration needs to re-authenticate your account", + "title": "Reauthenticate Integration" + }, + "user": { + "data": { + "password": "Password", + "username": "Username" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 70451b22001..ae2ab6339aa 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -21,6 +21,7 @@ FLOWS = { "airtouch4", "airvisual", "airzone", + "aladdin_connect", "alarmdecoder", "almond", "ambee", diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 85077aa295a..96ef2b55568 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -248,6 +248,9 @@ airthings_cloud==0.1.0 # homeassistant.components.airtouch4 airtouch4pyapi==1.0.5 +# homeassistant.components.aladdin_connect +aladdin_connect==0.4 + # homeassistant.components.ambee ambee==0.4.0 diff --git a/tests/components/aladdin_connect/__init__.py b/tests/components/aladdin_connect/__init__.py new file mode 100644 index 00000000000..6e108ed88df --- /dev/null +++ b/tests/components/aladdin_connect/__init__.py @@ -0,0 +1 @@ +"""The tests for Aladdin Connect platforms.""" diff --git a/tests/components/aladdin_connect/test_config_flow.py b/tests/components/aladdin_connect/test_config_flow.py new file mode 100644 index 00000000000..37ec64ba6f0 --- /dev/null +++ b/tests/components/aladdin_connect/test_config_flow.py @@ -0,0 +1,323 @@ +"""Test the Aladdin Connect config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components.aladdin_connect.config_flow import InvalidAuth +from homeassistant.components.aladdin_connect.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) + +from tests.common import MockConfigEntry + + +async def test_form(hass: HomeAssistant) -> None: + """Test we get the form.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] is None + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == "Aladdin Connect" + assert result2["data"] == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_invalid_auth(hass: HomeAssistant) -> None: + """Test we handle invalid auth.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + side_effect=InvalidAuth, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_form_cannot_connect(hass: HomeAssistant) -> None: + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + side_effect=ConnectionError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + side_effect=TypeError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=False, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_form_already_configured(hass): + """Test we handle already configured error.""" + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["step_id"] == config_entries.SOURCE_USER + + with patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "abort" + assert result2["reason"] == "already_configured" + + +async def test_import_flow_success(hass: HomeAssistant) -> None: + """Test a successful import of yaml.""" + + with patch( + "homeassistant.components.aladdin_connect.cover.async_setup_platform", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_USERNAME: "test-user", + CONF_PASSWORD: "test-password", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == "Aladdin Connect" + assert result2["data"] == { + CONF_USERNAME: "test-user", + CONF_PASSWORD: "test-password", + } + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_reauth_flow(hass: HomeAssistant) -> None: + """Test a successful reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={"username": "test-username", "password": "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.aladdin_connect.cover.async_setup_platform", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.async_setup_entry", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_ABORT + assert result2["reason"] == "reauth_successful" + assert mock_entry.data == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "new-password", + } + + +async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None: + """Test a successful reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={"username": "test-username", "password": "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.aladdin_connect.cover.async_setup_platform", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + return_value=False, + ), patch( + "homeassistant.components.aladdin_connect.cover.async_setup_entry", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_reauth_flow_other_error(hass: HomeAssistant) -> None: + """Test an unsuccessful reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-username", "password": "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={"username": "test-username", "password": "new-password"}, + ) + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.aladdin_connect.cover.async_setup_platform", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", + side_effect=ValueError, + ), patch( + "homeassistant.components.aladdin_connect.cover.async_setup_entry", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} diff --git a/tests/components/aladdin_connect/test_cover.py b/tests/components/aladdin_connect/test_cover.py new file mode 100644 index 00000000000..4904d904ad4 --- /dev/null +++ b/tests/components/aladdin_connect/test_cover.py @@ -0,0 +1,272 @@ +"""Test the Aladdin Connect Cover.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.aladdin_connect.const import DOMAIN +import homeassistant.components.aladdin_connect.cover as cover +from homeassistant.components.cover import DOMAIN as COVER_DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import ( + CONF_PASSWORD, + CONF_USERNAME, + STATE_CLOSED, + STATE_CLOSING, + STATE_OPEN, + STATE_OPENING, +) +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + +YAML_CONFIG = {"username": "test-user", "password": "test-password"} + +DEVICE_CONFIG_OPEN = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "open", + "link_status": "Connected", +} + +DEVICE_CONFIG_OPENING = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "opening", + "link_status": "Connected", +} + +DEVICE_CONFIG_CLOSED = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "closed", + "link_status": "Connected", +} + +DEVICE_CONFIG_CLOSING = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "closing", + "link_status": "Connected", +} + +DEVICE_CONFIG_DISCONNECTED = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "open", + "link_status": "Disconnected", +} + +DEVICE_CONFIG_BAD = { + "device_id": 533255, + "door_number": 1, + "name": "home", + "status": "open", +} +DEVICE_CONFIG_BAD_NO_DOOR = { + "device_id": 533255, + "door_number": 2, + "name": "home", + "status": "open", + "link_status": "Disconnected", +} + + +@pytest.mark.parametrize( + "side_effect", + [ + (TypeError), + (KeyError), + (NameError), + (ValueError), + ], +) +async def test_setup_get_doors_errors( + hass: HomeAssistant, side_effect: Exception +) -> None: + """Test component setup Get Doors Errors.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=YAML_CONFIG, + unique_id="test-id", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + side_effect=side_effect, + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) is True + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 0 + + +@pytest.mark.parametrize( + "side_effect", + [ + (TypeError), + (KeyError), + (NameError), + (ValueError), + ], +) +async def test_setup_login_error(hass: HomeAssistant, side_effect: Exception) -> None: + """Test component setup Login Errors.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=YAML_CONFIG, + unique_id="test-id", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + side_effect=side_effect, + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) is False + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 0 + + +async def test_setup_component_noerror(hass: HomeAssistant) -> None: + """Test component setup No Error.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=YAML_CONFIG, + unique_id="test-id", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ): + + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state == ConfigEntryState.LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + +async def test_cover_operation(hass: HomeAssistant) -> None: + """Test component setup open cover, close cover.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=YAML_CONFIG, + unique_id="test-id", + ) + config_entry.add_to_hass(hass) + + assert await async_setup_component(hass, "homeassistant", {}) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_OPEN], + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state == ConfigEntryState.LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert COVER_DOMAIN in hass.config.components + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.open_door", + return_value=True, + ): + await hass.services.async_call( + "cover", "open_cover", {"entity_id": "cover.home"}, blocking=True + ) + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.close_door", + return_value=True, + ): + await hass.services.async_call( + "cover", "close_cover", {"entity_id": "cover.home"}, blocking=True + ) + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_CLOSED], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state == STATE_CLOSED + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_OPEN], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state == STATE_OPEN + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_OPENING], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state == STATE_OPENING + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_CLOSING], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state == STATE_CLOSING + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_BAD], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_BAD_NO_DOOR], + ): + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state + + +async def test_yaml_import(hass: HomeAssistant, caplog: pytest.LogCaptureFixture): + """Test setup YAML import.""" + assert COVER_DOMAIN not in hass.config.components + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_CLOSED], + ): + await cover.async_setup_platform(hass, YAML_CONFIG, None) + await hass.async_block_till_done() + + assert "Configuring Aladdin Connect through yaml is deprecated" in caplog.text + + assert hass.config_entries.async_entries(DOMAIN) + config_data = hass.config_entries.async_entries(DOMAIN)[0].data + assert config_data[CONF_USERNAME] == "test-user" + assert config_data[CONF_PASSWORD] == "test-password" diff --git a/tests/components/aladdin_connect/test_init.py b/tests/components/aladdin_connect/test_init.py new file mode 100644 index 00000000000..c9814adb051 --- /dev/null +++ b/tests/components/aladdin_connect/test_init.py @@ -0,0 +1,76 @@ +"""Test for Aladdin Connect init logic.""" +from unittest.mock import patch + +from homeassistant.components.aladdin_connect.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry + +YAML_CONFIG = {"username": "test-user", "password": "test-password"} + + +async def test_unload_entry(hass: HomeAssistant): + """Test successful unload of entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-user", "password": "test-password"}, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ): + + assert (await async_setup_component(hass, DOMAIN, entry)) is True + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert entry.state is ConfigEntryState.LOADED + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_entry_password_fail(hass: HomeAssistant): + """Test successful unload of entry.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={"username": "test-user", "password": "test-password"}, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=False, + ): + + assert (await async_setup_component(hass, DOMAIN, entry)) is True + assert entry.state is ConfigEntryState.SETUP_ERROR + + +async def test_load_and_unload(hass: HomeAssistant) -> None: + """Test loading and unloading Aladdin Connect entry.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=YAML_CONFIG, + unique_id="test-id", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", + return_value=True, + ): + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state == ConfigEntryState.LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + + assert await config_entry.async_unload(hass) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.NOT_LOADED diff --git a/tests/components/aladdin_connect/test_model.py b/tests/components/aladdin_connect/test_model.py new file mode 100644 index 00000000000..e802ae53b74 --- /dev/null +++ b/tests/components/aladdin_connect/test_model.py @@ -0,0 +1,18 @@ +"""Test the Aladdin Connect model class.""" +from homeassistant.components.aladdin_connect.model import DoorDevice +from homeassistant.core import HomeAssistant + + +async def test_model(hass: HomeAssistant) -> None: + """Test model for Aladdin Connect Model.""" + test_values = { + "device_id": "1", + "door_number": "2", + "name": "my door", + "status": "good", + } + result2 = DoorDevice(test_values) + assert result2["device_id"] == "1" + assert result2["door_number"] == "2" + assert result2["name"] == "my door" + assert result2["status"] == "good" From 3e386064cfe3d81acf396f4ea0530b23b0861fb2 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Fri, 13 May 2022 18:42:33 -0400 Subject: [PATCH 434/930] Fix handling package detection for latest UniFi Protect beta (#71821) Co-authored-by: J. Nick Koston --- homeassistant/components/unifiprotect/config_flow.py | 8 ++++++-- homeassistant/components/unifiprotect/manifest.json | 2 +- homeassistant/components/unifiprotect/select.py | 2 +- homeassistant/components/unifiprotect/switch.py | 9 +++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/unifiprotect/test_switch.py | 10 +++++++--- 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/unifiprotect/config_flow.py b/homeassistant/components/unifiprotect/config_flow.py index 0cfce44a6ca..27108d24eaa 100644 --- a/homeassistant/components/unifiprotect/config_flow.py +++ b/homeassistant/components/unifiprotect/config_flow.py @@ -143,7 +143,9 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): user_input[CONF_VERIFY_SSL] = False nvr_data, errors = await self._async_get_nvr_data(user_input) if nvr_data and not errors: - return self._async_create_entry(nvr_data.name, user_input) + return self._async_create_entry( + nvr_data.name or nvr_data.type, user_input + ) placeholders = { "name": discovery_info["hostname"] @@ -289,7 +291,9 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(nvr_data.mac) self._abort_if_unique_id_configured() - return self._async_create_entry(nvr_data.name, user_input) + return self._async_create_entry( + nvr_data.name or nvr_data.type, user_input + ) user_input = user_input or {} return self.async_show_form( diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 3c3b461ed4f..3efad51db31 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Protect", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==3.4.1", "unifi-discovery==1.1.2"], + "requirements": ["pyunifiprotect==3.5.1", "unifi-discovery==1.1.2"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index b7b53ff81c8..f0500ea54e5 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -140,7 +140,7 @@ def _get_doorbell_options(api: ProtectApiClient) -> list[dict[str, Any]]: def _get_paired_camera_options(api: ProtectApiClient) -> list[dict[str, Any]]: options = [{"id": TYPE_EMPTY_VALUE, "name": "Not Paired"}] for camera in api.bootstrap.cameras.values(): - options.append({"id": camera.id, "name": camera.name}) + options.append({"id": camera.id, "name": camera.name or camera.type}) return options diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index 2257f399f75..85a089994f8 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -159,6 +159,15 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ufp_value="is_face_detection_on", ufp_set_method="set_face_detection", ), + ProtectSwitchEntityDescription( + key="smart_package", + name="Detections: Package", + icon="mdi:package-variant-closed", + entity_category=EntityCategory.CONFIG, + ufp_required_field="can_detect_package", + ufp_value="is_package_detection_on", + ufp_set_method="set_package_detection", + ), ) SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( diff --git a/requirements_all.txt b/requirements_all.txt index 79ee89d3cb7..2e8c89e5797 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1981,7 +1981,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.4.1 +pyunifiprotect==3.5.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 96ef2b55568..30510f08910 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1310,7 +1310,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.4.1 +pyunifiprotect==3.5.1 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/tests/components/unifiprotect/test_switch.py b/tests/components/unifiprotect/test_switch.py index 0a3ac92076e..c54d04a8cb7 100644 --- a/tests/components/unifiprotect/test_switch.py +++ b/tests/components/unifiprotect/test_switch.py @@ -26,9 +26,13 @@ from .conftest import ( ids_from_device_description, ) -CAMERA_SWITCHES_NO_FACE = [d for d in CAMERA_SWITCHES if d.name != "Detections: Face"] +CAMERA_SWITCHES_BASIC = [ + d + for d in CAMERA_SWITCHES + if d.name != "Detections: Face" and d.name != "Detections: Package" +] CAMERA_SWITCHES_NO_EXTRA = [ - d for d in CAMERA_SWITCHES_NO_FACE if d.name not in ("High FPS", "Privacy Mode") + d for d in CAMERA_SWITCHES_BASIC if d.name not in ("High FPS", "Privacy Mode") ] @@ -253,7 +257,7 @@ async def test_switch_setup_camera_all( entity_registry = er.async_get(hass) - for description in CAMERA_SWITCHES_NO_FACE: + for description in CAMERA_SWITCHES_BASIC: unique_id, entity_id = ids_from_device_description( Platform.SWITCH, camera, description ) From 72a65b6a211262d4690dda5067e9100d5ff4f253 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Fri, 13 May 2022 17:03:25 -0600 Subject: [PATCH 435/930] Add last seen and status code diagnostic sensors to litterrobot (#71760) --- .../components/litterrobot/sensor.py | 17 ++++++++++- .../litterrobot/strings.sensor.json | 28 +++++++++++++++++++ .../litterrobot/translations/sensor.en.json | 28 +++++++++++++++++++ .../components/litterrobot/vacuum.py | 3 -- 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/litterrobot/strings.sensor.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.en.json diff --git a/homeassistant/components/litterrobot/sensor.py b/homeassistant/components/litterrobot/sensor.py index 751d16551c3..01deaa302cf 100644 --- a/homeassistant/components/litterrobot/sensor.py +++ b/homeassistant/components/litterrobot/sensor.py @@ -17,6 +17,7 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN @@ -63,7 +64,9 @@ class LitterRobotSensorEntity(LitterRobotEntity, SensorEntity): def native_value(self) -> StateType | datetime: """Return the state.""" if self.entity_description.should_report(self.robot): - return getattr(self.robot, self.entity_description.key) + if isinstance(val := getattr(self.robot, self.entity_description.key), str): + return val.lower() + return val return None @property @@ -93,6 +96,18 @@ ROBOT_SENSORS = [ device_class=SensorDeviceClass.TIMESTAMP, should_report=lambda robot: robot.sleep_mode_enabled, ), + LitterRobotSensorEntityDescription( + name="Last Seen", + key="last_seen", + device_class=SensorDeviceClass.TIMESTAMP, + entity_category=EntityCategory.DIAGNOSTIC, + ), + LitterRobotSensorEntityDescription( + name="Status Code", + key="status_code", + device_class="litterrobot__status_code", + entity_category=EntityCategory.DIAGNOSTIC, + ), ] diff --git a/homeassistant/components/litterrobot/strings.sensor.json b/homeassistant/components/litterrobot/strings.sensor.json new file mode 100644 index 00000000000..d9ad141cf21 --- /dev/null +++ b/homeassistant/components/litterrobot/strings.sensor.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Bonnet Removed", + "ccc": "Clean Cycle Complete", + "ccp": "Clean Cycle In Progress", + "csf": "Cat Sensor Fault", + "csi": "Cat Sensor Interrupted", + "cst": "Cat Sensor Timing", + "df1": "Drawer Almost Full - 2 Cycles Left", + "df2": "Drawer Almost Full - 1 Cycle Left", + "dfs": "Drawer Full", + "dhf": "Dump + Home Position Fault", + "dpf": "Dump Position Fault", + "ec": "Empty Cycle", + "hpf": "Home Position Fault", + "off": "[%key:common::state::off%]", + "offline": "Offline", + "otf": "Over Torque Fault", + "p": "[%key:common::state::paused%]", + "pd": "Pinch Detect", + "rdy": "Ready", + "scf": "Cat Sensor Fault At Startup", + "sdf": "Drawer Full At Startup", + "spf": "Pinch Detect At Startup" + } + } +} diff --git a/homeassistant/components/litterrobot/translations/sensor.en.json b/homeassistant/components/litterrobot/translations/sensor.en.json new file mode 100644 index 00000000000..5491a6a835f --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.en.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Bonnet Removed", + "ccc": "Clean Cycle Complete", + "ccp": "Clean Cycle In Progress", + "csf": "Cat Sensor Fault", + "csi": "Cat Sensor Interrupted", + "cst": "Cat Sensor Timing", + "df1": "Drawer Almost Full - 2 Cycles Left", + "df2": "Drawer Almost Full - 1 Cycle Left", + "dfs": "Drawer Full", + "dhf": "Dump + Home Position Fault", + "dpf": "Dump Position Fault", + "ec": "Empty Cycle", + "hpf": "Home Position Fault", + "off": "Off", + "offline": "Offline", + "otf": "Over Torque Fault", + "p": "Paused", + "pd": "Pinch Detect", + "rdy": "Ready", + "scf": "Cat Sensor Fault At Startup", + "sdf": "Drawer Full At Startup", + "spf": "Pinch Detect At Startup" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/vacuum.py b/homeassistant/components/litterrobot/vacuum.py index 0d9a2b1dc3d..dbe51270857 100644 --- a/homeassistant/components/litterrobot/vacuum.py +++ b/homeassistant/components/litterrobot/vacuum.py @@ -162,11 +162,8 @@ class LitterRobotCleaner(LitterRobotControlEntity, StateVacuumEntity): def extra_state_attributes(self) -> dict[str, Any]: """Return device specific state attributes.""" return { - "clean_cycle_wait_time_minutes": self.robot.clean_cycle_wait_time_minutes, "is_sleeping": self.robot.is_sleeping, "sleep_mode_enabled": self.robot.sleep_mode_enabled, "power_status": self.robot.power_status, - "status_code": self.robot.status_code, - "last_seen": self.robot.last_seen, "status": self.status, } From c5460ce4b9d90934825a7e8da9c712c51ddb1fc4 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Fri, 13 May 2022 18:04:58 -0500 Subject: [PATCH 436/930] Fix Sonos idle states (#71756) --- homeassistant/components/sonos/media.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/sonos/media.py b/homeassistant/components/sonos/media.py index e661a8320dc..e3d8f043d4b 100644 --- a/homeassistant/components/sonos/media.py +++ b/homeassistant/components/sonos/media.py @@ -120,6 +120,8 @@ class SonosMedia: self.clear() track_info = self.poll_track_info() + if not track_info["uri"]: + return self.uri = track_info["uri"] audio_source = self.soco.music_source_from_uri(self.uri) From 21b1667de9c38edf43f4ce73d50caab194780361 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sat, 14 May 2022 01:57:19 +0200 Subject: [PATCH 437/930] Adjust Fan Modes in insteon (#71804) --- homeassistant/components/insteon/climate.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/insteon/climate.py b/homeassistant/components/insteon/climate.py index 833180583e2..97e21a02f6f 100644 --- a/homeassistant/components/insteon/climate.py +++ b/homeassistant/components/insteon/climate.py @@ -9,8 +9,7 @@ from homeassistant.components.climate.const import ( ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, DOMAIN as CLIMATE_DOMAIN, - HVAC_MODE_AUTO, - HVAC_MODE_FAN_ONLY, + FAN_AUTO, ClimateEntityFeature, HVACAction, HVACMode, @@ -25,6 +24,8 @@ from .const import SIGNAL_ADD_ENTITIES from .insteon_entity import InsteonEntity from .utils import async_add_insteon_entities +FAN_ONLY = "fan_only" + COOLING = 1 HEATING = 2 DEHUMIDIFYING = 3 @@ -46,7 +47,7 @@ HVAC_MODES = { 2: HVACMode.COOL, 3: HVACMode.HEAT_COOL, } -FAN_MODES = {4: HVAC_MODE_AUTO, 8: HVAC_MODE_FAN_ONLY} +FAN_MODES = {4: FAN_AUTO, 8: FAN_ONLY} async def async_setup_entry( From 4ea6e5dfc0f50adff04e5676edab5c713e472808 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Fri, 13 May 2022 20:05:06 -0400 Subject: [PATCH 438/930] Add config flow to Slack integration (#69880) --- .coveragerc | 1 + CODEOWNERS | 4 +- homeassistant/components/slack/__init__.py | 64 +++++++- homeassistant/components/slack/config_flow.py | 87 +++++++++++ homeassistant/components/slack/const.py | 16 ++ homeassistant/components/slack/manifest.json | 3 +- homeassistant/components/slack/notify.py | 71 ++++----- homeassistant/components/slack/strings.json | 29 ++++ .../components/slack/translations/en.json | 29 ++++ homeassistant/generated/config_flows.py | 1 + tests/components/slack/__init__.py | 73 ++++++++- .../components/slack/fixtures/auth_test.json | 10 ++ tests/components/slack/test_config_flow.py | 140 ++++++++++++++++++ tests/components/slack/test_init.py | 39 +++++ tests/components/slack/test_notify.py | 97 +++--------- 15 files changed, 537 insertions(+), 127 deletions(-) create mode 100644 homeassistant/components/slack/config_flow.py create mode 100644 homeassistant/components/slack/const.py create mode 100644 homeassistant/components/slack/strings.json create mode 100644 homeassistant/components/slack/translations/en.json create mode 100644 tests/components/slack/fixtures/auth_test.json create mode 100644 tests/components/slack/test_config_flow.py create mode 100644 tests/components/slack/test_init.py diff --git a/.coveragerc b/.coveragerc index 7ce0e5ee299..898a14b9c86 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1054,6 +1054,7 @@ omit = homeassistant/components/sky_hub/* homeassistant/components/skybeacon/sensor.py homeassistant/components/skybell/* + homeassistant/components/slack/__init__.py homeassistant/components/slack/notify.py homeassistant/components/sia/__init__.py homeassistant/components/sia/alarm_control_panel.py diff --git a/CODEOWNERS b/CODEOWNERS index d07ce028ba9..68f9017363e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -919,8 +919,8 @@ build.json @home-assistant/supervisor /tests/components/siren/ @home-assistant/core @raman325 /homeassistant/components/sisyphus/ @jkeljo /homeassistant/components/sky_hub/ @rogerselwyn -/homeassistant/components/slack/ @bachya -/tests/components/slack/ @bachya +/homeassistant/components/slack/ @bachya @tkdrob +/tests/components/slack/ @bachya @tkdrob /homeassistant/components/sleepiq/ @mfugate1 @kbickar /tests/components/sleepiq/ @mfugate1 @kbickar /homeassistant/components/slide/ @ualex73 diff --git a/homeassistant/components/slack/__init__.py b/homeassistant/components/slack/__init__.py index e3ae111f2ea..ae52013621f 100644 --- a/homeassistant/components/slack/__init__.py +++ b/homeassistant/components/slack/__init__.py @@ -1,2 +1,62 @@ -"""The slack component.""" -DOMAIN = "slack" +"""The slack integration.""" +import logging + +from aiohttp.client_exceptions import ClientError +from slack import WebClient +from slack.errors import SlackApiError + +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_PLATFORM, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import aiohttp_client, discovery +from homeassistant.helpers.typing import ConfigType + +from .const import DATA_CLIENT, DOMAIN + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS = [Platform.NOTIFY] + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the Slack component.""" + # Iterate all entries for notify to only get Slack + if Platform.NOTIFY in config: + for entry in config[Platform.NOTIFY]: + if entry[CONF_PLATFORM] == DOMAIN: + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data=entry + ) + ) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Slack from a config entry.""" + session = aiohttp_client.async_get_clientsession(hass) + slack = WebClient(token=entry.data[CONF_API_KEY], run_async=True, session=session) + + try: + await slack.auth_test() + except (SlackApiError, ClientError) as ex: + if isinstance(ex, SlackApiError) and ex.response["error"] == "invalid_auth": + _LOGGER.error("Invalid API key") + return False + raise ConfigEntryNotReady("Error while setting up integration") from ex + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data | {DATA_CLIENT: slack} + + hass.async_create_task( + discovery.async_load_platform( + hass, + Platform.NOTIFY, + DOMAIN, + hass.data[DOMAIN][entry.entry_id], + hass.data[DOMAIN], + ) + ) + + return True diff --git a/homeassistant/components/slack/config_flow.py b/homeassistant/components/slack/config_flow.py new file mode 100644 index 00000000000..253750a2310 --- /dev/null +++ b/homeassistant/components/slack/config_flow.py @@ -0,0 +1,87 @@ +"""Config flow for Slack integration.""" +from __future__ import annotations + +import logging + +from slack import WebClient +from slack.errors import SlackApiError +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_API_KEY, CONF_ICON, CONF_NAME, CONF_USERNAME +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import aiohttp_client + +from .const import CONF_DEFAULT_CHANNEL, DOMAIN + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = vol.Schema( + { + vol.Required(CONF_API_KEY): str, + vol.Required(CONF_DEFAULT_CHANNEL): str, + vol.Optional(CONF_ICON): str, + vol.Optional(CONF_USERNAME): str, + } +) + + +class SlackFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Slack.""" + + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: + """Handle a flow initiated by the user.""" + errors = {} + + if user_input is not None: + error, info = await self._async_try_connect(user_input[CONF_API_KEY]) + if error is not None: + errors["base"] = error + elif info is not None: + await self.async_set_unique_id(info["team_id"].lower()) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=user_input.get(CONF_NAME, info["team"]), + data={CONF_NAME: user_input.get(CONF_NAME, info["team"])} + | user_input, + ) + + user_input = user_input or {} + return self.async_show_form( + step_id="user", + data_schema=CONFIG_SCHEMA, + errors=errors, + ) + + async def async_step_import(self, import_config: dict[str, str]) -> FlowResult: + """Import a config entry from configuration.yaml.""" + _LOGGER.warning( + "Configuration of the Slack integration in YAML is deprecated and " + "will be removed in a future release; Your existing configuration " + "has been imported into the UI automatically and can be safely removed " + "from your configuration.yaml file" + ) + entries = self._async_current_entries() + if any(x.data[CONF_API_KEY] == import_config[CONF_API_KEY] for x in entries): + return self.async_abort(reason="already_configured") + return await self.async_step_user(import_config) + + async def _async_try_connect( + self, token: str + ) -> tuple[str, None] | tuple[None, dict[str, str]]: + """Try connecting to Slack.""" + session = aiohttp_client.async_get_clientsession(self.hass) + client = WebClient(token=token, run_async=True, session=session) + + try: + info = await client.auth_test() + except SlackApiError as ex: + if ex.response["error"] == "invalid_auth": + return "invalid_auth", None + return "cannot_connect", None + except Exception as ex: # pylint:disable=broad-except + _LOGGER.exception("Unexpected exception: %s", ex) + return "unknown", None + return None, info diff --git a/homeassistant/components/slack/const.py b/homeassistant/components/slack/const.py new file mode 100644 index 00000000000..b7b5707aeeb --- /dev/null +++ b/homeassistant/components/slack/const.py @@ -0,0 +1,16 @@ +"""Constants for the Slack integration.""" +from typing import Final + +ATTR_BLOCKS = "blocks" +ATTR_BLOCKS_TEMPLATE = "blocks_template" +ATTR_FILE = "file" +ATTR_PASSWORD = "password" +ATTR_PATH = "path" +ATTR_URL = "url" +ATTR_USERNAME = "username" + +CONF_DEFAULT_CHANNEL = "default_channel" + +DATA_CLIENT = "client" +DEFAULT_TIMEOUT_SECONDS = 15 +DOMAIN: Final = "slack" diff --git a/homeassistant/components/slack/manifest.json b/homeassistant/components/slack/manifest.json index d54bb9e0ec6..57c1690e647 100644 --- a/homeassistant/components/slack/manifest.json +++ b/homeassistant/components/slack/manifest.json @@ -1,9 +1,10 @@ { "domain": "slack", "name": "Slack", + "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/slack", "requirements": ["slackclient==2.5.0"], - "codeowners": ["@bachya"], + "codeowners": ["@bachya", "@tkdrob"], "iot_class": "cloud_push", "loggers": ["slack"] } diff --git a/homeassistant/components/slack/notify.py b/homeassistant/components/slack/notify.py index 4dfacda266c..bfcf79ef676 100644 --- a/homeassistant/components/slack/notify.py +++ b/homeassistant/components/slack/notify.py @@ -20,26 +20,32 @@ from homeassistant.components.notify import ( PLATFORM_SCHEMA, BaseNotificationService, ) -from homeassistant.const import ATTR_ICON, CONF_API_KEY, CONF_ICON, CONF_USERNAME +from homeassistant.const import ( + ATTR_ICON, + CONF_API_KEY, + CONF_ICON, + CONF_PATH, + CONF_USERNAME, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import aiohttp_client, config_validation as cv, template from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from .const import ( + ATTR_BLOCKS, + ATTR_BLOCKS_TEMPLATE, + ATTR_FILE, + ATTR_PASSWORD, + ATTR_PATH, + ATTR_URL, + ATTR_USERNAME, + CONF_DEFAULT_CHANNEL, + DATA_CLIENT, +) + _LOGGER = logging.getLogger(__name__) -ATTR_BLOCKS = "blocks" -ATTR_BLOCKS_TEMPLATE = "blocks_template" -ATTR_FILE = "file" -ATTR_PASSWORD = "password" -ATTR_PATH = "path" -ATTR_URL = "url" -ATTR_USERNAME = "username" - -CONF_DEFAULT_CHANNEL = "default_channel" - -DEFAULT_TIMEOUT_SECONDS = 15 - -FILE_PATH_SCHEMA = vol.Schema({vol.Required(ATTR_PATH): cv.isfile}) +FILE_PATH_SCHEMA = vol.Schema({vol.Required(CONF_PATH): cv.isfile}) FILE_URL_SCHEMA = vol.Schema( { @@ -66,6 +72,7 @@ DATA_SCHEMA = vol.All( cv.ensure_list, [vol.Any(DATA_FILE_SCHEMA, DATA_TEXT_ONLY_SCHEMA)] ) +# Deprecated in Home Assistant 2022.5 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_API_KEY): cv.string, @@ -109,27 +116,13 @@ async def async_get_service( discovery_info: DiscoveryInfoType | None = None, ) -> SlackNotificationService | None: """Set up the Slack notification service.""" - session = aiohttp_client.async_get_clientsession(hass) - client = WebClient(token=config[CONF_API_KEY], run_async=True, session=session) - - try: - await client.auth_test() - except SlackApiError as err: - _LOGGER.error("Error while setting up integration: %r", err) + if discovery_info is None: return None - except ClientError as err: - _LOGGER.warning( - "Error testing connection to slack: %r " - "Continuing setup anyway, but notify service might not work", - err, - ) return SlackNotificationService( hass, - client, - config[CONF_DEFAULT_CHANNEL], - username=config.get(CONF_USERNAME), - icon=config.get(CONF_ICON), + discovery_info.pop(DATA_CLIENT), + discovery_info, ) @@ -153,16 +146,12 @@ class SlackNotificationService(BaseNotificationService): self, hass: HomeAssistant, client: WebClient, - default_channel: str, - username: str | None, - icon: str | None, + config: dict[str, str], ) -> None: """Initialize.""" - self._client = client - self._default_channel = default_channel self._hass = hass - self._icon = icon - self._username = username + self._client = client + self._config = config async def _async_send_local_file_message( self, @@ -294,7 +283,7 @@ class SlackNotificationService(BaseNotificationService): title = kwargs.get(ATTR_TITLE) targets = _async_sanitize_channel_names( - kwargs.get(ATTR_TARGET, [self._default_channel]) + kwargs.get(ATTR_TARGET, [self._config[CONF_DEFAULT_CHANNEL]]) ) # Message Type 1: A text-only message @@ -312,8 +301,8 @@ class SlackNotificationService(BaseNotificationService): targets, message, title, - username=data.get(ATTR_USERNAME, self._username), - icon=data.get(ATTR_ICON, self._icon), + username=data.get(ATTR_USERNAME, self._config.get(ATTR_USERNAME)), + icon=data.get(ATTR_ICON, self._config.get(ATTR_ICON)), blocks=blocks, ) diff --git a/homeassistant/components/slack/strings.json b/homeassistant/components/slack/strings.json new file mode 100644 index 00000000000..f14129cf156 --- /dev/null +++ b/homeassistant/components/slack/strings.json @@ -0,0 +1,29 @@ +{ + "config": { + "step": { + "user": { + "description": "Refer to the documentation on getting your Slack API key.", + "data": { + "api_key": "[%key:common::config_flow::data::api_key%]", + "default_channel": "Default Channel", + "icon": "Icon", + "username": "[%key:common::config_flow::data::username%]" + }, + "data_description": { + "api_key": "The Slack API token to use for sending Slack messages.", + "default_channel": "The channel to post to if no channel is specified when sending a message.", + "icon": "Use one of the Slack emojis as an Icon for the supplied username.", + "username": "Home Assistant will post to Slack using the username specified." + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_service%]" + } + } +} diff --git a/homeassistant/components/slack/translations/en.json b/homeassistant/components/slack/translations/en.json new file mode 100644 index 00000000000..5a97fb1938e --- /dev/null +++ b/homeassistant/components/slack/translations/en.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Service is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "api_key": "API Key", + "default_channel": "Default Channel", + "icon": "Icon", + "username": "Username" + }, + "description": "Refer to the documentation on getting your Slack API key.", + "data_description": { + "api_key": "The Slack API token to use for sending Slack messages.", + "default_channel": "The channel to post to if no channel is specified when sending a message.", + "icon": "Use one of the Slack emojis as an Icon for the supplied username.", + "username": "Home Assistant will post to Slack using the username specified." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index ae2ab6339aa..89a9e4e489a 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -306,6 +306,7 @@ FLOWS = { "shopping_list", "sia", "simplisafe", + "slack", "sleepiq", "slimproto", "sma", diff --git a/tests/components/slack/__init__.py b/tests/components/slack/__init__.py index b32ec5ef7b1..6a258ce9027 100644 --- a/tests/components/slack/__init__.py +++ b/tests/components/slack/__init__.py @@ -1 +1,72 @@ -"""Slack notification tests.""" +"""Tests for the Slack integration.""" +from __future__ import annotations + +import json + +from homeassistant.components.slack.const import CONF_DEFAULT_CHANNEL, DOMAIN +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_KEY, CONF_NAME +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, load_fixture +from tests.test_util.aiohttp import AiohttpClientMocker + +AUTH_URL = "https://www.slack.com/api/auth.test" + +TOKEN = "abc123" +TEAM_NAME = "Test Team" +TEAM_ID = "abc123def" + +CONF_INPUT = {CONF_API_KEY: TOKEN, CONF_DEFAULT_CHANNEL: "test_channel"} + +CONF_DATA = CONF_INPUT | {CONF_NAME: TEAM_NAME} + + +def create_entry(hass: HomeAssistant) -> ConfigEntry: + """Add config entry in Home Assistant.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=CONF_DATA, + unique_id=TEAM_ID, + ) + entry.add_to_hass(hass) + return entry + + +def mock_connection( + aioclient_mock: AiohttpClientMocker, error: str | None = None +) -> None: + """Mock connection.""" + if error is not None: + if error == "invalid_auth": + aioclient_mock.post( + AUTH_URL, + text=json.dumps({"ok": False, "error": "invalid_auth"}), + ) + else: + aioclient_mock.post( + AUTH_URL, + text=json.dumps({"ok": False, "error": "cannot_connect"}), + ) + else: + aioclient_mock.post( + AUTH_URL, + text=load_fixture("slack/auth_test.json"), + ) + + +async def async_init_integration( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + skip_setup: bool = False, + error: str | None = None, +) -> ConfigEntry: + """Set up the Slack integration in Home Assistant.""" + entry = create_entry(hass) + mock_connection(aioclient_mock, error) + + if not skip_setup: + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + return entry diff --git a/tests/components/slack/fixtures/auth_test.json b/tests/components/slack/fixtures/auth_test.json new file mode 100644 index 00000000000..b7b6ee3e9bd --- /dev/null +++ b/tests/components/slack/fixtures/auth_test.json @@ -0,0 +1,10 @@ +{ + "ok": true, + "url": "https://newscorp-tech.slack.com/", + "team": "Test Team", + "user": "user.name", + "team_id": "ABC123DEF", + "user_id": "ABCDEF12345", + "enterprise_id": "123ABCDEF", + "is_enterprise_install": false +} diff --git a/tests/components/slack/test_config_flow.py b/tests/components/slack/test_config_flow.py new file mode 100644 index 00000000000..850690783e8 --- /dev/null +++ b/tests/components/slack/test_config_flow.py @@ -0,0 +1,140 @@ +"""Test Slack config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.slack.const import DOMAIN +from homeassistant.core import HomeAssistant + +from . import CONF_DATA, CONF_INPUT, TEAM_NAME, create_entry, mock_connection + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_flow_user( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test user initialized flow.""" + mock_connection(aioclient_mock) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=CONF_INPUT, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == TEAM_NAME + assert result["data"] == CONF_DATA + + +async def test_flow_user_already_configured( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test user initialized flow with duplicate server.""" + create_entry(hass) + mock_connection(aioclient_mock) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=CONF_INPUT, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_flow_user_invalid_auth( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test user initialized flow with invalid token.""" + mock_connection(aioclient_mock, "invalid_auth") + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=CONF_DATA, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "invalid_auth"} + + +async def test_flow_user_cannot_connect( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test user initialized flow with unreachable server.""" + mock_connection(aioclient_mock, "cannot_connect") + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=CONF_DATA, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "cannot_connect"} + + +async def test_flow_user_unknown_error(hass: HomeAssistant) -> None: + """Test user initialized flow with unreachable server.""" + with patch( + "homeassistant.components.slack.config_flow.WebClient.auth_test" + ) as mock: + mock.side_effect = Exception + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=CONF_DATA, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + assert result["errors"] == {"base": "unknown"} + + +async def test_flow_import( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test an import flow.""" + mock_connection(aioclient_mock) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data=CONF_DATA, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == TEAM_NAME + assert result["data"] == CONF_DATA + + +async def test_flow_import_no_name( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test import flow with no name in config.""" + mock_connection(aioclient_mock) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data=CONF_INPUT, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == TEAM_NAME + assert result["data"] == CONF_DATA + + +async def test_flow_import_already_configured( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test an import flow already configured.""" + create_entry(hass) + mock_connection(aioclient_mock) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data=CONF_DATA, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" diff --git a/tests/components/slack/test_init.py b/tests/components/slack/test_init.py new file mode 100644 index 00000000000..487a65b8ef0 --- /dev/null +++ b/tests/components/slack/test_init.py @@ -0,0 +1,39 @@ +"""Test Slack integration.""" +from homeassistant.components.slack.const import DOMAIN +from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.core import HomeAssistant + +from . import CONF_DATA, async_init_integration + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_setup(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None: + """Test Slack setup.""" + entry: ConfigEntry = await async_init_integration(hass, aioclient_mock) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert entry.data == CONF_DATA + + +async def test_async_setup_entry_not_ready( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test that it throws ConfigEntryNotReady when exception occurs during setup.""" + entry: ConfigEntry = await async_init_integration( + hass, aioclient_mock, error="cannot_connect" + ) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_async_setup_entry_invalid_auth( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test invalid auth during setup.""" + entry: ConfigEntry = await async_init_integration( + hass, aioclient_mock, error="invalid_auth" + ) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.SETUP_ERROR diff --git a/tests/components/slack/test_notify.py b/tests/components/slack/test_notify.py index f10673cced4..b5fa08fa54f 100644 --- a/tests/components/slack/test_notify.py +++ b/tests/components/slack/test_notify.py @@ -1,13 +1,10 @@ """Test slack notifications.""" from __future__ import annotations -import copy import logging -from unittest.mock import AsyncMock, Mock, patch +from unittest.mock import AsyncMock, Mock from _pytest.logging import LogCaptureFixture -import aiohttp -from slack.errors import SlackApiError from homeassistant.components import notify from homeassistant.components.slack import DOMAIN @@ -15,15 +12,9 @@ from homeassistant.components.slack.notify import ( CONF_DEFAULT_CHANNEL, SlackNotificationService, ) -from homeassistant.const import ( - CONF_API_KEY, - CONF_ICON, - CONF_NAME, - CONF_PLATFORM, - CONF_USERNAME, -) -from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component +from homeassistant.const import ATTR_ICON, CONF_API_KEY, CONF_NAME, CONF_PLATFORM + +from . import CONF_DATA MODULE_PATH = "homeassistant.components.slack.notify" SERVICE_NAME = f"notify_{DOMAIN}" @@ -47,74 +38,14 @@ def filter_log_records(caplog: LogCaptureFixture) -> list[logging.LogRecord]: ] -async def test_setup(hass: HomeAssistant, caplog: LogCaptureFixture): - """Test setup slack notify.""" - config = DEFAULT_CONFIG - - with patch( - MODULE_PATH + ".aiohttp_client", - **{"async_get_clientsession.return_value": (session := Mock())}, - ), patch( - MODULE_PATH + ".WebClient", - return_value=(client := AsyncMock()), - ) as mock_client: - - await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert hass.services.has_service(notify.DOMAIN, SERVICE_NAME) - caplog_records_slack = filter_log_records(caplog) - assert len(caplog_records_slack) == 0 - mock_client.assert_called_with(token="12345", run_async=True, session=session) - client.auth_test.assert_called_once_with() - - -async def test_setup_clientError(hass: HomeAssistant, caplog: LogCaptureFixture): - """Test setup slack notify with aiohttp.ClientError exception.""" - config = copy.deepcopy(DEFAULT_CONFIG) - config[notify.DOMAIN][0].update({CONF_USERNAME: "user", CONF_ICON: "icon"}) - - with patch( - MODULE_PATH + ".aiohttp_client", - **{"async_get_clientsession.return_value": Mock()}, - ), patch(MODULE_PATH + ".WebClient", return_value=(client := AsyncMock())): - - client.auth_test.side_effect = [aiohttp.ClientError] - await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert hass.services.has_service(notify.DOMAIN, SERVICE_NAME) - caplog_records_slack = filter_log_records(caplog) - assert len(caplog_records_slack) == 1 - record = caplog_records_slack[0] - assert record.levelno == logging.WARNING - assert aiohttp.ClientError.__qualname__ in record.message - - -async def test_setup_slackApiError(hass: HomeAssistant, caplog: LogCaptureFixture): - """Test setup slack notify with SlackApiError exception.""" - config = DEFAULT_CONFIG - - with patch( - MODULE_PATH + ".aiohttp_client", - **{"async_get_clientsession.return_value": Mock()}, - ), patch(MODULE_PATH + ".WebClient", return_value=(client := AsyncMock())): - - client.auth_test.side_effect = [err := SlackApiError("msg", "resp")] - await async_setup_component(hass, notify.DOMAIN, config) - await hass.async_block_till_done() - assert hass.services.has_service(notify.DOMAIN, SERVICE_NAME) is False - caplog_records_slack = filter_log_records(caplog) - assert len(caplog_records_slack) == 1 - record = caplog_records_slack[0] - assert record.levelno == logging.ERROR - assert err.__class__.__qualname__ in record.message - - async def test_message_includes_default_emoji(): """Tests that default icon is used when no message icon is given.""" mock_client = Mock() mock_client.chat_postMessage = AsyncMock() expected_icon = ":robot_face:" - service = SlackNotificationService(None, mock_client, "_", "_", expected_icon) + service = SlackNotificationService( + None, mock_client, CONF_DATA | {ATTR_ICON: expected_icon} + ) await service.async_send_message("test") @@ -128,7 +59,9 @@ async def test_message_emoji_overrides_default(): """Tests that overriding the default icon emoji when sending a message works.""" mock_client = Mock() mock_client.chat_postMessage = AsyncMock() - service = SlackNotificationService(None, mock_client, "_", "_", "default_icon") + service = SlackNotificationService( + None, mock_client, CONF_DATA | {ATTR_ICON: "default_icon"} + ) expected_icon = ":new:" await service.async_send_message("test", data={"icon": expected_icon}) @@ -144,7 +77,9 @@ async def test_message_includes_default_icon_url(): mock_client = Mock() mock_client.chat_postMessage = AsyncMock() expected_icon = "https://example.com/hass.png" - service = SlackNotificationService(None, mock_client, "_", "_", expected_icon) + service = SlackNotificationService( + None, mock_client, CONF_DATA | {ATTR_ICON: expected_icon} + ) await service.async_send_message("test") @@ -158,10 +93,12 @@ async def test_message_icon_url_overrides_default(): """Tests that overriding the default icon url when sending a message works.""" mock_client = Mock() mock_client.chat_postMessage = AsyncMock() - service = SlackNotificationService(None, mock_client, "_", "_", "default_icon") + service = SlackNotificationService( + None, mock_client, CONF_DATA | {ATTR_ICON: "default_icon"} + ) expected_icon = "https://example.com/hass.png" - await service.async_send_message("test", data={"icon": expected_icon}) + await service.async_send_message("test", data={ATTR_ICON: expected_icon}) mock_fn = mock_client.chat_postMessage mock_fn.assert_called_once() From e8a8d352898bd025f0fd57f5c574b5f6ef4c0309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 14 May 2022 02:23:18 +0200 Subject: [PATCH 439/930] Add Sensors for Airzone WebServer (#69748) --- homeassistant/components/airzone/entity.py | 67 +++++++++++++++------- homeassistant/components/airzone/sensor.py | 61 +++++++++++++++++++- tests/components/airzone/test_sensor.py | 11 +++- tests/components/airzone/util.py | 3 +- 4 files changed, 116 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/airzone/entity.py b/homeassistant/components/airzone/entity.py index 687d7873ece..f697a364bc8 100644 --- a/homeassistant/components/airzone/entity.py +++ b/homeassistant/components/airzone/entity.py @@ -7,16 +7,19 @@ from aioairzone.const import ( AZD_FIRMWARE, AZD_FULL_NAME, AZD_ID, + AZD_MAC, AZD_MODEL, AZD_NAME, AZD_SYSTEM, AZD_SYSTEMS, AZD_THERMOSTAT_FW, AZD_THERMOSTAT_MODEL, + AZD_WEBSERVER, AZD_ZONES, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -46,17 +49,15 @@ class AirzoneSystemEntity(AirzoneEntity): self.system_id = system_data[AZD_ID] - self._attr_device_info: DeviceInfo = { - "identifiers": {(DOMAIN, f"{entry.entry_id}_{self.system_id}")}, - "manufacturer": MANUFACTURER, - "model": self.get_airzone_value(AZD_MODEL), - "name": self.get_airzone_value(AZD_FULL_NAME), - "sw_version": self.get_airzone_value(AZD_FIRMWARE), - "via_device": (DOMAIN, f"{entry.entry_id}_ws"), - } - self._attr_unique_id = ( - entry.entry_id if entry.unique_id is None else entry.unique_id + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{entry.entry_id}_{self.system_id}")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_MODEL), + name=self.get_airzone_value(AZD_FULL_NAME), + sw_version=self.get_airzone_value(AZD_FIRMWARE), + via_device=(DOMAIN, f"{entry.entry_id}_ws"), ) + self._attr_unique_id = entry.unique_id or entry.entry_id def get_airzone_value(self, key: str) -> Any: """Return system value by key.""" @@ -67,6 +68,34 @@ class AirzoneSystemEntity(AirzoneEntity): return value +class AirzoneWebServerEntity(AirzoneEntity): + """Define an Airzone WebServer entity.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator) + + mac = self.get_airzone_value(AZD_MAC) + + self._attr_device_info = DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, mac)}, + identifiers={(DOMAIN, f"{entry.entry_id}_ws")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_MODEL), + name=self.get_airzone_value(AZD_FULL_NAME), + sw_version=self.get_airzone_value(AZD_FIRMWARE), + ) + self._attr_unique_id = entry.unique_id or entry.entry_id + + def get_airzone_value(self, key: str) -> Any: + """Return system value by key.""" + return self.coordinator.data[AZD_WEBSERVER].get(key) + + class AirzoneZoneEntity(AirzoneEntity): """Define an Airzone Zone entity.""" @@ -84,17 +113,15 @@ class AirzoneZoneEntity(AirzoneEntity): self.system_zone_id = system_zone_id self.zone_id = zone_data[AZD_ID] - self._attr_device_info: DeviceInfo = { - "identifiers": {(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, - "manufacturer": MANUFACTURER, - "model": self.get_airzone_value(AZD_THERMOSTAT_MODEL), - "name": f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", - "sw_version": self.get_airzone_value(AZD_THERMOSTAT_FW), - "via_device": (DOMAIN, f"{entry.entry_id}_{self.system_id}"), - } - self._attr_unique_id = ( - entry.entry_id if entry.unique_id is None else entry.unique_id + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, f"{entry.entry_id}_{system_zone_id}")}, + manufacturer=MANUFACTURER, + model=self.get_airzone_value(AZD_THERMOSTAT_MODEL), + name=f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}", + sw_version=self.get_airzone_value(AZD_THERMOSTAT_FW), + via_device=(DOMAIN, f"{entry.entry_id}_{self.system_id}"), ) + self._attr_unique_id = entry.unique_id or entry.entry_id def get_airzone_value(self, key: str) -> Any: """Return zone value by key.""" diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index 3e81f5df094..1671eb919a7 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -3,7 +3,15 @@ from __future__ import annotations from typing import Any, Final -from aioairzone.const import AZD_HUMIDITY, AZD_NAME, AZD_TEMP, AZD_TEMP_UNIT, AZD_ZONES +from aioairzone.const import ( + AZD_HUMIDITY, + AZD_NAME, + AZD_TEMP, + AZD_TEMP_UNIT, + AZD_WEBSERVER, + AZD_WIFI_RSSI, + AZD_ZONES, +) from homeassistant.components.sensor import ( SensorDeviceClass, @@ -12,13 +20,30 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.const import ( + PERCENTAGE, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + TEMP_CELSIUS, +) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS from .coordinator import AirzoneUpdateCoordinator -from .entity import AirzoneEntity, AirzoneZoneEntity +from .entity import AirzoneEntity, AirzoneWebServerEntity, AirzoneZoneEntity + +WEBSERVER_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( + SensorEntityDescription( + device_class=SensorDeviceClass.SIGNAL_STRENGTH, + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + key=AZD_WIFI_RSSI, + name="RSSI", + native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + state_class=SensorStateClass.MEASUREMENT, + ), +) ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( @@ -45,6 +70,19 @@ async def async_setup_entry( coordinator = hass.data[DOMAIN][entry.entry_id] sensors: list[AirzoneSensor] = [] + + if AZD_WEBSERVER in coordinator.data: + ws_data = coordinator.data[AZD_WEBSERVER] + for description in WEBSERVER_SENSOR_TYPES: + if description.key in ws_data: + sensors.append( + AirzoneWebServerSensor( + coordinator, + description, + entry, + ) + ) + for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items(): for description in ZONE_SENSOR_TYPES: if description.key in zone_data: @@ -70,6 +108,23 @@ class AirzoneSensor(AirzoneEntity, SensorEntity): self._attr_native_value = self.get_airzone_value(self.entity_description.key) +class AirzoneWebServerSensor(AirzoneWebServerEntity, AirzoneSensor): + """Define an Airzone WebServer sensor.""" + + def __init__( + self, + coordinator: AirzoneUpdateCoordinator, + description: SensorEntityDescription, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry) + self._attr_name = f"WebServer {description.name}" + self._attr_unique_id = f"{self._attr_unique_id}_ws_{description.key}" + self.entity_description = description + self._async_update_attrs() + + class AirzoneZoneSensor(AirzoneZoneEntity, AirzoneSensor): """Define an Airzone Zone sensor.""" diff --git a/tests/components/airzone/test_sensor.py b/tests/components/airzone/test_sensor.py index c68be2abbab..bd57129cae0 100644 --- a/tests/components/airzone/test_sensor.py +++ b/tests/components/airzone/test_sensor.py @@ -1,15 +1,24 @@ """The sensor tests for the Airzone platform.""" +from unittest.mock import AsyncMock + from homeassistant.core import HomeAssistant from .util import async_init_integration -async def test_airzone_create_sensors(hass: HomeAssistant) -> None: +async def test_airzone_create_sensors( + hass: HomeAssistant, entity_registry_enabled_by_default: AsyncMock +) -> None: """Test creation of sensors.""" await async_init_integration(hass) + # WebServer + state = hass.states.get("sensor.webserver_rssi") + assert state.state == "-42" + + # Zones state = hass.states.get("sensor.despacho_temperature") assert state.state == "21.2" diff --git a/tests/components/airzone/util.py b/tests/components/airzone/util.py index ede4bcc3b53..6b81c493eb6 100644 --- a/tests/components/airzone/util.py +++ b/tests/components/airzone/util.py @@ -34,7 +34,6 @@ from aioairzone.const import ( API_WIFI_RSSI, API_ZONE_ID, ) -from aioairzone.exceptions import InvalidMethod from homeassistant.components.airzone import DOMAIN from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT @@ -219,7 +218,7 @@ async def async_init_integration( return_value=HVAC_SYSTEMS_MOCK, ), patch( "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", - side_effect=InvalidMethod, + return_value=HVAC_WEBSERVER_MOCK, ): await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() From abe78b1212602d8b19562d6acc0adf9361302327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Sat, 14 May 2022 02:24:29 +0200 Subject: [PATCH 440/930] Add QNAP QSW Button platform (#70980) Co-authored-by: J. Nick Koston --- homeassistant/components/qnap_qsw/__init__.py | 2 +- homeassistant/components/qnap_qsw/button.py | 77 +++++++++++++++++++ homeassistant/components/qnap_qsw/const.py | 1 + tests/components/qnap_qsw/test_button.py | 37 +++++++++ tests/components/qnap_qsw/util.py | 6 ++ 5 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/qnap_qsw/button.py create mode 100644 tests/components/qnap_qsw/test_button.py diff --git a/homeassistant/components/qnap_qsw/__init__.py b/homeassistant/components/qnap_qsw/__init__.py index 838a567199d..26ed8066686 100644 --- a/homeassistant/components/qnap_qsw/__init__.py +++ b/homeassistant/components/qnap_qsw/__init__.py @@ -11,7 +11,7 @@ from homeassistant.helpers import aiohttp_client from .const import DOMAIN from .coordinator import QswUpdateCoordinator -PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/qnap_qsw/button.py b/homeassistant/components/qnap_qsw/button.py new file mode 100644 index 00000000000..1c13310fe05 --- /dev/null +++ b/homeassistant/components/qnap_qsw/button.py @@ -0,0 +1,77 @@ +"""Support for the QNAP QSW buttons.""" +from __future__ import annotations + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Final + +from aioqsw.localapi import QnapQswApi + +from homeassistant.components.button import ( + ButtonDeviceClass, + ButtonEntity, + ButtonEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN, QSW_REBOOT +from .coordinator import QswUpdateCoordinator +from .entity import QswEntity + + +@dataclass +class QswButtonDescriptionMixin: + """Mixin to describe a Button entity.""" + + press_action: Callable[[QnapQswApi], Awaitable[bool]] + + +@dataclass +class QswButtonDescription(ButtonEntityDescription, QswButtonDescriptionMixin): + """Class to describe a Button entity.""" + + +BUTTON_TYPES: Final[tuple[QswButtonDescription, ...]] = ( + QswButtonDescription( + device_class=ButtonDeviceClass.RESTART, + entity_category=EntityCategory.CONFIG, + key=QSW_REBOOT, + name="Reboot", + press_action=lambda qsw: qsw.reboot(), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Add QNAP QSW buttons from a config_entry.""" + coordinator: QswUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + QswButton(coordinator, description, entry) for description in BUTTON_TYPES + ) + + +class QswButton(QswEntity, ButtonEntity): + """Define a QNAP QSW button.""" + + entity_description: QswButtonDescription + + def __init__( + self, + coordinator: QswUpdateCoordinator, + description: QswButtonDescription, + entry: ConfigEntry, + ) -> None: + """Initialize.""" + super().__init__(coordinator, entry) + self._attr_name = f"{self.product} {description.name}" + self._attr_unique_id = f"{entry.unique_id}_{description.key}" + self.entity_description = description + + async def async_press(self) -> None: + """Triggers the QNAP QSW button action.""" + await self.entity_description.press_action(self.coordinator.qsw) diff --git a/homeassistant/components/qnap_qsw/const.py b/homeassistant/components/qnap_qsw/const.py index a6cacfd1c40..e583c0250f4 100644 --- a/homeassistant/components/qnap_qsw/const.py +++ b/homeassistant/components/qnap_qsw/const.py @@ -10,4 +10,5 @@ MANUFACTURER: Final = "QNAP" RPM: Final = "rpm" +QSW_REBOOT = "reboot" QSW_TIMEOUT_SEC: Final = 25 diff --git a/tests/components/qnap_qsw/test_button.py b/tests/components/qnap_qsw/test_button.py new file mode 100644 index 00000000000..5423c9686d4 --- /dev/null +++ b/tests/components/qnap_qsw/test_button.py @@ -0,0 +1,37 @@ +"""The sensor tests for the QNAP QSW platform.""" + +from unittest.mock import patch + +from homeassistant.components.button.const import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN +from homeassistant.core import HomeAssistant + +from .util import SYSTEM_COMMAND_MOCK, USERS_VERIFICATION_MOCK, async_init_integration + + +async def test_qnap_buttons(hass: HomeAssistant) -> None: + """Test buttons.""" + + await async_init_integration(hass) + + state = hass.states.get("button.qsw_m408_4c_reboot") + assert state + assert state.state == STATE_UNKNOWN + + with patch( + "homeassistant.components.qnap_qsw.QnapQswApi.get_users_verification", + return_value=USERS_VERIFICATION_MOCK, + ) as mock_users_verification, patch( + "homeassistant.components.qnap_qsw.QnapQswApi.post_system_command", + return_value=SYSTEM_COMMAND_MOCK, + ) as mock_post_system_command: + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.qsw_m408_4c_reboot"}, + blocking=True, + ) + await hass.async_block_till_done() + + mock_users_verification.assert_called_once() + mock_post_system_command.assert_called_once() diff --git a/tests/components/qnap_qsw/util.py b/tests/components/qnap_qsw/util.py index 57b7b61a59d..28e7f7881d5 100644 --- a/tests/components/qnap_qsw/util.py +++ b/tests/components/qnap_qsw/util.py @@ -90,6 +90,12 @@ FIRMWARE_INFO_MOCK = { }, } +SYSTEM_COMMAND_MOCK = { + API_ERROR_CODE: 200, + API_ERROR_MESSAGE: "OK", + API_RESULT: "None", +} + SYSTEM_SENSOR_MOCK = { API_ERROR_CODE: 200, API_ERROR_MESSAGE: "OK", From a8f1dda004c2f32f2c29e95238f08a7bf3c1b7f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 May 2022 20:26:09 -0400 Subject: [PATCH 441/930] Use ciso8601 for parsing MySQLdb datetimes (#71818) * Use ciso8601 for parsing MySQLDB datetimes The default parser is this: https://github.com/PyMySQL/mysqlclient/blob/5340191feb16b1f99a7b43fe7c74a2f690138cb1/MySQLdb/times.py#L66 * tweak * tweak * add coverage for building the MySQLdb connect conv param --- homeassistant/components/recorder/const.py | 1 + homeassistant/components/recorder/core.py | 10 +++++++++ homeassistant/components/recorder/util.py | 25 ++++++++++++++++++++++ tests/components/recorder/test_util.py | 19 +++++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/recorder/const.py b/homeassistant/components/recorder/const.py index 5d650ec83e2..e558d19b530 100644 --- a/homeassistant/components/recorder/const.py +++ b/homeassistant/components/recorder/const.py @@ -10,6 +10,7 @@ from homeassistant.helpers.json import JSONEncoder DATA_INSTANCE = "recorder_instance" SQLITE_URL_PREFIX = "sqlite://" +MYSQLDB_URL_PREFIX = "mysql://" DOMAIN = "recorder" CONF_DB_INTEGRITY_CHECK = "db_integrity_check" diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index a008ae6767b..ba0e5ba17d1 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -41,6 +41,7 @@ from .const import ( DB_WORKER_PREFIX, KEEPALIVE_TIME, MAX_QUEUE_BACKLOG, + MYSQLDB_URL_PREFIX, SQLITE_URL_PREFIX, SupportedDialect, ) @@ -77,6 +78,7 @@ from .tasks import ( WaitTask, ) from .util import ( + build_mysqldb_conv, dburl_to_path, end_incomplete_runs, is_second_sunday, @@ -1014,6 +1016,14 @@ class Recorder(threading.Thread): kwargs["pool_reset_on_return"] = None elif self.db_url.startswith(SQLITE_URL_PREFIX): kwargs["poolclass"] = RecorderPool + elif self.db_url.startswith(MYSQLDB_URL_PREFIX): + # If they have configured MySQLDB but don't have + # the MySQLDB module installed this will throw + # an ImportError which we suppress here since + # sqlalchemy will give them a better error when + # it tried to import it below. + with contextlib.suppress(ImportError): + kwargs["connect_args"] = {"conv": build_mysqldb_conv()} else: kwargs["echo"] = False diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index 9f6bef86a29..f48a6126ea9 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -15,6 +15,7 @@ from awesomeversion import ( AwesomeVersionException, AwesomeVersionStrategy, ) +import ciso8601 from sqlalchemy import text from sqlalchemy.engine.cursor import CursorFetchStrategy from sqlalchemy.exc import OperationalError, SQLAlchemyError @@ -331,6 +332,30 @@ def _extract_version_from_server_response( return None +def _datetime_or_none(value: str) -> datetime | None: + """Fast version of mysqldb DateTime_or_None. + + https://github.com/PyMySQL/mysqlclient/blob/v2.1.0/MySQLdb/times.py#L66 + """ + try: + return ciso8601.parse_datetime(value) + except ValueError: + return None + + +def build_mysqldb_conv() -> dict: + """Build a MySQLDB conv dict that uses cisco8601 to parse datetimes.""" + # Late imports since we only call this if they are using mysqldb + from MySQLdb.constants import ( # pylint: disable=import-outside-toplevel,import-error + FIELD_TYPE, + ) + from MySQLdb.converters import ( # pylint: disable=import-outside-toplevel,import-error + conversions, + ) + + return {**conversions, FIELD_TYPE.DATETIME: _datetime_or_none} + + def setup_connection_for_dialect( instance: Recorder, dialect_name: str, diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index 6b1093ee038..237030c1186 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta import os import sqlite3 -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, Mock, patch import pytest from sqlalchemy import text @@ -641,3 +641,20 @@ def test_is_second_sunday(): assert is_second_sunday(datetime(2022, 5, 8, 0, 0, 0, tzinfo=dt_util.UTC)) is True assert is_second_sunday(datetime(2022, 1, 10, 0, 0, 0, tzinfo=dt_util.UTC)) is False + + +def test_build_mysqldb_conv(): + """Test building the MySQLdb connect conv param.""" + mock_converters = Mock(conversions={"original": "preserved"}) + mock_constants = Mock(FIELD_TYPE=Mock(DATETIME="DATETIME")) + with patch.dict( + "sys.modules", + **{"MySQLdb.constants": mock_constants, "MySQLdb.converters": mock_converters}, + ): + conv = util.build_mysqldb_conv() + + assert conv["original"] == "preserved" + assert conv["DATETIME"]("INVALID") is None + assert conv["DATETIME"]("2022-05-13T22:33:12.741") == datetime( + 2022, 5, 13, 22, 33, 12, 741000, tzinfo=None + ) From 0bc843c133468f535f0168eef88850358dc1730a Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 14 May 2022 02:40:34 +0200 Subject: [PATCH 442/930] Add remaining tests for Sensibo (#71764) * Sensibo remaining tests * Use fixture for enable entity --- .coveragerc | 4 - tests/components/sensibo/fixtures/data.json | 2 +- tests/components/sensibo/response.py | 400 -------------------- tests/components/sensibo/test_entity.py | 27 +- tests/components/sensibo/test_number.py | 101 +++++ tests/components/sensibo/test_select.py | 163 ++++++++ tests/components/sensibo/test_sensor.py | 50 +++ tests/components/sensibo/test_update.py | 47 +++ 8 files changed, 367 insertions(+), 427 deletions(-) delete mode 100644 tests/components/sensibo/response.py create mode 100644 tests/components/sensibo/test_number.py create mode 100644 tests/components/sensibo/test_select.py create mode 100644 tests/components/sensibo/test_sensor.py create mode 100644 tests/components/sensibo/test_update.py diff --git a/.coveragerc b/.coveragerc index 898a14b9c86..2a8e6cdec9a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1020,10 +1020,6 @@ omit = homeassistant/components/senseme/fan.py homeassistant/components/senseme/light.py homeassistant/components/senseme/switch.py - homeassistant/components/sensibo/number.py - homeassistant/components/sensibo/select.py - homeassistant/components/sensibo/sensor.py - homeassistant/components/sensibo/update.py homeassistant/components/senz/__init__.py homeassistant/components/senz/api.py homeassistant/components/senz/climate.py diff --git a/tests/components/sensibo/fixtures/data.json b/tests/components/sensibo/fixtures/data.json index c75423fe464..c787ea5592c 100644 --- a/tests/components/sensibo/fixtures/data.json +++ b/tests/components/sensibo/fixtures/data.json @@ -61,7 +61,7 @@ "firmwareType": "esp8266ex", "productModel": "skyv2", "configGroup": "stable", - "currentlyAvailableFirmwareVersion": "SKY30046", + "currentlyAvailableFirmwareVersion": "SKY30048", "cleanFiltersNotificationEnabled": false, "shouldShowFilterCleaningNotification": false, "isGeofenceOnExitEnabled": false, diff --git a/tests/components/sensibo/response.py b/tests/components/sensibo/response.py deleted file mode 100644 index e6b39b81881..00000000000 --- a/tests/components/sensibo/response.py +++ /dev/null @@ -1,400 +0,0 @@ -"""Test api response for the Sensibo integration.""" -from __future__ import annotations - -from pysensibo.model import MotionSensor, SensiboData, SensiboDevice - -DATA_FROM_API = SensiboData( - raw={ - "status": "success", - "result": [ - { - "id": "ABC999111", - "qrId": "AAAAAAAAAA", - "room": {"uid": "99TT99TT", "name": "Hallway", "icon": "Lounge"}, - "acState": { - "timestamp": { - "time": "2022-04-30T19:58:15.544787Z", - "secondsAgo": 0, - }, - "on": False, - "mode": "fan", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "location": { - "id": "ZZZZZZZZZZZZ", - "name": "Home", - "latLon": [58.9806976, 20.5864297], - "address": ["Sealand 99", "Some county"], - "country": "United Country", - "createTime": { - "time": "2020-03-21T15:44:15Z", - "secondsAgo": 66543240, - }, - "updateTime": None, - "features": [], - "geofenceTriggerRadius": 200, - "subscription": None, - "technician": None, - "shareAnalytics": False, - "occupancy": "n/a", - }, - "accessPoint": {"ssid": "SENSIBO-I-99999", "password": None}, - "macAddress": "00:02:00:B6:00:00", - "autoOffMinutes": None, - "autoOffEnabled": False, - "antiMoldTimer": None, - "antiMoldConfig": None, - } - ], - }, - parsed={ - "ABC999111": SensiboDevice( - id="ABC999111", - mac="00:02:00:B6:00:00", - name="Hallway", - ac_states={ - "timestamp": {"time": "2022-04-30T19:58:15.544787Z", "secondsAgo": 0}, - "on": False, - "mode": "heat", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - temp=22.4, - humidity=38, - target_temp=25, - hvac_mode="heat", - device_on=True, - fan_mode="high", - swing_mode="stopped", - horizontal_swing_mode="stopped", - light_mode="on", - available=True, - hvac_modes=["cool", "heat", "dry", "auto", "fan", "off"], - fan_modes=["quiet", "low", "medium"], - swing_modes=[ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - horizontal_swing_modes=[ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - light_modes=["on", "off"], - temp_unit="C", - temp_list=[18, 19, 20], - temp_step=1, - active_features=[ - "timestamp", - "on", - "mode", - "fanLevel", - "swing", - "targetTemperature", - "horizontalSwing", - "light", - ], - full_features={ - "targetTemperature", - "fanLevel", - "swing", - "horizontalSwing", - "light", - }, - state="heat", - fw_ver="SKY30046", - fw_ver_available="SKY30046", - fw_type="esp8266ex", - model="skyv2", - calibration_temp=0.1, - calibration_hum=0.1, - full_capabilities={ - "modes": { - "cool": { - "temperatures": { - "F": { - "isNative": False, - "values": [ - 64, - 66, - 68, - ], - }, - "C": { - "isNative": True, - "values": [ - 18, - 19, - 20, - ], - }, - }, - "fanLevels": [ - "quiet", - "low", - "medium", - ], - "swing": [ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - "horizontalSwing": [ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - "light": ["on", "off"], - }, - "heat": { - "temperatures": { - "F": { - "isNative": False, - "values": [ - 63, - 64, - 66, - ], - }, - "C": { - "isNative": True, - "values": [ - 17, - 18, - 19, - ], - }, - }, - "fanLevels": ["quiet", "low", "medium"], - "swing": [ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - "horizontalSwing": [ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - "light": ["on", "off"], - }, - "dry": { - "temperatures": { - "F": { - "isNative": False, - "values": [ - 64, - 66, - 68, - ], - }, - "C": { - "isNative": True, - "values": [ - 18, - 19, - 20, - ], - }, - }, - "swing": [ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - "horizontalSwing": [ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - "light": ["on", "off"], - }, - "auto": { - "temperatures": { - "F": { - "isNative": False, - "values": [ - 64, - 66, - 68, - ], - }, - "C": { - "isNative": True, - "values": [ - 18, - 19, - 20, - ], - }, - }, - "fanLevels": [ - "quiet", - "low", - "medium", - ], - "swing": [ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - "horizontalSwing": [ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - "light": ["on", "off"], - }, - "fan": { - "temperatures": {}, - "fanLevels": [ - "quiet", - "low", - ], - "swing": [ - "stopped", - "fixedTop", - "fixedMiddleTop", - ], - "horizontalSwing": [ - "stopped", - "fixedLeft", - "fixedCenterLeft", - ], - "light": ["on", "off"], - }, - } - }, - motion_sensors={ - "AABBCC": MotionSensor( - id="AABBCC", - alive=True, - motion=True, - fw_ver="V17", - fw_type="nrf52", - is_main_sensor=True, - battery_voltage=3000, - humidity=57, - temperature=23.9, - model="motion_sensor", - rssi=-72, - ) - }, - pm25=None, - room_occupied=True, - update_available=False, - schedules={}, - pure_boost_enabled=None, - pure_sensitivity=None, - pure_ac_integration=None, - pure_geo_integration=None, - pure_measure_integration=None, - timer_on=False, - timer_id=None, - timer_state_on=None, - timer_time=None, - smart_on=False, - smart_type="temperature", - smart_low_temp_threshold=0.0, - smart_high_temp_threshold=27.5, - smart_low_state={ - "on": True, - "targetTemperature": 21, - "temperatureUnit": "C", - "mode": "heat", - "fanLevel": "low", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - smart_high_state={ - "on": True, - "targetTemperature": 21, - "temperatureUnit": "C", - "mode": "cool", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - filter_clean=False, - filter_last_reset="2022-03-12T15:24:26Z", - ), - "AAZZAAZZ": SensiboDevice( - id="AAZZAAZZ", - mac="00:01:00:01:00:01", - name="Kitchen", - ac_states={ - "timestamp": {"time": "2022-04-30T19:58:15.568753Z", "secondsAgo": 0}, - "on": False, - "mode": "fan", - "fanLevel": "low", - "light": "on", - }, - temp=None, - humidity=None, - target_temp=None, - hvac_mode="off", - device_on=False, - fan_mode="low", - swing_mode=None, - horizontal_swing_mode=None, - light_mode="on", - available=True, - hvac_modes=["fan", "off"], - fan_modes=["low", "high"], - swing_modes=None, - horizontal_swing_modes=None, - light_modes=["on", "dim", "off"], - temp_unit="C", - temp_list=[0, 1], - temp_step=1, - active_features=["timestamp", "on", "mode", "fanLevel", "light"], - full_features={"light", "targetTemperature", "fanLevel"}, - state="off", - fw_ver="PUR00111", - fw_ver_available="PUR00111", - fw_type="pure-esp32", - model="pure", - calibration_temp=0.0, - calibration_hum=0.0, - full_capabilities={ - "modes": { - "fan": { - "temperatures": {}, - "fanLevels": ["low", "high"], - "light": ["on", "dim", "off"], - } - } - }, - motion_sensors={}, - pm25=1, - room_occupied=None, - update_available=False, - schedules={}, - pure_boost_enabled=False, - pure_sensitivity="N", - pure_ac_integration=False, - pure_geo_integration=False, - pure_measure_integration=True, - timer_on=None, - timer_id=None, - timer_state_on=None, - timer_time=None, - smart_on=None, - smart_type=None, - smart_low_temp_threshold=None, - smart_high_temp_threshold=None, - smart_low_state=None, - smart_high_state=None, - filter_clean=False, - filter_last_reset="2022-04-23T15:58:45Z", - ), - }, -) diff --git a/tests/components/sensibo/test_entity.py b/tests/components/sensibo/test_entity.py index 27e0f4df772..bf512f9f220 100644 --- a/tests/components/sensibo/test_entity.py +++ b/tests/components/sensibo/test_entity.py @@ -1,8 +1,7 @@ """The test for the sensibo entity.""" from __future__ import annotations -from datetime import timedelta -from unittest.mock import patch +from unittest.mock import AsyncMock, patch from pysensibo.model import SensiboData import pytest @@ -23,9 +22,6 @@ from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er -from homeassistant.util import dt - -from tests.common import async_fire_time_changed async def test_entity( @@ -98,26 +94,13 @@ async def test_entity_send_command( async def test_entity_send_command_calibration( - hass: HomeAssistant, load_int: ConfigEntry, get_data: SensiboData + hass: HomeAssistant, + entity_registry_enabled_by_default: AsyncMock, + load_int: ConfigEntry, + get_data: SensiboData, ) -> None: """Test the Sensibo send command for calibration.""" - registry = er.async_get(hass) - registry.async_update_entity( - "number.hallway_temperature_calibration", disabled_by=None - ) - await hass.async_block_till_done() - - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state = hass.states.get("number.hallway_temperature_calibration") assert state.state == "0.1" diff --git a/tests/components/sensibo/test_number.py b/tests/components/sensibo/test_number.py new file mode 100644 index 00000000000..ed60d6653e9 --- /dev/null +++ b/tests/components/sensibo/test_number.py @@ -0,0 +1,101 @@ +"""The test for the sensibo number platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import AsyncMock, patch + +from pysensibo.model import SensiboData +import pytest +from pytest import MonkeyPatch + +from homeassistant.components.number.const import ( + ATTR_VALUE, + DOMAIN as NUMBER_DOMAIN, + SERVICE_SET_VALUE, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util import dt + +from tests.common import async_fire_time_changed + + +async def test_number( + hass: HomeAssistant, + entity_registry_enabled_by_default: AsyncMock, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo number.""" + + state1 = hass.states.get("number.hallway_temperature_calibration") + state2 = hass.states.get("number.hallway_humidity_calibration") + assert state1.state == "0.1" + assert state2.state == "0.0" + + monkeypatch.setattr(get_data.parsed["ABC999111"], "calibration_temp", 0.2) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("number.hallway_temperature_calibration") + assert state1.state == "0.2" + + +async def test_number_set_value( + hass: HomeAssistant, + entity_registry_enabled_by_default: AsyncMock, + load_int: ConfigEntry, + get_data: SensiboData, +) -> None: + """Test the Sensibo number service.""" + + state1 = hass.states.get("number.hallway_temperature_calibration") + assert state1.state == "0.1" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_calibration", + return_value={"status": "failure"}, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_VALUE: "0.2"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("number.hallway_temperature_calibration") + assert state2.state == "0.1" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_calibration", + return_value={"status": "success"}, + ): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_VALUE: "0.2"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("number.hallway_temperature_calibration") + assert state2.state == "0.2" diff --git a/tests/components/sensibo/test_select.py b/tests/components/sensibo/test_select.py new file mode 100644 index 00000000000..ce361e224c9 --- /dev/null +++ b/tests/components/sensibo/test_select.py @@ -0,0 +1,163 @@ +"""The test for the sensibo select platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from pysensibo.model import SensiboData +import pytest +from pytest import MonkeyPatch + +from homeassistant.components.select.const import ( + ATTR_OPTION, + DOMAIN as SELECT_DOMAIN, + SERVICE_SELECT_OPTION, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.util import dt + +from tests.common import async_fire_time_changed + + +async def test_select( + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo select.""" + + state1 = hass.states.get("select.hallway_horizontal_swing") + assert state1.state == "stopped" + + monkeypatch.setattr( + get_data.parsed["ABC999111"], "horizontal_swing_mode", "fixedLeft" + ) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("select.hallway_horizontal_swing") + assert state1.state == "fixedLeft" + + +async def test_select_set_option( + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo select service.""" + + monkeypatch.setattr( + get_data.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "targetTemperature", + "light", + ], + ) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("select.hallway_horizontal_swing") + assert state1.state == "stopped" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "failed"}}, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedLeft"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("select.hallway_horizontal_swing") + assert state2.state == "stopped" + + monkeypatch.setattr( + get_data.parsed["ABC999111"], + "active_features", + [ + "timestamp", + "on", + "mode", + "targetTemperature", + "horizontalSwing", + "light", + ], + ) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Failed", "failureReason": "No connection"}}, + ): + with pytest.raises(HomeAssistantError): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedLeft"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("select.hallway_horizontal_swing") + assert state2.state == "stopped" + + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", + return_value=get_data, + ), patch( + "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", + return_value={"result": {"status": "Success"}}, + ): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedLeft"}, + blocking=True, + ) + await hass.async_block_till_done() + + state2 = hass.states.get("select.hallway_horizontal_swing") + assert state2.state == "fixedLeft" diff --git a/tests/components/sensibo/test_sensor.py b/tests/components/sensibo/test_sensor.py new file mode 100644 index 00000000000..413b62f6b9f --- /dev/null +++ b/tests/components/sensibo/test_sensor.py @@ -0,0 +1,50 @@ +"""The test for the sensibo select platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from pysensibo.model import SensiboData +from pytest import MonkeyPatch + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.util import dt + +from tests.common import async_fire_time_changed + + +async def test_sensor( + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo sensor.""" + + state1 = hass.states.get("sensor.hallway_motion_sensor_battery_voltage") + state2 = hass.states.get("sensor.kitchen_pm2_5") + assert state1.state == "3000" + assert state2.state == "1" + assert state2.attributes == { + "state_class": "measurement", + "unit_of_measurement": "µg/m³", + "device_class": "pm25", + "icon": "mdi:air-filter", + "friendly_name": "Kitchen PM2.5", + } + + monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pm25", 2) + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("sensor.kitchen_pm2_5") + assert state1.state == "2" diff --git a/tests/components/sensibo/test_update.py b/tests/components/sensibo/test_update.py new file mode 100644 index 00000000000..11b019d111e --- /dev/null +++ b/tests/components/sensibo/test_update.py @@ -0,0 +1,47 @@ +"""The test for the sensibo update platform.""" +from __future__ import annotations + +from datetime import timedelta +from unittest.mock import patch + +from pysensibo.model import SensiboData +from pytest import MonkeyPatch + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant +from homeassistant.util import dt + +from tests.common import async_fire_time_changed + + +async def test_select( + hass: HomeAssistant, + load_int: ConfigEntry, + monkeypatch: MonkeyPatch, + get_data: SensiboData, +) -> None: + """Test the Sensibo update.""" + + state1 = hass.states.get("update.hallway_update_available") + state2 = hass.states.get("update.kitchen_update_available") + assert state1.state == STATE_ON + assert state1.attributes["installed_version"] == "SKY30046" + assert state1.attributes["latest_version"] == "SKY30048" + assert state1.attributes["title"] == "skyv2" + assert state2.state == STATE_OFF + + monkeypatch.setattr(get_data.parsed["ABC999111"], "fw_ver", "SKY30048") + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", + return_value=get_data, + ): + async_fire_time_changed( + hass, + dt.utcnow() + timedelta(minutes=5), + ) + await hass.async_block_till_done() + + state1 = hass.states.get("update.hallway_update_available") + assert state1.state == STATE_OFF From d84c6af55df12700aebfc18963e2b0640aa59728 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 14 May 2022 02:42:11 +0200 Subject: [PATCH 443/930] Add multiple departures to Trafikverket Ferry (#71484) --- .../trafikverket_ferry/coordinator.py | 18 +++++++----- .../components/trafikverket_ferry/sensor.py | 29 ++++++++++++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/trafikverket_ferry/coordinator.py b/homeassistant/components/trafikverket_ferry/coordinator.py index 96a1e58c6c9..7c2c64d49f0 100644 --- a/homeassistant/components/trafikverket_ferry/coordinator.py +++ b/homeassistant/components/trafikverket_ferry/coordinator.py @@ -77,8 +77,10 @@ class TVDataUpdateCoordinator(DataUpdateCoordinator): when = current_time try: - routedata: FerryStop = await self._ferry_api.async_get_next_ferry_stop( - self._from, self._to, when + routedata: list[ + FerryStop + ] = await self._ferry_api.async_get_next_ferry_stops( + self._from, self._to, when, 3 ) except ValueError as error: raise UpdateFailed( @@ -86,11 +88,13 @@ class TVDataUpdateCoordinator(DataUpdateCoordinator): ) from error states = { - "departure_time": routedata.departure_time, - "departure_from": routedata.from_harbor_name, - "departure_to": routedata.to_harbor_name, - "departure_modified": routedata.modified_time, - "departure_information": routedata.other_information, + "departure_time": routedata[0].departure_time, + "departure_from": routedata[0].from_harbor_name, + "departure_to": routedata[0].to_harbor_name, + "departure_modified": routedata[0].modified_time, + "departure_information": routedata[0].other_information, + "departure_time_next": routedata[1].departure_time, + "departure_time_next_next": routedata[2].departure_time, } _LOGGER.debug("States: %s", states) return states diff --git a/homeassistant/components/trafikverket_ferry/sensor.py b/homeassistant/components/trafikverket_ferry/sensor.py index d93aecd38c1..bab73d72210 100644 --- a/homeassistant/components/trafikverket_ferry/sensor.py +++ b/homeassistant/components/trafikverket_ferry/sensor.py @@ -38,7 +38,7 @@ class TrafikverketRequiredKeysMixin: """Mixin for required keys.""" value_fn: Callable[[dict[str, Any]], StateType | datetime] - info_fn: Callable[[dict[str, Any]], StateType | list] + info_fn: Callable[[dict[str, Any]], StateType | list] | None @dataclass @@ -80,6 +80,24 @@ SENSOR_TYPES: tuple[TrafikverketSensorEntityDescription, ...] = ( info_fn=lambda data: data["departure_information"], entity_registry_enabled_default=False, ), + TrafikverketSensorEntityDescription( + key="departure_time_next", + name="Departure Time Next", + icon="mdi:clock", + device_class=SensorDeviceClass.TIMESTAMP, + value_fn=lambda data: as_utc(data["departure_time_next"]), + info_fn=None, + entity_registry_enabled_default=False, + ), + TrafikverketSensorEntityDescription( + key="departure_time_next_next", + name="Departure Time Next After", + icon="mdi:clock", + device_class=SensorDeviceClass.TIMESTAMP, + value_fn=lambda data: as_utc(data["departure_time_next_next"]), + info_fn=None, + entity_registry_enabled_default=False, + ), ) @@ -132,9 +150,12 @@ class FerrySensor(CoordinatorEntity[TVDataUpdateCoordinator], SensorEntity): self.coordinator.data ) - self._attr_extra_state_attributes = { - "other_information": self.entity_description.info_fn(self.coordinator.data), - } + if self.entity_description.info_fn: + self._attr_extra_state_attributes = { + "other_information": self.entity_description.info_fn( + self.coordinator.data + ), + } @callback def _handle_coordinator_update(self) -> None: From ef9d8944f1a45cdb9d10fe1c6f9a149620f80ca8 Mon Sep 17 00:00:00 2001 From: "Clifford W. Hansen" Date: Sat, 14 May 2022 05:09:09 +0200 Subject: [PATCH 444/930] Update sonarr sensor (#71576) Fixed issue when episodeFileCount/episodeCount might not exist --- homeassistant/components/sonarr/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sonarr/sensor.py b/homeassistant/components/sonarr/sensor.py index c9a946758c5..adc588f6951 100644 --- a/homeassistant/components/sonarr/sensor.py +++ b/homeassistant/components/sonarr/sensor.py @@ -237,7 +237,7 @@ class SonarrSensor(SonarrEntity, SensorEntity): stats = item.statistics attrs[ item.title - ] = f"{stats.episodeFileCount}/{stats.episodeCount} Episodes" + ] = f"{getattr(stats,'episodeFileCount', 0)}/{getattr(stats, 'episodeCount', 0)} Episodes" elif key == "upcoming" and self.data.get(key) is not None: for episode in self.data[key]: identifier = f"S{episode.seasonNumber:02d}E{episode.episodeNumber:02d}" From 991f0b40f2d43431d0c44656127cfe0bf07ef02b Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 14 May 2022 05:55:02 +0200 Subject: [PATCH 445/930] Remove YAML configuration from Discord (#71696) --- homeassistant/components/discord/__init__.py | 20 +------- .../components/discord/config_flow.py | 18 +------- homeassistant/components/discord/notify.py | 8 +--- tests/components/discord/__init__.py | 6 +-- tests/components/discord/test_config_flow.py | 46 ------------------- 5 files changed, 6 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/discord/__init__.py b/homeassistant/components/discord/__init__.py index ec41ac9d073..ae06447f741 100644 --- a/homeassistant/components/discord/__init__.py +++ b/homeassistant/components/discord/__init__.py @@ -2,33 +2,17 @@ from aiohttp.client_exceptions import ClientConnectorError import nextcord -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import CONF_API_TOKEN, CONF_PLATFORM, Platform +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_TOKEN, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import discovery -from homeassistant.helpers.typing import ConfigType from .const import DOMAIN PLATFORMS = [Platform.NOTIFY] -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Discord component.""" - # Iterate all entries for notify to only get Discord - if Platform.NOTIFY in config: - for entry in config[Platform.NOTIFY]: - if entry[CONF_PLATFORM] == DOMAIN: - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=entry - ) - ) - - return True - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Discord from a config entry.""" nextcord.VoiceClient.warn_nacl = False diff --git a/homeassistant/components/discord/config_flow.py b/homeassistant/components/discord/config_flow.py index 8abd2a6be37..bce27feced3 100644 --- a/homeassistant/components/discord/config_flow.py +++ b/homeassistant/components/discord/config_flow.py @@ -8,7 +8,7 @@ import nextcord import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_API_TOKEN, CONF_NAME, CONF_TOKEN +from homeassistant.const import CONF_API_TOKEN, CONF_NAME from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN, URL_PLACEHOLDER @@ -69,7 +69,7 @@ class DiscordFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured() return self.async_create_entry( title=info.name, - data=user_input | {CONF_NAME: user_input.get(CONF_NAME, info.name)}, + data=user_input | {CONF_NAME: info.name}, ) user_input = user_input or {} @@ -80,20 +80,6 @@ class DiscordFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_import(self, import_config: dict[str, str]) -> FlowResult: - """Import a config entry from configuration.yaml.""" - _LOGGER.warning( - "Configuration of the Discord integration in YAML is deprecated and " - "will be removed in Home Assistant 2022.6; Your existing configuration " - "has been imported into the UI automatically and can be safely removed " - "from your configuration.yaml file" - ) - for entry in self._async_current_entries(): - if entry.data[CONF_API_TOKEN] == import_config[CONF_TOKEN]: - return self.async_abort(reason="already_configured") - import_config[CONF_API_TOKEN] = import_config.pop(CONF_TOKEN) - return await self.async_step_user(import_config) - async def _async_try_connect(token: str) -> tuple[str | None, nextcord.AppInfo | None]: """Try connecting to Discord.""" diff --git a/homeassistant/components/discord/notify.py b/homeassistant/components/discord/notify.py index 098857876a1..299919472cf 100644 --- a/homeassistant/components/discord/notify.py +++ b/homeassistant/components/discord/notify.py @@ -7,17 +7,14 @@ from typing import Any, cast import nextcord from nextcord.abc import Messageable -import voluptuous as vol from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, - PLATFORM_SCHEMA, BaseNotificationService, ) -from homeassistant.const import CONF_API_TOKEN, CONF_TOKEN +from homeassistant.const import CONF_API_TOKEN from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType _LOGGER = logging.getLogger(__name__) @@ -33,9 +30,6 @@ ATTR_EMBED_THUMBNAIL = "thumbnail" ATTR_EMBED_URL = "url" ATTR_IMAGES = "images" -# Deprecated in Home Assistant 2022.4 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_TOKEN): cv.string}) - async def async_get_service( hass: HomeAssistant, diff --git a/tests/components/discord/__init__.py b/tests/components/discord/__init__.py index ebc23360555..bf7c188b7b5 100644 --- a/tests/components/discord/__init__.py +++ b/tests/components/discord/__init__.py @@ -6,7 +6,7 @@ import nextcord from homeassistant.components.discord.const import DOMAIN from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_API_TOKEN, CONF_NAME, CONF_TOKEN +from homeassistant.const import CONF_API_TOKEN, CONF_NAME from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -21,10 +21,6 @@ CONF_DATA = { CONF_NAME: NAME, } -CONF_IMPORT_DATA_NO_NAME = {CONF_TOKEN: TOKEN} - -CONF_IMPORT_DATA = CONF_IMPORT_DATA_NO_NAME | {CONF_NAME: NAME} - def create_entry(hass: HomeAssistant) -> ConfigEntry: """Add config entry in Home Assistant.""" diff --git a/tests/components/discord/test_config_flow.py b/tests/components/discord/test_config_flow.py index 64588b052fe..59030187866 100644 --- a/tests/components/discord/test_config_flow.py +++ b/tests/components/discord/test_config_flow.py @@ -1,6 +1,5 @@ """Test Discord config flow.""" import nextcord -from pytest import LogCaptureFixture from homeassistant import config_entries, data_entry_flow from homeassistant.components.discord.const import DOMAIN @@ -9,8 +8,6 @@ from homeassistant.core import HomeAssistant from . import ( CONF_DATA, - CONF_IMPORT_DATA, - CONF_IMPORT_DATA_NO_NAME, CONF_INPUT, NAME, create_entry, @@ -121,49 +118,6 @@ async def test_flow_user_unknown_error(hass: HomeAssistant) -> None: assert result["data"] == CONF_DATA -async def test_flow_import(hass: HomeAssistant, caplog: LogCaptureFixture) -> None: - """Test an import flow.""" - with mocked_discord_info(), patch_discord_login(): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=CONF_IMPORT_DATA.copy(), - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == NAME - assert result["data"] == CONF_DATA - assert "Discord integration in YAML" in caplog.text - - -async def test_flow_import_no_name(hass: HomeAssistant) -> None: - """Test import flow with no name in config.""" - with mocked_discord_info(), patch_discord_login(): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=CONF_IMPORT_DATA_NO_NAME, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == NAME - assert result["data"] == CONF_DATA - - -async def test_flow_import_already_configured(hass: HomeAssistant) -> None: - """Test an import flow already configured.""" - create_entry(hass) - with mocked_discord_info(), patch_discord_login(): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=CONF_IMPORT_DATA, - ) - - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "already_configured" - - async def test_flow_reauth(hass: HomeAssistant) -> None: """Test a reauth flow.""" entry = create_entry(hass) From c0ae31d86c841107930cf471fd60d65b5c163f16 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sat, 14 May 2022 13:16:22 +0200 Subject: [PATCH 446/930] Code quality Filesize (#71768) --- homeassistant/components/filesize/__init__.py | 10 +++++-- .../components/filesize/config_flow.py | 27 ++++++++++--------- homeassistant/components/filesize/sensor.py | 15 +++++------ tests/components/filesize/__init__.py | 9 ++++++- tests/components/filesize/test_config_flow.py | 25 +++++++++-------- tests/components/filesize/test_init.py | 6 ++--- tests/components/filesize/test_sensor.py | 6 ++--- 7 files changed, 54 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/filesize/__init__.py b/homeassistant/components/filesize/__init__.py index b3292ed9c9f..61ef26f0a66 100644 --- a/homeassistant/components/filesize/__init__.py +++ b/homeassistant/components/filesize/__init__.py @@ -11,13 +11,19 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import PLATFORMS +def check_path(path: pathlib.Path) -> bool: + """Check path.""" + return path.exists() and path.is_file() + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up from a config entry.""" path = entry.data[CONF_FILE_PATH] - get_path = await hass.async_add_executor_job(pathlib.Path, path) + get_path = pathlib.Path(path) - if not get_path.exists() and not get_path.is_file(): + check_file = await hass.async_add_executor_job(check_path, get_path) + if not check_file: raise ConfigEntryNotReady(f"Can not access file {path}") if not hass.config.is_allowed_path(path): diff --git a/homeassistant/components/filesize/config_flow.py b/homeassistant/components/filesize/config_flow.py index ed2d4ab0940..3f58e636b0e 100644 --- a/homeassistant/components/filesize/config_flow.py +++ b/homeassistant/components/filesize/config_flow.py @@ -11,6 +11,7 @@ from homeassistant.config_entries import ConfigFlow from homeassistant.const import CONF_FILE_PATH from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult +from homeassistant.exceptions import HomeAssistantError from .const import DOMAIN @@ -19,19 +20,20 @@ DATA_SCHEMA = vol.Schema({vol.Required(CONF_FILE_PATH): str}) _LOGGER = logging.getLogger(__name__) -def validate_path(hass: HomeAssistant, path: str) -> pathlib.Path: +def validate_path(hass: HomeAssistant, path: str) -> str: """Validate path.""" - try: - get_path = pathlib.Path(path) - except OSError as error: - _LOGGER.error("Can not access file %s, error %s", path, error) - raise NotValidError from error + get_path = pathlib.Path(path) + if not get_path.exists() or not get_path.is_file(): + _LOGGER.error("Can not access file %s", path) + raise NotValidError if not hass.config.is_allowed_path(path): - _LOGGER.error("Filepath %s is not valid or allowed", path) + _LOGGER.error("Filepath %s is not allowed", path) raise NotAllowedError - return get_path + full_path = get_path.absolute() + + return str(full_path) class FilesizeConfigFlow(ConfigFlow, domain=DOMAIN): @@ -47,14 +49,13 @@ class FilesizeConfigFlow(ConfigFlow, domain=DOMAIN): if user_input is not None: try: - get_path = validate_path(self.hass, user_input[CONF_FILE_PATH]) + full_path = validate_path(self.hass, user_input[CONF_FILE_PATH]) except NotValidError: errors["base"] = "not_valid" except NotAllowedError: errors["base"] = "not_allowed" else: - fullpath = str(get_path.absolute()) - await self.async_set_unique_id(fullpath) + await self.async_set_unique_id(full_path) self._abort_if_unique_id_configured() name = str(user_input[CONF_FILE_PATH]).rsplit("/", maxsplit=1)[-1] @@ -68,9 +69,9 @@ class FilesizeConfigFlow(ConfigFlow, domain=DOMAIN): ) -class NotValidError(Exception): +class NotValidError(HomeAssistantError): """Path is not valid error.""" -class NotAllowedError(Exception): +class NotAllowedError(HomeAssistantError): """Path is not allowed error.""" diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 70896bcacad..6c52fcbdd5a 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -74,13 +74,10 @@ async def async_setup_entry( coordinator = FileSizeCoordinator(hass, fullpath) await coordinator.async_config_entry_first_refresh() - if get_path.exists() and get_path.is_file(): - async_add_entities( - [ - FilesizeEntity(description, fullpath, entry.entry_id, coordinator) - for description in SENSOR_TYPES - ] - ) + async_add_entities( + FilesizeEntity(description, fullpath, entry.entry_id, coordinator) + for description in SENSOR_TYPES + ) class FileSizeCoordinator(DataUpdateCoordinator): @@ -119,7 +116,7 @@ class FileSizeCoordinator(DataUpdateCoordinator): class FilesizeEntity(CoordinatorEntity[FileSizeCoordinator], SensorEntity): - """Encapsulates file size information.""" + """Filesize sensor.""" entity_description: SensorEntityDescription @@ -130,7 +127,7 @@ class FilesizeEntity(CoordinatorEntity[FileSizeCoordinator], SensorEntity): entry_id: str, coordinator: FileSizeCoordinator, ) -> None: - """Initialize the data object.""" + """Initialize the Filesize sensor.""" super().__init__(coordinator) base_name = path.split("/")[-1] self._attr_name = f"{base_name} {description.name}" diff --git a/tests/components/filesize/__init__.py b/tests/components/filesize/__init__.py index d7aa8dd2481..3e09a745387 100644 --- a/tests/components/filesize/__init__.py +++ b/tests/components/filesize/__init__.py @@ -1,6 +1,8 @@ """Tests for the filesize component.""" import os +from homeassistant.core import HomeAssistant + TEST_DIR = os.path.join(os.path.dirname(__file__)) TEST_FILE_NAME = "mock_file_test_filesize.txt" TEST_FILE_NAME2 = "mock_file_test_filesize2.txt" @@ -8,7 +10,12 @@ TEST_FILE = os.path.join(TEST_DIR, TEST_FILE_NAME) TEST_FILE2 = os.path.join(TEST_DIR, TEST_FILE_NAME2) -def create_file(path) -> None: +async def async_create_file(hass: HomeAssistant, path: str) -> None: """Create a test file.""" + await hass.async_add_executor_job(create_file, path) + + +def create_file(path: str) -> None: + """Create the test file.""" with open(path, "w", encoding="utf-8") as test_file: test_file.write("test") diff --git a/tests/components/filesize/test_config_flow.py b/tests/components/filesize/test_config_flow.py index f6873e64128..16f0ab38dc1 100644 --- a/tests/components/filesize/test_config_flow.py +++ b/tests/components/filesize/test_config_flow.py @@ -11,14 +11,14 @@ from homeassistant.data_entry_flow import ( RESULT_TYPE_FORM, ) -from . import TEST_DIR, TEST_FILE, TEST_FILE_NAME, create_file +from . import TEST_DIR, TEST_FILE, TEST_FILE_NAME, async_create_file from tests.common import MockConfigEntry async def test_full_user_flow(hass: HomeAssistant) -> None: """Test the full user configuration flow.""" - create_file(TEST_FILE) + await async_create_file(hass, TEST_FILE) hass.config.allowlist_external_dirs = {TEST_DIR} result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} @@ -43,6 +43,7 @@ async def test_unique_path( mock_config_entry: MockConfigEntry, ) -> None: """Test we abort if already setup.""" + await async_create_file(hass, TEST_FILE) hass.config.allowlist_external_dirs = {TEST_DIR} mock_config_entry.add_to_hass(hass) @@ -56,7 +57,7 @@ async def test_unique_path( async def test_flow_fails_on_validation(hass: HomeAssistant) -> None: """Test config flow errors.""" - create_file(TEST_FILE) + hass.config.allowlist_external_dirs = {} result = await hass.config_entries.flow.async_init( @@ -66,19 +67,17 @@ async def test_flow_fails_on_validation(hass: HomeAssistant) -> None: assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == SOURCE_USER - with patch( - "homeassistant.components.filesize.config_flow.pathlib.Path", - side_effect=OSError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_FILE_PATH: TEST_FILE, - }, - ) + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_FILE_PATH: TEST_FILE, + }, + ) assert result2["errors"] == {"base": "not_valid"} + await async_create_file(hass, TEST_FILE) + with patch("homeassistant.components.filesize.config_flow.pathlib.Path",), patch( "homeassistant.components.filesize.async_setup_entry", return_value=True, diff --git a/tests/components/filesize/test_init.py b/tests/components/filesize/test_init.py index 99285e56b6f..effab8f75d8 100644 --- a/tests/components/filesize/test_init.py +++ b/tests/components/filesize/test_init.py @@ -4,7 +4,7 @@ from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_FILE_PATH from homeassistant.core import HomeAssistant -from . import create_file +from . import async_create_file from tests.common import MockConfigEntry @@ -14,7 +14,7 @@ async def test_load_unload_config_entry( ) -> None: """Test the Filesize configuration entry loading/unloading.""" testfile = f"{tmpdir}/file.txt" - create_file(testfile) + await async_create_file(hass, testfile) hass.config.allowlist_external_dirs = {tmpdir} mock_config_entry.add_to_hass(hass) hass.config_entries.async_update_entry( @@ -54,7 +54,7 @@ async def test_not_valid_path_to_file( ) -> None: """Test that an invalid path is caught.""" testfile = f"{tmpdir}/file.txt" - create_file(testfile) + await async_create_file(hass, testfile) mock_config_entry.add_to_hass(hass) hass.config_entries.async_update_entry( mock_config_entry, unique_id=testfile, data={CONF_FILE_PATH: testfile} diff --git a/tests/components/filesize/test_sensor.py b/tests/components/filesize/test_sensor.py index 803aa96610d..5b072769f56 100644 --- a/tests/components/filesize/test_sensor.py +++ b/tests/components/filesize/test_sensor.py @@ -5,7 +5,7 @@ from homeassistant.const import CONF_FILE_PATH, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_component import async_update_entity -from . import TEST_FILE, TEST_FILE_NAME, create_file +from . import TEST_FILE, TEST_FILE_NAME, async_create_file from tests.common import MockConfigEntry @@ -28,7 +28,7 @@ async def test_valid_path( ) -> None: """Test for a valid path.""" testfile = f"{tmpdir}/file.txt" - create_file(testfile) + await async_create_file(hass, testfile) hass.config.allowlist_external_dirs = {tmpdir} mock_config_entry.add_to_hass(hass) hass.config_entries.async_update_entry( @@ -50,7 +50,7 @@ async def test_state_unavailable( ) -> None: """Verify we handle state unavailable.""" testfile = f"{tmpdir}/file.txt" - create_file(testfile) + await async_create_file(hass, testfile) hass.config.allowlist_external_dirs = {tmpdir} mock_config_entry.add_to_hass(hass) hass.config_entries.async_update_entry( From ba7d3977045fc50af4b357fb09f85ab3a6464e69 Mon Sep 17 00:00:00 2001 From: eyager1 <44526531+eyager1@users.noreply.github.com> Date: Sat, 14 May 2022 11:14:35 -0400 Subject: [PATCH 447/930] Improve reliability of VLC metadata parsing (#71856) * Improve reliability of metadata parsing. * Remove media_album_name property * Apply suggestions from code review Co-authored-by: Martin Hjelmare Co-authored-by: Martin Hjelmare --- homeassistant/components/vlc_telnet/media_player.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/vlc_telnet/media_player.py b/homeassistant/components/vlc_telnet/media_player.py index 7a0a796f665..89fa1a3c323 100644 --- a/homeassistant/components/vlc_telnet/media_player.py +++ b/homeassistant/components/vlc_telnet/media_player.py @@ -162,8 +162,16 @@ class VlcDevice(MediaPlayerEntity): data = info.data LOGGER.debug("Info data: %s", data) - self._media_artist = data.get(0, {}).get("artist") - self._media_title = data.get(0, {}).get("title") + self._attr_media_album_name = data.get("data", {}).get("album") + self._media_artist = data.get("data", {}).get("artist") + self._media_title = data.get("data", {}).get("title") + now_playing = data.get("data", {}).get("now_playing") + + # Many radio streams put artist/title/album in now_playing and title is the station name. + if now_playing: + if not self._media_artist: + self._media_artist = self._media_title + self._media_title = now_playing if self._media_title: return From fdc8830dd362e6d0b08b3ea1e139cf83f82d853c Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sat, 14 May 2022 12:07:17 -0400 Subject: [PATCH 448/930] Remove ssh switch from unsupported devices for UniFi Protect (#71859) --- .../components/unifiprotect/switch.py | 28 +++++++++++++++---- tests/components/unifiprotect/test_switch.py | 25 +++++++++-------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index 85a089994f8..6051e4e596f 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -43,7 +43,7 @@ async def _set_highfps(obj: Camera, value: bool) -> None: await obj.set_video_mode(VideoMode.DEFAULT) -ALL_DEVICES_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( +CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ProtectSwitchEntityDescription( key="ssh", name="SSH Enabled", @@ -53,9 +53,6 @@ ALL_DEVICES_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ufp_value="is_ssh_enabled", ufp_set_method="set_ssh", ), -) - -CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ProtectSwitchEntityDescription( key="status_light", name="Status Light On", @@ -222,6 +219,15 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( LIGHT_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( + ProtectSwitchEntityDescription( + key="ssh", + name="SSH Enabled", + icon="mdi:lock", + entity_registry_enabled_default=False, + entity_category=EntityCategory.CONFIG, + ufp_value="is_ssh_enabled", + ufp_set_method="set_ssh", + ), ProtectSwitchEntityDescription( key="status_light", name="Status Light On", @@ -243,6 +249,18 @@ DOORLOCK_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ), ) +VIEWER_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( + ProtectSwitchEntityDescription( + key="ssh", + name="SSH Enabled", + icon="mdi:lock", + entity_registry_enabled_default=False, + entity_category=EntityCategory.CONFIG, + ufp_value="is_ssh_enabled", + ufp_set_method="set_ssh", + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -254,11 +272,11 @@ async def async_setup_entry( entities: list[ProtectDeviceEntity] = async_all_device_entities( data, ProtectSwitch, - all_descs=ALL_DEVICES_SWITCHES, camera_descs=CAMERA_SWITCHES, light_descs=LIGHT_SWITCHES, sense_descs=SENSE_SWITCHES, lock_descs=DOORLOCK_SWITCHES, + viewer_descs=VIEWER_SWITCHES, ) async_add_entities(entities) diff --git a/tests/components/unifiprotect/test_switch.py b/tests/components/unifiprotect/test_switch.py index c54d04a8cb7..498f4c6b3b7 100644 --- a/tests/components/unifiprotect/test_switch.py +++ b/tests/components/unifiprotect/test_switch.py @@ -10,7 +10,6 @@ from pyunifiprotect.data.types import RecordingMode, SmartDetectObjectType, Vide from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.components.unifiprotect.switch import ( - ALL_DEVICES_SWITCHES, CAMERA_SWITCHES, LIGHT_SWITCHES, ProtectSwitchEntityDescription, @@ -29,7 +28,9 @@ from .conftest import ( CAMERA_SWITCHES_BASIC = [ d for d in CAMERA_SWITCHES - if d.name != "Detections: Face" and d.name != "Detections: Package" + if d.name != "Detections: Face" + and d.name != "Detections: Package" + and d.name != "SSH Enabled" ] CAMERA_SWITCHES_NO_EXTRA = [ d for d in CAMERA_SWITCHES_BASIC if d.name not in ("High FPS", "Privacy Mode") @@ -215,7 +216,7 @@ async def test_switch_setup_light( entity_registry = er.async_get(hass) - description = LIGHT_SWITCHES[0] + description = LIGHT_SWITCHES[1] unique_id, entity_id = ids_from_device_description( Platform.SWITCH, light, description @@ -230,7 +231,7 @@ async def test_switch_setup_light( assert state.state == STATE_OFF assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION - description = ALL_DEVICES_SWITCHES[0] + description = LIGHT_SWITCHES[0] unique_id = f"{light.id}_{description.key}" entity_id = f"switch.test_light_{description.name.lower().replace(' ', '_')}" @@ -271,7 +272,7 @@ async def test_switch_setup_camera_all( assert state.state == STATE_OFF assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION - description = ALL_DEVICES_SWITCHES[0] + description = CAMERA_SWITCHES[0] description_entity_name = ( description.name.lower().replace(":", "").replace(" ", "_") @@ -301,7 +302,7 @@ async def test_switch_setup_camera_none( entity_registry = er.async_get(hass) - for description in CAMERA_SWITCHES: + for description in CAMERA_SWITCHES_BASIC: if description.ufp_required_field is not None: continue @@ -318,7 +319,7 @@ async def test_switch_setup_camera_none( assert state.state == STATE_OFF assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION - description = ALL_DEVICES_SWITCHES[0] + description = CAMERA_SWITCHES[0] description_entity_name = ( description.name.lower().replace(":", "").replace(" ", "_") @@ -342,7 +343,7 @@ async def test_switch_setup_camera_none( async def test_switch_light_status(hass: HomeAssistant, light: Light): """Tests status light switch for lights.""" - description = LIGHT_SWITCHES[0] + description = LIGHT_SWITCHES[1] light.__fields__["set_status_light"] = Mock() light.set_status_light = AsyncMock() @@ -367,7 +368,7 @@ async def test_switch_camera_ssh( ): """Tests SSH switch for cameras.""" - description = ALL_DEVICES_SWITCHES[0] + description = CAMERA_SWITCHES[0] camera.__fields__["set_ssh"] = Mock() camera.set_ssh = AsyncMock() @@ -418,7 +419,7 @@ async def test_switch_camera_simple( async def test_switch_camera_highfps(hass: HomeAssistant, camera: Camera): """Tests High FPS switch for cameras.""" - description = CAMERA_SWITCHES[2] + description = CAMERA_SWITCHES[3] camera.__fields__["set_video_mode"] = Mock() camera.set_video_mode = AsyncMock() @@ -441,7 +442,7 @@ async def test_switch_camera_highfps(hass: HomeAssistant, camera: Camera): async def test_switch_camera_privacy(hass: HomeAssistant, camera: Camera): """Tests Privacy Mode switch for cameras.""" - description = CAMERA_SWITCHES[3] + description = CAMERA_SWITCHES[4] camera.__fields__["set_privacy"] = Mock() camera.set_privacy = AsyncMock() @@ -468,7 +469,7 @@ async def test_switch_camera_privacy_already_on( ): """Tests Privacy Mode switch for cameras with privacy mode defaulted on.""" - description = CAMERA_SWITCHES[3] + description = CAMERA_SWITCHES[4] camera_privacy.__fields__["set_privacy"] = Mock() camera_privacy.set_privacy = AsyncMock() From 656e88faecea8e9d2ae389a67c99a4235d51c725 Mon Sep 17 00:00:00 2001 From: AlainH Date: Sat, 14 May 2022 18:10:15 +0200 Subject: [PATCH 449/930] Update pyRFXtrx dependency to 0.29.0 (#71852) * rfxtrx: update pyRFXtrx dependency to 0.29.0 * Update requirements_all.txt / requirements_test_all.txt --- homeassistant/components/rfxtrx/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rfxtrx/manifest.json b/homeassistant/components/rfxtrx/manifest.json index edbf5c8556c..cfe1049c888 100644 --- a/homeassistant/components/rfxtrx/manifest.json +++ b/homeassistant/components/rfxtrx/manifest.json @@ -2,7 +2,7 @@ "domain": "rfxtrx", "name": "RFXCOM RFXtrx", "documentation": "https://www.home-assistant.io/integrations/rfxtrx", - "requirements": ["pyRFXtrx==0.28.0"], + "requirements": ["pyRFXtrx==0.29.0"], "codeowners": ["@danielhiversen", "@elupus", "@RobBie1221"], "config_flow": true, "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 2e8c89e5797..fe73d53bee5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1324,7 +1324,7 @@ pyMetEireann==2021.8.0 pyMetno==0.9.0 # homeassistant.components.rfxtrx -pyRFXtrx==0.28.0 +pyRFXtrx==0.29.0 # homeassistant.components.switchmate # pySwitchmate==0.4.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 30510f08910..5bf3a4aa4eb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -899,7 +899,7 @@ pyMetEireann==2021.8.0 pyMetno==0.9.0 # homeassistant.components.rfxtrx -pyRFXtrx==0.28.0 +pyRFXtrx==0.29.0 # homeassistant.components.tibber pyTibber==0.22.3 From 355445db2d5c94dff91f3ea9799ec0ebe2581e4f Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 14 May 2022 10:27:47 -0700 Subject: [PATCH 450/930] Add application credentials platform for google calendar integration (#71808) * Add google application_credentials platform * Further simplify custom auth implementation overrides * Add test coverage in application_credentials * Simplify wording in a comment * Remove unused imports accidentally left from merge * Wrap lines that are too long for style guide * Move application credential loading to only where it is needed * Leave CLIENT_ID and CLIENT_SECRET as required. --- .../application_credentials/__init__.py | 62 +++++++++---- homeassistant/components/google/__init__.py | 25 ++++-- homeassistant/components/google/api.py | 16 +--- .../google/application_credentials.py | 23 +++++ homeassistant/components/google/manifest.json | 2 +- .../generated/application_credentials.py | 1 + .../application_credentials/test_init.py | 49 ++++++++++- tests/components/google/conftest.py | 5 +- tests/components/google/test_config_flow.py | 88 ++++++++++++++++++- tests/components/google/test_init.py | 34 +++++++ 10 files changed, 258 insertions(+), 47 deletions(-) create mode 100644 homeassistant/components/google/application_credentials.py diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index b5c828762c1..0dda775774f 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -151,7 +151,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_import_client_credential( - hass: HomeAssistant, domain: str, credential: ClientCredential + hass: HomeAssistant, + domain: str, + credential: ClientCredential, + auth_domain: str = None, ) -> None: """Import an existing credential from configuration.yaml.""" if DOMAIN not in hass.data: @@ -161,7 +164,7 @@ async def async_import_client_credential( CONF_DOMAIN: domain, CONF_CLIENT_ID: credential.client_id, CONF_CLIENT_SECRET: credential.client_secret, - CONF_AUTH_DOMAIN: domain, + CONF_AUTH_DOMAIN: auth_domain if auth_domain else domain, } await storage_collection.async_import_item(item) @@ -169,6 +172,23 @@ async def async_import_client_credential( class AuthImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation): """Application Credentials local oauth2 implementation.""" + def __init__( + self, + hass: HomeAssistant, + auth_domain: str, + credential: ClientCredential, + authorization_server: AuthorizationServer, + ) -> None: + """Initialize AuthImplementation.""" + super().__init__( + hass, + auth_domain, + credential.client_id, + credential.client_secret, + authorization_server.authorize_url, + authorization_server.token_url, + ) + @property def name(self) -> str: """Name of the implementation.""" @@ -184,29 +204,38 @@ async def _async_provide_implementation( if not platform: return [] - authorization_server = await platform.async_get_authorization_server(hass) storage_collection = hass.data[DOMAIN][DATA_STORAGE] credentials = storage_collection.async_client_credentials(domain) + if hasattr(platform, "async_get_auth_implementation"): + return [ + await platform.async_get_auth_implementation(hass, auth_domain, credential) + for auth_domain, credential in credentials.items() + ] + authorization_server = await platform.async_get_authorization_server(hass) return [ - AuthImplementation( - hass, - auth_domain, - credential.client_id, - credential.client_secret, - authorization_server.authorize_url, - authorization_server.token_url, - ) + AuthImplementation(hass, auth_domain, credential, authorization_server) for auth_domain, credential in credentials.items() ] class ApplicationCredentialsProtocol(Protocol): - """Define the format that application_credentials platforms can have.""" + """Define the format that application_credentials platforms may have. + + Most platforms typically just implement async_get_authorization_server, and + the default oauth implementation will be used. Otherwise a platform may + implement async_get_auth_implementation to give their use a custom + AbstractOAuth2Implementation. + """ async def async_get_authorization_server( self, hass: HomeAssistant ) -> AuthorizationServer: - """Return authorization server.""" + """Return authorization server, for the default auth implementation.""" + + async def async_get_auth_implementation( + self, hass: HomeAssistant, auth_domain: str, credential: ClientCredential + ) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return a custom auth implementation.""" async def _get_platform( @@ -227,9 +256,12 @@ async def _get_platform( err, ) return None - if not hasattr(platform, "async_get_authorization_server"): + if not hasattr(platform, "async_get_authorization_server") and not hasattr( + platform, "async_get_auth_implementation" + ): raise ValueError( - f"Integration '{integration_domain}' platform application_credentials did not implement 'async_get_authorization_server'" + f"Integration '{integration_domain}' platform {DOMAIN} did not " + f"implement 'async_get_authorization_server' or 'async_get_auth_implementation'" ) return platform diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index c5f62c0034f..61b25a9c027 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -17,6 +17,10 @@ from voluptuous.error import Error as VoluptuousError import yaml from homeassistant import config_entries +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_CLIENT_ID, @@ -39,12 +43,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import generate_entity_id from homeassistant.helpers.typing import ConfigType -from . import config_flow -from .api import ApiAuthImpl, DeviceAuth, get_feature_access +from .api import ApiAuthImpl, get_feature_access from .const import ( CONF_CALENDAR_ACCESS, DATA_CONFIG, DATA_SERVICE, + DEVICE_AUTH_IMPL, DISCOVER_CALENDAR, DOMAIN, FeatureAccess, @@ -159,14 +163,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Google component.""" conf = config.get(DOMAIN, {}) hass.data[DOMAIN] = {DATA_CONFIG: conf} - config_flow.OAuth2FlowHandler.async_register_implementation( - hass, - DeviceAuth( + + if CONF_CLIENT_ID in conf and CONF_CLIENT_SECRET in conf: + await async_import_client_credential( hass, - conf[CONF_CLIENT_ID], - conf[CONF_CLIENT_SECRET], - ), - ) + DOMAIN, + ClientCredential( + conf[CONF_CLIENT_ID], + conf[CONF_CLIENT_SECRET], + ), + DEVICE_AUTH_IMPL, + ) # Import credentials from the old token file into the new way as # a ConfigEntry managed by home assistant. diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index bb32d46f0e4..dceeb6ba6a4 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -10,7 +10,6 @@ from typing import Any import aiohttp from gcal_sync.auth import AbstractAuth -import oauth2client from oauth2client.client import ( Credentials, DeviceFlowInfo, @@ -19,6 +18,7 @@ from oauth2client.client import ( OAuth2WebServerFlow, ) +from homeassistant.components.application_credentials import AuthImplementation from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.event import async_track_time_interval @@ -28,7 +28,6 @@ from .const import ( CONF_CALENDAR_ACCESS, DATA_CONFIG, DEFAULT_FEATURE_ACCESS, - DEVICE_AUTH_IMPL, DOMAIN, FeatureAccess, ) @@ -44,20 +43,9 @@ class OAuthError(Exception): """OAuth related error.""" -class DeviceAuth(config_entry_oauth2_flow.LocalOAuth2Implementation): +class DeviceAuth(AuthImplementation): """OAuth implementation for Device Auth.""" - def __init__(self, hass: HomeAssistant, client_id: str, client_secret: str) -> None: - """Initialize InstalledAppAuth.""" - super().__init__( - hass, - DEVICE_AUTH_IMPL, - client_id, - client_secret, - oauth2client.GOOGLE_AUTH_URI, - oauth2client.GOOGLE_TOKEN_URI, - ) - async def async_resolve_external_data(self, external_data: Any) -> dict: """Resolve a Google API Credentials object to Home Assistant token.""" creds: Credentials = external_data[DEVICE_AUTH_CREDS] diff --git a/homeassistant/components/google/application_credentials.py b/homeassistant/components/google/application_credentials.py new file mode 100644 index 00000000000..2f1fcba8084 --- /dev/null +++ b/homeassistant/components/google/application_credentials.py @@ -0,0 +1,23 @@ +"""application_credentials platform for nest.""" + +import oauth2client + +from homeassistant.components.application_credentials import ( + AuthorizationServer, + ClientCredential, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .api import DeviceAuth + +AUTHORIZATION_SERVER = AuthorizationServer( + oauth2client.GOOGLE_AUTH_URI, oauth2client.GOOGLE_TOKEN_URI +) + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return auth implementation.""" + return DeviceAuth(hass, auth_domain, credential, AUTHORIZATION_SERVER) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 2cf852fc6af..87069bc463a 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -2,7 +2,7 @@ "domain": "google", "name": "Google Calendars", "config_flow": true, - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", "requirements": ["gcal-sync==0.7.1", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index ec6c1886e0a..3bde9e4681f 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -6,5 +6,6 @@ To update, run python3 -m script.hassfest # fmt: off APPLICATION_CREDENTIALS = [ + "google", "xbox" ] diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index 8929d8f9c54..b5f51a9b837 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -14,6 +14,7 @@ from homeassistant import config_entries, data_entry_flow from homeassistant.components.application_credentials import ( CONF_AUTH_DOMAIN, DOMAIN, + AuthImplementation, AuthorizationServer, ClientCredential, async_import_client_credential, @@ -64,12 +65,14 @@ async def setup_application_credentials_integration( ) -> None: """Set up a fake application_credentials integration.""" hass.config.components.add(domain) + mock_platform_impl = Mock( + async_get_authorization_server=AsyncMock(return_value=authorization_server), + ) + del mock_platform_impl.async_get_auth_implementation # return False on hasattr mock_platform( hass, f"{domain}.application_credentials", - Mock( - async_get_authorization_server=AsyncMock(return_value=authorization_server), - ), + mock_platform_impl, ) @@ -585,6 +588,7 @@ async def test_websocket_without_authorization_server( # Platform does not implemenent async_get_authorization_server platform = Mock() del platform.async_get_authorization_server + del platform.async_get_auth_implementation mock_platform( hass, f"{TEST_DOMAIN}.application_credentials", @@ -611,6 +615,45 @@ async def test_websocket_without_authorization_server( ) +@pytest.mark.parametrize("config_credential", [DEVELOPER_CREDENTIAL]) +async def test_platform_with_auth_implementation( + hass, + hass_client_no_auth, + aioclient_mock, + oauth_fixture, + config_credential, + import_config_credential, + authorization_server, +): + """Test config flow with custom OAuth2 implementation.""" + + assert await async_setup_component(hass, "application_credentials", {}) + hass.config.components.add(TEST_DOMAIN) + + async def get_auth_impl( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential + ) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + return AuthImplementation(hass, auth_domain, credential, authorization_server) + + mock_platform_impl = Mock( + async_get_auth_implementation=get_auth_impl, + ) + del mock_platform_impl.async_get_authorization_server + mock_platform( + hass, + f"{TEST_DOMAIN}.application_credentials", + mock_platform_impl, + ) + + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + result = await oauth_fixture.complete_external_step(result) + # Uses the imported auth domain for compatibility + assert result["data"].get("auth_implementation") == TEST_DOMAIN + + async def test_websocket_integration_list(ws_client: ClientFixture): """Test websocket integration list command.""" client = await ws_client() diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index 9594963008f..f48996de72a 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -171,7 +171,8 @@ def config_entry_token_expiry(token_expiry: datetime.datetime) -> float: @pytest.fixture def config_entry( - token_scopes: list[str], config_entry_token_expiry: float + token_scopes: list[str], + config_entry_token_expiry: float, ) -> MockConfigEntry: """Fixture to create a config entry for the integration.""" return MockConfigEntry( @@ -291,7 +292,7 @@ def google_config(google_config_track_new: bool | None) -> dict[str, Any]: @pytest.fixture def config(google_config: dict[str, Any]) -> dict[str, Any]: """Fixture for overriding component config.""" - return {DOMAIN: google_config} + return {DOMAIN: google_config} if google_config else {} @pytest.fixture diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 0991f4e5194..f061aeb3057 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -1,6 +1,7 @@ """Test the google config flow.""" import datetime +from typing import Any from unittest.mock import Mock, patch from oauth2client.client import ( @@ -11,6 +12,10 @@ from oauth2client.client import ( import pytest from homeassistant import config_entries +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.google.const import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow @@ -65,7 +70,7 @@ async def fire_alarm(hass, point_in_time): await hass.async_block_till_done() -async def test_full_flow( +async def test_full_flow_yaml_creds( hass: HomeAssistant, mock_code_flow: Mock, mock_exchange: Mock, @@ -94,7 +99,7 @@ async def test_full_flow( ) assert result.get("type") == "create_entry" - assert result.get("title") == "Configuration.yaml" + assert result.get("title") == "client-id" assert "data" in result data = result["data"] assert "token" in data @@ -121,6 +126,68 @@ async def test_full_flow( assert len(entries) == 1 +@pytest.mark.parametrize("google_config", [None]) +async def test_full_flow_application_creds( + hass: HomeAssistant, + mock_code_flow: Mock, + mock_exchange: Mock, + config: dict[str, Any], + component_setup: ComponentSetup, +) -> None: + """Test successful creds setup.""" + assert await component_setup() + + await async_import_client_credential( + hass, DOMAIN, ClientCredential("client-id", "client-secret"), "imported-cred" + ) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == "progress" + assert result.get("step_id") == "auth" + assert "description_placeholders" in result + assert "url" in result["description_placeholders"] + + with patch( + "homeassistant.components.google.async_setup_entry", return_value=True + ) as mock_setup: + # Run one tick to invoke the credential exchange check + now = utcnow() + await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA) + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + flow_id=result["flow_id"] + ) + + assert result.get("type") == "create_entry" + assert result.get("title") == "client-id" + assert "data" in result + data = result["data"] + assert "token" in data + assert 0 < data["token"]["expires_in"] < 8 * 86400 + assert ( + datetime.datetime.now().timestamp() + <= data["token"]["expires_at"] + < (datetime.datetime.now() + datetime.timedelta(days=8)).timestamp() + ) + data["token"].pop("expires_at") + data["token"].pop("expires_in") + assert data == { + "auth_implementation": "imported-cred", + "token": { + "access_token": "ACCESS_TOKEN", + "refresh_token": "REFRESH_TOKEN", + "scope": "https://www.googleapis.com/auth/calendar", + "token_type": "Bearer", + }, + } + + assert len(mock_setup.mock_calls) == 1 + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 + + async def test_code_error( hass: HomeAssistant, mock_code_flow: Mock, @@ -211,7 +278,7 @@ async def test_exchange_error( ) assert result.get("type") == "create_entry" - assert result.get("title") == "Configuration.yaml" + assert result.get("title") == "client-id" assert "data" in result data = result["data"] assert "token" in data @@ -263,6 +330,21 @@ async def test_missing_configuration( assert result.get("reason") == "missing_configuration" +@pytest.mark.parametrize("google_config", [None]) +async def test_missing_configuration_yaml_empty( + hass: HomeAssistant, + component_setup: ComponentSetup, +) -> None: + """Test setup with an empty yaml configuration and no credentials.""" + assert await component_setup() + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == "abort" + assert result.get("reason") == "missing_configuration" + + async def test_wrong_configuration( hass: HomeAssistant, ) -> None: diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index c536ef7ea00..511e8545b40 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -10,6 +10,10 @@ from unittest.mock import patch import pytest +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.google import ( DOMAIN, SERVICE_ADD_EVENT, @@ -18,6 +22,7 @@ from homeassistant.components.google import ( from homeassistant.config_entries import ConfigEntryState from homeassistant.const import STATE_OFF from homeassistant.core import HomeAssistant, State +from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow from .conftest import ( @@ -224,6 +229,35 @@ async def test_found_calendar_from_api( assert not hass.states.get(TEST_YAML_ENTITY) +@pytest.mark.parametrize("calendars_config,google_config", [([], {})]) +async def test_load_application_credentials( + hass: HomeAssistant, + component_setup: ComponentSetup, + mock_calendars_yaml: None, + mock_calendars_list: ApiResult, + test_api_calendar: dict[str, Any], + mock_events_list: ApiResult, + setup_config_entry: MockConfigEntry, +) -> None: + """Test loading an application credentials and a config entry.""" + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( + hass, DOMAIN, ClientCredential("client-id", "client-secret"), "device_auth" + ) + + mock_calendars_list({"items": [test_api_calendar]}) + mock_events_list({}) + assert await component_setup() + + state = hass.states.get(TEST_API_ENTITY) + assert state + assert state.name == TEST_API_ENTITY_NAME + assert state.state == STATE_OFF + + # No yaml config loaded that overwrites the entity name + assert not hass.states.get(TEST_YAML_ENTITY) + + @pytest.mark.parametrize( "calendars_config_track,expected_state", [ From 532b3d780f58df7178cb3278513c451f7450ada1 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Sat, 14 May 2022 13:40:26 -0500 Subject: [PATCH 451/930] Rework Sonos battery and ping activity tracking (#70942) --- homeassistant/components/sonos/__init__.py | 13 +--- homeassistant/components/sonos/exception.py | 4 ++ homeassistant/components/sonos/speaker.py | 73 ++++++++++++--------- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 114c2815a56..c775b475dc4 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -40,6 +40,7 @@ from .const import ( SONOS_VANISHED, UPNP_ST, ) +from .exception import SonosUpdateError from .favorites import SonosFavorites from .speaker import SonosSpeaker @@ -264,19 +265,11 @@ class SonosDiscoveryManager: self._create_visible_speakers(ip_addr) elif not known_speaker.available: try: - known_speaker.soco.renderingControl.GetVolume( - [("InstanceID", 0), ("Channel", "Master")], timeout=1 - ) - except OSError: + known_speaker.ping() + except SonosUpdateError: _LOGGER.debug( "Manual poll to %s failed, keeping unavailable", ip_addr ) - else: - dispatcher_send( - self.hass, - f"{SONOS_SPEAKER_ACTIVITY}-{known_speaker.uid}", - "manual rediscovery", - ) self.data.hosts_heartbeat = call_later( self.hass, DISCOVERY_INTERVAL.total_seconds(), self._poll_manual_hosts diff --git a/homeassistant/components/sonos/exception.py b/homeassistant/components/sonos/exception.py index bce1e3233c1..dd2d30796cc 100644 --- a/homeassistant/components/sonos/exception.py +++ b/homeassistant/components/sonos/exception.py @@ -9,3 +9,7 @@ class UnknownMediaType(BrowseError): class SonosUpdateError(HomeAssistantError): """Update failed.""" + + +class S1BatteryMissing(SonosUpdateError): + """Battery update failed on S1 firmware.""" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index a57bb4d3206..5d4199ec905 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -57,6 +57,7 @@ from .const import ( SONOS_VANISHED, SUBSCRIPTION_TIMEOUT, ) +from .exception import S1BatteryMissing, SonosUpdateError from .favorites import SonosFavorites from .helpers import soco_error from .media import SonosMedia @@ -83,16 +84,6 @@ UNUSED_DEVICE_KEYS = ["SPID", "TargetRoomName"] _LOGGER = logging.getLogger(__name__) -def fetch_battery_info_or_none(soco: SoCo) -> dict[str, Any] | None: - """Fetch battery_info from the given SoCo object. - - Returns None if the device doesn't support battery info - or if the device is offline. - """ - with contextlib.suppress(ConnectionError, TimeoutError, SoCoException): - return soco.get_battery_info() - - class SonosSpeaker: """Representation of a Sonos speaker.""" @@ -207,8 +198,11 @@ class SonosSpeaker: self.hass, SONOS_CREATE_AUDIO_FORMAT_SENSOR, self, audio_format ) - if battery_info := fetch_battery_info_or_none(self.soco): - self.battery_info = battery_info + try: + self.battery_info = self.fetch_battery_info() + except SonosUpdateError: + _LOGGER.debug("No battery available for %s", self.zone_name) + else: # Battery events can be infrequent, polling is still necessary self._battery_poll_timer = track_time_interval( self.hass, self.async_poll_battery, BATTERY_SCAN_INTERVAL @@ -530,6 +524,13 @@ class SonosSpeaker: # # Speaker availability methods # + @soco_error() + def ping(self) -> None: + """Test device availability. Failure will raise SonosUpdateError.""" + self.soco.renderingControl.GetVolume( + [("InstanceID", 0), ("Channel", "Master")], timeout=1 + ) + @callback def speaker_activity(self, source): """Track the last activity on this speaker, set availability and resubscribe.""" @@ -560,23 +561,13 @@ class SonosSpeaker: return try: - # Make a short-timeout call as a final check - # before marking this speaker as unavailable - await self.hass.async_add_executor_job( - partial( - self.soco.renderingControl.GetVolume, - [("InstanceID", 0), ("Channel", "Master")], - timeout=1, - ) - ) - except OSError: + await self.hass.async_add_executor_job(self.ping) + except SonosUpdateError: _LOGGER.warning( "No recent activity and cannot reach %s, marking unavailable", self.zone_name, ) await self.async_offline() - else: - self.speaker_activity("timeout poll") async def async_offline(self) -> None: """Handle removal of speaker when unavailable.""" @@ -619,6 +610,15 @@ class SonosSpeaker: # # Battery management # + @soco_error() + def fetch_battery_info(self) -> dict[str, Any]: + """Fetch battery_info for the speaker.""" + battery_info = self.soco.get_battery_info() + if not battery_info: + # S1 firmware returns an empty payload + raise S1BatteryMissing + return battery_info + async def async_update_battery_info(self, more_info: str) -> None: """Update battery info using a SonosEvent payload value.""" battery_dict = dict(x.split(":") for x in more_info.split(",")) @@ -658,11 +658,17 @@ class SonosSpeaker: if is_charging == self.charging: self.battery_info.update({"Level": int(battery_dict["BattPct"])}) + elif not is_charging: + # Avoid polling the speaker if possible + self.battery_info["PowerSource"] = "BATTERY" else: - if battery_info := await self.hass.async_add_executor_job( - fetch_battery_info_or_none, self.soco - ): - self.battery_info = battery_info + # Poll to obtain current power source not provided by event + try: + self.battery_info = await self.hass.async_add_executor_job( + self.fetch_battery_info + ) + except SonosUpdateError as err: + _LOGGER.debug("Could not request current power source: %s", err) @property def power_source(self) -> str | None: @@ -692,10 +698,13 @@ class SonosSpeaker: ): return - if battery_info := await self.hass.async_add_executor_job( - fetch_battery_info_or_none, self.soco - ): - self.battery_info = battery_info + try: + self.battery_info = await self.hass.async_add_executor_job( + self.fetch_battery_info + ) + except SonosUpdateError as err: + _LOGGER.debug("Could not poll battery info: %s", err) + else: self.async_write_entity_states() # From 8c2743bb67dca45796c596814e7978905477cd38 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 15:06:31 -0400 Subject: [PATCH 452/930] Avoid storing last_changed in the database if its the same as last_updated (#71843) --- homeassistant/components/logbook/queries.py | 10 ++- homeassistant/components/recorder/history.py | 78 +++++++++++------ homeassistant/components/recorder/models.py | 74 +++++++++------- tests/components/recorder/test_history.py | 92 +++++++++++++++++++- tests/components/recorder/test_models.py | 2 +- 5 files changed, 189 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 2456c73fe22..ba1138c2c26 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -270,7 +270,9 @@ def _legacy_select_events_context_id( NOT_CONTEXT_ONLY, ) .outerjoin(States, (Events.event_id == States.event_id)) - .where(States.last_updated == States.last_changed) + .where( + (States.last_updated == States.last_changed) | States.last_changed.is_(None) + ) .where(_not_continuous_entity_matcher()) .outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) @@ -302,7 +304,7 @@ def _select_states(start_day: dt, end_day: dt) -> Select: "event_type" ), literal(value=None, type_=sqlalchemy.Text).label("event_data"), - States.last_changed.label("time_fired"), + States.last_updated.label("time_fired"), States.context_id.label("context_id"), States.context_user_id.label("context_user_id"), States.context_parent_id.label("context_parent_id"), @@ -314,7 +316,9 @@ def _select_states(start_day: dt, end_day: dt) -> Select: .outerjoin(old_state, (States.old_state_id == old_state.state_id)) .where(_missing_state_matcher(old_state)) .where(_not_continuous_entity_matcher()) - .where(States.last_updated == States.last_changed) + .where( + (States.last_updated == States.last_changed) | States.last_changed.is_(None) + ) .outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 179d25b9f5b..f434c1d5fe2 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -68,19 +68,19 @@ BASE_STATES = [ States.last_changed, States.last_updated, ] -BASE_STATES_NO_LAST_UPDATED = [ +BASE_STATES_NO_LAST_CHANGED = [ States.entity_id, States.state, - States.last_changed, - literal(value=None, type_=Text).label("last_updated"), + literal(value=None, type_=Text).label("last_changed"), + States.last_updated, ] QUERY_STATE_NO_ATTR = [ *BASE_STATES, literal(value=None, type_=Text).label("attributes"), literal(value=None, type_=Text).label("shared_attrs"), ] -QUERY_STATE_NO_ATTR_NO_LAST_UPDATED = [ - *BASE_STATES_NO_LAST_UPDATED, +QUERY_STATE_NO_ATTR_NO_LAST_CHANGED = [ + *BASE_STATES_NO_LAST_CHANGED, literal(value=None, type_=Text).label("attributes"), literal(value=None, type_=Text).label("shared_attrs"), ] @@ -92,8 +92,8 @@ QUERY_STATES_PRE_SCHEMA_25 = [ States.attributes, literal(value=None, type_=Text).label("shared_attrs"), ] -QUERY_STATES_PRE_SCHEMA_25_NO_LAST_UPDATED = [ - *BASE_STATES_NO_LAST_UPDATED, +QUERY_STATES_PRE_SCHEMA_25_NO_LAST_CHANGED = [ + *BASE_STATES_NO_LAST_CHANGED, States.attributes, literal(value=None, type_=Text).label("shared_attrs"), ] @@ -103,8 +103,8 @@ QUERY_STATES = [ States.attributes, StateAttributes.shared_attrs, ] -QUERY_STATES_NO_LAST_UPDATED = [ - *BASE_STATES_NO_LAST_UPDATED, +QUERY_STATES_NO_LAST_CHANGED = [ + *BASE_STATES_NO_LAST_CHANGED, # Remove States.attributes once all attributes are in StateAttributes.shared_attrs States.attributes, StateAttributes.shared_attrs, @@ -114,7 +114,7 @@ HISTORY_BAKERY = "recorder_history_bakery" def bake_query_and_join_attributes( - hass: HomeAssistant, no_attributes: bool, include_last_updated: bool = True + hass: HomeAssistant, no_attributes: bool, include_last_changed: bool = True ) -> tuple[Any, bool]: """Return the initial backed query and if StateAttributes should be joined. @@ -126,31 +126,31 @@ def bake_query_and_join_attributes( # without the attributes fields and do not join the # state_attributes table if no_attributes: - if include_last_updated: + if include_last_changed: return bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR)), False return ( - bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR_NO_LAST_UPDATED)), + bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR_NO_LAST_CHANGED)), False, ) # If we in the process of migrating schema we do # not want to join the state_attributes table as we # do not know if it will be there yet if recorder.get_instance(hass).schema_version < 25: - if include_last_updated: + if include_last_changed: return ( bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25)), False, ) return ( - bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25_NO_LAST_UPDATED)), + bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25_NO_LAST_CHANGED)), False, ) # Finally if no migration is in progress and no_attributes # was not requested, we query both attributes columns and # join state_attributes - if include_last_updated: + if include_last_changed: return bakery(lambda s: s.query(*QUERY_STATES)), True - return bakery(lambda s: s.query(*QUERY_STATES_NO_LAST_UPDATED)), True + return bakery(lambda s: s.query(*QUERY_STATES_NO_LAST_CHANGED)), True def async_setup(hass: HomeAssistant) -> None: @@ -213,7 +213,9 @@ def _query_significant_states_with_session( if _LOGGER.isEnabledFor(logging.DEBUG): timer_start = time.perf_counter() - baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query, join_attributes = bake_query_and_join_attributes( + hass, no_attributes, include_last_changed=True + ) if entity_ids is not None and len(entity_ids) == 1: if ( @@ -221,10 +223,11 @@ def _query_significant_states_with_session( and split_entity_id(entity_ids[0])[0] not in SIGNIFICANT_DOMAINS ): baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_updated=False + hass, no_attributes, include_last_changed=False ) baked_query += lambda q: q.filter( - States.last_changed == States.last_updated + (States.last_changed == States.last_updated) + | States.last_changed.is_(None) ) elif significant_changes_only: baked_query += lambda q: q.filter( @@ -233,7 +236,10 @@ def _query_significant_states_with_session( States.entity_id.like(entity_domain) for entity_domain in SIGNIFICANT_DOMAINS_ENTITY_ID_LIKE ], - (States.last_changed == States.last_updated), + ( + (States.last_changed == States.last_updated) + | States.last_changed.is_(None) + ), ) ) @@ -360,11 +366,14 @@ def state_changes_during_period( """Return states changes during UTC period start_time - end_time.""" with session_scope(hass=hass) as session: baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_updated=False + hass, no_attributes, include_last_changed=False ) baked_query += lambda q: q.filter( - (States.last_changed == States.last_updated) + ( + (States.last_changed == States.last_updated) + | States.last_changed.is_(None) + ) & (States.last_updated > bindparam("start_time")) ) @@ -424,10 +433,12 @@ def get_last_state_changes( with session_scope(hass=hass) as session: baked_query, join_attributes = bake_query_and_join_attributes( - hass, False, include_last_updated=False + hass, False, include_last_changed=False ) - baked_query += lambda q: q.filter(States.last_changed == States.last_updated) + baked_query += lambda q: q.filter( + (States.last_changed == States.last_updated) | States.last_changed.is_(None) + ) if entity_id is not None: baked_query += lambda q: q.filter_by(entity_id=bindparam("entity_id")) @@ -489,7 +500,9 @@ def _get_states_baked_query_for_entites( no_attributes: bool = False, ) -> BakedQuery: """Baked query to get states for specific entities.""" - baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query, join_attributes = bake_query_and_join_attributes( + hass, no_attributes, include_last_changed=True + ) baked_query += _most_recent_state_ids_entities_subquery if join_attributes: baked_query += lambda q: q.outerjoin( @@ -540,7 +553,9 @@ def _get_states_baked_query_for_all( no_attributes: bool = False, ) -> BakedQuery: """Baked query to get states for all entities.""" - baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query, join_attributes = bake_query_and_join_attributes( + hass, no_attributes, include_last_changed=True + ) baked_query += _most_recent_state_ids_subquery baked_query += _ignore_domains_filter if filters: @@ -599,7 +614,9 @@ def _get_single_entity_states_with_session( ) -> list[Row]: # Use an entirely different (and extremely fast) query if we only # have a single entity id - baked_query, join_attributes = bake_query_and_join_attributes(hass, no_attributes) + baked_query, join_attributes = bake_query_and_join_attributes( + hass, no_attributes, include_last_changed=True + ) baked_query += lambda q: q.filter( States.last_updated < bindparam("utc_point_in_time"), States.entity_id == bindparam("entity_id"), @@ -720,7 +737,12 @@ def _sorted_states_to_dict( ent_results.append( { attr_state: state, - attr_last_changed: _process_timestamp(row.last_changed), + # + # minimal_response only makes sense with last_updated == last_updated + # + # We use last_updated for for last_changed since its the same + # + attr_last_changed: _process_timestamp(row.last_updated), } ) prev_state = state diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 38b03eb824e..9faedbbdb1e 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -250,7 +250,7 @@ class States(Base): # type: ignore[misc,valid-type] event_id = Column( Integer, ForeignKey("events.event_id", ondelete="CASCADE"), index=True ) - last_changed = Column(DATETIME_TYPE, default=dt_util.utcnow) + last_changed = Column(DATETIME_TYPE) last_updated = Column(DATETIME_TYPE, default=dt_util.utcnow, index=True) old_state_id = Column(Integer, ForeignKey("states.state_id"), index=True) attributes_id = Column( @@ -291,12 +291,16 @@ class States(Base): # type: ignore[misc,valid-type] # None state means the state was removed from the state machine if state is None: dbstate.state = "" - dbstate.last_changed = event.time_fired dbstate.last_updated = event.time_fired + dbstate.last_changed = None + return dbstate + + dbstate.state = state.state + dbstate.last_updated = state.last_updated + if state.last_updated == state.last_changed: + dbstate.last_changed = None else: - dbstate.state = state.state dbstate.last_changed = state.last_changed - dbstate.last_updated = state.last_updated return dbstate @@ -308,21 +312,27 @@ class States(Base): # type: ignore[misc,valid-type] parent_id=self.context_parent_id, ) try: - return State( - self.entity_id, - self.state, - # Join the state_attributes table on attributes_id to get the attributes - # for newer states - json.loads(self.attributes) if self.attributes else {}, - process_timestamp(self.last_changed), - process_timestamp(self.last_updated), - context=context, - validate_entity_id=validate_entity_id, - ) + attrs = json.loads(self.attributes) if self.attributes else {} except ValueError: # When json.loads fails _LOGGER.exception("Error converting row to state: %s", self) return None + if self.last_changed is None or self.last_changed == self.last_updated: + last_changed = last_updated = process_timestamp(self.last_updated) + else: + last_updated = process_timestamp(self.last_updated) + last_changed = process_timestamp(self.last_changed) + return State( + self.entity_id, + self.state, + # Join the state_attributes table on attributes_id to get the attributes + # for newer states + attrs, + last_changed, + last_updated, + context=context, + validate_entity_id=validate_entity_id, + ) class StateAttributes(Base): # type: ignore[misc,valid-type] @@ -708,7 +718,10 @@ class LazyState(State): def last_changed(self) -> datetime: # type: ignore[override] """Last changed datetime.""" if self._last_changed is None: - self._last_changed = process_timestamp(self._row.last_changed) + if (last_changed := self._row.last_changed) is not None: + self._last_changed = process_timestamp(last_changed) + else: + self._last_changed = self.last_updated return self._last_changed @last_changed.setter @@ -720,10 +733,7 @@ class LazyState(State): def last_updated(self) -> datetime: # type: ignore[override] """Last updated datetime.""" if self._last_updated is None: - if (last_updated := self._row.last_updated) is not None: - self._last_updated = process_timestamp(last_updated) - else: - self._last_updated = self.last_changed + self._last_updated = process_timestamp(self._row.last_updated) return self._last_updated @last_updated.setter @@ -739,24 +749,24 @@ class LazyState(State): To be used for JSON serialization. """ if self._last_changed is None and self._last_updated is None: - last_changed_isoformat = process_timestamp_to_utc_isoformat( - self._row.last_changed + last_updated_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_updated ) if ( - self._row.last_updated is None + self._row.last_changed is None or self._row.last_changed == self._row.last_updated ): - last_updated_isoformat = last_changed_isoformat + last_changed_isoformat = last_updated_isoformat else: - last_updated_isoformat = process_timestamp_to_utc_isoformat( - self._row.last_updated + last_changed_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_changed ) else: - last_changed_isoformat = self.last_changed.isoformat() + last_updated_isoformat = self.last_updated.isoformat() if self.last_changed == self.last_updated: - last_updated_isoformat = last_changed_isoformat + last_changed_isoformat = last_updated_isoformat else: - last_updated_isoformat = self.last_updated.isoformat() + last_changed_isoformat = self.last_changed.isoformat() return { "entity_id": self.entity_id, "state": self.state, @@ -801,13 +811,13 @@ def row_to_compressed_state( if start_time: last_changed = last_updated = start_time.timestamp() else: - row_changed_changed: datetime = row.last_changed + row_last_updated: datetime = row.last_updated if ( - not (row_last_updated := row.last_updated) + not (row_changed_changed := row.last_changed) or row_last_updated == row_changed_changed ): last_changed = last_updated = process_datetime_to_timestamp( - row_changed_changed + row_last_updated ) else: last_changed = process_datetime_to_timestamp(row_changed_changed) diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index a98712ef282..1d59893745b 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -22,12 +22,15 @@ from homeassistant.components.recorder.models import ( ) from homeassistant.components.recorder.util import session_scope import homeassistant.core as ha -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State from homeassistant.helpers.json import JSONEncoder import homeassistant.util.dt as dt_util from tests.common import SetupRecorderInstanceT, mock_state_change_event -from tests.components.recorder.common import wait_recording_done +from tests.components.recorder.common import ( + async_wait_recording_done, + wait_recording_done, +) async def _async_get_states( @@ -79,7 +82,7 @@ def _add_db_entries( entity_id=entity_id, state="on", attributes='{"name":"the light"}', - last_changed=point, + last_changed=None, last_updated=point, event_id=1001 + idx, attributes_id=1002 + idx, @@ -785,3 +788,86 @@ async def test_get_states_query_during_migration_to_schema_25_multiple_entities( ) assert hist[0].attributes == {"name": "the light"} assert hist[1].attributes == {"name": "the light"} + + +async def test_get_full_significant_states_handles_empty_last_changed( + hass: ha.HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, +): + """Test getting states when last_changed is null.""" + await async_setup_recorder_instance(hass, {}) + + now = dt_util.utcnow() + hass.states.async_set("sensor.one", "on", {"attr": "original"}) + state0 = hass.states.get("sensor.one") + await hass.async_block_till_done() + hass.states.async_set("sensor.one", "on", {"attr": "new"}) + state1 = hass.states.get("sensor.one") + + assert state0.last_changed == state1.last_changed + assert state0.last_updated != state1.last_updated + await async_wait_recording_done(hass) + + def _get_entries(): + with session_scope(hass=hass) as session: + return history.get_full_significant_states_with_session( + hass, + session, + now, + dt_util.utcnow(), + entity_ids=["sensor.one"], + significant_changes_only=False, + ) + + states = await recorder.get_instance(hass).async_add_executor_job(_get_entries) + sensor_one_states: list[State] = states["sensor.one"] + assert sensor_one_states[0] == state0 + assert sensor_one_states[1] == state1 + assert sensor_one_states[0].last_changed == sensor_one_states[1].last_changed + assert sensor_one_states[0].last_updated != sensor_one_states[1].last_updated + + def _fetch_native_states() -> list[State]: + with session_scope(hass=hass) as session: + native_states = [] + db_state_attributes = { + state_attributes.attributes_id: state_attributes + for state_attributes in session.query(StateAttributes) + } + for db_state in session.query(States): + state = db_state.to_native() + state.attributes = db_state_attributes[ + db_state.attributes_id + ].to_native() + native_states.append(state) + return native_states + + native_sensor_one_states = await recorder.get_instance(hass).async_add_executor_job( + _fetch_native_states + ) + assert native_sensor_one_states[0] == state0 + assert native_sensor_one_states[1] == state1 + assert ( + native_sensor_one_states[0].last_changed + == native_sensor_one_states[1].last_changed + ) + assert ( + native_sensor_one_states[0].last_updated + != native_sensor_one_states[1].last_updated + ) + + def _fetch_db_states() -> list[State]: + with session_scope(hass=hass) as session: + states = list(session.query(States)) + session.expunge_all() + return states + + db_sensor_one_states = await recorder.get_instance(hass).async_add_executor_job( + _fetch_db_states + ) + assert db_sensor_one_states[0].last_changed is None + assert ( + process_timestamp(db_sensor_one_states[1].last_changed) == state0.last_changed + ) + assert db_sensor_one_states[0].last_updated is not None + assert db_sensor_one_states[1].last_updated is not None + assert db_sensor_one_states[0].last_updated != db_sensor_one_states[1].last_updated diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index ca68d5951d8..9d07c33a17a 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -79,7 +79,7 @@ def test_from_event_to_delete_state(): assert db_state.entity_id == "sensor.temperature" assert db_state.state == "" - assert db_state.last_changed == event.time_fired + assert db_state.last_changed is None assert db_state.last_updated == event.time_fired From ebce5660e3f80ceb95c21d8fe231f792ad0dfd7f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 15:12:08 -0400 Subject: [PATCH 453/930] Sync event timed_fired and the context ulid time (#71854) --- homeassistant/components/recorder/models.py | 16 +- homeassistant/core.py | 10 +- homeassistant/util/dt.py | 17 + homeassistant/util/ulid.py | 9 +- .../generic_hygrostat/test_humidifier.py | 8 +- .../generic_thermostat/test_climate.py | 12 +- tests/test_core.py | 328 +++++++++++++++++- tests/util/test_dt.py | 6 + 8 files changed, 370 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 9faedbbdb1e..2f71324fac0 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -55,10 +55,6 @@ SCHEMA_VERSION = 28 _LOGGER = logging.getLogger(__name__) -# EPOCHORDINAL is not exposed as a constant -# https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L12 -EPOCHORDINAL = datetime(1970, 1, 1).toordinal() - DB_TIMEZONE = "+00:00" TABLE_EVENTS = "events" @@ -649,16 +645,8 @@ def process_datetime_to_timestamp(ts: datetime) -> float: Mirrors the behavior of process_timestamp_to_utc_isoformat except it returns the epoch time. """ - if ts.tzinfo is None: - # Taken from - # https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L185 - return ( - (ts.toordinal() - EPOCHORDINAL) * 86400 - + ts.hour * 3600 - + ts.minute * 60 - + ts.second - + (ts.microsecond / 1000000) - ) + if ts.tzinfo is None or ts.tzinfo == dt_util.UTC: + return dt_util.utc_to_timestamp(ts) return ts.timestamp() diff --git a/homeassistant/core.py b/homeassistant/core.py index 916dd0c6f72..061695ade6a 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -734,7 +734,9 @@ class Event: self.data = data or {} self.origin = origin self.time_fired = time_fired or dt_util.utcnow() - self.context: Context = context or Context() + self.context: Context = context or Context( + id=ulid_util.ulid(dt_util.utc_to_timestamp(self.time_fired)) + ) def __hash__(self) -> int: """Make hashable.""" @@ -1363,11 +1365,11 @@ class StateMachine: if same_state and same_attr: return - if context is None: - context = Context() - now = dt_util.utcnow() + if context is None: + context = Context(id=ulid_util.ulid(dt_util.utc_to_timestamp(now))) + state = State( entity_id, new_state, diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index 4b4b798a2d8..7c0a4923e71 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -14,6 +14,10 @@ DATE_STR_FORMAT = "%Y-%m-%d" UTC = dt.timezone.utc DEFAULT_TIME_ZONE: dt.tzinfo = dt.timezone.utc +# EPOCHORDINAL is not exposed as a constant +# https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L12 +EPOCHORDINAL = dt.datetime(1970, 1, 1).toordinal() + # Copyright (c) Django Software Foundation and individual contributors. # All rights reserved. # https://github.com/django/django/blob/master/LICENSE @@ -98,6 +102,19 @@ def utc_from_timestamp(timestamp: float) -> dt.datetime: return dt.datetime.utcfromtimestamp(timestamp).replace(tzinfo=UTC) +def utc_to_timestamp(utc_dt: dt.datetime) -> float: + """Fast conversion of a datetime in UTC to a timestamp.""" + # Taken from + # https://github.com/python/cpython/blob/3.10/Lib/zoneinfo/_zoneinfo.py#L185 + return ( + (utc_dt.toordinal() - EPOCHORDINAL) * 86400 + + utc_dt.hour * 3600 + + utc_dt.minute * 60 + + utc_dt.second + + (utc_dt.microsecond / 1000000) + ) + + def start_of_local_day(dt_or_d: dt.date | dt.datetime | None = None) -> dt.datetime: """Return local datetime object of start of day from date or datetime.""" if dt_or_d is None: diff --git a/homeassistant/util/ulid.py b/homeassistant/util/ulid.py index c38d95bd169..d40b0f48e16 100644 --- a/homeassistant/util/ulid.py +++ b/homeassistant/util/ulid.py @@ -1,4 +1,5 @@ """Helpers to generate ulids.""" +from __future__ import annotations from random import getrandbits import time @@ -17,7 +18,7 @@ def ulid_hex() -> str: return f"{int(time.time()*1000):012x}{getrandbits(80):020x}" -def ulid() -> str: +def ulid(timestamp: float | None = None) -> str: """Generate a ULID. This ulid should not be used for cryptographically secure @@ -34,9 +35,9 @@ def ulid() -> str: import ulid ulid.parse(ulid_util.ulid()) """ - ulid_bytes = int(time.time() * 1000).to_bytes(6, byteorder="big") + int( - getrandbits(80) - ).to_bytes(10, byteorder="big") + ulid_bytes = int((timestamp or time.time()) * 1000).to_bytes( + 6, byteorder="big" + ) + int(getrandbits(80)).to_bytes(10, byteorder="big") # This is base32 crockford encoding with the loop unrolled for performance # diff --git a/tests/components/generic_hygrostat/test_humidifier.py b/tests/components/generic_hygrostat/test_humidifier.py index d0412731c78..b27b6324f86 100644 --- a/tests/components/generic_hygrostat/test_humidifier.py +++ b/tests/components/generic_hygrostat/test_humidifier.py @@ -811,7 +811,7 @@ async def test_humidity_change_dry_trigger_on_not_long_enough(hass, setup_comp_4 async def test_humidity_change_dry_trigger_on_long_enough(hass, setup_comp_4): """Test if humidity change turn dry on.""" fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc + 1970, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc ) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed @@ -845,7 +845,7 @@ async def test_humidity_change_dry_trigger_off_not_long_enough(hass, setup_comp_ async def test_humidity_change_dry_trigger_off_long_enough(hass, setup_comp_4): """Test if humidity change turn dry on.""" fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc + 1970, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc ) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed @@ -967,7 +967,7 @@ async def test_humidity_change_humidifier_trigger_on_not_long_enough( async def test_humidity_change_humidifier_trigger_on_long_enough(hass, setup_comp_6): """Test if humidity change turn humidifier on after min cycle.""" fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc + 1970, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc ) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed @@ -989,7 +989,7 @@ async def test_humidity_change_humidifier_trigger_on_long_enough(hass, setup_com async def test_humidity_change_humidifier_trigger_off_long_enough(hass, setup_comp_6): """Test if humidity change turn humidifier off after min cycle.""" fake_changed = datetime.datetime( - 1918, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc + 1970, 11, 11, 11, 11, 11, tzinfo=datetime.timezone.utc ) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed diff --git a/tests/components/generic_thermostat/test_climate.py b/tests/components/generic_thermostat/test_climate.py index d607c6dbb61..c4d23b41579 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -749,7 +749,7 @@ async def test_temp_change_ac_trigger_on_not_long_enough(hass, setup_comp_4): async def test_temp_change_ac_trigger_on_long_enough(hass, setup_comp_4): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -775,7 +775,7 @@ async def test_temp_change_ac_trigger_off_not_long_enough(hass, setup_comp_4): async def test_temp_change_ac_trigger_off_long_enough(hass, setup_comp_4): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -855,7 +855,7 @@ async def test_temp_change_ac_trigger_on_not_long_enough_2(hass, setup_comp_5): async def test_temp_change_ac_trigger_on_long_enough_2(hass, setup_comp_5): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -881,7 +881,7 @@ async def test_temp_change_ac_trigger_off_not_long_enough_2(hass, setup_comp_5): async def test_temp_change_ac_trigger_off_long_enough_2(hass, setup_comp_5): """Test if temperature change turn ac on.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -969,7 +969,7 @@ async def test_temp_change_heater_trigger_on_not_long_enough(hass, setup_comp_6) async def test_temp_change_heater_trigger_on_long_enough(hass, setup_comp_6): """Test if temperature change turn heater on after min cycle.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): @@ -986,7 +986,7 @@ async def test_temp_change_heater_trigger_on_long_enough(hass, setup_comp_6): async def test_temp_change_heater_trigger_off_long_enough(hass, setup_comp_6): """Test if temperature change turn heater off after min cycle.""" - fake_changed = datetime.datetime(1918, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) + fake_changed = datetime.datetime(1970, 11, 11, 11, 11, 11, tzinfo=dt_util.UTC) with patch( "homeassistant.helpers.condition.dt_util.utcnow", return_value=fake_changed ): diff --git a/tests/test_core.py b/tests/test_core.py index c870605fc01..104828f64e1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,5 +1,8 @@ """Test to verify that Home Assistant core works.""" +from __future__ import annotations + # pylint: disable=protected-access +import array import asyncio from datetime import datetime, timedelta import functools @@ -28,6 +31,7 @@ from homeassistant.const import ( __version__, ) import homeassistant.core as ha +from homeassistant.core import State from homeassistant.exceptions import ( InvalidEntityFormatError, InvalidStateError, @@ -1489,9 +1493,300 @@ async def test_reserving_states(hass): assert hass.states.async_available("light.bedroom") is True -async def test_state_change_events_match_state_time(hass): - """Test last_updated and timed_fired only call utcnow once.""" +def _ulid_timestamp(ulid: str) -> int: + encoded = ulid[:10].encode("ascii") + # This unpacks the time from the ulid + # Copied from + # https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L296 + decoding = array.array( + "B", + ( + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + 0x10, + 0x11, + 0x01, + 0x12, + 0x13, + 0x01, + 0x14, + 0x15, + 0x00, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0xFF, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + 0x10, + 0x11, + 0x01, + 0x12, + 0x13, + 0x01, + 0x14, + 0x15, + 0x00, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0xFF, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ), + ) + return int.from_bytes( + bytes( + ( + ((decoding[encoded[0]] << 5) | decoding[encoded[1]]) & 0xFF, + ((decoding[encoded[2]] << 3) | (decoding[encoded[3]] >> 2)) & 0xFF, + ( + (decoding[encoded[3]] << 6) + | (decoding[encoded[4]] << 1) + | (decoding[encoded[5]] >> 4) + ) + & 0xFF, + ((decoding[encoded[5]] << 4) | (decoding[encoded[6]] >> 1)) & 0xFF, + ( + (decoding[encoded[6]] << 7) + | (decoding[encoded[7]] << 2) + | (decoding[encoded[8]] >> 3) + ) + & 0xFF, + ((decoding[encoded[8]] << 5) | (decoding[encoded[9]])) & 0xFF, + ) + ), + byteorder="big", + ) + + +async def test_state_change_events_context_id_match_state_time(hass): + """Test last_updated, timed_fired, and the ulid all have the same time.""" events = [] @ha.callback @@ -1502,6 +1797,31 @@ async def test_state_change_events_match_state_time(hass): hass.states.async_set("light.bedroom", "on") await hass.async_block_till_done() - state = hass.states.get("light.bedroom") - + state: State = hass.states.get("light.bedroom") assert state.last_updated == events[0].time_fired + assert len(state.context.id) == 26 + # ULIDs store time to 3 decimal places compared to python timestamps + assert _ulid_timestamp(state.context.id) == int( + state.last_updated.timestamp() * 1000 + ) + + +async def test_state_firing_event_matches_context_id_ulid_time(hass): + """Test timed_fired and the ulid have the same time.""" + events = [] + + @ha.callback + def _event_listener(event): + events.append(event) + + hass.bus.async_listen(EVENT_HOMEASSISTANT_STARTED, _event_listener) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) + await hass.async_block_till_done() + + event = events[0] + assert len(event.context.id) == 26 + # ULIDs store time to 3 decimal places compared to python timestamps + assert _ulid_timestamp(event.context.id) == int( + events[0].time_fired.timestamp() * 1000 + ) diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index d2c453f070d..6e499e6e6f1 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -106,6 +106,12 @@ def test_utc_from_timestamp(): ) +def test_timestamp_to_utc(): + """Test we can convert a utc datetime to a timestamp.""" + utc_now = dt_util.utcnow() + assert dt_util.utc_to_timestamp(utc_now) == utc_now.timestamp() + + def test_as_timestamp(): """Test as_timestamp method.""" ts = 1462401234 From 4e9bc9eaffd464f192d187a01771a86699b2f932 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 15:13:32 -0400 Subject: [PATCH 454/930] Small cleanups to find_next_time_expression and addition of tests (#71845) --- homeassistant/util/dt.py | 8 +++---- tests/util/test_dt.py | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index 7c0a4923e71..c7073c0306f 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -339,8 +339,8 @@ def find_next_time_expression_time( now += dt.timedelta(seconds=1) continue - now_is_ambiguous = _datetime_ambiguous(now) - result_is_ambiguous = _datetime_ambiguous(result) + if not _datetime_ambiguous(now): + return result # When leaving DST and clocks are turned backward. # Then there are wall clock times that are ambiguous i.e. exist with DST and without DST @@ -348,7 +348,7 @@ def find_next_time_expression_time( # in a day. # Example: on 2021.10.31 02:00:00 in CET timezone clocks are turned backward an hour - if now_is_ambiguous and result_is_ambiguous: + if _datetime_ambiguous(result): # `now` and `result` are both ambiguous, so the next match happens # _within_ the current fold. @@ -357,7 +357,7 @@ def find_next_time_expression_time( # 2. 2021.10.31 02:00:00+01:00 with pattern 02:30 -> 2021.10.31 02:30:00+01:00 return result.replace(fold=now.fold) - if now_is_ambiguous and now.fold == 0 and not result_is_ambiguous: + if now.fold == 0: # `now` is in the first fold, but result is not ambiguous (meaning it no longer matches # within the fold). # -> Check if result matches in the next fold. If so, emit that match diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index 6e499e6e6f1..c7992e05068 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -641,3 +641,53 @@ def test_find_next_time_expression_time_leave_dst_chicago_past_the_fold_ahead_2_ assert dt_util.as_utc(next_time) == datetime( 2021, 11, 7, 8, 20, 1, tzinfo=dt_util.UTC ) + + +def test_find_next_time_expression_microseconds(): + """Test finding next time expression with microsecond clock drift.""" + hour_minute_second = (None, "5", "10") + test_time = datetime(2022, 5, 13, 0, 5, 9, tzinfo=dt_util.UTC) + matching_hours, matching_minutes, matching_seconds = _get_matches( + *hour_minute_second + ) + next_time = dt_util.find_next_time_expression_time( + test_time, matching_seconds, matching_minutes, matching_hours + ) + assert next_time == datetime(2022, 5, 13, 0, 5, 10, tzinfo=dt_util.UTC) + next_time_last_microsecond_plus_one = next_time.replace( + microsecond=999999 + ) + timedelta(seconds=1) + time_after = dt_util.find_next_time_expression_time( + next_time_last_microsecond_plus_one, + matching_seconds, + matching_minutes, + matching_hours, + ) + assert time_after == datetime(2022, 5, 13, 1, 5, 10, tzinfo=dt_util.UTC) + + +def test_find_next_time_expression_tenth_second_pattern_does_not_drift_entering_dst(): + """Test finding next time expression tenth second pattern does not drift entering dst.""" + tz = dt_util.get_time_zone("America/Chicago") + dt_util.set_default_time_zone(tz) + tenth_second_pattern = (None, None, "10") + # Entering DST, clocks go forward + test_time = datetime(2021, 3, 15, 2, 30, 0, tzinfo=tz, fold=0) + matching_hours, matching_minutes, matching_seconds = _get_matches( + *tenth_second_pattern + ) + next_time = dt_util.find_next_time_expression_time( + test_time, matching_seconds, matching_minutes, matching_hours + ) + assert next_time == datetime(2021, 3, 15, 2, 30, 10, tzinfo=tz) + prev_target = next_time + for i in range(1000): + next_target = dt_util.find_next_time_expression_time( + prev_target.replace(microsecond=999999) + timedelta(seconds=1), + matching_seconds, + matching_minutes, + matching_hours, + ) + assert (next_target - prev_target).total_seconds() == 60 + assert next_target.second == 10 + prev_target = next_target From cd2898886bef9938e472d6d0f9170e8745a6954d Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 14 May 2022 12:24:52 -0700 Subject: [PATCH 455/930] Upgrade grpcio to 1.46.1 (#71865) --- homeassistant/package_constraints.txt | 4 ++-- script/gen_requirements_all.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 9c9354c584b..ec3ac5de9b3 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -50,8 +50,8 @@ httplib2>=0.19.0 # gRPC is an implicit dependency that we want to make explicit so we manage # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. -grpcio==1.45.0 -grpcio-status==1.45.0 +grpcio==1.46.1 +grpcio-status==1.46.1 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index be94fdac22b..369045e5124 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -66,8 +66,8 @@ httplib2>=0.19.0 # gRPC is an implicit dependency that we want to make explicit so we manage # upgrades intentionally. It is a large package to build from source and we # want to ensure we have wheels built. -grpcio==1.45.0 -grpcio-status==1.45.0 +grpcio==1.46.1 +grpcio-status==1.46.1 # libcst >=0.4.0 requires a newer Rust than we currently have available, # thus our wheels builds fail. This pins it to the last working version, From 68632cb2671c26a8d367c1dafb6ac2e6c2904b95 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 15:37:35 -0400 Subject: [PATCH 456/930] Implement use_include_order in the history websocket api (#71839) --- homeassistant/components/history/__init__.py | 121 ++++++++++--------- homeassistant/components/logbook/queries.py | 2 +- tests/components/history/test_init.py | 58 +++++++++ 3 files changed, 124 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index e5b6c99eb2a..2ebe6405a7a 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -10,6 +10,8 @@ from typing import Any, Literal, cast from aiohttp import web from sqlalchemy import not_, or_ +from sqlalchemy.ext.baked import BakedQuery +from sqlalchemy.orm import Query import voluptuous as vol from homeassistant.components import frontend, websocket_api @@ -36,12 +38,12 @@ from homeassistant.helpers.entityfilter import ( from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util -# mypy: allow-untyped-defs, no-check-untyped-defs - _LOGGER = logging.getLogger(__name__) DOMAIN = "history" HISTORY_FILTERS = "history_filters" +HISTORY_USE_INCLUDE_ORDER = "history_use_include_order" + CONF_ORDER = "use_include_order" GLOB_TO_SQL_CHARS = { @@ -66,8 +68,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.data[HISTORY_FILTERS] = filters = sqlalchemy_filter_from_include_exclude_conf( conf ) - - use_include_order = conf.get(CONF_ORDER) + hass.data[HISTORY_USE_INCLUDE_ORDER] = use_include_order = conf.get(CONF_ORDER) hass.http.register_view(HistoryPeriodView(filters, use_include_order)) frontend.async_register_built_in_panel(hass, "history", "history", "hass:chart-box") @@ -176,30 +177,41 @@ def _ws_get_significant_states( hass: HomeAssistant, msg_id: int, start_time: dt, - end_time: dt | None = None, - entity_ids: list[str] | None = None, - filters: Any | None = None, - include_start_time_state: bool = True, - significant_changes_only: bool = True, - minimal_response: bool = False, - no_attributes: bool = False, + end_time: dt | None, + entity_ids: list[str] | None, + filters: Filters | None, + use_include_order: bool | None, + include_start_time_state: bool, + significant_changes_only: bool, + minimal_response: bool, + no_attributes: bool, ) -> str: """Fetch history significant_states and convert them to json in the executor.""" + states = history.get_significant_states( + hass, + start_time, + end_time, + entity_ids, + filters, + include_start_time_state, + significant_changes_only, + minimal_response, + no_attributes, + True, + ) + + if not use_include_order or not filters: + return JSON_DUMP(messages.result_message(msg_id, states)) + return JSON_DUMP( messages.result_message( msg_id, - history.get_significant_states( - hass, - start_time, - end_time, - entity_ids, - filters, - include_start_time_state, - significant_changes_only, - minimal_response, - no_attributes, - True, - ), + { + order_entity: states.pop(order_entity) + for order_entity in filters.included_entities + if order_entity in states + } + | states, ) ) @@ -267,6 +279,7 @@ async def ws_get_history_during_period( end_time, entity_ids, hass.data[HISTORY_FILTERS], + hass.data[HISTORY_USE_INCLUDE_ORDER], include_start_time_state, significant_changes_only, minimal_response, @@ -351,20 +364,20 @@ class HistoryPeriodView(HomeAssistantView): def _sorted_significant_states_json( self, - hass, - start_time, - end_time, - entity_ids, - include_start_time_state, - significant_changes_only, - minimal_response, - no_attributes, - ): + hass: HomeAssistant, + start_time: dt, + end_time: dt, + entity_ids: list[str] | None, + include_start_time_state: bool, + significant_changes_only: bool, + minimal_response: bool, + no_attributes: bool, + ) -> web.Response: """Fetch significant stats from the database as json.""" timer_start = time.perf_counter() with session_scope(hass=hass) as session: - result = history.get_significant_states_with_session( + states = history.get_significant_states_with_session( hass, session, start_time, @@ -377,25 +390,24 @@ class HistoryPeriodView(HomeAssistantView): no_attributes, ) - result = list(result.values()) if _LOGGER.isEnabledFor(logging.DEBUG): elapsed = time.perf_counter() - timer_start - _LOGGER.debug("Extracted %d states in %fs", sum(map(len, result)), elapsed) + _LOGGER.debug( + "Extracted %d states in %fs", sum(map(len, states.values())), elapsed + ) # Optionally reorder the result to respect the ordering given # by any entities explicitly included in the configuration. - if self.filters and self.use_include_order: - sorted_result = [] - for order_entity in self.filters.included_entities: - for state_list in result: - if state_list[0].entity_id == order_entity: - sorted_result.append(state_list) - result.remove(state_list) - break - sorted_result.extend(result) - result = sorted_result + if not self.filters or not self.use_include_order: + return self.json(list(states.values())) - return self.json(result) + sorted_result = [ + states.pop(order_entity) + for order_entity in self.filters.included_entities + if order_entity in states + ] + sorted_result.extend(list(states.values())) + return self.json(sorted_result) def sqlalchemy_filter_from_include_exclude_conf(conf: ConfigType) -> Filters | None: @@ -426,7 +438,7 @@ class Filters: self.included_domains: list[str] = [] self.included_entity_globs: list[str] = [] - def apply(self, query): + def apply(self, query: Query) -> Query: """Apply the entity filter.""" if not self.has_config: return query @@ -434,21 +446,18 @@ class Filters: return query.filter(self.entity_filter()) @property - def has_config(self): + def has_config(self) -> bool: """Determine if there is any filter configuration.""" - if ( + return bool( self.excluded_entities or self.excluded_domains or self.excluded_entity_globs or self.included_entities or self.included_domains or self.included_entity_globs - ): - return True + ) - return False - - def bake(self, baked_query): + def bake(self, baked_query: BakedQuery) -> None: """Update a baked query. Works the same as apply on a baked_query. @@ -458,7 +467,7 @@ class Filters: baked_query += lambda q: q.filter(self.entity_filter()) - def entity_filter(self): + def entity_filter(self) -> Any: """Generate the entity filter query.""" includes = [] if self.included_domains: @@ -502,7 +511,7 @@ class Filters: return or_(*includes) & not_(or_(*excludes)) -def _glob_to_like(glob_str): +def _glob_to_like(glob_str: str) -> Any: """Translate glob to sql.""" return history_models.States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index ba1138c2c26..1cea6599327 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -83,7 +83,7 @@ def statement_for_request( # No entities: logbook sends everything for the timeframe # limited by the context_id and the yaml configured filter if not entity_ids: - entity_filter = filters.entity_filter() if filters else None # type: ignore[no-untyped-call] + entity_filter = filters.entity_filter() if filters else None return _all_stmt(start_day, end_day, event_types, entity_filter, context_id) # Multiple entities: logbook sends everything for the timeframe for the entities diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 1dc18e5cc73..bcbab1e21ca 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -11,6 +11,7 @@ from pytest import approx from homeassistant.components import history from homeassistant.components.recorder.history import get_significant_states from homeassistant.components.recorder.models import process_timestamp +from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES import homeassistant.core as ha from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component @@ -1476,3 +1477,60 @@ async def test_history_during_period_bad_end_time(hass, hass_ws_client, recorder response = await client.receive_json() assert not response["success"] assert response["error"]["code"] == "invalid_end_time" + + +async def test_history_during_period_with_use_include_order( + hass, hass_ws_client, recorder_mock +): + """Test history_during_period.""" + now = dt_util.utcnow() + sort_order = ["sensor.two", "sensor.four", "sensor.one"] + await async_setup_component( + hass, + "history", + { + history.DOMAIN: { + history.CONF_ORDER: True, + history.CONF_INCLUDE: { + CONF_ENTITIES: sort_order, + CONF_DOMAINS: ["sensor"], + }, + } + }, + ) + await async_setup_component(hass, "sensor", {}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.one", "on", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.two", "off", attributes={"any": "attr"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.three", "off", attributes={"any": "changed"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("sensor.four", "off", attributes={"any": "again"}) + await async_recorder_block_till_done(hass) + hass.states.async_set("switch.excluded", "off", attributes={"any": "again"}) + await async_wait_recording_done(hass) + + do_adhoc_statistics(hass, start=now) + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "history/history_during_period", + "start_time": now.isoformat(), + "include_start_time_state": True, + "significant_changes_only": False, + "no_attributes": True, + "minimal_response": True, + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + assert list(response["result"]) == [ + *sort_order, + "sensor.three", + ] From 0584e84c30903aae07cf16898138ce4e1e8b6be7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 17:01:36 -0500 Subject: [PATCH 457/930] Add MySQL index hints to logbook (#71864) * Add MySQL index hints to logbook * fix mysql query planner --- homeassistant/components/logbook/queries.py | 102 ++++++++++++++------ homeassistant/components/recorder/models.py | 4 +- 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 1cea6599327..e8e691f2787 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -7,7 +7,7 @@ from typing import Any import sqlalchemy from sqlalchemy import lambda_stmt, select, union_all -from sqlalchemy.orm import aliased +from sqlalchemy.orm import Query, aliased from sqlalchemy.sql.expression import literal from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import Select @@ -15,6 +15,8 @@ from sqlalchemy.sql.selectable import Select from homeassistant.components.history import Filters from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder.models import ( + ENTITY_ID_LAST_UPDATED_INDEX, + LAST_UPDATED_INDEX, EventData, Events, StateAttributes, @@ -31,6 +33,8 @@ CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" +OLD_STATE = aliased(States, name="old_state") + EVENT_COLUMNS = ( Events.event_id.label("event_id"), @@ -126,7 +130,7 @@ def _select_entities_context_ids_sub_query( _select_events_context_id_subquery(start_day, end_day, event_types).where( _apply_event_entity_id_matchers(entity_ids) ), - select(States.context_id) + _apply_entities_hints(select(States.context_id)) .filter((States.last_updated > start_day) & (States.last_updated < end_day)) .where(States.entity_id.in_(entity_ids)), ).c.context_id @@ -156,7 +160,7 @@ def _entities_stmt( ) stmt = stmt.add_criteria( lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)).union_all( - _select_states(start_day, end_day).where(States.entity_id.in_(entity_ids)), + _states_query_for_entitiy_ids(start_day, end_day, entity_ids), _select_events_context_only().where( Events.context_id.in_( _select_entities_context_ids_sub_query( @@ -192,7 +196,7 @@ def _select_entity_context_ids_sub_query( Events.event_data.like(entity_id_like) | EventData.shared_data.like(entity_id_like) ), - select(States.context_id) + _apply_entities_hints(select(States.context_id)) .filter((States.last_updated > start_day) & (States.last_updated < end_day)) .where(States.entity_id == entity_id), ).c.context_id @@ -214,7 +218,7 @@ def _single_entity_stmt( | EventData.shared_data.like(entity_id_like) ) .union_all( - _select_states(start_day, end_day).where(States.entity_id == entity_id), + _states_query_for_entitiy_id(start_day, end_day, entity_id), _select_events_context_only().where( Events.context_id.in_( _select_entity_context_ids_sub_query( @@ -244,15 +248,15 @@ def _all_stmt( # are gone from the database remove the # _legacy_select_events_context_id() stmt += lambda s: s.where(Events.context_id == context_id).union_all( - _select_states(start_day, end_day).where(States.context_id == context_id), + _states_query_for_context_id(start_day, end_day, context_id), _legacy_select_events_context_id(start_day, end_day, context_id), ) elif entity_filter is not None: stmt += lambda s: s.union_all( - _select_states(start_day, end_day).where(entity_filter) + _states_query_for_all(start_day, end_day).where(entity_filter) ) else: - stmt += lambda s: s.union_all(_select_states(start_day, end_day)) + stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day)) stmt += lambda s: s.order_by(Events.time_fired) return stmt @@ -294,27 +298,67 @@ def _select_events_without_states( ) -def _select_states(start_day: dt, end_day: dt) -> Select: +def _states_query_for_context_id(start_day: dt, end_day: dt, context_id: str) -> Query: + return _apply_states_filters(_select_states(), start_day, end_day).where( + States.context_id == context_id + ) + + +def _states_query_for_entitiy_id(start_day: dt, end_day: dt, entity_id: str) -> Query: + return _apply_states_filters( + _apply_entities_hints(_select_states()), start_day, end_day + ).where(States.entity_id == entity_id) + + +def _states_query_for_entitiy_ids( + start_day: dt, end_day: dt, entity_ids: list[str] +) -> Query: + return _apply_states_filters( + _apply_entities_hints(_select_states()), start_day, end_day + ).where(States.entity_id.in_(entity_ids)) + + +def _states_query_for_all(start_day: dt, end_day: dt) -> Query: + return _apply_states_filters(_apply_all_hints(_select_states()), start_day, end_day) + + +def _select_states() -> Select: """Generate a states select that formats the states table as event rows.""" - old_state = aliased(States, name="old_state") + return select( + literal(value=None, type_=sqlalchemy.Text).label("event_id"), + literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label("event_type"), + literal(value=None, type_=sqlalchemy.Text).label("event_data"), + States.last_updated.label("time_fired"), + States.context_id.label("context_id"), + States.context_user_id.label("context_user_id"), + States.context_parent_id.label("context_parent_id"), + literal(value=None, type_=sqlalchemy.Text).label("shared_data"), + *STATE_COLUMNS, + NOT_CONTEXT_ONLY, + ) + + +def _apply_all_hints(query: Query) -> Query: + """Force mysql to use the right index on large selects.""" + return query.with_hint( + States, f"FORCE INDEX ({LAST_UPDATED_INDEX})", dialect_name="mysql" + ) + + +def _apply_entities_hints(query: Query) -> Query: + """Force mysql to use the right index on large selects.""" + return query.with_hint( + States, f"FORCE INDEX ({ENTITY_ID_LAST_UPDATED_INDEX})", dialect_name="mysql" + ) + + +def _apply_states_filters(query: Query, start_day: dt, end_day: dt) -> Query: return ( - select( - literal(value=None, type_=sqlalchemy.Text).label("event_id"), - literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( - "event_type" - ), - literal(value=None, type_=sqlalchemy.Text).label("event_data"), - States.last_updated.label("time_fired"), - States.context_id.label("context_id"), - States.context_user_id.label("context_user_id"), - States.context_parent_id.label("context_parent_id"), - literal(value=None, type_=sqlalchemy.Text).label("shared_data"), - *STATE_COLUMNS, - NOT_CONTEXT_ONLY, + query.filter( + (States.last_updated > start_day) & (States.last_updated < end_day) ) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .outerjoin(old_state, (States.old_state_id == old_state.state_id)) - .where(_missing_state_matcher(old_state)) + .outerjoin(OLD_STATE, (States.old_state_id == OLD_STATE.state_id)) + .where(_missing_state_matcher()) .where(_not_continuous_entity_matcher()) .where( (States.last_updated == States.last_changed) | States.last_changed.is_(None) @@ -325,13 +369,13 @@ def _select_states(start_day: dt, end_day: dt) -> Select: ) -def _missing_state_matcher(old_state: States) -> sqlalchemy.and_: +def _missing_state_matcher() -> sqlalchemy.and_: # The below removes state change events that do not have # and old_state or the old_state is missing (newly added entities) # or the new_state is missing (removed entities) return sqlalchemy.and_( - old_state.state_id.isnot(None), - (States.state != old_state.state), + OLD_STATE.state_id.isnot(None), + (States.state != OLD_STATE.state), States.state.isnot(None), ) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 2f71324fac0..4c3832c4fc0 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -88,6 +88,8 @@ TABLES_TO_CHECK = [ TABLE_SCHEMA_CHANGES, ] +LAST_UPDATED_INDEX = "ix_states_last_updated" +ENTITY_ID_LAST_UPDATED_INDEX = "ix_states_entity_id_last_updated" EMPTY_JSON_OBJECT = "{}" @@ -235,7 +237,7 @@ class States(Base): # type: ignore[misc,valid-type] __table_args__ = ( # Used for fetching the state of entities at a specific time # (get_states in history.py) - Index("ix_states_entity_id_last_updated", "entity_id", "last_updated"), + Index(ENTITY_ID_LAST_UPDATED_INDEX, "entity_id", "last_updated"), {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, ) __tablename__ = TABLE_STATES From 51c6a68036acb91bce72beb94f366d583d5a2b6a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 17:22:47 -0500 Subject: [PATCH 458/930] Add Big Ass Fans integration (#71498) Co-authored-by: Paulus Schoutsen --- .coveragerc | 3 + .strict-typing | 1 + CODEOWNERS | 2 + homeassistant/components/baf/__init__.py | 46 +++++ homeassistant/components/baf/config_flow.py | 120 +++++++++++++ homeassistant/components/baf/const.py | 19 +++ homeassistant/components/baf/entity.py | 48 ++++++ homeassistant/components/baf/fan.py | 97 +++++++++++ homeassistant/components/baf/manifest.json | 13 ++ homeassistant/components/baf/models.py | 25 +++ homeassistant/components/baf/strings.json | 23 +++ .../components/baf/translations/en.json | 23 +++ homeassistant/generated/config_flows.py | 1 + homeassistant/generated/zeroconf.py | 14 ++ mypy.ini | 11 ++ requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/baf/__init__.py | 1 + tests/components/baf/test_config_flow.py | 158 ++++++++++++++++++ 19 files changed, 611 insertions(+) create mode 100644 homeassistant/components/baf/__init__.py create mode 100644 homeassistant/components/baf/config_flow.py create mode 100644 homeassistant/components/baf/const.py create mode 100644 homeassistant/components/baf/entity.py create mode 100644 homeassistant/components/baf/fan.py create mode 100644 homeassistant/components/baf/manifest.json create mode 100644 homeassistant/components/baf/models.py create mode 100644 homeassistant/components/baf/strings.json create mode 100644 homeassistant/components/baf/translations/en.json create mode 100644 tests/components/baf/__init__.py create mode 100644 tests/components/baf/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 2a8e6cdec9a..15b9544dad5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -93,6 +93,9 @@ omit = homeassistant/components/azure_devops/const.py homeassistant/components/azure_devops/sensor.py homeassistant/components/azure_service_bus/* + homeassistant/components/baf/__init__.py + homeassistant/components/baf/entity.py + homeassistant/components/baf/fan.py homeassistant/components/baidu/tts.py homeassistant/components/balboa/__init__.py homeassistant/components/beewi_smartclim/sensor.py diff --git a/.strict-typing b/.strict-typing index 8c580dfa1aa..36ed1685e9f 100644 --- a/.strict-typing +++ b/.strict-typing @@ -55,6 +55,7 @@ homeassistant.components.aseko_pool_live.* homeassistant.components.asuswrt.* homeassistant.components.automation.* homeassistant.components.backup.* +homeassistant.components.baf.* homeassistant.components.binary_sensor.* homeassistant.components.bluetooth_tracker.* homeassistant.components.bmw_connected_drive.* diff --git a/CODEOWNERS b/CODEOWNERS index 68f9017363e..0805b17cff5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -120,6 +120,8 @@ build.json @home-assistant/supervisor /homeassistant/components/azure_service_bus/ @hfurubotten /homeassistant/components/backup/ @home-assistant/core /tests/components/backup/ @home-assistant/core +/homeassistant/components/baf/ @bdraco @jfroy +/tests/components/baf/ @bdraco @jfroy /homeassistant/components/balboa/ @garbled1 /tests/components/balboa/ @garbled1 /homeassistant/components/beewi_smartclim/ @alemuro diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py new file mode 100644 index 00000000000..4327eb9ddb9 --- /dev/null +++ b/homeassistant/components/baf/__init__.py @@ -0,0 +1,46 @@ +"""The Big Ass Fans integration.""" +from __future__ import annotations + +import asyncio + +from aiobafi6 import Device, Service +from aiobafi6.discovery import PORT + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_IP_ADDRESS, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady + +from .const import DOMAIN, QUERY_INTERVAL, RUN_TIMEOUT +from .models import BAFData + +PLATFORMS: list[Platform] = [Platform.FAN] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up Big Ass Fans from a config entry.""" + ip_address = entry.data[CONF_IP_ADDRESS] + + service = Service(ip_addresses=[ip_address], uuid=entry.unique_id, port=PORT) + device = Device(service, query_interval_seconds=QUERY_INTERVAL) + run_future = device.async_run() + + try: + await asyncio.wait_for(device.async_wait_available(), timeout=RUN_TIMEOUT) + except asyncio.TimeoutError as ex: + run_future.cancel() + raise ConfigEntryNotReady(f"Timed out connecting to {ip_address}") from ex + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = BAFData(device, run_future) + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + data: BAFData = hass.data[DOMAIN].pop(entry.entry_id) + data.run_future.cancel() + + return unload_ok diff --git a/homeassistant/components/baf/config_flow.py b/homeassistant/components/baf/config_flow.py new file mode 100644 index 00000000000..1c2873eb759 --- /dev/null +++ b/homeassistant/components/baf/config_flow.py @@ -0,0 +1,120 @@ +"""Config flow for baf.""" +from __future__ import annotations + +import asyncio +import logging +from typing import Any + +from aiobafi6 import Device, Service +from aiobafi6.discovery import PORT +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.components import zeroconf +from homeassistant.const import CONF_IP_ADDRESS +from homeassistant.data_entry_flow import FlowResult +from homeassistant.util.network import is_ipv6_address + +from .const import DOMAIN, RUN_TIMEOUT +from .models import BAFDiscovery + +_LOGGER = logging.getLogger(__name__) + + +async def async_try_connect(ip_address: str) -> Device: + """Validate we can connect to a device.""" + device = Device(Service(ip_addresses=[ip_address], port=PORT)) + run_future = device.async_run() + try: + await asyncio.wait_for(device.async_wait_available(), timeout=RUN_TIMEOUT) + except asyncio.TimeoutError as ex: + raise CannotConnect from ex + finally: + run_future.cancel() + return device + + +class BAFFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Handle BAF discovery config flow.""" + + VERSION = 1 + + def __init__(self) -> None: + """Initialize the BAF config flow.""" + self.discovery: BAFDiscovery | None = None + + async def async_step_zeroconf( + self, discovery_info: zeroconf.ZeroconfServiceInfo + ) -> FlowResult: + """Handle zeroconf discovery.""" + properties = discovery_info.properties + ip_address = discovery_info.host + if is_ipv6_address(ip_address): + return self.async_abort(reason="ipv6_not_supported") + uuid = properties["uuid"] + model = properties["model"] + name = properties["name"] + await self.async_set_unique_id(uuid, raise_on_progress=False) + self._abort_if_unique_id_configured(updates={CONF_IP_ADDRESS: ip_address}) + self.discovery = BAFDiscovery(ip_address, name, uuid, model) + return await self.async_step_discovery_confirm() + + async def async_step_discovery_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm discovery.""" + assert self.discovery is not None + discovery = self.discovery + if user_input is not None: + return self.async_create_entry( + title=discovery.name, + data={CONF_IP_ADDRESS: discovery.ip_address}, + ) + placeholders = { + "name": discovery.name, + "model": discovery.model, + "ip_address": discovery.ip_address, + } + self.context["title_placeholders"] = placeholders + self._set_confirm_only() + return self.async_show_form( + step_id="discovery_confirm", description_placeholders=placeholders + ) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + errors = {} + ip_address = (user_input or {}).get(CONF_IP_ADDRESS, "") + if user_input is not None: + try: + device = await async_try_connect(ip_address) + except CannotConnect: + errors[CONF_IP_ADDRESS] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception( + "Unknown exception during connection test to %s", ip_address + ) + errors["base"] = "unknown" + else: + await self.async_set_unique_id(device.dns_sd_uuid) + self._abort_if_unique_id_configured( + updates={CONF_IP_ADDRESS: ip_address} + ) + return self.async_create_entry( + title=device.name, + data={CONF_IP_ADDRESS: ip_address}, + ) + + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + {vol.Required(CONF_IP_ADDRESS, default=ip_address): str} + ), + errors=errors, + ) + + +class CannotConnect(Exception): + """Exception to raise when we cannot connect.""" diff --git a/homeassistant/components/baf/const.py b/homeassistant/components/baf/const.py new file mode 100644 index 00000000000..9876d7ffec3 --- /dev/null +++ b/homeassistant/components/baf/const.py @@ -0,0 +1,19 @@ +"""Constants for the Big Ass Fans integration.""" + +DOMAIN = "baf" + +# Most properties are pushed, only the +# query every 5 minutes so we keep the RPM +# sensors up to date +QUERY_INTERVAL = 300 + +RUN_TIMEOUT = 20 + +PRESET_MODE_AUTO = "Auto" + +SPEED_COUNT = 7 +SPEED_RANGE = (1, SPEED_COUNT) + +ONE_MIN_SECS = 60 +ONE_DAY_SECS = 86400 +HALF_DAY_SECS = 43200 diff --git a/homeassistant/components/baf/entity.py b/homeassistant/components/baf/entity.py new file mode 100644 index 00000000000..22054d0b16d --- /dev/null +++ b/homeassistant/components/baf/entity.py @@ -0,0 +1,48 @@ +"""The baf integration entities.""" +from __future__ import annotations + +from aiobafi6 import Device + +from homeassistant.core import callback +from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.device_registry import format_mac +from homeassistant.helpers.entity import DeviceInfo, Entity + + +class BAFEntity(Entity): + """Base class for baf entities.""" + + _attr_should_poll = False + + def __init__(self, device: Device, name: str) -> None: + """Initialize the entity.""" + self._device = device + self._attr_unique_id = format_mac(self._device.mac_address) + self._attr_name = name + self._attr_device_info = DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, self._device.mac_address)}, + name=self._device.name, + manufacturer="Big Ass Fans", + model=self._device.model, + sw_version=self._device.firmware_version, + ) + self._async_update_attrs() + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + self._attr_available = self._device.available + + @callback + def _async_update_from_device(self, device: Device) -> None: + """Process an update from the device.""" + self._async_update_attrs() + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Add data updated listener after this object has been initialized.""" + self._device.add_callback(self._async_update_from_device) + + async def async_will_remove_from_hass(self) -> None: + """Remove data updated listener after this object has been initialized.""" + self._device.remove_callback(self._async_update_from_device) diff --git a/homeassistant/components/baf/fan.py b/homeassistant/components/baf/fan.py new file mode 100644 index 00000000000..360926363a5 --- /dev/null +++ b/homeassistant/components/baf/fan.py @@ -0,0 +1,97 @@ +"""Support for Big Ass Fans fan.""" +from __future__ import annotations + +import math +from typing import Any + +from aiobafi6 import OffOnAuto + +from homeassistant import config_entries +from homeassistant.components.fan import ( + DIRECTION_FORWARD, + DIRECTION_REVERSE, + FanEntity, + FanEntityFeature, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util.percentage import ( + percentage_to_ranged_value, + ranged_value_to_percentage, +) + +from .const import DOMAIN, PRESET_MODE_AUTO, SPEED_COUNT, SPEED_RANGE +from .entity import BAFEntity +from .models import BAFData + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up SenseME fans.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + if data.device.has_fan: + async_add_entities([BAFFan(data.device, data.device.name)]) + + +class BAFFan(BAFEntity, FanEntity): + """BAF ceiling fan component.""" + + _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION + _attr_preset_modes = [PRESET_MODE_AUTO] + _attr_speed_count = SPEED_COUNT + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + self._attr_is_on = self._device.fan_mode == OffOnAuto.ON + self._attr_current_direction = DIRECTION_FORWARD + if self._device.reverse_enable: + self._attr_current_direction = DIRECTION_REVERSE + if self._device.speed is not None: + self._attr_percentage = ranged_value_to_percentage( + SPEED_RANGE, self._device.speed + ) + else: + self._attr_percentage = None + auto = self._device.fan_mode == OffOnAuto.AUTO + self._attr_preset_mode = PRESET_MODE_AUTO if auto else None + super()._async_update_attrs() + + async def async_set_percentage(self, percentage: int) -> None: + """Set the speed of the fan, as a percentage.""" + device = self._device + if device.fan_mode != OffOnAuto.ON: + device.fan_mode = OffOnAuto.ON + device.speed = math.ceil(percentage_to_ranged_value(SPEED_RANGE, percentage)) + + async def async_turn_on( + self, + percentage: int | None = None, + preset_mode: str | None = None, + **kwargs: Any, + ) -> None: + """Turn the fan on with a percentage or preset mode.""" + if preset_mode is not None: + await self.async_set_preset_mode(preset_mode) + return + if percentage is None: + self._device.fan_mode = OffOnAuto.ON + return + await self.async_set_percentage(percentage) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the fan off.""" + self._device.fan_mode = OffOnAuto.OFF + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set the preset mode of the fan.""" + if preset_mode != PRESET_MODE_AUTO: + raise ValueError(f"Invalid preset mode: {preset_mode}") + self._device.fan_mode = OffOnAuto.AUTO + + async def async_set_direction(self, direction: str) -> None: + """Set the direction of the fan.""" + self._device.reverse_enable = direction == DIRECTION_REVERSE diff --git a/homeassistant/components/baf/manifest.json b/homeassistant/components/baf/manifest.json new file mode 100644 index 00000000000..9dfc35685e3 --- /dev/null +++ b/homeassistant/components/baf/manifest.json @@ -0,0 +1,13 @@ +{ + "domain": "baf", + "name": "Big Ass Fans", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/baf", + "requirements": ["aiobafi6==0.3.0"], + "codeowners": ["@bdraco", "@jfroy"], + "iot_class": "local_push", + "zeroconf": [ + { "type": "_api._tcp.local.", "properties": { "model": "haiku*" } }, + { "type": "_api._tcp.local.", "properties": { "model": "i6*" } } + ] +} diff --git a/homeassistant/components/baf/models.py b/homeassistant/components/baf/models.py new file mode 100644 index 00000000000..de5c4a3498b --- /dev/null +++ b/homeassistant/components/baf/models.py @@ -0,0 +1,25 @@ +"""The baf integration models.""" +from __future__ import annotations + +import asyncio +from dataclasses import dataclass + +from aiobafi6 import Device + + +@dataclass +class BAFData: + """Data for the baf integration.""" + + device: Device + run_future: asyncio.Future + + +@dataclass +class BAFDiscovery: + """A BAF Discovery.""" + + ip_address: str + name: str + uuid: str + model: str diff --git a/homeassistant/components/baf/strings.json b/homeassistant/components/baf/strings.json new file mode 100644 index 00000000000..a26e3152326 --- /dev/null +++ b/homeassistant/components/baf/strings.json @@ -0,0 +1,23 @@ +{ + "config": { + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "user": { + "data": { + "ip_address": "[%key:common::config_flow::data::ip%]" + } + }, + "discovery_confirm": { + "description": "Do you want to setup {name} - {model} ({ip_address})?" + } + }, + "abort": { + "ipv6_not_supported": "IPv6 is not supported.", + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + } + } +} diff --git a/homeassistant/components/baf/translations/en.json b/homeassistant/components/baf/translations/en.json new file mode 100644 index 00000000000..4bb7256a692 --- /dev/null +++ b/homeassistant/components/baf/translations/en.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured", + "ipv6_not_supported": "IPv6 is not supported." + }, + "error": { + "cannot_connect": "Failed to connect", + "unknown": "Unexpected error" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Do you want to setup {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "IP Address" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 89a9e4e489a..77146689b8c 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -42,6 +42,7 @@ FLOWS = { "axis", "azure_devops", "azure_event_hub", + "baf", "balboa", "blebox", "blink", diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index d1a15356ddc..415e2746c6d 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -42,6 +42,20 @@ ZEROCONF = { "domain": "apple_tv" } ], + "_api._tcp.local.": [ + { + "domain": "baf", + "properties": { + "model": "haiku*" + } + }, + { + "domain": "baf", + "properties": { + "model": "i6*" + } + } + ], "_api._udp.local.": [ { "domain": "guardian" diff --git a/mypy.ini b/mypy.ini index 532c4526372..7784acd9fe6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -368,6 +368,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.baf.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.binary_sensor.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index fe73d53bee5..e1bde3fb6ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -121,6 +121,9 @@ aioasuswrt==1.4.0 # homeassistant.components.azure_devops aioazuredevops==1.3.5 +# homeassistant.components.baf +aiobafi6==0.3.0 + # homeassistant.components.aws aiobotocore==2.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5bf3a4aa4eb..e2d7267149f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -108,6 +108,9 @@ aioasuswrt==1.4.0 # homeassistant.components.azure_devops aioazuredevops==1.3.5 +# homeassistant.components.baf +aiobafi6==0.3.0 + # homeassistant.components.aws aiobotocore==2.1.0 diff --git a/tests/components/baf/__init__.py b/tests/components/baf/__init__.py new file mode 100644 index 00000000000..e0432ed643a --- /dev/null +++ b/tests/components/baf/__init__.py @@ -0,0 +1 @@ +"""Tests for the Big Ass Fans integration.""" diff --git a/tests/components/baf/test_config_flow.py b/tests/components/baf/test_config_flow.py new file mode 100644 index 00000000000..687a871ed4c --- /dev/null +++ b/tests/components/baf/test_config_flow.py @@ -0,0 +1,158 @@ +"""Test the baf config flow.""" +import asyncio +from unittest.mock import patch + +from homeassistant import config_entries +from homeassistant.components import zeroconf +from homeassistant.components.baf.const import DOMAIN +from homeassistant.const import CONF_IP_ADDRESS +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) + +from tests.common import MockConfigEntry + + +async def test_form_user(hass): + """Test we get the user form.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( + "homeassistant.components.baf.config_flow.Device.async_wait_available", + ), patch( + "homeassistant.components.baf.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_IP_ADDRESS: "127.0.0.1"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == "127.0.0.1" + assert result2["data"] == {CONF_IP_ADDRESS: "127.0.0.1"} + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_cannot_connect(hass): + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( + "homeassistant.components.baf.config_flow.Device.async_wait_available", + side_effect=asyncio.TimeoutError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_IP_ADDRESS: "127.0.0.1"}, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {CONF_IP_ADDRESS: "cannot_connect"} + + +async def test_form_unknown_exception(hass): + """Test we handle unknown exceptions.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( + "homeassistant.components.baf.config_flow.Device.async_wait_available", + side_effect=Exception, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_IP_ADDRESS: "127.0.0.1"}, + ) + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_zeroconf_discovery(hass): + """Test we can setup from zeroconf discovery.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="127.0.0.1", + addresses=["127.0.0.1"], + hostname="mock_hostname", + name="testfan", + port=None, + properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + type="mock_type", + ), + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] is None + + with patch( + "homeassistant.components.baf.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "My Fan" + assert result2["data"] == {CONF_IP_ADDRESS: "127.0.0.1"} + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_zeroconf_updates_existing_ip(hass): + """Test we can setup from zeroconf discovery.""" + entry = MockConfigEntry( + domain=DOMAIN, data={CONF_IP_ADDRESS: "127.0.0.2"}, unique_id="1234" + ) + entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="127.0.0.1", + addresses=["127.0.0.1"], + hostname="mock_hostname", + name="testfan", + port=None, + properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + type="mock_type", + ), + ) + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + assert entry.data[CONF_IP_ADDRESS] == "127.0.0.1" + + +async def test_zeroconf_rejects_ipv6(hass): + """Test zeroconf discovery rejects ipv6.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="fd00::b27c:63bb:cc85:4ea0", + addresses=["fd00::b27c:63bb:cc85:4ea0"], + hostname="mock_hostname", + name="testfan", + port=None, + properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + type="mock_type", + ), + ) + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "ipv6_not_supported" From 10624e93c802dadb624e92d58faf617cbffa11ea Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 15 May 2022 00:30:16 +0000 Subject: [PATCH 459/930] [ci skip] Translation update --- .../aladdin_connect/translations/ca.json | 26 +++++++++++ .../aladdin_connect/translations/de.json | 27 +++++++++++ .../aladdin_connect/translations/el.json | 27 +++++++++++ .../aladdin_connect/translations/et.json | 27 +++++++++++ .../aladdin_connect/translations/fr.json | 27 +++++++++++ .../aladdin_connect/translations/hu.json | 27 +++++++++++ .../aladdin_connect/translations/ja.json | 27 +++++++++++ .../aladdin_connect/translations/nl.json | 27 +++++++++++ .../aladdin_connect/translations/pt-BR.json | 27 +++++++++++ .../aladdin_connect/translations/ru.json | 27 +++++++++++ .../aladdin_connect/translations/zh-Hans.json | 26 +++++++++++ .../aladdin_connect/translations/zh-Hant.json | 27 +++++++++++ .../components/apple_tv/translations/es.json | 2 +- .../components/baf/translations/nl.json | 23 ++++++++++ .../components/bosch_shc/translations/ja.json | 2 +- .../components/canary/translations/es.json | 2 +- .../components/generic/translations/ca.json | 4 ++ .../components/generic/translations/de.json | 4 ++ .../components/generic/translations/el.json | 4 ++ .../components/generic/translations/et.json | 4 ++ .../components/generic/translations/fr.json | 4 ++ .../components/generic/translations/hu.json | 4 ++ .../components/generic/translations/id.json | 4 ++ .../components/generic/translations/it.json | 4 ++ .../components/generic/translations/ja.json | 4 ++ .../components/generic/translations/nl.json | 4 ++ .../components/generic/translations/no.json | 4 ++ .../components/generic/translations/pl.json | 4 ++ .../generic/translations/pt-BR.json | 4 ++ .../components/generic/translations/ru.json | 4 ++ .../generic/translations/zh-Hans.json | 11 +++++ .../generic/translations/zh-Hant.json | 4 ++ .../geocaching/translations/ja.json | 25 +++++++++++ .../geocaching/translations/nl.json | 25 +++++++++++ .../geocaching/translations/no.json | 25 +++++++++++ .../geocaching/translations/pl.json | 25 +++++++++++ .../geocaching/translations/ru.json | 25 +++++++++++ .../geocaching/translations/zh-Hans.json | 21 +++++++++ .../components/google/translations/ja.json | 2 +- .../components/isy994/translations/ja.json | 1 + .../litterrobot/translations/sensor.ca.json | 9 ++++ .../litterrobot/translations/sensor.de.json | 28 ++++++++++++ .../litterrobot/translations/sensor.el.json | 28 ++++++++++++ .../litterrobot/translations/sensor.et.json | 28 ++++++++++++ .../litterrobot/translations/sensor.fr.json | 28 ++++++++++++ .../litterrobot/translations/sensor.hu.json | 28 ++++++++++++ .../litterrobot/translations/sensor.ja.json | 28 ++++++++++++ .../litterrobot/translations/sensor.nl.json | 25 +++++++++++ .../translations/sensor.pt-BR.json | 28 ++++++++++++ .../litterrobot/translations/sensor.ru.json | 28 ++++++++++++ .../translations/sensor.zh-Hans.json | 10 +++++ .../translations/sensor.zh-Hant.json | 28 ++++++++++++ .../components/meater/translations/ja.json | 6 ++- .../min_max/translations/zh-Hans.json | 4 +- .../motion_blinds/translations/ca.json | 2 +- .../motion_blinds/translations/de.json | 2 +- .../motion_blinds/translations/et.json | 2 +- .../motion_blinds/translations/fr.json | 2 +- .../motion_blinds/translations/id.json | 2 +- .../motion_blinds/translations/it.json | 2 +- .../motion_blinds/translations/nl.json | 2 +- .../motion_blinds/translations/pt-BR.json | 2 +- .../motion_blinds/translations/ru.json | 2 +- .../motion_blinds/translations/zh-Hant.json | 2 +- .../components/nest/translations/ja.json | 2 +- .../components/netatmo/translations/ja.json | 2 +- .../components/onewire/translations/es.json | 1 + .../simplisafe/translations/ja.json | 8 +++- .../components/slack/translations/ca.json | 22 +++++++++ .../components/slack/translations/de.json | 29 ++++++++++++ .../components/slack/translations/el.json | 29 ++++++++++++ .../components/slack/translations/en.json | 4 +- .../components/slack/translations/et.json | 29 ++++++++++++ .../components/slack/translations/fr.json | 29 ++++++++++++ .../components/slack/translations/hu.json | 29 ++++++++++++ .../components/slack/translations/ja.json | 29 ++++++++++++ .../components/slack/translations/nl.json | 29 ++++++++++++ .../components/slack/translations/pt-BR.json | 29 ++++++++++++ .../components/slack/translations/ru.json | 29 ++++++++++++ .../slack/translations/zh-Hans.json | 29 ++++++++++++ .../slack/translations/zh-Hant.json | 29 ++++++++++++ .../components/smarttub/translations/ja.json | 2 +- .../steam_online/translations/ja.json | 11 +++++ .../components/tautulli/translations/ja.json | 4 +- .../totalconnect/translations/ja.json | 2 +- .../trafikverket_ferry/translations/ja.json | 3 +- .../ukraine_alarm/translations/ca.json | 12 +++-- .../ukraine_alarm/translations/de.json | 12 +++-- .../ukraine_alarm/translations/el.json | 10 ++++- .../ukraine_alarm/translations/es.json | 11 +++++ .../ukraine_alarm/translations/et.json | 12 +++-- .../ukraine_alarm/translations/fr.json | 12 +++-- .../ukraine_alarm/translations/hu.json | 10 ++++- .../ukraine_alarm/translations/id.json | 12 +++-- .../ukraine_alarm/translations/it.json | 12 +++-- .../ukraine_alarm/translations/ja.json | 10 ++++- .../ukraine_alarm/translations/nl.json | 12 +++-- .../ukraine_alarm/translations/no.json | 12 +++-- .../ukraine_alarm/translations/pl.json | 12 +++-- .../ukraine_alarm/translations/pt-BR.json | 12 +++-- .../ukraine_alarm/translations/ru.json | 24 ++++++---- .../ukraine_alarm/translations/uk.json | 45 +++++++++++++++++++ .../ukraine_alarm/translations/zh-Hans.json | 18 ++++++++ .../ukraine_alarm/translations/zh-Hant.json | 12 +++-- .../components/vulcan/translations/ja.json | 4 +- 105 files changed, 1466 insertions(+), 75 deletions(-) create mode 100644 homeassistant/components/aladdin_connect/translations/ca.json create mode 100644 homeassistant/components/aladdin_connect/translations/de.json create mode 100644 homeassistant/components/aladdin_connect/translations/el.json create mode 100644 homeassistant/components/aladdin_connect/translations/et.json create mode 100644 homeassistant/components/aladdin_connect/translations/fr.json create mode 100644 homeassistant/components/aladdin_connect/translations/hu.json create mode 100644 homeassistant/components/aladdin_connect/translations/ja.json create mode 100644 homeassistant/components/aladdin_connect/translations/nl.json create mode 100644 homeassistant/components/aladdin_connect/translations/pt-BR.json create mode 100644 homeassistant/components/aladdin_connect/translations/ru.json create mode 100644 homeassistant/components/aladdin_connect/translations/zh-Hans.json create mode 100644 homeassistant/components/aladdin_connect/translations/zh-Hant.json create mode 100644 homeassistant/components/baf/translations/nl.json create mode 100644 homeassistant/components/generic/translations/zh-Hans.json create mode 100644 homeassistant/components/geocaching/translations/ja.json create mode 100644 homeassistant/components/geocaching/translations/nl.json create mode 100644 homeassistant/components/geocaching/translations/no.json create mode 100644 homeassistant/components/geocaching/translations/pl.json create mode 100644 homeassistant/components/geocaching/translations/ru.json create mode 100644 homeassistant/components/geocaching/translations/zh-Hans.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.ca.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.de.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.el.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.et.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.fr.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.hu.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.ja.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.nl.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.pt-BR.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.ru.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.zh-Hans.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.zh-Hant.json create mode 100644 homeassistant/components/slack/translations/ca.json create mode 100644 homeassistant/components/slack/translations/de.json create mode 100644 homeassistant/components/slack/translations/el.json create mode 100644 homeassistant/components/slack/translations/et.json create mode 100644 homeassistant/components/slack/translations/fr.json create mode 100644 homeassistant/components/slack/translations/hu.json create mode 100644 homeassistant/components/slack/translations/ja.json create mode 100644 homeassistant/components/slack/translations/nl.json create mode 100644 homeassistant/components/slack/translations/pt-BR.json create mode 100644 homeassistant/components/slack/translations/ru.json create mode 100644 homeassistant/components/slack/translations/zh-Hans.json create mode 100644 homeassistant/components/slack/translations/zh-Hant.json create mode 100644 homeassistant/components/ukraine_alarm/translations/es.json create mode 100644 homeassistant/components/ukraine_alarm/translations/uk.json create mode 100644 homeassistant/components/ukraine_alarm/translations/zh-Hans.json diff --git a/homeassistant/components/aladdin_connect/translations/ca.json b/homeassistant/components/aladdin_connect/translations/ca.json new file mode 100644 index 00000000000..c7115a32099 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/ca.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Contrasenya" + }, + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + }, + "user": { + "data": { + "password": "Contrasenya", + "username": "Nom d'usuari" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/de.json b/homeassistant/components/aladdin_connect/translations/de.json new file mode 100644 index 00000000000..057f31e9078 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/de.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Passwort" + }, + "description": "Die Aladdin Connect-Integration muss dein Konto erneut authentifizieren", + "title": "Integration erneut authentifizieren" + }, + "user": { + "data": { + "password": "Passwort", + "username": "Benutzername" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/el.json b/homeassistant/components/aladdin_connect/translations/el.json new file mode 100644 index 00000000000..6fa2af154f7 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/el.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "reauth_successful": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + }, + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Aladdin Connect \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03c3\u03b1\u03c2", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + }, + "user": { + "data": { + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/et.json b/homeassistant/components/aladdin_connect/translations/et.json new file mode 100644 index 00000000000..4eb34a1d8bf --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/et.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Salas\u00f5na" + }, + "description": "Aladdin Connecti sidumine peab konto uuesti autentima", + "title": "Taastuvasta sidumine" + }, + "user": { + "data": { + "password": "Salas\u00f5na", + "username": "Kasutajanimi" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/fr.json b/homeassistant/components/aladdin_connect/translations/fr.json new file mode 100644 index 00000000000..5586c751335 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/fr.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Mot de passe" + }, + "description": "L'int\u00e9gration Aladdin Connect doit r\u00e9-authentifier votre compte", + "title": "R\u00e9-authentifier l'int\u00e9gration" + }, + "user": { + "data": { + "password": "Mot de passe", + "username": "Nom d'utilisateur" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/hu.json b/homeassistant/components/aladdin_connect/translations/hu.json new file mode 100644 index 00000000000..8d0f979e05a --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/hu.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Jelsz\u00f3" + }, + "description": "The Aladdin Connect integration needs to re-authenticate your account", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + }, + "user": { + "data": { + "password": "Jelsz\u00f3", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/ja.json b/homeassistant/components/aladdin_connect/translations/ja.json new file mode 100644 index 00000000000..196b7f1d066 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/ja.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" + }, + "description": "Aladdin Connect\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + }, + "user": { + "data": { + "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/nl.json b/homeassistant/components/aladdin_connect/translations/nl.json new file mode 100644 index 00000000000..314df5712f8 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/nl.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Wachtwoord" + }, + "description": "De Aladdin Connect-integratie moet uw account opnieuw verifi\u00ebren", + "title": "Verifieer de integratie opnieuw" + }, + "user": { + "data": { + "password": "Wachtwoord", + "username": "Gebruikersnaam" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/pt-BR.json b/homeassistant/components/aladdin_connect/translations/pt-BR.json new file mode 100644 index 00000000000..2d709bf1125 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/pt-BR.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Senha" + }, + "description": "A integra\u00e7\u00e3o do Aladdin Connect precisa autenticar novamente sua conta", + "title": "Reautenticar Integra\u00e7\u00e3o" + }, + "user": { + "data": { + "password": "Senha", + "username": "Nome de usu\u00e1rio" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/ru.json b/homeassistant/components/aladdin_connect/translations/ru.json new file mode 100644 index 00000000000..d2db9c388ce --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/ru.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + }, + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Aladdin Connect.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + }, + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/zh-Hans.json b/homeassistant/components/aladdin_connect/translations/zh-Hans.json new file mode 100644 index 00000000000..d7088d39951 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/zh-Hans.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u8bbe\u5907\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86", + "reauth_successful": "\u91cd\u65b0\u8ba4\u8bc1\u6210\u529f" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_auth": "\u8eab\u4efd\u8ba4\u8bc1\u65e0\u6548" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u5bc6\u7801" + }, + "title": "\u4f7f\u96c6\u6210\u91cd\u65b0\u8fdb\u884c\u8eab\u4efd\u8ba4\u8bc1" + }, + "user": { + "data": { + "password": "\u5bc6\u7801", + "username": "\u7528\u6237\u540d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/zh-Hant.json b/homeassistant/components/aladdin_connect/translations/zh-Hant.json new file mode 100644 index 00000000000..1dcca5b6f9e --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/zh-Hant.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u5bc6\u78bc" + }, + "description": "Aladdin \u9023\u7dda\u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + }, + "user": { + "data": { + "password": "\u5bc6\u78bc", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/es.json b/homeassistant/components/apple_tv/translations/es.json index f72b7ec4cff..65f096ca6ae 100644 --- a/homeassistant/components/apple_tv/translations/es.json +++ b/homeassistant/components/apple_tv/translations/es.json @@ -24,7 +24,7 @@ "flow_title": "Apple TV: {name}", "step": { "confirm": { - "description": "Est\u00e1s a punto de a\u00f1adir el Apple TV con nombre `{name}` a Home Assistant.\n\n**Para completar el proceso, puede que tengas que introducir varios c\u00f3digos PIN.**\n\nTen en cuenta que *no* podr\u00e1s apagar tu Apple TV con esta integraci\u00f3n. \u00a1S\u00f3lo se apagar\u00e1 el reproductor de medios de Home Assistant!", + "description": "Est\u00e1s a punto de a\u00f1adir `{name}` con el tipo `{type}` en Home Assistant.\n\n**Para completar el proceso, puede que tengas que introducir varios c\u00f3digos PIN.**\n\nTen en cuenta que *no* podr\u00e1s apagar tu Apple TV con esta integraci\u00f3n. \u00a1S\u00f3lo se apagar\u00e1 el reproductor de medios de Home Assistant!", "title": "Confirma la adici\u00f3n del Apple TV" }, "pair_no_pin": { diff --git a/homeassistant/components/baf/translations/nl.json b/homeassistant/components/baf/translations/nl.json new file mode 100644 index 00000000000..9b619390165 --- /dev/null +++ b/homeassistant/components/baf/translations/nl.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd", + "ipv6_not_supported": "IPv6 wordt niet ondersteund" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "unknown": "Onverwachte fout" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Wilt u {name} - {model} ({ip_address}) instellen?" + }, + "user": { + "data": { + "ip_address": "IP-adres" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/ja.json b/homeassistant/components/bosch_shc/translations/ja.json index 5146f81301c..0a624bd2eb0 100644 --- a/homeassistant/components/bosch_shc/translations/ja.json +++ b/homeassistant/components/bosch_shc/translations/ja.json @@ -22,7 +22,7 @@ } }, "reauth_confirm": { - "description": "bosch_shc\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "description": "Bosch_shc\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" }, "user": { diff --git a/homeassistant/components/canary/translations/es.json b/homeassistant/components/canary/translations/es.json index 018a1f30b08..76a439da49d 100644 --- a/homeassistant/components/canary/translations/es.json +++ b/homeassistant/components/canary/translations/es.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "Canary: {name}", + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/generic/translations/ca.json b/homeassistant/components/generic/translations/ca.json index 3c2f5055ba5..7d69e837130 100644 --- a/homeassistant/components/generic/translations/ca.json +++ b/homeassistant/components/generic/translations/ca.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protocol de transport RTSP", "still_image_url": "URL d'imatge fixa (p. ex. http://...)", "stream_source": "URL origen del flux (p. ex. rtsp://...)", + "use_wallclock_as_timestamps": "Utilitza el rellotge de paret com a marca de temps", "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Aquesta opci\u00f3 pot corregir problemes de segmentaci\u00f3 o bloqueig en algunes c\u00e0meres derivats d'implementacions de marca de temps amb errors" } } } diff --git a/homeassistant/components/generic/translations/de.json b/homeassistant/components/generic/translations/de.json index 849555dcc5e..8d40216001a 100644 --- a/homeassistant/components/generic/translations/de.json +++ b/homeassistant/components/generic/translations/de.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP-Transportprotokoll", "still_image_url": "Standbild-URL (z.B. http://...)", "stream_source": "Stream-Quell-URL (z.B. rtsp://...)", + "use_wallclock_as_timestamps": "Wanduhr als Zeitstempel verwenden", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + }, + "data_description": { + "use_wallclock_as_timestamps": "Diese Option kann Segmentierungs- oder Absturzprobleme beheben, die durch fehlerhafte Zeitstempel-Implementierungen auf einigen Kameras entstehen" } } } diff --git a/homeassistant/components/generic/translations/el.json b/homeassistant/components/generic/translations/el.json index f3fae37f5ca..8b15c7de851 100644 --- a/homeassistant/components/generic/translations/el.json +++ b/homeassistant/components/generic/translations/el.json @@ -79,8 +79,12 @@ "rtsp_transport": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03bf \u03bc\u03b5\u03c4\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 RTSP", "still_image_url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03b5\u03b9\u03ba\u03cc\u03bd\u03b1\u03c2 (\u03c0.\u03c7. http://...)", "stream_source": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03c0\u03b7\u03b3\u03ae\u03c2 \u03c1\u03bf\u03ae\u03c2 (\u03c0.\u03c7. rtsp://...)", + "use_wallclock_as_timestamps": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c1\u03bf\u03bb\u03bf\u03b3\u03b9\u03bf\u03cd \u03c4\u03bf\u03af\u03c7\u03bf\u03c5 \u03c9\u03c2 \u03c7\u03c1\u03bf\u03bd\u03bf\u03c3\u03c6\u03c1\u03b1\u03b3\u03af\u03b4\u03b5\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b4\u03b9\u03bf\u03c1\u03b8\u03ce\u03c3\u03b5\u03b9 \u03b6\u03b7\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c4\u03bc\u03b7\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03ae \u03c3\u03c6\u03b1\u03bb\u03bc\u03ac\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03c0\u03c1\u03bf\u03ba\u03cd\u03c0\u03c4\u03bf\u03c5\u03bd \u03b1\u03c0\u03cc \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ad\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ce\u03bd \u03c3\u03c6\u03c1\u03b1\u03b3\u03af\u03b4\u03c9\u03bd \u03bc\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1\u03c4\u03b1 \u03c3\u03b5 \u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03ba\u03ac\u03bc\u03b5\u03c1\u03b5\u03c2" } } } diff --git a/homeassistant/components/generic/translations/et.json b/homeassistant/components/generic/translations/et.json index ae6f5aa75f8..0a4bdc0731f 100644 --- a/homeassistant/components/generic/translations/et.json +++ b/homeassistant/components/generic/translations/et.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP transpordiprotokoll", "still_image_url": "Pildi URL (nt http://...)", "stream_source": "Voo allikas URL (nt rtsp://...)", + "use_wallclock_as_timestamps": "Wallclocki kasutamine ajatemplitena", "username": "Kasutajanimi", "verify_ssl": "Kontrolli SSL sertifikaati" + }, + "data_description": { + "use_wallclock_as_timestamps": "See suvand v\u00f5ib parandada segmentimis- v\u00f5i krahhiprobleeme, mis tulenevad m\u00f5ne kaamera vigastest ajatemplirakendustest" } } } diff --git a/homeassistant/components/generic/translations/fr.json b/homeassistant/components/generic/translations/fr.json index 4905afb64e7..454f9b0de03 100644 --- a/homeassistant/components/generic/translations/fr.json +++ b/homeassistant/components/generic/translations/fr.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protocole de transport RTSP", "still_image_url": "URL d'image fixe (par exemple, http://\u2026)", "stream_source": "URL de la source du flux (par exemple, rtsp://\u2026)", + "use_wallclock_as_timestamps": "Utiliser l'horloge murale comme horodatage", "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Cette option peut corriger des probl\u00e8mes de segmentation ou de plantage dus \u00e0 des impl\u00e9mentations d'horodatage d\u00e9fectueuses sur certaines cam\u00e9ras" } } } diff --git a/homeassistant/components/generic/translations/hu.json b/homeassistant/components/generic/translations/hu.json index 4e97e594d1e..bc8172caaf9 100644 --- a/homeassistant/components/generic/translations/hu.json +++ b/homeassistant/components/generic/translations/hu.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP protokoll", "still_image_url": "\u00c1ll\u00f3k\u00e9p URL (pl. http://...)", "stream_source": "Mozg\u00f3k\u00e9p adatfolyam URL (pl. rtsp://...)", + "use_wallclock_as_timestamps": "Wallclock haszn\u00e1lata id\u0151b\u00e9lyegz\u0151k\u00e9nt", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" + }, + "data_description": { + "use_wallclock_as_timestamps": "Ez az opci\u00f3 korrig\u00e1lhatja az egyes kamer\u00e1k hib\u00e1s id\u0151b\u00e9lyegz\u0151 implement\u00e1ci\u00f3j\u00e1b\u00f3l ered\u0151 szegment\u00e1l\u00e1si vagy \u00f6sszeoml\u00e1si probl\u00e9m\u00e1kat." } } } diff --git a/homeassistant/components/generic/translations/id.json b/homeassistant/components/generic/translations/id.json index 4b3106ad1df..7c4be981e4a 100644 --- a/homeassistant/components/generic/translations/id.json +++ b/homeassistant/components/generic/translations/id.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protokol transportasi RTSP", "still_image_url": "URL Gambar Diam (mis. http://...)", "stream_source": "URL Sumber Streaming (mis. rtsp://...)", + "use_wallclock_as_timestamps": "Gunakan jam dinding sebagai stempel waktu", "username": "Nama Pengguna", "verify_ssl": "Verifikasi sertifikat SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Opsi ini dapat memperbaiki masalah segmentasi atau mogok yang timbul dari implementasi stempel waktu bermasalah pada beberapa kamera" } } } diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json index 880fcff500b..e5e41e50285 100644 --- a/homeassistant/components/generic/translations/it.json +++ b/homeassistant/components/generic/translations/it.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protocollo di trasporto RTSP", "still_image_url": "URL immagine fissa (ad es. http://...)", "stream_source": "URL sorgente del flusso (ad es. rtsp://...)", + "use_wallclock_as_timestamps": "Usa wallclock come marca temporale", "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Questa opzione pu\u00f2 correggere problemi di segmentazione o di arresto anomalo derivanti da implementazioni difettose di marche temporali su alcune telecamere" } } } diff --git a/homeassistant/components/generic/translations/ja.json b/homeassistant/components/generic/translations/ja.json index 916142125e6..97c1a76ab36 100644 --- a/homeassistant/components/generic/translations/ja.json +++ b/homeassistant/components/generic/translations/ja.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP\u30c8\u30e9\u30f3\u30b9\u30dd\u30fc\u30c8\u30d7\u30ed\u30c8\u30b3\u30eb", "still_image_url": "\u9759\u6b62\u753b\u50cf\u306eURL(\u4f8b: http://...)", "stream_source": "\u30b9\u30c8\u30ea\u30fc\u30e0\u30bd\u30fc\u30b9\u306eURL(\u4f8b: rtsp://...)", + "use_wallclock_as_timestamps": "\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u306bwallclock\u3092\u4f7f\u7528", "username": "\u30e6\u30fc\u30b6\u30fc\u540d", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" + }, + "data_description": { + "use_wallclock_as_timestamps": "\u3053\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u3001\u4e00\u90e8\u306e\u30ab\u30e1\u30e9\u306e\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u5b9f\u88c5\u306e\u30d0\u30b0\u304b\u3089\u767a\u751f\u3059\u308b\u30bb\u30b0\u30e1\u30f3\u30c6\u30fc\u30b7\u30e7\u30f3\u3084\u30af\u30e9\u30c3\u30b7\u30e5\u306e\u554f\u984c\u3092\u4fee\u6b63\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059" } } } diff --git a/homeassistant/components/generic/translations/nl.json b/homeassistant/components/generic/translations/nl.json index 167374c7aeb..5c45b783c4f 100644 --- a/homeassistant/components/generic/translations/nl.json +++ b/homeassistant/components/generic/translations/nl.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP-transportprotocol", "still_image_url": "URL van stilstaande afbeelding (bijv. http://...)", "stream_source": "Url van streambron (bijv. rtsp://...)", + "use_wallclock_as_timestamps": "Gebruik de wandklok als tijdstempel", "username": "Gebruikersnaam", "verify_ssl": "SSL-certificaat verifi\u00ebren" + }, + "data_description": { + "use_wallclock_as_timestamps": "Deze optie kan problemen met segmenteren of crashen verhelpen die het gevolg zijn van buggy timestamp implementaties op sommige camera's" } } } diff --git a/homeassistant/components/generic/translations/no.json b/homeassistant/components/generic/translations/no.json index 1f9eedaced4..78b5618b073 100644 --- a/homeassistant/components/generic/translations/no.json +++ b/homeassistant/components/generic/translations/no.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP transportprotokoll", "still_image_url": "Stillbilde-URL (f.eks. http://...)", "stream_source": "Str\u00f8mkilde-URL (f.eks. rtsp://...)", + "use_wallclock_as_timestamps": "Bruk veggklokke som tidsstempler", "username": "Brukernavn", "verify_ssl": "Verifisere SSL-sertifikat" + }, + "data_description": { + "use_wallclock_as_timestamps": "Dette alternativet kan korrigere segmenterings- eller krasjproblemer som oppst\u00e5r fra buggy-tidsstempelimplementeringer p\u00e5 enkelte kameraer" } } } diff --git a/homeassistant/components/generic/translations/pl.json b/homeassistant/components/generic/translations/pl.json index a791a56c065..16a912587e5 100644 --- a/homeassistant/components/generic/translations/pl.json +++ b/homeassistant/components/generic/translations/pl.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protok\u00f3\u0142 transportowy RTSP", "still_image_url": "Adres URL obrazu nieruchomego (np. http://...)", "stream_source": "Adres URL strumienia (np. rtsp://...)", + "use_wallclock_as_timestamps": "U\u017cyj wallclock jako znacznika czasu", "username": "Nazwa u\u017cytkownika", "verify_ssl": "Weryfikacja certyfikatu SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Ta opcja mo\u017ce rozwi\u0105za\u0107 problemy z segmentacj\u0105 lub awariami wynikaj\u0105ce z wadliwych implementacji znacznik\u00f3w czasu w niekt\u00f3rych kamerach." } } } diff --git a/homeassistant/components/generic/translations/pt-BR.json b/homeassistant/components/generic/translations/pt-BR.json index 0e3ce2bd75d..ec093f3ff1a 100644 --- a/homeassistant/components/generic/translations/pt-BR.json +++ b/homeassistant/components/generic/translations/pt-BR.json @@ -79,8 +79,12 @@ "rtsp_transport": "Protocolo RTSP", "still_image_url": "URL da imagem est\u00e1tica (por exemplo, http://...)", "stream_source": "URL de origem da Stream (por exemplo, rtsp://...)", + "use_wallclock_as_timestamps": "Use o rel\u00f3gio de parede como data/hora", "username": "Usu\u00e1rio", "verify_ssl": "Verifique o certificado SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "Esta op\u00e7\u00e3o pode corrigir problemas de segmenta\u00e7\u00e3o ou travamento decorrentes de implementa\u00e7\u00f5es de data/hora com erros em algumas c\u00e2meras" } } } diff --git a/homeassistant/components/generic/translations/ru.json b/homeassistant/components/generic/translations/ru.json index 06f41613a50..e88f9f9cd1e 100644 --- a/homeassistant/components/generic/translations/ru.json +++ b/homeassistant/components/generic/translations/ru.json @@ -79,8 +79,12 @@ "rtsp_transport": "\u0422\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b RTSP", "still_image_url": "URL-\u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, http://...)", "stream_source": "URL-\u0430\u0434\u0440\u0435\u0441 \u043f\u043e\u0442\u043e\u043a\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, rtsp://...)", + "use_wallclock_as_timestamps": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u043a", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + }, + "data_description": { + "use_wallclock_as_timestamps": "\u042d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u0438\u043b\u0438 \u0441\u0431\u043e\u0435\u043c, \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u044e\u0449\u0438\u0435 \u0438\u0437-\u0437\u0430 \u043e\u0448\u0438\u0431\u043e\u0447\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u043a \u043d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043a\u0430\u043c\u0435\u0440\u0430\u0445." } } } diff --git a/homeassistant/components/generic/translations/zh-Hans.json b/homeassistant/components/generic/translations/zh-Hans.json new file mode 100644 index 00000000000..f13ba39c5b8 --- /dev/null +++ b/homeassistant/components/generic/translations/zh-Hans.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data_description": { + "use_wallclock_as_timestamps": "\u6b64\u9009\u9879\u53ef\u80fd\u4f1a\u7ea0\u6b63\u7531\u4e8e\u67d0\u4e9b\u6444\u50cf\u5934\u4e0a\u7684\u9519\u8bef\u65f6\u95f4\u6233\u5f15\u8d77\u7684\u5206\u6bb5\u6216\u5d29\u6e83\u95ee\u9898" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/zh-Hant.json b/homeassistant/components/generic/translations/zh-Hant.json index 1b72dcb0ce4..e37ee2d9235 100644 --- a/homeassistant/components/generic/translations/zh-Hant.json +++ b/homeassistant/components/generic/translations/zh-Hant.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP \u50b3\u8f38\u5354\u5b9a", "still_image_url": "\u975c\u614b\u5f71\u50cf URL\uff08\u4f8b\u5982 http://...\uff09", "stream_source": "\u4e32\u6d41\u4f86\u6e90 URL\uff08\u4f8b\u5982 rtsp://...\uff09", + "use_wallclock_as_timestamps": "\u4f7f\u7528\u639b\u9418\u4f5c\u70ba\u6642\u9593\u6233", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + }, + "data_description": { + "use_wallclock_as_timestamps": "\u6b64\u9078\u9805\u53ef\u80fd\u4fee\u6b63\u67d0\u4fee\u93e1\u982d\u4e0a\u932f\u8aa4\u7684\u6642\u9593\u6233\u5c0e\u81f4\u7684\u5206\u6bb5\u6216\u7576\u6a5f\u554f\u984c" } } } diff --git a/homeassistant/components/geocaching/translations/ja.json b/homeassistant/components/geocaching/translations/ja.json new file mode 100644 index 00000000000..41a238aa637 --- /dev/null +++ b/homeassistant/components/geocaching/translations/ja.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "authorize_url_timeout": "\u8a8d\u8a3cURL\u306e\u751f\u6210\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", + "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", + "no_url_available": "\u4f7f\u7528\u53ef\u80fd\u306aURL\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30a8\u30e9\u30fc\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001[\u30d8\u30eb\u30d7\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044]({docs_url})", + "oauth_error": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u30c7\u30fc\u30bf\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "create_entry": { + "default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f" + }, + "step": { + "pick_implementation": { + "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" + }, + "reauth_confirm": { + "description": "Geocaching\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/nl.json b/homeassistant/components/geocaching/translations/nl.json new file mode 100644 index 00000000000..82d40849146 --- /dev/null +++ b/homeassistant/components/geocaching/translations/nl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "already_in_progress": "De configuratiestroom is al aan de gang", + "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", + "oauth_error": "Ongeldige token data ontvangen.", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "create_entry": { + "default": "Succesvol geauthenticeerd" + }, + "step": { + "pick_implementation": { + "title": "Kies een authenticatie methode" + }, + "reauth_confirm": { + "description": "De Geocaching integratie moet uw account herauthenticeren", + "title": "Verifieer de integratie opnieuw" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/no.json b/homeassistant/components/geocaching/translations/no.json new file mode 100644 index 00000000000..f0b0b724861 --- /dev/null +++ b/homeassistant/components/geocaching/translations/no.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "authorize_url_timeout": "Tidsavbrudd ved oppretting av godkjenningsadresse", + "missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen", + "no_url_available": "Ingen URL tilgjengelig. For informasjon om denne feilen, [sjekk hjelpseksjonen]({docs_url})", + "oauth_error": "Mottatt ugyldige token data.", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "create_entry": { + "default": "Vellykket godkjenning" + }, + "step": { + "pick_implementation": { + "title": "Velg godkjenningsmetode" + }, + "reauth_confirm": { + "description": "Geocaching-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt", + "title": "Godkjenne integrering p\u00e5 nytt" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/pl.json b/homeassistant/components/geocaching/translations/pl.json new file mode 100644 index 00000000000..f38f7e137f6 --- /dev/null +++ b/homeassistant/components/geocaching/translations/pl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "authorize_url_timeout": "Przekroczono limit czasu generowania URL autoryzacji", + "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", + "no_url_available": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})", + "oauth_error": "Otrzymano nieprawid\u0142owe dane tokena.", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, + "step": { + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" + }, + "reauth_confirm": { + "description": "Integracja Geocaching wymaga ponownego uwierzytelnienia Twojego konta", + "title": "Ponownie uwierzytelnij integracj\u0119" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/ru.json b/homeassistant/components/geocaching/translations/ru.json new file mode 100644 index 00000000000..96bc2b576a1 --- /dev/null +++ b/homeassistant/components/geocaching/translations/ru.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", + "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", + "no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435.", + "oauth_error": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "step": { + "pick_implementation": { + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + }, + "reauth_confirm": { + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Geocaching.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/zh-Hans.json b/homeassistant/components/geocaching/translations/zh-Hans.json new file mode 100644 index 00000000000..21e284119ed --- /dev/null +++ b/homeassistant/components/geocaching/translations/zh-Hans.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "no_url_available": "\u6ca1\u6709\u53ef\u7528\u7684\u7f51\u5740\u3002\u6709\u5173\u6b64\u9519\u8bef\u7684\u4fe1\u606f\uff0c\u8bf7[\u68c0\u67e5\u5e2e\u52a9\u90e8\u5206]\uff08{docs_url}\uff09", + "oauth_error": "\u6536\u5230\u7684 token \u6570\u636e\u65e0\u6548\u3002", + "reauth_successful": "\u91cd\u65b0\u8ba4\u8bc1\u6210\u529f" + }, + "create_entry": { + "default": "\u8ba4\u8bc1\u6210\u529f" + }, + "step": { + "pick_implementation": { + "title": "\u8bf7\u9009\u62e9\u8ba4\u8bc1\u65b9\u6cd5" + }, + "reauth_confirm": { + "description": "Geocaching \u96c6\u6210\u9700\u8981\u91cd\u65b0\u9a8c\u8bc1\u60a8\u7684\u5e10\u6237", + "title": "\u4f7f\u96c6\u6210\u91cd\u65b0\u8fdb\u884c\u8eab\u4efd\u8ba4\u8bc1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/ja.json b/homeassistant/components/google/translations/ja.json index 7eab5abc6f6..81f2d941a73 100644 --- a/homeassistant/components/google/translations/ja.json +++ b/homeassistant/components/google/translations/ja.json @@ -23,7 +23,7 @@ "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" }, "reauth_confirm": { - "description": "Nest\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "description": "Nest\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" } } diff --git a/homeassistant/components/isy994/translations/ja.json b/homeassistant/components/isy994/translations/ja.json index 0245baf01a0..f1a447901b9 100644 --- a/homeassistant/components/isy994/translations/ja.json +++ b/homeassistant/components/isy994/translations/ja.json @@ -17,6 +17,7 @@ "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, + "description": "{host} \u306e\u8cc7\u683c\u60c5\u5831\u304c\u7121\u52b9\u306b\u306a\u308a\u307e\u3057\u305f\u3002", "title": "ISY\u306e\u518d\u8a8d\u8a3c" }, "user": { diff --git a/homeassistant/components/litterrobot/translations/sensor.ca.json b/homeassistant/components/litterrobot/translations/sensor.ca.json new file mode 100644 index 00000000000..72a6906f03a --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.ca.json @@ -0,0 +1,9 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "OFF", + "offline": "Fora de l\u00ednia", + "p": "Pausat/ada" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.de.json b/homeassistant/components/litterrobot/translations/sensor.de.json new file mode 100644 index 00000000000..2901b0e5c55 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.de.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Haube entfernt", + "ccc": "Reinigungszyklus abgeschlossen", + "ccp": "Reinigungszyklus l\u00e4uft", + "csf": "Katzensensor Fehler", + "csi": "Katzensensor unterbrochen", + "cst": "Katzensensor Timing", + "df1": "Schublade fast voll - 2 Zyklen \u00fcbrig", + "df2": "Schublade fast voll - 1 Zyklus \u00fcbrig", + "dfs": "Schublade voll", + "dhf": "Fehler in Leerungs- und Grundstellung", + "dpf": "Fehler in der Leerungsstellung", + "ec": "Leerungszyklus", + "hpf": "Fehler in der Grundstellung", + "off": "Aus", + "offline": "Offline", + "otf": "\u00dcberdrehungsfehler", + "p": "Pausiert", + "pd": "Einklemmen erkennen", + "rdy": "Bereit", + "scf": "Katzen-Sensorfehler beim Start", + "sdf": "Schublade voll beim Start", + "spf": "Einklemmerkennung beim Start" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.el.json b/homeassistant/components/litterrobot/translations/sensor.el.json new file mode 100644 index 00000000000..f34d6078495 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.el.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "\u03a4\u03bf \u03ba\u03b1\u03c0\u03cc \u03b1\u03c6\u03b1\u03b9\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5", + "ccc": "\u039f\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5 \u03bf \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2 \u03ba\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd", + "ccp": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2 \u03ba\u03b1\u03b8\u03b1\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "csf": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "csi": "\u0394\u03b9\u03b1\u03ba\u03bf\u03c0\u03ae \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "cst": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2", + "df1": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03c3\u03c7\u03b5\u03b4\u03cc\u03bd \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf - \u0391\u03c0\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5\u03bd 2 \u03ba\u03cd\u03ba\u03bb\u03bf\u03b9", + "df2": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03c3\u03c7\u03b5\u03b4\u03cc\u03bd \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf - \u0391\u03c0\u03bf\u03bc\u03ad\u03bd\u03b5\u03b9 1 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2", + "dfs": "\u03a3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03b3\u03b5\u03bc\u03ac\u03c4\u03bf", + "dhf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03b1\u03c1\u03c7\u03b9\u03ba\u03ae\u03c2 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2", + "dpf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2", + "ec": "\u0386\u03b4\u03b5\u03b9\u03bf\u03c2 \u03ba\u03cd\u03ba\u03bb\u03bf\u03c2", + "hpf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03c4\u03b7\u03c2 \u03b1\u03c1\u03c7\u03b9\u03ba\u03ae\u03c2 \u03b8\u03ad\u03c3\u03b7\u03c2", + "off": "\u0391\u03bd\u03b5\u03bd\u03b5\u03c1\u03b3\u03cc", + "offline": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "otf": "\u0392\u03bb\u03ac\u03b2\u03b7 \u03c5\u03c0\u03b5\u03c1\u03b2\u03bf\u03bb\u03b9\u03ba\u03ae\u03c2 \u03c1\u03bf\u03c0\u03ae\u03c2", + "p": "\u03a3\u03b5 \u03c0\u03b1\u03cd\u03c3\u03b7", + "pd": "\u0391\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7 \u03c4\u03c3\u03b9\u03bc\u03c0\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", + "rdy": "\u0388\u03c4\u03bf\u03b9\u03bc\u03bf", + "scf": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b3\u03ac\u03c4\u03b1\u03c2 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7", + "sdf": "\u0393\u03b5\u03bc\u03ac\u03c4\u03bf \u03c3\u03c5\u03c1\u03c4\u03ac\u03c1\u03b9 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7", + "spf": "\u0391\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7 \u03c4\u03c3\u03b9\u03bc\u03c0\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.et.json b/homeassistant/components/litterrobot/translations/sensor.et.json new file mode 100644 index 00000000000..98b04dfd207 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.et.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Kaast eemaldatud", + "ccc": "Puhastusts\u00fckkel on l\u00f5ppenud", + "ccp": "Puhastusts\u00fckkel on pooleli", + "csf": "Kassianduri viga", + "csi": "Kassianduri h\u00e4iring", + "cst": "Kassianduri ajastus", + "df1": "Sahtli peaaegu t\u00e4is - 2 ts\u00fcklit j\u00e4\u00e4nud", + "df2": "Sahtli peaaegu t\u00e4is - 1 ts\u00fckkel j\u00e4\u00e4nud", + "dfs": "Sahtli t\u00e4is", + "dhf": "Pr\u00fcgikasti + koduasendi t\u00f5rge", + "dpf": "T\u00fchjenduspunkti viga", + "ec": "T\u00fchjendusts\u00fckkel", + "hpf": "Koduasendi viga", + "off": "V\u00e4ljas", + "offline": "\u00dchenduseta", + "otf": "\u00dclekoornuse viga", + "p": "Ootel", + "pd": "Pinch Detect", + "rdy": "Valmis", + "scf": "Kassianduri viga k\u00e4ivitamisel", + "sdf": "Sahtel t\u00e4is k\u00e4ivitamisel", + "spf": "Pinch Detect k\u00e4ivitamisel" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.fr.json b/homeassistant/components/litterrobot/translations/sensor.fr.json new file mode 100644 index 00000000000..fba0796e2b0 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.fr.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Capot retir\u00e9", + "ccc": "Cycle de nettoyage termin\u00e9", + "ccp": "Cycle de nettoyage en cours", + "csf": "D\u00e9faut du capteur de chat", + "csi": "Interruption du capteur de chat", + "cst": "Minutage du capteur de chat", + "df1": "Tiroir presque plein \u2013\u00a02\u00a0cycles restants", + "df2": "Tiroir presque plein \u2013\u00a01\u00a0cycle restant", + "dfs": "Tiroir plein", + "dhf": "D\u00e9faut de position de vidage +\u00a0origine", + "dpf": "D\u00e9faut de position de vidage", + "ec": "Cycle de vidage", + "hpf": "D\u00e9faut de position d'origine", + "off": "D\u00e9sactiv\u00e9", + "offline": "Hors ligne", + "otf": "D\u00e9faut de surcouple", + "p": "En pause", + "pd": "D\u00e9tection de pincement", + "rdy": "Pr\u00eat", + "scf": "D\u00e9faut du capteur de chat au d\u00e9marrage", + "sdf": "Tiroir plein au d\u00e9marrage", + "spf": "D\u00e9tection de pincement au d\u00e9marrage" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.hu.json b/homeassistant/components/litterrobot/translations/sensor.hu.json new file mode 100644 index 00000000000..6c68a231bf4 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.hu.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Tet\u0151 elt\u00e1vol\u00edtva", + "ccc": "Tiszt\u00edt\u00e1s befejez\u0151d\u00f6tt", + "ccp": "Tiszt\u00edt\u00e1s folyamatban", + "csf": "Macska\u00e9rz\u00e9kel\u0151 hiba", + "csi": "Macska\u00e9rz\u00e9kel\u0151 megszak\u00edtva", + "cst": "Macska\u00e9rz\u00e9kel\u0151 id\u0151z\u00edt\u00e9se", + "df1": "A fi\u00f3k majdnem megtelt \u2013 2 ciklus van h\u00e1tra", + "df2": "A fi\u00f3k majdnem megtelt \u2013 1 ciklus maradt", + "dfs": "Fi\u00f3k tele", + "dhf": "Dump + Home poz\u00edci\u00f3 hiba", + "dpf": "Dump poz\u00edci\u00f3 hiba", + "ec": "\u00dcres ciklus", + "hpf": "Home poz\u00edci\u00f3 hiba", + "off": "Ki", + "offline": "Offline", + "otf": "T\u00falzott nyomat\u00e9k hiba", + "p": "Sz\u00fcnetel", + "pd": "Pinch Detect", + "rdy": "K\u00e9sz", + "scf": "Macska\u00e9rz\u00e9kel\u0151 hiba ind\u00edt\u00e1skor", + "sdf": "Ind\u00edt\u00e1skor megtelt a fi\u00f3k", + "spf": "Pinch Detect ind\u00edt\u00e1skor" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.ja.json b/homeassistant/components/litterrobot/translations/sensor.ja.json new file mode 100644 index 00000000000..4191e47ff4a --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.ja.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "\u30dc\u30f3\u30cd\u30c3\u30c8\u304c\u53d6\u308a\u5916\u3055\u308c\u307e\u3057\u305f", + "ccc": "\u30af\u30ea\u30fc\u30f3\u30b5\u30a4\u30af\u30eb\u5b8c\u4e86", + "ccp": "\u30af\u30ea\u30fc\u30f3\u30b5\u30a4\u30af\u30eb\u304c\u9032\u884c\u4e2d", + "csf": "Cat\u30bb\u30f3\u30b5\u30fc\u4e0d\u826f", + "csi": "Cat\u30bb\u30f3\u30b5\u30fc\u304c\u4e2d\u65ad\u3055\u308c\u307e\u3057\u305f", + "cst": "Cat\u30bb\u30f3\u30b5\u30fc\u30bf\u30a4\u30df\u30f3\u30b0", + "df1": "\u30c9\u30ed\u30ef\u30fc\u307b\u307c\u6e80\u676f - \u6b8b\u308a2\u30b5\u30a4\u30af\u30eb", + "df2": "\u30c9\u30ed\u30ef\u30fc\u307b\u307c\u6e80\u676f - \u6b8b\u308a1\u30b5\u30a4\u30af\u30eb", + "dfs": "\u30c9\u30ed\u30ef\u30fc\u30d5\u30eb", + "dhf": "\u30c0\u30f3\u30d7+\u30db\u30fc\u30e0\u30dd\u30b8\u30b7\u30e7\u30f3\u4e0d\u826f", + "dpf": "\u30c0\u30f3\u30d7\u30dd\u30b8\u30b7\u30e7\u30f3\u4e0d\u826f", + "ec": "\u7a7a\u306e\u30b5\u30a4\u30af\u30eb", + "hpf": "\u30db\u30fc\u30e0\u30dd\u30b8\u30b7\u30e7\u30f3\u4e0d\u826f", + "off": "\u30aa\u30d5", + "offline": "\u30aa\u30d5\u30e9\u30a4\u30f3", + "otf": "\u30aa\u30fc\u30d0\u30fc\u30c8\u30eb\u30af\u4e0d\u826f", + "p": "\u4e00\u6642\u505c\u6b62", + "pd": "\u30d4\u30f3\u30c1\u691c\u51fa", + "rdy": "\u6e96\u5099", + "scf": "\u8d77\u52d5\u6642\u306bCat\u30bb\u30f3\u30b5\u30fc\u4e0d\u826f", + "sdf": "\u8d77\u52d5\u6642\u306b\u30c9\u30ed\u30ef\u30fc\u6e80\u676f", + "spf": "\u8d77\u52d5\u6642\u306b\u30d4\u30f3\u30c1\u691c\u51fa" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.nl.json b/homeassistant/components/litterrobot/translations/sensor.nl.json new file mode 100644 index 00000000000..6a383627aee --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.nl.json @@ -0,0 +1,25 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Motorkap verwijderd", + "ccc": "Reinigingscyclus voltooid", + "ccp": "Reinigingscyclus in uitvoering", + "csf": "Kattensensor fout", + "csi": "Kattensensor onderbroken", + "cst": "Timing kattensensor", + "df1": "Lade bijna vol - nog 2 cycli over", + "df2": "Lade bijna vol - nog 1 cyclus over", + "dfs": "Lade vol", + "dhf": "Dump + Home positie fout", + "dpf": "Dump Positie Fout", + "ec": "Lege cyclus", + "hpf": "Home Positie Fout", + "off": "Uit", + "offline": "Offline", + "p": "Gepauzeerd", + "rdy": "Gereed", + "scf": "Kattensensorfout bij opstarten", + "sdf": "Lade vol bij opstarten" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.pt-BR.json b/homeassistant/components/litterrobot/translations/sensor.pt-BR.json new file mode 100644 index 00000000000..9eb1dd8be8b --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.pt-BR.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Cap\u00f4 removido", + "ccc": "Ciclo de limpeza conclu\u00eddo", + "ccp": "Ciclo de limpeza em andamento", + "csf": "Falha do Sensor Cat", + "csi": "Sensor Cat interrompido", + "cst": "Sincroniza\u00e7\u00e3o do Sensor Cat", + "df1": "Gaveta quase cheia - 2 ciclos restantes", + "df2": "Gaveta quase cheia - 1 ciclo \u00e0 esquerda", + "dfs": "Gaveta cheia", + "dhf": "Despejo + Falha de Posi\u00e7\u00e3o Inicial", + "dpf": "Falha de posi\u00e7\u00e3o de despejo", + "ec": "Ciclo vazio", + "hpf": "Falha de posi\u00e7\u00e3o inicial", + "off": "Desligado", + "offline": "Offline", + "otf": "Falha de sobretorque", + "p": "Pausado", + "pd": "Detec\u00e7\u00e3o de pin\u00e7a", + "rdy": "Pronto", + "scf": "Falha do sensor Cat na inicializa\u00e7\u00e3o", + "sdf": "Gaveta cheia na inicializa\u00e7\u00e3o", + "spf": "Detec\u00e7\u00e3o de pin\u00e7a na inicializa\u00e7\u00e3o" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.ru.json b/homeassistant/components/litterrobot/translations/sensor.ru.json new file mode 100644 index 00000000000..3a28cfe08ec --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.ru.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "\u041a\u043e\u0436\u0443\u0445 \u0441\u043d\u044f\u0442", + "ccc": "\u0426\u0438\u043a\u043b \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d", + "ccp": "\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0446\u0438\u043a\u043b \u043e\u0447\u0438\u0441\u0442\u043a\u0438", + "csf": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0430", + "csi": "\u0414\u0430\u0442\u0447\u0438\u043a \u043f\u0435\u0440\u0435\u043a\u0440\u044b\u0442", + "cst": "\u0412\u0440\u0435\u043c\u044f \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u0442\u0447\u0438\u043a\u0430", + "df1": "\u042f\u0449\u0438\u043a \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043e\u043d \u2014 \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c 2 \u0446\u0438\u043a\u043b\u0430", + "df2": "\u042f\u0449\u0438\u043a \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043e\u043d \u2014 \u043e\u0441\u0442\u0430\u043b\u0441\u044f 1 \u0446\u0438\u043a\u043b", + "dfs": "\u042f\u0449\u0438\u043a \u043f\u043e\u043b\u043d\u044b\u0439", + "dhf": "\u0421\u0431\u0440\u043e\u0441 + \u043e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f", + "dpf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0431\u0440\u043e\u0441\u0430", + "ec": "\u041f\u0443\u0441\u0442\u043e\u0439 \u0446\u0438\u043a\u043b", + "hpf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f", + "off": "\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e", + "offline": "\u041d\u0435 \u0432 \u0441\u0435\u0442\u0438", + "otf": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u043a\u0440\u0443\u0442\u044f\u0449\u0435\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430", + "p": "\u041f\u0430\u0443\u0437\u0430", + "pd": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0435\u043c\u043b\u0435\u043d\u0438\u044f", + "rdy": "\u0413\u043e\u0442\u043e\u0432", + "scf": "\u041d\u0435\u0438\u0441\u043f\u0440\u0430\u0432\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435", + "sdf": "\u042f\u0449\u0438\u043a \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435", + "spf": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0435\u043c\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.zh-Hans.json b/homeassistant/components/litterrobot/translations/sensor.zh-Hans.json new file mode 100644 index 00000000000..400c2b81dcb --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.zh-Hans.json @@ -0,0 +1,10 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "\u5173\u95ed", + "offline": "\u79bb\u7ebf", + "p": "\u5df2\u6682\u505c", + "rdy": "\u5c31\u7eea" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.zh-Hant.json b/homeassistant/components/litterrobot/translations/sensor.zh-Hant.json new file mode 100644 index 00000000000..f51c03f9c8c --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.zh-Hant.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "\u4e0a\u84cb\u906d\u958b\u555f", + "ccc": "\u8c93\u7802\u6e05\u7406\u5b8c\u6210", + "ccp": "\u8c93\u7802\u6e05\u7406\u4e2d", + "csf": "\u8c93\u54aa\u611f\u6e2c\u5668\u6545\u969c", + "csi": "\u8c93\u54aa\u611f\u6e2c\u5668\u906d\u4e2d\u65b7", + "cst": "\u8c93\u54aa\u611f\u6e2c\u5668\u8a08\u6642", + "df1": "\u6392\u5ee2\u76d2\u5feb\u6eff\u4e86 - \u5269\u4e0b 2 \u6b21\u6e05\u7406", + "df2": "\u6392\u5ee2\u76d2\u5feb\u6eff\u4e86 - \u5269\u4e0b 1 \u6b21\u6e05\u7406", + "dfs": "\u6392\u5ee2\u76d2\u5df2\u6eff", + "dhf": "\u50be\u5012\u8207\u539f\u9ede\u4f4d\u7f6e\u6545\u969c", + "dpf": "\u50be\u5012\u4f4d\u7f6e\u6545\u969c", + "ec": "\u6574\u5e73", + "hpf": "\u539f\u9ede\u5b9a\u4f4d\u6545\u969c", + "off": "\u95dc\u9589", + "offline": "\u96e2\u7dda", + "otf": "\u8f49\u52d5\u5931\u6557", + "p": "\u5df2\u66ab\u505c", + "pd": "\u7570\u7269\u5075\u6e2c", + "rdy": "\u6e96\u5099\u5c31\u7dd2", + "scf": "\u555f\u52d5\u6642\u8c93\u54aa\u611f\u6e2c\u5668\u5931\u6548", + "sdf": "\u555f\u52d5\u6642\u6392\u5ee2\u76d2\u5df2\u6eff", + "spf": "\u555f\u52d5\u6642\u5075\u6e2c\u5230\u7570\u7269" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ja.json b/homeassistant/components/meater/translations/ja.json index e24fc9199cc..72912036ddd 100644 --- a/homeassistant/components/meater/translations/ja.json +++ b/homeassistant/components/meater/translations/ja.json @@ -9,13 +9,17 @@ "reauth_confirm": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - } + }, + "description": "Meater Cloud\u30a2\u30ab\u30a6\u30f3\u30c8 {username} \u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "user": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, + "data_description": { + "username": "Meater Cloud\u306e\u30e6\u30fc\u30b6\u30fc\u540d\u3001\u901a\u5e38\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3067\u3059\u3002" + }, "description": "Meater Cloud\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" } } diff --git a/homeassistant/components/min_max/translations/zh-Hans.json b/homeassistant/components/min_max/translations/zh-Hans.json index ca5555bbd0a..9fb15dafd2a 100644 --- a/homeassistant/components/min_max/translations/zh-Hans.json +++ b/homeassistant/components/min_max/translations/zh-Hans.json @@ -11,7 +11,7 @@ "data_description": { "round_digits": "\u5f53\u7edf\u8ba1\u9879\u4e3a\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u65f6\uff0c\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" }, - "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u3001\u6216\u4e2d\u4f4d\u6570\u3002", + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u3002", "title": "\u6dfb\u52a0\u6700\u5927\u503c/\u6700\u5c0f\u503c/\u5e73\u5747\u503c/\u4e2d\u4f4d\u6570\u4f20\u611f\u5668" } } @@ -34,7 +34,7 @@ "round_digits": "\u7cbe\u5ea6", "type": "\u7edf\u8ba1\u9879" }, - "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u3001\u6216\u4e2d\u4f4d\u6570\u3002" + "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u3002" } } }, diff --git a/homeassistant/components/motion_blinds/translations/ca.json b/homeassistant/components/motion_blinds/translations/ca.json index 18cc8f892d6..4ec912aa25c 100644 --- a/homeassistant/components/motion_blinds/translations/ca.json +++ b/homeassistant/components/motion_blinds/translations/ca.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat, la configuraci\u00f3 de connexi\u00f3 s'ha actualitzat", + "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "connection_error": "Ha fallat la connexi\u00f3" }, diff --git a/homeassistant/components/motion_blinds/translations/de.json b/homeassistant/components/motion_blinds/translations/de.json index c5ced3ddd55..d3b6b219516 100644 --- a/homeassistant/components/motion_blinds/translations/de.json +++ b/homeassistant/components/motion_blinds/translations/de.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert, Verbindungseinstellungen werden aktualisiert", + "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "connection_error": "Verbindung fehlgeschlagen" }, diff --git a/homeassistant/components/motion_blinds/translations/et.json b/homeassistant/components/motion_blinds/translations/et.json index 935e345ea3a..715208cd9f4 100644 --- a/homeassistant/components/motion_blinds/translations/et.json +++ b/homeassistant/components/motion_blinds/translations/et.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud, \u00fchenduse s\u00e4tted on v\u00e4rskendatud", + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", "connection_error": "\u00dchendamine nurjus" }, diff --git a/homeassistant/components/motion_blinds/translations/fr.json b/homeassistant/components/motion_blinds/translations/fr.json index 09cf93fde60..17df2654026 100644 --- a/homeassistant/components/motion_blinds/translations/fr.json +++ b/homeassistant/components/motion_blinds/translations/fr.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9, les param\u00e8tres de connexion ont \u00e9t\u00e9 mis \u00e0 jour", + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "connection_error": "\u00c9chec de connexion" }, diff --git a/homeassistant/components/motion_blinds/translations/id.json b/homeassistant/components/motion_blinds/translations/id.json index d576ced8c0a..42b9b8d127d 100644 --- a/homeassistant/components/motion_blinds/translations/id.json +++ b/homeassistant/components/motion_blinds/translations/id.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi, pengaturan koneksi diperbarui", + "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", "connection_error": "Gagal terhubung" }, diff --git a/homeassistant/components/motion_blinds/translations/it.json b/homeassistant/components/motion_blinds/translations/it.json index 0871ce07bf8..ddb29c2c042 100644 --- a/homeassistant/components/motion_blinds/translations/it.json +++ b/homeassistant/components/motion_blinds/translations/it.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato, le impostazioni di connessione sono aggiornate", + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "connection_error": "Impossibile connettersi" }, diff --git a/homeassistant/components/motion_blinds/translations/nl.json b/homeassistant/components/motion_blinds/translations/nl.json index 6cb69b06b87..c837e656c56 100644 --- a/homeassistant/components/motion_blinds/translations/nl.json +++ b/homeassistant/components/motion_blinds/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd, verbindingsinstellingen zijn bijgewerkt", + "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", "connection_error": "Kan geen verbinding maken" }, diff --git a/homeassistant/components/motion_blinds/translations/pt-BR.json b/homeassistant/components/motion_blinds/translations/pt-BR.json index 01658cea852..afbd117d260 100644 --- a/homeassistant/components/motion_blinds/translations/pt-BR.json +++ b/homeassistant/components/motion_blinds/translations/pt-BR.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado, as configura\u00e7\u00f5es de conex\u00e3o s\u00e3o atualizadas", + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "connection_error": "Falha ao conectar" }, diff --git a/homeassistant/components/motion_blinds/translations/ru.json b/homeassistant/components/motion_blinds/translations/ru.json index e7a4492bd54..0ab743f8895 100644 --- a/homeassistant/components/motion_blinds/translations/ru.json +++ b/homeassistant/components/motion_blinds/translations/ru.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b.", + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "connection_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, diff --git a/homeassistant/components/motion_blinds/translations/zh-Hant.json b/homeassistant/components/motion_blinds/translations/zh-Hant.json index e7fa565ba36..ccfe6e782ed 100644 --- a/homeassistant/components/motion_blinds/translations/zh-Hant.json +++ b/homeassistant/components/motion_blinds/translations/zh-Hant.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u9023\u7dda\u8a2d\u5b9a\u5df2\u66f4\u65b0", + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "connection_error": "\u9023\u7dda\u5931\u6557" }, diff --git a/homeassistant/components/nest/translations/ja.json b/homeassistant/components/nest/translations/ja.json index 752e847336f..c2994de0532 100644 --- a/homeassistant/components/nest/translations/ja.json +++ b/homeassistant/components/nest/translations/ja.json @@ -54,7 +54,7 @@ "title": "Google Cloud\u306e\u8a2d\u5b9a" }, "reauth_confirm": { - "description": "Nest\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "description": "Nest\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" } } diff --git a/homeassistant/components/netatmo/translations/ja.json b/homeassistant/components/netatmo/translations/ja.json index ecb21ffe039..b9f2f17960a 100644 --- a/homeassistant/components/netatmo/translations/ja.json +++ b/homeassistant/components/netatmo/translations/ja.json @@ -15,7 +15,7 @@ "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" }, "reauth_confirm": { - "description": "Netatmo\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "description": "Netatmo\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" } } diff --git a/homeassistant/components/onewire/translations/es.json b/homeassistant/components/onewire/translations/es.json index d789e46875f..9879ccb0a00 100644 --- a/homeassistant/components/onewire/translations/es.json +++ b/homeassistant/components/onewire/translations/es.json @@ -17,6 +17,7 @@ }, "user": { "data": { + "port": "Puerto", "type": "Tipo de conexi\u00f3n" }, "title": "Configurar 1 cable" diff --git a/homeassistant/components/simplisafe/translations/ja.json b/homeassistant/components/simplisafe/translations/ja.json index 2d3f53b0710..67e1630469e 100644 --- a/homeassistant/components/simplisafe/translations/ja.json +++ b/homeassistant/components/simplisafe/translations/ja.json @@ -2,15 +2,20 @@ "config": { "abort": { "already_configured": "\u3053\u306eSimpliSafe account\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002", + "email_2fa_timed_out": "\u96fb\u5b50\u30e1\u30fc\u30eb\u306b\u3088\u308b2\u8981\u7d20\u8a8d\u8a3c\u306e\u5f85\u6a5f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", "wrong_account": "\u63d0\u4f9b\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u60c5\u5831\u304c\u3001\u3053\u306eSimpliSafe\u30a2\u30ab\u30a6\u30f3\u30c8\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002" }, "error": { + "2fa_timed_out": "\u4e8c\u8981\u7d20\u8a8d\u8a3c\u306e\u5f85\u6a5f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f", "identifier_exists": "\u30a2\u30ab\u30a6\u30f3\u30c8\u767b\u9332\u6e08\u307f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "still_awaiting_mfa": "MFA email click\u3092\u307e\u3060\u5f85\u3063\u3066\u3044\u307e\u3059", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, + "progress": { + "email_2fa": "Simplisafe\u304b\u3089\u306e\u78ba\u8a8d\u30ea\u30f3\u30af\u304c\u306a\u3044\u304b\u30e1\u30fc\u30eb\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, "step": { "mfa": { "title": "SimpliSafe\u591a\u8981\u7d20\u8a8d\u8a3c" @@ -25,7 +30,8 @@ "sms_2fa": { "data": { "code": "\u30b3\u30fc\u30c9" - } + }, + "description": "SMS\u3067\u9001\u3089\u308c\u3066\u304d\u305f2\u8981\u7d20\u8a8d\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u307e\u3059\u3002" }, "user": { "data": { diff --git a/homeassistant/components/slack/translations/ca.json b/homeassistant/components/slack/translations/ca.json new file mode 100644 index 00000000000..58ff126ae27 --- /dev/null +++ b/homeassistant/components/slack/translations/ca.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "El servei ja est\u00e0 configurat" + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "user": { + "data": { + "api_key": "Clau API", + "default_channel": "Canal predeterminat", + "icon": "Icona", + "username": "Nom d'usuari" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/de.json b/homeassistant/components/slack/translations/de.json new file mode 100644 index 00000000000..d87383ffac7 --- /dev/null +++ b/homeassistant/components/slack/translations/de.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Der Dienst ist bereits konfiguriert" + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "default_channel": "Standardkanal", + "icon": "Symbol", + "username": "Benutzername" + }, + "data_description": { + "api_key": "Der Slack-API-Token, das zum Senden von Slack-Nachrichten verwendet werden soll.", + "default_channel": "Der Kanal, an den gepostet werden soll, wenn beim Senden einer Nachricht kein Kanal angegeben wird.", + "icon": "Verwende eines der Slack-Emojis als Symbol f\u00fcr den bereitgestellten Benutzernamen.", + "username": "Home Assistant postet unter Verwendung des angegebenen Benutzernamens in Slack." + }, + "description": "Lies in der Dokumentation nach, wie du deinen Slack-API-Schl\u00fcssel erh\u00e4ltst." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/el.json b/homeassistant/components/slack/translations/el.json new file mode 100644 index 00000000000..8628dde5821 --- /dev/null +++ b/homeassistant/components/slack/translations/el.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af" + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "default_channel": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03ba\u03b1\u03bd\u03ac\u03bb\u03b9", + "icon": "\u0395\u03b9\u03ba\u03bf\u03bd\u03af\u03b4\u03b9\u03bf", + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + }, + "data_description": { + "api_key": "\u03a4\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc Slack API \u03b3\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03bc\u03b7\u03bd\u03c5\u03bc\u03ac\u03c4\u03c9\u03bd Slack.", + "default_channel": "\u03a4\u03bf \u03ba\u03b1\u03bd\u03ac\u03bb\u03b9 \u03c3\u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03b8\u03b1 \u03b4\u03b7\u03bc\u03bf\u03c3\u03b9\u03b5\u03cd\u03c3\u03b5\u03c4\u03b5 \u03b1\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03ba\u03b1\u03bd\u03ac\u03bb\u03b9 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03c0\u03bf\u03c3\u03c4\u03bf\u03bb\u03ae \u03b5\u03bd\u03cc\u03c2 \u03bc\u03b7\u03bd\u03cd\u03bc\u03b1\u03c4\u03bf\u03c2.", + "icon": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 Slack emoji \u03c9\u03c2 \u03b5\u03b9\u03ba\u03bf\u03bd\u03af\u03b4\u03b9\u03bf \u03b3\u03b9\u03b1 \u03c4\u03bf \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7.", + "username": "\u03a4\u03bf Home Assistant \u03b8\u03b1 \u03b4\u03b7\u03bc\u03bf\u03c3\u03b9\u03b5\u03cd\u03b5\u03b9 \u03c3\u03c4\u03bf Slack \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03bf \u03ba\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7." + }, + "description": "\u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03bb\u03ae\u03c8\u03b7 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd Slack API." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/en.json b/homeassistant/components/slack/translations/en.json index 5a97fb1938e..23e5830764e 100644 --- a/homeassistant/components/slack/translations/en.json +++ b/homeassistant/components/slack/translations/en.json @@ -16,13 +16,13 @@ "icon": "Icon", "username": "Username" }, - "description": "Refer to the documentation on getting your Slack API key.", "data_description": { "api_key": "The Slack API token to use for sending Slack messages.", "default_channel": "The channel to post to if no channel is specified when sending a message.", "icon": "Use one of the Slack emojis as an Icon for the supplied username.", "username": "Home Assistant will post to Slack using the username specified." - } + }, + "description": "Refer to the documentation on getting your Slack API key." } } } diff --git a/homeassistant/components/slack/translations/et.json b/homeassistant/components/slack/translations/et.json new file mode 100644 index 00000000000..bc76c6d848a --- /dev/null +++ b/homeassistant/components/slack/translations/et.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Teenus on juba seadistatud" + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "user": { + "data": { + "api_key": "API v\u00f5ti", + "default_channel": "Vaikimisi kanal", + "icon": "Ikoon", + "username": "Kasutajanimi" + }, + "data_description": { + "api_key": "Slacki API v\u00f5ti mida kasutatakse Slacki s\u00f5numite saatmiseks.", + "default_channel": "Kanal, kuhu postitada kui s\u00f5numi saatmisel ei ole kanalit m\u00e4\u00e4ratud.", + "icon": "Kasuta \u00fchte Slacki emotikoni esitatud kasutajanime ikoonina.", + "username": "Home Assistant postitab Slacki kasutades m\u00e4\u00e4ratud kasutajanime." + }, + "description": "Vaata oma Slack API v\u00f5tme hankimise dokumentatsiooni." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/fr.json b/homeassistant/components/slack/translations/fr.json new file mode 100644 index 00000000000..0573e8c81c2 --- /dev/null +++ b/homeassistant/components/slack/translations/fr.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "default_channel": "Canal par d\u00e9faut", + "icon": "Ic\u00f4ne", + "username": "Nom d'utilisateur" + }, + "data_description": { + "api_key": "Le jeton d'API Slack \u00e0 utiliser pour l'envoi des messages sur Slack.", + "default_channel": "Le canal sur lequel poster si aucun canal n'est sp\u00e9cifi\u00e9 lors de l'envoi d'un message.", + "icon": "Utilisez l'un des \u00e9mojis de Slack en tant qu'ic\u00f4ne pour le nom d'utilisateur fourni.", + "username": "Home Assistant postera sur Slack en utilisant le nom d'utilisateur sp\u00e9cifi\u00e9." + }, + "description": "Consultez la documentation pour obtenir votre cl\u00e9 d'API Slack." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/hu.json b/homeassistant/components/slack/translations/hu.json new file mode 100644 index 00000000000..c2737dd6e01 --- /dev/null +++ b/homeassistant/components/slack/translations/hu.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "user": { + "data": { + "api_key": "API kulcs", + "default_channel": "Alap\u00e9rtelmezett csatorna", + "icon": "Ikon", + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + }, + "data_description": { + "api_key": "A Slack API token a Slack \u00fczenetek k\u00fcld\u00e9s\u00e9re.", + "default_channel": "Az a csatorna, amelyre k\u00f6zz\u00e9tehet, ha nincs megadva csatorna az \u00fczenet k\u00fcld\u00e9sekor.", + "icon": "Haszn\u00e1lja a Slack hangulatjelek egyik\u00e9t ikonk\u00e9nt a megadott felhaszn\u00e1l\u00f3n\u00e9vhez.", + "username": "A Home Assistant a megadott felhaszn\u00e1l\u00f3n\u00e9vvel fog \u00fczenetet k\u00fcldeni a Slackre." + }, + "description": "L\u00e1sd a Slack API-kulcs megszerz\u00e9s\u00e9nek dokument\u00e1ci\u00f3j\u00e1t." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/ja.json b/homeassistant/components/slack/translations/ja.json new file mode 100644 index 00000000000..c6a7f478db6 --- /dev/null +++ b/homeassistant/components/slack/translations/ja.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "default_channel": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30c1\u30e3\u30f3\u30cd\u30eb", + "icon": "\u30a2\u30a4\u30b3\u30f3", + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + }, + "data_description": { + "api_key": "Slack\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u9001\u4fe1\u306b\u4f7f\u7528\u3059\u308b\u3001Slack API\u30c8\u30fc\u30af\u30f3\u3002", + "default_channel": "\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u9001\u4fe1\u6642\u306b\u3001\u30c1\u30e3\u30f3\u30cd\u30eb\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u304b\u3063\u305f\u5834\u5408\u306b\u6295\u7a3f\u3059\u308b\u30c1\u30e3\u30f3\u30cd\u30eb\u3002", + "icon": "Slack\u306e\u7d75\u6587\u5b57\u3092\u3001\u6307\u5b9a\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u306e\u30a2\u30a4\u30b3\u30f3\u3068\u3057\u3066\u4f7f\u7528\u3057\u307e\u3059\u3002", + "username": "Home Assistant\u306f\u3001\u6307\u5b9a\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u3092\u4f7f\u7528\u3057\u3066Slack\u306b\u6295\u7a3f\u3057\u307e\u3059\u3002" + }, + "description": "Slack API\u30ad\u30fc\u306e\u53d6\u5f97\u306b\u3064\u3044\u3066\u306f\u3001\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/nl.json b/homeassistant/components/slack/translations/nl.json new file mode 100644 index 00000000000..8022048798e --- /dev/null +++ b/homeassistant/components/slack/translations/nl.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Service is al geconfigureerd" + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "user": { + "data": { + "api_key": "API-sleutel", + "default_channel": "Standaard kanaal", + "icon": "Icoon", + "username": "Gebruikersnaam" + }, + "data_description": { + "api_key": "Het Slack API-token dat moet worden gebruikt voor het verzenden van Slack-berichten.", + "default_channel": "Het kanaal waarnaar moet worden gepost als er geen kanaal is opgegeven bij het verzenden van een bericht.", + "icon": "Gebruik een van de Slack emoji's als pictogram voor de opgegeven gebruikersnaam.", + "username": "Home Assistant plaatst berichten op Slack met de opgegeven gebruikersnaam." + }, + "description": "Raadpleeg de documentatie over het verkrijgen van uw Slack API-sleutel." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/pt-BR.json b/homeassistant/components/slack/translations/pt-BR.json new file mode 100644 index 00000000000..a6299848bfc --- /dev/null +++ b/homeassistant/components/slack/translations/pt-BR.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Falhou ao conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "user": { + "data": { + "api_key": "Chave de API", + "default_channel": "Canal padr\u00e3o", + "icon": "\u00cdcone", + "username": "Nome de usu\u00e1rio" + }, + "data_description": { + "api_key": "O token da API do Slack a ser usado para enviar mensagens do Slack.", + "default_channel": "O canal para postar se nenhum canal for especificado ao enviar uma mensagem.", + "icon": "Use um dos emojis do Slack como \u00edcone para o nome de usu\u00e1rio fornecido.", + "username": "O Home Assistant postar\u00e1 no Slack usando o nome de usu\u00e1rio especificado." + }, + "description": "Consulte a documenta\u00e7\u00e3o sobre como obter sua chave de API do Slack." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/ru.json b/homeassistant/components/slack/translations/ru.json new file mode 100644 index 00000000000..dd25d00e367 --- /dev/null +++ b/homeassistant/components/slack/translations/ru.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "default_channel": "\u041a\u0430\u043d\u0430\u043b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", + "icon": "\u0418\u043a\u043e\u043d\u043a\u0430", + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + }, + "data_description": { + "api_key": "\u0422\u043e\u043a\u0435\u043d API Slack, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439.", + "default_channel": "\u041a\u0430\u043d\u0430\u043b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043a\u0430\u043d\u0430\u043b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d.", + "icon": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u043c\u0430\u0439\u043b\u0438\u043a\u043e\u0432 Slack \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u043d\u0430\u0447\u043a\u0430 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.", + "username": "Home Assistant \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 Slack, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f." + }, + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u043a\u043b\u044e\u0447\u0430 API Slack." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/zh-Hans.json b/homeassistant/components/slack/translations/zh-Hans.json new file mode 100644 index 00000000000..0f9177e8a5c --- /dev/null +++ b/homeassistant/components/slack/translations/zh-Hans.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52a1\u5df2\u914d\u7f6e" + }, + "error": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "invalid_auth": "\u8eab\u4efd\u9a8c\u8bc1\u65e0\u6548", + "unknown": "\u610f\u5916\u7684\u9519\u8bef" + }, + "step": { + "user": { + "data": { + "api_key": "API \u5bc6\u94a5", + "default_channel": "\u9ed8\u8ba4\u901a\u9053", + "icon": "\u56fe\u6807", + "username": "\u7528\u6237\u540d" + }, + "data_description": { + "api_key": "\u7528\u4e8e\u53d1\u9001 Slack \u6d88\u606f\u7684 Slack API token\u3002", + "default_channel": "\u5728\u53d1\u9001\u6d88\u606f\u672a\u6307\u5b9a\u901a\u9053\u65f6\u8981\u53d1\u5e03\u5230\u7684\u901a\u9053\u3002", + "icon": "\u4f7f\u7528\u5176\u4e2d\u4e00\u4e2a Slack \u8868\u60c5\u7b26\u53f7\u4f5c\u4e3a\u63d0\u4f9b\u7684\u7528\u6237\u540d\u7684\u56fe\u6807\u3002", + "username": "Home Assistant \u5c06\u4f7f\u7528\u6307\u5b9a\u7684\u7528\u6237\u540d\u53d1\u5e03\u5230 Slack\u3002" + }, + "description": "\u8bf7\u53c2\u9605\u6709\u5173\u83b7\u53d6 Slack API \u5bc6\u94a5\u7684\u6587\u6863\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/zh-Hant.json b/homeassistant/components/slack/translations/zh-Hant.json new file mode 100644 index 00000000000..083f5a9ffeb --- /dev/null +++ b/homeassistant/components/slack/translations/zh-Hant.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "default_channel": "\u9810\u8a2d\u983b\u9053", + "icon": "\u5716\u793a", + "username": "\u4f7f\u7528\u8005\u540d\u7a31" + }, + "data_description": { + "api_key": "\u4f7f\u7528\u65bc\u50b3\u9001 Slack \u8a0a\u606f\u7684 Slack API \u6b0a\u6756\u3002", + "default_channel": "\u767c\u9001\u8a0a\u606f\u6642\u3001\u82e5\u7121\u6307\u5b9a\u983b\u9053\u6642\uff0c\u6240\u8981\u5f35\u8cbc\u7684\u983b\u9053\u3002", + "icon": "\u4f7f\u7528 Slack \u8868\u60c5\u7b26\u865f\u4f5c\u70ba\u63d0\u4f9b\u4f7f\u7528\u8005\u540d\u7a31\u4e4b\u5716\u793a\u3002", + "username": "Home Assistant \u5c07\u6703\u4f7f\u7528\u6307\u5b9a\u4f7f\u7528\u8005\u540d\u7a31\u5f35\u8cbc\u81f3 Slack\u3002" + }, + "description": "\u8acb\u53c3\u8003\u6587\u4ef6\u4ee5\u53d6\u5f97 API \u91d1\u9470\u3002" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/smarttub/translations/ja.json b/homeassistant/components/smarttub/translations/ja.json index dfe2ef1ab26..44d753fa22e 100644 --- a/homeassistant/components/smarttub/translations/ja.json +++ b/homeassistant/components/smarttub/translations/ja.json @@ -9,7 +9,7 @@ }, "step": { "reauth_confirm": { - "description": "SmartTub\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "description": "SmartTub\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" }, "user": { diff --git a/homeassistant/components/steam_online/translations/ja.json b/homeassistant/components/steam_online/translations/ja.json index 5e3b98684e8..935c975c801 100644 --- a/homeassistant/components/steam_online/translations/ja.json +++ b/homeassistant/components/steam_online/translations/ja.json @@ -12,12 +12,23 @@ }, "step": { "reauth_confirm": { + "description": "Steam\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306f\u624b\u52d5\u3067\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\n\n\u3053\u3053\u3067\u3042\u306a\u305f\u306e\u30ad\u30fc\u3092\u898b\u3064\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059: {api_key_url}", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" }, "user": { "data": { "account": "Steam\u30a2\u30ab\u30a6\u30f3\u30c8ID", "api_key": "API\u30ad\u30fc" + }, + "description": "{account_id_url} \u3092\u4f7f\u7528\u3057\u3066\u3001\u3042\u306a\u305f\u306eSteam\u306e\u30a2\u30ab\u30a6\u30f3\u30c8ID\u3092\u898b\u3064\u3051\u307e\u3059" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\u76e3\u8996\u5bfe\u8c61\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u540d" } } } diff --git a/homeassistant/components/tautulli/translations/ja.json b/homeassistant/components/tautulli/translations/ja.json index f6bec08752a..6f733e1cad4 100644 --- a/homeassistant/components/tautulli/translations/ja.json +++ b/homeassistant/components/tautulli/translations/ja.json @@ -14,6 +14,7 @@ "data": { "api_key": "API\u30ad\u30fc" }, + "description": "API\u30ad\u30fc\u3092\u898b\u3064\u3051\u308b\u306b\u306f\u3001Tautulli Web\u30da\u30fc\u30b8\u3092\u958b\u304d\u3001Settings\u306b\u79fb\u52d5\u3057\u3066\u304b\u3089\u3001Web interface\u306b\u79fb\u52d5\u3057\u307e\u3059\u3002API\u30ad\u30fc\u306f\u3001\u305d\u306e\u30da\u30fc\u30b8\u306e\u4e00\u756a\u4e0b\u306b\u3042\u308a\u307e\u3059\u3002", "title": "Tautulli\u3092\u518d\u8a8d\u8a3c\u3057\u307e\u3059" }, "user": { @@ -21,7 +22,8 @@ "api_key": "API\u30ad\u30fc", "url": "URL", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" - } + }, + "description": "API\u30ad\u30fc\u3092\u898b\u3064\u3051\u308b\u306b\u306f\u3001Tautulli Web\u30da\u30fc\u30b8\u3092\u958b\u304d\u3001Settings\u306b\u79fb\u52d5\u3057\u3066\u304b\u3089\u3001Web interface\u306b\u79fb\u52d5\u3057\u307e\u3059\u3002API\u30ad\u30fc\u306f\u3001\u305d\u306e\u30da\u30fc\u30b8\u306e\u4e00\u756a\u4e0b\u306b\u3042\u308a\u307e\u3059\u3002\n\nURL\u306e\u4f8b: ```http://192.168.0.10:8181``` \u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30dd\u30fc\u30c8\u306f\u30018181 \u3067\u3059\u3002" } } } diff --git a/homeassistant/components/totalconnect/translations/ja.json b/homeassistant/components/totalconnect/translations/ja.json index d6bffce6dae..7eefbc3a873 100644 --- a/homeassistant/components/totalconnect/translations/ja.json +++ b/homeassistant/components/totalconnect/translations/ja.json @@ -19,7 +19,7 @@ "title": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u30e6\u30fc\u30b6\u30fc\u30b3\u30fc\u30c9" }, "reauth_confirm": { - "description": "Total Connect\u306f\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "description": "Total Connect\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" }, "user": { diff --git a/homeassistant/components/trafikverket_ferry/translations/ja.json b/homeassistant/components/trafikverket_ferry/translations/ja.json index 8081c3a9198..251117bc773 100644 --- a/homeassistant/components/trafikverket_ferry/translations/ja.json +++ b/homeassistant/components/trafikverket_ferry/translations/ja.json @@ -7,7 +7,8 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "incorrect_api_key": "\u9078\u629e\u3057\u305f\u30a2\u30ab\u30a6\u30f3\u30c8\u306eAPI\u30ad\u30fc\u304c\u7121\u52b9\u3067\u3059", - "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "invalid_route": "\u63d0\u4f9b\u3055\u308c\u305f\u60c5\u5831\u3067\u30eb\u30fc\u30c8\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/ukraine_alarm/translations/ca.json b/homeassistant/components/ukraine_alarm/translations/ca.json index cd5cda1cc8b..c650423723d 100644 --- a/homeassistant/components/ukraine_alarm/translations/ca.json +++ b/homeassistant/components/ukraine_alarm/translations/ca.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada" + "already_configured": "La ubicaci\u00f3 ja est\u00e0 configurada", + "cannot_connect": "Ha fallat la connexi\u00f3", + "max_regions": "Es poden configurar un m\u00e0xim de 5 regions", + "rate_limit": "Massa peticions", + "timeout": "Temps m\u00e0xim d'espera per establir la connexi\u00f3 esgotat", + "unknown": "Error inesperat" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Clau API" + "api_key": "Clau API", + "region": "Regi\u00f3" }, - "description": "Configura la integraci\u00f3 d'Alarma Ucra\u00efna. Per generar la clau API, v\u00e9s a {api_url}" + "description": "Escull l'estat a monitorar" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/de.json b/homeassistant/components/ukraine_alarm/translations/de.json index 75dc7a7856a..2eb99ee692f 100644 --- a/homeassistant/components/ukraine_alarm/translations/de.json +++ b/homeassistant/components/ukraine_alarm/translations/de.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Standort ist bereits konfiguriert" + "already_configured": "Standort ist bereits konfiguriert", + "cannot_connect": "Verbindung fehlgeschlagen", + "max_regions": "Es k\u00f6nnen maximal 5 Regionen konfiguriert werden", + "rate_limit": "Zu viele Anfragen", + "timeout": "Zeit\u00fcberschreitung beim Verbindungsaufbau", + "unknown": "Unerwarteter Fehler" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "API-Schl\u00fcssel" + "api_key": "API-Schl\u00fcssel", + "region": "Region" }, - "description": "Richte die Ukraine Alarm-Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe zu {api_url}" + "description": "Zu \u00fcberwachendes Bundesland ausw\u00e4hlen" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/el.json b/homeassistant/components/ukraine_alarm/translations/el.json index 57e40095d37..61b40412415 100644 --- a/homeassistant/components/ukraine_alarm/translations/el.json +++ b/homeassistant/components/ukraine_alarm/translations/el.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + "already_configured": "\u0397 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "max_regions": "\u039c\u03c0\u03bf\u03c1\u03bf\u03cd\u03bd \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03bf\u03cd\u03bd \u03ad\u03c9\u03c2 5 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ad\u03c2", + "rate_limit": "\u03a0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ac \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1", + "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", @@ -30,7 +35,8 @@ }, "user": { "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae\u03c2" }, "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd \u03c4\u03b7\u03c2 \u039f\u03c5\u03ba\u03c1\u03b1\u03bd\u03af\u03b1\u03c2. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 {api_url}" } diff --git a/homeassistant/components/ukraine_alarm/translations/es.json b/homeassistant/components/ukraine_alarm/translations/es.json new file mode 100644 index 00000000000..7fa891bde3e --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/es.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "region": "Regi\u00f3n" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/et.json b/homeassistant/components/ukraine_alarm/translations/et.json index 45649336837..452b9d8cd11 100644 --- a/homeassistant/components/ukraine_alarm/translations/et.json +++ b/homeassistant/components/ukraine_alarm/translations/et.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Asukoht on juba m\u00e4\u00e4ratud" + "already_configured": "Asukoht on juba m\u00e4\u00e4ratud", + "cannot_connect": "\u00dchendamine nurjus", + "max_regions": "Seadistada saab kuni 5 piirkonda", + "rate_limit": "Liiga palju taotlusi", + "timeout": "\u00dchenduse ajal\u00f5pp", + "unknown": "Ootamatu t\u00f5rge" }, "error": { "cannot_connect": "\u00dchendamine nurjus", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "API v\u00f5ti" + "api_key": "API v\u00f5ti", + "region": "Piirkond" }, - "description": "Loo Ukraina h\u00e4ire integreerimine. API-v\u00f5tme loomiseks ava {api_url}" + "description": "Vali j\u00e4lgitav oblast" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/fr.json b/homeassistant/components/ukraine_alarm/translations/fr.json index 51706dfee5f..6cafde0e4d0 100644 --- a/homeassistant/components/ukraine_alarm/translations/fr.json +++ b/homeassistant/components/ukraine_alarm/translations/fr.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9" + "already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9", + "cannot_connect": "\u00c9chec de connexion", + "max_regions": "Un maximum de cinq r\u00e9gions peuvent \u00eatre configur\u00e9es", + "rate_limit": "Trop de demandes", + "timeout": "D\u00e9lai d'attente pour \u00e9tablir la connexion expir\u00e9", + "unknown": "Erreur inattendue" }, "error": { "cannot_connect": "\u00c9chec de connexion", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Cl\u00e9 d'API" + "api_key": "Cl\u00e9 d'API", + "region": "R\u00e9gion" }, - "description": "Configurez l'int\u00e9gration Ukraine Alarm. Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur {api_url}" + "description": "Choisissez l'\u00c9tat \u00e0 surveiller" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/hu.json b/homeassistant/components/ukraine_alarm/translations/hu.json index fb00a32a507..e72d6140c01 100644 --- a/homeassistant/components/ukraine_alarm/translations/hu.json +++ b/homeassistant/components/ukraine_alarm/translations/hu.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "A hely m\u00e1r konfigur\u00e1lva van" + "already_configured": "A hely m\u00e1r konfigur\u00e1lva van", + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "max_regions": "Legfeljebb 5 r\u00e9gi\u00f3 konfigur\u00e1lhat\u00f3", + "rate_limit": "T\u00fal sok k\u00e9r\u00e9s", + "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a kapcsolat l\u00e9trehoz\u00e1sa sor\u00e1n", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", @@ -30,7 +35,8 @@ }, "user": { "data": { - "api_key": "API kulcs" + "api_key": "API kulcs", + "region": "R\u00e9gi\u00f3" }, "description": "\u00c1ll\u00edtsa be az Ukraine Alarm integr\u00e1ci\u00f3t. API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}" } diff --git a/homeassistant/components/ukraine_alarm/translations/id.json b/homeassistant/components/ukraine_alarm/translations/id.json index 6c10857bc80..bd453d78e06 100644 --- a/homeassistant/components/ukraine_alarm/translations/id.json +++ b/homeassistant/components/ukraine_alarm/translations/id.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Lokasi sudah dikonfigurasi" + "already_configured": "Lokasi sudah dikonfigurasi", + "cannot_connect": "Gagal terhubung", + "max_regions": "Maksimal 5 wilayah dapat dikonfigurasi", + "rate_limit": "Terlalu banyak permintaan", + "timeout": "Tenggang waktu membuat koneksi habis", + "unknown": "Kesalahan yang tidak diharapkan" }, "error": { "cannot_connect": "Gagal terhubung", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Kunci API" + "api_key": "Kunci API", + "region": "Wilayah" }, - "description": "Siapkan integrasi Alarm Ukraina. Untuk membuat kunci API, buka {api_url}" + "description": "Pilih negara bagian untuk dipantau" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/it.json b/homeassistant/components/ukraine_alarm/translations/it.json index 23d0f2a12a5..4994cf45d33 100644 --- a/homeassistant/components/ukraine_alarm/translations/it.json +++ b/homeassistant/components/ukraine_alarm/translations/it.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "La posizione \u00e8 gi\u00e0 configurata" + "already_configured": "La posizione \u00e8 gi\u00e0 configurata", + "cannot_connect": "Impossibile connettersi", + "max_regions": "\u00c8 possibile configurare al massimo 5 regioni", + "rate_limit": "Troppe richieste", + "timeout": "Tempo scaduto per stabile la connessione.", + "unknown": "Errore imprevisto" }, "error": { "cannot_connect": "Impossibile connettersi", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Chiave API" + "api_key": "Chiave API", + "region": "Regione" }, - "description": "Imposta l'integrazione di Ukraine Alarm. Per generare una chiave API vai su {api_url}" + "description": "Scegli lo stato da monitorare" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/ja.json b/homeassistant/components/ukraine_alarm/translations/ja.json index 303ac7df051..b879cf4541e 100644 --- a/homeassistant/components/ukraine_alarm/translations/ja.json +++ b/homeassistant/components/ukraine_alarm/translations/ja.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + "already_configured": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "max_regions": "\u6700\u59275\u3064\u306e\u30ea\u30fc\u30b8\u30e7\u30f3\u3092\u8a2d\u5b9a\u53ef\u80fd", + "rate_limit": "\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u591a\u3059\u304e\u307e\u3059", + "timeout": "\u63a5\u7d9a\u78ba\u7acb\u6642\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", @@ -30,7 +35,8 @@ }, "user": { "data": { - "api_key": "API\u30ad\u30fc" + "api_key": "API\u30ad\u30fc", + "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" }, "description": "\u30a6\u30af\u30e9\u30a4\u30ca\u306e\u8b66\u5831\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001 {api_url} \u306b\u79fb\u52d5\u3057\u3066\u304f\u3060\u3055\u3044" } diff --git a/homeassistant/components/ukraine_alarm/translations/nl.json b/homeassistant/components/ukraine_alarm/translations/nl.json index 6ec93b0526d..ab9973a0618 100644 --- a/homeassistant/components/ukraine_alarm/translations/nl.json +++ b/homeassistant/components/ukraine_alarm/translations/nl.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd.", + "cannot_connect": "Kan geen verbinding maken", + "max_regions": "Er kunnen maximaal 5 regio's worden geconfigureerd", + "rate_limit": "Te veel verzoeken", + "timeout": "Time-out bij het maken van verbinding", + "unknown": "Onverwachte fout" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "API-sleutel" + "api_key": "API-sleutel", + "region": "Regio" }, - "description": "Stel de Oekra\u00efne Alarm integratie in. Om een API sleutel te genereren ga naar {api_url}" + "description": "Kies staat om te monitoren" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/no.json b/homeassistant/components/ukraine_alarm/translations/no.json index b7ee3275920..10a717c72ec 100644 --- a/homeassistant/components/ukraine_alarm/translations/no.json +++ b/homeassistant/components/ukraine_alarm/translations/no.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Plasseringen er allerede konfigurert" + "already_configured": "Plasseringen er allerede konfigurert", + "cannot_connect": "Tilkobling mislyktes", + "max_regions": "Maks 5 regioner kan konfigureres", + "rate_limit": "For mange foresp\u00f8rsler", + "timeout": "Tidsavbrudd oppretter forbindelse", + "unknown": "Uventet feil" }, "error": { "cannot_connect": "Tilkobling mislyktes", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "API-n\u00f8kkel" + "api_key": "API-n\u00f8kkel", + "region": "Region" }, - "description": "Sett opp Ukraina Alarm-integrasjonen. For \u00e5 generere en API-n\u00f8kkel, g\u00e5 til {api_url}" + "description": "Velg tilstand \u00e5 overv\u00e5ke" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/pl.json b/homeassistant/components/ukraine_alarm/translations/pl.json index 9ed67385f75..6b970b759ed 100644 --- a/homeassistant/components/ukraine_alarm/translations/pl.json +++ b/homeassistant/components/ukraine_alarm/translations/pl.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "Lokalizacja jest ju\u017c skonfigurowana" + "already_configured": "Lokalizacja jest ju\u017c skonfigurowana", + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "max_regions": "Mo\u017cna skonfigurowa\u0107 maksymalnie 5 region\u00f3w", + "rate_limit": "Zbyt wiele \u017c\u0105da\u0144", + "timeout": "Limit czasu na nawi\u0105zanie po\u0142\u0105czenia", + "unknown": "Nieoczekiwany b\u0142\u0105d" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Klucz API" + "api_key": "Klucz API", + "region": "Region" }, - "description": "Skonfiguruj integracj\u0119 Alarmy w Ukrainie. Aby wygenerowa\u0107 klucz API, przejd\u017a do {api_url}." + "description": "Wybierz rejon do monitorowania" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/pt-BR.json b/homeassistant/components/ukraine_alarm/translations/pt-BR.json index 92c5eb20b6d..316a8e96563 100644 --- a/homeassistant/components/ukraine_alarm/translations/pt-BR.json +++ b/homeassistant/components/ukraine_alarm/translations/pt-BR.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "O local j\u00e1 est\u00e1 configurado" + "already_configured": "O local j\u00e1 est\u00e1 configurado", + "cannot_connect": "Falhou ao conectar", + "max_regions": "M\u00e1ximo de 5 regi\u00f5es podem ser configuradas", + "rate_limit": "Excesso de pedidos", + "timeout": "Tempo limite estabelecendo conex\u00e3o", + "unknown": "Erro inesperado" }, "error": { "cannot_connect": "Falhou ao conectar", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "Chave de API" + "api_key": "Chave de API", + "region": "Regi\u00e3o" }, - "description": "Configure a integra\u00e7\u00e3o do Alarme da Ucr\u00e2nia. Para gerar uma chave de API, acesse {api_url}" + "description": "Escolha o estado para monitorar" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/ru.json b/homeassistant/components/ukraine_alarm/translations/ru.json index cd852ad0745..fdb9d4b3f23 100644 --- a/homeassistant/components/ukraine_alarm/translations/ru.json +++ b/homeassistant/components/ukraine_alarm/translations/ru.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430." + "already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0440\u0435\u0433\u0438\u043e\u043d\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.", + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "max_regions": "\u041c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 5 \u0440\u0435\u0433\u0438\u043e\u043d\u043e\u0432.", + "rate_limit": "\u0421\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.", + "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", @@ -12,27 +17,28 @@ "step": { "community": { "data": { - "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + "region": "\u0420\u0430\u0439\u043e\u043d" }, - "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0448\u0442\u0430\u0442\u043e\u043c \u0438 \u043e\u043a\u0440\u0443\u0433\u043e\u043c, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e." + "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e \u0438 \u0433\u0440\u043e\u043c\u0430\u0434\u043e\u0439, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0451 \u0440\u0430\u0439\u043e\u043d" }, "district": { "data": { - "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + "region": "\u0413\u0440\u043e\u043c\u0430\u0434\u0430" }, - "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u0448\u0442\u0430\u0442\u043e\u043c, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0440\u0430\u0439\u043e\u043d." + "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0451 \u0433\u0440\u043e\u043c\u0430\u0434\u0443" }, "state": { "data": { - "region": "\u0420\u0435\u0433\u0438\u043e\u043d" + "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u0442\u0430\u0442 \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430." + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API" + "api_key": "\u041a\u043b\u044e\u0447 API", + "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" }, - "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 {api_url}" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/uk.json b/homeassistant/components/ukraine_alarm/translations/uk.json new file mode 100644 index 00000000000..2d5e881e41b --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/uk.json @@ -0,0 +1,45 @@ +{ + "config": { + "abort": { + "already_configured": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0434\u043b\u044f \u0446\u044c\u043e\u0433\u043e \u0440\u0435\u0433\u0456\u043e\u043d\u0443 \u0432\u0436\u0435 \u0432\u0438\u043a\u043e\u043d\u0430\u043d\u043e", + "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", + "max_regions": "\u041c\u043e\u0436\u043d\u0430 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 5 \u0440\u0435\u0433\u0456\u043e\u043d\u0456\u0432", + "rate_limit": "\u0417\u0430\u0431\u0430\u0433\u0430\u0442\u043e \u0437\u0430\u043f\u0438\u0442\u0456\u0432", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0437\u2019\u0454\u0434\u043d\u0430\u043d\u043d\u044f", + "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", + "invalid_api_key": "\u0425\u0438\u0431\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API", + "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0437\u2019\u0454\u0434\u043d\u0430\u043d\u043d\u044f", + "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" + }, + "step": { + "community": { + "data": { + "region": "\u0420\u0430\u0439\u043e\u043d" + }, + "description": "\u042f\u043a\u0449\u043e \u0432\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0443\u0432\u0430\u0442\u0438 \u043d\u0435 \u0442\u0456\u043b\u044c\u043a\u0438 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0442\u0430 \u0433\u0440\u043e\u043c\u0430\u0434\u0443, \u043e\u0431\u0435\u0440\u0456\u0442\u044c \u0457\u0457 \u0440\u0430\u0439\u043e\u043d" + }, + "district": { + "data": { + "region": "\u0413\u0440\u043e\u043c\u0430\u0434\u0430" + }, + "description": "\u042f\u043a\u0449\u043e \u0432\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0443\u0432\u0430\u0442\u0438 \u043d\u0435 \u0442\u0456\u043b\u044c\u043a\u0438 \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u043e\u0431\u0435\u0440\u0456\u0442\u044c \u0457\u0457 \u0433\u0440\u043e\u043c\u0430\u0434\u0443" + }, + "state": { + "data": { + "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" + }, + "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0435\u043d\u043d\u044f" + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" + }, + "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0435\u043d\u043d\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/zh-Hans.json b/homeassistant/components/ukraine_alarm/translations/zh-Hans.json new file mode 100644 index 00000000000..e4f7da73890 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/zh-Hans.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "cannot_connect": "\u8fde\u63a5\u5931\u8d25", + "max_regions": "\u6700\u591a\u53ef\u914d\u7f6e 5 \u4e2a\u533a\u57df", + "rate_limit": "\u8bf7\u6c42\u8fc7\u591a", + "timeout": "\u5efa\u7acb\u8fde\u63a5\u8d85\u65f6", + "unknown": "\u610f\u5916\u7684\u9519\u8bef" + }, + "step": { + "user": { + "data": { + "region": "\u5730\u533a" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/zh-Hant.json b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json index 0704a45dd56..33e75d23df0 100644 --- a/homeassistant/components/ukraine_alarm/translations/zh-Hant.json +++ b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json @@ -1,7 +1,12 @@ { "config": { "abort": { - "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + "already_configured": "\u5ea7\u6a19\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "max_regions": "\u6700\u9ad8\u53ef\u8a2d\u5b9a 5 \u500b\u5340\u57df", + "rate_limit": "\u8acb\u6c42\u6b21\u6578\u904e\u591a", + "timeout": "\u5efa\u7acb\u9023\u7dda\u903e\u6642", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", @@ -30,9 +35,10 @@ }, "user": { "data": { - "api_key": "API \u91d1\u9470" + "api_key": "API \u91d1\u9470", + "region": "\u5340\u57df" }, - "description": "\u8a2d\u5b9a\u70cf\u514b\u862d\u8b66\u5831\u6574\u5408\uff0c\u8acb\u9023\u7dda\u81f3 {api_url} \u4ee5\u53d6\u5f97 API \u91d1\u9470\u3002" + "description": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684\u72c0\u614b" } } } diff --git a/homeassistant/components/vulcan/translations/ja.json b/homeassistant/components/vulcan/translations/ja.json index 1f97968e9ae..2f83f44ab53 100644 --- a/homeassistant/components/vulcan/translations/ja.json +++ b/homeassistant/components/vulcan/translations/ja.json @@ -3,6 +3,7 @@ "abort": { "all_student_already_configured": "\u3059\u3079\u3066\u306e\u751f\u5f92\u306f\u3059\u3067\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002", "already_configured": "\u305d\u306e\u5b66\u751f\u306f\u3059\u3067\u306b\u8ffd\u52a0\u3055\u308c\u3066\u3044\u307e\u3059\u3002", + "no_matching_entries": "\u4e00\u81f4\u3059\u308b\u30a8\u30f3\u30c8\u30ea\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u5225\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u4f7f\u7528\u3059\u308b\u304b\u3001outdated student\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u524a\u9664\u3057\u3066\u304f\u3060\u3055\u3044..", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f" }, "error": { @@ -42,7 +43,8 @@ "pin": "\u30d4\u30f3", "region": "\u30b7\u30f3\u30dc\u30eb", "token": "\u30c8\u30fc\u30af\u30f3" - } + }, + "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" }, "select_saved_credentials": { "data": { From 3d2f4e31afb8fb739534400afb6d94b2d0f614a5 Mon Sep 17 00:00:00 2001 From: Austin Mroczek Date: Sat, 14 May 2022 19:06:05 -0700 Subject: [PATCH 460/930] Add diagnostics for TotalConnect (#71506) --- .../components/totalconnect/diagnostics.py | 113 ++++++++++++++++++ tests/components/totalconnect/common.py | 25 ++++ .../totalconnect/test_diagnostics.py | 32 +++++ 3 files changed, 170 insertions(+) create mode 100644 homeassistant/components/totalconnect/diagnostics.py create mode 100644 tests/components/totalconnect/test_diagnostics.py diff --git a/homeassistant/components/totalconnect/diagnostics.py b/homeassistant/components/totalconnect/diagnostics.py new file mode 100644 index 00000000000..4a9a73c89a9 --- /dev/null +++ b/homeassistant/components/totalconnect/diagnostics.py @@ -0,0 +1,113 @@ +"""Provides diagnostics for TotalConnect.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.diagnostics import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN + +TO_REDACT = [ + "username", + "Password", + "Usercode", + "UserID", + "Serial Number", + "serial_number", + "sensor_serial_number", +] + +# Private variable access needed for diagnostics +# pylint: disable=protected-access + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + client = hass.data[DOMAIN][config_entry.entry_id].client + + data: dict[str, Any] = {} + data["client"] = { + "auto_bypass_low_battery": client.auto_bypass_low_battery, + "module_flags": client._module_flags, + "retry_delay": client.retry_delay, + "invalid_credentials": client._invalid_credentials, + } + + data["user"] = { + "master": client._user._master_user, + "user_admin": client._user._user_admin, + "config_admin": client._user._config_admin, + "security_problem": client._user.security_problem(), + "features": client._user._features, + } + + data["locations"] = [] + for location in client.locations.values(): + new_location = { + "location_id": location.location_id, + "name": location.location_name, + "module_flags": location._module_flags, + "security_device_id": location.security_device_id, + "ac_loss": location.ac_loss, + "low_battery": location.low_battery, + "auto_bypass_low_battery": location.auto_bypass_low_battery, + "cover_tampered": location.cover_tampered, + "arming_state": location.arming_state, + } + + new_location["devices"] = [] + for device in location.devices.values(): + new_device = { + "device_id": device.deviceid, + "name": device.name, + "class_id": device.class_id, + "serial_number": device.serial_number, + "security_panel_type_id": device.security_panel_type_id, + "serial_text": device.serial_text, + "flags": device.flags, + } + new_location["devices"].append(new_device) + + new_location["partitions"] = [] + for partition in location.partitions.values(): + new_partition = { + "partition_id": partition.partitionid, + "name": partition.name, + "is_stay_armed": partition.is_stay_armed, + "is_fire_enabled": partition.is_fire_enabled, + "is_common_enabled": partition.is_common_enabled, + "is_locked": partition.is_locked, + "is_new_partition": partition.is_new_partition, + "is_night_stay_enabled": partition.is_night_stay_enabled, + "exit_delay_timer": partition.exit_delay_timer, + } + new_location["partitions"].append(new_partition) + + new_location["zones"] = [] + for zone in location.zones.values(): + new_zone = { + "zone_id": zone.zoneid, + "description": zone.description, + "partition": zone.partition, + "status": zone.status, + "zone_type_id": zone.zone_type_id, + "can_be_bypassed": zone.can_be_bypassed, + "battery_level": zone.battery_level, + "signal_strength": zone.signal_strength, + "sensor_serial_number": zone.sensor_serial_number, + "loop_number": zone.loop_number, + "response_type": zone.response_type, + "alarm_report_state": zone.alarm_report_state, + "supervision_type": zone.supervision_type, + "chime_state": zone.chime_state, + "device_type": zone.device_type, + } + new_location["zones"].append(new_zone) + + data["locations"].append(new_location) + + return async_redact_data(data, TO_REDACT) diff --git a/tests/components/totalconnect/common.py b/tests/components/totalconnect/common.py index ec0c182895f..65b10718fd5 100644 --- a/tests/components/totalconnect/common.py +++ b/tests/components/totalconnect/common.py @@ -345,3 +345,28 @@ async def setup_platform(hass, platform): await hass.async_block_till_done() return mock_entry + + +async def init_integration(hass): + """Set up the TotalConnect integration.""" + # first set up a config entry and add it to hass + mock_entry = MockConfigEntry(domain=DOMAIN, data=CONFIG_DATA) + mock_entry.add_to_hass(hass) + + responses = [ + RESPONSE_AUTHENTICATE, + RESPONSE_PARTITION_DETAILS, + RESPONSE_GET_ZONE_DETAILS_SUCCESS, + RESPONSE_DISARMED, + RESPONSE_DISARMED, + ] + + with patch( + TOTALCONNECT_REQUEST, + side_effect=responses, + ) as mock_request: + await hass.config_entries.async_setup(mock_entry.entry_id) + assert mock_request.call_count == 5 + await hass.async_block_till_done() + + return mock_entry diff --git a/tests/components/totalconnect/test_diagnostics.py b/tests/components/totalconnect/test_diagnostics.py new file mode 100644 index 00000000000..9c6b1975097 --- /dev/null +++ b/tests/components/totalconnect/test_diagnostics.py @@ -0,0 +1,32 @@ +"""Test TotalConnect diagnostics.""" + +from homeassistant.components.diagnostics import REDACTED + +from .common import LOCATION_ID, init_integration + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_entry_diagnostics(hass, hass_client): + """Test config entry diagnostics.""" + entry = await init_integration(hass) + + result = await get_diagnostics_for_config_entry(hass, hass_client, entry) + + client = result["client"] + assert client["invalid_credentials"] is False + + user = result["user"] + assert user["master"] is False + + location = result["locations"][0] + assert location["location_id"] == LOCATION_ID + + device = location["devices"][0] + assert device["serial_number"] == REDACTED + + partition = location["partitions"][0] + assert partition["name"] == "Test1" + + zone = location["zones"][0] + assert zone["zone_id"] == "1" From 8f3343750786017a2ecf9783b86af2ca601a4709 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 May 2022 22:04:31 -0500 Subject: [PATCH 461/930] Correct typo in internal logbook function names (#71882) --- homeassistant/components/logbook/queries.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index e8e691f2787..29dac31a432 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -160,7 +160,7 @@ def _entities_stmt( ) stmt = stmt.add_criteria( lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)).union_all( - _states_query_for_entitiy_ids(start_day, end_day, entity_ids), + _states_query_for_entity_ids(start_day, end_day, entity_ids), _select_events_context_only().where( Events.context_id.in_( _select_entities_context_ids_sub_query( @@ -218,7 +218,7 @@ def _single_entity_stmt( | EventData.shared_data.like(entity_id_like) ) .union_all( - _states_query_for_entitiy_id(start_day, end_day, entity_id), + _states_query_for_entity_id(start_day, end_day, entity_id), _select_events_context_only().where( Events.context_id.in_( _select_entity_context_ids_sub_query( @@ -304,13 +304,13 @@ def _states_query_for_context_id(start_day: dt, end_day: dt, context_id: str) -> ) -def _states_query_for_entitiy_id(start_day: dt, end_day: dt, entity_id: str) -> Query: +def _states_query_for_entity_id(start_day: dt, end_day: dt, entity_id: str) -> Query: return _apply_states_filters( _apply_entities_hints(_select_states()), start_day, end_day ).where(States.entity_id == entity_id) -def _states_query_for_entitiy_ids( +def _states_query_for_entity_ids( start_day: dt, end_day: dt, entity_ids: list[str] ) -> Query: return _apply_states_filters( From 6a6d31180b0107242657edbfac741f4ef5dd6db6 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sun, 15 May 2022 05:08:30 +0200 Subject: [PATCH 462/930] Motion blinds restore angle (#71790) * restore tilt for TiltDevices * add tilt option to set_absolute_position * improve set_absolute_position service * fix styling --- .../components/motion_blinds/cover.py | 18 ++++++++++++++++-- .../components/motion_blinds/manifest.json | 2 +- .../components/motion_blinds/services.yaml | 12 +++++++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index ccd4043faee..e09bb531208 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -71,6 +71,7 @@ TDBU_DEVICE_MAP = { SET_ABSOLUTE_POSITION_SCHEMA = { vol.Required(ATTR_ABSOLUTE_POSITION): vol.All(cv.positive_int, vol.Range(max=100)), + vol.Optional(ATTR_TILT_POSITION): vol.All(cv.positive_int, vol.Range(max=100)), vol.Optional(ATTR_WIDTH): vol.All(cv.positive_int, vol.Range(max=100)), } @@ -163,6 +164,8 @@ async def async_setup_entry( class MotionPositionDevice(CoordinatorEntity, CoverEntity): """Representation of a Motion Blind Device.""" + _restore_tilt = False + def __init__(self, coordinator, blind, device_class, sw_version): """Initialize the blind.""" super().__init__(coordinator) @@ -287,16 +290,25 @@ class MotionPositionDevice(CoordinatorEntity, CoverEntity): position = kwargs[ATTR_POSITION] async with self._api_lock: await self.hass.async_add_executor_job( - self._blind.Set_position, 100 - position + self._blind.Set_position, + 100 - position, + None, + self._restore_tilt, ) await self.async_request_position_till_stop() async def async_set_absolute_position(self, **kwargs): """Move the cover to a specific absolute position (see TDBU).""" position = kwargs[ATTR_ABSOLUTE_POSITION] + angle = kwargs.get(ATTR_TILT_POSITION) + if angle is not None: + angle = angle * 180 / 100 async with self._api_lock: await self.hass.async_add_executor_job( - self._blind.Set_position, 100 - position + self._blind.Set_position, + 100 - position, + angle, + self._restore_tilt, ) await self.async_request_position_till_stop() @@ -309,6 +321,8 @@ class MotionPositionDevice(CoordinatorEntity, CoverEntity): class MotionTiltDevice(MotionPositionDevice): """Representation of a Motion Blind Device.""" + _restore_tilt = True + @property def current_cover_tilt_position(self): """ diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 5ec39d8c638..1e8ad0eb0a1 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -3,7 +3,7 @@ "name": "Motion Blinds", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/motion_blinds", - "requirements": ["motionblinds==0.6.5"], + "requirements": ["motionblinds==0.6.7"], "dependencies": ["network"], "dhcp": [ { "registered_devices": true }, diff --git a/homeassistant/components/motion_blinds/services.yaml b/homeassistant/components/motion_blinds/services.yaml index 1ee60923332..d37d6bb5be8 100644 --- a/homeassistant/components/motion_blinds/services.yaml +++ b/homeassistant/components/motion_blinds/services.yaml @@ -14,8 +14,17 @@ set_absolute_position: required: true selector: number: - min: 1 + min: 0 max: 100 + unit_of_measurement: "%" + tilt_position: + name: Tilt position + description: Tilt position to move to. + selector: + number: + min: 0 + max: 100 + unit_of_measurement: "%" width: name: Width description: Specify the width that is covered, only for TDBU Combined entities. @@ -23,3 +32,4 @@ set_absolute_position: number: min: 1 max: 100 + unit_of_measurement: "%" diff --git a/requirements_all.txt b/requirements_all.txt index e1bde3fb6ca..457c2e9ec1d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1035,7 +1035,7 @@ mitemp_bt==0.0.5 moehlenhoff-alpha2==1.1.2 # homeassistant.components.motion_blinds -motionblinds==0.6.5 +motionblinds==0.6.7 # homeassistant.components.motioneye motioneye-client==0.3.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e2d7267149f..dbb54b45643 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -712,7 +712,7 @@ minio==5.0.10 moehlenhoff-alpha2==1.1.2 # homeassistant.components.motion_blinds -motionblinds==0.6.5 +motionblinds==0.6.7 # homeassistant.components.motioneye motioneye-client==0.3.12 From 65f44bd80b148c802b5ff21a911f13b77b0ab967 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 01:03:56 -0500 Subject: [PATCH 463/930] Exclude last_changed when same as last_updated for history websocket api (#71886) --- homeassistant/components/recorder/history.py | 8 ++--- homeassistant/components/recorder/models.py | 32 +++++++++----------- tests/components/history/test_init.py | 31 +++++++++++-------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index f434c1d5fe2..7def35ce3ac 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -19,7 +19,7 @@ from sqlalchemy.sql.expression import literal from homeassistant.components import recorder from homeassistant.components.websocket_api.const import ( - COMPRESSED_STATE_LAST_CHANGED, + COMPRESSED_STATE_LAST_UPDATED, COMPRESSED_STATE_STATE, ) from homeassistant.core import HomeAssistant, State, split_entity_id @@ -662,12 +662,12 @@ def _sorted_states_to_dict( _process_timestamp: Callable[ [datetime], float | str ] = process_datetime_to_timestamp - attr_last_changed = COMPRESSED_STATE_LAST_CHANGED + attr_time = COMPRESSED_STATE_LAST_UPDATED attr_state = COMPRESSED_STATE_STATE else: state_class = LazyState # type: ignore[assignment] _process_timestamp = process_timestamp_to_utc_isoformat - attr_last_changed = LAST_CHANGED_KEY + attr_time = LAST_CHANGED_KEY attr_state = STATE_KEY result: dict[str, list[State | dict[str, Any]]] = defaultdict(list) @@ -742,7 +742,7 @@ def _sorted_states_to_dict( # # We use last_updated for for last_changed since its the same # - attr_last_changed: _process_timestamp(row.last_updated), + attr_time: _process_timestamp(row.last_updated), } ) prev_state = state diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 4c3832c4fc0..d64d85f3ce4 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -798,23 +798,21 @@ def row_to_compressed_state( start_time: datetime | None = None, ) -> dict[str, Any]: """Convert a database row to a compressed state.""" - if start_time: - last_changed = last_updated = start_time.timestamp() - else: - row_last_updated: datetime = row.last_updated - if ( - not (row_changed_changed := row.last_changed) - or row_last_updated == row_changed_changed - ): - last_changed = last_updated = process_datetime_to_timestamp( - row_last_updated - ) - else: - last_changed = process_datetime_to_timestamp(row_changed_changed) - last_updated = process_datetime_to_timestamp(row_last_updated) - return { + comp_state = { COMPRESSED_STATE_STATE: row.state, COMPRESSED_STATE_ATTRIBUTES: decode_attributes_from_row(row, attr_cache), - COMPRESSED_STATE_LAST_CHANGED: last_changed, - COMPRESSED_STATE_LAST_UPDATED: last_updated, } + if start_time: + comp_state[COMPRESSED_STATE_LAST_UPDATED] = start_time.timestamp() + else: + row_last_updated: datetime = row.last_updated + comp_state[COMPRESSED_STATE_LAST_UPDATED] = process_datetime_to_timestamp( + row_last_updated + ) + if ( + row_changed_changed := row.last_changed + ) and row_last_updated != row_changed_changed: + comp_state[COMPRESSED_STATE_LAST_CHANGED] = process_datetime_to_timestamp( + row_changed_changed + ) + return comp_state diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index bcbab1e21ca..23e0550d6aa 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -1133,11 +1133,12 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert "a" not in sensor_test_history[1] assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[2]["s"] == "on" assert sensor_test_history[2]["a"] == {} @@ -1163,10 +1164,11 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"any": "attr"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"any": "attr"} assert sensor_test_history[4]["s"] == "on" @@ -1193,10 +1195,11 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"any": "attr"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"any": "attr"} assert sensor_test_history[2]["s"] == "on" @@ -1331,11 +1334,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert "a" in sensor_test_history[1] assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[4]["s"] == "on" assert sensor_test_history[4]["a"] == {} @@ -1361,10 +1364,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "1"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"temperature": "2"} assert sensor_test_history[4]["s"] == "on" @@ -1391,10 +1395,11 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "1"} assert isinstance(sensor_test_history[0]["lu"], float) - assert isinstance(sensor_test_history[0]["lc"], float) + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["s"] == "off" - assert isinstance(sensor_test_history[1]["lc"], float) + assert isinstance(sensor_test_history[1]["lu"], float) + assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[1]["a"] == {"temperature": "2"} assert sensor_test_history[2]["s"] == "off" @@ -1429,7 +1434,7 @@ async def test_history_during_period_significant_domain( assert sensor_test_history[0]["s"] == "on" assert sensor_test_history[0]["a"] == {"temperature": "5"} assert sensor_test_history[0]["lu"] == later.timestamp() - assert sensor_test_history[0]["lc"] == later.timestamp() + assert "lc" not in sensor_test_history[0] # skipped if the same a last_updated (lu) async def test_history_during_period_bad_start_time( From 1f753ecd88d4453edf02154c70fc408741fc993f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 01:04:23 -0500 Subject: [PATCH 464/930] Relocate sqlalchemy filter builder to recorder/filters.py (#71883) --- homeassistant/components/history/__init__.py | 129 +------------------ homeassistant/components/logbook/__init__.py | 6 +- homeassistant/components/logbook/queries.py | 8 +- homeassistant/components/recorder/filters.py | 119 +++++++++++++++++ homeassistant/components/recorder/history.py | 15 ++- tests/components/history/conftest.py | 13 +- tests/components/history/test_init.py | 70 +++++----- 7 files changed, 177 insertions(+), 183 deletions(-) create mode 100644 homeassistant/components/recorder/filters.py diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index 2ebe6405a7a..27acff54f99 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -6,20 +6,17 @@ from datetime import datetime as dt, timedelta from http import HTTPStatus import logging import time -from typing import Any, Literal, cast +from typing import Literal, cast from aiohttp import web -from sqlalchemy import not_, or_ -from sqlalchemy.ext.baked import BakedQuery -from sqlalchemy.orm import Query import voluptuous as vol from homeassistant.components import frontend, websocket_api from homeassistant.components.http import HomeAssistantView -from homeassistant.components.recorder import ( - get_instance, - history, - models as history_models, +from homeassistant.components.recorder import get_instance, history +from homeassistant.components.recorder.filters import ( + Filters, + sqlalchemy_filter_from_include_exclude_conf, ) from homeassistant.components.recorder.statistics import ( list_statistic_ids, @@ -28,13 +25,9 @@ from homeassistant.components.recorder.statistics import ( from homeassistant.components.recorder.util import session_scope from homeassistant.components.websocket_api import messages from homeassistant.components.websocket_api.const import JSON_DUMP -from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entityfilter import ( - CONF_ENTITY_GLOBS, - INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, -) +from homeassistant.helpers.entityfilter import INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util @@ -46,10 +39,6 @@ HISTORY_USE_INCLUDE_ORDER = "history_use_include_order" CONF_ORDER = "use_include_order" -GLOB_TO_SQL_CHARS = { - 42: "%", # * - 46: "_", # . -} CONFIG_SCHEMA = vol.Schema( { @@ -410,112 +399,6 @@ class HistoryPeriodView(HomeAssistantView): return self.json(sorted_result) -def sqlalchemy_filter_from_include_exclude_conf(conf: ConfigType) -> Filters | None: - """Build a sql filter from config.""" - filters = Filters() - if exclude := conf.get(CONF_EXCLUDE): - filters.excluded_entities = exclude.get(CONF_ENTITIES, []) - filters.excluded_domains = exclude.get(CONF_DOMAINS, []) - filters.excluded_entity_globs = exclude.get(CONF_ENTITY_GLOBS, []) - if include := conf.get(CONF_INCLUDE): - filters.included_entities = include.get(CONF_ENTITIES, []) - filters.included_domains = include.get(CONF_DOMAINS, []) - filters.included_entity_globs = include.get(CONF_ENTITY_GLOBS, []) - - return filters if filters.has_config else None - - -class Filters: - """Container for the configured include and exclude filters.""" - - def __init__(self) -> None: - """Initialise the include and exclude filters.""" - self.excluded_entities: list[str] = [] - self.excluded_domains: list[str] = [] - self.excluded_entity_globs: list[str] = [] - - self.included_entities: list[str] = [] - self.included_domains: list[str] = [] - self.included_entity_globs: list[str] = [] - - def apply(self, query: Query) -> Query: - """Apply the entity filter.""" - if not self.has_config: - return query - - return query.filter(self.entity_filter()) - - @property - def has_config(self) -> bool: - """Determine if there is any filter configuration.""" - return bool( - self.excluded_entities - or self.excluded_domains - or self.excluded_entity_globs - or self.included_entities - or self.included_domains - or self.included_entity_globs - ) - - def bake(self, baked_query: BakedQuery) -> None: - """Update a baked query. - - Works the same as apply on a baked_query. - """ - if not self.has_config: - return - - baked_query += lambda q: q.filter(self.entity_filter()) - - def entity_filter(self) -> Any: - """Generate the entity filter query.""" - includes = [] - if self.included_domains: - includes.append( - or_( - *[ - history_models.States.entity_id.like(f"{domain}.%") - for domain in self.included_domains - ] - ).self_group() - ) - if self.included_entities: - includes.append(history_models.States.entity_id.in_(self.included_entities)) - for glob in self.included_entity_globs: - includes.append(_glob_to_like(glob)) - - excludes = [] - if self.excluded_domains: - excludes.append( - or_( - *[ - history_models.States.entity_id.like(f"{domain}.%") - for domain in self.excluded_domains - ] - ).self_group() - ) - if self.excluded_entities: - excludes.append(history_models.States.entity_id.in_(self.excluded_entities)) - for glob in self.excluded_entity_globs: - excludes.append(_glob_to_like(glob)) - - if not includes and not excludes: - return None - - if includes and not excludes: - return or_(*includes) - - if not includes and excludes: - return not_(or_(*excludes)) - - return or_(*includes) & not_(or_(*excludes)) - - -def _glob_to_like(glob_str: str) -> Any: - """Translate glob to sql.""" - return history_models.States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) - - def _entities_may_have_state_changes_after( hass: HomeAssistant, entity_ids: Iterable, start_time: dt ) -> bool: diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 809f13ca598..4bef1f1a23d 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -17,12 +17,12 @@ import voluptuous as vol from homeassistant.components import frontend, websocket_api from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED -from homeassistant.components.history import ( +from homeassistant.components.http import HomeAssistantView +from homeassistant.components.recorder import get_instance +from homeassistant.components.recorder.filters import ( Filters, sqlalchemy_filter_from_include_exclude_conf, ) -from homeassistant.components.http import HomeAssistantView -from homeassistant.components.recorder import get_instance from homeassistant.components.recorder.models import ( process_datetime_to_timestamp, process_timestamp_to_utc_isoformat, diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 29dac31a432..89c530aec43 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -3,17 +3,17 @@ from __future__ import annotations from collections.abc import Iterable from datetime import datetime as dt -from typing import Any import sqlalchemy from sqlalchemy import lambda_stmt, select, union_all from sqlalchemy.orm import Query, aliased +from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.expression import literal from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import Select -from homeassistant.components.history import Filters from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN +from homeassistant.components.recorder.filters import Filters from homeassistant.components.recorder.models import ( ENTITY_ID_LAST_UPDATED_INDEX, LAST_UPDATED_INDEX, @@ -236,7 +236,7 @@ def _all_stmt( start_day: dt, end_day: dt, event_types: tuple[str, ...], - entity_filter: Any | None = None, + entity_filter: ClauseList | None = None, context_id: str | None = None, ) -> StatementLambdaElement: """Generate a logbook query for all entities.""" @@ -410,7 +410,7 @@ def _continuous_domain_matcher() -> sqlalchemy.or_: ).self_group() -def _not_uom_attributes_matcher() -> Any: +def _not_uom_attributes_matcher() -> ClauseList: """Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql.""" return ~StateAttributes.shared_attrs.like( UNIT_OF_MEASUREMENT_JSON_LIKE diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py new file mode 100644 index 00000000000..bb19dfc6d62 --- /dev/null +++ b/homeassistant/components/recorder/filters.py @@ -0,0 +1,119 @@ +"""Provide pre-made queries on top of the recorder component.""" +from __future__ import annotations + +from sqlalchemy import not_, or_ +from sqlalchemy.ext.baked import BakedQuery +from sqlalchemy.sql.elements import ClauseList + +from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE +from homeassistant.helpers.entityfilter import CONF_ENTITY_GLOBS +from homeassistant.helpers.typing import ConfigType + +from .models import States + +DOMAIN = "history" +HISTORY_FILTERS = "history_filters" + +GLOB_TO_SQL_CHARS = { + 42: "%", # * + 46: "_", # . +} + + +def sqlalchemy_filter_from_include_exclude_conf(conf: ConfigType) -> Filters | None: + """Build a sql filter from config.""" + filters = Filters() + if exclude := conf.get(CONF_EXCLUDE): + filters.excluded_entities = exclude.get(CONF_ENTITIES, []) + filters.excluded_domains = exclude.get(CONF_DOMAINS, []) + filters.excluded_entity_globs = exclude.get(CONF_ENTITY_GLOBS, []) + if include := conf.get(CONF_INCLUDE): + filters.included_entities = include.get(CONF_ENTITIES, []) + filters.included_domains = include.get(CONF_DOMAINS, []) + filters.included_entity_globs = include.get(CONF_ENTITY_GLOBS, []) + + return filters if filters.has_config else None + + +class Filters: + """Container for the configured include and exclude filters.""" + + def __init__(self) -> None: + """Initialise the include and exclude filters.""" + self.excluded_entities: list[str] = [] + self.excluded_domains: list[str] = [] + self.excluded_entity_globs: list[str] = [] + + self.included_entities: list[str] = [] + self.included_domains: list[str] = [] + self.included_entity_globs: list[str] = [] + + @property + def has_config(self) -> bool: + """Determine if there is any filter configuration.""" + return bool( + self.excluded_entities + or self.excluded_domains + or self.excluded_entity_globs + or self.included_entities + or self.included_domains + or self.included_entity_globs + ) + + def bake(self, baked_query: BakedQuery) -> BakedQuery: + """Update a baked query. + + Works the same as apply on a baked_query. + """ + if not self.has_config: + return + + baked_query += lambda q: q.filter(self.entity_filter()) + + def entity_filter(self) -> ClauseList: + """Generate the entity filter query.""" + includes = [] + if self.included_domains: + includes.append( + or_( + *[ + States.entity_id.like(f"{domain}.%") + for domain in self.included_domains + ] + ).self_group() + ) + if self.included_entities: + includes.append(States.entity_id.in_(self.included_entities)) + for glob in self.included_entity_globs: + includes.append(_glob_to_like(glob)) + + excludes = [] + if self.excluded_domains: + excludes.append( + or_( + *[ + States.entity_id.like(f"{domain}.%") + for domain in self.excluded_domains + ] + ).self_group() + ) + if self.excluded_entities: + excludes.append(States.entity_id.in_(self.excluded_entities)) + for glob in self.excluded_entity_globs: + excludes.append(_glob_to_like(glob)) + + if not includes and not excludes: + return None + + if includes and not excludes: + return or_(*includes) + + if not includes and excludes: + return not_(or_(*excludes)) + + return or_(*includes) & not_(or_(*excludes)) + + +def _glob_to_like(glob_str: str) -> ClauseList: + """Translate glob to sql.""" + return States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 7def35ce3ac..316e5ab27c8 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -25,6 +25,7 @@ from homeassistant.components.websocket_api.const import ( from homeassistant.core import HomeAssistant, State, split_entity_id import homeassistant.util.dt as dt_util +from .filters import Filters from .models import ( LazyState, RecorderRuns, @@ -163,7 +164,7 @@ def get_significant_states( start_time: datetime, end_time: datetime | None = None, entity_ids: list[str] | None = None, - filters: Any | None = None, + filters: Filters | None = None, include_start_time_state: bool = True, significant_changes_only: bool = True, minimal_response: bool = False, @@ -205,7 +206,7 @@ def _query_significant_states_with_session( start_time: datetime, end_time: datetime | None = None, entity_ids: list[str] | None = None, - filters: Any = None, + filters: Filters | None = None, significant_changes_only: bool = True, no_attributes: bool = False, ) -> list[Row]: @@ -281,7 +282,7 @@ def get_significant_states_with_session( start_time: datetime, end_time: datetime | None = None, entity_ids: list[str] | None = None, - filters: Any = None, + filters: Filters | None = None, include_start_time_state: bool = True, significant_changes_only: bool = True, minimal_response: bool = False, @@ -330,7 +331,7 @@ def get_full_significant_states_with_session( start_time: datetime, end_time: datetime | None = None, entity_ids: list[str] | None = None, - filters: Any = None, + filters: Filters | None = None, include_start_time_state: bool = True, significant_changes_only: bool = True, no_attributes: bool = False, @@ -549,7 +550,7 @@ def _most_recent_state_ids_subquery(query: Query) -> Query: def _get_states_baked_query_for_all( hass: HomeAssistant, - filters: Any | None = None, + filters: Filters | None = None, no_attributes: bool = False, ) -> BakedQuery: """Baked query to get states for all entities.""" @@ -573,7 +574,7 @@ def _get_rows_with_session( utc_point_in_time: datetime, entity_ids: list[str] | None = None, run: RecorderRuns | None = None, - filters: Any | None = None, + filters: Filters | None = None, no_attributes: bool = False, ) -> list[Row]: """Return the states at a specific point in time.""" @@ -640,7 +641,7 @@ def _sorted_states_to_dict( states: Iterable[Row], start_time: datetime, entity_ids: list[str] | None, - filters: Any = None, + filters: Filters | None = None, include_start_time_state: bool = True, minimal_response: bool = False, no_attributes: bool = False, diff --git a/tests/components/history/conftest.py b/tests/components/history/conftest.py index 5e81b444393..a2916153acc 100644 --- a/tests/components/history/conftest.py +++ b/tests/components/history/conftest.py @@ -2,6 +2,7 @@ import pytest from homeassistant.components import history +from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE from homeassistant.setup import setup_component @@ -13,13 +14,13 @@ def hass_history(hass_recorder): config = history.CONFIG_SCHEMA( { history.DOMAIN: { - history.CONF_INCLUDE: { - history.CONF_DOMAINS: ["media_player"], - history.CONF_ENTITIES: ["thermostat.test"], + CONF_INCLUDE: { + CONF_DOMAINS: ["media_player"], + CONF_ENTITIES: ["thermostat.test"], }, - history.CONF_EXCLUDE: { - history.CONF_DOMAINS: ["thermostat"], - history.CONF_ENTITIES: ["media_player.test"], + CONF_EXCLUDE: { + CONF_DOMAINS: ["thermostat"], + CONF_ENTITIES: ["media_player.test"], }, } } diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 23e0550d6aa..0425c9bc2e7 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -11,7 +11,7 @@ from pytest import approx from homeassistant.components import history from homeassistant.components.recorder.history import get_significant_states from homeassistant.components.recorder.models import process_timestamp -from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES +from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE import homeassistant.core as ha from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component @@ -186,9 +186,7 @@ def test_get_significant_states_exclude_domain(hass_history): config = history.CONFIG_SCHEMA( { ha.DOMAIN: {}, - history.DOMAIN: { - history.CONF_EXCLUDE: {history.CONF_DOMAINS: ["media_player"]} - }, + history.DOMAIN: {CONF_EXCLUDE: {CONF_DOMAINS: ["media_player"]}}, } ) check_significant_states(hass, zero, four, states, config) @@ -207,9 +205,7 @@ def test_get_significant_states_exclude_entity(hass_history): config = history.CONFIG_SCHEMA( { ha.DOMAIN: {}, - history.DOMAIN: { - history.CONF_EXCLUDE: {history.CONF_ENTITIES: ["media_player.test"]} - }, + history.DOMAIN: {CONF_EXCLUDE: {CONF_ENTITIES: ["media_player.test"]}}, } ) check_significant_states(hass, zero, four, states, config) @@ -230,9 +226,9 @@ def test_get_significant_states_exclude(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_EXCLUDE: { - history.CONF_DOMAINS: ["thermostat"], - history.CONF_ENTITIES: ["media_player.test"], + CONF_EXCLUDE: { + CONF_DOMAINS: ["thermostat"], + CONF_ENTITIES: ["media_player.test"], } }, } @@ -257,10 +253,8 @@ def test_get_significant_states_exclude_include_entity(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_INCLUDE: { - history.CONF_ENTITIES: ["media_player.test", "thermostat.test"] - }, - history.CONF_EXCLUDE: {history.CONF_DOMAINS: ["thermostat"]}, + CONF_INCLUDE: {CONF_ENTITIES: ["media_player.test", "thermostat.test"]}, + CONF_EXCLUDE: {CONF_DOMAINS: ["thermostat"]}, }, } ) @@ -282,9 +276,7 @@ def test_get_significant_states_include_domain(hass_history): config = history.CONFIG_SCHEMA( { ha.DOMAIN: {}, - history.DOMAIN: { - history.CONF_INCLUDE: {history.CONF_DOMAINS: ["thermostat", "script"]} - }, + history.DOMAIN: {CONF_INCLUDE: {CONF_DOMAINS: ["thermostat", "script"]}}, } ) check_significant_states(hass, zero, four, states, config) @@ -306,9 +298,7 @@ def test_get_significant_states_include_entity(hass_history): config = history.CONFIG_SCHEMA( { ha.DOMAIN: {}, - history.DOMAIN: { - history.CONF_INCLUDE: {history.CONF_ENTITIES: ["media_player.test"]} - }, + history.DOMAIN: {CONF_INCLUDE: {CONF_ENTITIES: ["media_player.test"]}}, } ) check_significant_states(hass, zero, four, states, config) @@ -330,9 +320,9 @@ def test_get_significant_states_include(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_INCLUDE: { - history.CONF_DOMAINS: ["thermostat"], - history.CONF_ENTITIES: ["media_player.test"], + CONF_INCLUDE: { + CONF_DOMAINS: ["thermostat"], + CONF_ENTITIES: ["media_player.test"], } }, } @@ -359,8 +349,8 @@ def test_get_significant_states_include_exclude_domain(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_INCLUDE: {history.CONF_DOMAINS: ["media_player"]}, - history.CONF_EXCLUDE: {history.CONF_DOMAINS: ["media_player"]}, + CONF_INCLUDE: {CONF_DOMAINS: ["media_player"]}, + CONF_EXCLUDE: {CONF_DOMAINS: ["media_player"]}, }, } ) @@ -386,8 +376,8 @@ def test_get_significant_states_include_exclude_entity(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_INCLUDE: {history.CONF_ENTITIES: ["media_player.test"]}, - history.CONF_EXCLUDE: {history.CONF_ENTITIES: ["media_player.test"]}, + CONF_INCLUDE: {CONF_ENTITIES: ["media_player.test"]}, + CONF_EXCLUDE: {CONF_ENTITIES: ["media_player.test"]}, }, } ) @@ -410,13 +400,13 @@ def test_get_significant_states_include_exclude(hass_history): { ha.DOMAIN: {}, history.DOMAIN: { - history.CONF_INCLUDE: { - history.CONF_DOMAINS: ["media_player"], - history.CONF_ENTITIES: ["thermostat.test"], + CONF_INCLUDE: { + CONF_DOMAINS: ["media_player"], + CONF_ENTITIES: ["thermostat.test"], }, - history.CONF_EXCLUDE: { - history.CONF_DOMAINS: ["thermostat"], - history.CONF_ENTITIES: ["media_player.test"], + CONF_EXCLUDE: { + CONF_DOMAINS: ["thermostat"], + CONF_ENTITIES: ["media_player.test"], }, }, } @@ -503,14 +493,14 @@ def test_get_significant_states_only(hass_history): def check_significant_states(hass, zero, four, states, config): """Check if significant states are retrieved.""" filters = history.Filters() - exclude = config[history.DOMAIN].get(history.CONF_EXCLUDE) + exclude = config[history.DOMAIN].get(CONF_EXCLUDE) if exclude: - filters.excluded_entities = exclude.get(history.CONF_ENTITIES, []) - filters.excluded_domains = exclude.get(history.CONF_DOMAINS, []) - include = config[history.DOMAIN].get(history.CONF_INCLUDE) + filters.excluded_entities = exclude.get(CONF_ENTITIES, []) + filters.excluded_domains = exclude.get(CONF_DOMAINS, []) + include = config[history.DOMAIN].get(CONF_INCLUDE) if include: - filters.included_entities = include.get(history.CONF_ENTITIES, []) - filters.included_domains = include.get(history.CONF_DOMAINS, []) + filters.included_entities = include.get(CONF_ENTITIES, []) + filters.included_domains = include.get(CONF_DOMAINS, []) hist = get_significant_states(hass, zero, four, filters=filters) assert states == hist @@ -1496,7 +1486,7 @@ async def test_history_during_period_with_use_include_order( { history.DOMAIN: { history.CONF_ORDER: True, - history.CONF_INCLUDE: { + CONF_INCLUDE: { CONF_ENTITIES: sort_order, CONF_DOMAINS: ["sensor"], }, From e0bf1fba8d9fb37364af422575eeac855ab406cc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 01:05:56 -0500 Subject: [PATCH 465/930] Add logbook descriptions for mobile app zone enter and exit (#71749) --- .../components/mobile_app/logbook.py | 54 +++++++++++++++++++ tests/components/mobile_app/test_logbook.py | 50 +++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 homeassistant/components/mobile_app/logbook.py create mode 100644 tests/components/mobile_app/test_logbook.py diff --git a/homeassistant/components/mobile_app/logbook.py b/homeassistant/components/mobile_app/logbook.py new file mode 100644 index 00000000000..6dd4d007e3e --- /dev/null +++ b/homeassistant/components/mobile_app/logbook.py @@ -0,0 +1,54 @@ +"""Describe mobile_app logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON +from homeassistant.core import Event, HomeAssistant, callback + +from .const import DOMAIN + +IOS_EVENT_ZONE_ENTERED = "ios.zone_entered" +IOS_EVENT_ZONE_EXITED = "ios.zone_exited" + +ATTR_ZONE = "zone" +ATTR_SOURCE_DEVICE_NAME = "sourceDeviceName" +ATTR_SOURCE_DEVICE_ID = "sourceDeviceID" +EVENT_TO_DESCRIPTION = { + IOS_EVENT_ZONE_ENTERED: "entered zone", + IOS_EVENT_ZONE_EXITED: "exited zone", +} + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + + @callback + def async_describe_zone_event(event: Event) -> dict[str, str]: + """Describe mobile_app logbook event.""" + data = event.data + event_description = EVENT_TO_DESCRIPTION[event.event_type] + zone_entity_id = data.get(ATTR_ZONE) + source_device_name = data.get( + ATTR_SOURCE_DEVICE_NAME, data.get(ATTR_SOURCE_DEVICE_ID) + ) + zone_name = None + zone_icon = None + if zone_entity_id and (zone_state := hass.states.get(zone_entity_id)): + zone_name = zone_state.attributes.get(ATTR_FRIENDLY_NAME) + zone_icon = zone_state.attributes.get(ATTR_ICON) + description = { + "name": source_device_name, + "message": f"{event_description} {zone_name or zone_entity_id}", + "icon": zone_icon or "mdi:crosshairs-gps", + } + if zone_entity_id: + description["entity_id"] = zone_entity_id + return description + + async_describe_event(DOMAIN, IOS_EVENT_ZONE_ENTERED, async_describe_zone_event) + async_describe_event(DOMAIN, IOS_EVENT_ZONE_EXITED, async_describe_zone_event) diff --git a/tests/components/mobile_app/test_logbook.py b/tests/components/mobile_app/test_logbook.py new file mode 100644 index 00000000000..b151bd11a26 --- /dev/null +++ b/tests/components/mobile_app/test_logbook.py @@ -0,0 +1,50 @@ +"""The tests for mobile_app logbook.""" + +from homeassistant.components.mobile_app.logbook import ( + DOMAIN, + IOS_EVENT_ZONE_ENTERED, + IOS_EVENT_ZONE_EXITED, +) +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON +from homeassistant.setup import async_setup_component + +from tests.components.logbook.common import MockRow, mock_humanify + + +async def test_humanify_ios_events(hass): + """Test humanifying ios events.""" + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + hass.states.async_set( + "zone.bad_place", + "0", + {ATTR_FRIENDLY_NAME: "passport control", ATTR_ICON: "mdi:airplane-marker"}, + ) + await hass.async_block_till_done() + + (event1, event2) = mock_humanify( + hass, + [ + MockRow( + IOS_EVENT_ZONE_ENTERED, + {"sourceDeviceName": "test_phone", "zone": "zone.happy_place"}, + ), + MockRow( + IOS_EVENT_ZONE_EXITED, + {"sourceDeviceName": "test_phone", "zone": "zone.bad_place"}, + ), + ], + ) + + assert event1["name"] == "test_phone" + assert event1["domain"] == DOMAIN + assert event1["message"] == "entered zone zone.happy_place" + assert event1["icon"] == "mdi:crosshairs-gps" + assert event1["entity_id"] == "zone.happy_place" + + assert event2["name"] == "test_phone" + assert event2["domain"] == DOMAIN + assert event2["message"] == "exited zone passport control" + assert event2["icon"] == "mdi:airplane-marker" + assert event2["entity_id"] == "zone.bad_place" From 617b0d04dcab521ce3b68b4f4956c1f341f6ea60 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Sun, 15 May 2022 14:31:18 +0800 Subject: [PATCH 466/930] Decouple stream options from PyAV options (#71247) Co-authored-by: Allen Porter --- homeassistant/components/generic/camera.py | 19 ++++---- .../components/generic/config_flow.py | 17 ++++---- homeassistant/components/generic/const.py | 12 ------ homeassistant/components/onvif/__init__.py | 11 ++--- homeassistant/components/onvif/camera.py | 2 +- homeassistant/components/onvif/config_flow.py | 15 ++----- homeassistant/components/onvif/const.py | 3 -- homeassistant/components/stream/__init__.py | 43 ++++++++++++++++--- homeassistant/components/stream/const.py | 11 +++++ homeassistant/components/stream/worker.py | 4 ++ tests/components/generic/test_config_flow.py | 6 ++- tests/components/onvif/test_config_flow.py | 4 +- 12 files changed, 83 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index 197890efad3..4886e3a0693 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -12,6 +12,11 @@ from homeassistant.components.camera import ( Camera, CameraEntityFeature, ) +from homeassistant.components.stream.const import ( + CONF_RTSP_TRANSPORT, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + RTSP_TRANSPORTS, +) from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( CONF_AUTHENTICATION, @@ -34,14 +39,10 @@ from .const import ( CONF_CONTENT_TYPE, CONF_FRAMERATE, CONF_LIMIT_REFETCH_TO_URL_CHANGE, - CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, - CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DEFAULT_NAME, - FFMPEG_OPTION_MAP, GET_IMAGE_TIMEOUT, - RTSP_TRANSPORTS, ) _LOGGER = logging.getLogger(__name__) @@ -63,7 +64,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( cv.small_float, cv.positive_int ), vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, - vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS.keys()), + vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS), } ) @@ -157,14 +158,10 @@ class GenericCamera(Camera): self.content_type = device_info[CONF_CONTENT_TYPE] self.verify_ssl = device_info[CONF_VERIFY_SSL] if device_info.get(CONF_RTSP_TRANSPORT): - self.stream_options[FFMPEG_OPTION_MAP[CONF_RTSP_TRANSPORT]] = device_info[ - CONF_RTSP_TRANSPORT - ] + self.stream_options[CONF_RTSP_TRANSPORT] = device_info[CONF_RTSP_TRANSPORT] self._auth = generate_auth(device_info) if device_info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): - self.stream_options[ - FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] - ] = "1" + self.stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True self._last_url = None self._last_image = None diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 0a49393d9cc..bf9499c07df 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -16,7 +16,12 @@ from httpx import HTTPStatusError, RequestError, TimeoutException import voluptuous as vol import yarl -from homeassistant.components.stream.const import SOURCE_TIMEOUT +from homeassistant.components.stream.const import ( + CONF_RTSP_TRANSPORT, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + RTSP_TRANSPORTS, + SOURCE_TIMEOUT, +) from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import ( CONF_AUTHENTICATION, @@ -38,15 +43,11 @@ from .const import ( CONF_CONTENT_TYPE, CONF_FRAMERATE, CONF_LIMIT_REFETCH_TO_URL_CHANGE, - CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, - CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DEFAULT_NAME, DOMAIN, - FFMPEG_OPTION_MAP, GET_IMAGE_TIMEOUT, - RTSP_TRANSPORTS, ) _LOGGER = logging.getLogger(__name__) @@ -200,16 +201,16 @@ async def async_test_stream(hass, info) -> dict[str, str]: # For RTSP streams, prefer TCP. This code is duplicated from # homeassistant.components.stream.__init__.py:create_stream() # It may be possible & better to call create_stream() directly. - stream_options: dict[str, str] = {} + stream_options: dict[str, bool | str] = {} if isinstance(stream_source, str) and stream_source[:7] == "rtsp://": stream_options = { "rtsp_flags": "prefer_tcp", "stimeout": "5000000", } if rtsp_transport := info.get(CONF_RTSP_TRANSPORT): - stream_options[FFMPEG_OPTION_MAP[CONF_RTSP_TRANSPORT]] = rtsp_transport + stream_options[CONF_RTSP_TRANSPORT] = rtsp_transport if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): - stream_options[FFMPEG_OPTION_MAP[CONF_USE_WALLCLOCK_AS_TIMESTAMPS]] = "1" + stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True _LOGGER.debug("Attempting to open stream %s", stream_source) container = await hass.async_add_executor_job( partial( diff --git a/homeassistant/components/generic/const.py b/homeassistant/components/generic/const.py index 8ae5f16c4c4..eb0d81d493c 100644 --- a/homeassistant/components/generic/const.py +++ b/homeassistant/components/generic/const.py @@ -7,18 +7,6 @@ CONF_LIMIT_REFETCH_TO_URL_CHANGE = "limit_refetch_to_url_change" CONF_STILL_IMAGE_URL = "still_image_url" CONF_STREAM_SOURCE = "stream_source" CONF_FRAMERATE = "framerate" -CONF_RTSP_TRANSPORT = "rtsp_transport" -CONF_USE_WALLCLOCK_AS_TIMESTAMPS = "use_wallclock_as_timestamps" -FFMPEG_OPTION_MAP = { - CONF_RTSP_TRANSPORT: "rtsp_transport", - CONF_USE_WALLCLOCK_AS_TIMESTAMPS: "use_wallclock_as_timestamps", -} -RTSP_TRANSPORTS = { - "tcp": "TCP", - "udp": "UDP", - "udp_multicast": "UDP Multicast", - "http": "HTTP", -} GET_IMAGE_TIMEOUT = 10 DEFAULT_USERNAME = None diff --git a/homeassistant/components/onvif/__init__.py b/homeassistant/components/onvif/__init__.py index 8956f0ae2f9..d966549a36f 100644 --- a/homeassistant/components/onvif/__init__.py +++ b/homeassistant/components/onvif/__init__.py @@ -2,6 +2,7 @@ from onvif.exceptions import ONVIFAuthError, ONVIFError, ONVIFTimeoutError from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS +from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, @@ -12,13 +13,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import ( - CONF_RTSP_TRANSPORT, - CONF_SNAPSHOT_AUTH, - DEFAULT_ARGUMENTS, - DOMAIN, - RTSP_TRANS_PROTOCOLS, -) +from .const import CONF_SNAPSHOT_AUTH, DEFAULT_ARGUMENTS, DOMAIN from .device import ONVIFDevice @@ -99,7 +94,7 @@ async def async_populate_options(hass, entry): """Populate default options for device.""" options = { CONF_EXTRA_ARGUMENTS: DEFAULT_ARGUMENTS, - CONF_RTSP_TRANSPORT: RTSP_TRANS_PROTOCOLS[0], + CONF_RTSP_TRANSPORT: next(iter(RTSP_TRANSPORTS)), } hass.config_entries.async_update_entry(entry, options=options) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 3475df241b0..6b61a37eb16 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -9,6 +9,7 @@ from yarl import URL from homeassistant.components import ffmpeg from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, get_ffmpeg_manager +from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT from homeassistant.config_entries import ConfigEntry from homeassistant.const import HTTP_BASIC_AUTHENTICATION from homeassistant.core import HomeAssistant @@ -27,7 +28,6 @@ from .const import ( ATTR_SPEED, ATTR_TILT, ATTR_ZOOM, - CONF_RTSP_TRANSPORT, CONF_SNAPSHOT_AUTH, CONTINUOUS_MOVE, DIR_DOWN, diff --git a/homeassistant/components/onvif/config_flow.py b/homeassistant/components/onvif/config_flow.py index c4579702675..894f2fee3df 100644 --- a/homeassistant/components/onvif/config_flow.py +++ b/homeassistant/components/onvif/config_flow.py @@ -13,6 +13,7 @@ from zeep.exceptions import Fault from homeassistant import config_entries from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS +from homeassistant.components.stream import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS from homeassistant.const import ( CONF_HOST, CONF_NAME, @@ -22,15 +23,7 @@ from homeassistant.const import ( ) from homeassistant.core import callback -from .const import ( - CONF_DEVICE_ID, - CONF_RTSP_TRANSPORT, - DEFAULT_ARGUMENTS, - DEFAULT_PORT, - DOMAIN, - LOGGER, - RTSP_TRANS_PROTOCOLS, -) +from .const import CONF_DEVICE_ID, DEFAULT_ARGUMENTS, DEFAULT_PORT, DOMAIN, LOGGER from .device import get_device CONF_MANUAL_INPUT = "Manually configure ONVIF device" @@ -294,9 +287,9 @@ class OnvifOptionsFlowHandler(config_entries.OptionsFlow): vol.Optional( CONF_RTSP_TRANSPORT, default=self.config_entry.options.get( - CONF_RTSP_TRANSPORT, RTSP_TRANS_PROTOCOLS[0] + CONF_RTSP_TRANSPORT, next(iter(RTSP_TRANSPORTS)) ), - ): vol.In(RTSP_TRANS_PROTOCOLS), + ): vol.In(RTSP_TRANSPORTS), } ), ) diff --git a/homeassistant/components/onvif/const.py b/homeassistant/components/onvif/const.py index 3a2e802a5a0..410088f28df 100644 --- a/homeassistant/components/onvif/const.py +++ b/homeassistant/components/onvif/const.py @@ -9,11 +9,8 @@ DEFAULT_PORT = 80 DEFAULT_ARGUMENTS = "-pred 1" CONF_DEVICE_ID = "deviceid" -CONF_RTSP_TRANSPORT = "rtsp_transport" CONF_SNAPSHOT_AUTH = "snapshot_auth" -RTSP_TRANS_PROTOCOLS = ["tcp", "udp", "udp_multicast", "http"] - ATTR_PAN = "pan" ATTR_TILT = "tilt" ATTR_ZOOM = "zoom" diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index abaf367486d..fbdfd97f9b2 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -23,7 +23,7 @@ import secrets import threading import time from types import MappingProxyType -from typing import Any, cast +from typing import Any, Final, cast import voluptuous as vol @@ -39,12 +39,15 @@ from .const import ( ATTR_STREAMS, CONF_LL_HLS, CONF_PART_DURATION, + CONF_RTSP_TRANSPORT, CONF_SEGMENT_DURATION, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DOMAIN, HLS_PROVIDER, MAX_SEGMENTS, OUTPUT_IDLE_TIMEOUT, RECORDER_PROVIDER, + RTSP_TRANSPORTS, SEGMENT_DURATION_ADJUSTER, STREAM_RESTART_INCREMENT, STREAM_RESTART_RESET_TIME, @@ -72,28 +75,32 @@ def redact_credentials(data: str) -> str: def create_stream( hass: HomeAssistant, stream_source: str, - options: dict[str, str], + options: dict[str, Any], stream_label: str | None = None, ) -> Stream: """Create a stream with the specified identfier based on the source url. The stream_source is typically an rtsp url (though any url accepted by ffmpeg is fine) and - options are passed into pyav / ffmpeg as options. + options (see STREAM_OPTIONS_SCHEMA) are converted and passed into pyav / ffmpeg. The stream_label is a string used as an additional message in logging. """ if DOMAIN not in hass.config.components: raise HomeAssistantError("Stream integration is not set up.") + # Convert extra stream options into PyAV options + pyav_options = convert_stream_options(options) # For RTSP streams, prefer TCP if isinstance(stream_source, str) and stream_source[:7] == "rtsp://": - options = { + pyav_options = { "rtsp_flags": "prefer_tcp", "stimeout": "5000000", - **options, + **pyav_options, } - stream = Stream(hass, stream_source, options=options, stream_label=stream_label) + stream = Stream( + hass, stream_source, options=pyav_options, stream_label=stream_label + ) hass.data[DOMAIN][ATTR_STREAMS].append(stream) return stream @@ -464,3 +471,27 @@ class Stream: def _should_retry() -> bool: """Return true if worker failures should be retried, for disabling during tests.""" return True + + +STREAM_OPTIONS_SCHEMA: Final = vol.Schema( + { + vol.Optional(CONF_RTSP_TRANSPORT): vol.In(RTSP_TRANSPORTS), + vol.Optional(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): bool, + } +) + + +def convert_stream_options(stream_options: dict[str, Any]) -> dict[str, str]: + """Convert options from stream options into PyAV options.""" + pyav_options: dict[str, str] = {} + try: + STREAM_OPTIONS_SCHEMA(stream_options) + except vol.Invalid as exc: + raise HomeAssistantError("Invalid stream options") from exc + + if rtsp_transport := stream_options.get(CONF_RTSP_TRANSPORT): + pyav_options["rtsp_transport"] = rtsp_transport + if stream_options.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): + pyav_options["use_wallclock_as_timestamps"] = "1" + + return pyav_options diff --git a/homeassistant/components/stream/const.py b/homeassistant/components/stream/const.py index 50ae43df0d0..f8c9ba85d59 100644 --- a/homeassistant/components/stream/const.py +++ b/homeassistant/components/stream/const.py @@ -42,3 +42,14 @@ STREAM_RESTART_RESET_TIME = 300 # Reset wait_timeout after this many seconds CONF_LL_HLS = "ll_hls" CONF_PART_DURATION = "part_duration" CONF_SEGMENT_DURATION = "segment_duration" + +CONF_PREFER_TCP = "prefer_tcp" +CONF_RTSP_TRANSPORT = "rtsp_transport" +# The first dict entry below may be used as the default when populating options +RTSP_TRANSPORTS = { + "tcp": "TCP", + "udp": "UDP", + "udp_multicast": "UDP Multicast", + "http": "HTTP", +} +CONF_USE_WALLCLOCK_AS_TIMESTAMPS = "use_wallclock_as_timestamps" diff --git a/homeassistant/components/stream/worker.py b/homeassistant/components/stream/worker.py index bde5ab0fb05..f8d12c1cb44 100644 --- a/homeassistant/components/stream/worker.py +++ b/homeassistant/components/stream/worker.py @@ -452,6 +452,10 @@ def stream_worker( ) -> None: """Handle consuming streams.""" + if av.library_versions["libavformat"][0] >= 59 and "stimeout" in options: + # the stimeout option was renamed to timeout as of ffmpeg 5.0 + options["timeout"] = options["stimeout"] + del options["stimeout"] try: container = av.open(source, options=options, timeout=SOURCE_TIMEOUT) except av.AVError as err: diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index dd53cb8548e..82cf41c6e91 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -15,12 +15,14 @@ from homeassistant.components.generic.const import ( CONF_CONTENT_TYPE, CONF_FRAMERATE, CONF_LIMIT_REFETCH_TO_URL_CHANGE, - CONF_RTSP_TRANSPORT, CONF_STILL_IMAGE_URL, CONF_STREAM_SOURCE, - CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DOMAIN, ) +from homeassistant.components.stream.const import ( + CONF_RTSP_TRANSPORT, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, +) from homeassistant.const import ( CONF_AUTHENTICATION, CONF_NAME, diff --git a/tests/components/onvif/test_config_flow.py b/tests/components/onvif/test_config_flow.py index 0760ead2ba1..1e4dabfe7cf 100644 --- a/tests/components/onvif/test_config_flow.py +++ b/tests/components/onvif/test_config_flow.py @@ -323,12 +323,12 @@ async def test_option_flow(hass): result["flow_id"], user_input={ config_flow.CONF_EXTRA_ARGUMENTS: "", - config_flow.CONF_RTSP_TRANSPORT: config_flow.RTSP_TRANS_PROTOCOLS[1], + config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1], }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["data"] == { config_flow.CONF_EXTRA_ARGUMENTS: "", - config_flow.CONF_RTSP_TRANSPORT: config_flow.RTSP_TRANS_PROTOCOLS[1], + config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1], } From 8549af38554084605b4edd62ee80d3a12ee67749 Mon Sep 17 00:00:00 2001 From: RadekHvizdos <10856567+RadekHvizdos@users.noreply.github.com> Date: Sun, 15 May 2022 11:29:35 +0200 Subject: [PATCH 467/930] Add missing Shelly Cover sensors bugfix (#71831) Switching Shelly Plus 2PM from switch to cover mode results in missing sensors for Power, Voltage, Energy and Temperature. These parameters are still available in the API, but need to be accessed via "cover" key instead of "switch" key. This change adds the missing sensors. --- homeassistant/components/shelly/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index df5a75a7ed9..77c09283fbf 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -297,6 +297,9 @@ def get_rpc_key_instances(keys_dict: dict[str, Any], key: str) -> list[str]: if key in keys_dict: return [key] + if key == "switch" and "cover:0" in keys_dict: + key = "cover" + keys_list: list[str] = [] for i in range(MAX_RPC_KEY_INSTANCES): key_inst = f"{key}:{i}" From 2b637f71faaeb16fa855af578c4ab6a6b22190cb Mon Sep 17 00:00:00 2001 From: rappenze Date: Sun, 15 May 2022 15:02:05 +0200 Subject: [PATCH 468/930] Limit parallel requests in fibaro light (#71762) --- homeassistant/components/fibaro/light.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index 08a9e651668..0115e0301c3 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -23,6 +23,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import FIBARO_DEVICES, FibaroDevice from .const import DOMAIN +PARALLEL_UPDATES = 2 + def scaleto255(value: int | None) -> int: """Scale the input value from 0-100 to 0-255.""" From 8ea5ec6f0814326b11339f56655448d7cf036af3 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 15 May 2022 15:56:45 +0200 Subject: [PATCH 469/930] Streamline setup of deCONZ number platform (#71840) --- homeassistant/components/deconz/number.py | 53 +++++++---------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 12ff768cad2..81886fd3c7c 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -5,6 +5,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from pydeconz.models.event import EventType from pydeconz.models.sensor.presence import PRESENCE_DELAY, Presence from homeassistant.components.number import ( @@ -14,7 +15,6 @@ from homeassistant.components.number import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -62,48 +62,27 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_sensor(sensors: list[Presence] | None = None) -> None: - """Add number config sensor from deCONZ.""" - entities = [] - - if sensors is None: - sensors = list(gateway.api.sensors.presence.values()) - - for sensor in sensors: - - if sensor.type.startswith("CLIP"): + def async_add_sensor(_: EventType, sensor_id: str) -> None: + """Add sensor from deCONZ.""" + sensor = gateway.api.sensors.presence[sensor_id] + if sensor.type.startswith("CLIP"): + return + for description in ENTITY_DESCRIPTIONS.get(type(sensor), []): + if ( + not hasattr(sensor, description.key) + or description.value_fn(sensor) is None + ): continue - - known_entities = set(gateway.entities[DOMAIN]) - for description in ENTITY_DESCRIPTIONS.get(type(sensor), []): - - if ( - not hasattr(sensor, description.key) - or description.value_fn(sensor) is None - ): - continue - - new_entity = DeconzNumber(sensor, gateway, description) - if new_entity.unique_id not in known_entities: - entities.append(new_entity) - - if entities: - async_add_entities(entities) + async_add_entities([DeconzNumber(sensor, gateway, description)]) config_entry.async_on_unload( - async_dispatcher_connect( - hass, - gateway.signal_new_sensor, + gateway.api.sensors.presence.subscribe( async_add_sensor, + EventType.ADDED, ) ) - - async_add_sensor( - [ - gateway.api.sensors.presence[key] - for key in sorted(gateway.api.sensors.presence, key=int) - ] - ) + for sensor_id in gateway.api.sensors.presence: + async_add_sensor(EventType.ADDED, sensor_id) class DeconzNumber(DeconzDevice, NumberEntity): From 98809675ff8cc87dbb5e165ad3096d75e69f3b44 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 10:47:29 -0500 Subject: [PATCH 470/930] Convert history queries to use lambda_stmt (#71870) Co-authored-by: Paulus Schoutsen --- homeassistant/components/recorder/__init__.py | 3 +- homeassistant/components/recorder/filters.py | 11 - homeassistant/components/recorder/history.py | 454 ++++++++---------- homeassistant/components/recorder/util.py | 46 +- tests/components/recorder/test_util.py | 61 ++- 5 files changed, 313 insertions(+), 262 deletions(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index ca1ecd8c71a..4063e443e8b 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -20,7 +20,7 @@ from homeassistant.helpers.integration_platform import ( from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -from . import history, statistics, websocket_api +from . import statistics, websocket_api from .const import ( CONF_DB_INTEGRITY_CHECK, DATA_INSTANCE, @@ -166,7 +166,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: instance.async_register() instance.start() async_register_services(hass, instance) - history.async_setup(hass) statistics.async_setup(hass) websocket_api.async_setup(hass) await async_process_integration_platforms(hass, DOMAIN, _process_recorder_platform) diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index bb19dfc6d62..adc746379e6 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -2,7 +2,6 @@ from __future__ import annotations from sqlalchemy import not_, or_ -from sqlalchemy.ext.baked import BakedQuery from sqlalchemy.sql.elements import ClauseList from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE @@ -60,16 +59,6 @@ class Filters: or self.included_entity_globs ) - def bake(self, baked_query: BakedQuery) -> BakedQuery: - """Update a baked query. - - Works the same as apply on a baked_query. - """ - if not self.has_config: - return - - baked_query += lambda q: q.filter(self.entity_filter()) - def entity_filter(self) -> ClauseList: """Generate the entity filter query.""" includes = [] diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 316e5ab27c8..3df444faccc 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -9,13 +9,12 @@ import logging import time from typing import Any, cast -from sqlalchemy import Column, Text, and_, bindparam, func, or_ +from sqlalchemy import Column, Text, and_, func, lambda_stmt, or_, select from sqlalchemy.engine.row import Row -from sqlalchemy.ext import baked -from sqlalchemy.ext.baked import BakedQuery from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal +from sqlalchemy.sql.lambdas import StatementLambdaElement from homeassistant.components import recorder from homeassistant.components.websocket_api.const import ( @@ -36,7 +35,7 @@ from .models import ( process_timestamp_to_utc_isoformat, row_to_compressed_state, ) -from .util import execute, session_scope +from .util import execute_stmt_lambda_element, session_scope # mypy: allow-untyped-defs, no-check-untyped-defs @@ -111,52 +110,48 @@ QUERY_STATES_NO_LAST_CHANGED = [ StateAttributes.shared_attrs, ] -HISTORY_BAKERY = "recorder_history_bakery" + +def _schema_version(hass: HomeAssistant) -> int: + return recorder.get_instance(hass).schema_version -def bake_query_and_join_attributes( - hass: HomeAssistant, no_attributes: bool, include_last_changed: bool = True -) -> tuple[Any, bool]: - """Return the initial backed query and if StateAttributes should be joined. +def lambda_stmt_and_join_attributes( + schema_version: int, no_attributes: bool, include_last_changed: bool = True +) -> tuple[StatementLambdaElement, bool]: + """Return the lambda_stmt and if StateAttributes should be joined. - Because these are baked queries the values inside the lambdas need + Because these are lambda_stmt the values inside the lambdas need to be explicitly written out to avoid caching the wrong values. """ - bakery: baked.bakery = hass.data[HISTORY_BAKERY] # If no_attributes was requested we do the query # without the attributes fields and do not join the # state_attributes table if no_attributes: if include_last_changed: - return bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR)), False + return lambda_stmt(lambda: select(*QUERY_STATE_NO_ATTR)), False return ( - bakery(lambda s: s.query(*QUERY_STATE_NO_ATTR_NO_LAST_CHANGED)), + lambda_stmt(lambda: select(*QUERY_STATE_NO_ATTR_NO_LAST_CHANGED)), False, ) # If we in the process of migrating schema we do # not want to join the state_attributes table as we # do not know if it will be there yet - if recorder.get_instance(hass).schema_version < 25: + if schema_version < 25: if include_last_changed: return ( - bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25)), + lambda_stmt(lambda: select(*QUERY_STATES_PRE_SCHEMA_25)), False, ) return ( - bakery(lambda s: s.query(*QUERY_STATES_PRE_SCHEMA_25_NO_LAST_CHANGED)), + lambda_stmt(lambda: select(*QUERY_STATES_PRE_SCHEMA_25_NO_LAST_CHANGED)), False, ) # Finally if no migration is in progress and no_attributes # was not requested, we query both attributes columns and # join state_attributes if include_last_changed: - return bakery(lambda s: s.query(*QUERY_STATES)), True - return bakery(lambda s: s.query(*QUERY_STATES_NO_LAST_CHANGED)), True - - -def async_setup(hass: HomeAssistant) -> None: - """Set up the history hooks.""" - hass.data[HISTORY_BAKERY] = baked.bakery() + return lambda_stmt(lambda: select(*QUERY_STATES)), True + return lambda_stmt(lambda: select(*QUERY_STATES_NO_LAST_CHANGED)), True def get_significant_states( @@ -200,38 +195,30 @@ def _ignore_domains_filter(query: Query) -> Query: ) -def _query_significant_states_with_session( - hass: HomeAssistant, - session: Session, +def _significant_states_stmt( + schema_version: int, start_time: datetime, - end_time: datetime | None = None, - entity_ids: list[str] | None = None, - filters: Filters | None = None, - significant_changes_only: bool = True, - no_attributes: bool = False, -) -> list[Row]: + end_time: datetime | None, + entity_ids: list[str] | None, + filters: Filters | None, + significant_changes_only: bool, + no_attributes: bool, +) -> StatementLambdaElement: """Query the database for significant state changes.""" - if _LOGGER.isEnabledFor(logging.DEBUG): - timer_start = time.perf_counter() - - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=True + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, no_attributes, include_last_changed=not significant_changes_only ) - - if entity_ids is not None and len(entity_ids) == 1: - if ( - significant_changes_only - and split_entity_id(entity_ids[0])[0] not in SIGNIFICANT_DOMAINS - ): - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=False - ) - baked_query += lambda q: q.filter( - (States.last_changed == States.last_updated) - | States.last_changed.is_(None) - ) + if ( + entity_ids + and len(entity_ids) == 1 + and significant_changes_only + and split_entity_id(entity_ids[0])[0] not in SIGNIFICANT_DOMAINS + ): + stmt += lambda q: q.filter( + (States.last_changed == States.last_updated) | States.last_changed.is_(None) + ) elif significant_changes_only: - baked_query += lambda q: q.filter( + stmt += lambda q: q.filter( or_( *[ States.entity_id.like(entity_domain) @@ -244,36 +231,24 @@ def _query_significant_states_with_session( ) ) - if entity_ids is not None: - baked_query += lambda q: q.filter( - States.entity_id.in_(bindparam("entity_ids", expanding=True)) - ) + if entity_ids: + stmt += lambda q: q.filter(States.entity_id.in_(entity_ids)) else: - baked_query += _ignore_domains_filter - if filters: - filters.bake(baked_query) + stmt += _ignore_domains_filter + if filters and filters.has_config: + entity_filter = filters.entity_filter() + stmt += lambda q: q.filter(entity_filter) - baked_query += lambda q: q.filter(States.last_updated > bindparam("start_time")) - if end_time is not None: - baked_query += lambda q: q.filter(States.last_updated < bindparam("end_time")) + stmt += lambda q: q.filter(States.last_updated > start_time) + if end_time: + stmt += lambda q: q.filter(States.last_updated < end_time) if join_attributes: - baked_query += lambda q: q.outerjoin( + stmt += lambda q: q.outerjoin( StateAttributes, States.attributes_id == StateAttributes.attributes_id ) - baked_query += lambda q: q.order_by(States.entity_id, States.last_updated) - - states = execute( - baked_query(session).params( - start_time=start_time, end_time=end_time, entity_ids=entity_ids - ) - ) - - if _LOGGER.isEnabledFor(logging.DEBUG): - elapsed = time.perf_counter() - timer_start - _LOGGER.debug("get_significant_states took %fs", elapsed) - - return states + stmt += lambda q: q.order_by(States.entity_id, States.last_updated) + return stmt def get_significant_states_with_session( @@ -301,9 +276,8 @@ def get_significant_states_with_session( as well as all states from certain domains (for instance thermostat so that we get current temperature in our graphs). """ - states = _query_significant_states_with_session( - hass, - session, + stmt = _significant_states_stmt( + _schema_version(hass), start_time, end_time, entity_ids, @@ -311,6 +285,9 @@ def get_significant_states_with_session( significant_changes_only, no_attributes, ) + states = execute_stmt_lambda_element( + session, stmt, None if entity_ids else start_time, end_time + ) return _sorted_states_to_dict( hass, session, @@ -354,6 +331,38 @@ def get_full_significant_states_with_session( ) +def _state_changed_during_period_stmt( + schema_version: int, + start_time: datetime, + end_time: datetime | None, + entity_id: str | None, + no_attributes: bool, + descending: bool, + limit: int | None, +) -> StatementLambdaElement: + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, no_attributes, include_last_changed=False + ) + stmt += lambda q: q.filter( + ((States.last_changed == States.last_updated) | States.last_changed.is_(None)) + & (States.last_updated > start_time) + ) + if end_time: + stmt += lambda q: q.filter(States.last_updated < end_time) + stmt += lambda q: q.filter(States.entity_id == entity_id) + if join_attributes: + stmt += lambda q: q.outerjoin( + StateAttributes, States.attributes_id == StateAttributes.attributes_id + ) + if descending: + stmt += lambda q: q.order_by(States.entity_id, States.last_updated.desc()) + else: + stmt += lambda q: q.order_by(States.entity_id, States.last_updated) + if limit: + stmt += lambda q: q.limit(limit) + return stmt + + def state_changes_during_period( hass: HomeAssistant, start_time: datetime, @@ -365,52 +374,21 @@ def state_changes_during_period( include_start_time_state: bool = True, ) -> MutableMapping[str, list[State]]: """Return states changes during UTC period start_time - end_time.""" + entity_id = entity_id.lower() if entity_id is not None else None + with session_scope(hass=hass) as session: - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=False + stmt = _state_changed_during_period_stmt( + _schema_version(hass), + start_time, + end_time, + entity_id, + no_attributes, + descending, + limit, ) - - baked_query += lambda q: q.filter( - ( - (States.last_changed == States.last_updated) - | States.last_changed.is_(None) - ) - & (States.last_updated > bindparam("start_time")) + states = execute_stmt_lambda_element( + session, stmt, None if entity_id else start_time, end_time ) - - if end_time is not None: - baked_query += lambda q: q.filter( - States.last_updated < bindparam("end_time") - ) - - if entity_id is not None: - baked_query += lambda q: q.filter_by(entity_id=bindparam("entity_id")) - entity_id = entity_id.lower() - - if join_attributes: - baked_query += lambda q: q.outerjoin( - StateAttributes, States.attributes_id == StateAttributes.attributes_id - ) - - if descending: - baked_query += lambda q: q.order_by( - States.entity_id, States.last_updated.desc() - ) - else: - baked_query += lambda q: q.order_by(States.entity_id, States.last_updated) - - if limit: - baked_query += lambda q: q.limit(bindparam("limit")) - - states = execute( - baked_query(session).params( - start_time=start_time, - end_time=end_time, - entity_id=entity_id, - limit=limit, - ) - ) - entity_ids = [entity_id] if entity_id is not None else None return cast( @@ -426,41 +404,37 @@ def state_changes_during_period( ) +def _get_last_state_changes_stmt( + schema_version: int, number_of_states: int, entity_id: str +) -> StatementLambdaElement: + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, False, include_last_changed=False + ) + stmt += lambda q: q.filter( + (States.last_changed == States.last_updated) | States.last_changed.is_(None) + ).filter(States.entity_id == entity_id) + if join_attributes: + stmt += lambda q: q.outerjoin( + StateAttributes, States.attributes_id == StateAttributes.attributes_id + ) + stmt += lambda q: q.order_by(States.entity_id, States.last_updated.desc()).limit( + number_of_states + ) + return stmt + + def get_last_state_changes( hass: HomeAssistant, number_of_states: int, entity_id: str ) -> MutableMapping[str, list[State]]: """Return the last number_of_states.""" start_time = dt_util.utcnow() + entity_id = entity_id.lower() if entity_id is not None else None with session_scope(hass=hass) as session: - baked_query, join_attributes = bake_query_and_join_attributes( - hass, False, include_last_changed=False + stmt = _get_last_state_changes_stmt( + _schema_version(hass), number_of_states, entity_id ) - - baked_query += lambda q: q.filter( - (States.last_changed == States.last_updated) | States.last_changed.is_(None) - ) - - if entity_id is not None: - baked_query += lambda q: q.filter_by(entity_id=bindparam("entity_id")) - entity_id = entity_id.lower() - - if join_attributes: - baked_query += lambda q: q.outerjoin( - StateAttributes, States.attributes_id == StateAttributes.attributes_id - ) - baked_query += lambda q: q.order_by( - States.entity_id, States.last_updated.desc() - ) - - baked_query += lambda q: q.limit(bindparam("number_of_states")) - - states = execute( - baked_query(session).params( - number_of_states=number_of_states, entity_id=entity_id - ) - ) - + states = list(execute_stmt_lambda_element(session, stmt)) entity_ids = [entity_id] if entity_id is not None else None return cast( @@ -476,96 +450,91 @@ def get_last_state_changes( ) -def _most_recent_state_ids_entities_subquery(query: Query) -> Query: - """Query to find the most recent state id for specific entities.""" +def _get_states_for_entites_stmt( + schema_version: int, + run_start: datetime, + utc_point_in_time: datetime, + entity_ids: list[str], + no_attributes: bool, +) -> StatementLambdaElement: + """Baked query to get states for specific entities.""" + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, no_attributes, include_last_changed=True + ) # We got an include-list of entities, accelerate the query by filtering already # in the inner query. - most_recent_state_ids = ( - query.session.query(func.max(States.state_id).label("max_state_id")) - .filter( - (States.last_updated >= bindparam("run_start")) - & (States.last_updated < bindparam("utc_point_in_time")) - ) - .filter(States.entity_id.in_(bindparam("entity_ids", expanding=True))) - .group_by(States.entity_id) - .subquery() + stmt += lambda q: q.where( + States.state_id + == ( + select(func.max(States.state_id).label("max_state_id")) + .filter( + (States.last_updated >= run_start) + & (States.last_updated < utc_point_in_time) + ) + .filter(States.entity_id.in_(entity_ids)) + .group_by(States.entity_id) + .subquery() + ).c.max_state_id ) - return query.join( - most_recent_state_ids, - States.state_id == most_recent_state_ids.c.max_state_id, - ) - - -def _get_states_baked_query_for_entites( - hass: HomeAssistant, - no_attributes: bool = False, -) -> BakedQuery: - """Baked query to get states for specific entities.""" - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=True - ) - baked_query += _most_recent_state_ids_entities_subquery if join_attributes: - baked_query += lambda q: q.outerjoin( + stmt += lambda q: q.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) - return baked_query + return stmt -def _most_recent_state_ids_subquery(query: Query) -> Query: - """Find the most recent state ids for all entiites.""" +def _get_states_for_all_stmt( + schema_version: int, + run_start: datetime, + utc_point_in_time: datetime, + filters: Filters | None, + no_attributes: bool, +) -> StatementLambdaElement: + """Baked query to get states for all entities.""" + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, no_attributes, include_last_changed=True + ) # We did not get an include-list of entities, query all states in the inner # query, then filter out unwanted domains as well as applying the custom filter. # This filtering can't be done in the inner query because the domain column is # not indexed and we can't control what's in the custom filter. most_recent_states_by_date = ( - query.session.query( + select( States.entity_id.label("max_entity_id"), func.max(States.last_updated).label("max_last_updated"), ) .filter( - (States.last_updated >= bindparam("run_start")) - & (States.last_updated < bindparam("utc_point_in_time")) + (States.last_updated >= run_start) + & (States.last_updated < utc_point_in_time) ) .group_by(States.entity_id) .subquery() ) - most_recent_state_ids = ( - query.session.query(func.max(States.state_id).label("max_state_id")) - .join( - most_recent_states_by_date, - and_( - States.entity_id == most_recent_states_by_date.c.max_entity_id, - States.last_updated == most_recent_states_by_date.c.max_last_updated, - ), - ) - .group_by(States.entity_id) - .subquery() + stmt += lambda q: q.where( + States.state_id + == ( + select(func.max(States.state_id).label("max_state_id")) + .join( + most_recent_states_by_date, + and_( + States.entity_id == most_recent_states_by_date.c.max_entity_id, + States.last_updated + == most_recent_states_by_date.c.max_last_updated, + ), + ) + .group_by(States.entity_id) + .subquery() + ).c.max_state_id, ) - return query.join( - most_recent_state_ids, - States.state_id == most_recent_state_ids.c.max_state_id, - ) - - -def _get_states_baked_query_for_all( - hass: HomeAssistant, - filters: Filters | None = None, - no_attributes: bool = False, -) -> BakedQuery: - """Baked query to get states for all entities.""" - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=True - ) - baked_query += _most_recent_state_ids_subquery - baked_query += _ignore_domains_filter - if filters: - filters.bake(baked_query) + stmt += _ignore_domains_filter + if filters and filters.has_config: + entity_filter = filters.entity_filter() + stmt += lambda q: q.filter(entity_filter) if join_attributes: - baked_query += lambda q: q.outerjoin( + stmt += lambda q: q.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id) ) - return baked_query + return stmt def _get_rows_with_session( @@ -576,11 +545,15 @@ def _get_rows_with_session( run: RecorderRuns | None = None, filters: Filters | None = None, no_attributes: bool = False, -) -> list[Row]: +) -> Iterable[Row]: """Return the states at a specific point in time.""" + schema_version = _schema_version(hass) if entity_ids and len(entity_ids) == 1: - return _get_single_entity_states_with_session( - hass, session, utc_point_in_time, entity_ids[0], no_attributes + return execute_stmt_lambda_element( + session, + _get_single_entity_states_stmt( + schema_version, utc_point_in_time, entity_ids[0], no_attributes + ), ) if run is None: @@ -593,46 +566,41 @@ def _get_rows_with_session( # We have more than one entity to look at so we need to do a query on states # since the last recorder run started. if entity_ids: - baked_query = _get_states_baked_query_for_entites(hass, no_attributes) - else: - baked_query = _get_states_baked_query_for_all(hass, filters, no_attributes) - - return execute( - baked_query(session).params( - run_start=run.start, - utc_point_in_time=utc_point_in_time, - entity_ids=entity_ids, + stmt = _get_states_for_entites_stmt( + schema_version, run.start, utc_point_in_time, entity_ids, no_attributes ) - ) + else: + stmt = _get_states_for_all_stmt( + schema_version, run.start, utc_point_in_time, filters, no_attributes + ) + + return execute_stmt_lambda_element(session, stmt) -def _get_single_entity_states_with_session( - hass: HomeAssistant, - session: Session, +def _get_single_entity_states_stmt( + schema_version: int, utc_point_in_time: datetime, entity_id: str, no_attributes: bool = False, -) -> list[Row]: +) -> StatementLambdaElement: # Use an entirely different (and extremely fast) query if we only # have a single entity id - baked_query, join_attributes = bake_query_and_join_attributes( - hass, no_attributes, include_last_changed=True + stmt, join_attributes = lambda_stmt_and_join_attributes( + schema_version, no_attributes, include_last_changed=True ) - baked_query += lambda q: q.filter( - States.last_updated < bindparam("utc_point_in_time"), - States.entity_id == bindparam("entity_id"), + stmt += ( + lambda q: q.filter( + States.last_updated < utc_point_in_time, + States.entity_id == entity_id, + ) + .order_by(States.last_updated.desc()) + .limit(1) ) if join_attributes: - baked_query += lambda q: q.outerjoin( + stmt += lambda q: q.outerjoin( StateAttributes, States.attributes_id == StateAttributes.attributes_id ) - baked_query += lambda q: q.order_by(States.last_updated.desc()).limit(1) - - query = baked_query(session).params( - utc_point_in_time=utc_point_in_time, entity_id=entity_id - ) - - return execute(query) + return stmt def _sorted_states_to_dict( diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index f48a6126ea9..eb99c304808 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -1,7 +1,7 @@ """SQLAlchemy util functions.""" from __future__ import annotations -from collections.abc import Callable, Generator +from collections.abc import Callable, Generator, Iterable from contextlib import contextmanager from datetime import date, datetime, timedelta import functools @@ -18,9 +18,12 @@ from awesomeversion import ( import ciso8601 from sqlalchemy import text from sqlalchemy.engine.cursor import CursorFetchStrategy +from sqlalchemy.engine.row import Row from sqlalchemy.exc import OperationalError, SQLAlchemyError +from sqlalchemy.ext.baked import Result from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session +from sqlalchemy.sql.lambdas import StatementLambdaElement from typing_extensions import Concatenate, ParamSpec from homeassistant.core import HomeAssistant @@ -46,6 +49,7 @@ _LOGGER = logging.getLogger(__name__) RETRIES = 3 QUERY_RETRY_WAIT = 0.1 SQLITE3_POSTFIXES = ["", "-wal", "-shm"] +DEFAULT_YIELD_STATES_ROWS = 32768 MIN_VERSION_MARIA_DB = AwesomeVersion("10.3.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_MARIA_DB_ROWNUM = AwesomeVersion("10.2.0", AwesomeVersionStrategy.SIMPLEVER) @@ -119,8 +123,10 @@ def commit(session: Session, work: Any) -> bool: def execute( - qry: Query, to_native: bool = False, validate_entity_ids: bool = True -) -> list: + qry: Query | Result, + to_native: bool = False, + validate_entity_ids: bool = True, +) -> list[Row]: """Query the database and convert the objects to HA native form. This method also retries a few times in the case of stale connections. @@ -163,7 +169,39 @@ def execute( raise time.sleep(QUERY_RETRY_WAIT) - assert False # unreachable + assert False # unreachable # pragma: no cover + + +def execute_stmt_lambda_element( + session: Session, + stmt: StatementLambdaElement, + start_time: datetime | None = None, + end_time: datetime | None = None, + yield_per: int | None = DEFAULT_YIELD_STATES_ROWS, +) -> Iterable[Row]: + """Execute a StatementLambdaElement. + + If the time window passed is greater than one day + the execution method will switch to yield_per to + reduce memory pressure. + + It is not recommended to pass a time window + when selecting non-ranged rows (ie selecting + specific entities) since they are usually faster + with .all(). + """ + executed = session.execute(stmt) + use_all = not start_time or ((end_time or dt_util.utcnow()) - start_time).days <= 1 + for tryno in range(0, RETRIES): + try: + return executed.all() if use_all else executed.yield_per(yield_per) # type: ignore[no-any-return] + except SQLAlchemyError as err: + _LOGGER.error("Error executing query: %s", err) + if tryno == RETRIES - 1: + raise + time.sleep(QUERY_RETRY_WAIT) + + assert False # unreachable # pragma: no cover def validate_or_move_away_sqlite_database(dburl: str) -> bool: diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index 237030c1186..c650ec20fb0 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -6,10 +6,13 @@ from unittest.mock import MagicMock, Mock, patch import pytest from sqlalchemy import text +from sqlalchemy.engine.result import ChunkedIteratorResult +from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.sql.elements import TextClause +from sqlalchemy.sql.lambdas import StatementLambdaElement from homeassistant.components import recorder -from homeassistant.components.recorder import util +from homeassistant.components.recorder import history, util from homeassistant.components.recorder.const import DATA_INSTANCE, SQLITE_URL_PREFIX from homeassistant.components.recorder.models import RecorderRuns from homeassistant.components.recorder.util import ( @@ -24,6 +27,7 @@ from homeassistant.util import dt as dt_util from .common import corrupt_db_file, run_information_with_session from tests.common import SetupRecorderInstanceT, async_test_home_assistant +from tests.components.recorder.common import wait_recording_done def test_session_scope_not_setup(hass_recorder): @@ -510,8 +514,10 @@ def test_basic_sanity_check(hass_recorder): def test_combined_checks(hass_recorder, caplog): """Run Checks on the open database.""" hass = hass_recorder() + instance = recorder.get_instance(hass) + instance.db_retry_wait = 0 - cursor = hass.data[DATA_INSTANCE].engine.raw_connection().cursor() + cursor = instance.engine.raw_connection().cursor() assert util.run_checks_on_open_db("fake_db_path", cursor) is None assert "could not validate that the sqlite3 database" in caplog.text @@ -658,3 +664,54 @@ def test_build_mysqldb_conv(): assert conv["DATETIME"]("2022-05-13T22:33:12.741") == datetime( 2022, 5, 13, 22, 33, 12, 741000, tzinfo=None ) + + +@patch("homeassistant.components.recorder.util.QUERY_RETRY_WAIT", 0) +def test_execute_stmt_lambda_element(hass_recorder): + """Test executing with execute_stmt_lambda_element.""" + hass = hass_recorder() + instance = recorder.get_instance(hass) + hass.states.set("sensor.on", "on") + new_state = hass.states.get("sensor.on") + wait_recording_done(hass) + now = dt_util.utcnow() + tomorrow = now + timedelta(days=1) + one_week_from_now = now + timedelta(days=7) + + class MockExecutor: + def __init__(self, stmt): + assert isinstance(stmt, StatementLambdaElement) + self.calls = 0 + + def all(self): + self.calls += 1 + if self.calls == 2: + return ["mock_row"] + raise SQLAlchemyError + + with session_scope(hass=hass) as session: + # No time window, we always get a list + stmt = history._get_single_entity_states_stmt( + instance.schema_version, dt_util.utcnow(), "sensor.on", False + ) + rows = util.execute_stmt_lambda_element(session, stmt) + assert isinstance(rows, list) + assert rows[0].state == new_state.state + assert rows[0].entity_id == new_state.entity_id + + # Time window >= 2 days, we get a ChunkedIteratorResult + rows = util.execute_stmt_lambda_element(session, stmt, now, one_week_from_now) + assert isinstance(rows, ChunkedIteratorResult) + row = next(rows) + assert row.state == new_state.state + assert row.entity_id == new_state.entity_id + + # Time window < 2 days, we get a list + rows = util.execute_stmt_lambda_element(session, stmt, now, tomorrow) + assert isinstance(rows, list) + assert rows[0].state == new_state.state + assert rows[0].entity_id == new_state.entity_id + + with patch.object(session, "execute", MockExecutor): + rows = util.execute_stmt_lambda_element(session, stmt, now, tomorrow) + assert rows == ["mock_row"] From 221b77297e2021b4b337594ff644ab5139507764 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Sun, 15 May 2022 23:58:57 +0800 Subject: [PATCH 471/930] Declare exports from stream explicitly (#71898) --- homeassistant/components/camera/__init__.py | 8 ++++++-- homeassistant/components/camera/media_source.py | 2 +- homeassistant/components/generic/camera.py | 2 +- homeassistant/components/generic/config_flow.py | 2 +- homeassistant/components/onvif/__init__.py | 2 +- homeassistant/components/onvif/camera.py | 2 +- homeassistant/components/stream/__init__.py | 15 +++++++++++++++ mypy.ini | 1 + script/hassfest/mypy_config.py | 1 + tests/components/camera/test_media_source.py | 2 +- tests/components/generic/test_config_flow.py | 2 +- tests/components/roku/test_media_player.py | 2 +- 12 files changed, 31 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 57019f7c68d..bff0a07a9be 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -29,8 +29,12 @@ from homeassistant.components.media_player.const import ( DOMAIN as DOMAIN_MP, SERVICE_PLAY_MEDIA, ) -from homeassistant.components.stream import Stream, create_stream -from homeassistant.components.stream.const import FORMAT_CONTENT_TYPE, OUTPUT_FORMATS +from homeassistant.components.stream import ( + FORMAT_CONTENT_TYPE, + OUTPUT_FORMATS, + Stream, + create_stream, +) from homeassistant.components.websocket_api import ActiveConnection from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( diff --git a/homeassistant/components/camera/media_source.py b/homeassistant/components/camera/media_source.py index ffa1962f9ef..733efb3a430 100644 --- a/homeassistant/components/camera/media_source.py +++ b/homeassistant/components/camera/media_source.py @@ -15,7 +15,7 @@ from homeassistant.components.media_source.models import ( MediaSourceItem, PlayMedia, ) -from homeassistant.components.stream.const import FORMAT_CONTENT_TYPE, HLS_PROVIDER +from homeassistant.components.stream import FORMAT_CONTENT_TYPE, HLS_PROVIDER from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_component import EntityComponent diff --git a/homeassistant/components/generic/camera.py b/homeassistant/components/generic/camera.py index 4886e3a0693..edc51430f0d 100644 --- a/homeassistant/components/generic/camera.py +++ b/homeassistant/components/generic/camera.py @@ -12,7 +12,7 @@ from homeassistant.components.camera import ( Camera, CameraEntityFeature, ) -from homeassistant.components.stream.const import ( +from homeassistant.components.stream import ( CONF_RTSP_TRANSPORT, CONF_USE_WALLCLOCK_AS_TIMESTAMPS, RTSP_TRANSPORTS, diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index bf9499c07df..435fdf6f729 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -16,7 +16,7 @@ from httpx import HTTPStatusError, RequestError, TimeoutException import voluptuous as vol import yarl -from homeassistant.components.stream.const import ( +from homeassistant.components.stream import ( CONF_RTSP_TRANSPORT, CONF_USE_WALLCLOCK_AS_TIMESTAMPS, RTSP_TRANSPORTS, diff --git a/homeassistant/components/onvif/__init__.py b/homeassistant/components/onvif/__init__.py index d966549a36f..7922d59ca53 100644 --- a/homeassistant/components/onvif/__init__.py +++ b/homeassistant/components/onvif/__init__.py @@ -2,7 +2,7 @@ from onvif.exceptions import ONVIFAuthError, ONVIFError, ONVIFTimeoutError from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS -from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS +from homeassistant.components.stream import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index 6b61a37eb16..e46e1a2ac59 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -9,7 +9,7 @@ from yarl import URL from homeassistant.components import ffmpeg from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, get_ffmpeg_manager -from homeassistant.components.stream.const import CONF_RTSP_TRANSPORT +from homeassistant.components.stream import CONF_RTSP_TRANSPORT from homeassistant.config_entries import ConfigEntry from homeassistant.const import HTTP_BASIC_AUTHENTICATION from homeassistant.core import HomeAssistant diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index fbdfd97f9b2..95e9afe6c36 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -43,12 +43,15 @@ from .const import ( CONF_SEGMENT_DURATION, CONF_USE_WALLCLOCK_AS_TIMESTAMPS, DOMAIN, + FORMAT_CONTENT_TYPE, HLS_PROVIDER, MAX_SEGMENTS, + OUTPUT_FORMATS, OUTPUT_IDLE_TIMEOUT, RECORDER_PROVIDER, RTSP_TRANSPORTS, SEGMENT_DURATION_ADJUSTER, + SOURCE_TIMEOUT, STREAM_RESTART_INCREMENT, STREAM_RESTART_RESET_TIME, TARGET_SEGMENT_DURATION_NON_LL_HLS, @@ -57,6 +60,18 @@ from .core import PROVIDERS, IdleTimer, KeyFrameConverter, StreamOutput, StreamS from .diagnostics import Diagnostics from .hls import HlsStreamOutput, async_setup_hls +__all__ = [ + "CONF_RTSP_TRANSPORT", + "CONF_USE_WALLCLOCK_AS_TIMESTAMPS", + "FORMAT_CONTENT_TYPE", + "HLS_PROVIDER", + "OUTPUT_FORMATS", + "RTSP_TRANSPORTS", + "SOURCE_TIMEOUT", + "Stream", + "create_stream", +] + _LOGGER = logging.getLogger(__name__) STREAM_SOURCE_REDACT_PATTERN = [ diff --git a/mypy.ini b/mypy.ini index 7784acd9fe6..dc023da8d01 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2017,6 +2017,7 @@ disallow_untyped_defs = true no_implicit_optional = true warn_return_any = true warn_unreachable = true +no_implicit_reexport = true [mypy-homeassistant.components.sun.*] check_untyped_defs = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index d21dc0faf7d..2b7c1e00f94 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -200,6 +200,7 @@ IGNORED_MODULES: Final[list[str]] = [ NO_IMPLICIT_REEXPORT_MODULES: set[str] = { "homeassistant.components", "homeassistant.components.diagnostics.*", + "homeassistant.components.stream.*", } HEADER: Final = """ diff --git a/tests/components/camera/test_media_source.py b/tests/components/camera/test_media_source.py index f684d81a2b1..b7c273bb23a 100644 --- a/tests/components/camera/test_media_source.py +++ b/tests/components/camera/test_media_source.py @@ -5,7 +5,7 @@ import pytest from homeassistant.components import media_source from homeassistant.components.camera.const import StreamType -from homeassistant.components.stream.const import FORMAT_CONTENT_TYPE +from homeassistant.components.stream import FORMAT_CONTENT_TYPE from homeassistant.setup import async_setup_component diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index 82cf41c6e91..b7f3bf73527 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -19,7 +19,7 @@ from homeassistant.components.generic.const import ( CONF_STREAM_SOURCE, DOMAIN, ) -from homeassistant.components.stream.const import ( +from homeassistant.components.stream import ( CONF_RTSP_TRANSPORT, CONF_USE_WALLCLOCK_AS_TIMESTAMPS, ) diff --git a/tests/components/roku/test_media_player.py b/tests/components/roku/test_media_player.py index 21fd2e861b6..c95eda2288a 100644 --- a/tests/components/roku/test_media_player.py +++ b/tests/components/roku/test_media_player.py @@ -52,7 +52,7 @@ from homeassistant.components.roku.const import ( DOMAIN, SERVICE_SEARCH, ) -from homeassistant.components.stream.const import FORMAT_CONTENT_TYPE, HLS_PROVIDER +from homeassistant.components.stream import FORMAT_CONTENT_TYPE, HLS_PROVIDER from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.config import async_process_ha_core_config from homeassistant.const import ( From f2da1fceb2857f209ae61930cf32d45240195126 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 15 May 2022 10:33:46 -0700 Subject: [PATCH 472/930] Bump gcal_sync to 0.8.0 (#71900) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 87069bc463a..fde04d96baf 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.7.1", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.8.0", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 457c2e9ec1d..1c4f2ec0860 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.7.1 +gcal-sync==0.8.0 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dbb54b45643..7e9f5a5011c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -492,7 +492,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.7.1 +gcal-sync==0.8.0 # homeassistant.components.geocaching geocachingapi==0.2.1 From b6c7422607511f4896d7d2e28cca689413903da1 Mon Sep 17 00:00:00 2001 From: moritzbeck01 <48137462+moritzbeck01@users.noreply.github.com> Date: Sun, 15 May 2022 19:37:24 +0200 Subject: [PATCH 473/930] Add timer to the the helper category (#71837) --- homeassistant/components/timer/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/timer/manifest.json b/homeassistant/components/timer/manifest.json index 19748332221..160c96f9664 100644 --- a/homeassistant/components/timer/manifest.json +++ b/homeassistant/components/timer/manifest.json @@ -1,6 +1,7 @@ { "domain": "timer", "name": "Timer", + "integration_type": "helper", "documentation": "https://www.home-assistant.io/integrations/timer", "codeowners": [], "quality_scale": "internal" From 690fb2ae86cfe59907a6e42931165be1bf59ae16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Drobni=C4=8D?= Date: Sun, 15 May 2022 20:31:22 +0200 Subject: [PATCH 474/930] Add back description and location to calendar endpoint (#71887) --- homeassistant/components/calendar/__init__.py | 2 ++ homeassistant/components/demo/calendar.py | 4 ++++ tests/components/caldav/test_calendar.py | 2 ++ tests/components/calendar/test_init.py | 2 ++ tests/components/twentemilieu/test_calendar.py | 2 ++ 5 files changed, 12 insertions(+) diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index 0f122fea55f..432b6943473 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -341,6 +341,8 @@ class CalendarEventView(http.HomeAssistantView): [ { "summary": event.summary, + "description": event.description, + "location": event.location, "start": _get_api_date(event.start), "end": _get_api_date(event.end), } diff --git a/homeassistant/components/demo/calendar.py b/homeassistant/components/demo/calendar.py index 42ec04e42f2..3a8b909bd0c 100644 --- a/homeassistant/components/demo/calendar.py +++ b/homeassistant/components/demo/calendar.py @@ -39,6 +39,8 @@ def calendar_data_future() -> CalendarEvent: start=one_hour_from_now, end=one_hour_from_now + datetime.timedelta(minutes=60), summary="Future Event", + description="Future Description", + location="Future Location", ) @@ -90,6 +92,8 @@ class LegacyDemoCalendar(CalendarEventDevice): ).isoformat() }, "summary": "Future Event", + "description": "Future Description", + "location": "Future Location", } @property diff --git a/tests/components/caldav/test_calendar.py b/tests/components/caldav/test_calendar.py index 2131eebe997..f35fa609e4a 100644 --- a/tests/components/caldav/test_calendar.py +++ b/tests/components/caldav/test_calendar.py @@ -937,5 +937,7 @@ async def test_get_events_custom_calendars(hass, calendar, get_api_events): "end": {"dateTime": "2017-11-27T10:00:00-08:00"}, "start": {"dateTime": "2017-11-27T09:00:00-08:00"}, "summary": "This is a normal event", + "location": "Hamburg", + "description": "Surprisingly rainy", } ] diff --git a/tests/components/calendar/test_init.py b/tests/components/calendar/test_init.py index 26f9320a196..0a91f58b0b2 100644 --- a/tests/components/calendar/test_init.py +++ b/tests/components/calendar/test_init.py @@ -57,3 +57,5 @@ async def test_events_http_api_shim(hass, hass_client): assert response.status == HTTPStatus.OK events = await response.json() assert events[0]["summary"] == "Future Event" + assert events[0]["description"] == "Future Description" + assert events[0]["location"] == "Future Location" diff --git a/tests/components/twentemilieu/test_calendar.py b/tests/components/twentemilieu/test_calendar.py index 0a0f32be212..b9f1dd9247d 100644 --- a/tests/components/twentemilieu/test_calendar.py +++ b/tests/components/twentemilieu/test_calendar.py @@ -79,4 +79,6 @@ async def test_api_events( "start": {"date": "2022-01-06"}, "end": {"date": "2022-01-06"}, "summary": "Christmas Tree Pickup", + "description": None, + "location": None, } From 904be03d72b78c043d553ab02d6392b571f442c7 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 15 May 2022 20:36:57 +0200 Subject: [PATCH 475/930] Revert changing `pysnmp` to `pysnmplib` (#71901) --- homeassistant/components/brother/manifest.json | 2 +- homeassistant/components/snmp/manifest.json | 2 +- requirements_all.txt | 4 ++-- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/brother/manifest.json b/homeassistant/components/brother/manifest.json index c230373ea36..aaf1af72db9 100644 --- a/homeassistant/components/brother/manifest.json +++ b/homeassistant/components/brother/manifest.json @@ -3,7 +3,7 @@ "name": "Brother Printer", "documentation": "https://www.home-assistant.io/integrations/brother", "codeowners": ["@bieniu"], - "requirements": ["brother==1.2.0"], + "requirements": ["brother==1.1.0"], "zeroconf": [ { "type": "_printer._tcp.local.", diff --git a/homeassistant/components/snmp/manifest.json b/homeassistant/components/snmp/manifest.json index ef0213e82dc..76df9e18606 100644 --- a/homeassistant/components/snmp/manifest.json +++ b/homeassistant/components/snmp/manifest.json @@ -2,7 +2,7 @@ "domain": "snmp", "name": "SNMP", "documentation": "https://www.home-assistant.io/integrations/snmp", - "requirements": ["pysnmplib==5.0.10"], + "requirements": ["pysnmp==4.4.12"], "codeowners": [], "iot_class": "local_polling", "loggers": ["pyasn1", "pysmi", "pysnmp"] diff --git a/requirements_all.txt b/requirements_all.txt index 1c4f2ec0860..5b35a595083 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -436,7 +436,7 @@ bravia-tv==1.0.11 broadlink==0.18.1 # homeassistant.components.brother -brother==1.2.0 +brother==1.1.0 # homeassistant.components.brottsplatskartan brottsplatskartan==0.0.1 @@ -1832,7 +1832,7 @@ pysmarty==0.8 pysml==0.0.7 # homeassistant.components.snmp -pysnmplib==5.0.10 +pysnmp==4.4.12 # homeassistant.components.soma pysoma==0.0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7e9f5a5011c..888cd30a056 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -333,7 +333,7 @@ bravia-tv==1.0.11 broadlink==0.18.1 # homeassistant.components.brother -brother==1.2.0 +brother==1.1.0 # homeassistant.components.brunt brunt==1.2.0 From 73f2f2ad1ba6cb86ed4961009e2277301274c529 Mon Sep 17 00:00:00 2001 From: moritzbeck01 <48137462+moritzbeck01@users.noreply.github.com> Date: Sun, 15 May 2022 21:19:18 +0200 Subject: [PATCH 476/930] Add counter to the the helper category (#71838) --- homeassistant/components/counter/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/counter/manifest.json b/homeassistant/components/counter/manifest.json index ab1a4bf0438..6db4a0a7a97 100644 --- a/homeassistant/components/counter/manifest.json +++ b/homeassistant/components/counter/manifest.json @@ -1,6 +1,7 @@ { "domain": "counter", "name": "Counter", + "integration_type": "helper", "documentation": "https://www.home-assistant.io/integrations/counter", "codeowners": ["@fabaff"], "quality_scale": "internal" From 37f81b261dcfdf78951ad3a51957c93e84394509 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 16 May 2022 00:24:28 +0000 Subject: [PATCH 477/930] [ci skip] Translation update --- .../components/adax/translations/sk.json | 7 + .../components/adguard/translations/es.json | 6 +- .../components/airthings/translations/es.json | 2 +- .../components/airvisual/translations/es.json | 4 +- .../airvisual/translations/sensor.sk.json | 7 + .../components/airzone/translations/es.json | 20 +++ .../aladdin_connect/translations/ca.json | 1 + .../aladdin_connect/translations/es.json | 26 ++++ .../aladdin_connect/translations/it.json | 27 ++++ .../aladdin_connect/translations/sk.json | 11 ++ .../aladdin_connect/translations/tr.json | 27 ++++ .../alarm_control_panel/translations/es.json | 2 +- .../components/almond/translations/es.json | 4 +- .../components/apple_tv/translations/es.json | 4 +- .../translations/es.json | 3 + .../aseko_pool_live/translations/es.json | 2 +- .../components/asuswrt/translations/es.json | 3 +- .../components/atag/translations/sk.json | 3 + .../components/august/translations/es.json | 2 +- .../aussie_broadband/translations/es.json | 23 ++- .../aussie_broadband/translations/sk.json | 10 ++ .../components/awair/translations/es.json | 2 +- .../azure_devops/translations/es.json | 4 +- .../azure_event_hub/translations/sk.json | 7 + .../components/baf/translations/ca.json | 23 +++ .../components/baf/translations/es.json | 23 +++ .../components/baf/translations/et.json | 23 +++ .../components/baf/translations/fr.json | 23 +++ .../components/baf/translations/it.json | 23 +++ .../components/baf/translations/pt-BR.json | 23 +++ .../components/baf/translations/sk.json | 19 +++ .../components/baf/translations/tr.json | 23 +++ .../binary_sensor/translations/es.json | 7 +- .../binary_sensor/translations/sk.json | 4 + .../components/blebox/translations/es.json | 2 +- .../components/braviatv/translations/es.json | 2 +- .../components/brother/translations/es.json | 2 +- .../components/brunt/translations/es.json | 2 +- .../components/brunt/translations/sk.json | 7 + .../components/bsblan/translations/es.json | 2 +- .../components/button/translations/sk.json | 7 + .../components/cast/translations/es.json | 2 +- .../components/climacell/translations/es.json | 2 +- .../climacell/translations/sensor.sk.json | 7 + .../components/climate/translations/es.json | 2 +- .../components/coinbase/translations/es.json | 4 +- .../components/coinbase/translations/sk.json | 5 + .../configurator/translations/es.json | 2 +- .../components/cover/translations/es.json | 2 +- .../crownstone/translations/es.json | 2 +- .../components/deconz/translations/es.json | 5 +- .../components/deluge/translations/es.json | 19 +++ .../components/deluge/translations/sk.json | 12 ++ .../components/denonavr/translations/es.json | 2 +- .../derivative/translations/es.json | 43 ++++++ .../derivative/translations/sk.json | 11 ++ .../device_tracker/translations/es.json | 4 +- .../devolo_home_control/translations/es.json | 2 +- .../components/dexcom/translations/es.json | 2 +- .../dialogflow/translations/es.json | 1 + .../components/directv/translations/es.json | 2 +- .../components/discord/translations/es.json | 23 +++ .../components/dlna_dmr/translations/es.json | 1 + .../components/dlna_dms/translations/es.json | 5 + .../components/elgato/translations/es.json | 8 +- .../components/elkm1/translations/es.json | 5 +- .../emulated_roku/translations/es.json | 2 +- .../components/esphome/translations/es.json | 2 +- .../components/ezviz/translations/es.json | 2 +- .../faa_delays/translations/sk.json | 7 + .../components/fan/translations/es.json | 1 + .../components/fan/translations/tr.json | 1 + .../components/fibaro/translations/es.json | 16 ++ .../components/fibaro/translations/sk.json | 11 ++ .../components/filesize/translations/es.json | 7 + .../fireservicerota/translations/es.json | 2 +- .../components/fivem/translations/es.json | 2 +- .../flick_electric/translations/es.json | 2 +- .../flunearyou/translations/es.json | 2 +- .../forecast_solar/translations/es.json | 1 + .../forked_daapd/translations/es.json | 4 +- .../components/freebox/translations/es.json | 4 +- .../components/fritz/translations/es.json | 1 + .../components/fritzbox/translations/es.json | 1 + .../fritzbox_callmonitor/translations/es.json | 2 +- .../components/generic/translations/es.json | 68 +++++++++ .../components/generic/translations/tr.json | 4 + .../geocaching/translations/es.json | 24 +++ .../geocaching/translations/tr.json | 25 ++++ .../components/github/translations/es.json | 16 ++ .../components/goalzero/translations/es.json | 2 +- .../components/google/translations/es.json | 10 +- .../components/gpslogger/translations/es.json | 1 + .../components/group/translations/es.json | 141 +++++++++++++++++- .../components/group/translations/sk.json | 18 +++ .../components/guardian/translations/es.json | 2 +- .../components/hangouts/translations/es.json | 6 +- .../components/harmony/translations/es.json | 4 +- .../hisense_aehw4a1/translations/es.json | 4 +- .../components/hive/translations/es.json | 2 +- .../home_connect/translations/es.json | 2 +- .../home_plus_control/translations/es.json | 4 +- .../homeassistant/translations/es.json | 2 +- .../components/homekit/translations/es.json | 20 ++- .../homekit_controller/translations/es.json | 4 +- .../translations/select.es.json | 8 + .../homekit_controller/translations/sk.json | 5 + .../homematicip_cloud/translations/es.json | 2 +- .../homewizard/translations/es.json | 2 +- .../components/honeywell/translations/es.json | 9 ++ .../components/honeywell/translations/sk.json | 7 + .../huawei_lte/translations/es.json | 4 +- .../components/hue/translations/es.json | 5 +- .../components/hue/translations/sk.json | 5 + .../components/hyperion/translations/es.json | 2 +- .../components/iaqualink/translations/es.json | 4 +- .../components/icloud/translations/es.json | 4 +- .../input_datetime/translations/es.json | 2 +- .../input_number/translations/es.json | 2 +- .../input_number/translations/tr.json | 2 +- .../components/insteon/translations/es.json | 7 +- .../components/insteon/translations/sk.json | 3 + .../integration/translations/es.json | 36 +++++ .../integration/translations/sk.json | 20 +++ .../intellifire/translations/es.json | 43 ++++++ .../components/ios/translations/es.json | 4 +- .../components/iss/translations/es.json | 3 +- .../components/isy994/translations/es.json | 10 +- .../components/jellyfin/translations/sk.json | 7 + .../components/juicenet/translations/es.json | 2 +- .../kaleidescape/translations/es.json | 20 +++ .../components/knx/translations/es.json | 56 +++++-- .../components/knx/translations/sk.json | 10 ++ .../components/konnected/translations/es.json | 6 +- .../components/kraken/translations/sk.json | 11 ++ .../components/life360/translations/es.json | 2 +- .../components/lifx/translations/es.json | 4 +- .../litterrobot/translations/sensor.ca.json | 21 ++- .../litterrobot/translations/sensor.es.json | 14 ++ .../litterrobot/translations/sensor.it.json | 28 ++++ .../litterrobot/translations/sensor.sk.json | 8 + .../litterrobot/translations/sensor.tr.json | 28 ++++ .../components/locative/translations/es.json | 1 + .../logi_circle/translations/es.json | 4 +- .../components/luftdaten/translations/es.json | 2 +- .../lutron_caseta/translations/es.json | 4 +- .../components/lyric/translations/es.json | 2 +- .../components/mailgun/translations/es.json | 1 + .../components/mazda/translations/es.json | 2 +- .../components/meater/translations/es.json | 19 +++ .../components/meater/translations/sk.json | 7 + .../media_player/translations/es.json | 5 +- .../components/melcloud/translations/es.json | 2 +- .../meteo_france/translations/es.json | 2 +- .../components/metoffice/translations/es.json | 2 +- .../components/mill/translations/es.json | 2 +- .../components/min_max/translations/es.json | 39 +++++ .../moehlenhoff_alpha2/translations/es.json | 2 +- .../motion_blinds/translations/tr.json | 2 +- .../components/myq/translations/es.json | 2 +- .../components/nam/translations/es.json | 2 +- .../components/nanoleaf/translations/es.json | 5 + .../components/nanoleaf/translations/sk.json | 5 + .../components/neato/translations/es.json | 2 +- .../components/nest/translations/es.json | 4 +- .../components/nest/translations/sk.json | 3 + .../components/netatmo/translations/es.json | 6 +- .../components/netatmo/translations/tr.json | 2 +- .../components/netgear/translations/es.json | 2 +- .../components/nexia/translations/es.json | 2 +- .../nmap_tracker/translations/es.json | 4 +- .../components/notion/translations/es.json | 2 +- .../components/nzbget/translations/es.json | 2 +- .../components/oncue/translations/sk.json | 3 +- .../ondilo_ico/translations/es.json | 2 +- .../components/onewire/translations/es.json | 19 +++ .../components/onewire/translations/tr.json | 4 +- .../opentherm_gw/translations/es.json | 2 +- .../components/openuv/translations/es.json | 2 +- .../openweathermap/translations/es.json | 4 +- .../components/overkiz/translations/es.json | 5 +- .../overkiz/translations/select.sk.json | 7 + .../overkiz/translations/sensor.sk.json | 10 ++ .../components/overkiz/translations/sk.json | 7 + .../ovo_energy/translations/es.json | 4 +- .../components/owntracks/translations/es.json | 1 + .../components/peco/translations/es.json | 14 ++ .../components/person/translations/es.json | 2 +- .../components/plaato/translations/es.json | 9 +- .../components/plex/translations/es.json | 4 +- .../components/plugwise/translations/es.json | 6 +- .../plum_lightpad/translations/es.json | 2 +- .../components/point/translations/es.json | 2 +- .../components/poolsense/translations/es.json | 2 +- .../components/powerwall/translations/es.json | 3 + .../components/ps4/translations/es.json | 2 +- .../pure_energie/translations/es.json | 2 +- .../pvpc_hourly_pricing/translations/es.json | 4 +- .../components/qnap_qsw/translations/es.json | 21 +++ .../components/rachio/translations/es.json | 6 +- .../radio_browser/translations/es.json | 5 + .../rainmachine/translations/es.json | 2 +- .../components/recorder/translations/es.json | 8 + .../components/recorder/translations/tr.json | 1 + .../components/renault/translations/es.json | 2 +- .../components/rfxtrx/translations/es.json | 1 + .../components/risco/translations/es.json | 2 +- .../components/risco/translations/sk.json | 9 ++ .../components/roku/translations/es.json | 2 +- .../components/roomba/translations/es.json | 4 +- .../components/roon/translations/es.json | 9 +- .../ruckus_unleashed/translations/sk.json | 7 + .../components/sabnzbd/translations/es.json | 18 +++ .../components/sabnzbd/translations/sk.json | 11 ++ .../components/samsungtv/translations/es.json | 9 +- .../season/translations/sensor.sk.json | 7 + .../components/sense/translations/es.json | 3 +- .../components/senseme/translations/sk.json | 7 + .../components/sensibo/translations/es.json | 11 +- .../components/sensibo/translations/sk.json | 3 + .../components/sensor/translations/sk.json | 9 ++ .../components/senz/translations/es.json | 20 +++ .../components/shelly/translations/es.json | 2 +- .../components/sia/translations/sk.json | 3 +- .../simplisafe/translations/es.json | 14 +- .../components/slack/translations/ca.json | 9 +- .../components/slack/translations/es.json | 29 ++++ .../components/slack/translations/it.json | 29 ++++ .../components/slack/translations/sk.json | 20 +++ .../components/slack/translations/tr.json | 29 ++++ .../components/sleepiq/translations/es.json | 6 +- .../components/slimproto/translations/es.json | 7 + .../components/slimproto/translations/tr.json | 6 + .../components/smappee/translations/es.json | 2 +- .../components/smarttub/translations/es.json | 2 +- .../components/smhi/translations/es.json | 3 + .../components/sms/translations/es.json | 1 + .../components/sms/translations/tr.json | 1 + .../components/solax/translations/es.json | 8 + .../components/soma/translations/es.json | 4 +- .../components/somfy/translations/es.json | 4 +- .../components/sonarr/translations/es.json | 2 +- .../components/sonos/translations/es.json | 2 +- .../speedtestdotnet/translations/es.json | 2 +- .../components/sql/translations/es.json | 55 +++++++ .../steam_online/translations/es.json | 34 +++++ .../components/subaru/translations/es.json | 18 ++- .../surepetcare/translations/es.json | 2 +- .../surepetcare/translations/sk.json | 7 + .../components/switch/translations/es.json | 7 + .../switch_as_x/translations/es.json | 13 ++ .../components/syncthru/translations/es.json | 2 +- .../synology_dsm/translations/es.json | 3 +- .../synology_dsm/translations/sk.json | 6 + .../system_bridge/translations/sk.json | 3 +- .../components/tado/translations/es.json | 2 +- .../tankerkoenig/translations/es.json | 33 ++++ .../tankerkoenig/translations/sk.json | 11 ++ .../components/tautulli/translations/es.json | 25 ++++ .../tellduslive/translations/es.json | 2 +- .../components/threshold/translations/es.json | 24 +++ .../components/tile/translations/es.json | 2 +- .../components/tod/translations/es.json | 25 ++++ .../tomorrowio/translations/es.json | 29 ++++ .../tomorrowio/translations/sensor.es.json | 16 ++ .../tomorrowio/translations/sensor.sk.json | 7 + .../tomorrowio/translations/sk.json | 12 ++ .../components/toon/translations/es.json | 2 +- .../totalconnect/translations/es.json | 2 +- .../components/traccar/translations/es.json | 6 +- .../components/tradfri/translations/es.json | 4 +- .../trafikverket_ferry/translations/es.json | 26 ++++ .../trafikverket_train/translations/es.json | 17 +++ .../components/tuya/translations/es.json | 6 +- .../tuya/translations/select.es.json | 9 ++ .../tuya/translations/select.sk.json | 18 +++ .../tuya/translations/sensor.sk.json | 7 + .../twentemilieu/translations/es.json | 4 +- .../components/twilio/translations/es.json | 3 +- .../components/twinkly/translations/es.json | 2 +- .../ukraine_alarm/translations/es.json | 27 +++- .../ukraine_alarm/translations/tr.json | 45 ++++++ .../components/unifi/translations/es.json | 11 +- .../unifiprotect/translations/es.json | 9 +- .../components/upb/translations/sk.json | 7 + .../components/update/translations/es.json | 7 + .../components/upnp/translations/es.json | 2 +- .../uptimerobot/translations/es.json | 3 +- .../uptimerobot/translations/sensor.es.json | 9 ++ .../utility_meter/translations/es.json | 29 ++++ .../components/velbus/translations/es.json | 4 +- .../components/verisure/translations/es.json | 2 +- .../components/vulcan/translations/es.json | 51 +++++++ .../components/vulcan/translations/sk.json | 9 ++ .../components/weather/translations/es.json | 2 +- .../components/weather/translations/sk.json | 2 +- .../components/webostv/translations/es.json | 5 + .../components/wemo/translations/es.json | 4 +- .../components/whois/translations/es.json | 4 + .../components/whois/translations/sk.json | 11 ++ .../components/wiffi/translations/es.json | 2 +- .../components/wilight/translations/es.json | 4 +- .../components/withings/translations/es.json | 8 +- .../components/wiz/translations/sk.json | 3 + .../components/wled/translations/es.json | 2 +- .../components/ws66i/translations/es.json | 34 +++++ .../components/ws66i/translations/tr.json | 34 +++++ .../components/xbox/translations/es.json | 2 +- .../xiaomi_aqara/translations/es.json | 4 +- .../xiaomi_miio/translations/es.json | 6 +- .../xiaomi_miio/translations/select.sk.json | 7 + .../yale_smart_alarm/translations/es.json | 3 +- .../yale_smart_alarm/translations/sk.json | 3 +- .../yamaha_musiccast/translations/sk.json | 7 + .../components/zha/translations/es.json | 2 +- .../components/zwave_js/translations/es.json | 4 + .../components/zwave_me/translations/es.json | 2 +- 317 files changed, 2608 insertions(+), 261 deletions(-) create mode 100644 homeassistant/components/airvisual/translations/sensor.sk.json create mode 100644 homeassistant/components/airzone/translations/es.json create mode 100644 homeassistant/components/aladdin_connect/translations/es.json create mode 100644 homeassistant/components/aladdin_connect/translations/it.json create mode 100644 homeassistant/components/aladdin_connect/translations/sk.json create mode 100644 homeassistant/components/aladdin_connect/translations/tr.json create mode 100644 homeassistant/components/application_credentials/translations/es.json create mode 100644 homeassistant/components/azure_event_hub/translations/sk.json create mode 100644 homeassistant/components/baf/translations/ca.json create mode 100644 homeassistant/components/baf/translations/es.json create mode 100644 homeassistant/components/baf/translations/et.json create mode 100644 homeassistant/components/baf/translations/fr.json create mode 100644 homeassistant/components/baf/translations/it.json create mode 100644 homeassistant/components/baf/translations/pt-BR.json create mode 100644 homeassistant/components/baf/translations/sk.json create mode 100644 homeassistant/components/baf/translations/tr.json create mode 100644 homeassistant/components/button/translations/sk.json create mode 100644 homeassistant/components/climacell/translations/sensor.sk.json create mode 100644 homeassistant/components/deluge/translations/es.json create mode 100644 homeassistant/components/deluge/translations/sk.json create mode 100644 homeassistant/components/derivative/translations/es.json create mode 100644 homeassistant/components/derivative/translations/sk.json create mode 100644 homeassistant/components/discord/translations/es.json create mode 100644 homeassistant/components/faa_delays/translations/sk.json create mode 100644 homeassistant/components/fibaro/translations/es.json create mode 100644 homeassistant/components/fibaro/translations/sk.json create mode 100644 homeassistant/components/filesize/translations/es.json create mode 100644 homeassistant/components/generic/translations/es.json create mode 100644 homeassistant/components/geocaching/translations/es.json create mode 100644 homeassistant/components/geocaching/translations/tr.json create mode 100644 homeassistant/components/github/translations/es.json create mode 100644 homeassistant/components/homekit_controller/translations/select.es.json create mode 100644 homeassistant/components/integration/translations/es.json create mode 100644 homeassistant/components/integration/translations/sk.json create mode 100644 homeassistant/components/intellifire/translations/es.json create mode 100644 homeassistant/components/kaleidescape/translations/es.json create mode 100644 homeassistant/components/kraken/translations/sk.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.es.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.it.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.sk.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.tr.json create mode 100644 homeassistant/components/meater/translations/es.json create mode 100644 homeassistant/components/meater/translations/sk.json create mode 100644 homeassistant/components/min_max/translations/es.json create mode 100644 homeassistant/components/overkiz/translations/select.sk.json create mode 100644 homeassistant/components/overkiz/translations/sensor.sk.json create mode 100644 homeassistant/components/peco/translations/es.json create mode 100644 homeassistant/components/qnap_qsw/translations/es.json create mode 100644 homeassistant/components/recorder/translations/es.json create mode 100644 homeassistant/components/sabnzbd/translations/es.json create mode 100644 homeassistant/components/sabnzbd/translations/sk.json create mode 100644 homeassistant/components/season/translations/sensor.sk.json create mode 100644 homeassistant/components/senseme/translations/sk.json create mode 100644 homeassistant/components/senz/translations/es.json create mode 100644 homeassistant/components/slack/translations/es.json create mode 100644 homeassistant/components/slack/translations/it.json create mode 100644 homeassistant/components/slack/translations/sk.json create mode 100644 homeassistant/components/slack/translations/tr.json create mode 100644 homeassistant/components/slimproto/translations/es.json create mode 100644 homeassistant/components/sql/translations/es.json create mode 100644 homeassistant/components/steam_online/translations/es.json create mode 100644 homeassistant/components/switch_as_x/translations/es.json create mode 100644 homeassistant/components/tankerkoenig/translations/es.json create mode 100644 homeassistant/components/tankerkoenig/translations/sk.json create mode 100644 homeassistant/components/tautulli/translations/es.json create mode 100644 homeassistant/components/threshold/translations/es.json create mode 100644 homeassistant/components/tod/translations/es.json create mode 100644 homeassistant/components/tomorrowio/translations/es.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.es.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.sk.json create mode 100644 homeassistant/components/tomorrowio/translations/sk.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/es.json create mode 100644 homeassistant/components/trafikverket_train/translations/es.json create mode 100644 homeassistant/components/tuya/translations/select.sk.json create mode 100644 homeassistant/components/tuya/translations/sensor.sk.json create mode 100644 homeassistant/components/ukraine_alarm/translations/tr.json create mode 100644 homeassistant/components/upb/translations/sk.json create mode 100644 homeassistant/components/uptimerobot/translations/sensor.es.json create mode 100644 homeassistant/components/utility_meter/translations/es.json create mode 100644 homeassistant/components/vulcan/translations/es.json create mode 100644 homeassistant/components/vulcan/translations/sk.json create mode 100644 homeassistant/components/whois/translations/sk.json create mode 100644 homeassistant/components/ws66i/translations/es.json create mode 100644 homeassistant/components/ws66i/translations/tr.json create mode 100644 homeassistant/components/xiaomi_miio/translations/select.sk.json create mode 100644 homeassistant/components/yamaha_musiccast/translations/sk.json diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index 2c3ed1dd930..8bacfc145e2 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/adguard/translations/es.json b/homeassistant/components/adguard/translations/es.json index 5750808ab76..7fc2f972fa1 100644 --- a/homeassistant/components/adguard/translations/es.json +++ b/homeassistant/components/adguard/translations/es.json @@ -10,16 +10,16 @@ "step": { "hassio_confirm": { "description": "\u00bfDesea configurar Home Assistant para conectarse al AdGuard Home proporcionado por el complemento Supervisor: {addon} ?", - "title": "AdGuard Home a trav\u00e9s del complemento Supervisor" + "title": "AdGuard Home v\u00eda complemento de Home Assistant" }, "user": { "data": { "host": "Host", "password": "Contrase\u00f1a", "port": "Puerto", - "ssl": "AdGuard Home utiliza un certificado SSL", + "ssl": "Utiliza un certificado SSL", "username": "Usuario", - "verify_ssl": "AdGuard Home utiliza un certificado apropiado" + "verify_ssl": "Verificar certificado SSL" }, "description": "Configure su instancia de AdGuard Home para permitir la supervisi\u00f3n y el control." } diff --git a/homeassistant/components/airthings/translations/es.json b/homeassistant/components/airthings/translations/es.json index 55dd0c10026..5feddd20875 100644 --- a/homeassistant/components/airthings/translations/es.json +++ b/homeassistant/components/airthings/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/airvisual/translations/es.json b/homeassistant/components/airvisual/translations/es.json index 6e7ea6e6903..b51d3035fe5 100644 --- a/homeassistant/components/airvisual/translations/es.json +++ b/homeassistant/components/airvisual/translations/es.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "No se pudo conectar", "general_error": "Se ha producido un error desconocido.", - "invalid_api_key": "Se proporciona una clave API no v\u00e1lida.", + "invalid_api_key": "Clave API no v\u00e1lida", "location_not_found": "Ubicaci\u00f3n no encontrada" }, "step": { @@ -32,7 +32,7 @@ }, "node_pro": { "data": { - "ip_address": "Direcci\u00f3n IP/Nombre de host de la Unidad", + "ip_address": "Host", "password": "Contrase\u00f1a" }, "description": "Monitorizar una unidad personal AirVisual. La contrase\u00f1a puede ser recuperada desde la interfaz de la unidad.", diff --git a/homeassistant/components/airvisual/translations/sensor.sk.json b/homeassistant/components/airvisual/translations/sensor.sk.json new file mode 100644 index 00000000000..6d640d965b9 --- /dev/null +++ b/homeassistant/components/airvisual/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "airvisual__pollutant_level": { + "good": "Dobr\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/airzone/translations/es.json b/homeassistant/components/airzone/translations/es.json new file mode 100644 index 00000000000..fbb00d71bf0 --- /dev/null +++ b/homeassistant/components/airzone/translations/es.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_system_id": "ID de sistema Airzone inv\u00e1lido" + }, + "step": { + "user": { + "data": { + "host": "Host", + "port": "Puerto" + }, + "description": "Configura la integraci\u00f3n Airzone." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/ca.json b/homeassistant/components/aladdin_connect/translations/ca.json index c7115a32099..41cb5c550fa 100644 --- a/homeassistant/components/aladdin_connect/translations/ca.json +++ b/homeassistant/components/aladdin_connect/translations/ca.json @@ -13,6 +13,7 @@ "data": { "password": "Contrasenya" }, + "description": "La integraci\u00f3 d'Aladdin Connect ha de tornar a autenticar-se amb el teu compte", "title": "Reautenticaci\u00f3 de la integraci\u00f3" }, "user": { diff --git a/homeassistant/components/aladdin_connect/translations/es.json b/homeassistant/components/aladdin_connect/translations/es.json new file mode 100644 index 00000000000..67e509e2626 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/es.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "reauth_successful": "La reautenticaci\u00f3n fue exitosa" + }, + "error": { + "cannot_connect": "Error al conectar", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Contrase\u00f1a" + }, + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" + }, + "user": { + "data": { + "password": "Contrase\u00f1a", + "username": "Nombre de usuario" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/it.json b/homeassistant/components/aladdin_connect/translations/it.json new file mode 100644 index 00000000000..997d7f36e12 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/it.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Password" + }, + "description": "L'integrazione Aladdin Connect deve autenticare nuovamente il tuo account", + "title": "Autentica nuovamente l'integrazione" + }, + "user": { + "data": { + "password": "Password", + "username": "Nome utente" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/sk.json b/homeassistant/components/aladdin_connect/translations/sk.json new file mode 100644 index 00000000000..c9a7b4c204a --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/tr.json b/homeassistant/components/aladdin_connect/translations/tr.json new file mode 100644 index 00000000000..21e063e21ee --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/tr.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Parola" + }, + "description": "Aladdin Connect entegrasyonunun hesab\u0131n\u0131z\u0131 yeniden do\u011frulamas\u0131 gerekiyor", + "title": "Entegrasyonu Yeniden Do\u011frula" + }, + "user": { + "data": { + "password": "Parola", + "username": "Kullan\u0131c\u0131 Ad\u0131" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/alarm_control_panel/translations/es.json b/homeassistant/components/alarm_control_panel/translations/es.json index a76c6bd5af9..6255f8cc574 100644 --- a/homeassistant/components/alarm_control_panel/translations/es.json +++ b/homeassistant/components/alarm_control_panel/translations/es.json @@ -40,5 +40,5 @@ "triggered": "Disparada" } }, - "title": "Panel de control de alarmas" + "title": "Panel de control de alarma" } \ No newline at end of file diff --git a/homeassistant/components/almond/translations/es.json b/homeassistant/components/almond/translations/es.json index 4dc5e4ee1c0..83e78317741 100644 --- a/homeassistant/components/almond/translations/es.json +++ b/homeassistant/components/almond/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "cannot_connect": "No se puede conectar al servidor Almond.", - "missing_configuration": "Consulte la documentaci\u00f3n sobre c\u00f3mo configurar Almond.", + "cannot_connect": "No se pudo conectar", + "missing_configuration": "El componente no est\u00e1 configurado. Mira su documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, diff --git a/homeassistant/components/apple_tv/translations/es.json b/homeassistant/components/apple_tv/translations/es.json index 65f096ca6ae..3f22edd8d77 100644 --- a/homeassistant/components/apple_tv/translations/es.json +++ b/homeassistant/components/apple_tv/translations/es.json @@ -21,7 +21,7 @@ "no_usable_service": "Se encontr\u00f3 un dispositivo, pero no se pudo identificar ninguna manera de establecer una conexi\u00f3n con \u00e9l. Si sigues viendo este mensaje, intenta especificar su direcci\u00f3n IP o reiniciar el Apple TV.", "unknown": "Error inesperado" }, - "flow_title": "Apple TV: {name}", + "flow_title": "{name} ({type})", "step": { "confirm": { "description": "Est\u00e1s a punto de a\u00f1adir `{name}` con el tipo `{type}` en Home Assistant.\n\n**Para completar el proceso, puede que tengas que introducir varios c\u00f3digos PIN.**\n\nTen en cuenta que *no* podr\u00e1s apagar tu Apple TV con esta integraci\u00f3n. \u00a1S\u00f3lo se apagar\u00e1 el reproductor de medios de Home Assistant!", @@ -47,7 +47,7 @@ "title": "No es posible el emparejamiento" }, "reconfigure": { - "description": "Este Apple TV est\u00e1 experimentando algunos problemas de conexi\u00f3n y debe ser reconfigurado.", + "description": "Vuelve a configurar este dispositivo para restablecer su funcionamiento.", "title": "Reconfiguraci\u00f3n del dispositivo" }, "service_problem": { diff --git a/homeassistant/components/application_credentials/translations/es.json b/homeassistant/components/application_credentials/translations/es.json new file mode 100644 index 00000000000..bf7462ead66 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/es.json @@ -0,0 +1,3 @@ +{ + "title": "Credenciales de la aplicaci\u00f3n" +} \ No newline at end of file diff --git a/homeassistant/components/aseko_pool_live/translations/es.json b/homeassistant/components/aseko_pool_live/translations/es.json index 419359a942c..79c199d012d 100644 --- a/homeassistant/components/aseko_pool_live/translations/es.json +++ b/homeassistant/components/aseko_pool_live/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/asuswrt/translations/es.json b/homeassistant/components/asuswrt/translations/es.json index c5792babf00..8a7876bb45b 100644 --- a/homeassistant/components/asuswrt/translations/es.json +++ b/homeassistant/components/asuswrt/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "invalid_unique_id": "No se pudo determinar ning\u00fan identificador \u00fanico v\u00e1lido del dispositivo", "single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "error": { @@ -18,7 +19,7 @@ "mode": "Modo", "name": "Nombre", "password": "Contrase\u00f1a", - "port": "Puerto", + "port": "Puerto (d\u00e9jalo vac\u00edo por el predeterminado del protocolo)", "protocol": "Protocolo de comunicaci\u00f3n a utilizar", "ssh_key": "Ruta de acceso a su archivo de clave SSH (en lugar de la contrase\u00f1a)", "username": "Nombre de usuario" diff --git a/homeassistant/components/atag/translations/sk.json b/homeassistant/components/atag/translations/sk.json index 892b8b2cd91..5b7f75998d7 100644 --- a/homeassistant/components/atag/translations/sk.json +++ b/homeassistant/components/atag/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/august/translations/es.json b/homeassistant/components/august/translations/es.json index a660d11f996..cd50020bb2a 100644 --- a/homeassistant/components/august/translations/es.json +++ b/homeassistant/components/august/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/aussie_broadband/translations/es.json b/homeassistant/components/aussie_broadband/translations/es.json index ff13c88c598..e0df929fe9e 100644 --- a/homeassistant/components/aussie_broadband/translations/es.json +++ b/homeassistant/components/aussie_broadband/translations/es.json @@ -1,11 +1,17 @@ { "config": { "abort": { - "no_services_found": "No se han encontrado servicios para esta cuenta" + "no_services_found": "No se han encontrado servicios para esta cuenta", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "step": { "reauth": { - "description": "Actualizar la contrase\u00f1a de {username}" + "description": "Actualizar la contrase\u00f1a de {username}", + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" }, "reauth_confirm": { "data": { @@ -19,6 +25,19 @@ "services": "Servicios" }, "title": "Seleccionar Servicios" + }, + "user": { + "data": { + "password": "Contrase\u00f1a", + "username": "Usuario" + } + } + } + }, + "options": { + "step": { + "init": { + "title": "Selecciona servicios" } } } diff --git a/homeassistant/components/aussie_broadband/translations/sk.json b/homeassistant/components/aussie_broadband/translations/sk.json index a8cf7db8bbf..2d028b5f36c 100644 --- a/homeassistant/components/aussie_broadband/translations/sk.json +++ b/homeassistant/components/aussie_broadband/translations/sk.json @@ -5,11 +5,21 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "service": { + "title": "Vyberte slu\u017eby" + } } }, "options": { "abort": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "init": { + "title": "Vyberte slu\u017eby" + } } } } \ No newline at end of file diff --git a/homeassistant/components/awair/translations/es.json b/homeassistant/components/awair/translations/es.json index e87ce031b95..1a3240d3802 100644 --- a/homeassistant/components/awair/translations/es.json +++ b/homeassistant/components/awair/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "no_devices_found": "No se encontraron dispositivos en la red", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, diff --git a/homeassistant/components/azure_devops/translations/es.json b/homeassistant/components/azure_devops/translations/es.json index 1055fdebf41..c71fc22b1bd 100644 --- a/homeassistant/components/azure_devops/translations/es.json +++ b/homeassistant/components/azure_devops/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", - "reauth_successful": "Token de acceso actualizado correctamente " + "already_configured": "La cuenta ya est\u00e1 configurada", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/azure_event_hub/translations/sk.json b/homeassistant/components/azure_event_hub/translations/sk.json new file mode 100644 index 00000000000..3f20d345b26 --- /dev/null +++ b/homeassistant/components/azure_event_hub/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/ca.json b/homeassistant/components/baf/translations/ca.json new file mode 100644 index 00000000000..f471d071017 --- /dev/null +++ b/homeassistant/components/baf/translations/ca.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat", + "ipv6_not_supported": "IPv6 no est\u00e0 suportat." + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "unknown": "Error inesperat" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Vols configurar {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "Adre\u00e7a IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/es.json b/homeassistant/components/baf/translations/es.json new file mode 100644 index 00000000000..4e2800090d2 --- /dev/null +++ b/homeassistant/components/baf/translations/es.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "ipv6_not_supported": "IPv6 no est\u00e1 soportado." + }, + "error": { + "cannot_connect": "Error al conectar", + "unknown": "Error Inesperado" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "\u00bfQuieres configurar {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "Direcci\u00f3n IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/et.json b/homeassistant/components/baf/translations/et.json new file mode 100644 index 00000000000..e958eb4545d --- /dev/null +++ b/homeassistant/components/baf/translations/et.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud", + "ipv6_not_supported": "IPv6 ei ole toetatud." + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Kas seadistada {name} \u2013 {model} ( {ip_address} )?" + }, + "user": { + "data": { + "ip_address": "IP aadress" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/fr.json b/homeassistant/components/baf/translations/fr.json new file mode 100644 index 00000000000..8088de223c3 --- /dev/null +++ b/homeassistant/components/baf/translations/fr.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", + "ipv6_not_supported": "IPv6 n'est pas pris en charge." + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "unknown": "Erreur inattendue" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Voulez-vous configurer {name} - {model} ({ip_address})\u00a0?" + }, + "user": { + "data": { + "ip_address": "Adresse IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/it.json b/homeassistant/components/baf/translations/it.json new file mode 100644 index 00000000000..86fcdab723b --- /dev/null +++ b/homeassistant/components/baf/translations/it.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", + "ipv6_not_supported": "IPv6 non \u00e8 supportato." + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "unknown": "Errore imprevisto" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Vuoi configurare {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "Indirizzo IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/pt-BR.json b/homeassistant/components/baf/translations/pt-BR.json new file mode 100644 index 00000000000..72ce0dd06fc --- /dev/null +++ b/homeassistant/components/baf/translations/pt-BR.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", + "ipv6_not_supported": "IPv6 n\u00e3o \u00e9 suportado." + }, + "error": { + "cannot_connect": "Falhou ao se conectar", + "unknown": "Erro inesperado" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Deseja configurar {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "Endere\u00e7o IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/sk.json b/homeassistant/components/baf/translations/sk.json new file mode 100644 index 00000000000..5ca1b9820a9 --- /dev/null +++ b/homeassistant/components/baf/translations/sk.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9", + "ipv6_not_supported": "IPv6 nie je podporovan\u00e9" + }, + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/tr.json b/homeassistant/components/baf/translations/tr.json new file mode 100644 index 00000000000..ffa458b7366 --- /dev/null +++ b/homeassistant/components/baf/translations/tr.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "ipv6_not_supported": "IPv6 desteklenmiyor." + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "unknown": "Beklenmeyen hata" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "{name} - {model} ( {ip_address} ) kurulumunu yapmak istiyor musunuz?" + }, + "user": { + "data": { + "ip_address": "IP Adresi" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/es.json b/homeassistant/components/binary_sensor/translations/es.json index 1e328150c39..5875804175e 100644 --- a/homeassistant/components/binary_sensor/translations/es.json +++ b/homeassistant/components/binary_sensor/translations/es.json @@ -83,7 +83,7 @@ "not_moist": "{entity_name} se sec\u00f3", "not_moving": "{entity_name} dej\u00f3 de moverse", "not_occupied": "{entity_name} no est\u00e1 ocupado", - "not_opened": "{entity_name} cerrado", + "not_opened": "{entity_name} se cierra", "not_plugged_in": "{entity_name} desconectado", "not_powered": "{entity_name} no est\u00e1 activado", "not_present": "{entity_name} no est\u00e1 presente", @@ -92,7 +92,7 @@ "not_unsafe": "{entity_name} se volvi\u00f3 seguro", "occupied": "{entity_name} se convirti\u00f3 en ocupado", "opened": "{entity_name} abierto", - "plugged_in": "{entity_name} conectado", + "plugged_in": "{entity_name} se ha enchufado", "powered": "{entity_name} alimentado", "present": "{entity_name} presente", "problem": "{entity_name} empez\u00f3 a detectar problemas", @@ -135,6 +135,7 @@ "on": "Cargando" }, "carbon_monoxide": { + "off": "Libre", "on": "Detectado" }, "co": { @@ -198,7 +199,7 @@ "on": "Enchufado" }, "presence": { - "off": "Fuera de casa", + "off": "Fuera", "on": "En casa" }, "problem": { diff --git a/homeassistant/components/binary_sensor/translations/sk.json b/homeassistant/components/binary_sensor/translations/sk.json index 5cff82615ae..89e6288a979 100644 --- a/homeassistant/components/binary_sensor/translations/sk.json +++ b/homeassistant/components/binary_sensor/translations/sk.json @@ -1,4 +1,8 @@ { + "device_class": { + "motion": "pohyb", + "vibration": "vibr\u00e1cia" + }, "state": { "_": { "off": "Neakt\u00edvny", diff --git a/homeassistant/components/blebox/translations/es.json b/homeassistant/components/blebox/translations/es.json index a415edd9809..c62caa44eec 100644 --- a/homeassistant/components/blebox/translations/es.json +++ b/homeassistant/components/blebox/translations/es.json @@ -9,7 +9,7 @@ "unknown": "Error inesperado", "unsupported_version": "El dispositivo BleBox tiene un firmware anticuado. Por favor, actual\u00edzalo primero." }, - "flow_title": "Dispositivo BleBox: {name} ({host})", + "flow_title": "{name} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/es.json b/homeassistant/components/braviatv/translations/es.json index 2cc3b05010c..d095deea33b 100644 --- a/homeassistant/components/braviatv/translations/es.json +++ b/homeassistant/components/braviatv/translations/es.json @@ -15,7 +15,7 @@ "pin": "C\u00f3digo PIN" }, "description": "Introduce el c\u00f3digo PIN que se muestra en el televisor Sony Bravia.\n\nSi no se muestra ning\u00fan c\u00f3digo PIN, necesitas eliminar el registro de Home Assistant de tu televisor, ve a: Configuraci\u00f3n -> Red -> Configuraci\u00f3n del dispositivo remoto -> Eliminar el dispositivo remoto.", - "title": "Autorizar televisor Sony Bravia" + "title": "Autorizaci\u00f3n del televisor Sony Bravia" }, "user": { "data": { diff --git a/homeassistant/components/brother/translations/es.json b/homeassistant/components/brother/translations/es.json index bc6aa749445..9c327a2887a 100644 --- a/homeassistant/components/brother/translations/es.json +++ b/homeassistant/components/brother/translations/es.json @@ -9,7 +9,7 @@ "snmp_error": "El servidor SNMP est\u00e1 apagado o la impresora no es compatible.", "wrong_host": "Nombre del host o direcci\u00f3n IP no v\u00e1lidos." }, - "flow_title": "Impresora Brother: {model} {serial_number}", + "flow_title": "{model} {serial_number}", "step": { "user": { "data": { diff --git a/homeassistant/components/brunt/translations/es.json b/homeassistant/components/brunt/translations/es.json index 61370b2791f..362ef22b838 100644 --- a/homeassistant/components/brunt/translations/es.json +++ b/homeassistant/components/brunt/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/brunt/translations/sk.json b/homeassistant/components/brunt/translations/sk.json index 71a7aea5018..3f00701d0ba 100644 --- a/homeassistant/components/brunt/translations/sk.json +++ b/homeassistant/components/brunt/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/bsblan/translations/es.json b/homeassistant/components/bsblan/translations/es.json index 691136f5441..830752dd863 100644 --- a/homeassistant/components/bsblan/translations/es.json +++ b/homeassistant/components/bsblan/translations/es.json @@ -6,7 +6,7 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "BSB-Lan: {name}", + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/button/translations/sk.json b/homeassistant/components/button/translations/sk.json new file mode 100644 index 00000000000..5dfc88234c6 --- /dev/null +++ b/homeassistant/components/button/translations/sk.json @@ -0,0 +1,7 @@ +{ + "device_automation": { + "action_type": { + "press": "Stla\u010dte tla\u010didlo {entity_name}" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/translations/es.json b/homeassistant/components/cast/translations/es.json index d38067588a8..39e34975af8 100644 --- a/homeassistant/components/cast/translations/es.json +++ b/homeassistant/components/cast/translations/es.json @@ -9,7 +9,7 @@ "step": { "config": { "data": { - "known_hosts": "Lista opcional de hosts conocidos si el descubrimiento mDNS no funciona." + "known_hosts": "Anfitriones conocidos" }, "description": "Introduce la configuraci\u00f3n de Google Cast.", "title": "Configuraci\u00f3n de Google Cast" diff --git a/homeassistant/components/climacell/translations/es.json b/homeassistant/components/climacell/translations/es.json index 4e709f03ad1..dba1957dd21 100644 --- a/homeassistant/components/climacell/translations/es.json +++ b/homeassistant/components/climacell/translations/es.json @@ -26,7 +26,7 @@ "timestep": "Min. Entre pron\u00f3sticos de NowCast" }, "description": "Si elige habilitar la entidad de pron\u00f3stico \"nowcast\", puede configurar el n\u00famero de minutos entre cada pron\u00f3stico. El n\u00famero de pron\u00f3sticos proporcionados depende del n\u00famero de minutos elegidos entre los pron\u00f3sticos.", - "title": "Actualizar las opciones ClimaCell" + "title": "Actualizaci\u00f3n de opciones de ClimaCell" } } }, diff --git a/homeassistant/components/climacell/translations/sensor.sk.json b/homeassistant/components/climacell/translations/sensor.sk.json new file mode 100644 index 00000000000..843169b2f3b --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "climacell__health_concern": { + "unhealthy": "Nezdrav\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climate/translations/es.json b/homeassistant/components/climate/translations/es.json index a7258609d59..09806ddf5ef 100644 --- a/homeassistant/components/climate/translations/es.json +++ b/homeassistant/components/climate/translations/es.json @@ -17,7 +17,7 @@ "state": { "_": { "auto": "Autom\u00e1tico", - "cool": "Fr\u00edo", + "cool": "Enfr\u00eda", "dry": "Seco", "fan_only": "Solo ventilador", "heat": "Calor", diff --git a/homeassistant/components/coinbase/translations/es.json b/homeassistant/components/coinbase/translations/es.json index 311fcdb8546..d8961ee6b3a 100644 --- a/homeassistant/components/coinbase/translations/es.json +++ b/homeassistant/components/coinbase/translations/es.json @@ -26,6 +26,7 @@ "options": { "error": { "currency_unavaliable": "La API de Coinbase no proporciona uno o m\u00e1s de los saldos de divisas solicitados.", + "exchange_rate_unavailable": "El API de Coinbase no proporciona alguno/s de los tipos de cambio que has solicitado.", "exchange_rate_unavaliable": "Coinbase no proporciona uno o m\u00e1s de los tipos de cambio solicitados.", "unknown": "Error inesperado" }, @@ -34,7 +35,8 @@ "data": { "account_balance_currencies": "Saldos de la cartera para informar.", "exchange_base": "Moneda base para sensores de tipo de cambio.", - "exchange_rate_currencies": "Tipos de cambio a informar." + "exchange_rate_currencies": "Tipos de cambio a informar.", + "exchnage_rate_precision": "N\u00famero de posiciones decimales para los tipos de cambio." }, "description": "Ajustar las opciones de Coinbase" } diff --git a/homeassistant/components/coinbase/translations/sk.json b/homeassistant/components/coinbase/translations/sk.json index ff853127803..f161003be52 100644 --- a/homeassistant/components/coinbase/translations/sk.json +++ b/homeassistant/components/coinbase/translations/sk.json @@ -10,5 +10,10 @@ } } } + }, + "options": { + "error": { + "currency_unavailable": "Jeden alebo viacero po\u017eadovan\u00fdch zostatkov mien nie s\u00fa poskytovan\u00e9 Va\u0161\u00edm Coinbase API." + } } } \ No newline at end of file diff --git a/homeassistant/components/configurator/translations/es.json b/homeassistant/components/configurator/translations/es.json index dffb90e6d49..50ea2f68a7c 100644 --- a/homeassistant/components/configurator/translations/es.json +++ b/homeassistant/components/configurator/translations/es.json @@ -1,7 +1,7 @@ { "state": { "_": { - "configure": "Configurar", + "configure": "Configura", "configured": "Configurado" } }, diff --git a/homeassistant/components/cover/translations/es.json b/homeassistant/components/cover/translations/es.json index 181cda45fb5..550c9d368e9 100644 --- a/homeassistant/components/cover/translations/es.json +++ b/homeassistant/components/cover/translations/es.json @@ -35,5 +35,5 @@ "stopped": "Detenido" } }, - "title": "Persiana" + "title": "Cubierta" } \ No newline at end of file diff --git a/homeassistant/components/crownstone/translations/es.json b/homeassistant/components/crownstone/translations/es.json index f52b0074322..b71c69db33c 100644 --- a/homeassistant/components/crownstone/translations/es.json +++ b/homeassistant/components/crownstone/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "usb_setup_complete": "Configuraci\u00f3n USB de Crownstone completa.", "usb_setup_unsuccessful": "La configuraci\u00f3n del USB de Crownstone no tuvo \u00e9xito." }, diff --git a/homeassistant/components/deconz/translations/es.json b/homeassistant/components/deconz/translations/es.json index 6822b71d564..0174c4cf76d 100644 --- a/homeassistant/components/deconz/translations/es.json +++ b/homeassistant/components/deconz/translations/es.json @@ -2,20 +2,21 @@ "config": { "abort": { "already_configured": "La pasarela ya est\u00e1 configurada", - "already_in_progress": "El flujo de configuraci\u00f3n para la pasarela ya est\u00e1 en marcha.", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "no_bridges": "No se han descubierto pasarelas deCONZ", "no_hardware_available": "No hay hardware de radio conectado a deCONZ", "not_deconz_bridge": "No es una pasarela deCONZ", "updated_instance": "Instancia deCONZ actualizada con nueva direcci\u00f3n de host" }, "error": { + "linking_not_possible": "No se ha podido enlazar con la pasarela", "no_key": "No se pudo obtener una clave API" }, "flow_title": "{host}", "step": { "hassio_confirm": { "description": "\u00bfQuieres configurar Home Assistant para que se conecte al gateway de deCONZ proporcionado por el add-on {addon} de Supervisor?", - "title": "Add-on deCONZ Zigbee v\u00eda Supervisor" + "title": "Pasarela de enlace de CONZ Zigbee v\u00eda complemento de Home Assistant" }, "link": { "description": "Desbloquea tu gateway de deCONZ para registrarte con Home Assistant.\n\n1. Dir\u00edgete a deCONZ Settings -> Gateway -> Advanced\n2. Pulsa el bot\u00f3n \"Authenticate app\"", diff --git a/homeassistant/components/deluge/translations/es.json b/homeassistant/components/deluge/translations/es.json new file mode 100644 index 00000000000..7ba1b8c3bf9 --- /dev/null +++ b/homeassistant/components/deluge/translations/es.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Contrase\u00f1a", + "port": "Puerto", + "username": "Usuario", + "web_port": "Puerto web (para el servicio de visita)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deluge/translations/sk.json b/homeassistant/components/deluge/translations/sk.json new file mode 100644 index 00000000000..0fbba9ccd6b --- /dev/null +++ b/homeassistant/components/deluge/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "port": "Port", + "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/denonavr/translations/es.json b/homeassistant/components/denonavr/translations/es.json index 04bcb55ace5..33051047719 100644 --- a/homeassistant/components/denonavr/translations/es.json +++ b/homeassistant/components/denonavr/translations/es.json @@ -10,7 +10,7 @@ "error": { "discovery_error": "Error detectando un Receptor AVR Denon en Red" }, - "flow_title": "Receptor AVR Denon en Red: {name}", + "flow_title": "{name}", "step": { "confirm": { "description": "Por favor confirma la adici\u00f3n del receptor", diff --git a/homeassistant/components/derivative/translations/es.json b/homeassistant/components/derivative/translations/es.json new file mode 100644 index 00000000000..114c2102678 --- /dev/null +++ b/homeassistant/components/derivative/translations/es.json @@ -0,0 +1,43 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Nombre", + "source": "Sensor de entrada", + "time_window": "Ventana de tiempo", + "unit_prefix": "Prefijo m\u00e9trico", + "unit_time": "Unidad de tiempo" + }, + "description": "Crea un sensor que ama la derivada de otro sensor.", + "title": "A\u00f1ade sensor derivativo" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "Nombre", + "round": "Precisi\u00f3n", + "unit_prefix": "Prefijo m\u00e9trico", + "unit_time": "Unidad de tiempo" + }, + "data_description": { + "round": "Controla el n\u00famero de d\u00edgitos decimales en la salida.", + "unit_prefix": "a salida se escalar\u00e1 seg\u00fan el prefijo m\u00e9trico y la unidad de tiempo de la derivada seleccionados." + } + }, + "options": { + "data": { + "name": "Nombre", + "time_window": "Ventana de tiempo", + "unit_prefix": "Prefijo m\u00e9trico", + "unit_time": "Unidad de tiempo" + }, + "description": "Crea un sensor que ama la derivada de otro sensor." + } + } + }, + "title": "Sensor derivatiu" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/sk.json b/homeassistant/components/derivative/translations/sk.json new file mode 100644 index 00000000000..cc4a1cc7cb9 --- /dev/null +++ b/homeassistant/components/derivative/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "Meno" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/device_tracker/translations/es.json b/homeassistant/components/device_tracker/translations/es.json index eccef30e765..47948da66ec 100644 --- a/homeassistant/components/device_tracker/translations/es.json +++ b/homeassistant/components/device_tracker/translations/es.json @@ -12,8 +12,8 @@ "state": { "_": { "home": "En casa", - "not_home": "Fuera de casa" + "not_home": "Fuera" } }, - "title": "Rastreador de dispositivo" + "title": "Seguimiento de dispositivos" } \ No newline at end of file diff --git a/homeassistant/components/devolo_home_control/translations/es.json b/homeassistant/components/devolo_home_control/translations/es.json index b4a7a873aaa..91a06530273 100644 --- a/homeassistant/components/devolo_home_control/translations/es.json +++ b/homeassistant/components/devolo_home_control/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/dexcom/translations/es.json b/homeassistant/components/dexcom/translations/es.json index 934c40be05e..24d06db3ee6 100644 --- a/homeassistant/components/dexcom/translations/es.json +++ b/homeassistant/components/dexcom/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/dialogflow/translations/es.json b/homeassistant/components/dialogflow/translations/es.json index ca2370f9086..8c2c5b4993e 100644 --- a/homeassistant/components/dialogflow/translations/es.json +++ b/homeassistant/components/dialogflow/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, diff --git a/homeassistant/components/directv/translations/es.json b/homeassistant/components/directv/translations/es.json index f1d896e698b..92cf160462c 100644 --- a/homeassistant/components/directv/translations/es.json +++ b/homeassistant/components/directv/translations/es.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "Error al conectar" }, - "flow_title": "DirecTV: {name}", + "flow_title": "{name}", "step": { "ssdp_confirm": { "description": "\u00bfQuieres configurar {name}?" diff --git a/homeassistant/components/discord/translations/es.json b/homeassistant/components/discord/translations/es.json new file mode 100644 index 00000000000..768afb877f3 --- /dev/null +++ b/homeassistant/components/discord/translations/es.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n" + }, + "step": { + "reauth_confirm": { + "data": { + "api_token": "Token API" + }, + "description": "Consulta la documentaci\u00f3n para obtener tu clave de bote de Discord.\n\n{url}" + }, + "user": { + "data": { + "api_token": "Token API" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/es.json b/homeassistant/components/dlna_dmr/translations/es.json index 6c7bd0ae4e3..6878a05b766 100644 --- a/homeassistant/components/dlna_dmr/translations/es.json +++ b/homeassistant/components/dlna_dmr/translations/es.json @@ -47,6 +47,7 @@ "step": { "init": { "data": { + "browse_unfiltered": "Muestra archivos multimedia incompatibles mientras navega", "callback_url_override": "URL de devoluci\u00f3n de llamada del detector de eventos", "listen_port": "Puerto de escucha de eventos (aleatorio si no se establece)", "poll_availability": "Sondeo para la disponibilidad del dispositivo" diff --git a/homeassistant/components/dlna_dms/translations/es.json b/homeassistant/components/dlna_dms/translations/es.json index b4bc90a666a..1ce13967ea5 100644 --- a/homeassistant/components/dlna_dms/translations/es.json +++ b/homeassistant/components/dlna_dms/translations/es.json @@ -1,10 +1,15 @@ { "config": { "abort": { + "bad_ssdp": "Falta un valor necesario en los datos SSDP", "no_devices_found": "No se han encontrado dispositivos en la red" }, + "flow_title": "{name}", "step": { "user": { + "data": { + "host": "Host" + }, "description": "Escoge un dispositivo a configurar" } } diff --git a/homeassistant/components/elgato/translations/es.json b/homeassistant/components/elgato/translations/es.json index 940272f5562..2cdbd7e6ae1 100644 --- a/homeassistant/components/elgato/translations/es.json +++ b/homeassistant/components/elgato/translations/es.json @@ -1,20 +1,20 @@ { "config": { "abort": { - "already_configured": "Este dispositivo Elgato Key Light ya est\u00e1 configurado.", + "already_configured": "El dispositivo ya est\u00e1 configurado", "cannot_connect": "No se pudo conectar" }, "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "Elgato Key Light: {serial_number}", + "flow_title": "{serial_number}", "step": { "user": { "data": { - "host": "Host o direcci\u00f3n IP", + "host": "Host", "port": "Puerto" }, - "description": "Configura tu Elgato Key Light para integrarlo con Home Assistant." + "description": "Configura la integraci\u00f3n de Elgato Light con Home Assistant." }, "zeroconf_confirm": { "description": "\u00bfDesea a\u00f1adir Elgato Key Light con el n\u00famero de serie `{serial_number}` a Home Assistant?", diff --git a/homeassistant/components/elkm1/translations/es.json b/homeassistant/components/elkm1/translations/es.json index 46e25dd288f..2bd75f83e2f 100644 --- a/homeassistant/components/elkm1/translations/es.json +++ b/homeassistant/components/elkm1/translations/es.json @@ -4,7 +4,8 @@ "address_already_configured": "Ya est\u00e1 configurado un Elk-M1 con esta direcci\u00f3n", "already_configured": "Ya est\u00e1 configurado un Elk-M1 con este prefijo", "already_in_progress": "La configuraci\u00f3n ya se encuentra en proceso", - "cannot_connect": "Error al conectar" + "cannot_connect": "Error al conectar", + "unknown": "Error inesperado" }, "error": { "cannot_connect": "No se pudo conectar", @@ -20,7 +21,7 @@ "temperature_unit": "La unidad de temperatura que el ElkM1 usa.", "username": "Usuario" }, - "description": "Con\u00e9ctese al sistema detectado: {mac_address} ({host})", + "description": "Con\u00e9ctate al sistema descubierto: {mac_address} ({host})", "title": "Conectar con Control Elk-M1" }, "manual_connection": { diff --git a/homeassistant/components/emulated_roku/translations/es.json b/homeassistant/components/emulated_roku/translations/es.json index 1bc53c03f19..abbcac75479 100644 --- a/homeassistant/components/emulated_roku/translations/es.json +++ b/homeassistant/components/emulated_roku/translations/es.json @@ -7,7 +7,7 @@ "user": { "data": { "advertise_ip": "IP para anunciar", - "advertise_port": "Puerto para anunciar", + "advertise_port": "Puerto de advertencias", "host_ip": "Direcci\u00f3n IP del host", "listen_port": "Puerto de escucha", "name": "Nombre", diff --git a/homeassistant/components/esphome/translations/es.json b/homeassistant/components/esphome/translations/es.json index f7fd73cd227..c682ddab294 100644 --- a/homeassistant/components/esphome/translations/es.json +++ b/homeassistant/components/esphome/translations/es.json @@ -11,7 +11,7 @@ "invalid_psk": "La clave de transporte cifrado no es v\u00e1lida. Por favor, aseg\u00farese de que coincide con la que tiene en su configuraci\u00f3n", "resolve_error": "No se puede resolver la direcci\u00f3n de ESP. Si el error persiste, configura una direcci\u00f3n IP est\u00e1tica: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips" }, - "flow_title": "ESPHome: {name}", + "flow_title": "{name}", "step": { "authenticate": { "data": { diff --git a/homeassistant/components/ezviz/translations/es.json b/homeassistant/components/ezviz/translations/es.json index a0f624bf8df..eef8343c17a 100644 --- a/homeassistant/components/ezviz/translations/es.json +++ b/homeassistant/components/ezviz/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured_account": "La cuenta ya ha sido configurada", + "already_configured_account": "La cuenta ya est\u00e1 configurada", "ezviz_cloud_account_missing": "Falta la cuenta de Ezviz Cloud. Por favor, reconfigura la cuenta de Ezviz Cloud", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/faa_delays/translations/sk.json b/homeassistant/components/faa_delays/translations/sk.json new file mode 100644 index 00000000000..bbadecd391b --- /dev/null +++ b/homeassistant/components/faa_delays/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "invalid_airport": "K\u00f3d letiska je neplatn\u00fd" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fan/translations/es.json b/homeassistant/components/fan/translations/es.json index c4edae6f9ee..85cc347f82d 100644 --- a/homeassistant/components/fan/translations/es.json +++ b/homeassistant/components/fan/translations/es.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "Cambiar {entity_name}", "turn_off": "Desactivar {entity_name}", "turn_on": "Activar {entity_name}" }, diff --git a/homeassistant/components/fan/translations/tr.json b/homeassistant/components/fan/translations/tr.json index 37091db264c..b4f7a9b5291 100644 --- a/homeassistant/components/fan/translations/tr.json +++ b/homeassistant/components/fan/translations/tr.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "{entity_name} de\u011fi\u015ftir", "turn_off": "{entity_name} kapat", "turn_on": "{entity_name} a\u00e7\u0131n" }, diff --git a/homeassistant/components/fibaro/translations/es.json b/homeassistant/components/fibaro/translations/es.json new file mode 100644 index 00000000000..00a7eeb8ece --- /dev/null +++ b/homeassistant/components/fibaro/translations/es.json @@ -0,0 +1,16 @@ +{ + "config": { + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n" + }, + "step": { + "user": { + "data": { + "password": "Contrase\u00f1a", + "url": "URL en el format http://HOST/api/", + "username": "Usuario" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fibaro/translations/sk.json b/homeassistant/components/fibaro/translations/sk.json new file mode 100644 index 00000000000..649a1e186ee --- /dev/null +++ b/homeassistant/components/fibaro/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/filesize/translations/es.json b/homeassistant/components/filesize/translations/es.json new file mode 100644 index 00000000000..e2bc079b961 --- /dev/null +++ b/homeassistant/components/filesize/translations/es.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "not_allowed": "Ruta no permitida" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/fireservicerota/translations/es.json b/homeassistant/components/fireservicerota/translations/es.json index 9f27181dfe5..085bce8f343 100644 --- a/homeassistant/components/fireservicerota/translations/es.json +++ b/homeassistant/components/fireservicerota/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "create_entry": { diff --git a/homeassistant/components/fivem/translations/es.json b/homeassistant/components/fivem/translations/es.json index d8b3f3c6e1c..873119cf4b9 100644 --- a/homeassistant/components/fivem/translations/es.json +++ b/homeassistant/components/fivem/translations/es.json @@ -12,7 +12,7 @@ "step": { "user": { "data": { - "host": "Anfitri\u00f3n", + "host": "Host", "name": "Nombre", "port": "Puerto" } diff --git a/homeassistant/components/flick_electric/translations/es.json b/homeassistant/components/flick_electric/translations/es.json index 435ead31d9a..424c73da24f 100644 --- a/homeassistant/components/flick_electric/translations/es.json +++ b/homeassistant/components/flick_electric/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/flunearyou/translations/es.json b/homeassistant/components/flunearyou/translations/es.json index 5d0c05c0c54..5d7b8fb6a6f 100644 --- a/homeassistant/components/flunearyou/translations/es.json +++ b/homeassistant/components/flunearyou/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Estas coordenadas ya est\u00e1n registradas." + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada" }, "error": { "unknown": "Error inesperado" diff --git a/homeassistant/components/forecast_solar/translations/es.json b/homeassistant/components/forecast_solar/translations/es.json index d688c577024..d82bd944202 100644 --- a/homeassistant/components/forecast_solar/translations/es.json +++ b/homeassistant/components/forecast_solar/translations/es.json @@ -22,6 +22,7 @@ "azimuth": "Azimut (360 grados, 0 = Norte, 90 = Este, 180 = Sur, 270 = Oeste)", "damping": "Factor de amortiguaci\u00f3n: ajusta los resultados por la ma\u00f1ana y por la noche", "declination": "Declinaci\u00f3n (0 = Horizontal, 90 = Vertical)", + "inverter_size": "Potencia del inversor (Watts)", "modules power": "Potencia pico total en vatios de tus m\u00f3dulos solares" }, "description": "Estos valores permiten ajustar el resultado de Solar.Forecast. Consulte la documentaci\u00f3n si un campo no est\u00e1 claro." diff --git a/homeassistant/components/forked_daapd/translations/es.json b/homeassistant/components/forked_daapd/translations/es.json index 7ec30e72b1a..54b1adb479f 100644 --- a/homeassistant/components/forked_daapd/translations/es.json +++ b/homeassistant/components/forked_daapd/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado.", + "already_configured": "El dispositivo ya est\u00e1 configurado", "not_forked_daapd": "El dispositivo no es un servidor forked-daapd." }, "error": { @@ -12,7 +12,7 @@ "wrong_password": "Contrase\u00f1a incorrecta.", "wrong_server_type": "La integraci\u00f3n forked-daapd requiere un servidor forked-daapd con versi\u00f3n >= 27.0." }, - "flow_title": "Servidor forked-daapd: {name} ({host})", + "flow_title": "{name} ({host})", "step": { "user": { "data": { diff --git a/homeassistant/components/freebox/translations/es.json b/homeassistant/components/freebox/translations/es.json index fb2da18c6c6..bbd691c9764 100644 --- a/homeassistant/components/freebox/translations/es.json +++ b/homeassistant/components/freebox/translations/es.json @@ -4,9 +4,9 @@ "already_configured": "El dispositivo ya est\u00e1 configurado." }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo.", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "register_failed": "No se pudo registrar, int\u00e9ntalo de nuevo", - "unknown": "Error desconocido: por favor, int\u00e9ntalo de nuevo m\u00e1s" + "unknown": "Error inesperado" }, "step": { "link": { diff --git a/homeassistant/components/fritz/translations/es.json b/homeassistant/components/fritz/translations/es.json index 7decdcd01f5..e8337dbef60 100644 --- a/homeassistant/components/fritz/translations/es.json +++ b/homeassistant/components/fritz/translations/es.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", + "ignore_ip6_link_local": "El enlace con direcciones IPv6 locales no est\u00e1 permitido", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/fritzbox/translations/es.json b/homeassistant/components/fritzbox/translations/es.json index fcb240deb77..77c8bb64ee5 100644 --- a/homeassistant/components/fritzbox/translations/es.json +++ b/homeassistant/components/fritzbox/translations/es.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Este AVM FRITZ!Box ya est\u00e1 configurado.", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", + "ignore_ip6_link_local": "El enlace con direcciones IPv6 locales no est\u00e1 permitido", "no_devices_found": "No se encontraron dispositivos en la red", "not_supported": "Conectado a AVM FRITZ!Box pero no es capaz de controlar dispositivos Smart Home.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" diff --git a/homeassistant/components/fritzbox_callmonitor/translations/es.json b/homeassistant/components/fritzbox_callmonitor/translations/es.json index d6891db5ef9..61e295d3b99 100644 --- a/homeassistant/components/fritzbox_callmonitor/translations/es.json +++ b/homeassistant/components/fritzbox_callmonitor/translations/es.json @@ -8,7 +8,7 @@ "error": { "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, - "flow_title": "Monitor de llamadas de AVM FRITZ! Box: {name}", + "flow_title": "{name}", "step": { "phonebook": { "data": { diff --git a/homeassistant/components/generic/translations/es.json b/homeassistant/components/generic/translations/es.json new file mode 100644 index 00000000000..90bff434f6d --- /dev/null +++ b/homeassistant/components/generic/translations/es.json @@ -0,0 +1,68 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." + }, + "error": { + "already_exists": "Ya hay una c\u00e1mara con esa URL de configuraci\u00f3n.", + "invalid_still_image": "La URL no ha devuelto una imagen fija v\u00e1lida", + "no_still_image_or_stream_url": "Tienes que especificar al menos una imagen una URL de flujo", + "stream_http_not_found": "HTTP 404 'Not found' al intentar conectarse al flujo de datos ('stream')", + "stream_no_route_to_host": "No se pudo encontrar el anfitri\u00f3n mientras intentaba conectar al flujo de datos", + "stream_no_video": "El flujo no contiene v\u00eddeo", + "stream_unauthorised": "La autorizaci\u00f3n ha fallado mientras se intentaba conectar con el flujo de datos", + "timeout": "El tiempo m\u00e1ximo de carga de la URL ha expirado", + "unknown": "Error inesperado" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tipos de contenido" + }, + "description": "Especifique el tipo de contenido para el flujo de datos (stream)." + }, + "user": { + "data": { + "authentication": "Autenticaci\u00f3n", + "content_type": "Tipo de contenido", + "framerate": "Frecuencia de visualizaci\u00f3n (Hz)", + "limit_refetch_to_url_change": "Limita la lectura al cambio de URL", + "still_image_url": "URL de imagen fija (ej. http://...)", + "stream_source": "URL origen del flux (p. ex. rtsp://...)", + "username": "Usuario", + "verify_ssl": "Verifica el certificat SSL" + } + } + } + }, + "options": { + "error": { + "already_exists": "Ya hay una c\u00e1mara con esa URL de configuraci\u00f3n.", + "stream_no_route_to_host": "No se pudo encontrar el anfitri\u00f3n mientras intentaba conectar al flujo de datos", + "stream_unauthorised": "La autorizaci\u00f3n ha fallado mientras se intentaba conectar con el flujo de datos", + "timeout": "El tiempo m\u00e1ximo de carga de la URL ha expirado", + "unknown": "Error inesperado" + }, + "step": { + "content_type": { + "data": { + "content_type": "Tipos de contenido" + }, + "description": "Especifique el tipo de contenido para el flujo de datos (stream)." + }, + "init": { + "data": { + "authentication": "Autenticaci\u00f3n", + "content_type": "Tipo de contenido", + "framerate": "Frecuencia de visualizaci\u00f3n (Hz)", + "limit_refetch_to_url_change": "Limita la lectura al cambio de URL", + "password": "Contrase\u00f1a", + "rtsp_transport": "Protocolo de transporte RTSP", + "still_image_url": "URL de imagen fija (ej. http://...)", + "username": "Usuario", + "verify_ssl": "Verifica el certificado SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/tr.json b/homeassistant/components/generic/translations/tr.json index c2fb343f227..d6ba8601beb 100644 --- a/homeassistant/components/generic/translations/tr.json +++ b/homeassistant/components/generic/translations/tr.json @@ -79,8 +79,12 @@ "rtsp_transport": "RTSP aktar\u0131m protokol\u00fc", "still_image_url": "Hareketsiz G\u00f6r\u00fcnt\u00fc URL'si (\u00f6rne\u011fin http://...)", "stream_source": "Ak\u0131\u015f Kayna\u011f\u0131 URL'si (\u00f6r. rtsp://...)", + "use_wallclock_as_timestamps": "Wallclock'u zaman damgas\u0131 olarak kullan\u0131n", "username": "Kullan\u0131c\u0131 Ad\u0131", "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" + }, + "data_description": { + "use_wallclock_as_timestamps": "Bu se\u00e7enek, baz\u0131 kameralarda hatal\u0131 zaman damgas\u0131 uygulamalar\u0131ndan kaynaklanan segmentlere ay\u0131rma veya kilitlenme sorunlar\u0131n\u0131 d\u00fczeltebilir" } } } diff --git a/homeassistant/components/geocaching/translations/es.json b/homeassistant/components/geocaching/translations/es.json new file mode 100644 index 00000000000..8b03adca234 --- /dev/null +++ b/homeassistant/components/geocaching/translations/es.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "La cuenta ya est\u00e1 configurada", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", + "missing_configuration": "El componente no est\u00e1 configurado. Mira su documentaci\u00f3n.", + "oauth_error": "Se han recibido datos token inv\u00e1lidos.", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "create_entry": { + "default": "Autenticaci\u00f3n exitosa" + }, + "step": { + "pick_implementation": { + "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" + }, + "reauth_confirm": { + "description": "La integraci\u00f3n Geocaching debe volver a autenticarse con tu cuenta", + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/tr.json b/homeassistant/components/geocaching/translations/tr.json new file mode 100644 index 00000000000..b2c7a1031a8 --- /dev/null +++ b/homeassistant/components/geocaching/translations/tr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "authorize_url_timeout": "Yetkilendirme URL'si olu\u015ftururken zaman a\u015f\u0131m\u0131.", + "missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.", + "no_url_available": "Kullan\u0131labilir URL yok. Bu hata hakk\u0131nda bilgi i\u00e7in [yard\u0131m b\u00f6l\u00fcm\u00fcne bak\u0131n]({docs_url})", + "oauth_error": "Ge\u00e7ersiz anahtar verileri al\u0131nd\u0131.", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "create_entry": { + "default": "Ba\u015far\u0131yla do\u011fruland\u0131" + }, + "step": { + "pick_implementation": { + "title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7" + }, + "reauth_confirm": { + "description": "Geocaching entegrasyonunun hesab\u0131n\u0131z\u0131 yeniden do\u011frulamas\u0131 gerekiyor", + "title": "Entegrasyonu Yeniden Do\u011frula" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/github/translations/es.json b/homeassistant/components/github/translations/es.json new file mode 100644 index 00000000000..d53004c3cb5 --- /dev/null +++ b/homeassistant/components/github/translations/es.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "already_configured": "El servicio ya est\u00e1 configurado", + "could_not_register": "No se pudo registrar la integraci\u00f3n con GitHub" + }, + "step": { + "repositories": { + "data": { + "repositories": "Seleccione los repositorios a seguir." + }, + "title": "Configuraci\u00f3n de repositorios" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/goalzero/translations/es.json b/homeassistant/components/goalzero/translations/es.json index fa54d6d6afc..921941d1c16 100644 --- a/homeassistant/components/goalzero/translations/es.json +++ b/homeassistant/components/goalzero/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "invalid_host": "Nombre de host o direcci\u00f3n IP no v\u00e1lidos", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/google/translations/es.json b/homeassistant/components/google/translations/es.json index 8072ac95d4b..c6d990c2caa 100644 --- a/homeassistant/components/google/translations/es.json +++ b/homeassistant/components/google/translations/es.json @@ -1,11 +1,19 @@ { "config": { "abort": { - "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n." + "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.", + "oauth_error": "Se han recibido datos token inv\u00e1lidos.", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "create_entry": { + "default": "Autenticaci\u00f3n exitosa" }, "step": { "auth": { "title": "Vincular cuenta de Google" + }, + "pick_implementation": { + "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" } } } diff --git a/homeassistant/components/gpslogger/translations/es.json b/homeassistant/components/gpslogger/translations/es.json index 733871c5ee7..ea3221ee2f5 100644 --- a/homeassistant/components/gpslogger/translations/es.json +++ b/homeassistant/components/gpslogger/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, diff --git a/homeassistant/components/group/translations/es.json b/homeassistant/components/group/translations/es.json index e74adda486d..719d42c772a 100644 --- a/homeassistant/components/group/translations/es.json +++ b/homeassistant/components/group/translations/es.json @@ -1,9 +1,17 @@ { "config": { "step": { + "binary_sensor": { + "data": { + "hide_members": "Esconde miembros" + }, + "title": "Agregar grupo" + }, "cover": { "data": { - "name": "Nombre del Grupo" + "hide_members": "Esconde miembros", + "name": "Nombre del Grupo", + "title": "Agregar grupo" }, "description": "Seleccionar opciones de grupo" }, @@ -11,6 +19,135 @@ "data": { "entities": "Miembros del grupo" } + }, + "fan": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros", + "title": "Agregar grupo" + }, + "description": "Selecciona las opciones del grupo", + "title": "Agregar grupo" + }, + "fan_options": { + "data": { + "entities": "Miembros del grupo" + } + }, + "init": { + "data": { + "group_type": "Tipo de grupo" + }, + "description": "Selecciona el tipo de grupo" + }, + "light": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros", + "title": "Agregar grupo" + }, + "title": "Agregar grupo" + }, + "light_options": { + "data": { + "entities": "Miembros del grupo" + }, + "description": "Selecciona las opciones del grupo" + }, + "lock": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros" + }, + "title": "Agregar grupo" + }, + "media_player": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros", + "name": "Nombre" + }, + "description": "Selecciona las opciones del grupo", + "title": "Agregar grupo" + }, + "media_player_options": { + "description": "Selecciona las opciones del grupo" + }, + "switch": { + "data": { + "entities": "Miembros" + } + }, + "user": { + "menu_options": { + "binary_sensor": "Grupo de sensores binarios", + "cover": "Grupo de cubiertas", + "fan": "Grupo de ventiladores", + "switch": "Grupo de conmutadores" + } + } + } + }, + "options": { + "step": { + "binary_sensor": { + "data": { + "all": "Todas las entidades", + "hide_members": "Esconde miembros" + } + }, + "binary_sensor_options": { + "data": { + "all": "Todas las entidades" + } + }, + "cover": { + "data": { + "hide_members": "Esconde miembros" + } + }, + "cover_options": { + "data": { + "entities": "Miembros" + } + }, + "fan": { + "data": { + "hide_members": "Esconde miembros" + } + }, + "fan_options": { + "data": { + "entities": "Miembros" + } + }, + "light": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros" + } + }, + "light_options": { + "data": { + "all": "Todas las entidades", + "entities": "Miembros" + } + }, + "lock": { + "data": { + "entities": "Miembros" + } + }, + "media_player": { + "data": { + "entities": "Miembros", + "hide_members": "Esconde miembros" + } + }, + "media_player_options": { + "data": { + "entities": "Miembros" + } } } }, @@ -19,7 +156,7 @@ "closed": "Cerrado", "home": "En casa", "locked": "Bloqueado", - "not_home": "Fuera de casa", + "not_home": "Fuera", "off": "Apagado", "ok": "OK", "on": "Encendido", diff --git a/homeassistant/components/group/translations/sk.json b/homeassistant/components/group/translations/sk.json index 151cc1c47b0..51759a9dc86 100644 --- a/homeassistant/components/group/translations/sk.json +++ b/homeassistant/components/group/translations/sk.json @@ -1,4 +1,22 @@ { + "config": { + "step": { + "media_player": { + "data": { + "name": "Meno" + } + } + } + }, + "options": { + "step": { + "light": { + "data": { + "entities": "\u010clenovia" + } + } + } + }, "state": { "_": { "closed": "Zatvoren\u00e1", diff --git a/homeassistant/components/guardian/translations/es.json b/homeassistant/components/guardian/translations/es.json index 981534cca9b..531d4ac5774 100644 --- a/homeassistant/components/guardian/translations/es.json +++ b/homeassistant/components/guardian/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Este dispositivo Guardian ya ha sido configurado", + "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "La configuraci\u00f3n del dispositivo Guardian ya est\u00e1 en proceso.", "cannot_connect": "No se pudo conectar" }, diff --git a/homeassistant/components/hangouts/translations/es.json b/homeassistant/components/hangouts/translations/es.json index 692df44c5bc..a2aba99c24c 100644 --- a/homeassistant/components/hangouts/translations/es.json +++ b/homeassistant/components/hangouts/translations/es.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "already_configured": "Google Hangouts ya est\u00e1 configurado", + "already_configured": "El servicio ya est\u00e1 configurado", "unknown": "Error desconocido" }, "error": { "invalid_2fa": "Autenticaci\u00f3n de 2 factores no v\u00e1lida, por favor, int\u00e9ntelo de nuevo.", - "invalid_2fa_method": "M\u00e9todo 2FA no v\u00e1lido (verificar en el tel\u00e9fono).", + "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar en el tel\u00e9fono).", "invalid_login": "Inicio de sesi\u00f3n no v\u00e1lido, por favor, int\u00e9ntalo de nuevo." }, "step": { @@ -24,7 +24,7 @@ "password": "Contrase\u00f1a" }, "description": "Vac\u00edo", - "title": "Iniciar sesi\u00f3n en Google Hangouts" + "title": "Inicio de sesi\u00f3n de Google Chat" } } } diff --git a/homeassistant/components/harmony/translations/es.json b/homeassistant/components/harmony/translations/es.json index 39305d30680..527cd8433e8 100644 --- a/homeassistant/components/harmony/translations/es.json +++ b/homeassistant/components/harmony/translations/es.json @@ -4,10 +4,10 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "unknown": "Error inesperado" }, - "flow_title": "Logitech Harmony Hub {name}", + "flow_title": "{name}", "step": { "link": { "description": "\u00bfQuieres configurar {name} ({host})?", diff --git a/homeassistant/components/hisense_aehw4a1/translations/es.json b/homeassistant/components/hisense_aehw4a1/translations/es.json index 1a3a8ca221d..8eb81391ec4 100644 --- a/homeassistant/components/hisense_aehw4a1/translations/es.json +++ b/homeassistant/components/hisense_aehw4a1/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "no_devices_found": "No se encontraron dispositivos Hisense AEH-W4A1 en la red.", - "single_instance_allowed": "Solo es posible una \u00fanica configuraci\u00f3n de Hisense AEH-W4A1." + "no_devices_found": "No se encontraron dispositivos en la red", + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "confirm": { diff --git a/homeassistant/components/hive/translations/es.json b/homeassistant/components/hive/translations/es.json index 727a33ec66e..09acc273536 100644 --- a/homeassistant/components/hive/translations/es.json +++ b/homeassistant/components/hive/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", "unknown_entry": "No se puede encontrar una entrada existente." }, diff --git a/homeassistant/components/home_connect/translations/es.json b/homeassistant/components/home_connect/translations/es.json index 10b49c96926..9ee5769583b 100644 --- a/homeassistant/components/home_connect/translations/es.json +++ b/homeassistant/components/home_connect/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "missing_configuration": "El componente Home Connect no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.", + "missing_configuration": "El componente no est\u00e1 configurado. Mira su documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})" }, "create_entry": { diff --git a/homeassistant/components/home_plus_control/translations/es.json b/homeassistant/components/home_plus_control/translations/es.json index 3c471ffc75e..82144e727ab 100644 --- a/homeassistant/components/home_plus_control/translations/es.json +++ b/homeassistant/components/home_plus_control/translations/es.json @@ -1,9 +1,9 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." diff --git a/homeassistant/components/homeassistant/translations/es.json b/homeassistant/components/homeassistant/translations/es.json index 0a9342afa69..6b00a1b3b51 100644 --- a/homeassistant/components/homeassistant/translations/es.json +++ b/homeassistant/components/homeassistant/translations/es.json @@ -6,7 +6,7 @@ "docker": "Docker", "hassio": "Supervisor", "installation_type": "Tipo de instalaci\u00f3n", - "os_name": "Nombre del Sistema Operativo", + "os_name": "Familia del sistema operativo", "os_version": "Versi\u00f3n del Sistema Operativo", "python_version": "Versi\u00f3n de Python", "timezone": "Zona horaria", diff --git a/homeassistant/components/homekit/translations/es.json b/homeassistant/components/homekit/translations/es.json index ef60610a8a1..c673a4e9749 100644 --- a/homeassistant/components/homekit/translations/es.json +++ b/homeassistant/components/homekit/translations/es.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "port_name_in_use": "Ya est\u00e1 configurada una pasarela con el mismo nombre o puerto." + "port_name_in_use": "Ya existe un enlace o accesorio configurado con ese nombre o puerto." }, "step": { "pairing": { "description": "Para completar el emparejamiento, sigue las instrucciones en \"Notificaciones\" en \"Emparejamiento HomeKit\".", - "title": "Vincular pasarela Homekit" + "title": "Vinculaci\u00f3n HomeKit" }, "user": { "data": { @@ -19,6 +19,11 @@ }, "options": { "step": { + "accessory": { + "data": { + "entities": "Entidad" + } + }, "advanced": { "data": { "auto_start": "Arranque autom\u00e1tico (desactivado si se utiliza Z-Wave u otro sistema de arranque retardado)", @@ -36,16 +41,23 @@ "title": "Seleccione el c\u00f3dec de video de la c\u00e1mara." }, "exclude": { + "data": { + "entities": "Entidades" + }, + "title": "Selecciona las entidades a excluir" + }, + "include": { "data": { "entities": "Entidades" } }, "init": { "data": { - "mode": "Modo" + "domains": "Dominios a incluir", + "mode": "Mode de HomeKit" }, "description": "Las entidades de los \"Dominios que se van a incluir\" se establecer\u00e1n en HomeKit. Podr\u00e1 seleccionar qu\u00e9 entidades excluir de esta lista en la siguiente pantalla.", - "title": "Seleccione los dominios que desea establecer un puente." + "title": "Selecciona el modo y dominios." }, "yaml": { "description": "Esta entrada se controla a trav\u00e9s de YAML", diff --git a/homeassistant/components/homekit_controller/translations/es.json b/homeassistant/components/homekit_controller/translations/es.json index 52b295ecf21..a528d66ea06 100644 --- a/homeassistant/components/homekit_controller/translations/es.json +++ b/homeassistant/components/homekit_controller/translations/es.json @@ -18,7 +18,7 @@ "unable_to_pair": "No se ha podido emparejar, por favor int\u00e9ntelo de nuevo.", "unknown_error": "El dispositivo report\u00f3 un error desconocido. La vinculaci\u00f3n ha fallado." }, - "flow_title": "Accesorio HomeKit: {name}", + "flow_title": "{name}", "step": { "busy_error": { "description": "Interrumpe el emparejamiento en todos los controladores o intenta reiniciar el dispositivo y luego contin\u00faa con el emparejamiento.", @@ -69,5 +69,5 @@ "single_press": "\"{subtype}\" pulsado" } }, - "title": "Accesorio HomeKit" + "title": "Controlador HomeKit" } \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/select.es.json b/homeassistant/components/homekit_controller/translations/select.es.json new file mode 100644 index 00000000000..0cbfbc71373 --- /dev/null +++ b/homeassistant/components/homekit_controller/translations/select.es.json @@ -0,0 +1,8 @@ +{ + "state": { + "homekit_controller__ecobee_mode": { + "away": "Afuera", + "sleep": "Durmiendo" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homekit_controller/translations/sk.json b/homeassistant/components/homekit_controller/translations/sk.json index bee0999420f..8ebd0e1e08e 100644 --- a/homeassistant/components/homekit_controller/translations/sk.json +++ b/homeassistant/components/homekit_controller/translations/sk.json @@ -2,6 +2,11 @@ "config": { "abort": { "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" + }, + "step": { + "user": { + "title": "V\u00fdber zariadenia" + } } } } \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/translations/es.json b/homeassistant/components/homematicip_cloud/translations/es.json index 28a084a7ee9..454fa8f9f2a 100644 --- a/homeassistant/components/homematicip_cloud/translations/es.json +++ b/homeassistant/components/homematicip_cloud/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El punto de acceso ya est\u00e1 configurado", - "connection_aborted": "No se pudo conectar al servidor HMIP", + "connection_aborted": "Fall\u00f3 la conexi\u00f3n", "unknown": "Se ha producido un error desconocido." }, "error": { diff --git a/homeassistant/components/homewizard/translations/es.json b/homeassistant/components/homewizard/translations/es.json index 92faffd15c8..898d37fed09 100644 --- a/homeassistant/components/homewizard/translations/es.json +++ b/homeassistant/components/homewizard/translations/es.json @@ -4,7 +4,7 @@ "already_configured": "El dispositivo ya est\u00e1 configurado", "api_not_enabled": "La API no est\u00e1 habilitada. Habilite la API en la aplicaci\u00f3n HomeWizard Energy en configuraci\u00f3n", "device_not_supported": "Este dispositivo no es compatible", - "invalid_discovery_parameters": "unsupported_api_version", + "invalid_discovery_parameters": "Versi\u00f3n de API no compatible detectada", "unknown_error": "Error inesperado" }, "step": { diff --git a/homeassistant/components/honeywell/translations/es.json b/homeassistant/components/honeywell/translations/es.json index bdae26b8794..f30e9606a4d 100644 --- a/homeassistant/components/honeywell/translations/es.json +++ b/homeassistant/components/honeywell/translations/es.json @@ -12,5 +12,14 @@ "description": "Por favor, introduzca las credenciales utilizadas para iniciar sesi\u00f3n en mytotalconnectcomfort.com." } } + }, + "options": { + "step": { + "init": { + "data": { + "away_cool_temperature": "Temperatura fria, modo fuera" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/honeywell/translations/sk.json b/homeassistant/components/honeywell/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/honeywell/translations/sk.json +++ b/homeassistant/components/honeywell/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/huawei_lte/translations/es.json b/homeassistant/components/huawei_lte/translations/es.json index 5d5e72e70c3..09a63bb1f62 100644 --- a/homeassistant/components/huawei_lte/translations/es.json +++ b/homeassistant/components/huawei_lte/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Este dispositivo ya ha sido configurado", - "already_in_progress": "Este dispositivo ya se est\u00e1 configurando", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "not_huawei_lte": "No es un dispositivo Huawei LTE" }, "error": { @@ -15,7 +15,7 @@ "response_error": "Error desconocido del dispositivo", "unknown": "Error inesperado" }, - "flow_title": "Huawei LTE: {name}", + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/hue/translations/es.json b/homeassistant/components/hue/translations/es.json index e8611cbe690..7017cca99c1 100644 --- a/homeassistant/components/hue/translations/es.json +++ b/homeassistant/components/hue/translations/es.json @@ -3,11 +3,12 @@ "abort": { "all_configured": "Ya se han configurado todas las pasarelas Philips Hue", "already_configured": "El dispositivo ya est\u00e1 configurado", - "already_in_progress": "El flujo de configuraci\u00f3n para la pasarela ya est\u00e1 en marcha.", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "cannot_connect": "No se pudo conectar", "discover_timeout": "Imposible encontrar pasarelas Philips Hue", + "invalid_host": "Host inv\u00e1lido", "no_bridges": "No se han encontrado pasarelas Philips Hue.", - "not_hue_bridge": "No es una pasarela Hue", + "not_hue_bridge": "No es un enlace Hue", "unknown": "Error inesperado" }, "error": { diff --git a/homeassistant/components/hue/translations/sk.json b/homeassistant/components/hue/translations/sk.json index 424ac0d9252..10605f27ce1 100644 --- a/homeassistant/components/hue/translations/sk.json +++ b/homeassistant/components/hue/translations/sk.json @@ -20,5 +20,10 @@ "description": "Pre registr\u00e1ciu Philips Hue s Home Assistant stla\u010dte tla\u010didlo na Philips Hue bridge.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)" } } + }, + "device_automation": { + "trigger_subtype": { + "1": "Prv\u00e9 tla\u010didlo" + } } } \ No newline at end of file diff --git a/homeassistant/components/hyperion/translations/es.json b/homeassistant/components/hyperion/translations/es.json index 5b4534069dd..496064b1543 100644 --- a/homeassistant/components/hyperion/translations/es.json +++ b/homeassistant/components/hyperion/translations/es.json @@ -23,7 +23,7 @@ "description": "Configurar autorizaci\u00f3n a tu servidor Hyperion Ambilight" }, "confirm": { - "description": "\u00bfQuieres a\u00f1adir este Hyperion Ambilight a Home Assistant?\n\n**Host:** {host}\n**Puerto:** {port}\n**Identificaci\u00f3n**: {id}", + "description": "\u00bfQuieres a\u00f1adir el siguiente Hyperion Ambilight en Home Assistant?\n\n**Host:** {host}\n**Puerto:** {port}\n**Identificaci\u00f3n**: {id}", "title": "Confirmar la adici\u00f3n del servicio Hyperion Ambilight" }, "create_token": { diff --git a/homeassistant/components/iaqualink/translations/es.json b/homeassistant/components/iaqualink/translations/es.json index 71dc95c82f5..c95f9d51927 100644 --- a/homeassistant/components/iaqualink/translations/es.json +++ b/homeassistant/components/iaqualink/translations/es.json @@ -13,8 +13,8 @@ "password": "Contrase\u00f1a", "username": "Usuario" }, - "description": "Por favor, introduzca el nombre de usuario y la contrase\u00f1a de su cuenta de iAqualink.", - "title": "Con\u00e9ctese a iAqualink" + "description": "Introduce el nombre de usuario y contrase\u00f1a de tu cuenta de iAqualink.", + "title": "Conexi\u00f3n con iAqualink" } } } diff --git a/homeassistant/components/icloud/translations/es.json b/homeassistant/components/icloud/translations/es.json index 593f2753d99..31db2283f66 100644 --- a/homeassistant/components/icloud/translations/es.json +++ b/homeassistant/components/icloud/translations/es.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "no_device": "Ninguno de tus dispositivos tiene activado \"Buscar mi iPhone\"", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "send_verification_code": "Error al enviar el c\u00f3digo de verificaci\u00f3n", - "validate_verification_code": "No se pudo verificar el c\u00f3digo de verificaci\u00f3n, elegir un dispositivo de confianza e iniciar la verificaci\u00f3n de nuevo" + "validate_verification_code": "No se ha podido verificar el c\u00f3digo de verificaci\u00f3n, vuelve a intentarlo" }, "step": { "reauth": { diff --git a/homeassistant/components/input_datetime/translations/es.json b/homeassistant/components/input_datetime/translations/es.json index 025943ee4fb..570c7b17592 100644 --- a/homeassistant/components/input_datetime/translations/es.json +++ b/homeassistant/components/input_datetime/translations/es.json @@ -1,3 +1,3 @@ { - "title": "Entrada de fecha" + "title": "Entrada de data i hora" } \ No newline at end of file diff --git a/homeassistant/components/input_number/translations/es.json b/homeassistant/components/input_number/translations/es.json index e05a9130580..e31e0ccc72f 100644 --- a/homeassistant/components/input_number/translations/es.json +++ b/homeassistant/components/input_number/translations/es.json @@ -1,3 +1,3 @@ { - "title": "Entrada de n\u00famero" + "title": "Entrada num\u00e9rica" } \ No newline at end of file diff --git a/homeassistant/components/input_number/translations/tr.json b/homeassistant/components/input_number/translations/tr.json index c1805180884..8d85abf5c15 100644 --- a/homeassistant/components/input_number/translations/tr.json +++ b/homeassistant/components/input_number/translations/tr.json @@ -1,3 +1,3 @@ { - "title": "Numara giriniz" + "title": "Numara giri\u015fi" } \ No newline at end of file diff --git a/homeassistant/components/insteon/translations/es.json b/homeassistant/components/insteon/translations/es.json index 31088b3bf60..b7b72cefa28 100644 --- a/homeassistant/components/insteon/translations/es.json +++ b/homeassistant/components/insteon/translations/es.json @@ -1,14 +1,19 @@ { "config": { "abort": { - "cannot_connect": "No se puede conectar al m\u00f3dem Insteon", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "not_insteon_device": "El dispositivo descubierto no es un dispositivo Insteon", "single_instance_allowed": "Ya esta configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "error": { "cannot_connect": "No se conect\u00f3 al m\u00f3dem Insteon, por favor, int\u00e9ntelo de nuevo.", "select_single": "Seleccione una opci\u00f3n." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "\u00bfQuieres configurar {name}?" + }, "hubv1": { "data": { "host": "Direcci\u00f3n IP", diff --git a/homeassistant/components/insteon/translations/sk.json b/homeassistant/components/insteon/translations/sk.json index c563a509f07..b3711644c03 100644 --- a/homeassistant/components/insteon/translations/sk.json +++ b/homeassistant/components/insteon/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "hubv1": { "data": { diff --git a/homeassistant/components/integration/translations/es.json b/homeassistant/components/integration/translations/es.json new file mode 100644 index 00000000000..8c903976d1d --- /dev/null +++ b/homeassistant/components/integration/translations/es.json @@ -0,0 +1,36 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "M\u00e9todo de integraci\u00f3n", + "round": "Precisi\u00f3n", + "source": "Sensor de entrada", + "unit_prefix": "Prefijo m\u00e9trico", + "unit_time": "Unidad de tiempo" + }, + "data_description": { + "round": "Controla el n\u00famero de d\u00edgitos decimales en la salida.", + "unit_prefix": "La salida se escalar\u00e1 seg\u00fan el prefijo m\u00e9trico seleccionado.", + "unit_time": "La salida se escalar\u00e1 seg\u00fan la unidad de tiempo seleccionada." + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "Precisi\u00f3n" + }, + "data_description": { + "round": "Controla el n\u00famero de d\u00edgitos decimales en la salida." + } + }, + "options": { + "description": "La precisi\u00f3n controla el n\u00famero de d\u00edgitos decimales en la salida." + } + } + }, + "title": "Integraci\u00f3n - Sensor integral de suma de Riemann" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/sk.json b/homeassistant/components/integration/translations/sk.json new file mode 100644 index 00000000000..aec16fc71b8 --- /dev/null +++ b/homeassistant/components/integration/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "step": { + "user": { + "data": { + "round": "Presnos\u0165" + } + } + } + }, + "options": { + "step": { + "options": { + "data": { + "round": "Presnos\u0165" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/es.json b/homeassistant/components/intellifire/translations/es.json new file mode 100644 index 00000000000..e848376bdfb --- /dev/null +++ b/homeassistant/components/intellifire/translations/es.json @@ -0,0 +1,43 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "not_intellifire_device": "No es un dispositivo IntelliFire.", + "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" + }, + "error": { + "api_error": "Ha Fallado el inicio de sesi\u00f3n", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "iftapi_connect": "Se ha producido un error al conectar a iftapi.net" + }, + "flow_title": "{serial} ({host})", + "step": { + "api_config": { + "data": { + "password": "Contrase\u00f1a", + "username": "Correo electr\u00f3nico" + } + }, + "dhcp_confirm": { + "description": "\u00bfQuieres configurar {host} \nSerie: {serial}?" + }, + "manual_device_entry": { + "data": { + "host": "Host (direcci\u00f3n IP)" + } + }, + "pick_device": { + "data": { + "host": "Host" + }, + "description": "Se han descubierto los siguientes dispositivos IntelliFire. Selecciona lo que quieras configurar.", + "title": "Selecci\u00f3n de dispositivo" + }, + "user": { + "data": { + "host": "Host" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ios/translations/es.json b/homeassistant/components/ios/translations/es.json index 3c4064280e7..8adb4041b20 100644 --- a/homeassistant/components/ios/translations/es.json +++ b/homeassistant/components/ios/translations/es.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Solo se necesita una \u00fanica configuraci\u00f3n de Home Assistant iOS." + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "confirm": { - "description": "\u00bfQuieres configurar el componente iOS de Home Assistant?" + "description": "\u00bfQuieres iniciar la configuraci\u00f3n?" } } } diff --git a/homeassistant/components/iss/translations/es.json b/homeassistant/components/iss/translations/es.json index 2cc46fefdb1..a0456431ad8 100644 --- a/homeassistant/components/iss/translations/es.json +++ b/homeassistant/components/iss/translations/es.json @@ -7,7 +7,8 @@ "user": { "data": { "show_on_map": "\u00bfMostrar en el mapa?" - } + }, + "description": "\u00bfQuieres configurar Estaci\u00f3n Espacial Internacional (ISS)?" } } }, diff --git a/homeassistant/components/isy994/translations/es.json b/homeassistant/components/isy994/translations/es.json index e5e15fc9c13..b9b2c1f1ed5 100644 --- a/homeassistant/components/isy994/translations/es.json +++ b/homeassistant/components/isy994/translations/es.json @@ -11,6 +11,14 @@ }, "flow_title": "{name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "Contrase\u00f1a", + "username": "Nombre de usuario" + }, + "description": "Las credenciales de {host} ya no son v\u00e1lidas.", + "title": "Re-autenticaci\u00f3n de ISY" + }, "user": { "data": { "host": "URL", @@ -19,7 +27,7 @@ "username": "Usuario" }, "description": "La entrada del host debe estar en formato URL completo, por ejemplo, http://192.168.10.100:80", - "title": "Conectar con tu ISY994" + "title": "Conexi\u00f3n con ISY" } } }, diff --git a/homeassistant/components/jellyfin/translations/sk.json b/homeassistant/components/jellyfin/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/jellyfin/translations/sk.json +++ b/homeassistant/components/jellyfin/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/juicenet/translations/es.json b/homeassistant/components/juicenet/translations/es.json index 12b5159c99c..2fa2b61cd60 100644 --- a/homeassistant/components/juicenet/translations/es.json +++ b/homeassistant/components/juicenet/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/kaleidescape/translations/es.json b/homeassistant/components/kaleidescape/translations/es.json new file mode 100644 index 00000000000..6586a20f202 --- /dev/null +++ b/homeassistant/components/kaleidescape/translations/es.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", + "unsupported": "Dispositiu no compatible" + }, + "flow_title": "{model} ({name})", + "step": { + "discovery_confirm": { + "description": "\u00bfQuieres configurar el reproductor {name} modelo {model}?" + }, + "user": { + "data": { + "host": "Host" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index b0256167cce..4e1c3130d64 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -5,28 +5,58 @@ "single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "error": { - "cannot_connect": "Error al conectar" + "cannot_connect": "Error al conectar", + "invalid_individual_address": "El valor no coincide con el patr\u00f3n de direcci\u00f3n KNX individual. 'area.line.device'", + "invalid_ip_address": "Direcci\u00f3n IPv4 inv\u00e1lida." }, "step": { "manual_tunnel": { "data": { "host": "Host", "individual_address": "Direcci\u00f3n individual para la conexi\u00f3n", - "local_ip": "IP local de Home Assistant (d\u00e9jela vac\u00eda para la detecci\u00f3n autom\u00e1tica)", + "local_ip": "IP local de Home Assistant", "port": "Puerto", "route_back": "Modo Route Back / NAT" }, + "data_description": { + "local_ip": "D\u00e9jalo en blanco para utilizar el descubrimiento autom\u00e1tico.", + "port": "Puerto del dispositivo de tunelizaci\u00f3n KNX/IP.Puerto del dispositivo de tunelizaci\u00f3n KNX/IP." + }, "description": "Introduzca la informaci\u00f3n de conexi\u00f3n de su dispositivo de tunelizaci\u00f3n." }, "routing": { "data": { - "individual_address": "Direcci\u00f3n individual para la conexi\u00f3n de enrutamiento", - "local_ip": "IP local de Home Assistant (d\u00e9jela vac\u00eda para la detecci\u00f3n autom\u00e1tica)", + "individual_address": "Direcci\u00f3n individual", + "local_ip": "IP local de Home Assistant", "multicast_group": "El grupo de multidifusi\u00f3n utilizado para el enrutamiento", "multicast_port": "El puerto de multidifusi\u00f3n utilizado para el enrutamiento" }, "description": "Por favor, configure las opciones de enrutamiento." }, + "secure_knxkeys": { + "data": { + "knxkeys_password": "Contrase\u00f1a para descifrar el archivo `.knxkeys`." + }, + "data_description": { + "knxkeys_password": "Se ha definido durante la exportaci\u00f3n del archivo desde ETS.Se ha definido durante la exportaci\u00f3n del archivo desde ETS." + }, + "description": "Introduce la informaci\u00f3n de tu archivo `.knxkeys`." + }, + "secure_manual": { + "data": { + "user_id": "ID de usuario" + }, + "data_description": { + "user_id": "A menudo, es el n\u00famero del t\u00fanel +1. Por tanto, 'T\u00fanel 2' tendr\u00eda el ID de usuario '3'." + }, + "description": "Introduce la informaci\u00f3n de seguridad IP (IP Secure)." + }, + "secure_tunneling": { + "description": "Selecciona c\u00f3mo quieres configurar KNX/IP Secure.", + "menu_options": { + "secure_manual": "Configura manualmente las claves de seguridad IP (IP Secure)" + } + }, "tunnel": { "data": { "gateway": "Conexi\u00f3n de t\u00fanel KNX" @@ -47,11 +77,15 @@ "data": { "connection_type": "Tipo de conexi\u00f3n KNX", "individual_address": "Direcci\u00f3n individual predeterminada", - "local_ip": "IP local del Asistente Hogar (utilice 0.0.0.0 para la detecci\u00f3n autom\u00e1tica)", - "multicast_group": "Grupo de multidifusi\u00f3n utilizado para enrutamiento y descubrimiento", - "multicast_port": "Puerto de multidifusi\u00f3n utilizado para enrutamiento y descubrimiento", - "rate_limit": "M\u00e1ximo de telegramas salientes por segundo", - "state_updater": "Habilitar globalmente la lectura de estados del Bus KNX" + "local_ip": "IP local de Home Assistant", + "multicast_group": "Grupo multidifusi\u00f3n", + "multicast_port": "Puerto multidifusi\u00f3n", + "rate_limit": "Frecuencia m\u00e1xima", + "state_updater": "Actualizador de estado" + }, + "data_description": { + "individual_address": "Direcci\u00f3n KNX para utilizar con Home Assistant, ej. `0.0.4`", + "rate_limit": "Telegramas de salida m\u00e1ximos por segundo. \nRecomendado: de 20 a 40" } }, "tunnel": { @@ -60,6 +94,10 @@ "local_ip": "IP local (d\u00e9jelo en blanco si no est\u00e1 seguro)", "port": "Puerto", "route_back": "Modo Route Back / NAT" + }, + "data_description": { + "host": "Direcci\u00f3n IP del dispositivo de tunelizaci\u00f3n KNX/IP.", + "port": "Puerto del dispositivo de tunelizaci\u00f3n KNX/IP." } } } diff --git a/homeassistant/components/knx/translations/sk.json b/homeassistant/components/knx/translations/sk.json index 6668aaa92fb..347dbdfbcef 100644 --- a/homeassistant/components/knx/translations/sk.json +++ b/homeassistant/components/knx/translations/sk.json @@ -8,11 +8,21 @@ "data": { "port": "Port" } + }, + "secure_manual": { + "data": { + "device_authentication": "Heslo na overenie zariadenia" + } } } }, "options": { "step": { + "init": { + "data": { + "local_ip": "Lok\u00e1lna IP adresa Home Assistant-a" + } + }, "tunnel": { "data": { "port": "Port" diff --git a/homeassistant/components/konnected/translations/es.json b/homeassistant/components/konnected/translations/es.json index 25c79d9f5c7..1074711901c 100644 --- a/homeassistant/components/konnected/translations/es.json +++ b/homeassistant/components/konnected/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "already_in_progress": "El flujo de configuraci\u00f3n para el dispositivo ya est\u00e1 en marcha.", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "cannot_connect": "No se pudo conectar", "not_konn_panel": "No es un dispositivo Konnected.io reconocido", "unknown": "Se produjo un error desconocido" @@ -33,7 +33,7 @@ "not_konn_panel": "No es un dispositivo Konnected.io reconocido" }, "error": { - "bad_host": "URL del host de la API de invalidaci\u00f3n no v\u00e1lida" + "bad_host": "La URL de sustituci\u00f3n del host de la API es inv\u00e1lida" }, "step": { "options_binary": { @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Invalidar la direcci\u00f3n URL del host de la API (opcional)", + "api_host": "Sustituye la URL del host de la API (opcional)", "blink": "Parpadea el LED del panel cuando se env\u00eda un cambio de estado", "discovery": "Responde a las solicitudes de descubrimiento en tu red", "override_api_host": "Reemplazar la URL predeterminada del panel host de la API de Home Assistant" diff --git a/homeassistant/components/kraken/translations/sk.json b/homeassistant/components/kraken/translations/sk.json new file mode 100644 index 00000000000..ba5e13223a7 --- /dev/null +++ b/homeassistant/components/kraken/translations/sk.json @@ -0,0 +1,11 @@ +{ + "options": { + "step": { + "init": { + "data": { + "scan_interval": "Interval aktualiz\u00e1cie" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/life360/translations/es.json b/homeassistant/components/life360/translations/es.json index 019d2bc88d8..02a4a349ee9 100644 --- a/homeassistant/components/life360/translations/es.json +++ b/homeassistant/components/life360/translations/es.json @@ -8,7 +8,7 @@ "default": "Para configurar las opciones avanzadas, consulta la [documentaci\u00f3n de Life360]({docs_url})." }, "error": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", "invalid_username": "Nombre de usuario no v\u00e1lido", "unknown": "Error inesperado" diff --git a/homeassistant/components/lifx/translations/es.json b/homeassistant/components/lifx/translations/es.json index c5b157a1499..484d59ba55f 100644 --- a/homeassistant/components/lifx/translations/es.json +++ b/homeassistant/components/lifx/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "no_devices_found": "No se encontraron dispositivos LIFX en la red.", - "single_instance_allowed": "S\u00f3lo es posible una \u00fanica configuraci\u00f3n de LIFX." + "no_devices_found": "No se encontraron dispositivos en la red", + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "confirm": { diff --git a/homeassistant/components/litterrobot/translations/sensor.ca.json b/homeassistant/components/litterrobot/translations/sensor.ca.json index 72a6906f03a..dd8731b78c5 100644 --- a/homeassistant/components/litterrobot/translations/sensor.ca.json +++ b/homeassistant/components/litterrobot/translations/sensor.ca.json @@ -1,9 +1,28 @@ { "state": { "litterrobot__status_code": { + "br": "Bossa extreta", + "ccc": "Cicle de neteja completat", + "ccp": "Cicle de neteja en curs", + "csf": "Error del sensor de gat", + "csi": "Sensor de gat interromput", + "cst": "Temps del sensor de gats", + "df1": "Dip\u00f2sit gaireb\u00e9 ple - Queden 2 cicles", + "df2": "Dip\u00f2sit gaireb\u00e9 ple - Queda 1 cicle", + "dfs": "Dip\u00f2sit ple", + "dhf": "Error de posici\u00f3 d'abocament + inici", + "dpf": "Error de posici\u00f3 d'abocament", + "ec": "Cicle de buidatge", + "hpf": "Error de posici\u00f3 d'inici", "off": "OFF", "offline": "Fora de l\u00ednia", - "p": "Pausat/ada" + "otf": "Error per sobre-parell", + "p": "Pausat/ada", + "pd": "Detecci\u00f3 de pessigada", + "rdy": "A punt", + "scf": "Error del sensor de gat a l'arrencada", + "sdf": "Dip\u00f2sit ple a l'arrencada", + "spf": "Detecci\u00f3 de pessigada a l'arrencada" } } } \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.es.json b/homeassistant/components/litterrobot/translations/sensor.es.json new file mode 100644 index 00000000000..1bf023d68bc --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.es.json @@ -0,0 +1,14 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Bolsa extra\u00edda", + "ccc": "Ciclo de limpieza completado", + "ccp": "Ciclo de limpieza en curso", + "dhf": "Error de posici\u00f3n de vertido + inicio", + "ec": "Ciclo vac\u00edo", + "off": "Apagado", + "offline": "Desconectado", + "rdy": "Listo" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.it.json b/homeassistant/components/litterrobot/translations/sensor.it.json new file mode 100644 index 00000000000..926cb5d68d7 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.it.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Coperchio rimosso", + "ccc": "Ciclo di pulizia completato", + "ccp": "Ciclo di pulizia in corso", + "csf": "Errore del sensore Gatto", + "csi": "Sensore Gatto interrotto", + "cst": "Temporizzazione del sensore Gatto", + "df1": "Cassetto quasi pieno - 2 cicli rimasti", + "df2": "Cassetto quasi pieno - 1 ciclo rimasto", + "dfs": "Cassetto pieno", + "dhf": "Errore di scarico + posizione iniziale", + "dpf": "Errore di posizione di scarico", + "ec": "Ciclo vuoto", + "hpf": "Errore di posizione iniziale", + "off": "Spento", + "offline": "Non in linea", + "otf": "Errore di sovracoppia", + "p": "In pausa", + "pd": "Antipresa", + "rdy": "Pronto", + "scf": "Errore del sensore Gatto all'avvio", + "sdf": "Cassetto pieno all'avvio", + "spf": "Antipresa all'avvio" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.sk.json b/homeassistant/components/litterrobot/translations/sensor.sk.json new file mode 100644 index 00000000000..b4c5c43292d --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.sk.json @@ -0,0 +1,8 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "Vypnut\u00fd", + "rdy": "Pripraven\u00fd" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.tr.json b/homeassistant/components/litterrobot/translations/sensor.tr.json new file mode 100644 index 00000000000..2db5e574f7e --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.tr.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Kapak \u00c7\u0131kar\u0131ld\u0131", + "ccc": "Temizleme Tamamland\u0131", + "ccp": "Temizleme Devam Ediyor", + "csf": "Kedi Sens\u00f6r\u00fc Hatas\u0131", + "csi": "Kedi Sens\u00f6r\u00fc Kesildi", + "cst": "Kedi Sens\u00f6r Zamanlamas\u0131", + "df1": "Hazne Neredeyse Dolu - 2 kez daha s\u00fcp\u00fcrebilir", + "df2": "Hazne Neredeyse Dolu - 1 kez daha s\u00fcp\u00fcrebilir", + "dfs": "Hazne Dolu", + "dhf": "Bo\u015faltma + Ana Konum Hatas\u0131", + "dpf": "Bo\u015faltma Konumu Hatas\u0131", + "ec": "Bo\u015f D\u00f6ng\u00fc", + "hpf": "Ev Konumu Hatas\u0131", + "off": "Kapal\u0131", + "offline": "\u00c7evrimd\u0131\u015f\u0131", + "otf": "A\u015f\u0131r\u0131 Tork Ar\u0131zas\u0131", + "p": "Durduruldu", + "pd": "S\u0131k\u0131\u015fma Alg\u0131lama", + "rdy": "Haz\u0131r", + "scf": "Ba\u015flang\u0131\u00e7ta Cat Sens\u00f6r\u00fc Hatas\u0131", + "sdf": "Ba\u015flang\u0131\u00e7ta Hazne Dolu", + "spf": "Ba\u015flang\u0131\u00e7ta S\u0131k\u0131\u015fma Alg\u0131lama" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/locative/translations/es.json b/homeassistant/components/locative/translations/es.json index 9fa02248d42..e3c63d7b35d 100644 --- a/homeassistant/components/locative/translations/es.json +++ b/homeassistant/components/locative/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, diff --git a/homeassistant/components/logi_circle/translations/es.json b/homeassistant/components/logi_circle/translations/es.json index 6bd99289230..7fe62a28d05 100644 --- a/homeassistant/components/logi_circle/translations/es.json +++ b/homeassistant/components/logi_circle/translations/es.json @@ -1,13 +1,13 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "external_error": "Se produjo una excepci\u00f3n de otro flujo.", "external_setup": "Logi Circle se ha configurado correctamente a partir de otro flujo.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n." }, "error": { - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "follow_link": "Accede al enlace e identif\u00edcate antes de pulsar Enviar.", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, diff --git a/homeassistant/components/luftdaten/translations/es.json b/homeassistant/components/luftdaten/translations/es.json index bc33a13d870..99e5f7d5058 100644 --- a/homeassistant/components/luftdaten/translations/es.json +++ b/homeassistant/components/luftdaten/translations/es.json @@ -9,7 +9,7 @@ "user": { "data": { "show_on_map": "Mostrar en el mapa", - "station_id": "Sensro ID de Luftdaten" + "station_id": "ID del sensor" }, "title": "Definir Luftdaten" } diff --git a/homeassistant/components/lutron_caseta/translations/es.json b/homeassistant/components/lutron_caseta/translations/es.json index 098a90377d8..d13fded562e 100644 --- a/homeassistant/components/lutron_caseta/translations/es.json +++ b/homeassistant/components/lutron_caseta/translations/es.json @@ -8,10 +8,10 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "Lutron Cas\u00e9ta {name} ({host})", + "flow_title": "{name} ({host})", "step": { "import_failed": { - "description": "No se puede configurar bridge (host: {host}) importado desde configuration.yaml.", + "description": "No se ha podido configurar el enlace (anfitri\u00f3n: {host}) importado de configuration.yaml.", "title": "Error al importar la configuraci\u00f3n del bridge Cas\u00e9ta." }, "link": { diff --git a/homeassistant/components/lyric/translations/es.json b/homeassistant/components/lyric/translations/es.json index 404a812e676..12692849ce3 100644 --- a/homeassistant/components/lyric/translations/es.json +++ b/homeassistant/components/lyric/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, diff --git a/homeassistant/components/mailgun/translations/es.json b/homeassistant/components/mailgun/translations/es.json index 50290059f54..1fcd54a5266 100644 --- a/homeassistant/components/mailgun/translations/es.json +++ b/homeassistant/components/mailgun/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, diff --git a/homeassistant/components/mazda/translations/es.json b/homeassistant/components/mazda/translations/es.json index f0ba0f4da49..01140eb3aad 100644 --- a/homeassistant/components/mazda/translations/es.json +++ b/homeassistant/components/mazda/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/meater/translations/es.json b/homeassistant/components/meater/translations/es.json new file mode 100644 index 00000000000..39d35b38d4a --- /dev/null +++ b/homeassistant/components/meater/translations/es.json @@ -0,0 +1,19 @@ +{ + "config": { + "error": { + "service_unavailable_error": "La API no est\u00e1 disponible actualmente, vuelva a intentarlo m\u00e1s tarde.", + "unknown_auth_error": "Error inesperado" + }, + "step": { + "reauth_confirm": { + "description": "Confirma la contrase\u00f1a de la cuenta de Meater Cloud {username}." + }, + "user": { + "data": { + "password": "Contrase\u00f1a", + "username": "Usuario" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/sk.json b/homeassistant/components/meater/translations/sk.json new file mode 100644 index 00000000000..a52d3b46e7c --- /dev/null +++ b/homeassistant/components/meater/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "service_unavailable_error": "Rozhranie API je moment\u00e1lne nedostupn\u00e9, sk\u00faste to pros\u00edm nesk\u00f4r." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/media_player/translations/es.json b/homeassistant/components/media_player/translations/es.json index 0dfc063a035..fd60d09f562 100644 --- a/homeassistant/components/media_player/translations/es.json +++ b/homeassistant/components/media_player/translations/es.json @@ -1,6 +1,7 @@ { "device_automation": { "condition_type": { + "is_buffering": "{entity_name} se est\u00e1 cargando en memoria", "is_idle": "{entity_name} est\u00e1 inactivo", "is_off": "{entity_name} est\u00e1 apagado", "is_on": "{entity_name} est\u00e1 activado", @@ -8,6 +9,7 @@ "is_playing": "{entity_name} est\u00e1 reproduciendo" }, "trigger_type": { + "buffering": "{entity_name} comienza a cargarse en memoria", "changed_states": "{entity_name} ha cambiado de estado", "idle": "{entity_name} est\u00e1 inactivo", "paused": "{entity_name} est\u00e1 en pausa", @@ -18,12 +20,13 @@ }, "state": { "_": { + "buffering": "Cargando", "idle": "Inactivo", "off": "Apagado", "on": "Encendido", "paused": "En pausa", "playing": "Reproduciendo", - "standby": "Apagado" + "standby": "En espera" } }, "title": "Reproductor multimedia" diff --git a/homeassistant/components/melcloud/translations/es.json b/homeassistant/components/melcloud/translations/es.json index caba17be17a..be4f6cabe6c 100644 --- a/homeassistant/components/melcloud/translations/es.json +++ b/homeassistant/components/melcloud/translations/es.json @@ -4,7 +4,7 @@ "already_configured": "Integraci\u00f3n mELCloud ya configurada para este correo electr\u00f3nico. Se ha actualizado el token de acceso." }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntelo de nuevo.", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "invalid_auth": "Autentificaci\u00f3n inv\u00e1lida", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/meteo_france/translations/es.json b/homeassistant/components/meteo_france/translations/es.json index 8843d301779..cd6d2d80812 100644 --- a/homeassistant/components/meteo_france/translations/es.json +++ b/homeassistant/components/meteo_france/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La ciudad ya est\u00e1 configurada", + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada", "unknown": "Error desconocido: por favor, vuelva a intentarlo m\u00e1s tarde" }, "error": { diff --git a/homeassistant/components/metoffice/translations/es.json b/homeassistant/components/metoffice/translations/es.json index 098e8fb0335..5751db1f760 100644 --- a/homeassistant/components/metoffice/translations/es.json +++ b/homeassistant/components/metoffice/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado" + "already_configured": "El servicio ya est\u00e1 configurado" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/mill/translations/es.json b/homeassistant/components/mill/translations/es.json index 4f2ddd29651..104c9d37d84 100644 --- a/homeassistant/components/mill/translations/es.json +++ b/homeassistant/components/mill/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar" diff --git a/homeassistant/components/min_max/translations/es.json b/homeassistant/components/min_max/translations/es.json new file mode 100644 index 00000000000..ffe5217b1f9 --- /dev/null +++ b/homeassistant/components/min_max/translations/es.json @@ -0,0 +1,39 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "Entidades de entrada", + "name": "Nombre", + "type": "Caracter\u00edstica estad\u00edstica" + }, + "data_description": { + "round_digits": "Controla el n\u00famero de d\u00edgitos decimales cuando la caracter\u00edstica estad\u00edstica es la media o mediana." + }, + "title": "Agregar sensor m\u00edn / m\u00e1x / media / mediana" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "Entidades de entrada", + "round_digits": "Precisi\u00f3n", + "type": "Caracter\u00edstica estad\u00edstica" + }, + "data_description": { + "round_digits": "Controla el n\u00famero de d\u00edgitos decimales cuando la caracter\u00edstica estad\u00edstica es la media o mediana." + } + }, + "options": { + "data": { + "entity_ids": "Entidades de entrada", + "round_digits": "Precisi\u00f3n", + "type": "Caracter\u00edstica estad\u00edstica" + } + } + } + }, + "title": "Sensor m\u00edn / m\u00e1x / media / mediana" +} \ No newline at end of file diff --git a/homeassistant/components/moehlenhoff_alpha2/translations/es.json b/homeassistant/components/moehlenhoff_alpha2/translations/es.json index 81e3d96b7bc..12821ae906d 100644 --- a/homeassistant/components/moehlenhoff_alpha2/translations/es.json +++ b/homeassistant/components/moehlenhoff_alpha2/translations/es.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "host": "Anfitri\u00f3n" + "host": "Host" } } } diff --git a/homeassistant/components/motion_blinds/translations/tr.json b/homeassistant/components/motion_blinds/translations/tr.json index 824569ce173..f53b251a8ec 100644 --- a/homeassistant/components/motion_blinds/translations/tr.json +++ b/homeassistant/components/motion_blinds/translations/tr.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f , ba\u011flant\u0131 ayarlar\u0131 g\u00fcncellendi", + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "connection_error": "Ba\u011flanma hatas\u0131" }, diff --git a/homeassistant/components/myq/translations/es.json b/homeassistant/components/myq/translations/es.json index 08d2ccbee73..98d02b56280 100644 --- a/homeassistant/components/myq/translations/es.json +++ b/homeassistant/components/myq/translations/es.json @@ -6,7 +6,7 @@ }, "error": { "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo.", - "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", "unknown": "Error inesperado" }, "step": { diff --git a/homeassistant/components/nam/translations/es.json b/homeassistant/components/nam/translations/es.json index 81e83c4376b..feeff2015f9 100644 --- a/homeassistant/components/nam/translations/es.json +++ b/homeassistant/components/nam/translations/es.json @@ -14,7 +14,7 @@ "flow_title": "{host}", "step": { "confirm_discovery": { - "description": "\u00bfQuieres configurar Nettigo Air Monitor en {host} ?" + "description": "\u00bfQuieres configurar Nettigo Air Monitor en {host}?" }, "credentials": { "data": { diff --git a/homeassistant/components/nanoleaf/translations/es.json b/homeassistant/components/nanoleaf/translations/es.json index 605997c6439..187517ed478 100644 --- a/homeassistant/components/nanoleaf/translations/es.json +++ b/homeassistant/components/nanoleaf/translations/es.json @@ -24,5 +24,10 @@ } } } + }, + "device_automation": { + "trigger_type": { + "swipe_down": "Desliza hacia abajo" + } } } \ No newline at end of file diff --git a/homeassistant/components/nanoleaf/translations/sk.json b/homeassistant/components/nanoleaf/translations/sk.json index c2f015fe339..c34ad96714a 100644 --- a/homeassistant/components/nanoleaf/translations/sk.json +++ b/homeassistant/components/nanoleaf/translations/sk.json @@ -3,5 +3,10 @@ "abort": { "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" } + }, + "device_automation": { + "trigger_type": { + "swipe_right": "Potiahnite prstom doprava" + } } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/es.json b/homeassistant/components/neato/translations/es.json index 64c58279614..bcb9dc3619e 100644 --- a/homeassistant/components/neato/translations/es.json +++ b/homeassistant/components/neato/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" diff --git a/homeassistant/components/nest/translations/es.json b/homeassistant/components/nest/translations/es.json index 2921b0272f8..ea65d2fc78a 100644 --- a/homeassistant/components/nest/translations/es.json +++ b/homeassistant/components/nest/translations/es.json @@ -18,7 +18,7 @@ "invalid_pin": "C\u00f3digo PIN no v\u00e1lido", "subscriber_error": "Error de abonado desconocido, ver registros", "timeout": "Tiempo de espera agotado validando el c\u00f3digo", - "unknown": "Error desconocido validando el c\u00f3digo", + "unknown": "Error inesperado", "wrong_project_id": "Por favor, introduzca un ID de proyecto Cloud v\u00e1lido (encontr\u00f3 el ID de proyecto de acceso al dispositivo)" }, "step": { @@ -33,7 +33,7 @@ "data": { "flow_impl": "Proveedor" }, - "description": "Elija a trav\u00e9s de qu\u00e9 proveedor de autenticaci\u00f3n desea autenticarse con Nest.", + "description": "Selecciona el m\u00e9todo de autenticaci\u00f3n", "title": "Proveedor de autenticaci\u00f3n" }, "link": { diff --git a/homeassistant/components/nest/translations/sk.json b/homeassistant/components/nest/translations/sk.json index 029432864bf..43b57c320d4 100644 --- a/homeassistant/components/nest/translations/sk.json +++ b/homeassistant/components/nest/translations/sk.json @@ -6,6 +6,9 @@ "create_entry": { "default": "\u00daspe\u0161ne overen\u00e9" }, + "error": { + "invalid_pin": "Nespr\u00e1vny PIN" + }, "step": { "auth": { "data": { diff --git a/homeassistant/components/netatmo/translations/es.json b/homeassistant/components/netatmo/translations/es.json index 15b67d4b32b..dd9fcf18d60 100644 --- a/homeassistant/components/netatmo/translations/es.json +++ b/homeassistant/components/netatmo/translations/es.json @@ -47,9 +47,9 @@ "public_weather": { "data": { "area_name": "Nombre del \u00e1rea", - "lat_ne": "Latitud esquina Noreste", - "lat_sw": "Latitud esquina Suroeste", - "lon_ne": "Longitud esquina Noreste", + "lat_ne": "Longitud esquina noreste", + "lat_sw": "Latitud esquina suroeste", + "lon_ne": "Longitud esquina noreste", "lon_sw": "Longitud esquina Suroeste", "mode": "C\u00e1lculo", "show_on_map": "Mostrar en el mapa" diff --git a/homeassistant/components/netatmo/translations/tr.json b/homeassistant/components/netatmo/translations/tr.json index d40de61e36a..807497e9d60 100644 --- a/homeassistant/components/netatmo/translations/tr.json +++ b/homeassistant/components/netatmo/translations/tr.json @@ -60,7 +60,7 @@ "public_weather_areas": { "data": { "new_area": "Alan ad\u0131", - "weather_areas": "Hava alanlar\u0131" + "weather_areas": "Hava b\u00f6lgeleri" }, "description": "Genel hava durumu sens\u00f6rlerini yap\u0131land\u0131r\u0131n.", "title": "Netatmo genel hava durumu sens\u00f6r\u00fc" diff --git a/homeassistant/components/netgear/translations/es.json b/homeassistant/components/netgear/translations/es.json index 7aadc51efb1..e1edb726d52 100644 --- a/homeassistant/components/netgear/translations/es.json +++ b/homeassistant/components/netgear/translations/es.json @@ -15,7 +15,7 @@ "ssl": "Utiliza un certificado SSL", "username": "Usuario (Opcional)" }, - "description": "Host predeterminado: {host}\nPuerto predeterminado: {port}\nNombre de usuario predeterminado: {username}", + "description": "Host predeterminado: {host} \nNombre de usuario predeterminado: {username}", "title": "Netgear" } } diff --git a/homeassistant/components/nexia/translations/es.json b/homeassistant/components/nexia/translations/es.json index 1698b8db1d1..4306a8795b8 100644 --- a/homeassistant/components/nexia/translations/es.json +++ b/homeassistant/components/nexia/translations/es.json @@ -4,7 +4,7 @@ "already_configured": "Este nexia home ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo.", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/nmap_tracker/translations/es.json b/homeassistant/components/nmap_tracker/translations/es.json index 212b56a9606..e68d8ff59ab 100644 --- a/homeassistant/components/nmap_tracker/translations/es.json +++ b/homeassistant/components/nmap_tracker/translations/es.json @@ -11,7 +11,7 @@ "data": { "exclude": "Direcciones de red (separadas por comas) para excluir del escaneo", "home_interval": "N\u00famero m\u00ednimo de minutos entre los escaneos de los dispositivos activos (preservar la bater\u00eda)", - "hosts": "Direcciones de red (separadas por comas) para escanear", + "hosts": "Direcciones de red a escanear (separadas por comas)", "scan_options": "Opciones de escaneo configurables sin procesar para Nmap" }, "description": "Configure los hosts que ser\u00e1n escaneados por Nmap. Las direcciones de red y los excluidos pueden ser direcciones IP (192.168.1.1), redes IP (192.168.0.0/24) o rangos IP (192.168.1.0-32)." @@ -28,7 +28,7 @@ "consider_home": "Segundos de espera hasta que se marca un dispositivo de seguimiento como no en casa despu\u00e9s de no ser visto.", "exclude": "Direcciones de red (separadas por comas) para excluir del escaneo", "home_interval": "N\u00famero m\u00ednimo de minutos entre los escaneos de los dispositivos activos (preservar la bater\u00eda)", - "hosts": "Direcciones de red (separadas por comas) para escanear", + "hosts": "Direcciones de red a escanear (separadas por comas)", "interval_seconds": "Intervalo de exploraci\u00f3n", "scan_options": "Opciones de escaneo configurables sin procesar para Nmap", "track_new_devices": "Seguimiento de nuevos dispositivos" diff --git a/homeassistant/components/notion/translations/es.json b/homeassistant/components/notion/translations/es.json index b2012363f63..b537bd5bd3b 100644 --- a/homeassistant/components/notion/translations/es.json +++ b/homeassistant/components/notion/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/nzbget/translations/es.json b/homeassistant/components/nzbget/translations/es.json index cc89bdd2469..e3b1a595c90 100644 --- a/homeassistant/components/nzbget/translations/es.json +++ b/homeassistant/components/nzbget/translations/es.json @@ -7,7 +7,7 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "NZBGet: {name}", + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/oncue/translations/sk.json b/homeassistant/components/oncue/translations/sk.json index 5ada995aa6e..82a91905e7c 100644 --- a/homeassistant/components/oncue/translations/sk.json +++ b/homeassistant/components/oncue/translations/sk.json @@ -1,7 +1,8 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" } } } \ No newline at end of file diff --git a/homeassistant/components/ondilo_ico/translations/es.json b/homeassistant/components/ondilo_ico/translations/es.json index db8d744d176..1de8399aced 100644 --- a/homeassistant/components/ondilo_ico/translations/es.json +++ b/homeassistant/components/ondilo_ico/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n." }, "create_entry": { diff --git a/homeassistant/components/onewire/translations/es.json b/homeassistant/components/onewire/translations/es.json index 9879ccb0a00..cb13e1992bd 100644 --- a/homeassistant/components/onewire/translations/es.json +++ b/homeassistant/components/onewire/translations/es.json @@ -17,6 +17,7 @@ }, "user": { "data": { + "host": "Host", "port": "Puerto", "type": "Tipo de conexi\u00f3n" }, @@ -27,6 +28,24 @@ "options": { "error": { "device_not_selected": "Seleccionar los dispositivos a configurar" + }, + "step": { + "ack_no_options": { + "title": "Opciones SysBus de OneWire" + }, + "configure_device": { + "data": { + "precision": "Precisi\u00f3n del sensor" + }, + "description": "Selecciona la precisi\u00f3n del sensor {sensor_id}" + }, + "device_selection": { + "data": { + "clear_device_options": "Borra todas las configuraciones de dispositivo" + }, + "description": "Seleccione los pasos de configuraci\u00f3n a procesar", + "title": "Opciones de dispositivo OneWire" + } } } } \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/tr.json b/homeassistant/components/onewire/translations/tr.json index 6fea23cfc76..4859b5ec865 100644 --- a/homeassistant/components/onewire/translations/tr.json +++ b/homeassistant/components/onewire/translations/tr.json @@ -17,9 +17,11 @@ }, "user": { "data": { + "host": "Sunucu", + "port": "Port", "type": "Ba\u011flant\u0131 t\u00fcr\u00fc" }, - "title": "1-Wire'\u0131 kurun" + "title": "Sunucu ayr\u0131nt\u0131lar\u0131n\u0131 ayarla" } } }, diff --git a/homeassistant/components/opentherm_gw/translations/es.json b/homeassistant/components/opentherm_gw/translations/es.json index d780548a8fa..8c773c8c1e2 100644 --- a/homeassistant/components/opentherm_gw/translations/es.json +++ b/homeassistant/components/opentherm_gw/translations/es.json @@ -1,7 +1,7 @@ { "config": { "error": { - "already_configured": "Gateway ya configurado", + "already_configured": "El dispositivo ya est\u00e1 configurado", "cannot_connect": "No se pudo conectar", "id_exists": "El ID del Gateway ya existe" }, diff --git a/homeassistant/components/openuv/translations/es.json b/homeassistant/components/openuv/translations/es.json index 014eba04f52..bb5f36499ba 100644 --- a/homeassistant/components/openuv/translations/es.json +++ b/homeassistant/components/openuv/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Estas coordenadas ya est\u00e1n registradas." + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada" }, "error": { "invalid_api_key": "Clave API no v\u00e1lida" diff --git a/homeassistant/components/openweathermap/translations/es.json b/homeassistant/components/openweathermap/translations/es.json index 8a44668e8ac..5d276f60cb6 100644 --- a/homeassistant/components/openweathermap/translations/es.json +++ b/homeassistant/components/openweathermap/translations/es.json @@ -15,9 +15,9 @@ "latitude": "Latitud", "longitude": "Longitud", "mode": "Modo", - "name": "Nombre de la integraci\u00f3n" + "name": "Nombre" }, - "description": "Configurar la integraci\u00f3n de OpenWeatherMap. Para generar la clave API, ve a https://openweathermap.org/appid", + "description": "Para generar la clave API, ve a https://openweathermap.org/appid", "title": "OpenWeatherMap" } } diff --git a/homeassistant/components/overkiz/translations/es.json b/homeassistant/components/overkiz/translations/es.json index f1702a6d703..817864deba8 100644 --- a/homeassistant/components/overkiz/translations/es.json +++ b/homeassistant/components/overkiz/translations/es.json @@ -1,12 +1,15 @@ { "config": { "abort": { - "already_configured": "La cuenta ya est\u00e1 configurada" + "already_configured": "La cuenta ya est\u00e1 configurada", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente", + "reauth_wrong_account": "S\u00f3lo puedes volver a autenticar esta entrada con la misma cuenta y hub de Overkiz" }, "error": { "cannot_connect": "Error al conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "server_in_maintenance": "El servidor est\u00e1 inactivo por mantenimiento", + "too_many_attempts": "Demasiados intentos con un 'token' inv\u00e1lido, bloqueado temporalmente", "too_many_requests": "Demasiadas solicitudes, int\u00e9ntalo de nuevo m\u00e1s tarde.", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/overkiz/translations/select.sk.json b/homeassistant/components/overkiz/translations/select.sk.json new file mode 100644 index 00000000000..460b1d67415 --- /dev/null +++ b/homeassistant/components/overkiz/translations/select.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "overkiz__open_closed_pedestrian": { + "pedestrian": "Chodec" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sensor.sk.json b/homeassistant/components/overkiz/translations/sensor.sk.json new file mode 100644 index 00000000000..cfa849319eb --- /dev/null +++ b/homeassistant/components/overkiz/translations/sensor.sk.json @@ -0,0 +1,10 @@ +{ + "state": { + "overkiz__discrete_rssi_level": { + "good": "Dobr\u00e1" + }, + "overkiz__priority_lock_originator": { + "wind": "Vietor" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/overkiz/translations/sk.json b/homeassistant/components/overkiz/translations/sk.json index 71a7aea5018..93eb8dcc1b5 100644 --- a/homeassistant/components/overkiz/translations/sk.json +++ b/homeassistant/components/overkiz/translations/sk.json @@ -5,6 +5,13 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/ovo_energy/translations/es.json b/homeassistant/components/ovo_energy/translations/es.json index aa9708c60d0..549f3af16ca 100644 --- a/homeassistant/components/ovo_energy/translations/es.json +++ b/homeassistant/components/ovo_energy/translations/es.json @@ -5,7 +5,7 @@ "cannot_connect": "No se pudo conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, - "flow_title": "OVO Energy: {username}", + "flow_title": "{username}", "step": { "reauth": { "data": { @@ -20,7 +20,7 @@ "username": "Usuario" }, "description": "Configurar una instancia de OVO Energy para acceder a su consumo de energ\u00eda.", - "title": "A\u00f1adir OVO Energy" + "title": "A\u00f1adir cuenta de OVO Energy" } } } diff --git a/homeassistant/components/owntracks/translations/es.json b/homeassistant/components/owntracks/translations/es.json index 1832e701559..dc420a806f4 100644 --- a/homeassistant/components/owntracks/translations/es.json +++ b/homeassistant/components/owntracks/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "create_entry": { diff --git a/homeassistant/components/peco/translations/es.json b/homeassistant/components/peco/translations/es.json new file mode 100644 index 00000000000..ecfb586561c --- /dev/null +++ b/homeassistant/components/peco/translations/es.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "already_configured": "El servicio ya est\u00e1 configurado" + }, + "step": { + "user": { + "data": { + "county": "Condado" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/person/translations/es.json b/homeassistant/components/person/translations/es.json index 98fca470569..6576d02bb5e 100644 --- a/homeassistant/components/person/translations/es.json +++ b/homeassistant/components/person/translations/es.json @@ -2,7 +2,7 @@ "state": { "_": { "home": "En casa", - "not_home": "Fuera de casa" + "not_home": "Fuera" } }, "title": "Persona" diff --git a/homeassistant/components/plaato/translations/es.json b/homeassistant/components/plaato/translations/es.json index d38bf2a8265..693c6bf99c7 100644 --- a/homeassistant/components/plaato/translations/es.json +++ b/homeassistant/components/plaato/translations/es.json @@ -1,12 +1,13 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "\u00a1Tu Plaato {device_type} con nombre **{device_name}** se configur\u00f3 correctamente!" + "default": "\u00a1El dispositivo Plaato {device_type} con nombre **{device_name}** se ha configurado correctamente!" }, "error": { "invalid_webhook_device": "Has seleccionado un dispositivo que no admite el env\u00edo de datos a un webhook. Solo est\u00e1 disponible para Airlock", @@ -27,8 +28,8 @@ "device_name": "Nombre de su dispositivo", "device_type": "Tipo de dispositivo Plaato" }, - "description": "\u00bfEst\u00e1s seguro de que quieres configurar el Airlock de Plaato?", - "title": "Configurar el webhook de Plaato" + "description": "\u00bfQuieres empezar la configuraci\u00f3n?", + "title": "Configura dispositivos Plaato" }, "webhook": { "description": "Para enviar eventos a Home Assistant, deber\u00e1 configurar la funci\u00f3n de webhook en Plaato Airlock. \n\n Complete la siguiente informaci\u00f3n: \n\n - URL: `{webhook_url}`\n - M\u00e9todo: POST \n\n Consulte [la documentaci\u00f3n]({docs_url}) para obtener m\u00e1s detalles.", diff --git a/homeassistant/components/plex/translations/es.json b/homeassistant/components/plex/translations/es.json index c31dcf26e07..38f57b91c7b 100644 --- a/homeassistant/components/plex/translations/es.json +++ b/homeassistant/components/plex/translations/es.json @@ -3,10 +3,10 @@ "abort": { "all_configured": "Todos los servidores vinculados ya configurados", "already_configured": "Este servidor Plex ya est\u00e1 configurado", - "already_in_progress": "Plex se est\u00e1 configurando", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", "token_request_timeout": "Tiempo de espera agotado para la obtenci\u00f3n del token", - "unknown": "Fall\u00f3 por razones desconocidas" + "unknown": "Error inesperado" }, "error": { "faulty_credentials": "La autorizaci\u00f3n ha fallado, verifica el token", diff --git a/homeassistant/components/plugwise/translations/es.json b/homeassistant/components/plugwise/translations/es.json index 8f3ce70cb88..e1d1bf3359b 100644 --- a/homeassistant/components/plugwise/translations/es.json +++ b/homeassistant/components/plugwise/translations/es.json @@ -4,17 +4,17 @@ "already_configured": "El servicio ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntelo de nuevo.", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida, comprueba los 8 caracteres de tu Smile ID", "unknown": "Error inesperado" }, - "flow_title": "Smile: {name}", + "flow_title": "{name}", "step": { "user": { "data": { "flow_type": "Tipo de conexi\u00f3n" }, - "description": "Detalles", + "description": "Producto:", "title": "Conectarse a Smile" }, "user_gateway": { diff --git a/homeassistant/components/plum_lightpad/translations/es.json b/homeassistant/components/plum_lightpad/translations/es.json index b5f3c8b1439..c22321bc0b7 100644 --- a/homeassistant/components/plum_lightpad/translations/es.json +++ b/homeassistant/components/plum_lightpad/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar" diff --git a/homeassistant/components/point/translations/es.json b/homeassistant/components/point/translations/es.json index c495a4fe3bd..3d1f47c2ceb 100644 --- a/homeassistant/components/point/translations/es.json +++ b/homeassistant/components/point/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_setup": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "external_setup": "Point se ha configurado correctamente a partir de otro flujo.", "no_flows": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "unknown_authorize_url_generation": "Error desconocido al generar una URL de autorizaci\u00f3n." diff --git a/homeassistant/components/poolsense/translations/es.json b/homeassistant/components/poolsense/translations/es.json index e2e5272f7ac..2d38dd670d3 100644 --- a/homeassistant/components/poolsense/translations/es.json +++ b/homeassistant/components/poolsense/translations/es.json @@ -12,7 +12,7 @@ "email": "Correo electr\u00f3nico", "password": "Contrase\u00f1a" }, - "description": "[%key:common::config_flow::description%]", + "description": "\u00bfQuieres empezar la configuraci\u00f3n?", "title": "PoolSense" } } diff --git a/homeassistant/components/powerwall/translations/es.json b/homeassistant/components/powerwall/translations/es.json index 5d82ecda8e5..c0f73c2ac99 100644 --- a/homeassistant/components/powerwall/translations/es.json +++ b/homeassistant/components/powerwall/translations/es.json @@ -12,6 +12,9 @@ }, "flow_title": "Powerwall de Tesla ({ip_address})", "step": { + "confirm_discovery": { + "description": "\u00bfQuieres configurar {name} ({ip_address})?" + }, "reauth_confim": { "data": { "password": "Contrase\u00f1a" diff --git a/homeassistant/components/ps4/translations/es.json b/homeassistant/components/ps4/translations/es.json index f65a27c61b6..7a807953b5e 100644 --- a/homeassistant/components/ps4/translations/es.json +++ b/homeassistant/components/ps4/translations/es.json @@ -20,7 +20,7 @@ }, "link": { "data": { - "code": "PIN", + "code": "C\u00f3digo PIN", "ip_address": "Direcci\u00f3n IP", "name": "Nombre", "region": "Regi\u00f3n" diff --git a/homeassistant/components/pure_energie/translations/es.json b/homeassistant/components/pure_energie/translations/es.json index eb5d98c7ebf..9725448be66 100644 --- a/homeassistant/components/pure_energie/translations/es.json +++ b/homeassistant/components/pure_energie/translations/es.json @@ -11,7 +11,7 @@ "step": { "user": { "data": { - "host": "Anfitri\u00f3n" + "host": "Host" } }, "zeroconf_confirm": { diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/es.json b/homeassistant/components/pvpc_hourly_pricing/translations/es.json index 4af0de8f594..ec5634a160e 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/es.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/es.json @@ -9,10 +9,10 @@ "name": "Nombre del sensor", "power": "Potencia contratada (kW)", "power_p3": "Potencia contratada para el per\u00edodo valle P3 (kW)", - "tariff": "Tarifa contratada (1, 2 o 3 per\u00edodos)" + "tariff": "Tarifa aplicable por zona geogr\u00e1fica" }, "description": "Este sensor utiliza la API oficial para obtener [el precio horario de la electricidad (PVPC)](https://www.esios.ree.es/es/pvpc) en Espa\u00f1a.\nPara obtener una explicaci\u00f3n m\u00e1s precisa, visita los [documentos de la integraci\u00f3n](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).\n\nSelecciona la tarifa contratada en funci\u00f3n del n\u00famero de per\u00edodos de facturaci\u00f3n por d\u00eda:\n- 1 per\u00edodo: normal\n- 2 per\u00edodos: discriminaci\u00f3n (tarifa nocturna)\n- 3 per\u00edodos: coche el\u00e9ctrico (tarifa nocturna de 3 per\u00edodos)", - "title": "Selecci\u00f3n de tarifa" + "title": "Configuraci\u00f3n del sensor" } } }, diff --git a/homeassistant/components/qnap_qsw/translations/es.json b/homeassistant/components/qnap_qsw/translations/es.json new file mode 100644 index 00000000000..b58fcb71fc7 --- /dev/null +++ b/homeassistant/components/qnap_qsw/translations/es.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "invalid_id": "El dispositivo ha devuelto un ID \u00fanico inv\u00e1lido" + }, + "error": { + "cannot_connect": "Ha fallado la conexi\u00f3n", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "password": "Contrase\u00f1a", + "url": "URL", + "username": "Nombre de usuario" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/rachio/translations/es.json b/homeassistant/components/rachio/translations/es.json index 7e4a03c138a..f3821b7aa5c 100644 --- a/homeassistant/components/rachio/translations/es.json +++ b/homeassistant/components/rachio/translations/es.json @@ -4,8 +4,8 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo.", - "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", "unknown": "Error inesperado" }, "step": { @@ -22,7 +22,7 @@ "step": { "init": { "data": { - "manual_run_mins": "Durante cu\u00e1nto tiempo, en minutos, permanece encendida una estaci\u00f3n cuando el interruptor est\u00e1 activado." + "manual_run_mins": "Duraci\u00f3n en minutos a ejecutar cuando se active un interruptor de zona" } } } diff --git a/homeassistant/components/radio_browser/translations/es.json b/homeassistant/components/radio_browser/translations/es.json index 6bb377d2d28..aa8e6336550 100644 --- a/homeassistant/components/radio_browser/translations/es.json +++ b/homeassistant/components/radio_browser/translations/es.json @@ -2,6 +2,11 @@ "config": { "abort": { "single_instance_allowed": "Ya est\u00e1 configurado. Solamente una configuraci\u00f3n es posible." + }, + "step": { + "user": { + "description": "\u00bfQuieres a\u00f1adir el navegador radio a Home Assistant?" + } } } } \ No newline at end of file diff --git a/homeassistant/components/rainmachine/translations/es.json b/homeassistant/components/rainmachine/translations/es.json index 317339ed39f..3e13d925b34 100644 --- a/homeassistant/components/rainmachine/translations/es.json +++ b/homeassistant/components/rainmachine/translations/es.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "ip_address": "Nombre de host o direcci\u00f3n IP", + "ip_address": "Nombre del host o direcci\u00f3n IP", "password": "Contrase\u00f1a", "port": "Puerto" }, diff --git a/homeassistant/components/recorder/translations/es.json b/homeassistant/components/recorder/translations/es.json new file mode 100644 index 00000000000..81bcf29d548 --- /dev/null +++ b/homeassistant/components/recorder/translations/es.json @@ -0,0 +1,8 @@ +{ + "system_health": { + "info": { + "current_recorder_run": "Hora de inicio de la ejecuci\u00f3n actual", + "estimated_db_size": "Mida estimada de la base de datos (MiB)" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/tr.json b/homeassistant/components/recorder/translations/tr.json index 80fd4fd45be..1fdc0e408b3 100644 --- a/homeassistant/components/recorder/translations/tr.json +++ b/homeassistant/components/recorder/translations/tr.json @@ -2,6 +2,7 @@ "system_health": { "info": { "current_recorder_run": "Mevcut \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131", + "estimated_db_size": "Tahmini Veritaban\u0131 Boyutu (MB)", "oldest_recorder_run": "En Eski \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131" } } diff --git a/homeassistant/components/renault/translations/es.json b/homeassistant/components/renault/translations/es.json index cf0f88983e0..1d8b5f447e3 100644 --- a/homeassistant/components/renault/translations/es.json +++ b/homeassistant/components/renault/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "kamereon_no_account": "No se pudo encontrar la cuenta de Kamereon.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, diff --git a/homeassistant/components/rfxtrx/translations/es.json b/homeassistant/components/rfxtrx/translations/es.json index fa45fe8a777..ec7777ee0d4 100644 --- a/homeassistant/components/rfxtrx/translations/es.json +++ b/homeassistant/components/rfxtrx/translations/es.json @@ -61,6 +61,7 @@ "debug": "Activar la depuraci\u00f3n", "device": "Seleccionar dispositivo para configurar", "event_code": "Introducir el c\u00f3digo de evento para a\u00f1adir", + "protocols": "Protocolos", "remove_device": "Selecciona el dispositivo a eliminar" }, "title": "Opciones de Rfxtrx" diff --git a/homeassistant/components/risco/translations/es.json b/homeassistant/components/risco/translations/es.json index 68e7031c102..6f85fe0c4ab 100644 --- a/homeassistant/components/risco/translations/es.json +++ b/homeassistant/components/risco/translations/es.json @@ -33,7 +33,7 @@ "init": { "data": { "code_arm_required": "Requiere un c\u00f3digo PIN para armar", - "code_disarm_required": "Requiere un c\u00f3digo PIN para desarmar", + "code_disarm_required": "Requiere un c\u00f3digo PIN para desactivar", "scan_interval": "Con qu\u00e9 frecuencia sondear Risco (en segundos)" }, "title": "Configurar opciones" diff --git a/homeassistant/components/risco/translations/sk.json b/homeassistant/components/risco/translations/sk.json index 5ada995aa6e..3f464b4046d 100644 --- a/homeassistant/components/risco/translations/sk.json +++ b/homeassistant/components/risco/translations/sk.json @@ -3,5 +3,14 @@ "error": { "invalid_auth": "Neplatn\u00e9 overenie" } + }, + "options": { + "step": { + "risco_to_ha": { + "data": { + "A": "Skupina A" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/roku/translations/es.json b/homeassistant/components/roku/translations/es.json index 189a4aec179..202c7f6c8ff 100644 --- a/homeassistant/components/roku/translations/es.json +++ b/homeassistant/components/roku/translations/es.json @@ -8,7 +8,7 @@ "error": { "cannot_connect": "Fallo al conectar" }, - "flow_title": "Roku: {name}", + "flow_title": "{name}", "step": { "discovery_confirm": { "description": "\u00bfQuieres configurar {name} ?", diff --git a/homeassistant/components/roomba/translations/es.json b/homeassistant/components/roomba/translations/es.json index 315c8bda096..c8760f75fdd 100644 --- a/homeassistant/components/roomba/translations/es.json +++ b/homeassistant/components/roomba/translations/es.json @@ -16,7 +16,7 @@ "host": "Host" }, "description": "Selecciona una Roomba o Braava.", - "title": "Conectar autom\u00e1ticamente con el dispositivo" + "title": "Conexi\u00f3n autom\u00e1tica con el dispositivo" }, "link": { "description": "Mant\u00e9n pulsado el bot\u00f3n Inicio en {name} hasta que el dispositivo genere un sonido (aproximadamente dos segundos).", @@ -46,7 +46,7 @@ "password": "Contrase\u00f1a" }, "description": "Actualmente recuperar el BLID y la contrase\u00f1a es un proceso manual. Sigue los pasos descritos en la documentaci\u00f3n en: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", - "title": "Conectarse al dispositivo" + "title": "Conexi\u00f3n autom\u00e1tica con el dispositivo" } } }, diff --git a/homeassistant/components/roon/translations/es.json b/homeassistant/components/roon/translations/es.json index e38b7691f40..45038d8e7dd 100644 --- a/homeassistant/components/roon/translations/es.json +++ b/homeassistant/components/roon/translations/es.json @@ -8,6 +8,13 @@ "unknown": "Error inesperado" }, "step": { + "fallback": { + "data": { + "host": "Host", + "port": "Puerto" + }, + "description": "No se ha podrido descubrir el servidor Roon, introduce el anfitri\u00f3n y el puerto." + }, "link": { "description": "Debes autorizar Home Assistant en Roon. Despu\u00e9s de pulsar en Enviar, ve a la aplicaci\u00f3n Roon Core, abre Configuraci\u00f3n y activa HomeAssistant en la pesta\u00f1a Extensiones.", "title": "Autorizar HomeAssistant en Roon" @@ -16,7 +23,7 @@ "data": { "host": "Host" }, - "description": "Introduce el nombre de Host o IP del servidor Roon." + "description": "No se ha podido descubrir el servidor Roon, introduce el nombre de host o la IP." } } } diff --git a/homeassistant/components/ruckus_unleashed/translations/sk.json b/homeassistant/components/ruckus_unleashed/translations/sk.json index 5ada995aa6e..dbe0480911b 100644 --- a/homeassistant/components/ruckus_unleashed/translations/sk.json +++ b/homeassistant/components/ruckus_unleashed/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/es.json b/homeassistant/components/sabnzbd/translations/es.json new file mode 100644 index 00000000000..f38e28a3287 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/es.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "Ha fallado la conexi\u00f3n", + "invalid_api_key": "Clave API inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "api_key": "Clave API", + "name": "Nombre", + "path": "Ruta", + "url": "URL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sabnzbd/translations/sk.json b/homeassistant/components/sabnzbd/translations/sk.json new file mode 100644 index 00000000000..9d5ee388dc3 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/samsungtv/translations/es.json b/homeassistant/components/samsungtv/translations/es.json index 42b0f794e7a..922ee0da4fe 100644 --- a/homeassistant/components/samsungtv/translations/es.json +++ b/homeassistant/components/samsungtv/translations/es.json @@ -12,7 +12,8 @@ "unknown": "Error inesperado" }, "error": { - "auth_missing": "Home Assistant no est\u00e1 autorizado para conectarse a este televisor Samsung. Revisa la configuraci\u00f3n de tu televisor para autorizar a Home Assistant." + "auth_missing": "Home Assistant no est\u00e1 autorizado para conectarse a este televisor Samsung. Revisa la configuraci\u00f3n de tu televisor para autorizar a Home Assistant.", + "invalid_pin": "El PIN es inv\u00e1lido; vuelve a intentarlo." }, "flow_title": "{device}", "step": { @@ -20,9 +21,15 @@ "description": "\u00bfQuieres configurar {device}? Si nunca la has conectado a Home Assistant antes deber\u00edas ver una ventana en tu TV pidiendo autorizaci\u00f3n.", "title": "Samsung TV" }, + "encrypted_pairing": { + "description": "Introduce el PIN que se muestra en {device}." + }, "reauth_confirm": { "description": "Despu\u00e9s de enviarlo, acepte la ventana emergente en {device} solicitando autorizaci\u00f3n dentro de los 30 segundos." }, + "reauth_confirm_encrypted": { + "description": "Introduce el PIN que se muestra en {device}." + }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/season/translations/sensor.sk.json b/homeassistant/components/season/translations/sensor.sk.json new file mode 100644 index 00000000000..6a1f5dd293b --- /dev/null +++ b/homeassistant/components/season/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "season__season__": { + "spring": "Jar" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sense/translations/es.json b/homeassistant/components/sense/translations/es.json index ca77c97900b..5621988c60b 100644 --- a/homeassistant/components/sense/translations/es.json +++ b/homeassistant/components/sense/translations/es.json @@ -12,7 +12,8 @@ "reauth_validate": { "data": { "password": "Contrase\u00f1a" - } + }, + "description": "La integraci\u00f3n Sense debe volver a autenticar la cuenta {email}." }, "user": { "data": { diff --git a/homeassistant/components/senseme/translations/sk.json b/homeassistant/components/senseme/translations/sk.json new file mode 100644 index 00000000000..ffc3e13321e --- /dev/null +++ b/homeassistant/components/senseme/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/es.json b/homeassistant/components/sensibo/translations/es.json index f4ff854c502..c5033bce962 100644 --- a/homeassistant/components/sensibo/translations/es.json +++ b/homeassistant/components/sensibo/translations/es.json @@ -4,9 +4,18 @@ "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { - "cannot_connect": "No se pudo conectar" + "cannot_connect": "No se pudo conectar", + "incorrect_api_key": "Clave API inv\u00e1lida para la cuenta seleccionada", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", + "no_devices": "No se ha descubierto ning\u00fan dispositivo", + "no_username": "No se pudo obtener el nombre de usuario" }, "step": { + "reauth_confirm": { + "data": { + "api_key": "Clave API" + } + }, "user": { "data": { "api_key": "Clave API", diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index 694f006218b..c3bf532e7a3 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "error": { + "incorrect_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d pre vybran\u00fd \u00fa\u010det" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/sensor/translations/sk.json b/homeassistant/components/sensor/translations/sk.json index 8f3507f56c1..f86600559a4 100644 --- a/homeassistant/components/sensor/translations/sk.json +++ b/homeassistant/components/sensor/translations/sk.json @@ -1,4 +1,13 @@ { + "device_automation": { + "condition_type": { + "is_voltage": "S\u00fa\u010dasn\u00e9 nap\u00e4tie {entity_name}" + }, + "trigger_type": { + "current": "{entity_name} pr\u00fad sa zmen\u00ed", + "voltage": "{entity_name} zmen\u00ed nap\u00e4tie" + } + }, "state": { "_": { "off": "Neakt\u00edvny", diff --git a/homeassistant/components/senz/translations/es.json b/homeassistant/components/senz/translations/es.json new file mode 100644 index 00000000000..f81af28f4d9 --- /dev/null +++ b/homeassistant/components/senz/translations/es.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "La cuenta ya est\u00e1 configurada", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", + "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", + "no_url_available": "No hay ninguna URL disponible. Para m\u00e1s informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", + "oauth_error": "Se han recibido datos token inv\u00e1lidos." + }, + "create_entry": { + "default": "Autenticaci\u00f3n exitosa" + }, + "step": { + "pick_implementation": { + "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/es.json b/homeassistant/components/shelly/translations/es.json index 6f5c86417d4..25cbb8eb30b 100644 --- a/homeassistant/components/shelly/translations/es.json +++ b/homeassistant/components/shelly/translations/es.json @@ -42,7 +42,7 @@ "double": "Pulsaci\u00f3n doble de {subtype}", "double_push": "Pulsaci\u00f3n doble de {subtype}", "long": "Pulsaci\u00f3n larga de {subtype}", - "long_push": "Pulsaci\u00f3n larga de {subtype}", + "long_push": "{subtype} pulsado durante un rato", "long_single": "Pulsaci\u00f3n larga de {subtype} seguida de una pulsaci\u00f3n simple", "single": "Pulsaci\u00f3n simple de {subtype}", "single_long": "Pulsaci\u00f3n simple de {subtype} seguida de una pulsaci\u00f3n larga", diff --git a/homeassistant/components/sia/translations/sk.json b/homeassistant/components/sia/translations/sk.json index 892b8b2cd91..d703643c7de 100644 --- a/homeassistant/components/sia/translations/sk.json +++ b/homeassistant/components/sia/translations/sk.json @@ -3,7 +3,8 @@ "step": { "user": { "data": { - "port": "Port" + "port": "Port", + "protocol": "Protokol" } } } diff --git a/homeassistant/components/simplisafe/translations/es.json b/homeassistant/components/simplisafe/translations/es.json index d4b066890f1..632fc50e4c0 100644 --- a/homeassistant/components/simplisafe/translations/es.json +++ b/homeassistant/components/simplisafe/translations/es.json @@ -6,11 +6,15 @@ "wrong_account": "Las credenciales de usuario proporcionadas no coinciden con esta cuenta de SimpliSafe." }, "error": { + "2fa_timed_out": "Se ha agotado el tiempo de espera m\u00e1ximo durante la autenticaci\u00f3n de dos factores", "identifier_exists": "Cuenta ya registrada", "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", "still_awaiting_mfa": "Esperando todav\u00eda el clic en el correo electr\u00f3nico de MFA", "unknown": "Error inesperado" }, + "progress": { + "email_2fa": "Mira el correo electr\u00f3nico donde deber\u00edas encontrar el enlace de verificaci\u00f3n de Simplisafe." + }, "step": { "mfa": { "title": "Autenticaci\u00f3n Multi-Factor SimpliSafe" @@ -19,8 +23,14 @@ "data": { "password": "Contrase\u00f1a" }, - "description": "Tu token de acceso ha expirado o ha sido revocado. Introduce tu contrase\u00f1a para volver a vincular tu cuenta.", - "title": "Volver a vincular la Cuenta SimpliSafe" + "description": "Vuelve a introducir la contrase\u00f1a de {username}", + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" + }, + "sms_2fa": { + "data": { + "code": "C\u00f3digo" + }, + "description": "Introduce el c\u00f3digo de autenticaci\u00f3n de dos factores enviado por SMS." }, "user": { "data": { diff --git a/homeassistant/components/slack/translations/ca.json b/homeassistant/components/slack/translations/ca.json index 58ff126ae27..ca107cede26 100644 --- a/homeassistant/components/slack/translations/ca.json +++ b/homeassistant/components/slack/translations/ca.json @@ -15,7 +15,14 @@ "default_channel": "Canal predeterminat", "icon": "Icona", "username": "Nom d'usuari" - } + }, + "data_description": { + "api_key": "'Token' API de Slack que s'utilitza per enviar els missatges de Slack.", + "default_channel": "Canal al qual publicar en cas que no s'especifiqui cap canal en enviar un missatge.", + "icon": "Utilitza un dels emojis de Slack com a icona per al nom d'usuari proporcionat.", + "username": "Home Assistant publicar\u00e0 a Slack amb el nom d'usuari especificat." + }, + "description": "Consulta la documentaci\u00f3 per obtenir la teva clau API de Slack." } } } diff --git a/homeassistant/components/slack/translations/es.json b/homeassistant/components/slack/translations/es.json new file mode 100644 index 00000000000..52aee38b4f9 --- /dev/null +++ b/homeassistant/components/slack/translations/es.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "El servicio ya est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Error al conectar", + "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", + "unknown": "Error inesperado" + }, + "step": { + "user": { + "data": { + "api_key": "Clave API", + "default_channel": "Canal por defecto", + "icon": "Icono", + "username": "Nombre de usuario" + }, + "data_description": { + "api_key": "El token de la API de Slack que se usar\u00e1 para enviar mensajes de Slack.", + "default_channel": "Canal al que publicar en caso de que no se especifique ning\u00fan canal al enviar un mensaje.", + "icon": "Utilice uno de los emojis de Slack como icono para el nombre de usuario proporcionado.", + "username": "Home Assistant publicar\u00e1 en Slack con el nombre de usuario especificado." + }, + "description": "Consulte la documentaci\u00f3n sobre c\u00f3mo obtener su clave API de Slack." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/it.json b/homeassistant/components/slack/translations/it.json new file mode 100644 index 00000000000..dbd6a44de23 --- /dev/null +++ b/homeassistant/components/slack/translations/it.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Il servizio \u00e8 gi\u00e0 configurato" + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "user": { + "data": { + "api_key": "Chiave API", + "default_channel": "Canale predefinito", + "icon": "Icona", + "username": "Nome utente" + }, + "data_description": { + "api_key": "Il token API Slack da utilizzare per inviare messaggi Slack.", + "default_channel": "Il canale su cui inviare se non viene specificato alcun canale durante l'invio di un messaggio.", + "icon": "Usa uno degli emoji Slack come icona per il nome utente fornito.", + "username": "Home Assistant pubblicher\u00e0 su Slack utilizzando il nome utente specificato." + }, + "description": "Fai riferimento alla documentazione su come ottenere la chiave API Slack." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/sk.json b/homeassistant/components/slack/translations/sk.json new file mode 100644 index 00000000000..4e2a5fa0ed7 --- /dev/null +++ b/homeassistant/components/slack/translations/sk.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepodarilo sa pripoji\u0165", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "icon": "Ikona", + "username": "U\u017e\u00edvate\u013esk\u00e9 meno" + }, + "data_description": { + "username": "Home Assistant odo\u0161le pr\u00edspevok na Slack pomocou zadan\u00e9ho pou\u017e\u00edvate\u013esk\u00e9ho mena." + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/tr.json b/homeassistant/components/slack/translations/tr.json new file mode 100644 index 00000000000..4fd5a28f790 --- /dev/null +++ b/homeassistant/components/slack/translations/tr.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "default_channel": "Varsay\u0131lan Kanal", + "icon": "Simge", + "username": "Kullan\u0131c\u0131 Ad\u0131" + }, + "data_description": { + "api_key": "Slack mesajlar\u0131 g\u00f6ndermek i\u00e7in kullan\u0131lacak Slack API belirteci.", + "default_channel": "Mesaj g\u00f6nderirken kanal belirtilmemi\u015fse g\u00f6nderi yap\u0131lacak kanal.", + "icon": "Sa\u011flanan kullan\u0131c\u0131 ad\u0131 i\u00e7in Slack emojilerinden birini simge olarak kullan\u0131n.", + "username": "Home Assistant, belirtilen kullan\u0131c\u0131 ad\u0131n\u0131 kullanarak Slack'e g\u00f6nderecektir." + }, + "description": "Slack API anahtar\u0131n\u0131z\u0131 almayla ilgili belgelere bak\u0131n." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sleepiq/translations/es.json b/homeassistant/components/sleepiq/translations/es.json index ae6059aa227..57b8f421844 100644 --- a/homeassistant/components/sleepiq/translations/es.json +++ b/homeassistant/components/sleepiq/translations/es.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "La cuenta ya est\u00e1 configurada" + "already_configured": "La cuenta ya est\u00e1 configurada", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" }, "error": { "cannot_connect": "Error al conectar", @@ -11,7 +12,8 @@ "reauth_confirm": { "data": { "password": "Contrase\u00f1a" - } + }, + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" }, "user": { "data": { diff --git a/homeassistant/components/slimproto/translations/es.json b/homeassistant/components/slimproto/translations/es.json new file mode 100644 index 00000000000..352f82f605f --- /dev/null +++ b/homeassistant/components/slimproto/translations/es.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ya configurado. S\u00f3lo es posible una sola configuraci\u00f3n." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slimproto/translations/tr.json b/homeassistant/components/slimproto/translations/tr.json index a152eb19468..b04fdc4468d 100644 --- a/homeassistant/components/slimproto/translations/tr.json +++ b/homeassistant/components/slimproto/translations/tr.json @@ -2,6 +2,12 @@ "config": { "abort": { "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "step": { + "user": { + "one": "Bo\u015f", + "other": "Bo\u015f" + } } } } \ No newline at end of file diff --git a/homeassistant/components/smappee/translations/es.json b/homeassistant/components/smappee/translations/es.json index a1124557876..891e9642d53 100644 --- a/homeassistant/components/smappee/translations/es.json +++ b/homeassistant/components/smappee/translations/es.json @@ -3,7 +3,7 @@ "abort": { "already_configured_device": "El dispositivo ya est\u00e1 configurado", "already_configured_local_device": "Los dispositivos locales ya est\u00e1n configurados. Elim\u00ednelos primero antes de configurar un dispositivo en la nube.", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "cannot_connect": "No se pudo conectar", "invalid_mdns": "Dispositivo no compatible para la integraci\u00f3n de Smappee.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", diff --git a/homeassistant/components/smarttub/translations/es.json b/homeassistant/components/smarttub/translations/es.json index 20a57210448..8f2eb153cb7 100644 --- a/homeassistant/components/smarttub/translations/es.json +++ b/homeassistant/components/smarttub/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/smhi/translations/es.json b/homeassistant/components/smhi/translations/es.json index d9902ba7d08..ab34438b12d 100644 --- a/homeassistant/components/smhi/translations/es.json +++ b/homeassistant/components/smhi/translations/es.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "already_configured": "La cuenta ya est\u00e1 configurada" + }, "error": { "name_exists": "Nombre ya existe", "wrong_location": "Ubicaci\u00f3n Suecia solamente" diff --git a/homeassistant/components/sms/translations/es.json b/homeassistant/components/sms/translations/es.json index f2f36d426f9..27669a2b52f 100644 --- a/homeassistant/components/sms/translations/es.json +++ b/homeassistant/components/sms/translations/es.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Velocidad en Baudis", "device": "Dispositivo" }, "title": "Conectar con el m\u00f3dem" diff --git a/homeassistant/components/sms/translations/tr.json b/homeassistant/components/sms/translations/tr.json index 0488390beed..1558fe5a58e 100644 --- a/homeassistant/components/sms/translations/tr.json +++ b/homeassistant/components/sms/translations/tr.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "Baud H\u0131z\u0131", "device": "Cihaz" }, "title": "Modeme ba\u011flan\u0131n" diff --git a/homeassistant/components/solax/translations/es.json b/homeassistant/components/solax/translations/es.json index 4728aed6395..f6658c63353 100644 --- a/homeassistant/components/solax/translations/es.json +++ b/homeassistant/components/solax/translations/es.json @@ -1,7 +1,15 @@ { "config": { "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "unknown": "Error inesperado" + }, + "step": { + "user": { + "data": { + "port": "Puerto" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/soma/translations/es.json b/homeassistant/components/soma/translations/es.json index 6f6b47275dd..0ae2d26adec 100644 --- a/homeassistant/components/soma/translations/es.json +++ b/homeassistant/components/soma/translations/es.json @@ -2,13 +2,13 @@ "config": { "abort": { "already_setup": "S\u00f3lo puede configurar una cuenta de Soma.", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "connection_error": "No se ha podido conectar a SOMA Connect.", "missing_configuration": "El componente Soma no est\u00e1 configurado. Por favor, leer la documentaci\u00f3n.", "result_error": "SOMA Connect respondi\u00f3 con un error." }, "create_entry": { - "default": "Autenticado con \u00e9xito con Soma." + "default": "Autenticaci\u00f3n exitosa" }, "step": { "user": { diff --git a/homeassistant/components/somfy/translations/es.json b/homeassistant/components/somfy/translations/es.json index 1fd22ebda3f..2f8b35f5af8 100644 --- a/homeassistant/components/somfy/translations/es.json +++ b/homeassistant/components/somfy/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n", - "missing_configuration": "El componente Somfy no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", + "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, diff --git a/homeassistant/components/sonarr/translations/es.json b/homeassistant/components/sonarr/translations/es.json index b2f073297d9..f7d55cc5353 100644 --- a/homeassistant/components/sonarr/translations/es.json +++ b/homeassistant/components/sonarr/translations/es.json @@ -13,7 +13,7 @@ "step": { "reauth_confirm": { "description": "La integraci\u00f3n de Sonarr necesita volver a autenticarse manualmente con la API de Sonarr alojada en: {host}", - "title": "Volver a autenticarse con Sonarr" + "title": "Reautenticaci\u00f3n de la integraci\u00f3n" }, "user": { "data": { diff --git a/homeassistant/components/sonos/translations/es.json b/homeassistant/components/sonos/translations/es.json index 3560280c90e..e6af9ac0e44 100644 --- a/homeassistant/components/sonos/translations/es.json +++ b/homeassistant/components/sonos/translations/es.json @@ -3,7 +3,7 @@ "abort": { "no_devices_found": "No se encontraron dispositivos en la red", "not_sonos_device": "El dispositivo descubierto no es un dispositivo Sonos", - "single_instance_allowed": "S\u00f3lo se necesita una \u00fanica configuraci\u00f3n de Sonos." + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "confirm": { diff --git a/homeassistant/components/speedtestdotnet/translations/es.json b/homeassistant/components/speedtestdotnet/translations/es.json index 1ccca471c7e..1727f4d2a6c 100644 --- a/homeassistant/components/speedtestdotnet/translations/es.json +++ b/homeassistant/components/speedtestdotnet/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", - "wrong_server_id": "Id del servidor no v\u00e1lido" + "wrong_server_id": "El identificador del servidor no es v\u00e1lido" }, "step": { "user": { diff --git a/homeassistant/components/sql/translations/es.json b/homeassistant/components/sql/translations/es.json new file mode 100644 index 00000000000..3d794607feb --- /dev/null +++ b/homeassistant/components/sql/translations/es.json @@ -0,0 +1,55 @@ +{ + "config": { + "abort": { + "already_configured": "La cuenta ya est\u00e1 configurada" + }, + "error": { + "db_url_invalid": "URL de la base de datos inv\u00e1lido" + }, + "step": { + "user": { + "data": { + "name": "Nombre", + "query": "Selecciona la consulta", + "unit_of_measurement": "Unidad de medida", + "value_template": "Plantilla de valor" + }, + "data_description": { + "column": "Columna de respuesta de la consulta para presentar como estado", + "db_url": "URL de la base de datos, d\u00e9jalo en blanco para utilizar la predeterminada de HA", + "name": "Nombre que se utilizar\u00e1 para la entrada de configuraci\u00f3n y tambi\u00e9n para el sensor", + "query": "Consulta a ejecutar, debe empezar por 'SELECT'", + "unit_of_measurement": "Unidad de medida (opcional)", + "value_template": "Plantilla de valor (opcional)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "URL de la base de datos inv\u00e1lido", + "query_invalid": "Consulta SQL inv\u00e1lida", + "value_template_invalid": "Plantilla de valor inv\u00e1lida" + }, + "step": { + "init": { + "data": { + "column": "Columna", + "db_url": "URL de la base de datos", + "name": "Nombre", + "query": "Selecciona la consulta", + "unit_of_measurement": "Unidad de medida", + "value_template": "Plantilla de valor" + }, + "data_description": { + "column": "Columna de respuesta de la consulta para presentar como estado", + "db_url": "URL de la base de datos, d\u00e9jalo en blanco para utilizar la predeterminada de HA", + "name": "Nombre que se utilizar\u00e1 para la entrada de configuraci\u00f3n y tambi\u00e9n para el sensor", + "query": "Consulta a ejecutar, debe empezar por 'SELECT'", + "unit_of_measurement": "Unidad de medida (opcional)", + "value_template": "Plantilla de valor (opcional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/es.json b/homeassistant/components/steam_online/translations/es.json new file mode 100644 index 00000000000..9636fc04a54 --- /dev/null +++ b/homeassistant/components/steam_online/translations/es.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "El servicio ya est\u00e1 configurado", + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_account": "ID de la cuenta inv\u00e1lida", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida" + }, + "step": { + "reauth_confirm": { + "title": "Volver a autenticar la integraci\u00f3n" + }, + "user": { + "data": { + "account": "ID de la cuenta de Steam", + "api_key": "Clave API" + }, + "description": "Utiliza {account_id_url} para encontrar el ID de tu cuenta de Steam" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "Nombres de las cuentas a controlar" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/subaru/translations/es.json b/homeassistant/components/subaru/translations/es.json index 42a13a37a7a..170983e8df4 100644 --- a/homeassistant/components/subaru/translations/es.json +++ b/homeassistant/components/subaru/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "cannot_connect": "No se pudo conectar" }, "error": { @@ -18,11 +18,25 @@ "description": "Por favor, introduzca su PIN de MySubaru\nNOTA: Todos los veh\u00edculos de la cuenta deben tener el mismo PIN", "title": "Configuraci\u00f3n de Subaru Starlink" }, + "two_factor": { + "data": { + "contact_method": "Selecciona un m\u00e9todo de contacto:" + }, + "description": "Autenticaci\u00f3n de dos factores requerida", + "title": "Configuraci\u00f3n de Subaru Starlink" + }, + "two_factor_validate": { + "data": { + "validation_code": "C\u00f3digo de validaci\u00f3n" + }, + "description": "Introduce el c\u00f3digo de validaci\u00f3n recibido", + "title": "Configuraci\u00f3n de Subaru Starlink" + }, "user": { "data": { "country": "Seleccionar pa\u00eds", "password": "Contrase\u00f1a", - "username": "Nombre de usuario" + "username": "Usuario" }, "description": "Por favor, introduzca sus credenciales de MySubaru\nNOTA: La configuraci\u00f3n inicial puede tardar hasta 30 segundos", "title": "Configuraci\u00f3n de Subaru Starlink" diff --git a/homeassistant/components/surepetcare/translations/es.json b/homeassistant/components/surepetcare/translations/es.json index 3d3945748cb..13f2eb38bef 100644 --- a/homeassistant/components/surepetcare/translations/es.json +++ b/homeassistant/components/surepetcare/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/surepetcare/translations/sk.json b/homeassistant/components/surepetcare/translations/sk.json index 5ada995aa6e..1b1e671c054 100644 --- a/homeassistant/components/surepetcare/translations/sk.json +++ b/homeassistant/components/surepetcare/translations/sk.json @@ -2,6 +2,13 @@ "config": { "error": { "invalid_auth": "Neplatn\u00e9 overenie" + }, + "step": { + "user": { + "data": { + "password": "Heslo" + } + } } } } \ No newline at end of file diff --git a/homeassistant/components/switch/translations/es.json b/homeassistant/components/switch/translations/es.json index 95a60ab55ea..2c1f6050ce6 100644 --- a/homeassistant/components/switch/translations/es.json +++ b/homeassistant/components/switch/translations/es.json @@ -1,4 +1,11 @@ { + "config": { + "step": { + "init": { + "description": "Seleccione el interruptor de la l\u00e1mpara." + } + } + }, "device_automation": { "action_type": { "toggle": "Alternar {entity_name}", diff --git a/homeassistant/components/switch_as_x/translations/es.json b/homeassistant/components/switch_as_x/translations/es.json new file mode 100644 index 00000000000..7e91d3217a4 --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/es.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_id": "Conmutador", + "target_domain": "Nuevo tipo" + } + } + } + }, + "title": "Cambia el tipo de dispositivo de un conmutador" +} \ No newline at end of file diff --git a/homeassistant/components/syncthru/translations/es.json b/homeassistant/components/syncthru/translations/es.json index 513cbf17fc0..da3002496db 100644 --- a/homeassistant/components/syncthru/translations/es.json +++ b/homeassistant/components/syncthru/translations/es.json @@ -8,7 +8,7 @@ "syncthru_not_supported": "El dispositivo no es compatible con SyncThru", "unknown_state": "Estado de la impresora desconocido, verifica la URL y la conectividad de la red" }, - "flow_title": "Impresora Samsung SyncThru: {name}", + "flow_title": "{name}", "step": { "confirm": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/es.json b/homeassistant/components/synology_dsm/translations/es.json index 134f99cd75d..382797dc81e 100644 --- a/homeassistant/components/synology_dsm/translations/es.json +++ b/homeassistant/components/synology_dsm/translations/es.json @@ -10,7 +10,7 @@ "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "missing_data": "Faltan datos: por favor, vuelva a intentarlo m\u00e1s tarde o pruebe con otra configuraci\u00f3n", "otp_failed": "La autenticaci\u00f3n de dos pasos fall\u00f3, vuelva a intentar con un nuevo c\u00f3digo de acceso", - "unknown": "Error desconocido: por favor, consulta logs para obtener m\u00e1s detalles" + "unknown": "Error inesperado" }, "flow_title": "{name} ({host})", "step": { @@ -64,6 +64,7 @@ "init": { "data": { "scan_interval": "Minutos entre escaneos", + "snap_profile_type": "Calidad de las fotos de la c\u00e1mara (0:alta, 1:media, 2:baja)", "timeout": "Tiempo de espera (segundos)" } } diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index e9c37059842..59f0e333d63 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9", "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { @@ -12,6 +13,11 @@ "port": "Port" } }, + "reauth": { + "data": { + "password": "Heslo" + } + }, "user": { "data": { "port": "Port" diff --git a/homeassistant/components/system_bridge/translations/sk.json b/homeassistant/components/system_bridge/translations/sk.json index 276eac51dd4..4a6f823bbe6 100644 --- a/homeassistant/components/system_bridge/translations/sk.json +++ b/homeassistant/components/system_bridge/translations/sk.json @@ -4,7 +4,8 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "error": { - "invalid_auth": "Neplatn\u00e9 overenie" + "invalid_auth": "Neplatn\u00e9 overenie", + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { "authenticate": { diff --git a/homeassistant/components/tado/translations/es.json b/homeassistant/components/tado/translations/es.json index c9a26a1a9ab..db71d41f789 100644 --- a/homeassistant/components/tado/translations/es.json +++ b/homeassistant/components/tado/translations/es.json @@ -23,7 +23,7 @@ "step": { "init": { "data": { - "fallback": "Activar modo de salvaguarda." + "fallback": "Elige el modo alternativo." }, "description": "El modo de salvaguarda volver\u00e1 a la Planificaci\u00f3n Inteligente en el siguiente cambio de programaci\u00f3n despu\u00e9s de ajustar manualmente una zona.", "title": "Ajustar las opciones de Tado" diff --git a/homeassistant/components/tankerkoenig/translations/es.json b/homeassistant/components/tankerkoenig/translations/es.json new file mode 100644 index 00000000000..ec97b5886d3 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/es.json @@ -0,0 +1,33 @@ +{ + "config": { + "abort": { + "already_configured": "La ubicaci\u00f3n ya est\u00e1 configurada" + }, + "error": { + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", + "no_stations": "No se pudo encontrar ninguna estaci\u00f3n al alcance." + }, + "step": { + "select_station": { + "data": { + "stations": "Estaciones" + }, + "title": "Selecciona las estaciones a a\u00f1adir" + }, + "user": { + "data": { + "name": "Nombre de la regi\u00f3n" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "show_on_map": "Muestra las estaciones en el mapa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tankerkoenig/translations/sk.json b/homeassistant/components/tankerkoenig/translations/sk.json new file mode 100644 index 00000000000..06c74b52725 --- /dev/null +++ b/homeassistant/components/tankerkoenig/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "radius": "Polomer vyh\u013ead\u00e1vania" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tautulli/translations/es.json b/homeassistant/components/tautulli/translations/es.json new file mode 100644 index 00000000000..0d6fd1d3b9e --- /dev/null +++ b/homeassistant/components/tautulli/translations/es.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." + }, + "error": { + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Clave API" + }, + "title": "Re-autenticaci\u00f3n de Tautulli" + }, + "user": { + "data": { + "url": "URL", + "verify_ssl": "Verifica el certificat SSL" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tellduslive/translations/es.json b/homeassistant/components/tellduslive/translations/es.json index 882e553d46e..5971c89f43e 100644 --- a/homeassistant/components/tellduslive/translations/es.json +++ b/homeassistant/components/tellduslive/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "TelldusLive ya est\u00e1 configurado", + "already_configured": "El servicio ya est\u00e1 configurado", "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n", "unknown": "Se produjo un error desconocido", "unknown_authorize_url_generation": "Error desconocido al generar una URL de autorizaci\u00f3n." diff --git a/homeassistant/components/threshold/translations/es.json b/homeassistant/components/threshold/translations/es.json new file mode 100644 index 00000000000..0200c840243 --- /dev/null +++ b/homeassistant/components/threshold/translations/es.json @@ -0,0 +1,24 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_id": "Sensor de entrada", + "hysteresis": "Hist\u00e9resis", + "lower": "L\u00edmite inferior", + "upper": "L\u00edmite superior" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "hysteresis": "Hist\u00e9resis" + } + } + } + }, + "title": "Sensor de umbral" +} \ No newline at end of file diff --git a/homeassistant/components/tile/translations/es.json b/homeassistant/components/tile/translations/es.json index 42eb3527a6f..c13488183d3 100644 --- a/homeassistant/components/tile/translations/es.json +++ b/homeassistant/components/tile/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n fue exitosa" }, "error": { diff --git a/homeassistant/components/tod/translations/es.json b/homeassistant/components/tod/translations/es.json new file mode 100644 index 00000000000..899b4ce18f0 --- /dev/null +++ b/homeassistant/components/tod/translations/es.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "user": { + "data": { + "after_time": "Tiempo de activaci\u00f3n", + "before_time": "Tiempo de desactivaci\u00f3n" + }, + "description": "Crea un sensor binario que se activa o desactiva en funci\u00f3n de la hora.", + "title": "A\u00f1ade sensor tiempo del d\u00eda" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "after_time": "Tiempo de activaci\u00f3n" + }, + "description": "Crea un sensor binario que se activa o desactiva en funci\u00f3n de la hora." + } + } + }, + "title": "Sensor tiempo del d\u00eda" +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/es.json b/homeassistant/components/tomorrowio/translations/es.json new file mode 100644 index 00000000000..9ad330dab15 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/es.json @@ -0,0 +1,29 @@ +{ + "config": { + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "invalid_api_key": "Clave API inv\u00e1lida", + "unknown": "Error inesperado" + }, + "step": { + "user": { + "data": { + "api_key": "Clave API", + "latitude": "Latitud", + "location": "Ubicaci\u00f3n", + "name": "Nombre" + }, + "description": "Para obtener una clave API, reg\u00edstrate en [Tomorrow.io](https://app.tomorrow.io/signup)" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "timestep": "Min. entre previsiones de NowCast" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.es.json b/homeassistant/components/tomorrowio/translations/sensor.es.json new file mode 100644 index 00000000000..03820d30265 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.es.json @@ -0,0 +1,16 @@ +{ + "state": { + "tomorrowio__health_concern": { + "unhealthy": "Poco saludable", + "unhealthy_for_sensitive_groups": "No saludable para grupos sensibles", + "very_unhealthy": "Nada saludable" + }, + "tomorrowio__pollen_index": { + "medium": "Medio", + "very_low": "Muy bajo" + }, + "tomorrowio__precipitation_type": { + "snow": "Nieve" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sensor.sk.json b/homeassistant/components/tomorrowio/translations/sensor.sk.json new file mode 100644 index 00000000000..3dd3dede27b --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "tomorrowio__pollen_index": { + "low": "N\u00edzka" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tomorrowio/translations/sk.json b/homeassistant/components/tomorrowio/translations/sk.json new file mode 100644 index 00000000000..7f480c9778c --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sk.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "api_key": "API k\u013e\u00fa\u010d", + "name": "Meno" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/translations/es.json b/homeassistant/components/toon/translations/es.json index d048e53ec90..423d81dde0c 100644 --- a/homeassistant/components/toon/translations/es.json +++ b/homeassistant/components/toon/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El acuerdo seleccionado ya est\u00e1 configurado.", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "no_agreements": "Esta cuenta no tiene pantallas Toon.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", diff --git a/homeassistant/components/totalconnect/translations/es.json b/homeassistant/components/totalconnect/translations/es.json index 5c402fb76af..97822a10300 100644 --- a/homeassistant/components/totalconnect/translations/es.json +++ b/homeassistant/components/totalconnect/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "no_locations": "No hay ubicaciones disponibles para este usuario, compruebe la configuraci\u00f3n de TotalConnect", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, diff --git a/homeassistant/components/traccar/translations/es.json b/homeassistant/components/traccar/translations/es.json index a87e7aae2c1..851984b0024 100644 --- a/homeassistant/components/traccar/translations/es.json +++ b/homeassistant/components/traccar/translations/es.json @@ -5,12 +5,12 @@ "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, "create_entry": { - "default": "Para enviar eventos a Home Assistant, necesitar\u00e1 configurar la funci\u00f3n de webhook en Traccar.\n\nUtilice la siguiente url: ``{webhook_url}``\n\nConsulte la [documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." + "default": "Para enviar eventos a Home Assistant, tendr\u00e1s que configurar la opci\u00f3n webhook de Traccar.\n\nUtilice la siguiente url: `{webhook_url}`\n\nConsulte la [documentaci\u00f3n]({docs_url}) para m\u00e1s detalles." }, "step": { "user": { - "description": "\u00bfEst\u00e1 seguro de querer configurar Traccar?", - "title": "Configurar Traccar" + "description": "\u00bfEst\u00e1s seguro de que quieres configurar Traccar?", + "title": "Configura Traccar" } } } diff --git a/homeassistant/components/tradfri/translations/es.json b/homeassistant/components/tradfri/translations/es.json index 1f342c4630e..531b11cc0a1 100644 --- a/homeassistant/components/tradfri/translations/es.json +++ b/homeassistant/components/tradfri/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "already_configured": "La pasarela ya est\u00e1 configurada", - "already_in_progress": "La configuraci\u00f3n de la pasarela ya est\u00e1 en marcha." + "already_configured": "El dispositivo ya est\u00e1 configurado", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso" }, "error": { "cannot_authenticate": "No se puede autenticar, \u00bfGateway est\u00e1 emparejado con otro servidor como, por ejemplo, Homekit?", diff --git a/homeassistant/components/trafikverket_ferry/translations/es.json b/homeassistant/components/trafikverket_ferry/translations/es.json new file mode 100644 index 00000000000..26532fbce5e --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/es.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "reauth_successful": "Re-autenticaci\u00f3n realizada correctamente" + }, + "error": { + "incorrect_api_key": "Clave API inv\u00e1lida para la cuenta seleccionada", + "invalid_route": "No se pudo encontrar la ruta con la informaci\u00f3n proporcionada" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "Clave API" + } + }, + "user": { + "data": { + "api_key": "Clave API", + "from": "Des del puerto", + "time": "Hora", + "to": "Al puerto" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/es.json b/homeassistant/components/trafikverket_train/translations/es.json new file mode 100644 index 00000000000..4ce1da04b02 --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/es.json @@ -0,0 +1,17 @@ +{ + "config": { + "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "incorrect_api_key": "Clave API inv\u00e1lida para la cuenta seleccionada", + "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida" + }, + "step": { + "user": { + "data": { + "from": "Desde la estaci\u00f3n", + "time": "Hora (opcional)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/es.json b/homeassistant/components/tuya/translations/es.json index bcd83f85af9..235f87d5374 100644 --- a/homeassistant/components/tuya/translations/es.json +++ b/homeassistant/components/tuya/translations/es.json @@ -30,13 +30,13 @@ "access_secret": "Tuya IoT Access Secret", "country_code": "C\u00f3digo de pais de tu cuenta (por ejemplo, 1 para USA o 86 para China)", "password": "Contrase\u00f1a", - "platform": "La aplicaci\u00f3n en la cual registraste tu cuenta", + "platform": "La aplicaci\u00f3n donde se registra tu cuenta", "region": "Regi\u00f3n", "tuya_project_type": "Tipo de proyecto en la nube de Tuya", "username": "Usuario" }, - "description": "Introduce tu credencial Tuya.", - "title": "Tuya" + "description": "Introduce tus credencial de Tuya", + "title": "Integraci\u00f3n Tuya" } } }, diff --git a/homeassistant/components/tuya/translations/select.es.json b/homeassistant/components/tuya/translations/select.es.json index 7dc4cf1067b..e29ccdc381f 100644 --- a/homeassistant/components/tuya/translations/select.es.json +++ b/homeassistant/components/tuya/translations/select.es.json @@ -27,6 +27,10 @@ "0": "Sensibilidad baja", "1": "Sensibilidad alta" }, + "tuya__fan_angle": { + "30": "30\u00b0", + "90": "90\u00b0" + }, "tuya__fingerbot_mode": { "click": "Push", "switch": "Interruptor" @@ -43,7 +47,12 @@ "level_8": "Nivel 8", "level_9": "Nivel 9" }, + "tuya__humidifier_moodlighting": { + "3": "Estado 3", + "4": "Estado 4" + }, "tuya__humidifier_spray_mode": { + "auto": "Autom\u00e1tico", "health": "Salud", "humidity": "Humedad", "sleep": "Dormir", diff --git a/homeassistant/components/tuya/translations/select.sk.json b/homeassistant/components/tuya/translations/select.sk.json new file mode 100644 index 00000000000..493576472b9 --- /dev/null +++ b/homeassistant/components/tuya/translations/select.sk.json @@ -0,0 +1,18 @@ +{ + "state": { + "tuya__countdown": { + "3h": "3 hodiny" + }, + "tuya__decibel_sensitivity": { + "1": "Vysok\u00e1 citlivos\u0165" + }, + "tuya__motion_sensitivity": { + "0": "N\u00edzka citlivos\u0165", + "1": "Stredn\u00e1 citlivos\u0165", + "2": "Vysok\u00e1 citlivos\u0165" + }, + "tuya__vacuum_mode": { + "random": "N\u00e1hodn\u00fd" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/sensor.sk.json b/homeassistant/components/tuya/translations/sensor.sk.json new file mode 100644 index 00000000000..4f80ab106ad --- /dev/null +++ b/homeassistant/components/tuya/translations/sensor.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "tuya__status": { + "boiling_temp": "Teplota varu" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/twentemilieu/translations/es.json b/homeassistant/components/twentemilieu/translations/es.json index 259d202a9c3..93effca8aad 100644 --- a/homeassistant/components/twentemilieu/translations/es.json +++ b/homeassistant/components/twentemilieu/translations/es.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "No se pudo conectar", - "invalid_address": "Direcci\u00f3n no encontrada en el \u00e1rea de servicio de Twente Milieu." + "invalid_address": "No se ha encontrado la direcci\u00f3n en el \u00e1rea de servicio de Twente Milieu." }, "step": { "user": { @@ -14,7 +14,7 @@ "house_number": "N\u00famero de casa", "post_code": "C\u00f3digo postal" }, - "description": "Configure Twente Milieu proporcionando informaci\u00f3n sobre la recolecci\u00f3n de residuos en su direcci\u00f3n." + "description": "Configura Twente Milieu con informaci\u00f3n de la recogida de residuos en tu direcci\u00f3n." } } } diff --git a/homeassistant/components/twilio/translations/es.json b/homeassistant/components/twilio/translations/es.json index 46499faca59..41057c5f4a0 100644 --- a/homeassistant/components/twilio/translations/es.json +++ b/homeassistant/components/twilio/translations/es.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "cloud_not_connected": "No conectado a Home Assistant Cloud.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", "webhook_not_internet_accessible": "Tu instancia de Home Assistant debe estar accesible desde Internet para recibir mensajes webhook." }, @@ -9,7 +10,7 @@ }, "step": { "user": { - "description": "\u00bfEst\u00e1s seguro de que quieres configurar Twilio?", + "description": "\u00bfQuieres empezar la configuraci\u00f3n?", "title": "Configurar el Webhook de Twilio" } } diff --git a/homeassistant/components/twinkly/translations/es.json b/homeassistant/components/twinkly/translations/es.json index e18d54adb9e..a45be8da295 100644 --- a/homeassistant/components/twinkly/translations/es.json +++ b/homeassistant/components/twinkly/translations/es.json @@ -12,7 +12,7 @@ }, "user": { "data": { - "host": "Host (o direcci\u00f3n IP) de tu dispositivo Twinkly" + "host": "Host" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/es.json b/homeassistant/components/ukraine_alarm/translations/es.json index 7fa891bde3e..7f8db3877c0 100644 --- a/homeassistant/components/ukraine_alarm/translations/es.json +++ b/homeassistant/components/ukraine_alarm/translations/es.json @@ -1,10 +1,33 @@ { "config": { + "abort": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", + "max_regions": "Se pueden configurar un m\u00e1ximo de 5 regiones", + "timeout": "Tiempo m\u00e1ximo de espera para establecer la conexi\u00f3n agotado", + "unknown": "Error inesperado" + }, + "error": { + "unknown": "Error inesperado" + }, "step": { - "user": { + "district": { "data": { "region": "Regi\u00f3n" - } + }, + "description": "Si quieres monitorear no s\u00f3lo el estado, elige el distrito espec\u00edfico" + }, + "state": { + "data": { + "region": "Regi\u00f3n" + }, + "description": "Escoja el estado a monitorear" + }, + "user": { + "data": { + "api_key": "Clave API", + "region": "Regi\u00f3n" + }, + "description": "Escoja el estado a monitorear" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/tr.json b/homeassistant/components/ukraine_alarm/translations/tr.json new file mode 100644 index 00000000000..3662a32b1fa --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/tr.json @@ -0,0 +1,45 @@ +{ + "config": { + "abort": { + "already_configured": "Konum zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "cannot_connect": "Ba\u011flanma hatas\u0131", + "max_regions": "En fazla 5 b\u00f6lge yap\u0131land\u0131r\u0131labilir", + "rate_limit": "\u00c7ok fazla istek", + "timeout": "Ba\u011flant\u0131 kurulurken zaman a\u015f\u0131m\u0131", + "unknown": "Beklenmeyen hata" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", + "timeout": "Ba\u011flant\u0131 kurulurken zaman a\u015f\u0131m\u0131", + "unknown": "Beklenmeyen hata" + }, + "step": { + "community": { + "data": { + "region": "B\u00f6lge" + }, + "description": "Yaln\u0131zca eyalet ve il\u00e7eyi izlemek istemiyorsan\u0131z, belirli toplulu\u011funu se\u00e7in" + }, + "district": { + "data": { + "region": "B\u00f6lge" + }, + "description": "Sadece eyaleti izlemek istemiyorsan\u0131z, belirli bir b\u00f6lgeyi se\u00e7in" + }, + "state": { + "data": { + "region": "B\u00f6lge" + }, + "description": "\u0130zlenecek durumu se\u00e7in" + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "region": "B\u00f6lge" + }, + "description": "\u0130zlenecek durumu se\u00e7in" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifi/translations/es.json b/homeassistant/components/unifi/translations/es.json index a5963d7019e..ebaf1d3c127 100644 --- a/homeassistant/components/unifi/translations/es.json +++ b/homeassistant/components/unifi/translations/es.json @@ -21,11 +21,14 @@ "username": "Usuario", "verify_ssl": "Controlador usando el certificado adecuado" }, - "title": "Configurar el controlador UniFi" + "title": "Configuraci\u00f3n de UniFi Network" } } }, "options": { + "abort": { + "integration_not_setup": "La integraci\u00f3n UniFi no est\u00e1 configurada" + }, "step": { "client_control": { "data": { @@ -39,7 +42,7 @@ "device_tracker": { "data": { "detection_time": "Tiempo en segundos desde la \u00faltima vez que se vio hasta considerarlo desconectado", - "ignore_wired_bug": "Desactivar la l\u00f3gica para el bug cableado de UniFi", + "ignore_wired_bug": "Desactiva la l\u00f3gica de errores de UniFi Network", "ssid_filter": "Seleccione los SSIDs para realizar seguimiento de clientes inal\u00e1mbricos", "track_clients": "Seguimiento de los clientes de red", "track_devices": "Rastree dispositivos de red (dispositivos Ubiquiti)", @@ -54,7 +57,7 @@ "track_clients": "Rastree clientes de red", "track_devices": "Rastree dispositivos de red (dispositivos Ubiquiti)" }, - "description": "Configurar la integraci\u00f3n de UniFi" + "description": "Configura la integraci\u00f3n UniFi Network" }, "statistics_sensors": { "data": { @@ -62,7 +65,7 @@ "allow_uptime_sensors": "Sensores de tiempo de actividad para clientes de la red" }, "description": "Configurar estad\u00edsticas de los sensores", - "title": "Opciones UniFi 3/3" + "title": "Opciones de UniFi Network 3/3" } } } diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index bcd7efe6cdf..901f1707de5 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado" + "already_configured": "El dispositivo ya est\u00e1 configurado", + "discovery_started": "Se ha iniciado el descubrimiento" }, "error": { "cannot_connect": "No se pudo conectar", @@ -10,6 +11,12 @@ "unknown": "Error inesperado" }, "step": { + "discovery_confirm": { + "data": { + "username": "Usuario", + "verify_ssl": "Verifica el certificado SSL" + } + }, "reauth_confirm": { "data": { "host": "IP/Host del servidor UniFi Protect", diff --git a/homeassistant/components/upb/translations/sk.json b/homeassistant/components/upb/translations/sk.json new file mode 100644 index 00000000000..3f20d345b26 --- /dev/null +++ b/homeassistant/components/upb/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/update/translations/es.json b/homeassistant/components/update/translations/es.json index 79993c92b20..ee92c01c938 100644 --- a/homeassistant/components/update/translations/es.json +++ b/homeassistant/components/update/translations/es.json @@ -1,3 +1,10 @@ { + "device_automation": { + "trigger_type": { + "changed_states": "La disponibilidad de la actualizaci\u00f3n de {entity_name} cambie", + "turned_off": "{entity_name} se actualice", + "turned_on": "{entity_name} tenga una actualizaci\u00f3n disponible" + } + }, "title": "Actualizar" } \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/es.json b/homeassistant/components/upnp/translations/es.json index 356376e2e07..7a76ab44bf0 100644 --- a/homeassistant/components/upnp/translations/es.json +++ b/homeassistant/components/upnp/translations/es.json @@ -9,7 +9,7 @@ "one": "UNO", "other": "OTRO" }, - "flow_title": "UPnP / IGD: {name}", + "flow_title": "{name}", "step": { "ssdp_confirm": { "description": "\u00bfQuieres configurar este dispositivo UPnP/IGD?" diff --git a/homeassistant/components/uptimerobot/translations/es.json b/homeassistant/components/uptimerobot/translations/es.json index 455c96cd644..e78a90b4176 100644 --- a/homeassistant/components/uptimerobot/translations/es.json +++ b/homeassistant/components/uptimerobot/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_failed_existing": "No se pudo actualizar la entrada de configuraci\u00f3n, elimine la integraci\u00f3n y config\u00farela nuevamente.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", "unknown": "Error inesperado" @@ -9,6 +9,7 @@ "error": { "cannot_connect": "No se pudo conectar", "invalid_api_key": "Clave API no v\u00e1lida", + "not_main_key": "Se ha detectado un tipo de clave API incorrecta, utiliza la clave API 'principal'", "reauth_failed_matching_account": "La clave de API que has proporcionado no coincide con el ID de cuenta para la configuraci\u00f3n existente.", "unknown": "Error inesperado" }, diff --git a/homeassistant/components/uptimerobot/translations/sensor.es.json b/homeassistant/components/uptimerobot/translations/sensor.es.json new file mode 100644 index 00000000000..1f037738b42 --- /dev/null +++ b/homeassistant/components/uptimerobot/translations/sensor.es.json @@ -0,0 +1,9 @@ +{ + "state": { + "uptimerobot__monitor_status": { + "not_checked_yet": "No comprobado", + "pause": "En pausa", + "up": "Funcionante" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/es.json b/homeassistant/components/utility_meter/translations/es.json new file mode 100644 index 00000000000..bea05df125d --- /dev/null +++ b/homeassistant/components/utility_meter/translations/es.json @@ -0,0 +1,29 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "Ciclo de reinicio del contador", + "delta_values": "Valores delta", + "source": "Sensor de entrada", + "tariffs": "Tarifas soportadas" + }, + "data_description": { + "net_consumption": "Act\u00edvalo si es un contador limpio, es decir, puede aumentar y disminuir.", + "offset": "Desplaza el d\u00eda de restablecimiento mensual del contador.", + "tariffs": "Lista de tarifas admitidas, d\u00e9jala en blanco si utilizas una \u00fanica tarifa." + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "Sensor de entrada" + } + } + } + }, + "title": "Contador" +} \ No newline at end of file diff --git a/homeassistant/components/velbus/translations/es.json b/homeassistant/components/velbus/translations/es.json index cb600d577e7..ed2585a03e9 100644 --- a/homeassistant/components/velbus/translations/es.json +++ b/homeassistant/components/velbus/translations/es.json @@ -10,10 +10,10 @@ "step": { "user": { "data": { - "name": "El nombre de esta conexi\u00f3n velbus", + "name": "Nombre de la conexi\u00f3n Velbus", "port": "Cadena de conexi\u00f3n" }, - "title": "Definir el tipo de conexi\u00f3n velbus" + "title": "Tipo de conexi\u00f3n Velbus" } } } diff --git a/homeassistant/components/verisure/translations/es.json b/homeassistant/components/verisure/translations/es.json index 7ec2812d964..19517bb7ed0 100644 --- a/homeassistant/components/verisure/translations/es.json +++ b/homeassistant/components/verisure/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { diff --git a/homeassistant/components/vulcan/translations/es.json b/homeassistant/components/vulcan/translations/es.json new file mode 100644 index 00000000000..fa303806a76 --- /dev/null +++ b/homeassistant/components/vulcan/translations/es.json @@ -0,0 +1,51 @@ +{ + "config": { + "abort": { + "already_configured": "Ya se ha a\u00f1adido a este alumno.", + "reauth_successful": "Re-autenticaci\u00f3n exitosa" + }, + "error": { + "expired_token": "Token caducado, genera un nuevo token", + "invalid_symbol": "S\u00edmbolo inv\u00e1lido", + "invalid_token": "Token inv\u00e1lido" + }, + "step": { + "auth": { + "data": { + "pin": "PIN" + } + }, + "reauth": { + "data": { + "pin": "PIN", + "region": "S\u00edmbolo", + "token": "Token" + }, + "description": "Inicia sesi\u00f3n en tu cuenta de Vulcan mediante la p\u00e1gina de registro de la aplicaci\u00f3n m\u00f3vil." + }, + "reauth_confirm": { + "data": { + "region": "S\u00edmbolo" + } + }, + "select_student": { + "data": { + "student_name": "Selecciona al alumno" + } + } + } + }, + "options": { + "error": { + "error": "Se ha producido un error" + }, + "step": { + "init": { + "data": { + "message_notify": "Muestra notificaciones cuando se reciba un mensaje nuevo", + "scan_interval": "Intervalo de actualizaci\u00f3n (minutos)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/sk.json b/homeassistant/components/vulcan/translations/sk.json new file mode 100644 index 00000000000..c16ed208d24 --- /dev/null +++ b/homeassistant/components/vulcan/translations/sk.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "add_next_config_entry": { + "description": "Prida\u0165 \u010fal\u0161ieho \u0161tudenta." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/weather/translations/es.json b/homeassistant/components/weather/translations/es.json index 2457f68cf92..a6a7336e612 100644 --- a/homeassistant/components/weather/translations/es.json +++ b/homeassistant/components/weather/translations/es.json @@ -9,7 +9,7 @@ "lightning": "Rel\u00e1mpagos", "lightning-rainy": "Rel\u00e1mpagos, lluvioso", "partlycloudy": "Parcialmente nublado", - "pouring": "Torrencial", + "pouring": "Lluvia", "rainy": "Lluvioso", "snowy": "Nevado", "snowy-rainy": "Nevado, lluvioso", diff --git a/homeassistant/components/weather/translations/sk.json b/homeassistant/components/weather/translations/sk.json index 12c3e530e9e..b174efccf4f 100644 --- a/homeassistant/components/weather/translations/sk.json +++ b/homeassistant/components/weather/translations/sk.json @@ -11,7 +11,7 @@ "partlycloudy": "\u010ciasto\u010dne zamra\u010den\u00e9", "pouring": "Lej\u00faco", "rainy": "Da\u017edivo", - "snowy": "Zasne\u017eeno", + "snowy": "Zasne\u017een\u00fd", "snowy-rainy": "Zasne\u017eeno, da\u017edivo", "sunny": "slne\u010dno", "windy": "Veterno", diff --git a/homeassistant/components/webostv/translations/es.json b/homeassistant/components/webostv/translations/es.json index d15f31b514c..712de905ddc 100644 --- a/homeassistant/components/webostv/translations/es.json +++ b/homeassistant/components/webostv/translations/es.json @@ -1,6 +1,8 @@ { "config": { "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso", "error_pairing": "Conectado a LG webOS TV pero no emparejado" }, "error": { @@ -13,6 +15,9 @@ "title": "Emparejamiento de webOS TV" }, "user": { + "data": { + "name": "Nombre" + }, "description": "Encienda la televisi\u00f3n, rellene los siguientes campos y haga clic en enviar", "title": "Conectarse a webOS TV" } diff --git a/homeassistant/components/wemo/translations/es.json b/homeassistant/components/wemo/translations/es.json index 4c176762d04..29dc3d8db46 100644 --- a/homeassistant/components/wemo/translations/es.json +++ b/homeassistant/components/wemo/translations/es.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "no_devices_found": "No se encontraron dispositivos Wemo en la red.", - "single_instance_allowed": "Solo es posible una \u00fanica configuraci\u00f3n de Wemo." + "no_devices_found": "No se encontraron dispositivos en la red", + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "confirm": { diff --git a/homeassistant/components/whois/translations/es.json b/homeassistant/components/whois/translations/es.json index 712c85865cd..0da233e02ad 100644 --- a/homeassistant/components/whois/translations/es.json +++ b/homeassistant/components/whois/translations/es.json @@ -3,6 +3,10 @@ "abort": { "already_configured": "El servicio ya est\u00e1 configurado" }, + "error": { + "unknown_date_format": "Formato de fecha desconocido en la respuesta del servidor whois", + "whois_command_failed": "El comando whois ha fallado: no se pudo obtener la informaci\u00f3n whois" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/whois/translations/sk.json b/homeassistant/components/whois/translations/sk.json new file mode 100644 index 00000000000..102f110d9ba --- /dev/null +++ b/homeassistant/components/whois/translations/sk.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "user": { + "data": { + "domain": "Dom\u00e9nov\u00e9 meno" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wiffi/translations/es.json b/homeassistant/components/wiffi/translations/es.json index 392392e0f31..dbcec6f2dc9 100644 --- a/homeassistant/components/wiffi/translations/es.json +++ b/homeassistant/components/wiffi/translations/es.json @@ -8,7 +8,7 @@ "step": { "user": { "data": { - "port": "Puerto del servidor" + "port": "Puerto" }, "title": "Configurar servidor TCP para dispositivos WIFFI" } diff --git a/homeassistant/components/wilight/translations/es.json b/homeassistant/components/wilight/translations/es.json index b0104e6e44c..23dfdfcc728 100644 --- a/homeassistant/components/wilight/translations/es.json +++ b/homeassistant/components/wilight/translations/es.json @@ -5,10 +5,10 @@ "not_supported_device": "Este WiLight no es compatible actualmente", "not_wilight_device": "Este dispositivo no es un Wilight" }, - "flow_title": "WiLight: {name}", + "flow_title": "{name}", "step": { "confirm": { - "description": "\u00bfQuieres configurar WiLight {name} ? \n\n Es compatible con: {components}", + "description": "Se admiten los siguientes componentes: {componentes}", "title": "WiLight" } } diff --git a/homeassistant/components/withings/translations/es.json b/homeassistant/components/withings/translations/es.json index 7f83e45c8c9..d3a3f60b12f 100644 --- a/homeassistant/components/withings/translations/es.json +++ b/homeassistant/components/withings/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Configuraci\u00f3n actualizada para el perfil.", - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})" }, @@ -10,16 +10,16 @@ "default": "Autenticado correctamente con Withings." }, "error": { - "already_configured": "La cuenta ya ha sido configurada" + "already_configured": "La cuenta ya est\u00e1 configurada" }, - "flow_title": "Withings: {profile}", + "flow_title": "{profile}", "step": { "pick_implementation": { "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" }, "profile": { "data": { - "profile": "Perfil" + "profile": "Nombre de perfil" }, "description": "\u00bfQu\u00e9 perfil seleccion\u00f3 en el sitio web de Withings? Es importante que los perfiles coincidan, de lo contrario los datos se etiquetar\u00e1n incorrectamente.", "title": "Perfil de usuario." diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index af15f92c2f2..f97ee8e1837 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "cannot_connect": "Nepodarilo sa pripoji\u0165" + }, "step": { "user": { "data": { diff --git a/homeassistant/components/wled/translations/es.json b/homeassistant/components/wled/translations/es.json index d883ba6ff23..07ebd6c2e02 100644 --- a/homeassistant/components/wled/translations/es.json +++ b/homeassistant/components/wled/translations/es.json @@ -8,7 +8,7 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "WLED: {name}", + "flow_title": "{name}", "step": { "user": { "data": { diff --git a/homeassistant/components/ws66i/translations/es.json b/homeassistant/components/ws66i/translations/es.json new file mode 100644 index 00000000000..d19017fdb95 --- /dev/null +++ b/homeassistant/components/ws66i/translations/es.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositivo ya est\u00e1 configurado" + }, + "error": { + "cannot_connect": "Ha fallado la conexi\u00f3n", + "unknown": "Error inesperado" + }, + "step": { + "user": { + "data": { + "ip_address": "Direcci\u00f3n IP" + }, + "title": "Conexi\u00f3n con el dispositivo" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Nombre de la fuente #1", + "source_2": "Nombre de la fuente #2", + "source_3": "Nombre de la fuente #3", + "source_4": "Nombre de la fuente #4", + "source_5": "Nombre de la fuente #5", + "source_6": "Nombre de la fuente #6" + }, + "title": "Configuraci\u00f3n de las fuentes" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/tr.json b/homeassistant/components/ws66i/translations/tr.json new file mode 100644 index 00000000000..5baea0cee9d --- /dev/null +++ b/homeassistant/components/ws66i/translations/tr.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "unknown": "Beklenmeyen hata" + }, + "step": { + "user": { + "data": { + "ip_address": "IP Adresi" + }, + "title": "Cihaza ba\u011flan\u0131n" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "Kaynak #1 ad\u0131", + "source_2": "Kaynak #2 ad\u0131", + "source_3": "Kaynak #3 ad\u0131", + "source_4": "Kaynak #4 ad\u0131", + "source_5": "Kaynak #5 ad\u0131", + "source_6": "Kaynak #6 ad\u0131" + }, + "title": "Kaynaklar\u0131 yap\u0131land\u0131r" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox/translations/es.json b/homeassistant/components/xbox/translations/es.json index 52fb998dfd3..27cbeaec139 100644 --- a/homeassistant/components/xbox/translations/es.json +++ b/homeassistant/components/xbox/translations/es.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "authorize_url_timeout": "Tiempo de espera agotado generando la url de autorizaci\u00f3n.", + "authorize_url_timeout": "Tiempo de espera agotado durante la generaci\u00f3n de la URL de autorizaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Consulta la documentaci\u00f3n.", "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, diff --git a/homeassistant/components/xiaomi_aqara/translations/es.json b/homeassistant/components/xiaomi_aqara/translations/es.json index 04da1a8caf5..671797b914f 100644 --- a/homeassistant/components/xiaomi_aqara/translations/es.json +++ b/homeassistant/components/xiaomi_aqara/translations/es.json @@ -16,7 +16,7 @@ "step": { "select": { "data": { - "select_ip": "IP del gateway" + "select_ip": "Direcci\u00f3n IP" }, "description": "Ejecuta la configuraci\u00f3n de nuevo si deseas conectar gateways adicionales", "title": "Selecciona el Xiaomi Aqara Gateway que quieres conectar" @@ -27,7 +27,7 @@ "name": "Nombre del Gateway" }, "description": "La clave (contrase\u00f1a) se puede obtener con este tutorial: https://www.domoticz.com/wiki/Xiaomi_Gateway_(Aqara)#Adding_the_Xiaomi_Gateway_to_Domoticz. Si no se proporciona la clave solo se podr\u00e1 acceder a los sensores", - "title": "Xiaomi Aqara Gateway, configuraciones opcionales" + "title": "Configuraciones opcionales" }, "user": { "data": { diff --git a/homeassistant/components/xiaomi_miio/translations/es.json b/homeassistant/components/xiaomi_miio/translations/es.json index ae59404a793..2c1dad04111 100644 --- a/homeassistant/components/xiaomi_miio/translations/es.json +++ b/homeassistant/components/xiaomi_miio/translations/es.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "already_in_progress": "El flujo de configuraci\u00f3n para este dispositivo Xiaomi Miio ya est\u00e1 en marcha.", + "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "incomplete_info": "Informaci\u00f3n incompleta para configurar el dispositivo, no se ha suministrado ning\u00fan host o token.", "not_xiaomi_miio": "El dispositivo no es (todav\u00eda) compatible con Xiaomi Miio.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" @@ -10,13 +10,13 @@ "error": { "cannot_connect": "No se pudo conectar", "cloud_credentials_incomplete": "Las credenciales de la nube est\u00e1n incompletas, por favor, rellene el nombre de usuario, la contrase\u00f1a y el pa\u00eds", - "cloud_login_error": "No se ha podido iniciar sesi\u00f3n en Xioami Miio Cloud, comprueba las credenciales.", + "cloud_login_error": "No se ha podido iniciar sesi\u00f3n en Xiaomi Miio Cloud, comprueba las credenciales.", "cloud_no_devices": "No se han encontrado dispositivos en esta cuenta de Xiaomi Miio.", "no_device_selected": "No se ha seleccionado ning\u00fan dispositivo, por favor, seleccione un dispositivo.", "unknown_device": "No se conoce el modelo del dispositivo, no se puede configurar el dispositivo mediante el flujo de configuraci\u00f3n.", "wrong_token": "Error de suma de comprobaci\u00f3n, token err\u00f3neo" }, - "flow_title": "Xiaomi Miio: {name}", + "flow_title": "{name}", "step": { "cloud": { "data": { diff --git a/homeassistant/components/xiaomi_miio/translations/select.sk.json b/homeassistant/components/xiaomi_miio/translations/select.sk.json new file mode 100644 index 00000000000..8745c700fe6 --- /dev/null +++ b/homeassistant/components/xiaomi_miio/translations/select.sk.json @@ -0,0 +1,7 @@ +{ + "state": { + "xiaomi_miio__led_brightness": { + "off": "Vypnut\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yale_smart_alarm/translations/es.json b/homeassistant/components/yale_smart_alarm/translations/es.json index 4df58fda1b7..e9c24aab7f2 100644 --- a/homeassistant/components/yale_smart_alarm/translations/es.json +++ b/homeassistant/components/yale_smart_alarm/translations/es.json @@ -1,10 +1,11 @@ { "config": { "abort": { - "already_configured": "La cuenta ya ha sido configurada", + "already_configured": "La cuenta ya est\u00e1 configurada", "reauth_successful": "La reautenticaci\u00f3n fue exitosa" }, "error": { + "cannot_connect": "Fall\u00f3 la conexi\u00f3n", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "step": { diff --git a/homeassistant/components/yale_smart_alarm/translations/sk.json b/homeassistant/components/yale_smart_alarm/translations/sk.json index 00dddc88d1d..6130380de31 100644 --- a/homeassistant/components/yale_smart_alarm/translations/sk.json +++ b/homeassistant/components/yale_smart_alarm/translations/sk.json @@ -9,7 +9,8 @@ "step": { "reauth_confirm": { "data": { - "name": "N\u00e1zov" + "name": "N\u00e1zov", + "password": "Heslo" } }, "user": { diff --git a/homeassistant/components/yamaha_musiccast/translations/sk.json b/homeassistant/components/yamaha_musiccast/translations/sk.json new file mode 100644 index 00000000000..f74ba4b46d2 --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/translations/sk.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_configured": "Zariadenie je u\u017e nakonfigurovan\u00e9" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zha/translations/es.json b/homeassistant/components/zha/translations/es.json index 36ab27c53b5..d8162401f3d 100644 --- a/homeassistant/components/zha/translations/es.json +++ b/homeassistant/components/zha/translations/es.json @@ -8,7 +8,7 @@ "error": { "cannot_connect": "No se pudo conectar" }, - "flow_title": "ZHA: {name}", + "flow_title": "{name}", "step": { "confirm": { "description": "\u00bfQuieres configurar {name} ?" diff --git a/homeassistant/components/zwave_js/translations/es.json b/homeassistant/components/zwave_js/translations/es.json index 1002d0ad0b3..dfe8c1de296 100644 --- a/homeassistant/components/zwave_js/translations/es.json +++ b/homeassistant/components/zwave_js/translations/es.json @@ -59,6 +59,10 @@ }, "usb_confirm": { "description": "\u00bfQuieres configurar {name} con el complemento Z-Wave JS?" + }, + "zeroconf_confirm": { + "description": "\u00bfQuieres a\u00f1adir el servidor Z-Wave JS con ID {home_id} que se encuentra en {url} en Home Assistant?", + "title": "Servidor Z-Wave JS descubierto" } } }, diff --git a/homeassistant/components/zwave_me/translations/es.json b/homeassistant/components/zwave_me/translations/es.json index eab23bbd0ff..2443547e59d 100644 --- a/homeassistant/components/zwave_me/translations/es.json +++ b/homeassistant/components/zwave_me/translations/es.json @@ -10,7 +10,7 @@ "step": { "user": { "data": { - "token": "Token", + "token": "Token API", "url": "URL" }, "description": "Direcci\u00f3n IP de entrada del servidor Z-Way y token de acceso Z-Way. La direcci\u00f3n IP se puede prefijar con wss:// si se debe usar HTTPS en lugar de HTTP. Para obtener el token, vaya a la interfaz de usuario de Z-Way > Configuraci\u00f3n de > de men\u00fa > token de API de > de usuario. Se sugiere crear un nuevo usuario para Home Assistant y conceder acceso a los dispositivos que necesita controlar desde Home Assistant. Tambi\u00e9n es posible utilizar el acceso remoto a trav\u00e9s de find.z-wave.me para conectar un Z-Way remoto. Ingrese wss://find.z-wave.me en el campo IP y copie el token con alcance global (inicie sesi\u00f3n en Z-Way a trav\u00e9s de find.z-wave.me para esto)." From 089eb9960a835be6e0f336b644d2b62f5c2feaad Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 22:48:57 -0500 Subject: [PATCH 478/930] Reduce logbook websocket payload size and parse json attributes via the DBM (#71895) --- homeassistant/components/logbook/__init__.py | 45 +++++++++++--------- homeassistant/components/logbook/queries.py | 38 ++++++++++++++--- homeassistant/components/recorder/models.py | 3 ++ tests/components/logbook/test_init.py | 8 ++-- 4 files changed, 64 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 4bef1f1a23d..35fa17bb3a4 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -40,7 +40,6 @@ from homeassistant.const import ( ATTR_SERVICE, EVENT_CALL_SERVICE, EVENT_LOGBOOK_ENTRY, - EVENT_STATE_CHANGED, ) from homeassistant.core import ( Context, @@ -65,14 +64,12 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass import homeassistant.util.dt as dt_util -from .queries import statement_for_request +from .queries import PSUEDO_EVENT_STATE_CHANGED, statement_for_request _LOGGER = logging.getLogger(__name__) -FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') -ICON_JSON_EXTRACT = re.compile('"icon": ?"([^"]+)"') ATTR_MESSAGE = "message" DOMAIN = "logbook" @@ -235,6 +232,7 @@ def _ws_formatted_get_events( entities_filter, context_id, True, + False, ), ) ) @@ -368,6 +366,7 @@ class LogbookView(HomeAssistantView): self.entities_filter, context_id, False, + True, ) ) @@ -385,6 +384,7 @@ def _humanify( ], entity_name_cache: EntityNameCache, format_time: Callable[[Row], Any], + include_entity_name: bool = True, ) -> Generator[dict[str, Any], None, None]: """Generate a converted list of events into entries.""" # Continuous sensors, will be excluded from the logbook @@ -419,13 +419,13 @@ def _humanify( continue event_type = row.event_type if event_type == EVENT_CALL_SERVICE or ( - event_type != EVENT_STATE_CHANGED + event_type is not PSUEDO_EVENT_STATE_CHANGED and entities_filter is not None and not _keep_row(row, event_type) ): continue - if event_type == EVENT_STATE_CHANGED: + if event_type is PSUEDO_EVENT_STATE_CHANGED: entity_id = row.entity_id assert entity_id is not None # Skip continuous sensors @@ -439,14 +439,15 @@ def _humanify( data = { LOGBOOK_ENTRY_WHEN: format_time(row), - LOGBOOK_ENTRY_NAME: entity_name_cache.get(entity_id, row), LOGBOOK_ENTRY_STATE: row.state, LOGBOOK_ENTRY_ENTITY_ID: entity_id, } - if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): + if include_entity_name: + data[LOGBOOK_ENTRY_NAME] = entity_name_cache.get(entity_id, row) + if icon := row.icon or row.old_format_icon: data[LOGBOOK_ENTRY_ICON] = icon - context_augmenter.augment(data, row, context_id) + context_augmenter.augment(data, row, context_id, include_entity_name) yield data elif event_type in external_events: @@ -454,7 +455,7 @@ def _humanify( data = describe_event(event_cache.get(row)) data[LOGBOOK_ENTRY_WHEN] = format_time(row) data[LOGBOOK_ENTRY_DOMAIN] = domain - context_augmenter.augment(data, row, context_id) + context_augmenter.augment(data, row, context_id, include_entity_name) yield data elif event_type == EVENT_LOGBOOK_ENTRY: @@ -474,7 +475,7 @@ def _humanify( LOGBOOK_ENTRY_DOMAIN: entry_domain, LOGBOOK_ENTRY_ENTITY_ID: entry_entity_id, } - context_augmenter.augment(data, row, context_id) + context_augmenter.augment(data, row, context_id, include_entity_name) yield data @@ -487,6 +488,7 @@ def _get_events( entities_filter: EntityFilter | Callable[[str], bool] | None = None, context_id: str | None = None, timestamp: bool = False, + include_entity_name: bool = True, ) -> list[dict[str, Any]]: """Get events for a period of time.""" assert not ( @@ -540,6 +542,7 @@ def _get_events( external_events, entity_name_cache, format_time, + include_entity_name, ) ) @@ -562,7 +565,9 @@ class ContextAugmenter: self.external_events = external_events self.event_cache = event_cache - def augment(self, data: dict[str, Any], row: Row, context_id: str) -> None: + def augment( + self, data: dict[str, Any], row: Row, context_id: str, include_entity_name: bool + ) -> None: """Augment data from the row and cache.""" if context_user_id := row.context_user_id: data[CONTEXT_USER_ID] = context_user_id @@ -589,9 +594,10 @@ class ContextAugmenter: # State change if context_entity_id := context_row.entity_id: data[CONTEXT_ENTITY_ID] = context_entity_id - data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( - context_entity_id, context_row - ) + if include_entity_name: + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( + context_entity_id, context_row + ) data[CONTEXT_EVENT_TYPE] = event_type return @@ -619,9 +625,10 @@ class ContextAugmenter: if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): return data[CONTEXT_ENTITY_ID] = attr_entity_id - data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( - attr_entity_id, context_row - ) + if include_entity_name: + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( + attr_entity_id, context_row + ) def _is_sensor_continuous(ent_reg: er.EntityRegistry, entity_id: str) -> bool: @@ -735,8 +742,6 @@ class EntityNameCache: friendly_name := current_state.attributes.get(ATTR_FRIENDLY_NAME) ): self._names[entity_id] = friendly_name - elif extracted_name := _row_attributes_extract(row, FRIENDLY_NAME_JSON_EXTRACT): - self._names[entity_id] = extracted_name else: return split_entity_id(entity_id)[1].replace("_", " ") diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py index 89c530aec43..6fe20bfc561 100644 --- a/homeassistant/components/logbook/queries.py +++ b/homeassistant/components/logbook/queries.py @@ -5,7 +5,7 @@ from collections.abc import Iterable from datetime import datetime as dt import sqlalchemy -from sqlalchemy import lambda_stmt, select, union_all +from sqlalchemy import JSON, lambda_stmt, select, type_coerce, union_all from sqlalchemy.orm import Query, aliased from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.expression import literal @@ -16,6 +16,7 @@ from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder.filters import Filters from homeassistant.components.recorder.models import ( ENTITY_ID_LAST_UPDATED_INDEX, + JSON_VARIENT_CAST, LAST_UPDATED_INDEX, EventData, Events, @@ -23,7 +24,6 @@ from homeassistant.components.recorder.models import ( States, ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import EVENT_STATE_CHANGED ENTITY_ID_JSON_TEMPLATE = '%"entity_id":"{}"%' @@ -36,6 +36,22 @@ UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" OLD_STATE = aliased(States, name="old_state") +SHARED_ATTRS_JSON = type_coerce( + StateAttributes.shared_attrs.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) +OLD_FORMAT_ATTRS_JSON = type_coerce( + States.attributes.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) + + +PSUEDO_EVENT_STATE_CHANGED = None +# Since we don't store event_types and None +# and we don't store state_changed in events +# we use a NULL for state_changed events +# when we synthesize them from the states table +# since it avoids another column being sent +# in the payload + EVENT_COLUMNS = ( Events.event_id.label("event_id"), Events.event_type.label("event_type"), @@ -50,18 +66,20 @@ STATE_COLUMNS = ( States.state_id.label("state_id"), States.state.label("state"), States.entity_id.label("entity_id"), - States.attributes.label("attributes"), - StateAttributes.shared_attrs.label("shared_attrs"), + SHARED_ATTRS_JSON["icon"].as_string().label("icon"), + OLD_FORMAT_ATTRS_JSON["icon"].as_string().label("old_format_icon"), ) + EMPTY_STATE_COLUMNS = ( literal(value=None, type_=sqlalchemy.String).label("state_id"), literal(value=None, type_=sqlalchemy.String).label("state"), literal(value=None, type_=sqlalchemy.String).label("entity_id"), - literal(value=None, type_=sqlalchemy.Text).label("attributes"), - literal(value=None, type_=sqlalchemy.Text).label("shared_attrs"), + literal(value=None, type_=sqlalchemy.String).label("icon"), + literal(value=None, type_=sqlalchemy.String).label("old_format_icon"), ) + EVENT_ROWS_NO_STATES = ( *EVENT_COLUMNS, EventData.shared_data.label("shared_data"), @@ -326,7 +344,13 @@ def _select_states() -> Select: """Generate a states select that formats the states table as event rows.""" return select( literal(value=None, type_=sqlalchemy.Text).label("event_id"), - literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label("event_type"), + # We use PSUEDO_EVENT_STATE_CHANGED aka None for + # state_changed events since it takes up less + # space in the response and every row has to be + # marked with the event_type + literal(value=PSUEDO_EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( + "event_type" + ), literal(value=None, type_=sqlalchemy.Text).label("event_data"), States.last_updated.label("time_fired"), States.context_id.label("context_id"), diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index d64d85f3ce4..f5498e941d3 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -102,6 +102,9 @@ class FAST_PYSQLITE_DATETIME(sqlite.DATETIME): # type: ignore[misc] return lambda value: None if value is None else ciso8601.parse_datetime(value) +JSON_VARIENT_CAST = Text().with_variant( + postgresql.JSON(none_as_null=True), "postgresql" +) DATETIME_TYPE = ( DateTime(timezone=True) .with_variant(mysql.DATETIME(timezone=True, fsp=6), "mysql") diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index a515afdf16e..ed95b4d10bc 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -30,7 +30,6 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, - EVENT_STATE_CHANGED, STATE_OFF, STATE_ON, ) @@ -327,7 +326,7 @@ def create_state_changed_event_from_old_new( ], ) - row.event_type = EVENT_STATE_CHANGED + row.event_type = logbook.PSUEDO_EVENT_STATE_CHANGED row.event_data = "{}" row.shared_data = "{}" row.attributes = attributes_json @@ -338,6 +337,9 @@ def create_state_changed_event_from_old_new( row.domain = entity_id and ha.split_entity_id(entity_id)[0] row.context_only = False row.context_id = None + row.friendly_name = None + row.icon = None + row.old_format_icon = None row.context_user_id = None row.context_parent_id = None row.old_state_id = old_state and 1 @@ -719,7 +721,7 @@ async def test_logbook_entity_no_longer_in_state_machine( ) assert response.status == HTTPStatus.OK json_dict = await response.json() - assert json_dict[0]["name"] == "Alarm Control Panel" + assert json_dict[0]["name"] == "area 001" async def test_filter_continuous_sensor_values( From a70e2a33dcd85608f1145d8fc2e89a87620f4ef3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 15 May 2022 23:25:07 -0500 Subject: [PATCH 479/930] Fixing purging legacy rows and improve performance (#71916) --- homeassistant/components/recorder/models.py | 10 +- homeassistant/components/recorder/purge.py | 289 ++++++++++++++---- homeassistant/components/recorder/queries.py | 19 ++ tests/components/recorder/test_purge.py | 293 +++++++++++++++++-- 4 files changed, 530 insertions(+), 81 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index f5498e941d3..d7bb59bdeb1 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -130,10 +130,10 @@ class Events(Base): # type: ignore[misc,valid-type] {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, ) __tablename__ = TABLE_EVENTS - event_id = Column(Integer, Identity(), primary_key=True) # no longer used + event_id = Column(Integer, Identity(), primary_key=True) event_type = Column(String(MAX_LENGTH_EVENT_EVENT_TYPE)) event_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) - origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) # no longer used + origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) # no longer used for new rows origin_idx = Column(SmallInteger) time_fired = Column(DATETIME_TYPE, index=True) context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) @@ -247,8 +247,10 @@ class States(Base): # type: ignore[misc,valid-type] state_id = Column(Integer, Identity(), primary_key=True) entity_id = Column(String(MAX_LENGTH_STATE_ENTITY_ID)) state = Column(String(MAX_LENGTH_STATE_STATE)) - attributes = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) - event_id = Column( + attributes = Column( + Text().with_variant(mysql.LONGTEXT, "mysql") + ) # no longer used for new rows + event_id = Column( # no longer used for new rows Integer, ForeignKey("events.event_id", ondelete="CASCADE"), index=True ) last_changed = Column(DATETIME_TYPE) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index f94f3d3d641..432e3993add 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -3,9 +3,10 @@ from __future__ import annotations from collections.abc import Callable, Iterable from datetime import datetime -from itertools import zip_longest +from functools import partial +from itertools import islice, zip_longest import logging -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import distinct @@ -29,6 +30,8 @@ from .queries import ( disconnect_states_rows, find_events_to_purge, find_latest_statistics_runs_run_id, + find_legacy_event_state_and_attributes_and_data_ids_to_purge, + find_legacy_row, find_short_term_statistics_to_purge, find_states_to_purge, find_statistics_runs_to_purge, @@ -42,9 +45,34 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) +DEFAULT_STATES_BATCHES_PER_PURGE = 20 # We expect ~95% de-dupe rate +DEFAULT_EVENTS_BATCHES_PER_PURGE = 15 # We expect ~92% de-dupe rate + + +def take(take_num: int, iterable: Iterable) -> list[Any]: + """Return first n items of the iterable as a list. + + From itertools recipes + """ + return list(islice(iterable, take_num)) + + +def chunked(iterable: Iterable, chunked_num: int) -> Iterable[Any]: + """Break *iterable* into lists of length *n*. + + From more-itertools + """ + return iter(partial(take, chunked_num, iter(iterable)), []) + + @retryable_database_job("purge") def purge_old_data( - instance: Recorder, purge_before: datetime, repack: bool, apply_filter: bool = False + instance: Recorder, + purge_before: datetime, + repack: bool, + apply_filter: bool = False, + events_batch_size: int = DEFAULT_EVENTS_BATCHES_PER_PURGE, + states_batch_size: int = DEFAULT_STATES_BATCHES_PER_PURGE, ) -> bool: """Purge events and states older than purge_before. @@ -58,40 +86,37 @@ def purge_old_data( with session_scope(session=instance.get_session()) as session: # Purge a max of MAX_ROWS_TO_PURGE, based on the oldest states or events record - ( - event_ids, - state_ids, - attributes_ids, - data_ids, - ) = _select_event_state_attributes_ids_data_ids_to_purge(session, purge_before) + has_more_to_purge = False + if _purging_legacy_format(session): + _LOGGER.debug( + "Purge running in legacy format as there are states with event_id remaining" + ) + has_more_to_purge |= _purge_legacy_format( + instance, session, purge_before, using_sqlite + ) + else: + _LOGGER.debug( + "Purge running in new format as there are NO states with event_id remaining" + ) + # Once we are done purging legacy rows, we use the new method + has_more_to_purge |= _purge_states_and_attributes_ids( + instance, session, states_batch_size, purge_before, using_sqlite + ) + has_more_to_purge |= _purge_events_and_data_ids( + instance, session, events_batch_size, purge_before, using_sqlite + ) + statistics_runs = _select_statistics_runs_to_purge(session, purge_before) short_term_statistics = _select_short_term_statistics_to_purge( session, purge_before ) - - if state_ids: - _purge_state_ids(instance, session, state_ids) - - if unused_attribute_ids_set := _select_unused_attributes_ids( - session, attributes_ids, using_sqlite - ): - _purge_attributes_ids(instance, session, unused_attribute_ids_set) - - if event_ids: - _purge_event_ids(session, event_ids) - - if unused_data_ids_set := _select_unused_event_data_ids( - session, data_ids, using_sqlite - ): - _purge_event_data_ids(instance, session, unused_data_ids_set) - if statistics_runs: _purge_statistics_runs(session, statistics_runs) if short_term_statistics: _purge_short_term_statistics(session, short_term_statistics) - if state_ids or event_ids or statistics_runs or short_term_statistics: + if has_more_to_purge or statistics_runs or short_term_statistics: # Return false, as we might not be done yet. _LOGGER.debug("Purging hasn't fully completed yet") return False @@ -106,27 +131,132 @@ def purge_old_data( return True -def _select_event_state_attributes_ids_data_ids_to_purge( +def _purging_legacy_format(session: Session) -> bool: + """Check if there are any legacy event_id linked states rows remaining.""" + return bool(session.execute(find_legacy_row()).scalar()) + + +def _purge_legacy_format( + instance: Recorder, session: Session, purge_before: datetime, using_sqlite: bool +) -> bool: + """Purge rows that are still linked by the event_ids.""" + ( + event_ids, + state_ids, + attributes_ids, + data_ids, + ) = _select_legacy_event_state_and_attributes_and_data_ids_to_purge( + session, purge_before + ) + if state_ids: + _purge_state_ids(instance, session, state_ids) + _purge_unused_attributes_ids(instance, session, attributes_ids, using_sqlite) + if event_ids: + _purge_event_ids(session, event_ids) + _purge_unused_data_ids(instance, session, data_ids, using_sqlite) + return bool(event_ids or state_ids or attributes_ids or data_ids) + + +def _purge_states_and_attributes_ids( + instance: Recorder, + session: Session, + states_batch_size: int, + purge_before: datetime, + using_sqlite: bool, +) -> bool: + """Purge states and linked attributes id in a batch. + + Returns true if there are more states to purge. + """ + has_remaining_state_ids_to_purge = True + # There are more states relative to attributes_ids so + # we purge enough state_ids to try to generate a full + # size batch of attributes_ids that will be around the size + # MAX_ROWS_TO_PURGE + attributes_ids_batch: set[int] = set() + for _ in range(states_batch_size): + state_ids, attributes_ids = _select_state_attributes_ids_to_purge( + session, purge_before + ) + if not state_ids: + has_remaining_state_ids_to_purge = False + break + _purge_state_ids(instance, session, state_ids) + attributes_ids_batch = attributes_ids_batch | attributes_ids + + _purge_unused_attributes_ids(instance, session, attributes_ids_batch, using_sqlite) + _LOGGER.debug( + "After purging states and attributes_ids remaining=%s", + has_remaining_state_ids_to_purge, + ) + return has_remaining_state_ids_to_purge + + +def _purge_events_and_data_ids( + instance: Recorder, + session: Session, + events_batch_size: int, + purge_before: datetime, + using_sqlite: bool, +) -> bool: + """Purge states and linked attributes id in a batch. + + Returns true if there are more states to purge. + """ + has_remaining_event_ids_to_purge = True + # There are more events relative to data_ids so + # we purge enough event_ids to try to generate a full + # size batch of data_ids that will be around the size + # MAX_ROWS_TO_PURGE + data_ids_batch: set[int] = set() + for _ in range(events_batch_size): + event_ids, data_ids = _select_event_data_ids_to_purge(session, purge_before) + if not event_ids: + has_remaining_event_ids_to_purge = False + break + _purge_event_ids(session, event_ids) + data_ids_batch = data_ids_batch | data_ids + + _purge_unused_data_ids(instance, session, data_ids_batch, using_sqlite) + _LOGGER.debug( + "After purging event and data_ids remaining=%s", + has_remaining_event_ids_to_purge, + ) + return has_remaining_event_ids_to_purge + + +def _select_state_attributes_ids_to_purge( session: Session, purge_before: datetime -) -> tuple[set[int], set[int], set[int], set[int]]: - """Return a list of event, state, and attribute ids to purge.""" - events = session.execute(find_events_to_purge(purge_before)).all() - _LOGGER.debug("Selected %s event ids to remove", len(events)) - states = session.execute(find_states_to_purge(purge_before)).all() - _LOGGER.debug("Selected %s state ids to remove", len(states)) - event_ids = set() +) -> tuple[set[int], set[int]]: + """Return sets of state and attribute ids to purge.""" state_ids = set() attributes_ids = set() - data_ids = set() - for event in events: - event_ids.add(event.event_id) - if event.data_id: - data_ids.add(event.data_id) - for state in states: + for state in session.execute(find_states_to_purge(purge_before)).all(): state_ids.add(state.state_id) if state.attributes_id: attributes_ids.add(state.attributes_id) - return event_ids, state_ids, attributes_ids, data_ids + _LOGGER.debug( + "Selected %s state ids and %s attributes_ids to remove", + len(state_ids), + len(attributes_ids), + ) + return state_ids, attributes_ids + + +def _select_event_data_ids_to_purge( + session: Session, purge_before: datetime +) -> tuple[set[int], set[int]]: + """Return sets of event and data ids to purge.""" + event_ids = set() + data_ids = set() + for event in session.execute(find_events_to_purge(purge_before)).all(): + event_ids.add(event.event_id) + if event.data_id: + data_ids.add(event.data_id) + _LOGGER.debug( + "Selected %s event ids and %s data_ids to remove", len(event_ids), len(data_ids) + ) + return event_ids, data_ids def _select_unused_attributes_ids( @@ -197,6 +327,18 @@ def _select_unused_attributes_ids( return to_remove +def _purge_unused_attributes_ids( + instance: Recorder, + session: Session, + attributes_ids_batch: set[int], + using_sqlite: bool, +) -> None: + if unused_attribute_ids_set := _select_unused_attributes_ids( + session, attributes_ids_batch, using_sqlite + ): + _purge_batch_attributes_ids(instance, session, unused_attribute_ids_set) + + def _select_unused_event_data_ids( session: Session, data_ids: set[int], using_sqlite: bool ) -> set[int]: @@ -229,6 +371,16 @@ def _select_unused_event_data_ids( return to_remove +def _purge_unused_data_ids( + instance: Recorder, session: Session, data_ids_batch: set[int], using_sqlite: bool +) -> None: + + if unused_data_ids_set := _select_unused_event_data_ids( + session, data_ids_batch, using_sqlite + ): + _purge_batch_data_ids(instance, session, unused_data_ids_set) + + def _select_statistics_runs_to_purge( session: Session, purge_before: datetime ) -> list[int]: @@ -256,6 +408,34 @@ def _select_short_term_statistics_to_purge( return [statistic.id for statistic in statistics] +def _select_legacy_event_state_and_attributes_and_data_ids_to_purge( + session: Session, purge_before: datetime +) -> tuple[set[int], set[int], set[int], set[int]]: + """Return a list of event, state, and attribute ids to purge that are linked by the event_id. + + We do not link these anymore since state_change events + do not exist in the events table anymore, however we + still need to be able to purge them. + """ + events = session.execute( + find_legacy_event_state_and_attributes_and_data_ids_to_purge(purge_before) + ).all() + _LOGGER.debug("Selected %s event ids to remove", len(events)) + event_ids = set() + state_ids = set() + attributes_ids = set() + data_ids = set() + for event in events: + event_ids.add(event.event_id) + if event.state_id: + state_ids.add(event.state_id) + if event.attributes_id: + attributes_ids.add(event.attributes_id) + if event.data_id: + data_ids.add(event.data_id) + return event_ids, state_ids, attributes_ids, data_ids + + def _purge_state_ids(instance: Recorder, session: Session, state_ids: set[int]) -> None: """Disconnect states and delete by state id.""" @@ -327,24 +507,27 @@ def _evict_purged_attributes_from_attributes_cache( ) -def _purge_attributes_ids( +def _purge_batch_attributes_ids( instance: Recorder, session: Session, attributes_ids: set[int] ) -> None: - """Delete old attributes ids.""" - deleted_rows = session.execute(delete_states_attributes_rows(attributes_ids)) - _LOGGER.debug("Deleted %s attribute states", deleted_rows) + """Delete old attributes ids in batches of MAX_ROWS_TO_PURGE.""" + for attributes_ids_chunk in chunked(attributes_ids, MAX_ROWS_TO_PURGE): + deleted_rows = session.execute( + delete_states_attributes_rows(attributes_ids_chunk) + ) + _LOGGER.debug("Deleted %s attribute states", deleted_rows) # Evict any entries in the state_attributes_ids cache referring to a purged state _evict_purged_attributes_from_attributes_cache(instance, attributes_ids) -def _purge_event_data_ids( +def _purge_batch_data_ids( instance: Recorder, session: Session, data_ids: set[int] ) -> None: - """Delete old event data ids.""" - - deleted_rows = session.execute(delete_event_data_rows(data_ids)) - _LOGGER.debug("Deleted %s data events", deleted_rows) + """Delete old event data ids in batches of MAX_ROWS_TO_PURGE.""" + for data_ids_chunk in chunked(data_ids, MAX_ROWS_TO_PURGE): + deleted_rows = session.execute(delete_event_data_rows(data_ids_chunk)) + _LOGGER.debug("Deleted %s data events", deleted_rows) # Evict any entries in the event_data_ids cache referring to a purged state _evict_purged_data_from_data_cache(instance, data_ids) @@ -438,7 +621,7 @@ def _purge_filtered_states( unused_attribute_ids_set = _select_unused_attributes_ids( session, {id_ for id_ in attributes_ids if id_ is not None}, using_sqlite ) - _purge_attributes_ids(instance, session, unused_attribute_ids_set) + _purge_batch_attributes_ids(instance, session, unused_attribute_ids_set) def _purge_filtered_events( @@ -466,7 +649,7 @@ def _purge_filtered_events( if unused_data_ids_set := _select_unused_event_data_ids( session, set(data_ids), using_sqlite ): - _purge_event_data_ids(instance, session, unused_data_ids_set) + _purge_batch_data_ids(instance, session, unused_data_ids_set) if EVENT_STATE_CHANGED in excluded_event_types: session.query(StateAttributes).delete(synchronize_session=False) instance._state_attributes_ids = {} # pylint: disable=protected-access diff --git a/homeassistant/components/recorder/queries.py b/homeassistant/components/recorder/queries.py index 76098663bd7..5532c5c0703 100644 --- a/homeassistant/components/recorder/queries.py +++ b/homeassistant/components/recorder/queries.py @@ -621,3 +621,22 @@ def find_statistics_runs_to_purge( def find_latest_statistics_runs_run_id() -> StatementLambdaElement: """Find the latest statistics_runs run_id.""" return lambda_stmt(lambda: select(func.max(StatisticsRuns.run_id))) + + +def find_legacy_event_state_and_attributes_and_data_ids_to_purge( + purge_before: datetime, +) -> StatementLambdaElement: + """Find the latest row in the legacy format to purge.""" + return lambda_stmt( + lambda: select( + Events.event_id, Events.data_id, States.state_id, States.attributes_id + ) + .join(States, Events.event_id == States.event_id) + .filter(Events.time_fired < purge_before) + .limit(MAX_ROWS_TO_PURGE) + ) + + +def find_legacy_row() -> StatementLambdaElement: + """Check if there are still states in the table with an event_id.""" + return lambda_stmt(lambda: select(func.max(States.event_id))) diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index de9db9d5014..c8ba5e9d076 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -11,6 +11,7 @@ from sqlalchemy.orm.session import Session from homeassistant.components import recorder from homeassistant.components.recorder.const import MAX_ROWS_TO_PURGE, SupportedDialect from homeassistant.components.recorder.models import ( + EventData, Events, RecorderRuns, StateAttributes, @@ -76,7 +77,13 @@ async def test_purge_old_states( purge_before = dt_util.utcnow() - timedelta(days=4) # run purge_old_data() - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + states_batch_size=1, + events_batch_size=1, + repack=False, + ) assert not finished assert states.count() == 2 assert state_attributes.count() == 1 @@ -96,7 +103,13 @@ async def test_purge_old_states( # run purge_old_data again purge_before = dt_util.utcnow() - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + states_batch_size=1, + events_batch_size=1, + repack=False, + ) assert not finished assert states.count() == 0 assert state_attributes.count() == 0 @@ -223,12 +236,24 @@ async def test_purge_old_events( purge_before = dt_util.utcnow() - timedelta(days=4) # run purge_old_data() - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=1, + states_batch_size=1, + ) assert not finished assert events.count() == 2 # we should only have 2 events left - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=1, + states_batch_size=1, + ) assert finished assert events.count() == 2 @@ -249,10 +274,22 @@ async def test_purge_old_recorder_runs( purge_before = dt_util.utcnow() # run purge_old_data() - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=1, + states_batch_size=1, + ) assert not finished - finished = purge_old_data(instance, purge_before, repack=False) + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=1, + states_batch_size=1, + ) assert finished assert recorder_runs.count() == 1 @@ -1271,7 +1308,7 @@ async def _add_test_states(hass: HomeAssistant): await set_state("test.recorder2", state, attributes=attributes) -async def _add_test_events(hass: HomeAssistant): +async def _add_test_events(hass: HomeAssistant, iterations: int = 1): """Add a few events for testing.""" utcnow = dt_util.utcnow() five_days_ago = utcnow - timedelta(days=5) @@ -1282,25 +1319,64 @@ async def _add_test_events(hass: HomeAssistant): await async_wait_recording_done(hass) with session_scope(hass=hass) as session: - for event_id in range(6): - if event_id < 2: - timestamp = eleven_days_ago - event_type = "EVENT_TEST_AUTOPURGE" - elif event_id < 4: - timestamp = five_days_ago - event_type = "EVENT_TEST_PURGE" - else: - timestamp = utcnow - event_type = "EVENT_TEST" + for _ in range(iterations): + for event_id in range(6): + if event_id < 2: + timestamp = eleven_days_ago + event_type = "EVENT_TEST_AUTOPURGE" + elif event_id < 4: + timestamp = five_days_ago + event_type = "EVENT_TEST_PURGE" + else: + timestamp = utcnow + event_type = "EVENT_TEST" - session.add( - Events( - event_type=event_type, - event_data=json.dumps(event_data), - origin="LOCAL", - time_fired=timestamp, + session.add( + Events( + event_type=event_type, + event_data=json.dumps(event_data), + origin="LOCAL", + time_fired=timestamp, + ) + ) + + +async def _add_events_with_event_data(hass: HomeAssistant, iterations: int = 1): + """Add a few events with linked event_data for testing.""" + utcnow = dt_util.utcnow() + five_days_ago = utcnow - timedelta(days=5) + eleven_days_ago = utcnow - timedelta(days=11) + event_data = {"test_attr": 5, "test_attr_10": "nice"} + + await hass.async_block_till_done() + await async_wait_recording_done(hass) + + with session_scope(hass=hass) as session: + for _ in range(iterations): + for event_id in range(6): + if event_id < 2: + timestamp = eleven_days_ago + event_type = "EVENT_TEST_AUTOPURGE_WITH_EVENT_DATA" + shared_data = '{"type":{"EVENT_TEST_AUTOPURGE_WITH_EVENT_DATA"}' + elif event_id < 4: + timestamp = five_days_ago + event_type = "EVENT_TEST_PURGE_WITH_EVENT_DATA" + shared_data = '{"type":{"EVENT_TEST_PURGE_WITH_EVENT_DATA"}' + else: + timestamp = utcnow + event_type = "EVENT_TEST_WITH_EVENT_DATA" + shared_data = '{"type":{"EVENT_TEST_WITH_EVENT_DATA"}' + + event_data = EventData(hash=1234, shared_data=shared_data) + + session.add( + Events( + event_type=event_type, + origin="LOCAL", + time_fired=timestamp, + event_data_rel=event_data, + ) ) - ) async def _add_test_statistics(hass: HomeAssistant): @@ -1384,6 +1460,29 @@ async def _add_test_statistics_runs(hass: HomeAssistant): ) +def _add_state_without_event_linkage( + session: Session, + entity_id: str, + state: str, + timestamp: datetime, +): + state_attrs = StateAttributes( + hash=1234, shared_attrs=json.dumps({entity_id: entity_id}) + ) + session.add(state_attrs) + session.add( + States( + entity_id=entity_id, + state=state, + attributes=None, + last_changed=timestamp, + last_updated=timestamp, + event_id=None, + state_attributes=state_attrs, + ) + ) + + def _add_state_and_state_changed_event( session: Session, entity_id: str, @@ -1416,3 +1515,149 @@ def _add_state_and_state_changed_event( time_fired=timestamp, ) ) + + +async def test_purge_many_old_events( + hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT +): + """Test deleting old events.""" + instance = await async_setup_recorder_instance(hass) + + await _add_test_events(hass, MAX_ROWS_TO_PURGE) + + with session_scope(hass=hass) as session: + events = session.query(Events).filter(Events.event_type.like("EVENT_TEST%")) + event_datas = session.query(EventData) + assert events.count() == MAX_ROWS_TO_PURGE * 6 + assert event_datas.count() == 5 + + purge_before = dt_util.utcnow() - timedelta(days=4) + + # run purge_old_data() + finished = purge_old_data( + instance, + purge_before, + repack=False, + states_batch_size=3, + events_batch_size=3, + ) + assert not finished + assert events.count() == MAX_ROWS_TO_PURGE * 3 + assert event_datas.count() == 5 + + # we should only have 2 groups of events left + finished = purge_old_data( + instance, + purge_before, + repack=False, + states_batch_size=3, + events_batch_size=3, + ) + assert finished + assert events.count() == MAX_ROWS_TO_PURGE * 2 + assert event_datas.count() == 5 + + # we should now purge everything + finished = purge_old_data( + instance, + dt_util.utcnow(), + repack=False, + states_batch_size=20, + events_batch_size=20, + ) + assert finished + assert events.count() == 0 + assert event_datas.count() == 0 + + +async def test_purge_can_mix_legacy_and_new_format( + hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT +): + """Test purging with legacy a new events.""" + instance = await async_setup_recorder_instance(hass) + utcnow = dt_util.utcnow() + eleven_days_ago = utcnow - timedelta(days=11) + with session_scope(hass=hass) as session: + broken_state_no_time = States( + event_id=None, + entity_id="orphened.state", + last_updated=None, + last_changed=None, + ) + session.add(broken_state_no_time) + start_id = 50000 + for event_id in range(start_id, start_id + 50): + _add_state_and_state_changed_event( + session, + "sensor.excluded", + "purgeme", + eleven_days_ago, + event_id, + ) + await _add_test_events(hass, 50) + await _add_events_with_event_data(hass, 50) + with session_scope(hass=hass) as session: + for _ in range(50): + _add_state_without_event_linkage( + session, "switch.random", "on", eleven_days_ago + ) + states_with_event_id = session.query(States).filter( + States.event_id.is_not(None) + ) + states_without_event_id = session.query(States).filter( + States.event_id.is_(None) + ) + + assert states_with_event_id.count() == 50 + assert states_without_event_id.count() == 51 + + purge_before = dt_util.utcnow() - timedelta(days=4) + finished = purge_old_data( + instance, + purge_before, + repack=False, + ) + assert not finished + assert states_with_event_id.count() == 0 + assert states_without_event_id.count() == 51 + # At this point all the legacy states are gone + # and we switch methods + purge_before = dt_util.utcnow() - timedelta(days=4) + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=1, + states_batch_size=1, + ) + # Since we only allow one iteration, we won't + # check if we are finished this loop similar + # to the legacy method + assert not finished + assert states_with_event_id.count() == 0 + assert states_without_event_id.count() == 1 + finished = purge_old_data( + instance, + purge_before, + repack=False, + events_batch_size=100, + states_batch_size=100, + ) + assert finished + assert states_with_event_id.count() == 0 + assert states_without_event_id.count() == 1 + _add_state_without_event_linkage( + session, "switch.random", "on", eleven_days_ago + ) + assert states_with_event_id.count() == 0 + assert states_without_event_id.count() == 2 + finished = purge_old_data( + instance, + purge_before, + repack=False, + ) + assert finished + # The broken state without a timestamp + # does not prevent future purges. Its ignored. + assert states_with_event_id.count() == 0 + assert states_without_event_id.count() == 1 From 2613c865f7720ede45ab287d4a9aff8a94517439 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 00:31:14 -0700 Subject: [PATCH 480/930] Add Spotify application_credentials platform (#71871) --- homeassistant/components/spotify/__init__.py | 45 ++++++++++++------- .../spotify/application_credentials.py | 12 +++++ .../components/spotify/manifest.json | 2 +- .../generated/application_credentials.py | 1 + 4 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/spotify/application_credentials.py diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py index c057ea240c0..bf4e9d8deae 100644 --- a/homeassistant/components/spotify/__init__.py +++ b/homeassistant/components/spotify/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from datetime import timedelta +import logging from typing import Any import aiohttp @@ -10,6 +11,10 @@ import requests from spotipy import Spotify, SpotifyException import voluptuous as vol +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CREDENTIALS, @@ -19,7 +24,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.config_entry_oauth2_flow import ( OAuth2Session, async_get_config_entry_implementation, @@ -27,7 +32,6 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from . import config_flow from .browse_media import async_browse_media from .const import DOMAIN, LOGGER, SPOTIFY_SCOPES from .util import ( @@ -36,15 +40,20 @@ from .util import ( spotify_uri_from_media_browser_url, ) +_LOGGER = logging.getLogger(__name__) + CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Inclusive(CONF_CLIENT_ID, ATTR_CREDENTIALS): cv.string, - vol.Inclusive(CONF_CLIENT_SECRET, ATTR_CREDENTIALS): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Inclusive(CONF_CLIENT_ID, ATTR_CREDENTIALS): cv.string, + vol.Inclusive(CONF_CLIENT_SECRET, ATTR_CREDENTIALS): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -76,17 +85,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True if CONF_CLIENT_ID in config[DOMAIN]: - config_flow.SpotifyFlowHandler.async_register_implementation( + await async_import_client_credential( hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, + DOMAIN, + ClientCredential( config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], - "https://accounts.spotify.com/authorize", - "https://accounts.spotify.com/api/token", ), ) + _LOGGER.warning( + "Configuration of Spotify integration in YAML is deprecated and " + "will be removed in a future release; Your existing OAuth " + "Application Credentials have been imported into the UI " + "automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/spotify/application_credentials.py b/homeassistant/components/spotify/application_credentials.py new file mode 100644 index 00000000000..203028ba7d2 --- /dev/null +++ b/homeassistant/components/spotify/application_credentials.py @@ -0,0 +1,12 @@ +"""Application credentials platform for spotify.""" + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url="https://accounts.spotify.com/authorize", + token_url="https://accounts.spotify.com/api/token", + ) diff --git a/homeassistant/components/spotify/manifest.json b/homeassistant/components/spotify/manifest.json index dd18a05bdc7..979f262e54a 100644 --- a/homeassistant/components/spotify/manifest.json +++ b/homeassistant/components/spotify/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/spotify", "requirements": ["spotipy==2.19.0"], "zeroconf": ["_spotify-connect._tcp.local."], - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "codeowners": ["@frenck"], "config_flow": true, "quality_scale": "silver", diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 3bde9e4681f..3e283dcfeee 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -7,5 +7,6 @@ To update, run python3 -m script.hassfest APPLICATION_CREDENTIALS = [ "google", + "spotify", "xbox" ] From a32321aa9f335b9f1e880d5312882f6e55895f02 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 00:35:29 -0700 Subject: [PATCH 481/930] Mark xbox configuration.yaml as deprecated after app creds import (#71908) --- homeassistant/components/xbox/__init__.py | 26 ++++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 2b5772dd0ba..19bbec8bbf5 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -38,14 +38,17 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -71,6 +74,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET] ), ) + _LOGGER.warning( + "Configuration of Xbox integration in YAML is deprecated and " + "will be removed in a future release; Your existing configuration " + "(including OAuth Application Credentials) has been imported into " + "the UI automatically and can be safely removed from your " + "configuration.yaml file" + ) return True From aa35b878844ba4077f598c4b083c72be4b3babc2 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 00:37:36 -0700 Subject: [PATCH 482/930] Improve error handling for application credentials deletion (#71868) --- homeassistant/components/application_credentials/__init__.py | 5 ++++- tests/components/application_credentials/test_init.py | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index 0dda775774f..9117a91c33d 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -17,6 +17,7 @@ from homeassistant.components import websocket_api from homeassistant.components.websocket_api.connection import ActiveConnection from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN, CONF_ID from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, config_entry_oauth2_flow import homeassistant.helpers.config_validation as cv from homeassistant.helpers.storage import Store @@ -98,7 +99,9 @@ class ApplicationCredentialsStorageCollection(collection.StorageCollection): entries = self.hass.config_entries.async_entries(current[CONF_DOMAIN]) for entry in entries: if entry.data.get("auth_implementation") == item_id: - raise ValueError("Cannot delete credential in use by an integration") + raise HomeAssistantError( + f"Cannot delete credential in use by integration {entry.domain}" + ) await super().async_delete_item(item_id) diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index b5f51a9b837..b62f8a0139c 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -445,6 +445,10 @@ async def test_config_flow( assert not resp.get("success") assert "error" in resp assert resp["error"].get("code") == "unknown_error" + assert ( + resp["error"].get("message") + == "Cannot delete credential in use by integration fake_integration" + ) async def test_config_flow_multiple_entries( From 7c68278482aa79817012af78f6cd49fd7fc51ffd Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 00:57:25 -0700 Subject: [PATCH 483/930] Add application_credentials platform to geocaching integration (#71880) --- .../components/geocaching/__init__.py | 46 +------------------ .../geocaching/application_credentials.py | 14 ++++++ .../components/geocaching/manifest.json | 2 +- homeassistant/components/geocaching/oauth.py | 34 +++++++------- .../generated/application_credentials.py | 1 + .../components/geocaching/test_config_flow.py | 41 ++++++----------- 6 files changed, 48 insertions(+), 90 deletions(-) create mode 100644 homeassistant/components/geocaching/application_credentials.py diff --git a/homeassistant/components/geocaching/__init__.py b/homeassistant/components/geocaching/__init__.py index f04fbbd608f..430cbc9a8d0 100644 --- a/homeassistant/components/geocaching/__init__.py +++ b/homeassistant/components/geocaching/__init__.py @@ -1,61 +1,19 @@ """The Geocaching integration.""" -import voluptuous as vol -from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, ConfigEntry -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv from homeassistant.helpers.config_entry_oauth2_flow import ( OAuth2Session, async_get_config_entry_implementation, ) -from homeassistant.helpers.typing import ConfigType -from .config_flow import GeocachingFlowHandler from .const import DOMAIN from .coordinator import GeocachingDataUpdateCoordinator -from .oauth import GeocachingOAuth2Implementation - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, - extra=vol.ALLOW_EXTRA, -) PLATFORMS = [Platform.SENSOR] -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Geocaching component.""" - if DOMAIN not in config: - return True - - GeocachingFlowHandler.async_register_implementation( - hass, - GeocachingOAuth2Implementation( - hass, - client_id=config[DOMAIN][CONF_CLIENT_ID], - client_secret=config[DOMAIN][CONF_CLIENT_SECRET], - name="Geocaching", - ), - ) - - # When manual configuration is done, discover the integration. - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_INTEGRATION_DISCOVERY} - ) - ) - - return True - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Geocaching from a config entry.""" implementation = await async_get_config_entry_implementation(hass, entry) diff --git a/homeassistant/components/geocaching/application_credentials.py b/homeassistant/components/geocaching/application_credentials.py new file mode 100644 index 00000000000..e3d35f57a81 --- /dev/null +++ b/homeassistant/components/geocaching/application_credentials.py @@ -0,0 +1,14 @@ +"""application_credentials platform for Geocaching.""" + +from homeassistant.components.application_credentials import ClientCredential +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .oauth import GeocachingOAuth2Implementation + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return auth implementation.""" + return GeocachingOAuth2Implementation(hass, auth_domain, credential) diff --git a/homeassistant/components/geocaching/manifest.json b/homeassistant/components/geocaching/manifest.json index 683a23d474a..59c3da45cf5 100644 --- a/homeassistant/components/geocaching/manifest.json +++ b/homeassistant/components/geocaching/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/geocaching", "requirements": ["geocachingapi==0.2.1"], - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "codeowners": ["@Sholofly", "@reinder83"], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/geocaching/oauth.py b/homeassistant/components/geocaching/oauth.py index 29371eb793c..e0120344cdb 100644 --- a/homeassistant/components/geocaching/oauth.py +++ b/homeassistant/components/geocaching/oauth.py @@ -3,37 +3,37 @@ from __future__ import annotations from typing import Any, cast +from homeassistant.components.application_credentials import ( + AuthImplementation, + AuthorizationServer, + ClientCredential, +) from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession -from .const import DOMAIN, ENVIRONMENT, ENVIRONMENT_URLS +from .const import ENVIRONMENT, ENVIRONMENT_URLS -class GeocachingOAuth2Implementation( - config_entry_oauth2_flow.LocalOAuth2Implementation -): +class GeocachingOAuth2Implementation(AuthImplementation): """Local OAuth2 implementation for Geocaching.""" def __init__( - self, hass: HomeAssistant, client_id: str, client_secret: str, name: str + self, + hass: HomeAssistant, + auth_domain: str, + credential: ClientCredential, ) -> None: """Local Geocaching Oauth Implementation.""" - self._name = name super().__init__( hass=hass, - client_id=client_id, - client_secret=client_secret, - domain=DOMAIN, - authorize_url=ENVIRONMENT_URLS[ENVIRONMENT]["authorize_url"], - token_url=ENVIRONMENT_URLS[ENVIRONMENT]["token_url"], + auth_domain=auth_domain, + credential=credential, + authorization_server=AuthorizationServer( + authorize_url=ENVIRONMENT_URLS[ENVIRONMENT]["authorize_url"], + token_url=ENVIRONMENT_URLS[ENVIRONMENT]["token_url"], + ), ) - @property - def name(self) -> str: - """Name of the implementation.""" - return f"{self._name}" - @property def extra_authorize_data(self) -> dict: """Extra data that needs to be appended to the authorize url.""" diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 3e283dcfeee..56340ccc44e 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -6,6 +6,7 @@ To update, run python3 -m script.hassfest # fmt: off APPLICATION_CREDENTIALS = [ + "geocaching", "google", "spotify", "xbox" diff --git a/tests/components/geocaching/test_config_flow.py b/tests/components/geocaching/test_config_flow.py index 0f5d182b2db..56a301e2f3c 100644 --- a/tests/components/geocaching/test_config_flow.py +++ b/tests/components/geocaching/test_config_flow.py @@ -4,19 +4,18 @@ from http import HTTPStatus from unittest.mock import MagicMock from aiohttp.test_utils import TestClient +import pytest +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.geocaching.const import ( DOMAIN, ENVIRONMENT, ENVIRONMENT_URLS, ) -from homeassistant.config_entries import ( - DEFAULT_DISCOVERY_UNIQUE_ID, - SOURCE_INTEGRATION_DISCOVERY, - SOURCE_REAUTH, - SOURCE_USER, -) -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_EXTERNAL_STEP from homeassistant.helpers import config_entry_oauth2_flow @@ -30,17 +29,14 @@ from tests.test_util.aiohttp import AiohttpClientMocker CURRENT_ENVIRONMENT_URLS = ENVIRONMENT_URLS[ENVIRONMENT] -async def setup_geocaching_component(hass: HomeAssistant) -> bool: - """Set up the Geocaching component.""" - return await async_setup_component( +@pytest.fixture(autouse=True) +async def setup_credentials(hass: HomeAssistant) -> None: + """Fixture to setup credentials.""" + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( hass, DOMAIN, - { - DOMAIN: { - CONF_CLIENT_ID: CLIENT_ID, - CONF_CLIENT_SECRET: CLIENT_SECRET, - }, - }, + ClientCredential(CLIENT_ID, CLIENT_SECRET), ) @@ -53,15 +49,6 @@ async def test_full_flow( mock_setup_entry: MagicMock, ) -> None: """Check full flow.""" - assert await setup_geocaching_component(hass) - - # Ensure integration is discovered when manual implementation is configured - flows = hass.config_entries.flow.async_progress() - assert len(flows) == 1 - assert "context" in flows[0] - assert flows[0]["context"]["source"] == SOURCE_INTEGRATION_DISCOVERY - assert flows[0]["context"]["unique_id"] == DEFAULT_DISCOVERY_UNIQUE_ID - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) @@ -113,9 +100,9 @@ async def test_existing_entry( mock_geocaching_config_flow: MagicMock, mock_setup_entry: MagicMock, mock_config_entry: MockConfigEntry, + setup_credentials: None, ) -> None: """Check existing entry.""" - assert await setup_geocaching_component(hass) mock_config_entry.add_to_hass(hass) assert len(hass.config_entries.async_entries(DOMAIN)) == 1 @@ -161,7 +148,6 @@ async def test_oauth_error( mock_setup_entry: MagicMock, ) -> None: """Check if aborted when oauth error occurs.""" - assert await setup_geocaching_component(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER} ) @@ -213,7 +199,6 @@ async def test_reauthentication( ) -> None: """Test Geocaching reauthentication.""" mock_config_entry.add_to_hass(hass) - assert await setup_geocaching_component(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_REAUTH} From 77d39f9c4f4b10bfab25eb850b5e26542d8cbcdc Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 10:07:39 +0200 Subject: [PATCH 484/930] Add missing title translation for the Siren domain (#71924) --- homeassistant/components/siren/strings.json | 3 +++ homeassistant/components/siren/translations/en.json | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 homeassistant/components/siren/strings.json create mode 100644 homeassistant/components/siren/translations/en.json diff --git a/homeassistant/components/siren/strings.json b/homeassistant/components/siren/strings.json new file mode 100644 index 00000000000..c8e60e91ce0 --- /dev/null +++ b/homeassistant/components/siren/strings.json @@ -0,0 +1,3 @@ +{ + "title": "Siren" +} diff --git a/homeassistant/components/siren/translations/en.json b/homeassistant/components/siren/translations/en.json new file mode 100644 index 00000000000..549bad8914b --- /dev/null +++ b/homeassistant/components/siren/translations/en.json @@ -0,0 +1,3 @@ +{ + "title": "Siren" +} \ No newline at end of file From e30a9d1fc015c6de92e71821d8ad36c1e3dcbcd5 Mon Sep 17 00:00:00 2001 From: Ethan Madden Date: Mon, 16 May 2022 01:30:49 -0700 Subject: [PATCH 485/930] Fix VeSync air_quality fan attribute (#71771) * Refactor attribute inclusion for VeSync fans. A recent change to pyvesync (introduced in 2.2) changed `air_quality` to refer to air quality as an integer representation of perceived air quality rather than a direct reading of the PM2.5 sensor. With 2.3 the PM2.5 sensor access was restored as `air_quality_value`. Unfortunately, `air_quality_value` was not added as an attribute on the fan object, and rather only exists in the `details` dictionary on the fan object. * Update homeassistant/components/vesync/fan.py Co-authored-by: Martin Hjelmare * Rename `air_quality_value` attribute to `pm25` This should make it more clear what the attribute actually represents * `air_quality` attribute reports `air_quality_value` This restores previous behavior for this integration to what it was before the `pyvesync==2.02` upgrade, using the `air_quality` attribute to report pm2.5 concentrations (formerly `air_quality`) rather the vague measurement now reported by `air_quality`. Co-authored-by: Martin Hjelmare --- homeassistant/components/vesync/fan.py | 4 ++-- homeassistant/components/vesync/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index e37a6c8893e..f16a785ee1e 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -171,8 +171,8 @@ class VeSyncFanHA(VeSyncDevice, FanEntity): if hasattr(self.smartfan, "night_light"): attr["night_light"] = self.smartfan.night_light - if hasattr(self.smartfan, "air_quality"): - attr["air_quality"] = self.smartfan.air_quality + if self.smartfan.details.get("air_quality_value") is not None: + attr["air_quality"] = self.smartfan.details["air_quality_value"] if hasattr(self.smartfan, "mode"): attr["mode"] = self.smartfan.mode diff --git a/homeassistant/components/vesync/manifest.json b/homeassistant/components/vesync/manifest.json index c93e070a484..49be473b748 100644 --- a/homeassistant/components/vesync/manifest.json +++ b/homeassistant/components/vesync/manifest.json @@ -3,7 +3,7 @@ "name": "VeSync", "documentation": "https://www.home-assistant.io/integrations/vesync", "codeowners": ["@markperdue", "@webdjoe", "@thegardenmonkey"], - "requirements": ["pyvesync==2.0.2"], + "requirements": ["pyvesync==2.0.3"], "config_flow": true, "iot_class": "cloud_polling", "loggers": ["pyvesync"] diff --git a/requirements_all.txt b/requirements_all.txt index 5b35a595083..b0281677308 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1999,7 +1999,7 @@ pyvera==0.3.13 pyversasense==0.0.6 # homeassistant.components.vesync -pyvesync==2.0.2 +pyvesync==2.0.3 # homeassistant.components.vizio pyvizio==0.1.57 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 888cd30a056..c56bf1aeb98 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1322,7 +1322,7 @@ pyuptimerobot==22.2.0 pyvera==0.3.13 # homeassistant.components.vesync -pyvesync==2.0.2 +pyvesync==2.0.3 # homeassistant.components.vizio pyvizio==0.1.57 From f7a2a6ea216f4beeca4e03064217609794503419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 16 May 2022 12:26:54 +0200 Subject: [PATCH 486/930] Bump awesomeversion from 22.2.0 to 22.5.1 (#71933) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index ec3ac5de9b3..d2f88767d0c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -8,7 +8,7 @@ async-upnp-client==0.29.0 async_timeout==4.0.2 atomicwrites==1.4.0 attrs==21.2.0 -awesomeversion==22.2.0 +awesomeversion==22.5.1 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements.txt b/requirements.txt index 2c96c00fdb3..0cd6c1fc6ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ astral==2.2 async_timeout==4.0.2 attrs==21.2.0 atomicwrites==1.4.0 -awesomeversion==22.2.0 +awesomeversion==22.5.1 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/setup.cfg b/setup.cfg index 47bf617755a..e14915c4998 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,7 @@ install_requires = async_timeout==4.0.2 attrs==21.2.0 atomicwrites==1.4.0 - awesomeversion==22.2.0 + awesomeversion==22.5.1 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 From 0ee838b25e850204fc4df971c2520f8e725b96b4 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Mon, 16 May 2022 12:50:03 +0200 Subject: [PATCH 487/930] Properly handle Shelly gen2 device disconnect (#71937) --- homeassistant/components/shelly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index d29584c4e83..41a9e68fbdd 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -812,7 +812,7 @@ class RpcPollingWrapper(update_coordinator.DataUpdateCoordinator): LOGGER.debug("Polling Shelly RPC Device - %s", self.name) async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): await self.device.update_status() - except OSError as err: + except (OSError, aioshelly.exceptions.RPCTimeout) as err: raise update_coordinator.UpdateFailed("Device disconnected") from err @property From 732082b7700cf52f04c6841d3ac058c3c6a88688 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 12:54:24 +0200 Subject: [PATCH 488/930] Update apprise to 0.9.8.3 (#71934) --- homeassistant/components/apprise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/apprise/manifest.json b/homeassistant/components/apprise/manifest.json index 21e2ed7c94d..b4422a49ef4 100644 --- a/homeassistant/components/apprise/manifest.json +++ b/homeassistant/components/apprise/manifest.json @@ -2,7 +2,7 @@ "domain": "apprise", "name": "Apprise", "documentation": "https://www.home-assistant.io/integrations/apprise", - "requirements": ["apprise==0.9.7"], + "requirements": ["apprise==0.9.8.3"], "codeowners": ["@caronc"], "iot_class": "cloud_push", "loggers": ["apprise"] diff --git a/requirements_all.txt b/requirements_all.txt index b0281677308..1fef7fae54b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -313,7 +313,7 @@ anthemav==1.2.0 apcaccess==0.0.13 # homeassistant.components.apprise -apprise==0.9.7 +apprise==0.9.8.3 # homeassistant.components.aprs aprslib==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c56bf1aeb98..a20ea91d92e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -267,7 +267,7 @@ ambiclimate==0.2.1 androidtv[async]==0.0.67 # homeassistant.components.apprise -apprise==0.9.7 +apprise==0.9.8.3 # homeassistant.components.aprs aprslib==0.7.0 From fcd9fcffe6c96b31990f143c574531d02d34058e Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 12:55:21 +0200 Subject: [PATCH 489/930] Update watchdog to 2.1.8 (#71927) --- homeassistant/components/folder_watcher/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/folder_watcher/manifest.json b/homeassistant/components/folder_watcher/manifest.json index 918492c82e9..f7562633ba0 100644 --- a/homeassistant/components/folder_watcher/manifest.json +++ b/homeassistant/components/folder_watcher/manifest.json @@ -2,7 +2,7 @@ "domain": "folder_watcher", "name": "Folder Watcher", "documentation": "https://www.home-assistant.io/integrations/folder_watcher", - "requirements": ["watchdog==2.1.7"], + "requirements": ["watchdog==2.1.8"], "codeowners": [], "quality_scale": "internal", "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 1fef7fae54b..fa529f22320 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2418,7 +2418,7 @@ wallbox==0.4.4 waqiasync==1.0.0 # homeassistant.components.folder_watcher -watchdog==2.1.7 +watchdog==2.1.8 # homeassistant.components.waterfurnace waterfurnace==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a20ea91d92e..c8b37a3c73d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1588,7 +1588,7 @@ wakeonlan==2.0.1 wallbox==0.4.4 # homeassistant.components.folder_watcher -watchdog==2.1.7 +watchdog==2.1.8 # homeassistant.components.whirlpool whirlpool-sixth-sense==0.15.1 From ace2f18697b7254f81dfcc1891989a367e1ba756 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 13:58:54 +0200 Subject: [PATCH 490/930] Update pyupgrade to v2.32.1 (#71939) --- .pre-commit-config.yaml | 2 +- requirements_test_pre_commit.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a012441a6b..539308c08f1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v2.32.0 + rev: v2.32.1 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt index bed60c11c53..047dcbf90ad 100644 --- a/requirements_test_pre_commit.txt +++ b/requirements_test_pre_commit.txt @@ -12,5 +12,5 @@ mccabe==0.6.1 pycodestyle==2.8.0 pydocstyle==6.1.1 pyflakes==2.4.0 -pyupgrade==2.32.0 +pyupgrade==2.32.1 yamllint==1.26.3 From a90289905803b4d88d38651fac4961c6605ba995 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 14:44:07 +0200 Subject: [PATCH 491/930] Remove auto_start translation from HomeKit (#71938) --- homeassistant/components/homekit/strings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/homekit/strings.json b/homeassistant/components/homekit/strings.json index d2b6951a698..2c0fad0290e 100644 --- a/homeassistant/components/homekit/strings.json +++ b/homeassistant/components/homekit/strings.json @@ -44,8 +44,7 @@ }, "advanced": { "data": { - "devices": "Devices (Triggers)", - "auto_start": "Autostart (disable if you are calling the homekit.start service manually)" + "devices": "Devices (Triggers)" }, "description": "Programmable switches are created for each selected device. When a device trigger fires, HomeKit can be configured to run an automation or scene.", "title": "Advanced Configuration" From 2d7723169ae463d7cfe9052a0a6f05de857ca56e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Mon, 16 May 2022 15:01:31 +0200 Subject: [PATCH 492/930] Update pylint to 2.13.9 (#71941) * Update pylint to 2.13.9 * Small change --- homeassistant/components/sms/config_flow.py | 2 +- requirements_test.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sms/config_flow.py b/homeassistant/components/sms/config_flow.py index acc33075397..9128b6187c1 100644 --- a/homeassistant/components/sms/config_flow.py +++ b/homeassistant/components/sms/config_flow.py @@ -39,7 +39,7 @@ async def get_imei_from_config(hass: core.HomeAssistant, data): raise CannotConnect try: imei = await gateway.get_imei_async() - except gammu.GSMError as err: # pylint: disable=no-member + except gammu.GSMError as err: raise CannotConnect from err finally: await gateway.terminate_async() diff --git a/requirements_test.txt b/requirements_test.txt index 49615d104a7..f0cdac24e5f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -13,7 +13,7 @@ freezegun==1.2.1 mock-open==1.4.0 mypy==0.950 pre-commit==2.19.0 -pylint==2.13.8 +pylint==2.13.9 pipdeptree==2.2.1 pylint-strict-informational==0.1 pytest-aiohttp==0.3.0 From e74794d50020b8c54c4a791755ef4f21bdb9bdb4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 09:10:18 -0500 Subject: [PATCH 493/930] Add sensor platform to Big Ass Fans (#71877) --- .coveragerc | 1 + homeassistant/components/baf/__init__.py | 2 +- homeassistant/components/baf/sensor.py | 132 +++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/baf/sensor.py diff --git a/.coveragerc b/.coveragerc index 15b9544dad5..12aa3972f1a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -96,6 +96,7 @@ omit = homeassistant/components/baf/__init__.py homeassistant/components/baf/entity.py homeassistant/components/baf/fan.py + homeassistant/components/baf/sensor.py homeassistant/components/baidu/tts.py homeassistant/components/balboa/__init__.py homeassistant/components/beewi_smartclim/sensor.py diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py index 4327eb9ddb9..f6401cbbc40 100644 --- a/homeassistant/components/baf/__init__.py +++ b/homeassistant/components/baf/__init__.py @@ -14,7 +14,7 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import DOMAIN, QUERY_INTERVAL, RUN_TIMEOUT from .models import BAFData -PLATFORMS: list[Platform] = [Platform.FAN] +PLATFORMS: list[Platform] = [Platform.FAN, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/baf/sensor.py b/homeassistant/components/baf/sensor.py new file mode 100644 index 00000000000..0f4239962cf --- /dev/null +++ b/homeassistant/components/baf/sensor.py @@ -0,0 +1,132 @@ +"""Support for Big Ass Fans sensors.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Optional, cast + +from aiobafi6 import Device + +from homeassistant import config_entries +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.const import PERCENTAGE, TEMP_CELSIUS +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import BAFEntity +from .models import BAFData + + +@dataclass +class BAFSensorDescriptionMixin: + """Required values for BAF sensors.""" + + value_fn: Callable[[Device], int | float | str | None] + + +@dataclass +class BAFSensorDescription( + SensorEntityDescription, + BAFSensorDescriptionMixin, +): + """Class describing BAF sensor entities.""" + + +BASE_SENSORS = ( + BAFSensorDescription( + key="temperature", + name="Temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda device: cast(Optional[float], device.temperature), + ), +) + +DEFINED_ONLY_SENSORS = ( + BAFSensorDescription( + key="humidity", + name="Humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + value_fn=lambda device: cast(Optional[float], device.humidity), + ), +) + +FAN_SENSORS = ( + BAFSensorDescription( + key="current_rpm", + name="Current RPM", + native_unit_of_measurement="RPM", + entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda device: cast(Optional[int], device.current_rpm), + ), + BAFSensorDescription( + key="target_rpm", + name="Target RPM", + native_unit_of_measurement="RPM", + entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda device: cast(Optional[int], device.target_rpm), + ), + BAFSensorDescription( + key="wifi_ssid", + name="WiFi SSID", + entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda device: cast(Optional[int], device.wifi_ssid), + ), + BAFSensorDescription( + key="ip_address", + name="IP Address", + entity_registry_enabled_default=False, + entity_category=EntityCategory.DIAGNOSTIC, + value_fn=lambda device: cast(Optional[str], device.ip_address), + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up BAF fan sensors.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + device = data.device + sensors_descriptions = list(BASE_SENSORS) + for description in DEFINED_ONLY_SENSORS: + if getattr(device, description.key): + sensors_descriptions.append(description) + if device.has_fan: + sensors_descriptions.extend(FAN_SENSORS) + async_add_entities( + BAFSensor(device, description) for description in sensors_descriptions + ) + + +class BAFSensor(BAFEntity, SensorEntity): + """BAF sensor.""" + + entity_description: BAFSensorDescription + + def __init__(self, device: Device, description: BAFSensorDescription) -> None: + """Initialize the entity.""" + self.entity_description = description + super().__init__(device, f"{device.name} {description.name}") + self._attr_unique_id = f"{self._device.mac_address}-{description.key}" + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + description = self.entity_description + self._attr_native_value = description.value_fn(self._device) From 514e7708b3e9d7bcacec8dc2f279f161854d5e8f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 16:38:01 +0200 Subject: [PATCH 494/930] Update PyJWT to 2.4.0 (#71928) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index d2f88767d0c..b9d08a07591 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -1,4 +1,4 @@ -PyJWT==2.3.0 +PyJWT==2.4.0 PyNaCl==1.5.0 aiodiscover==1.4.11 aiohttp==3.8.1 diff --git a/requirements.txt b/requirements.txt index 0cd6c1fc6ba..f2308908a30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ ciso8601==2.2.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 -PyJWT==2.3.0 +PyJWT==2.4.0 cryptography==36.0.2 pip>=21.0,<22.1 python-slugify==4.0.1 diff --git a/setup.cfg b/setup.cfg index e14915c4998..9d4fad881b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,7 +45,7 @@ install_requires = httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 - PyJWT==2.3.0 + PyJWT==2.4.0 # PyJWT has loose dependency. We want the latest one. cryptography==36.0.2 pip>=21.0,<22.1 From 5d32659d17f4a42f995a67ab647dfe1838f3ddca Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 08:11:09 -0700 Subject: [PATCH 495/930] Update scaffold script to use application_credentials platform (#71881) --- script/scaffold/generate.py | 2 +- .../integration/__init__.py | 49 ++----------------- .../integration/application_credentials.py | 16 ++++++ .../tests/test_config_flow.py | 31 ++++++++---- 4 files changed, 42 insertions(+), 56 deletions(-) create mode 100644 script/scaffold/templates/config_flow_oauth2/integration/application_credentials.py diff --git a/script/scaffold/generate.py b/script/scaffold/generate.py index a86c4e3a015..7f418868463 100644 --- a/script/scaffold/generate.py +++ b/script/scaffold/generate.py @@ -173,7 +173,7 @@ def _custom_tasks(template, info: Info) -> None: ) elif template == "config_flow_oauth2": - info.update_manifest(config_flow=True, dependencies=["auth"]) + info.update_manifest(config_flow=True, dependencies=["application_credentials"]) info.update_strings( config={ "step": { diff --git a/script/scaffold/templates/config_flow_oauth2/integration/__init__.py b/script/scaffold/templates/config_flow_oauth2/integration/__init__.py index b580e609bba..24dcee48ccd 100644 --- a/script/scaffold/templates/config_flow_oauth2/integration/__init__.py +++ b/script/scaffold/templates/config_flow_oauth2/integration/__init__.py @@ -1,60 +1,19 @@ """The NEW_NAME integration.""" from __future__ import annotations -import voluptuous as vol - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform +from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import ( - aiohttp_client, - config_entry_oauth2_flow, - config_validation as cv, -) -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow -from . import api, config_flow -from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, - extra=vol.ALLOW_EXTRA, -) +from . import api +from .const import DOMAIN # TODO List the platforms that you want to support. # For your initial PR, limit it to 1 platform. PLATFORMS: list[Platform] = [Platform.LIGHT] -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the NEW_NAME component.""" - hass.data[DOMAIN] = {} - - if DOMAIN not in config: - return True - - config_flow.OAuth2FlowHandler.async_register_implementation( - hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, - config[DOMAIN][CONF_CLIENT_ID], - config[DOMAIN][CONF_CLIENT_SECRET], - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, - ), - ) - - return True - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up NEW_NAME from a config entry.""" implementation = ( diff --git a/script/scaffold/templates/config_flow_oauth2/integration/application_credentials.py b/script/scaffold/templates/config_flow_oauth2/integration/application_credentials.py new file mode 100644 index 00000000000..51ef70b1885 --- /dev/null +++ b/script/scaffold/templates/config_flow_oauth2/integration/application_credentials.py @@ -0,0 +1,16 @@ +"""application_credentials platform the NEW_NAME integration.""" + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + +# TODO Update with your own urls +OAUTH2_AUTHORIZE = "https://www.example.com/auth/authorize" +OAUTH2_TOKEN = "https://www.example.com/auth/token" + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ) diff --git a/script/scaffold/templates/config_flow_oauth2/tests/test_config_flow.py b/script/scaffold/templates/config_flow_oauth2/tests/test_config_flow.py index 3ed8d9d293f..bc087119c6e 100644 --- a/script/scaffold/templates/config_flow_oauth2/tests/test_config_flow.py +++ b/script/scaffold/templates/config_flow_oauth2/tests/test_config_flow.py @@ -1,35 +1,46 @@ """Test the NEW_NAME config flow.""" + from unittest.mock import patch -from homeassistant import config_entries, setup +import pytest + +from homeassistant import config_entries from homeassistant.components.NEW_DOMAIN.const import ( DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN, ) +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.setup import async_setup_component CLIENT_ID = "1234" CLIENT_SECRET = "5678" +@pytest.fixture +async def setup_credentials(hass: HomeAssistant) -> None: + """Fixture to setup credentials.""" + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( + hass, + DOMAIN, + ClientCredential(CLIENT_ID, CLIENT_SECRET), + ) + + async def test_full_flow( hass: HomeAssistant, hass_client_no_auth, aioclient_mock, current_request_with_host, + setup_credentials, ) -> None: """Check full flow.""" - assert await setup.async_setup_component( - hass, - "NEW_DOMAIN", - { - "NEW_DOMAIN": {"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, - "http": {"base_url": "https://example.com"}, - }, - ) - result = await hass.config_entries.flow.async_init( "NEW_DOMAIN", context={"source": config_entries.SOURCE_USER} ) From 32b3ce5727d2a63b167652330198d1b561d6b673 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 17 May 2022 00:06:54 +0800 Subject: [PATCH 496/930] Clean up use_wallclock_as_timestamps in generic (#71940) --- homeassistant/components/camera/__init__.py | 2 +- .../components/generic/config_flow.py | 20 ++++++++++++------- homeassistant/components/stream/__init__.py | 5 +++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index bff0a07a9be..31dede33e7a 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -454,7 +454,7 @@ class Camera(Entity): def __init__(self) -> None: """Initialize a camera.""" self.stream: Stream | None = None - self.stream_options: dict[str, str] = {} + self.stream_options: dict[str, str | bool] = {} self.content_type: str = DEFAULT_CONTENT_TYPE self.access_tokens: collections.deque = collections.deque([], 2) self._warned_old_signature = False diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 435fdf6f729..651ed87da39 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -21,6 +21,7 @@ from homeassistant.components.stream import ( CONF_USE_WALLCLOCK_AS_TIMESTAMPS, RTSP_TRANSPORTS, SOURCE_TIMEOUT, + convert_stream_options, ) from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import ( @@ -202,21 +203,23 @@ async def async_test_stream(hass, info) -> dict[str, str]: # homeassistant.components.stream.__init__.py:create_stream() # It may be possible & better to call create_stream() directly. stream_options: dict[str, bool | str] = {} - if isinstance(stream_source, str) and stream_source[:7] == "rtsp://": - stream_options = { - "rtsp_flags": "prefer_tcp", - "stimeout": "5000000", - } if rtsp_transport := info.get(CONF_RTSP_TRANSPORT): stream_options[CONF_RTSP_TRANSPORT] = rtsp_transport if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True + pyav_options = convert_stream_options(stream_options) + if isinstance(stream_source, str) and stream_source[:7] == "rtsp://": + pyav_options = { + "rtsp_flags": "prefer_tcp", + "stimeout": "5000000", + **pyav_options, + } _LOGGER.debug("Attempting to open stream %s", stream_source) container = await hass.async_add_executor_job( partial( av.open, stream_source, - options=stream_options, + options=pyav_options, timeout=SOURCE_TIMEOUT, ) ) @@ -369,7 +372,10 @@ class GenericOptionsFlowHandler(OptionsFlow): CONF_FRAMERATE: user_input[CONF_FRAMERATE], CONF_VERIFY_SSL: user_input[CONF_VERIFY_SSL], CONF_USE_WALLCLOCK_AS_TIMESTAMPS: user_input.get( - CONF_USE_WALLCLOCK_AS_TIMESTAMPS + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + self.config_entry.options.get( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, False + ), ), } return self.async_create_entry( diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 95e9afe6c36..895bdaf3201 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -90,7 +90,7 @@ def redact_credentials(data: str) -> str: def create_stream( hass: HomeAssistant, stream_source: str, - options: dict[str, Any], + options: dict[str, str | bool], stream_label: str | None = None, ) -> Stream: """Create a stream with the specified identfier based on the source url. @@ -496,7 +496,7 @@ STREAM_OPTIONS_SCHEMA: Final = vol.Schema( ) -def convert_stream_options(stream_options: dict[str, Any]) -> dict[str, str]: +def convert_stream_options(stream_options: dict[str, str | bool]) -> dict[str, str]: """Convert options from stream options into PyAV options.""" pyav_options: dict[str, str] = {} try: @@ -505,6 +505,7 @@ def convert_stream_options(stream_options: dict[str, Any]) -> dict[str, str]: raise HomeAssistantError("Invalid stream options") from exc if rtsp_transport := stream_options.get(CONF_RTSP_TRANSPORT): + assert isinstance(rtsp_transport, str) pyav_options["rtsp_transport"] = rtsp_transport if stream_options.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS): pyav_options["use_wallclock_as_timestamps"] = "1" From 57c94c0350a5765bc6f93b396c5e53766f7f12eb Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Mon, 16 May 2022 13:04:32 -0400 Subject: [PATCH 497/930] Add additional configuration entities for ZHA lights (#70597) * Add more configuration entities for ZHA lights * fix typing circular imports * enhance filter in entity factory * fix read attribute chunking * add test * add exception test --- .../components/zha/core/channels/base.py | 2 +- .../components/zha/core/channels/general.py | 8 + homeassistant/components/zha/number.py | 153 +++++++++++++++ tests/components/zha/test_discover.py | 10 +- tests/components/zha/test_number.py | 175 +++++++++++++++++- 5 files changed, 341 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index 7beefe2f0d0..7ba28a52116 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -446,7 +446,7 @@ class ZigbeeChannel(LogMixin): try: self.debug("Reading attributes in chunks: %s", chunk) read, _ = await self.cluster.read_attributes( - attributes, + chunk, allow_cache=from_cache, only_cache=only_cache, manufacturer=manufacturer, diff --git a/homeassistant/components/zha/core/channels/general.py b/homeassistant/components/zha/core/channels/general.py index 2e6093dd4f7..8b67c81db44 100644 --- a/homeassistant/components/zha/core/channels/general.py +++ b/homeassistant/components/zha/core/channels/general.py @@ -222,6 +222,14 @@ class LevelControlChannel(ZigbeeChannel): CURRENT_LEVEL = 0 REPORT_CONFIG = ({"attr": "current_level", "config": REPORT_CONFIG_ASAP},) + ZCL_INIT_ATTRS = { + "on_off_transition_time": True, + "on_level": True, + "on_transition_time": True, + "off_transition_time": True, + "default_move_rate": True, + "start_up_current_level": True, + } @property def current_level(self) -> int | None: diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index e1191b4ece4..22d086891ca 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -1,17 +1,25 @@ """Support for ZHA AnalogOutput cluster.""" +from __future__ import annotations + import functools import logging +from typing import TYPE_CHECKING + +import zigpy.exceptions +from zigpy.zcl.foundation import Status from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .core import discovery from .core.const import ( CHANNEL_ANALOG_OUTPUT, + CHANNEL_LEVEL, DATA_ZHA, SIGNAL_ADD_ENTITIES, SIGNAL_ATTR_UPDATED, @@ -19,9 +27,16 @@ from .core.const import ( from .core.registries import ZHA_ENTITIES from .entity import ZhaEntity +if TYPE_CHECKING: + from .core.channels.base import ZigbeeChannel + from .core.device import ZHADevice + _LOGGER = logging.getLogger(__name__) STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.NUMBER) +CONFIG_DIAGNOSTIC_MATCH = functools.partial( + ZHA_ENTITIES.config_diagnostic_match, Platform.NUMBER +) UNITS = { @@ -342,3 +357,141 @@ class ZhaNumber(ZhaEntity, NumberEntity): "present_value", from_cache=False ) _LOGGER.debug("read value=%s", value) + + +class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity): + """Representation of a ZHA number configuration entity.""" + + _attr_entity_category = EntityCategory.CONFIG + _attr_step: float = 1.0 + _zcl_attribute: str + + @classmethod + def create_entity( + cls, + unique_id: str, + zha_device: ZHADevice, + channels: list[ZigbeeChannel], + **kwargs, + ) -> ZhaEntity | None: + """Entity Factory. + + Return entity if it is a supported configuration, otherwise return None + """ + channel = channels[0] + if ( + cls._zcl_attribute in channel.cluster.unsupported_attributes + or channel.cluster.get(cls._zcl_attribute) is None + ): + _LOGGER.debug( + "%s is not supported - skipping %s entity creation", + cls._zcl_attribute, + cls.__name__, + ) + return None + + return cls(unique_id, zha_device, channels, **kwargs) + + def __init__( + self, + unique_id: str, + zha_device: ZHADevice, + channels: list[ZigbeeChannel], + **kwargs, + ) -> None: + """Init this number configuration entity.""" + self._channel: ZigbeeChannel = channels[0] + super().__init__(unique_id, zha_device, channels, **kwargs) + + @property + def value(self) -> float: + """Return the current value.""" + return self._channel.cluster.get(self._zcl_attribute) + + async def async_set_value(self, value: float) -> None: + """Update the current value from HA.""" + try: + res = await self._channel.cluster.write_attributes( + {self._zcl_attribute: int(value)} + ) + except zigpy.exceptions.ZigbeeException as ex: + self.error("Could not set value: %s", ex) + return + if not isinstance(res, Exception) and all( + record.status == Status.SUCCESS for record in res[0] + ): + self.async_write_ha_state() + + async def async_update(self) -> None: + """Attempt to retrieve the state of the entity.""" + await super().async_update() + _LOGGER.debug("polling current state") + if self._channel: + value = await self._channel.get_attribute_value( + self._zcl_attribute, from_cache=False + ) + _LOGGER.debug("read value=%s", value) + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class OnOffTransitionTimeConfigurationEntity( + ZHANumberConfigurationEntity, id_suffix="on_off_transition_time" +): + """Representation of a ZHA on off transition time configuration entity.""" + + _attr_min_value: float = 0x0000 + _attr_max_value: float = 0xFFFF + _zcl_attribute: str = "on_off_transition_time" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class OnLevelConfigurationEntity(ZHANumberConfigurationEntity, id_suffix="on_level"): + """Representation of a ZHA on level configuration entity.""" + + _attr_min_value: float = 0x00 + _attr_max_value: float = 0xFF + _zcl_attribute: str = "on_level" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class OnTransitionTimeConfigurationEntity( + ZHANumberConfigurationEntity, id_suffix="on_transition_time" +): + """Representation of a ZHA on transition time configuration entity.""" + + _attr_min_value: float = 0x0000 + _attr_max_value: float = 0xFFFE + _zcl_attribute: str = "on_transition_time" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class OffTransitionTimeConfigurationEntity( + ZHANumberConfigurationEntity, id_suffix="off_transition_time" +): + """Representation of a ZHA off transition time configuration entity.""" + + _attr_min_value: float = 0x0000 + _attr_max_value: float = 0xFFFE + _zcl_attribute: str = "off_transition_time" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class DefaultMoveRateConfigurationEntity( + ZHANumberConfigurationEntity, id_suffix="default_move_rate" +): + """Representation of a ZHA default move rate configuration entity.""" + + _attr_min_value: float = 0x00 + _attr_max_value: float = 0xFE + _zcl_attribute: str = "default_move_rate" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) +class StartUpCurrentLevelConfigurationEntity( + ZHANumberConfigurationEntity, id_suffix="start_up_current_level" +): + """Representation of a ZHA startup current level configuration entity.""" + + _attr_min_value: float = 0x00 + _attr_max_value: float = 0xFF + _zcl_attribute: str = "start_up_current_level" diff --git a/tests/components/zha/test_discover.py b/tests/components/zha/test_discover.py index 149c77314a1..4de251fda8b 100644 --- a/tests/components/zha/test_discover.py +++ b/tests/components/zha/test_discover.py @@ -44,7 +44,15 @@ from .zha_devices_list import ( NO_TAIL_ID = re.compile("_\\d$") UNIQUE_ID_HD = re.compile(r"^(([\da-fA-F]{2}:){7}[\da-fA-F]{2}-\d{1,3})", re.X) -IGNORE_SUFFIXES = [zigpy.zcl.clusters.general.OnOff.StartUpOnOff.__name__] +IGNORE_SUFFIXES = [ + zigpy.zcl.clusters.general.OnOff.StartUpOnOff.__name__, + "on_off_transition_time", + "on_level", + "on_transition_time", + "off_transition_time", + "default_move_rate", + "start_up_current_level", +] def contains_ignored_suffix(unique_id: str) -> bool: diff --git a/tests/components/zha/test_number.py b/tests/components/zha/test_number.py index 336800f9ccb..01946c05f1a 100644 --- a/tests/components/zha/test_number.py +++ b/tests/components/zha/test_number.py @@ -2,13 +2,14 @@ from unittest.mock import call, patch import pytest -import zigpy.profiles.zha -import zigpy.types +from zigpy.exceptions import ZigbeeException +from zigpy.profiles import zha import zigpy.zcl.clusters.general as general import zigpy.zcl.foundation as zcl_f from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN -from homeassistant.const import STATE_UNAVAILABLE, Platform +from homeassistant.const import ENTITY_CATEGORY_CONFIG, STATE_UNAVAILABLE, Platform +from homeassistant.helpers import entity_registry as er from homeassistant.setup import async_setup_component from .common import ( @@ -18,7 +19,7 @@ from .common import ( send_attributes_report, update_attribute_cache, ) -from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_TYPE +from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE from tests.common import mock_coro @@ -29,7 +30,7 @@ def zigpy_analog_output_device(zigpy_device_mock): endpoints = { 1: { - SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.LEVEL_CONTROL_SWITCH, + SIG_EP_TYPE: zha.DeviceType.LEVEL_CONTROL_SWITCH, SIG_EP_INPUT: [general.AnalogOutput.cluster_id, general.Basic.cluster_id], SIG_EP_OUTPUT: [], } @@ -37,6 +38,30 @@ def zigpy_analog_output_device(zigpy_device_mock): return zigpy_device_mock(endpoints) +@pytest.fixture +async def light(zigpy_device_mock): + """Siren fixture.""" + + zigpy_device = zigpy_device_mock( + { + 1: { + SIG_EP_PROFILE: zha.PROFILE_ID, + SIG_EP_TYPE: zha.DeviceType.ON_OFF_LIGHT, + SIG_EP_INPUT: [ + general.Basic.cluster_id, + general.Identify.cluster_id, + general.OnOff.cluster_id, + general.LevelControl.cluster_id, + ], + SIG_EP_OUTPUT: [general.Ota.cluster_id], + } + }, + node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00", + ) + + return zigpy_device + + async def test_number(hass, zha_device_joined_restored, zigpy_analog_output_device): """Test zha number platform.""" @@ -139,3 +164,143 @@ async def test_number(hass, zha_device_joined_restored, zigpy_analog_output_devi assert hass.states.get(entity_id).state == "40.0" assert cluster.read_attributes.call_count == 10 assert "present_value" in cluster.read_attributes.call_args[0][0] + + +@pytest.mark.parametrize( + "attr, initial_value, new_value", + ( + ("on_off_transition_time", 20, 5), + ("on_level", 255, 50), + ("on_transition_time", 5, 1), + ("off_transition_time", 5, 1), + ("default_move_rate", 1, 5), + ("start_up_current_level", 254, 125), + ), +) +async def test_level_control_number( + hass, light, zha_device_joined, attr, initial_value, new_value +): + """Test zha level control number entities - new join.""" + + entity_registry = er.async_get(hass) + level_control_cluster = light.endpoints[1].level + level_control_cluster.PLUGGED_ATTR_READS = { + attr: initial_value, + } + zha_device = await zha_device_joined(light) + + entity_id = await find_entity_id( + Platform.NUMBER, + zha_device, + hass, + qualifier=attr, + ) + assert entity_id is not None + + assert level_control_cluster.read_attributes.call_count == 3 + assert ( + call( + [ + "on_off_transition_time", + "on_level", + "on_transition_time", + "off_transition_time", + "default_move_rate", + ], + allow_cache=True, + only_cache=False, + manufacturer=None, + ) + in level_control_cluster.read_attributes.call_args_list + ) + + assert ( + call( + ["start_up_current_level"], + allow_cache=True, + only_cache=False, + manufacturer=None, + ) + in level_control_cluster.read_attributes.call_args_list + ) + + assert ( + call( + [ + "current_level", + ], + allow_cache=False, + only_cache=False, + manufacturer=None, + ) + in level_control_cluster.read_attributes.call_args_list + ) + + state = hass.states.get(entity_id) + assert state + assert state.state == str(initial_value) + + entity_entry = entity_registry.async_get(entity_id) + assert entity_entry + assert entity_entry.entity_category == ENTITY_CATEGORY_CONFIG + + # Test number set_value + await hass.services.async_call( + "number", + "set_value", + { + "entity_id": entity_id, + "value": new_value, + }, + blocking=True, + ) + + assert level_control_cluster.write_attributes.call_count == 1 + assert level_control_cluster.write_attributes.call_args[0][0] == { + attr: new_value, + } + + state = hass.states.get(entity_id) + assert state + assert state.state == str(new_value) + + level_control_cluster.read_attributes.reset_mock() + await async_setup_component(hass, "homeassistant", {}) + await hass.async_block_till_done() + + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": entity_id}, blocking=True + ) + # the mocking doesn't update the attr cache so this flips back to initial value + assert hass.states.get(entity_id).state == str(initial_value) + assert level_control_cluster.read_attributes.call_count == 1 + assert ( + call( + [ + attr, + ], + allow_cache=False, + only_cache=False, + manufacturer=None, + ) + in level_control_cluster.read_attributes.call_args_list + ) + + level_control_cluster.write_attributes.reset_mock() + level_control_cluster.write_attributes.side_effect = ZigbeeException + + await hass.services.async_call( + "number", + "set_value", + { + "entity_id": entity_id, + "value": new_value, + }, + blocking=True, + ) + + assert level_control_cluster.write_attributes.call_count == 1 + assert level_control_cluster.write_attributes.call_args[0][0] == { + attr: new_value, + } + assert hass.states.get(entity_id).state == str(initial_value) From bac2dce5ee3b15e604ec8a165983f436c3014c0f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 20:14:04 +0200 Subject: [PATCH 498/930] Update twentemilieu to 0.6.1 (#71953) --- homeassistant/components/twentemilieu/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/twentemilieu/manifest.json b/homeassistant/components/twentemilieu/manifest.json index e8e280dcd3d..c7dd2508fd8 100644 --- a/homeassistant/components/twentemilieu/manifest.json +++ b/homeassistant/components/twentemilieu/manifest.json @@ -3,7 +3,7 @@ "name": "Twente Milieu", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/twentemilieu", - "requirements": ["twentemilieu==0.6.0"], + "requirements": ["twentemilieu==0.6.1"], "codeowners": ["@frenck"], "quality_scale": "platinum", "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index fa529f22320..ede1f0cb075 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2337,7 +2337,7 @@ ttls==1.4.3 tuya-iot-py-sdk==0.6.6 # homeassistant.components.twentemilieu -twentemilieu==0.6.0 +twentemilieu==0.6.1 # homeassistant.components.twilio twilio==6.32.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c8b37a3c73d..a1b49258def 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1525,7 +1525,7 @@ ttls==1.4.3 tuya-iot-py-sdk==0.6.6 # homeassistant.components.twentemilieu -twentemilieu==0.6.0 +twentemilieu==0.6.1 # homeassistant.components.twilio twilio==6.32.0 From fb7aead7567181087a23caa0221918bf6c8079d0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 14:15:04 -0500 Subject: [PATCH 499/930] Guard expensive `cast`s in performance sensitive spots with `if TYPE_CHECKING` (#71960) --- homeassistant/core.py | 44 +++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 061695ade6a..973a940ffbc 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -406,7 +406,12 @@ class HomeAssistant: if asyncio.iscoroutine(target): return self.async_create_task(target) - target = cast(Callable[..., Union[Coroutine[Any, Any, _R], _R]], target) + # This code path is performance sensitive and uses + # if TYPE_CHECKING to avoid the overhead of constructing + # the type used for the cast. For history see: + # https://github.com/home-assistant/core/pull/71960 + if TYPE_CHECKING: + target = cast(Callable[..., Union[Coroutine[Any, Any, _R], _R]], target) return self.async_add_hass_job(HassJob(target), *args) @overload @@ -434,17 +439,25 @@ class HomeAssistant: args: parameters for method to call. """ task: asyncio.Future[_R] + # This code path is performance sensitive and uses + # if TYPE_CHECKING to avoid the overhead of constructing + # the type used for the cast. For history see: + # https://github.com/home-assistant/core/pull/71960 if hassjob.job_type == HassJobType.Coroutinefunction: - task = self.loop.create_task( - cast(Callable[..., Coroutine[Any, Any, _R]], hassjob.target)(*args) - ) + if TYPE_CHECKING: + hassjob.target = cast( + Callable[..., Coroutine[Any, Any, _R]], hassjob.target + ) + task = self.loop.create_task(hassjob.target(*args)) elif hassjob.job_type == HassJobType.Callback: - self.loop.call_soon(cast(Callable[..., _R], hassjob.target), *args) + if TYPE_CHECKING: + hassjob.target = cast(Callable[..., _R], hassjob.target) + self.loop.call_soon(hassjob.target, *args) return None else: - task = self.loop.run_in_executor( - None, cast(Callable[..., _R], hassjob.target), *args - ) + if TYPE_CHECKING: + hassjob.target = cast(Callable[..., _R], hassjob.target) + task = self.loop.run_in_executor(None, hassjob.target, *args) # If a task is scheduled if self._track_task: @@ -522,8 +535,14 @@ class HomeAssistant: hassjob: HassJob args: parameters for method to call. """ + # This code path is performance sensitive and uses + # if TYPE_CHECKING to avoid the overhead of constructing + # the type used for the cast. For history see: + # https://github.com/home-assistant/core/pull/71960 if hassjob.job_type == HassJobType.Callback: - cast(Callable[..., _R], hassjob.target)(*args) + if TYPE_CHECKING: + hassjob.target = cast(Callable[..., _R], hassjob.target) + hassjob.target(*args) return None return self.async_add_hass_job(hassjob, *args) @@ -565,7 +584,12 @@ class HomeAssistant: if asyncio.iscoroutine(target): return self.async_create_task(target) - target = cast(Callable[..., Union[Coroutine[Any, Any, _R], _R]], target) + # This code path is performance sensitive and uses + # if TYPE_CHECKING to avoid the overhead of constructing + # the type used for the cast. For history see: + # https://github.com/home-assistant/core/pull/71960 + if TYPE_CHECKING: + target = cast(Callable[..., Union[Coroutine[Any, Any, _R], _R]], target) return self.async_run_hass_job(HassJob(target), *args) def block_till_done(self) -> None: From d22472208f34e4ee2c248cbae019d8f182c26ead Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 16 May 2022 21:41:43 +0200 Subject: [PATCH 500/930] Update frontend to 20220516.0 (#71964) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 8c475d51abb..0040dece159 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220504.1"], + "requirements": ["home-assistant-frontend==20220516.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index b9d08a07591..db6433c65b7 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220504.1 +home-assistant-frontend==20220516.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index ede1f0cb075..2f6bd5cac3f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220504.1 +home-assistant-frontend==20220516.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a1b49258def..752788718e4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220504.1 +home-assistant-frontend==20220516.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 03e98a9a32f87590da803fcdc6c3898a3429c09b Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 16 May 2022 22:13:43 +0200 Subject: [PATCH 501/930] Update sentry-sdk to 1.5.12 (#71930) * Update sentry-sdk to 1.5.12 * Remove now unneeded mypy ignore --- homeassistant/components/sentry/__init__.py | 2 +- homeassistant/components/sentry/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sentry/__init__.py b/homeassistant/components/sentry/__init__.py index c02b16fcfb0..ab3b80414dd 100644 --- a/homeassistant/components/sentry/__init__.py +++ b/homeassistant/components/sentry/__init__.py @@ -80,7 +80,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: } # pylint: disable-next=abstract-class-instantiated - sentry_sdk.init( # type: ignore[abstract] + sentry_sdk.init( dsn=entry.data[CONF_DSN], environment=entry.options.get(CONF_ENVIRONMENT), integrations=[sentry_logging, AioHttpIntegration(), SqlalchemyIntegration()], diff --git a/homeassistant/components/sentry/manifest.json b/homeassistant/components/sentry/manifest.json index 1096edd4e36..b76318f046d 100644 --- a/homeassistant/components/sentry/manifest.json +++ b/homeassistant/components/sentry/manifest.json @@ -3,7 +3,7 @@ "name": "Sentry", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sentry", - "requirements": ["sentry-sdk==1.5.10"], + "requirements": ["sentry-sdk==1.5.12"], "codeowners": ["@dcramer", "@frenck"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 2f6bd5cac3f..e5a091ee65d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2141,7 +2141,7 @@ sendgrid==6.8.2 sense_energy==0.10.4 # homeassistant.components.sentry -sentry-sdk==1.5.10 +sentry-sdk==1.5.12 # homeassistant.components.sharkiq sharkiq==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 752788718e4..2623f133464 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1407,7 +1407,7 @@ securetar==2022.2.0 sense_energy==0.10.4 # homeassistant.components.sentry -sentry-sdk==1.5.10 +sentry-sdk==1.5.12 # homeassistant.components.sharkiq sharkiq==0.0.1 From 007c6d22366b39c166bc4c511a145382d8fd7551 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 17 May 2022 00:04:57 +0200 Subject: [PATCH 502/930] Streamline setup of deCONZ binary sensor platform (#71820) --- .../components/deconz/alarm_control_panel.py | 2 +- .../components/deconz/binary_sensor.py | 59 ++++++++++--------- homeassistant/components/deconz/climate.py | 4 +- homeassistant/components/deconz/cover.py | 2 +- .../components/deconz/deconz_event.py | 4 +- homeassistant/components/deconz/fan.py | 2 +- homeassistant/components/deconz/gateway.py | 45 ++++++++++++-- homeassistant/components/deconz/light.py | 2 +- homeassistant/components/deconz/lock.py | 4 +- homeassistant/components/deconz/number.py | 2 +- homeassistant/components/deconz/services.py | 1 + homeassistant/components/deconz/siren.py | 2 +- homeassistant/components/deconz/switch.py | 14 ++--- 13 files changed, 89 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index a29c439123e..2e825dafd65 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -81,7 +81,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.ancillary_control.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index c3b16e509cb..e57f0bde3a6 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -5,6 +5,7 @@ from collections.abc import Callable from dataclasses import dataclass from pydeconz.interfaces.sensors import SensorResources +from pydeconz.models.event import EventType from pydeconz.models.sensor.alarm import Alarm from pydeconz.models.sensor.carbon_monoxide import CarbonMonoxide from pydeconz.models.sensor.fire import Fire @@ -188,48 +189,48 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_sensor(sensors: list[SensorResources] | None = None) -> None: - """Add binary sensor from deCONZ.""" - entities: list[DeconzBinarySensor] = [] + def async_add_sensor(_: EventType, sensor_id: str) -> None: + """Add sensor from deCONZ.""" + sensor = gateway.api.sensors[sensor_id] - if sensors is None: - sensors = gateway.api.sensors.values() + if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + return - for sensor in sensors: - - if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + for description in ( + ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS + ): + if ( + not hasattr(sensor, description.key) + or description.value_fn(sensor) is None + ): continue - known_entities = set(gateway.entities[DOMAIN]) - for description in ( - ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS - ): + async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) - if ( - not hasattr(sensor, description.key) - or description.value_fn(sensor) is None - ): - continue + config_entry.async_on_unload( + gateway.api.sensors.subscribe( + gateway.evaluate_add_device(async_add_sensor), + EventType.ADDED, + ) + ) + for sensor_id in gateway.api.sensors: + async_add_sensor(EventType.ADDED, sensor_id) - new_sensor = DeconzBinarySensor(sensor, gateway, description) - if new_sensor.unique_id not in known_entities: - entities.append(new_sensor) - - if entities: - async_add_entities(entities) + @callback + def async_reload_clip_sensors() -> None: + """Load clip sensor sensors from deCONZ.""" + for sensor_id, sensor in gateway.api.sensors.items(): + if sensor.type.startswith("CLIP"): + async_add_sensor(EventType.ADDED, sensor_id) config_entry.async_on_unload( async_dispatcher_connect( hass, - gateway.signal_new_sensor, - async_add_sensor, + gateway.signal_reload_clip_sensors, + async_reload_clip_sensors, ) ) - async_add_sensor( - [gateway.api.sensors[key] for key in sorted(gateway.api.sensors, key=int)] - ) - class DeconzBinarySensor(DeconzDevice, BinarySensorEntity): """Representation of a deCONZ binary sensor.""" diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index bdc366ff7db..1d57288d275 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -106,7 +106,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.thermostat.subscribe( - async_add_climate, + gateway.evaluate_add_device(async_add_climate), EventType.ADDED, ) ) @@ -115,7 +115,7 @@ async def async_setup_entry( @callback def async_reload_clip_sensors() -> None: - """Load clip climate sensors from deCONZ.""" + """Load clip sensors from deCONZ.""" for climate_id, climate in gateway.api.sensors.thermostat.items(): if climate.type.startswith("CLIP"): async_add_climate(EventType.ADDED, climate_id) diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 3d5dabb73c1..1931ac78389 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -45,7 +45,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.covers.subscribe( - async_add_cover, + gateway.evaluate_add_device(async_add_cover), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index 4f04aa34fe2..cfd60a63ec9 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -66,14 +66,14 @@ async def async_setup_events(gateway: DeconzGateway) -> None: gateway.config_entry.async_on_unload( gateway.api.sensors.ancillary_control.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) gateway.config_entry.async_on_unload( gateway.api.sensors.switch.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/fan.py b/homeassistant/components/deconz/fan.py index 93daae55ccf..b0d6d7755a2 100644 --- a/homeassistant/components/deconz/fan.py +++ b/homeassistant/components/deconz/fan.py @@ -50,7 +50,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.fans.subscribe( - async_add_fan, + gateway.evaluate_add_device(async_add_fan), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 0e880fa22c1..d59c5e2d160 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio +from collections.abc import Callable from types import MappingProxyType from typing import TYPE_CHECKING, Any, cast @@ -10,6 +11,7 @@ import async_timeout from pydeconz import DeconzSession, errors from pydeconz.models import ResourceGroup from pydeconz.models.alarm_system import AlarmSystem as DeconzAlarmSystem +from pydeconz.models.event import EventType from pydeconz.models.group import Group as DeconzGroup from pydeconz.models.light import LightBase as DeconzLight from pydeconz.models.sensor import SensorBase as DeconzSensor @@ -76,10 +78,14 @@ class DeconzGateway: self.deconz_ids: dict[str, str] = {} self.entities: dict[str, set[str]] = {} self.events: list[DeconzAlarmEvent | DeconzEvent] = [] + self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set() self._option_allow_deconz_groups = self.config_entry.options.get( CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS ) + self.option_allow_new_devices = self.config_entry.options.get( + CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES + ) @property def bridgeid(self) -> str: @@ -112,12 +118,31 @@ class DeconzGateway: CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS ) - @property - def option_allow_new_devices(self) -> bool: - """Allow automatic adding of new devices.""" - return self.config_entry.options.get( - CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES - ) + @callback + def evaluate_add_device( + self, add_device_callback: Callable[[EventType, str], None] + ) -> Callable[[EventType, str], None]: + """Wrap add_device_callback to check allow_new_devices option.""" + + def async_add_device(event: EventType, device_id: str) -> None: + """Add device or add it to ignored_devices set. + + If ignore_state_updates is True means device_refresh service is used. + Device_refresh is expected to load new devices. + """ + if not self.option_allow_new_devices and not self.ignore_state_updates: + self.ignored_devices.add((add_device_callback, device_id)) + return + add_device_callback(event, device_id) + + return async_add_device + + @callback + def load_ignored_devices(self) -> None: + """Load previously ignored devices.""" + for add_entities, device_id in self.ignored_devices: + add_entities(EventType.ADDED, device_id) + self.ignored_devices.clear() # Callbacks @@ -230,6 +255,14 @@ class DeconzGateway: self._option_allow_deconz_groups = self.option_allow_deconz_groups + option_allow_new_devices = self.config_entry.options.get( + CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES + ) + if option_allow_new_devices and not self.option_allow_new_devices: + self.load_ignored_devices() + + self.option_allow_new_devices = option_allow_new_devices + entity_registry = er.async_get(self.hass) for entity_id, deconz_id in self.deconz_ids.items(): diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 2b03bb0ddb4..0f23ff02831 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -95,7 +95,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.lights.subscribe( - async_add_light, + gateway.evaluate_add_device(async_add_light), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index 5b8c9d87e67..dc3da3cbe8c 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -34,7 +34,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.locks.subscribe( - async_add_lock_from_light, + gateway.evaluate_add_device(async_add_lock_from_light), EventType.ADDED, ) ) @@ -49,7 +49,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.door_lock.subscribe( - async_add_lock_from_sensor, + gateway.evaluate_add_device(async_add_lock_from_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 81886fd3c7c..5d555797372 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -77,7 +77,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.sensors.presence.subscribe( - async_add_sensor, + gateway.evaluate_add_device(async_add_sensor), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/services.py b/homeassistant/components/deconz/services.py index 3d12a293c39..a9a5172d72e 100644 --- a/homeassistant/components/deconz/services.py +++ b/homeassistant/components/deconz/services.py @@ -144,6 +144,7 @@ async def async_refresh_devices_service(gateway: DeconzGateway) -> None: """Refresh available devices from deCONZ.""" gateway.ignore_state_updates = True await gateway.api.refresh_state() + gateway.load_ignored_devices() gateway.ignore_state_updates = False for resource_type in gateway.deconz_resource_type_to_signal_new_device: diff --git a/homeassistant/components/deconz/siren.py b/homeassistant/components/deconz/siren.py index 2a11cd5346a..0c5b3010626 100644 --- a/homeassistant/components/deconz/siren.py +++ b/homeassistant/components/deconz/siren.py @@ -37,7 +37,7 @@ async def async_setup_entry( config_entry.async_on_unload( gateway.api.lights.sirens.subscribe( - async_add_siren, + gateway.evaluate_add_device(async_add_siren), EventType.ADDED, ) ) diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index 6789b20b3c7..d6e12365407 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -30,21 +30,21 @@ async def async_setup_entry( gateway.entities[DOMAIN] = set() @callback - def async_add_switch(_: EventType, light_id: str) -> None: + def async_add_switch(_: EventType, switch_id: str) -> None: """Add switch from deCONZ.""" - light = gateway.api.lights.lights[light_id] - if light.type not in POWER_PLUGS: + switch = gateway.api.lights.lights[switch_id] + if switch.type not in POWER_PLUGS: return - async_add_entities([DeconzPowerPlug(light, gateway)]) + async_add_entities([DeconzPowerPlug(switch, gateway)]) config_entry.async_on_unload( gateway.api.lights.lights.subscribe( - async_add_switch, + gateway.evaluate_add_device(async_add_switch), EventType.ADDED, ) ) - for light_id in gateway.api.lights.lights: - async_add_switch(EventType.ADDED, light_id) + for switch_id in gateway.api.lights.lights: + async_add_switch(EventType.ADDED, switch_id) class DeconzPowerPlug(DeconzDevice, SwitchEntity): From f4a2afeb375483f952a0e142fb6c4ef2a7b4d21a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 17:48:14 -0500 Subject: [PATCH 503/930] Guard against recorder pool current connection disappearing during global destruction (#71971) --- homeassistant/components/recorder/pool.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/recorder/pool.py b/homeassistant/components/recorder/pool.py index f2f952121af..52b6b74dfa1 100644 --- a/homeassistant/components/recorder/pool.py +++ b/homeassistant/components/recorder/pool.py @@ -55,7 +55,12 @@ class RecorderPool(SingletonThreadPool, NullPool): # type: ignore[misc] def shutdown(self) -> None: """Close the connection.""" - if self.recorder_or_dbworker and self._conn and (conn := self._conn.current()): + if ( + self.recorder_or_dbworker + and self._conn + and hasattr(self._conn, "current") + and (conn := self._conn.current()) + ): conn.close() def dispose(self) -> None: From 8fcee01db0543271cdbf0ef46ab028fa3eed1326 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 18:03:02 -0500 Subject: [PATCH 504/930] Remove unnecessary flush from recorder (#71910) --- homeassistant/components/recorder/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index ba0e5ba17d1..91dc0a27ead 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -847,15 +847,14 @@ class Recorder(threading.Thread): assert self.event_session is not None self._commits_without_expire += 1 + self.event_session.commit() if self._pending_expunge: - self.event_session.flush() for dbstate in self._pending_expunge: # Expunge the state so its not expired # until we use it later for dbstate.old_state if dbstate in self.event_session: self.event_session.expunge(dbstate) self._pending_expunge = [] - self.event_session.commit() # We just committed the state attributes to the database # and we now know the attributes_ids. We can save From e339f433221547baa6218e75783acd743efdefce Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 16 May 2022 18:03:39 -0500 Subject: [PATCH 505/930] Add a timeout during Sonos speaker setup (#71973) --- homeassistant/components/sonos/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index c775b475dc4..1d7acd8d8dc 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -204,7 +204,7 @@ class SonosDiscoveryManager: def _add_speaker(self, soco: SoCo) -> None: """Create and set up a new SonosSpeaker instance.""" try: - speaker_info = soco.get_speaker_info(True) + speaker_info = soco.get_speaker_info(True, timeout=7) if soco.uid not in self.data.boot_counts: self.data.boot_counts[soco.uid] = soco.boot_seqnum _LOGGER.debug("Adding new speaker: %s", speaker_info) From 9092dcaceaaa0396aaf175edac3a56a2aeadf33c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 18:04:05 -0500 Subject: [PATCH 506/930] Use async_capture_events for core tests (#71970) --- tests/test_core.py | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 104828f64e1..1ac7002cb7b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1055,13 +1055,7 @@ def test_config_is_allowed_external_url(): async def test_event_on_update(hass): """Test that event is fired on update.""" - events = [] - - @ha.callback - def callback(event): - events.append(event) - - hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, callback) + events = async_capture_events(hass, EVENT_CORE_CONFIG_UPDATE) assert hass.config.latitude != 12 @@ -1141,13 +1135,7 @@ async def test_service_executed_with_subservices(hass): async def test_service_call_event_contains_original_data(hass): """Test that service call event contains original data.""" - events = [] - - @ha.callback - def callback(event): - events.append(event) - - hass.bus.async_listen(EVENT_CALL_SERVICE, callback) + events = async_capture_events(hass, EVENT_CALL_SERVICE) calls = async_mock_service( hass, "test", "service", vol.Schema({"number": vol.Coerce(int)}) @@ -1787,14 +1775,7 @@ def _ulid_timestamp(ulid: str) -> int: async def test_state_change_events_context_id_match_state_time(hass): """Test last_updated, timed_fired, and the ulid all have the same time.""" - events = [] - - @ha.callback - def _event_listener(event): - events.append(event) - - hass.bus.async_listen(ha.EVENT_STATE_CHANGED, _event_listener) - + events = async_capture_events(hass, ha.EVENT_STATE_CHANGED) hass.states.async_set("light.bedroom", "on") await hass.async_block_till_done() state: State = hass.states.get("light.bedroom") @@ -1808,14 +1789,7 @@ async def test_state_change_events_context_id_match_state_time(hass): async def test_state_firing_event_matches_context_id_ulid_time(hass): """Test timed_fired and the ulid have the same time.""" - events = [] - - @ha.callback - def _event_listener(event): - events.append(event) - - hass.bus.async_listen(EVENT_HOMEASSISTANT_STARTED, _event_listener) - + events = async_capture_events(hass, EVENT_HOMEASSISTANT_STARTED) hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() From 17470618206e05561bd381017a6476bb53356670 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Tue, 17 May 2022 01:51:30 +0200 Subject: [PATCH 507/930] Enable NUT strict typing (#71913) --- .strict-typing | 1 + homeassistant/components/nut/__init__.py | 81 ++++++++++++--------- homeassistant/components/nut/config_flow.py | 62 +++++++++------- homeassistant/components/nut/sensor.py | 35 ++++++++- mypy.ini | 11 +++ 5 files changed, 126 insertions(+), 64 deletions(-) diff --git a/.strict-typing b/.strict-typing index 36ed1685e9f..4ec4fbff5ee 100644 --- a/.strict-typing +++ b/.strict-typing @@ -165,6 +165,7 @@ homeassistant.components.no_ip.* homeassistant.components.notify.* homeassistant.components.notion.* homeassistant.components.number.* +homeassistant.components.nut.* homeassistant.components.oncue.* homeassistant.components.onewire.* homeassistant.components.open_meteo.* diff --git a/homeassistant/components/nut/__init__.py b/homeassistant/components/nut/__init__.py index 775c512f946..28c41ccda3a 100644 --- a/homeassistant/components/nut/__init__.py +++ b/homeassistant/components/nut/__init__.py @@ -1,4 +1,7 @@ """The nut component.""" +from __future__ import annotations + +from dataclasses import dataclass from datetime import timedelta import logging @@ -7,9 +10,6 @@ from pynut2.nut2 import PyNUTClient, PyNUTError from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_SW_VERSION, CONF_ALIAS, CONF_HOST, CONF_PASSWORD, @@ -59,7 +59,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = PyNUTData(host, port, alias, username, password) - async def async_update_data(): + async def async_update_data() -> dict[str, str]: """Fetch data from NUT.""" async with async_timeout.timeout(10): await hass.async_add_executor_job(data.update) @@ -98,9 +98,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: config_entry_id=entry.entry_id, identifiers={(DOMAIN, unique_id)}, name=data.name.title(), - manufacturer=data.device_info.get(ATTR_MANUFACTURER), - model=data.device_info.get(ATTR_MODEL), - sw_version=data.device_info.get(ATTR_SW_VERSION), + manufacturer=data.device_info.manufacturer, + model=data.device_info.model, + sw_version=data.device_info.firmware, ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) @@ -120,7 +120,7 @@ async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> Non await hass.config_entries.async_reload(entry.entry_id) -def _manufacturer_from_status(status): +def _manufacturer_from_status(status: dict[str, str]) -> str | None: """Find the best manufacturer value from the status.""" return ( status.get("device.mfr") @@ -130,7 +130,7 @@ def _manufacturer_from_status(status): ) -def _model_from_status(status): +def _model_from_status(status: dict[str, str]) -> str | None: """Find the best model value from the status.""" return ( status.get("device.model") @@ -139,12 +139,12 @@ def _model_from_status(status): ) -def _firmware_from_status(status): +def _firmware_from_status(status: dict[str, str]) -> str | None: """Find the best firmware value from the status.""" return status.get("ups.firmware") or status.get("ups.firmware.aux") -def _serial_from_status(status): +def _serial_from_status(status: dict[str, str]) -> str | None: """Find the best serialvalue from the status.""" serial = status.get("device.serial") or status.get("ups.serial") if serial and ( @@ -154,7 +154,7 @@ def _serial_from_status(status): return serial -def _unique_id_from_status(status): +def _unique_id_from_status(status: dict[str, str]) -> str | None: """Find the best unique id value from the status.""" serial = _serial_from_status(status) # We must have a serial for this to be unique @@ -174,6 +174,15 @@ def _unique_id_from_status(status): return "_".join(unique_id_group) +@dataclass +class NUTDeviceInfo: + """Device information for NUT.""" + + manufacturer: str | None = None + model: str | None = None + firmware: str | None = None + + class PyNUTData: """Stores the data retrieved from NUT. @@ -181,7 +190,14 @@ class PyNUTData: updates from the server. """ - def __init__(self, host, port, alias, username, password): + def __init__( + self, + host: str, + port: int, + alias: str | None, + username: str | None, + password: str | None, + ) -> None: """Initialize the data object.""" self._host = host @@ -190,29 +206,29 @@ class PyNUTData: # Establish client with persistent=False to open/close connection on # each update call. This is more reliable with async. self._client = PyNUTClient(self._host, port, username, password, 5, False) - self.ups_list = None - self._status = None - self._device_info = None + self.ups_list: dict[str, str] | None = None + self._status: dict[str, str] | None = None + self._device_info: NUTDeviceInfo | None = None @property - def status(self): + def status(self) -> dict[str, str] | None: """Get latest update if throttle allows. Return status.""" return self._status @property - def name(self): + def name(self) -> str: """Return the name of the ups.""" - return self._alias + return self._alias or f"Nut-{self._host}" @property - def device_info(self): + def device_info(self) -> NUTDeviceInfo: """Return the device info for the ups.""" - return self._device_info or {} + return self._device_info or NUTDeviceInfo() - def _get_alias(self): + def _get_alias(self) -> str | None: """Get the ups alias from NUT.""" try: - ups_list = self._client.list_ups() + ups_list: dict[str, str] = self._client.list_ups() except PyNUTError as err: _LOGGER.error("Failure getting NUT ups alias, %s", err) return None @@ -224,7 +240,7 @@ class PyNUTData: self.ups_list = ups_list return list(ups_list)[0] - def _get_device_info(self): + def _get_device_info(self) -> NUTDeviceInfo | None: """Get the ups device info from NUT.""" if not self._status: return None @@ -232,27 +248,24 @@ class PyNUTData: manufacturer = _manufacturer_from_status(self._status) model = _model_from_status(self._status) firmware = _firmware_from_status(self._status) - device_info = {} - if model: - device_info[ATTR_MODEL] = model - if manufacturer: - device_info[ATTR_MANUFACTURER] = manufacturer - if firmware: - device_info[ATTR_SW_VERSION] = firmware + device_info = NUTDeviceInfo(manufacturer, model, firmware) + return device_info - def _get_status(self): + def _get_status(self) -> dict[str, str] | None: """Get the ups status from NUT.""" if self._alias is None: self._alias = self._get_alias() try: - return self._client.list_vars(self._alias) + status: dict[str, str] = self._client.list_vars(self._alias) except (PyNUTError, ConnectionResetError) as err: _LOGGER.debug("Error getting NUT vars for host %s: %s", self._host, err) return None - def update(self): + return status + + def update(self) -> None: """Fetch the latest status from NUT.""" self._status = self._get_status() if self._device_info is None: diff --git a/homeassistant/components/nut/config_flow.py b/homeassistant/components/nut/config_flow.py index fb0a2210a69..5ba8024878a 100644 --- a/homeassistant/components/nut/config_flow.py +++ b/homeassistant/components/nut/config_flow.py @@ -2,11 +2,13 @@ from __future__ import annotations import logging +from typing import Any import voluptuous as vol -from homeassistant import config_entries, core, exceptions +from homeassistant import exceptions from homeassistant.components import zeroconf +from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import ( CONF_ALIAS, CONF_BASE, @@ -16,7 +18,7 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, CONF_USERNAME, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from . import PyNUTData @@ -42,12 +44,12 @@ def _base_schema(discovery_info: zeroconf.ZeroconfServiceInfo | None) -> vol.Sch return vol.Schema(base_schema) -def _ups_schema(ups_list): +def _ups_schema(ups_list: dict[str, str]) -> vol.Schema: """UPS selection schema.""" return vol.Schema({vol.Required(CONF_ALIAS): vol.In(ups_list)}) -async def validate_input(hass: core.HomeAssistant, data): +async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]: """Validate the user input allows us to connect. Data has the keys from _base_schema with values provided by the user. @@ -59,15 +61,15 @@ async def validate_input(hass: core.HomeAssistant, data): username = data.get(CONF_USERNAME) password = data.get(CONF_PASSWORD) - data = PyNUTData(host, port, alias, username, password) - await hass.async_add_executor_job(data.update) - if not (status := data.status): + nut_data = PyNUTData(host, port, alias, username, password) + await hass.async_add_executor_job(nut_data.update) + if not (status := nut_data.status): raise CannotConnect - return {"ups_list": data.ups_list, "available_resources": status} + return {"ups_list": nut_data.ups_list, "available_resources": status} -def _format_host_port_alias(user_input): +def _format_host_port_alias(user_input: dict[str, Any]) -> str: """Format a host, port, and alias so it can be used for comparison or display.""" host = user_input[CONF_HOST] port = user_input[CONF_PORT] @@ -77,17 +79,17 @@ def _format_host_port_alias(user_input): return f"{host}:{port}" -class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): +class NutConfigFlow(ConfigFlow, domain=DOMAIN): """Handle a config flow for Network UPS Tools (NUT).""" VERSION = 1 - def __init__(self): + def __init__(self) -> None: """Initialize the nut config flow.""" - self.nut_config = {} + self.nut_config: dict[str, Any] = {} self.discovery_info: zeroconf.ZeroconfServiceInfo | None = None - self.ups_list = None - self.title = None + self.ups_list: dict[str, str] | None = None + self.title: str | None = None async def async_step_zeroconf( self, discovery_info: zeroconf.ZeroconfServiceInfo @@ -101,9 +103,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): } return await self.async_step_user() - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the user input.""" - errors = {} + errors: dict[str, str] = {} if user_input is not None: if self.discovery_info: user_input.update( @@ -129,9 +133,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=_base_schema(self.discovery_info), errors=errors ) - async def async_step_ups(self, user_input=None): + async def async_step_ups( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the picking the ups.""" - errors = {} + errors: dict[str, str] = {} if user_input is not None: self.nut_config.update(user_input) @@ -144,20 +150,22 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="ups", - data_schema=_ups_schema(self.ups_list), + data_schema=_ups_schema(self.ups_list or {}), errors=errors, ) - def _host_port_alias_already_configured(self, user_input): + def _host_port_alias_already_configured(self, user_input: dict[str, Any]) -> bool: """See if we already have a nut entry matching user input configured.""" existing_host_port_aliases = { - _format_host_port_alias(entry.data) + _format_host_port_alias(dict(entry.data)) for entry in self._async_current_entries() if CONF_HOST in entry.data } return _format_host_port_alias(user_input) in existing_host_port_aliases - async def _async_validate_or_error(self, config): + async def _async_validate_or_error( + self, config: dict[str, Any] + ) -> tuple[dict[str, Any], dict[str, str]]: errors = {} info = {} try: @@ -171,19 +179,21 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) -class OptionsFlowHandler(config_entries.OptionsFlow): +class OptionsFlowHandler(OptionsFlow): """Handle a option flow for nut.""" - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" self.config_entry = config_entry - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle options flow.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 1992ba49635..6992e41366e 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -1,11 +1,18 @@ """Provides a sensor to track various status aspects of a UPS.""" from __future__ import annotations +from dataclasses import asdict import logging +from typing import cast from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_UNKNOWN +from homeassistant.const import ( + ATTR_MANUFACTURER, + ATTR_MODEL, + ATTR_SW_VERSION, + STATE_UNKNOWN, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -26,9 +33,27 @@ from .const import ( STATE_TYPES, ) +NUT_DEV_INFO_TO_DEV_INFO: dict[str, str] = { + "manufacturer": ATTR_MANUFACTURER, + "model": ATTR_MODEL, + "firmware": ATTR_SW_VERSION, +} + _LOGGER = logging.getLogger(__name__) +def _get_nut_device_info(data: PyNUTData) -> DeviceInfo: + """Return a DeviceInfo object filled with NUT device info.""" + nut_dev_infos = asdict(data.device_info) + nut_infos = { + info_key: nut_dev_infos[nut_key] + for nut_key, info_key in NUT_DEV_INFO_TO_DEV_INFO.items() + if nut_dev_infos[nut_key] is not None + } + + return cast(DeviceInfo, nut_infos) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -64,6 +89,8 @@ async def async_setup_entry( class NUTSensor(CoordinatorEntity, SensorEntity): """Representation of a sensor entity for NUT status values.""" + coordinator: DataUpdateCoordinator[dict[str, str]] + def __init__( self, coordinator: DataUpdateCoordinator, @@ -82,10 +109,10 @@ class NUTSensor(CoordinatorEntity, SensorEntity): identifiers={(DOMAIN, unique_id)}, name=device_name, ) - self._attr_device_info.update(data.device_info) + self._attr_device_info.update(_get_nut_device_info(data)) @property - def native_value(self): + def native_value(self) -> str | None: """Return entity state from ups.""" status = self.coordinator.data if self.entity_description.key == KEY_STATUS_DISPLAY: @@ -93,7 +120,7 @@ class NUTSensor(CoordinatorEntity, SensorEntity): return status.get(self.entity_description.key) -def _format_display_state(status): +def _format_display_state(status: dict[str, str]) -> str: """Return UPS display state.""" try: return " ".join(STATE_TYPES[state] for state in status[KEY_STATUS].split()) diff --git a/mypy.ini b/mypy.ini index dc023da8d01..898c6b860f0 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1578,6 +1578,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.nut.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.oncue.*] check_untyped_defs = true disallow_incomplete_defs = true From 1a6ccdbf597787be11d2e456ae7da3daf4d5b5dd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 May 2022 18:52:05 -0500 Subject: [PATCH 508/930] Bump unifi-discovery to 1.1.3 (#71975) --- homeassistant/components/unifiprotect/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 3efad51db31..36b6011e8fc 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Protect", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==3.5.1", "unifi-discovery==1.1.2"], + "requirements": ["pyunifiprotect==3.5.1", "unifi-discovery==1.1.3"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index e5a091ee65d..027b6406b2a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2352,7 +2352,7 @@ uEagle==0.0.2 uasiren==0.0.1 # homeassistant.components.unifiprotect -unifi-discovery==1.1.2 +unifi-discovery==1.1.3 # homeassistant.components.unifiled unifiled==0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2623f133464..c9949ba3b2c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1540,7 +1540,7 @@ uEagle==0.0.2 uasiren==0.0.1 # homeassistant.components.unifiprotect -unifi-discovery==1.1.2 +unifi-discovery==1.1.3 # homeassistant.components.upb upb_lib==0.4.12 From 3b88c6c0126c8b019322fecd9e8547cc2b6e8b60 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 16 May 2022 17:10:34 -0700 Subject: [PATCH 509/930] Inverse parallel updates default check, follow sync "update" method (#71720) --- homeassistant/helpers/entity_platform.py | 6 +++--- tests/helpers/test_entity_platform.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 3d57a9c3dc0..ecf2125962a 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -120,7 +120,7 @@ class EntityPlatform: @callback def _get_parallel_updates_semaphore( - self, entity_has_async_update: bool + self, entity_has_sync_update: bool ) -> asyncio.Semaphore | None: """Get or create a semaphore for parallel updates. @@ -138,7 +138,7 @@ class EntityPlatform: parallel_updates = getattr(self.platform, "PARALLEL_UPDATES", None) - if parallel_updates is None and not entity_has_async_update: + if parallel_updates is None and entity_has_sync_update: parallel_updates = 1 if parallel_updates == 0: @@ -422,7 +422,7 @@ class EntityPlatform: entity.add_to_platform_start( self.hass, self, - self._get_parallel_updates_semaphore(hasattr(entity, "async_update")), + self._get_parallel_updates_semaphore(hasattr(entity, "update")), ) # Update properties before we generate the entity_id diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index 3f18d565e83..933669ebc53 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -332,6 +332,25 @@ async def test_parallel_updates_sync_platform(hass): assert entity.parallel_updates._value == 1 +async def test_parallel_updates_no_update_method(hass): + """Test platform parallel_updates default set to 0.""" + platform = MockPlatform() + + mock_entity_platform(hass, "test_domain.platform", platform) + + component = EntityComponent(_LOGGER, DOMAIN, hass) + component._platforms = {} + + await component.async_setup({DOMAIN: {"platform": "platform"}}) + await hass.async_block_till_done() + + handle = list(component._platforms.values())[-1] + + entity = MockEntity() + await handle.async_add_entities([entity]) + assert entity.parallel_updates is None + + async def test_parallel_updates_sync_platform_with_constant(hass): """Test sync platform can set parallel_updates limit.""" platform = MockPlatform() From 2d1a612976e59a40211356bfdfd2564f2b6679a6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 17 May 2022 00:23:03 +0000 Subject: [PATCH 510/930] [ci skip] Translation update --- .../aladdin_connect/translations/bg.json | 26 +++++++++++++++++ .../aladdin_connect/translations/no.json | 27 +++++++++++++++++ .../aladdin_connect/translations/pl.json | 26 +++++++++++++++++ .../components/aurora/translations/nl.json | 2 +- .../components/baf/translations/bg.json | 23 +++++++++++++++ .../components/baf/translations/de.json | 23 +++++++++++++++ .../components/baf/translations/el.json | 23 +++++++++++++++ .../components/baf/translations/hu.json | 23 +++++++++++++++ .../components/baf/translations/ja.json | 23 +++++++++++++++ .../components/baf/translations/ko.json | 13 +++++++++ .../components/baf/translations/no.json | 23 +++++++++++++++ .../components/baf/translations/pl.json | 18 ++++++++++++ .../components/baf/translations/zh-Hant.json | 23 +++++++++++++++ .../derivative/translations/nl.json | 22 +++++++------- .../geocaching/translations/bg.json | 17 +++++++++++ .../components/group/translations/nl.json | 6 ++-- .../components/hassio/translations/it.json | 6 ++-- .../homeassistant/translations/it.json | 6 ++-- .../components/knx/translations/it.json | 2 +- .../litterrobot/translations/sensor.bg.json | 8 +++++ .../litterrobot/translations/sensor.no.json | 28 ++++++++++++++++++ .../litterrobot/translations/sensor.pl.json | 9 ++++++ .../motion_blinds/translations/no.json | 2 +- .../season/translations/sensor.fy.json | 7 +++++ .../components/siren/translations/bg.json | 3 ++ .../components/siren/translations/ca.json | 3 ++ .../components/siren/translations/de.json | 3 ++ .../components/siren/translations/el.json | 3 ++ .../components/siren/translations/et.json | 3 ++ .../components/siren/translations/fr.json | 3 ++ .../components/siren/translations/it.json | 3 ++ .../components/siren/translations/ja.json | 3 ++ .../components/siren/translations/ko.json | 3 ++ .../components/siren/translations/nl.json | 3 ++ .../components/siren/translations/pt-BR.json | 3 ++ .../components/siren/translations/tr.json | 3 ++ .../components/slack/translations/bg.json | 22 ++++++++++++++ .../components/slack/translations/ko.json | 9 ++++++ .../components/slack/translations/no.json | 29 +++++++++++++++++++ .../components/slack/translations/pl.json | 20 +++++++++++++ .../components/threshold/translations/nl.json | 4 +-- .../ukraine_alarm/translations/bg.json | 8 +++-- .../components/zha/translations/fy.json | 7 +++++ .../components/zwave_js/translations/it.json | 10 +++---- 44 files changed, 499 insertions(+), 32 deletions(-) create mode 100644 homeassistant/components/aladdin_connect/translations/bg.json create mode 100644 homeassistant/components/aladdin_connect/translations/no.json create mode 100644 homeassistant/components/aladdin_connect/translations/pl.json create mode 100644 homeassistant/components/baf/translations/bg.json create mode 100644 homeassistant/components/baf/translations/de.json create mode 100644 homeassistant/components/baf/translations/el.json create mode 100644 homeassistant/components/baf/translations/hu.json create mode 100644 homeassistant/components/baf/translations/ja.json create mode 100644 homeassistant/components/baf/translations/ko.json create mode 100644 homeassistant/components/baf/translations/no.json create mode 100644 homeassistant/components/baf/translations/pl.json create mode 100644 homeassistant/components/baf/translations/zh-Hant.json create mode 100644 homeassistant/components/geocaching/translations/bg.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.bg.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.no.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.pl.json create mode 100644 homeassistant/components/season/translations/sensor.fy.json create mode 100644 homeassistant/components/siren/translations/bg.json create mode 100644 homeassistant/components/siren/translations/ca.json create mode 100644 homeassistant/components/siren/translations/de.json create mode 100644 homeassistant/components/siren/translations/el.json create mode 100644 homeassistant/components/siren/translations/et.json create mode 100644 homeassistant/components/siren/translations/fr.json create mode 100644 homeassistant/components/siren/translations/it.json create mode 100644 homeassistant/components/siren/translations/ja.json create mode 100644 homeassistant/components/siren/translations/ko.json create mode 100644 homeassistant/components/siren/translations/nl.json create mode 100644 homeassistant/components/siren/translations/pt-BR.json create mode 100644 homeassistant/components/siren/translations/tr.json create mode 100644 homeassistant/components/slack/translations/bg.json create mode 100644 homeassistant/components/slack/translations/ko.json create mode 100644 homeassistant/components/slack/translations/no.json create mode 100644 homeassistant/components/slack/translations/pl.json create mode 100644 homeassistant/components/zha/translations/fy.json diff --git a/homeassistant/components/aladdin_connect/translations/bg.json b/homeassistant/components/aladdin_connect/translations/bg.json new file mode 100644 index 00000000000..d5babb02c1c --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/bg.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + }, + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + }, + "user": { + "data": { + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/no.json b/homeassistant/components/aladdin_connect/translations/no.json new file mode 100644 index 00000000000..c6e6e8413b3 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/no.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Passord" + }, + "description": "Aladdin Connect-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt", + "title": "Godkjenne integrering p\u00e5 nytt" + }, + "user": { + "data": { + "password": "Passord", + "username": "Brukernavn" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/pl.json b/homeassistant/components/aladdin_connect/translations/pl.json new file mode 100644 index 00000000000..3ebc9a4ff92 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/pl.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Has\u0142o" + }, + "title": "Ponownie uwierzytelnij integracj\u0119" + }, + "user": { + "data": { + "password": "Has\u0142o", + "username": "Nazwa u\u017cytkownika" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aurora/translations/nl.json b/homeassistant/components/aurora/translations/nl.json index fe7b4809f13..423e722921d 100644 --- a/homeassistant/components/aurora/translations/nl.json +++ b/homeassistant/components/aurora/translations/nl.json @@ -17,7 +17,7 @@ "step": { "init": { "data": { - "threshold": "Drempel (%)" + "threshold": "Drempelwaarde (%)" } } } diff --git a/homeassistant/components/baf/translations/bg.json b/homeassistant/components/baf/translations/bg.json new file mode 100644 index 00000000000..c5e33f4c079 --- /dev/null +++ b/homeassistant/components/baf/translations/bg.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "ipv6_not_supported": "IPv6 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430." + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "IP \u0430\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/de.json b/homeassistant/components/baf/translations/de.json new file mode 100644 index 00000000000..8e5344cf9b0 --- /dev/null +++ b/homeassistant/components/baf/translations/de.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert", + "ipv6_not_supported": "IPv6 wird nicht unterst\u00fctzt." + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "unknown": "Unerwarteter Fehler" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "M\u00f6chtest du {name} - {model} ({ip_address}) einrichten?" + }, + "user": { + "data": { + "ip_address": "IP-Adresse" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/el.json b/homeassistant/components/baf/translations/el.json new file mode 100644 index 00000000000..a73fe3aa490 --- /dev/null +++ b/homeassistant/components/baf/translations/el.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "ipv6_not_supported": "\u03a4\u03bf IPv6 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9." + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} - {model} ({ip_address});" + }, + "user": { + "data": { + "ip_address": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/hu.json b/homeassistant/components/baf/translations/hu.json new file mode 100644 index 00000000000..82e7d6aae75 --- /dev/null +++ b/homeassistant/components/baf/translations/hu.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", + "ipv6_not_supported": "Az IPv6 nem t\u00e1mogatott." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "IP c\u00edm" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/ja.json b/homeassistant/components/baf/translations/ja.json new file mode 100644 index 00000000000..0e256a2f92c --- /dev/null +++ b/homeassistant/components/baf/translations/ja.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "ipv6_not_supported": "IPv6\u306b\u306f\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093\u3002" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "{name} - {model} ({ip_address}) \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" + }, + "user": { + "data": { + "ip_address": "IP\u30a2\u30c9\u30ec\u30b9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/ko.json b/homeassistant/components/baf/translations/ko.json new file mode 100644 index 00000000000..a8884c0403e --- /dev/null +++ b/homeassistant/components/baf/translations/ko.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "ipv6_not_supported": "IPv6\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + }, + "flow_title": "{name} - {model} ( {ip_address} )", + "step": { + "discovery_confirm": { + "description": "{name} - {model} ({ip_address}) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/no.json b/homeassistant/components/baf/translations/no.json new file mode 100644 index 00000000000..fa65c5ca21f --- /dev/null +++ b/homeassistant/components/baf/translations/no.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert", + "ipv6_not_supported": "IPv6 st\u00f8ttes ikke." + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "unknown": "Uventet feil" + }, + "flow_title": "{name} \u2013 {model} ( {ip_address} )", + "step": { + "discovery_confirm": { + "description": "Vil du konfigurere {name} - {model} ( {ip_address} )?" + }, + "user": { + "data": { + "ip_address": "IP adresse" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/pl.json b/homeassistant/components/baf/translations/pl.json new file mode 100644 index 00000000000..5c64afd30a8 --- /dev/null +++ b/homeassistant/components/baf/translations/pl.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "ip_address": "Adres IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/zh-Hant.json b/homeassistant/components/baf/translations/zh-Hant.json new file mode 100644 index 00000000000..4d191cb6d87 --- /dev/null +++ b/homeassistant/components/baf/translations/zh-Hant.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "ipv6_not_supported": "\u4e0d\u652f\u63f4 IPv6\u3002" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} - {model} ({ip_address})\uff1f" + }, + "user": { + "data": { + "ip_address": "IP \u4f4d\u5740" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/nl.json b/homeassistant/components/derivative/translations/nl.json index 8b7cf5a9402..aa8eadc214c 100644 --- a/homeassistant/components/derivative/translations/nl.json +++ b/homeassistant/components/derivative/translations/nl.json @@ -4,19 +4,19 @@ "user": { "data": { "name": "Naam", - "round": "Precisie", - "source": "Invoer sensor", + "round": "Nauwkeurigheid", + "source": "Invoersensor", "time_window": "Tijdsvenster", "unit_prefix": "Metrisch voorvoegsel", "unit_time": "Tijdseenheid" }, "data_description": { - "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "round": "Regelt het aantal decimalen in de uitvoer.", "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide" }, "description": "Maak een sensor die de afgeleide van een sensor schat.", - "title": "Voeg afgeleide sensor toe" + "title": "Voeg afgeleidesensor toe" } } }, @@ -25,14 +25,14 @@ "init": { "data": { "name": "Naam", - "round": "Precisie", - "source": "Invoer sensor", + "round": "Nauwkeurigheid", + "source": "Invoersensor", "time_window": "Tijdsvenster", "unit_prefix": "Metrisch voorvoegsel", "unit_time": "Tijdseenheid" }, "data_description": { - "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "round": "Regelt het aantal decimalen in de uitvoer.", "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." } @@ -40,14 +40,14 @@ "options": { "data": { "name": "Naam", - "round": "Precisie", - "source": "Invoer sensor", + "round": "Nauwkeurigheid", + "source": "Invoersensor", "time_window": "Tijdsvenster", "unit_prefix": "Metrisch voorvoegsel", "unit_time": "Tijdseenheid" }, "data_description": { - "round": "Regelt het aantal decimale cijfers in de uitvoer.", + "round": "Regelt het aantal decimalen in de uitvoer.", "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." }, @@ -55,5 +55,5 @@ } } }, - "title": "Derivatieve sensor" + "title": "Afgeleide" } \ No newline at end of file diff --git a/homeassistant/components/geocaching/translations/bg.json b/homeassistant/components/geocaching/translations/bg.json new file mode 100644 index 00000000000..00c237dd077 --- /dev/null +++ b/homeassistant/components/geocaching/translations/bg.json @@ -0,0 +1,17 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "create_entry": { + "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d\u043e" + }, + "step": { + "reauth_confirm": { + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/group/translations/nl.json b/homeassistant/components/group/translations/nl.json index e670e2e853a..1343279c365 100644 --- a/homeassistant/components/group/translations/nl.json +++ b/homeassistant/components/group/translations/nl.json @@ -104,13 +104,13 @@ }, "description": "Met groepen kunt u een nieuwe entiteit cre\u00ebren die meerdere entiteiten van hetzelfde type vertegenwoordigt.", "menu_options": { - "binary_sensor": "Binaire sensorgroep", + "binary_sensor": "Binaire-sensorgroep", "cover": "Rolluikgroep", "fan": "Ventilatorgroep", "light": "Lichtgroep", - "lock": "Groep vergrendelen", + "lock": "Slotgroep", "media_player": "Mediaspelergroep", - "switch": "Groep wisselen" + "switch": "Schakelaargroep" }, "title": "Groep toevoegen" } diff --git a/homeassistant/components/hassio/translations/it.json b/homeassistant/components/hassio/translations/it.json index b601a11241d..44499d3f002 100644 --- a/homeassistant/components/hassio/translations/it.json +++ b/homeassistant/components/hassio/translations/it.json @@ -6,10 +6,10 @@ "disk_used": "Disco utilizzato", "docker_version": "Versione Docker", "healthy": "Integrit\u00e0", - "host_os": "Sistema operativo dell'host", + "host_os": "Sistema Operativo Host", "installed_addons": "Componenti aggiuntivi installati", - "supervisor_api": "API supervisore", - "supervisor_version": "Versione supervisore", + "supervisor_api": "API Supervisor", + "supervisor_version": "Versione Supervisor", "supported": "Supportato", "update_channel": "Canale di aggiornamento", "version_api": "Versione API" diff --git a/homeassistant/components/homeassistant/translations/it.json b/homeassistant/components/homeassistant/translations/it.json index b85f3072620..3052a536338 100644 --- a/homeassistant/components/homeassistant/translations/it.json +++ b/homeassistant/components/homeassistant/translations/it.json @@ -4,10 +4,10 @@ "arch": "Architettura della CPU", "dev": "Sviluppo", "docker": "Docker", - "hassio": "Supervisore", + "hassio": "Supervisor", "installation_type": "Tipo di installazione", - "os_name": "Famiglia del sistema operativo", - "os_version": "Versione del sistema operativo", + "os_name": "Famiglia del Sistema Operativo", + "os_version": "Versione del Sistema Operativo", "python_version": "Versione Python", "timezone": "Fuso orario", "user": "Utente", diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index c05bdba3944..e47d93a27a5 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -47,7 +47,7 @@ "knxkeys_password": "La password per decifrare il file `.knxkeys`" }, "data_description": { - "knxkeys_filename": "Il file dovrebbe essere trovato nella tua cartella di configurazione in `.storage/knx/`.\n Nel sistema operativo Home Assistant questo sarebbe `/config/.storage/knx/`\n Esempio: `mio_progetto.knxkeys`", + "knxkeys_filename": "Il file dovrebbe essere trovato nella tua cartella di configurazione in `.storage/knx/`.\n Nel Sistema Operativo di Home Assistant questo sarebbe `/config/.storage/knx/`\n Esempio: `mio_progetto.knxkeys`", "knxkeys_password": "Questo \u00e8 stato impostato durante l'esportazione del file da ETS." }, "description": "Inserisci le informazioni per il tuo file `.knxkeys`." diff --git a/homeassistant/components/litterrobot/translations/sensor.bg.json b/homeassistant/components/litterrobot/translations/sensor.bg.json new file mode 100644 index 00000000000..3081fb9b285 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.bg.json @@ -0,0 +1,8 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "\u0418\u0437\u043a\u043b.", + "offline": "\u041e\u0444\u043b\u0430\u0439\u043d" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.no.json b/homeassistant/components/litterrobot/translations/sensor.no.json new file mode 100644 index 00000000000..2997930e53b --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.no.json @@ -0,0 +1,28 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Panser fjernet", + "ccc": "Rengj\u00f8ringssyklus fullf\u00f8rt", + "ccp": "Rengj\u00f8ringssyklus p\u00e5g\u00e5r", + "csf": "Feil p\u00e5 kattesensor", + "csi": "Kattesensor avbrutt", + "cst": "Tidsberegning for kattesensor", + "df1": "Skuff nesten full - 2 sykluser igjen", + "df2": "Skuff nesten full - 1 syklus til venstre", + "dfs": "Skuff full", + "dhf": "Dump + Hjemmeposisjonsfeil", + "dpf": "Dumpposisjonsfeil", + "ec": "Tom syklus", + "hpf": "Hjemmeposisjonsfeil", + "off": "Av", + "offline": "Frakoblet", + "otf": "Over dreiemomentfeil", + "p": "Pauset", + "pd": "Knip gjenkjenning", + "rdy": "Klar", + "scf": "Kattesensorfeil ved oppstart", + "sdf": "Skuff full ved oppstart", + "spf": "Knipegjenkjenning ved oppstart" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.pl.json b/homeassistant/components/litterrobot/translations/sensor.pl.json new file mode 100644 index 00000000000..4f12cd1f30c --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.pl.json @@ -0,0 +1,9 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "wy\u0142.", + "offline": "Offline", + "p": "wstrzymany" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/no.json b/homeassistant/components/motion_blinds/translations/no.json index d6c4708ea8c..655e574cab9 100644 --- a/homeassistant/components/motion_blinds/translations/no.json +++ b/homeassistant/components/motion_blinds/translations/no.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert , tilkoblingsinnstillingene er oppdatert", + "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "connection_error": "Tilkobling mislyktes" }, diff --git a/homeassistant/components/season/translations/sensor.fy.json b/homeassistant/components/season/translations/sensor.fy.json new file mode 100644 index 00000000000..f0990e02749 --- /dev/null +++ b/homeassistant/components/season/translations/sensor.fy.json @@ -0,0 +1,7 @@ +{ + "state": { + "season__season__": { + "summer": "Simmer" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/bg.json b/homeassistant/components/siren/translations/bg.json new file mode 100644 index 00000000000..bd7725464a7 --- /dev/null +++ b/homeassistant/components/siren/translations/bg.json @@ -0,0 +1,3 @@ +{ + "title": "\u0421\u0438\u0440\u0435\u043d\u0430" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/ca.json b/homeassistant/components/siren/translations/ca.json new file mode 100644 index 00000000000..f3c1468be31 --- /dev/null +++ b/homeassistant/components/siren/translations/ca.json @@ -0,0 +1,3 @@ +{ + "title": "Sirena" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/de.json b/homeassistant/components/siren/translations/de.json new file mode 100644 index 00000000000..bcea16c56f1 --- /dev/null +++ b/homeassistant/components/siren/translations/de.json @@ -0,0 +1,3 @@ +{ + "title": "Sirene" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/el.json b/homeassistant/components/siren/translations/el.json new file mode 100644 index 00000000000..c5a968c2fde --- /dev/null +++ b/homeassistant/components/siren/translations/el.json @@ -0,0 +1,3 @@ +{ + "title": "\u03a3\u03b5\u03b9\u03c1\u03ae\u03bd\u03b1" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/et.json b/homeassistant/components/siren/translations/et.json new file mode 100644 index 00000000000..c0bd3d03ec7 --- /dev/null +++ b/homeassistant/components/siren/translations/et.json @@ -0,0 +1,3 @@ +{ + "title": "Sireen" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/fr.json b/homeassistant/components/siren/translations/fr.json new file mode 100644 index 00000000000..2dad0e1b3b8 --- /dev/null +++ b/homeassistant/components/siren/translations/fr.json @@ -0,0 +1,3 @@ +{ + "title": "Sir\u00e8ne" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/it.json b/homeassistant/components/siren/translations/it.json new file mode 100644 index 00000000000..f3c1468be31 --- /dev/null +++ b/homeassistant/components/siren/translations/it.json @@ -0,0 +1,3 @@ +{ + "title": "Sirena" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/ja.json b/homeassistant/components/siren/translations/ja.json new file mode 100644 index 00000000000..c83a8107246 --- /dev/null +++ b/homeassistant/components/siren/translations/ja.json @@ -0,0 +1,3 @@ +{ + "title": "\u30b5\u30a4\u30ec\u30f3" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/ko.json b/homeassistant/components/siren/translations/ko.json new file mode 100644 index 00000000000..4fe08e488ee --- /dev/null +++ b/homeassistant/components/siren/translations/ko.json @@ -0,0 +1,3 @@ +{ + "title": "\uc0ac\uc774\ub80c" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/nl.json b/homeassistant/components/siren/translations/nl.json new file mode 100644 index 00000000000..bcea16c56f1 --- /dev/null +++ b/homeassistant/components/siren/translations/nl.json @@ -0,0 +1,3 @@ +{ + "title": "Sirene" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/pt-BR.json b/homeassistant/components/siren/translations/pt-BR.json new file mode 100644 index 00000000000..bcea16c56f1 --- /dev/null +++ b/homeassistant/components/siren/translations/pt-BR.json @@ -0,0 +1,3 @@ +{ + "title": "Sirene" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/tr.json b/homeassistant/components/siren/translations/tr.json new file mode 100644 index 00000000000..549bad8914b --- /dev/null +++ b/homeassistant/components/siren/translations/tr.json @@ -0,0 +1,3 @@ +{ + "title": "Siren" +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/bg.json b/homeassistant/components/slack/translations/bg.json new file mode 100644 index 00000000000..35a5c7dd36d --- /dev/null +++ b/homeassistant/components/slack/translations/bg.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "already_configured": "\u0423\u0441\u043b\u0443\u0433\u0430\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0430" + }, + "error": { + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + }, + "step": { + "user": { + "data": { + "api_key": "API \u043a\u043b\u044e\u0447", + "default_channel": "\u041a\u0430\u043d\u0430\u043b \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435", + "icon": "\u0418\u043a\u043e\u043d\u0430", + "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/ko.json b/homeassistant/components/slack/translations/ko.json new file mode 100644 index 00000000000..ffc01a9bbcb --- /dev/null +++ b/homeassistant/components/slack/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "Slack API \ud0a4\ub97c \uac00\uc838\uc624\ub294 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc124\uba85\uc11c\ub97c \ucc38\uc870\ud558\uc2ed\uc2dc\uc624." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/no.json b/homeassistant/components/slack/translations/no.json new file mode 100644 index 00000000000..850d18ff5e0 --- /dev/null +++ b/homeassistant/components/slack/translations/no.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Tjenesten er allerede konfigurert" + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "default_channel": "Standard kanal", + "icon": "Ikon", + "username": "Brukernavn" + }, + "data_description": { + "api_key": "Slack API-tokenet som skal brukes til \u00e5 sende Slack-meldinger.", + "default_channel": "Kanalen det skal legges inn p\u00e5 hvis ingen kanal er angitt n\u00e5r du sender en melding.", + "icon": "Bruk en av Slack-emojiene som et ikon for det oppgitte brukernavnet.", + "username": "Home Assistant vil legge ut p\u00e5 Slack ved \u00e5 bruke det spesifiserte brukernavnet." + }, + "description": "Se dokumentasjonen for \u00e5 f\u00e5 Slack API-n\u00f8kkelen din." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/pl.json b/homeassistant/components/slack/translations/pl.json new file mode 100644 index 00000000000..3ce8f4bb311 --- /dev/null +++ b/homeassistant/components/slack/translations/pl.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "Us\u0142uga jest ju\u017c skonfigurowana" + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "api_key": "Klucz API", + "username": "Nazwa u\u017cytkownika" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/nl.json b/homeassistant/components/threshold/translations/nl.json index 73af8fcc8b7..c49b00eee0d 100644 --- a/homeassistant/components/threshold/translations/nl.json +++ b/homeassistant/components/threshold/translations/nl.json @@ -14,7 +14,7 @@ "upper": "Bovengrens" }, "description": "Maak een binaire sensor die aan en uit gaat afhankelijk van de waarde van een sensor\n\nAlleen ondergrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor kleiner is dan de ondergrens.\nAlleen bovengrens geconfigureerd - Schakelt in wanneer de waarde van de ingangssensor groter is dan de bovengrens.\nZowel ondergrens als bovengrens ingesteld - Schakelt in wanneer de waarde van de ingangssensor binnen het bereik [ondergrens ... bovengrens] ligt.", - "title": "Drempelsensor toevoegen" + "title": "Drempelwaardesensor toevoegen" } } }, @@ -36,5 +36,5 @@ } } }, - "title": "Drempelsensor" + "title": "Drempelwaardesensor" } \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/bg.json b/homeassistant/components/ukraine_alarm/translations/bg.json index 68cd63e86b1..181f28745b9 100644 --- a/homeassistant/components/ukraine_alarm/translations/bg.json +++ b/homeassistant/components/ukraine_alarm/translations/bg.json @@ -1,7 +1,10 @@ { "config": { "abort": { - "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" + "already_configured": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", + "max_regions": "\u041c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 5 \u0440\u0435\u0433\u0438\u043e\u043d\u0430", + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", @@ -26,7 +29,8 @@ }, "user": { "data": { - "api_key": "API \u043a\u043b\u044e\u0447" + "api_key": "API \u043a\u043b\u044e\u0447", + "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 Ukraine Alarm. \u0417\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447, \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}" } diff --git a/homeassistant/components/zha/translations/fy.json b/homeassistant/components/zha/translations/fy.json new file mode 100644 index 00000000000..790851a469e --- /dev/null +++ b/homeassistant/components/zha/translations/fy.json @@ -0,0 +1,7 @@ +{ + "device_automation": { + "trigger_subtype": { + "turn_on": "Ynskeakelje" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/it.json b/homeassistant/components/zwave_js/translations/it.json index 5922601921a..e82ccfe8476 100644 --- a/homeassistant/components/zwave_js/translations/it.json +++ b/homeassistant/components/zwave_js/translations/it.json @@ -9,7 +9,7 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "cannot_connect": "Impossibile connettersi", - "discovery_requires_supervisor": "Il rilevamento richiede il supervisore.", + "discovery_requires_supervisor": "Il rilevamento richiede il Supervisor.", "not_zwave_device": "Il dispositivo rilevato non \u00e8 un dispositivo Z-Wave." }, "error": { @@ -49,9 +49,9 @@ }, "on_supervisor": { "data": { - "use_addon": "Usa il componente aggiuntivo Z-Wave JS del supervisore" + "use_addon": "Usa il componente aggiuntivo Z-Wave JS del Supervisor" }, - "description": "Desideri utilizzare il componente aggiuntivo Z-Wave JS del supervisore?", + "description": "Desideri utilizzare il componente aggiuntivo Z-Wave JS del Supervisor?", "title": "Seleziona il metodo di connessione" }, "start_addon": { @@ -137,9 +137,9 @@ }, "on_supervisor": { "data": { - "use_addon": "Usa il componente aggiuntivo Z-Wave JS del supervisore" + "use_addon": "Usa il componente aggiuntivo Z-Wave JS di Supervisor" }, - "description": "Desideri utilizzare il componente aggiuntivo Z-Wave JS del supervisore?", + "description": "Desideri utilizzare il componente aggiuntivo Z-Wave JS del Supervisor?", "title": "Seleziona il metodo di connessione" }, "start_addon": { From 3de31939d8d7ff2626d3b52f40bd9dd0def6727b Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Mon, 16 May 2022 23:51:13 -0400 Subject: [PATCH 511/930] Refactor button code to allow for other button types for UniFi Protect (#71911) Co-authored-by: J. Nick Koston --- .../components/unifiprotect/__init__.py | 58 ++++++++++ .../components/unifiprotect/button.py | 61 ++++++---- tests/components/unifiprotect/test_button.py | 2 +- tests/components/unifiprotect/test_init.py | 105 +++++++++++++++++- 4 files changed, 205 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index 97fdc6eac20..c28f2639e00 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -17,9 +17,11 @@ from homeassistant.const import ( CONF_USERNAME, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP, + Platform, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.aiohttp_client import async_create_clientsession from .const import ( @@ -27,6 +29,7 @@ from .const import ( CONF_OVERRIDE_CHOST, DEFAULT_SCAN_INTERVAL, DEVICES_FOR_SUBSCRIBE, + DEVICES_THAT_ADOPT, DOMAIN, MIN_REQUIRED_PROTECT_V, OUTDATED_LOG_MESSAGE, @@ -41,6 +44,60 @@ _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=DEFAULT_SCAN_INTERVAL) +async def _async_migrate_data( + hass: HomeAssistant, entry: ConfigEntry, protect: ProtectApiClient +) -> None: + + registry = er.async_get(hass) + to_migrate = [] + for entity in er.async_entries_for_config_entry(registry, entry.entry_id): + if entity.domain == Platform.BUTTON and "_" not in entity.unique_id: + _LOGGER.debug("Button %s needs migration", entity.entity_id) + to_migrate.append(entity) + + if len(to_migrate) == 0: + _LOGGER.debug("No entities need migration") + return + + _LOGGER.info("Migrating %s reboot button entities ", len(to_migrate)) + bootstrap = await protect.get_bootstrap() + count = 0 + for button in to_migrate: + device = None + for model in DEVICES_THAT_ADOPT: + attr = f"{model.value}s" + device = getattr(bootstrap, attr).get(button.unique_id) + if device is not None: + break + + if device is None: + continue + + new_unique_id = f"{device.id}_reboot" + _LOGGER.debug( + "Migrating entity %s (old unique_id: %s, new unique_id: %s)", + button.entity_id, + button.unique_id, + new_unique_id, + ) + try: + registry.async_update_entity(button.entity_id, new_unique_id=new_unique_id) + except ValueError: + _LOGGER.warning( + "Could not migrate entity %s (old unique_id: %s, new unique_id: %s)", + button.entity_id, + button.unique_id, + new_unique_id, + ) + else: + count += 1 + + if count < len(to_migrate): + _LOGGER.warning("Failed to migate %s reboot buttons", len(to_migrate) - count) + else: + _LOGGER.info("Migrated %s reboot button entities", count) + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the UniFi Protect config entries.""" @@ -75,6 +132,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) return False + await _async_migrate_data(hass, entry, protect) if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=nvr_info.mac) diff --git a/homeassistant/components/unifiprotect/button.py b/homeassistant/components/unifiprotect/button.py index 3940c85d21a..731b1eaf86a 100644 --- a/homeassistant/components/unifiprotect/button.py +++ b/homeassistant/components/unifiprotect/button.py @@ -1,20 +1,47 @@ """Support for Ubiquiti's UniFi Protect NVR.""" from __future__ import annotations -import logging +from dataclasses import dataclass +from typing import Final from pyunifiprotect.data.base import ProtectAdoptableDeviceModel -from homeassistant.components.button import ButtonDeviceClass, ButtonEntity +from homeassistant.components.button import ( + ButtonDeviceClass, + ButtonEntity, + ButtonEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DEVICES_THAT_ADOPT, DOMAIN +from .const import DOMAIN from .data import ProtectData -from .entity import ProtectDeviceEntity +from .entity import ProtectDeviceEntity, async_all_device_entities +from .models import ProtectSetableKeysMixin, T -_LOGGER = logging.getLogger(__name__) + +@dataclass +class ProtectButtonEntityDescription( + ProtectSetableKeysMixin[T], ButtonEntityDescription +): + """Describes UniFi Protect Button entity.""" + + ufp_press: str | None = None + + +DEVICE_CLASS_CHIME_BUTTON: Final = "unifiprotect__chime_button" + + +ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = ( + ProtectButtonEntityDescription( + key="reboot", + entity_registry_enabled_default=False, + device_class=ButtonDeviceClass.RESTART, + name="Reboot Device", + ufp_press="reboot", + ), +) async def async_setup_entry( @@ -25,34 +52,30 @@ async def async_setup_entry( """Discover devices on a UniFi Protect NVR.""" data: ProtectData = hass.data[DOMAIN][entry.entry_id] - async_add_entities( - [ - ProtectButton( - data, - device, - ) - for device in data.get_by_types(DEVICES_THAT_ADOPT) - ] + entities: list[ProtectDeviceEntity] = async_all_device_entities( + data, ProtectButton, all_descs=ALL_DEVICE_BUTTONS ) + async_add_entities(entities) + class ProtectButton(ProtectDeviceEntity, ButtonEntity): """A Ubiquiti UniFi Protect Reboot button.""" - _attr_entity_registry_enabled_default = False - _attr_device_class = ButtonDeviceClass.RESTART + entity_description: ProtectButtonEntityDescription def __init__( self, data: ProtectData, device: ProtectAdoptableDeviceModel, + description: ProtectButtonEntityDescription, ) -> None: """Initialize an UniFi camera.""" - super().__init__(data, device) - self._attr_name = f"{self.device.name} Reboot Device" + super().__init__(data, device, description) + self._attr_name = f"{self.device.name} {self.entity_description.name}" async def async_press(self) -> None: """Press the button.""" - _LOGGER.debug("Rebooting %s with id %s", self.device.model, self.device.id) - await self.device.reboot() + if self.entity_description.ufp_press is not None: + await getattr(self.device, self.entity_description.ufp_press)() diff --git a/tests/components/unifiprotect/test_button.py b/tests/components/unifiprotect/test_button.py index 0064781c6ce..64677dd1d77 100644 --- a/tests/components/unifiprotect/test_button.py +++ b/tests/components/unifiprotect/test_button.py @@ -49,7 +49,7 @@ async def test_button( mock_entry.api.reboot_device = AsyncMock() - unique_id = f"{camera[0].id}" + unique_id = f"{camera[0].id}_reboot" entity_id = camera[1] entity_registry = er.async_get(hass) diff --git a/tests/components/unifiprotect/test_init.py b/tests/components/unifiprotect/test_init.py index 77bf900d87e..53588984e25 100644 --- a/tests/components/unifiprotect/test_init.py +++ b/tests/components/unifiprotect/test_init.py @@ -1,14 +1,17 @@ """Test the UniFi Protect setup flow.""" +# pylint: disable=protected-access from __future__ import annotations from unittest.mock import AsyncMock, patch from pyunifiprotect import NotAuthorized, NvrError -from pyunifiprotect.data import NVR +from pyunifiprotect.data import NVR, Light from homeassistant.components.unifiprotect.const import CONF_DISABLE_RTSP, DOMAIN from homeassistant.config_entries import ConfigEntry, ConfigEntryState +from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from . import _patch_discovery from .conftest import MockBootstrap, MockEntityFixture @@ -175,3 +178,103 @@ async def test_setup_starts_discovery( assert mock_entry.entry.state == ConfigEntryState.LOADED await hass.async_block_till_done() assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1 + + +async def test_migrate_reboot_button( + hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light +): + """Test migrating unique ID of reboot button.""" + + light1 = mock_light.copy() + light1._api = mock_entry.api + light1.name = "Test Light 1" + light1.id = "lightid1" + + light2 = mock_light.copy() + light2._api = mock_entry.api + light2.name = "Test Light 2" + light2.id = "lightid2" + mock_entry.api.bootstrap.lights = { + light1.id: light1, + light2.id: light2, + } + mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap) + + registry = er.async_get(hass) + registry.async_get_or_create( + Platform.BUTTON, Platform.BUTTON, light1.id, config_entry=mock_entry.entry + ) + registry.async_get_or_create( + Platform.BUTTON, + Platform.BUTTON, + f"{light2.id}_reboot", + config_entry=mock_entry.entry, + ) + + await hass.config_entries.async_setup(mock_entry.entry.entry_id) + await hass.async_block_till_done() + + assert mock_entry.entry.state == ConfigEntryState.LOADED + assert mock_entry.api.update.called + assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac + + assert registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device_2") is None + light = registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device") + assert light is not None + assert light.unique_id == f"{light1.id}_reboot" + + assert registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device_2") is None + light = registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device") + assert light is not None + assert light.unique_id == f"{light2.id}_reboot" + + buttons = [] + for entity in er.async_entries_for_config_entry( + registry, mock_entry.entry.entry_id + ): + if entity.platform == Platform.BUTTON.value: + buttons.append(entity) + assert len(buttons) == 2 + + +async def test_migrate_reboot_button_fail( + hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light +): + """Test migrating unique ID of reboot button.""" + + light1 = mock_light.copy() + light1._api = mock_entry.api + light1.name = "Test Light 1" + light1.id = "lightid1" + + mock_entry.api.bootstrap.lights = { + light1.id: light1, + } + mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap) + + registry = er.async_get(hass) + registry.async_get_or_create( + Platform.BUTTON, + Platform.BUTTON, + light1.id, + config_entry=mock_entry.entry, + suggested_object_id=light1.name, + ) + registry.async_get_or_create( + Platform.BUTTON, + Platform.BUTTON, + f"{light1.id}_reboot", + config_entry=mock_entry.entry, + suggested_object_id=light1.name, + ) + + await hass.config_entries.async_setup(mock_entry.entry.entry_id) + await hass.async_block_till_done() + + assert mock_entry.entry.state == ConfigEntryState.LOADED + assert mock_entry.api.update.called + assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac + + light = registry.async_get(f"{Platform.BUTTON}.test_light_1") + assert light is not None + assert light.unique_id == f"{light1.id}" From 0608506bac7a57be28fd274cfaaba8785fbbf96f Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 17 May 2022 11:52:48 +0800 Subject: [PATCH 512/930] Reuse codec_context on stream thread restart (#71942) --- homeassistant/components/stream/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/stream/core.py b/homeassistant/components/stream/core.py index 8db6a239818..8c0b867752e 100644 --- a/homeassistant/components/stream/core.py +++ b/homeassistant/components/stream/core.py @@ -395,6 +395,9 @@ class KeyFrameConverter: This is run by the worker thread and will only be called once per worker. """ + if self._codec_context: + return + # Keep import here so that we can import stream integration without installing reqs # pylint: disable=import-outside-toplevel from av import CodecContext From 99f68ab85848cd89ee2aae0a7570d06bf9ee02ed Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 06:04:59 +0200 Subject: [PATCH 513/930] Update dsmr_parser to v0.33 (#71946) --- homeassistant/components/dsmr/const.py | 2 +- homeassistant/components/dsmr/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/dsmr/test_sensor.py | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/dsmr/const.py b/homeassistant/components/dsmr/const.py index 2533aa8d025..43c0e66e945 100644 --- a/homeassistant/components/dsmr/const.py +++ b/homeassistant/components/dsmr/const.py @@ -271,7 +271,7 @@ SENSORS: tuple[DSMRSensorEntityDescription, ...] = ( state_class=SensorStateClass.TOTAL_INCREASING, ), DSMRSensorEntityDescription( - key=obis_references.BELGIUM_HOURLY_GAS_METER_READING, + key=obis_references.BELGIUM_5MIN_GAS_METER_READING, name="Gas Consumption", dsmr_versions={"5B"}, is_gas=True, diff --git a/homeassistant/components/dsmr/manifest.json b/homeassistant/components/dsmr/manifest.json index e15a7c3b80a..b086944a4d2 100644 --- a/homeassistant/components/dsmr/manifest.json +++ b/homeassistant/components/dsmr/manifest.json @@ -2,7 +2,7 @@ "domain": "dsmr", "name": "DSMR Slimme Meter", "documentation": "https://www.home-assistant.io/integrations/dsmr", - "requirements": ["dsmr_parser==0.32"], + "requirements": ["dsmr_parser==0.33"], "codeowners": ["@Robbie1221", "@frenck"], "config_flow": true, "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 027b6406b2a..a7c59f1853d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -560,7 +560,7 @@ doorbirdpy==2.1.0 dovado==0.4.1 # homeassistant.components.dsmr -dsmr_parser==0.32 +dsmr_parser==0.33 # homeassistant.components.dwd_weather_warnings dwdwfsapi==1.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c9949ba3b2c..f9926797e6f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -415,7 +415,7 @@ discovery30303==0.2.1 doorbirdpy==2.1.0 # homeassistant.components.dsmr -dsmr_parser==0.32 +dsmr_parser==0.33 # homeassistant.components.dynalite dynalite_devices==0.1.46 diff --git a/tests/components/dsmr/test_sensor.py b/tests/components/dsmr/test_sensor.py index 93dd78034cc..4502f61586a 100644 --- a/tests/components/dsmr/test_sensor.py +++ b/tests/components/dsmr/test_sensor.py @@ -393,7 +393,7 @@ async def test_belgian_meter(hass, dsmr_connection_fixture): (connection_factory, transport, protocol) = dsmr_connection_fixture from dsmr_parser.obis_references import ( - BELGIUM_HOURLY_GAS_METER_READING, + BELGIUM_5MIN_GAS_METER_READING, ELECTRICITY_ACTIVE_TARIFF, ) from dsmr_parser.objects import CosemObject, MBusObject @@ -411,7 +411,7 @@ async def test_belgian_meter(hass, dsmr_connection_fixture): } telegram = { - BELGIUM_HOURLY_GAS_METER_READING: MBusObject( + BELGIUM_5MIN_GAS_METER_READING: MBusObject( [ {"value": datetime.datetime.fromtimestamp(1551642213)}, {"value": Decimal(745.695), "unit": "m3"}, From ddecf76f6f301f0240c012ed73adf3699ec2d6a5 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 16 May 2022 21:09:48 -0700 Subject: [PATCH 514/930] Add application_credentials platform for netatmo (#71884) --- homeassistant/components/netatmo/__init__.py | 43 +++++++++++-------- .../netatmo/application_credentials.py | 14 ++++++ .../components/netatmo/manifest.json | 2 +- .../generated/application_credentials.py | 1 + 4 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 homeassistant/components/netatmo/application_credentials.py diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index f6e43b29653..a3a9d22e017 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -11,6 +11,10 @@ import pyatmo import voluptuous as vol from homeassistant.components import cloud +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.webhook import ( async_generate_url as webhook_generate_url, async_register as webhook_register, @@ -35,7 +39,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later from homeassistant.helpers.typing import ConfigType -from . import api, config_flow +from . import api from .const import ( AUTH, CONF_CLOUDHOOK_URL, @@ -48,8 +52,6 @@ from .const import ( DATA_SCHEDULES, DOMAIN, NETATMO_SCOPES, - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, PLATFORMS, WEBHOOK_DEACTIVATION, WEBHOOK_PUSH_TYPE, @@ -60,14 +62,17 @@ from .webhook import async_handle_webhook _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -88,17 +93,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in config: return True - config_flow.NetatmoFlowHandler.async_register_implementation( + await async_import_client_credential( hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, + DOMAIN, + ClientCredential( config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, ), ) + _LOGGER.warning( + "Configuration of Netatmo integration in YAML is deprecated and " + "will be removed in a future release; Your existing configuration " + "(including OAuth Application Credentials) have been imported into " + "the UI automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/netatmo/application_credentials.py b/homeassistant/components/netatmo/application_credentials.py new file mode 100644 index 00000000000..5536343ebe6 --- /dev/null +++ b/homeassistant/components/netatmo/application_credentials.py @@ -0,0 +1,14 @@ +"""Application credentials platform for Netatmo.""" + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + +from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ) diff --git a/homeassistant/components/netatmo/manifest.json b/homeassistant/components/netatmo/manifest.json index 565bd42594a..2081f9bd274 100644 --- a/homeassistant/components/netatmo/manifest.json +++ b/homeassistant/components/netatmo/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/netatmo", "requirements": ["pyatmo==6.2.4"], "after_dependencies": ["cloud", "media_source"], - "dependencies": ["auth", "webhook"], + "dependencies": ["application_credentials", "webhook"], "codeowners": ["@cgtobi"], "config_flow": true, "homekit": { diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 56340ccc44e..40f451de153 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -8,6 +8,7 @@ To update, run python3 -m script.hassfest APPLICATION_CREDENTIALS = [ "geocaching", "google", + "netatmo", "spotify", "xbox" ] From 78f0716574dbb7f6619b570489f9d34e939a4031 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 May 2022 01:22:56 -0500 Subject: [PATCH 515/930] Add support for specifying the integrations manifest/list fetches (#71982) * Add support for specifying the integrations manifest/list fetches See https://github.com/home-assistant/core/pull/71979 for the motovation * Update homeassistant/components/websocket_api/commands.py Co-authored-by: Paulus Schoutsen Co-authored-by: Paulus Schoutsen --- .../components/websocket_api/commands.py | 10 +++++++--- .../components/websocket_api/test_commands.py | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index edec628fd2c..61bcb8badf0 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -359,15 +359,19 @@ def handle_get_config( connection.send_result(msg["id"], hass.config.as_dict()) -@decorators.websocket_command({vol.Required("type"): "manifest/list"}) +@decorators.websocket_command( + {vol.Required("type"): "manifest/list", vol.Optional("integrations"): [str]} +) @decorators.async_response async def handle_manifest_list( hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any] ) -> None: """Handle integrations command.""" - loaded_integrations = async_get_loaded_integrations(hass) + wanted_integrations = msg.get("integrations") + if wanted_integrations is None: + wanted_integrations = async_get_loaded_integrations(hass) integrations = await asyncio.gather( - *(async_get_integration(hass, domain) for domain in loaded_integrations) + *(async_get_integration(hass, domain) for domain in wanted_integrations) ) connection.send_result( msg["id"], [integration.manifest for integration in integrations] diff --git a/tests/components/websocket_api/test_commands.py b/tests/components/websocket_api/test_commands.py index 007e130ff6f..4d3302f7c13 100644 --- a/tests/components/websocket_api/test_commands.py +++ b/tests/components/websocket_api/test_commands.py @@ -1351,6 +1351,25 @@ async def test_manifest_list(hass, websocket_client): ] +async def test_manifest_list_specific_integrations(hass, websocket_client): + """Test loading manifests for specific integrations.""" + websocket_api = await async_get_integration(hass, "websocket_api") + + await websocket_client.send_json( + {"id": 5, "type": "manifest/list", "integrations": ["hue", "websocket_api"]} + ) + hue = await async_get_integration(hass, "hue") + + msg = await websocket_client.receive_json() + assert msg["id"] == 5 + assert msg["type"] == const.TYPE_RESULT + assert msg["success"] + assert sorted(msg["result"], key=lambda manifest: manifest["domain"]) == [ + hue.manifest, + websocket_api.manifest, + ] + + async def test_manifest_get(hass, websocket_client): """Test getting a manifest.""" hue = await async_get_integration(hass, "hue") From a614ddca287c8a7ebf2a2c0336bfb5a8b55d20ed Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 May 2022 01:23:11 -0500 Subject: [PATCH 516/930] Support requesting translations for multiple integrations in a single request (#71979) --- homeassistant/components/frontend/__init__.py | 2 +- homeassistant/components/onboarding/views.py | 5 +- homeassistant/helpers/translation.py | 13 +++-- tests/components/frontend/test_init.py | 54 ++++++++++++++++++- tests/helpers/test_translation.py | 29 +++++++--- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 0c540a477ef..91b9c9a126f 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -667,7 +667,7 @@ def websocket_get_themes( "type": "frontend/get_translations", vol.Required("language"): str, vol.Required("category"): str, - vol.Optional("integration"): str, + vol.Optional("integration"): vol.All(cv.ensure_list, [str]), vol.Optional("config_flow"): bool, } ) diff --git a/homeassistant/components/onboarding/views.py b/homeassistant/components/onboarding/views.py index b277bd97edf..07b3dfa9cc2 100644 --- a/homeassistant/components/onboarding/views.py +++ b/homeassistant/components/onboarding/views.py @@ -15,6 +15,7 @@ from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView from homeassistant.core import callback from homeassistant.helpers.system_info import async_get_system_info +from homeassistant.helpers.translation import async_get_translations from .const import ( DEFAULT_AREAS, @@ -147,8 +148,8 @@ class UserOnboardingView(_BaseOnboardingView): await person.async_create_person(hass, data["name"], user_id=user.id) # Create default areas using the users supplied language. - translations = await hass.helpers.translation.async_get_translations( - data["language"], "area", DOMAIN + translations = await async_get_translations( + hass, data["language"], "area", {DOMAIN} ) area_registry = await hass.helpers.area_registry.async_get_registry() diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 976c66dda56..cda50de535b 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from collections import ChainMap -from collections.abc import Mapping +from collections.abc import Iterable, Mapping import logging from typing import Any @@ -286,7 +286,7 @@ async def async_get_translations( hass: HomeAssistant, language: str, category: str, - integration: str | None = None, + integrations: Iterable[str] | None = None, config_flow: bool | None = None, ) -> dict[str, Any]: """Return all backend translations. @@ -297,8 +297,8 @@ async def async_get_translations( """ lock = hass.data.setdefault(TRANSLATION_LOAD_LOCK, asyncio.Lock()) - if integration is not None: - components = {integration} + if integrations is not None: + components = set(integrations) elif config_flow: components = (await async_get_config_flows(hass)) - hass.config.components elif category == "state": @@ -310,7 +310,10 @@ async def async_get_translations( } async with lock: - cache = hass.data.setdefault(TRANSLATION_FLATTEN_CACHE, _TranslationCache(hass)) + if TRANSLATION_FLATTEN_CACHE in hass.data: + cache = hass.data[TRANSLATION_FLATTEN_CACHE] + else: + cache = hass.data[TRANSLATION_FLATTEN_CACHE] = _TranslationCache(hass) cached = await cache.async_fetch(language, category, components) return dict(ChainMap(*cached)) diff --git a/tests/components/frontend/test_init.py b/tests/components/frontend/test_init.py index 8c8fd3bf671..84ca04df3ba 100644 --- a/tests/components/frontend/test_init.py +++ b/tests/components/frontend/test_init.py @@ -424,7 +424,7 @@ async def test_get_translations(hass, ws_client): """Test get_translations command.""" with patch( "homeassistant.components.frontend.async_get_translations", - side_effect=lambda hass, lang, category, integration, config_flow: { + side_effect=lambda hass, lang, category, integrations, config_flow: { "lang": lang }, ): @@ -444,6 +444,58 @@ async def test_get_translations(hass, ws_client): assert msg["result"] == {"resources": {"lang": "nl"}} +async def test_get_translations_for_integrations(hass, ws_client): + """Test get_translations for integrations command.""" + with patch( + "homeassistant.components.frontend.async_get_translations", + side_effect=lambda hass, lang, category, integration, config_flow: { + "lang": lang, + "integration": integration, + }, + ): + await ws_client.send_json( + { + "id": 5, + "type": "frontend/get_translations", + "integration": ["frontend", "http"], + "language": "nl", + "category": "lang", + } + ) + msg = await ws_client.receive_json() + + assert msg["id"] == 5 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + assert set(msg["result"]["resources"]["integration"]) == {"frontend", "http"} + + +async def test_get_translations_for_single_integration(hass, ws_client): + """Test get_translations for integration command.""" + with patch( + "homeassistant.components.frontend.async_get_translations", + side_effect=lambda hass, lang, category, integrations, config_flow: { + "lang": lang, + "integration": integrations, + }, + ): + await ws_client.send_json( + { + "id": 5, + "type": "frontend/get_translations", + "integration": "http", + "language": "nl", + "category": "lang", + } + ) + msg = await ws_client.receive_json() + + assert msg["id"] == 5 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + assert msg["result"] == {"resources": {"lang": "nl", "integration": ["http"]}} + + async def test_auth_load(hass): """Test auth component loaded by default.""" frontend = await async_get_integration(hass, "frontend") diff --git a/tests/helpers/test_translation.py b/tests/helpers/test_translation.py index 5520269ca9d..2e30a649a7b 100644 --- a/tests/helpers/test_translation.py +++ b/tests/helpers/test_translation.py @@ -290,12 +290,29 @@ async def test_translation_merging_loaded_apart(hass, caplog): assert "component.sensor.state.moon__phase.first_quarter" in translations translations = await translation.async_get_translations( - hass, "en", "state", integration="sensor" + hass, "en", "state", integrations={"sensor"} ) assert "component.sensor.state.moon__phase.first_quarter" in translations +async def test_translation_merging_loaded_together(hass, caplog): + """Test we merge translations of two integrations when they are loaded at the same time.""" + hass.config.components.add("hue") + hass.config.components.add("homekit") + hue_translations = await translation.async_get_translations( + hass, "en", "config", integrations={"hue"} + ) + homekit_translations = await translation.async_get_translations( + hass, "en", "config", integrations={"homekit"} + ) + + translations = await translation.async_get_translations( + hass, "en", "config", integrations={"hue", "homekit"} + ) + assert translations == hue_translations | homekit_translations + + async def test_caching(hass): """Test we cache data.""" hass.config.components.add("sensor") @@ -320,14 +337,14 @@ async def test_caching(hass): ) load_sensor_only = await translation.async_get_translations( - hass, "en", "state", integration="sensor" + hass, "en", "state", integrations={"sensor"} ) assert load_sensor_only for key in load_sensor_only: assert key.startswith("component.sensor.state.") load_light_only = await translation.async_get_translations( - hass, "en", "state", integration="light" + hass, "en", "state", integrations={"light"} ) assert load_light_only for key in load_light_only: @@ -341,7 +358,7 @@ async def test_caching(hass): side_effect=translation._build_resources, ) as mock_build: load_sensor_only = await translation.async_get_translations( - hass, "en", "title", integration="sensor" + hass, "en", "title", integrations={"sensor"} ) assert load_sensor_only for key in load_sensor_only: @@ -349,12 +366,12 @@ async def test_caching(hass): assert len(mock_build.mock_calls) == 0 assert await translation.async_get_translations( - hass, "en", "title", integration="sensor" + hass, "en", "title", integrations={"sensor"} ) assert len(mock_build.mock_calls) == 0 load_light_only = await translation.async_get_translations( - hass, "en", "title", integration="media_player" + hass, "en", "title", integrations={"media_player"} ) assert load_light_only for key in load_light_only: From 513e276bbaba4c1d750cad4253db5c79fb341ec5 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 17 May 2022 08:32:28 +0200 Subject: [PATCH 517/930] Avoid polling fjaraskupan if no broadcast is received (#71969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Switch to subclassed coordinator * Avoid polling fjäråskupan only do on request We still want to timeout the availability if no data was received. * Remove unused variable * Update homeassistant/components/fjaraskupan/__init__.py Co-authored-by: Paulus Schoutsen Co-authored-by: Paulus Schoutsen --- .../components/fjaraskupan/__init__.py | 91 ++++++++++++------- .../components/fjaraskupan/binary_sensor.py | 21 ++--- homeassistant/components/fjaraskupan/fan.py | 17 ++-- homeassistant/components/fjaraskupan/light.py | 21 ++--- .../components/fjaraskupan/number.py | 19 ++-- .../components/fjaraskupan/sensor.py | 21 ++--- 6 files changed, 92 insertions(+), 98 deletions(-) diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 64962a746f7..488139b080b 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -20,7 +20,7 @@ from homeassistant.helpers.dispatcher import ( ) from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DISPATCH_DETECTION, DOMAIN @@ -35,13 +35,48 @@ PLATFORMS = [ _LOGGER = logging.getLogger(__name__) -@dataclass -class DeviceState: - """Store state of a device.""" +class Coordinator(DataUpdateCoordinator[State]): + """Update coordinator for each device.""" - device: Device - coordinator: DataUpdateCoordinator[State] - device_info: DeviceInfo + def __init__( + self, hass: HomeAssistant, device: Device, device_info: DeviceInfo + ) -> None: + """Initialize the coordinator.""" + self.device = device + self.device_info = device_info + self._refresh_was_scheduled = False + + super().__init__( + hass, _LOGGER, name="Fjäråskupan", update_interval=timedelta(seconds=120) + ) + + async def _async_refresh( + self, + log_failures: bool = True, + raise_on_auth_failed: bool = False, + scheduled: bool = False, + ) -> None: + self._refresh_was_scheduled = scheduled + await super()._async_refresh( + log_failures=log_failures, + raise_on_auth_failed=raise_on_auth_failed, + scheduled=scheduled, + ) + + async def _async_update_data(self) -> State: + """Handle an explicit update request.""" + if self._refresh_was_scheduled: + raise UpdateFailed("No data received within schedule.") + + await self.device.update() + return self.device.state + + def detection_callback( + self, ble_device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Handle a new announcement of data.""" + self.device.detection_callback(ble_device, advertisement_data) + self.async_set_updated_data(self.device.state) @dataclass @@ -49,7 +84,7 @@ class EntryState: """Store state of config entry.""" scanner: BleakScanner - devices: dict[str, DeviceState] + coordinators: dict[str, Coordinator] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -64,13 +99,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def detection_callback( ble_device: BLEDevice, advertisement_data: AdvertisementData ) -> None: - if data := state.devices.get(ble_device.address): + if data := state.coordinators.get(ble_device.address): _LOGGER.debug( "Update: %s %s - %s", ble_device.name, ble_device, advertisement_data ) - data.device.detection_callback(ble_device, advertisement_data) - data.coordinator.async_set_updated_data(data.device.state) + data.detection_callback(ble_device, advertisement_data) else: if not device_filter(ble_device, advertisement_data): return @@ -80,31 +114,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) device = Device(ble_device) - device.detection_callback(ble_device, advertisement_data) - - async def async_update_data(): - """Handle an explicit update request.""" - await device.update() - return device.state - - coordinator: DataUpdateCoordinator[State] = DataUpdateCoordinator( - hass, - logger=_LOGGER, - name="Fjaraskupan Updater", - update_interval=timedelta(seconds=120), - update_method=async_update_data, - ) - coordinator.async_set_updated_data(device.state) - device_info = DeviceInfo( identifiers={(DOMAIN, ble_device.address)}, manufacturer="Fjäråskupan", name="Fjäråskupan", ) - device_state = DeviceState(device, coordinator, device_info) - state.devices[ble_device.address] = device_state + + coordinator: Coordinator = Coordinator(hass, device, device_info) + coordinator.detection_callback(ble_device, advertisement_data) + + state.coordinators[ble_device.address] = coordinator async_dispatcher_send( - hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state + hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", coordinator ) scanner.register_detection_callback(detection_callback) @@ -119,20 +140,20 @@ def async_setup_entry_platform( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, - constructor: Callable[[DeviceState], list[Entity]], + constructor: Callable[[Coordinator], list[Entity]], ) -> None: """Set up a platform with added entities.""" entry_state: EntryState = hass.data[DOMAIN][entry.entry_id] async_add_entities( entity - for device_state in entry_state.devices.values() - for entity in constructor(device_state) + for coordinator in entry_state.coordinators.values() + for entity in constructor(coordinator) ) @callback - def _detection(device_state: DeviceState) -> None: - async_add_entities(constructor(device_state)) + def _detection(coordinator: Coordinator) -> None: + async_add_entities(constructor(coordinator)) entry.async_on_unload( async_dispatcher_connect( diff --git a/homeassistant/components/fjaraskupan/binary_sensor.py b/homeassistant/components/fjaraskupan/binary_sensor.py index f1672530c45..ef4f64c5ecd 100644 --- a/homeassistant/components/fjaraskupan/binary_sensor.py +++ b/homeassistant/components/fjaraskupan/binary_sensor.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -15,12 +15,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform @dataclass @@ -53,12 +50,12 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: + def _constructor(coordinator: Coordinator) -> list[Entity]: return [ BinarySensor( - device_state.coordinator, - device_state.device, - device_state.device_info, + coordinator, + coordinator.device, + coordinator.device_info, entity_description, ) for entity_description in SENSORS @@ -67,14 +64,14 @@ async def async_setup_entry( async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class BinarySensor(CoordinatorEntity[DataUpdateCoordinator[State]], BinarySensorEntity): +class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity): """Grease filter sensor.""" entity_description: EntityDescription def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, entity_description: EntityDescription, diff --git a/homeassistant/components/fjaraskupan/fan.py b/homeassistant/components/fjaraskupan/fan.py index a8f8e13f3da..fcd95090400 100644 --- a/homeassistant/components/fjaraskupan/fan.py +++ b/homeassistant/components/fjaraskupan/fan.py @@ -16,16 +16,13 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util.percentage import ( ordered_list_item_to_percentage, percentage_to_ordered_list_item, ) -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform ORDERED_NAMED_FAN_SPEEDS = ["1", "2", "3", "4", "5", "6", "7", "8"] @@ -58,22 +55,20 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState): - return [ - Fan(device_state.coordinator, device_state.device, device_state.device_info) - ] + def _constructor(coordinator: Coordinator): + return [Fan(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Fan(CoordinatorEntity[DataUpdateCoordinator[State]], FanEntity): +class Fan(CoordinatorEntity[Coordinator], FanEntity): """Fan entity.""" _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/light.py b/homeassistant/components/fjaraskupan/light.py index 6c3e6e458f8..9d52c5cac82 100644 --- a/homeassistant/components/fjaraskupan/light.py +++ b/homeassistant/components/fjaraskupan/light.py @@ -1,19 +1,16 @@ """Support for lights.""" from __future__ import annotations -from fjaraskupan import COMMAND_LIGHT_ON_OFF, Device, State +from fjaraskupan import COMMAND_LIGHT_ON_OFF, Device from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -23,22 +20,18 @@ async def async_setup_entry( ) -> None: """Set up tuya sensors dynamically through tuya discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: - return [ - Light( - device_state.coordinator, device_state.device, device_state.device_info - ) - ] + def _constructor(coordinator: Coordinator) -> list[Entity]: + return [Light(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Light(CoordinatorEntity[DataUpdateCoordinator[State]], LightEntity): +class Light(CoordinatorEntity[Coordinator], LightEntity): """Light device.""" def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/number.py b/homeassistant/components/fjaraskupan/number.py index bbde9bd8898..6314b9c9cc1 100644 --- a/homeassistant/components/fjaraskupan/number.py +++ b/homeassistant/components/fjaraskupan/number.py @@ -1,7 +1,7 @@ """Support for sensors.""" from __future__ import annotations -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry @@ -9,12 +9,9 @@ from homeassistant.const import TIME_MINUTES from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -24,19 +21,17 @@ async def async_setup_entry( ) -> None: """Set up number entities dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: + def _constructor(coordinator: Coordinator) -> list[Entity]: return [ PeriodicVentingTime( - device_state.coordinator, device_state.device, device_state.device_info + coordinator, coordinator.device, coordinator.device_info ), ] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class PeriodicVentingTime( - CoordinatorEntity[DataUpdateCoordinator[State]], NumberEntity -): +class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): """Periodic Venting.""" _attr_max_value: float = 59 @@ -47,7 +42,7 @@ class PeriodicVentingTime( def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/sensor.py b/homeassistant/components/fjaraskupan/sensor.py index fbd9d5f6d08..e4dbffab38e 100644 --- a/homeassistant/components/fjaraskupan/sensor.py +++ b/homeassistant/components/fjaraskupan/sensor.py @@ -1,7 +1,7 @@ """Support for sensors.""" from __future__ import annotations -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.sensor import ( SensorDeviceClass, @@ -14,12 +14,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -29,22 +26,18 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: - return [ - RssiSensor( - device_state.coordinator, device_state.device, device_state.device_info - ) - ] + def _constructor(coordinator: Coordinator) -> list[Entity]: + return [RssiSensor(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class RssiSensor(CoordinatorEntity[DataUpdateCoordinator[State]], SensorEntity): +class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity): """Sensor device.""" def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: From e0154d6fb1983a9d3156c8cb410a2eff8b61c4dd Mon Sep 17 00:00:00 2001 From: Matrix Date: Tue, 17 May 2022 15:59:39 +0800 Subject: [PATCH 518/930] Add YoLink product integration (#69167) * add yolink integration with door sensor * Add test flow and update .coveragerc * Yolink integration Bug fix * resovle test flow * issues resolve * issues resolve * issues resolve * resolve issues * issues resolve * issues resolve * change .coveragerc and test_flow * Update yolink api version * add test for config entry * change config flow and add test cases * remove config entry data * Add token check for re-auth test flow * fix test flow issues * Add application credentials * Add alias for application_credentials * support application credentials and cloud account linking * fix suggest change --- .coveragerc | 6 + CODEOWNERS | 2 + homeassistant/components/yolink/__init__.py | 64 ++++++ homeassistant/components/yolink/api.py | 30 +++ .../yolink/application_credentials.py | 14 ++ .../components/yolink/config_flow.py | 63 ++++++ homeassistant/components/yolink/const.py | 16 ++ .../components/yolink/coordinator.py | 109 +++++++++ homeassistant/components/yolink/entity.py | 52 +++++ homeassistant/components/yolink/manifest.json | 10 + homeassistant/components/yolink/sensor.py | 104 +++++++++ homeassistant/components/yolink/strings.json | 25 +++ .../components/yolink/translations/en.json | 25 +++ .../generated/application_credentials.py | 3 +- homeassistant/generated/config_flows.py | 1 + requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/yolink/__init__.py | 1 + tests/components/yolink/test_config_flow.py | 210 ++++++++++++++++++ 19 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/yolink/__init__.py create mode 100644 homeassistant/components/yolink/api.py create mode 100644 homeassistant/components/yolink/application_credentials.py create mode 100644 homeassistant/components/yolink/config_flow.py create mode 100644 homeassistant/components/yolink/const.py create mode 100644 homeassistant/components/yolink/coordinator.py create mode 100644 homeassistant/components/yolink/entity.py create mode 100644 homeassistant/components/yolink/manifest.json create mode 100644 homeassistant/components/yolink/sensor.py create mode 100644 homeassistant/components/yolink/strings.json create mode 100644 homeassistant/components/yolink/translations/en.json create mode 100644 tests/components/yolink/__init__.py create mode 100644 tests/components/yolink/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 12aa3972f1a..da12b41d222 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1467,6 +1467,12 @@ omit = homeassistant/components/yandex_transport/* homeassistant/components/yeelightsunflower/light.py homeassistant/components/yi/camera.py + homeassistant/components/yolink/__init__.py + homeassistant/components/yolink/api.py + homeassistant/components/yolink/const.py + homeassistant/components/yolink/coordinator.py + homeassistant/components/yolink/entity.py + homeassistant/components/yolink/sensor.py homeassistant/components/youless/__init__.py homeassistant/components/youless/const.py homeassistant/components/youless/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 0805b17cff5..a1d329cbee0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1201,6 +1201,8 @@ build.json @home-assistant/supervisor /tests/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015 /homeassistant/components/yeelightsunflower/ @lindsaymarkward /homeassistant/components/yi/ @bachya +/homeassistant/components/yolink/ @YoSmart-Inc +/tests/components/yolink/ @YoSmart-Inc /homeassistant/components/youless/ @gjong /tests/components/youless/ @gjong /homeassistant/components/zengge/ @emontnemery diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py new file mode 100644 index 00000000000..d31a082f82f --- /dev/null +++ b/homeassistant/components/yolink/__init__.py @@ -0,0 +1,64 @@ +"""The yolink integration.""" +from __future__ import annotations + +from datetime import timedelta +import logging + +from yolink.client import YoLinkClient +from yolink.mqtt_client import MqttClient + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow + +from . import api +from .const import ATTR_CLIENT, ATTR_COORDINATOR, ATTR_MQTT_CLIENT, DOMAIN +from .coordinator import YoLinkCoordinator + +SCAN_INTERVAL = timedelta(minutes=5) + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS = [Platform.SENSOR] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up yolink from a config entry.""" + hass.data.setdefault(DOMAIN, {}) + implementation = ( + await config_entry_oauth2_flow.async_get_config_entry_implementation( + hass, entry + ) + ) + + session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) + + auth_mgr = api.ConfigEntryAuth( + hass, aiohttp_client.async_get_clientsession(hass), session + ) + + yolink_http_client = YoLinkClient(auth_mgr) + yolink_mqtt_client = MqttClient(auth_mgr) + coordinator = YoLinkCoordinator(hass, yolink_http_client, yolink_mqtt_client) + await coordinator.init_coordinator() + try: + await coordinator.async_config_entry_first_refresh() + except ConfigEntryNotReady as ex: + _LOGGER.error("Fetching initial data failed: %s", ex) + + hass.data[DOMAIN][entry.entry_id] = { + ATTR_CLIENT: yolink_http_client, + ATTR_MQTT_CLIENT: yolink_mqtt_client, + ATTR_COORDINATOR: coordinator, + } + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + return unload_ok diff --git a/homeassistant/components/yolink/api.py b/homeassistant/components/yolink/api.py new file mode 100644 index 00000000000..0991baed23f --- /dev/null +++ b/homeassistant/components/yolink/api.py @@ -0,0 +1,30 @@ +"""API for yolink bound to Home Assistant OAuth.""" +from aiohttp import ClientSession +from yolink.auth_mgr import YoLinkAuthMgr + +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + + +class ConfigEntryAuth(YoLinkAuthMgr): + """Provide yolink authentication tied to an OAuth2 based config entry.""" + + def __init__( + self, + hass: HomeAssistant, + websession: ClientSession, + oauth2Session: config_entry_oauth2_flow.OAuth2Session, + ) -> None: + """Initialize yolink Auth.""" + self.hass = hass + self.oauth_session = oauth2Session + super().__init__(websession) + + def access_token(self) -> str: + """Return the access token.""" + return self.oauth_session.token["access_token"] + + async def check_and_refresh_token(self) -> str: + """Check the token.""" + await self.oauth_session.async_ensure_token_valid() + return self.access_token() diff --git a/homeassistant/components/yolink/application_credentials.py b/homeassistant/components/yolink/application_credentials.py new file mode 100644 index 00000000000..f8378299952 --- /dev/null +++ b/homeassistant/components/yolink/application_credentials.py @@ -0,0 +1,14 @@ +"""Application credentials platform for yolink.""" + +from yolink.const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ) diff --git a/homeassistant/components/yolink/config_flow.py b/homeassistant/components/yolink/config_flow.py new file mode 100644 index 00000000000..35a4c4ebea8 --- /dev/null +++ b/homeassistant/components/yolink/config_flow.py @@ -0,0 +1,63 @@ +"""Config flow for yolink.""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.config_entries import ConfigEntry +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import config_entry_oauth2_flow + +from .const import DOMAIN + + +class OAuth2FlowHandler( + config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN +): + """Config flow to handle yolink OAuth2 authentication.""" + + DOMAIN = DOMAIN + _reauth_entry: ConfigEntry | None = None + + @property + def logger(self) -> logging.Logger: + """Return logger.""" + return logging.getLogger(__name__) + + @property + def extra_authorize_data(self) -> dict: + """Extra data that needs to be appended to the authorize url.""" + scopes = ["create"] + return {"scope": " ".join(scopes)} + + async def async_step_reauth(self, user_input=None) -> FlowResult: + """Perform reauth upon an API authentication error.""" + self._reauth_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm(self, user_input=None) -> FlowResult: + """Dialog that informs the user that reauth is required.""" + if user_input is None: + return self.async_show_form(step_id="reauth_confirm") + return await self.async_step_user() + + async def async_oauth_create_entry(self, data: dict) -> FlowResult: + """Create an oauth config entry or update existing entry for reauth.""" + if existing_entry := self._reauth_entry: + self.hass.config_entries.async_update_entry( + existing_entry, data=existing_entry.data | data + ) + await self.hass.config_entries.async_reload(existing_entry.entry_id) + return self.async_abort(reason="reauth_successful") + return self.async_create_entry(title="YoLink", data=data) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle a flow start.""" + existing_entry = await self.async_set_unique_id(DOMAIN) + if existing_entry and not self._reauth_entry: + return self.async_abort(reason="already_configured") + return await super().async_step_user(user_input) diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py new file mode 100644 index 00000000000..1804838e8b3 --- /dev/null +++ b/homeassistant/components/yolink/const.py @@ -0,0 +1,16 @@ +"""Constants for the yolink integration.""" + +DOMAIN = "yolink" +MANUFACTURER = "YoLink" +HOME_ID = "homeId" +HOME_SUBSCRIPTION = "home_subscription" +ATTR_PLATFORM_SENSOR = "sensor" +ATTR_COORDINATOR = "coordinator" +ATTR_DEVICE = "devices" +ATTR_DEVICE_TYPE = "type" +ATTR_DEVICE_NAME = "name" +ATTR_DEVICE_STATE = "state" +ATTR_CLIENT = "client" +ATTR_MQTT_CLIENT = "mqtt_client" +ATTR_DEVICE_ID = "deviceId" +ATTR_DEVICE_DOOR_SENSOR = "DoorSensor" diff --git a/homeassistant/components/yolink/coordinator.py b/homeassistant/components/yolink/coordinator.py new file mode 100644 index 00000000000..e5578eae4b2 --- /dev/null +++ b/homeassistant/components/yolink/coordinator.py @@ -0,0 +1,109 @@ +"""YoLink DataUpdateCoordinator.""" +from __future__ import annotations + +import asyncio +from datetime import timedelta +import logging + +import async_timeout +from yolink.client import YoLinkClient +from yolink.device import YoLinkDevice +from yolink.exception import YoLinkAuthFailError, YoLinkClientError +from yolink.model import BRDP +from yolink.mqtt_client import MqttClient + +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import ATTR_DEVICE, ATTR_DEVICE_STATE, DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +class YoLinkCoordinator(DataUpdateCoordinator[dict]): + """YoLink DataUpdateCoordinator.""" + + def __init__( + self, hass: HomeAssistant, yl_client: YoLinkClient, yl_mqtt_client: MqttClient + ) -> None: + """Init YoLink DataUpdateCoordinator. + + fetch state every 30 minutes base on yolink device heartbeat interval + data is None before the first successful update, but we need to use data at first update + """ + super().__init__( + hass, _LOGGER, name=DOMAIN, update_interval=timedelta(minutes=30) + ) + self._client = yl_client + self._mqtt_client = yl_mqtt_client + self.yl_devices: list[YoLinkDevice] = [] + self.data = {} + + def on_message_callback(self, message: tuple[str, BRDP]): + """On message callback.""" + data = message[1] + if data.event is None: + return + event_param = data.event.split(".") + event_type = event_param[len(event_param) - 1] + if event_type not in ( + "Report", + "Alert", + "StatusChange", + "getState", + ): + return + resolved_state = data.data + if resolved_state is None: + return + self.data[message[0]] = resolved_state + self.async_set_updated_data(self.data) + + async def init_coordinator(self): + """Init coordinator.""" + try: + async with async_timeout.timeout(10): + home_info = await self._client.get_general_info() + await self._mqtt_client.init_home_connection( + home_info.data["id"], self.on_message_callback + ) + async with async_timeout.timeout(10): + device_response = await self._client.get_auth_devices() + + except YoLinkAuthFailError as yl_auth_err: + raise ConfigEntryAuthFailed from yl_auth_err + + except (YoLinkClientError, asyncio.TimeoutError) as err: + raise ConfigEntryNotReady from err + + yl_devices: list[YoLinkDevice] = [] + + for device_info in device_response.data[ATTR_DEVICE]: + yl_devices.append(YoLinkDevice(device_info, self._client)) + + self.yl_devices = yl_devices + + async def fetch_device_state(self, device: YoLinkDevice): + """Fetch Device State.""" + try: + async with async_timeout.timeout(10): + device_state_resp = await device.fetch_state_with_api() + if ATTR_DEVICE_STATE in device_state_resp.data: + self.data[device.device_id] = device_state_resp.data[ + ATTR_DEVICE_STATE + ] + except YoLinkAuthFailError as yl_auth_err: + raise ConfigEntryAuthFailed from yl_auth_err + except YoLinkClientError as yl_client_err: + raise UpdateFailed( + f"Error communicating with API: {yl_client_err}" + ) from yl_client_err + + async def _async_update_data(self) -> dict: + fetch_tasks = [] + for yl_device in self.yl_devices: + fetch_tasks.append(self.fetch_device_state(yl_device)) + if fetch_tasks: + await asyncio.gather(*fetch_tasks) + return self.data diff --git a/homeassistant/components/yolink/entity.py b/homeassistant/components/yolink/entity.py new file mode 100644 index 00000000000..6954b117728 --- /dev/null +++ b/homeassistant/components/yolink/entity.py @@ -0,0 +1,52 @@ +"""Support for YoLink Device.""" +from __future__ import annotations + +from abc import abstractmethod + +from yolink.device import YoLinkDevice + +from homeassistant.core import callback +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, MANUFACTURER +from .coordinator import YoLinkCoordinator + + +class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]): + """YoLink Device Basic Entity.""" + + def __init__( + self, + coordinator: YoLinkCoordinator, + device_info: YoLinkDevice, + ) -> None: + """Init YoLink Entity.""" + super().__init__(coordinator) + self.device = device_info + + @property + def device_id(self) -> str: + """Return the device id of the YoLink device.""" + return self.device.device_id + + @callback + def _handle_coordinator_update(self) -> None: + data = self.coordinator.data.get(self.device.device_id) + if data is not None: + self.update_entity_state(data) + + @property + def device_info(self) -> DeviceInfo: + """Return the device info for HA.""" + return DeviceInfo( + identifiers={(DOMAIN, self.device.device_id)}, + manufacturer=MANUFACTURER, + model=self.device.device_type, + name=self.device.device_name, + ) + + @callback + @abstractmethod + def update_entity_state(self, state: dict) -> None: + """Parse and update entity state, should be overridden.""" diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json new file mode 100644 index 00000000000..d0072145c19 --- /dev/null +++ b/homeassistant/components/yolink/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "yolink", + "name": "YoLink", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/yolink", + "requirements": ["yolink-api==0.0.5"], + "dependencies": ["auth", "application_credentials"], + "codeowners": ["@YoSmart-Inc"], + "iot_class": "cloud_push" +} diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py new file mode 100644 index 00000000000..075cfc24179 --- /dev/null +++ b/homeassistant/components/yolink/sensor.py @@ -0,0 +1,104 @@ +"""YoLink Binary Sensor.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from yolink.device import YoLinkDevice + +from homeassistant.components.sensor import ( + SensorDeviceClass, + SensorEntity, + SensorEntityDescription, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import PERCENTAGE +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util import percentage + +from .const import ATTR_COORDINATOR, ATTR_DEVICE_DOOR_SENSOR, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +@dataclass +class YoLinkSensorEntityDescriptionMixin: + """Mixin for device type.""" + + exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True + + +@dataclass +class YoLinkSensorEntityDescription( + YoLinkSensorEntityDescriptionMixin, SensorEntityDescription +): + """YoLink SensorEntityDescription.""" + + value: Callable = lambda state: state + + +SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( + YoLinkSensorEntityDescription( + key="battery", + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement=PERCENTAGE, + name="Battery", + state_class=SensorStateClass.MEASUREMENT, + value=lambda value: percentage.ordered_list_item_to_percentage( + [1, 2, 3, 4], value + ), + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], + ), +) + +SENSOR_DEVICE_TYPE = [ATTR_DEVICE_DOOR_SENSOR] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink Sensor from a config entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] + sensor_devices = [ + device + for device in coordinator.yl_devices + if device.device_type in SENSOR_DEVICE_TYPE + ] + entities = [] + for sensor_device in sensor_devices: + for description in SENSOR_TYPES: + if description.exists_fn(sensor_device): + entities.append( + YoLinkSensorEntity(coordinator, description, sensor_device) + ) + async_add_entities(entities) + + +class YoLinkSensorEntity(YoLinkEntity, SensorEntity): + """YoLink Sensor Entity.""" + + entity_description: YoLinkSensorEntityDescription + + def __init__( + self, + coordinator: YoLinkCoordinator, + description: YoLinkSensorEntityDescription, + device: YoLinkDevice, + ) -> None: + """Init YoLink Sensor.""" + super().__init__(coordinator, device) + self.entity_description = description + self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" + self._attr_name = f"{device.device_name} ({self.entity_description.name})" + + @callback + def update_entity_state(self, state: dict) -> None: + """Update HA Entity State.""" + self._attr_native_value = self.entity_description.value( + state[self.entity_description.key] + ) + self.async_write_ha_state() diff --git a/homeassistant/components/yolink/strings.json b/homeassistant/components/yolink/strings.json new file mode 100644 index 00000000000..94fe5dc09aa --- /dev/null +++ b/homeassistant/components/yolink/strings.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "pick_implementation": { + "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" + }, + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "The yolink integration needs to re-authenticate your account" + } + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", + "already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]", + "oauth_error": "[%key:common::config_flow::abort::oauth2_error%]", + "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", + "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", + "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + }, + "create_entry": { + "default": "[%key:common::config_flow::create_entry::authenticated%]" + } + } +} diff --git a/homeassistant/components/yolink/translations/en.json b/homeassistant/components/yolink/translations/en.json new file mode 100644 index 00000000000..d1817fbe011 --- /dev/null +++ b/homeassistant/components/yolink/translations/en.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Account is already configured", + "already_in_progress": "Configuration flow is already in progress", + "authorize_url_timeout": "Timeout generating authorize URL.", + "missing_configuration": "The component is not configured. Please follow the documentation.", + "no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", + "oauth_error": "Received invalid token data.", + "reauth_successful": "Re-authentication was successful" + }, + "create_entry": { + "default": "Successfully authenticated" + }, + "step": { + "pick_implementation": { + "title": "Pick Authentication Method" + }, + "reauth_confirm": { + "description": "The yolink integration needs to re-authenticate your account", + "title": "Reauthenticate Integration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 40f451de153..0c3e60b8b8d 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -10,5 +10,6 @@ APPLICATION_CREDENTIALS = [ "google", "netatmo", "spotify", - "xbox" + "xbox", + "yolink" ] diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 77146689b8c..7f0059b2da9 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -411,6 +411,7 @@ FLOWS = { "yale_smart_alarm", "yamaha_musiccast", "yeelight", + "yolink", "youless", "zerproc", "zha", diff --git a/requirements_all.txt b/requirements_all.txt index a7c59f1853d..687962e94b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2479,6 +2479,9 @@ yeelight==0.7.10 # homeassistant.components.yeelightsunflower yeelightsunflower==0.0.10 +# homeassistant.components.yolink +yolink-api==0.0.5 + # homeassistant.components.youless youless-api==0.16 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f9926797e6f..05a7207c01a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1631,6 +1631,9 @@ yalexs==1.1.25 # homeassistant.components.yeelight yeelight==0.7.10 +# homeassistant.components.yolink +yolink-api==0.0.5 + # homeassistant.components.youless youless-api==0.16 diff --git a/tests/components/yolink/__init__.py b/tests/components/yolink/__init__.py new file mode 100644 index 00000000000..a72667661b9 --- /dev/null +++ b/tests/components/yolink/__init__.py @@ -0,0 +1 @@ +"""Tests for the yolink integration.""" diff --git a/tests/components/yolink/test_config_flow.py b/tests/components/yolink/test_config_flow.py new file mode 100644 index 00000000000..4dd347f4076 --- /dev/null +++ b/tests/components/yolink/test_config_flow.py @@ -0,0 +1,210 @@ +"""Test yolink config flow.""" +import asyncio +from http import HTTPStatus +from unittest.mock import patch + +from homeassistant import config_entries, data_entry_flow, setup +from homeassistant.components import application_credentials +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from tests.common import MockConfigEntry + +CLIENT_ID = "12345" +CLIENT_SECRET = "6789" +YOLINK_HOST = "api.yosmart.com" +YOLINK_HTTP_HOST = f"http://{YOLINK_HOST}" +DOMAIN = "yolink" +OAUTH2_AUTHORIZE = f"{YOLINK_HTTP_HOST}/oauth/v2/authorization.htm" +OAUTH2_TOKEN = f"{YOLINK_HTTP_HOST}/open/yolink/token" + + +async def test_abort_if_no_configuration(hass): + """Check flow abort when no configuration.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "missing_configuration" + + +async def test_abort_if_existing_entry(hass: HomeAssistant): + """Check flow abort when an entry already exist.""" + MockConfigEntry(domain=DOMAIN, unique_id=DOMAIN).add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_full_flow( + hass, hass_client_no_auth, aioclient_mock, current_request_with_host +): + """Check full flow.""" + assert await setup.async_setup_component( + hass, + DOMAIN, + {}, + ) + await application_credentials.async_import_client_credential( + hass, + DOMAIN, + application_credentials.ClientCredential(CLIENT_ID, CLIENT_SECRET), + ) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + assert result["url"] == ( + f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope=create" + ) + + client = await hass_client_no_auth() + resp = await client.get(f"/auth/external/callback?code=abcd&state={state}") + assert resp.status == HTTPStatus.OK + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + aioclient_mock.post( + OAUTH2_TOKEN, + json={ + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + }, + ) + + with patch("homeassistant.components.yolink.api.ConfigEntryAuth"), patch( + "homeassistant.components.yolink.async_setup_entry", return_value=True + ) as mock_setup: + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert result["data"]["auth_implementation"] == DOMAIN + + result["data"]["token"].pop("expires_at") + assert result["data"]["token"] == { + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + } + + assert DOMAIN in hass.config.components + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.state is config_entries.ConfigEntryState.LOADED + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert len(mock_setup.mock_calls) == 1 + + +async def test_abort_if_authorization_timeout(hass, current_request_with_host): + """Check yolink authorization timeout.""" + assert await setup.async_setup_component( + hass, + DOMAIN, + {}, + ) + await application_credentials.async_import_client_credential( + hass, + DOMAIN, + application_credentials.ClientCredential(CLIENT_ID, CLIENT_SECRET), + ) + with patch( + "homeassistant.components.yolink.config_entry_oauth2_flow." + "LocalOAuth2Implementation.async_generate_authorize_url", + side_effect=asyncio.TimeoutError, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "authorize_url_timeout" + + +async def test_reauthentication( + hass, hass_client_no_auth, aioclient_mock, current_request_with_host +): + """Test yolink reauthentication.""" + await setup.async_setup_component( + hass, + DOMAIN, + {}, + ) + + await application_credentials.async_import_client_credential( + hass, + DOMAIN, + application_credentials.ClientCredential(CLIENT_ID, CLIENT_SECRET), + ) + + old_entry = MockConfigEntry( + domain=DOMAIN, + unique_id=DOMAIN, + version=1, + data={ + "refresh_token": "outdated_fresh_token", + "access_token": "outdated_access_token", + }, + ) + old_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "unique_id": old_entry.unique_id, + "entry_id": old_entry.entry_id, + }, + data=old_entry.data, + ) + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + + result = await hass.config_entries.flow.async_configure(flows[0]["flow_id"], {}) + + # pylint: disable=protected-access + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + client = await hass_client_no_auth() + await client.get(f"/auth/external/callback?code=abcd&state={state}") + + aioclient_mock.post( + OAUTH2_TOKEN, + json={ + "refresh_token": "mock-refresh-token", + "access_token": "mock-access-token", + "type": "Bearer", + "expires_in": 60, + }, + ) + + with patch("homeassistant.components.yolink.api.ConfigEntryAuth"): + with patch( + "homeassistant.components.yolink.async_setup_entry", return_value=True + ) as mock_setup: + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + token_data = old_entry.data["token"] + assert token_data["access_token"] == "mock-access-token" + assert token_data["refresh_token"] == "mock-refresh-token" + assert token_data["type"] == "Bearer" + assert token_data["expires_in"] == 60 + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "reauth_successful" + assert len(mock_setup.mock_calls) == 1 From 7d26be2d4d0711796c0870d7917d68751aad7a66 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 17 May 2022 01:48:31 -0700 Subject: [PATCH 519/930] Add Home Connect application_credentials platform and deprecate configuration.yaml (#71988) Add Home Connect application_credentials platform --- .../components/home_connect/__init__.py | 43 ++++++++++++------- .../home_connect/application_credentials.py | 14 ++++++ .../components/home_connect/manifest.json | 2 +- .../generated/application_credentials.py | 1 + 4 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/home_connect/application_credentials.py diff --git a/homeassistant/components/home_connect/__init__.py b/homeassistant/components/home_connect/__init__.py index 26448893438..345eeeddaaa 100644 --- a/homeassistant/components/home_connect/__init__.py +++ b/homeassistant/components/home_connect/__init__.py @@ -6,6 +6,10 @@ import logging from requests import HTTPError import voluptuous as vol +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform from homeassistant.core import HomeAssistant @@ -13,22 +17,25 @@ from homeassistant.helpers import config_entry_oauth2_flow, config_validation as from homeassistant.helpers.typing import ConfigType from homeassistant.util import Throttle -from . import api, config_flow -from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN +from . import api +from .const import DOMAIN _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(minutes=1) CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -42,17 +49,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in config: return True - config_flow.OAuth2FlowHandler.async_register_implementation( + await async_import_client_credential( hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, + DOMAIN, + ClientCredential( config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, ), ) + _LOGGER.warning( + "Configuration of Home Connect integration in YAML is deprecated and " + "will be removed in a future release; Your existing OAuth " + "Application Credentials have been imported into the UI " + "automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/home_connect/application_credentials.py b/homeassistant/components/home_connect/application_credentials.py new file mode 100644 index 00000000000..3d5a407b487 --- /dev/null +++ b/homeassistant/components/home_connect/application_credentials.py @@ -0,0 +1,14 @@ +"""Application credentials platform for Home Connect.""" + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + +from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ) diff --git a/homeassistant/components/home_connect/manifest.json b/homeassistant/components/home_connect/manifest.json index e50053d2d1b..0a055c971c5 100644 --- a/homeassistant/components/home_connect/manifest.json +++ b/homeassistant/components/home_connect/manifest.json @@ -2,7 +2,7 @@ "domain": "home_connect", "name": "Home Connect", "documentation": "https://www.home-assistant.io/integrations/home_connect", - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "codeowners": ["@DavidMStraub"], "requirements": ["homeconnect==0.7.0"], "config_flow": true, diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 0c3e60b8b8d..7521c5c30b8 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -8,6 +8,7 @@ To update, run python3 -m script.hassfest APPLICATION_CREDENTIALS = [ "geocaching", "google", + "home_connect", "netatmo", "spotify", "xbox", From 39b27e4d38b91498245e19ded3c5fba6641cbcd4 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Tue, 17 May 2022 13:08:38 +0200 Subject: [PATCH 520/930] Improve NUT typing (#72002) --- homeassistant/components/nut/config_flow.py | 5 +++-- homeassistant/components/nut/sensor.py | 6 ++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/nut/config_flow.py b/homeassistant/components/nut/config_flow.py index 5ba8024878a..917f004ce32 100644 --- a/homeassistant/components/nut/config_flow.py +++ b/homeassistant/components/nut/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Network UPS Tools (NUT) integration.""" from __future__ import annotations +from collections.abc import Mapping import logging from typing import Any @@ -69,7 +70,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, return {"ups_list": nut_data.ups_list, "available_resources": status} -def _format_host_port_alias(user_input: dict[str, Any]) -> str: +def _format_host_port_alias(user_input: Mapping[str, Any]) -> str: """Format a host, port, and alias so it can be used for comparison or display.""" host = user_input[CONF_HOST] port = user_input[CONF_PORT] @@ -157,7 +158,7 @@ class NutConfigFlow(ConfigFlow, domain=DOMAIN): def _host_port_alias_already_configured(self, user_input: dict[str, Any]) -> bool: """See if we already have a nut entry matching user input configured.""" existing_host_port_aliases = { - _format_host_port_alias(dict(entry.data)) + _format_host_port_alias(entry.data) for entry in self._async_current_entries() if CONF_HOST in entry.data } diff --git a/homeassistant/components/nut/sensor.py b/homeassistant/components/nut/sensor.py index 6992e41366e..6c68f2b6c6b 100644 --- a/homeassistant/components/nut/sensor.py +++ b/homeassistant/components/nut/sensor.py @@ -86,14 +86,12 @@ async def async_setup_entry( async_add_entities(entities, True) -class NUTSensor(CoordinatorEntity, SensorEntity): +class NUTSensor(CoordinatorEntity[DataUpdateCoordinator[dict[str, str]]], SensorEntity): """Representation of a sensor entity for NUT status values.""" - coordinator: DataUpdateCoordinator[dict[str, str]] - def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[dict[str, str]], sensor_description: SensorEntityDescription, data: PyNUTData, unique_id: str, From 7d2deae5921989fc4a9add9aef910160ab0ac358 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 13:40:19 +0200 Subject: [PATCH 521/930] Clean up use of deprecated async_get_registry methods (#72001) --- homeassistant/components/alarm_control_panel/device_action.py | 2 +- .../components/alarm_control_panel/device_condition.py | 2 +- .../components/alarm_control_panel/device_trigger.py | 2 +- homeassistant/components/arcam_fmj/device_trigger.py | 2 +- homeassistant/components/climate/device_action.py | 2 +- homeassistant/components/climate/device_condition.py | 2 +- homeassistant/components/climate/device_trigger.py | 2 +- homeassistant/components/cover/device_action.py | 2 +- homeassistant/components/cover/device_condition.py | 2 +- homeassistant/components/cover/device_trigger.py | 2 +- homeassistant/components/device_tracker/device_condition.py | 2 +- homeassistant/components/device_tracker/device_trigger.py | 2 +- homeassistant/components/fan/device_condition.py | 2 +- homeassistant/components/geofency/device_tracker.py | 2 +- homeassistant/components/gpslogger/device_tracker.py | 2 +- homeassistant/components/huawei_lte/device_tracker.py | 2 +- homeassistant/components/humidifier/device_action.py | 2 +- homeassistant/components/humidifier/device_condition.py | 2 +- homeassistant/components/humidifier/device_trigger.py | 2 +- homeassistant/components/keenetic_ndms2/device_tracker.py | 2 +- homeassistant/components/kodi/device_trigger.py | 2 +- homeassistant/components/kodi/media_player.py | 2 +- homeassistant/components/lock/device_action.py | 2 +- homeassistant/components/lock/device_condition.py | 2 +- homeassistant/components/lock/device_trigger.py | 2 +- homeassistant/components/media_player/device_condition.py | 2 +- homeassistant/components/media_player/device_trigger.py | 2 +- homeassistant/components/mikrotik/device_tracker.py | 2 +- homeassistant/components/netatmo/device_trigger.py | 2 +- homeassistant/components/number/device_action.py | 2 +- homeassistant/components/owntracks/device_tracker.py | 2 +- homeassistant/components/ps4/__init__.py | 2 +- homeassistant/components/ps4/media_player.py | 4 ++-- homeassistant/components/ruckus_unleashed/__init__.py | 2 +- homeassistant/components/ruckus_unleashed/device_tracker.py | 2 +- homeassistant/components/select/device_action.py | 2 +- homeassistant/components/select/device_condition.py | 2 +- homeassistant/components/select/device_trigger.py | 2 +- homeassistant/components/shelly/climate.py | 2 +- homeassistant/components/shelly/entity.py | 2 +- homeassistant/components/traccar/device_tracker.py | 2 +- homeassistant/components/vacuum/device_action.py | 2 +- homeassistant/components/vacuum/device_condition.py | 2 +- homeassistant/components/vacuum/device_trigger.py | 2 +- homeassistant/components/water_heater/device_action.py | 2 +- homeassistant/config_entries.py | 2 +- homeassistant/helpers/device_registry.py | 2 +- .../templates/device_action/integration/device_action.py | 2 +- .../device_condition/integration/device_condition.py | 2 +- .../templates/device_trigger/integration/device_trigger.py | 2 +- 50 files changed, 51 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/device_action.py b/homeassistant/components/alarm_control_panel/device_action.py index 2680b033b03..ff14b851df8 100644 --- a/homeassistant/components/alarm_control_panel/device_action.py +++ b/homeassistant/components/alarm_control_panel/device_action.py @@ -57,7 +57,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Alarm control panel devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/alarm_control_panel/device_condition.py b/homeassistant/components/alarm_control_panel/device_condition.py index a378cf262ed..4764d5cfcbe 100644 --- a/homeassistant/components/alarm_control_panel/device_condition.py +++ b/homeassistant/components/alarm_control_panel/device_condition.py @@ -64,7 +64,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Alarm control panel devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/alarm_control_panel/device_trigger.py b/homeassistant/components/alarm_control_panel/device_trigger.py index ce53596fc8d..18508661034 100644 --- a/homeassistant/components/alarm_control_panel/device_trigger.py +++ b/homeassistant/components/alarm_control_panel/device_trigger.py @@ -60,7 +60,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Alarm control panel devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers: list[dict[str, str]] = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index 2b1a3bf3a19..d1fca811fcc 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -37,7 +37,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Arcam FMJ Receiver control devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/climate/device_action.py b/homeassistant/components/climate/device_action.py index 81189417c2b..45160eed8eb 100644 --- a/homeassistant/components/climate/device_action.py +++ b/homeassistant/components/climate/device_action.py @@ -43,7 +43,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Climate devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/climate/device_condition.py b/homeassistant/components/climate/device_condition.py index dd5842cd2a8..56bac123d28 100644 --- a/homeassistant/components/climate/device_condition.py +++ b/homeassistant/components/climate/device_condition.py @@ -45,7 +45,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Climate devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/climate/device_trigger.py b/homeassistant/components/climate/device_trigger.py index 6bd6f4c3e02..e40cf32f2a9 100644 --- a/homeassistant/components/climate/device_trigger.py +++ b/homeassistant/components/climate/device_trigger.py @@ -67,7 +67,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Climate devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/cover/device_action.py b/homeassistant/components/cover/device_action.py index debb2368cf2..9bdf21f168e 100644 --- a/homeassistant/components/cover/device_action.py +++ b/homeassistant/components/cover/device_action.py @@ -63,7 +63,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Cover devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/cover/device_condition.py b/homeassistant/components/cover/device_condition.py index cca608187a2..bb66d54b79b 100644 --- a/homeassistant/components/cover/device_condition.py +++ b/homeassistant/components/cover/device_condition.py @@ -66,7 +66,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Cover devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions: list[dict[str, str]] = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/cover/device_trigger.py b/homeassistant/components/cover/device_trigger.py index f960fcdcce6..f7b2f54f4dd 100644 --- a/homeassistant/components/cover/device_trigger.py +++ b/homeassistant/components/cover/device_trigger.py @@ -76,7 +76,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Cover devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/device_tracker/device_condition.py b/homeassistant/components/device_tracker/device_condition.py index 0703d3c7646..1a6adabda63 100644 --- a/homeassistant/components/device_tracker/device_condition.py +++ b/homeassistant/components/device_tracker/device_condition.py @@ -33,7 +33,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Device tracker devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/device_tracker/device_trigger.py b/homeassistant/components/device_tracker/device_trigger.py index 4350f03cefc..134e9d8dca3 100644 --- a/homeassistant/components/device_tracker/device_trigger.py +++ b/homeassistant/components/device_tracker/device_trigger.py @@ -41,7 +41,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Device Tracker devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/fan/device_condition.py b/homeassistant/components/fan/device_condition.py index b0882137d7f..7e27ea29f98 100644 --- a/homeassistant/components/fan/device_condition.py +++ b/homeassistant/components/fan/device_condition.py @@ -34,7 +34,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Fan devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/geofency/device_tracker.py b/homeassistant/components/geofency/device_tracker.py index c92b2905be2..bd4b2852019 100644 --- a/homeassistant/components/geofency/device_tracker.py +++ b/homeassistant/components/geofency/device_tracker.py @@ -35,7 +35,7 @@ async def async_setup_entry( ] = async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data) # Restore previously loaded devices - dev_reg = await device_registry.async_get_registry(hass) + dev_reg = device_registry.async_get(hass) dev_ids = { identifier[1] for device in dev_reg.devices.values() diff --git a/homeassistant/components/gpslogger/device_tracker.py b/homeassistant/components/gpslogger/device_tracker.py index fb0e1385fba..ea648ed7495 100644 --- a/homeassistant/components/gpslogger/device_tracker.py +++ b/homeassistant/components/gpslogger/device_tracker.py @@ -45,7 +45,7 @@ async def async_setup_entry( ] = async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data) # Restore previously loaded devices - dev_reg = await device_registry.async_get_registry(hass) + dev_reg = device_registry.async_get(hass) dev_ids = { identifier[1] for device in dev_reg.devices.values() diff --git a/homeassistant/components/huawei_lte/device_tracker.py b/homeassistant/components/huawei_lte/device_tracker.py index c8e9a5d7a0d..ee643c59b12 100644 --- a/homeassistant/components/huawei_lte/device_tracker.py +++ b/homeassistant/components/huawei_lte/device_tracker.py @@ -66,7 +66,7 @@ async def async_setup_entry( # Initialize already tracked entities tracked: set[str] = set() - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) known_entities: list[Entity] = [] track_wired_clients = router.config_entry.options.get( CONF_TRACK_WIRED_CLIENTS, DEFAULT_TRACK_WIRED_CLIENTS diff --git a/homeassistant/components/humidifier/device_action.py b/homeassistant/components/humidifier/device_action.py index db51ab34baa..03f0bb1fea1 100644 --- a/homeassistant/components/humidifier/device_action.py +++ b/homeassistant/components/humidifier/device_action.py @@ -49,7 +49,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Humidifier devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = await toggle_entity.async_get_actions(hass, device_id, DOMAIN) # Get all the integrations entities for this device diff --git a/homeassistant/components/humidifier/device_condition.py b/homeassistant/components/humidifier/device_condition.py index c58c247d569..b8e8d2a13ae 100644 --- a/homeassistant/components/humidifier/device_condition.py +++ b/homeassistant/components/humidifier/device_condition.py @@ -41,7 +41,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Humidifier devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = await toggle_entity.async_get_conditions(hass, device_id, DOMAIN) # Get all the integrations entities for this device diff --git a/homeassistant/components/humidifier/device_trigger.py b/homeassistant/components/humidifier/device_trigger.py index e4b0440e869..a2d15097335 100644 --- a/homeassistant/components/humidifier/device_trigger.py +++ b/homeassistant/components/humidifier/device_trigger.py @@ -61,7 +61,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Humidifier devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) # Get all the integrations entities for this device diff --git a/homeassistant/components/keenetic_ndms2/device_tracker.py b/homeassistant/components/keenetic_ndms2/device_tracker.py index 5a92d04e498..b625cabbbc1 100644 --- a/homeassistant/components/keenetic_ndms2/device_tracker.py +++ b/homeassistant/components/keenetic_ndms2/device_tracker.py @@ -40,7 +40,7 @@ async def async_setup_entry( update_from_router() - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) # Restore devices that are not a part of active clients list. restored = [] for entity_entry in registry.entities.values(): diff --git a/homeassistant/components/kodi/device_trigger.py b/homeassistant/components/kodi/device_trigger.py index 68735bfa386..e4fe6cfa103 100644 --- a/homeassistant/components/kodi/device_trigger.py +++ b/homeassistant/components/kodi/device_trigger.py @@ -38,7 +38,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Kodi devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index 66bb582e6e0..cea3adcde00 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -426,7 +426,7 @@ class KodiEntity(MediaPlayerEntity): version = (await self._kodi.get_application_properties(["version"]))["version"] sw_version = f"{version['major']}.{version['minor']}" - dev_reg = await device_registry.async_get_registry(self.hass) + dev_reg = device_registry.async_get(self.hass) device = dev_reg.async_get_device({(DOMAIN, self.unique_id)}) dev_reg.async_update_device(device.id, sw_version=sw_version) diff --git a/homeassistant/components/lock/device_action.py b/homeassistant/components/lock/device_action.py index 092aff8878d..038c2f1c0da 100644 --- a/homeassistant/components/lock/device_action.py +++ b/homeassistant/components/lock/device_action.py @@ -34,7 +34,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Lock devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/lock/device_condition.py b/homeassistant/components/lock/device_condition.py index a818a2b5fa4..cdaa02de618 100644 --- a/homeassistant/components/lock/device_condition.py +++ b/homeassistant/components/lock/device_condition.py @@ -45,7 +45,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Lock devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/lock/device_trigger.py b/homeassistant/components/lock/device_trigger.py index 75415bbf3e1..d875cadb446 100644 --- a/homeassistant/components/lock/device_trigger.py +++ b/homeassistant/components/lock/device_trigger.py @@ -45,7 +45,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Lock devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/media_player/device_condition.py b/homeassistant/components/media_player/device_condition.py index 5f57bcea48e..2f398790ac3 100644 --- a/homeassistant/components/media_player/device_condition.py +++ b/homeassistant/components/media_player/device_condition.py @@ -45,7 +45,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Media player devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/media_player/device_trigger.py b/homeassistant/components/media_player/device_trigger.py index aeed2fd646a..9644e5e0010 100644 --- a/homeassistant/components/media_player/device_trigger.py +++ b/homeassistant/components/media_player/device_trigger.py @@ -57,7 +57,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Media player entities.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = await entity.async_get_triggers(hass, device_id, DOMAIN) # Get all the integration entities for this device diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index 166415caf39..dee4a5de08d 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -34,7 +34,7 @@ async def async_setup_entry( tracked: dict[str, MikrotikHubTracker] = {} - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) # Restore clients that is not a part of active clients list. for entity in registry.entities.values(): diff --git a/homeassistant/components/netatmo/device_trigger.py b/homeassistant/components/netatmo/device_trigger.py index e3173fde160..7a9acac2f96 100644 --- a/homeassistant/components/netatmo/device_trigger.py +++ b/homeassistant/components/netatmo/device_trigger.py @@ -93,7 +93,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Netatmo devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) device_registry = await hass.helpers.device_registry.async_get_registry() triggers = [] diff --git a/homeassistant/components/number/device_action.py b/homeassistant/components/number/device_action.py index 917bcdce74f..3d449ffc213 100644 --- a/homeassistant/components/number/device_action.py +++ b/homeassistant/components/number/device_action.py @@ -32,7 +32,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Number.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions: list[dict[str, str]] = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index ae4d88df718..92f34617462 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -26,7 +26,7 @@ async def async_setup_entry( ) -> None: """Set up OwnTracks based off an entry.""" # Restore previously loaded devices - dev_reg = await device_registry.async_get_registry(hass) + dev_reg = device_registry.async_get(hass) dev_ids = { identifier[1] for device in dev_reg.devices.values() diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index e6c22552fc2..7e215060d73 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -116,7 +116,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Migrate Version 2 -> Version 3: Update identifier format. if version == 2: # Prevent changing entity_id. Updates entity registry. - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) for entity_id, e_entry in registry.entities.items(): if e_entry.config_entry_id == entry.entry_id: diff --git a/homeassistant/components/ps4/media_player.py b/homeassistant/components/ps4/media_player.py index ef6cc3c364b..1a1c93e210a 100644 --- a/homeassistant/components/ps4/media_player.py +++ b/homeassistant/components/ps4/media_player.py @@ -333,8 +333,8 @@ class PS4Device(MediaPlayerEntity): # If cannot get status on startup, assume info from registry. if status is None: _LOGGER.info("Assuming status from registry") - e_registry = await entity_registry.async_get_registry(self.hass) - d_registry = await device_registry.async_get_registry(self.hass) + e_registry = entity_registry.async_get(self.hass) + d_registry = device_registry.async_get(self.hass) for entity_id, entry in e_registry.entities.items(): if entry.config_entry_id == self._entry_id: self._unique_id = entry.unique_id diff --git a/homeassistant/components/ruckus_unleashed/__init__.py b/homeassistant/components/ruckus_unleashed/__init__.py index 6ea3b736dcd..2c8c8bb108d 100644 --- a/homeassistant/components/ruckus_unleashed/__init__.py +++ b/homeassistant/components/ruckus_unleashed/__init__.py @@ -44,7 +44,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: system_info = await hass.async_add_executor_job(ruckus.system_info) - registry = await device_registry.async_get_registry(hass) + registry = device_registry.async_get(hass) ap_info = await hass.async_add_executor_job(ruckus.ap_info) for device in ap_info[API_AP][API_ID].values(): registry.async_get_or_create( diff --git a/homeassistant/components/ruckus_unleashed/device_tracker.py b/homeassistant/components/ruckus_unleashed/device_tracker.py index 562f4835547..67c86f3bb51 100644 --- a/homeassistant/components/ruckus_unleashed/device_tracker.py +++ b/homeassistant/components/ruckus_unleashed/device_tracker.py @@ -38,7 +38,7 @@ async def async_setup_entry( coordinator.async_add_listener(router_update) ) - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) restore_entities(registry, coordinator, entry, async_add_entities, tracked) diff --git a/homeassistant/components/select/device_action.py b/homeassistant/components/select/device_action.py index f4c4d7883d0..f55a12da62a 100644 --- a/homeassistant/components/select/device_action.py +++ b/homeassistant/components/select/device_action.py @@ -34,7 +34,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Select devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) return [ { CONF_DEVICE_ID: device_id, diff --git a/homeassistant/components/select/device_condition.py b/homeassistant/components/select/device_condition.py index ed48f2afb15..6e6a3c704b3 100644 --- a/homeassistant/components/select/device_condition.py +++ b/homeassistant/components/select/device_condition.py @@ -38,7 +38,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Select devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) return [ { CONF_CONDITION: "device", diff --git a/homeassistant/components/select/device_trigger.py b/homeassistant/components/select/device_trigger.py index 79e1c4a221f..6d0378946e8 100644 --- a/homeassistant/components/select/device_trigger.py +++ b/homeassistant/components/select/device_trigger.py @@ -49,7 +49,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Select devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) return [ { CONF_PLATFORM: "device", diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index ffd5e012ef7..a042254fdc6 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -87,7 +87,7 @@ async def async_restore_climate_entities( ) -> None: """Restore sleeping climate devices.""" - ent_reg = await entity_registry.async_get_registry(hass) + ent_reg = entity_registry.async_get(hass) entries = entity_registry.async_entries_for_config_entry( ent_reg, config_entry.entry_id ) diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index f544770722f..de7d3dd9437 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -134,7 +134,7 @@ async def async_restore_block_attribute_entities( """Restore block attributes entities.""" entities = [] - ent_reg = await entity_registry.async_get_registry(hass) + ent_reg = entity_registry.async_get(hass) entries = entity_registry.async_entries_for_config_entry( ent_reg, config_entry.entry_id ) diff --git a/homeassistant/components/traccar/device_tracker.py b/homeassistant/components/traccar/device_tracker.py index 74ce3ead901..0ed13bceefa 100644 --- a/homeassistant/components/traccar/device_tracker.py +++ b/homeassistant/components/traccar/device_tracker.py @@ -144,7 +144,7 @@ async def async_setup_entry( ] = async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data) # Restore previously loaded devices - dev_reg = await device_registry.async_get_registry(hass) + dev_reg = device_registry.async_get(hass) dev_ids = { identifier[1] for device in dev_reg.devices.values() diff --git a/homeassistant/components/vacuum/device_action.py b/homeassistant/components/vacuum/device_action.py index 702f3fe7439..e8dac646153 100644 --- a/homeassistant/components/vacuum/device_action.py +++ b/homeassistant/components/vacuum/device_action.py @@ -30,7 +30,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Vacuum devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/vacuum/device_condition.py b/homeassistant/components/vacuum/device_condition.py index 7a973c93694..fa76dd800ec 100644 --- a/homeassistant/components/vacuum/device_condition.py +++ b/homeassistant/components/vacuum/device_condition.py @@ -32,7 +32,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for Vacuum devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/vacuum/device_trigger.py b/homeassistant/components/vacuum/device_trigger.py index 25a874a1e69..502f9f01410 100644 --- a/homeassistant/components/vacuum/device_trigger.py +++ b/homeassistant/components/vacuum/device_trigger.py @@ -40,7 +40,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for Vacuum devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # Get all the integrations entities for this device diff --git a/homeassistant/components/water_heater/device_action.py b/homeassistant/components/water_heater/device_action.py index dae9e4d579b..f200498c350 100644 --- a/homeassistant/components/water_heater/device_action.py +++ b/homeassistant/components/water_heater/device_action.py @@ -32,7 +32,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for Water Heater devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] for entry in entity_registry.async_entries_for_device(registry, device_id): diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 97278f3b2e3..7dfbb131c1b 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -1549,7 +1549,7 @@ class EntityRegistryDisabledHandler: async def _handle_entry_updated(self, event: Event) -> None: """Handle entity registry entry update.""" if self.registry is None: - self.registry = await entity_registry.async_get_registry(self.hass) + self.registry = entity_registry.async_get(self.hass) entity_entry = self.registry.async_get(event.data["entity_id"]) diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index c45b7d5f37b..7299da43958 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -823,7 +823,7 @@ def async_setup_cleanup(hass: HomeAssistant, dev_reg: DeviceRegistry) -> None: async def cleanup() -> None: """Cleanup.""" - ent_reg = await entity_registry.async_get_registry(hass) + ent_reg = entity_registry.async_get(hass) async_cleanup(hass, dev_reg, ent_reg) debounced_cleanup = Debouncer( diff --git a/script/scaffold/templates/device_action/integration/device_action.py b/script/scaffold/templates/device_action/integration/device_action.py index 5eb5249211b..a9d77853e55 100644 --- a/script/scaffold/templates/device_action/integration/device_action.py +++ b/script/scaffold/templates/device_action/integration/device_action.py @@ -33,7 +33,7 @@ async def async_get_actions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device actions for NEW_NAME devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) actions = [] # TODO Read this comment and remove it. diff --git a/script/scaffold/templates/device_condition/integration/device_condition.py b/script/scaffold/templates/device_condition/integration/device_condition.py index 6f129289af8..cc5ad765885 100644 --- a/script/scaffold/templates/device_condition/integration/device_condition.py +++ b/script/scaffold/templates/device_condition/integration/device_condition.py @@ -35,7 +35,7 @@ async def async_get_conditions( hass: HomeAssistant, device_id: str ) -> list[dict[str, str]]: """List device conditions for NEW_NAME devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) conditions = [] # Get all the integrations entities for this device diff --git a/script/scaffold/templates/device_trigger/integration/device_trigger.py b/script/scaffold/templates/device_trigger/integration/device_trigger.py index 9082d27953a..a03e27394e2 100644 --- a/script/scaffold/templates/device_trigger/integration/device_trigger.py +++ b/script/scaffold/templates/device_trigger/integration/device_trigger.py @@ -41,7 +41,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for NEW_NAME devices.""" - registry = await entity_registry.async_get_registry(hass) + registry = entity_registry.async_get(hass) triggers = [] # TODO Read this comment and remove it. From c67bbd06d4416ea63d6c7efb23cd519bdf25b06a Mon Sep 17 00:00:00 2001 From: rhadamantys <46837767+rhadamantys@users.noreply.github.com> Date: Tue, 17 May 2022 13:43:36 +0200 Subject: [PATCH 522/930] Provide unique id for enocean devices (#71774) Co-authored-by: Martin Hjelmare --- .../components/enocean/binary_sensor.py | 2 + homeassistant/components/enocean/light.py | 2 + homeassistant/components/enocean/sensor.py | 39 +++++++++++++++---- homeassistant/components/enocean/switch.py | 2 + 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/enocean/binary_sensor.py b/homeassistant/components/enocean/binary_sensor.py index 3afa7110657..e18542241da 100644 --- a/homeassistant/components/enocean/binary_sensor.py +++ b/homeassistant/components/enocean/binary_sensor.py @@ -1,6 +1,7 @@ """Support for EnOcean binary sensors.""" from __future__ import annotations +from enocean.utils import combine_hex import voluptuous as vol from homeassistant.components.binary_sensor import ( @@ -57,6 +58,7 @@ class EnOceanBinarySensor(EnOceanEntity, BinarySensorEntity): self._device_class = device_class self.which = -1 self.onoff = -1 + self._attr_unique_id = f"{combine_hex(dev_id)}-{device_class}" @property def name(self): diff --git a/homeassistant/components/enocean/light.py b/homeassistant/components/enocean/light.py index c7bf076c738..8ecc3f63831 100644 --- a/homeassistant/components/enocean/light.py +++ b/homeassistant/components/enocean/light.py @@ -3,6 +3,7 @@ from __future__ import annotations import math +from enocean.utils import combine_hex import voluptuous as vol from homeassistant.components.light import ( @@ -58,6 +59,7 @@ class EnOceanLight(EnOceanEntity, LightEntity): self._on_state = False self._brightness = 50 self._sender_id = sender_id + self._attr_unique_id = f"{combine_hex(dev_id)}" @property def name(self): diff --git a/homeassistant/components/enocean/sensor.py b/homeassistant/components/enocean/sensor.py index e48f117648a..06ea50d4cdb 100644 --- a/homeassistant/components/enocean/sensor.py +++ b/homeassistant/components/enocean/sensor.py @@ -1,6 +1,10 @@ """Support for EnOcean sensors.""" from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass + +from enocean.utils import combine_hex import voluptuous as vol from homeassistant.components.sensor import ( @@ -40,37 +44,56 @@ SENSOR_TYPE_POWER = "powersensor" SENSOR_TYPE_TEMPERATURE = "temperature" SENSOR_TYPE_WINDOWHANDLE = "windowhandle" -SENSOR_DESC_TEMPERATURE = SensorEntityDescription( + +@dataclass +class EnOceanSensorEntityDescriptionMixin: + """Mixin for required keys.""" + + unique_id: Callable[[list[int]], str | None] + + +@dataclass +class EnOceanSensorEntityDescription( + SensorEntityDescription, EnOceanSensorEntityDescriptionMixin +): + """Describes EnOcean sensor entity.""" + + +SENSOR_DESC_TEMPERATURE = EnOceanSensorEntityDescription( key=SENSOR_TYPE_TEMPERATURE, name="Temperature", native_unit_of_measurement=TEMP_CELSIUS, icon="mdi:thermometer", device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, + unique_id=lambda dev_id: f"{combine_hex(dev_id)}-{SENSOR_TYPE_TEMPERATURE}", ) -SENSOR_DESC_HUMIDITY = SensorEntityDescription( +SENSOR_DESC_HUMIDITY = EnOceanSensorEntityDescription( key=SENSOR_TYPE_HUMIDITY, name="Humidity", native_unit_of_measurement=PERCENTAGE, icon="mdi:water-percent", device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, + unique_id=lambda dev_id: f"{combine_hex(dev_id)}-{SENSOR_TYPE_HUMIDITY}", ) -SENSOR_DESC_POWER = SensorEntityDescription( +SENSOR_DESC_POWER = EnOceanSensorEntityDescription( key=SENSOR_TYPE_POWER, name="Power", native_unit_of_measurement=POWER_WATT, icon="mdi:power-plug", device_class=SensorDeviceClass.POWER, state_class=SensorStateClass.MEASUREMENT, + unique_id=lambda dev_id: f"{combine_hex(dev_id)}-{SENSOR_TYPE_POWER}", ) -SENSOR_DESC_WINDOWHANDLE = SensorEntityDescription( +SENSOR_DESC_WINDOWHANDLE = EnOceanSensorEntityDescription( key=SENSOR_TYPE_WINDOWHANDLE, name="WindowHandle", - icon="mdi:window", + icon="mdi:window-open-variant", + unique_id=lambda dev_id: f"{combine_hex(dev_id)}-{SENSOR_TYPE_WINDOWHANDLE}", ) @@ -132,11 +155,12 @@ def setup_platform( class EnOceanSensor(EnOceanEntity, RestoreEntity, SensorEntity): """Representation of an EnOcean sensor device such as a power meter.""" - def __init__(self, dev_id, dev_name, description: SensorEntityDescription): + def __init__(self, dev_id, dev_name, description: EnOceanSensorEntityDescription): """Initialize the EnOcean sensor device.""" super().__init__(dev_id, dev_name) self.entity_description = description self._attr_name = f"{description.name} {dev_name}" + self._attr_unique_id = description.unique_id(dev_id) async def async_added_to_hass(self): """Call when entity about to be added to hass.""" @@ -194,7 +218,7 @@ class EnOceanTemperatureSensor(EnOceanSensor): self, dev_id, dev_name, - description: SensorEntityDescription, + description: EnOceanSensorEntityDescription, *, scale_min, scale_max, @@ -248,7 +272,6 @@ class EnOceanWindowHandle(EnOceanSensor): def value_changed(self, packet): """Update the internal state of the sensor.""" - action = (packet.data[1] & 0x70) >> 4 if action == 0x07: diff --git a/homeassistant/components/enocean/switch.py b/homeassistant/components/enocean/switch.py index fc788e88d72..a53f691df19 100644 --- a/homeassistant/components/enocean/switch.py +++ b/homeassistant/components/enocean/switch.py @@ -1,6 +1,7 @@ """Support for EnOcean switches.""" from __future__ import annotations +from enocean.utils import combine_hex import voluptuous as vol from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity @@ -48,6 +49,7 @@ class EnOceanSwitch(EnOceanEntity, SwitchEntity): self._on_state = False self._on_state2 = False self.channel = channel + self._attr_unique_id = f"{combine_hex(dev_id)}" @property def is_on(self): From cba2fda93d27ca45e015247b1f146cf3d7ad723d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 14:13:21 +0200 Subject: [PATCH 523/930] Fix clear config entry from registry in Samsung TV migration (#72004) * Fix clear config entry from device registry * Fix clear config entry from entity registry --- homeassistant/components/samsungtv/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py index a7b8f7d1aec..49963734e83 100644 --- a/homeassistant/components/samsungtv/__init__.py +++ b/homeassistant/components/samsungtv/__init__.py @@ -314,10 +314,10 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> # 1 -> 2: Unique ID format changed, so delete and re-import: if version == 1: dev_reg = await hass.helpers.device_registry.async_get_registry() - dev_reg.async_clear_config_entry(config_entry) + dev_reg.async_clear_config_entry(config_entry.entry_id) en_reg = await hass.helpers.entity_registry.async_get_registry() - en_reg.async_clear_config_entry(config_entry) + en_reg.async_clear_config_entry(config_entry.entry_id) version = config_entry.version = 2 hass.config_entries.async_update_entry(config_entry) From 81259f4eefe3149ca84c3a066e835af7740cafbe Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Tue, 17 May 2022 15:08:21 +0200 Subject: [PATCH 524/930] Update xknx to 0.21.3 (#72006) --- homeassistant/components/knx/cover.py | 46 ++++------------------ homeassistant/components/knx/manifest.json | 2 +- homeassistant/components/knx/strings.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 11 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index b3096a75df5..ca18ad4835a 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -2,11 +2,10 @@ from __future__ import annotations from collections.abc import Callable -from datetime import datetime from typing import Any from xknx import XKNX -from xknx.devices import Cover as XknxCover, Device as XknxDevice +from xknx.devices import Cover as XknxCover from homeassistant import config_entries from homeassistant.components.cover import ( @@ -22,9 +21,8 @@ from homeassistant.const import ( CONF_NAME, Platform, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.event import async_track_utc_time_change from homeassistant.helpers.typing import ConfigType from .const import DATA_KNX_CONFIG, DOMAIN @@ -104,13 +102,6 @@ class KNXCover(KnxEntity, CoverEntity): f"{self._device.position_target.group_address}" ) - @callback - async def after_update_callback(self, device: XknxDevice) -> None: - """Call after device was updated.""" - self.async_write_ha_state() - if self._device.is_traveling(): - self.start_auto_updater() - @property def current_cover_position(self) -> int | None: """Return the current position of the cover. @@ -118,8 +109,9 @@ class KNXCover(KnxEntity, CoverEntity): None is unknown, 0 is closed, 100 is fully open. """ # In KNX 0 is open, 100 is closed. - pos = self._device.current_position() - return 100 - pos if pos is not None else None + if (pos := self._device.current_position()) is not None: + return 100 - pos + return None @property def is_closed(self) -> bool | None: @@ -155,14 +147,12 @@ class KNXCover(KnxEntity, CoverEntity): async def async_stop_cover(self, **kwargs: Any) -> None: """Stop the cover.""" await self._device.stop() - self.stop_auto_updater() @property def current_cover_tilt_position(self) -> int | None: """Return current tilt position of cover.""" - if self._device.supports_angle: - ang = self._device.current_angle() - return 100 - ang if ang is not None else None + if (angle := self._device.current_angle()) is not None: + return 100 - angle return None async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: @@ -181,25 +171,3 @@ class KNXCover(KnxEntity, CoverEntity): async def async_stop_cover_tilt(self, **kwargs: Any) -> None: """Stop the cover tilt.""" await self._device.stop() - self.stop_auto_updater() - - def start_auto_updater(self) -> None: - """Start the autoupdater to update Home Assistant while cover is moving.""" - if self._unsubscribe_auto_updater is None: - self._unsubscribe_auto_updater = async_track_utc_time_change( - self.hass, self.auto_updater_hook - ) - - def stop_auto_updater(self) -> None: - """Stop the autoupdater.""" - if self._unsubscribe_auto_updater is not None: - self._unsubscribe_auto_updater() - self._unsubscribe_auto_updater = None - - @callback - def auto_updater_hook(self, now: datetime) -> None: - """Call for the autoupdater.""" - self.async_write_ha_state() - if self._device.position_reached(): - self.hass.async_create_task(self._device.auto_stop_if_necessary()) - self.stop_auto_updater() diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index 00b4c6cdc5f..b8f9bdcbd30 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -3,7 +3,7 @@ "name": "KNX", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.21.2"], + "requirements": ["xknx==0.21.3"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "platinum", "iot_class": "local_push", diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index 018db071adf..c8161462d66 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -101,7 +101,7 @@ "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", "multicast_port": "Used for routing and discovery. Default: `3671`", "local_ip": "Use `0.0.0.0` for auto-discovery.", - "state_updater": "Globally enable or disable reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve states from the KNX Bus, `sync_state` entity options will have no effect.", + "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options.", "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40" } }, diff --git a/requirements_all.txt b/requirements_all.txt index 687962e94b6..3fa07679b1a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2454,7 +2454,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.21.2 +xknx==0.21.3 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 05a7207c01a..9195dc04ff4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1612,7 +1612,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.21.2 +xknx==0.21.3 # homeassistant.components.bluesound # homeassistant.components.fritz From c7b4aca998936da50ce4dabe8db138aebe75f33c Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 15:29:22 +0200 Subject: [PATCH 525/930] Add more to no implicit reexport modules (#71947) --- mypy.ini | 7 +++++++ script/hassfest/mypy_config.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/mypy.ini b/mypy.ini index 898c6b860f0..0f698bda35d 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2271,6 +2271,7 @@ disallow_untyped_defs = true no_implicit_optional = true warn_return_any = true warn_unreachable = true +no_implicit_reexport = true [mypy-homeassistant.components.uptime.*] check_untyped_defs = true @@ -2514,6 +2515,12 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.application_credentials.*] +no_implicit_reexport = true + +[mypy-homeassistant.components.spotify.*] +no_implicit_reexport = true + [mypy-homeassistant.components.diagnostics.*] no_implicit_reexport = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 2b7c1e00f94..3517307548b 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -199,8 +199,11 @@ IGNORED_MODULES: Final[list[str]] = [ # Component modules which should set no_implicit_reexport = true. NO_IMPLICIT_REEXPORT_MODULES: set[str] = { "homeassistant.components", + "homeassistant.components.application_credentials.*", "homeassistant.components.diagnostics.*", + "homeassistant.components.spotify.*", "homeassistant.components.stream.*", + "homeassistant.components.update.*", } HEADER: Final = """ From 69cc6ab5f1d58adc586c3b300a4f7f0cde2cd0c2 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 16:40:45 +0200 Subject: [PATCH 526/930] Clean up accessing entity_registry.async_get_registry helper via hass (#72005) --- homeassistant/auth/auth_store.py | 9 ++++----- .../components/binary_sensor/device_trigger.py | 7 +++---- homeassistant/components/device_automation/entity.py | 7 +++---- .../components/device_automation/toggle_entity.py | 11 +++++++---- homeassistant/components/emulated_kasa/__init__.py | 6 +++--- homeassistant/components/evohome/__init__.py | 3 ++- homeassistant/components/geniushub/__init__.py | 4 ++-- homeassistant/components/heos/__init__.py | 8 ++++---- homeassistant/components/homekit/aidmanager.py | 10 ++++------ homeassistant/components/nest/__init__.py | 4 ++-- homeassistant/components/risco/sensor.py | 5 ++--- homeassistant/components/samsungtv/__init__.py | 5 +++-- homeassistant/components/sensor/device_trigger.py | 7 +++---- homeassistant/components/seventeentrack/sensor.py | 8 ++++++-- homeassistant/components/shelly/utils.py | 4 ++-- homeassistant/components/unifi/switch.py | 8 +++++--- homeassistant/components/vera/config_flow.py | 6 ++---- homeassistant/components/withings/common.py | 7 ++----- homeassistant/helpers/service.py | 2 +- tests/auth/test_auth_store.py | 4 ++-- tests/components/kraken/test_sensor.py | 3 ++- tests/components/lcn/test_cover.py | 3 ++- tests/components/lcn/test_light.py | 3 ++- tests/components/lcn/test_switch.py | 3 ++- tests/components/nws/test_sensor.py | 5 +++-- tests/components/octoprint/test_binary_sensor.py | 5 +++-- tests/components/picnic/test_sensor.py | 5 ++--- tests/components/plex/test_device_handling.py | 9 +++++---- tests/components/prosegur/test_alarm_control_panel.py | 4 ++-- tests/components/subaru/test_lock.py | 3 ++- .../totalconnect/test_alarm_control_panel.py | 3 ++- 31 files changed, 89 insertions(+), 82 deletions(-) diff --git a/homeassistant/auth/auth_store.py b/homeassistant/auth/auth_store.py index 398a4ec839a..d2c3db79726 100644 --- a/homeassistant/auth/auth_store.py +++ b/homeassistant/auth/auth_store.py @@ -9,6 +9,7 @@ from logging import getLogger from typing import Any from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.util import dt as dt_util from . import models @@ -303,11 +304,9 @@ class AuthStore: async def _async_load_task(self) -> None: """Load the users.""" - [ent_reg, dev_reg, data] = await asyncio.gather( - self.hass.helpers.entity_registry.async_get_registry(), - self.hass.helpers.device_registry.async_get_registry(), - self._store.async_load(), - ) + dev_reg = dr.async_get(self.hass) + ent_reg = er.async_get(self.hass) + data = await self._store.async_load() # Make sure that we're not overriding data if 2 loads happened at the # same time diff --git a/homeassistant/components/binary_sensor/device_trigger.py b/homeassistant/components/binary_sensor/device_trigger.py index 9989e415242..5b20b991403 100644 --- a/homeassistant/components/binary_sensor/device_trigger.py +++ b/homeassistant/components/binary_sensor/device_trigger.py @@ -8,9 +8,8 @@ from homeassistant.components.device_automation.const import ( ) from homeassistant.components.homeassistant.triggers import state as state_trigger from homeassistant.const import CONF_ENTITY_ID, CONF_FOR, CONF_TYPE -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity import get_device_class -from homeassistant.helpers.entity_registry import async_entries_for_device from . import DOMAIN, BinarySensorDeviceClass @@ -280,11 +279,11 @@ async def async_attach_trigger(hass, config, action, automation_info): async def async_get_triggers(hass, device_id): """List device triggers.""" triggers = [] - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == DOMAIN ] diff --git a/homeassistant/components/device_automation/entity.py b/homeassistant/components/device_automation/entity.py index b27a8c67561..9fa878ea038 100644 --- a/homeassistant/components/device_automation/entity.py +++ b/homeassistant/components/device_automation/entity.py @@ -12,8 +12,7 @@ from homeassistant.components.automation import ( from homeassistant.components.homeassistant.triggers import state as state_trigger from homeassistant.const import CONF_ENTITY_ID, CONF_FOR, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant -from homeassistant.helpers import config_validation as cv -from homeassistant.helpers.entity_registry import async_entries_for_device +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.typing import ConfigType from . import DEVICE_TRIGGER_BASE_SCHEMA @@ -68,11 +67,11 @@ async def _async_get_automations( ) -> list[dict[str, str]]: """List device automations.""" automations: list[dict[str, str]] = [] - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == domain ] diff --git a/homeassistant/components/device_automation/toggle_entity.py b/homeassistant/components/device_automation/toggle_entity.py index 4559e88f9d3..4ae32927bf4 100644 --- a/homeassistant/components/device_automation/toggle_entity.py +++ b/homeassistant/components/device_automation/toggle_entity.py @@ -20,8 +20,11 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant, callback -from homeassistant.helpers import condition, config_validation as cv -from homeassistant.helpers.entity_registry import async_entries_for_device +from homeassistant.helpers import ( + condition, + config_validation as cv, + entity_registry as er, +) from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DEVICE_TRIGGER_BASE_SCHEMA, entity @@ -187,11 +190,11 @@ async def _async_get_automations( ) -> list[dict[str, str]]: """List device automations.""" automations: list[dict[str, str]] = [] - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == domain ] diff --git a/homeassistant/components/emulated_kasa/__init__.py b/homeassistant/components/emulated_kasa/__init__.py index 9643962ecb4..41198f922cf 100644 --- a/homeassistant/components/emulated_kasa/__init__.py +++ b/homeassistant/components/emulated_kasa/__init__.py @@ -14,8 +14,8 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_registry import RegistryEntry from homeassistant.helpers.template import Template, is_template_string from homeassistant.helpers.typing import ConfigType @@ -79,7 +79,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def validate_configs(hass, entity_configs): """Validate that entities exist and ensure templates are ready to use.""" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) for entity_id, entity_config in entity_configs.items(): if (state := hass.states.get(entity_id)) is None: _LOGGER.debug("Entity not found: %s", entity_id) @@ -108,7 +108,7 @@ async def validate_configs(hass, entity_configs): _LOGGER.debug("No power value defined for: %s", entity_id) -def get_system_unique_id(entity: RegistryEntry): +def get_system_unique_id(entity: er.RegistryEntry): """Determine the system wide unique_id for an entity.""" return f"{entity.platform}.{entity.domain}.{entity.unique_id}" diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 5f83caa8648..0085eb701d0 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -24,6 +24,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform @@ -297,7 +298,7 @@ def setup_service_functions(hass: HomeAssistant, broker): """Set the zone override (setpoint).""" entity_id = call.data[ATTR_ENTITY_ID] - registry = await hass.helpers.entity_registry.async_get_registry() + registry = er.async_get(hass) registry_entry = registry.async_get(entity_id) if registry_entry is None or registry_entry.platform != DOMAIN: diff --git a/homeassistant/components/geniushub/__init__.py b/homeassistant/components/geniushub/__init__.py index 947dd325064..3c5cc22af81 100644 --- a/homeassistant/components/geniushub/__init__.py +++ b/homeassistant/components/geniushub/__init__.py @@ -21,7 +21,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, ServiceCall, callback -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import ( @@ -146,7 +146,7 @@ def setup_service_functions(hass: HomeAssistant, broker): """Set the system mode.""" entity_id = call.data[ATTR_ENTITY_ID] - registry = await hass.helpers.entity_registry.async_get_registry() + registry = er.async_get(hass) registry_entry = registry.async_get(entity_id) if registry_entry is None or registry_entry.platform != DOMAIN: diff --git a/homeassistant/components/heos/__init__.py b/homeassistant/components/heos/__init__.py index dbd66e28307..a7f56e91368 100644 --- a/homeassistant/components/heos/__init__.py +++ b/homeassistant/components/heos/__init__.py @@ -12,6 +12,7 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError +from homeassistant.helpers import device_registry as dr, entity_registry as er import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, @@ -167,10 +168,9 @@ class ControllerManager: async def connect_listeners(self): """Subscribe to events of interest.""" - self._device_registry, self._entity_registry = await asyncio.gather( - self._hass.helpers.device_registry.async_get_registry(), - self._hass.helpers.entity_registry.async_get_registry(), - ) + self._device_registry = dr.async_get(self._hass) + self._entity_registry = er.async_get(self._hass) + # Handle controller events self._signals.append( self.controller.dispatcher.connect( diff --git a/homeassistant/components/homekit/aidmanager.py b/homeassistant/components/homekit/aidmanager.py index ddba9d02bcd..27bd6234d46 100644 --- a/homeassistant/components/homekit/aidmanager.py +++ b/homeassistant/components/homekit/aidmanager.py @@ -17,7 +17,7 @@ import random from fnvhash import fnv1a_32 from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.entity_registry import EntityRegistry, RegistryEntry +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.storage import Store from .util import get_aid_storage_filename_for_entry_id @@ -34,7 +34,7 @@ AID_MIN = 2 AID_MAX = 18446744073709551615 -def get_system_unique_id(entity: RegistryEntry) -> str: +def get_system_unique_id(entity: er.RegistryEntry) -> str: """Determine the system wide unique_id for an entity.""" return f"{entity.platform}.{entity.domain}.{entity.unique_id}" @@ -74,13 +74,11 @@ class AccessoryAidStorage: self.allocated_aids: set[int] = set() self._entry_id = entry_id self.store: Store | None = None - self._entity_registry: EntityRegistry | None = None + self._entity_registry: er.EntityRegistry | None = None async def async_initialize(self) -> None: """Load the latest AID data.""" - self._entity_registry = ( - await self.hass.helpers.entity_registry.async_get_registry() - ) + self._entity_registry = er.async_get(self.hass) aidstore = get_aid_storage_filename_for_entry_id(self._entry_id) self.store = Store(self.hass, AID_MANAGER_STORAGE_VERSION, aidstore) diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 076b9a58814..5a34df1d74b 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -42,7 +42,7 @@ from homeassistant.exceptions import ( HomeAssistantError, Unauthorized, ) -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity_registry import async_entries_for_device from homeassistant.helpers.typing import ConfigType @@ -266,7 +266,7 @@ class NestEventViewBase(HomeAssistantView, ABC): ) -> web.StreamResponse: """Start a GET request.""" user = request[KEY_HASS_USER] - entity_registry = await self.hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(self.hass) for entry in async_entries_for_device(entity_registry, device_id): if not user.permissions.check_entity(entry.entity_id, POLICY_READ): raise Unauthorized(entity_id=entry.entity_id) diff --git a/homeassistant/components/risco/sensor.py b/homeassistant/components/risco/sensor.py index 3f3dc221fac..6038c2911c9 100644 --- a/homeassistant/components/risco/sensor.py +++ b/homeassistant/components/risco/sensor.py @@ -3,6 +3,7 @@ from homeassistant.components.binary_sensor import DOMAIN as BS_DOMAIN from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util @@ -75,9 +76,7 @@ class RiscoSensor(CoordinatorEntity, SensorEntity): async def async_added_to_hass(self): """When entity is added to hass.""" - self._entity_registry = ( - await self.hass.helpers.entity_registry.async_get_registry() - ) + self._entity_registry = er.async_get(self.hass) self.async_on_remove( self.coordinator.async_add_listener(self._refresh_from_coordinator) ) diff --git a/homeassistant/components/samsungtv/__init__.py b/homeassistant/components/samsungtv/__init__.py index 49963734e83..b870aab62d4 100644 --- a/homeassistant/components/samsungtv/__init__.py +++ b/homeassistant/components/samsungtv/__init__.py @@ -26,6 +26,7 @@ from homeassistant.const import ( ) from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr, entity_registry as er import homeassistant.helpers.config_validation as cv from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.typing import ConfigType @@ -313,10 +314,10 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> # 1 -> 2: Unique ID format changed, so delete and re-import: if version == 1: - dev_reg = await hass.helpers.device_registry.async_get_registry() + dev_reg = dr.async_get(hass) dev_reg.async_clear_config_entry(config_entry.entry_id) - en_reg = await hass.helpers.entity_registry.async_get_registry() + en_reg = er.async_get(hass) en_reg.async_clear_config_entry(config_entry.entry_id) version = config_entry.version = 2 diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index f90022cf5f3..d760b92b31c 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -16,13 +16,12 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity import ( get_capability, get_device_class, get_unit_of_measurement, ) -from homeassistant.helpers.entity_registry import async_entries_for_device from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass @@ -159,11 +158,11 @@ async def async_attach_trigger(hass, config, action, automation_info): async def async_get_triggers(hass, device_id): """List device triggers.""" triggers = [] - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == DOMAIN ] diff --git a/homeassistant/components/seventeentrack/sensor.py b/homeassistant/components/seventeentrack/sensor.py index 36fabaa7337..12cdcc0680c 100644 --- a/homeassistant/components/seventeentrack/sensor.py +++ b/homeassistant/components/seventeentrack/sensor.py @@ -19,7 +19,11 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers import ( + aiohttp_client, + config_validation as cv, + entity_registry as er, +) from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -234,7 +238,7 @@ class SeventeenTrackPackageSensor(SensorEntity): """Remove entity itself.""" await self.async_remove(force_remove=True) - reg = await self.hass.helpers.entity_registry.async_get_registry() + reg = er.async_get(self.hass) entity_id = reg.async_get_entity_id( "sensor", "seventeentrack", diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 77c09283fbf..0398250df71 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -11,7 +11,7 @@ from aioshelly.rpc_device import RpcDevice from homeassistant.config_entries import ConfigEntry from homeassistant.const import EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import device_registry, singleton +from homeassistant.helpers import device_registry, entity_registry, singleton from homeassistant.helpers.typing import EventType from homeassistant.util.dt import utcnow @@ -34,7 +34,7 @@ async def async_remove_shelly_entity( hass: HomeAssistant, domain: str, unique_id: str ) -> None: """Remove a Shelly entity.""" - entity_reg = await hass.helpers.entity_registry.async_get_registry() + entity_reg = entity_registry.async_get(hass) entity_id = entity_reg.async_get_entity_id(domain, DOMAIN, unique_id) if entity_id: LOGGER.debug("Removing entity: %s", entity_id) diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 9151c81543a..67c4d5c4544 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -20,6 +20,7 @@ from homeassistant.components.switch import DOMAIN, SwitchDeviceClass, SwitchEnt from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_NAME from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import ( CONNECTION_NETWORK_MAC, DeviceEntryType, @@ -27,7 +28,6 @@ from homeassistant.helpers.device_registry import ( from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_entries_for_config_entry from homeassistant.helpers.restore_state import RestoreEntity from .const import ATTR_MANUFACTURER, DOMAIN as UNIFI_DOMAIN @@ -65,8 +65,10 @@ async def async_setup_entry( # Store previously known POE control entities in case their POE are turned off. known_poe_clients = [] - entity_registry = await hass.helpers.entity_registry.async_get_registry() - for entry in async_entries_for_config_entry(entity_registry, config_entry.entry_id): + entity_registry = er.async_get(hass) + for entry in er.async_entries_for_config_entry( + entity_registry, config_entry.entry_id + ): if not entry.unique_id.startswith(POE_SWITCH): continue diff --git a/homeassistant/components/vera/config_flow.py b/homeassistant/components/vera/config_flow.py index b2c4fe5e5db..319dcd031d0 100644 --- a/homeassistant/components/vera/config_flow.py +++ b/homeassistant/components/vera/config_flow.py @@ -14,7 +14,7 @@ from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_EXCLUDE, CONF_LIGHTS, CONF_SOURCE from homeassistant.core import callback -from homeassistant.helpers.entity_registry import EntityRegistry +from homeassistant.helpers import entity_registry as er from .const import CONF_CONTROLLER, CONF_LEGACY_UNIQUE_ID, DOMAIN @@ -119,9 +119,7 @@ class VeraFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # If there are entities with the legacy unique_id, then this imported config # should also use the legacy unique_id for entity creation. - entity_registry: EntityRegistry = ( - await self.hass.helpers.entity_registry.async_get_registry() - ) + entity_registry = er.async_get(self.hass) use_legacy_unique_id = ( len( [ diff --git a/homeassistant/components/withings/common.py b/homeassistant/components/withings/common.py index c0dadb47924..78ae375b4bc 100644 --- a/homeassistant/components/withings/common.py +++ b/homeassistant/components/withings/common.py @@ -41,7 +41,7 @@ from homeassistant.const import ( ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.helpers import config_entry_oauth2_flow, entity_registry as er from homeassistant.helpers.config_entry_oauth2_flow import ( AUTH_CALLBACK_PATH, AbstractOAuth2Implementation, @@ -49,7 +49,6 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( OAuth2Session, ) from homeassistant.helpers.entity import Entity -from homeassistant.helpers.entity_registry import EntityRegistry from homeassistant.helpers.network import get_url from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.util import dt @@ -918,9 +917,7 @@ async def async_get_entity_id( hass: HomeAssistant, attribute: WithingsAttribute, user_id: int ) -> str | None: """Get an entity id for a user's attribute.""" - entity_registry: EntityRegistry = ( - await hass.helpers.entity_registry.async_get_registry() - ) + entity_registry = er.async_get(hass) unique_id = get_attribute_unique_id(attribute, user_id) entity_id = entity_registry.async_get_entity_id( diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 975b05067b2..9a1e6caa27e 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -791,7 +791,7 @@ def verify_domain_control( user_id=call.context.user_id, ) - reg = await hass.helpers.entity_registry.async_get_registry() + reg = entity_registry.async_get(hass) authorized = False diff --git a/tests/auth/test_auth_store.py b/tests/auth/test_auth_store.py index 0c650adba3c..e52a0dc88f5 100644 --- a/tests/auth/test_auth_store.py +++ b/tests/auth/test_auth_store.py @@ -236,9 +236,9 @@ async def test_loading_race_condition(hass): """Test only one storage load called when concurrent loading occurred .""" store = auth_store.AuthStore(hass) with patch( - "homeassistant.helpers.entity_registry.async_get_registry" + "homeassistant.helpers.entity_registry.async_get" ) as mock_ent_registry, patch( - "homeassistant.helpers.device_registry.async_get_registry" + "homeassistant.helpers.device_registry.async_get" ) as mock_dev_registry, patch( "homeassistant.helpers.storage.Store.async_load", return_value=None ) as mock_load: diff --git a/tests/components/kraken/test_sensor.py b/tests/components/kraken/test_sensor.py index bc37b67e676..cbfda938f99 100644 --- a/tests/components/kraken/test_sensor.py +++ b/tests/components/kraken/test_sensor.py @@ -11,6 +11,7 @@ from homeassistant.components.kraken.const import ( DOMAIN, ) from homeassistant.const import CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import DeviceEntryType import homeassistant.util.dt as dt_util @@ -52,7 +53,7 @@ async def test_sensor(hass): ) entry.add_to_hass(hass) - registry = await hass.helpers.entity_registry.async_get_registry() + registry = er.async_get(hass) # Pre-create registry entries for disabled by default sensors registry.async_get_or_create( diff --git a/tests/components/lcn/test_cover.py b/tests/components/lcn/test_cover.py index f89cfa41071..8c6b814e525 100644 --- a/tests/components/lcn/test_cover.py +++ b/tests/components/lcn/test_cover.py @@ -18,6 +18,7 @@ from homeassistant.const import ( STATE_OPENING, STATE_UNAVAILABLE, ) +from homeassistant.helpers import entity_registry as er from .conftest import MockModuleConnection @@ -35,7 +36,7 @@ async def test_setup_lcn_cover(hass, entry, lcn_connection): async def test_entity_attributes(hass, entry, lcn_connection): """Test the attributes of an entity.""" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entity_outputs = entity_registry.async_get("cover.cover_outputs") diff --git a/tests/components/lcn/test_light.py b/tests/components/lcn/test_light.py index c74ecd2beb9..efde0daa68f 100644 --- a/tests/components/lcn/test_light.py +++ b/tests/components/lcn/test_light.py @@ -23,6 +23,7 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.helpers import entity_registry as er from .conftest import MockModuleConnection @@ -54,7 +55,7 @@ async def test_entity_state(hass, lcn_connection): async def test_entity_attributes(hass, entry, lcn_connection): """Test the attributes of an entity.""" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entity_output = entity_registry.async_get("light.light_output1") diff --git a/tests/components/lcn/test_switch.py b/tests/components/lcn/test_switch.py index aee063f310d..8c4fb1ff0a8 100644 --- a/tests/components/lcn/test_switch.py +++ b/tests/components/lcn/test_switch.py @@ -15,6 +15,7 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.helpers import entity_registry as er from .conftest import MockModuleConnection @@ -34,7 +35,7 @@ async def test_setup_lcn_switch(hass, lcn_connection): async def test_entity_attributes(hass, entry, lcn_connection): """Test the attributes of an entity.""" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entity_output = entity_registry.async_get("switch.switch_output1") diff --git a/tests/components/nws/test_sensor.py b/tests/components/nws/test_sensor.py index aa5ca3bf66c..5a55907e53b 100644 --- a/tests/components/nws/test_sensor.py +++ b/tests/components/nws/test_sensor.py @@ -4,6 +4,7 @@ import pytest from homeassistant.components.nws.const import ATTRIBUTION, DOMAIN, SENSOR_TYPES from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ATTR_ATTRIBUTION, STATE_UNKNOWN +from homeassistant.helpers import entity_registry as er from homeassistant.util import slugify from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM @@ -33,7 +34,7 @@ async def test_imperial_metric( hass, units, result_observation, result_forecast, mock_simple_nws, no_weather ): """Test with imperial and metric units.""" - registry = await hass.helpers.entity_registry.async_get_registry() + registry = er.async_get(hass) for description in SENSOR_TYPES: registry.async_get_or_create( @@ -66,7 +67,7 @@ async def test_none_values(hass, mock_simple_nws, no_weather): instance = mock_simple_nws.return_value instance.observation = NONE_OBSERVATION - registry = await hass.helpers.entity_registry.async_get_registry() + registry = er.async_get(hass) for description in SENSOR_TYPES: registry.async_get_or_create( diff --git a/tests/components/octoprint/test_binary_sensor.py b/tests/components/octoprint/test_binary_sensor.py index 55e240eb282..e4a028f346a 100644 --- a/tests/components/octoprint/test_binary_sensor.py +++ b/tests/components/octoprint/test_binary_sensor.py @@ -1,6 +1,7 @@ """The tests for Octoptint binary sensor module.""" from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.helpers import entity_registry as er from . import init_integration @@ -16,7 +17,7 @@ async def test_sensors(hass): } await init_integration(hass, "binary_sensor", printer=printer) - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) state = hass.states.get("binary_sensor.octoprint_printing") assert state is not None @@ -37,7 +38,7 @@ async def test_sensors_printer_offline(hass): """Test the underlying sensors when the printer is offline.""" await init_integration(hass, "binary_sensor", printer=None) - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) state = hass.states.get("binary_sensor.octoprint_printing") assert state is not None diff --git a/tests/components/picnic/test_sensor.py b/tests/components/picnic/test_sensor.py index 7b1bdeb1d12..808f1ce6f41 100644 --- a/tests/components/picnic/test_sensor.py +++ b/tests/components/picnic/test_sensor.py @@ -17,6 +17,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.util import dt @@ -97,9 +98,7 @@ class TestPicnicSensor(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): """Set up things to be run when tests are started.""" self.hass = await async_test_home_assistant(None) - self.entity_registry = ( - await self.hass.helpers.entity_registry.async_get_registry() - ) + self.entity_registry = er.async_get(self.hass) # Patch the api client self.picnic_patcher = patch("homeassistant.components.picnic.PicnicAPI") diff --git a/tests/components/plex/test_device_handling.py b/tests/components/plex/test_device_handling.py index 2c23ee7cb09..6d2b7524d34 100644 --- a/tests/components/plex/test_device_handling.py +++ b/tests/components/plex/test_device_handling.py @@ -2,14 +2,15 @@ from homeassistant.components.plex.const import DOMAIN from homeassistant.const import Platform +from homeassistant.helpers import device_registry as dr, entity_registry as er async def test_cleanup_orphaned_devices(hass, entry, setup_plex_server): """Test cleaning up orphaned devices on startup.""" test_device_id = {(DOMAIN, "temporary_device_123")} - device_registry = await hass.helpers.device_registry.async_get_registry() - entity_registry = await hass.helpers.entity_registry.async_get_registry() + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) test_device = device_registry.async_get_or_create( config_entry_id=entry.entry_id, @@ -44,8 +45,8 @@ async def test_migrate_transient_devices( non_plexweb_device_id = {(DOMAIN, "1234567890123456-com-plexapp-android")} plex_client_service_device_id = {(DOMAIN, "plex.tv-clients")} - device_registry = await hass.helpers.device_registry.async_get_registry() - entity_registry = await hass.helpers.entity_registry.async_get_registry() + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) # Pre-create devices and entities to test device migration plexweb_device = device_registry.async_get_or_create( diff --git a/tests/components/prosegur/test_alarm_control_panel.py b/tests/components/prosegur/test_alarm_control_panel.py index 9ab0c0d37de..8a50319047e 100644 --- a/tests/components/prosegur/test_alarm_control_panel.py +++ b/tests/components/prosegur/test_alarm_control_panel.py @@ -18,7 +18,7 @@ from homeassistant.const import ( STATE_ALARM_DISARMED, STATE_UNAVAILABLE, ) -from homeassistant.helpers import entity_component +from homeassistant.helpers import entity_component, entity_registry as er from .common import CONTRACT, setup_platform @@ -49,7 +49,7 @@ def mock_status(request): async def test_entity_registry(hass, mock_auth, mock_status): """Tests that the devices are registered in the entity registry.""" await setup_platform(hass) - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entry = entity_registry.async_get(PROSEGUR_ALARM_ENTITY) # Prosegur alarm device unique_id is the contract id associated to the alarm account diff --git a/tests/components/subaru/test_lock.py b/tests/components/subaru/test_lock.py index 19918ba205c..7ccc1e5fdf5 100644 --- a/tests/components/subaru/test_lock.py +++ b/tests/components/subaru/test_lock.py @@ -13,6 +13,7 @@ from homeassistant.components.subaru.const import ( ) from homeassistant.const import ATTR_ENTITY_ID, SERVICE_LOCK, SERVICE_UNLOCK from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er from .conftest import MOCK_API @@ -23,7 +24,7 @@ DEVICE_ID = "lock.test_vehicle_2_door_locks" async def test_device_exists(hass, ev_entry): """Test subaru lock entity exists.""" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entry = entity_registry.async_get(DEVICE_ID) assert entry diff --git a/tests/components/totalconnect/test_alarm_control_panel.py b/tests/components/totalconnect/test_alarm_control_panel.py index 3066dfff172..07cb5f3d40a 100644 --- a/tests/components/totalconnect/test_alarm_control_panel.py +++ b/tests/components/totalconnect/test_alarm_control_panel.py @@ -30,6 +30,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er from homeassistant.util import dt from .common import ( @@ -76,7 +77,7 @@ async def test_attributes(hass: HomeAssistant) -> None: mock_request.assert_called_once() assert state.attributes.get(ATTR_FRIENDLY_NAME) == "test" - entity_registry = await hass.helpers.entity_registry.async_get_registry() + entity_registry = er.async_get(hass) entry = entity_registry.async_get(ENTITY_ID) # TotalConnect partition #1 alarm device unique_id is the location_id assert entry.unique_id == LOCATION_ID From 4d8593402ef033108c04f69e02674608306fa3c6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 17 May 2022 17:35:03 +0200 Subject: [PATCH 527/930] Fix no-implicit-reexport sorting issue (#72015) --- mypy.ini | 4 ++-- script/hassfest/mypy_config.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index 0f698bda35d..8f0b1868bce 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2518,10 +2518,10 @@ warn_unreachable = true [mypy-homeassistant.components.application_credentials.*] no_implicit_reexport = true -[mypy-homeassistant.components.spotify.*] +[mypy-homeassistant.components.diagnostics.*] no_implicit_reexport = true -[mypy-homeassistant.components.diagnostics.*] +[mypy-homeassistant.components.spotify.*] no_implicit_reexport = true [mypy-tests.*] diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 3517307548b..0b705fab983 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -367,7 +367,9 @@ def generate_and_validate(config: Config) -> str: if strict_module in NO_IMPLICIT_REEXPORT_MODULES: mypy_config.set(strict_section, "no_implicit_reexport", "true") - for reexport_module in NO_IMPLICIT_REEXPORT_MODULES.difference(strict_modules): + for reexport_module in sorted( + NO_IMPLICIT_REEXPORT_MODULES.difference(strict_modules) + ): reexport_section = f"mypy-{reexport_module}" mypy_config.add_section(reexport_section) mypy_config.set(reexport_section, "no_implicit_reexport", "true") From e4573273dcc91035fcb06ff4afd051c957de6963 Mon Sep 17 00:00:00 2001 From: Elad Bar <3207137+elad-bar@users.noreply.github.com> Date: Tue, 17 May 2022 18:57:19 +0300 Subject: [PATCH 528/930] Add Tuya Multi-functional Sensor (dgnbj) (#71778) Co-authored-by: Franck Nijhof --- .../components/tuya/binary_sensor.py | 73 +++++++++++++++++ homeassistant/components/tuya/number.py | 9 +++ homeassistant/components/tuya/select.py | 9 +++ homeassistant/components/tuya/sensor.py | 78 +++++++++++++++++++ homeassistant/components/tuya/siren.py | 8 ++ 5 files changed, 177 insertions(+) diff --git a/homeassistant/components/tuya/binary_sensor.py b/homeassistant/components/tuya/binary_sensor.py index 2c61151bfaf..d5e4a9b22b0 100644 --- a/homeassistant/components/tuya/binary_sensor.py +++ b/homeassistant/components/tuya/binary_sensor.py @@ -46,6 +46,79 @@ TAMPER_BINARY_SENSOR = TuyaBinarySensorEntityDescription( # end up being a binary sensor. # https://developer.tuya.com/en/docs/iot/standarddescription?id=K9i5ql6waswzq BINARY_SENSORS: dict[str, tuple[TuyaBinarySensorEntityDescription, ...]] = { + # Multi-functional Sensor + # https://developer.tuya.com/en/docs/iot/categorydgnbj?id=Kaiuz3yorvzg3 + "dgnbj": ( + TuyaBinarySensorEntityDescription( + key=DPCode.GAS_SENSOR_STATE, + name="Gas", + icon="mdi:gas-cylinder", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.CH4_SENSOR_STATE, + name="Methane", + device_class=BinarySensorDeviceClass.GAS, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.VOC_STATE, + name="Volatile Organic Compound", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.PM25_STATE, + name="Particulate Matter 2.5 µm", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.CO_STATE, + name="Carbon Monoxide", + icon="mdi:molecule-co", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.CO2_STATE, + icon="mdi:molecule-co2", + name="Carbon Dioxide", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.CH2O_STATE, + name="Formaldehyde", + device_class=BinarySensorDeviceClass.SAFETY, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.DOORCONTACT_STATE, + name="Door", + device_class=BinarySensorDeviceClass.DOOR, + ), + TuyaBinarySensorEntityDescription( + key=DPCode.WATERSENSOR_STATE, + name="Water Leak", + device_class=BinarySensorDeviceClass.MOISTURE, + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.PRESSURE_STATE, + name="Pressure", + on_value="alarm", + ), + TuyaBinarySensorEntityDescription( + key=DPCode.SMOKE_SENSOR_STATE, + name="Smoke", + icon="mdi:smoke-detector", + device_class=BinarySensorDeviceClass.SMOKE, + on_value="alarm", + ), + TAMPER_BINARY_SENSOR, + ), # CO2 Detector # https://developer.tuya.com/en/docs/iot/categoryco2bj?id=Kaiuz3wes7yuy "co2bj": ( diff --git a/homeassistant/components/tuya/number.py b/homeassistant/components/tuya/number.py index d9cde61a276..35efd78871f 100644 --- a/homeassistant/components/tuya/number.py +++ b/homeassistant/components/tuya/number.py @@ -18,6 +18,15 @@ from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType # default instructions set of each category end up being a number. # https://developer.tuya.com/en/docs/iot/standarddescription?id=K9i5ql6waswzq NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { + # Multi-functional Sensor + # https://developer.tuya.com/en/docs/iot/categorydgnbj?id=Kaiuz3yorvzg3 + "dgnbj": ( + NumberEntityDescription( + key=DPCode.ALARM_TIME, + name="Time", + entity_category=EntityCategory.CONFIG, + ), + ), # Smart Kettle # https://developer.tuya.com/en/docs/iot/fbh?id=K9gf484m21yq7 "bh": ( diff --git a/homeassistant/components/tuya/select.py b/homeassistant/components/tuya/select.py index d9103b916f4..974268c109f 100644 --- a/homeassistant/components/tuya/select.py +++ b/homeassistant/components/tuya/select.py @@ -18,6 +18,15 @@ from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, DPType, TuyaDeviceClass # default instructions set of each category end up being a select. # https://developer.tuya.com/en/docs/iot/standarddescription?id=K9i5ql6waswzq SELECTS: dict[str, tuple[SelectEntityDescription, ...]] = { + # Multi-functional Sensor + # https://developer.tuya.com/en/docs/iot/categorydgnbj?id=Kaiuz3yorvzg3 + "dgnbj": ( + SelectEntityDescription( + key=DPCode.ALARM_VOLUME, + name="Volume", + entity_category=EntityCategory.CONFIG, + ), + ), # Coffee maker # https://developer.tuya.com/en/docs/iot/categorykfj?id=Kaiuz2p12pc7f "kfj": ( diff --git a/homeassistant/components/tuya/sensor.py b/homeassistant/components/tuya/sensor.py index 3ee88d2d57b..acb2ffe7987 100644 --- a/homeassistant/components/tuya/sensor.py +++ b/homeassistant/components/tuya/sensor.py @@ -82,6 +82,84 @@ BATTERY_SENSORS: tuple[TuyaSensorEntityDescription, ...] = ( # end up being a sensor. # https://developer.tuya.com/en/docs/iot/standarddescription?id=K9i5ql6waswzq SENSORS: dict[str, tuple[TuyaSensorEntityDescription, ...]] = { + # Multi-functional Sensor + # https://developer.tuya.com/en/docs/iot/categorydgnbj?id=Kaiuz3yorvzg3 + "dgnbj": ( + TuyaSensorEntityDescription( + key=DPCode.GAS_SENSOR_VALUE, + name="Gas", + icon="mdi:gas-cylinder", + device_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.CH4_SENSOR_VALUE, + name="Methane", + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.VOC_VALUE, + name="Volatile Organic Compound", + device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.PM25_VALUE, + name="Particulate Matter 2.5 µm", + device_class=SensorDeviceClass.PM25, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.CO_VALUE, + name="Carbon Monoxide", + icon="mdi:molecule-co", + device_class=SensorDeviceClass.CO, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.CO2_VALUE, + name="Carbon Dioxide", + icon="mdi:molecule-co2", + device_class=SensorDeviceClass.CO2, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.CH2O_VALUE, + name="Formaldehyde", + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.BRIGHT_STATE, + name="Luminosity", + icon="mdi:brightness-6", + ), + TuyaSensorEntityDescription( + key=DPCode.BRIGHT_VALUE, + name="Luminosity", + icon="mdi:brightness-6", + device_class=SensorDeviceClass.ILLUMINANCE, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.TEMP_CURRENT, + name="Temperature", + device_class=SensorDeviceClass.TEMPERATURE, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.HUMIDITY_VALUE, + name="Humidity", + device_class=SensorDeviceClass.HUMIDITY, + state_class=SensorStateClass.MEASUREMENT, + ), + TuyaSensorEntityDescription( + key=DPCode.SMOKE_SENSOR_VALUE, + name="Smoke Amount", + icon="mdi:smoke-detector", + entity_category=EntityCategory.DIAGNOSTIC, + device_class=SensorStateClass.MEASUREMENT, + ), + *BATTERY_SENSORS, + ), # Smart Kettle # https://developer.tuya.com/en/docs/iot/fbh?id=K9gf484m21yq7 "bh": ( diff --git a/homeassistant/components/tuya/siren.py b/homeassistant/components/tuya/siren.py index dcb26871bb2..a60e24eca86 100644 --- a/homeassistant/components/tuya/siren.py +++ b/homeassistant/components/tuya/siren.py @@ -22,6 +22,14 @@ from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode # All descriptions can be found here: # https://developer.tuya.com/en/docs/iot/standarddescription?id=K9i5ql6waswzq SIRENS: dict[str, tuple[SirenEntityDescription, ...]] = { + # Multi-functional Sensor + # https://developer.tuya.com/en/docs/iot/categorydgnbj?id=Kaiuz3yorvzg3 + "dgnbj": ( + SirenEntityDescription( + key=DPCode.ALARM_SWITCH, + name="Siren", + ), + ), # Siren Alarm # https://developer.tuya.com/en/docs/iot/categorysgbj?id=Kaiuz37tlpbnu "sgbj": ( From 0b09376360eef91f95c227f4d2b958010729662d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 May 2022 09:05:49 -0700 Subject: [PATCH 529/930] Mobile app to notify when sensor is disabled (#71561) * Mobile app to notify when sensor is disabled * Add entity status to get_config * Allow overriding enabled/disabled --- homeassistant/components/mobile_app/const.py | 2 +- homeassistant/components/mobile_app/entity.py | 4 +- .../components/mobile_app/webhook.py | 47 +++++++++++-- tests/components/mobile_app/test_sensor.py | 69 ++++++++++++++++++- tests/components/mobile_app/test_webhook.py | 53 ++++++++++++-- 5 files changed, 162 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index e6a26430f11..efc105a80ea 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -67,7 +67,7 @@ ERR_INVALID_FORMAT = "invalid_format" ATTR_SENSOR_ATTRIBUTES = "attributes" ATTR_SENSOR_DEVICE_CLASS = "device_class" -ATTR_SENSOR_DEFAULT_DISABLED = "default_disabled" +ATTR_SENSOR_DISABLED = "disabled" ATTR_SENSOR_ENTITY_CATEGORY = "entity_category" ATTR_SENSOR_ICON = "icon" ATTR_SENSOR_NAME = "name" diff --git a/homeassistant/components/mobile_app/entity.py b/homeassistant/components/mobile_app/entity.py index b38774d56d6..d4c4374b8d9 100644 --- a/homeassistant/components/mobile_app/entity.py +++ b/homeassistant/components/mobile_app/entity.py @@ -9,8 +9,8 @@ from homeassistant.helpers.restore_state import RestoreEntity from .const import ( ATTR_SENSOR_ATTRIBUTES, - ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, + ATTR_SENSOR_DISABLED, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, ATTR_SENSOR_STATE, @@ -64,7 +64,7 @@ class MobileAppEntity(RestoreEntity): @property def entity_registry_enabled_default(self) -> bool: """Return if entity should be enabled by default.""" - return not self._config.get(ATTR_SENSOR_DEFAULT_DISABLED) + return not self._config.get(ATTR_SENSOR_DISABLED) @property def device_class(self): diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index cdf6fc874d9..97d386691d1 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -64,8 +64,8 @@ from .const import ( ATTR_NO_LEGACY_ENCRYPTION, ATTR_OS_VERSION, ATTR_SENSOR_ATTRIBUTES, - ATTR_SENSOR_DEFAULT_DISABLED, ATTR_SENSOR_DEVICE_CLASS, + ATTR_SENSOR_DISABLED, ATTR_SENSOR_ENTITY_CATEGORY, ATTR_SENSOR_ICON, ATTR_SENSOR_NAME, @@ -439,6 +439,11 @@ def _gen_unique_id(webhook_id, sensor_unique_id): return f"{webhook_id}_{sensor_unique_id}" +def _extract_sensor_unique_id(webhook_id, unique_id): + """Return a unique sensor ID.""" + return unique_id[len(webhook_id) + 1 :] + + @WEBHOOK_COMMANDS.register("register_sensor") @validate_schema( vol.All( @@ -457,7 +462,7 @@ def _gen_unique_id(webhook_id, sensor_unique_id): vol.Optional(ATTR_SENSOR_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA, vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon, vol.Optional(ATTR_SENSOR_STATE_CLASS): vol.In(SENSOSR_STATE_CLASSES), - vol.Optional(ATTR_SENSOR_DEFAULT_DISABLED): bool, + vol.Optional(ATTR_SENSOR_DISABLED): bool, }, _validate_state_class_sensor, ) @@ -490,6 +495,15 @@ async def webhook_register_sensor(hass, config_entry, data): ) != entry.original_name: changes["original_name"] = new_name + if ( + should_be_disabled := data.get(ATTR_SENSOR_DISABLED) + ) is None or should_be_disabled == entry.disabled: + pass + elif should_be_disabled: + changes["disabled_by"] = er.RegistryEntryDisabler.INTEGRATION + else: + changes["disabled_by"] = None + for ent_reg_key, data_key in ( ("device_class", ATTR_SENSOR_DEVICE_CLASS), ("unit_of_measurement", ATTR_SENSOR_UOM), @@ -551,6 +565,7 @@ async def webhook_update_sensor_states(hass, config_entry, data): device_name = config_entry.data[ATTR_DEVICE_NAME] resp = {} + entity_registry = er.async_get(hass) for sensor in data: entity_type = sensor[ATTR_SENSOR_TYPE] @@ -559,9 +574,10 @@ async def webhook_update_sensor_states(hass, config_entry, data): unique_store_key = _gen_unique_id(config_entry.data[CONF_WEBHOOK_ID], unique_id) - entity_registry = er.async_get(hass) - if not entity_registry.async_get_entity_id( - entity_type, DOMAIN, unique_store_key + if not ( + entity_id := entity_registry.async_get_entity_id( + entity_type, DOMAIN, unique_store_key + ) ): _LOGGER.debug( "Refusing to update %s non-registered sensor: %s", @@ -601,6 +617,12 @@ async def webhook_update_sensor_states(hass, config_entry, data): resp[unique_id] = {"success": True} + # Check if disabled + entry = entity_registry.async_get(entity_id) + + if entry.disabled_by: + resp[unique_id]["is_disabled"] = True + return webhook_response(resp, registration=config_entry.data) @@ -637,6 +659,21 @@ async def webhook_get_config(hass, config_entry, data): with suppress(hass.components.cloud.CloudNotAvailable): resp[CONF_REMOTE_UI_URL] = cloud.async_remote_ui_url(hass) + webhook_id = config_entry.data[CONF_WEBHOOK_ID] + + entities = {} + for entry in er.async_entries_for_config_entry( + er.async_get(hass), config_entry.entry_id + ): + if entry.domain in ("binary_sensor", "sensor"): + unique_id = _extract_sensor_unique_id(webhook_id, entry.unique_id) + else: + unique_id = entry.unique_id + + entities[unique_id] = {"disabled": entry.disabled} + + resp["entities"] = entities + return webhook_response(resp, registration=config_entry.data) diff --git a/tests/components/mobile_app/test_sensor.py b/tests/components/mobile_app/test_sensor.py index 301c49381f7..c0f7f126a49 100644 --- a/tests/components/mobile_app/test_sensor.py +++ b/tests/components/mobile_app/test_sensor.py @@ -355,7 +355,7 @@ async def test_default_disabling_entity(hass, create_registrations, webhook_clie "name": "Battery State", "type": "sensor", "unique_id": "battery_state", - "default_disabled": True, + "disabled": True, }, }, ) @@ -373,3 +373,70 @@ async def test_default_disabling_entity(hass, create_registrations, webhook_clie er.async_get(hass).async_get("sensor.test_1_battery_state").disabled_by == er.RegistryEntryDisabler.INTEGRATION ) + + +async def test_updating_disabled_sensor(hass, create_registrations, webhook_client): + """Test that sensors return error if disabled in instance.""" + webhook_id = create_registrations[1]["webhook_id"] + webhook_url = f"/api/webhook/{webhook_id}" + + reg_resp = await webhook_client.post( + webhook_url, + json={ + "type": "register_sensor", + "data": { + "name": "Battery State", + "state": None, + "type": "sensor", + "unique_id": "battery_state", + }, + }, + ) + + assert reg_resp.status == HTTPStatus.CREATED + + update_resp = await webhook_client.post( + webhook_url, + json={ + "type": "update_sensor_states", + "data": [ + { + "icon": "mdi:battery-unknown", + "state": 123, + "type": "sensor", + "unique_id": "battery_state", + }, + ], + }, + ) + + assert update_resp.status == HTTPStatus.OK + + json = await update_resp.json() + assert json["battery_state"]["success"] is True + assert "is_disabled" not in json["battery_state"] + + er.async_get(hass).async_update_entity( + "sensor.test_1_battery_state", disabled_by=er.RegistryEntryDisabler.USER + ) + + update_resp = await webhook_client.post( + webhook_url, + json={ + "type": "update_sensor_states", + "data": [ + { + "icon": "mdi:battery-unknown", + "state": 123, + "type": "sensor", + "unique_id": "battery_state", + }, + ], + }, + ) + + assert update_resp.status == HTTPStatus.OK + + json = await update_resp.json() + assert json["battery_state"]["success"] is True + assert json["battery_state"]["is_disabled"] is True diff --git a/tests/components/mobile_app/test_webhook.py b/tests/components/mobile_app/test_webhook.py index 3eac2d97b19..0bc237b1c11 100644 --- a/tests/components/mobile_app/test_webhook.py +++ b/tests/components/mobile_app/test_webhook.py @@ -254,10 +254,30 @@ async def test_webhook_handle_get_zones(hass, create_registrations, webhook_clie async def test_webhook_handle_get_config(hass, create_registrations, webhook_client): """Test that we can get config properly.""" - resp = await webhook_client.post( - "/api/webhook/{}".format(create_registrations[1]["webhook_id"]), - json={"type": "get_config"}, - ) + webhook_id = create_registrations[1]["webhook_id"] + webhook_url = f"/api/webhook/{webhook_id}" + + # Create two entities + for sensor in ( + { + "name": "Battery State", + "type": "sensor", + "unique_id": "battery-state-id", + }, + { + "name": "Battery Charging", + "type": "sensor", + "unique_id": "battery-charging-id", + "disabled": True, + }, + ): + reg_resp = await webhook_client.post( + webhook_url, + json={"type": "register_sensor", "data": sensor}, + ) + assert reg_resp.status == HTTPStatus.CREATED + + resp = await webhook_client.post(webhook_url, json={"type": "get_config"}) assert resp.status == HTTPStatus.OK @@ -279,6 +299,11 @@ async def test_webhook_handle_get_config(hass, create_registrations, webhook_cli "components": hass_config["components"], "version": hass_config["version"], "theme_color": "#03A9F4", # Default frontend theme color + "entities": { + "mock-device-id": {"disabled": False}, + "battery-state-id": {"disabled": False}, + "battery-charging-id": {"disabled": True}, + }, } assert expected_dict == json @@ -902,6 +927,7 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client): assert entry.unit_of_measurement is None assert entry.entity_category is None assert entry.original_icon == "mdi:cellphone" + assert entry.disabled_by is None reg_resp = await webhook_client.post( webhook_url, @@ -917,6 +943,7 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client): "entity_category": "diagnostic", "icon": "mdi:new-icon", "unit_of_measurement": "%", + "disabled": True, }, }, ) @@ -928,3 +955,21 @@ async def test_reregister_sensor(hass, create_registrations, webhook_client): assert entry.unit_of_measurement == "%" assert entry.entity_category == "diagnostic" assert entry.original_icon == "mdi:new-icon" + assert entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION + + reg_resp = await webhook_client.post( + webhook_url, + json={ + "type": "register_sensor", + "data": { + "name": "New Name", + "type": "sensor", + "unique_id": "abcd", + "disabled": False, + }, + }, + ) + + assert reg_resp.status == HTTPStatus.CREATED + entry = ent_reg.async_get("sensor.test_1_battery_state") + assert entry.disabled_by is None From c8f700c80319cef81a9a817c1b9111887ea98b1a Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 18:41:36 +0200 Subject: [PATCH 530/930] Clean up accessing dispatcher helpers via hass (#72014) Clean up accessing ditpatcher helpers via hass --- .../components/alarmdecoder/__init__.py | 11 ++++---- .../components/aqualogic/__init__.py | 3 ++- .../components/arcam_fmj/__init__.py | 11 +++----- .../components/arcam_fmj/media_player.py | 13 +++------- homeassistant/components/enocean/device.py | 4 +-- homeassistant/components/enocean/dongle.py | 4 +-- .../homekit_controller/connection.py | 5 ++-- .../components/mobile_app/webhook.py | 4 +-- .../components/owntracks/__init__.py | 9 ++++--- .../components/qwikswitch/__init__.py | 9 ++++--- homeassistant/components/rfxtrx/__init__.py | 7 +++-- .../components/tellstick/__init__.py | 9 ++++--- .../components/waterfurnace/__init__.py | 3 ++- .../components/websocket_api/http.py | 9 +++---- .../components/arcam_fmj/test_media_player.py | 26 ++++++------------- tests/components/cloud/test_binary_sensor.py | 5 ++-- 16 files changed, 64 insertions(+), 68 deletions(-) diff --git a/homeassistant/components/alarmdecoder/__init__.py b/homeassistant/components/alarmdecoder/__init__.py index 3be3d67e32b..c196ebb8fbc 100644 --- a/homeassistant/components/alarmdecoder/__init__.py +++ b/homeassistant/components/alarmdecoder/__init__.py @@ -15,6 +15,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.util import dt as dt_util from .const import ( @@ -81,23 +82,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: def handle_message(sender, message): """Handle message from AlarmDecoder.""" - hass.helpers.dispatcher.dispatcher_send(SIGNAL_PANEL_MESSAGE, message) + dispatcher_send(hass, SIGNAL_PANEL_MESSAGE, message) def handle_rfx_message(sender, message): """Handle RFX message from AlarmDecoder.""" - hass.helpers.dispatcher.dispatcher_send(SIGNAL_RFX_MESSAGE, message) + dispatcher_send(hass, SIGNAL_RFX_MESSAGE, message) def zone_fault_callback(sender, zone): """Handle zone fault from AlarmDecoder.""" - hass.helpers.dispatcher.dispatcher_send(SIGNAL_ZONE_FAULT, zone) + dispatcher_send(hass, SIGNAL_ZONE_FAULT, zone) def zone_restore_callback(sender, zone): """Handle zone restore from AlarmDecoder.""" - hass.helpers.dispatcher.dispatcher_send(SIGNAL_ZONE_RESTORE, zone) + dispatcher_send(hass, SIGNAL_ZONE_RESTORE, zone) def handle_rel_message(sender, message): """Handle relay or zone expander message from AlarmDecoder.""" - hass.helpers.dispatcher.dispatcher_send(SIGNAL_REL_MESSAGE, message) + dispatcher_send(hass, SIGNAL_REL_MESSAGE, message) baud = ad_connection.get(CONF_DEVICE_BAUD) if protocol == PROTOCOL_SOCKET: diff --git a/homeassistant/components/aqualogic/__init__.py b/homeassistant/components/aqualogic/__init__.py index 0c4ecaa1683..94941b30713 100644 --- a/homeassistant/components/aqualogic/__init__.py +++ b/homeassistant/components/aqualogic/__init__.py @@ -15,6 +15,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) @@ -70,7 +71,7 @@ class AquaLogicProcessor(threading.Thread): def data_changed(self, panel): """Aqualogic data changed callback.""" - self._hass.helpers.dispatcher.dispatcher_send(UPDATE_TOPIC) + dispatcher_send(self._hass, UPDATE_TOPIC) def run(self): """Event thread.""" diff --git a/homeassistant/components/arcam_fmj/__init__.py b/homeassistant/components/arcam_fmj/__init__.py index ee86fc8c4b5..a82e0239842 100644 --- a/homeassistant/components/arcam_fmj/__init__.py +++ b/homeassistant/components/arcam_fmj/__init__.py @@ -11,6 +11,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP, Platform from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import ConfigType from .const import ( @@ -81,7 +82,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def _run_client(hass, client, interval): def _listen(_): - hass.helpers.dispatcher.async_dispatcher_send(SIGNAL_CLIENT_DATA, client.host) + async_dispatcher_send(hass, SIGNAL_CLIENT_DATA, client.host) while True: try: @@ -89,9 +90,7 @@ async def _run_client(hass, client, interval): await client.start() _LOGGER.debug("Client connected %s", client.host) - hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_CLIENT_STARTED, client.host - ) + async_dispatcher_send(hass, SIGNAL_CLIENT_STARTED, client.host) try: with client.listen(_listen): @@ -100,9 +99,7 @@ async def _run_client(hass, client, interval): await client.stop() _LOGGER.debug("Client disconnected %s", client.host) - hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_CLIENT_STOPPED, client.host - ) + async_dispatcher_send(hass, SIGNAL_CLIENT_STOPPED, client.host) except ConnectionFailed: await asyncio.sleep(interval) diff --git a/homeassistant/components/arcam_fmj/media_player.py b/homeassistant/components/arcam_fmj/media_player.py index 245e309af9c..731eb0b0352 100644 --- a/homeassistant/components/arcam_fmj/media_player.py +++ b/homeassistant/components/arcam_fmj/media_player.py @@ -18,6 +18,7 @@ from homeassistant.components.media_player.errors import BrowseError from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -127,21 +128,15 @@ class ArcamFmj(MediaPlayerEntity): self.async_schedule_update_ha_state(force_refresh=True) self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_CLIENT_DATA, _data - ) + async_dispatcher_connect(self.hass, SIGNAL_CLIENT_DATA, _data) ) self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_CLIENT_STARTED, _started - ) + async_dispatcher_connect(self.hass, SIGNAL_CLIENT_STARTED, _started) ) self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - SIGNAL_CLIENT_STOPPED, _stopped - ) + async_dispatcher_connect(self.hass, SIGNAL_CLIENT_STOPPED, _stopped) ) async def async_update(self): diff --git a/homeassistant/components/enocean/device.py b/homeassistant/components/enocean/device.py index b57b053f4a7..0bd084742b5 100644 --- a/homeassistant/components/enocean/device.py +++ b/homeassistant/components/enocean/device.py @@ -2,7 +2,7 @@ from enocean.protocol.packet import Packet from enocean.utils import combine_hex -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from homeassistant.helpers.entity import Entity from .const import SIGNAL_RECEIVE_MESSAGE, SIGNAL_SEND_MESSAGE @@ -37,4 +37,4 @@ class EnOceanEntity(Entity): """Send a command via the EnOcean dongle.""" packet = Packet(packet_type, data=data, optional=optional) - self.hass.helpers.dispatcher.dispatcher_send(SIGNAL_SEND_MESSAGE, packet) + dispatcher_send(self.hass, SIGNAL_SEND_MESSAGE, packet) diff --git a/homeassistant/components/enocean/dongle.py b/homeassistant/components/enocean/dongle.py index 63ab3e86925..9ccaeba504c 100644 --- a/homeassistant/components/enocean/dongle.py +++ b/homeassistant/components/enocean/dongle.py @@ -7,7 +7,7 @@ from enocean.communicators import SerialCommunicator from enocean.protocol.packet import RadioPacket import serial -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from .const import SIGNAL_RECEIVE_MESSAGE, SIGNAL_SEND_MESSAGE @@ -58,7 +58,7 @@ class EnOceanDongle: if isinstance(packet, RadioPacket): _LOGGER.debug("Received radio packet: %s", packet) - self.hass.helpers.dispatcher.dispatcher_send(SIGNAL_RECEIVE_MESSAGE, packet) + dispatcher_send(self.hass, SIGNAL_RECEIVE_MESSAGE, packet) def detect(): diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 9642d5d3bc5..7a85e234807 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -19,6 +19,7 @@ from aiohomekit.model.services import Service from homeassistant.const import ATTR_VIA_DEVICE from homeassistant.core import CALLBACK_TYPE, callback from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.event import async_track_time_interval @@ -162,7 +163,7 @@ class HKDevice: if self.available == available: return self.available = available - self.hass.helpers.dispatcher.async_dispatcher_send(self.signal_state_updated) + async_dispatcher_send(self.hass, self.signal_state_updated) async def async_setup(self) -> bool: """Prepare to use a paired HomeKit device in Home Assistant.""" @@ -568,7 +569,7 @@ class HKDevice: # For now we update both self.entity_map.process_changes(new_values_dict) - self.hass.helpers.dispatcher.async_dispatcher_send(self.signal_state_updated) + async_dispatcher_send(self.hass, self.signal_state_updated) async def get_characteristics(self, *args, **kwargs) -> dict[str, Any]: """Read latest state from homekit accessory.""" diff --git a/homeassistant/components/mobile_app/webhook.py b/homeassistant/components/mobile_app/webhook.py index 97d386691d1..61b69ebad5c 100644 --- a/homeassistant/components/mobile_app/webhook.py +++ b/homeassistant/components/mobile_app/webhook.py @@ -353,8 +353,8 @@ async def webhook_render_template(hass, config_entry, data): ) async def webhook_update_location(hass, config_entry, data): """Handle an update location webhook.""" - hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_LOCATION_UPDATE.format(config_entry.entry_id), data + async_dispatcher_send( + hass, SIGNAL_LOCATION_UPDATE.format(config_entry.entry_id), data ) return empty_okay_response() diff --git a/homeassistant/components/owntracks/__init__.py b/homeassistant/components/owntracks/__init__.py index a9f89d26238..5a21862c767 100644 --- a/homeassistant/components/owntracks/__init__.py +++ b/homeassistant/components/owntracks/__init__.py @@ -18,7 +18,10 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.typing import ConfigType from homeassistant.setup import async_when_setup @@ -140,7 +143,7 @@ async def async_connect_mqtt(hass, component): return message["topic"] = msg.topic - hass.helpers.dispatcher.async_dispatcher_send(DOMAIN, hass, context, message) + async_dispatcher_send(hass, DOMAIN, hass, context, message) await mqtt.async_subscribe(hass, context.mqtt_topic, async_handle_mqtt_message, 1) @@ -179,7 +182,7 @@ async def handle_webhook(hass, webhook_id, request): # Keep it as a 200 response so the incorrect packet is discarded return json_response([]) - hass.helpers.dispatcher.async_dispatcher_send(DOMAIN, hass, context, message) + async_dispatcher_send(hass, DOMAIN, hass, context, message) response = [] diff --git a/homeassistant/components/qwikswitch/__init__.py b/homeassistant/components/qwikswitch/__init__.py index e529483d0cc..2df4a2ab73e 100644 --- a/homeassistant/components/qwikswitch/__init__.py +++ b/homeassistant/components/qwikswitch/__init__.py @@ -21,7 +21,10 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ConfigType @@ -150,7 +153,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: def callback_value_changed(_qsd, qsid, _val): """Update entity values based on device change.""" _LOGGER.debug("Dispatch %s (update from devices)", qsid) - hass.helpers.dispatcher.async_dispatcher_send(qsid, None) + async_dispatcher_send(hass, qsid, None) session = async_get_clientsession(hass) qsusb = QSUsb( @@ -221,7 +224,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if qspacket[QS_ID] in sensor_ids: _LOGGER.debug("Dispatch %s ((%s))", qspacket[QS_ID], qspacket) - hass.helpers.dispatcher.async_dispatcher_send(qspacket[QS_ID], qspacket) + async_dispatcher_send(hass, qspacket[QS_ID], qspacket) # Update all ha_objects hass.async_add_job(qsusb.update_from_devices) diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index c10075bbb79..89521aba9f2 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -30,7 +30,10 @@ from homeassistant.helpers.device_registry import ( DeviceEntry, DeviceRegistry, ) -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -214,7 +217,7 @@ async def async_setup_internal(hass, entry: ConfigEntry): event_data[ATTR_DEVICE_ID] = device_entry.id # Callback to HA registered components. - hass.helpers.dispatcher.async_dispatcher_send(SIGNAL_EVENT, event, device_id) + async_dispatcher_send(hass, SIGNAL_EVENT, event, device_id) # Signal event to any other listeners hass.bus.async_fire(EVENT_RFXTRX_EVENT, event_data) diff --git a/homeassistant/components/tellstick/__init__.py b/homeassistant/components/tellstick/__init__.py index 8611c99b654..1007867362a 100644 --- a/homeassistant/components/tellstick/__init__.py +++ b/homeassistant/components/tellstick/__init__.py @@ -17,7 +17,10 @@ from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import ConfigType @@ -147,8 +150,8 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: @callback def async_handle_callback(tellcore_id, tellcore_command, tellcore_data, cid): """Handle the actual callback from Tellcore.""" - hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_TELLCORE_CALLBACK, tellcore_id, tellcore_command, tellcore_data + async_dispatcher_send( + hass, SIGNAL_TELLCORE_CALLBACK, tellcore_id, tellcore_command, tellcore_data ) # Register callback diff --git a/homeassistant/components/waterfurnace/__init__.py b/homeassistant/components/waterfurnace/__init__.py index 1da170f2b75..107ae7b9d67 100644 --- a/homeassistant/components/waterfurnace/__init__.py +++ b/homeassistant/components/waterfurnace/__init__.py @@ -16,6 +16,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import config_validation as cv, discovery +from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) @@ -156,5 +157,5 @@ class WaterFurnaceData(threading.Thread): self._reconnect() else: - self.hass.helpers.dispatcher.dispatcher_send(UPDATE_TOPIC) + dispatcher_send(self.hass, UPDATE_TOPIC) time.sleep(SCAN_INTERVAL.total_seconds()) diff --git a/homeassistant/components/websocket_api/http.py b/homeassistant/components/websocket_api/http.py index a913b81c384..e8972a227c8 100644 --- a/homeassistant/components/websocket_api/http.py +++ b/homeassistant/components/websocket_api/http.py @@ -14,6 +14,7 @@ import async_timeout from homeassistant.components.http import HomeAssistantView from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later from .auth import AuthPhase, auth_required_message @@ -203,9 +204,7 @@ class WebSocketHandler: self.hass.data[DATA_CONNECTIONS] = ( self.hass.data.get(DATA_CONNECTIONS, 0) + 1 ) - self.hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_WEBSOCKET_CONNECTED - ) + async_dispatcher_send(self.hass, SIGNAL_WEBSOCKET_CONNECTED) # Command phase while not wsock.closed: @@ -258,8 +257,6 @@ class WebSocketHandler: if connection is not None: self.hass.data[DATA_CONNECTIONS] -= 1 - self.hass.helpers.dispatcher.async_dispatcher_send( - SIGNAL_WEBSOCKET_DISCONNECTED - ) + async_dispatcher_send(self.hass, SIGNAL_WEBSOCKET_DISCONNECTED) return wsock diff --git a/tests/components/arcam_fmj/test_media_player.py b/tests/components/arcam_fmj/test_media_player.py index 81f6a5a935f..d3b221a05fd 100644 --- a/tests/components/arcam_fmj/test_media_player.py +++ b/tests/components/arcam_fmj/test_media_player.py @@ -1,6 +1,6 @@ """Tests for arcam fmj receivers.""" from math import isclose -from unittest.mock import ANY, MagicMock, PropertyMock, patch +from unittest.mock import ANY, PropertyMock, patch from arcam.fmj import DecodeMode2CH, DecodeModeMCH, SourceCodes import pytest @@ -303,22 +303,12 @@ async def test_added_to_hass(player, state): SIGNAL_CLIENT_STOPPED, ) - connectors = {} + with patch( + "homeassistant.components.arcam_fmj.media_player.async_dispatcher_connect" + ) as connect: + await player.async_added_to_hass() - def _connect(signal, fun): - connectors[signal] = fun - - player.hass = MagicMock() - player.hass.helpers.dispatcher.async_dispatcher_connect.side_effects = _connect - - await player.async_added_to_hass() state.start.assert_called_with() - player.hass.helpers.dispatcher.async_dispatcher_connect.assert_any_call( - SIGNAL_CLIENT_DATA, ANY - ) - player.hass.helpers.dispatcher.async_dispatcher_connect.assert_any_call( - SIGNAL_CLIENT_STARTED, ANY - ) - player.hass.helpers.dispatcher.async_dispatcher_connect.assert_any_call( - SIGNAL_CLIENT_STOPPED, ANY - ) + connect.assert_any_call(player.hass, SIGNAL_CLIENT_DATA, ANY) + connect.assert_any_call(player.hass, SIGNAL_CLIENT_STARTED, ANY) + connect.assert_any_call(player.hass, SIGNAL_CLIENT_STOPPED, ANY) diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py index c9c9d53981e..0aacb189d72 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -2,6 +2,7 @@ from unittest.mock import Mock, patch from homeassistant.components.cloud.const import DISPATCHER_REMOTE_UPDATE +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component @@ -29,14 +30,14 @@ async def test_remote_connection_sensor(hass): with patch("homeassistant.components.cloud.binary_sensor.WAIT_UNTIL_CHANGE", 0): cloud.remote.is_connected = False cloud.remote.certificate = object() - hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + async_dispatcher_send(hass, DISPATCHER_REMOTE_UPDATE, {}) await hass.async_block_till_done() state = hass.states.get("binary_sensor.remote_ui") assert state.state == "off" cloud.remote.is_connected = True - hass.helpers.dispatcher.async_dispatcher_send(DISPATCHER_REMOTE_UPDATE, {}) + async_dispatcher_send(hass, DISPATCHER_REMOTE_UPDATE, {}) await hass.async_block_till_done() state = hass.states.get("binary_sensor.remote_ui") From 5f44d0f8f9db03371164ff596f7b09615a5f6efe Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 18:45:57 +0200 Subject: [PATCH 531/930] Clean up accessing storage.Store helper via hass (#72009) --- homeassistant/auth/auth_store.py | 7 ++--- homeassistant/auth/mfa_modules/notify.py | 9 ++++--- homeassistant/auth/mfa_modules/totp.py | 9 ++++--- homeassistant/auth/providers/homeassistant.py | 12 ++++++--- homeassistant/components/alexa/auth.py | 3 ++- .../components/ambiclimate/climate.py | 3 ++- .../components/ambiclimate/config_flow.py | 3 ++- .../components/analytics/analytics.py | 2 +- homeassistant/components/camera/prefs.py | 7 +++-- homeassistant/components/cloud/prefs.py | 3 ++- homeassistant/components/evohome/__init__.py | 3 ++- homeassistant/components/freebox/router.py | 3 ++- homeassistant/components/frontend/__init__.py | 8 +++--- homeassistant/components/frontend/storage.py | 6 +++-- homeassistant/components/hassio/__init__.py | 3 ++- homeassistant/components/http/auth.py | 5 ++-- homeassistant/components/icloud/__init__.py | 3 ++- .../components/icloud/config_flow.py | 9 +++---- .../components/mobile_app/__init__.py | 7 +++-- homeassistant/components/network/network.py | 5 ++-- .../components/smartthings/smartapp.py | 7 ++--- homeassistant/components/unifi/__init__.py | 3 ++- homeassistant/components/zha/core/store.py | 3 ++- homeassistant/core.py | 26 +++++++++++++++---- homeassistant/helpers/area_registry.py | 7 +++-- 25 files changed, 101 insertions(+), 55 deletions(-) diff --git a/homeassistant/auth/auth_store.py b/homeassistant/auth/auth_store.py index d2c3db79726..baf5a8bf3b3 100644 --- a/homeassistant/auth/auth_store.py +++ b/homeassistant/auth/auth_store.py @@ -10,6 +10,7 @@ from typing import Any from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.storage import Store from homeassistant.util import dt as dt_util from . import models @@ -45,8 +46,8 @@ class AuthStore: self._users: dict[str, models.User] | None = None self._groups: dict[str, models.Group] | None = None self._perm_lookup: PermissionLookup | None = None - self._store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True + self._store = Store( + hass, STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True ) self._lock = asyncio.Lock() @@ -315,7 +316,7 @@ class AuthStore: self._perm_lookup = perm_lookup = PermissionLookup(ent_reg, dev_reg) - if data is None: + if data is None or not isinstance(data, dict): self._set_defaults() return diff --git a/homeassistant/auth/mfa_modules/notify.py b/homeassistant/auth/mfa_modules/notify.py index 37b35a5087d..3872257a205 100644 --- a/homeassistant/auth/mfa_modules/notify.py +++ b/homeassistant/auth/mfa_modules/notify.py @@ -17,6 +17,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import ServiceNotFound from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.storage import Store from . import ( MULTI_FACTOR_AUTH_MODULE_SCHEMA, @@ -99,8 +100,8 @@ class NotifyAuthModule(MultiFactorAuthModule): """Initialize the user data store.""" super().__init__(hass, config) self._user_settings: _UsersDict | None = None - self._user_store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True + self._user_store = Store( + hass, STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True ) self._include = config.get(CONF_INCLUDE, []) self._exclude = config.get(CONF_EXCLUDE, []) @@ -118,7 +119,9 @@ class NotifyAuthModule(MultiFactorAuthModule): if self._user_settings is not None: return - if (data := await self._user_store.async_load()) is None: + if (data := await self._user_store.async_load()) is None or not isinstance( + data, dict + ): data = {STORAGE_USERS: {}} self._user_settings = { diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py index bb5fc47469f..e503198f08b 100644 --- a/homeassistant/auth/mfa_modules/totp.py +++ b/homeassistant/auth/mfa_modules/totp.py @@ -10,6 +10,7 @@ import voluptuous as vol from homeassistant.auth.models import User from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.storage import Store from . import ( MULTI_FACTOR_AUTH_MODULE_SCHEMA, @@ -76,8 +77,8 @@ class TotpAuthModule(MultiFactorAuthModule): """Initialize the user data store.""" super().__init__(hass, config) self._users: dict[str, str] | None = None - self._user_store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True + self._user_store = Store( + hass, STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True ) self._init_lock = asyncio.Lock() @@ -92,7 +93,9 @@ class TotpAuthModule(MultiFactorAuthModule): if self._users is not None: return - if (data := await self._user_store.async_load()) is None: + if (data := await self._user_store.async_load()) is None or not isinstance( + data, dict + ): data = {STORAGE_USERS: {}} self._users = data.get(STORAGE_USERS, {}) diff --git a/homeassistant/auth/providers/homeassistant.py b/homeassistant/auth/providers/homeassistant.py index 3971cb43080..cb95907c9b2 100644 --- a/homeassistant/auth/providers/homeassistant.py +++ b/homeassistant/auth/providers/homeassistant.py @@ -14,6 +14,7 @@ from homeassistant.const import CONF_ID from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.storage import Store from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow from ..models import Credentials, UserMeta @@ -60,8 +61,8 @@ class Data: def __init__(self, hass: HomeAssistant) -> None: """Initialize the user data store.""" self.hass = hass - self._store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True + self._store = Store( + hass, STORAGE_VERSION, STORAGE_KEY, private=True, atomic_writes=True ) self._data: dict[str, Any] | None = None # Legacy mode will allow usernames to start/end with whitespace @@ -79,7 +80,9 @@ class Data: async def async_load(self) -> None: """Load stored data.""" - if (data := await self._store.async_load()) is None: + if (data := await self._store.async_load()) is None or not isinstance( + data, dict + ): data = {"users": []} seen: set[str] = set() @@ -203,7 +206,8 @@ class Data: async def async_save(self) -> None: """Save data.""" - await self._store.async_save(self._data) + if self._data is not None: + await self._store.async_save(self._data) @AUTH_PROVIDERS.register("homeassistant") diff --git a/homeassistant/components/alexa/auth.py b/homeassistant/components/alexa/auth.py index d888b91a39e..1ce20d154bd 100644 --- a/homeassistant/components/alexa/auth.py +++ b/homeassistant/components/alexa/auth.py @@ -11,6 +11,7 @@ import async_timeout from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.core import callback from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.storage import Store from homeassistant.util import dt _LOGGER = logging.getLogger(__name__) @@ -37,7 +38,7 @@ class Auth: self.client_secret = client_secret self._prefs = None - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) self._get_token_lock = asyncio.Lock() diff --git a/homeassistant/components/ambiclimate/climate.py b/homeassistant/components/ambiclimate/climate.py index 0bf4ec35526..93d9348655f 100644 --- a/homeassistant/components/ambiclimate/climate.py +++ b/homeassistant/components/ambiclimate/climate.py @@ -23,6 +23,7 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import ( @@ -63,7 +64,7 @@ async def async_setup_entry( """Set up the Ambiclimate device from config entry.""" config = entry.data websession = async_get_clientsession(hass) - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) token_info = await store.async_load() oauth = ambiclimate.AmbiclimateOAuth( diff --git a/homeassistant/components/ambiclimate/config_flow.py b/homeassistant/components/ambiclimate/config_flow.py index e93c21ce5ba..bde5e41e392 100644 --- a/homeassistant/components/ambiclimate/config_flow.py +++ b/homeassistant/components/ambiclimate/config_flow.py @@ -10,6 +10,7 @@ from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.core import callback from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.network import get_url +from homeassistant.helpers.storage import Store from .const import ( AUTH_CALLBACK_NAME, @@ -102,7 +103,7 @@ class AmbiclimateFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.error("Failed to get access token", exc_info=True) return None - store = self.hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + store = Store(self.hass, STORAGE_VERSION, STORAGE_KEY) await store.async_save(token_info) return token_info diff --git a/homeassistant/components/analytics/analytics.py b/homeassistant/components/analytics/analytics.py index 39c813b2696..802aa33585a 100644 --- a/homeassistant/components/analytics/analytics.py +++ b/homeassistant/components/analytics/analytics.py @@ -71,7 +71,7 @@ class Analytics: ATTR_ONBOARDED: False, ATTR_UUID: None, } - self._store: Store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) @property def preferences(self) -> dict: diff --git a/homeassistant/components/camera/prefs.py b/homeassistant/components/camera/prefs.py index 53a149ff7d8..3d54c10d09a 100644 --- a/homeassistant/components/camera/prefs.py +++ b/homeassistant/components/camera/prefs.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Final from homeassistant.core import HomeAssistant +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import UNDEFINED, UndefinedType from .const import DOMAIN, PREF_PRELOAD_STREAM @@ -35,12 +36,14 @@ class CameraPreferences: def __init__(self, hass: HomeAssistant) -> None: """Initialize camera prefs.""" self._hass = hass - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) self._prefs: dict[str, dict[str, bool]] | None = None async def async_initialize(self) -> None: """Finish initializing the preferences.""" - if (prefs := await self._store.async_load()) is None: + if (prefs := await self._store.async_load()) is None or not isinstance( + prefs, dict + ): prefs = {} self._prefs = prefs diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index e6747c42c45..275c2a56326 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -5,6 +5,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.auth.models import User from homeassistant.components import webhook from homeassistant.core import callback +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import UNDEFINED from homeassistant.util.logging import async_create_catching_coro @@ -46,7 +47,7 @@ class CloudPreferences: def __init__(self, hass): """Initialize cloud prefs.""" self._hass = hass - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) self._prefs = None self._listeners = [] diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index 0085eb701d0..aa09bb666f4 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -35,6 +35,7 @@ from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_call_later from homeassistant.helpers.service import verify_domain_control +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util @@ -198,7 +199,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: user_data = tokens.pop(USER_DATA, None) return (tokens, user_data) - store = hass.helpers.storage.Store(STORAGE_VER, STORAGE_KEY) + store = Store(hass, STORAGE_VER, STORAGE_KEY) tokens, user_data = await load_auth_tokens(store) client_v2 = evohomeasync2.EvohomeClient( diff --git a/homeassistant/components/freebox/router.py b/homeassistant/components/freebox/router.py index 0d20545fdcd..70fc7b86a40 100644 --- a/homeassistant/components/freebox/router.py +++ b/homeassistant/components/freebox/router.py @@ -18,6 +18,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.storage import Store from homeassistant.util import slugify from .const import ( @@ -32,7 +33,7 @@ from .const import ( async def get_api(hass: HomeAssistant, host: str) -> Freepybox: """Get the Freebox API.""" - freebox_path = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY).path + freebox_path = Store(hass, STORAGE_VERSION, STORAGE_KEY).path if not os.path.exists(freebox_path): await hass.async_add_executor_job(os.makedirs, freebox_path) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 91b9c9a126f..c1deb02fc6a 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -22,6 +22,7 @@ from homeassistant.const import CONF_MODE, CONF_NAME, EVENT_THEMES_UPDATED from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.helpers import service import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.storage import Store from homeassistant.helpers.translation import async_get_translations from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_get_integration, bind_hass @@ -389,11 +390,12 @@ async def _async_setup_themes( """Set up themes data and services.""" hass.data[DATA_THEMES] = themes or {} - store = hass.data[DATA_THEMES_STORE] = hass.helpers.storage.Store( - THEMES_STORAGE_VERSION, THEMES_STORAGE_KEY + store = hass.data[DATA_THEMES_STORE] = Store( + hass, THEMES_STORAGE_VERSION, THEMES_STORAGE_KEY ) - theme_data = await store.async_load() or {} + if not (theme_data := await store.async_load()) or not isinstance(theme_data, dict): + theme_data = {} theme_name = theme_data.get(DATA_DEFAULT_THEME, DEFAULT_THEME) dark_theme_name = theme_data.get(DATA_DEFAULT_DARK_THEME) diff --git a/homeassistant/components/frontend/storage.py b/homeassistant/components/frontend/storage.py index e5100ca3ab2..bfda566de55 100644 --- a/homeassistant/components/frontend/storage.py +++ b/homeassistant/components/frontend/storage.py @@ -35,8 +35,10 @@ def with_store(orig_func: Callable) -> Callable: user_id = connection.user.id if (store := stores.get(user_id)) is None: - store = stores[user_id] = hass.helpers.storage.Store( - STORAGE_VERSION_USER_DATA, f"frontend.user_data_{connection.user.id}" + store = stores[user_id] = Store( + hass, + STORAGE_VERSION_USER_DATA, + f"frontend.user_data_{connection.user.id}", ) if user_id not in data: diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index a3689f61746..34773987014 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -41,6 +41,7 @@ from homeassistant.helpers.device_registry import ( async_get_registry, ) from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.loader import bind_hass @@ -519,7 +520,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: if not await hassio.is_connected(): _LOGGER.warning("Not connected with the supervisor / system too busy!") - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) if (data := await store.async_load()) is None: data = {} diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 4788680a6fa..dab6abede4c 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -17,6 +17,7 @@ from homeassistant.auth.const import GROUP_ID_READ_ONLY from homeassistant.auth.models import User from homeassistant.components import websocket_api from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.storage import Store from homeassistant.util import dt as dt_util from homeassistant.util.network import is_local @@ -108,8 +109,8 @@ def async_user_not_allowed_do_auth( async def async_setup_auth(hass: HomeAssistant, app: Application) -> None: """Create auth middleware for the app.""" - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) - if (data := await store.async_load()) is None: + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) + if (data := await store.async_load()) is None or not isinstance(data, dict): data = {} refresh_token = None diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py index 17cc15b195a..8175cf43f27 100644 --- a/homeassistant/components/icloud/__init__.py +++ b/homeassistant/components/icloud/__init__.py @@ -5,6 +5,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.storage import Store from homeassistant.util import slugify from .account import IcloudAccount @@ -81,7 +82,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=username) - icloud_dir = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + icloud_dir = Store(hass, STORAGE_VERSION, STORAGE_KEY) account = IcloudAccount( hass, diff --git a/homeassistant/components/icloud/config_flow.py b/homeassistant/components/icloud/config_flow.py index f3630abfdaa..a4c29acff75 100644 --- a/homeassistant/components/icloud/config_flow.py +++ b/homeassistant/components/icloud/config_flow.py @@ -13,6 +13,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.helpers.storage import Store from .const import ( CONF_GPS_ACCURACY_THRESHOLD, @@ -113,7 +114,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): PyiCloudService, self._username, self._password, - self.hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY).path, + Store(self.hass, STORAGE_VERSION, STORAGE_KEY).path, True, None, self._with_family, @@ -162,7 +163,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle a flow initiated by the user.""" errors = {} - icloud_dir = self.hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + icloud_dir = Store(self.hass, STORAGE_VERSION, STORAGE_KEY) if not os.path.exists(icloud_dir.path): await self.hass.async_add_executor_job(os.makedirs, icloud_dir.path) @@ -276,9 +277,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): PyiCloudService, self._username, self._password, - self.hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY - ).path, + Store(self.hass, STORAGE_VERSION, STORAGE_KEY).path, True, None, self._with_family, diff --git a/homeassistant/components/mobile_app/__init__.py b/homeassistant/components/mobile_app/__init__.py index 9f92285625b..1b308705624 100644 --- a/homeassistant/components/mobile_app/__init__.py +++ b/homeassistant/components/mobile_app/__init__.py @@ -10,6 +10,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_DEVICE_ID, CONF_WEBHOOK_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, discovery +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from . import websocket_api @@ -37,8 +38,10 @@ PLATFORMS = [Platform.SENSOR, Platform.BINARY_SENSOR, Platform.DEVICE_TRACKER] async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the mobile app component.""" - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) - if (app_config := await store.async_load()) is None: + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) + if (app_config := await store.async_load()) is None or not isinstance( + app_config, dict + ): app_config = { DATA_CONFIG_ENTRIES: {}, DATA_DELETED_IDS: [], diff --git a/homeassistant/components/network/network.py b/homeassistant/components/network/network.py index 6ec9941da3c..b2caf6438bd 100644 --- a/homeassistant/components/network/network.py +++ b/homeassistant/components/network/network.py @@ -6,6 +6,7 @@ from typing import Any, cast from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.singleton import singleton +from homeassistant.helpers.storage import Store from .const import ( ATTR_CONFIGURED_ADAPTERS, @@ -37,9 +38,7 @@ class Network: def __init__(self, hass: HomeAssistant) -> None: """Initialize the Network class.""" - self._store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, atomic_writes=True - ) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY, atomic_writes=True) self._data: dict[str, Any] = {} self.adapters: list[Adapter] = [] diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index a6d35c40335..28b60b57447 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -32,6 +32,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, ) from homeassistant.helpers.network import NoURLAvailableError, get_url +from homeassistant.helpers.storage import Store from .const import ( APP_NAME_PREFIX, @@ -210,8 +211,8 @@ async def setup_smartapp_endpoint(hass: HomeAssistant): return # Get/create config to store a unique id for this hass instance. - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) - if not (config := await store.async_load()): + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) + if not (config := await store.async_load()) or not isinstance(config, dict): # Create config config = { CONF_INSTANCE_ID: str(uuid4()), @@ -282,7 +283,7 @@ async def unload_smartapp_endpoint(hass: HomeAssistant): if cloudhook_url and cloud.async_is_logged_in(hass): await cloud.async_delete_cloudhook(hass, hass.data[DOMAIN][CONF_WEBHOOK_ID]) # Remove cloudhook from storage - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + store = Store(hass, STORAGE_VERSION, STORAGE_KEY) await store.async_save( { CONF_INSTANCE_ID: hass.data[DOMAIN][CONF_INSTANCE_ID], diff --git a/homeassistant/components/unifi/__init__.py b/homeassistant/components/unifi/__init__.py index a396f8ce38f..7a874aff993 100644 --- a/homeassistant/components/unifi/__init__.py +++ b/homeassistant/components/unifi/__init__.py @@ -4,6 +4,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from .const import ( @@ -106,7 +107,7 @@ class UnifiWirelessClients: """Set up client storage.""" self.hass = hass self.data = {} - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) async def async_load(self): """Load data from file.""" diff --git a/homeassistant/components/zha/core/store.py b/homeassistant/components/zha/core/store.py index 5d33375ace2..28983bdb427 100644 --- a/homeassistant/components/zha/core/store.py +++ b/homeassistant/components/zha/core/store.py @@ -10,6 +10,7 @@ from typing import cast import attr from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.storage import Store from homeassistant.loader import bind_hass from .typing import ZhaDeviceType @@ -38,7 +39,7 @@ class ZhaStorage: """Initialize the zha device storage.""" self.hass: HomeAssistant = hass self.devices: MutableMapping[str, ZhaDeviceEntry] = {} - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) @callback def async_create_device(self, device: ZhaDeviceType) -> ZhaDeviceEntry: diff --git a/homeassistant/core.py b/homeassistant/core.py index 973a940ffbc..7fcf07d9c66 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1895,11 +1895,19 @@ class Config: async def async_load(self) -> None: """Load [homeassistant] core config.""" - store = self.hass.helpers.storage.Store( - CORE_STORAGE_VERSION, CORE_STORAGE_KEY, private=True, atomic_writes=True + # Circular dep + # pylint: disable=import-outside-toplevel + from .helpers.storage import Store + + store = Store( + self.hass, + CORE_STORAGE_VERSION, + CORE_STORAGE_KEY, + private=True, + atomic_writes=True, ) - if not (data := await store.async_load()): + if not (data := await store.async_load()) or not isinstance(data, dict): return # In 2021.9 we fixed validation to disallow a path (because that's never correct) @@ -1931,6 +1939,10 @@ class Config: async def async_store(self) -> None: """Store [homeassistant] core config.""" + # Circular dep + # pylint: disable=import-outside-toplevel + from .helpers.storage import Store + data = { "latitude": self.latitude, "longitude": self.longitude, @@ -1943,7 +1955,11 @@ class Config: "currency": self.currency, } - store = self.hass.helpers.storage.Store( - CORE_STORAGE_VERSION, CORE_STORAGE_KEY, private=True, atomic_writes=True + store = Store( + self.hass, + CORE_STORAGE_VERSION, + CORE_STORAGE_KEY, + private=True, + atomic_writes=True, ) await store.async_save(data) diff --git a/homeassistant/helpers/area_registry.py b/homeassistant/helpers/area_registry.py index 660c433d8c7..ad4930825c8 100644 --- a/homeassistant/helpers/area_registry.py +++ b/homeassistant/helpers/area_registry.py @@ -12,6 +12,7 @@ from homeassistant.loader import bind_hass from homeassistant.util import slugify from . import device_registry as dr, entity_registry as er +from .storage import Store from .typing import UNDEFINED, UndefinedType DATA_REGISTRY = "area_registry" @@ -47,9 +48,7 @@ class AreaRegistry: """Initialize the area registry.""" self.hass = hass self.areas: MutableMapping[str, AreaEntry] = {} - self._store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY, atomic_writes=True - ) + self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY, atomic_writes=True) self._normalized_name_area_idx: dict[str, str] = {} @callback @@ -176,7 +175,7 @@ class AreaRegistry: areas: MutableMapping[str, AreaEntry] = OrderedDict() - if data is not None: + if isinstance(data, dict): for area in data["areas"]: normalized_name = normalize_area_name(area["name"]) areas[area["id"]] = AreaEntry( From 993e76a44dcc38647197e1b6c49be1bd78ae7a61 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 19:36:02 +0200 Subject: [PATCH 532/930] Increase timeout for running full suite tests (#72024) --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 50a809a5d21..0fb77fdc789 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -783,7 +783,7 @@ jobs: echo "::add-matcher::.github/workflows/matchers/pytest-slow.json" - name: Run pytest (fully) if: needs.changes.outputs.test_full_suite == 'true' - timeout-minutes: 45 + timeout-minutes: 60 run: | . venv/bin/activate python --version From 8f4caf414124f380a8f5e1d54aedb54a8f6c5c05 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 19:36:29 +0200 Subject: [PATCH 533/930] Clean up accessing event helpers via hass (#72011) --- .../components/alarmdecoder/__init__.py | 5 ++- .../components/alexa/state_report.py | 5 +-- homeassistant/components/camera/__init__.py | 3 +- homeassistant/components/evohome/__init__.py | 6 +-- homeassistant/components/freedns/__init__.py | 5 +-- .../google_assistant/report_state.py | 6 +-- .../components/google_domains/__init__.py | 3 +- homeassistant/components/hassio/__init__.py | 5 ++- homeassistant/components/homematic/entity.py | 7 ++-- homeassistant/components/no_ip/__init__.py | 3 +- .../components/universal/media_player.py | 10 +++-- homeassistant/scripts/benchmark/__init__.py | 16 ++++---- .../components/bayesian/test_binary_sensor.py | 9 ++-- .../components/universal/test_media_player.py | 5 ++- tests/helpers/test_event.py | 41 ++++++++----------- 15 files changed, 65 insertions(+), 64 deletions(-) diff --git a/homeassistant/components/alarmdecoder/__init__.py b/homeassistant/components/alarmdecoder/__init__.py index c196ebb8fbc..d5c1b88e08e 100644 --- a/homeassistant/components/alarmdecoder/__init__.py +++ b/homeassistant/components/alarmdecoder/__init__.py @@ -16,6 +16,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import async_track_point_in_time from homeassistant.util import dt as dt_util from .const import ( @@ -65,8 +66,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await hass.async_add_executor_job(controller.open, baud) except NoDeviceError: _LOGGER.debug("Failed to connect. Retrying in 5 seconds") - hass.helpers.event.async_track_point_in_time( - open_connection, dt_util.utcnow() + timedelta(seconds=5) + async_track_point_in_time( + hass, open_connection, dt_util.utcnow() + timedelta(seconds=5) ) return _LOGGER.debug("Established a connection with the alarmdecoder") diff --git a/homeassistant/components/alexa/state_report.py b/homeassistant/components/alexa/state_report.py index 161ac4072b6..d3e476c2e8c 100644 --- a/homeassistant/components/alexa/state_report.py +++ b/homeassistant/components/alexa/state_report.py @@ -12,6 +12,7 @@ import async_timeout from homeassistant.const import MATCH_ALL, STATE_ON from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.significant_change import create_checker import homeassistant.util.dt as dt_util @@ -102,9 +103,7 @@ async def async_enable_proactive_mode(hass, smart_home_config): hass, smart_home_config, alexa_changed_entity, alexa_properties ) - return hass.helpers.event.async_track_state_change( - MATCH_ALL, async_entity_state_listener - ) + return async_track_state_change(hass, MATCH_ALL, async_entity_state_listener) async def async_send_changereport_message( diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 31dede33e7a..4a6e1546f46 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -54,6 +54,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401 ) from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.network import get_url from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass @@ -398,7 +399,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: entity.async_update_token() entity.async_write_ha_state() - hass.helpers.event.async_track_time_interval(update_tokens, TOKEN_CHANGE_INTERVAL) + async_track_time_interval(hass, update_tokens, TOKEN_CHANGE_INTERVAL) component.async_register_entity_service( SERVICE_ENABLE_MOTION, {}, "async_enable_motion_detection" diff --git a/homeassistant/components/evohome/__init__.py b/homeassistant/components/evohome/__init__.py index aa09bb666f4..908dff48aef 100644 --- a/homeassistant/components/evohome/__init__.py +++ b/homeassistant/components/evohome/__init__.py @@ -33,7 +33,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, ) from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.event import async_call_later, async_track_time_interval from homeassistant.helpers.service import verify_domain_control from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType @@ -259,8 +259,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async_load_platform(hass, Platform.WATER_HEATER, DOMAIN, {}, config) ) - hass.helpers.event.async_track_time_interval( - broker.async_update, config[DOMAIN][CONF_SCAN_INTERVAL] + async_track_time_interval( + hass, broker.async_update, config[DOMAIN][CONF_SCAN_INTERVAL] ) setup_service_functions(hass, broker) diff --git a/homeassistant/components/freedns/__init__.py b/homeassistant/components/freedns/__init__.py index bc651e726ec..a5c507c3857 100644 --- a/homeassistant/components/freedns/__init__.py +++ b/homeassistant/components/freedns/__init__.py @@ -11,6 +11,7 @@ from homeassistant.const import CONF_ACCESS_TOKEN, CONF_SCAN_INTERVAL, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) @@ -56,9 +57,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Update the FreeDNS entry.""" await _update_freedns(hass, session, url, auth_token) - hass.helpers.event.async_track_time_interval( - update_domain_callback, update_interval - ) + async_track_time_interval(hass, update_domain_callback, update_interval) return True diff --git a/homeassistant/components/google_assistant/report_state.py b/homeassistant/components/google_assistant/report_state.py index c3f8ba3bffd..4e8ac1624cc 100644 --- a/homeassistant/components/google_assistant/report_state.py +++ b/homeassistant/components/google_assistant/report_state.py @@ -6,7 +6,7 @@ import logging from homeassistant.const import MATCH_ALL from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback -from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.event import async_call_later, async_track_state_change from homeassistant.helpers.significant_change import create_checker from .const import DOMAIN @@ -136,9 +136,7 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig await google_config.async_report_state_all({"devices": {"states": entities}}) - unsub = hass.helpers.event.async_track_state_change( - MATCH_ALL, async_entity_state_listener - ) + unsub = async_track_state_change(hass, MATCH_ALL, async_entity_state_listener) unsub = async_call_later(hass, INITIAL_REPORT_DELAY, initial_report) diff --git a/homeassistant/components/google_domains/__init__.py b/homeassistant/components/google_domains/__init__.py index 8ccc9d78c64..c7f7e632bd6 100644 --- a/homeassistant/components/google_domains/__init__.py +++ b/homeassistant/components/google_domains/__init__.py @@ -11,6 +11,7 @@ from homeassistant.const import CONF_DOMAIN, CONF_PASSWORD, CONF_TIMEOUT, CONF_U from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) @@ -56,7 +57,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Update the Google Domains entry.""" await _update_google_domains(hass, session, domain, user, password, timeout) - hass.helpers.event.async_track_time_interval(update_domain_interval, INTERVAL) + async_track_time_interval(hass, update_domain_interval, INTERVAL) return True diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 34773987014..3ded34bd6f1 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -41,6 +41,7 @@ from homeassistant.helpers.device_registry import ( async_get_registry, ) from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -638,8 +639,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: except HassioAPIError as err: _LOGGER.warning("Can't read Supervisor data: %s", err) - hass.helpers.event.async_track_point_in_utc_time( - update_info_data, utcnow() + HASSIO_UPDATE_INTERVAL + async_track_point_in_utc_time( + hass, update_info_data, utcnow() + HASSIO_UPDATE_INTERVAL ) # Fetch data diff --git a/homeassistant/components/homematic/entity.py b/homeassistant/components/homematic/entity.py index 1fe3799bbd9..fee68caf7ed 100644 --- a/homeassistant/components/homematic/entity.py +++ b/homeassistant/components/homematic/entity.py @@ -11,6 +11,7 @@ from pyhomematic.devicetypes.generic import HMGeneric from homeassistant.const import ATTR_NAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity, EntityDescription +from homeassistant.helpers.event import track_time_interval from .const import ( ATTR_ADDRESS, @@ -222,12 +223,10 @@ class HMHub(Entity): self._state = None # Load data - self.hass.helpers.event.track_time_interval(self._update_hub, SCAN_INTERVAL_HUB) + track_time_interval(self.hass, self._update_hub, SCAN_INTERVAL_HUB) self.hass.add_job(self._update_hub, None) - self.hass.helpers.event.track_time_interval( - self._update_variables, SCAN_INTERVAL_VARIABLES - ) + track_time_interval(self.hass, self._update_variables, SCAN_INTERVAL_VARIABLES) self.hass.add_job(self._update_variables, None) @property diff --git a/homeassistant/components/no_ip/__init__.py b/homeassistant/components/no_ip/__init__.py index 5340e926e62..a903b1af5b6 100644 --- a/homeassistant/components/no_ip/__init__.py +++ b/homeassistant/components/no_ip/__init__.py @@ -16,6 +16,7 @@ from homeassistant.helpers.aiohttp_client import ( async_get_clientsession, ) import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) @@ -76,7 +77,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Update the NO-IP entry.""" await _update_no_ip(hass, session, domain, auth_str, timeout) - hass.helpers.event.async_track_time_interval(update_domain_interval, INTERVAL) + async_track_time_interval(hass, update_domain_interval, INTERVAL) return True diff --git a/homeassistant/components/universal/media_player.py b/homeassistant/components/universal/media_player.py index e29a18f285f..7ffd8b9d13d 100644 --- a/homeassistant/components/universal/media_player.py +++ b/homeassistant/components/universal/media_player.py @@ -79,7 +79,11 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.event import TrackTemplate, async_track_template_result +from homeassistant.helpers.event import ( + TrackTemplate, + async_track_state_change_event, + async_track_template_result, +) from homeassistant.helpers.reload import async_setup_reload_service from homeassistant.helpers.service import async_call_from_config from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -202,8 +206,8 @@ class UniversalMediaPlayer(MediaPlayerEntity): depend.append(entity[0]) self.async_on_remove( - self.hass.helpers.event.async_track_state_change_event( - list(set(depend)), _async_on_dependency_update + async_track_state_change_event( + self.hass, list(set(depend)), _async_on_dependency_update ) ) diff --git a/homeassistant/scripts/benchmark/__init__.py b/homeassistant/scripts/benchmark/__init__.py index 8f95f7db66b..a681b3e210d 100644 --- a/homeassistant/scripts/benchmark/__init__.py +++ b/homeassistant/scripts/benchmark/__init__.py @@ -15,6 +15,10 @@ from homeassistant import core from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import EVENT_STATE_CHANGED from homeassistant.helpers.entityfilter import convert_include_exclude_filter +from homeassistant.helpers.event import ( + async_track_state_change, + async_track_state_change_event, +) from homeassistant.helpers.json import JSONEncoder # mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs @@ -134,9 +138,7 @@ async def state_changed_helper(hass): event.set() for idx in range(1000): - hass.helpers.event.async_track_state_change( - f"{entity_id}{idx}", listener, "off", "on" - ) + async_track_state_change(hass, f"{entity_id}{idx}", listener, "off", "on") event_data = { "entity_id": f"{entity_id}0", "old_state": core.State(entity_id, "off"), @@ -166,8 +168,8 @@ async def state_changed_event_helper(hass): nonlocal count count += 1 - hass.helpers.event.async_track_state_change_event( - [f"{entity_id}{idx}" for idx in range(1000)], listener + async_track_state_change_event( + hass, [f"{entity_id}{idx}" for idx in range(1000)], listener ) event_data = { @@ -201,8 +203,8 @@ async def state_changed_event_filter_helper(hass): nonlocal count count += 1 - hass.helpers.event.async_track_state_change_event( - [f"{entity_id}{idx}" for idx in range(1000)], listener + async_track_state_change_event( + hass, [f"{entity_id}{idx}" for idx in range(1000)], listener ) event_data = { diff --git a/tests/components/bayesian/test_binary_sensor.py b/tests/components/bayesian/test_binary_sensor.py index c2f289b0697..2f45e0e475e 100644 --- a/tests/components/bayesian/test_binary_sensor.py +++ b/tests/components/bayesian/test_binary_sensor.py @@ -16,6 +16,7 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import Context, callback +from homeassistant.helpers.event import async_track_state_change_event from homeassistant.setup import async_setup_component from tests.common import get_fixture_path @@ -709,8 +710,8 @@ async def test_template_triggers(hass): assert hass.states.get("binary_sensor.test_binary").state == STATE_OFF events = [] - hass.helpers.event.async_track_state_change_event( - "binary_sensor.test_binary", callback(lambda event: events.append(event)) + async_track_state_change_event( + hass, "binary_sensor.test_binary", callback(lambda event: events.append(event)) ) context = Context() @@ -748,8 +749,8 @@ async def test_state_triggers(hass): assert hass.states.get("binary_sensor.test_binary").state == STATE_OFF events = [] - hass.helpers.event.async_track_state_change_event( - "binary_sensor.test_binary", callback(lambda event: events.append(event)) + async_track_state_change_event( + hass, "binary_sensor.test_binary", callback(lambda event: events.append(event)) ) context = Context() diff --git a/tests/components/universal/test_media_player.py b/tests/components/universal/test_media_player.py index 4a1838aba12..d4fe2ce64ae 100644 --- a/tests/components/universal/test_media_player.py +++ b/tests/components/universal/test_media_player.py @@ -21,6 +21,7 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import Context, callback +from homeassistant.helpers.event import async_track_state_change_event from homeassistant.setup import async_setup_component from tests.common import async_mock_service, get_fixture_path @@ -1136,8 +1137,8 @@ async def test_master_state_with_template(hass): events = [] - hass.helpers.event.async_track_state_change_event( - "media_player.tv", callback(lambda event: events.append(event)) + async_track_state_change_event( + hass, "media_player.tv", callback(lambda event: events.append(event)) ) context = Context() diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index d0355bba5a8..57b89e64cce 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -21,6 +21,7 @@ from homeassistant.helpers.event import ( TrackTemplate, TrackTemplateResult, async_call_later, + async_track_entity_registry_updated_event, async_track_point_in_time, async_track_point_in_utc_time, async_track_same_state, @@ -4230,12 +4231,10 @@ async def test_track_point_in_utc_time_cancel(hass): with pytest.raises(TypeError): track_point_in_utc_time("nothass", run_callback, utc_now) - unsub1 = hass.helpers.event.track_point_in_utc_time( - run_callback, utc_now + timedelta(seconds=0.1) - ) - hass.helpers.event.track_point_in_utc_time( - run_callback, utc_now + timedelta(seconds=0.1) + unsub1 = track_point_in_utc_time( + hass, run_callback, utc_now + timedelta(seconds=0.1) ) + track_point_in_utc_time(hass, run_callback, utc_now + timedelta(seconds=0.1)) unsub1() @@ -4262,12 +4261,10 @@ async def test_async_track_point_in_time_cancel(hass): utc_now = dt_util.utcnow() hst_now = utc_now.astimezone(hst_tz) - unsub1 = hass.helpers.event.async_track_point_in_time( - run_callback, hst_now + timedelta(seconds=0.1) - ) - hass.helpers.event.async_track_point_in_time( - run_callback, hst_now + timedelta(seconds=0.1) + unsub1 = async_track_point_in_time( + hass, run_callback, hst_now + timedelta(seconds=0.1) ) + async_track_point_in_time(hass, run_callback, hst_now + timedelta(seconds=0.1)) unsub1() @@ -4292,11 +4289,9 @@ async def test_async_track_entity_registry_updated_event(hass): def run_callback(event): event_data.append(event.data) - unsub1 = hass.helpers.event.async_track_entity_registry_updated_event( - entity_id, run_callback - ) - unsub2 = hass.helpers.event.async_track_entity_registry_updated_event( - new_entity_id, run_callback + unsub1 = async_track_entity_registry_updated_event(hass, entity_id, run_callback) + unsub2 = async_track_entity_registry_updated_event( + hass, new_entity_id, run_callback ) hass.bus.async_fire( EVENT_ENTITY_REGISTRY_UPDATED, {"action": "create", "entity_id": entity_id} @@ -4362,12 +4357,10 @@ async def test_async_track_entity_registry_updated_event_with_a_callback_that_th def failing_callback(event): raise ValueError - unsub1 = hass.helpers.event.async_track_entity_registry_updated_event( - entity_id, failing_callback - ) - unsub2 = hass.helpers.event.async_track_entity_registry_updated_event( - entity_id, run_callback + unsub1 = async_track_entity_registry_updated_event( + hass, entity_id, failing_callback ) + unsub2 = async_track_entity_registry_updated_event(hass, entity_id, run_callback) hass.bus.async_fire( EVENT_ENTITY_REGISTRY_UPDATED, {"action": "create", "entity_id": entity_id} ) @@ -4380,11 +4373,11 @@ async def test_async_track_entity_registry_updated_event_with_a_callback_that_th async def test_async_track_entity_registry_updated_event_with_empty_list(hass): """Test async_track_entity_registry_updated_event passing an empty list of entities.""" - unsub_single = hass.helpers.event.async_track_entity_registry_updated_event( - [], ha.callback(lambda event: None) + unsub_single = async_track_entity_registry_updated_event( + hass, [], ha.callback(lambda event: None) ) - unsub_single2 = hass.helpers.event.async_track_entity_registry_updated_event( - [], ha.callback(lambda event: None) + unsub_single2 = async_track_entity_registry_updated_event( + hass, [], ha.callback(lambda event: None) ) unsub_single2() From 5433c0a5355f3167309e4aa7a8baeb7488ccd6cf Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 May 2022 10:43:58 -0700 Subject: [PATCH 534/930] Make sure empty get_events results is always a list (#72021) --- homeassistant/components/logbook/__init__.py | 2 +- tests/components/logbook/test_init.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 35fa17bb3a4..549518e875a 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -271,7 +271,7 @@ async def ws_get_events( return if start_time > utc_now: - connection.send_result(msg["id"], {}) + connection.send_result(msg["id"], []) return entity_ids = msg.get("entity_ids") diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index ed95b4d10bc..dddbbc61134 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -2391,6 +2391,7 @@ async def test_get_events_future_start_time(hass, hass_ws_client, recorder_mock) assert response["id"] == 1 results = response["result"] + assert isinstance(results, list) assert len(results) == 0 From c0da97b038ac43bbe4801cb9c3f56d341a2ab7c6 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 19:56:57 +0200 Subject: [PATCH 535/930] Clean up accessing service helpers via hass (#72013) --- .../components/cast/home_assistant_cast.py | 4 +++- homeassistant/components/cloud/__init__.py | 9 ++++---- .../components/homeassistant/__init__.py | 23 +++++++++++-------- .../components/homeassistant/scene.py | 5 ++-- homeassistant/components/homekit/__init__.py | 8 +++++-- .../components/huawei_lte/__init__.py | 4 +++- homeassistant/components/isy994/services.py | 21 +++++++++-------- homeassistant/components/template/__init__.py | 5 ++-- tests/components/script/test_init.py | 2 +- tests/helpers/test_service.py | 19 +++++++-------- 10 files changed, 55 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/cast/home_assistant_cast.py b/homeassistant/components/cast/home_assistant_cast.py index 2f9583f329c..010e4a046b4 100644 --- a/homeassistant/components/cast/home_assistant_cast.py +++ b/homeassistant/components/cast/home_assistant_cast.py @@ -9,6 +9,7 @@ from homeassistant.const import ATTR_ENTITY_ID from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, dispatcher from homeassistant.helpers.network import NoURLAvailableError, get_url +from homeassistant.helpers.service import async_register_admin_service from .const import DOMAIN, SIGNAL_HASS_CAST_SHOW_VIEW @@ -65,7 +66,8 @@ async def async_setup_ha_cast( call.data.get(ATTR_URL_PATH), ) - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, DOMAIN, SERVICE_SHOW_VIEW, handle_show_view, diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 360e726c89e..559453fe02b 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -26,6 +26,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) +from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass from homeassistant.util.aiohttp import MockRequest @@ -250,11 +251,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: elif service.service == SERVICE_REMOTE_DISCONNECT: await prefs.async_update(remote_enabled=False) - hass.helpers.service.async_register_admin_service( - DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler - ) - hass.helpers.service.async_register_admin_service( - DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler + async_register_admin_service(hass, DOMAIN, SERVICE_REMOTE_CONNECT, _service_handler) + async_register_admin_service( + hass, DOMAIN, SERVICE_REMOTE_DISCONNECT, _service_handler ) loaded = False diff --git a/homeassistant/components/homeassistant/__init__.py b/homeassistant/components/homeassistant/__init__.py index f79213e2484..81d79b03c10 100644 --- a/homeassistant/components/homeassistant/__init__.py +++ b/homeassistant/components/homeassistant/__init__.py @@ -27,6 +27,7 @@ from homeassistant.helpers.entity_component import async_update_entity from homeassistant.helpers.service import ( async_extract_config_entry_ids, async_extract_referenced_entity_ids, + async_register_admin_service, ) from homeassistant.helpers.typing import ConfigType @@ -206,14 +207,14 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no if tasks: await asyncio.wait(tasks) - hass.helpers.service.async_register_admin_service( - ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service ) - hass.helpers.service.async_register_admin_service( - ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service ) - hass.helpers.service.async_register_admin_service( - ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service ) hass.services.async_register( ha.DOMAIN, @@ -233,8 +234,8 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no # auth only processed during startup await conf_util.async_process_ha_core_config(hass, conf.get(ha.DOMAIN) or {}) - hass.helpers.service.async_register_admin_service( - ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config ) async def async_set_location(call: ha.ServiceCall) -> None: @@ -243,7 +244,8 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no latitude=call.data[ATTR_LATITUDE], longitude=call.data[ATTR_LONGITUDE] ) - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_SET_LOCATION, async_set_location, @@ -265,7 +267,8 @@ async def async_setup(hass: ha.HomeAssistant, config: ConfigType) -> bool: # no ) ) - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, ha.DOMAIN, SERVICE_RELOAD_CONFIG_ENTRY, async_handle_reload_config_entry, diff --git a/homeassistant/components/homeassistant/scene.py b/homeassistant/components/homeassistant/scene.py index 505dc0027d6..bf8affd902e 100644 --- a/homeassistant/components/homeassistant/scene.py +++ b/homeassistant/components/homeassistant/scene.py @@ -35,6 +35,7 @@ from homeassistant.helpers import ( entity_platform, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.state import async_reproduce_state from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.loader import async_get_integration @@ -206,9 +207,7 @@ async def async_setup_platform( hass.bus.async_fire(EVENT_SCENE_RELOADED, context=call.context) - hass.helpers.service.async_register_admin_service( - SCENE_DOMAIN, SERVICE_RELOAD, reload_config - ) + async_register_admin_service(hass, SCENE_DOMAIN, SERVICE_RELOAD, reload_config) async def apply_service(call: ServiceCall) -> None: """Apply a scene.""" diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 48f8f336fe7..1cba9746609 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -54,7 +54,10 @@ from homeassistant.helpers.entityfilter import ( EntityFilter, ) from homeassistant.helpers.reload import async_integration_yaml_config -from homeassistant.helpers.service import async_extract_referenced_entity_ids +from homeassistant.helpers.service import ( + async_extract_referenced_entity_ids, + async_register_admin_service, +) from homeassistant.helpers.typing import ConfigType from homeassistant.loader import IntegrationNotFound, async_get_integration @@ -461,7 +464,8 @@ def _async_register_events_and_services(hass: HomeAssistant) -> None: await asyncio.gather(*reload_tasks) - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, DOMAIN, SERVICE_RELOAD, _handle_homekit_reload, diff --git a/homeassistant/components/huawei_lte/__init__.py b/homeassistant/components/huawei_lte/__init__.py index d8afbd752a4..601a3b9af8d 100644 --- a/homeassistant/components/huawei_lte/__init__.py +++ b/homeassistant/components/huawei_lte/__init__.py @@ -47,6 +47,7 @@ from homeassistant.helpers import ( from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from .const import ( @@ -531,7 +532,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.error("%s: unsupported service", service.service) for service in ADMIN_SERVICES: - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, DOMAIN, service, service_handler, diff --git a/homeassistant/components/isy994/services.py b/homeassistant/components/isy994/services.py index 8323394803f..654b65309fc 100644 --- a/homeassistant/components/isy994/services.py +++ b/homeassistant/components/isy994/services.py @@ -19,6 +19,7 @@ from homeassistant.helpers import entity_platform import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import async_get_platforms import homeassistant.helpers.entity_registry as er +from homeassistant.helpers.service import entity_service_call from .const import ( _LOGGER, @@ -373,8 +374,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 ) async def _async_send_raw_node_command(call: ServiceCall) -> None: - await hass.helpers.service.entity_service_call( - async_get_platforms(hass, DOMAIN), "async_send_raw_node_command", call + await entity_service_call( + hass, async_get_platforms(hass, DOMAIN), "async_send_raw_node_command", call ) hass.services.async_register( @@ -385,8 +386,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 ) async def _async_send_node_command(call: ServiceCall) -> None: - await hass.helpers.service.entity_service_call( - async_get_platforms(hass, DOMAIN), "async_send_node_command", call + await entity_service_call( + hass, async_get_platforms(hass, DOMAIN), "async_send_node_command", call ) hass.services.async_register( @@ -397,8 +398,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 ) async def _async_get_zwave_parameter(call: ServiceCall) -> None: - await hass.helpers.service.entity_service_call( - async_get_platforms(hass, DOMAIN), "async_get_zwave_parameter", call + await entity_service_call( + hass, async_get_platforms(hass, DOMAIN), "async_get_zwave_parameter", call ) hass.services.async_register( @@ -409,8 +410,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 ) async def _async_set_zwave_parameter(call: ServiceCall) -> None: - await hass.helpers.service.entity_service_call( - async_get_platforms(hass, DOMAIN), "async_set_zwave_parameter", call + await entity_service_call( + hass, async_get_platforms(hass, DOMAIN), "async_set_zwave_parameter", call ) hass.services.async_register( @@ -421,8 +422,8 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901 ) async def _async_rename_node(call: ServiceCall) -> None: - await hass.helpers.service.entity_service_call( - async_get_platforms(hass, DOMAIN), "async_rename_node", call + await entity_service_call( + hass, async_get_platforms(hass, DOMAIN), "async_rename_node", call ) hass.services.async_register( diff --git a/homeassistant/components/template/__init__.py b/homeassistant/components/template/__init__.py index a1231708b91..47b51853bcd 100644 --- a/homeassistant/components/template/__init__.py +++ b/homeassistant/components/template/__init__.py @@ -19,6 +19,7 @@ from homeassistant.helpers import ( update_coordinator, ) from homeassistant.helpers.reload import async_reload_integration_platforms +from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_get_integration @@ -54,9 +55,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.bus.async_fire(f"event_{DOMAIN}_reloaded", context=call.context) - hass.helpers.service.async_register_admin_service( - DOMAIN, SERVICE_RELOAD, _reload_config - ) + async_register_admin_service(hass, DOMAIN, SERVICE_RELOAD, _reload_config) return True diff --git a/tests/components/script/test_init.py b/tests/components/script/test_init.py index 480bbcbc4f0..4ef4cd9d2b0 100644 --- a/tests/components/script/test_init.py +++ b/tests/components/script/test_init.py @@ -377,7 +377,7 @@ async def test_async_get_descriptions_script(hass): } await async_setup_component(hass, DOMAIN, script_config) - descriptions = await hass.helpers.service.async_get_all_descriptions() + descriptions = await async_get_all_descriptions(hass) assert descriptions[DOMAIN]["test1"]["description"] == "" assert not descriptions[DOMAIN]["test1"]["fields"] diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index cf87377dd8f..1e947a9353f 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -819,8 +819,9 @@ async def test_register_admin_service(hass, hass_read_only_user, hass_admin_user async def mock_service(call): calls.append(call) - hass.helpers.service.async_register_admin_service("test", "test", mock_service) - hass.helpers.service.async_register_admin_service( + service.async_register_admin_service(hass, "test", "test", mock_service) + service.async_register_admin_service( + hass, "test", "test2", mock_service, @@ -887,7 +888,7 @@ async def test_domain_control_not_async(hass, mock_entities): calls.append(call) with pytest.raises(exceptions.HomeAssistantError): - hass.helpers.service.verify_domain_control("test_domain")(mock_service_log) + service.verify_domain_control(hass, "test_domain")(mock_service_log) async def test_domain_control_unknown(hass, mock_entities): @@ -902,9 +903,9 @@ async def test_domain_control_unknown(hass, mock_entities): "homeassistant.helpers.entity_registry.async_get_registry", return_value=Mock(entities=mock_entities), ): - protected_mock_service = hass.helpers.service.verify_domain_control( - "test_domain" - )(mock_service_log) + protected_mock_service = service.verify_domain_control(hass, "test_domain")( + mock_service_log + ) hass.services.async_register( "test_domain", "test_service", protected_mock_service, schema=None @@ -940,7 +941,7 @@ async def test_domain_control_unauthorized(hass, hass_read_only_user): """Define a protected service.""" calls.append(call) - protected_mock_service = hass.helpers.service.verify_domain_control("test_domain")( + protected_mock_service = service.verify_domain_control(hass, "test_domain")( mock_service_log ) @@ -979,7 +980,7 @@ async def test_domain_control_admin(hass, hass_admin_user): """Define a protected service.""" calls.append(call) - protected_mock_service = hass.helpers.service.verify_domain_control("test_domain")( + protected_mock_service = service.verify_domain_control(hass, "test_domain")( mock_service_log ) @@ -1017,7 +1018,7 @@ async def test_domain_control_no_user(hass): """Define a protected service.""" calls.append(call) - protected_mock_service = hass.helpers.service.verify_domain_control("test_domain")( + protected_mock_service = service.verify_domain_control(hass, "test_domain")( mock_service_log ) From 0d94324d58dd049cf2520d041a769e50368c8662 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Tue, 17 May 2022 20:57:41 +0300 Subject: [PATCH 536/930] Clean up Shelly async methods that are not awaiting (#72026) --- homeassistant/components/shelly/__init__.py | 9 ++++---- .../components/shelly/binary_sensor.py | 8 +++---- homeassistant/components/shelly/climate.py | 12 +++++----- homeassistant/components/shelly/cover.py | 10 ++++---- homeassistant/components/shelly/entity.py | 23 +++++++++++-------- homeassistant/components/shelly/light.py | 14 ++++++----- homeassistant/components/shelly/number.py | 2 +- homeassistant/components/shelly/sensor.py | 8 +++---- homeassistant/components/shelly/switch.py | 14 ++++++----- homeassistant/components/shelly/utils.py | 3 ++- 10 files changed, 58 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 41a9e68fbdd..4551fee5590 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -182,7 +182,7 @@ async def async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> bo data["model"] = device.settings["device"]["type"] hass.config_entries.async_update_entry(entry, data=data) - hass.async_create_task(async_block_device_setup(hass, entry, device)) + async_block_device_setup(hass, entry, device) if sleep_period == 0: # Not a sleeping device, finish setup @@ -197,7 +197,7 @@ async def async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> bo except OSError as err: raise ConfigEntryNotReady(str(err) or "Error during device setup") from err - await async_block_device_setup(hass, entry, device) + async_block_device_setup(hass, entry, device) elif sleep_period is None or device_entry is None: # Need to get sleep info or first time sleeping device setup, wait for device hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][DEVICE] = device @@ -208,12 +208,13 @@ async def async_setup_block_entry(hass: HomeAssistant, entry: ConfigEntry) -> bo else: # Restore sensors for sleeping device LOGGER.debug("Setting up offline block device %s", entry.title) - await async_block_device_setup(hass, entry, device) + async_block_device_setup(hass, entry, device) return True -async def async_block_device_setup( +@callback +def async_block_device_setup( hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice ) -> None: """Set up a block based device that is online.""" diff --git a/homeassistant/components/shelly/binary_sensor.py b/homeassistant/components/shelly/binary_sensor.py index a6cde0c4670..b33947ad6b7 100644 --- a/homeassistant/components/shelly/binary_sensor.py +++ b/homeassistant/components/shelly/binary_sensor.py @@ -235,12 +235,12 @@ async def async_setup_entry( ) -> None: """Set up sensors for device.""" if get_device_entry_gen(config_entry) == 2: - return await async_setup_entry_rpc( + return async_setup_entry_rpc( hass, config_entry, async_add_entities, RPC_SENSORS, RpcBinarySensor ) if config_entry.data[CONF_SLEEP_PERIOD]: - await async_setup_entry_attribute_entities( + async_setup_entry_attribute_entities( hass, config_entry, async_add_entities, @@ -249,7 +249,7 @@ async def async_setup_entry( _build_block_description, ) else: - await async_setup_entry_attribute_entities( + async_setup_entry_attribute_entities( hass, config_entry, async_add_entities, @@ -257,7 +257,7 @@ async def async_setup_entry( BlockBinarySensor, _build_block_description, ) - await async_setup_entry_rest( + async_setup_entry_rest( hass, config_entry, async_add_entities, diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index a042254fdc6..453d67f39a7 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -50,14 +50,13 @@ async def async_setup_entry( ][BLOCK] if wrapper.device.initialized: - await async_setup_climate_entities(async_add_entities, wrapper) + async_setup_climate_entities(async_add_entities, wrapper) else: - await async_restore_climate_entities( - hass, config_entry, async_add_entities, wrapper - ) + async_restore_climate_entities(hass, config_entry, async_add_entities, wrapper) -async def async_setup_climate_entities( +@callback +def async_setup_climate_entities( async_add_entities: AddEntitiesCallback, wrapper: BlockDeviceWrapper, ) -> None: @@ -79,7 +78,8 @@ async def async_setup_climate_entities( async_add_entities([BlockSleepingClimate(wrapper, sensor_block, device_block)]) -async def async_restore_climate_entities( +@callback +def async_restore_climate_entities( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, diff --git a/homeassistant/components/shelly/cover.py b/homeassistant/components/shelly/cover.py index 9bc61f9415f..e28fe22a528 100644 --- a/homeassistant/components/shelly/cover.py +++ b/homeassistant/components/shelly/cover.py @@ -28,12 +28,13 @@ async def async_setup_entry( ) -> None: """Set up switches for device.""" if get_device_entry_gen(config_entry) == 2: - return await async_setup_rpc_entry(hass, config_entry, async_add_entities) + return async_setup_rpc_entry(hass, config_entry, async_add_entities) - return await async_setup_block_entry(hass, config_entry, async_add_entities) + return async_setup_block_entry(hass, config_entry, async_add_entities) -async def async_setup_block_entry( +@callback +def async_setup_block_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -48,7 +49,8 @@ async def async_setup_block_entry( async_add_entities(BlockShellyCover(wrapper, block) for block in blocks) -async def async_setup_rpc_entry( +@callback +def async_setup_rpc_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, diff --git a/homeassistant/components/shelly/entity.py b/homeassistant/components/shelly/entity.py index de7d3dd9437..8cba4d2804e 100644 --- a/homeassistant/components/shelly/entity.py +++ b/homeassistant/components/shelly/entity.py @@ -46,7 +46,8 @@ from .utils import ( ) -async def async_setup_entry_attribute_entities( +@callback +def async_setup_entry_attribute_entities( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -62,11 +63,11 @@ async def async_setup_entry_attribute_entities( ][BLOCK] if wrapper.device.initialized: - await async_setup_block_attribute_entities( + async_setup_block_attribute_entities( hass, async_add_entities, wrapper, sensors, sensor_class ) else: - await async_restore_block_attribute_entities( + async_restore_block_attribute_entities( hass, config_entry, async_add_entities, @@ -77,7 +78,8 @@ async def async_setup_entry_attribute_entities( ) -async def async_setup_block_attribute_entities( +@callback +def async_setup_block_attribute_entities( hass: HomeAssistant, async_add_entities: AddEntitiesCallback, wrapper: BlockDeviceWrapper, @@ -105,7 +107,7 @@ async def async_setup_block_attribute_entities( ): domain = sensor_class.__module__.split(".")[-1] unique_id = f"{wrapper.mac}-{block.description}-{sensor_id}" - await async_remove_shelly_entity(hass, domain, unique_id) + async_remove_shelly_entity(hass, domain, unique_id) else: blocks.append((block, sensor_id, description)) @@ -120,7 +122,8 @@ async def async_setup_block_attribute_entities( ) -async def async_restore_block_attribute_entities( +@callback +def async_restore_block_attribute_entities( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -158,7 +161,8 @@ async def async_restore_block_attribute_entities( async_add_entities(entities) -async def async_setup_entry_rpc( +@callback +def async_setup_entry_rpc( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -192,7 +196,7 @@ async def async_setup_entry_rpc( ): domain = sensor_class.__module__.split(".")[-1] unique_id = f"{wrapper.mac}-{key}-{sensor_id}" - await async_remove_shelly_entity(hass, domain, unique_id) + async_remove_shelly_entity(hass, domain, unique_id) else: if description.use_polling_wrapper: entities.append( @@ -207,7 +211,8 @@ async def async_setup_entry_rpc( async_add_entities(entities) -async def async_setup_entry_rest( +@callback +def async_setup_entry_rest( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index 295f64b01f6..79db9c509f4 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -65,12 +65,13 @@ async def async_setup_entry( ) -> None: """Set up lights for device.""" if get_device_entry_gen(config_entry) == 2: - return await async_setup_rpc_entry(hass, config_entry, async_add_entities) + return async_setup_rpc_entry(hass, config_entry, async_add_entities) - return await async_setup_block_entry(hass, config_entry, async_add_entities) + return async_setup_block_entry(hass, config_entry, async_add_entities) -async def async_setup_block_entry( +@callback +def async_setup_block_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -92,7 +93,7 @@ async def async_setup_block_entry( blocks.append(block) assert wrapper.device.shelly unique_id = f"{wrapper.mac}-{block.type}_{block.channel}" - await async_remove_shelly_entity(hass, "switch", unique_id) + async_remove_shelly_entity(hass, "switch", unique_id) if not blocks: return @@ -100,7 +101,8 @@ async def async_setup_block_entry( async_add_entities(BlockShellyLight(wrapper, block) for block in blocks) -async def async_setup_rpc_entry( +@callback +def async_setup_rpc_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -116,7 +118,7 @@ async def async_setup_rpc_entry( switch_ids.append(id_) unique_id = f"{wrapper.mac}-switch:{id_}" - await async_remove_shelly_entity(hass, "switch", unique_id) + async_remove_shelly_entity(hass, "switch", unique_id) if not switch_ids: return diff --git a/homeassistant/components/shelly/number.py b/homeassistant/components/shelly/number.py index bfac4cd4033..dcedc32602d 100644 --- a/homeassistant/components/shelly/number.py +++ b/homeassistant/components/shelly/number.py @@ -81,7 +81,7 @@ async def async_setup_entry( return if config_entry.data[CONF_SLEEP_PERIOD]: - await async_setup_entry_attribute_entities( + async_setup_entry_attribute_entities( hass, config_entry, async_add_entities, diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 7e19b9724d7..92c19734414 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -394,12 +394,12 @@ async def async_setup_entry( ) -> None: """Set up sensors for device.""" if get_device_entry_gen(config_entry) == 2: - return await async_setup_entry_rpc( + return async_setup_entry_rpc( hass, config_entry, async_add_entities, RPC_SENSORS, RpcSensor ) if config_entry.data[CONF_SLEEP_PERIOD]: - await async_setup_entry_attribute_entities( + async_setup_entry_attribute_entities( hass, config_entry, async_add_entities, @@ -408,7 +408,7 @@ async def async_setup_entry( _build_block_description, ) else: - await async_setup_entry_attribute_entities( + async_setup_entry_attribute_entities( hass, config_entry, async_add_entities, @@ -416,7 +416,7 @@ async def async_setup_entry( BlockSensor, _build_block_description, ) - await async_setup_entry_rest( + async_setup_entry_rest( hass, config_entry, async_add_entities, REST_SENSORS, RestSensor ) diff --git a/homeassistant/components/shelly/switch.py b/homeassistant/components/shelly/switch.py index 9114587a910..d65568d0a2a 100644 --- a/homeassistant/components/shelly/switch.py +++ b/homeassistant/components/shelly/switch.py @@ -29,12 +29,13 @@ async def async_setup_entry( ) -> None: """Set up switches for device.""" if get_device_entry_gen(config_entry) == 2: - return await async_setup_rpc_entry(hass, config_entry, async_add_entities) + return async_setup_rpc_entry(hass, config_entry, async_add_entities) - return await async_setup_block_entry(hass, config_entry, async_add_entities) + return async_setup_block_entry(hass, config_entry, async_add_entities) -async def async_setup_block_entry( +@callback +def async_setup_block_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -59,7 +60,7 @@ async def async_setup_block_entry( relay_blocks.append(block) unique_id = f"{wrapper.mac}-{block.type}_{block.channel}" - await async_remove_shelly_entity(hass, "light", unique_id) + async_remove_shelly_entity(hass, "light", unique_id) if not relay_blocks: return @@ -67,7 +68,8 @@ async def async_setup_block_entry( async_add_entities(BlockRelaySwitch(wrapper, block) for block in relay_blocks) -async def async_setup_rpc_entry( +@callback +def async_setup_rpc_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, @@ -84,7 +86,7 @@ async def async_setup_rpc_entry( switch_ids.append(id_) unique_id = f"{wrapper.mac}-switch:{id_}" - await async_remove_shelly_entity(hass, "light", unique_id) + async_remove_shelly_entity(hass, "light", unique_id) if not switch_ids: return diff --git a/homeassistant/components/shelly/utils.py b/homeassistant/components/shelly/utils.py index 0398250df71..6dfc2fb3be8 100644 --- a/homeassistant/components/shelly/utils.py +++ b/homeassistant/components/shelly/utils.py @@ -30,7 +30,8 @@ from .const import ( ) -async def async_remove_shelly_entity( +@callback +def async_remove_shelly_entity( hass: HomeAssistant, domain: str, unique_id: str ) -> None: """Remove a Shelly entity.""" From 69e622b3278c4d26a180969d746b9381756f7d15 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 20:33:51 +0200 Subject: [PATCH 537/930] Clean up accessing intent helpers via hass (#72028) --- homeassistant/components/cover/intent.py | 10 ++++++---- .../components/hangouts/hangouts_bot.py | 11 +++++++--- homeassistant/components/humidifier/intent.py | 11 +++++----- homeassistant/components/intent/__init__.py | 15 ++++++++------ homeassistant/components/light/intent.py | 6 +++--- tests/components/humidifier/test_intent.py | 20 ++++++++++++------- tests/components/light/test_intent.py | 11 ++++++---- 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/cover/intent.py b/homeassistant/components/cover/intent.py index 1fa353151ba..9174e8399f3 100644 --- a/homeassistant/components/cover/intent.py +++ b/homeassistant/components/cover/intent.py @@ -11,13 +11,15 @@ INTENT_CLOSE_COVER = "HassCloseCover" async def async_setup_intents(hass: HomeAssistant) -> None: """Set up the cover intents.""" - hass.helpers.intent.async_register( + intent.async_register( + hass, intent.ServiceIntentHandler( INTENT_OPEN_COVER, DOMAIN, SERVICE_OPEN_COVER, "Opened {}" - ) + ), ) - hass.helpers.intent.async_register( + intent.async_register( + hass, intent.ServiceIntentHandler( INTENT_CLOSE_COVER, DOMAIN, SERVICE_CLOSE_COVER, "Closed {}" - ) + ), ) diff --git a/homeassistant/components/hangouts/hangouts_bot.py b/homeassistant/components/hangouts/hangouts_bot.py index 11181c5b0ff..c3c363ef55e 100644 --- a/homeassistant/components/hangouts/hangouts_bot.py +++ b/homeassistant/components/hangouts/hangouts_bot.py @@ -187,11 +187,16 @@ class HangoutsBot: if not (match := matcher.match(text)): continue if intent_type == INTENT_HELP: - return await self.hass.helpers.intent.async_handle( - DOMAIN, intent_type, {"conv_id": {"value": conv_id}}, text + return await intent.async_handle( + self.hass, + DOMAIN, + intent_type, + {"conv_id": {"value": conv_id}}, + text, ) - return await self.hass.helpers.intent.async_handle( + return await intent.async_handle( + self.hass, DOMAIN, intent_type, {"conv_id": {"value": conv_id}} diff --git a/homeassistant/components/humidifier/intent.py b/homeassistant/components/humidifier/intent.py index aeeb18cceec..57f42f58fe0 100644 --- a/homeassistant/components/humidifier/intent.py +++ b/homeassistant/components/humidifier/intent.py @@ -22,8 +22,8 @@ INTENT_MODE = "HassHumidifierMode" async def async_setup_intents(hass: HomeAssistant) -> None: """Set up the humidifier intents.""" - hass.helpers.intent.async_register(HumidityHandler()) - hass.helpers.intent.async_register(SetModeHandler()) + intent.async_register(hass, HumidityHandler()) + intent.async_register(hass, SetModeHandler()) class HumidityHandler(intent.IntentHandler): @@ -39,8 +39,8 @@ class HumidityHandler(intent.IntentHandler): """Handle the hass intent.""" hass = intent_obj.hass slots = self.async_validate_slots(intent_obj.slots) - state = hass.helpers.intent.async_match_state( - slots["name"]["value"], hass.states.async_all(DOMAIN) + state = intent.async_match_state( + hass, slots["name"]["value"], hass.states.async_all(DOMAIN) ) service_data = {ATTR_ENTITY_ID: state.entity_id} @@ -83,7 +83,8 @@ class SetModeHandler(intent.IntentHandler): """Handle the hass intent.""" hass = intent_obj.hass slots = self.async_validate_slots(intent_obj.slots) - state = hass.helpers.intent.async_match_state( + state = intent.async_match_state( + hass, slots["name"]["value"], hass.states.async_all(DOMAIN), ) diff --git a/homeassistant/components/intent/__init__.py b/homeassistant/components/intent/__init__.py index d626daa8c3b..c43643b0244 100644 --- a/homeassistant/components/intent/__init__.py +++ b/homeassistant/components/intent/__init__.py @@ -19,20 +19,23 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass, DOMAIN, _async_process_intent ) - hass.helpers.intent.async_register( + intent.async_register( + hass, intent.ServiceIntentHandler( intent.INTENT_TURN_ON, HA_DOMAIN, SERVICE_TURN_ON, "Turned {} on" - ) + ), ) - hass.helpers.intent.async_register( + intent.async_register( + hass, intent.ServiceIntentHandler( intent.INTENT_TURN_OFF, HA_DOMAIN, SERVICE_TURN_OFF, "Turned {} off" - ) + ), ) - hass.helpers.intent.async_register( + intent.async_register( + hass, intent.ServiceIntentHandler( intent.INTENT_TOGGLE, HA_DOMAIN, SERVICE_TOGGLE, "Toggled {}" - ) + ), ) return True diff --git a/homeassistant/components/light/intent.py b/homeassistant/components/light/intent.py index 261cbbd973d..2ffd73a9792 100644 --- a/homeassistant/components/light/intent.py +++ b/homeassistant/components/light/intent.py @@ -21,7 +21,7 @@ INTENT_SET = "HassLightSet" async def async_setup_intents(hass: HomeAssistant) -> None: """Set up the light intents.""" - hass.helpers.intent.async_register(SetIntentHandler()) + intent.async_register(hass, SetIntentHandler()) def _test_supports_color(state: State) -> None: @@ -56,8 +56,8 @@ class SetIntentHandler(intent.IntentHandler): """Handle the hass intent.""" hass = intent_obj.hass slots = self.async_validate_slots(intent_obj.slots) - state = hass.helpers.intent.async_match_state( - slots["name"]["value"], hass.states.async_all(DOMAIN) + state = intent.async_match_state( + hass, slots["name"]["value"], hass.states.async_all(DOMAIN) ) service_data = {ATTR_ENTITY_ID: state.entity_id} diff --git a/tests/components/humidifier/test_intent.py b/tests/components/humidifier/test_intent.py index 66ff62872f3..ed207231bc6 100644 --- a/tests/components/humidifier/test_intent.py +++ b/tests/components/humidifier/test_intent.py @@ -15,7 +15,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, ) -from homeassistant.helpers.intent import IntentHandleError +from homeassistant.helpers.intent import IntentHandleError, async_handle from tests.common import async_mock_service @@ -29,7 +29,8 @@ async def test_intent_set_humidity(hass): turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_HUMIDITY, {"name": {"value": "Bedroom humidifier"}, "humidity": {"value": "50"}}, @@ -56,7 +57,8 @@ async def test_intent_set_humidity_and_turn_on(hass): turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_HUMIDITY, {"name": {"value": "Bedroom humidifier"}, "humidity": {"value": "50"}}, @@ -97,7 +99,8 @@ async def test_intent_set_mode(hass): turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_MODE, {"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}}, @@ -134,7 +137,8 @@ async def test_intent_set_mode_and_turn_on(hass): turn_on_calls = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_MODE, {"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}}, @@ -168,7 +172,8 @@ async def test_intent_set_mode_tests_feature(hass): await intent.async_setup_intents(hass) try: - await hass.helpers.intent.async_handle( + await async_handle( + hass, "test", intent.INTENT_MODE, {"name": {"value": "Bedroom humidifier"}, "mode": {"value": "away"}}, @@ -196,7 +201,8 @@ async def test_intent_set_unknown_mode(hass): await intent.async_setup_intents(hass) try: - await hass.helpers.intent.async_handle( + await async_handle( + hass, "test", intent.INTENT_MODE, {"name": {"value": "Bedroom humidifier"}, "mode": {"value": "eco"}}, diff --git a/tests/components/light/test_intent.py b/tests/components/light/test_intent.py index 19b25efa772..0c837a49c42 100644 --- a/tests/components/light/test_intent.py +++ b/tests/components/light/test_intent.py @@ -2,7 +2,7 @@ from homeassistant.components import light from homeassistant.components.light import ATTR_SUPPORTED_COLOR_MODES, ColorMode, intent from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON -from homeassistant.helpers.intent import IntentHandleError +from homeassistant.helpers.intent import IntentHandleError, async_handle from tests.common import async_mock_service @@ -16,7 +16,8 @@ async def test_intent_set_color(hass): calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_SET, {"name": {"value": "Hello"}, "color": {"value": "blue"}}, @@ -40,7 +41,8 @@ async def test_intent_set_color_tests_feature(hass): await intent.async_setup_intents(hass) try: - await hass.helpers.intent.async_handle( + await async_handle( + hass, "test", intent.INTENT_SET, {"name": {"value": "Hello"}, "color": {"value": "blue"}}, @@ -61,7 +63,8 @@ async def test_intent_set_color_and_brightness(hass): calls = async_mock_service(hass, light.DOMAIN, light.SERVICE_TURN_ON) await intent.async_setup_intents(hass) - result = await hass.helpers.intent.async_handle( + result = await async_handle( + hass, "test", intent.INTENT_SET, { From c3d19f38274935ab8f784e6fbeb7c5959a551074 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 21:22:15 +0200 Subject: [PATCH 538/930] Clean up accessing device_registry helpers via hass (#72031) --- homeassistant/components/guardian/__init__.py | 2 +- homeassistant/components/hue/v1/hue_event.py | 5 ++--- homeassistant/components/insteon/api/aldb.py | 3 ++- .../components/insteon/api/device.py | 3 ++- homeassistant/components/insteon/utils.py | 3 ++- homeassistant/components/mikrotik/__init__.py | 4 ++-- homeassistant/components/nest/__init__.py | 8 +++++-- .../components/nest/device_trigger.py | 6 ++--- .../components/netatmo/device_trigger.py | 22 ++++++++++++++----- .../components/netatmo/netatmo_entity_base.py | 7 +++--- homeassistant/components/netatmo/sensor.py | 6 ++--- homeassistant/components/plex/__init__.py | 4 ++-- homeassistant/components/rfxtrx/__init__.py | 15 ++++--------- homeassistant/components/starline/__init__.py | 3 ++- .../components/tellduslive/__init__.py | 4 ++-- homeassistant/components/tradfri/__init__.py | 2 +- homeassistant/components/zha/__init__.py | 8 ++++--- homeassistant/components/zha/core/helpers.py | 3 ++- tests/components/deconz/test_deconz_event.py | 17 ++++++++------ tests/components/deconz/test_logbook.py | 5 +++-- .../home_plus_control/test_switch.py | 13 +++++------ tests/components/kaleidescape/test_init.py | 3 ++- .../kaleidescape/test_media_player.py | 3 ++- tests/components/kraken/test_sensor.py | 7 +++--- tests/components/mikrotik/test_init.py | 2 +- tests/components/picnic/test_sensor.py | 7 +++--- tests/components/unifi/test_init.py | 7 +++--- tests/components/unifi/test_services.py | 20 ++++++++--------- 28 files changed, 105 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/guardian/__init__.py b/homeassistant/components/guardian/__init__.py index b971a428a76..25e0df913d8 100644 --- a/homeassistant/components/guardian/__init__.py +++ b/homeassistant/components/guardian/__init__.py @@ -363,7 +363,7 @@ class PairedSensorManager: # Remove the paired sensor device from the device registry (which will # clean up entities and the entity registry): - dev_reg = await self._hass.helpers.device_registry.async_get_registry() + dev_reg = dr.async_get(self._hass) device = dev_reg.async_get_or_create( config_entry_id=self._entry.entry_id, identifiers={(DOMAIN, uid)} ) diff --git a/homeassistant/components/hue/v1/hue_event.py b/homeassistant/components/hue/v1/hue_event.py index 9074baaaa88..b3faf88c2d9 100644 --- a/homeassistant/components/hue/v1/hue_event.py +++ b/homeassistant/components/hue/v1/hue_event.py @@ -10,6 +10,7 @@ from aiohue.v1.sensors import ( from homeassistant.const import CONF_DEVICE_ID, CONF_EVENT, CONF_ID, CONF_UNIQUE_ID from homeassistant.core import callback +from homeassistant.helpers import device_registry as dr from homeassistant.util import dt as dt_util, slugify from ..const import ATTR_HUE_EVENT @@ -89,9 +90,7 @@ class HueEvent(GenericHueDevice): async def async_update_device_registry(self): """Update device registry.""" - device_registry = ( - await self.bridge.hass.helpers.device_registry.async_get_registry() - ) + device_registry = dr.async_get(self.bridge.hass) entry = device_registry.async_get_or_create( config_entry_id=self.bridge.config_entry.entry_id, **self.device_info diff --git a/homeassistant/components/insteon/api/aldb.py b/homeassistant/components/insteon/api/aldb.py index a3132570ccc..a119707b03d 100644 --- a/homeassistant/components/insteon/api/aldb.py +++ b/homeassistant/components/insteon/api/aldb.py @@ -12,6 +12,7 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr from ..const import DEVICE_ADDRESS, ID, INSTEON_DEVICE_NOT_FOUND, TYPE from .device import async_device_name, notify_device_not_found @@ -82,7 +83,7 @@ async def websocket_get_aldb( aldb.update(device.aldb.pending_changes) changed_records = list(device.aldb.pending_changes.keys()) - dev_registry = await hass.helpers.device_registry.async_get_registry() + dev_registry = dr.async_get(hass) records = [ await async_aldb_record_to_dict( diff --git a/homeassistant/components/insteon/api/device.py b/homeassistant/components/insteon/api/device.py index beef78394fa..ea6bade4835 100644 --- a/homeassistant/components/insteon/api/device.py +++ b/homeassistant/components/insteon/api/device.py @@ -6,6 +6,7 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr from ..const import ( DEVICE_ADDRESS, @@ -68,7 +69,7 @@ async def websocket_get_device( msg: dict, ) -> None: """Get an Insteon device.""" - dev_registry = await hass.helpers.device_registry.async_get_registry() + dev_registry = dr.async_get(hass) if not (ha_device := dev_registry.async_get(msg[DEVICE_ID])): notify_device_not_found(connection, msg, HA_DEVICE_NOT_FOUND) return diff --git a/homeassistant/components/insteon/utils.py b/homeassistant/components/insteon/utils.py index 03647559345..e8e34ee8e62 100644 --- a/homeassistant/components/insteon/utils.py +++ b/homeassistant/components/insteon/utils.py @@ -28,6 +28,7 @@ from homeassistant.const import ( ENTITY_MATCH_ALL, ) from homeassistant.core import ServiceCall, callback +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -294,7 +295,7 @@ def async_register_services(hass): """Remove the device and all entities from hass.""" signal = f"{address.id}_{SIGNAL_REMOVE_ENTITY}" async_dispatcher_send(hass, signal) - dev_registry = await hass.helpers.device_registry.async_get_registry() + dev_registry = dr.async_get(hass) device = dev_registry.async_get_device(identifiers={(DOMAIN, str(address))}) if device: dev_registry.async_remove_device(device.id) diff --git a/homeassistant/components/mikrotik/__init__.py b/homeassistant/components/mikrotik/__init__.py index f0dc249d26a..1ef250a3f4e 100644 --- a/homeassistant/components/mikrotik/__init__.py +++ b/homeassistant/components/mikrotik/__init__.py @@ -11,7 +11,7 @@ from homeassistant.const import ( CONF_VERIFY_SSL, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.typing import ConfigType from .const import ( @@ -76,7 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b return False hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = hub - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, connections={(DOMAIN, hub.serial_num)}, diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 5a34df1d74b..7d8353738ff 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -42,7 +42,11 @@ from homeassistant.exceptions import ( HomeAssistantError, Unauthorized, ) -from homeassistant.helpers import config_validation as cv, entity_registry as er +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + entity_registry as er, +) from homeassistant.helpers.entity_registry import async_entries_for_device from homeassistant.helpers.typing import ConfigType @@ -145,7 +149,7 @@ class SignalUpdateCallback: if not (events := event_message.resource_update_events): return _LOGGER.debug("Event Update %s", events.keys()) - device_registry = await self._hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(self._hass) device_entry = device_registry.async_get_device({(DOMAIN, device_id)}) if not device_entry: return diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index 28eb444b91a..67b361e76d8 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -16,7 +16,7 @@ from homeassistant.components.device_automation.exceptions import ( from homeassistant.components.homeassistant.triggers import event as event_trigger from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant -from homeassistant.helpers.device_registry import DeviceRegistry +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType from .const import DATA_SUBSCRIBER, DOMAIN @@ -35,9 +35,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_nest_device_id(hass: HomeAssistant, device_id: str) -> str | None: """Get the nest API device_id from the HomeAssistant device_id.""" - device_registry: DeviceRegistry = ( - await hass.helpers.device_registry.async_get_registry() - ) + device_registry = dr.async_get(hass) if device := device_registry.async_get(device_id): for (domain, unique_id) in device.identifiers: if domain == DOMAIN: diff --git a/homeassistant/components/netatmo/device_trigger.py b/homeassistant/components/netatmo/device_trigger.py index 7a9acac2f96..ae62956b691 100644 --- a/homeassistant/components/netatmo/device_trigger.py +++ b/homeassistant/components/netatmo/device_trigger.py @@ -23,7 +23,11 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant -from homeassistant.helpers import config_validation as cv, entity_registry +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + entity_registry, +) from homeassistant.helpers.typing import ConfigType from .climate import STATE_NETATMO_AWAY, STATE_NETATMO_HG, STATE_NETATMO_SCHEDULE @@ -74,9 +78,14 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device = device_registry.async_get(config[CONF_DEVICE_ID]) + if not device: + raise InvalidDeviceAutomationConfig( + f"Trigger invalid, device with ID {config[CONF_DEVICE_ID]} not found" + ) + trigger = config[CONF_TYPE] if ( @@ -94,11 +103,14 @@ async def async_get_triggers( ) -> list[dict[str, Any]]: """List device triggers for Netatmo devices.""" registry = entity_registry.async_get(hass) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) triggers = [] for entry in entity_registry.async_entries_for_device(registry, device_id): - device = device_registry.async_get(device_id) + if ( + device := device_registry.async_get(device_id) + ) is None or device.model is None: + continue for trigger in DEVICES.get(device.model, []): if trigger in SUBTYPES: @@ -134,7 +146,7 @@ async def async_attach_trigger( automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Attach a trigger.""" - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device = device_registry.async_get(config[CONF_DEVICE_ID]) if not device: diff --git a/homeassistant/components/netatmo/netatmo_entity_base.py b/homeassistant/components/netatmo/netatmo_entity_base.py index 56f25e04906..decedbbdfbd 100644 --- a/homeassistant/components/netatmo/netatmo_entity_base.py +++ b/homeassistant/components/netatmo/netatmo_entity_base.py @@ -3,6 +3,7 @@ from __future__ import annotations from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import callback +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import DeviceInfo, Entity from .const import ( @@ -65,9 +66,9 @@ class NetatmoBase(Entity): if sub is None: await self.data_handler.unregister_data_class(signal_name, None) - registry = await self.hass.helpers.device_registry.async_get_registry() - device = registry.async_get_device({(DOMAIN, self._id)}) - self.hass.data[DOMAIN][DATA_DEVICE_IDS][self._id] = device.id + registry = dr.async_get(self.hass) + if device := registry.async_get_device({(DOMAIN, self._id)}): + self.hass.data[DOMAIN][DATA_DEVICE_IDS][self._id] = device.id self.async_update_callback() diff --git a/homeassistant/components/netatmo/sensor.py b/homeassistant/components/netatmo/sensor.py index 41ae27b2992..217b2146cc9 100644 --- a/homeassistant/components/netatmo/sensor.py +++ b/homeassistant/components/netatmo/sensor.py @@ -29,7 +29,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import PlatformNotReady -from homeassistant.helpers.device_registry import async_entries_for_config_entry +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -393,13 +393,13 @@ async def async_setup_entry( async_add_entities(await find_entities(data_class_name), True) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) async def add_public_entities(update: bool = True) -> None: """Retrieve Netatmo public weather entities.""" entities = { device.name: device.id - for device in async_entries_for_config_entry( + for device in dr.async_entries_for_config_entry( device_registry, entry.entry_id ) if device.model == "Public Weather stations" diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 4ca83d98242..dbfe55077d7 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -299,14 +299,14 @@ def async_cleanup_plex_devices(hass, entry): device_registry = dev_reg.async_get(hass) entity_registry = ent_reg.async_get(hass) - device_entries = hass.helpers.device_registry.async_entries_for_config_entry( + device_entries = dev_reg.async_entries_for_config_entry( device_registry, entry.entry_id ) for device_entry in device_entries: if ( len( - hass.helpers.entity_registry.async_entries_for_device( + ent_reg.async_entries_for_device( entity_registry, device_entry.id, include_disabled_entities=True ) ) diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index 89521aba9f2..b517abafc86 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -24,12 +24,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import Event, HomeAssistant, ServiceCall, callback -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import ( - EVENT_DEVICE_REGISTRY_UPDATED, - DeviceEntry, - DeviceRegistry, -) +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -174,9 +169,7 @@ async def async_setup_internal(hass, entry: ConfigEntry): devices = _get_device_lookup(config[CONF_DEVICES]) pt2262_devices: list[str] = [] - device_registry: DeviceRegistry = ( - await hass.helpers.device_registry.async_get_registry() - ) + device_registry = dr.async_get(hass) # Declare the Handle event @callback @@ -268,7 +261,7 @@ async def async_setup_internal(hass, entry: ConfigEntry): _remove_device(device_id) entry.async_on_unload( - hass.bus.async_listen(EVENT_DEVICE_REGISTRY_UPDATED, _updated_device) + hass.bus.async_listen(dr.EVENT_DEVICE_REGISTRY_UPDATED, _updated_device) ) def _shutdown_rfxtrx(event): @@ -448,7 +441,7 @@ def get_device_tuple_from_identifiers( async def async_remove_config_entry_device( - hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry ) -> bool: """Remove config entry from a device. diff --git a/homeassistant/components/starline/__init__.py b/homeassistant/components/starline/__init__.py index 7d05c0d47ee..886a583ae93 100644 --- a/homeassistant/components/starline/__init__.py +++ b/homeassistant/components/starline/__init__.py @@ -7,6 +7,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from .account import StarlineAccount from .const import ( @@ -33,7 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN] = {} hass.data[DOMAIN][entry.entry_id] = account - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) for device in account.api.devices.values(): device_registry.async_get_or_create( config_entry_id=entry.entry_id, **account.device_info(device) diff --git a/homeassistant/components/tellduslive/__init__.py b/homeassistant/components/tellduslive/__init__.py index 572950b816b..92feb69c60c 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -10,7 +10,7 @@ from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_SCAN_INTERVAL from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_call_later from homeassistant.helpers.typing import ConfigType @@ -84,7 +84,7 @@ async def async_new_client(hass, session, entry): _LOGGER.debug("Update interval %s seconds", interval) client = TelldusLiveClient(hass, entry, session, interval) hass.data[DOMAIN] = client - dev_reg = await hass.helpers.device_registry.async_get_registry() + dev_reg = dr.async_get(hass) for hub in await client.async_get_hubs(): _LOGGER.debug("Connected hub %s", hub["name"]) dev_reg.async_get_or_create( diff --git a/homeassistant/components/tradfri/__init__.py b/homeassistant/components/tradfri/__init__.py index e4d13568e6d..e79cd8396e1 100644 --- a/homeassistant/components/tradfri/__init__.py +++ b/homeassistant/components/tradfri/__init__.py @@ -84,7 +84,7 @@ async def async_setup_entry( await factory.shutdown() raise ConfigEntryNotReady from exc - dev_reg = await hass.helpers.device_registry.async_get_registry() + dev_reg = dr.async_get(hass) dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index c512e104ae8..0e11d992a25 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -9,8 +9,8 @@ from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH from homeassistant import const as ha_const from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import ConfigType @@ -106,10 +106,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b coro = hass.config_entries.async_forward_entry_setup(config_entry, platform) zha_data[DATA_ZHA_PLATFORM_LOADED].append(hass.async_create_task(coro)) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(CONNECTION_ZIGBEE, str(zha_gateway.application_controller.ieee))}, + connections={ + (dr.CONNECTION_ZIGBEE, str(zha_gateway.application_controller.ieee)) + }, identifiers={(DOMAIN, str(zha_gateway.application_controller.ieee))}, name="Zigbee Coordinator", manufacturer="ZHA", diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index fcd29c1619f..19a1d3fef9a 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -25,6 +25,7 @@ import zigpy.zdo.types as zdo_types from homeassistant.config_entries import ConfigEntry from homeassistant.core import State, callback +from homeassistant.helpers import device_registry as dr from .const import ( CLUSTER_TYPE_IN, @@ -161,7 +162,7 @@ def async_cluster_exists(hass, cluster_id): async def async_get_zha_device(hass, device_id): """Get a ZHA device for the given device registry id.""" - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) registry_device = device_registry.async_get(device_id) zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee_address = list(list(registry_device.identifiers)[0])[1] diff --git a/tests/components/deconz/test_deconz_event.py b/tests/components/deconz/test_deconz_event.py index 5b04b459278..e697edd5a9a 100644 --- a/tests/components/deconz/test_deconz_event.py +++ b/tests/components/deconz/test_deconz_event.py @@ -22,7 +22,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, STATE_UNAVAILABLE, ) -from homeassistant.helpers.device_registry import async_entries_for_config_entry +from homeassistant.helpers import device_registry as dr from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration @@ -73,12 +73,13 @@ async def test_deconz_events(hass, aioclient_mock, mock_deconz_websocket): with patch.dict(DECONZ_WEB_REQUEST, data): config_entry = await setup_deconz_integration(hass, aioclient_mock) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) assert len(hass.states.async_all()) == 3 # 5 switches + 2 additional devices for deconz service and host assert ( - len(async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 7 + len(dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)) + == 7 ) assert hass.states.get("sensor.switch_2_battery").state == "100" assert hass.states.get("sensor.switch_3_battery").state == "100" @@ -267,12 +268,13 @@ async def test_deconz_alarm_events(hass, aioclient_mock, mock_deconz_websocket): with patch.dict(DECONZ_WEB_REQUEST, data): config_entry = await setup_deconz_integration(hass, aioclient_mock) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) assert len(hass.states.async_all()) == 4 # 1 alarm control device + 2 additional devices for deconz service and host assert ( - len(async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 3 + len(dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)) + == 3 ) captured_events = async_capture_events(hass, CONF_DECONZ_ALARM_EVENT) @@ -435,9 +437,10 @@ async def test_deconz_events_bad_unique_id(hass, aioclient_mock, mock_deconz_web with patch.dict(DECONZ_WEB_REQUEST, data): config_entry = await setup_deconz_integration(hass, aioclient_mock) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) assert len(hass.states.async_all()) == 1 assert ( - len(async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 2 + len(dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)) + == 2 ) diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index a78e795573a..9ba0799d04e 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -15,6 +15,7 @@ from homeassistant.const import ( CONF_UNIQUE_ID, STATE_ALARM_ARMED_AWAY, ) +from homeassistant.helpers import device_registry as dr from homeassistant.setup import async_setup_component from homeassistant.util import slugify @@ -56,7 +57,7 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): with patch.dict(DECONZ_WEB_REQUEST, data): await setup_deconz_integration(hass, aioclient_mock) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) keypad_event_id = slugify(data["sensors"]["1"]["name"]) keypad_serial = data["sensors"]["1"]["uniqueid"].split("-", 1)[0] @@ -127,7 +128,7 @@ async def test_humanifying_deconz_event(hass, aioclient_mock): with patch.dict(DECONZ_WEB_REQUEST, data): await setup_deconz_integration(hass, aioclient_mock) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) switch_event_id = slugify(data["sensors"]["1"]["name"]) switch_serial = data["sensors"]["1"]["uniqueid"].split("-", 1)[0] diff --git a/tests/components/home_plus_control/test_switch.py b/tests/components/home_plus_control/test_switch.py index 75d416ba2b1..44a969392bf 100644 --- a/tests/components/home_plus_control/test_switch.py +++ b/tests/components/home_plus_control/test_switch.py @@ -16,6 +16,7 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) +from homeassistant.helpers import device_registry as dr, entity_registry as er from tests.common import async_fire_time_changed from tests.components.home_plus_control.conftest import ( @@ -33,8 +34,8 @@ def entity_assertions( expected_devices=None, ): """Assert number of entities and devices.""" - entity_reg = hass.helpers.entity_registry.async_get(hass) - device_reg = hass.helpers.device_registry.async_get(hass) + entity_reg = er.async_get(hass) + device_reg = dr.async_get(hass) if num_exp_devices is None: num_exp_devices = num_exp_entities @@ -53,13 +54,11 @@ def entity_assertions( def one_entity_state(hass, device_uid): """Assert the presence of an entity and return its state.""" - entity_reg = hass.helpers.entity_registry.async_get(hass) - device_reg = hass.helpers.device_registry.async_get(hass) + entity_reg = er.async_get(hass) + device_reg = dr.async_get(hass) device_id = device_reg.async_get_device({(DOMAIN, device_uid)}).id - entity_entries = hass.helpers.entity_registry.async_entries_for_device( - entity_reg, device_id - ) + entity_entries = er.async_entries_for_device(entity_reg, device_id) assert len(entity_entries) == 1 entity_entry = entity_entries[0] diff --git a/tests/components/kaleidescape/test_init.py b/tests/components/kaleidescape/test_init.py index 876c02ba5a6..d0826f4714a 100644 --- a/tests/components/kaleidescape/test_init.py +++ b/tests/components/kaleidescape/test_init.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock from homeassistant.components.kaleidescape.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from . import MOCK_SERIAL @@ -50,7 +51,7 @@ async def test_device( mock_integration: MockConfigEntry, ) -> None: """Test device.""" - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device = device_registry.async_get_device( identifiers={("kaleidescape", MOCK_SERIAL)} ) diff --git a/tests/components/kaleidescape/test_media_player.py b/tests/components/kaleidescape/test_media_player.py index 94ba7f82fe8..11f5d5f5f2b 100644 --- a/tests/components/kaleidescape/test_media_player.py +++ b/tests/components/kaleidescape/test_media_player.py @@ -21,6 +21,7 @@ from homeassistant.const import ( STATE_PLAYING, ) from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from . import MOCK_SERIAL @@ -173,7 +174,7 @@ async def test_device( mock_integration: MockConfigEntry, ) -> None: """Test device attributes.""" - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device = device_registry.async_get_device( identifiers={("kaleidescape", MOCK_SERIAL)} ) diff --git a/tests/components/kraken/test_sensor.py b/tests/components/kraken/test_sensor.py index cbfda938f99..1d6b9e6518d 100644 --- a/tests/components/kraken/test_sensor.py +++ b/tests/components/kraken/test_sensor.py @@ -11,8 +11,7 @@ from homeassistant.components.kraken.const import ( DOMAIN, ) from homeassistant.const import CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START -from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers import device_registry as dr, entity_registry as er import homeassistant.util.dt as dt_util from .const import ( @@ -249,13 +248,13 @@ async def test_sensors_available_after_restart(hass): }, ) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(DOMAIN, "XBT_USD")}, name="XBT USD", manufacturer="Kraken.com", - entry_type=DeviceEntryType.SERVICE, + entry_type=dr.DeviceEntryType.SERVICE, ) entry.add_to_hass(hass) diff --git a/tests/components/mikrotik/test_init.py b/tests/components/mikrotik/test_init.py index 281b70e36be..30fa1a0a89f 100644 --- a/tests/components/mikrotik/test_init.py +++ b/tests/components/mikrotik/test_init.py @@ -25,7 +25,7 @@ async def test_successful_config_entry(hass): mock_registry = Mock() with patch.object(mikrotik, "MikrotikHub") as mock_hub, patch( - "homeassistant.helpers.device_registry.async_get_registry", + "homeassistant.components.mikrotik.dr.async_get", return_value=mock_registry, ): mock_hub.return_value.async_setup = AsyncMock(return_value=True) diff --git a/tests/components/picnic/test_sensor.py b/tests/components/picnic/test_sensor.py index 808f1ce6f41..8d680327bc8 100644 --- a/tests/components/picnic/test_sensor.py +++ b/tests/components/picnic/test_sensor.py @@ -17,8 +17,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.util import dt from tests.common import ( @@ -497,13 +496,13 @@ class TestPicnicSensor(unittest.IsolatedAsyncioTestCase): # Setup platform and default mock responses await self._setup_platform(use_default_responses=True) - device_registry = await self.hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(self.hass) picnic_service = device_registry.async_get_device( identifiers={(const.DOMAIN, DEFAULT_USER_RESPONSE["user_id"])} ) assert picnic_service.model == DEFAULT_USER_RESPONSE["user_id"] assert picnic_service.name == "Picnic: Commonstreet 123a" - assert picnic_service.entry_type is DeviceEntryType.SERVICE + assert picnic_service.entry_type is dr.DeviceEntryType.SERVICE async def test_auth_token_is_saved_on_update(self): """Test that auth-token changes in the session object are reflected by the config entry.""" diff --git a/tests/components/unifi/test_init.py b/tests/components/unifi/test_init.py index b2d37ad7ee3..f183e1c22ff 100644 --- a/tests/components/unifi/test_init.py +++ b/tests/components/unifi/test_init.py @@ -4,7 +4,7 @@ from unittest.mock import AsyncMock, patch from homeassistant.components import unifi from homeassistant.components.unifi import async_flatten_entry_data from homeassistant.components.unifi.const import CONF_CONTROLLER, DOMAIN as UNIFI_DOMAIN -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers import device_registry as dr from homeassistant.setup import async_setup_component from .test_controller import ( @@ -53,9 +53,10 @@ async def test_controller_mac(hass): assert len(mock_controller.mock_calls) == 2 - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( - config_entry_id=entry.entry_id, connections={(CONNECTION_NETWORK_MAC, "mac1")} + config_entry_id=entry.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, "mac1")}, ) assert device.configuration_url == "https://123:443" assert device.manufacturer == "Ubiquiti Networks" diff --git a/tests/components/unifi/test_services.py b/tests/components/unifi/test_services.py index 27e4ddea930..0c6f20869c8 100644 --- a/tests/components/unifi/test_services.py +++ b/tests/components/unifi/test_services.py @@ -9,7 +9,7 @@ from homeassistant.components.unifi.services import ( SUPPORTED_SERVICES, ) from homeassistant.const import ATTR_DEVICE_ID -from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers import device_registry as dr from .test_controller import setup_unifi_integration @@ -62,10 +62,10 @@ async def test_reconnect_client(hass, aioclient_mock): f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", ) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(CONNECTION_NETWORK_MAC, clients[0]["mac"])}, + connections={(dr.CONNECTION_NETWORK_MAC, clients[0]["mac"])}, ) await hass.services.async_call( @@ -98,7 +98,7 @@ async def test_reconnect_device_without_mac(hass, aioclient_mock): aioclient_mock.clear_requests() - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, connections={("other connection", "not mac")}, @@ -132,10 +132,10 @@ async def test_reconnect_client_controller_unavailable(hass, aioclient_mock): f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr", ) - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(CONNECTION_NETWORK_MAC, clients[0]["mac"])}, + connections={(dr.CONNECTION_NETWORK_MAC, clients[0]["mac"])}, ) await hass.services.async_call( @@ -153,10 +153,10 @@ async def test_reconnect_client_unknown_mac(hass, aioclient_mock): aioclient_mock.clear_requests() - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(CONNECTION_NETWORK_MAC, "mac unknown to controller")}, + connections={(dr.CONNECTION_NETWORK_MAC, "mac unknown to controller")}, ) await hass.services.async_call( @@ -182,10 +182,10 @@ async def test_reconnect_wired_client(hass, aioclient_mock): aioclient_mock.clear_requests() - device_registry = await hass.helpers.device_registry.async_get_registry() + device_registry = dr.async_get(hass) device_entry = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, - connections={(CONNECTION_NETWORK_MAC, clients[0]["mac"])}, + connections={(dr.CONNECTION_NETWORK_MAC, clients[0]["mac"])}, ) await hass.services.async_call( From a78f183b640b7f59ece8429c95aae21bd3120b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Tue, 17 May 2022 21:47:28 +0200 Subject: [PATCH 539/930] Fix Airzone sensor and binary sensor updates (#72025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit airzone: fix sensor and binary sensor updates This regression was introduced in b9b83c05e9d53963891bb50ec9ed14c2bb07f4b5 along with the modifications for the strict typing. Signed-off-by: Álvaro Fernández Rojas --- homeassistant/components/airzone/binary_sensor.py | 6 ++++++ homeassistant/components/airzone/sensor.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/homeassistant/components/airzone/binary_sensor.py b/homeassistant/components/airzone/binary_sensor.py index d1885e0edf9..7ad3254c080 100644 --- a/homeassistant/components/airzone/binary_sensor.py +++ b/homeassistant/components/airzone/binary_sensor.py @@ -119,6 +119,12 @@ class AirzoneBinarySensor(AirzoneEntity, BinarySensorEntity): entity_description: AirzoneBinarySensorEntityDescription + @callback + def _handle_coordinator_update(self) -> None: + """Update attributes when the coordinator updates.""" + self._async_update_attrs() + super()._handle_coordinator_update() + @callback def _async_update_attrs(self) -> None: """Update binary sensor attributes.""" diff --git a/homeassistant/components/airzone/sensor.py b/homeassistant/components/airzone/sensor.py index 1671eb919a7..86d88aa5a55 100644 --- a/homeassistant/components/airzone/sensor.py +++ b/homeassistant/components/airzone/sensor.py @@ -102,6 +102,12 @@ async def async_setup_entry( class AirzoneSensor(AirzoneEntity, SensorEntity): """Define an Airzone sensor.""" + @callback + def _handle_coordinator_update(self) -> None: + """Update attributes when the coordinator updates.""" + self._async_update_attrs() + super()._handle_coordinator_update() + @callback def _async_update_attrs(self) -> None: """Update sensor attributes.""" From afe2b71b2e694bbc08138ad7dd2c6c24d3125049 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 17 May 2022 21:49:19 +0200 Subject: [PATCH 540/930] Update model info from SSDP in SamsungTV (#71992) * Update model info from SSDP in SamsungTV * Add tests --- .../components/samsungtv/config_flow.py | 6 ++++ .../components/samsungtv/test_config_flow.py | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/homeassistant/components/samsungtv/config_flow.py b/homeassistant/components/samsungtv/config_flow.py index adcec0e8b2b..02414d3f476 100644 --- a/homeassistant/components/samsungtv/config_flow.py +++ b/homeassistant/components/samsungtv/config_flow.py @@ -167,6 +167,8 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): updates = {CONF_HOST: self._host} if self._mac: updates[CONF_MAC] = self._mac + if self._model: + updates[CONF_MODEL] = self._model if self._ssdp_rendering_control_location: updates[ CONF_SSDP_RENDERING_CONTROL_LOCATION @@ -380,10 +382,12 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): != self._ssdp_main_tv_agent_location ) update_mac = self._mac and not data.get(CONF_MAC) + update_model = self._model and not data.get(CONF_MODEL) if ( update_ssdp_rendering_control_location or update_ssdp_main_tv_agent_location or update_mac + or update_model ): if update_ssdp_rendering_control_location: data[ @@ -395,6 +399,8 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ] = self._ssdp_main_tv_agent_location if update_mac: data[CONF_MAC] = self._mac + if update_model: + data[CONF_MODEL] = self._model entry_kw_args["data"] = data if not entry_kw_args: return None diff --git a/tests/components/samsungtv/test_config_flow.py b/tests/components/samsungtv/test_config_flow.py index 40397a68d7d..11aaf12d9ee 100644 --- a/tests/components/samsungtv/test_config_flow.py +++ b/tests/components/samsungtv/test_config_flow.py @@ -1420,6 +1420,36 @@ async def test_update_missing_mac_unique_id_added_from_zeroconf( assert entry.unique_id == "be9554b9-c9fb-41f4-8920-22da015376a4" +@pytest.mark.usefixtures("remote", "rest_api_failing") +async def test_update_missing_model_added_from_ssdp(hass: HomeAssistant) -> None: + """Test missing model added via ssdp on legacy models.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=MOCK_OLD_ENTRY, + unique_id=None, + ) + entry.add_to_hass(hass) + with patch( + "homeassistant.components.samsungtv.async_setup", + return_value=True, + ) as mock_setup, patch( + "homeassistant.components.samsungtv.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_SSDP}, + data=MOCK_SSDP_DATA, + ) + await hass.async_block_till_done() + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + assert result["type"] == "abort" + assert result["reason"] == "already_configured" + assert entry.data[CONF_MODEL] == "fake_model" + + @pytest.mark.usefixtures("remotews", "rest_api", "remoteencws_failing") async def test_update_missing_mac_unique_id_ssdp_location_added_from_ssdp( hass: HomeAssistant, From d1d6c6b9231f23455e763b89bb95de1b665b6b24 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 17 May 2022 21:50:03 +0200 Subject: [PATCH 541/930] Cleanup SamsungTV log message (#71987) --- homeassistant/components/samsungtv/bridge.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index c3201a493eb..fe0b102647a 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -307,9 +307,7 @@ class SamsungTVLegacyBridge(SamsungTVBridge): if self._remote is None: # We need to create a new instance to reconnect. try: - LOGGER.debug( - "Create SamsungTVLegacyBridge for %s (%s)", CONF_NAME, self.host - ) + LOGGER.debug("Create SamsungTVLegacyBridge for %s", self.host) self._remote = Remote(self.config.copy()) # This is only happening when the auth was switched to DENY # A removed auth will lead to socket timeout because waiting for auth popup is just an open socket From bfb47eb212ea1a00aa42140b3d898419ebd553bf Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 17 May 2022 22:42:37 +0200 Subject: [PATCH 542/930] Final clean up of helpers accessed via hass (#72032) * Final clean up of helpers accessed via hass * Fix circular dep * Fix import --- homeassistant/components/cloud/__init__.py | 13 ++----- homeassistant/components/demo/__init__.py | 5 +-- homeassistant/components/homekit/__init__.py | 4 +- homeassistant/components/ombi/__init__.py | 3 +- homeassistant/components/onboarding/views.py | 3 +- .../components/proxmoxve/__init__.py | 5 +-- homeassistant/components/sentry/__init__.py | 9 +++-- homeassistant/components/zeroconf/__init__.py | 4 +- homeassistant/helpers/device_registry.py | 12 ++++-- tests/components/cloud/test_binary_sensor.py | 5 +-- .../components/config/test_config_entries.py | 6 +-- tests/components/fan/test_reproduce_state.py | 38 ++++++++++--------- tests/components/homekit/test_homekit.py | 8 ++-- tests/components/plex/test_device_handling.py | 18 +++------ tests/helpers/test_instance_id.py | 6 ++- tests/helpers/test_integration_platform.py | 9 ++--- tests/helpers/test_system_info.py | 9 +++-- 17 files changed, 75 insertions(+), 82 deletions(-) diff --git a/homeassistant/components/cloud/__init__.py b/homeassistant/components/cloud/__init__.py index 559453fe02b..aa31f796491 100644 --- a/homeassistant/components/cloud/__init__.py +++ b/homeassistant/components/cloud/__init__.py @@ -22,6 +22,7 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, entityfilter from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -267,15 +268,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return loaded = True - await hass.helpers.discovery.async_load_platform( - Platform.BINARY_SENSOR, DOMAIN, {}, config - ) - await hass.helpers.discovery.async_load_platform( - Platform.STT, DOMAIN, {}, config - ) - await hass.helpers.discovery.async_load_platform( - Platform.TTS, DOMAIN, {}, config - ) + await async_load_platform(hass, Platform.BINARY_SENSOR, DOMAIN, {}, config) + await async_load_platform(hass, Platform.STT, DOMAIN, {}, config) + await async_load_platform(hass, Platform.TTS, DOMAIN, {}, config) async_dispatcher_send( hass, SIGNAL_CLOUD_CONNECTION_STATE, CloudConnectionState.CLOUD_CONNECTED diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py index 9c399c67f35..d6c5a5d3afc 100644 --- a/homeassistant/components/demo/__init__.py +++ b/homeassistant/components/demo/__init__.py @@ -18,6 +18,7 @@ from homeassistant.const import ( ) import homeassistant.core as ha from homeassistant.core import HomeAssistant +from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util @@ -71,9 +72,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # Set up demo platforms for platform in COMPONENTS_WITH_DEMO_PLATFORM: - hass.async_create_task( - hass.helpers.discovery.async_load_platform(platform, DOMAIN, {}, config) - ) + hass.async_create_task(async_load_platform(hass, platform, DOMAIN, {}, config)) config.setdefault(ha.DOMAIN, {}) config.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 1cba9746609..5d7e647e466 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -46,7 +46,7 @@ from homeassistant.const import ( ) from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State, callback from homeassistant.exceptions import HomeAssistantError, Unauthorized -from homeassistant.helpers import device_registry, entity_registry +from homeassistant.helpers import device_registry, entity_registry, instance_id import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entityfilter import ( BASE_FILTER_SCHEMA, @@ -726,7 +726,7 @@ class HomeKit: return self.status = STATUS_WAIT async_zc_instance = await zeroconf.async_get_async_instance(self.hass) - uuid = await self.hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(self.hass) await self.hass.async_add_executor_job(self.setup, async_zc_instance, uuid) self.aid_storage = AccessoryAidStorage(self.hass, self._entry_id) await self.aid_storage.async_initialize() diff --git a/homeassistant/components/ombi/__init__.py b/homeassistant/components/ombi/__init__.py index bdf8334ff02..b67b097dfbf 100644 --- a/homeassistant/components/ombi/__init__.py +++ b/homeassistant/components/ombi/__init__.py @@ -16,6 +16,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.typing import ConfigType from .const import ( @@ -152,6 +153,6 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: submit_tv_request, schema=SUBMIT_TV_REQUEST_SERVICE_SCHEMA, ) - hass.helpers.discovery.load_platform(Platform.SENSOR, DOMAIN, {}, config) + load_platform(hass, Platform.SENSOR, DOMAIN, {}, config) return True diff --git a/homeassistant/components/onboarding/views.py b/homeassistant/components/onboarding/views.py index 07b3dfa9cc2..7f40ad87e84 100644 --- a/homeassistant/components/onboarding/views.py +++ b/homeassistant/components/onboarding/views.py @@ -14,6 +14,7 @@ from homeassistant.components.http.const import KEY_HASS_REFRESH_TOKEN_ID from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView from homeassistant.core import callback +from homeassistant.helpers import area_registry as ar from homeassistant.helpers.system_info import async_get_system_info from homeassistant.helpers.translation import async_get_translations @@ -152,7 +153,7 @@ class UserOnboardingView(_BaseOnboardingView): hass, data["language"], "area", {DOMAIN} ) - area_registry = await hass.helpers.area_registry.async_get_registry() + area_registry = ar.async_get(hass) for area in DEFAULT_AREAS: area_registry.async_create( diff --git a/homeassistant/components/proxmoxve/__init__.py b/homeassistant/components/proxmoxve/__init__.py index d10e64772ae..1334a66cfa0 100644 --- a/homeassistant/components/proxmoxve/__init__.py +++ b/homeassistant/components/proxmoxve/__init__.py @@ -21,6 +21,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, @@ -178,9 +179,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: for component in PLATFORMS: await hass.async_create_task( - hass.helpers.discovery.async_load_platform( - component, DOMAIN, {"config": config}, config - ) + async_load_platform(hass, component, DOMAIN, {"config": config}, config) ) return True diff --git a/homeassistant/components/sentry/__init__.py b/homeassistant/components/sentry/__init__.py index ab3b80414dd..3037f1dc374 100644 --- a/homeassistant/components/sentry/__init__.py +++ b/homeassistant/components/sentry/__init__.py @@ -16,8 +16,9 @@ from homeassistant.const import ( __version__ as current_version, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import config_validation as cv, entity_platform +from homeassistant.helpers import config_validation as cv, entity_platform, instance_id from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.system_info import async_get_system_info from homeassistant.loader import Integration, async_get_custom_components from .const import ( @@ -67,8 +68,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Additional/extra data collection channel = get_channel(current_version) - huuid = await hass.helpers.instance_id.async_get() - system_info = await hass.helpers.system_info.async_get_system_info() + huuid = await instance_id.async_get(hass) + system_info = await async_get_system_info(hass) custom_components = await async_get_custom_components(hass) tracing = {} @@ -100,7 +101,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def update_system_info(now): nonlocal system_info - system_info = await hass.helpers.system_info.async_get_system_info() + system_info = await async_get_system_info(hass) # Update system info every hour async_call_later(hass, 3600, update_system_info) diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 2c16868f9de..29afd5fc236 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -28,7 +28,7 @@ from homeassistant.const import ( ) from homeassistant.core import Event, HomeAssistant, callback from homeassistant.data_entry_flow import BaseServiceInfo -from homeassistant.helpers import discovery_flow +from homeassistant.helpers import discovery_flow, instance_id import homeassistant.helpers.config_validation as cv from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.typing import ConfigType @@ -198,7 +198,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: Wait till started or otherwise HTTP is not up and running. """ - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) await _async_register_hass_zc_service(hass, aio_zc, uuid) async def _async_zeroconf_hass_stop(_event: Event) -> None: diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 7299da43958..901242607e3 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -420,6 +420,10 @@ class DeviceRegistry: via_device_id: str | None | UndefinedType = UNDEFINED, ) -> DeviceEntry | None: """Update device attributes.""" + # Circular dep + # pylint: disable=import-outside-toplevel + from . import area_registry as ar + old = self.devices[device_id] new_values: dict[str, Any] = {} # Dict with new key/value pairs @@ -442,13 +446,13 @@ class DeviceRegistry: disabled_by = DeviceEntryDisabler(disabled_by) if ( - suggested_area not in (UNDEFINED, None, "") + suggested_area is not None + and suggested_area is not UNDEFINED + and suggested_area != "" and area_id is UNDEFINED and old.area_id is None ): - area = self.hass.helpers.area_registry.async_get( - self.hass - ).async_get_or_create(suggested_area) + area = ar.async_get(self.hass).async_get_or_create(suggested_area) area_id = area.id if ( diff --git a/tests/components/cloud/test_binary_sensor.py b/tests/components/cloud/test_binary_sensor.py index 0aacb189d72..fb2ca562715 100644 --- a/tests/components/cloud/test_binary_sensor.py +++ b/tests/components/cloud/test_binary_sensor.py @@ -2,6 +2,7 @@ from unittest.mock import Mock, patch from homeassistant.components.cloud.const import DISPATCHER_REMOTE_UPDATE +from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.setup import async_setup_component @@ -14,9 +15,7 @@ async def test_remote_connection_sensor(hass): assert hass.states.get("binary_sensor.remote_ui") is None # Fake connection/discovery - await hass.helpers.discovery.async_load_platform( - "binary_sensor", "cloud", {}, {"cloud": {}} - ) + await async_load_platform(hass, "binary_sensor", "cloud", {}, {"cloud": {}}) # Mock test env cloud = hass.data["cloud"] = Mock() diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 6366eca4c6d..bb0d67fc306 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -12,7 +12,7 @@ from homeassistant.components.config import config_entries from homeassistant.config_entries import HANDLERS, ConfigFlow from homeassistant.core import callback from homeassistant.generated import config_flows -from homeassistant.helpers import config_validation as cv +from homeassistant.helpers import config_entry_flow, config_validation as cv from homeassistant.setup import async_setup_component from tests.common import ( @@ -68,9 +68,7 @@ async def test_get_entries(hass, client, clear_handlers): """Return options flow support for this handler.""" return True - hass.helpers.config_entry_flow.register_discovery_flow( - "comp2", "Comp 2", lambda: None - ) + config_entry_flow.register_discovery_flow("comp2", "Comp 2", lambda: None) entry = MockConfigEntry( domain="comp1", diff --git a/tests/components/fan/test_reproduce_state.py b/tests/components/fan/test_reproduce_state.py index cca56d3e128..e33f1005d75 100644 --- a/tests/components/fan/test_reproduce_state.py +++ b/tests/components/fan/test_reproduce_state.py @@ -183,8 +183,8 @@ async def test_modern_turn_on_invalid(hass, start_state): set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") # Turn on with an invalid config (speed, percentage, preset_modes all None) - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_INVALID_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_INVALID_STATE)] ) assert len(turn_on_calls) == 1 @@ -227,8 +227,8 @@ async def test_modern_turn_on_percentage_from_different_speed(hass, start_state) set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] ) assert len(turn_on_calls) == 1 @@ -256,8 +256,8 @@ async def test_modern_turn_on_percentage_from_same_speed(hass): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] ) assert len(turn_on_calls) == 1 @@ -293,8 +293,8 @@ async def test_modern_turn_on_preset_mode_from_different_speed(hass, start_state set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] ) assert len(turn_on_calls) == 1 @@ -324,8 +324,8 @@ async def test_modern_turn_on_preset_mode_from_same_speed(hass): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] ) assert len(turn_on_calls) == 1 @@ -361,8 +361,9 @@ async def test_modern_turn_on_preset_mode_reverse(hass, start_state): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_PRESET_MODE_AUTO_REVERSE_STATE)] + await async_reproduce_state( + hass, + [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_PRESET_MODE_AUTO_REVERSE_STATE)], ) assert len(turn_on_calls) == 1 @@ -403,8 +404,8 @@ async def test_modern_to_preset(hass, start_state): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PRESET_MODE_AUTO_STATE)] ) assert len(turn_on_calls) == 0 @@ -439,8 +440,8 @@ async def test_modern_to_percentage(hass, start_state): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] + await async_reproduce_state( + hass, [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_ON_PERCENTAGE15_STATE)] ) assert len(turn_on_calls) == 0 @@ -467,8 +468,9 @@ async def test_modern_direction(hass): set_percentage_mode = async_mock_service(hass, "fan", "set_percentage") set_preset_mode = async_mock_service(hass, "fan", "set_preset_mode") - await hass.helpers.state.async_reproduce_state( - [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_PRESET_MODE_AUTO_REVERSE_STATE)] + await async_reproduce_state( + hass, + [State(MODERN_FAN_ENTITY, "on", MODERN_FAN_PRESET_MODE_AUTO_REVERSE_STATE)], ) assert len(turn_on_calls) == 0 diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index ff0f3f4d72b..be514ce2b6a 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -49,7 +49,7 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.core import HomeAssistantError, State -from homeassistant.helpers import device_registry, entity_registry as er +from homeassistant.helpers import device_registry, entity_registry as er, instance_id from homeassistant.helpers.entityfilter import ( CONF_EXCLUDE_DOMAINS, CONF_EXCLUDE_ENTITIES, @@ -201,7 +201,7 @@ async def test_homekit_setup(hass, hk_driver, mock_async_zeroconf): hass.states.async_set("light.demo", "on") hass.states.async_set("light.demo2", "on") zeroconf_mock = MagicMock() - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) with patch(f"{PATH_HOMEKIT}.HomeDriver", return_value=hk_driver) as mock_driver: await hass.async_add_executor_job(homekit.setup, zeroconf_mock, uuid) @@ -245,7 +245,7 @@ async def test_homekit_setup_ip_address(hass, hk_driver, mock_async_zeroconf): ) path = get_persist_fullpath_for_entry_id(hass, entry.entry_id) - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) with patch(f"{PATH_HOMEKIT}.HomeDriver", return_value=hk_driver) as mock_driver: await hass.async_add_executor_job(homekit.setup, mock_async_zeroconf, uuid) mock_driver.assert_called_with( @@ -287,7 +287,7 @@ async def test_homekit_setup_advertise_ip(hass, hk_driver, mock_async_zeroconf): async_zeroconf_instance = MagicMock() path = get_persist_fullpath_for_entry_id(hass, entry.entry_id) - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) with patch(f"{PATH_HOMEKIT}.HomeDriver", return_value=hk_driver) as mock_driver: await hass.async_add_executor_job(homekit.setup, async_zeroconf_instance, uuid) mock_driver.assert_called_with( diff --git a/tests/components/plex/test_device_handling.py b/tests/components/plex/test_device_handling.py index 6d2b7524d34..38145b27deb 100644 --- a/tests/components/plex/test_device_handling.py +++ b/tests/components/plex/test_device_handling.py @@ -88,16 +88,12 @@ async def test_migrate_transient_devices( ) assert ( - len( - hass.helpers.entity_registry.async_entries_for_device( - entity_registry, device_id=plexweb_device.id - ) - ) + len(er.async_entries_for_device(entity_registry, device_id=plexweb_device.id)) == 1 ) assert ( len( - hass.helpers.entity_registry.async_entries_for_device( + er.async_entries_for_device( entity_registry, device_id=non_plexweb_device.id ) ) @@ -113,16 +109,12 @@ async def test_migrate_transient_devices( ) assert ( - len( - hass.helpers.entity_registry.async_entries_for_device( - entity_registry, device_id=plexweb_device.id - ) - ) + len(er.async_entries_for_device(entity_registry, device_id=plexweb_device.id)) == 0 ) assert ( len( - hass.helpers.entity_registry.async_entries_for_device( + er.async_entries_for_device( entity_registry, device_id=non_plexweb_device.id ) ) @@ -130,7 +122,7 @@ async def test_migrate_transient_devices( ) assert ( len( - hass.helpers.entity_registry.async_entries_for_device( + er.async_entries_for_device( entity_registry, device_id=plex_service_device.id ) ) diff --git a/tests/helpers/test_instance_id.py b/tests/helpers/test_instance_id.py index c8417c519a1..a8c040f48ba 100644 --- a/tests/helpers/test_instance_id.py +++ b/tests/helpers/test_instance_id.py @@ -1,10 +1,12 @@ """Tests for instance ID helper.""" from unittest.mock import patch +from homeassistant.helpers import instance_id + async def test_get_id_empty(hass, hass_storage): """Get unique ID.""" - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) assert uuid is not None # Assert it's stored assert hass_storage["core.uuid"]["data"]["uuid"] == uuid @@ -15,7 +17,7 @@ async def test_get_id_migrate(hass, hass_storage): with patch( "homeassistant.util.json.load_json", return_value={"uuid": "1234"} ), patch("os.path.isfile", return_value=True), patch("os.remove") as mock_remove: - uuid = await hass.helpers.instance_id.async_get() + uuid = await instance_id.async_get(hass) assert uuid == "1234" diff --git a/tests/helpers/test_integration_platform.py b/tests/helpers/test_integration_platform.py index a9d0da0b849..584af602062 100644 --- a/tests/helpers/test_integration_platform.py +++ b/tests/helpers/test_integration_platform.py @@ -3,6 +3,7 @@ from unittest.mock import Mock from homeassistant.helpers.integration_platform import ( async_process_integration_platform_for_component, + async_process_integration_platforms, ) from homeassistant.setup import ATTR_COMPONENT, EVENT_COMPONENT_LOADED @@ -24,8 +25,8 @@ async def test_process_integration_platforms(hass): """Process platform.""" processed.append((domain, platform)) - await hass.helpers.integration_platform.async_process_integration_platforms( - "platform_to_check", _process_platform + await async_process_integration_platforms( + hass, "platform_to_check", _process_platform ) assert len(processed) == 1 @@ -40,9 +41,7 @@ async def test_process_integration_platforms(hass): assert processed[1][1] == event_platform # Verify we only process the platform once if we call it manually - await hass.helpers.integration_platform.async_process_integration_platform_for_component( - hass, "event" - ) + await async_process_integration_platform_for_component(hass, "event") assert len(processed) == 2 diff --git a/tests/helpers/test_system_info.py b/tests/helpers/test_system_info.py index e4aba5fbb24..a3b6dafe600 100644 --- a/tests/helpers/test_system_info.py +++ b/tests/helpers/test_system_info.py @@ -3,11 +3,12 @@ import json from unittest.mock import patch from homeassistant.const import __version__ as current_version +from homeassistant.helpers.system_info import async_get_system_info async def test_get_system_info(hass): """Test the get system info.""" - info = await hass.helpers.system_info.async_get_system_info() + info = await async_get_system_info(hass) assert isinstance(info, dict) assert info["version"] == current_version assert info["user"] is not None @@ -19,18 +20,18 @@ async def test_container_installationtype(hass): with patch("platform.system", return_value="Linux"), patch( "os.path.isfile", return_value=True ), patch("homeassistant.helpers.system_info.getuser", return_value="root"): - info = await hass.helpers.system_info.async_get_system_info() + info = await async_get_system_info(hass) assert info["installation_type"] == "Home Assistant Container" with patch("platform.system", return_value="Linux"), patch( "os.path.isfile", side_effect=lambda file: file == "/.dockerenv" ), patch("homeassistant.helpers.system_info.getuser", return_value="user"): - info = await hass.helpers.system_info.async_get_system_info() + info = await async_get_system_info(hass) assert info["installation_type"] == "Unsupported Third Party Container" async def test_getuser_keyerror(hass): """Test getuser keyerror.""" with patch("homeassistant.helpers.system_info.getuser", side_effect=KeyError): - info = await hass.helpers.system_info.async_get_system_info() + info = await async_get_system_info(hass) assert info["user"] is None From a4c1bcefb9d2a6f2aa0bc189fca496d46c78e3b0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 May 2022 18:12:15 -0500 Subject: [PATCH 543/930] Tune sqlite based on configured settings (#72016) --- homeassistant/components/recorder/util.py | 14 ++++- tests/components/recorder/test_util.py | 64 ++++++++++++++++++++--- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index eb99c304808..e464ac4126b 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -426,8 +426,18 @@ def setup_connection_for_dialect( version or version_string, "SQLite", MIN_VERSION_SQLITE ) - # approximately 8MiB of memory - execute_on_connection(dbapi_connection, "PRAGMA cache_size = -8192") + # The upper bound on the cache size is approximately 16MiB of memory + execute_on_connection(dbapi_connection, "PRAGMA cache_size = -16384") + + # + # Enable FULL synchronous if they have a commit interval of 0 + # or NORMAL if they do not. + # + # https://sqlite.org/pragma.html#pragma_synchronous + # The synchronous=NORMAL setting is a good choice for most applications running in WAL mode. + # + synchronous = "NORMAL" if instance.commit_interval else "FULL" + execute_on_connection(dbapi_connection, f"PRAGMA synchronous={synchronous}") # enable support for foreign keys execute_on_connection(dbapi_connection, "PRAGMA foreign_keys=ON") diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index c650ec20fb0..fea255647ec 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -234,18 +234,70 @@ def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_num util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, True) - assert len(execute_args) == 4 + assert len(execute_args) == 5 assert execute_args[0] == "PRAGMA journal_mode=WAL" assert execute_args[1] == "SELECT sqlite_version()" - assert execute_args[2] == "PRAGMA cache_size = -8192" - assert execute_args[3] == "PRAGMA foreign_keys=ON" + assert execute_args[2] == "PRAGMA cache_size = -16384" + assert execute_args[3] == "PRAGMA synchronous=NORMAL" + assert execute_args[4] == "PRAGMA foreign_keys=ON" execute_args = [] util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, False) - assert len(execute_args) == 2 - assert execute_args[0] == "PRAGMA cache_size = -8192" - assert execute_args[1] == "PRAGMA foreign_keys=ON" + assert len(execute_args) == 3 + assert execute_args[0] == "PRAGMA cache_size = -16384" + assert execute_args[1] == "PRAGMA synchronous=NORMAL" + assert execute_args[2] == "PRAGMA foreign_keys=ON" + + assert instance_mock._db_supports_row_number == db_supports_row_number + + +@pytest.mark.parametrize( + "sqlite_version, db_supports_row_number", + [ + ("3.25.0", True), + ("3.24.0", False), + ], +) +def test_setup_connection_for_dialect_sqlite_zero_commit_interval( + sqlite_version, db_supports_row_number +): + """Test setting up the connection for a sqlite dialect with a zero commit interval.""" + instance_mock = MagicMock(_db_supports_row_number=True, commit_interval=0) + execute_args = [] + close_mock = MagicMock() + + def execute_mock(statement): + nonlocal execute_args + execute_args.append(statement) + + def fetchall_mock(): + nonlocal execute_args + if execute_args[-1] == "SELECT sqlite_version()": + return [[sqlite_version]] + return None + + def _make_cursor_mock(*_): + return MagicMock(execute=execute_mock, close=close_mock, fetchall=fetchall_mock) + + dbapi_connection = MagicMock(cursor=_make_cursor_mock) + + util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, True) + + assert len(execute_args) == 5 + assert execute_args[0] == "PRAGMA journal_mode=WAL" + assert execute_args[1] == "SELECT sqlite_version()" + assert execute_args[2] == "PRAGMA cache_size = -16384" + assert execute_args[3] == "PRAGMA synchronous=FULL" + assert execute_args[4] == "PRAGMA foreign_keys=ON" + + execute_args = [] + util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, False) + + assert len(execute_args) == 3 + assert execute_args[0] == "PRAGMA cache_size = -16384" + assert execute_args[1] == "PRAGMA synchronous=FULL" + assert execute_args[2] == "PRAGMA foreign_keys=ON" assert instance_mock._db_supports_row_number == db_supports_row_number From 1d6659224f49f6dcc7b8f1a7d06766620a8118a4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 18 May 2022 00:22:07 +0000 Subject: [PATCH 544/930] [ci skip] Translation update --- .../aladdin_connect/translations/id.json | 27 ++++++++++++ .../aladdin_connect/translations/ko.json | 27 ++++++++++++ .../components/baf/translations/id.json | 23 ++++++++++ .../components/baf/translations/ko.json | 10 +++++ .../components/fan/translations/ko.json | 1 + .../geocaching/translations/bg.json | 3 ++ .../geocaching/translations/ko.json | 25 +++++++++++ .../components/group/translations/de.json | 4 +- .../components/knx/translations/en.json | 2 +- .../components/knx/translations/et.json | 2 +- .../components/knx/translations/id.json | 2 +- .../components/knx/translations/it.json | 2 +- .../components/knx/translations/tr.json | 2 +- .../components/knx/translations/zh-Hant.json | 2 +- .../litterrobot/translations/sensor.id.json | 24 +++++++++++ .../litterrobot/translations/sensor.ko.json | 24 +++++++++++ .../components/meater/translations/ko.json | 11 +++++ .../components/onewire/translations/ko.json | 2 + .../components/recorder/translations/ko.json | 7 +++ .../components/siren/translations/hu.json | 3 ++ .../components/siren/translations/id.json | 3 ++ .../siren/translations/zh-Hant.json | 3 ++ .../components/slack/translations/id.json | 25 +++++++++++ .../components/slack/translations/ko.json | 20 +++++++++ .../components/sms/translations/ko.json | 1 + .../ukraine_alarm/translations/ko.json | 43 +++++++++++++++++++ .../components/vulcan/translations/ko.json | 12 ++++++ .../components/ws66i/translations/ko.json | 34 +++++++++++++++ .../components/yolink/translations/bg.json | 20 +++++++++ .../components/yolink/translations/et.json | 25 +++++++++++ .../components/yolink/translations/fr.json | 25 +++++++++++ .../components/yolink/translations/hu.json | 25 +++++++++++ .../components/yolink/translations/id.json | 24 +++++++++++ .../components/yolink/translations/it.json | 25 +++++++++++ .../components/yolink/translations/ja.json | 25 +++++++++++ .../components/yolink/translations/ko.json | 23 ++++++++++ .../components/yolink/translations/nl.json | 25 +++++++++++ .../components/yolink/translations/pt-BR.json | 25 +++++++++++ .../components/yolink/translations/tr.json | 25 +++++++++++ .../yolink/translations/zh-Hant.json | 25 +++++++++++ 40 files changed, 628 insertions(+), 8 deletions(-) create mode 100644 homeassistant/components/aladdin_connect/translations/id.json create mode 100644 homeassistant/components/aladdin_connect/translations/ko.json create mode 100644 homeassistant/components/baf/translations/id.json create mode 100644 homeassistant/components/geocaching/translations/ko.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.id.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.ko.json create mode 100644 homeassistant/components/meater/translations/ko.json create mode 100644 homeassistant/components/recorder/translations/ko.json create mode 100644 homeassistant/components/siren/translations/hu.json create mode 100644 homeassistant/components/siren/translations/id.json create mode 100644 homeassistant/components/siren/translations/zh-Hant.json create mode 100644 homeassistant/components/slack/translations/id.json create mode 100644 homeassistant/components/ukraine_alarm/translations/ko.json create mode 100644 homeassistant/components/vulcan/translations/ko.json create mode 100644 homeassistant/components/ws66i/translations/ko.json create mode 100644 homeassistant/components/yolink/translations/bg.json create mode 100644 homeassistant/components/yolink/translations/et.json create mode 100644 homeassistant/components/yolink/translations/fr.json create mode 100644 homeassistant/components/yolink/translations/hu.json create mode 100644 homeassistant/components/yolink/translations/id.json create mode 100644 homeassistant/components/yolink/translations/it.json create mode 100644 homeassistant/components/yolink/translations/ja.json create mode 100644 homeassistant/components/yolink/translations/ko.json create mode 100644 homeassistant/components/yolink/translations/nl.json create mode 100644 homeassistant/components/yolink/translations/pt-BR.json create mode 100644 homeassistant/components/yolink/translations/tr.json create mode 100644 homeassistant/components/yolink/translations/zh-Hant.json diff --git a/homeassistant/components/aladdin_connect/translations/id.json b/homeassistant/components/aladdin_connect/translations/id.json new file mode 100644 index 00000000000..37a42d096cf --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/id.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Kata Sandi" + }, + "description": "Integrasi Aladdin Connect perlu mengautentikasi ulang akun Anda", + "title": "Autentikasi Ulang Integrasi" + }, + "user": { + "data": { + "password": "Kata Sandi", + "username": "Nama Pengguna" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/ko.json b/homeassistant/components/aladdin_connect/translations/ko.json new file mode 100644 index 00000000000..d2e9a9e1375 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/ko.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "description": "Aladdin Connect \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/id.json b/homeassistant/components/baf/translations/id.json new file mode 100644 index 00000000000..116742e613c --- /dev/null +++ b/homeassistant/components/baf/translations/id.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi", + "ipv6_not_supported": "IPv6 tidak didukung." + }, + "error": { + "cannot_connect": "Gagal terhubung", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "Ingin menyiapkan {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "Alamat IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/ko.json b/homeassistant/components/baf/translations/ko.json index a8884c0403e..9bf0efc5136 100644 --- a/homeassistant/components/baf/translations/ko.json +++ b/homeassistant/components/baf/translations/ko.json @@ -1,12 +1,22 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "ipv6_not_supported": "IPv6\uc740 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "flow_title": "{name} - {model} ( {ip_address} )", "step": { "discovery_confirm": { "description": "{name} - {model} ({ip_address}) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + } } } } diff --git a/homeassistant/components/fan/translations/ko.json b/homeassistant/components/fan/translations/ko.json index c2157f29e72..6616d7a39f4 100644 --- a/homeassistant/components/fan/translations/ko.json +++ b/homeassistant/components/fan/translations/ko.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "{entity_name} \ud1a0\uae00", "turn_off": "{entity_name}\uc744(\ub97c) \ub044\uae30", "turn_on": "{entity_name}\uc744(\ub97c) \ucf1c\uae30" }, diff --git a/homeassistant/components/geocaching/translations/bg.json b/homeassistant/components/geocaching/translations/bg.json index 00c237dd077..d3ca579dff7 100644 --- a/homeassistant/components/geocaching/translations/bg.json +++ b/homeassistant/components/geocaching/translations/bg.json @@ -9,6 +9,9 @@ "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d\u043e" }, "step": { + "pick_implementation": { + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, "reauth_confirm": { "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" } diff --git a/homeassistant/components/geocaching/translations/ko.json b/homeassistant/components/geocaching/translations/ko.json new file mode 100644 index 00000000000..0d46ef64d4b --- /dev/null +++ b/homeassistant/components/geocaching/translations/ko.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", + "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "create_entry": { + "default": "\uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + }, + "reauth_confirm": { + "description": "Geocaching \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/group/translations/de.json b/homeassistant/components/group/translations/de.json index aeaedd3a7cc..c76c92ccb19 100644 --- a/homeassistant/components/group/translations/de.json +++ b/homeassistant/components/group/translations/de.json @@ -108,9 +108,9 @@ "cover": "Abdeckung-Gruppe", "fan": "L\u00fcfter-Gruppe", "light": "Licht-Gruppe", - "lock": "Gruppe sperren", + "lock": "Schloss-Gruppe", "media_player": "Media-Player-Gruppe", - "switch": "Gruppe wechseln" + "switch": "Schalter-Gruppe" }, "title": "Gruppe hinzuf\u00fcgen" } diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index ba073eba888..fd8ac9499f1 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -104,7 +104,7 @@ "multicast_group": "Used for routing and discovery. Default: `224.0.23.12`", "multicast_port": "Used for routing and discovery. Default: `3671`", "rate_limit": "Maximum outgoing telegrams per second.\nRecommended: 20 to 40", - "state_updater": "Globally enable or disable reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve states from the KNX Bus, `sync_state` entity options will have no effect." + "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index c4410d490e4..b7cd079ad3f 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -104,7 +104,7 @@ "multicast_group": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikimisi: \"224.0.23.12\"", "multicast_port": "Kasutatakse marsruutimiseks ja avastamiseks. Vaikev\u00e4\u00e4rtus: \"3671\"", "rate_limit": "Maksimaalne v\u00e4ljaminevate telegrammide arv sekundis.\nSoovitatav: 20 kuni 40", - "state_updater": "KNX siini lugemisolekute globaalne lubamine v\u00f5i keelamine. Kui see on keelatud, ei too Home Assistant aktiivselt olekuid KNX siinilt, olemi s\u00fcnkroonimisoleku valikudi ei m\u00f5juta." + "state_updater": "M\u00e4\u00e4ra KNX siini olekute lugemise vaikev\u00e4\u00e4rtused. Kui see on keelatud, ei too Home Assistant aktiivselt olemi olekuid KNX siinilt. Saab alistada olemivalikute s\u00fcnkroonimise_olekuga." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 92d812c1b1b..e2ca98fff94 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -104,7 +104,7 @@ "multicast_group": "Digunakan untuk perutean dan penemuan. Bawaan: `224.0.23.12`", "multicast_port": "Digunakan untuk perutean dan penemuan. Bawaan: `3671`", "rate_limit": "Telegram keluar maksimum per detik.\nDirekomendasikan: 20 hingga 40", - "state_updater": "Secara global mengaktifkan atau menonaktifkan status pembacaan dari KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status dari KNX Bus, opsi entitas 'sync_state' tidak akan berpengaruh." + "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas 'sync_state'." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index e47d93a27a5..ac780ea96ab 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -104,7 +104,7 @@ "multicast_group": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `224.0.23.12`", "multicast_port": "Utilizzato per l'instradamento e il rilevamento. Predefinito: `3671`", "rate_limit": "Numero massimo di telegrammi in uscita al secondo.\n Consigliato: da 20 a 40", - "state_updater": "Abilita o disabilita globalmente gli stati di lettura dal bus KNX. Se disabilitato, Home Assistant non recuperer\u00e0 attivamente gli stati dal bus KNX, le opzioni dell'entit\u00e0 `sync_state` non avranno alcun effetto." + "state_updater": "Impostazione predefinita per la lettura degli stati dal bus KNX. Se disabilitata Home Assistant non recuperer\u00e0 attivamente gli stati delle entit\u00e0 dal bus KNX. Pu\u00f2 essere sovrascritta dalle opzioni dell'entit\u00e0 `sync_state`." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/tr.json b/homeassistant/components/knx/translations/tr.json index c10f35ca48f..5a2863baaf4 100644 --- a/homeassistant/components/knx/translations/tr.json +++ b/homeassistant/components/knx/translations/tr.json @@ -104,7 +104,7 @@ "multicast_group": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"224.0.23.12\"", "multicast_port": "Y\u00f6nlendirme ve ke\u015fif i\u00e7in kullan\u0131l\u0131r. Varsay\u0131lan: \"3671\"", "rate_limit": "Saniyede maksimum giden telegram say\u0131s\u0131.\n \u00d6nerilen: 20 ila 40", - "state_updater": "KNX Bus'tan okuma durumlar\u0131n\u0131 k\u00fcresel olarak etkinle\u015ftirin veya devre d\u0131\u015f\u0131 b\u0131rak\u0131n. Devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131\u011f\u0131nda, Home Assistant KNX Bus'tan durumlar\u0131 aktif olarak almaz, 'sync_state' varl\u0131k se\u00e7eneklerinin hi\u00e7bir etkisi olmaz." + "state_updater": "KNX Bus'tan okuma durumlar\u0131 i\u00e7in varsay\u0131lan\u0131 ayarlay\u0131n. Devre d\u0131\u015f\u0131 b\u0131rak\u0131ld\u0131\u011f\u0131nda, Home Assistant varl\u0131k durumlar\u0131n\u0131 KNX Bus'tan aktif olarak almaz. 'sync_state' varl\u0131k se\u00e7enekleri taraf\u0131ndan ge\u00e7ersiz k\u0131l\u0131nabilir." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index 8f740b85f6d..613cf4f74e5 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -104,7 +104,7 @@ "multicast_group": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`224.0.23.12`", "multicast_port": "\u4f7f\u7528\u65bc\u8def\u7531\u8207\u81ea\u52d5\u641c\u7d22\u3002\u9810\u8a2d\u503c\uff1a`3671`", "rate_limit": "\u6bcf\u79d2\u6700\u5927 Telegram \u767c\u9001\u91cf\u3002\u5efa\u8b70\uff1a20 - 40", - "state_updater": "\u5168\u5c40\u958b\u555f\u6216\u95dc\u9589\u81ea KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u72c0\u614b\uff0c`sync_state` \u5be6\u9ad4\u9078\u9805\u5c07\u4e0d\u5177\u6548\u679c\u3002" + "state_updater": "\u8a2d\u5b9a\u9810\u8a2d KNX Bus \u8b80\u53d6\u72c0\u614b\u3002\u7576\u95dc\u9589\u6642\u3001Home Assistant \u5c07\u4e0d\u6703\u4e3b\u52d5\u5f9e KNX Bus \u7372\u53d6\u5be6\u9ad4\u72c0\u614b\uff0c\u53ef\u88ab`sync_state` \u5be6\u9ad4\u9078\u9805\u8986\u84cb\u3002" } }, "tunnel": { diff --git a/homeassistant/components/litterrobot/translations/sensor.id.json b/homeassistant/components/litterrobot/translations/sensor.id.json new file mode 100644 index 00000000000..20f76ca4322 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.id.json @@ -0,0 +1,24 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "Bonnet Dihapus", + "ccc": "Siklus Bersih Selesai", + "ccp": "Siklus Bersih Sedang Berlangsung", + "csf": "Kesalahan Sensor Kucing", + "csi": "Sensor Kucing Terganggu", + "cst": "Waktu Sensor Kucing", + "df1": "Laci Hampir Penuh - Tersisa 2 Siklus", + "df2": "Laci Hampir Penuh - Tersisa 1 Siklus", + "dfs": "Laci Penuh", + "ec": "Siklus Kosong", + "hpf": "Kesalahan Posisi Rumah", + "off": "Mati", + "offline": "Luring", + "otf": "Kesalahan Torsi Berlebih", + "p": "Jeda", + "rdy": "Siap", + "scf": "Kesalahan Sensor Kucing Saat Mulai", + "sdf": "Laci Penuh Saat Memulai" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.ko.json b/homeassistant/components/litterrobot/translations/sensor.ko.json new file mode 100644 index 00000000000..c9c452b4275 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.ko.json @@ -0,0 +1,24 @@ +{ + "state": { + "litterrobot__status_code": { + "br": "\ubcf4\ub137 \uc81c\uac70\ub428", + "ccc": "\uccad\uc18c \uc644\ub8cc", + "ccp": "\uccad\uc18c \uc911", + "csf": "\ucea3\uc13c\uc11c \uc624\ub958", + "csi": "\ucea3\uc13c\uc11c \uc911\uc9c0", + "df1": "\ubcf4\uad00\ud568 \uac70\uc758 \ucc38 - 2\uc0ac\uc774\ud074 \ub0a8\uc74c", + "df2": "\ubcf4\uad00\ud568 \uac70\uc758 \ucc38 - 1\uc0ac\uc774\ud074 \ub0a8\uc74c", + "dfs": "\ubcf4\uad00\ud568 \uac00\ub4dd \ucc38", + "ec": "\ube44\uc6c0 \uc8fc\uae30", + "off": "\uaebc\uc9d0", + "offline": "\uc624\ud504\ub77c\uc778", + "otf": "\ud1a0\ud06c \uc624\ub958 \uc774\uc0c1", + "p": "\uc77c\uc2dc\uc911\uc9c0", + "pd": "\ud540\uce58 \uac10\uc9c0", + "rdy": "\uc900\ube44", + "scf": "\ucea3\uc13c\uc11c \uc624\ub958", + "sdf": "\ubcf4\uad00\ud568 \uac00\ub4dd \ucc38", + "spf": "\ud540\uce58 \uac10\uc9c0" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/meater/translations/ko.json b/homeassistant/components/meater/translations/ko.json new file mode 100644 index 00000000000..b7fdeff3c2a --- /dev/null +++ b/homeassistant/components/meater/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/ko.json b/homeassistant/components/onewire/translations/ko.json index 038be16108a..e6665d83768 100644 --- a/homeassistant/components/onewire/translations/ko.json +++ b/homeassistant/components/onewire/translations/ko.json @@ -17,6 +17,8 @@ }, "user": { "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8", "type": "\uc5f0\uacb0 \uc720\ud615" }, "title": "1-Wire \uc124\uc815\ud558\uae30" diff --git a/homeassistant/components/recorder/translations/ko.json b/homeassistant/components/recorder/translations/ko.json new file mode 100644 index 00000000000..b8cd6320a55 --- /dev/null +++ b/homeassistant/components/recorder/translations/ko.json @@ -0,0 +1,7 @@ +{ + "system_health": { + "info": { + "estimated_db_size": "\uc608\uc0c1 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \ud06c\uae30(MiB)" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/hu.json b/homeassistant/components/siren/translations/hu.json new file mode 100644 index 00000000000..64642eb828d --- /dev/null +++ b/homeassistant/components/siren/translations/hu.json @@ -0,0 +1,3 @@ +{ + "title": "Szir\u00e9na" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/id.json b/homeassistant/components/siren/translations/id.json new file mode 100644 index 00000000000..bcea16c56f1 --- /dev/null +++ b/homeassistant/components/siren/translations/id.json @@ -0,0 +1,3 @@ +{ + "title": "Sirene" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/zh-Hant.json b/homeassistant/components/siren/translations/zh-Hant.json new file mode 100644 index 00000000000..549bad8914b --- /dev/null +++ b/homeassistant/components/siren/translations/zh-Hant.json @@ -0,0 +1,3 @@ +{ + "title": "Siren" +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/id.json b/homeassistant/components/slack/translations/id.json new file mode 100644 index 00000000000..3180294b97e --- /dev/null +++ b/homeassistant/components/slack/translations/id.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Layanan sudah dikonfigurasi" + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "user": { + "data": { + "api_key": "Kunci API", + "icon": "Ikon", + "username": "Nama Pengguna" + }, + "data_description": { + "api_key": "Token API Slack yang digunakan untuk mengirim pesan Slack." + }, + "description": "Lihat dokumentasi tentang mendapatkan kunci API Slack Anda." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/ko.json b/homeassistant/components/slack/translations/ko.json index ffc01a9bbcb..3e0a8596029 100644 --- a/homeassistant/components/slack/translations/ko.json +++ b/homeassistant/components/slack/translations/ko.json @@ -1,7 +1,27 @@ { "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "user": { + "data": { + "api_key": "API \ud0a4", + "default_channel": "\uae30\ubcf8 \ucc44\ub110", + "icon": "\uc544\uc774\ucf58", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "data_description": { + "api_key": "Slack \uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0b4\ub294 \ub370 \uc0ac\uc6a9\ud560 Slack API \ud1a0\ud070\uc785\ub2c8\ub2e4.", + "default_channel": "\uba54\uc2dc\uc9c0\ub97c \ubcf4\ub0bc \ub54c \ucc44\ub110\uc774 \uc9c0\uc815\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 \uc0ac\uc6a9\ud560 \ucc44\ub110\uc785\ub2c8\ub2e4.", + "icon": "Slack \uc774\ubaa8\ud2f0\ucf58\uc744 \uc0ac\uc6a9\uc790 \uc774\ub984\uc758 \uc544\uc774\ucf58\uc73c\ub85c \uc0ac\uc6a9\ud569\ub2c8\ub2e4.", + "username": "Home Assistant\ub294 \uc0ac\uc6a9\uc790 \uc774\ub984\uc73c\ub85c Slack\uc5d0 \uac8c\uc2dc\ud569\ub2c8\ub2e4." + }, "description": "Slack API \ud0a4\ub97c \uac00\uc838\uc624\ub294 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc124\uba85\uc11c\ub97c \ucc38\uc870\ud558\uc2ed\uc2dc\uc624." } } diff --git a/homeassistant/components/sms/translations/ko.json b/homeassistant/components/sms/translations/ko.json index 5ead95c1a27..aa954a52e19 100644 --- a/homeassistant/components/sms/translations/ko.json +++ b/homeassistant/components/sms/translations/ko.json @@ -11,6 +11,7 @@ "step": { "user": { "data": { + "baud_speed": "\uc804\uc1a1 \uc18d\ub3c4", "device": "\uae30\uae30" }, "title": "\ubaa8\ub380\uc5d0 \uc5f0\uacb0\ud558\uae30" diff --git a/homeassistant/components/ukraine_alarm/translations/ko.json b/homeassistant/components/ukraine_alarm/translations/ko.json new file mode 100644 index 00000000000..ab66909f96a --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/ko.json @@ -0,0 +1,43 @@ +{ + "config": { + "abort": { + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "max_regions": "\ucd5c\ub300 5\uac1c \uc9c0\uc5ed\uc744 \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "rate_limit": "\uc694\uccad\uc774 \ub108\ubb34 \ub9ce\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "community": { + "data": { + "region": "\uc9c0\uc5ed" + }, + "description": "\uc8fc \ubc0f \uc9c0\uc5ed\ubfd0\ub9cc \uc544\ub2c8\ub77c \ud574\ub2f9 \uc9c0\uc5ed\uc758 \ud2b9\uc815 \ucee4\ubba4\ub2c8\ud2f0\ub97c \ubaa8\ub2c8\ud130\ub9c1\ud558\ub824\uba74 \ud574\ub2f9 \ucee4\ubba4\ub2c8\ud2f0\ub97c \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624" + }, + "district": { + "data": { + "region": "\uc9c0\uc5ed" + }, + "description": "\uc8fc\ubfd0\ub9cc \uc544\ub2c8\ub77c \ud2b9\uc815 \uc9c0\uc5ed\uc744 \ubaa8\ub2c8\ud130\ub9c1\ud558\ub824\uba74 \ud574\ub2f9 \uc9c0\uc5ed\uc744 \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624" + }, + "state": { + "data": { + "region": "\uc9c0\uc5ed" + }, + "description": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \uc8fc \uc120\ud0dd" + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "region": "\uc9c0\uc5ed" + }, + "description": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \uc8fc \uc120\ud0dd" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ko.json b/homeassistant/components/vulcan/translations/ko.json new file mode 100644 index 00000000000..c98f79dd84b --- /dev/null +++ b/homeassistant/components/vulcan/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "token": "\ud1a0\ud070" + }, + "description": "\ubaa8\ubc14\uc77c \uc571\uc73c\ub85c Vulcan \uacc4\uc815\uc5d0 \ub85c\uadf8\uc778\ud569\ub2c8\ub2e4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ws66i/translations/ko.json b/homeassistant/components/ws66i/translations/ko.json new file mode 100644 index 00000000000..ba31d74c21a --- /dev/null +++ b/homeassistant/components/ws66i/translations/ko.json @@ -0,0 +1,34 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "ip_address": "IP \uc8fc\uc18c" + }, + "title": "\uae30\uae30\uc5d0 \uc5f0\uacb0\ud558\uae30" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source_1": "\uc18c\uc2a4 \uc774\ub984 #1", + "source_2": "\uc18c\uc2a4 \uc774\ub984 #2", + "source_3": "\uc18c\uc2a4 \uc774\ub984 #3", + "source_4": "\uc18c\uc2a4 \uc774\ub984 #4", + "source_5": "\uc18c\uc2a4 \uc774\ub984 #5", + "source_6": "\uc18c\uc2a4 \uc774\ub984 #6" + }, + "title": "\uc785\ub825 \uc18c\uc2a4 \uad6c\uc131\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/bg.json b/homeassistant/components/yolink/translations/bg.json new file mode 100644 index 00000000000..5f7e924f493 --- /dev/null +++ b/homeassistant/components/yolink/translations/bg.json @@ -0,0 +1,20 @@ +{ + "config": { + "abort": { + "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", + "missing_configuration": "\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044a\u0442 \u043d\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d. \u041c\u043e\u043b\u044f, \u0441\u043b\u0435\u0434\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" + }, + "create_entry": { + "default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "step": { + "pick_implementation": { + "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + }, + "reauth_confirm": { + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/et.json b/homeassistant/components/yolink/translations/et.json new file mode 100644 index 00000000000..0428b9229ad --- /dev/null +++ b/homeassistant/components/yolink/translations/et.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Kasutaja on juba seadistatud", + "already_in_progress": "Seadistamine on juba k\u00e4imas", + "authorize_url_timeout": "Kinnitus-URLi loomise ajal\u00f5pp", + "missing_configuration": "Komponent pole seadistatud. Palun loe dokumentatsiooni.", + "no_url_available": "URL pole saadaval. Selle t\u00f5rke kohta teabe saamiseks vaata [spikrijaotis]({docs_url})", + "oauth_error": "Saadi sobimatud loaandmed.", + "reauth_successful": "Taastuvastamine \u00f5nnestus" + }, + "create_entry": { + "default": "Tuvastamine \u00f5nnestus" + }, + "step": { + "pick_implementation": { + "title": "Vali tuvastusmeetod" + }, + "reauth_confirm": { + "description": "yolink sidumine peab konto uuesti tuvastama.", + "title": "Taastuvasta sidumine" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/fr.json b/homeassistant/components/yolink/translations/fr.json new file mode 100644 index 00000000000..57fc1f3bb64 --- /dev/null +++ b/homeassistant/components/yolink/translations/fr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9", + "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", + "authorize_url_timeout": "D\u00e9lai de g\u00e9n\u00e9ration de l'URL d'authentification expir\u00e9.", + "missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.", + "no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})", + "oauth_error": "Des donn\u00e9es de jeton non valides ont \u00e9t\u00e9 re\u00e7ues.", + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" + }, + "create_entry": { + "default": "Authentification r\u00e9ussie" + }, + "step": { + "pick_implementation": { + "title": "S\u00e9lectionner une m\u00e9thode d'authentification" + }, + "reauth_confirm": { + "description": "L'int\u00e9gration yolink doit r\u00e9-authentifier votre compte", + "title": "R\u00e9-authentifier l'int\u00e9gration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/hu.json b/homeassistant/components/yolink/translations/hu.json new file mode 100644 index 00000000000..fc0d5809e79 --- /dev/null +++ b/homeassistant/components/yolink/translations/hu.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van", + "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", + "authorize_url_timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a hiteles\u00edt\u00e9si URL gener\u00e1l\u00e1sa sor\u00e1n.", + "missing_configuration": "A komponens nincs konfigur\u00e1lva. K\u00e9rem, k\u00f6vesse a dokument\u00e1ci\u00f3t.", + "no_url_available": "Nincs el\u00e9rhet\u0151 URL. A hib\u00e1r\u00f3l tov\u00e1bbi inform\u00e1ci\u00f3 [a s\u00fag\u00f3ban]({docs_url}) tal\u00e1lhat\u00f3.", + "oauth_error": "\u00c9rv\u00e9nytelen token adatok \u00e9rkeztek.", + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." + }, + "create_entry": { + "default": "Sikeres hiteles\u00edt\u00e9s" + }, + "step": { + "pick_implementation": { + "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" + }, + "reauth_confirm": { + "description": "A yolink integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie a fi\u00f3kj\u00e1t", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/id.json b/homeassistant/components/yolink/translations/id.json new file mode 100644 index 00000000000..e2c5ce51b91 --- /dev/null +++ b/homeassistant/components/yolink/translations/id.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "Akun sudah dikonfigurasi", + "already_in_progress": "Alur konfigurasi sedang berlangsung", + "authorize_url_timeout": "Tenggang waktu pembuatan URL otorisasi habis.", + "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", + "no_url_available": "Tidak ada URL yang tersedia. Untuk informasi tentang kesalahan ini, [lihat bagian bantuan]({docs_url})", + "oauth_error": "Menerima respons token yang tidak valid.", + "reauth_successful": "Autentikasi ulang berhasil" + }, + "create_entry": { + "default": "Berhasil diautentikasi" + }, + "step": { + "pick_implementation": { + "title": "Pilih Metode Autentikasi" + }, + "reauth_confirm": { + "title": "Autentikasi Ulang Integrasi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/it.json b/homeassistant/components/yolink/translations/it.json new file mode 100644 index 00000000000..343755c2171 --- /dev/null +++ b/homeassistant/components/yolink/translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "L'account \u00e8 gi\u00e0 configurato", + "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", + "authorize_url_timeout": "Tempo scaduto nel generare l'URL di autorizzazione.", + "missing_configuration": "Il componente non \u00e8 configurato. Segui la documentazione.", + "no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})", + "oauth_error": "Ricevuti dati token non validi.", + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" + }, + "create_entry": { + "default": "Autenticazione riuscita" + }, + "step": { + "pick_implementation": { + "title": "Scegli il metodo di autenticazione" + }, + "reauth_confirm": { + "description": "L'integrazione yolink deve autenticare nuovamente il tuo account", + "title": "Autentica nuovamente l'integrazione" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/ja.json b/homeassistant/components/yolink/translations/ja.json new file mode 100644 index 00000000000..7d2545803bf --- /dev/null +++ b/homeassistant/components/yolink/translations/ja.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", + "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", + "authorize_url_timeout": "\u8a8d\u8a3cURL\u306e\u751f\u6210\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", + "missing_configuration": "\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002", + "no_url_available": "\u4f7f\u7528\u53ef\u80fd\u306aURL\u304c\u3042\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30a8\u30e9\u30fc\u306e\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u3001[\u30d8\u30eb\u30d7\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044]({docs_url})", + "oauth_error": "\u7121\u52b9\u306a\u30c8\u30fc\u30af\u30f3\u30c7\u30fc\u30bf\u3092\u53d7\u4fe1\u3057\u307e\u3057\u305f\u3002", + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" + }, + "create_entry": { + "default": "\u6b63\u5e38\u306b\u8a8d\u8a3c\u3055\u308c\u307e\u3057\u305f" + }, + "step": { + "pick_implementation": { + "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" + }, + "reauth_confirm": { + "description": "Yolink\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/ko.json b/homeassistant/components/yolink/translations/ko.json new file mode 100644 index 00000000000..6ead9fca594 --- /dev/null +++ b/homeassistant/components/yolink/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "create_entry": { + "default": "\uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + }, + "reauth_confirm": { + "description": "yolink \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/nl.json b/homeassistant/components/yolink/translations/nl.json new file mode 100644 index 00000000000..ae9739b3bd6 --- /dev/null +++ b/homeassistant/components/yolink/translations/nl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Account is al geconfigureerd", + "already_in_progress": "De configuratiestroom is al aan de gang", + "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", + "oauth_error": "Ongeldige token data ontvangen.", + "reauth_successful": "Herauthenticatie was succesvol" + }, + "create_entry": { + "default": "Succesvol geauthenticeerd" + }, + "step": { + "pick_implementation": { + "title": "Kies een authenticatie methode" + }, + "reauth_confirm": { + "description": "De yolink-integratie moet opnieuw inloggen bij uw account", + "title": "Verifieer de integratie opnieuw" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/pt-BR.json b/homeassistant/components/yolink/translations/pt-BR.json new file mode 100644 index 00000000000..31a69f7ed3f --- /dev/null +++ b/homeassistant/components/yolink/translations/pt-BR.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "A conta j\u00e1 est\u00e1 configurada", + "already_in_progress": "A configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", + "authorize_url_timeout": "Tempo limite gerando URL de autoriza\u00e7\u00e3o.", + "missing_configuration": "O componente n\u00e3o est\u00e1 configurado. Por favor, siga a documenta\u00e7\u00e3o.", + "no_url_available": "Nenhuma URL dispon\u00edvel. Para obter informa\u00e7\u00f5es sobre este erro, [verifique a se\u00e7\u00e3o de ajuda]({docs_url})", + "oauth_error": "Dados de token inv\u00e1lidos recebidos.", + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" + }, + "create_entry": { + "default": "Autenticado com sucesso" + }, + "step": { + "pick_implementation": { + "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" + }, + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o do yolink precisa autenticar novamente sua conta", + "title": "Reautenticar Integra\u00e7\u00e3o" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/tr.json b/homeassistant/components/yolink/translations/tr.json new file mode 100644 index 00000000000..f75dd975f1c --- /dev/null +++ b/homeassistant/components/yolink/translations/tr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", + "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", + "authorize_url_timeout": "Yetkilendirme URL'si olu\u015ftururken zaman a\u015f\u0131m\u0131.", + "missing_configuration": "Bile\u015fen yap\u0131land\u0131r\u0131lmam\u0131\u015f. L\u00fctfen belgeleri takip edin.", + "no_url_available": "Kullan\u0131labilir URL yok. Bu hata hakk\u0131nda bilgi i\u00e7in [yard\u0131m b\u00f6l\u00fcm\u00fcne bak\u0131n]({docs_url})", + "oauth_error": "Ge\u00e7ersiz anahtar verileri al\u0131nd\u0131.", + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" + }, + "create_entry": { + "default": "Ba\u015far\u0131yla do\u011fruland\u0131" + }, + "step": { + "pick_implementation": { + "title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7" + }, + "reauth_confirm": { + "description": "Yollink entegrasyonunun hesab\u0131n\u0131z\u0131 yeniden do\u011frulamas\u0131 gerekiyor", + "title": "Entegrasyonu Yeniden Do\u011frula" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/zh-Hant.json b/homeassistant/components/yolink/translations/zh-Hant.json new file mode 100644 index 00000000000..48e9dc10c66 --- /dev/null +++ b/homeassistant/components/yolink/translations/zh-Hant.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", + "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", + "authorize_url_timeout": "\u7522\u751f\u8a8d\u8b49 URL \u6642\u903e\u6642\u3002", + "missing_configuration": "\u5143\u4ef6\u5c1a\u672a\u8a2d\u7f6e\uff0c\u8acb\u53c3\u95b1\u6587\u4ef6\u8aaa\u660e\u3002", + "no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})", + "oauth_error": "\u6536\u5230\u7121\u6548\u7684\u6b0a\u6756\u8cc7\u6599\u3002", + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" + }, + "create_entry": { + "default": "\u5df2\u6210\u529f\u8a8d\u8b49" + }, + "step": { + "pick_implementation": { + "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" + }, + "reauth_confirm": { + "description": "Yolink \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + } + } + } +} \ No newline at end of file From ec01e001848fbcaff405d1847b3e2c3767a37e85 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 May 2022 23:10:28 -0500 Subject: [PATCH 545/930] Small cleanup to logbook context augmenter (#72043) --- homeassistant/components/logbook/__init__.py | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 549518e875a..4b24e8e5ef5 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -393,7 +393,11 @@ def _humanify( context_lookup: dict[str | None, Row | None] = {None: None} event_cache = EventCache(event_data_cache) context_augmenter = ContextAugmenter( - context_lookup, entity_name_cache, external_events, event_cache + context_lookup, + entity_name_cache, + external_events, + event_cache, + include_entity_name, ) def _keep_row(row: Row, event_type: str) -> bool: @@ -447,7 +451,7 @@ def _humanify( if icon := row.icon or row.old_format_icon: data[LOGBOOK_ENTRY_ICON] = icon - context_augmenter.augment(data, row, context_id, include_entity_name) + context_augmenter.augment(data, row, context_id) yield data elif event_type in external_events: @@ -455,7 +459,7 @@ def _humanify( data = describe_event(event_cache.get(row)) data[LOGBOOK_ENTRY_WHEN] = format_time(row) data[LOGBOOK_ENTRY_DOMAIN] = domain - context_augmenter.augment(data, row, context_id, include_entity_name) + context_augmenter.augment(data, row, context_id) yield data elif event_type == EVENT_LOGBOOK_ENTRY: @@ -475,7 +479,7 @@ def _humanify( LOGBOOK_ENTRY_DOMAIN: entry_domain, LOGBOOK_ENTRY_ENTITY_ID: entry_entity_id, } - context_augmenter.augment(data, row, context_id, include_entity_name) + context_augmenter.augment(data, row, context_id) yield data @@ -558,16 +562,16 @@ class ContextAugmenter: str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] ], event_cache: EventCache, + include_entity_name: bool, ) -> None: """Init the augmenter.""" self.context_lookup = context_lookup self.entity_name_cache = entity_name_cache self.external_events = external_events self.event_cache = event_cache + self.include_entity_name = include_entity_name - def augment( - self, data: dict[str, Any], row: Row, context_id: str, include_entity_name: bool - ) -> None: + def augment(self, data: dict[str, Any], row: Row, context_id: str) -> None: """Augment data from the row and cache.""" if context_user_id := row.context_user_id: data[CONTEXT_USER_ID] = context_user_id @@ -594,7 +598,7 @@ class ContextAugmenter: # State change if context_entity_id := context_row.entity_id: data[CONTEXT_ENTITY_ID] = context_entity_id - if include_entity_name: + if self.include_entity_name: data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( context_entity_id, context_row ) @@ -625,7 +629,7 @@ class ContextAugmenter: if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): return data[CONTEXT_ENTITY_ID] = attr_entity_id - if include_entity_name: + if self.include_entity_name: data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( attr_entity_id, context_row ) From bcabe5c6e42282f52c35b924823a8eb6f3e0ef4e Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Wed, 18 May 2022 07:27:00 +0200 Subject: [PATCH 546/930] Decouple up-down and position inversion for KNX covers (#72012) --- homeassistant/components/knx/cover.py | 1 + homeassistant/components/knx/schema.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index ca18ad4835a..29bd9b4f6a9 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -66,6 +66,7 @@ class KNXCover(KnxEntity, CoverEntity): group_address_position=config.get(CoverSchema.CONF_POSITION_ADDRESS), travel_time_down=config[CoverSchema.CONF_TRAVELLING_TIME_DOWN], travel_time_up=config[CoverSchema.CONF_TRAVELLING_TIME_UP], + invert_updown=config[CoverSchema.CONF_INVERT_UPDOWN], invert_position=config[CoverSchema.CONF_INVERT_POSITION], invert_angle=config[CoverSchema.CONF_INVERT_ANGLE], ) diff --git a/homeassistant/components/knx/schema.py b/homeassistant/components/knx/schema.py index abba1b0c027..c7c1e264975 100644 --- a/homeassistant/components/knx/schema.py +++ b/homeassistant/components/knx/schema.py @@ -489,6 +489,7 @@ class CoverSchema(KNXPlatformSchema): CONF_ANGLE_STATE_ADDRESS = "angle_state_address" CONF_TRAVELLING_TIME_DOWN = "travelling_time_down" CONF_TRAVELLING_TIME_UP = "travelling_time_up" + CONF_INVERT_UPDOWN = "invert_updown" CONF_INVERT_POSITION = "invert_position" CONF_INVERT_ANGLE = "invert_angle" @@ -521,6 +522,7 @@ class CoverSchema(KNXPlatformSchema): vol.Optional( CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME ): cv.positive_float, + vol.Optional(CONF_INVERT_UPDOWN, default=False): cv.boolean, vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean, vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean, vol.Optional(CONF_DEVICE_CLASS): COVER_DEVICE_CLASSES_SCHEMA, From 8eb4a16a0f6336042e5f7010bf5f69c7648849d1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 08:01:28 +0200 Subject: [PATCH 547/930] Drop unnecessary async definitions in samsungtv (#72019) * Drop unnecessary async definitions in samsungtv * keep prefix --- homeassistant/components/samsungtv/config_flow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/samsungtv/config_flow.py b/homeassistant/components/samsungtv/config_flow.py index 02414d3f476..bfa2482f617 100644 --- a/homeassistant/components/samsungtv/config_flow.py +++ b/homeassistant/components/samsungtv/config_flow.py @@ -414,7 +414,8 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) return entry - async def _async_start_discovery_with_mac_address(self) -> None: + @callback + def _async_start_discovery_with_mac_address(self) -> None: """Start discovery.""" assert self._host is not None if (entry := self._async_update_existing_matching_entry()) and entry.unique_id: @@ -491,7 +492,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): LOGGER.debug("Samsung device found via DHCP: %s", discovery_info) self._mac = discovery_info.macaddress self._host = discovery_info.ip - await self._async_start_discovery_with_mac_address() + self._async_start_discovery_with_mac_address() await self._async_set_device_unique_id() self.context["title_placeholders"] = {"device": self._title} return await self.async_step_confirm() @@ -503,7 +504,7 @@ class SamsungTVConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): LOGGER.debug("Samsung device found via ZEROCONF: %s", discovery_info) self._mac = format_mac(discovery_info.properties["deviceid"]) self._host = discovery_info.host - await self._async_start_discovery_with_mac_address() + self._async_start_discovery_with_mac_address() await self._async_set_device_unique_id() self.context["title_placeholders"] = {"device": self._title} return await self.async_step_confirm() From 2bb6e4bb877e21f2e49bcf66eddba18b66ff13ed Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 May 2022 18:15:15 +1200 Subject: [PATCH 548/930] Bump aioesphomeapi to 10.9.0 (#72049) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 72a36076bf4..50334808dbf 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==10.8.2"], + "requirements": ["aioesphomeapi==10.9.0"], "zeroconf": ["_esphomelib._tcp.local."], "codeowners": ["@OttoWinter", "@jesserockz"], "after_dependencies": ["zeroconf", "tag"], diff --git a/requirements_all.txt b/requirements_all.txt index 3fa07679b1a..ad732ab2034 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -144,7 +144,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.8.2 +aioesphomeapi==10.9.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9195dc04ff4..02bf3ea39b0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -131,7 +131,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.8.2 +aioesphomeapi==10.9.0 # homeassistant.components.flo aioflo==2021.11.0 From 0dc12c70e38a2262f7c263eacdc56a91304c9bdb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 01:47:02 -0500 Subject: [PATCH 549/930] Add logbook descriptions for elkm1 keypad press events (#72017) * Add logbook descriptions for elkm1 keypad press events * drop extra block --- homeassistant/components/elkm1/__init__.py | 8 +-- homeassistant/components/elkm1/const.py | 1 + homeassistant/components/elkm1/logbook.py | 39 ++++++++++++++ tests/components/elkm1/test_logbook.py | 63 ++++++++++++++++++++++ 4 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/elkm1/logbook.py create mode 100644 tests/components/elkm1/test_logbook.py diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py index 22dd50499d6..9a7c7dbc43a 100644 --- a/homeassistant/components/elkm1/__init__.py +++ b/homeassistant/components/elkm1/__init__.py @@ -42,6 +42,7 @@ from .const import ( ATTR_KEY, ATTR_KEY_NAME, ATTR_KEYPAD_ID, + ATTR_KEYPAD_NAME, CONF_AREA, CONF_AUTO_CONFIGURE, CONF_COUNTER, @@ -266,21 +267,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) elk.connect() - def _element_changed(element: Element, changeset: dict[str, Any]) -> None: + def _keypad_changed(keypad: Element, changeset: dict[str, Any]) -> None: if (keypress := changeset.get("last_keypress")) is None: return hass.bus.async_fire( EVENT_ELKM1_KEYPAD_KEY_PRESSED, { - ATTR_KEYPAD_ID: element.index + 1, + ATTR_KEYPAD_NAME: keypad.name, + ATTR_KEYPAD_ID: keypad.index + 1, ATTR_KEY_NAME: keypress[0], ATTR_KEY: keypress[1], }, ) for keypad in elk.keypads: - keypad.add_callback(_element_changed) + keypad.add_callback(_keypad_changed) try: if not await async_wait_for_elk_to_sync(elk, LOGIN_TIMEOUT, SYNC_TIMEOUT): diff --git a/homeassistant/components/elkm1/const.py b/homeassistant/components/elkm1/const.py index fd4856bd5d5..a2bb5744c11 100644 --- a/homeassistant/components/elkm1/const.py +++ b/homeassistant/components/elkm1/const.py @@ -43,6 +43,7 @@ EVENT_ELKM1_KEYPAD_KEY_PRESSED = "elkm1.keypad_key_pressed" ATTR_KEYPAD_ID = "keypad_id" ATTR_KEY = "key" ATTR_KEY_NAME = "key_name" +ATTR_KEYPAD_NAME = "keypad_name" ATTR_CHANGED_BY_KEYPAD = "changed_by_keypad" ATTR_CHANGED_BY_ID = "changed_by_id" ATTR_CHANGED_BY_TIME = "changed_by_time" diff --git a/homeassistant/components/elkm1/logbook.py b/homeassistant/components/elkm1/logbook.py new file mode 100644 index 00000000000..01019ce77e0 --- /dev/null +++ b/homeassistant/components/elkm1/logbook.py @@ -0,0 +1,39 @@ +"""Describe elkm1 logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.core import Event, HomeAssistant, callback + +from .const import ( + ATTR_KEY, + ATTR_KEY_NAME, + ATTR_KEYPAD_ID, + ATTR_KEYPAD_NAME, + DOMAIN, + EVENT_ELKM1_KEYPAD_KEY_PRESSED, +) + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + + @callback + def async_describe_button_event(event: Event) -> dict[str, str]: + """Describe elkm1 logbook event.""" + data = event.data + keypad_name = data.get( + ATTR_KEYPAD_NAME, data[ATTR_KEYPAD_ID] + ) # added in 2022.6 + return { + "name": f"Elk Keypad {keypad_name}", + "message": f"pressed {data[ATTR_KEY_NAME]} ({data[ATTR_KEY]})", + } + + async_describe_event( + DOMAIN, EVENT_ELKM1_KEYPAD_KEY_PRESSED, async_describe_button_event + ) diff --git a/tests/components/elkm1/test_logbook.py b/tests/components/elkm1/test_logbook.py new file mode 100644 index 00000000000..d6ff9f75d5d --- /dev/null +++ b/tests/components/elkm1/test_logbook.py @@ -0,0 +1,63 @@ +"""The tests for elkm1 logbook.""" +from homeassistant.components.elkm1.const import ( + ATTR_KEY, + ATTR_KEY_NAME, + ATTR_KEYPAD_ID, + ATTR_KEYPAD_NAME, + DOMAIN, + EVENT_ELKM1_KEYPAD_KEY_PRESSED, +) +from homeassistant.const import CONF_HOST +from homeassistant.setup import async_setup_component + +from . import _patch_discovery, _patch_elk + +from tests.common import MockConfigEntry +from tests.components.logbook.common import MockRow, mock_humanify + + +async def test_humanify_elkm1_keypad_event(hass): + """Test humanifying elkm1 keypad presses.""" + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + config_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: "elks://1.2.3.4"}, + unique_id="aa:bb:cc:dd:ee:ff", + ) + config_entry.add_to_hass(hass) + + with _patch_discovery(), _patch_elk(): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + (event1, event2) = mock_humanify( + hass, + [ + MockRow( + EVENT_ELKM1_KEYPAD_KEY_PRESSED, + { + ATTR_KEYPAD_ID: 1, + ATTR_KEY_NAME: "four", + ATTR_KEY: "4", + ATTR_KEYPAD_NAME: "Main Bedroom", + }, + ), + MockRow( + EVENT_ELKM1_KEYPAD_KEY_PRESSED, + { + ATTR_KEYPAD_ID: 1, + ATTR_KEY_NAME: "five", + ATTR_KEY: "5", + }, + ), + ], + ) + + assert event1["name"] == "Elk Keypad Main Bedroom" + assert event1["domain"] == DOMAIN + assert event1["message"] == "pressed four (4)" + + assert event2["name"] == "Elk Keypad 1" + assert event2["domain"] == DOMAIN + assert event2["message"] == "pressed five (5)" From c4fc84ec1e77a18ff392b34389baa86d52388246 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 01:58:30 -0500 Subject: [PATCH 550/930] Add support for selecting device_ids from the logbook (#72039) Co-authored-by: Paulus Schoutsen --- homeassistant/components/logbook/__init__.py | 106 +++- homeassistant/components/logbook/queries.py | 451 ------------------ .../components/logbook/queries/__init__.py | 70 +++ .../components/logbook/queries/all.py | 64 +++ .../components/logbook/queries/common.py | 286 +++++++++++ .../components/logbook/queries/devices.py | 87 ++++ .../components/logbook/queries/entities.py | 124 +++++ .../logbook/queries/entities_and_devices.py | 111 +++++ homeassistant/components/recorder/models.py | 3 + tests/components/logbook/test_init.py | 280 ++++++++++- 10 files changed, 1112 insertions(+), 470 deletions(-) delete mode 100644 homeassistant/components/logbook/queries.py create mode 100644 homeassistant/components/logbook/queries/__init__.py create mode 100644 homeassistant/components/logbook/queries/all.py create mode 100644 homeassistant/components/logbook/queries/common.py create mode 100644 homeassistant/components/logbook/queries/devices.py create mode 100644 homeassistant/components/logbook/queries/entities.py create mode 100644 homeassistant/components/logbook/queries/entities_and_devices.py diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 4b24e8e5ef5..806ba00d2c8 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -50,7 +50,11 @@ from homeassistant.core import ( split_entity_id, ) from homeassistant.exceptions import InvalidEntityFormatError -from homeassistant.helpers import config_validation as cv, entity_registry as er +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + entity_registry as er, +) from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, EntityFilter, @@ -64,7 +68,8 @@ from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass import homeassistant.util.dt as dt_util -from .queries import PSUEDO_EVENT_STATE_CHANGED, statement_for_request +from .queries import statement_for_request +from .queries.common import PSUEDO_EVENT_STATE_CHANGED _LOGGER = logging.getLogger(__name__) @@ -96,8 +101,11 @@ LOGBOOK_ENTRY_STATE = "state" LOGBOOK_ENTRY_WHEN = "when" ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE} - -SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} +ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY = { + EVENT_LOGBOOK_ENTRY, + EVENT_AUTOMATION_TRIGGERED, + EVENT_SCRIPT_STARTED, +} LOG_MESSAGE_SCHEMA = vol.Schema( { @@ -209,12 +217,61 @@ async def _process_logbook_platform( platform.async_describe_events(hass, _async_describe_event) +def _async_determine_event_types( + hass: HomeAssistant, entity_ids: list[str] | None, device_ids: list[str] | None +) -> tuple[str, ...]: + """Reduce the event types based on the entity ids and device ids.""" + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ] = hass.data.get(DOMAIN, {}) + if not entity_ids and not device_ids: + return (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) + config_entry_ids: set[str] = set() + intrested_event_types: set[str] = set() + + if entity_ids: + # + # Home Assistant doesn't allow firing events from + # entities so we have a limited list to check + # + # automations and scripts can refer to entities + # but they do not have a config entry so we need + # to add them. + # + # We also allow entity_ids to be recorded via + # manual logbook entries. + # + intrested_event_types |= ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY + + if device_ids: + dev_reg = dr.async_get(hass) + for device_id in device_ids: + if (device := dev_reg.async_get(device_id)) and device.config_entries: + config_entry_ids |= device.config_entries + interested_domains: set[str] = set() + for entry_id in config_entry_ids: + if entry := hass.config_entries.async_get_entry(entry_id): + interested_domains.add(entry.domain) + for external_event, domain_call in external_events.items(): + if domain_call[0] in interested_domains: + intrested_event_types.add(external_event) + + return tuple( + event_type + for event_type in (EVENT_LOGBOOK_ENTRY, *external_events) + if event_type in intrested_event_types + ) + + def _ws_formatted_get_events( hass: HomeAssistant, msg_id: int, start_day: dt, end_day: dt, + event_types: tuple[str, ...], + ent_reg: er.EntityRegistry, entity_ids: list[str] | None = None, + device_ids: list[str] | None = None, filters: Filters | None = None, entities_filter: EntityFilter | Callable[[str], bool] | None = None, context_id: str | None = None, @@ -227,7 +284,10 @@ def _ws_formatted_get_events( hass, start_day, end_day, + event_types, + ent_reg, entity_ids, + device_ids, filters, entities_filter, context_id, @@ -244,6 +304,7 @@ def _ws_formatted_get_events( vol.Required("start_time"): str, vol.Optional("end_time"): str, vol.Optional("entity_ids"): [str], + vol.Optional("device_ids"): [str], vol.Optional("context_id"): str, } ) @@ -274,8 +335,11 @@ async def ws_get_events( connection.send_result(msg["id"], []) return + device_ids = msg.get("device_ids") entity_ids = msg.get("entity_ids") context_id = msg.get("context_id") + event_types = _async_determine_event_types(hass, entity_ids, device_ids) + ent_reg = er.async_get(hass) connection.send_message( await get_instance(hass).async_add_executor_job( @@ -284,7 +348,10 @@ async def ws_get_events( msg["id"], start_time, end_time, + event_types, + ent_reg, entity_ids, + device_ids, hass.data[LOGBOOK_FILTERS], hass.data[LOGBOOK_ENTITIES_FILTER], context_id, @@ -354,6 +421,9 @@ class LogbookView(HomeAssistantView): "Can't combine entity with context_id", HTTPStatus.BAD_REQUEST ) + event_types = _async_determine_event_types(hass, entity_ids, None) + ent_reg = er.async_get(hass) + def json_events() -> web.Response: """Fetch events and generate JSON.""" return self.json( @@ -361,7 +431,10 @@ class LogbookView(HomeAssistantView): hass, start_day, end_day, + event_types, + ent_reg, entity_ids, + None, self.filters, self.entities_filter, context_id, @@ -487,7 +560,10 @@ def _get_events( hass: HomeAssistant, start_day: dt, end_day: dt, + event_types: tuple[str, ...], + ent_reg: er.EntityRegistry, entity_ids: list[str] | None = None, + device_ids: list[str] | None = None, filters: Filters | None = None, entities_filter: EntityFilter | Callable[[str], bool] | None = None, context_id: str | None = None, @@ -496,17 +572,13 @@ def _get_events( ) -> list[dict[str, Any]]: """Get events for a period of time.""" assert not ( - entity_ids and context_id - ), "can't pass in both entity_ids and context_id" - + context_id and (entity_ids or device_ids) + ), "can't pass in both context_id and (entity_ids or device_ids)" external_events: dict[ str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] ] = hass.data.get(DOMAIN, {}) - event_types = (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) format_time = _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat entity_name_cache = EntityNameCache(hass) - ent_reg = er.async_get(hass) - if entity_ids is not None: entities_filter = generate_filter([], entity_ids, [], []) @@ -529,7 +601,13 @@ def _get_events( return query.yield_per(1024) # type: ignore[no-any-return] stmt = statement_for_request( - start_day, end_day, event_types, entity_ids, filters, context_id + start_day, + end_day, + event_types, + entity_ids, + device_ids, + filters, + context_id, ) if _LOGGER.isEnabledFor(logging.DEBUG): _LOGGER.debug( @@ -668,12 +746,6 @@ def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None: return result.group(1) if result else None -def _row_attributes_extract(row: Row, extractor: re.Pattern) -> str | None: - """Extract from attributes row.""" - result = extractor.search(row.shared_attrs or row.attributes or "") - return result.group(1) if result else None - - def _row_time_fired_isoformat(row: Row) -> str: """Convert the row timed_fired to isoformat.""" return process_timestamp_to_utc_isoformat(row.time_fired or dt_util.utcnow()) diff --git a/homeassistant/components/logbook/queries.py b/homeassistant/components/logbook/queries.py deleted file mode 100644 index 6fe20bfc561..00000000000 --- a/homeassistant/components/logbook/queries.py +++ /dev/null @@ -1,451 +0,0 @@ -"""Queries for logbook.""" -from __future__ import annotations - -from collections.abc import Iterable -from datetime import datetime as dt - -import sqlalchemy -from sqlalchemy import JSON, lambda_stmt, select, type_coerce, union_all -from sqlalchemy.orm import Query, aliased -from sqlalchemy.sql.elements import ClauseList -from sqlalchemy.sql.expression import literal -from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select - -from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN -from homeassistant.components.recorder.filters import Filters -from homeassistant.components.recorder.models import ( - ENTITY_ID_LAST_UPDATED_INDEX, - JSON_VARIENT_CAST, - LAST_UPDATED_INDEX, - EventData, - Events, - StateAttributes, - States, -) -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN - -ENTITY_ID_JSON_TEMPLATE = '%"entity_id":"{}"%' - -CONTINUOUS_DOMAINS = {PROXIMITY_DOMAIN, SENSOR_DOMAIN} -CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] - -UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' -UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" - -OLD_STATE = aliased(States, name="old_state") - - -SHARED_ATTRS_JSON = type_coerce( - StateAttributes.shared_attrs.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) -) -OLD_FORMAT_ATTRS_JSON = type_coerce( - States.attributes.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) -) - - -PSUEDO_EVENT_STATE_CHANGED = None -# Since we don't store event_types and None -# and we don't store state_changed in events -# we use a NULL for state_changed events -# when we synthesize them from the states table -# since it avoids another column being sent -# in the payload - -EVENT_COLUMNS = ( - Events.event_id.label("event_id"), - Events.event_type.label("event_type"), - Events.event_data.label("event_data"), - Events.time_fired.label("time_fired"), - Events.context_id.label("context_id"), - Events.context_user_id.label("context_user_id"), - Events.context_parent_id.label("context_parent_id"), -) - -STATE_COLUMNS = ( - States.state_id.label("state_id"), - States.state.label("state"), - States.entity_id.label("entity_id"), - SHARED_ATTRS_JSON["icon"].as_string().label("icon"), - OLD_FORMAT_ATTRS_JSON["icon"].as_string().label("old_format_icon"), -) - - -EMPTY_STATE_COLUMNS = ( - literal(value=None, type_=sqlalchemy.String).label("state_id"), - literal(value=None, type_=sqlalchemy.String).label("state"), - literal(value=None, type_=sqlalchemy.String).label("entity_id"), - literal(value=None, type_=sqlalchemy.String).label("icon"), - literal(value=None, type_=sqlalchemy.String).label("old_format_icon"), -) - - -EVENT_ROWS_NO_STATES = ( - *EVENT_COLUMNS, - EventData.shared_data.label("shared_data"), - *EMPTY_STATE_COLUMNS, -) - -# Virtual column to tell logbook if it should avoid processing -# the event as its only used to link contexts -CONTEXT_ONLY = literal("1").label("context_only") -NOT_CONTEXT_ONLY = literal(None).label("context_only") - - -def statement_for_request( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_ids: list[str] | None = None, - filters: Filters | None = None, - context_id: str | None = None, -) -> StatementLambdaElement: - """Generate the logbook statement for a logbook request.""" - - # No entities: logbook sends everything for the timeframe - # limited by the context_id and the yaml configured filter - if not entity_ids: - entity_filter = filters.entity_filter() if filters else None - return _all_stmt(start_day, end_day, event_types, entity_filter, context_id) - - # Multiple entities: logbook sends everything for the timeframe for the entities - # - # This is the least efficient query because we use - # like matching which means part of the query has to be built each - # time when the entity_ids are not in the cache - if len(entity_ids) > 1: - return _entities_stmt(start_day, end_day, event_types, entity_ids) - - # Single entity: logbook sends everything for the timeframe for the entity - entity_id = entity_ids[0] - entity_like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) - return _single_entity_stmt(start_day, end_day, event_types, entity_id, entity_like) - - -def _select_events_context_id_subquery( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], -) -> Select: - """Generate the select for a context_id subquery.""" - return ( - select(Events.context_id) - .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) - .where(Events.event_type.in_(event_types)) - .outerjoin(EventData, (Events.data_id == EventData.data_id)) - ) - - -def _select_entities_context_ids_sub_query( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_ids: list[str], -) -> Select: - """Generate a subquery to find context ids for multiple entities.""" - return select( - union_all( - _select_events_context_id_subquery(start_day, end_day, event_types).where( - _apply_event_entity_id_matchers(entity_ids) - ), - _apply_entities_hints(select(States.context_id)) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id.in_(entity_ids)), - ).c.context_id - ) - - -def _select_events_context_only() -> Select: - """Generate an events query that mark them as for context_only. - - By marking them as context_only we know they are only for - linking context ids and we can avoid processing them. - """ - return select(*EVENT_ROWS_NO_STATES, CONTEXT_ONLY).outerjoin( - EventData, (Events.data_id == EventData.data_id) - ) - - -def _entities_stmt( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_ids: list[str], -) -> StatementLambdaElement: - """Generate a logbook query for multiple entities.""" - stmt = lambda_stmt( - lambda: _select_events_without_states(start_day, end_day, event_types) - ) - stmt = stmt.add_criteria( - lambda s: s.where(_apply_event_entity_id_matchers(entity_ids)).union_all( - _states_query_for_entity_ids(start_day, end_day, entity_ids), - _select_events_context_only().where( - Events.context_id.in_( - _select_entities_context_ids_sub_query( - start_day, - end_day, - event_types, - entity_ids, - ) - ) - ), - ), - # Since _apply_event_entity_id_matchers generates multiple - # like statements we need to use the entity_ids in the - # the cache key since the sql can change based on the - # likes. - track_on=(str(entity_ids),), - ) - stmt += lambda s: s.order_by(Events.time_fired) - return stmt - - -def _select_entity_context_ids_sub_query( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_id: str, - entity_id_like: str, -) -> Select: - """Generate a subquery to find context ids for a single entity.""" - return select( - union_all( - _select_events_context_id_subquery(start_day, end_day, event_types).where( - Events.event_data.like(entity_id_like) - | EventData.shared_data.like(entity_id_like) - ), - _apply_entities_hints(select(States.context_id)) - .filter((States.last_updated > start_day) & (States.last_updated < end_day)) - .where(States.entity_id == entity_id), - ).c.context_id - ) - - -def _single_entity_stmt( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_id: str, - entity_id_like: str, -) -> StatementLambdaElement: - """Generate a logbook query for a single entity.""" - stmt = lambda_stmt( - lambda: _select_events_without_states(start_day, end_day, event_types) - .where( - Events.event_data.like(entity_id_like) - | EventData.shared_data.like(entity_id_like) - ) - .union_all( - _states_query_for_entity_id(start_day, end_day, entity_id), - _select_events_context_only().where( - Events.context_id.in_( - _select_entity_context_ids_sub_query( - start_day, end_day, event_types, entity_id, entity_id_like - ) - ) - ), - ) - .order_by(Events.time_fired) - ) - return stmt - - -def _all_stmt( - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - entity_filter: ClauseList | None = None, - context_id: str | None = None, -) -> StatementLambdaElement: - """Generate a logbook query for all entities.""" - stmt = lambda_stmt( - lambda: _select_events_without_states(start_day, end_day, event_types) - ) - if context_id is not None: - # Once all the old `state_changed` events - # are gone from the database remove the - # _legacy_select_events_context_id() - stmt += lambda s: s.where(Events.context_id == context_id).union_all( - _states_query_for_context_id(start_day, end_day, context_id), - _legacy_select_events_context_id(start_day, end_day, context_id), - ) - elif entity_filter is not None: - stmt += lambda s: s.union_all( - _states_query_for_all(start_day, end_day).where(entity_filter) - ) - else: - stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day)) - stmt += lambda s: s.order_by(Events.time_fired) - return stmt - - -def _legacy_select_events_context_id( - start_day: dt, end_day: dt, context_id: str -) -> Select: - """Generate a legacy events context id select that also joins states.""" - # This can be removed once we no longer have event_ids in the states table - return ( - select( - *EVENT_COLUMNS, - literal(value=None, type_=sqlalchemy.String).label("shared_data"), - *STATE_COLUMNS, - NOT_CONTEXT_ONLY, - ) - .outerjoin(States, (Events.event_id == States.event_id)) - .where( - (States.last_updated == States.last_changed) | States.last_changed.is_(None) - ) - .where(_not_continuous_entity_matcher()) - .outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) - .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) - .where(Events.context_id == context_id) - ) - - -def _select_events_without_states( - start_day: dt, end_day: dt, event_types: tuple[str, ...] -) -> Select: - """Generate an events select that does not join states.""" - return ( - select(*EVENT_ROWS_NO_STATES, NOT_CONTEXT_ONLY) - .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) - .where(Events.event_type.in_(event_types)) - .outerjoin(EventData, (Events.data_id == EventData.data_id)) - ) - - -def _states_query_for_context_id(start_day: dt, end_day: dt, context_id: str) -> Query: - return _apply_states_filters(_select_states(), start_day, end_day).where( - States.context_id == context_id - ) - - -def _states_query_for_entity_id(start_day: dt, end_day: dt, entity_id: str) -> Query: - return _apply_states_filters( - _apply_entities_hints(_select_states()), start_day, end_day - ).where(States.entity_id == entity_id) - - -def _states_query_for_entity_ids( - start_day: dt, end_day: dt, entity_ids: list[str] -) -> Query: - return _apply_states_filters( - _apply_entities_hints(_select_states()), start_day, end_day - ).where(States.entity_id.in_(entity_ids)) - - -def _states_query_for_all(start_day: dt, end_day: dt) -> Query: - return _apply_states_filters(_apply_all_hints(_select_states()), start_day, end_day) - - -def _select_states() -> Select: - """Generate a states select that formats the states table as event rows.""" - return select( - literal(value=None, type_=sqlalchemy.Text).label("event_id"), - # We use PSUEDO_EVENT_STATE_CHANGED aka None for - # state_changed events since it takes up less - # space in the response and every row has to be - # marked with the event_type - literal(value=PSUEDO_EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( - "event_type" - ), - literal(value=None, type_=sqlalchemy.Text).label("event_data"), - States.last_updated.label("time_fired"), - States.context_id.label("context_id"), - States.context_user_id.label("context_user_id"), - States.context_parent_id.label("context_parent_id"), - literal(value=None, type_=sqlalchemy.Text).label("shared_data"), - *STATE_COLUMNS, - NOT_CONTEXT_ONLY, - ) - - -def _apply_all_hints(query: Query) -> Query: - """Force mysql to use the right index on large selects.""" - return query.with_hint( - States, f"FORCE INDEX ({LAST_UPDATED_INDEX})", dialect_name="mysql" - ) - - -def _apply_entities_hints(query: Query) -> Query: - """Force mysql to use the right index on large selects.""" - return query.with_hint( - States, f"FORCE INDEX ({ENTITY_ID_LAST_UPDATED_INDEX})", dialect_name="mysql" - ) - - -def _apply_states_filters(query: Query, start_day: dt, end_day: dt) -> Query: - return ( - query.filter( - (States.last_updated > start_day) & (States.last_updated < end_day) - ) - .outerjoin(OLD_STATE, (States.old_state_id == OLD_STATE.state_id)) - .where(_missing_state_matcher()) - .where(_not_continuous_entity_matcher()) - .where( - (States.last_updated == States.last_changed) | States.last_changed.is_(None) - ) - .outerjoin( - StateAttributes, (States.attributes_id == StateAttributes.attributes_id) - ) - ) - - -def _missing_state_matcher() -> sqlalchemy.and_: - # The below removes state change events that do not have - # and old_state or the old_state is missing (newly added entities) - # or the new_state is missing (removed entities) - return sqlalchemy.and_( - OLD_STATE.state_id.isnot(None), - (States.state != OLD_STATE.state), - States.state.isnot(None), - ) - - -def _not_continuous_entity_matcher() -> sqlalchemy.or_: - """Match non continuous entities.""" - return sqlalchemy.or_( - _not_continuous_domain_matcher(), - sqlalchemy.and_( - _continuous_domain_matcher, _not_uom_attributes_matcher() - ).self_group(), - ) - - -def _not_continuous_domain_matcher() -> sqlalchemy.and_: - """Match not continuous domains.""" - return sqlalchemy.and_( - *[ - ~States.entity_id.like(entity_domain) - for entity_domain in CONTINUOUS_ENTITY_ID_LIKE - ], - ).self_group() - - -def _continuous_domain_matcher() -> sqlalchemy.or_: - """Match continuous domains.""" - return sqlalchemy.or_( - *[ - States.entity_id.like(entity_domain) - for entity_domain in CONTINUOUS_ENTITY_ID_LIKE - ], - ).self_group() - - -def _not_uom_attributes_matcher() -> ClauseList: - """Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql.""" - return ~StateAttributes.shared_attrs.like( - UNIT_OF_MEASUREMENT_JSON_LIKE - ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) - - -def _apply_event_entity_id_matchers(entity_ids: Iterable[str]) -> sqlalchemy.or_: - """Create matchers for the entity_id in the event_data.""" - ors = [] - for entity_id in entity_ids: - like = ENTITY_ID_JSON_TEMPLATE.format(entity_id) - ors.append(Events.event_data.like(like)) - ors.append(EventData.shared_data.like(like)) - return sqlalchemy.or_(*ors) diff --git a/homeassistant/components/logbook/queries/__init__.py b/homeassistant/components/logbook/queries/__init__.py new file mode 100644 index 00000000000..3672f1e761c --- /dev/null +++ b/homeassistant/components/logbook/queries/__init__.py @@ -0,0 +1,70 @@ +"""Queries for logbook.""" +from __future__ import annotations + +from datetime import datetime as dt + +from sqlalchemy.sql.lambdas import StatementLambdaElement + +from homeassistant.components.recorder.filters import Filters + +from .all import all_stmt +from .devices import devices_stmt +from .entities import entities_stmt +from .entities_and_devices import entities_devices_stmt + + +def statement_for_request( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str] | None = None, + device_ids: list[str] | None = None, + filters: Filters | None = None, + context_id: str | None = None, +) -> StatementLambdaElement: + """Generate the logbook statement for a logbook request.""" + + # No entities: logbook sends everything for the timeframe + # limited by the context_id and the yaml configured filter + if not entity_ids and not device_ids: + entity_filter = filters.entity_filter() if filters else None + return all_stmt(start_day, end_day, event_types, entity_filter, context_id) + + # sqlalchemy caches object quoting, the + # json quotable ones must be a different + # object from the non-json ones to prevent + # sqlalchemy from quoting them incorrectly + + # entities and devices: logbook sends everything for the timeframe for the entities and devices + if entity_ids and device_ids: + json_quotable_entity_ids = list(entity_ids) + json_quotable_device_ids = list(device_ids) + return entities_devices_stmt( + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + json_quotable_device_ids, + ) + + # entities: logbook sends everything for the timeframe for the entities + if entity_ids: + json_quotable_entity_ids = list(entity_ids) + return entities_stmt( + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + ) + + # devices: logbook sends everything for the timeframe for the devices + assert device_ids is not None + json_quotable_device_ids = list(device_ids) + return devices_stmt( + start_day, + end_day, + event_types, + json_quotable_device_ids, + ) diff --git a/homeassistant/components/logbook/queries/all.py b/homeassistant/components/logbook/queries/all.py new file mode 100644 index 00000000000..da17c7bddeb --- /dev/null +++ b/homeassistant/components/logbook/queries/all.py @@ -0,0 +1,64 @@ +"""All queries for logbook.""" +from __future__ import annotations + +from datetime import datetime as dt + +from sqlalchemy import lambda_stmt +from sqlalchemy.orm import Query +from sqlalchemy.sql.elements import ClauseList +from sqlalchemy.sql.lambdas import StatementLambdaElement + +from homeassistant.components.recorder.models import LAST_UPDATED_INDEX, Events, States + +from .common import ( + apply_states_filters, + legacy_select_events_context_id, + select_events_without_states, + select_states, +) + + +def all_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_filter: ClauseList | None = None, + context_id: str | None = None, +) -> StatementLambdaElement: + """Generate a logbook query for all entities.""" + stmt = lambda_stmt( + lambda: select_events_without_states(start_day, end_day, event_types) + ) + if context_id is not None: + # Once all the old `state_changed` events + # are gone from the database remove the + # _legacy_select_events_context_id() + stmt += lambda s: s.where(Events.context_id == context_id).union_all( + _states_query_for_context_id(start_day, end_day, context_id), + legacy_select_events_context_id(start_day, end_day, context_id), + ) + elif entity_filter is not None: + stmt += lambda s: s.union_all( + _states_query_for_all(start_day, end_day).where(entity_filter) + ) + else: + stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day)) + stmt += lambda s: s.order_by(Events.time_fired) + return stmt + + +def _states_query_for_all(start_day: dt, end_day: dt) -> Query: + return apply_states_filters(_apply_all_hints(select_states()), start_day, end_day) + + +def _apply_all_hints(query: Query) -> Query: + """Force mysql to use the right index on large selects.""" + return query.with_hint( + States, f"FORCE INDEX ({LAST_UPDATED_INDEX})", dialect_name="mysql" + ) + + +def _states_query_for_context_id(start_day: dt, end_day: dt, context_id: str) -> Query: + return apply_states_filters(select_states(), start_day, end_day).where( + States.context_id == context_id + ) diff --git a/homeassistant/components/logbook/queries/common.py b/homeassistant/components/logbook/queries/common.py new file mode 100644 index 00000000000..237fde3f653 --- /dev/null +++ b/homeassistant/components/logbook/queries/common.py @@ -0,0 +1,286 @@ +"""Queries for logbook.""" +from __future__ import annotations + +from collections.abc import Callable +from datetime import datetime as dt +import json +from typing import Any + +import sqlalchemy +from sqlalchemy import JSON, select, type_coerce +from sqlalchemy.orm import Query, aliased +from sqlalchemy.sql.elements import ClauseList +from sqlalchemy.sql.expression import literal +from sqlalchemy.sql.selectable import Select + +from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN +from homeassistant.components.recorder.models import ( + JSON_VARIENT_CAST, + JSONB_VARIENT_CAST, + EventData, + Events, + StateAttributes, + States, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN + +CONTINUOUS_DOMAINS = {PROXIMITY_DOMAIN, SENSOR_DOMAIN} +CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] + +UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' +UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" + +OLD_STATE = aliased(States, name="old_state") + + +class JSONLiteral(JSON): # type: ignore[misc] + """Teach SA how to literalize json.""" + + def literal_processor(self, dialect: str) -> Callable[[Any], str]: + """Processor to convert a value to JSON.""" + + def process(value: Any) -> str: + """Dump json.""" + return json.dumps(value) + + return process + + +EVENT_DATA_JSON = type_coerce( + EventData.shared_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) +) +OLD_FORMAT_EVENT_DATA_JSON = type_coerce( + Events.event_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) +) + +SHARED_ATTRS_JSON = type_coerce( + StateAttributes.shared_attrs.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) +OLD_FORMAT_ATTRS_JSON = type_coerce( + States.attributes.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) + + +PSUEDO_EVENT_STATE_CHANGED = None +# Since we don't store event_types and None +# and we don't store state_changed in events +# we use a NULL for state_changed events +# when we synthesize them from the states table +# since it avoids another column being sent +# in the payload + +EVENT_COLUMNS = ( + Events.event_id.label("event_id"), + Events.event_type.label("event_type"), + Events.event_data.label("event_data"), + Events.time_fired.label("time_fired"), + Events.context_id.label("context_id"), + Events.context_user_id.label("context_user_id"), + Events.context_parent_id.label("context_parent_id"), +) + +STATE_COLUMNS = ( + States.state_id.label("state_id"), + States.state.label("state"), + States.entity_id.label("entity_id"), + SHARED_ATTRS_JSON["icon"].as_string().label("icon"), + OLD_FORMAT_ATTRS_JSON["icon"].as_string().label("old_format_icon"), +) + +STATE_CONTEXT_ONLY_COLUMNS = ( + States.state_id.label("state_id"), + States.state.label("state"), + States.entity_id.label("entity_id"), + literal(value=None, type_=sqlalchemy.String).label("icon"), + literal(value=None, type_=sqlalchemy.String).label("old_format_icon"), +) + +EVENT_COLUMNS_FOR_STATE_SELECT = [ + literal(value=None, type_=sqlalchemy.Text).label("event_id"), + # We use PSUEDO_EVENT_STATE_CHANGED aka None for + # state_changed events since it takes up less + # space in the response and every row has to be + # marked with the event_type + literal(value=PSUEDO_EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( + "event_type" + ), + literal(value=None, type_=sqlalchemy.Text).label("event_data"), + States.last_updated.label("time_fired"), + States.context_id.label("context_id"), + States.context_user_id.label("context_user_id"), + States.context_parent_id.label("context_parent_id"), + literal(value=None, type_=sqlalchemy.Text).label("shared_data"), +] + +EMPTY_STATE_COLUMNS = ( + literal(value=None, type_=sqlalchemy.String).label("state_id"), + literal(value=None, type_=sqlalchemy.String).label("state"), + literal(value=None, type_=sqlalchemy.String).label("entity_id"), + literal(value=None, type_=sqlalchemy.String).label("icon"), + literal(value=None, type_=sqlalchemy.String).label("old_format_icon"), +) + + +EVENT_ROWS_NO_STATES = ( + *EVENT_COLUMNS, + EventData.shared_data.label("shared_data"), + *EMPTY_STATE_COLUMNS, +) + +# Virtual column to tell logbook if it should avoid processing +# the event as its only used to link contexts +CONTEXT_ONLY = literal("1").label("context_only") +NOT_CONTEXT_ONLY = literal(None).label("context_only") + + +def select_events_context_id_subquery( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], +) -> Select: + """Generate the select for a context_id subquery.""" + return ( + select(Events.context_id) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.event_type.in_(event_types)) + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + + +def select_events_context_only() -> Select: + """Generate an events query that mark them as for context_only. + + By marking them as context_only we know they are only for + linking context ids and we can avoid processing them. + """ + return select(*EVENT_ROWS_NO_STATES, CONTEXT_ONLY).outerjoin( + EventData, (Events.data_id == EventData.data_id) + ) + + +def select_states_context_only() -> Select: + """Generate an states query that mark them as for context_only. + + By marking them as context_only we know they are only for + linking context ids and we can avoid processing them. + """ + return select( + *EVENT_COLUMNS_FOR_STATE_SELECT, *STATE_CONTEXT_ONLY_COLUMNS, CONTEXT_ONLY + ) + + +def select_events_without_states( + start_day: dt, end_day: dt, event_types: tuple[str, ...] +) -> Select: + """Generate an events select that does not join states.""" + return ( + select(*EVENT_ROWS_NO_STATES, NOT_CONTEXT_ONLY) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.event_type.in_(event_types)) + .outerjoin(EventData, (Events.data_id == EventData.data_id)) + ) + + +def select_states() -> Select: + """Generate a states select that formats the states table as event rows.""" + return select( + *EVENT_COLUMNS_FOR_STATE_SELECT, + *STATE_COLUMNS, + NOT_CONTEXT_ONLY, + ) + + +def legacy_select_events_context_id( + start_day: dt, end_day: dt, context_id: str +) -> Select: + """Generate a legacy events context id select that also joins states.""" + # This can be removed once we no longer have event_ids in the states table + return ( + select( + *EVENT_COLUMNS, + literal(value=None, type_=sqlalchemy.String).label("shared_data"), + *STATE_COLUMNS, + NOT_CONTEXT_ONLY, + ) + .outerjoin(States, (Events.event_id == States.event_id)) + .where( + (States.last_updated == States.last_changed) | States.last_changed.is_(None) + ) + .where(_not_continuous_entity_matcher()) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + .where((Events.time_fired > start_day) & (Events.time_fired < end_day)) + .where(Events.context_id == context_id) + ) + + +def apply_states_filters(query: Query, start_day: dt, end_day: dt) -> Query: + """Filter states by time range. + + Filters states that do not have an old state or new state (added / removed) + Filters states that are in a continuous domain with a UOM. + Filters states that do not have matching last_updated and last_changed. + """ + return ( + query.filter( + (States.last_updated > start_day) & (States.last_updated < end_day) + ) + .outerjoin(OLD_STATE, (States.old_state_id == OLD_STATE.state_id)) + .where(_missing_state_matcher()) + .where(_not_continuous_entity_matcher()) + .where( + (States.last_updated == States.last_changed) | States.last_changed.is_(None) + ) + .outerjoin( + StateAttributes, (States.attributes_id == StateAttributes.attributes_id) + ) + ) + + +def _missing_state_matcher() -> sqlalchemy.and_: + # The below removes state change events that do not have + # and old_state or the old_state is missing (newly added entities) + # or the new_state is missing (removed entities) + return sqlalchemy.and_( + OLD_STATE.state_id.isnot(None), + (States.state != OLD_STATE.state), + States.state.isnot(None), + ) + + +def _not_continuous_entity_matcher() -> sqlalchemy.or_: + """Match non continuous entities.""" + return sqlalchemy.or_( + _not_continuous_domain_matcher(), + sqlalchemy.and_( + _continuous_domain_matcher, _not_uom_attributes_matcher() + ).self_group(), + ) + + +def _not_continuous_domain_matcher() -> sqlalchemy.and_: + """Match not continuous domains.""" + return sqlalchemy.and_( + *[ + ~States.entity_id.like(entity_domain) + for entity_domain in CONTINUOUS_ENTITY_ID_LIKE + ], + ).self_group() + + +def _continuous_domain_matcher() -> sqlalchemy.or_: + """Match continuous domains.""" + return sqlalchemy.or_( + *[ + States.entity_id.like(entity_domain) + for entity_domain in CONTINUOUS_ENTITY_ID_LIKE + ], + ).self_group() + + +def _not_uom_attributes_matcher() -> ClauseList: + """Prefilter ATTR_UNIT_OF_MEASUREMENT as its much faster in sql.""" + return ~StateAttributes.shared_attrs.like( + UNIT_OF_MEASUREMENT_JSON_LIKE + ) | ~States.attributes.like(UNIT_OF_MEASUREMENT_JSON_LIKE) diff --git a/homeassistant/components/logbook/queries/devices.py b/homeassistant/components/logbook/queries/devices.py new file mode 100644 index 00000000000..20b56ef8dd6 --- /dev/null +++ b/homeassistant/components/logbook/queries/devices.py @@ -0,0 +1,87 @@ +"""Devices queries for logbook.""" +from __future__ import annotations + +from collections.abc import Iterable +from datetime import datetime as dt + +from sqlalchemy import Column, lambda_stmt, select, union_all +from sqlalchemy.orm import Query +from sqlalchemy.sql.elements import ClauseList +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from homeassistant.components.recorder.models import Events, States + +from .common import ( + EVENT_DATA_JSON, + select_events_context_id_subquery, + select_events_context_only, + select_events_without_states, + select_states_context_only, +) + +DEVICE_ID_IN_EVENT: Column = EVENT_DATA_JSON["device_id"] + + +def _select_device_id_context_ids_sub_query( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + json_quotable_device_ids: list[str], +) -> Select: + """Generate a subquery to find context ids for multiple devices.""" + return select( + union_all( + select_events_context_id_subquery(start_day, end_day, event_types).where( + apply_event_device_id_matchers(json_quotable_device_ids) + ), + ).c.context_id + ) + + +def _apply_devices_context_union( + query: Query, + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + json_quotable_device_ids: list[str], +) -> StatementLambdaElement: + """Generate a CTE to find the device context ids and a query to find linked row.""" + devices_cte = _select_device_id_context_ids_sub_query( + start_day, + end_day, + event_types, + json_quotable_device_ids, + ).cte() + return query.union_all( + select_events_context_only().where(Events.context_id.in_(devices_cte)), + select_states_context_only().where(States.context_id.in_(devices_cte)), + ) + + +def devices_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + json_quotable_device_ids: list[str], +) -> StatementLambdaElement: + """Generate a logbook query for multiple devices.""" + stmt = lambda_stmt( + lambda: _apply_devices_context_union( + select_events_without_states(start_day, end_day, event_types).where( + apply_event_device_id_matchers(json_quotable_device_ids) + ), + start_day, + end_day, + event_types, + json_quotable_device_ids, + ).order_by(Events.time_fired) + ) + return stmt + + +def apply_event_device_id_matchers( + json_quotable_device_ids: Iterable[str], +) -> ClauseList: + """Create matchers for the device_ids in the event_data.""" + return DEVICE_ID_IN_EVENT.in_(json_quotable_device_ids) diff --git a/homeassistant/components/logbook/queries/entities.py b/homeassistant/components/logbook/queries/entities.py new file mode 100644 index 00000000000..6db0931e9f3 --- /dev/null +++ b/homeassistant/components/logbook/queries/entities.py @@ -0,0 +1,124 @@ +"""Entities queries for logbook.""" +from __future__ import annotations + +from collections.abc import Iterable +from datetime import datetime as dt + +import sqlalchemy +from sqlalchemy import Column, lambda_stmt, select, union_all +from sqlalchemy.orm import Query +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from homeassistant.components.recorder.models import ( + ENTITY_ID_LAST_UPDATED_INDEX, + Events, + States, +) + +from .common import ( + EVENT_DATA_JSON, + OLD_FORMAT_EVENT_DATA_JSON, + apply_states_filters, + select_events_context_id_subquery, + select_events_context_only, + select_events_without_states, + select_states, + select_states_context_only, +) + +ENTITY_ID_IN_EVENT: Column = EVENT_DATA_JSON["entity_id"] +OLD_ENTITY_ID_IN_EVENT: Column = OLD_FORMAT_EVENT_DATA_JSON["entity_id"] + + +def _select_entities_context_ids_sub_query( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], +) -> Select: + """Generate a subquery to find context ids for multiple entities.""" + return select( + union_all( + select_events_context_id_subquery(start_day, end_day, event_types).where( + apply_event_entity_id_matchers(json_quotable_entity_ids) + ), + apply_entities_hints(select(States.context_id)) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)), + ).c.context_id + ) + + +def _apply_entities_context_union( + query: Query, + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], +) -> StatementLambdaElement: + """Generate a CTE to find the entity and device context ids and a query to find linked row.""" + entities_cte = _select_entities_context_ids_sub_query( + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + ).cte() + return query.union_all( + states_query_for_entity_ids(start_day, end_day, entity_ids), + select_events_context_only().where(Events.context_id.in_(entities_cte)), + select_states_context_only() + .where(States.entity_id.not_in(entity_ids)) + .where(States.context_id.in_(entities_cte)), + ) + + +def entities_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], +) -> StatementLambdaElement: + """Generate a logbook query for multiple entities.""" + assert json_quotable_entity_ids is not None + return lambda_stmt( + lambda: _apply_entities_context_union( + select_events_without_states(start_day, end_day, event_types).where( + apply_event_entity_id_matchers(json_quotable_entity_ids) + ), + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + ).order_by(Events.time_fired) + ) + + +def states_query_for_entity_ids( + start_day: dt, end_day: dt, entity_ids: list[str] +) -> Query: + """Generate a select for states from the States table for specific entities.""" + return apply_states_filters( + apply_entities_hints(select_states()), start_day, end_day + ).where(States.entity_id.in_(entity_ids)) + + +def apply_event_entity_id_matchers( + json_quotable_entity_ids: Iterable[str], +) -> sqlalchemy.or_: + """Create matchers for the entity_id in the event_data.""" + return ENTITY_ID_IN_EVENT.in_( + json_quotable_entity_ids + ) | OLD_ENTITY_ID_IN_EVENT.in_(json_quotable_entity_ids) + + +def apply_entities_hints(query: Query) -> Query: + """Force mysql to use the right index on large selects.""" + return query.with_hint( + States, f"FORCE INDEX ({ENTITY_ID_LAST_UPDATED_INDEX})", dialect_name="mysql" + ) diff --git a/homeassistant/components/logbook/queries/entities_and_devices.py b/homeassistant/components/logbook/queries/entities_and_devices.py new file mode 100644 index 00000000000..d8a23635ad7 --- /dev/null +++ b/homeassistant/components/logbook/queries/entities_and_devices.py @@ -0,0 +1,111 @@ +"""Entities and Devices queries for logbook.""" +from __future__ import annotations + +from collections.abc import Iterable +from datetime import datetime as dt + +import sqlalchemy +from sqlalchemy import lambda_stmt, select, union_all +from sqlalchemy.orm import Query +from sqlalchemy.sql.lambdas import StatementLambdaElement +from sqlalchemy.sql.selectable import Select + +from homeassistant.components.recorder.models import Events, States + +from .common import ( + select_events_context_id_subquery, + select_events_context_only, + select_events_without_states, + select_states_context_only, +) +from .devices import apply_event_device_id_matchers +from .entities import ( + apply_entities_hints, + apply_event_entity_id_matchers, + states_query_for_entity_ids, +) + + +def _select_entities_device_id_context_ids_sub_query( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], + json_quotable_device_ids: list[str], +) -> Select: + """Generate a subquery to find context ids for multiple entities and multiple devices.""" + return select( + union_all( + select_events_context_id_subquery(start_day, end_day, event_types).where( + _apply_event_entity_id_device_id_matchers( + json_quotable_entity_ids, json_quotable_device_ids + ) + ), + apply_entities_hints(select(States.context_id)) + .filter((States.last_updated > start_day) & (States.last_updated < end_day)) + .where(States.entity_id.in_(entity_ids)), + ).c.context_id + ) + + +def _apply_entities_devices_context_union( + query: Query, + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], + json_quotable_device_ids: list[str], +) -> StatementLambdaElement: + devices_entities_cte = _select_entities_device_id_context_ids_sub_query( + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + json_quotable_device_ids, + ).cte() + return query.union_all( + states_query_for_entity_ids(start_day, end_day, entity_ids), + select_events_context_only().where(Events.context_id.in_(devices_entities_cte)), + select_states_context_only() + .where(States.entity_id.not_in(entity_ids)) + .where(States.context_id.in_(devices_entities_cte)), + ) + + +def entities_devices_stmt( + start_day: dt, + end_day: dt, + event_types: tuple[str, ...], + entity_ids: list[str], + json_quotable_entity_ids: list[str], + json_quotable_device_ids: list[str], +) -> StatementLambdaElement: + """Generate a logbook query for multiple entities.""" + stmt = lambda_stmt( + lambda: _apply_entities_devices_context_union( + select_events_without_states(start_day, end_day, event_types).where( + _apply_event_entity_id_device_id_matchers( + json_quotable_entity_ids, json_quotable_device_ids + ) + ), + start_day, + end_day, + event_types, + entity_ids, + json_quotable_entity_ids, + json_quotable_device_ids, + ).order_by(Events.time_fired) + ) + return stmt + + +def _apply_event_entity_id_device_id_matchers( + json_quotable_entity_ids: Iterable[str], json_quotable_device_ids: Iterable[str] +) -> sqlalchemy.or_: + """Create matchers for the device_id and entity_id in the event_data.""" + return apply_event_entity_id_matchers( + json_quotable_entity_ids + ) | apply_event_device_id_matchers(json_quotable_device_ids) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index d7bb59bdeb1..9d16541e398 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -105,6 +105,9 @@ class FAST_PYSQLITE_DATETIME(sqlite.DATETIME): # type: ignore[misc] JSON_VARIENT_CAST = Text().with_variant( postgresql.JSON(none_as_null=True), "postgresql" ) +JSONB_VARIENT_CAST = Text().with_variant( + postgresql.JSONB(none_as_null=True), "postgresql" +) DATETIME_TYPE = ( DateTime(timezone=True) .with_variant(mysql.DATETIME(timezone=True, fsp=6), "mysql") diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index dddbbc61134..7657ebf2b83 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -5,6 +5,7 @@ import collections from datetime import datetime, timedelta from http import HTTPStatus import json +from typing import Callable from unittest.mock import Mock, patch import pytest @@ -30,11 +31,13 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP, + EVENT_LOGBOOK_ENTRY, STATE_OFF, STATE_ON, ) import homeassistant.core as ha -from homeassistant.helpers import entity_registry as er +from homeassistant.core import Event, HomeAssistant +from homeassistant.helpers import device_registry, entity_registry as er from homeassistant.helpers.entityfilter import CONF_ENTITY_GLOBS from homeassistant.helpers.json import JSONEncoder from homeassistant.setup import async_setup_component @@ -42,7 +45,7 @@ import homeassistant.util.dt as dt_util from .common import MockRow, mock_humanify -from tests.common import async_capture_events, mock_platform +from tests.common import MockConfigEntry, async_capture_events, mock_platform from tests.components.recorder.common import ( async_recorder_block_till_done, async_wait_recording_done, @@ -92,12 +95,15 @@ async def test_service_call_create_logbook_entry(hass_): # Our service call will unblock when the event listeners have been # scheduled. This means that they may not have been processed yet. await async_wait_recording_done(hass_) + ent_reg = er.async_get(hass_) events = list( logbook._get_events( hass_, dt_util.utcnow() - timedelta(hours=1), dt_util.utcnow() + timedelta(hours=1), + (EVENT_LOGBOOK_ENTRY,), + ent_reg, ) ) assert len(events) == 2 @@ -131,12 +137,15 @@ async def test_service_call_create_logbook_entry_invalid_entity_id(hass, recorde }, ) await async_wait_recording_done(hass) + ent_reg = er.async_get(hass) events = list( logbook._get_events( hass, dt_util.utcnow() - timedelta(hours=1), dt_util.utcnow() + timedelta(hours=1), + (EVENT_LOGBOOK_ENTRY,), + ent_reg, ) ) assert len(events) == 1 @@ -2431,3 +2440,270 @@ async def test_get_events_bad_end_time(hass, hass_ws_client, recorder_mock): response = await client.receive_json() assert not response["success"] assert response["error"]["code"] == "invalid_end_time" + + +async def test_get_events_with_device_ids(hass, hass_ws_client, recorder_mock): + """Test logbook get_events for device ids.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + + entry = MockConfigEntry(domain="test", data={"first": True}, options=None) + entry.add_to_hass(hass) + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_or_create( + config_entry_id=entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + identifiers={("bridgeid", "0123")}, + sw_version="sw-version", + name="device name", + manufacturer="manufacturer", + model="model", + suggested_area="Game Room", + ) + + class MockLogbookPlatform: + """Mock a logbook platform.""" + + @ha.callback + def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[ + [str, str, Callable[[Event], dict[str, str]]], None + ], + ) -> None: + """Describe logbook events.""" + + @ha.callback + def async_describe_test_event(event: Event) -> dict[str, str]: + """Describe mock logbook event.""" + return { + "name": "device name", + "message": "is on fire", + } + + async_describe_event("test", "mock_event", async_describe_test_event) + + await logbook._process_logbook_platform(hass, "test", MockLogbookPlatform) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + hass.bus.async_fire("mock_event", {"device_id": device.id}) + + hass.states.async_set("light.kitchen", STATE_OFF) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 100}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 200}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 300}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 400}) + await hass.async_block_till_done() + context = ha.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + + hass.states.async_set("light.kitchen", STATE_OFF, context=context) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + client = await hass_ws_client() + + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "device_ids": [device.id], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + results = response["result"] + assert len(results) == 1 + assert results[0]["name"] == "device name" + assert results[0]["message"] == "is on fire" + assert isinstance(results[0]["when"], float) + + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + "device_ids": [device.id], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + + results = response["result"] + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "on" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "off" + + await client.send_json( + { + "id": 3, + "type": "logbook/get_events", + "start_time": now.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + + results = response["result"] + assert len(results) == 4 + assert results[0]["message"] == "started" + assert results[1]["name"] == "device name" + assert results[1]["message"] == "is on fire" + assert isinstance(results[1]["when"], float) + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "on" + assert isinstance(results[2]["when"], float) + assert results[3]["entity_id"] == "light.kitchen" + assert results[3]["state"] == "off" + assert isinstance(results[3]["when"], float) + + +async def test_logbook_select_entities_context_id(hass, recorder_mock, hass_client): + """Test the logbook view with end_time and entity with automations and scripts.""" + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await async_recorder_block_till_done(hass) + + context = ha.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + + # An Automation + automation_entity_id_test = "automation.alarm" + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: automation_entity_id_test}, + context=context, + ) + hass.bus.async_fire( + EVENT_SCRIPT_STARTED, + {ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"}, + context=context, + ) + hass.states.async_set( + automation_entity_id_test, + STATE_ON, + {ATTR_FRIENDLY_NAME: "Alarm Automation"}, + context=context, + ) + + entity_id_test = "alarm_control_panel.area_001" + hass.states.async_set(entity_id_test, STATE_OFF, context=context) + await hass.async_block_till_done() + hass.states.async_set(entity_id_test, STATE_ON, context=context) + await hass.async_block_till_done() + entity_id_second = "alarm_control_panel.area_002" + hass.states.async_set(entity_id_second, STATE_OFF, context=context) + await hass.async_block_till_done() + hass.states.async_set(entity_id_second, STATE_ON, context=context) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + entity_id_third = "alarm_control_panel.area_003" + + logbook.async_log_entry( + hass, + "mock_name", + "mock_message", + "alarm_control_panel", + entity_id_third, + context, + ) + await hass.async_block_till_done() + + logbook.async_log_entry( + hass, + "mock_name", + "mock_message", + "homeassistant", + None, + context, + ) + await hass.async_block_till_done() + + # A service call + light_turn_off_service_context = ha.Context( + id="9c5bd62de45711eaaeb351041eec8dd9", + user_id="9400facee45711eaa9308bfd3d19e474", + ) + hass.states.async_set("light.switch", STATE_ON) + await hass.async_block_till_done() + + hass.bus.async_fire( + EVENT_CALL_SERVICE, + { + ATTR_DOMAIN: "light", + ATTR_SERVICE: "turn_off", + ATTR_ENTITY_ID: "light.switch", + }, + context=light_turn_off_service_context, + ) + await hass.async_block_till_done() + + hass.states.async_set( + "light.switch", STATE_OFF, context=light_turn_off_service_context + ) + await async_wait_recording_done(hass) + + client = await hass_client() + + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + + # Test today entries with filter by end_time + end_time = start + timedelta(hours=24) + response = await client.get( + f"/api/logbook/{start_date.isoformat()}?end_time={end_time}&entity={entity_id_test},{entity_id_second},{entity_id_third},light.switch" + ) + assert response.status == HTTPStatus.OK + json_dict = await response.json() + + assert json_dict[0]["entity_id"] == entity_id_test + assert json_dict[0]["context_event_type"] == "automation_triggered" + assert json_dict[0]["context_entity_id"] == "automation.alarm" + assert json_dict[0]["context_entity_id_name"] == "Alarm Automation" + assert json_dict[0]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + + assert json_dict[1]["entity_id"] == entity_id_second + assert json_dict[1]["context_event_type"] == "automation_triggered" + assert json_dict[1]["context_entity_id"] == "automation.alarm" + assert json_dict[1]["context_entity_id_name"] == "Alarm Automation" + assert json_dict[1]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + + assert json_dict[2]["entity_id"] == "alarm_control_panel.area_003" + assert json_dict[2]["context_event_type"] == "automation_triggered" + assert json_dict[2]["context_entity_id"] == "automation.alarm" + assert json_dict[2]["domain"] == "alarm_control_panel" + assert json_dict[2]["context_entity_id_name"] == "Alarm Automation" + assert json_dict[2]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + + assert json_dict[3]["entity_id"] == "light.switch" + assert json_dict[3]["context_event_type"] == "call_service" + assert json_dict[3]["context_domain"] == "light" + assert json_dict[3]["context_service"] == "turn_off" + assert json_dict[3]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" From c52f535eb3b0a48d5da203ed6d032f28c6ad2ebb Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Wed, 18 May 2022 10:00:46 +0300 Subject: [PATCH 551/930] Fix filesize doing IO in event loop (#72038) --- homeassistant/components/filesize/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/filesize/sensor.py b/homeassistant/components/filesize/sensor.py index 6c52fcbdd5a..5f66aefaab5 100644 --- a/homeassistant/components/filesize/sensor.py +++ b/homeassistant/components/filesize/sensor.py @@ -96,7 +96,7 @@ class FileSizeCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> dict[str, float | int | datetime]: """Fetch file information.""" try: - statinfo = os.stat(self._path) + statinfo = await self.hass.async_add_executor_job(os.stat, self._path) except OSError as error: raise UpdateFailed(f"Can not retrieve file statistics {error}") from error From 14361f95878d1e548430e0a792255c754156d11d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 18 May 2022 00:14:33 -0700 Subject: [PATCH 552/930] Add media browser support for GStreamer (#72051) * Add media browser support for GStreamer * Fix media type check --- .../components/gstreamer/media_player.py | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/gstreamer/media_player.py b/homeassistant/components/gstreamer/media_player.py index d1b0fb056ff..545941f2924 100644 --- a/homeassistant/components/gstreamer/media_player.py +++ b/homeassistant/components/gstreamer/media_player.py @@ -6,11 +6,16 @@ import logging from gsp import GstreamerPlayer import voluptuous as vol +from homeassistant.components import media_source from homeassistant.components.media_player import ( PLATFORM_SCHEMA, + BrowseMedia, MediaPlayerEntity, MediaPlayerEntityFeature, ) +from homeassistant.components.media_player.browse_media import ( + async_process_play_media_url, +) from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC from homeassistant.const import CONF_NAME, EVENT_HOMEASSISTANT_STOP, STATE_IDLE from homeassistant.core import HomeAssistant @@ -58,6 +63,7 @@ class GstreamerDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.NEXT_TRACK + | MediaPlayerEntityFeature.BROWSE_MEDIA ) def __init__(self, player, name): @@ -86,12 +92,20 @@ class GstreamerDevice(MediaPlayerEntity): """Set the volume level.""" self._player.volume = volume - def play_media(self, media_type, media_id, **kwargs): + async def async_play_media(self, media_type, media_id, **kwargs): """Play media.""" - if media_type != MEDIA_TYPE_MUSIC: + # Handle media_source + if media_source.is_media_source_id(media_id): + sourced_media = await media_source.async_resolve_media(self.hass, media_id) + media_id = sourced_media.url + + elif media_type != MEDIA_TYPE_MUSIC: _LOGGER.error("Invalid media type") return - self._player.queue(media_id) + + media_id = async_process_play_media_url(self.hass, media_id) + + await self.hass.async_add_executor_job(self._player.queue, media_id) def media_play(self): """Play.""" @@ -149,3 +163,13 @@ class GstreamerDevice(MediaPlayerEntity): def media_album_name(self): """Media album.""" return self._album + + async def async_browse_media( + self, media_content_type=None, media_content_id=None + ) -> BrowseMedia: + """Implement the websocket media browsing helper.""" + return await media_source.async_browse_media( + self.hass, + media_content_id, + content_filter=lambda item: item.media_content_type.startswith("audio/"), + ) From 12020ffac1a537e2f83df40ba911252ad478919d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 18 May 2022 00:15:17 -0700 Subject: [PATCH 553/930] Add Media Browser support to VLC (#72052) --- homeassistant/components/vlc/media_player.py | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/vlc/media_player.py b/homeassistant/components/vlc/media_player.py index 10903295f95..7312eacd1c6 100644 --- a/homeassistant/components/vlc/media_player.py +++ b/homeassistant/components/vlc/media_player.py @@ -6,11 +6,16 @@ import logging import vlc import voluptuous as vol +from homeassistant.components import media_source from homeassistant.components.media_player import ( PLATFORM_SCHEMA, + BrowseMedia, MediaPlayerEntity, MediaPlayerEntityFeature, ) +from homeassistant.components.media_player.browse_media import ( + async_process_play_media_url, +) from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC from homeassistant.const import CONF_NAME, STATE_IDLE, STATE_PAUSED, STATE_PLAYING from homeassistant.core import HomeAssistant @@ -54,6 +59,7 @@ class VlcDevice(MediaPlayerEntity): | MediaPlayerEntityFeature.PLAY_MEDIA | MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.STOP + | MediaPlayerEntityFeature.BROWSE_MEDIA ) def __init__(self, name, arguments): @@ -158,15 +164,36 @@ class VlcDevice(MediaPlayerEntity): self._vlc.stop() self._state = STATE_IDLE - def play_media(self, media_type, media_id, **kwargs): + async def async_play_media(self, media_type, media_id, **kwargs): """Play media from a URL or file.""" - if media_type != MEDIA_TYPE_MUSIC: + # Handle media_source + if media_source.is_media_source_id(media_id): + sourced_media = await media_source.async_resolve_media(self.hass, media_id) + media_id = sourced_media.url + + elif media_type != MEDIA_TYPE_MUSIC: _LOGGER.error( "Invalid media type %s. Only %s is supported", media_type, MEDIA_TYPE_MUSIC, ) return - self._vlc.set_media(self._instance.media_new(media_id)) - self._vlc.play() + + media_id = async_process_play_media_url(self.hass, media_id) + + def play(): + self._vlc.set_media(self._instance.media_new(media_id)) + self._vlc.play() + + await self.hass.async_add_executor_job(play) self._state = STATE_PLAYING + + async def async_browse_media( + self, media_content_type=None, media_content_id=None + ) -> BrowseMedia: + """Implement the websocket media browsing helper.""" + return await media_source.async_browse_media( + self.hass, + media_content_id, + content_filter=lambda item: item.media_content_type.startswith("audio/"), + ) From 8d57f704668c020f79e5590d046ee8868913e8cb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 09:36:20 +0200 Subject: [PATCH 554/930] Ignore UpnpXmlContentError in SamsungTV (#72056) --- homeassistant/components/samsungtv/media_player.py | 6 ++++-- tests/components/samsungtv/test_media_player.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index a0b70af2db5..0599115774e 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -15,6 +15,7 @@ from async_upnp_client.exceptions import ( UpnpConnectionError, UpnpError, UpnpResponseError, + UpnpXmlContentError, ) from async_upnp_client.profiles.dlna import DmrDevice from async_upnp_client.utils import async_get_local_ip @@ -270,11 +271,12 @@ class SamsungTVDevice(MediaPlayerEntity): # NETWORK,NONE upnp_factory = UpnpFactory(upnp_requester, non_strict=True) upnp_device: UpnpDevice | None = None - with contextlib.suppress(UpnpConnectionError, UpnpResponseError): + try: upnp_device = await upnp_factory.async_create_device( self._ssdp_rendering_control_location ) - if not upnp_device: + except (UpnpConnectionError, UpnpResponseError, UpnpXmlContentError) as err: + LOGGER.debug("Unable to create Upnp DMR device: %r", err, exc_info=True) return _, event_ip = await async_get_local_ip( self._ssdp_rendering_control_location, self.hass.loop diff --git a/tests/components/samsungtv/test_media_player.py b/tests/components/samsungtv/test_media_player.py index e8407a86a3e..56cce6ebfbf 100644 --- a/tests/components/samsungtv/test_media_player.py +++ b/tests/components/samsungtv/test_media_player.py @@ -1368,6 +1368,7 @@ async def test_upnp_not_available( ) -> None: """Test for volume control when Upnp is not available.""" await setup_samsungtv_entry(hass, MOCK_ENTRY_WS) + assert "Unable to create Upnp DMR device" in caplog.text # Upnp action fails assert await hass.services.async_call( @@ -1385,6 +1386,7 @@ async def test_upnp_missing_service( ) -> None: """Test for volume control when Upnp is not available.""" await setup_samsungtv_entry(hass, MOCK_ENTRY_WS) + assert "Unable to create Upnp DMR device" in caplog.text # Upnp action fails assert await hass.services.async_call( From 28515404533865fa8001000e393ba710d1feafb5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 10:34:20 +0200 Subject: [PATCH 555/930] Drop unnecessary async definitions in onewire (#72018) --- homeassistant/components/onewire/config_flow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/onewire/config_flow.py b/homeassistant/components/onewire/config_flow.py index 5e944c51e0a..5876bcfd206 100644 --- a/homeassistant/components/onewire/config_flow.py +++ b/homeassistant/components/onewire/config_flow.py @@ -139,7 +139,7 @@ class OnewireOptionsFlowHandler(OptionsFlow): if user_input.get(INPUT_ENTRY_CLEAR_OPTIONS): # Reset all options self.options = {} - return await self._update_options() + return self._async_update_options() selected_devices: list[str] = ( user_input.get(INPUT_ENTRY_DEVICE_SELECTION) or [] @@ -181,7 +181,7 @@ class OnewireOptionsFlowHandler(OptionsFlow): self._update_device_options(user_input) if self.devices_to_configure: return await self.async_step_configure_device(user_input=None) - return await self._update_options() + return self._async_update_options() self.current_device, description = self.devices_to_configure.popitem() data_schema = vol.Schema( @@ -201,7 +201,8 @@ class OnewireOptionsFlowHandler(OptionsFlow): description_placeholders={"sensor_id": self.current_device}, ) - async def _update_options(self) -> FlowResult: + @callback + def _async_update_options(self) -> FlowResult: """Update config entry options.""" return self.async_create_entry(title="", data=self.options) From 5724d87c4068fa76dab3c6787dc49393f818ebc7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 11:46:09 +0200 Subject: [PATCH 556/930] Cleanup deprecated async_get_registry in uptimerobot (#72076) --- homeassistant/components/uptimerobot/__init__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/uptimerobot/__init__.py b/homeassistant/components/uptimerobot/__init__.py index 6d9be1b2364..a4c975ff58e 100644 --- a/homeassistant/components/uptimerobot/__init__.py +++ b/homeassistant/components/uptimerobot/__init__.py @@ -12,12 +12,8 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import ( - DeviceRegistry, - async_entries_for_config_entry, - async_get_registry, -) from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import API_ATTR_OK, COORDINATOR_UPDATE_INTERVAL, DOMAIN, LOGGER, PLATFORMS @@ -32,7 +28,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "Wrong API key type detected, use the 'main' API key" ) uptime_robot_api = UptimeRobot(key, async_get_clientsession(hass)) - dev_reg = await async_get_registry(hass) + dev_reg = dr.async_get(hass) hass.data[DOMAIN][entry.entry_id] = coordinator = UptimeRobotDataUpdateCoordinator( hass, @@ -67,7 +63,7 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): self, hass: HomeAssistant, config_entry_id: str, - dev_reg: DeviceRegistry, + dev_reg: dr.DeviceRegistry, api: UptimeRobot, ) -> None: """Initialize coordinator.""" @@ -97,7 +93,7 @@ class UptimeRobotDataUpdateCoordinator(DataUpdateCoordinator): current_monitors = { list(device.identifiers)[0][1] - for device in async_entries_for_config_entry( + for device in dr.async_entries_for_config_entry( self._device_registry, self._config_entry_id ) } From 2060f428189eb15f9ef613c494ac32f4f794dcfc Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 11:55:08 +0200 Subject: [PATCH 557/930] Cleanup deprecated async_get_registry in acmeda (#72060) --- homeassistant/components/acmeda/base.py | 8 +++----- homeassistant/components/acmeda/helpers.py | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/acmeda/base.py b/homeassistant/components/acmeda/base.py index 3338bf9667d..e9ffb94c6c6 100644 --- a/homeassistant/components/acmeda/base.py +++ b/homeassistant/components/acmeda/base.py @@ -2,10 +2,8 @@ import aiopulse from homeassistant.core import callback -from homeassistant.helpers import entity -from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg +from homeassistant.helpers import device_registry as dr, entity, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity_registry import async_get_registry as get_ent_reg from .const import ACMEDA_ENTITY_REMOVE, DOMAIN, LOGGER @@ -21,11 +19,11 @@ class AcmedaBase(entity.Entity): """Unregister from entity and device registry and call entity remove function.""" LOGGER.error("Removing %s %s", self.__class__.__name__, self.unique_id) - ent_registry = await get_ent_reg(self.hass) + ent_registry = er.async_get(self.hass) if self.entity_id in ent_registry.entities: ent_registry.async_remove(self.entity_id) - dev_registry = await get_dev_reg(self.hass) + dev_registry = dr.async_get(self.hass) device = dev_registry.async_get_device(identifiers={(DOMAIN, self.unique_id)}) if device is not None: dev_registry.async_update_device( diff --git a/homeassistant/components/acmeda/helpers.py b/homeassistant/components/acmeda/helpers.py index a1a262be77e..ff8f28ffbc3 100644 --- a/homeassistant/components/acmeda/helpers.py +++ b/homeassistant/components/acmeda/helpers.py @@ -3,7 +3,7 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, LOGGER @@ -36,7 +36,7 @@ def async_add_acmeda_entities( async def update_devices(hass: HomeAssistant, config_entry: ConfigEntry, api): """Tell hass that device info has been updated.""" - dev_registry = await get_dev_reg(hass) + dev_registry = dr.async_get(hass) for api_item in api.values(): # Update Device name From fc84e4061f8d723ef9df565cda355d1c6aff7e32 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 11:59:13 +0200 Subject: [PATCH 558/930] Cleanup deprecated async_get_registry in philips_js (#72071) --- homeassistant/components/philips_js/device_trigger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/philips_js/device_trigger.py b/homeassistant/components/philips_js/device_trigger.py index 09784dae63f..a48f9e58331 100644 --- a/homeassistant/components/philips_js/device_trigger.py +++ b/homeassistant/components/philips_js/device_trigger.py @@ -12,7 +12,7 @@ from homeassistant.components.automation import ( from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant -from homeassistant.helpers.device_registry import DeviceRegistry, async_get_registry +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType from . import PhilipsTVDataUpdateCoordinator @@ -53,7 +53,7 @@ async def async_attach_trigger( ) -> CALLBACK_TYPE | None: """Attach a trigger.""" trigger_data = automation_info["trigger_data"] - registry: DeviceRegistry = await async_get_registry(hass) + registry: dr.DeviceRegistry = dr.async_get(hass) if config[CONF_TYPE] == TRIGGER_TYPE_TURN_ON: variables = { "trigger": { From 8b5803735f1f0e3b049b8c3c89943af08fc7d956 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 12:00:01 +0200 Subject: [PATCH 559/930] Cleanup deprecated async_get_registry in rfxtrx (#72073) --- .../components/rfxtrx/config_flow.py | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/rfxtrx/config_flow.py b/homeassistant/components/rfxtrx/config_flow.py index 8fce56564a4..61d01b8d533 100644 --- a/homeassistant/components/rfxtrx/config_flow.py +++ b/homeassistant/components/rfxtrx/config_flow.py @@ -24,16 +24,10 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.core import callback -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import ( - DeviceEntry, - DeviceRegistry, - async_entries_for_config_entry, - async_get_registry as async_get_device_registry, -) -from homeassistant.helpers.entity_registry import ( - async_entries_for_device, - async_get_registry as async_get_entity_registry, +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + entity_registry as er, ) from . import ( @@ -80,8 +74,8 @@ def none_or_int(value, base): class OptionsFlow(config_entries.OptionsFlow): """Handle Rfxtrx options.""" - _device_registry: DeviceRegistry - _device_entries: list[DeviceEntry] + _device_registry: dr.DeviceRegistry + _device_entries: list[dr.DeviceEntry] def __init__(self, config_entry: ConfigEntry) -> None: """Initialize rfxtrx options flow.""" @@ -135,8 +129,8 @@ class OptionsFlow(config_entries.OptionsFlow): return self.async_create_entry(title="", data={}) - device_registry = await async_get_device_registry(self.hass) - device_entries = async_entries_for_config_entry( + device_registry = dr.async_get(self.hass) + device_entries = dr.async_entries_for_config_entry( device_registry, self._config_entry.entry_id ) self._device_registry = device_registry @@ -320,8 +314,8 @@ class OptionsFlow(config_entries.OptionsFlow): old_device_id = "_".join(x for x in old_device_data[CONF_DEVICE_ID]) new_device_id = "_".join(x for x in new_device_data[CONF_DEVICE_ID]) - entity_registry = await async_get_entity_registry(self.hass) - entity_entries = async_entries_for_device( + entity_registry = er.async_get(self.hass) + entity_entries = er.async_entries_for_device( entity_registry, old_device, include_disabled_entities=True ) entity_migration_map = {} From 8492f282cb0e105d6751449649183aa89a5b7e8b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 12:37:27 +0200 Subject: [PATCH 560/930] Cleanup deprecated async_get_registry in xbox (#72079) --- homeassistant/components/xbox/binary_sensor.py | 6 ++---- homeassistant/components/xbox/sensor.py | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/xbox/binary_sensor.py b/homeassistant/components/xbox/binary_sensor.py index 592909f3aac..7cf7ca6a6a5 100644 --- a/homeassistant/components/xbox/binary_sensor.py +++ b/homeassistant/components/xbox/binary_sensor.py @@ -6,10 +6,8 @@ from functools import partial from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import ( - async_get_registry as async_get_entity_registry, -) from . import XboxUpdateCoordinator from .base_sensor import XboxBaseSensorEntity @@ -80,7 +78,7 @@ async def async_remove_entities( current: dict[str, XboxBinarySensorEntity], ) -> None: """Remove friend sensors from Home Assistant.""" - registry = await async_get_entity_registry(coordinator.hass) + registry = er.async_get(coordinator.hass) entities = current[xuid] for entity in entities: if entity.entity_id in registry.entities: diff --git a/homeassistant/components/xbox/sensor.py b/homeassistant/components/xbox/sensor.py index edcc4a8c135..02b4f8b84a4 100644 --- a/homeassistant/components/xbox/sensor.py +++ b/homeassistant/components/xbox/sensor.py @@ -6,10 +6,8 @@ from functools import partial from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import ( - async_get_registry as async_get_entity_registry, -) from . import XboxUpdateCoordinator from .base_sensor import XboxBaseSensorEntity @@ -82,7 +80,7 @@ async def async_remove_entities( current: dict[str, XboxSensorEntity], ) -> None: """Remove friend sensors from Home Assistant.""" - registry = await async_get_entity_registry(coordinator.hass) + registry = er.async_get(coordinator.hass) entities = current[xuid] for entity in entities: if entity.entity_id in registry.entities: From 4eb46d45caa78712fcb4c0db764ade02bd53fb82 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:12:38 +0200 Subject: [PATCH 561/930] Cleanup deprecated async_get_registry in tests (#72059) --- tests/components/efergy/test_init.py | 2 +- tests/components/goalzero/test_init.py | 2 +- tests/components/hyperion/test_camera.py | 2 +- tests/components/hyperion/test_light.py | 2 +- tests/components/hyperion/test_switch.py | 2 +- tests/components/insteon/test_api_device.py | 6 +++--- tests/components/knx/test_binary_sensor.py | 6 ++---- tests/components/knx/test_scene.py | 6 ++---- tests/components/mikrotik/test_init.py | 2 +- tests/components/motioneye/test_camera.py | 7 +++---- tests/components/motioneye/test_media_source.py | 8 ++++---- tests/components/motioneye/test_sensor.py | 2 +- tests/components/motioneye/test_switch.py | 2 +- tests/components/motioneye/test_web_hooks.py | 12 ++++++------ tests/components/ps4/test_init.py | 2 +- tests/components/steam_online/test_init.py | 2 +- tests/components/unifiprotect/test_services.py | 4 ++-- tests/components/uptimerobot/test_init.py | 13 +++++-------- tests/helpers/test_service.py | 2 +- 19 files changed, 38 insertions(+), 46 deletions(-) diff --git a/tests/components/efergy/test_init.py b/tests/components/efergy/test_init.py index 07c80e7bb04..49a152516b7 100644 --- a/tests/components/efergy/test_init.py +++ b/tests/components/efergy/test_init.py @@ -49,7 +49,7 @@ async def test_async_setup_entry_auth_failed(hass: HomeAssistant): async def test_device_info(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): """Test device info.""" entry = await setup_platform(hass, aioclient_mock, SENSOR_DOMAIN) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_device({(DOMAIN, entry.entry_id)}) diff --git a/tests/components/goalzero/test_init.py b/tests/components/goalzero/test_init.py index a436b491d48..5f84842ad27 100644 --- a/tests/components/goalzero/test_init.py +++ b/tests/components/goalzero/test_init.py @@ -68,7 +68,7 @@ async def test_update_failed( async def test_device_info(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): """Test device info.""" entry = await async_init_integration(hass, aioclient_mock) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_device({(DOMAIN, entry.entry_id)}) diff --git a/tests/components/hyperion/test_camera.py b/tests/components/hyperion/test_camera.py index 71e1e42cb1a..f83ed9c7e78 100644 --- a/tests/components/hyperion/test_camera.py +++ b/tests/components/hyperion/test_camera.py @@ -200,7 +200,7 @@ async def test_device_info(hass: HomeAssistant) -> None: assert device.model == HYPERION_MODEL_NAME assert device.name == TEST_INSTANCE_1["friendly_name"] - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/hyperion/test_light.py b/tests/components/hyperion/test_light.py index 2514f6b6cba..136a67f0dba 100644 --- a/tests/components/hyperion/test_light.py +++ b/tests/components/hyperion/test_light.py @@ -1331,7 +1331,7 @@ async def test_device_info(hass: HomeAssistant) -> None: assert device.model == HYPERION_MODEL_NAME assert device.name == TEST_INSTANCE_1["friendly_name"] - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/hyperion/test_switch.py b/tests/components/hyperion/test_switch.py index 7ec441716ce..cd1dbdcda5b 100644 --- a/tests/components/hyperion/test_switch.py +++ b/tests/components/hyperion/test_switch.py @@ -172,7 +172,7 @@ async def test_device_info(hass: HomeAssistant) -> None: assert device.model == HYPERION_MODEL_NAME assert device.name == TEST_INSTANCE_1["friendly_name"] - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/insteon/test_api_device.py b/tests/components/insteon/test_api_device.py index 49588c6ea8f..206565cb7c7 100644 --- a/tests/components/insteon/test_api_device.py +++ b/tests/components/insteon/test_api_device.py @@ -17,7 +17,7 @@ from homeassistant.components.insteon.api.device import ( async_device_name, ) from homeassistant.components.insteon.const import DOMAIN, MULTIPLE -from homeassistant.helpers.device_registry import async_get_registry +from homeassistant.helpers import device_registry as dr from .const import MOCK_USER_INPUT_PLM from .mock_devices import MockDevices @@ -40,7 +40,7 @@ async def _async_setup(hass, hass_ws_client): devices = MockDevices() await devices.async_load() - dev_reg = await async_get_registry(hass) + dev_reg = dr.async_get(hass) # Create device registry entry for mock node ha_device = dev_reg.async_get_or_create( config_entry_id=config_entry.entry_id, @@ -94,7 +94,7 @@ async def test_no_insteon_device(hass, hass_ws_client): devices = MockDevices() await devices.async_load() - dev_reg = await async_get_registry(hass) + dev_reg = dr.async_get(hass) # Create device registry entry for a Insteon device not in the Insteon devices list ha_device_1 = dev_reg.async_get_or_create( config_entry_id=config_entry.entry_id, diff --git a/tests/components/knx/test_binary_sensor.py b/tests/components/knx/test_binary_sensor.py index 25a223e76c8..ff58a36391c 100644 --- a/tests/components/knx/test_binary_sensor.py +++ b/tests/components/knx/test_binary_sensor.py @@ -6,10 +6,8 @@ from homeassistant.components.knx.const import CONF_STATE_ADDRESS, CONF_SYNC_STA from homeassistant.components.knx.schema import BinarySensorSchema from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant, State +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory -from homeassistant.helpers.entity_registry import ( - async_get_registry as async_get_entity_registry, -) from homeassistant.util import dt from .conftest import KNXTestKit @@ -35,7 +33,7 @@ async def test_binary_sensor_entity_category(hass: HomeAssistant, knx: KNXTestKi await knx.assert_read("1/1/1") await knx.receive_response("1/1/1", True) - registry = await async_get_entity_registry(hass) + registry = er.async_get(hass) entity = registry.async_get("binary_sensor.test_normal") assert entity.entity_category is EntityCategory.DIAGNOSTIC diff --git a/tests/components/knx/test_scene.py b/tests/components/knx/test_scene.py index 37e4ac12728..a7de98abb20 100644 --- a/tests/components/knx/test_scene.py +++ b/tests/components/knx/test_scene.py @@ -4,10 +4,8 @@ from homeassistant.components.knx.const import KNX_ADDRESS from homeassistant.components.knx.schema import SceneSchema from homeassistant.const import CONF_ENTITY_CATEGORY, CONF_NAME from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory -from homeassistant.helpers.entity_registry import ( - async_get_registry as async_get_entity_registry, -) from .conftest import KNXTestKit @@ -28,7 +26,7 @@ async def test_activate_knx_scene(hass: HomeAssistant, knx: KNXTestKit): ) assert len(hass.states.async_all()) == 1 - registry = await async_get_entity_registry(hass) + registry = er.async_get(hass) entity = registry.async_get("scene.test") assert entity.entity_category is EntityCategory.DIAGNOSTIC assert entity.unique_id == "1/1/1_24" diff --git a/tests/components/mikrotik/test_init.py b/tests/components/mikrotik/test_init.py index 30fa1a0a89f..bc00602789c 100644 --- a/tests/components/mikrotik/test_init.py +++ b/tests/components/mikrotik/test_init.py @@ -76,7 +76,7 @@ async def test_unload_entry(hass): entry.add_to_hass(hass) with patch.object(mikrotik, "MikrotikHub") as mock_hub, patch( - "homeassistant.helpers.device_registry.async_get_registry", + "homeassistant.helpers.device_registry.async_get", return_value=Mock(), ): mock_hub.return_value.async_setup = AsyncMock(return_value=True) diff --git a/tests/components/motioneye/test_camera.py b/tests/components/motioneye/test_camera.py index 15462f6c592..8ba9fb07715 100644 --- a/tests/components/motioneye/test_camera.py +++ b/tests/components/motioneye/test_camera.py @@ -42,7 +42,6 @@ from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er -from homeassistant.helpers.device_registry import async_get_registry import homeassistant.util.dt as dt_util from . import ( @@ -138,8 +137,8 @@ async def test_setup_camera_new_data_same(hass: HomeAssistant) -> None: async def test_setup_camera_new_data_camera_removed(hass: HomeAssistant) -> None: """Test a data refresh with a removed camera.""" - device_registry = await async_get_registry(hass) - entity_registry = await er.async_get_registry(hass) + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) client = create_mock_motioneye_client() config_entry = await setup_mock_motioneye_config_entry(hass, client=client) @@ -328,7 +327,7 @@ async def test_device_info(hass: HomeAssistant) -> None: assert device.model == MOTIONEYE_MANUFACTURER assert device.name == TEST_CAMERA_NAME - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/motioneye/test_media_source.py b/tests/components/motioneye/test_media_source.py index 6979d5c645d..9b86b783d43 100644 --- a/tests/components/motioneye/test_media_source.py +++ b/tests/components/motioneye/test_media_source.py @@ -80,7 +80,7 @@ async def test_async_browse_media_success(hass: HomeAssistant) -> None: client = create_mock_motioneye_client() config = await setup_mock_motioneye_config_entry(hass, client=client) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=config.entry_id, identifiers={TEST_CAMERA_DEVICE_IDENTIFIER}, @@ -301,7 +301,7 @@ async def test_async_browse_media_images_success(hass: HomeAssistant) -> None: client = create_mock_motioneye_client() config = await setup_mock_motioneye_config_entry(hass, client=client) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=config.entry_id, identifiers={TEST_CAMERA_DEVICE_IDENTIFIER}, @@ -353,7 +353,7 @@ async def test_async_resolve_media_success(hass: HomeAssistant) -> None: config = await setup_mock_motioneye_config_entry(hass, client=client) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=config.entry_id, identifiers={TEST_CAMERA_DEVICE_IDENTIFIER}, @@ -391,7 +391,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: config = await setup_mock_motioneye_config_entry(hass, client=client) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=config.entry_id, identifiers={TEST_CAMERA_DEVICE_IDENTIFIER}, diff --git a/tests/components/motioneye/test_sensor.py b/tests/components/motioneye/test_sensor.py index 5ab6fc46f49..ea07834976b 100644 --- a/tests/components/motioneye/test_sensor.py +++ b/tests/components/motioneye/test_sensor.py @@ -91,7 +91,7 @@ async def test_sensor_device_info(hass: HomeAssistant) -> None: device = device_registry.async_get_device({device_identifer}) assert device - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/motioneye/test_switch.py b/tests/components/motioneye/test_switch.py index 09db967e5e3..03c39a4b542 100644 --- a/tests/components/motioneye/test_switch.py +++ b/tests/components/motioneye/test_switch.py @@ -196,7 +196,7 @@ async def test_switch_device_info(hass: HomeAssistant) -> None: device = device_registry.async_get_device({device_identifer}) assert device - entity_registry = await er.async_get_registry(hass) + entity_registry = er.async_get(hass) entities_from_device = [ entry.entity_id for entry in er.async_entries_for_device(entity_registry, device.id) diff --git a/tests/components/motioneye/test_web_hooks.py b/tests/components/motioneye/test_web_hooks.py index c7aaa4a8638..442ea9eb782 100644 --- a/tests/components/motioneye/test_web_hooks.py +++ b/tests/components/motioneye/test_web_hooks.py @@ -67,7 +67,7 @@ async def test_setup_camera_without_webhook(hass: HomeAssistant) -> None: client = create_mock_motioneye_client() config_entry = await setup_mock_motioneye_config_entry(hass, client=client) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_device( identifiers={TEST_CAMERA_DEVICE_IDENTIFIER} ) @@ -122,7 +122,7 @@ async def test_setup_camera_with_wrong_webhook( ) await hass.async_block_till_done() - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_device( identifiers={TEST_CAMERA_DEVICE_IDENTIFIER} ) @@ -175,7 +175,7 @@ async def test_setup_camera_with_old_webhook( ) assert client.async_set_camera.called - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_device( identifiers={TEST_CAMERA_DEVICE_IDENTIFIER} ) @@ -211,7 +211,7 @@ async def test_setup_camera_with_correct_webhook( hass, data={CONF_URL: TEST_URL, CONF_WEBHOOK_ID: "webhook_secret_id"} ) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) device = device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, identifiers={TEST_CAMERA_DEVICE_IDENTIFIER}, @@ -281,7 +281,7 @@ async def test_good_query(hass: HomeAssistant, hass_client_no_auth: Any) -> None """Test good callbacks.""" await async_setup_component(hass, "http", {"http": {}}) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) client = create_mock_motioneye_client() config_entry = await setup_mock_motioneye_config_entry(hass, client=client) @@ -378,7 +378,7 @@ async def test_event_media_data(hass: HomeAssistant, hass_client_no_auth: Any) - """Test an event with a file path generates media data.""" await async_setup_component(hass, "http", {"http": {}}) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) client = create_mock_motioneye_client() config_entry = await setup_mock_motioneye_config_entry(hass, client=client) diff --git a/tests/components/ps4/test_init.py b/tests/components/ps4/test_init.py index 8c43bd5df90..a84adba2a70 100644 --- a/tests/components/ps4/test_init.py +++ b/tests/components/ps4/test_init.py @@ -155,7 +155,7 @@ async def test_config_flow_entry_migrate(hass): "homeassistant.util.location.async_detect_location_info", return_value=MOCK_LOCATION, ), patch( - "homeassistant.helpers.entity_registry.async_get_registry", + "homeassistant.helpers.entity_registry.async_get", return_value=mock_e_registry, ): await ps4.async_migrate_entry(hass, mock_entry) diff --git a/tests/components/steam_online/test_init.py b/tests/components/steam_online/test_init.py index 2a015a4ed36..435a5ac6f5a 100644 --- a/tests/components/steam_online/test_init.py +++ b/tests/components/steam_online/test_init.py @@ -41,7 +41,7 @@ async def test_device_info(hass: HomeAssistant) -> None: entry = create_entry(hass) with patch_interface(): await hass.config_entries.async_setup(entry.entry_id) - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) await hass.async_block_till_done() device = device_registry.async_get_device({(DOMAIN, entry.entry_id)}) diff --git a/tests/components/unifiprotect/test_services.py b/tests/components/unifiprotect/test_services.py index 82e4434ad08..0230bc3d36c 100644 --- a/tests/components/unifiprotect/test_services.py +++ b/tests/components/unifiprotect/test_services.py @@ -29,7 +29,7 @@ async def device_fixture(hass: HomeAssistant, mock_entry: MockEntityFixture): await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) return list(device_registry.devices.values())[0] @@ -48,7 +48,7 @@ async def subdevice_fixture( await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - device_registry = await dr.async_get_registry(hass) + device_registry = dr.async_get(hass) return [d for d in device_registry.devices.values() if d.name != "UnifiProtect"][0] diff --git a/tests/components/uptimerobot/test_init.py b/tests/components/uptimerobot/test_init.py index 8efa51e05a6..00e7f5c27e0 100644 --- a/tests/components/uptimerobot/test_init.py +++ b/tests/components/uptimerobot/test_init.py @@ -11,10 +11,7 @@ from homeassistant.components.uptimerobot.const import ( ) from homeassistant.const import STATE_ON, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant -from homeassistant.helpers.device_registry import ( - async_entries_for_config_entry, - async_get_registry, -) +from homeassistant.helpers import device_registry as dr from homeassistant.util import dt from .common import ( @@ -187,9 +184,9 @@ async def test_update_errors(hass: HomeAssistant, caplog: LogCaptureFixture): async def test_device_management(hass: HomeAssistant): """Test that we are adding and removing devices for monitors returned from the API.""" mock_entry = await setup_uptimerobot_integration(hass) - dev_reg = await async_get_registry(hass) + dev_reg = dr.async_get(hass) - devices = async_entries_for_config_entry(dev_reg, mock_entry.entry_id) + devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 1 assert devices[0].identifiers == {(DOMAIN, "1234")} @@ -207,7 +204,7 @@ async def test_device_management(hass: HomeAssistant): async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL) await hass.async_block_till_done() - devices = async_entries_for_config_entry(dev_reg, mock_entry.entry_id) + devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 2 assert devices[0].identifiers == {(DOMAIN, "1234")} assert devices[1].identifiers == {(DOMAIN, "12345")} @@ -224,7 +221,7 @@ async def test_device_management(hass: HomeAssistant): async_fire_time_changed(hass, dt.utcnow() + COORDINATOR_UPDATE_INTERVAL) await hass.async_block_till_done() - devices = async_entries_for_config_entry(dev_reg, mock_entry.entry_id) + devices = dr.async_entries_for_config_entry(dev_reg, mock_entry.entry_id) assert len(devices) == 1 assert devices[0].identifiers == {(DOMAIN, "1234")} diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 1e947a9353f..76cf83e31bf 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -900,7 +900,7 @@ async def test_domain_control_unknown(hass, mock_entities): calls.append(call) with patch( - "homeassistant.helpers.entity_registry.async_get_registry", + "homeassistant.helpers.entity_registry.async_get", return_value=Mock(entities=mock_entities), ): protected_mock_service = service.verify_domain_control(hass, "test_domain")( From 0cea2eba84dbe2044ce59f95e397a1e9b14bc33a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:15:28 +0200 Subject: [PATCH 562/930] Cleanup deprecated async_get_registry in airly (#72061) --- homeassistant/components/airly/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/airly/__init__.py b/homeassistant/components/airly/__init__.py index 83a9a50ec7f..31396ecf51b 100644 --- a/homeassistant/components/airly/__init__.py +++ b/homeassistant/components/airly/__init__.py @@ -15,9 +15,8 @@ from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import async_get_registry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt as dt_util @@ -82,7 +81,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # identifiers in device_info should use tuple[str, str] type, but latitude and # longitude are float, so we convert old device entries to use correct types # We used to use a str 3-tuple here sometime, convert that to a 2-tuple too. - device_registry = await async_get_registry(hass) + device_registry = dr.async_get(hass) old_ids = (DOMAIN, latitude, longitude) for old_ids in ( (DOMAIN, latitude, longitude), @@ -114,7 +113,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.config_entries.async_setup_platforms(entry, PLATFORMS) # Remove air_quality entities from registry if they exist - ent_reg = entity_registry.async_get(hass) + ent_reg = er.async_get(hass) unique_id = f"{coordinator.latitude}-{coordinator.longitude}" if entity_id := ent_reg.async_get_entity_id( AIR_QUALITY_PLATFORM, DOMAIN, unique_id From 1c5541c875513104e162e4fa7e779afc963541a3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:16:04 +0200 Subject: [PATCH 563/930] Cleanup deprecated async_get_registry in august (#72062) --- homeassistant/components/august/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/august/sensor.py b/homeassistant/components/august/sensor.py index 29f17c69661..8a6d169fcb0 100644 --- a/homeassistant/components/august/sensor.py +++ b/homeassistant/components/august/sensor.py @@ -20,9 +20,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ENTITY_PICTURE, PERCENTAGE, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.helpers.restore_state import RestoreEntity from . import AugustData @@ -154,7 +154,7 @@ async def async_setup_entry( async def _async_migrate_old_unique_ids(hass, devices): """Keypads now have their own serial number.""" - registry = await async_get_registry(hass) + registry = er.async_get(hass) for device in devices: old_entity_id = registry.async_get_entity_id( "sensor", DOMAIN, device.old_unique_id From 19fdc3e630514e717d1f89b4fc5ef905aabe2abb Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:18:08 +0200 Subject: [PATCH 564/930] Cleanup deprecated async_get_registry in edl21 (#72063) --- homeassistant/components/edl21/sensor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/edl21/sensor.py b/homeassistant/components/edl21/sensor.py index 7614dc54ac4..f7a79d727a0 100644 --- a/homeassistant/components/edl21/sensor.py +++ b/homeassistant/components/edl21/sensor.py @@ -24,13 +24,12 @@ from homeassistant.const import ( POWER_WATT, ) from homeassistant.core import HomeAssistant, callback -import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util.dt import utcnow @@ -340,7 +339,7 @@ class EDL21: async def add_entities(self, new_entities) -> None: """Migrate old unique IDs, then add entities to hass.""" - registry = await async_get_registry(self._hass) + registry = er.async_get(self._hass) for entity in new_entities: old_entity_id = registry.async_get_entity_id( From eae8a59b7ba2af5008755c8917d3c9095b0f4e03 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:18:43 +0200 Subject: [PATCH 565/930] Cleanup deprecated async_get_registry in gios (#72065) --- homeassistant/components/gios/__init__.py | 7 +++---- homeassistant/components/gios/sensor.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/gios/__init__.py b/homeassistant/components/gios/__init__.py index 579b5ae0ab3..73d773561f5 100644 --- a/homeassistant/components/gios/__init__.py +++ b/homeassistant/components/gios/__init__.py @@ -13,9 +13,8 @@ from homeassistant.components.air_quality import DOMAIN as AIR_QUALITY_PLATFORM from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import async_get_registry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import API_TIMEOUT, CONF_STATION_ID, DOMAIN, SCAN_INTERVAL @@ -35,7 +34,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.config_entries.async_update_entry(entry, unique_id=str(station_id)) # type: ignore[unreachable] # We used to use int in device_entry identifiers, convert this to str. - device_registry = await async_get_registry(hass) + device_registry = dr.async_get(hass) old_ids = (DOMAIN, station_id) device_entry = device_registry.async_get_device({old_ids}) # type: ignore[arg-type] if device_entry and entry.entry_id in device_entry.config_entries: @@ -53,7 +52,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.config_entries.async_setup_platforms(entry, PLATFORMS) # Remove air_quality entities from registry if they exist - ent_reg = entity_registry.async_get(hass) + ent_reg = er.async_get(hass) unique_id = str(coordinator.gios.station_id) if entity_id := ent_reg.async_get_entity_id( AIR_QUALITY_PLATFORM, DOMAIN, unique_id diff --git a/homeassistant/components/gios/sensor.py b/homeassistant/components/gios/sensor.py index c14a99051e4..391976ad793 100644 --- a/homeassistant/components/gios/sensor.py +++ b/homeassistant/components/gios/sensor.py @@ -8,10 +8,10 @@ from homeassistant.components.sensor import DOMAIN as PLATFORM, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION, ATTR_NAME, CONF_NAME from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -43,7 +43,7 @@ async def async_setup_entry( # Due to the change of the attribute name of one sensor, it is necessary to migrate # the unique_id to the new name. - entity_registry = await async_get_registry(hass) + entity_registry = er.async_get(hass) old_unique_id = f"{coordinator.gios.station_id}-pm2.5" if entity_id := entity_registry.async_get_entity_id( PLATFORM, DOMAIN, old_unique_id From ca785266b453a70d75ba528f88c652754e5a1340 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:42:49 +0200 Subject: [PATCH 566/930] Cleanup deprecated async_get_registry in hue (#72068) --- homeassistant/components/hue/v1/helpers.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/hue/v1/helpers.py b/homeassistant/components/hue/v1/helpers.py index d4582f0bb52..b0d774915df 100644 --- a/homeassistant/components/hue/v1/helpers.py +++ b/homeassistant/components/hue/v1/helpers.py @@ -1,7 +1,6 @@ """Helper functions for Philips Hue.""" -from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg -from homeassistant.helpers.entity_registry import async_get_registry as get_ent_reg +from homeassistant.helpers import device_registry as dr, entity_registry as er from ..const import DOMAIN @@ -18,10 +17,10 @@ async def remove_devices(bridge, api_ids, current): entity = current[item_id] removed_items.append(item_id) await entity.async_remove(force_remove=True) - ent_registry = await get_ent_reg(bridge.hass) + ent_registry = er.async_get(bridge.hass) if entity.entity_id in ent_registry.entities: ent_registry.async_remove(entity.entity_id) - dev_registry = await get_dev_reg(bridge.hass) + dev_registry = dr.async_get(bridge.hass) device = dev_registry.async_get_device(identifiers={(DOMAIN, entity.device_id)}) if device is not None: dev_registry.async_update_device( From 4e6887515bf8409d3f4ff621f244abbe6bf922e0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:45:49 +0200 Subject: [PATCH 567/930] Cleanup deprecated async_get_registry in zha (#72080) --- homeassistant/components/zha/core/gateway.py | 23 ++++++-------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 0ba375362f7..642a5b3ec55 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -25,18 +25,9 @@ from homeassistant.components.system_log import LogEntry, _figure_out_source from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.device_registry import ( - CONNECTION_ZIGBEE, - DeviceRegistry, - async_get_registry as get_dev_reg, -) +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.entity_registry import ( - EntityRegistry, - async_entries_for_device, - async_get_registry as get_ent_reg, -) from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType @@ -129,8 +120,8 @@ class ZHAGateway: # -- Set in async_initialize -- zha_storage: ZhaStorage - ha_device_registry: DeviceRegistry - ha_entity_registry: EntityRegistry + ha_device_registry: dr.DeviceRegistry + ha_entity_registry: er.EntityRegistry application_controller: ControllerApplication radio_description: str @@ -161,8 +152,8 @@ class ZHAGateway: discovery.GROUP_PROBE.initialize(self._hass) self.zha_storage = await async_get_registry(self._hass) - self.ha_device_registry = await get_dev_reg(self._hass) - self.ha_entity_registry = await get_ent_reg(self._hass) + self.ha_device_registry = dr.async_get(self._hass) + self.ha_entity_registry = er.async_get(self._hass) radio_type = self.config_entry.data[CONF_RADIO_TYPE] @@ -437,7 +428,7 @@ class ZHAGateway: ] # then we get all group entity entries tied to the coordinator - all_group_entity_entries = async_entries_for_device( + all_group_entity_entries = er.async_entries_for_device( self.ha_entity_registry, self.coordinator_zha_device.device_id, include_disabled_entities=True, @@ -528,7 +519,7 @@ class ZHAGateway: self._devices[zigpy_device.ieee] = zha_device device_registry_device = self.ha_device_registry.async_get_or_create( config_entry_id=self.config_entry.entry_id, - connections={(CONNECTION_ZIGBEE, str(zha_device.ieee))}, + connections={(dr.CONNECTION_ZIGBEE, str(zha_device.ieee))}, identifiers={(DOMAIN, str(zha_device.ieee))}, name=zha_device.name, manufacturer=zha_device.manufacturer, From b9c84bd65576f8c0de7754aca037f733334f0aef Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:47:09 +0200 Subject: [PATCH 568/930] Cleanup deprecated async_get_registry in opentherm_gw (#72070) --- homeassistant/components/opentherm_gw/__init__.py | 7 ++----- homeassistant/components/opentherm_gw/binary_sensor.py | 4 ++-- homeassistant/components/opentherm_gw/sensor.py | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 1ea24ae9709..5ec8c6420d5 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -23,10 +23,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, ServiceCall -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import ( - async_get_registry as async_get_dev_reg, -) +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import ConfigType @@ -436,7 +433,7 @@ class OpenThermGatewayDevice: _LOGGER.debug( "Connected to OpenTherm Gateway %s at %s", self.gw_version, self.device_path ) - dev_reg = await async_get_dev_reg(self.hass) + dev_reg = dr.async_get(self.hass) gw_dev = dev_reg.async_get_or_create( config_entry_id=self.config_entry_id, identifiers={(DOMAIN, self.gw_id)}, diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index f630cdf273b..e4880ed26e9 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -6,10 +6,10 @@ from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT, BinarySenso from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ID from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, async_generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from . import DOMAIN from .const import ( @@ -32,7 +32,7 @@ async def async_setup_entry( sensors = [] deprecated_sensors = [] gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]] - ent_reg = await async_get_registry(hass) + ent_reg = er.async_get(hass) for var, info in BINARY_SENSOR_INFO.items(): device_class = info[0] friendly_name_format = info[1] diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index 9b2e6c45899..7fb518b0e6d 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -6,10 +6,10 @@ from homeassistant.components.sensor import ENTITY_ID_FORMAT, SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ID from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, async_generate_entity_id from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from . import DOMAIN from .const import ( @@ -32,7 +32,7 @@ async def async_setup_entry( sensors = [] deprecated_sensors = [] gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]] - ent_reg = await async_get_registry(hass) + ent_reg = er.async_get(hass) for var, info in SENSOR_INFO.items(): device_class = info[0] unit = info[1] From 9c4a046a2bccec3c3d9f42183c5e3b8c34b90a50 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 13:53:57 +0200 Subject: [PATCH 569/930] Cleanup deprecated async_get_registry in sense (#72074) --- homeassistant/components/sense/binary_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sense/binary_sensor.py b/homeassistant/components/sense/binary_sensor.py index aa895da166f..10399fa8e2b 100644 --- a/homeassistant/components/sense/binary_sensor.py +++ b/homeassistant/components/sense/binary_sensor.py @@ -8,9 +8,9 @@ from homeassistant.components.binary_sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from .const import ( ATTRIBUTION, @@ -50,7 +50,7 @@ async def async_setup_entry( async def _migrate_old_unique_ids(hass, devices): - registry = await async_get_registry(hass) + registry = er.async_get(hass) for device in devices: # Migration of old not so unique ids old_entity_id = registry.async_get_entity_id( From df13fcd1a53d35acae9ec412ab087dcf199d27d4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:09:04 +0200 Subject: [PATCH 570/930] Cleanup deprecated async_get_registry in synology_dsm (#72075) --- homeassistant/components/synology_dsm/__init__.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index 1151bf128cc..8dbdecb9305 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -22,12 +22,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import device_registry -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import ( - DeviceEntry, - async_get_registry as get_dev_reg, -) +from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .common import SynoApi @@ -57,8 +52,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Synology DSM sensors.""" # Migrate device indentifiers - dev_reg = await get_dev_reg(hass) - devices: list[DeviceEntry] = device_registry.async_entries_for_config_entry( + dev_reg = dr.async_get(hass) + devices: list[dr.DeviceEntry] = dr.async_entries_for_config_entry( dev_reg, entry.entry_id ) for device in devices: From 64b0dd1e7aed807b81869d85765689540e23ff3b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:09:25 +0200 Subject: [PATCH 571/930] Cleanup deprecated async_get_registry in plex (#72077) --- homeassistant/components/plex/media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index a9f688d9b13..ce76c4be3ff 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -20,6 +20,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, @@ -27,7 +28,6 @@ from homeassistant.helpers.dispatcher import ( ) from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.helpers.network import is_internal_request from .const import ( @@ -76,7 +76,7 @@ async def async_setup_entry( ) -> None: """Set up Plex media_player from a config entry.""" server_id = config_entry.data[CONF_SERVER_IDENTIFIER] - registry = await async_get_registry(hass) + registry = er.async_get(hass) @callback def async_new_media_players(new_entities): From 70aa71eeed3140a2d9b32074b7997fb7c8fe478b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:09:43 +0200 Subject: [PATCH 572/930] Cleanup deprecated async_get_registry in isy994 (#72078) --- homeassistant/components/isy994/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/isy994/helpers.py b/homeassistant/components/isy994/helpers.py index 7efbb3bade7..3b0de172a85 100644 --- a/homeassistant/components/isy994/helpers.py +++ b/homeassistant/components/isy994/helpers.py @@ -24,7 +24,7 @@ from homeassistant.components.sensor import DOMAIN as SENSOR from homeassistant.components.switch import DOMAIN as SWITCH from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_registry import async_get_registry +from homeassistant.helpers import entity_registry as er from .const import ( _LOGGER, @@ -383,7 +383,7 @@ async def migrate_old_unique_ids( hass: HomeAssistant, platform: str, entities: Sequence[ISYEntity] ) -> None: """Migrate to new controller-specific unique ids.""" - registry = await async_get_registry(hass) + registry = er.async_get(hass) for entity in entities: if entity.old_unique_id is None or entity.unique_id is None: From 4978d5f86e266764de4101c8a819374662c6cfb4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:21:56 +0200 Subject: [PATCH 573/930] Cleanup deprecated async_get_registry in homekit_controller (#72069) --- .../components/homekit_controller/config_flow.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index c3bdfcc42ae..b4bd66aa626 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -14,10 +14,7 @@ from homeassistant import config_entries from homeassistant.components import zeroconf from homeassistant.core import callback from homeassistant.data_entry_flow import AbortFlow, FlowResult -from homeassistant.helpers.device_registry import ( - CONNECTION_NETWORK_MAC, - async_get_registry as async_get_device_registry, -) +from homeassistant.helpers import device_registry as dr from .const import DOMAIN, KNOWN_DEVICES from .utils import async_get_controller @@ -163,9 +160,9 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def _hkid_is_homekit(self, hkid): """Determine if the device is a homekit bridge or accessory.""" - dev_reg = await async_get_device_registry(self.hass) + dev_reg = dr.async_get(self.hass) device = dev_reg.async_get_device( - identifiers=set(), connections={(CONNECTION_NETWORK_MAC, hkid)} + identifiers=set(), connections={(dr.CONNECTION_NETWORK_MAC, hkid)} ) if device is None: From 977c5b7693697ffe6d685f3370b2cb4f78c3d8ff Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:22:38 +0200 Subject: [PATCH 574/930] Cleanup deprecated async_get_registry in gdacs (#72066) --- homeassistant/components/gdacs/geo_location.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/gdacs/geo_location.py b/homeassistant/components/gdacs/geo_location.py index 2b6ffd556a9..5bfbd915b6c 100644 --- a/homeassistant/components/gdacs/geo_location.py +++ b/homeassistant/components/gdacs/geo_location.py @@ -12,9 +12,9 @@ from homeassistant.const import ( LENGTH_MILES, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.util.unit_system import IMPERIAL_SYSTEM from .const import DEFAULT_ICON, DOMAIN, FEED @@ -114,7 +114,7 @@ class GdacsEvent(GeolocationEvent): self._remove_signal_delete() self._remove_signal_update() # Remove from entity registry. - entity_registry = await async_get_registry(self.hass) + entity_registry = er.async_get(self.hass) if self.entity_id in entity_registry.entities: entity_registry.async_remove(self.entity_id) From 7c4cc389c9ea94bae7b477303defc6f5ea38fa32 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:22:56 +0200 Subject: [PATCH 575/930] Cleanup deprecated async_get_registry in geonetnz_quakes (#72064) --- homeassistant/components/geonetnz_quakes/geo_location.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/geonetnz_quakes/geo_location.py b/homeassistant/components/geonetnz_quakes/geo_location.py index 860a80f99c5..515fce56439 100644 --- a/homeassistant/components/geonetnz_quakes/geo_location.py +++ b/homeassistant/components/geonetnz_quakes/geo_location.py @@ -13,9 +13,9 @@ from homeassistant.const import ( LENGTH_MILES, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.entity_registry import async_get_registry from homeassistant.util.unit_system import IMPERIAL_SYSTEM from .const import DOMAIN, FEED @@ -100,7 +100,7 @@ class GeonetnzQuakesEvent(GeolocationEvent): self._remove_signal_delete() self._remove_signal_update() # Remove from entity registry. - entity_registry = await async_get_registry(self.hass) + entity_registry = er.async_get(self.hass) if self.entity_id in entity_registry.entities: entity_registry.async_remove(self.entity_id) From 619a92eab9cc5564abe3f1e53c543ad7074fda13 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 14:23:49 +0200 Subject: [PATCH 576/930] Cleanup deprecated async_get_registry in fronius (#72067) --- homeassistant/components/fronius/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/fronius/__init__.py b/homeassistant/components/fronius/__init__.py index 03340f19081..f6607aed11f 100644 --- a/homeassistant/components/fronius/__init__.py +++ b/homeassistant/components/fronius/__init__.py @@ -161,7 +161,7 @@ class FroniusSolarNet: "value" ] - device_registry = await dr.async_get_registry(self.hass) + device_registry = dr.async_get(self.hass) device_registry.async_get_or_create( config_entry_id=self.config_entry.entry_id, **solar_net_device, From 23cb5cfd3f85cd1f480c3aa8e548fba6e38764a7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 19 May 2022 00:27:02 +1200 Subject: [PATCH 577/930] Bump aioesphomeapi to 10.10.0 (#72083) --- homeassistant/components/esphome/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/esphome/manifest.json b/homeassistant/components/esphome/manifest.json index 50334808dbf..b89671c6f90 100644 --- a/homeassistant/components/esphome/manifest.json +++ b/homeassistant/components/esphome/manifest.json @@ -3,7 +3,7 @@ "name": "ESPHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/esphome", - "requirements": ["aioesphomeapi==10.9.0"], + "requirements": ["aioesphomeapi==10.10.0"], "zeroconf": ["_esphomelib._tcp.local."], "codeowners": ["@OttoWinter", "@jesserockz"], "after_dependencies": ["zeroconf", "tag"], diff --git a/requirements_all.txt b/requirements_all.txt index ad732ab2034..94ab58682ff 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -144,7 +144,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.9.0 +aioesphomeapi==10.10.0 # homeassistant.components.flo aioflo==2021.11.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 02bf3ea39b0..3adafb068de 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -131,7 +131,7 @@ aioeagle==1.1.0 aioemonitor==1.0.5 # homeassistant.components.esphome -aioesphomeapi==10.9.0 +aioesphomeapi==10.10.0 # homeassistant.components.flo aioflo==2021.11.0 From 26ee289be3824157cda155c53e06754b8446374c Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 18 May 2022 06:30:57 -0700 Subject: [PATCH 578/930] Add return code to integration application credentials in config flow (#71986) * Add return code to integration application credentials in config flow * Update google tests to use new return code * Update spotify test for no auth configured * Add translation for oauth2_missing_credentials * Add new return code to yolink * Update homeassistant/strings.json Co-authored-by: Martin Hjelmare Co-authored-by: Franck Nijhof Co-authored-by: Martin Hjelmare --- homeassistant/helpers/config_entry_oauth2_flow.py | 3 +++ homeassistant/strings.json | 1 + tests/components/google/test_config_flow.py | 4 ++-- tests/components/spotify/test_config_flow.py | 4 ++-- tests/components/yolink/test_config_flow.py | 2 +- tests/helpers/test_config_entry_oauth2_flow.py | 11 +++++++++++ 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index d0aaca71304..2d45269a82c 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -25,6 +25,7 @@ from homeassistant import config_entries from homeassistant.components import http from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.loader import async_get_application_credentials from .aiohttp_client import async_get_clientsession from .network import NoURLAvailableError @@ -239,6 +240,8 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta): return await self.async_step_auth() if not implementations: + if self.DOMAIN in await async_get_application_credentials(self.hass): + return self.async_abort(reason="missing_credentials") return self.async_abort(reason="missing_configuration") req = http.current_request.get() diff --git a/homeassistant/strings.json b/homeassistant/strings.json index 9dcf8c7fe49..e4d363c22be 100644 --- a/homeassistant/strings.json +++ b/homeassistant/strings.json @@ -71,6 +71,7 @@ "webhook_not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive webhook messages.", "oauth2_error": "Received invalid token data.", "oauth2_missing_configuration": "The component is not configured. Please follow the documentation.", + "oauth2_missing_credentials": "The integration requires application credentials.", "oauth2_authorize_url_timeout": "Timeout generating authorize URL.", "oauth2_no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", "reauth_successful": "Re-authentication was successful", diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index f061aeb3057..9339ca9988f 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -327,7 +327,7 @@ async def test_missing_configuration( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") == "abort" - assert result.get("reason") == "missing_configuration" + assert result.get("reason") == "missing_credentials" @pytest.mark.parametrize("google_config", [None]) @@ -342,7 +342,7 @@ async def test_missing_configuration_yaml_empty( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") == "abort" - assert result.get("reason") == "missing_configuration" + assert result.get("reason") == "missing_credentials" async def test_wrong_configuration( diff --git a/tests/components/spotify/test_config_flow.py b/tests/components/spotify/test_config_flow.py index e2f1878c04d..3b1e4851ff1 100644 --- a/tests/components/spotify/test_config_flow.py +++ b/tests/components/spotify/test_config_flow.py @@ -31,14 +31,14 @@ async def test_abort_if_no_configuration(hass): ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "missing_configuration" + assert result["reason"] == "missing_credentials" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=BLANK_ZEROCONF_INFO ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "missing_configuration" + assert result["reason"] == "missing_credentials" async def test_zeroconf_abort_if_existing_entry(hass): diff --git a/tests/components/yolink/test_config_flow.py b/tests/components/yolink/test_config_flow.py index 4dd347f4076..5d6bb8fd727 100644 --- a/tests/components/yolink/test_config_flow.py +++ b/tests/components/yolink/test_config_flow.py @@ -25,7 +25,7 @@ async def test_abort_if_no_configuration(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "missing_configuration" + assert result["reason"] == "missing_credentials" async def test_abort_if_existing_entry(hass: HomeAssistant): diff --git a/tests/helpers/test_config_entry_oauth2_flow.py b/tests/helpers/test_config_entry_oauth2_flow.py index 97e728d022d..248f3b8dbb0 100644 --- a/tests/helpers/test_config_entry_oauth2_flow.py +++ b/tests/helpers/test_config_entry_oauth2_flow.py @@ -114,6 +114,17 @@ async def test_abort_if_no_implementation(hass, flow_handler): assert result["reason"] == "missing_configuration" +async def test_missing_credentials_for_domain(hass, flow_handler): + """Check flow abort for integration supporting application credentials.""" + flow = flow_handler() + flow.hass = hass + + with patch("homeassistant.loader.APPLICATION_CREDENTIALS", [TEST_DOMAIN]): + result = await flow.async_step_user() + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "missing_credentials" + + async def test_abort_if_authorization_timeout( hass, flow_handler, local_impl, current_request_with_host ): From f3c582815c21fd76ae1e85baa4d5d870d5cf2191 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 09:22:21 -0500 Subject: [PATCH 579/930] Convert statistics to use lambda_stmt (#71903) * Convert stats to use lambda_stmt - Since baked queries are now [deprecated in 1.4](https://docs.sqlalchemy.org/en/14/orm/extensions/baked.html#module-sqlalchemy.ext.baked) the next step is to convert these to `lambda_stmt` https://docs.sqlalchemy.org/en/14/core/connections.html#quick-guidelines-for-lambdas * Update homeassistant/components/recorder/statistics.py Co-authored-by: Erik Montnemery --- .../components/recorder/statistics.py | 277 +++++++++--------- homeassistant/components/recorder/util.py | 5 +- 2 files changed, 143 insertions(+), 139 deletions(-) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 732c042d9c2..77f56bc59fa 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -14,11 +14,12 @@ import re from statistics import mean from typing import TYPE_CHECKING, Any, Literal, overload -from sqlalchemy import bindparam, func +from sqlalchemy import bindparam, func, lambda_stmt, select +from sqlalchemy.engine.row import Row from sqlalchemy.exc import SQLAlchemyError, StatementError -from sqlalchemy.ext import baked from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import literal_column, true +from sqlalchemy.sql.lambdas import StatementLambdaElement import voluptuous as vol from homeassistant.const import ( @@ -50,7 +51,12 @@ from .models import ( process_timestamp, process_timestamp_to_utc_isoformat, ) -from .util import execute, retryable_database_job, session_scope +from .util import ( + execute, + execute_stmt_lambda_element, + retryable_database_job, + session_scope, +) if TYPE_CHECKING: from . import Recorder @@ -120,8 +126,6 @@ QUERY_STATISTIC_META_ID = [ StatisticsMeta.statistic_id, ] -STATISTICS_BAKERY = "recorder_statistics_bakery" - # Convert pressure, temperature and volume statistics from the normalized unit used for # statistics to the unit configured by the user @@ -203,7 +207,6 @@ class ValidationIssue: def async_setup(hass: HomeAssistant) -> None: """Set up the history hooks.""" - hass.data[STATISTICS_BAKERY] = baked.bakery() def _entity_id_changed(event: Event) -> None: """Handle entity_id changed.""" @@ -420,6 +423,36 @@ def delete_duplicates(hass: HomeAssistant, session: Session) -> None: ) +def _compile_hourly_statistics_summary_mean_stmt( + start_time: datetime, end_time: datetime +) -> StatementLambdaElement: + """Generate the summary mean statement for hourly statistics.""" + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SUMMARY_MEAN)) + stmt += ( + lambda q: q.filter(StatisticsShortTerm.start >= start_time) + .filter(StatisticsShortTerm.start < end_time) + .group_by(StatisticsShortTerm.metadata_id) + .order_by(StatisticsShortTerm.metadata_id) + ) + return stmt + + +def _compile_hourly_statistics_summary_sum_legacy_stmt( + start_time: datetime, end_time: datetime +) -> StatementLambdaElement: + """Generate the legacy sum statement for hourly statistics. + + This is used for databases not supporting row number. + """ + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SUMMARY_SUM_LEGACY)) + stmt += ( + lambda q: q.filter(StatisticsShortTerm.start >= start_time) + .filter(StatisticsShortTerm.start < end_time) + .order_by(StatisticsShortTerm.metadata_id, StatisticsShortTerm.start.desc()) + ) + return stmt + + def compile_hourly_statistics( instance: Recorder, session: Session, start: datetime ) -> None: @@ -434,20 +467,8 @@ def compile_hourly_statistics( # Compute last hour's average, min, max summary: dict[str, StatisticData] = {} - baked_query = instance.hass.data[STATISTICS_BAKERY]( - lambda session: session.query(*QUERY_STATISTICS_SUMMARY_MEAN) - ) - - baked_query += lambda q: q.filter( - StatisticsShortTerm.start >= bindparam("start_time") - ) - baked_query += lambda q: q.filter(StatisticsShortTerm.start < bindparam("end_time")) - baked_query += lambda q: q.group_by(StatisticsShortTerm.metadata_id) - baked_query += lambda q: q.order_by(StatisticsShortTerm.metadata_id) - - stats = execute( - baked_query(session).params(start_time=start_time, end_time=end_time) - ) + stmt = _compile_hourly_statistics_summary_mean_stmt(start_time, end_time) + stats = execute_stmt_lambda_element(session, stmt) if stats: for stat in stats: @@ -493,23 +514,8 @@ def compile_hourly_statistics( "sum": _sum, } else: - baked_query = instance.hass.data[STATISTICS_BAKERY]( - lambda session: session.query(*QUERY_STATISTICS_SUMMARY_SUM_LEGACY) - ) - - baked_query += lambda q: q.filter( - StatisticsShortTerm.start >= bindparam("start_time") - ) - baked_query += lambda q: q.filter( - StatisticsShortTerm.start < bindparam("end_time") - ) - baked_query += lambda q: q.order_by( - StatisticsShortTerm.metadata_id, StatisticsShortTerm.start.desc() - ) - - stats = execute( - baked_query(session).params(start_time=start_time, end_time=end_time) - ) + stmt = _compile_hourly_statistics_summary_sum_legacy_stmt(start_time, end_time) + stats = execute_stmt_lambda_element(session, stmt) if stats: for metadata_id, group in groupby(stats, lambda stat: stat["metadata_id"]): # type: ignore[no-any-return] @@ -669,6 +675,24 @@ def _update_statistics( ) +def _generate_get_metadata_stmt( + statistic_ids: list[str] | tuple[str] | None = None, + statistic_type: Literal["mean"] | Literal["sum"] | None = None, + statistic_source: str | None = None, +) -> StatementLambdaElement: + """Generate a statement to fetch metadata.""" + stmt = lambda_stmt(lambda: select(*QUERY_STATISTIC_META)) + if statistic_ids is not None: + stmt += lambda q: q.where(StatisticsMeta.statistic_id.in_(statistic_ids)) + if statistic_source is not None: + stmt += lambda q: q.where(StatisticsMeta.source == statistic_source) + if statistic_type == "mean": + stmt += lambda q: q.where(StatisticsMeta.has_mean == true()) + elif statistic_type == "sum": + stmt += lambda q: q.where(StatisticsMeta.has_sum == true()) + return stmt + + def get_metadata_with_session( hass: HomeAssistant, session: Session, @@ -686,26 +710,8 @@ def get_metadata_with_session( """ # Fetch metatadata from the database - baked_query = hass.data[STATISTICS_BAKERY]( - lambda session: session.query(*QUERY_STATISTIC_META) - ) - if statistic_ids is not None: - baked_query += lambda q: q.filter( - StatisticsMeta.statistic_id.in_(bindparam("statistic_ids")) - ) - if statistic_source is not None: - baked_query += lambda q: q.filter( - StatisticsMeta.source == bindparam("statistic_source") - ) - if statistic_type == "mean": - baked_query += lambda q: q.filter(StatisticsMeta.has_mean == true()) - elif statistic_type == "sum": - baked_query += lambda q: q.filter(StatisticsMeta.has_sum == true()) - result = execute( - baked_query(session).params( - statistic_ids=statistic_ids, statistic_source=statistic_source - ) - ) + stmt = _generate_get_metadata_stmt(statistic_ids, statistic_type, statistic_source) + result = execute_stmt_lambda_element(session, stmt) if not result: return {} @@ -852,31 +858,6 @@ def list_statistic_ids( ] -def _statistics_during_period_query( - hass: HomeAssistant, - end_time: datetime | None, - statistic_ids: list[str] | None, - baked_query: baked.BakedQuery, - table: type[Statistics | StatisticsShortTerm], -) -> Callable: - """Prepare a database query for statistics during a given period. - - This prepares a baked query, so we don't insert the parameters yet. - """ - baked_query += lambda q: q.filter(table.start >= bindparam("start_time")) - - if end_time is not None: - baked_query += lambda q: q.filter(table.start < bindparam("end_time")) - - if statistic_ids is not None: - baked_query += lambda q: q.filter( - table.metadata_id.in_(bindparam("metadata_ids")) - ) - - baked_query += lambda q: q.order_by(table.metadata_id, table.start) - return baked_query # type: ignore[no-any-return] - - def _reduce_statistics( stats: dict[str, list[dict[str, Any]]], same_period: Callable[[datetime, datetime], bool], @@ -975,6 +956,34 @@ def _reduce_statistics_per_month( return _reduce_statistics(stats, same_month, month_start_end, timedelta(days=31)) +def _statistics_during_period_stmt( + start_time: datetime, + end_time: datetime | None, + statistic_ids: list[str] | None, + metadata_ids: list[int] | None, + table: type[Statistics | StatisticsShortTerm], +) -> StatementLambdaElement: + """Prepare a database query for statistics during a given period. + + This prepares a lambda_stmt query, so we don't insert the parameters yet. + """ + if table == StatisticsShortTerm: + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SHORT_TERM)) + else: + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS)) + + stmt += lambda q: q.filter(table.start >= start_time) + + if end_time is not None: + stmt += lambda q: q.filter(table.start < end_time) + + if statistic_ids is not None: + stmt += lambda q: q.filter(table.metadata_id.in_(metadata_ids)) + + stmt += lambda q: q.order_by(table.metadata_id, table.start) + return stmt + + def statistics_during_period( hass: HomeAssistant, start_time: datetime, @@ -999,25 +1008,16 @@ def statistics_during_period( if statistic_ids is not None: metadata_ids = [metadata_id for metadata_id, _ in metadata.values()] - bakery = hass.data[STATISTICS_BAKERY] if period == "5minute": - baked_query = bakery( - lambda session: session.query(*QUERY_STATISTICS_SHORT_TERM) - ) table = StatisticsShortTerm else: - baked_query = bakery(lambda session: session.query(*QUERY_STATISTICS)) table = Statistics - baked_query = _statistics_during_period_query( - hass, end_time, statistic_ids, baked_query, table + stmt = _statistics_during_period_stmt( + start_time, end_time, statistic_ids, metadata_ids, table ) + stats = execute_stmt_lambda_element(session, stmt) - stats = execute( - baked_query(session).params( - start_time=start_time, end_time=end_time, metadata_ids=metadata_ids - ) - ) if not stats: return {} # Return statistics combined with metadata @@ -1044,6 +1044,24 @@ def statistics_during_period( return _reduce_statistics_per_month(result) +def _get_last_statistics_stmt( + metadata_id: int, + number_of_stats: int, + table: type[Statistics | StatisticsShortTerm], +) -> StatementLambdaElement: + """Generate a statement for number_of_stats statistics for a given statistic_id.""" + if table == StatisticsShortTerm: + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SHORT_TERM)) + else: + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS)) + stmt += ( + lambda q: q.filter_by(metadata_id=metadata_id) + .order_by(table.metadata_id, table.start.desc()) + .limit(number_of_stats) + ) + return stmt + + def _get_last_statistics( hass: HomeAssistant, number_of_stats: int, @@ -1058,27 +1076,10 @@ def _get_last_statistics( metadata = get_metadata_with_session(hass, session, statistic_ids=statistic_ids) if not metadata: return {} - - bakery = hass.data[STATISTICS_BAKERY] - if table == StatisticsShortTerm: - baked_query = bakery( - lambda session: session.query(*QUERY_STATISTICS_SHORT_TERM) - ) - else: - baked_query = bakery(lambda session: session.query(*QUERY_STATISTICS)) - - baked_query += lambda q: q.filter_by(metadata_id=bindparam("metadata_id")) metadata_id = metadata[statistic_id][0] + stmt = _get_last_statistics_stmt(metadata_id, number_of_stats, table) + stats = execute_stmt_lambda_element(session, stmt) - baked_query += lambda q: q.order_by(table.metadata_id, table.start.desc()) - - baked_query += lambda q: q.limit(bindparam("number_of_stats")) - - stats = execute( - baked_query(session).params( - number_of_stats=number_of_stats, metadata_id=metadata_id - ) - ) if not stats: return {} @@ -1113,14 +1114,36 @@ def get_last_short_term_statistics( ) +def _latest_short_term_statistics_stmt( + metadata_ids: list[int], +) -> StatementLambdaElement: + """Create the statement for finding the latest short term stat rows.""" + stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SHORT_TERM)) + most_recent_statistic_row = ( + select( + StatisticsShortTerm.metadata_id, + func.max(StatisticsShortTerm.start).label("start_max"), + ) + .where(StatisticsShortTerm.metadata_id.in_(metadata_ids)) + .group_by(StatisticsShortTerm.metadata_id) + ).subquery() + stmt += lambda s: s.join( + most_recent_statistic_row, + ( + StatisticsShortTerm.metadata_id # pylint: disable=comparison-with-callable + == most_recent_statistic_row.c.metadata_id + ) + & (StatisticsShortTerm.start == most_recent_statistic_row.c.start_max), + ) + return stmt + + def get_latest_short_term_statistics( hass: HomeAssistant, statistic_ids: list[str], metadata: dict[str, tuple[int, StatisticMetaData]] | None = None, ) -> dict[str, list[dict]]: """Return the latest short term statistics for a list of statistic_ids.""" - # This function doesn't use a baked query, we instead rely on the - # "Transparent SQL Compilation Caching" feature introduced in SQLAlchemy 1.4 with session_scope(hass=hass) as session: # Fetch metadata for the given statistic_ids if not metadata: @@ -1134,24 +1157,8 @@ def get_latest_short_term_statistics( for statistic_id in statistic_ids if statistic_id in metadata ] - most_recent_statistic_row = ( - session.query( - StatisticsShortTerm.metadata_id, - func.max(StatisticsShortTerm.start).label("start_max"), - ) - .filter(StatisticsShortTerm.metadata_id.in_(metadata_ids)) - .group_by(StatisticsShortTerm.metadata_id) - ).subquery() - stats = execute( - session.query(*QUERY_STATISTICS_SHORT_TERM).join( - most_recent_statistic_row, - ( - StatisticsShortTerm.metadata_id # pylint: disable=comparison-with-callable - == most_recent_statistic_row.c.metadata_id - ) - & (StatisticsShortTerm.start == most_recent_statistic_row.c.start_max), - ) - ) + stmt = _latest_short_term_statistics_stmt(metadata_ids) + stats = execute_stmt_lambda_element(session, stmt) if not stats: return {} @@ -1203,7 +1210,7 @@ def _statistics_at_time( def _sorted_statistics_to_dict( hass: HomeAssistant, session: Session, - stats: list, + stats: Iterable[Row], statistic_ids: list[str] | None, _metadata: dict[str, tuple[int, StatisticMetaData]], convert_units: bool, @@ -1215,7 +1222,7 @@ def _sorted_statistics_to_dict( result: dict = defaultdict(list) units = hass.config.units metadata = dict(_metadata.values()) - need_stat_at_start_time = set() + need_stat_at_start_time: set[int] = set() stats_at_start_time = {} def no_conversion(val: Any, _: Any) -> float | None: diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index e464ac4126b..f086119f7f9 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -20,7 +20,6 @@ from sqlalchemy import text from sqlalchemy.engine.cursor import CursorFetchStrategy from sqlalchemy.engine.row import Row from sqlalchemy.exc import OperationalError, SQLAlchemyError -from sqlalchemy.ext.baked import Result from sqlalchemy.orm.query import Query from sqlalchemy.orm.session import Session from sqlalchemy.sql.lambdas import StatementLambdaElement @@ -123,9 +122,7 @@ def commit(session: Session, work: Any) -> bool: def execute( - qry: Query | Result, - to_native: bool = False, - validate_entity_ids: bool = True, + qry: Query, to_native: bool = False, validate_entity_ids: bool = True ) -> list[Row]: """Query the database and convert the objects to HA native form. From 037f6947d88f0754b15d156180cdffb053a25b1a Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 18 May 2022 16:52:46 +0200 Subject: [PATCH 580/930] Fail recorder setup with unsupported dialect or version (#70888) --- homeassistant/components/recorder/core.py | 3 ++ homeassistant/components/recorder/models.py | 4 ++ homeassistant/components/recorder/util.py | 32 ++++++++-------- tests/components/recorder/test_util.py | 42 ++++++++++++--------- 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 91dc0a27ead..5a3a41568a1 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -56,6 +56,7 @@ from .models import ( StatisticData, StatisticMetaData, StatisticsRuns, + UnsupportedDialect, process_timestamp, ) from .pool import POOL_SIZE, MutexPool, RecorderPool @@ -606,6 +607,8 @@ class Recorder(threading.Thread): try: self._setup_connection() return migration.get_schema_version(self.get_session) + except UnsupportedDialect: + break except Exception as err: # pylint: disable=broad-except _LOGGER.exception( "Error during connection setup: %s (retrying in %s seconds)", diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 9d16541e398..4dabd7899e0 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -123,6 +123,10 @@ EVENT_ORIGIN_ORDER = [EventOrigin.local, EventOrigin.remote] EVENT_ORIGIN_TO_IDX = {origin: idx for idx, origin in enumerate(EVENT_ORIGIN_ORDER)} +class UnsupportedDialect(Exception): + """The dialect or its version is not supported.""" + + class Events(Base): # type: ignore[misc,valid-type] """Event history data.""" diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index f086119f7f9..ce8812a653e 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -34,6 +34,7 @@ from .models import ( TABLE_SCHEMA_CHANGES, TABLES_TO_CHECK, RecorderRuns, + UnsupportedDialect, process_timestamp, ) @@ -328,29 +329,31 @@ def query_on_connection(dbapi_connection: Any, statement: str) -> Any: return result -def _warn_unsupported_dialect(dialect_name: str) -> None: +def _fail_unsupported_dialect(dialect_name: str) -> None: """Warn about unsupported database version.""" - _LOGGER.warning( + _LOGGER.error( "Database %s is not supported; Home Assistant supports %s. " - "Starting with Home Assistant 2022.2 this will prevent the recorder from " - "starting. Please migrate your database to a supported software before then", + "Starting with Home Assistant 2022.6 this prevents the recorder from " + "starting. Please migrate your database to a supported software", dialect_name, "MariaDB ≥ 10.3, MySQL ≥ 8.0, PostgreSQL ≥ 12, SQLite ≥ 3.31.0", ) + raise UnsupportedDialect -def _warn_unsupported_version( +def _fail_unsupported_version( server_version: str, dialect_name: str, minimum_version: str ) -> None: """Warn about unsupported database version.""" - _LOGGER.warning( + _LOGGER.error( "Version %s of %s is not supported; minimum supported version is %s. " - "Starting with Home Assistant 2022.2 this will prevent the recorder from " - "starting. Please upgrade your database software before then", + "Starting with Home Assistant 2022.6 this prevents the recorder from " + "starting. Please upgrade your database software", server_version, dialect_name, minimum_version, ) + raise UnsupportedDialect def _extract_version_from_server_response( @@ -398,9 +401,6 @@ def setup_connection_for_dialect( first_connection: bool, ) -> None: """Execute statements needed for dialect connection.""" - # Returns False if the the connection needs to be setup - # on the next connection, returns True if the connection - # never needs to be setup again. if dialect_name == SupportedDialect.SQLITE: if first_connection: old_isolation = dbapi_connection.isolation_level @@ -419,7 +419,7 @@ def setup_connection_for_dialect( False ) if not version or version < MIN_VERSION_SQLITE: - _warn_unsupported_version( + _fail_unsupported_version( version or version_string, "SQLite", MIN_VERSION_SQLITE ) @@ -453,7 +453,7 @@ def setup_connection_for_dialect( False ) if not version or version < MIN_VERSION_MARIA_DB: - _warn_unsupported_version( + _fail_unsupported_version( version or version_string, "MariaDB", MIN_VERSION_MARIA_DB ) else: @@ -462,7 +462,7 @@ def setup_connection_for_dialect( False ) if not version or version < MIN_VERSION_MYSQL: - _warn_unsupported_version( + _fail_unsupported_version( version or version_string, "MySQL", MIN_VERSION_MYSQL ) @@ -473,12 +473,12 @@ def setup_connection_for_dialect( version_string = result[0][0] version = _extract_version_from_server_response(version_string) if not version or version < MIN_VERSION_PGSQL: - _warn_unsupported_version( + _fail_unsupported_version( version or version_string, "PostgreSQL", MIN_VERSION_PGSQL ) else: - _warn_unsupported_dialect(dialect_name) + _fail_unsupported_dialect(dialect_name) def end_incomplete_runs(session: Session, start_time: datetime) -> None: diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index fea255647ec..e7685a93ff2 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -14,7 +14,7 @@ from sqlalchemy.sql.lambdas import StatementLambdaElement from homeassistant.components import recorder from homeassistant.components.recorder import history, util from homeassistant.components.recorder.const import DATA_INSTANCE, SQLITE_URL_PREFIX -from homeassistant.components.recorder.models import RecorderRuns +from homeassistant.components.recorder.models import RecorderRuns, UnsupportedDialect from homeassistant.components.recorder.util import ( end_incomplete_runs, is_second_sunday, @@ -168,10 +168,8 @@ async def test_last_run_was_recently_clean( @pytest.mark.parametrize( "mysql_version, db_supports_row_number", [ - ("10.2.0-MariaDB", True), - ("10.1.0-MariaDB", False), - ("5.8.0", True), - ("5.7.0", False), + ("10.3.0-MariaDB", True), + ("8.0.0", True), ], ) def test_setup_connection_for_dialect_mysql(mysql_version, db_supports_row_number): @@ -207,8 +205,7 @@ def test_setup_connection_for_dialect_mysql(mysql_version, db_supports_row_numbe @pytest.mark.parametrize( "sqlite_version, db_supports_row_number", [ - ("3.25.0", True), - ("3.24.0", False), + ("3.31.0", True), ], ) def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_number): @@ -255,8 +252,7 @@ def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_num @pytest.mark.parametrize( "sqlite_version, db_supports_row_number", [ - ("3.25.0", True), - ("3.24.0", False), + ("3.31.0", True), ], ) def test_setup_connection_for_dialect_sqlite_zero_commit_interval( @@ -319,7 +315,7 @@ def test_setup_connection_for_dialect_sqlite_zero_commit_interval( ), ], ) -def test_warn_outdated_mysql(caplog, mysql_version, message): +def test_fail_outdated_mysql(caplog, mysql_version, message): """Test setting up the connection for an outdated mysql version.""" instance_mock = MagicMock(_db_supports_row_number=True) execute_args = [] @@ -340,7 +336,10 @@ def test_warn_outdated_mysql(caplog, mysql_version, message): dbapi_connection = MagicMock(cursor=_make_cursor_mock) - util.setup_connection_for_dialect(instance_mock, "mysql", dbapi_connection, True) + with pytest.raises(UnsupportedDialect): + util.setup_connection_for_dialect( + instance_mock, "mysql", dbapi_connection, True + ) assert message in caplog.text @@ -395,7 +394,7 @@ def test_supported_mysql(caplog, mysql_version): ), ], ) -def test_warn_outdated_pgsql(caplog, pgsql_version, message): +def test_fail_outdated_pgsql(caplog, pgsql_version, message): """Test setting up the connection for an outdated PostgreSQL version.""" instance_mock = MagicMock(_db_supports_row_number=True) execute_args = [] @@ -416,9 +415,10 @@ def test_warn_outdated_pgsql(caplog, pgsql_version, message): dbapi_connection = MagicMock(cursor=_make_cursor_mock) - util.setup_connection_for_dialect( - instance_mock, "postgresql", dbapi_connection, True - ) + with pytest.raises(UnsupportedDialect): + util.setup_connection_for_dialect( + instance_mock, "postgresql", dbapi_connection, True + ) assert message in caplog.text @@ -472,7 +472,7 @@ def test_supported_pgsql(caplog, pgsql_version): ), ], ) -def test_warn_outdated_sqlite(caplog, sqlite_version, message): +def test_fail_outdated_sqlite(caplog, sqlite_version, message): """Test setting up the connection for an outdated sqlite version.""" instance_mock = MagicMock(_db_supports_row_number=True) execute_args = [] @@ -493,7 +493,10 @@ def test_warn_outdated_sqlite(caplog, sqlite_version, message): dbapi_connection = MagicMock(cursor=_make_cursor_mock) - util.setup_connection_for_dialect(instance_mock, "sqlite", dbapi_connection, True) + with pytest.raises(UnsupportedDialect): + util.setup_connection_for_dialect( + instance_mock, "sqlite", dbapi_connection, True + ) assert message in caplog.text @@ -544,7 +547,10 @@ def test_warn_unsupported_dialect(caplog, dialect, message): instance_mock = MagicMock() dbapi_connection = MagicMock() - util.setup_connection_for_dialect(instance_mock, dialect, dbapi_connection, True) + with pytest.raises(UnsupportedDialect): + util.setup_connection_for_dialect( + instance_mock, dialect, dbapi_connection, True + ) assert message in caplog.text From bd78eec73257d85a291f9aa7660f88b030ea5053 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 10:42:14 -0500 Subject: [PATCH 581/930] Fix reversed raise_on_progress in baf config_flow (#72094) --- homeassistant/components/baf/config_flow.py | 6 +- tests/components/baf/__init__.py | 36 ++++++++++ tests/components/baf/test_config_flow.py | 74 ++++++++++++++++----- 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/baf/config_flow.py b/homeassistant/components/baf/config_flow.py index 1c2873eb759..2326d30937b 100644 --- a/homeassistant/components/baf/config_flow.py +++ b/homeassistant/components/baf/config_flow.py @@ -54,7 +54,7 @@ class BAFFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): uuid = properties["uuid"] model = properties["model"] name = properties["name"] - await self.async_set_unique_id(uuid, raise_on_progress=False) + await self.async_set_unique_id(uuid) self._abort_if_unique_id_configured(updates={CONF_IP_ADDRESS: ip_address}) self.discovery = BAFDiscovery(ip_address, name, uuid, model) return await self.async_step_discovery_confirm() @@ -98,7 +98,9 @@ class BAFFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) errors["base"] = "unknown" else: - await self.async_set_unique_id(device.dns_sd_uuid) + await self.async_set_unique_id( + device.dns_sd_uuid, raise_on_progress=False + ) self._abort_if_unique_id_configured( updates={CONF_IP_ADDRESS: ip_address} ) diff --git a/tests/components/baf/__init__.py b/tests/components/baf/__init__.py index e0432ed643a..4e435dc1a2e 100644 --- a/tests/components/baf/__init__.py +++ b/tests/components/baf/__init__.py @@ -1 +1,37 @@ """Tests for the Big Ass Fans integration.""" + + +import asyncio + +from aiobafi6 import Device + +MOCK_UUID = "1234" +MOCK_NAME = "Living Room Fan" + + +class MockBAFDevice(Device): + """A simple mock for a BAF Device.""" + + def __init__(self, async_wait_available_side_effect=None): + """Init simple mock.""" + self._async_wait_available_side_effect = async_wait_available_side_effect + + @property + def dns_sd_uuid(self): + """Mock the unique id.""" + return MOCK_UUID + + @property + def name(self): + """Mock the name of the device.""" + return MOCK_NAME + + async def async_wait_available(self): + """Mock async_wait_available.""" + if self._async_wait_available_side_effect: + raise self._async_wait_available_side_effect + return + + def async_run(self): + """Mock async_run.""" + return asyncio.Future() diff --git a/tests/components/baf/test_config_flow.py b/tests/components/baf/test_config_flow.py index 687a871ed4c..77a83a9673b 100644 --- a/tests/components/baf/test_config_flow.py +++ b/tests/components/baf/test_config_flow.py @@ -12,9 +12,20 @@ from homeassistant.data_entry_flow import ( RESULT_TYPE_FORM, ) +from . import MOCK_NAME, MOCK_UUID, MockBAFDevice + from tests.common import MockConfigEntry +def _patch_device_config_flow(side_effect=None): + """Mock out the BAF Device object.""" + + def _create_mock_baf(*args, **kwargs): + return MockBAFDevice(side_effect) + + return patch("homeassistant.components.baf.config_flow.Device", _create_mock_baf) + + async def test_form_user(hass): """Test we get the user form.""" @@ -24,9 +35,7 @@ async def test_form_user(hass): assert result["type"] == "form" assert result["errors"] == {} - with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( - "homeassistant.components.baf.config_flow.Device.async_wait_available", - ), patch( + with _patch_device_config_flow(), patch( "homeassistant.components.baf.async_setup_entry", return_value=True, ) as mock_setup_entry: @@ -37,7 +46,7 @@ async def test_form_user(hass): await hass.async_block_till_done() assert result2["type"] == RESULT_TYPE_CREATE_ENTRY - assert result2["title"] == "127.0.0.1" + assert result2["title"] == MOCK_NAME assert result2["data"] == {CONF_IP_ADDRESS: "127.0.0.1"} assert len(mock_setup_entry.mock_calls) == 1 @@ -48,10 +57,7 @@ async def test_form_cannot_connect(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( - "homeassistant.components.baf.config_flow.Device.async_wait_available", - side_effect=asyncio.TimeoutError, - ): + with _patch_device_config_flow(asyncio.TimeoutError): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "127.0.0.1"}, @@ -67,10 +73,7 @@ async def test_form_unknown_exception(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("homeassistant.components.baf.config_flow.Device.async_run",), patch( - "homeassistant.components.baf.config_flow.Device.async_wait_available", - side_effect=Exception, - ): + with _patch_device_config_flow(Exception): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "127.0.0.1"}, @@ -92,7 +95,7 @@ async def test_zeroconf_discovery(hass): hostname="mock_hostname", name="testfan", port=None, - properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + properties={"name": "My Fan", "model": "Haiku", "uuid": MOCK_UUID}, type="mock_type", ), ) @@ -118,7 +121,7 @@ async def test_zeroconf_discovery(hass): async def test_zeroconf_updates_existing_ip(hass): """Test we can setup from zeroconf discovery.""" entry = MockConfigEntry( - domain=DOMAIN, data={CONF_IP_ADDRESS: "127.0.0.2"}, unique_id="1234" + domain=DOMAIN, data={CONF_IP_ADDRESS: "127.0.0.2"}, unique_id=MOCK_UUID ) entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( @@ -130,7 +133,7 @@ async def test_zeroconf_updates_existing_ip(hass): hostname="mock_hostname", name="testfan", port=None, - properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + properties={"name": "My Fan", "model": "Haiku", "uuid": MOCK_UUID}, type="mock_type", ), ) @@ -150,9 +153,48 @@ async def test_zeroconf_rejects_ipv6(hass): hostname="mock_hostname", name="testfan", port=None, - properties={"name": "My Fan", "model": "Haiku", "uuid": "1234"}, + properties={"name": "My Fan", "model": "Haiku", "uuid": MOCK_UUID}, type="mock_type", ), ) assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "ipv6_not_supported" + + +async def test_user_flow_is_not_blocked_by_discovery(hass): + """Test we can setup from the user flow when there is also a discovery.""" + discovery_result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="127.0.0.1", + addresses=["127.0.0.1"], + hostname="mock_hostname", + name="testfan", + port=None, + properties={"name": "My Fan", "model": "Haiku", "uuid": MOCK_UUID}, + type="mock_type", + ), + ) + assert discovery_result["type"] == RESULT_TYPE_FORM + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with _patch_device_config_flow(), patch( + "homeassistant.components.baf.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_IP_ADDRESS: "127.0.0.1"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == MOCK_NAME + assert result2["data"] == {CONF_IP_ADDRESS: "127.0.0.1"} + assert len(mock_setup_entry.mock_calls) == 1 From 50ca14538a147b3c5be41c02ae05b389285998ae Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 17:58:28 +0200 Subject: [PATCH 582/930] Cleanup deprecated async_get_registry in core (#72087) --- .../binary_sensor/device_condition.py | 14 ++++---- .../components/device_tracker/legacy.py | 11 +++--- homeassistant/components/hassio/__init__.py | 35 ++++++++++--------- .../components/sensor/device_condition.py | 14 ++++---- homeassistant/helpers/entity_registry.py | 2 +- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/binary_sensor/device_condition.py b/homeassistant/components/binary_sensor/device_condition.py index 1f16956ff41..4b3aa70d716 100644 --- a/homeassistant/components/binary_sensor/device_condition.py +++ b/homeassistant/components/binary_sensor/device_condition.py @@ -12,12 +12,12 @@ from homeassistant.const import ( CONF_TYPE, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import condition, config_validation as cv -from homeassistant.helpers.entity import get_device_class -from homeassistant.helpers.entity_registry import ( - async_entries_for_device, - async_get_registry, +from homeassistant.helpers import ( + condition, + config_validation as cv, + entity_registry as er, ) +from homeassistant.helpers.entity import get_device_class from homeassistant.helpers.typing import ConfigType from . import DOMAIN, BinarySensorDeviceClass @@ -268,10 +268,10 @@ async def async_get_conditions( ) -> list[dict[str, str]]: """List device conditions.""" conditions: list[dict[str, str]] = [] - entity_registry = await async_get_registry(hass) + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == DOMAIN ] diff --git a/homeassistant/components/device_tracker/legacy.py b/homeassistant/components/device_tracker/legacy.py index 01fc8a444e1..87026fc32ff 100644 --- a/homeassistant/components/device_tracker/legacy.py +++ b/homeassistant/components/device_tracker/legacy.py @@ -30,9 +30,12 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_per_platform, discovery -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_registry import async_get_registry +from homeassistant.helpers import ( + config_per_platform, + config_validation as cv, + discovery, + entity_registry as er, +) from homeassistant.helpers.event import ( async_track_time_interval, async_track_utc_time_change, @@ -487,7 +490,7 @@ class DeviceTracker: This method is a coroutine. """ - registry = await async_get_registry(self.hass) + registry = er.async_get(self.hass) if mac is None and dev_id is None: raise HomeAssistantError("Neither mac or device id passed in") if mac is not None: diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 3ded34bd6f1..7e976536691 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -33,13 +33,12 @@ from homeassistant.core import ( callback, ) from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv, recorder -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import ( - DeviceEntryType, - DeviceRegistry, - async_get_registry, +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + recorder, ) +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.storage import Store @@ -715,7 +714,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a config entry.""" - dev_reg = await async_get_registry(hass) + dev_reg = dr.async_get(hass) coordinator = HassioDataUpdateCoordinator(hass, entry, dev_reg) hass.data[ADDONS_COORDINATOR] = coordinator await coordinator.async_config_entry_first_refresh() @@ -737,7 +736,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @callback def async_register_addons_in_dev_reg( - entry_id: str, dev_reg: DeviceRegistry, addons: list[dict[str, Any]] + entry_id: str, dev_reg: dr.DeviceRegistry, addons: list[dict[str, Any]] ) -> None: """Register addons in the device registry.""" for addon in addons: @@ -746,7 +745,7 @@ def async_register_addons_in_dev_reg( model=SupervisorEntityModel.ADDON, sw_version=addon[ATTR_VERSION], name=addon[ATTR_NAME], - entry_type=DeviceEntryType.SERVICE, + entry_type=dr.DeviceEntryType.SERVICE, configuration_url=f"homeassistant://hassio/addon/{addon[ATTR_SLUG]}", ) if manufacturer := addon.get(ATTR_REPOSITORY) or addon.get(ATTR_URL): @@ -756,7 +755,7 @@ def async_register_addons_in_dev_reg( @callback def async_register_os_in_dev_reg( - entry_id: str, dev_reg: DeviceRegistry, os_dict: dict[str, Any] + entry_id: str, dev_reg: dr.DeviceRegistry, os_dict: dict[str, Any] ) -> None: """Register OS in the device registry.""" params = DeviceInfo( @@ -765,7 +764,7 @@ def async_register_os_in_dev_reg( model=SupervisorEntityModel.OS, sw_version=os_dict[ATTR_VERSION], name="Home Assistant Operating System", - entry_type=DeviceEntryType.SERVICE, + entry_type=dr.DeviceEntryType.SERVICE, ) dev_reg.async_get_or_create(config_entry_id=entry_id, **params) @@ -773,7 +772,7 @@ def async_register_os_in_dev_reg( @callback def async_register_core_in_dev_reg( entry_id: str, - dev_reg: DeviceRegistry, + dev_reg: dr.DeviceRegistry, core_dict: dict[str, Any], ) -> None: """Register OS in the device registry.""" @@ -783,7 +782,7 @@ def async_register_core_in_dev_reg( model=SupervisorEntityModel.CORE, sw_version=core_dict[ATTR_VERSION], name="Home Assistant Core", - entry_type=DeviceEntryType.SERVICE, + entry_type=dr.DeviceEntryType.SERVICE, ) dev_reg.async_get_or_create(config_entry_id=entry_id, **params) @@ -791,7 +790,7 @@ def async_register_core_in_dev_reg( @callback def async_register_supervisor_in_dev_reg( entry_id: str, - dev_reg: DeviceRegistry, + dev_reg: dr.DeviceRegistry, supervisor_dict: dict[str, Any], ) -> None: """Register OS in the device registry.""" @@ -801,13 +800,15 @@ def async_register_supervisor_in_dev_reg( model=SupervisorEntityModel.SUPERVIOSR, sw_version=supervisor_dict[ATTR_VERSION], name="Home Assistant Supervisor", - entry_type=DeviceEntryType.SERVICE, + entry_type=dr.DeviceEntryType.SERVICE, ) dev_reg.async_get_or_create(config_entry_id=entry_id, **params) @callback -def async_remove_addons_from_dev_reg(dev_reg: DeviceRegistry, addons: set[str]) -> None: +def async_remove_addons_from_dev_reg( + dev_reg: dr.DeviceRegistry, addons: set[str] +) -> None: """Remove addons from the device registry.""" for addon_slug in addons: if dev := dev_reg.async_get_device({(DOMAIN, addon_slug)}): @@ -818,7 +819,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator): """Class to retrieve Hass.io status.""" def __init__( - self, hass: HomeAssistant, config_entry: ConfigEntry, dev_reg: DeviceRegistry + self, hass: HomeAssistant, config_entry: ConfigEntry, dev_reg: dr.DeviceRegistry ) -> None: """Initialize coordinator.""" super().__init__( diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index 70a265e3b25..a77105764d5 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -15,16 +15,16 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import condition, config_validation as cv +from homeassistant.helpers import ( + condition, + config_validation as cv, + entity_registry as er, +) from homeassistant.helpers.entity import ( get_capability, get_device_class, get_unit_of_measurement, ) -from homeassistant.helpers.entity_registry import ( - async_entries_for_device, - async_get_registry, -) from homeassistant.helpers.typing import ConfigType from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass @@ -141,10 +141,10 @@ async def async_get_conditions( ) -> list[dict[str, str]]: """List device conditions.""" conditions: list[dict[str, str]] = [] - entity_registry = await async_get_registry(hass) + entity_registry = er.async_get(hass) entries = [ entry - for entry in async_entries_for_device(entity_registry, device_id) + for entry in er.async_entries_for_device(entity_registry, device_id) if entry.domain == DOMAIN ] diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 64ab0323f6c..2c87c4cbdd2 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -995,7 +995,7 @@ async def async_migrate_entries( entry_callback: Callable[[RegistryEntry], dict[str, Any] | None], ) -> None: """Migrator of unique IDs.""" - ent_reg = await async_get_registry(hass) + ent_reg = async_get(hass) for entry in ent_reg.entities.values(): if entry.config_entry_id != config_entry_id: From f4b252a51d4c450821920e696339c8122180b531 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 18:43:43 +0200 Subject: [PATCH 583/930] Cleanup hue async methods which are not awaiting (#72097) --- homeassistant/components/hue/device_trigger.py | 4 ++-- homeassistant/components/hue/v1/device_trigger.py | 4 +++- homeassistant/components/hue/v2/device_trigger.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/hue/device_trigger.py b/homeassistant/components/hue/device_trigger.py index a4b545aa141..29df1fd2476 100644 --- a/homeassistant/components/hue/device_trigger.py +++ b/homeassistant/components/hue/device_trigger.py @@ -99,5 +99,5 @@ async def async_get_triggers(hass: "HomeAssistant", device_id: str): bridge: HueBridge = hass.data[DOMAIN][conf_entry_id] if bridge.api_version == 1: - return await async_get_triggers_v1(bridge, device_entry) - return await async_get_triggers_v2(bridge, device_entry) + return async_get_triggers_v1(bridge, device_entry) + return async_get_triggers_v2(bridge, device_entry) diff --git a/homeassistant/components/hue/v1/device_trigger.py b/homeassistant/components/hue/v1/device_trigger.py index d6b471b7257..7b58bf42089 100644 --- a/homeassistant/components/hue/v1/device_trigger.py +++ b/homeassistant/components/hue/v1/device_trigger.py @@ -16,6 +16,7 @@ from homeassistant.const import ( CONF_TYPE, CONF_UNIQUE_ID, ) +from homeassistant.core import callback from homeassistant.helpers.device_registry import DeviceEntry from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN @@ -160,7 +161,8 @@ async def async_attach_trigger(bridge, device_entry, config, action, automation_ ) -async def async_get_triggers(bridge: "HueBridge", device: DeviceEntry): +@callback +def async_get_triggers(bridge: "HueBridge", device: DeviceEntry): """Return device triggers for device on `v1` bridge. Make sure device is a supported remote model. diff --git a/homeassistant/components/hue/v2/device_trigger.py b/homeassistant/components/hue/v2/device_trigger.py index cab21b63d6d..8c5da8febb9 100644 --- a/homeassistant/components/hue/v2/device_trigger.py +++ b/homeassistant/components/hue/v2/device_trigger.py @@ -119,7 +119,8 @@ async def async_attach_trigger( ) -async def async_get_triggers(bridge: "HueBridge", device_entry: DeviceEntry): +@callback +def async_get_triggers(bridge: HueBridge, device_entry: DeviceEntry): """Return device triggers for device on `v2` bridge.""" api: HueBridgeV2 = bridge.api From 8ff0ced846e505a0c33a848e21b19820861e6884 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 19 May 2022 04:46:13 +1200 Subject: [PATCH 584/930] Initial implementation of ESPHome media players (#72047) Co-authored-by: Paulus Schoutsen Co-authored-by: Franck Nijhof --- .coveragerc | 1 + .../components/esphome/entry_data.py | 2 + .../components/esphome/media_player.py | 151 ++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 homeassistant/components/esphome/media_player.py diff --git a/.coveragerc b/.coveragerc index da12b41d222..bd7aa405b94 100644 --- a/.coveragerc +++ b/.coveragerc @@ -314,6 +314,7 @@ omit = homeassistant/components/esphome/fan.py homeassistant/components/esphome/light.py homeassistant/components/esphome/lock.py + homeassistant/components/esphome/media_player.py homeassistant/components/esphome/number.py homeassistant/components/esphome/select.py homeassistant/components/esphome/sensor.py diff --git a/homeassistant/components/esphome/entry_data.py b/homeassistant/components/esphome/entry_data.py index c00073b4432..4c5a94afe0f 100644 --- a/homeassistant/components/esphome/entry_data.py +++ b/homeassistant/components/esphome/entry_data.py @@ -20,6 +20,7 @@ from aioesphomeapi import ( FanInfo, LightInfo, LockInfo, + MediaPlayerInfo, NumberInfo, SelectInfo, SensorInfo, @@ -46,6 +47,7 @@ INFO_TYPE_TO_PLATFORM: dict[type[EntityInfo], str] = { FanInfo: "fan", LightInfo: "light", LockInfo: "lock", + MediaPlayerInfo: "media_player", NumberInfo: "number", SelectInfo: "select", SensorInfo: "sensor", diff --git a/homeassistant/components/esphome/media_player.py b/homeassistant/components/esphome/media_player.py new file mode 100644 index 00000000000..6e83d12a427 --- /dev/null +++ b/homeassistant/components/esphome/media_player.py @@ -0,0 +1,151 @@ +"""Support for ESPHome media players.""" +from __future__ import annotations + +from typing import Any + +from aioesphomeapi import ( + MediaPlayerCommand, + MediaPlayerEntityState, + MediaPlayerInfo, + MediaPlayerState, +) + +from homeassistant.components import media_source +from homeassistant.components.media_player import ( + MediaPlayerDeviceClass, + MediaPlayerEntity, +) +from homeassistant.components.media_player.browse_media import ( + BrowseMedia, + async_process_play_media_url, +) +from homeassistant.components.media_player.const import MediaPlayerEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_IDLE, STATE_PAUSED, STATE_PLAYING +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import EsphomeEntity, EsphomeEnumMapper, platform_async_setup_entry + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up esphome media players based on a config entry.""" + await platform_async_setup_entry( + hass, + entry, + async_add_entities, + component_key="media_player", + info_type=MediaPlayerInfo, + entity_type=EsphomeMediaPlayer, + state_type=MediaPlayerEntityState, + ) + + +_STATES: EsphomeEnumMapper[MediaPlayerState, str] = EsphomeEnumMapper( + { + MediaPlayerState.IDLE: STATE_IDLE, + MediaPlayerState.PLAYING: STATE_PLAYING, + MediaPlayerState.PAUSED: STATE_PAUSED, + } +) + + +class EsphomeMediaPlayer( + EsphomeEntity[MediaPlayerInfo, MediaPlayerEntityState], MediaPlayerEntity +): + """A media player implementation for esphome.""" + + _attr_device_class = MediaPlayerDeviceClass.SPEAKER + + @property + def state(self) -> str | None: + """Return current state.""" + return _STATES.from_esphome(self._state.state) + + @property + def is_volume_muted(self) -> bool: + """Return true if volume is muted.""" + return self._state.muted + + @property + def volume_level(self) -> float | None: + """Volume level of the media player (0..1).""" + return self._state.volume + + @property + def supported_features(self) -> int: + """Flag supported features.""" + flags = ( + MediaPlayerEntityFeature.PLAY_MEDIA + | MediaPlayerEntityFeature.BROWSE_MEDIA + | MediaPlayerEntityFeature.STOP + | MediaPlayerEntityFeature.VOLUME_SET + | MediaPlayerEntityFeature.VOLUME_MUTE + ) + if self._static_info.supports_pause: + flags |= MediaPlayerEntityFeature.PAUSE | MediaPlayerEntityFeature.PLAY + return flags + + async def async_play_media( + self, media_type: str, media_id: str, **kwargs: Any + ) -> None: + """Send the play command with media url to the media player.""" + if media_source.is_media_source_id(media_id): + sourced_media = await media_source.async_resolve_media(self.hass, media_id) + media_id = sourced_media.url + + media_id = async_process_play_media_url(self.hass, media_id) + + await self._client.media_player_command( + self._static_info.key, + media_url=media_id, + ) + + async def async_browse_media( + self, media_content_type: str | None = None, media_content_id: str | None = None + ) -> BrowseMedia: + """Implement the websocket media browsing helper.""" + return await media_source.async_browse_media( + self.hass, + media_content_id, + content_filter=lambda item: item.media_content_type.startswith("audio/"), + ) + + async def async_set_volume_level(self, volume: float) -> None: + """Set volume level, range 0..1.""" + await self._client.media_player_command( + self._static_info.key, + volume=volume, + ) + + async def async_media_pause(self) -> None: + """Send pause command.""" + await self._client.media_player_command( + self._static_info.key, + command=MediaPlayerCommand.PAUSE, + ) + + async def async_media_play(self) -> None: + """Send play command.""" + await self._client.media_player_command( + self._static_info.key, + command=MediaPlayerCommand.PLAY, + ) + + async def async_media_stop(self) -> None: + """Send stop command.""" + await self._client.media_player_command( + self._static_info.key, + command=MediaPlayerCommand.STOP, + ) + + async def async_mute_volume(self, mute: bool) -> None: + """Mute the volume.""" + await self._client.media_player_command( + self._static_info.key, + command=MediaPlayerCommand.MUTE if mute else MediaPlayerCommand.UNMUTE, + ) From 8f7f3f328e01c798c271dab8b744c7641f339c1c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 19:01:56 +0200 Subject: [PATCH 585/930] Suppress Upnp error in SamsungTV resubscribe (#71925) * Suppress Upnp error in SamsungTV resubscribe * Supress UpnpCommunicationError instead * Log resubscribe errors * Add tests * Add exc_info --- .../components/samsungtv/media_player.py | 5 +- .../components/samsungtv/test_media_player.py | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 0599115774e..423a778011d 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -12,6 +12,7 @@ from async_upnp_client.client import UpnpDevice, UpnpService, UpnpStateVariable from async_upnp_client.client_factory import UpnpFactory from async_upnp_client.exceptions import ( UpnpActionResponseError, + UpnpCommunicationError, UpnpConnectionError, UpnpError, UpnpResponseError, @@ -309,8 +310,10 @@ class SamsungTVDevice(MediaPlayerEntity): async def _async_resubscribe_dmr(self) -> None: assert self._dmr_device - with contextlib.suppress(UpnpConnectionError): + try: await self._dmr_device.async_subscribe_services(auto_resubscribe=True) + except UpnpCommunicationError as err: + LOGGER.debug("Device rejected re-subscription: %r", err, exc_info=True) async def _async_shutdown_dmr(self) -> None: """Handle removal.""" diff --git a/tests/components/samsungtv/test_media_player.py b/tests/components/samsungtv/test_media_player.py index 56cce6ebfbf..e548822f1d0 100644 --- a/tests/components/samsungtv/test_media_player.py +++ b/tests/components/samsungtv/test_media_player.py @@ -6,6 +6,8 @@ from unittest.mock import DEFAULT as DEFAULT_MOCK, AsyncMock, Mock, call, patch from async_upnp_client.exceptions import ( UpnpActionResponseError, + UpnpCommunicationError, + UpnpConnectionError, UpnpError, UpnpResponseError, ) @@ -1507,3 +1509,49 @@ async def test_upnp_re_subscribe_events( assert state.state == STATE_ON assert dmr_device.async_subscribe_services.call_count == 2 assert dmr_device.async_unsubscribe_services.call_count == 1 + + +@pytest.mark.usefixtures("rest_api", "upnp_notify_server") +@pytest.mark.parametrize( + "error", + {UpnpConnectionError(), UpnpCommunicationError(), UpnpResponseError(status=400)}, +) +async def test_upnp_failed_re_subscribe_events( + hass: HomeAssistant, + remotews: Mock, + dmr_device: Mock, + mock_now: datetime, + caplog: pytest.LogCaptureFixture, + error: Exception, +) -> None: + """Test for Upnp event feedback.""" + await setup_samsungtv_entry(hass, MOCK_ENTRY_WS) + + state = hass.states.get(ENTITY_ID) + assert state.state == STATE_ON + assert dmr_device.async_subscribe_services.call_count == 1 + assert dmr_device.async_unsubscribe_services.call_count == 0 + + with patch.object( + remotews, "start_listening", side_effect=WebSocketException("Boom") + ), patch.object(remotews, "is_alive", return_value=False): + next_update = mock_now + timedelta(minutes=5) + with patch("homeassistant.util.dt.utcnow", return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.state == STATE_OFF + assert dmr_device.async_subscribe_services.call_count == 1 + assert dmr_device.async_unsubscribe_services.call_count == 1 + + next_update = mock_now + timedelta(minutes=10) + with patch("homeassistant.util.dt.utcnow", return_value=next_update), patch.object( + dmr_device, "async_subscribe_services", side_effect=error + ): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.state == STATE_ON + assert "Device rejected re-subscription" in caplog.text From c74b2419495d1c29112dc745c05c6c11a9dd313a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 12:28:04 -0500 Subject: [PATCH 586/930] Include initial state in history_stats count (#71952) --- .../components/history_stats/data.py | 14 +++++----- .../components/history_stats/sensor.py | 2 +- tests/components/history_stats/test_sensor.py | 26 +++++++++---------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index 3d21cca6b6d..8153557422d 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -19,7 +19,7 @@ class HistoryStatsState: """The current stats of the history stats.""" hours_matched: float | None - changes_to_match_state: int | None + match_count: int | None period: tuple[datetime.datetime, datetime.datetime] @@ -121,14 +121,12 @@ class HistoryStats: self._state = HistoryStatsState(None, None, self._period) return self._state - hours_matched, changes_to_match_state = self._async_compute_hours_and_changes( + hours_matched, match_count = self._async_compute_hours_and_changes( now_timestamp, current_period_start_timestamp, current_period_end_timestamp, ) - self._state = HistoryStatsState( - hours_matched, changes_to_match_state, self._period - ) + self._state = HistoryStatsState(hours_matched, match_count, self._period) return self._state def _update_from_database( @@ -156,7 +154,7 @@ class HistoryStats: ) last_state_change_timestamp = start_timestamp elapsed = 0.0 - changes_to_match_state = 0 + match_count = 1 if previous_state_matches else 0 # Make calculations for item in self._history_current_period: @@ -166,7 +164,7 @@ class HistoryStats: if previous_state_matches: elapsed += state_change_timestamp - last_state_change_timestamp elif current_state_matches: - changes_to_match_state += 1 + match_count += 1 previous_state_matches = current_state_matches last_state_change_timestamp = state_change_timestamp @@ -178,4 +176,4 @@ class HistoryStats: # Save value in hours hours_matched = elapsed / 3600 - return hours_matched, changes_to_match_state + return hours_matched, match_count diff --git a/homeassistant/components/history_stats/sensor.py b/homeassistant/components/history_stats/sensor.py index b3e64106d9f..b0ce1a8fca5 100644 --- a/homeassistant/components/history_stats/sensor.py +++ b/homeassistant/components/history_stats/sensor.py @@ -166,4 +166,4 @@ class HistoryStatsSensor(HistoryStatsSensorBase): elif self._type == CONF_TYPE_RATIO: self._attr_native_value = pretty_ratio(state.hours_matched, state.period) elif self._type == CONF_TYPE_COUNT: - self._attr_native_value = state.changes_to_match_state + self._attr_native_value = state.match_count diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 4f56edaa291..b375a8f63c4 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -438,7 +438,7 @@ async def test_measure(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "83.3" @@ -519,7 +519,7 @@ async def test_async_on_entire_period(hass, recorder_mock): assert hass.states.get("sensor.on_sensor1").state == "1.0" assert hass.states.get("sensor.on_sensor2").state == "1.0" - assert hass.states.get("sensor.on_sensor3").state == "0" + assert hass.states.get("sensor.on_sensor3").state == "1" assert hass.states.get("sensor.on_sensor4").state == "100.0" @@ -886,7 +886,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "0.0" assert hass.states.get("sensor.sensor2").state == "0.0" - assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "0.0" one_hour_in = start_time + timedelta(minutes=60) @@ -896,7 +896,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "1.0" assert hass.states.get("sensor.sensor2").state == "1.0" - assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "50.0" turn_off_time = start_time + timedelta(minutes=90) @@ -908,7 +908,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "1.5" assert hass.states.get("sensor.sensor2").state == "1.5" - assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "75.0" turn_back_on_time = start_time + timedelta(minutes=105) @@ -918,7 +918,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "1.5" assert hass.states.get("sensor.sensor2").state == "1.5" - assert hass.states.get("sensor.sensor3").state == "0" + assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "75.0" with freeze_time(turn_back_on_time): @@ -927,7 +927,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "1.5" assert hass.states.get("sensor.sensor2").state == "1.5" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "75.0" end_time = start_time + timedelta(minutes=120) @@ -937,7 +937,7 @@ async def test_async_start_from_history_and_switch_to_watching_state_changes_mul assert hass.states.get("sensor.sensor1").state == "1.75" assert hass.states.get("sensor.sensor2").state == "1.75" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "87.5" @@ -1198,7 +1198,7 @@ async def test_measure_sliding_window(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "41.7" past_next_update = start_time + timedelta(minutes=30) @@ -1211,7 +1211,7 @@ async def test_measure_sliding_window(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "41.7" @@ -1291,7 +1291,7 @@ async def test_measure_from_end_going_backwards(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "83.3" past_next_update = start_time + timedelta(minutes=30) @@ -1304,7 +1304,7 @@ async def test_measure_from_end_going_backwards(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "83.3" @@ -1385,7 +1385,7 @@ async def test_measure_cet(hass, recorder_mock): assert hass.states.get("sensor.sensor1").state == "0.83" assert hass.states.get("sensor.sensor2").state == "0.83" - assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor3").state == "2" assert hass.states.get("sensor.sensor4").state == "83.3" From 4a95539d9dd032eac5d7c7f9d67c303e56eff9c8 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Wed, 18 May 2022 19:29:02 +0200 Subject: [PATCH 587/930] Warn user if "model" key is missing from Shelly firmware (#71612) Co-authored-by: Paulus Schoutsen --- .../components/shelly/config_flow.py | 33 ++++++++++++------- homeassistant/components/shelly/strings.json | 3 +- .../components/shelly/translations/en.json | 1 + tests/components/shelly/test_config_flow.py | 23 +++++++++++++ 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index 9311be1a49e..abcfe689e93 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -119,6 +119,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) except HTTP_CONNECT_ERRORS: errors["base"] = "cannot_connect" + except KeyError: + errors["base"] = "firmware_not_fully_provisioned" except Exception: # pylint: disable=broad-except LOGGER.exception("Unexpected exception") errors["base"] = "unknown" @@ -160,6 +162,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "cannot_connect" except aioshelly.exceptions.JSONRPCError: errors["base"] = "cannot_connect" + except KeyError: + errors["base"] = "firmware_not_fully_provisioned" except Exception: # pylint: disable=broad-except LOGGER.exception("Unexpected exception") errors["base"] = "unknown" @@ -219,6 +223,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: self.device_info = await validate_input(self.hass, self.host, self.info, {}) + except KeyError: + LOGGER.debug("Shelly host %s firmware not fully provisioned", self.host) except HTTP_CONNECT_ERRORS: return self.async_abort(reason="cannot_connect") @@ -229,18 +235,21 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle discovery confirm.""" errors: dict[str, str] = {} - if user_input is not None: - return self.async_create_entry( - title=self.device_info["title"], - data={ - "host": self.host, - CONF_SLEEP_PERIOD: self.device_info[CONF_SLEEP_PERIOD], - "model": self.device_info["model"], - "gen": self.device_info["gen"], - }, - ) - - self._set_confirm_only() + try: + if user_input is not None: + return self.async_create_entry( + title=self.device_info["title"], + data={ + "host": self.host, + CONF_SLEEP_PERIOD: self.device_info[CONF_SLEEP_PERIOD], + "model": self.device_info["model"], + "gen": self.device_info["gen"], + }, + ) + except KeyError: + errors["base"] = "firmware_not_fully_provisioned" + else: + self._set_confirm_only() return self.async_show_form( step_id="confirm_discovery", diff --git a/homeassistant/components/shelly/strings.json b/homeassistant/components/shelly/strings.json index 209ae6682b8..db1c6043187 100644 --- a/homeassistant/components/shelly/strings.json +++ b/homeassistant/components/shelly/strings.json @@ -21,7 +21,8 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "unknown": "[%key:common::config_flow::error::unknown%]" + "unknown": "[%key:common::config_flow::error::unknown%]", + "firmware_not_fully_provisioned": "Device not fully provisioned. Please contact Shelly support" }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", diff --git a/homeassistant/components/shelly/translations/en.json b/homeassistant/components/shelly/translations/en.json index c23eb13840c..f3e882c3016 100644 --- a/homeassistant/components/shelly/translations/en.json +++ b/homeassistant/components/shelly/translations/en.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Failed to connect", + "firmware_not_fully_provisioned": "Device not fully provisioned. Please contact Shelly support", "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 1293fe92760..713999de36f 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -225,6 +225,29 @@ async def test_form_errors_get_info(hass, error): assert result2["errors"] == {"base": base_error} +@pytest.mark.parametrize("error", [(KeyError, "firmware_not_fully_provisioned")]) +async def test_form_missing_key_get_info(hass, error): + """Test we handle missing key.""" + exc, base_error = error + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + with patch( + "aioshelly.common.get_info", + return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False, "gen": "2"}, + ), patch( + "homeassistant.components.shelly.config_flow.validate_input", + side_effect=KeyError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"host": "1.1.1.1"}, + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": base_error} + + @pytest.mark.parametrize( "error", [(asyncio.TimeoutError, "cannot_connect"), (ValueError, "unknown")] ) From 985bcb23f2bc8ad86a6b76c21262675dd1933fe2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 12:33:58 -0500 Subject: [PATCH 588/930] Fix SAWarning in logbook queries (#72101) --- .../components/logbook/queries/devices.py | 12 ++++++------ .../components/logbook/queries/entities.py | 15 ++++++++------- .../logbook/queries/entities_and_devices.py | 14 ++++++++------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/logbook/queries/devices.py b/homeassistant/components/logbook/queries/devices.py index 20b56ef8dd6..5e7827b87a0 100644 --- a/homeassistant/components/logbook/queries/devices.py +++ b/homeassistant/components/logbook/queries/devices.py @@ -8,7 +8,7 @@ from sqlalchemy import Column, lambda_stmt, select, union_all from sqlalchemy.orm import Query from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select +from sqlalchemy.sql.selectable import CTE, CompoundSelect from homeassistant.components.recorder.models import Events, States @@ -28,7 +28,7 @@ def _select_device_id_context_ids_sub_query( end_day: dt, event_types: tuple[str, ...], json_quotable_device_ids: list[str], -) -> Select: +) -> CompoundSelect: """Generate a subquery to find context ids for multiple devices.""" return select( union_all( @@ -45,17 +45,17 @@ def _apply_devices_context_union( end_day: dt, event_types: tuple[str, ...], json_quotable_device_ids: list[str], -) -> StatementLambdaElement: +) -> CompoundSelect: """Generate a CTE to find the device context ids and a query to find linked row.""" - devices_cte = _select_device_id_context_ids_sub_query( + devices_cte: CTE = _select_device_id_context_ids_sub_query( start_day, end_day, event_types, json_quotable_device_ids, ).cte() return query.union_all( - select_events_context_only().where(Events.context_id.in_(devices_cte)), - select_states_context_only().where(States.context_id.in_(devices_cte)), + select_events_context_only().where(Events.context_id.in_(devices_cte.select())), + select_states_context_only().where(States.context_id.in_(devices_cte.select())), ) diff --git a/homeassistant/components/logbook/queries/entities.py b/homeassistant/components/logbook/queries/entities.py index 6db0931e9f3..844890c23a9 100644 --- a/homeassistant/components/logbook/queries/entities.py +++ b/homeassistant/components/logbook/queries/entities.py @@ -8,7 +8,7 @@ import sqlalchemy from sqlalchemy import Column, lambda_stmt, select, union_all from sqlalchemy.orm import Query from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select +from sqlalchemy.sql.selectable import CTE, CompoundSelect from homeassistant.components.recorder.models import ( ENTITY_ID_LAST_UPDATED_INDEX, @@ -37,7 +37,7 @@ def _select_entities_context_ids_sub_query( event_types: tuple[str, ...], entity_ids: list[str], json_quotable_entity_ids: list[str], -) -> Select: +) -> CompoundSelect: """Generate a subquery to find context ids for multiple entities.""" return select( union_all( @@ -58,9 +58,9 @@ def _apply_entities_context_union( event_types: tuple[str, ...], entity_ids: list[str], json_quotable_entity_ids: list[str], -) -> StatementLambdaElement: +) -> CompoundSelect: """Generate a CTE to find the entity and device context ids and a query to find linked row.""" - entities_cte = _select_entities_context_ids_sub_query( + entities_cte: CTE = _select_entities_context_ids_sub_query( start_day, end_day, event_types, @@ -69,10 +69,12 @@ def _apply_entities_context_union( ).cte() return query.union_all( states_query_for_entity_ids(start_day, end_day, entity_ids), - select_events_context_only().where(Events.context_id.in_(entities_cte)), + select_events_context_only().where( + Events.context_id.in_(entities_cte.select()) + ), select_states_context_only() .where(States.entity_id.not_in(entity_ids)) - .where(States.context_id.in_(entities_cte)), + .where(States.context_id.in_(entities_cte.select())), ) @@ -84,7 +86,6 @@ def entities_stmt( json_quotable_entity_ids: list[str], ) -> StatementLambdaElement: """Generate a logbook query for multiple entities.""" - assert json_quotable_entity_ids is not None return lambda_stmt( lambda: _apply_entities_context_union( select_events_without_states(start_day, end_day, event_types).where( diff --git a/homeassistant/components/logbook/queries/entities_and_devices.py b/homeassistant/components/logbook/queries/entities_and_devices.py index d8a23635ad7..d1c86ddbec5 100644 --- a/homeassistant/components/logbook/queries/entities_and_devices.py +++ b/homeassistant/components/logbook/queries/entities_and_devices.py @@ -8,7 +8,7 @@ import sqlalchemy from sqlalchemy import lambda_stmt, select, union_all from sqlalchemy.orm import Query from sqlalchemy.sql.lambdas import StatementLambdaElement -from sqlalchemy.sql.selectable import Select +from sqlalchemy.sql.selectable import CTE, CompoundSelect from homeassistant.components.recorder.models import Events, States @@ -33,7 +33,7 @@ def _select_entities_device_id_context_ids_sub_query( entity_ids: list[str], json_quotable_entity_ids: list[str], json_quotable_device_ids: list[str], -) -> Select: +) -> CompoundSelect: """Generate a subquery to find context ids for multiple entities and multiple devices.""" return select( union_all( @@ -57,8 +57,8 @@ def _apply_entities_devices_context_union( entity_ids: list[str], json_quotable_entity_ids: list[str], json_quotable_device_ids: list[str], -) -> StatementLambdaElement: - devices_entities_cte = _select_entities_device_id_context_ids_sub_query( +) -> CompoundSelect: + devices_entities_cte: CTE = _select_entities_device_id_context_ids_sub_query( start_day, end_day, event_types, @@ -68,10 +68,12 @@ def _apply_entities_devices_context_union( ).cte() return query.union_all( states_query_for_entity_ids(start_day, end_day, entity_ids), - select_events_context_only().where(Events.context_id.in_(devices_entities_cte)), + select_events_context_only().where( + Events.context_id.in_(devices_entities_cte.select()) + ), select_states_context_only() .where(States.entity_id.not_in(entity_ids)) - .where(States.context_id.in_(devices_entities_cte)), + .where(States.context_id.in_(devices_entities_cte.select())), ) From 784fbf3291160eea3d7d62bc2a2ef3c54beea3d4 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 19:50:15 +0200 Subject: [PATCH 589/930] Cleanup nest async method which is not awaiting (#72096) --- homeassistant/components/nest/device_trigger.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index 67b361e76d8..1f7f2b642dc 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -15,7 +15,7 @@ from homeassistant.components.device_automation.exceptions import ( ) from homeassistant.components.homeassistant.triggers import event as event_trigger from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE -from homeassistant.core import CALLBACK_TYPE, HomeAssistant +from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType @@ -33,7 +33,8 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( ) -async def async_get_nest_device_id(hass: HomeAssistant, device_id: str) -> str | None: +@callback +def async_get_nest_device_id(hass: HomeAssistant, device_id: str) -> str | None: """Get the nest API device_id from the HomeAssistant device_id.""" device_registry = dr.async_get(hass) if device := device_registry.async_get(device_id): @@ -67,7 +68,7 @@ async def async_get_triggers( hass: HomeAssistant, device_id: str ) -> list[dict[str, Any]]: """List device triggers for a Nest device.""" - nest_device_id = await async_get_nest_device_id(hass, device_id) + nest_device_id = async_get_nest_device_id(hass, device_id) if not nest_device_id: raise InvalidDeviceAutomationConfig(f"Device not found {device_id}") trigger_types = await async_get_device_trigger_types(hass, nest_device_id) From e300908a8e732e4a9ec3f4a7245bfe4570e8c737 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Wed, 18 May 2022 19:59:35 +0200 Subject: [PATCH 590/930] Refresh camera stream source of Synology DSM connected cameras (#70938) Co-authored-by: Paulus Schoutsen --- .../components/synology_dsm/__init__.py | 24 +++++++++++++--- .../components/synology_dsm/camera.py | 28 ++++++++++++++++++- .../components/synology_dsm/const.py | 3 ++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/synology_dsm/__init__.py b/homeassistant/components/synology_dsm/__init__.py index 8dbdecb9305..ece38bf7326 100644 --- a/homeassistant/components/synology_dsm/__init__.py +++ b/homeassistant/components/synology_dsm/__init__.py @@ -23,6 +23,7 @@ from homeassistant.const import CONF_MAC, CONF_SCAN_INTERVAL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_validation as cv, device_registry as dr +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .common import SynoApi @@ -36,6 +37,7 @@ from .const import ( EXCEPTION_DETAILS, EXCEPTION_UNKNOWN, PLATFORMS, + SIGNAL_CAMERA_SOURCE_CHANGED, SYNO_API, SYSTEM_LOADED, UNDO_UPDATE_LISTENER, @@ -123,6 +125,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return None surveillance_station = api.surveillance_station + current_data: dict[str, SynoCamera] = { + camera.id: camera for camera in surveillance_station.get_all_cameras() + } try: async with async_timeout.timeout(30): @@ -130,12 +135,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except SynologyDSMAPIErrorException as err: raise UpdateFailed(f"Error communicating with API: {err}") from err - return { - "cameras": { - camera.id: camera for camera in surveillance_station.get_all_cameras() - } + new_data: dict[str, SynoCamera] = { + camera.id: camera for camera in surveillance_station.get_all_cameras() } + for cam_id, cam_data_new in new_data.items(): + if ( + (cam_data_current := current_data.get(cam_id)) is not None + and cam_data_current.live_view.rtsp != cam_data_new.live_view.rtsp + ): + async_dispatcher_send( + hass, + f"{SIGNAL_CAMERA_SOURCE_CHANGED}_{entry.entry_id}_{cam_id}", + cam_data_new.live_view.rtsp, + ) + + return {"cameras": new_data} + async def async_coordinator_update_data_central() -> None: """Fetch all device and sensor data from api.""" try: diff --git a/homeassistant/components/synology_dsm/camera.py b/homeassistant/components/synology_dsm/camera.py index c6d44d8883d..cab2536187c 100644 --- a/homeassistant/components/synology_dsm/camera.py +++ b/homeassistant/components/synology_dsm/camera.py @@ -16,7 +16,8 @@ from homeassistant.components.camera import ( CameraEntityFeature, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -27,6 +28,7 @@ from .const import ( COORDINATOR_CAMERAS, DEFAULT_SNAPSHOT_QUALITY, DOMAIN, + SIGNAL_CAMERA_SOURCE_CHANGED, SYNO_API, ) from .entity import SynologyDSMBaseEntity, SynologyDSMEntityDescription @@ -130,6 +132,29 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): """Return the camera motion detection status.""" return self.camera_data.is_motion_detection_enabled # type: ignore[no-any-return] + def _listen_source_updates(self) -> None: + """Listen for camera source changed events.""" + + @callback + def _handle_signal(url: str) -> None: + if self.stream: + _LOGGER.debug("Update stream URL for camera %s", self.camera_data.name) + self.stream.update_source(url) + + assert self.platform + assert self.platform.config_entry + self.async_on_remove( + async_dispatcher_connect( + self.hass, + f"{SIGNAL_CAMERA_SOURCE_CHANGED}_{self.platform.config_entry.entry_id}_{self.camera_data.id}", + _handle_signal, + ) + ) + + async def async_added_to_hass(self) -> None: + """Subscribe to signal.""" + self._listen_source_updates() + def camera_image( self, width: int | None = None, height: int | None = None ) -> bytes | None: @@ -162,6 +187,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): ) if not self.available: return None + return self.camera_data.live_view.rtsp # type: ignore[no-any-return] def enable_motion_detection(self) -> None: diff --git a/homeassistant/components/synology_dsm/const.py b/homeassistant/components/synology_dsm/const.py index 1b4e5f0bb36..f716130a5e4 100644 --- a/homeassistant/components/synology_dsm/const.py +++ b/homeassistant/components/synology_dsm/const.py @@ -43,6 +43,9 @@ DEFAULT_SNAPSHOT_QUALITY = SNAPSHOT_PROFILE_BALANCED ENTITY_UNIT_LOAD = "load" +# Signals +SIGNAL_CAMERA_SOURCE_CHANGED = "synology_dsm.camera_stream_source_changed" + # Services SERVICE_REBOOT = "reboot" SERVICE_SHUTDOWN = "shutdown" From 349347cade1c20b163233e05f08a82f07ad7321d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 20:04:42 +0200 Subject: [PATCH 591/930] Cleanup unused import in SamsungTV (#72102) --- homeassistant/components/samsungtv/media_player.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 423a778011d..6a884c59a87 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio from collections.abc import Coroutine, Sequence -import contextlib from datetime import datetime, timedelta from typing import Any From 99941b1c32262f5bdeb5b9a7dce10d176c9e6f8f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 May 2022 21:04:06 +0200 Subject: [PATCH 592/930] Warn on use of deprecated async_get_registry (#72088) Co-authored-by: Paulus Schoutsen --- homeassistant/helpers/area_registry.py | 4 ++++ homeassistant/helpers/device_registry.py | 3 +++ homeassistant/helpers/entity_registry.py | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/homeassistant/helpers/area_registry.py b/homeassistant/helpers/area_registry.py index ad4930825c8..e5d35ccbf44 100644 --- a/homeassistant/helpers/area_registry.py +++ b/homeassistant/helpers/area_registry.py @@ -12,6 +12,7 @@ from homeassistant.loader import bind_hass from homeassistant.util import slugify from . import device_registry as dr, entity_registry as er +from .frame import report from .storage import Store from .typing import UNDEFINED, UndefinedType @@ -226,6 +227,9 @@ async def async_get_registry(hass: HomeAssistant) -> AreaRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ + report( + "uses deprecated `async_get_registry` to access area registry, use async_get instead" + ) return async_get(hass) diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 901242607e3..ed3d5a7b06f 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -720,6 +720,9 @@ async def async_get_registry(hass: HomeAssistant) -> DeviceRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ + report( + "uses deprecated `async_get_registry` to access device registry, use async_get instead" + ) return async_get(hass) diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 2c87c4cbdd2..d03d272b1ac 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -45,6 +45,7 @@ from homeassistant.util.yaml import load_yaml from . import device_registry as dr, storage from .device_registry import EVENT_DEVICE_REGISTRY_UPDATED +from .frame import report from .typing import UNDEFINED, UndefinedType if TYPE_CHECKING: @@ -819,6 +820,9 @@ async def async_get_registry(hass: HomeAssistant) -> EntityRegistry: This is deprecated and will be removed in the future. Use async_get instead. """ + report( + "uses deprecated `async_get_registry` to access entity registry, use async_get instead" + ) return async_get(hass) From 18b40990a258ec81fceace827819b98cbd57cc80 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 18 May 2022 13:22:30 -0700 Subject: [PATCH 593/930] Bump frontend to 20220518.0 (#72106) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 0040dece159..5cf48e76d4b 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220516.0"], + "requirements": ["home-assistant-frontend==20220518.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index db6433c65b7..4e6174db561 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220516.0 +home-assistant-frontend==20220518.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 94ab58682ff..bddac66cb90 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220516.0 +home-assistant-frontend==20220518.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3adafb068de..522583f4ce4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220516.0 +home-assistant-frontend==20220518.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 274557361002a168fb14519f30f4590aeac42142 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 15:35:35 -0500 Subject: [PATCH 594/930] Small cleanups lutron_caseta (#72099) --- .../components/lutron_caseta/__init__.py | 9 ++++++ .../components/lutron_caseta/binary_sensor.py | 18 ++++-------- .../components/lutron_caseta/cover.py | 29 +++++-------------- homeassistant/components/lutron_caseta/fan.py | 29 ++++--------------- .../components/lutron_caseta/light.py | 23 ++++----------- .../components/lutron_caseta/scene.py | 8 +---- .../components/lutron_caseta/switch.py | 24 ++++----------- 7 files changed, 41 insertions(+), 99 deletions(-) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index e9ada82bb54..3daa45d30ee 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -353,3 +353,12 @@ class LutronCasetaDevice(Entity): def extra_state_attributes(self): """Return the state attributes.""" return {"device_id": self.device_id, "zone_id": self._device["zone"]} + + +class LutronCasetaDeviceUpdatableEntity(LutronCasetaDevice): + """A lutron_caseta entity that can update by syncing data from the bridge.""" + + async def async_update(self): + """Update when forcing a refresh of the device.""" + self._device = self._smartbridge.get_device_by_id(self.device_id) + _LOGGER.debug(self._device) diff --git a/homeassistant/components/lutron_caseta/binary_sensor.py b/homeassistant/components/lutron_caseta/binary_sensor.py index f61e644a331..6a6e3853280 100644 --- a/homeassistant/components/lutron_caseta/binary_sensor.py +++ b/homeassistant/components/lutron_caseta/binary_sensor.py @@ -26,22 +26,21 @@ async def async_setup_entry( Adds occupancy groups from the Caseta bridge associated with the config_entry as binary_sensor entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] bridge_device = data[BRIDGE_DEVICE] occupancy_groups = bridge.occupancy_groups - - for occupancy_group in occupancy_groups.values(): - entity = LutronOccupancySensor(occupancy_group, bridge, bridge_device) - entities.append(entity) - - async_add_entities(entities, True) + async_add_entities( + LutronOccupancySensor(occupancy_group, bridge, bridge_device) + for occupancy_group in occupancy_groups.values() + ) class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity): """Representation of a Lutron occupancy group.""" + _attr_device_class = BinarySensorDeviceClass.OCCUPANCY + def __init__(self, device, bridge, bridge_device): """Init an occupancy sensor.""" super().__init__(device, bridge, bridge_device) @@ -59,11 +58,6 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity): info[ATTR_SUGGESTED_AREA] = area self._attr_device_info = info - @property - def device_class(self): - """Flag supported features.""" - return BinarySensorDeviceClass.OCCUPANCY - @property def is_on(self): """Return the brightness of the light.""" diff --git a/homeassistant/components/lutron_caseta/cover.py b/homeassistant/components/lutron_caseta/cover.py index 61c9c42a1b0..afddb2677a7 100644 --- a/homeassistant/components/lutron_caseta/cover.py +++ b/homeassistant/components/lutron_caseta/cover.py @@ -1,5 +1,4 @@ """Support for Lutron Caseta shades.""" -import logging from homeassistant.components.cover import ( ATTR_POSITION, @@ -12,11 +11,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import LutronCasetaDevice +from . import LutronCasetaDeviceUpdatableEntity from .const import BRIDGE_DEVICE, BRIDGE_LEAP, DOMAIN as CASETA_DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry( hass: HomeAssistant, @@ -28,20 +25,17 @@ async def async_setup_entry( Adds shades from the Caseta bridge associated with the config_entry as cover entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] bridge_device = data[BRIDGE_DEVICE] cover_devices = bridge.get_devices_by_domain(DOMAIN) - - for cover_device in cover_devices: - entity = LutronCasetaCover(cover_device, bridge, bridge_device) - entities.append(entity) - - async_add_entities(entities, True) + async_add_entities( + LutronCasetaCover(cover_device, bridge, bridge_device) + for cover_device in cover_devices + ) -class LutronCasetaCover(LutronCasetaDevice, CoverEntity): +class LutronCasetaCover(LutronCasetaDeviceUpdatableEntity, CoverEntity): """Representation of a Lutron shade.""" _attr_supported_features = ( @@ -50,6 +44,7 @@ class LutronCasetaCover(LutronCasetaDevice, CoverEntity): | CoverEntityFeature.STOP | CoverEntityFeature.SET_POSITION ) + _attr_device_class = CoverDeviceClass.SHADE @property def is_closed(self): @@ -61,11 +56,6 @@ class LutronCasetaCover(LutronCasetaDevice, CoverEntity): """Return the current position of cover.""" return self._device["current_state"] - @property - def device_class(self): - """Return the device class.""" - return CoverDeviceClass.SHADE - async def async_stop_cover(self, **kwargs): """Top the cover.""" await self._smartbridge.stop_cover(self.device_id) @@ -87,8 +77,3 @@ class LutronCasetaCover(LutronCasetaDevice, CoverEntity): if ATTR_POSITION in kwargs: position = kwargs[ATTR_POSITION] await self._smartbridge.set_value(self.device_id, position) - - async def async_update(self): - """Call when forcing a refresh of the device.""" - self._device = self._smartbridge.get_device_by_id(self.device_id) - _LOGGER.debug(self._device) diff --git a/homeassistant/components/lutron_caseta/fan.py b/homeassistant/components/lutron_caseta/fan.py index 00236405735..cdf00959ed1 100644 --- a/homeassistant/components/lutron_caseta/fan.py +++ b/homeassistant/components/lutron_caseta/fan.py @@ -1,8 +1,6 @@ """Support for Lutron Caseta fans.""" from __future__ import annotations -import logging - from pylutron_caseta import FAN_HIGH, FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_OFF from homeassistant.components.fan import DOMAIN, FanEntity, FanEntityFeature @@ -14,11 +12,9 @@ from homeassistant.util.percentage import ( percentage_to_ordered_list_item, ) -from . import LutronCasetaDevice +from . import LutronCasetaDeviceUpdatableEntity from .const import BRIDGE_DEVICE, BRIDGE_LEAP, DOMAIN as CASETA_DOMAIN -_LOGGER = logging.getLogger(__name__) - DEFAULT_ON_PERCENTAGE = 50 ORDERED_NAMED_FAN_SPEEDS = [FAN_LOW, FAN_MEDIUM, FAN_MEDIUM_HIGH, FAN_HIGH] @@ -33,23 +29,20 @@ async def async_setup_entry( Adds fan controllers from the Caseta bridge associated with the config_entry as fan entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] bridge_device = data[BRIDGE_DEVICE] fan_devices = bridge.get_devices_by_domain(DOMAIN) - - for fan_device in fan_devices: - entity = LutronCasetaFan(fan_device, bridge, bridge_device) - entities.append(entity) - - async_add_entities(entities, True) + async_add_entities( + LutronCasetaFan(fan_device, bridge, bridge_device) for fan_device in fan_devices + ) -class LutronCasetaFan(LutronCasetaDevice, FanEntity): +class LutronCasetaFan(LutronCasetaDeviceUpdatableEntity, FanEntity): """Representation of a Lutron Caseta fan. Including Fan Speed.""" _attr_supported_features = FanEntityFeature.SET_SPEED + _attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS) @property def percentage(self) -> int | None: @@ -62,11 +55,6 @@ class LutronCasetaFan(LutronCasetaDevice, FanEntity): ORDERED_NAMED_FAN_SPEEDS, self._device["fan_speed"] ) - @property - def speed_count(self) -> int: - """Return the number of speeds the fan supports.""" - return len(ORDERED_NAMED_FAN_SPEEDS) - async def async_turn_on( self, percentage: int = None, @@ -98,8 +86,3 @@ class LutronCasetaFan(LutronCasetaDevice, FanEntity): def is_on(self): """Return true if device is on.""" return self.percentage and self.percentage > 0 - - async def async_update(self): - """Update when forcing a refresh of the device.""" - self._device = self._smartbridge.get_device_by_id(self.device_id) - _LOGGER.debug("State of this lutron fan device is %s", self._device) diff --git a/homeassistant/components/lutron_caseta/light.py b/homeassistant/components/lutron_caseta/light.py index cba4450bf27..a58fc21aadf 100644 --- a/homeassistant/components/lutron_caseta/light.py +++ b/homeassistant/components/lutron_caseta/light.py @@ -1,6 +1,5 @@ """Support for Lutron Caseta lights.""" from datetime import timedelta -import logging from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -14,11 +13,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import LutronCasetaDevice +from . import LutronCasetaDeviceUpdatableEntity from .const import BRIDGE_DEVICE, BRIDGE_LEAP, DOMAIN as CASETA_DOMAIN -_LOGGER = logging.getLogger(__name__) - def to_lutron_level(level): """Convert the given Home Assistant light level (0-255) to Lutron (0-100).""" @@ -40,20 +37,17 @@ async def async_setup_entry( Adds dimmers from the Caseta bridge associated with the config_entry as light entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] bridge_device = data[BRIDGE_DEVICE] light_devices = bridge.get_devices_by_domain(DOMAIN) - - for light_device in light_devices: - entity = LutronCasetaLight(light_device, bridge, bridge_device) - entities.append(entity) - - async_add_entities(entities, True) + async_add_entities( + LutronCasetaLight(light_device, bridge, bridge_device) + for light_device in light_devices + ) -class LutronCasetaLight(LutronCasetaDevice, LightEntity): +class LutronCasetaLight(LutronCasetaDeviceUpdatableEntity, LightEntity): """Representation of a Lutron Light, including dimmable.""" _attr_color_mode = ColorMode.BRIGHTNESS @@ -88,8 +82,3 @@ class LutronCasetaLight(LutronCasetaDevice, LightEntity): def is_on(self): """Return true if device is on.""" return self._device["current_state"] > 0 - - async def async_update(self): - """Call when forcing a refresh of the device.""" - self._device = self._smartbridge.get_device_by_id(self.device_id) - _LOGGER.debug(self._device) diff --git a/homeassistant/components/lutron_caseta/scene.py b/homeassistant/components/lutron_caseta/scene.py index d81a741359c..bb932c61316 100644 --- a/homeassistant/components/lutron_caseta/scene.py +++ b/homeassistant/components/lutron_caseta/scene.py @@ -19,16 +19,10 @@ async def async_setup_entry( Adds scenes from the Caseta bridge associated with the config_entry as scene entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] scenes = bridge.get_scenes() - - for scene in scenes: - entity = LutronCasetaScene(scenes[scene], bridge) - entities.append(entity) - - async_add_entities(entities, True) + async_add_entities(LutronCasetaScene(scenes[scene], bridge) for scene in scenes) class LutronCasetaScene(Scene): diff --git a/homeassistant/components/lutron_caseta/switch.py b/homeassistant/components/lutron_caseta/switch.py index 836968c3796..7e963352264 100644 --- a/homeassistant/components/lutron_caseta/switch.py +++ b/homeassistant/components/lutron_caseta/switch.py @@ -1,16 +1,13 @@ """Support for Lutron Caseta switches.""" -import logging from homeassistant.components.switch import DOMAIN, SwitchEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import LutronCasetaDevice +from . import LutronCasetaDeviceUpdatableEntity from .const import BRIDGE_DEVICE, BRIDGE_LEAP, DOMAIN as CASETA_DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry( hass: HomeAssistant, @@ -22,21 +19,17 @@ async def async_setup_entry( Adds switches from the Caseta bridge associated with the config_entry as switch entities. """ - entities = [] data = hass.data[CASETA_DOMAIN][config_entry.entry_id] bridge = data[BRIDGE_LEAP] bridge_device = data[BRIDGE_DEVICE] switch_devices = bridge.get_devices_by_domain(DOMAIN) - - for switch_device in switch_devices: - entity = LutronCasetaLight(switch_device, bridge, bridge_device) - entities.append(entity) - - async_add_entities(entities, True) - return True + async_add_entities( + LutronCasetaLight(switch_device, bridge, bridge_device) + for switch_device in switch_devices + ) -class LutronCasetaLight(LutronCasetaDevice, SwitchEntity): +class LutronCasetaLight(LutronCasetaDeviceUpdatableEntity, SwitchEntity): """Representation of a Lutron Caseta switch.""" async def async_turn_on(self, **kwargs): @@ -51,8 +44,3 @@ class LutronCasetaLight(LutronCasetaDevice, SwitchEntity): def is_on(self): """Return true if device is on.""" return self._device["current_state"] > 0 - - async def async_update(self): - """Update when forcing a refresh of the device.""" - self._device = self._smartbridge.get_device_by_id(self.device_id) - _LOGGER.debug(self._device) From 5e59c3fd6d749c6885b7735019203ce118411ac9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 15:36:47 -0500 Subject: [PATCH 595/930] Add switch platform to Big Ass Fans (#71954) --- .coveragerc | 1 + homeassistant/components/baf/__init__.py | 2 +- homeassistant/components/baf/switch.py | 148 +++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/baf/switch.py diff --git a/.coveragerc b/.coveragerc index bd7aa405b94..fbe98e4594f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -97,6 +97,7 @@ omit = homeassistant/components/baf/entity.py homeassistant/components/baf/fan.py homeassistant/components/baf/sensor.py + homeassistant/components/baf/switch.py homeassistant/components/baidu/tts.py homeassistant/components/balboa/__init__.py homeassistant/components/beewi_smartclim/sensor.py diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py index f6401cbbc40..6e76014f0b7 100644 --- a/homeassistant/components/baf/__init__.py +++ b/homeassistant/components/baf/__init__.py @@ -14,7 +14,7 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import DOMAIN, QUERY_INTERVAL, RUN_TIMEOUT from .models import BAFData -PLATFORMS: list[Platform] = [Platform.FAN, Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.FAN, Platform.SENSOR, Platform.SWITCH] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/baf/switch.py b/homeassistant/components/baf/switch.py new file mode 100644 index 00000000000..6cefa0db65d --- /dev/null +++ b/homeassistant/components/baf/switch.py @@ -0,0 +1,148 @@ +"""Support for Big Ass Fans switch.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any, Optional, cast + +from aiobafi6 import Device + +from homeassistant import config_entries +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import BAFEntity +from .models import BAFData + + +@dataclass +class BAFSwitchDescriptionMixin: + """Required values for BAF sensors.""" + + value_fn: Callable[[Device], bool | None] + + +@dataclass +class BAFSwitchDescription( + SwitchEntityDescription, + BAFSwitchDescriptionMixin, +): + """Class describing BAF switch entities.""" + + +BASE_SWITCHES = [ + BAFSwitchDescription( + key="legacy_ir_remote_enable", + name="Legacy IR Remote", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.legacy_ir_remote_enable), + ), + BAFSwitchDescription( + key="led_indicators_enable", + name="Led Indicators", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.led_indicators_enable), + ), +] + +FAN_SWITCHES = [ + BAFSwitchDescription( + key="comfort_heat_assist_enable", + name="Auto Comfort Heat Assist", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.comfort_heat_assist_enable), + ), + BAFSwitchDescription( + key="fan_beep_enable", + name="Beep", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.fan_beep_enable), + ), + BAFSwitchDescription( + key="eco_enable", + name="Eco Mode", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.eco_enable), + ), + BAFSwitchDescription( + key="motion_sense_enable", + name="Motion Sense", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.motion_sense_enable), + ), + BAFSwitchDescription( + key="return_to_auto_enable", + name="Return to Auto", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.return_to_auto_enable), + ), + BAFSwitchDescription( + key="whoosh_enable", + name="Whoosh", + # Not a configuration switch + value_fn=lambda device: cast(Optional[bool], device.whoosh_enable), + ), +] + + +LIGHT_SWITCHES = [ + BAFSwitchDescription( + key="light_dim_to_warm_enable", + name="Dim to Warm", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[bool], device.light_dim_to_warm_enable), + ), + BAFSwitchDescription( + key="light_return_to_auto_enable", + name="Light Return to Auto", + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast( + Optional[bool], device.light_return_to_auto_enable + ), + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up BAF fan switches.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + device = data.device + descriptions: list[BAFSwitchDescription] = [] + descriptions.extend(BASE_SWITCHES) + if device.has_fan: + descriptions.extend(FAN_SWITCHES) + if device.has_light: + descriptions.extend(LIGHT_SWITCHES) + async_add_entities(BAFSwitch(device, description) for description in descriptions) + + +class BAFSwitch(BAFEntity, SwitchEntity): + """BAF switch component.""" + + entity_description: BAFSwitchDescription + + def __init__(self, device: Device, description: BAFSwitchDescription) -> None: + """Initialize the entity.""" + self.entity_description = description + super().__init__(device, f"{device.name} {description.name}") + self._attr_unique_id = f"{self._device.mac_address}-{description.key}" + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + self._attr_is_on = self.entity_description.value_fn(self._device) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on the switch.""" + setattr(self._device, self.entity_description.key, True) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off the switch.""" + setattr(self._device, self.entity_description.key, False) From bf63d381b2d4daf6e923d2b738bc9b57c2f03b2a Mon Sep 17 00:00:00 2001 From: Jeef Date: Wed, 18 May 2022 14:54:52 -0600 Subject: [PATCH 596/930] IntelliFire On/Off Switches (#70377) Co-authored-by: J. Nick Koston --- .coveragerc | 1 + .../components/intellifire/__init__.py | 2 +- .../components/intellifire/switch.py | 93 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/intellifire/switch.py diff --git a/.coveragerc b/.coveragerc index fbe98e4594f..5fabf2851fb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -550,6 +550,7 @@ omit = homeassistant/components/intellifire/coordinator.py homeassistant/components/intellifire/binary_sensor.py homeassistant/components/intellifire/sensor.py + homeassistant/components/intellifire/switch.py homeassistant/components/intellifire/entity.py homeassistant/components/incomfort/* homeassistant/components/intesishome/* diff --git a/homeassistant/components/intellifire/__init__.py b/homeassistant/components/intellifire/__init__.py index e139626b88c..83c6e05f572 100644 --- a/homeassistant/components/intellifire/__init__.py +++ b/homeassistant/components/intellifire/__init__.py @@ -13,7 +13,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from .const import DOMAIN, LOGGER from .coordinator import IntellifireDataUpdateCoordinator -PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/intellifire/switch.py b/homeassistant/components/intellifire/switch.py new file mode 100644 index 00000000000..9c196a59fd4 --- /dev/null +++ b/homeassistant/components/intellifire/switch.py @@ -0,0 +1,93 @@ +"""Define switch func.""" +from __future__ import annotations + +from collections.abc import Awaitable, Callable +from dataclasses import dataclass +from typing import Any + +from intellifire4py import IntellifireControlAsync, IntellifirePollData + +from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import IntellifireDataUpdateCoordinator +from .entity import IntellifireEntity + + +@dataclass() +class IntellifireSwitchRequiredKeysMixin: + """Mixin for required keys.""" + + on_fn: Callable[[IntellifireControlAsync], Awaitable] + off_fn: Callable[[IntellifireControlAsync], Awaitable] + value_fn: Callable[[IntellifirePollData], bool] + + +@dataclass +class IntellifireSwitchEntityDescription( + SwitchEntityDescription, IntellifireSwitchRequiredKeysMixin +): + """Describes a switch entity.""" + + +INTELLIFIRE_SWITCHES: tuple[IntellifireSwitchEntityDescription, ...] = ( + IntellifireSwitchEntityDescription( + key="on_off", + name="Flame", + on_fn=lambda control_api: control_api.flame_on( + fireplace=control_api.default_fireplace + ), + off_fn=lambda control_api: control_api.flame_off( + fireplace=control_api.default_fireplace + ), + value_fn=lambda data: data.is_on, + ), + IntellifireSwitchEntityDescription( + key="pilot", + name="Pilot Light", + icon="mdi:fire-alert", + on_fn=lambda control_api: control_api.pilot_on( + fireplace=control_api.default_fireplace + ), + off_fn=lambda control_api: control_api.pilot_off( + fireplace=control_api.default_fireplace + ), + value_fn=lambda data: data.pilot_on, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Configure switch entities.""" + coordinator: IntellifireDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + IntellifireSwitch(coordinator=coordinator, description=description) + for description in INTELLIFIRE_SWITCHES + ) + + +class IntellifireSwitch(IntellifireEntity, SwitchEntity): + """Define an Intellifire Switch.""" + + entity_description: IntellifireSwitchEntityDescription + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on the switch.""" + await self.entity_description.on_fn(self.coordinator.control_api) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off the switch.""" + await self.entity_description.off_fn(self.coordinator.control_api) + + @property + def is_on(self) -> bool | None: + """Return the on state.""" + return self.entity_description.value_fn(self.coordinator.read_api.data) From d8a580a90f8bf3206b31619493f4e653fceb3f4b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 18:08:02 -0500 Subject: [PATCH 597/930] Update nexia to use asyncio (#72108) --- homeassistant/components/nexia/__init__.py | 40 ++++++----- homeassistant/components/nexia/climate.py | 72 ++++++++++--------- homeassistant/components/nexia/config_flow.py | 30 ++++---- homeassistant/components/nexia/coordinator.py | 2 +- homeassistant/components/nexia/entity.py | 13 ++-- homeassistant/components/nexia/manifest.json | 2 +- homeassistant/components/nexia/scene.py | 8 ++- homeassistant/components/nexia/sensor.py | 3 +- homeassistant/components/nexia/switch.py | 8 +-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/nexia/test_config_flow.py | 22 +++--- tests/components/nexia/util.py | 15 ++-- 13 files changed, 121 insertions(+), 98 deletions(-) diff --git a/homeassistant/components/nexia/__init__.py b/homeassistant/components/nexia/__init__.py index 634f69b49d3..ab491c0a271 100644 --- a/homeassistant/components/nexia/__init__.py +++ b/homeassistant/components/nexia/__init__.py @@ -1,15 +1,16 @@ """Support for Nexia / Trane XL Thermostats.""" -from functools import partial +import asyncio import logging +import aiohttp from nexia.const import BRAND_NEXIA from nexia.home import NexiaHome -from requests.exceptions import ConnectTimeout, HTTPError from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv from .const import CONF_BRAND, DOMAIN, PLATFORMS @@ -30,31 +31,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: brand = conf.get(CONF_BRAND, BRAND_NEXIA) state_file = hass.config.path(f"nexia_config_{username}.conf") + session = async_get_clientsession(hass) + nexia_home = NexiaHome( + session, + username=username, + password=password, + device_name=hass.config.location_name, + state_file=state_file, + brand=brand, + ) try: - nexia_home = await hass.async_add_executor_job( - partial( - NexiaHome, - username=username, - password=password, - device_name=hass.config.location_name, - state_file=state_file, - brand=brand, - ) - ) - except ConnectTimeout as ex: - _LOGGER.error("Unable to connect to Nexia service: %s", ex) - raise ConfigEntryNotReady from ex - except HTTPError as http_ex: - if is_invalid_auth_code(http_ex.response.status_code): + await nexia_home.login() + except asyncio.TimeoutError as ex: + raise ConfigEntryNotReady( + f"Timed out trying to connect to Nexia service: {ex}" + ) from ex + except aiohttp.ClientResponseError as http_ex: + if is_invalid_auth_code(http_ex.status): _LOGGER.error( "Access error from Nexia service, please check credentials: %s", http_ex ) return False - _LOGGER.error("HTTP error from Nexia service: %s", http_ex) - raise ConfigEntryNotReady from http_ex + raise ConfigEntryNotReady(f"Error from Nexia service: {http_ex}") from http_ex coordinator = NexiaDataUpdateCoordinator(hass, nexia_home) + await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 7eeb04c5676..0c27b1497f5 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -123,13 +123,17 @@ async def async_setup_entry( platform.async_register_entity_service( SERVICE_SET_HUMIDIFY_SETPOINT, SET_HUMIDITY_SCHEMA, - SERVICE_SET_HUMIDIFY_SETPOINT, + f"async_{SERVICE_SET_HUMIDIFY_SETPOINT}", ) platform.async_register_entity_service( - SERVICE_SET_AIRCLEANER_MODE, SET_AIRCLEANER_SCHEMA, SERVICE_SET_AIRCLEANER_MODE + SERVICE_SET_AIRCLEANER_MODE, + SET_AIRCLEANER_SCHEMA, + f"async_{SERVICE_SET_AIRCLEANER_MODE}", ) platform.async_register_entity_service( - SERVICE_SET_HVAC_RUN_MODE, SET_HVAC_RUN_MODE_SCHEMA, SERVICE_SET_HVAC_RUN_MODE + SERVICE_SET_HVAC_RUN_MODE, + SET_HVAC_RUN_MODE_SCHEMA, + f"async_{SERVICE_SET_HVAC_RUN_MODE}", ) entities: list[NexiaZone] = [] @@ -192,20 +196,20 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): """Return the fan setting.""" return self._thermostat.get_fan_mode() - def set_fan_mode(self, fan_mode): + async def async_set_fan_mode(self, fan_mode): """Set new target fan mode.""" - self._thermostat.set_fan_mode(fan_mode) + await self._thermostat.set_fan_mode(fan_mode) self._signal_thermostat_update() - def set_hvac_run_mode(self, run_mode, hvac_mode): + async def async_set_hvac_run_mode(self, run_mode, hvac_mode): """Set the hvac run mode.""" if run_mode is not None: if run_mode == HOLD_PERMANENT: - self._zone.call_permanent_hold() + await self._zone.call_permanent_hold() else: - self._zone.call_return_to_schedule() + await self._zone.call_return_to_schedule() if hvac_mode is not None: - self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) + await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) self._signal_thermostat_update() @property @@ -213,12 +217,12 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): """Preset that is active.""" return self._zone.get_preset() - def set_humidity(self, humidity): + async def async_set_humidity(self, humidity): """Dehumidify target.""" if self._thermostat.has_dehumidify_support(): - self.set_dehumidify_setpoint(humidity) + await self.async_set_dehumidify_setpoint(humidity) else: - self.set_humidify_setpoint(humidity) + await self.async_set_humidify_setpoint(humidity) self._signal_thermostat_update() @property @@ -300,7 +304,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): return NEXIA_TO_HA_HVAC_MODE_MAP[mode] - def set_temperature(self, **kwargs): + async def async_set_temperature(self, **kwargs): """Set target temperature.""" new_heat_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) new_cool_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) @@ -332,7 +336,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): ): new_heat_temp = new_cool_temp - deadband - self._zone.set_heat_cool_temp( + await self._zone.set_heat_cool_temp( heat_temperature=new_heat_temp, cool_temperature=new_cool_temp, set_temperature=set_temp, @@ -366,63 +370,63 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): return data - def set_preset_mode(self, preset_mode: str): + async def async_set_preset_mode(self, preset_mode: str): """Set the preset mode.""" - self._zone.set_preset(preset_mode) + await self._zone.set_preset(preset_mode) self._signal_zone_update() - def turn_aux_heat_off(self): + async def async_turn_aux_heat_off(self): """Turn. Aux Heat off.""" - self._thermostat.set_emergency_heat(False) + await self._thermostat.set_emergency_heat(False) self._signal_thermostat_update() - def turn_aux_heat_on(self): + async def async_turn_aux_heat_on(self): """Turn. Aux Heat on.""" self._thermostat.set_emergency_heat(True) self._signal_thermostat_update() - def turn_off(self): + async def async_turn_off(self): """Turn. off the zone.""" - self.set_hvac_mode(OPERATION_MODE_OFF) + await self.set_hvac_mode(OPERATION_MODE_OFF) self._signal_zone_update() - def turn_on(self): + async def async_turn_on(self): """Turn. on the zone.""" - self.set_hvac_mode(OPERATION_MODE_AUTO) + await self.set_hvac_mode(OPERATION_MODE_AUTO) self._signal_zone_update() - def set_hvac_mode(self, hvac_mode: HVACMode) -> None: + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the system mode (Auto, Heat_Cool, Cool, Heat, etc).""" if hvac_mode == HVACMode.AUTO: - self._zone.call_return_to_schedule() - self._zone.set_mode(mode=OPERATION_MODE_AUTO) + await self._zone.call_return_to_schedule() + await self._zone.set_mode(mode=OPERATION_MODE_AUTO) else: - self._zone.call_permanent_hold() - self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) + await self._zone.call_permanent_hold() + await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) self._signal_zone_update() - def set_aircleaner_mode(self, aircleaner_mode): + async def async_set_aircleaner_mode(self, aircleaner_mode): """Set the aircleaner mode.""" - self._thermostat.set_air_cleaner(aircleaner_mode) + await self._thermostat.set_air_cleaner(aircleaner_mode) self._signal_thermostat_update() - def set_humidify_setpoint(self, humidity): + async def async_set_humidify_setpoint(self, humidity): """Set the humidify setpoint.""" target_humidity = find_humidity_setpoint(humidity / 100.0) if self._thermostat.get_humidify_setpoint() == target_humidity: # Trying to set the humidify setpoint to the # same value will cause the api to timeout return - self._thermostat.set_humidify_setpoint(target_humidity) + await self._thermostat.set_humidify_setpoint(target_humidity) self._signal_thermostat_update() - def set_dehumidify_setpoint(self, humidity): + async def async_set_dehumidify_setpoint(self, humidity): """Set the dehumidify setpoint.""" target_humidity = find_humidity_setpoint(humidity / 100.0) if self._thermostat.get_dehumidify_setpoint() == target_humidity: # Trying to set the dehumidify setpoint to the # same value will cause the api to timeout return - self._thermostat.set_dehumidify_setpoint(target_humidity) + await self._thermostat.set_dehumidify_setpoint(target_humidity) self._signal_thermostat_update() diff --git a/homeassistant/components/nexia/config_flow.py b/homeassistant/components/nexia/config_flow.py index 4e48123a5de..de5640beef7 100644 --- a/homeassistant/components/nexia/config_flow.py +++ b/homeassistant/components/nexia/config_flow.py @@ -1,13 +1,15 @@ """Config flow for Nexia integration.""" +import asyncio import logging +import aiohttp from nexia.const import BRAND_ASAIR, BRAND_NEXIA, BRAND_TRANE from nexia.home import NexiaHome -from requests.exceptions import ConnectTimeout, HTTPError import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( BRAND_ASAIR_NAME, @@ -44,23 +46,23 @@ async def validate_input(hass: core.HomeAssistant, data): state_file = hass.config.path( f"{data[CONF_BRAND]}_config_{data[CONF_USERNAME]}.conf" ) + session = async_get_clientsession(hass) + nexia_home = NexiaHome( + session, + username=data[CONF_USERNAME], + password=data[CONF_PASSWORD], + brand=data[CONF_BRAND], + device_name=hass.config.location_name, + state_file=state_file, + ) try: - nexia_home = NexiaHome( - username=data[CONF_USERNAME], - password=data[CONF_PASSWORD], - brand=data[CONF_BRAND], - auto_login=False, - auto_update=False, - device_name=hass.config.location_name, - state_file=state_file, - ) - await hass.async_add_executor_job(nexia_home.login) - except ConnectTimeout as ex: + await nexia_home.login() + except asyncio.TimeoutError as ex: _LOGGER.error("Unable to connect to Nexia service: %s", ex) raise CannotConnect from ex - except HTTPError as http_ex: + except aiohttp.ClientResponseError as http_ex: _LOGGER.error("HTTP error from Nexia service: %s", http_ex) - if is_invalid_auth_code(http_ex.response.status_code): + if is_invalid_auth_code(http_ex.status): raise InvalidAuth from http_ex raise CannotConnect from http_ex diff --git a/homeassistant/components/nexia/coordinator.py b/homeassistant/components/nexia/coordinator.py index 7f92ca9354b..ba61a3591f0 100644 --- a/homeassistant/components/nexia/coordinator.py +++ b/homeassistant/components/nexia/coordinator.py @@ -33,4 +33,4 @@ class NexiaDataUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> None: """Fetch data from API endpoint.""" - return await self.hass.async_add_executor_job(self.nexia_home.update) + return await self.nexia_home.update() diff --git a/homeassistant/components/nexia/entity.py b/homeassistant/components/nexia/entity.py index 0be5c05396d..dde0e5ae8f3 100644 --- a/homeassistant/components/nexia/entity.py +++ b/homeassistant/components/nexia/entity.py @@ -3,7 +3,10 @@ from nexia.thermostat import NexiaThermostat from nexia.zone import NexiaThermostatZone from homeassistant.const import ATTR_ATTRIBUTION -from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -20,7 +23,9 @@ from .coordinator import NexiaDataUpdateCoordinator class NexiaEntity(CoordinatorEntity): """Base class for nexia entities.""" - def __init__(self, coordinator, name, unique_id): + def __init__( + self, coordinator: NexiaDataUpdateCoordinator, name: str, unique_id: str + ) -> None: """Initialize the entity.""" super().__init__(coordinator) self._unique_id = unique_id @@ -85,7 +90,7 @@ class NexiaThermostatEntity(NexiaEntity): Update all the zones on the thermostat. """ - dispatcher_send( + async_dispatcher_send( self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}" ) @@ -132,4 +137,4 @@ class NexiaThermostatZoneEntity(NexiaThermostatEntity): Update a single zone. """ - dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}") + async_dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}") diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index 29b80fb00e9..bd1cd58c00b 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia/American Standard/Trane", - "requirements": ["nexia==0.9.13"], + "requirements": ["nexia==1.0.0"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/homeassistant/components/nexia/scene.py b/homeassistant/components/nexia/scene.py index 3dd90b07ca2..28c892fe8e5 100644 --- a/homeassistant/components/nexia/scene.py +++ b/homeassistant/components/nexia/scene.py @@ -1,6 +1,8 @@ """Support for Nexia Automations.""" from typing import Any +from nexia.automation import NexiaAutomation + from homeassistant.components.scene import Scene from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -37,7 +39,9 @@ async def async_setup_entry( class NexiaAutomationScene(NexiaEntity, Scene): """Provides Nexia automation support.""" - def __init__(self, coordinator, automation): + def __init__( + self, coordinator: NexiaDataUpdateCoordinator, automation: NexiaAutomation + ) -> None: """Initialize the automation scene.""" super().__init__( coordinator, @@ -60,7 +64,7 @@ class NexiaAutomationScene(NexiaEntity, Scene): async def async_activate(self, **kwargs: Any) -> None: """Activate an automation scene.""" - await self.hass.async_add_executor_job(self._automation.activate) + await self._automation.activate() async def refresh_callback(_): await self.coordinator.async_refresh() diff --git a/homeassistant/components/nexia/sensor.py b/homeassistant/components/nexia/sensor.py index 8137da8e5cc..3f280581ee7 100644 --- a/homeassistant/components/nexia/sensor.py +++ b/homeassistant/components/nexia/sensor.py @@ -2,6 +2,7 @@ from __future__ import annotations from nexia.const import UNIT_CELSIUS +from nexia.thermostat import NexiaThermostat from homeassistant.components.sensor import ( SensorDeviceClass, @@ -32,7 +33,7 @@ async def async_setup_entry( # Thermostat / System Sensors for thermostat_id in nexia_home.get_thermostat_ids(): - thermostat = nexia_home.get_thermostat_by_id(thermostat_id) + thermostat: NexiaThermostat = nexia_home.get_thermostat_by_id(thermostat_id) entities.append( NexiaThermostatSensor( diff --git a/homeassistant/components/nexia/switch.py b/homeassistant/components/nexia/switch.py index 09bc8a3852e..380fea8c4a0 100644 --- a/homeassistant/components/nexia/switch.py +++ b/homeassistant/components/nexia/switch.py @@ -56,12 +56,12 @@ class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity): """Return the icon for the switch.""" return "mdi:timer-off" if self._zone.is_in_permanent_hold() else "mdi:timer" - def turn_on(self, **kwargs: Any) -> None: + async def async_turn_on(self, **kwargs: Any) -> None: """Enable permanent hold.""" - self._zone.call_permanent_hold() + await self._zone.call_permanent_hold() self._signal_zone_update() - def turn_off(self, **kwargs: Any) -> None: + async def async_turn_off(self, **kwargs: Any) -> None: """Disable permanent hold.""" - self._zone.call_return_to_schedule() + await self._zone.call_return_to_schedule() self._signal_zone_update() diff --git a/requirements_all.txt b/requirements_all.txt index bddac66cb90..05403b5d3aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1077,7 +1077,7 @@ nettigo-air-monitor==1.2.4 neurio==0.3.1 # homeassistant.components.nexia -nexia==0.9.13 +nexia==1.0.0 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 522583f4ce4..d11047f4c95 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -742,7 +742,7 @@ netmap==0.7.0.2 nettigo-air-monitor==1.2.4 # homeassistant.components.nexia -nexia==0.9.13 +nexia==1.0.0 # homeassistant.components.discord nextcord==2.0.0a8 diff --git a/tests/components/nexia/test_config_flow.py b/tests/components/nexia/test_config_flow.py index bd3eb9180e7..5bfd7fb582c 100644 --- a/tests/components/nexia/test_config_flow.py +++ b/tests/components/nexia/test_config_flow.py @@ -1,9 +1,10 @@ """Test the nexia config flow.""" +import asyncio from unittest.mock import MagicMock, patch +import aiohttp from nexia.const import BRAND_ASAIR, BRAND_NEXIA import pytest -from requests.exceptions import ConnectTimeout, HTTPError from homeassistant import config_entries from homeassistant.components.nexia.const import CONF_BRAND, DOMAIN @@ -52,7 +53,10 @@ async def test_form_invalid_auth(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - with patch("homeassistant.components.nexia.config_flow.NexiaHome.login"): + with patch("homeassistant.components.nexia.config_flow.NexiaHome.login",), patch( + "homeassistant.components.nexia.config_flow.NexiaHome.get_name", + return_value=None, + ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { @@ -74,7 +78,7 @@ async def test_form_cannot_connect(hass): with patch( "homeassistant.components.nexia.config_flow.NexiaHome.login", - side_effect=ConnectTimeout, + side_effect=asyncio.TimeoutError, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -95,11 +99,11 @@ async def test_form_invalid_auth_http_401(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - response_mock = MagicMock() - type(response_mock).status_code = 401 with patch( "homeassistant.components.nexia.config_flow.NexiaHome.login", - side_effect=HTTPError(response=response_mock), + side_effect=aiohttp.ClientResponseError( + status=401, request_info=MagicMock(), history=MagicMock() + ), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -120,11 +124,11 @@ async def test_form_cannot_connect_not_found(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) - response_mock = MagicMock() - type(response_mock).status_code = 404 with patch( "homeassistant.components.nexia.config_flow.NexiaHome.login", - side_effect=HTTPError(response=response_mock), + side_effect=aiohttp.ClientResponseError( + status=404, request_info=MagicMock(), history=MagicMock() + ), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], diff --git a/tests/components/nexia/util.py b/tests/components/nexia/util.py index b6d5c697a18..d564ccc351c 100644 --- a/tests/components/nexia/util.py +++ b/tests/components/nexia/util.py @@ -3,13 +3,13 @@ from unittest.mock import patch import uuid from nexia.home import NexiaHome -import requests_mock from homeassistant.components.nexia.const import DOMAIN from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry, load_fixture +from tests.test_util.aiohttp import mock_aiohttp_client async def async_init_integration( @@ -21,17 +21,18 @@ async def async_init_integration( house_fixture = "nexia/mobile_houses_123456.json" session_fixture = "nexia/session_123456.json" sign_in_fixture = "nexia/sign_in.json" - nexia = NexiaHome(auto_login=False) - - with requests_mock.mock() as m, patch( + with mock_aiohttp_client() as mock_session, patch( "nexia.home.load_or_create_uuid", return_value=uuid.uuid4() ): - m.post(nexia.API_MOBILE_SESSION_URL, text=load_fixture(session_fixture)) - m.get( + nexia = NexiaHome(mock_session) + mock_session.post( + nexia.API_MOBILE_SESSION_URL, text=load_fixture(session_fixture) + ) + mock_session.get( nexia.API_MOBILE_HOUSES_URL.format(house_id=123456), text=load_fixture(house_fixture), ) - m.post( + mock_session.post( nexia.API_MOBILE_ACCOUNTS_SIGN_IN_URL, text=load_fixture(sign_in_fixture), ) From 506d09d0580dc8550dda5b0a36390dcf768de411 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Wed, 18 May 2022 17:27:58 -0600 Subject: [PATCH 598/930] Increase timeout for SimpliSafe email-based 2FA to 10 minutes (#72115) Increase timeout for SimpliSafe email-based 2FAA to 10 minutes --- homeassistant/components/simplisafe/config_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/simplisafe/config_flow.py b/homeassistant/components/simplisafe/config_flow.py index 926f904a912..14afc743b23 100644 --- a/homeassistant/components/simplisafe/config_flow.py +++ b/homeassistant/components/simplisafe/config_flow.py @@ -20,7 +20,7 @@ from homeassistant.helpers import aiohttp_client, config_validation as cv from .const import DOMAIN, LOGGER DEFAULT_EMAIL_2FA_SLEEP = 3 -DEFAULT_EMAIL_2FA_TIMEOUT = 300 +DEFAULT_EMAIL_2FA_TIMEOUT = 600 STEP_REAUTH_SCHEMA = vol.Schema( { From 3a13ffcf1376bafff6a32df19b197083abf6e826 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 19 May 2022 00:26:11 +0000 Subject: [PATCH 599/930] [ci skip] Translation update --- .../accuweather/translations/ar.json | 9 +-- .../accuweather/translations/bg.json | 3 +- .../accuweather/translations/ca.json | 7 +- .../accuweather/translations/cs.json | 7 +- .../accuweather/translations/de.json | 7 +- .../accuweather/translations/el.json | 7 +- .../accuweather/translations/en.json | 7 +- .../accuweather/translations/es-419.json | 9 +-- .../accuweather/translations/es.json | 7 +- .../accuweather/translations/et.json | 7 +- .../accuweather/translations/fr.json | 7 +- .../accuweather/translations/he.json | 7 +- .../accuweather/translations/hu.json | 7 +- .../accuweather/translations/id.json | 7 +- .../accuweather/translations/it.json | 7 +- .../accuweather/translations/ja.json | 7 +- .../accuweather/translations/ko.json | 7 +- .../accuweather/translations/lb.json | 7 +- .../accuweather/translations/nl.json | 7 +- .../accuweather/translations/no.json | 7 +- .../accuweather/translations/pl.json | 7 +- .../accuweather/translations/pt-BR.json | 7 +- .../accuweather/translations/ru.json | 7 +- .../accuweather/translations/tr.json | 7 +- .../accuweather/translations/uk.json | 7 +- .../accuweather/translations/zh-Hant.json | 7 +- .../components/adax/translations/bg.json | 9 +-- .../components/adax/translations/ca.json | 8 +- .../components/adax/translations/cs.json | 8 +- .../components/adax/translations/de.json | 8 +- .../components/adax/translations/el.json | 8 +- .../components/adax/translations/en.json | 8 +- .../components/adax/translations/es-419.json | 1 - .../components/adax/translations/es.json | 8 +- .../components/adax/translations/et.json | 8 +- .../components/adax/translations/fr.json | 8 +- .../components/adax/translations/he.json | 10 +-- .../components/adax/translations/hu.json | 8 +- .../components/adax/translations/id.json | 8 +- .../components/adax/translations/it.json | 8 +- .../components/adax/translations/ja.json | 8 +- .../components/adax/translations/nl.json | 8 +- .../components/adax/translations/no.json | 8 +- .../components/adax/translations/pl.json | 8 +- .../components/adax/translations/pt-BR.json | 8 +- .../components/adax/translations/ru.json | 8 +- .../components/adax/translations/sk.json | 10 --- .../components/adax/translations/tr.json | 8 +- .../components/adax/translations/zh-Hans.json | 3 +- .../components/adax/translations/zh-Hant.json | 8 +- .../components/aemet/translations/ca.json | 3 +- .../components/aemet/translations/de.json | 3 +- .../components/aemet/translations/el.json | 3 +- .../components/aemet/translations/en.json | 3 +- .../components/aemet/translations/es-419.json | 3 +- .../components/aemet/translations/es.json | 3 +- .../components/aemet/translations/et.json | 3 +- .../components/aemet/translations/fr.json | 3 +- .../components/aemet/translations/hu.json | 3 +- .../components/aemet/translations/id.json | 3 +- .../components/aemet/translations/it.json | 3 +- .../components/aemet/translations/ja.json | 3 +- .../components/aemet/translations/ko.json | 3 +- .../components/aemet/translations/lb.json | 3 +- .../components/aemet/translations/nl.json | 3 +- .../components/aemet/translations/no.json | 3 +- .../components/aemet/translations/pl.json | 3 +- .../components/aemet/translations/pt-BR.json | 3 +- .../components/aemet/translations/ru.json | 3 +- .../components/aemet/translations/tr.json | 3 +- .../aemet/translations/zh-Hant.json | 3 +- .../components/airly/translations/bg.json | 3 +- .../components/airly/translations/ca.json | 3 +- .../components/airly/translations/cs.json | 3 +- .../components/airly/translations/da.json | 3 +- .../components/airly/translations/de.json | 3 +- .../components/airly/translations/el.json | 3 +- .../components/airly/translations/en.json | 3 +- .../components/airly/translations/es-419.json | 3 +- .../components/airly/translations/es.json | 3 +- .../components/airly/translations/et.json | 3 +- .../components/airly/translations/fr.json | 3 +- .../components/airly/translations/he.json | 3 +- .../components/airly/translations/hu.json | 3 +- .../components/airly/translations/id.json | 3 +- .../components/airly/translations/it.json | 3 +- .../components/airly/translations/ja.json | 3 +- .../components/airly/translations/ko.json | 3 +- .../components/airly/translations/lb.json | 3 +- .../components/airly/translations/nl.json | 3 +- .../components/airly/translations/nn.json | 9 --- .../components/airly/translations/no.json | 3 +- .../components/airly/translations/pl.json | 3 +- .../components/airly/translations/pt-BR.json | 3 +- .../components/airly/translations/pt.json | 3 +- .../components/airly/translations/ru.json | 3 +- .../components/airly/translations/sl.json | 3 +- .../components/airly/translations/sv.json | 3 +- .../components/airly/translations/tr.json | 3 +- .../components/airly/translations/uk.json | 3 +- .../airly/translations/zh-Hant.json | 3 +- .../components/airnow/translations/ca.json | 6 +- .../components/airnow/translations/cs.json | 6 +- .../components/airnow/translations/de.json | 6 +- .../components/airnow/translations/el.json | 6 +- .../components/airnow/translations/en.json | 6 +- .../airnow/translations/es-419.json | 6 +- .../components/airnow/translations/es.json | 6 +- .../components/airnow/translations/et.json | 6 +- .../components/airnow/translations/fr.json | 6 +- .../components/airnow/translations/hu.json | 6 +- .../components/airnow/translations/id.json | 6 +- .../components/airnow/translations/it.json | 6 +- .../components/airnow/translations/ja.json | 6 +- .../components/airnow/translations/ko.json | 6 +- .../components/airnow/translations/lb.json | 6 +- .../components/airnow/translations/nl.json | 6 +- .../components/airnow/translations/no.json | 6 +- .../components/airnow/translations/pl.json | 6 +- .../components/airnow/translations/pt-BR.json | 6 +- .../components/airnow/translations/pt.json | 6 +- .../components/airnow/translations/ru.json | 6 +- .../components/airnow/translations/tr.json | 6 +- .../components/airnow/translations/uk.json | 6 +- .../airnow/translations/zh-Hant.json | 6 +- .../aladdin_connect/translations/cs.json | 22 ++++++ .../aladdin_connect/translations/he.json | 26 +++++++ .../amberelectric/translations/bg.json | 6 +- .../amberelectric/translations/ca.json | 6 +- .../amberelectric/translations/de.json | 6 +- .../amberelectric/translations/el.json | 6 +- .../amberelectric/translations/en.json | 6 +- .../amberelectric/translations/es.json | 6 +- .../amberelectric/translations/et.json | 6 +- .../amberelectric/translations/fr.json | 6 +- .../amberelectric/translations/hu.json | 6 +- .../amberelectric/translations/id.json | 6 +- .../amberelectric/translations/it.json | 6 +- .../amberelectric/translations/ja.json | 6 +- .../amberelectric/translations/nl.json | 6 +- .../amberelectric/translations/no.json | 6 +- .../amberelectric/translations/pl.json | 6 +- .../amberelectric/translations/pt-BR.json | 6 +- .../amberelectric/translations/ru.json | 6 +- .../amberelectric/translations/tr.json | 6 +- .../amberelectric/translations/zh-Hant.json | 6 +- .../components/androidtv/translations/bg.json | 3 +- .../components/androidtv/translations/ca.json | 7 +- .../components/androidtv/translations/de.json | 7 +- .../components/androidtv/translations/el.json | 7 +- .../components/androidtv/translations/en.json | 7 +- .../androidtv/translations/es-419.json | 7 +- .../components/androidtv/translations/es.json | 7 +- .../components/androidtv/translations/et.json | 7 +- .../components/androidtv/translations/fr.json | 7 +- .../components/androidtv/translations/he.json | 7 +- .../components/androidtv/translations/hu.json | 7 +- .../components/androidtv/translations/id.json | 7 +- .../components/androidtv/translations/it.json | 7 +- .../components/androidtv/translations/ja.json | 7 +- .../components/androidtv/translations/nl.json | 7 +- .../components/androidtv/translations/no.json | 7 +- .../components/androidtv/translations/pl.json | 7 +- .../androidtv/translations/pt-BR.json | 7 +- .../components/androidtv/translations/ru.json | 7 +- .../components/androidtv/translations/tr.json | 7 +- .../androidtv/translations/zh-Hans.json | 7 +- .../androidtv/translations/zh-Hant.json | 7 +- .../components/apple_tv/translations/bg.json | 4 +- .../components/apple_tv/translations/ca.json | 6 +- .../components/apple_tv/translations/cs.json | 5 +- .../components/apple_tv/translations/de.json | 6 +- .../components/apple_tv/translations/el.json | 6 +- .../components/apple_tv/translations/en.json | 6 +- .../apple_tv/translations/es-419.json | 9 +-- .../components/apple_tv/translations/es.json | 6 +- .../components/apple_tv/translations/et.json | 6 +- .../components/apple_tv/translations/fr.json | 6 +- .../components/apple_tv/translations/he.json | 1 - .../components/apple_tv/translations/hu.json | 6 +- .../components/apple_tv/translations/id.json | 6 +- .../components/apple_tv/translations/it.json | 6 +- .../components/apple_tv/translations/ja.json | 6 +- .../components/apple_tv/translations/ko.json | 6 +- .../components/apple_tv/translations/lb.json | 5 +- .../components/apple_tv/translations/nl.json | 6 +- .../components/apple_tv/translations/no.json | 6 +- .../components/apple_tv/translations/pl.json | 6 +- .../apple_tv/translations/pt-BR.json | 6 +- .../components/apple_tv/translations/pt.json | 5 +- .../components/apple_tv/translations/ru.json | 6 +- .../components/apple_tv/translations/sl.json | 6 +- .../components/apple_tv/translations/tr.json | 6 +- .../components/apple_tv/translations/uk.json | 6 +- .../apple_tv/translations/zh-Hans.json | 6 +- .../apple_tv/translations/zh-Hant.json | 6 +- .../translations/ko.json | 3 + .../components/asuswrt/translations/ca.json | 4 +- .../components/asuswrt/translations/cs.json | 3 - .../components/asuswrt/translations/de.json | 4 +- .../components/asuswrt/translations/el.json | 4 +- .../components/asuswrt/translations/en.json | 4 +- .../components/asuswrt/translations/es.json | 3 +- .../components/asuswrt/translations/et.json | 4 +- .../components/asuswrt/translations/fr.json | 4 +- .../components/asuswrt/translations/he.json | 3 - .../components/asuswrt/translations/hu.json | 4 +- .../components/asuswrt/translations/id.json | 4 +- .../components/asuswrt/translations/it.json | 4 +- .../components/asuswrt/translations/ja.json | 4 +- .../components/asuswrt/translations/ko.json | 3 +- .../components/asuswrt/translations/nl.json | 4 +- .../components/asuswrt/translations/no.json | 4 +- .../components/asuswrt/translations/pl.json | 4 +- .../asuswrt/translations/pt-BR.json | 4 +- .../components/asuswrt/translations/ru.json | 4 +- .../components/asuswrt/translations/sv.json | 4 +- .../components/asuswrt/translations/tr.json | 4 +- .../asuswrt/translations/zh-Hans.json | 3 - .../asuswrt/translations/zh-Hant.json | 4 +- .../aurora_abb_powerone/translations/bg.json | 3 - .../aurora_abb_powerone/translations/ca.json | 3 +- .../aurora_abb_powerone/translations/cs.json | 7 -- .../aurora_abb_powerone/translations/de.json | 3 +- .../aurora_abb_powerone/translations/el.json | 3 +- .../aurora_abb_powerone/translations/en.json | 3 +- .../translations/en_GB.json | 3 - .../aurora_abb_powerone/translations/es.json | 3 +- .../aurora_abb_powerone/translations/et.json | 3 +- .../aurora_abb_powerone/translations/fr.json | 3 +- .../aurora_abb_powerone/translations/he.json | 3 - .../aurora_abb_powerone/translations/hu.json | 3 +- .../aurora_abb_powerone/translations/id.json | 3 +- .../aurora_abb_powerone/translations/it.json | 3 +- .../aurora_abb_powerone/translations/ja.json | 3 +- .../aurora_abb_powerone/translations/nl.json | 3 +- .../aurora_abb_powerone/translations/no.json | 3 +- .../aurora_abb_powerone/translations/pl.json | 3 +- .../translations/pt-BR.json | 3 +- .../aurora_abb_powerone/translations/ru.json | 3 +- .../aurora_abb_powerone/translations/sl.json | 3 - .../aurora_abb_powerone/translations/tr.json | 3 +- .../translations/zh-Hant.json | 3 +- .../aussie_broadband/translations/bg.json | 7 -- .../aussie_broadband/translations/ca.json | 7 -- .../aussie_broadband/translations/cs.json | 6 -- .../aussie_broadband/translations/de.json | 7 -- .../aussie_broadband/translations/el.json | 7 -- .../aussie_broadband/translations/en.json | 7 -- .../aussie_broadband/translations/es-419.json | 3 - .../aussie_broadband/translations/es.json | 4 - .../aussie_broadband/translations/et.json | 7 -- .../aussie_broadband/translations/fr.json | 7 -- .../aussie_broadband/translations/he.json | 6 -- .../aussie_broadband/translations/hu.json | 7 -- .../aussie_broadband/translations/id.json | 7 -- .../aussie_broadband/translations/it.json | 7 -- .../aussie_broadband/translations/ja.json | 7 -- .../aussie_broadband/translations/nl.json | 7 -- .../aussie_broadband/translations/no.json | 7 -- .../aussie_broadband/translations/pl.json | 7 -- .../aussie_broadband/translations/pt-BR.json | 7 -- .../aussie_broadband/translations/ru.json | 7 -- .../aussie_broadband/translations/sv.json | 7 -- .../aussie_broadband/translations/tr.json | 7 -- .../translations/zh-Hant.json | 7 -- .../azure_event_hub/translations/ko.json | 8 ++ .../components/baf/translations/cs.json | 18 +++++ .../components/baf/translations/he.json | 19 +++++ .../binary_sensor/translations/ca.json | 6 -- .../binary_sensor/translations/cs.json | 2 - .../binary_sensor/translations/de.json | 6 -- .../binary_sensor/translations/el.json | 6 -- .../binary_sensor/translations/en.json | 6 -- .../binary_sensor/translations/es-419.json | 4 - .../binary_sensor/translations/es.json | 6 -- .../binary_sensor/translations/et.json | 6 -- .../binary_sensor/translations/fr.json | 6 -- .../binary_sensor/translations/he.json | 6 -- .../binary_sensor/translations/hu.json | 6 -- .../binary_sensor/translations/id.json | 6 -- .../binary_sensor/translations/it.json | 6 -- .../binary_sensor/translations/ja.json | 6 -- .../binary_sensor/translations/nl.json | 6 -- .../binary_sensor/translations/no.json | 6 -- .../binary_sensor/translations/pl.json | 6 -- .../binary_sensor/translations/pt-BR.json | 6 -- .../binary_sensor/translations/ru.json | 6 -- .../binary_sensor/translations/sl.json | 2 - .../binary_sensor/translations/tr.json | 6 -- .../binary_sensor/translations/zh-Hans.json | 6 -- .../binary_sensor/translations/zh-Hant.json | 6 -- .../bmw_connected_drive/translations/ca.json | 3 +- .../bmw_connected_drive/translations/de.json | 3 +- .../bmw_connected_drive/translations/el.json | 3 +- .../bmw_connected_drive/translations/en.json | 3 +- .../translations/es-419.json | 3 +- .../bmw_connected_drive/translations/es.json | 3 +- .../bmw_connected_drive/translations/et.json | 3 +- .../bmw_connected_drive/translations/fr.json | 3 +- .../bmw_connected_drive/translations/hu.json | 3 +- .../bmw_connected_drive/translations/id.json | 3 +- .../bmw_connected_drive/translations/it.json | 3 +- .../bmw_connected_drive/translations/ja.json | 3 +- .../bmw_connected_drive/translations/ko.json | 3 +- .../bmw_connected_drive/translations/nl.json | 3 +- .../bmw_connected_drive/translations/no.json | 3 +- .../bmw_connected_drive/translations/pl.json | 3 +- .../translations/pt-BR.json | 3 +- .../bmw_connected_drive/translations/ru.json | 3 +- .../bmw_connected_drive/translations/sv.json | 3 +- .../bmw_connected_drive/translations/tr.json | 3 +- .../bmw_connected_drive/translations/uk.json | 3 +- .../translations/zh-Hant.json | 3 +- .../components/bosch_shc/translations/bg.json | 3 +- .../components/bosch_shc/translations/ca.json | 3 +- .../components/bosch_shc/translations/de.json | 3 +- .../components/bosch_shc/translations/el.json | 3 +- .../components/bosch_shc/translations/en.json | 3 +- .../bosch_shc/translations/es-419.json | 3 +- .../components/bosch_shc/translations/es.json | 3 +- .../components/bosch_shc/translations/et.json | 3 +- .../components/bosch_shc/translations/fr.json | 3 +- .../components/bosch_shc/translations/hu.json | 3 +- .../components/bosch_shc/translations/id.json | 3 +- .../components/bosch_shc/translations/it.json | 3 +- .../components/bosch_shc/translations/ja.json | 3 +- .../components/bosch_shc/translations/nl.json | 3 +- .../components/bosch_shc/translations/no.json | 3 +- .../components/bosch_shc/translations/pl.json | 3 +- .../bosch_shc/translations/pt-BR.json | 3 +- .../components/bosch_shc/translations/ru.json | 3 +- .../components/bosch_shc/translations/tr.json | 3 +- .../bosch_shc/translations/zh-Hant.json | 3 +- .../components/braviatv/translations/bg.json | 3 +- .../components/braviatv/translations/ca.json | 3 +- .../components/braviatv/translations/cs.json | 3 +- .../components/braviatv/translations/da.json | 3 - .../components/braviatv/translations/de.json | 3 +- .../components/braviatv/translations/el.json | 3 +- .../components/braviatv/translations/en.json | 3 +- .../braviatv/translations/es-419.json | 3 +- .../components/braviatv/translations/es.json | 3 +- .../components/braviatv/translations/et.json | 3 +- .../components/braviatv/translations/fr.json | 3 +- .../components/braviatv/translations/hu.json | 3 +- .../components/braviatv/translations/id.json | 3 +- .../components/braviatv/translations/it.json | 3 +- .../components/braviatv/translations/ja.json | 3 +- .../components/braviatv/translations/ko.json | 3 +- .../components/braviatv/translations/lb.json | 3 +- .../components/braviatv/translations/nl.json | 3 +- .../components/braviatv/translations/no.json | 3 +- .../components/braviatv/translations/pl.json | 3 +- .../braviatv/translations/pt-BR.json | 3 +- .../components/braviatv/translations/pt.json | 3 +- .../components/braviatv/translations/ru.json | 3 +- .../components/braviatv/translations/sl.json | 3 +- .../components/braviatv/translations/sv.json | 3 +- .../components/braviatv/translations/tr.json | 3 +- .../components/braviatv/translations/uk.json | 3 +- .../braviatv/translations/zh-Hans.json | 3 +- .../braviatv/translations/zh-Hant.json | 3 +- .../components/climacell/translations/af.json | 5 +- .../components/climacell/translations/bg.json | 18 ----- .../components/climacell/translations/ca.json | 23 +----- .../components/climacell/translations/cs.json | 21 ----- .../components/climacell/translations/de.json | 23 +----- .../components/climacell/translations/el.json | 23 +----- .../components/climacell/translations/en.json | 23 +----- .../climacell/translations/es-419.json | 15 +--- .../components/climacell/translations/es.json | 23 +----- .../components/climacell/translations/et.json | 25 +----- .../components/climacell/translations/fr.json | 23 +----- .../components/climacell/translations/hu.json | 25 +----- .../components/climacell/translations/id.json | 23 +----- .../components/climacell/translations/it.json | 23 +----- .../components/climacell/translations/ja.json | 25 +----- .../components/climacell/translations/ko.json | 25 +----- .../components/climacell/translations/lb.json | 11 --- .../components/climacell/translations/nl.json | 23 +----- .../components/climacell/translations/no.json | 23 +----- .../components/climacell/translations/pl.json | 23 +----- .../climacell/translations/pt-BR.json | 23 +----- .../components/climacell/translations/pt.json | 19 ----- .../components/climacell/translations/ru.json | 23 +----- .../components/climacell/translations/sk.json | 17 ---- .../components/climacell/translations/sv.json | 14 ---- .../components/climacell/translations/tr.json | 23 +----- .../climacell/translations/zh-Hans.json | 12 --- .../climacell/translations/zh-Hant.json | 23 +----- .../components/coinbase/translations/ar.json | 5 +- .../components/coinbase/translations/ca.json | 6 +- .../components/coinbase/translations/cs.json | 3 +- .../components/coinbase/translations/de.json | 6 +- .../components/coinbase/translations/el.json | 6 +- .../components/coinbase/translations/en.json | 6 +- .../coinbase/translations/es-419.json | 8 +- .../components/coinbase/translations/es.json | 6 +- .../components/coinbase/translations/et.json | 6 +- .../components/coinbase/translations/fr.json | 6 +- .../components/coinbase/translations/hu.json | 6 +- .../components/coinbase/translations/id.json | 6 +- .../components/coinbase/translations/it.json | 6 +- .../components/coinbase/translations/ja.json | 6 +- .../components/coinbase/translations/nl.json | 6 +- .../components/coinbase/translations/no.json | 6 +- .../components/coinbase/translations/pl.json | 6 +- .../coinbase/translations/pt-BR.json | 6 +- .../components/coinbase/translations/ru.json | 6 +- .../components/coinbase/translations/tr.json | 6 +- .../coinbase/translations/zh-Hans.json | 6 +- .../coinbase/translations/zh-Hant.json | 6 +- .../components/cpuspeed/translations/ko.json | 9 +++ .../components/deconz/translations/bg.json | 1 - .../components/deconz/translations/ca.json | 1 - .../components/deconz/translations/cs.json | 1 - .../components/deconz/translations/da.json | 1 - .../components/deconz/translations/de.json | 1 - .../components/deconz/translations/el.json | 1 - .../components/deconz/translations/en.json | 1 - .../deconz/translations/es-419.json | 1 - .../components/deconz/translations/es.json | 1 - .../components/deconz/translations/et.json | 1 - .../components/deconz/translations/fr.json | 1 - .../components/deconz/translations/hu.json | 1 - .../components/deconz/translations/id.json | 1 - .../components/deconz/translations/it.json | 1 - .../components/deconz/translations/ja.json | 1 - .../components/deconz/translations/ko.json | 2 +- .../components/deconz/translations/lb.json | 1 - .../components/deconz/translations/nl.json | 1 - .../components/deconz/translations/no.json | 1 - .../components/deconz/translations/pl.json | 1 - .../components/deconz/translations/pt-BR.json | 1 - .../components/deconz/translations/pt.json | 3 +- .../components/deconz/translations/ru.json | 1 - .../components/deconz/translations/sl.json | 1 - .../components/deconz/translations/sv.json | 1 - .../components/deconz/translations/tr.json | 1 - .../components/deconz/translations/uk.json | 1 - .../deconz/translations/zh-Hant.json | 1 - .../components/denonavr/translations/ca.json | 10 +-- .../components/denonavr/translations/cs.json | 3 +- .../components/denonavr/translations/de.json | 10 +-- .../components/denonavr/translations/el.json | 10 +-- .../components/denonavr/translations/en.json | 10 +-- .../denonavr/translations/es-419.json | 10 +-- .../components/denonavr/translations/es.json | 10 +-- .../components/denonavr/translations/et.json | 10 +-- .../components/denonavr/translations/fr.json | 10 +-- .../components/denonavr/translations/hu.json | 10 +-- .../components/denonavr/translations/id.json | 10 +-- .../components/denonavr/translations/it.json | 10 +-- .../components/denonavr/translations/ja.json | 10 +-- .../components/denonavr/translations/ko.json | 10 +-- .../components/denonavr/translations/lb.json | 10 +-- .../components/denonavr/translations/nl.json | 10 +-- .../components/denonavr/translations/no.json | 10 +-- .../components/denonavr/translations/pl.json | 10 +-- .../denonavr/translations/pt-BR.json | 10 +-- .../components/denonavr/translations/ru.json | 10 +-- .../components/denonavr/translations/tr.json | 10 +-- .../components/denonavr/translations/uk.json | 10 +-- .../denonavr/translations/zh-Hant.json | 10 +-- .../derivative/translations/bg.json | 5 -- .../derivative/translations/ca.json | 16 ---- .../derivative/translations/de.json | 16 ---- .../derivative/translations/el.json | 16 ---- .../derivative/translations/en.json | 16 ---- .../derivative/translations/es.json | 9 --- .../derivative/translations/et.json | 16 ---- .../derivative/translations/fr.json | 16 ---- .../derivative/translations/he.json | 11 --- .../derivative/translations/hu.json | 16 ---- .../derivative/translations/id.json | 16 ---- .../derivative/translations/it.json | 16 ---- .../derivative/translations/ja.json | 16 ---- .../derivative/translations/ko.json | 43 +++++++++++ .../derivative/translations/nl.json | 16 ---- .../derivative/translations/no.json | 16 ---- .../derivative/translations/pl.json | 16 ---- .../derivative/translations/pt-BR.json | 16 ---- .../derivative/translations/ru.json | 16 ---- .../derivative/translations/tr.json | 16 ---- .../derivative/translations/zh-Hans.json | 16 ---- .../derivative/translations/zh-Hant.json | 16 ---- .../devolo_home_network/translations/ko.json | 9 +++ .../components/dlna_dmr/translations/bg.json | 7 +- .../components/dlna_dmr/translations/ca.json | 5 +- .../components/dlna_dmr/translations/cs.json | 5 -- .../components/dlna_dmr/translations/de.json | 5 +- .../components/dlna_dmr/translations/el.json | 5 +- .../components/dlna_dmr/translations/en.json | 5 +- .../dlna_dmr/translations/es-419.json | 2 - .../components/dlna_dmr/translations/es.json | 5 +- .../components/dlna_dmr/translations/et.json | 5 +- .../components/dlna_dmr/translations/fr.json | 5 +- .../components/dlna_dmr/translations/he.json | 3 +- .../components/dlna_dmr/translations/hu.json | 5 +- .../components/dlna_dmr/translations/id.json | 5 +- .../components/dlna_dmr/translations/it.json | 5 +- .../components/dlna_dmr/translations/ja.json | 5 +- .../components/dlna_dmr/translations/ko.json | 24 ++++++ .../components/dlna_dmr/translations/nl.json | 5 +- .../components/dlna_dmr/translations/no.json | 5 +- .../components/dlna_dmr/translations/pl.json | 5 +- .../dlna_dmr/translations/pt-BR.json | 5 +- .../components/dlna_dmr/translations/ru.json | 5 +- .../components/dlna_dmr/translations/tr.json | 5 +- .../dlna_dmr/translations/zh-Hans.json | 5 +- .../dlna_dmr/translations/zh-Hant.json | 5 +- .../components/dlna_dms/translations/ko.json | 9 +++ .../components/doorbird/translations/ca.json | 6 +- .../components/doorbird/translations/cs.json | 6 +- .../components/doorbird/translations/de.json | 6 +- .../components/doorbird/translations/el.json | 6 +- .../components/doorbird/translations/en.json | 6 +- .../doorbird/translations/es-419.json | 6 +- .../components/doorbird/translations/es.json | 6 +- .../components/doorbird/translations/et.json | 6 +- .../components/doorbird/translations/fr.json | 6 +- .../components/doorbird/translations/hu.json | 6 +- .../components/doorbird/translations/id.json | 6 +- .../components/doorbird/translations/it.json | 6 +- .../components/doorbird/translations/ja.json | 6 +- .../components/doorbird/translations/ko.json | 6 +- .../components/doorbird/translations/lb.json | 6 +- .../components/doorbird/translations/nl.json | 6 +- .../components/doorbird/translations/no.json | 6 +- .../components/doorbird/translations/pl.json | 6 +- .../doorbird/translations/pt-BR.json | 6 +- .../components/doorbird/translations/ru.json | 6 +- .../components/doorbird/translations/sl.json | 6 +- .../components/doorbird/translations/sv.json | 3 +- .../components/doorbird/translations/tr.json | 6 +- .../components/doorbird/translations/uk.json | 6 +- .../doorbird/translations/zh-Hant.json | 6 +- .../components/dunehd/translations/ca.json | 3 +- .../components/dunehd/translations/cs.json | 3 +- .../components/dunehd/translations/de.json | 3 +- .../components/dunehd/translations/el.json | 3 +- .../components/dunehd/translations/en.json | 3 +- .../dunehd/translations/es-419.json | 3 +- .../components/dunehd/translations/es.json | 3 +- .../components/dunehd/translations/et.json | 3 +- .../components/dunehd/translations/fr.json | 3 +- .../components/dunehd/translations/hu.json | 3 +- .../components/dunehd/translations/id.json | 3 +- .../components/dunehd/translations/it.json | 3 +- .../components/dunehd/translations/ja.json | 3 +- .../components/dunehd/translations/ko.json | 3 +- .../components/dunehd/translations/lb.json | 3 +- .../components/dunehd/translations/nl.json | 3 +- .../components/dunehd/translations/no.json | 3 +- .../components/dunehd/translations/pl.json | 3 +- .../components/dunehd/translations/pt-BR.json | 3 +- .../components/dunehd/translations/ru.json | 3 +- .../components/dunehd/translations/tr.json | 3 +- .../components/dunehd/translations/uk.json | 3 +- .../dunehd/translations/zh-Hant.json | 3 +- .../components/efergy/translations/ca.json | 3 +- .../components/efergy/translations/cs.json | 3 +- .../components/efergy/translations/de.json | 3 +- .../components/efergy/translations/el.json | 3 +- .../components/efergy/translations/en.json | 3 +- .../components/efergy/translations/es.json | 3 +- .../components/efergy/translations/et.json | 3 +- .../components/efergy/translations/fr.json | 3 +- .../components/efergy/translations/hu.json | 3 +- .../components/efergy/translations/id.json | 3 +- .../components/efergy/translations/it.json | 3 +- .../components/efergy/translations/ja.json | 3 +- .../components/efergy/translations/nl.json | 3 +- .../components/efergy/translations/no.json | 3 +- .../components/efergy/translations/pl.json | 3 +- .../components/efergy/translations/pt-BR.json | 3 +- .../components/efergy/translations/ru.json | 3 +- .../components/efergy/translations/tr.json | 3 +- .../efergy/translations/zh-Hant.json | 3 +- .../components/elmax/translations/bg.json | 4 +- .../components/elmax/translations/ca.json | 13 +--- .../components/elmax/translations/cs.json | 13 +--- .../components/elmax/translations/de.json | 13 +--- .../components/elmax/translations/el.json | 13 +--- .../components/elmax/translations/en.json | 13 +--- .../components/elmax/translations/es.json | 13 +--- .../components/elmax/translations/et.json | 13 +--- .../components/elmax/translations/fr.json | 13 +--- .../components/elmax/translations/hu.json | 13 +--- .../components/elmax/translations/id.json | 13 +--- .../components/elmax/translations/it.json | 13 +--- .../components/elmax/translations/ja.json | 13 +--- .../components/elmax/translations/lt.json | 6 +- .../components/elmax/translations/nl.json | 13 +--- .../components/elmax/translations/no.json | 13 +--- .../components/elmax/translations/pl.json | 13 +--- .../components/elmax/translations/pt-BR.json | 13 +--- .../components/elmax/translations/ru.json | 13 +--- .../components/elmax/translations/sk.json | 3 +- .../components/elmax/translations/tr.json | 13 +--- .../elmax/translations/zh-Hans.json | 3 +- .../elmax/translations/zh-Hant.json | 13 +--- .../components/fan/translations/ca.json | 1 - .../components/fan/translations/cs.json | 2 +- .../components/fan/translations/de.json | 1 - .../components/fan/translations/el.json | 1 - .../components/fan/translations/en.json | 1 - .../components/fan/translations/es.json | 1 - .../components/fan/translations/et.json | 1 - .../components/fan/translations/fr.json | 1 - .../components/fan/translations/he.json | 1 - .../components/fan/translations/hu.json | 1 - .../components/fan/translations/id.json | 1 - .../components/fan/translations/it.json | 1 - .../components/fan/translations/ja.json | 1 - .../components/fan/translations/nl.json | 1 - .../components/fan/translations/no.json | 1 - .../components/fan/translations/pl.json | 1 - .../components/fan/translations/pt-BR.json | 1 - .../components/fan/translations/ru.json | 1 - .../components/fan/translations/sv.json | 1 - .../components/fan/translations/tr.json | 1 - .../components/fan/translations/zh-Hans.json | 1 - .../components/fan/translations/zh-Hant.json | 1 - .../components/fivem/translations/ca.json | 1 - .../components/fivem/translations/de.json | 1 - .../components/fivem/translations/el.json | 1 - .../components/fivem/translations/en.json | 1 - .../components/fivem/translations/es.json | 1 - .../components/fivem/translations/et.json | 1 - .../components/fivem/translations/fr.json | 1 - .../components/fivem/translations/hu.json | 1 - .../components/fivem/translations/id.json | 1 - .../components/fivem/translations/it.json | 1 - .../components/fivem/translations/ja.json | 1 - .../components/fivem/translations/nl.json | 1 - .../components/fivem/translations/no.json | 1 - .../components/fivem/translations/pl.json | 1 - .../components/fivem/translations/pt-BR.json | 1 - .../components/fivem/translations/ru.json | 1 - .../components/fivem/translations/tr.json | 1 - .../fivem/translations/zh-Hant.json | 1 - .../components/freebox/translations/ca.json | 3 +- .../components/freebox/translations/cs.json | 3 +- .../components/freebox/translations/de.json | 3 +- .../components/freebox/translations/el.json | 3 +- .../components/freebox/translations/en.json | 3 +- .../freebox/translations/es-419.json | 3 +- .../components/freebox/translations/es.json | 3 +- .../components/freebox/translations/et.json | 3 +- .../components/freebox/translations/fr.json | 3 +- .../components/freebox/translations/hu.json | 3 +- .../components/freebox/translations/id.json | 3 +- .../components/freebox/translations/it.json | 3 +- .../components/freebox/translations/ja.json | 3 +- .../components/freebox/translations/ko.json | 3 +- .../components/freebox/translations/lb.json | 3 +- .../components/freebox/translations/nl.json | 3 +- .../components/freebox/translations/no.json | 3 +- .../components/freebox/translations/pl.json | 3 +- .../freebox/translations/pt-BR.json | 3 +- .../components/freebox/translations/ru.json | 3 +- .../components/freebox/translations/sl.json | 3 +- .../components/freebox/translations/tr.json | 3 +- .../components/freebox/translations/uk.json | 3 +- .../freebox/translations/zh-Hant.json | 3 +- .../components/fritz/translations/bg.json | 5 -- .../components/fritz/translations/ca.json | 11 --- .../components/fritz/translations/cs.json | 9 --- .../components/fritz/translations/de.json | 11 --- .../components/fritz/translations/el.json | 11 --- .../components/fritz/translations/en.json | 11 --- .../components/fritz/translations/es.json | 11 --- .../components/fritz/translations/et.json | 11 --- .../components/fritz/translations/fr.json | 11 --- .../components/fritz/translations/he.json | 9 --- .../components/fritz/translations/hu.json | 11 --- .../components/fritz/translations/id.json | 11 --- .../components/fritz/translations/it.json | 11 --- .../components/fritz/translations/ja.json | 11 --- .../components/fritz/translations/ko.json | 10 +-- .../components/fritz/translations/nb.json | 5 -- .../components/fritz/translations/nl.json | 11 --- .../components/fritz/translations/no.json | 11 --- .../components/fritz/translations/pl.json | 11 --- .../components/fritz/translations/pt-BR.json | 11 --- .../components/fritz/translations/ru.json | 11 --- .../components/fritz/translations/sk.json | 5 -- .../components/fritz/translations/tr.json | 11 --- .../fritz/translations/zh-Hans.json | 5 -- .../fritz/translations/zh-Hant.json | 11 --- .../components/generic/translations/bg.json | 2 - .../components/generic/translations/ca.json | 2 - .../components/generic/translations/de.json | 2 - .../components/generic/translations/el.json | 2 - .../components/generic/translations/en.json | 2 - .../components/generic/translations/es.json | 2 - .../components/generic/translations/et.json | 2 - .../components/generic/translations/fr.json | 2 - .../components/generic/translations/he.json | 2 - .../components/generic/translations/hu.json | 2 - .../components/generic/translations/id.json | 2 - .../components/generic/translations/it.json | 2 - .../components/generic/translations/ja.json | 2 - .../components/generic/translations/ko.json | 9 +++ .../components/generic/translations/nl.json | 2 - .../components/generic/translations/no.json | 2 - .../components/generic/translations/pl.json | 2 - .../generic/translations/pt-BR.json | 2 - .../components/generic/translations/ru.json | 2 - .../components/generic/translations/tr.json | 2 - .../generic/translations/zh-Hant.json | 2 - .../geocaching/translations/he.json | 24 ++++++ .../components/gios/translations/ca.json | 1 - .../components/gios/translations/cs.json | 1 - .../components/gios/translations/da.json | 1 - .../components/gios/translations/de.json | 1 - .../components/gios/translations/el.json | 1 - .../components/gios/translations/en.json | 1 - .../components/gios/translations/es-419.json | 1 - .../components/gios/translations/es.json | 1 - .../components/gios/translations/et.json | 1 - .../components/gios/translations/fr.json | 1 - .../components/gios/translations/hu.json | 1 - .../components/gios/translations/id.json | 1 - .../components/gios/translations/it.json | 1 - .../components/gios/translations/ja.json | 1 - .../components/gios/translations/ko.json | 1 - .../components/gios/translations/lb.json | 1 - .../components/gios/translations/nl.json | 1 - .../components/gios/translations/no.json | 1 - .../components/gios/translations/pl.json | 1 - .../components/gios/translations/pt-BR.json | 1 - .../components/gios/translations/ru.json | 1 - .../components/gios/translations/sl.json | 1 - .../components/gios/translations/sv.json | 1 - .../components/gios/translations/tr.json | 1 - .../components/gios/translations/uk.json | 1 - .../components/gios/translations/zh-Hant.json | 1 - .../components/goalzero/translations/ca.json | 6 +- .../components/goalzero/translations/cs.json | 3 +- .../components/goalzero/translations/de.json | 6 +- .../components/goalzero/translations/el.json | 6 +- .../components/goalzero/translations/en.json | 6 +- .../components/goalzero/translations/es.json | 6 +- .../components/goalzero/translations/et.json | 6 +- .../components/goalzero/translations/fr.json | 6 +- .../components/goalzero/translations/hu.json | 6 +- .../components/goalzero/translations/id.json | 6 +- .../components/goalzero/translations/it.json | 6 +- .../components/goalzero/translations/ja.json | 6 +- .../components/goalzero/translations/ko.json | 3 +- .../components/goalzero/translations/lb.json | 3 +- .../components/goalzero/translations/nl.json | 6 +- .../components/goalzero/translations/no.json | 6 +- .../components/goalzero/translations/pl.json | 6 +- .../goalzero/translations/pt-BR.json | 6 +- .../components/goalzero/translations/ru.json | 6 +- .../components/goalzero/translations/tr.json | 6 +- .../components/goalzero/translations/uk.json | 3 +- .../goalzero/translations/zh-Hant.json | 6 +- .../components/google/translations/ko.json | 7 ++ .../components/group/translations/bg.json | 45 +---------- .../components/group/translations/ca.json | 77 +------------------ .../components/group/translations/cs.json | 77 +------------------ .../components/group/translations/de.json | 77 +------------------ .../components/group/translations/el.json | 77 +------------------ .../components/group/translations/en.json | 77 +------------------ .../components/group/translations/es.json | 63 +-------------- .../components/group/translations/et.json | 77 +------------------ .../components/group/translations/fr.json | 77 +------------------ .../components/group/translations/he.json | 77 +------------------ .../components/group/translations/hu.json | 77 +------------------ .../components/group/translations/id.json | 77 +------------------ .../components/group/translations/it.json | 77 +------------------ .../components/group/translations/ja.json | 77 +------------------ .../components/group/translations/ko.json | 7 ++ .../components/group/translations/nl.json | 77 +------------------ .../components/group/translations/no.json | 77 +------------------ .../components/group/translations/pl.json | 77 +------------------ .../components/group/translations/pt-BR.json | 77 +------------------ .../components/group/translations/pt.json | 19 ----- .../components/group/translations/ru.json | 77 +------------------ .../components/group/translations/sv.json | 53 ------------- .../components/group/translations/tr.json | 77 +------------------ .../group/translations/zh-Hant.json | 77 +------------------ .../components/guardian/translations/ca.json | 3 - .../components/guardian/translations/cs.json | 3 - .../components/guardian/translations/de.json | 3 - .../components/guardian/translations/el.json | 3 - .../components/guardian/translations/en.json | 3 - .../components/guardian/translations/es.json | 3 - .../components/guardian/translations/et.json | 3 - .../components/guardian/translations/fr.json | 3 - .../components/guardian/translations/hu.json | 3 - .../components/guardian/translations/id.json | 3 - .../components/guardian/translations/it.json | 3 - .../components/guardian/translations/ja.json | 3 - .../components/guardian/translations/ko.json | 3 - .../components/guardian/translations/lb.json | 3 - .../components/guardian/translations/nl.json | 3 - .../components/guardian/translations/no.json | 3 - .../components/guardian/translations/pl.json | 3 - .../guardian/translations/pt-BR.json | 3 - .../components/guardian/translations/ru.json | 3 - .../components/guardian/translations/tr.json | 3 - .../components/guardian/translations/uk.json | 3 - .../guardian/translations/zh-Hant.json | 3 - .../components/habitica/translations/ca.json | 3 +- .../components/habitica/translations/de.json | 3 +- .../components/habitica/translations/el.json | 3 +- .../components/habitica/translations/en.json | 3 +- .../components/habitica/translations/es.json | 3 +- .../components/habitica/translations/et.json | 3 +- .../components/habitica/translations/fr.json | 3 +- .../components/habitica/translations/hu.json | 3 +- .../components/habitica/translations/id.json | 3 +- .../components/habitica/translations/it.json | 3 +- .../components/habitica/translations/ja.json | 3 +- .../components/habitica/translations/ko.json | 3 +- .../components/habitica/translations/nl.json | 3 +- .../components/habitica/translations/no.json | 3 +- .../components/habitica/translations/pl.json | 3 +- .../habitica/translations/pt-BR.json | 3 +- .../components/habitica/translations/ru.json | 3 +- .../components/habitica/translations/tr.json | 3 +- .../habitica/translations/zh-Hant.json | 3 +- .../home_plus_control/translations/ca.json | 3 +- .../home_plus_control/translations/de.json | 3 +- .../home_plus_control/translations/el.json | 3 +- .../home_plus_control/translations/en.json | 3 +- .../translations/es-419.json | 3 +- .../home_plus_control/translations/es.json | 3 +- .../home_plus_control/translations/et.json | 3 +- .../home_plus_control/translations/fr.json | 3 +- .../home_plus_control/translations/hu.json | 3 +- .../home_plus_control/translations/id.json | 3 +- .../home_plus_control/translations/it.json | 3 +- .../home_plus_control/translations/ja.json | 3 +- .../home_plus_control/translations/ko.json | 3 +- .../home_plus_control/translations/nl.json | 3 +- .../home_plus_control/translations/no.json | 3 +- .../home_plus_control/translations/pl.json | 3 +- .../home_plus_control/translations/pt-BR.json | 3 +- .../home_plus_control/translations/ru.json | 3 +- .../home_plus_control/translations/tr.json | 3 +- .../translations/zh-Hant.json | 3 +- .../components/homekit/translations/ca.json | 1 - .../components/homekit/translations/de.json | 1 - .../components/homekit/translations/el.json | 1 - .../components/homekit/translations/en.json | 1 - .../homekit/translations/es-419.json | 3 - .../components/homekit/translations/es.json | 1 - .../components/homekit/translations/et.json | 1 - .../components/homekit/translations/fr.json | 1 - .../components/homekit/translations/hu.json | 1 - .../components/homekit/translations/id.json | 1 - .../components/homekit/translations/it.json | 1 - .../components/homekit/translations/ja.json | 1 - .../components/homekit/translations/ko.json | 3 - .../components/homekit/translations/lb.json | 3 - .../components/homekit/translations/nl.json | 1 - .../components/homekit/translations/no.json | 1 - .../components/homekit/translations/pl.json | 1 - .../homekit/translations/pt-BR.json | 1 - .../components/homekit/translations/pt.json | 3 - .../components/homekit/translations/ru.json | 1 - .../components/homekit/translations/sl.json | 3 - .../components/homekit/translations/tr.json | 1 - .../components/homekit/translations/uk.json | 3 - .../homekit/translations/zh-Hans.json | 1 - .../homekit/translations/zh-Hant.json | 1 - .../huawei_lte/translations/bg.json | 5 +- .../huawei_lte/translations/ca.json | 3 - .../huawei_lte/translations/cs.json | 5 +- .../huawei_lte/translations/da.json | 5 +- .../huawei_lte/translations/de.json | 3 - .../huawei_lte/translations/el.json | 3 - .../huawei_lte/translations/en.json | 3 - .../huawei_lte/translations/es-419.json | 5 +- .../huawei_lte/translations/es.json | 3 - .../huawei_lte/translations/et.json | 3 - .../huawei_lte/translations/fr.json | 3 - .../huawei_lte/translations/he.json | 4 - .../huawei_lte/translations/hu.json | 3 - .../huawei_lte/translations/id.json | 3 - .../huawei_lte/translations/it.json | 3 - .../huawei_lte/translations/ja.json | 3 - .../huawei_lte/translations/ko.json | 5 +- .../huawei_lte/translations/lb.json | 5 +- .../huawei_lte/translations/nl.json | 3 - .../huawei_lte/translations/no.json | 3 - .../huawei_lte/translations/pl.json | 3 - .../huawei_lte/translations/pt-BR.json | 3 - .../huawei_lte/translations/pt.json | 7 +- .../huawei_lte/translations/ru.json | 3 - .../huawei_lte/translations/sk.json | 3 - .../huawei_lte/translations/sl.json | 5 +- .../huawei_lte/translations/sv.json | 5 +- .../huawei_lte/translations/tr.json | 3 - .../huawei_lte/translations/uk.json | 5 +- .../huawei_lte/translations/zh-Hans.json | 3 - .../huawei_lte/translations/zh-Hant.json | 3 - .../humidifier/translations/ca.json | 1 - .../humidifier/translations/cs.json | 1 - .../humidifier/translations/de.json | 1 - .../humidifier/translations/el.json | 1 - .../humidifier/translations/en.json | 1 - .../humidifier/translations/es.json | 1 - .../humidifier/translations/et.json | 1 - .../humidifier/translations/fr.json | 1 - .../humidifier/translations/he.json | 1 - .../humidifier/translations/hu.json | 1 - .../humidifier/translations/id.json | 1 - .../humidifier/translations/it.json | 1 - .../humidifier/translations/ja.json | 1 - .../humidifier/translations/nl.json | 1 - .../humidifier/translations/no.json | 1 - .../humidifier/translations/pl.json | 1 - .../humidifier/translations/pt-BR.json | 1 - .../humidifier/translations/ru.json | 1 - .../humidifier/translations/sv.json | 1 - .../humidifier/translations/tr.json | 1 - .../humidifier/translations/zh-Hans.json | 1 - .../humidifier/translations/zh-Hant.json | 1 - .../components/insteon/translations/ca.json | 22 ++---- .../components/insteon/translations/cs.json | 22 ++---- .../components/insteon/translations/de.json | 22 ++---- .../components/insteon/translations/el.json | 22 ++---- .../components/insteon/translations/en.json | 22 ++---- .../components/insteon/translations/es.json | 22 ++---- .../components/insteon/translations/et.json | 22 ++---- .../components/insteon/translations/fr.json | 22 ++---- .../components/insteon/translations/hu.json | 22 ++---- .../components/insteon/translations/id.json | 22 ++---- .../components/insteon/translations/it.json | 22 ++---- .../components/insteon/translations/ja.json | 22 ++---- .../components/insteon/translations/ko.json | 26 +++---- .../components/insteon/translations/lb.json | 22 ++---- .../components/insteon/translations/nl.json | 22 ++---- .../components/insteon/translations/no.json | 22 ++---- .../components/insteon/translations/pl.json | 22 ++---- .../insteon/translations/pt-BR.json | 22 ++---- .../components/insteon/translations/pt.json | 6 +- .../components/insteon/translations/ru.json | 22 ++---- .../components/insteon/translations/tr.json | 22 ++---- .../components/insteon/translations/uk.json | 22 ++---- .../insteon/translations/zh-Hant.json | 22 ++---- .../integration/translations/ca.json | 6 -- .../integration/translations/cs.json | 6 -- .../integration/translations/de.json | 6 -- .../integration/translations/el.json | 6 -- .../integration/translations/en.json | 6 -- .../integration/translations/es.json | 3 - .../integration/translations/et.json | 6 -- .../integration/translations/fr.json | 6 -- .../integration/translations/he.json | 6 -- .../integration/translations/hu.json | 6 -- .../integration/translations/id.json | 6 -- .../integration/translations/it.json | 6 -- .../integration/translations/ja.json | 6 -- .../integration/translations/ko.json | 36 +++++++++ .../integration/translations/nl.json | 6 -- .../integration/translations/no.json | 6 -- .../integration/translations/pl.json | 6 -- .../integration/translations/pt-BR.json | 6 -- .../integration/translations/ru.json | 6 -- .../integration/translations/sk.json | 9 --- .../integration/translations/tr.json | 6 -- .../integration/translations/zh-Hans.json | 6 -- .../integration/translations/zh-Hant.json | 6 -- .../intellifire/translations/bg.json | 8 +- .../intellifire/translations/ca.json | 8 +- .../intellifire/translations/cs.json | 8 +- .../intellifire/translations/de.json | 8 +- .../intellifire/translations/el.json | 8 +- .../intellifire/translations/en.json | 8 +- .../intellifire/translations/es.json | 5 -- .../intellifire/translations/et.json | 8 +- .../intellifire/translations/fr.json | 8 +- .../intellifire/translations/he.json | 8 +- .../intellifire/translations/hu.json | 8 +- .../intellifire/translations/id.json | 8 +- .../intellifire/translations/it.json | 8 +- .../intellifire/translations/ja.json | 8 +- .../intellifire/translations/ko.json | 23 ++++++ .../intellifire/translations/nl.json | 8 +- .../intellifire/translations/no.json | 8 +- .../intellifire/translations/pl.json | 8 +- .../intellifire/translations/pt-BR.json | 8 +- .../intellifire/translations/ru.json | 8 +- .../intellifire/translations/sv.json | 10 +-- .../intellifire/translations/tr.json | 8 +- .../intellifire/translations/zh-Hant.json | 8 +- .../components/iqvia/translations/bg.json | 3 +- .../components/iqvia/translations/ca.json | 3 +- .../components/iqvia/translations/cs.json | 3 +- .../components/iqvia/translations/da.json | 3 +- .../components/iqvia/translations/de.json | 3 +- .../components/iqvia/translations/el.json | 3 +- .../components/iqvia/translations/en.json | 3 +- .../components/iqvia/translations/es-419.json | 3 +- .../components/iqvia/translations/es.json | 3 +- .../components/iqvia/translations/et.json | 3 +- .../components/iqvia/translations/fi.json | 3 +- .../components/iqvia/translations/fr.json | 3 +- .../components/iqvia/translations/hu.json | 3 +- .../components/iqvia/translations/id.json | 3 +- .../components/iqvia/translations/it.json | 3 +- .../components/iqvia/translations/ja.json | 3 +- .../components/iqvia/translations/ko.json | 3 +- .../components/iqvia/translations/lb.json | 3 +- .../components/iqvia/translations/nl.json | 3 +- .../components/iqvia/translations/nn.json | 9 --- .../components/iqvia/translations/no.json | 3 +- .../components/iqvia/translations/pl.json | 3 +- .../components/iqvia/translations/pt-BR.json | 3 +- .../components/iqvia/translations/ru.json | 3 +- .../components/iqvia/translations/sl.json | 3 +- .../components/iqvia/translations/sv.json | 3 +- .../components/iqvia/translations/tr.json | 3 +- .../components/iqvia/translations/uk.json | 3 +- .../iqvia/translations/zh-Hans.json | 3 +- .../iqvia/translations/zh-Hant.json | 3 +- .../components/iss/translations/bg.json | 3 - .../components/iss/translations/ca.json | 3 - .../components/iss/translations/de.json | 3 - .../components/iss/translations/el.json | 3 - .../components/iss/translations/en.json | 3 - .../components/iss/translations/es.json | 3 - .../components/iss/translations/et.json | 3 - .../components/iss/translations/fr.json | 3 - .../components/iss/translations/hu.json | 3 - .../components/iss/translations/id.json | 3 - .../components/iss/translations/it.json | 3 - .../components/iss/translations/ja.json | 3 - .../components/iss/translations/nl.json | 3 - .../components/iss/translations/no.json | 3 - .../components/iss/translations/pl.json | 3 - .../components/iss/translations/pt-BR.json | 5 +- .../components/iss/translations/ru.json | 3 - .../components/iss/translations/tr.json | 3 - .../components/iss/translations/uk.json | 3 - .../components/iss/translations/zh-Hans.json | 11 --- .../components/iss/translations/zh-Hant.json | 3 - .../components/isy994/translations/ko.json | 9 +++ .../kaleidescape/translations/bg.json | 6 +- .../kaleidescape/translations/ca.json | 6 +- .../kaleidescape/translations/de.json | 6 +- .../kaleidescape/translations/el.json | 6 +- .../kaleidescape/translations/en.json | 6 +- .../kaleidescape/translations/et.json | 6 +- .../kaleidescape/translations/fr.json | 6 +- .../kaleidescape/translations/hu.json | 6 +- .../kaleidescape/translations/id.json | 6 +- .../kaleidescape/translations/it.json | 6 +- .../kaleidescape/translations/ja.json | 6 +- .../kaleidescape/translations/nl.json | 6 +- .../kaleidescape/translations/no.json | 6 +- .../kaleidescape/translations/pl.json | 6 +- .../kaleidescape/translations/pt-BR.json | 6 +- .../kaleidescape/translations/ru.json | 6 +- .../kaleidescape/translations/tr.json | 6 +- .../kaleidescape/translations/zh-Hant.json | 6 +- .../components/knx/translations/bg.json | 1 - .../components/knx/translations/ca.json | 6 +- .../components/knx/translations/de.json | 6 +- .../components/knx/translations/el.json | 4 - .../components/knx/translations/en.json | 4 - .../components/knx/translations/es.json | 8 +- .../components/knx/translations/et.json | 4 - .../components/knx/translations/fr.json | 4 - .../components/knx/translations/hu.json | 4 - .../components/knx/translations/id.json | 4 - .../components/knx/translations/it.json | 4 - .../components/knx/translations/ja.json | 4 - .../components/knx/translations/ko.json | 6 +- .../components/knx/translations/nl.json | 4 - .../components/knx/translations/no.json | 6 +- .../components/knx/translations/pl.json | 4 - .../components/knx/translations/pt-BR.json | 6 +- .../components/knx/translations/ru.json | 4 - .../components/knx/translations/sl.json | 1 - .../components/knx/translations/tr.json | 4 - .../components/knx/translations/zh-Hant.json | 4 - .../components/kraken/translations/ko.json | 9 +++ .../components/light/translations/ca.json | 1 - .../components/light/translations/cs.json | 1 - .../components/light/translations/de.json | 1 - .../components/light/translations/el.json | 1 - .../components/light/translations/en.json | 1 - .../components/light/translations/es.json | 1 - .../components/light/translations/et.json | 1 - .../components/light/translations/fr.json | 1 - .../components/light/translations/he.json | 1 - .../components/light/translations/hu.json | 1 - .../components/light/translations/id.json | 1 - .../components/light/translations/it.json | 1 - .../components/light/translations/ja.json | 1 - .../components/light/translations/nl.json | 1 - .../components/light/translations/no.json | 1 - .../components/light/translations/pl.json | 1 - .../components/light/translations/pt-BR.json | 1 - .../components/light/translations/ru.json | 1 - .../components/light/translations/sv.json | 1 - .../components/light/translations/tr.json | 1 - .../light/translations/zh-Hans.json | 1 - .../light/translations/zh-Hant.json | 1 - .../litterrobot/translations/sensor.cs.json | 9 +++ .../litterrobot/translations/sensor.he.json | 8 ++ .../litterrobot/translations/sensor.ko.json | 3 + .../components/luftdaten/translations/bg.json | 3 +- .../components/luftdaten/translations/ca.json | 3 +- .../components/luftdaten/translations/cs.json | 3 +- .../components/luftdaten/translations/da.json | 3 +- .../components/luftdaten/translations/de.json | 3 +- .../components/luftdaten/translations/el.json | 3 +- .../components/luftdaten/translations/en.json | 3 +- .../luftdaten/translations/es-419.json | 3 +- .../components/luftdaten/translations/es.json | 3 +- .../components/luftdaten/translations/et.json | 3 +- .../components/luftdaten/translations/fi.json | 3 +- .../components/luftdaten/translations/fr.json | 3 +- .../components/luftdaten/translations/hu.json | 3 +- .../components/luftdaten/translations/id.json | 3 +- .../components/luftdaten/translations/it.json | 3 +- .../components/luftdaten/translations/ja.json | 3 +- .../components/luftdaten/translations/ko.json | 3 +- .../components/luftdaten/translations/lb.json | 3 +- .../components/luftdaten/translations/nl.json | 3 +- .../components/luftdaten/translations/no.json | 3 +- .../components/luftdaten/translations/pl.json | 3 +- .../luftdaten/translations/pt-BR.json | 5 +- .../components/luftdaten/translations/pt.json | 3 +- .../components/luftdaten/translations/ru.json | 3 +- .../components/luftdaten/translations/sl.json | 3 +- .../components/luftdaten/translations/sv.json | 3 +- .../components/luftdaten/translations/tr.json | 3 +- .../components/luftdaten/translations/uk.json | 3 +- .../luftdaten/translations/zh-Hans.json | 3 +- .../luftdaten/translations/zh-Hant.json | 3 +- .../components/meater/translations/ko.json | 18 ++++- .../components/mill/translations/bg.json | 6 -- .../components/mill/translations/ca.json | 4 +- .../components/mill/translations/cs.json | 8 -- .../components/mill/translations/de.json | 4 +- .../components/mill/translations/el.json | 4 +- .../components/mill/translations/en.json | 4 +- .../components/mill/translations/es.json | 4 +- .../components/mill/translations/et.json | 4 +- .../components/mill/translations/fi.json | 12 --- .../components/mill/translations/fr.json | 4 +- .../components/mill/translations/he.json | 6 -- .../components/mill/translations/hu.json | 4 +- .../components/mill/translations/id.json | 4 +- .../components/mill/translations/it.json | 4 +- .../components/mill/translations/ja.json | 4 +- .../components/mill/translations/ko.json | 6 -- .../components/mill/translations/lb.json | 8 -- .../components/mill/translations/nl.json | 4 +- .../components/mill/translations/no.json | 4 +- .../components/mill/translations/pl.json | 4 +- .../components/mill/translations/pt-BR.json | 4 +- .../components/mill/translations/pt.json | 8 -- .../components/mill/translations/ru.json | 4 +- .../components/mill/translations/sv.json | 11 --- .../components/mill/translations/tr.json | 4 +- .../components/mill/translations/uk.json | 8 -- .../components/mill/translations/zh-Hans.json | 7 -- .../components/mill/translations/zh-Hant.json | 4 +- .../components/min_max/translations/ca.json | 8 -- .../components/min_max/translations/cs.json | 8 -- .../components/min_max/translations/de.json | 8 -- .../components/min_max/translations/el.json | 8 -- .../components/min_max/translations/en.json | 8 -- .../components/min_max/translations/es.json | 7 -- .../components/min_max/translations/et.json | 8 -- .../components/min_max/translations/fr.json | 8 -- .../components/min_max/translations/he.json | 7 -- .../components/min_max/translations/hu.json | 8 -- .../components/min_max/translations/id.json | 8 -- .../components/min_max/translations/it.json | 8 -- .../components/min_max/translations/ja.json | 8 -- .../components/min_max/translations/ko.json | 34 ++++++++ .../components/min_max/translations/nl.json | 8 -- .../components/min_max/translations/no.json | 8 -- .../components/min_max/translations/pl.json | 8 -- .../min_max/translations/pt-BR.json | 8 -- .../components/min_max/translations/ru.json | 8 -- .../components/min_max/translations/tr.json | 8 -- .../min_max/translations/zh-Hans.json | 8 -- .../min_max/translations/zh-Hant.json | 8 -- .../modem_callerid/translations/bg.json | 6 +- .../modem_callerid/translations/ca.json | 6 +- .../modem_callerid/translations/de.json | 6 +- .../modem_callerid/translations/el.json | 6 +- .../modem_callerid/translations/en.json | 6 +- .../modem_callerid/translations/es.json | 6 +- .../modem_callerid/translations/et.json | 6 +- .../modem_callerid/translations/fr.json | 6 +- .../modem_callerid/translations/hu.json | 6 +- .../modem_callerid/translations/id.json | 6 +- .../modem_callerid/translations/it.json | 6 +- .../modem_callerid/translations/ja.json | 6 +- .../modem_callerid/translations/nl.json | 6 +- .../modem_callerid/translations/no.json | 6 +- .../modem_callerid/translations/pl.json | 6 +- .../modem_callerid/translations/pt-BR.json | 6 +- .../modem_callerid/translations/ru.json | 6 +- .../modem_callerid/translations/tr.json | 6 +- .../modem_callerid/translations/zh-Hant.json | 6 +- .../modern_forms/translations/bg.json | 6 +- .../modern_forms/translations/ca.json | 6 +- .../modern_forms/translations/de.json | 6 +- .../modern_forms/translations/el.json | 6 +- .../modern_forms/translations/en.json | 6 +- .../modern_forms/translations/es.json | 6 +- .../modern_forms/translations/et.json | 6 +- .../modern_forms/translations/fr.json | 6 +- .../modern_forms/translations/he.json | 3 - .../modern_forms/translations/hu.json | 6 +- .../modern_forms/translations/id.json | 6 +- .../modern_forms/translations/it.json | 6 +- .../modern_forms/translations/ja.json | 6 +- .../modern_forms/translations/nl.json | 6 +- .../modern_forms/translations/no.json | 6 +- .../modern_forms/translations/pl.json | 6 +- .../modern_forms/translations/pt-BR.json | 6 +- .../modern_forms/translations/ru.json | 6 +- .../modern_forms/translations/tr.json | 6 +- .../modern_forms/translations/zh-Hant.json | 6 +- .../components/moon/translations/ko.json | 9 +++ .../motion_blinds/translations/bg.json | 4 - .../motion_blinds/translations/ca.json | 17 ++-- .../motion_blinds/translations/cs.json | 4 +- .../motion_blinds/translations/de.json | 17 ++-- .../motion_blinds/translations/el.json | 17 ++-- .../motion_blinds/translations/en.json | 17 ++-- .../motion_blinds/translations/es.json | 17 ++-- .../motion_blinds/translations/et.json | 17 ++-- .../motion_blinds/translations/fr.json | 17 ++-- .../motion_blinds/translations/he.json | 1 - .../motion_blinds/translations/hu.json | 17 ++-- .../motion_blinds/translations/id.json | 17 ++-- .../motion_blinds/translations/it.json | 17 ++-- .../motion_blinds/translations/ja.json | 17 ++-- .../motion_blinds/translations/ka.json | 4 +- .../motion_blinds/translations/ko.json | 7 +- .../motion_blinds/translations/lb.json | 4 +- .../motion_blinds/translations/nl.json | 17 ++-- .../motion_blinds/translations/no.json | 17 ++-- .../motion_blinds/translations/pl.json | 17 ++-- .../motion_blinds/translations/pt-BR.json | 17 ++-- .../motion_blinds/translations/pt.json | 7 +- .../motion_blinds/translations/ru.json | 17 ++-- .../motion_blinds/translations/sk.json | 5 -- .../motion_blinds/translations/sl.json | 17 ---- .../motion_blinds/translations/tr.json | 17 ++-- .../motion_blinds/translations/uk.json | 7 +- .../motion_blinds/translations/zh-Hans.json | 1 - .../motion_blinds/translations/zh-Hant.json | 17 ++-- .../components/mysensors/translations/ca.json | 3 +- .../components/mysensors/translations/cs.json | 3 +- .../components/mysensors/translations/de.json | 3 +- .../components/mysensors/translations/el.json | 3 +- .../components/mysensors/translations/en.json | 3 +- .../components/mysensors/translations/es.json | 3 +- .../components/mysensors/translations/et.json | 3 +- .../components/mysensors/translations/fr.json | 3 +- .../components/mysensors/translations/hu.json | 3 +- .../components/mysensors/translations/id.json | 3 +- .../components/mysensors/translations/it.json | 3 +- .../components/mysensors/translations/ja.json | 3 +- .../components/mysensors/translations/ko.json | 3 +- .../components/mysensors/translations/nl.json | 3 +- .../components/mysensors/translations/no.json | 3 +- .../components/mysensors/translations/pl.json | 3 +- .../mysensors/translations/pt-BR.json | 3 +- .../components/mysensors/translations/ru.json | 3 +- .../components/mysensors/translations/tr.json | 3 +- .../mysensors/translations/zh-Hant.json | 3 +- .../components/neato/translations/ca.json | 3 +- .../components/neato/translations/cs.json | 3 +- .../components/neato/translations/de.json | 3 +- .../components/neato/translations/el.json | 3 +- .../components/neato/translations/en.json | 3 +- .../components/neato/translations/es.json | 3 +- .../components/neato/translations/et.json | 3 +- .../components/neato/translations/fr.json | 3 +- .../components/neato/translations/hu.json | 3 +- .../components/neato/translations/id.json | 3 +- .../components/neato/translations/it.json | 3 +- .../components/neato/translations/ja.json | 3 +- .../components/neato/translations/ko.json | 3 +- .../components/neato/translations/lb.json | 3 +- .../components/neato/translations/nl.json | 3 +- .../components/neato/translations/no.json | 3 +- .../components/neato/translations/pl.json | 3 +- .../components/neato/translations/pt-BR.json | 3 +- .../components/neato/translations/ru.json | 3 +- .../components/neato/translations/sl.json | 3 +- .../components/neato/translations/tr.json | 3 +- .../components/neato/translations/uk.json | 3 +- .../neato/translations/zh-Hant.json | 3 +- .../components/netatmo/translations/nl.json | 2 +- .../netatmo/translations/pt-BR.json | 2 +- .../components/netgear/translations/bg.json | 11 +-- .../components/netgear/translations/ca.json | 8 +- .../components/netgear/translations/cs.json | 2 - .../components/netgear/translations/de.json | 8 +- .../components/netgear/translations/el.json | 8 +- .../components/netgear/translations/en.json | 8 +- .../components/netgear/translations/es.json | 8 +- .../components/netgear/translations/et.json | 8 +- .../components/netgear/translations/fr.json | 8 +- .../components/netgear/translations/he.json | 12 +-- .../components/netgear/translations/hu.json | 8 +- .../components/netgear/translations/id.json | 8 +- .../components/netgear/translations/it.json | 8 +- .../components/netgear/translations/ja.json | 8 +- .../components/netgear/translations/nl.json | 8 +- .../components/netgear/translations/no.json | 8 +- .../components/netgear/translations/pl.json | 8 +- .../netgear/translations/pt-BR.json | 8 +- .../components/netgear/translations/ru.json | 8 +- .../components/netgear/translations/sk.json | 11 --- .../components/netgear/translations/tr.json | 8 +- .../netgear/translations/zh-Hans.json | 8 +- .../netgear/translations/zh-Hant.json | 8 +- .../components/nexia/translations/bg.json | 3 +- .../components/nexia/translations/ca.json | 3 +- .../components/nexia/translations/cs.json | 3 +- .../components/nexia/translations/de.json | 3 +- .../components/nexia/translations/el.json | 3 +- .../components/nexia/translations/en.json | 3 +- .../components/nexia/translations/es-419.json | 3 +- .../components/nexia/translations/es.json | 3 +- .../components/nexia/translations/et.json | 3 +- .../components/nexia/translations/fr.json | 3 +- .../components/nexia/translations/he.json | 3 +- .../components/nexia/translations/hu.json | 3 +- .../components/nexia/translations/id.json | 3 +- .../components/nexia/translations/it.json | 3 +- .../components/nexia/translations/ja.json | 3 +- .../components/nexia/translations/ko.json | 3 +- .../components/nexia/translations/lb.json | 3 +- .../components/nexia/translations/nl.json | 3 +- .../components/nexia/translations/no.json | 3 +- .../components/nexia/translations/pl.json | 3 +- .../components/nexia/translations/pt-BR.json | 3 +- .../components/nexia/translations/ru.json | 3 +- .../components/nexia/translations/sl.json | 3 +- .../components/nexia/translations/sv.json | 3 +- .../components/nexia/translations/tr.json | 3 +- .../components/nexia/translations/uk.json | 3 +- .../nexia/translations/zh-Hant.json | 3 +- .../nfandroidtv/translations/bg.json | 3 +- .../nfandroidtv/translations/ca.json | 3 +- .../nfandroidtv/translations/de.json | 3 +- .../nfandroidtv/translations/el.json | 3 +- .../nfandroidtv/translations/en.json | 3 +- .../nfandroidtv/translations/es.json | 3 +- .../nfandroidtv/translations/et.json | 3 +- .../nfandroidtv/translations/fr.json | 3 +- .../nfandroidtv/translations/he.json | 3 +- .../nfandroidtv/translations/hu.json | 3 +- .../nfandroidtv/translations/id.json | 3 +- .../nfandroidtv/translations/it.json | 3 +- .../nfandroidtv/translations/ja.json | 3 +- .../nfandroidtv/translations/nl.json | 3 +- .../nfandroidtv/translations/no.json | 3 +- .../nfandroidtv/translations/pl.json | 3 +- .../nfandroidtv/translations/pt-BR.json | 3 +- .../nfandroidtv/translations/ru.json | 3 +- .../nfandroidtv/translations/tr.json | 3 +- .../nfandroidtv/translations/zh-Hant.json | 3 +- .../nightscout/translations/ca.json | 1 - .../nightscout/translations/cs.json | 1 - .../nightscout/translations/de.json | 1 - .../nightscout/translations/el.json | 1 - .../nightscout/translations/en.json | 1 - .../nightscout/translations/es.json | 1 - .../nightscout/translations/et.json | 1 - .../nightscout/translations/fr.json | 1 - .../nightscout/translations/hu.json | 1 - .../nightscout/translations/id.json | 1 - .../nightscout/translations/it.json | 1 - .../nightscout/translations/ja.json | 1 - .../nightscout/translations/ko.json | 1 - .../nightscout/translations/lb.json | 1 - .../nightscout/translations/nl.json | 1 - .../nightscout/translations/no.json | 1 - .../nightscout/translations/pl.json | 1 - .../nightscout/translations/pt-BR.json | 1 - .../nightscout/translations/ru.json | 1 - .../nightscout/translations/tr.json | 1 - .../nightscout/translations/uk.json | 1 - .../nightscout/translations/zh-Hant.json | 1 - .../nmap_tracker/translations/ar.json | 3 +- .../nmap_tracker/translations/ca.json | 3 +- .../nmap_tracker/translations/cs.json | 3 +- .../nmap_tracker/translations/de.json | 3 +- .../nmap_tracker/translations/el.json | 3 +- .../nmap_tracker/translations/en.json | 3 +- .../nmap_tracker/translations/es.json | 3 +- .../nmap_tracker/translations/et.json | 3 +- .../nmap_tracker/translations/fr.json | 3 +- .../nmap_tracker/translations/he.json | 3 +- .../nmap_tracker/translations/hu.json | 3 +- .../nmap_tracker/translations/id.json | 3 +- .../nmap_tracker/translations/it.json | 3 +- .../nmap_tracker/translations/ja.json | 3 +- .../nmap_tracker/translations/nl.json | 3 +- .../nmap_tracker/translations/no.json | 3 +- .../nmap_tracker/translations/pl.json | 3 +- .../nmap_tracker/translations/pt-BR.json | 3 +- .../nmap_tracker/translations/ru.json | 3 +- .../nmap_tracker/translations/tr.json | 3 +- .../nmap_tracker/translations/zh-Hans.json | 3 +- .../nmap_tracker/translations/zh-Hant.json | 3 +- .../components/notion/translations/bg.json | 1 - .../components/notion/translations/ca.json | 1 - .../components/notion/translations/cs.json | 1 - .../components/notion/translations/da.json | 3 - .../components/notion/translations/de.json | 1 - .../components/notion/translations/el.json | 1 - .../components/notion/translations/en.json | 1 - .../notion/translations/es-419.json | 3 - .../components/notion/translations/es.json | 1 - .../components/notion/translations/et.json | 1 - .../components/notion/translations/fr.json | 1 - .../components/notion/translations/hr.json | 3 - .../components/notion/translations/hu.json | 1 - .../components/notion/translations/id.json | 1 - .../components/notion/translations/it.json | 1 - .../components/notion/translations/ja.json | 1 - .../components/notion/translations/ko.json | 3 +- .../components/notion/translations/lb.json | 3 +- .../components/notion/translations/nl.json | 1 - .../components/notion/translations/no.json | 1 - .../components/notion/translations/pl.json | 1 - .../components/notion/translations/pt-BR.json | 1 - .../components/notion/translations/ru.json | 1 - .../components/notion/translations/sl.json | 3 - .../components/notion/translations/sv.json | 3 - .../components/notion/translations/tr.json | 1 - .../components/notion/translations/uk.json | 3 +- .../notion/translations/zh-Hans.json | 3 - .../notion/translations/zh-Hant.json | 1 - .../components/nut/translations/bg.json | 10 --- .../components/nut/translations/ca.json | 17 +--- .../components/nut/translations/cs.json | 17 +--- .../components/nut/translations/de.json | 17 +--- .../components/nut/translations/el.json | 17 +--- .../components/nut/translations/en.json | 17 +--- .../components/nut/translations/es-419.json | 13 +--- .../components/nut/translations/es.json | 17 +--- .../components/nut/translations/et.json | 17 +--- .../components/nut/translations/fr.json | 17 +--- .../components/nut/translations/he.json | 6 -- .../components/nut/translations/hu.json | 17 +--- .../components/nut/translations/id.json | 17 +--- .../components/nut/translations/it.json | 17 +--- .../components/nut/translations/ja.json | 17 +--- .../components/nut/translations/ko.json | 17 +--- .../components/nut/translations/lb.json | 13 +--- .../components/nut/translations/nl.json | 17 +--- .../components/nut/translations/no.json | 17 +--- .../components/nut/translations/pl.json | 17 +--- .../components/nut/translations/pt-BR.json | 17 +--- .../components/nut/translations/pt.json | 11 --- .../components/nut/translations/ru.json | 17 +--- .../components/nut/translations/sk.json | 17 +--- .../components/nut/translations/sl.json | 13 +--- .../components/nut/translations/sv.json | 10 --- .../components/nut/translations/tr.json | 17 +--- .../components/nut/translations/uk.json | 13 +--- .../components/nut/translations/zh-Hans.json | 17 +--- .../components/nut/translations/zh-Hant.json | 17 +--- .../components/onewire/translations/af.json | 14 ---- .../components/onewire/translations/bg.json | 12 +-- .../components/onewire/translations/ca.json | 17 +--- .../components/onewire/translations/cs.json | 13 +--- .../components/onewire/translations/de.json | 17 +--- .../components/onewire/translations/el.json | 17 +--- .../components/onewire/translations/en.json | 17 +--- .../components/onewire/translations/es.json | 16 +--- .../components/onewire/translations/et.json | 17 +--- .../components/onewire/translations/fr.json | 17 +--- .../components/onewire/translations/he.json | 2 +- .../components/onewire/translations/hu.json | 17 +--- .../components/onewire/translations/id.json | 17 +--- .../components/onewire/translations/it.json | 17 +--- .../components/onewire/translations/ja.json | 17 +--- .../components/onewire/translations/ka.json | 13 +--- .../components/onewire/translations/ko.json | 13 +--- .../components/onewire/translations/lb.json | 16 +--- .../components/onewire/translations/nl.json | 17 +--- .../components/onewire/translations/no.json | 17 +--- .../components/onewire/translations/pl.json | 17 +--- .../onewire/translations/pt-BR.json | 17 +--- .../components/onewire/translations/pt.json | 8 -- .../components/onewire/translations/ru.json | 17 +--- .../components/onewire/translations/sk.json | 12 +-- .../components/onewire/translations/sl.json | 3 - .../components/onewire/translations/sv.json | 9 --- .../components/onewire/translations/tr.json | 17 +--- .../components/onewire/translations/uk.json | 13 +--- .../onewire/translations/zh-Hant.json | 17 +--- .../components/onvif/translations/bg.json | 13 ---- .../components/onvif/translations/ca.json | 15 ---- .../components/onvif/translations/cs.json | 15 ---- .../components/onvif/translations/de.json | 15 ---- .../components/onvif/translations/el.json | 15 ---- .../components/onvif/translations/en.json | 15 ---- .../components/onvif/translations/es-419.json | 14 ---- .../components/onvif/translations/es.json | 15 ---- .../components/onvif/translations/et.json | 15 ---- .../components/onvif/translations/fi.json | 13 ---- .../components/onvif/translations/fr.json | 15 ---- .../components/onvif/translations/he.json | 15 ---- .../components/onvif/translations/hu.json | 15 ---- .../components/onvif/translations/id.json | 15 ---- .../components/onvif/translations/it.json | 15 ---- .../components/onvif/translations/ja.json | 15 ---- .../components/onvif/translations/ko.json | 15 ---- .../components/onvif/translations/lb.json | 15 ---- .../components/onvif/translations/nl.json | 15 ---- .../components/onvif/translations/no.json | 15 ---- .../components/onvif/translations/pl.json | 15 ---- .../components/onvif/translations/pt-BR.json | 15 ---- .../components/onvif/translations/pt.json | 15 ---- .../components/onvif/translations/ru.json | 15 ---- .../components/onvif/translations/sk.json | 11 --- .../components/onvif/translations/sl.json | 14 ---- .../components/onvif/translations/sv.json | 12 --- .../components/onvif/translations/tr.json | 15 ---- .../components/onvif/translations/uk.json | 15 ---- .../onvif/translations/zh-Hans.json | 15 ---- .../onvif/translations/zh-Hant.json | 15 ---- .../opentherm_gw/translations/bg.json | 6 +- .../opentherm_gw/translations/ca.json | 6 +- .../opentherm_gw/translations/cs.json | 6 +- .../opentherm_gw/translations/da.json | 6 +- .../opentherm_gw/translations/de.json | 6 +- .../opentherm_gw/translations/el.json | 6 +- .../opentherm_gw/translations/en.json | 6 +- .../opentherm_gw/translations/es-419.json | 6 +- .../opentherm_gw/translations/es.json | 6 +- .../opentherm_gw/translations/et.json | 6 +- .../opentherm_gw/translations/fr.json | 6 +- .../opentherm_gw/translations/hu.json | 6 +- .../opentherm_gw/translations/id.json | 6 +- .../opentherm_gw/translations/it.json | 6 +- .../opentherm_gw/translations/ja.json | 6 +- .../opentherm_gw/translations/ko.json | 6 +- .../opentherm_gw/translations/lb.json | 6 +- .../opentherm_gw/translations/nl.json | 6 +- .../opentherm_gw/translations/no.json | 6 +- .../opentherm_gw/translations/pl.json | 6 +- .../opentherm_gw/translations/pt-BR.json | 6 +- .../opentherm_gw/translations/ru.json | 6 +- .../opentherm_gw/translations/sl.json | 6 +- .../opentherm_gw/translations/sv.json | 6 +- .../opentherm_gw/translations/tr.json | 6 +- .../opentherm_gw/translations/uk.json | 6 +- .../opentherm_gw/translations/zh-Hant.json | 6 +- .../openweathermap/translations/ar.json | 3 +- .../openweathermap/translations/bg.json | 3 +- .../openweathermap/translations/ca.json | 3 +- .../openweathermap/translations/cs.json | 3 +- .../openweathermap/translations/de.json | 3 +- .../openweathermap/translations/el.json | 3 +- .../openweathermap/translations/en.json | 3 +- .../openweathermap/translations/es.json | 3 +- .../openweathermap/translations/et.json | 3 +- .../openweathermap/translations/fr.json | 3 +- .../openweathermap/translations/he.json | 3 +- .../openweathermap/translations/hu.json | 3 +- .../openweathermap/translations/id.json | 3 +- .../openweathermap/translations/it.json | 3 +- .../openweathermap/translations/ja.json | 3 +- .../openweathermap/translations/ko.json | 3 +- .../openweathermap/translations/lb.json | 3 +- .../openweathermap/translations/nl.json | 3 +- .../openweathermap/translations/no.json | 3 +- .../openweathermap/translations/pl.json | 3 +- .../openweathermap/translations/pt-BR.json | 3 +- .../openweathermap/translations/ru.json | 3 +- .../openweathermap/translations/sv.json | 3 +- .../openweathermap/translations/tr.json | 3 +- .../openweathermap/translations/uk.json | 3 +- .../openweathermap/translations/zh-Hant.json | 3 +- .../p1_monitor/translations/bg.json | 3 +- .../p1_monitor/translations/ca.json | 3 +- .../p1_monitor/translations/cs.json | 3 +- .../p1_monitor/translations/de.json | 3 +- .../p1_monitor/translations/el.json | 3 +- .../p1_monitor/translations/en.json | 3 +- .../p1_monitor/translations/es.json | 3 +- .../p1_monitor/translations/et.json | 3 +- .../p1_monitor/translations/fr.json | 3 +- .../p1_monitor/translations/he.json | 3 +- .../p1_monitor/translations/hu.json | 3 +- .../p1_monitor/translations/id.json | 3 +- .../p1_monitor/translations/it.json | 3 +- .../p1_monitor/translations/ja.json | 3 +- .../p1_monitor/translations/ko.json | 3 +- .../p1_monitor/translations/nl.json | 3 +- .../p1_monitor/translations/no.json | 3 +- .../p1_monitor/translations/pl.json | 3 +- .../p1_monitor/translations/pt-BR.json | 3 +- .../p1_monitor/translations/ru.json | 3 +- .../p1_monitor/translations/tr.json | 3 +- .../p1_monitor/translations/zh-Hant.json | 3 +- .../components/peco/translations/ca.json | 4 +- .../components/peco/translations/de.json | 4 +- .../components/peco/translations/el.json | 4 +- .../components/peco/translations/en.json | 4 +- .../components/peco/translations/et.json | 4 +- .../components/peco/translations/fr.json | 4 +- .../components/peco/translations/hu.json | 4 +- .../components/peco/translations/id.json | 4 +- .../components/peco/translations/it.json | 4 +- .../components/peco/translations/ja.json | 4 +- .../components/peco/translations/nl.json | 4 +- .../components/peco/translations/no.json | 4 +- .../components/peco/translations/pl.json | 4 +- .../components/peco/translations/pt-BR.json | 4 +- .../components/peco/translations/ru.json | 4 +- .../components/peco/translations/tr.json | 4 +- .../components/peco/translations/zh-Hant.json | 4 +- .../components/picnic/translations/ca.json | 3 +- .../components/picnic/translations/de.json | 3 +- .../components/picnic/translations/el.json | 3 +- .../components/picnic/translations/en.json | 3 +- .../components/picnic/translations/es.json | 3 +- .../components/picnic/translations/et.json | 3 +- .../components/picnic/translations/fr.json | 3 +- .../components/picnic/translations/he.json | 3 +- .../components/picnic/translations/hu.json | 3 +- .../components/picnic/translations/id.json | 3 +- .../components/picnic/translations/it.json | 3 +- .../components/picnic/translations/ja.json | 3 +- .../components/picnic/translations/nl.json | 3 +- .../components/picnic/translations/no.json | 3 +- .../components/picnic/translations/pl.json | 3 +- .../components/picnic/translations/pt-BR.json | 3 +- .../components/picnic/translations/ru.json | 3 +- .../components/picnic/translations/tr.json | 3 +- .../picnic/translations/zh-Hant.json | 3 +- .../components/plex/translations/bg.json | 6 -- .../components/plex/translations/ca.json | 6 +- .../components/plex/translations/cs.json | 6 +- .../components/plex/translations/de.json | 6 +- .../components/plex/translations/el.json | 6 +- .../components/plex/translations/en.json | 6 +- .../components/plex/translations/es.json | 6 +- .../components/plex/translations/et.json | 6 +- .../components/plex/translations/fi.json | 6 +- .../components/plex/translations/fr.json | 6 +- .../components/plex/translations/hu.json | 6 +- .../components/plex/translations/id.json | 6 +- .../components/plex/translations/it.json | 6 +- .../components/plex/translations/ja.json | 6 +- .../components/plex/translations/ko.json | 6 +- .../components/plex/translations/lb.json | 6 +- .../components/plex/translations/nl.json | 6 +- .../components/plex/translations/no.json | 6 +- .../components/plex/translations/pl.json | 6 +- .../components/plex/translations/pt-BR.json | 6 +- .../components/plex/translations/ru.json | 6 +- .../components/plex/translations/sl.json | 6 +- .../components/plex/translations/sv.json | 6 +- .../components/plex/translations/tr.json | 6 +- .../components/plex/translations/uk.json | 6 +- .../components/plex/translations/zh-Hans.json | 6 +- .../components/plex/translations/zh-Hant.json | 6 +- .../components/poolsense/translations/bg.json | 4 +- .../components/poolsense/translations/ca.json | 4 +- .../components/poolsense/translations/cs.json | 4 +- .../components/poolsense/translations/de.json | 4 +- .../components/poolsense/translations/el.json | 4 +- .../components/poolsense/translations/en.json | 4 +- .../components/poolsense/translations/es.json | 4 +- .../components/poolsense/translations/et.json | 4 +- .../components/poolsense/translations/fr.json | 4 +- .../components/poolsense/translations/he.json | 3 +- .../components/poolsense/translations/hu.json | 4 +- .../components/poolsense/translations/id.json | 4 +- .../components/poolsense/translations/it.json | 4 +- .../components/poolsense/translations/ja.json | 4 +- .../components/poolsense/translations/ko.json | 4 +- .../components/poolsense/translations/lb.json | 4 +- .../components/poolsense/translations/nl.json | 4 +- .../components/poolsense/translations/no.json | 4 +- .../components/poolsense/translations/pl.json | 4 +- .../poolsense/translations/pt-BR.json | 4 +- .../components/poolsense/translations/pt.json | 4 +- .../components/poolsense/translations/ru.json | 4 +- .../components/poolsense/translations/sv.json | 9 --- .../components/poolsense/translations/tr.json | 4 +- .../components/poolsense/translations/uk.json | 4 +- .../poolsense/translations/zh-Hant.json | 4 +- .../components/ps4/translations/bg.json | 11 +-- .../components/ps4/translations/ca.json | 11 +-- .../components/ps4/translations/cs.json | 11 +-- .../components/ps4/translations/da.json | 11 +-- .../components/ps4/translations/de.json | 11 +-- .../components/ps4/translations/el.json | 11 +-- .../components/ps4/translations/en.json | 11 +-- .../components/ps4/translations/es-419.json | 11 +-- .../components/ps4/translations/es.json | 11 +-- .../components/ps4/translations/et.json | 11 +-- .../components/ps4/translations/fi.json | 9 +-- .../components/ps4/translations/fr.json | 11 +-- .../components/ps4/translations/he.json | 9 +-- .../components/ps4/translations/hu.json | 11 +-- .../components/ps4/translations/id.json | 11 +-- .../components/ps4/translations/it.json | 11 +-- .../components/ps4/translations/ja.json | 11 +-- .../components/ps4/translations/ko.json | 11 +-- .../components/ps4/translations/lb.json | 11 +-- .../components/ps4/translations/nl.json | 11 +-- .../components/ps4/translations/nn.json | 9 +-- .../components/ps4/translations/no.json | 11 +-- .../components/ps4/translations/pl.json | 11 +-- .../components/ps4/translations/pt-BR.json | 11 +-- .../components/ps4/translations/pt.json | 9 +-- .../components/ps4/translations/ru.json | 11 +-- .../components/ps4/translations/sl.json | 11 +-- .../components/ps4/translations/sv.json | 11 +-- .../components/ps4/translations/th.json | 9 +-- .../components/ps4/translations/tr.json | 11 +-- .../components/ps4/translations/uk.json | 11 +-- .../components/ps4/translations/zh-Hans.json | 10 +-- .../components/ps4/translations/zh-Hant.json | 11 +-- .../pvpc_hourly_pricing/translations/ca.json | 8 +- .../pvpc_hourly_pricing/translations/cs.json | 3 +- .../pvpc_hourly_pricing/translations/de.json | 8 +- .../pvpc_hourly_pricing/translations/el.json | 8 +- .../pvpc_hourly_pricing/translations/en.json | 8 +- .../translations/es-419.json | 4 +- .../pvpc_hourly_pricing/translations/es.json | 8 +- .../pvpc_hourly_pricing/translations/et.json | 8 +- .../pvpc_hourly_pricing/translations/fr.json | 8 +- .../pvpc_hourly_pricing/translations/he.json | 7 -- .../pvpc_hourly_pricing/translations/hu.json | 8 +- .../pvpc_hourly_pricing/translations/id.json | 8 +- .../pvpc_hourly_pricing/translations/it.json | 8 +- .../pvpc_hourly_pricing/translations/ja.json | 8 +- .../pvpc_hourly_pricing/translations/ko.json | 4 +- .../pvpc_hourly_pricing/translations/lb.json | 4 +- .../pvpc_hourly_pricing/translations/nl.json | 8 +- .../pvpc_hourly_pricing/translations/no.json | 8 +- .../pvpc_hourly_pricing/translations/pl.json | 8 +- .../translations/pt-BR.json | 8 +- .../pvpc_hourly_pricing/translations/ru.json | 8 +- .../pvpc_hourly_pricing/translations/sl.json | 4 +- .../pvpc_hourly_pricing/translations/tr.json | 8 +- .../pvpc_hourly_pricing/translations/uk.json | 4 +- .../translations/zh-Hant.json | 8 +- .../components/recorder/translations/ko.json | 4 +- .../components/remote/translations/ca.json | 1 - .../components/remote/translations/cs.json | 1 - .../components/remote/translations/de.json | 1 - .../components/remote/translations/el.json | 1 - .../components/remote/translations/en.json | 1 - .../components/remote/translations/es.json | 1 - .../components/remote/translations/et.json | 1 - .../components/remote/translations/fr.json | 1 - .../components/remote/translations/he.json | 1 - .../components/remote/translations/hu.json | 1 - .../components/remote/translations/id.json | 1 - .../components/remote/translations/it.json | 1 - .../components/remote/translations/ja.json | 1 - .../components/remote/translations/nl.json | 1 - .../components/remote/translations/no.json | 1 - .../components/remote/translations/pl.json | 1 - .../components/remote/translations/pt-BR.json | 1 - .../components/remote/translations/ru.json | 1 - .../components/remote/translations/sv.json | 1 - .../components/remote/translations/tr.json | 1 - .../remote/translations/zh-Hans.json | 1 - .../remote/translations/zh-Hant.json | 1 - .../components/rfxtrx/translations/ca.json | 5 +- .../components/rfxtrx/translations/cs.json | 7 +- .../components/rfxtrx/translations/de.json | 5 +- .../components/rfxtrx/translations/el.json | 5 +- .../components/rfxtrx/translations/en.json | 5 +- .../components/rfxtrx/translations/es.json | 5 +- .../components/rfxtrx/translations/et.json | 5 +- .../components/rfxtrx/translations/fr.json | 5 +- .../components/rfxtrx/translations/hu.json | 5 +- .../components/rfxtrx/translations/id.json | 5 +- .../components/rfxtrx/translations/it.json | 5 +- .../components/rfxtrx/translations/ja.json | 5 +- .../components/rfxtrx/translations/ko.json | 5 +- .../components/rfxtrx/translations/lb.json | 7 +- .../components/rfxtrx/translations/nl.json | 5 +- .../components/rfxtrx/translations/no.json | 5 +- .../components/rfxtrx/translations/pl.json | 5 +- .../components/rfxtrx/translations/pt-BR.json | 5 +- .../components/rfxtrx/translations/ru.json | 5 +- .../components/rfxtrx/translations/sv.json | 7 +- .../components/rfxtrx/translations/tr.json | 5 +- .../components/rfxtrx/translations/uk.json | 5 +- .../rfxtrx/translations/zh-Hant.json | 5 +- .../components/roku/translations/ca.json | 7 +- .../components/roku/translations/cs.json | 7 +- .../components/roku/translations/de.json | 7 +- .../components/roku/translations/el.json | 7 +- .../components/roku/translations/en.json | 7 +- .../components/roku/translations/es-419.json | 4 - .../components/roku/translations/es.json | 7 +- .../components/roku/translations/et.json | 7 +- .../components/roku/translations/fr.json | 7 +- .../components/roku/translations/hu.json | 7 +- .../components/roku/translations/id.json | 7 +- .../components/roku/translations/it.json | 7 +- .../components/roku/translations/ja.json | 7 +- .../components/roku/translations/ko.json | 7 +- .../components/roku/translations/lb.json | 7 +- .../components/roku/translations/nl.json | 7 +- .../components/roku/translations/no.json | 7 +- .../components/roku/translations/pl.json | 7 +- .../components/roku/translations/pt-BR.json | 7 +- .../components/roku/translations/pt.json | 3 - .../components/roku/translations/ru.json | 7 +- .../components/roku/translations/sl.json | 4 +- .../components/roku/translations/sv.json | 4 - .../components/roku/translations/tr.json | 7 +- .../components/roku/translations/uk.json | 7 +- .../components/roku/translations/zh-Hant.json | 7 +- .../components/roomba/translations/bg.json | 6 +- .../components/roomba/translations/ca.json | 14 +--- .../components/roomba/translations/cs.json | 9 +-- .../components/roomba/translations/da.json | 3 - .../components/roomba/translations/de.json | 14 +--- .../components/roomba/translations/el.json | 14 +--- .../components/roomba/translations/en.json | 14 +--- .../roomba/translations/es-419.json | 6 +- .../components/roomba/translations/es.json | 14 +--- .../components/roomba/translations/et.json | 14 +--- .../components/roomba/translations/fr.json | 14 +--- .../components/roomba/translations/he.json | 8 +- .../components/roomba/translations/hu.json | 14 +--- .../components/roomba/translations/id.json | 14 +--- .../components/roomba/translations/it.json | 14 +--- .../components/roomba/translations/ja.json | 14 +--- .../components/roomba/translations/ko.json | 14 +--- .../components/roomba/translations/lb.json | 14 +--- .../components/roomba/translations/nl.json | 14 +--- .../components/roomba/translations/no.json | 14 +--- .../components/roomba/translations/pl.json | 14 +--- .../components/roomba/translations/pt-BR.json | 14 +--- .../components/roomba/translations/pt.json | 5 +- .../components/roomba/translations/ru.json | 14 +--- .../components/roomba/translations/sl.json | 6 +- .../components/roomba/translations/sv.json | 6 +- .../components/roomba/translations/tr.json | 14 +--- .../components/roomba/translations/uk.json | 6 +- .../roomba/translations/zh-Hant.json | 14 +--- .../components/roon/translations/bg.json | 5 -- .../components/roon/translations/ca.json | 6 -- .../components/roon/translations/cs.json | 5 -- .../components/roon/translations/de.json | 6 -- .../components/roon/translations/el.json | 6 -- .../components/roon/translations/en.json | 6 -- .../components/roon/translations/es.json | 6 -- .../components/roon/translations/et.json | 6 -- .../components/roon/translations/fr.json | 6 -- .../components/roon/translations/he.json | 6 -- .../components/roon/translations/hu.json | 4 - .../components/roon/translations/id.json | 6 -- .../components/roon/translations/it.json | 4 - .../components/roon/translations/ja.json | 6 -- .../components/roon/translations/ko.json | 13 ++-- .../components/roon/translations/lb.json | 6 -- .../components/roon/translations/nl.json | 6 -- .../components/roon/translations/no.json | 6 -- .../components/roon/translations/pl.json | 6 -- .../components/roon/translations/pt-BR.json | 6 -- .../components/roon/translations/pt.json | 7 -- .../components/roon/translations/ru.json | 6 -- .../components/roon/translations/tr.json | 6 -- .../components/roon/translations/uk.json | 6 -- .../components/roon/translations/zh-Hant.json | 6 -- .../components/sabnzbd/translations/ko.json | 15 ++++ .../components/samsungtv/translations/bg.json | 3 - .../components/samsungtv/translations/ca.json | 4 +- .../components/samsungtv/translations/cs.json | 3 +- .../components/samsungtv/translations/da.json | 3 +- .../components/samsungtv/translations/de.json | 4 +- .../components/samsungtv/translations/el.json | 4 +- .../components/samsungtv/translations/en.json | 4 +- .../samsungtv/translations/es-419.json | 3 +- .../components/samsungtv/translations/es.json | 4 +- .../components/samsungtv/translations/et.json | 4 +- .../components/samsungtv/translations/fr.json | 4 +- .../components/samsungtv/translations/he.json | 3 - .../components/samsungtv/translations/hu.json | 4 +- .../components/samsungtv/translations/id.json | 4 +- .../components/samsungtv/translations/it.json | 4 +- .../components/samsungtv/translations/ja.json | 4 +- .../components/samsungtv/translations/ko.json | 6 +- .../components/samsungtv/translations/lb.json | 3 +- .../components/samsungtv/translations/nl.json | 4 +- .../components/samsungtv/translations/no.json | 4 +- .../components/samsungtv/translations/pl.json | 4 +- .../samsungtv/translations/pt-BR.json | 4 +- .../components/samsungtv/translations/pt.json | 3 - .../components/samsungtv/translations/ru.json | 4 +- .../components/samsungtv/translations/sl.json | 3 +- .../components/samsungtv/translations/sv.json | 3 +- .../components/samsungtv/translations/tr.json | 4 +- .../components/samsungtv/translations/uk.json | 3 +- .../samsungtv/translations/zh-Hans.json | 3 +- .../samsungtv/translations/zh-Hant.json | 4 +- .../components/sensibo/translations/bg.json | 3 +- .../components/sensibo/translations/ca.json | 3 +- .../components/sensibo/translations/cs.json | 3 +- .../components/sensibo/translations/de.json | 3 +- .../components/sensibo/translations/el.json | 3 +- .../components/sensibo/translations/en.json | 3 +- .../components/sensibo/translations/es.json | 3 +- .../components/sensibo/translations/et.json | 3 +- .../components/sensibo/translations/fr.json | 3 +- .../components/sensibo/translations/he.json | 3 +- .../components/sensibo/translations/hu.json | 3 +- .../components/sensibo/translations/id.json | 3 +- .../components/sensibo/translations/it.json | 3 +- .../components/sensibo/translations/ja.json | 3 +- .../components/sensibo/translations/nl.json | 3 +- .../components/sensibo/translations/no.json | 3 +- .../components/sensibo/translations/pl.json | 3 +- .../sensibo/translations/pt-BR.json | 3 +- .../components/sensibo/translations/ru.json | 3 +- .../components/sensibo/translations/sk.json | 3 +- .../components/sensibo/translations/tr.json | 3 +- .../sensibo/translations/zh-Hant.json | 3 +- .../components/sentry/translations/af.json | 9 --- .../components/sentry/translations/ca.json | 4 +- .../components/sentry/translations/cs.json | 4 +- .../components/sentry/translations/da.json | 6 -- .../components/sentry/translations/de.json | 4 +- .../components/sentry/translations/el.json | 4 +- .../components/sentry/translations/en.json | 4 +- .../sentry/translations/es-419.json | 6 -- .../components/sentry/translations/es.json | 4 +- .../components/sentry/translations/et.json | 4 +- .../components/sentry/translations/fr.json | 4 +- .../components/sentry/translations/hu.json | 4 +- .../components/sentry/translations/id.json | 4 +- .../components/sentry/translations/it.json | 4 +- .../components/sentry/translations/ja.json | 4 +- .../components/sentry/translations/ko.json | 4 +- .../components/sentry/translations/lb.json | 4 +- .../components/sentry/translations/nl.json | 4 +- .../components/sentry/translations/no.json | 4 +- .../components/sentry/translations/pl.json | 4 +- .../components/sentry/translations/pt-BR.json | 4 +- .../components/sentry/translations/ru.json | 4 +- .../components/sentry/translations/sl.json | 6 -- .../components/sentry/translations/sv.json | 6 -- .../components/sentry/translations/tr.json | 4 +- .../components/sentry/translations/uk.json | 4 +- .../sentry/translations/zh-Hant.json | 4 +- .../components/senz/translations/ko.json | 16 ++++ .../components/shelly/translations/ca.json | 1 + .../components/shelly/translations/fr.json | 1 + .../components/shelly/translations/hu.json | 1 + .../components/shelly/translations/it.json | 1 + .../components/shelly/translations/pt-BR.json | 1 + .../components/sia/translations/ar.json | 3 +- .../components/sia/translations/ca.json | 3 +- .../components/sia/translations/de.json | 3 +- .../components/sia/translations/el.json | 3 +- .../components/sia/translations/en.json | 3 +- .../components/sia/translations/es.json | 3 +- .../components/sia/translations/et.json | 3 +- .../components/sia/translations/fr.json | 3 +- .../components/sia/translations/hu.json | 3 +- .../components/sia/translations/id.json | 3 +- .../components/sia/translations/it.json | 3 +- .../components/sia/translations/ja.json | 3 +- .../components/sia/translations/nl.json | 3 +- .../components/sia/translations/no.json | 3 +- .../components/sia/translations/pl.json | 3 +- .../components/sia/translations/pt-BR.json | 3 +- .../components/sia/translations/ru.json | 3 +- .../components/sia/translations/tr.json | 3 +- .../components/sia/translations/zh-Hant.json | 3 +- .../simplisafe/translations/bg.json | 5 +- .../simplisafe/translations/ca.json | 14 +--- .../simplisafe/translations/cs.json | 9 +-- .../simplisafe/translations/da.json | 6 +- .../simplisafe/translations/de.json | 14 +--- .../simplisafe/translations/el.json | 14 +--- .../simplisafe/translations/en.json | 14 +--- .../simplisafe/translations/es-419.json | 7 +- .../simplisafe/translations/es.json | 14 +--- .../simplisafe/translations/et.json | 14 +--- .../simplisafe/translations/fi.json | 6 +- .../simplisafe/translations/fr.json | 14 +--- .../simplisafe/translations/hu.json | 14 +--- .../simplisafe/translations/id.json | 14 +--- .../simplisafe/translations/it.json | 14 +--- .../simplisafe/translations/ja.json | 14 +--- .../simplisafe/translations/ko.json | 12 +-- .../simplisafe/translations/lb.json | 9 +-- .../simplisafe/translations/nl.json | 14 +--- .../simplisafe/translations/no.json | 14 +--- .../simplisafe/translations/pl.json | 14 +--- .../simplisafe/translations/pt-BR.json | 14 +--- .../simplisafe/translations/pt.json | 4 +- .../simplisafe/translations/ro.json | 6 +- .../simplisafe/translations/ru.json | 14 +--- .../simplisafe/translations/sl.json | 10 +-- .../simplisafe/translations/sv.json | 7 +- .../simplisafe/translations/tr.json | 14 +--- .../simplisafe/translations/uk.json | 9 +-- .../simplisafe/translations/zh-Hans.json | 5 +- .../simplisafe/translations/zh-Hant.json | 14 +--- .../components/siren/translations/cs.json | 3 + .../components/siren/translations/no.json | 3 + .../components/slack/translations/cs.json | 19 +++++ .../{climacell => slack}/translations/he.json | 11 +-- .../components/slack/translations/id.json | 6 +- .../components/smhi/translations/bg.json | 4 +- .../components/smhi/translations/ca.json | 4 +- .../components/smhi/translations/cs.json | 4 +- .../components/smhi/translations/da.json | 4 +- .../components/smhi/translations/de.json | 4 +- .../components/smhi/translations/el.json | 4 +- .../components/smhi/translations/en.json | 4 +- .../components/smhi/translations/es-419.json | 4 +- .../components/smhi/translations/es.json | 4 +- .../components/smhi/translations/et.json | 4 +- .../components/smhi/translations/fi.json | 4 +- .../components/smhi/translations/fr.json | 4 +- .../components/smhi/translations/he.json | 3 +- .../components/smhi/translations/hu.json | 4 +- .../components/smhi/translations/id.json | 4 +- .../components/smhi/translations/it.json | 4 +- .../components/smhi/translations/ja.json | 4 +- .../components/smhi/translations/ko.json | 4 +- .../components/smhi/translations/lb.json | 4 +- .../components/smhi/translations/nl.json | 4 +- .../components/smhi/translations/no.json | 4 +- .../components/smhi/translations/pl.json | 4 +- .../components/smhi/translations/pt-BR.json | 4 +- .../components/smhi/translations/pt.json | 4 +- .../components/smhi/translations/ro.json | 4 +- .../components/smhi/translations/ru.json | 4 +- .../components/smhi/translations/sk.json | 3 +- .../components/smhi/translations/sl.json | 4 +- .../components/smhi/translations/sv.json | 4 +- .../components/smhi/translations/th.json | 3 +- .../components/smhi/translations/tr.json | 4 +- .../components/smhi/translations/uk.json | 4 +- .../components/smhi/translations/zh-Hans.json | 4 +- .../components/smhi/translations/zh-Hant.json | 4 +- .../somfy_mylink/translations/ca.json | 12 +-- .../somfy_mylink/translations/de.json | 12 +-- .../somfy_mylink/translations/el.json | 12 +-- .../somfy_mylink/translations/en.json | 12 +-- .../somfy_mylink/translations/es.json | 12 +-- .../somfy_mylink/translations/et.json | 12 +-- .../somfy_mylink/translations/fr.json | 12 +-- .../somfy_mylink/translations/hu.json | 12 +-- .../somfy_mylink/translations/id.json | 12 +-- .../somfy_mylink/translations/it.json | 12 +-- .../somfy_mylink/translations/ja.json | 12 +-- .../somfy_mylink/translations/ko.json | 12 +-- .../somfy_mylink/translations/lb.json | 3 +- .../somfy_mylink/translations/nl.json | 12 +-- .../somfy_mylink/translations/no.json | 12 +-- .../somfy_mylink/translations/pl.json | 12 +-- .../somfy_mylink/translations/pt-BR.json | 12 +-- .../somfy_mylink/translations/ru.json | 12 +-- .../somfy_mylink/translations/tr.json | 12 +-- .../somfy_mylink/translations/uk.json | 10 +-- .../somfy_mylink/translations/zh-Hant.json | 12 +-- .../components/sonarr/translations/bg.json | 3 - .../components/sonarr/translations/ca.json | 4 - .../components/sonarr/translations/cs.json | 3 - .../components/sonarr/translations/de.json | 4 - .../components/sonarr/translations/el.json | 4 - .../components/sonarr/translations/en.json | 4 - .../components/sonarr/translations/es.json | 4 - .../components/sonarr/translations/et.json | 4 - .../components/sonarr/translations/fr.json | 4 - .../components/sonarr/translations/he.json | 3 - .../components/sonarr/translations/hu.json | 4 - .../components/sonarr/translations/id.json | 4 - .../components/sonarr/translations/it.json | 4 - .../components/sonarr/translations/ja.json | 4 - .../components/sonarr/translations/ko.json | 4 - .../components/sonarr/translations/lb.json | 4 - .../components/sonarr/translations/nl.json | 4 - .../components/sonarr/translations/no.json | 4 - .../components/sonarr/translations/pl.json | 4 - .../components/sonarr/translations/pt-BR.json | 4 - .../components/sonarr/translations/pt.json | 3 - .../components/sonarr/translations/ru.json | 4 - .../components/sonarr/translations/sk.json | 3 +- .../components/sonarr/translations/sl.json | 1 - .../components/sonarr/translations/tr.json | 4 - .../components/sonarr/translations/uk.json | 4 - .../sonarr/translations/zh-Hans.json | 3 - .../sonarr/translations/zh-Hant.json | 4 - .../speedtestdotnet/translations/ar.json | 3 - .../speedtestdotnet/translations/ca.json | 3 +- .../speedtestdotnet/translations/cs.json | 3 +- .../speedtestdotnet/translations/de.json | 3 +- .../speedtestdotnet/translations/el.json | 3 +- .../speedtestdotnet/translations/en.json | 3 +- .../speedtestdotnet/translations/es.json | 3 +- .../speedtestdotnet/translations/et.json | 3 +- .../speedtestdotnet/translations/fr.json | 3 +- .../speedtestdotnet/translations/he.json | 3 +- .../speedtestdotnet/translations/hu.json | 3 +- .../speedtestdotnet/translations/id.json | 3 +- .../speedtestdotnet/translations/it.json | 3 +- .../speedtestdotnet/translations/ja.json | 3 +- .../speedtestdotnet/translations/ko.json | 3 +- .../speedtestdotnet/translations/lb.json | 3 +- .../speedtestdotnet/translations/nl.json | 3 +- .../speedtestdotnet/translations/no.json | 3 +- .../speedtestdotnet/translations/pl.json | 3 +- .../speedtestdotnet/translations/pt-BR.json | 3 +- .../speedtestdotnet/translations/ru.json | 3 +- .../speedtestdotnet/translations/tr.json | 3 +- .../speedtestdotnet/translations/uk.json | 3 +- .../speedtestdotnet/translations/zh-Hant.json | 3 +- .../components/sql/translations/ca.json | 6 +- .../components/sql/translations/de.json | 6 +- .../components/sql/translations/el.json | 6 +- .../components/sql/translations/en.json | 6 +- .../components/sql/translations/es.json | 3 +- .../components/sql/translations/et.json | 6 +- .../components/sql/translations/fr.json | 6 +- .../components/sql/translations/hu.json | 6 +- .../components/sql/translations/id.json | 6 +- .../components/sql/translations/it.json | 6 +- .../components/sql/translations/ja.json | 6 +- .../components/sql/translations/ko.json | 51 ++++++++++++ .../components/sql/translations/nl.json | 6 +- .../components/sql/translations/no.json | 6 +- .../components/sql/translations/pl.json | 6 +- .../components/sql/translations/pt-BR.json | 6 +- .../components/sql/translations/ru.json | 6 +- .../components/sql/translations/tr.json | 6 +- .../components/sql/translations/zh-Hant.json | 6 +- .../steam_online/translations/ko.json | 36 +++++++++ .../components/sun/translations/ko.json | 7 ++ .../components/switch/translations/ca.json | 11 --- .../components/switch/translations/cs.json | 11 --- .../components/switch/translations/de.json | 11 --- .../components/switch/translations/el.json | 11 --- .../components/switch/translations/en.json | 11 --- .../components/switch/translations/es.json | 8 -- .../components/switch/translations/et.json | 11 --- .../components/switch/translations/fr.json | 11 --- .../components/switch/translations/he.json | 11 --- .../components/switch/translations/hu.json | 11 --- .../components/switch/translations/id.json | 11 --- .../components/switch/translations/it.json | 11 --- .../components/switch/translations/ja.json | 11 --- .../components/switch/translations/nl.json | 11 --- .../components/switch/translations/no.json | 11 --- .../components/switch/translations/pl.json | 11 --- .../components/switch/translations/pt-BR.json | 11 --- .../components/switch/translations/ru.json | 11 --- .../components/switch/translations/sv.json | 1 - .../components/switch/translations/tr.json | 11 --- .../switch/translations/zh-Hans.json | 1 - .../switch/translations/zh-Hant.json | 11 --- .../switch_as_x/translations/ca.json | 8 +- .../switch_as_x/translations/cs.json | 8 +- .../switch_as_x/translations/de.json | 8 +- .../switch_as_x/translations/el.json | 8 +- .../switch_as_x/translations/en.json | 8 +- .../switch_as_x/translations/et.json | 8 +- .../switch_as_x/translations/fr.json | 8 +- .../switch_as_x/translations/he.json | 11 --- .../switch_as_x/translations/hu.json | 8 +- .../switch_as_x/translations/id.json | 8 +- .../switch_as_x/translations/it.json | 8 +- .../switch_as_x/translations/ja.json | 8 +- .../switch_as_x/translations/ko.json | 14 ++++ .../switch_as_x/translations/nl.json | 8 +- .../switch_as_x/translations/no.json | 8 +- .../switch_as_x/translations/pl.json | 8 +- .../switch_as_x/translations/pt-BR.json | 8 +- .../switch_as_x/translations/ru.json | 8 +- .../switch_as_x/translations/sv.json | 8 +- .../switch_as_x/translations/tr.json | 8 +- .../switch_as_x/translations/zh-Hans.json | 8 +- .../switch_as_x/translations/zh-Hant.json | 8 +- .../components/switchbot/translations/bg.json | 3 - .../components/switchbot/translations/ca.json | 3 - .../components/switchbot/translations/cs.json | 3 - .../components/switchbot/translations/de.json | 3 - .../components/switchbot/translations/el.json | 3 - .../components/switchbot/translations/en.json | 3 - .../components/switchbot/translations/es.json | 3 - .../components/switchbot/translations/et.json | 3 - .../components/switchbot/translations/fr.json | 3 - .../components/switchbot/translations/he.json | 3 - .../components/switchbot/translations/hu.json | 1 - .../components/switchbot/translations/id.json | 3 - .../components/switchbot/translations/it.json | 1 - .../components/switchbot/translations/ja.json | 3 - .../components/switchbot/translations/nl.json | 3 - .../components/switchbot/translations/no.json | 3 - .../components/switchbot/translations/pl.json | 1 - .../switchbot/translations/pt-BR.json | 3 - .../components/switchbot/translations/ru.json | 3 - .../components/switchbot/translations/tr.json | 1 - .../switchbot/translations/zh-Hant.json | 3 - .../switcher_kis/translations/ko.json | 9 +++ .../synology_dsm/translations/bg.json | 13 +--- .../synology_dsm/translations/ca.json | 14 +--- .../synology_dsm/translations/cs.json | 13 +--- .../synology_dsm/translations/de.json | 14 +--- .../synology_dsm/translations/el.json | 14 +--- .../synology_dsm/translations/en.json | 14 +--- .../synology_dsm/translations/es-419.json | 6 +- .../synology_dsm/translations/es.json | 14 +--- .../synology_dsm/translations/et.json | 14 +--- .../synology_dsm/translations/fi.json | 3 - .../synology_dsm/translations/fr.json | 14 +--- .../synology_dsm/translations/he.json | 6 -- .../synology_dsm/translations/hu.json | 14 +--- .../synology_dsm/translations/id.json | 14 +--- .../synology_dsm/translations/it.json | 14 +--- .../synology_dsm/translations/ja.json | 14 +--- .../synology_dsm/translations/ko.json | 6 +- .../synology_dsm/translations/lb.json | 6 +- .../synology_dsm/translations/nb.json | 5 -- .../synology_dsm/translations/nl.json | 14 +--- .../synology_dsm/translations/no.json | 14 +--- .../synology_dsm/translations/pl.json | 14 +--- .../synology_dsm/translations/pt-BR.json | 14 +--- .../synology_dsm/translations/ru.json | 14 +--- .../synology_dsm/translations/sk.json | 5 -- .../synology_dsm/translations/sl.json | 6 +- .../synology_dsm/translations/sv.json | 3 +- .../synology_dsm/translations/tr.json | 14 +--- .../synology_dsm/translations/uk.json | 6 +- .../synology_dsm/translations/zh-Hans.json | 12 +-- .../synology_dsm/translations/zh-Hant.json | 14 +--- .../components/tasmota/translations/bg.json | 3 - .../components/tasmota/translations/ca.json | 4 +- .../components/tasmota/translations/cs.json | 4 - .../components/tasmota/translations/de.json | 4 +- .../components/tasmota/translations/el.json | 4 +- .../components/tasmota/translations/en.json | 4 +- .../components/tasmota/translations/es.json | 4 +- .../components/tasmota/translations/et.json | 4 +- .../components/tasmota/translations/fr.json | 4 +- .../components/tasmota/translations/he.json | 4 +- .../components/tasmota/translations/hu.json | 4 +- .../components/tasmota/translations/id.json | 4 +- .../components/tasmota/translations/it.json | 4 +- .../components/tasmota/translations/ja.json | 4 +- .../components/tasmota/translations/ko.json | 4 +- .../components/tasmota/translations/lb.json | 4 +- .../components/tasmota/translations/nl.json | 4 +- .../components/tasmota/translations/no.json | 4 +- .../components/tasmota/translations/pl.json | 4 +- .../tasmota/translations/pt-BR.json | 4 +- .../components/tasmota/translations/pt.json | 3 +- .../components/tasmota/translations/ru.json | 4 +- .../components/tasmota/translations/tr.json | 4 +- .../components/tasmota/translations/uk.json | 4 +- .../tasmota/translations/zh-Hant.json | 4 +- .../components/tautulli/translations/ko.json | 12 +++ .../components/threshold/translations/ca.json | 2 - .../components/threshold/translations/de.json | 2 - .../components/threshold/translations/el.json | 2 - .../components/threshold/translations/en.json | 2 - .../components/threshold/translations/et.json | 2 - .../components/threshold/translations/fr.json | 2 - .../components/threshold/translations/hu.json | 2 - .../components/threshold/translations/id.json | 2 - .../components/threshold/translations/it.json | 2 - .../components/threshold/translations/ja.json | 2 - .../components/threshold/translations/ko.json | 38 +++++++++ .../components/threshold/translations/nl.json | 2 - .../components/threshold/translations/no.json | 2 - .../components/threshold/translations/pl.json | 2 - .../threshold/translations/pt-BR.json | 2 - .../components/threshold/translations/ru.json | 2 - .../components/threshold/translations/tr.json | 2 - .../threshold/translations/zh-Hans.json | 2 - .../threshold/translations/zh-Hant.json | 2 - .../components/tibber/translations/ca.json | 3 +- .../components/tibber/translations/cs.json | 3 +- .../components/tibber/translations/de.json | 3 +- .../components/tibber/translations/el.json | 3 +- .../components/tibber/translations/en.json | 3 +- .../components/tibber/translations/es.json | 3 +- .../components/tibber/translations/et.json | 3 +- .../components/tibber/translations/fi.json | 9 --- .../components/tibber/translations/fr.json | 3 +- .../components/tibber/translations/he.json | 3 +- .../components/tibber/translations/hu.json | 3 +- .../components/tibber/translations/id.json | 3 +- .../components/tibber/translations/it.json | 3 +- .../components/tibber/translations/ja.json | 3 +- .../components/tibber/translations/ko.json | 3 +- .../components/tibber/translations/lb.json | 3 +- .../components/tibber/translations/nl.json | 3 +- .../components/tibber/translations/no.json | 3 +- .../components/tibber/translations/pl.json | 3 +- .../components/tibber/translations/pt-BR.json | 3 +- .../components/tibber/translations/pt.json | 3 +- .../components/tibber/translations/ru.json | 3 +- .../components/tibber/translations/sl.json | 3 +- .../components/tibber/translations/tr.json | 3 +- .../components/tibber/translations/uk.json | 3 +- .../tibber/translations/zh-Hant.json | 3 +- .../components/tod/translations/ca.json | 7 +- .../components/tod/translations/de.json | 7 +- .../components/tod/translations/el.json | 7 +- .../components/tod/translations/en.json | 7 +- .../components/tod/translations/es.json | 3 +- .../components/tod/translations/et.json | 7 +- .../components/tod/translations/fr.json | 7 +- .../components/tod/translations/he.json | 7 +- .../components/tod/translations/hu.json | 7 +- .../components/tod/translations/id.json | 7 +- .../components/tod/translations/it.json | 7 +- .../components/tod/translations/ja.json | 7 +- .../components/tod/translations/ko.json | 3 + .../components/tod/translations/nl.json | 7 +- .../components/tod/translations/no.json | 7 +- .../components/tod/translations/pl.json | 7 +- .../components/tod/translations/pt-BR.json | 7 +- .../components/tod/translations/ru.json | 7 +- .../components/tod/translations/tr.json | 7 +- .../components/tod/translations/zh-Hans.json | 7 +- .../components/tod/translations/zh-Hant.json | 7 +- .../components/tolo/translations/bg.json | 3 +- .../components/tolo/translations/ca.json | 3 +- .../components/tolo/translations/de.json | 3 +- .../components/tolo/translations/el.json | 3 +- .../components/tolo/translations/en.json | 3 +- .../components/tolo/translations/es.json | 3 +- .../components/tolo/translations/et.json | 3 +- .../components/tolo/translations/fr.json | 3 +- .../components/tolo/translations/he.json | 3 +- .../components/tolo/translations/hu.json | 3 +- .../components/tolo/translations/id.json | 3 +- .../components/tolo/translations/it.json | 3 +- .../components/tolo/translations/ja.json | 3 +- .../components/tolo/translations/ko.json | 9 +++ .../components/tolo/translations/nl.json | 3 +- .../components/tolo/translations/no.json | 3 +- .../components/tolo/translations/pl.json | 3 +- .../components/tolo/translations/pt-BR.json | 3 +- .../components/tolo/translations/ru.json | 3 +- .../components/tolo/translations/sl.json | 3 +- .../components/tolo/translations/tr.json | 3 +- .../components/tolo/translations/zh-Hant.json | 3 +- .../tomorrowio/translations/bg.json | 2 - .../tomorrowio/translations/ca.json | 2 - .../tomorrowio/translations/de.json | 2 - .../tomorrowio/translations/el.json | 2 - .../tomorrowio/translations/en.json | 2 - .../tomorrowio/translations/es.json | 1 - .../tomorrowio/translations/et.json | 2 - .../tomorrowio/translations/fr.json | 2 - .../tomorrowio/translations/he.json | 2 - .../tomorrowio/translations/hu.json | 2 - .../tomorrowio/translations/id.json | 2 - .../tomorrowio/translations/it.json | 2 - .../tomorrowio/translations/ja.json | 2 - .../tomorrowio/translations/nl.json | 2 - .../tomorrowio/translations/no.json | 2 - .../tomorrowio/translations/pl.json | 2 - .../tomorrowio/translations/pt-BR.json | 2 - .../tomorrowio/translations/ru.json | 2 - .../tomorrowio/translations/tr.json | 2 - .../tomorrowio/translations/zh-Hant.json | 2 - .../totalconnect/translations/ca.json | 4 +- .../totalconnect/translations/cs.json | 8 +- .../totalconnect/translations/de.json | 4 +- .../totalconnect/translations/el.json | 4 +- .../totalconnect/translations/en.json | 4 +- .../totalconnect/translations/es-419.json | 3 +- .../totalconnect/translations/es.json | 4 +- .../totalconnect/translations/et.json | 4 +- .../totalconnect/translations/fr.json | 4 +- .../totalconnect/translations/he.json | 1 - .../totalconnect/translations/hu.json | 4 +- .../totalconnect/translations/id.json | 4 +- .../totalconnect/translations/it.json | 4 +- .../totalconnect/translations/ja.json | 4 +- .../totalconnect/translations/ko.json | 6 +- .../totalconnect/translations/lb.json | 3 +- .../totalconnect/translations/nl.json | 4 +- .../totalconnect/translations/no.json | 4 +- .../totalconnect/translations/pl.json | 4 +- .../totalconnect/translations/pt-BR.json | 4 +- .../totalconnect/translations/pt.json | 5 -- .../totalconnect/translations/ru.json | 4 +- .../totalconnect/translations/sk.json | 7 -- .../totalconnect/translations/sl.json | 3 +- .../totalconnect/translations/tr.json | 4 +- .../totalconnect/translations/uk.json | 3 +- .../totalconnect/translations/zh-Hant.json | 4 +- .../components/tplink/translations/bg.json | 6 +- .../components/tplink/translations/ca.json | 6 +- .../components/tplink/translations/cs.json | 6 +- .../components/tplink/translations/da.json | 8 +- .../components/tplink/translations/de.json | 6 +- .../components/tplink/translations/el.json | 6 +- .../components/tplink/translations/en.json | 6 +- .../tplink/translations/es-419.json | 8 +- .../components/tplink/translations/es.json | 6 +- .../components/tplink/translations/et.json | 6 +- .../components/tplink/translations/fr.json | 6 +- .../components/tplink/translations/he.json | 6 +- .../components/tplink/translations/hu.json | 6 +- .../components/tplink/translations/id.json | 6 +- .../components/tplink/translations/it.json | 6 +- .../components/tplink/translations/ja.json | 6 +- .../components/tplink/translations/ko.json | 6 +- .../components/tplink/translations/lb.json | 8 +- .../components/tplink/translations/nl.json | 6 +- .../components/tplink/translations/no.json | 6 +- .../components/tplink/translations/pl.json | 6 +- .../components/tplink/translations/pt-BR.json | 6 +- .../components/tplink/translations/pt.json | 8 +- .../components/tplink/translations/ru.json | 6 +- .../components/tplink/translations/sl.json | 8 +- .../components/tplink/translations/sv.json | 8 +- .../components/tplink/translations/tr.json | 6 +- .../components/tplink/translations/uk.json | 8 +- .../tplink/translations/zh-Hans.json | 8 +- .../tplink/translations/zh-Hant.json | 6 +- .../trafikverket_ferry/translations/ko.json | 28 +++++++ .../translations/bg.json | 1 - .../translations/ca.json | 2 - .../translations/de.json | 2 - .../translations/el.json | 2 - .../translations/en.json | 2 - .../translations/es.json | 2 - .../translations/et.json | 2 - .../translations/fr.json | 2 - .../translations/he.json | 1 - .../translations/hu.json | 2 - .../translations/id.json | 2 - .../translations/it.json | 2 - .../translations/ja.json | 2 - .../translations/lt.json | 11 --- .../translations/nb.json | 3 +- .../translations/nl.json | 2 - .../translations/no.json | 2 - .../translations/pl.json | 2 - .../translations/pt-BR.json | 2 - .../translations/ru.json | 2 - .../translations/tr.json | 2 - .../translations/zh-Hant.json | 2 - .../components/tuya/translations/af.json | 8 -- .../components/tuya/translations/bg.json | 36 +-------- .../components/tuya/translations/ca.json | 65 +--------------- .../components/tuya/translations/cs.json | 51 +----------- .../components/tuya/translations/de.json | 65 +--------------- .../components/tuya/translations/el.json | 65 +--------------- .../components/tuya/translations/en.json | 65 +--------------- .../components/tuya/translations/en_GB.json | 14 ---- .../components/tuya/translations/es.json | 65 +--------------- .../components/tuya/translations/et.json | 65 +--------------- .../components/tuya/translations/fi.json | 5 +- .../components/tuya/translations/fr.json | 65 +--------------- .../components/tuya/translations/he.json | 52 +------------ .../components/tuya/translations/hu.json | 65 +--------------- .../components/tuya/translations/id.json | 65 +--------------- .../components/tuya/translations/is.json | 15 +--- .../components/tuya/translations/it.json | 65 +--------------- .../components/tuya/translations/ja.json | 65 +--------------- .../components/tuya/translations/ka.json | 37 --------- .../components/tuya/translations/ko.json | 50 +----------- .../components/tuya/translations/lb.json | 44 +---------- .../components/tuya/translations/nl.json | 65 +--------------- .../components/tuya/translations/no.json | 65 +--------------- .../components/tuya/translations/pl.json | 65 +--------------- .../components/tuya/translations/pt-BR.json | 65 +--------------- .../components/tuya/translations/pt.json | 10 --- .../components/tuya/translations/ru.json | 65 +--------------- .../components/tuya/translations/sk.json | 11 +-- .../components/tuya/translations/sl.json | 20 ----- .../components/tuya/translations/sv.json | 5 +- .../components/tuya/translations/tr.json | 65 +--------------- .../components/tuya/translations/uk.json | 48 +----------- .../components/tuya/translations/zh-Hans.json | 64 +-------------- .../components/tuya/translations/zh-Hant.json | 65 +--------------- .../twentemilieu/translations/nl.json | 2 +- .../ukraine_alarm/translations/bg.json | 11 --- .../ukraine_alarm/translations/ca.json | 13 ---- .../ukraine_alarm/translations/de.json | 13 ---- .../ukraine_alarm/translations/el.json | 13 ---- .../ukraine_alarm/translations/en.json | 13 ---- .../ukraine_alarm/translations/es.json | 10 --- .../ukraine_alarm/translations/et.json | 13 ---- .../ukraine_alarm/translations/fr.json | 13 ---- .../ukraine_alarm/translations/he.json | 27 +++++++ .../ukraine_alarm/translations/hu.json | 17 +--- .../ukraine_alarm/translations/id.json | 13 ---- .../ukraine_alarm/translations/it.json | 17 +--- .../ukraine_alarm/translations/ja.json | 17 +--- .../ukraine_alarm/translations/ko.json | 12 --- .../ukraine_alarm/translations/nl.json | 17 +--- .../ukraine_alarm/translations/no.json | 13 ---- .../ukraine_alarm/translations/pl.json | 13 ---- .../ukraine_alarm/translations/pt-BR.json | 13 ---- .../ukraine_alarm/translations/ru.json | 13 ---- .../ukraine_alarm/translations/tr.json | 13 ---- .../ukraine_alarm/translations/uk.json | 13 ---- .../ukraine_alarm/translations/zh-Hant.json | 13 ---- .../components/unifi/translations/ko.json | 3 + .../unifiprotect/translations/bg.json | 3 +- .../unifiprotect/translations/ca.json | 6 +- .../unifiprotect/translations/cs.json | 6 +- .../unifiprotect/translations/de.json | 6 +- .../unifiprotect/translations/el.json | 6 +- .../unifiprotect/translations/en.json | 6 +- .../unifiprotect/translations/es.json | 6 +- .../unifiprotect/translations/et.json | 6 +- .../unifiprotect/translations/fr.json | 6 +- .../unifiprotect/translations/he.json | 6 +- .../unifiprotect/translations/hu.json | 6 +- .../unifiprotect/translations/id.json | 6 +- .../unifiprotect/translations/it.json | 6 +- .../unifiprotect/translations/ja.json | 6 +- .../unifiprotect/translations/ko.json | 9 +++ .../unifiprotect/translations/nl.json | 6 +- .../unifiprotect/translations/no.json | 6 +- .../unifiprotect/translations/pl.json | 6 +- .../unifiprotect/translations/pt-BR.json | 6 +- .../unifiprotect/translations/ru.json | 6 +- .../unifiprotect/translations/tr.json | 6 +- .../unifiprotect/translations/zh-Hant.json | 6 +- .../components/upnp/translations/bg.json | 3 +- .../components/upnp/translations/ca.json | 4 +- .../components/upnp/translations/cs.json | 6 -- .../components/upnp/translations/de.json | 4 +- .../components/upnp/translations/el.json | 4 +- .../components/upnp/translations/en.json | 4 +- .../components/upnp/translations/es.json | 4 +- .../components/upnp/translations/et.json | 4 +- .../components/upnp/translations/fi.json | 8 -- .../components/upnp/translations/fr.json | 4 +- .../components/upnp/translations/he.json | 3 +- .../components/upnp/translations/hu.json | 4 +- .../components/upnp/translations/id.json | 4 +- .../components/upnp/translations/it.json | 4 +- .../components/upnp/translations/ja.json | 4 +- .../components/upnp/translations/ko.json | 6 -- .../components/upnp/translations/lb.json | 6 -- .../components/upnp/translations/nl.json | 4 +- .../components/upnp/translations/no.json | 4 +- .../components/upnp/translations/pl.json | 4 +- .../components/upnp/translations/pt-BR.json | 4 +- .../components/upnp/translations/pt.json | 5 -- .../components/upnp/translations/ru.json | 4 +- .../components/upnp/translations/sl.json | 5 -- .../components/upnp/translations/sv.json | 7 -- .../components/upnp/translations/tr.json | 4 +- .../components/upnp/translations/uk.json | 6 -- .../components/upnp/translations/zh-Hans.json | 3 +- .../components/upnp/translations/zh-Hant.json | 4 +- .../components/uptime/translations/ko.json | 9 +++ .../utility_meter/translations/ko.json | 35 +++++++++ .../components/vicare/translations/af.json | 1 - .../components/vicare/translations/bg.json | 5 +- .../components/vicare/translations/ca.json | 5 +- .../components/vicare/translations/cs.json | 1 - .../components/vicare/translations/de.json | 5 +- .../components/vicare/translations/el.json | 5 +- .../components/vicare/translations/en.json | 5 +- .../components/vicare/translations/es.json | 5 +- .../components/vicare/translations/et.json | 5 +- .../components/vicare/translations/fr.json | 5 +- .../components/vicare/translations/he.json | 4 +- .../components/vicare/translations/hu.json | 5 +- .../components/vicare/translations/id.json | 5 +- .../components/vicare/translations/it.json | 5 +- .../components/vicare/translations/ja.json | 5 +- .../components/vicare/translations/nl.json | 5 +- .../components/vicare/translations/no.json | 5 +- .../components/vicare/translations/pl.json | 5 +- .../components/vicare/translations/pt-BR.json | 5 +- .../components/vicare/translations/ru.json | 5 +- .../components/vicare/translations/sk.json | 1 - .../components/vicare/translations/tr.json | 5 +- .../vicare/translations/zh-Hant.json | 5 +- .../components/vilfo/translations/ca.json | 4 +- .../components/vilfo/translations/cs.json | 4 +- .../components/vilfo/translations/da.json | 4 +- .../components/vilfo/translations/de.json | 4 +- .../components/vilfo/translations/el.json | 4 +- .../components/vilfo/translations/en.json | 4 +- .../components/vilfo/translations/es-419.json | 4 +- .../components/vilfo/translations/es.json | 4 +- .../components/vilfo/translations/et.json | 4 +- .../components/vilfo/translations/fr.json | 4 +- .../components/vilfo/translations/he.json | 3 +- .../components/vilfo/translations/hu.json | 4 +- .../components/vilfo/translations/id.json | 4 +- .../components/vilfo/translations/it.json | 4 +- .../components/vilfo/translations/ja.json | 4 +- .../components/vilfo/translations/ko.json | 4 +- .../components/vilfo/translations/lb.json | 4 +- .../components/vilfo/translations/nl.json | 4 +- .../components/vilfo/translations/no.json | 4 +- .../components/vilfo/translations/pl.json | 4 +- .../components/vilfo/translations/pt-BR.json | 4 +- .../components/vilfo/translations/ru.json | 4 +- .../components/vilfo/translations/sl.json | 4 +- .../components/vilfo/translations/sv.json | 4 +- .../components/vilfo/translations/tr.json | 4 +- .../components/vilfo/translations/uk.json | 4 +- .../vilfo/translations/zh-Hans.json | 4 +- .../vilfo/translations/zh-Hant.json | 4 +- .../components/vulcan/translations/bg.json | 13 ---- .../components/vulcan/translations/ca.json | 23 ------ .../components/vulcan/translations/de.json | 23 ------ .../components/vulcan/translations/el.json | 23 ------ .../components/vulcan/translations/en.json | 23 ------ .../components/vulcan/translations/es.json | 21 ----- .../components/vulcan/translations/et.json | 23 ------ .../components/vulcan/translations/fr.json | 23 ------ .../components/vulcan/translations/hu.json | 23 ------ .../components/vulcan/translations/id.json | 23 ------ .../components/vulcan/translations/it.json | 23 ------ .../components/vulcan/translations/ja.json | 23 ------ .../components/vulcan/translations/nl.json | 23 ------ .../components/vulcan/translations/no.json | 23 ------ .../components/vulcan/translations/pl.json | 23 ------ .../components/vulcan/translations/pt-BR.json | 23 ------ .../components/vulcan/translations/ru.json | 23 ------ .../components/vulcan/translations/tr.json | 23 ------ .../vulcan/translations/zh-Hant.json | 23 ------ .../components/webostv/translations/ko.json | 47 +++++++++++ .../components/wilight/translations/ar.json | 3 +- .../components/wilight/translations/ca.json | 3 +- .../components/wilight/translations/cs.json | 3 +- .../components/wilight/translations/de.json | 3 +- .../components/wilight/translations/el.json | 3 +- .../components/wilight/translations/en.json | 3 +- .../components/wilight/translations/es.json | 3 +- .../components/wilight/translations/et.json | 3 +- .../components/wilight/translations/fr.json | 3 +- .../components/wilight/translations/he.json | 7 +- .../components/wilight/translations/hu.json | 3 +- .../components/wilight/translations/id.json | 3 +- .../components/wilight/translations/it.json | 3 +- .../components/wilight/translations/ja.json | 3 +- .../components/wilight/translations/ko.json | 3 +- .../components/wilight/translations/lb.json | 3 +- .../components/wilight/translations/nl.json | 3 +- .../components/wilight/translations/no.json | 3 +- .../components/wilight/translations/pl.json | 3 +- .../wilight/translations/pt-BR.json | 3 +- .../components/wilight/translations/pt.json | 3 +- .../components/wilight/translations/ru.json | 3 +- .../components/wilight/translations/tr.json | 3 +- .../components/wilight/translations/uk.json | 3 +- .../wilight/translations/zh-Hant.json | 3 +- .../components/wiz/translations/bg.json | 3 +- .../components/wiz/translations/ca.json | 6 +- .../components/wiz/translations/cs.json | 6 +- .../components/wiz/translations/de.json | 6 +- .../components/wiz/translations/el.json | 6 +- .../components/wiz/translations/en.json | 6 +- .../components/wiz/translations/es.json | 6 +- .../components/wiz/translations/et.json | 6 +- .../components/wiz/translations/fr.json | 6 +- .../components/wiz/translations/he.json | 6 +- .../components/wiz/translations/hu.json | 6 +- .../components/wiz/translations/id.json | 6 +- .../components/wiz/translations/it.json | 6 +- .../components/wiz/translations/ja.json | 6 +- .../components/wiz/translations/nl.json | 6 +- .../components/wiz/translations/no.json | 6 +- .../components/wiz/translations/pl.json | 6 +- .../components/wiz/translations/pt-BR.json | 6 +- .../components/wiz/translations/ru.json | 6 +- .../components/wiz/translations/sk.json | 7 -- .../components/wiz/translations/tr.json | 6 +- .../components/wiz/translations/zh-Hant.json | 6 +- .../components/ws66i/translations/he.json | 18 +++++ .../xiaomi_aqara/translations/ca.json | 6 +- .../xiaomi_aqara/translations/cs.json | 6 +- .../xiaomi_aqara/translations/de.json | 6 +- .../xiaomi_aqara/translations/el.json | 6 +- .../xiaomi_aqara/translations/en.json | 6 +- .../xiaomi_aqara/translations/es.json | 6 +- .../xiaomi_aqara/translations/et.json | 6 +- .../xiaomi_aqara/translations/fr.json | 6 +- .../xiaomi_aqara/translations/he.json | 6 +- .../xiaomi_aqara/translations/hu.json | 6 +- .../xiaomi_aqara/translations/id.json | 6 +- .../xiaomi_aqara/translations/it.json | 6 +- .../xiaomi_aqara/translations/ja.json | 6 +- .../xiaomi_aqara/translations/ko.json | 6 +- .../xiaomi_aqara/translations/lb.json | 6 +- .../xiaomi_aqara/translations/nl.json | 6 +- .../xiaomi_aqara/translations/no.json | 6 +- .../xiaomi_aqara/translations/pl.json | 6 +- .../xiaomi_aqara/translations/pt-BR.json | 6 +- .../xiaomi_aqara/translations/ru.json | 6 +- .../xiaomi_aqara/translations/tr.json | 6 +- .../xiaomi_aqara/translations/uk.json | 6 +- .../xiaomi_aqara/translations/zh-Hans.json | 6 +- .../xiaomi_aqara/translations/zh-Hant.json | 6 +- .../xiaomi_miio/translations/bg.json | 14 +--- .../xiaomi_miio/translations/ca.json | 44 ++--------- .../xiaomi_miio/translations/cs.json | 44 ++--------- .../xiaomi_miio/translations/de.json | 44 ++--------- .../xiaomi_miio/translations/el.json | 44 ++--------- .../xiaomi_miio/translations/en.json | 44 ++--------- .../xiaomi_miio/translations/es-419.json | 25 ------ .../xiaomi_miio/translations/es.json | 44 ++--------- .../xiaomi_miio/translations/et.json | 44 ++--------- .../xiaomi_miio/translations/fi.json | 24 ------ .../xiaomi_miio/translations/fr.json | 44 ++--------- .../xiaomi_miio/translations/he.json | 44 ++--------- .../xiaomi_miio/translations/hu.json | 44 ++--------- .../xiaomi_miio/translations/id.json | 44 ++--------- .../xiaomi_miio/translations/it.json | 44 ++--------- .../xiaomi_miio/translations/ja.json | 44 ++--------- .../xiaomi_miio/translations/ko.json | 31 +------- .../xiaomi_miio/translations/lb.json | 23 +----- .../xiaomi_miio/translations/nl.json | 44 ++--------- .../xiaomi_miio/translations/no.json | 44 ++--------- .../xiaomi_miio/translations/pl.json | 44 ++--------- .../xiaomi_miio/translations/pt-BR.json | 44 ++--------- .../xiaomi_miio/translations/pt.json | 14 ---- .../xiaomi_miio/translations/ru.json | 44 ++--------- .../xiaomi_miio/translations/sk.json | 10 --- .../xiaomi_miio/translations/sl.json | 19 ----- .../xiaomi_miio/translations/sv.json | 24 ------ .../xiaomi_miio/translations/tr.json | 44 ++--------- .../xiaomi_miio/translations/uk.json | 23 +----- .../xiaomi_miio/translations/zh-Hans.json | 41 +--------- .../xiaomi_miio/translations/zh-Hant.json | 44 ++--------- .../yale_smart_alarm/translations/nl.json | 4 +- .../yamaha_musiccast/translations/ko.json | 9 +++ .../components/yeelight/translations/ar.json | 3 +- .../components/yeelight/translations/bg.json | 3 +- .../components/yeelight/translations/ca.json | 3 +- .../components/yeelight/translations/cs.json | 3 +- .../components/yeelight/translations/de.json | 3 +- .../components/yeelight/translations/el.json | 3 +- .../components/yeelight/translations/en.json | 3 +- .../components/yeelight/translations/es.json | 3 +- .../components/yeelight/translations/et.json | 3 +- .../components/yeelight/translations/fr.json | 3 +- .../components/yeelight/translations/he.json | 3 +- .../components/yeelight/translations/hu.json | 3 +- .../components/yeelight/translations/id.json | 3 +- .../components/yeelight/translations/it.json | 3 +- .../components/yeelight/translations/ja.json | 3 +- .../components/yeelight/translations/ko.json | 3 +- .../components/yeelight/translations/lb.json | 3 +- .../components/yeelight/translations/nl.json | 3 +- .../components/yeelight/translations/no.json | 3 +- .../components/yeelight/translations/pl.json | 3 +- .../yeelight/translations/pt-BR.json | 3 +- .../components/yeelight/translations/pt.json | 3 +- .../components/yeelight/translations/ru.json | 3 +- .../components/yeelight/translations/tr.json | 3 +- .../components/yeelight/translations/uk.json | 3 +- .../yeelight/translations/zh-Hans.json | 3 +- .../yeelight/translations/zh-Hant.json | 3 +- .../components/yolink/translations/ca.json | 25 ++++++ .../components/yolink/translations/de.json | 25 ++++++ .../components/yolink/translations/he.json | 24 ++++++ .../components/yolink/translations/id.json | 1 + .../components/yolink/translations/ko.json | 2 + .../components/yolink/translations/no.json | 25 ++++++ .../components/zwave_js/translations/bg.json | 1 - .../components/zwave_js/translations/ca.json | 5 +- .../components/zwave_js/translations/cs.json | 3 +- .../components/zwave_js/translations/de.json | 5 +- .../components/zwave_js/translations/el.json | 5 +- .../components/zwave_js/translations/en.json | 5 +- .../components/zwave_js/translations/es.json | 5 +- .../components/zwave_js/translations/et.json | 5 +- .../components/zwave_js/translations/fr.json | 5 +- .../components/zwave_js/translations/he.json | 4 +- .../components/zwave_js/translations/hu.json | 5 +- .../components/zwave_js/translations/id.json | 5 +- .../components/zwave_js/translations/it.json | 5 +- .../components/zwave_js/translations/ja.json | 5 +- .../components/zwave_js/translations/ko.json | 7 +- .../components/zwave_js/translations/lb.json | 3 +- .../components/zwave_js/translations/nl.json | 5 +- .../components/zwave_js/translations/no.json | 5 +- .../components/zwave_js/translations/pl.json | 5 +- .../zwave_js/translations/pt-BR.json | 5 +- .../components/zwave_js/translations/ru.json | 5 +- .../components/zwave_js/translations/tr.json | 5 +- .../components/zwave_js/translations/uk.json | 3 +- .../zwave_js/translations/zh-Hant.json | 5 +- 2813 files changed, 3881 insertions(+), 16900 deletions(-) delete mode 100644 homeassistant/components/airly/translations/nn.json create mode 100644 homeassistant/components/aladdin_connect/translations/cs.json create mode 100644 homeassistant/components/aladdin_connect/translations/he.json create mode 100644 homeassistant/components/application_credentials/translations/ko.json delete mode 100644 homeassistant/components/aurora_abb_powerone/translations/cs.json create mode 100644 homeassistant/components/azure_event_hub/translations/ko.json create mode 100644 homeassistant/components/baf/translations/cs.json create mode 100644 homeassistant/components/baf/translations/he.json delete mode 100644 homeassistant/components/climacell/translations/bg.json delete mode 100644 homeassistant/components/climacell/translations/cs.json delete mode 100644 homeassistant/components/climacell/translations/lb.json delete mode 100644 homeassistant/components/climacell/translations/pt.json delete mode 100644 homeassistant/components/climacell/translations/sk.json delete mode 100644 homeassistant/components/climacell/translations/sv.json delete mode 100644 homeassistant/components/climacell/translations/zh-Hans.json create mode 100644 homeassistant/components/cpuspeed/translations/ko.json delete mode 100644 homeassistant/components/derivative/translations/he.json create mode 100644 homeassistant/components/derivative/translations/ko.json create mode 100644 homeassistant/components/devolo_home_network/translations/ko.json create mode 100644 homeassistant/components/dlna_dmr/translations/ko.json create mode 100644 homeassistant/components/dlna_dms/translations/ko.json create mode 100644 homeassistant/components/generic/translations/ko.json create mode 100644 homeassistant/components/geocaching/translations/he.json create mode 100644 homeassistant/components/google/translations/ko.json create mode 100644 homeassistant/components/integration/translations/ko.json create mode 100644 homeassistant/components/intellifire/translations/ko.json delete mode 100644 homeassistant/components/iqvia/translations/nn.json delete mode 100644 homeassistant/components/iss/translations/zh-Hans.json create mode 100644 homeassistant/components/kraken/translations/ko.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.cs.json create mode 100644 homeassistant/components/litterrobot/translations/sensor.he.json delete mode 100644 homeassistant/components/mill/translations/fi.json delete mode 100644 homeassistant/components/mill/translations/sv.json create mode 100644 homeassistant/components/min_max/translations/ko.json create mode 100644 homeassistant/components/moon/translations/ko.json delete mode 100644 homeassistant/components/motion_blinds/translations/sl.json delete mode 100644 homeassistant/components/netgear/translations/sk.json delete mode 100644 homeassistant/components/onewire/translations/af.json delete mode 100644 homeassistant/components/poolsense/translations/sv.json create mode 100644 homeassistant/components/sabnzbd/translations/ko.json delete mode 100644 homeassistant/components/sentry/translations/af.json create mode 100644 homeassistant/components/senz/translations/ko.json create mode 100644 homeassistant/components/siren/translations/cs.json create mode 100644 homeassistant/components/siren/translations/no.json create mode 100644 homeassistant/components/slack/translations/cs.json rename homeassistant/components/{climacell => slack}/translations/he.json (52%) create mode 100644 homeassistant/components/sql/translations/ko.json create mode 100644 homeassistant/components/steam_online/translations/ko.json delete mode 100644 homeassistant/components/switch_as_x/translations/he.json create mode 100644 homeassistant/components/switch_as_x/translations/ko.json create mode 100644 homeassistant/components/switcher_kis/translations/ko.json create mode 100644 homeassistant/components/tautulli/translations/ko.json create mode 100644 homeassistant/components/threshold/translations/ko.json delete mode 100644 homeassistant/components/tibber/translations/fi.json create mode 100644 homeassistant/components/tod/translations/ko.json create mode 100644 homeassistant/components/tolo/translations/ko.json create mode 100644 homeassistant/components/trafikverket_ferry/translations/ko.json delete mode 100644 homeassistant/components/trafikverket_weatherstation/translations/lt.json delete mode 100644 homeassistant/components/tuya/translations/af.json delete mode 100644 homeassistant/components/tuya/translations/en_GB.json delete mode 100644 homeassistant/components/tuya/translations/ka.json delete mode 100644 homeassistant/components/tuya/translations/sl.json create mode 100644 homeassistant/components/ukraine_alarm/translations/he.json create mode 100644 homeassistant/components/unifiprotect/translations/ko.json create mode 100644 homeassistant/components/uptime/translations/ko.json create mode 100644 homeassistant/components/utility_meter/translations/ko.json create mode 100644 homeassistant/components/webostv/translations/ko.json create mode 100644 homeassistant/components/ws66i/translations/he.json delete mode 100644 homeassistant/components/xiaomi_miio/translations/es-419.json delete mode 100644 homeassistant/components/xiaomi_miio/translations/fi.json delete mode 100644 homeassistant/components/xiaomi_miio/translations/sv.json create mode 100644 homeassistant/components/yamaha_musiccast/translations/ko.json create mode 100644 homeassistant/components/yolink/translations/ca.json create mode 100644 homeassistant/components/yolink/translations/de.json create mode 100644 homeassistant/components/yolink/translations/he.json create mode 100644 homeassistant/components/yolink/translations/no.json diff --git a/homeassistant/components/accuweather/translations/ar.json b/homeassistant/components/accuweather/translations/ar.json index 0694e096019..5aadd35df92 100644 --- a/homeassistant/components/accuweather/translations/ar.json +++ b/homeassistant/components/accuweather/translations/ar.json @@ -2,12 +2,6 @@ "config": { "error": { "requests_exceeded": "\u062a\u0645 \u062a\u062c\u0627\u0648\u0632 \u0627\u0644\u0639\u062f\u062f \u0627\u0644\u0645\u0633\u0645\u0648\u062d \u0628\u0647 \u0645\u0646 \u0627\u0644\u0637\u0644\u0628\u0627\u062a \u0625\u0644\u0649 Accuweather API. \u0639\u0644\u064a\u0643 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631 \u0623\u0648 \u062a\u063a\u064a\u064a\u0631 \u0645\u0641\u062a\u0627\u062d API." - }, - "step": { - "user": { - "description": "\u0625\u0630\u0627 \u0643\u0646\u062a \u0628\u062d\u0627\u062c\u0629 \u0625\u0644\u0649 \u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0627\u0644\u062a\u0643\u0648\u064a\u0646 \u060c \u0641\u0642\u0645 \u0628\u0625\u0644\u0642\u0627\u0621 \u0646\u0638\u0631\u0629 \u0647\u0646\u0627: https://www.home-assistant.io/integrations/accuweather/ \n\n \u0644\u0627 \u064a\u062a\u0645 \u062a\u0645\u0643\u064a\u0646 \u0628\u0639\u0636 \u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u0627\u0633\u062a\u0634\u0639\u0627\u0631 \u0628\u0634\u0643\u0644 \u0627\u0641\u062a\u0631\u0627\u0636\u064a. \u064a\u0645\u0643\u0646\u0643 \u062a\u0645\u0643\u064a\u0646\u0647\u0645 \u0641\u064a \u0633\u062c\u0644 \u0627\u0644\u0643\u064a\u0627\u0646 \u0628\u0639\u062f \u062a\u0643\u0648\u064a\u0646 \u0627\u0644\u062a\u0643\u0627\u0645\u0644.\n \u0644\u0627 \u064a\u062a\u0645 \u062a\u0645\u0643\u064a\u0646 \u062a\u0648\u0642\u0639\u0627\u062a \u0627\u0644\u0637\u0642\u0633 \u0627\u0641\u062a\u0631\u0627\u0636\u064a\u064b\u0627. \u064a\u0645\u0643\u0646\u0643 \u062a\u0645\u0643\u064a\u0646\u0647 \u0641\u064a \u062e\u064a\u0627\u0631\u0627\u062a \u0627\u0644\u062a\u0643\u0627\u0645\u0644.", - "title": "AccuWeather" - } } }, "options": { @@ -16,8 +10,7 @@ "data": { "forecast": "\u0627\u0644\u0646\u0634\u0631\u0629 \u0627\u0644\u062c\u0648\u064a\u0629" }, - "description": "\u0646\u0638\u0631\u064b\u0627 \u0644\u0642\u064a\u0648\u062f \u0627\u0644\u0625\u0635\u062f\u0627\u0631 \u0627\u0644\u0645\u062c\u0627\u0646\u064a \u0645\u0646 \u0645\u0641\u062a\u0627\u062d AccuWeather API \u060c \u0639\u0646\u062f \u062a\u0645\u0643\u064a\u0646 \u0627\u0644\u062a\u0646\u0628\u0624 \u0628\u0627\u0644\u0637\u0642\u0633 \u060c \u0633\u064a\u062a\u0645 \u0625\u062c\u0631\u0627\u0621 \u062a\u062d\u062f\u064a\u062b\u0627\u062a \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0643\u0644 80 \u062f\u0642\u064a\u0642\u0629 \u0628\u062f\u0644\u0627\u064b \u0645\u0646 \u0643\u0644 40 \u062f\u0642\u064a\u0642\u0629.", - "title": "\u062e\u064a\u0627\u0631\u0627\u062a AccuWeather" + "description": "\u0646\u0638\u0631\u064b\u0627 \u0644\u0642\u064a\u0648\u062f \u0627\u0644\u0625\u0635\u062f\u0627\u0631 \u0627\u0644\u0645\u062c\u0627\u0646\u064a \u0645\u0646 \u0645\u0641\u062a\u0627\u062d AccuWeather API \u060c \u0639\u0646\u062f \u062a\u0645\u0643\u064a\u0646 \u0627\u0644\u062a\u0646\u0628\u0624 \u0628\u0627\u0644\u0637\u0642\u0633 \u060c \u0633\u064a\u062a\u0645 \u0625\u062c\u0631\u0627\u0621 \u062a\u062d\u062f\u064a\u062b\u0627\u062a \u0627\u0644\u0628\u064a\u0627\u0646\u0627\u062a \u0643\u0644 80 \u062f\u0642\u064a\u0642\u0629 \u0628\u062f\u0644\u0627\u064b \u0645\u0646 \u0643\u0644 40 \u062f\u0642\u064a\u0642\u0629." } } }, diff --git a/homeassistant/components/accuweather/translations/bg.json b/homeassistant/components/accuweather/translations/bg.json index b037c01144f..26fdf8e85d5 100644 --- a/homeassistant/components/accuweather/translations/bg.json +++ b/homeassistant/components/accuweather/translations/bg.json @@ -14,8 +14,7 @@ "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", "name": "\u0418\u043c\u0435" - }, - "title": "AccuWeather" + } } } } diff --git a/homeassistant/components/accuweather/translations/ca.json b/homeassistant/components/accuweather/translations/ca.json index feda485d8fd..e80a006141a 100644 --- a/homeassistant/components/accuweather/translations/ca.json +++ b/homeassistant/components/accuweather/translations/ca.json @@ -18,9 +18,7 @@ "latitude": "Latitud", "longitude": "Longitud", "name": "Nom" - }, - "description": "Si necessites ajuda amb la configuraci\u00f3, consulta els seg\u00fcent enlla\u00e7: https://www.home-assistant.io/integrations/accuweather/ \n\n Alguns sensors no estan activats de manera predeterminada. Els pots activar des del registre d'entitats, despr\u00e9s de la configurraci\u00f3 de la integraci\u00f3.\n La previsi\u00f3 meteorol\u00f2gica no est\u00e0 activada de manera predeterminada. Pots activar-la en les opcions de la integraci\u00f3.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Previsi\u00f3 meteorol\u00f2gica" }, - "description": "Per culpa de les limitacions de la versi\u00f3 gratu\u00efta l'API d'AccuWeather, quan habilitis la previsi\u00f3 meteorol\u00f2gica, les actualitzacions de dades es faran cada 80 minuts en comptes de cada 40.", - "title": "Opcions d'AccuWeather" + "description": "Per culpa de les limitacions de la versi\u00f3 gratu\u00efta l'API d'AccuWeather, quan habilitis la previsi\u00f3 meteorol\u00f2gica, les actualitzacions de dades es faran cada 80 minuts en comptes de cada 40." } } }, diff --git a/homeassistant/components/accuweather/translations/cs.json b/homeassistant/components/accuweather/translations/cs.json index 1cf34a42695..e3ae982cddc 100644 --- a/homeassistant/components/accuweather/translations/cs.json +++ b/homeassistant/components/accuweather/translations/cs.json @@ -15,9 +15,7 @@ "latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka", "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka", "name": "Jm\u00e9no" - }, - "description": "Pokud pot\u0159ebujete pomoc s nastaven\u00ed, pod\u00edvejte se na: https://www.home-assistant.io/integrations/accuweather/\n\nN\u011bkter\u00e9 senzory nejsou ve v\u00fdchoz\u00edm nastaven\u00ed povoleny. M\u016f\u017eete je povolit po nastaven\u00ed integrace v registru entit.\nP\u0159edpov\u011b\u010f po\u010das\u00ed nen\u00ed ve v\u00fdchoz\u00edm nastaven\u00ed povolena. M\u016f\u017eete ji povolit v mo\u017enostech integrace.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "P\u0159edpov\u011b\u010f po\u010das\u00ed" }, - "description": "Kdy\u017e povol\u00edte p\u0159edpov\u011b\u010f po\u010das\u00ed, budou aktualizace dat prov\u00e1d\u011bny ka\u017ed\u00fdch 80 minut nam\u00edsto 40 minut z d\u016fvodu omezen\u00ed bezplatn\u00e9 verze AccuWeather.", - "title": "Mo\u017enosti AccuWeather" + "description": "Kdy\u017e povol\u00edte p\u0159edpov\u011b\u010f po\u010das\u00ed, budou aktualizace dat prov\u00e1d\u011bny ka\u017ed\u00fdch 80 minut nam\u00edsto 40 minut z d\u016fvodu omezen\u00ed bezplatn\u00e9 verze AccuWeather." } } }, diff --git a/homeassistant/components/accuweather/translations/de.json b/homeassistant/components/accuweather/translations/de.json index ac0c430de04..f7b02feb091 100644 --- a/homeassistant/components/accuweather/translations/de.json +++ b/homeassistant/components/accuweather/translations/de.json @@ -18,9 +18,7 @@ "latitude": "Breitengrad", "longitude": "L\u00e4ngengrad", "name": "Name" - }, - "description": "Wenn du Hilfe bei der Konfiguration ben\u00f6tigst, schaue hier nach: https://www.home-assistant.io/integrations/accuweather/\n\nEinige Sensoren sind standardm\u00e4\u00dfig nicht aktiviert. Du kannst sie in der Entit\u00e4tsregister nach der Integrationskonfiguration aktivieren.\nDie Wettervorhersage ist nicht standardm\u00e4\u00dfig aktiviert. Du kannst sie in den Integrationsoptionen aktivieren.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Wettervorhersage" }, - "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt.", - "title": "AccuWeather Optionen" + "description": "Aufgrund der Einschr\u00e4nkungen der kostenlosen Version des AccuWeather-API-Schl\u00fcssels werden bei aktivierter Wettervorhersage Datenaktualisierungen alle 80 Minuten statt alle 40 Minuten durchgef\u00fchrt." } } }, diff --git a/homeassistant/components/accuweather/translations/el.json b/homeassistant/components/accuweather/translations/el.json index 43a65158455..b8d7d22df70 100644 --- a/homeassistant/components/accuweather/translations/el.json +++ b/homeassistant/components/accuweather/translations/el.json @@ -18,9 +18,7 @@ "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2", "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" - }, - "description": "\u0391\u03bd \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c3\u03c4\u03b5 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7, \u03c1\u03af\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03bc\u03b1\u03c4\u03b9\u03ac \u03b5\u03b4\u03ce: https://www.home-assistant.io/integrations/accuweather/\n\n\u039f\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03b9 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03b9 \u03b1\u03c0\u03cc \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae. \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03bf\u03c5\u03c2 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c3\u03c4\u03bf \u03bc\u03b7\u03c4\u03c1\u03ce\u03bf \u03bf\u03bd\u03c4\u03bf\u03c4\u03ae\u03c4\u03c9\u03bd \u03bc\u03b5\u03c4\u03ac \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2.\n\u0397 \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b1\u03c0\u03cc \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae. \u039c\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "\u03a0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd" }, - "description": "\u039b\u03cc\u03b3\u03c9 \u03c4\u03c9\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd \u03c4\u03b7\u03c2 \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API \u03c4\u03bf\u03c5 AccuWeather, \u03cc\u03c4\u03b1\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd, \u03bf\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b8\u03b1 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 80 \u03bb\u03b5\u03c0\u03c4\u03ac \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 40 \u03bb\u03b5\u03c0\u03c4\u03ac.", - "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 AccuWeather" + "description": "\u039b\u03cc\u03b3\u03c9 \u03c4\u03c9\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd \u03c4\u03b7\u03c2 \u03b4\u03c9\u03c1\u03b5\u03ac\u03bd \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API \u03c4\u03bf\u03c5 AccuWeather, \u03cc\u03c4\u03b1\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03cc\u03b3\u03bd\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9\u03c1\u03bf\u03cd, \u03bf\u03b9 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b8\u03b1 \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03b9\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03ba\u03ac\u03b8\u03b5 80 \u03bb\u03b5\u03c0\u03c4\u03ac \u03b1\u03bd\u03c4\u03af \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 40 \u03bb\u03b5\u03c0\u03c4\u03ac." } } }, diff --git a/homeassistant/components/accuweather/translations/en.json b/homeassistant/components/accuweather/translations/en.json index a984080fd86..d391ce83e1e 100644 --- a/homeassistant/components/accuweather/translations/en.json +++ b/homeassistant/components/accuweather/translations/en.json @@ -18,9 +18,7 @@ "latitude": "Latitude", "longitude": "Longitude", "name": "Name" - }, - "description": "If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/accuweather/\n\nSome sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Weather forecast" }, - "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes.", - "title": "AccuWeather Options" + "description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes." } } }, diff --git a/homeassistant/components/accuweather/translations/es-419.json b/homeassistant/components/accuweather/translations/es-419.json index 72d295da073..2b2b8dc98ce 100644 --- a/homeassistant/components/accuweather/translations/es-419.json +++ b/homeassistant/components/accuweather/translations/es-419.json @@ -7,12 +7,6 @@ "cannot_connect": "No se pudo conectar", "invalid_api_key": "Clave de API no v\u00e1lida", "requests_exceeded": "Se super\u00f3 el n\u00famero permitido de solicitudes a la API de Accuweather. Tiene que esperar o cambiar la clave de API." - }, - "step": { - "user": { - "description": "Si necesita ayuda con la configuraci\u00f3n, eche un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nAlgunos sensores no est\u00e1n habilitados de forma predeterminada. Puede habilitarlos en el registro de entidades despu\u00e9s de la configuraci\u00f3n de integraci\u00f3n. La previsi\u00f3n meteorol\u00f3gica no est\u00e1 habilitada de forma predeterminada. Puede habilitarlo en las opciones de integraci\u00f3n.", - "title": "AccuWeather" - } } }, "options": { @@ -21,8 +15,7 @@ "data": { "forecast": "Pron\u00f3stico del tiempo" }, - "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos.", - "title": "Opciones de AccuWeather" + "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilita el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/es.json b/homeassistant/components/accuweather/translations/es.json index 9d0396fddb3..0c3d5560d67 100644 --- a/homeassistant/components/accuweather/translations/es.json +++ b/homeassistant/components/accuweather/translations/es.json @@ -15,9 +15,7 @@ "latitude": "Latitud", "longitude": "Longitud", "name": "Nombre" - }, - "description": "Si necesitas ayuda con la configuraci\u00f3n, echa un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nAlgunos sensores no est\u00e1n habilitados por defecto. Los puedes habilitar en el registro de entidades despu\u00e9s de configurar la integraci\u00f3n.\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado por defecto. Puedes habilitarlo en las opciones de la integraci\u00f3n.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "Pron\u00f3stico del tiempo" }, - "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos.", - "title": "Opciones de AccuWeather" + "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/et.json b/homeassistant/components/accuweather/translations/et.json index 429e2dec61e..2f85bd8663e 100644 --- a/homeassistant/components/accuweather/translations/et.json +++ b/homeassistant/components/accuweather/translations/et.json @@ -18,9 +18,7 @@ "latitude": "Laiuskraad", "longitude": "Pikkuskraad", "name": "Sidumise nimi" - }, - "description": "Kui vajate seadistamisel abi vaadake siit: https://www.home-assistant.io/integrations/accuweather/ \n\n M\u00f5ni andur pole vaikimisi lubatud. P\u00e4rast sidumise seadistamist saate need \u00fcksused lubada. \n Ilmapennustus pole vaikimisi lubatud. Saate selle lubada sidumise s\u00e4tetes.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Ilmateade" }, - "description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 80 minuti j\u00e4rel (muidu 40 minutit).", - "title": "AccuWeatheri valikud" + "description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 80 minuti j\u00e4rel (muidu 40 minutit)." } } }, diff --git a/homeassistant/components/accuweather/translations/fr.json b/homeassistant/components/accuweather/translations/fr.json index cf407ef3962..75ef209a1cf 100644 --- a/homeassistant/components/accuweather/translations/fr.json +++ b/homeassistant/components/accuweather/translations/fr.json @@ -18,9 +18,7 @@ "latitude": "Latitude", "longitude": "Longitude", "name": "Nom" - }, - "description": "Si vous avez besoin d'aide pour la configuration, consultez\u00a0: https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s une fois la configuration de l'int\u00e9gration termin\u00e9e.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez les activer dans les options de l'int\u00e9gration.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Pr\u00e9visions m\u00e9t\u00e9orologiques" }, - "description": "En raison des limitations de la version gratuite de la cl\u00e9 API AccuWeather, lorsque vous activez les pr\u00e9visions m\u00e9t\u00e9orologiques, les mises \u00e0 jour des donn\u00e9es seront effectu\u00e9es toutes les 64 minutes au lieu de toutes les 32 minutes.", - "title": "Options AccuWeather" + "description": "En raison des limitations de la version gratuite de la cl\u00e9 API AccuWeather, lorsque vous activez les pr\u00e9visions m\u00e9t\u00e9orologiques, les mises \u00e0 jour des donn\u00e9es seront effectu\u00e9es toutes les 64 minutes au lieu de toutes les 32 minutes." } } }, diff --git a/homeassistant/components/accuweather/translations/he.json b/homeassistant/components/accuweather/translations/he.json index 77c1e54f3e5..0f054ff11fe 100644 --- a/homeassistant/components/accuweather/translations/he.json +++ b/homeassistant/components/accuweather/translations/he.json @@ -15,9 +15,7 @@ "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "name": "\u05e9\u05dd" - }, - "description": "\u05d0\u05dd \u05d4\u05d9\u05e0\u05da \u05d6\u05e7\u05d5\u05e7 \u05dc\u05e2\u05d6\u05e8\u05d4 \u05e2\u05dd \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4, \u05d9\u05e9 \u05dc\u05e2\u05d9\u05d9\u05df \u05db\u05d0\u05df: https://www.home-assistant.io/integrations/accuweather/\n\n\u05d7\u05d9\u05d9\u05e9\u05e0\u05d9\u05dd \u05de\u05e1\u05d5\u05d9\u05de\u05d9\u05dd \u05d0\u05d9\u05e0\u05dd \u05d6\u05de\u05d9\u05e0\u05d9\u05dd \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc. \u05d1\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea\u05da \u05dc\u05d4\u05e4\u05d5\u05da \u05d0\u05d5\u05ea\u05dd \u05dc\u05d6\u05de\u05d9\u05e0\u05d9\u05dd \u05d1\u05e8\u05d9\u05e9\u05d5\u05dd \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05dc\u05d0\u05d7\u05e8 \u05e7\u05d1\u05d9\u05e2\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1.\n\u05ea\u05d7\u05d6\u05d9\u05ea \u05de\u05d6\u05d2 \u05d4\u05d0\u05d5\u05d5\u05d9\u05e8 \u05d0\u05d9\u05e0\u05d4 \u05d6\u05de\u05d9\u05e0\u05d4 \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc. \u05d1\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea\u05da \u05dc\u05d4\u05e4\u05d5\u05da \u05d0\u05d5\u05ea\u05d5 \u05dc\u05d6\u05de\u05d9\u05df \u05d1\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d4\u05e9\u05d9\u05dc\u05d5\u05d1.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "\u05ea\u05d7\u05d6\u05d9\u05ea \u05de\u05d6\u05d2 \u05d4\u05d0\u05d5\u05d5\u05d9\u05e8" }, - "description": "\u05d1\u05e9\u05dc \u05de\u05d2\u05d1\u05dc\u05d5\u05ea \u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05d7\u05d9\u05e0\u05de\u05d9\u05ea \u05e9\u05dc \u05de\u05e4\u05ea\u05d7 \u05d4-API \u05e9\u05dc AccuWeather, \u05db\u05d0\u05e9\u05e8 \u05ea\u05e4\u05e2\u05d9\u05dc \u05ea\u05d7\u05d6\u05d9\u05ea \u05de\u05d6\u05d2 \u05d0\u05d5\u05d5\u05d9\u05e8, \u05e2\u05d3\u05db\u05d5\u05e0\u05d9 \u05e0\u05ea\u05d5\u05e0\u05d9\u05dd \u05d9\u05d1\u05d5\u05e6\u05e2\u05d5 \u05db\u05dc 80 \u05d3\u05e7\u05d5\u05ea \u05d1\u05de\u05e7\u05d5\u05dd \u05db\u05dc 40 \u05d3\u05e7\u05d5\u05ea.", - "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea AccuWeather" + "description": "\u05d1\u05e9\u05dc \u05de\u05d2\u05d1\u05dc\u05d5\u05ea \u05d4\u05d2\u05d9\u05e8\u05e1\u05d4 \u05d4\u05d7\u05d9\u05e0\u05de\u05d9\u05ea \u05e9\u05dc \u05de\u05e4\u05ea\u05d7 \u05d4-API \u05e9\u05dc AccuWeather, \u05db\u05d0\u05e9\u05e8 \u05ea\u05e4\u05e2\u05d9\u05dc \u05ea\u05d7\u05d6\u05d9\u05ea \u05de\u05d6\u05d2 \u05d0\u05d5\u05d5\u05d9\u05e8, \u05e2\u05d3\u05db\u05d5\u05e0\u05d9 \u05e0\u05ea\u05d5\u05e0\u05d9\u05dd \u05d9\u05d1\u05d5\u05e6\u05e2\u05d5 \u05db\u05dc 80 \u05d3\u05e7\u05d5\u05ea \u05d1\u05de\u05e7\u05d5\u05dd \u05db\u05dc 40 \u05d3\u05e7\u05d5\u05ea." } } }, diff --git a/homeassistant/components/accuweather/translations/hu.json b/homeassistant/components/accuweather/translations/hu.json index 1b30dd09daf..0e207c5fde8 100644 --- a/homeassistant/components/accuweather/translations/hu.json +++ b/homeassistant/components/accuweather/translations/hu.json @@ -18,9 +18,7 @@ "latitude": "Sz\u00e9less\u00e9g", "longitude": "Hossz\u00fas\u00e1g", "name": "Elnevez\u00e9s" - }, - "description": "Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ge a konfigur\u00e1l\u00e1shoz, n\u00e9zze meg itt: https://www.home-assistant.io/integrations/accuweather/ \n\nEgyes \u00e9rz\u00e9kel\u0151k alap\u00e9rtelmez\u00e9s szerint nincsenek enged\u00e9lyezve. Az integr\u00e1ci\u00f3s konfigur\u00e1ci\u00f3 ut\u00e1n enged\u00e9lyezheti \u0151ket az entit\u00e1s-nyilv\u00e1ntart\u00e1sban.\nAz id\u0151j\u00e1r\u00e1s-el\u0151rejelz\u00e9s alap\u00e9rtelmez\u00e9s szerint nincs enged\u00e9lyezve. Ezt az integr\u00e1ci\u00f3s be\u00e1ll\u00edt\u00e1sokban enged\u00e9lyezheti.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Id\u0151j\u00e1r\u00e1s el\u0151rejelz\u00e9s" }, - "description": "Az AccuWeather API kulcs ingyenes verzi\u00f3j\u00e1nak korl\u00e1tai miatt, amikor enged\u00e9lyezi az id\u0151j\u00e1r\u00e1s -el\u0151rejelz\u00e9st, az adatfriss\u00edt\u00e9seket 40 percenk\u00e9nt 80 percenk\u00e9nt hajtj\u00e1k v\u00e9gre.", - "title": "AccuWeather be\u00e1ll\u00edt\u00e1sok" + "description": "Az AccuWeather API kulcs ingyenes verzi\u00f3j\u00e1nak korl\u00e1tai miatt, amikor enged\u00e9lyezi az id\u0151j\u00e1r\u00e1s -el\u0151rejelz\u00e9st, az adatfriss\u00edt\u00e9seket 40 percenk\u00e9nt 80 percenk\u00e9nt hajtj\u00e1k v\u00e9gre." } } }, diff --git a/homeassistant/components/accuweather/translations/id.json b/homeassistant/components/accuweather/translations/id.json index 7bf9f27e8b2..1cd49752342 100644 --- a/homeassistant/components/accuweather/translations/id.json +++ b/homeassistant/components/accuweather/translations/id.json @@ -18,9 +18,7 @@ "latitude": "Lintang", "longitude": "Bujur", "name": "Nama" - }, - "description": "Jika Anda memerlukan bantuan tentang konfigurasi, baca di sini: https://www.home-assistant.io/integrations/accuweather/\n\nBeberapa sensor tidak diaktifkan secara default. Anda dapat mengaktifkannya di registri entitas setelah konfigurasi integrasi.\nPrakiraan cuaca tidak diaktifkan secara default. Anda dapat mengaktifkannya di opsi integrasi.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Prakiraan cuaca" }, - "description": "Karena keterbatasan versi gratis kunci API AccuWeather, ketika Anda mengaktifkan prakiraan cuaca, pembaruan data akan dilakukan setiap 80 menit, bukan setiap 40 menit.", - "title": "Opsi AccuWeather" + "description": "Karena keterbatasan versi gratis kunci API AccuWeather, ketika Anda mengaktifkan prakiraan cuaca, pembaruan data akan dilakukan setiap 80 menit, bukan setiap 40 menit." } } }, diff --git a/homeassistant/components/accuweather/translations/it.json b/homeassistant/components/accuweather/translations/it.json index 9daaf378fb4..d07a900adc9 100644 --- a/homeassistant/components/accuweather/translations/it.json +++ b/homeassistant/components/accuweather/translations/it.json @@ -18,9 +18,7 @@ "latitude": "Latitudine", "longitude": "Logitudine", "name": "Nome" - }, - "description": "Se hai bisogno di aiuto con la configurazione dai un'occhiata qui: https://www.home-assistant.io/integrations/accuweather/ \n\nAlcuni sensori non sono abilitati per impostazione predefinita. \u00c8 possibile abilitarli nel registro entit\u00e0 dopo la configurazione di integrazione. \nLe previsioni meteo non sono abilitate per impostazione predefinita. Puoi abilitarle nelle opzioni di integrazione.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Previsioni meteo" }, - "description": "A causa delle limitazioni della versione gratuita della chiave API AccuWeather, quando si abilitano le previsioni del tempo, gli aggiornamenti dei dati verranno eseguiti ogni 80 minuti invece che ogni 40.", - "title": "Opzioni AccuWeather" + "description": "A causa delle limitazioni della versione gratuita della chiave API AccuWeather, quando si abilitano le previsioni del tempo, gli aggiornamenti dei dati verranno eseguiti ogni 80 minuti invece che ogni 40." } } }, diff --git a/homeassistant/components/accuweather/translations/ja.json b/homeassistant/components/accuweather/translations/ja.json index d05e75e74b5..21ec4e6068d 100644 --- a/homeassistant/components/accuweather/translations/ja.json +++ b/homeassistant/components/accuweather/translations/ja.json @@ -18,9 +18,7 @@ "latitude": "\u7def\u5ea6", "longitude": "\u7d4c\u5ea6", "name": "\u540d\u524d" - }, - "description": "\u8a2d\u5b9a\u306b\u3064\u3044\u3066\u30d8\u30eb\u30d7\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/accuweather/ \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\n\n\u4e00\u90e8\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306e\u8a2d\u5b9a\u5f8c\u306b\u3001\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30ec\u30b8\u30b9\u30c8\u30ea\u3067\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\n\u5929\u6c17\u4e88\u5831\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3067\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "\u5929\u6c17\u4e88\u5831" }, - "description": "\u5236\u9650\u306b\u3088\u308a\u7121\u6599\u30d0\u30fc\u30b8\u30e7\u30f3\u306eAccuWeather API\u30ad\u30fc\u3067\u306f\u3001\u5929\u6c17\u4e88\u5831\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u306f40\u5206\u3067\u306f\u306a\u304f80\u5206\u3054\u3068\u3067\u3059\u3002", - "title": "AccuWeather\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" + "description": "\u5236\u9650\u306b\u3088\u308a\u7121\u6599\u30d0\u30fc\u30b8\u30e7\u30f3\u306eAccuWeather API\u30ad\u30fc\u3067\u306f\u3001\u5929\u6c17\u4e88\u5831\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u30c7\u30fc\u30bf\u306e\u66f4\u65b0\u306f40\u5206\u3067\u306f\u306a\u304f80\u5206\u3054\u3068\u3067\u3059\u3002" } } }, diff --git a/homeassistant/components/accuweather/translations/ko.json b/homeassistant/components/accuweather/translations/ko.json index d992d0bfdd4..ad33fae591c 100644 --- a/homeassistant/components/accuweather/translations/ko.json +++ b/homeassistant/components/accuweather/translations/ko.json @@ -15,9 +15,7 @@ "latitude": "\uc704\ub3c4", "longitude": "\uacbd\ub3c4", "name": "\uc774\ub984" - }, - "description": "\uad6c\uc131\uc5d0 \ub300\ud55c \ub3c4\uc6c0\uc774 \ud544\uc694\ud55c \uacbd\uc6b0 \ub2e4\uc74c\uc744 \ucc38\uc870\ud574\uc8fc\uc138\uc694: https://www.home-assistant.io/integrations/accuweather/\n\n\uc77c\ubd80 \uc13c\uc11c\ub294 \uae30\ubcf8\uc801\uc73c\ub85c \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uad6c\uc131 \ud6c4 \uad6c\uc131\uc694\uc18c \ub808\uc9c0\uc2a4\ud2b8\ub9ac\uc5d0\uc11c \ud65c\uc131\ud654\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.\n\uc77c\uae30\uc608\ubcf4\ub294 \uae30\ubcf8\uc801\uc73c\ub85c \ud65c\uc131\ud654\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc635\uc158\uc5d0\uc11c \ud65c\uc131\ud654\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "\ub0a0\uc528 \uc608\ubcf4" }, - "description": "\ubb34\ub8cc \ubc84\uc804\uc758 AccuWeather API \ud0a4\ub85c \uc77c\uae30\uc608\ubcf4\ub97c \ud65c\uc131\ud654\ud55c \uacbd\uc6b0 \uc81c\ud55c\uc0ac\ud56d\uc73c\ub85c \uc778\ud574 \uc5c5\ub370\uc774\ud2b8\ub294 40 \ubd84\uc774 \uc544\ub2cc 80 \ubd84\ub9c8\ub2e4 \uc218\ud589\ub429\ub2c8\ub2e4.", - "title": "AccuWeather \uc635\uc158" + "description": "\ubb34\ub8cc \ubc84\uc804\uc758 AccuWeather API \ud0a4\ub85c \uc77c\uae30\uc608\ubcf4\ub97c \ud65c\uc131\ud654\ud55c \uacbd\uc6b0 \uc81c\ud55c\uc0ac\ud56d\uc73c\ub85c \uc778\ud574 \uc5c5\ub370\uc774\ud2b8\ub294 40 \ubd84\uc774 \uc544\ub2cc 80 \ubd84\ub9c8\ub2e4 \uc218\ud589\ub429\ub2c8\ub2e4." } } }, diff --git a/homeassistant/components/accuweather/translations/lb.json b/homeassistant/components/accuweather/translations/lb.json index 7f3855a7b9c..618b9ef984b 100644 --- a/homeassistant/components/accuweather/translations/lb.json +++ b/homeassistant/components/accuweather/translations/lb.json @@ -15,9 +15,7 @@ "latitude": "Breedegrad", "longitude": "L\u00e4ngegrad", "name": "Numm" - }, - "description": "Falls du H\u00ebllef mat der Konfiguratioun brauch kuck h\u00e9i:\nhttps://www.home-assistant.io/integrations/accuweather/\n\nVerschidde Sensoren si standardm\u00e9isseg net aktiv. Du kanns d\u00e9i an der Entit\u00e9ie Registry no der Konfiguratioun vun der Integratioun aschalten.\n\nWieder Pr\u00e9visounen si standardm\u00e9isseg net aktiv. Du kanns d\u00e9i an den Optioune vun der Integratioun aschalten.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "Wieder Pr\u00e9visioun" }, - "description": "Duerch d'Limite vun der Gratis Versioun vun der AccuWeather API, wann d'Wieder Pr\u00e9visoune aktiv\u00e9iert sinn, ginn d'Aktualis\u00e9ierungen all 64 Minutten gemaach, am plaatz vun all 32 Minutten.", - "title": "AccuWeather Optiounen" + "description": "Duerch d'Limite vun der Gratis Versioun vun der AccuWeather API, wann d'Wieder Pr\u00e9visoune aktiv\u00e9iert sinn, ginn d'Aktualis\u00e9ierungen all 64 Minutten gemaach, am plaatz vun all 32 Minutten." } } }, diff --git a/homeassistant/components/accuweather/translations/nl.json b/homeassistant/components/accuweather/translations/nl.json index 2a6dd1a812b..e83b3d8355a 100644 --- a/homeassistant/components/accuweather/translations/nl.json +++ b/homeassistant/components/accuweather/translations/nl.json @@ -18,9 +18,7 @@ "latitude": "Breedtegraad", "longitude": "Lengtegraad", "name": "Naam" - }, - "description": "Als je hulp nodig hebt bij de configuratie, kijk dan hier: https://www.home-assistant.io/integrations/accuweather/ \n\n Sommige sensoren zijn niet standaard ingeschakeld. U kunt ze inschakelen in het entiteitenregister na de integratieconfiguratie.\n Weersvoorspelling is niet standaard ingeschakeld. U kunt het inschakelen in de integratieopties.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Weervoorspelling" }, - "description": "Vanwege de beperkingen van de gratis versie van de AccuWeather API-sleutel, worden gegevensupdates elke 64 minuten in plaats van elke 32 minuten uitgevoerd wanneer u weersvoorspelling inschakelt.", - "title": "AccuWeather-opties" + "description": "Vanwege de beperkingen van de gratis versie van de AccuWeather API-sleutel, worden gegevensupdates elke 64 minuten in plaats van elke 32 minuten uitgevoerd wanneer u weersvoorspelling inschakelt." } } }, diff --git a/homeassistant/components/accuweather/translations/no.json b/homeassistant/components/accuweather/translations/no.json index af689b2fd1c..dc203abe714 100644 --- a/homeassistant/components/accuweather/translations/no.json +++ b/homeassistant/components/accuweather/translations/no.json @@ -18,9 +18,7 @@ "latitude": "Breddegrad", "longitude": "Lengdegrad", "name": "Navn" - }, - "description": "Hvis du trenger hjelp med konfigurasjonen, kan du se her: https://www.home-assistant.io/integrations/accuweather/ \n\nNoen sensorer er ikke aktivert som standard. Du kan aktivere dem i entitetsregisteret etter integrasjonskonfigurasjonen. \nV\u00e6rmelding er ikke aktivert som standard. Du kan aktivere det i integrasjonsalternativene.", - "title": "" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "V\u00e6rmelding" }, - "description": "P\u00e5 grunn av begrensningene i den gratis versjonen av AccuWeather API-n\u00f8kkelen, vil dataoppdateringer utf\u00f8res hvert 80. minutt i stedet for hvert 40. minutt n\u00e5r du aktiverer v\u00e6rmelding.", - "title": "AccuWeather-alternativer" + "description": "P\u00e5 grunn av begrensningene i den gratis versjonen av AccuWeather API-n\u00f8kkelen, vil dataoppdateringer utf\u00f8res hvert 80. minutt i stedet for hvert 40. minutt n\u00e5r du aktiverer v\u00e6rmelding." } } }, diff --git a/homeassistant/components/accuweather/translations/pl.json b/homeassistant/components/accuweather/translations/pl.json index a8ef8a3bfa0..764218f8e11 100644 --- a/homeassistant/components/accuweather/translations/pl.json +++ b/homeassistant/components/accuweather/translations/pl.json @@ -18,9 +18,7 @@ "latitude": "Szeroko\u015b\u0107 geograficzna", "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa" - }, - "description": "Je\u015bli potrzebujesz pomocy z konfiguracj\u0105, przejd\u017a na stron\u0119: https://www.home-assistant.io/integrations/accuweather/ \n\nCz\u0119\u015b\u0107 sensor\u00f3w nie jest w\u0142\u0105czona domy\u015blnie. Mo\u017cesz je w\u0142\u0105czy\u0107 w rejestrze encji po konfiguracji integracji.\nPrognoza pogody nie jest domy\u015blnie w\u0142\u0105czona. Mo\u017cesz j\u0105 w\u0142\u0105czy\u0107 w opcjach integracji.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Prognoza pogody" }, - "description": "Ze wzgl\u0119du na ograniczenia darmowej wersji klucza API AccuWeather po w\u0142\u0105czeniu prognozy pogody aktualizacje danych b\u0119d\u0105 wykonywane co 80 minut zamiast co 40 minut.", - "title": "Opcje AccuWeather" + "description": "Ze wzgl\u0119du na ograniczenia darmowej wersji klucza API AccuWeather po w\u0142\u0105czeniu prognozy pogody aktualizacje danych b\u0119d\u0105 wykonywane co 80 minut zamiast co 40 minut." } } }, diff --git a/homeassistant/components/accuweather/translations/pt-BR.json b/homeassistant/components/accuweather/translations/pt-BR.json index 4bb1bbbc97e..3fc0d95ad2a 100644 --- a/homeassistant/components/accuweather/translations/pt-BR.json +++ b/homeassistant/components/accuweather/translations/pt-BR.json @@ -18,9 +18,7 @@ "latitude": "Latitude", "longitude": "Longitude", "name": "Nome" - }, - "description": "Se precisar de ajuda com a configura\u00e7\u00e3o, d\u00ea uma olhada aqui: https://www.home-assistant.io/integrations/accuweather/ \n\nAlguns sensores n\u00e3o s\u00e3o ativados por padr\u00e3o. Voc\u00ea pode habilit\u00e1-los no registro da entidade ap\u00f3s a configura\u00e7\u00e3o da integra\u00e7\u00e3o.\nA previs\u00e3o do tempo n\u00e3o est\u00e1 habilitada por padr\u00e3o. Voc\u00ea pode habilit\u00e1-lo nas op\u00e7\u00f5es de integra\u00e7\u00e3o.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Previs\u00e3o do Tempo" }, - "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave da API AccuWeather, quando voc\u00ea habilita a previs\u00e3o do tempo, as atualiza\u00e7\u00f5es de dados ser\u00e3o realizadas a cada 64 minutos em vez de a cada 32 minutos.", - "title": "Op\u00e7\u00f5es do AccuWeather" + "description": "Devido \u00e0s limita\u00e7\u00f5es da vers\u00e3o gratuita da chave da API AccuWeather, quando voc\u00ea habilita a previs\u00e3o do tempo, as atualiza\u00e7\u00f5es de dados ser\u00e3o realizadas a cada 64 minutos em vez de a cada 32 minutos." } } }, diff --git a/homeassistant/components/accuweather/translations/ru.json b/homeassistant/components/accuweather/translations/ru.json index dfd24b6f147..2f08263b4f5 100644 --- a/homeassistant/components/accuweather/translations/ru.json +++ b/homeassistant/components/accuweather/translations/ru.json @@ -18,9 +18,7 @@ "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" - }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438, \u0435\u0441\u043b\u0438 \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439:\nhttps://www.home-assistant.io/integrations/accuweather/ \n\n\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0441\u043a\u0440\u044b\u0442\u044b \u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432 \u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u044b" }, - "description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 80 \u043c\u0438\u043d\u0443\u0442, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 40 \u043c\u0438\u043d\u0443\u0442.", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 AccuWeather" + "description": "\u0412 \u0441\u0432\u044f\u0437\u0438 \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430 \u043f\u043e\u0433\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0435 80 \u043c\u0438\u043d\u0443\u0442, \u0430 \u043d\u0435 \u043a\u0430\u0436\u0434\u044b\u0435 40 \u043c\u0438\u043d\u0443\u0442." } } }, diff --git a/homeassistant/components/accuweather/translations/tr.json b/homeassistant/components/accuweather/translations/tr.json index b5efeb7080e..e0907fe2fa8 100644 --- a/homeassistant/components/accuweather/translations/tr.json +++ b/homeassistant/components/accuweather/translations/tr.json @@ -18,9 +18,7 @@ "latitude": "Enlem", "longitude": "Boylam", "name": "Ad" - }, - "description": "Yap\u0131land\u0131rmayla ilgili yard\u0131ma ihtiyac\u0131n\u0131z varsa buraya bak\u0131n: https://www.home-assistant.io/integrations/accuweather/ \n\n Baz\u0131 sens\u00f6rler varsay\u0131lan olarak etkin de\u011fildir. Bunlar\u0131, entegrasyon yap\u0131land\u0131rmas\u0131ndan sonra varl\u0131k kay\u0131t defterinde etkinle\u015ftirebilirsiniz.\n Hava tahmini varsay\u0131lan olarak etkin de\u011fildir. Entegrasyon se\u00e7eneklerinde etkinle\u015ftirebilirsiniz.", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "Hava Durumu tahmini" }, - "description": "AccuWeather API anahtar\u0131n\u0131n \u00fccretsiz s\u00fcr\u00fcm\u00fcn\u00fcn s\u0131n\u0131rlamalar\u0131 nedeniyle, hava tahminini etkinle\u015ftirdi\u011finizde, veri g\u00fcncellemeleri her 40 dakikada bir yerine 80 dakikada bir ger\u00e7ekle\u015ftirilir.", - "title": "AccuWeather Se\u00e7enekleri" + "description": "AccuWeather API anahtar\u0131n\u0131n \u00fccretsiz s\u00fcr\u00fcm\u00fcn\u00fcn s\u0131n\u0131rlamalar\u0131 nedeniyle, hava tahminini etkinle\u015ftirdi\u011finizde, veri g\u00fcncellemeleri her 40 dakikada bir yerine 80 dakikada bir ger\u00e7ekle\u015ftirilir." } } }, diff --git a/homeassistant/components/accuweather/translations/uk.json b/homeassistant/components/accuweather/translations/uk.json index cb02c7e0ad5..99818d354f9 100644 --- a/homeassistant/components/accuweather/translations/uk.json +++ b/homeassistant/components/accuweather/translations/uk.json @@ -15,9 +15,7 @@ "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", "longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430" - }, - "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438, \u044f\u043a\u0449\u043e \u0412\u0430\u043c \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u0430 \u0434\u043e\u043f\u043e\u043c\u043e\u0433\u0430 \u0437 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u043c:\n https://www.home-assistant.io/integrations/accuweather/ \n\n\u0417\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0434\u0435\u044f\u043a\u0456 \u0441\u0435\u043d\u0441\u043e\u0440\u0438 \u043f\u0440\u0438\u0445\u043e\u0432\u0430\u043d\u0456 \u0456 \u0432\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u0438. \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0430\u043a\u0442\u0438\u0432\u0443\u0432\u0430\u0442\u0438 \u0432\u0456\u0434\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u0438\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u0456\u0432 \u0432 \u0440\u0435\u0454\u0441\u0442\u0440\u0456 \u043e\u0431'\u0454\u043a\u0442\u0456\u0432 \u0456 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u043f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u0438 \u0432 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u0445 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457.", - "title": "AccuWeather" + } } } }, @@ -27,8 +25,7 @@ "data": { "forecast": "\u041f\u0440\u043e\u0433\u043d\u043e\u0437 \u043f\u043e\u0433\u043e\u0434\u0438" }, - "description": "\u0423 \u0437\u0432'\u044f\u0437\u043a\u0443 \u0437 \u043e\u0431\u043c\u0435\u0436\u0435\u043d\u043d\u044f\u043c\u0438 \u0431\u0435\u0437\u043a\u043e\u0448\u0442\u043e\u0432\u043d\u043e\u0457 \u0432\u0435\u0440\u0441\u0456\u0457 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u0456 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0443 \u043f\u043e\u0433\u043e\u0434\u0438 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0434\u0430\u043d\u0438\u0445 \u0431\u0443\u0434\u0435 \u0432\u0456\u0434\u0431\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u043a\u043e\u0436\u043d\u0456 64 \u0445\u0432\u0438\u043b\u0438\u043d\u0438, \u0430 \u043d\u0435 \u043a\u043e\u0436\u043d\u0456 32 \u0445\u0432\u0438\u043b\u0438\u043d\u0438.", - "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f AccuWeather" + "description": "\u0423 \u0437\u0432'\u044f\u0437\u043a\u0443 \u0437 \u043e\u0431\u043c\u0435\u0436\u0435\u043d\u043d\u044f\u043c\u0438 \u0431\u0435\u0437\u043a\u043e\u0448\u0442\u043e\u0432\u043d\u043e\u0457 \u0432\u0435\u0440\u0441\u0456\u0457 \u043a\u043b\u044e\u0447\u0430 API AccuWeather, \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u0456 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0443 \u043f\u043e\u0433\u043e\u0434\u0438 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0434\u0430\u043d\u0438\u0445 \u0431\u0443\u0434\u0435 \u0432\u0456\u0434\u0431\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u043a\u043e\u0436\u043d\u0456 64 \u0445\u0432\u0438\u043b\u0438\u043d\u0438, \u0430 \u043d\u0435 \u043a\u043e\u0436\u043d\u0456 32 \u0445\u0432\u0438\u043b\u0438\u043d\u0438." } } }, diff --git a/homeassistant/components/accuweather/translations/zh-Hant.json b/homeassistant/components/accuweather/translations/zh-Hant.json index 6d9e7e1c36f..c7476b33460 100644 --- a/homeassistant/components/accuweather/translations/zh-Hant.json +++ b/homeassistant/components/accuweather/translations/zh-Hant.json @@ -18,9 +18,7 @@ "latitude": "\u7def\u5ea6", "longitude": "\u7d93\u5ea6", "name": "\u540d\u7a31" - }, - "description": "\u5047\u5982\u4f60\u9700\u8981\u5354\u52a9\u9032\u884c\u8a2d\u5b9a\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/accuweather/\n\n\u67d0\u4e9b\u611f\u6e2c\u5668\u9810\u8a2d\u70ba\u672a\u555f\u7528\uff0c\u53ef\u4ee5\u65bc\u6574\u5408\u8a2d\u5b9a\u4e2d\u555f\u7528\u9019\u4e9b\u5be6\u9ad4\u3002\u5929\u6c23\u9810\u5831\u9810\u8a2d\u672a\u958b\u555f\u3002\u53ef\u4ee5\u65bc\u6574\u5408\u9078\u9805\u4e2d\u958b\u555f\u3002", - "title": "AccuWeather" + } } } }, @@ -30,8 +28,7 @@ "data": { "forecast": "\u5929\u6c23\u9810\u5831" }, - "description": "\u7531\u65bc AccuWeather API \u91d1\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 80 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 40 \u5206\u9418\u3002", - "title": "AccuWeather \u9078\u9805" + "description": "\u7531\u65bc AccuWeather API \u91d1\u9470\u514d\u8cbb\u7248\u672c\u9650\u5236\uff0c\u7576\u958b\u555f\u5929\u6c23\u9810\u5831\u6642\u3001\u6578\u64da\u6703\u6bcf 80 \u5206\u9418\u66f4\u65b0\u4e00\u6b21\uff0c\u800c\u975e 40 \u5206\u9418\u3002" } } }, diff --git a/homeassistant/components/adax/translations/bg.json b/homeassistant/components/adax/translations/bg.json index d76109bfe01..e0204d6b43b 100644 --- a/homeassistant/components/adax/translations/bg.json +++ b/homeassistant/components/adax/translations/bg.json @@ -5,8 +5,7 @@ "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { "cloud": { @@ -19,12 +18,6 @@ "wifi_pswd": "Wi-Fi \u043f\u0430\u0440\u043e\u043b\u0430", "wifi_ssid": "Wi-Fi SSID" } - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - } } } } diff --git a/homeassistant/components/adax/translations/ca.json b/homeassistant/components/adax/translations/ca.json index 4e9f5eeb317..af3f2c3dc3f 100644 --- a/homeassistant/components/adax/translations/ca.json +++ b/homeassistant/components/adax/translations/ca.json @@ -7,8 +7,7 @@ "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida" + "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID del compte", - "connection_type": "Selecciona el tipus de connexi\u00f3", - "host": "Amfitri\u00f3", - "password": "Contrasenya" + "connection_type": "Selecciona el tipus de connexi\u00f3" }, "description": "Selecciona el tipus de connexi\u00f3. La local necessita escalfadors amb Bluetooth" } diff --git a/homeassistant/components/adax/translations/cs.json b/homeassistant/components/adax/translations/cs.json index 07eeaa34f63..cbe934d3495 100644 --- a/homeassistant/components/adax/translations/cs.json +++ b/homeassistant/components/adax/translations/cs.json @@ -7,8 +7,7 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID \u00fa\u010dtu", - "connection_type": "Vyberte typ p\u0159ipojen\u00ed", - "host": "Hostitel", - "password": "Heslo" + "connection_type": "Vyberte typ p\u0159ipojen\u00ed" }, "description": "Vyberte typ p\u0159ipojen\u00ed. Lok\u00e1ln\u00ed vy\u017eaduje oh\u0159\u00edva\u010de s bluetooth" } diff --git a/homeassistant/components/adax/translations/de.json b/homeassistant/components/adax/translations/de.json index 711a8b44645..a3e4b1b78d4 100644 --- a/homeassistant/components/adax/translations/de.json +++ b/homeassistant/components/adax/translations/de.json @@ -7,8 +7,7 @@ "invalid_auth": "Ung\u00fcltige Authentifizierung" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_auth": "Ung\u00fcltige Authentifizierung" + "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Konto-ID", - "connection_type": "Verbindungstyp ausw\u00e4hlen", - "host": "Host", - "password": "Passwort" + "connection_type": "Verbindungstyp ausw\u00e4hlen" }, "description": "Verbindungstyp ausw\u00e4hlen. Lokal erfordert Heizungen mit Bluetooth" } diff --git a/homeassistant/components/adax/translations/el.json b/homeassistant/components/adax/translations/el.json index ead0eb7e4f1..e1be44a197a 100644 --- a/homeassistant/components/adax/translations/el.json +++ b/homeassistant/components/adax/translations/el.json @@ -7,8 +7,7 @@ "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd", - "connection_type": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c4\u03cd\u03c0\u03bf\u03c5 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + "connection_type": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c4\u03cd\u03c0\u03bf\u03c5 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2. \u03a4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03b8\u03b5\u03c1\u03bc\u03ac\u03c3\u03c4\u03c1\u03b5\u03c2 \u03bc\u03b5 bluetooth" } diff --git a/homeassistant/components/adax/translations/en.json b/homeassistant/components/adax/translations/en.json index ae31fdbc041..89c73702106 100644 --- a/homeassistant/components/adax/translations/en.json +++ b/homeassistant/components/adax/translations/en.json @@ -7,8 +7,7 @@ "invalid_auth": "Invalid authentication" }, "error": { - "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication" + "cannot_connect": "Failed to connect" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Account ID", - "connection_type": "Select connection type", - "host": "Host", - "password": "Password" + "connection_type": "Select connection type" }, "description": "Select connection type. Local requires heaters with bluetooth" } diff --git a/homeassistant/components/adax/translations/es-419.json b/homeassistant/components/adax/translations/es-419.json index 85df150d663..597d031c598 100644 --- a/homeassistant/components/adax/translations/es-419.json +++ b/homeassistant/components/adax/translations/es-419.json @@ -20,7 +20,6 @@ }, "user": { "data": { - "account_id": "ID de cuenta", "connection_type": "Seleccione el tipo de conexi\u00f3n" }, "description": "Seleccione el tipo de conexi\u00f3n. Local requiere calentadores con bluetooth" diff --git a/homeassistant/components/adax/translations/es.json b/homeassistant/components/adax/translations/es.json index 413f8be0fa0..ea350d97f4e 100644 --- a/homeassistant/components/adax/translations/es.json +++ b/homeassistant/components/adax/translations/es.json @@ -7,8 +7,7 @@ "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "error": { - "cannot_connect": "No se pudo conectar", - "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" + "cannot_connect": "No se pudo conectar" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID de la cuenta", - "connection_type": "Seleccione el tipo de conexi\u00f3n", - "host": "Host", - "password": "Contrase\u00f1a" + "connection_type": "Seleccione el tipo de conexi\u00f3n" }, "description": "Seleccione el tipo de conexi\u00f3n. Local requiere calentadores con bluetooth" } diff --git a/homeassistant/components/adax/translations/et.json b/homeassistant/components/adax/translations/et.json index 2d154614295..8f164bc167c 100644 --- a/homeassistant/components/adax/translations/et.json +++ b/homeassistant/components/adax/translations/et.json @@ -7,8 +7,7 @@ "invalid_auth": "Tuvastamine nurjus" }, "error": { - "cannot_connect": "\u00dchendamine nurjus", - "invalid_auth": "Tuvastamise viga" + "cannot_connect": "\u00dchendamine nurjus" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Konto ID", - "connection_type": "Vali \u00fchenduse t\u00fc\u00fcp", - "host": "Host", - "password": "Salas\u00f5na" + "connection_type": "Vali \u00fchenduse t\u00fc\u00fcp" }, "description": "Vali \u00fchenduse t\u00fc\u00fcp. Kohalik n\u00f5uab bluetoothiga k\u00fctteseadmeid" } diff --git a/homeassistant/components/adax/translations/fr.json b/homeassistant/components/adax/translations/fr.json index edcdcdd2738..1883db9c7f0 100644 --- a/homeassistant/components/adax/translations/fr.json +++ b/homeassistant/components/adax/translations/fr.json @@ -7,8 +7,7 @@ "invalid_auth": "Authentification non valide" }, "error": { - "cannot_connect": "\u00c9chec de connexion", - "invalid_auth": "Authentification non valide" + "cannot_connect": "\u00c9chec de connexion" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "identifiant de compte", - "connection_type": "S\u00e9lectionner le type de connexion", - "host": "H\u00f4te", - "password": "Mot de passe" + "connection_type": "S\u00e9lectionner le type de connexion" }, "description": "S\u00e9lectionnez le type de connexion. Local n\u00e9cessite des radiateurs avec Bluetooth" } diff --git a/homeassistant/components/adax/translations/he.json b/homeassistant/components/adax/translations/he.json index 0ba47926a07..e07cb6338ef 100644 --- a/homeassistant/components/adax/translations/he.json +++ b/homeassistant/components/adax/translations/he.json @@ -5,21 +5,13 @@ "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" }, "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { "cloud": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" } - }, - "user": { - "data": { - "account_id": "\u05de\u05d6\u05d4\u05d4 \u05d7\u05e9\u05d1\u05d5\u05df", - "host": "\u05de\u05d0\u05e8\u05d7", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - } } } } diff --git a/homeassistant/components/adax/translations/hu.json b/homeassistant/components/adax/translations/hu.json index a2a6dcdacaf..d0209483231 100644 --- a/homeassistant/components/adax/translations/hu.json +++ b/homeassistant/components/adax/translations/hu.json @@ -7,8 +7,7 @@ "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" }, "error": { - "cannot_connect": "Nem siker\u00fclt csatlakozni", - "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s" + "cannot_connect": "Nem siker\u00fclt csatlakozni" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Fi\u00f3k ID", - "connection_type": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t", - "host": "C\u00edm", - "password": "Jelsz\u00f3" + "connection_type": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t" }, "description": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t. A Helyi kapcsolathoz bluetooth-os f\u0171t\u0151berendez\u00e9sekre van sz\u00fcks\u00e9g" } diff --git a/homeassistant/components/adax/translations/id.json b/homeassistant/components/adax/translations/id.json index 864a8ed623d..5dd3f88d47d 100644 --- a/homeassistant/components/adax/translations/id.json +++ b/homeassistant/components/adax/translations/id.json @@ -7,8 +7,7 @@ "invalid_auth": "Autentikasi tidak valid" }, "error": { - "cannot_connect": "Gagal terhubung", - "invalid_auth": "Autentikasi tidak valid" + "cannot_connect": "Gagal terhubung" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID Akun", - "connection_type": "Pilih jenis koneksi", - "host": "Host", - "password": "Kata Sandi" + "connection_type": "Pilih jenis koneksi" }, "description": "Pilih jenis koneksi. Lokal membutuhkan pemanas dengan bluetooth" } diff --git a/homeassistant/components/adax/translations/it.json b/homeassistant/components/adax/translations/it.json index 17095fab12b..0b516951483 100644 --- a/homeassistant/components/adax/translations/it.json +++ b/homeassistant/components/adax/translations/it.json @@ -7,8 +7,7 @@ "invalid_auth": "Autenticazione non valida" }, "error": { - "cannot_connect": "Impossibile connettersi", - "invalid_auth": "Autenticazione non valida" + "cannot_connect": "Impossibile connettersi" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID account", - "connection_type": "Seleziona il tipo di connessione", - "host": "Host", - "password": "Password" + "connection_type": "Seleziona il tipo di connessione" }, "description": "Seleziona il tipo di connessione. Locale richiede riscaldatori con bluetooth" } diff --git a/homeassistant/components/adax/translations/ja.json b/homeassistant/components/adax/translations/ja.json index e432e23e081..604aa892f5b 100644 --- a/homeassistant/components/adax/translations/ja.json +++ b/homeassistant/components/adax/translations/ja.json @@ -7,8 +7,7 @@ "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "\u30a2\u30ab\u30a6\u30f3\u30c8ID", - "connection_type": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u306e\u9078\u629e", - "host": "\u30db\u30b9\u30c8", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" + "connection_type": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u306e\u9078\u629e" }, "description": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u30ed\u30fc\u30ab\u30eb\u306b\u306fBluetooth\u4ed8\u304d\u306e\u30d2\u30fc\u30bf\u30fc\u304c\u5fc5\u8981\u3067\u3059" } diff --git a/homeassistant/components/adax/translations/nl.json b/homeassistant/components/adax/translations/nl.json index ced3b8bdab0..cef5c118af5 100644 --- a/homeassistant/components/adax/translations/nl.json +++ b/homeassistant/components/adax/translations/nl.json @@ -7,8 +7,7 @@ "invalid_auth": "Ongeldige authenticatie" }, "error": { - "cannot_connect": "Kan geen verbinding maken", - "invalid_auth": "Ongeldige authenticatie" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Account ID", - "connection_type": "Selecteer verbindingstype", - "host": "Host", - "password": "Wachtwoord" + "connection_type": "Selecteer verbindingstype" }, "description": "Selecteer verbindingstype. Lokaal vereist verwarming met Bluetooth." } diff --git a/homeassistant/components/adax/translations/no.json b/homeassistant/components/adax/translations/no.json index 23e82250673..a8787392af3 100644 --- a/homeassistant/components/adax/translations/no.json +++ b/homeassistant/components/adax/translations/no.json @@ -7,8 +7,7 @@ "invalid_auth": "Ugyldig godkjenning" }, "error": { - "cannot_connect": "Tilkobling mislyktes", - "invalid_auth": "Ugyldig godkjenning" + "cannot_connect": "Tilkobling mislyktes" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Konto-ID", - "connection_type": "Velg tilkoblingstype", - "host": "Vert", - "password": "Passord" + "connection_type": "Velg tilkoblingstype" }, "description": "Velg tilkoblingstype. Lokalt krever varmeovner med bluetooth" } diff --git a/homeassistant/components/adax/translations/pl.json b/homeassistant/components/adax/translations/pl.json index 2a8e1016805..c92862cbcd4 100644 --- a/homeassistant/components/adax/translations/pl.json +++ b/homeassistant/components/adax/translations/pl.json @@ -7,8 +7,7 @@ "invalid_auth": "Niepoprawne uwierzytelnienie" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_auth": "Niepoprawne uwierzytelnienie" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Identyfikator konta", - "connection_type": "Wybierz typ po\u0142\u0105czenia", - "host": "Nazwa hosta lub adres IP", - "password": "Has\u0142o" + "connection_type": "Wybierz typ po\u0142\u0105czenia" }, "description": "Wybierz typ po\u0142\u0105czenia. \"Lokalny\" wymaga grzejnik\u00f3w z bluetooth." } diff --git a/homeassistant/components/adax/translations/pt-BR.json b/homeassistant/components/adax/translations/pt-BR.json index 09498932c20..f7cb68b30a9 100644 --- a/homeassistant/components/adax/translations/pt-BR.json +++ b/homeassistant/components/adax/translations/pt-BR.json @@ -7,8 +7,7 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "error": { - "cannot_connect": "Falha ao conectar", - "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" + "cannot_connect": "Falha ao conectar" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID da conta", - "connection_type": "Selecione o tipo de conex\u00e3o", - "host": "Nome do host", - "password": "Senha" + "connection_type": "Selecione o tipo de conex\u00e3o" }, "description": "Selecione o tipo de conex\u00e3o. Local requer aquecedores com bluetooth" } diff --git a/homeassistant/components/adax/translations/ru.json b/homeassistant/components/adax/translations/ru.json index 27596e86485..bb2d7c856e3 100644 --- a/homeassistant/components/adax/translations/ru.json +++ b/homeassistant/components/adax/translations/ru.json @@ -7,8 +7,7 @@ "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "ID \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438", - "connection_type": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + "connection_type": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. \u0414\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u043e\u0433\u0440\u0435\u0432\u0430\u0442\u0435\u043b\u0438 \u0441 Bluetooth." } diff --git a/homeassistant/components/adax/translations/sk.json b/homeassistant/components/adax/translations/sk.json index 8bacfc145e2..af9d83e3703 100644 --- a/homeassistant/components/adax/translations/sk.json +++ b/homeassistant/components/adax/translations/sk.json @@ -2,16 +2,6 @@ "config": { "abort": { "invalid_auth": "Neplatn\u00e9 overenie" - }, - "error": { - "invalid_auth": "Neplatn\u00e9 overenie" - }, - "step": { - "user": { - "data": { - "password": "Heslo" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/adax/translations/tr.json b/homeassistant/components/adax/translations/tr.json index bd7ef0fb6e9..e1fec21628f 100644 --- a/homeassistant/components/adax/translations/tr.json +++ b/homeassistant/components/adax/translations/tr.json @@ -7,8 +7,7 @@ "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama" + "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "Hesap Kimli\u011fi", - "connection_type": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in", - "host": "Sunucu", - "password": "Parola" + "connection_type": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in" }, "description": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in. Yerel Bluetooth'lu \u0131s\u0131t\u0131c\u0131lar gerektirir" } diff --git a/homeassistant/components/adax/translations/zh-Hans.json b/homeassistant/components/adax/translations/zh-Hans.json index 86a404bcbf6..161112a3500 100644 --- a/homeassistant/components/adax/translations/zh-Hans.json +++ b/homeassistant/components/adax/translations/zh-Hans.json @@ -21,8 +21,7 @@ }, "user": { "data": { - "connection_type": "\u9009\u62e9\u8fde\u63a5\u7c7b\u578b", - "password": "\u5bc6\u7801" + "connection_type": "\u9009\u62e9\u8fde\u63a5\u7c7b\u578b" } } } diff --git a/homeassistant/components/adax/translations/zh-Hant.json b/homeassistant/components/adax/translations/zh-Hant.json index 92018be07da..cd99affe40e 100644 --- a/homeassistant/components/adax/translations/zh-Hant.json +++ b/homeassistant/components/adax/translations/zh-Hant.json @@ -7,8 +7,7 @@ "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548" + "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { "cloud": { @@ -26,10 +25,7 @@ }, "user": { "data": { - "account_id": "\u5e33\u865f ID", - "connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225", - "host": "\u4e3b\u6a5f\u7aef", - "password": "\u5bc6\u78bc" + "connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225" }, "description": "\u9078\u64c7\u9023\u7dda\u985e\u5225\u3002\u672c\u5730\u7aef\u5c07\u9700\u8981\u5177\u5099\u85cd\u82bd\u52a0\u71b1\u5668" } diff --git a/homeassistant/components/aemet/translations/ca.json b/homeassistant/components/aemet/translations/ca.json index 7dab53fc049..91641dd4329 100644 --- a/homeassistant/components/aemet/translations/ca.json +++ b/homeassistant/components/aemet/translations/ca.json @@ -14,8 +14,7 @@ "longitude": "Longitud", "name": "Nom de la integraci\u00f3" }, - "description": "Per generar la clau API, v\u00e9s a https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Per generar la clau API, v\u00e9s a https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/de.json b/homeassistant/components/aemet/translations/de.json index 25f374f2e43..cc8b8ead259 100644 --- a/homeassistant/components/aemet/translations/de.json +++ b/homeassistant/components/aemet/translations/de.json @@ -14,8 +14,7 @@ "longitude": "L\u00e4ngengrad", "name": "Name der Integration" }, - "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/el.json b/homeassistant/components/aemet/translations/el.json index 757fd41be70..a1aa6fa7ba2 100644 --- a/homeassistant/components/aemet/translations/el.json +++ b/homeassistant/components/aemet/translations/el.json @@ -14,8 +14,7 @@ "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 AEMET OpenData. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 AEMET OpenData. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/en.json b/homeassistant/components/aemet/translations/en.json index 7c10d1dc676..f732768c219 100644 --- a/homeassistant/components/aemet/translations/en.json +++ b/homeassistant/components/aemet/translations/en.json @@ -14,8 +14,7 @@ "longitude": "Longitude", "name": "Name of the integration" }, - "description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/es-419.json b/homeassistant/components/aemet/translations/es-419.json index 3a02d682f34..cc047365ee4 100644 --- a/homeassistant/components/aemet/translations/es-419.json +++ b/homeassistant/components/aemet/translations/es-419.json @@ -5,8 +5,7 @@ "data": { "name": "Nombre de la integraci\u00f3n" }, - "description": "Configure la integraci\u00f3n de AEMET OpenData. Para generar la clave API vaya a https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Configure la integraci\u00f3n de AEMET OpenData. Para generar la clave API vaya a https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/es.json b/homeassistant/components/aemet/translations/es.json index 558d87886d9..b46f7720789 100644 --- a/homeassistant/components/aemet/translations/es.json +++ b/homeassistant/components/aemet/translations/es.json @@ -14,8 +14,7 @@ "longitude": "Longitud", "name": "Nombre de la integraci\u00f3n" }, - "description": "Configurar la integraci\u00f3n de AEMET OpenData. Para generar la clave API, ve a https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Configurar la integraci\u00f3n de AEMET OpenData. Para generar la clave API, ve a https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/et.json b/homeassistant/components/aemet/translations/et.json index 04292b3d912..e1781241227 100644 --- a/homeassistant/components/aemet/translations/et.json +++ b/homeassistant/components/aemet/translations/et.json @@ -14,8 +14,7 @@ "longitude": "Pikkuskraad", "name": "Sidumise nimi" }, - "description": "API-v\u00f5tme loomiseks mine aadressile https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "API-v\u00f5tme loomiseks mine aadressile https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/fr.json b/homeassistant/components/aemet/translations/fr.json index bddfd2507bf..6c9d1be2cf2 100644 --- a/homeassistant/components/aemet/translations/fr.json +++ b/homeassistant/components/aemet/translations/fr.json @@ -14,8 +14,7 @@ "longitude": "Longitude", "name": "Nom de l'int\u00e9gration" }, - "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/hu.json b/homeassistant/components/aemet/translations/hu.json index 2f2984b1c90..2fa9636ccec 100644 --- a/homeassistant/components/aemet/translations/hu.json +++ b/homeassistant/components/aemet/translations/hu.json @@ -14,8 +14,7 @@ "longitude": "Hossz\u00fas\u00e1g", "name": "Az integr\u00e1ci\u00f3 neve" }, - "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://opendata.aemet.es/centrodedescargas/altaUsuario webhelyet", - "title": "AEMET OpenData" + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://opendata.aemet.es/centrodedescargas/altaUsuario webhelyet" } } }, diff --git a/homeassistant/components/aemet/translations/id.json b/homeassistant/components/aemet/translations/id.json index 62239172aae..22e7213b4a3 100644 --- a/homeassistant/components/aemet/translations/id.json +++ b/homeassistant/components/aemet/translations/id.json @@ -14,8 +14,7 @@ "longitude": "Bujur", "name": "Nama integrasi" }, - "description": "Untuk membuat kunci API, buka https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Untuk membuat kunci API, buka https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/it.json b/homeassistant/components/aemet/translations/it.json index 4b3f6f2251b..ac62822d3c9 100644 --- a/homeassistant/components/aemet/translations/it.json +++ b/homeassistant/components/aemet/translations/it.json @@ -14,8 +14,7 @@ "longitude": "Logitudine", "name": "Nome dell'integrazione" }, - "description": "Per generare la chiave API, vai su https://opendata.aemet.es/centrodescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Per generare la chiave API, vai su https://opendata.aemet.es/centrodescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/ja.json b/homeassistant/components/aemet/translations/ja.json index a3a0904c223..fd79c699cee 100644 --- a/homeassistant/components/aemet/translations/ja.json +++ b/homeassistant/components/aemet/translations/ja.json @@ -14,8 +14,7 @@ "longitude": "\u7d4c\u5ea6", "name": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u540d\u524d" }, - "description": "AEMET OpenData\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://opendata.aemet.es/centrodedescargas/altaUsuario \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "AEMET OpenData" + "description": "AEMET OpenData\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://opendata.aemet.es/centrodedescargas/altaUsuario \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" } } }, diff --git a/homeassistant/components/aemet/translations/ko.json b/homeassistant/components/aemet/translations/ko.json index 95c11b018fe..23ca054f5be 100644 --- a/homeassistant/components/aemet/translations/ko.json +++ b/homeassistant/components/aemet/translations/ko.json @@ -14,8 +14,7 @@ "longitude": "\uacbd\ub3c4", "name": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc774\ub984" }, - "description": "AEMET OpenData \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://opendata.aemet.es/centrodedescargas/altaUsuario \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694", - "title": "AEMET OpenData" + "description": "AEMET OpenData \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://opendata.aemet.es/centrodedescargas/altaUsuario \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694" } } } diff --git a/homeassistant/components/aemet/translations/lb.json b/homeassistant/components/aemet/translations/lb.json index 8e83c0e86d3..a2152f0b4d0 100644 --- a/homeassistant/components/aemet/translations/lb.json +++ b/homeassistant/components/aemet/translations/lb.json @@ -7,8 +7,7 @@ "latitude": "L\u00e4ngegrad", "longitude": "Breedegrad", "name": "Numm vun der Integratioun" - }, - "title": "AEMET OpenData" + } } } } diff --git a/homeassistant/components/aemet/translations/nl.json b/homeassistant/components/aemet/translations/nl.json index abf590e5a36..5479d206fa9 100644 --- a/homeassistant/components/aemet/translations/nl.json +++ b/homeassistant/components/aemet/translations/nl.json @@ -14,8 +14,7 @@ "longitude": "Lengtegraad", "name": "Naam van de integratie" }, - "description": "Om een API sleutel te genereren ga naar https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Om een API sleutel te genereren ga naar https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/no.json b/homeassistant/components/aemet/translations/no.json index a6076844122..86eed98a153 100644 --- a/homeassistant/components/aemet/translations/no.json +++ b/homeassistant/components/aemet/translations/no.json @@ -14,8 +14,7 @@ "longitude": "Lengdegrad", "name": "Navnet p\u00e5 integrasjonen" }, - "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/pl.json b/homeassistant/components/aemet/translations/pl.json index f1021585140..5c83e00b974 100644 --- a/homeassistant/components/aemet/translations/pl.json +++ b/homeassistant/components/aemet/translations/pl.json @@ -14,8 +14,7 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa integracji" }, - "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/pt-BR.json b/homeassistant/components/aemet/translations/pt-BR.json index 7c5972dccd8..7cd1c704f99 100644 --- a/homeassistant/components/aemet/translations/pt-BR.json +++ b/homeassistant/components/aemet/translations/pt-BR.json @@ -14,8 +14,7 @@ "longitude": "Longitude", "name": "Nome da integra\u00e7\u00e3o" }, - "description": "Para gerar a chave API acesse https://opendata.aemet.es/centrodedescargas/altaUsuario", - "title": "AEMET OpenData" + "description": "Para gerar a chave API acesse https://opendata.aemet.es/centrodedescargas/altaUsuario" } } }, diff --git a/homeassistant/components/aemet/translations/ru.json b/homeassistant/components/aemet/translations/ru.json index 9d8496cbb2d..142b05d3a52 100644 --- a/homeassistant/components/aemet/translations/ru.json +++ b/homeassistant/components/aemet/translations/ru.json @@ -14,8 +14,7 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://opendata.aemet.es/centrodedescargas/altaUsuario.", - "title": "AEMET OpenData" + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://opendata.aemet.es/centrodedescargas/altaUsuario." } } }, diff --git a/homeassistant/components/aemet/translations/tr.json b/homeassistant/components/aemet/translations/tr.json index e1e3cae01ff..b835bf6ddc3 100644 --- a/homeassistant/components/aemet/translations/tr.json +++ b/homeassistant/components/aemet/translations/tr.json @@ -14,8 +14,7 @@ "longitude": "Boylam", "name": "Entegrasyonun ad\u0131" }, - "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://opendata.aemet.es/centrodedescargas/altaUsuario adresine gidin.", - "title": "AEMET OpenData" + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://opendata.aemet.es/centrodedescargas/altaUsuario adresine gidin." } } }, diff --git a/homeassistant/components/aemet/translations/zh-Hant.json b/homeassistant/components/aemet/translations/zh-Hant.json index 4d77ca17505..63e6d69d6cd 100644 --- a/homeassistant/components/aemet/translations/zh-Hant.json +++ b/homeassistant/components/aemet/translations/zh-Hant.json @@ -14,8 +14,7 @@ "longitude": "\u7d93\u5ea6", "name": "\u6574\u5408\u540d\u7a31" }, - "description": "\u8acb\u81f3 https://opendata.aemet.es/centrodedescargas/altaUsuario \u4ee5\u7522\u751f API \u91d1\u9470", - "title": "AEMET OpenData" + "description": "\u8acb\u81f3 https://opendata.aemet.es/centrodedescargas/altaUsuario \u4ee5\u7522\u751f API \u91d1\u9470" } } }, diff --git a/homeassistant/components/airly/translations/bg.json b/homeassistant/components/airly/translations/bg.json index 955cc8c1ac4..03decc0ac69 100644 --- a/homeassistant/components/airly/translations/bg.json +++ b/homeassistant/components/airly/translations/bg.json @@ -15,8 +15,7 @@ "longitude": "\u0414\u044a\u043b\u0436\u0438\u043d\u0430", "name": "\u0418\u043c\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0430 \u0432\u044a\u0437\u0434\u0443\u0445\u0430 Airly \u0417\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 \u043a\u043b\u044e\u0447 \u0437\u0430 API, \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 https://developer.airly.eu/register", - "title": "Airly" + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0430 \u0432\u044a\u0437\u0434\u0443\u0445\u0430 Airly \u0417\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 \u043a\u043b\u044e\u0447 \u0437\u0430 API, \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 https://developer.airly.eu/register" } } } diff --git a/homeassistant/components/airly/translations/ca.json b/homeassistant/components/airly/translations/ca.json index ac956185c14..36d7aaf8190 100644 --- a/homeassistant/components/airly/translations/ca.json +++ b/homeassistant/components/airly/translations/ca.json @@ -15,8 +15,7 @@ "longitude": "Longitud", "name": "Nom" }, - "description": "Per generar la clau API, v\u00e9s a https://developer.airly.eu/register", - "title": "Airly" + "description": "Per generar la clau API, v\u00e9s a https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/cs.json b/homeassistant/components/airly/translations/cs.json index 8b35399bcb0..3b4f98f53f8 100644 --- a/homeassistant/components/airly/translations/cs.json +++ b/homeassistant/components/airly/translations/cs.json @@ -15,8 +15,7 @@ "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka", "name": "Jm\u00e9no" }, - "description": "Nastavte integraci kvality vzduchu Airly. Chcete-li vygenerovat kl\u00ed\u010d rozhran\u00ed API, p\u0159ejd\u011bte na https://developer.airly.eu/register", - "title": "Airly" + "description": "Nastavte integraci kvality vzduchu Airly. Chcete-li vygenerovat kl\u00ed\u010d rozhran\u00ed API, p\u0159ejd\u011bte na https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/da.json b/homeassistant/components/airly/translations/da.json index 53b05a38992..2b1cbe643b8 100644 --- a/homeassistant/components/airly/translations/da.json +++ b/homeassistant/components/airly/translations/da.json @@ -14,8 +14,7 @@ "longitude": "L\u00e6ngdegrad", "name": "Integrationens navn" }, - "description": "Konfigurer Airly luftkvalitetsintegration. For at generere API-n\u00f8gle, g\u00e5 til https://developer.airly.eu/register", - "title": "Airly" + "description": "Konfigurer Airly luftkvalitetsintegration. For at generere API-n\u00f8gle, g\u00e5 til https://developer.airly.eu/register" } } } diff --git a/homeassistant/components/airly/translations/de.json b/homeassistant/components/airly/translations/de.json index 216a8c56c6d..bc37bbc139c 100644 --- a/homeassistant/components/airly/translations/de.json +++ b/homeassistant/components/airly/translations/de.json @@ -15,8 +15,7 @@ "longitude": "L\u00e4ngengrad", "name": "Name" }, - "description": "Um einen API-Schl\u00fcssel zu generieren, besuche https://developer.airly.eu/register", - "title": "Airly" + "description": "Um einen API-Schl\u00fcssel zu generieren, besuche https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/el.json b/homeassistant/components/airly/translations/el.json index adb944a4259..e8d61743523 100644 --- a/homeassistant/components/airly/translations/el.json +++ b/homeassistant/components/airly/translations/el.json @@ -15,8 +15,7 @@ "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b1\u03ad\u03c1\u03b1 Airly. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://developer.airly.eu/register", - "title": "Airly" + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b1\u03ad\u03c1\u03b1 Airly. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/en.json b/homeassistant/components/airly/translations/en.json index 249905eff2a..1dee608b1da 100644 --- a/homeassistant/components/airly/translations/en.json +++ b/homeassistant/components/airly/translations/en.json @@ -15,8 +15,7 @@ "longitude": "Longitude", "name": "Name" }, - "description": "To generate API key go to https://developer.airly.eu/register", - "title": "Airly" + "description": "To generate API key go to https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/es-419.json b/homeassistant/components/airly/translations/es-419.json index c7d1e388d67..31149641d11 100644 --- a/homeassistant/components/airly/translations/es-419.json +++ b/homeassistant/components/airly/translations/es-419.json @@ -14,8 +14,7 @@ "longitude": "Longitud", "name": "Nombre de la integraci\u00f3n" }, - "description": "Configure la integraci\u00f3n de la calidad del aire de Airly. Para generar la clave API, vaya a https://developer.airly.eu/register", - "title": "Airly" + "description": "Configure la integraci\u00f3n de la calidad del aire de Airly. Para generar la clave API, vaya a https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/es.json b/homeassistant/components/airly/translations/es.json index a96a2f62293..9d0b254713b 100644 --- a/homeassistant/components/airly/translations/es.json +++ b/homeassistant/components/airly/translations/es.json @@ -15,8 +15,7 @@ "longitude": "Longitud", "name": "Nombre" }, - "description": "Establecer la integraci\u00f3n de la calidad del aire de Airly. Para generar la clave de la API vaya a https://developer.airly.eu/register", - "title": "Airly" + "description": "Establecer la integraci\u00f3n de la calidad del aire de Airly. Para generar la clave de la API vaya a https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/et.json b/homeassistant/components/airly/translations/et.json index 6a55262ac6c..ea17ec01ea6 100644 --- a/homeassistant/components/airly/translations/et.json +++ b/homeassistant/components/airly/translations/et.json @@ -15,8 +15,7 @@ "longitude": "Pikkuskraad", "name": "Nimi" }, - "description": "API-v\u00f5tme loomiseks mine aadressile https://developer.airly.eu/register", - "title": "" + "description": "API-v\u00f5tme loomiseks mine aadressile https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/fr.json b/homeassistant/components/airly/translations/fr.json index 17a7248f7ad..e9134879c00 100644 --- a/homeassistant/components/airly/translations/fr.json +++ b/homeassistant/components/airly/translations/fr.json @@ -15,8 +15,7 @@ "longitude": "Longitude", "name": "Nom" }, - "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.airly.eu/register", - "title": "Airly" + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/he.json b/homeassistant/components/airly/translations/he.json index 8faa6ac2092..edbf648397f 100644 --- a/homeassistant/components/airly/translations/he.json +++ b/homeassistant/components/airly/translations/he.json @@ -13,8 +13,7 @@ "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "name": "\u05e9\u05dd" - }, - "title": "\u05d0\u05d5\u05d5\u05e8\u05d9\u05e8\u05d9" + } } } } diff --git a/homeassistant/components/airly/translations/hu.json b/homeassistant/components/airly/translations/hu.json index 04d667f4079..6955c2456cc 100644 --- a/homeassistant/components/airly/translations/hu.json +++ b/homeassistant/components/airly/translations/hu.json @@ -15,8 +15,7 @@ "longitude": "Hossz\u00fas\u00e1g", "name": "Elnevez\u00e9s" }, - "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://developer.airly.eu/register webhelyet", - "title": "Airly" + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://developer.airly.eu/register webhelyet" } } }, diff --git a/homeassistant/components/airly/translations/id.json b/homeassistant/components/airly/translations/id.json index bc97db4135a..7e2d7e3930e 100644 --- a/homeassistant/components/airly/translations/id.json +++ b/homeassistant/components/airly/translations/id.json @@ -15,8 +15,7 @@ "longitude": "Bujur", "name": "Nama" }, - "description": "Untuk membuat kunci API, buka https://developer.airly.eu/register", - "title": "Airly" + "description": "Untuk membuat kunci API, buka https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/it.json b/homeassistant/components/airly/translations/it.json index b96d5c8a74c..d57023faeca 100644 --- a/homeassistant/components/airly/translations/it.json +++ b/homeassistant/components/airly/translations/it.json @@ -15,8 +15,7 @@ "longitude": "Logitudine", "name": "Nome" }, - "description": "Per generare la chiave API, vai su https://developer.airly.eu/register", - "title": "Airly" + "description": "Per generare la chiave API, vai su https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/ja.json b/homeassistant/components/airly/translations/ja.json index 6835412e6db..6ff5e2216a6 100644 --- a/homeassistant/components/airly/translations/ja.json +++ b/homeassistant/components/airly/translations/ja.json @@ -15,8 +15,7 @@ "longitude": "\u7d4c\u5ea6", "name": "\u540d\u524d" }, - "description": "Airly\u306e\u7a7a\u6c17\u54c1\u8cea\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://developer.airly.eu/register \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "Airly" + "description": "Airly\u306e\u7a7a\u6c17\u54c1\u8cea\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://developer.airly.eu/register \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" } } }, diff --git a/homeassistant/components/airly/translations/ko.json b/homeassistant/components/airly/translations/ko.json index 1f9db4a592e..d66bb93dc39 100644 --- a/homeassistant/components/airly/translations/ko.json +++ b/homeassistant/components/airly/translations/ko.json @@ -15,8 +15,7 @@ "longitude": "\uacbd\ub3c4", "name": "\uc774\ub984" }, - "description": "Airly \uacf5\uae30 \ud488\uc9c8 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://developer.airly.eu/register \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694", - "title": "Airly" + "description": "Airly \uacf5\uae30 \ud488\uc9c8 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://developer.airly.eu/register \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694" } } }, diff --git a/homeassistant/components/airly/translations/lb.json b/homeassistant/components/airly/translations/lb.json index dd24ee3066f..32832f52266 100644 --- a/homeassistant/components/airly/translations/lb.json +++ b/homeassistant/components/airly/translations/lb.json @@ -15,8 +15,7 @@ "longitude": "L\u00e4ngegrad", "name": "Numm" }, - "description": "Airly Loft Qualit\u00e9it Integratioun ariichten. Fir een API Schl\u00ebssel z'erstelle gitt op https://developer.airly.eu/register", - "title": "Airly" + "description": "Airly Loft Qualit\u00e9it Integratioun ariichten. Fir een API Schl\u00ebssel z'erstelle gitt op https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/nl.json b/homeassistant/components/airly/translations/nl.json index 70fbca2074f..7cfa0c5776f 100644 --- a/homeassistant/components/airly/translations/nl.json +++ b/homeassistant/components/airly/translations/nl.json @@ -15,8 +15,7 @@ "longitude": "Lengtegraad", "name": "Naam" }, - "description": "Om een API sleutel te genereren ga naar https://developer.airly.eu/register", - "title": "Airly" + "description": "Om een API sleutel te genereren ga naar https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/nn.json b/homeassistant/components/airly/translations/nn.json deleted file mode 100644 index 9cf2b5d70fb..00000000000 --- a/homeassistant/components/airly/translations/nn.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Airly" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/airly/translations/no.json b/homeassistant/components/airly/translations/no.json index 015e95af8f2..f4f87d6d887 100644 --- a/homeassistant/components/airly/translations/no.json +++ b/homeassistant/components/airly/translations/no.json @@ -15,8 +15,7 @@ "longitude": "Lengdegrad", "name": "Navn" }, - "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://developer.airly.eu/register", - "title": "" + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/pl.json b/homeassistant/components/airly/translations/pl.json index dd44f2bf635..72d35ec97a7 100644 --- a/homeassistant/components/airly/translations/pl.json +++ b/homeassistant/components/airly/translations/pl.json @@ -15,8 +15,7 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa" }, - "description": "By wygenerowa\u0107 klucz API, przejd\u017a na stron\u0119 https://developer.airly.eu/register", - "title": "Airly" + "description": "By wygenerowa\u0107 klucz API, przejd\u017a na stron\u0119 https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/pt-BR.json b/homeassistant/components/airly/translations/pt-BR.json index 0e9913b559c..fe19bc8b3a7 100644 --- a/homeassistant/components/airly/translations/pt-BR.json +++ b/homeassistant/components/airly/translations/pt-BR.json @@ -15,8 +15,7 @@ "longitude": "Longitude", "name": "Nome" }, - "description": "Para gerar a chave de API, acesse https://developer.airly.eu/register", - "title": "Airly" + "description": "Para gerar a chave de API, acesse https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/pt.json b/homeassistant/components/airly/translations/pt.json index 6ebb22b565a..aff2cb2399b 100644 --- a/homeassistant/components/airly/translations/pt.json +++ b/homeassistant/components/airly/translations/pt.json @@ -13,8 +13,7 @@ "latitude": "Latitude", "longitude": "Longitude", "name": "Nome" - }, - "title": "" + } } } } diff --git a/homeassistant/components/airly/translations/ru.json b/homeassistant/components/airly/translations/ru.json index 80c6a98813c..4b159f1e50d 100644 --- a/homeassistant/components/airly/translations/ru.json +++ b/homeassistant/components/airly/translations/ru.json @@ -15,8 +15,7 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://developer.airly.eu/register.", - "title": "Airly" + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://developer.airly.eu/register." } } }, diff --git a/homeassistant/components/airly/translations/sl.json b/homeassistant/components/airly/translations/sl.json index e1c89501394..2debfc1c0ef 100644 --- a/homeassistant/components/airly/translations/sl.json +++ b/homeassistant/components/airly/translations/sl.json @@ -14,8 +14,7 @@ "longitude": "Zemljepisna dol\u017eina", "name": "Ime integracije" }, - "description": "Nastavite Airly integracijo za kakovost zraka. \u010ce \u017eelite ustvariti API klju\u010d pojdite na https://developer.airly.eu/register", - "title": "Airly" + "description": "Nastavite Airly integracijo za kakovost zraka. \u010ce \u017eelite ustvariti API klju\u010d pojdite na https://developer.airly.eu/register" } } }, diff --git a/homeassistant/components/airly/translations/sv.json b/homeassistant/components/airly/translations/sv.json index e47cb5cd328..d182115230b 100644 --- a/homeassistant/components/airly/translations/sv.json +++ b/homeassistant/components/airly/translations/sv.json @@ -14,8 +14,7 @@ "longitude": "Longitud", "name": "Integrationens namn" }, - "description": "Konfigurera integration av luftkvalitet. F\u00f6r att skapa API-nyckel, g\u00e5 till https://developer.airly.eu/register", - "title": "Airly" + "description": "Konfigurera integration av luftkvalitet. F\u00f6r att skapa API-nyckel, g\u00e5 till https://developer.airly.eu/register" } } } diff --git a/homeassistant/components/airly/translations/tr.json b/homeassistant/components/airly/translations/tr.json index 7f5643be2d0..989179b73d8 100644 --- a/homeassistant/components/airly/translations/tr.json +++ b/homeassistant/components/airly/translations/tr.json @@ -15,8 +15,7 @@ "longitude": "Boylam", "name": "Ad" }, - "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.airly.eu/register adresine gidin.", - "title": "Airly" + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.airly.eu/register adresine gidin." } } }, diff --git a/homeassistant/components/airly/translations/uk.json b/homeassistant/components/airly/translations/uk.json index 51bcf5195df..08d92e2282d 100644 --- a/homeassistant/components/airly/translations/uk.json +++ b/homeassistant/components/airly/translations/uk.json @@ -15,8 +15,7 @@ "longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430" }, - "description": "\u0406\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044f \u0441\u0435\u0440\u0432\u0456\u0441\u0443 \u0437 \u0430\u043d\u0430\u043b\u0456\u0437\u0443 \u044f\u043a\u043e\u0441\u0442\u0456 \u043f\u043e\u0432\u0456\u0442\u0440\u044f Airly. \u0429\u043e\u0431 \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u0437\u0430 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f\u043c https://developer.airly.eu/register.", - "title": "Airly" + "description": "\u0406\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044f \u0441\u0435\u0440\u0432\u0456\u0441\u0443 \u0437 \u0430\u043d\u0430\u043b\u0456\u0437\u0443 \u044f\u043a\u043e\u0441\u0442\u0456 \u043f\u043e\u0432\u0456\u0442\u0440\u044f Airly. \u0429\u043e\u0431 \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u0437\u0430 \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f\u043c https://developer.airly.eu/register." } } }, diff --git a/homeassistant/components/airly/translations/zh-Hant.json b/homeassistant/components/airly/translations/zh-Hant.json index 65f0bf8cde5..9973269facd 100644 --- a/homeassistant/components/airly/translations/zh-Hant.json +++ b/homeassistant/components/airly/translations/zh-Hant.json @@ -15,8 +15,7 @@ "longitude": "\u7d93\u5ea6", "name": "\u540d\u7a31" }, - "description": "\u8acb\u81f3 https://developer.airly.eu/register \u4ee5\u7522\u751f API \u91d1\u9470", - "title": "Airly" + "description": "\u8acb\u81f3 https://developer.airly.eu/register \u4ee5\u7522\u751f API \u91d1\u9470" } } }, diff --git a/homeassistant/components/airnow/translations/ca.json b/homeassistant/components/airnow/translations/ca.json index 9a2b0aa9afc..a4f8de82284 100644 --- a/homeassistant/components/airnow/translations/ca.json +++ b/homeassistant/components/airnow/translations/ca.json @@ -17,10 +17,8 @@ "longitude": "Longitud", "radius": "Radi de l'estaci\u00f3 (milles; opcional)" }, - "description": "Per generar la clau API, v\u00e9s a https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Per generar la clau API, v\u00e9s a https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/cs.json b/homeassistant/components/airnow/translations/cs.json index d978e44c70a..5268257905e 100644 --- a/homeassistant/components/airnow/translations/cs.json +++ b/homeassistant/components/airnow/translations/cs.json @@ -14,10 +14,8 @@ "api_key": "Kl\u00ed\u010d API", "latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka", "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka" - }, - "title": "AirNow" + } } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/de.json b/homeassistant/components/airnow/translations/de.json index 3c207a39cce..c42da3491c1 100644 --- a/homeassistant/components/airnow/translations/de.json +++ b/homeassistant/components/airnow/translations/de.json @@ -17,10 +17,8 @@ "longitude": "L\u00e4ngengrad", "radius": "Stationsradius (Meilen; optional)" }, - "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/el.json b/homeassistant/components/airnow/translations/el.json index caaaafc9e9a..c8e97c5a925 100644 --- a/homeassistant/components/airnow/translations/el.json +++ b/homeassistant/components/airnow/translations/el.json @@ -17,10 +17,8 @@ "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", "radius": "\u0391\u03ba\u03c4\u03af\u03bd\u03b1 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd (\u03bc\u03af\u03bb\u03b9\u03b1, \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 AirNow \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03ad\u03c1\u03b1. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 AirNow \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03bf\u03c5 \u03b1\u03ad\u03c1\u03b1. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/en.json b/homeassistant/components/airnow/translations/en.json index cb3081284b4..07915652847 100644 --- a/homeassistant/components/airnow/translations/en.json +++ b/homeassistant/components/airnow/translations/en.json @@ -17,10 +17,8 @@ "longitude": "Longitude", "radius": "Station Radius (miles; optional)" }, - "description": "To generate API key go to https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "To generate API key go to https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/es-419.json b/homeassistant/components/airnow/translations/es-419.json index 015d7242ef1..5f49ebdb143 100644 --- a/homeassistant/components/airnow/translations/es-419.json +++ b/homeassistant/components/airnow/translations/es-419.json @@ -8,10 +8,8 @@ "data": { "radius": "Radio de la estaci\u00f3n (millas; opcional)" }, - "description": "Configure la integraci\u00f3n de la calidad del aire de AirNow. Para generar la clave de API, vaya a https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Configure la integraci\u00f3n de la calidad del aire de AirNow. Para generar la clave de API, vaya a https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/es.json b/homeassistant/components/airnow/translations/es.json index d6a228a6e27..b5b915910c5 100644 --- a/homeassistant/components/airnow/translations/es.json +++ b/homeassistant/components/airnow/translations/es.json @@ -17,10 +17,8 @@ "longitude": "Longitud", "radius": "Radio de la estaci\u00f3n (millas; opcional)" }, - "description": "Configurar la integraci\u00f3n de calidad del aire de AirNow. Para generar una clave API, ve a https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Configurar la integraci\u00f3n de calidad del aire de AirNow. Para generar una clave API, ve a https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/et.json b/homeassistant/components/airnow/translations/et.json index 3bdf8661d36..1d6744e69ae 100644 --- a/homeassistant/components/airnow/translations/et.json +++ b/homeassistant/components/airnow/translations/et.json @@ -17,10 +17,8 @@ "longitude": "Pikkuskraad", "radius": "Jaama raadius (miilid; valikuline)" }, - "description": "API-v\u00f5tme loomiseks mine aadressile https://docs.airnowapi.org/account/request/", - "title": "" + "description": "API-v\u00f5tme loomiseks mine aadressile https://docs.airnowapi.org/account/request/" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/fr.json b/homeassistant/components/airnow/translations/fr.json index 2da7427e1be..ce45573f9bf 100644 --- a/homeassistant/components/airnow/translations/fr.json +++ b/homeassistant/components/airnow/translations/fr.json @@ -17,10 +17,8 @@ "longitude": "Longitude", "radius": "Rayon d'action de la station (en miles, facultatif)" }, - "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/hu.json b/homeassistant/components/airnow/translations/hu.json index 52bf8cd63a0..a0d86014768 100644 --- a/homeassistant/components/airnow/translations/hu.json +++ b/homeassistant/components/airnow/translations/hu.json @@ -17,10 +17,8 @@ "longitude": "Hossz\u00fas\u00e1g", "radius": "\u00c1llom\u00e1s sugara (m\u00e9rf\u00f6ld; opcion\u00e1lis)" }, - "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://docs.airnowapi.org/account/request/ webhelyet", - "title": "AirNow" + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://docs.airnowapi.org/account/request/ webhelyet" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/id.json b/homeassistant/components/airnow/translations/id.json index 4d4ac987320..b0beea88cb1 100644 --- a/homeassistant/components/airnow/translations/id.json +++ b/homeassistant/components/airnow/translations/id.json @@ -17,10 +17,8 @@ "longitude": "Bujur", "radius": "Radius Stasiun (mil; opsional)" }, - "description": "Untuk membuat kunci API buka https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Untuk membuat kunci API buka https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/it.json b/homeassistant/components/airnow/translations/it.json index 77715910c75..d35e4659c90 100644 --- a/homeassistant/components/airnow/translations/it.json +++ b/homeassistant/components/airnow/translations/it.json @@ -17,10 +17,8 @@ "longitude": "Logitudine", "radius": "Raggio stazione (miglia; opzionale)" }, - "description": "Per generare la chiave API vai su https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Per generare la chiave API vai su https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/ja.json b/homeassistant/components/airnow/translations/ja.json index d535f7fc044..17a1b14e164 100644 --- a/homeassistant/components/airnow/translations/ja.json +++ b/homeassistant/components/airnow/translations/ja.json @@ -17,10 +17,8 @@ "longitude": "\u7d4c\u5ea6", "radius": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u534a\u5f84(\u30de\u30a4\u30eb; \u30aa\u30d7\u30b7\u30e7\u30f3)" }, - "description": "AirNow\u306e\u7a7a\u6c17\u54c1\u8cea\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://docs.airnowapi.org/account/request/ \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "AirNow" + "description": "AirNow\u306e\u7a7a\u6c17\u54c1\u8cea\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://docs.airnowapi.org/account/request/ \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/ko.json b/homeassistant/components/airnow/translations/ko.json index adfbf0be8ed..0364b7642a6 100644 --- a/homeassistant/components/airnow/translations/ko.json +++ b/homeassistant/components/airnow/translations/ko.json @@ -17,10 +17,8 @@ "longitude": "\uacbd\ub3c4", "radius": "\uce21\uc815 \uc2a4\ud14c\uc774\uc158 \ubc18\uacbd (\ub9c8\uc77c; \uc120\ud0dd \uc0ac\ud56d)" }, - "description": "AirNow \uacf5\uae30 \ud488\uc9c8 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://docs.airnowapi.org/account/request \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694", - "title": "AirNow" + "description": "AirNow \uacf5\uae30 \ud488\uc9c8 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://docs.airnowapi.org/account/request \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/lb.json b/homeassistant/components/airnow/translations/lb.json index a62bd0bf478..90ce6b3a4b2 100644 --- a/homeassistant/components/airnow/translations/lb.json +++ b/homeassistant/components/airnow/translations/lb.json @@ -15,10 +15,8 @@ "api_key": "API Schl\u00ebssel", "latitude": "L\u00e4ngegrad", "longitude": "Breedegrag" - }, - "title": "AirNow" + } } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/nl.json b/homeassistant/components/airnow/translations/nl.json index 5e5e33bf93d..0ba6ce2a965 100644 --- a/homeassistant/components/airnow/translations/nl.json +++ b/homeassistant/components/airnow/translations/nl.json @@ -17,10 +17,8 @@ "longitude": "Lengtegraad", "radius": "Stationsradius (mijl; optioneel)" }, - "description": "Om een API sleutel te genereren ga naar https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Om een API sleutel te genereren ga naar https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/no.json b/homeassistant/components/airnow/translations/no.json index b56b7096e2b..ee2bf44672c 100644 --- a/homeassistant/components/airnow/translations/no.json +++ b/homeassistant/components/airnow/translations/no.json @@ -17,10 +17,8 @@ "longitude": "Lengdegrad", "radius": "Stasjonsradius (miles; valgfritt)" }, - "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://docs.airnowapi.org/account/request/", - "title": "" + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://docs.airnowapi.org/account/request/" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/pl.json b/homeassistant/components/airnow/translations/pl.json index f29a48dc47b..bbf31af597a 100644 --- a/homeassistant/components/airnow/translations/pl.json +++ b/homeassistant/components/airnow/translations/pl.json @@ -17,10 +17,8 @@ "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "radius": "Promie\u0144 od stacji (w milach; opcjonalnie)" }, - "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/pt-BR.json b/homeassistant/components/airnow/translations/pt-BR.json index c1bda0098cd..a3450355e0c 100644 --- a/homeassistant/components/airnow/translations/pt-BR.json +++ b/homeassistant/components/airnow/translations/pt-BR.json @@ -17,10 +17,8 @@ "longitude": "Longitude", "radius": "Raio da Esta\u00e7\u00e3o (milhas; opcional)" }, - "description": "Para gerar a chave de API, acesse https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "Para gerar a chave de API, acesse https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/pt.json b/homeassistant/components/airnow/translations/pt.json index 7b9af9e8a23..f846047c307 100644 --- a/homeassistant/components/airnow/translations/pt.json +++ b/homeassistant/components/airnow/translations/pt.json @@ -14,10 +14,8 @@ "api_key": "Chave da API", "latitude": "Latitude", "longitude": "Longitude" - }, - "title": "" + } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/ru.json b/homeassistant/components/airnow/translations/ru.json index c5a9137f4c7..fb8516e6529 100644 --- a/homeassistant/components/airnow/translations/ru.json +++ b/homeassistant/components/airnow/translations/ru.json @@ -17,10 +17,8 @@ "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "radius": "\u0420\u0430\u0434\u0438\u0443\u0441 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 (\u0432 \u043c\u0438\u043b\u044f\u0445; \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" }, - "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://docs.airnowapi.org/account/request/.", - "title": "AirNow" + "description": "\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 https://docs.airnowapi.org/account/request/." } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/tr.json b/homeassistant/components/airnow/translations/tr.json index 1f80d7805da..25c3cf15d41 100644 --- a/homeassistant/components/airnow/translations/tr.json +++ b/homeassistant/components/airnow/translations/tr.json @@ -17,10 +17,8 @@ "longitude": "Boylam", "radius": "\u0130stasyon Yar\u0131\u00e7ap\u0131 (mil; iste\u011fe ba\u011fl\u0131)" }, - "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://docs.airnowapi.org/account/request/ adresine gidin.", - "title": "AirNow" + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://docs.airnowapi.org/account/request/ adresine gidin." } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/uk.json b/homeassistant/components/airnow/translations/uk.json index bb872123f54..db8c740234f 100644 --- a/homeassistant/components/airnow/translations/uk.json +++ b/homeassistant/components/airnow/translations/uk.json @@ -17,10 +17,8 @@ "longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430", "radius": "\u0420\u0430\u0434\u0456\u0443\u0441 \u0441\u0442\u0430\u043d\u0446\u0456\u0457 (\u043c\u0438\u043b\u0456; \u043d\u0435\u043e\u0431\u043e\u0432\u2019\u044f\u0437\u043a\u043e\u0432\u043e)" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e \u044f\u043a\u043e\u0441\u0442\u0456 \u043f\u043e\u0432\u0456\u0442\u0440\u044f AirNow. \u0429\u043e\u0431 \u0437\u0433\u0435\u043d\u0435\u0440\u0443\u0432\u0430\u0442\u0438 \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0443 https://docs.airnowapi.org/account/request/", - "title": "AirNow" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u044e \u044f\u043a\u043e\u0441\u0442\u0456 \u043f\u043e\u0432\u0456\u0442\u0440\u044f AirNow. \u0429\u043e\u0431 \u0437\u0433\u0435\u043d\u0435\u0440\u0443\u0432\u0430\u0442\u0438 \u043a\u043b\u044e\u0447 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0443 https://docs.airnowapi.org/account/request/" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/airnow/translations/zh-Hant.json b/homeassistant/components/airnow/translations/zh-Hant.json index bb18a5d8975..e257e43a3e6 100644 --- a/homeassistant/components/airnow/translations/zh-Hant.json +++ b/homeassistant/components/airnow/translations/zh-Hant.json @@ -17,10 +17,8 @@ "longitude": "\u7d93\u5ea6", "radius": "\u89c0\u6e2c\u7ad9\u534a\u5f91\uff08\u82f1\u91cc\uff1b\u9078\u9805\uff09" }, - "description": "\u8acb\u81f3 https://docs.airnowapi.org/account/request/ \u4ee5\u7522\u751f API \u91d1\u9470", - "title": "AirNow" + "description": "\u8acb\u81f3 https://docs.airnowapi.org/account/request/ \u4ee5\u7522\u751f API \u91d1\u9470" } } - }, - "title": "AirNow" + } } \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/cs.json b/homeassistant/components/aladdin_connect/translations/cs.json new file mode 100644 index 00000000000..a3144ba2f55 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/cs.json @@ -0,0 +1,22 @@ +{ + "config": { + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "Heslo" + }, + "title": "Znovu ov\u011b\u0159it integraci" + }, + "user": { + "data": { + "password": "Heslo", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/aladdin_connect/translations/he.json b/homeassistant/components/aladdin_connect/translations/he.json new file mode 100644 index 00000000000..a26c20aa4c7 --- /dev/null +++ b/homeassistant/components/aladdin_connect/translations/he.json @@ -0,0 +1,26 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" + }, + "step": { + "reauth_confirm": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4" + }, + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + }, + "user": { + "data": { + "password": "\u05e1\u05d9\u05e1\u05de\u05d4", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/amberelectric/translations/bg.json b/homeassistant/components/amberelectric/translations/bg.json index 5cfcc05b133..6f035fae4e6 100644 --- a/homeassistant/components/amberelectric/translations/bg.json +++ b/homeassistant/components/amberelectric/translations/bg.json @@ -1,12 +1,8 @@ { "config": { "step": { - "site": { - "title": "Amber Electric" - }, "user": { - "description": "\u041e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}, \u0437\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447", - "title": "Amber Electric" + "description": "\u041e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}, \u0437\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447" } } } diff --git a/homeassistant/components/amberelectric/translations/ca.json b/homeassistant/components/amberelectric/translations/ca.json index cf9bca64df6..208c7fb9f7c 100644 --- a/homeassistant/components/amberelectric/translations/ca.json +++ b/homeassistant/components/amberelectric/translations/ca.json @@ -6,16 +6,14 @@ "site_name": "Nom del lloc", "site_nmi": "NMI del lloc" }, - "description": "Selecciona l'NMI del lloc que vulguis afegir", - "title": "Amber Electric" + "description": "Selecciona l'NMI del lloc que vulguis afegir" }, "user": { "data": { "api_token": "Token d'API", "site_id": "ID del lloc" }, - "description": "Ves a {api_url} per generar una clau API", - "title": "Amber Electric" + "description": "Ves a {api_url} per generar una clau API" } } } diff --git a/homeassistant/components/amberelectric/translations/de.json b/homeassistant/components/amberelectric/translations/de.json index 2143795f479..34ce233fed8 100644 --- a/homeassistant/components/amberelectric/translations/de.json +++ b/homeassistant/components/amberelectric/translations/de.json @@ -6,16 +6,14 @@ "site_name": "Name des Standorts", "site_nmi": "Standort NMI" }, - "description": "W\u00e4hle die NMI des Standorts, den du hinzuf\u00fcgen m\u00f6chtest", - "title": "Amber Electric" + "description": "W\u00e4hle die NMI des Standorts, den du hinzuf\u00fcgen m\u00f6chtest" }, "user": { "data": { "api_token": "API-Token", "site_id": "Site-ID" }, - "description": "Gehe zu {api_url}, um einen API-Schl\u00fcssel zu generieren", - "title": "Amber Electric" + "description": "Gehe zu {api_url}, um einen API-Schl\u00fcssel zu generieren" } } } diff --git a/homeassistant/components/amberelectric/translations/el.json b/homeassistant/components/amberelectric/translations/el.json index b6e939ea466..3dc8fc9dd78 100644 --- a/homeassistant/components/amberelectric/translations/el.json +++ b/homeassistant/components/amberelectric/translations/el.json @@ -6,16 +6,14 @@ "site_name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2", "site_nmi": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 NMI" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf NMI \u03c4\u03b7\u03c2 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5.", - "title": "Amber Electric" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf NMI \u03c4\u03b7\u03c2 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5." }, "user": { "data": { "api_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API", "site_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2" }, - "description": "\u039c\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf {api_url} \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", - "title": "Amber Electric" + "description": "\u039c\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf {api_url} \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API" } } } diff --git a/homeassistant/components/amberelectric/translations/en.json b/homeassistant/components/amberelectric/translations/en.json index 60c7caae456..b4d30925aa7 100644 --- a/homeassistant/components/amberelectric/translations/en.json +++ b/homeassistant/components/amberelectric/translations/en.json @@ -6,16 +6,14 @@ "site_name": "Site Name", "site_nmi": "Site NMI" }, - "description": "Select the NMI of the site you would like to add", - "title": "Amber Electric" + "description": "Select the NMI of the site you would like to add" }, "user": { "data": { "api_token": "API Token", "site_id": "Site ID" }, - "description": "Go to {api_url} to generate an API key", - "title": "Amber Electric" + "description": "Go to {api_url} to generate an API key" } } } diff --git a/homeassistant/components/amberelectric/translations/es.json b/homeassistant/components/amberelectric/translations/es.json index d4b09e69930..18af819d5d1 100644 --- a/homeassistant/components/amberelectric/translations/es.json +++ b/homeassistant/components/amberelectric/translations/es.json @@ -6,16 +6,14 @@ "site_name": "Nombre del sitio", "site_nmi": "Sitio NMI" }, - "description": "Seleccione el NMI del sitio que le gustar\u00eda agregar", - "title": "Amber Electric" + "description": "Seleccione el NMI del sitio que le gustar\u00eda agregar" }, "user": { "data": { "api_token": "API Token", "site_id": "ID del sitio" }, - "description": "Ve a {api_url} para generar una clave API", - "title": "Amber Electric" + "description": "Ve a {api_url} para generar una clave API" } } } diff --git a/homeassistant/components/amberelectric/translations/et.json b/homeassistant/components/amberelectric/translations/et.json index 05a7e6c6dc2..e48f6f2a749 100644 --- a/homeassistant/components/amberelectric/translations/et.json +++ b/homeassistant/components/amberelectric/translations/et.json @@ -6,16 +6,14 @@ "site_name": "Saidi nimi", "site_nmi": "Saidi NMI" }, - "description": "Vali lisatava saidi NMI", - "title": "Amber Electric" + "description": "Vali lisatava saidi NMI" }, "user": { "data": { "api_token": "API v\u00f5ti", "site_id": "Saidi ID" }, - "description": "API-v\u00f5tme saamiseks ava {api_url}.", - "title": "Amber Electric" + "description": "API-v\u00f5tme saamiseks ava {api_url}." } } } diff --git a/homeassistant/components/amberelectric/translations/fr.json b/homeassistant/components/amberelectric/translations/fr.json index 487ceff33f3..a5b1164924c 100644 --- a/homeassistant/components/amberelectric/translations/fr.json +++ b/homeassistant/components/amberelectric/translations/fr.json @@ -6,16 +6,14 @@ "site_name": "Nom du site", "site_nmi": "Site NMI" }, - "description": "S\u00e9lectionnez le NMI du site que vous souhaitez ajouter", - "title": "Amber Electrique" + "description": "S\u00e9lectionnez le NMI du site que vous souhaitez ajouter" }, "user": { "data": { "api_token": "Jeton d'API", "site_id": "ID du site" }, - "description": "Acc\u00e9dez \u00e0 {api_url} pour g\u00e9n\u00e9rer une cl\u00e9 API", - "title": "Amber Electrique" + "description": "Acc\u00e9dez \u00e0 {api_url} pour g\u00e9n\u00e9rer une cl\u00e9 API" } } } diff --git a/homeassistant/components/amberelectric/translations/hu.json b/homeassistant/components/amberelectric/translations/hu.json index 9811f5a5f8f..2d361e1e76f 100644 --- a/homeassistant/components/amberelectric/translations/hu.json +++ b/homeassistant/components/amberelectric/translations/hu.json @@ -6,16 +6,14 @@ "site_name": "Hely neve", "site_nmi": "Hely NMI" }, - "description": "V\u00e1lassza ki a hozz\u00e1adni k\u00edv\u00e1nt hely NMI-j\u00e9t.", - "title": "Amber Electric" + "description": "V\u00e1lassza ki a hozz\u00e1adni k\u00edv\u00e1nt hely NMI-j\u00e9t." }, "user": { "data": { "api_token": "API Token", "site_id": "Hely ID" }, - "description": "API-kulcs gener\u00e1l\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}", - "title": "Amber Electric" + "description": "API-kulcs gener\u00e1l\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}" } } } diff --git a/homeassistant/components/amberelectric/translations/id.json b/homeassistant/components/amberelectric/translations/id.json index 4920ee7b177..a88fca7c520 100644 --- a/homeassistant/components/amberelectric/translations/id.json +++ b/homeassistant/components/amberelectric/translations/id.json @@ -6,16 +6,14 @@ "site_name": "Nama Situs", "site_nmi": "Situs NMI" }, - "description": "Pilih NMI dari situs yang ingin ditambahkan", - "title": "Amber Electric" + "description": "Pilih NMI dari situs yang ingin ditambahkan" }, "user": { "data": { "api_token": "Token API", "site_id": "ID Site" }, - "description": "Buka {api_url} untuk membuat kunci API", - "title": "Amber Electric" + "description": "Buka {api_url} untuk membuat kunci API" } } } diff --git a/homeassistant/components/amberelectric/translations/it.json b/homeassistant/components/amberelectric/translations/it.json index 5b061561954..f181f549fff 100644 --- a/homeassistant/components/amberelectric/translations/it.json +++ b/homeassistant/components/amberelectric/translations/it.json @@ -6,16 +6,14 @@ "site_name": "Nome del sito", "site_nmi": "Sito NMI" }, - "description": "Seleziona l'NMI del sito che desideri aggiungere", - "title": "Amber Electric" + "description": "Seleziona l'NMI del sito che desideri aggiungere" }, "user": { "data": { "api_token": "Token API", "site_id": "ID sito" }, - "description": "Vai su {api_url} per generare una chiave API", - "title": "Amber Electric" + "description": "Vai su {api_url} per generare una chiave API" } } } diff --git a/homeassistant/components/amberelectric/translations/ja.json b/homeassistant/components/amberelectric/translations/ja.json index 1fc5f6c58c1..0c061b26112 100644 --- a/homeassistant/components/amberelectric/translations/ja.json +++ b/homeassistant/components/amberelectric/translations/ja.json @@ -6,16 +6,14 @@ "site_name": "\u30b5\u30a4\u30c8\u540d", "site_nmi": "\u30b5\u30a4\u30c8NMI" }, - "description": "\u8ffd\u52a0\u3057\u305f\u3044\u30b5\u30a4\u30c8\u306eNMI\u3092\u9078\u629e", - "title": "Amber Electric" + "description": "\u8ffd\u52a0\u3057\u305f\u3044\u30b5\u30a4\u30c8\u306eNMI\u3092\u9078\u629e" }, "user": { "data": { "api_token": "API\u30c8\u30fc\u30af\u30f3", "site_id": "\u30b5\u30a4\u30c8ID" }, - "description": "API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u305f\u3081\u306b {api_url} \u306b\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3059", - "title": "Amber Electric" + "description": "API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u305f\u3081\u306b {api_url} \u306b\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3059" } } } diff --git a/homeassistant/components/amberelectric/translations/nl.json b/homeassistant/components/amberelectric/translations/nl.json index a874c12f283..263e94962a0 100644 --- a/homeassistant/components/amberelectric/translations/nl.json +++ b/homeassistant/components/amberelectric/translations/nl.json @@ -6,16 +6,14 @@ "site_name": "Sitenaam", "site_nmi": "Site NMI" }, - "description": "Selecteer de NMI van de site die u wilt toevoegen", - "title": "Amber Electric" + "description": "Selecteer de NMI van de site die u wilt toevoegen" }, "user": { "data": { "api_token": "API Token", "site_id": "Site ID" }, - "description": "Ga naar {api_url} om een API sleutel aan te maken", - "title": "Amber Electric" + "description": "Ga naar {api_url} om een API sleutel aan te maken" } } } diff --git a/homeassistant/components/amberelectric/translations/no.json b/homeassistant/components/amberelectric/translations/no.json index 90d4bd930b9..74df9323ce5 100644 --- a/homeassistant/components/amberelectric/translations/no.json +++ b/homeassistant/components/amberelectric/translations/no.json @@ -6,16 +6,14 @@ "site_name": "Side navn", "site_nmi": "Nettsted NMI" }, - "description": "Velg NMI for nettstedet du vil legge til", - "title": "Amber Electric" + "description": "Velg NMI for nettstedet du vil legge til" }, "user": { "data": { "api_token": "API-token", "site_id": "Nettsted -ID" }, - "description": "G\u00e5 til {api_url} \u00e5 generere en API -n\u00f8kkel", - "title": "Amber Electric" + "description": "G\u00e5 til {api_url} \u00e5 generere en API -n\u00f8kkel" } } } diff --git a/homeassistant/components/amberelectric/translations/pl.json b/homeassistant/components/amberelectric/translations/pl.json index 1e9b66e3c3c..2810149273f 100644 --- a/homeassistant/components/amberelectric/translations/pl.json +++ b/homeassistant/components/amberelectric/translations/pl.json @@ -6,16 +6,14 @@ "site_name": "Nazwa obiektu", "site_nmi": "Numer identyfikacyjny (NMI) obiektu" }, - "description": "Wybierz NMI obiektu, kt\u00f3ry chcesz doda\u0107", - "title": "Amber Electric" + "description": "Wybierz NMI obiektu, kt\u00f3ry chcesz doda\u0107" }, "user": { "data": { "api_token": "Token API", "site_id": "Identyfikator obiektu" }, - "description": "Przejd\u017a do {api_url}, aby wygenerowa\u0107 klucz API", - "title": "Amber Electric" + "description": "Przejd\u017a do {api_url}, aby wygenerowa\u0107 klucz API" } } } diff --git a/homeassistant/components/amberelectric/translations/pt-BR.json b/homeassistant/components/amberelectric/translations/pt-BR.json index 6e8eb4d0ac8..35541dbf290 100644 --- a/homeassistant/components/amberelectric/translations/pt-BR.json +++ b/homeassistant/components/amberelectric/translations/pt-BR.json @@ -6,16 +6,14 @@ "site_name": "Nome do site", "site_nmi": "Site NMI" }, - "description": "Selecione o NMI do site que voc\u00ea gostaria de adicionar", - "title": "\u00c2mbar el\u00e9trico" + "description": "Selecione o NMI do site que voc\u00ea gostaria de adicionar" }, "user": { "data": { "api_token": "Token de API", "site_id": "ID do site" }, - "description": "V\u00e1 para {api_url} para gerar uma chave de API", - "title": "\u00c2mbar el\u00e9trico" + "description": "V\u00e1 para {api_url} para gerar uma chave de API" } } } diff --git a/homeassistant/components/amberelectric/translations/ru.json b/homeassistant/components/amberelectric/translations/ru.json index 4b8caee72ee..793f8bcae9d 100644 --- a/homeassistant/components/amberelectric/translations/ru.json +++ b/homeassistant/components/amberelectric/translations/ru.json @@ -6,16 +6,14 @@ "site_name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0447\u0430\u0441\u0442\u043a\u0430", "site_nmi": "NMI \u0443\u0447\u0430\u0441\u0442\u043a\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 NMI \u0443\u0447\u0430\u0441\u0442\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c", - "title": "Amber Electric" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 NMI \u0443\u0447\u0430\u0441\u0442\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c" }, "user": { "data": { "api_token": "\u0422\u043e\u043a\u0435\u043d API", "site_id": "ID \u0443\u0447\u0430\u0441\u0442\u043a\u0430" }, - "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 {api_url} \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API.", - "title": "Amber Electric" + "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 {api_url} \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447 API." } } } diff --git a/homeassistant/components/amberelectric/translations/tr.json b/homeassistant/components/amberelectric/translations/tr.json index 274a347e578..393b2cf08ee 100644 --- a/homeassistant/components/amberelectric/translations/tr.json +++ b/homeassistant/components/amberelectric/translations/tr.json @@ -6,16 +6,14 @@ "site_name": "Site Ad\u0131", "site_nmi": "Site NMI" }, - "description": "Eklemek istedi\u011finiz sitenin NMI'sini se\u00e7in", - "title": "Amber Electric" + "description": "Eklemek istedi\u011finiz sitenin NMI'sini se\u00e7in" }, "user": { "data": { "api_token": "API Anahtar\u0131", "site_id": "Site Kimli\u011fi" }, - "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in {api_url} konumuna gidin", - "title": "Amber Electric" + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in {api_url} konumuna gidin" } } } diff --git a/homeassistant/components/amberelectric/translations/zh-Hant.json b/homeassistant/components/amberelectric/translations/zh-Hant.json index c2cbef2778b..42b671d3530 100644 --- a/homeassistant/components/amberelectric/translations/zh-Hant.json +++ b/homeassistant/components/amberelectric/translations/zh-Hant.json @@ -6,16 +6,14 @@ "site_name": "\u4f4d\u5740\u540d\u7a31", "site_nmi": "\u4f4d\u5740 NMI" }, - "description": "\u9078\u64c7\u6240\u8981\u65b0\u589e\u7684\u4f4d\u5740 NMI", - "title": "Amber Electric" + "description": "\u9078\u64c7\u6240\u8981\u65b0\u589e\u7684\u4f4d\u5740 NMI" }, "user": { "data": { "api_token": "API \u6b0a\u6756", "site_id": "\u4f4d\u5740 ID" }, - "description": "\u9023\u7dda\u81f3 {api_url} \u4ee5\u7522\u751f API \u91d1\u9470", - "title": "Amber Electric" + "description": "\u9023\u7dda\u81f3 {api_url} \u4ee5\u7522\u751f API \u91d1\u9470" } } } diff --git a/homeassistant/components/androidtv/translations/bg.json b/homeassistant/components/androidtv/translations/bg.json index 9dd3bde62ad..f912f17d257 100644 --- a/homeassistant/components/androidtv/translations/bg.json +++ b/homeassistant/components/androidtv/translations/bg.json @@ -13,8 +13,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442" - }, - "title": "Android TV" + } } } } diff --git a/homeassistant/components/androidtv/translations/ca.json b/homeassistant/components/androidtv/translations/ca.json index 3fccdc45771..d640a250fb0 100644 --- a/homeassistant/components/androidtv/translations/ca.json +++ b/homeassistant/components/androidtv/translations/ca.json @@ -20,9 +20,7 @@ "device_class": "Tipus de dispositiu", "host": "Amfitri\u00f3", "port": "Port" - }, - "description": "Estableix els par\u00e0metres necessaris per connectar al teu dispositiu Android TV", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configura les regles de detecci\u00f3 d'estat", "turn_off_command": "Comanda d'apagada de shell ADB (deixa-ho buit per utilitzar la predeterminada)", "turn_on_command": "Comanda d'engegada de shell ADB (deixa-ho buit per utilitzar la predeterminada)" - }, - "title": "Opcions d'Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/de.json b/homeassistant/components/androidtv/translations/de.json index e69e9ac2183..55a3b75d76c 100644 --- a/homeassistant/components/androidtv/translations/de.json +++ b/homeassistant/components/androidtv/translations/de.json @@ -20,9 +20,7 @@ "device_class": "Der Typ des Ger\u00e4ts", "host": "Host", "port": "Port" - }, - "description": "Stelle die erforderlichen Parameter f\u00fcr die Verbindung mit deinem Android TV-Ger\u00e4t ein", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Regeln zur Statuserkennung konfigurieren", "turn_off_command": "ADB-Shell-Abschaltbefehl (f\u00fcr Standard leer lassen)", "turn_on_command": "ADB-Shell-Einschaltbefehl (f\u00fcr Standard leer lassen)" - }, - "title": "Android TV-Optionen" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/el.json b/homeassistant/components/androidtv/translations/el.json index 67db15ff170..a831586fc41 100644 --- a/homeassistant/components/androidtv/translations/el.json +++ b/homeassistant/components/androidtv/translations/el.json @@ -20,9 +20,7 @@ "device_class": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "port": "\u0398\u03cd\u03c1\u03b1" - }, - "description": "\u039f\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b5\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03bf\u03c5\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c3\u03b1\u03c2 Android TV", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03b1\u03bd\u03cc\u03bd\u03c9\u03bd \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", "turn_off_command": "\u0395\u03bd\u03c4\u03bf\u03bb\u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03ba\u03b5\u03bb\u03cd\u03c6\u03bf\u03c5\u03c2 ADB (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae)", "turn_on_command": "\u0395\u03bd\u03c4\u03bf\u03bb\u03ae \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03ba\u03b5\u03bb\u03cd\u03c6\u03bf\u03c5\u03c2 ADB (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae)" - }, - "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/en.json b/homeassistant/components/androidtv/translations/en.json index f3232b4b229..638ba7fbcd5 100644 --- a/homeassistant/components/androidtv/translations/en.json +++ b/homeassistant/components/androidtv/translations/en.json @@ -20,9 +20,7 @@ "device_class": "The type of device", "host": "Host", "port": "Port" - }, - "description": "Set required parameters to connect to your Android TV device", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configure state detection rules", "turn_off_command": "ADB shell turn off command (leave empty for default)", "turn_on_command": "ADB shell turn on command (leave empty for default)" - }, - "title": "Android TV Options" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/es-419.json b/homeassistant/components/androidtv/translations/es-419.json index 0e97b8c1265..d8f04b2e7c4 100644 --- a/homeassistant/components/androidtv/translations/es-419.json +++ b/homeassistant/components/androidtv/translations/es-419.json @@ -7,9 +7,7 @@ "adb_server_port": "Puerto del servidor ADB", "adbkey": "Ruta a su archivo de clave ADB (d\u00e9jelo en blanco para generarlo autom\u00e1ticamente)", "device_class": "El tipo de dispositivo" - }, - "description": "Establezca los par\u00e1metros requeridos para conectarse a su dispositivo Android TV", - "title": "Android TV" + } } } }, @@ -36,8 +34,7 @@ "state_detection_rules": "Configurar reglas de detecci\u00f3n de estado", "turn_off_command": "Comando de apagado de shell ADB (d\u00e9jelo vac\u00edo por defecto)", "turn_on_command": "Comando de activaci\u00f3n de shell ADB (d\u00e9jelo vac\u00edo por defecto)" - }, - "title": "Opciones de Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/es.json b/homeassistant/components/androidtv/translations/es.json index 333910a2b64..74c7484257c 100644 --- a/homeassistant/components/androidtv/translations/es.json +++ b/homeassistant/components/androidtv/translations/es.json @@ -20,9 +20,7 @@ "device_class": "Tipo de dispositivo", "host": "Host", "port": "Puerto" - }, - "description": "Establezca los par\u00e1metros necesarios para conectarse a su Android TV", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configurar reglas de detecci\u00f3n de estado", "turn_off_command": "Comando de apagado del shell de ADB (dejar vac\u00edo por defecto)", "turn_on_command": "Comando de activaci\u00f3n del shell ADB (dejar vac\u00edo por defecto)" - }, - "title": "Opciones de Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/et.json b/homeassistant/components/androidtv/translations/et.json index 292b01fa849..1704f450482 100644 --- a/homeassistant/components/androidtv/translations/et.json +++ b/homeassistant/components/androidtv/translations/et.json @@ -20,9 +20,7 @@ "device_class": "Seadme t\u00fc\u00fcp", "host": "Host", "port": "Port" - }, - "description": "Seadista Android TV seadmega \u00fchenduse loomiseks vajalikud parameetrid", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "M\u00e4\u00e4ra oleku tuvastamise reeglid", "turn_off_command": "ADB shell turn off k\u00e4sk (vaikimisi j\u00e4ta t\u00fchjaks)", "turn_on_command": "ADB shell turn on k\u00e4sk (vaikimisi j\u00e4ta t\u00fchjaks)" - }, - "title": "Android TV suvandid" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/fr.json b/homeassistant/components/androidtv/translations/fr.json index ea79fddb98a..76124671f2c 100644 --- a/homeassistant/components/androidtv/translations/fr.json +++ b/homeassistant/components/androidtv/translations/fr.json @@ -20,9 +20,7 @@ "device_class": "Le type d'appareil", "host": "H\u00f4te", "port": "Port" - }, - "description": "D\u00e9finissez les param\u00e8tres requis pour vous connecter \u00e0 votre appareil Android TV", - "title": "T\u00e9l\u00e9vision Android" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configurer les r\u00e8gles de d\u00e9tection d'\u00e9tat", "turn_off_command": "Commande de d\u00e9sactivation du shell ADB (laisser vide par d\u00e9faut)", "turn_on_command": "Commande d'activation du shell ADB (laisser vide par d\u00e9faut)" - }, - "title": "Options de t\u00e9l\u00e9vision Android" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/he.json b/homeassistant/components/androidtv/translations/he.json index 81870f85c75..557e868298f 100644 --- a/homeassistant/components/androidtv/translations/he.json +++ b/homeassistant/components/androidtv/translations/he.json @@ -20,9 +20,7 @@ "device_class": "\u05e1\u05d5\u05d2 \u05d4\u05d4\u05ea\u05e7\u05df", "host": "\u05de\u05d0\u05e8\u05d7", "port": "\u05e4\u05ea\u05d7\u05d4" - }, - "description": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05d4\u05e4\u05e8\u05de\u05d8\u05e8\u05d9\u05dd \u05d4\u05e0\u05d3\u05e8\u05e9\u05d9\u05dd \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d4\u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3 \u05e9\u05dc\u05da", - "title": "\u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u05e7\u05d1\u05d9\u05e2\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05db\u05dc\u05dc\u05d9 \u05d6\u05d9\u05d4\u05d5\u05d9 \u05de\u05e6\u05d1\u05d9\u05dd", "turn_off_command": "\u05d4\u05e4\u05e7\u05d5\u05d3\u05d4 \u05db\u05d9\u05d1\u05d5\u05d9 \u05de\u05e2\u05d8\u05e4\u05ea ADB (\u05d4\u05e9\u05d0\u05e8 \u05e8\u05d9\u05e7\u05d4 \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc)", "turn_on_command": "\u05d4\u05e4\u05e7\u05d5\u05d3\u05d4 \u05d4\u05e4\u05e2\u05dc \u05de\u05e2\u05d8\u05e4\u05ea ADB (\u05d4\u05e9\u05d0\u05e8 \u05e8\u05d9\u05e7\u05d4 \u05db\u05d1\u05e8\u05d9\u05e8\u05ea \u05de\u05d7\u05d3\u05dc)" - }, - "title": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/hu.json b/homeassistant/components/androidtv/translations/hu.json index 3b75153a351..98c82a9779b 100644 --- a/homeassistant/components/androidtv/translations/hu.json +++ b/homeassistant/components/androidtv/translations/hu.json @@ -20,9 +20,7 @@ "device_class": "Az eszk\u00f6z t\u00edpusa", "host": "C\u00edm", "port": "Port" - }, - "description": "Az Android TV k\u00e9sz\u00fcl\u00e9khez val\u00f3 csatlakoz\u00e1shoz sz\u00fcks\u00e9ges param\u00e9terek be\u00e1ll\u00edt\u00e1sa", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u00c1llapotfelismer\u00e9si szab\u00e1lyok konfigur\u00e1l\u00e1sa", "turn_off_command": "ADB shell kikapcsol\u00e1si parancs (alap\u00e9rtelmez\u00e9s szerint hagyja \u00fcresen)", "turn_on_command": "ADB shell bekapcsol\u00e1si parancs (alap\u00e9rtelmez\u00e9s szerint hagyja \u00fcresen)" - }, - "title": "Android TV be\u00e1ll\u00edt\u00e1sok" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/id.json b/homeassistant/components/androidtv/translations/id.json index 2b754860c5d..2c81a6b2b96 100644 --- a/homeassistant/components/androidtv/translations/id.json +++ b/homeassistant/components/androidtv/translations/id.json @@ -20,9 +20,7 @@ "device_class": "Jenis perangkat", "host": "Host", "port": "Port" - }, - "description": "Setel parameter yang diperlukan untuk terhubung ke perangkat Android TV Anda", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Konfigurasikan aturan deteksi status", "turn_off_command": "Perintah mematikan shell ADB (kosongkan untuk default)", "turn_on_command": "Perintah nyalakan shell ADB (kosongkan untuk default)" - }, - "title": "Opsi Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/it.json b/homeassistant/components/androidtv/translations/it.json index ce6d3c92bca..91f56c48853 100644 --- a/homeassistant/components/androidtv/translations/it.json +++ b/homeassistant/components/androidtv/translations/it.json @@ -20,9 +20,7 @@ "device_class": "Il tipo di dispositivo", "host": "Host", "port": "Porta" - }, - "description": "Imposta i parametri richiesti per connetterti al tuo dispositivo Android TV", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configura le regole di rilevamento dello stato", "turn_off_command": "Comando di disattivazione della shell ADB (lascia vuoto per impostazione predefinita)", "turn_on_command": "Comando di attivazione della shell ADB (lascia vuoto per impostazione predefinita)" - }, - "title": "Opzioni Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/ja.json b/homeassistant/components/androidtv/translations/ja.json index 365a6078366..26b83a2643a 100644 --- a/homeassistant/components/androidtv/translations/ja.json +++ b/homeassistant/components/androidtv/translations/ja.json @@ -20,9 +20,7 @@ "device_class": "\u30c7\u30d0\u30a4\u30b9\u306e\u7a2e\u985e", "host": "\u30db\u30b9\u30c8", "port": "\u30dd\u30fc\u30c8" - }, - "description": "Android TV\u30c7\u30d0\u30a4\u30b9\u306b\u63a5\u7d9a\u3059\u308b\u305f\u3081\u306b\u5fc5\u8981\u306a\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3092\u8a2d\u5b9a\u3057\u307e\u3059", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u72b6\u614b\u691c\u51fa\u30eb\u30fc\u30eb\u3092\u8a2d\u5b9a", "turn_off_command": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306eturn_off\u30b3\u30de\u30f3\u30c9\u3092\u4e0a\u66f8\u304d\u3059\u308bADB\u30b7\u30a7\u30eb\u30b3\u30de\u30f3\u30c9", "turn_on_command": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306eturn_on\u30b3\u30de\u30f3\u30c9\u3092\u4e0a\u66f8\u304d\u3059\u308bADB\u30b7\u30a7\u30eb\u30b3\u30de\u30f3\u30c9" - }, - "title": "Android TV\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/nl.json b/homeassistant/components/androidtv/translations/nl.json index e8fdf44387c..40df125d384 100644 --- a/homeassistant/components/androidtv/translations/nl.json +++ b/homeassistant/components/androidtv/translations/nl.json @@ -20,9 +20,7 @@ "device_class": "Het type apparaat", "host": "Host", "port": "Poort" - }, - "description": "Stel de vereiste parameters in om verbinding te maken met uw Android TV-apparaat", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Regels voor statusdetectie van Android TV configureren", "turn_off_command": "ADB shell-uitschakelcommando(laat standaard leeg)", "turn_on_command": "ADB shell-inschakelcommando (laat standaard leeg)" - }, - "title": "Android TV-opties" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/no.json b/homeassistant/components/androidtv/translations/no.json index 92d74ad5e0b..707352476a2 100644 --- a/homeassistant/components/androidtv/translations/no.json +++ b/homeassistant/components/androidtv/translations/no.json @@ -20,9 +20,7 @@ "device_class": "Type enhet", "host": "Vert", "port": "Port" - }, - "description": "Angi n\u00f8dvendige parametere for \u00e5 koble til Android TV-enheten din", - "title": "" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Konfigurere regler for tilstandsgjenkjenning", "turn_off_command": "ADB shell sl\u00e5 av kommando (la st\u00e5 tomt som standard)", "turn_on_command": "ADB-skall sl\u00e5 p\u00e5 kommando (la st\u00e5 tomt som standard)" - }, - "title": "Android TV-alternativer" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/pl.json b/homeassistant/components/androidtv/translations/pl.json index 5a4f7e39130..2f8dd47ee23 100644 --- a/homeassistant/components/androidtv/translations/pl.json +++ b/homeassistant/components/androidtv/translations/pl.json @@ -20,9 +20,7 @@ "device_class": "Typ urz\u0105dzenia", "host": "Nazwa hosta lub adres IP", "port": "Port" - }, - "description": "Ustaw wymagane parametry, aby po\u0142\u0105czy\u0107 si\u0119 z Android TV", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Skonfiguruj regu\u0142y wykrywania stanu", "turn_off_command": "Polecenie wy\u0142\u0105czania z pow\u0142oki ADB (pozostaw puste dla warto\u015bci domy\u015blnej)", "turn_on_command": "Polecenie w\u0142\u0105czenia z pow\u0142oki ADB (pozostaw puste dla warto\u015bci domy\u015blnej)" - }, - "title": "Opcje Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/pt-BR.json b/homeassistant/components/androidtv/translations/pt-BR.json index 86e9a29dd12..496f7ea0013 100644 --- a/homeassistant/components/androidtv/translations/pt-BR.json +++ b/homeassistant/components/androidtv/translations/pt-BR.json @@ -20,9 +20,7 @@ "device_class": "O tipo de dispositivo", "host": "Nome do host", "port": "Porta" - }, - "description": "Defina os par\u00e2metros necess\u00e1rios para se conectar ao seu dispositivo Android TV", - "title": "AndroidTV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Configurar regras de detec\u00e7\u00e3o de estado", "turn_off_command": "Comando de desligamento do shell ADB (deixe vazio por padr\u00e3o)", "turn_on_command": "Comando de ativa\u00e7\u00e3o do shell ADB (deixe vazio por padr\u00e3o)" - }, - "title": "Op\u00e7\u00f5es de TV Android" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/ru.json b/homeassistant/components/androidtv/translations/ru.json index 0803a16c986..61eb431fbf4 100644 --- a/homeassistant/components/androidtv/translations/ru.json +++ b/homeassistant/components/androidtv/translations/ru.json @@ -20,9 +20,7 @@ "device_class": "\u0422\u0438\u043f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Android TV.", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439", "turn_off_command": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u0438 ADB \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c)", "turn_on_command": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u0438 ADB \u0434\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c)" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Android TV" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/tr.json b/homeassistant/components/androidtv/translations/tr.json index bb77c35ed1b..6e52e9b3200 100644 --- a/homeassistant/components/androidtv/translations/tr.json +++ b/homeassistant/components/androidtv/translations/tr.json @@ -20,9 +20,7 @@ "device_class": "Cihaz\u0131n t\u00fcr\u00fc", "host": "Sunucu", "port": "Port" - }, - "description": "Android TV cihaz\u0131n\u0131za ba\u011flanmak i\u00e7in gerekli parametreleri ayarlay\u0131n", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "Durum alg\u0131lama kurallar\u0131n\u0131 yap\u0131land\u0131r\u0131n", "turn_off_command": "ADB kabu\u011fu kapatma komutu (varsay\u0131lan olarak bo\u015f b\u0131rak\u0131n)", "turn_on_command": "ADB kabu\u011fu a\u00e7ma komutu (varsay\u0131lan olarak bo\u015f b\u0131rak\u0131n)" - }, - "title": "Android TV Se\u00e7enekleri" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/zh-Hans.json b/homeassistant/components/androidtv/translations/zh-Hans.json index b3aa722d8a5..03bdfd8851d 100644 --- a/homeassistant/components/androidtv/translations/zh-Hans.json +++ b/homeassistant/components/androidtv/translations/zh-Hans.json @@ -19,9 +19,7 @@ "device_class": "\u8bbe\u5907\u7c7b\u578b", "host": "\u4e3b\u673a", "port": "\u7aef\u53e3" - }, - "description": "\u8bf7\u586b\u5199\u4ee5\u4e0b\u53c2\u6570\uff0c\u8fde\u63a5\u5230 Android TV \u8bbe\u5907\u3002", - "title": "Android TV" + } } } }, @@ -48,8 +46,7 @@ "state_detection_rules": "\u914d\u7f6e\u72b6\u6001\u68c0\u6d4b\u89c4\u5219", "turn_off_command": "ADB shell \u5173\u673a\u547d\u4ee4\uff08\u7559\u7a7a\u4ee5\u4f7f\u7528\u9ed8\u8ba4\u503c\uff09", "turn_on_command": "ADB shell \u5f00\u673a\u547d\u4ee4\uff08\u7559\u7a7a\u4ee5\u4f7f\u7528\u9ed8\u8ba4\u503c\uff09" - }, - "title": "Android TV \u9009\u9879" + } }, "rules": { "data": { diff --git a/homeassistant/components/androidtv/translations/zh-Hant.json b/homeassistant/components/androidtv/translations/zh-Hant.json index 9e9d78f0629..3e572ea509d 100644 --- a/homeassistant/components/androidtv/translations/zh-Hant.json +++ b/homeassistant/components/androidtv/translations/zh-Hant.json @@ -20,9 +20,7 @@ "device_class": "\u88dd\u7f6e\u985e\u5225", "host": "\u4e3b\u6a5f\u7aef", "port": "\u901a\u8a0a\u57e0" - }, - "description": "\u8a2d\u5b9a\u9023\u7dda\u81f3 Android TV \u88dd\u7f6e\u6240\u9700\u53c3\u6578", - "title": "Android TV" + } } } }, @@ -49,8 +47,7 @@ "state_detection_rules": "\u8a2d\u5b9a\u72c0\u614b\u5075\u6e2c\u898f\u5247", "turn_off_command": "ADB shell turn off \u6307\u4ee4\uff08\u9810\u8a2d\u7a7a\u767d\uff09", "turn_on_command": "ADB shell turn on \u6307\u4ee4\uff08\u9810\u8a2d\u7a7a\u767d\uff09" - }, - "title": "Android TV \u9078\u9805" + } }, "rules": { "data": { diff --git a/homeassistant/components/apple_tv/translations/bg.json b/homeassistant/components/apple_tv/translations/bg.json index b1923cab650..cd0488141c4 100644 --- a/homeassistant/components/apple_tv/translations/bg.json +++ b/homeassistant/components/apple_tv/translations/bg.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "already_configured_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "ipv6_not_supported": "IPv6 \u043d\u0435 \u0441\u0435 \u043f\u043e\u0434\u0434\u044a\u0440\u0436\u0430.", "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e", @@ -36,6 +35,5 @@ } } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/ca.json b/homeassistant/components/apple_tv/translations/ca.json index c672733193a..ca0ca27bcfe 100644 --- a/homeassistant/components/apple_tv/translations/ca.json +++ b/homeassistant/components/apple_tv/translations/ca.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", - "already_configured_device": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "backoff": "En aquests moments el dispositiu no accepta sol\u00b7licituds de vinculaci\u00f3 (\u00e9s possible que hagis introdu\u00eft un codi PIN inv\u00e0lid massa vegades), torna-ho a provar m\u00e9s tard.", "device_did_not_pair": "No s'ha fet cap intent d'acabar el proc\u00e9s de vinculaci\u00f3 des del dispositiu.", "device_not_found": "No s'ha trobat el dispositiu durant el descobriment, prova de tornar-lo a afegir.", "inconsistent_device": "Els protocols esperats no s'han trobat durant el descobriment. Normalment aix\u00f2 indica un problema amb el DNS multicast (Zeroconf). Prova d'afegir el dispositiu de nou.", - "invalid_config": "La configuraci\u00f3 d'aquest dispositiu no est\u00e0 completa. Intenta'l tornar a afegir.", "ipv6_not_supported": "IPv6 no est\u00e0 suportat.", "no_devices_found": "No s'han trobat dispositius a la xarxa", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", @@ -19,7 +17,6 @@ "already_configured": "El dispositiu ja est\u00e0 configurat", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "no_devices_found": "No s'han trobat dispositius a la xarxa", - "no_usable_service": "S'ha trobat un dispositiu per\u00f2 no ha pogut identificar cap manera d'establir-hi una connexi\u00f3. Si continues veient aquest missatge, prova d'especificar-ne l'adre\u00e7a IP o reinicia l'Apple TV.", "unknown": "Error inesperat" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Configuraci\u00f3 dels par\u00e0metres generals del dispositiu" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/cs.json b/homeassistant/components/apple_tv/translations/cs.json index 27b9e1dfeee..b7c44b60f6b 100644 --- a/homeassistant/components/apple_tv/translations/cs.json +++ b/homeassistant/components/apple_tv/translations/cs.json @@ -2,9 +2,7 @@ "config": { "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", - "already_configured_device": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", - "invalid_config": "Nastaven\u00ed tohoto za\u0159\u00edzen\u00ed je ne\u00fapln\u00e9. Zkuste jej p\u0159idat znovu.", "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" @@ -57,6 +55,5 @@ "description": "Konfigurace obecn\u00fdch mo\u017enost\u00ed za\u0159\u00edzen\u00ed" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/de.json b/homeassistant/components/apple_tv/translations/de.json index 48149ce8394..27ecc3452f0 100644 --- a/homeassistant/components/apple_tv/translations/de.json +++ b/homeassistant/components/apple_tv/translations/de.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "already_configured_device": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "backoff": "Das Ger\u00e4t akzeptiert zur Zeit keine Kopplungsanfragen (Du hast m\u00f6glicherweise zu oft einen ung\u00fcltigen PIN-Code eingegeben), versuche es sp\u00e4ter erneut.", "device_did_not_pair": "Es wurde kein Versuch unternommen, den Kopplungsvorgang vom Ger\u00e4t aus abzuschlie\u00dfen.", "device_not_found": "Das Ger\u00e4t wurde bei der Erkennung nicht gefunden. Bitte versuche es erneut hinzuzuf\u00fcgen.", "inconsistent_device": "Die erwarteten Protokolle wurden bei der Erkennung nicht gefunden. Dies deutet normalerweise auf ein Problem mit Multicast-DNS (Zeroconf) hin. Bitte versuche das Ger\u00e4t erneut hinzuzuf\u00fcgen.", - "invalid_config": "Die Konfiguration f\u00fcr dieses Ger\u00e4t ist unvollst\u00e4ndig. Bitte versuche, es erneut hinzuzuf\u00fcgen.", "ipv6_not_supported": "IPv6 wird nicht unterst\u00fctzt.", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", @@ -19,7 +17,6 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert", "invalid_auth": "Ung\u00fcltige Authentifizierung", "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", - "no_usable_service": "Es wurde ein Ger\u00e4t gefunden, aber es konnte keine M\u00f6glichkeit gefunden werden, eine Verbindung zu diesem Ger\u00e4t herzustellen. Wenn diese Meldung weiterhin erscheint, versuche, die IP-Adresse anzugeben oder den Apple TV neu zu starten.", "unknown": "Unerwarteter Fehler" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Konfiguriere die allgemeinen Ger\u00e4teeinstellungen" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/el.json b/homeassistant/components/apple_tv/translations/el.json index cbe36bf56fb..a017d67b834 100644 --- a/homeassistant/components/apple_tv/translations/el.json +++ b/homeassistant/components/apple_tv/translations/el.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "already_configured_device": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "backoff": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03b1\u03b9\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae (\u03af\u03c3\u03c9\u03c2 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03bb\u03b7\u03ba\u03c4\u03c1\u03bf\u03bb\u03bf\u03b3\u03ae\u03c3\u03b5\u03b9 \u03ac\u03ba\u03c5\u03c1\u03bf \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc PIN \u03c0\u03ac\u03c1\u03b1 \u03c0\u03bf\u03bb\u03bb\u03ad\u03c2 \u03c6\u03bf\u03c1\u03ad\u03c2), \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1.", "device_did_not_pair": "\u0394\u03b5\u03bd \u03ad\u03b3\u03b9\u03bd\u03b5 \u03ba\u03b1\u03bc\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03bf\u03bb\u03bf\u03ba\u03bb\u03ae\u03c1\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1\u03c2 \u03c3\u03cd\u03b6\u03b5\u03c5\u03be\u03b7\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae.", "device_not_found": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", "inconsistent_device": "\u03a4\u03b1 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03b1 \u03c0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03b1 \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7. \u0391\u03c5\u03c4\u03cc \u03c3\u03c5\u03bd\u03ae\u03b8\u03c9\u03c2 \u03c5\u03c0\u03bf\u03b4\u03b7\u03bb\u03ce\u03bd\u03b5\u03b9 \u03c0\u03c1\u03cc\u03b2\u03bb\u03b7\u03bc\u03b1 \u03bc\u03b5 \u03c4\u03bf multicast DNS (Zeroconf). \u03a0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae.", - "invalid_config": "\u0397 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bb\u03bb\u03b9\u03c0\u03ae\u03c2. \u03a0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", "ipv6_not_supported": "\u03a4\u03bf IPv6 \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9.", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", @@ -19,7 +17,6 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", - "no_usable_service": "\u0392\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae, \u03b1\u03bb\u03bb\u03ac \u03b4\u03b5\u03bd \u03bc\u03c0\u03cc\u03c1\u03b5\u03c3\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03b5\u03af \u03ba\u03b1\u03bd\u03ad\u03bd\u03b1\u03c2 \u03c4\u03c1\u03cc\u03c0\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03bc\u03b9\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd. \u0391\u03bd \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b2\u03bb\u03ad\u03c0\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03bc\u03ae\u03bd\u03c5\u03bc\u03b1, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ae \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Apple TV.", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03b3\u03b5\u03bd\u03b9\u03ba\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/en.json b/homeassistant/components/apple_tv/translations/en.json index 792eaf32c29..f455d590d79 100644 --- a/homeassistant/components/apple_tv/translations/en.json +++ b/homeassistant/components/apple_tv/translations/en.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Device is already configured", - "already_configured_device": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "backoff": "Device does not accept pairing requests at this time (you might have entered an invalid PIN code too many times), try again later.", "device_did_not_pair": "No attempt to finish pairing process was made from the device.", "device_not_found": "Device was not found during discovery, please try adding it again.", "inconsistent_device": "Expected protocols were not found during discovery. This normally indicates a problem with multicast DNS (Zeroconf). Please try adding the device again.", - "invalid_config": "The configuration for this device is incomplete. Please try adding it again.", "ipv6_not_supported": "IPv6 is not supported.", "no_devices_found": "No devices found on the network", "reauth_successful": "Re-authentication was successful", @@ -19,7 +17,6 @@ "already_configured": "Device is already configured", "invalid_auth": "Invalid authentication", "no_devices_found": "No devices found on the network", - "no_usable_service": "A device was found but could not identify any way to establish a connection to it. If you keep seeing this message, try specifying its IP address or restarting your Apple TV.", "unknown": "Unexpected error" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Configure general device settings" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/es-419.json b/homeassistant/components/apple_tv/translations/es-419.json index 75e6fb43ff2..f8035822cb6 100644 --- a/homeassistant/components/apple_tv/translations/es-419.json +++ b/homeassistant/components/apple_tv/translations/es-419.json @@ -2,11 +2,7 @@ "config": { "abort": { "backoff": "El dispositivo no acepta solicitudes de emparejamiento en este momento (es posible que haya ingresado un c\u00f3digo PIN no v\u00e1lido demasiadas veces), vuelva a intentarlo m\u00e1s tarde.", - "device_did_not_pair": "No se intent\u00f3 finalizar el proceso de emparejamiento desde el dispositivo.", - "invalid_config": "La configuraci\u00f3n de este dispositivo est\u00e1 incompleta. Intente agregarlo nuevamente." - }, - "error": { - "no_usable_service": "Se encontr\u00f3 un dispositivo, pero no se pudo identificar ninguna forma de establecer una conexi\u00f3n con \u00e9l. Si sigue viendo este mensaje, intente especificar su direcci\u00f3n IP o reinicie su Apple TV." + "device_did_not_pair": "No se intent\u00f3 finalizar el proceso de emparejamiento desde el dispositivo." }, "step": { "confirm": { @@ -47,6 +43,5 @@ "description": "Configurar los ajustes generales del dispositivo" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/es.json b/homeassistant/components/apple_tv/translations/es.json index 3f22edd8d77..1a0ed773169 100644 --- a/homeassistant/components/apple_tv/translations/es.json +++ b/homeassistant/components/apple_tv/translations/es.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "already_configured_device": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "backoff": "El dispositivo no acepta solicitudes de emparejamiento en este momento (es posible que hayas introducido un c\u00f3digo PIN no v\u00e1lido demasiadas veces), int\u00e9ntalo de nuevo m\u00e1s tarde.", "device_did_not_pair": "No se ha intentado finalizar el proceso de emparejamiento desde el dispositivo.", "device_not_found": "No se ha encontrado el dispositivo durante la detecci\u00f3n, por favor, intente a\u00f1adirlo de nuevo.", "inconsistent_device": "No se encontraron los protocolos esperados durante el descubrimiento. Esto normalmente indica un problema con el DNS de multidifusi\u00f3n (Zeroconf). Por favor, intente a\u00f1adir el dispositivo de nuevo.", - "invalid_config": "La configuraci\u00f3n para este dispositivo est\u00e1 incompleta. Intenta a\u00f1adirlo de nuevo.", "no_devices_found": "No se encontraron dispositivos en la red", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", "setup_failed": "No se ha podido configurar el dispositivo.", @@ -18,7 +16,6 @@ "already_configured": "El dispositivo ya est\u00e1 configurado", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "no_devices_found": "No se encontraron dispositivos en la red", - "no_usable_service": "Se encontr\u00f3 un dispositivo, pero no se pudo identificar ninguna manera de establecer una conexi\u00f3n con \u00e9l. Si sigues viendo este mensaje, intenta especificar su direcci\u00f3n IP o reiniciar el Apple TV.", "unknown": "Error inesperado" }, "flow_title": "{name} ({type})", @@ -72,6 +69,5 @@ "description": "Configurar los ajustes generales del dispositivo" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/et.json b/homeassistant/components/apple_tv/translations/et.json index 8180b95fe12..81c5183ece1 100644 --- a/homeassistant/components/apple_tv/translations/et.json +++ b/homeassistant/components/apple_tv/translations/et.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "already_configured_device": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4imas", "backoff": "Seade ei aktsepteeri praegu sidumisn\u00f5udeid (v\u00f5ib-olla oled liiga palju kordi vale PIN-koodi sisestanud), proovi hiljem uuesti.", "device_did_not_pair": "Seade ei \u00fcritatud sidumisprotsessi l\u00f5pule viia.", "device_not_found": "Seadet avastamise ajal ei leitud, proovi seda uuesti lisada.", "inconsistent_device": "Eeldatavaid protokolle avastamise ajal ei leitud. See n\u00e4itab tavaliselt probleemi multcast DNS-iga (Zeroconf). Proovi seade uuesti lisada.", - "invalid_config": "Selle seadme s\u00e4tted on puudulikud. Proovi see uuesti lisada.", "ipv6_not_supported": "IPv6 ei ole toetatud.", "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet", "reauth_successful": "Taastuvastamine \u00f5nnestus", @@ -19,7 +17,6 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "invalid_auth": "Vigane autentimine", "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet", - "no_usable_service": "Leiti seade kuid ei suudetud tuvastada moodust \u00fchenduse loomiseks. Kui n\u00e4ed seda teadet pidevalt, proovi m\u00e4\u00e4rata seadme IP-aadress v\u00f5i taask\u00e4ivita Apple TV.", "unknown": "Ootamatu t\u00f5rge" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Seadme \u00fclds\u00e4tete seadistamine" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/fr.json b/homeassistant/components/apple_tv/translations/fr.json index 6d7deea6e5a..92075463f4d 100644 --- a/homeassistant/components/apple_tv/translations/fr.json +++ b/homeassistant/components/apple_tv/translations/fr.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", - "already_configured_device": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "backoff": "L'appareil n'accepte pas les demandes d'appariement pour le moment (vous avez peut-\u00eatre saisi un code PIN non valide trop de fois), r\u00e9essayez plus tard.", "device_did_not_pair": "Aucune tentative pour terminer l'appairage n'a \u00e9t\u00e9 effectu\u00e9e \u00e0 partir de l'appareil.", "device_not_found": "L'appareil n'a pas \u00e9t\u00e9 trouv\u00e9 lors de la d\u00e9couverte, veuillez r\u00e9essayer de l'ajouter.", "inconsistent_device": "Les protocoles attendus n'ont pas \u00e9t\u00e9 trouv\u00e9s lors de la d\u00e9couverte. Cela indique normalement un probl\u00e8me avec le DNS multicast (Zeroconf). Veuillez r\u00e9essayer d'ajouter l'appareil.", - "invalid_config": "La configuration de cet appareil est incompl\u00e8te. Veuillez r\u00e9essayer de l'ajouter.", "ipv6_not_supported": "IPv6 n'est pas pris en charge.", "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", @@ -19,7 +17,6 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "invalid_auth": "Authentification non valide", "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", - "no_usable_service": "Un dispositif a \u00e9t\u00e9 trouv\u00e9, mais aucun moyen d\u2019\u00e9tablir un lien avec lui. Si vous continuez \u00e0 voir ce message, essayez de sp\u00e9cifier son adresse IP ou de red\u00e9marrer votre Apple TV.", "unknown": "Erreur inattendue" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Configurer les param\u00e8tres g\u00e9n\u00e9raux de l'appareil" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/he.json b/homeassistant/components/apple_tv/translations/he.json index 61ca863bd66..26ec85e8dc7 100644 --- a/homeassistant/components/apple_tv/translations/he.json +++ b/homeassistant/components/apple_tv/translations/he.json @@ -2,7 +2,6 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "already_configured_device": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "ipv6_not_supported": "IPv6 \u05d0\u05d9\u05e0\u05d5 \u05e0\u05ea\u05de\u05da.", "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", diff --git a/homeassistant/components/apple_tv/translations/hu.json b/homeassistant/components/apple_tv/translations/hu.json index 73a30fbdd9a..4c3a8cdee94 100644 --- a/homeassistant/components/apple_tv/translations/hu.json +++ b/homeassistant/components/apple_tv/translations/hu.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_configured_device": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "backoff": "Az eszk\u00f6z jelenleg nem fogadja el a p\u00e1ros\u00edt\u00e1si k\u00e9relmeket (lehet, hogy t\u00fal sokszor adott meg \u00e9rv\u00e9nytelen PIN-k\u00f3dot), pr\u00f3b\u00e1lkozzon \u00fajra k\u00e9s\u0151bb.", "device_did_not_pair": "A p\u00e1ros\u00edt\u00e1s folyamat\u00e1t az eszk\u00f6zr\u0151l nem pr\u00f3b\u00e1lt\u00e1k befejezni.", "device_not_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a felder\u00edt\u00e9s sor\u00e1n, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni.", "inconsistent_device": "Az elv\u00e1rt protokollok nem tal\u00e1lhat\u00f3k a felder\u00edt\u00e9s sor\u00e1n. Ez \u00e1ltal\u00e1ban a multicast DNS (Zeroconf) probl\u00e9m\u00e1j\u00e1t jelzi. K\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni az eszk\u00f6zt.", - "invalid_config": "Az eszk\u00f6z konfigur\u00e1l\u00e1sa nem teljes. K\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg \u00fajra hozz\u00e1adni.", "ipv6_not_supported": "Az IPv6 nem t\u00e1mogatott.", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", @@ -19,7 +17,6 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", - "no_usable_service": "Tal\u00e1ltunk egy eszk\u00f6zt, de nem tudtuk azonos\u00edtani, hogyan lehetne kapcsolatot l\u00e9tes\u00edteni vele. Ha tov\u00e1bbra is ezt az \u00fczenetet l\u00e1tja, pr\u00f3b\u00e1lja meg megadni az IP-c\u00edm\u00e9t, vagy ind\u00edtsa \u00fajra az Apple TV-t.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Konfigur\u00e1lja az eszk\u00f6z \u00e1ltal\u00e1nos be\u00e1ll\u00edt\u00e1sait" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/id.json b/homeassistant/components/apple_tv/translations/id.json index 7120d0671b8..fcb77511abe 100644 --- a/homeassistant/components/apple_tv/translations/id.json +++ b/homeassistant/components/apple_tv/translations/id.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Perangkat sudah dikonfigurasi", - "already_configured_device": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", "backoff": "Perangkat tidak bisa menerima permintaan pemasangan saat ini (Anda mungkin telah berulang kali memasukkan kode PIN yang salah). Coba lagi nanti.", "device_did_not_pair": "Tidak ada upaya untuk menyelesaikan proses pemasangan dari sisi perangkat.", "device_not_found": "Perangkat tidak ditemukan selama penemuan, coba tambahkan lagi.", "inconsistent_device": "Protokol yang diharapkan tidak ditemukan selama penemuan. Ini biasanya terjadi karena masalah dengan DNS multicast (Zeroconf). Coba tambahkan perangkat lagi.", - "invalid_config": "Konfigurasi untuk perangkat ini tidak lengkap. Coba tambahkan lagi.", "ipv6_not_supported": "IPv6 tidak didukung.", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", "reauth_successful": "Autentikasi ulang berhasil", @@ -19,7 +17,6 @@ "already_configured": "Perangkat sudah dikonfigurasi", "invalid_auth": "Autentikasi tidak valid", "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", - "no_usable_service": "Perangkat ditemukan tetapi kami tidak dapat mengidentifikasi berbagai cara untuk membuat koneksi ke perangkat tersebut. Jika Anda terus melihat pesan ini, coba tentukan alamat IP-nya atau mulai ulang Apple TV Anda.", "unknown": "Kesalahan yang tidak diharapkan" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Konfigurasikan pengaturan umum perangkat" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/it.json b/homeassistant/components/apple_tv/translations/it.json index 8b6e744ce53..25bcbeaf9fe 100644 --- a/homeassistant/components/apple_tv/translations/it.json +++ b/homeassistant/components/apple_tv/translations/it.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "already_configured_device": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "backoff": "Il dispositivo non accetta richieste di abbinamento in questo momento (potresti aver inserito un codice PIN non valido troppe volte), riprova pi\u00f9 tardi.", "device_did_not_pair": "Nessun tentativo di completare il processo di abbinamento \u00e8 stato effettuato dal dispositivo.", "device_not_found": "Il dispositivo non \u00e8 stato trovato durante il rilevamento, prova ad aggiungerlo di nuovo.", "inconsistent_device": "I protocolli previsti non sono stati trovati durante il rilevamento. Questo normalmente indica un problema con DNS multicast (Zeroconf). Prova ad aggiungere di nuovo il dispositivo.", - "invalid_config": "La configurazione per questo dispositivo \u00e8 incompleta. Prova ad aggiungerlo di nuovo.", "ipv6_not_supported": "IPv6 non \u00e8 supportato.", "no_devices_found": "Nessun dispositivo trovato sulla rete", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", @@ -19,7 +17,6 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "invalid_auth": "Autenticazione non valida", "no_devices_found": "Nessun dispositivo trovato sulla rete", - "no_usable_service": "\u00c8 stato trovato un dispositivo, ma non \u00e8 stato possibile identificare alcun modo per stabilire una connessione con esso. Se continui a vedere questo messaggio, prova a specificarne l'indirizzo IP o a riavviare l'Apple TV.", "unknown": "Errore imprevisto" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Configura le impostazioni generali del dispositivo" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/ja.json b/homeassistant/components/apple_tv/translations/ja.json index 9984006365e..3661cbeedb3 100644 --- a/homeassistant/components/apple_tv/translations/ja.json +++ b/homeassistant/components/apple_tv/translations/ja.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "already_configured_device": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", "backoff": "\u73fe\u5728\u3001\u30c7\u30d0\u30a4\u30b9\u306f\u30da\u30a2\u30ea\u30f3\u30b0\u8981\u6c42\u3092\u53d7\u3051\u4ed8\u3051\u3066\u3044\u307e\u305b\u3093(\u7121\u52b9\u306aPIN\u30b3\u30fc\u30c9\u3092\u4f55\u5ea6\u3082\u5165\u529b\u3057\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059)\u3001\u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "device_did_not_pair": "\u30c7\u30d0\u30a4\u30b9\u304b\u3089\u30da\u30a2\u30ea\u30f3\u30b0\u30d7\u30ed\u30bb\u30b9\u3092\u7d42\u4e86\u3059\u308b\u8a66\u307f\u306f\u884c\u308f\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002", "device_not_found": "\u691c\u51fa\u4e2d\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", "inconsistent_device": "\u691c\u51fa\u4e2d\u306b\u671f\u5f85\u3057\u305f\u30d7\u30ed\u30c8\u30b3\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3053\u308c\u306f\u901a\u5e38\u3001\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8DNS(Zeroconf)\u306b\u554f\u984c\u304c\u3042\u308b\u3053\u3068\u3092\u793a\u3057\u3066\u3044\u307e\u3059\u3002\u30c7\u30d0\u30a4\u30b9\u3092\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", - "invalid_config": "\u3053\u306e\u30c7\u30d0\u30a4\u30b9\u306e\u8a2d\u5b9a\u306f\u4e0d\u5b8c\u5168\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u8ffd\u52a0\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", "ipv6_not_supported": "IPv6\u306b\u306f\u5bfe\u5fdc\u3057\u3066\u3044\u307e\u305b\u3093\u3002", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", @@ -19,7 +17,6 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", - "no_usable_service": "\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f\u304c\u3001\u30c7\u30d0\u30a4\u30b9\u3078\u306e\u63a5\u7d9a\u3092\u78ba\u7acb\u3059\u308b\u65b9\u6cd5\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u3053\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u304c\u5f15\u304d\u7d9a\u304d\u8868\u793a\u3055\u308c\u308b\u5834\u5408\u306f\u3001IP\u30a2\u30c9\u30ec\u30b9\u3092\u6307\u5b9a\u3059\u308b\u304b\u3001Apple TV\u3092\u518d\u8d77\u52d5\u3057\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "flow_title": "{name}", @@ -73,6 +70,5 @@ "description": "\u30c7\u30d0\u30a4\u30b9\u306e\u4e00\u822c\u7684\u306a\u8a2d\u5b9a" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/ko.json b/homeassistant/components/apple_tv/translations/ko.json index 278dbec04e4..fb2373eac4f 100644 --- a/homeassistant/components/apple_tv/translations/ko.json +++ b/homeassistant/components/apple_tv/translations/ko.json @@ -1,11 +1,9 @@ { "config": { "abort": { - "already_configured_device": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "backoff": "\uae30\uae30\uac00 \ud604\uc7ac \ud398\uc5b4\ub9c1 \uc694\uccad\uc744 \uc218\ub77d\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4(\uc798\ubabb\ub41c PIN \ucf54\ub4dc\ub97c \ub108\ubb34 \ub9ce\uc774 \uc785\ub825\ud588\uc744 \uc218 \uc788\uc74c). \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", "device_did_not_pair": "\uae30\uae30\uc5d0\uc11c \ud398\uc5b4\ub9c1 \ud504\ub85c\uc138\uc2a4\ub97c \uc644\ub8cc\ud558\ub824\uace0 \uc2dc\ub3c4\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", - "invalid_config": "\uc774 \uae30\uae30\uc5d0 \ub300\ud55c \uad6c\uc131\uc774 \ubd88\uc644\uc804\ud569\ub2c8\ub2e4. \ub2e4\uc2dc \ucd94\uac00\ud574\uc8fc\uc138\uc694.", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, @@ -13,7 +11,6 @@ "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", - "no_usable_service": "\uae30\uae30\ub97c \ucc3e\uc558\uc9c0\ub9cc \uae30\uae30\uc5d0 \uc5f0\uacb0\ud558\ub294 \ubc29\ubc95\uc744 \uc2dd\ubcc4\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uba54\uc2dc\uc9c0\uac00 \uacc4\uc18d \ud45c\uc2dc\ub418\uba74 \ud574\ub2f9 IP \uc8fc\uc18c\ub97c \uc9c1\uc811 \uc9c0\uc815\ud574\uc8fc\uc2dc\uac70\ub098 Apple TV\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud574\uc8fc\uc138\uc694.", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "flow_title": "Apple TV: {name}", @@ -59,6 +56,5 @@ "description": "\uc77c\ubc18 \uae30\uae30 \uc124\uc815 \uad6c\uc131" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/lb.json b/homeassistant/components/apple_tv/translations/lb.json index 2354033b577..0950ff8235a 100644 --- a/homeassistant/components/apple_tv/translations/lb.json +++ b/homeassistant/components/apple_tv/translations/lb.json @@ -1,9 +1,7 @@ { "config": { "abort": { - "already_configured_device": "Apparat ass scho konfigur\u00e9iert", "already_in_progress": "Konfiguratioun's Oflaf ass schon am gaang", - "invalid_config": "Konfiguratioun fir d\u00ebsen Apparat ass net komplett. Prob\u00e9ier fir et nach emol dob\u00e4i ze setzen.", "no_devices_found": "Keng Apparater am Netzwierk fonnt", "unknown": "Onerwaarte Feeler" }, @@ -55,6 +53,5 @@ "description": "Allgemeng Apparat Astellungen konfigur\u00e9ieren" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/nl.json b/homeassistant/components/apple_tv/translations/nl.json index 8aaf120403c..36bebc02761 100644 --- a/homeassistant/components/apple_tv/translations/nl.json +++ b/homeassistant/components/apple_tv/translations/nl.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_configured_device": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", "backoff": "Het apparaat accepteert op dit moment geen koppelingsverzoeken (u heeft mogelijk te vaak een ongeldige pincode ingevoerd), probeer het later opnieuw.", "device_did_not_pair": "Er is geen poging gedaan om het koppelingsproces te voltooien vanaf het apparaat.", "device_not_found": "Apparaat werd niet gevonden tijdens het zoeken, probeer het opnieuw toe te voegen.", "inconsistent_device": "De verwachte protocollen zijn niet gevonden tijdens het zoeken. Dit wijst gewoonlijk op een probleem met multicast DNS (Zeroconf). Probeer het apparaat opnieuw toe te voegen.", - "invalid_config": "De configuratie voor dit apparaat is onvolledig. Probeer het opnieuw toe te voegen.", "ipv6_not_supported": "IPv6 wordt niet ondersteund.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "reauth_successful": "Herauthenticatie was succesvol", @@ -19,7 +17,6 @@ "already_configured": "Apparaat is al geconfigureerd", "invalid_auth": "Ongeldige authenticatie", "no_devices_found": "Geen apparaten gevonden op het netwerk", - "no_usable_service": "Er is een apparaat gevonden, maar er kon geen manier worden gevonden om er verbinding mee te maken. Als u dit bericht blijft zien, probeert u het IP-adres in te voeren of uw Apple TV opnieuw op te starten.", "unknown": "Onverwachte fout" }, "flow_title": "{name} ( {type} )", @@ -73,6 +70,5 @@ "description": "Algemene apparaatinstellingen configureren" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/no.json b/homeassistant/components/apple_tv/translations/no.json index e364633597e..97f80c7dfe7 100644 --- a/homeassistant/components/apple_tv/translations/no.json +++ b/homeassistant/components/apple_tv/translations/no.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Enheten er allerede konfigurert", - "already_configured_device": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "backoff": "Enheten godtar ikke sammenkoblingsforesp\u00f8rsler for \u00f8yeblikket (du kan ha skrevet inn en ugyldig PIN-kode for mange ganger), pr\u00f8v igjen senere.", "device_did_not_pair": "Ingen fors\u00f8k p\u00e5 \u00e5 fullf\u00f8re paringsprosessen ble gjort fra enheten", "device_not_found": "Enheten ble ikke funnet under oppdagelsen. Pr\u00f8v \u00e5 legge den til p\u00e5 nytt.", "inconsistent_device": "Forventede protokoller ble ikke funnet under oppdagelsen. Dette indikerer vanligvis et problem med multicast DNS (Zeroconf). Pr\u00f8v \u00e5 legge til enheten p\u00e5 nytt.", - "invalid_config": "Konfigurasjonen for denne enheten er ufullstendig. Pr\u00f8v \u00e5 legge den til p\u00e5 nytt.", "ipv6_not_supported": "IPv6 st\u00f8ttes ikke.", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", @@ -19,7 +17,6 @@ "already_configured": "Enheten er allerede konfigurert", "invalid_auth": "Ugyldig godkjenning", "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", - "no_usable_service": "En enhet ble funnet, men kunne ikke identifisere noen m\u00e5te \u00e5 etablere en tilkobling til den. Hvis du fortsetter \u00e5 se denne meldingen, kan du pr\u00f8ve \u00e5 angi IP-adressen eller starte Apple TV p\u00e5 nytt.", "unknown": "Uventet feil" }, "flow_title": "{name} ( {type} )", @@ -73,6 +70,5 @@ "description": "Konfigurer generelle enhetsinnstillinger" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/pl.json b/homeassistant/components/apple_tv/translations/pl.json index 303de09c1dd..6c236c7b3ea 100644 --- a/homeassistant/components/apple_tv/translations/pl.json +++ b/homeassistant/components/apple_tv/translations/pl.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "already_configured_device": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", "backoff": "Urz\u0105dzenie w tej chwili nie akceptuje \u017c\u0105da\u0144 parowania (by\u0107 mo\u017ce zbyt wiele razy wpisa\u0142e\u015b nieprawid\u0142owy kod PIN), spr\u00f3buj ponownie p\u00f3\u017aniej.", "device_did_not_pair": "Nie podj\u0119to pr\u00f3by zako\u0144czenia procesu parowania z urz\u0105dzenia.", "device_not_found": "Urz\u0105dzenie nie zosta\u0142o znalezione podczas wykrywania, spr\u00f3buj doda\u0107 je ponownie.", "inconsistent_device": "Oczekiwane protoko\u0142y nie zosta\u0142y znalezione podczas wykrywania. Zwykle wskazuje to na problem z multicastem DNS (Zeroconf). Spr\u00f3buj ponownie doda\u0107 urz\u0105dzenie.", - "invalid_config": "Konfiguracja tego urz\u0105dzenia jest niekompletna. Spr\u00f3buj doda\u0107 go ponownie.", "ipv6_not_supported": "IPv6 nie jest obs\u0142ugiwany.", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", @@ -19,7 +17,6 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "invalid_auth": "Niepoprawne uwierzytelnienie", "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", - "no_usable_service": "Znaleziono urz\u0105dzenie, ale nie uda\u0142o si\u0119 zidentyfikowa\u0107 \u017cadnego sposobu na nawi\u0105zanie z nim po\u0142\u0105czenia. Je\u015bli nadal widzisz t\u0119 wiadomo\u015b\u0107, spr\u00f3buj poda\u0107 jego adres IP lub uruchom ponownie Apple TV.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Skonfiguruj og\u00f3lne ustawienia urz\u0105dzenia" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/pt-BR.json b/homeassistant/components/apple_tv/translations/pt-BR.json index 539335c17d3..801f6df094b 100644 --- a/homeassistant/components/apple_tv/translations/pt-BR.json +++ b/homeassistant/components/apple_tv/translations/pt-BR.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "already_configured_device": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "backoff": "O dispositivo n\u00e3o aceita solicita\u00e7\u00f5es de emparelhamento neste momento (voc\u00ea pode ter digitado um c\u00f3digo PIN inv\u00e1lido muitas vezes), tente novamente mais tarde.", "device_did_not_pair": "Nenhuma tentativa de concluir o processo de emparelhamento foi feita a partir do dispositivo.", "device_not_found": "O dispositivo n\u00e3o foi encontrado durante a descoberta. Tente adicion\u00e1-lo novamente.", "inconsistent_device": "Os protocolos esperados n\u00e3o foram encontrados durante a descoberta. Isso normalmente indica um problema com o DNS multicast (Zeroconf). Tente adicionar o dispositivo novamente.", - "invalid_config": "A configura\u00e7\u00e3o deste dispositivo est\u00e1 incompleta. Tente adicion\u00e1-lo novamente.", "ipv6_not_supported": "IPv6 n\u00e3o \u00e9 suportado.", "no_devices_found": "Nenhum dispositivo encontrado na rede", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", @@ -19,7 +17,6 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "no_devices_found": "Nenhum dispositivo encontrado na rede", - "no_usable_service": "Um dispositivo foi encontrado, mas n\u00e3o foi poss\u00edvel identificar nenhuma maneira de estabelecer uma conex\u00e3o com ele. Se voc\u00ea continuar vendo esta mensagem, tente especificar o endere\u00e7o IP ou reiniciar a Apple TV.", "unknown": "Erro inesperado" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "Definir as configura\u00e7\u00f5es gerais do dispositivo" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/pt.json b/homeassistant/components/apple_tv/translations/pt.json index 486ff0c51e4..deec60a19ea 100644 --- a/homeassistant/components/apple_tv/translations/pt.json +++ b/homeassistant/components/apple_tv/translations/pt.json @@ -1,7 +1,6 @@ { "config": { "abort": { - "already_configured_device": "O dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", "no_devices_found": "Nenhum dispositivo encontrado na rede", "unknown": "Erro inesperado" @@ -10,7 +9,6 @@ "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "no_devices_found": "Nenhum dispositivo encontrado na rede", - "no_usable_service": "Foi encontrado um dispositivo, mas n\u00e3o foi poss\u00edvel identificar nenhuma forma de estabelecer uma liga\u00e7\u00e3o com ele. Se continuar a ver esta mensagem, tente especificar o endere\u00e7o IP ou reiniciar a sua Apple TV.", "unknown": "Erro inesperado" }, "flow_title": "Apple TV: {name}", @@ -56,6 +54,5 @@ "description": "Definir as configura\u00e7\u00f5es gerais do dispositivo" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/ru.json b/homeassistant/components/apple_tv/translations/ru.json index 88516d45af3..024103258a8 100644 --- a/homeassistant/components/apple_tv/translations/ru.json +++ b/homeassistant/components/apple_tv/translations/ru.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "already_configured_device": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "backoff": "\u0412 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u0435 (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0412\u044b \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0437 \u0432\u0432\u043e\u0434\u0438\u043b\u0438 \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 PIN-\u043a\u043e\u0434), \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", "device_did_not_pair": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u044b\u0442\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u043e\u043f\u0440\u044f\u0436\u0435\u043d\u0438\u044f.", "device_not_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", "inconsistent_device": "\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b. \u041e\u0431\u044b\u0447\u043d\u043e \u044d\u0442\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441 \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u044b\u043c DNS (Zeroconf). \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", - "invalid_config": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0435\u0449\u0451 \u0440\u0430\u0437.", "ipv6_not_supported": "IPv6 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", @@ -19,7 +17,6 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", - "no_usable_service": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u043c\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443. \u0415\u0441\u043b\u0438 \u0412\u044b \u0443\u0436\u0435 \u0432\u0438\u0434\u0435\u043b\u0438 \u044d\u0442\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u043b\u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0435\u0433\u043e.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/sl.json b/homeassistant/components/apple_tv/translations/sl.json index 997d60402ca..da9af2347c0 100644 --- a/homeassistant/components/apple_tv/translations/sl.json +++ b/homeassistant/components/apple_tv/translations/sl.json @@ -1,11 +1,9 @@ { "config": { "abort": { - "already_configured_device": "Naprava je \u017ee name\u0161\u010dena", "already_in_progress": "Name\u0161\u010danje se \u017ee izvaja", "backoff": "Naprav v tem trenutku ne sprejema zahtev za seznanitev (morda ste preve\u010dkrat vnesli napa\u010den PIN). Pokusitve znova kasneje.", "device_did_not_pair": "Iz te naprave ni bilo poskusov zaklju\u010diti seznanjanja.", - "invalid_config": "Namestitev te naprave ni bila zaklju\u010dena. Poskusite ponovno.", "no_devices_found": "Ni najdenih naprav v omre\u017eju", "unknown": "Nepri\u010dakovana napaka" }, @@ -13,7 +11,6 @@ "already_configured": "Naprava je \u017ee name\u0161\u010dena", "invalid_auth": "Napaka pri overjanju", "no_devices_found": "Ni najdenih naprav v omre\u017eju", - "no_usable_service": "Najdena je bila naprava, za katero ni znan na\u010din povezovanja. \u010ce boste \u0161e vedno videli to sporo\u010dilo, poskusite dolo\u010diti IP naslov ali pa ponovno za\u017eenite Apple TV.", "unknown": "Nepri\u010dakovana napaka" }, "flow_title": "Apple TV: {name}", @@ -59,6 +56,5 @@ "description": "Konfiguracija splo\u0161nih nastavitev naprave" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/tr.json b/homeassistant/components/apple_tv/translations/tr.json index cee9fcce81e..4919d48c15c 100644 --- a/homeassistant/components/apple_tv/translations/tr.json +++ b/homeassistant/components/apple_tv/translations/tr.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "already_configured_device": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "backoff": "Cihaz \u015fu anda e\u015fle\u015ftirme isteklerini kabul etmiyor (\u00e7ok say\u0131da ge\u00e7ersiz PIN kodu girmi\u015f olabilirsiniz), daha sonra tekrar deneyin.", "device_did_not_pair": "Cihazdan e\u015fle\u015ftirme i\u015flemini bitirmek i\u00e7in herhangi bir giri\u015fimde bulunulmad\u0131.", "device_not_found": "Cihaz ke\u015fif s\u0131ras\u0131nda bulunamad\u0131, l\u00fctfen tekrar eklemeyi deneyin.", "inconsistent_device": "Ke\u015fif s\u0131ras\u0131nda beklenen protokoller bulunamad\u0131. Bu normalde \u00e7ok noktaya yay\u0131n DNS (Zeroconf) ile ilgili bir sorunu g\u00f6sterir. L\u00fctfen cihaz\u0131 tekrar eklemeyi deneyin.", - "invalid_config": "Bu ayg\u0131t\u0131n yap\u0131land\u0131rmas\u0131 tamamlanmad\u0131. L\u00fctfen tekrar eklemeyi deneyin.", "ipv6_not_supported": "IPv6 desteklenmiyor.", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", @@ -19,7 +17,6 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "no_devices_found": "A\u011fda cihaz bulunamad\u0131", - "no_usable_service": "Bir ayg\u0131t bulundu, ancak ba\u011flant\u0131 kurman\u0131n herhangi bir yolunu tan\u0131mlayamad\u0131. Bu iletiyi g\u00f6rmeye devam ederseniz, IP adresini belirtmeye veya Apple TV'nizi yeniden ba\u015flatmaya \u00e7al\u0131\u015f\u0131n.", "unknown": "Beklenmeyen hata" }, "flow_title": "{name} ( {type} )", @@ -73,6 +70,5 @@ "description": "Genel cihaz ayarlar\u0131n\u0131 yap\u0131land\u0131r\u0131n" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/uk.json b/homeassistant/components/apple_tv/translations/uk.json index a1ae2259ada..984aeceaaeb 100644 --- a/homeassistant/components/apple_tv/translations/uk.json +++ b/homeassistant/components/apple_tv/translations/uk.json @@ -1,11 +1,9 @@ { "config": { "abort": { - "already_configured_device": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0436\u0435 \u0442\u0440\u0438\u0432\u0430\u0454.", "backoff": "\u0412 \u0434\u0430\u043d\u0438\u0439 \u0447\u0430\u0441 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043d\u0435 \u043f\u0440\u0438\u0439\u043c\u0430\u0454 \u0437\u0430\u043f\u0438\u0442\u0438 \u043d\u0430 \u0441\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f \u043f\u0430\u0440\u0438 (\u043c\u043e\u0436\u043b\u0438\u0432\u043e, \u0412\u0438 \u0437\u0430\u043d\u0430\u0434\u0442\u043e \u0431\u0430\u0433\u0430\u0442\u043e \u0440\u0430\u0437 \u0432\u0432\u043e\u0434\u0438\u043b\u0438 \u043d\u0435\u0432\u0456\u0440\u043d\u0438\u0439 PIN-\u043a\u043e\u0434), \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0449\u0435 \u0440\u0430\u0437 \u043f\u0456\u0437\u043d\u0456\u0448\u0435.", "device_did_not_pair": "\u041f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043d\u0435 \u043d\u0430\u043c\u0430\u0433\u0430\u0432\u0441\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441 \u0441\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f \u043f\u0430\u0440\u0438.", - "invalid_config": "\u041a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f \u0446\u044c\u043e\u0433\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u0421\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 \u0439\u043e\u0433\u043e \u0449\u0435 \u0440\u0430\u0437.", "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, @@ -13,7 +11,6 @@ "already_configured": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.", "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456.", - "no_usable_service": "\u041d\u0435\u043c\u043e\u0436\u043b\u0438\u0432\u043e \u0432\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0441\u043f\u043e\u0441\u0456\u0431 \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e. \u042f\u043a\u0449\u043e \u0412\u0438 \u0432\u0436\u0435 \u0431\u0430\u0447\u0438\u043b\u0438 \u0446\u0435 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u043d\u044f, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0432\u043a\u0430\u0437\u0430\u0442\u0438 IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e \u0430\u0431\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0456\u0442\u044c \u0439\u043e\u0433\u043e.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, "flow_title": "Apple TV: {name}", @@ -59,6 +56,5 @@ "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0456\u0432 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/zh-Hans.json b/homeassistant/components/apple_tv/translations/zh-Hans.json index b08ccafc837..365bf6cf6c0 100644 --- a/homeassistant/components/apple_tv/translations/zh-Hans.json +++ b/homeassistant/components/apple_tv/translations/zh-Hans.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "\u8bbe\u5907\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86", - "already_configured_device": "\u8bbe\u5907\u5df2\u88ab\u914d\u7f6e", "already_in_progress": "\u914d\u7f6e\u6d41\u7a0b\u5df2\u5728\u8fdb\u884c\u4e2d", "backoff": "\u8bbe\u5907\u76ee\u524d\u6682\u4e0d\u63a5\u53d7\u914d\u5bf9\u8bf7\u6c42\uff0c\u53ef\u80fd\u662f\u56e0\u4e3a\u591a\u6b21 PIN \u7801\u8f93\u5165\u9519\u8bef\u3002\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002", "device_did_not_pair": "\u672a\u5c1d\u8bd5\u4ece\u8bbe\u5907\u5b8c\u6210\u914d\u5bf9\u8fc7\u7a0b\u3002", "device_not_found": "\u672a\u627e\u5230\u8bbe\u5907\uff0c\u8bf7\u5c1d\u8bd5\u91cd\u65b0\u6dfb\u52a0\u3002", "inconsistent_device": "\u641c\u7d22\u671f\u95f4\u672a\u53d1\u73b0\u914d\u7f6e\u8bbe\u5907\u6240\u5fc5\u9700\u7684\u534f\u8bae\u3002\u8fd9\u901a\u5e38\u662f\u56e0\u4e3a mDNS \u534f\u8bae\uff08zeroconf\uff09\u5b58\u5728\u95ee\u9898\u3002\u8bf7\u7a0d\u540e\u518d\u91cd\u65b0\u5c1d\u8bd5\u6dfb\u52a0\u8bbe\u5907\u3002", - "invalid_config": "\u6b64\u8bbe\u5907\u7684\u914d\u7f6e\u4fe1\u606f\u4e0d\u5b8c\u6574\u3002\u8bf7\u5c1d\u8bd5\u91cd\u65b0\u6dfb\u52a0\u3002", "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230\u8bbe\u5907", "reauth_successful": "\u91cd\u65b0\u8ba4\u8bc1\u6210\u529f", "setup_failed": "\u8bbe\u7f6e\u8bbe\u5907\u5931\u8d25\u3002", @@ -18,7 +16,6 @@ "already_configured": "\u8bbe\u5907\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86", "invalid_auth": "\u8eab\u4efd\u8ba4\u8bc1\u65e0\u6548", "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230\u8bbe\u5907", - "no_usable_service": "\u5df2\u627e\u5230\u8bbe\u5907\uff0c\u4f46\u672a\u8bc6\u522b\u51fa\u4e0e\u5176\u5efa\u7acb\u8fde\u63a5\u7684\u65b9\u5f0f\u3002\u5982\u679c\u6b64\u95ee\u9898\u6301\u7eed\u5b58\u5728\uff0c\u8bf7\u5c1d\u8bd5\u6307\u5b9a\u5176 IP \u5730\u5740\uff0c\u6216\u91cd\u65b0\u542f\u52a8\u8bbe\u5907\u3002", "unknown": "\u975e\u9884\u671f\u7684\u9519\u8bef" }, "flow_title": "{name} ({type})", @@ -72,6 +69,5 @@ "description": "\u914d\u7f6e\u8bbe\u5907\u901a\u7528\u8bbe\u7f6e" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/apple_tv/translations/zh-Hant.json b/homeassistant/components/apple_tv/translations/zh-Hant.json index b4e5108d474..be2bfe08fdd 100644 --- a/homeassistant/components/apple_tv/translations/zh-Hant.json +++ b/homeassistant/components/apple_tv/translations/zh-Hant.json @@ -2,13 +2,11 @@ "config": { "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "already_configured_device": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "backoff": "\u88dd\u7f6e\u4e0d\u63a5\u53d7\u6b64\u6b21\u914d\u5c0d\u8acb\u6c42\uff08\u53ef\u80fd\u8f38\u5165\u592a\u591a\u6b21\u7121\u6548\u7684 PIN \u78bc\uff09\uff0c\u8acb\u7a0d\u5f8c\u518d\u8a66\u3002", "device_did_not_pair": "\u88dd\u7f6e\u6c92\u6709\u5617\u8a66\u914d\u5c0d\u5b8c\u6210\u904e\u7a0b\u3002", "device_not_found": "\u641c\u5c0b\u4e0d\u5230\u88dd\u7f6e\u3001\u8acb\u8a66\u8457\u518d\u65b0\u589e\u4e00\u6b21\u3002", "inconsistent_device": "\u641c\u5c0b\u4e0d\u5230\u9810\u671f\u7684\u901a\u8a0a\u5354\u5b9a\u3002\u901a\u5e38\u539f\u56e0\u70ba Multicast DNS (Zeroconf) \u554f\u984c\u3001\u8acb\u8a66\u8457\u518d\u65b0\u589e\u4e00\u6b21\u3002", - "invalid_config": "\u6b64\u88dd\u7f6e\u8a2d\u5b9a\u4e0d\u5b8c\u6574\uff0c\u8acb\u7a0d\u5019\u518d\u8a66\u4e00\u6b21\u3002", "ipv6_not_supported": "\u4e0d\u652f\u63f4 IPv6\u3002", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", @@ -19,7 +17,6 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", - "no_usable_service": "\u627e\u5230\u7684\u88dd\u7f6e\u7121\u6cd5\u8b58\u5225\u4ee5\u9032\u884c\u9023\u7dda\u3002\u5047\u5982\u6b64\u8a0a\u606f\u91cd\u8907\u767c\u751f\u3002\u8acb\u8a66\u8457\u6307\u5b9a\u7279\u5b9a IP \u4f4d\u5740\u6216\u91cd\u555f Apple TV\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "flow_title": "{name} ({type})", @@ -73,6 +70,5 @@ "description": "\u8a2d\u5b9a\u4e00\u822c\u88dd\u7f6e\u8a2d\u5b9a" } } - }, - "title": "Apple TV" + } } \ No newline at end of file diff --git a/homeassistant/components/application_credentials/translations/ko.json b/homeassistant/components/application_credentials/translations/ko.json new file mode 100644 index 00000000000..3beeea8e2e4 --- /dev/null +++ b/homeassistant/components/application_credentials/translations/ko.json @@ -0,0 +1,3 @@ +{ + "title": "\uc560\ud50c\ub9ac\ucf00\uc774\uc158 \uc790\uaca9 \uc99d\uba85" +} \ No newline at end of file diff --git a/homeassistant/components/asuswrt/translations/ca.json b/homeassistant/components/asuswrt/translations/ca.json index 93821419fc4..8cded456ee9 100644 --- a/homeassistant/components/asuswrt/translations/ca.json +++ b/homeassistant/components/asuswrt/translations/ca.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "No s'ha pogut determinar cap identificador \u00fanic v\u00e0lid del dispositiu", - "no_unique_id": "Ja s'ha configurat un dispositiu sense un identificador \u00fanic v\u00e0lid. La configuraci\u00f3 de diverses inst\u00e0ncies no \u00e9s possible", - "not_unique_id_exist": "Ja s'ha configurat un dispositiu sense un identificador \u00fanic v\u00e0lid. La configuraci\u00f3 de diverses inst\u00e0ncies no \u00e9s possible", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + "no_unique_id": "Ja s'ha configurat un dispositiu sense un identificador \u00fanic v\u00e0lid. La configuraci\u00f3 de diverses inst\u00e0ncies no \u00e9s possible" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", diff --git a/homeassistant/components/asuswrt/translations/cs.json b/homeassistant/components/asuswrt/translations/cs.json index d9766e9a6d0..26358d8c4bd 100644 --- a/homeassistant/components/asuswrt/translations/cs.json +++ b/homeassistant/components/asuswrt/translations/cs.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." - }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_host": "Neplatn\u00fd hostitel nebo IP adresa", diff --git a/homeassistant/components/asuswrt/translations/de.json b/homeassistant/components/asuswrt/translations/de.json index 1a0dc26903d..be17c242d58 100644 --- a/homeassistant/components/asuswrt/translations/de.json +++ b/homeassistant/components/asuswrt/translations/de.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Unm\u00f6glich, eine g\u00fcltige eindeutige Kennung f\u00fcr das Ger\u00e4t zu ermitteln", - "no_unique_id": "Ein Ger\u00e4t ohne g\u00fcltige, eindeutige ID ist bereits konfiguriert. Die Konfiguration mehrerer Instanzen ist nicht m\u00f6glich", - "not_unique_id_exist": "Ein Ger\u00e4t ohne g\u00fcltige, eindeutige ID ist bereits konfiguriert. Die Konfiguration mehrerer Instanzen ist nicht m\u00f6glich", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + "no_unique_id": "Ein Ger\u00e4t ohne g\u00fcltige, eindeutige ID ist bereits konfiguriert. Die Konfiguration mehrerer Instanzen ist nicht m\u00f6glich" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", diff --git a/homeassistant/components/asuswrt/translations/el.json b/homeassistant/components/asuswrt/translations/el.json index bb18890091c..6a380b23152 100644 --- a/homeassistant/components/asuswrt/translations/el.json +++ b/homeassistant/components/asuswrt/translations/el.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "\u0391\u03b4\u03cd\u03bd\u03b1\u03c4\u03bf\u03c2 \u03bf \u03c0\u03c1\u03bf\u03c3\u03b4\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03bd\u03cc\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c5 \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03bf\u03cd \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03bf\u03cd \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", - "no_unique_id": "\u039c\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", - "not_unique_id_exist": "\u039c\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + "no_unique_id": "\u039c\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/asuswrt/translations/en.json b/homeassistant/components/asuswrt/translations/en.json index 93e8f8869b0..f93eae80e6d 100644 --- a/homeassistant/components/asuswrt/translations/en.json +++ b/homeassistant/components/asuswrt/translations/en.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Impossible to determine a valid unique id for the device", - "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", - "not_unique_id_exist": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible", - "single_instance_allowed": "Already configured. Only a single configuration possible." + "no_unique_id": "A device without a valid unique id is already configured. Configuration of multiple instance is not possible" }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/asuswrt/translations/es.json b/homeassistant/components/asuswrt/translations/es.json index 8a7876bb45b..9a2e0485aa7 100644 --- a/homeassistant/components/asuswrt/translations/es.json +++ b/homeassistant/components/asuswrt/translations/es.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "invalid_unique_id": "No se pudo determinar ning\u00fan identificador \u00fanico v\u00e1lido del dispositivo", - "single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n." + "invalid_unique_id": "No se pudo determinar ning\u00fan identificador \u00fanico v\u00e1lido del dispositivo" }, "error": { "cannot_connect": "No se pudo conectar", diff --git a/homeassistant/components/asuswrt/translations/et.json b/homeassistant/components/asuswrt/translations/et.json index 03dc665fcf9..e76f5006de7 100644 --- a/homeassistant/components/asuswrt/translations/et.json +++ b/homeassistant/components/asuswrt/translations/et.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Seadme kehtivat kordumatut ID-d on v\u00f5imatu m\u00e4\u00e4rata", - "no_unique_id": "Seade, millel puudub kehtiv kordumatu ID, on juba seadistatud. Mitme eksemplari seadistamine pole v\u00f5imalik", - "not_unique_id_exist": "Seade, millel puudub kehtiv kordumatu ID, on juba seadistatud. Mitme eksemplari seadistamine pole v\u00f5imalik", - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + "no_unique_id": "Seade, millel puudub kehtiv kordumatu ID, on juba seadistatud. Mitme eksemplari seadistamine pole v\u00f5imalik" }, "error": { "cannot_connect": "\u00dchendamine nurjus", diff --git a/homeassistant/components/asuswrt/translations/fr.json b/homeassistant/components/asuswrt/translations/fr.json index 9cb849632b3..7ef4eafc6b3 100644 --- a/homeassistant/components/asuswrt/translations/fr.json +++ b/homeassistant/components/asuswrt/translations/fr.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Impossible de d\u00e9terminer un identifiant unique valide pour l'appareil", - "no_unique_id": "Un appareil sans identifiant unique valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", - "not_unique_id_exist": "Un appareil sans identifiant unique valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + "no_unique_id": "Un appareil sans identifiant unique valide est d\u00e9j\u00e0 configur\u00e9. La configuration de plusieurs instances n'est pas possible" }, "error": { "cannot_connect": "\u00c9chec de connexion", diff --git a/homeassistant/components/asuswrt/translations/he.json b/homeassistant/components/asuswrt/translations/he.json index 867cc6e4f5c..fdc894892fa 100644 --- a/homeassistant/components/asuswrt/translations/he.json +++ b/homeassistant/components/asuswrt/translations/he.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." - }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_host": "\u05e9\u05dd \u05de\u05d0\u05e8\u05d7 \u05d0\u05d5 \u05db\u05ea\u05d5\u05d1\u05ea IP \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd", diff --git a/homeassistant/components/asuswrt/translations/hu.json b/homeassistant/components/asuswrt/translations/hu.json index 86339d7274c..34581ec6b7a 100644 --- a/homeassistant/components/asuswrt/translations/hu.json +++ b/homeassistant/components/asuswrt/translations/hu.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Lehetetlen \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3t meghat\u00e1rozni az eszk\u00f6zh\u00f6z", - "no_unique_id": "Az \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3 n\u00e9lk\u00fcli eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges", - "not_unique_id_exist": "Egy \u00e9rv\u00e9nyes UniqueID azonos\u00edt\u00f3val nem rendelkez\u0151 eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + "no_unique_id": "Az \u00e9rv\u00e9nyes egyedi azonos\u00edt\u00f3 n\u00e9lk\u00fcli eszk\u00f6z m\u00e1r konfigur\u00e1lva van. T\u00f6bb p\u00e9ld\u00e1ny konfigur\u00e1l\u00e1sa nem lehets\u00e9ges" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", diff --git a/homeassistant/components/asuswrt/translations/id.json b/homeassistant/components/asuswrt/translations/id.json index 249744a1ebe..758d88b9f48 100644 --- a/homeassistant/components/asuswrt/translations/id.json +++ b/homeassistant/components/asuswrt/translations/id.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Penentuan ID unik yang valid untuk perangkat tidak dimungkinkan", - "no_unique_id": "Perangkat tanpa ID unik yang valid sudah dikonfigurasi. Konfigurasi beberapa instans tidak dimungkinkan", - "not_unique_id_exist": "Perangkat tanpa ID unik yang valid sudah dikonfigurasi. Konfigurasi beberapa instans tidak dimungkinkan", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + "no_unique_id": "Perangkat tanpa ID unik yang valid sudah dikonfigurasi. Konfigurasi beberapa instans tidak dimungkinkan" }, "error": { "cannot_connect": "Gagal terhubung", diff --git a/homeassistant/components/asuswrt/translations/it.json b/homeassistant/components/asuswrt/translations/it.json index d2152c85357..38225900691 100644 --- a/homeassistant/components/asuswrt/translations/it.json +++ b/homeassistant/components/asuswrt/translations/it.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Impossibile determinare un ID univoco valido per il dispositivo", - "no_unique_id": "Un dispositivo senza un ID univoco valido \u00e8 gi\u00e0 configurato. La configurazione di pi\u00f9 istanze non \u00e8 possibile", - "not_unique_id_exist": "Un dispositivo senza un ID univoco valido \u00e8 gi\u00e0 configurato. La configurazione di pi\u00f9 istanze non \u00e8 possibile", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + "no_unique_id": "Un dispositivo senza un ID univoco valido \u00e8 gi\u00e0 configurato. La configurazione di pi\u00f9 istanze non \u00e8 possibile" }, "error": { "cannot_connect": "Impossibile connettersi", diff --git a/homeassistant/components/asuswrt/translations/ja.json b/homeassistant/components/asuswrt/translations/ja.json index 0ae15e8b86b..6bbe87fbf7e 100644 --- a/homeassistant/components/asuswrt/translations/ja.json +++ b/homeassistant/components/asuswrt/translations/ja.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "\u30c7\u30d0\u30a4\u30b9\u306e\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6c7a\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093", - "no_unique_id": "\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6301\u305f\u306a\u3044\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8907\u6570\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093", - "not_unique_id_exist": "\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6301\u305f\u306a\u3044\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8907\u6570\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + "no_unique_id": "\u6709\u52b9\u306a\u30e6\u30cb\u30fc\u30afID\u3092\u6301\u305f\u306a\u3044\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u8907\u6570\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u69cb\u6210\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", diff --git a/homeassistant/components/asuswrt/translations/ko.json b/homeassistant/components/asuswrt/translations/ko.json index 4e60a0d15b0..3825ed92b2e 100644 --- a/homeassistant/components/asuswrt/translations/ko.json +++ b/homeassistant/components/asuswrt/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + "invalid_unique_id": "\uae30\uae30\uc758 \uc720\ud6a8\ud55c \uace0\uc720 ID\ub97c \ud655\uc778\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", + "no_unique_id": "\uc720\ud6a8\ud55c \uace0\uc720 ID\uac00 \uc5c6\ub294 \uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.\n\uc5ec\ub7ec \uc778\uc2a4\ud134\uc2a4\ub97c \uad6c\uc131\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", diff --git a/homeassistant/components/asuswrt/translations/nl.json b/homeassistant/components/asuswrt/translations/nl.json index 4f3611289e5..17be650bf07 100644 --- a/homeassistant/components/asuswrt/translations/nl.json +++ b/homeassistant/components/asuswrt/translations/nl.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Onmogelijk om een geldige unieke id voor het apparaat te bepalen", - "no_unique_id": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk", - "not_unique_id_exist": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "no_unique_id": "Een apparaat zonder geldige unieke id is al geconfigureerd. Configuratie van meerdere instanties is niet mogelijk" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/asuswrt/translations/no.json b/homeassistant/components/asuswrt/translations/no.json index de540484cac..39539e74f1c 100644 --- a/homeassistant/components/asuswrt/translations/no.json +++ b/homeassistant/components/asuswrt/translations/no.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Umulig \u00e5 bestemme en gyldig unik ID for enheten", - "no_unique_id": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", - "not_unique_id_exist": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + "no_unique_id": "En enhet uten en gyldig unik ID er allerede konfigurert. Konfigurasjon av flere forekomster er ikke mulig" }, "error": { "cannot_connect": "Tilkobling mislyktes", diff --git a/homeassistant/components/asuswrt/translations/pl.json b/homeassistant/components/asuswrt/translations/pl.json index d30a1e9e778..7a03225633d 100644 --- a/homeassistant/components/asuswrt/translations/pl.json +++ b/homeassistant/components/asuswrt/translations/pl.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Nie mo\u017cna okre\u015bli\u0107 prawid\u0142owego unikalnego identyfikatora urz\u0105dzenia", - "no_unique_id": "Urz\u0105dzenie bez prawid\u0142owego unikalnego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa.", - "not_unique_id_exist": "Urz\u0105dzenie bez prawid\u0142owego unikalnego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa.", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + "no_unique_id": "Urz\u0105dzenie bez prawid\u0142owego unikalnego identyfikatora jest ju\u017c skonfigurowane. Konfiguracja wielu instancji nie jest mo\u017cliwa." }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", diff --git a/homeassistant/components/asuswrt/translations/pt-BR.json b/homeassistant/components/asuswrt/translations/pt-BR.json index 24f2ab51305..a42cab6fe0d 100644 --- a/homeassistant/components/asuswrt/translations/pt-BR.json +++ b/homeassistant/components/asuswrt/translations/pt-BR.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Imposs\u00edvel determinar um ID exclusivo v\u00e1lido para o dispositivo", - "no_unique_id": "Um dispositivo sem um ID exclusivo v\u00e1lido j\u00e1 est\u00e1 configurado. A configura\u00e7\u00e3o de v\u00e1rias inst\u00e2ncias n\u00e3o \u00e9 poss\u00edvel", - "not_unique_id_exist": "Um dispositivo sem um ID exclusivo v\u00e1lido j\u00e1 est\u00e1 configurado. A configura\u00e7\u00e3o de v\u00e1rias inst\u00e2ncias n\u00e3o \u00e9 poss\u00edvel", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + "no_unique_id": "[%key:component::asuswrt::config::abort::not_unique_id_exist%]" }, "error": { "cannot_connect": "Falha ao conectar", diff --git a/homeassistant/components/asuswrt/translations/ru.json b/homeassistant/components/asuswrt/translations/ru.json index 923faf174a5..0253cd20d1b 100644 --- a/homeassistant/components/asuswrt/translations/ru.json +++ b/homeassistant/components/asuswrt/translations/ru.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", - "no_unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0435\u0437 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430.", - "not_unique_id_exist": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0435\u0437 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + "no_unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0431\u0435\u0437 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0443\u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", diff --git a/homeassistant/components/asuswrt/translations/sv.json b/homeassistant/components/asuswrt/translations/sv.json index ba461c681e3..057a107356a 100644 --- a/homeassistant/components/asuswrt/translations/sv.json +++ b/homeassistant/components/asuswrt/translations/sv.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Om\u00f6jligt att fastst\u00e4lla ett giltigt unikt ID f\u00f6r enheten", - "no_unique_id": "En enhet utan ett giltigt unikt ID \u00e4r redan konfigurerad. Konfiguration av flera instanser \u00e4r inte m\u00f6jlig", - "not_unique_id_exist": "En enhet utan ett giltigt unikt ID \u00e4r redan konfigurerad. Konfiguration av flera instanser \u00e4r inte m\u00f6jlig", - "single_instance_allowed": "Redan konfigurerad. Bara en konfiguration \u00e4r m\u00f6jlig." + "no_unique_id": "En enhet utan ett giltigt unikt ID \u00e4r redan konfigurerad. Konfiguration av flera instanser \u00e4r inte m\u00f6jlig" }, "error": { "cannot_connect": "Kunde inte ansluta", diff --git a/homeassistant/components/asuswrt/translations/tr.json b/homeassistant/components/asuswrt/translations/tr.json index 10be601fd10..177c069298f 100644 --- a/homeassistant/components/asuswrt/translations/tr.json +++ b/homeassistant/components/asuswrt/translations/tr.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "Cihaz i\u00e7in ge\u00e7erli bir benzersiz kimlik belirlemek imkans\u0131z", - "no_unique_id": "Ge\u00e7erli bir benzersiz kimli\u011fi olmayan bir cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Birden \u00e7ok \u00f6rne\u011fin yap\u0131land\u0131r\u0131lmas\u0131 m\u00fcmk\u00fcn de\u011fil", - "not_unique_id_exist": "Ge\u00e7erli bir benzersiz kimli\u011fi olmayan bir cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Birden \u00e7ok \u00f6rne\u011fin yap\u0131land\u0131r\u0131lmas\u0131 m\u00fcmk\u00fcn de\u011fil", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + "no_unique_id": "Ge\u00e7erli bir benzersiz kimli\u011fi olmayan bir cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Birden \u00e7ok \u00f6rne\u011fin yap\u0131land\u0131r\u0131lmas\u0131 m\u00fcmk\u00fcn de\u011fil" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", diff --git a/homeassistant/components/asuswrt/translations/zh-Hans.json b/homeassistant/components/asuswrt/translations/zh-Hans.json index 69f7bf98df3..a8e6dbf8c10 100644 --- a/homeassistant/components/asuswrt/translations/zh-Hans.json +++ b/homeassistant/components/asuswrt/translations/zh-Hans.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "single_instance_allowed": "\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86\uff0c\u4e14\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002" - }, "error": { "cannot_connect": "\u8fde\u63a5\u5931\u8d25", "invalid_host": "\u65e0\u6548\u7684\u4e3b\u673a\u5730\u5740\u6216 IP \u5730\u5740", diff --git a/homeassistant/components/asuswrt/translations/zh-Hant.json b/homeassistant/components/asuswrt/translations/zh-Hant.json index d5955313b93..a8c4d18023a 100644 --- a/homeassistant/components/asuswrt/translations/zh-Hant.json +++ b/homeassistant/components/asuswrt/translations/zh-Hant.json @@ -2,9 +2,7 @@ "config": { "abort": { "invalid_unique_id": "\u7121\u6cd5\u78ba\u8a8d\u88dd\u7f6e\u6709\u6548\u552f\u4e00 ID", - "no_unique_id": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b", - "not_unique_id_exist": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + "no_unique_id": "\u5df2\u8a2d\u5b9a\u4e0d\u5177\u6709\u6548\u552f\u4e00 ID \u7684\u88dd\u7f6e\uff0c\u7121\u6cd5\u8a2d\u5b9a\u591a\u500b\u5be6\u4f8b" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", diff --git a/homeassistant/components/aurora_abb_powerone/translations/bg.json b/homeassistant/components/aurora_abb_powerone/translations/bg.json index 88f52d84269..37b6f40c82e 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/bg.json +++ b/homeassistant/components/aurora_abb_powerone/translations/bg.json @@ -2,9 +2,6 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" - }, - "error": { - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" } } } \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/translations/ca.json b/homeassistant/components/aurora_abb_powerone/translations/ca.json index 98f77dad13b..43b4acd25ea 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/ca.json +++ b/homeassistant/components/aurora_abb_powerone/translations/ca.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "No s'ha pogut connectar, comprova el port s\u00e8rie, l'adre\u00e7a, la connexi\u00f3 el\u00e8ctrica i que l'inversor estigui enc\u00e8s", "cannot_open_serial_port": "No s'ha pogut obrir el port s\u00e8rie, comprova'l i torna-ho a provar", - "invalid_serial_port": "El port s\u00e8rie no t\u00e9 un dispositiu v\u00e0lid o no s'ha pogut obrir", - "unknown": "Error inesperat" + "invalid_serial_port": "El port s\u00e8rie no t\u00e9 un dispositiu v\u00e0lid o no s'ha pogut obrir" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/cs.json b/homeassistant/components/aurora_abb_powerone/translations/cs.json deleted file mode 100644 index e1bf8e7f45f..00000000000 --- a/homeassistant/components/aurora_abb_powerone/translations/cs.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "config": { - "error": { - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/translations/de.json b/homeassistant/components/aurora_abb_powerone/translations/de.json index a60138da16d..fab4d1f6e31 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/de.json +++ b/homeassistant/components/aurora_abb_powerone/translations/de.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Verbindung kann nicht hergestellt werden, bitte \u00fcberpr\u00fcfe den seriellen Anschluss, die Adresse, die elektrische Verbindung und ob der Wechselrichter eingeschaltet ist (bei Tageslicht)", "cannot_open_serial_port": "Serielle Schnittstelle kann nicht ge\u00f6ffnet werden, bitte pr\u00fcfen und erneut versuchen", - "invalid_serial_port": "Serielle Schnittstelle ist kein g\u00fcltiges Ger\u00e4t oder konnte nicht ge\u00f6ffnet werden", - "unknown": "Unerwarteter Fehler" + "invalid_serial_port": "Serielle Schnittstelle ist kein g\u00fcltiges Ger\u00e4t oder konnte nicht ge\u00f6ffnet werden" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/el.json b/homeassistant/components/aurora_abb_powerone/translations/el.json index 834c5794861..52b43a668be 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/el.json +++ b/homeassistant/components/aurora_abb_powerone/translations/el.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7, \u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1, \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7, \u03c4\u03b7\u03bd \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03b9\u03ba\u03ae \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03cc\u03c4\u03b9 \u03bf \u03bc\u03b5\u03c4\u03b1\u03c4\u03c1\u03bf\u03c0\u03ad\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 (\u03c3\u03c4\u03bf \u03c6\u03c9\u03c2 \u03c4\u03b7\u03c2 \u03b7\u03bc\u03ad\u03c1\u03b1\u03c2)", "cannot_open_serial_port": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc \u03c4\u03bf \u03ac\u03bd\u03bf\u03b9\u03b3\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae\u03c2 \u03b8\u03cd\u03c1\u03b1\u03c2, \u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac", - "invalid_serial_port": "\u0397 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ae \u03b4\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc \u03bd\u03b1 \u03b1\u03bd\u03bf\u03af\u03be\u03b5\u03b9", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "invalid_serial_port": "\u0397 \u03c3\u03b5\u03b9\u03c1\u03b9\u03b1\u03ba\u03ae \u03b8\u03cd\u03c1\u03b1 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ae \u03b4\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc \u03bd\u03b1 \u03b1\u03bd\u03bf\u03af\u03be\u03b5\u03b9" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/en.json b/homeassistant/components/aurora_abb_powerone/translations/en.json index fe5d668f573..748a570f291 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/en.json +++ b/homeassistant/components/aurora_abb_powerone/translations/en.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Unable to connect, please check serial port, address, electrical connection and that inverter is on (in daylight)", "cannot_open_serial_port": "Cannot open serial port, please check and try again", - "invalid_serial_port": "Serial port is not a valid device or could not be openned", - "unknown": "Unexpected error" + "invalid_serial_port": "Serial port is not a valid device or could not be openned" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/en_GB.json b/homeassistant/components/aurora_abb_powerone/translations/en_GB.json index 59c6263bd14..25f797239b9 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/en_GB.json +++ b/homeassistant/components/aurora_abb_powerone/translations/en_GB.json @@ -1,8 +1,5 @@ { "config": { - "error": { - "unknown": "Unexpected error" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/es.json b/homeassistant/components/aurora_abb_powerone/translations/es.json index f71039bda56..4e1d95a126d 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/es.json +++ b/homeassistant/components/aurora_abb_powerone/translations/es.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "No se puede conectar, por favor, compruebe el puerto serie, la direcci\u00f3n, la conexi\u00f3n el\u00e9ctrica y que el inversor est\u00e1 encendido", "cannot_open_serial_port": "No se puede abrir el puerto serie, por favor, compruebe y vuelva a intentarlo", - "invalid_serial_port": "El puerto serie no es v\u00e1lido para este dispositivo o no se ha podido abrir", - "unknown": "Error inesperado" + "invalid_serial_port": "El puerto serie no es v\u00e1lido para este dispositivo o no se ha podido abrir" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/et.json b/homeassistant/components/aurora_abb_powerone/translations/et.json index b7764fa0f33..115bab56eca 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/et.json +++ b/homeassistant/components/aurora_abb_powerone/translations/et.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "\u00dchendust ei saa luua, palun kontrolli jadaporti, aadressi, elektri\u00fchendust ja et inverter on sisse l\u00fclitatud (p\u00e4evavalguses)", "cannot_open_serial_port": "Jadaporti ei saa avada, kontrolli ja proovi uuesti", - "invalid_serial_port": "Jadaport pole sobiv seade v\u00f5i seda ei saa avada", - "unknown": "Ootamatu t\u00f5rge" + "invalid_serial_port": "Jadaport pole sobiv seade v\u00f5i seda ei saa avada" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/fr.json b/homeassistant/components/aurora_abb_powerone/translations/fr.json index 206167a5669..c36cc62196f 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/fr.json +++ b/homeassistant/components/aurora_abb_powerone/translations/fr.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Connexion impossible, veuillez v\u00e9rifier le port s\u00e9rie, l'adresse, la connexion \u00e9lectrique et que l'onduleur est allum\u00e9 (\u00e0 la lumi\u00e8re du jour)", "cannot_open_serial_port": "Impossible d'ouvrir le port s\u00e9rie, veuillez v\u00e9rifier et r\u00e9essayer", - "invalid_serial_port": "Le port s\u00e9rie n'est pas un p\u00e9riph\u00e9rique valide ou n'a pas pu \u00eatre ouvert", - "unknown": "Erreur inattendue" + "invalid_serial_port": "Le port s\u00e9rie n'est pas un p\u00e9riph\u00e9rique valide ou n'a pas pu \u00eatre ouvert" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/he.json b/homeassistant/components/aurora_abb_powerone/translations/he.json index ea40181bd9a..cdb921611c4 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/he.json +++ b/homeassistant/components/aurora_abb_powerone/translations/he.json @@ -2,9 +2,6 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" - }, - "error": { - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" } } } \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/translations/hu.json b/homeassistant/components/aurora_abb_powerone/translations/hu.json index ffc812dfd4b..6342466bffe 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/hu.json +++ b/homeassistant/components/aurora_abb_powerone/translations/hu.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "A csatlakoz\u00e1s sikertelen. Ellen\u0151rizze a soros portot, a c\u00edmet, az elektromos csatlakoz\u00e1st \u00e9s azt, hogy az inverter be van-e kapcsolva (nappal).", "cannot_open_serial_port": "A soros port nem nyithat\u00f3 meg, k\u00e9rem ellen\u0151rizze \u00e9s pr\u00f3b\u00e1lkozzon \u00fajra", - "invalid_serial_port": "A soros port nem \u00e9rv\u00e9nyes eszk\u00f6z, vagy nem nyithat\u00f3 meg", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + "invalid_serial_port": "A soros port nem \u00e9rv\u00e9nyes eszk\u00f6z, vagy nem nyithat\u00f3 meg" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/id.json b/homeassistant/components/aurora_abb_powerone/translations/id.json index 4157502c2ad..70811e170a1 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/id.json +++ b/homeassistant/components/aurora_abb_powerone/translations/id.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Tidak dapat terhubung, periksa port serial, alamat, koneksi listrik dan apakah inverter sedang nyala (di siang hari)", "cannot_open_serial_port": "Tidak dapat membuka port serial, periksa dan coba lagi", - "invalid_serial_port": "Port serial bukan perangkat yang valid atau tidak dapat dibuka", - "unknown": "Kesalahan yang tidak diharapkan" + "invalid_serial_port": "Port serial bukan perangkat yang valid atau tidak dapat dibuka" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/it.json b/homeassistant/components/aurora_abb_powerone/translations/it.json index bb534d079cc..765aaf5e9ed 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/it.json +++ b/homeassistant/components/aurora_abb_powerone/translations/it.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Impossibile connettersi, controllare la porta seriale, l'indirizzo, la connessione elettrica e che l'inverter sia acceso (alla luce del giorno)", "cannot_open_serial_port": "Impossibile aprire la porta seriale, controllare e riprovare", - "invalid_serial_port": "La porta seriale non \u00e8 un dispositivo valido o non pu\u00f2 essere aperta", - "unknown": "Errore imprevisto" + "invalid_serial_port": "La porta seriale non \u00e8 un dispositivo valido o non pu\u00f2 essere aperta" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/ja.json b/homeassistant/components/aurora_abb_powerone/translations/ja.json index a558f088f07..5977b1513b0 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/ja.json +++ b/homeassistant/components/aurora_abb_powerone/translations/ja.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u3067\u304d\u306a\u3044\u5834\u5408\u306f\u3001\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3001\u30a2\u30c9\u30ec\u30b9\u3001\u96fb\u6c17\u7684\u63a5\u7d9a\u3092\u78ba\u8a8d\u3057\u3001\u30a4\u30f3\u30d0\u30fc\u30bf\u30fc\u306e\u96fb\u6e90\u304c\u5165\u3063\u3066\u3044\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044(\u663c\u9593)", "cannot_open_serial_port": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u3092\u958b\u3051\u307e\u305b\u3093\u3002\u78ba\u8a8d\u3057\u3066\u304b\u3089\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044", - "invalid_serial_port": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u304c\u6709\u52b9\u306a\u30c7\u30d0\u30a4\u30b9\u3067\u306f\u306a\u3044\u3001\u3082\u3057\u304f\u306f\u958b\u304f\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + "invalid_serial_port": "\u30b7\u30ea\u30a2\u30eb\u30dd\u30fc\u30c8\u304c\u6709\u52b9\u306a\u30c7\u30d0\u30a4\u30b9\u3067\u306f\u306a\u3044\u3001\u3082\u3057\u304f\u306f\u958b\u304f\u3053\u3068\u304c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/nl.json b/homeassistant/components/aurora_abb_powerone/translations/nl.json index d70113e9c19..cb5cd511f81 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/nl.json +++ b/homeassistant/components/aurora_abb_powerone/translations/nl.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Kan geen verbinding maken, controleer de seri\u00eble poort, het adres, de elektrische aansluiting en of de omvormer aan staat (bij daglicht)", "cannot_open_serial_port": "Kan seri\u00eble poort niet openen, controleer en probeer het opnieuw", - "invalid_serial_port": "Seri\u00eble poort is geen geldig apparaat of kan niet worden geopend", - "unknown": "Onverwachte fout" + "invalid_serial_port": "Seri\u00eble poort is geen geldig apparaat of kan niet worden geopend" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/no.json b/homeassistant/components/aurora_abb_powerone/translations/no.json index 9d4cd656f45..38cef3a2c96 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/no.json +++ b/homeassistant/components/aurora_abb_powerone/translations/no.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Kan ikke koble til, sjekk seriell port, adresse, elektrisk tilkobling og at omformeren er p\u00e5 (i dagslys)", "cannot_open_serial_port": "Kan ikke \u00e5pne serieporten, sjekk og pr\u00f8v igjen", - "invalid_serial_port": "Seriell port er ikke en gyldig enhet eller kunne ikke \u00e5pnes", - "unknown": "Uventet feil" + "invalid_serial_port": "Seriell port er ikke en gyldig enhet eller kunne ikke \u00e5pnes" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/pl.json b/homeassistant/components/aurora_abb_powerone/translations/pl.json index d6131cfa195..3b773295256 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/pl.json +++ b/homeassistant/components/aurora_abb_powerone/translations/pl.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia, sprawd\u017a port szeregowy, adres, po\u0142\u0105czenie elektryczne i czy falownik jest w\u0142\u0105czony (w \u015bwietle dziennym)", "cannot_open_serial_port": "Nie mo\u017cna otworzy\u0107 portu szeregowego, sprawd\u017a i spr\u00f3buj ponownie", - "invalid_serial_port": "Port szeregowy nie jest prawid\u0142owym urz\u0105dzeniem lub nie mo\u017cna go otworzy\u0107", - "unknown": "Nieoczekiwany b\u0142\u0105d" + "invalid_serial_port": "Port szeregowy nie jest prawid\u0142owym urz\u0105dzeniem lub nie mo\u017cna go otworzy\u0107" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/pt-BR.json b/homeassistant/components/aurora_abb_powerone/translations/pt-BR.json index 8fb79bcefa2..82dbcb466ec 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/pt-BR.json +++ b/homeassistant/components/aurora_abb_powerone/translations/pt-BR.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "N\u00e3o \u00e9 poss\u00edvel conectar, verifique a porta serial, endere\u00e7o, conex\u00e3o el\u00e9trica e se o inversor est\u00e1 ligado (\u00e0 luz do dia)", "cannot_open_serial_port": "N\u00e3o \u00e9 poss\u00edvel abrir a porta serial, verifique e tente novamente", - "invalid_serial_port": "A porta serial n\u00e3o \u00e9 um dispositivo v\u00e1lido ou n\u00e3o p\u00f4de ser aberta", - "unknown": "Erro inesperado" + "invalid_serial_port": "A porta serial n\u00e3o \u00e9 um dispositivo v\u00e1lido ou n\u00e3o p\u00f4de ser aberta" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/ru.json b/homeassistant/components/aurora_abb_powerone/translations/ru.json index 6baec9324d2..22e73931232 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/ru.json +++ b/homeassistant/components/aurora_abb_powerone/translations/ru.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442, \u0430\u0434\u0440\u0435\u0441, \u044d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0438 \u0447\u0442\u043e \u0438\u043d\u0432\u0435\u0440\u0442\u043e\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d (\u043f\u0440\u0438 \u0434\u043d\u0435\u0432\u043d\u043e\u043c \u0441\u0432\u0435\u0442\u0435).", "cannot_open_serial_port": "\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0435 \u0440\u0430\u0437.", - "invalid_serial_port": "\u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c \u0438\u043b\u0438 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "invalid_serial_port": "\u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c \u0438\u043b\u0438 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442." }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/sl.json b/homeassistant/components/aurora_abb_powerone/translations/sl.json index 87fce100c77..16a4bd0b1c1 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/sl.json +++ b/homeassistant/components/aurora_abb_powerone/translations/sl.json @@ -2,9 +2,6 @@ "config": { "abort": { "already_configured": "Naprava je \u017ee konfigurirana" - }, - "error": { - "unknown": "Nepri\u010dakovana napaka" } } } \ No newline at end of file diff --git a/homeassistant/components/aurora_abb_powerone/translations/tr.json b/homeassistant/components/aurora_abb_powerone/translations/tr.json index ec8ee6da4a3..39a046adf43 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/tr.json +++ b/homeassistant/components/aurora_abb_powerone/translations/tr.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "Ba\u011flant\u0131 kurulam\u0131yor, l\u00fctfen seri portu, adresi, elektrik ba\u011flant\u0131s\u0131n\u0131 ve invert\u00f6r\u00fcn a\u00e7\u0131k oldu\u011funu (g\u00fcn \u0131\u015f\u0131\u011f\u0131nda) kontrol edin.", "cannot_open_serial_port": "Seri ba\u011flant\u0131 noktas\u0131 a\u00e7\u0131lam\u0131yor, l\u00fctfen kontrol edip tekrar deneyin", - "invalid_serial_port": "Seri ba\u011flant\u0131 noktas\u0131 ge\u00e7erli bir ayg\u0131t de\u011fil veya a\u00e7\u0131lamad\u0131", - "unknown": "Beklenmeyen hata" + "invalid_serial_port": "Seri ba\u011flant\u0131 noktas\u0131 ge\u00e7erli bir ayg\u0131t de\u011fil veya a\u00e7\u0131lamad\u0131" }, "step": { "user": { diff --git a/homeassistant/components/aurora_abb_powerone/translations/zh-Hant.json b/homeassistant/components/aurora_abb_powerone/translations/zh-Hant.json index 0a05e640994..bec84f72b80 100644 --- a/homeassistant/components/aurora_abb_powerone/translations/zh-Hant.json +++ b/homeassistant/components/aurora_abb_powerone/translations/zh-Hant.json @@ -7,8 +7,7 @@ "error": { "cannot_connect": "\u7121\u6cd5\u9023\u63a5\uff0c\u8acb\u6aa2\u67e5\u5e8f\u5217\u57e0\u3001\u4f4d\u5740\u3001\u96fb\u529b\u9023\u63a5\uff0c\u4e26\u78ba\u5b9a\u8a72\u8b8a\u6d41\u5668\u70ba\u958b\u555f\u72c0\u614b\uff08\u767d\u5929\uff09", "cannot_open_serial_port": "\u7121\u6cd5\u958b\u555f\u5e8f\u5217\u57e0\u3001\u8acb\u6aa2\u67e5\u5f8c\u518d\u8a66\u4e00\u6b21", - "invalid_serial_port": "\u5e8f\u5217\u57e0\u70ba\u7121\u6548\u88dd\u7f6e\u6216\u7121\u6cd5\u958b\u555f", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + "invalid_serial_port": "\u5e8f\u5217\u57e0\u70ba\u7121\u6548\u88dd\u7f6e\u6216\u7121\u6cd5\u958b\u555f" }, "step": { "user": { diff --git a/homeassistant/components/aussie_broadband/translations/bg.json b/homeassistant/components/aussie_broadband/translations/bg.json index 8c36ef66120..8098935e86c 100644 --- a/homeassistant/components/aussie_broadband/translations/bg.json +++ b/homeassistant/components/aussie_broadband/translations/bg.json @@ -11,13 +11,6 @@ "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - }, - "description": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 \u0437\u0430 {username}", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" diff --git a/homeassistant/components/aussie_broadband/translations/ca.json b/homeassistant/components/aussie_broadband/translations/ca.json index 83ec53ad5a4..4688357a3c8 100644 --- a/homeassistant/components/aussie_broadband/translations/ca.json +++ b/homeassistant/components/aussie_broadband/translations/ca.json @@ -11,13 +11,6 @@ "unknown": "Error inesperat" }, "step": { - "reauth": { - "data": { - "password": "Contrasenya" - }, - "description": "Actualitza la contrasenya de {username}", - "title": "Reautenticaci\u00f3 de la integraci\u00f3" - }, "reauth_confirm": { "data": { "password": "Contrasenya" diff --git a/homeassistant/components/aussie_broadband/translations/cs.json b/homeassistant/components/aussie_broadband/translations/cs.json index 8ea2970a132..131dddca93a 100644 --- a/homeassistant/components/aussie_broadband/translations/cs.json +++ b/homeassistant/components/aussie_broadband/translations/cs.json @@ -10,12 +10,6 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "reauth": { - "data": { - "password": "Heslo" - }, - "title": "Znovu ov\u011b\u0159it integraci" - }, "user": { "data": { "password": "Heslo", diff --git a/homeassistant/components/aussie_broadband/translations/de.json b/homeassistant/components/aussie_broadband/translations/de.json index ed5cd5fd02c..e4c5bd327ff 100644 --- a/homeassistant/components/aussie_broadband/translations/de.json +++ b/homeassistant/components/aussie_broadband/translations/de.json @@ -11,13 +11,6 @@ "unknown": "Unerwarteter Fehler" }, "step": { - "reauth": { - "data": { - "password": "Passwort" - }, - "description": "Passwort f\u00fcr {username} aktualisieren", - "title": "Integration erneut authentifizieren" - }, "reauth_confirm": { "data": { "password": "Passwort" diff --git a/homeassistant/components/aussie_broadband/translations/el.json b/homeassistant/components/aussie_broadband/translations/el.json index 0b78eacb826..c1e36e01654 100644 --- a/homeassistant/components/aussie_broadband/translations/el.json +++ b/homeassistant/components/aussie_broadband/translations/el.json @@ -11,13 +11,6 @@ "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { - "reauth": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03cd \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 {username}", - "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" - }, "reauth_confirm": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/aussie_broadband/translations/en.json b/homeassistant/components/aussie_broadband/translations/en.json index 2843916df2e..4d18251f270 100644 --- a/homeassistant/components/aussie_broadband/translations/en.json +++ b/homeassistant/components/aussie_broadband/translations/en.json @@ -11,13 +11,6 @@ "unknown": "Unexpected error" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "Update password for {username}", - "title": "Reauthenticate Integration" - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/aussie_broadband/translations/es-419.json b/homeassistant/components/aussie_broadband/translations/es-419.json index df9322bf01d..f90e4891935 100644 --- a/homeassistant/components/aussie_broadband/translations/es-419.json +++ b/homeassistant/components/aussie_broadband/translations/es-419.json @@ -4,9 +4,6 @@ "no_services_found": "No se encontraron servicios para esta cuenta" }, "step": { - "reauth": { - "description": "Actualizar contrase\u00f1a para {username}" - }, "service": { "data": { "services": "Servicios" diff --git a/homeassistant/components/aussie_broadband/translations/es.json b/homeassistant/components/aussie_broadband/translations/es.json index e0df929fe9e..1410af6ad76 100644 --- a/homeassistant/components/aussie_broadband/translations/es.json +++ b/homeassistant/components/aussie_broadband/translations/es.json @@ -9,10 +9,6 @@ "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "step": { - "reauth": { - "description": "Actualizar la contrase\u00f1a de {username}", - "title": "Reautenticaci\u00f3n de la integraci\u00f3n" - }, "reauth_confirm": { "data": { "password": "Contrase\u00f1a" diff --git a/homeassistant/components/aussie_broadband/translations/et.json b/homeassistant/components/aussie_broadband/translations/et.json index 7dbf2d26b59..0d0d97b6ff2 100644 --- a/homeassistant/components/aussie_broadband/translations/et.json +++ b/homeassistant/components/aussie_broadband/translations/et.json @@ -11,13 +11,6 @@ "unknown": "Ootamatu t\u00f5rge" }, "step": { - "reauth": { - "data": { - "password": "Salas\u00f5na" - }, - "description": "{username} salas\u00f5na v\u00e4rskendamine", - "title": "Taastuvasta sidumine" - }, "reauth_confirm": { "data": { "password": "Salas\u00f5na" diff --git a/homeassistant/components/aussie_broadband/translations/fr.json b/homeassistant/components/aussie_broadband/translations/fr.json index d540e5fd2f9..37d570e77d6 100644 --- a/homeassistant/components/aussie_broadband/translations/fr.json +++ b/homeassistant/components/aussie_broadband/translations/fr.json @@ -11,13 +11,6 @@ "unknown": "Erreur inattendue" }, "step": { - "reauth": { - "data": { - "password": "Mot de passe" - }, - "description": "Mettre \u00e0 jour le mot de passe pour {username}", - "title": "R\u00e9-authentifier l'int\u00e9gration" - }, "reauth_confirm": { "data": { "password": "Mot de passe" diff --git a/homeassistant/components/aussie_broadband/translations/he.json b/homeassistant/components/aussie_broadband/translations/he.json index 5861357a6d4..7a1ef5f9cc5 100644 --- a/homeassistant/components/aussie_broadband/translations/he.json +++ b/homeassistant/components/aussie_broadband/translations/he.json @@ -10,12 +10,6 @@ "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { - "reauth": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" - }, "reauth_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4" diff --git a/homeassistant/components/aussie_broadband/translations/hu.json b/homeassistant/components/aussie_broadband/translations/hu.json index a8c3543873d..2ad64c2155d 100644 --- a/homeassistant/components/aussie_broadband/translations/hu.json +++ b/homeassistant/components/aussie_broadband/translations/hu.json @@ -11,13 +11,6 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { - "reauth": { - "data": { - "password": "Jelsz\u00f3" - }, - "description": "Jelsz\u00f3 friss\u00edt\u00e9se {username} sz\u00e1m\u00e1ra", - "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" - }, "reauth_confirm": { "data": { "password": "Jelsz\u00f3" diff --git a/homeassistant/components/aussie_broadband/translations/id.json b/homeassistant/components/aussie_broadband/translations/id.json index 18020014268..d3353b46f3c 100644 --- a/homeassistant/components/aussie_broadband/translations/id.json +++ b/homeassistant/components/aussie_broadband/translations/id.json @@ -11,13 +11,6 @@ "unknown": "Kesalahan yang tidak diharapkan" }, "step": { - "reauth": { - "data": { - "password": "Kata Sandi" - }, - "description": "Perbarui kata sandi untuk {username}", - "title": "Autentikasi Ulang Integrasi" - }, "reauth_confirm": { "data": { "password": "Kata Sandi" diff --git a/homeassistant/components/aussie_broadband/translations/it.json b/homeassistant/components/aussie_broadband/translations/it.json index a29e10db60a..6c53ec28dff 100644 --- a/homeassistant/components/aussie_broadband/translations/it.json +++ b/homeassistant/components/aussie_broadband/translations/it.json @@ -11,13 +11,6 @@ "unknown": "Errore imprevisto" }, "step": { - "reauth": { - "data": { - "password": "Password" - }, - "description": "Aggiorna la password per {username}", - "title": "Autentica nuovamente l'integrazione" - }, "reauth_confirm": { "data": { "password": "Password" diff --git a/homeassistant/components/aussie_broadband/translations/ja.json b/homeassistant/components/aussie_broadband/translations/ja.json index f08e02f73c1..d8351bbc98b 100644 --- a/homeassistant/components/aussie_broadband/translations/ja.json +++ b/homeassistant/components/aussie_broadband/translations/ja.json @@ -11,13 +11,6 @@ "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { - "reauth": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "{username} \u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u66f4\u65b0\u3057\u307e\u3059", - "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" - }, "reauth_confirm": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" diff --git a/homeassistant/components/aussie_broadband/translations/nl.json b/homeassistant/components/aussie_broadband/translations/nl.json index 21da52666bc..ec95145c67b 100644 --- a/homeassistant/components/aussie_broadband/translations/nl.json +++ b/homeassistant/components/aussie_broadband/translations/nl.json @@ -11,13 +11,6 @@ "unknown": "Onverwachte fout" }, "step": { - "reauth": { - "data": { - "password": "Wachtwoord" - }, - "description": "Update wachtwoord voor {username}", - "title": "Verifieer de integratie opnieuw" - }, "reauth_confirm": { "data": { "password": "Wachtwoord" diff --git a/homeassistant/components/aussie_broadband/translations/no.json b/homeassistant/components/aussie_broadband/translations/no.json index 8129a8c9cc2..00e911bbfba 100644 --- a/homeassistant/components/aussie_broadband/translations/no.json +++ b/homeassistant/components/aussie_broadband/translations/no.json @@ -11,13 +11,6 @@ "unknown": "Uventet feil" }, "step": { - "reauth": { - "data": { - "password": "Passord" - }, - "description": "Oppdater passordet for {username}", - "title": "Godkjenne integrering p\u00e5 nytt" - }, "reauth_confirm": { "data": { "password": "Passord" diff --git a/homeassistant/components/aussie_broadband/translations/pl.json b/homeassistant/components/aussie_broadband/translations/pl.json index c2f686c11dc..ed6860b31f8 100644 --- a/homeassistant/components/aussie_broadband/translations/pl.json +++ b/homeassistant/components/aussie_broadband/translations/pl.json @@ -11,13 +11,6 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { - "reauth": { - "data": { - "password": "Has\u0142o" - }, - "description": "Zaktualizuj has\u0142o dla {username}", - "title": "Ponownie uwierzytelnij integracj\u0119" - }, "reauth_confirm": { "data": { "password": "Has\u0142o" diff --git a/homeassistant/components/aussie_broadband/translations/pt-BR.json b/homeassistant/components/aussie_broadband/translations/pt-BR.json index efe1fca7c80..884d426a04b 100644 --- a/homeassistant/components/aussie_broadband/translations/pt-BR.json +++ b/homeassistant/components/aussie_broadband/translations/pt-BR.json @@ -11,13 +11,6 @@ "unknown": "Erro inesperado" }, "step": { - "reauth": { - "data": { - "password": "Senha" - }, - "description": "Atualizar senha para {username}", - "title": "Reautenticar Integra\u00e7\u00e3o" - }, "reauth_confirm": { "data": { "password": "Senha" diff --git a/homeassistant/components/aussie_broadband/translations/ru.json b/homeassistant/components/aussie_broadband/translations/ru.json index 15a9f44a98a..24a3c3d67fb 100644 --- a/homeassistant/components/aussie_broadband/translations/ru.json +++ b/homeassistant/components/aussie_broadband/translations/ru.json @@ -11,13 +11,6 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043b\u044f {username}.", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" diff --git a/homeassistant/components/aussie_broadband/translations/sv.json b/homeassistant/components/aussie_broadband/translations/sv.json index a82ed912c19..b159fb71b87 100644 --- a/homeassistant/components/aussie_broadband/translations/sv.json +++ b/homeassistant/components/aussie_broadband/translations/sv.json @@ -11,13 +11,6 @@ "unknown": "Ov\u00e4ntat fel" }, "step": { - "reauth": { - "data": { - "password": "L\u00f6senord" - }, - "description": "Uppdatera l\u00f6senord f\u00f6r {username}", - "title": "\u00c5terautentisera integration" - }, "service": { "data": { "services": "Tj\u00e4nster" diff --git a/homeassistant/components/aussie_broadband/translations/tr.json b/homeassistant/components/aussie_broadband/translations/tr.json index 28eae33719d..e9d656acb26 100644 --- a/homeassistant/components/aussie_broadband/translations/tr.json +++ b/homeassistant/components/aussie_broadband/translations/tr.json @@ -11,13 +11,6 @@ "unknown": "Beklenmeyen hata" }, "step": { - "reauth": { - "data": { - "password": "Parola" - }, - "description": "{username} i\u00e7in \u015fifreyi g\u00fcncelleyin", - "title": "Entegrasyonu Yeniden Do\u011frula" - }, "reauth_confirm": { "data": { "password": "Parola" diff --git a/homeassistant/components/aussie_broadband/translations/zh-Hant.json b/homeassistant/components/aussie_broadband/translations/zh-Hant.json index f7beefb2962..5f248360de1 100644 --- a/homeassistant/components/aussie_broadband/translations/zh-Hant.json +++ b/homeassistant/components/aussie_broadband/translations/zh-Hant.json @@ -11,13 +11,6 @@ "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { - "reauth": { - "data": { - "password": "\u5bc6\u78bc" - }, - "description": "\u66f4\u65b0 {username} \u5bc6\u78bc", - "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" - }, "reauth_confirm": { "data": { "password": "\u5bc6\u78bc" diff --git a/homeassistant/components/azure_event_hub/translations/ko.json b/homeassistant/components/azure_event_hub/translations/ko.json new file mode 100644 index 00000000000..814b5cba06f --- /dev/null +++ b/homeassistant/components/azure_event_hub/translations/ko.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "cannot_connect": "configuration.yaml\uc758 \uc815\ubcf4\ub85c \uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. yaml\uc5d0\uc11c \uc81c\uac70\ud558\uace0 \uad6c\uc131 \ud750\ub984\uc744 \uc0ac\uc6a9\ud558\uc138\uc694.", + "unknown": "\uc54c \uc218 \uc5c6\ub294 \uc624\ub958\ub85c \uc778\ud574 configuration.yaml\uc758 \uc815\ubcf4\ub85c \uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. yaml\uc5d0\uc11c \uc81c\uac70\ud558\uace0 \uad6c\uc131 \ud750\ub984\uc744 \uc0ac\uc6a9\ud558\uc138\uc694." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/cs.json b/homeassistant/components/baf/translations/cs.json new file mode 100644 index 00000000000..04f18366eaf --- /dev/null +++ b/homeassistant/components/baf/translations/cs.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" + }, + "error": { + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "ip_address": "IP adresa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/baf/translations/he.json b/homeassistant/components/baf/translations/he.json new file mode 100644 index 00000000000..61151ab6737 --- /dev/null +++ b/homeassistant/components/baf/translations/he.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "user": { + "data": { + "ip_address": "\u05db\u05ea\u05d5\u05d1\u05ea IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/binary_sensor/translations/ca.json b/homeassistant/components/binary_sensor/translations/ca.json index 4c27c1e2966..10d705e797b 100644 --- a/homeassistant/components/binary_sensor/translations/ca.json +++ b/homeassistant/components/binary_sensor/translations/ca.json @@ -59,8 +59,6 @@ "connected": "{entity_name} est\u00e0 connectat", "gas": "{entity_name} ha comen\u00e7at a detectar gas", "hot": "{entity_name} es torna calent", - "is_not_tampered": "{entity_name} ha deixat de detectar manipulaci\u00f3", - "is_tampered": "{entity_name} ha comen\u00e7at a detectar manipulaci\u00f3", "light": "{entity_name} ha comen\u00e7at a detectar llum", "locked": "{entity_name} est\u00e0 bloquejat", "moist": "{entity_name} es torna humit", @@ -138,10 +136,6 @@ "off": "Lliure", "on": "Detectat" }, - "co": { - "off": "Lliure", - "on": "Detectat" - }, "cold": { "off": "Normal", "on": "Fred" diff --git a/homeassistant/components/binary_sensor/translations/cs.json b/homeassistant/components/binary_sensor/translations/cs.json index ced02ffc326..b8793a2c087 100644 --- a/homeassistant/components/binary_sensor/translations/cs.json +++ b/homeassistant/components/binary_sensor/translations/cs.json @@ -53,8 +53,6 @@ "connected": "{entity_name} p\u0159ipojeno", "gas": "{entity_name} za\u010dalo detekovat plyn", "hot": "{entity_name} se zah\u0159\u00e1l", - "is_not_tampered": "{entity_name} p\u0159estalo detekovat neopr\u00e1vn\u011bnou manipulaci", - "is_tampered": "{entity_name} za\u010dalo detekovat neopr\u00e1vn\u011bnou manipulaci", "light": "{entity_name} za\u010dalo detekovat sv\u011btlo", "locked": "{entity_name} zam\u010deno", "moist": "{entity_name} zvlhnul", diff --git a/homeassistant/components/binary_sensor/translations/de.json b/homeassistant/components/binary_sensor/translations/de.json index 1d6257c48c6..85163bce0a3 100644 --- a/homeassistant/components/binary_sensor/translations/de.json +++ b/homeassistant/components/binary_sensor/translations/de.json @@ -59,8 +59,6 @@ "connected": "{entity_name} verbunden", "gas": "{entity_name} hat Gas detektiert", "hot": "{entity_name} wurde hei\u00df", - "is_not_tampered": "{entity_name} hat aufgeh\u00f6rt, Manipulationen zu erkennen", - "is_tampered": "{entity_name} hat begonnen, Manipulationen zu erkennen", "light": "{entity_name} hat Licht detektiert", "locked": "{entity_name} gesperrt", "moist": "{entity_name} wurde feucht", @@ -138,10 +136,6 @@ "off": "Normal", "on": "Erkannt" }, - "co": { - "off": "Normal", - "on": "Erkannt" - }, "cold": { "off": "Normal", "on": "Kalt" diff --git a/homeassistant/components/binary_sensor/translations/el.json b/homeassistant/components/binary_sensor/translations/el.json index ec78f1cd575..ed291a70f0d 100644 --- a/homeassistant/components/binary_sensor/translations/el.json +++ b/homeassistant/components/binary_sensor/translations/el.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5", "gas": "{entity_name} \u03ac\u03c1\u03c7\u03b9\u03c3\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b9\u03c7\u03bd\u03b5\u03cd\u03b5\u03b9 \u03b1\u03ad\u03c1\u03b9\u03bf", "hot": "{entity_name} \u03b6\u03b5\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5", - "is_not_tampered": "{entity_name} \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03b6\u03b5\u03b9 \u03c0\u03b1\u03c1\u03b1\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", - "is_tampered": "{entity_name} \u03ac\u03c1\u03c7\u03b9\u03c3\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03b6\u03b5\u03b9 \u03c0\u03b1\u03c1\u03b1\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", "light": "{entity_name} \u03ac\u03c1\u03c7\u03b9\u03c3\u03b5 \u03bd\u03b1 \u03b1\u03bd\u03b9\u03c7\u03bd\u03b5\u03cd\u03b5\u03b9 \u03c6\u03c9\u03c2", "locked": "{entity_name} \u03ba\u03bb\u03b5\u03b9\u03b4\u03ce\u03b8\u03b7\u03ba\u03b5", "moist": "{entity_name} \u03ad\u03b3\u03b9\u03bd\u03b5 \u03c5\u03b3\u03c1\u03cc", @@ -138,10 +136,6 @@ "off": "\u0394\u03b5\u03bd \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5", "on": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5" }, - "co": { - "off": "\u0394\u03b5\u03bd \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5", - "on": "\u0395\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03c4\u03b7\u03ba\u03b5" - }, "cold": { "off": "\u03a6\u03c5\u03c3\u03b9\u03bf\u03bb\u03bf\u03b3\u03b9\u03ba\u03cc", "on": "\u039a\u03c1\u03cd\u03bf" diff --git a/homeassistant/components/binary_sensor/translations/en.json b/homeassistant/components/binary_sensor/translations/en.json index 1d4f30fef52..1dc6cf2caa1 100644 --- a/homeassistant/components/binary_sensor/translations/en.json +++ b/homeassistant/components/binary_sensor/translations/en.json @@ -59,8 +59,6 @@ "connected": "{entity_name} connected", "gas": "{entity_name} started detecting gas", "hot": "{entity_name} became hot", - "is_not_tampered": "{entity_name} stopped detecting tampering", - "is_tampered": "{entity_name} started detecting tampering", "light": "{entity_name} started detecting light", "locked": "{entity_name} locked", "moist": "{entity_name} became moist", @@ -138,10 +136,6 @@ "off": "Clear", "on": "Detected" }, - "co": { - "off": "Clear", - "on": "Detected" - }, "cold": { "off": "Normal", "on": "Cold" diff --git a/homeassistant/components/binary_sensor/translations/es-419.json b/homeassistant/components/binary_sensor/translations/es-419.json index 2951e71b114..bc77a635165 100644 --- a/homeassistant/components/binary_sensor/translations/es-419.json +++ b/homeassistant/components/binary_sensor/translations/es-419.json @@ -121,10 +121,6 @@ "off": "No esta cargando", "on": "Cargando" }, - "co": { - "off": "Despejado", - "on": "Detectado" - }, "cold": { "off": "Normal", "on": "Fr\u00edo" diff --git a/homeassistant/components/binary_sensor/translations/es.json b/homeassistant/components/binary_sensor/translations/es.json index 5875804175e..005f2669e50 100644 --- a/homeassistant/components/binary_sensor/translations/es.json +++ b/homeassistant/components/binary_sensor/translations/es.json @@ -59,8 +59,6 @@ "connected": "{entity_name} conectado", "gas": "{entity_name} empez\u00f3 a detectar gas", "hot": "{entity_name} se est\u00e1 calentando", - "is_not_tampered": "{entity_name} dej\u00f3 de detectar alteraciones", - "is_tampered": "{entity_name} comenz\u00f3 a detectar alteraciones", "light": "{entity_name} empez\u00f3 a detectar la luz", "locked": "{entity_name} bloqueado", "moist": "{entity_name} se humedece", @@ -138,10 +136,6 @@ "off": "Libre", "on": "Detectado" }, - "co": { - "off": "No detectado", - "on": "Detectado" - }, "cold": { "off": "Normal", "on": "Fr\u00edo" diff --git a/homeassistant/components/binary_sensor/translations/et.json b/homeassistant/components/binary_sensor/translations/et.json index 92b4e52952d..6e16d5da000 100644 --- a/homeassistant/components/binary_sensor/translations/et.json +++ b/homeassistant/components/binary_sensor/translations/et.json @@ -59,8 +59,6 @@ "connected": "{entity_name} on \u00fchendatud", "gas": "{entity_name} tuvastas gaasi(leket)", "hot": "{entity_name} muutus kuumaks", - "is_not_tampered": "{entity_name} l\u00f5petas omavolilise muutmise tuvastamise", - "is_tampered": "{entity_name} alustas omavolilise muutmise tuvastamist", "light": "{entity_name} tuvastas valgust", "locked": "{entity_name} on lukus", "moist": "{entity_name} muutus niiskeks", @@ -138,10 +136,6 @@ "off": "Korras", "on": "Tuvastatud" }, - "co": { - "off": "Puudub", - "on": "Tuvastatud" - }, "cold": { "off": "Normaalne", "on": "Jahe" diff --git a/homeassistant/components/binary_sensor/translations/fr.json b/homeassistant/components/binary_sensor/translations/fr.json index a9bd4ba30b1..b5280a897cc 100644 --- a/homeassistant/components/binary_sensor/translations/fr.json +++ b/homeassistant/components/binary_sensor/translations/fr.json @@ -59,8 +59,6 @@ "connected": "{entity_name} s'est connect\u00e9", "gas": "{entity_name} a commenc\u00e9 \u00e0 d\u00e9tecter du gaz", "hot": "{entity_name} est devenu chaud", - "is_not_tampered": "{entity_name} a cess\u00e9 de d\u00e9tecter une manipulation", - "is_tampered": "{entity_name} a commenc\u00e9 \u00e0 d\u00e9tecter une manipulation", "light": "{entity_name} a commenc\u00e9 \u00e0 d\u00e9tecter de la lumi\u00e8re", "locked": "{entity_name} s'est verrouill\u00e9", "moist": "{entity_name} est devenu humide", @@ -138,10 +136,6 @@ "off": "Non d\u00e9tect\u00e9", "on": "D\u00e9tect\u00e9" }, - "co": { - "off": "Non d\u00e9tect\u00e9", - "on": "D\u00e9tect\u00e9" - }, "cold": { "off": "Normal", "on": "Froid" diff --git a/homeassistant/components/binary_sensor/translations/he.json b/homeassistant/components/binary_sensor/translations/he.json index 5f0e14ccac1..cd3b24ed9a3 100644 --- a/homeassistant/components/binary_sensor/translations/he.json +++ b/homeassistant/components/binary_sensor/translations/he.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u05de\u05d7\u05d5\u05d1\u05e8", "gas": "{entity_name} \u05d4\u05d7\u05dc \u05dc\u05d6\u05d4\u05d5\u05ea \u05d2\u05d6", "hot": "{entity_name} \u05e0\u05e2\u05e9\u05d4 \u05d7\u05dd", - "is_not_tampered": "{entity_name} \u05d4\u05e4\u05e1\u05d9\u05e7 \u05dc\u05d6\u05d4\u05d5\u05ea \u05d7\u05d1\u05dc\u05d4", - "is_tampered": "{entity_name} \u05d4\u05d7\u05dc \u05dc\u05d6\u05d4\u05d5\u05ea \u05d7\u05d1\u05dc\u05d4", "light": "{entity_name} \u05d4\u05ea\u05d7\u05d9\u05dc \u05dc\u05d6\u05d4\u05d5\u05ea \u05d0\u05d5\u05e8", "locked": "{entity_name} \u05e0\u05e2\u05d5\u05dc", "moist": "{entity_name} \u05d4\u05e4\u05da \u05dc\u05d7", @@ -138,10 +136,6 @@ "off": "\u05e0\u05e7\u05d9", "on": "\u05d6\u05d5\u05d4\u05d4" }, - "co": { - "off": "\u05e0\u05e7\u05d9", - "on": "\u05d6\u05d5\u05d4\u05d4" - }, "cold": { "off": "\u05e0\u05d5\u05e8\u05de\u05dc\u05d9", "on": "\u05e7\u05e8" diff --git a/homeassistant/components/binary_sensor/translations/hu.json b/homeassistant/components/binary_sensor/translations/hu.json index 3690c1def1e..56df2994766 100644 --- a/homeassistant/components/binary_sensor/translations/hu.json +++ b/homeassistant/components/binary_sensor/translations/hu.json @@ -59,8 +59,6 @@ "connected": "{entity_name} csatlakozik", "gas": "{entity_name} g\u00e1zt \u00e9rz\u00e9kel", "hot": "{entity_name} felforr\u00f3sodik", - "is_not_tampered": "{entity_name} nem \u00e9szlelt manipul\u00e1l\u00e1st", - "is_tampered": "{entity_name} manipul\u00e1l\u00e1st \u00e9szlelt", "light": "{entity_name} f\u00e9nyt \u00e9rz\u00e9kel", "locked": "{entity_name} be lett z\u00e1rva", "moist": "{entity_name} nedves lett", @@ -138,10 +136,6 @@ "off": "Norm\u00e1l", "on": "\u00c9szlelve" }, - "co": { - "off": "Tiszta", - "on": "\u00c9rz\u00e9kelve" - }, "cold": { "off": "Norm\u00e1l", "on": "Hideg" diff --git a/homeassistant/components/binary_sensor/translations/id.json b/homeassistant/components/binary_sensor/translations/id.json index 5215f57814a..e01ee10cc3c 100644 --- a/homeassistant/components/binary_sensor/translations/id.json +++ b/homeassistant/components/binary_sensor/translations/id.json @@ -59,8 +59,6 @@ "connected": "{entity_name} terhubung", "gas": "{entity_name} mulai mendeteksi gas", "hot": "{entity_name} menjadi panas", - "is_not_tampered": "{entity_name} berhenti mendeteksi gangguan", - "is_tampered": "{entity_name} mulai mendeteksi gangguan", "light": "{entity_name} mulai mendeteksi cahaya", "locked": "{entity_name} terkunci", "moist": "{entity_name} menjadi lembab", @@ -138,10 +136,6 @@ "off": "Tidak ada", "on": "Terdeteksi" }, - "co": { - "off": "Tidak ada", - "on": "Terdeteksi" - }, "cold": { "off": "Normal", "on": "Dingin" diff --git a/homeassistant/components/binary_sensor/translations/it.json b/homeassistant/components/binary_sensor/translations/it.json index 6054fd9d9fe..933e72a285a 100644 --- a/homeassistant/components/binary_sensor/translations/it.json +++ b/homeassistant/components/binary_sensor/translations/it.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u00e8 connesso", "gas": "{entity_name} ha iniziato a rilevare il gas", "hot": "{entity_name} \u00e8 diventato caldo", - "is_not_tampered": "{entity_name} ha smesso di rilevare manomissioni", - "is_tampered": "{entity_name} ha iniziato a rilevare manomissioni", "light": "{entity_name} ha iniziato a rilevare la luce", "locked": "{entity_name} bloccato", "moist": "{entity_name} diventato umido", @@ -138,10 +136,6 @@ "off": "Assente", "on": "Rilevato" }, - "co": { - "off": "Non rilevato", - "on": "Rilevato" - }, "cold": { "off": "Normale", "on": "Freddo" diff --git a/homeassistant/components/binary_sensor/translations/ja.json b/homeassistant/components/binary_sensor/translations/ja.json index 541c5073961..a49c683b00b 100644 --- a/homeassistant/components/binary_sensor/translations/ja.json +++ b/homeassistant/components/binary_sensor/translations/ja.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u3059", "gas": "{entity_name} \u304c\u30ac\u30b9\u306e\u691c\u51fa\u3092\u958b\u59cb\u3057\u307e\u3057\u305f", "hot": "{entity_name} \u6e29\u307e\u3063\u3066\u3044\u307e\u3059", - "is_not_tampered": "{entity_name} \u304c\u6539\u7ac4(tampering)\u306e\u691c\u51fa\u3092\u505c\u6b62\u3057\u307e\u3057\u305f", - "is_tampered": "{entity_name} \u304c\u6539\u7ac4(tampering)\u306e\u691c\u51fa\u3092\u958b\u59cb\u3057\u307e\u3057\u305f", "light": "{entity_name} \u306f\u3001\u5149\u306e\u691c\u51fa\u3092\u958b\u59cb\u3057\u307e\u3057\u305f", "locked": "{entity_name} \u306f\u30ed\u30c3\u30af\u3055\u308c\u3066\u3044\u307e\u3059", "moist": "{entity_name} \u304c\u6e7f\u3063\u305f", @@ -138,10 +136,6 @@ "off": "\u30af\u30ea\u30a2", "on": "\u691c\u51fa" }, - "co": { - "off": "\u30af\u30ea\u30a2", - "on": "\u691c\u51fa\u3055\u308c\u307e\u3057\u305f" - }, "cold": { "off": "\u901a\u5e38", "on": "\u4f4e\u6e29" diff --git a/homeassistant/components/binary_sensor/translations/nl.json b/homeassistant/components/binary_sensor/translations/nl.json index 7afbcaf616f..eb830fdd450 100644 --- a/homeassistant/components/binary_sensor/translations/nl.json +++ b/homeassistant/components/binary_sensor/translations/nl.json @@ -59,8 +59,6 @@ "connected": "{entity_name} verbonden", "gas": "{entity_name} begon gas te detecteren", "hot": "{entity_name} werd heet", - "is_not_tampered": "{entity_name} gestopt met het detecteren van sabotage", - "is_tampered": "{entity_name} begonnen met het detecteren van sabotage", "light": "{entity_name} begon licht te detecteren", "locked": "{entity_name} vergrendeld", "moist": "{entity_name} werd vochtig", @@ -138,10 +136,6 @@ "off": "Niet gedetecteerd", "on": "Gedetecteerd" }, - "co": { - "off": "Niet gedetecteerd", - "on": "Gedetecteerd" - }, "cold": { "off": "Normaal", "on": "Koud" diff --git a/homeassistant/components/binary_sensor/translations/no.json b/homeassistant/components/binary_sensor/translations/no.json index 62cf2d7cc1b..e29fab0a709 100644 --- a/homeassistant/components/binary_sensor/translations/no.json +++ b/homeassistant/components/binary_sensor/translations/no.json @@ -59,8 +59,6 @@ "connected": "{entity_name} tilkoblet", "gas": "{entity_name} begynte \u00e5 registrere gass", "hot": "{entity_name} ble varm", - "is_not_tampered": "{entity_name} sluttet \u00e5 oppdage manipulering", - "is_tampered": "{entity_name} begynte \u00e5 oppdage manipulering", "light": "{entity_name} begynte \u00e5 registrere lys", "locked": "{entity_name} l\u00e5st", "moist": "{entity_name} ble fuktig", @@ -138,10 +136,6 @@ "off": "Klart", "on": "Oppdaget" }, - "co": { - "off": "Klart", - "on": "Oppdaget" - }, "cold": { "off": "Normal", "on": "Kald" diff --git a/homeassistant/components/binary_sensor/translations/pl.json b/homeassistant/components/binary_sensor/translations/pl.json index d318968da56..d27d6443b55 100644 --- a/homeassistant/components/binary_sensor/translations/pl.json +++ b/homeassistant/components/binary_sensor/translations/pl.json @@ -59,8 +59,6 @@ "connected": "nast\u0105pi pod\u0142\u0105czenie {entity_name}", "gas": "sensor {entity_name} wykryje gaz", "hot": "sensor {entity_name} wykryje gor\u0105co", - "is_not_tampered": "sensor {entity_name} przestanie wykrywa\u0107 naruszenie", - "is_tampered": "sensor {entity_name} wykryje naruszenie", "light": "sensor {entity_name} wykryje \u015bwiat\u0142o", "locked": "nast\u0105pi zamkni\u0119cie {entity_name}", "moist": "nast\u0105pi wykrycie wilgoci {entity_name}", @@ -138,10 +136,6 @@ "off": "brak", "on": "wykryto" }, - "co": { - "off": "brak", - "on": "wykryto" - }, "cold": { "off": "normalnie", "on": "zimno" diff --git a/homeassistant/components/binary_sensor/translations/pt-BR.json b/homeassistant/components/binary_sensor/translations/pt-BR.json index 5b1ba2a1abf..5bd166f9367 100644 --- a/homeassistant/components/binary_sensor/translations/pt-BR.json +++ b/homeassistant/components/binary_sensor/translations/pt-BR.json @@ -59,8 +59,6 @@ "connected": "{entity_name} conectado", "gas": "{entity_name} come\u00e7ou a detectar g\u00e1s", "hot": "{entity_name} tornou-se quente", - "is_not_tampered": "{entity_name} parar de detectar adultera\u00e7\u00e3o", - "is_tampered": "{entity_name} come\u00e7ar a detectar adultera\u00e7\u00e3o", "light": "{entity_name} come\u00e7ou a detectar luz", "locked": "{entity_name} bloqueado", "moist": "{entity_name} ficar \u00famido", @@ -138,10 +136,6 @@ "off": "Normal", "on": "Detectado" }, - "co": { - "off": "Limpo", - "on": "Detectado" - }, "cold": { "off": "Normal", "on": "Frio" diff --git a/homeassistant/components/binary_sensor/translations/ru.json b/homeassistant/components/binary_sensor/translations/ru.json index cc9573e2b14..9522720571b 100644 --- a/homeassistant/components/binary_sensor/translations/ru.json +++ b/homeassistant/components/binary_sensor/translations/ru.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "gas": "{entity_name} \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0442\u044c \u0433\u0430\u0437", "hot": "{entity_name} \u043d\u0430\u0433\u0440\u0435\u0432\u0430\u0435\u0442\u0441\u044f", - "is_not_tampered": "{entity_name} \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0435", - "is_tampered": "{entity_name} \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0435", "light": "{entity_name} \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0442\u044c \u0441\u0432\u0435\u0442", "locked": "{entity_name} \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442\u0441\u044f", "moist": "{entity_name} \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432\u043b\u0430\u0436\u043d\u044b\u043c", @@ -138,10 +136,6 @@ "off": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d", "on": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d" }, - "co": { - "off": "\u041d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d", - "on": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d" - }, "cold": { "off": "\u041d\u043e\u0440\u043c\u0430", "on": "\u041e\u0445\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435" diff --git a/homeassistant/components/binary_sensor/translations/sl.json b/homeassistant/components/binary_sensor/translations/sl.json index cc34982ad0a..6b47f4e9e7e 100644 --- a/homeassistant/components/binary_sensor/translations/sl.json +++ b/homeassistant/components/binary_sensor/translations/sl.json @@ -52,8 +52,6 @@ "connected": "{entity_name} povezan", "gas": "{entity_name} za\u010del zaznavati plin", "hot": "{entity_name} je postal vro\u010d", - "is_not_tampered": "{entity_name} je prenehal zaznavati nedovoljena dejanja", - "is_tampered": "{entity_name} je za\u010del zaznavati nedovoljeno poseganje", "light": "{entity_name} za\u010del zaznavati svetlobo", "locked": "{entity_name} zaklenjen", "moist": "{entity_name} postal vla\u017een", diff --git a/homeassistant/components/binary_sensor/translations/tr.json b/homeassistant/components/binary_sensor/translations/tr.json index 2b7eb33e6f0..c54454b2230 100644 --- a/homeassistant/components/binary_sensor/translations/tr.json +++ b/homeassistant/components/binary_sensor/translations/tr.json @@ -59,8 +59,6 @@ "connected": "{entity_name} ba\u011fland\u0131", "gas": "{entity_name} gaz alg\u0131lamaya ba\u015flad\u0131", "hot": "{entity_name} \u0131s\u0131nd\u0131", - "is_not_tampered": "{entity_name} kurcalamay\u0131 alg\u0131lamay\u0131 durdurdu", - "is_tampered": "{entity_name} , kurcalamay\u0131 alg\u0131lamaya ba\u015flad\u0131", "light": "{entity_name} \u0131\u015f\u0131\u011f\u0131 alg\u0131lamaya ba\u015flad\u0131", "locked": "{entity_name} kilitlendi", "moist": "{entity_name} nemli oldu", @@ -138,10 +136,6 @@ "off": "Temiz", "on": "Alg\u0131land\u0131" }, - "co": { - "off": "Temiz", - "on": "Alg\u0131land\u0131" - }, "cold": { "off": "Normal", "on": "So\u011fuk" diff --git a/homeassistant/components/binary_sensor/translations/zh-Hans.json b/homeassistant/components/binary_sensor/translations/zh-Hans.json index 202a51740de..70ff33a6e53 100644 --- a/homeassistant/components/binary_sensor/translations/zh-Hans.json +++ b/homeassistant/components/binary_sensor/translations/zh-Hans.json @@ -59,8 +59,6 @@ "connected": "{entity_name} \u5df2\u8fde\u63a5", "gas": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u71c3\u6c14\u6cc4\u6f0f", "hot": "{entity_name} \u53d8\u70ed", - "is_not_tampered": "{entity_name} \u4e0d\u518d\u68c0\u6d4b\u5230\u81ea\u8eab\u88ab\u62c6\u89e3", - "is_tampered": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u81ea\u8eab\u88ab\u62c6\u89e3", "light": "{entity_name} \u5f00\u59cb\u68c0\u6d4b\u5230\u5149\u7ebf", "locked": "{entity_name} \u88ab\u9501\u5b9a", "moist": "{entity_name} \u53d8\u6e7f", @@ -134,10 +132,6 @@ "off": "\u672a\u5728\u5145\u7535", "on": "\u6b63\u5728\u5145\u7535" }, - "co": { - "off": "\u672a\u89e6\u53d1", - "on": "\u89e6\u53d1" - }, "cold": { "off": "\u6b63\u5e38", "on": "\u8fc7\u51b7" diff --git a/homeassistant/components/binary_sensor/translations/zh-Hant.json b/homeassistant/components/binary_sensor/translations/zh-Hant.json index 417ca652cb5..32c1aab1cd1 100644 --- a/homeassistant/components/binary_sensor/translations/zh-Hant.json +++ b/homeassistant/components/binary_sensor/translations/zh-Hant.json @@ -59,8 +59,6 @@ "connected": "{entity_name}\u5df2\u9023\u7dda", "gas": "{entity_name}\u5df2\u958b\u59cb\u5075\u6e2c\u6c23\u9ad4", "hot": "{entity_name}\u5df2\u8b8a\u71b1", - "is_not_tampered": "{entity_name}\u5df2\u505c\u6b62\u5075\u6e2c\u6e1b\u5f31", - "is_tampered": "{entity_name}\u5df2\u5075\u6e2c\u5230\u6e1b\u5f31", "light": "{entity_name}\u5df2\u958b\u59cb\u5075\u6e2c\u5149\u7dda", "locked": "{entity_name}\u5df2\u4e0a\u9396", "moist": "{entity_name}\u5df2\u8b8a\u6f6e\u6fd5", @@ -138,10 +136,6 @@ "off": "\u672a\u89f8\u767c", "on": "\u5df2\u89f8\u767c" }, - "co": { - "off": "\u672a\u5075\u6e2c", - "on": "\u5075\u6e2c" - }, "cold": { "off": "\u6b63\u5e38", "on": "\u51b7" diff --git a/homeassistant/components/bmw_connected_drive/translations/ca.json b/homeassistant/components/bmw_connected_drive/translations/ca.json index eb12ac6fc3b..b6129aa4de8 100644 --- a/homeassistant/components/bmw_connected_drive/translations/ca.json +++ b/homeassistant/components/bmw_connected_drive/translations/ca.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Nom\u00e9s de lectura (nom\u00e9s sensors i notificacions, sense execuci\u00f3 de serveis, sense bloqueig)", - "use_location": "Utilitza la ubicaci\u00f3 de Home Assistant per a les crides de localitzaci\u00f3 del cotxe (obligatori per a vehicles que no siguin i3/i8 produ\u00efts abans del 7/2014)" + "read_only": "Nom\u00e9s de lectura (nom\u00e9s sensors i notificacions, sense execuci\u00f3 de serveis, sense bloqueig)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/de.json b/homeassistant/components/bmw_connected_drive/translations/de.json index 85e27b5b3e4..0ee49c105f4 100644 --- a/homeassistant/components/bmw_connected_drive/translations/de.json +++ b/homeassistant/components/bmw_connected_drive/translations/de.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Schreibgesch\u00fctzt (nur Sensoren und Notify, keine Ausf\u00fchrung von Diensten, kein Abschlie\u00dfen)", - "use_location": "Standort des Home Assistant f\u00fcr die Abfrage des Fahrzeugstandorts verwenden (erforderlich f\u00fcr nicht i3/i8 Fahrzeuge, die vor 7/2014 produziert wurden)" + "read_only": "Schreibgesch\u00fctzt (nur Sensoren und Notify, keine Ausf\u00fchrung von Diensten, kein Abschlie\u00dfen)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/el.json b/homeassistant/components/bmw_connected_drive/translations/el.json index bb20b0a61a2..8ba712e7373 100644 --- a/homeassistant/components/bmw_connected_drive/translations/el.json +++ b/homeassistant/components/bmw_connected_drive/translations/el.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\u039c\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7 (\u03bc\u03cc\u03bd\u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2, \u03cc\u03c7\u03b9 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03b9\u03ce\u03bd, \u03cc\u03c7\u03b9 \u03ba\u03bb\u03b5\u03af\u03b4\u03c9\u03bc\u03b1)", - "use_location": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1\u03c2 \u03c4\u03bf\u03c5 Home Assistant \u03b3\u03b9\u03b1 \u03c4\u03b9\u03c2 \u03b4\u03b7\u03bc\u03bf\u03c3\u03ba\u03bf\u03c0\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03c5\u03c4\u03bf\u03ba\u03b9\u03bd\u03ae\u03c4\u03bf\u03c5 (\u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03bf\u03c7\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c0\u03bf\u03c5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 i3/i8 \u03ba\u03b1\u03b9 \u03ad\u03c7\u03bf\u03c5\u03bd \u03c0\u03b1\u03c1\u03b1\u03c7\u03b8\u03b5\u03af \u03c0\u03c1\u03b9\u03bd \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 7/2014)" + "read_only": "\u039c\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03b1\u03bd\u03ac\u03b3\u03bd\u03c9\u03c3\u03b7 (\u03bc\u03cc\u03bd\u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b5\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2, \u03cc\u03c7\u03b9 \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03b9\u03ce\u03bd, \u03cc\u03c7\u03b9 \u03ba\u03bb\u03b5\u03af\u03b4\u03c9\u03bc\u03b1)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/en.json b/homeassistant/components/bmw_connected_drive/translations/en.json index dedd84d070b..db98df61d28 100644 --- a/homeassistant/components/bmw_connected_drive/translations/en.json +++ b/homeassistant/components/bmw_connected_drive/translations/en.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Read-only (only sensors and notify, no execution of services, no lock)", - "use_location": "Use Home Assistant location for car location polls (required for non i3/i8 vehicles produced before 7/2014)" + "read_only": "Read-only (only sensors and notify, no execution of services, no lock)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/es-419.json b/homeassistant/components/bmw_connected_drive/translations/es-419.json index bb19124b55c..0bce46abd97 100644 --- a/homeassistant/components/bmw_connected_drive/translations/es-419.json +++ b/homeassistant/components/bmw_connected_drive/translations/es-419.json @@ -12,8 +12,7 @@ "step": { "account_options": { "data": { - "read_only": "Solo lectura (solo sensores y notificaci\u00f3n, sin ejecuci\u00f3n de servicios, sin bloqueo)", - "use_location": "Use la ubicaci\u00f3n de Home Assistant para encuestas de ubicaci\u00f3n de autom\u00f3viles (obligatorio para veh\u00edculos que no sean i3/i8 fabricados antes de julio de 2014)" + "read_only": "Solo lectura (solo sensores y notificaci\u00f3n, sin ejecuci\u00f3n de servicios, sin bloqueo)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/es.json b/homeassistant/components/bmw_connected_drive/translations/es.json index 65ed9643f89..2a4fd84f708 100644 --- a/homeassistant/components/bmw_connected_drive/translations/es.json +++ b/homeassistant/components/bmw_connected_drive/translations/es.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "S\u00f3lo lectura (s\u00f3lo sensores y notificaci\u00f3n, sin ejecuci\u00f3n de servicios, sin bloqueo)", - "use_location": "Usar la ubicaci\u00f3n de Home Assistant para las encuestas de localizaci\u00f3n de autom\u00f3viles (necesario para los veh\u00edculos no i3/i8 producidos antes del 7/2014)" + "read_only": "S\u00f3lo lectura (s\u00f3lo sensores y notificaci\u00f3n, sin ejecuci\u00f3n de servicios, sin bloqueo)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/et.json b/homeassistant/components/bmw_connected_drive/translations/et.json index f28209a1e7a..adee392525e 100644 --- a/homeassistant/components/bmw_connected_drive/translations/et.json +++ b/homeassistant/components/bmw_connected_drive/translations/et.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Kirjutuskaitstud (ainult andurid ja teavitused, ei k\u00e4ivita teenuseid, kood puudub)", - "use_location": "Kasuta HA asukohta auto asukoha k\u00fcsitluste jaoks (n\u00f5utav enne 7/2014 toodetud muude kui i3 / i8 s\u00f5idukite jaoks)" + "read_only": "Kirjutuskaitstud (ainult andurid ja teavitused, ei k\u00e4ivita teenuseid, kood puudub)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/fr.json b/homeassistant/components/bmw_connected_drive/translations/fr.json index 5181620d465..afed216c64c 100644 --- a/homeassistant/components/bmw_connected_drive/translations/fr.json +++ b/homeassistant/components/bmw_connected_drive/translations/fr.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Lecture seule (uniquement capteurs et notification, pas d'ex\u00e9cution de services, pas de verrouillage)", - "use_location": "Utilisez la localisation de Home Assistant pour les sondages de localisation de voiture (obligatoire pour les v\u00e9hicules non i3 / i8 produits avant 7/2014)" + "read_only": "Lecture seule (uniquement capteurs et notification, pas d'ex\u00e9cution de services, pas de verrouillage)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/hu.json b/homeassistant/components/bmw_connected_drive/translations/hu.json index fa3fcf2df57..637083154bd 100644 --- a/homeassistant/components/bmw_connected_drive/translations/hu.json +++ b/homeassistant/components/bmw_connected_drive/translations/hu.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Csak olvashat\u00f3 (csak \u00e9rz\u00e9kel\u0151k \u00e9s \u00e9rtes\u00edt\u00e9sek, szolg\u00e1ltat\u00e1sok v\u00e9grehajt\u00e1sa, z\u00e1rol\u00e1s n\u00e9lk\u00fcl)", - "use_location": "Haszn\u00e1lja a Home Assistant hely\u00e9t az aut\u00f3 helymeghat\u00e1roz\u00e1si lek\u00e9rdez\u00e9seihez (a 2014.07.07. el\u0151tt gy\u00e1rtott nem i3/i8 j\u00e1rm\u0171vekhez sz\u00fcks\u00e9ges)" + "read_only": "Csak olvashat\u00f3 (csak \u00e9rz\u00e9kel\u0151k \u00e9s \u00e9rtes\u00edt\u00e9sek, szolg\u00e1ltat\u00e1sok v\u00e9grehajt\u00e1sa, z\u00e1rol\u00e1s n\u00e9lk\u00fcl)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/id.json b/homeassistant/components/bmw_connected_drive/translations/id.json index 3701a49ccfb..23e57c8e301 100644 --- a/homeassistant/components/bmw_connected_drive/translations/id.json +++ b/homeassistant/components/bmw_connected_drive/translations/id.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Hanya baca (hanya sensor dan notifikasi, tidak ada eksekusi layanan, tidak ada fitur penguncian)", - "use_location": "Gunakan lokasi Home Assistant untuk polling lokasi mobil (diperlukan untuk kendaraan non i3/i8 yang diproduksi sebelum Juli 2014)" + "read_only": "Hanya baca (hanya sensor dan notifikasi, tidak ada eksekusi layanan, tidak ada fitur penguncian)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/it.json b/homeassistant/components/bmw_connected_drive/translations/it.json index eeca1909ea7..3c0bb4ee830 100644 --- a/homeassistant/components/bmw_connected_drive/translations/it.json +++ b/homeassistant/components/bmw_connected_drive/translations/it.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Sola lettura (solo sensori e notifica, nessuna esecuzione di servizi, nessun blocco)", - "use_location": "Usa la posizione di Home Assistant per le richieste di posizione dell'auto (richiesto per veicoli non i3/i8 prodotti prima del 7/2014)" + "read_only": "Sola lettura (solo sensori e notifica, nessuna esecuzione di servizi, nessun blocco)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/ja.json b/homeassistant/components/bmw_connected_drive/translations/ja.json index a3fa86348fe..643e3b70cc3 100644 --- a/homeassistant/components/bmw_connected_drive/translations/ja.json +++ b/homeassistant/components/bmw_connected_drive/translations/ja.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\u30ea\u30fc\u30c9\u30aa\u30f3\u30ea\u30fc(\u30bb\u30f3\u30b5\u30fc\u3068\u901a\u77e5\u306e\u307f\u3001\u30b5\u30fc\u30d3\u30b9\u306e\u5b9f\u884c\u306f\u4e0d\u53ef\u3001\u30ed\u30c3\u30af\u4e0d\u53ef)", - "use_location": "Home Assistant\u306e\u5834\u6240\u3092\u3001\u8eca\u306e\u4f4d\u7f6e\u3068\u3057\u3066\u30dd\u30fc\u30ea\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b(2014\u5e747\u67087\u65e5\u4ee5\u524d\u306b\u751f\u7523\u3055\u308c\u305f\u3001i3/i8\u4ee5\u5916\u306e\u8eca\u4e21\u3067\u306f\u5fc5\u9808)" + "read_only": "\u30ea\u30fc\u30c9\u30aa\u30f3\u30ea\u30fc(\u30bb\u30f3\u30b5\u30fc\u3068\u901a\u77e5\u306e\u307f\u3001\u30b5\u30fc\u30d3\u30b9\u306e\u5b9f\u884c\u306f\u4e0d\u53ef\u3001\u30ed\u30c3\u30af\u4e0d\u53ef)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/ko.json b/homeassistant/components/bmw_connected_drive/translations/ko.json index 4c9872573be..588c5111afc 100644 --- a/homeassistant/components/bmw_connected_drive/translations/ko.json +++ b/homeassistant/components/bmw_connected_drive/translations/ko.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\uc77d\uae30 \uc804\uc6a9(\uc13c\uc11c \ubc0f \uc54c\ub9bc\ub9cc \uac00\ub2a5, \uc11c\ube44\uc2a4 \uc2e4\ud589 \ubc0f \uc7a0\uae08 \uc5c6\uc74c)", - "use_location": "\ucc28\ub7c9 \uc704\uce58 \ud3f4\ub9c1\uc5d0 Home Assistant \uc704\uce58\ub97c \uc0ac\uc6a9\ud569\ub2c8\ub2e4(2014\ub144 7\uc6d4 \uc774\uc804\uc5d0 \uc0dd\uc0b0\ub41c i3/i8\uc774 \uc544\ub2cc \ucc28\ub7c9\uc758 \uacbd\uc6b0 \ud544\uc694)" + "read_only": "\uc77d\uae30 \uc804\uc6a9(\uc13c\uc11c \ubc0f \uc54c\ub9bc\ub9cc \uac00\ub2a5, \uc11c\ube44\uc2a4 \uc2e4\ud589 \ubc0f \uc7a0\uae08 \uc5c6\uc74c)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/nl.json b/homeassistant/components/bmw_connected_drive/translations/nl.json index 8fa6c839112..a566afd7ad8 100644 --- a/homeassistant/components/bmw_connected_drive/translations/nl.json +++ b/homeassistant/components/bmw_connected_drive/translations/nl.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Alleen-lezen (alleen sensoren en notificatie, geen uitvoering van diensten, geen vergrendeling)", - "use_location": "Gebruik Home Assistant locatie voor auto locatie peilingen (vereist voor niet i3/i8 voertuigen geproduceerd voor 7/2014)" + "read_only": "Alleen-lezen (alleen sensoren en notificatie, geen uitvoering van diensten, geen vergrendeling)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/no.json b/homeassistant/components/bmw_connected_drive/translations/no.json index f1715c550db..ed6acfeac38 100644 --- a/homeassistant/components/bmw_connected_drive/translations/no.json +++ b/homeassistant/components/bmw_connected_drive/translations/no.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Skrivebeskyttet (bare sensorer og varsler, ingen utf\u00f8relse av tjenester, ingen l\u00e5s)", - "use_location": "Bruk Home Assistant plassering for avstemningssteder for biler (p\u00e5krevd for ikke i3 / i8-kj\u00f8ret\u00f8y produsert f\u00f8r 7/2014)" + "read_only": "Skrivebeskyttet (bare sensorer og varsler, ingen utf\u00f8relse av tjenester, ingen l\u00e5s)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/pl.json b/homeassistant/components/bmw_connected_drive/translations/pl.json index 146916edd5b..00602b2c14e 100644 --- a/homeassistant/components/bmw_connected_drive/translations/pl.json +++ b/homeassistant/components/bmw_connected_drive/translations/pl.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Tylko odczyt (tylko sensory i powiadomienia, brak wykonywania us\u0142ug, brak blokady)", - "use_location": "U\u017cyj lokalizacji Home Assistant do sondowania lokalizacji samochodu (wymagane w przypadku pojazd\u00f3w innych ni\u017c i3/i8 wyprodukowanych przed 7/2014)" + "read_only": "Tylko odczyt (tylko sensory i powiadomienia, brak wykonywania us\u0142ug, brak blokady)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/pt-BR.json b/homeassistant/components/bmw_connected_drive/translations/pt-BR.json index b4b82c1fe18..a30ede043bd 100644 --- a/homeassistant/components/bmw_connected_drive/translations/pt-BR.json +++ b/homeassistant/components/bmw_connected_drive/translations/pt-BR.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Somente leitura (somente sensores e notificar, sem execu\u00e7\u00e3o de servi\u00e7os, sem bloqueio)", - "use_location": "Use a localiza\u00e7\u00e3o do Home Assistant para pesquisas de localiza\u00e7\u00e3o de carros (necess\u00e1rio para ve\u00edculos n\u00e3o i3/i8 produzidos antes de 7/2014)" + "read_only": "Somente leitura (somente sensores e notificar, sem execu\u00e7\u00e3o de servi\u00e7os, sem bloqueio)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/ru.json b/homeassistant/components/bmw_connected_drive/translations/ru.json index 8ab4e4e1207..7a398c8ca55 100644 --- a/homeassistant/components/bmw_connected_drive/translations/ru.json +++ b/homeassistant/components/bmw_connected_drive/translations/ru.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\u0422\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u0435\u043d\u0438\u0435 (\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f, \u0431\u0435\u0437 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u043b\u0443\u0436\u0431, \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438)", - "use_location": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Home Assistant \u0434\u043b\u044f \u043e\u043f\u0440\u043e\u0441\u0430 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 (\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u0435\u0439 \u043d\u0435 i3/i8, \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0445 \u0434\u043e 7/2014)" + "read_only": "\u0422\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u0435\u043d\u0438\u0435 (\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f, \u0431\u0435\u0437 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u043b\u0443\u0436\u0431, \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/sv.json b/homeassistant/components/bmw_connected_drive/translations/sv.json index 93cd828041e..eb2322abb1f 100644 --- a/homeassistant/components/bmw_connected_drive/translations/sv.json +++ b/homeassistant/components/bmw_connected_drive/translations/sv.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Endast l\u00e4sbar (endast sensorer och meddelanden, ingen k\u00f6rning av tj\u00e4nster, ingen l\u00e5sning)", - "use_location": "Anv\u00e4nd plats f\u00f6r Home Assistant som plats f\u00f6r bilen (kr\u00e4vs f\u00f6r icke i3/i8-fordon tillverkade f\u00f6re 7/2014)" + "read_only": "Endast l\u00e4sbar (endast sensorer och meddelanden, ingen k\u00f6rning av tj\u00e4nster, ingen l\u00e5sning)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/tr.json b/homeassistant/components/bmw_connected_drive/translations/tr.json index d38f950392b..52a0b02b044 100644 --- a/homeassistant/components/bmw_connected_drive/translations/tr.json +++ b/homeassistant/components/bmw_connected_drive/translations/tr.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "Salt okunur (yaln\u0131zca sens\u00f6rler ve bildirim, hizmetlerin y\u00fcr\u00fct\u00fclmesi yok, kilit yok)", - "use_location": "Araba konumu anketleri i\u00e7in Home Assistant konumunu kullan\u0131n (7/2014 tarihinden \u00f6nce \u00fcretilmi\u015f i3/i8 olmayan ara\u00e7lar i\u00e7in gereklidir)" + "read_only": "Salt okunur (yaln\u0131zca sens\u00f6rler ve bildirim, hizmetlerin y\u00fcr\u00fct\u00fclmesi yok, kilit yok)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/uk.json b/homeassistant/components/bmw_connected_drive/translations/uk.json index 68cdee2a66f..4286e0e6063 100644 --- a/homeassistant/components/bmw_connected_drive/translations/uk.json +++ b/homeassistant/components/bmw_connected_drive/translations/uk.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\u041b\u0438\u0448\u0435 \u0434\u043b\u044f \u0447\u0438\u0442\u0430\u043d\u043d\u044f (\u043b\u0438\u0448\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0442\u0430 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u043d\u044f, \u0431\u0435\u0437 \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u0441\u0435\u0440\u0432\u0456\u0441\u0456\u0432, \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0443\u0432\u0430\u043d\u043d\u044f)", - "use_location": "\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u043c\u0456\u0441\u0446\u0435\u0437\u043d\u0430\u0445\u043e\u0434\u0436\u0435\u043d\u043d\u044f Home Assistant \u0434\u043b\u044f \u043e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u044c \u043c\u0456\u0441\u0446\u044f \u0440\u043e\u0437\u0442\u0430\u0448\u0443\u0432\u0430\u043d\u043d\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0456\u043b\u0456\u0432 (\u043e\u0431\u043e\u0432\u2019\u044f\u0437\u043a\u043e\u0432\u043e \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0456\u043b\u0456\u0432, \u0449\u043e \u043d\u0435 \u043d\u0430\u043b\u0435\u0436\u0430\u0442\u044c \u0434\u043e i3/i8, \u0432\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u0445 \u0434\u043e 7/2014)" + "read_only": "\u041b\u0438\u0448\u0435 \u0434\u043b\u044f \u0447\u0438\u0442\u0430\u043d\u043d\u044f (\u043b\u0438\u0448\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 \u0442\u0430 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u043d\u044f, \u0431\u0435\u0437 \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u0441\u0435\u0440\u0432\u0456\u0441\u0456\u0432, \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0443\u0432\u0430\u043d\u043d\u044f)" } } } diff --git a/homeassistant/components/bmw_connected_drive/translations/zh-Hant.json b/homeassistant/components/bmw_connected_drive/translations/zh-Hant.json index 4f62df586f5..38bf3bbb7cb 100644 --- a/homeassistant/components/bmw_connected_drive/translations/zh-Hant.json +++ b/homeassistant/components/bmw_connected_drive/translations/zh-Hant.json @@ -21,8 +21,7 @@ "step": { "account_options": { "data": { - "read_only": "\u552f\u8b80\uff08\u50c5\u652f\u63f4\u611f\u6e2c\u5668\u8207\u901a\u77e5\uff0c\u4e0d\n\u5305\u542b\u670d\u52d9\u8207\u9396\u5b9a\uff09", - "use_location": "\u4f7f\u7528 Home Assistant \u4f4d\u7f6e\u53d6\u5f97\u6c7d\u8eca\u4f4d\u7f6e\uff08\u9700\u8981\u70ba2014/7 \u524d\u751f\u7522\u7684\u975ei3/i8 \u8eca\u6b3e\uff09" + "read_only": "\u552f\u8b80\uff08\u50c5\u652f\u63f4\u611f\u6e2c\u5668\u8207\u901a\u77e5\uff0c\u4e0d\n\u5305\u542b\u670d\u52d9\u8207\u9396\u5b9a\uff09" } } } diff --git a/homeassistant/components/bosch_shc/translations/bg.json b/homeassistant/components/bosch_shc/translations/bg.json index 759dd6b21fb..a0b0548b51b 100644 --- a/homeassistant/components/bosch_shc/translations/bg.json +++ b/homeassistant/components/bosch_shc/translations/bg.json @@ -20,6 +20,5 @@ } } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/ca.json b/homeassistant/components/bosch_shc/translations/ca.json index 6db49cf7103..f6b06e1d884 100644 --- a/homeassistant/components/bosch_shc/translations/ca.json +++ b/homeassistant/components/bosch_shc/translations/ca.json @@ -33,6 +33,5 @@ "title": "Par\u00e0metres d'autenticaci\u00f3 SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/de.json b/homeassistant/components/bosch_shc/translations/de.json index 46b6469afe6..b99c00d6a6f 100644 --- a/homeassistant/components/bosch_shc/translations/de.json +++ b/homeassistant/components/bosch_shc/translations/de.json @@ -33,6 +33,5 @@ "title": "SHC Authentifizierungsparameter" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/el.json b/homeassistant/components/bosch_shc/translations/el.json index f9b5f4ad3d0..3b92111ce1a 100644 --- a/homeassistant/components/bosch_shc/translations/el.json +++ b/homeassistant/components/bosch_shc/translations/el.json @@ -33,6 +33,5 @@ "title": "\u03a0\u03b1\u03c1\u03ac\u03bc\u03b5\u03c4\u03c1\u03bf\u03b9 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/en.json b/homeassistant/components/bosch_shc/translations/en.json index 65f675e2f4d..ab5cde9ef27 100644 --- a/homeassistant/components/bosch_shc/translations/en.json +++ b/homeassistant/components/bosch_shc/translations/en.json @@ -33,6 +33,5 @@ "title": "SHC authentication parameters" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/es-419.json b/homeassistant/components/bosch_shc/translations/es-419.json index fdb8903a318..e868ffb1add 100644 --- a/homeassistant/components/bosch_shc/translations/es-419.json +++ b/homeassistant/components/bosch_shc/translations/es-419.json @@ -17,6 +17,5 @@ "title": "Par\u00e1metros de autenticaci\u00f3n SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/es.json b/homeassistant/components/bosch_shc/translations/es.json index 6d002cfadc2..0e3c536995d 100644 --- a/homeassistant/components/bosch_shc/translations/es.json +++ b/homeassistant/components/bosch_shc/translations/es.json @@ -33,6 +33,5 @@ "title": "Par\u00e1metros de autenticaci\u00f3n SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/et.json b/homeassistant/components/bosch_shc/translations/et.json index cb095bb9de7..7311d1f34e7 100644 --- a/homeassistant/components/bosch_shc/translations/et.json +++ b/homeassistant/components/bosch_shc/translations/et.json @@ -33,6 +33,5 @@ "title": "SHC autentimisparameetrid" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/fr.json b/homeassistant/components/bosch_shc/translations/fr.json index c34f5549663..bc9f62e1d11 100644 --- a/homeassistant/components/bosch_shc/translations/fr.json +++ b/homeassistant/components/bosch_shc/translations/fr.json @@ -33,6 +33,5 @@ "title": "Param\u00e8tres d'authentification SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/hu.json b/homeassistant/components/bosch_shc/translations/hu.json index b3e0ed77815..df5a484cabe 100644 --- a/homeassistant/components/bosch_shc/translations/hu.json +++ b/homeassistant/components/bosch_shc/translations/hu.json @@ -33,6 +33,5 @@ "title": "SHC hiteles\u00edt\u00e9si param\u00e9terek" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/id.json b/homeassistant/components/bosch_shc/translations/id.json index 723c6bdabbe..191c0cd96e8 100644 --- a/homeassistant/components/bosch_shc/translations/id.json +++ b/homeassistant/components/bosch_shc/translations/id.json @@ -33,6 +33,5 @@ "title": "Parameter autentikasi SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/it.json b/homeassistant/components/bosch_shc/translations/it.json index 2c64beee59c..05336a6abea 100644 --- a/homeassistant/components/bosch_shc/translations/it.json +++ b/homeassistant/components/bosch_shc/translations/it.json @@ -33,6 +33,5 @@ "title": "Parametri di autenticazione SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/ja.json b/homeassistant/components/bosch_shc/translations/ja.json index 0a624bd2eb0..da4b471c621 100644 --- a/homeassistant/components/bosch_shc/translations/ja.json +++ b/homeassistant/components/bosch_shc/translations/ja.json @@ -33,6 +33,5 @@ "title": "SHC\u8a8d\u8a3c\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/nl.json b/homeassistant/components/bosch_shc/translations/nl.json index 8a6cbd6cfdd..50aa82e2c7b 100644 --- a/homeassistant/components/bosch_shc/translations/nl.json +++ b/homeassistant/components/bosch_shc/translations/nl.json @@ -33,6 +33,5 @@ "title": "SHC-authenticatieparameters" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/no.json b/homeassistant/components/bosch_shc/translations/no.json index 53d64519fd5..1b5b9fb0642 100644 --- a/homeassistant/components/bosch_shc/translations/no.json +++ b/homeassistant/components/bosch_shc/translations/no.json @@ -33,6 +33,5 @@ "title": "SHC-autentiseringsparametere" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/pl.json b/homeassistant/components/bosch_shc/translations/pl.json index 6b21fd87b7c..11ae44dbe48 100644 --- a/homeassistant/components/bosch_shc/translations/pl.json +++ b/homeassistant/components/bosch_shc/translations/pl.json @@ -33,6 +33,5 @@ "title": "Parametry uwierzytelniania SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/pt-BR.json b/homeassistant/components/bosch_shc/translations/pt-BR.json index f8a74157ab3..22f5bbe22bb 100644 --- a/homeassistant/components/bosch_shc/translations/pt-BR.json +++ b/homeassistant/components/bosch_shc/translations/pt-BR.json @@ -33,6 +33,5 @@ "title": "Par\u00e2metros de autentica\u00e7\u00e3o SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/ru.json b/homeassistant/components/bosch_shc/translations/ru.json index 498003b2501..c5a6a53582f 100644 --- a/homeassistant/components/bosch_shc/translations/ru.json +++ b/homeassistant/components/bosch_shc/translations/ru.json @@ -33,6 +33,5 @@ "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 SHC" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/tr.json b/homeassistant/components/bosch_shc/translations/tr.json index f48286cf9cd..173b8e124ca 100644 --- a/homeassistant/components/bosch_shc/translations/tr.json +++ b/homeassistant/components/bosch_shc/translations/tr.json @@ -33,6 +33,5 @@ "title": "SHC kimlik do\u011frulama parametreleri" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/zh-Hant.json b/homeassistant/components/bosch_shc/translations/zh-Hant.json index fd667210d3e..2e4086051d6 100644 --- a/homeassistant/components/bosch_shc/translations/zh-Hant.json +++ b/homeassistant/components/bosch_shc/translations/zh-Hant.json @@ -33,6 +33,5 @@ "title": "SHC \u8a8d\u8b49\u53c3\u6578" } } - }, - "title": "Bosch SHC" + } } \ No newline at end of file diff --git a/homeassistant/components/braviatv/translations/bg.json b/homeassistant/components/braviatv/translations/bg.json index d05511b8d29..ef8edbdab20 100644 --- a/homeassistant/components/braviatv/translations/bg.json +++ b/homeassistant/components/braviatv/translations/bg.json @@ -17,8 +17,7 @@ "user": { "data": { "host": "\u0425\u043e\u0441\u0442" - }, - "title": "Sony Bravia TV" + } } } } diff --git a/homeassistant/components/braviatv/translations/ca.json b/homeassistant/components/braviatv/translations/ca.json index b8616f5e8ba..2f35c77caa1 100644 --- a/homeassistant/components/braviatv/translations/ca.json +++ b/homeassistant/components/braviatv/translations/ca.json @@ -21,8 +21,7 @@ "data": { "host": "Amfitri\u00f3" }, - "description": "Assegura't que el televisor est\u00e0 engegat abans de configurar-lo.", - "title": "Televisor Sony Bravia" + "description": "Assegura't que el televisor est\u00e0 engegat abans de configurar-lo." } } }, diff --git a/homeassistant/components/braviatv/translations/cs.json b/homeassistant/components/braviatv/translations/cs.json index ae6e3e5c851..f08c5d82861 100644 --- a/homeassistant/components/braviatv/translations/cs.json +++ b/homeassistant/components/braviatv/translations/cs.json @@ -20,8 +20,7 @@ "data": { "host": "Hostitel" }, - "description": "Nastavte integraci televize Sony Bravia. Pokud m\u00e1te s nastaven\u00edm probl\u00e9my, pod\u00edvejte se na: https://www.home-assistant.io/integrations/braviatv\n\n Ujist\u011bte se, \u017ee va\u0161e televize je zapnut\u00e1.", - "title": "Televize Sony Bravia" + "description": "Nastavte integraci televize Sony Bravia. Pokud m\u00e1te s nastaven\u00edm probl\u00e9my, pod\u00edvejte se na: https://www.home-assistant.io/integrations/braviatv\n\n Ujist\u011bte se, \u017ee va\u0161e televize je zapnut\u00e1." } } }, diff --git a/homeassistant/components/braviatv/translations/da.json b/homeassistant/components/braviatv/translations/da.json index 006d6b708e5..5b9778575a4 100644 --- a/homeassistant/components/braviatv/translations/da.json +++ b/homeassistant/components/braviatv/translations/da.json @@ -3,9 +3,6 @@ "step": { "authorize": { "title": "Godkend Sony Bravia TV" - }, - "user": { - "title": "Sony Bravia TV" } } } diff --git a/homeassistant/components/braviatv/translations/de.json b/homeassistant/components/braviatv/translations/de.json index 55ddbec464e..ff00828c0f3 100644 --- a/homeassistant/components/braviatv/translations/de.json +++ b/homeassistant/components/braviatv/translations/de.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Stelle sicher, dass dein Fernseher eingeschaltet ist, bevor du versuchst, ihn einzurichten.", - "title": "Sony Bravia TV" + "description": "Stelle sicher, dass dein Fernseher eingeschaltet ist, bevor du versuchst, ihn einzurichten." } } }, diff --git a/homeassistant/components/braviatv/translations/el.json b/homeassistant/components/braviatv/translations/el.json index 489bf0a7c36..070f546d532 100644 --- a/homeassistant/components/braviatv/translations/el.json +++ b/homeassistant/components/braviatv/translations/el.json @@ -21,8 +21,7 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7\u03c2 Sony Bravia. \u0395\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://www.home-assistant.io/integrations/braviatv \n\n\u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7.", - "title": "\u03a4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7 Sony Bravia" + "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7\u03c2 Sony Bravia. \u0395\u03ac\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://www.home-assistant.io/integrations/braviatv \n\n\u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7." } } }, diff --git a/homeassistant/components/braviatv/translations/en.json b/homeassistant/components/braviatv/translations/en.json index 9cc2ac23d17..45722d13b9a 100644 --- a/homeassistant/components/braviatv/translations/en.json +++ b/homeassistant/components/braviatv/translations/en.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Ensure that your TV is turned on before trying to set it up.", - "title": "Sony Bravia TV" + "description": "Ensure that your TV is turned on before trying to set it up." } } }, diff --git a/homeassistant/components/braviatv/translations/es-419.json b/homeassistant/components/braviatv/translations/es-419.json index 319eff13b98..ef12c71d681 100644 --- a/homeassistant/components/braviatv/translations/es-419.json +++ b/homeassistant/components/braviatv/translations/es-419.json @@ -18,8 +18,7 @@ "data": { "host": "Nombre de host de TV o direcci\u00f3n IP" }, - "description": "Configure la integraci\u00f3n de Sony Bravia TV. Si tiene problemas con la configuraci\u00f3n, vaya a: https://www.home-assistant.io/integrations/braviatv \n\n Aseg\u00farese de que su televisi\u00f3n est\u00e9 encendida.", - "title": "Sony Bravia TV" + "description": "Configure la integraci\u00f3n de Sony Bravia TV. Si tiene problemas con la configuraci\u00f3n, vaya a: https://www.home-assistant.io/integrations/braviatv \n\n Aseg\u00farese de que su televisi\u00f3n est\u00e9 encendida." } } }, diff --git a/homeassistant/components/braviatv/translations/es.json b/homeassistant/components/braviatv/translations/es.json index d095deea33b..401a254eeaf 100644 --- a/homeassistant/components/braviatv/translations/es.json +++ b/homeassistant/components/braviatv/translations/es.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Configura la integraci\u00f3n del televisor Sony Bravia. Si tienes problemas con la configuraci\u00f3n, ve a: https://www.home-assistant.io/integrations/braviatv\n\nAseg\u00farate de que tu televisor est\u00e1 encendido.", - "title": "Televisor Sony Bravia" + "description": "Configura la integraci\u00f3n del televisor Sony Bravia. Si tienes problemas con la configuraci\u00f3n, ve a: https://www.home-assistant.io/integrations/braviatv\n\nAseg\u00farate de que tu televisor est\u00e1 encendido." } } }, diff --git a/homeassistant/components/braviatv/translations/et.json b/homeassistant/components/braviatv/translations/et.json index c63e3635185..ecfc89b968d 100644 --- a/homeassistant/components/braviatv/translations/et.json +++ b/homeassistant/components/braviatv/translations/et.json @@ -21,8 +21,7 @@ "data": { "host": "" }, - "description": "Enne teleri seadistamist veendu, et see oleks sisse l\u00fclitatud.", - "title": "" + "description": "Enne teleri seadistamist veendu, et see oleks sisse l\u00fclitatud." } } }, diff --git a/homeassistant/components/braviatv/translations/fr.json b/homeassistant/components/braviatv/translations/fr.json index f85aea705fb..0290dad6857 100644 --- a/homeassistant/components/braviatv/translations/fr.json +++ b/homeassistant/components/braviatv/translations/fr.json @@ -21,8 +21,7 @@ "data": { "host": "H\u00f4te" }, - "description": "Assurez-vous que votre t\u00e9l\u00e9viseur est allum\u00e9 avant d'essayer de le configurer.", - "title": "Sony Bravia TV" + "description": "Assurez-vous que votre t\u00e9l\u00e9viseur est allum\u00e9 avant d'essayer de le configurer." } } }, diff --git a/homeassistant/components/braviatv/translations/hu.json b/homeassistant/components/braviatv/translations/hu.json index 554e74c52b1..d0d372df898 100644 --- a/homeassistant/components/braviatv/translations/hu.json +++ b/homeassistant/components/braviatv/translations/hu.json @@ -21,8 +21,7 @@ "data": { "host": "C\u00edm" }, - "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a TV k\u00e9sz\u00fcl\u00e9k be van kapcsolva a be\u00e1ll\u00edt\u00e1s el\u0151tt.", - "title": "Sony Bravia TV" + "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a TV k\u00e9sz\u00fcl\u00e9k be van kapcsolva a be\u00e1ll\u00edt\u00e1s el\u0151tt." } } }, diff --git a/homeassistant/components/braviatv/translations/id.json b/homeassistant/components/braviatv/translations/id.json index cb7d5396f7a..e387a2113b0 100644 --- a/homeassistant/components/braviatv/translations/id.json +++ b/homeassistant/components/braviatv/translations/id.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Pastikan TV Anda dinyalakan sebelum menyiapkan.", - "title": "TV Sony Bravia" + "description": "Pastikan TV Anda dinyalakan sebelum menyiapkan." } } }, diff --git a/homeassistant/components/braviatv/translations/it.json b/homeassistant/components/braviatv/translations/it.json index 7ab149fa6c7..3dd57d1359a 100644 --- a/homeassistant/components/braviatv/translations/it.json +++ b/homeassistant/components/braviatv/translations/it.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Assicurati che la tua TV sia accesa prima di provare a configurarla.", - "title": "Sony Bravia TV" + "description": "Assicurati che la tua TV sia accesa prima di provare a configurarla." } } }, diff --git a/homeassistant/components/braviatv/translations/ja.json b/homeassistant/components/braviatv/translations/ja.json index 87f903f0640..860fd5c89ea 100644 --- a/homeassistant/components/braviatv/translations/ja.json +++ b/homeassistant/components/braviatv/translations/ja.json @@ -21,8 +21,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "\u30bd\u30cb\u30fc Bravia TV\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/braviatv \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\n\n\u304d\u3061\u3093\u3068\u30c6\u30ec\u30d3\u306e\u96fb\u6e90\u304c\u5165\u3063\u3066\u3044\u308b\u3053\u3068\u3082\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30bd\u30cb\u30fc Bravia TV" + "description": "\u30bd\u30cb\u30fc Bravia TV\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/braviatv \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\n\n\u304d\u3061\u3093\u3068\u30c6\u30ec\u30d3\u306e\u96fb\u6e90\u304c\u5165\u3063\u3066\u3044\u308b\u3053\u3068\u3082\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } }, diff --git a/homeassistant/components/braviatv/translations/ko.json b/homeassistant/components/braviatv/translations/ko.json index 7bad0f0047c..78c02bceb44 100644 --- a/homeassistant/components/braviatv/translations/ko.json +++ b/homeassistant/components/braviatv/translations/ko.json @@ -21,8 +21,7 @@ "data": { "host": "\ud638\uc2a4\ud2b8" }, - "description": "Sony Bravia TV \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694. \uad6c\uc131\uc5d0 \ubb38\uc81c\uac00 \uc788\ub294 \uacbd\uc6b0 https://www.home-assistant.io/integrations/braviatv \ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n\nTV\uac00 \ucf1c\uc838 \uc788\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694.", - "title": "Sony Bravia TV" + "description": "Sony Bravia TV \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694. \uad6c\uc131\uc5d0 \ubb38\uc81c\uac00 \uc788\ub294 \uacbd\uc6b0 https://www.home-assistant.io/integrations/braviatv \ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n\nTV\uac00 \ucf1c\uc838 \uc788\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694." } } }, diff --git a/homeassistant/components/braviatv/translations/lb.json b/homeassistant/components/braviatv/translations/lb.json index 3bcf8702c4f..6510666ad55 100644 --- a/homeassistant/components/braviatv/translations/lb.json +++ b/homeassistant/components/braviatv/translations/lb.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Sony Bravia TV Integratioun ariichten. Falls et Problemer mat der Konfiguratioun g\u00ebtt g\u00e9i op:\nhttps://www.home-assistant.io/integrations/braviatv\nStell s\u00e9cher dass d\u00e4in Fernseh un ass.", - "title": "Sony Bravia TV" + "description": "Sony Bravia TV Integratioun ariichten. Falls et Problemer mat der Konfiguratioun g\u00ebtt g\u00e9i op:\nhttps://www.home-assistant.io/integrations/braviatv\nStell s\u00e9cher dass d\u00e4in Fernseh un ass." } } }, diff --git a/homeassistant/components/braviatv/translations/nl.json b/homeassistant/components/braviatv/translations/nl.json index 133c5277045..f839b83e883 100644 --- a/homeassistant/components/braviatv/translations/nl.json +++ b/homeassistant/components/braviatv/translations/nl.json @@ -21,8 +21,7 @@ "data": { "host": "Host" }, - "description": "Zorg ervoor dat uw TV aan staat voordat u hem probeert in te stellen.", - "title": "Sony Bravia TV" + "description": "Zorg ervoor dat uw TV aan staat voordat u hem probeert in te stellen." } } }, diff --git a/homeassistant/components/braviatv/translations/no.json b/homeassistant/components/braviatv/translations/no.json index 6465db8d3c0..067a2fcda97 100644 --- a/homeassistant/components/braviatv/translations/no.json +++ b/homeassistant/components/braviatv/translations/no.json @@ -21,8 +21,7 @@ "data": { "host": "Vert" }, - "description": "S\u00f8rg for at TV-en er sl\u00e5tt p\u00e5 f\u00f8r du pr\u00f8ver \u00e5 sette den opp.", - "title": "" + "description": "S\u00f8rg for at TV-en er sl\u00e5tt p\u00e5 f\u00f8r du pr\u00f8ver \u00e5 sette den opp." } } }, diff --git a/homeassistant/components/braviatv/translations/pl.json b/homeassistant/components/braviatv/translations/pl.json index 354962a62d9..83521554e21 100644 --- a/homeassistant/components/braviatv/translations/pl.json +++ b/homeassistant/components/braviatv/translations/pl.json @@ -21,8 +21,7 @@ "data": { "host": "Nazwa hosta lub adres IP" }, - "description": "Upewnij si\u0119, \u017ce telewizor jest w\u0142\u0105czony, zanim spr\u00f3bujesz go skonfigurowa\u0107.", - "title": "Sony Bravia TV" + "description": "Upewnij si\u0119, \u017ce telewizor jest w\u0142\u0105czony, zanim spr\u00f3bujesz go skonfigurowa\u0107." } } }, diff --git a/homeassistant/components/braviatv/translations/pt-BR.json b/homeassistant/components/braviatv/translations/pt-BR.json index a784e30d84c..3931935ff38 100644 --- a/homeassistant/components/braviatv/translations/pt-BR.json +++ b/homeassistant/components/braviatv/translations/pt-BR.json @@ -21,8 +21,7 @@ "data": { "host": "Nome do host" }, - "description": "Certifique-se de que sua TV esteja ligada antes de tentar configur\u00e1-la.", - "title": "Sony Bravia TV" + "description": "Certifique-se de que sua TV esteja ligada antes de tentar configur\u00e1-la." } } }, diff --git a/homeassistant/components/braviatv/translations/pt.json b/homeassistant/components/braviatv/translations/pt.json index 5e5f1367f58..e113d74d6fc 100644 --- a/homeassistant/components/braviatv/translations/pt.json +++ b/homeassistant/components/braviatv/translations/pt.json @@ -19,8 +19,7 @@ "user": { "data": { "host": "Servidor" - }, - "title": "TV Sony Bravia" + } } } }, diff --git a/homeassistant/components/braviatv/translations/ru.json b/homeassistant/components/braviatv/translations/ru.json index 3aeb83f7686..046a46c5ae4 100644 --- a/homeassistant/components/braviatv/translations/ru.json +++ b/homeassistant/components/braviatv/translations/ru.json @@ -21,8 +21,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d.", - "title": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Sony Bravia" + "description": "\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u0430\u0448 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 \u0432\u043a\u043b\u044e\u0447\u0435\u043d." } } }, diff --git a/homeassistant/components/braviatv/translations/sl.json b/homeassistant/components/braviatv/translations/sl.json index ae98bf15dc3..d412b545579 100644 --- a/homeassistant/components/braviatv/translations/sl.json +++ b/homeassistant/components/braviatv/translations/sl.json @@ -17,8 +17,7 @@ "data": { "host": "TV ime gostitelja ali IP naslov" }, - "description": "Nastavite integracijo Sony Bravia TV. \u010ce imate te\u017eave s konfiguracijo, pojdite na: https://www.home-assistant.io/integrations/braviatv \n\n Prepri\u010dajte se, da je va\u0161 televizor vklopljen.", - "title": "Sony Bravia TV" + "description": "Nastavite integracijo Sony Bravia TV. \u010ce imate te\u017eave s konfiguracijo, pojdite na: https://www.home-assistant.io/integrations/braviatv \n\n Prepri\u010dajte se, da je va\u0161 televizor vklopljen." } } }, diff --git a/homeassistant/components/braviatv/translations/sv.json b/homeassistant/components/braviatv/translations/sv.json index b07494f15cb..e3b98b4e76e 100644 --- a/homeassistant/components/braviatv/translations/sv.json +++ b/homeassistant/components/braviatv/translations/sv.json @@ -14,8 +14,7 @@ "user": { "data": { "host": "V\u00e4rdnamn eller IP-adress f\u00f6r TV" - }, - "title": "Sony Bravia TV" + } } } } diff --git a/homeassistant/components/braviatv/translations/tr.json b/homeassistant/components/braviatv/translations/tr.json index b6df4a7999a..cf5cc45640e 100644 --- a/homeassistant/components/braviatv/translations/tr.json +++ b/homeassistant/components/braviatv/translations/tr.json @@ -21,8 +21,7 @@ "data": { "host": "Ana Bilgisayar" }, - "description": "Kurmaya \u00e7al\u0131\u015fmadan \u00f6nce TV'nizin a\u00e7\u0131k oldu\u011fundan emin olun.", - "title": "Sony Bravia TV" + "description": "Kurmaya \u00e7al\u0131\u015fmadan \u00f6nce TV'nizin a\u00e7\u0131k oldu\u011fundan emin olun." } } }, diff --git a/homeassistant/components/braviatv/translations/uk.json b/homeassistant/components/braviatv/translations/uk.json index 7f66329c57e..5d9e22e59de 100644 --- a/homeassistant/components/braviatv/translations/uk.json +++ b/homeassistant/components/braviatv/translations/uk.json @@ -21,8 +21,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0454\u044e \u043f\u043e \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457:\nhttps://www.home-assistant.io/integrations/braviatv", - "title": "\u0422\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 Sony Bravia" + "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0454\u044e \u043f\u043e \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457:\nhttps://www.home-assistant.io/integrations/braviatv" } } }, diff --git a/homeassistant/components/braviatv/translations/zh-Hans.json b/homeassistant/components/braviatv/translations/zh-Hans.json index d02d562d55d..447c136dcf4 100644 --- a/homeassistant/components/braviatv/translations/zh-Hans.json +++ b/homeassistant/components/braviatv/translations/zh-Hans.json @@ -9,8 +9,7 @@ "title": "\u6388\u6743 Sony Bravia \u7535\u89c6" }, "user": { - "description": "\u8bbe\u7f6e Sony Bravia \u7535\u89c6\u96c6\u6210\u3002\u5982\u679c\u60a8\u5728\u914d\u7f6e\u65b9\u9762\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u8bbf\u95ee\uff1ahttps://www.home-assistant.io/integrations/braviatv\n\u786e\u4fdd\u7535\u89c6\u5df2\u6253\u5f00\u3002", - "title": "Sony Bravia TV" + "description": "\u8bbe\u7f6e Sony Bravia \u7535\u89c6\u96c6\u6210\u3002\u5982\u679c\u60a8\u5728\u914d\u7f6e\u65b9\u9762\u9047\u5230\u95ee\u9898\uff0c\u8bf7\u8bbf\u95ee\uff1ahttps://www.home-assistant.io/integrations/braviatv\n\u786e\u4fdd\u7535\u89c6\u5df2\u6253\u5f00\u3002" } } }, diff --git a/homeassistant/components/braviatv/translations/zh-Hant.json b/homeassistant/components/braviatv/translations/zh-Hant.json index 35ef6ef2e4f..1fb0931d4b6 100644 --- a/homeassistant/components/braviatv/translations/zh-Hant.json +++ b/homeassistant/components/braviatv/translations/zh-Hant.json @@ -21,8 +21,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef" }, - "description": "\u65bc\u8a2d\u5b9a\u524d\u78ba\u5b9a\u96fb\u8996\u5df2\u7d93\u958b\u555f\u3002", - "title": "Sony Bravia \u96fb\u8996" + "description": "\u65bc\u8a2d\u5b9a\u524d\u78ba\u5b9a\u96fb\u8996\u5df2\u7d93\u958b\u555f\u3002" } } }, diff --git a/homeassistant/components/climacell/translations/af.json b/homeassistant/components/climacell/translations/af.json index b62fc7023a4..d05e07e4eff 100644 --- a/homeassistant/components/climacell/translations/af.json +++ b/homeassistant/components/climacell/translations/af.json @@ -2,9 +2,8 @@ "options": { "step": { "init": { - "title": "Update ClimaCell opties" + "title": "Update [%key:component::climacell::title%] opties" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/bg.json b/homeassistant/components/climacell/translations/bg.json deleted file mode 100644 index af84485310d..00000000000 --- a/homeassistant/components/climacell/translations/bg.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "config": { - "error": { - "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" - }, - "step": { - "user": { - "data": { - "api_key": "API \u043a\u043b\u044e\u0447", - "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", - "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", - "name": "\u0418\u043c\u0435" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ca.json b/homeassistant/components/climacell/translations/ca.json index 78d43991e90..2b6abb46737 100644 --- a/homeassistant/components/climacell/translations/ca.json +++ b/homeassistant/components/climacell/translations/ca.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_api_key": "Clau API inv\u00e0lida", - "rate_limited": "Freq\u00fc\u00e8ncia limitada temporalment, torna-ho a provar m\u00e9s tard.", - "unknown": "Error inesperat" - }, - "step": { - "user": { - "data": { - "api_key": "Clau API", - "api_version": "Versi\u00f3 de l'API", - "latitude": "Latitud", - "longitude": "Longitud", - "name": "Nom" - }, - "description": "Si no es proporcionen la Latitud i Longitud, s'utilitzaran els valors per defecte de la configuraci\u00f3 de Home Assistant. Es crear\u00e0 una entitat per a cada tipus de previsi\u00f3, per\u00f2 nom\u00e9s s'habilitaran les que seleccionis." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Actualitzaci\u00f3 d'opcions de ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/cs.json b/homeassistant/components/climacell/translations/cs.json deleted file mode 100644 index e9a608680d5..00000000000 --- a/homeassistant/components/climacell/translations/cs.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "config": { - "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_api_key": "Neplatn\u00fd kl\u00ed\u010d API", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, - "step": { - "user": { - "data": { - "api_key": "Kl\u00ed\u010d API", - "api_version": "Verze API", - "latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka", - "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka", - "name": "Jm\u00e9no" - } - } - } - }, - "title": "ClimaCell" -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/de.json b/homeassistant/components/climacell/translations/de.json index 01e9a81647e..7c3e929dde2 100644 --- a/homeassistant/components/climacell/translations/de.json +++ b/homeassistant/components/climacell/translations/de.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", - "rate_limited": "Aktuelle Aktualisierungsrate gedrosselt, bitte versuche es sp\u00e4ter erneut.", - "unknown": "Unerwarteter Fehler" - }, - "step": { - "user": { - "data": { - "api_key": "API-Schl\u00fcssel", - "api_version": "API Version", - "latitude": "Breitengrad", - "longitude": "L\u00e4ngengrad", - "name": "Name" - }, - "description": "Wenn Breitengrad und L\u00e4ngengrad nicht angegeben werden, werden die Standardwerte in der Home Assistant-Konfiguration verwendet. F\u00fcr jeden Vorhersagetyp wird eine Entit\u00e4t erstellt, aber nur die von Ihnen ausgew\u00e4hlten werden standardm\u00e4\u00dfig aktiviert." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "ClimaCell-Optionen aktualisieren" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/el.json b/homeassistant/components/climacell/translations/el.json index 26c6d5e8d40..392573f693c 100644 --- a/homeassistant/components/climacell/translations/el.json +++ b/homeassistant/components/climacell/translations/el.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", - "rate_limited": "\u0391\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae \u03b7 \u03c4\u03b9\u03bc\u03ae \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03b1\u03c1\u03b3\u03cc\u03c4\u03b5\u03c1\u03b1.", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, - "step": { - "user": { - "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "api_version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 API", - "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03a0\u03bb\u03ac\u03c4\u03bf\u03c2", - "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u039c\u03ae\u03ba\u03bf\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" - }, - "description": "\u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03c4\u03bf \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2 \u03ba\u03b1\u03b9 \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2, \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd \u03bf\u03b9 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c4\u03b9\u03bc\u03ad\u03c2 \u03c3\u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c4\u03bf\u03c5 Home Assistant. \u0398\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03bc\u03b9\u03b1 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c4\u03cd\u03c0\u03bf \u03b4\u03b5\u03bb\u03c4\u03af\u03bf\u03c5, \u03b1\u03bb\u03bb\u03ac \u03bc\u03cc\u03bd\u03bf \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c0\u03bf\u03c5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03b3\u03b5\u03c4\u03b5 \u03b8\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03bf\u03cd\u03bd \u03b1\u03c0\u03cc \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/en.json b/homeassistant/components/climacell/translations/en.json index 3e5cd436ba8..a35be85d5b2 100644 --- a/homeassistant/components/climacell/translations/en.json +++ b/homeassistant/components/climacell/translations/en.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Failed to connect", - "invalid_api_key": "Invalid API key", - "rate_limited": "Currently rate limited, please try again later.", - "unknown": "Unexpected error" - }, - "step": { - "user": { - "data": { - "api_key": "API Key", - "api_version": "API Version", - "latitude": "Latitude", - "longitude": "Longitude", - "name": "Name" - }, - "description": "If Latitude and Longitude are not provided, the default values in the Home Assistant configuration will be used. An entity will be created for each forecast type but only the ones you select will be enabled by default." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Update ClimaCell Options" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es-419.json b/homeassistant/components/climacell/translations/es-419.json index 17c63089dbf..449ad1ba367 100644 --- a/homeassistant/components/climacell/translations/es-419.json +++ b/homeassistant/components/climacell/translations/es-419.json @@ -1,16 +1,4 @@ { - "config": { - "error": { - "rate_limited": "Actualmente la tarifa est\u00e1 limitada. Vuelve a intentarlo m\u00e1s tarde." - }, - "step": { - "user": { - "data": { - "api_version": "Versi\u00f3n de la API" - } - } - } - }, "options": { "step": { "init": { @@ -21,6 +9,5 @@ "title": "Actualizar opciones de ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/es.json b/homeassistant/components/climacell/translations/es.json index dba1957dd21..056d26b077d 100644 --- a/homeassistant/components/climacell/translations/es.json +++ b/homeassistant/components/climacell/translations/es.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Fallo al conectar", - "invalid_api_key": "Clave API no v\u00e1lida", - "rate_limited": "Actualmente la tarifa est\u00e1 limitada, por favor int\u00e9ntelo m\u00e1s tarde.", - "unknown": "Error inesperado" - }, - "step": { - "user": { - "data": { - "api_key": "Clave API", - "api_version": "Versi\u00f3n del API", - "latitude": "Latitud", - "longitude": "Longitud", - "name": "Nombre" - }, - "description": "Si no se proporcionan Latitud y Longitud , se utilizar\u00e1n los valores predeterminados en la configuraci\u00f3n de Home Assistant. Se crear\u00e1 una entidad para cada tipo de pron\u00f3stico, pero solo las que seleccione estar\u00e1n habilitadas de forma predeterminada." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Actualizaci\u00f3n de opciones de ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/et.json b/homeassistant/components/climacell/translations/et.json index 46ac184fa3c..5d915a87d80 100644 --- a/homeassistant/components/climacell/translations/et.json +++ b/homeassistant/components/climacell/translations/et.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u00dchendamine nurjus", - "invalid_api_key": "Vale API v\u00f5ti", - "rate_limited": "Hetkel on p\u00e4ringud piiratud, proovi hiljem uuesti.", - "unknown": "Ootamatu t\u00f5rge" - }, - "step": { - "user": { - "data": { - "api_key": "API v\u00f5ti", - "api_version": "API versioon", - "latitude": "Laiuskraad", - "longitude": "Pikkuskraad", - "name": "Nimi" - }, - "description": "Kui [%key:component::climacell::config::step::user::d ata::latitude%] ja [%key:component::climacell::config::step::user::d ata::longitude%] andmed pole sisestatud kasutatakse Home Assistanti vaikev\u00e4\u00e4rtusi. Olem luuakse iga prognoosit\u00fc\u00fcbi jaoks kuid vaikimisi lubatakse ainult need, mille valid." - } - } - }, "options": { "step": { "init": { @@ -26,9 +6,8 @@ "timestep": "Minuteid NowCasti prognooside vahel" }, "description": "Kui otsustad lubada \"nowcast\" prognoosi\u00fcksuse, saad seadistada minutite arvu iga prognoosi vahel. Esitatavate prognooside arv s\u00f5ltub prognooside vahel valitud minutite arvust.", - "title": "V\u00e4rskenda ClimaCell suvandeid" + "title": "V\u00e4rskenda [%key:component::climacell::title%] suvandeid" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/fr.json b/homeassistant/components/climacell/translations/fr.json index 9194073bb5a..b2c1285ecc9 100644 --- a/homeassistant/components/climacell/translations/fr.json +++ b/homeassistant/components/climacell/translations/fr.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u00c9chec de connexion", - "invalid_api_key": "Cl\u00e9 d'API non valide", - "rate_limited": "Nombre maximal de tentatives de connexion d\u00e9pass\u00e9, veuillez r\u00e9essayer ult\u00e9rieurement", - "unknown": "Erreur inattendue" - }, - "step": { - "user": { - "data": { - "api_key": "Cl\u00e9 d'API", - "api_version": "Version de l'API", - "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nom" - }, - "description": "Si Latitude et Longitude ne sont pas fournis, les valeurs par d\u00e9faut de la configuration de Home Assistant seront utilis\u00e9es. Une entit\u00e9 sera cr\u00e9\u00e9e pour chaque type de pr\u00e9vision, mais seules celles que vous s\u00e9lectionnez seront activ\u00e9es par d\u00e9faut." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Mettre \u00e0 jour les options ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/hu.json b/homeassistant/components/climacell/translations/hu.json index bdeb913275c..f65fa638ced 100644 --- a/homeassistant/components/climacell/translations/hu.json +++ b/homeassistant/components/climacell/translations/hu.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", - "rate_limited": "Jelenleg korl\u00e1tozott a hozz\u00e1f\u00e9r\u00e9s, k\u00e9rj\u00fck, pr\u00f3b\u00e1lja meg k\u00e9s\u0151bb \u00fajra.", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, - "step": { - "user": { - "data": { - "api_key": "API kulcs", - "api_version": "API Verzi\u00f3", - "latitude": "Sz\u00e9less\u00e9g", - "longitude": "Hossz\u00fas\u00e1g", - "name": "Elnevez\u00e9s" - }, - "description": "Ha a Sz\u00e9less\u00e9g \u00e9s Hossz\u00fas\u00e1g nincs megadva, akkor a Home Assistant konfigur\u00e1ci\u00f3j\u00e1ban l\u00e9v\u0151 alap\u00e9rtelmezett \u00e9rt\u00e9keket fogjuk haszn\u00e1lni. Minden el\u0151rejelz\u00e9si t\u00edpushoz l\u00e9trej\u00f6n egy entit\u00e1s, de alap\u00e9rtelmez\u00e9s szerint csak az \u00d6n \u00e1ltal kiv\u00e1lasztottak lesznek enged\u00e9lyezve." - } - } - }, "options": { "step": { "init": { @@ -26,9 +6,8 @@ "timestep": "Min. A NowCast el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt" }, "description": "Ha a `nowcast` el\u0151rejelz\u00e9si entit\u00e1s enged\u00e9lyez\u00e9s\u00e9t v\u00e1lasztja, be\u00e1ll\u00edthatja az egyes el\u0151rejelz\u00e9sek k\u00f6z\u00f6tti percek sz\u00e1m\u00e1t. A megadott el\u0151rejelz\u00e9sek sz\u00e1ma az el\u0151rejelz\u00e9sek k\u00f6z\u00f6tt kiv\u00e1lasztott percek sz\u00e1m\u00e1t\u00f3l f\u00fcgg.", - "title": "ClimaCell be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" + "title": "[%key:component::climacell::title%] be\u00e1ll\u00edt\u00e1sok friss\u00edt\u00e9se" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/id.json b/homeassistant/components/climacell/translations/id.json index 88b377261bb..57f88557a69 100644 --- a/homeassistant/components/climacell/translations/id.json +++ b/homeassistant/components/climacell/translations/id.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Gagal terhubung", - "invalid_api_key": "Kunci API tidak valid", - "rate_limited": "Saat ini tingkatnya dibatasi, coba lagi nanti.", - "unknown": "Kesalahan yang tidak diharapkan" - }, - "step": { - "user": { - "data": { - "api_key": "Kunci API", - "api_version": "Versi API", - "latitude": "Lintang", - "longitude": "Bujur", - "name": "Nama" - }, - "description": "Jika Lintang dan Bujur tidak tersedia, nilai default dalam konfigurasi Home Assistant akan digunakan. Entitas akan dibuat untuk setiap jenis prakiraan tetapi hanya yang Anda pilih yang akan diaktifkan secara default." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Perbarui Opsi ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/it.json b/homeassistant/components/climacell/translations/it.json index bd1bdd88238..b9667d6bfb1 100644 --- a/homeassistant/components/climacell/translations/it.json +++ b/homeassistant/components/climacell/translations/it.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Impossibile connettersi", - "invalid_api_key": "Chiave API non valida", - "rate_limited": "Al momento la tariffa \u00e8 limitata, riprova pi\u00f9 tardi.", - "unknown": "Errore imprevisto" - }, - "step": { - "user": { - "data": { - "api_key": "Chiave API", - "api_version": "Versione API", - "latitude": "Latitudine", - "longitude": "Logitudine", - "name": "Nome" - }, - "description": "Se Latitudine e Logitudine non vengono forniti, verranno utilizzati i valori predefiniti nella configurazione di Home Assistant. Verr\u00e0 creata un'entit\u00e0 per ogni tipo di previsione, ma solo quelli selezionati saranno abilitati per impostazione predefinita." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Aggiorna le opzioni di ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ja.json b/homeassistant/components/climacell/translations/ja.json index 5114f8e9881..5c78820c853 100644 --- a/homeassistant/components/climacell/translations/ja.json +++ b/homeassistant/components/climacell/translations/ja.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", - "rate_limited": "\u73fe\u5728\u30ec\u30fc\u30c8\u304c\u5236\u9650\u3055\u308c\u3066\u3044\u307e\u3059\u306e\u3067\u3001\u5f8c\u3067\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, - "step": { - "user": { - "data": { - "api_key": "API\u30ad\u30fc", - "api_version": "API\u30d0\u30fc\u30b8\u30e7\u30f3", - "latitude": "\u7def\u5ea6", - "longitude": "\u7d4c\u5ea6", - "name": "\u540d\u524d" - }, - "description": "\u7def\u5ea6\u3068\u7d4c\u5ea6\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001Home Assistant\u8a2d\u5b9a\u306e\u65e2\u5b9a\u5024\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059\u3002\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u306f\u4e88\u6e2c\u30bf\u30a4\u30d7\u3054\u3068\u306b\u4f5c\u6210\u3055\u308c\u307e\u3059\u304c\u3001\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u9078\u629e\u3057\u305f\u3082\u306e\u3060\u3051\u304c\u6709\u52b9\u306b\u306a\u308a\u307e\u3059\u3002" - } - } - }, "options": { "step": { "init": { @@ -26,9 +6,8 @@ "timestep": "\u6700\u5c0f: NowCast Forecasts\u306e\u9593" }, "description": "`nowcast` forecast(\u4e88\u6e2c) \u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u6709\u52b9\u306b\u3059\u308b\u3053\u3068\u3092\u9078\u629e\u3057\u305f\u5834\u5408\u3001\u5404\u4e88\u6e2c\u9593\u306e\u5206\u6570\u3092\u8a2d\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u63d0\u4f9b\u3055\u308c\u308bforecast(\u4e88\u6e2c)\u306e\u6570\u306f\u3001forecast(\u4e88\u6e2c)\u306e\u9593\u306b\u9078\u629e\u3057\u305f\u5206\u6570\u306b\u4f9d\u5b58\u3057\u307e\u3059\u3002", - "title": "ClimaCell\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u66f4\u65b0\u3057\u307e\u3059" + "title": "[%key:component::climacell::title%]\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u66f4\u65b0\u3057\u307e\u3059" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ko.json b/homeassistant/components/climacell/translations/ko.json index b5936bbc7d7..8accc07410d 100644 --- a/homeassistant/components/climacell/translations/ko.json +++ b/homeassistant/components/climacell/translations/ko.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "rate_limited": "\ud604\uc7ac \uc0ac\uc6a9 \ud69f\uc218\ub97c \ucd08\uacfc\ud588\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, - "step": { - "user": { - "data": { - "api_key": "API \ud0a4", - "api_version": "API \ubc84\uc804", - "latitude": "\uc704\ub3c4", - "longitude": "\uacbd\ub3c4", - "name": "\uc774\ub984" - }, - "description": "\uc704\ub3c4 \ubc0f \uacbd\ub3c4\uac00 \uc81c\uacf5\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 Home Assistant \uad6c\uc131\uc758 \uae30\ubcf8\uac12\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4. \uac01 \uc77c\uae30\uc608\ubcf4 \uc720\ud615\uc5d0 \ub300\ud574 \uad6c\uc131\uc694\uc18c\uac00 \uc0dd\uc131\ub418\uc9c0\ub9cc \uae30\ubcf8\uc801\uc73c\ub85c \uc120\ud0dd\ud55c \uad6c\uc131\uc694\uc18c\ub9cc \ud65c\uc131\ud654\ub429\ub2c8\ub2e4." - } - } - }, "options": { "step": { "init": { @@ -26,9 +6,8 @@ "timestep": "\ub2e8\uae30\uc608\uce21 \uc77c\uae30\uc608\ubcf4 \uac04 \ucd5c\uc18c \uc2dc\uac04" }, "description": "`nowcast` \uc77c\uae30\uc608\ubcf4 \uad6c\uc131\uc694\uc18c\ub97c \uc0ac\uc6a9\ud558\ub3c4\ub85d \uc120\ud0dd\ud55c \uacbd\uc6b0 \uac01 \uc77c\uae30\uc608\ubcf4 \uc0ac\uc774\uc758 \uc2dc\uac04(\ubd84)\uc744 \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc81c\uacf5\ub41c \uc77c\uae30\uc608\ubcf4 \ud69f\uc218\ub294 \uc608\uce21 \uac04 \uc120\ud0dd\ud55c \uc2dc\uac04(\ubd84)\uc5d0 \ub530\ub77c \ub2ec\ub77c\uc9d1\ub2c8\ub2e4.", - "title": "ClimaCell \uc635\uc158 \uc5c5\ub370\uc774\ud2b8\ud558\uae30" + "title": "[%key:component::climacell::title%] \uc635\uc158 \uc5c5\ub370\uc774\ud2b8\ud558\uae30" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/lb.json b/homeassistant/components/climacell/translations/lb.json deleted file mode 100644 index e075d198b7f..00000000000 --- a/homeassistant/components/climacell/translations/lb.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "api_version": "API Versioun" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/nl.json b/homeassistant/components/climacell/translations/nl.json index a8754e81943..a895fa8234d 100644 --- a/homeassistant/components/climacell/translations/nl.json +++ b/homeassistant/components/climacell/translations/nl.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Kan geen verbinding maken", - "invalid_api_key": "Ongeldige API-sleutel", - "rate_limited": "Momenteel is een beperkt aantal aanvragen mogelijk, probeer later opnieuw.", - "unknown": "Onverwachte fout" - }, - "step": { - "user": { - "data": { - "api_key": "API-sleutel", - "api_version": "API-versie", - "latitude": "Breedtegraad", - "longitude": "Lengtegraad", - "name": "Naam" - }, - "description": "Indien Breedtegraad en Lengtegraad niet worden opgegeven, worden de standaardwaarden in de Home Assistant-configuratie gebruikt. Er wordt een entiteit gemaakt voor elk voorspellingstype, maar alleen degenen die u selecteert worden standaard ingeschakeld." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Update ClimaCell Opties" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/no.json b/homeassistant/components/climacell/translations/no.json index c20c458c64e..9f050624967 100644 --- a/homeassistant/components/climacell/translations/no.json +++ b/homeassistant/components/climacell/translations/no.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Tilkobling mislyktes", - "invalid_api_key": "Ugyldig API-n\u00f8kkel", - "rate_limited": "Prisen er for \u00f8yeblikket begrenset. Pr\u00f8v igjen senere.", - "unknown": "Uventet feil" - }, - "step": { - "user": { - "data": { - "api_key": "API-n\u00f8kkel", - "api_version": "API-versjon", - "latitude": "Breddegrad", - "longitude": "Lengdegrad", - "name": "Navn" - }, - "description": "Hvis Breddegrad og Lengdegrad ikke er oppgitt, vil standardverdiene i Home Assistant-konfigurasjonen bli brukt. Det blir opprettet en entitet for hver prognosetype, men bare de du velger blir aktivert som standard." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Oppdater ClimaCell-alternativer" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pl.json b/homeassistant/components/climacell/translations/pl.json index e107be1e001..5f69764ffab 100644 --- a/homeassistant/components/climacell/translations/pl.json +++ b/homeassistant/components/climacell/translations/pl.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_api_key": "Nieprawid\u0142owy klucz API", - "rate_limited": "Przekroczono limit, spr\u00f3buj ponownie p\u00f3\u017aniej.", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, - "step": { - "user": { - "data": { - "api_key": "Klucz API", - "api_version": "Wersja API", - "latitude": "Szeroko\u015b\u0107 geograficzna", - "longitude": "D\u0142ugo\u015b\u0107 geograficzna", - "name": "Nazwa" - }, - "description": "Je\u015bli szeroko\u015b\u0107 i d\u0142ugo\u015b\u0107 geograficzna nie zostan\u0105 podane, zostan\u0105 u\u017cyte domy\u015blne warto\u015bci z konfiguracji Home Assistanta. Zostanie utworzona encja dla ka\u017cdego typu prognozy, ale domy\u015blnie w\u0142\u0105czone bed\u0105 tylko te, kt\u00f3re wybierzesz." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Opcje aktualizacji ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pt-BR.json b/homeassistant/components/climacell/translations/pt-BR.json index 54de15d1f7f..b7e71d45971 100644 --- a/homeassistant/components/climacell/translations/pt-BR.json +++ b/homeassistant/components/climacell/translations/pt-BR.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Falha ao conectar", - "invalid_api_key": "Chave de API inv\u00e1lida", - "rate_limited": "Taxa atualmente limitada, tente novamente mais tarde.", - "unknown": "Erro inesperado" - }, - "step": { - "user": { - "data": { - "api_key": "Chave da API", - "api_version": "Vers\u00e3o da API", - "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nome" - }, - "description": "Se Latitude e Longitude n\u00e3o forem fornecidos, os valores padr\u00f5es na configura\u00e7\u00e3o do Home Assistant ser\u00e3o usados. Uma entidade ser\u00e1 criada para cada tipo de previs\u00e3o, mas apenas as selecionadas ser\u00e3o habilitadas por padr\u00e3o." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "Atualizar as op\u00e7\u00f5es do ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/pt.json b/homeassistant/components/climacell/translations/pt.json deleted file mode 100644 index 5c790233b20..00000000000 --- a/homeassistant/components/climacell/translations/pt.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "config": { - "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_api_key": "Chave de API inv\u00e1lida", - "unknown": "Erro inesperado" - }, - "step": { - "user": { - "data": { - "api_key": "Chave da API", - "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nome" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/ru.json b/homeassistant/components/climacell/translations/ru.json index 0f1a80b5e09..9f3219ce4d6 100644 --- a/homeassistant/components/climacell/translations/ru.json +++ b/homeassistant/components/climacell/translations/ru.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", - "rate_limited": "\u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043f\u044b\u0442\u043e\u043a, \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, - "step": { - "user": { - "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", - "api_version": "\u0412\u0435\u0440\u0441\u0438\u044f API", - "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", - "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" - }, - "description": "\u0415\u0441\u043b\u0438 \u0428\u0438\u0440\u043e\u0442\u0430 \u0438 \u0414\u043e\u043b\u0433\u043e\u0442\u0430 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u044b, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 Home Assistant. \u041e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u043f\u0440\u043e\u0433\u043d\u043e\u0437\u0430, \u043d\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0412\u0430\u043c\u0438." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 ClimaCell" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sk.json b/homeassistant/components/climacell/translations/sk.json deleted file mode 100644 index 8e0bc629a13..00000000000 --- a/homeassistant/components/climacell/translations/sk.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "config": { - "error": { - "invalid_api_key": "Neplatn\u00fd API k\u013e\u00fa\u010d" - }, - "step": { - "user": { - "data": { - "api_key": "API k\u013e\u00fa\u010d", - "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka", - "name": "N\u00e1zov" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sv.json b/homeassistant/components/climacell/translations/sv.json deleted file mode 100644 index e6e7a77926f..00000000000 --- a/homeassistant/components/climacell/translations/sv.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "api_version": "API-version", - "latitude": "Latitud", - "longitude": "Longitud", - "name": "Namn" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/tr.json b/homeassistant/components/climacell/translations/tr.json index 369d4c6ae13..54e24f813e4 100644 --- a/homeassistant/components/climacell/translations/tr.json +++ b/homeassistant/components/climacell/translations/tr.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", - "rate_limited": "\u015eu anda oran s\u0131n\u0131rl\u0131, l\u00fctfen daha sonra tekrar deneyin.", - "unknown": "Beklenmeyen hata" - }, - "step": { - "user": { - "data": { - "api_key": "API Anahtar\u0131", - "api_version": "API S\u00fcr\u00fcm\u00fc", - "latitude": "Enlem", - "longitude": "Boylam", - "name": "Ad" - }, - "description": "Enlem ve Boylam sa\u011flanmazsa, Home Assistant yap\u0131land\u0131rmas\u0131ndaki varsay\u0131lan de\u011ferler kullan\u0131l\u0131r. Her tahmin t\u00fcr\u00fc i\u00e7in bir varl\u0131k olu\u015fturulacak, ancak varsay\u0131lan olarak yaln\u0131zca se\u00e7tikleriniz etkinle\u015ftirilecektir." - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "ClimaCell Se\u00e7eneklerini G\u00fcncelleyin" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/zh-Hans.json b/homeassistant/components/climacell/translations/zh-Hans.json deleted file mode 100644 index 315d060bc69..00000000000 --- a/homeassistant/components/climacell/translations/zh-Hans.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "api_key": "API Key", - "name": "\u540d\u5b57" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/zh-Hant.json b/homeassistant/components/climacell/translations/zh-Hant.json index 68e06219ae7..309b39ab242 100644 --- a/homeassistant/components/climacell/translations/zh-Hant.json +++ b/homeassistant/components/climacell/translations/zh-Hant.json @@ -1,24 +1,4 @@ { - "config": { - "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_api_key": "API \u91d1\u9470\u7121\u6548", - "rate_limited": "\u9054\u5230\u9650\u5236\u983b\u7387\u3001\u8acb\u7a0d\u5019\u518d\u8a66\u3002", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, - "step": { - "user": { - "data": { - "api_key": "API \u91d1\u9470", - "api_version": "API \u7248\u672c", - "latitude": "\u7def\u5ea6", - "longitude": "\u7d93\u5ea6", - "name": "\u540d\u7a31" - }, - "description": "\u5047\u5982\u672a\u63d0\u4f9b\u7def\u5ea6\u8207\u7d93\u5ea6\uff0c\u5c07\u6703\u4f7f\u7528 Home Assistant \u8a2d\u5b9a\u4f5c\u70ba\u9810\u8a2d\u503c\u3002\u6bcf\u4e00\u500b\u9810\u5831\u985e\u5225\u90fd\u6703\u7522\u751f\u4e00\u7d44\u5be6\u9ad4\uff0c\u6216\u8005\u9810\u8a2d\u70ba\u6240\u9078\u64c7\u555f\u7528\u7684\u9810\u5831\u3002" - } - } - }, "options": { "step": { "init": { @@ -29,6 +9,5 @@ "title": "\u66f4\u65b0 ClimaCell \u9078\u9805" } } - }, - "title": "ClimaCell" + } } \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/ar.json b/homeassistant/components/coinbase/translations/ar.json index 30655126631..ec83ad6d22b 100644 --- a/homeassistant/components/coinbase/translations/ar.json +++ b/homeassistant/components/coinbase/translations/ar.json @@ -3,8 +3,7 @@ "step": { "user": { "data": { - "api_token": "\u0633\u0631 API", - "exchange_rates": "\u0623\u0633\u0639\u0627\u0631 \u0627\u0644\u0635\u0631\u0641" + "api_token": "\u0633\u0631 API" }, "description": "\u064a\u0631\u062c\u0649 \u0625\u062f\u062e\u0627\u0644 \u062a\u0641\u0627\u0635\u064a\u0644 \u0645\u0641\u062a\u0627\u062d API \u0627\u0644\u062e\u0627\u0635 \u0628\u0643 \u0639\u0644\u0649 \u0627\u0644\u0646\u062d\u0648 \u0627\u0644\u0645\u0646\u0635\u0648\u0635 \u0639\u0644\u064a\u0647 \u0645\u0646 \u0642\u0628\u0644 Coinbase.", "title": "\u062a\u0641\u0627\u0635\u064a\u0644 \u0645\u0641\u062a\u0627\u062d Coinbase API" @@ -13,8 +12,6 @@ }, "options": { "error": { - "currency_unavaliable": "\u0644\u0627 \u064a\u062a\u0645 \u062a\u0648\u0641\u064a\u0631 \u0648\u0627\u062d\u062f \u0623\u0648 \u0623\u0643\u062b\u0631 \u0645\u0646 \u0623\u0631\u0635\u062f\u0629 \u0627\u0644\u0639\u0645\u0644\u0627\u062a \u0627\u0644\u0645\u0637\u0644\u0648\u0628\u0629 \u0628\u0648\u0627\u0633\u0637\u0629 Coinbase API \u0627\u0644\u062e\u0627\u0635 \u0628\u0643.", - "exchange_rate_unavaliable": "\u0644\u0627 \u064a\u062a\u0645 \u062a\u0648\u0641\u064a\u0631 \u0648\u0627\u062d\u062f \u0623\u0648 \u0623\u0643\u062b\u0631 \u0645\u0646 \u0623\u0633\u0639\u0627\u0631 \u0627\u0644\u0635\u0631\u0641 \u0627\u0644\u0645\u0637\u0644\u0648\u0628\u0629 \u0645\u0646 Coinbase.", "unknown": "\u062d\u062f\u062b \u062e\u0637\u0623 \u063a\u064a\u0631 \u0645\u062a\u0648\u0642\u0639" }, "step": { diff --git a/homeassistant/components/coinbase/translations/ca.json b/homeassistant/components/coinbase/translations/ca.json index 0543f97003a..116b611f272 100644 --- a/homeassistant/components/coinbase/translations/ca.json +++ b/homeassistant/components/coinbase/translations/ca.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Clau API", - "api_token": "Secret API", - "currencies": "Monedes del saldo del compte", - "exchange_rates": "Tipus de canvi" + "api_token": "Secret API" }, "description": "Introdueix els detalls de la teva clau API tal com els proporciona Coinbase.", "title": "Detalls de la clau API de Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "L'API de Coinbase no proporciona algun/s dels saldos de moneda que has sol\u00b7licitat.", - "currency_unavaliable": "L'API de Coinbase no proporciona algun/s dels saldos de moneda que has sol\u00b7licitat.", "exchange_rate_unavailable": "L'API de Coinbase no proporciona algun/s dels tipus de canvi que has sol\u00b7licitat.", - "exchange_rate_unavaliable": "L'API de Coinbase no proporciona algun/s dels tipus de canvi que has sol\u00b7licitat.", "unknown": "Error inesperat" }, "step": { diff --git a/homeassistant/components/coinbase/translations/cs.json b/homeassistant/components/coinbase/translations/cs.json index d25e431651d..7ec64b2fa14 100644 --- a/homeassistant/components/coinbase/translations/cs.json +++ b/homeassistant/components/coinbase/translations/cs.json @@ -12,8 +12,7 @@ "user": { "data": { "api_key": "Kl\u00ed\u010d API", - "api_token": "API Secret", - "exchange_rates": "Sm\u011bnn\u00e9 kurzy" + "api_token": "API Secret" }, "title": "Podrobnosti o API kl\u00ed\u010di Coinbase" } diff --git a/homeassistant/components/coinbase/translations/de.json b/homeassistant/components/coinbase/translations/de.json index d4b58ae42bd..f6200633950 100644 --- a/homeassistant/components/coinbase/translations/de.json +++ b/homeassistant/components/coinbase/translations/de.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API-Schl\u00fcssel", - "api_token": "API-Geheimnis", - "currencies": "Kontostand W\u00e4hrungen", - "exchange_rates": "Wechselkurse" + "api_token": "API-Geheimnis" }, "description": "Bitte gib die Details deines API-Schl\u00fcssels ein, wie von Coinbase bereitgestellt.", "title": "Coinbase API Schl\u00fcssel Details" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Eine oder mehrere der angeforderten W\u00e4hrungssalden werden von deiner Coinbase-API nicht bereitgestellt.", - "currency_unavaliable": "Eine oder mehrere der angeforderten W\u00e4hrungssalden werden von deiner Coinbase-API nicht bereitgestellt.", "exchange_rate_unavailable": "Einer oder mehrere der angeforderten Wechselkurse werden nicht von Coinbase bereitgestellt.", - "exchange_rate_unavaliable": "Einer oder mehrere der angeforderten Wechselkurse werden nicht von Coinbase bereitgestellt.", "unknown": "Unerwarteter Fehler" }, "step": { diff --git a/homeassistant/components/coinbase/translations/el.json b/homeassistant/components/coinbase/translations/el.json index 05d6a1f7415..6b2141f091f 100644 --- a/homeassistant/components/coinbase/translations/el.json +++ b/homeassistant/components/coinbase/translations/el.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "api_token": "\u039c\u03c5\u03c3\u03c4\u03b9\u03ba\u03cc API", - "currencies": "\u039d\u03bf\u03bc\u03af\u03c3\u03bc\u03b1\u03c4\u03b1 \u03c5\u03c0\u03bf\u03bb\u03bf\u03af\u03c0\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd", - "exchange_rates": "\u03a3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2" + "api_token": "\u039c\u03c5\u03c3\u03c4\u03b9\u03ba\u03cc API" }, "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c4\u03bf\u03c5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03b9\u03bf\u03cd API \u03c3\u03b1\u03c2, \u03cc\u03c0\u03c9\u03c2 \u03b1\u03c5\u03c4\u03ac \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd Coinbase.", "title": "\u0392\u03b1\u03c3\u03b9\u03ba\u03ad\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2 API \u03c4\u03bf\u03c5 Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "\u0388\u03bd\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03b1 \u03bd\u03bf\u03bc\u03b9\u03c3\u03bc\u03ac\u03c4\u03c9\u03bd \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf API \u03c4\u03b7\u03c2 Coinbase.", - "currency_unavaliable": "\u0388\u03bd\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03c5\u03c0\u03cc\u03bb\u03bf\u03b9\u03c0\u03b1 \u03bd\u03bf\u03bc\u03b9\u03c3\u03bc\u03ac\u03c4\u03c9\u03bd \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03bf Coinbase API \u03c3\u03b1\u03c2.", "exchange_rate_unavailable": "\u039c\u03af\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2 \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd Coinbase.", - "exchange_rate_unavaliable": "\u039c\u03af\u03b1 \u03ae \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b9\u03c2 \u03b6\u03b7\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03bc\u03b1\u03c4\u03b9\u03ba\u03ad\u03c2 \u03b9\u03c3\u03bf\u03c4\u03b9\u03bc\u03af\u03b5\u03c2 \u03b4\u03b5\u03bd \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd Coinbase.", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { diff --git a/homeassistant/components/coinbase/translations/en.json b/homeassistant/components/coinbase/translations/en.json index d5d7483e260..019159c8057 100644 --- a/homeassistant/components/coinbase/translations/en.json +++ b/homeassistant/components/coinbase/translations/en.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API Key", - "api_token": "API Secret", - "currencies": "Account Balance Currencies", - "exchange_rates": "Exchange Rates" + "api_token": "API Secret" }, "description": "Please enter the details of your API key as provided by Coinbase.", "title": "Coinbase API Key Details" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "One or more of the requested currency balances is not provided by your Coinbase API.", - "currency_unavaliable": "One or more of the requested currency balances is not provided by your Coinbase API.", "exchange_rate_unavailable": "One or more of the requested exchange rates is not provided by Coinbase.", - "exchange_rate_unavaliable": "One or more of the requested exchange rates is not provided by Coinbase.", "unknown": "Unexpected error" }, "step": { diff --git a/homeassistant/components/coinbase/translations/es-419.json b/homeassistant/components/coinbase/translations/es-419.json index c5bc63ee29a..1b72a0c0b2e 100644 --- a/homeassistant/components/coinbase/translations/es-419.json +++ b/homeassistant/components/coinbase/translations/es-419.json @@ -7,9 +7,7 @@ "step": { "user": { "data": { - "api_token": "Secreto de la API", - "currencies": "Monedas del saldo de la cuenta", - "exchange_rates": "Tipos de cambio" + "api_token": "Secreto de la API" }, "description": "Ingrese los detalles de su clave API proporcionada por Coinbase.", "title": "Detalles clave de la API de Coinbase" @@ -19,9 +17,7 @@ "options": { "error": { "currency_unavailable": "Su API de Coinbase no proporciona uno o m\u00e1s de los saldos de divisas solicitados.", - "currency_unavaliable": "Su API de Coinbase no proporciona uno o m\u00e1s de los saldos de divisas solicitados.", - "exchange_rate_unavailable": "Coinbase no proporciona uno o m\u00e1s de los tipos de cambio solicitados.", - "exchange_rate_unavaliable": "Coinbase no proporciona uno o m\u00e1s de los tipos de cambio solicitados." + "exchange_rate_unavailable": "Coinbase no proporciona uno o m\u00e1s de los tipos de cambio solicitados." } } } \ No newline at end of file diff --git a/homeassistant/components/coinbase/translations/es.json b/homeassistant/components/coinbase/translations/es.json index d8961ee6b3a..5454aca8ec6 100644 --- a/homeassistant/components/coinbase/translations/es.json +++ b/homeassistant/components/coinbase/translations/es.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Clave API", - "api_token": "Secreto de la API", - "currencies": "Saldo de la cuenta Monedas", - "exchange_rates": "Tipos de cambio" + "api_token": "Secreto de la API" }, "description": "Por favor, introduce los detalles de tu clave API tal y como te la ha proporcionado Coinbase.", "title": "Detalles de la clave API de Coinbase" @@ -25,9 +23,7 @@ }, "options": { "error": { - "currency_unavaliable": "La API de Coinbase no proporciona uno o m\u00e1s de los saldos de divisas solicitados.", "exchange_rate_unavailable": "El API de Coinbase no proporciona alguno/s de los tipos de cambio que has solicitado.", - "exchange_rate_unavaliable": "Coinbase no proporciona uno o m\u00e1s de los tipos de cambio solicitados.", "unknown": "Error inesperado" }, "step": { diff --git a/homeassistant/components/coinbase/translations/et.json b/homeassistant/components/coinbase/translations/et.json index 821d17656d2..14bd1eea370 100644 --- a/homeassistant/components/coinbase/translations/et.json +++ b/homeassistant/components/coinbase/translations/et.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API v\u00f5ti", - "api_token": "API salas\u00f5na", - "currencies": "Konto saldo valuutad", - "exchange_rates": "Vahetuskursid" + "api_token": "API salas\u00f5na" }, "description": "Sisesta Coinbase'i pakutava API-v\u00f5tme \u00fcksikasjad.", "title": "Coinbase'i API v\u00f5tme \u00fcksikasjad" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Coinbase API ei paku \u00fchte v\u00f5i mitut soovitud valuutasaldot.", - "currency_unavaliable": "Coinbase'i API ei paku \u00fchte v\u00f5i mitut taotletud valuutasaldot.", "exchange_rate_unavailable": "Coinbase ei paku \u00fchte v\u00f5i mitut soovitud vahetuskurssi.", - "exchange_rate_unavaliable": "\u00dchte v\u00f5i mitut taotletud vahetuskurssi Coinbase ei paku.", "unknown": "Ootamatu t\u00f5rge" }, "step": { diff --git a/homeassistant/components/coinbase/translations/fr.json b/homeassistant/components/coinbase/translations/fr.json index 77664cca73d..91f52941a89 100644 --- a/homeassistant/components/coinbase/translations/fr.json +++ b/homeassistant/components/coinbase/translations/fr.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Cl\u00e9 d'API", - "api_token": "API secr\u00e8te", - "currencies": "Devises du solde du compte", - "exchange_rates": "Taux d'\u00e9change" + "api_token": "API secr\u00e8te" }, "description": "Veuillez saisir les d\u00e9tails de votre cl\u00e9 API tels que fournis par Coinbase.", "title": "D\u00e9tails de la cl\u00e9 de l'API Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Un ou plusieurs des soldes de devises demand\u00e9s ne sont pas fournis par votre API Coinbase.", - "currency_unavaliable": "Un ou plusieurs des soldes de devises demand\u00e9s ne sont pas fournis par votre API Coinbase.", "exchange_rate_unavailable": "Un ou plusieurs des taux de change demand\u00e9s ne sont pas fournis par Coinbase.", - "exchange_rate_unavaliable": "Un ou plusieurs des taux de change demand\u00e9s ne sont pas fournis par Coinbase.", "unknown": "Erreur inattendue" }, "step": { diff --git a/homeassistant/components/coinbase/translations/hu.json b/homeassistant/components/coinbase/translations/hu.json index 44287da0ee6..bcf409d2dda 100644 --- a/homeassistant/components/coinbase/translations/hu.json +++ b/homeassistant/components/coinbase/translations/hu.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API kulcs", - "api_token": "API jelsz\u00f3", - "currencies": "Sz\u00e1mlaegyenleg-p\u00e9nznemek", - "exchange_rates": "\u00c1rfolyamok" + "api_token": "API jelsz\u00f3" }, "description": "K\u00e9rj\u00fck, adja meg API kulcs\u00e1nak adatait a Coinbase \u00e1ltal megadott m\u00f3don.", "title": "Coinbase API kulcs r\u00e9szletei" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "A k\u00e9rt valutaegyenlegek k\u00f6z\u00fcl egyet vagy t\u00f6bbet nem biztos\u00edt a Coinbase API.", - "currency_unavaliable": "A k\u00e9rt valutaegyenlegek k\u00f6z\u00fcl egyet vagy t\u00f6bbet nem biztos\u00edt a Coinbase API.", "exchange_rate_unavailable": "A k\u00e9rt \u00e1rfolyamok k\u00f6z\u00fcl egyet vagy t\u00f6bbet a Coinbase nem biztos\u00edt.", - "exchange_rate_unavaliable": "A k\u00e9rt \u00e1rfolyamok k\u00f6z\u00fcl egyet vagy t\u00f6bbet a Coinbase nem biztos\u00edt.", "unknown": "Ismeretlen hiba" }, "step": { diff --git a/homeassistant/components/coinbase/translations/id.json b/homeassistant/components/coinbase/translations/id.json index 477aceafa45..ba2d2de0753 100644 --- a/homeassistant/components/coinbase/translations/id.json +++ b/homeassistant/components/coinbase/translations/id.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Kunci API", - "api_token": "Kode Rahasia API", - "currencies": "Mata Uang Saldo Akun", - "exchange_rates": "Nilai Tukar" + "api_token": "Kode Rahasia API" }, "description": "Silakan masukkan detail kunci API Anda sesuai yang disediakan oleh Coinbase.", "title": "Detail Kunci API Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Satu atau beberapa saldo mata uang yang diminta tidak disediakan oleh API Coinbase Anda.", - "currency_unavaliable": "Satu atau beberapa saldo mata uang yang diminta tidak disediakan oleh API Coinbase Anda.", "exchange_rate_unavailable": "Satu atau beberapa nilai tukar yang diminta tidak disediakan oleh Coinbase.", - "exchange_rate_unavaliable": "Satu atau beberapa nilai tukar yang diminta tidak disediakan oleh Coinbase.", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { diff --git a/homeassistant/components/coinbase/translations/it.json b/homeassistant/components/coinbase/translations/it.json index 64b5b0cdca7..f26e08a727c 100644 --- a/homeassistant/components/coinbase/translations/it.json +++ b/homeassistant/components/coinbase/translations/it.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Chiave API", - "api_token": "API segreta", - "currencies": "Valute del saldo del conto", - "exchange_rates": "Tassi di cambio" + "api_token": "API segreta" }, "description": "Inserisci i dettagli della tua chiave API come forniti da Coinbase.", "title": "Dettagli della chiave API di Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Uno o pi\u00f9 dei saldi in valuta richiesti non sono forniti dalla tua API Coinbase.", - "currency_unavaliable": "Uno o pi\u00f9 saldi in valuta richiesti non sono forniti dalla tua API Coinbase.", "exchange_rate_unavailable": "Uno o pi\u00f9 dei tassi di cambio richiesti non sono forniti da Coinbase.", - "exchange_rate_unavaliable": "Uno o pi\u00f9 dei tassi di cambio richiesti non sono forniti da Coinbase.", "unknown": "Errore imprevisto" }, "step": { diff --git a/homeassistant/components/coinbase/translations/ja.json b/homeassistant/components/coinbase/translations/ja.json index 321e0c05d9d..011ff093747 100644 --- a/homeassistant/components/coinbase/translations/ja.json +++ b/homeassistant/components/coinbase/translations/ja.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API\u30ad\u30fc", - "api_token": "API\u30b7\u30fc\u30af\u30ec\u30c3\u30c8", - "currencies": "\u53e3\u5ea7\u6b8b\u9ad8 \u901a\u8ca8", - "exchange_rates": "\u70ba\u66ff\u30ec\u30fc\u30c8" + "api_token": "API\u30b7\u30fc\u30af\u30ec\u30c3\u30c8" }, "description": "Coinbase\u304b\u3089\u63d0\u4f9b\u3055\u308c\u305fAPI\u30ad\u30fc\u306e\u8a73\u7d30\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "Coinbase API\u30ad\u30fc\u306e\u8a73\u7d30" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "\u30ea\u30af\u30a8\u30b9\u30c8\u3057\u305f\u901a\u8ca8\u6b8b\u9ad8\u306e1\u3064\u4ee5\u4e0a\u304c\u3001Coinbase API\u306b\u3088\u3063\u3066\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", - "currency_unavaliable": "\u8981\u6c42\u3055\u308c\u305f\u901a\u8ca8\u6b8b\u9ad8\u306e1\u3064\u4ee5\u4e0a\u304c\u3001Coinbase API\u306b\u3088\u3063\u3066\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", "exchange_rate_unavailable": "\u30ea\u30af\u30a8\u30b9\u30c8\u3057\u305f\u70ba\u66ff\u30ec\u30fc\u30c8\u306e1\u3064\u4ee5\u4e0a\u304c\u3001Coinbase\u306b\u3088\u3063\u3066\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", - "exchange_rate_unavaliable": "\u8981\u6c42\u3055\u308c\u305f\u70ba\u66ff\u30ec\u30fc\u30c8\u306e1\u3064\u4ee5\u4e0a\u304cCoinbase\u306b\u3088\u3063\u3066\u63d0\u4f9b\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { diff --git a/homeassistant/components/coinbase/translations/nl.json b/homeassistant/components/coinbase/translations/nl.json index 98763deb9a7..472a15659c0 100644 --- a/homeassistant/components/coinbase/translations/nl.json +++ b/homeassistant/components/coinbase/translations/nl.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API-sleutel", - "api_token": "API-geheim", - "currencies": "Valuta's van rekeningsaldo", - "exchange_rates": "Wisselkoersen" + "api_token": "API-geheim" }, "description": "Voer de gegevens van uw API-sleutel in zoals verstrekt door Coinbase.", "title": "Coinbase API Sleutel Details" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Een of meer van de gevraagde valutabalansen wordt niet geleverd door uw Coinbase API.", - "currency_unavaliable": "Een of meer van de gevraagde valutasaldi worden niet geleverd door uw Coinbase API.", "exchange_rate_unavailable": "Een of meer van de gevraagde wisselkoersen worden niet door Coinbase geleverd.", - "exchange_rate_unavaliable": "Een of meer van de gevraagde wisselkoersen worden niet door Coinbase verstrekt.", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/coinbase/translations/no.json b/homeassistant/components/coinbase/translations/no.json index 5171814cf9d..c3f2b34cf92 100644 --- a/homeassistant/components/coinbase/translations/no.json +++ b/homeassistant/components/coinbase/translations/no.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API-n\u00f8kkel", - "api_token": "API-hemmelighet", - "currencies": "Valutaer for kontosaldo", - "exchange_rates": "Valutakurser" + "api_token": "API-hemmelighet" }, "description": "Vennligst skriv inn detaljene for API-n\u00f8kkelen din som gitt av Coinbase.", "title": "Detaljer for Coinbase API-n\u00f8kkel" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "En eller flere av de forespurte valutasaldoene leveres ikke av Coinbase API.", - "currency_unavaliable": "En eller flere av de forespurte valutasaldoene leveres ikke av Coinbase API.", "exchange_rate_unavailable": "En eller flere av de forespurte valutakursene er ikke levert av Coinbase.", - "exchange_rate_unavaliable": "En eller flere av de forespurte valutakursene leveres ikke av Coinbase.", "unknown": "Uventet feil" }, "step": { diff --git a/homeassistant/components/coinbase/translations/pl.json b/homeassistant/components/coinbase/translations/pl.json index 7465ae24486..70a1a021cdf 100644 --- a/homeassistant/components/coinbase/translations/pl.json +++ b/homeassistant/components/coinbase/translations/pl.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Klucz API", - "api_token": "Sekretne API", - "currencies": "Waluty salda konta", - "exchange_rates": "Kursy wymiany" + "api_token": "Sekretne API" }, "description": "Wprowad\u017a dane swojego klucza API podane przez Coinbase.", "title": "Szczeg\u00f3\u0142y klucza API Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Jeden lub wi\u0119cej \u017c\u0105danych sald walutowych nie jest dostarczanych przez interfejs API Coinbase.", - "currency_unavaliable": "Jeden lub wi\u0119cej \u017c\u0105danych sald walutowych nie jest dostarczanych przez interfejs API Coinbase.", "exchange_rate_unavailable": "Jeden lub wi\u0119cej z \u017c\u0105danych kurs\u00f3w wymiany nie jest dostarczany przez Coinbase.", - "exchange_rate_unavaliable": "Jeden lub wi\u0119cej z \u017c\u0105danych kurs\u00f3w wymiany nie jest dostarczany przez Coinbase.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { diff --git a/homeassistant/components/coinbase/translations/pt-BR.json b/homeassistant/components/coinbase/translations/pt-BR.json index 5ed52fa7afc..5f2bb7d96e3 100644 --- a/homeassistant/components/coinbase/translations/pt-BR.json +++ b/homeassistant/components/coinbase/translations/pt-BR.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "Chave da API", - "api_token": "Segredo da API", - "currencies": "Moedas do saldo da conta", - "exchange_rates": "Taxas de c\u00e2mbio" + "api_token": "Segredo da API" }, "description": "Por favor, insira os detalhes da sua chave de API conforme fornecido pela Coinbase.", "title": "Detalhes da chave da API Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Um ou mais dos saldos de moeda solicitados n\u00e3o s\u00e3o fornecidos pela sua API Coinbase.", - "currency_unavaliable": "Um ou mais dos saldos de moeda solicitados n\u00e3o s\u00e3o fornecidos pela sua API Coinbase.", "exchange_rate_unavailable": "Uma ou mais taxas de c\u00e2mbio solicitadas n\u00e3o s\u00e3o fornecidas pela Coinbase.", - "exchange_rate_unavaliable": "Uma ou mais taxas de c\u00e2mbio solicitadas n\u00e3o s\u00e3o fornecidas pela Coinbase.", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/coinbase/translations/ru.json b/homeassistant/components/coinbase/translations/ru.json index 951c0182320..cbdf39e61a6 100644 --- a/homeassistant/components/coinbase/translations/ru.json +++ b/homeassistant/components/coinbase/translations/ru.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", - "api_token": "\u0421\u0435\u043a\u0440\u0435\u0442 API", - "currencies": "\u041e\u0441\u0442\u0430\u0442\u043e\u043a \u0432\u0430\u043b\u044e\u0442\u044b \u043d\u0430 \u0441\u0447\u0435\u0442\u0435", - "exchange_rates": "\u041e\u0431\u043c\u0435\u043d\u043d\u044b\u0435 \u043a\u0443\u0440\u0441\u044b" + "api_token": "\u0421\u0435\u043a\u0440\u0435\u0442 API" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0412\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u044e\u0447\u0430 API Coinbase.", "title": "\u041a\u043b\u044e\u0447 API Coinbase" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "\u041e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0445 \u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0432\u0430\u043b\u044e\u0442\u044b \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0412\u0430\u0448\u0438\u043c API Coinbase.", - "currency_unavaliable": "\u041e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0445 \u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0432\u0430\u043b\u044e\u0442\u044b \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0412\u0430\u0448\u0438\u043c API Coinbase.", "exchange_rate_unavailable": "Coinbase \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0445 \u043e\u0431\u043c\u0435\u043d\u043d\u044b\u0445 \u043a\u0443\u0440\u0441\u043e\u0432.", - "exchange_rate_unavaliable": "Coinbase \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043d\u044b\u0445 \u043e\u0431\u043c\u0435\u043d\u043d\u044b\u0445 \u043a\u0443\u0440\u0441\u043e\u0432.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/coinbase/translations/tr.json b/homeassistant/components/coinbase/translations/tr.json index e21cab489e4..b84e2bf740e 100644 --- a/homeassistant/components/coinbase/translations/tr.json +++ b/homeassistant/components/coinbase/translations/tr.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API Anahtar\u0131", - "api_token": "API Gizli Anahtar\u0131", - "currencies": "Hesap Bakiyesi Para Birimleri", - "exchange_rates": "D\u00f6viz Kurlar\u0131" + "api_token": "API Gizli Anahtar\u0131" }, "description": "L\u00fctfen API anahtar\u0131n\u0131z\u0131n ayr\u0131nt\u0131lar\u0131n\u0131 Coinbase taraf\u0131ndan sa\u011flanan \u015fekilde girin.", "title": "Coinbase API Anahtar Ayr\u0131nt\u0131lar\u0131" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "\u0130stenen para birimi bakiyelerinden biri veya daha fazlas\u0131 Coinbase API'niz taraf\u0131ndan sa\u011flanm\u0131yor.", - "currency_unavaliable": "\u0130stenen para birimi bakiyelerinden biri veya daha fazlas\u0131 Coinbase API'niz taraf\u0131ndan sa\u011flanm\u0131yor.", "exchange_rate_unavailable": "\u0130stenen d\u00f6viz kurlar\u0131ndan biri veya daha fazlas\u0131 Coinbase taraf\u0131ndan sa\u011flanm\u0131yor.", - "exchange_rate_unavaliable": "\u0130stenen d\u00f6viz kurlar\u0131ndan biri veya daha fazlas\u0131 Coinbase taraf\u0131ndan sa\u011flanm\u0131yor.", "unknown": "Beklenmeyen hata" }, "step": { diff --git a/homeassistant/components/coinbase/translations/zh-Hans.json b/homeassistant/components/coinbase/translations/zh-Hans.json index 1a5eaa19dec..954a10c70a6 100644 --- a/homeassistant/components/coinbase/translations/zh-Hans.json +++ b/homeassistant/components/coinbase/translations/zh-Hans.json @@ -12,9 +12,7 @@ "user": { "data": { "api_key": "API \u5bc6\u94a5", - "api_token": "API Token", - "currencies": "\u8d26\u6237\u4f59\u989d", - "exchange_rates": "\u6c47\u7387" + "api_token": "API Token" }, "description": "\u8bf7\u8f93\u5165\u7531 Coinbase \u63d0\u4f9b\u7684 API \u5bc6\u94a5\u4fe1\u606f", "title": "Coinbase API \u5bc6\u94a5\u8be6\u60c5" @@ -23,8 +21,6 @@ }, "options": { "error": { - "currency_unavaliable": "Coinbase \u65e0\u6cd5\u63d0\u4f9b\u5176\u8bbe\u5b9a\u7684\u6c47\u7387\u4fe1\u606f", - "exchange_rate_unavaliable": "Coinbase \u65e0\u6cd5\u63d0\u4f9b\u5176\u8bbe\u5b9a\u7684\u6c47\u7387\u4fe1\u606f", "unknown": "\u672a\u77e5\u9519\u8bef" }, "step": { diff --git a/homeassistant/components/coinbase/translations/zh-Hant.json b/homeassistant/components/coinbase/translations/zh-Hant.json index 315fe90254f..ea48d90fc7e 100644 --- a/homeassistant/components/coinbase/translations/zh-Hant.json +++ b/homeassistant/components/coinbase/translations/zh-Hant.json @@ -14,9 +14,7 @@ "user": { "data": { "api_key": "API \u91d1\u9470", - "api_token": "API \u79c1\u9470", - "currencies": "\u5e33\u6236\u9918\u984d\u8ca8\u5e63", - "exchange_rates": "\u532f\u7387" + "api_token": "API \u79c1\u9470" }, "description": "\u8acb\u8f38\u5165\u7531 Coinbase \u63d0\u4f9b\u7684 API \u91d1\u9470\u8cc7\u8a0a\u3002", "title": "Coinbase API \u91d1\u9470\u8cc7\u6599" @@ -26,9 +24,7 @@ "options": { "error": { "currency_unavailable": "Coinbase API \u672a\u63d0\u4f9b\u4e00\u500b\u6216\u591a\u500b\u6240\u8981\u6c42\u7684\u8ca8\u5e63\u9918\u984d\u3002", - "currency_unavaliable": "Coinbase API \u672a\u63d0\u4f9b\u4e00\u500b\u6216\u591a\u500b\u6240\u8981\u6c42\u7684\u8ca8\u5e63\u9918\u984d\u3002", "exchange_rate_unavailable": "Coinbase \u672a\u63d0\u4f9b\u4e00\u500b\u6216\u591a\u500b\u6240\u8981\u6c42\u7684\u532f\u7387\u3002", - "exchange_rate_unavaliable": "Coinbase \u672a\u63d0\u4f9b\u4e00\u500b\u6216\u591a\u500b\u6240\u8981\u6c42\u7684\u532f\u7387\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { diff --git a/homeassistant/components/cpuspeed/translations/ko.json b/homeassistant/components/cpuspeed/translations/ko.json new file mode 100644 index 00000000000..758f3336cd4 --- /dev/null +++ b/homeassistant/components/cpuspeed/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/translations/bg.json b/homeassistant/components/deconz/translations/bg.json index efba936a8d0..f8b1e351fb0 100644 --- a/homeassistant/components/deconz/translations/bg.json +++ b/homeassistant/components/deconz/translations/bg.json @@ -4,7 +4,6 @@ "already_configured": "\u041c\u043e\u0441\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d", "already_in_progress": "\u0412 \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0442\u0435\u0447\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f.", "no_bridges": "\u041d\u0435 \u0441\u0430 \u043e\u0442\u043a\u0440\u0438\u0442\u0438 \u043c\u043e\u0441\u0442\u043e\u0432\u0435 deCONZ", - "not_deconz_bridge": "\u041d\u0435 \u0435 deCONZ \u0431\u0430\u0437\u043e\u0432\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f", "updated_instance": "\u041e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 deCONZ \u0441 \u043d\u043e\u0432 \u0430\u0434\u0440\u0435\u0441" }, "error": { diff --git a/homeassistant/components/deconz/translations/ca.json b/homeassistant/components/deconz/translations/ca.json index 2d15801b590..f4659557503 100644 --- a/homeassistant/components/deconz/translations/ca.json +++ b/homeassistant/components/deconz/translations/ca.json @@ -5,7 +5,6 @@ "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "no_bridges": "No s'han descobert enlla\u00e7os amb deCONZ", "no_hardware_available": "No hi ha cap maquinari r\u00e0dio connectat a deCONZ", - "not_deconz_bridge": "No \u00e9s un enlla\u00e7 deCONZ", "updated_instance": "S'ha actualitzat la inst\u00e0ncia de deCONZ amb una nova adre\u00e7a" }, "error": { diff --git a/homeassistant/components/deconz/translations/cs.json b/homeassistant/components/deconz/translations/cs.json index 323b29ddac8..8e6c6a71a44 100644 --- a/homeassistant/components/deconz/translations/cs.json +++ b/homeassistant/components/deconz/translations/cs.json @@ -5,7 +5,6 @@ "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", "no_bridges": "\u017d\u00e1dn\u00e9 deCONZ p\u0159emost\u011bn\u00ed nebyly nalezeny", "no_hardware_available": "K deCONZ nen\u00ed p\u0159ipojeno \u017e\u00e1dn\u00e9 r\u00e1diov\u00e9 za\u0159\u00edzen\u00ed", - "not_deconz_bridge": "Nejedn\u00e1 se o deCONZ p\u0159emost\u011bn\u00ed", "updated_instance": "Instance deCONZ aktualizov\u00e1na s nov\u00fdm hostitelem" }, "error": { diff --git a/homeassistant/components/deconz/translations/da.json b/homeassistant/components/deconz/translations/da.json index 6f63540c924..0091e9d0349 100644 --- a/homeassistant/components/deconz/translations/da.json +++ b/homeassistant/components/deconz/translations/da.json @@ -4,7 +4,6 @@ "already_configured": "Bridge er allerede konfigureret", "already_in_progress": "Konfigurationsflow for bro er allerede i gang.", "no_bridges": "Ingen deConz-bro fundet", - "not_deconz_bridge": "Ikke en deCONZ-bro", "updated_instance": "Opdaterede deCONZ-instans med ny v\u00e6rtadresse" }, "error": { diff --git a/homeassistant/components/deconz/translations/de.json b/homeassistant/components/deconz/translations/de.json index 05fa6ce8b3f..29b322466d5 100644 --- a/homeassistant/components/deconz/translations/de.json +++ b/homeassistant/components/deconz/translations/de.json @@ -5,7 +5,6 @@ "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "no_bridges": "Keine deCONZ-Bridges entdeckt", "no_hardware_available": "Keine Funkhardware an deCONZ angeschlossen", - "not_deconz_bridge": "Keine deCONZ Bridge entdeckt", "updated_instance": "deCONZ-Instanz mit neuer Host-Adresse aktualisiert" }, "error": { diff --git a/homeassistant/components/deconz/translations/el.json b/homeassistant/components/deconz/translations/el.json index 18eeaf700b6..3e7d3b166c1 100644 --- a/homeassistant/components/deconz/translations/el.json +++ b/homeassistant/components/deconz/translations/el.json @@ -5,7 +5,6 @@ "already_in_progress": "\u03a4\u03bf \u03b4\u03b9\u03ac\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03b3\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "no_bridges": "\u0394\u03b5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03b3\u03ad\u03c6\u03c5\u03c1\u03b5\u03c2 deCONZ", "no_hardware_available": "\u0394\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03b1\u03c3\u03cd\u03c1\u03bc\u03b1\u03c4\u03bf \u03c5\u03bb\u03b9\u03ba\u03cc \u03c3\u03c4\u03bf deCONZ", - "not_deconz_bridge": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b3\u03ad\u03c6\u03c5\u03c1\u03b1 deCONZ", "updated_instance": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03c9\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03af\u03b1 deCONZ \u03bc\u03b5 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae" }, "error": { diff --git a/homeassistant/components/deconz/translations/en.json b/homeassistant/components/deconz/translations/en.json index 16034e12414..af73a3b43be 100644 --- a/homeassistant/components/deconz/translations/en.json +++ b/homeassistant/components/deconz/translations/en.json @@ -5,7 +5,6 @@ "already_in_progress": "Configuration flow is already in progress", "no_bridges": "No deCONZ bridges discovered", "no_hardware_available": "No radio hardware connected to deCONZ", - "not_deconz_bridge": "Not a deCONZ bridge", "updated_instance": "Updated deCONZ instance with new host address" }, "error": { diff --git a/homeassistant/components/deconz/translations/es-419.json b/homeassistant/components/deconz/translations/es-419.json index ceb0ca39d2c..a8a8965daf5 100644 --- a/homeassistant/components/deconz/translations/es-419.json +++ b/homeassistant/components/deconz/translations/es-419.json @@ -5,7 +5,6 @@ "already_in_progress": "El flujo de configuraci\u00f3n para el puente ya est\u00e1 en progreso.", "no_bridges": "No se descubrieron puentes deCONZ", "no_hardware_available": "No hay hardware de radio conectado a deCONZ", - "not_deconz_bridge": "No es un puente deCONZ", "updated_instance": "Instancia deCONZ actualizada con nueva direcci\u00f3n de host" }, "error": { diff --git a/homeassistant/components/deconz/translations/es.json b/homeassistant/components/deconz/translations/es.json index 0174c4cf76d..2921dc2f2bf 100644 --- a/homeassistant/components/deconz/translations/es.json +++ b/homeassistant/components/deconz/translations/es.json @@ -5,7 +5,6 @@ "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "no_bridges": "No se han descubierto pasarelas deCONZ", "no_hardware_available": "No hay hardware de radio conectado a deCONZ", - "not_deconz_bridge": "No es una pasarela deCONZ", "updated_instance": "Instancia deCONZ actualizada con nueva direcci\u00f3n de host" }, "error": { diff --git a/homeassistant/components/deconz/translations/et.json b/homeassistant/components/deconz/translations/et.json index 8519182878c..0fd28a0889d 100644 --- a/homeassistant/components/deconz/translations/et.json +++ b/homeassistant/components/deconz/translations/et.json @@ -5,7 +5,6 @@ "already_in_progress": "Seadistamine on juba k\u00e4imas", "no_bridges": "DeCONZ l\u00fc\u00fcse ei leitud", "no_hardware_available": "DeCONZi raadio riistvara puudub", - "not_deconz_bridge": "See pole deCONZ-i sild", "updated_instance": "DeCONZ-i eksemplarile omistati uus hostiaadress" }, "error": { diff --git a/homeassistant/components/deconz/translations/fr.json b/homeassistant/components/deconz/translations/fr.json index 12f63765314..711322df9dd 100644 --- a/homeassistant/components/deconz/translations/fr.json +++ b/homeassistant/components/deconz/translations/fr.json @@ -5,7 +5,6 @@ "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "no_bridges": "Aucun pont deCONZ n'a \u00e9t\u00e9 d\u00e9couvert", "no_hardware_available": "Aucun mat\u00e9riel radio connect\u00e9 \u00e0 deCONZ", - "not_deconz_bridge": "Pas un pont deCONZ", "updated_instance": "Instance deCONZ mise \u00e0 jour avec la nouvelle adresse d'h\u00f4te" }, "error": { diff --git a/homeassistant/components/deconz/translations/hu.json b/homeassistant/components/deconz/translations/hu.json index c92a5dd36cd..7bf6d021634 100644 --- a/homeassistant/components/deconz/translations/hu.json +++ b/homeassistant/components/deconz/translations/hu.json @@ -5,7 +5,6 @@ "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "no_bridges": "Nem tal\u00e1lhat\u00f3 deCONZ \u00e1tj\u00e1r\u00f3", "no_hardware_available": "Nincs deCONZ-hoz csatlakoztatott r\u00e1di\u00f3hardver", - "not_deconz_bridge": "Nem egy deCONZ \u00e1tj\u00e1r\u00f3", "updated_instance": "A deCONZ-p\u00e9ld\u00e1ny \u00faj \u00e1llom\u00e1sc\u00edmmel friss\u00edtve" }, "error": { diff --git a/homeassistant/components/deconz/translations/id.json b/homeassistant/components/deconz/translations/id.json index e76e8b98d55..b6d7329758b 100644 --- a/homeassistant/components/deconz/translations/id.json +++ b/homeassistant/components/deconz/translations/id.json @@ -5,7 +5,6 @@ "already_in_progress": "Alur konfigurasi sedang berlangsung", "no_bridges": "deCONZ bridge tidak ditemukan", "no_hardware_available": "Tidak ada perangkat keras radio yang terhubung ke deCONZ", - "not_deconz_bridge": "Bukan bridge deCONZ", "updated_instance": "Instans deCONZ yang diperbarui dengan alamat host baru" }, "error": { diff --git a/homeassistant/components/deconz/translations/it.json b/homeassistant/components/deconz/translations/it.json index 44c204d7ed9..638b4db4222 100644 --- a/homeassistant/components/deconz/translations/it.json +++ b/homeassistant/components/deconz/translations/it.json @@ -5,7 +5,6 @@ "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "no_bridges": "Nessun bridge deCONZ rilevato", "no_hardware_available": "Nessun hardware radio collegato a deCONZ", - "not_deconz_bridge": "Non \u00e8 un bridge deCONZ", "updated_instance": "Istanza deCONZ aggiornata con nuovo indirizzo host" }, "error": { diff --git a/homeassistant/components/deconz/translations/ja.json b/homeassistant/components/deconz/translations/ja.json index ce1cab4dea6..2962f3d4531 100644 --- a/homeassistant/components/deconz/translations/ja.json +++ b/homeassistant/components/deconz/translations/ja.json @@ -5,7 +5,6 @@ "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", "no_bridges": "deCONZ\u30d6\u30ea\u30c3\u30b8\u306f\u691c\u51fa\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f", "no_hardware_available": "deCONZ\u306b\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u7121\u7dda\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u304c\u3042\u308a\u307e\u305b\u3093", - "not_deconz_bridge": "deCONZ bridge\u3067\u306f\u3042\u308a\u307e\u305b\u3093", "updated_instance": "\u65b0\u3057\u3044\u30db\u30b9\u30c8\u30a2\u30c9\u30ec\u30b9\u3067deCONZ\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u66f4\u65b0\u3057\u307e\u3057\u305f" }, "error": { diff --git a/homeassistant/components/deconz/translations/ko.json b/homeassistant/components/deconz/translations/ko.json index 5158d557106..e12bab3b64a 100644 --- a/homeassistant/components/deconz/translations/ko.json +++ b/homeassistant/components/deconz/translations/ko.json @@ -5,10 +5,10 @@ "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "no_bridges": "\ubc1c\uacac\ub41c deCONZ \ube0c\ub9ac\uc9c0\uac00 \uc5c6\uc2b5\ub2c8\ub2e4", "no_hardware_available": "deCONZ\uc5d0 \uc5f0\uacb0\ub41c \ubb34\uc120 \ud558\ub4dc\uc6e8\uc5b4\uac00 \uc5c6\uc2b5\ub2c8\ub2e4", - "not_deconz_bridge": "deCONZ \ube0c\ub9ac\uc9c0\uac00 \uc544\ub2d9\ub2c8\ub2e4", "updated_instance": "deCONZ \uc778\uc2a4\ud134\uc2a4\ub97c \uc0c8\ub85c\uc6b4 \ud638\uc2a4\ud2b8 \uc8fc\uc18c\ub85c \uc5c5\ub370\uc774\ud2b8\ud588\uc2b5\ub2c8\ub2e4" }, "error": { + "linking_not_possible": "\uac8c\uc774\ud2b8\uc6e8\uc774\uc640 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "no_key": "API \ud0a4\ub97c \uac00\uc838\uc62c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" }, "flow_title": "deCONZ Zigbee \uac8c\uc774\ud2b8\uc6e8\uc774 ({host})", diff --git a/homeassistant/components/deconz/translations/lb.json b/homeassistant/components/deconz/translations/lb.json index 84a535c8ebc..b5bb383d29a 100644 --- a/homeassistant/components/deconz/translations/lb.json +++ b/homeassistant/components/deconz/translations/lb.json @@ -5,7 +5,6 @@ "already_in_progress": "Konfiguratioun's Oflaf ass schonn am gaang.", "no_bridges": "Keng dECONZ bridges fonnt", "no_hardware_available": "Keng Radio Hardware verbonne mat deCONZ", - "not_deconz_bridge": "Keng deCONZ Bridge", "updated_instance": "deCONZ Instanz gouf mat der neier Adress vum Apparat ge\u00e4nnert" }, "error": { diff --git a/homeassistant/components/deconz/translations/nl.json b/homeassistant/components/deconz/translations/nl.json index 6339ad0fd10..caa6ca461fc 100644 --- a/homeassistant/components/deconz/translations/nl.json +++ b/homeassistant/components/deconz/translations/nl.json @@ -5,7 +5,6 @@ "already_in_progress": "De configuratiestroom is al aan de gang", "no_bridges": "Geen deCONZ bridges ontdekt", "no_hardware_available": "Geen radiohardware aangesloten op deCONZ", - "not_deconz_bridge": "Geen deCONZ bridge", "updated_instance": "DeCONZ-instantie bijgewerkt met nieuw host-adres" }, "error": { diff --git a/homeassistant/components/deconz/translations/no.json b/homeassistant/components/deconz/translations/no.json index 7682cb15a5d..22a294d3242 100644 --- a/homeassistant/components/deconz/translations/no.json +++ b/homeassistant/components/deconz/translations/no.json @@ -5,7 +5,6 @@ "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "no_bridges": "Ingen deCONZ broer oppdaget", "no_hardware_available": "Ingen radiomaskinvare koblet til deCONZ", - "not_deconz_bridge": "Ikke en deCONZ bro", "updated_instance": "Oppdatert deCONZ forekomst med ny vertsadresse" }, "error": { diff --git a/homeassistant/components/deconz/translations/pl.json b/homeassistant/components/deconz/translations/pl.json index 7f49a259304..7894494336e 100644 --- a/homeassistant/components/deconz/translations/pl.json +++ b/homeassistant/components/deconz/translations/pl.json @@ -5,7 +5,6 @@ "already_in_progress": "Konfiguracja jest ju\u017c w toku", "no_bridges": "Nie odkryto mostk\u00f3w deCONZ", "no_hardware_available": "Nie wykryto komponentu radiowego pod\u0142\u0105czonego do deCONZ", - "not_deconz_bridge": "To nie jest mostek deCONZ", "updated_instance": "Zaktualizowano instancj\u0119 deCONZ o nowy adres hosta" }, "error": { diff --git a/homeassistant/components/deconz/translations/pt-BR.json b/homeassistant/components/deconz/translations/pt-BR.json index fbbd776cbe8..785ada6b4c3 100644 --- a/homeassistant/components/deconz/translations/pt-BR.json +++ b/homeassistant/components/deconz/translations/pt-BR.json @@ -5,7 +5,6 @@ "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "no_bridges": "N\u00e3o h\u00e1 pontes de deCONZ descobertas", "no_hardware_available": "Nenhum hardware de r\u00e1dio conectado ao deCONZ", - "not_deconz_bridge": "N\u00e3o \u00e9 uma ponte deCONZ", "updated_instance": "Atualiza\u00e7\u00e3o da inst\u00e2ncia deCONZ com novo endere\u00e7o de host" }, "error": { diff --git a/homeassistant/components/deconz/translations/pt.json b/homeassistant/components/deconz/translations/pt.json index a64e37ed15b..94efb5c68ca 100644 --- a/homeassistant/components/deconz/translations/pt.json +++ b/homeassistant/components/deconz/translations/pt.json @@ -3,8 +3,7 @@ "abort": { "already_configured": "Bridge j\u00e1 est\u00e1 configurada", "already_in_progress": "O processo de configura\u00e7\u00e3o j\u00e1 est\u00e1 a decorrer", - "no_bridges": "Nenhum hub deCONZ descoberto", - "not_deconz_bridge": "N\u00e3o \u00e9 uma bridge deCONZ" + "no_bridges": "Nenhum hub deCONZ descoberto" }, "error": { "no_key": "N\u00e3o foi poss\u00edvel obter uma Chave da API" diff --git a/homeassistant/components/deconz/translations/ru.json b/homeassistant/components/deconz/translations/ru.json index f92ebd0e1c7..91c44036331 100644 --- a/homeassistant/components/deconz/translations/ru.json +++ b/homeassistant/components/deconz/translations/ru.json @@ -5,7 +5,6 @@ "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "no_bridges": "\u0428\u043b\u044e\u0437\u044b deCONZ \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b.", "no_hardware_available": "\u041a deCONZ \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u0440\u0430\u0434\u0438\u043e\u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435.", - "not_deconz_bridge": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0448\u043b\u044e\u0437\u043e\u043c deCONZ.", "updated_instance": "\u0410\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d." }, "error": { diff --git a/homeassistant/components/deconz/translations/sl.json b/homeassistant/components/deconz/translations/sl.json index 9e8ed42c07e..cfb52ca7af5 100644 --- a/homeassistant/components/deconz/translations/sl.json +++ b/homeassistant/components/deconz/translations/sl.json @@ -4,7 +4,6 @@ "already_configured": "Most je \u017ee nastavljen", "already_in_progress": "Konfiguracijski tok za most je \u017ee v teku.", "no_bridges": "Ni odkritih mostov deCONZ", - "not_deconz_bridge": "Ni deCONZ most", "updated_instance": "Posodobljen deCONZ z novim naslovom gostitelja" }, "error": { diff --git a/homeassistant/components/deconz/translations/sv.json b/homeassistant/components/deconz/translations/sv.json index 1a867d0c7fb..fd0968a941a 100644 --- a/homeassistant/components/deconz/translations/sv.json +++ b/homeassistant/components/deconz/translations/sv.json @@ -4,7 +4,6 @@ "already_configured": "Bryggan \u00e4r redan konfigurerad", "already_in_progress": "Konfigurations fl\u00f6det f\u00f6r bryggan p\u00e5g\u00e5r redan.", "no_bridges": "Inga deCONZ-bryggor uppt\u00e4cktes", - "not_deconz_bridge": "Inte en deCONZ-brygga", "updated_instance": "Uppdaterad deCONZ-instans med ny v\u00e4rdadress" }, "error": { diff --git a/homeassistant/components/deconz/translations/tr.json b/homeassistant/components/deconz/translations/tr.json index 77045776c98..8b57d284864 100644 --- a/homeassistant/components/deconz/translations/tr.json +++ b/homeassistant/components/deconz/translations/tr.json @@ -5,7 +5,6 @@ "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "no_bridges": "DeCONZ k\u00f6pr\u00fcs\u00fc bulunamad\u0131", "no_hardware_available": "deCONZ'a ba\u011fl\u0131 radyo donan\u0131m\u0131 yok", - "not_deconz_bridge": "deCONZ k\u00f6pr\u00fcs\u00fc de\u011fil", "updated_instance": "DeCONZ yeni ana bilgisayar adresiyle g\u00fcncelle\u015ftirildi" }, "error": { diff --git a/homeassistant/components/deconz/translations/uk.json b/homeassistant/components/deconz/translations/uk.json index 3b09a517385..5df5e6a6078 100644 --- a/homeassistant/components/deconz/translations/uk.json +++ b/homeassistant/components/deconz/translations/uk.json @@ -5,7 +5,6 @@ "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0436\u0435 \u0442\u0440\u0438\u0432\u0430\u0454.", "no_bridges": "\u0428\u043b\u044e\u0437\u0438 deCONZ \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456.", "no_hardware_available": "\u0420\u0430\u0434\u0456\u043e\u043e\u0431\u043b\u0430\u0434\u043d\u0430\u043d\u043d\u044f \u043d\u0435 \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u0434\u043e deCONZ.", - "not_deconz_bridge": "\u041f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043d\u0435 \u0454 \u0448\u043b\u044e\u0437\u043e\u043c deCONZ.", "updated_instance": "\u0410\u0434\u0440\u0435\u0441\u0443 \u0445\u043e\u0441\u0442\u0430 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043e." }, "error": { diff --git a/homeassistant/components/deconz/translations/zh-Hant.json b/homeassistant/components/deconz/translations/zh-Hant.json index d2e37ffa710..bd945ecc360 100644 --- a/homeassistant/components/deconz/translations/zh-Hant.json +++ b/homeassistant/components/deconz/translations/zh-Hant.json @@ -5,7 +5,6 @@ "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "no_bridges": "\u672a\u767c\u73fe\u5230 deCONZ Bridfe", "no_hardware_available": "deCONZ \u6c92\u6709\u4efb\u4f55\u7121\u7dda\u96fb\u88dd\u7f6e\u9023\u7dda", - "not_deconz_bridge": "\u975e deCONZ Bridge \u88dd\u7f6e", "updated_instance": "\u4f7f\u7528\u65b0\u4e3b\u6a5f\u7aef\u4f4d\u5740\u66f4\u65b0 deCONZ \u88dd\u7f6e" }, "error": { diff --git a/homeassistant/components/denonavr/translations/ca.json b/homeassistant/components/denonavr/translations/ca.json index 11065514ce4..ba857fceab1 100644 --- a/homeassistant/components/denonavr/translations/ca.json +++ b/homeassistant/components/denonavr/translations/ca.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Confirma l'addici\u00f3 del receptor", - "title": "Receptors de xarxa AVR de Denon" + "description": "Confirma l'addici\u00f3 del receptor" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Deixeu-ho en blanc per utilitzar descobriment autom\u00e0tic" - }, - "description": "Connecta el teu receptor, si no es configura l'adre\u00e7a IP, s'utilitza el descobriment autom\u00e0tic", - "title": "Receptors de xarxa AVR de Denon" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Configura la Zona 2", "zone3": "Configura la Zona 3" }, - "description": "Especifica par\u00e0metres opcionals", - "title": "Receptors de xarxa AVR de Denon" + "description": "Especifica par\u00e0metres opcionals" } } } diff --git a/homeassistant/components/denonavr/translations/cs.json b/homeassistant/components/denonavr/translations/cs.json index 1c66dae10f0..0f385ce1675 100644 --- a/homeassistant/components/denonavr/translations/cs.json +++ b/homeassistant/components/denonavr/translations/cs.json @@ -13,8 +13,7 @@ "flow_title": "S\u00ed\u0165ov\u00fd p\u0159ij\u00edma\u010d Denon AVR: {name}", "step": { "confirm": { - "description": "Potvr\u010fte pros\u00edm p\u0159id\u00e1n\u00ed p\u0159ij\u00edma\u010de", - "title": "S\u00ed\u0165ov\u00e9 p\u0159ij\u00edma\u010de Denon AVR" + "description": "Potvr\u010fte pros\u00edm p\u0159id\u00e1n\u00ed p\u0159ij\u00edma\u010de" }, "user": { "data": { diff --git a/homeassistant/components/denonavr/translations/de.json b/homeassistant/components/denonavr/translations/de.json index 1c9a1a8ec95..e4df128612b 100644 --- a/homeassistant/components/denonavr/translations/de.json +++ b/homeassistant/components/denonavr/translations/de.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Bitte best\u00e4tige das Hinzuf\u00fcgen des Receivers", - "title": "Denon AVR-Netzwerk-Receiver" + "description": "Bitte best\u00e4tige das Hinzuf\u00fcgen des Receivers" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Leer lassen, um automatische Erkennung zu verwenden" - }, - "description": "Verbinde dich mit deinem Receiver, wenn die IP-Adresse nicht eingestellt ist, wird die automatische Erkennung verwendet", - "title": "Denon AVR-Netzwerk-Receiver" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Zone 2 einrichten", "zone3": "Zone 3 einrichten" }, - "description": "Optionale Einstellungen festlegen", - "title": "Denon AVR-Netzwerk-Receiver" + "description": "Optionale Einstellungen festlegen" } } } diff --git a/homeassistant/components/denonavr/translations/el.json b/homeassistant/components/denonavr/translations/el.json index d176fd944d6..77cc89731f5 100644 --- a/homeassistant/components/denonavr/translations/el.json +++ b/homeassistant/components/denonavr/translations/el.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u0395\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03ad\u03ba\u03c4\u03b7", - "title": "\u0394\u03ad\u03ba\u03c4\u03b5\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 Denon AVR" + "description": "\u0395\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03ad\u03ba\u03c4\u03b7" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7" - }, - "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b4\u03ad\u03ba\u03c4\u03b7 \u03c3\u03b1\u03c2, \u03b5\u03ac\u03bd \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", - "title": "\u0394\u03ad\u03ba\u03c4\u03b5\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b6\u03ce\u03bd\u03b7\u03c2 2", "zone3": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b6\u03ce\u03bd\u03b7\u03c2 3" }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2", - "title": "\u0394\u03ad\u03ba\u03c4\u03b5\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 Denon AVR" + "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2" } } } diff --git a/homeassistant/components/denonavr/translations/en.json b/homeassistant/components/denonavr/translations/en.json index 2d937856b1c..668e18aae6e 100644 --- a/homeassistant/components/denonavr/translations/en.json +++ b/homeassistant/components/denonavr/translations/en.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Please confirm adding the receiver", - "title": "Denon AVR Network Receivers" + "description": "Please confirm adding the receiver" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Leave blank to use auto-discovery" - }, - "description": "Connect to your receiver, if the IP address is not set, auto-discovery is used", - "title": "Denon AVR Network Receivers" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Set up Zone 2", "zone3": "Set up Zone 3" }, - "description": "Specify optional settings", - "title": "Denon AVR Network Receivers" + "description": "Specify optional settings" } } } diff --git a/homeassistant/components/denonavr/translations/es-419.json b/homeassistant/components/denonavr/translations/es-419.json index e22b8feebd1..eb8e5698c09 100644 --- a/homeassistant/components/denonavr/translations/es-419.json +++ b/homeassistant/components/denonavr/translations/es-419.json @@ -11,8 +11,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Por favor, confirme la adici\u00f3n del receptor", - "title": "Receptores de red Denon AVR" + "description": "Por favor, confirme la adici\u00f3n del receptor" }, "select": { "data": { @@ -20,10 +19,6 @@ }, "description": "Vuelva a ejecutar la configuraci\u00f3n si desea conectar receptores adicionales", "title": "Seleccione el receptor que desea conectar" - }, - "user": { - "description": "Con\u00e9ctese a su receptor, si la direcci\u00f3n IP no est\u00e1 configurada, se usa el descubrimiento autom\u00e1tico", - "title": "Receptores de red Denon AVR" } } }, @@ -36,8 +31,7 @@ "zone2": "Configurar Zona 2", "zone3": "Configurar Zona 3" }, - "description": "Especificar configuraciones opcionales", - "title": "Receptores de red Denon AVR" + "description": "Especificar configuraciones opcionales" } } } diff --git a/homeassistant/components/denonavr/translations/es.json b/homeassistant/components/denonavr/translations/es.json index 33051047719..3cfe69aeac4 100644 --- a/homeassistant/components/denonavr/translations/es.json +++ b/homeassistant/components/denonavr/translations/es.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Por favor confirma la adici\u00f3n del receptor", - "title": "Receptores AVR Denon en Red" + "description": "Por favor confirma la adici\u00f3n del receptor" }, "select": { "data": { @@ -26,9 +25,7 @@ "user": { "data": { "host": "Direcci\u00f3n IP" - }, - "description": "Con\u00e9ctar con tu receptor, si la direcci\u00f3n IP no est\u00e1 configurada, se utilizar\u00e1 la detecci\u00f3n autom\u00e1tica", - "title": "Receptores AVR Denon en Red" + } } } }, @@ -41,8 +38,7 @@ "zone2": "Configurar la Zona 2", "zone3": "Configurar la Zona 3" }, - "description": "Especificar configuraciones opcionales", - "title": "Receptores AVR Denon en Red" + "description": "Especificar configuraciones opcionales" } } } diff --git a/homeassistant/components/denonavr/translations/et.json b/homeassistant/components/denonavr/translations/et.json index f133aaf9dd7..490624212b5 100644 --- a/homeassistant/components/denonavr/translations/et.json +++ b/homeassistant/components/denonavr/translations/et.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Palun kinnita vastuv\u00f5tja lisamine", - "title": "" + "description": "Palun kinnita vastuv\u00f5tja lisamine" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Automaatse avastamise kasutamiseks j\u00e4ta v\u00e4li t\u00fchjaks." - }, - "description": "Kui IP-aadressi pole m\u00e4\u00e4ratud, kasutatakse automaatset avastamist", - "title": "" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Seadista tsoon 2", "zone3": "Seadista tsoon 3" }, - "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine", - "title": "" + "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine" } } } diff --git a/homeassistant/components/denonavr/translations/fr.json b/homeassistant/components/denonavr/translations/fr.json index 27a72477164..3b3af580f1c 100644 --- a/homeassistant/components/denonavr/translations/fr.json +++ b/homeassistant/components/denonavr/translations/fr.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Veuillez confirmer l'ajout du r\u00e9cepteur", - "title": "R\u00e9cepteurs r\u00e9seaux Denon AVR" + "description": "Veuillez confirmer l'ajout du r\u00e9cepteur" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Laissez le champ vide pour utiliser la d\u00e9couverte automatique" - }, - "description": "Connectez-vous \u00e0 votre r\u00e9cepteur, si l'adresse IP n'est pas d\u00e9finie, la d\u00e9tection automatique est utilis\u00e9e", - "title": "R\u00e9cepteurs r\u00e9seaux Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Configurer Zone 2", "zone3": "Configurer Zone 3" }, - "description": "Sp\u00e9cifiez les param\u00e8tres optionnels", - "title": "R\u00e9cepteurs r\u00e9seaux Denon AVR" + "description": "Sp\u00e9cifiez les param\u00e8tres optionnels" } } } diff --git a/homeassistant/components/denonavr/translations/hu.json b/homeassistant/components/denonavr/translations/hu.json index 6891d18a9c4..8c51c7e990f 100644 --- a/homeassistant/components/denonavr/translations/hu.json +++ b/homeassistant/components/denonavr/translations/hu.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "K\u00e9rj\u00fck, er\u0151s\u00edtse meg a vev\u0151 hozz\u00e1ad\u00e1s\u00e1t", - "title": "Denon AVR h\u00e1l\u00f3zati vev\u0151k\u00e9sz\u00fcl\u00e9kek" + "description": "K\u00e9rj\u00fck, er\u0151s\u00edtse meg a vev\u0151 hozz\u00e1ad\u00e1s\u00e1t" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen" - }, - "description": "Csatlakozzon a vev\u0151h\u00f6z, ha az IP-c\u00edm nincs be\u00e1ll\u00edtva, az automatikus felder\u00edt\u00e9st haszn\u00e1lja", - "title": "Denon AVR h\u00e1l\u00f3zati vev\u0151k\u00e9sz\u00fcl\u00e9kek" + } } } }, @@ -44,8 +41,7 @@ "zone2": "\u00c1ll\u00edtsa be a 2. z\u00f3n\u00e1t", "zone3": "\u00c1ll\u00edtsa be a 3. z\u00f3n\u00e1t" }, - "description": "Adja meg az opcion\u00e1lis be\u00e1ll\u00edt\u00e1sokat", - "title": "Denon AVR h\u00e1l\u00f3zati vev\u0151k\u00e9sz\u00fcl\u00e9kek" + "description": "Adja meg az opcion\u00e1lis be\u00e1ll\u00edt\u00e1sokat" } } } diff --git a/homeassistant/components/denonavr/translations/id.json b/homeassistant/components/denonavr/translations/id.json index 50a28ef5447..5c20f9b377d 100644 --- a/homeassistant/components/denonavr/translations/id.json +++ b/homeassistant/components/denonavr/translations/id.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Konfirmasikan penambahan Receiver", - "title": "Network Receiver Denon AVR" + "description": "Konfirmasikan penambahan Receiver" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Kosongkan untuk menggunakan penemuan otomatis" - }, - "description": "Hubungkan ke Receiver Anda. Jika alamat IP tidak ditentukan, penemuan otomatis akan digunakan", - "title": "Network Receiver Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Siapkan Zona 2", "zone3": "Siapkan Zona 3" }, - "description": "Tentukan pengaturan opsional", - "title": "Network Receiver Denon AVR" + "description": "Tentukan pengaturan opsional" } } } diff --git a/homeassistant/components/denonavr/translations/it.json b/homeassistant/components/denonavr/translations/it.json index 23fffd3ab44..142ff573880 100644 --- a/homeassistant/components/denonavr/translations/it.json +++ b/homeassistant/components/denonavr/translations/it.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Conferma l'aggiunta del ricevitore", - "title": "Ricevitori di rete Denon AVR" + "description": "Conferma l'aggiunta del ricevitore" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Lascia vuoto per usare il rilevamento automatico" - }, - "description": "Collega il ricevitore, se l'indirizzo IP non \u00e8 impostato, sar\u00e0 utilizzato il rilevamento automatico", - "title": "Ricevitori di rete Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Configura la zona 2", "zone3": "Configura la zona 3" }, - "description": "Specificare le impostazioni opzionali", - "title": "Ricevitori di rete Denon AVR" + "description": "Specificare le impostazioni opzionali" } } } diff --git a/homeassistant/components/denonavr/translations/ja.json b/homeassistant/components/denonavr/translations/ja.json index cd3198b7e10..d81d5fab462 100644 --- a/homeassistant/components/denonavr/translations/ja.json +++ b/homeassistant/components/denonavr/translations/ja.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u53d7\u4fe1\u6a5f\u306e\u8ffd\u52a0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "\u30c7\u30ce\u30f3(Denon)AVR\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ec\u30b7\u30fc\u30d0\u30fc" + "description": "\u53d7\u4fe1\u6a5f\u306e\u8ffd\u52a0\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "\u81ea\u52d5\u691c\u51fa\u3092\u4f7f\u7528\u3059\u308b\u306b\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059" - }, - "description": "\u53d7\u4fe1\u6a5f\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", - "title": "\u30c7\u30ce\u30f3(Denon)AVR\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ec\u30b7\u30fc\u30d0\u30fc" + } } } }, @@ -44,8 +41,7 @@ "zone2": "\u30be\u30fc\u30f32\u306e\u8a2d\u5b9a", "zone3": "\u30be\u30fc\u30f33\u306e\u8a2d\u5b9a" }, - "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a", - "title": "\u30c7\u30ce\u30f3(Denon)AVR\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ec\u30b7\u30fc\u30d0\u30fc" + "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a" } } } diff --git a/homeassistant/components/denonavr/translations/ko.json b/homeassistant/components/denonavr/translations/ko.json index c0121a1e2ca..573011186aa 100644 --- a/homeassistant/components/denonavr/translations/ko.json +++ b/homeassistant/components/denonavr/translations/ko.json @@ -13,8 +13,7 @@ "flow_title": "Denon AVR \ub124\ud2b8\uc6cc\ud06c \ub9ac\uc2dc\ubc84: {name}", "step": { "confirm": { - "description": "\ub9ac\uc2dc\ubc84 \ucd94\uac00\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694", - "title": "Denon AVR \ub124\ud2b8\uc6cc\ud06c \ub9ac\uc2dc\ubc84" + "description": "\ub9ac\uc2dc\ubc84 \ucd94\uac00\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694" }, "select": { "data": { @@ -26,9 +25,7 @@ "user": { "data": { "host": "IP \uc8fc\uc18c" - }, - "description": "\ub9ac\uc2dc\ubc84\uc5d0 \uc5f0\uacb0\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 \uc790\ub3d9 \uac80\uc0c9\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4", - "title": "Denon AVR \ub124\ud2b8\uc6cc\ud06c \ub9ac\uc2dc\ubc84" + } } } }, @@ -40,8 +37,7 @@ "zone2": "Zone 2 \uc124\uc815", "zone3": "Zone 3 \uc124\uc815" }, - "description": "\uc635\uc158 \uc124\uc815 \uc9c0\uc815", - "title": "Denon AVR \ub124\ud2b8\uc6cc\ud06c \ub9ac\uc2dc\ubc84" + "description": "\uc635\uc158 \uc124\uc815 \uc9c0\uc815" } } } diff --git a/homeassistant/components/denonavr/translations/lb.json b/homeassistant/components/denonavr/translations/lb.json index fd8ece3c0bc..021d1de1a8d 100644 --- a/homeassistant/components/denonavr/translations/lb.json +++ b/homeassistant/components/denonavr/translations/lb.json @@ -13,8 +13,7 @@ "flow_title": "Denon AVR Netzwierk Empf\u00e4nger: {name}", "step": { "confirm": { - "description": "Best\u00e4teg dob\u00e4isetzen vum Receiver", - "title": "Denon AVR Netzwierk Empf\u00e4nger" + "description": "Best\u00e4teg dob\u00e4isetzen vum Receiver" }, "select": { "data": { @@ -26,9 +25,7 @@ "user": { "data": { "host": "IP Adress" - }, - "description": "Mam Receiver verbannen, falls keng IP Adress uginn ass g\u00ebtt auto-discovery benotzt", - "title": "Denon AVR Netzwierk Empf\u00e4nger" + } } } }, @@ -40,8 +37,7 @@ "zone2": "Zone 2 ariichten", "zone3": "Zone 3 ariichten" }, - "description": "Optionell Astellungen uginn", - "title": "Denon AVR Netzwierk Empf\u00e4nger" + "description": "Optionell Astellungen uginn" } } } diff --git a/homeassistant/components/denonavr/translations/nl.json b/homeassistant/components/denonavr/translations/nl.json index f03895452df..f3683561686 100644 --- a/homeassistant/components/denonavr/translations/nl.json +++ b/homeassistant/components/denonavr/translations/nl.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Bevestig het toevoegen van de ontvanger", - "title": "Denon AVR Network Receivers" + "description": "Bevestig het toevoegen van de ontvanger" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Leeg laten om auto-discovery te gebruiken" - }, - "description": "Maak verbinding met uw ontvanger. Als het IP-adres niet is ingesteld, wordt automatische detectie gebruikt", - "title": "Denon AVR Netwerk Ontvangers" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Stel Zone 2 in", "zone3": "Stel Zone 3 in" }, - "description": "Optionele instellingen opgeven", - "title": "Denon AVR Network Receivers" + "description": "Optionele instellingen opgeven" } } } diff --git a/homeassistant/components/denonavr/translations/no.json b/homeassistant/components/denonavr/translations/no.json index 333c55a44f7..46f23680812 100644 --- a/homeassistant/components/denonavr/translations/no.json +++ b/homeassistant/components/denonavr/translations/no.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Bekreft at du legger til mottakeren", - "title": "Denon AVR nettverksmottakere" + "description": "Bekreft at du legger til mottakeren" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "La feltet st\u00e5 tomt hvis du vil bruke automatisk s\u00f8k" - }, - "description": "Koble til mottakeren, hvis IP-adressen ikke er angitt, brukes automatisk oppdagelse", - "title": "Denon AVR Network Receivers" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Sett opp sone 2", "zone3": "Sett opp sone 3" }, - "description": "Spesifiser valgfrie innstillinger", - "title": "Denon AVR Network Receivers" + "description": "Spesifiser valgfrie innstillinger" } } } diff --git a/homeassistant/components/denonavr/translations/pl.json b/homeassistant/components/denonavr/translations/pl.json index 054371c6ba1..b82a6867dd8 100644 --- a/homeassistant/components/denonavr/translations/pl.json +++ b/homeassistant/components/denonavr/translations/pl.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Prosz\u0119 potwierdzi\u0107 dodanie urz\u0105dzenia", - "title": "Denon AVR" + "description": "Prosz\u0119 potwierdzi\u0107 dodanie urz\u0105dzenia" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Pozostaw puste, aby u\u017cy\u0107 automatycznego wykrywania" - }, - "description": "\u0141\u0105czenie z urz\u0105dzeniem, je\u015bli adres IP nie jest zdefiniowany, u\u017cywane jest automatyczne wykrywanie.", - "title": "Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Konfiguracja Strefy 2", "zone3": "Konfiguracja Strefy 3" }, - "description": "Ustawienia opcjonalne", - "title": "Denon AVR" + "description": "Ustawienia opcjonalne" } } } diff --git a/homeassistant/components/denonavr/translations/pt-BR.json b/homeassistant/components/denonavr/translations/pt-BR.json index 2a716dacdca..558accaf777 100644 --- a/homeassistant/components/denonavr/translations/pt-BR.json +++ b/homeassistant/components/denonavr/translations/pt-BR.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Confirme a adi\u00e7\u00e3o do receptor", - "title": "Receptores de rede Denon AVR" + "description": "Confirme a adi\u00e7\u00e3o do receptor" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Deixe em branco para usar a descoberta autom\u00e1tica" - }, - "description": "Conecte-se ao seu receptor, se o endere\u00e7o IP n\u00e3o estiver definido, a descoberta autom\u00e1tica ser\u00e1 usada", - "title": "Receptores de rede Denon AVR" + } } } }, @@ -44,8 +41,7 @@ "zone2": "Configure a Zona 2", "zone3": "Configurar a Zona 3" }, - "description": "Especificar configura\u00e7\u00f5es opcionais", - "title": "Receptores de rede Denon AVR" + "description": "Especificar configura\u00e7\u00f5es opcionais" } } } diff --git a/homeassistant/components/denonavr/translations/ru.json b/homeassistant/components/denonavr/translations/ru.json index 1db49decaad..01f0d868897 100644 --- a/homeassistant/components/denonavr/translations/ru.json +++ b/homeassistant/components/denonavr/translations/ru.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0438\u0432\u0435\u0440\u0430", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + "description": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0438\u0432\u0435\u0440\u0430" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435" - }, - "description": "\u0415\u0441\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + } } } }, @@ -44,8 +41,7 @@ "zone2": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0437\u043e\u043d\u044b 2", "zone3": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0437\u043e\u043d\u044b 3" }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" } } } diff --git a/homeassistant/components/denonavr/translations/tr.json b/homeassistant/components/denonavr/translations/tr.json index 046e535c621..fde01548e14 100644 --- a/homeassistant/components/denonavr/translations/tr.json +++ b/homeassistant/components/denonavr/translations/tr.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "L\u00fctfen al\u0131c\u0131y\u0131 eklemeyi onaylay\u0131n", - "title": "Denon AVR A\u011f Al\u0131c\u0131lar\u0131" + "description": "L\u00fctfen al\u0131c\u0131y\u0131 eklemeyi onaylay\u0131n" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "Otomatik bulmay\u0131 kullanmak i\u00e7in bo\u015f b\u0131rak\u0131n" - }, - "description": "Al\u0131c\u0131n\u0131za ba\u011flan\u0131n, IP adresi ayarlanmazsa otomatik bulma kullan\u0131l\u0131r", - "title": "Denon AVR A\u011f Al\u0131c\u0131lar\u0131" + } } } }, @@ -44,8 +41,7 @@ "zone2": "B\u00f6lge 2'yi kurun", "zone3": "B\u00f6lge 3'\u00fc kurun" }, - "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin", - "title": "Denon AVR A\u011f Al\u0131c\u0131lar\u0131" + "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin" } } } diff --git a/homeassistant/components/denonavr/translations/uk.json b/homeassistant/components/denonavr/translations/uk.json index efb4cb41777..dcc68648fcc 100644 --- a/homeassistant/components/denonavr/translations/uk.json +++ b/homeassistant/components/denonavr/translations/uk.json @@ -13,8 +13,7 @@ "flow_title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon: {name}", "step": { "confirm": { - "description": "\u041f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0456\u0442\u044c \u0434\u043e\u0434\u0430\u0432\u0430\u043d\u043d\u044f \u0440\u0435\u0441\u0438\u0432\u0435\u0440\u0430", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + "description": "\u041f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0456\u0442\u044c \u0434\u043e\u0434\u0430\u0432\u0430\u043d\u043d\u044f \u0440\u0435\u0441\u0438\u0432\u0435\u0440\u0430" }, "select": { "data": { @@ -26,9 +25,7 @@ "user": { "data": { "host": "IP-\u0430\u0434\u0440\u0435\u0441\u0430" - }, - "description": "\u042f\u043a\u0449\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u043d\u0435 \u0432\u043a\u0430\u0437\u0430\u043d\u0430, \u0431\u0443\u0434\u0435 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u0435 \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + } } } }, @@ -40,8 +37,7 @@ "zone2": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0437\u043e\u043d\u0438 2", "zone3": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0437\u043e\u043d\u0438 3" }, - "description": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f", - "title": "\u0420\u0435\u0441\u0438\u0432\u0435\u0440 Denon" + "description": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f" } } } diff --git a/homeassistant/components/denonavr/translations/zh-Hant.json b/homeassistant/components/denonavr/translations/zh-Hant.json index 1217a6d7b87..7983a4f7972 100644 --- a/homeassistant/components/denonavr/translations/zh-Hant.json +++ b/homeassistant/components/denonavr/translations/zh-Hant.json @@ -13,8 +13,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u8acb\u78ba\u8a8d\u65b0\u589e\u63a5\u6536\u5668", - "title": "Denon AVR \u7db2\u8def\u63a5\u6536\u5668" + "description": "\u8acb\u78ba\u8a8d\u65b0\u589e\u63a5\u6536\u5668" }, "select": { "data": { @@ -29,9 +28,7 @@ }, "data_description": { "host": "\u4fdd\u6301\u7a7a\u767d\u4ee5\u4f7f\u7528\u81ea\u52d5\u641c\u7d22" - }, - "description": "\u9023\u7dda\u81f3\u63a5\u6536\u5668\u3002\u5047\u5982\u672a\u8a2d\u5b9a IP \u4f4d\u5740\uff0c\u5c07\u4f7f\u7528\u81ea\u52d5\u63a2\u7d22\u3002", - "title": "Denon AVR \u7db2\u8def\u63a5\u6536\u5668" + } } } }, @@ -44,8 +41,7 @@ "zone2": "\u8a2d\u5b9a\u5340\u57df 2", "zone3": "\u8a2d\u5b9a\u5340\u57df 3" }, - "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a", - "title": "Denon AVR \u7db2\u8def\u63a5\u6536\u5668" + "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a" } } } diff --git a/homeassistant/components/derivative/translations/bg.json b/homeassistant/components/derivative/translations/bg.json index 14946d95fe0..b6c3577d1d0 100644 --- a/homeassistant/components/derivative/translations/bg.json +++ b/homeassistant/components/derivative/translations/bg.json @@ -14,11 +14,6 @@ "data": { "name": "\u0418\u043c\u0435" } - }, - "options": { - "data": { - "name": "\u0418\u043c\u0435" - } } } } diff --git a/homeassistant/components/derivative/translations/ca.json b/homeassistant/components/derivative/translations/ca.json index 3003b9349fd..9dff83bb746 100644 --- a/homeassistant/components/derivative/translations/ca.json +++ b/homeassistant/components/derivative/translations/ca.json @@ -36,22 +36,6 @@ "time_window": "Si s'estableix, el valor del sensor \u00e9s una mitjana m\u00f2bil ponderada en el temps de les derivades dins d'aquesta finestra.", "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric i la unitat de temps de la derivada seleccionats." } - }, - "options": { - "data": { - "name": "Nom", - "round": "Precisi\u00f3", - "source": "Sensor d'entrada", - "time_window": "Finestra de temps", - "unit_prefix": "Prefix m\u00e8tric", - "unit_time": "Unitat de temps" - }, - "data_description": { - "round": "Controla el nombre de d\u00edgits decimals a la sortida.", - "time_window": "Si s'estableix, el valor del sensor \u00e9s una mitjana m\u00f2bil ponderada en el temps de les derivades dins d'aquesta finestra.", - "unit_prefix": "La sortida s'escalar\u00e0 segons el prefix m\u00e8tric i la unitat de temps de la derivada seleccionats." - }, - "description": "Crea un sensor que estima la derivada d'un altre sensor." } } }, diff --git a/homeassistant/components/derivative/translations/de.json b/homeassistant/components/derivative/translations/de.json index 4e1dbc1929c..1a23de37c25 100644 --- a/homeassistant/components/derivative/translations/de.json +++ b/homeassistant/components/derivative/translations/de.json @@ -36,22 +36,6 @@ "time_window": "Wenn gesetzt, ist der Sensorwert ein zeitgewichteter gleitender Durchschnitt von Ableitungen innerhalb dieses Fensters.", "unit_prefix": "Die Ausgabe wird gem\u00e4\u00df dem ausgew\u00e4hlten metrischen Pr\u00e4fix und der Zeiteinheit der Ableitung skaliert.." } - }, - "options": { - "data": { - "name": "Name", - "round": "Genauigkeit", - "source": "Eingangssensor", - "time_window": "Zeitfenster", - "unit_prefix": "Metrisches Pr\u00e4fix", - "unit_time": "Zeiteinheit" - }, - "data_description": { - "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe.", - "time_window": "Wenn gesetzt, ist der Sensorwert ein zeitgewichteter gleitender Durchschnitt von Ableitungen innerhalb dieses Fensters.", - "unit_prefix": "Die Ausgabe wird gem\u00e4\u00df dem ausgew\u00e4hlten metrischen Pr\u00e4fix und der Zeiteinheit der Ableitung skaliert.." - }, - "description": "Erstelle einen Sensor, der die Ableitung eines Sensors sch\u00e4tzt." } } }, diff --git a/homeassistant/components/derivative/translations/el.json b/homeassistant/components/derivative/translations/el.json index a5a19efc1e5..a0c55e93a5e 100644 --- a/homeassistant/components/derivative/translations/el.json +++ b/homeassistant/components/derivative/translations/el.json @@ -36,22 +36,6 @@ "time_window": "\u0395\u03ac\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.", "unit_prefix": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." } - }, - "options": { - "data": { - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", - "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", - "source": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", - "time_window": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf", - "unit_prefix": "\u039c\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1", - "unit_time": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" - }, - "data_description": { - "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.", - "time_window": "\u0395\u03ac\u03bd \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.", - "unit_prefix": "\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03c9\u03b8\u03b5\u03af \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." - }, - "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf.\n\u0395\u03ac\u03bd \u03c4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 0, \u03b7 \u03c4\u03b9\u03bc\u03ae \u03c4\u03bf\u03c5 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03ac \u03c3\u03c4\u03b1\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b9\u03bd\u03b7\u03c4\u03cc\u03c2 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03c4\u03c9\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03c9\u03bd \u03b5\u03bd\u03c4\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c0\u03b1\u03c1\u03b1\u03b8\u03cd\u03c1\u03bf\u03c5.\n\u0397 \u03c0\u03b1\u03c1\u03ac\u03b3\u03c9\u03b3\u03bf\u03c2 \u03b8\u03b1 \u03ba\u03bb\u03b9\u03bc\u03b1\u03ba\u03ce\u03bd\u03b5\u03c4\u03b1\u03b9 \u03c3\u03cd\u03bc\u03c6\u03c9\u03bd\u03b1 \u03bc\u03b5 \u03c4\u03bf \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03bc\u03b5\u03c4\u03c1\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5 \u03c4\u03b7\u03c2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03ce\u03b3\u03bf\u03c5." } } }, diff --git a/homeassistant/components/derivative/translations/en.json b/homeassistant/components/derivative/translations/en.json index 884f1fa5244..b91318b5237 100644 --- a/homeassistant/components/derivative/translations/en.json +++ b/homeassistant/components/derivative/translations/en.json @@ -36,22 +36,6 @@ "time_window": "If set, the sensor's value is a time weighted moving average of derivatives within this window.", "unit_prefix": "The output will be scaled according to the selected metric prefix and time unit of the derivative.." } - }, - "options": { - "data": { - "name": "Name", - "round": "Precision", - "source": "Input sensor", - "time_window": "Time window", - "unit_prefix": "Metric prefix", - "unit_time": "Time unit" - }, - "data_description": { - "round": "Controls the number of decimal digits in the output.", - "time_window": "If set, the sensor's value is a time weighted moving average of derivatives within this window.", - "unit_prefix": "The output will be scaled according to the selected metric prefix and time unit of the derivative.." - }, - "description": "Create a sensor that estimates the derivative of a sensor." } } }, diff --git a/homeassistant/components/derivative/translations/es.json b/homeassistant/components/derivative/translations/es.json index 114c2102678..e6df75ba4d6 100644 --- a/homeassistant/components/derivative/translations/es.json +++ b/homeassistant/components/derivative/translations/es.json @@ -27,15 +27,6 @@ "round": "Controla el n\u00famero de d\u00edgitos decimales en la salida.", "unit_prefix": "a salida se escalar\u00e1 seg\u00fan el prefijo m\u00e9trico y la unidad de tiempo de la derivada seleccionados." } - }, - "options": { - "data": { - "name": "Nombre", - "time_window": "Ventana de tiempo", - "unit_prefix": "Prefijo m\u00e9trico", - "unit_time": "Unidad de tiempo" - }, - "description": "Crea un sensor que ama la derivada de otro sensor." } } }, diff --git a/homeassistant/components/derivative/translations/et.json b/homeassistant/components/derivative/translations/et.json index 45c566fac9b..4c3af55a294 100644 --- a/homeassistant/components/derivative/translations/et.json +++ b/homeassistant/components/derivative/translations/et.json @@ -36,22 +36,6 @@ "time_window": "Kui see on m\u00e4\u00e4ratud on anduri v\u00e4\u00e4rtus selle akna tuletisinstrumentide ajaga kaalutud liikuv keskmine.", "unit_prefix": "Tuletis skaleeritakse vastavalt valitud meetrilisele eesliitele ja tuletise aja\u00fchikule." } - }, - "options": { - "data": { - "name": "Nimi", - "round": "T\u00e4psus", - "source": "Sisendandur", - "time_window": "Ajavahemik", - "unit_prefix": "M\u00f5\u00f5diku eesliide", - "unit_time": "Aja\u00fchik" - }, - "data_description": { - "round": "K\u00fcmnendkohtade arv v\u00e4ljundis.", - "time_window": "Kui see on m\u00e4\u00e4ratud, on anduri v\u00e4\u00e4rtus selle akna tuletisinstrumentide ajaga kaalutud liikuv keskmine.", - "unit_prefix": "Tuletis skaleeritakse vastavalt valitud meetrilisele eesliitele ja tuletise aja\u00fchikule." - }, - "description": "T\u00e4psus reguleerib k\u00fcmnendkohtade arvu v\u00e4ljundis.\n Kui ajaaken ei ole 0, on anduri v\u00e4\u00e4rtuseks aknas olevate tuletisinstrumentide ajaga kaalutud liikuv keskmine.\n Tuletist skaleeritakse vastavalt valitud meetrilise prefiksile ja tuletise aja\u00fchikule." } } }, diff --git a/homeassistant/components/derivative/translations/fr.json b/homeassistant/components/derivative/translations/fr.json index d967a3abc89..3716c6907b9 100644 --- a/homeassistant/components/derivative/translations/fr.json +++ b/homeassistant/components/derivative/translations/fr.json @@ -36,22 +36,6 @@ "time_window": "Si d\u00e9finie, la valeur du capteur est une moyenne mobile pond\u00e9r\u00e9e dans le temps des d\u00e9riv\u00e9es dans cette p\u00e9riode.", "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique et de l'unit\u00e9 de temps de la d\u00e9riv\u00e9e s\u00e9lectionn\u00e9s.." } - }, - "options": { - "data": { - "name": "Nom", - "round": "Pr\u00e9cision", - "source": "Capteur d'entr\u00e9e", - "time_window": "P\u00e9riode", - "unit_prefix": "Pr\u00e9fixe m\u00e9trique", - "unit_time": "Unit\u00e9 de temps" - }, - "data_description": { - "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie.", - "time_window": "Si d\u00e9finie, la valeur du capteur est une moyenne mobile pond\u00e9r\u00e9e dans le temps des d\u00e9riv\u00e9es dans cette p\u00e9riode.", - "unit_prefix": "La sortie sera mise \u00e0 l'\u00e9chelle en fonction du pr\u00e9fixe m\u00e9trique et de l'unit\u00e9 de temps de la d\u00e9riv\u00e9e s\u00e9lectionn\u00e9s." - }, - "description": "Cr\u00e9ez un capteur calculant la d\u00e9riv\u00e9e d'un autre capteur." } } }, diff --git a/homeassistant/components/derivative/translations/he.json b/homeassistant/components/derivative/translations/he.json deleted file mode 100644 index 317b836da6d..00000000000 --- a/homeassistant/components/derivative/translations/he.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "options": { - "step": { - "options": { - "data_description": { - "unit_prefix": "." - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/hu.json b/homeassistant/components/derivative/translations/hu.json index e71175d2a32..65c9a38a473 100644 --- a/homeassistant/components/derivative/translations/hu.json +++ b/homeassistant/components/derivative/translations/hu.json @@ -36,22 +36,6 @@ "time_window": "Ha be van \u00e1ll\u00edtva, az \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az ablakon bel\u00fcli sz\u00e1rmaz\u00e9kok id\u0151vel s\u00falyozott mozg\u00f3\u00e1tlaga.", "unit_prefix": "A sz\u00e1rmaz\u00e9kos \u00e9rt\u00e9k a sz\u00e1rmaztatott term\u00e9k kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9g el\u0151tagja \u00e9s id\u0151egys\u00e9ge szerint lesz sk\u00e1l\u00e1zva.." } - }, - "options": { - "data": { - "name": "Elnevez\u00e9s", - "round": "Pontoss\u00e1g", - "source": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", - "time_window": "Id\u0151ablak", - "unit_prefix": "M\u00e9rt\u00e9kegys\u00e9g el\u0151tag", - "unit_time": "Id\u0151egys\u00e9g" - }, - "data_description": { - "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma.", - "time_window": "Ha be van \u00e1ll\u00edtva, az \u00e9rz\u00e9kel\u0151 \u00e9rt\u00e9ke az ablakon bel\u00fcli sz\u00e1rmaz\u00e9kok id\u0151vel s\u00falyozott mozg\u00f3\u00e1tlaga.", - "unit_prefix": "A sz\u00e1rmaz\u00e9kos \u00e9rt\u00e9k a sz\u00e1rmaztatott term\u00e9k kiv\u00e1lasztott m\u00e9rt\u00e9kegys\u00e9g el\u0151tagja \u00e9s id\u0151egys\u00e9ge szerint lesz sk\u00e1l\u00e1zva.." - }, - "description": "Hozzon l\u00e9tre egy \u00e9rz\u00e9kel\u0151t, amely megbecs\u00fcli a forr\u00e1s \u00e9rz\u00e9kel\u0151 sz\u00e1rmaz\u00e9k\u00e1t." } } }, diff --git a/homeassistant/components/derivative/translations/id.json b/homeassistant/components/derivative/translations/id.json index 67d6752a182..0461824523a 100644 --- a/homeassistant/components/derivative/translations/id.json +++ b/homeassistant/components/derivative/translations/id.json @@ -36,22 +36,6 @@ "time_window": "Jika disetel, nilai sensor adalah rata-rata bergerak berbobot waktu dari turunan dalam jangka ini.", "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih dan unit waktu turunan.." } - }, - "options": { - "data": { - "name": "Nama", - "round": "Presisi", - "source": "Sensor input", - "time_window": "Jangka waktu", - "unit_prefix": "Prefiks metrik", - "unit_time": "Unit waktu" - }, - "data_description": { - "round": "Mengontrol jumlah digit desimal dalam output.", - "time_window": "Jika disetel, nilai sensor adalah rata-rata bergerak berbobot waktu dari turunan dalam jangka ini.", - "unit_prefix": "Output akan diskalakan sesuai dengan prefiks metrik yang dipilih dan unit waktu turunan.." - }, - "description": "Buat sensor yang memperkirakan nilai turunan dari sebuah sensor." } } }, diff --git a/homeassistant/components/derivative/translations/it.json b/homeassistant/components/derivative/translations/it.json index ad888e13745..713f8334037 100644 --- a/homeassistant/components/derivative/translations/it.json +++ b/homeassistant/components/derivative/translations/it.json @@ -36,22 +36,6 @@ "time_window": "Se impostato, il valore del sensore \u00e8 una media mobile delle derivate ponderata nel tempo all'interno di questa finestra.", "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso metrico selezionato e all'unit\u00e0 di tempo della derivata.." } - }, - "options": { - "data": { - "name": "Nome", - "round": "Precisione", - "source": "Sensore di ingresso", - "time_window": "Finestra temporale", - "unit_prefix": "Prefisso metrico", - "unit_time": "Unit\u00e0 di tempo" - }, - "data_description": { - "round": "Controlla il numero di cifre decimali nell'uscita.", - "time_window": "Se impostato, il valore del sensore \u00e8 una media mobile delle derivate ponderata nel tempo all'interno di questa finestra.", - "unit_prefix": "L'output sar\u00e0 ridimensionato in base al prefisso metrico selezionato e all'unit\u00e0 di tempo della derivata.." - }, - "description": "Crea un sensore che stimi la derivata di un sensore." } } }, diff --git a/homeassistant/components/derivative/translations/ja.json b/homeassistant/components/derivative/translations/ja.json index 10232f996d6..c5939e95deb 100644 --- a/homeassistant/components/derivative/translations/ja.json +++ b/homeassistant/components/derivative/translations/ja.json @@ -36,22 +36,6 @@ "time_window": "\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u3068\u306a\u308a\u307e\u3059\u3002", "unit_prefix": "\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002." } - }, - "options": { - "data": { - "name": "\u540d\u524d", - "round": "\u7cbe\u5ea6", - "source": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", - "time_window": "\u6642\u9593\u8ef8", - "unit_prefix": "\u30e1\u30c8\u30ea\u30c3\u30af\u63a5\u982d\u8f9e", - "unit_time": "\u6642\u9593\u5358\u4f4d" - }, - "data_description": { - "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002", - "time_window": "\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u308b\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u3053\u306e\u30a6\u30a3\u30f3\u30c9\u30a6\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u3068\u306a\u308a\u307e\u3059\u3002", - "unit_prefix": "\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ec\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002." - }, - "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002\n\u6642\u9593\u7a93\u304c0\u3067\u306a\u3044\u5834\u5408\u3001\u30bb\u30f3\u30b5\u30fc\u306e\u5024\u306f\u7a93\u5185\u306e\u5fae\u5206\u306e\u6642\u9593\u52a0\u91cd\u79fb\u52d5\u5e73\u5747\u306b\u306a\u308a\u307e\u3059\u3002\n\u5fae\u5206\u306f\u3001\u9078\u629e\u3055\u308c\u305f\u5358\u4f4d\u306e\u30d7\u30ea\u30d5\u30a3\u30c3\u30af\u30b9\u3068\u5fae\u5206\u306e\u6642\u9593\u5358\u4f4d\u306b\u5f93\u3063\u3066\u30b9\u30b1\u30fc\u30ea\u30f3\u30b0\u3055\u308c\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/derivative/translations/ko.json b/homeassistant/components/derivative/translations/ko.json new file mode 100644 index 00000000000..a45cb7d22e5 --- /dev/null +++ b/homeassistant/components/derivative/translations/ko.json @@ -0,0 +1,43 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "\uc774\ub984", + "round": "\uc18c\uc218\uc810", + "source": "\uc785\ub825 \uc13c\uc11c", + "time_window": "\uc2dc\uac04\ub300", + "unit_prefix": "\ubbf8\ud130\ubc95", + "unit_time": "\uc2dc\uac04 \ub2e8\uc704" + }, + "data_description": { + "round": "\uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4.", + "time_window": "\uc124\uc815\ub41c \uacbd\uc6b0 \uc13c\uc11c\uc758 \uac12\uc740 \uc2dc\uac04\ub300 \ub0b4 \ub3c4\ud568\uc218\uc758 \uc2dc\uac04 \uac00\uc911 \uc774\ub3d9 \ud3c9\uade0\uc785\ub2c8\ub2e4.", + "unit_prefix": "\uc120\ud0dd\ud55c \ubbf8\ud130\ubc95 \ubc0f \ub3c4\ud568\uc218\uc758 \ub2e8\uc704\uc2dc\uac04\uc73c\ub85c \ud45c\uc2dc\ub429\ub2c8\ub2e4." + }, + "description": "\ub3c4\ud568\uc218\ub97c \uad6c\ud558\ub294 \uc13c\uc11c\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4.", + "title": "\ub3c4\ud568\uc218 \uc13c\uc11c \ucd94\uac00" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "name": "\uc774\ub984", + "round": "\uc18c\uc218\uc810", + "source": "\uc785\ub825 \uc13c\uc11c", + "time_window": "\uc2dc\uac04\ub300", + "unit_prefix": "\ubbf8\ud130\ubc95", + "unit_time": "\uc2dc\uac04 \ub2e8\uc704" + }, + "data_description": { + "round": "\uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4.", + "time_window": "\uc124\uc815\ub41c \uacbd\uc6b0 \uc13c\uc11c\uc758 \uac12\uc740 \uc2dc\uac04\ub300 \ub0b4 \ub3c4\ud568\uc218\uc758 \uc2dc\uac04 \uac00\uc911 \uc774\ub3d9 \ud3c9\uade0\uc785\ub2c8\ub2e4.", + "unit_prefix": "\uc120\ud0dd\ud55c \ubbf8\ud130\ubc95 \ubc0f \ub3c4\ud568\uc218\uc758 \ub2e8\uc704\uc2dc\uac04\uc73c\ub85c \ud45c\uc2dc\ub429\ub2c8\ub2e4.." + } + } + } + }, + "title": "\ub3c4\ud568\uc218 \uc13c\uc11c" +} \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/nl.json b/homeassistant/components/derivative/translations/nl.json index aa8eadc214c..f3dc9c07de7 100644 --- a/homeassistant/components/derivative/translations/nl.json +++ b/homeassistant/components/derivative/translations/nl.json @@ -36,22 +36,6 @@ "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." } - }, - "options": { - "data": { - "name": "Naam", - "round": "Nauwkeurigheid", - "source": "Invoersensor", - "time_window": "Tijdsvenster", - "unit_prefix": "Metrisch voorvoegsel", - "unit_time": "Tijdseenheid" - }, - "data_description": { - "round": "Regelt het aantal decimalen in de uitvoer.", - "time_window": "Indien ingesteld, is de waarde van de sensor een tijdgewogen voortschrijdend gemiddelde van de afgeleiden binnen dit venster.", - "unit_prefix": "De uitvoer wordt geschaald volgens het geselecteerde metrische voorvoegsel en de tijdseenheid van de afgeleide." - }, - "description": "Maak een sensor die de afgeleide van een sensor schat." } } }, diff --git a/homeassistant/components/derivative/translations/no.json b/homeassistant/components/derivative/translations/no.json index 735f1fae6ae..ca04f9d0d28 100644 --- a/homeassistant/components/derivative/translations/no.json +++ b/homeassistant/components/derivative/translations/no.json @@ -36,22 +36,6 @@ "time_window": "Hvis den er angitt, er sensorens verdi et tidsvektet glidende gjennomsnitt av derivater i dette vinduet.", "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset og tidsenheten til den deriverte.." } - }, - "options": { - "data": { - "name": "Navn", - "round": "Presisjon", - "source": "Inngangssensor", - "time_window": "Tidsvindu", - "unit_prefix": "Metrisk prefiks", - "unit_time": "Tidsenhet" - }, - "data_description": { - "round": "Styrer antall desimaler i utdataene.", - "time_window": "Hvis den er angitt, er sensorens verdi et tidsvektet glidende gjennomsnitt av derivater i dette vinduet.", - "unit_prefix": "Utdataene skaleres i henhold til det valgte metriske prefikset og tidsenheten til den deriverte.." - }, - "description": "Lag en sensor som estimerer den deriverte av en sensor." } } }, diff --git a/homeassistant/components/derivative/translations/pl.json b/homeassistant/components/derivative/translations/pl.json index 041d52ffeff..3f97ff29c2c 100644 --- a/homeassistant/components/derivative/translations/pl.json +++ b/homeassistant/components/derivative/translations/pl.json @@ -36,22 +36,6 @@ "time_window": "Je\u015bli jest ustawiona, warto\u015b\u0107 sensora jest wa\u017con\u0105 w czasie \u015bredni\u0105 ruchom\u0105 pochodnych w tym oknie.", "unit_prefix": "Wynik b\u0119dzie skalowany zgodnie z wybranym prefiksem metrycznym i jednostk\u0105 czasu pochodnej." } - }, - "options": { - "data": { - "name": "Nazwa", - "round": "Precyzja", - "source": "Sensor wej\u015bciowy", - "time_window": "Okno czasowe", - "unit_prefix": "Prefiks metryczny", - "unit_time": "Jednostka czasu" - }, - "data_description": { - "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych.", - "time_window": "Je\u015bli jest ustawiona, warto\u015b\u0107 sensora jest wa\u017con\u0105 w czasie \u015bredni\u0105 ruchom\u0105 pochodnych w tym oknie.", - "unit_prefix": "Wynik b\u0119dzie skalowany zgodnie z wybranym prefiksem metrycznym i jednostk\u0105 czasu pochodnej." - }, - "description": "Tworzy sensor, kt\u00f3ry szacuje pochodn\u0105 sensora." } } }, diff --git a/homeassistant/components/derivative/translations/pt-BR.json b/homeassistant/components/derivative/translations/pt-BR.json index 4d29a3970ee..54acbb3a5e5 100644 --- a/homeassistant/components/derivative/translations/pt-BR.json +++ b/homeassistant/components/derivative/translations/pt-BR.json @@ -36,22 +36,6 @@ "time_window": "Se definido, o valor do sensor \u00e9 uma m\u00e9dia m\u00f3vel ponderada no tempo das derivadas dentro desta janela.", "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado e a unidade de tempo da derivada.." } - }, - "options": { - "data": { - "name": "Nome", - "round": "Precis\u00e3o", - "source": "Sensor de entrada", - "time_window": "Janela do tempo", - "unit_prefix": "Prefixo da m\u00e9trica", - "unit_time": "Unidade de tempo" - }, - "data_description": { - "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda.", - "time_window": "Se definido, o valor do sensor \u00e9 uma m\u00e9dia m\u00f3vel ponderada no tempo das derivadas dentro desta janela.", - "unit_prefix": "A sa\u00edda ser\u00e1 dimensionada de acordo com o prefixo m\u00e9trico selecionado e a unidade de tempo da derivada.." - }, - "description": "Crie um sensor que estime a derivada de um sensor." } } }, diff --git a/homeassistant/components/derivative/translations/ru.json b/homeassistant/components/derivative/translations/ru.json index f3ff7cf0e83..6155d64301a 100644 --- a/homeassistant/components/derivative/translations/ru.json +++ b/homeassistant/components/derivative/translations/ru.json @@ -36,22 +36,6 @@ "time_window": "\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u043e \u0432\u0437\u0432\u0435\u0448\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u043c\u0443 \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439.." } - }, - "options": { - "data": { - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", - "source": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", - "time_window": "\u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u043e\u043a\u043d\u043e", - "unit_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u043c\u0435\u0442\u0440\u0438\u043a\u0438", - "unit_time": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438" - }, - "data_description": { - "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439.", - "time_window": "\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u043d\u043e \u0432\u0437\u0432\u0435\u0448\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u043a\u043e\u043b\u044c\u0437\u044f\u0449\u0435\u043c\u0443 \u0441\u0440\u0435\u0434\u043d\u0435\u043c\u0443 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u044d\u0442\u043e\u0433\u043e \u043e\u043a\u043d\u0430.", - "unit_prefix": "\u0414\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439.." - }, - "description": "\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u0443\u044e \u0441\u0435\u043d\u0441\u043e\u0440\u0430." } } }, diff --git a/homeassistant/components/derivative/translations/tr.json b/homeassistant/components/derivative/translations/tr.json index 68016c74372..500a0007494 100644 --- a/homeassistant/components/derivative/translations/tr.json +++ b/homeassistant/components/derivative/translations/tr.json @@ -36,22 +36,6 @@ "time_window": "Ayarlan\u0131rsa, sens\u00f6r\u00fcn de\u011feri, bu penceredeki t\u00fcrevlerin zaman a\u011f\u0131rl\u0131kl\u0131 hareketli ortalamas\u0131d\u0131r.", "unit_prefix": "\u00c7\u0131kt\u0131, t\u00fcrevin se\u00e7ilen metrik \u00f6nekine ve zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir.." } - }, - "options": { - "data": { - "name": "Ad", - "round": "Hassas", - "source": "Giri\u015f sens\u00f6r\u00fc", - "time_window": "Zaman penceresi", - "unit_prefix": "Metrik \u00f6neki", - "unit_time": "Zaman birimi" - }, - "data_description": { - "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder.", - "time_window": "Ayarlan\u0131rsa, sens\u00f6r\u00fcn de\u011feri, bu penceredeki t\u00fcrevlerin zaman a\u011f\u0131rl\u0131kl\u0131 hareketli ortalamas\u0131d\u0131r.", - "unit_prefix": "\u00c7\u0131kt\u0131, t\u00fcrevin se\u00e7ilen metrik \u00f6nekine ve zaman birimine g\u00f6re \u00f6l\u00e7eklenecektir.." - }, - "description": "Bir sens\u00f6r\u00fcn t\u00fcrevini tahmin eden bir sens\u00f6r olu\u015fturun." } } }, diff --git a/homeassistant/components/derivative/translations/zh-Hans.json b/homeassistant/components/derivative/translations/zh-Hans.json index 689f057dec0..130e7292282 100644 --- a/homeassistant/components/derivative/translations/zh-Hans.json +++ b/homeassistant/components/derivative/translations/zh-Hans.json @@ -36,22 +36,6 @@ "time_window": "\u5982\u679c\u8bbe\u7f6e\uff0c\u4f20\u611f\u5668\u5c06\u8f93\u51fa\u6b64\u65f6\u95f4\u7a97\u53e3\u5185\u7684\u53d8\u5316\u7387\u6309\u7167\u201c\u65f6\u95f4\u52a0\u6743\u79fb\u52a8\u5e73\u5747\u6cd5\u201d\u5904\u7406\u540e\u7684\u503c\u3002", "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u548c\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" } - }, - "options": { - "data": { - "name": "\u540d\u79f0", - "round": "\u7cbe\u5ea6", - "source": "\u8f93\u5165\u4f20\u611f\u5668", - "time_window": "\u65f6\u95f4\u7a97\u53e3", - "unit_prefix": "\u5355\u4f4d\u524d\u7f00", - "unit_time": "\u65f6\u95f4\u5355\u4f4d" - }, - "data_description": { - "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002", - "time_window": "\u5982\u679c\u8bbe\u7f6e\uff0c\u4f20\u611f\u5668\u5c06\u8f93\u51fa\u6b64\u65f6\u95f4\u7a97\u53e3\u5185\u7684\u53d8\u5316\u7387\u6309\u7167\u201c\u65f6\u95f4\u52a0\u6743\u79fb\u52a8\u5e73\u5747\u6cd5\u201d\u5904\u7406\u540e\u7684\u503c\u3002", - "unit_prefix": "\u8f93\u51fa\u503c\u5c06\u6839\u636e\u6240\u9009\u7684\u5355\u4f4d\u524d\u7f00\u548c\u65f6\u95f4\u5355\u4f4d\u8fdb\u884c\u7f29\u653e\u3002" - }, - "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u4f30\u7b97\u53e6\u4e00\u4e2a\u4f20\u611f\u5668\u7684\u53d8\u5316\u7387\u3002" } } }, diff --git a/homeassistant/components/derivative/translations/zh-Hant.json b/homeassistant/components/derivative/translations/zh-Hant.json index 3c100df8034..11236b0ad63 100644 --- a/homeassistant/components/derivative/translations/zh-Hant.json +++ b/homeassistant/components/derivative/translations/zh-Hant.json @@ -36,22 +36,6 @@ "time_window": "\u8a2d\u5b9a\u5f8c\u3001\u611f\u6e2c\u5668\u6578\u503c\u5c07\u70ba\u8996\u7a97\u5167\u5c0e\u6578\u7684\u6642\u9593\u52a0\u6b0a\u52a0\u6b0a\u79fb\u52d5\u5e73\u5747\u503c\u3002", "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u53ca\u5c0e\u6578\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002." } - }, - "options": { - "data": { - "name": "\u540d\u7a31", - "round": "\u6e96\u78ba\u5ea6", - "source": "\u8f38\u5165\u611f\u6e2c\u5668", - "time_window": "\u6642\u9593\u8996\u7a97", - "unit_prefix": "\u516c\u5236\u524d\u7db4", - "unit_time": "\u6642\u9593\u55ae\u4f4d" - }, - "data_description": { - "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002", - "time_window": "\u8a2d\u5b9a\u5f8c\u3001\u611f\u6e2c\u5668\u6578\u503c\u5c07\u70ba\u8996\u7a97\u5167\u5c0e\u6578\u7684\u6642\u9593\u52a0\u6b0a\u52a0\u6b0a\u79fb\u52d5\u5e73\u5747\u503c\u3002", - "unit_prefix": "\u8f38\u51fa\u5c07\u53d7\u6240\u9078\u64c7\u516c\u5236\u524d\u7db4\u53ca\u5c0e\u6578\u6642\u9593\u55ae\u4f4d\u800c\u8b8a\u5316\u3002" - }, - "description": "\u65b0\u589e\u9810\u4f30\u611f\u6e2c\u5668\u5c0e\u6578\u4e4b\u611f\u6e2c\u5668\u3002" } } }, diff --git a/homeassistant/components/devolo_home_network/translations/ko.json b/homeassistant/components/devolo_home_network/translations/ko.json new file mode 100644 index 00000000000..758f3336cd4 --- /dev/null +++ b/homeassistant/components/devolo_home_network/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/bg.json b/homeassistant/components/dlna_dmr/translations/bg.json index 00e64e1568d..4227d0b68f6 100644 --- a/homeassistant/components/dlna_dmr/translations/bg.json +++ b/homeassistant/components/dlna_dmr/translations/bg.json @@ -3,13 +3,11 @@ "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "could_not_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 DLNA \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", "incomplete_config": "\u0412 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u0442\u0430 \u043b\u0438\u043f\u0441\u0432\u0430 \u0437\u0430\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u0430 \u043f\u0440\u043e\u043c\u0435\u043d\u043b\u0438\u0432\u0430", "non_unique_id": "\u041d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0441\u0430 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 \u0435\u0434\u0438\u043d \u0438 \u0441\u044a\u0449 \u0443\u043d\u0438\u043a\u0430\u043b\u0435\u043d \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "could_not_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 DLNA \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{name}", "step": { @@ -23,8 +21,7 @@ }, "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "url": "URL" + "host": "\u0425\u043e\u0441\u0442" }, "title": "\u041e\u0442\u043a\u0440\u0438\u0442\u0438 DLNA DMR \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" } diff --git a/homeassistant/components/dlna_dmr/translations/ca.json b/homeassistant/components/dlna_dmr/translations/ca.json index 7c528d5ad29..0bbdc0b3de8 100644 --- a/homeassistant/components/dlna_dmr/translations/ca.json +++ b/homeassistant/components/dlna_dmr/translations/ca.json @@ -4,7 +4,6 @@ "already_configured": "El dispositiu ja est\u00e0 configurat", "alternative_integration": "El dispositiu t\u00e9 millor compatibilitat amb una altra integraci\u00f3", "cannot_connect": "Ha fallat la connexi\u00f3", - "could_not_connect": "No s'ha pogut connectar amb el dispositiu DLNA", "discovery_error": "No s'ha pogut descobrir cap dispositiu DLNA coincident", "incomplete_config": "Falta una variable obligat\u00f2ria a la configuraci\u00f3", "non_unique_id": "S'han trobat diversos dispositius amb el mateix identificador \u00fanic", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", - "could_not_connect": "No s'ha pogut connectar amb el dispositiu DLNA", "not_dmr": "El dispositiu no \u00e9s un renderitzador de mitjans digitals compatible" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Amfitri\u00f3", - "url": "URL" + "host": "Amfitri\u00f3" }, "description": "Tria un dispositiu a configurar o deixeu-ho en blanc per introduir un URL", "title": "Dispositius descoberts DLNA DMR" diff --git a/homeassistant/components/dlna_dmr/translations/cs.json b/homeassistant/components/dlna_dmr/translations/cs.json index 85c9a831dda..c9087b82ab7 100644 --- a/homeassistant/components/dlna_dmr/translations/cs.json +++ b/homeassistant/components/dlna_dmr/translations/cs.json @@ -7,11 +7,6 @@ "step": { "confirm": { "description": "Chcete za\u010d\u00edt nastavovat?" - }, - "user": { - "data": { - "url": "URL" - } } } } diff --git a/homeassistant/components/dlna_dmr/translations/de.json b/homeassistant/components/dlna_dmr/translations/de.json index baf1a53efca..64cae60c13e 100644 --- a/homeassistant/components/dlna_dmr/translations/de.json +++ b/homeassistant/components/dlna_dmr/translations/de.json @@ -4,7 +4,6 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert", "alternative_integration": "Das Ger\u00e4t wird besser durch eine andere Integration unterst\u00fctzt", "cannot_connect": "Verbindung fehlgeschlagen", - "could_not_connect": "Verbindung zum DLNA-Ger\u00e4t fehlgeschlagen", "discovery_error": "Ein passendes DLNA-Ger\u00e4t konnte nicht gefunden werden", "incomplete_config": "In der Konfiguration fehlt eine erforderliche Variable", "non_unique_id": "Mehrere Ger\u00e4te mit derselben eindeutigen ID gefunden", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", - "could_not_connect": "Verbindung zum DLNA-Ger\u00e4t fehlgeschlagen", "not_dmr": "Ger\u00e4t ist kein unterst\u00fctzter Digital Media Renderer" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "W\u00e4hle ein zu konfigurierendes Ger\u00e4t oder lasse es leer, um eine URL einzugeben.", "title": "Erkannte DLNA-DMR-Ger\u00e4te" diff --git a/homeassistant/components/dlna_dmr/translations/el.json b/homeassistant/components/dlna_dmr/translations/el.json index 5dc56ef3e37..884e1c51cb2 100644 --- a/homeassistant/components/dlna_dmr/translations/el.json +++ b/homeassistant/components/dlna_dmr/translations/el.json @@ -4,7 +4,6 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "alternative_integration": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03ba\u03b1\u03bb\u03cd\u03c4\u03b5\u03c1\u03b1 \u03b1\u03c0\u03cc \u03ac\u03bb\u03bb\u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "could_not_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae DLNA", "discovery_error": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2 \u03bc\u03b9\u03b1\u03c2 \u03b1\u03bd\u03c4\u03af\u03c3\u03c4\u03bf\u03b9\u03c7\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 DLNA", "incomplete_config": "\u0391\u03c0\u03cc \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bb\u03b5\u03af\u03c0\u03b5\u03b9 \u03bc\u03b9\u03b1 \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b7 \u03bc\u03b5\u03c4\u03b1\u03b2\u03bb\u03b7\u03c4\u03ae", "non_unique_id": "\u0392\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ad\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03bc\u03b5 \u03c4\u03bf \u03af\u03b4\u03b9\u03bf \u03bc\u03bf\u03bd\u03b1\u03b4\u03b9\u03ba\u03cc \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "could_not_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae DLNA", "not_dmr": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03bd\u03b1\u03c2 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf\u03c2 \u03c8\u03b7\u03c6\u03b9\u03b1\u03ba\u03cc\u03c2 \u03b1\u03bd\u03b1\u03bc\u03b5\u03c4\u03b1\u03b4\u03cc\u03c4\u03b7\u03c2 \u03c0\u03bf\u03bb\u03c5\u03bc\u03ad\u03c3\u03c9\u03bd" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL" + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ae \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", "title": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 DLNA DMR" diff --git a/homeassistant/components/dlna_dmr/translations/en.json b/homeassistant/components/dlna_dmr/translations/en.json index 51cbd875211..153f3f892d0 100644 --- a/homeassistant/components/dlna_dmr/translations/en.json +++ b/homeassistant/components/dlna_dmr/translations/en.json @@ -4,7 +4,6 @@ "already_configured": "Device is already configured", "alternative_integration": "Device is better supported by another integration", "cannot_connect": "Failed to connect", - "could_not_connect": "Failed to connect to DLNA device", "discovery_error": "Failed to discover a matching DLNA device", "incomplete_config": "Configuration is missing a required variable", "non_unique_id": "Multiple devices found with the same unique ID", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Failed to connect", - "could_not_connect": "Failed to connect to DLNA device", "not_dmr": "Device is not a supported Digital Media Renderer" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Choose a device to configure or leave blank to enter a URL", "title": "Discovered DLNA DMR devices" diff --git a/homeassistant/components/dlna_dmr/translations/es-419.json b/homeassistant/components/dlna_dmr/translations/es-419.json index 3dff7685122..14f0a98c68f 100644 --- a/homeassistant/components/dlna_dmr/translations/es-419.json +++ b/homeassistant/components/dlna_dmr/translations/es-419.json @@ -2,14 +2,12 @@ "config": { "abort": { "alternative_integration": "El dispositivo es mejor compatible con otra integraci\u00f3n", - "could_not_connect": "No se pudo conectar al dispositivo DLNA", "discovery_error": "Error al descubrir un dispositivo DLNA coincidente", "incomplete_config": "A la configuraci\u00f3n le falta una variable requerida", "non_unique_id": "Varios dispositivos encontrados con la misma ID \u00fanica", "not_dmr": "El dispositivo no es un renderizador de medios digitales compatible" }, "error": { - "could_not_connect": "No se pudo conectar al dispositivo DLNA", "not_dmr": "El dispositivo no es un renderizador de medios digitales compatible" }, "flow_title": "{name}", diff --git a/homeassistant/components/dlna_dmr/translations/es.json b/homeassistant/components/dlna_dmr/translations/es.json index 6878a05b766..6bb9dd83dbd 100644 --- a/homeassistant/components/dlna_dmr/translations/es.json +++ b/homeassistant/components/dlna_dmr/translations/es.json @@ -4,7 +4,6 @@ "already_configured": "El dispositivo ya est\u00e1 configurado", "alternative_integration": "Dispositivo compatible con otra integraci\u00f3n", "cannot_connect": "No se pudo conectar", - "could_not_connect": "No se pudo conectar al dispositivo DLNA", "discovery_error": "No se ha podido descubrir un dispositivo DLNA coincidente", "incomplete_config": "A la configuraci\u00f3n le falta una variable necesaria", "non_unique_id": "Se han encontrado varios dispositivos con el mismo ID \u00fanico", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "No se pudo conectar", - "could_not_connect": "No se pudo conectar al dispositivo DLNA", "not_dmr": "El dispositivo no es un procesador de medios digitales compatible" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Elija un dispositivo para configurar o d\u00e9jelo en blanco para introducir una URL", "title": "Dispositivos DLNA DMR descubiertos" diff --git a/homeassistant/components/dlna_dmr/translations/et.json b/homeassistant/components/dlna_dmr/translations/et.json index 1981b6eefde..adf665e4d2e 100644 --- a/homeassistant/components/dlna_dmr/translations/et.json +++ b/homeassistant/components/dlna_dmr/translations/et.json @@ -4,7 +4,6 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "alternative_integration": "Seadet toetab paremini teine sidumine", "cannot_connect": "\u00dchendamine nurjus", - "could_not_connect": "DLNA seadmega \u00fchenduse loomine nurjus", "discovery_error": "Sobiva DLNA -seadme leidmine nurjus", "incomplete_config": "Seadetes puudub n\u00f5utav muutuja", "non_unique_id": "Leiti mitu sama unikaalse ID-ga seadet", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u00dchendamine nurjus", - "could_not_connect": "DLNA seadmega \u00fchenduse loomine nurjus", "not_dmr": "Seade ei ole toetatud digitaalne meediumiedastusseade" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Vali h\u00e4\u00e4lestatav seade v\u00f5i j\u00e4ta URL -i sisestamiseks t\u00fchjaks", "title": "Avastatud DLNA DMR-seadmed" diff --git a/homeassistant/components/dlna_dmr/translations/fr.json b/homeassistant/components/dlna_dmr/translations/fr.json index 87686251c4d..9d3ed1e9f0a 100644 --- a/homeassistant/components/dlna_dmr/translations/fr.json +++ b/homeassistant/components/dlna_dmr/translations/fr.json @@ -4,7 +4,6 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "alternative_integration": "L'appareil est mieux pris en charge par une autre int\u00e9gration", "cannot_connect": "\u00c9chec de connexion", - "could_not_connect": "\u00c9chec de la connexion \u00e0 l'appareil DLNA", "discovery_error": "\u00c9chec de la d\u00e9couverte d'un appareil DLNA correspondant", "incomplete_config": "Il manque une variable requise dans la configuration", "non_unique_id": "Plusieurs appareils trouv\u00e9s avec le m\u00eame identifiant unique", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u00c9chec de connexion", - "could_not_connect": "\u00c9chec de la connexion \u00e0 l'appareil DLNA", "not_dmr": "L'appareil n'est pas un moteur de rendu multim\u00e9dia num\u00e9rique pris en charge" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "H\u00f4te", - "url": "URL" + "host": "H\u00f4te" }, "description": "Choisissez un appareil \u00e0 configurer ou laissez vide pour saisir une URL", "title": "Appareils DLNA DMR d\u00e9couverts" diff --git a/homeassistant/components/dlna_dmr/translations/he.json b/homeassistant/components/dlna_dmr/translations/he.json index 7025721fa43..145b91f2646 100644 --- a/homeassistant/components/dlna_dmr/translations/he.json +++ b/homeassistant/components/dlna_dmr/translations/he.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8" + "host": "\u05de\u05d0\u05e8\u05d7" } } } diff --git a/homeassistant/components/dlna_dmr/translations/hu.json b/homeassistant/components/dlna_dmr/translations/hu.json index 2773cf2bae4..e030d89530f 100644 --- a/homeassistant/components/dlna_dmr/translations/hu.json +++ b/homeassistant/components/dlna_dmr/translations/hu.json @@ -4,7 +4,6 @@ "already_configured": "Az eszk\u00f6z m\u00e1r be van konfigur\u00e1lva", "alternative_integration": "Az eszk\u00f6zt jobban t\u00e1mogatja egy m\u00e1sik integr\u00e1ci\u00f3", "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "could_not_connect": "Nem siker\u00fclt csatlakozni a DLNA-eszk\u00f6zh\u00f6z", "discovery_error": "Nem siker\u00fclt megfelel\u0151 DLNA-eszk\u00f6zt tal\u00e1lni", "incomplete_config": "A konfigur\u00e1ci\u00f3b\u00f3l hi\u00e1nyzik egy sz\u00fcks\u00e9ges \u00e9rt\u00e9k", "non_unique_id": "T\u00f6bb eszk\u00f6z tal\u00e1lhat\u00f3 ugyanazzal az egyedi azonos\u00edt\u00f3val", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "could_not_connect": "Nem siker\u00fclt csatlakozni a DLNA-eszk\u00f6zh\u00f6z", "not_dmr": "Az eszk\u00f6z nem digit\u00e1lis m\u00e9dia renderel\u0151" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "C\u00edm", - "url": "URL" + "host": "C\u00edm" }, "description": "V\u00e1lassza ki a konfigur\u00e1lni k\u00edv\u00e1nt eszk\u00f6zt, vagy hagyja \u00fcresen az URL-c\u00edm k\u00e9zi megad\u00e1s\u00e1hoz", "title": "DLNA digit\u00e1lis m\u00e9dia renderel\u0151" diff --git a/homeassistant/components/dlna_dmr/translations/id.json b/homeassistant/components/dlna_dmr/translations/id.json index 415ab7dc653..ee35acf0d5a 100644 --- a/homeassistant/components/dlna_dmr/translations/id.json +++ b/homeassistant/components/dlna_dmr/translations/id.json @@ -4,7 +4,6 @@ "already_configured": "Perangkat sudah dikonfigurasi", "alternative_integration": "Perangkat dapat didukung lebih baik lewat integrasi lainnya", "cannot_connect": "Gagal terhubung", - "could_not_connect": "Gagal terhubung ke perangkat DLNA", "discovery_error": "Gagal menemukan perangkat DLNA yang cocok", "incomplete_config": "Konfigurasi tidak memiliki variabel yang diperlukan", "non_unique_id": "Beberapa perangkat ditemukan dengan ID unik yang sama", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Gagal terhubung", - "could_not_connect": "Gagal terhubung ke perangkat DLNA", "not_dmr": "Perangkat bukan Digital Media Renderer yang didukung" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Pilih perangkat untuk dikonfigurasi atau biarkan kosong untuk memasukkan URL", "title": "Perangkat DLNA DMR yang ditemukan" diff --git a/homeassistant/components/dlna_dmr/translations/it.json b/homeassistant/components/dlna_dmr/translations/it.json index f9e3e11d5f0..e9b725de2d3 100644 --- a/homeassistant/components/dlna_dmr/translations/it.json +++ b/homeassistant/components/dlna_dmr/translations/it.json @@ -4,7 +4,6 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "alternative_integration": "Il dispositivo \u00e8 meglio supportato da un'altra integrazione", "cannot_connect": "Impossibile connettersi", - "could_not_connect": "Impossibile connettersi al dispositivo DLNA", "discovery_error": "Impossibile individuare un dispositivo DLNA corrispondente", "incomplete_config": "Nella configurazione manca una variabile richiesta", "non_unique_id": "Pi\u00f9 dispositivi trovati con lo stesso ID univoco", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Impossibile connettersi", - "could_not_connect": "Impossibile connettersi al dispositivo DLNA", "not_dmr": "Il dispositivo non \u00e8 un Digital Media Renderer supportato" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Scegli un dispositivo da configurare o lascia vuoto per inserire un URL", "title": "Rilevati dispositivi DLNA DMR" diff --git a/homeassistant/components/dlna_dmr/translations/ja.json b/homeassistant/components/dlna_dmr/translations/ja.json index 4c71d57b468..73e242dbb36 100644 --- a/homeassistant/components/dlna_dmr/translations/ja.json +++ b/homeassistant/components/dlna_dmr/translations/ja.json @@ -4,7 +4,6 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "alternative_integration": "\u30c7\u30d0\u30a4\u30b9\u306f\u5225\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3001\u3088\u308a\u9069\u5207\u306b\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u3059", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "could_not_connect": "DLNA\u30c7\u30d0\u30a4\u30b9\u3078\u306e\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "discovery_error": "\u4e00\u81f4\u3059\u308bDLNA \u30c7\u30d0\u30a4\u30b9\u3092\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f", "incomplete_config": "\u8a2d\u5b9a\u306b\u5fc5\u8981\u306a\u5909\u6570\u304c\u3042\u308a\u307e\u305b\u3093", "non_unique_id": "\u540c\u4e00\u306eID\u3067\u8907\u6570\u306e\u30c7\u30d0\u30a4\u30b9\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "could_not_connect": "DLNA\u30c7\u30d0\u30a4\u30b9\u3078\u306e\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "not_dmr": "\u30c7\u30d0\u30a4\u30b9\u304c\u3001\u672a\u30b5\u30dd\u30fc\u30c8\u306aDigital Media Renderer\u3067\u3059" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "\u30db\u30b9\u30c8", - "url": "URL" + "host": "\u30db\u30b9\u30c8" }, "description": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b\u304b\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066URL\u3092\u5165\u529b\u3057\u307e\u3059\u3002", "title": "\u767a\u898b\u3055\u308c\u305fDLNA DMR\u6a5f\u5668" diff --git a/homeassistant/components/dlna_dmr/translations/ko.json b/homeassistant/components/dlna_dmr/translations/ko.json new file mode 100644 index 00000000000..c3c5f1a74b2 --- /dev/null +++ b/homeassistant/components/dlna_dmr/translations/ko.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "alternative_integration": "\uae30\uae30\uac00 \ub2e4\ub978 \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uc5d0\uc11c \ub354 \uc798 \uc9c0\uc6d0\ub429\ub2c8\ub2e4." + }, + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "manual": { + "description": "\uc7a5\uce58 \uc124\uba85 XML \ud30c\uc77c\uc758 URL" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "browse_unfiltered": "\ud638\ud658\ub418\uc9c0 \uc54a\ub294 \ubbf8\ub514\uc5b4 \ud45c\uc2dc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/dlna_dmr/translations/nl.json b/homeassistant/components/dlna_dmr/translations/nl.json index 6147fac2007..360d24fd590 100644 --- a/homeassistant/components/dlna_dmr/translations/nl.json +++ b/homeassistant/components/dlna_dmr/translations/nl.json @@ -4,7 +4,6 @@ "already_configured": "Apparaat is al geconfigureerd", "alternative_integration": "Apparaat wordt beter ondersteund door een andere integratie", "cannot_connect": "Kan geen verbinding maken", - "could_not_connect": "Mislukt om te verbinden met DNLA apparaat", "discovery_error": "Kan geen overeenkomend DLNA-apparaat vinden", "incomplete_config": "Configuratie mist een vereiste variabele", "non_unique_id": "Meerdere apparaten gevonden met hetzelfde unieke ID", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "could_not_connect": "Mislukt om te verbinden met DNLA apparaat", "not_dmr": "Apparaat is een niet-ondersteund Digital Media Renderer" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Host", - "url": "URL" + "host": "Host" }, "description": "Kies een apparaat om te configureren of laat leeg om een URL in te voeren", "title": "Ontdekt DLNA Digital Media Renderer" diff --git a/homeassistant/components/dlna_dmr/translations/no.json b/homeassistant/components/dlna_dmr/translations/no.json index 04fed89ca78..a1f4fb9b7cc 100644 --- a/homeassistant/components/dlna_dmr/translations/no.json +++ b/homeassistant/components/dlna_dmr/translations/no.json @@ -4,7 +4,6 @@ "already_configured": "Enheten er allerede konfigurert", "alternative_integration": "Enheten st\u00f8ttes bedre av en annen integrasjon", "cannot_connect": "Tilkobling mislyktes", - "could_not_connect": "Kunne ikke koble til DLNA -enhet", "discovery_error": "Kunne ikke finne en matchende DLNA -enhet", "incomplete_config": "Konfigurasjonen mangler en n\u00f8dvendig variabel", "non_unique_id": "Flere enheter ble funnet med samme unike ID", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Tilkobling mislyktes", - "could_not_connect": "Kunne ikke koble til DLNA -enhet", "not_dmr": "Enheten er ikke en st\u00f8ttet Digital Media Renderer" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Vert", - "url": "URL" + "host": "Vert" }, "description": "Velg en enhet du vil konfigurere, eller la den st\u00e5 tom for \u00e5 angi en URL", "title": "Oppdaget DLNA DMR -enheter" diff --git a/homeassistant/components/dlna_dmr/translations/pl.json b/homeassistant/components/dlna_dmr/translations/pl.json index 23f46c84c38..dae2b58e1c9 100644 --- a/homeassistant/components/dlna_dmr/translations/pl.json +++ b/homeassistant/components/dlna_dmr/translations/pl.json @@ -4,7 +4,6 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "alternative_integration": "Urz\u0105dzenie jest lepiej obs\u0142ugiwane przez inn\u0105 integracj\u0119", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "could_not_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z urz\u0105dzeniem DLNA", "discovery_error": "Nie uda\u0142o si\u0119 wykry\u0107 pasuj\u0105cego urz\u0105dzenia DLNA", "incomplete_config": "W konfiguracji brakuje wymaganej zmiennej", "non_unique_id": "Znaleziono wiele urz\u0105dze\u0144 z tym samym unikalnym identyfikatorem", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "could_not_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z urz\u0105dzeniem DLNA", "not_dmr": "Urz\u0105dzenie nie jest obs\u0142ugiwanym rendererem multimedi\u00f3w cyfrowych (DMR)" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Nazwa hosta lub adres IP", - "url": "URL" + "host": "Nazwa hosta lub adres IP" }, "description": "Wybierz urz\u0105dzenie do skonfigurowania lub pozostaw puste, aby wprowadzi\u0107 adres URL", "title": "Wykryto urz\u0105dzenia DLNA DMR" diff --git a/homeassistant/components/dlna_dmr/translations/pt-BR.json b/homeassistant/components/dlna_dmr/translations/pt-BR.json index 5caf64af4b1..f5f8370e52c 100644 --- a/homeassistant/components/dlna_dmr/translations/pt-BR.json +++ b/homeassistant/components/dlna_dmr/translations/pt-BR.json @@ -4,7 +4,6 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "alternative_integration": "O dispositivo \u00e9 melhor suportado por outra integra\u00e7\u00e3o", "cannot_connect": "Falha ao conectar", - "could_not_connect": "Falha ao conectar ao dispositivo DLNA", "discovery_error": "Falha ao descobrir um dispositivo DLNA correspondente", "incomplete_config": "A configura\u00e7\u00e3o n\u00e3o tem uma vari\u00e1vel obrigat\u00f3ria", "non_unique_id": "V\u00e1rios dispositivos encontrados com o mesmo ID exclusivo", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Falha ao conectar", - "could_not_connect": "Falha ao conectar-se ao dispositivo DLNA", "not_dmr": "O dispositivo n\u00e3o \u00e9 um renderizador de m\u00eddia digital compat\u00edvel" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Nome do host", - "url": "URL" + "host": "Nome do host" }, "description": "Escolha um dispositivo para configurar ou deixe em branco para inserir um URL", "title": "Dispositivos DMR DLNA descobertos" diff --git a/homeassistant/components/dlna_dmr/translations/ru.json b/homeassistant/components/dlna_dmr/translations/ru.json index 09c98173b3b..7028b754d5d 100644 --- a/homeassistant/components/dlna_dmr/translations/ru.json +++ b/homeassistant/components/dlna_dmr/translations/ru.json @@ -4,7 +4,6 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "alternative_integration": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "could_not_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443.", "discovery_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0435\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e DLNA.", "incomplete_config": "\u0412 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f.", "non_unique_id": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0441 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c.", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "could_not_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443.", "not_dmr": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 Digital Media Renderer." }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "url": "URL-\u0430\u0434\u0440\u0435\u0441" + "host": "\u0425\u043e\u0441\u0442" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0432\u0432\u0435\u0441\u0442\u0438 URL.", "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 DLNA DMR" diff --git a/homeassistant/components/dlna_dmr/translations/tr.json b/homeassistant/components/dlna_dmr/translations/tr.json index bee4a922a75..26d911056b7 100644 --- a/homeassistant/components/dlna_dmr/translations/tr.json +++ b/homeassistant/components/dlna_dmr/translations/tr.json @@ -4,7 +4,6 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "alternative_integration": "Cihaz ba\u015fka bir entegrasyon taraf\u0131ndan daha iyi destekleniyor", "cannot_connect": "Ba\u011flanma hatas\u0131", - "could_not_connect": "DLNA cihaz\u0131na ba\u011flan\u0131lamad\u0131", "discovery_error": "E\u015fle\u015fen bir DLNA cihaz\u0131 bulunamad\u0131", "incomplete_config": "Yap\u0131land\u0131rmada gerekli bir de\u011fi\u015fken eksik", "non_unique_id": "Ayn\u0131 benzersiz kimli\u011fe sahip birden fazla cihaz bulundu", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", - "could_not_connect": "DLNA cihaz\u0131na ba\u011flan\u0131lamad\u0131", "not_dmr": "Cihaz, desteklenen bir Dijital Medya Olu\u015fturucu de\u011fil" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "Sunucu", - "url": "URL" + "host": "Sunucu" }, "description": "Yap\u0131land\u0131rmak i\u00e7in bir cihaz se\u00e7in veya bir URL girmek i\u00e7in bo\u015f b\u0131rak\u0131n", "title": "Ke\u015ffedilen DLNA DMR cihazlar\u0131" diff --git a/homeassistant/components/dlna_dmr/translations/zh-Hans.json b/homeassistant/components/dlna_dmr/translations/zh-Hans.json index 2046f1c2a47..16bd75d28bf 100644 --- a/homeassistant/components/dlna_dmr/translations/zh-Hans.json +++ b/homeassistant/components/dlna_dmr/translations/zh-Hans.json @@ -4,7 +4,6 @@ "already_configured": "\u8bbe\u5907\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86", "alternative_integration": "\u8be5\u8bbe\u5907\u5728\u53e6\u4e00\u96c6\u6210\u80fd\u63d0\u4f9b\u66f4\u597d\u7684\u652f\u6301", "cannot_connect": "\u8fde\u63a5\u5931\u8d25", - "could_not_connect": "\u8fde\u63a5 DLNA \u8bbe\u5907\u5931\u8d25", "discovery_error": "\u672a\u53d1\u73b0\u53ef\u7528\u7684 DLNA \u8bbe\u5907", "incomplete_config": "\u914d\u7f6e\u7f3a\u5c11\u5fc5\u8981\u7684\u53d8\u91cf\u4fe1\u606f", "non_unique_id": "\u53d1\u73b0\u591a\u53f0\u8bbe\u5907\u5177\u6709\u76f8\u540c\u7684 unique ID", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u8fde\u63a5\u5931\u8d25", - "could_not_connect": "\u65e0\u6cd5\u8fde\u63a5\u5230 DLNA \u8bbe\u5907", "not_dmr": "\u8be5\u8bbe\u5907\u4e0d\u662f\u53d7\u652f\u6301\u7684\u6570\u5b57\u5a92\u4f53\u6e32\u67d3\u5668\uff08DMR\uff09" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "\u4e3b\u673a", - "url": "\u7f51\u5740" + "host": "\u4e3b\u673a" }, "title": "\u53d1\u73b0 DLNA DMR \u8bbe\u5907" } diff --git a/homeassistant/components/dlna_dmr/translations/zh-Hant.json b/homeassistant/components/dlna_dmr/translations/zh-Hant.json index c75334df1ae..07293607685 100644 --- a/homeassistant/components/dlna_dmr/translations/zh-Hant.json +++ b/homeassistant/components/dlna_dmr/translations/zh-Hant.json @@ -4,7 +4,6 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "alternative_integration": "\u4f7f\u7528\u5176\u4ed6\u6574\u5408\u4ee5\u53d6\u5f97\u66f4\u4f73\u7684\u88dd\u7f6e\u652f\u63f4", "cannot_connect": "\u9023\u7dda\u5931\u6557", - "could_not_connect": "DLNA \u88dd\u7f6e\u9023\u7dda\u5931\u6557\u3002", "discovery_error": "DLNA \u88dd\u7f6e\u641c\u7d22\u5931\u6557", "incomplete_config": "\u6240\u7f3a\u5c11\u7684\u8a2d\u5b9a\u70ba\u5fc5\u9808\u8b8a\u6578", "non_unique_id": "\u627e\u5230\u591a\u7d44\u88dd\u7f6e\u4f7f\u7528\u4e86\u76f8\u540c\u552f\u4e00 ID", @@ -12,7 +11,6 @@ }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", - "could_not_connect": "DLNA \u88dd\u7f6e\u9023\u7dda\u5931\u6557\u3002", "not_dmr": "\u88dd\u7f6e\u70ba\u975e\u652f\u63f4 Digital Media Renderer" }, "flow_title": "{name}", @@ -32,8 +30,7 @@ }, "user": { "data": { - "host": "\u4e3b\u6a5f\u7aef", - "url": "\u7db2\u5740" + "host": "\u4e3b\u6a5f\u7aef" }, "description": "\u9078\u64c7\u88dd\u7f6e\u9032\u884c\u8a2d\u5b9a\u6216\u4fdd\u7559\u7a7a\u767d\u4ee5\u8f38\u5165 URL", "title": "\u5df2\u767c\u73fe\u7684 DLNA DMR \u88dd\u7f6e" diff --git a/homeassistant/components/dlna_dms/translations/ko.json b/homeassistant/components/dlna_dms/translations/ko.json new file mode 100644 index 00000000000..20ad990e862 --- /dev/null +++ b/homeassistant/components/dlna_dms/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/doorbird/translations/ca.json b/homeassistant/components/doorbird/translations/ca.json index 0049709caa3..14c5f5f452d 100644 --- a/homeassistant/components/doorbird/translations/ca.json +++ b/homeassistant/components/doorbird/translations/ca.json @@ -18,8 +18,7 @@ "name": "Nom del dispositiu", "password": "[%key::common::config_flow::data::password%]", "username": "[%key::common::config_flow::data::username%]" - }, - "title": "Connexi\u00f3 amb DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Afegeix el/s noms del/s esdeveniment/s que vulguis seguir separats per comes. Despr\u00e9s d'introduir-los, utilitza l'aplicaci\u00f3 de DoorBird per assignar-los a un esdeveniment espec\u00edfic.\n\nExemple: algu_ha_premut_el_boto, moviment" - }, - "description": "Afegeix el/s noms del/s esdeveniment/s que vulguis seguir separats per comes. Despr\u00e9s d'introduir-los, utilitza l'aplicaci\u00f3 de DoorBird per assignar-los a un esdeveniment espec\u00edfic. Consulta la documentaci\u00f3 a https://www.home-assistant.io/integrations/doorbird/#events.\nExemple: algu_ha_premut_el_boto, moviment_detectat" + } } } } diff --git a/homeassistant/components/doorbird/translations/cs.json b/homeassistant/components/doorbird/translations/cs.json index fea0647ec85..2b09c0648d0 100644 --- a/homeassistant/components/doorbird/translations/cs.json +++ b/homeassistant/components/doorbird/translations/cs.json @@ -18,8 +18,7 @@ "name": "Jm\u00e9no za\u0159\u00edzen\u00ed", "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, - "title": "P\u0159ipojen\u00ed k DoorBird" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "Seznam ud\u00e1lost\u00ed odd\u011blen\u00fdch \u010d\u00e1rkami." - }, - "description": "Zadejte n\u00e1zvy ud\u00e1lost\u00ed odd\u011blen\u00e9 \u010d\u00e1rkou, kter\u00e9 chcete sledovat. Po jejich zad\u00e1n\u00ed je pomoc\u00ed aplikace DoorBird p\u0159i\u0159a\u010fte ke konkr\u00e9tn\u00ed ud\u00e1losti. Viz dokumentace na https://www.home-assistant.io/integrations/doorbird/#events. P\u0159\u00edklad: nekdo_stiskl_tlacitko, pohyb" + } } } } diff --git a/homeassistant/components/doorbird/translations/de.json b/homeassistant/components/doorbird/translations/de.json index 7a60a3bf4ae..9689b0bc728 100644 --- a/homeassistant/components/doorbird/translations/de.json +++ b/homeassistant/components/doorbird/translations/de.json @@ -18,8 +18,7 @@ "name": "Ger\u00e4tename", "password": "Passwort", "username": "Benutzername" - }, - "title": "Stelle eine Verbindung zu DoorBird her" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "F\u00fcge f\u00fcr jedes Ereignis, das du verfolgen m\u00f6chtest, einen durch Komma getrennten Ereignisnamen hinzu. Nachdem du sie hier eingegeben hast, verwende die DoorBird-App, um sie einem bestimmten Ereignis zuzuordnen.\n\nBeispiel: jemand_drueckte_den_Knopf, bewegung" - }, - "description": "F\u00fcge f\u00fcr jedes Ereignis, das du verfolgen m\u00f6chtest, einen durch Kommas getrennten Ereignisnamen hinzu. Nachdem du sie hier eingegeben hast, verwende die DoorBird-App, um sie einem bestimmten Ereignis zuzuweisen. Weitere Informationen findest du in der Dokumentation unter https://www.home-assistant.io/integrations/doorbird/#events. Beispiel: jemand_hat_den_knopf_gedr\u00fcckt, bewegung" + } } } } diff --git a/homeassistant/components/doorbird/translations/el.json b/homeassistant/components/doorbird/translations/el.json index da06dc3e0be..1805e57e70a 100644 --- a/homeassistant/components/doorbird/translations/el.json +++ b/homeassistant/components/doorbird/translations/el.json @@ -18,8 +18,7 @@ "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03b5 \u03c4\u03bf DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5. \u0391\u03c6\u03bf\u03cd \u03c4\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03b4\u03ce, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae DoorBird \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03b9\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03bf \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd.\n\n\u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: somebody_pressed_the_button, motion" - }, - "description": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03b5\u03c4\u03b5. \u0391\u03c6\u03bf\u03cd \u03c4\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03b4\u03ce, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae DoorBird \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c4\u03b1 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03bf\u03b9\u03c7\u03af\u03c3\u03b5\u03c4\u03b5 \u03c3\u03b5 \u03ad\u03bd\u03b1 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03bf \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/doorbird/#events. \u03a0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/en.json b/homeassistant/components/doorbird/translations/en.json index ee005dfbfed..b5df9f89257 100644 --- a/homeassistant/components/doorbird/translations/en.json +++ b/homeassistant/components/doorbird/translations/en.json @@ -18,8 +18,7 @@ "name": "Device Name", "password": "Password", "username": "Username" - }, - "title": "Connect to the DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Add an comma separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event.\n\nExample: somebody_pressed_the_button, motion" - }, - "description": "Add an comma separated event name for each event you wish to track. After entering them here, use the DoorBird app to assign them to a specific event. See the documentation at https://www.home-assistant.io/integrations/doorbird/#events. Example: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/es-419.json b/homeassistant/components/doorbird/translations/es-419.json index 6b893b93014..9eed0664e00 100644 --- a/homeassistant/components/doorbird/translations/es-419.json +++ b/homeassistant/components/doorbird/translations/es-419.json @@ -14,8 +14,7 @@ "data": { "host": "Host (direcci\u00f3n IP)", "name": "Nombre del dispositivo" - }, - "title": "Conectar con DoorBird" + } } } }, @@ -24,8 +23,7 @@ "init": { "data": { "events": "Lista de eventos separados por comas." - }, - "description": "Agregue un nombre de evento separado por comas para cada evento que desee rastrear. Despu\u00e9s de ingresarlos aqu\u00ed, use la aplicaci\u00f3n DoorBird para asignarlos a un evento espec\u00edfico. Consulte la documentaci\u00f3n en https://www.home-assistant.io/integrations/doorbird/#events. Ejemplo: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/es.json b/homeassistant/components/doorbird/translations/es.json index f7e2ba8d92c..68de2419d2a 100644 --- a/homeassistant/components/doorbird/translations/es.json +++ b/homeassistant/components/doorbird/translations/es.json @@ -18,8 +18,7 @@ "name": "Nombre del dispositivo", "password": "Contrase\u00f1a", "username": "Usuario" - }, - "title": "Conectar con DoorBird" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "Lista de eventos separados por comas." - }, - "description": "A\u00f1ade un nombre de evento separado por comas para cada evento del que deseas realizar un seguimiento. Despu\u00e9s de introducirlos aqu\u00ed, utiliza la aplicaci\u00f3n DoorBird para asignarlos a un evento espec\u00edfico. Consulta la documentaci\u00f3n en https://www.home-assistant.io/integrations/doorbird/#events. Ejemplo: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/et.json b/homeassistant/components/doorbird/translations/et.json index f37d5d0244b..01601861ffc 100644 --- a/homeassistant/components/doorbird/translations/et.json +++ b/homeassistant/components/doorbird/translations/et.json @@ -18,8 +18,7 @@ "name": "Seadme nimi", "password": "Salas\u00f5na", "username": "Kasutajanimi" - }, - "title": "Loo \u00fchendus DoorBird-ga" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Lisa iga s\u00fcndmuse jaoks, mida soovid j\u00e4lgida, komaga eraldatud s\u00fcndmuse nimi. P\u00e4rast nende sisestamist siia kasuta DoorBirdi rakendust, et m\u00e4\u00e4rata need konkreetsele s\u00fcndmusele.\n\nN\u00e4ide: somebody_pressed_the_button, liikumine" - }, - "description": "Lisa komaga eraldatud s\u00fcndmuse nimi igale j\u00e4lgitavale s\u00fcndmusele. P\u00e4rast nende sisestamist kasuta rakendust DoorBird, et siduda need konkreetse s\u00fcndmusega. Vaata dokumentatsiooni aadressil https://www.home-assistant.io/integrations/doorbird/#events. N\u00e4ide: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/fr.json b/homeassistant/components/doorbird/translations/fr.json index b4819897366..873204b0ef0 100644 --- a/homeassistant/components/doorbird/translations/fr.json +++ b/homeassistant/components/doorbird/translations/fr.json @@ -18,8 +18,7 @@ "name": "Nom de l'appareil", "password": "Mot de passe", "username": "Nom d'utilisateur" - }, - "title": "Connectez-vous au DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Ajoutez les noms des \u00e9v\u00e9nements que vous souhaitez suivre, s\u00e9par\u00e9s par des virgules. Apr\u00e8s les avoir saisis ici, utilisez l'application DoorBird pour les affecter \u00e0 un \u00e9v\u00e9nement sp\u00e9cifique.\n\nExemple\u00a0: somebody_pressed_the_button, motion" - }, - "description": "Ajoutez un nom d'\u00e9v\u00e9nement s\u00e9par\u00e9 par des virgules pour chaque \u00e9v\u00e9nement que vous souhaitez suivre. Apr\u00e8s les avoir saisis ici, utilisez l'application DoorBird pour les affecter \u00e0 un \u00e9v\u00e9nement sp\u00e9cifique. Consultez la documentation sur https://www.home-assistant.io/integrations/doorbird/#events. Exemple: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/hu.json b/homeassistant/components/doorbird/translations/hu.json index 51bbb0864dd..0062ffd46ef 100644 --- a/homeassistant/components/doorbird/translations/hu.json +++ b/homeassistant/components/doorbird/translations/hu.json @@ -18,8 +18,7 @@ "name": "Eszk\u00f6z neve", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "title": "Csatlakozzon a DoorBird-hez" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Adjon hozz\u00e1 egy vessz\u0151vel elv\u00e1lasztott esem\u00e9nynevet minden egyes esem\u00e9nyhez, amelyet nyomon k\u00edv\u00e1n k\u00f6vetni. Miut\u00e1n be\u00edrta \u0151ket ide, a DoorBird alkalmaz\u00e1ssal rendelje \u0151ket egy adott esem\u00e9nyhez.\n\nP\u00e9lda: valaki_megnyomta_a_gombot, motion" - }, - "description": "Adjon hozz\u00e1 vessz\u0151vel elv\u00e1lasztott esem\u00e9nynevet minden k\u00f6vetni k\u00edv\u00e1nt esem\u00e9nyhez. Miut\u00e1n itt megadta \u0151ket, haszn\u00e1lja a DoorBird alkalmaz\u00e1st, hogy hozz\u00e1rendelje \u0151ket egy adott esem\u00e9nyhez. Tekintse meg a dokument\u00e1ci\u00f3t a https://www.home-assistant.io/integrations/doorbird/#events c\u00edmen. P\u00e9lda: valaki_pr\u00e9selt_gomb, mozg\u00e1s" + } } } } diff --git a/homeassistant/components/doorbird/translations/id.json b/homeassistant/components/doorbird/translations/id.json index c5d5733457d..e9155a8315f 100644 --- a/homeassistant/components/doorbird/translations/id.json +++ b/homeassistant/components/doorbird/translations/id.json @@ -18,8 +18,7 @@ "name": "Nama Perangkat", "password": "Kata Sandi", "username": "Nama Pengguna" - }, - "title": "Hubungkan ke DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Tambahkan nama event yang dipisahkan koma untuk setiap event yang ingin dilacak. Setelah memasukkannya di sini, gunakan aplikasi DoorBird untuk menetapkannya ke event tertentu.\n\nMisalnya: ada_yang_menekan_tombol, gerakan" - }, - "description": "Tambahkan nama event yang dipisahkan koma untuk setiap event yang ingin dilacak. Setelah memasukkannya di sini, gunakan aplikasi DoorBird untuk menetapkannya ke event tertentu. Baca dokumentasi di https://www.home-assistant.io/integrations/doorbird/#events. Contoh: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/it.json b/homeassistant/components/doorbird/translations/it.json index 9864f100954..4922ed6bc48 100644 --- a/homeassistant/components/doorbird/translations/it.json +++ b/homeassistant/components/doorbird/translations/it.json @@ -18,8 +18,7 @@ "name": "Nome del dispositivo", "password": "Password", "username": "Nome utente" - }, - "title": "Connettiti a DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Aggiungi un nome evento separato da virgole per ogni evento che desideri monitorare. Dopo averli inseriti qui, usa l'applicazione DoorBird per assegnarli a un evento specifico. \n\nEsempio: somebody_pressed_the_button, movimento" - }, - "description": "Aggiungere un nome di evento separato da virgola per ogni evento che desideri monitorare. Dopo averli inseriti qui, usa l'applicazione DoorBird per assegnarli a un evento specifico. Consulta la documentazione su https://www.home-assistant.io/integrations/doorbird/#events. Esempio: qualcuno_premuto_il_pulsante, movimento" + } } } } diff --git a/homeassistant/components/doorbird/translations/ja.json b/homeassistant/components/doorbird/translations/ja.json index 55363310ec5..e47c1b21e5e 100644 --- a/homeassistant/components/doorbird/translations/ja.json +++ b/homeassistant/components/doorbird/translations/ja.json @@ -18,8 +18,7 @@ "name": "\u30c7\u30d0\u30a4\u30b9\u540d", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "title": "DoorBird\u306b\u63a5\u7d9a" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "\u8ffd\u8de1\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u3054\u3068\u306b\u30b3\u30f3\u30de\u533a\u5207\u308a\u306e\u30a4\u30d9\u30f3\u30c8\u540d\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u3053\u3053\u306b\u5165\u529b\u5f8c\u3001DoorBird\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u3066\u7279\u5b9a\u306e\u30a4\u30d9\u30f3\u30c8\u306b\u5272\u308a\u5f53\u3066\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002 \n\n\u4f8b: somebody_pressed_the_button, motion" - }, - "description": "\u8ffd\u8de1\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u3054\u3068\u306b\u3001\u30b3\u30f3\u30de\u533a\u5207\u308a\u3067\u30a4\u30d9\u30f3\u30c8\u540d\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u3053\u3053\u306b\u5165\u529b\u3057\u305f\u5f8c\u3001DoorBird\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u3066\u7279\u5b9a\u306e\u30a4\u30d9\u30f3\u30c8\u306b\u5272\u308a\u5f53\u3066\u307e\u3059\u3002https://www.home-assistant.io/integrations/doorbird/#events. \u306e\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4f8b: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/ko.json b/homeassistant/components/doorbird/translations/ko.json index 85d00317c2d..7406536982e 100644 --- a/homeassistant/components/doorbird/translations/ko.json +++ b/homeassistant/components/doorbird/translations/ko.json @@ -18,8 +18,7 @@ "name": "\uae30\uae30 \uc774\ub984", "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - }, - "title": "DoorBird\uc5d0 \uc5f0\uacb0\ud558\uae30" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "\uc27c\ud45c\ub85c \uad6c\ubd84\ub41c \uc774\ubca4\ud2b8 \ubaa9\ub85d." - }, - "description": "\ucd94\uc801\ud558\ub824\ub294 \uac01 \uc774\ubca4\ud2b8\uc5d0 \ub300\ud574 \uc27c\ud45c\ub85c \uad6c\ubd84\ub41c \uc774\ubca4\ud2b8 \uc774\ub984\uc744 \ucd94\uac00\ud574\uc8fc\uc138\uc694. \uc5ec\uae30\uc5d0 \uc785\ub825\ud55c \ud6c4 DoorBird \uc571\uc744 \uc0ac\uc6a9\ud558\uc5ec \ud2b9\uc815 \uc774\ubca4\ud2b8\uc5d0 \ud560\ub2f9\ud574\uc8fc\uc138\uc694. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 https://www.home-assistant.io/integrations/doorbird/#event \uc124\uba85\uc11c\ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694. \uc608: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/lb.json b/homeassistant/components/doorbird/translations/lb.json index c7cb7234fea..1e10d04ba19 100644 --- a/homeassistant/components/doorbird/translations/lb.json +++ b/homeassistant/components/doorbird/translations/lb.json @@ -18,8 +18,7 @@ "name": "Numm vum Apparat", "password": "Passwuert", "username": "Benotzernumm" - }, - "title": "Mat DoorBird verbannen" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "Komma getrennte L\u00ebscht vun Evenementer" - }, - "description": "Setzt ee mat Komma getrennten Evenement Numm fir all Evenement dob\u00e4i d\u00e9i sollt suiv\u00e9iert ginn. Wann's du se hei aginn hues, benotz d'DoorBird App fir se zu engem spezifeschen Evenement dob\u00e4i ze setzen. Kuckt d'Dokumentatioun op https://www.home-assistant.io/integrations/doorbird/#events. Beispill: somebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/doorbird/translations/nl.json b/homeassistant/components/doorbird/translations/nl.json index 83e47ca4c5e..cc59cd156b4 100644 --- a/homeassistant/components/doorbird/translations/nl.json +++ b/homeassistant/components/doorbird/translations/nl.json @@ -18,8 +18,7 @@ "name": "Apparaatnaam", "password": "Wachtwoord", "username": "Gebruikersnaam" - }, - "title": "Maak verbinding met de DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Voeg een door komma's gescheiden evenementnaam toe voor elk evenement dat u wilt volgen. Nadat u ze hier hebt ingevoerd, gebruikt u de DoorBird app om ze aan een specifieke gebeurtenis toe te wijzen.\n\nVoorbeeld: iemand_drukt_op_de_knop, beweging" - }, - "description": "Voeg een door komma's gescheiden evenementnaam toe voor elk evenement dat u wilt volgen. Nadat je ze hier hebt ingevoerd, gebruik je de DoorBird-app om ze toe te wijzen aan een specifiek evenement. Zie de documentatie op https://www.home-assistant.io/integrations/doorbird/#events. Voorbeeld: iemand_drukte_knop, beweging" + } } } } diff --git a/homeassistant/components/doorbird/translations/no.json b/homeassistant/components/doorbird/translations/no.json index a30fc93013a..0c1732b64c6 100644 --- a/homeassistant/components/doorbird/translations/no.json +++ b/homeassistant/components/doorbird/translations/no.json @@ -18,8 +18,7 @@ "name": "Enhetsnavn", "password": "Passord", "username": "Brukernavn" - }, - "title": "Koble til DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Legg til et navn p\u00e5 en kommadelt hendelse for hver hendelse du vil spore. N\u00e5r du har skrevet dem inn her, bruker du DoorBird-appen til \u00e5 tilordne dem til en bestemt hendelse.\n\nEksempel: somebody_pressed_the_button, bevegelse" - }, - "description": "Legg til et kommaseparert hendelsesnavn for hvert arrangement du \u00f8nsker \u00e5 spore. Etter \u00e5 ha skrevet dem inn her, bruker du DoorBird-appen til \u00e5 tilordne dem til en bestemt hendelse. Se dokumentasjonen p\u00e5 https://www.home-assistant.io/integrations/doorbird/#events. Eksempel: noen_trykket_knappen, bevegelse" + } } } } diff --git a/homeassistant/components/doorbird/translations/pl.json b/homeassistant/components/doorbird/translations/pl.json index 85a7a8765a7..f8731e1bb04 100644 --- a/homeassistant/components/doorbird/translations/pl.json +++ b/homeassistant/components/doorbird/translations/pl.json @@ -18,8 +18,7 @@ "name": "Nazwa urz\u0105dzenia", "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" - }, - "title": "Po\u0142\u0105czenie z DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Dodaj nazw\u0119 oddzielon\u0105 przecinkami dla ka\u017cdego wydarzenia, kt\u00f3re chcesz \u015bledzi\u0107. Po wprowadzeniu ich tutaj u\u017cyj aplikacji DoorBird, aby przypisa\u0107 je do konkretnego wydarzenia. \n\nPrzyk\u0142ad: kto\u015b_nacisn\u0105\u0142_przycisk, ruch" - }, - "description": "Dodaj nazwy wydarze\u0144 oddzielonych przecinkami, kt\u00f3re chcesz \u015bledzi\u0107. Po wprowadzeniu ich tutaj u\u017cyj aplikacji DoorBird, aby przypisa\u0107 je do okre\u015blonych zdarze\u0144. Zapoznaj si\u0119 z dokumentacj\u0105 na stronie https://www.home-assistant.io/integrations/doorbird/#events.\nPrzyk\u0142ad: nacisniecie_przycisku, ruch" + } } } } diff --git a/homeassistant/components/doorbird/translations/pt-BR.json b/homeassistant/components/doorbird/translations/pt-BR.json index 07170c086b7..7265638b43b 100644 --- a/homeassistant/components/doorbird/translations/pt-BR.json +++ b/homeassistant/components/doorbird/translations/pt-BR.json @@ -18,8 +18,7 @@ "name": "Nome do dispositivo", "password": "Senha", "username": "Usu\u00e1rio" - }, - "title": "Conecte-se ao DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "Adicione um nome de evento separado por v\u00edrgula para cada evento que voc\u00ea deseja rastrear. Depois de inseri-los aqui, use o aplicativo DoorBird para atribu\u00ed-los a um evento espec\u00edfico. \n\n Exemplo: someone_pressed_the_button, movimento" - }, - "description": "Adicione um nome de evento separado por v\u00edrgula para cada evento que voc\u00ea deseja rastrear. Depois de inseri-los aqui, use o aplicativo DoorBird para atribu\u00ed-los a um evento espec\u00edfico. Consulte a documenta\u00e7\u00e3o em https://www.home-assistant.io/integrations/doorbird/#events. Exemplo: alguem_pressionou_o_botao movimento" + } } } } diff --git a/homeassistant/components/doorbird/translations/ru.json b/homeassistant/components/doorbird/translations/ru.json index 045092a2b97..be790392f50 100644 --- a/homeassistant/components/doorbird/translations/ru.json +++ b/homeassistant/components/doorbird/translations/ru.json @@ -18,8 +18,7 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 DoorBird, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044e.\n\n\u041f\u0440\u0438\u043c\u0435\u0440: somebody_pressed_the_button, motion." - }, - "description": "\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 DoorBird, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0438\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u044e. \u041f\u0440\u0438\u043c\u0435\u0440: somebody_pressed_the_button, motion. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438: https://www.home-assistant.io/integrations/doorbird/#events." + } } } } diff --git a/homeassistant/components/doorbird/translations/sl.json b/homeassistant/components/doorbird/translations/sl.json index 336a40904d2..446445a2b31 100644 --- a/homeassistant/components/doorbird/translations/sl.json +++ b/homeassistant/components/doorbird/translations/sl.json @@ -18,8 +18,7 @@ "name": "Ime naprave", "password": "Geslo", "username": "Uporabni\u0161ko ime" - }, - "title": "Pove\u017eite se z DoorBird" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "Seznam dogodkov, lo\u010denih z vejico." - }, - "description": "Za vsak dogodek, ki ga \u017eelite spremljati, dodajte ime, lo\u010deno z vejico. Ko jih tukaj vnesete, uporabite aplikacijo DoorBird, da jih dodelite dolo\u010denemu dogodku. Glej dokumentacijo na strani https://www.home-assistant.io/integrations/doorbird/#events. Primer: nekdo_pritisnil_gumb, gibanje" + } } } } diff --git a/homeassistant/components/doorbird/translations/sv.json b/homeassistant/components/doorbird/translations/sv.json index 546535fb937..b2a809a576e 100644 --- a/homeassistant/components/doorbird/translations/sv.json +++ b/homeassistant/components/doorbird/translations/sv.json @@ -9,8 +9,7 @@ "host": "V\u00e4rd (IP-adress)", "name": "Enhetsnamn", "username": "Anv\u00e4ndarnamn" - }, - "title": "Anslut till DoorBird" + } } } } diff --git a/homeassistant/components/doorbird/translations/tr.json b/homeassistant/components/doorbird/translations/tr.json index 1e472a43535..bffb5aacf82 100644 --- a/homeassistant/components/doorbird/translations/tr.json +++ b/homeassistant/components/doorbird/translations/tr.json @@ -18,8 +18,7 @@ "name": "Cihaz ad\u0131", "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "title": "DoorBird'e ba\u011flan\u0131n" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "\u0130zlemek istedi\u011finiz her etkinlik i\u00e7in virg\u00fclle ayr\u0131lm\u0131\u015f bir etkinlik ad\u0131 ekleyin. Bunlar\u0131 buraya girdikten sonra, onlar\u0131 belirli bir etkinli\u011fe atamak i\u00e7in DoorBird uygulamas\u0131n\u0131 kullan\u0131n. \n\n \u00d6rnek: birisi_butona_basti, hareket" - }, - "description": "\u0130zlemek istedi\u011finiz her etkinlik i\u00e7in virg\u00fclle ayr\u0131lm\u0131\u015f bir etkinlik ad\u0131 ekleyin. Bunlar\u0131 buraya girdikten sonra, onlar\u0131 belirli bir etkinli\u011fe atamak i\u00e7in DoorBird uygulamas\u0131n\u0131 kullan\u0131n. https://www.home-assistant.io/integrations/doorbird/#events adresindeki belgelere bak\u0131n. \u00d6rnek: birisi_pressed_the_button, hareket" + } } } } diff --git a/homeassistant/components/doorbird/translations/uk.json b/homeassistant/components/doorbird/translations/uk.json index 07bbdfacafe..85a7959abdf 100644 --- a/homeassistant/components/doorbird/translations/uk.json +++ b/homeassistant/components/doorbird/translations/uk.json @@ -18,8 +18,7 @@ "name": "\u041d\u0430\u0437\u0432\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - }, - "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e DoorBird" + } } } }, @@ -28,8 +27,7 @@ "init": { "data": { "events": "\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0434\u0456\u0439 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u0443." - }, - "description": "\u0414\u043e\u0434\u0430\u0439\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043c\u0443 \u043d\u0430\u0437\u0432\u0438 \u043f\u043e\u0434\u0456\u0439, \u044f\u043a\u0435 \u0412\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u0432\u0456\u0434\u0441\u043b\u0456\u0434\u043a\u043e\u0432\u0443\u0432\u0430\u0442\u0438. \u041f\u0456\u0441\u043b\u044f \u0446\u044c\u043e\u0433\u043e, \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u043e\u043a DoorBird, \u0449\u043e\u0431 \u043f\u0440\u0438\u0437\u043d\u0430\u0447\u0438\u0442\u0438 \u0457\u0445 \u0434\u043e \u043f\u0435\u0432\u043d\u043e\u0457 \u043f\u043e\u0434\u0456\u0457. \u041f\u0440\u0438\u043a\u043b\u0430\u0434: somebody_pressed_the_button, motion. \u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0454\u044e \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0431\u0456\u043b\u044c\u0448 \u0434\u043e\u043a\u043b\u0430\u0434\u043d\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457: https://www.home-assistant.io/integrations/doorbird/#events." + } } } } diff --git a/homeassistant/components/doorbird/translations/zh-Hant.json b/homeassistant/components/doorbird/translations/zh-Hant.json index 616e346b0dc..22bdf8b0c91 100644 --- a/homeassistant/components/doorbird/translations/zh-Hant.json +++ b/homeassistant/components/doorbird/translations/zh-Hant.json @@ -18,8 +18,7 @@ "name": "\u88dd\u7f6e\u540d\u7a31", "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "title": "\u9023\u7dda\u81f3 DoorBird" + } } } }, @@ -31,8 +30,7 @@ }, "data_description": { "events": "\u4ee5\u9017\u865f\u5206\u5225\u6240\u8981\u8ffd\u8e64\u7684\u4e8b\u4ef6\u540d\u7a31\u3002\u65bc\u6b64\u8f38\u5165\u5f8c\uff0c\u4f7f\u7528 DoorBird App \u6307\u5b9a\u81f3\u7279\u5b9a\u4e8b\u4ef6\u3002\n\n\u4f8b\u5982\uff1asomebody_pressed_the_button, motion" - }, - "description": "\u4ee5\u9017\u865f\u5206\u5225\u6240\u8981\u8ffd\u8e64\u7684\u4e8b\u4ef6\u540d\u7a31\u3002\u65bc\u6b64\u8f38\u5165\u5f8c\uff0c\u4f7f\u7528 DoorBird App \u6307\u5b9a\u81f3\u7279\u5b9a\u4e8b\u4ef6\u3002\u8acb\u53c3\u95b1\u6587\u4ef6\uff1ahttps://www.home-assistant.io/integrations/doorbird/#events\u3002\u4f8b\u5982\uff1asomebody_pressed_the_button, motion" + } } } } diff --git a/homeassistant/components/dunehd/translations/ca.json b/homeassistant/components/dunehd/translations/ca.json index 08dd841bf4f..e1da2dc867c 100644 --- a/homeassistant/components/dunehd/translations/ca.json +++ b/homeassistant/components/dunehd/translations/ca.json @@ -13,8 +13,7 @@ "data": { "host": "Amfitri\u00f3" }, - "description": "Assegura't que el reproductor est\u00e0 engegat.", - "title": "Dune HD" + "description": "Assegura't que el reproductor est\u00e0 engegat." } } } diff --git a/homeassistant/components/dunehd/translations/cs.json b/homeassistant/components/dunehd/translations/cs.json index b52eb3d2cdf..e0e097b925d 100644 --- a/homeassistant/components/dunehd/translations/cs.json +++ b/homeassistant/components/dunehd/translations/cs.json @@ -13,8 +13,7 @@ "data": { "host": "Hostitel" }, - "description": "Nastaven\u00ed integrace Dune HD. Pokud m\u00e1te probl\u00e9my s nastaven\u00edm, p\u0159ejd\u011bte na: https://www.home-assistant.io/integrations/dunehd \n\nUjist\u011bte se, \u017ee je v\u00e1\u0161 p\u0159ehr\u00e1va\u010d zapnut\u00fd.", - "title": "Dune HD" + "description": "Nastaven\u00ed integrace Dune HD. Pokud m\u00e1te probl\u00e9my s nastaven\u00edm, p\u0159ejd\u011bte na: https://www.home-assistant.io/integrations/dunehd \n\nUjist\u011bte se, \u017ee je v\u00e1\u0161 p\u0159ehr\u00e1va\u010d zapnut\u00fd." } } } diff --git a/homeassistant/components/dunehd/translations/de.json b/homeassistant/components/dunehd/translations/de.json index bcab02b2a09..58171e2fa23 100644 --- a/homeassistant/components/dunehd/translations/de.json +++ b/homeassistant/components/dunehd/translations/de.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Stelle sicher, dass dein Player eingeschaltet ist.", - "title": "Dune HD" + "description": "Stelle sicher, dass dein Player eingeschaltet ist." } } } diff --git a/homeassistant/components/dunehd/translations/el.json b/homeassistant/components/dunehd/translations/el.json index 96ad16ac67f..e14fb5c7308 100644 --- a/homeassistant/components/dunehd/translations/el.json +++ b/homeassistant/components/dunehd/translations/el.json @@ -13,8 +13,7 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Dune HD. \u0391\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://www.home-assistant.io/integrations/dunehd \n\n\u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7.", - "title": "Dune HD" + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Dune HD. \u0391\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c0\u03c1\u03bf\u03b2\u03bb\u03ae\u03bc\u03b1\u03c4\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c0\u03b7\u03b3\u03b1\u03af\u03bd\u03b5\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7: https://www.home-assistant.io/integrations/dunehd \n\n\u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7." } } } diff --git a/homeassistant/components/dunehd/translations/en.json b/homeassistant/components/dunehd/translations/en.json index d6392509fcf..6139d5b5251 100644 --- a/homeassistant/components/dunehd/translations/en.json +++ b/homeassistant/components/dunehd/translations/en.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Ensure that your player is turned on.", - "title": "Dune HD" + "description": "Ensure that your player is turned on." } } } diff --git a/homeassistant/components/dunehd/translations/es-419.json b/homeassistant/components/dunehd/translations/es-419.json index 5ad7a6640b4..d74cafdedde 100644 --- a/homeassistant/components/dunehd/translations/es-419.json +++ b/homeassistant/components/dunehd/translations/es-419.json @@ -2,8 +2,7 @@ "config": { "step": { "user": { - "description": "Configure la integraci\u00f3n de Dune HD. Si tiene problemas con la configuraci\u00f3n, vaya a: https://www.home-assistant.io/integrations/dunehd \n\n Aseg\u00farese de que su reproductor est\u00e9 encendido.", - "title": "Dune HD" + "description": "Configure la integraci\u00f3n de Dune HD. Si tiene problemas con la configuraci\u00f3n, vaya a: https://www.home-assistant.io/integrations/dunehd \n\n Aseg\u00farese de que su reproductor est\u00e9 encendido." } } } diff --git a/homeassistant/components/dunehd/translations/es.json b/homeassistant/components/dunehd/translations/es.json index 33f2e026359..7e2c1282ca6 100644 --- a/homeassistant/components/dunehd/translations/es.json +++ b/homeassistant/components/dunehd/translations/es.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Configura la integraci\u00f3n de Dune HD. Si tienes problemas con la configuraci\u00f3n, ve a: https://www.home-assistant.io/integrations/dunehd \n\n Aseg\u00farate de que tu reproductor est\u00e1 encendido.", - "title": "Dune HD" + "description": "Configura la integraci\u00f3n de Dune HD. Si tienes problemas con la configuraci\u00f3n, ve a: https://www.home-assistant.io/integrations/dunehd \n\n Aseg\u00farate de que tu reproductor est\u00e1 encendido." } } } diff --git a/homeassistant/components/dunehd/translations/et.json b/homeassistant/components/dunehd/translations/et.json index 3f4a9b4b085..9cd7d30bc2c 100644 --- a/homeassistant/components/dunehd/translations/et.json +++ b/homeassistant/components/dunehd/translations/et.json @@ -13,8 +13,7 @@ "data": { "host": "" }, - "description": "Veendu, et m\u00e4ngija on sisse l\u00fclitatud.", - "title": "" + "description": "Veendu, et m\u00e4ngija on sisse l\u00fclitatud." } } } diff --git a/homeassistant/components/dunehd/translations/fr.json b/homeassistant/components/dunehd/translations/fr.json index 8c3bc89a0e6..a8831b94f49 100644 --- a/homeassistant/components/dunehd/translations/fr.json +++ b/homeassistant/components/dunehd/translations/fr.json @@ -13,8 +13,7 @@ "data": { "host": "H\u00f4te" }, - "description": "Assurez-vous que votre lecteur est allum\u00e9.", - "title": "Dune HD" + "description": "Assurez-vous que votre lecteur est allum\u00e9." } } } diff --git a/homeassistant/components/dunehd/translations/hu.json b/homeassistant/components/dunehd/translations/hu.json index 1dc7c8ec6cc..66245b690fd 100644 --- a/homeassistant/components/dunehd/translations/hu.json +++ b/homeassistant/components/dunehd/translations/hu.json @@ -13,8 +13,7 @@ "data": { "host": "C\u00edm" }, - "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a lej\u00e1tsz\u00f3 be van kapcsolva.", - "title": "Dune HD" + "description": "Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l, hogy a lej\u00e1tsz\u00f3 be van kapcsolva." } } } diff --git a/homeassistant/components/dunehd/translations/id.json b/homeassistant/components/dunehd/translations/id.json index 5a86947a07a..0a0f90a5a0f 100644 --- a/homeassistant/components/dunehd/translations/id.json +++ b/homeassistant/components/dunehd/translations/id.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Pastikan pemutar Anda dinyalakan.", - "title": "Dune HD" + "description": "Pastikan pemutar Anda dinyalakan." } } } diff --git a/homeassistant/components/dunehd/translations/it.json b/homeassistant/components/dunehd/translations/it.json index 0e33feb2640..9ef4be284b6 100644 --- a/homeassistant/components/dunehd/translations/it.json +++ b/homeassistant/components/dunehd/translations/it.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Assicurati che il tuo lettore sia acceso.", - "title": "Dune HD" + "description": "Assicurati che il tuo lettore sia acceso." } } } diff --git a/homeassistant/components/dunehd/translations/ja.json b/homeassistant/components/dunehd/translations/ja.json index 79bb6b0746d..c82d7ce8f94 100644 --- a/homeassistant/components/dunehd/translations/ja.json +++ b/homeassistant/components/dunehd/translations/ja.json @@ -13,8 +13,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "Dune HD\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/dunehd \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\n\n\u304d\u3061\u3093\u3068\u30d7\u30ec\u30fc\u30e4\u30fc\u306e\u96fb\u6e90\u304c\u5165\u3063\u3066\u3044\u308b\u3053\u3068\u3082\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Dune HD" + "description": "Dune HD\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u95a2\u3057\u3066\u554f\u984c\u304c\u767a\u751f\u3057\u305f\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/dunehd \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\n\n\u304d\u3061\u3093\u3068\u30d7\u30ec\u30fc\u30e4\u30fc\u306e\u96fb\u6e90\u304c\u5165\u3063\u3066\u3044\u308b\u3053\u3068\u3082\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } } diff --git a/homeassistant/components/dunehd/translations/ko.json b/homeassistant/components/dunehd/translations/ko.json index 5c7feb27f7e..8bb3de70327 100644 --- a/homeassistant/components/dunehd/translations/ko.json +++ b/homeassistant/components/dunehd/translations/ko.json @@ -13,8 +13,7 @@ "data": { "host": "\ud638\uc2a4\ud2b8" }, - "description": "Dune HD \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694. \uad6c\uc131\uc5d0 \ubb38\uc81c\uac00 \uc788\ub294 \uacbd\uc6b0 https://www.home-assistant.io/integrations/dunehd \ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n\n\uae30\uae30\uac00 \ucf1c\uc838 \uc788\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694.", - "title": "Dune HD" + "description": "Dune HD \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud574\uc8fc\uc138\uc694. \uad6c\uc131\uc5d0 \ubb38\uc81c\uac00 \uc788\ub294 \uacbd\uc6b0 https://www.home-assistant.io/integrations/dunehd \ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n\n\uae30\uae30\uac00 \ucf1c\uc838 \uc788\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/dunehd/translations/lb.json b/homeassistant/components/dunehd/translations/lb.json index 035bd24f6b3..9dc5f1f2c5d 100644 --- a/homeassistant/components/dunehd/translations/lb.json +++ b/homeassistant/components/dunehd/translations/lb.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Dune HD Integratioun ariichten. Falls et Problemer mat der Konfiguratioun g\u00ebtt g\u00e9i op:\nhttps://www.home-assistant.io/integrations/dunehd \nStell s\u00e9cher dass d\u00e4in Ofspiller un ass.", - "title": "Dune HD" + "description": "Dune HD Integratioun ariichten. Falls et Problemer mat der Konfiguratioun g\u00ebtt g\u00e9i op:\nhttps://www.home-assistant.io/integrations/dunehd \nStell s\u00e9cher dass d\u00e4in Ofspiller un ass." } } } diff --git a/homeassistant/components/dunehd/translations/nl.json b/homeassistant/components/dunehd/translations/nl.json index f4c54a5dba5..3ee04091a1c 100644 --- a/homeassistant/components/dunehd/translations/nl.json +++ b/homeassistant/components/dunehd/translations/nl.json @@ -13,8 +13,7 @@ "data": { "host": "Host" }, - "description": "Zorg ervoor dat uw speler is ingeschakeld.", - "title": "Dune HD" + "description": "Zorg ervoor dat uw speler is ingeschakeld." } } } diff --git a/homeassistant/components/dunehd/translations/no.json b/homeassistant/components/dunehd/translations/no.json index 1b35f5581b1..ac38bbbdbf9 100644 --- a/homeassistant/components/dunehd/translations/no.json +++ b/homeassistant/components/dunehd/translations/no.json @@ -13,8 +13,7 @@ "data": { "host": "Vert" }, - "description": "S\u00f8rg for at spilleren er sl\u00e5tt p\u00e5.", - "title": "" + "description": "S\u00f8rg for at spilleren er sl\u00e5tt p\u00e5." } } } diff --git a/homeassistant/components/dunehd/translations/pl.json b/homeassistant/components/dunehd/translations/pl.json index 376b84e05d9..4af098c58f9 100644 --- a/homeassistant/components/dunehd/translations/pl.json +++ b/homeassistant/components/dunehd/translations/pl.json @@ -13,8 +13,7 @@ "data": { "host": "Nazwa hosta lub adres IP" }, - "description": "Upewnij si\u0119, \u017ce odtwarzacz jest w\u0142\u0105czony.", - "title": "Dune HD" + "description": "Upewnij si\u0119, \u017ce odtwarzacz jest w\u0142\u0105czony." } } } diff --git a/homeassistant/components/dunehd/translations/pt-BR.json b/homeassistant/components/dunehd/translations/pt-BR.json index 4775f1af379..a9c2f20a225 100644 --- a/homeassistant/components/dunehd/translations/pt-BR.json +++ b/homeassistant/components/dunehd/translations/pt-BR.json @@ -13,8 +13,7 @@ "data": { "host": "Nome do host" }, - "description": "Certifique-se de que seu player est\u00e1 ligado.", - "title": "Dune HD" + "description": "Certifique-se de que seu player est\u00e1 ligado." } } } diff --git a/homeassistant/components/dunehd/translations/ru.json b/homeassistant/components/dunehd/translations/ru.json index 134511e66f2..8c32af72af7 100644 --- a/homeassistant/components/dunehd/translations/ru.json +++ b/homeassistant/components/dunehd/translations/ru.json @@ -13,8 +13,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d.", - "title": "Dune HD" + "description": "\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d." } } } diff --git a/homeassistant/components/dunehd/translations/tr.json b/homeassistant/components/dunehd/translations/tr.json index 79c2f033cfc..f4c7c0db43a 100644 --- a/homeassistant/components/dunehd/translations/tr.json +++ b/homeassistant/components/dunehd/translations/tr.json @@ -13,8 +13,7 @@ "data": { "host": "Sunucu" }, - "description": "Oynat\u0131c\u0131n\u0131z\u0131n a\u00e7\u0131k oldu\u011fundan emin olun.", - "title": "Dune HD" + "description": "Oynat\u0131c\u0131n\u0131z\u0131n a\u00e7\u0131k oldu\u011fundan emin olun." } } } diff --git a/homeassistant/components/dunehd/translations/uk.json b/homeassistant/components/dunehd/translations/uk.json index d2f4eadbdcb..bec5590ecb5 100644 --- a/homeassistant/components/dunehd/translations/uk.json +++ b/homeassistant/components/dunehd/translations/uk.json @@ -13,8 +13,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Dune HD. \u042f\u043a\u0449\u043e \u0443 \u0412\u0430\u0441 \u0432\u0438\u043d\u0438\u043a\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0437 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u043c, \u043e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438 \u0437\u0430 \u0430\u0434\u0440\u0435\u0441\u043e\u044e: https://www.home-assistant.io/integrations/dunehd \n\n \u041f\u0435\u0440\u0435\u043a\u043e\u043d\u0430\u0439\u0442\u0435\u0441\u044f, \u0449\u043e \u0412\u0430\u0448 \u043f\u043b\u0435\u0454\u0440 \u0443\u0432\u0456\u043c\u043a\u043d\u0435\u043d\u0438\u0439.", - "title": "Dune HD" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Dune HD. \u042f\u043a\u0449\u043e \u0443 \u0412\u0430\u0441 \u0432\u0438\u043d\u0438\u043a\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0438 \u0437 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u043c, \u043e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438 \u0437\u0430 \u0430\u0434\u0440\u0435\u0441\u043e\u044e: https://www.home-assistant.io/integrations/dunehd \n\n \u041f\u0435\u0440\u0435\u043a\u043e\u043d\u0430\u0439\u0442\u0435\u0441\u044f, \u0449\u043e \u0412\u0430\u0448 \u043f\u043b\u0435\u0454\u0440 \u0443\u0432\u0456\u043c\u043a\u043d\u0435\u043d\u0438\u0439." } } } diff --git a/homeassistant/components/dunehd/translations/zh-Hant.json b/homeassistant/components/dunehd/translations/zh-Hant.json index 993a6d9a210..138b3801adf 100644 --- a/homeassistant/components/dunehd/translations/zh-Hant.json +++ b/homeassistant/components/dunehd/translations/zh-Hant.json @@ -13,8 +13,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef" }, - "description": "\u78ba\u5b9a\u64ad\u653e\u5668\u5df2\u7d93\u958b\u555f\u3002", - "title": "Dune HD" + "description": "\u78ba\u5b9a\u64ad\u653e\u5668\u5df2\u7d93\u958b\u555f\u3002" } } } diff --git a/homeassistant/components/efergy/translations/ca.json b/homeassistant/components/efergy/translations/ca.json index 298826e75e5..5b8a723019a 100644 --- a/homeassistant/components/efergy/translations/ca.json +++ b/homeassistant/components/efergy/translations/ca.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Clau API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/cs.json b/homeassistant/components/efergy/translations/cs.json index b2fe2ea015f..058abebbf28 100644 --- a/homeassistant/components/efergy/translations/cs.json +++ b/homeassistant/components/efergy/translations/cs.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API kl\u00ed\u010d" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/de.json b/homeassistant/components/efergy/translations/de.json index 3945d3da7d4..659657731f4 100644 --- a/homeassistant/components/efergy/translations/de.json +++ b/homeassistant/components/efergy/translations/de.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API-Schl\u00fcssel" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/el.json b/homeassistant/components/efergy/translations/el.json index f1206afd71b..8ce9b0ddd8c 100644 --- a/homeassistant/components/efergy/translations/el.json +++ b/homeassistant/components/efergy/translations/el.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/en.json b/homeassistant/components/efergy/translations/en.json index aa76f9c0636..0909241c423 100644 --- a/homeassistant/components/efergy/translations/en.json +++ b/homeassistant/components/efergy/translations/en.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API Key" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/es.json b/homeassistant/components/efergy/translations/es.json index a72347abef6..ce25d3f3184 100644 --- a/homeassistant/components/efergy/translations/es.json +++ b/homeassistant/components/efergy/translations/es.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Clave API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/et.json b/homeassistant/components/efergy/translations/et.json index 73c2f1a3547..c3f599934dc 100644 --- a/homeassistant/components/efergy/translations/et.json +++ b/homeassistant/components/efergy/translations/et.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API v\u00f5ti" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/fr.json b/homeassistant/components/efergy/translations/fr.json index a506454bc14..9bcf81e0cab 100644 --- a/homeassistant/components/efergy/translations/fr.json +++ b/homeassistant/components/efergy/translations/fr.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Cl\u00e9 d'API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/hu.json b/homeassistant/components/efergy/translations/hu.json index 032ef05d527..9d1f47f803c 100644 --- a/homeassistant/components/efergy/translations/hu.json +++ b/homeassistant/components/efergy/translations/hu.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API kulcs" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/id.json b/homeassistant/components/efergy/translations/id.json index 234e5122db2..037d72a0a15 100644 --- a/homeassistant/components/efergy/translations/id.json +++ b/homeassistant/components/efergy/translations/id.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Kunci API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/it.json b/homeassistant/components/efergy/translations/it.json index d5677424d42..df4b96223e5 100644 --- a/homeassistant/components/efergy/translations/it.json +++ b/homeassistant/components/efergy/translations/it.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Chiave API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/ja.json b/homeassistant/components/efergy/translations/ja.json index 98dd06ab4f0..319fbd744cf 100644 --- a/homeassistant/components/efergy/translations/ja.json +++ b/homeassistant/components/efergy/translations/ja.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API\u30ad\u30fc" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/nl.json b/homeassistant/components/efergy/translations/nl.json index 4f97bad11a0..0a51ce58a9c 100644 --- a/homeassistant/components/efergy/translations/nl.json +++ b/homeassistant/components/efergy/translations/nl.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API-sleutel" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/no.json b/homeassistant/components/efergy/translations/no.json index 388cbb36f64..4a109ab8fa9 100644 --- a/homeassistant/components/efergy/translations/no.json +++ b/homeassistant/components/efergy/translations/no.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API-n\u00f8kkel" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/pl.json b/homeassistant/components/efergy/translations/pl.json index b96038d24b3..9ef0e4a5a43 100644 --- a/homeassistant/components/efergy/translations/pl.json +++ b/homeassistant/components/efergy/translations/pl.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Klucz API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/pt-BR.json b/homeassistant/components/efergy/translations/pt-BR.json index 8197121b5d5..065c29ab9ab 100644 --- a/homeassistant/components/efergy/translations/pt-BR.json +++ b/homeassistant/components/efergy/translations/pt-BR.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "Chave da API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/ru.json b/homeassistant/components/efergy/translations/ru.json index 6a659c9b7c6..3c0e7797c7b 100644 --- a/homeassistant/components/efergy/translations/ru.json +++ b/homeassistant/components/efergy/translations/ru.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/tr.json b/homeassistant/components/efergy/translations/tr.json index e13f215b5fb..cbb81598338 100644 --- a/homeassistant/components/efergy/translations/tr.json +++ b/homeassistant/components/efergy/translations/tr.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API Anahtar\u0131" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/efergy/translations/zh-Hant.json b/homeassistant/components/efergy/translations/zh-Hant.json index c8f2d660c2f..dda28849714 100644 --- a/homeassistant/components/efergy/translations/zh-Hant.json +++ b/homeassistant/components/efergy/translations/zh-Hant.json @@ -13,8 +13,7 @@ "user": { "data": { "api_key": "API \u91d1\u9470" - }, - "title": "Efergy" + } } } } diff --git a/homeassistant/components/elmax/translations/bg.json b/homeassistant/components/elmax/translations/bg.json index babce99193f..aa6a3dc3ef5 100644 --- a/homeassistant/components/elmax/translations/bg.json +++ b/homeassistant/components/elmax/translations/bg.json @@ -4,12 +4,10 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "error": { - "bad_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "invalid_pin": "\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0435\u043d\u0438\u044f\u0442 \u041f\u0418\u041d \u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d", "network_error": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u043c\u0440\u0435\u0436\u043e\u0432\u0430 \u0433\u0440\u0435\u0448\u043a\u0430", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430", - "unknown_error": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u043d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { "panels": { diff --git a/homeassistant/components/elmax/translations/ca.json b/homeassistant/components/elmax/translations/ca.json index 74d9f72dce5..bac698a9656 100644 --- a/homeassistant/components/elmax/translations/ca.json +++ b/homeassistant/components/elmax/translations/ca.json @@ -4,13 +4,11 @@ "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { - "bad_auth": "Autenticaci\u00f3 inv\u00e0lida", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "invalid_pin": "El pin proporcionat no \u00e9s v\u00e0lid", "network_error": "S'ha produ\u00eft un error de xarxa", "no_panel_online": "No s'ha trobat cap panell de control d'Elmax en l\u00ednia.", - "unknown": "Error inesperat", - "unknown_error": "S'ha produ\u00eft un error desconegut" + "unknown": "Error inesperat" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nom del panell", "panel_pin": "Codi PIN" }, - "description": "Selecciona quin panell vols controlar amb aquesta integraci\u00f3. Tingues en compte que el panell ha d'estar ON per poder ser configurat.", - "title": "Selecci\u00f3 del panell" + "description": "Selecciona quin panell vols controlar amb aquesta integraci\u00f3. Tingues en compte que el panell ha d'estar ON per poder ser configurat." }, "user": { "data": { "password": "Contrasenya", "username": "Nom d'usuari" }, - "description": "Inicia sessi\u00f3 a Elmax Cloud amb les teves credencials", - "title": "Inici de sessi\u00f3" + "description": "Inicia sessi\u00f3 a Elmax Cloud amb les teves credencials" } } - }, - "title": "Configuraci\u00f3 d'Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/cs.json b/homeassistant/components/elmax/translations/cs.json index a3e919af352..6c534c1a8a6 100644 --- a/homeassistant/components/elmax/translations/cs.json +++ b/homeassistant/components/elmax/translations/cs.json @@ -1,13 +1,11 @@ { "config": { "error": { - "bad_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "invalid_pin": "Poskytnut\u00fd k\u00f3d PIN je neplatn\u00fd", "network_error": "Do\u0161lo k chyb\u011b s\u00edt\u011b", "no_panel_online": "Nebyl nalezen \u017e\u00e1dn\u00fd online ovl\u00e1dac\u00ed panel Elmax.", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba", - "unknown_error": "Vyskytla se neo\u010dek\u00e1van\u00e1 chyba" + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { "panels": { @@ -16,18 +14,15 @@ "panel_name": "N\u00e1zev panelu", "panel_pin": "PIN k\u00f3d" }, - "description": "Vyberte, kter\u00fd panel chcete touto integrac\u00ed ovl\u00e1dat. Vezm\u011bte pros\u00edm na v\u011bdom\u00ed, \u017ee panel mus\u00ed b\u00fdt zapnut\u00fd, aby mohl b\u00fdt nakonfigurov\u00e1n.", - "title": "V\u00fdb\u011br panelu" + "description": "Vyberte, kter\u00fd panel chcete touto integrac\u00ed ovl\u00e1dat. Vezm\u011bte pros\u00edm na v\u011bdom\u00ed, \u017ee panel mus\u00ed b\u00fdt zapnut\u00fd, aby mohl b\u00fdt nakonfigurov\u00e1n." }, "user": { "data": { "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" }, - "description": "P\u0159ihlaste se do cloudu Elmax pomoc\u00ed sv\u00fdch p\u0159ihla\u0161ovac\u00edch \u00fadaj\u016f", - "title": "P\u0159ihl\u00e1\u0161en\u00ed k \u00fa\u010dtu" + "description": "P\u0159ihlaste se do cloudu Elmax pomoc\u00ed sv\u00fdch p\u0159ihla\u0161ovac\u00edch \u00fadaj\u016f" } } - }, - "title": "Nastaven\u00ed Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/de.json b/homeassistant/components/elmax/translations/de.json index 241802e82d4..55e947d5bd0 100644 --- a/homeassistant/components/elmax/translations/de.json +++ b/homeassistant/components/elmax/translations/de.json @@ -4,13 +4,11 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { - "bad_auth": "Ung\u00fcltige Authentifizierung", "invalid_auth": "Ung\u00fcltige Authentifizierung", "invalid_pin": "Die angegebene Pin ist ung\u00fcltig", "network_error": "Ein Netzwerkfehler ist aufgetreten", "no_panel_online": "Es wurde kein Elmax-Bedienfeld gefunden, das online ist.", - "unknown": "Unerwarteter Fehler", - "unknown_error": "Ein unerwarteter Fehler ist aufgetreten" + "unknown": "Unerwarteter Fehler" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Panel-Name", "panel_pin": "PIN-Code" }, - "description": "W\u00e4hle die Zentrale aus, die du mit dieser Integration steuern m\u00f6chtest. Bitte beachte, dass die Zentrale eingeschaltet sein muss, damit sie konfiguriert werden kann.", - "title": "Panelauswahl" + "description": "W\u00e4hle die Zentrale aus, die du mit dieser Integration steuern m\u00f6chtest. Bitte beachte, dass die Zentrale eingeschaltet sein muss, damit sie konfiguriert werden kann." }, "user": { "data": { "password": "Passwort", "username": "Benutzername" }, - "description": "Bitte melde dich mit deinen Zugangsdaten bei der Elmax-Cloud an", - "title": "Konto-Anmeldung" + "description": "Bitte melde dich mit deinen Zugangsdaten bei der Elmax-Cloud an" } } - }, - "title": "Elmax Cloud-Einrichtung" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/el.json b/homeassistant/components/elmax/translations/el.json index 916ab585935..2b99f715ccc 100644 --- a/homeassistant/components/elmax/translations/el.json +++ b/homeassistant/components/elmax/translations/el.json @@ -4,13 +4,11 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { - "bad_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "invalid_pin": "\u03a4\u03bf \u03c0\u03b1\u03c1\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf pin \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf", "network_error": "\u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5", "no_panel_online": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc\u03c2 \u03c0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 Elmax.", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1", - "unknown_error": "\u03a0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ac\u03c3\u03c4\u03b7\u03ba\u03b5 \u03bc\u03b7 \u03b1\u03bd\u03b1\u03bc\u03b5\u03bd\u03cc\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c0\u03af\u03bd\u03b1\u03ba\u03b1", "panel_pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b8\u03ad\u03bb\u03b1\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03c4\u03b5 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7. \u03a3\u03b7\u03bc\u03b5\u03b9\u03ce\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03bf \u03c0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af.", - "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03af\u03bd\u03b1\u03ba\u03b1" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03af\u03bd\u03b1\u03ba\u03b1 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03b8\u03ad\u03bb\u03b1\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03c4\u03b5 \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7. \u03a3\u03b7\u03bc\u03b5\u03b9\u03ce\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03bf \u03c0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af." }, "user": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, - "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf cloud \u03c4\u03b7\u03c2 Elmax \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd" + "description": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf cloud \u03c4\u03b7\u03c2 Elmax \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2" } } - }, - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/en.json b/homeassistant/components/elmax/translations/en.json index 60da5d88dc3..dabd58fc941 100644 --- a/homeassistant/components/elmax/translations/en.json +++ b/homeassistant/components/elmax/translations/en.json @@ -4,13 +4,11 @@ "already_configured": "Device is already configured" }, "error": { - "bad_auth": "Invalid authentication", "invalid_auth": "Invalid authentication", "invalid_pin": "The provided pin is invalid", "network_error": "A network error occurred", "no_panel_online": "No online Elmax control panel was found.", - "unknown": "Unexpected error", - "unknown_error": "An unexpected error occurred" + "unknown": "Unexpected error" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Panel Name", "panel_pin": "PIN Code" }, - "description": "Select which panel you would like to control with this integration. Please note that the panel must be ON in order to be configured.", - "title": "Panel selection" + "description": "Select which panel you would like to control with this integration. Please note that the panel must be ON in order to be configured." }, "user": { "data": { "password": "Password", "username": "Username" }, - "description": "Please login to the Elmax cloud using your credentials", - "title": "Account Login" + "description": "Please login to the Elmax cloud using your credentials" } } - }, - "title": "Elmax Cloud Setup" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/es.json b/homeassistant/components/elmax/translations/es.json index 76d3d14aeab..a8130c70d7e 100644 --- a/homeassistant/components/elmax/translations/es.json +++ b/homeassistant/components/elmax/translations/es.json @@ -4,13 +4,11 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "bad_auth": "Autenticaci\u00f3n no v\u00e1lida", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "invalid_pin": "El pin proporcionado no es v\u00e1lido", "network_error": "Se ha producido un error de red", "no_panel_online": "No se encontr\u00f3 ning\u00fan panel de control de Elmax en l\u00ednea.", - "unknown": "Error inesperado", - "unknown_error": "Error inesperado" + "unknown": "Error inesperado" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nombre del panel", "panel_pin": "C\u00f3digo PIN" }, - "description": "Seleccione el panel que desea controlar con esta integraci\u00f3n. Tenga en cuenta que el panel debe estar activado para poder configurarlo.", - "title": "Selecci\u00f3n de panel" + "description": "Seleccione el panel que desea controlar con esta integraci\u00f3n. Tenga en cuenta que el panel debe estar activado para poder configurarlo." }, "user": { "data": { "password": "Contrase\u00f1a", "username": "Nombre de usuario" }, - "description": "Inicie sesi\u00f3n en Elmax Cloud con sus credenciales", - "title": "Inicio de sesi\u00f3n de cuenta" + "description": "Inicie sesi\u00f3n en Elmax Cloud con sus credenciales" } } - }, - "title": "Configuraci\u00f3n de Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/et.json b/homeassistant/components/elmax/translations/et.json index 593bd00713f..5db777a8666 100644 --- a/homeassistant/components/elmax/translations/et.json +++ b/homeassistant/components/elmax/translations/et.json @@ -4,13 +4,11 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { - "bad_auth": "Vigane autentimine", "invalid_auth": "Tuvastamine nurjus", "invalid_pin": "Sisestatud pin on kehtetu", "network_error": "Ilmnes v\u00f5rgut\u00f5rge", "no_panel_online": "V\u00f5rgus olevat Elmaxi juhtpaneeli ei leitud.", - "unknown": "Ootamatu t\u00f5rge", - "unknown_error": "Ilmnes ootamatu t\u00f5rge" + "unknown": "Ootamatu t\u00f5rge" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Paneeli nimi", "panel_pin": "PIN kood" }, - "description": "Vali millist paneeli soovid selle sidumisega juhtida. Pane t\u00e4hele, et paneel peab seadistamiseks olema SEES.", - "title": "Paneeli valik" + "description": "Vali millist paneeli soovid selle sidumisega juhtida. Pane t\u00e4hele, et paneel peab seadistamiseks olema SEES." }, "user": { "data": { "password": "Salas\u00f5na", "username": "Kasutajanimi" }, - "description": "Logi oma mandaate kasutades Elmaxi pilve sisse", - "title": "Kontole sisselogimine" + "description": "Logi oma mandaate kasutades Elmaxi pilve sisse" } } - }, - "title": "Elmaxi pilve seadistamine" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/fr.json b/homeassistant/components/elmax/translations/fr.json index 7fe57a8290f..8ca02f3232a 100644 --- a/homeassistant/components/elmax/translations/fr.json +++ b/homeassistant/components/elmax/translations/fr.json @@ -4,13 +4,11 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { - "bad_auth": "Authentification non valide", "invalid_auth": "Authentification non valide", "invalid_pin": "Le code PIN fourni n\u2019est pas valide", "network_error": "Une erreur r\u00e9seau s'est produite", "no_panel_online": "Aucun panneau de contr\u00f4le Elmax en ligne n'a \u00e9t\u00e9 trouv\u00e9.", - "unknown": "Erreur inattendue", - "unknown_error": "une erreur inattendue est apparue" + "unknown": "Erreur inattendue" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nom du panneau", "panel_pin": "Code PIN" }, - "description": "S\u00e9lectionnez le panneau que vous souhaitez contr\u00f4ler avec cette int\u00e9gration. Veuillez noter que le panneau doit \u00eatre allum\u00e9 pour \u00eatre configur\u00e9.", - "title": "S\u00e9lection du panneau" + "description": "S\u00e9lectionnez le panneau que vous souhaitez contr\u00f4ler avec cette int\u00e9gration. Veuillez noter que le panneau doit \u00eatre allum\u00e9 pour \u00eatre configur\u00e9." }, "user": { "data": { "password": "Mot de passe", "username": "Nom d'utilisateur" }, - "description": "Veuillez vous connecter au cloud Elmax en utilisant vos informations d'identification", - "title": "Connexion au compte" + "description": "Veuillez vous connecter au cloud Elmax en utilisant vos informations d'identification" } } - }, - "title": "Configuration d\u2019Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/hu.json b/homeassistant/components/elmax/translations/hu.json index eac087baf27..9a5c70beba8 100644 --- a/homeassistant/components/elmax/translations/hu.json +++ b/homeassistant/components/elmax/translations/hu.json @@ -4,13 +4,11 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { - "bad_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "invalid_pin": "A megadott PIN-k\u00f3d \u00e9rv\u00e9nytelen", "network_error": "H\u00e1l\u00f3zati hiba t\u00f6rt\u00e9nt", "no_panel_online": "Nem tal\u00e1lhat\u00f3 online Elmax vez\u00e9rl\u0151panel.", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt", - "unknown_error": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Panel neve", "panel_pin": "PIN-k\u00f3d" }, - "description": "V\u00e1lassza ki, hogy melyik panelt szeretn\u00e9 vez\u00e9relni ezzel az integr\u00e1ci\u00f3val. A panelnek bekapcsolt \u00e1llapotban kell lennie a konfigur\u00e1l\u00e1shoz.", - "title": "Panel kiv\u00e1laszt\u00e1sa" + "description": "V\u00e1lassza ki, hogy melyik panelt szeretn\u00e9 vez\u00e9relni ezzel az integr\u00e1ci\u00f3val. A panelnek bekapcsolt \u00e1llapotban kell lennie a konfigur\u00e1l\u00e1shoz." }, "user": { "data": { "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "K\u00e9rem, jelentkezzen be az Elmax felh\u0151be a hiteles\u00edt\u0151 adataival", - "title": "Fi\u00f3k bejelentkez\u00e9s" + "description": "K\u00e9rem, jelentkezzen be az Elmax felh\u0151be a hiteles\u00edt\u0151 adataival" } } - }, - "title": "Elmax Cloud be\u00e1ll\u00edt\u00e1sa" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/id.json b/homeassistant/components/elmax/translations/id.json index 1df5e47679d..9b12a0d166e 100644 --- a/homeassistant/components/elmax/translations/id.json +++ b/homeassistant/components/elmax/translations/id.json @@ -4,13 +4,11 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "bad_auth": "Autentikasi tidak valid", "invalid_auth": "Autentikasi tidak valid", "invalid_pin": "PIN yang diberikan tidak valid", "network_error": "Terjadi kesalahan jaringan", "no_panel_online": "Tidak ada panel kontrol Elmax online yang ditemukan.", - "unknown": "Kesalahan yang tidak diharapkan", - "unknown_error": "Terjadi kesalahan tak terduga" + "unknown": "Kesalahan yang tidak diharapkan" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nama Panel", "panel_pin": "Kode PIN" }, - "description": "Pilih panel mana yang ingin dikontrol dengan integrasi ini. Perhatikan bahwa panel harus AKTIF agar dapat dikonfigurasi.", - "title": "Pemilihan panel" + "description": "Pilih panel mana yang ingin dikontrol dengan integrasi ini. Perhatikan bahwa panel harus AKTIF agar dapat dikonfigurasi." }, "user": { "data": { "password": "Kata Sandi", "username": "Nama Pengguna" }, - "description": "Masuk ke cloud Elmax menggunakan kredensial Anda", - "title": "Akun Masuk" + "description": "Masuk ke cloud Elmax menggunakan kredensial Anda" } } - }, - "title": "Penyiapan Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/it.json b/homeassistant/components/elmax/translations/it.json index 07539c739bd..5db43bdb935 100644 --- a/homeassistant/components/elmax/translations/it.json +++ b/homeassistant/components/elmax/translations/it.json @@ -4,13 +4,11 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "bad_auth": "Autenticazione non valida", "invalid_auth": "Autenticazione non valida", "invalid_pin": "Il PIN fornito non \u00e8 valido", "network_error": "Si \u00e8 verificato un errore di rete", "no_panel_online": "Non \u00e8 stato trovato alcun pannello di controllo Elmax in linea.", - "unknown": "Errore imprevisto", - "unknown_error": "Si \u00e8 verificato un errore imprevisto" + "unknown": "Errore imprevisto" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nome del pannello", "panel_pin": "Codice PIN" }, - "description": "Seleziona quale pannello vuoi controllare con questa integrazione. Nota che il pannello deve essere acceso per essere configurato.", - "title": "Selezione del pannello" + "description": "Seleziona quale pannello vuoi controllare con questa integrazione. Nota che il pannello deve essere acceso per essere configurato." }, "user": { "data": { "password": "Password", "username": "Nome utente" }, - "description": "Esegui l'accesso al cloud di Elmax utilizzando le tue credenziali", - "title": "Accesso all'account" + "description": "Esegui l'accesso al cloud di Elmax utilizzando le tue credenziali" } } - }, - "title": "Configurazione di Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/ja.json b/homeassistant/components/elmax/translations/ja.json index ffed5307921..8730ae4791b 100644 --- a/homeassistant/components/elmax/translations/ja.json +++ b/homeassistant/components/elmax/translations/ja.json @@ -4,13 +4,11 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { - "bad_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "invalid_pin": "\u63d0\u4f9b\u3055\u308c\u305f\u30d4\u30f3\u304c\u7121\u52b9\u3067\u3059", "network_error": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f", "no_panel_online": "\u30aa\u30f3\u30e9\u30a4\u30f3\u306eElmax\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d1\u30cd\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc", - "unknown_error": "\u4e88\u671f\u305b\u306c\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f" + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "\u30d1\u30cd\u30eb\u540d", "panel_pin": "PIN\u30b3\u30fc\u30c9" }, - "description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u5236\u5fa1\u3059\u308b\u30d1\u30cd\u30eb\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u3059\u308b\u306b\u306f\u3001\u30d1\u30cd\u30eb\u304c\u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30d1\u30cd\u30eb\u306e\u9078\u629e" + "description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u5236\u5fa1\u3059\u308b\u30d1\u30cd\u30eb\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u3059\u308b\u306b\u306f\u3001\u30d1\u30cd\u30eb\u304c\u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u308b\u5fc5\u8981\u304c\u3042\u308b\u3053\u3068\u306b\u6ce8\u610f\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "user": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, - "description": "\u3042\u306a\u305f\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u4f7f\u7528\u3057\u3066\u3001Elmax\u30af\u30e9\u30a6\u30c9\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30a2\u30ab\u30a6\u30f3\u30c8 \u30ed\u30b0\u30a4\u30f3" + "description": "\u3042\u306a\u305f\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u4f7f\u7528\u3057\u3066\u3001Elmax\u30af\u30e9\u30a6\u30c9\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } - }, - "title": "Elmax Cloud\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/lt.json b/homeassistant/components/elmax/translations/lt.json index d59749831b1..955dc21bf1c 100644 --- a/homeassistant/components/elmax/translations/lt.json +++ b/homeassistant/components/elmax/translations/lt.json @@ -14,10 +14,8 @@ "password": "Slapta\u017eodis", "username": "Prisijungimo vardas" }, - "description": "Prisijunkite prie \"Elmax\" debesies naudodami savo prisijungimo duomenis", - "title": "Paskyros prisijungimas" + "description": "Prisijunkite prie \"Elmax\" debesies naudodami savo prisijungimo duomenis" } } - }, - "title": "Elmax Cloud nustatymai" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/nl.json b/homeassistant/components/elmax/translations/nl.json index ed82cbf82c4..0a282680ce6 100644 --- a/homeassistant/components/elmax/translations/nl.json +++ b/homeassistant/components/elmax/translations/nl.json @@ -4,13 +4,11 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "bad_auth": "Ongeldige authenticatie", "invalid_auth": "Ongeldige authenticatie", "invalid_pin": "De opgegeven pincode is ongeldig", "network_error": "Er is een netwerkfout opgetreden", "no_panel_online": "Er is geen online Elmax-controlepaneel gevonden.", - "unknown": "Onverwachte fout", - "unknown_error": "Een onbekende fout is opgetreden." + "unknown": "Onverwachte fout" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Paneelnaam:", "panel_pin": "PIN Code" }, - "description": "Selecteer welk paneel je wilt bedienen met deze integratie. Houd er rekening mee dat het paneel AAN moet staan om te kunnen worden geconfigureerd.", - "title": "Paneelselectie" + "description": "Selecteer welk paneel je wilt bedienen met deze integratie. Houd er rekening mee dat het paneel AAN moet staan om te kunnen worden geconfigureerd." }, "user": { "data": { "password": "Wachtwoord", "username": "Gebruikersnaam" }, - "description": "Log in op de Elmax-cloud met uw inloggegevens", - "title": "Account Login" + "description": "Log in op de Elmax-cloud met uw inloggegevens" } } - }, - "title": "Elmax Cloud Setup" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/no.json b/homeassistant/components/elmax/translations/no.json index 35d8bb409f5..ef806106848 100644 --- a/homeassistant/components/elmax/translations/no.json +++ b/homeassistant/components/elmax/translations/no.json @@ -4,13 +4,11 @@ "already_configured": "Enheten er allerede konfigurert" }, "error": { - "bad_auth": "Ugyldig godkjenning", "invalid_auth": "Ugyldig godkjenning", "invalid_pin": "Den angitte PIN-koden er ugyldig", "network_error": "Det oppstod en nettverksfeil", "no_panel_online": "Ingen online Elmax kontrollpanel ble funnet.", - "unknown": "Uventet feil", - "unknown_error": "Uventet feil" + "unknown": "Uventet feil" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Navn p\u00e5 panel", "panel_pin": "PIN kode" }, - "description": "Velg hvilket panel du vil kontrollere med denne integrasjonen. V\u00e6r oppmerksom p\u00e5 at panelet m\u00e5 v\u00e6re P\u00c5 for \u00e5 kunne konfigureres.", - "title": "Valg av panel" + "description": "Velg hvilket panel du vil kontrollere med denne integrasjonen. V\u00e6r oppmerksom p\u00e5 at panelet m\u00e5 v\u00e6re P\u00c5 for \u00e5 kunne konfigureres." }, "user": { "data": { "password": "Passord", "username": "Brukernavn" }, - "description": "Logg p\u00e5 Elmax-skyen ved \u00e5 bruke legitimasjonen din", - "title": "P\u00e5logging til konto" + "description": "Logg p\u00e5 Elmax-skyen ved \u00e5 bruke legitimasjonen din" } } - }, - "title": "Elmax Cloud Setup" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/pl.json b/homeassistant/components/elmax/translations/pl.json index d5776432f5f..a66a1e810cd 100644 --- a/homeassistant/components/elmax/translations/pl.json +++ b/homeassistant/components/elmax/translations/pl.json @@ -4,13 +4,11 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { - "bad_auth": "Niepoprawne uwierzytelnienie", "invalid_auth": "Niepoprawne uwierzytelnienie", "invalid_pin": "Podany kod PIN jest nieprawid\u0142owy", "network_error": "Wyst\u0105pi\u0142 b\u0142\u0105d sieci.", "no_panel_online": "Nie znaleziono panelu sterowania online Elmax.", - "unknown": "Nieoczekiwany b\u0142\u0105d", - "unknown_error": "Nieoczekiwany b\u0142\u0105d" + "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nazwa panelu", "panel_pin": "Kod PIN" }, - "description": "Wybierz panel, kt\u00f3rym chcesz sterowa\u0107 za pomoc\u0105 tej integracji. Nale\u017cy pami\u0119ta\u0107, \u017ce panel musi by\u0107 w\u0142\u0105czony, aby mo\u017cna by\u0142o go skonfigurowa\u0107.", - "title": "Wyb\u00f3r panelu" + "description": "Wybierz panel, kt\u00f3rym chcesz sterowa\u0107 za pomoc\u0105 tej integracji. Nale\u017cy pami\u0119ta\u0107, \u017ce panel musi by\u0107 w\u0142\u0105czony, aby mo\u017cna by\u0142o go skonfigurowa\u0107." }, "user": { "data": { "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, - "description": "Prosz\u0119 zalogowa\u0107 si\u0119 do chmury Elmax za pomoc\u0105 swoich danych uwierzytelniaj\u0105cych", - "title": "Logowanie do konta" + "description": "Prosz\u0119 zalogowa\u0107 si\u0119 do chmury Elmax za pomoc\u0105 swoich danych uwierzytelniaj\u0105cych" } } - }, - "title": "Konfiguracja chmury Elmax" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/pt-BR.json b/homeassistant/components/elmax/translations/pt-BR.json index 9db13c83fbc..c68583bd431 100644 --- a/homeassistant/components/elmax/translations/pt-BR.json +++ b/homeassistant/components/elmax/translations/pt-BR.json @@ -4,13 +4,11 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "bad_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "invalid_pin": "O C\u00f3digo PIN fornecido \u00e9 inv\u00e1lido", "network_error": "Ocorreu um erro de rede", "no_panel_online": "Nenhum painel de controle on-line Elmax foi encontrado.", - "unknown": "Erro inesperado", - "unknown_error": "Erro inesperado" + "unknown": "Erro inesperado" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Nome do painel", "panel_pin": "C\u00f3digo PIN" }, - "description": "Selecione qual painel voc\u00ea gostaria de controlar com esta integra\u00e7\u00e3o. Observe que o painel deve estar LIGADO para ser configurado.", - "title": "Sele\u00e7\u00e3o do painel" + "description": "Selecione qual painel voc\u00ea gostaria de controlar com esta integra\u00e7\u00e3o. Observe que o painel deve estar LIGADO para ser configurado." }, "user": { "data": { "password": "Senha", "username": "Usu\u00e1rio" }, - "description": "Fa\u00e7a login na nuvem Elmax usando suas credenciais", - "title": "Login da conta" + "description": "Fa\u00e7a login na nuvem Elmax usando suas credenciais" } } - }, - "title": "Configura\u00e7\u00e3o de nuvem Elmax" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/ru.json b/homeassistant/components/elmax/translations/ru.json index d5b3483ad32..35dc047efbb 100644 --- a/homeassistant/components/elmax/translations/ru.json +++ b/homeassistant/components/elmax/translations/ru.json @@ -4,13 +4,11 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "bad_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "invalid_pin": "\u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 PIN-\u043a\u043e\u0434 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d.", "network_error": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 \u0432 \u0441\u0435\u0442\u0438.", "no_panel_online": "\u041f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Elmax \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430.", - "unknown_error": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u043d\u0435\u043b\u0438", "panel_pin": "PIN-\u043a\u043e\u0434" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u043d\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430.", - "title": "\u0412\u044b\u0431\u043e\u0440 \u043f\u0430\u043d\u0435\u043b\u0438" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u043d\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430." }, "user": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 Elmax Cloud, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0432\u043e\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.", - "title": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f" + "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 Elmax Cloud, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0432\u043e\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435." } } - }, - "title": "Elmax Cloud" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/sk.json b/homeassistant/components/elmax/translations/sk.json index ae37c2ed275..5ada995aa6e 100644 --- a/homeassistant/components/elmax/translations/sk.json +++ b/homeassistant/components/elmax/translations/sk.json @@ -1,8 +1,7 @@ { "config": { "error": { - "invalid_auth": "Neplatn\u00e9 overenie", - "unknown_error": "Vyskytla sa neo\u010dak\u00e1van\u00e1 chyba" + "invalid_auth": "Neplatn\u00e9 overenie" } } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/tr.json b/homeassistant/components/elmax/translations/tr.json index 5bcea9da388..dc43e42ab14 100644 --- a/homeassistant/components/elmax/translations/tr.json +++ b/homeassistant/components/elmax/translations/tr.json @@ -4,13 +4,11 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { - "bad_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "invalid_pin": "Sa\u011flanan pin ge\u00e7ersiz", "network_error": "Bir a\u011f hatas\u0131 olu\u015ftu", "no_panel_online": "\u00c7evrimi\u00e7i Elmax kontrol paneli bulunamad\u0131.", - "unknown": "Beklenmeyen hata", - "unknown_error": "Beklenmeyen bir hata olu\u015ftu" + "unknown": "Beklenmeyen hata" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "Panel Ad\u0131", "panel_pin": "PIN Kodu" }, - "description": "Bu entegrasyon ile hangi paneli kontrol etmek istedi\u011finizi se\u00e7in. L\u00fctfen konfig\u00fcre edilebilmesi i\u00e7in panelin A\u00c7IK olmas\u0131 gerekti\u011fini unutmay\u0131n.", - "title": "Panel se\u00e7imi" + "description": "Bu entegrasyon ile hangi paneli kontrol etmek istedi\u011finizi se\u00e7in. L\u00fctfen konfig\u00fcre edilebilmesi i\u00e7in panelin A\u00c7IK olmas\u0131 gerekti\u011fini unutmay\u0131n." }, "user": { "data": { "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "L\u00fctfen kimlik bilgilerinizi kullanarak Elmax bulutuna giri\u015f yap\u0131n", - "title": "Hesap Giri\u015fi" + "description": "L\u00fctfen kimlik bilgilerinizi kullanarak Elmax bulutuna giri\u015f yap\u0131n" } } - }, - "title": "Elmax Bulut Kurulumu" + } } \ No newline at end of file diff --git a/homeassistant/components/elmax/translations/zh-Hans.json b/homeassistant/components/elmax/translations/zh-Hans.json index 0d0c87c6a5b..b07f985a2c7 100644 --- a/homeassistant/components/elmax/translations/zh-Hans.json +++ b/homeassistant/components/elmax/translations/zh-Hans.json @@ -10,8 +10,7 @@ "data": { "password": "\u5bc6\u7801", "username": "\u7528\u6237\u540d" - }, - "title": "\u8d26\u6237\u767b\u5f55" + } } } } diff --git a/homeassistant/components/elmax/translations/zh-Hant.json b/homeassistant/components/elmax/translations/zh-Hant.json index b1fc8886d1c..c67fcddaa7a 100644 --- a/homeassistant/components/elmax/translations/zh-Hant.json +++ b/homeassistant/components/elmax/translations/zh-Hant.json @@ -4,13 +4,11 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "bad_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "invalid_pin": "\u6240\u63d0\u4f9b\u7684 PIN \u7121\u6548\u3002", "network_error": "\u767c\u751f\u7db2\u8def\u932f\u8aa4", "no_panel_online": "\u627e\u4e0d\u5230 Elmax \u63a7\u5236\u9762\u677f\u3002", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4", - "unknown_error": "\u767c\u751f\u672a\u77e5\u932f\u8aa4" + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { "panels": { @@ -19,18 +17,15 @@ "panel_name": "\u9762\u677f\u540d\u7a31", "panel_pin": "PIN \u78bc" }, - "description": "\u9078\u64c7\u6574\u5408\u6240\u8981\u4f7f\u7528\u7684\u9762\u677f\u3002\u8acb\u6ce8\u610f\u3001\u9762\u677f\u5fc5\u9808\u70ba\u958b\u555f\u72c0\u614b\u65b9\u80fd\u9032\u884c\u8a2d\u5b9a\u3002", - "title": "\u9078\u64c7\u9762\u677f" + "description": "\u9078\u64c7\u6574\u5408\u6240\u8981\u4f7f\u7528\u7684\u9762\u677f\u3002\u8acb\u6ce8\u610f\u3001\u9762\u677f\u5fc5\u9808\u70ba\u958b\u555f\u72c0\u614b\u65b9\u80fd\u9032\u884c\u8a2d\u5b9a\u3002" }, "user": { "data": { "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "\u8acb\u4f7f\u7528\u6191\u8b49\u4ee5\u767b\u5165 Elmax \u96f2\u670d\u52d9", - "title": "\u767b\u5165\u5e33\u865f" + "description": "\u8acb\u4f7f\u7528\u6191\u8b49\u4ee5\u767b\u5165 Elmax \u96f2\u670d\u52d9" } } - }, - "title": "Elmax \u96f2\u670d\u52d9\u8a2d\u5b9a" + } } \ No newline at end of file diff --git a/homeassistant/components/fan/translations/ca.json b/homeassistant/components/fan/translations/ca.json index 206933b1c9a..c253be819c9 100644 --- a/homeassistant/components/fan/translations/ca.json +++ b/homeassistant/components/fan/translations/ca.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} s'enc\u00e9n o s'apaga", - "toggled": "{entity_name} s'enc\u00e9n o s'apaga", "turned_off": "{entity_name} s'ha apagat", "turned_on": "{entity_name} s'ha enc\u00e8s" } diff --git a/homeassistant/components/fan/translations/cs.json b/homeassistant/components/fan/translations/cs.json index 98336d5cfbe..9a2f89e5caa 100644 --- a/homeassistant/components/fan/translations/cs.json +++ b/homeassistant/components/fan/translations/cs.json @@ -1,6 +1,7 @@ { "device_automation": { "action_type": { + "toggle": "P\u0159epnout {entity_name}", "turn_off": "Vypnout {entity_name}", "turn_on": "Zapnout {entity_name}" }, @@ -9,7 +10,6 @@ "is_on": "{entity_name} je zapnuto" }, "trigger_type": { - "toggled": "{entity_name} zapnuto nebo vypnuto", "turned_off": "{entity_name} bylo vypnuto", "turned_on": "{entity_name} bylo zapnuto" } diff --git a/homeassistant/components/fan/translations/de.json b/homeassistant/components/fan/translations/de.json index 5048a576bee..6f27d79ad81 100644 --- a/homeassistant/components/fan/translations/de.json +++ b/homeassistant/components/fan/translations/de.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ein- oder ausgeschaltet", - "toggled": "{entity_name} ein- oder ausgeschaltet", "turned_off": "{entity_name} ausgeschaltet", "turned_on": "{entity_name} eingeschaltet" } diff --git a/homeassistant/components/fan/translations/el.json b/homeassistant/components/fan/translations/el.json index b0ff671f737..78e78722bbc 100644 --- a/homeassistant/components/fan/translations/el.json +++ b/homeassistant/components/fan/translations/el.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "\u03a4\u03bf {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", - "toggled": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_off": "{entity_name} \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_on": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5" } diff --git a/homeassistant/components/fan/translations/en.json b/homeassistant/components/fan/translations/en.json index 55feb3dad9f..94335a91799 100644 --- a/homeassistant/components/fan/translations/en.json +++ b/homeassistant/components/fan/translations/en.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} turned on or off", - "toggled": "{entity_name} turned on or off", "turned_off": "{entity_name} turned off", "turned_on": "{entity_name} turned on" } diff --git a/homeassistant/components/fan/translations/es.json b/homeassistant/components/fan/translations/es.json index 85cc347f82d..b66ca0b0256 100644 --- a/homeassistant/components/fan/translations/es.json +++ b/homeassistant/components/fan/translations/es.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} activado o desactivado", - "toggled": "{entity_name} activado o desactivado", "turned_off": "{entity_name} desactivado", "turned_on": "{entity_name} activado" } diff --git a/homeassistant/components/fan/translations/et.json b/homeassistant/components/fan/translations/et.json index d168ca75158..23066054d37 100644 --- a/homeassistant/components/fan/translations/et.json +++ b/homeassistant/components/fan/translations/et.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", - "toggled": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "turned_off": "{entity_name} l\u00fclitus v\u00e4lja", "turned_on": "{entity_name} l\u00fclitus sisse" } diff --git a/homeassistant/components/fan/translations/fr.json b/homeassistant/components/fan/translations/fr.json index 1ff0ed5eea9..27615a525dd 100644 --- a/homeassistant/components/fan/translations/fr.json +++ b/homeassistant/components/fan/translations/fr.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} allum\u00e9 ou \u00e9teint", - "toggled": "{entity_name} activ\u00e9 ou d\u00e9sactiv\u00e9", "turned_off": "{entity_name} est \u00e9teint", "turned_on": "{entity_name} allum\u00e9" } diff --git a/homeassistant/components/fan/translations/he.json b/homeassistant/components/fan/translations/he.json index a97b300060a..5d1a6aea1c3 100644 --- a/homeassistant/components/fan/translations/he.json +++ b/homeassistant/components/fan/translations/he.json @@ -10,7 +10,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", - "toggled": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", "turned_off": "{entity_name} \u05db\u05d5\u05d1\u05d4", "turned_on": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc" } diff --git a/homeassistant/components/fan/translations/hu.json b/homeassistant/components/fan/translations/hu.json index e7ef69d8800..626004a9f69 100644 --- a/homeassistant/components/fan/translations/hu.json +++ b/homeassistant/components/fan/translations/hu.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} be- vagy kikapcsolt", - "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" } diff --git a/homeassistant/components/fan/translations/id.json b/homeassistant/components/fan/translations/id.json index 85fa77539c3..b87c2b5a2bc 100644 --- a/homeassistant/components/fan/translations/id.json +++ b/homeassistant/components/fan/translations/id.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} diaktifkan atau dinonaktifkan", - "toggled": "{entity_name} diaktifkan atau dinonaktifkan", "turned_off": "{entity_name} dimatikan", "turned_on": "{entity_name} dinyalakan" } diff --git a/homeassistant/components/fan/translations/it.json b/homeassistant/components/fan/translations/it.json index c74f477ea73..af4aff2691d 100644 --- a/homeassistant/components/fan/translations/it.json +++ b/homeassistant/components/fan/translations/it.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} attivata o disattivata", - "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" } diff --git a/homeassistant/components/fan/translations/ja.json b/homeassistant/components/fan/translations/ja.json index 75d528404a2..e3fe8e8ba1a 100644 --- a/homeassistant/components/fan/translations/ja.json +++ b/homeassistant/components/fan/translations/ja.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", - "toggled": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_off": "{entity_name} \u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_on": "{entity_name} \u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u307e\u3059" } diff --git a/homeassistant/components/fan/translations/nl.json b/homeassistant/components/fan/translations/nl.json index bdc2f64195b..d24f439ff16 100644 --- a/homeassistant/components/fan/translations/nl.json +++ b/homeassistant/components/fan/translations/nl.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", - "toggled": "{entity_name} in- of uitgeschakeld", "turned_off": "{entity_name} uitgeschakeld", "turned_on": "{entity_name} ingeschakeld" } diff --git a/homeassistant/components/fan/translations/no.json b/homeassistant/components/fan/translations/no.json index d9fdf8c5ea5..0c3649ecbc5 100644 --- a/homeassistant/components/fan/translations/no.json +++ b/homeassistant/components/fan/translations/no.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} sl\u00e5tt p\u00e5 eller av", - "toggled": "{entity_name} sl\u00e5tt p\u00e5 eller av", "turned_off": "{entity_name} sl\u00e5tt av", "turned_on": "{entity_name} sl\u00e5tt p\u00e5" } diff --git a/homeassistant/components/fan/translations/pl.json b/homeassistant/components/fan/translations/pl.json index 9cfc6fa9b28..817dbdde339 100644 --- a/homeassistant/components/fan/translations/pl.json +++ b/homeassistant/components/fan/translations/pl.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", - "toggled": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "turned_off": "nast\u0105pi wy\u0142\u0105czenie {entity_name}", "turned_on": "nast\u0105pi w\u0142\u0105czenie {entity_name}" } diff --git a/homeassistant/components/fan/translations/pt-BR.json b/homeassistant/components/fan/translations/pt-BR.json index 14eafd36f40..8ebc5b2c73f 100644 --- a/homeassistant/components/fan/translations/pt-BR.json +++ b/homeassistant/components/fan/translations/pt-BR.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ligado ou desligado", - "toggled": "{entity_name} ligado ou desligado", "turned_off": "{entity_name} for desligado", "turned_on": "{entity_name} for ligado" } diff --git a/homeassistant/components/fan/translations/ru.json b/homeassistant/components/fan/translations/ru.json index 5a4571f2dad..fcf8b6ec4a5 100644 --- a/homeassistant/components/fan/translations/ru.json +++ b/homeassistant/components/fan/translations/ru.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", - "toggled": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_off": "{entity_name} \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_on": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f" } diff --git a/homeassistant/components/fan/translations/sv.json b/homeassistant/components/fan/translations/sv.json index fd1fec6cdfc..dd1aaad4052 100644 --- a/homeassistant/components/fan/translations/sv.json +++ b/homeassistant/components/fan/translations/sv.json @@ -9,7 +9,6 @@ "is_on": "{entity_name} \u00e4r p\u00e5" }, "trigger_type": { - "toggled": "{entity_name} slogs p\u00e5 eller av", "turned_off": "{entity_name} st\u00e4ngdes av", "turned_on": "{entity_name} aktiverades" } diff --git a/homeassistant/components/fan/translations/tr.json b/homeassistant/components/fan/translations/tr.json index b4f7a9b5291..5c06cddf5ee 100644 --- a/homeassistant/components/fan/translations/tr.json +++ b/homeassistant/components/fan/translations/tr.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", - "toggled": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "turned_off": "{entity_name} kapat\u0131ld\u0131", "turned_on": "{entity_name} a\u00e7\u0131ld\u0131" } diff --git a/homeassistant/components/fan/translations/zh-Hans.json b/homeassistant/components/fan/translations/zh-Hans.json index 082133913bc..13b6917f4ad 100644 --- a/homeassistant/components/fan/translations/zh-Hans.json +++ b/homeassistant/components/fan/translations/zh-Hans.json @@ -9,7 +9,6 @@ "is_on": "{entity_name} \u5df2\u5f00\u542f" }, "trigger_type": { - "toggled": "{entity_name} \u88ab\u6253\u5f00\u6216\u5173\u95ed", "turned_off": "{entity_name} \u88ab\u5173\u95ed", "turned_on": "{entity_name} \u88ab\u6253\u5f00" } diff --git a/homeassistant/components/fan/translations/zh-Hant.json b/homeassistant/components/fan/translations/zh-Hant.json index ba68f7c5158..8151ee76a77 100644 --- a/homeassistant/components/fan/translations/zh-Hant.json +++ b/homeassistant/components/fan/translations/zh-Hant.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", - "toggled": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f" } diff --git a/homeassistant/components/fivem/translations/ca.json b/homeassistant/components/fivem/translations/ca.json index 8e2a192a18c..ae322b4748c 100644 --- a/homeassistant/components/fivem/translations/ca.json +++ b/homeassistant/components/fivem/translations/ca.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3. Comprova l'amfitri\u00f3 i el port i torna-ho a provar. Assegurat que est\u00e0s utilitzant la versi\u00f3 del servidor FiveM m\u00e9s recent.", "invalid_game_name": "L'API del joc al qual est\u00e0s intentant connectar-te no \u00e9s d'un joc FiveM.", - "invalid_gamename": "L'API del joc al qual est\u00e0s intentant connectar-te no \u00e9s d'un joc FiveM.", "unknown_error": "Error inesperat" }, "step": { diff --git a/homeassistant/components/fivem/translations/de.json b/homeassistant/components/fivem/translations/de.json index 5c6852a126e..bb3b3439cbf 100644 --- a/homeassistant/components/fivem/translations/de.json +++ b/homeassistant/components/fivem/translations/de.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen. Bitte \u00fcberpr\u00fcfe den Host und den Port und versuche es erneut. Vergewissere dich auch, dass du den neuesten FiveM-Server verwendest.", "invalid_game_name": "Die API des Spiels, mit dem du dich verbinden willst, ist kein FiveM-Spiel.", - "invalid_gamename": "Die API des Spiels, mit dem du dich verbinden willst, ist kein FiveM-Spiel.", "unknown_error": "Unerwarteter Fehler" }, "step": { diff --git a/homeassistant/components/fivem/translations/el.json b/homeassistant/components/fivem/translations/el.json index 29e798361ff..2f038de5ba9 100644 --- a/homeassistant/components/fivem/translations/el.json +++ b/homeassistant/components/fivem/translations/el.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u0391\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03b8\u03cd\u03c1\u03b1 \u03ba\u03b1\u03b9 \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac. \u0392\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03b5\u03c0\u03af\u03c3\u03b7\u03c2 \u03cc\u03c4\u03b9 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03b9\u03bf \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae FiveM.", "invalid_game_name": "\u03a4\u03bf api \u03c4\u03bf\u03c5 \u03c0\u03b1\u03b9\u03c7\u03bd\u03b9\u03b4\u03b9\u03bf\u03cd \u03c3\u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 FiveM.", - "invalid_gamename": "\u03a4\u03bf api \u03c4\u03bf\u03c5 \u03c0\u03b1\u03b9\u03c7\u03bd\u03b9\u03b4\u03b9\u03bf\u03cd \u03c3\u03c4\u03bf \u03bf\u03c0\u03bf\u03af\u03bf \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 FiveM.", "unknown_error": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { diff --git a/homeassistant/components/fivem/translations/en.json b/homeassistant/components/fivem/translations/en.json index e07c0666e24..8c4f7a54156 100644 --- a/homeassistant/components/fivem/translations/en.json +++ b/homeassistant/components/fivem/translations/en.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Failed to connect. Please check the host and port and try again. Also ensure that you are running the latest FiveM server.", "invalid_game_name": "The api of the game you are trying to connect to is not a FiveM game.", - "invalid_gamename": "The api of the game you are trying to connect to is not a FiveM game.", "unknown_error": "Unexpected error" }, "step": { diff --git a/homeassistant/components/fivem/translations/es.json b/homeassistant/components/fivem/translations/es.json index 873119cf4b9..273435a4e5e 100644 --- a/homeassistant/components/fivem/translations/es.json +++ b/homeassistant/components/fivem/translations/es.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Error al conectarse. Compruebe el host y el puerto e int\u00e9ntelo de nuevo. Aseg\u00farese tambi\u00e9n de que est\u00e1 ejecutando el servidor FiveM m\u00e1s reciente.", "invalid_game_name": "La API del juego al que intentas conectarte no es un juego de FiveM.", - "invalid_gamename": "La API del juego al que intentas conectarte no es un juego de FiveM.", "unknown_error": "Error inesperado" }, "step": { diff --git a/homeassistant/components/fivem/translations/et.json b/homeassistant/components/fivem/translations/et.json index 0ca491bd232..49edde59e1f 100644 --- a/homeassistant/components/fivem/translations/et.json +++ b/homeassistant/components/fivem/translations/et.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u00dchendamine eba\u00f5nnestus. Kontrolli hosti ja porti ning proovi uuesti. Veendu, et kasutad uusimat FiveM-i serverit.", "invalid_game_name": "M\u00e4ngu API, millega proovid \u00fchendust luua, ei ole FiveM-m\u00e4ng.", - "invalid_gamename": "M\u00e4ngu API, millega proovid \u00fchendust luua, ei ole FiveM-m\u00e4ng.", "unknown_error": "Ootamatu t\u00f5rge" }, "step": { diff --git a/homeassistant/components/fivem/translations/fr.json b/homeassistant/components/fivem/translations/fr.json index 4cd16be65a3..be8254a74de 100644 --- a/homeassistant/components/fivem/translations/fr.json +++ b/homeassistant/components/fivem/translations/fr.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u00c9chec de connexion. Veuillez v\u00e9rifier l'h\u00f4te et le port et r\u00e9essayer. Assurez-vous \u00e9galement que vous utilisez le dernier serveur FiveM.", "invalid_game_name": "L'API du jeu auquel vous essayez de vous connecter n'est pas un jeu FiveM.", - "invalid_gamename": "L\u2019API du jeu auquel vous essayez de vous connecter n\u2019est pas un jeu FiveM.", "unknown_error": "Erreur inattendue" }, "step": { diff --git a/homeassistant/components/fivem/translations/hu.json b/homeassistant/components/fivem/translations/hu.json index 4e56dc3c17f..d29d0c9a802 100644 --- a/homeassistant/components/fivem/translations/hu.json +++ b/homeassistant/components/fivem/translations/hu.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Nem siker\u00fclt csatlakozni. K\u00e9rj\u00fck, ellen\u0151rizze a c\u00edmet \u00e9s a portot, \u00e9s pr\u00f3b\u00e1lja meg \u00fajra. Gy\u0151z\u0151dj\u00f6n meg arr\u00f3l is, hogy a leg\u00fajabb FiveM szervert futtatja.", "invalid_game_name": "A j\u00e1t\u00e9k API-ja, amelyhez csatlakozni pr\u00f3b\u00e1l, nem FiveM.", - "invalid_gamename": "A j\u00e1t\u00e9k API-ja, amelyhez csatlakozni pr\u00f3b\u00e1l, nem FiveM.", "unknown_error": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { diff --git a/homeassistant/components/fivem/translations/id.json b/homeassistant/components/fivem/translations/id.json index 3cf44f86f5d..95e9e4decde 100644 --- a/homeassistant/components/fivem/translations/id.json +++ b/homeassistant/components/fivem/translations/id.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Gagal terhubung ke server. Periksa host dan port lalu coba lagi. Pastikan juga Anda menjalankan server FiveM terbaru.", "invalid_game_name": "API dari permainan yang Anda coba hubungkan bukanlah game FiveM.", - "invalid_gamename": "API dari permainan yang Anda coba hubungkan bukanlah game FiveM.", "unknown_error": "Kesalahan yang tidak diharapkan" }, "step": { diff --git a/homeassistant/components/fivem/translations/it.json b/homeassistant/components/fivem/translations/it.json index 0128d1fbeca..e14e8492835 100644 --- a/homeassistant/components/fivem/translations/it.json +++ b/homeassistant/components/fivem/translations/it.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Connessione non riuscita. Controlla l'host e la porta e riprova. Assicurati inoltre di eseguire il server FiveM pi\u00f9 recente.", "invalid_game_name": "L'API del gioco a cui stai tentando di connetterti non \u00e8 un gioco FiveM.", - "invalid_gamename": "L'API del gioco a cui stai tentando di connetterti non \u00e8 un gioco FiveM.", "unknown_error": "Errore imprevisto" }, "step": { diff --git a/homeassistant/components/fivem/translations/ja.json b/homeassistant/components/fivem/translations/ja.json index eb398cccff4..6ac9af80f2c 100644 --- a/homeassistant/components/fivem/translations/ja.json +++ b/homeassistant/components/fivem/translations/ja.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30db\u30b9\u30c8\u3068\u30dd\u30fc\u30c8\u3092\u78ba\u8a8d\u3057\u3066\u3001\u3082\u3046\u4e00\u5ea6\u3084\u308a\u76f4\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u6700\u65b0\u306eFiveM\u30b5\u30fc\u30d0\u30fc\u3092\u5b9f\u884c\u3057\u3066\u3044\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "invalid_game_name": "\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u30b2\u30fc\u30e0\u306eAPI\u306f\u3001FiveM\u306e\u30b2\u30fc\u30e0\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002", - "invalid_gamename": "\u63a5\u7d9a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u308b\u30b2\u30fc\u30e0\u306eAPI\u306f\u3001FiveM\u306e\u30b2\u30fc\u30e0\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002", "unknown_error": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { diff --git a/homeassistant/components/fivem/translations/nl.json b/homeassistant/components/fivem/translations/nl.json index 599bcbc771e..cac4b7ed12f 100644 --- a/homeassistant/components/fivem/translations/nl.json +++ b/homeassistant/components/fivem/translations/nl.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Kan geen verbinding maken. Controleer de host en poort en probeer het opnieuw. Zorg er ook voor dat u de nieuwste FiveM-server gebruikt.", "invalid_game_name": "De api van het spel waarmee je probeert te verbinden is geen FiveM spel.", - "invalid_gamename": "De api van het spel waarmee je probeert te verbinden is geen FiveM spel.", "unknown_error": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/fivem/translations/no.json b/homeassistant/components/fivem/translations/no.json index ac292c10b64..b609f8ad64f 100644 --- a/homeassistant/components/fivem/translations/no.json +++ b/homeassistant/components/fivem/translations/no.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Tilkobling mislyktes. Kontroller verten og porten og pr\u00f8v igjen. S\u00f8rg ogs\u00e5 for at du kj\u00f8rer den nyeste FiveM-serveren.", "invalid_game_name": "API-et til spillet du pr\u00f8ver \u00e5 koble til er ikke et FiveM-spill.", - "invalid_gamename": "API-et til spillet du pr\u00f8ver \u00e5 koble til er ikke et FiveM-spill.", "unknown_error": "Uventet feil" }, "step": { diff --git a/homeassistant/components/fivem/translations/pl.json b/homeassistant/components/fivem/translations/pl.json index 420ebca7463..f60bd6c23df 100644 --- a/homeassistant/components/fivem/translations/pl.json +++ b/homeassistant/components/fivem/translations/pl.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia. Sprawd\u017a adres hosta oraz port i spr\u00f3buj ponownie. Upewnij si\u0119, \u017ce posiadasz najnowsz\u0105 wersj\u0119 serwera FiveM.", "invalid_game_name": "API gry, do kt\u00f3rej pr\u00f3bujesz si\u0119 po\u0142\u0105czy\u0107, nie jest gr\u0105 FiveM.", - "invalid_gamename": "API gry, do kt\u00f3rej pr\u00f3bujesz si\u0119 po\u0142\u0105czy\u0107, nie jest gr\u0105 FiveM.", "unknown_error": "Nieoczekiwany b\u0142\u0105d" }, "step": { diff --git a/homeassistant/components/fivem/translations/pt-BR.json b/homeassistant/components/fivem/translations/pt-BR.json index b576192718f..af5980a90f1 100644 --- a/homeassistant/components/fivem/translations/pt-BR.json +++ b/homeassistant/components/fivem/translations/pt-BR.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Falha ao se conectar. Verifique o host e a porta e tente novamente. Verifique tamb\u00e9m se voc\u00ea est\u00e1 executando o servidor FiveM mais recente.", "invalid_game_name": "A API do jogo ao qual voc\u00ea est\u00e1 tentando se conectar n\u00e3o \u00e9 um jogo FiveM.", - "invalid_gamename": "A API do jogo ao qual voc\u00ea est\u00e1 tentando se conectar n\u00e3o \u00e9 um jogo FiveM.", "unknown_error": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/fivem/translations/ru.json b/homeassistant/components/fivem/translations/ru.json index c6da81663ca..2f2d3d3be73 100644 --- a/homeassistant/components/fivem/translations/ru.json +++ b/homeassistant/components/fivem/translations/ru.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442\u0430 \u0438 \u043f\u043e\u0440\u0442 \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443. \u0422\u0430\u043a\u0436\u0435 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 FiveM.", "invalid_game_name": "API \u0438\u0433\u0440\u044b, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0412\u044b \u043f\u044b\u0442\u0430\u0435\u0442\u0435\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f, \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0433\u0440\u043e\u0439 FiveM.", - "invalid_gamename": "API \u0438\u0433\u0440\u044b, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0412\u044b \u043f\u044b\u0442\u0430\u0435\u0442\u0435\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f, \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0433\u0440\u043e\u0439 FiveM.", "unknown_error": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/fivem/translations/tr.json b/homeassistant/components/fivem/translations/tr.json index 46921dd33c0..f6bfc9cf274 100644 --- a/homeassistant/components/fivem/translations/tr.json +++ b/homeassistant/components/fivem/translations/tr.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "Ba\u011flanma hatas\u0131. L\u00fctfen ana bilgisayar\u0131 ve ba\u011flant\u0131 noktas\u0131n\u0131 kontrol edin ve tekrar deneyin. Ayr\u0131ca en son FiveM sunucusunu \u00e7al\u0131\u015ft\u0131rd\u0131\u011f\u0131n\u0131zdan emin olun.", "invalid_game_name": "Ba\u011flanmaya \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131z oyunun api'si bir FiveM oyunu de\u011fil.", - "invalid_gamename": "Ba\u011flanmaya \u00e7al\u0131\u015ft\u0131\u011f\u0131n\u0131z oyunun api'si bir FiveM oyunu de\u011fil.", "unknown_error": "Beklenmeyen hata" }, "step": { diff --git a/homeassistant/components/fivem/translations/zh-Hant.json b/homeassistant/components/fivem/translations/zh-Hant.json index 3b1527f7a35..a05ecfb0dd4 100644 --- a/homeassistant/components/fivem/translations/zh-Hant.json +++ b/homeassistant/components/fivem/translations/zh-Hant.json @@ -6,7 +6,6 @@ "error": { "cannot_connect": "\u4f3a\u670d\u5668\u9023\u7dda\u5931\u6557\u3002\u8acb\u6aa2\u67e5\u4e3b\u6a5f\u7aef\u8207\u901a\u8a0a\u57e0\u5f8c\u518d\u8a66\u4e00\u6b21\u3002\u53e6\u8acb\u78ba\u8a8d\u57f7\u884c\u6700\u65b0\u7248 FiveM \u4f3a\u670d\u5668\u3002", "invalid_game_name": "\u5617\u8a66\u9023\u7dda\u7684\u904a\u6232 API \u4e26\u975e FiveM \u904a\u6232\u3002", - "invalid_gamename": "\u5617\u8a66\u9023\u7dda\u7684\u904a\u6232 API \u4e26\u975e FiveM \u904a\u6232\u3002", "unknown_error": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { diff --git a/homeassistant/components/freebox/translations/ca.json b/homeassistant/components/freebox/translations/ca.json index 2568c3db0b4..e02ca372a3e 100644 --- a/homeassistant/components/freebox/translations/ca.json +++ b/homeassistant/components/freebox/translations/ca.json @@ -17,8 +17,7 @@ "data": { "host": "Amfitri\u00f3", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/cs.json b/homeassistant/components/freebox/translations/cs.json index 4c11d4b442b..0bb07fc6535 100644 --- a/homeassistant/components/freebox/translations/cs.json +++ b/homeassistant/components/freebox/translations/cs.json @@ -17,8 +17,7 @@ "data": { "host": "Hostitel", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/de.json b/homeassistant/components/freebox/translations/de.json index 50644a87982..864ce5f0c99 100644 --- a/homeassistant/components/freebox/translations/de.json +++ b/homeassistant/components/freebox/translations/de.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/el.json b/homeassistant/components/freebox/translations/el.json index e881230014b..60c9f807765 100644 --- a/homeassistant/components/freebox/translations/el.json +++ b/homeassistant/components/freebox/translations/el.json @@ -17,8 +17,7 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "port": "\u0398\u03cd\u03c1\u03b1" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/en.json b/homeassistant/components/freebox/translations/en.json index 539cfbcfe1e..f47fd810117 100644 --- a/homeassistant/components/freebox/translations/en.json +++ b/homeassistant/components/freebox/translations/en.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/es-419.json b/homeassistant/components/freebox/translations/es-419.json index 1c99bc8b472..c013a730ae8 100644 --- a/homeassistant/components/freebox/translations/es-419.json +++ b/homeassistant/components/freebox/translations/es-419.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Puerto" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/es.json b/homeassistant/components/freebox/translations/es.json index bbd691c9764..de176304d1f 100644 --- a/homeassistant/components/freebox/translations/es.json +++ b/homeassistant/components/freebox/translations/es.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Puerto" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/et.json b/homeassistant/components/freebox/translations/et.json index 4f0239350e3..88bd7260291 100644 --- a/homeassistant/components/freebox/translations/et.json +++ b/homeassistant/components/freebox/translations/et.json @@ -17,8 +17,7 @@ "data": { "host": "", "port": "" - }, - "title": "" + } } } } diff --git a/homeassistant/components/freebox/translations/fr.json b/homeassistant/components/freebox/translations/fr.json index 7b459ebc0ab..60289791408 100644 --- a/homeassistant/components/freebox/translations/fr.json +++ b/homeassistant/components/freebox/translations/fr.json @@ -17,8 +17,7 @@ "data": { "host": "H\u00f4te", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/hu.json b/homeassistant/components/freebox/translations/hu.json index 39cfe189449..5f35297b538 100644 --- a/homeassistant/components/freebox/translations/hu.json +++ b/homeassistant/components/freebox/translations/hu.json @@ -17,8 +17,7 @@ "data": { "host": "C\u00edm", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/id.json b/homeassistant/components/freebox/translations/id.json index b03ec248edb..85800e695dd 100644 --- a/homeassistant/components/freebox/translations/id.json +++ b/homeassistant/components/freebox/translations/id.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/it.json b/homeassistant/components/freebox/translations/it.json index f1978217547..7bf36f69ff4 100644 --- a/homeassistant/components/freebox/translations/it.json +++ b/homeassistant/components/freebox/translations/it.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Porta" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/ja.json b/homeassistant/components/freebox/translations/ja.json index fa11e1c4822..5034f2bcc72 100644 --- a/homeassistant/components/freebox/translations/ja.json +++ b/homeassistant/components/freebox/translations/ja.json @@ -17,8 +17,7 @@ "data": { "host": "\u30db\u30b9\u30c8", "port": "\u30dd\u30fc\u30c8" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/ko.json b/homeassistant/components/freebox/translations/ko.json index c3bb5a9bd40..ff8e6b95282 100644 --- a/homeassistant/components/freebox/translations/ko.json +++ b/homeassistant/components/freebox/translations/ko.json @@ -17,8 +17,7 @@ "data": { "host": "\ud638\uc2a4\ud2b8", "port": "\ud3ec\ud2b8" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/lb.json b/homeassistant/components/freebox/translations/lb.json index e2fdaf18352..6d6155d4f68 100644 --- a/homeassistant/components/freebox/translations/lb.json +++ b/homeassistant/components/freebox/translations/lb.json @@ -17,8 +17,7 @@ "data": { "host": "Apparat", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/nl.json b/homeassistant/components/freebox/translations/nl.json index 7fbd57dd6ff..2d3972a59af 100644 --- a/homeassistant/components/freebox/translations/nl.json +++ b/homeassistant/components/freebox/translations/nl.json @@ -17,8 +17,7 @@ "data": { "host": "Host", "port": "Poort" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/no.json b/homeassistant/components/freebox/translations/no.json index 69ef2af9c7f..7f157a5dc31 100644 --- a/homeassistant/components/freebox/translations/no.json +++ b/homeassistant/components/freebox/translations/no.json @@ -17,8 +17,7 @@ "data": { "host": "Vert", "port": "Port" - }, - "title": "" + } } } } diff --git a/homeassistant/components/freebox/translations/pl.json b/homeassistant/components/freebox/translations/pl.json index fe6aa517c88..c195a7f2a1f 100644 --- a/homeassistant/components/freebox/translations/pl.json +++ b/homeassistant/components/freebox/translations/pl.json @@ -17,8 +17,7 @@ "data": { "host": "Nazwa hosta lub adres IP", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/pt-BR.json b/homeassistant/components/freebox/translations/pt-BR.json index 021ab5c902a..1e8e4b60315 100644 --- a/homeassistant/components/freebox/translations/pt-BR.json +++ b/homeassistant/components/freebox/translations/pt-BR.json @@ -17,8 +17,7 @@ "data": { "host": "Nome do host", "port": "Porta" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/ru.json b/homeassistant/components/freebox/translations/ru.json index 4ec5b68516c..c1012dbc743 100644 --- a/homeassistant/components/freebox/translations/ru.json +++ b/homeassistant/components/freebox/translations/ru.json @@ -17,8 +17,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/sl.json b/homeassistant/components/freebox/translations/sl.json index a161bfa494e..4dc79b32256 100644 --- a/homeassistant/components/freebox/translations/sl.json +++ b/homeassistant/components/freebox/translations/sl.json @@ -17,8 +17,7 @@ "data": { "host": "Gostitelj", "port": "Vrata" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/tr.json b/homeassistant/components/freebox/translations/tr.json index 6690b2a5b23..cfd42fbdba5 100644 --- a/homeassistant/components/freebox/translations/tr.json +++ b/homeassistant/components/freebox/translations/tr.json @@ -17,8 +17,7 @@ "data": { "host": "Sunucu", "port": "Port" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/uk.json b/homeassistant/components/freebox/translations/uk.json index 8676c9164a1..8492d3b3eff 100644 --- a/homeassistant/components/freebox/translations/uk.json +++ b/homeassistant/components/freebox/translations/uk.json @@ -17,8 +17,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/freebox/translations/zh-Hant.json b/homeassistant/components/freebox/translations/zh-Hant.json index 6cf0a90f4c0..9f45cdb44d4 100644 --- a/homeassistant/components/freebox/translations/zh-Hant.json +++ b/homeassistant/components/freebox/translations/zh-Hant.json @@ -17,8 +17,7 @@ "data": { "host": "\u4e3b\u6a5f\u7aef", "port": "\u901a\u8a0a\u57e0" - }, - "title": "Freebox" + } } } } diff --git a/homeassistant/components/fritz/translations/bg.json b/homeassistant/components/fritz/translations/bg.json index 3fca53d1013..43f9aaf5357 100644 --- a/homeassistant/components/fritz/translations/bg.json +++ b/homeassistant/components/fritz/translations/bg.json @@ -4,11 +4,6 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { - "start_config": { - "data": { - "port": "\u041f\u043e\u0440\u0442" - } - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", diff --git a/homeassistant/components/fritz/translations/ca.json b/homeassistant/components/fritz/translations/ca.json index 04d5b14cac3..df78d9ec4b0 100644 --- a/homeassistant/components/fritz/translations/ca.json +++ b/homeassistant/components/fritz/translations/ca.json @@ -10,7 +10,6 @@ "already_configured": "El dispositiu ja est\u00e0 configurat", "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "cannot_connect": "Ha fallat la connexi\u00f3", - "connection_error": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "upnp_not_configured": "Falta la configuraci\u00f3 UPnP al dispositiu." }, @@ -32,16 +31,6 @@ "description": "Actualitza les credencials de FRITZ!Box Tools de: {host}.\n\nFRITZ!Box Tools no pot iniciar sessi\u00f3 a FRITZ!Box.", "title": "Actualitzant les credencials de FRITZ!Box Tools" }, - "start_config": { - "data": { - "host": "Amfitri\u00f3", - "password": "Contrasenya", - "port": "Port", - "username": "Nom d'usuari" - }, - "description": "Configura FRITZ!Box Tools per poder controlar FRITZ!Box.\nEl m\u00ednim necessari \u00e9s: nom d'usuari i contrasenya.", - "title": "Configuraci\u00f3 de FRITZ!Box Tools - obligatori" - }, "user": { "data": { "host": "Amfitri\u00f3", diff --git a/homeassistant/components/fritz/translations/cs.json b/homeassistant/components/fritz/translations/cs.json index 9afc3d85536..55326bb1803 100644 --- a/homeassistant/components/fritz/translations/cs.json +++ b/homeassistant/components/fritz/translations/cs.json @@ -8,7 +8,6 @@ "error": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", - "connection_error": "Nepoda\u0159ilo se p\u0159ipojit", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "step": { @@ -24,14 +23,6 @@ "username": "U\u017eivatelsk\u00e9 jm\u00e9no" } }, - "start_config": { - "data": { - "host": "Hostitel", - "password": "Heslo", - "port": "Port", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - } - }, "user": { "data": { "port": "Port" diff --git a/homeassistant/components/fritz/translations/de.json b/homeassistant/components/fritz/translations/de.json index d64845ba9b7..16d2be68adb 100644 --- a/homeassistant/components/fritz/translations/de.json +++ b/homeassistant/components/fritz/translations/de.json @@ -10,7 +10,6 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert", "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "cannot_connect": "Verbindung fehlgeschlagen", - "connection_error": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", "upnp_not_configured": "Fehlende UPnP-Einstellungen auf dem Ger\u00e4t." }, @@ -32,16 +31,6 @@ "description": "Aktualisiere die Anmeldeinformationen von FRITZ!Box Tools f\u00fcr: {host}. \n\nFRITZ!Box Tools kann sich nicht an deiner FRITZ!Box anmelden.", "title": "Aktualisieren der FRITZ!Box Tools - Anmeldeinformationen" }, - "start_config": { - "data": { - "host": "Host", - "password": "Passwort", - "port": "Port", - "username": "Benutzername" - }, - "description": "Einrichten der FRITZ!Box Tools zur Steuerung deiner FRITZ!Box.\nBen\u00f6tigt: Benutzername, Passwort.", - "title": "Setup FRITZ!Box Tools - obligatorisch" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/el.json b/homeassistant/components/fritz/translations/el.json index 6dfc67e08d6..0cbaa10ef20 100644 --- a/homeassistant/components/fritz/translations/el.json +++ b/homeassistant/components/fritz/translations/el.json @@ -10,7 +10,6 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "connection_error": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "upnp_not_configured": "\u039b\u03b5\u03af\u03c0\u03bf\u03c5\u03bd \u03bf\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 UPnP \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae." }, @@ -32,16 +31,6 @@ "description": "\u0395\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c4\u03bf\u03c5 FRITZ!Box Tools \u03b3\u03b9\u03b1: {host} . \n\n \u03a4\u03bf FRITZ!Box Tools \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03c4\u03bf FRITZ!Box \u03c3\u03b1\u03c2.", "title": "\u0395\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 FRITZ!Box Tools - \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1" }, - "start_config": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf FRITZ!Box Tools \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03c4\u03b5 \u03c4\u03bf FRITZ!Box \u03c3\u03b1\u03c2.\n \u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03bf: \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7, \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2.", - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 FRITZ!Box Tools - \u03c5\u03c0\u03bf\u03c7\u03c1\u03b5\u03c9\u03c4\u03b9\u03ba\u03cc" - }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", diff --git a/homeassistant/components/fritz/translations/en.json b/homeassistant/components/fritz/translations/en.json index 3ce31866c2f..e7ee1568684 100644 --- a/homeassistant/components/fritz/translations/en.json +++ b/homeassistant/components/fritz/translations/en.json @@ -10,7 +10,6 @@ "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "cannot_connect": "Failed to connect", - "connection_error": "Failed to connect", "invalid_auth": "Invalid authentication", "upnp_not_configured": "Missing UPnP settings on device." }, @@ -32,16 +31,6 @@ "description": "Update FRITZ!Box Tools credentials for: {host}.\n\nFRITZ!Box Tools is unable to log in to your FRITZ!Box.", "title": "Updating FRITZ!Box Tools - credentials" }, - "start_config": { - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username" - }, - "description": "Setup FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", - "title": "Setup FRITZ!Box Tools - mandatory" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/es.json b/homeassistant/components/fritz/translations/es.json index e8337dbef60..964db5b5325 100644 --- a/homeassistant/components/fritz/translations/es.json +++ b/homeassistant/components/fritz/translations/es.json @@ -10,7 +10,6 @@ "already_configured": "El dispositivo ya est\u00e1 configurado", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "cannot_connect": "No se pudo conectar", - "connection_error": "No se pudo conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" }, "flow_title": "FRITZ!Box Tools: {name}", @@ -31,16 +30,6 @@ "description": "Actualizar credenciales de FRITZ!Box Tools para: {host}.\n\n FRITZ!Box Tools no puede iniciar sesi\u00f3n en tu FRITZ!Box.", "title": "Actualizando FRITZ!Box Tools - credenciales" }, - "start_config": { - "data": { - "host": "Host", - "password": "Contrase\u00f1a", - "port": "Puerto", - "username": "Usuario" - }, - "description": "Configurar FRITZ!Box Tools para controlar tu FRITZ!Box.\nM\u00ednimo necesario: usuario, contrase\u00f1a.", - "title": "Configurar FRITZ!Box Tools - obligatorio" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/et.json b/homeassistant/components/fritz/translations/et.json index 89bf4a3b7c9..ac2c9dbfe40 100644 --- a/homeassistant/components/fritz/translations/et.json +++ b/homeassistant/components/fritz/translations/et.json @@ -10,7 +10,6 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud", "already_in_progress": "Seadistamine on juba k\u00e4ivitatud", "cannot_connect": "\u00dchendamine nurjus", - "connection_error": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", "upnp_not_configured": "Puuduvad seadme UPnP-seaded." }, @@ -32,16 +31,6 @@ "description": "V\u00e4rskenda FRITZ!Box Tools'i volitusi: {host}.\n\nFRITZ!Box Tools ei saa FRITZ!Boxi sisse logida.", "title": "FRITZ!Boxi t\u00f6\u00f6riistade uuendamine - volitused" }, - "start_config": { - "data": { - "host": "Host", - "password": "Salas\u00f5na", - "port": "Port", - "username": "Kasutajanimi" - }, - "description": "Seadista FRITZ!Boxi t\u00f6\u00f6riistad oma FRITZ!Boxi juhtimiseks.\n Minimaalselt vaja: kasutajanimi ja salas\u00f5na.", - "title": "FRITZ! Boxi t\u00f6\u00f6riistade seadistamine - kohustuslik" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/fr.json b/homeassistant/components/fritz/translations/fr.json index cfcd942b530..538e542c4b6 100644 --- a/homeassistant/components/fritz/translations/fr.json +++ b/homeassistant/components/fritz/translations/fr.json @@ -10,7 +10,6 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "cannot_connect": "\u00c9chec de connexion", - "connection_error": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", "upnp_not_configured": "Param\u00e8tres UPnP manquants sur l'appareil." }, @@ -32,16 +31,6 @@ "description": "Mettre \u00e0 jour les informations d'identification FRITZ!Box Tools pour : {host}.", "title": "Mise \u00e0 jour de FRITZ!Box Tools - informations d'identification" }, - "start_config": { - "data": { - "host": "H\u00f4te", - "password": "Mot de passe", - "port": "Port", - "username": "Nom d'utilisateur" - }, - "description": "Configuration de FRITZ!Box Tools pour contr\u00f4ler votre FRITZ!Box.\nMinimum requis: nom d'utilisateur, mot de passe.", - "title": "Configuration FRITZ!Box Tools - obligatoire" - }, "user": { "data": { "host": "H\u00f4te", diff --git a/homeassistant/components/fritz/translations/he.json b/homeassistant/components/fritz/translations/he.json index 783f215cc40..1b27fdb7581 100644 --- a/homeassistant/components/fritz/translations/he.json +++ b/homeassistant/components/fritz/translations/he.json @@ -9,7 +9,6 @@ "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "connection_error": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" }, "flow_title": "{name}", @@ -26,14 +25,6 @@ "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" } }, - "start_config": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "port": "\u05e4\u05ea\u05d7\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } - }, "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7", diff --git a/homeassistant/components/fritz/translations/hu.json b/homeassistant/components/fritz/translations/hu.json index d1ff2c0c6bf..71e6c0233ae 100644 --- a/homeassistant/components/fritz/translations/hu.json +++ b/homeassistant/components/fritz/translations/hu.json @@ -10,7 +10,6 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A konfigur\u00e1ci\u00f3 m\u00e1r folyamatban van", "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "connection_error": "Nem siker\u00fclt csatlakozni", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "upnp_not_configured": "Hi\u00e1nyz\u00f3 UPnP-be\u00e1ll\u00edt\u00e1sok az eszk\u00f6z\u00f6n." }, @@ -32,16 +31,6 @@ "description": "{host} FRITZ! Box Tools hiteles\u00edt\u0151 adatait. \n\n A FRITZ! Box Tools nem tud bejelentkezni a FRITZ! Box eszk\u00f6zbe.", "title": "A FRITZ! Box Tools friss\u00edt\u00e9se - hiteles\u00edt\u0151 adatok" }, - "start_config": { - "data": { - "host": "C\u00edm", - "password": "Jelsz\u00f3", - "port": "Port", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "description": "A FRITZ! Box eszk\u00f6z\u00f6k be\u00e1ll\u00edt\u00e1sa a FRITZ! Box vez\u00e9rl\u00e9s\u00e9hez.\n Minimum sz\u00fcks\u00e9ges: felhaszn\u00e1l\u00f3n\u00e9v, jelsz\u00f3.", - "title": "A FRITZ! Box Tools be\u00e1ll\u00edt\u00e1sa - k\u00f6telez\u0151" - }, "user": { "data": { "host": "C\u00edm", diff --git a/homeassistant/components/fritz/translations/id.json b/homeassistant/components/fritz/translations/id.json index c31bdf8b77c..71e1e3e8246 100644 --- a/homeassistant/components/fritz/translations/id.json +++ b/homeassistant/components/fritz/translations/id.json @@ -10,7 +10,6 @@ "already_configured": "Perangkat sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", "cannot_connect": "Gagal terhubung", - "connection_error": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", "upnp_not_configured": "Pengaturan UPnP pada perangkat tidak ada." }, @@ -32,16 +31,6 @@ "description": "Perbarui kredensial FRITZ!Box Tools untuk: {host} . \n\nFRITZ!Box Tools tidak dapat masuk ke FRITZ!Box Anda.", "title": "Memperbarui FRITZ!Box Tools - kredensial" }, - "start_config": { - "data": { - "host": "Host", - "password": "Kata Sandi", - "port": "Port", - "username": "Nama Pengguna" - }, - "description": "Siapkan FRITZ!Box Tools untuk mengontrol FRITZ!Box Anda.\nDiperlukan minimal: nama pengguna dan kata sandi.", - "title": "Siapkan FRITZ!Box Tools - wajib" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/it.json b/homeassistant/components/fritz/translations/it.json index 0516449f5db..b1fa200731e 100644 --- a/homeassistant/components/fritz/translations/it.json +++ b/homeassistant/components/fritz/translations/it.json @@ -10,7 +10,6 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "cannot_connect": "Impossibile connettersi", - "connection_error": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", "upnp_not_configured": "Impostazioni UPnP mancanti sul dispositivo." }, @@ -32,16 +31,6 @@ "description": "Aggiorna le credenziali di FRITZ!Box Tools per: {host} . \n\n FRITZ!Box Tools non riesce ad accedere al tuo FRITZ! Box.", "title": "Aggiornamento degli strumenti del FRITZ!Box - credenziali" }, - "start_config": { - "data": { - "host": "Host", - "password": "Password", - "port": "Porta", - "username": "Nome utente" - }, - "description": "Configura gli strumenti FRITZ!Box per controllare il tuo FRITZ!Box.\n Minimo necessario: nome utente, password.", - "title": "Configurazione degli strumenti FRITZ!Box - obbligatorio" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/ja.json b/homeassistant/components/fritz/translations/ja.json index 8bd9747c9bc..1b915b28577 100644 --- a/homeassistant/components/fritz/translations/ja.json +++ b/homeassistant/components/fritz/translations/ja.json @@ -10,7 +10,6 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "connection_error": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "upnp_not_configured": "\u30c7\u30d0\u30a4\u30b9\u306bUPnP\u306e\u8a2d\u5b9a\u304c\u3042\u308a\u307e\u305b\u3093\u3002" }, @@ -32,16 +31,6 @@ "description": "FRITZ!Box Tools\u306e\u8a8d\u8a3c\u3092\u66f4\u65b0\u3057\u307e\u3059: {host}\n\nFRITZ!Box Tools\u304c\u3001FRITZ!Box\u306b\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u307e\u305b\u3093\u3002", "title": "FRITZ!Box Tools\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8 - \u8a8d\u8a3c\u60c5\u5831" }, - "start_config": { - "data": { - "host": "\u30db\u30b9\u30c8", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "port": "\u30dd\u30fc\u30c8", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "description": "FRITZ!Box Tools\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u3066FRITZ!Box\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002\n\u6700\u4f4e\u9650\u5fc5\u8981\u306a\u3082\u306e: \u30e6\u30fc\u30b6\u30fc\u540d\u3001\u30d1\u30b9\u30ef\u30fc\u30c9\u3002", - "title": "FRITZ!Box Tools\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7 - \u5fc5\u9808" - }, "user": { "data": { "host": "\u30db\u30b9\u30c8", diff --git a/homeassistant/components/fritz/translations/ko.json b/homeassistant/components/fritz/translations/ko.json index 718b105df33..99c42938d6f 100644 --- a/homeassistant/components/fritz/translations/ko.json +++ b/homeassistant/components/fritz/translations/ko.json @@ -3,12 +3,12 @@ "abort": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "ignore_ip6_link_local": "IPv6 \ub9c1\ud06c \ub85c\uceec \uc8fc\uc18c\ub294 \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", - "connection_error": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "flow_title": "{name}", @@ -24,14 +24,6 @@ "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } - }, - "start_config": { - "data": { - "host": "\ud638\uc2a4\ud2b8", - "password": "\ube44\ubc00\ubc88\ud638", - "port": "\ud3ec\ud2b8", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } } } } diff --git a/homeassistant/components/fritz/translations/nb.json b/homeassistant/components/fritz/translations/nb.json index 5e712fccbd9..a7e24e2ed11 100644 --- a/homeassistant/components/fritz/translations/nb.json +++ b/homeassistant/components/fritz/translations/nb.json @@ -11,11 +11,6 @@ "username": "Brukernavn" } }, - "start_config": { - "data": { - "username": "Brukernavn" - } - }, "user": { "data": { "username": "Brukernavn" diff --git a/homeassistant/components/fritz/translations/nl.json b/homeassistant/components/fritz/translations/nl.json index 11fabceaf8d..80aebf4fc8a 100644 --- a/homeassistant/components/fritz/translations/nl.json +++ b/homeassistant/components/fritz/translations/nl.json @@ -10,7 +10,6 @@ "already_configured": "Apparaat is al geconfigureerd", "already_in_progress": "De configuratiestroom is al aan de gang", "cannot_connect": "Kan geen verbinding maken", - "connection_error": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "upnp_not_configured": "Ontbrekende UPnP instellingen op apparaat." }, @@ -32,16 +31,6 @@ "description": "Update FRITZ! Box Tools-inloggegevens voor: {host}. \n\n FRITZ! Box Tools kan niet inloggen op uw FRITZ!Box.", "title": "Updating FRITZ!Box Tools - referenties" }, - "start_config": { - "data": { - "host": "Host", - "password": "Wachtwoord", - "port": "Poort", - "username": "Gebruikersnaam" - }, - "description": "Stel FRITZ!Box Tools in om uw FRITZ!Box te bedienen.\nMinimaal nodig: gebruikersnaam, wachtwoord.", - "title": "Configureer FRITZ! Box Tools - verplicht" - }, "user": { "data": { "host": "Host", diff --git a/homeassistant/components/fritz/translations/no.json b/homeassistant/components/fritz/translations/no.json index a18df1a7b14..5ed97636675 100644 --- a/homeassistant/components/fritz/translations/no.json +++ b/homeassistant/components/fritz/translations/no.json @@ -10,7 +10,6 @@ "already_configured": "Enheten er allerede konfigurert", "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "cannot_connect": "Tilkobling mislyktes", - "connection_error": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", "upnp_not_configured": "Mangler UPnP-innstillinger p\u00e5 enheten." }, @@ -32,16 +31,6 @@ "description": "Oppdater legitimasjonen til FRITZ!Box Tools for: {host} . \n\n FRITZ!Box Tools kan ikke logge p\u00e5 FRITZ! Box.", "title": "Oppdaterer FRITZ!Box verkt\u00f8y - legitimasjon" }, - "start_config": { - "data": { - "host": "Vert", - "password": "Passord", - "port": "Port", - "username": "Brukernavn" - }, - "description": "Sett opp FRITZ!Box verkt\u00f8y for \u00e5 kontrollere fritz! Boksen.\nMinimum n\u00f8dvendig: brukernavn, passord.", - "title": "Sett opp FRITZ!Box verkt\u00f8y - obligatorisk" - }, "user": { "data": { "host": "Vert", diff --git a/homeassistant/components/fritz/translations/pl.json b/homeassistant/components/fritz/translations/pl.json index fed010f1987..f14d41ed399 100644 --- a/homeassistant/components/fritz/translations/pl.json +++ b/homeassistant/components/fritz/translations/pl.json @@ -10,7 +10,6 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", "upnp_not_configured": "Brak ustawie\u0144 UPnP w urz\u0105dzeniu." }, @@ -32,16 +31,6 @@ "description": "Zaktualizuj dane logowania narz\u0119dzi FRITZ!Box dla: {host} . \n\nNarz\u0119dzia FRITZ!Box nie mo\u017ce zalogowa\u0107 si\u0119 do urz\u0105dzenia FRITZ!Box.", "title": "Aktualizacja danych logowania narz\u0119dzi FRITZ!Box" }, - "start_config": { - "data": { - "host": "Nazwa hosta lub adres IP", - "password": "Has\u0142o", - "port": "Port", - "username": "Nazwa u\u017cytkownika" - }, - "description": "Skonfiguruj narz\u0119dzia FRITZ!Box, aby sterowa\u0107 urz\u0105dzeniem FRITZ! Box.\nMinimalne wymagania: nazwa u\u017cytkownika, has\u0142o.", - "title": "Konfiguracja narz\u0119dzi FRITZ!Box - obowi\u0105zkowe" - }, "user": { "data": { "host": "Nazwa hosta lub adres IP", diff --git a/homeassistant/components/fritz/translations/pt-BR.json b/homeassistant/components/fritz/translations/pt-BR.json index ceb295310c7..29bb04ebf3f 100644 --- a/homeassistant/components/fritz/translations/pt-BR.json +++ b/homeassistant/components/fritz/translations/pt-BR.json @@ -10,7 +10,6 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "cannot_connect": "Falha ao conectar", - "connection_error": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "upnp_not_configured": "Faltam configura\u00e7\u00f5es de UPnP no dispositivo." }, @@ -32,16 +31,6 @@ "description": "Atualize as credenciais do FRITZ!Box Tools para: {host} . \n\n O FRITZ!Box Tools n\u00e3o consegue iniciar sess\u00e3o no seu FRITZ!Box.", "title": "Atualizando as Ferramentas do FRITZ!Box - credenciais" }, - "start_config": { - "data": { - "host": "Nome do host", - "password": "Senha", - "port": "Porta", - "username": "Usu\u00e1rio" - }, - "description": "Configure as Ferramentas do FRITZ!Box para controlar o seu FRITZ!Box.\n M\u00ednimo necess\u00e1rio: nome de usu\u00e1rio, senha.", - "title": "Configurar as Ferramentas do FRITZ!Box - obrigat\u00f3rio" - }, "user": { "data": { "host": "Nome do host", diff --git a/homeassistant/components/fritz/translations/ru.json b/homeassistant/components/fritz/translations/ru.json index e45c36fe736..63d4463a055 100644 --- a/homeassistant/components/fritz/translations/ru.json +++ b/homeassistant/components/fritz/translations/ru.json @@ -10,7 +10,6 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "connection_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "upnp_not_configured": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 UPnP \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435." }, @@ -32,16 +31,6 @@ "description": "\u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 FRITZ!Box Tools \u0434\u043b\u044f {host}.\n\n\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e FRITZ!Box Tools \u043d\u0430 \u0412\u0430\u0448\u0435\u043c FRITZ!Box.", "title": "FRITZ!Box Tools" }, - "start_config": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "port": "\u041f\u043e\u0440\u0442", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 FRITZ!Box Tools \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0412\u0430\u0448\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c FRITZ!Box.\n\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.", - "title": "FRITZ!Box Tools" - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", diff --git a/homeassistant/components/fritz/translations/sk.json b/homeassistant/components/fritz/translations/sk.json index 9d83bc4b756..17cb7bfc78b 100644 --- a/homeassistant/components/fritz/translations/sk.json +++ b/homeassistant/components/fritz/translations/sk.json @@ -9,11 +9,6 @@ "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { - "start_config": { - "data": { - "port": "Port" - } - }, "user": { "data": { "port": "Port" diff --git a/homeassistant/components/fritz/translations/tr.json b/homeassistant/components/fritz/translations/tr.json index 86cabbb782b..af443cac976 100644 --- a/homeassistant/components/fritz/translations/tr.json +++ b/homeassistant/components/fritz/translations/tr.json @@ -10,7 +10,6 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "cannot_connect": "Ba\u011flanma hatas\u0131", - "connection_error": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "upnp_not_configured": "Cihazda UPnP ayarlar\u0131 eksik." }, @@ -32,16 +31,6 @@ "description": "{host} i\u00e7in FRITZ!Box Tools kimlik bilgilerini g\u00fcncelleyin. \n\n FRITZ!Box Tools, FRITZ!Box'\u0131n\u0131zda oturum a\u00e7am\u0131yor.", "title": "FRITZ!Box Tools - kimlik bilgilerinin g\u00fcncellenmesi" }, - "start_config": { - "data": { - "host": "Sunucu", - "password": "Parola", - "port": "Port", - "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "description": "FRITZ!Box'\u0131n\u0131z\u0131 kontrol etmek i\u00e7in FRITZ!Box Tools'u kurun.\n Minimum gerekli: kullan\u0131c\u0131 ad\u0131, \u015fifre.", - "title": "FRITZ!Box Tools Kurulumu - zorunlu" - }, "user": { "data": { "host": "Sunucu", diff --git a/homeassistant/components/fritz/translations/zh-Hans.json b/homeassistant/components/fritz/translations/zh-Hans.json index 91d68989675..3f5a3f2afa0 100644 --- a/homeassistant/components/fritz/translations/zh-Hans.json +++ b/homeassistant/components/fritz/translations/zh-Hans.json @@ -11,11 +11,6 @@ "password": "\u5bc6\u7801" } }, - "start_config": { - "data": { - "password": "\u5bc6\u7801" - } - }, "user": { "data": { "password": "\u5bc6\u7801" diff --git a/homeassistant/components/fritz/translations/zh-Hant.json b/homeassistant/components/fritz/translations/zh-Hant.json index 778c38e36f7..0565a1e292a 100644 --- a/homeassistant/components/fritz/translations/zh-Hant.json +++ b/homeassistant/components/fritz/translations/zh-Hant.json @@ -10,7 +10,6 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "cannot_connect": "\u9023\u7dda\u5931\u6557", - "connection_error": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "upnp_not_configured": "\u672a\u8a2d\u5b9a\u88dd\u7f6e UPnP \u8a2d\u5b9a\u3002" }, @@ -32,16 +31,6 @@ "description": "\u66f4\u65b0 FRITZ!Box Tools \u6191\u8b49\uff1a{host}\u3002\n\nFRITZ!Box Tools \u7121\u6cd5\u767b\u5165 FRITZ!Box\u3002", "title": "\u66f4\u65b0 FRITZ!Box Tools - \u6191\u8b49" }, - "start_config": { - "data": { - "host": "\u4e3b\u6a5f\u7aef", - "password": "\u5bc6\u78bc", - "port": "\u901a\u8a0a\u57e0", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "description": "\u8a2d\u5b9a FRITZ!Box Tools \u4ee5\u63a7\u5236 FRITZ!Box\u3002\n\u9700\u8981\u8f38\u5165\uff1a\u4f7f\u7528\u8005\u540d\u7a31\u3001\u5bc6\u78bc\u3002", - "title": "\u8a2d\u5b9a FRITZ!Box Tools - \u5f37\u5236" - }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", diff --git a/homeassistant/components/generic/translations/bg.json b/homeassistant/components/generic/translations/bg.json index dc9bd439f0b..ebb2d32c21d 100644 --- a/homeassistant/components/generic/translations/bg.json +++ b/homeassistant/components/generic/translations/bg.json @@ -20,7 +20,6 @@ "user": { "data": { "authentication": "\u0423\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" @@ -42,7 +41,6 @@ "init": { "data": { "authentication": "\u0423\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "content_type": "\u0422\u0438\u043f \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "rtsp_transport": "RTSP \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u0435\u043d \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" diff --git a/homeassistant/components/generic/translations/ca.json b/homeassistant/components/generic/translations/ca.json index 7d69e837130..3f2cd6afa47 100644 --- a/homeassistant/components/generic/translations/ca.json +++ b/homeassistant/components/generic/translations/ca.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Autenticaci\u00f3", - "content_type": "Tipus de contingut", "framerate": "Freq\u00fc\u00e8ncia de visualitzaci\u00f3 (Hz)", "limit_refetch_to_url_change": "Limita la lectura al canvi d'URL", "password": "Contrasenya", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Autenticaci\u00f3", - "content_type": "Tipus de contingut", "framerate": "Freq\u00fc\u00e8ncia de visualitzaci\u00f3 (Hz)", "limit_refetch_to_url_change": "Limita la lectura al canvi d'URL", "password": "Contrasenya", diff --git a/homeassistant/components/generic/translations/de.json b/homeassistant/components/generic/translations/de.json index 8d40216001a..d4c5c268eed 100644 --- a/homeassistant/components/generic/translations/de.json +++ b/homeassistant/components/generic/translations/de.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Authentifizierung", - "content_type": "Inhaltstyp", "framerate": "Bildfrequenz (Hz)", "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", "password": "Passwort", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Authentifizierung", - "content_type": "Inhaltstyp", "framerate": "Bildfrequenz (Hz)", "limit_refetch_to_url_change": "Neuabruf auf URL-\u00c4nderung beschr\u00e4nken", "password": "Passwort", diff --git a/homeassistant/components/generic/translations/el.json b/homeassistant/components/generic/translations/el.json index 8b15c7de851..be1c963740a 100644 --- a/homeassistant/components/generic/translations/el.json +++ b/homeassistant/components/generic/translations/el.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5", "framerate": "\u03a1\u03c5\u03b8\u03bc\u03cc\u03c2 \u03ba\u03b1\u03c1\u03ad (Hz)", "limit_refetch_to_url_change": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae url", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "\u0395\u03bb\u03ad\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "content_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03bf\u03bc\u03ad\u03bd\u03bf\u03c5", "framerate": "\u03a1\u03c5\u03b8\u03bc\u03cc\u03c2 \u03ba\u03b1\u03c1\u03ad (Hz)", "limit_refetch_to_url_change": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac\u03c2 \u03c3\u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae url", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index 334f00fab6f..b552c780d29 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Authentication", - "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Authentication", - "content_type": "Content Type", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Limit refetch to url change", "password": "Password", diff --git a/homeassistant/components/generic/translations/es.json b/homeassistant/components/generic/translations/es.json index 90bff434f6d..4d8019002bb 100644 --- a/homeassistant/components/generic/translations/es.json +++ b/homeassistant/components/generic/translations/es.json @@ -24,7 +24,6 @@ "user": { "data": { "authentication": "Autenticaci\u00f3n", - "content_type": "Tipo de contenido", "framerate": "Frecuencia de visualizaci\u00f3n (Hz)", "limit_refetch_to_url_change": "Limita la lectura al cambio de URL", "still_image_url": "URL de imagen fija (ej. http://...)", @@ -53,7 +52,6 @@ "init": { "data": { "authentication": "Autenticaci\u00f3n", - "content_type": "Tipo de contenido", "framerate": "Frecuencia de visualizaci\u00f3n (Hz)", "limit_refetch_to_url_change": "Limita la lectura al cambio de URL", "password": "Contrase\u00f1a", diff --git a/homeassistant/components/generic/translations/et.json b/homeassistant/components/generic/translations/et.json index 0a4bdc0731f..41e1881573e 100644 --- a/homeassistant/components/generic/translations/et.json +++ b/homeassistant/components/generic/translations/et.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Autentimine", - "content_type": "Sisu t\u00fc\u00fcp", "framerate": "Kaadrisagedus (Hz)", "limit_refetch_to_url_change": "Piira laadimist URL-i muutmiseni", "password": "Salas\u00f5na", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Autentimine", - "content_type": "Sisu t\u00fc\u00fcp", "framerate": "Kaadrisagedus (Hz)", "limit_refetch_to_url_change": "Piira laadimist URL-i muutmiseni", "password": "Salas\u00f5na", diff --git a/homeassistant/components/generic/translations/fr.json b/homeassistant/components/generic/translations/fr.json index 454f9b0de03..6215c579be7 100644 --- a/homeassistant/components/generic/translations/fr.json +++ b/homeassistant/components/generic/translations/fr.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Authentification", - "content_type": "Type de contenu", "framerate": "Fr\u00e9quence d'images (en hertz)", "limit_refetch_to_url_change": "Limiter la r\u00e9cup\u00e9ration aux changements d'URL", "password": "Mot de passe", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Authentification", - "content_type": "Type de contenu", "framerate": "Fr\u00e9quence d'images (en hertz)", "limit_refetch_to_url_change": "Limiter la r\u00e9cup\u00e9ration aux changements d'URL", "password": "Mot de passe", diff --git a/homeassistant/components/generic/translations/he.json b/homeassistant/components/generic/translations/he.json index b20b0ea88a7..f39f78074f5 100644 --- a/homeassistant/components/generic/translations/he.json +++ b/homeassistant/components/generic/translations/he.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", - "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df", "framerate": "\u05e7\u05e6\u05d1 \u05e4\u05e8\u05d9\u05d9\u05de\u05d9\u05dd (\u05d4\u05e8\u05e5)", "limit_refetch_to_url_change": "\u05d4\u05d2\u05d1\u05dc\u05d4 \u05e9\u05dc \u05d0\u05d7\u05e1\u05d5\u05df \u05d7\u05d5\u05d6\u05e8 \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "\u05d0\u05d9\u05de\u05d5\u05ea", - "content_type": "\u05e1\u05d5\u05d2 \u05ea\u05d5\u05db\u05df", "framerate": "\u05e7\u05e6\u05d1 \u05e4\u05e8\u05d9\u05d9\u05de\u05d9\u05dd (\u05d4\u05e8\u05e5)", "limit_refetch_to_url_change": "\u05d4\u05d2\u05d1\u05dc\u05d4 \u05e9\u05dc \u05d0\u05d7\u05e1\u05d5\u05df \u05d7\u05d5\u05d6\u05e8 \u05dc\u05e9\u05d9\u05e0\u05d5\u05d9 \u05db\u05ea\u05d5\u05d1\u05ea \u05d4\u05d0\u05ea\u05e8", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", diff --git a/homeassistant/components/generic/translations/hu.json b/homeassistant/components/generic/translations/hu.json index bc8172caaf9..59840b3195b 100644 --- a/homeassistant/components/generic/translations/hu.json +++ b/homeassistant/components/generic/translations/hu.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Hiteles\u00edt\u00e9s", - "content_type": "Tartalom t\u00edpusa", "framerate": "K\u00e9pkockasebess\u00e9g (Hz)", "limit_refetch_to_url_change": "Korl\u00e1tozza a visszah\u00edv\u00e1st az url v\u00e1ltoz\u00e1sra", "password": "Jelsz\u00f3", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Hiteles\u00edt\u00e9s", - "content_type": "Tartalom t\u00edpusa", "framerate": "K\u00e9pkockasebess\u00e9g (Hz)", "limit_refetch_to_url_change": "Korl\u00e1tozza a visszah\u00edv\u00e1st az url v\u00e1ltoz\u00e1sra", "password": "Jelsz\u00f3", diff --git a/homeassistant/components/generic/translations/id.json b/homeassistant/components/generic/translations/id.json index 7c4be981e4a..95c6d6e2ae2 100644 --- a/homeassistant/components/generic/translations/id.json +++ b/homeassistant/components/generic/translations/id.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Autentikasi", - "content_type": "Jenis Konten", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Batasi pengambilan ulang untuk perubahan URL", "password": "Kata Sandi", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Autentikasi", - "content_type": "Jenis Konten", "framerate": "Frame Rate (Hz)", "limit_refetch_to_url_change": "Batasi pengambilan ulang untuk perubahan URL", "password": "Kata Sandi", diff --git a/homeassistant/components/generic/translations/it.json b/homeassistant/components/generic/translations/it.json index e5e41e50285..ff4b9822601 100644 --- a/homeassistant/components/generic/translations/it.json +++ b/homeassistant/components/generic/translations/it.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Autenticazione", - "content_type": "Tipo di contenuto", "framerate": "Frequenza fotogrammi (Hz)", "limit_refetch_to_url_change": "Limita il recupero alla modifica dell'URL", "password": "Password", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Autenticazione", - "content_type": "Tipo di contenuto", "framerate": "Frequenza fotogrammi (Hz)", "limit_refetch_to_url_change": "Limita il recupero alla modifica dell'URL", "password": "Password", diff --git a/homeassistant/components/generic/translations/ja.json b/homeassistant/components/generic/translations/ja.json index 97c1a76ab36..a106cbf4cdc 100644 --- a/homeassistant/components/generic/translations/ja.json +++ b/homeassistant/components/generic/translations/ja.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "\u8a8d\u8a3c", - "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e", "framerate": "\u30d5\u30ec\u30fc\u30e0\u30ec\u30fc\u30c8\uff08Hz\uff09", "limit_refetch_to_url_change": "URL\u5909\u66f4\u6642\u306e\u518d\u53d6\u5f97\u3092\u5236\u9650\u3059\u308b", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "\u8a8d\u8a3c", - "content_type": "\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u7a2e\u985e", "framerate": "\u30d5\u30ec\u30fc\u30e0\u30ec\u30fc\u30c8\uff08Hz\uff09", "limit_refetch_to_url_change": "URL\u5909\u66f4\u6642\u306e\u518d\u53d6\u5f97\u3092\u5236\u9650\u3059\u308b", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", diff --git a/homeassistant/components/generic/translations/ko.json b/homeassistant/components/generic/translations/ko.json new file mode 100644 index 00000000000..20ad990e862 --- /dev/null +++ b/homeassistant/components/generic/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/nl.json b/homeassistant/components/generic/translations/nl.json index 5c45b783c4f..2b5bc828727 100644 --- a/homeassistant/components/generic/translations/nl.json +++ b/homeassistant/components/generic/translations/nl.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Authenticatie", - "content_type": "Inhoudstype", "framerate": "Framesnelheid (Hz)", "limit_refetch_to_url_change": "Beperk refetch tot url verandering", "password": "Wachtwoord", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Authenticatie", - "content_type": "Inhoudstype", "framerate": "Framesnelheid (Hz)", "limit_refetch_to_url_change": "Beperk refetch tot url verandering", "password": "Wachtwoord", diff --git a/homeassistant/components/generic/translations/no.json b/homeassistant/components/generic/translations/no.json index 78b5618b073..0c228e314d2 100644 --- a/homeassistant/components/generic/translations/no.json +++ b/homeassistant/components/generic/translations/no.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Godkjenning", - "content_type": "Innholdstype", "framerate": "Bildefrekvens (Hz)", "limit_refetch_to_url_change": "Begrens gjenhenting til endring av nettadresse", "password": "Passord", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Godkjenning", - "content_type": "Innholdstype", "framerate": "Bildefrekvens (Hz)", "limit_refetch_to_url_change": "Begrens gjenhenting til endring av nettadresse", "password": "Passord", diff --git a/homeassistant/components/generic/translations/pl.json b/homeassistant/components/generic/translations/pl.json index 16a912587e5..3669fd78e3f 100644 --- a/homeassistant/components/generic/translations/pl.json +++ b/homeassistant/components/generic/translations/pl.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Uwierzytelnianie", - "content_type": "Typ zawarto\u015bci", "framerate": "Od\u015bwie\u017canie (Hz)", "limit_refetch_to_url_change": "Ogranicz pobieranie do zmiany adresu URL", "password": "Has\u0142o", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Uwierzytelnianie", - "content_type": "Typ zawarto\u015bci", "framerate": "Od\u015bwie\u017canie (Hz)", "limit_refetch_to_url_change": "Ogranicz pobieranie do zmiany adresu URL", "password": "Has\u0142o", diff --git a/homeassistant/components/generic/translations/pt-BR.json b/homeassistant/components/generic/translations/pt-BR.json index ec093f3ff1a..ea1ede92f22 100644 --- a/homeassistant/components/generic/translations/pt-BR.json +++ b/homeassistant/components/generic/translations/pt-BR.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Autentica\u00e7\u00e3o", - "content_type": "Tipo de conte\u00fado", "framerate": "Taxa de quadros (Hz)", "limit_refetch_to_url_change": "Limitar a nova busca \u00e0 altera\u00e7\u00e3o de URL", "password": "Senha", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Autentica\u00e7\u00e3o", - "content_type": "Tipo de conte\u00fado", "framerate": "Taxa de quadros (Hz)", "limit_refetch_to_url_change": "Limitar a nova busca \u00e0 altera\u00e7\u00e3o de URL", "password": "Senha", diff --git a/homeassistant/components/generic/translations/ru.json b/homeassistant/components/generic/translations/ru.json index e88f9f9cd1e..f9fed6b5831 100644 --- a/homeassistant/components/generic/translations/ru.json +++ b/homeassistant/components/generic/translations/ru.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", - "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e", "framerate": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432 (\u0413\u0446)", "limit_refetch_to_url_change": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f", - "content_type": "\u0422\u0438\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e", "framerate": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432 (\u0413\u0446)", "limit_refetch_to_url_change": "\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0443\u044e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", diff --git a/homeassistant/components/generic/translations/tr.json b/homeassistant/components/generic/translations/tr.json index d6ba8601beb..7b6ab65f948 100644 --- a/homeassistant/components/generic/translations/tr.json +++ b/homeassistant/components/generic/translations/tr.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "Kimlik Do\u011frulama", - "content_type": "\u0130\u00e7erik T\u00fcr\u00fc", "framerate": "Kare H\u0131z\u0131 (Hz)", "limit_refetch_to_url_change": "Yeniden getirmeyi url de\u011fi\u015fikli\u011fiyle s\u0131n\u0131rla", "password": "Parola", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "Kimlik Do\u011frulama", - "content_type": "\u0130\u00e7erik T\u00fcr\u00fc", "framerate": "Kare H\u0131z\u0131 (Hz)", "limit_refetch_to_url_change": "Yeniden getirmeyi url de\u011fi\u015fikli\u011fiyle s\u0131n\u0131rla", "password": "Parola", diff --git a/homeassistant/components/generic/translations/zh-Hant.json b/homeassistant/components/generic/translations/zh-Hant.json index e37ee2d9235..d1079724252 100644 --- a/homeassistant/components/generic/translations/zh-Hant.json +++ b/homeassistant/components/generic/translations/zh-Hant.json @@ -32,7 +32,6 @@ "user": { "data": { "authentication": "\u9a57\u8b49", - "content_type": "\u5167\u5bb9\u985e\u578b", "framerate": "\u5f71\u683c\u7387 (Hz)", "limit_refetch_to_url_change": "\u9650\u5236 URL \u8b8a\u66f4\u66f4\u65b0", "password": "\u5bc6\u78bc", @@ -72,7 +71,6 @@ "init": { "data": { "authentication": "\u9a57\u8b49", - "content_type": "\u5167\u5bb9\u985e\u578b", "framerate": "\u5f71\u683c\u7387 (Hz)", "limit_refetch_to_url_change": "\u9650\u5236 URL \u8b8a\u66f4\u66f4\u65b0", "password": "\u5bc6\u78bc", diff --git a/homeassistant/components/geocaching/translations/he.json b/homeassistant/components/geocaching/translations/he.json new file mode 100644 index 00000000000..525624782ac --- /dev/null +++ b/homeassistant/components/geocaching/translations/he.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "authorize_url_timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05db\u05ea\u05d5\u05d1\u05ea URL \u05dc\u05d0\u05d9\u05e9\u05d5\u05e8.", + "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", + "no_url_available": "\u05d0\u05d9\u05df \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4. \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05d9\u05d9\u05df \u05d1\u05e1\u05e2\u05d9\u05e3 \u05d4\u05e2\u05d6\u05e8\u05d4] ({docs_url})", + "oauth_error": "\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd.", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "create_entry": { + "default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4" + }, + "step": { + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + }, + "reauth_confirm": { + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/gios/translations/ca.json b/homeassistant/components/gios/translations/ca.json index 8150e309b20..abfa55fef29 100644 --- a/homeassistant/components/gios/translations/ca.json +++ b/homeassistant/components/gios/translations/ca.json @@ -14,7 +14,6 @@ "name": "Nom", "station_id": "ID de l'estaci\u00f3 de mesura" }, - "description": "Integraci\u00f3 de mesura de qualitat de l'aire GIO\u015a (Polish Chief Inspectorate Of Environmental Protection). Si necessites ajuda amb la configuraci\u00f3, fes un cop d'ull a: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/cs.json b/homeassistant/components/gios/translations/cs.json index 8dea1f5e013..a80a5ae03bd 100644 --- a/homeassistant/components/gios/translations/cs.json +++ b/homeassistant/components/gios/translations/cs.json @@ -14,7 +14,6 @@ "name": "Jm\u00e9no", "station_id": "ID m\u011b\u0159ic\u00ed stanice" }, - "description": "Nastaven\u00ed integrace kvality ovzdu\u0161\u00ed GIO\u015a (polsk\u00fd hlavn\u00ed inspektor\u00e1t ochrany \u017eivotn\u00edho prost\u0159ed\u00ed). Pokud pot\u0159ebujete pomoc s nastaven\u00edm, pod\u00edvejte se na: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (polsk\u00fd hlavn\u00ed inspektor\u00e1t ochrany \u017eivotn\u00edho prost\u0159ed\u00ed)" } } diff --git a/homeassistant/components/gios/translations/da.json b/homeassistant/components/gios/translations/da.json index d4442982e1e..2d3a2e3e5e9 100644 --- a/homeassistant/components/gios/translations/da.json +++ b/homeassistant/components/gios/translations/da.json @@ -14,7 +14,6 @@ "name": "Navn p\u00e5 integrationen", "station_id": "ID for m\u00e5lestationen" }, - "description": "Ops\u00e6t GIO\u015a (polsk inspektorat for milj\u00f8beskyttelse) luftkvalitet-integration. Hvis du har brug for hj\u00e6lp med konfigurationen, kig her: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/de.json b/homeassistant/components/gios/translations/de.json index 99548187601..c22b2a197dd 100644 --- a/homeassistant/components/gios/translations/de.json +++ b/homeassistant/components/gios/translations/de.json @@ -14,7 +14,6 @@ "name": "Name", "station_id": "ID der Messstation" }, - "description": "Einrichtung von GIO\u015a (Polnische Hauptinspektion f\u00fcr Umweltschutz) Integration der Luftqualit\u00e4t. Wenn du Hilfe bei der Konfiguration ben\u00f6tigst, schaue hier: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polnische Hauptinspektion f\u00fcr Umweltschutz)" } } diff --git a/homeassistant/components/gios/translations/el.json b/homeassistant/components/gios/translations/el.json index ae916d70667..46bcc2d0b9c 100644 --- a/homeassistant/components/gios/translations/el.json +++ b/homeassistant/components/gios/translations/el.json @@ -14,7 +14,6 @@ "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "station_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c4\u03bf\u03c5 \u03c3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd \u03bc\u03ad\u03c4\u03c1\u03b7\u03c3\u03b7\u03c2" }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c4\u03bf\u03c5 \u03b1\u03ad\u03c1\u03b1 GIO\u015a (\u03a0\u03bf\u03bb\u03c9\u03bd\u03b9\u03ba\u03ae \u0395\u03c0\u03b9\u03ba\u03b5\u03c6\u03b1\u03bb\u03ae\u03c2 \u0395\u03c0\u03b9\u03b8\u03b5\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2 \u03a0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b1\u03c2 \u03c4\u03bf\u03c5 \u03a0\u03b5\u03c1\u03b9\u03b2\u03ac\u03bb\u03bb\u03bf\u03bd\u03c4\u03bf\u03c2). \u0395\u03ac\u03bd \u03c7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c3\u03c4\u03b5 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1 \u03bc\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7, \u03c1\u03af\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03bc\u03b1\u03c4\u03b9\u03ac \u03b5\u03b4\u03ce: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (\u03a0\u03bf\u03bb\u03c9\u03bd\u03b9\u03ba\u03ae \u0393\u03b5\u03bd\u03b9\u03ba\u03ae \u0395\u03c0\u03b9\u03b8\u03b5\u03ce\u03c1\u03b7\u03c3\u03b7 \u03a0\u03c1\u03bf\u03c3\u03c4\u03b1\u03c3\u03af\u03b1\u03c2 \u03a0\u03b5\u03c1\u03b9\u03b2\u03ac\u03bb\u03bb\u03bf\u03bd\u03c4\u03bf\u03c2)" } } diff --git a/homeassistant/components/gios/translations/en.json b/homeassistant/components/gios/translations/en.json index 86f05b8987e..232389291be 100644 --- a/homeassistant/components/gios/translations/en.json +++ b/homeassistant/components/gios/translations/en.json @@ -14,7 +14,6 @@ "name": "Name", "station_id": "ID of the measuring station" }, - "description": "Set up GIO\u015a (Polish Chief Inspectorate Of Environmental Protection) air quality integration. If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/es-419.json b/homeassistant/components/gios/translations/es-419.json index 848247bdf75..52640708ce8 100644 --- a/homeassistant/components/gios/translations/es-419.json +++ b/homeassistant/components/gios/translations/es-419.json @@ -14,7 +14,6 @@ "name": "Nombre de la integraci\u00f3n", "station_id": "Identificaci\u00f3n de la estaci\u00f3n de medici\u00f3n" }, - "description": "Establecer la integraci\u00f3n de la calidad del aire GIO\u015a (Inspecci\u00f3n Jefe de Protecci\u00f3n Ambiental de Polonia). Si necesita ayuda con la configuraci\u00f3n, eche un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/es.json b/homeassistant/components/gios/translations/es.json index 011ddd0b6f7..3d6bfc57150 100644 --- a/homeassistant/components/gios/translations/es.json +++ b/homeassistant/components/gios/translations/es.json @@ -14,7 +14,6 @@ "name": "Nombre", "station_id": "ID de la estaci\u00f3n de medici\u00f3n" }, - "description": "Configurar la integraci\u00f3n de la calidad del aire GIO\u015a (Inspecci\u00f3n Jefe de Protecci\u00f3n Ambiental de Polonia). Si necesita ayuda con la configuraci\u00f3n, eche un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Inspecci\u00f3n Jefe de Protecci\u00f3n del Medio Ambiente de Polonia)" } } diff --git a/homeassistant/components/gios/translations/et.json b/homeassistant/components/gios/translations/et.json index 2d0906f73ab..8565849250e 100644 --- a/homeassistant/components/gios/translations/et.json +++ b/homeassistant/components/gios/translations/et.json @@ -14,7 +14,6 @@ "name": "Nimi", "station_id": "M\u00f5\u00f5tejaama ID" }, - "description": "Loo GIO\u015a (Poola keskkonnakaitse peainspektsioon) \u00f5hukvaliteedi sidumnie. Kui vajad seadistamisel abi, vaats siit: https://www.home-assistant.io/integrations/gios", "title": "" } } diff --git a/homeassistant/components/gios/translations/fr.json b/homeassistant/components/gios/translations/fr.json index af107914c06..5533e9c48bf 100644 --- a/homeassistant/components/gios/translations/fr.json +++ b/homeassistant/components/gios/translations/fr.json @@ -14,7 +14,6 @@ "name": "Nom", "station_id": "Identifiant de la station de mesure" }, - "description": "Mettre en place l'int\u00e9gration de la qualit\u00e9 de l'air GIO\u015a (Inspection g\u00e9n\u00e9rale polonaise de la protection de l'environnement). Si vous avez besoin d'aide pour la configuration, regardez ici: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Inspection g\u00e9n\u00e9rale polonaise de la protection de l'environnement)" } } diff --git a/homeassistant/components/gios/translations/hu.json b/homeassistant/components/gios/translations/hu.json index 9ad0e244eaf..87f54802368 100644 --- a/homeassistant/components/gios/translations/hu.json +++ b/homeassistant/components/gios/translations/hu.json @@ -14,7 +14,6 @@ "name": "Elnevez\u00e9s", "station_id": "A m\u00e9r\u0151\u00e1llom\u00e1s azonos\u00edt\u00f3ja" }, - "description": "A GIO\u015a (lengyel k\u00f6rnyezetv\u00e9delmi f\u0151fel\u00fcgyel\u0151) leveg\u0151min\u0151s\u00e9gi integr\u00e1ci\u00f3j\u00e1nak be\u00e1ll\u00edt\u00e1sa. Ha seg\u00edts\u00e9gre van sz\u00fcks\u00e9ge a konfigur\u00e1ci\u00f3val kapcsolatban, l\u00e1togasson ide: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Lengyel K\u00f6rnyezetv\u00e9delmi F\u0151fel\u00fcgyel\u0151s\u00e9g)" } } diff --git a/homeassistant/components/gios/translations/id.json b/homeassistant/components/gios/translations/id.json index b32210c30d5..844b1e794b5 100644 --- a/homeassistant/components/gios/translations/id.json +++ b/homeassistant/components/gios/translations/id.json @@ -14,7 +14,6 @@ "name": "Nama", "station_id": "ID stasiun pengukuran" }, - "description": "Siapkan integrasi kualitas udara GIO\u015a (Inspektorat Jenderal Perlindungan Lingkungan Polandia). Jika Anda memerlukan bantuan tentang konfigurasi, baca di sini: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Inspektorat Jenderal Perlindungan Lingkungan Polandia)" } } diff --git a/homeassistant/components/gios/translations/it.json b/homeassistant/components/gios/translations/it.json index cb1f1cea6b0..bd88ded42b6 100644 --- a/homeassistant/components/gios/translations/it.json +++ b/homeassistant/components/gios/translations/it.json @@ -14,7 +14,6 @@ "name": "Nome", "station_id": "ID della stazione di misura" }, - "description": "Imposta l'integrazione della qualit\u00e0 dell'aria GIO\u015a (Ispettorato capo polacco di protezione ambientale). Se hai bisogno di aiuto con la configurazione dai un'occhiata qui: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Ispettorato capo polacco di protezione ambientale)" } } diff --git a/homeassistant/components/gios/translations/ja.json b/homeassistant/components/gios/translations/ja.json index ac05abbf923..f3c9464a7a5 100644 --- a/homeassistant/components/gios/translations/ja.json +++ b/homeassistant/components/gios/translations/ja.json @@ -14,7 +14,6 @@ "name": "\u540d\u524d", "station_id": "\u6e2c\u5b9a\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u306eID" }, - "description": "GIO\u015a(Polish Chief Inspectorate Of Environmental Protection)\u306e\u5927\u6c17\u8cea\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002\u8a2d\u5b9a\u306b\u3064\u3044\u3066\u30d8\u30eb\u30d7\u304c\u5fc5\u8981\u306a\u5834\u5408\u306f\u3001https://www.home-assistant.io/integrations/gios \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/ko.json b/homeassistant/components/gios/translations/ko.json index e462ef4e3b6..c49bd258c50 100644 --- a/homeassistant/components/gios/translations/ko.json +++ b/homeassistant/components/gios/translations/ko.json @@ -14,7 +14,6 @@ "name": "\uc774\ub984", "station_id": "\uce21\uc815 \uc2a4\ud14c\uc774\uc158\uc758 ID" }, - "description": "\ud3f4\ub780\ub4dc \ud658\uacbd\uccad (GIO\u015a) \ub300\uae30\uc9c8 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. \uad6c\uc131\uc5d0 \ub3c4\uc6c0\uc774 \ud544\uc694\ud55c \uacbd\uc6b0 https://www.home-assistant.io/integrations/gios \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694", "title": "\ud3f4\ub780\ub4dc \ud658\uacbd\uccad (GIO\u015a)" } } diff --git a/homeassistant/components/gios/translations/lb.json b/homeassistant/components/gios/translations/lb.json index cafea72fb78..22db9df424f 100644 --- a/homeassistant/components/gios/translations/lb.json +++ b/homeassistant/components/gios/translations/lb.json @@ -14,7 +14,6 @@ "name": "Numm", "station_id": "ID vun der Miess Statioun" }, - "description": "GIO\u015a (Polnesch Chefinspektorat vum \u00cbmweltschutz) Loft Qualit\u00e9it Integratioun ariichten. Fir w\u00e9ider H\u00ebllef mat der Konfiuratioun kuckt hei: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polnesch Chefinspektorat vum \u00cbmweltschutz)" } } diff --git a/homeassistant/components/gios/translations/nl.json b/homeassistant/components/gios/translations/nl.json index 87104523a31..26c83cfff7b 100644 --- a/homeassistant/components/gios/translations/nl.json +++ b/homeassistant/components/gios/translations/nl.json @@ -14,7 +14,6 @@ "name": "Naam", "station_id": "ID van het meetstation" }, - "description": "GIO\u015a (Poolse hoofdinspectie van milieubescherming) luchtkwaliteitintegratie instellen. Als u hulp nodig hebt bij de configuratie, kijk dan hier: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Poolse hoofdinspectie van milieubescherming)" } } diff --git a/homeassistant/components/gios/translations/no.json b/homeassistant/components/gios/translations/no.json index 038cbdc20a3..68d02c7deec 100644 --- a/homeassistant/components/gios/translations/no.json +++ b/homeassistant/components/gios/translations/no.json @@ -14,7 +14,6 @@ "name": "Navn", "station_id": "ID til m\u00e5lestasjon" }, - "description": "Sett opp GIO\u015a (Polish Chief Inspectorate Of Environmental Protection) luftkvalitet integrasjon. Hvis du trenger hjelp med konfigurasjonen ta en titt her: https://www.home-assistant.io/integrations/gios", "title": "" } } diff --git a/homeassistant/components/gios/translations/pl.json b/homeassistant/components/gios/translations/pl.json index 8bc909e2bab..6a7d2aa7064 100644 --- a/homeassistant/components/gios/translations/pl.json +++ b/homeassistant/components/gios/translations/pl.json @@ -14,7 +14,6 @@ "name": "Nazwa", "station_id": "Identyfikator stacji pomiarowej" }, - "description": "Konfiguracja integracji jako\u015bci powietrza GIO\u015a (G\u0142\u00f3wny Inspektorat Ochrony \u015arodowiska). Je\u015bli potrzebujesz pomocy z konfiguracj\u0105, przejd\u017a na stron\u0119: https://www.home-assistant.io/integrations/gios", "title": "G\u0142\u00f3wny Inspektorat Ochrony \u015arodowiska (GIO\u015a)" } } diff --git a/homeassistant/components/gios/translations/pt-BR.json b/homeassistant/components/gios/translations/pt-BR.json index 39472c3b420..e4aad1faf74 100644 --- a/homeassistant/components/gios/translations/pt-BR.json +++ b/homeassistant/components/gios/translations/pt-BR.json @@ -14,7 +14,6 @@ "name": "Nome", "station_id": "ID da esta\u00e7\u00e3o de medi\u00e7\u00e3o" }, - "description": "Configurar a integra\u00e7\u00e3o da qualidade do ar GIO\u015a (Polish Chief Inspectorate Of Environmental Protection). Se precisar de ajuda com a configura\u00e7\u00e3o, d\u00ea uma olhada em https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Inspetor-Chefe Polon\u00eas de Prote\u00e7\u00e3o Ambiental)" } } diff --git a/homeassistant/components/gios/translations/ru.json b/homeassistant/components/gios/translations/ru.json index 68d6ee44b0b..ede5f2d9a70 100644 --- a/homeassistant/components/gios/translations/ru.json +++ b/homeassistant/components/gios/translations/ru.json @@ -14,7 +14,6 @@ "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "station_id": "ID \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u043d\u0446\u0438\u0438" }, - "description": "\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u043e\u0437\u0434\u0443\u0445\u0430 \u043e\u0442 \u041f\u043e\u043b\u044c\u0441\u043a\u043e\u0439 \u0438\u043d\u0441\u043f\u0435\u043a\u0446\u0438\u0438 \u043f\u043e \u043e\u0445\u0440\u0430\u043d\u0435 \u043e\u043a\u0440\u0443\u0436\u0430\u044e\u0449\u0435\u0439 \u0441\u0440\u0435\u0434\u044b (GIO\u015a). \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438: https://www.home-assistant.io/integrations/gios.", "title": "GIO\u015a (\u041f\u043e\u043b\u044c\u0441\u043a\u0430\u044f \u0438\u043d\u0441\u043f\u0435\u043a\u0446\u0438\u044f \u043f\u043e \u043e\u0445\u0440\u0430\u043d\u0435 \u043e\u043a\u0440\u0443\u0436\u0430\u044e\u0449\u0435\u0439 \u0441\u0440\u0435\u0434\u044b)" } } diff --git a/homeassistant/components/gios/translations/sl.json b/homeassistant/components/gios/translations/sl.json index 4bbc28bfedd..bc8cac9941c 100644 --- a/homeassistant/components/gios/translations/sl.json +++ b/homeassistant/components/gios/translations/sl.json @@ -14,7 +14,6 @@ "name": "Ime integracije", "station_id": "ID merilne postaje" }, - "description": "Nastavite GIO\u015a (poljski glavni in\u0161pektorat za varstvo okolja) integracijo kakovosti zraka. \u010ce potrebujete pomo\u010d pri konfiguraciji si oglejte tukaj: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (glavni poljski in\u0161pektorat za varstvo okolja)" } } diff --git a/homeassistant/components/gios/translations/sv.json b/homeassistant/components/gios/translations/sv.json index a8bafa50119..98e9333c821 100644 --- a/homeassistant/components/gios/translations/sv.json +++ b/homeassistant/components/gios/translations/sv.json @@ -14,7 +14,6 @@ "name": "Integrationens namn", "station_id": "M\u00e4tstationens ID" }, - "description": "St\u00e4ll in luftkvalitetintegration f\u00f6r GIO\u015a (polsk chefinspektorat f\u00f6r milj\u00f6skydd). Om du beh\u00f6ver hj\u00e4lp med konfigurationen titta h\u00e4r: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polish Chief Inspectorate Of Environmental Protection)" } } diff --git a/homeassistant/components/gios/translations/tr.json b/homeassistant/components/gios/translations/tr.json index c0444ed99ed..e9419d53963 100644 --- a/homeassistant/components/gios/translations/tr.json +++ b/homeassistant/components/gios/translations/tr.json @@ -14,7 +14,6 @@ "name": "Ad", "station_id": "\u00d6l\u00e7\u00fcm istasyonunun kimli\u011fi" }, - "description": "GIO\u015a (Polonya \u00c7evre Koruma Ba\u015f M\u00fcfetti\u015fli\u011fi) hava kalitesi entegrasyonunu kurun. Yap\u0131land\u0131rmayla ilgili yard\u0131ma ihtiyac\u0131n\u0131z varsa buraya bak\u0131n: https://www.home-assistant.io/integrations/gios", "title": "GIO\u015a (Polonya \u00c7evre Koruma Ba\u015f M\u00fcfetti\u015fli\u011fi)" } } diff --git a/homeassistant/components/gios/translations/uk.json b/homeassistant/components/gios/translations/uk.json index f62408c5e8e..8151bf21809 100644 --- a/homeassistant/components/gios/translations/uk.json +++ b/homeassistant/components/gios/translations/uk.json @@ -14,7 +14,6 @@ "name": "\u041d\u0430\u0437\u0432\u0430", "station_id": "ID \u0432\u0438\u043c\u0456\u0440\u044e\u0432\u0430\u043b\u044c\u043d\u043e\u0457 \u0441\u0442\u0430\u043d\u0446\u0456\u0457" }, - "description": "\u0406\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u044f \u043f\u0440\u043e \u044f\u043a\u0456\u0441\u0442\u044c \u043f\u043e\u0432\u0456\u0442\u0440\u044f \u0432\u0456\u0434 \u041f\u043e\u043b\u044c\u0441\u044c\u043a\u043e\u0457 \u0456\u043d\u0441\u043f\u0435\u043a\u0446\u0456\u0457 \u0437 \u043e\u0445\u043e\u0440\u043e\u043d\u0438 \u043d\u0430\u0432\u043a\u043e\u043b\u0438\u0448\u043d\u044c\u043e\u0433\u043e \u0441\u0435\u0440\u0435\u0434\u043e\u0432\u0438\u0449\u0430 (GIO\u015a). \u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0454\u044e \u043f\u043e \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044e \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457: https://www.home-assistant.io/integrations/gios.", "title": "GIO\u015a (\u041f\u043e\u043b\u044c\u0441\u044c\u043a\u0430 \u0456\u043d\u0441\u043f\u0435\u043a\u0446\u0456\u044f \u0437 \u043e\u0445\u043e\u0440\u043e\u043d\u0438 \u043d\u0430\u0432\u043a\u043e\u043b\u0438\u0448\u043d\u044c\u043e\u0433\u043e \u0441\u0435\u0440\u0435\u0434\u043e\u0432\u0438\u0449\u0430)" } } diff --git a/homeassistant/components/gios/translations/zh-Hant.json b/homeassistant/components/gios/translations/zh-Hant.json index 98a62385ee1..209367511e7 100644 --- a/homeassistant/components/gios/translations/zh-Hant.json +++ b/homeassistant/components/gios/translations/zh-Hant.json @@ -14,7 +14,6 @@ "name": "\u540d\u7a31", "station_id": "\u76e3\u6e2c\u7ad9 ID" }, - "description": "\u8a2d\u5b9a GIO\u015a\uff08\u6ce2\u862d\u7e3d\u74b0\u5883\u4fdd\u8b77\u7763\u5bdf\u8655\uff09\u7a7a\u6c23\u54c1\u8cea\u6574\u5408\u3002\u5047\u5982\u9700\u8981\u5354\u52a9\uff0c\u8acb\u53c3\u8003\uff1ahttps://www.home-assistant.io/integrations/gios", "title": "GIO\u015a\uff08\u6ce2\u862d\u7e3d\u74b0\u5883\u4fdd\u8b77\u7763\u5bdf\u8655\uff09" } } diff --git a/homeassistant/components/goalzero/translations/ca.json b/homeassistant/components/goalzero/translations/ca.json index 3e7d6e714d3..d5ef3e9eda3 100644 --- a/homeassistant/components/goalzero/translations/ca.json +++ b/homeassistant/components/goalzero/translations/ca.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Es recomana que la reserva DHCP del router estigui configurada. Si no ho est\u00e0, pot ser que el dispositiu no estigui disponible mentre Home Assistant no detecti la nova IP. Consulta el manual del router.", - "title": "Goal Zero Yeti" + "description": "Es recomana que la reserva DHCP del router estigui configurada. Si no ho est\u00e0, pot ser que el dispositiu no estigui disponible mentre Home Assistant no detecti la nova IP. Consulta el manual del router." }, "user": { "data": { "host": "Amfitri\u00f3", "name": "Nom" }, - "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits.", - "title": "Goal Zero Yeti" + "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits." } } } diff --git a/homeassistant/components/goalzero/translations/cs.json b/homeassistant/components/goalzero/translations/cs.json index 2f2735f3c45..c8d0ab45fd6 100644 --- a/homeassistant/components/goalzero/translations/cs.json +++ b/homeassistant/components/goalzero/translations/cs.json @@ -15,8 +15,7 @@ "host": "Hostitel", "name": "Jm\u00e9no" }, - "description": "Nejprve si mus\u00edte st\u00e1hnout aplikaci Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\nPodle pokyn\u016f p\u0159ipojte za\u0159\u00edzen\u00ed Yeti k s\u00edti Wifi. Pot\u00e9 z\u00edskejte hostitelskou IP z routeru. Aby se IP hostitele nezm\u011bnila, mus\u00ed b\u00fdt v nastaven\u00ed routeru pro za\u0159\u00edzen\u00ed nastaven DHCP. Informace nalezenete v u\u017eivatelsk\u00e9 p\u0159\u00edru\u010dce k routeru.", - "title": "Goal Zero Yeti" + "description": "Nejprve si mus\u00edte st\u00e1hnout aplikaci Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\nPodle pokyn\u016f p\u0159ipojte za\u0159\u00edzen\u00ed Yeti k s\u00edti Wifi. Pot\u00e9 z\u00edskejte hostitelskou IP z routeru. Aby se IP hostitele nezm\u011bnila, mus\u00ed b\u00fdt v nastaven\u00ed routeru pro za\u0159\u00edzen\u00ed nastaven DHCP. Informace nalezenete v u\u017eivatelsk\u00e9 p\u0159\u00edru\u010dce k routeru." } } } diff --git a/homeassistant/components/goalzero/translations/de.json b/homeassistant/components/goalzero/translations/de.json index a9f7e3fa887..d41a2238854 100644 --- a/homeassistant/components/goalzero/translations/de.json +++ b/homeassistant/components/goalzero/translations/de.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Eine DHCP-Reservierung auf deinem Router wird empfohlen. Wenn sie nicht eingerichtet ist, ist das Ger\u00e4t m\u00f6glicherweise nicht mehr verf\u00fcgbar, bis Home Assistant die neue IP-Adresse erkennt. Schlage im Benutzerhandbuch deines Routers nach.", - "title": "Goal Zero Yeti" + "description": "Eine DHCP-Reservierung auf deinem Router wird empfohlen. Wenn sie nicht eingerichtet ist, ist das Ger\u00e4t m\u00f6glicherweise nicht mehr verf\u00fcgbar, bis Home Assistant die neue IP-Adresse erkennt. Schlage im Benutzerhandbuch deines Routers nach." }, "user": { "data": { "host": "Host", "name": "Name" }, - "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind.", - "title": "Goal Zero Yeti" + "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." } } } diff --git a/homeassistant/components/goalzero/translations/el.json b/homeassistant/components/goalzero/translations/el.json index 865106aa33c..6e793797046 100644 --- a/homeassistant/components/goalzero/translations/el.json +++ b/homeassistant/components/goalzero/translations/el.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "\u03a3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b7 \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2. \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03bc\u03b7\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03ad\u03c9\u03c2 \u03cc\u03c4\u03bf\u03c5 \u03c4\u03bf Home Assistant \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03c4\u03b7 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 ip. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2.", - "title": "Goal Zero Yeti" + "description": "\u03a3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b7 \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2. \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03bc\u03b7\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03ad\u03c9\u03c2 \u03cc\u03c4\u03bf\u03c5 \u03c4\u03bf Home Assistant \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03c4\u03b7 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 ip. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2." }, "user": { "data": { "host": "\u0394\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, - "description": "\u03a0\u03c1\u03ce\u03c4\u03b1, \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03c4\u03b5\u03b2\u03ac\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\n \u0391\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Yeti \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf Wi-Fi. \u03a3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b7 \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2. \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03bc\u03b7\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03ad\u03c9\u03c2 \u03cc\u03c4\u03bf\u03c5 \u03bf \u0392\u03bf\u03b7\u03b8\u03cc\u03c2 \u039f\u03b9\u03ba\u03af\u03b1\u03c2 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03c4\u03b7 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2.", - "title": "Goal Zero Yeti" + "description": "\u03a0\u03c1\u03ce\u03c4\u03b1, \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ba\u03b1\u03c4\u03b5\u03b2\u03ac\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Goal Zero: https://www.goalzero.com/product-features/yeti-app/ \n\n \u0391\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Yeti \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf Wi-Fi. \u03a3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9 \u03b7 \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2. \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b5\u03bd\u03b4\u03ad\u03c7\u03b5\u03c4\u03b1\u03b9 \u03bd\u03b1 \u03bc\u03b7\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03ad\u03c9\u03c2 \u03cc\u03c4\u03bf\u03c5 \u03bf \u0392\u03bf\u03b7\u03b8\u03cc\u03c2 \u039f\u03b9\u03ba\u03af\u03b1\u03c2 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03c4\u03b7 \u03bd\u03ad\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2." } } } diff --git a/homeassistant/components/goalzero/translations/en.json b/homeassistant/components/goalzero/translations/en.json index fd27892c794..7c7237ce0af 100644 --- a/homeassistant/components/goalzero/translations/en.json +++ b/homeassistant/components/goalzero/translations/en.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "DHCP reservation on your router is recommended. If not set up, the device may become unavailable until Home Assistant detects the new ip address. Refer to your router's user manual.", - "title": "Goal Zero Yeti" + "description": "DHCP reservation on your router is recommended. If not set up, the device may become unavailable until Home Assistant detects the new ip address. Refer to your router's user manual." }, "user": { "data": { "host": "Host", "name": "Name" }, - "description": "Please refer to the documentation to make sure all requirements are met.", - "title": "Goal Zero Yeti" + "description": "Please refer to the documentation to make sure all requirements are met." } } } diff --git a/homeassistant/components/goalzero/translations/es.json b/homeassistant/components/goalzero/translations/es.json index 921941d1c16..e02b06d32f5 100644 --- a/homeassistant/components/goalzero/translations/es.json +++ b/homeassistant/components/goalzero/translations/es.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Se recomienda reservar el DHCP en el router. Si no se configura, el dispositivo puede dejar de estar disponible hasta que el Home Assistant detecte la nueva direcci\u00f3n ip. Consulte el manual de usuario de su router.", - "title": "Goal Zero Yeti" + "description": "Se recomienda reservar el DHCP en el router. Si no se configura, el dispositivo puede dejar de estar disponible hasta que el Home Assistant detecte la nueva direcci\u00f3n ip. Consulte el manual de usuario de su router." }, "user": { "data": { "host": "Host", "name": "Nombre" }, - "description": "Primero, tienes que descargar la aplicaci\u00f3n Goal Zero: https://www.goalzero.com/product-features/yeti-app/\n\nSigue las instrucciones para conectar tu Yeti a tu red Wifi. Luego obt\u00e9n la IP de tu router. El DHCP debe estar configurado en los ajustes de tu router para asegurar que la IP de host del dispositivo no cambie. Consulta el manual de usuario de tu router.", - "title": "Goal Zero Yeti" + "description": "Primero, tienes que descargar la aplicaci\u00f3n Goal Zero: https://www.goalzero.com/product-features/yeti-app/\n\nSigue las instrucciones para conectar tu Yeti a tu red Wifi. Luego obt\u00e9n la IP de tu router. El DHCP debe estar configurado en los ajustes de tu router para asegurar que la IP de host del dispositivo no cambie. Consulta el manual de usuario de tu router." } } } diff --git a/homeassistant/components/goalzero/translations/et.json b/homeassistant/components/goalzero/translations/et.json index e529f0a3a4e..2947a5b82b6 100644 --- a/homeassistant/components/goalzero/translations/et.json +++ b/homeassistant/components/goalzero/translations/et.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Soovitatav on DHCP aadressi reserveerimine ruuteris. Kui seda pole seadistatud, v\u00f5ib seade osutuda k\u00e4ttesaamatuks kuni Home Assistant tuvastab uue IP-aadressi. Vaata ruuteri kasutusjuhendit.", - "title": "Goal Zero Yeti" + "description": "Soovitatav on DHCP aadressi reserveerimine ruuteris. Kui seda pole seadistatud, v\u00f5ib seade osutuda k\u00e4ttesaamatuks kuni Home Assistant tuvastab uue IP-aadressi. Vaata ruuteri kasutusjuhendit." }, "user": { "data": { "host": "", "name": "Nimi" }, - "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud.", - "title": "" + "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud." } } } diff --git a/homeassistant/components/goalzero/translations/fr.json b/homeassistant/components/goalzero/translations/fr.json index 430ad9927c4..4c2e987e096 100644 --- a/homeassistant/components/goalzero/translations/fr.json +++ b/homeassistant/components/goalzero/translations/fr.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "La r\u00e9servation DHCP sur votre routeur est recommand\u00e9e. S'il n'est pas configur\u00e9, l'appareil peut devenir indisponible jusqu'\u00e0 ce que Home Assistant d\u00e9tecte la nouvelle adresse IP. Reportez-vous au manuel d'utilisation de votre routeur.", - "title": "Objectif Z\u00e9ro Y\u00e9ti" + "description": "La r\u00e9servation DHCP sur votre routeur est recommand\u00e9e. S'il n'est pas configur\u00e9, l'appareil peut devenir indisponible jusqu'\u00e0 ce que Home Assistant d\u00e9tecte la nouvelle adresse IP. Reportez-vous au manuel d'utilisation de votre routeur." }, "user": { "data": { "host": "H\u00f4te", "name": "Nom" }, - "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es.", - "title": "Goal Zero Yeti" + "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es." } } } diff --git a/homeassistant/components/goalzero/translations/hu.json b/homeassistant/components/goalzero/translations/hu.json index 00196952e0c..c0376fc29b9 100644 --- a/homeassistant/components/goalzero/translations/hu.json +++ b/homeassistant/components/goalzero/translations/hu.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "DHCP foglal\u00e1s aj\u00e1nlott az routeren. Ha nincs be\u00e1ll\u00edtva, akkor az eszk\u00f6z el\u00e9rhetetlenn\u00e9 v\u00e1lhat, am\u00edg Home Assistant \u00e9szleli az \u00faj IP-c\u00edmet. Olvassa el az router felhaszn\u00e1l\u00f3i k\u00e9zik\u00f6nyv\u00e9t.", - "title": "Goal Zero Yeti" + "description": "DHCP foglal\u00e1s aj\u00e1nlott az routeren. Ha nincs be\u00e1ll\u00edtva, akkor az eszk\u00f6z el\u00e9rhetetlenn\u00e9 v\u00e1lhat, am\u00edg Home Assistant \u00e9szleli az \u00faj IP-c\u00edmet. Olvassa el az router felhaszn\u00e1l\u00f3i k\u00e9zik\u00f6nyv\u00e9t." }, "user": { "data": { "host": "C\u00edm", "name": "Elnevez\u00e9s" }, - "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl.", - "title": "Goal Zero Yeti" + "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl." } } } diff --git a/homeassistant/components/goalzero/translations/id.json b/homeassistant/components/goalzero/translations/id.json index d524b13bb0c..c34fd9edfd9 100644 --- a/homeassistant/components/goalzero/translations/id.json +++ b/homeassistant/components/goalzero/translations/id.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Dianjurkan untuk menggunakan reservasi DHCP pada router Anda. Jika tidak diatur, perangkat mungkin tidak tersedia hingga Home Assistant mendeteksi alamat IP baru. Lihat panduan pengguna router Anda.", - "title": "Goal Zero Yeti" + "description": "Dianjurkan untuk menggunakan reservasi DHCP pada router Anda. Jika tidak diatur, perangkat mungkin tidak tersedia hingga Home Assistant mendeteksi alamat IP baru. Lihat panduan pengguna router Anda." }, "user": { "data": { "host": "Host", "name": "Nama" }, - "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi.", - "title": "Goal Zero Yeti" + "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi." } } } diff --git a/homeassistant/components/goalzero/translations/it.json b/homeassistant/components/goalzero/translations/it.json index dbd71c82f4a..af88e8713e8 100644 --- a/homeassistant/components/goalzero/translations/it.json +++ b/homeassistant/components/goalzero/translations/it.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Si consiglia la prenotazione DHCP sul router. Se non configurato, il dispositivo potrebbe non essere disponibile fino a quando Home Assistant non rileva il nuovo indirizzo IP. Fai riferimento al manuale utente del router.", - "title": "Goal Zero Yeti" + "description": "Si consiglia la prenotazione DHCP sul router. Se non configurato, il dispositivo potrebbe non essere disponibile fino a quando Home Assistant non rileva il nuovo indirizzo IP. Fai riferimento al manuale utente del router." }, "user": { "data": { "host": "Host", "name": "Nome" }, - "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti.", - "title": "Goal Zero Yeti" + "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti." } } } diff --git a/homeassistant/components/goalzero/translations/ja.json b/homeassistant/components/goalzero/translations/ja.json index 3e2e33bc302..72818b6627f 100644 --- a/homeassistant/components/goalzero/translations/ja.json +++ b/homeassistant/components/goalzero/translations/ja.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04(DHCP reservation)\u3092\u304a\u52e7\u3081\u3057\u307e\u3059\u3002\u3053\u306e\u8a2d\u5b9a\u3092\u884c\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u306f\u3001Home Assistant\u304c\u65b0\u3057\u3044IP\u30a2\u30c9\u30ec\u30b9\u3092\u691c\u51fa\u3059\u308b\u307e\u3067\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Goal Zero Yeti" + "description": "\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04(DHCP reservation)\u3092\u304a\u52e7\u3081\u3057\u307e\u3059\u3002\u3053\u306e\u8a2d\u5b9a\u3092\u884c\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u306f\u3001Home Assistant\u304c\u65b0\u3057\u3044IP\u30a2\u30c9\u30ec\u30b9\u3092\u691c\u51fa\u3059\u308b\u307e\u3067\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "user": { "data": { "host": "\u30db\u30b9\u30c8", "name": "\u540d\u524d" }, - "description": "\u307e\u305a\u3001Goal Zero app\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: https://www.goalzero.com/product-features/yeti-app/\n\n\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04(DHCP reservation)\u3092\u304a\u52e7\u3081\u3057\u307e\u3059\u3002\u3053\u306e\u8a2d\u5b9a\u3092\u884c\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u306f\u3001Home Assistant\u304c\u65b0\u3057\u3044IP\u30a2\u30c9\u30ec\u30b9\u3092\u691c\u51fa\u3059\u308b\u307e\u3067\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Goal Zero Yeti" + "description": "\u307e\u305a\u3001Goal Zero app\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059: https://www.goalzero.com/product-features/yeti-app/\n\n\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04(DHCP reservation)\u3092\u304a\u52e7\u3081\u3057\u307e\u3059\u3002\u3053\u306e\u8a2d\u5b9a\u3092\u884c\u3063\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u306f\u3001Home Assistant\u304c\u65b0\u3057\u3044IP\u30a2\u30c9\u30ec\u30b9\u3092\u691c\u51fa\u3059\u308b\u307e\u3067\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } } diff --git a/homeassistant/components/goalzero/translations/ko.json b/homeassistant/components/goalzero/translations/ko.json index d5119363002..dd947530c16 100644 --- a/homeassistant/components/goalzero/translations/ko.json +++ b/homeassistant/components/goalzero/translations/ko.json @@ -14,8 +14,7 @@ "host": "\ud638\uc2a4\ud2b8", "name": "\uc774\ub984" }, - "description": "\uba3c\uc800 Goal Zero \uc571\uc744 \ub2e4\uc6b4\ub85c\ub4dc\ud574\uc57c \ud569\ub2c8\ub2e4. https://www.goalzero.com/product-features/yeti-app/\n\n\uc9c0\uce68\uc5d0 \ub530\ub77c Yeti\ub97c Wifi \ub124\ud2b8\uc6cc\ud06c\uc5d0 \uc5f0\uacb0\ud55c \ub2e4\uc74c \ub77c\uc6b0\ud130\uc5d0\uc11c \ud638\uc2a4\ud2b8 IP\ub97c \uac00\uc838\uc640\uc8fc\uc138\uc694. \ud638\uc2a4\ud2b8 IP\uac00 \ubcc0\uacbd\ub418\uc9c0 \uc54a\ub3c4\ub85d \ud558\ub824\uba74 \uae30\uae30\uc5d0 \ub300\ud574 \ub77c\uc6b0\ud130\uc5d0\uc11c DHCP\ub97c \uc54c\ub9de\uac8c \uc124\uc815\ud574\uc8fc\uc5b4\uc57c \ud569\ub2c8\ub2e4. \ud574\ub2f9 \ub0b4\uc6a9\uc5d0 \ub300\ud574\uc11c\ub294 \ub77c\uc6b0\ud130\uc758 \uc0ac\uc6a9\uc790 \uc124\uba85\uc11c\ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.", - "title": "Goal Zero Yeti" + "description": "\uba3c\uc800 Goal Zero \uc571\uc744 \ub2e4\uc6b4\ub85c\ub4dc\ud574\uc57c \ud569\ub2c8\ub2e4. https://www.goalzero.com/product-features/yeti-app/\n\n\uc9c0\uce68\uc5d0 \ub530\ub77c Yeti\ub97c Wifi \ub124\ud2b8\uc6cc\ud06c\uc5d0 \uc5f0\uacb0\ud55c \ub2e4\uc74c \ub77c\uc6b0\ud130\uc5d0\uc11c \ud638\uc2a4\ud2b8 IP\ub97c \uac00\uc838\uc640\uc8fc\uc138\uc694. \ud638\uc2a4\ud2b8 IP\uac00 \ubcc0\uacbd\ub418\uc9c0 \uc54a\ub3c4\ub85d \ud558\ub824\uba74 \uae30\uae30\uc5d0 \ub300\ud574 \ub77c\uc6b0\ud130\uc5d0\uc11c DHCP\ub97c \uc54c\ub9de\uac8c \uc124\uc815\ud574\uc8fc\uc5b4\uc57c \ud569\ub2c8\ub2e4. \ud574\ub2f9 \ub0b4\uc6a9\uc5d0 \ub300\ud574\uc11c\ub294 \ub77c\uc6b0\ud130\uc758 \uc0ac\uc6a9\uc790 \uc124\uba85\uc11c\ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/goalzero/translations/lb.json b/homeassistant/components/goalzero/translations/lb.json index 87a5590bfe4..203b70a269c 100644 --- a/homeassistant/components/goalzero/translations/lb.json +++ b/homeassistant/components/goalzero/translations/lb.json @@ -14,8 +14,7 @@ "host": "Host", "name": "Numm" }, - "description": "Fir d'\u00e9ischt muss Goal Zero App erofgeluede ginn:\nhttps://www.goalzero.com/product-features/yeti-app/\n\nFolleg d'Instruktioune fir d\u00e4in Yeti mat dengem Wifi ze verbannen. Dann erm\u00ebttel d'IP vum Yeti an dengem Router. DHCP muss aktiv sinn an de Yeti Apparat sollt \u00ebmmer d\u00e9iselwecht IP zougewise kr\u00e9ien. Kuck dat am Guide vun dengen Router Astellungen no.", - "title": "Goal Zero Yeti" + "description": "Fir d'\u00e9ischt muss Goal Zero App erofgeluede ginn:\nhttps://www.goalzero.com/product-features/yeti-app/\n\nFolleg d'Instruktioune fir d\u00e4in Yeti mat dengem Wifi ze verbannen. Dann erm\u00ebttel d'IP vum Yeti an dengem Router. DHCP muss aktiv sinn an de Yeti Apparat sollt \u00ebmmer d\u00e9iselwecht IP zougewise kr\u00e9ien. Kuck dat am Guide vun dengen Router Astellungen no." } } } diff --git a/homeassistant/components/goalzero/translations/nl.json b/homeassistant/components/goalzero/translations/nl.json index 680ff1a10cc..9f719d74efe 100644 --- a/homeassistant/components/goalzero/translations/nl.json +++ b/homeassistant/components/goalzero/translations/nl.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "DHCP-reservering op uw router wordt aanbevolen. Als dit niet het geval is, is het apparaat mogelijk niet meer beschikbaar totdat Home Assistant het nieuwe IP-adres detecteert. Raadpleeg de gebruikershandleiding van uw router.", - "title": "Goal Zero Yeti" + "description": "DHCP-reservering op uw router wordt aanbevolen. Als dit niet het geval is, is het apparaat mogelijk niet meer beschikbaar totdat Home Assistant het nieuwe IP-adres detecteert. Raadpleeg de gebruikershandleiding van uw router." }, "user": { "data": { "host": "Host", "name": "Naam" }, - "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten wordt voldaan.", - "title": "Goal Zero Yeti" + "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten wordt voldaan." } } } diff --git a/homeassistant/components/goalzero/translations/no.json b/homeassistant/components/goalzero/translations/no.json index 6bb00952fab..9c2da19b3e1 100644 --- a/homeassistant/components/goalzero/translations/no.json +++ b/homeassistant/components/goalzero/translations/no.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "DHCP-reservasjon p\u00e5 ruteren din anbefales. Hvis den ikke er konfigurert, kan enheten bli utilgjengelig til Home Assistant oppdager den nye ip-adressen. Se i brukerh\u00e5ndboken til ruteren.", - "title": "Goal Zero Yeti" + "description": "DHCP-reservasjon p\u00e5 ruteren din anbefales. Hvis den ikke er konfigurert, kan enheten bli utilgjengelig til Home Assistant oppdager den nye ip-adressen. Se i brukerh\u00e5ndboken til ruteren." }, "user": { "data": { "host": "Vert", "name": "Navn" }, - "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt.", - "title": "" + "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt." } } } diff --git a/homeassistant/components/goalzero/translations/pl.json b/homeassistant/components/goalzero/translations/pl.json index ee8f2022a0d..91b73044f11 100644 --- a/homeassistant/components/goalzero/translations/pl.json +++ b/homeassistant/components/goalzero/translations/pl.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Zaleca si\u0119 rezerwacj\u0119 DHCP w ustawieniach routera. Je\u015bli tego nie ustawisz, urz\u0105dzenie mo\u017ce sta\u0107 si\u0119 niedost\u0119pne, do czasu a\u017c Home Assistant wykryje nowy adres IP. Post\u0119puj wg instrukcji obs\u0142ugi routera.", - "title": "Goal Zero Yeti" + "description": "Zaleca si\u0119 rezerwacj\u0119 DHCP w ustawieniach routera. Je\u015bli tego nie ustawisz, urz\u0105dzenie mo\u017ce sta\u0107 si\u0119 niedost\u0119pne, do czasu a\u017c Home Assistant wykryje nowy adres IP. Post\u0119puj wg instrukcji obs\u0142ugi routera." }, "user": { "data": { "host": "Nazwa hosta lub adres IP", "name": "Nazwa" }, - "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione.", - "title": "Goal Zero Yeti" + "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione." } } } diff --git a/homeassistant/components/goalzero/translations/pt-BR.json b/homeassistant/components/goalzero/translations/pt-BR.json index fa67129b08a..7561acac487 100644 --- a/homeassistant/components/goalzero/translations/pt-BR.json +++ b/homeassistant/components/goalzero/translations/pt-BR.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "A reserva de DHCP em seu roteador \u00e9 recomendada. Se n\u00e3o estiver configurado, o dispositivo pode ficar indispon\u00edvel at\u00e9 que o Home Assistant detecte o novo endere\u00e7o IP. Consulte o manual do usu\u00e1rio do seu roteador.", - "title": "Gol Zero Yeti" + "description": "A reserva de DHCP em seu roteador \u00e9 recomendada. Se n\u00e3o estiver configurado, o dispositivo pode ficar indispon\u00edvel at\u00e9 que o Home Assistant detecte o novo endere\u00e7o IP. Consulte o manual do usu\u00e1rio do seu roteador." }, "user": { "data": { "host": "Nome do host", "name": "Nome" }, - "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos.", - "title": "Gol Zero Yeti" + "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos." } } } diff --git a/homeassistant/components/goalzero/translations/ru.json b/homeassistant/components/goalzero/translations/ru.json index ca114f6d92e..7bd8c3df311 100644 --- a/homeassistant/components/goalzero/translations/ru.json +++ b/homeassistant/components/goalzero/translations/ru.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0438\u043c\u0438, \u0447\u0442\u043e\u0431\u044b IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 Home Assistant \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u043d\u043e\u0432\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043a \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u0440\u043e\u0443\u0442\u0435\u0440\u0430.", - "title": "Goal Zero Yeti" + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0442\u0430\u043a\u0438\u043c\u0438, \u0447\u0442\u043e\u0431\u044b IP-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u043b\u0441\u044f \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 Home Assistant \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442 \u043d\u043e\u0432\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u043a \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0412\u0430\u0448\u0435\u0433\u043e \u0440\u043e\u0443\u0442\u0435\u0440\u0430." }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b.", - "title": "Goal Zero Yeti" + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b." } } } diff --git a/homeassistant/components/goalzero/translations/tr.json b/homeassistant/components/goalzero/translations/tr.json index 9be07def514..db04511a8f5 100644 --- a/homeassistant/components/goalzero/translations/tr.json +++ b/homeassistant/components/goalzero/translations/tr.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "Y\u00f6nlendiricinizde DHCP rezervasyonu yap\u0131lmas\u0131 \u00f6nerilir. Kurulmazsa, Home Assistant yeni ip adresini alg\u0131layana kadar cihaz kullan\u0131lamayabilir. Y\u00f6nlendiricinizin kullan\u0131m k\u0131lavuzuna bak\u0131n.", - "title": "Goal Zero Yeti" + "description": "Y\u00f6nlendiricinizde DHCP rezervasyonu yap\u0131lmas\u0131 \u00f6nerilir. Kurulmazsa, Home Assistant yeni ip adresini alg\u0131layana kadar cihaz kullan\u0131lamayabilir. Y\u00f6nlendiricinizin kullan\u0131m k\u0131lavuzuna bak\u0131n." }, "user": { "data": { "host": "Sunucu", "name": "Ad" }, - "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n.", - "title": "Goal Zero Yeti" + "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n." } } } diff --git a/homeassistant/components/goalzero/translations/uk.json b/homeassistant/components/goalzero/translations/uk.json index 6d67d949c28..6232a0af027 100644 --- a/homeassistant/components/goalzero/translations/uk.json +++ b/homeassistant/components/goalzero/translations/uk.json @@ -14,8 +14,7 @@ "host": "\u0425\u043e\u0441\u0442", "name": "\u041d\u0430\u0437\u0432\u0430" }, - "description": "\u0421\u043f\u043e\u0447\u0430\u0442\u043a\u0443 \u0412\u0430\u043c \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0437\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0434\u043e\u0434\u0430\u0442\u043e\u043a Goal Zero: https://www.goalzero.com/product-features/yeti-app/. \n\n \u0414\u043e\u0442\u0440\u0438\u043c\u0443\u0439\u0442\u0435\u0441\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0439 \u043f\u043e \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044e Yeti \u0434\u043e \u043c\u0435\u0440\u0435\u0436\u0456 WiFi. \u041f\u043e\u0442\u0456\u043c \u0434\u0456\u0437\u043d\u0430\u0439\u0442\u0435\u0441\u044f IP \u0430\u0434\u0440\u0435\u0441\u0443 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e, \u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434, \u0437 \u0412\u0430\u0448\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430. \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043f\u043e\u0432\u0438\u043d\u043d\u0456 \u0431\u0443\u0442\u0438 \u0442\u0430\u043a\u0438\u043c\u0438, \u0449\u043e\u0431 IP \u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e \u043d\u0435 \u0437\u043c\u0456\u043d\u044e\u0432\u0430\u043b\u0430\u0441\u044c \u0437 \u0447\u0430\u0441\u043e\u043c. \u041f\u0440\u043e \u0442\u0435, \u044f\u043a \u0446\u0435 \u0437\u0440\u043e\u0431\u0438\u0442\u0438, \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u0432 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0457 \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430 \u0412\u0430\u0448\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430.", - "title": "Goal Zero Yeti" + "description": "\u0421\u043f\u043e\u0447\u0430\u0442\u043a\u0443 \u0412\u0430\u043c \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0437\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0434\u043e\u0434\u0430\u0442\u043e\u043a Goal Zero: https://www.goalzero.com/product-features/yeti-app/. \n\n \u0414\u043e\u0442\u0440\u0438\u043c\u0443\u0439\u0442\u0435\u0441\u044c \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0439 \u043f\u043e \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044e Yeti \u0434\u043e \u043c\u0435\u0440\u0435\u0436\u0456 WiFi. \u041f\u043e\u0442\u0456\u043c \u0434\u0456\u0437\u043d\u0430\u0439\u0442\u0435\u0441\u044f IP \u0430\u0434\u0440\u0435\u0441\u0443 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e, \u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434, \u0437 \u0412\u0430\u0448\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430. \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043f\u043e\u0432\u0438\u043d\u043d\u0456 \u0431\u0443\u0442\u0438 \u0442\u0430\u043a\u0438\u043c\u0438, \u0449\u043e\u0431 IP \u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e \u043d\u0435 \u0437\u043c\u0456\u043d\u044e\u0432\u0430\u043b\u0430\u0441\u044c \u0437 \u0447\u0430\u0441\u043e\u043c. \u041f\u0440\u043e \u0442\u0435, \u044f\u043a \u0446\u0435 \u0437\u0440\u043e\u0431\u0438\u0442\u0438, \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u0432 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0457 \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430 \u0412\u0430\u0448\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430." } } } diff --git a/homeassistant/components/goalzero/translations/zh-Hant.json b/homeassistant/components/goalzero/translations/zh-Hant.json index 4f5b01fb9d4..2e5efb73350 100644 --- a/homeassistant/components/goalzero/translations/zh-Hant.json +++ b/homeassistant/components/goalzero/translations/zh-Hant.json @@ -12,16 +12,14 @@ }, "step": { "confirm_discovery": { - "description": "\u5efa\u8b70\u65bc\u8def\u7531\u5668\u7684 DHCP \u8a2d\u5b9a\u4e2d\u4fdd\u7559\u56fa\u5b9a IP\uff0c\u5047\u5982\u672a\u8a2d\u5b9a\u3001\u88dd\u7f6e\u53ef\u80fd\u6703\u5728 Home Assistant \u5075\u6e2c\u5230\u65b0 IP \u4e4b\u524d\u8b8a\u6210\u7121\u6cd5\u4f7f\u7528\u3002\u8acb\u53c3\u95b1\u8def\u7531\u5668\u624b\u518a\u4e86\u89e3\u5982\u4f55\u8a2d\u5b9a\u3002", - "title": "Goal Zero Yeti" + "description": "\u5efa\u8b70\u65bc\u8def\u7531\u5668\u7684 DHCP \u8a2d\u5b9a\u4e2d\u4fdd\u7559\u56fa\u5b9a IP\uff0c\u5047\u5982\u672a\u8a2d\u5b9a\u3001\u88dd\u7f6e\u53ef\u80fd\u6703\u5728 Home Assistant \u5075\u6e2c\u5230\u65b0 IP \u4e4b\u524d\u8b8a\u6210\u7121\u6cd5\u4f7f\u7528\u3002\u8acb\u53c3\u95b1\u8def\u7531\u5668\u624b\u518a\u4e86\u89e3\u5982\u4f55\u8a2d\u5b9a\u3002" }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", "name": "\u540d\u7a31" }, - "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002", - "title": "Goal Zero Yeti" + "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002" } } } diff --git a/homeassistant/components/google/translations/ko.json b/homeassistant/components/google/translations/ko.json new file mode 100644 index 00000000000..2906ebc5114 --- /dev/null +++ b/homeassistant/components/google/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/group/translations/bg.json b/homeassistant/components/group/translations/bg.json index 584d48e3f28..6be0657c774 100644 --- a/homeassistant/components/group/translations/bg.json +++ b/homeassistant/components/group/translations/bg.json @@ -8,22 +8,6 @@ }, "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" }, - "cover": { - "data": { - "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" - } - }, - "fan": { - "data": { - "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" - } - }, - "light": { - "data": { - "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", - "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" - } - }, "lock": { "data": { "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435", @@ -32,8 +16,7 @@ }, "media_player": { "data": { - "name": "\u0418\u043c\u0435 \u043d\u0430 \u0433\u0440\u0443\u043f\u0430", - "title": "\u041d\u043e\u0432\u0430 \u0433\u0440\u0443\u043f\u0430" + "name": "\u0418\u043c\u0435 \u043d\u0430 \u0433\u0440\u0443\u043f\u0430" } }, "switch": { @@ -56,42 +39,21 @@ "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } }, - "binary_sensor_options": { - "data": { - "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" - } - }, "cover": { "data": { "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } }, - "cover_options": { - "data": { - "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" - } - }, "fan": { "data": { "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } }, - "fan_options": { - "data": { - "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" - } - }, "light": { "data": { "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } }, - "light_options": { - "data": { - "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", - "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" - } - }, "lock": { "data": { "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" @@ -102,11 +64,6 @@ "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" } }, - "media_player_options": { - "data": { - "entities": "\u0427\u043b\u0435\u043d\u043e\u0432\u0435" - } - }, "switch": { "data": { "all": "\u0412\u0441\u0438\u0447\u043a\u0438 \u043e\u0431\u0435\u043a\u0442\u0438", diff --git a/homeassistant/components/group/translations/ca.json b/homeassistant/components/group/translations/ca.json index bd824fc7428..bb9fba7e184 100644 --- a/homeassistant/components/group/translations/ca.json +++ b/homeassistant/components/group/translations/ca.json @@ -15,57 +15,26 @@ "data": { "entities": "Membres", "hide_members": "Amaga membres", - "name": "Nom", - "title": "Afegeix grup" + "name": "Nom" }, - "description": "Selecciona les opcions del grup", "title": "Afegeix grup" }, - "cover_options": { - "data": { - "entities": "Membres del grup" - }, - "description": "Selecciona les opcions del grup" - }, "fan": { "data": { "entities": "Membres", "hide_members": "Amaga membres", - "name": "Nom", - "title": "Afegeix grup" + "name": "Nom" }, - "description": "Selecciona les opcions del grup", "title": "Afegeix grup" }, - "fan_options": { - "data": { - "entities": "Membres del grup" - }, - "description": "Selecciona les opcions del grup" - }, - "init": { - "data": { - "group_type": "Tipus de grup" - }, - "description": "Selecciona el tipus de grup" - }, "light": { "data": { - "all": "Totes les entitats", "entities": "Membres", "hide_members": "Amaga membres", - "name": "Nom", - "title": "Afegeix grup" + "name": "Nom" }, - "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre.", "title": "Afegeix grup" }, - "light_options": { - "data": { - "entities": "Membres del grup" - }, - "description": "Selecciona les opcions del grup" - }, "lock": { "data": { "entities": "Membres", @@ -78,18 +47,10 @@ "data": { "entities": "Membres", "hide_members": "Amaga membres", - "name": "Nom", - "title": "Afegeix grup" + "name": "Nom" }, - "description": "Selecciona les opcions del grup", "title": "Afegeix grup" }, - "media_player_options": { - "data": { - "entities": "Membres del grup" - }, - "description": "Selecciona les opcions del grup" - }, "switch": { "data": { "entities": "Membres", @@ -99,9 +60,6 @@ "title": "Afegeix grup" }, "user": { - "data": { - "group_type": "Tipus de grup" - }, "description": "Els grups et permeten crear una nova entitat que representa m\u00faltiples entitats del mateix tipus.", "menu_options": { "binary_sensor": "Grup de sensors binaris", @@ -126,34 +84,18 @@ }, "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre." }, - "binary_sensor_options": { - "data": { - "all": "Totes les entitats", - "entities": "Membres" - } - }, "cover": { "data": { "entities": "Membres", "hide_members": "Amaga membres" } }, - "cover_options": { - "data": { - "entities": "Membres" - } - }, "fan": { "data": { "entities": "Membres", "hide_members": "Amaga membres" } }, - "fan_options": { - "data": { - "entities": "Membres" - } - }, "light": { "data": { "all": "Totes les entitats", @@ -162,12 +104,6 @@ }, "description": "Si \"totes les entitats\" est\u00e0 activat, l'estat del grup estar\u00e0 activat (ON) si tots els membres estan activats. Si \"totes les entitats\" est\u00e0 desactivat, l'estat del grup s'activar\u00e0 si hi ha activat qualsevol membre." }, - "light_options": { - "data": { - "all": "Totes les entitats", - "entities": "Membres" - } - }, "lock": { "data": { "entities": "Membres", @@ -180,11 +116,6 @@ "hide_members": "Amaga membres" } }, - "media_player_options": { - "data": { - "entities": "Membres" - } - }, "switch": { "data": { "all": "Totes les entitats", diff --git a/homeassistant/components/group/translations/cs.json b/homeassistant/components/group/translations/cs.json index 232dbe9e629..9d24e3580a2 100644 --- a/homeassistant/components/group/translations/cs.json +++ b/homeassistant/components/group/translations/cs.json @@ -15,57 +15,26 @@ "data": { "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny", - "name": "Jm\u00e9no", - "title": "Nov\u00e1 skupina" + "name": "Jm\u00e9no" }, - "description": "Vyberte mo\u017enosti skupiny", "title": "Nov\u00e1 skupina" }, - "cover_options": { - "data": { - "entities": "\u010clenov\u00e9 skupiny" - }, - "description": "Vyberte mo\u017enosti skupiny" - }, "fan": { "data": { "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny", - "name": "Jm\u00e9no", - "title": "Nov\u00e1 skupina" + "name": "Jm\u00e9no" }, - "description": "Vyberte mo\u017enosti skupiny", "title": "Nov\u00e1 skupina" }, - "fan_options": { - "data": { - "entities": "\u010clenov\u00e9 skupiny" - }, - "description": "Vyberte mo\u017enosti skupiny" - }, - "init": { - "data": { - "group_type": "Typ skupiny" - }, - "description": "Vyberte typ skupiny" - }, "light": { "data": { - "all": "V\u0161echny entity", "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny", - "name": "Jm\u00e9no", - "title": "Nov\u00e1 skupina" + "name": "Jm\u00e9no" }, - "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen.", "title": "Nov\u00e1 skupina" }, - "light_options": { - "data": { - "entities": "\u010clenov\u00e9 skupiny" - }, - "description": "Vyberte mo\u017enosti skupiny" - }, "lock": { "data": { "entities": "\u010clenov\u00e9", @@ -78,18 +47,10 @@ "data": { "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny", - "name": "Jm\u00e9no", - "title": "Nov\u00e1 skupina" + "name": "Jm\u00e9no" }, - "description": "Vyberte mo\u017enosti skupiny", "title": "Nov\u00e1 skupina" }, - "media_player_options": { - "data": { - "entities": "\u010clenov\u00e9 skupiny" - }, - "description": "Vyberte mo\u017enosti skupiny" - }, "switch": { "data": { "entities": "\u010clenov\u00e9", @@ -99,9 +60,6 @@ "title": "Nov\u00e1 skupina" }, "user": { - "data": { - "group_type": "Typ skupiny" - }, "description": "Vyberte typ skupiny", "menu_options": { "binary_sensor": "Skupina bin\u00e1rn\u00edch senzor\u016f", @@ -126,34 +84,18 @@ }, "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen." }, - "binary_sensor_options": { - "data": { - "all": "V\u0161echny entity", - "entities": "\u010clenov\u00e9" - } - }, "cover": { "data": { "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny" } }, - "cover_options": { - "data": { - "entities": "\u010clenov\u00e9" - } - }, "fan": { "data": { "entities": "\u010clenov\u00e9", "hide_members": "Skr\u00fdt \u010dleny" } }, - "fan_options": { - "data": { - "entities": "\u010clenov\u00e9" - } - }, "light": { "data": { "all": "V\u0161echny entity", @@ -162,12 +104,6 @@ }, "description": "Pokud je povoleno \"v\u0161echny entity\", je stav skupiny zapnut\u00fd pouze tehdy, pokud jsou zapnuti v\u0161ichni \u010dlenov\u00e9.\nPokud je mo\u017enost \"v\u0161echny entity\" zak\u00e1z\u00e1na, je stav skupiny zapnut\u00fd, pokud je zapnut\u00fd kter\u00fdkoli \u010dlen." }, - "light_options": { - "data": { - "all": "V\u0161echny entity", - "entities": "\u010clenov\u00e9" - } - }, "lock": { "data": { "entities": "\u010clenov\u00e9", @@ -180,11 +116,6 @@ "hide_members": "Skr\u00fdt \u010dleny" } }, - "media_player_options": { - "data": { - "entities": "\u010clenov\u00e9" - } - }, "switch": { "data": { "all": "V\u0161echny entity", diff --git a/homeassistant/components/group/translations/de.json b/homeassistant/components/group/translations/de.json index c76c92ccb19..00c036d18ee 100644 --- a/homeassistant/components/group/translations/de.json +++ b/homeassistant/components/group/translations/de.json @@ -15,57 +15,26 @@ "data": { "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden", - "name": "Name", - "title": "Gruppe hinzuf\u00fcgen" + "name": "Name" }, - "description": "Gruppenoptionen ausw\u00e4hlen", "title": "Gruppe hinzuf\u00fcgen" }, - "cover_options": { - "data": { - "entities": "Gruppenmitglieder" - }, - "description": "Gruppenoptionen ausw\u00e4hlen" - }, "fan": { "data": { "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden", - "name": "Name", - "title": "Gruppe hinzuf\u00fcgen" + "name": "Name" }, - "description": "Gruppenoptionen ausw\u00e4hlen", "title": "Gruppe hinzuf\u00fcgen" }, - "fan_options": { - "data": { - "entities": "Gruppenmitglieder" - }, - "description": "Gruppenoptionen ausw\u00e4hlen" - }, - "init": { - "data": { - "group_type": "Gruppentyp" - }, - "description": "Gruppentyp ausw\u00e4hlen" - }, "light": { "data": { - "all": "Alle Entit\u00e4ten", "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden", - "name": "Name", - "title": "Gruppe hinzuf\u00fcgen" + "name": "Name" }, - "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist.", "title": "Gruppe hinzuf\u00fcgen" }, - "light_options": { - "data": { - "entities": "Gruppenmitglieder" - }, - "description": "Gruppenoptionen ausw\u00e4hlen" - }, "lock": { "data": { "entities": "Mitglieder", @@ -78,18 +47,10 @@ "data": { "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden", - "name": "Name", - "title": "Gruppe hinzuf\u00fcgen" + "name": "Name" }, - "description": "Gruppenoptionen ausw\u00e4hlen", "title": "Gruppe hinzuf\u00fcgen" }, - "media_player_options": { - "data": { - "entities": "Gruppenmitglieder" - }, - "description": "Gruppenoptionen ausw\u00e4hlen" - }, "switch": { "data": { "entities": "Mitglieder", @@ -99,9 +60,6 @@ "title": "Gruppe hinzuf\u00fcgen" }, "user": { - "data": { - "group_type": "Gruppentyp" - }, "description": "Mit Gruppen kannst du eine neue Entit\u00e4t erstellen, die mehrere Entit\u00e4ten desselben Typs darstellt.", "menu_options": { "binary_sensor": "Bin\u00e4rer Sensor-Gruppe", @@ -126,34 +84,18 @@ }, "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist." }, - "binary_sensor_options": { - "data": { - "all": "Alle Entit\u00e4ten", - "entities": "Mitglieder" - } - }, "cover": { "data": { "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden" } }, - "cover_options": { - "data": { - "entities": "Mitglieder" - } - }, "fan": { "data": { "entities": "Mitglieder", "hide_members": "Mitglieder ausblenden" } }, - "fan_options": { - "data": { - "entities": "Mitglieder" - } - }, "light": { "data": { "all": "Alle Entit\u00e4ten", @@ -162,12 +104,6 @@ }, "description": "Wenn \"alle Entit\u00e4ten\" aktiviert ist, ist der Status der Gruppe nur dann eingeschaltet, wenn alle Mitglieder eingeschaltet sind. Wenn \"alle Entit\u00e4ten\" deaktiviert ist, ist der Status der Gruppe eingeschaltet, wenn irgendein Mitglied eingeschaltet ist." }, - "light_options": { - "data": { - "all": "Alle Entit\u00e4ten", - "entities": "Mitglieder" - } - }, "lock": { "data": { "entities": "Mitglieder", @@ -180,11 +116,6 @@ "hide_members": "Mitglieder ausblenden" } }, - "media_player_options": { - "data": { - "entities": "Mitglieder" - } - }, "switch": { "data": { "all": "Alle Entit\u00e4ten", diff --git a/homeassistant/components/group/translations/el.json b/homeassistant/components/group/translations/el.json index edd67034de8..df1a5982f2c 100644 --- a/homeassistant/components/group/translations/el.json +++ b/homeassistant/components/group/translations/el.json @@ -15,57 +15,26 @@ "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", - "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "cover_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, "fan": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", - "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "fan_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "init": { - "data": { - "group_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03cd\u03c0\u03bf \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, "light": { "data": { - "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", - "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "light_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, "lock": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7", @@ -78,18 +47,10 @@ "data": { "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", - "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" + "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, - "media_player_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, "switch": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7", @@ -99,9 +60,6 @@ "title": "\u039d\u03ad\u03b1 \u03bf\u03bc\u03ac\u03b4\u03b1" }, "user": { - "data": { - "group_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2" - }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03cd\u03c0\u03bf \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2", "menu_options": { "binary_sensor": "\u039f\u03bc\u03ac\u03b4\u03b1 \u03b4\u03c5\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd", @@ -126,34 +84,18 @@ }, "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf." }, - "binary_sensor_options": { - "data": { - "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", - "entities": "\u039c\u03ad\u03bb\u03b7" - } - }, "cover": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" } }, - "cover_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7" - } - }, "fan": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7", "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" } }, - "fan_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7" - } - }, "light": { "data": { "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", @@ -162,12 +104,6 @@ }, "description": "\u0395\u03ac\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\", \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03bc\u03cc\u03bd\u03bf \u03b5\u03ac\u03bd \u03cc\u03bb\u03b1 \u03c4\u03b1 \u03bc\u03ad\u03bb\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b1. \u0395\u03ac\u03bd \u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \"\u03cc\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2\" \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7, \u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bf\u03bc\u03ac\u03b4\u03b1\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03b7 \u03b5\u03ac\u03bd \u03bf\u03c0\u03bf\u03b9\u03bf\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03bc\u03ad\u03bb\u03bf\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf." }, - "light_options": { - "data": { - "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", - "entities": "\u039c\u03ad\u03bb\u03b7" - } - }, "lock": { "data": { "entities": "\u039c\u03ad\u03bb\u03b7", @@ -180,11 +116,6 @@ "hide_members": "\u0391\u03c0\u03cc\u03ba\u03c1\u03c5\u03c8\u03b7 \u03bc\u03b5\u03bb\u03ce\u03bd" } }, - "media_player_options": { - "data": { - "entities": "\u039c\u03ad\u03bb\u03b7" - } - }, "switch": { "data": { "all": "\u038c\u03bb\u03b5\u03c2 \u03bf\u03b9 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2", diff --git a/homeassistant/components/group/translations/en.json b/homeassistant/components/group/translations/en.json index f7b3942c696..a67d23b812d 100644 --- a/homeassistant/components/group/translations/en.json +++ b/homeassistant/components/group/translations/en.json @@ -15,57 +15,26 @@ "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name", - "title": "Add Group" + "name": "Name" }, - "description": "Select group options", "title": "Add Group" }, - "cover_options": { - "data": { - "entities": "Group members" - }, - "description": "Select group options" - }, "fan": { "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name", - "title": "Add Group" + "name": "Name" }, - "description": "Select group options", "title": "Add Group" }, - "fan_options": { - "data": { - "entities": "Group members" - }, - "description": "Select group options" - }, - "init": { - "data": { - "group_type": "Group type" - }, - "description": "Select group type" - }, "light": { "data": { - "all": "All entities", "entities": "Members", "hide_members": "Hide members", - "name": "Name", - "title": "Add Group" + "name": "Name" }, - "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on.", "title": "Add Group" }, - "light_options": { - "data": { - "entities": "Group members" - }, - "description": "Select group options" - }, "lock": { "data": { "entities": "Members", @@ -78,18 +47,10 @@ "data": { "entities": "Members", "hide_members": "Hide members", - "name": "Name", - "title": "Add Group" + "name": "Name" }, - "description": "Select group options", "title": "Add Group" }, - "media_player_options": { - "data": { - "entities": "Group members" - }, - "description": "Select group options" - }, "switch": { "data": { "entities": "Members", @@ -99,9 +60,6 @@ "title": "Add Group" }, "user": { - "data": { - "group_type": "Group type" - }, "description": "Groups allow you to create a new entity that represents multiple entities of the same type.", "menu_options": { "binary_sensor": "Binary sensor group", @@ -126,34 +84,18 @@ }, "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on." }, - "binary_sensor_options": { - "data": { - "all": "All entities", - "entities": "Members" - } - }, "cover": { "data": { "entities": "Members", "hide_members": "Hide members" } }, - "cover_options": { - "data": { - "entities": "Members" - } - }, "fan": { "data": { "entities": "Members", "hide_members": "Hide members" } }, - "fan_options": { - "data": { - "entities": "Members" - } - }, "light": { "data": { "all": "All entities", @@ -162,12 +104,6 @@ }, "description": "If \"all entities\" is enabled, the group's state is on only if all members are on. If \"all entities\" is disabled, the group's state is on if any member is on." }, - "light_options": { - "data": { - "all": "All entities", - "entities": "Members" - } - }, "lock": { "data": { "entities": "Members", @@ -180,11 +116,6 @@ "hide_members": "Hide members" } }, - "media_player_options": { - "data": { - "entities": "Members" - } - }, "switch": { "data": { "all": "All entities", diff --git a/homeassistant/components/group/translations/es.json b/homeassistant/components/group/translations/es.json index 719d42c772a..67742954447 100644 --- a/homeassistant/components/group/translations/es.json +++ b/homeassistant/components/group/translations/es.json @@ -10,50 +10,23 @@ "cover": { "data": { "hide_members": "Esconde miembros", - "name": "Nombre del Grupo", - "title": "Agregar grupo" - }, - "description": "Seleccionar opciones de grupo" - }, - "cover_options": { - "data": { - "entities": "Miembros del grupo" + "name": "Nombre del Grupo" } }, "fan": { "data": { "entities": "Miembros", - "hide_members": "Esconde miembros", - "title": "Agregar grupo" + "hide_members": "Esconde miembros" }, - "description": "Selecciona las opciones del grupo", "title": "Agregar grupo" }, - "fan_options": { - "data": { - "entities": "Miembros del grupo" - } - }, - "init": { - "data": { - "group_type": "Tipo de grupo" - }, - "description": "Selecciona el tipo de grupo" - }, "light": { "data": { "entities": "Miembros", - "hide_members": "Esconde miembros", - "title": "Agregar grupo" + "hide_members": "Esconde miembros" }, "title": "Agregar grupo" }, - "light_options": { - "data": { - "entities": "Miembros del grupo" - }, - "description": "Selecciona las opciones del grupo" - }, "lock": { "data": { "entities": "Miembros", @@ -67,12 +40,8 @@ "hide_members": "Esconde miembros", "name": "Nombre" }, - "description": "Selecciona las opciones del grupo", "title": "Agregar grupo" }, - "media_player_options": { - "description": "Selecciona las opciones del grupo" - }, "switch": { "data": { "entities": "Miembros" @@ -96,43 +65,22 @@ "hide_members": "Esconde miembros" } }, - "binary_sensor_options": { - "data": { - "all": "Todas las entidades" - } - }, "cover": { "data": { "hide_members": "Esconde miembros" } }, - "cover_options": { - "data": { - "entities": "Miembros" - } - }, "fan": { "data": { "hide_members": "Esconde miembros" } }, - "fan_options": { - "data": { - "entities": "Miembros" - } - }, "light": { "data": { "entities": "Miembros", "hide_members": "Esconde miembros" } }, - "light_options": { - "data": { - "all": "Todas las entidades", - "entities": "Miembros" - } - }, "lock": { "data": { "entities": "Miembros" @@ -143,11 +91,6 @@ "entities": "Miembros", "hide_members": "Esconde miembros" } - }, - "media_player_options": { - "data": { - "entities": "Miembros" - } } } }, diff --git a/homeassistant/components/group/translations/et.json b/homeassistant/components/group/translations/et.json index d0d1466e26b..709d7b2c929 100644 --- a/homeassistant/components/group/translations/et.json +++ b/homeassistant/components/group/translations/et.json @@ -15,57 +15,26 @@ "data": { "entities": "Liikmed", "hide_members": "Peida grupi liikmed", - "name": "Nimi", - "title": "Uus r\u00fchm" + "name": "Nimi" }, - "description": "R\u00fchmasuvandite valimine", "title": "Uus grupp" }, - "cover_options": { - "data": { - "entities": "R\u00fchma liikmed" - }, - "description": "R\u00fchmasuvandite valimine" - }, "fan": { "data": { "entities": "Liikmed", "hide_members": "Peida grupi liikmed", - "name": "Nimi", - "title": "Uus r\u00fchm" + "name": "Nimi" }, - "description": "R\u00fchmasuvandite valimine", "title": "Uus grupp" }, - "fan_options": { - "data": { - "entities": "R\u00fchma liikmed" - }, - "description": "R\u00fchmasuvandite valimine" - }, - "init": { - "data": { - "group_type": "R\u00fchma t\u00fc\u00fcp" - }, - "description": "Vali r\u00fchma t\u00fc\u00fcp" - }, "light": { "data": { - "all": "K\u00f5ik olemid", "entities": "Liikmed", "hide_members": "Peida grupi liikmed", - "name": "Nimi", - "title": "Uus r\u00fchm" + "name": "Nimi" }, - "description": "Kui on vlitud \"K\u00f5ik olemid\" siis on grupi olek Sees kui k\u00f5ik olemid on sees. Kui \"K\u00f5ik olemid\" on keelatud siis on grupi olek Sees kui m\u00f5ni olem on sisse l\u00fclitatud.", "title": "Uus grupp" }, - "light_options": { - "data": { - "entities": "R\u00fchma liikmed" - }, - "description": "R\u00fchmasuvandite valimine" - }, "lock": { "data": { "entities": "Liikmed", @@ -78,18 +47,10 @@ "data": { "entities": "Liikmed", "hide_members": "Peida grupi liikmed", - "name": "Nimi", - "title": "Uus r\u00fchm" + "name": "Nimi" }, - "description": "R\u00fchmasuvandite valimine", "title": "Uus grupp" }, - "media_player_options": { - "data": { - "entities": "R\u00fchma liikmed" - }, - "description": "R\u00fchmasuvandite valimine" - }, "switch": { "data": { "entities": "Liikmed", @@ -99,9 +60,6 @@ "title": "Uus grupp" }, "user": { - "data": { - "group_type": "R\u00fchma t\u00fc\u00fcp" - }, "description": "R\u00fchmad v\u00f5imaldavad luua uue olemi,mis esindab mitut sama t\u00fc\u00fcpi olemit.", "menu_options": { "binary_sensor": "Olekuandurite r\u00fchm", @@ -126,34 +84,18 @@ }, "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud." }, - "binary_sensor_options": { - "data": { - "all": "K\u00f5ik olemid", - "entities": "Liikmed" - } - }, "cover": { "data": { "entities": "Grupi liikmed", "hide_members": "Peida grupi liikmed" } }, - "cover_options": { - "data": { - "entities": "Liikmed" - } - }, "fan": { "data": { "entities": "Grupi liikmed", "hide_members": "Peida grupi liikmed" } }, - "fan_options": { - "data": { - "entities": "Liikmed" - } - }, "light": { "data": { "all": "K\u00f5ik olemid", @@ -162,12 +104,6 @@ }, "description": "Kui \"k\u00f5ik olemid\" on lubatud,on r\u00fchma olek sees ainult siis kui k\u00f5ik liikmed on sisse l\u00fclitatud. Kui \"k\u00f5ik olemid\" on keelatud, on r\u00fchma olek sees kui m\u00f5ni liige on sisse l\u00fclitatud." }, - "light_options": { - "data": { - "all": "K\u00f5ik olemid", - "entities": "Liikmed" - } - }, "lock": { "data": { "entities": "Liikmed", @@ -180,11 +116,6 @@ "hide_members": "Peida grupi liikmed" } }, - "media_player_options": { - "data": { - "entities": "Liikmed" - } - }, "switch": { "data": { "all": "K\u00f5ik olemid", diff --git a/homeassistant/components/group/translations/fr.json b/homeassistant/components/group/translations/fr.json index e8b74e2f374..811776f8f9d 100644 --- a/homeassistant/components/group/translations/fr.json +++ b/homeassistant/components/group/translations/fr.json @@ -15,57 +15,26 @@ "data": { "entities": "Membres", "hide_members": "Cacher les membres", - "name": "Nom", - "title": "Ajouter un groupe" + "name": "Nom" }, - "description": "S\u00e9lectionnez les options du groupe", "title": "Ajouter un groupe" }, - "cover_options": { - "data": { - "entities": "Membres du groupe" - }, - "description": "S\u00e9lectionnez les options du groupe" - }, "fan": { "data": { "entities": "Membres", "hide_members": "Cacher les membres", - "name": "Nom", - "title": "Ajouter un groupe" + "name": "Nom" }, - "description": "S\u00e9lectionnez les options du groupe", "title": "Ajouter un groupe" }, - "fan_options": { - "data": { - "entities": "Membres du groupe" - }, - "description": "S\u00e9lectionnez les options du groupe" - }, - "init": { - "data": { - "group_type": "Type de groupe" - }, - "description": "S\u00e9lectionnez le type de groupe" - }, "light": { "data": { - "all": "Toutes les entit\u00e9s", "entities": "Membres", "hide_members": "Cacher les membres", - "name": "Nom", - "title": "Ajouter un groupe" + "name": "Nom" }, - "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9.", "title": "Ajouter un groupe" }, - "light_options": { - "data": { - "entities": "Membres du groupe" - }, - "description": "S\u00e9lectionnez les options du groupe" - }, "lock": { "data": { "entities": "Membres", @@ -78,18 +47,10 @@ "data": { "entities": "Membres", "hide_members": "Cacher les membres", - "name": "Nom", - "title": "Ajouter un groupe" + "name": "Nom" }, - "description": "S\u00e9lectionnez les options du groupe", "title": "Ajouter un groupe" }, - "media_player_options": { - "data": { - "entities": "Membres du groupe" - }, - "description": "S\u00e9lectionnez les options du groupe" - }, "switch": { "data": { "entities": "Membres", @@ -99,9 +60,6 @@ "title": "Ajouter un groupe" }, "user": { - "data": { - "group_type": "Type de groupe" - }, "description": "Les groupes vous permettent de cr\u00e9er une nouvelle entit\u00e9 repr\u00e9sentant plusieurs entit\u00e9s d'un m\u00eame type.", "menu_options": { "binary_sensor": "Groupe de capteurs binaires", @@ -126,34 +84,18 @@ }, "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9." }, - "binary_sensor_options": { - "data": { - "all": "Toutes les entit\u00e9s", - "entities": "Membres" - } - }, "cover": { "data": { "entities": "Membres", "hide_members": "Cacher les membres" } }, - "cover_options": { - "data": { - "entities": "Membres" - } - }, "fan": { "data": { "entities": "Membres", "hide_members": "Cacher les membres" } }, - "fan_options": { - "data": { - "entities": "Membres" - } - }, "light": { "data": { "all": "Toutes les entit\u00e9s", @@ -162,12 +104,6 @@ }, "description": "Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est activ\u00e9, l'\u00e9tat du groupe n'est activ\u00e9 que si tous les membres sont activ\u00e9s. Si \u00ab\u00a0toutes les entit\u00e9s\u00a0\u00bb est d\u00e9sactiv\u00e9, l'\u00e9tat du groupe est activ\u00e9 si au moins un membre est activ\u00e9." }, - "light_options": { - "data": { - "all": "Toutes les entit\u00e9s", - "entities": "Membres" - } - }, "lock": { "data": { "entities": "Membres", @@ -180,11 +116,6 @@ "hide_members": "Cacher les membres" } }, - "media_player_options": { - "data": { - "entities": "Membres" - } - }, "switch": { "data": { "all": "Toutes les entit\u00e9s", diff --git a/homeassistant/components/group/translations/he.json b/homeassistant/components/group/translations/he.json index ab7a90cb699..354a435f491 100644 --- a/homeassistant/components/group/translations/he.json +++ b/homeassistant/components/group/translations/he.json @@ -15,57 +15,26 @@ "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", - "name": "\u05e9\u05dd", - "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + "name": "\u05e9\u05dd" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "cover_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" - }, "fan": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", - "name": "\u05e9\u05dd", - "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + "name": "\u05e9\u05dd" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "fan_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "init": { - "data": { - "group_type": "\u05e1\u05d5\u05d2 \u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05e1\u05d5\u05d2 \u05e7\u05d1\u05d5\u05e6\u05d4" - }, "light": { "data": { - "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", - "name": "\u05e9\u05dd", - "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + "name": "\u05e9\u05dd" }, - "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc.", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "light_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" - }, "lock": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", @@ -78,18 +47,10 @@ "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd", - "name": "\u05e9\u05dd", - "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" + "name": "\u05e9\u05dd" }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4", "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, - "media_player_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e7\u05d1\u05d5\u05e6\u05d4" - }, "switch": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", @@ -99,9 +60,6 @@ "title": "\u05e7\u05d1\u05d5\u05e6\u05d4 \u05d7\u05d3\u05e9\u05d4" }, "user": { - "data": { - "group_type": "\u05e1\u05d5\u05d2 \u05e7\u05d1\u05d5\u05e6\u05d4" - }, "description": "\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05de\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05da \u05dc\u05d9\u05e6\u05d5\u05e8 \u05d9\u05e9\u05d5\u05ea \u05d7\u05d3\u05e9\u05d4 \u05d4\u05de\u05d9\u05d9\u05e6\u05d2\u05ea \u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05de\u05e8\u05d5\u05d1\u05d5\u05ea \u05de\u05d0\u05d5\u05ea\u05d5 \u05e1\u05d5\u05d2.", "menu_options": { "binary_sensor": "\u05e7\u05d1\u05d5\u05e6\u05ea \u05d7\u05d9\u05d9\u05e9\u05e0\u05d9\u05dd \u05d1\u05d9\u05e0\u05d0\u05e8\u05d9\u05d9\u05dd", @@ -124,34 +82,18 @@ }, "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc." }, - "binary_sensor_options": { - "data": { - "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", - "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" - } - }, "cover": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" } }, - "cover_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" - } - }, "fan": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" } }, - "fan_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" - } - }, "light": { "data": { "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", @@ -160,12 +102,6 @@ }, "description": "\u05d0\u05dd \u05d4\u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d6\u05de\u05d9\u05e0\u05d4, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05e8\u05e7 \u05d0\u05dd \u05db\u05dc \u05d4\u05d7\u05d1\u05e8\u05d9\u05dd \u05e4\u05d5\u05e2\u05dc\u05d9\u05dd. \u05d0\u05dd \"\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea\" \u05d0\u05d9\u05e0\u05d5 \u05d6\u05de\u05d9\u05df, \u05de\u05e6\u05d1 \u05d4\u05e7\u05d1\u05d5\u05e6\u05d4 \u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05dd \u05d7\u05d1\u05e8 \u05db\u05dc\u05e9\u05d4\u05d5 \u05e4\u05d5\u05e2\u05dc." }, - "light_options": { - "data": { - "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", - "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" - } - }, "lock": { "data": { "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd", @@ -178,11 +114,6 @@ "hide_members": "\u05d4\u05e1\u05ea\u05e8\u05ea \u05d7\u05d1\u05e8\u05d9\u05dd" } }, - "media_player_options": { - "data": { - "entities": "\u05d7\u05d1\u05e8\u05d9\u05dd" - } - }, "switch": { "data": { "all": "\u05db\u05dc \u05d4\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea", diff --git a/homeassistant/components/group/translations/hu.json b/homeassistant/components/group/translations/hu.json index ba368532cff..ae455bafebe 100644 --- a/homeassistant/components/group/translations/hu.json +++ b/homeassistant/components/group/translations/hu.json @@ -15,57 +15,26 @@ "data": { "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se", - "name": "Elnevez\u00e9s", - "title": "Csoport hozz\u00e1ad\u00e1sa" + "name": "Elnevez\u00e9s" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai", "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "cover_options": { - "data": { - "entities": "A csoport tagjai" - }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" - }, "fan": { "data": { "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se", - "name": "Elnevez\u00e9s", - "title": "Csoport hozz\u00e1ad\u00e1sa" + "name": "Elnevez\u00e9s" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai", "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "fan_options": { - "data": { - "entities": "A csoport tagjai" - }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" - }, - "init": { - "data": { - "group_type": "A csoport t\u00edpusa" - }, - "description": "A csoport t\u00edpusa" - }, "light": { "data": { - "all": "Minden entit\u00e1s", "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se", - "name": "Elnevez\u00e9s", - "title": "Csoport hozz\u00e1ad\u00e1sa" + "name": "Elnevez\u00e9s" }, - "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van.", "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "light_options": { - "data": { - "entities": "A csoport tagjai" - }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" - }, "lock": { "data": { "entities": "A csoport tagjai", @@ -78,18 +47,10 @@ "data": { "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se", - "name": "Elnevez\u00e9s", - "title": "Csoport hozz\u00e1ad\u00e1sa" + "name": "Elnevez\u00e9s" }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai", "title": "Csoport hozz\u00e1ad\u00e1sa" }, - "media_player_options": { - "data": { - "entities": "A csoport tagjai" - }, - "description": "Csoport be\u00e1ll\u00edt\u00e1sai" - }, "switch": { "data": { "entities": "A csoport tagjai", @@ -99,9 +60,6 @@ "title": "Csoport hozz\u00e1ad\u00e1sa" }, "user": { - "data": { - "group_type": "Csoport t\u00edpusa" - }, "description": "A csoportok lehet\u0151v\u00e9 teszik egy \u00faj entit\u00e1s l\u00e9trehoz\u00e1s\u00e1t, amely t\u00f6bb azonos t\u00edpus\u00fa entit\u00e1st k\u00e9pvisel.", "menu_options": { "binary_sensor": "Bin\u00e1ris \u00e9rz\u00e9kel\u0151 csoport", @@ -126,34 +84,18 @@ }, "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van." }, - "binary_sensor_options": { - "data": { - "all": "Minden entit\u00e1s", - "entities": "A csoport tagjai" - } - }, "cover": { "data": { "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se" } }, - "cover_options": { - "data": { - "entities": "A csoport tagjai" - } - }, "fan": { "data": { "entities": "A csoport tagjai", "hide_members": "Tagok elrejt\u00e9se" } }, - "fan_options": { - "data": { - "entities": "A csoport tagjai" - } - }, "light": { "data": { "all": "Minden entit\u00e1s", @@ -162,12 +104,6 @@ }, "description": "Ha \u201eMinden entit\u00e1s\u201d enged\u00e9lyezve van, a csoport \u00e1llapota csak akkor van bekapcsolva, ha minden tag \u00e1llapota bekapcsolt. Ha \u201eMinden entit\u00e1s\u201d le van tiltva, a csoport \u00e1llapota akkor van bekapcsolva, ha b\u00e1rmelyik tag bekapcsolt \u00e1llapotban van." }, - "light_options": { - "data": { - "all": "Minden entit\u00e1s", - "entities": "A csoport tagjai" - } - }, "lock": { "data": { "entities": "A csoport tagjai", @@ -180,11 +116,6 @@ "hide_members": "Tagok elrejt\u00e9se" } }, - "media_player_options": { - "data": { - "entities": "A csoport tagjai" - } - }, "switch": { "data": { "all": "Minden entit\u00e1s", diff --git a/homeassistant/components/group/translations/id.json b/homeassistant/components/group/translations/id.json index c0425c5ede6..9aa29f472c5 100644 --- a/homeassistant/components/group/translations/id.json +++ b/homeassistant/components/group/translations/id.json @@ -15,57 +15,26 @@ "data": { "entities": "Anggota", "hide_members": "Sembunyikan anggota", - "name": "Nama", - "title": "Tambahkan Grup" + "name": "Nama" }, - "description": "Pilih opsi grup", "title": "Tambahkan Grup" }, - "cover_options": { - "data": { - "entities": "Anggota grup" - }, - "description": "Pilih opsi grup" - }, "fan": { "data": { "entities": "Anggota", "hide_members": "Sembunyikan anggota", - "name": "Nama", - "title": "Tambahkan Grup" + "name": "Nama" }, - "description": "Pilih opsi grup", "title": "Tambahkan Grup" }, - "fan_options": { - "data": { - "entities": "Anggota grup" - }, - "description": "Pilih opsi grup" - }, - "init": { - "data": { - "group_type": "Jenis grup" - }, - "description": "Pilih jenis grup" - }, "light": { "data": { - "all": "Semua entitas", "entities": "Anggota", "hide_members": "Sembunyikan anggota", - "name": "Nama", - "title": "Tambahkan Grup" + "name": "Nama" }, - "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala.", "title": "Tambahkan Grup" }, - "light_options": { - "data": { - "entities": "Anggota grup" - }, - "description": "Pilih opsi grup" - }, "lock": { "data": { "entities": "Anggota", @@ -78,18 +47,10 @@ "data": { "entities": "Anggota", "hide_members": "Sembunyikan anggota", - "name": "Nama", - "title": "Tambahkan Grup" + "name": "Nama" }, - "description": "Pilih opsi grup", "title": "Tambahkan Grup" }, - "media_player_options": { - "data": { - "entities": "Anggota grup" - }, - "description": "Pilih opsi grup" - }, "switch": { "data": { "entities": "Anggota", @@ -99,9 +60,6 @@ "title": "Tambahkan Grup" }, "user": { - "data": { - "group_type": "Jenis grup" - }, "description": "Grup memungkinkan Anda membuat entitas baru yang mewakili beberapa entitas dari jenis yang sama.", "menu_options": { "binary_sensor": "Grup sensor biner", @@ -126,34 +84,18 @@ }, "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala." }, - "binary_sensor_options": { - "data": { - "all": "Semua entitas", - "entities": "Anggota" - } - }, "cover": { "data": { "entities": "Anggota", "hide_members": "Sembunyikan anggota" } }, - "cover_options": { - "data": { - "entities": "Anggota" - } - }, "fan": { "data": { "entities": "Anggota", "hide_members": "Sembunyikan anggota" } }, - "fan_options": { - "data": { - "entities": "Anggota" - } - }, "light": { "data": { "all": "Semua entitas", @@ -162,12 +104,6 @@ }, "description": "Jika \"semua entitas\" diaktifkan, status grup akan menyala jika semua anggota nyala. Jika \"semua entitas\" dinonaktifkan, status grup akan menyala jika ada salah satu atau lebih anggota yang menyala." }, - "light_options": { - "data": { - "all": "Semua entitas", - "entities": "Anggota" - } - }, "lock": { "data": { "entities": "Anggota", @@ -180,11 +116,6 @@ "hide_members": "Sembunyikan anggota" } }, - "media_player_options": { - "data": { - "entities": "Anggota" - } - }, "switch": { "data": { "all": "Semua entitas", diff --git a/homeassistant/components/group/translations/it.json b/homeassistant/components/group/translations/it.json index 4cbac9145d8..dac9fd264f2 100644 --- a/homeassistant/components/group/translations/it.json +++ b/homeassistant/components/group/translations/it.json @@ -15,57 +15,26 @@ "data": { "entities": "Membri", "hide_members": "Nascondi membri", - "name": "Nome", - "title": "Aggiungi gruppo" + "name": "Nome" }, - "description": "Seleziona le opzioni di gruppo", "title": "Aggiungi gruppo" }, - "cover_options": { - "data": { - "entities": "Membri del gruppo" - }, - "description": "Seleziona le opzioni di gruppo" - }, "fan": { "data": { "entities": "Membri", "hide_members": "Nascondi membri", - "name": "Nome", - "title": "Aggiungi gruppo" + "name": "Nome" }, - "description": "Seleziona le opzioni di gruppo", "title": "Aggiungi gruppo" }, - "fan_options": { - "data": { - "entities": "Membri del gruppo" - }, - "description": "Seleziona le opzioni di gruppo" - }, - "init": { - "data": { - "group_type": "Tipo di gruppo" - }, - "description": "Seleziona il tipo di gruppo" - }, "light": { "data": { - "all": "Tutte le entit\u00e0", "entities": "Membri", "hide_members": "Nascondi membri", - "name": "Nome", - "title": "Aggiungi gruppo" + "name": "Nome" }, - "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo.", "title": "Aggiungi gruppo" }, - "light_options": { - "data": { - "entities": "Membri del gruppo" - }, - "description": "Seleziona le opzioni di gruppo" - }, "lock": { "data": { "entities": "Membri", @@ -78,18 +47,10 @@ "data": { "entities": "Membri", "hide_members": "Nascondi membri", - "name": "Nome", - "title": "Aggiungi gruppo" + "name": "Nome" }, - "description": "Seleziona le opzioni di gruppo", "title": "Aggiungi gruppo" }, - "media_player_options": { - "data": { - "entities": "Membri del gruppo" - }, - "description": "Seleziona le opzioni di gruppo" - }, "switch": { "data": { "entities": "Membri", @@ -99,9 +60,6 @@ "title": "Aggiungi gruppo" }, "user": { - "data": { - "group_type": "Tipo di gruppo" - }, "description": "I gruppi consentono di creare una nuova entit\u00e0 che rappresenta pi\u00f9 entit\u00e0 dello stesso tipo.", "menu_options": { "binary_sensor": "Gruppo di sensori binari", @@ -126,34 +84,18 @@ }, "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo." }, - "binary_sensor_options": { - "data": { - "all": "Tutte le entit\u00e0", - "entities": "Membri" - } - }, "cover": { "data": { "entities": "Membri", "hide_members": "Nascondi membri" } }, - "cover_options": { - "data": { - "entities": "Membri" - } - }, "fan": { "data": { "entities": "Membri", "hide_members": "Nascondi membri" } }, - "fan_options": { - "data": { - "entities": "Membri" - } - }, "light": { "data": { "all": "Tutte le entit\u00e0", @@ -162,12 +104,6 @@ }, "description": "Se \"tutte le entit\u00e0\" \u00e8 abilitata, lo stato del gruppo \u00e8 attivo solo se tutti i membri sono attivi. Se \"tutte le entit\u00e0\" \u00e8 disabilitata, lo stato del gruppo \u00e8 attivo se un membro \u00e8 attivo." }, - "light_options": { - "data": { - "all": "Tutte le entit\u00e0", - "entities": "Membri" - } - }, "lock": { "data": { "entities": "Membri", @@ -180,11 +116,6 @@ "hide_members": "Nascondi membri" } }, - "media_player_options": { - "data": { - "entities": "Membri" - } - }, "switch": { "data": { "all": "Tutte le entit\u00e0", diff --git a/homeassistant/components/group/translations/ja.json b/homeassistant/components/group/translations/ja.json index 55e414927a4..46fbbc7b22f 100644 --- a/homeassistant/components/group/translations/ja.json +++ b/homeassistant/components/group/translations/ja.json @@ -15,57 +15,26 @@ "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", - "name": "\u30b0\u30eb\u30fc\u30d7\u540d", - "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + "name": "\u30b0\u30eb\u30fc\u30d7\u540d" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "cover_options": { - "data": { - "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc" - }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" - }, "fan": { "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", - "name": "\u30b0\u30eb\u30fc\u30d7\u540d", - "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + "name": "\u30b0\u30eb\u30fc\u30d7\u540d" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "fan_options": { - "data": { - "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc" - }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" - }, - "init": { - "data": { - "group_type": "\u30b0\u30eb\u30fc\u30d7\u30bf\u30a4\u30d7" - }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u7a2e\u985e\u3092\u9078\u629e" - }, "light": { "data": { - "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", - "name": "\u30b0\u30eb\u30fc\u30d7\u540d", - "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + "name": "\u30b0\u30eb\u30fc\u30d7\u540d" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "light_options": { - "data": { - "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc" - }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" - }, "lock": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc", @@ -78,18 +47,10 @@ "data": { "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b", - "name": "\u30b0\u30eb\u30fc\u30d7\u540d", - "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" + "name": "\u30b0\u30eb\u30fc\u30d7\u540d" }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e", "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, - "media_player_options": { - "data": { - "entities": "\u30b0\u30eb\u30fc\u30d7\u306e\u30e1\u30f3\u30d0\u30fc" - }, - "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e" - }, "switch": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc", @@ -99,9 +60,6 @@ "title": "\u65b0\u3057\u3044\u30b0\u30eb\u30fc\u30d7" }, "user": { - "data": { - "group_type": "\u30b0\u30eb\u30fc\u30d7\u30bf\u30a4\u30d7" - }, "description": "\u30b0\u30eb\u30fc\u30d7\u306e\u7a2e\u985e\u3092\u9078\u629e", "menu_options": { "binary_sensor": "\u30d0\u30a4\u30ca\u30ea\u30fc\u30bb\u30f3\u30b5\u30fc\u30b0\u30eb\u30fc\u30d7", @@ -126,34 +84,18 @@ }, "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002" }, - "binary_sensor_options": { - "data": { - "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", - "entities": "\u30e1\u30f3\u30d0\u30fc" - } - }, "cover": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" } }, - "cover_options": { - "data": { - "entities": "\u30e1\u30f3\u30d0\u30fc" - } - }, "fan": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc", "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" } }, - "fan_options": { - "data": { - "entities": "\u30e1\u30f3\u30d0\u30fc" - } - }, "light": { "data": { "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", @@ -162,12 +104,6 @@ }, "description": "\"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u3001\u3059\u3079\u3066\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u306e\u5834\u5408\u306b\u306e\u307f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002 \"\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\" \u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u308b\u5834\u5408\u3001\u3044\u305a\u308c\u304b\u306e\u30e1\u30f3\u30d0\u30fc\u304c\u30aa\u30f3\u3067\u3042\u308c\u3070\u3001\u30b0\u30eb\u30fc\u30d7\u306e\u72b6\u614b\u306f\u30aa\u30f3\u306b\u306a\u308a\u307e\u3059\u3002" }, - "light_options": { - "data": { - "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", - "entities": "\u30e1\u30f3\u30d0\u30fc" - } - }, "lock": { "data": { "entities": "\u30e1\u30f3\u30d0\u30fc", @@ -180,11 +116,6 @@ "hide_members": "\u30e1\u30f3\u30d0\u30fc\u3092\u975e\u8868\u793a\u306b\u3059\u308b" } }, - "media_player_options": { - "data": { - "entities": "\u30e1\u30f3\u30d0\u30fc" - } - }, "switch": { "data": { "all": "\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", diff --git a/homeassistant/components/group/translations/ko.json b/homeassistant/components/group/translations/ko.json index c2adb88c7ca..3b41a27075b 100644 --- a/homeassistant/components/group/translations/ko.json +++ b/homeassistant/components/group/translations/ko.json @@ -1,4 +1,11 @@ { + "config": { + "step": { + "user": { + "description": "\uadf8\ub8f9\uc744 \uc0ac\uc6a9\ud558\uba74 \ub3d9\uc77c\ud55c \uc720\ud615\uc758 \uc5ec\ub7ec \uad6c\uc131\uc694\uc18c\ub97c \ub098\ud0c0\ub0b4\ub294 \uc0c8 \uad6c\uc131\uc694\uc18c\ub97c \ub9cc\ub4e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } + }, "state": { "_": { "closed": "\ub2eb\ud798", diff --git a/homeassistant/components/group/translations/nl.json b/homeassistant/components/group/translations/nl.json index 1343279c365..e78e3462c60 100644 --- a/homeassistant/components/group/translations/nl.json +++ b/homeassistant/components/group/translations/nl.json @@ -15,57 +15,26 @@ "data": { "entities": "Leden", "hide_members": "Verberg leden", - "name": "Naam", - "title": "Groep toevoegen" + "name": "Naam" }, - "description": "Selecteer groepsopties", "title": "Groep toevoegen" }, - "cover_options": { - "data": { - "entities": "Groepsleden" - }, - "description": "Selecteer groepsopties" - }, "fan": { "data": { "entities": "Leden", "hide_members": "Verberg leden", - "name": "Naam", - "title": "Groep toevoegen" + "name": "Naam" }, - "description": "Selecteer groepsopties", "title": "Groep toevoegen" }, - "fan_options": { - "data": { - "entities": "Groepsleden" - }, - "description": "Selecteer groepsopties" - }, - "init": { - "data": { - "group_type": "Groepstype" - }, - "description": "Selecteer groepstype" - }, "light": { "data": { - "all": "Alle entiteiten", "entities": "Leden", "hide_members": "Verberg leden", - "name": "Naam", - "title": "Groep toevoegen" + "name": "Naam" }, - "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld.", "title": "Groep toevoegen" }, - "light_options": { - "data": { - "entities": "Groepsleden" - }, - "description": "Selecteer groepsopties" - }, "lock": { "data": { "entities": "Leden", @@ -78,18 +47,10 @@ "data": { "entities": "Leden", "hide_members": "Verberg leden", - "name": "Naam", - "title": "Groep toevoegen" + "name": "Naam" }, - "description": "Selecteer groepsopties", "title": "Groep toevoegen" }, - "media_player_options": { - "data": { - "entities": "Groepsleden" - }, - "description": "Selecteer groepsopties" - }, "switch": { "data": { "entities": "Leden", @@ -99,9 +60,6 @@ "title": "Nieuwe groep" }, "user": { - "data": { - "group_type": "Groepstype" - }, "description": "Met groepen kunt u een nieuwe entiteit cre\u00ebren die meerdere entiteiten van hetzelfde type vertegenwoordigt.", "menu_options": { "binary_sensor": "Binaire-sensorgroep", @@ -126,34 +84,18 @@ }, "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld." }, - "binary_sensor_options": { - "data": { - "all": "Alle entiteiten", - "entities": "Leden" - } - }, "cover": { "data": { "entities": "Leden", "hide_members": "Verberg leden" } }, - "cover_options": { - "data": { - "entities": "Leden" - } - }, "fan": { "data": { "entities": "Leden", "hide_members": "Verberg leden" } }, - "fan_options": { - "data": { - "entities": "Leden" - } - }, "light": { "data": { "all": "Alle entiteiten", @@ -162,12 +104,6 @@ }, "description": "Als \"alle entiteiten\" is ingeschakeld, is de groep alleen ingeschakeld als alle leden zijn ingeschakeld. Als \"all entities\" is uitgeschakeld, is de groep ingeschakeld als een lid is ingeschakeld." }, - "light_options": { - "data": { - "all": "Alle entiteiten", - "entities": "Leden" - } - }, "lock": { "data": { "entities": "Leden", @@ -180,11 +116,6 @@ "hide_members": "Verberg leden" } }, - "media_player_options": { - "data": { - "entities": "Leden" - } - }, "switch": { "data": { "all": "Alle entiteiten", diff --git a/homeassistant/components/group/translations/no.json b/homeassistant/components/group/translations/no.json index 016a9c8e934..a79e374f2a5 100644 --- a/homeassistant/components/group/translations/no.json +++ b/homeassistant/components/group/translations/no.json @@ -15,57 +15,26 @@ "data": { "entities": "Medlemmer", "hide_members": "Skjul medlemmer", - "name": "Navn", - "title": "Legg til gruppe" + "name": "Navn" }, - "description": "Velg gruppealternativer", "title": "Legg til gruppe" }, - "cover_options": { - "data": { - "entities": "Gruppemedlemmer" - }, - "description": "Velg gruppealternativer" - }, "fan": { "data": { "entities": "Medlemmer", "hide_members": "Skjul medlemmer", - "name": "Navn", - "title": "Legg til gruppe" + "name": "Navn" }, - "description": "Velg gruppealternativer", "title": "Legg til gruppe" }, - "fan_options": { - "data": { - "entities": "Gruppemedlemmer" - }, - "description": "Velg gruppealternativer" - }, - "init": { - "data": { - "group_type": "Gruppetype" - }, - "description": "Velg gruppetype" - }, "light": { "data": { - "all": "Alle enheter", "entities": "Medlemmer", "hide_members": "Skjul medlemmer", - "name": "Navn", - "title": "Legg til gruppe" + "name": "Navn" }, - "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5.", "title": "Legg til gruppe" }, - "light_options": { - "data": { - "entities": "Gruppemedlemmer" - }, - "description": "Velg gruppealternativer" - }, "lock": { "data": { "entities": "Medlemmer", @@ -78,18 +47,10 @@ "data": { "entities": "Medlemmer", "hide_members": "Skjul medlemmer", - "name": "Navn", - "title": "Legg til gruppe" + "name": "Navn" }, - "description": "Velg gruppealternativer", "title": "Legg til gruppe" }, - "media_player_options": { - "data": { - "entities": "Gruppemedlemmer" - }, - "description": "Velg gruppealternativer" - }, "switch": { "data": { "entities": "Medlemmer", @@ -99,9 +60,6 @@ "title": "Legg til gruppe" }, "user": { - "data": { - "group_type": "Gruppetype" - }, "description": "Grupper lar deg opprette en ny enhet som representerer flere enheter av samme type.", "menu_options": { "binary_sensor": "Bin\u00e6r sensorgruppe", @@ -126,34 +84,18 @@ }, "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5." }, - "binary_sensor_options": { - "data": { - "all": "Alle enheter", - "entities": "Medlemmer" - } - }, "cover": { "data": { "entities": "Medlemmer", "hide_members": "Skjul medlemmer" } }, - "cover_options": { - "data": { - "entities": "Medlemmer" - } - }, "fan": { "data": { "entities": "Medlemmer", "hide_members": "Skjul medlemmer" } }, - "fan_options": { - "data": { - "entities": "Medlemmer" - } - }, "light": { "data": { "all": "Alle enheter", @@ -162,12 +104,6 @@ }, "description": "Hvis \"alle enheter\" er aktivert, er gruppens tilstand p\u00e5 bare hvis alle medlemmene er p\u00e5. Hvis \"alle enheter\" er deaktivert, er gruppens tilstand p\u00e5 hvis et medlem er p\u00e5." }, - "light_options": { - "data": { - "all": "Alle enheter", - "entities": "Medlemmer" - } - }, "lock": { "data": { "entities": "Medlemmer", @@ -180,11 +116,6 @@ "hide_members": "Skjul medlemmer" } }, - "media_player_options": { - "data": { - "entities": "Medlemmer" - } - }, "switch": { "data": { "all": "Alle enheter", diff --git a/homeassistant/components/group/translations/pl.json b/homeassistant/components/group/translations/pl.json index 84cbd9f7b4d..3b6b6ee40ba 100644 --- a/homeassistant/components/group/translations/pl.json +++ b/homeassistant/components/group/translations/pl.json @@ -15,57 +15,26 @@ "data": { "entities": "Encje", "hide_members": "Ukryj encje", - "name": "Nazwa", - "title": "Dodaj grup\u0119" + "name": "Nazwa" }, - "description": "Wybierz opcje grupy", "title": "Dodaj grup\u0119" }, - "cover_options": { - "data": { - "entities": "Encje w grupie" - }, - "description": "Wybierz opcje grupy" - }, "fan": { "data": { "entities": "Encje", "hide_members": "Ukryj encje", - "name": "Nazwa", - "title": "Dodaj grup\u0119" + "name": "Nazwa" }, - "description": "Wybierz opcje grupy", "title": "Dodaj grup\u0119" }, - "fan_options": { - "data": { - "entities": "Encje w grupie" - }, - "description": "Wybierz opcje grupy" - }, - "init": { - "data": { - "group_type": "Rodzaj grupy" - }, - "description": "Wybierz rodzaj grupy" - }, "light": { "data": { - "all": "Wszystkie encje", "entities": "Encje", "hide_members": "Ukryj encje", - "name": "Nazwa", - "title": "Dodaj grup\u0119" + "name": "Nazwa" }, - "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona.", "title": "Dodaj grup\u0119" }, - "light_options": { - "data": { - "entities": "Encje w grupie" - }, - "description": "Wybierz opcje grupy" - }, "lock": { "data": { "entities": "Encje", @@ -78,18 +47,10 @@ "data": { "entities": "Encje", "hide_members": "Ukryj encje", - "name": "Nazwa", - "title": "Dodaj grup\u0119" + "name": "Nazwa" }, - "description": "Wybierz opcje grupy", "title": "Dodaj grup\u0119" }, - "media_player_options": { - "data": { - "entities": "Encje w grupie" - }, - "description": "Wybierz opcje grupy" - }, "switch": { "data": { "entities": "Encje", @@ -99,9 +60,6 @@ "title": "Dodaj grup\u0119" }, "user": { - "data": { - "group_type": "Rodzaj grupy" - }, "description": "Grupy umo\u017cliwiaj\u0105 tworzenie nowej encji, kt\u00f3ra reprezentuje wiele encji tego samego typu.", "menu_options": { "binary_sensor": "Grupa sensor\u00f3w binarnych", @@ -126,34 +84,18 @@ }, "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona." }, - "binary_sensor_options": { - "data": { - "all": "Wszystkie encje", - "entities": "Encje" - } - }, "cover": { "data": { "entities": "Encje", "hide_members": "Ukryj encje" } }, - "cover_options": { - "data": { - "entities": "Encje" - } - }, "fan": { "data": { "entities": "Encje", "hide_members": "Ukryj encje" } }, - "fan_options": { - "data": { - "entities": "Encje" - } - }, "light": { "data": { "all": "Wszystkie encje", @@ -162,12 +104,6 @@ }, "description": "Je\u015bli \u201ewszystkie encje\u201d jest w\u0142\u0105czone, stan grupy jest w\u0142\u0105czony tylko wtedy, gdy wszystkie encje s\u0105 w\u0142\u0105czone. Je\u015bli \u201ewszystkie encje\u201d jest wy\u0142\u0105czone, stan grupy jest w\u0142\u0105czony, je\u015bli kt\u00f3rakolwiek encja jest w\u0142\u0105czona." }, - "light_options": { - "data": { - "all": "Wszystkie encje", - "entities": "Encje" - } - }, "lock": { "data": { "entities": "Encje", @@ -180,11 +116,6 @@ "hide_members": "Ukryj encje" } }, - "media_player_options": { - "data": { - "entities": "Encje" - } - }, "switch": { "data": { "all": "Wszystkie encje", diff --git a/homeassistant/components/group/translations/pt-BR.json b/homeassistant/components/group/translations/pt-BR.json index 0987b41f4ae..1f1c7d09719 100644 --- a/homeassistant/components/group/translations/pt-BR.json +++ b/homeassistant/components/group/translations/pt-BR.json @@ -15,57 +15,26 @@ "data": { "entities": "Membros", "hide_members": "Ocultar membros", - "name": "Nome", - "title": "Adicionar grupo" + "name": "Nome" }, - "description": "Selecione as op\u00e7\u00f5es do grupo", "title": "Adicionar grupo" }, - "cover_options": { - "data": { - "entities": "Membros do grupo" - }, - "description": "Selecione as op\u00e7\u00f5es do grupo" - }, "fan": { "data": { "entities": "Membros", "hide_members": "Ocultar membros", - "name": "Nome", - "title": "Adicionar grupo" + "name": "Nome" }, - "description": "Selecione as op\u00e7\u00f5es do grupo", "title": "Adicionar grupo" }, - "fan_options": { - "data": { - "entities": "Membros do grupo" - }, - "description": "Selecione as op\u00e7\u00f5es do grupo" - }, - "init": { - "data": { - "group_type": "Tipo de grupo" - }, - "description": "Selecione o tipo de grupo" - }, "light": { "data": { - "all": "Todas as entidades", "entities": "Membros", "hide_members": "Ocultar membros", - "name": "Nome", - "title": "Adicionar grupo" + "name": "Nome" }, - "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado.", "title": "Adicionar grupo" }, - "light_options": { - "data": { - "entities": "Membros do grupo" - }, - "description": "Selecione as op\u00e7\u00f5es do grupo" - }, "lock": { "data": { "entities": "Membros", @@ -78,18 +47,10 @@ "data": { "entities": "Membros", "hide_members": "Ocultar membros", - "name": "Nome", - "title": "Adicionar grupo" + "name": "Nome" }, - "description": "Selecione as op\u00e7\u00f5es do grupo", "title": "Adicionar grupo" }, - "media_player_options": { - "data": { - "entities": "Membros do grupo" - }, - "description": "Selecione as op\u00e7\u00f5es do grupo" - }, "switch": { "data": { "entities": "Membros", @@ -99,9 +60,6 @@ "title": "Adicionar grupo" }, "user": { - "data": { - "group_type": "Tipo de grupo" - }, "description": "Os grupos permitem que voc\u00ea crie uma nova entidade que representa v\u00e1rias entidades do mesmo tipo.", "menu_options": { "binary_sensor": "Grupo de sensores bin\u00e1rios", @@ -126,34 +84,18 @@ }, "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado." }, - "binary_sensor_options": { - "data": { - "all": "Todas as entidades", - "entities": "Membros" - } - }, "cover": { "data": { "entities": "Membros", "hide_members": "Ocultar membros" } }, - "cover_options": { - "data": { - "entities": "Membros" - } - }, "fan": { "data": { "entities": "Membros", "hide_members": "Ocultar membros" } }, - "fan_options": { - "data": { - "entities": "Membros" - } - }, "light": { "data": { "all": "Todas as entidades", @@ -162,12 +104,6 @@ }, "description": "Se \"todas as entidades\" estiver habilitada, o estado do grupo estar\u00e1 ativado somente se todos os membros estiverem ativados. Se \"todas as entidades\" estiver desabilitada, o estado do grupo estar\u00e1 ativado se algum membro estiver ativado." }, - "light_options": { - "data": { - "all": "Todas as entidades", - "entities": "Membros" - } - }, "lock": { "data": { "entities": "Membros", @@ -180,11 +116,6 @@ "hide_members": "Ocultar membros" } }, - "media_player_options": { - "data": { - "entities": "Membros" - } - }, "switch": { "data": { "all": "Todas as entidades", diff --git a/homeassistant/components/group/translations/pt.json b/homeassistant/components/group/translations/pt.json index 9333d19b997..940aa088ced 100644 --- a/homeassistant/components/group/translations/pt.json +++ b/homeassistant/components/group/translations/pt.json @@ -9,25 +9,6 @@ } } }, - "options": { - "step": { - "fan_options": { - "data": { - "entities": "Membros" - } - }, - "light_options": { - "data": { - "entities": "Membros" - } - }, - "media_player_options": { - "data": { - "entities": "Membros" - } - } - } - }, "state": { "_": { "closed": "Fechada", diff --git a/homeassistant/components/group/translations/ru.json b/homeassistant/components/group/translations/ru.json index 0ba6d79900e..319121b69e8 100644 --- a/homeassistant/components/group/translations/ru.json +++ b/homeassistant/components/group/translations/ru.json @@ -15,57 +15,26 @@ "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "cover_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." - }, "fan": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "fan_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." - }, - "init": { - "data": { - "group_type": "\u0422\u0438\u043f \u0433\u0440\u0443\u043f\u043f\u044b" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u0433\u0440\u0443\u043f\u043f\u044b." - }, "light": { "data": { - "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432.", "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "light_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." - }, "lock": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", @@ -78,18 +47,10 @@ "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "title": "\u0413\u0440\u0443\u043f\u043f\u0430" + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b.", "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, - "media_player_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0433\u0440\u0443\u043f\u043f\u044b" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0433\u0440\u0443\u043f\u043f\u044b." - }, "switch": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", @@ -99,9 +60,6 @@ "title": "\u0413\u0440\u0443\u043f\u043f\u0430" }, "user": { - "data": { - "group_type": "\u0422\u0438\u043f \u0433\u0440\u0443\u043f\u043f\u044b" - }, "description": "\u0413\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.", "menu_options": { "binary_sensor": "\u0413\u0440\u0443\u043f\u043f\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432", @@ -126,34 +84,18 @@ }, "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432." }, - "binary_sensor_options": { - "data": { - "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" - } - }, "cover": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" } }, - "cover_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" - } - }, "fan": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" } }, - "fan_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" - } - }, "light": { "data": { "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", @@ -162,12 +104,6 @@ }, "description": "\u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \"\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u0441\u0435 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0433\u0440\u0443\u043f\u043f\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \"\u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\" \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0435\u0451 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432." }, - "light_options": { - "data": { - "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" - } - }, "lock": { "data": { "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438", @@ -180,11 +116,6 @@ "hide_members": "\u0421\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432" } }, - "media_player_options": { - "data": { - "entities": "\u0423\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438" - } - }, "switch": { "data": { "all": "\u0412\u0441\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", diff --git a/homeassistant/components/group/translations/sv.json b/homeassistant/components/group/translations/sv.json index 9ee5390db8a..77ae43cb1ee 100644 --- a/homeassistant/components/group/translations/sv.json +++ b/homeassistant/components/group/translations/sv.json @@ -9,64 +9,11 @@ }, "title": "Ny grupp" }, - "cover": { - "data": { - "title": "Ny grupp" - } - }, - "fan": { - "data": { - "title": "Ny grupp" - } - }, - "light": { - "data": { - "title": "Ny grupp" - } - }, - "media_player": { - "data": { - "title": "Ny grupp" - } - }, "user": { - "data": { - "group_type": "Grupptyp" - }, "title": "Ny grupp" } } }, - "options": { - "step": { - "binary_sensor_options": { - "data": { - "all": "Alla entiteter", - "entities": "Medlemmar" - } - }, - "cover_options": { - "data": { - "entities": "Medlemmar" - } - }, - "fan_options": { - "data": { - "entities": "Medlemmar" - } - }, - "light_options": { - "data": { - "entities": "Medlemmar" - } - }, - "media_player_options": { - "data": { - "entities": "Medlemmar" - } - } - } - }, "state": { "_": { "closed": "St\u00e4ngd", diff --git a/homeassistant/components/group/translations/tr.json b/homeassistant/components/group/translations/tr.json index 354ed29214c..49648f55ec4 100644 --- a/homeassistant/components/group/translations/tr.json +++ b/homeassistant/components/group/translations/tr.json @@ -15,57 +15,26 @@ "data": { "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle", - "name": "Ad", - "title": "Grup Ekle" + "name": "Ad" }, - "description": "Grup se\u00e7eneklerini se\u00e7in", "title": "Grup Ekle" }, - "cover_options": { - "data": { - "entities": "Grup \u00fcyeleri" - }, - "description": "Grup se\u00e7eneklerini se\u00e7in" - }, "fan": { "data": { "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle", - "name": "Ad", - "title": "Grup Ekle" + "name": "Ad" }, - "description": "Grup se\u00e7eneklerini se\u00e7in", "title": "Grup Ekle" }, - "fan_options": { - "data": { - "entities": "Grup \u00fcyeleri" - }, - "description": "Grup se\u00e7eneklerini se\u00e7in" - }, - "init": { - "data": { - "group_type": "Grup t\u00fcr\u00fc" - }, - "description": "Grup t\u00fcr\u00fcn\u00fc se\u00e7in" - }, "light": { "data": { - "all": "T\u00fcm varl\u0131klar", "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle", - "name": "Ad", - "title": "Grup Ekle" + "name": "Ad" }, - "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r.", "title": "Grup Ekle" }, - "light_options": { - "data": { - "entities": "Grup \u00fcyeleri" - }, - "description": "Grup se\u00e7eneklerini se\u00e7in" - }, "lock": { "data": { "entities": "\u00dcyeler", @@ -78,18 +47,10 @@ "data": { "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle", - "name": "Ad", - "title": "Grup Ekle" + "name": "Ad" }, - "description": "Grup se\u00e7eneklerini se\u00e7in", "title": "Grup Ekle" }, - "media_player_options": { - "data": { - "entities": "Grup \u00fcyeleri" - }, - "description": "Grup se\u00e7eneklerini se\u00e7in" - }, "switch": { "data": { "entities": "\u00dcyeler", @@ -99,9 +60,6 @@ "title": "Grup Ekle" }, "user": { - "data": { - "group_type": "Grup t\u00fcr\u00fc" - }, "description": "Gruplar, ayn\u0131 t\u00fcrden birden \u00e7ok varl\u0131\u011f\u0131 temsil eden yeni bir varl\u0131k olu\u015fturman\u0131za olanak tan\u0131r.", "menu_options": { "binary_sensor": "\u0130kili sens\u00f6r grubu", @@ -126,34 +84,18 @@ }, "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r." }, - "binary_sensor_options": { - "data": { - "all": "T\u00fcm varl\u0131klar", - "entities": "\u00dcyeler" - } - }, "cover": { "data": { "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle" } }, - "cover_options": { - "data": { - "entities": "\u00dcyeler" - } - }, "fan": { "data": { "entities": "\u00dcyeler", "hide_members": "\u00dcyeleri gizle" } }, - "fan_options": { - "data": { - "entities": "\u00dcyeler" - } - }, "light": { "data": { "all": "T\u00fcm varl\u0131klar", @@ -162,12 +104,6 @@ }, "description": "\"T\u00fcm varl\u0131klar\" etkinle\u015ftirilirse, grubun durumu yaln\u0131zca t\u00fcm \u00fcyeler a\u00e7\u0131ksa a\u00e7\u0131kt\u0131r. \"T\u00fcm varl\u0131klar\" devre d\u0131\u015f\u0131 b\u0131rak\u0131l\u0131rsa, herhangi bir \u00fcye a\u00e7\u0131ksa grubun durumu a\u00e7\u0131kt\u0131r." }, - "light_options": { - "data": { - "all": "T\u00fcm varl\u0131klar", - "entities": "\u00dcyeler" - } - }, "lock": { "data": { "entities": "\u00dcyeler", @@ -180,11 +116,6 @@ "hide_members": "\u00dcyeleri gizle" } }, - "media_player_options": { - "data": { - "entities": "\u00dcyeler" - } - }, "switch": { "data": { "all": "T\u00fcm varl\u0131klar", diff --git a/homeassistant/components/group/translations/zh-Hant.json b/homeassistant/components/group/translations/zh-Hant.json index 380c2216976..023c76ebbba 100644 --- a/homeassistant/components/group/translations/zh-Hant.json +++ b/homeassistant/components/group/translations/zh-Hant.json @@ -15,57 +15,26 @@ "data": { "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1", - "name": "\u540d\u7a31", - "title": "\u65b0\u589e\u7fa4\u7d44" + "name": "\u540d\u7a31" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "cover_options": { - "data": { - "entities": "\u7fa4\u7d44\u6210\u54e1" - }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" - }, "fan": { "data": { "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1", - "name": "\u540d\u7a31", - "title": "\u65b0\u589e\u7fa4\u7d44" + "name": "\u540d\u7a31" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "fan_options": { - "data": { - "entities": "\u7fa4\u7d44\u6210\u54e1" - }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" - }, - "init": { - "data": { - "group_type": "\u7fa4\u7d44\u985e\u5225" - }, - "description": "\u9078\u64c7\u7fa4\u7d44\u985e\u5225" - }, "light": { "data": { - "all": "\u6240\u6709\u5be6\u9ad4", "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1", - "name": "\u540d\u7a31", - "title": "\u65b0\u589e\u7fa4\u7d44" + "name": "\u540d\u7a31" }, - "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "light_options": { - "data": { - "entities": "\u7fa4\u7d44\u6210\u54e1" - }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" - }, "lock": { "data": { "entities": "\u6210\u54e1", @@ -78,18 +47,10 @@ "data": { "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1", - "name": "\u540d\u7a31", - "title": "\u65b0\u589e\u7fa4\u7d44" + "name": "\u540d\u7a31" }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805", "title": "\u65b0\u589e\u7fa4\u7d44" }, - "media_player_options": { - "data": { - "entities": "\u7fa4\u7d44\u6210\u54e1" - }, - "description": "\u9078\u64c7\u7fa4\u7d44\u9078\u9805" - }, "switch": { "data": { "entities": "\u6210\u54e1", @@ -99,9 +60,6 @@ "title": "\u65b0\u589e\u7fa4\u7d44" }, "user": { - "data": { - "group_type": "\u7fa4\u7d44\u985e\u5225" - }, "description": "\u7fa4\u7d44\u5141\u8a31\u4f7f\u7528\u76f8\u540c\u985e\u5225\u7684\u591a\u500b\u5be6\u9ad4\u7d44\u6210\u65b0\u5be6\u9ad4\u3002", "menu_options": { "binary_sensor": "\u4e8c\u9032\u4f4d\u611f\u6e2c\u5668\u7fa4\u7d44", @@ -126,34 +84,18 @@ }, "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" }, - "binary_sensor_options": { - "data": { - "all": "\u6240\u6709\u5be6\u9ad4", - "entities": "\u6210\u54e1" - } - }, "cover": { "data": { "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1" } }, - "cover_options": { - "data": { - "entities": "\u6210\u54e1" - } - }, "fan": { "data": { "entities": "\u6210\u54e1", "hide_members": "\u96b1\u85cf\u6210\u54e1" } }, - "fan_options": { - "data": { - "entities": "\u6210\u54e1" - } - }, "light": { "data": { "all": "\u6240\u6709\u5be6\u9ad4", @@ -162,12 +104,6 @@ }, "description": "\u5047\u5982\u958b\u555f \"\u6240\u6709\u5be6\u9ad4\"\uff0c\u50c5\u65bc\u7576\u6240\u6709\u6210\u54e1\u90fd\u70ba\u958b\u555f\u6642\u3001\u88dd\u614b\u624d\u6703\u986f\u793a\u70ba\u958b\u555f\u3002\u5047\u5982 \"\u6240\u6709\u5be6\u9ad4\" \u70ba\u95dc\u9589\u3001\u5247\u4efb\u4f55\u6210\u54e1\u958b\u59cb\u6642\uff0c\u7686\u6703\u986f\u793a\u70ba\u958b\u555f\u3002" }, - "light_options": { - "data": { - "all": "\u6240\u6709\u5be6\u9ad4", - "entities": "\u6210\u54e1" - } - }, "lock": { "data": { "entities": "\u6210\u54e1", @@ -180,11 +116,6 @@ "hide_members": "\u96b1\u85cf\u6210\u54e1" } }, - "media_player_options": { - "data": { - "entities": "\u6210\u54e1" - } - }, "switch": { "data": { "all": "\u6240\u6709\u5be6\u9ad4", diff --git a/homeassistant/components/guardian/translations/ca.json b/homeassistant/components/guardian/translations/ca.json index 0831975511e..59d9f6d03b8 100644 --- a/homeassistant/components/guardian/translations/ca.json +++ b/homeassistant/components/guardian/translations/ca.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Configura un dispositiu Elexa Guardian local." - }, - "zeroconf_confirm": { - "description": "Vols configurar aquest dispositiu Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/cs.json b/homeassistant/components/guardian/translations/cs.json index cb5ee7ef102..087c4b7ba02 100644 --- a/homeassistant/components/guardian/translations/cs.json +++ b/homeassistant/components/guardian/translations/cs.json @@ -12,9 +12,6 @@ "port": "Port" }, "description": "Nastate m\u00edstn\u00ed za\u0159\u00edzen\u00ed Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "Chcete nastavit toto za\u0159\u00edzen\u00ed Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/de.json b/homeassistant/components/guardian/translations/de.json index 63949b22de6..2078df1cae9 100644 --- a/homeassistant/components/guardian/translations/de.json +++ b/homeassistant/components/guardian/translations/de.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Konfiguriere ein lokales Elexa Guardian Ger\u00e4t." - }, - "zeroconf_confirm": { - "description": "M\u00f6chtest du dieses Guardian-Ger\u00e4t einrichten?" } } } diff --git a/homeassistant/components/guardian/translations/el.json b/homeassistant/components/guardian/translations/el.json index 8173f4d7fa0..8f6a77ac2ec 100644 --- a/homeassistant/components/guardian/translations/el.json +++ b/homeassistant/components/guardian/translations/el.json @@ -15,9 +15,6 @@ "port": "\u0398\u03cd\u03c1\u03b1" }, "description": "\u0394\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Guardian;" } } } diff --git a/homeassistant/components/guardian/translations/en.json b/homeassistant/components/guardian/translations/en.json index 52932cce02b..310f550bcc1 100644 --- a/homeassistant/components/guardian/translations/en.json +++ b/homeassistant/components/guardian/translations/en.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Configure a local Elexa Guardian device." - }, - "zeroconf_confirm": { - "description": "Do you want to set up this Guardian device?" } } } diff --git a/homeassistant/components/guardian/translations/es.json b/homeassistant/components/guardian/translations/es.json index 531d4ac5774..fe2367232a4 100644 --- a/homeassistant/components/guardian/translations/es.json +++ b/homeassistant/components/guardian/translations/es.json @@ -15,9 +15,6 @@ "port": "Puerto" }, "description": "Configurar un dispositivo local Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "\u00bfQuieres configurar este dispositivo Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/et.json b/homeassistant/components/guardian/translations/et.json index 56aec0e00c7..22ee0bf1300 100644 --- a/homeassistant/components/guardian/translations/et.json +++ b/homeassistant/components/guardian/translations/et.json @@ -15,9 +15,6 @@ "port": "" }, "description": "Seadista kohalik Elexa Guardiani seade." - }, - "zeroconf_confirm": { - "description": "Kas soovid seadistada seda Guardian'i seadet?" } } } diff --git a/homeassistant/components/guardian/translations/fr.json b/homeassistant/components/guardian/translations/fr.json index e1e1bcb4fcf..d76ec4886e3 100644 --- a/homeassistant/components/guardian/translations/fr.json +++ b/homeassistant/components/guardian/translations/fr.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Configurez un appareil Elexa Guardian local." - }, - "zeroconf_confirm": { - "description": "Voulez-vous configurer cet appareil Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/hu.json b/homeassistant/components/guardian/translations/hu.json index 82fd422abf7..1f8f9039707 100644 --- a/homeassistant/components/guardian/translations/hu.json +++ b/homeassistant/components/guardian/translations/hu.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Konfigur\u00e1lja a helyi Elexa Guardian eszk\u00f6zt." - }, - "zeroconf_confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani ezt a Guardian eszk\u00f6zt?" } } } diff --git a/homeassistant/components/guardian/translations/id.json b/homeassistant/components/guardian/translations/id.json index 8193386fb62..77c46e95afc 100644 --- a/homeassistant/components/guardian/translations/id.json +++ b/homeassistant/components/guardian/translations/id.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Konfigurasikan perangkat Elexa Guardian lokal." - }, - "zeroconf_confirm": { - "description": "Ingin menyiapkan perangkat Guardian ini?" } } } diff --git a/homeassistant/components/guardian/translations/it.json b/homeassistant/components/guardian/translations/it.json index 5db0956d80f..e6450dae21d 100644 --- a/homeassistant/components/guardian/translations/it.json +++ b/homeassistant/components/guardian/translations/it.json @@ -15,9 +15,6 @@ "port": "Porta" }, "description": "Configura un dispositivo Elexa Guardian locale." - }, - "zeroconf_confirm": { - "description": "Vuoi configurare questo dispositivo Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/ja.json b/homeassistant/components/guardian/translations/ja.json index 0c282a324bd..337223c5736 100644 --- a/homeassistant/components/guardian/translations/ja.json +++ b/homeassistant/components/guardian/translations/ja.json @@ -15,9 +15,6 @@ "port": "\u30dd\u30fc\u30c8" }, "description": "\u30ed\u30fc\u30ab\u30eb\u306eElexa Guardian\u30c7\u30d0\u30a4\u30b9\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002" - }, - "zeroconf_confirm": { - "description": "\u3053\u306eGuardian\u30c7\u30d0\u30a4\u30b9\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" } } } diff --git a/homeassistant/components/guardian/translations/ko.json b/homeassistant/components/guardian/translations/ko.json index d9f70ad2d33..8c3c5103c3c 100644 --- a/homeassistant/components/guardian/translations/ko.json +++ b/homeassistant/components/guardian/translations/ko.json @@ -12,9 +12,6 @@ "port": "\ud3ec\ud2b8" }, "description": "\ub85c\uceec Elexa Guardian \uae30\uae30\ub97c \uad6c\uc131\ud574\uc8fc\uc138\uc694." - }, - "zeroconf_confirm": { - "description": "\uc774 Guardian \uae30\uae30\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" } } } diff --git a/homeassistant/components/guardian/translations/lb.json b/homeassistant/components/guardian/translations/lb.json index 8d062eb8cf7..bc3fdc1eed3 100644 --- a/homeassistant/components/guardian/translations/lb.json +++ b/homeassistant/components/guardian/translations/lb.json @@ -12,9 +12,6 @@ "port": "Port" }, "description": "Ee lokalen Elexa Guardian Apparat ariichten." - }, - "zeroconf_confirm": { - "description": "Soll d\u00ebsen Guardian Apparat konfigur\u00e9iert ginn?" } } } diff --git a/homeassistant/components/guardian/translations/nl.json b/homeassistant/components/guardian/translations/nl.json index 409c3db9bed..42d499315bd 100644 --- a/homeassistant/components/guardian/translations/nl.json +++ b/homeassistant/components/guardian/translations/nl.json @@ -15,9 +15,6 @@ "port": "Poort" }, "description": "Configureer een lokaal Elexa Guardian-apparaat." - }, - "zeroconf_confirm": { - "description": "Wilt u dit Guardian-apparaat instellen?" } } } diff --git a/homeassistant/components/guardian/translations/no.json b/homeassistant/components/guardian/translations/no.json index 28313fa8520..a61b43dcfea 100644 --- a/homeassistant/components/guardian/translations/no.json +++ b/homeassistant/components/guardian/translations/no.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Konfigurer en lokal Elexa Guardian-enhet." - }, - "zeroconf_confirm": { - "description": "Vil du konfigurere denne Guardian-enheten?" } } } diff --git a/homeassistant/components/guardian/translations/pl.json b/homeassistant/components/guardian/translations/pl.json index 663265a7a11..5e5b2d143e2 100644 --- a/homeassistant/components/guardian/translations/pl.json +++ b/homeassistant/components/guardian/translations/pl.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Skonfiguruj lokalne urz\u0105dzenie Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "Czy chcesz skonfigurowa\u0107 to urz\u0105dzenie Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/pt-BR.json b/homeassistant/components/guardian/translations/pt-BR.json index f0996e6b84e..f4d4273b4ab 100644 --- a/homeassistant/components/guardian/translations/pt-BR.json +++ b/homeassistant/components/guardian/translations/pt-BR.json @@ -15,9 +15,6 @@ "port": "Porta" }, "description": "Configure um dispositivo local Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "Deseja configurar este dispositivo Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/ru.json b/homeassistant/components/guardian/translations/ru.json index ca904d980af..fe93ccb6369 100644 --- a/homeassistant/components/guardian/translations/ru.json +++ b/homeassistant/components/guardian/translations/ru.json @@ -15,9 +15,6 @@ "port": "\u041f\u043e\u0440\u0442" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Elexa Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/tr.json b/homeassistant/components/guardian/translations/tr.json index 9d3e903a2d6..fe4dffad3aa 100644 --- a/homeassistant/components/guardian/translations/tr.json +++ b/homeassistant/components/guardian/translations/tr.json @@ -15,9 +15,6 @@ "port": "Port" }, "description": "Yerel bir Elexa Guardian cihaz\u0131 yap\u0131land\u0131r\u0131n." - }, - "zeroconf_confirm": { - "description": "Bu Guardian cihaz\u0131n\u0131 kurmak istiyor musunuz?" } } } diff --git a/homeassistant/components/guardian/translations/uk.json b/homeassistant/components/guardian/translations/uk.json index 439a225895e..fd6ecc8d6d1 100644 --- a/homeassistant/components/guardian/translations/uk.json +++ b/homeassistant/components/guardian/translations/uk.json @@ -12,9 +12,6 @@ "port": "\u041f\u043e\u0440\u0442" }, "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Elexa Guardian." - }, - "zeroconf_confirm": { - "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u0446\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 Elexa Guardian?" } } } diff --git a/homeassistant/components/guardian/translations/zh-Hant.json b/homeassistant/components/guardian/translations/zh-Hant.json index dc3bde8ec1c..40a7de81170 100644 --- a/homeassistant/components/guardian/translations/zh-Hant.json +++ b/homeassistant/components/guardian/translations/zh-Hant.json @@ -15,9 +15,6 @@ "port": "\u901a\u8a0a\u57e0" }, "description": "\u8a2d\u5b9a\u5340\u57df Elexa Guardian \u88dd\u7f6e\u3002" - }, - "zeroconf_confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a Guardian \u88dd\u7f6e\uff1f" } } } diff --git a/homeassistant/components/habitica/translations/ca.json b/homeassistant/components/habitica/translations/ca.json index 675fc33db8c..729d03834b5 100644 --- a/homeassistant/components/habitica/translations/ca.json +++ b/homeassistant/components/habitica/translations/ca.json @@ -15,6 +15,5 @@ "description": "Connecta el perfil d'Habitica per permetre el seguiment del teu perfil i tasques d'usuari. Tingues en compte que l'api_id i l'api_key els has d'obtenir des de https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/de.json b/homeassistant/components/habitica/translations/de.json index 694bbdd65d4..6eac60a55d3 100644 --- a/homeassistant/components/habitica/translations/de.json +++ b/homeassistant/components/habitica/translations/de.json @@ -15,6 +15,5 @@ "description": "Verbinde dein Habitica-Profil, um die \u00dcberwachung des Profils und der Aufgaben deines Benutzers zu erm\u00f6glichen. Beachte, dass api_id und api_key von https://habitica.com/user/settings/api bezogen werden m\u00fcssen." } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/el.json b/homeassistant/components/habitica/translations/el.json index 43257c239a9..ddacd192d6e 100644 --- a/homeassistant/components/habitica/translations/el.json +++ b/homeassistant/components/habitica/translations/el.json @@ -15,6 +15,5 @@ "description": "\u03a3\u03c5\u03bd\u03b4\u03ad\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \u03c3\u03b1\u03c2 \u03c3\u03c4\u03bf Habitica \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03c0\u03b9\u03c4\u03c1\u03ad\u03c8\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c6\u03af\u03bb \u03ba\u03b1\u03b9 \u03c4\u03c9\u03bd \u03b5\u03c1\u03b3\u03b1\u03c3\u03b9\u03ce\u03bd \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c3\u03b1\u03c2. \u03a3\u03b7\u03bc\u03b5\u03b9\u03ce\u03c3\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03c4\u03bf api_id \u03ba\u03b1\u03b9 \u03c4\u03bf api_key \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03bb\u03b7\u03c6\u03b8\u03bf\u03cd\u03bd \u03b1\u03c0\u03cc \u03c4\u03bf https://habitica.com/user/settings/api." } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/en.json b/homeassistant/components/habitica/translations/en.json index ffbbd2de840..377e3129ee8 100644 --- a/homeassistant/components/habitica/translations/en.json +++ b/homeassistant/components/habitica/translations/en.json @@ -15,6 +15,5 @@ "description": "Connect your Habitica profile to allow monitoring of your user's profile and tasks. Note that api_id and api_key must be gotten from https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/es.json b/homeassistant/components/habitica/translations/es.json index 6850c903b99..55cf8eb7642 100644 --- a/homeassistant/components/habitica/translations/es.json +++ b/homeassistant/components/habitica/translations/es.json @@ -15,6 +15,5 @@ "description": "Conecta tu perfil de Habitica para permitir la supervisi\u00f3n del perfil y las tareas de tu usuario. Ten en cuenta que api_id y api_key deben obtenerse de https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/et.json b/homeassistant/components/habitica/translations/et.json index cfc2bcf898c..f2ff048c1c4 100644 --- a/homeassistant/components/habitica/translations/et.json +++ b/homeassistant/components/habitica/translations/et.json @@ -15,6 +15,5 @@ "description": "\u00dchenda oma Habitica profiil, et saaksid j\u00e4lgida oma kasutaja profiili ja \u00fclesandeid. Pane t\u00e4hele, et api_id ja api_key tuleb hankida aadressilt https://habitica.com/user/settings/api" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/fr.json b/homeassistant/components/habitica/translations/fr.json index 9f04d9ed588..343f28c0a3e 100644 --- a/homeassistant/components/habitica/translations/fr.json +++ b/homeassistant/components/habitica/translations/fr.json @@ -15,6 +15,5 @@ "description": "Connectez votre profil Habitica pour permettre la surveillance du profil et des t\u00e2ches de votre utilisateur. Notez que api_id et api_key doivent \u00eatre obtenus de https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/hu.json b/homeassistant/components/habitica/translations/hu.json index 589f53e852c..a26871e3cbf 100644 --- a/homeassistant/components/habitica/translations/hu.json +++ b/homeassistant/components/habitica/translations/hu.json @@ -15,6 +15,5 @@ "description": "Csatlakoztassa Habitica-profilj\u00e1t, hogy figyelemmel k\u00eds\u00e9rhesse felhaszn\u00e1l\u00f3i profilj\u00e1t \u00e9s feladatait. Ne feledje, hogy az api_id \u00e9s api_key c\u00edmeket a https://habitica.com/user/settings/api webhelyr\u0151l kell beszerezni" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/id.json b/homeassistant/components/habitica/translations/id.json index c7e4c549206..9130a320b6d 100644 --- a/homeassistant/components/habitica/translations/id.json +++ b/homeassistant/components/habitica/translations/id.json @@ -15,6 +15,5 @@ "description": "Hubungkan profil Habitica Anda untuk memungkinkan pemantauan profil dan tugas pengguna Anda. Perhatikan bahwa api_id dan api_key harus diperoleh dari https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/it.json b/homeassistant/components/habitica/translations/it.json index 2bef21519b6..9467c5af335 100644 --- a/homeassistant/components/habitica/translations/it.json +++ b/homeassistant/components/habitica/translations/it.json @@ -15,6 +15,5 @@ "description": "Collega il tuo profilo Habitica per consentire il monitoraggio del profilo e delle attivit\u00e0 dell'utente. Nota che api_id e api_key devono essere ottenuti da https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/ja.json b/homeassistant/components/habitica/translations/ja.json index 28d877a4788..b478f5f083d 100644 --- a/homeassistant/components/habitica/translations/ja.json +++ b/homeassistant/components/habitica/translations/ja.json @@ -15,6 +15,5 @@ "description": "Habitica profile\u306b\u63a5\u7d9a\u3057\u3066\u3001\u3042\u306a\u305f\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3068\u30bf\u30b9\u30af\u3092\u76e3\u8996\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002 \u6ce8\u610f: api_id\u3068api_key\u306f\u3001https://habitica.com/user/settings/api \u304b\u3089\u53d6\u5f97\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/ko.json b/homeassistant/components/habitica/translations/ko.json index 6b890a320df..7640f163cc0 100644 --- a/homeassistant/components/habitica/translations/ko.json +++ b/homeassistant/components/habitica/translations/ko.json @@ -15,6 +15,5 @@ "description": "\uc0ac\uc6a9\uc790\uc758 \ud504\ub85c\ud544 \ubc0f \uc791\uc5c5\uc744 \ubaa8\ub2c8\ud130\ub9c1\ud560 \uc218 \uc788\ub3c4\ub85d \ud558\ub824\uba74 Habitica \ud504\ub85c\ud544\uc744 \uc5f0\uacb0\ud574\uc8fc\uc138\uc694.\n\ucc38\uace0\ub85c api_id \ubc0f api_key\ub294 https://habitica.com/user/settings/api \uc5d0\uc11c \uac00\uc838\uc640\uc57c \ud569\ub2c8\ub2e4." } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/nl.json b/homeassistant/components/habitica/translations/nl.json index 817ffd8c616..91461372e59 100644 --- a/homeassistant/components/habitica/translations/nl.json +++ b/homeassistant/components/habitica/translations/nl.json @@ -15,6 +15,5 @@ "description": "Verbind uw Habitica-profiel om het profiel en de taken van uw gebruiker te bewaken. Houd er rekening mee dat api_id en api_key van https://habitica.com/user/settings/api moeten worden gehaald" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/no.json b/homeassistant/components/habitica/translations/no.json index cdb72d3c3d6..218b861e513 100644 --- a/homeassistant/components/habitica/translations/no.json +++ b/homeassistant/components/habitica/translations/no.json @@ -15,6 +15,5 @@ "description": "Koble til Habitica-profilen din for \u00e5 tillate overv\u00e5king av brukerens profil og oppgaver. Merk at api_id og api_key m\u00e5 hentes fra https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/pl.json b/homeassistant/components/habitica/translations/pl.json index f06f1a0e1aa..7a8c6a088bc 100644 --- a/homeassistant/components/habitica/translations/pl.json +++ b/homeassistant/components/habitica/translations/pl.json @@ -15,6 +15,5 @@ "description": "Po\u0142\u0105cz sw\u00f3j profil Habitica, aby umo\u017cliwi\u0107 monitorowanie profilu i zada\u0144 u\u017cytkownika. Pami\u0119taj, \u017ce api_id i api_key musz\u0105 zosta\u0107 pobrane z https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/pt-BR.json b/homeassistant/components/habitica/translations/pt-BR.json index cbdb6e3453f..b5aafdea88b 100644 --- a/homeassistant/components/habitica/translations/pt-BR.json +++ b/homeassistant/components/habitica/translations/pt-BR.json @@ -15,6 +15,5 @@ "description": "Conecte seu perfil do Habitica para permitir o monitoramento do perfil e das tarefas do seu usu\u00e1rio. Observe que api_id e api_key devem ser obtidos em https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/ru.json b/homeassistant/components/habitica/translations/ru.json index 4899cd1e43b..23576d2a6d6 100644 --- a/homeassistant/components/habitica/translations/ru.json +++ b/homeassistant/components/habitica/translations/ru.json @@ -15,6 +15,5 @@ "description": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c Habitica, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0444\u0438\u043b\u044c \u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0412\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e api_id \u0438 api_key \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0441 https://habitica.com/user/settings/api" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/tr.json b/homeassistant/components/habitica/translations/tr.json index 32ad5aa4957..dfb29c8d17c 100644 --- a/homeassistant/components/habitica/translations/tr.json +++ b/homeassistant/components/habitica/translations/tr.json @@ -15,6 +15,5 @@ "description": "Kullan\u0131c\u0131n\u0131z\u0131n profilinin ve g\u00f6revlerinin izlenmesine izin vermek i\u00e7in Habitica profilinizi ba\u011flay\u0131n. api_id ve api_key'in https://habitica.com/user/settings/api adresinden al\u0131nmas\u0131 gerekti\u011fini unutmay\u0131n." } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/habitica/translations/zh-Hant.json b/homeassistant/components/habitica/translations/zh-Hant.json index 65918685161..d5aeda2c359 100644 --- a/homeassistant/components/habitica/translations/zh-Hant.json +++ b/homeassistant/components/habitica/translations/zh-Hant.json @@ -15,6 +15,5 @@ "description": "\u9023\u7dda\u81f3 Habitica \u8a2d\u5b9a\u6a94\u4ee5\u4f9b\u76e3\u63a7\u500b\u4eba\u8a2d\u5b9a\u8207\u4efb\u52d9\u3002\u6ce8\u610f\uff1a\u5fc5\u9808\u7531 https://habitica.com/user/settings/api \u53d6\u5f97 api_id \u8207 api_key" } } - }, - "title": "Habitica" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/ca.json b/homeassistant/components/home_plus_control/translations/ca.json index 6e6dc1e0577..1fe3adb2d6c 100644 --- a/homeassistant/components/home_plus_control/translations/ca.json +++ b/homeassistant/components/home_plus_control/translations/ca.json @@ -16,6 +16,5 @@ "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/de.json b/homeassistant/components/home_plus_control/translations/de.json index 8cb47ae3fec..5b927e03007 100644 --- a/homeassistant/components/home_plus_control/translations/de.json +++ b/homeassistant/components/home_plus_control/translations/de.json @@ -16,6 +16,5 @@ "title": "W\u00e4hle die Authentifizierungsmethode" } } - }, - "title": "Legrand Home+ Steuerung" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/el.json b/homeassistant/components/home_plus_control/translations/el.json index 724dafff28f..f2fbb7f4bb3 100644 --- a/homeassistant/components/home_plus_control/translations/el.json +++ b/homeassistant/components/home_plus_control/translations/el.json @@ -16,6 +16,5 @@ "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" } } - }, - "title": "\u0388\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 Legrand Home+" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/en.json b/homeassistant/components/home_plus_control/translations/en.json index f5f8afe73d1..250ca9d56c2 100644 --- a/homeassistant/components/home_plus_control/translations/en.json +++ b/homeassistant/components/home_plus_control/translations/en.json @@ -16,6 +16,5 @@ "title": "Pick Authentication Method" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/es-419.json b/homeassistant/components/home_plus_control/translations/es-419.json index e35bb529a85..9596f3c9d2f 100644 --- a/homeassistant/components/home_plus_control/translations/es-419.json +++ b/homeassistant/components/home_plus_control/translations/es-419.json @@ -16,6 +16,5 @@ "title": "Escoja el m\u00e9todo de autenticaci\u00f3n" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/es.json b/homeassistant/components/home_plus_control/translations/es.json index 82144e727ab..194eff4bb8c 100644 --- a/homeassistant/components/home_plus_control/translations/es.json +++ b/homeassistant/components/home_plus_control/translations/es.json @@ -16,6 +16,5 @@ "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/et.json b/homeassistant/components/home_plus_control/translations/et.json index cfe40d86bcd..d03d44a9901 100644 --- a/homeassistant/components/home_plus_control/translations/et.json +++ b/homeassistant/components/home_plus_control/translations/et.json @@ -16,6 +16,5 @@ "title": "Vali tuvastusmeetod" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/fr.json b/homeassistant/components/home_plus_control/translations/fr.json index 4e250b743b7..0eaeafc19d4 100644 --- a/homeassistant/components/home_plus_control/translations/fr.json +++ b/homeassistant/components/home_plus_control/translations/fr.json @@ -16,6 +16,5 @@ "title": "S\u00e9lectionner une m\u00e9thode d'authentification" } } - }, - "title": "Legrand Home + Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/hu.json b/homeassistant/components/home_plus_control/translations/hu.json index 3dc4b451332..5e64b3e3880 100644 --- a/homeassistant/components/home_plus_control/translations/hu.json +++ b/homeassistant/components/home_plus_control/translations/hu.json @@ -16,6 +16,5 @@ "title": "V\u00e1lasszon egy hiteles\u00edt\u00e9si m\u00f3dszert" } } - }, - "title": "Legrand Home+ vez\u00e9rl\u00e9s" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/id.json b/homeassistant/components/home_plus_control/translations/id.json index 2ef7efe3d87..5d66b185d9a 100644 --- a/homeassistant/components/home_plus_control/translations/id.json +++ b/homeassistant/components/home_plus_control/translations/id.json @@ -16,6 +16,5 @@ "title": "Pilih Metode Autentikasi" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/it.json b/homeassistant/components/home_plus_control/translations/it.json index e79d22275f7..67c3ea2b92a 100644 --- a/homeassistant/components/home_plus_control/translations/it.json +++ b/homeassistant/components/home_plus_control/translations/it.json @@ -16,6 +16,5 @@ "title": "Scegli il metodo di autenticazione" } } - }, - "title": "Legrand Home + Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/ja.json b/homeassistant/components/home_plus_control/translations/ja.json index df5165fc3fa..3cef1cd5fa1 100644 --- a/homeassistant/components/home_plus_control/translations/ja.json +++ b/homeassistant/components/home_plus_control/translations/ja.json @@ -16,6 +16,5 @@ "title": "\u8a8d\u8a3c\u65b9\u6cd5\u306e\u9078\u629e" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/ko.json b/homeassistant/components/home_plus_control/translations/ko.json index 94c8bb91648..44667e6a324 100644 --- a/homeassistant/components/home_plus_control/translations/ko.json +++ b/homeassistant/components/home_plus_control/translations/ko.json @@ -16,6 +16,5 @@ "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/nl.json b/homeassistant/components/home_plus_control/translations/nl.json index 8f6df2fdad0..ffebcf58d60 100644 --- a/homeassistant/components/home_plus_control/translations/nl.json +++ b/homeassistant/components/home_plus_control/translations/nl.json @@ -16,6 +16,5 @@ "title": "Kies een authenticatie methode" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/no.json b/homeassistant/components/home_plus_control/translations/no.json index 363f5cf54f7..314cfe84c28 100644 --- a/homeassistant/components/home_plus_control/translations/no.json +++ b/homeassistant/components/home_plus_control/translations/no.json @@ -16,6 +16,5 @@ "title": "Velg godkjenningsmetode" } } - }, - "title": "Legrand Home + Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/pl.json b/homeassistant/components/home_plus_control/translations/pl.json index b684c874a7d..27448ab2398 100644 --- a/homeassistant/components/home_plus_control/translations/pl.json +++ b/homeassistant/components/home_plus_control/translations/pl.json @@ -16,6 +16,5 @@ "title": "Wybierz metod\u0119 uwierzytelniania" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/pt-BR.json b/homeassistant/components/home_plus_control/translations/pt-BR.json index 3b7340be7c7..12ca9127cff 100644 --- a/homeassistant/components/home_plus_control/translations/pt-BR.json +++ b/homeassistant/components/home_plus_control/translations/pt-BR.json @@ -16,6 +16,5 @@ "title": "Escolha o m\u00e9todo de autentica\u00e7\u00e3o" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/ru.json b/homeassistant/components/home_plus_control/translations/ru.json index c968cc261e2..b67c2eb9ce7 100644 --- a/homeassistant/components/home_plus_control/translations/ru.json +++ b/homeassistant/components/home_plus_control/translations/ru.json @@ -16,6 +16,5 @@ "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/tr.json b/homeassistant/components/home_plus_control/translations/tr.json index 0138716d548..105fe1ebbed 100644 --- a/homeassistant/components/home_plus_control/translations/tr.json +++ b/homeassistant/components/home_plus_control/translations/tr.json @@ -16,6 +16,5 @@ "title": "Kimlik Do\u011frulama Y\u00f6ntemini Se\u00e7" } } - }, - "title": "Legrand Home+ Kontrol" + } } \ No newline at end of file diff --git a/homeassistant/components/home_plus_control/translations/zh-Hant.json b/homeassistant/components/home_plus_control/translations/zh-Hant.json index da55be65f04..572edf837d1 100644 --- a/homeassistant/components/home_plus_control/translations/zh-Hant.json +++ b/homeassistant/components/home_plus_control/translations/zh-Hant.json @@ -16,6 +16,5 @@ "title": "\u9078\u64c7\u9a57\u8b49\u6a21\u5f0f" } } - }, - "title": "Legrand Home+ Control" + } } \ No newline at end of file diff --git a/homeassistant/components/homekit/translations/ca.json b/homeassistant/components/homekit/translations/ca.json index f1036cceb25..16843fa1741 100644 --- a/homeassistant/components/homekit/translations/ca.json +++ b/homeassistant/components/homekit/translations/ca.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Inici autom\u00e0tic (desactiva-ho si crides el servei homekit.start manualment)", "devices": "Dispositius (disparadors)" }, "description": "Els interruptors programables es creen per cada dispositiu seleccionat. HomeKit pot ser programat per a que executi una automatitzaci\u00f3 o escena quan un dispositiu es dispari.", diff --git a/homeassistant/components/homekit/translations/de.json b/homeassistant/components/homekit/translations/de.json index d9c07e4cf21..d145d826337 100644 --- a/homeassistant/components/homekit/translations/de.json +++ b/homeassistant/components/homekit/translations/de.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (deaktivieren, wenn du den homekit.start-Dienst manuell aufrufst)", "devices": "Ger\u00e4te (Trigger)" }, "description": "F\u00fcr jedes ausgew\u00e4hlte Ger\u00e4t werden programmierbare Schalter erstellt. Wenn ein Ger\u00e4teausl\u00f6ser ausgel\u00f6st wird, kann HomeKit so konfiguriert werden, dass eine Automatisierung oder Szene ausgef\u00fchrt wird.", diff --git a/homeassistant/components/homekit/translations/el.json b/homeassistant/components/homekit/translations/el.json index 370617bd5fd..1031fcca85d 100644 --- a/homeassistant/components/homekit/translations/el.json +++ b/homeassistant/components/homekit/translations/el.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 (\u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b1\u03bd \u03ba\u03b1\u03bb\u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 homekit.start \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1)", "devices": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 (\u0395\u03bd\u03b1\u03cd\u03c3\u03bc\u03b1\u03c4\u03b1)" }, "description": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03bf\u03cd\u03bd\u03c4\u03b1\u03b9 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf\u03b9 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b5\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03ac\u03b8\u03b5 \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u038c\u03c4\u03b1\u03bd \u03c0\u03c5\u03c1\u03bf\u03b4\u03bf\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b9\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2, \u03c4\u03bf HomeKit \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03ba\u03c4\u03ad\u03bb\u03b5\u03c3\u03b7 \u03b5\u03bd\u03cc\u03c2 \u03b1\u03c5\u03c4\u03bf\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd \u03ae \u03bc\u03b9\u03b1\u03c2 \u03c3\u03ba\u03b7\u03bd\u03ae\u03c2.", diff --git a/homeassistant/components/homekit/translations/en.json b/homeassistant/components/homekit/translations/en.json index 14585b60f1e..f0c26868e7d 100644 --- a/homeassistant/components/homekit/translations/en.json +++ b/homeassistant/components/homekit/translations/en.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (disable if you are calling the homekit.start service manually)", "devices": "Devices (Triggers)" }, "description": "Programmable switches are created for each selected device. When a device trigger fires, HomeKit can be configured to run an automation or scene.", diff --git a/homeassistant/components/homekit/translations/es-419.json b/homeassistant/components/homekit/translations/es-419.json index 459832cac80..55cbafcb647 100644 --- a/homeassistant/components/homekit/translations/es-419.json +++ b/homeassistant/components/homekit/translations/es-419.json @@ -20,9 +20,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "Inicio autom\u00e1tico (deshabilitar si se usa Z-Wave u otro sistema de inicio diferido)" - }, "description": "Esta configuraci\u00f3n solo necesita ser ajustada si el puente HomeKit no es funcional.", "title": "Configuraci\u00f3n avanzada" }, diff --git a/homeassistant/components/homekit/translations/es.json b/homeassistant/components/homekit/translations/es.json index c673a4e9749..9aa71faef12 100644 --- a/homeassistant/components/homekit/translations/es.json +++ b/homeassistant/components/homekit/translations/es.json @@ -26,7 +26,6 @@ }, "advanced": { "data": { - "auto_start": "Arranque autom\u00e1tico (desactivado si se utiliza Z-Wave u otro sistema de arranque retardado)", "devices": "Dispositivos (disparadores)" }, "description": "Esta configuraci\u00f3n solo necesita ser ajustada si el puente HomeKit no es funcional.", diff --git a/homeassistant/components/homekit/translations/et.json b/homeassistant/components/homekit/translations/et.json index 671dede46e0..350812600e7 100644 --- a/homeassistant/components/homekit/translations/et.json +++ b/homeassistant/components/homekit/translations/et.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (keela kui kasutad homekit.start teenust k\u00e4sitsi)", "devices": "Seadmed (p\u00e4\u00e4stikud)" }, "description": "Iga valitud seadme jaoks luuakse programmeeritavad l\u00fclitid. Seadme p\u00e4\u00e4stiku k\u00e4ivitamisel saab HomeKiti seadistada automaatiseeringu v\u00f5i stseeni k\u00e4ivitamiseks.", diff --git a/homeassistant/components/homekit/translations/fr.json b/homeassistant/components/homekit/translations/fr.json index e4850893bd3..57294ad4315 100644 --- a/homeassistant/components/homekit/translations/fr.json +++ b/homeassistant/components/homekit/translations/fr.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "D\u00e9marrage automatique (d\u00e9sactiver si vous utilisez Z-Wave ou un autre syst\u00e8me de d\u00e9marrage diff\u00e9r\u00e9)", "devices": "Appareils (d\u00e9clencheurs)" }, "description": "Ces param\u00e8tres ne doivent \u00eatre ajust\u00e9s que si le pont HomeKit n'est pas fonctionnel.", diff --git a/homeassistant/components/homekit/translations/hu.json b/homeassistant/components/homekit/translations/hu.json index a1c64f6fac3..eefbf2ad381 100644 --- a/homeassistant/components/homekit/translations/hu.json +++ b/homeassistant/components/homekit/translations/hu.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Automatikus ind\u00edt\u00e1s (tiltsa le, ha manu\u00e1lisan h\u00edvja a homekit.start szolg\u00e1ltat\u00e1st)", "devices": "Eszk\u00f6z\u00f6k (triggerek)" }, "description": "Programozhat\u00f3 kapcsol\u00f3k j\u00f6nnek l\u00e9tre minden kiv\u00e1lasztott eszk\u00f6zh\u00f6z. Amikor egy eszk\u00f6z esem\u00e9nyt ind\u00edt el, a HomeKit be\u00e1ll\u00edthat\u00f3 \u00fagy, hogy egy automatizmus vagy egy jelenet induljon el.", diff --git a/homeassistant/components/homekit/translations/id.json b/homeassistant/components/homekit/translations/id.json index 8293ad9d72c..e3889ce031f 100644 --- a/homeassistant/components/homekit/translations/id.json +++ b/homeassistant/components/homekit/translations/id.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Mulai otomatis (nonaktifkan jika Anda memanggil layanan homekit.start secara manual)", "devices": "Perangkat (Pemicu)" }, "description": "Sakelar yang dapat diprogram dibuat untuk setiap perangkat yang dipilih. Saat pemicu perangkat aktif, HomeKit dapat dikonfigurasi untuk menjalankan otomatisasi atau skenario.", diff --git a/homeassistant/components/homekit/translations/it.json b/homeassistant/components/homekit/translations/it.json index 7acec1af5c3..4086db071c2 100644 --- a/homeassistant/components/homekit/translations/it.json +++ b/homeassistant/components/homekit/translations/it.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Avvio automatico (disabilita se stai chiamando manualmente il servizio homekit.start)", "devices": "Dispositivi (Attivatori)" }, "description": "Gli interruttori programmabili vengono creati per ogni dispositivo selezionato. Quando si attiva un trigger del dispositivo, HomeKit pu\u00f2 essere configurato per eseguire un'automazione o una scena.", diff --git a/homeassistant/components/homekit/translations/ja.json b/homeassistant/components/homekit/translations/ja.json index 6bcad37ad61..a2364e44c78 100644 --- a/homeassistant/components/homekit/translations/ja.json +++ b/homeassistant/components/homekit/translations/ja.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "\u81ea\u52d5\u8d77\u52d5(homekit.start\u30b5\u30fc\u30d3\u30b9\u3092\u624b\u52d5\u3067\u547c\u3073\u51fa\u3059\u5834\u5408\u306f\u7121\u52b9\u306b\u3059\u308b)", "devices": "\u30c7\u30d0\u30a4\u30b9(\u30c8\u30ea\u30ac\u30fc)" }, "description": "\u9078\u629e\u3057\u305f\u30c7\u30d0\u30a4\u30b9\u3054\u3068\u306b\u3001\u30d7\u30ed\u30b0\u30e9\u30e0\u53ef\u80fd\u306a\u30b9\u30a4\u30c3\u30c1\u304c\u4f5c\u6210\u3055\u308c\u307e\u3059\u3002\u30c7\u30d0\u30a4\u30b9\u306e\u30c8\u30ea\u30ac\u30fc\u304c\u767a\u751f\u3059\u308b\u3068\u3001HomeKit\u306f\u30aa\u30fc\u30c8\u30e1\u30fc\u30b7\u30e7\u30f3\u3084\u30b7\u30fc\u30f3\u3092\u5b9f\u884c\u3059\u308b\u3088\u3046\u306b\u69cb\u6210\u3067\u304d\u307e\u3059\u3002", diff --git a/homeassistant/components/homekit/translations/ko.json b/homeassistant/components/homekit/translations/ko.json index cd8cbb81943..7db067c5803 100644 --- a/homeassistant/components/homekit/translations/ko.json +++ b/homeassistant/components/homekit/translations/ko.json @@ -20,9 +20,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "\uc790\ub3d9 \uc2dc\uc791 (homekit.start \uc11c\ube44\uc2a4\ub97c \uc218\ub3d9\uc73c\ub85c \ud638\ucd9c\ud558\ub824\uba74 \ube44\ud65c\uc131\ud654\ud574\uc8fc\uc138\uc694)" - }, "description": "\uc774 \uc124\uc815\uc740 HomeKit\uac00 \uc791\ub3d9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0\uc5d0\ub9cc \uc124\uc815\ud574\uc8fc\uc138\uc694.", "title": "\uace0\uae09 \uad6c\uc131" }, diff --git a/homeassistant/components/homekit/translations/lb.json b/homeassistant/components/homekit/translations/lb.json index 409d860940d..367472630bc 100644 --- a/homeassistant/components/homekit/translations/lb.json +++ b/homeassistant/components/homekit/translations/lb.json @@ -20,9 +20,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "Autostart (d\u00e9aktiv\u00e9ier falls Z-Wave oder een aanere verz\u00f6gerte Start System benotzt g\u00ebtt)" - }, "description": "D\u00ebs Astellungen brauche n\u00ebmmen ajust\u00e9iert ze ginn falls HomeKit net funktion\u00e9iert.", "title": "Erweidert Konfiguratioun" }, diff --git a/homeassistant/components/homekit/translations/nl.json b/homeassistant/components/homekit/translations/nl.json index 7f8ab1e2ff8..6ad97c5ecf0 100644 --- a/homeassistant/components/homekit/translations/nl.json +++ b/homeassistant/components/homekit/translations/nl.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (deactiveer als je de homekit.start service handmatig aanroept)", "devices": "Apparaten (triggers)" }, "description": "Voor elk geselecteerd apparaat worden programmeerbare schakelaars gemaakt. Wanneer een apparaattrigger wordt geactiveerd, kan HomeKit worden geconfigureerd om een automatisering of sc\u00e8ne uit te voeren.", diff --git a/homeassistant/components/homekit/translations/no.json b/homeassistant/components/homekit/translations/no.json index 29f775853fd..463b52bc27b 100644 --- a/homeassistant/components/homekit/translations/no.json +++ b/homeassistant/components/homekit/translations/no.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (deaktiver hvis du ringer til homekit.start-tjenesten manuelt)", "devices": "Enheter (utl\u00f8sere)" }, "description": "Programmerbare brytere opprettes for hver valgt enhet. N\u00e5r en enhetstrigger utl\u00f8ses, kan HomeKit konfigureres til \u00e5 kj\u00f8re en automatisering eller scene.", diff --git a/homeassistant/components/homekit/translations/pl.json b/homeassistant/components/homekit/translations/pl.json index 73ed271d636..9080a03ecc0 100644 --- a/homeassistant/components/homekit/translations/pl.json +++ b/homeassistant/components/homekit/translations/pl.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Automatyczne uruchomienie (wy\u0142\u0105cz, je\u015bli r\u0119cznie uruchamiasz us\u0142ug\u0119 homekit.start)", "devices": "Urz\u0105dzenia (Wyzwalacze)" }, "description": "Dla ka\u017cdego wybranego urz\u0105dzenia stworzony zostanie programowalny prze\u0142\u0105cznik. Po uruchomieniu wyzwalacza urz\u0105dzenia, HomeKit mo\u017cna skonfigurowa\u0107 do uruchamiania automatyzacji lub sceny.", diff --git a/homeassistant/components/homekit/translations/pt-BR.json b/homeassistant/components/homekit/translations/pt-BR.json index 5fcc2748d0c..5296facd831 100644 --- a/homeassistant/components/homekit/translations/pt-BR.json +++ b/homeassistant/components/homekit/translations/pt-BR.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Autostart (desabilite se voc\u00ea estiver chamando o servi\u00e7o homekit.start manualmente)", "devices": "Dispositivos (gatilhos)" }, "description": "Os interruptores program\u00e1veis s\u00e3o criados para cada dispositivo selecionado. Quando um dispositivo dispara, o HomeKit pode ser configurado para executar uma automa\u00e7\u00e3o ou cena.", diff --git a/homeassistant/components/homekit/translations/pt.json b/homeassistant/components/homekit/translations/pt.json index 3df84b70866..f122a97b19c 100644 --- a/homeassistant/components/homekit/translations/pt.json +++ b/homeassistant/components/homekit/translations/pt.json @@ -15,9 +15,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "[%key:component::homekit::config::step::user::data::auto_start%]" - }, "title": "Configura\u00e7\u00e3o avan\u00e7ada" }, "cameras": { diff --git a/homeassistant/components/homekit/translations/ru.json b/homeassistant/components/homekit/translations/ru.json index 81dd9afa302..1f10b632fba 100644 --- a/homeassistant/components/homekit/translations/ru.json +++ b/homeassistant/components/homekit/translations/ru.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "\u0410\u0432\u0442\u043e\u0437\u0430\u043f\u0443\u0441\u043a (\u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0412\u044b \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0435 \u0441\u043b\u0443\u0436\u0431\u0443 homekit.start)", "devices": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u0442\u0440\u0438\u0433\u0433\u0435\u0440\u044b)" }, "description": "\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430. HomeKit \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0441\u0446\u0435\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u0440\u0438\u0433\u0433\u0435\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", diff --git a/homeassistant/components/homekit/translations/sl.json b/homeassistant/components/homekit/translations/sl.json index 333ec699253..0428bebc85f 100644 --- a/homeassistant/components/homekit/translations/sl.json +++ b/homeassistant/components/homekit/translations/sl.json @@ -20,9 +20,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "Samodejni zagon (onemogo\u010dite, \u010de uporabljate Z-wave ali kakteri drug sistem z zakasnjenim zagonom)" - }, "description": "Te nastavitve je treba prilagoditi le, \u010de most HomeKit ni funkcionalen.", "title": "Napredna konfiguracija" }, diff --git a/homeassistant/components/homekit/translations/tr.json b/homeassistant/components/homekit/translations/tr.json index 52683302a34..f6ac036ff84 100644 --- a/homeassistant/components/homekit/translations/tr.json +++ b/homeassistant/components/homekit/translations/tr.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "Otomatik ba\u015flatma (homekit.start hizmetini manuel olarak ar\u0131yorsan\u0131z devre d\u0131\u015f\u0131 b\u0131rak\u0131n)", "devices": "Cihazlar (Tetikleyiciler)" }, "description": "Se\u00e7ilen her cihaz i\u00e7in programlanabilir anahtarlar olu\u015fturulur. Bir cihaz tetikleyicisi tetiklendi\u011finde, HomeKit bir otomasyon veya sahne \u00e7al\u0131\u015ft\u0131racak \u015fekilde yap\u0131land\u0131r\u0131labilir.", diff --git a/homeassistant/components/homekit/translations/uk.json b/homeassistant/components/homekit/translations/uk.json index 52da83cca73..43fe9b25a9d 100644 --- a/homeassistant/components/homekit/translations/uk.json +++ b/homeassistant/components/homekit/translations/uk.json @@ -20,9 +20,6 @@ "options": { "step": { "advanced": { - "data": { - "auto_start": "[%key:component::homekit::config::step::user::data::auto_start%]" - }, "description": "\u0426\u0456 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u0456, \u043b\u0438\u0448\u0435 \u044f\u043a\u0449\u043e HomeKit \u043d\u0435 \u043f\u0440\u0430\u0446\u044e\u0454.", "title": "\u0420\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u0456 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f" }, diff --git a/homeassistant/components/homekit/translations/zh-Hans.json b/homeassistant/components/homekit/translations/zh-Hans.json index 852979fb12a..4415fcdcd38 100644 --- a/homeassistant/components/homekit/translations/zh-Hans.json +++ b/homeassistant/components/homekit/translations/zh-Hans.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "\u81ea\u52a8\u542f\u52a8\uff08\u5982\u679c\u60a8\u624b\u52a8\u8c03\u7528 homekit.start \u670d\u52a1\uff0c\u8bf7\u7981\u7528\u6b64\u9879\uff09", "devices": "\u8bbe\u5907 (\u89e6\u53d1\u5668)" }, "description": "\u5c06\u4e3a\u6bcf\u4e2a\u9009\u62e9\u7684\u8bbe\u5907\u521b\u5efa\u4e00\u4e2a\u53ef\u7f16\u7a0b\u5f00\u5173\u914d\u4ef6\u3002\u53ef\u4ee5\u5728 HomeKit \u4e2d\u914d\u7f6e\u8fd9\u4e9b\u914d\u4ef6\uff0c\u5f53\u8bbe\u5907\u89e6\u53d1\u65f6\uff0c\u6267\u884c\u6307\u5b9a\u7684\u81ea\u52a8\u5316\u6216\u573a\u666f\u3002", diff --git a/homeassistant/components/homekit/translations/zh-Hant.json b/homeassistant/components/homekit/translations/zh-Hant.json index 3ad28a22f45..54c0bb29450 100644 --- a/homeassistant/components/homekit/translations/zh-Hant.json +++ b/homeassistant/components/homekit/translations/zh-Hant.json @@ -27,7 +27,6 @@ }, "advanced": { "data": { - "auto_start": "\u81ea\u52d5\u555f\u52d5\uff08\u5047\u5982\u624b\u52d5\u4f7f\u7528 homekit.start \u670d\u52d9\u6642\u3001\u8acb\u95dc\u9589\uff09", "devices": "\u88dd\u7f6e\uff08\u89f8\u767c\u5668\uff09" }, "description": "\u70ba\u6240\u9078\u64c7\u7684\u88dd\u7f6e\u65b0\u589e\u53ef\u7a0b\u5f0f\u958b\u95dc\u3002\u7576\u88dd\u7f6e\u89f8\u767c\u5668\u89f8\u767c\u6642\u3001Homekit \u53ef\u8a2d\u5b9a\u70ba\u57f7\u884c\u81ea\u52d5\u5316\u6216\u5834\u666f\u3002", diff --git a/homeassistant/components/huawei_lte/translations/bg.json b/homeassistant/components/huawei_lte/translations/bg.json index 741b8ec7d47..7e67477f000 100644 --- a/homeassistant/components/huawei_lte/translations/bg.json +++ b/homeassistant/components/huawei_lte/translations/bg.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "already_in_progress": "\u0422\u043e\u0432\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0432\u0435\u0447\u0435 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430", "not_huawei_lte": "\u041d\u0435 \u0435 Huawei LTE \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" }, "error": { @@ -30,8 +28,7 @@ "step": { "init": { "data": { - "recipient": "\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0438 \u043d\u0430 SMS \u0438\u0437\u0432\u0435\u0441\u0442\u0438\u044f", - "track_new_devices": "\u041f\u0440\u043e\u0441\u043b\u0435\u0434\u044f\u0432\u0430\u043d\u0435 \u043d\u0430 \u043d\u043e\u0432\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + "recipient": "\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0438 \u043d\u0430 SMS \u0438\u0437\u0432\u0435\u0441\u0442\u0438\u044f" } } } diff --git a/homeassistant/components/huawei_lte/translations/ca.json b/homeassistant/components/huawei_lte/translations/ca.json index 0347398ce8b..903ba233407 100644 --- a/homeassistant/components/huawei_lte/translations/ca.json +++ b/homeassistant/components/huawei_lte/translations/ca.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat", - "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", "not_huawei_lte": "No \u00e9s un dispositiu Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nom del servei de notificacions (reinici necessari si canvia)", "recipient": "Destinataris de notificacions SMS", - "track_new_devices": "Segueix dispositius nous", "track_wired_clients": "Segueix els clients connectats a la xarxa per cable", "unauthenticated_mode": "Mode no autenticat (canviar requereix tornar a carregar)" } diff --git a/homeassistant/components/huawei_lte/translations/cs.json b/homeassistant/components/huawei_lte/translations/cs.json index 298cf182b08..7782b2fc622 100644 --- a/homeassistant/components/huawei_lte/translations/cs.json +++ b/homeassistant/components/huawei_lte/translations/cs.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", - "already_in_progress": "Konfigurace ji\u017e prob\u00edh\u00e1", "not_huawei_lte": "Nejedn\u00e1 se o za\u0159\u00edzen\u00ed Huawei LTE" }, "error": { @@ -33,8 +31,7 @@ "init": { "data": { "name": "Jm\u00e9no slu\u017eby ozn\u00e1men\u00ed (zm\u011bna vy\u017eaduje restart)", - "recipient": "P\u0159\u00edjemci ozn\u00e1men\u00ed SMS", - "track_new_devices": "Sledovat nov\u00e1 za\u0159\u00edzen\u00ed" + "recipient": "P\u0159\u00edjemci ozn\u00e1men\u00ed SMS" } } } diff --git a/homeassistant/components/huawei_lte/translations/da.json b/homeassistant/components/huawei_lte/translations/da.json index 52765ad704a..2b1f937b6be 100644 --- a/homeassistant/components/huawei_lte/translations/da.json +++ b/homeassistant/components/huawei_lte/translations/da.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Denne enhed er allerede konfigureret", - "already_in_progress": "Denne enhed er allerede ved at blive konfigureret", "not_huawei_lte": "Ikke en Huawei LTE-enhed" }, "error": { @@ -29,8 +27,7 @@ "init": { "data": { "name": "Navn p\u00e5 meddelelsestjeneste (\u00e6ndring kr\u00e6ver genstart)", - "recipient": "Modtagere af SMS-meddelelse", - "track_new_devices": "Spor nye enheder" + "recipient": "Modtagere af SMS-meddelelse" } } } diff --git a/homeassistant/components/huawei_lte/translations/de.json b/homeassistant/components/huawei_lte/translations/de.json index a3b40d0c0ae..50e7b7a2e53 100644 --- a/homeassistant/components/huawei_lte/translations/de.json +++ b/homeassistant/components/huawei_lte/translations/de.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", "not_huawei_lte": "Kein Huawei LTE-Ger\u00e4t" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Name des Benachrichtigungsdienstes (\u00c4nderung erfordert Neustart)", "recipient": "SMS-Benachrichtigungsempf\u00e4nger", - "track_new_devices": "Neue Ger\u00e4te verfolgen", "track_wired_clients": "Kabelgebundene Netzwerk-Clients verfolgen", "unauthenticated_mode": "Nicht authentifizierter Modus (\u00c4nderung erfordert erneutes Laden)" } diff --git a/homeassistant/components/huawei_lte/translations/el.json b/homeassistant/components/huawei_lte/translations/el.json index 01526400165..8b6def091c9 100644 --- a/homeassistant/components/huawei_lte/translations/el.json +++ b/homeassistant/components/huawei_lte/translations/el.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", "not_huawei_lte": "\u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1\u03c2 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 (\u03b7 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7)", "recipient": "\u03a0\u03b1\u03c1\u03b1\u03bb\u03ae\u03c0\u03c4\u03b5\u03c2 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd SMS", - "track_new_devices": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03bd\u03ad\u03c9\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd", "track_wired_clients": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03c0\u03b5\u03bb\u03b1\u03c4\u03ce\u03bd \u03b5\u03bd\u03c3\u03cd\u03c1\u03bc\u03b1\u03c4\u03bf\u03c5 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5", "unauthenticated_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c7\u03c9\u03c1\u03af\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 (\u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c6\u03cc\u03c1\u03c4\u03c9\u03c3\u03b7)" } diff --git a/homeassistant/components/huawei_lte/translations/en.json b/homeassistant/components/huawei_lte/translations/en.json index ade7beed75c..5636d952b19 100644 --- a/homeassistant/components/huawei_lte/translations/en.json +++ b/homeassistant/components/huawei_lte/translations/en.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Device is already configured", - "already_in_progress": "Configuration flow is already in progress", "not_huawei_lte": "Not a Huawei LTE device" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Notification service name (change requires restart)", "recipient": "SMS notification recipients", - "track_new_devices": "Track new devices", "track_wired_clients": "Track wired network clients", "unauthenticated_mode": "Unauthenticated mode (change requires reload)" } diff --git a/homeassistant/components/huawei_lte/translations/es-419.json b/homeassistant/components/huawei_lte/translations/es-419.json index 29e7658b8e0..056b8dba886 100644 --- a/homeassistant/components/huawei_lte/translations/es-419.json +++ b/homeassistant/components/huawei_lte/translations/es-419.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Este dispositivo ya ha sido configurado", - "already_in_progress": "Este dispositivo ya est\u00e1 siendo configurado", "not_huawei_lte": "No es un dispositivo Huawei LTE" }, "error": { @@ -29,8 +27,7 @@ "init": { "data": { "name": "Nombre del servicio de notificaci\u00f3n (el cambio requiere reiniciar)", - "recipient": "Destinatarios de notificaciones por SMS", - "track_new_devices": "Rastrear nuevos dispositivos" + "recipient": "Destinatarios de notificaciones por SMS" } } } diff --git a/homeassistant/components/huawei_lte/translations/es.json b/homeassistant/components/huawei_lte/translations/es.json index 09a63bb1f62..485d2e16f69 100644 --- a/homeassistant/components/huawei_lte/translations/es.json +++ b/homeassistant/components/huawei_lte/translations/es.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Este dispositivo ya ha sido configurado", - "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "not_huawei_lte": "No es un dispositivo Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nombre del servicio de notificaci\u00f3n (el cambio requiere reiniciar)", "recipient": "Destinatarios de notificaciones por SMS", - "track_new_devices": "Rastrea nuevos dispositivos", "track_wired_clients": "Seguir clientes de red cableados", "unauthenticated_mode": "Modo no autenticado (el cambio requiere recarga)" } diff --git a/homeassistant/components/huawei_lte/translations/et.json b/homeassistant/components/huawei_lte/translations/et.json index 955d5cc2c5a..08fcca84b23 100644 --- a/homeassistant/components/huawei_lte/translations/et.json +++ b/homeassistant/components/huawei_lte/translations/et.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Seade on juba seadistatud", - "already_in_progress": "Seadistamine on juba k\u00e4imas", "not_huawei_lte": "Pole Huawei LTE seade" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Teavitusteenuse nimi (muudatus n\u00f5uab taask\u00e4ivitamist)", "recipient": "SMS teavituse saajad", - "track_new_devices": "Uute seadmete j\u00e4lgimine", "track_wired_clients": "J\u00e4lgi juhtmega v\u00f5rgukliente", "unauthenticated_mode": "Tuvastuseta re\u017eiim (muutmine n\u00f5uab taaslaadimist)" } diff --git a/homeassistant/components/huawei_lte/translations/fr.json b/homeassistant/components/huawei_lte/translations/fr.json index 1f5042d3aa1..117514985c9 100644 --- a/homeassistant/components/huawei_lte/translations/fr.json +++ b/homeassistant/components/huawei_lte/translations/fr.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", - "already_in_progress": "La configuration est d\u00e9j\u00e0 en cours", "not_huawei_lte": "Pas un appareil Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nom du service de notification (red\u00e9marrage requis)", "recipient": "Destinataires des notifications SMS", - "track_new_devices": "Suivre les nouveaux appareils", "track_wired_clients": "Suivre les clients du r\u00e9seau filaire", "unauthenticated_mode": "Mode non authentifi\u00e9 (le changement n\u00e9cessite un rechargement)" } diff --git a/homeassistant/components/huawei_lte/translations/he.json b/homeassistant/components/huawei_lte/translations/he.json index 1e4333a989a..daad7429a83 100644 --- a/homeassistant/components/huawei_lte/translations/he.json +++ b/homeassistant/components/huawei_lte/translations/he.json @@ -1,9 +1,5 @@ { "config": { - "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea" - }, "error": { "incorrect_password": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05e9\u05d2\u05d5\u05d9\u05d4", "incorrect_username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9 \u05e9\u05d2\u05d5\u05d9", diff --git a/homeassistant/components/huawei_lte/translations/hu.json b/homeassistant/components/huawei_lte/translations/hu.json index b3cc71d5a9d..c01a5cb4bb8 100644 --- a/homeassistant/components/huawei_lte/translations/hu.json +++ b/homeassistant/components/huawei_lte/translations/hu.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "not_huawei_lte": "Nem Huawei LTE eszk\u00f6z" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "\u00c9rtes\u00edt\u00e9si szolg\u00e1ltat\u00e1s neve (a m\u00f3dos\u00edt\u00e1s \u00fajraind\u00edt\u00e1st ig\u00e9nyel)", "recipient": "SMS-\u00e9rtes\u00edt\u00e9s c\u00edmzettjei", - "track_new_devices": "\u00daj eszk\u00f6z\u00f6k nyomk\u00f6vet\u00e9se", "track_wired_clients": "Vezet\u00e9kes h\u00e1l\u00f3zati \u00fcgyfelek nyomon k\u00f6vet\u00e9se", "unauthenticated_mode": "Nem hiteles\u00edtett m\u00f3d (a v\u00e1ltoztat\u00e1shoz \u00fajrat\u00f6lt\u00e9sre van sz\u00fcks\u00e9g)" } diff --git a/homeassistant/components/huawei_lte/translations/id.json b/homeassistant/components/huawei_lte/translations/id.json index fa586718cac..ac925d0b460 100644 --- a/homeassistant/components/huawei_lte/translations/id.json +++ b/homeassistant/components/huawei_lte/translations/id.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi", - "already_in_progress": "Alur konfigurasi sedang berlangsung", "not_huawei_lte": "Bukan perangkat Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nama layanan notifikasi (perubahan harus dimulai ulang)", "recipient": "Penerima notifikasi SMS", - "track_new_devices": "Lacak perangkat baru", "track_wired_clients": "Lacak klien jaringan kabel", "unauthenticated_mode": "Mode tidak diautentikasi (perubahan memerlukan pemuatan ulang)" } diff --git a/homeassistant/components/huawei_lte/translations/it.json b/homeassistant/components/huawei_lte/translations/it.json index ad8d4a82b08..6a90b67d307 100644 --- a/homeassistant/components/huawei_lte/translations/it.json +++ b/homeassistant/components/huawei_lte/translations/it.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "already_in_progress": "Il flusso di configurazione \u00e8 gi\u00e0 in corso", "not_huawei_lte": "Non \u00e8 un dispositivo Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nome del servizio di notifica (la modifica richiede il riavvio)", "recipient": "Destinatari della notifica SMS", - "track_new_devices": "Traccia nuovi dispositivi", "track_wired_clients": "Tieni traccia dei client di rete cablata", "unauthenticated_mode": "Modalit\u00e0 non autenticata (la modifica richiede il ricaricamento)" } diff --git a/homeassistant/components/huawei_lte/translations/ja.json b/homeassistant/components/huawei_lte/translations/ja.json index 6c74f5a7918..c3b41fbbcfc 100644 --- a/homeassistant/components/huawei_lte/translations/ja.json +++ b/homeassistant/components/huawei_lte/translations/ja.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "already_in_progress": "\u69cb\u6210\u30d5\u30ed\u30fc\u306f\u3059\u3067\u306b\u9032\u884c\u4e2d\u3067\u3059", "not_huawei_lte": "Huawei LTE\u30c7\u30d0\u30a4\u30b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "\u901a\u77e5\u30b5\u30fc\u30d3\u30b9\u540d(\u5909\u66f4\u306b\u306f\u518d\u8d77\u52d5\u304c\u5fc5\u8981)", "recipient": "SMS\u901a\u77e5\u306e\u53d7\u4fe1\u8005", - "track_new_devices": "\u65b0\u3057\u3044\u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u8de1", "track_wired_clients": "\u6709\u7dda\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u8ffd\u8de1\u3059\u308b", "unauthenticated_mode": "\u8a8d\u8a3c\u306a\u3057\u306e\u30e2\u30fc\u30c9(\u5909\u66f4\u306b\u306f\u30ea\u30ed\u30fc\u30c9\u304c\u5fc5\u8981)" } diff --git a/homeassistant/components/huawei_lte/translations/ko.json b/homeassistant/components/huawei_lte/translations/ko.json index ab108964ed8..930431d6c87 100644 --- a/homeassistant/components/huawei_lte/translations/ko.json +++ b/homeassistant/components/huawei_lte/translations/ko.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "not_huawei_lte": "\ud654\uc6e8\uc774 LTE \uae30\uae30\uac00 \uc544\ub2d9\ub2c8\ub2e4" }, "error": { @@ -33,8 +31,7 @@ "init": { "data": { "name": "\uc54c\ub9bc \uc11c\ube44\uc2a4 \uc774\ub984 (\ubcc0\uacbd \uc2dc \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud568)", - "recipient": "SMS \uc54c\ub9bc \uc218\uc2e0\uc790", - "track_new_devices": "\uc0c8\ub85c\uc6b4 \uae30\uae30 \ucd94\uc801" + "recipient": "SMS \uc54c\ub9bc \uc218\uc2e0\uc790" } } } diff --git a/homeassistant/components/huawei_lte/translations/lb.json b/homeassistant/components/huawei_lte/translations/lb.json index d6983bed8fc..2be64393358 100644 --- a/homeassistant/components/huawei_lte/translations/lb.json +++ b/homeassistant/components/huawei_lte/translations/lb.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Apparat ass scho konfigur\u00e9iert", - "already_in_progress": "Konfiguratioun's Oflas ass schon am gaang", "not_huawei_lte": "Keen Huawei LTE Apparat" }, "error": { @@ -33,8 +31,7 @@ "init": { "data": { "name": "Numm vum Notifikatioun's Service (Restart n\u00e9ideg bei \u00c4nnerung)", - "recipient": "Empf\u00e4nger vun SMS Notifikatioune", - "track_new_devices": "Nei Apparater verfollegen" + "recipient": "Empf\u00e4nger vun SMS Notifikatioune" } } } diff --git a/homeassistant/components/huawei_lte/translations/nl.json b/homeassistant/components/huawei_lte/translations/nl.json index e65c261d62b..3e5148170a7 100644 --- a/homeassistant/components/huawei_lte/translations/nl.json +++ b/homeassistant/components/huawei_lte/translations/nl.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", "not_huawei_lte": "Geen Huawei LTE-apparaat" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Naam meldingsservice (wijziging vereist opnieuw opstarten)", "recipient": "Ontvangers van sms-berichten", - "track_new_devices": "Volg nieuwe apparaten", "track_wired_clients": "Volg bekabelde netwerkclients", "unauthenticated_mode": "Niet-geverifieerde modus (wijzigen vereist opnieuw laden)" } diff --git a/homeassistant/components/huawei_lte/translations/no.json b/homeassistant/components/huawei_lte/translations/no.json index 3c8b26ab0cd..d1cd33ce2b0 100644 --- a/homeassistant/components/huawei_lte/translations/no.json +++ b/homeassistant/components/huawei_lte/translations/no.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert", - "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", "not_huawei_lte": "Ikke en Huawei LTE-enhet" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Navn p\u00e5 varslingstjeneste (endring krever omstart)", "recipient": "Mottakere av SMS-varsling", - "track_new_devices": "Spor nye enheter", "track_wired_clients": "Spor kablede nettverksklienter", "unauthenticated_mode": "Uautentisert modus (endring krever omlasting)" } diff --git a/homeassistant/components/huawei_lte/translations/pl.json b/homeassistant/components/huawei_lte/translations/pl.json index 38ee2117b9d..76e7c92a010 100644 --- a/homeassistant/components/huawei_lte/translations/pl.json +++ b/homeassistant/components/huawei_lte/translations/pl.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "already_in_progress": "Konfiguracja jest ju\u017c w toku", "not_huawei_lte": "To nie jest urz\u0105dzenie Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nazwa us\u0142ugi powiadomie\u0144 (zmiana wymaga ponownego uruchomienia)", "recipient": "Odbiorcy powiadomie\u0144 SMS", - "track_new_devices": "\u015aled\u017a nowe urz\u0105dzenia", "track_wired_clients": "\u015aled\u017a klient\u00f3w sieci przewodowej", "unauthenticated_mode": "Tryb nieuwierzytelniony (zmiana wymaga prze\u0142adowania)" } diff --git a/homeassistant/components/huawei_lte/translations/pt-BR.json b/homeassistant/components/huawei_lte/translations/pt-BR.json index 7b69fce212d..a92c2de3f10 100644 --- a/homeassistant/components/huawei_lte/translations/pt-BR.json +++ b/homeassistant/components/huawei_lte/translations/pt-BR.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "already_in_progress": "O fluxo de configura\u00e7\u00e3o j\u00e1 est\u00e1 em andamento", "not_huawei_lte": "N\u00e3o \u00e9 um dispositivo Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Nome do servi\u00e7o de notifica\u00e7\u00e3o (a altera\u00e7\u00e3o requer rein\u00edcio)", "recipient": "Destinat\u00e1rios de notifica\u00e7\u00e3o por SMS", - "track_new_devices": "Rastrear novos dispositivos", "track_wired_clients": "Rastrear clientes da rede cabeada", "unauthenticated_mode": "Modo n\u00e3o autenticado (a altera\u00e7\u00e3o requer recarga)" } diff --git a/homeassistant/components/huawei_lte/translations/pt.json b/homeassistant/components/huawei_lte/translations/pt.json index 34d057a9ab0..be10fe829f7 100644 --- a/homeassistant/components/huawei_lte/translations/pt.json +++ b/homeassistant/components/huawei_lte/translations/pt.json @@ -1,9 +1,5 @@ { "config": { - "abort": { - "already_configured": "Este dispositivo j\u00e1 foi configurado", - "already_in_progress": "Este dispositivo j\u00e1 est\u00e1 a ser configurado" - }, "error": { "connection_timeout": "Liga\u00e7\u00e3o expirou", "incorrect_password": "Palavra-passe incorreta", @@ -27,8 +23,7 @@ "step": { "init": { "data": { - "recipient": "Destinat\u00e1rios de notifica\u00e7\u00e3o por SMS", - "track_new_devices": "Seguir novos dispositivos" + "recipient": "Destinat\u00e1rios de notifica\u00e7\u00e3o por SMS" } } } diff --git a/homeassistant/components/huawei_lte/translations/ru.json b/homeassistant/components/huawei_lte/translations/ru.json index e9ddd73191d..6c27673b556 100644 --- a/homeassistant/components/huawei_lte/translations/ru.json +++ b/homeassistant/components/huawei_lte/translations/ru.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", "not_huawei_lte": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c Huawei LTE" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u0443\u0436\u0431\u044b \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a)", "recipient": "\u041f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u0438 SMS-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439", - "track_new_devices": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "track_wired_clients": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u043e\u0432\u043e\u0434\u043d\u043e\u0439 \u0441\u0435\u0442\u0438", "unauthenticated_mode": "\u0420\u0435\u0436\u0438\u043c \u0431\u0435\u0437 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430)" } diff --git a/homeassistant/components/huawei_lte/translations/sk.json b/homeassistant/components/huawei_lte/translations/sk.json index 0b7bf878ea9..5ada995aa6e 100644 --- a/homeassistant/components/huawei_lte/translations/sk.json +++ b/homeassistant/components/huawei_lte/translations/sk.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" - }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" } diff --git a/homeassistant/components/huawei_lte/translations/sl.json b/homeassistant/components/huawei_lte/translations/sl.json index 07a9b269986..0297db82c7b 100644 --- a/homeassistant/components/huawei_lte/translations/sl.json +++ b/homeassistant/components/huawei_lte/translations/sl.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Ta naprava je \u017ee konfigurirana", - "already_in_progress": "Ta naprava se \u017ee nastavlja", "not_huawei_lte": "Ni naprava Huawei LTE" }, "error": { @@ -29,8 +27,7 @@ "init": { "data": { "name": "Ime storitve obve\u0161\u010danja (sprememba zahteva ponovni zagon)", - "recipient": "Prejemniki obvestil SMS", - "track_new_devices": "Sledi novim napravam" + "recipient": "Prejemniki obvestil SMS" } } } diff --git a/homeassistant/components/huawei_lte/translations/sv.json b/homeassistant/components/huawei_lte/translations/sv.json index 3f478c3f826..83cbe335b2d 100644 --- a/homeassistant/components/huawei_lte/translations/sv.json +++ b/homeassistant/components/huawei_lte/translations/sv.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Den h\u00e4r enheten har redan konfigurerats", - "already_in_progress": "Den h\u00e4r enheten har redan konfigurerats", "not_huawei_lte": "Inte en Huawei LTE-enhet" }, "error": { @@ -29,8 +27,7 @@ "init": { "data": { "name": "Namn p\u00e5 meddelandetj\u00e4nsten (\u00e4ndring kr\u00e4ver omstart)", - "recipient": "Mottagare av SMS-meddelanden", - "track_new_devices": "Sp\u00e5ra nya enheter" + "recipient": "Mottagare av SMS-meddelanden" } } } diff --git a/homeassistant/components/huawei_lte/translations/tr.json b/homeassistant/components/huawei_lte/translations/tr.json index 92d40ca810b..6d231efa8ed 100644 --- a/homeassistant/components/huawei_lte/translations/tr.json +++ b/homeassistant/components/huawei_lte/translations/tr.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "already_in_progress": "Yap\u0131land\u0131rma ak\u0131\u015f\u0131 zaten devam ediyor", "not_huawei_lte": "Huawei LTE cihaz\u0131 de\u011fil" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "Bildirim hizmeti ad\u0131 (de\u011fi\u015fiklik yeniden ba\u015flatmay\u0131 gerektirir)", "recipient": "SMS bildirimi al\u0131c\u0131lar\u0131", - "track_new_devices": "Yeni cihazlar\u0131 izle", "track_wired_clients": "Kablolu a\u011f istemcilerini izleyin", "unauthenticated_mode": "Kimli\u011fi do\u011frulanmam\u0131\u015f mod (de\u011fi\u015fiklik yeniden y\u00fckleme gerektirir)" } diff --git a/homeassistant/components/huawei_lte/translations/uk.json b/homeassistant/components/huawei_lte/translations/uk.json index 17f3d3b71c3..ae98ce59332 100644 --- a/homeassistant/components/huawei_lte/translations/uk.json +++ b/homeassistant/components/huawei_lte/translations/uk.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.", - "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0436\u0435 \u0442\u0440\u0438\u0432\u0430\u0454.", "not_huawei_lte": "\u041f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043d\u0435 \u0454 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0454\u043c Huawei LTE" }, "error": { @@ -33,8 +31,7 @@ "init": { "data": { "name": "\u041d\u0430\u0437\u0432\u0430 \u0441\u043b\u0443\u0436\u0431\u0438 \u0441\u043f\u043e\u0432\u0456\u0449\u0435\u043d\u044c (\u043f\u043e\u0442\u0440\u0456\u0431\u0435\u043d \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a)", - "recipient": "\u041e\u0434\u0435\u0440\u0436\u0443\u0432\u0430\u0447\u0456 SMS-\u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u044c", - "track_new_devices": "\u0412\u0456\u0434\u0441\u0442\u0435\u0436\u0443\u0432\u0430\u0442\u0438 \u043d\u043e\u0432\u0456 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457" + "recipient": "\u041e\u0434\u0435\u0440\u0436\u0443\u0432\u0430\u0447\u0456 SMS-\u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u044c" } } } diff --git a/homeassistant/components/huawei_lte/translations/zh-Hans.json b/homeassistant/components/huawei_lte/translations/zh-Hans.json index a63ff964b62..c04409b2783 100644 --- a/homeassistant/components/huawei_lte/translations/zh-Hans.json +++ b/homeassistant/components/huawei_lte/translations/zh-Hans.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u8bbe\u5907\u5df2\u88ab\u914d\u7f6e", - "already_in_progress": "\u914d\u7f6e\u6d41\u7a0b\u5df2\u5728\u8fdb\u884c", "not_huawei_lte": "\u8be5\u8bbe\u5907\u4e0d\u662f\u534e\u4e3a LTE \u8bbe\u5907" }, "error": { @@ -32,7 +30,6 @@ "data": { "name": "\u63a8\u9001\u670d\u52a1\u540d\u79f0\uff08\u66f4\u6539\u540e\u9700\u8981\u91cd\u8f7d\uff09", "recipient": "\u77ed\u4fe1\u901a\u77e5\u6536\u4ef6\u4eba", - "track_new_devices": "\u8ddf\u8e2a\u65b0\u8bbe\u5907", "track_wired_clients": "\u8ddf\u8e2a\u6709\u7ebf\u7f51\u7edc\u5ba2\u6237\u7aef", "unauthenticated_mode": "\u672a\u7ecf\u8eab\u4efd\u9a8c\u8bc1\u7684\u6a21\u5f0f\uff08\u66f4\u6539\u540e\u9700\u8981\u91cd\u8f7d\uff09" } diff --git a/homeassistant/components/huawei_lte/translations/zh-Hant.json b/homeassistant/components/huawei_lte/translations/zh-Hant.json index 0c1d2baa7a9..85a2d84f6de 100644 --- a/homeassistant/components/huawei_lte/translations/zh-Hant.json +++ b/homeassistant/components/huawei_lte/translations/zh-Hant.json @@ -1,8 +1,6 @@ { "config": { "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "already_in_progress": "\u8a2d\u5b9a\u5df2\u7d93\u9032\u884c\u4e2d", "not_huawei_lte": "\u4e26\u975e\u83ef\u70ba LTE \u88dd\u7f6e" }, "error": { @@ -34,7 +32,6 @@ "data": { "name": "\u901a\u77e5\u670d\u52d9\u540d\u7a31\uff08\u8b8a\u66f4\u5f8c\u9700\u91cd\u555f\uff09", "recipient": "\u7c21\u8a0a\u901a\u77e5\u6536\u4ef6\u8005", - "track_new_devices": "\u8ffd\u8e64\u65b0\u88dd\u7f6e", "track_wired_clients": "\u8ffd\u8e64\u6709\u7dda\u7db2\u8def\u5ba2\u6236\u7aef", "unauthenticated_mode": "\u672a\u6388\u6b0a\u6a21\u5f0f\uff08\u8b8a\u66f4\u5f8c\u9700\u91cd\u555f\uff09" } diff --git a/homeassistant/components/humidifier/translations/ca.json b/homeassistant/components/humidifier/translations/ca.json index ebfda0d3722..cd980a39591 100644 --- a/homeassistant/components/humidifier/translations/ca.json +++ b/homeassistant/components/humidifier/translations/ca.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} s'enc\u00e9n o s'apaga", "target_humidity_changed": "Ha canviat la humitat desitjada de {entity_name}", - "toggled": "{entity_name} s'activa o es desactiva", "turned_off": "{entity_name} s'ha apagat", "turned_on": "{entity_name} s'ha engegat" } diff --git a/homeassistant/components/humidifier/translations/cs.json b/homeassistant/components/humidifier/translations/cs.json index d47de36b72f..2e373587afc 100644 --- a/homeassistant/components/humidifier/translations/cs.json +++ b/homeassistant/components/humidifier/translations/cs.json @@ -14,7 +14,6 @@ }, "trigger_type": { "target_humidity_changed": "C\u00edlov\u00e1 vlhkost {entity_name} zm\u011bn\u011bna", - "toggled": "{entity_name} zapnuto nebo vypnuto", "turned_off": "{entity_name} vypnuto", "turned_on": "{entity_name} zapnuto" } diff --git a/homeassistant/components/humidifier/translations/de.json b/homeassistant/components/humidifier/translations/de.json index f55117ab05f..16ad95f9980 100644 --- a/homeassistant/components/humidifier/translations/de.json +++ b/homeassistant/components/humidifier/translations/de.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} ein- oder ausgeschaltet", "target_humidity_changed": "{entity_name} Soll-Luftfeuchtigkeit ge\u00e4ndert", - "toggled": "{entity_name} ein- oder ausgeschaltet", "turned_off": "{entity_name} ausgeschaltet", "turned_on": "{entity_name} eingeschaltet" } diff --git a/homeassistant/components/humidifier/translations/el.json b/homeassistant/components/humidifier/translations/el.json index 85efa779f01..d52b5c0ba43 100644 --- a/homeassistant/components/humidifier/translations/el.json +++ b/homeassistant/components/humidifier/translations/el.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "\u03a4\u03bf {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "target_humidity_changed": "\u0397 \u03c3\u03c4\u03bf\u03c7\u03b5\u03c5\u03bc\u03ad\u03bd\u03b7 \u03c5\u03b3\u03c1\u03b1\u03c3\u03af\u03b1 \u03c4\u03bf\u03c5 {entity_name} \u03ac\u03bb\u03bb\u03b1\u03be\u03b5", - "toggled": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_off": "{entity_name} \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_on": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5" } diff --git a/homeassistant/components/humidifier/translations/en.json b/homeassistant/components/humidifier/translations/en.json index 6c62ec89842..a2fc80fe7da 100644 --- a/homeassistant/components/humidifier/translations/en.json +++ b/homeassistant/components/humidifier/translations/en.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} turned on or off", "target_humidity_changed": "{entity_name} target humidity changed", - "toggled": "{entity_name} turned on or off", "turned_off": "{entity_name} turned off", "turned_on": "{entity_name} turned on" } diff --git a/homeassistant/components/humidifier/translations/es.json b/homeassistant/components/humidifier/translations/es.json index e9c8bb02df9..8506445b25c 100644 --- a/homeassistant/components/humidifier/translations/es.json +++ b/homeassistant/components/humidifier/translations/es.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} activado o desactivado", "target_humidity_changed": "La humedad objetivo ha cambiado en {entity_name}", - "toggled": "{entity_name} activado o desactivado", "turned_off": "{entity_name} desactivado", "turned_on": "{entity_name} activado" } diff --git a/homeassistant/components/humidifier/translations/et.json b/homeassistant/components/humidifier/translations/et.json index dcf1f8488a1..7a4ad019e2a 100644 --- a/homeassistant/components/humidifier/translations/et.json +++ b/homeassistant/components/humidifier/translations/et.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "target_humidity_changed": "{entity_name} eelseatud niiskus muutus", - "toggled": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "turned_off": "{entity_name} l\u00fclitus v\u00e4lja", "turned_on": "{entity_name} l\u00fclitus sisse" } diff --git a/homeassistant/components/humidifier/translations/fr.json b/homeassistant/components/humidifier/translations/fr.json index 4caf57d81b5..3229b595881 100644 --- a/homeassistant/components/humidifier/translations/fr.json +++ b/homeassistant/components/humidifier/translations/fr.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} activ\u00e9 ou d\u00e9sactiv\u00e9", "target_humidity_changed": "{nom_de_l'entit\u00e9} changement de l'humidit\u00e9 cible", - "toggled": "{entity_name} activ\u00e9 ou d\u00e9sactiv\u00e9", "turned_off": "{entity_name} s'est \u00e9teint", "turned_on": "{entity_name} s'est allum\u00e9" } diff --git a/homeassistant/components/humidifier/translations/he.json b/homeassistant/components/humidifier/translations/he.json index 152de4c49ca..5a4c58c7934 100644 --- a/homeassistant/components/humidifier/translations/he.json +++ b/homeassistant/components/humidifier/translations/he.json @@ -13,7 +13,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", - "toggled": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", "turned_off": "{entity_name} \u05db\u05d5\u05d1\u05d4", "turned_on": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc" } diff --git a/homeassistant/components/humidifier/translations/hu.json b/homeassistant/components/humidifier/translations/hu.json index 7979334429d..6572f0e4955 100644 --- a/homeassistant/components/humidifier/translations/hu.json +++ b/homeassistant/components/humidifier/translations/hu.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} be- vagy kikapcsolt", "target_humidity_changed": "{name} k\u00edv\u00e1nt p\u00e1ratartalom megv\u00e1ltozott", - "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" } diff --git a/homeassistant/components/humidifier/translations/id.json b/homeassistant/components/humidifier/translations/id.json index 1996fd23786..8fd2ccaa9b0 100644 --- a/homeassistant/components/humidifier/translations/id.json +++ b/homeassistant/components/humidifier/translations/id.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} diaktifkan atau dinonaktifkan", "target_humidity_changed": "Kelembapan target {entity_name} berubah", - "toggled": "{entity_name} diaktifkan atau dinonaktifkan", "turned_off": "{entity_name} dimatikan", "turned_on": "{entity_name} dinyalakan" } diff --git a/homeassistant/components/humidifier/translations/it.json b/homeassistant/components/humidifier/translations/it.json index cc0220eca17..7a887c7f0c7 100644 --- a/homeassistant/components/humidifier/translations/it.json +++ b/homeassistant/components/humidifier/translations/it.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} attivata o disattivata", "target_humidity_changed": "{entity_name} umidit\u00e0 target modificata", - "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" } diff --git a/homeassistant/components/humidifier/translations/ja.json b/homeassistant/components/humidifier/translations/ja.json index 713049acb52..5c51b58cbfa 100644 --- a/homeassistant/components/humidifier/translations/ja.json +++ b/homeassistant/components/humidifier/translations/ja.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "target_humidity_changed": "{entity_name} \u30bf\u30fc\u30b2\u30c3\u30c8\u6e7f\u5ea6\u304c\u5909\u5316\u3057\u307e\u3057\u305f", - "toggled": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_off": "{entity_name} \u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_on": "{entity_name} \u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u307e\u3059" } diff --git a/homeassistant/components/humidifier/translations/nl.json b/homeassistant/components/humidifier/translations/nl.json index 8d96d698209..1cdf417e6ef 100644 --- a/homeassistant/components/humidifier/translations/nl.json +++ b/homeassistant/components/humidifier/translations/nl.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", "target_humidity_changed": "{entity_name} doel luchtvochtigheid gewijzigd", - "toggled": "{entity_name} in- of uitgeschakeld", "turned_off": "{entity_name} is uitgeschakeld", "turned_on": "{entity_name} is ingeschakeld" } diff --git a/homeassistant/components/humidifier/translations/no.json b/homeassistant/components/humidifier/translations/no.json index dbbe95493f0..276577b81c3 100644 --- a/homeassistant/components/humidifier/translations/no.json +++ b/homeassistant/components/humidifier/translations/no.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} sl\u00e5tt p\u00e5 eller av", "target_humidity_changed": "{entity_name} m\u00e5let fuktighet endret", - "toggled": "{entity_name} sl\u00e5tt p\u00e5 eller av", "turned_off": "{entity_name} sl\u00e5tt av", "turned_on": "{entity_name} sl\u00e5tt p\u00e5" } diff --git a/homeassistant/components/humidifier/translations/pl.json b/homeassistant/components/humidifier/translations/pl.json index 5c82a1e944b..d31db39da8c 100644 --- a/homeassistant/components/humidifier/translations/pl.json +++ b/homeassistant/components/humidifier/translations/pl.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "target_humidity_changed": "zmieni si\u0119 wilgotno\u015b\u0107 docelowa {entity_name}", - "toggled": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "turned_off": "nast\u0105pi wy\u0142\u0105czenie {entity_name}", "turned_on": "nast\u0105pi w\u0142\u0105czenie {entity_name}" } diff --git a/homeassistant/components/humidifier/translations/pt-BR.json b/homeassistant/components/humidifier/translations/pt-BR.json index bb4b6c00177..20b4e663f72 100644 --- a/homeassistant/components/humidifier/translations/pt-BR.json +++ b/homeassistant/components/humidifier/translations/pt-BR.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} for ligado ou desligado", "target_humidity_changed": "{entity_name} tiver a umidade alvo alterada", - "toggled": "{entity_name} for ligado ou desligado", "turned_off": "{entity_name} for desligado", "turned_on": "{entity_name} for ligado" } diff --git a/homeassistant/components/humidifier/translations/ru.json b/homeassistant/components/humidifier/translations/ru.json index 5cbaf6e8e54..fe8026c68b6 100644 --- a/homeassistant/components/humidifier/translations/ru.json +++ b/homeassistant/components/humidifier/translations/ru.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "target_humidity_changed": "{entity_name} \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438", - "toggled": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_off": "{entity_name} \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_on": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f" } diff --git a/homeassistant/components/humidifier/translations/sv.json b/homeassistant/components/humidifier/translations/sv.json index 489d8a095a3..325e9f2e6a0 100644 --- a/homeassistant/components/humidifier/translations/sv.json +++ b/homeassistant/components/humidifier/translations/sv.json @@ -1,7 +1,6 @@ { "device_automation": { "trigger_type": { - "toggled": "{entity_name} slogs p\u00e5 eller av", "turned_off": "{entity_name} st\u00e4ngdes av" } } diff --git a/homeassistant/components/humidifier/translations/tr.json b/homeassistant/components/humidifier/translations/tr.json index 41b870d446a..2f2680dce9b 100644 --- a/homeassistant/components/humidifier/translations/tr.json +++ b/homeassistant/components/humidifier/translations/tr.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "target_humidity_changed": "{entity_name} hedef nem de\u011fi\u015fti", - "toggled": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "turned_off": "{entity_name} kapat\u0131ld\u0131", "turned_on": "{entity_name} a\u00e7\u0131ld\u0131" } diff --git a/homeassistant/components/humidifier/translations/zh-Hans.json b/homeassistant/components/humidifier/translations/zh-Hans.json index 7a3185728fc..d21c7bf61f7 100644 --- a/homeassistant/components/humidifier/translations/zh-Hans.json +++ b/homeassistant/components/humidifier/translations/zh-Hans.json @@ -14,7 +14,6 @@ }, "trigger_type": { "target_humidity_changed": "{entity_name} \u7684\u8bbe\u5b9a\u6e7f\u5ea6\u53d8\u5316", - "toggled": "{entity_name} \u88ab\u6253\u5f00\u6216\u5173\u95ed", "turned_off": "{entity_name} \u88ab\u5173\u95ed", "turned_on": "{entity_name} \u88ab\u6253\u5f00" } diff --git a/homeassistant/components/humidifier/translations/zh-Hant.json b/homeassistant/components/humidifier/translations/zh-Hant.json index ed851c65795..eaeba2ab8a4 100644 --- a/homeassistant/components/humidifier/translations/zh-Hant.json +++ b/homeassistant/components/humidifier/translations/zh-Hant.json @@ -15,7 +15,6 @@ "trigger_type": { "changed_states": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "target_humidity_changed": "{entity_name}\u8a2d\u5b9a\u6fd5\u5ea6\u5df2\u8b8a\u66f4", - "toggled": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f" } diff --git a/homeassistant/components/insteon/translations/ca.json b/homeassistant/components/insteon/translations/ca.json index 9805d03c685..f8e0e1c853e 100644 --- a/homeassistant/components/insteon/translations/ca.json +++ b/homeassistant/components/insteon/translations/ca.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Tipus de m\u00f2dem." }, - "description": "Selecciona el tipus de m\u00f2dem Insteon.", - "title": "Insteon" + "description": "Selecciona el tipus de m\u00f2dem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Categoria del dispositiu (ex: 0x10)", "subcat": "Subcategoria del dispositiu (ex: 0x0a)" }, - "description": "Afegeix una substituci\u00f3 de dispositiu.", - "title": "Insteon" + "description": "Afegeix una substituci\u00f3 de dispositiu." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Passos del regulador d'intensitat (nom\u00e9s per a llums, 22 per defecte)", "unitcode": "Unitcode (1-16)" }, - "description": "Canvia la contrasenya de l'Insteon Hub.", - "title": "Insteon" + "description": "Canvia la contrasenya de l'Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Nom d'usuari" }, - "description": "Canvia la informaci\u00f3 de connexi\u00f3 de l'Insteon Hub. Has de reiniciar Home Assistant si fas canvis. Aix\u00f2 no canvia la configuraci\u00f3 del Hub en si. Per canviar la configuraci\u00f3 del Hub, utilitza la seva aplicaci\u00f3.", - "title": "Insteon" + "description": "Canvia la informaci\u00f3 de connexi\u00f3 de l'Insteon Hub. Has de reiniciar Home Assistant si fas canvis. Aix\u00f2 no canvia la configuraci\u00f3 del Hub en si. Per canviar la configuraci\u00f3 del Hub, utilitza la seva aplicaci\u00f3." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Canvia la configuraci\u00f3 del Hub.", "remove_override": "Elimina una substituci\u00f3 de dispositiu.", "remove_x10": "Elimina un dispositiu X10." - }, - "description": "Selecciona una opci\u00f3 a configurar.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Selecciona una adre\u00e7a de dispositiu a eliminar" }, - "description": "Elimina una substituci\u00f3 de dispositiu", - "title": "Insteon" + "description": "Elimina una substituci\u00f3 de dispositiu" }, "remove_x10": { "data": { "address": "Selecciona una adre\u00e7a de dispositiu a eliminar" }, - "description": "Elimina un dispositiu X10", - "title": "Insteon" + "description": "Elimina un dispositiu X10" } } } diff --git a/homeassistant/components/insteon/translations/cs.json b/homeassistant/components/insteon/translations/cs.json index 18d7ff1c999..e10e25e6361 100644 --- a/homeassistant/components/insteon/translations/cs.json +++ b/homeassistant/components/insteon/translations/cs.json @@ -37,8 +37,7 @@ "data": { "modem_type": "Typ modemu." }, - "description": "Vyberte typ modemu Insteon.", - "title": "Insteon" + "description": "Vyberte typ modemu Insteon." } } }, @@ -53,8 +52,7 @@ "data": { "address": "Adresa za\u0159\u00edzen\u00ed (tj. 1a2b3c)", "cat": "Kategorie za\u0159\u00edzen\u00ed (tj. 0x10)" - }, - "title": "Insteon" + } }, "add_x10": { "data": { @@ -62,8 +60,7 @@ "platform": "Platforma", "steps": "Kroky stm\u00edva\u010de (pouze pro sv\u011btla, v\u00fdchoz\u00ed 22)", "unitcode": "K\u00f3d jednotky (1-16)" - }, - "title": "Insteon" + } }, "change_hub_config": { "data": { @@ -72,29 +69,24 @@ "port": "Port", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" }, - "description": "Zm\u011b\u0148te informace o p\u0159ipojen\u00ed rozbo\u010dova\u010de Insteon. Po proveden\u00ed t\u00e9to zm\u011bny mus\u00edte Home Assistant restartovat. T\u00edm se nezm\u011bn\u00ed nastaven\u00ed samotn\u00e9ho rozbo\u010dova\u010de. Chcete-li zm\u011bnit nastaven\u00ed rozbo\u010dova\u010de, pou\u017eijte jeho aplikaci.", - "title": "Insteon" + "description": "Zm\u011b\u0148te informace o p\u0159ipojen\u00ed rozbo\u010dova\u010de Insteon. Po proveden\u00ed t\u00e9to zm\u011bny mus\u00edte Home Assistant restartovat. T\u00edm se nezm\u011bn\u00ed nastaven\u00ed samotn\u00e9ho rozbo\u010dova\u010de. Chcete-li zm\u011bnit nastaven\u00ed rozbo\u010dova\u010de, pou\u017eijte jeho aplikaci." }, "init": { "data": { "add_x10": "P\u0159idejte za\u0159\u00edzen\u00ed X10.", "remove_x10": "Odeberte za\u0159\u00edzen\u00ed X10." - }, - "description": "Vyberte mo\u017enost k nastaven\u00ed.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Vyberte adresu za\u0159\u00edzen\u00ed, kter\u00e9 chcete odebrat" - }, - "title": "Insteon" + } }, "remove_x10": { "data": { "address": "Vyberte adresu za\u0159\u00edzen\u00ed, kter\u00e9 chcete odebrat" }, - "description": "Odeberte za\u0159\u00edzen\u00ed X10", - "title": "Insteon" + "description": "Odeberte za\u0159\u00edzen\u00ed X10" } } } diff --git a/homeassistant/components/insteon/translations/de.json b/homeassistant/components/insteon/translations/de.json index 164fbccceed..d7f853d9a2a 100644 --- a/homeassistant/components/insteon/translations/de.json +++ b/homeassistant/components/insteon/translations/de.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modemtyp." }, - "description": "W\u00e4hle den Insteon-Modemtyp aus.", - "title": "Insteon" + "description": "W\u00e4hle den Insteon-Modemtyp aus." } } }, @@ -61,8 +60,7 @@ "cat": "Ger\u00e4tekategorie (z. B. 0x10)", "subcat": "Ger\u00e4teunterkategorie (z. B. 0x0a)" }, - "description": "F\u00fcge eine Ger\u00e4te\u00fcberschreibung hinzu.", - "title": "Insteon" + "description": "F\u00fcge eine Ger\u00e4te\u00fcberschreibung hinzu." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Dimmerstufen (nur f\u00fcr Lichtger\u00e4te, Voreinstellung 22)", "unitcode": "Unitcode (1 - 16)" }, - "description": "\u00c4ndere das Insteon Hub-Passwort.", - "title": "Insteon" + "description": "\u00c4ndere das Insteon Hub-Passwort." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Benutzername" }, - "description": "\u00c4ndere die Verbindungsinformationen des Insteon-Hubs. Du musst Home Assistant neu starten, nachdem du diese \u00c4nderung vorgenommen hast. Dies \u00e4ndert nicht die Konfiguration des Hubs selbst. Um die Konfiguration im Hub zu \u00e4ndern, verwende die Hub-App.", - "title": "Insteon" + "description": "\u00c4ndere die Verbindungsinformationen des Insteon-Hubs. Du musst Home Assistant neu starten, nachdem du diese \u00c4nderung vorgenommen hast. Dies \u00e4ndert nicht die Konfiguration des Hubs selbst. Um die Konfiguration im Hub zu \u00e4ndern, verwende die Hub-App." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "\u00c4ndere die Konfiguration des Hubs.", "remove_override": "Entferne eine Ger\u00e4te\u00fcbersteuerung.", "remove_x10": "Entferne ein X10-Ger\u00e4t." - }, - "description": "W\u00e4hle eine Option zum Konfigurieren aus.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "W\u00e4hle eine Ger\u00e4teadresse zum Entfernen" }, - "description": "Entfernen einer Ger\u00e4te\u00fcbersteuerung", - "title": "Insteon" + "description": "Entfernen einer Ger\u00e4te\u00fcbersteuerung" }, "remove_x10": { "data": { "address": "W\u00e4hle eine Ger\u00e4teadresse zum Entfernen" }, - "description": "Ein X10-Ger\u00e4t entfernen", - "title": "Insteon" + "description": "Ein X10-Ger\u00e4t entfernen" } } } diff --git a/homeassistant/components/insteon/translations/el.json b/homeassistant/components/insteon/translations/el.json index 4a4c402ddc0..9ff9e025f8f 100644 --- a/homeassistant/components/insteon/translations/el.json +++ b/homeassistant/components/insteon/translations/el.json @@ -43,8 +43,7 @@ "data": { "modem_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc." }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc Insteon.", - "title": "Insteon" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03cd\u03c0\u03bf \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "\u039a\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 (\u03c0.\u03c7. 0x10)", "subcat": "\u03a5\u03c0\u03bf\u03ba\u03b1\u03c4\u03b7\u03b3\u03bf\u03c1\u03af\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 (\u03c0.\u03c7. 0x0a)" }, - "description": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2.", - "title": "Insteon" + "description": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "\u0392\u03ae\u03bc\u03b1\u03c4\u03b1 \u03c1\u03bf\u03bf\u03c3\u03c4\u03ac\u03c4\u03b7 (\u03bc\u03cc\u03bd\u03bf \u03b3\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c6\u03c9\u03c4\u03b9\u03c3\u03bc\u03bf\u03cd, \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae 22)", "unitcode": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03bc\u03bf\u03bd\u03ac\u03b4\u03b1\u03c2 (1 - 16)" }, - "description": "\u0391\u03bb\u03bb\u03ac\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Insteon Hub.", - "title": "Insteon" + "description": "\u0391\u03bb\u03bb\u03ac\u03be\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "\u0398\u03cd\u03c1\u03b1", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, - "description": "\u0391\u03bb\u03bb\u03ac\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Insteon Hub. \u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03bc\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2. \u0391\u03c5\u03c4\u03cc \u03b4\u03b5\u03bd \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03af\u03b4\u03b9\u03bf\u03c5 \u03c4\u03bf\u03c5 Hub. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c3\u03c4\u03bf Hub \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Hub.", - "title": "Insteon" + "description": "\u0391\u03bb\u03bb\u03ac\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Insteon Hub. \u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03bc\u03b5\u03c4\u03ac \u03c4\u03b7\u03bd \u03c0\u03c1\u03b1\u03b3\u03bc\u03b1\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae\u03c2. \u0391\u03c5\u03c4\u03cc \u03b4\u03b5\u03bd \u03b1\u03bb\u03bb\u03ac\u03b6\u03b5\u03b9 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03af\u03b4\u03b9\u03bf\u03c5 \u03c4\u03bf\u03c5 Hub. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c3\u03c4\u03bf Hub \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "\u0391\u03bb\u03bb\u03ac\u03be\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Hub.", "remove_override": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03bc\u03b9\u03b1\u03c2 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2.", "remove_x10": "\u0391\u03c6\u03b1\u03b9\u03c1\u03ad\u03c3\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae X10." - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03b3\u03b9\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7" }, - "description": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", - "title": "Insteon" + "description": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" }, "remove_x10": { "data": { "address": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7" }, - "description": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 X10", - "title": "Insteon" + "description": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 X10" } } } diff --git a/homeassistant/components/insteon/translations/en.json b/homeassistant/components/insteon/translations/en.json index f9e64dcf3cf..441f5cf576a 100644 --- a/homeassistant/components/insteon/translations/en.json +++ b/homeassistant/components/insteon/translations/en.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modem type." }, - "description": "Select the Insteon modem type.", - "title": "Insteon" + "description": "Select the Insteon modem type." } } }, @@ -61,8 +60,7 @@ "cat": "Device category (i.e. 0x10)", "subcat": "Device subcategory (i.e. 0x0a)" }, - "description": "Add a device override.", - "title": "Insteon" + "description": "Add a device override." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Dimmer steps (for light devices only, default 22)", "unitcode": "Unitcode (1 - 16)" }, - "description": "Change the Insteon Hub password.", - "title": "Insteon" + "description": "Change the Insteon Hub password." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Username" }, - "description": "Change the Insteon Hub connection information. You must restart Home Assistant after making this change. This does not change the configuration of the Hub itself. To change the configuration in the Hub use the Hub app.", - "title": "Insteon" + "description": "Change the Insteon Hub connection information. You must restart Home Assistant after making this change. This does not change the configuration of the Hub itself. To change the configuration in the Hub use the Hub app." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Change the Hub configuration.", "remove_override": "Remove a device override.", "remove_x10": "Remove an X10 device." - }, - "description": "Select an option to configure.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Select a device address to remove" }, - "description": "Remove a device override", - "title": "Insteon" + "description": "Remove a device override" }, "remove_x10": { "data": { "address": "Select a device address to remove" }, - "description": "Remove an X10 device", - "title": "Insteon" + "description": "Remove an X10 device" } } } diff --git a/homeassistant/components/insteon/translations/es.json b/homeassistant/components/insteon/translations/es.json index b7b72cefa28..5434bc77a8a 100644 --- a/homeassistant/components/insteon/translations/es.json +++ b/homeassistant/components/insteon/translations/es.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Tipo de m\u00f3dem." }, - "description": "Seleccione el tipo de m\u00f3dem Insteon.", - "title": "Insteon" + "description": "Seleccione el tipo de m\u00f3dem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Categor\u00eda del dispositivo (es decir, 0x10)", "subcat": "Subcategor\u00eda del dispositivo (es decir, 0x0a)" }, - "description": "Agregue una anulaci\u00f3n del dispositivo.", - "title": "Insteon" + "description": "Agregue una anulaci\u00f3n del dispositivo." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Pasos de atenuaci\u00f3n (s\u00f3lo para dispositivos de luz, por defecto 22)", "unitcode": "Unitcode (1 - 16)" }, - "description": "Cambie la contrase\u00f1a del Hub Insteon.", - "title": "Insteon" + "description": "Cambie la contrase\u00f1a del Hub Insteon." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Puerto", "username": "Usuario" }, - "description": "Cambiar la informaci\u00f3n de la conexi\u00f3n del Hub Insteon. Debes reiniciar el Home Assistant despu\u00e9s de hacer este cambio. Esto no cambia la configuraci\u00f3n del Hub en s\u00ed. Para cambiar la configuraci\u00f3n del Hub usa la aplicaci\u00f3n Hub.", - "title": "Insteon" + "description": "Cambiar la informaci\u00f3n de la conexi\u00f3n del Hub Insteon. Debes reiniciar el Home Assistant despu\u00e9s de hacer este cambio. Esto no cambia la configuraci\u00f3n del Hub en s\u00ed. Para cambiar la configuraci\u00f3n del Hub usa la aplicaci\u00f3n Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Cambie la configuraci\u00f3n del Hub.", "remove_override": "Eliminar una anulaci\u00f3n del dispositivo.", "remove_x10": "Eliminar un dispositivo X10" - }, - "description": "Seleccione una opci\u00f3n para configurar.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Seleccione una direcci\u00f3n del dispositivo para eliminar" }, - "description": "Eliminar una anulaci\u00f3n del dispositivo", - "title": "Insteon" + "description": "Eliminar una anulaci\u00f3n del dispositivo" }, "remove_x10": { "data": { "address": "Seleccione la direcci\u00f3n del dispositivo para eliminar" }, - "description": "Eliminar un dispositivo X10", - "title": "Insteon" + "description": "Eliminar un dispositivo X10" } } } diff --git a/homeassistant/components/insteon/translations/et.json b/homeassistant/components/insteon/translations/et.json index eff2fd9b9b2..72f2a9ac5c4 100644 --- a/homeassistant/components/insteon/translations/et.json +++ b/homeassistant/components/insteon/translations/et.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modemi mudel." }, - "description": "Vali Insteoni modemi mudel.", - "title": "" + "description": "Vali Insteoni modemi mudel." } } }, @@ -61,8 +60,7 @@ "cat": "Seadme kategooria (n\u00e4iteks 0x10)", "subcat": "Seadme alamkategooria (n\u00e4iteks 0x0a)" }, - "description": "Lisa seadme alistamine.", - "title": "" + "description": "Lisa seadme alistamine." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "H\u00e4mardamise samm (ainult valgustite jaoks, vaikimisi 22)", "unitcode": "" }, - "description": "Muuda Insteon Hubi parooli.", - "title": "" + "description": "Muuda Insteon Hubi parooli." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "", "username": "Kasutajanimi" }, - "description": "Muutda Insteon Hubi \u00fchenduse teavet. P\u00e4rast selle muudatuse tegemist pead Home Assistanti taask\u00e4ivitama. See ei muuda jaoturi enda konfiguratsiooni. Hubis muudatuste tegemiseks kasuta rakendust Hub.", - "title": "" + "description": "Muutda Insteon Hubi \u00fchenduse teavet. P\u00e4rast selle muudatuse tegemist pead Home Assistanti taask\u00e4ivitama. See ei muuda jaoturi enda konfiguratsiooni. Hubis muudatuste tegemiseks kasuta rakendust Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Muuda jaoturi konfiguratsiooni.", "remove_override": "Seadme alistamise eemaldamine.", "remove_x10": "Eemalda X10 seade." - }, - "description": "Vali seadistussuvand.", - "title": "" + } }, "remove_override": { "data": { "address": "Vali eemaldatava seadme aadress" }, - "description": "Seadme alistamise eemaldamine", - "title": "" + "description": "Seadme alistamise eemaldamine" }, "remove_x10": { "data": { "address": "Vali eemaldatava seadme aadress" }, - "description": "Eemalda X10 seade", - "title": "" + "description": "Eemalda X10 seade" } } } diff --git a/homeassistant/components/insteon/translations/fr.json b/homeassistant/components/insteon/translations/fr.json index 2bb0e4d2e33..e9570aaf7e2 100644 --- a/homeassistant/components/insteon/translations/fr.json +++ b/homeassistant/components/insteon/translations/fr.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Type de modem." }, - "description": "S\u00e9lectionnez le type de modem Insteon.", - "title": "Insteon" + "description": "S\u00e9lectionnez le type de modem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Cat\u00e9gorie d'appareil (c.-\u00e0-d. 0x10)", "subcat": "Sous-cat\u00e9gorie d'appareil (par exemple 0x0a)" }, - "description": "Ajouter un remplacement d'appareil.", - "title": "Insteon" + "description": "Ajouter un remplacement d'appareil." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Pas de gradateur (pour les appareils d'\u00e9clairage uniquement, par d\u00e9faut 22)", "unitcode": "Code de l'unit\u00e9 (1-16)" }, - "description": "Modifiez le mot de passe Insteon Hub.", - "title": "Insteon" + "description": "Modifiez le mot de passe Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Nom d'utilisateur" }, - "description": "Modifiez les informations de connexion Insteon Hub. Vous devez red\u00e9marrer Home Assistant apr\u00e8s avoir effectu\u00e9 cette modification. Cela ne change pas la configuration du Hub lui-m\u00eame. Pour modifier la configuration dans le Hub, utilisez l'application Hub.", - "title": "Insteon" + "description": "Modifiez les informations de connexion Insteon Hub. Vous devez red\u00e9marrer Home Assistant apr\u00e8s avoir effectu\u00e9 cette modification. Cela ne change pas la configuration du Hub lui-m\u00eame. Pour modifier la configuration dans le Hub, utilisez l'application Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Modifier la configuration du Hub.", "remove_override": "Supprimer un remplacement d'appareil", "remove_x10": "Retirer un appareil X10." - }, - "description": "S\u00e9lectionnez une option \u00e0 configurer.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "S\u00e9lectionnez l'adresse d'un appareil \u00e0 retirer" }, - "description": "Supprimer un remplacement d'appareil", - "title": "Insteon" + "description": "Supprimer un remplacement d'appareil" }, "remove_x10": { "data": { "address": "S\u00e9lectionnez l'adresse d'un appareil \u00e0 retirer" }, - "description": "Retirer un appareil X10", - "title": "Insteon" + "description": "Retirer un appareil X10" } } } diff --git a/homeassistant/components/insteon/translations/hu.json b/homeassistant/components/insteon/translations/hu.json index 560c1fc118e..8ba8e3223fa 100644 --- a/homeassistant/components/insteon/translations/hu.json +++ b/homeassistant/components/insteon/translations/hu.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modem t\u00edpusa." }, - "description": "V\u00e1lassza ki az Insteon modem t\u00edpus\u00e1t.", - "title": "Insteon" + "description": "V\u00e1lassza ki az Insteon modem t\u00edpus\u00e1t." } } }, @@ -61,8 +60,7 @@ "cat": "Eszk\u00f6zkateg\u00f3ria (pl. 0x10)", "subcat": "Eszk\u00f6z alkateg\u00f3ria (pl. 0x0a)" }, - "description": "Eszk\u00f6z-fel\u00fclb\u00edr\u00e1l\u00e1s hozz\u00e1ad\u00e1sa.", - "title": "Insteon" + "description": "Eszk\u00f6z-fel\u00fclb\u00edr\u00e1l\u00e1s hozz\u00e1ad\u00e1sa." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "F\u00e9nyer\u0151-szab\u00e1lyoz\u00e1si l\u00e9p\u00e9sek (csak k\u00f6nny\u0171 eszk\u00f6z\u00f6k eset\u00e9n, alap\u00e9rtelmezett 22)", "unitcode": "Egys\u00e9gk\u00f3d (1 - 16)" }, - "description": "M\u00f3dos\u00edtsa az Insteon Hub jelszav\u00e1t.", - "title": "Insteon" + "description": "M\u00f3dos\u00edtsa az Insteon Hub jelszav\u00e1t." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "M\u00f3dos\u00edtsa az Insteon Hub csatlakoz\u00e1si adatait. A m\u00f3dos\u00edt\u00e1s elv\u00e9gz\u00e9se ut\u00e1n \u00fajra kell ind\u00edtania a Home Assistant alkalmaz\u00e1st. Ez nem v\u00e1ltoztatja meg a Hub konfigur\u00e1ci\u00f3j\u00e1t. A Hub konfigur\u00e1ci\u00f3j\u00e1nak m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a Hub alkalmaz\u00e1st.", - "title": "Insteon" + "description": "M\u00f3dos\u00edtsa az Insteon Hub csatlakoz\u00e1si adatait. A m\u00f3dos\u00edt\u00e1s elv\u00e9gz\u00e9se ut\u00e1n \u00fajra kell ind\u00edtania a Home Assistant alkalmaz\u00e1st. Ez nem v\u00e1ltoztatja meg a Hub konfigur\u00e1ci\u00f3j\u00e1t. A Hub konfigur\u00e1ci\u00f3j\u00e1nak m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a Hub alkalmaz\u00e1st." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "M\u00f3dos\u00edtsa a Hub konfigur\u00e1ci\u00f3j\u00e1t.", "remove_override": "Egy eszk\u00f6z fel\u00fclb\u00edr\u00e1lat\u00e1nak elt\u00e1vol\u00edt\u00e1sa.", "remove_x10": "T\u00e1vol\u00edtson el egy X10 eszk\u00f6zt." - }, - "description": "V\u00e1lasszon egy be\u00e1ll\u00edt\u00e1st.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "V\u00e1lassza ki az elt\u00e1vol\u00edtani k\u00edv\u00e1nt eszk\u00f6z c\u00edm\u00e9t" }, - "description": "T\u00e1vol\u00edtsa el az eszk\u00f6z fel\u00fclb\u00edr\u00e1l\u00e1s\u00e1t", - "title": "Insteon" + "description": "T\u00e1vol\u00edtsa el az eszk\u00f6z fel\u00fclb\u00edr\u00e1l\u00e1s\u00e1t" }, "remove_x10": { "data": { "address": "V\u00e1lassza ki az elt\u00e1vol\u00edtani k\u00edv\u00e1nt eszk\u00f6z c\u00edm\u00e9t" }, - "description": "T\u00e1vol\u00edtson el egy X10 eszk\u00f6zt", - "title": "Insteon" + "description": "T\u00e1vol\u00edtson el egy X10 eszk\u00f6zt" } } } diff --git a/homeassistant/components/insteon/translations/id.json b/homeassistant/components/insteon/translations/id.json index aae7e9f71ac..7520c6cd03d 100644 --- a/homeassistant/components/insteon/translations/id.json +++ b/homeassistant/components/insteon/translations/id.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Jenis modem." }, - "description": "Pilih jenis modem Insteon.", - "title": "Insteon" + "description": "Pilih jenis modem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Kategori perangkat (mis. 0x10)", "subcat": "Subkategori perangkat (mis. 0x0a)" }, - "description": "Tambahkan penimpaan nilai perangkat.", - "title": "Insteon" + "description": "Tambahkan penimpaan nilai perangkat." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Langkah peredup (hanya untuk perangkat ringan, nilai bawaan adalah 22)", "unitcode": "Unitcode (1 - 16)" }, - "description": "Ubah kata sandi Insteon Hub.", - "title": "Insteon" + "description": "Ubah kata sandi Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Nama Pengguna" }, - "description": "Ubah informasi koneksi Insteon Hub. Anda harus memulai ulang Home Assistant setelah melakukan perubahan ini. Ini tidak mengubah konfigurasi Hub itu sendiri. Untuk mengubah konfigurasi di Hub, gunakan aplikasi Hub.", - "title": "Insteon" + "description": "Ubah informasi koneksi Insteon Hub. Anda harus memulai ulang Home Assistant setelah melakukan perubahan ini. Ini tidak mengubah konfigurasi Hub itu sendiri. Untuk mengubah konfigurasi di Hub, gunakan aplikasi Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Ubah konfigurasi Hub.", "remove_override": "Hapus penimpaan nilai perangkat.", "remove_x10": "Hapus perangkat X10." - }, - "description": "Pilih opsi untuk dikonfigurasi.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Pilih alamat perangkat untuk dihapus" }, - "description": "Hapus penimpaan nilai perangkat", - "title": "Insteon" + "description": "Hapus penimpaan nilai perangkat" }, "remove_x10": { "data": { "address": "Pilih alamat perangkat untuk dihapus" }, - "description": "Hapus perangkat X10", - "title": "Insteon" + "description": "Hapus perangkat X10" } } } diff --git a/homeassistant/components/insteon/translations/it.json b/homeassistant/components/insteon/translations/it.json index b47a06c9d7a..c5bd0f1af87 100644 --- a/homeassistant/components/insteon/translations/it.json +++ b/homeassistant/components/insteon/translations/it.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Tipo di modem." }, - "description": "Seleziona il tipo di modem Insteon.", - "title": "Insteon" + "description": "Seleziona il tipo di modem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Categoria del dispositivo (ad es. 0x10)", "subcat": "Sottocategoria del dispositivo (ad es. 0x0a)" }, - "description": "Aggiungi una sostituzione del dispositivo.", - "title": "Insteon" + "description": "Aggiungi una sostituzione del dispositivo." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Livelli del dimmer (solo per dispositivi luminosi, predefiniti 22)", "unitcode": "Codice unit\u00e0 (1 - 16)" }, - "description": "Cambia la password di Insteon Hub.", - "title": "Insteon" + "description": "Cambia la password di Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Porta", "username": "Nome utente" }, - "description": "Modifica le informazioni di connessione di Insteon Hub. \u00c8 necessario riavviare Home Assistant dopo aver apportato questa modifica. Ci\u00f2 non modifica la configurazione dell'Hub stesso. Per modificare la configurazione nell'Hub, utilizza l'app Hub.", - "title": "Insteon" + "description": "Modifica le informazioni di connessione di Insteon Hub. \u00c8 necessario riavviare Home Assistant dopo aver apportato questa modifica. Ci\u00f2 non modifica la configurazione dell'Hub stesso. Per modificare la configurazione nell'Hub, utilizza l'app Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Modifica la configurazione dell'hub.", "remove_override": "Rimuovere una sostituzione del dispositivo.", "remove_x10": "Rimuovi un dispositivo X10." - }, - "description": "Seleziona un'opzione da configurare.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Seleziona un indirizzo del dispositivo da rimuovere" }, - "description": "Rimuovere una sostituzione del dispositivo", - "title": "Insteon" + "description": "Rimuovere una sostituzione del dispositivo" }, "remove_x10": { "data": { "address": "Seleziona un indirizzo del dispositivo da rimuovere" }, - "description": "Rimuovi un dispositivo X10", - "title": "Insteon" + "description": "Rimuovi un dispositivo X10" } } } diff --git a/homeassistant/components/insteon/translations/ja.json b/homeassistant/components/insteon/translations/ja.json index 81b7f246e41..812f9c0bfd6 100644 --- a/homeassistant/components/insteon/translations/ja.json +++ b/homeassistant/components/insteon/translations/ja.json @@ -43,8 +43,7 @@ "data": { "modem_type": "\u30e2\u30c7\u30e0\u306e\u7a2e\u985e\u3002" }, - "description": "Insteon modem type\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Insteon" + "description": "Insteon modem type\u3092\u9078\u629e\u3057\u307e\u3059\u3002" } } }, @@ -61,8 +60,7 @@ "cat": "\u30c7\u30d0\u30a4\u30b9\u30ab\u30c6\u30b4\u30ea\u30fc(\u4f8b: 0x10)", "subcat": "\u30c7\u30d0\u30a4\u30b9 \u30b5\u30d6\u30ab\u30c6\u30b4\u30ea\u30fc(\u4f8b: 0x0a)" }, - "description": "\u30c7\u30d0\u30a4\u30b9\u3092\u8ffd\u52a0\u3057\u3066\u4e0a\u66f8\u304d\u3057\u307e\u3059\u3002", - "title": "Insteon" + "description": "\u30c7\u30d0\u30a4\u30b9\u3092\u8ffd\u52a0\u3057\u3066\u4e0a\u66f8\u304d\u3057\u307e\u3059\u3002" }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "\u8abf\u5149\u30b9\u30c6\u30c3\u30d7(\u30e9\u30a4\u30c8\u30c7\u30d0\u30a4\u30b9\u306e\u307f\u3001\u30c7\u30d5\u30a9\u30eb\u30c822)", "unitcode": "\u30e6\u30cb\u30c3\u30c8\u30b3\u30fc\u30c9(1\u301c16)" }, - "description": "Insteon Hub\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5909\u66f4\u3059\u308b\u3002", - "title": "Insteon" + "description": "Insteon Hub\u306e\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5909\u66f4\u3059\u308b\u3002" }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "\u30dd\u30fc\u30c8", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, - "description": "Insteon Hub\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u5909\u66f4\u3057\u307e\u3059\u3002\u3053\u306e\u5909\u66f4\u3092\u884c\u3063\u305f\u5f8c\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u308c\u306f\u3001Hub\u81ea\u4f53\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3059\u308b\u3082\u306e\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002Hub\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001Hub\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002", - "title": "Insteon" + "description": "Insteon Hub\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u5909\u66f4\u3057\u307e\u3059\u3002\u3053\u306e\u5909\u66f4\u3092\u884c\u3063\u305f\u5f8c\u3001Home Assistant\u3092\u518d\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u308c\u306f\u3001Hub\u81ea\u4f53\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3059\u308b\u3082\u306e\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002Hub\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3059\u308b\u306b\u306f\u3001Hub\u30a2\u30d7\u30ea\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002" }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Hub\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3057\u307e\u3059\u3002", "remove_override": "\u30c7\u30d0\u30a4\u30b9\u3092\u524a\u9664\u3057\u3066\u4e0a\u66f8\u304d\u3057\u307e\u3059\u3002", "remove_x10": "X10\u30c7\u30d0\u30a4\u30b9\u524a\u9664\u3092\u524a\u9664\u3057\u307e\u3059\u3002" - }, - "description": "\u8a2d\u5b9a\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\u524a\u9664\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u306e\u30a2\u30c9\u30ec\u30b9\u3092\u9078\u629e" }, - "description": "\u30c7\u30d0\u30a4\u30b9\u3092\u524a\u9664\u3057\u3066\u4e0a\u66f8\u304d\u3057\u307e\u3059", - "title": "Insteon" + "description": "\u30c7\u30d0\u30a4\u30b9\u3092\u524a\u9664\u3057\u3066\u4e0a\u66f8\u304d\u3057\u307e\u3059" }, "remove_x10": { "data": { "address": "\u524a\u9664\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u306e\u30a2\u30c9\u30ec\u30b9\u3092\u9078\u629e" }, - "description": "X10\u30c7\u30d0\u30a4\u30b9\u306e\u524a\u9664", - "title": "Insteon" + "description": "X10\u30c7\u30d0\u30a4\u30b9\u306e\u524a\u9664" } } } diff --git a/homeassistant/components/insteon/translations/ko.json b/homeassistant/components/insteon/translations/ko.json index 559e3351932..d1a4624f7bf 100644 --- a/homeassistant/components/insteon/translations/ko.json +++ b/homeassistant/components/insteon/translations/ko.json @@ -8,7 +8,11 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "select_single": "\ud558\ub098\uc758 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694." }, + "flow_title": "{name}", "step": { + "confirm_usb": { + "description": "{name} \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, "hubv1": { "data": { "host": "IP \uc8fc\uc18c", @@ -38,8 +42,7 @@ "data": { "modem_type": "\ubaa8\ub380 \uc720\ud615." }, - "description": "Insteon \ubaa8\ub380 \uc720\ud615\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", - "title": "Insteon" + "description": "Insteon \ubaa8\ub380 \uc720\ud615\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694." } } }, @@ -56,8 +59,7 @@ "cat": "\uae30\uae30 \ubc94\uc8fc (\uc608: 0x10)", "subcat": "\uae30\uae30 \ud558\uc704 \ubc94\uc8fc (\uc608: 0x0a)" }, - "description": "\uae30\uae30 \uc7ac\uc815\uc758\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4.", - "title": "Insteon" + "description": "\uae30\uae30 \uc7ac\uc815\uc758\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4." }, "add_x10": { "data": { @@ -66,8 +68,7 @@ "steps": "\ubc1d\uae30 \uc870\uc808 \ub2e8\uacc4 (\uc870\uba85 \uae30\uae30 \uc804\uc6a9, \uae30\ubcf8\uac12 22)", "unitcode": "\uc720\ub2db \ucf54\ub4dc (1-16)" }, - "description": "Insteon Hub \ube44\ubc00\ubc88\ud638\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4.", - "title": "Insteon" + "description": "Insteon Hub \ube44\ubc00\ubc88\ud638\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4." }, "change_hub_config": { "data": { @@ -76,8 +77,7 @@ "port": "\ud3ec\ud2b8", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" }, - "description": "Insteon \ud5c8\ube0c\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4. \ubcc0\uacbd\ud55c \ud6c4\uc5d0\ub294 Home Assistant\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4. \ud5c8\ube0c \uc790\uccb4\uc758 \uad6c\uc131\uc740 \ubcc0\uacbd\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ud5c8\ube0c\uc758 \uad6c\uc131\uc744 \ubcc0\uacbd\ud558\ub824\uba74 \ud5c8\ube0c \uc571\uc744 \uc0ac\uc6a9\ud574\uc8fc\uc138\uc694.", - "title": "Insteon" + "description": "Insteon \ud5c8\ube0c\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4. \ubcc0\uacbd\ud55c \ud6c4\uc5d0\ub294 Home Assistant\ub97c \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4. \ud5c8\ube0c \uc790\uccb4\uc758 \uad6c\uc131\uc740 \ubcc0\uacbd\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \ud5c8\ube0c\uc758 \uad6c\uc131\uc744 \ubcc0\uacbd\ud558\ub824\uba74 \ud5c8\ube0c \uc571\uc744 \uc0ac\uc6a9\ud574\uc8fc\uc138\uc694." }, "init": { "data": { @@ -86,23 +86,19 @@ "change_hub_config": "\ud5c8\ube0c \uad6c\uc131\uc744 \ubcc0\uacbd\ud569\ub2c8\ub2e4.", "remove_override": "\uae30\uae30 \uc7ac\uc815\uc758\ub97c \uc81c\uac70\ud569\ub2c8\ub2e4.", "remove_x10": "X10 \uae30\uae30\ub97c \uc81c\uac70\ud569\ub2c8\ub2e4." - }, - "description": "\uad6c\uc131\ud560 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\uc81c\uac70\ud560 \uae30\uae30\uc758 \uc8fc\uc18c\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694" }, - "description": "\uae30\uae30 \uc7ac\uc815\uc758 \uc81c\uac70\ud558\uae30", - "title": "Insteon" + "description": "\uae30\uae30 \uc7ac\uc815\uc758 \uc81c\uac70\ud558\uae30" }, "remove_x10": { "data": { "address": "\uc81c\uac70\ud560 \uae30\uae30\uc758 \uc8fc\uc18c\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694" }, - "description": "X10 \uae30\uae30 \uc81c\uac70\ud558\uae30", - "title": "Insteon" + "description": "X10 \uae30\uae30 \uc81c\uac70\ud558\uae30" } } } diff --git a/homeassistant/components/insteon/translations/lb.json b/homeassistant/components/insteon/translations/lb.json index ea8bd295900..f5ebf3ea19f 100644 --- a/homeassistant/components/insteon/translations/lb.json +++ b/homeassistant/components/insteon/translations/lb.json @@ -38,8 +38,7 @@ "data": { "modem_type": "Typ vu Modem." }, - "description": "Insteon Modem Typ auswielen.", - "title": "Insteon" + "description": "Insteon Modem Typ auswielen." } } }, @@ -56,8 +55,7 @@ "cat": "Apparat Kategorie (Beispill 0x10)", "subcat": "Apparat \u00cbnnerkategorie (Beispill 0x0a)" }, - "description": "Apparat iwwerschr\u00e9iwen dob\u00e4isetzen", - "title": "Insteon" + "description": "Apparat iwwerschr\u00e9iwen dob\u00e4isetzen" }, "add_x10": { "data": { @@ -66,8 +64,7 @@ "steps": "Dimmer Schr\u00ebtt (n\u00ebmme fir Luuchten, standard 22)", "unitcode": "Unitcode (1-16)" }, - "description": "Insteon Hub passwuert \u00e4nneren", - "title": "Insteon" + "description": "Insteon Hub passwuert \u00e4nneren" }, "change_hub_config": { "data": { @@ -76,8 +73,7 @@ "port": "Port", "username": "Benotzernumm" }, - "description": "Insteon Hub Verbindungs Informatiounen \u00e4nneren. Du muss Home Assistant no der \u00c4nnerung fr\u00ebsch starten. D\u00ebst \u00e4nnert net d'Konfiguratioun vum Hub selwer. Fir d'Konfiguratioun vum Hub ze \u00e4nnere benotz d'Hub App.", - "title": "Insteon" + "description": "Insteon Hub Verbindungs Informatiounen \u00e4nneren. Du muss Home Assistant no der \u00c4nnerung fr\u00ebsch starten. D\u00ebst \u00e4nnert net d'Konfiguratioun vum Hub selwer. Fir d'Konfiguratioun vum Hub ze \u00e4nnere benotz d'Hub App." }, "init": { "data": { @@ -86,23 +82,19 @@ "change_hub_config": "Hub Konfiguratioun \u00e4nneren.", "remove_override": "Apparat iwwerschr\u00e9iwen l\u00e4schen", "remove_x10": "Een X10 Apparat l\u00e4schen." - }, - "description": "Eng Optioun auswielen fir ze konfigur\u00e9ieren", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Eng Apparat Adress auswielen fir ze l\u00e4schen" }, - "description": "Apparat iwwerschr\u00e9iwen l\u00e4schen", - "title": "Insteon" + "description": "Apparat iwwerschr\u00e9iwen l\u00e4schen" }, "remove_x10": { "data": { "address": "Wiel eng Adress vun egem Apparat aus fir ze l\u00e4schen" }, - "description": "Een X10 Apparat l\u00e4schen", - "title": "Insteon" + "description": "Een X10 Apparat l\u00e4schen" } } } diff --git a/homeassistant/components/insteon/translations/nl.json b/homeassistant/components/insteon/translations/nl.json index 207e370e245..59e799a084c 100644 --- a/homeassistant/components/insteon/translations/nl.json +++ b/homeassistant/components/insteon/translations/nl.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modemtype." }, - "description": "Selecteer het Insteon-modemtype.", - "title": "Insteon" + "description": "Selecteer het Insteon-modemtype." } } }, @@ -61,8 +60,7 @@ "cat": "Apparaatcategorie (bijv. 0x10)", "subcat": "Apparaatsubcategorie (bijv. 0x0a)" }, - "description": "Voeg een apparaat overschrijven toe.", - "title": "Insteon" + "description": "Voeg een apparaat overschrijven toe." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Dimmerstappen (alleen voor verlichtingsapparaten, standaard 22)", "unitcode": "Unitcode (1 - 16)" }, - "description": "Wijzig het wachtwoord van de Insteon Hub.", - "title": "Insteon" + "description": "Wijzig het wachtwoord van de Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Poort", "username": "Gebruikersnaam" }, - "description": "Wijzig de verbindingsgegevens van de Insteon Hub. Je moet Home Assistant opnieuw opstarten nadat je deze wijziging hebt aangebracht. Dit verandert niets aan de configuratie van de Hub zelf. Gebruik de Hub-app om de configuratie in de Hub te wijzigen.", - "title": "Insteon" + "description": "Wijzig de verbindingsgegevens van de Insteon Hub. Je moet Home Assistant opnieuw opstarten nadat je deze wijziging hebt aangebracht. Dit verandert niets aan de configuratie van de Hub zelf. Gebruik de Hub-app om de configuratie in de Hub te wijzigen." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Wijzig de Hub-configuratie.", "remove_override": "Verwijder een apparaatoverschrijving.", "remove_x10": "Verwijder een X10-apparaat." - }, - "description": "Selecteer een optie om te configureren.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Selecteer een apparaatadres om te verwijderen" }, - "description": "Verwijder een apparaatoverschrijving", - "title": "Insteon" + "description": "Verwijder een apparaatoverschrijving" }, "remove_x10": { "data": { "address": "Selecteer een apparaatadres om te verwijderen" }, - "description": "Verwijder een X10 apparaat", - "title": "Insteon" + "description": "Verwijder een X10 apparaat" } } } diff --git a/homeassistant/components/insteon/translations/no.json b/homeassistant/components/insteon/translations/no.json index 3716312b00f..70ecd17fb21 100644 --- a/homeassistant/components/insteon/translations/no.json +++ b/homeassistant/components/insteon/translations/no.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modemtype." }, - "description": "Velg Insteon modemtype.", - "title": "" + "description": "Velg Insteon modemtype." } } }, @@ -61,8 +60,7 @@ "cat": "Enhetskategori (dvs. 0x10)", "subcat": "Underkategori for enhet (dvs. 0x0a)" }, - "description": "Legg til en enhetsoverstyring.", - "title": "" + "description": "Legg til en enhetsoverstyring." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Dimmer trinn (kun for lette enheter, standard 22)", "unitcode": "Enhetskode (1 - 16)" }, - "description": "Endre insteon hub-passordet.", - "title": "" + "description": "Endre insteon hub-passordet." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Brukernavn" }, - "description": "Endre Insteon Hub-tilkoblingsinformasjonen. Du m\u00e5 starte Home Assistant p\u00e5 nytt n\u00e5r du har gjort denne endringen. Dette endrer ikke konfigurasjonen av selve huben. For \u00e5 endre konfigurasjonen i huben bruker du hub-appen.", - "title": "" + "description": "Endre Insteon Hub-tilkoblingsinformasjonen. Du m\u00e5 starte Home Assistant p\u00e5 nytt n\u00e5r du har gjort denne endringen. Dette endrer ikke konfigurasjonen av selve huben. For \u00e5 endre konfigurasjonen i huben bruker du hub-appen." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Endre hub-konfigurasjonen.", "remove_override": "Fjern en enhet overstyring.", "remove_x10": "Fjern en X10-enhet." - }, - "description": "Velg et alternativ for \u00e5 konfigurere.", - "title": "" + } }, "remove_override": { "data": { "address": "Velg en enhetsadresse du vil fjerne" }, - "description": "Fjerne en enhetsoverstyring", - "title": "" + "description": "Fjerne en enhetsoverstyring" }, "remove_x10": { "data": { "address": "Velg en enhetsadresse du vil fjerne" }, - "description": "Fjern en X10-enhet", - "title": "" + "description": "Fjern en X10-enhet" } } } diff --git a/homeassistant/components/insteon/translations/pl.json b/homeassistant/components/insteon/translations/pl.json index 6d3b6c4391d..9cadcfa955c 100644 --- a/homeassistant/components/insteon/translations/pl.json +++ b/homeassistant/components/insteon/translations/pl.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Typ modemu." }, - "description": "Wybierz typ modemu Insteon.", - "title": "Insteon" + "description": "Wybierz typ modemu Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Kategoria urz\u0105dzenia (np. 0x10)", "subcat": "Podkategoria urz\u0105dzenia (np. 0x0a)" }, - "description": "Dodawanie nadpisanie urz\u0105dzenia.", - "title": "Insteon" + "description": "Dodawanie nadpisanie urz\u0105dzenia." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Kroki \u015bciemniacza (tylko dla urz\u0105dze\u0144 o\u015bwietleniowych, domy\u015blnie 22)", "unitcode": "Unitcode (1\u201316)" }, - "description": "Zmie\u0144 has\u0142o Huba Insteon.", - "title": "Insteon" + "description": "Zmie\u0144 has\u0142o Huba Insteon." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Nazwa u\u017cytkownika" }, - "description": "Zmie\u0144 informacje o po\u0142\u0105czeniu Huba Insteon. Po wprowadzeniu tej zmiany musisz ponownie uruchomi\u0107 Home Assistanta. Nie zmienia to konfiguracji samego Huba. Aby zmieni\u0107 jego konfiguracj\u0119, u\u017cyj aplikacji Hub.", - "title": "Insteon" + "description": "Zmie\u0144 informacje o po\u0142\u0105czeniu Huba Insteon. Po wprowadzeniu tej zmiany musisz ponownie uruchomi\u0107 Home Assistanta. Nie zmienia to konfiguracji samego Huba. Aby zmieni\u0107 jego konfiguracj\u0119, u\u017cyj aplikacji Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Zmie\u0144 konfiguracj\u0119 Huba.", "remove_override": "Usu\u0144 nadpisanie urz\u0105dzenia.", "remove_x10": "Usu\u0144 urz\u0105dzenie X10." - }, - "description": "Wybierz opcj\u0119 do skonfigurowania.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Wybierz adres urz\u0105dzenia do usuni\u0119cia" }, - "description": "Usu\u0144 nadpisanie urz\u0105dzenia", - "title": "Insteon" + "description": "Usu\u0144 nadpisanie urz\u0105dzenia" }, "remove_x10": { "data": { "address": "Wybierz adres urz\u0105dzenia do usuni\u0119cia" }, - "description": "Usu\u0144 urz\u0105dzenie X10", - "title": "Insteon" + "description": "Usu\u0144 urz\u0105dzenie X10" } } } diff --git a/homeassistant/components/insteon/translations/pt-BR.json b/homeassistant/components/insteon/translations/pt-BR.json index b5487d9260a..e03bd8a55c0 100644 --- a/homeassistant/components/insteon/translations/pt-BR.json +++ b/homeassistant/components/insteon/translations/pt-BR.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Tipo de modem." }, - "description": "Selecione o tipo de modem Insteon.", - "title": "Insteon" + "description": "Selecione o tipo de modem Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "Subcategoria de dispositivo (ou seja, 0x10)", "subcat": "Subcategoria de dispositivo (ou seja, 0x0a)" }, - "description": "Escolha um dispositivo para sobrescrever", - "title": "Insteon" + "description": "Escolha um dispositivo para sobrescrever" }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Etapas de dimmer (apenas para dispositivos de lux, padr\u00e3o 22)", "unitcode": "C\u00f3digo de unidade (1 - 16)" }, - "description": "Altere a senha do Insteon Hub.", - "title": "Insteon" + "description": "Altere a senha do Insteon Hub." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Porta", "username": "Usu\u00e1rio" }, - "description": "Altere as informa\u00e7\u00f5es de conex\u00e3o do Hub Insteon. Voc\u00ea deve reiniciar o Home Assistant depois de fazer essa altera\u00e7\u00e3o. Isso n\u00e3o altera a configura\u00e7\u00e3o do pr\u00f3prio Hub. Para alterar a configura\u00e7\u00e3o no Hub, use o aplicativo Hub.", - "title": "Insteon" + "description": "Altere as informa\u00e7\u00f5es de conex\u00e3o do Hub Insteon. Voc\u00ea deve reiniciar o Home Assistant depois de fazer essa altera\u00e7\u00e3o. Isso n\u00e3o altera a configura\u00e7\u00e3o do pr\u00f3prio Hub. Para alterar a configura\u00e7\u00e3o no Hub, use o aplicativo Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Altere a configura\u00e7\u00e3o do Hub.", "remove_override": "Remova uma substitui\u00e7\u00e3o de dispositivo.", "remove_x10": "Remova um dispositivo X10." - }, - "description": "Selecione uma op\u00e7\u00e3o para configurar.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Selecione um endere\u00e7o de dispositivo para remover" }, - "description": "Remover uma substitui\u00e7\u00e3o de dispositivo", - "title": "Insteon" + "description": "Remover uma substitui\u00e7\u00e3o de dispositivo" }, "remove_x10": { "data": { "address": "Selecione um endere\u00e7o de dispositivo para remover" }, - "description": "Remover um dispositivo X10", - "title": "Insteon" + "description": "Remover um dispositivo X10" } } } diff --git a/homeassistant/components/insteon/translations/pt.json b/homeassistant/components/insteon/translations/pt.json index e25fe7db5bd..1a281774ffe 100644 --- a/homeassistant/components/insteon/translations/pt.json +++ b/homeassistant/components/insteon/translations/pt.json @@ -35,8 +35,7 @@ "data": { "modem_type": "Tipo de modem." }, - "description": "Selecione o tipo de modem Insteon.", - "title": "Insteon" + "description": "Selecione o tipo de modem Insteon." } } }, @@ -62,8 +61,7 @@ "init": { "data": { "remove_x10": "Remova um dispositivo X10." - }, - "description": "Selecione uma op\u00e7\u00e3o para configurar." + } } } } diff --git a/homeassistant/components/insteon/translations/ru.json b/homeassistant/components/insteon/translations/ru.json index 3151fa35252..a95d3a51afc 100644 --- a/homeassistant/components/insteon/translations/ru.json +++ b/homeassistant/components/insteon/translations/ru.json @@ -43,8 +43,7 @@ "data": { "modem_type": "\u0422\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0430" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0430 Insteon.", - "title": "Insteon" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0430 Insteon." } } }, @@ -61,8 +60,7 @@ "cat": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: 0x10)", "subcat": "\u041f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: 0x0a)" }, - "description": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "title": "Insteon" + "description": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "\u0428\u0430\u0433 \u0434\u0438\u043c\u043c\u0435\u0440\u0430 (\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043e\u0441\u0432\u0435\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u0431\u043e\u0440\u043e\u0432, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 22)", "unitcode": "\u042e\u043d\u0438\u0442\u043a\u043e\u0434 (1 - 16)" }, - "description": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u043a Insteon Hub", - "title": "Insteon" + "description": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c \u043a Insteon Hub" }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "\u041f\u043e\u0440\u0442", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 Insteon Hub. \u041f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Home Assistant. \u042d\u0442\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0441\u0430\u043c\u043e\u0433\u043e \u0445\u0430\u0431\u0430. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0445\u0430\u0431\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Hub.", - "title": "Insteon" + "description": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 Insteon Hub. \u041f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Home Assistant. \u042d\u0442\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0441\u0430\u043c\u043e\u0433\u043e \u0445\u0430\u0431\u0430. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0445\u0430\u0431\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Hub." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0445\u0430\u0431\u0430", "remove_override": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "remove_x10": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e X10" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u043f\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f" }, - "description": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "title": "Insteon" + "description": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" }, "remove_x10": { "data": { "address": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f" }, - "description": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 X10", - "title": "Insteon" + "description": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 X10" } } } diff --git a/homeassistant/components/insteon/translations/tr.json b/homeassistant/components/insteon/translations/tr.json index 2f74d6a98ae..2bf143699f4 100644 --- a/homeassistant/components/insteon/translations/tr.json +++ b/homeassistant/components/insteon/translations/tr.json @@ -43,8 +43,7 @@ "data": { "modem_type": "Modem t\u00fcr\u00fc." }, - "description": "Insteon modem tipini se\u00e7in.", - "title": "Insteon" + "description": "Insteon modem tipini se\u00e7in." } } }, @@ -61,8 +60,7 @@ "cat": "Cihaz alt kategorisi (\u00f6rnek: 0x10)", "subcat": "Cihaz alt kategorisi (yani 0x0a)" }, - "description": "Bir cihaz\u0131 ge\u00e7ersiz k\u0131lma ekleyin.", - "title": "Insteon" + "description": "Bir cihaz\u0131 ge\u00e7ersiz k\u0131lma ekleyin." }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "Dimmer ad\u0131mlar\u0131 (yaln\u0131zca hafif cihazlar i\u00e7in varsay\u0131lan 22)", "unitcode": "Birim kodu (1 - 16)" }, - "description": "Insteon Hub parolas\u0131n\u0131 de\u011fi\u015ftirin.", - "title": "Insteon" + "description": "Insteon Hub parolas\u0131n\u0131 de\u011fi\u015ftirin." }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "Port", "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "Insteon Hub ba\u011flant\u0131 bilgilerini de\u011fi\u015ftirin. Bu de\u011fi\u015fikli\u011fi yapt\u0131ktan sonra Home Assistant'\u0131 yeniden ba\u015flatman\u0131z gerekir. Bu, Hub'\u0131n yap\u0131land\u0131rmas\u0131n\u0131 de\u011fi\u015ftirmez. Hub'daki yap\u0131land\u0131rmay\u0131 de\u011fi\u015ftirmek i\u00e7in Hub uygulamas\u0131n\u0131 kullan\u0131n.", - "title": "Insteon" + "description": "Insteon Hub ba\u011flant\u0131 bilgilerini de\u011fi\u015ftirin. Bu de\u011fi\u015fikli\u011fi yapt\u0131ktan sonra Home Assistant'\u0131 yeniden ba\u015flatman\u0131z gerekir. Bu, Hub'\u0131n yap\u0131land\u0131rmas\u0131n\u0131 de\u011fi\u015ftirmez. Hub'daki yap\u0131land\u0131rmay\u0131 de\u011fi\u015ftirmek i\u00e7in Hub uygulamas\u0131n\u0131 kullan\u0131n." }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "Hub yap\u0131land\u0131rmas\u0131n\u0131 de\u011fi\u015ftirin.", "remove_override": "Bir cihaz\u0131 ge\u00e7ersiz k\u0131lma i\u015flemini kald\u0131r\u0131n.", "remove_x10": "Bir X10 cihaz\u0131n\u0131 \u00e7\u0131kar\u0131n." - }, - "description": "Yap\u0131land\u0131rmak i\u00e7in bir se\u00e7enek se\u00e7in.", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "Kald\u0131r\u0131lacak bir cihaz adresi se\u00e7in" }, - "description": "Bir cihaz\u0131 ge\u00e7ersiz k\u0131lmay\u0131 kald\u0131rma", - "title": "Insteon" + "description": "Bir cihaz\u0131 ge\u00e7ersiz k\u0131lmay\u0131 kald\u0131rma" }, "remove_x10": { "data": { "address": "Kald\u0131r\u0131lacak bir cihaz adresi se\u00e7in" }, - "description": "Bir X10 cihaz\u0131n\u0131 kald\u0131r\u0131n", - "title": "Insteon" + "description": "Bir X10 cihaz\u0131n\u0131 kald\u0131r\u0131n" } } } diff --git a/homeassistant/components/insteon/translations/uk.json b/homeassistant/components/insteon/translations/uk.json index 747e3a30176..3d450d8d973 100644 --- a/homeassistant/components/insteon/translations/uk.json +++ b/homeassistant/components/insteon/translations/uk.json @@ -38,8 +38,7 @@ "data": { "modem_type": "\u0422\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0443" }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0443 Insteon.", - "title": "Insteon" + "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0438\u043f \u043c\u043e\u0434\u0435\u043c\u0443 Insteon." } } }, @@ -56,8 +55,7 @@ "cat": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0456\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e (\u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434: 0x10)", "subcat": "\u041f\u0456\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0456\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e (\u043d\u0430\u043f\u0440\u0438\u043a\u043b\u0430\u0434, 0x0a)" }, - "description": "\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e", - "title": "Insteon" + "description": "\u041f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" }, "add_x10": { "data": { @@ -66,8 +64,7 @@ "steps": "\u041a\u0440\u043e\u043a \u0434\u0456\u043c\u043c\u0435\u0440\u0430 (\u0442\u0456\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u043e\u0441\u0432\u0456\u0442\u043b\u044e\u0432\u0430\u043b\u044c\u043d\u0438\u0445 \u043f\u0440\u0438\u043b\u0430\u0434\u0456\u0432, \u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c 22)", "unitcode": "\u042e\u043d\u0456\u0442\u043a\u043e\u0434 (1 - 16)" }, - "description": "\u0417\u043c\u0456\u043d\u0456\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043e Insteon Hub", - "title": "Insteon" + "description": "\u0417\u043c\u0456\u043d\u0456\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0434\u043e Insteon Hub" }, "change_hub_config": { "data": { @@ -76,8 +73,7 @@ "port": "\u041f\u043e\u0440\u0442", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" }, - "description": "\u0417\u043c\u0456\u043d\u0456\u0442\u044c \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u044e \u043f\u0440\u043e \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f Insteon Hub. \u041f\u0456\u0441\u043b\u044f \u0432\u043d\u0435\u0441\u0435\u043d\u043d\u044f \u0446\u0438\u0445 \u0437\u043c\u0456\u043d \u043d\u0435\u043e\u0431\u0445\u0456\u0434\u043d\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0438 Home Assistant. \u0426\u0435 \u043d\u0435 \u0437\u043c\u0456\u043d\u044e\u0454 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044e \u0441\u0430\u043c\u043e\u0433\u043e \u0445\u0430\u0431\u0430. \u0429\u043e\u0431 \u0437\u043c\u0456\u043d\u0438\u0442\u0438 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044e \u0445\u0430\u0431\u0430, \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u043e\u043a Hub.", - "title": "Insteon" + "description": "\u0417\u043c\u0456\u043d\u0456\u0442\u044c \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u044e \u043f\u0440\u043e \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f Insteon Hub. \u041f\u0456\u0441\u043b\u044f \u0432\u043d\u0435\u0441\u0435\u043d\u043d\u044f \u0446\u0438\u0445 \u0437\u043c\u0456\u043d \u043d\u0435\u043e\u0431\u0445\u0456\u0434\u043d\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0438 Home Assistant. \u0426\u0435 \u043d\u0435 \u0437\u043c\u0456\u043d\u044e\u0454 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044e \u0441\u0430\u043c\u043e\u0433\u043e \u0445\u0430\u0431\u0430. \u0429\u043e\u0431 \u0437\u043c\u0456\u043d\u0438\u0442\u0438 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044e \u0445\u0430\u0431\u0430, \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u043e\u043a Hub." }, "init": { "data": { @@ -86,23 +82,19 @@ "change_hub_config": "\u0417\u043c\u0456\u043d\u0438\u0442\u0438 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044e \u0445\u0430\u0431\u0430", "remove_override": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e", "remove_x10": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 X10" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u043f\u0446\u0456\u044e \u0434\u043b\u044f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0430\u0434\u0440\u0435\u0441\u0443 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e, \u044f\u043a\u0438\u0439 \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0432\u0438\u0434\u0430\u043b\u0438\u0442\u0438" }, - "description": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e", - "title": "Insteon" + "description": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u043f\u0435\u0440\u0435\u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" }, "remove_x10": { "data": { "address": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0430\u0434\u0440\u0435\u0441\u0443 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e, \u044f\u043a\u0438\u0439 \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0432\u0438\u0434\u0430\u043b\u0438\u0442\u0438" }, - "description": "\u0412\u0438\u0434\u0430\u043b\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e X10", - "title": "Insteon" + "description": "\u0412\u0438\u0434\u0430\u043b\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e X10" } } } diff --git a/homeassistant/components/insteon/translations/zh-Hant.json b/homeassistant/components/insteon/translations/zh-Hant.json index c55a1ea0a5c..608b6929e52 100644 --- a/homeassistant/components/insteon/translations/zh-Hant.json +++ b/homeassistant/components/insteon/translations/zh-Hant.json @@ -43,8 +43,7 @@ "data": { "modem_type": "\u6578\u64da\u6a5f\u985e\u5225\u3002" }, - "description": "\u9078\u64c7 Insteon \u6578\u64da\u6a5f\u985e\u5225\u3002", - "title": "Insteon" + "description": "\u9078\u64c7 Insteon \u6578\u64da\u6a5f\u985e\u5225\u3002" } } }, @@ -61,8 +60,7 @@ "cat": "\u88dd\u7f6e\u5b50\u985e\u5225\uff08\u4f8b\u5982 0x10\uff09", "subcat": "\u88dd\u7f6e\u5b50\u985e\u5225\uff08\u4f8b\u5982 0x0a\uff09" }, - "description": "\u65b0\u589e\u88dd\u7f6e\u8986\u5beb\u3002", - "title": "Insteon" + "description": "\u65b0\u589e\u88dd\u7f6e\u8986\u5beb\u3002" }, "add_x10": { "data": { @@ -71,8 +69,7 @@ "steps": "\u8abf\u5149\u968e\u6bb5\uff08\u50c5\u9069\u7528\u7167\u660e\u88dd\u7f6e\u3001\u9810\u8a2d\u503c\u70ba 22\uff09", "unitcode": "Unitcode (1 - 16)" }, - "description": "\u8b8a\u66f4 Insteon Hub \u5bc6\u78bc\u3002", - "title": "Insteon" + "description": "\u8b8a\u66f4 Insteon Hub \u5bc6\u78bc\u3002" }, "change_hub_config": { "data": { @@ -81,8 +78,7 @@ "port": "\u901a\u8a0a\u57e0", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "\u8b8a\u66f4 Insteon Hub \u9023\u7dda\u8cc7\u8a0a\u3002\u65bc\u8b8a\u66f4\u4e4b\u5f8c\u3001\u5fc5\u9808\u91cd\u555f Home Assistant\u3002\u6b64\u4e9b\u8a2d\u5b9a\u4e0d\u6703\u8b8a\u66f4 Hub \u88dd\u7f6e\u672c\u8eab\u7684\u8a2d\u5b9a\uff0c\u5982\u6b32\u8b8a\u66f4 Hub \u8a2d\u5b9a\u3001\u5247\u8acb\u4f7f\u7528 Hub app\u3002", - "title": "Insteon" + "description": "\u8b8a\u66f4 Insteon Hub \u9023\u7dda\u8cc7\u8a0a\u3002\u65bc\u8b8a\u66f4\u4e4b\u5f8c\u3001\u5fc5\u9808\u91cd\u555f Home Assistant\u3002\u6b64\u4e9b\u8a2d\u5b9a\u4e0d\u6703\u8b8a\u66f4 Hub \u88dd\u7f6e\u672c\u8eab\u7684\u8a2d\u5b9a\uff0c\u5982\u6b32\u8b8a\u66f4 Hub \u8a2d\u5b9a\u3001\u5247\u8acb\u4f7f\u7528 Hub app\u3002" }, "init": { "data": { @@ -91,23 +87,19 @@ "change_hub_config": "\u8b8a\u66f4 Hub \u8a2d\u5b9a\u3002", "remove_override": "\u79fb\u9664\u88dd\u7f6e\u8986\u5beb", "remove_x10": "\u79fb\u9664 X10 \u88dd\u7f6e\u3002" - }, - "description": "\u9078\u64c7\u9078\u9805\u4ee5\u8a2d\u5b9a", - "title": "Insteon" + } }, "remove_override": { "data": { "address": "\u9078\u64c7\u88dd\u7f6e\u4f4d\u5740\u4ee5\u79fb\u9664" }, - "description": "\u79fb\u9664\u88dd\u7f6e\u8986\u5beb", - "title": "Insteon" + "description": "\u79fb\u9664\u88dd\u7f6e\u8986\u5beb" }, "remove_x10": { "data": { "address": "\u9078\u64c7\u88dd\u7f6e\u4f4d\u5740\u4ee5\u79fb\u9664" }, - "description": "\u79fb\u9664 X10 \u88dd\u7f6e", - "title": "Insteon" + "description": "\u79fb\u9664 X10 \u88dd\u7f6e" } } } diff --git a/homeassistant/components/integration/translations/ca.json b/homeassistant/components/integration/translations/ca.json index bbe5e6e31b4..ad9553ff346 100644 --- a/homeassistant/components/integration/translations/ca.json +++ b/homeassistant/components/integration/translations/ca.json @@ -29,12 +29,6 @@ "data_description": { "round": "Controla el nombre de d\u00edgits decimals a la sortida." } - }, - "options": { - "data": { - "round": "Precisi\u00f3" - }, - "description": "La precisi\u00f3 controla el nombre de d\u00edgits decimals a la sortida." } } }, diff --git a/homeassistant/components/integration/translations/cs.json b/homeassistant/components/integration/translations/cs.json index 3e7fdfca729..64e92edeb1c 100644 --- a/homeassistant/components/integration/translations/cs.json +++ b/homeassistant/components/integration/translations/cs.json @@ -28,12 +28,6 @@ "data_description": { "round": "Ur\u010duje po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu." } - }, - "options": { - "data": { - "round": "P\u0159esnost" - }, - "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst ve v\u00fdstupu." } } }, diff --git a/homeassistant/components/integration/translations/de.json b/homeassistant/components/integration/translations/de.json index 3013fa9d039..af26ec446a2 100644 --- a/homeassistant/components/integration/translations/de.json +++ b/homeassistant/components/integration/translations/de.json @@ -29,12 +29,6 @@ "data_description": { "round": "Steuert die Anzahl der Dezimalstellen in der Ausgabe." } - }, - "options": { - "data": { - "round": "Genauigkeit" - }, - "description": "Die Genauigkeit steuert die Anzahl der Dezimalstellen in der Ausgabe." } } }, diff --git a/homeassistant/components/integration/translations/el.json b/homeassistant/components/integration/translations/el.json index e366137fce1..6893190d9bf 100644 --- a/homeassistant/components/integration/translations/el.json +++ b/homeassistant/components/integration/translations/el.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf." } - }, - "options": { - "data": { - "round": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1" - }, - "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf." } } }, diff --git a/homeassistant/components/integration/translations/en.json b/homeassistant/components/integration/translations/en.json index 3174eab3f69..1ee047b447f 100644 --- a/homeassistant/components/integration/translations/en.json +++ b/homeassistant/components/integration/translations/en.json @@ -29,12 +29,6 @@ "data_description": { "round": "Controls the number of decimal digits in the output." } - }, - "options": { - "data": { - "round": "Precision" - }, - "description": "Precision controls the number of decimal digits in the output." } } }, diff --git a/homeassistant/components/integration/translations/es.json b/homeassistant/components/integration/translations/es.json index 8c903976d1d..8034e4746fe 100644 --- a/homeassistant/components/integration/translations/es.json +++ b/homeassistant/components/integration/translations/es.json @@ -26,9 +26,6 @@ "data_description": { "round": "Controla el n\u00famero de d\u00edgitos decimales en la salida." } - }, - "options": { - "description": "La precisi\u00f3n controla el n\u00famero de d\u00edgitos decimales en la salida." } } }, diff --git a/homeassistant/components/integration/translations/et.json b/homeassistant/components/integration/translations/et.json index 31901bbb6f6..4b0143d0662 100644 --- a/homeassistant/components/integration/translations/et.json +++ b/homeassistant/components/integration/translations/et.json @@ -29,12 +29,6 @@ "data_description": { "round": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis." } - }, - "options": { - "data": { - "round": "T\u00e4psus" - }, - "description": "T\u00e4psus reguleerib k\u00fcmnendkohtade arvu v\u00e4ljundis." } } }, diff --git a/homeassistant/components/integration/translations/fr.json b/homeassistant/components/integration/translations/fr.json index 33b5ab86e7f..cbc0e89175b 100644 --- a/homeassistant/components/integration/translations/fr.json +++ b/homeassistant/components/integration/translations/fr.json @@ -29,12 +29,6 @@ "data_description": { "round": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie." } - }, - "options": { - "data": { - "round": "Pr\u00e9cision" - }, - "description": "La pr\u00e9cision contr\u00f4le le nombre de chiffres d\u00e9cimaux dans la sortie." } } }, diff --git a/homeassistant/components/integration/translations/he.json b/homeassistant/components/integration/translations/he.json index 4061da5f233..219c75605bb 100644 --- a/homeassistant/components/integration/translations/he.json +++ b/homeassistant/components/integration/translations/he.json @@ -27,12 +27,6 @@ "data_description": { "round": "\u05e9\u05dc\u05d9\u05d8\u05d4 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8." } - }, - "options": { - "data": { - "round": "\u05d3\u05d9\u05d5\u05e7" - }, - "description": "\u05d3\u05d9\u05d5\u05e7 \u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8." } } }, diff --git a/homeassistant/components/integration/translations/hu.json b/homeassistant/components/integration/translations/hu.json index c892e858b3f..763054d4469 100644 --- a/homeassistant/components/integration/translations/hu.json +++ b/homeassistant/components/integration/translations/hu.json @@ -29,12 +29,6 @@ "data_description": { "round": "Az eredm\u00e9ny tizedesjegyeinek sz\u00e1ma." } - }, - "options": { - "data": { - "round": "Pontoss\u00e1g" - }, - "description": "A pontoss\u00e1g hat\u00e1rozza meg az eredm\u00e9ny tizedesjegyeinek sz\u00e1m\u00e1t." } } }, diff --git a/homeassistant/components/integration/translations/id.json b/homeassistant/components/integration/translations/id.json index d585a4409e9..8327d6dc555 100644 --- a/homeassistant/components/integration/translations/id.json +++ b/homeassistant/components/integration/translations/id.json @@ -29,12 +29,6 @@ "data_description": { "round": "Mengontrol jumlah digit desimal dalam output." } - }, - "options": { - "data": { - "round": "Presisi" - }, - "description": "Presisi mengontrol jumlah digit desimal pada output." } } }, diff --git a/homeassistant/components/integration/translations/it.json b/homeassistant/components/integration/translations/it.json index d07b702962c..92e4077aa90 100644 --- a/homeassistant/components/integration/translations/it.json +++ b/homeassistant/components/integration/translations/it.json @@ -29,12 +29,6 @@ "data_description": { "round": "Controlla il numero di cifre decimali nell'output." } - }, - "options": { - "data": { - "round": "Precisione" - }, - "description": "Precisione controlla il numero di cifre decimali nell'uscita." } } }, diff --git a/homeassistant/components/integration/translations/ja.json b/homeassistant/components/integration/translations/ja.json index 5fac6ba692b..4f35ba53a4a 100644 --- a/homeassistant/components/integration/translations/ja.json +++ b/homeassistant/components/integration/translations/ja.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u51fa\u529b\u5024\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3002" } - }, - "options": { - "data": { - "round": "\u7cbe\u5ea6" - }, - "description": "\u7cbe\u5ea6\u306f\u3001\u51fa\u529b\u306e\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/integration/translations/ko.json b/homeassistant/components/integration/translations/ko.json new file mode 100644 index 00000000000..d217725b835 --- /dev/null +++ b/homeassistant/components/integration/translations/ko.json @@ -0,0 +1,36 @@ +{ + "config": { + "step": { + "user": { + "data": { + "method": "\uc801\ubd84 \ubc29\ubc95", + "name": "\uc774\ub984", + "round": "\uc18c\uc218\uc810", + "source": "\uc785\ub825 \uc13c\uc11c", + "unit_prefix": "\ubbf8\ud130\ubc95", + "unit_time": "\uc2dc\uac04 \ub2e8\uc704" + }, + "data_description": { + "round": "\uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4.", + "unit_prefix": "\uc120\ud0dd\ud55c \ubbf8\ud130\ubc95\uc73c\ub85c \ud45c\uc2dc\ub429\ub2c8\ub2e4.", + "unit_time": "\uc120\ud0dd\ud55c \uc2dc\uac04 \ub2e8\uc704\ub85c \ud45c\uc2dc\ub429\ub2c8\ub2e4." + }, + "description": "\ub9ac\ub9cc \ud569\uc744 \uc0ac\uc6a9\ud574\uc11c \uc801\ubd84\uac12\uc744 \uad6c\ud558\ub294 \uc13c\uc11c\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4.", + "title": "\ub9ac\ub9cc \ud569 \uc801\ubd84 \uc13c\uc11c \ucd94\uac00" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "round": "\uc18c\uc218\uc810" + }, + "data_description": { + "round": "\uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \ubcc0\uacbd\ud569\ub2c8\ub2e4." + } + } + } + }, + "title": "\uc801\ubd84 - \ub9ac\ub9cc \ud569 \uc801\ubd84 \uc13c\uc11c" +} \ No newline at end of file diff --git a/homeassistant/components/integration/translations/nl.json b/homeassistant/components/integration/translations/nl.json index 8753ee30ea7..e3e0259da71 100644 --- a/homeassistant/components/integration/translations/nl.json +++ b/homeassistant/components/integration/translations/nl.json @@ -29,12 +29,6 @@ "data_description": { "round": "Regelt het aantal decimale cijfers in de uitvoer." } - }, - "options": { - "data": { - "round": "Precisie" - }, - "description": "Precisie bepaalt het aantal decimale cijfers in de uitvoer." } } }, diff --git a/homeassistant/components/integration/translations/no.json b/homeassistant/components/integration/translations/no.json index aafa60b5811..9965d1c7521 100644 --- a/homeassistant/components/integration/translations/no.json +++ b/homeassistant/components/integration/translations/no.json @@ -29,12 +29,6 @@ "data_description": { "round": "Styrer antall desimaler i utdataene." } - }, - "options": { - "data": { - "round": "Presisjon" - }, - "description": "Presisjon styrer antall desimaler i utdataene." } } }, diff --git a/homeassistant/components/integration/translations/pl.json b/homeassistant/components/integration/translations/pl.json index 7971d97129c..5dfe00cd31b 100644 --- a/homeassistant/components/integration/translations/pl.json +++ b/homeassistant/components/integration/translations/pl.json @@ -29,12 +29,6 @@ "data_description": { "round": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych." } - }, - "options": { - "data": { - "round": "Precyzja" - }, - "description": "Precyzja kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych." } } }, diff --git a/homeassistant/components/integration/translations/pt-BR.json b/homeassistant/components/integration/translations/pt-BR.json index ae512b93fd4..999873290b8 100644 --- a/homeassistant/components/integration/translations/pt-BR.json +++ b/homeassistant/components/integration/translations/pt-BR.json @@ -29,12 +29,6 @@ "data_description": { "round": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda." } - }, - "options": { - "data": { - "round": "Precis\u00e3o" - }, - "description": "A precis\u00e3o controla o n\u00famero de d\u00edgitos decimais na sa\u00edda." } } }, diff --git a/homeassistant/components/integration/translations/ru.json b/homeassistant/components/integration/translations/ru.json index 67891293d7b..8e7ad96b803 100644 --- a/homeassistant/components/integration/translations/ru.json +++ b/homeassistant/components/integration/translations/ru.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." } - }, - "options": { - "data": { - "round": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435" - }, - "description": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439." } } }, diff --git a/homeassistant/components/integration/translations/sk.json b/homeassistant/components/integration/translations/sk.json index aec16fc71b8..c1372b00a8a 100644 --- a/homeassistant/components/integration/translations/sk.json +++ b/homeassistant/components/integration/translations/sk.json @@ -7,14 +7,5 @@ } } } - }, - "options": { - "step": { - "options": { - "data": { - "round": "Presnos\u0165" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/integration/translations/tr.json b/homeassistant/components/integration/translations/tr.json index de99ebd3633..2271c616150 100644 --- a/homeassistant/components/integration/translations/tr.json +++ b/homeassistant/components/integration/translations/tr.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u00c7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." } - }, - "options": { - "data": { - "round": "Hassas" - }, - "description": "Kesinlik, \u00e7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." } } }, diff --git a/homeassistant/components/integration/translations/zh-Hans.json b/homeassistant/components/integration/translations/zh-Hans.json index f7ebea98f4a..15cc310b2f4 100644 --- a/homeassistant/components/integration/translations/zh-Hans.json +++ b/homeassistant/components/integration/translations/zh-Hans.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" } - }, - "options": { - "data": { - "round": "\u7cbe\u5ea6" - }, - "description": "\u7cbe\u5ea6\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" } } }, diff --git a/homeassistant/components/integration/translations/zh-Hant.json b/homeassistant/components/integration/translations/zh-Hant.json index 2adbb3edc28..d7142365b05 100644 --- a/homeassistant/components/integration/translations/zh-Hant.json +++ b/homeassistant/components/integration/translations/zh-Hant.json @@ -29,12 +29,6 @@ "data_description": { "round": "\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002" } - }, - "options": { - "data": { - "round": "\u6e96\u78ba\u5ea6" - }, - "description": "\u7cbe\u6e96\u5ea6\u63a7\u5236\u8f38\u51fa\u4e2d\u7684\u5c0f\u6578\u4f4d\u6578\u3002" } } }, diff --git a/homeassistant/components/intellifire/translations/bg.json b/homeassistant/components/intellifire/translations/bg.json index 9926e7bb6a6..9df377170f4 100644 --- a/homeassistant/components/intellifire/translations/bg.json +++ b/homeassistant/components/intellifire/translations/bg.json @@ -7,8 +7,7 @@ "error": { "api_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u043b\u0438\u0437\u0430\u043d\u0435", "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "iftapi_connect": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u0441 iftapi.net", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + "iftapi_connect": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435\u0442\u043e \u0441 iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -32,11 +31,6 @@ "host": "\u0425\u043e\u0441\u0442" }, "title": "\u0418\u0437\u0431\u043e\u0440 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - } } } } diff --git a/homeassistant/components/intellifire/translations/ca.json b/homeassistant/components/intellifire/translations/ca.json index 9894ec4920a..d5991750966 100644 --- a/homeassistant/components/intellifire/translations/ca.json +++ b/homeassistant/components/intellifire/translations/ca.json @@ -8,8 +8,7 @@ "error": { "api_error": "Ha fallat l'inici de sessi\u00f3", "cannot_connect": "Ha fallat la connexi\u00f3", - "iftapi_connect": "S'ha produ\u00eft un error en connectar a iftapi.net", - "unknown": "Error inesperat" + "iftapi_connect": "S'ha produ\u00eft un error en connectar a iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "S'han descobert els dispositius IntelliFire seg\u00fcents. Selecciona el que vulguis configurar.", "title": "Selecci\u00f3 de dispositiu" - }, - "user": { - "data": { - "host": "Amfitri\u00f3" - } } } } diff --git a/homeassistant/components/intellifire/translations/cs.json b/homeassistant/components/intellifire/translations/cs.json index 48dbc509ea8..8684c426280 100644 --- a/homeassistant/components/intellifire/translations/cs.json +++ b/homeassistant/components/intellifire/translations/cs.json @@ -4,8 +4,7 @@ "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "flow_title": "{serial} ({host})", "step": { @@ -18,11 +17,6 @@ "data": { "host": "Hostitel" } - }, - "user": { - "data": { - "host": "Hostitel" - } } } } diff --git a/homeassistant/components/intellifire/translations/de.json b/homeassistant/components/intellifire/translations/de.json index f1c37a8a475..d9427853cdd 100644 --- a/homeassistant/components/intellifire/translations/de.json +++ b/homeassistant/components/intellifire/translations/de.json @@ -8,8 +8,7 @@ "error": { "api_error": "Login fehlgeschlagen", "cannot_connect": "Verbindung fehlgeschlagen", - "iftapi_connect": "Fehler beim Verbinden mit iftapi.net", - "unknown": "Unerwarteter Fehler" + "iftapi_connect": "Fehler beim Verbinden mit iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Die folgenden IntelliFire-Ger\u00e4te wurden gefunden. Bitte w\u00e4hle aus, welche du konfigurieren m\u00f6chtest.", "title": "Ger\u00e4teauswahl" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/el.json b/homeassistant/components/intellifire/translations/el.json index fa72581a4a6..ff286c04952 100644 --- a/homeassistant/components/intellifire/translations/el.json +++ b/homeassistant/components/intellifire/translations/el.json @@ -8,8 +8,7 @@ "error": { "api_error": "\u0397 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b1\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "iftapi_connect": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03bf iftapi.net", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "iftapi_connect": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03c3\u03c4\u03bf iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bf\u03b9 \u03b1\u03ba\u03cc\u03bb\u03bf\u03c5\u03b8\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 IntelliFire. \u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03bf\u03b9\u03b1 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5.", "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" - }, - "user": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - } } } } diff --git a/homeassistant/components/intellifire/translations/en.json b/homeassistant/components/intellifire/translations/en.json index f0c317efb93..83acafacd48 100644 --- a/homeassistant/components/intellifire/translations/en.json +++ b/homeassistant/components/intellifire/translations/en.json @@ -8,8 +8,7 @@ "error": { "api_error": "Login failed", "cannot_connect": "Failed to connect", - "iftapi_connect": "Error conecting to iftapi.net", - "unknown": "Unexpected error" + "iftapi_connect": "Error conecting to iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "The following IntelliFire devices were discovered. Please select which you wish to configure.", "title": "Device Selection" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/es.json b/homeassistant/components/intellifire/translations/es.json index e848376bdfb..8d19b2ba3bf 100644 --- a/homeassistant/components/intellifire/translations/es.json +++ b/homeassistant/components/intellifire/translations/es.json @@ -32,11 +32,6 @@ }, "description": "Se han descubierto los siguientes dispositivos IntelliFire. Selecciona lo que quieras configurar.", "title": "Selecci\u00f3n de dispositivo" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/et.json b/homeassistant/components/intellifire/translations/et.json index 53c87f112a7..b2caebb611a 100644 --- a/homeassistant/components/intellifire/translations/et.json +++ b/homeassistant/components/intellifire/translations/et.json @@ -8,8 +8,7 @@ "error": { "api_error": "Sisselogimine nurjus", "cannot_connect": "\u00dchendamine nurjus", - "iftapi_connect": "\u00dchendumine iftapi.net'iga nurjus", - "unknown": "Ootamatu t\u00f5rge" + "iftapi_connect": "\u00dchendumine iftapi.net'iga nurjus" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Avastati j\u00e4rgmised IntelliFire seadmed. Palun vali millist soovid seadistada.", "title": "Seadme valik" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/fr.json b/homeassistant/components/intellifire/translations/fr.json index 5c478358345..650f0cad77e 100644 --- a/homeassistant/components/intellifire/translations/fr.json +++ b/homeassistant/components/intellifire/translations/fr.json @@ -8,8 +8,7 @@ "error": { "api_error": "La connexion a \u00e9chou\u00e9", "cannot_connect": "\u00c9chec de connexion", - "iftapi_connect": "Erreur lors de la connexion \u00e0 iftapi.net", - "unknown": "Erreur inattendue" + "iftapi_connect": "Erreur lors de la connexion \u00e0 iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Les appareils IntelliFire suivants ont \u00e9t\u00e9 d\u00e9couverts. Veuillez s\u00e9lectionner celui que vous souhaitez configurer.", "title": "S\u00e9lection de l'appareil" - }, - "user": { - "data": { - "host": "H\u00f4te" - } } } } diff --git a/homeassistant/components/intellifire/translations/he.json b/homeassistant/components/intellifire/translations/he.json index e4b64392380..18cf71bf358 100644 --- a/homeassistant/components/intellifire/translations/he.json +++ b/homeassistant/components/intellifire/translations/he.json @@ -5,8 +5,7 @@ "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" }, "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { "api_config": { @@ -24,11 +23,6 @@ "data": { "host": "\u05de\u05d0\u05e8\u05d7" } - }, - "user": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7" - } } } } diff --git a/homeassistant/components/intellifire/translations/hu.json b/homeassistant/components/intellifire/translations/hu.json index f5d11abe4c0..2f680f2e776 100644 --- a/homeassistant/components/intellifire/translations/hu.json +++ b/homeassistant/components/intellifire/translations/hu.json @@ -8,8 +8,7 @@ "error": { "api_error": "A bejelentkez\u00e9s sikertelen", "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "iftapi_connect": "Hiba az iftapi.net-hez val\u00f3 csatlakoz\u00e1sban", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + "iftapi_connect": "Hiba az iftapi.net-hez val\u00f3 csatlakoz\u00e1sban" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "A k\u00f6vetkez\u0151 IntelliFire eszk\u00f6z\u00f6k \u00e9szlelve. K\u00e9rj\u00fck, v\u00e1lassza ki, melyiket szeretn\u00e9 konfigur\u00e1lni.", "title": "Eszk\u00f6z v\u00e1laszt\u00e1sa" - }, - "user": { - "data": { - "host": "C\u00edm" - } } } } diff --git a/homeassistant/components/intellifire/translations/id.json b/homeassistant/components/intellifire/translations/id.json index 07ec946b6ba..6a38f501811 100644 --- a/homeassistant/components/intellifire/translations/id.json +++ b/homeassistant/components/intellifire/translations/id.json @@ -8,8 +8,7 @@ "error": { "api_error": "Gagal masuk", "cannot_connect": "Gagal terhubung", - "iftapi_connect": "Kesalahan saat menyambung ke iftapi.net", - "unknown": "Kesalahan yang tidak diharapkan" + "iftapi_connect": "Kesalahan saat menyambung ke iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Perangkat IntelliFire berikut ditemukan. Pilih yang ingin dikonfigurasikan.", "title": "Pemilihan Perangkat" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/it.json b/homeassistant/components/intellifire/translations/it.json index 8689840c82c..a1282698c88 100644 --- a/homeassistant/components/intellifire/translations/it.json +++ b/homeassistant/components/intellifire/translations/it.json @@ -8,8 +8,7 @@ "error": { "api_error": "Accesso non riuscito", "cannot_connect": "Impossibile connettersi", - "iftapi_connect": "Errore durante la connessione a iftapi.net", - "unknown": "Errore imprevisto" + "iftapi_connect": "Errore durante la connessione a iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Sono stati rilevati i seguenti dispositivi IntelliFire. Seleziona quello che desideri configurare.", "title": "Selezione del dispositivo" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/ja.json b/homeassistant/components/intellifire/translations/ja.json index c623f0f59cf..6c74e4743ed 100644 --- a/homeassistant/components/intellifire/translations/ja.json +++ b/homeassistant/components/intellifire/translations/ja.json @@ -8,8 +8,7 @@ "error": { "api_error": "\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "iftapi_connect": "iftapi.net\u3078\u306e\u63a5\u7d9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + "iftapi_connect": "iftapi.net\u3078\u306e\u63a5\u7d9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "\u4ee5\u4e0b\u306eIntelliFire\u30c7\u30d0\u30a4\u30b9\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f\u3002\u8a2d\u5b9a\u3057\u305f\u3044\u3082\u306e\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "\u30c7\u30d0\u30a4\u30b9\u306e\u9078\u629e" - }, - "user": { - "data": { - "host": "\u30db\u30b9\u30c8" - } } } } diff --git a/homeassistant/components/intellifire/translations/ko.json b/homeassistant/components/intellifire/translations/ko.json new file mode 100644 index 00000000000..fde189b9541 --- /dev/null +++ b/homeassistant/components/intellifire/translations/ko.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "api_error": "\ub85c\uadf8\uc778 \uc2e4\ud328", + "iftapi_connect": "iftapi.net\uc5d0 \uc5f0\uacb0\ud558\ub294 \ub3d9\uc548 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4." + }, + "step": { + "api_config": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc774\uba54\uc77c" + } + }, + "pick_device": { + "description": "IntelliFire \uc7a5\uce58\uac00 \uac80\uc0c9\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uad6c\uc131\ud560 \ud56d\ubaa9\uc744 \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624.", + "title": "\uae30\uae30 \uc120\ud0dd" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/nl.json b/homeassistant/components/intellifire/translations/nl.json index a4f2b1c304a..7356c4a0d50 100644 --- a/homeassistant/components/intellifire/translations/nl.json +++ b/homeassistant/components/intellifire/translations/nl.json @@ -8,8 +8,7 @@ "error": { "api_error": "Inloggen mislukt", "cannot_connect": "Kan geen verbinding maken", - "iftapi_connect": "Fout bij het verbinden met iftapi.net", - "unknown": "Onverwachte fout" + "iftapi_connect": "Fout bij het verbinden met iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "De volgende IntelliFire-apparaten zijn ontdekt. Selecteer welke u wilt configureren.", "title": "Apparaat selectie" - }, - "user": { - "data": { - "host": "Host" - } } } } diff --git a/homeassistant/components/intellifire/translations/no.json b/homeassistant/components/intellifire/translations/no.json index 8175a085f30..53cf7495b94 100644 --- a/homeassistant/components/intellifire/translations/no.json +++ b/homeassistant/components/intellifire/translations/no.json @@ -8,8 +8,7 @@ "error": { "api_error": "Innlogging feilet", "cannot_connect": "Tilkobling mislyktes", - "iftapi_connect": "Feil ved tilkobling til iftapi.net", - "unknown": "Uventet feil" + "iftapi_connect": "Feil ved tilkobling til iftapi.net" }, "flow_title": "{serial} ( {host} )", "step": { @@ -34,11 +33,6 @@ }, "description": "F\u00f8lgende IntelliFire-enheter ble oppdaget. Velg hvilken du \u00f8nsker \u00e5 konfigurere.", "title": "Enhetsvalg" - }, - "user": { - "data": { - "host": "Vert" - } } } } diff --git a/homeassistant/components/intellifire/translations/pl.json b/homeassistant/components/intellifire/translations/pl.json index f63be88814f..c470c77d03b 100644 --- a/homeassistant/components/intellifire/translations/pl.json +++ b/homeassistant/components/intellifire/translations/pl.json @@ -8,8 +8,7 @@ "error": { "api_error": "Logowanie nie powiod\u0142o si\u0119", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "iftapi_connect": "B\u0142\u0105d po\u0142\u0105czenia z iftapi.net", - "unknown": "Nieoczekiwany b\u0142\u0105d" + "iftapi_connect": "B\u0142\u0105d po\u0142\u0105czenia z iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Wykryto nast\u0119puj\u0105ce urz\u0105dzenia IntelliFire. Wybierz, kt\u00f3re chcesz skonfigurowa\u0107.", "title": "Wyb\u00f3r urz\u0105dzenia" - }, - "user": { - "data": { - "host": "Nazwa hosta lub adres IP" - } } } } diff --git a/homeassistant/components/intellifire/translations/pt-BR.json b/homeassistant/components/intellifire/translations/pt-BR.json index babcc22bd97..31a98a0d6d4 100644 --- a/homeassistant/components/intellifire/translations/pt-BR.json +++ b/homeassistant/components/intellifire/translations/pt-BR.json @@ -8,8 +8,7 @@ "error": { "api_error": "Login falhou", "cannot_connect": "Falha ao conectar", - "iftapi_connect": "Erro ao conectar ao iftapi.net", - "unknown": "Erro inesperado" + "iftapi_connect": "Erro ao conectar ao iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "Os seguintes dispositivos IntelliFire foram descobertos. Por favor, selecione o que voc\u00ea deseja configura.", "title": "Sele\u00e7\u00e3o de dispositivo" - }, - "user": { - "data": { - "host": "Nome do host" - } } } } diff --git a/homeassistant/components/intellifire/translations/ru.json b/homeassistant/components/intellifire/translations/ru.json index 8e201924028..4471edf0811 100644 --- a/homeassistant/components/intellifire/translations/ru.json +++ b/homeassistant/components/intellifire/translations/ru.json @@ -8,8 +8,7 @@ "error": { "api_error": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "iftapi_connect": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a iftapi.net", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "iftapi_connect": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a iftapi.net" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "\u0411\u044b\u043b\u0438 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 IntelliFire. \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u043a\u0430\u043a\u043e\u0435 \u0438\u0437 \u043d\u0438\u0445 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c.", "title": "\u0412\u044b\u0431\u043e\u0440 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - } } } } diff --git a/homeassistant/components/intellifire/translations/sv.json b/homeassistant/components/intellifire/translations/sv.json index f341a6314ee..be36fec5fe3 100644 --- a/homeassistant/components/intellifire/translations/sv.json +++ b/homeassistant/components/intellifire/translations/sv.json @@ -4,15 +4,7 @@ "already_configured": "Enheten \u00e4r redan konfigurerad" }, "error": { - "cannot_connect": "Det gick inte att ansluta.", - "unknown": "Ov\u00e4ntat fel" - }, - "step": { - "user": { - "data": { - "host": "V\u00e4rd" - } - } + "cannot_connect": "Det gick inte att ansluta." } } } \ No newline at end of file diff --git a/homeassistant/components/intellifire/translations/tr.json b/homeassistant/components/intellifire/translations/tr.json index b5be24edd2f..1eba2a43e72 100644 --- a/homeassistant/components/intellifire/translations/tr.json +++ b/homeassistant/components/intellifire/translations/tr.json @@ -8,8 +8,7 @@ "error": { "api_error": "Oturum a\u00e7ma ba\u015far\u0131s\u0131z oldu", "cannot_connect": "Ba\u011flanma hatas\u0131", - "iftapi_connect": "iftapi.net'e ba\u011flan\u0131rken hata olu\u015ftu", - "unknown": "Beklenmeyen hata" + "iftapi_connect": "iftapi.net'e ba\u011flan\u0131rken hata olu\u015ftu" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "A\u015fa\u011f\u0131daki IntelliFire cihazlar\u0131 ke\u015ffedildi. L\u00fctfen yap\u0131land\u0131rmak istedi\u011finizi se\u00e7in.", "title": "Cihaz Se\u00e7imi" - }, - "user": { - "data": { - "host": "Sunucu" - } } } } diff --git a/homeassistant/components/intellifire/translations/zh-Hant.json b/homeassistant/components/intellifire/translations/zh-Hant.json index 107f9cca74c..b55b8fc55bd 100644 --- a/homeassistant/components/intellifire/translations/zh-Hant.json +++ b/homeassistant/components/intellifire/translations/zh-Hant.json @@ -8,8 +8,7 @@ "error": { "api_error": "\u767b\u5165\u5931\u6557", "cannot_connect": "\u9023\u7dda\u5931\u6557", - "iftapi_connect": "\u9023\u7dda\u81f3 iftapi.net \u932f\u8aa4", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + "iftapi_connect": "\u9023\u7dda\u81f3 iftapi.net \u932f\u8aa4" }, "flow_title": "{serial} ({host})", "step": { @@ -34,11 +33,6 @@ }, "description": "\u641c\u5c0b\u5230\u4ee5\u4e0b IntelliFire \u88dd\u7f6e\uff0c\u8acb\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e\u3002", "title": "\u88dd\u7f6e\u9078\u64c7" - }, - "user": { - "data": { - "host": "\u4e3b\u6a5f\u7aef" - } } } } diff --git a/homeassistant/components/iqvia/translations/bg.json b/homeassistant/components/iqvia/translations/bg.json index e59b361d2e0..3826c82abf7 100644 --- a/homeassistant/components/iqvia/translations/bg.json +++ b/homeassistant/components/iqvia/translations/bg.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u041f\u043e\u0449\u0435\u043d\u0441\u043a\u0438 \u043a\u043e\u0434" }, - "description": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u044f \u0430\u043c\u0435\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u0438 \u0438\u043b\u0438 \u043a\u0430\u043d\u0430\u0434\u0441\u043a\u0438 \u043f\u043e\u0449\u0435\u043d\u0441\u043a\u0438 \u043a\u043e\u0434.", - "title": "IQVIA" + "description": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u044f \u0430\u043c\u0435\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u0438 \u0438\u043b\u0438 \u043a\u0430\u043d\u0430\u0434\u0441\u043a\u0438 \u043f\u043e\u0449\u0435\u043d\u0441\u043a\u0438 \u043a\u043e\u0434." } } } diff --git a/homeassistant/components/iqvia/translations/ca.json b/homeassistant/components/iqvia/translations/ca.json index 0e062e192cf..ddea24c4711 100644 --- a/homeassistant/components/iqvia/translations/ca.json +++ b/homeassistant/components/iqvia/translations/ca.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Codi postal" }, - "description": "Introdueix el teu codi postal d'Estats Units o Canad\u00e0.", - "title": "IQVIA" + "description": "Introdueix el teu codi postal d'Estats Units o Canad\u00e0." } } } diff --git a/homeassistant/components/iqvia/translations/cs.json b/homeassistant/components/iqvia/translations/cs.json index 04829af0b8f..ac4be10dc8d 100644 --- a/homeassistant/components/iqvia/translations/cs.json +++ b/homeassistant/components/iqvia/translations/cs.json @@ -11,8 +11,7 @@ "data": { "zip_code": "PS\u010c" }, - "description": "Vypl\u0148te sv\u00e9 americk\u00e9 nebo kanadsk\u00e9 PS\u010c.", - "title": "IQVIA" + "description": "Vypl\u0148te sv\u00e9 americk\u00e9 nebo kanadsk\u00e9 PS\u010c." } } } diff --git a/homeassistant/components/iqvia/translations/da.json b/homeassistant/components/iqvia/translations/da.json index 8cc93be50ec..8e074aacea7 100644 --- a/homeassistant/components/iqvia/translations/da.json +++ b/homeassistant/components/iqvia/translations/da.json @@ -8,8 +8,7 @@ "data": { "zip_code": "Postnummer" }, - "description": "Udfyld dit amerikanske eller canadiske postnummer.", - "title": "IQVIA" + "description": "Udfyld dit amerikanske eller canadiske postnummer." } } } diff --git a/homeassistant/components/iqvia/translations/de.json b/homeassistant/components/iqvia/translations/de.json index 5d307eea829..90cf3a434c8 100644 --- a/homeassistant/components/iqvia/translations/de.json +++ b/homeassistant/components/iqvia/translations/de.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Postleitzahl" }, - "description": "Trage eine US-amerikanische oder kanadische Postleitzahl ein.", - "title": "IQVIA" + "description": "Trage eine US-amerikanische oder kanadische Postleitzahl ein." } } } diff --git a/homeassistant/components/iqvia/translations/el.json b/homeassistant/components/iqvia/translations/el.json index 4a3045201e2..44f2aab43eb 100644 --- a/homeassistant/components/iqvia/translations/el.json +++ b/homeassistant/components/iqvia/translations/el.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u03a4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc\u03c2 \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2" }, - "description": "\u03a3\u03c5\u03bc\u03c0\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 \u03c4\u03c9\u03bd \u0397\u03a0\u0391 \u03ae \u03c4\u03bf\u03c5 \u039a\u03b1\u03bd\u03b1\u03b4\u03ac.", - "title": "IQVIA" + "description": "\u03a3\u03c5\u03bc\u03c0\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c4\u03b1\u03c7\u03c5\u03b4\u03c1\u03bf\u03bc\u03b9\u03ba\u03cc \u03ba\u03ce\u03b4\u03b9\u03ba\u03b1 \u03c4\u03c9\u03bd \u0397\u03a0\u0391 \u03ae \u03c4\u03bf\u03c5 \u039a\u03b1\u03bd\u03b1\u03b4\u03ac." } } } diff --git a/homeassistant/components/iqvia/translations/en.json b/homeassistant/components/iqvia/translations/en.json index 46b9189660b..1732f9c9c2e 100644 --- a/homeassistant/components/iqvia/translations/en.json +++ b/homeassistant/components/iqvia/translations/en.json @@ -11,8 +11,7 @@ "data": { "zip_code": "ZIP Code" }, - "description": "Fill out your U.S. or Canadian ZIP code.", - "title": "IQVIA" + "description": "Fill out your U.S. or Canadian ZIP code." } } } diff --git a/homeassistant/components/iqvia/translations/es-419.json b/homeassistant/components/iqvia/translations/es-419.json index 61fa3f885a2..6c47623626b 100644 --- a/homeassistant/components/iqvia/translations/es-419.json +++ b/homeassistant/components/iqvia/translations/es-419.json @@ -8,8 +8,7 @@ "data": { "zip_code": "C\u00f3digo postal" }, - "description": "Complete su c\u00f3digo postal de EE. UU. o Canad\u00e1.", - "title": "IQVIA" + "description": "Complete su c\u00f3digo postal de EE. UU. o Canad\u00e1." } } } diff --git a/homeassistant/components/iqvia/translations/es.json b/homeassistant/components/iqvia/translations/es.json index 1d02f6e60c8..cecbb7af591 100644 --- a/homeassistant/components/iqvia/translations/es.json +++ b/homeassistant/components/iqvia/translations/es.json @@ -11,8 +11,7 @@ "data": { "zip_code": "C\u00f3digo postal" }, - "description": "Indica tu c\u00f3digo postal de Estados Unidos o Canad\u00e1.", - "title": "IQVIA" + "description": "Indica tu c\u00f3digo postal de Estados Unidos o Canad\u00e1." } } } diff --git a/homeassistant/components/iqvia/translations/et.json b/homeassistant/components/iqvia/translations/et.json index e1f6e51ef7c..5a9771f0d0f 100644 --- a/homeassistant/components/iqvia/translations/et.json +++ b/homeassistant/components/iqvia/translations/et.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Sihtnumber" }, - "description": "Sisesta oma USA v\u00f5i Kanada sihtnumber.", - "title": "" + "description": "Sisesta oma USA v\u00f5i Kanada sihtnumber." } } } diff --git a/homeassistant/components/iqvia/translations/fi.json b/homeassistant/components/iqvia/translations/fi.json index 375176873f0..bc76d09c8e5 100644 --- a/homeassistant/components/iqvia/translations/fi.json +++ b/homeassistant/components/iqvia/translations/fi.json @@ -7,8 +7,7 @@ "user": { "data": { "zip_code": "Postinumero" - }, - "title": "IQVIA" + } } } } diff --git a/homeassistant/components/iqvia/translations/fr.json b/homeassistant/components/iqvia/translations/fr.json index 97adf4c35a6..8bc7bd18ada 100644 --- a/homeassistant/components/iqvia/translations/fr.json +++ b/homeassistant/components/iqvia/translations/fr.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Code postal" }, - "description": "Entrez votre code postal am\u00e9ricain ou canadien.", - "title": "IQVIA" + "description": "Entrez votre code postal am\u00e9ricain ou canadien." } } } diff --git a/homeassistant/components/iqvia/translations/hu.json b/homeassistant/components/iqvia/translations/hu.json index 0ae420e47aa..c28fc2b23ab 100644 --- a/homeassistant/components/iqvia/translations/hu.json +++ b/homeassistant/components/iqvia/translations/hu.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Ir\u00e1ny\u00edt\u00f3sz\u00e1m" }, - "description": "T\u00f6ltse ki amerikai vagy kanadai ir\u00e1ny\u00edt\u00f3sz\u00e1m\u00e1t.", - "title": "IQVIA" + "description": "T\u00f6ltse ki amerikai vagy kanadai ir\u00e1ny\u00edt\u00f3sz\u00e1m\u00e1t." } } } diff --git a/homeassistant/components/iqvia/translations/id.json b/homeassistant/components/iqvia/translations/id.json index 32f9b77135b..ed6999558da 100644 --- a/homeassistant/components/iqvia/translations/id.json +++ b/homeassistant/components/iqvia/translations/id.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Kode Pos" }, - "description": "Isi kode pos AS atau Kanada.", - "title": "IQVIA" + "description": "Isi kode pos AS atau Kanada." } } } diff --git a/homeassistant/components/iqvia/translations/it.json b/homeassistant/components/iqvia/translations/it.json index 67d537bab5f..2e9b1e7fd9a 100644 --- a/homeassistant/components/iqvia/translations/it.json +++ b/homeassistant/components/iqvia/translations/it.json @@ -11,8 +11,7 @@ "data": { "zip_code": "CAP" }, - "description": "Compila il tuo CAP americano o canadese.", - "title": "IQVIA" + "description": "Compila il tuo CAP americano o canadese." } } } diff --git a/homeassistant/components/iqvia/translations/ja.json b/homeassistant/components/iqvia/translations/ja.json index 7a9e136ddc9..e4fde9a5eef 100644 --- a/homeassistant/components/iqvia/translations/ja.json +++ b/homeassistant/components/iqvia/translations/ja.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u90f5\u4fbf\u756a\u53f7" }, - "description": "\u7c73\u56fd\u307e\u305f\u306f\u30ab\u30ca\u30c0\u306e\u90f5\u4fbf\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "IQVIA" + "description": "\u7c73\u56fd\u307e\u305f\u306f\u30ab\u30ca\u30c0\u306e\u90f5\u4fbf\u756a\u53f7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } } diff --git a/homeassistant/components/iqvia/translations/ko.json b/homeassistant/components/iqvia/translations/ko.json index 1b0dfd980ec..f1b73c4a101 100644 --- a/homeassistant/components/iqvia/translations/ko.json +++ b/homeassistant/components/iqvia/translations/ko.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\uc6b0\ud3b8\ubc88\ud638" }, - "description": "\ubbf8\uad6d \ub610\ub294 \uce90\ub098\ub2e4\uc758 \uc6b0\ud3b8\ubc88\ud638\ub97c \uae30\uc785\ud574\uc8fc\uc138\uc694.", - "title": "IQVIA" + "description": "\ubbf8\uad6d \ub610\ub294 \uce90\ub098\ub2e4\uc758 \uc6b0\ud3b8\ubc88\ud638\ub97c \uae30\uc785\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/iqvia/translations/lb.json b/homeassistant/components/iqvia/translations/lb.json index 02fa61fe5cc..45a7984368c 100644 --- a/homeassistant/components/iqvia/translations/lb.json +++ b/homeassistant/components/iqvia/translations/lb.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Postleitzuel" }, - "description": "Gitt \u00e4r U.S. oder Kanadesch Postleitzuel un.", - "title": "IQVIA" + "description": "Gitt \u00e4r U.S. oder Kanadesch Postleitzuel un." } } } diff --git a/homeassistant/components/iqvia/translations/nl.json b/homeassistant/components/iqvia/translations/nl.json index 753e35c74ea..13d7e0ce4d3 100644 --- a/homeassistant/components/iqvia/translations/nl.json +++ b/homeassistant/components/iqvia/translations/nl.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Postcode" }, - "description": "Vul uw Amerikaanse of Canadese postcode in.", - "title": "IQVIA" + "description": "Vul uw Amerikaanse of Canadese postcode in." } } } diff --git a/homeassistant/components/iqvia/translations/nn.json b/homeassistant/components/iqvia/translations/nn.json deleted file mode 100644 index edbbd37f82a..00000000000 --- a/homeassistant/components/iqvia/translations/nn.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "IQVIA" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/iqvia/translations/no.json b/homeassistant/components/iqvia/translations/no.json index 5e073cefead..4042cb5346e 100644 --- a/homeassistant/components/iqvia/translations/no.json +++ b/homeassistant/components/iqvia/translations/no.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Postnummer" }, - "description": "Fyll ut ditt amerikanske eller kanadiske postnummer.", - "title": "" + "description": "Fyll ut ditt amerikanske eller kanadiske postnummer." } } } diff --git a/homeassistant/components/iqvia/translations/pl.json b/homeassistant/components/iqvia/translations/pl.json index b5d10e9cc09..9d396521cdf 100644 --- a/homeassistant/components/iqvia/translations/pl.json +++ b/homeassistant/components/iqvia/translations/pl.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Kod pocztowy" }, - "description": "Wprowad\u017a ameryka\u0144ski lub kanadyjski kod pocztowy.", - "title": "IQVIA" + "description": "Wprowad\u017a ameryka\u0144ski lub kanadyjski kod pocztowy." } } } diff --git a/homeassistant/components/iqvia/translations/pt-BR.json b/homeassistant/components/iqvia/translations/pt-BR.json index a366280ec35..0287fbb0d42 100644 --- a/homeassistant/components/iqvia/translations/pt-BR.json +++ b/homeassistant/components/iqvia/translations/pt-BR.json @@ -11,8 +11,7 @@ "data": { "zip_code": "C\u00f3digo postal" }, - "description": "Preencha o seu CEP dos EUA ou Canad\u00e1.", - "title": "IQVIA" + "description": "Preencha o seu CEP dos EUA ou Canad\u00e1." } } } diff --git a/homeassistant/components/iqvia/translations/ru.json b/homeassistant/components/iqvia/translations/ru.json index 6e2ddb93698..a785ef97a05 100644 --- a/homeassistant/components/iqvia/translations/ru.json +++ b/homeassistant/components/iqvia/translations/ru.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 (\u0434\u043b\u044f \u0421\u0428\u0410 \u0438\u043b\u0438 \u041a\u0430\u043d\u0430\u0434\u044b).", - "title": "IQVIA" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 (\u0434\u043b\u044f \u0421\u0428\u0410 \u0438\u043b\u0438 \u041a\u0430\u043d\u0430\u0434\u044b)." } } } diff --git a/homeassistant/components/iqvia/translations/sl.json b/homeassistant/components/iqvia/translations/sl.json index 351ad666c37..6afc35a489e 100644 --- a/homeassistant/components/iqvia/translations/sl.json +++ b/homeassistant/components/iqvia/translations/sl.json @@ -8,8 +8,7 @@ "data": { "zip_code": "Po\u0161tna \u0161tevilka" }, - "description": "Izpolnite svojo ameri\u0161ko ali kanadsko po\u0161tno \u0161tevilko.", - "title": "IQVIA" + "description": "Izpolnite svojo ameri\u0161ko ali kanadsko po\u0161tno \u0161tevilko." } } } diff --git a/homeassistant/components/iqvia/translations/sv.json b/homeassistant/components/iqvia/translations/sv.json index 88054725823..71eb118a858 100644 --- a/homeassistant/components/iqvia/translations/sv.json +++ b/homeassistant/components/iqvia/translations/sv.json @@ -8,8 +8,7 @@ "data": { "zip_code": "Postnummer" }, - "description": "Fyll i ditt Amerikanska eller Kanadensiska postnummer", - "title": "IQVIA" + "description": "Fyll i ditt Amerikanska eller Kanadensiska postnummer" } } } diff --git a/homeassistant/components/iqvia/translations/tr.json b/homeassistant/components/iqvia/translations/tr.json index 0b8a702d696..c5a6be17c9c 100644 --- a/homeassistant/components/iqvia/translations/tr.json +++ b/homeassistant/components/iqvia/translations/tr.json @@ -11,8 +11,7 @@ "data": { "zip_code": "Posta kodu" }, - "description": "ABD veya Kanada Posta kodunuzu doldurun.", - "title": "IQVIA" + "description": "ABD veya Kanada Posta kodunuzu doldurun." } } } diff --git a/homeassistant/components/iqvia/translations/uk.json b/homeassistant/components/iqvia/translations/uk.json index ab9813d6289..0d4d8940a86 100644 --- a/homeassistant/components/iqvia/translations/uk.json +++ b/homeassistant/components/iqvia/translations/uk.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u041f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441" }, - "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0441\u0432\u0456\u0439 \u043f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441 (\u0434\u043b\u044f \u0421\u0428\u0410 \u0430\u0431\u043e \u041a\u0430\u043d\u0430\u0434\u0438).", - "title": "IQVIA" + "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0441\u0432\u0456\u0439 \u043f\u043e\u0448\u0442\u043e\u0432\u0438\u0439 \u0456\u043d\u0434\u0435\u043a\u0441 (\u0434\u043b\u044f \u0421\u0428\u0410 \u0430\u0431\u043e \u041a\u0430\u043d\u0430\u0434\u0438)." } } } diff --git a/homeassistant/components/iqvia/translations/zh-Hans.json b/homeassistant/components/iqvia/translations/zh-Hans.json index 9b5f24c1d5f..b5150f514c4 100644 --- a/homeassistant/components/iqvia/translations/zh-Hans.json +++ b/homeassistant/components/iqvia/translations/zh-Hans.json @@ -8,8 +8,7 @@ "data": { "zip_code": "\u90ae\u653f\u7f16\u7801" }, - "description": "\u586b\u5199\u60a8\u7684\u7f8e\u56fd\u6216\u52a0\u62ff\u5927\u90ae\u653f\u7f16\u7801\u3002", - "title": "IQVIA" + "description": "\u586b\u5199\u60a8\u7684\u7f8e\u56fd\u6216\u52a0\u62ff\u5927\u90ae\u653f\u7f16\u7801\u3002" } } } diff --git a/homeassistant/components/iqvia/translations/zh-Hant.json b/homeassistant/components/iqvia/translations/zh-Hant.json index 12d1edce896..fcb239e82c7 100644 --- a/homeassistant/components/iqvia/translations/zh-Hant.json +++ b/homeassistant/components/iqvia/translations/zh-Hant.json @@ -11,8 +11,7 @@ "data": { "zip_code": "\u90f5\u905e\u5340\u865f" }, - "description": "\u586b\u5beb\u7f8e\u570b\u6216\u52a0\u62ff\u5927\u90f5\u905e\u5340\u865f\u3002", - "title": "IQVIA" + "description": "\u586b\u5beb\u7f8e\u570b\u6216\u52a0\u62ff\u5927\u90f5\u905e\u5340\u865f\u3002" } } } diff --git a/homeassistant/components/iss/translations/bg.json b/homeassistant/components/iss/translations/bg.json index 05945056fb4..d1b61daf0ec 100644 --- a/homeassistant/components/iss/translations/bg.json +++ b/homeassistant/components/iss/translations/bg.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u0414\u0430 \u0441\u0435 \u043f\u043e\u043a\u0430\u0436\u0435 \u043d\u0430 \u043a\u0430\u0440\u0442\u0430\u0442\u0430?" - }, "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442\u0435 \u041c\u0435\u0436\u0434\u0443\u043d\u0430\u0440\u043e\u0434\u043d\u0430\u0442\u0430 \u043a\u043e\u0441\u043c\u0438\u0447\u0435\u0441\u043a\u0430 \u0441\u0442\u0430\u043d\u0446\u0438\u044f?" } } diff --git a/homeassistant/components/iss/translations/ca.json b/homeassistant/components/iss/translations/ca.json index 23e4ff4eabd..3ce642e7564 100644 --- a/homeassistant/components/iss/translations/ca.json +++ b/homeassistant/components/iss/translations/ca.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Mostrar al mapa?" - }, "description": "Vols configurar Estaci\u00f3 Espacial Internacional (ISS)?" } } diff --git a/homeassistant/components/iss/translations/de.json b/homeassistant/components/iss/translations/de.json index 04ae0f6e9d5..c7eb9434db2 100644 --- a/homeassistant/components/iss/translations/de.json +++ b/homeassistant/components/iss/translations/de.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Auf der Karte anzeigen?" - }, "description": "M\u00f6chtest du die Internationale Raumstation (ISS) konfigurieren?" } } diff --git a/homeassistant/components/iss/translations/el.json b/homeassistant/components/iss/translations/el.json index 0938fd72c09..8d9570225ba 100644 --- a/homeassistant/components/iss/translations/el.json +++ b/homeassistant/components/iss/translations/el.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c3\u03c4\u03bf \u03c7\u03ac\u03c1\u03c4\u03b7;" - }, "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03bf\u03c5\u03c2 \u03c4\u03bf\u03c5 \u0394\u03b9\u03b5\u03b8\u03bd\u03bf\u03cd\u03c2 \u0394\u03b9\u03b1\u03c3\u03c4\u03b7\u03bc\u03b9\u03ba\u03bf\u03cd \u03a3\u03c4\u03b1\u03b8\u03bc\u03bf\u03cd (ISS);" } } diff --git a/homeassistant/components/iss/translations/en.json b/homeassistant/components/iss/translations/en.json index 56f9bd79e88..b90ff56964a 100644 --- a/homeassistant/components/iss/translations/en.json +++ b/homeassistant/components/iss/translations/en.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Show on map?" - }, "description": "Do you want to configure International Space Station (ISS)?" } } diff --git a/homeassistant/components/iss/translations/es.json b/homeassistant/components/iss/translations/es.json index a0456431ad8..6ca3e875615 100644 --- a/homeassistant/components/iss/translations/es.json +++ b/homeassistant/components/iss/translations/es.json @@ -5,9 +5,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u00bfMostrar en el mapa?" - }, "description": "\u00bfQuieres configurar Estaci\u00f3n Espacial Internacional (ISS)?" } } diff --git a/homeassistant/components/iss/translations/et.json b/homeassistant/components/iss/translations/et.json index 60385881b19..d298c57c64b 100644 --- a/homeassistant/components/iss/translations/et.json +++ b/homeassistant/components/iss/translations/et.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Kas n\u00e4idata kaardil?" - }, "description": "Kas seadistada rahvusvahelise kosmosejaama (ISS) sidumist?" } } diff --git a/homeassistant/components/iss/translations/fr.json b/homeassistant/components/iss/translations/fr.json index 2f9ad5d427c..d1ef91aed9d 100644 --- a/homeassistant/components/iss/translations/fr.json +++ b/homeassistant/components/iss/translations/fr.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Afficher sur la carte\u00a0?" - }, "description": "Voulez-vous configurer la Station spatiale internationale (ISS)\u00a0?" } } diff --git a/homeassistant/components/iss/translations/hu.json b/homeassistant/components/iss/translations/hu.json index bfe593e9592..5dd2d53d074 100644 --- a/homeassistant/components/iss/translations/hu.json +++ b/homeassistant/components/iss/translations/hu.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Megjelenjen a t\u00e9rk\u00e9pen?" - }, "description": "Szeretn\u00e9 konfigur\u00e1lni a Nemzetk\u00f6zi \u0170r\u00e1llom\u00e1st (ISS)?" } } diff --git a/homeassistant/components/iss/translations/id.json b/homeassistant/components/iss/translations/id.json index c53287164ee..ddf2b1a4f71 100644 --- a/homeassistant/components/iss/translations/id.json +++ b/homeassistant/components/iss/translations/id.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Tampilkan di peta?" - }, "description": "Ingin mengonfigurasi Stasiun Luar Angkasa Internasional (ISS)?" } } diff --git a/homeassistant/components/iss/translations/it.json b/homeassistant/components/iss/translations/it.json index b3ec1329eae..eaa43bc34aa 100644 --- a/homeassistant/components/iss/translations/it.json +++ b/homeassistant/components/iss/translations/it.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Mostrare sulla mappa?" - }, "description": "Vuoi configurare la Stazione Spaziale Internazionale (ISS)?" } } diff --git a/homeassistant/components/iss/translations/ja.json b/homeassistant/components/iss/translations/ja.json index 40178b203f9..bf5deeaa716 100644 --- a/homeassistant/components/iss/translations/ja.json +++ b/homeassistant/components/iss/translations/ja.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u5730\u56f3\u306b\u8868\u793a\u3057\u307e\u3059\u304b\uff1f" - }, "description": "\u56fd\u969b\u5b87\u5b99\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u306e\u8a2d\u5b9a\u3092\u3057\u307e\u3059\u304b\uff1f" } } diff --git a/homeassistant/components/iss/translations/nl.json b/homeassistant/components/iss/translations/nl.json index 08a170b1ccc..cbe31ac6674 100644 --- a/homeassistant/components/iss/translations/nl.json +++ b/homeassistant/components/iss/translations/nl.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Op kaart tonen?" - }, "description": "Wilt u International Space Station configureren?" } } diff --git a/homeassistant/components/iss/translations/no.json b/homeassistant/components/iss/translations/no.json index c204f5a9012..1999c50c8be 100644 --- a/homeassistant/components/iss/translations/no.json +++ b/homeassistant/components/iss/translations/no.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Vis p\u00e5 kart?" - }, "description": "Vil du konfigurere den internasjonale romstasjonen (ISS)?" } } diff --git a/homeassistant/components/iss/translations/pl.json b/homeassistant/components/iss/translations/pl.json index 2cdf8ef4863..c7e39260365 100644 --- a/homeassistant/components/iss/translations/pl.json +++ b/homeassistant/components/iss/translations/pl.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Pokaza\u0107 na mapie?" - }, "description": "Czy chcesz skonfigurowa\u0107 Mi\u0119dzynarodow\u0105 Stacj\u0119 Kosmiczn\u0105 (ISS)?" } } diff --git a/homeassistant/components/iss/translations/pt-BR.json b/homeassistant/components/iss/translations/pt-BR.json index c69fefed0c5..6618abe68f4 100644 --- a/homeassistant/components/iss/translations/pt-BR.json +++ b/homeassistant/components/iss/translations/pt-BR.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Mostrar no mapa" - }, "description": "Deseja configurar a Esta\u00e7\u00e3o Espacial Internacional (ISS)?" } } @@ -17,7 +14,7 @@ "step": { "init": { "data": { - "show_on_map": "Mostrar no mapa" + "show_on_map": "[%key:component::iss::config::step::user::data::show_on_map%]" } } } diff --git a/homeassistant/components/iss/translations/ru.json b/homeassistant/components/iss/translations/ru.json index 64604c1f460..e0f965bd212 100644 --- a/homeassistant/components/iss/translations/ru.json +++ b/homeassistant/components/iss/translations/ru.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043a\u0430\u0440\u0442\u0435" - }, "description": "\u041d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 International Space Station (ISS)?" } } diff --git a/homeassistant/components/iss/translations/tr.json b/homeassistant/components/iss/translations/tr.json index 3cb92db229c..f647a6c02c2 100644 --- a/homeassistant/components/iss/translations/tr.json +++ b/homeassistant/components/iss/translations/tr.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "Haritada g\u00f6sterilsin mi?" - }, "description": "Uluslararas\u0131 Uzay \u0130stasyonunu (ISS) yap\u0131land\u0131rmak istiyor musunuz?" } } diff --git a/homeassistant/components/iss/translations/uk.json b/homeassistant/components/iss/translations/uk.json index cfcf3e0c458..ca7bf63af76 100644 --- a/homeassistant/components/iss/translations/uk.json +++ b/homeassistant/components/iss/translations/uk.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043d\u0430 \u043c\u0430\u043f\u0456?" - }, "description": "\u0427\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u0432\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u041c\u0456\u0436\u043d\u0430\u0440\u043e\u0434\u043d\u0443 \u041a\u043e\u0441\u043c\u0456\u0447\u043d\u0443 \u0421\u0442\u0430\u043d\u0446\u0456\u044e?" } } diff --git a/homeassistant/components/iss/translations/zh-Hans.json b/homeassistant/components/iss/translations/zh-Hans.json deleted file mode 100644 index 47c25d7ddff..00000000000 --- a/homeassistant/components/iss/translations/zh-Hans.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "show_on_map": "\u5728\u5730\u56fe\u4e0a\u663e\u793a\uff1f" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/iss/translations/zh-Hant.json b/homeassistant/components/iss/translations/zh-Hant.json index f50730ff9a4..cdccbe14058 100644 --- a/homeassistant/components/iss/translations/zh-Hant.json +++ b/homeassistant/components/iss/translations/zh-Hant.json @@ -6,9 +6,6 @@ }, "step": { "user": { - "data": { - "show_on_map": "\u65bc\u5730\u5716\u986f\u793a\uff1f" - }, "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u570b\u969b\u592a\u7a7a\u7ad9\uff08ISS\uff09\uff1f" } } diff --git a/homeassistant/components/isy994/translations/ko.json b/homeassistant/components/isy994/translations/ko.json index 29829e066b9..8673aa7a148 100644 --- a/homeassistant/components/isy994/translations/ko.json +++ b/homeassistant/components/isy994/translations/ko.json @@ -7,10 +7,19 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_host": "\ud638\uc2a4\ud2b8 \ud56d\ubaa9\uc774 \uc644\uc804\ud55c URL \ud615\uc2dd\uc774 \uc544\ub2d9\ub2c8\ub2e4. \uc608: http://192.168.10.100:80", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "flow_title": "ISY994 \ubc94\uc6a9 \uae30\uae30: {name} ({host})", "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "description": "{host}\uc5d0 \ub300\ud55c \uc790\uaca9 \uc99d\uba85\uc774 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "title": "ISY \uc7ac\uc778\uc99d" + }, "user": { "data": { "host": "URL \uc8fc\uc18c", diff --git a/homeassistant/components/kaleidescape/translations/bg.json b/homeassistant/components/kaleidescape/translations/bg.json index cb36ec53c4b..c31eea5d60e 100644 --- a/homeassistant/components/kaleidescape/translations/bg.json +++ b/homeassistant/components/kaleidescape/translations/bg.json @@ -11,14 +11,10 @@ }, "flow_title": "{model} ({name})", "step": { - "discovery_confirm": { - "title": "Kaleidescape" - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430 Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/ca.json b/homeassistant/components/kaleidescape/translations/ca.json index ec7eac6ef4f..8a8df0f6503 100644 --- a/homeassistant/components/kaleidescape/translations/ca.json +++ b/homeassistant/components/kaleidescape/translations/ca.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Vols configurar el reproductor {name} model {model}?", - "title": "Kaleidescape" + "description": "Vols configurar el reproductor {name} model {model}?" }, "user": { "data": { "host": "Amfitri\u00f3" - }, - "title": "Configuraci\u00f3 de Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/de.json b/homeassistant/components/kaleidescape/translations/de.json index 3b353208a64..0d9a93f8596 100644 --- a/homeassistant/components/kaleidescape/translations/de.json +++ b/homeassistant/components/kaleidescape/translations/de.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du den Player {model} mit dem Namen {name} einrichten?", - "title": "Kaleidescape" + "description": "M\u00f6chtest du den Player {model} mit dem Namen {name} einrichten?" }, "user": { "data": { "host": "Host" - }, - "title": "Kaleidescape-Setup" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/el.json b/homeassistant/components/kaleidescape/translations/el.json index dc042655b02..e9315dd4cf7 100644 --- a/homeassistant/components/kaleidescape/translations/el.json +++ b/homeassistant/components/kaleidescape/translations/el.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 {model} \u03bc\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1{name};", - "title": "Kaleidescape" + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03c0\u03c1\u03cc\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 {model} \u03bc\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1{name};" }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - }, - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/en.json b/homeassistant/components/kaleidescape/translations/en.json index 43be9c030c0..cd6460e0fa9 100644 --- a/homeassistant/components/kaleidescape/translations/en.json +++ b/homeassistant/components/kaleidescape/translations/en.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Do you want to set up the {model} player named {name}?", - "title": "Kaleidescape" + "description": "Do you want to set up the {model} player named {name}?" }, "user": { "data": { "host": "Host" - }, - "title": "Kaleidescape Setup" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/et.json b/homeassistant/components/kaleidescape/translations/et.json index 52ece37ad97..9da5e2a8a68 100644 --- a/homeassistant/components/kaleidescape/translations/et.json +++ b/homeassistant/components/kaleidescape/translations/et.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Kas seadistada m\u00e4ngijat {model} nimega {name} ?", - "title": "Kaleidescape" + "description": "Kas seadistada m\u00e4ngijat {model} nimega {name} ?" }, "user": { "data": { "host": "Host" - }, - "title": "Kaleidescape'i seadistamine" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/fr.json b/homeassistant/components/kaleidescape/translations/fr.json index 48dfd027765..bf03d0ecacf 100644 --- a/homeassistant/components/kaleidescape/translations/fr.json +++ b/homeassistant/components/kaleidescape/translations/fr.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Voulez-vous configurer le lecteur {model} nomm\u00e9 {name}\u00a0?", - "title": "Kaleidescape" + "description": "Voulez-vous configurer le lecteur {model} nomm\u00e9 {name}\u00a0?" }, "user": { "data": { "host": "H\u00f4te" - }, - "title": "Configuration de Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/hu.json b/homeassistant/components/kaleidescape/translations/hu.json index cd8b00e18a3..ad26902cdf3 100644 --- a/homeassistant/components/kaleidescape/translations/hu.json +++ b/homeassistant/components/kaleidescape/translations/hu.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani a {name} nev\u0171, {model} t\u00edpus\u00fa lej\u00e1tsz\u00f3t?", - "title": "Kaleidescape" + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani a {name} nev\u0171, {model} t\u00edpus\u00fa lej\u00e1tsz\u00f3t?" }, "user": { "data": { "host": "C\u00edm" - }, - "title": "Kaleidescape be\u00e1ll\u00edt\u00e1sa" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/id.json b/homeassistant/components/kaleidescape/translations/id.json index 626f23354d4..7833bf5ad4c 100644 --- a/homeassistant/components/kaleidescape/translations/id.json +++ b/homeassistant/components/kaleidescape/translations/id.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Ingin menyiapkan pemutar {model} dengan nama {name}?", - "title": "Kaleidescape" + "description": "Ingin menyiapkan pemutar {model} dengan nama {name}?" }, "user": { "data": { "host": "Host" - }, - "title": "Penyiapan Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/it.json b/homeassistant/components/kaleidescape/translations/it.json index 022b73ae0d7..35235b2986b 100644 --- a/homeassistant/components/kaleidescape/translations/it.json +++ b/homeassistant/components/kaleidescape/translations/it.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Vuoi configurare il lettore {model} nome {name}?", - "title": "Kaleidescape" + "description": "Vuoi configurare il lettore {model} nome {name}?" }, "user": { "data": { "host": "Host" - }, - "title": "Configurazione di Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/ja.json b/homeassistant/components/kaleidescape/translations/ja.json index 368a1565648..b4d49c09f74 100644 --- a/homeassistant/components/kaleidescape/translations/ja.json +++ b/homeassistant/components/kaleidescape/translations/ja.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "{model} \u30d7\u30ec\u30a4\u30e4\u30fc\u540d {name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f", - "title": "\u30ab\u30ec\u30a4\u30c9\u30b9\u30b1\u30fc\u30d7(Kaleidescape)" + "description": "{model} \u30d7\u30ec\u30a4\u30e4\u30fc\u540d {name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, "user": { "data": { "host": "\u30db\u30b9\u30c8" - }, - "title": "\u30ab\u30ec\u30a4\u30c9\u30b9\u30b1\u30fc\u30d7(Kaleidescape)\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/nl.json b/homeassistant/components/kaleidescape/translations/nl.json index 2b2491deb85..b3498db12a3 100644 --- a/homeassistant/components/kaleidescape/translations/nl.json +++ b/homeassistant/components/kaleidescape/translations/nl.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Wil je de speler {model} met de naam {name} instellen?", - "title": "Kaleidescape" + "description": "Wil je de speler {model} met de naam {name} instellen?" }, "user": { "data": { "host": "Host" - }, - "title": "Kaleidescape configuratie" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/no.json b/homeassistant/components/kaleidescape/translations/no.json index c07276eeeb1..6d3450c0b68 100644 --- a/homeassistant/components/kaleidescape/translations/no.json +++ b/homeassistant/components/kaleidescape/translations/no.json @@ -13,14 +13,12 @@ "flow_title": "{model} ( {name} )", "step": { "discovery_confirm": { - "description": "Vil du sette opp {model} -spilleren med navnet {name} ?", - "title": "Kaleidescape" + "description": "Vil du sette opp {model} -spilleren med navnet {name} ?" }, "user": { "data": { "host": "Vert" - }, - "title": "Kaleidescape-oppsett" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/pl.json b/homeassistant/components/kaleidescape/translations/pl.json index 111dc4db7e6..499f813aa27 100644 --- a/homeassistant/components/kaleidescape/translations/pl.json +++ b/homeassistant/components/kaleidescape/translations/pl.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Czy chcesz skonfigurowa\u0107 odtwarzacz {model} o nazwie {name}?", - "title": "Kaleidescape" + "description": "Czy chcesz skonfigurowa\u0107 odtwarzacz {model} o nazwie {name}?" }, "user": { "data": { "host": "Nazwa hosta lub adres IP" - }, - "title": "Konfiguracja Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/pt-BR.json b/homeassistant/components/kaleidescape/translations/pt-BR.json index f87534cb88a..5b5c4a35d2b 100644 --- a/homeassistant/components/kaleidescape/translations/pt-BR.json +++ b/homeassistant/components/kaleidescape/translations/pt-BR.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "Deseja configurar o player {model} chamado {name}?", - "title": "Kaleidescape" + "description": "Deseja configurar o player {model} chamado {name}?" }, "user": { "data": { "host": "Nome do host" - }, - "title": "Configura\u00e7\u00e3o do Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/ru.json b/homeassistant/components/kaleidescape/translations/ru.json index 3112fdd06e5..d94ffb58900 100644 --- a/homeassistant/components/kaleidescape/translations/ru.json +++ b/homeassistant/components/kaleidescape/translations/ru.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c {model} ({name})?", - "title": "Kaleidescape" + "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c {model} ({name})?" }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" - }, - "title": "Kaleidescape" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/tr.json b/homeassistant/components/kaleidescape/translations/tr.json index 1b891cc450c..40b004c2ea2 100644 --- a/homeassistant/components/kaleidescape/translations/tr.json +++ b/homeassistant/components/kaleidescape/translations/tr.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "{name} adl\u0131 {model} oynat\u0131c\u0131s\u0131n\u0131 kurmak istiyor musunuz?", - "title": "Kaleidescape" + "description": "{name} adl\u0131 {model} oynat\u0131c\u0131s\u0131n\u0131 kurmak istiyor musunuz?" }, "user": { "data": { "host": "Sunucu" - }, - "title": "Kaleidescape Kurulumu" + } } } } diff --git a/homeassistant/components/kaleidescape/translations/zh-Hant.json b/homeassistant/components/kaleidescape/translations/zh-Hant.json index 2fedf1ede20..4e6c5564c61 100644 --- a/homeassistant/components/kaleidescape/translations/zh-Hant.json +++ b/homeassistant/components/kaleidescape/translations/zh-Hant.json @@ -13,14 +13,12 @@ "flow_title": "{model} ({name})", "step": { "discovery_confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u540d\u70ba {name} \u7684 {model} \u64ad\u653e\u5668\uff1f", - "title": "Kaleidescape" + "description": "\u662f\u5426\u8981\u8a2d\u5b9a\u540d\u70ba {name} \u7684 {model} \u64ad\u653e\u5668\uff1f" }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef" - }, - "title": "Kaleidescape \u8a2d\u5b9a" + } } } } diff --git a/homeassistant/components/knx/translations/bg.json b/homeassistant/components/knx/translations/bg.json index 3cbc8b57b0b..331c85d4065 100644 --- a/homeassistant/components/knx/translations/bg.json +++ b/homeassistant/components/knx/translations/bg.json @@ -49,7 +49,6 @@ "tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP (\u043e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u043d\u0435 \u0441\u0442\u0435 \u0441\u0438\u0433\u0443\u0440\u043d\u0438)", "port": "\u041f\u043e\u0440\u0442", "tunneling_type": "KNX \u0442\u0443\u043d\u0435\u043b\u0435\u043d \u0442\u0438\u043f" } diff --git a/homeassistant/components/knx/translations/ca.json b/homeassistant/components/knx/translations/ca.json index b79887e1586..ff83ebec070 100644 --- a/homeassistant/components/knx/translations/ca.json +++ b/homeassistant/components/knx/translations/ca.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Amfitri\u00f3", - "individual_address": "Adre\u00e7a individual de la connexi\u00f3", "local_ip": "IP local de Home Assistant", "port": "Port", - "route_back": "Encaminament de retorn / Mode NAT", "tunneling_type": "Tipus de t\u00fanel KNX" }, "data_description": { @@ -104,15 +102,13 @@ "multicast_group": "Utilitzada per a l'encaminament i el descobriment. Per defecte: `224.0.23.12`", "multicast_port": "Utilitzat per a l'encaminament i el descobriment. Per defecte: `3671`", "rate_limit": "Telegrames de sortida m\u00e0xims per segon.\nRecomanat: de 20 a 40", - "state_updater": "Activa o desactiva globalment la lectura d'estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX, les opcions d'entitat `sync_state` no tindran cap efecte." + "state_updater": "Configuraci\u00f3 predeterminadament per llegir els estats del bus KNX. Si est\u00e0 desactivat, Home Assistant no obtindr\u00e0 activament els estats del bus KNX. Les opcions d'entitat `sync_state` poden substituir-ho." } }, "tunnel": { "data": { "host": "Amfitri\u00f3", - "local_ip": "IP local (deixa-ho en blanc si no n'est\u00e0s segur/a)", "port": "Port", - "route_back": "Encaminament de retorn / Mode NAT", "tunneling_type": "Tipus de t\u00fanel KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/de.json b/homeassistant/components/knx/translations/de.json index 588a853a8b6..1daffa9c301 100644 --- a/homeassistant/components/knx/translations/de.json +++ b/homeassistant/components/knx/translations/de.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Physikalische Adresse f\u00fcr die Verbindung", "local_ip": "Lokale IP von Home Assistant", "port": "Port", - "route_back": "Route Back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, "data_description": { @@ -104,15 +102,13 @@ "multicast_group": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: `224.0.23.12`", "multicast_port": "Wird f\u00fcr Routing und Netzwerkerkennung verwendet. Standard: \u201e3671\u201c.", "rate_limit": "Maximal gesendete Telegramme pro Sekunde.\nEmpfohlen: 20 bis 40", - "state_updater": "Aktiviere oder deaktiviere global das Lesen von Zust\u00e4nden vom KNX-Bus. Wenn deaktiviert, ruft Home Assistant nicht aktiv Zust\u00e4nde vom KNX-Bus ab, \u201esync_state\u201c-Entity-Optionen haben keine Auswirkung." + "state_updater": "Standardeinstellung f\u00fcr das Lesen von Zust\u00e4nden aus dem KNX-Bus. Wenn diese Option deaktiviert ist, wird der Home Assistant den Zustand der Entit\u00e4ten nicht aktiv vom KNX-Bus abrufen. Kann durch die Entity-Optionen `sync_state` au\u00dfer Kraft gesetzt werden." } }, "tunnel": { "data": { "host": "Host", - "local_ip": "Lokale IP (im Zweifel leer lassen)", "port": "Port", - "route_back": "Route Back / NAT-Modus", "tunneling_type": "KNX Tunneling Typ" }, "data_description": { diff --git a/homeassistant/components/knx/translations/el.json b/homeassistant/components/knx/translations/el.json index 81dd03f73fa..30047781c26 100644 --- a/homeassistant/components/knx/translations/el.json +++ b/homeassistant/components/knx/translations/el.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "individual_address": "\u0391\u03c4\u03bf\u03bc\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7", "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP \u03c4\u03bf\u03c5 Home Assistant (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03af\u03c7\u03bd\u03b5\u03c5\u03c3\u03b7)", "port": "\u0398\u03cd\u03c1\u03b1", - "route_back": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 Route Back / NAT", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "local_ip": "\u03a4\u03bf\u03c0\u03b9\u03ba\u03ae IP (\u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03b5\u03bd\u03ae \u03b1\u03bd \u03b4\u03b5\u03bd \u03b5\u03af\u03c3\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03bf\u03b9)", "port": "\u0398\u03cd\u03c1\u03b1", - "route_back": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 Route Back / NAT", "tunneling_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03ae\u03c1\u03b1\u03b3\u03b3\u03b1\u03c2 KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/en.json b/homeassistant/components/knx/translations/en.json index fd8ac9499f1..6dffe059b2a 100644 --- a/homeassistant/components/knx/translations/en.json +++ b/homeassistant/components/knx/translations/en.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Individual address for the connection", "local_ip": "Local IP of Home Assistant", "port": "Port", - "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "Local IP (leave empty if unsure)", "port": "Port", - "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { diff --git a/homeassistant/components/knx/translations/es.json b/homeassistant/components/knx/translations/es.json index 4e1c3130d64..6c8ec3e2eb6 100644 --- a/homeassistant/components/knx/translations/es.json +++ b/homeassistant/components/knx/translations/es.json @@ -13,10 +13,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Direcci\u00f3n individual para la conexi\u00f3n", "local_ip": "IP local de Home Assistant", - "port": "Puerto", - "route_back": "Modo Route Back / NAT" + "port": "Puerto" }, "data_description": { "local_ip": "D\u00e9jalo en blanco para utilizar el descubrimiento autom\u00e1tico.", @@ -91,9 +89,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "IP local (d\u00e9jelo en blanco si no est\u00e1 seguro)", - "port": "Puerto", - "route_back": "Modo Route Back / NAT" + "port": "Puerto" }, "data_description": { "host": "Direcci\u00f3n IP del dispositivo de tunelizaci\u00f3n KNX/IP.", diff --git a/homeassistant/components/knx/translations/et.json b/homeassistant/components/knx/translations/et.json index b7cd079ad3f..fe60f5404de 100644 --- a/homeassistant/components/knx/translations/et.json +++ b/homeassistant/components/knx/translations/et.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "\u00dchenduse individuaalne aadress", "local_ip": "Home Assistanti kohalik IP aadress", "port": "Port", - "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "Kohalik IP (j\u00e4ta t\u00fchjaks, kui ei ole kindel)", "port": "Port", - "route_back": "Marsruudi tagasitee / NAT-re\u017eiim", "tunneling_type": "KNX tunneli t\u00fc\u00fcp" }, "data_description": { diff --git a/homeassistant/components/knx/translations/fr.json b/homeassistant/components/knx/translations/fr.json index c75fd76debb..803fd71b734 100644 --- a/homeassistant/components/knx/translations/fr.json +++ b/homeassistant/components/knx/translations/fr.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "H\u00f4te", - "individual_address": "Adresse individuelle pour la connexion", "local_ip": "IP locale de Home Assistant", "port": "Port", - "route_back": "Retour/Mode NAT", "tunneling_type": "Type de tunnel KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "H\u00f4te", - "local_ip": "IP locale (laisser vide en cas de doute)", "port": "Port", - "route_back": "Retour/Mode NAT", "tunneling_type": "Type de tunnel KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index 02b8b0a0465..ccb9eb63c61 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "C\u00edm", - "individual_address": "A kapcsolat egy\u00e9ni c\u00edme", "local_ip": "Home Assistant lok\u00e1lis IP c\u00edme", "port": "Port", - "route_back": "\u00datvonal (route) vissza / NAT m\u00f3d", "tunneling_type": "KNX alag\u00fat t\u00edpusa" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "C\u00edm", - "local_ip": "Helyi IP c\u00edm (hagyja \u00fcresen, ha nem biztos benne)", "port": "Port", - "route_back": "\u00datvonal (route) vissza / NAT m\u00f3d", "tunneling_type": "KNX alag\u00fat t\u00edpusa" }, "data_description": { diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index e2ca98fff94..4c55a3ef392 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Alamat individu untuk koneksi", "local_ip": "IP lokal Home Assistant", "port": "Port", - "route_back": "Dirutekan Kembali/Mode NAT", "tunneling_type": "Jenis Tunnel KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "IP lokal (kosongkan jika tidak yakin)", "port": "Port", - "route_back": "Dirutekan Kembali/Mode NAT", "tunneling_type": "Jenis Tunnel KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/it.json b/homeassistant/components/knx/translations/it.json index ac780ea96ab..8c2c58ad2d5 100644 --- a/homeassistant/components/knx/translations/it.json +++ b/homeassistant/components/knx/translations/it.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Indirizzo individuale per la connessione", "local_ip": "IP locale di Home Assistant", "port": "Porta", - "route_back": "Torna indietro / Modalit\u00e0 NAT", "tunneling_type": "Tipo tunnel KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "IP locale (lascia vuoto se non sei sicuro)", "port": "Porta", - "route_back": "Torna indietro / Modalit\u00e0 NAT", "tunneling_type": "Tipo tunnel KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/ja.json b/homeassistant/components/knx/translations/ja.json index 475958af9a0..dcd44d5838f 100644 --- a/homeassistant/components/knx/translations/ja.json +++ b/homeassistant/components/knx/translations/ja.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "\u30db\u30b9\u30c8", - "individual_address": "\u63a5\u7d9a\u7528\u306e\u500b\u5225\u30a2\u30c9\u30ec\u30b9", "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "port": "\u30dd\u30fc\u30c8", - "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "\u30db\u30b9\u30c8", - "local_ip": "\u30ed\u30fc\u30ab\u30ebIP(\u4e0d\u660e\u306a\u5834\u5408\u306f\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u304f\u3060\u3055\u3044)", "port": "\u30dd\u30fc\u30c8", - "route_back": "\u30eb\u30fc\u30c8\u30d0\u30c3\u30af / NAT\u30e2\u30fc\u30c9", "tunneling_type": "KNX\u30c8\u30f3\u30cd\u30ea\u30f3\u30b0\u30bf\u30a4\u30d7" }, "data_description": { diff --git a/homeassistant/components/knx/translations/ko.json b/homeassistant/components/knx/translations/ko.json index 676037b15e4..0762b130455 100644 --- a/homeassistant/components/knx/translations/ko.json +++ b/homeassistant/components/knx/translations/ko.json @@ -1,10 +1,8 @@ { "config": { "step": { - "manual_tunnel": { - "data": { - "route_back": "\ub77c\uc6b0\ud2b8 \ubc31 / NAT \ubaa8\ub4dc" - } + "secure_manual": { + "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." } } } diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index f40b9d7729f..4c342d2741b 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Host", - "individual_address": "Individueel adres voor de verbinding", "local_ip": "Lokale IP van Home Assistant", "port": "Poort", - "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Host", - "local_ip": "Lokaal IP (laat leeg indien niet zeker)", "port": "Poort", - "route_back": "Route Back / NAT Mode", "tunneling_type": "KNX Tunneling Type" }, "data_description": { diff --git a/homeassistant/components/knx/translations/no.json b/homeassistant/components/knx/translations/no.json index f5d03e3160c..596071695b4 100644 --- a/homeassistant/components/knx/translations/no.json +++ b/homeassistant/components/knx/translations/no.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Vert", - "individual_address": "Individuell adresse for tilkoblingen", "local_ip": "Lokal IP for hjemmeassistent", "port": "Port", - "route_back": "Rute tilbake / NAT-modus", "tunneling_type": "KNX tunneltype" }, "data_description": { @@ -104,15 +102,13 @@ "multicast_group": "Brukes til ruting og oppdagelse. Standard: `224.0.23.12`", "multicast_port": "Brukes til ruting og oppdagelse. Standard: `3671`", "rate_limit": "Maksimalt utg\u00e5ende telegrammer per sekund.\n Anbefalt: 20 til 40", - "state_updater": "Globalt aktiver eller deaktiver lesetilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente statuser fra KNX-bussen, \"sync_state\"-enhetsalternativer vil ikke ha noen effekt." + "state_updater": "Sett standard for lesing av tilstander fra KNX-bussen. N\u00e5r den er deaktivert, vil ikke Home Assistant aktivt hente enhetstilstander fra KNX-bussen. Kan overstyres av entitetsalternativer for \"sync_state\"." } }, "tunnel": { "data": { "host": "Vert", - "local_ip": "Lokal IP (la st\u00e5 tomt hvis du er usikker)", "port": "Port", - "route_back": "Rute tilbake / NAT-modus", "tunneling_type": "KNX tunneltype" }, "data_description": { diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index e0821090f29..d972d9b7061 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Nazwa hosta lub adres IP", - "individual_address": "Indywidualny adres dla po\u0142\u0105czenia", "local_ip": "Lokalny adres IP Home Assistanta", "port": "Port", - "route_back": "Tryb Route Back / NAT", "tunneling_type": "Typ tunelowania KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Nazwa hosta lub adres IP", - "local_ip": "Lokalny adres IP (pozostaw pusty, je\u015bli nie masz pewno\u015bci)", "port": "Port", - "route_back": "Tryb Route Back / NAT", "tunneling_type": "Typ tunelowania KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/pt-BR.json b/homeassistant/components/knx/translations/pt-BR.json index 950cedc0721..dcdf057493b 100644 --- a/homeassistant/components/knx/translations/pt-BR.json +++ b/homeassistant/components/knx/translations/pt-BR.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Nome do host", - "individual_address": "Endere\u00e7o individual para a conex\u00e3o", "local_ip": "IP local do Home Assistant", "port": "Porta", - "route_back": "Modo Rota de Retorno / NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { @@ -104,15 +102,13 @@ "multicast_group": "Usado para roteamento e descoberta. Padr\u00e3o: `224.0.23.12`", "multicast_port": "Usado para roteamento e descoberta. Padr\u00e3o: `3671`", "rate_limit": "M\u00e1ximo de telegramas de sa\u00edda por segundo.\n Recomendado: 20 a 40", - "state_updater": "Habilite ou desabilite globalmente os estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados do barramento KNX, as op\u00e7\u00f5es de entidade `sync_state` n\u00e3o ter\u00e3o efeito." + "state_updater": "Defina o padr\u00e3o para estados de leitura do barramento KNX. Quando desativado, o Home Assistant n\u00e3o recuperar\u00e1 ativamente os estados de entidade do barramento KNX. Pode ser substitu\u00eddo pelas op\u00e7\u00f5es de entidade `sync_state`." } }, "tunnel": { "data": { "host": "Nome do host", - "local_ip": "IP local (deixe em branco se n\u00e3o tiver certeza)", "port": "Porta", - "route_back": "Modo Rota de Retorno / NAT", "tunneling_type": "Tipo de t\u00fanel KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/ru.json b/homeassistant/components/knx/translations/ru.json index 14b9f919aa2..1ca87d9ac80 100644 --- a/homeassistant/components/knx/translations/ru.json +++ b/homeassistant/components/knx/translations/ru.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", - "individual_address": "\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 Home Assistant", "port": "\u041f\u043e\u0440\u0442", - "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "\u0425\u043e\u0441\u0442", - "local_ip": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 (\u0435\u0441\u043b\u0438 \u043d\u0435 \u0437\u043d\u0430\u0435\u0442\u0435, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c)", "port": "\u041f\u043e\u0440\u0442", - "route_back": "\u041e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 / \u0440\u0435\u0436\u0438\u043c NAT", "tunneling_type": "\u0422\u0438\u043f \u0442\u0443\u043d\u043d\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f KNX" }, "data_description": { diff --git a/homeassistant/components/knx/translations/sl.json b/homeassistant/components/knx/translations/sl.json index 2e32080bfa0..d44b7dbd9cb 100644 --- a/homeassistant/components/knx/translations/sl.json +++ b/homeassistant/components/knx/translations/sl.json @@ -11,7 +11,6 @@ "manual_tunnel": { "data": { "host": "Gostitelj", - "individual_address": "Posamezni naslov za povezavo", "port": "Vrata" } }, diff --git a/homeassistant/components/knx/translations/tr.json b/homeassistant/components/knx/translations/tr.json index 5a2863baaf4..6ed12e70aee 100644 --- a/homeassistant/components/knx/translations/tr.json +++ b/homeassistant/components/knx/translations/tr.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "Sunucu", - "individual_address": "Ba\u011flant\u0131 i\u00e7in bireysel adres", "local_ip": "Home Asistan\u0131n\u0131n Yerel IP'si", "port": "Port", - "route_back": "Geri Y\u00f6nlendirme / NAT Modu", "tunneling_type": "KNX T\u00fcnel Tipi" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "Sunucu", - "local_ip": "Yerel IP (emin de\u011filseniz bo\u015f b\u0131rak\u0131n)", "port": "Port", - "route_back": "Geri Y\u00f6nlendirme / NAT Modu", "tunneling_type": "KNX T\u00fcnel Tipi" }, "data_description": { diff --git a/homeassistant/components/knx/translations/zh-Hant.json b/homeassistant/components/knx/translations/zh-Hant.json index 613cf4f74e5..b348f38701d 100644 --- a/homeassistant/components/knx/translations/zh-Hant.json +++ b/homeassistant/components/knx/translations/zh-Hant.json @@ -15,10 +15,8 @@ "manual_tunnel": { "data": { "host": "\u4e3b\u6a5f\u7aef", - "individual_address": "\u9023\u7dda\u500b\u5225\u4f4d\u5740", "local_ip": "Home Assistant \u672c\u5730\u7aef IP", "port": "\u901a\u8a0a\u57e0", - "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" }, "data_description": { @@ -110,9 +108,7 @@ "tunnel": { "data": { "host": "\u4e3b\u6a5f\u7aef", - "local_ip": "\u672c\u5730\u7aef IP\uff08\u5047\u5982\u4e0d\u78ba\u5b9a\uff0c\u4fdd\u7559\u7a7a\u767d\uff09", "port": "\u901a\u8a0a\u57e0", - "route_back": "\u8def\u7531\u8fd4\u56de / NAT \u6a21\u5f0f", "tunneling_type": "KNX \u901a\u9053\u985e\u5225" }, "data_description": { diff --git a/homeassistant/components/kraken/translations/ko.json b/homeassistant/components/kraken/translations/ko.json new file mode 100644 index 00000000000..758f3336cd4 --- /dev/null +++ b/homeassistant/components/kraken/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/light/translations/ca.json b/homeassistant/components/light/translations/ca.json index 6ceae0d5100..2c59305e4d9 100644 --- a/homeassistant/components/light/translations/ca.json +++ b/homeassistant/components/light/translations/ca.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} s'enc\u00e9n o s'apaga", - "toggled": "{entity_name} s'enc\u00e9n o s'apaga", "turned_off": "{entity_name} apagat", "turned_on": "{entity_name} enc\u00e8s" } diff --git a/homeassistant/components/light/translations/cs.json b/homeassistant/components/light/translations/cs.json index 0a4294a44f3..5f43c684b09 100644 --- a/homeassistant/components/light/translations/cs.json +++ b/homeassistant/components/light/translations/cs.json @@ -13,7 +13,6 @@ "is_on": "{entity_name} je zapnuto" }, "trigger_type": { - "toggled": "{entity_name} zapnuto nebo vypnuto", "turned_off": "{entity_name} vypnuto", "turned_on": "{entity_name} zapnuto" } diff --git a/homeassistant/components/light/translations/de.json b/homeassistant/components/light/translations/de.json index a214f1cb9cf..f1ac946b64a 100644 --- a/homeassistant/components/light/translations/de.json +++ b/homeassistant/components/light/translations/de.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ein- oder ausgeschaltet", - "toggled": "{entity_name} ein- oder ausgeschaltet", "turned_off": "{entity_name} ausgeschaltet", "turned_on": "{entity_name} eingeschaltet" } diff --git a/homeassistant/components/light/translations/el.json b/homeassistant/components/light/translations/el.json index dbba423dd2a..16198683deb 100644 --- a/homeassistant/components/light/translations/el.json +++ b/homeassistant/components/light/translations/el.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "\u03a4\u03bf {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", - "toggled": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_off": "{entity_name} \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_on": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5" } diff --git a/homeassistant/components/light/translations/en.json b/homeassistant/components/light/translations/en.json index bdaa8a87b84..526f902b158 100644 --- a/homeassistant/components/light/translations/en.json +++ b/homeassistant/components/light/translations/en.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} turned on or off", - "toggled": "{entity_name} turned on or off", "turned_off": "{entity_name} turned off", "turned_on": "{entity_name} turned on" } diff --git a/homeassistant/components/light/translations/es.json b/homeassistant/components/light/translations/es.json index 10e8dfa3d17..53cf50215aa 100644 --- a/homeassistant/components/light/translations/es.json +++ b/homeassistant/components/light/translations/es.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} activado o desactivado", - "toggled": "{entity_name} activado o desactivado", "turned_off": "{entity_name} apagada", "turned_on": "{entity_name} encendida" } diff --git a/homeassistant/components/light/translations/et.json b/homeassistant/components/light/translations/et.json index 05d1a7690fc..224ec559bb6 100644 --- a/homeassistant/components/light/translations/et.json +++ b/homeassistant/components/light/translations/et.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", - "toggled": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "turned_off": "{entity_name} l\u00fclitus v\u00e4lja", "turned_on": "{entity_name} l\u00fclitus sisse" } diff --git a/homeassistant/components/light/translations/fr.json b/homeassistant/components/light/translations/fr.json index bcb6cac594e..c74bd8507a0 100644 --- a/homeassistant/components/light/translations/fr.json +++ b/homeassistant/components/light/translations/fr.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a \u00e9t\u00e9 allum\u00e9e ou \u00e9teinte", - "toggled": "{entity_name} a \u00e9t\u00e9 allum\u00e9e ou \u00e9teinte", "turned_off": "{entity_name} a \u00e9t\u00e9 \u00e9teinte", "turned_on": "{entity_name} a \u00e9t\u00e9 allum\u00e9e" } diff --git a/homeassistant/components/light/translations/he.json b/homeassistant/components/light/translations/he.json index b802e64ad40..2ebb31d089e 100644 --- a/homeassistant/components/light/translations/he.json +++ b/homeassistant/components/light/translations/he.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", - "toggled": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", "turned_off": "{entity_name} \u05db\u05d5\u05d1\u05d4", "turned_on": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc" } diff --git a/homeassistant/components/light/translations/hu.json b/homeassistant/components/light/translations/hu.json index 986fd5d1787..73bf81858b0 100644 --- a/homeassistant/components/light/translations/hu.json +++ b/homeassistant/components/light/translations/hu.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} be- vagy kikapcsolt", - "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} le lett kapcsolva", "turned_on": "{entity_name} fel lett kapcsolva" } diff --git a/homeassistant/components/light/translations/id.json b/homeassistant/components/light/translations/id.json index 334e938d41e..600644a36c4 100644 --- a/homeassistant/components/light/translations/id.json +++ b/homeassistant/components/light/translations/id.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} diaktifkan atau dinonaktifkan", - "toggled": "{entity_name} diaktifkan atau dinonaktifkan", "turned_off": "{entity_name} dimatikan", "turned_on": "{entity_name} dinyalakan" } diff --git a/homeassistant/components/light/translations/it.json b/homeassistant/components/light/translations/it.json index 9e751a9a4b8..7a71a2dbd91 100644 --- a/homeassistant/components/light/translations/it.json +++ b/homeassistant/components/light/translations/it.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} attivata o disattivata", - "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" } diff --git a/homeassistant/components/light/translations/ja.json b/homeassistant/components/light/translations/ja.json index 7e9599af54a..cd1008cc5a0 100644 --- a/homeassistant/components/light/translations/ja.json +++ b/homeassistant/components/light/translations/ja.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", - "toggled": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_off": "{entity_name} \u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_on": "{entity_name} \u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u307e\u3059" } diff --git a/homeassistant/components/light/translations/nl.json b/homeassistant/components/light/translations/nl.json index f70830601c7..3708a1e9a8d 100644 --- a/homeassistant/components/light/translations/nl.json +++ b/homeassistant/components/light/translations/nl.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", - "toggled": "{entity_name} in- of uitgeschakeld", "turned_off": "{entity_name} is uitgeschakeld", "turned_on": "{entity_name} is ingeschakeld" } diff --git a/homeassistant/components/light/translations/no.json b/homeassistant/components/light/translations/no.json index 92d7102d217..8ed11defddc 100644 --- a/homeassistant/components/light/translations/no.json +++ b/homeassistant/components/light/translations/no.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} sl\u00e5tt p\u00e5 eller av", - "toggled": "{entity_name} sl\u00e5tt p\u00e5 eller av", "turned_off": "{entity_name} sl\u00e5tt av", "turned_on": "{entity_name} sl\u00e5tt p\u00e5" } diff --git a/homeassistant/components/light/translations/pl.json b/homeassistant/components/light/translations/pl.json index 375cf8a8ce3..1a3b9c8b124 100644 --- a/homeassistant/components/light/translations/pl.json +++ b/homeassistant/components/light/translations/pl.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", - "toggled": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "turned_off": "nast\u0105pi wy\u0142\u0105czenie {entity_name}", "turned_on": "nast\u0105pi w\u0142\u0105czenie {entity_name}" } diff --git a/homeassistant/components/light/translations/pt-BR.json b/homeassistant/components/light/translations/pt-BR.json index f884baf9b3c..893def8c67b 100644 --- a/homeassistant/components/light/translations/pt-BR.json +++ b/homeassistant/components/light/translations/pt-BR.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} for ligada ou desligada", - "toggled": "{entity_name} for ligada ou desligada", "turned_off": "{entity_name} for desligada", "turned_on": "{entity_name} for ligada" } diff --git a/homeassistant/components/light/translations/ru.json b/homeassistant/components/light/translations/ru.json index 4010139e381..efe8ba188ec 100644 --- a/homeassistant/components/light/translations/ru.json +++ b/homeassistant/components/light/translations/ru.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", - "toggled": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_off": "{entity_name} \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_on": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f" } diff --git a/homeassistant/components/light/translations/sv.json b/homeassistant/components/light/translations/sv.json index 99f0b123b6a..d5f0bdaf767 100644 --- a/homeassistant/components/light/translations/sv.json +++ b/homeassistant/components/light/translations/sv.json @@ -12,7 +12,6 @@ "is_on": "{entity_name} \u00e4r p\u00e5" }, "trigger_type": { - "toggled": "{entity_name} slogs p\u00e5 eller av", "turned_off": "{entity_name} avst\u00e4ngd", "turned_on": "{entity_name} slogs p\u00e5" } diff --git a/homeassistant/components/light/translations/tr.json b/homeassistant/components/light/translations/tr.json index 4ddde6dabb1..a5efe591214 100644 --- a/homeassistant/components/light/translations/tr.json +++ b/homeassistant/components/light/translations/tr.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", - "toggled": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "turned_off": "{entity_name} kapat\u0131ld\u0131", "turned_on": "{entity_name} a\u00e7\u0131ld\u0131" } diff --git a/homeassistant/components/light/translations/zh-Hans.json b/homeassistant/components/light/translations/zh-Hans.json index 211fa1ed50b..1054820c6bb 100644 --- a/homeassistant/components/light/translations/zh-Hans.json +++ b/homeassistant/components/light/translations/zh-Hans.json @@ -13,7 +13,6 @@ "is_on": "{entity_name} \u5df2\u6253\u5f00" }, "trigger_type": { - "toggled": "{entity_name} \u88ab\u6253\u5f00\u6216\u5173\u95ed", "turned_off": "{entity_name} \u88ab\u5173\u95ed", "turned_on": "{entity_name} \u88ab\u6253\u5f00" } diff --git a/homeassistant/components/light/translations/zh-Hant.json b/homeassistant/components/light/translations/zh-Hant.json index e008376298e..054c325be82 100644 --- a/homeassistant/components/light/translations/zh-Hant.json +++ b/homeassistant/components/light/translations/zh-Hant.json @@ -14,7 +14,6 @@ }, "trigger_type": { "changed_states": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", - "toggled": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f" } diff --git a/homeassistant/components/litterrobot/translations/sensor.cs.json b/homeassistant/components/litterrobot/translations/sensor.cs.json new file mode 100644 index 00000000000..5b728084bed --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.cs.json @@ -0,0 +1,9 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "Vypnuto", + "offline": "Offline", + "p": "Pozastaveno" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.he.json b/homeassistant/components/litterrobot/translations/sensor.he.json new file mode 100644 index 00000000000..0c693d10157 --- /dev/null +++ b/homeassistant/components/litterrobot/translations/sensor.he.json @@ -0,0 +1,8 @@ +{ + "state": { + "litterrobot__status_code": { + "off": "\u05db\u05d1\u05d5\u05d9", + "p": "\u05de\u05d5\u05e9\u05d4\u05d4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/litterrobot/translations/sensor.ko.json b/homeassistant/components/litterrobot/translations/sensor.ko.json index c9c452b4275..49812dd242a 100644 --- a/homeassistant/components/litterrobot/translations/sensor.ko.json +++ b/homeassistant/components/litterrobot/translations/sensor.ko.json @@ -9,7 +9,10 @@ "df1": "\ubcf4\uad00\ud568 \uac70\uc758 \ucc38 - 2\uc0ac\uc774\ud074 \ub0a8\uc74c", "df2": "\ubcf4\uad00\ud568 \uac70\uc758 \ucc38 - 1\uc0ac\uc774\ud074 \ub0a8\uc74c", "dfs": "\ubcf4\uad00\ud568 \uac00\ub4dd \ucc38", + "dhf": "\ub364\ud504 + \ud648 \uc704\uce58 \uc624\ub958", + "dpf": "\ub364\ud504 \uc704\uce58 \uc624\ub958", "ec": "\ube44\uc6c0 \uc8fc\uae30", + "hpf": "\ud648 \uc704\uce58 \uc624\ub958", "off": "\uaebc\uc9d0", "offline": "\uc624\ud504\ub77c\uc778", "otf": "\ud1a0\ud06c \uc624\ub958 \uc774\uc0c1", diff --git a/homeassistant/components/luftdaten/translations/bg.json b/homeassistant/components/luftdaten/translations/bg.json index 784b010219d..5221cf5fc0e 100644 --- a/homeassistant/components/luftdaten/translations/bg.json +++ b/homeassistant/components/luftdaten/translations/bg.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u0430\u0440\u0442\u0430\u0442\u0430", "station_id": "ID \u043d\u0430 \u0441\u0435\u043d\u0437\u043e\u0440\u0430 \u043d\u0430 Luftdaten" - }, - "title": "\u0414\u0435\u0444\u0438\u043d\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/ca.json b/homeassistant/components/luftdaten/translations/ca.json index 84794f6bccb..97ca6a0eebc 100644 --- a/homeassistant/components/luftdaten/translations/ca.json +++ b/homeassistant/components/luftdaten/translations/ca.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Mostrar al mapa", "station_id": "ID del sensor" - }, - "title": "Configuraci\u00f3 de Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/cs.json b/homeassistant/components/luftdaten/translations/cs.json index 2a9d786cf12..a3522f1fb7f 100644 --- a/homeassistant/components/luftdaten/translations/cs.json +++ b/homeassistant/components/luftdaten/translations/cs.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Uka\u017e na map\u011b", "station_id": "ID senzoru Luftdaten" - }, - "title": "Definujte Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/da.json b/homeassistant/components/luftdaten/translations/da.json index fa32979112b..0612d406aa5 100644 --- a/homeassistant/components/luftdaten/translations/da.json +++ b/homeassistant/components/luftdaten/translations/da.json @@ -8,8 +8,7 @@ "data": { "show_on_map": "Vis p\u00e5 kort", "station_id": "Luftdaten sensor-id" - }, - "title": "Definer Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/de.json b/homeassistant/components/luftdaten/translations/de.json index 09fd47e4e20..56615ee355f 100644 --- a/homeassistant/components/luftdaten/translations/de.json +++ b/homeassistant/components/luftdaten/translations/de.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Auf Karte anzeigen", "station_id": "Sensor-ID" - }, - "title": "Luftdaten einrichten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/el.json b/homeassistant/components/luftdaten/translations/el.json index 59519c251ef..5b2db34c47e 100644 --- a/homeassistant/components/luftdaten/translations/el.json +++ b/homeassistant/components/luftdaten/translations/el.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03c3\u03c4\u03bf \u03c7\u03ac\u03c1\u03c4\u03b7", "station_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" - }, - "title": "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/en.json b/homeassistant/components/luftdaten/translations/en.json index ed6983eaa92..fa6a65dcaa7 100644 --- a/homeassistant/components/luftdaten/translations/en.json +++ b/homeassistant/components/luftdaten/translations/en.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Show on map", "station_id": "Sensor ID" - }, - "title": "Define Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/es-419.json b/homeassistant/components/luftdaten/translations/es-419.json index e9e97ff034d..cdd130f7266 100644 --- a/homeassistant/components/luftdaten/translations/es-419.json +++ b/homeassistant/components/luftdaten/translations/es-419.json @@ -8,8 +8,7 @@ "data": { "show_on_map": "Mostrar en el mapa", "station_id": "ID del sensor de Luftdaten" - }, - "title": "Definir Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/es.json b/homeassistant/components/luftdaten/translations/es.json index 99e5f7d5058..075d30dc751 100644 --- a/homeassistant/components/luftdaten/translations/es.json +++ b/homeassistant/components/luftdaten/translations/es.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Mostrar en el mapa", "station_id": "ID del sensor" - }, - "title": "Definir Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/et.json b/homeassistant/components/luftdaten/translations/et.json index 148007a0251..96897e55784 100644 --- a/homeassistant/components/luftdaten/translations/et.json +++ b/homeassistant/components/luftdaten/translations/et.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Kuva kaardil", "station_id": "Anduri ID" - }, - "title": "M\u00e4\u00e4ratle Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/fi.json b/homeassistant/components/luftdaten/translations/fi.json index 452cda748f7..64a83b3ed50 100644 --- a/homeassistant/components/luftdaten/translations/fi.json +++ b/homeassistant/components/luftdaten/translations/fi.json @@ -5,8 +5,7 @@ "data": { "show_on_map": "N\u00e4yt\u00e4 kartalla", "station_id": "Luftdaten-anturin ID" - }, - "title": "M\u00e4\u00e4rit\u00e4 Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/fr.json b/homeassistant/components/luftdaten/translations/fr.json index 4eacd984ad8..225ce28d13d 100644 --- a/homeassistant/components/luftdaten/translations/fr.json +++ b/homeassistant/components/luftdaten/translations/fr.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Montrer sur la carte", "station_id": "ID du capteur" - }, - "title": "D\u00e9finir Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/hu.json b/homeassistant/components/luftdaten/translations/hu.json index 7439546f796..c867456e945 100644 --- a/homeassistant/components/luftdaten/translations/hu.json +++ b/homeassistant/components/luftdaten/translations/hu.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Megjelen\u00edt\u00e9s a t\u00e9rk\u00e9pen", "station_id": "\u00c9rz\u00e9kel\u0151 azonos\u00edt\u00f3" - }, - "title": "Luftdaten be\u00e1ll\u00edt\u00e1sa" + } } } } diff --git a/homeassistant/components/luftdaten/translations/id.json b/homeassistant/components/luftdaten/translations/id.json index 11fc2917061..2ed6e65e2c3 100644 --- a/homeassistant/components/luftdaten/translations/id.json +++ b/homeassistant/components/luftdaten/translations/id.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Tampilkan di peta", "station_id": "ID Sensor" - }, - "title": "Konfigurasikan Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/it.json b/homeassistant/components/luftdaten/translations/it.json index a39b3dd7e59..84a870fd8be 100644 --- a/homeassistant/components/luftdaten/translations/it.json +++ b/homeassistant/components/luftdaten/translations/it.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Mostra sulla mappa", "station_id": "ID sensore" - }, - "title": "Definisci Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/ja.json b/homeassistant/components/luftdaten/translations/ja.json index 15dc417c156..ab01dc703d6 100644 --- a/homeassistant/components/luftdaten/translations/ja.json +++ b/homeassistant/components/luftdaten/translations/ja.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u5730\u56f3\u306b\u8868\u793a", "station_id": "Luftdaten\u30bb\u30f3\u30b5\u30fcID" - }, - "title": "Luftdaten\u306e\u5b9a\u7fa9" + } } } } diff --git a/homeassistant/components/luftdaten/translations/ko.json b/homeassistant/components/luftdaten/translations/ko.json index fbb5a26e7ee..820f47803ad 100644 --- a/homeassistant/components/luftdaten/translations/ko.json +++ b/homeassistant/components/luftdaten/translations/ko.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\uc9c0\ub3c4\uc5d0 \ud45c\uc2dc\ud558\uae30", "station_id": "Luftdaten \uc13c\uc11c ID" - }, - "title": "Luftdaten \uc815\uc758\ud558\uae30" + } } } } diff --git a/homeassistant/components/luftdaten/translations/lb.json b/homeassistant/components/luftdaten/translations/lb.json index 83254e05c83..5990ccefd17 100644 --- a/homeassistant/components/luftdaten/translations/lb.json +++ b/homeassistant/components/luftdaten/translations/lb.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Op der Kaart uweisen", "station_id": "Luftdaten Sensor ID" - }, - "title": "Luftdaten d\u00e9fin\u00e9ieren" + } } } } diff --git a/homeassistant/components/luftdaten/translations/nl.json b/homeassistant/components/luftdaten/translations/nl.json index 44ace40baa9..adfdb1101a0 100644 --- a/homeassistant/components/luftdaten/translations/nl.json +++ b/homeassistant/components/luftdaten/translations/nl.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Toon op kaart", "station_id": "Sensor ID" - }, - "title": "Definieer Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/no.json b/homeassistant/components/luftdaten/translations/no.json index 9ae681bcf91..bd5b6c97cda 100644 --- a/homeassistant/components/luftdaten/translations/no.json +++ b/homeassistant/components/luftdaten/translations/no.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Vis p\u00e5 kart", "station_id": "Sensor ID" - }, - "title": "" + } } } } diff --git a/homeassistant/components/luftdaten/translations/pl.json b/homeassistant/components/luftdaten/translations/pl.json index 7045a0a3dbe..03b004037c5 100644 --- a/homeassistant/components/luftdaten/translations/pl.json +++ b/homeassistant/components/luftdaten/translations/pl.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Poka\u017c na mapie", "station_id": "ID sensora" - }, - "title": "Konfiguracja Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/pt-BR.json b/homeassistant/components/luftdaten/translations/pt-BR.json index 82b1f09735b..877cc5d133c 100644 --- a/homeassistant/components/luftdaten/translations/pt-BR.json +++ b/homeassistant/components/luftdaten/translations/pt-BR.json @@ -8,10 +8,9 @@ "step": { "user": { "data": { - "show_on_map": "Mostrar no mapa", + "show_on_map": "[%key:component::iss::config::step::user::data::show_on_map%]", "station_id": "ID do Sensor Luftdaten" - }, - "title": "Definir Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/pt.json b/homeassistant/components/luftdaten/translations/pt.json index 5811494fffb..709ed6af0a1 100644 --- a/homeassistant/components/luftdaten/translations/pt.json +++ b/homeassistant/components/luftdaten/translations/pt.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Mostrar no mapa", "station_id": "Luftdaten Sensor ID" - }, - "title": "Definir Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/ru.json b/homeassistant/components/luftdaten/translations/ru.json index 82813ee3a53..cfb5ca68f93 100644 --- a/homeassistant/components/luftdaten/translations/ru.json +++ b/homeassistant/components/luftdaten/translations/ru.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043a\u0430\u0440\u0442\u0435", "station_id": "ID \u0434\u0430\u0442\u0447\u0438\u043a\u0430" - }, - "title": "Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/sl.json b/homeassistant/components/luftdaten/translations/sl.json index 249a0258536..7697abcf9e7 100644 --- a/homeassistant/components/luftdaten/translations/sl.json +++ b/homeassistant/components/luftdaten/translations/sl.json @@ -8,8 +8,7 @@ "data": { "show_on_map": "Prika\u017ei na zemljevidu", "station_id": "Luftdaten ID Senzorja" - }, - "title": "Dolo\u010dite Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/sv.json b/homeassistant/components/luftdaten/translations/sv.json index 52180093ba4..63de146c6d7 100644 --- a/homeassistant/components/luftdaten/translations/sv.json +++ b/homeassistant/components/luftdaten/translations/sv.json @@ -8,8 +8,7 @@ "data": { "show_on_map": "Visa p\u00e5 karta", "station_id": "Luftdaten Sensor-ID" - }, - "title": "Definiera Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/tr.json b/homeassistant/components/luftdaten/translations/tr.json index a74b35252c1..f8bbaf818d3 100644 --- a/homeassistant/components/luftdaten/translations/tr.json +++ b/homeassistant/components/luftdaten/translations/tr.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "Haritada g\u00f6ster", "station_id": "Sens\u00f6r Kimli\u011fi" - }, - "title": "Luftdaten'i tan\u0131mlay\u0131n" + } } } } diff --git a/homeassistant/components/luftdaten/translations/uk.json b/homeassistant/components/luftdaten/translations/uk.json index 9fd33dc3da2..d4cb21d89bb 100644 --- a/homeassistant/components/luftdaten/translations/uk.json +++ b/homeassistant/components/luftdaten/translations/uk.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u043d\u0430 \u043c\u0430\u043f\u0456", "station_id": "\u0406\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u043d\u0441\u043e\u0440\u0430 Luftdaten" - }, - "title": "Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/zh-Hans.json b/homeassistant/components/luftdaten/translations/zh-Hans.json index d66c44499df..193c2078bc1 100644 --- a/homeassistant/components/luftdaten/translations/zh-Hans.json +++ b/homeassistant/components/luftdaten/translations/zh-Hans.json @@ -9,8 +9,7 @@ "data": { "show_on_map": "\u5728\u5730\u56fe\u4e0a\u663e\u793a", "station_id": "Luftdaten \u4f20\u611f\u5668 ID" - }, - "title": "\u5b9a\u4e49 Luftdaten" + } } } } diff --git a/homeassistant/components/luftdaten/translations/zh-Hant.json b/homeassistant/components/luftdaten/translations/zh-Hant.json index ae9aa11da65..7fc2a8725e8 100644 --- a/homeassistant/components/luftdaten/translations/zh-Hant.json +++ b/homeassistant/components/luftdaten/translations/zh-Hant.json @@ -10,8 +10,7 @@ "data": { "show_on_map": "\u65bc\u5730\u5716\u986f\u793a", "station_id": "\u611f\u61c9\u5668 ID" - }, - "title": "\u5b9a\u7fa9 Luftdaten" + } } } } diff --git a/homeassistant/components/meater/translations/ko.json b/homeassistant/components/meater/translations/ko.json index b7fdeff3c2a..b414109dedc 100644 --- a/homeassistant/components/meater/translations/ko.json +++ b/homeassistant/components/meater/translations/ko.json @@ -1,10 +1,26 @@ { "config": { + "error": { + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "service_unavailable_error": "API\ub294 \ud604\uc7ac \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \uc8fc\uc138\uc694.", + "unknown_auth_error": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, "step": { "reauth_confirm": { "data": { "password": "\ube44\ubc00\ubc88\ud638" - } + }, + "description": "Meater Cloud \uacc4\uc815 {\uc0ac\uc6a9\uc790 \uc774\ub984}\uc5d0 \ub300\ud55c \uc554\ud638\ub97c \ud655\uc778\ud569\ub2c8\ub2e4." + }, + "user": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "data_description": { + "username": "Meater Cloud \uc0ac\uc6a9\uc790 \uc774\ub984, \uc77c\ubc18\uc801\uc73c\ub85c \uc774\uba54\uc77c \uc8fc\uc18c\uc785\ub2c8\ub2e4." + }, + "description": "Meater Cloud \uacc4\uc815\uc744 \uc124\uc815\ud569\ub2c8\ub2e4." } } } diff --git a/homeassistant/components/mill/translations/bg.json b/homeassistant/components/mill/translations/bg.json index 43611df6920..63c760b7bee 100644 --- a/homeassistant/components/mill/translations/bg.json +++ b/homeassistant/components/mill/translations/bg.json @@ -18,12 +18,6 @@ "ip_address": "IP \u0430\u0434\u0440\u0435\u0441" }, "description": "\u041b\u043e\u043a\u0430\u043b\u0435\u043d IP \u0430\u0434\u0440\u0435\u0441 \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e." - }, - "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - } } } } diff --git a/homeassistant/components/mill/translations/ca.json b/homeassistant/components/mill/translations/ca.json index ffb567fc218..ba350e217ac 100644 --- a/homeassistant/components/mill/translations/ca.json +++ b/homeassistant/components/mill/translations/ca.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Selecciona el tipus de connexi\u00f3", - "password": "Contrasenya", - "username": "Nom d'usuari" + "connection_type": "Selecciona el tipus de connexi\u00f3" }, "description": "Selecciona el tipus de connexi\u00f3. La local necessita escalfadors de generaci\u00f3 3" } diff --git a/homeassistant/components/mill/translations/cs.json b/homeassistant/components/mill/translations/cs.json index fcbdf3fb428..f6c47b4c840 100644 --- a/homeassistant/components/mill/translations/cs.json +++ b/homeassistant/components/mill/translations/cs.json @@ -5,14 +5,6 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" - }, - "step": { - "user": { - "data": { - "password": "Heslo", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/de.json b/homeassistant/components/mill/translations/de.json index 1688dddb321..6b8e912674a 100644 --- a/homeassistant/components/mill/translations/de.json +++ b/homeassistant/components/mill/translations/de.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Verbindungstyp ausw\u00e4hlen", - "password": "Passwort", - "username": "Benutzername" + "connection_type": "Verbindungstyp ausw\u00e4hlen" }, "description": "W\u00e4hle die Anschlussart. Lokal erfordert Heizger\u00e4te der Generation 3" } diff --git a/homeassistant/components/mill/translations/el.json b/homeassistant/components/mill/translations/el.json index 18743aada0a..dc5fb70915b 100644 --- a/homeassistant/components/mill/translations/el.json +++ b/homeassistant/components/mill/translations/el.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c4\u03cd\u03c0\u03bf\u03c5 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" + "connection_type": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c4\u03cd\u03c0\u03bf\u03c5 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03cd\u03c0\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2. \u0397 \u03c4\u03bf\u03c0\u03b9\u03ba\u03ae \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03b8\u03b5\u03c1\u03bc\u03b1\u03bd\u03c4\u03ae\u03c1\u03b5\u03c2 3\u03b7\u03c2 \u03b3\u03b5\u03bd\u03b9\u03ac\u03c2" } diff --git a/homeassistant/components/mill/translations/en.json b/homeassistant/components/mill/translations/en.json index 20291847893..ee66706832e 100644 --- a/homeassistant/components/mill/translations/en.json +++ b/homeassistant/components/mill/translations/en.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Select connection type", - "password": "Password", - "username": "Username" + "connection_type": "Select connection type" }, "description": "Select connection type. Local requires generation 3 heaters" } diff --git a/homeassistant/components/mill/translations/es.json b/homeassistant/components/mill/translations/es.json index 104c9d37d84..280d4ad4ba9 100644 --- a/homeassistant/components/mill/translations/es.json +++ b/homeassistant/components/mill/translations/es.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Seleccione el tipo de conexi\u00f3n", - "password": "Contrase\u00f1a", - "username": "Usuario" + "connection_type": "Seleccione el tipo de conexi\u00f3n" }, "description": "Seleccione el tipo de conexi\u00f3n. Local requiere calentadores de generaci\u00f3n 3" } diff --git a/homeassistant/components/mill/translations/et.json b/homeassistant/components/mill/translations/et.json index dad66420441..7b1be866b6a 100644 --- a/homeassistant/components/mill/translations/et.json +++ b/homeassistant/components/mill/translations/et.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Vali \u00fchenduse t\u00fc\u00fcp", - "password": "Salas\u00f5na", - "username": "Kasutajanimi" + "connection_type": "Vali \u00fchenduse t\u00fc\u00fcp" }, "description": "Vali \u00fchenduse t\u00fc\u00fcp. Kohalik vajab 3. p\u00f5lvkonna k\u00fctteseadmeid" } diff --git a/homeassistant/components/mill/translations/fi.json b/homeassistant/components/mill/translations/fi.json deleted file mode 100644 index 61febe9dd9c..00000000000 --- a/homeassistant/components/mill/translations/fi.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "password": "Salasana", - "username": "K\u00e4ytt\u00e4j\u00e4tunnus" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/mill/translations/fr.json b/homeassistant/components/mill/translations/fr.json index 440ef77ea0a..78b25cd079a 100644 --- a/homeassistant/components/mill/translations/fr.json +++ b/homeassistant/components/mill/translations/fr.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "S\u00e9lectionner le type de connexion", - "password": "Mot de passe", - "username": "Nom d'utilisateur" + "connection_type": "S\u00e9lectionner le type de connexion" }, "description": "S\u00e9lectionnez le type de connexion. Local n\u00e9cessite des radiateurs de g\u00e9n\u00e9ration 3" } diff --git a/homeassistant/components/mill/translations/he.json b/homeassistant/components/mill/translations/he.json index b551a319b2b..35c0b30e0de 100644 --- a/homeassistant/components/mill/translations/he.json +++ b/homeassistant/components/mill/translations/he.json @@ -17,12 +17,6 @@ "data": { "ip_address": "\u05db\u05ea\u05d5\u05d1\u05ea IP" } - }, - "user": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } } } } diff --git a/homeassistant/components/mill/translations/hu.json b/homeassistant/components/mill/translations/hu.json index 8a77a822fc8..38c3c96dbdb 100644 --- a/homeassistant/components/mill/translations/hu.json +++ b/homeassistant/components/mill/translations/hu.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t", - "password": "Jelsz\u00f3", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" + "connection_type": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t" }, "description": "V\u00e1lassza ki a kapcsolat t\u00edpus\u00e1t. A helyi kapcsolat 3. gener\u00e1ci\u00f3s f\u0171t\u0151berendez\u00e9seket ig\u00e9nyel" } diff --git a/homeassistant/components/mill/translations/id.json b/homeassistant/components/mill/translations/id.json index 92670be251a..ee5c548bdf6 100644 --- a/homeassistant/components/mill/translations/id.json +++ b/homeassistant/components/mill/translations/id.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Pilih jenis koneksi", - "password": "Kata Sandi", - "username": "Nama Pengguna" + "connection_type": "Pilih jenis koneksi" }, "description": "Pilih jenis koneksi. Lokal membutuhkan pemanas generasi 3" } diff --git a/homeassistant/components/mill/translations/it.json b/homeassistant/components/mill/translations/it.json index fa19ebc4cbd..5085817a6ca 100644 --- a/homeassistant/components/mill/translations/it.json +++ b/homeassistant/components/mill/translations/it.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Seleziona il tipo di connessione", - "password": "Password", - "username": "Nome utente" + "connection_type": "Seleziona il tipo di connessione" }, "description": "Seleziona il tipo di connessione. Locale richiede riscaldatori di terza generazione" } diff --git a/homeassistant/components/mill/translations/ja.json b/homeassistant/components/mill/translations/ja.json index 9250a503b58..8bb2ab5f0ea 100644 --- a/homeassistant/components/mill/translations/ja.json +++ b/homeassistant/components/mill/translations/ja.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u306e\u9078\u629e", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" + "connection_type": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u306e\u9078\u629e" }, "description": "\u63a5\u7d9a\u30bf\u30a4\u30d7\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30ed\u30fc\u30ab\u30eb\u306b\u306f\u7b2c3\u4e16\u4ee3\u306e\u30d2\u30fc\u30bf\u30fc\u304c\u5fc5\u8981\u3067\u3059" } diff --git a/homeassistant/components/mill/translations/ko.json b/homeassistant/components/mill/translations/ko.json index 1041727d84a..a38c2d98582 100644 --- a/homeassistant/components/mill/translations/ko.json +++ b/homeassistant/components/mill/translations/ko.json @@ -9,12 +9,6 @@ "step": { "local": { "description": "\ub85c\uceec IP \uc8fc\uc18c" - }, - "user": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } } } } diff --git a/homeassistant/components/mill/translations/lb.json b/homeassistant/components/mill/translations/lb.json index 84aeac8bd97..36841f1020f 100644 --- a/homeassistant/components/mill/translations/lb.json +++ b/homeassistant/components/mill/translations/lb.json @@ -5,14 +5,6 @@ }, "error": { "cannot_connect": "Feeler beim verbannen" - }, - "step": { - "user": { - "data": { - "password": "Passwuert", - "username": "Benotzernumm" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/nl.json b/homeassistant/components/mill/translations/nl.json index f37a5cf0758..1d431395b48 100644 --- a/homeassistant/components/mill/translations/nl.json +++ b/homeassistant/components/mill/translations/nl.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Selecteer verbindingstype", - "password": "Wachtwoord", - "username": "Gebruikersnaam" + "connection_type": "Selecteer verbindingstype" }, "description": "Selecteer verbindingstype. Lokaal vereist generatie 3 kachels" } diff --git a/homeassistant/components/mill/translations/no.json b/homeassistant/components/mill/translations/no.json index d5306fd090d..df51f7d73d3 100644 --- a/homeassistant/components/mill/translations/no.json +++ b/homeassistant/components/mill/translations/no.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Velg tilkoblingstype", - "password": "Passord", - "username": "Brukernavn" + "connection_type": "Velg tilkoblingstype" }, "description": "Velg tilkoblingstype. Lokal krever generasjon 3 varmeovner" } diff --git a/homeassistant/components/mill/translations/pl.json b/homeassistant/components/mill/translations/pl.json index 73abdedbfbc..b75a5da2df3 100644 --- a/homeassistant/components/mill/translations/pl.json +++ b/homeassistant/components/mill/translations/pl.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Wybierz typ po\u0142\u0105czenia", - "password": "Has\u0142o", - "username": "Nazwa u\u017cytkownika" + "connection_type": "Wybierz typ po\u0142\u0105czenia" }, "description": "Wybierz typ po\u0142\u0105czenia. Lokalnie wymaga grzejnik\u00f3w 3 generacji" } diff --git a/homeassistant/components/mill/translations/pt-BR.json b/homeassistant/components/mill/translations/pt-BR.json index 8d90531191a..73eb1808a1c 100644 --- a/homeassistant/components/mill/translations/pt-BR.json +++ b/homeassistant/components/mill/translations/pt-BR.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Selecione o tipo de conex\u00e3o", - "password": "Senha", - "username": "Usu\u00e1rio" + "connection_type": "Selecione o tipo de conex\u00e3o" }, "description": "Selecione o tipo de conex\u00e3o. Local requer aquecedores de 3\u00aa gera\u00e7\u00e3o" } diff --git a/homeassistant/components/mill/translations/pt.json b/homeassistant/components/mill/translations/pt.json index 4348cecf5c3..63c9b4e0011 100644 --- a/homeassistant/components/mill/translations/pt.json +++ b/homeassistant/components/mill/translations/pt.json @@ -5,14 +5,6 @@ }, "error": { "cannot_connect": "Falha na liga\u00e7\u00e3o" - }, - "step": { - "user": { - "data": { - "password": "Palavra-passe", - "username": "Nome de Utilizador" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/ru.json b/homeassistant/components/mill/translations/ru.json index ab09ec1fdaa..bac13e68163 100644 --- a/homeassistant/components/mill/translations/ru.json +++ b/homeassistant/components/mill/translations/ru.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" + "connection_type": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u043f\u0446\u0438\u0438 Local \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u043e\u0433\u0440\u0435\u0432\u0430\u0442\u0435\u043b\u044c \u0442\u0440\u0435\u0442\u044c\u0435\u0433\u043e \u043f\u043e\u043a\u043e\u043b\u0435\u043d\u0438\u044f." } diff --git a/homeassistant/components/mill/translations/sv.json b/homeassistant/components/mill/translations/sv.json deleted file mode 100644 index 23c825f256f..00000000000 --- a/homeassistant/components/mill/translations/sv.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "username": "Anv\u00e4ndarnamn" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/mill/translations/tr.json b/homeassistant/components/mill/translations/tr.json index 63ddc4ee7b1..1b75d70c51c 100644 --- a/homeassistant/components/mill/translations/tr.json +++ b/homeassistant/components/mill/translations/tr.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in", - "password": "Parola", - "username": "Kullan\u0131c\u0131 Ad\u0131" + "connection_type": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in" }, "description": "Ba\u011flant\u0131 t\u00fcr\u00fcn\u00fc se\u00e7in. Yerel, 3. nesil \u0131s\u0131t\u0131c\u0131lar gerektirir" } diff --git a/homeassistant/components/mill/translations/uk.json b/homeassistant/components/mill/translations/uk.json index b8a5aea578e..ceee910d966 100644 --- a/homeassistant/components/mill/translations/uk.json +++ b/homeassistant/components/mill/translations/uk.json @@ -5,14 +5,6 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" - }, - "step": { - "user": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/zh-Hans.json b/homeassistant/components/mill/translations/zh-Hans.json index 79079e6c408..2941dfd9383 100644 --- a/homeassistant/components/mill/translations/zh-Hans.json +++ b/homeassistant/components/mill/translations/zh-Hans.json @@ -2,13 +2,6 @@ "config": { "error": { "cannot_connect": "\u8fde\u63a5\u5931\u8d25" - }, - "step": { - "user": { - "data": { - "username": "\u7528\u6237\u540d" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/mill/translations/zh-Hant.json b/homeassistant/components/mill/translations/zh-Hant.json index 5c4745b23ae..cf8960a2a71 100644 --- a/homeassistant/components/mill/translations/zh-Hant.json +++ b/homeassistant/components/mill/translations/zh-Hant.json @@ -21,9 +21,7 @@ }, "user": { "data": { - "connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225", - "password": "\u5bc6\u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" + "connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225" }, "description": "\u9078\u64c7\u9023\u7dda\u985e\u5225\u3002\u672c\u5730\u7aef\u5c07\u9700\u8981\u7b2c\u4e09\u4ee3\u52a0\u71b1\u5668" } diff --git a/homeassistant/components/min_max/translations/ca.json b/homeassistant/components/min_max/translations/ca.json index b114e8ecf40..4bc9e69f182 100644 --- a/homeassistant/components/min_max/translations/ca.json +++ b/homeassistant/components/min_max/translations/ca.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Controla el nombre de d\u00edgits decimals quan la caracter\u00edstica estad\u00edstica \u00e9s la mitjana o la mediana." } - }, - "options": { - "data": { - "entity_ids": "Entitats d'entrada", - "round_digits": "Precisi\u00f3", - "type": "Caracter\u00edstica estad\u00edstica" - }, - "description": "Crea un sensor que calcula el m\u00ednim, m\u00e0xim, la mitjana o la mediana d'una llista de sensors d'entrada." } } }, diff --git a/homeassistant/components/min_max/translations/cs.json b/homeassistant/components/min_max/translations/cs.json index a969a39234a..181a5c93f14 100644 --- a/homeassistant/components/min_max/translations/cs.json +++ b/homeassistant/components/min_max/translations/cs.json @@ -20,14 +20,6 @@ "round_digits": "P\u0159esnost", "type": "Statistika" } - }, - "options": { - "data": { - "entity_ids": "Vstupn\u00ed entity", - "round_digits": "P\u0159esnost", - "type": "Statistika" - }, - "description": "P\u0159esnost ur\u010duje po\u010det desetinn\u00fdch m\u00edst, kdy\u017e je statistikou pr\u016fm\u011br nebo medi\u00e1n." } } } diff --git a/homeassistant/components/min_max/translations/de.json b/homeassistant/components/min_max/translations/de.json index c8b9d5ddf63..cb4ad7b9996 100644 --- a/homeassistant/components/min_max/translations/de.json +++ b/homeassistant/components/min_max/translations/de.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Steuert die Anzahl der Dezimalstellen in der Ausgabe, wenn das Statistikmerkmal Mittelwert oder Median ist." } - }, - "options": { - "data": { - "entity_ids": "Eingabe-Entit\u00e4ten", - "round_digits": "Genauigkeit", - "type": "Statistisches Merkmal" - }, - "description": "Erstelle einen Sensor, der einen Mindest-, H\u00f6chst-, Mittel- oder Medianwert aus einer Liste von Eingabesensoren berechnet." } } }, diff --git a/homeassistant/components/min_max/translations/el.json b/homeassistant/components/min_max/translations/el.json index 218c46fd683..53499682941 100644 --- a/homeassistant/components/min_max/translations/el.json +++ b/homeassistant/components/min_max/translations/el.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u0395\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03c3\u03c4\u03b7\u03bd \u03ad\u03be\u03bf\u03b4\u03bf \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bc\u03ad\u03c3\u03bf\u03c2 \u03ae \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." } - }, - "options": { - "data": { - "entity_ids": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b5\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", - "round_digits": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", - "type": "\u03a3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc" - }, - "description": "\u0397 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b5\u03bb\u03ad\u03b3\u03c7\u03b5\u03b9 \u03c4\u03bf\u03bd \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc \u03c4\u03c9\u03bd \u03b4\u03b5\u03ba\u03b1\u03b4\u03b9\u03ba\u03ce\u03bd \u03c8\u03b7\u03c6\u03af\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03c4\u03bf \u03c3\u03c4\u03b1\u03c4\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03b7\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b5\u03af\u03bd\u03b1\u03b9 \u03bf \u03bc\u03ad\u03c3\u03bf\u03c2 \u03cc\u03c1\u03bf\u03c2 \u03ae \u03b7 \u03b4\u03b9\u03ac\u03bc\u03b5\u03c3\u03bf\u03c2." } } }, diff --git a/homeassistant/components/min_max/translations/en.json b/homeassistant/components/min_max/translations/en.json index b5b6eb5d731..8cc0d41c419 100644 --- a/homeassistant/components/min_max/translations/en.json +++ b/homeassistant/components/min_max/translations/en.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean or median." } - }, - "options": { - "data": { - "entity_ids": "Input entities", - "round_digits": "Precision", - "type": "Statistic characteristic" - }, - "description": "Create a sensor that calculates a min, max, mean or median value from a list of input sensors." } } }, diff --git a/homeassistant/components/min_max/translations/es.json b/homeassistant/components/min_max/translations/es.json index ffe5217b1f9..aceaa287a3b 100644 --- a/homeassistant/components/min_max/translations/es.json +++ b/homeassistant/components/min_max/translations/es.json @@ -25,13 +25,6 @@ "data_description": { "round_digits": "Controla el n\u00famero de d\u00edgitos decimales cuando la caracter\u00edstica estad\u00edstica es la media o mediana." } - }, - "options": { - "data": { - "entity_ids": "Entidades de entrada", - "round_digits": "Precisi\u00f3n", - "type": "Caracter\u00edstica estad\u00edstica" - } } } }, diff --git a/homeassistant/components/min_max/translations/et.json b/homeassistant/components/min_max/translations/et.json index 2f1689e8577..c57b4a055a8 100644 --- a/homeassistant/components/min_max/translations/et.json +++ b/homeassistant/components/min_max/translations/et.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "M\u00e4\u00e4rab k\u00fcmnendkohtade arvu v\u00e4ljundis kui statistika tunnus on keskmine v\u00f5i mediaan." } - }, - "options": { - "data": { - "entity_ids": "Sisendolemid", - "round_digits": "T\u00e4psus", - "type": "Statistiline tunnus" - }, - "description": "T\u00e4psus kontrollib k\u00fcmnendnumbrite arvu kui statistika tunnus on keskmine v\u00f5i mediaan." } } }, diff --git a/homeassistant/components/min_max/translations/fr.json b/homeassistant/components/min_max/translations/fr.json index b42ccf0bb2a..562e7c7f735 100644 --- a/homeassistant/components/min_max/translations/fr.json +++ b/homeassistant/components/min_max/translations/fr.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Contr\u00f4le le nombre de chiffres d\u00e9cimaux de la sortie lorsque l'indicateur statistique est la moyenne ou la m\u00e9diane." } - }, - "options": { - "data": { - "entity_ids": "Entit\u00e9s d'entr\u00e9e", - "round_digits": "Pr\u00e9cision", - "type": "Indicateur statistique" - }, - "description": "Cr\u00e9ez un capteur qui calcule une valeur minimale, maximale, moyenne ou m\u00e9diane \u00e0 partir d'une liste de capteurs d'entr\u00e9e." } } }, diff --git a/homeassistant/components/min_max/translations/he.json b/homeassistant/components/min_max/translations/he.json index 267e38f0ce2..b9f2f18a4c7 100644 --- a/homeassistant/components/min_max/translations/he.json +++ b/homeassistant/components/min_max/translations/he.json @@ -24,13 +24,6 @@ "data_description": { "round_digits": "\u05e9\u05d5\u05dc\u05d8 \u05d1\u05de\u05e1\u05e4\u05e8 \u05d4\u05e1\u05e4\u05e8\u05d5\u05ea \u05d4\u05e2\u05e9\u05e8\u05d5\u05e0\u05d9\u05d5\u05ea \u05d1\u05e4\u05dc\u05d8 \u05db\u05d0\u05e9\u05e8 \u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05d4\u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4 \u05d4\u05d5\u05d0 \u05de\u05de\u05d5\u05e6\u05e2 \u05d0\u05d5 \u05d7\u05e6\u05d9\u05d5\u05e0\u05d9." } - }, - "options": { - "data": { - "entity_ids": "\u05d9\u05e9\u05d5\u05d9\u05d5\u05ea \u05e7\u05dc\u05d8", - "round_digits": "\u05d3\u05d9\u05d5\u05e7", - "type": "\u05de\u05d0\u05e4\u05d9\u05d9\u05df \u05e1\u05d8\u05d8\u05d9\u05e1\u05d8\u05d9\u05e7\u05d4" - } } } } diff --git a/homeassistant/components/min_max/translations/hu.json b/homeassistant/components/min_max/translations/hu.json index 98d021dbb31..7330df0d3e0 100644 --- a/homeassistant/components/min_max/translations/hu.json +++ b/homeassistant/components/min_max/translations/hu.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Szab\u00e1lyozza a tizedesjegyek sz\u00e1m\u00e1t, amikor a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." } - }, - "options": { - "data": { - "entity_ids": "Forr\u00e1s entit\u00e1sok", - "round_digits": "Pontoss\u00e1g", - "type": "Statisztikai jellemz\u0151" - }, - "description": "A pontoss\u00e1g a tizedesjegyek sz\u00e1m\u00e1t szab\u00e1lyozza, ha a statisztikai jellemz\u0151 az \u00e1tlag vagy a medi\u00e1n." } } }, diff --git a/homeassistant/components/min_max/translations/id.json b/homeassistant/components/min_max/translations/id.json index 32ccfd2596d..9e06b47de95 100644 --- a/homeassistant/components/min_max/translations/id.json +++ b/homeassistant/components/min_max/translations/id.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Mengontrol jumlah digit desimal dalam output ketika karakteristik statistik adalah rata-rata atau median." } - }, - "options": { - "data": { - "entity_ids": "Entitas input", - "round_digits": "Presisi", - "type": "Karakteristik statistik" - }, - "description": "Buat sensor yang menghitung nilai min, maks, rata-rata, atau median dari sejumlah sensor input." } } }, diff --git a/homeassistant/components/min_max/translations/it.json b/homeassistant/components/min_max/translations/it.json index 49c715a7507..4574b80e863 100644 --- a/homeassistant/components/min_max/translations/it.json +++ b/homeassistant/components/min_max/translations/it.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Controlla il numero di cifre decimali nell'output quando la caratteristica statistica \u00e8 media o mediana." } - }, - "options": { - "data": { - "entity_ids": "Entit\u00e0 di ingresso", - "round_digits": "Precisione", - "type": "Caratteristica statistica" - }, - "description": "Crea un sensore che calcoli un valore minimo, massimo, medio o mediano da un elenco di sensori di input." } } }, diff --git a/homeassistant/components/min_max/translations/ja.json b/homeassistant/components/min_max/translations/ja.json index c853a7c389e..f9e767082d6 100644 --- a/homeassistant/components/min_max/translations/ja.json +++ b/homeassistant/components/min_max/translations/ja.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u5024\u307e\u305f\u306f\u4e2d\u592e\u5024\u306e\u5834\u5408\u306b\u3001\u51fa\u529b\u306b\u542b\u307e\u308c\u308b\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" } - }, - "options": { - "data": { - "entity_ids": "\u5165\u529b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3", - "round_digits": "\u7cbe\u5ea6", - "type": "\u7d71\u8a08\u7684\u7279\u6027" - }, - "description": "\u7d71\u8a08\u7684\u7279\u6027\u304c\u5e73\u5747\u307e\u305f\u306f\u4e2d\u592e\u5024\u306a\u5834\u5408\u306e\u7cbe\u5ea6\u3067\u3001\u5c0f\u6570\u70b9\u4ee5\u4e0b\u306e\u6841\u6570\u3092\u5236\u5fa1\u3057\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/min_max/translations/ko.json b/homeassistant/components/min_max/translations/ko.json new file mode 100644 index 00000000000..3b9bcebca94 --- /dev/null +++ b/homeassistant/components/min_max/translations/ko.json @@ -0,0 +1,34 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_ids": "\uad6c\uc131\uc694\uc18c \uc785\ub825", + "name": "\uc774\ub984", + "round_digits": "\uc18c\uc218\uc810", + "type": "\ud1b5\uacc4\uc801 \ud2b9\uc131" + }, + "data_description": { + "round_digits": "\ud1b5\uacc4\uc801 \ud2b9\uc131\uc774 \ud3c9\uade0 \ub610\ub294 \uc911\uc559\uac12\uc77c \ub54c\uc758 \uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \uacb0\uc815\ud569\ub2c8\ub2e4." + }, + "description": "\ucd5c\uc18c, \ucd5c\ub300, \ud3c9\uade0 \ub610\ub294 \uc911\uc559\uac12\uc744 \uacc4\uc0b0\ud558\ub294 \uc13c\uc11c\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4.", + "title": "\ucd5c\uc18c/\ucd5c\ub300/\ud3c9\uade0/\uc911\uc559\uac12 \uc13c\uc11c \ucd94\uac00" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "entity_ids": "\uad6c\uc131\uc694\uc18c \uc785\ub825", + "round_digits": "\uc18c\uc218\uc810", + "type": "\ud1b5\uacc4\uc801 \ud2b9\uc131" + }, + "data_description": { + "round_digits": "\ud1b5\uacc4\uc801 \ud2b9\uc131\uc774 \ud3c9\uade0 \ub610\ub294 \uc911\uc559\uac12\uc77c \ub54c\uc758 \uc18c\uc218\uc810 \uc790\ub9bf\uc218\ub97c \uacb0\uc815\ud569\ub2c8\ub2e4." + } + } + } + }, + "title": "\ucd5c\uc18c / \ucd5c\ub300 / \ud3c9\uade0 / \uc911\uc559\uac12 \uc13c\uc11c" +} \ No newline at end of file diff --git a/homeassistant/components/min_max/translations/nl.json b/homeassistant/components/min_max/translations/nl.json index 36f09f97d54..75d8d69357b 100644 --- a/homeassistant/components/min_max/translations/nl.json +++ b/homeassistant/components/min_max/translations/nl.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Regelt het aantal decimale cijfers in de uitvoer wanneer de statistische eigenschap gemiddelde of mediaan is." } - }, - "options": { - "data": { - "entity_ids": "Invoer entiteiten", - "round_digits": "Precisie", - "type": "Statistische karakteristieken" - }, - "description": "Precisie bepaalt het aantal decimale cijfers wanneer het statistische kenmerk gemiddelde of mediaan is." } } }, diff --git a/homeassistant/components/min_max/translations/no.json b/homeassistant/components/min_max/translations/no.json index 798430cec03..5c9391b374f 100644 --- a/homeassistant/components/min_max/translations/no.json +++ b/homeassistant/components/min_max/translations/no.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Styrer antall desimaler i utdata n\u00e5r statistikkkarakteristikken er gjennomsnitt eller median." } - }, - "options": { - "data": { - "entity_ids": "Inndataenheter", - "round_digits": "Presisjon", - "type": "Statistisk karakteristikk" - }, - "description": "Lag en sensor som beregner en min, maks, middelverdi eller medianverdi fra en liste over inngangssensorer." } } }, diff --git a/homeassistant/components/min_max/translations/pl.json b/homeassistant/components/min_max/translations/pl.json index 0a29ad78339..cf4157eb612 100644 --- a/homeassistant/components/min_max/translations/pl.json +++ b/homeassistant/components/min_max/translations/pl.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Kontroluje liczb\u0119 cyfr dziesi\u0119tnych w danych wyj\u015bciowych, gdy charakterystyka statystyki jest \u015bredni\u0105 lub median\u0105." } - }, - "options": { - "data": { - "entity_ids": "Encje wej\u015bciowe", - "round_digits": "Precyzja", - "type": "Charakterystyka statystyczna" - }, - "description": "Utw\u00f3rz sensor, kt\u00f3ry oblicza warto\u015b\u0107 minimaln\u0105, maksymaln\u0105, \u015bredni\u0105 lub median\u0119 z listy sensor\u00f3w wej\u015bciowych." } } }, diff --git a/homeassistant/components/min_max/translations/pt-BR.json b/homeassistant/components/min_max/translations/pt-BR.json index 9c1c77c304a..5a2c9a85d4f 100644 --- a/homeassistant/components/min_max/translations/pt-BR.json +++ b/homeassistant/components/min_max/translations/pt-BR.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "Controla o n\u00famero de d\u00edgitos decimais na sa\u00edda quando a caracter\u00edstica estat\u00edstica \u00e9 m\u00e9dia ou mediana." } - }, - "options": { - "data": { - "entity_ids": "Entidades de entrada", - "round_digits": "Precis\u00e3o", - "type": "Caracter\u00edstica estat\u00edstica" - }, - "description": "Crie um sensor que calcula um valor m\u00ednimo, m\u00e1ximo, m\u00e9dio ou mediano de uma lista de sensores de entrada." } } }, diff --git a/homeassistant/components/min_max/translations/ru.json b/homeassistant/components/min_max/translations/ru.json index 551660fcabb..0fdd9198a72 100644 --- a/homeassistant/components/min_max/translations/ru.json +++ b/homeassistant/components/min_max/translations/ru.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u044f\u0442\u043e\u0439, \u043a\u043e\u0433\u0434\u0430 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u043d\u0435\u0439 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0439." } - }, - "options": { - "data": { - "entity_ids": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b", - "round_digits": "\u041e\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u0438\u0435", - "type": "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430" - }, - "description": "\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435, \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0438\u043b\u0438 \u043c\u0435\u0434\u0438\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432." } } }, diff --git a/homeassistant/components/min_max/translations/tr.json b/homeassistant/components/min_max/translations/tr.json index 7f192888615..ab826ed913f 100644 --- a/homeassistant/components/min_max/translations/tr.json +++ b/homeassistant/components/min_max/translations/tr.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u0130statistik \u00f6zelli\u011fi ortalama veya medyan oldu\u011funda \u00e7\u0131kt\u0131daki ondal\u0131k basamak say\u0131s\u0131n\u0131 kontrol eder." } - }, - "options": { - "data": { - "entity_ids": "Giri\u015f varl\u0131klar\u0131", - "round_digits": "Hassas", - "type": "\u0130statistik \u00f6zelli\u011fi" - }, - "description": "Giri\u015f sens\u00f6rleri listesinden minimum, maksimum, ortalama veya medyan de\u011feri hesaplayan bir sens\u00f6r olu\u015fturun." } } }, diff --git a/homeassistant/components/min_max/translations/zh-Hans.json b/homeassistant/components/min_max/translations/zh-Hans.json index 9fb15dafd2a..bd0562d2e48 100644 --- a/homeassistant/components/min_max/translations/zh-Hans.json +++ b/homeassistant/components/min_max/translations/zh-Hans.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u5f53\u7edf\u8ba1\u9879\u4e3a\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u65f6\uff0c\u63a7\u5236\u8f93\u51fa\u7684\u5c0f\u6570\u4f4d\u6570\u3002" } - }, - "options": { - "data": { - "entity_ids": "\u8f93\u5165\u5b9e\u4f53", - "round_digits": "\u7cbe\u5ea6", - "type": "\u7edf\u8ba1\u9879" - }, - "description": "\u521b\u5efa\u4f20\u611f\u5668\u6765\u8ba1\u7b97\u591a\u4e2a\u8f93\u5165\u4f20\u611f\u5668\u503c\u7684\u6700\u5927\u503c\u3001\u6700\u5c0f\u503c\u3001\u5e73\u5747\u503c\u6216\u4e2d\u4f4d\u6570\u3002" } } }, diff --git a/homeassistant/components/min_max/translations/zh-Hant.json b/homeassistant/components/min_max/translations/zh-Hant.json index b0166e26cac..abdf3fe737f 100644 --- a/homeassistant/components/min_max/translations/zh-Hant.json +++ b/homeassistant/components/min_max/translations/zh-Hant.json @@ -27,14 +27,6 @@ "data_description": { "round_digits": "\u7576\u7d71\u8a08\u7279\u5fb5\u70ba\u5e73\u5747\u503c\u6216\u4e2d\u503c\u6642\u3001\u63a7\u5236\u8f38\u51fa\u5c0f\u6578\u4f4d\u6578\u3002" } - }, - "options": { - "data": { - "entity_ids": "\u8f38\u5165\u5be6\u9ad4", - "round_digits": "\u6e96\u78ba\u5ea6", - "type": "\u7d71\u8a08\u7279\u5fb5" - }, - "description": "\u65b0\u589e\u81ea\u8f38\u5165\u611f\u6e2c\u5668\u4e86\u8868\u4e2d\uff0c\u8a08\u7b97\u6700\u4f4e\u3001\u6700\u9ad8\u3001\u5e73\u5747\u503c\u6216\u4e2d\u503c\u611f\u6e2c\u5668\u3002" } } }, diff --git a/homeassistant/components/modem_callerid/translations/bg.json b/homeassistant/components/modem_callerid/translations/bg.json index 7383941d1ca..15ece1452ad 100644 --- a/homeassistant/components/modem_callerid/translations/bg.json +++ b/homeassistant/components/modem_callerid/translations/bg.json @@ -7,15 +7,11 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { - "usb_confirm": { - "title": "\u0422\u0435\u043b\u0435\u0444\u043e\u043d\u0435\u043d \u043c\u043e\u0434\u0435\u043c" - }, "user": { "data": { "name": "\u0418\u043c\u0435", "port": "\u041f\u043e\u0440\u0442" - }, - "title": "\u0422\u0435\u043b\u0435\u0444\u043e\u043d\u0435\u043d \u043c\u043e\u0434\u0435\u043c" + } } } } diff --git a/homeassistant/components/modem_callerid/translations/ca.json b/homeassistant/components/modem_callerid/translations/ca.json index d94d4cf392d..996406acdbe 100644 --- a/homeassistant/components/modem_callerid/translations/ca.json +++ b/homeassistant/components/modem_callerid/translations/ca.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Integraci\u00f3 per a trucades fixes amb el m\u00f2dem de veu CX93001. Pot obtenir l'identificador del que truca i pot rebutjar trucades entrants.", - "title": "M\u00f2dem telef\u00f2nic" + "description": "Integraci\u00f3 per a trucades fixes amb el m\u00f2dem de veu CX93001. Pot obtenir l'identificador del que truca i pot rebutjar trucades entrants." }, "user": { "data": { "name": "Nom", "port": "Port" }, - "description": "Integraci\u00f3 per a trucades fixes amb el m\u00f2dem de veu CX93001. Pot obtenir l'identificador del que truca i pot rebutjar trucades entrants.", - "title": "M\u00f2dem telef\u00f2nic" + "description": "Integraci\u00f3 per a trucades fixes amb el m\u00f2dem de veu CX93001. Pot obtenir l'identificador del que truca i pot rebutjar trucades entrants." } } } diff --git a/homeassistant/components/modem_callerid/translations/de.json b/homeassistant/components/modem_callerid/translations/de.json index 0bc505be5c8..51056a601c3 100644 --- a/homeassistant/components/modem_callerid/translations/de.json +++ b/homeassistant/components/modem_callerid/translations/de.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Dies ist eine Integration f\u00fcr Festnetzanrufe mit einem CX93001 Sprachmodem. Damit k\u00f6nnen Anrufer-ID-Informationen mit einer Option zum Abweisen eines eingehenden Anrufs abgerufen werden.", - "title": "Telefonmodem" + "description": "Dies ist eine Integration f\u00fcr Festnetzanrufe mit einem CX93001 Sprachmodem. Damit k\u00f6nnen Anrufer-ID-Informationen mit einer Option zum Abweisen eines eingehenden Anrufs abgerufen werden." }, "user": { "data": { "name": "Name", "port": "Port" }, - "description": "Dies ist eine Integration f\u00fcr Festnetzanrufe mit einem CX93001 Sprachmodem. Damit k\u00f6nnen Anrufer-ID-Informationen mit einer Option zum Abweisen eines eingehenden Anrufs abgerufen werden.", - "title": "Telefonmodem" + "description": "Dies ist eine Integration f\u00fcr Festnetzanrufe mit einem CX93001 Sprachmodem. Damit k\u00f6nnen Anrufer-ID-Informationen mit einer Option zum Abweisen eines eingehenden Anrufs abgerufen werden." } } } diff --git a/homeassistant/components/modem_callerid/translations/el.json b/homeassistant/components/modem_callerid/translations/el.json index 6d3961cc258..cf2ef08b92e 100644 --- a/homeassistant/components/modem_callerid/translations/el.json +++ b/homeassistant/components/modem_callerid/translations/el.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "\u03a0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03ba\u03bb\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03c4\u03b7\u03bb\u03b5\u03c6\u03c9\u03bd\u03af\u03b1\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c6\u03c9\u03bd\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc CX93001. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ba\u03b1\u03bb\u03bf\u03cd\u03bd\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2 \u03bc\u03b9\u03b1\u03c2 \u03b5\u03b9\u03c3\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b7\u03c2 \u03ba\u03bb\u03ae\u03c3\u03b7\u03c2.", - "title": "\u039c\u03cc\u03bd\u03c4\u03b5\u03bc \u03c4\u03b7\u03bb\u03b5\u03c6\u03ce\u03bd\u03bf\u03c5" + "description": "\u03a0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03ba\u03bb\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03c4\u03b7\u03bb\u03b5\u03c6\u03c9\u03bd\u03af\u03b1\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c6\u03c9\u03bd\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc CX93001. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ba\u03b1\u03bb\u03bf\u03cd\u03bd\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2 \u03bc\u03b9\u03b1\u03c2 \u03b5\u03b9\u03c3\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b7\u03c2 \u03ba\u03bb\u03ae\u03c3\u03b7\u03c2." }, "user": { "data": { "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "port": "\u0398\u03cd\u03c1\u03b1" }, - "description": "\u03a0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03ba\u03bb\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03c4\u03b7\u03bb\u03b5\u03c6\u03c9\u03bd\u03af\u03b1\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c6\u03c9\u03bd\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc CX93001. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ba\u03b1\u03bb\u03bf\u03cd\u03bd\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2 \u03bc\u03b9\u03b1\u03c2 \u03b5\u03b9\u03c3\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b7\u03c2 \u03ba\u03bb\u03ae\u03c3\u03b7\u03c2.", - "title": "\u039c\u03cc\u03bd\u03c4\u03b5\u03bc \u03c4\u03b7\u03bb\u03b5\u03c6\u03ce\u03bd\u03bf\u03c5" + "description": "\u03a0\u03c1\u03cc\u03ba\u03b5\u03b9\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03ba\u03bb\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c3\u03c4\u03b1\u03b8\u03b5\u03c1\u03ae\u03c2 \u03c4\u03b7\u03bb\u03b5\u03c6\u03c9\u03bd\u03af\u03b1\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c6\u03c9\u03bd\u03b7\u03c4\u03b9\u03ba\u03bf\u03cd \u03bc\u03cc\u03bd\u03c4\u03b5\u03bc CX93001. \u0391\u03c5\u03c4\u03cc \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b1\u03bd\u03b1\u03ba\u03c4\u03ae\u03c3\u03b5\u03b9 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ba\u03b1\u03bb\u03bf\u03cd\u03bd\u03c4\u03bf\u03c2 \u03bc\u03b5 \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b1\u03c0\u03cc\u03c1\u03c1\u03b9\u03c8\u03b7\u03c2 \u03bc\u03b9\u03b1\u03c2 \u03b5\u03b9\u03c3\u03b5\u03c1\u03c7\u03cc\u03bc\u03b5\u03bd\u03b7\u03c2 \u03ba\u03bb\u03ae\u03c3\u03b7\u03c2." } } } diff --git a/homeassistant/components/modem_callerid/translations/en.json b/homeassistant/components/modem_callerid/translations/en.json index 5450a930ff3..1cad101f36d 100644 --- a/homeassistant/components/modem_callerid/translations/en.json +++ b/homeassistant/components/modem_callerid/translations/en.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "This is an integration for landline calls using a CX93001 voice modem. This can retrieve caller ID information with an option to reject an incoming call.", - "title": "Phone Modem" + "description": "This is an integration for landline calls using a CX93001 voice modem. This can retrieve caller ID information with an option to reject an incoming call." }, "user": { "data": { "name": "Name", "port": "Port" }, - "description": "This is an integration for landline calls using a CX93001 voice modem. This can retrieve caller ID information with an option to reject an incoming call.", - "title": "Phone Modem" + "description": "This is an integration for landline calls using a CX93001 voice modem. This can retrieve caller ID information with an option to reject an incoming call." } } } diff --git a/homeassistant/components/modem_callerid/translations/es.json b/homeassistant/components/modem_callerid/translations/es.json index 3a9853dd5f7..6c62f64a1d6 100644 --- a/homeassistant/components/modem_callerid/translations/es.json +++ b/homeassistant/components/modem_callerid/translations/es.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Se trata de una integraci\u00f3n para llamadas a tel\u00e9fonos fijos que utilizan un m\u00f3dem de voz CX93001. Puede recuperar la informaci\u00f3n del identificador de llamadas con una opci\u00f3n para rechazar una llamada entrante.", - "title": "M\u00f3dem telef\u00f3nico" + "description": "Se trata de una integraci\u00f3n para llamadas a tel\u00e9fonos fijos que utilizan un m\u00f3dem de voz CX93001. Puede recuperar la informaci\u00f3n del identificador de llamadas con una opci\u00f3n para rechazar una llamada entrante." }, "user": { "data": { "name": "Nombre", "port": "Puerto" }, - "description": "Se trata de una integraci\u00f3n para llamadas a tel\u00e9fonos fijos que utilizan un m\u00f3dem de voz CX93001. Puede recuperar la informaci\u00f3n del identificador de llamadas con una opci\u00f3n para rechazar una llamada entrante.", - "title": "M\u00f3dem telef\u00f3nico" + "description": "Se trata de una integraci\u00f3n para llamadas a tel\u00e9fonos fijos que utilizan un m\u00f3dem de voz CX93001. Puede recuperar la informaci\u00f3n del identificador de llamadas con una opci\u00f3n para rechazar una llamada entrante." } } } diff --git a/homeassistant/components/modem_callerid/translations/et.json b/homeassistant/components/modem_callerid/translations/et.json index 463d24e8f9f..10f80b53068 100644 --- a/homeassistant/components/modem_callerid/translations/et.json +++ b/homeassistant/components/modem_callerid/translations/et.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "See on sidumine fiksv\u00f5rgu telefonile kasutades CX93001 modemit. See v\u00f5ib hankida helistaja ID teabe koos sissetulevast k\u00f5nestloobumise v\u00f5imalusega.", - "title": "Telefoniliini modem" + "description": "See on sidumine fiksv\u00f5rgu telefonile kasutades CX93001 modemit. See v\u00f5ib hankida helistaja ID teabe koos sissetulevast k\u00f5nestloobumise v\u00f5imalusega." }, "user": { "data": { "name": "Nimi", "port": "Port" }, - "description": "See on sidumine fiksv\u00f5rgu telefonile kasutades CX93001 modemit. See v\u00f5ib hankida helistaja ID teabe koos sissetulevast k\u00f5nestloobumise v\u00f5imalusega.", - "title": "Telefoniliini modem" + "description": "See on sidumine fiksv\u00f5rgu telefonile kasutades CX93001 modemit. See v\u00f5ib hankida helistaja ID teabe koos sissetulevast k\u00f5nestloobumise v\u00f5imalusega." } } } diff --git a/homeassistant/components/modem_callerid/translations/fr.json b/homeassistant/components/modem_callerid/translations/fr.json index 696927d66db..36a07399836 100644 --- a/homeassistant/components/modem_callerid/translations/fr.json +++ b/homeassistant/components/modem_callerid/translations/fr.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Il s'agit d'une int\u00e9gration pour les appels fixes utilisant un modem vocal CX93001. Cela peut r\u00e9cup\u00e9rer les informations d'identification de l'appelant avec une option pour rejeter un appel entrant.", - "title": "Modem t\u00e9l\u00e9phonique" + "description": "Il s'agit d'une int\u00e9gration pour les appels fixes utilisant un modem vocal CX93001. Cela peut r\u00e9cup\u00e9rer les informations d'identification de l'appelant avec une option pour rejeter un appel entrant." }, "user": { "data": { "name": "Nom", "port": "Port" }, - "description": "Il s'agit d'une int\u00e9gration pour les appels fixes utilisant un modem vocal CX93001. Cela peut r\u00e9cup\u00e9rer les informations d'identification de l'appelant avec une option pour rejeter un appel entrant.", - "title": "Modem t\u00e9l\u00e9phonique" + "description": "Il s'agit d'une int\u00e9gration pour les appels fixes utilisant un modem vocal CX93001. Cela peut r\u00e9cup\u00e9rer les informations d'identification de l'appelant avec une option pour rejeter un appel entrant." } } } diff --git a/homeassistant/components/modem_callerid/translations/hu.json b/homeassistant/components/modem_callerid/translations/hu.json index 2a53c24d902..5e3f7727314 100644 --- a/homeassistant/components/modem_callerid/translations/hu.json +++ b/homeassistant/components/modem_callerid/translations/hu.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Ez egy integr\u00e1ci\u00f3 a CX93001 hangmodemmel t\u00f6rt\u00e9n\u0151 vezet\u00e9kes h\u00edv\u00e1sokhoz. Ez k\u00e9pes lek\u00e9rdezni a h\u00edv\u00f3azonos\u00edt\u00f3 inform\u00e1ci\u00f3t a bej\u00f6v\u0151 h\u00edv\u00e1s visszautas\u00edt\u00e1s\u00e1nak lehet\u0151s\u00e9g\u00e9vel.", - "title": "Telefon modem" + "description": "Ez egy integr\u00e1ci\u00f3 a CX93001 hangmodemmel t\u00f6rt\u00e9n\u0151 vezet\u00e9kes h\u00edv\u00e1sokhoz. Ez k\u00e9pes lek\u00e9rdezni a h\u00edv\u00f3azonos\u00edt\u00f3 inform\u00e1ci\u00f3t a bej\u00f6v\u0151 h\u00edv\u00e1s visszautas\u00edt\u00e1s\u00e1nak lehet\u0151s\u00e9g\u00e9vel." }, "user": { "data": { "name": "Elnevez\u00e9s", "port": "Port" }, - "description": "Ez egy integr\u00e1ci\u00f3 a CX93001 hangmodemmel t\u00f6rt\u00e9n\u0151 vezet\u00e9kes h\u00edv\u00e1sokhoz. Ez k\u00e9pes lek\u00e9rdezni a h\u00edv\u00f3azonos\u00edt\u00f3 inform\u00e1ci\u00f3t a bej\u00f6v\u0151 h\u00edv\u00e1s visszautas\u00edt\u00e1s\u00e1nak lehet\u0151s\u00e9g\u00e9vel.", - "title": "Telefon modem" + "description": "Ez egy integr\u00e1ci\u00f3 a CX93001 hangmodemmel t\u00f6rt\u00e9n\u0151 vezet\u00e9kes h\u00edv\u00e1sokhoz. Ez k\u00e9pes lek\u00e9rdezni a h\u00edv\u00f3azonos\u00edt\u00f3 inform\u00e1ci\u00f3t a bej\u00f6v\u0151 h\u00edv\u00e1s visszautas\u00edt\u00e1s\u00e1nak lehet\u0151s\u00e9g\u00e9vel." } } } diff --git a/homeassistant/components/modem_callerid/translations/id.json b/homeassistant/components/modem_callerid/translations/id.json index 3aa455d5f4c..accce6cea78 100644 --- a/homeassistant/components/modem_callerid/translations/id.json +++ b/homeassistant/components/modem_callerid/translations/id.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Integrasi ini adalah integrasi untuk panggilan darat menggunakan modem suara CX93001. Integrasi dapat mengambil informasi ID pemanggil dengan opsi untuk menolak panggilan masuk.", - "title": "Modem Telepon" + "description": "Integrasi ini adalah integrasi untuk panggilan darat menggunakan modem suara CX93001. Integrasi dapat mengambil informasi ID pemanggil dengan opsi untuk menolak panggilan masuk." }, "user": { "data": { "name": "Nama", "port": "Port" }, - "description": "Integrasi ini adalah integrasi untuk panggilan darat menggunakan modem suara CX93001. Integrasi dapat mengambil informasi ID pemanggil dengan opsi untuk menolak panggilan masuk.", - "title": "Modem Telepon" + "description": "Integrasi ini adalah integrasi untuk panggilan darat menggunakan modem suara CX93001. Integrasi dapat mengambil informasi ID pemanggil dengan opsi untuk menolak panggilan masuk." } } } diff --git a/homeassistant/components/modem_callerid/translations/it.json b/homeassistant/components/modem_callerid/translations/it.json index 65d1c74f956..1282541da5c 100644 --- a/homeassistant/components/modem_callerid/translations/it.json +++ b/homeassistant/components/modem_callerid/translations/it.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Questa \u00e8 un'integrazione per le chiamate su linea fissa che utilizza un modem vocale CX93001. Questo pu\u00f2 recuperare le informazioni sull'ID del chiamante con un'opzione per rifiutare una chiamata in arrivo.", - "title": "Modem del telefono" + "description": "Questa \u00e8 un'integrazione per le chiamate su linea fissa che utilizza un modem vocale CX93001. Questo pu\u00f2 recuperare le informazioni sull'ID del chiamante con un'opzione per rifiutare una chiamata in arrivo." }, "user": { "data": { "name": "Nome", "port": "Porta" }, - "description": "Questa \u00e8 un'integrazione per le chiamate su linea fissa che utilizza un modem vocale CX93001. Questo pu\u00f2 recuperare le informazioni sull'ID del chiamante con un'opzione per rifiutare una chiamata in arrivo.", - "title": "Modem del telefono" + "description": "Questa \u00e8 un'integrazione per le chiamate su linea fissa che utilizza un modem vocale CX93001. Questo pu\u00f2 recuperare le informazioni sull'ID del chiamante con un'opzione per rifiutare una chiamata in arrivo." } } } diff --git a/homeassistant/components/modem_callerid/translations/ja.json b/homeassistant/components/modem_callerid/translations/ja.json index ab5ab732931..a5b9df933ef 100644 --- a/homeassistant/components/modem_callerid/translations/ja.json +++ b/homeassistant/components/modem_callerid/translations/ja.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "\u3053\u308c\u306f\u3001CX93001\u97f3\u58f0\u30e2\u30c7\u30e0\u3092\u4f7f\u7528\u3057\u305f\u56fa\u5b9a\u96fb\u8a71\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3059\u3002\u767a\u4fe1\u8005\u756a\u53f7\u60c5\u5831\u3092\u53d6\u5f97\u3059\u308b\u3053\u3068\u3067\u3001\u7740\u4fe1\u3092\u62d2\u5426\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3082\u3042\u308a\u307e\u3059\u3002", - "title": "Phone\u30e2\u30c7\u30e0" + "description": "\u3053\u308c\u306f\u3001CX93001\u97f3\u58f0\u30e2\u30c7\u30e0\u3092\u4f7f\u7528\u3057\u305f\u56fa\u5b9a\u96fb\u8a71\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3059\u3002\u767a\u4fe1\u8005\u756a\u53f7\u60c5\u5831\u3092\u53d6\u5f97\u3059\u308b\u3053\u3068\u3067\u3001\u7740\u4fe1\u3092\u62d2\u5426\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3082\u3042\u308a\u307e\u3059\u3002" }, "user": { "data": { "name": "\u540d\u524d", "port": "\u30dd\u30fc\u30c8" }, - "description": "\u3053\u308c\u306f\u3001CX93001\u97f3\u58f0\u30e2\u30c7\u30e0\u3092\u4f7f\u7528\u3057\u305f\u56fa\u5b9a\u96fb\u8a71\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3059\u3002\u767a\u4fe1\u8005\u756a\u53f7\u60c5\u5831\u3092\u53d6\u5f97\u3059\u308b\u3053\u3068\u3067\u3001\u7740\u4fe1\u3092\u62d2\u5426\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3082\u3042\u308a\u307e\u3059\u3002", - "title": "Phone\u30e2\u30c7\u30e0" + "description": "\u3053\u308c\u306f\u3001CX93001\u97f3\u58f0\u30e2\u30c7\u30e0\u3092\u4f7f\u7528\u3057\u305f\u56fa\u5b9a\u96fb\u8a71\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u3059\u3002\u767a\u4fe1\u8005\u756a\u53f7\u60c5\u5831\u3092\u53d6\u5f97\u3059\u308b\u3053\u3068\u3067\u3001\u7740\u4fe1\u3092\u62d2\u5426\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3082\u3042\u308a\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/modem_callerid/translations/nl.json b/homeassistant/components/modem_callerid/translations/nl.json index 4077a03105b..6e26dd3d1c0 100644 --- a/homeassistant/components/modem_callerid/translations/nl.json +++ b/homeassistant/components/modem_callerid/translations/nl.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Dit is een integratie voor vaste telefoongesprekken met een CX93001 spraakmodem. Hiermee kan beller-ID informatie worden opgehaald met een optie om een inkomende oproep te weigeren.", - "title": "Telefoonmodem" + "description": "Dit is een integratie voor vaste telefoongesprekken met een CX93001 spraakmodem. Hiermee kan beller-ID informatie worden opgehaald met een optie om een inkomende oproep te weigeren." }, "user": { "data": { "name": "Naam", "port": "Poort" }, - "description": "Dit is een integratie voor vaste telefoongesprekken met een CX93001 spraakmodem. Hiermee kan beller-ID informatie worden opgehaald met een optie om een inkomende oproep te weigeren.", - "title": "Telefoonmodem" + "description": "Dit is een integratie voor vaste telefoongesprekken met een CX93001 spraakmodem. Hiermee kan beller-ID informatie worden opgehaald met een optie om een inkomende oproep te weigeren." } } } diff --git a/homeassistant/components/modem_callerid/translations/no.json b/homeassistant/components/modem_callerid/translations/no.json index 2e1103b5092..768d8a29ce3 100644 --- a/homeassistant/components/modem_callerid/translations/no.json +++ b/homeassistant/components/modem_callerid/translations/no.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Dette er en integrasjon for fasttelefonsamtaler ved hjelp av et talemodem CX93001. Dette kan hente oppringer -ID -informasjon med et alternativ for \u00e5 avvise et innkommende anrop.", - "title": "Telefonmodem" + "description": "Dette er en integrasjon for fasttelefonsamtaler ved hjelp av et talemodem CX93001. Dette kan hente oppringer -ID -informasjon med et alternativ for \u00e5 avvise et innkommende anrop." }, "user": { "data": { "name": "Navn", "port": "Port" }, - "description": "Dette er en integrasjon for fasttelefonsamtaler ved hjelp av et talemodem CX93001. Dette kan hente oppringer -ID -informasjon med et alternativ for \u00e5 avvise et innkommende anrop.", - "title": "Telefonmodem" + "description": "Dette er en integrasjon for fasttelefonsamtaler ved hjelp av et talemodem CX93001. Dette kan hente oppringer -ID -informasjon med et alternativ for \u00e5 avvise et innkommende anrop." } } } diff --git a/homeassistant/components/modem_callerid/translations/pl.json b/homeassistant/components/modem_callerid/translations/pl.json index 5101fc574a0..b608a467395 100644 --- a/homeassistant/components/modem_callerid/translations/pl.json +++ b/homeassistant/components/modem_callerid/translations/pl.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Jest to integracja dla po\u0142\u0105cze\u0144 stacjonarnych przy u\u017cyciu modemu g\u0142osowego CX93001. Mo\u017ce ona pobra\u0107 informacje o identyfikatorze dzwoni\u0105cego z opcj\u0105 odrzucenia po\u0142\u0105czenia przychodz\u0105cego.", - "title": "Modem telefoniczny" + "description": "Jest to integracja dla po\u0142\u0105cze\u0144 stacjonarnych przy u\u017cyciu modemu g\u0142osowego CX93001. Mo\u017ce ona pobra\u0107 informacje o identyfikatorze dzwoni\u0105cego z opcj\u0105 odrzucenia po\u0142\u0105czenia przychodz\u0105cego." }, "user": { "data": { "name": "Nazwa", "port": "Port" }, - "description": "Jest to integracja dla po\u0142\u0105cze\u0144 stacjonarnych przy u\u017cyciu modemu g\u0142osowego CX93001. Mo\u017ce ona pobra\u0107 informacje o identyfikatorze dzwoni\u0105cego z opcj\u0105 odrzucenia po\u0142\u0105czenia przychodz\u0105cego.", - "title": "Modem telefoniczny" + "description": "Jest to integracja dla po\u0142\u0105cze\u0144 stacjonarnych przy u\u017cyciu modemu g\u0142osowego CX93001. Mo\u017ce ona pobra\u0107 informacje o identyfikatorze dzwoni\u0105cego z opcj\u0105 odrzucenia po\u0142\u0105czenia przychodz\u0105cego." } } } diff --git a/homeassistant/components/modem_callerid/translations/pt-BR.json b/homeassistant/components/modem_callerid/translations/pt-BR.json index 39394d0752e..d82fbf8e5b6 100644 --- a/homeassistant/components/modem_callerid/translations/pt-BR.json +++ b/homeassistant/components/modem_callerid/translations/pt-BR.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Esta \u00e9 uma integra\u00e7\u00e3o para chamadas fixas usando um modem de voz CX93001. Isso pode recuperar informa\u00e7\u00f5es de identifica\u00e7\u00e3o de chamadas com a op\u00e7\u00e3o de rejeitar uma chamada recebida.", - "title": "Modem do telefone" + "description": "Esta \u00e9 uma integra\u00e7\u00e3o para chamadas fixas usando um modem de voz CX93001. Isso pode recuperar informa\u00e7\u00f5es de identifica\u00e7\u00e3o de chamadas com a op\u00e7\u00e3o de rejeitar uma chamada recebida." }, "user": { "data": { "name": "Nome", "port": "Porta" }, - "description": "Esta \u00e9 uma integra\u00e7\u00e3o para chamadas fixas usando um modem de voz CX93001. Isso pode recuperar informa\u00e7\u00f5es de identifica\u00e7\u00e3o de chamadas com a op\u00e7\u00e3o de rejeitar uma chamada recebida.", - "title": "Modem do telefone" + "description": "Esta \u00e9 uma integra\u00e7\u00e3o para chamadas fixas usando um modem de voz CX93001. Isso pode recuperar informa\u00e7\u00f5es de identifica\u00e7\u00e3o de chamadas com a op\u00e7\u00e3o de rejeitar uma chamada recebida." } } } diff --git a/homeassistant/components/modem_callerid/translations/ru.json b/homeassistant/components/modem_callerid/translations/ru.json index f5fa5061a4a..7217131b78f 100644 --- a/homeassistant/components/modem_callerid/translations/ru.json +++ b/homeassistant/components/modem_callerid/translations/ru.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u043c\u0430 CX93001 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u043f\u043e \u0441\u0442\u0430\u0446\u0438\u043e\u043d\u0430\u0440\u043d\u043e\u0439 \u043b\u0438\u043d\u0438\u0438. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0430\u0431\u043e\u043d\u0435\u043d\u0442\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.", - "title": "\u0422\u0435\u043b\u0435\u0444\u043e\u043d\u043d\u044b\u0439 \u043c\u043e\u0434\u0435\u043c" + "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u043c\u0430 CX93001 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u043f\u043e \u0441\u0442\u0430\u0446\u0438\u043e\u043d\u0430\u0440\u043d\u043e\u0439 \u043b\u0438\u043d\u0438\u0438. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0430\u0431\u043e\u043d\u0435\u043d\u0442\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430." }, "user": { "data": { "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "port": "\u041f\u043e\u0440\u0442" }, - "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u043c\u0430 CX93001 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u043f\u043e \u0441\u0442\u0430\u0446\u0438\u043e\u043d\u0430\u0440\u043d\u043e\u0439 \u043b\u0438\u043d\u0438\u0438. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0430\u0431\u043e\u043d\u0435\u043d\u0442\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.", - "title": "\u0422\u0435\u043b\u0435\u0444\u043e\u043d\u043d\u044b\u0439 \u043c\u043e\u0434\u0435\u043c" + "description": "\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u043c\u0430 CX93001 \u0434\u043b\u044f \u0437\u0432\u043e\u043d\u043a\u043e\u0432 \u043f\u043e \u0441\u0442\u0430\u0446\u0438\u043e\u043d\u0430\u0440\u043d\u043e\u0439 \u043b\u0438\u043d\u0438\u0438. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e \u0430\u0431\u043e\u043d\u0435\u043d\u0442\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u043b\u043e\u043d\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430." } } } diff --git a/homeassistant/components/modem_callerid/translations/tr.json b/homeassistant/components/modem_callerid/translations/tr.json index eec011d8c6f..373fe0d5e2f 100644 --- a/homeassistant/components/modem_callerid/translations/tr.json +++ b/homeassistant/components/modem_callerid/translations/tr.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "Bu, CX93001 sesli modem kullanan sabit hat aramalar\u0131 i\u00e7in bir entegrasyondur. Bu, gelen bir aramay\u0131 reddetme se\u00e7ene\u011fi ile arayan kimli\u011fi bilgilerini alabilir.", - "title": "Telefon Modemi" + "description": "Bu, CX93001 sesli modem kullanan sabit hat aramalar\u0131 i\u00e7in bir entegrasyondur. Bu, gelen bir aramay\u0131 reddetme se\u00e7ene\u011fi ile arayan kimli\u011fi bilgilerini alabilir." }, "user": { "data": { "name": "Ad", "port": "Port" }, - "description": "Bu, CX93001 sesli modem kullanan sabit hat aramalar\u0131 i\u00e7in bir entegrasyondur. Bu, gelen bir aramay\u0131 reddetme se\u00e7ene\u011fi ile arayan kimli\u011fi bilgilerini alabilir.", - "title": "Telefon Modemi" + "description": "Bu, CX93001 sesli modem kullanan sabit hat aramalar\u0131 i\u00e7in bir entegrasyondur. Bu, gelen bir aramay\u0131 reddetme se\u00e7ene\u011fi ile arayan kimli\u011fi bilgilerini alabilir." } } } diff --git a/homeassistant/components/modem_callerid/translations/zh-Hant.json b/homeassistant/components/modem_callerid/translations/zh-Hant.json index 542a12e8c5d..2d07c73cb1e 100644 --- a/homeassistant/components/modem_callerid/translations/zh-Hant.json +++ b/homeassistant/components/modem_callerid/translations/zh-Hant.json @@ -10,16 +10,14 @@ }, "step": { "usb_confirm": { - "description": "\u6b64\u6574\u5408\u4f7f\u7528 CX93001 \u8a9e\u97f3\u6578\u64da\u6a5f\u9032\u884c\u5e02\u8a71\u901a\u8a71\u3002\u53ef\u7528\u4ee5\u6aa2\u67e5\u4f86\u96fb ID \u8cc7\u8a0a\u3001\u4e26\u9032\u884c\u62d2\u63a5\u4f86\u96fb\u7684\u529f\u80fd\u3002", - "title": "\u624b\u6a5f\u6578\u64da\u6a5f" + "description": "\u6b64\u6574\u5408\u4f7f\u7528 CX93001 \u8a9e\u97f3\u6578\u64da\u6a5f\u9032\u884c\u5e02\u8a71\u901a\u8a71\u3002\u53ef\u7528\u4ee5\u6aa2\u67e5\u4f86\u96fb ID \u8cc7\u8a0a\u3001\u4e26\u9032\u884c\u62d2\u63a5\u4f86\u96fb\u7684\u529f\u80fd\u3002" }, "user": { "data": { "name": "\u540d\u7a31", "port": "\u901a\u8a0a\u57e0" }, - "description": "\u6b64\u6574\u5408\u4f7f\u7528 CX93001 \u8a9e\u97f3\u6578\u64da\u6a5f\u9032\u884c\u5e02\u8a71\u901a\u8a71\u3002\u53ef\u7528\u4ee5\u6aa2\u67e5\u4f86\u96fb ID \u8cc7\u8a0a\u3001\u4e26\u9032\u884c\u62d2\u63a5\u4f86\u96fb\u7684\u529f\u80fd\u3002", - "title": "\u624b\u6a5f\u6578\u64da\u6a5f" + "description": "\u6b64\u6574\u5408\u4f7f\u7528 CX93001 \u8a9e\u97f3\u6578\u64da\u6a5f\u9032\u884c\u5e02\u8a71\u901a\u8a71\u3002\u53ef\u7528\u4ee5\u6aa2\u67e5\u4f86\u96fb ID \u8cc7\u8a0a\u3001\u4e26\u9032\u884c\u62d2\u63a5\u4f86\u96fb\u7684\u529f\u80fd\u3002" } } } diff --git a/homeassistant/components/modern_forms/translations/bg.json b/homeassistant/components/modern_forms/translations/bg.json index 4200524546f..d2124752f04 100644 --- a/homeassistant/components/modern_forms/translations/bg.json +++ b/homeassistant/components/modern_forms/translations/bg.json @@ -9,15 +9,11 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?" - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" } } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/ca.json b/homeassistant/components/modern_forms/translations/ca.json index e7a70e80b93..4296368ac89 100644 --- a/homeassistant/components/modern_forms/translations/ca.json +++ b/homeassistant/components/modern_forms/translations/ca.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Vols comen\u00e7ar la configuraci\u00f3?" - }, "user": { "data": { "host": "Amfitri\u00f3" @@ -23,6 +20,5 @@ "title": "Dispositiu ventilador Modern Forms descobert" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/de.json b/homeassistant/components/modern_forms/translations/de.json index bf16c9532fc..3c9280b22f3 100644 --- a/homeassistant/components/modern_forms/translations/de.json +++ b/homeassistant/components/modern_forms/translations/de.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Erkannter Modern Forms Ventilator" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/el.json b/homeassistant/components/modern_forms/translations/el.json index b8cb77ffa99..10811327076 100644 --- a/homeassistant/components/modern_forms/translations/el.json +++ b/homeassistant/components/modern_forms/translations/el.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" - }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" @@ -23,6 +20,5 @@ "title": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b1\u03bd\u03b5\u03bc\u03b9\u03c3\u03c4\u03ae\u03c1\u03c9\u03bd Modern Forms" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/en.json b/homeassistant/components/modern_forms/translations/en.json index 6c51e42a2d1..f61cd67cfde 100644 --- a/homeassistant/components/modern_forms/translations/en.json +++ b/homeassistant/components/modern_forms/translations/en.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Do you want to start set up?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Discovered Modern Forms fan device" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/es.json b/homeassistant/components/modern_forms/translations/es.json index 4512c0bc595..29b51c03cf1 100644 --- a/homeassistant/components/modern_forms/translations/es.json +++ b/homeassistant/components/modern_forms/translations/es.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u00bfQuieres iniciar la configuraci\u00f3n?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Dispositivo de ventilador de Modern Forms descubierto" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/et.json b/homeassistant/components/modern_forms/translations/et.json index 7140888db84..155c081f67c 100644 --- a/homeassistant/components/modern_forms/translations/et.json +++ b/homeassistant/components/modern_forms/translations/et.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Kas soovid alustada seadistamist?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Leitud Modern Forms ventilaator" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/fr.json b/homeassistant/components/modern_forms/translations/fr.json index d68f4a7f680..85b7681057a 100644 --- a/homeassistant/components/modern_forms/translations/fr.json +++ b/homeassistant/components/modern_forms/translations/fr.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Voulez-vous commencer la configuration\u00a0?" - }, "user": { "data": { "host": "H\u00f4te" @@ -23,6 +20,5 @@ "title": "D\u00e9couverte du dispositif de ventilateur Modern Forms" } } - }, - "title": "Formes modernes" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/he.json b/homeassistant/components/modern_forms/translations/he.json index 701a3689598..1cd249b4daa 100644 --- a/homeassistant/components/modern_forms/translations/he.json +++ b/homeassistant/components/modern_forms/translations/he.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" - }, "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7" diff --git a/homeassistant/components/modern_forms/translations/hu.json b/homeassistant/components/modern_forms/translations/hu.json index 49f5da5339f..a4ef399188c 100644 --- a/homeassistant/components/modern_forms/translations/hu.json +++ b/homeassistant/components/modern_forms/translations/hu.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" - }, "user": { "data": { "host": "C\u00edm" @@ -23,6 +20,5 @@ "title": "Felfedezte a Modern Forms rajong\u00f3i eszk\u00f6zt" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/id.json b/homeassistant/components/modern_forms/translations/id.json index c85667c4449..703ffce37be 100644 --- a/homeassistant/components/modern_forms/translations/id.json +++ b/homeassistant/components/modern_forms/translations/id.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Ingin memulai penyiapan?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Perangkat kipas Modern Forms yang Ditemukan" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/it.json b/homeassistant/components/modern_forms/translations/it.json index 18f1d5f503a..8feeab0a058 100644 --- a/homeassistant/components/modern_forms/translations/it.json +++ b/homeassistant/components/modern_forms/translations/it.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Vuoi iniziare la configurazione?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Rilevato il dispositivo ventilatore di Modern Forms" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/ja.json b/homeassistant/components/modern_forms/translations/ja.json index 7806675b5c6..064f4b8f4cd 100644 --- a/homeassistant/components/modern_forms/translations/ja.json +++ b/homeassistant/components/modern_forms/translations/ja.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" - }, "user": { "data": { "host": "\u30db\u30b9\u30c8" @@ -23,6 +20,5 @@ "title": "Modern Forms fan device\u3092\u767a\u898b" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/nl.json b/homeassistant/components/modern_forms/translations/nl.json index ccbdf7d5b44..ecb65db17d8 100644 --- a/homeassistant/components/modern_forms/translations/nl.json +++ b/homeassistant/components/modern_forms/translations/nl.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Wilt u beginnen met instellen?" - }, "user": { "data": { "host": "Host" @@ -23,6 +20,5 @@ "title": "Ontdekt Modern Forms ventilator apparaat" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/no.json b/homeassistant/components/modern_forms/translations/no.json index e7da915d39b..a96d1617117 100644 --- a/homeassistant/components/modern_forms/translations/no.json +++ b/homeassistant/components/modern_forms/translations/no.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Vil du starte oppsettet?" - }, "user": { "data": { "host": "Vert" @@ -23,6 +20,5 @@ "title": "Oppdaget Modern Forms-vifteenhet" } } - }, - "title": "Moderne former" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/pl.json b/homeassistant/components/modern_forms/translations/pl.json index f68be10b7cd..8437f00f2f8 100644 --- a/homeassistant/components/modern_forms/translations/pl.json +++ b/homeassistant/components/modern_forms/translations/pl.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" - }, "user": { "data": { "host": "Nazwa hosta lub adres IP" @@ -23,6 +20,5 @@ "title": "Wykryto wentylator Modern Forms" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/pt-BR.json b/homeassistant/components/modern_forms/translations/pt-BR.json index 07a68e2fb84..63b5f7e12f5 100644 --- a/homeassistant/components/modern_forms/translations/pt-BR.json +++ b/homeassistant/components/modern_forms/translations/pt-BR.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Deseja iniciar a configura\u00e7\u00e3o?" - }, "user": { "data": { "host": "Nome do host" @@ -23,6 +20,5 @@ "title": "Dispositivo de ventilador do Modern Forms descoberto" } } - }, - "title": "Formas modernas" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/ru.json b/homeassistant/components/modern_forms/translations/ru.json index 68b46c57f47..86f2080a90f 100644 --- a/homeassistant/components/modern_forms/translations/ru.json +++ b/homeassistant/components/modern_forms/translations/ru.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442" @@ -23,6 +20,5 @@ "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Modern Forms" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/tr.json b/homeassistant/components/modern_forms/translations/tr.json index e27881802fb..d80e905e17a 100644 --- a/homeassistant/components/modern_forms/translations/tr.json +++ b/homeassistant/components/modern_forms/translations/tr.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "Kuruluma ba\u015flamak ister misiniz?" - }, "user": { "data": { "host": "Sunucu" @@ -23,6 +20,5 @@ "title": "Ke\u015ffedilen Modern Formlar fan cihaz\u0131" } } - }, - "title": "Modern Formlar" + } } \ No newline at end of file diff --git a/homeassistant/components/modern_forms/translations/zh-Hant.json b/homeassistant/components/modern_forms/translations/zh-Hant.json index df3ebf486c7..054ad19ee1a 100644 --- a/homeassistant/components/modern_forms/translations/zh-Hant.json +++ b/homeassistant/components/modern_forms/translations/zh-Hant.json @@ -9,9 +9,6 @@ }, "flow_title": "{name}", "step": { - "confirm": { - "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" - }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef" @@ -23,6 +20,5 @@ "title": "\u6240\u767c\u73fe\u7684\u88dd\u7f6e\u4e26\u975e Modern Forms \u98a8\u6247\u88dd\u7f6e" } } - }, - "title": "Modern Forms" + } } \ No newline at end of file diff --git a/homeassistant/components/moon/translations/ko.json b/homeassistant/components/moon/translations/ko.json new file mode 100644 index 00000000000..758f3336cd4 --- /dev/null +++ b/homeassistant/components/moon/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/bg.json b/homeassistant/components/motion_blinds/translations/bg.json index dad41cb1999..e78d7032040 100644 --- a/homeassistant/components/motion_blinds/translations/bg.json +++ b/homeassistant/components/motion_blinds/translations/bg.json @@ -4,9 +4,6 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", "connection_error": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, - "error": { - "invalid_interface": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u043c\u0440\u0435\u0436\u043e\u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441" - }, "step": { "connect": { "data": { @@ -20,7 +17,6 @@ }, "user": { "data": { - "api_key": "API \u043a\u043b\u044e\u0447", "host": "IP \u0430\u0434\u0440\u0435\u0441" } } diff --git a/homeassistant/components/motion_blinds/translations/ca.json b/homeassistant/components/motion_blinds/translations/ca.json index 4ec912aa25c..8de2711298b 100644 --- a/homeassistant/components/motion_blinds/translations/ca.json +++ b/homeassistant/components/motion_blinds/translations/ca.json @@ -6,18 +6,15 @@ "connection_error": "Ha fallat la connexi\u00f3" }, "error": { - "discovery_error": "No s'ha pogut descobrir cap Motion Gateway", - "invalid_interface": "Interf\u00edcie de xarxa no v\u00e0lida" + "discovery_error": "No s'ha pogut descobrir cap Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Clau API", - "interface": "Interf\u00edcie de xarxa a utilitzar" + "api_key": "Clau API" }, - "description": "Necessitar\u00e0s la clau API de 16 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key.", - "title": "Motion Blinds" + "description": "Necessitar\u00e0s la clau API de 16 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key." }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Clau API", "host": "Adre\u00e7a IP" }, - "description": "Connecta el teu Motion Gateway, si no es configura l'adre\u00e7a IP, s'utilitza el descobriment autom\u00e0tic", - "title": "Motion Blinds" + "description": "Connecta el teu Motion Gateway, si no es configura l'adre\u00e7a IP, s'utilitza el descobriment autom\u00e0tic" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Espera l'entrada multidifusi\u00f3 en actualitzar" - }, - "description": "Especifica configuracions opcionals", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/cs.json b/homeassistant/components/motion_blinds/translations/cs.json index 899f04d7cd4..be4dd0eb8bf 100644 --- a/homeassistant/components/motion_blinds/translations/cs.json +++ b/homeassistant/components/motion_blinds/translations/cs.json @@ -19,10 +19,8 @@ }, "user": { "data": { - "api_key": "Kl\u00ed\u010d API", "host": "IP adresa" - }, - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/de.json b/homeassistant/components/motion_blinds/translations/de.json index d3b6b219516..b483ea35dd5 100644 --- a/homeassistant/components/motion_blinds/translations/de.json +++ b/homeassistant/components/motion_blinds/translations/de.json @@ -6,18 +6,15 @@ "connection_error": "Verbindung fehlgeschlagen" }, "error": { - "discovery_error": "Motion-Gateway konnte nicht gefunden werden", - "invalid_interface": "Ung\u00fcltige Netzwerkschnittstelle" + "discovery_error": "Motion-Gateway konnte nicht gefunden werden" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API-Schl\u00fcssel", - "interface": "Die zu verwendende Netzwerkschnittstelle" + "api_key": "API-Schl\u00fcssel" }, - "description": "Ein 16-Zeichen-API-Schl\u00fcssel wird ben\u00f6tigt, siehe https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "Motion Jalousien" + "description": "Ein 16-Zeichen-API-Schl\u00fcssel wird ben\u00f6tigt, siehe https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API-Schl\u00fcssel", "host": "IP-Adresse" }, - "description": "Stelle eine Verbindung zu deinem Motion Gateway her. Wenn die IP-Adresse leer bleibt, wird die automatische Erkennung verwendet", - "title": "Jalousien" + "description": "Stelle eine Verbindung zu deinem Motion Gateway her. Wenn die IP-Adresse leer bleibt, wird die automatische Erkennung verwendet" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Warten auf Multicast-Push bei Aktualisierung" - }, - "description": "Optionale Einstellungen angeben", - "title": "Motion Jalousien" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/el.json b/homeassistant/components/motion_blinds/translations/el.json index 2914382e820..b9d67703c57 100644 --- a/homeassistant/components/motion_blinds/translations/el.json +++ b/homeassistant/components/motion_blinds/translations/el.json @@ -6,18 +6,15 @@ "connection_error": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "error": { - "discovery_error": "\u0391\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 \u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2", - "invalid_interface": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5" + "discovery_error": "\u0391\u03c0\u03ad\u03c4\u03c5\u03c7\u03b5 \u03bd\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03af\u03c3\u03b5\u03b9 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 \u03ba\u03af\u03bd\u03b7\u03c3\u03b7\u03c2" }, "flow_title": "Motion Blinds", "step": { "connect": { "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "interface": "\u0397 \u03b4\u03b9\u03b1\u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af" + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" }, - "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API 16 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd, \u03b4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2.", - "title": "Motion Blinds" + "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API 16 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd, \u03b4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2." }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" }, - "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf Motion Gateway, \u03b5\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7", - "title": "Motion Blinds" + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf Motion Gateway, \u03b5\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03bf\u03c1\u03b9\u03c3\u03c4\u03b5\u03af \u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "\u0391\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 multicast push \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7" - }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ce\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/en.json b/homeassistant/components/motion_blinds/translations/en.json index 7a6d6763fdc..33784e1d386 100644 --- a/homeassistant/components/motion_blinds/translations/en.json +++ b/homeassistant/components/motion_blinds/translations/en.json @@ -6,18 +6,15 @@ "connection_error": "Failed to connect" }, "error": { - "discovery_error": "Failed to discover a Motion Gateway", - "invalid_interface": "Invalid network interface" + "discovery_error": "Failed to discover a Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API Key", - "interface": "The network interface to use" + "api_key": "API Key" }, - "description": "You will need the 16 character API Key, see https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key for instructions", - "title": "Motion Blinds" + "description": "You will need the 16 character API Key, see https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key for instructions" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API Key", "host": "IP Address" }, - "description": "Connect to your Motion Gateway, if the IP address is not set, auto-discovery is used", - "title": "Motion Blinds" + "description": "Connect to your Motion Gateway, if the IP address is not set, auto-discovery is used" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Wait for multicast push on update" - }, - "description": "Specify optional settings", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/es.json b/homeassistant/components/motion_blinds/translations/es.json index 0c364f394ef..1a4312d4fe0 100644 --- a/homeassistant/components/motion_blinds/translations/es.json +++ b/homeassistant/components/motion_blinds/translations/es.json @@ -6,18 +6,15 @@ "connection_error": "No se pudo conectar" }, "error": { - "discovery_error": "No se pudo descubrir un detector de movimiento", - "invalid_interface": "Interfaz de red inv\u00e1lida" + "discovery_error": "No se pudo descubrir un detector de movimiento" }, "flow_title": "Motion Blinds", "step": { "connect": { "data": { - "api_key": "Clave API", - "interface": "La interfaz de la red a usar" + "api_key": "Clave API" }, - "description": "Necesitar\u00e1 la clave de API de 16 caracteres, consulte https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key para obtener instrucciones", - "title": "Estores motorizados" + "description": "Necesitar\u00e1 la clave de API de 16 caracteres, consulte https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key para obtener instrucciones" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Clave API", "host": "Direcci\u00f3n IP" }, - "description": "Con\u00e9ctate a tu Motion Gateway, si la direcci\u00f3n IP no est\u00e1 establecida, se utilitzar\u00e1 la detecci\u00f3n autom\u00e1tica", - "title": "Motion Blinds" + "description": "Con\u00e9ctate a tu Motion Gateway, si la direcci\u00f3n IP no est\u00e1 establecida, se utilitzar\u00e1 la detecci\u00f3n autom\u00e1tica" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Espere a que se realice la actualizaci\u00f3n de multidifusi\u00f3n" - }, - "description": "Especifica los ajustes opcionales", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/et.json b/homeassistant/components/motion_blinds/translations/et.json index 715208cd9f4..eb1fecf1118 100644 --- a/homeassistant/components/motion_blinds/translations/et.json +++ b/homeassistant/components/motion_blinds/translations/et.json @@ -6,18 +6,15 @@ "connection_error": "\u00dchendamine nurjus" }, "error": { - "discovery_error": "Motion Gateway avastamine nurjus", - "invalid_interface": "Sobimatu v\u00f5rguliides" + "discovery_error": "Motion Gateway avastamine nurjus" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API v\u00f5ti", - "interface": "Kasutatav v\u00f5rguliides" + "api_key": "API v\u00f5ti" }, - "description": "On vaja 16-kohalist API-v\u00f5tit, juhiste saamiseks vaata https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "" + "description": "On vaja 16-kohalist API-v\u00f5tit, juhiste saamiseks vaata https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API v\u00f5ti", "host": "IP-aadress" }, - "description": "\u00dchenda oma Motion Gatewayga. Kui IP-aadress on m\u00e4\u00e4ramata kasutatakse automaatset avastamist", - "title": "" + "description": "\u00dchenda oma Motion Gatewayga. Kui IP-aadress on m\u00e4\u00e4ramata kasutatakse automaatset avastamist" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Oota multicast'i t\u00f5ukev\u00e4rskendust" - }, - "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/fr.json b/homeassistant/components/motion_blinds/translations/fr.json index 17df2654026..c22ac830ebe 100644 --- a/homeassistant/components/motion_blinds/translations/fr.json +++ b/homeassistant/components/motion_blinds/translations/fr.json @@ -6,18 +6,15 @@ "connection_error": "\u00c9chec de connexion" }, "error": { - "discovery_error": "Impossible de d\u00e9couvrir une Motion Gateway", - "invalid_interface": "Interface r\u00e9seau non valide" + "discovery_error": "Impossible de d\u00e9couvrir une Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Cl\u00e9 d'API", - "interface": "Interface r\u00e9seau \u00e0 utiliser" + "api_key": "Cl\u00e9 d'API" }, - "description": "Vous aurez besoin de la cl\u00e9 API de 16 caract\u00e8res, voir https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key pour les instructions", - "title": "Stores de mouvement" + "description": "Vous aurez besoin de la cl\u00e9 API de 16 caract\u00e8res, voir https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key pour les instructions" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Cl\u00e9 d'API", "host": "Adresse IP" }, - "description": "Connectez-vous \u00e0 votre Motion Gateway, si l'adresse IP n'est pas d\u00e9finie, la d\u00e9tection automatique est utilis\u00e9e", - "title": "Stores de mouvement" + "description": "Connectez-vous \u00e0 votre Motion Gateway, si l'adresse IP n'est pas d\u00e9finie, la d\u00e9tection automatique est utilis\u00e9e" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Attendre la mise \u00e0 jour de la diffusion group\u00e9e" - }, - "description": "Sp\u00e9cifiez les param\u00e8tres optionnels", - "title": "Store motoris\u00e9" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/he.json b/homeassistant/components/motion_blinds/translations/he.json index ce6f3d2918e..3cf199985e5 100644 --- a/homeassistant/components/motion_blinds/translations/he.json +++ b/homeassistant/components/motion_blinds/translations/he.json @@ -18,7 +18,6 @@ }, "user": { "data": { - "api_key": "\u05de\u05e4\u05ea\u05d7 API", "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP" } } diff --git a/homeassistant/components/motion_blinds/translations/hu.json b/homeassistant/components/motion_blinds/translations/hu.json index 64334c54a28..8ab58169d05 100644 --- a/homeassistant/components/motion_blinds/translations/hu.json +++ b/homeassistant/components/motion_blinds/translations/hu.json @@ -6,18 +6,15 @@ "connection_error": "Sikertelen csatlakoz\u00e1s" }, "error": { - "discovery_error": "Nem siker\u00fclt felfedezni a Motion Gateway-t", - "invalid_interface": "\u00c9rv\u00e9nytelen h\u00e1l\u00f3zati interf\u00e9sz" + "discovery_error": "Nem siker\u00fclt felfedezni a Motion Gateway-t" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API kulcs", - "interface": "A haszn\u00e1lni k\u00edv\u00e1nt h\u00e1l\u00f3zati interf\u00e9sz" + "api_key": "API kulcs" }, - "description": "Sz\u00fcks\u00e9ge lesz a 16 karakteres API kulcsra, \u00fatmutat\u00e1s\u00e9rt l\u00e1sd: https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "Red\u0151ny/rol\u00f3" + "description": "Sz\u00fcks\u00e9ge lesz a 16 karakteres API kulcsra, \u00fatmutat\u00e1s\u00e9rt l\u00e1sd: https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API kulcs", "host": "IP c\u00edm" }, - "description": "Csatlakozzon a Motion Gateway-hez, ha az IP-c\u00edm nincs be\u00e1ll\u00edtva, akkor az automatikus felder\u00edt\u00e9st haszn\u00e1lja", - "title": "Red\u0151ny/rol\u00f3" + "description": "Csatlakozzon a Motion Gateway-hez, ha az IP-c\u00edm nincs be\u00e1ll\u00edtva, akkor az automatikus felder\u00edt\u00e9st haszn\u00e1lja" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Multicast adatokra v\u00e1rakoz\u00e1s friss\u00edt\u00e9skor" - }, - "description": "Opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1sa", - "title": "Red\u0151ny/rol\u00f3" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/id.json b/homeassistant/components/motion_blinds/translations/id.json index 42b9b8d127d..5892916aa0d 100644 --- a/homeassistant/components/motion_blinds/translations/id.json +++ b/homeassistant/components/motion_blinds/translations/id.json @@ -6,18 +6,15 @@ "connection_error": "Gagal terhubung" }, "error": { - "discovery_error": "Gagal menemukan Motion Gateway", - "invalid_interface": "Antarmuka jaringan tidak valid" + "discovery_error": "Gagal menemukan Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Kunci API", - "interface": "Antarmuka jaringan yang akan digunakan" + "api_key": "Kunci API" }, - "description": "Anda akan memerlukan Kunci API 16 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "Motion Blinds" + "description": "Anda akan memerlukan Kunci API 16 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Kunci API", "host": "Alamat IP" }, - "description": "Hubungkan ke Motion Gateway Anda, jika alamat IP tidak disetel, penemuan otomatis akan digunakan", - "title": "Motion Blinds" + "description": "Hubungkan ke Motion Gateway Anda, jika alamat IP tidak disetel, penemuan otomatis akan digunakan" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Tunggu push multicast pada pembaruan" - }, - "description": "Tentukan pengaturan opsional", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/it.json b/homeassistant/components/motion_blinds/translations/it.json index ddb29c2c042..35168c391b7 100644 --- a/homeassistant/components/motion_blinds/translations/it.json +++ b/homeassistant/components/motion_blinds/translations/it.json @@ -6,18 +6,15 @@ "connection_error": "Impossibile connettersi" }, "error": { - "discovery_error": "Impossibile rilevare un Motion Gateway", - "invalid_interface": "Interfaccia di rete non valida" + "discovery_error": "Impossibile rilevare un Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Chiave API", - "interface": "L'interfaccia di rete da utilizzare" + "api_key": "Chiave API" }, - "description": "Avrai bisogno della chiave API di 16 caratteri, consulta https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key per le istruzioni", - "title": "Motion Blinds" + "description": "Avrai bisogno della chiave API di 16 caratteri, consulta https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key per le istruzioni" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Chiave API", "host": "Indirizzo IP" }, - "description": "Connetti il tuo Motion Gateway, se l'indirizzo IP non \u00e8 impostato, sar\u00e0 utilizzato il rilevamento automatico", - "title": "Tende Motion" + "description": "Connetti il tuo Motion Gateway, se l'indirizzo IP non \u00e8 impostato, sar\u00e0 utilizzato il rilevamento automatico" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Attendi il push multicast all'aggiornamento" - }, - "description": "Specifica le impostazioni opzionali", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/ja.json b/homeassistant/components/motion_blinds/translations/ja.json index b81cfa365c3..df0ba1f8e47 100644 --- a/homeassistant/components/motion_blinds/translations/ja.json +++ b/homeassistant/components/motion_blinds/translations/ja.json @@ -6,18 +6,15 @@ "connection_error": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "error": { - "discovery_error": "Motion Gateway\u306e\u691c\u51fa\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_interface": "\u7121\u52b9\u306a\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30a4\u30b9" + "discovery_error": "Motion Gateway\u306e\u691c\u51fa\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "flow_title": "\u30e2\u30fc\u30b7\u30e7\u30f3\u30d6\u30e9\u30a4\u30f3\u30c9", "step": { "connect": { "data": { - "api_key": "API\u30ad\u30fc", - "interface": "\u4f7f\u7528\u3059\u308b\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30a4\u30b9" + "api_key": "API\u30ad\u30fc" }, - "description": "16\u6587\u5b57\u306eAPI\u30ad\u30fc\u304c\u5fc5\u8981\u3067\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30e2\u30fc\u30b7\u30e7\u30f3\u30d6\u30e9\u30a4\u30f3\u30c9" + "description": "16\u6587\u5b57\u306eAPI\u30ad\u30fc\u304c\u5fc5\u8981\u3067\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API\u30ad\u30fc", "host": "IP\u30a2\u30c9\u30ec\u30b9" }, - "description": "Motion Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", - "title": "\u30e2\u30fc\u30b7\u30e7\u30f3\u30d6\u30e9\u30a4\u30f3\u30c9" + "description": "Motion Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u304c\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "\u66f4\u65b0\u6642\u306b\u30de\u30eb\u30c1\u30ad\u30e3\u30b9\u30c8 \u30d7\u30c3\u30b7\u30e5\u3092\u5f85\u6a5f\u3059\u308b" - }, - "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a", - "title": "\u30e2\u30fc\u30b7\u30e7\u30f3\u30d6\u30e9\u30a4\u30f3\u30c9" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/ka.json b/homeassistant/components/motion_blinds/translations/ka.json index e8ea5e0deba..71223e44db4 100644 --- a/homeassistant/components/motion_blinds/translations/ka.json +++ b/homeassistant/components/motion_blinds/translations/ka.json @@ -9,11 +9,9 @@ "step": { "user": { "data": { - "api_key": "API Key", "host": "IP \u10db\u10d8\u10e1\u10d0\u10db\u10d0\u10e0\u10d7\u10d8" }, - "description": "\u10d7\u10e5\u10d5\u10d4\u10dc \u10d3\u10d0\u10d2\u10ed\u10d8\u10e0\u10d3\u10d4\u10d1\u10d0\u10d7 16 \u10d0\u10e1\u10dd\u10d8\u10d0\u10dc\u10d8 API key, \u10d8\u10dc\u10e1\u10e2\u10e0\u10e3\u10e5\u10ea\u10d8\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1 \u10d8\u10ee\u10d8\u10da\u10d4\u10d7 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "\u10db\u10dd\u10eb\u10e0\u10d0\u10d5\u10d8 \u10df\u10d0\u10da\u10e3\u10d6\u10d4\u10d1\u10d8" + "description": "\u10d7\u10e5\u10d5\u10d4\u10dc \u10d3\u10d0\u10d2\u10ed\u10d8\u10e0\u10d3\u10d4\u10d1\u10d0\u10d7 16 \u10d0\u10e1\u10dd\u10d8\u10d0\u10dc\u10d8 API key, \u10d8\u10dc\u10e1\u10e2\u10e0\u10e3\u10e5\u10ea\u10d8\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1 \u10d8\u10ee\u10d8\u10da\u10d4\u10d7 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" } } } diff --git a/homeassistant/components/motion_blinds/translations/ko.json b/homeassistant/components/motion_blinds/translations/ko.json index 69ed2cd7b35..19ad1c03da8 100644 --- a/homeassistant/components/motion_blinds/translations/ko.json +++ b/homeassistant/components/motion_blinds/translations/ko.json @@ -14,8 +14,7 @@ "data": { "api_key": "API \ud0a4" }, - "description": "16\uac1c\uc758 \ubb38\uc790\uc5f4\ub85c \uad6c\uc131\ub41c API Key\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \uc815\ubcf4\ub294 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.", - "title": "Motion Blinds" + "description": "16\uac1c\uc758 \ubb38\uc790\uc5f4\ub85c \uad6c\uc131\ub41c API Key\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \uc815\ubcf4\ub294 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694." }, "select": { "data": { @@ -26,11 +25,9 @@ }, "user": { "data": { - "api_key": "API \ud0a4", "host": "IP \uc8fc\uc18c" }, - "description": "Motion \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 \uc790\ub3d9 \uac80\uc0c9\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4", - "title": "Motion Blinds" + "description": "Motion \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 \uc790\ub3d9 \uac80\uc0c9\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4" } } } diff --git a/homeassistant/components/motion_blinds/translations/lb.json b/homeassistant/components/motion_blinds/translations/lb.json index 85caeea79e5..b8f301535bf 100644 --- a/homeassistant/components/motion_blinds/translations/lb.json +++ b/homeassistant/components/motion_blinds/translations/lb.json @@ -22,10 +22,8 @@ }, "user": { "data": { - "api_key": "API Schl\u00ebssel", "host": "IP Adresse" - }, - "title": "Steierbar Jalousien" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/nl.json b/homeassistant/components/motion_blinds/translations/nl.json index c837e656c56..73522480df5 100644 --- a/homeassistant/components/motion_blinds/translations/nl.json +++ b/homeassistant/components/motion_blinds/translations/nl.json @@ -6,18 +6,15 @@ "connection_error": "Kan geen verbinding maken" }, "error": { - "discovery_error": "Kan geen Motion Gateway vinden", - "invalid_interface": "Ongeldige netwerkinterface" + "discovery_error": "Kan geen Motion Gateway vinden" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API-sleutel", - "interface": "De te gebruiken netwerkinterface" + "api_key": "API-sleutel" }, - "description": "U hebt de API-sleutel van 16 tekens nodig, zie https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key voor instructies", - "title": "Motion Blinds" + "description": "U hebt de API-sleutel van 16 tekens nodig, zie https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key voor instructies" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API-sleutel", "host": "IP-adres" }, - "description": "Maak verbinding met uw Motion Gateway, als het IP-adres niet is ingesteld, wordt auto-discovery gebruikt", - "title": "Motion Blinds" + "description": "Maak verbinding met uw Motion Gateway, als het IP-adres niet is ingesteld, wordt auto-discovery gebruikt" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Wacht op multicast push bij update" - }, - "description": "Optionele instellingen opgeven", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/no.json b/homeassistant/components/motion_blinds/translations/no.json index 655e574cab9..3545a03a543 100644 --- a/homeassistant/components/motion_blinds/translations/no.json +++ b/homeassistant/components/motion_blinds/translations/no.json @@ -6,18 +6,15 @@ "connection_error": "Tilkobling mislyktes" }, "error": { - "discovery_error": "Kunne ikke oppdage en Motion Gateway", - "invalid_interface": "Ugyldig nettverksgrensesnitt" + "discovery_error": "Kunne ikke oppdage en Motion Gateway" }, "flow_title": "{short_mac} ( {ip_address} )", "step": { "connect": { "data": { - "api_key": "API-n\u00f8kkel", - "interface": "Nettverksgrensesnittet som skal brukes" + "api_key": "API-n\u00f8kkel" }, - "description": "Du trenger API-n\u00f8kkelen med 16 tegn, se https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key for instruksjoner", - "title": "" + "description": "Du trenger API-n\u00f8kkelen med 16 tegn, se https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key for instruksjoner" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API-n\u00f8kkel", "host": "IP adresse" }, - "description": "Koble til Motion Gateway. Hvis IP-adressen ikke er angitt, brukes automatisk oppdagelse", - "title": "Motion Blinds" + "description": "Koble til Motion Gateway. Hvis IP-adressen ikke er angitt, brukes automatisk oppdagelse" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Vent p\u00e5 multicast push p\u00e5 oppdateringen" - }, - "description": "Spesifiser valgfrie innstillinger", - "title": "Bevegelse Persienner" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/pl.json b/homeassistant/components/motion_blinds/translations/pl.json index 2a042859b88..60277ba9360 100644 --- a/homeassistant/components/motion_blinds/translations/pl.json +++ b/homeassistant/components/motion_blinds/translations/pl.json @@ -6,18 +6,15 @@ "connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "error": { - "discovery_error": "Nie uda\u0142o si\u0119 wykry\u0107 bramki ruchu", - "invalid_interface": "Nieprawid\u0142owy interfejs sieciowy" + "discovery_error": "Nie uda\u0142o si\u0119 wykry\u0107 bramki ruchu" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Klucz API", - "interface": "Interfejs sieciowy" + "api_key": "Klucz API" }, - "description": "B\u0119dziesz potrzebowa\u0142 16-znakowego klucza API, instrukcje znajdziesz na https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key", - "title": "Motion Blinds" + "description": "B\u0119dziesz potrzebowa\u0142 16-znakowego klucza API, instrukcje znajdziesz na https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Klucz API", "host": "Adres IP" }, - "description": "Po\u0142\u0105cz si\u0119 z bram\u0105 ruchu. Je\u015bli adres IP nie jest ustawiony, u\u017cywane jest automatyczne wykrywanie", - "title": "Rolety Motion" + "description": "Po\u0142\u0105cz si\u0119 z bram\u0105 ruchu. Je\u015bli adres IP nie jest ustawiony, u\u017cywane jest automatyczne wykrywanie" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Poczekaj na aktualizacj\u0119 multicast push" - }, - "description": "Okre\u015bl opcjonalne ustawienia", - "title": "Rolety Motion" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/pt-BR.json b/homeassistant/components/motion_blinds/translations/pt-BR.json index afbd117d260..0a3b68357ee 100644 --- a/homeassistant/components/motion_blinds/translations/pt-BR.json +++ b/homeassistant/components/motion_blinds/translations/pt-BR.json @@ -6,18 +6,15 @@ "connection_error": "Falha ao conectar" }, "error": { - "discovery_error": "Falha ao descobrir um Motion Gateway", - "invalid_interface": "Interface de rede inv\u00e1lida" + "discovery_error": "Falha ao descobrir um Motion Gateway" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "Chave da API", - "interface": "A interface de rede a ser utilizada" + "api_key": "Chave da API" }, - "description": "Voc\u00ea precisar\u00e1 da chave de API de 16 caracteres, consulte https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key para obter instru\u00e7\u00f5es", - "title": "Cortinas de movimento" + "description": "Voc\u00ea precisar\u00e1 da chave de API de 16 caracteres, consulte https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key para obter instru\u00e7\u00f5es" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "Chave da API", "host": "Endere\u00e7o IP" }, - "description": "Conecte-se ao seu Motion Gateway, se o endere\u00e7o IP n\u00e3o estiver definido, a descoberta autom\u00e1tica ser\u00e1 usada", - "title": "Cortinas de movimento" + "description": "Conecte-se ao seu Motion Gateway, se o endere\u00e7o IP n\u00e3o estiver definido, a descoberta autom\u00e1tica ser\u00e1 usada" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "Aguarde o push multicast na atualiza\u00e7\u00e3o" - }, - "description": "Especifique as configura\u00e7\u00f5es opcionais", - "title": "Cortinas de movimento" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/pt.json b/homeassistant/components/motion_blinds/translations/pt.json index e43b71438db..7538043f1ce 100644 --- a/homeassistant/components/motion_blinds/translations/pt.json +++ b/homeassistant/components/motion_blinds/translations/pt.json @@ -10,8 +10,7 @@ "connect": { "data": { "api_key": "Chave da API" - }, - "title": "Cortinas Motion" + } }, "select": { "data": { @@ -20,10 +19,8 @@ }, "user": { "data": { - "api_key": "Chave da API", "host": "Endere\u00e7o IP" - }, - "title": "Cortinas Motion" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/ru.json b/homeassistant/components/motion_blinds/translations/ru.json index 0ab743f8895..afcde99b123 100644 --- a/homeassistant/components/motion_blinds/translations/ru.json +++ b/homeassistant/components/motion_blinds/translations/ru.json @@ -6,18 +6,15 @@ "connection_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "error": { - "discovery_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0448\u043b\u044e\u0437 Motion.", - "invalid_interface": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441." + "discovery_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0448\u043b\u044e\u0437 Motion." }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", - "interface": "\u0421\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441" + "api_key": "\u041a\u043b\u044e\u0447 API" }, - "description": "\u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key.", - "title": "Motion Blinds" + "description": "\u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key." }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", "host": "IP-\u0430\u0434\u0440\u0435\u0441" }, - "description": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u043b\u044e\u0437\u0443 Motion. \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u0443\u0441\u0442\u044b\u043c.", - "title": "Motion Blinds" + "description": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u043b\u044e\u0437\u0443 Motion. \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u0443\u0441\u0442\u044b\u043c." } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "\u041e\u0436\u0438\u0434\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0430\u0434\u0440\u0435\u0441\u043d\u043e\u0439 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438 \u043e\u0431 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438" - }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/sk.json b/homeassistant/components/motion_blinds/translations/sk.json index e58538162d7..6ea9c064f16 100644 --- a/homeassistant/components/motion_blinds/translations/sk.json +++ b/homeassistant/components/motion_blinds/translations/sk.json @@ -8,11 +8,6 @@ "data": { "api_key": "API k\u013e\u00fa\u010d" } - }, - "user": { - "data": { - "api_key": "API k\u013e\u00fa\u010d" - } } } } diff --git a/homeassistant/components/motion_blinds/translations/sl.json b/homeassistant/components/motion_blinds/translations/sl.json deleted file mode 100644 index 80ea8b24fbb..00000000000 --- a/homeassistant/components/motion_blinds/translations/sl.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "config": { - "error": { - "invalid_interface": "Neveljaven omre\u017eni vmesnik" - }, - "step": { - "connect": { - "data": { - "interface": "Omre\u017eni vmesnik za uporabo" - } - }, - "user": { - "title": "Motion Blinds" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/tr.json b/homeassistant/components/motion_blinds/translations/tr.json index f53b251a8ec..5af004ae0cc 100644 --- a/homeassistant/components/motion_blinds/translations/tr.json +++ b/homeassistant/components/motion_blinds/translations/tr.json @@ -6,18 +6,15 @@ "connection_error": "Ba\u011flanma hatas\u0131" }, "error": { - "discovery_error": "Motion Gateway bulunamad\u0131", - "invalid_interface": "Ge\u00e7ersiz a\u011f aray\u00fcz\u00fc" + "discovery_error": "Motion Gateway bulunamad\u0131" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API Anahtar\u0131", - "interface": "Kullan\u0131lacak a\u011f aray\u00fcz\u00fc" + "api_key": "API Anahtar\u0131" }, - "description": "16 karakterlik API Anahtar\u0131na ihtiyac\u0131n\u0131z olacak, talimatlar i\u00e7in https://www.home-assistant.io/integrations/motion_blinds/#retriving-the-key adresine bak\u0131n.", - "title": "Hareketli Perdeler" + "description": "16 karakterlik API Anahtar\u0131na ihtiyac\u0131n\u0131z olacak, talimatlar i\u00e7in https://www.home-assistant.io/integrations/motion_blinds/#retriving-the-key adresine bak\u0131n." }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API Anahtar\u0131", "host": "IP Adresi" }, - "description": "Motion Gateway'inize ba\u011flan\u0131n, IP adresi ayarlanmad\u0131ysa, otomatik ke\u015fif kullan\u0131l\u0131r", - "title": "Hareketli Perdeler" + "description": "Motion Gateway'inize ba\u011flan\u0131n, IP adresi ayarlanmad\u0131ysa, otomatik ke\u015fif kullan\u0131l\u0131r" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "G\u00fcncellemede \u00e7ok noktaya yay\u0131n i\u00e7in bekleyin" - }, - "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin", - "title": "Hareketli Panjurlar" + } } } } diff --git a/homeassistant/components/motion_blinds/translations/uk.json b/homeassistant/components/motion_blinds/translations/uk.json index 99ccb60dc6c..a3d8ee57c1f 100644 --- a/homeassistant/components/motion_blinds/translations/uk.json +++ b/homeassistant/components/motion_blinds/translations/uk.json @@ -14,8 +14,7 @@ "data": { "api_key": "\u041a\u043b\u044e\u0447 API" }, - "description": "\u0412\u0430\u043c \u043f\u043e\u0442\u0440\u0456\u0431\u0435\u043d 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API, \u0434\u0438\u0432. https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0439", - "title": "Motion Blinds" + "description": "\u0412\u0430\u043c \u043f\u043e\u0442\u0440\u0456\u0431\u0435\u043d 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API, \u0434\u0438\u0432. https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u0439" }, "select": { "data": { @@ -26,11 +25,9 @@ }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", "host": "IP-\u0430\u0434\u0440\u0435\u0441\u0430" }, - "description": "\u041f\u0440\u043e \u0442\u0435, \u044f\u043a \u043e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API, \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0457 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key.", - "title": "Motion Blinds" + "description": "\u041f\u0440\u043e \u0442\u0435, \u044f\u043a \u043e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 16-\u0441\u0438\u043c\u0432\u043e\u043b\u044c\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API, \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0457 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key." } } } diff --git a/homeassistant/components/motion_blinds/translations/zh-Hans.json b/homeassistant/components/motion_blinds/translations/zh-Hans.json index f8dac159488..d8f87406260 100644 --- a/homeassistant/components/motion_blinds/translations/zh-Hans.json +++ b/homeassistant/components/motion_blinds/translations/zh-Hans.json @@ -6,7 +6,6 @@ "step": { "user": { "data": { - "api_key": "API\u5bc6\u7801", "host": "IP\u5730\u5740" } } diff --git a/homeassistant/components/motion_blinds/translations/zh-Hant.json b/homeassistant/components/motion_blinds/translations/zh-Hant.json index ccfe6e782ed..189f1474b22 100644 --- a/homeassistant/components/motion_blinds/translations/zh-Hant.json +++ b/homeassistant/components/motion_blinds/translations/zh-Hant.json @@ -6,18 +6,15 @@ "connection_error": "\u9023\u7dda\u5931\u6557" }, "error": { - "discovery_error": "\u641c\u7d22 Motion \u9598\u9053\u5668\u5931\u6557", - "invalid_interface": "\u7db2\u8def\u4ecb\u9762\u7121\u6548" + "discovery_error": "\u641c\u7d22 Motion \u9598\u9053\u5668\u5931\u6557" }, "flow_title": "{short_mac} ({ip_address})", "step": { "connect": { "data": { - "api_key": "API \u91d1\u9470", - "interface": "\u4f7f\u7528\u7684\u7db2\u8def\u4ecb\u9762" + "api_key": "API \u91d1\u9470" }, - "description": "\u5c07\u9700\u8981\u8f38\u5165 16 \u4f4d\u5b57\u5143 API \u91d1\u9470\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u4ee5\u7372\u5f97\u7372\u53d6\u91d1\u9470\u7684\u6559\u5b78\u3002", - "title": "Motion Blinds" + "description": "\u5c07\u9700\u8981\u8f38\u5165 16 \u4f4d\u5b57\u5143 API \u91d1\u9470\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/motion_blinds/#retrieving-the-key \u4ee5\u7372\u5f97\u7372\u53d6\u91d1\u9470\u7684\u6559\u5b78\u3002" }, "select": { "data": { @@ -28,11 +25,9 @@ }, "user": { "data": { - "api_key": "API \u91d1\u9470", "host": "IP \u4f4d\u5740" }, - "description": "\u9023\u7dda\u81f3 Motion \u9598\u9053\u5668\uff0c\u5047\u5982\u672a\u63d0\u4f9b IP \u4f4d\u5740\uff0c\u5c07\u4f7f\u7528\u81ea\u52d5\u641c\u7d22", - "title": "Motion Blinds" + "description": "\u9023\u7dda\u81f3 Motion \u9598\u9053\u5668\uff0c\u5047\u5982\u672a\u63d0\u4f9b IP \u4f4d\u5740\uff0c\u5c07\u4f7f\u7528\u81ea\u52d5\u641c\u7d22" } } }, @@ -41,9 +36,7 @@ "init": { "data": { "wait_for_push": "\u7b49\u5019 Multicast \u63a8\u9001\u901a\u77e5" - }, - "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a", - "title": "Motion Blinds" + } } } } diff --git a/homeassistant/components/mysensors/translations/ca.json b/homeassistant/components/mysensors/translations/ca.json index 6e20f0bcbee..99cf16939ad 100644 --- a/homeassistant/components/mysensors/translations/ca.json +++ b/homeassistant/components/mysensors/translations/ca.json @@ -75,6 +75,5 @@ "description": "Tria el m\u00e8tode de connexi\u00f3 a la passarel\u00b7la" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/cs.json b/homeassistant/components/mysensors/translations/cs.json index abe47f046ff..3434cfe82ec 100644 --- a/homeassistant/components/mysensors/translations/cs.json +++ b/homeassistant/components/mysensors/translations/cs.json @@ -12,6 +12,5 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/de.json b/homeassistant/components/mysensors/translations/de.json index cfef6f7d363..0b90165fa5a 100644 --- a/homeassistant/components/mysensors/translations/de.json +++ b/homeassistant/components/mysensors/translations/de.json @@ -75,6 +75,5 @@ "description": "Verbindungsmethode zum Gateway w\u00e4hlen" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/el.json b/homeassistant/components/mysensors/translations/el.json index 17bf83158b7..81ba72eea7e 100644 --- a/homeassistant/components/mysensors/translations/el.json +++ b/homeassistant/components/mysensors/translations/el.json @@ -75,6 +75,5 @@ "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/en.json b/homeassistant/components/mysensors/translations/en.json index 7ca3516e50d..5ec81c22186 100644 --- a/homeassistant/components/mysensors/translations/en.json +++ b/homeassistant/components/mysensors/translations/en.json @@ -75,6 +75,5 @@ "description": "Choose connection method to the gateway" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/es.json b/homeassistant/components/mysensors/translations/es.json index 411501db49d..91f2b7f0d1e 100644 --- a/homeassistant/components/mysensors/translations/es.json +++ b/homeassistant/components/mysensors/translations/es.json @@ -75,6 +75,5 @@ "description": "Elija el m\u00e9todo de conexi\u00f3n con la pasarela" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/et.json b/homeassistant/components/mysensors/translations/et.json index 7aff6b1c3da..00614e57252 100644 --- a/homeassistant/components/mysensors/translations/et.json +++ b/homeassistant/components/mysensors/translations/et.json @@ -75,6 +75,5 @@ "description": "Vali l\u00fc\u00fcsi \u00fchendusviis" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/fr.json b/homeassistant/components/mysensors/translations/fr.json index c9d64ee73e6..722bf639111 100644 --- a/homeassistant/components/mysensors/translations/fr.json +++ b/homeassistant/components/mysensors/translations/fr.json @@ -75,6 +75,5 @@ "description": "Choisissez la m\u00e9thode de connexion \u00e0 la passerelle" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/hu.json b/homeassistant/components/mysensors/translations/hu.json index a52ac3cd289..433714de477 100644 --- a/homeassistant/components/mysensors/translations/hu.json +++ b/homeassistant/components/mysensors/translations/hu.json @@ -75,6 +75,5 @@ "description": "V\u00e1lassza ki az \u00e1tj\u00e1r\u00f3hoz val\u00f3 csatlakoz\u00e1si m\u00f3dot" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/id.json b/homeassistant/components/mysensors/translations/id.json index 93b10bb8c2c..e6256bd8757 100644 --- a/homeassistant/components/mysensors/translations/id.json +++ b/homeassistant/components/mysensors/translations/id.json @@ -75,6 +75,5 @@ "description": "Pilih metode koneksi ke gateway" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/it.json b/homeassistant/components/mysensors/translations/it.json index 56ce395aa4c..0a16a4f045c 100644 --- a/homeassistant/components/mysensors/translations/it.json +++ b/homeassistant/components/mysensors/translations/it.json @@ -75,6 +75,5 @@ "description": "Scegli il metodo di connessione al gateway" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/ja.json b/homeassistant/components/mysensors/translations/ja.json index b178d136009..da0354fe3c7 100644 --- a/homeassistant/components/mysensors/translations/ja.json +++ b/homeassistant/components/mysensors/translations/ja.json @@ -75,6 +75,5 @@ "description": "\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3078\u306e\u63a5\u7d9a\u65b9\u6cd5\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/ko.json b/homeassistant/components/mysensors/translations/ko.json index e57f60aafbf..7a6f3e856a7 100644 --- a/homeassistant/components/mysensors/translations/ko.json +++ b/homeassistant/components/mysensors/translations/ko.json @@ -74,6 +74,5 @@ "description": "\uac8c\uc774\ud2b8\uc6e8\uc774 \uc5f0\uacb0 \ubc29\ubc95\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/nl.json b/homeassistant/components/mysensors/translations/nl.json index 14055639f60..8293e815d8c 100644 --- a/homeassistant/components/mysensors/translations/nl.json +++ b/homeassistant/components/mysensors/translations/nl.json @@ -75,6 +75,5 @@ "description": "Kies de verbindingsmethode met de gateway" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/no.json b/homeassistant/components/mysensors/translations/no.json index f0e307a1ab2..14fb39910fe 100644 --- a/homeassistant/components/mysensors/translations/no.json +++ b/homeassistant/components/mysensors/translations/no.json @@ -75,6 +75,5 @@ "description": "Velg tilkoblingsmetode til gatewayen" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/pl.json b/homeassistant/components/mysensors/translations/pl.json index f3233a01d50..3c4bed1ee86 100644 --- a/homeassistant/components/mysensors/translations/pl.json +++ b/homeassistant/components/mysensors/translations/pl.json @@ -75,6 +75,5 @@ "description": "Wybierz metod\u0119 po\u0142\u0105czenia z bramk\u0105" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/pt-BR.json b/homeassistant/components/mysensors/translations/pt-BR.json index 468acf70588..2c0f312da72 100644 --- a/homeassistant/components/mysensors/translations/pt-BR.json +++ b/homeassistant/components/mysensors/translations/pt-BR.json @@ -75,6 +75,5 @@ "description": "Escolha o m\u00e9todo de conex\u00e3o com o gateway" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/ru.json b/homeassistant/components/mysensors/translations/ru.json index 16f23f6efff..2801b8e8c00 100644 --- a/homeassistant/components/mysensors/translations/ru.json +++ b/homeassistant/components/mysensors/translations/ru.json @@ -75,6 +75,5 @@ "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0448\u043b\u044e\u0437\u0443" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/tr.json b/homeassistant/components/mysensors/translations/tr.json index 59f7a319e07..9f99525c7b2 100644 --- a/homeassistant/components/mysensors/translations/tr.json +++ b/homeassistant/components/mysensors/translations/tr.json @@ -75,6 +75,5 @@ "description": "A\u011f ge\u00e7idine ba\u011flant\u0131 y\u00f6ntemini se\u00e7in" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/mysensors/translations/zh-Hant.json b/homeassistant/components/mysensors/translations/zh-Hant.json index 5774df64b78..a3ec7ba2dd7 100644 --- a/homeassistant/components/mysensors/translations/zh-Hant.json +++ b/homeassistant/components/mysensors/translations/zh-Hant.json @@ -75,6 +75,5 @@ "description": "\u9078\u64c7\u9598\u9053\u5668\u9023\u7dda\u65b9\u5f0f" } } - }, - "title": "MySensors" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/ca.json b/homeassistant/components/neato/translations/ca.json index ab8210afb2f..e45c8a28e5f 100644 --- a/homeassistant/components/neato/translations/ca.json +++ b/homeassistant/components/neato/translations/ca.json @@ -18,6 +18,5 @@ "title": "Vols comen\u00e7ar la configuraci\u00f3?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/cs.json b/homeassistant/components/neato/translations/cs.json index 6cf7e314c4b..4618be9d933 100644 --- a/homeassistant/components/neato/translations/cs.json +++ b/homeassistant/components/neato/translations/cs.json @@ -18,6 +18,5 @@ "title": "Chcete za\u010d\u00edt nastavovat?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/de.json b/homeassistant/components/neato/translations/de.json index c7fd239c585..03ad20e3caf 100644 --- a/homeassistant/components/neato/translations/de.json +++ b/homeassistant/components/neato/translations/de.json @@ -18,6 +18,5 @@ "title": "M\u00f6chtest Du mit der Einrichtung beginnen?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/el.json b/homeassistant/components/neato/translations/el.json index 5e57ddd8b8b..a1a6232cfdb 100644 --- a/homeassistant/components/neato/translations/el.json +++ b/homeassistant/components/neato/translations/el.json @@ -18,6 +18,5 @@ "title": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/en.json b/homeassistant/components/neato/translations/en.json index 684d667678b..7c435c3749b 100644 --- a/homeassistant/components/neato/translations/en.json +++ b/homeassistant/components/neato/translations/en.json @@ -18,6 +18,5 @@ "title": "Do you want to start set up?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/es.json b/homeassistant/components/neato/translations/es.json index bcb9dc3619e..7eeecb4dde0 100644 --- a/homeassistant/components/neato/translations/es.json +++ b/homeassistant/components/neato/translations/es.json @@ -18,6 +18,5 @@ "title": "\u00bfQuieres iniciar la configuraci\u00f3n?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/et.json b/homeassistant/components/neato/translations/et.json index 40e601dfe9d..eff32d5b877 100644 --- a/homeassistant/components/neato/translations/et.json +++ b/homeassistant/components/neato/translations/et.json @@ -18,6 +18,5 @@ "title": "Kas soovid alustada seadistamist?" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/fr.json b/homeassistant/components/neato/translations/fr.json index df805121f6b..4334a2180bb 100644 --- a/homeassistant/components/neato/translations/fr.json +++ b/homeassistant/components/neato/translations/fr.json @@ -18,6 +18,5 @@ "title": "Voulez-vous commencer la configuration\u00a0?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/hu.json b/homeassistant/components/neato/translations/hu.json index 255378eda35..3415d966717 100644 --- a/homeassistant/components/neato/translations/hu.json +++ b/homeassistant/components/neato/translations/hu.json @@ -18,6 +18,5 @@ "title": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/id.json b/homeassistant/components/neato/translations/id.json index b1811fe298e..e38096f47aa 100644 --- a/homeassistant/components/neato/translations/id.json +++ b/homeassistant/components/neato/translations/id.json @@ -18,6 +18,5 @@ "title": "Ingin memulai penyiapan?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/it.json b/homeassistant/components/neato/translations/it.json index 000625a0294..cda062ceff0 100644 --- a/homeassistant/components/neato/translations/it.json +++ b/homeassistant/components/neato/translations/it.json @@ -18,6 +18,5 @@ "title": "Vuoi iniziare la configurazione?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/ja.json b/homeassistant/components/neato/translations/ja.json index c5f4605159c..39eeedddc91 100644 --- a/homeassistant/components/neato/translations/ja.json +++ b/homeassistant/components/neato/translations/ja.json @@ -18,6 +18,5 @@ "title": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/ko.json b/homeassistant/components/neato/translations/ko.json index dc651939665..faaea344741 100644 --- a/homeassistant/components/neato/translations/ko.json +++ b/homeassistant/components/neato/translations/ko.json @@ -18,6 +18,5 @@ "title": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/lb.json b/homeassistant/components/neato/translations/lb.json index d54443e6671..31a2b8d2f73 100644 --- a/homeassistant/components/neato/translations/lb.json +++ b/homeassistant/components/neato/translations/lb.json @@ -17,6 +17,5 @@ "title": "Soll den Ariichtungs Prozess gestart ginn?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/nl.json b/homeassistant/components/neato/translations/nl.json index 2e9ab212fa9..0bf8009b88e 100644 --- a/homeassistant/components/neato/translations/nl.json +++ b/homeassistant/components/neato/translations/nl.json @@ -18,6 +18,5 @@ "title": "Wilt u beginnen met instellen?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/no.json b/homeassistant/components/neato/translations/no.json index fc50308eda8..d62c8e5d1f1 100644 --- a/homeassistant/components/neato/translations/no.json +++ b/homeassistant/components/neato/translations/no.json @@ -18,6 +18,5 @@ "title": "Vil du starte oppsettet?" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/pl.json b/homeassistant/components/neato/translations/pl.json index 34f1d42bda5..a97184b8b35 100644 --- a/homeassistant/components/neato/translations/pl.json +++ b/homeassistant/components/neato/translations/pl.json @@ -18,6 +18,5 @@ "title": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/pt-BR.json b/homeassistant/components/neato/translations/pt-BR.json index 684f7cdff22..41bc8c5e4a6 100644 --- a/homeassistant/components/neato/translations/pt-BR.json +++ b/homeassistant/components/neato/translations/pt-BR.json @@ -18,6 +18,5 @@ "title": "Deseja iniciar a configura\u00e7\u00e3o?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/ru.json b/homeassistant/components/neato/translations/ru.json index 29201df669d..9c12f8c962c 100644 --- a/homeassistant/components/neato/translations/ru.json +++ b/homeassistant/components/neato/translations/ru.json @@ -18,6 +18,5 @@ "title": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/sl.json b/homeassistant/components/neato/translations/sl.json index 1aaae76ada8..a82510d9b95 100644 --- a/homeassistant/components/neato/translations/sl.json +++ b/homeassistant/components/neato/translations/sl.json @@ -18,6 +18,5 @@ "title": "Bi radi zagnali namestitev?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/tr.json b/homeassistant/components/neato/translations/tr.json index a461d5b0c2f..4c93fdc78b0 100644 --- a/homeassistant/components/neato/translations/tr.json +++ b/homeassistant/components/neato/translations/tr.json @@ -18,6 +18,5 @@ "title": "Kuruluma ba\u015flamak ister misiniz?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/uk.json b/homeassistant/components/neato/translations/uk.json index 353005546cf..532808c1fb6 100644 --- a/homeassistant/components/neato/translations/uk.json +++ b/homeassistant/components/neato/translations/uk.json @@ -18,6 +18,5 @@ "title": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043f\u043e\u0447\u0430\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f?" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/neato/translations/zh-Hant.json b/homeassistant/components/neato/translations/zh-Hant.json index 781ee1f952b..1252e80f4b7 100644 --- a/homeassistant/components/neato/translations/zh-Hant.json +++ b/homeassistant/components/neato/translations/zh-Hant.json @@ -18,6 +18,5 @@ "title": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" } } - }, - "title": "Neato Botvac" + } } \ No newline at end of file diff --git a/homeassistant/components/netatmo/translations/nl.json b/homeassistant/components/netatmo/translations/nl.json index 1a740f9d5b1..a5ddba5772c 100644 --- a/homeassistant/components/netatmo/translations/nl.json +++ b/homeassistant/components/netatmo/translations/nl.json @@ -59,7 +59,7 @@ }, "public_weather_areas": { "data": { - "new_area": "Naam van het gebied", + "new_area": "Naam van ruimte", "weather_areas": "Weersgebieden" }, "description": "Configureer openbare weersensoren.", diff --git a/homeassistant/components/netatmo/translations/pt-BR.json b/homeassistant/components/netatmo/translations/pt-BR.json index b47c0ea3646..9f438868d43 100644 --- a/homeassistant/components/netatmo/translations/pt-BR.json +++ b/homeassistant/components/netatmo/translations/pt-BR.json @@ -52,7 +52,7 @@ "lon_ne": "Longitude nordeste", "lon_sw": "Longitude sudoeste", "mode": "C\u00e1lculo", - "show_on_map": "Mostrar no mapa" + "show_on_map": "[%key:component::iss::config::step::user::data::show_on_map%]" }, "description": "Configure um sensor meteorol\u00f3gico p\u00fablico para uma \u00e1rea.", "title": "Sensor meteorol\u00f3gico p\u00fablico Netatmo" diff --git a/homeassistant/components/netgear/translations/bg.json b/homeassistant/components/netgear/translations/bg.json index dd5e4fa2f38..17bc7cd1666 100644 --- a/homeassistant/components/netgear/translations/bg.json +++ b/homeassistant/components/netgear/translations/bg.json @@ -11,18 +11,9 @@ "data": { "host": "\u0425\u043e\u0441\u0442 (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "port": "\u041f\u043e\u0440\u0442 (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435 (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)" }, - "description": "\u0425\u043e\u0441\u0442 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {host}\n\u041f\u043e\u0440\u0442 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {port}\n\u041f\u043e\u0442\u0440. \u0438\u043c\u0435 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {username}", - "title": "Netgear" - } - } - }, - "options": { - "step": { - "init": { - "title": "Netgear" + "description": "\u0425\u043e\u0441\u0442 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {host}\n\u041f\u043e\u0440\u0442 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {port}\n\u041f\u043e\u0442\u0440. \u0438\u043c\u0435 \u043f\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0431\u0438\u0440\u0430\u043d\u0435: {username}" } } } diff --git a/homeassistant/components/netgear/translations/ca.json b/homeassistant/components/netgear/translations/ca.json index 6d5edbd8e1f..f89e68cdfce 100644 --- a/homeassistant/components/netgear/translations/ca.json +++ b/homeassistant/components/netgear/translations/ca.json @@ -11,12 +11,9 @@ "data": { "host": "Amfitri\u00f3 (opcional)", "password": "Contrasenya", - "port": "Port (opcional)", - "ssl": "Utilitza un certificat SSL", "username": "Nom d'usuari (opcional)" }, - "description": "Amfitri\u00f3 predeterminat: {host}\nNom d'usuari predeterminat: {username}", - "title": "Netgear" + "description": "Amfitri\u00f3 predeterminat: {host}\nNom d'usuari predeterminat: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Temps per considerar 'a casa' (segons)" }, - "description": "Especifica les configuracions opcional", - "title": "Netgear" + "description": "Especifica les configuracions opcional" } } } diff --git a/homeassistant/components/netgear/translations/cs.json b/homeassistant/components/netgear/translations/cs.json index 6d942ff2ff4..67e2611aa87 100644 --- a/homeassistant/components/netgear/translations/cs.json +++ b/homeassistant/components/netgear/translations/cs.json @@ -8,8 +8,6 @@ "data": { "host": "Hostitel (nepovinn\u00fd)", "password": "Heslo", - "port": "Port (nepovinn\u00fd)", - "ssl": "Pou\u017e\u00edv\u00e1 SSL certifik\u00e1t", "username": "U\u017eivatelsk\u00e9 jm\u00e9no (nepovinn\u00e9)" } } diff --git a/homeassistant/components/netgear/translations/de.json b/homeassistant/components/netgear/translations/de.json index b742fb36ec5..bbfadbb8254 100644 --- a/homeassistant/components/netgear/translations/de.json +++ b/homeassistant/components/netgear/translations/de.json @@ -11,12 +11,9 @@ "data": { "host": "Host (Optional)", "password": "Passwort", - "port": "Port (Optional)", - "ssl": "Verwendet ein SSL-Zertifikat", "username": "Benutzername (Optional)" }, - "description": "Standardhost: {host}\nStandardbenutzername: {username}", - "title": "Netgear" + "description": "Standardhost: {host}\nStandardbenutzername: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Zu Hause Zeit (Sekunden)" }, - "description": "Optionale Einstellungen angeben", - "title": "Netgear" + "description": "Optionale Einstellungen angeben" } } } diff --git a/homeassistant/components/netgear/translations/el.json b/homeassistant/components/netgear/translations/el.json index 0ac5cb328bc..51f982da008 100644 --- a/homeassistant/components/netgear/translations/el.json +++ b/homeassistant/components/netgear/translations/el.json @@ -11,12 +11,9 @@ "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2 (\u03a0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1 (\u03a0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", - "ssl": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ad\u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 (\u03a0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" }, - "description": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2: {host}\n \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7: {username}", - "title": "Netgear" + "description": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2: {host}\n \u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "\u0395\u03be\u03b5\u03c4\u03ac\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ce\u03c1\u03b1 \u03c3\u03c4\u03bf \u03c3\u03c0\u03af\u03c4\u03b9 (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)" }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2", - "title": "Netgear" + "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ad\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2" } } } diff --git a/homeassistant/components/netgear/translations/en.json b/homeassistant/components/netgear/translations/en.json index 42b014d9ed3..82712b7c738 100644 --- a/homeassistant/components/netgear/translations/en.json +++ b/homeassistant/components/netgear/translations/en.json @@ -11,12 +11,9 @@ "data": { "host": "Host (Optional)", "password": "Password", - "port": "Port (Optional)", - "ssl": "Uses an SSL certificate", "username": "Username (Optional)" }, - "description": "Default host: {host}\nDefault username: {username}", - "title": "Netgear" + "description": "Default host: {host}\nDefault username: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Consider home time (seconds)" }, - "description": "Specify optional settings", - "title": "Netgear" + "description": "Specify optional settings" } } } diff --git a/homeassistant/components/netgear/translations/es.json b/homeassistant/components/netgear/translations/es.json index e1edb726d52..9edbd1101d0 100644 --- a/homeassistant/components/netgear/translations/es.json +++ b/homeassistant/components/netgear/translations/es.json @@ -11,12 +11,9 @@ "data": { "host": "Host (Opcional)", "password": "Contrase\u00f1a", - "port": "Puerto (Opcional)", - "ssl": "Utiliza un certificado SSL", "username": "Usuario (Opcional)" }, - "description": "Host predeterminado: {host} \nNombre de usuario predeterminado: {username}", - "title": "Netgear" + "description": "Host predeterminado: {host} \nNombre de usuario predeterminado: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Considere el tiempo en casa (segundos)" }, - "description": "Especifica los ajustes opcionales", - "title": "Netgear" + "description": "Especifica los ajustes opcionales" } } } diff --git a/homeassistant/components/netgear/translations/et.json b/homeassistant/components/netgear/translations/et.json index dcbaf4bcb2e..56b999fdbef 100644 --- a/homeassistant/components/netgear/translations/et.json +++ b/homeassistant/components/netgear/translations/et.json @@ -11,12 +11,9 @@ "data": { "host": "Host (valikuline)", "password": "Salas\u00f5na", - "port": "Port (valikuline)", - "ssl": "Kasutusel on SSL sert", "username": "Kasutajanimi (valikuline)" }, - "description": "Vaikimisi host: {host}\nVaikimisi kasutajanimi: {username}", - "title": "Netgear" + "description": "Vaikimisi host: {host}\nVaikimisi kasutajanimi: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Kohaloleku m\u00e4\u00e4ramise aeg (sekundites)" }, - "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine", - "title": "Netgear" + "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine" } } } diff --git a/homeassistant/components/netgear/translations/fr.json b/homeassistant/components/netgear/translations/fr.json index fe507462e8d..a86df352ff6 100644 --- a/homeassistant/components/netgear/translations/fr.json +++ b/homeassistant/components/netgear/translations/fr.json @@ -11,12 +11,9 @@ "data": { "host": "H\u00f4te (facultatif)", "password": "Mot de passe", - "port": "Port (facultatif)", - "ssl": "Utilise un certificat SSL", "username": "Nom d'utilisateur (facultatif)" }, - "description": "H\u00f4te par d\u00e9faut\u00a0: {host}\nNom d'utilisateur par d\u00e9faut\u00a0: {username}", - "title": "Netgear" + "description": "H\u00f4te par d\u00e9faut\u00a0: {host}\nNom d'utilisateur par d\u00e9faut\u00a0: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Consid\u00e9rez le temps pass\u00e9 \u00e0 la maison (secondes)" }, - "description": "Sp\u00e9cifiez les param\u00e8tres optionnels", - "title": "Netgear" + "description": "Sp\u00e9cifiez les param\u00e8tres optionnels" } } } diff --git a/homeassistant/components/netgear/translations/he.json b/homeassistant/components/netgear/translations/he.json index f1f42b6c771..e1aa083fcb8 100644 --- a/homeassistant/components/netgear/translations/he.json +++ b/homeassistant/components/netgear/translations/he.json @@ -8,18 +8,8 @@ "data": { "host": "\u05de\u05d0\u05e8\u05d7 (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "port": "\u05e4\u05ea\u05d7\u05d4 (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)", - "ssl": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d0\u05d9\u05e9\u05d5\u05e8 SSL", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9 (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)" - }, - "title": "Netgear" - } - } - }, - "options": { - "step": { - "init": { - "title": "Netgear" + } } } } diff --git a/homeassistant/components/netgear/translations/hu.json b/homeassistant/components/netgear/translations/hu.json index cf8d31ec389..4432e08a508 100644 --- a/homeassistant/components/netgear/translations/hu.json +++ b/homeassistant/components/netgear/translations/hu.json @@ -11,12 +11,9 @@ "data": { "host": "C\u00edm (nem k\u00f6telez\u0151)", "password": "Jelsz\u00f3", - "port": "Port (nem k\u00f6telez\u0151)", - "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "username": "Felhaszn\u00e1l\u00f3n\u00e9v (nem k\u00f6telez\u0151)" }, - "description": "Alap\u00e9rtelmezett c\u00edm: {host}\nAlap\u00e9rtelmezett felhaszn\u00e1l\u00f3n\u00e9v: {username}", - "title": "Netgear" + "description": "Alap\u00e9rtelmezett c\u00edm: {host}\nAlap\u00e9rtelmezett felhaszn\u00e1l\u00f3n\u00e9v: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Otthoni \u00e1llapotnak tekint\u00e9s (m\u00e1sodperc)" }, - "description": "Opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1sa", - "title": "Netgear" + "description": "Opcion\u00e1lis be\u00e1ll\u00edt\u00e1sok megad\u00e1sa" } } } diff --git a/homeassistant/components/netgear/translations/id.json b/homeassistant/components/netgear/translations/id.json index 7f3eb3a0796..589459e180b 100644 --- a/homeassistant/components/netgear/translations/id.json +++ b/homeassistant/components/netgear/translations/id.json @@ -11,12 +11,9 @@ "data": { "host": "Host (Opsional)", "password": "Kata Sandi", - "port": "Port (Opsional)", - "ssl": "Menggunakan sertifikat SSL", "username": "Nama Pengguna (Opsional)" }, - "description": "Host default: {host}\nNama pengguna default: {username}", - "title": "Netgear" + "description": "Host default: {host}\nNama pengguna default: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Pertimbangan waktu sebagai di rumah (detik)" }, - "description": "Tentukan pengaturan opsional", - "title": "Netgear" + "description": "Tentukan pengaturan opsional" } } } diff --git a/homeassistant/components/netgear/translations/it.json b/homeassistant/components/netgear/translations/it.json index 15237e3a16a..28dc49326d2 100644 --- a/homeassistant/components/netgear/translations/it.json +++ b/homeassistant/components/netgear/translations/it.json @@ -11,12 +11,9 @@ "data": { "host": "Host (Facoltativo)", "password": "Password", - "port": "Porta (Facoltativo)", - "ssl": "Utilizza un certificato SSL", "username": "Nome utente (Facoltativo)" }, - "description": "Host predefinito: {host}\nNome utente predefinito: {username}", - "title": "Netgear" + "description": "Host predefinito: {host}\nNome utente predefinito: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Considera il tempo in casa (secondi)" }, - "description": "Specifica le impostazioni opzionali", - "title": "Netgear" + "description": "Specifica le impostazioni opzionali" } } } diff --git a/homeassistant/components/netgear/translations/ja.json b/homeassistant/components/netgear/translations/ja.json index a1d795bca9f..4402afcb92d 100644 --- a/homeassistant/components/netgear/translations/ja.json +++ b/homeassistant/components/netgear/translations/ja.json @@ -11,12 +11,9 @@ "data": { "host": "\u30db\u30b9\u30c8(\u30aa\u30d7\u30b7\u30e7\u30f3)", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "port": "\u30dd\u30fc\u30c8(\u30aa\u30d7\u30b7\u30e7\u30f3)", - "ssl": "SSL\u8a3c\u660e\u66f8\u3092\u4f7f\u7528\u3059\u308b", "username": "\u30e6\u30fc\u30b6\u30fc\u540d(\u30aa\u30d7\u30b7\u30e7\u30f3)" }, - "description": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30db\u30b9\u30c8: {host}\n\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30dd\u30fc\u30c8: {port}\n\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30e6\u30fc\u30b6\u30fc\u540d: {username}", - "title": "Netgear" + "description": "\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30db\u30b9\u30c8: {host}\n\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30dd\u30fc\u30c8: {port}\n\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30e6\u30fc\u30b6\u30fc\u540d: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "\u30db\u30fc\u30e0\u30bf\u30a4\u30e0\u3092\u8003\u616e\u3059\u308b(\u79d2)" }, - "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a", - "title": "Netgear" + "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a" } } } diff --git a/homeassistant/components/netgear/translations/nl.json b/homeassistant/components/netgear/translations/nl.json index 1d0ae357a8f..95e824ef591 100644 --- a/homeassistant/components/netgear/translations/nl.json +++ b/homeassistant/components/netgear/translations/nl.json @@ -11,12 +11,9 @@ "data": { "host": "Host (optioneel)", "password": "Wachtwoord", - "port": "Poort (optioneel)", - "ssl": "Gebruikt een SSL certificaat", "username": "Gebruikersnaam (optioneel)" }, - "description": "Standaardhost: {host}\n Standaard gebruikersnaam: {username}", - "title": "Netgear" + "description": "Standaardhost: {host}\n Standaard gebruikersnaam: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Aantal seconden dat wordt gewacht voordat een apparaat als afwezig wordt beschouwd" }, - "description": "Optionele instellingen opgeven", - "title": "Netgear" + "description": "Optionele instellingen opgeven" } } } diff --git a/homeassistant/components/netgear/translations/no.json b/homeassistant/components/netgear/translations/no.json index 73735f3e91a..65b90889a20 100644 --- a/homeassistant/components/netgear/translations/no.json +++ b/homeassistant/components/netgear/translations/no.json @@ -11,12 +11,9 @@ "data": { "host": "Vert (valgfritt)", "password": "Passord", - "port": "Port (valgfritt)", - "ssl": "Bruker et SSL-sertifikat", "username": "Brukernavn (Valgfritt)" }, - "description": "Standard vert: {host}\n Standard brukernavn: {username}", - "title": "Netgear" + "description": "Standard vert: {host}\n Standard brukernavn: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Vurder hjemmetid (sekunder)" }, - "description": "Spesifiser valgfrie innstillinger", - "title": "Netgear" + "description": "Spesifiser valgfrie innstillinger" } } } diff --git a/homeassistant/components/netgear/translations/pl.json b/homeassistant/components/netgear/translations/pl.json index 0f3a6ad3cde..a3ec916efcc 100644 --- a/homeassistant/components/netgear/translations/pl.json +++ b/homeassistant/components/netgear/translations/pl.json @@ -11,12 +11,9 @@ "data": { "host": "Nazwa hosta lub adres IP (opcjonalnie)", "password": "Has\u0142o", - "port": "Port (opcjonalnie)", - "ssl": "Certyfikat SSL", "username": "Nazwa u\u017cytkownika (opcjonalnie)" }, - "description": "Domy\u015blne IP lub nazwa hosta: {host}\nDomy\u015blna nazwa u\u017cytkownika: {username}", - "title": "Netgear" + "description": "Domy\u015blne IP lub nazwa hosta: {host}\nDomy\u015blna nazwa u\u017cytkownika: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Czas przed oznaczeniem \"w domu\" (w sekundach)" }, - "description": "Okre\u015bl opcjonalne ustawienia", - "title": "Netgear" + "description": "Okre\u015bl opcjonalne ustawienia" } } } diff --git a/homeassistant/components/netgear/translations/pt-BR.json b/homeassistant/components/netgear/translations/pt-BR.json index 66ffd692374..ac52b1d18c3 100644 --- a/homeassistant/components/netgear/translations/pt-BR.json +++ b/homeassistant/components/netgear/translations/pt-BR.json @@ -11,12 +11,9 @@ "data": { "host": "Nome do host (Opcional)", "password": "Senha", - "port": "Porta (Opcional)", - "ssl": "Usar um certificado SSL", "username": "Usu\u00e1rio (Opcional)" }, - "description": "Host padr\u00e3o: {host}\n Nome de usu\u00e1rio padr\u00e3o: {username}", - "title": "Netgear" + "description": "Host padr\u00e3o: {host}\n Nome de usu\u00e1rio padr\u00e3o: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Considere o tempo de casa (segundos)" }, - "description": "Especifique configura\u00e7\u00f5es opcionais", - "title": "Netgear" + "description": "Especifique configura\u00e7\u00f5es opcionais" } } } diff --git a/homeassistant/components/netgear/translations/ru.json b/homeassistant/components/netgear/translations/ru.json index e6e5f9a008a..d2fd1f500f5 100644 --- a/homeassistant/components/netgear/translations/ru.json +++ b/homeassistant/components/netgear/translations/ru.json @@ -11,12 +11,9 @@ "data": { "host": "\u0425\u043e\u0441\u0442 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "port": "\u041f\u043e\u0440\u0442 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", - "ssl": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" }, - "description": "\u0425\u043e\u0441\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: {host}\n\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: {username}", - "title": "Netgear" + "description": "\u0425\u043e\u0441\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: {host}\n\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "\u0412\u0440\u0435\u043c\u044f, \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0447\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u043c\u0430 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", - "title": "Netgear" + "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" } } } diff --git a/homeassistant/components/netgear/translations/sk.json b/homeassistant/components/netgear/translations/sk.json deleted file mode 100644 index ea85ad39b4a..00000000000 --- a/homeassistant/components/netgear/translations/sk.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "port": "Port (volite\u013en\u00fd)" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/netgear/translations/tr.json b/homeassistant/components/netgear/translations/tr.json index b29a9f075aa..2f49affd564 100644 --- a/homeassistant/components/netgear/translations/tr.json +++ b/homeassistant/components/netgear/translations/tr.json @@ -11,12 +11,9 @@ "data": { "host": "Sunucu (\u0130ste\u011fe ba\u011fl\u0131)", "password": "Parola", - "port": "Port (\u0130ste\u011fe ba\u011fl\u0131)", - "ssl": "SSL sertifikas\u0131 kullan\u0131r", "username": "Kullan\u0131c\u0131 Ad\u0131 (\u0130ste\u011fe ba\u011fl\u0131)" }, - "description": "Varsay\u0131lan sunucu: {host}\nVarsay\u0131lan kullan\u0131c\u0131 ad\u0131: {username}", - "title": "Netgear" + "description": "Varsay\u0131lan sunucu: {host}\nVarsay\u0131lan kullan\u0131c\u0131 ad\u0131: {username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "Ev s\u00fcresini g\u00f6z \u00f6n\u00fcnde bulundurun (saniye)" }, - "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin", - "title": "Netgear" + "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin" } } } diff --git a/homeassistant/components/netgear/translations/zh-Hans.json b/homeassistant/components/netgear/translations/zh-Hans.json index dd7b165d2d4..4048634787b 100644 --- a/homeassistant/components/netgear/translations/zh-Hans.json +++ b/homeassistant/components/netgear/translations/zh-Hans.json @@ -11,20 +11,16 @@ "data": { "host": "\u4e3b\u673a\u5730\u5740 (\u53ef\u9009)", "password": "\u5bc6\u7801", - "port": "\u7aef\u53e3 (\u53ef\u9009)", - "ssl": "\u4f7f\u7528 SSL \u51ed\u8bc1\u767b\u5f55", "username": "\u7528\u6237\u540d (\u53ef\u9009)" }, - "description": "\u9ed8\u8ba4\u4e3b\u673a\u5730\u5740: {host}\n\u9ed8\u8ba4\u7528\u6237\u540d: {username}", - "title": "\u7f51\u4ef6\u8def\u7531\u5668" + "description": "\u9ed8\u8ba4\u4e3b\u673a\u5730\u5740: {host}\n\u9ed8\u8ba4\u7528\u6237\u540d: {username}" } } }, "options": { "step": { "init": { - "description": "\u6307\u5b9a\u53ef\u9009\u8bbe\u7f6e", - "title": "\u7f51\u4ef6\u8def\u7531\u5668" + "description": "\u6307\u5b9a\u53ef\u9009\u8bbe\u7f6e" } } } diff --git a/homeassistant/components/netgear/translations/zh-Hant.json b/homeassistant/components/netgear/translations/zh-Hant.json index 39b481f85b9..6a2866bc22d 100644 --- a/homeassistant/components/netgear/translations/zh-Hant.json +++ b/homeassistant/components/netgear/translations/zh-Hant.json @@ -11,12 +11,9 @@ "data": { "host": "\u4e3b\u6a5f\u7aef\uff08\u9078\u9805\uff09", "password": "\u5bc6\u78bc", - "port": "\u901a\u8a0a\u57e0\uff08\u9078\u9805\uff09", - "ssl": "\u4f7f\u7528 SSL \u8a8d\u8b49", "username": "\u4f7f\u7528\u8005\u540d\u7a31\uff08\u9078\u9805\uff09" }, - "description": "\u9810\u8a2d\u4e3b\u6a5f\u7aef\uff1a{host}\n\u9810\u8a2d\u4f7f\u7528\u8005\u540d\u7a31\uff1a{username}", - "title": "Netgear" + "description": "\u9810\u8a2d\u4e3b\u6a5f\u7aef\uff1a{host}\n\u9810\u8a2d\u4f7f\u7528\u8005\u540d\u7a31\uff1a{username}" } } }, @@ -26,8 +23,7 @@ "data": { "consider_home": "\u5224\u5b9a\u5728\u5bb6\u6642\u9593\uff08\u79d2\uff09" }, - "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a", - "title": "Netgear" + "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a" } } } diff --git a/homeassistant/components/nexia/translations/bg.json b/homeassistant/components/nexia/translations/bg.json index 0c8cf4f44a6..78264e2adbd 100644 --- a/homeassistant/components/nexia/translations/bg.json +++ b/homeassistant/components/nexia/translations/bg.json @@ -4,8 +4,7 @@ "user": { "data": { "brand": "\u041c\u0430\u0440\u043a\u0430" - }, - "title": "\u0421\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/ca.json b/homeassistant/components/nexia/translations/ca.json index e0f0c87f60f..f369d64bde9 100644 --- a/homeassistant/components/nexia/translations/ca.json +++ b/homeassistant/components/nexia/translations/ca.json @@ -14,8 +14,7 @@ "brand": "Marca", "password": "Contrasenya", "username": "Nom d'usuari" - }, - "title": "Connexi\u00f3 amb mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/cs.json b/homeassistant/components/nexia/translations/cs.json index 0196d0d03ed..dc27752e935 100644 --- a/homeassistant/components/nexia/translations/cs.json +++ b/homeassistant/components/nexia/translations/cs.json @@ -13,8 +13,7 @@ "data": { "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, - "title": "P\u0159ipojen\u00ed k mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/de.json b/homeassistant/components/nexia/translations/de.json index 94094f08037..b2eeebb2f17 100644 --- a/homeassistant/components/nexia/translations/de.json +++ b/homeassistant/components/nexia/translations/de.json @@ -14,8 +14,7 @@ "brand": "Marke", "password": "Passwort", "username": "Benutzername" - }, - "title": "Stelle eine Verbindung zu mynexia.com her" + } } } } diff --git a/homeassistant/components/nexia/translations/el.json b/homeassistant/components/nexia/translations/el.json index 47d2a624cd3..4db4ad008ed 100644 --- a/homeassistant/components/nexia/translations/el.json +++ b/homeassistant/components/nexia/translations/el.json @@ -14,8 +14,7 @@ "brand": "\u039c\u03ac\u03c1\u03ba\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03bc\u03b5 mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/en.json b/homeassistant/components/nexia/translations/en.json index 20b0a137970..050db24e0cf 100644 --- a/homeassistant/components/nexia/translations/en.json +++ b/homeassistant/components/nexia/translations/en.json @@ -14,8 +14,7 @@ "brand": "Brand", "password": "Password", "username": "Username" - }, - "title": "Connect to mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/es-419.json b/homeassistant/components/nexia/translations/es-419.json index e2f04c7d4b4..ea277d080cc 100644 --- a/homeassistant/components/nexia/translations/es-419.json +++ b/homeassistant/components/nexia/translations/es-419.json @@ -13,8 +13,7 @@ "data": { "password": "Contrase\u00f1a", "username": "Nombre de usuario" - }, - "title": "Con\u00e9ctese a mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/es.json b/homeassistant/components/nexia/translations/es.json index 4306a8795b8..ee89d9db6f5 100644 --- a/homeassistant/components/nexia/translations/es.json +++ b/homeassistant/components/nexia/translations/es.json @@ -14,8 +14,7 @@ "brand": "Marca", "password": "Contrase\u00f1a", "username": "Usuario" - }, - "title": "Conectar con mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/et.json b/homeassistant/components/nexia/translations/et.json index 2f9348c1eed..7e24abee556 100644 --- a/homeassistant/components/nexia/translations/et.json +++ b/homeassistant/components/nexia/translations/et.json @@ -14,8 +14,7 @@ "brand": "Tootja", "password": "Salas\u00f5na", "username": "Kasutajanimi" - }, - "title": "\u00dchendu mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/fr.json b/homeassistant/components/nexia/translations/fr.json index 3955d07ff7a..be31f08787b 100644 --- a/homeassistant/components/nexia/translations/fr.json +++ b/homeassistant/components/nexia/translations/fr.json @@ -14,8 +14,7 @@ "brand": "Marque", "password": "Mot de passe", "username": "Nom d'utilisateur" - }, - "title": "Se connecter \u00e0 mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/he.json b/homeassistant/components/nexia/translations/he.json index 7f9efa5b24e..d27edd983c2 100644 --- a/homeassistant/components/nexia/translations/he.json +++ b/homeassistant/components/nexia/translations/he.json @@ -14,8 +14,7 @@ "brand": "\u05de\u05d5\u05ea\u05d2", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - }, - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc-mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/hu.json b/homeassistant/components/nexia/translations/hu.json index ac85fec6456..f9178aafa07 100644 --- a/homeassistant/components/nexia/translations/hu.json +++ b/homeassistant/components/nexia/translations/hu.json @@ -14,8 +14,7 @@ "brand": "M\u00e1rka", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "title": "Csatlakoz\u00e1s a mynexia.com-hoz" + } } } } diff --git a/homeassistant/components/nexia/translations/id.json b/homeassistant/components/nexia/translations/id.json index 0600315fc78..1dba99e3984 100644 --- a/homeassistant/components/nexia/translations/id.json +++ b/homeassistant/components/nexia/translations/id.json @@ -14,8 +14,7 @@ "brand": "Merek", "password": "Kata Sandi", "username": "Nama Pengguna" - }, - "title": "Hubungkan ke mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/it.json b/homeassistant/components/nexia/translations/it.json index 65d06c3c8f0..dad681085de 100644 --- a/homeassistant/components/nexia/translations/it.json +++ b/homeassistant/components/nexia/translations/it.json @@ -14,8 +14,7 @@ "brand": "Marca", "password": "Password", "username": "Nome utente" - }, - "title": "Connettersi a mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/ja.json b/homeassistant/components/nexia/translations/ja.json index f06358ef78c..b80fe6c7cd2 100644 --- a/homeassistant/components/nexia/translations/ja.json +++ b/homeassistant/components/nexia/translations/ja.json @@ -14,8 +14,7 @@ "brand": "\u30d6\u30e9\u30f3\u30c9", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "title": "mynexia.com\u306b\u63a5\u7d9a" + } } } } diff --git a/homeassistant/components/nexia/translations/ko.json b/homeassistant/components/nexia/translations/ko.json index 4bd1589e4e8..94261de9637 100644 --- a/homeassistant/components/nexia/translations/ko.json +++ b/homeassistant/components/nexia/translations/ko.json @@ -13,8 +13,7 @@ "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - }, - "title": "mynexia.com\uc5d0 \uc5f0\uacb0\ud558\uae30" + } } } } diff --git a/homeassistant/components/nexia/translations/lb.json b/homeassistant/components/nexia/translations/lb.json index fa9b186ccbf..60b1bb6f305 100644 --- a/homeassistant/components/nexia/translations/lb.json +++ b/homeassistant/components/nexia/translations/lb.json @@ -13,8 +13,7 @@ "data": { "password": "Passwuert", "username": "Benotzernumm" - }, - "title": "Mat mynexia.com verbannen" + } } } } diff --git a/homeassistant/components/nexia/translations/nl.json b/homeassistant/components/nexia/translations/nl.json index 14efdfcd221..106dcc85cc7 100644 --- a/homeassistant/components/nexia/translations/nl.json +++ b/homeassistant/components/nexia/translations/nl.json @@ -14,8 +14,7 @@ "brand": "Brand", "password": "Wachtwoord", "username": "Gebruikersnaam" - }, - "title": "Verbinding maken met mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/no.json b/homeassistant/components/nexia/translations/no.json index 4533b94e48e..6cb06d0c827 100644 --- a/homeassistant/components/nexia/translations/no.json +++ b/homeassistant/components/nexia/translations/no.json @@ -14,8 +14,7 @@ "brand": "Merke", "password": "Passord", "username": "Brukernavn" - }, - "title": "Koble til mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/pl.json b/homeassistant/components/nexia/translations/pl.json index 6e40e21a7f0..8f4021ec9d8 100644 --- a/homeassistant/components/nexia/translations/pl.json +++ b/homeassistant/components/nexia/translations/pl.json @@ -14,8 +14,7 @@ "brand": "Marka", "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" - }, - "title": "Po\u0142\u0105czenie z mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/pt-BR.json b/homeassistant/components/nexia/translations/pt-BR.json index e9cce64aef9..01a830027fa 100644 --- a/homeassistant/components/nexia/translations/pt-BR.json +++ b/homeassistant/components/nexia/translations/pt-BR.json @@ -14,8 +14,7 @@ "brand": "Marca", "password": "Senha", "username": "Usu\u00e1rio" - }, - "title": "Conecte-se a mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/ru.json b/homeassistant/components/nexia/translations/ru.json index b5572c9f2da..e72388f6c9f 100644 --- a/homeassistant/components/nexia/translations/ru.json +++ b/homeassistant/components/nexia/translations/ru.json @@ -14,8 +14,7 @@ "brand": "\u041c\u0430\u0440\u043a\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/sl.json b/homeassistant/components/nexia/translations/sl.json index 55c234fbde3..a1e926075f7 100644 --- a/homeassistant/components/nexia/translations/sl.json +++ b/homeassistant/components/nexia/translations/sl.json @@ -13,8 +13,7 @@ "data": { "password": "Geslo", "username": "Uporabni\u0161ko ime" - }, - "title": "Pove\u017eite se z mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/sv.json b/homeassistant/components/nexia/translations/sv.json index 60044361f65..9cfd620ac73 100644 --- a/homeassistant/components/nexia/translations/sv.json +++ b/homeassistant/components/nexia/translations/sv.json @@ -10,8 +10,7 @@ "data": { "password": "L\u00f6senord", "username": "Anv\u00e4ndarnamn" - }, - "title": "Anslut till mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/tr.json b/homeassistant/components/nexia/translations/tr.json index 8918bcd8f5e..11bacdf69df 100644 --- a/homeassistant/components/nexia/translations/tr.json +++ b/homeassistant/components/nexia/translations/tr.json @@ -14,8 +14,7 @@ "brand": "Marka", "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "title": "Mynexia.com'a ba\u011flan\u0131n" + } } } } diff --git a/homeassistant/components/nexia/translations/uk.json b/homeassistant/components/nexia/translations/uk.json index 8cb2aec836a..49bceaa3f6e 100644 --- a/homeassistant/components/nexia/translations/uk.json +++ b/homeassistant/components/nexia/translations/uk.json @@ -13,8 +13,7 @@ "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - }, - "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e mynexia.com" + } } } } diff --git a/homeassistant/components/nexia/translations/zh-Hant.json b/homeassistant/components/nexia/translations/zh-Hant.json index c066a433d1b..0924ebf7259 100644 --- a/homeassistant/components/nexia/translations/zh-Hant.json +++ b/homeassistant/components/nexia/translations/zh-Hant.json @@ -14,8 +14,7 @@ "brand": "\u54c1\u724c", "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "title": "\u9023\u7dda\u81f3 mynexia.com" + } } } } diff --git a/homeassistant/components/nfandroidtv/translations/bg.json b/homeassistant/components/nfandroidtv/translations/bg.json index 484dd2b98e3..5b58777a0e2 100644 --- a/homeassistant/components/nfandroidtv/translations/bg.json +++ b/homeassistant/components/nfandroidtv/translations/bg.json @@ -12,8 +12,7 @@ "data": { "host": "\u0425\u043e\u0441\u0442", "name": "\u0418\u043c\u0435" - }, - "title": "\u0418\u0437\u0432\u0435\u0441\u0442\u0438\u044f \u0437\u0430 Android TV / Fire TV" + } } } } diff --git a/homeassistant/components/nfandroidtv/translations/ca.json b/homeassistant/components/nfandroidtv/translations/ca.json index 0eda4938d9d..90b910617f9 100644 --- a/homeassistant/components/nfandroidtv/translations/ca.json +++ b/homeassistant/components/nfandroidtv/translations/ca.json @@ -13,8 +13,7 @@ "host": "Amfitri\u00f3", "name": "Nom" }, - "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits.", - "title": "Notificacions per a Android TV / Fire TV" + "description": "Consulta la documentaci\u00f3 per assegurar-te que compleixes tots els requisits." } } } diff --git a/homeassistant/components/nfandroidtv/translations/de.json b/homeassistant/components/nfandroidtv/translations/de.json index d9df9058d13..fef64459261 100644 --- a/homeassistant/components/nfandroidtv/translations/de.json +++ b/homeassistant/components/nfandroidtv/translations/de.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Name" }, - "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind.", - "title": "Benachrichtigungen f\u00fcr Android TV / Fire TV" + "description": "Bitte lies die Dokumentation, um sicherzustellen, dass alle Anforderungen erf\u00fcllt sind." } } } diff --git a/homeassistant/components/nfandroidtv/translations/el.json b/homeassistant/components/nfandroidtv/translations/el.json index b95ec27a3bf..8512834e2b8 100644 --- a/homeassistant/components/nfandroidtv/translations/el.json +++ b/homeassistant/components/nfandroidtv/translations/el.json @@ -13,8 +13,7 @@ "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, - "description": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u0395\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b3\u03b9\u03b1 Android TV. \n\n \u0393\u03b9\u03b1 Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n \u0393\u03b9\u03b1 Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\n \u0398\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2 (\u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2) \u03b5\u03af\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u0395\u03ac\u03bd \u03cc\u03c7\u03b9, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b5\u03af \u03c4\u03b5\u03bb\u03b9\u03ba\u03ac \u03bc\u03b7 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7.", - "title": "\u0395\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b3\u03b9\u03b1 Android TV / Fire TV" + "description": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u0395\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b3\u03b9\u03b1 Android TV. \n\n \u0393\u03b9\u03b1 Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\n \u0393\u03b9\u03b1 Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK \n\n \u0398\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03b5\u03af\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03c1\u03ac\u03c4\u03b7\u03c3\u03b7 DHCP \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2 (\u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b5\u03b3\u03c7\u03b5\u03b9\u03c1\u03af\u03b4\u03b9\u03bf \u03c7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae \u03c3\u03b1\u03c2) \u03b5\u03af\u03c4\u03b5 \u03bc\u03b9\u03b1 \u03c3\u03c4\u03b1\u03c4\u03b9\u03ba\u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae. \u0395\u03ac\u03bd \u03cc\u03c7\u03b9, \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03b1 \u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b5\u03af \u03c4\u03b5\u03bb\u03b9\u03ba\u03ac \u03bc\u03b7 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7." } } } diff --git a/homeassistant/components/nfandroidtv/translations/en.json b/homeassistant/components/nfandroidtv/translations/en.json index 3c1383b91b8..4164a968c81 100644 --- a/homeassistant/components/nfandroidtv/translations/en.json +++ b/homeassistant/components/nfandroidtv/translations/en.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Name" }, - "description": "Please refer to the documentation to make sure all requirements are met.", - "title": "Notifications for Android TV / Fire TV" + "description": "Please refer to the documentation to make sure all requirements are met." } } } diff --git a/homeassistant/components/nfandroidtv/translations/es.json b/homeassistant/components/nfandroidtv/translations/es.json index 880835cfb1e..e382855479f 100644 --- a/homeassistant/components/nfandroidtv/translations/es.json +++ b/homeassistant/components/nfandroidtv/translations/es.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Nombre" }, - "description": "Esta integraci\u00f3n requiere la aplicaci\u00f3n de Notificaciones para Android TV.\n\nPara Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPara Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nDebes configurar una reserva DHCP en su router (consulta el manual de usuario de tu router) o una direcci\u00f3n IP est\u00e1tica en el dispositivo. Si no, el dispositivo acabar\u00e1 por no estar disponible.", - "title": "Notificaciones para Android TV / Fire TV" + "description": "Esta integraci\u00f3n requiere la aplicaci\u00f3n de Notificaciones para Android TV.\n\nPara Android TV: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nPara Fire TV: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\nDebes configurar una reserva DHCP en su router (consulta el manual de usuario de tu router) o una direcci\u00f3n IP est\u00e1tica en el dispositivo. Si no, el dispositivo acabar\u00e1 por no estar disponible." } } } diff --git a/homeassistant/components/nfandroidtv/translations/et.json b/homeassistant/components/nfandroidtv/translations/et.json index f567795b608..4e5ae85ca00 100644 --- a/homeassistant/components/nfandroidtv/translations/et.json +++ b/homeassistant/components/nfandroidtv/translations/et.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Nimi" }, - "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud.", - "title": "Android TV / Fire TV teavitused" + "description": "Vaata dokumentatsiooni, et veenduda, et k\u00f5ik n\u00f5uded on t\u00e4idetud." } } } diff --git a/homeassistant/components/nfandroidtv/translations/fr.json b/homeassistant/components/nfandroidtv/translations/fr.json index 7c69bbc6ac0..30d592c6edf 100644 --- a/homeassistant/components/nfandroidtv/translations/fr.json +++ b/homeassistant/components/nfandroidtv/translations/fr.json @@ -13,8 +13,7 @@ "host": "H\u00f4te", "name": "Nom" }, - "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es.", - "title": "Notifications pour Android TV / Fire TV" + "description": "Veuillez consulter la documentation afin de vous assurer que toutes les conditions sont respect\u00e9es." } } } diff --git a/homeassistant/components/nfandroidtv/translations/he.json b/homeassistant/components/nfandroidtv/translations/he.json index 65eefcd05b4..cad2cfc04a3 100644 --- a/homeassistant/components/nfandroidtv/translations/he.json +++ b/homeassistant/components/nfandroidtv/translations/he.json @@ -13,8 +13,7 @@ "host": "\u05de\u05d0\u05e8\u05d7", "name": "\u05e9\u05dd" }, - "description": "\u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05ea\u05d9\u05e2\u05d5\u05d3 \u05db\u05d3\u05d9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05db\u05dc \u05d4\u05d3\u05e8\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05e7\u05d9\u05d9\u05de\u05d5\u05ea.", - "title": "\u05d4\u05ea\u05e8\u05d0\u05d5\u05ea \u05e2\u05d1\u05d5\u05e8 \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d0\u05e0\u05d3\u05e8\u05d5\u05d0\u05d9\u05d3 / \u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d9\u05ea \u05d0\u05de\u05d6\u05d5\u05df" + "description": "\u05e0\u05d0 \u05dc\u05e2\u05d9\u05d9\u05df \u05d1\u05ea\u05d9\u05e2\u05d5\u05d3 \u05db\u05d3\u05d9 \u05dc\u05d5\u05d5\u05d3\u05d0 \u05e9\u05db\u05dc \u05d4\u05d3\u05e8\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05e7\u05d9\u05d9\u05de\u05d5\u05ea." } } } diff --git a/homeassistant/components/nfandroidtv/translations/hu.json b/homeassistant/components/nfandroidtv/translations/hu.json index 0c8d0567483..65cd2bebc01 100644 --- a/homeassistant/components/nfandroidtv/translations/hu.json +++ b/homeassistant/components/nfandroidtv/translations/hu.json @@ -13,8 +13,7 @@ "host": "C\u00edm", "name": "Elnevez\u00e9s" }, - "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl.", - "title": "\u00c9rtes\u00edt\u00e9sek Android TV / Fire TV eset\u00e9n" + "description": "K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t, hogy megbizonyosodjon arr\u00f3l, hogy minden k\u00f6vetelm\u00e9ny teljes\u00fcl." } } } diff --git a/homeassistant/components/nfandroidtv/translations/id.json b/homeassistant/components/nfandroidtv/translations/id.json index de4ded1e98a..4495908ef84 100644 --- a/homeassistant/components/nfandroidtv/translations/id.json +++ b/homeassistant/components/nfandroidtv/translations/id.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Nama" }, - "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi.", - "title": "Notifikasi untuk Android TV/Fire TV" + "description": "Rujuk ke dokumentasi untuk memastikan semua persyaratan terpenuhi." } } } diff --git a/homeassistant/components/nfandroidtv/translations/it.json b/homeassistant/components/nfandroidtv/translations/it.json index 83eea49b419..4fb88f80726 100644 --- a/homeassistant/components/nfandroidtv/translations/it.json +++ b/homeassistant/components/nfandroidtv/translations/it.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Nome" }, - "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti.", - "title": "Notifiche per Android TV / Fire TV" + "description": "Fai riferimento alla documentazione per assicurarti che tutti i requisiti siano soddisfatti." } } } diff --git a/homeassistant/components/nfandroidtv/translations/ja.json b/homeassistant/components/nfandroidtv/translations/ja.json index c3cd4fee235..fff28117234 100644 --- a/homeassistant/components/nfandroidtv/translations/ja.json +++ b/homeassistant/components/nfandroidtv/translations/ja.json @@ -13,8 +13,7 @@ "host": "\u30db\u30b9\u30c8", "name": "\u540d\u524d" }, - "description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306b\u306f\u3001AndroidTV\u30a2\u30d7\u30ea\u306e\u901a\u77e5\u304c\u5fc5\u8981\u3067\u3059\u3002 \n\nAndroid TV\u306e\u5834\u5408: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFire TV\u306e\u5834\u5408: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\n\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04((DHCP reservation)\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167))\u307e\u305f\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306b\u9759\u7684IP\u30a2\u30c9\u30ec\u30b9\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3067\u306a\u3044\u5834\u5408\u3001\u30c7\u30d0\u30a4\u30b9\u306f\u6700\u7d42\u7684\u306b\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002", - "title": "Android TV / Fire TV\u306e\u901a\u77e5" + "description": "\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306b\u306f\u3001AndroidTV\u30a2\u30d7\u30ea\u306e\u901a\u77e5\u304c\u5fc5\u8981\u3067\u3059\u3002 \n\nAndroid TV\u306e\u5834\u5408: https://play.google.com/store/apps/details?id=de.cyberdream.androidtv.notifications.google\nFire TV\u306e\u5834\u5408: https://www.amazon.com/Christian-Fees-Notifications-for-Fire/dp/B00OESCXEK\n\n\u30eb\u30fc\u30bf\u30fc\u306eDHCP\u4e88\u7d04((DHCP reservation)\u30eb\u30fc\u30bf\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30de\u30cb\u30e5\u30a2\u30eb\u3092\u53c2\u7167))\u307e\u305f\u306f\u3001\u30c7\u30d0\u30a4\u30b9\u306b\u9759\u7684IP\u30a2\u30c9\u30ec\u30b9\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u3046\u3067\u306a\u3044\u5834\u5408\u3001\u30c7\u30d0\u30a4\u30b9\u306f\u6700\u7d42\u7684\u306b\u4f7f\u7528\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u3002" } } } diff --git a/homeassistant/components/nfandroidtv/translations/nl.json b/homeassistant/components/nfandroidtv/translations/nl.json index 10c9f44a94a..5a2342b740d 100644 --- a/homeassistant/components/nfandroidtv/translations/nl.json +++ b/homeassistant/components/nfandroidtv/translations/nl.json @@ -13,8 +13,7 @@ "host": "Host", "name": "Naam" }, - "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten is voldaan.", - "title": "Meldingen voor Android TV / Fire TV" + "description": "Raadpleeg de documentatie om er zeker van te zijn dat aan alle vereisten is voldaan." } } } diff --git a/homeassistant/components/nfandroidtv/translations/no.json b/homeassistant/components/nfandroidtv/translations/no.json index 8d59ca40b0d..73c1a98538f 100644 --- a/homeassistant/components/nfandroidtv/translations/no.json +++ b/homeassistant/components/nfandroidtv/translations/no.json @@ -13,8 +13,7 @@ "host": "Vert", "name": "Navn" }, - "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt.", - "title": "Varsler for Android TV / Fire TV" + "description": "Se dokumentasjonen for \u00e5 sikre at alle krav er oppfylt." } } } diff --git a/homeassistant/components/nfandroidtv/translations/pl.json b/homeassistant/components/nfandroidtv/translations/pl.json index a42335ceb10..a050ee07a90 100644 --- a/homeassistant/components/nfandroidtv/translations/pl.json +++ b/homeassistant/components/nfandroidtv/translations/pl.json @@ -13,8 +13,7 @@ "host": "Nazwa hosta lub adres IP", "name": "Nazwa" }, - "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione.", - "title": "Powiadomienia dla Android TV / Fire TV" + "description": "Zapoznaj si\u0119 z dokumentacj\u0105, aby upewni\u0107 si\u0119, \u017ce wszystkie wymagania s\u0105 spe\u0142nione." } } } diff --git a/homeassistant/components/nfandroidtv/translations/pt-BR.json b/homeassistant/components/nfandroidtv/translations/pt-BR.json index f4488408b42..255c67527e9 100644 --- a/homeassistant/components/nfandroidtv/translations/pt-BR.json +++ b/homeassistant/components/nfandroidtv/translations/pt-BR.json @@ -13,8 +13,7 @@ "host": "Nome do host", "name": "Nome" }, - "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos.", - "title": "Notifica\u00e7\u00f5es para Android TV / Fire TV" + "description": "Consulte a documenta\u00e7\u00e3o para garantir que todos os requisitos sejam atendidos." } } } diff --git a/homeassistant/components/nfandroidtv/translations/ru.json b/homeassistant/components/nfandroidtv/translations/ru.json index 12451d4735b..3cd5b134813 100644 --- a/homeassistant/components/nfandroidtv/translations/ru.json +++ b/homeassistant/components/nfandroidtv/translations/ru.json @@ -13,8 +13,7 @@ "host": "\u0425\u043e\u0441\u0442", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b.", - "title": "Notifications for Android TV / Fire TV" + "description": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0431\u043b\u044e\u0434\u0435\u043d\u044b." } } } diff --git a/homeassistant/components/nfandroidtv/translations/tr.json b/homeassistant/components/nfandroidtv/translations/tr.json index 835e61eea66..efb33dafb9c 100644 --- a/homeassistant/components/nfandroidtv/translations/tr.json +++ b/homeassistant/components/nfandroidtv/translations/tr.json @@ -13,8 +13,7 @@ "host": "Sunucu", "name": "Ad" }, - "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n.", - "title": "Android TV / Fire TV i\u00e7in Bildirimler" + "description": "T\u00fcm gereksinimlerin kar\u015f\u0131land\u0131\u011f\u0131ndan emin olmak i\u00e7in l\u00fctfen belgelere bak\u0131n." } } } diff --git a/homeassistant/components/nfandroidtv/translations/zh-Hant.json b/homeassistant/components/nfandroidtv/translations/zh-Hant.json index 755ccdfeec8..f84b329d74f 100644 --- a/homeassistant/components/nfandroidtv/translations/zh-Hant.json +++ b/homeassistant/components/nfandroidtv/translations/zh-Hant.json @@ -13,8 +13,7 @@ "host": "\u4e3b\u6a5f\u7aef", "name": "\u540d\u7a31" }, - "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002", - "title": "Android TV / Fire TV \u901a\u77e5" + "description": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u6240\u6709\u5fc5\u8981\u9700\u6c42\u3002" } } } diff --git a/homeassistant/components/nightscout/translations/ca.json b/homeassistant/components/nightscout/translations/ca.json index 21a472680b4..911c75a19de 100644 --- a/homeassistant/components/nightscout/translations/ca.json +++ b/homeassistant/components/nightscout/translations/ca.json @@ -8,7 +8,6 @@ "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "unknown": "Error inesperat" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/cs.json b/homeassistant/components/nightscout/translations/cs.json index dac121c7fb1..3a4f8bf2b0e 100644 --- a/homeassistant/components/nightscout/translations/cs.json +++ b/homeassistant/components/nightscout/translations/cs.json @@ -8,7 +8,6 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/de.json b/homeassistant/components/nightscout/translations/de.json index 32bc6653af1..93524c79689 100644 --- a/homeassistant/components/nightscout/translations/de.json +++ b/homeassistant/components/nightscout/translations/de.json @@ -8,7 +8,6 @@ "invalid_auth": "Ung\u00fcltige Authentifizierung", "unknown": "Unerwarteter Fehler" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/el.json b/homeassistant/components/nightscout/translations/el.json index c484d715e76..c2359686297 100644 --- a/homeassistant/components/nightscout/translations/el.json +++ b/homeassistant/components/nightscout/translations/el.json @@ -8,7 +8,6 @@ "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/en.json b/homeassistant/components/nightscout/translations/en.json index baec475fc2d..97ce458c8c0 100644 --- a/homeassistant/components/nightscout/translations/en.json +++ b/homeassistant/components/nightscout/translations/en.json @@ -8,7 +8,6 @@ "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/es.json b/homeassistant/components/nightscout/translations/es.json index 1731cd7f48c..d98b8e345b6 100644 --- a/homeassistant/components/nightscout/translations/es.json +++ b/homeassistant/components/nightscout/translations/es.json @@ -8,7 +8,6 @@ "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "unknown": "Error inesperado" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/et.json b/homeassistant/components/nightscout/translations/et.json index 0d00cebb6a5..7521ee633c5 100644 --- a/homeassistant/components/nightscout/translations/et.json +++ b/homeassistant/components/nightscout/translations/et.json @@ -8,7 +8,6 @@ "invalid_auth": "Tuvastamise viga", "unknown": "Tundmatu viga" }, - "flow_title": "", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/fr.json b/homeassistant/components/nightscout/translations/fr.json index 0b99785652d..9c9cd110990 100644 --- a/homeassistant/components/nightscout/translations/fr.json +++ b/homeassistant/components/nightscout/translations/fr.json @@ -8,7 +8,6 @@ "invalid_auth": "Authentification non valide", "unknown": "Erreur inattendue" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/hu.json b/homeassistant/components/nightscout/translations/hu.json index 569a4f3aca9..85f0976d017 100644 --- a/homeassistant/components/nightscout/translations/hu.json +++ b/homeassistant/components/nightscout/translations/hu.json @@ -8,7 +8,6 @@ "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/id.json b/homeassistant/components/nightscout/translations/id.json index 147c3131213..d9a113784da 100644 --- a/homeassistant/components/nightscout/translations/id.json +++ b/homeassistant/components/nightscout/translations/id.json @@ -8,7 +8,6 @@ "invalid_auth": "Autentikasi tidak valid", "unknown": "Kesalahan yang tidak diharapkan" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/it.json b/homeassistant/components/nightscout/translations/it.json index 8a1bfc826e2..c0438787915 100644 --- a/homeassistant/components/nightscout/translations/it.json +++ b/homeassistant/components/nightscout/translations/it.json @@ -8,7 +8,6 @@ "invalid_auth": "Autenticazione non valida", "unknown": "Errore imprevisto" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/ja.json b/homeassistant/components/nightscout/translations/ja.json index d0a88be1985..211113d943c 100644 --- a/homeassistant/components/nightscout/translations/ja.json +++ b/homeassistant/components/nightscout/translations/ja.json @@ -8,7 +8,6 @@ "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/ko.json b/homeassistant/components/nightscout/translations/ko.json index 1146e6926e3..7574e1f6d47 100644 --- a/homeassistant/components/nightscout/translations/ko.json +++ b/homeassistant/components/nightscout/translations/ko.json @@ -8,7 +8,6 @@ "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/lb.json b/homeassistant/components/nightscout/translations/lb.json index cc1f048e84e..b592e209dd8 100644 --- a/homeassistant/components/nightscout/translations/lb.json +++ b/homeassistant/components/nightscout/translations/lb.json @@ -8,7 +8,6 @@ "invalid_auth": "Ong\u00eblteg Authentifikatioun", "unknown": "Onerwaarte Feeler" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/nl.json b/homeassistant/components/nightscout/translations/nl.json index a9b81e9403e..397150acbde 100644 --- a/homeassistant/components/nightscout/translations/nl.json +++ b/homeassistant/components/nightscout/translations/nl.json @@ -8,7 +8,6 @@ "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/no.json b/homeassistant/components/nightscout/translations/no.json index 6a21b13aca7..08f1ae09fee 100644 --- a/homeassistant/components/nightscout/translations/no.json +++ b/homeassistant/components/nightscout/translations/no.json @@ -8,7 +8,6 @@ "invalid_auth": "Ugyldig godkjenning", "unknown": "Uventet feil" }, - "flow_title": "", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/pl.json b/homeassistant/components/nightscout/translations/pl.json index bc70aab3bf8..fe0da7ee321 100644 --- a/homeassistant/components/nightscout/translations/pl.json +++ b/homeassistant/components/nightscout/translations/pl.json @@ -8,7 +8,6 @@ "invalid_auth": "Niepoprawne uwierzytelnienie", "unknown": "Nieoczekiwany b\u0142\u0105d" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/pt-BR.json b/homeassistant/components/nightscout/translations/pt-BR.json index 4c31da85534..b8ebefa1f08 100644 --- a/homeassistant/components/nightscout/translations/pt-BR.json +++ b/homeassistant/components/nightscout/translations/pt-BR.json @@ -8,7 +8,6 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/ru.json b/homeassistant/components/nightscout/translations/ru.json index c7688973c1b..7e24a9cf1f2 100644 --- a/homeassistant/components/nightscout/translations/ru.json +++ b/homeassistant/components/nightscout/translations/ru.json @@ -8,7 +8,6 @@ "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/tr.json b/homeassistant/components/nightscout/translations/tr.json index 8a27b28aa6e..dc134ac4f40 100644 --- a/homeassistant/components/nightscout/translations/tr.json +++ b/homeassistant/components/nightscout/translations/tr.json @@ -8,7 +8,6 @@ "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "unknown": "Beklenmeyen hata" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/uk.json b/homeassistant/components/nightscout/translations/uk.json index 6504b00eb88..f23c457f553 100644 --- a/homeassistant/components/nightscout/translations/uk.json +++ b/homeassistant/components/nightscout/translations/uk.json @@ -8,7 +8,6 @@ "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nightscout/translations/zh-Hant.json b/homeassistant/components/nightscout/translations/zh-Hant.json index 2ad1c4fde39..cbf1a33cde5 100644 --- a/homeassistant/components/nightscout/translations/zh-Hant.json +++ b/homeassistant/components/nightscout/translations/zh-Hant.json @@ -8,7 +8,6 @@ "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, - "flow_title": "Nightscout", "step": { "user": { "data": { diff --git a/homeassistant/components/nmap_tracker/translations/ar.json b/homeassistant/components/nmap_tracker/translations/ar.json index 9f223966416..620adf5b41c 100644 --- a/homeassistant/components/nmap_tracker/translations/ar.json +++ b/homeassistant/components/nmap_tracker/translations/ar.json @@ -16,8 +16,7 @@ "step": { "init": { "data": { - "interval_seconds": "\u0641\u062a\u0631\u0629 \u062a\u0643\u0631\u0627\u0631 \u0627\u0644\u0628\u062d\u062b", - "track_new_devices": "\u062a\u062a\u0628\u0639 \u0627\u0644\u0623\u062c\u0647\u0632\u0629 \u0627\u0644\u062c\u062f\u064a\u062f\u0629" + "interval_seconds": "\u0641\u062a\u0631\u0629 \u062a\u0643\u0631\u0627\u0631 \u0627\u0644\u0628\u062d\u062b" } } } diff --git a/homeassistant/components/nmap_tracker/translations/ca.json b/homeassistant/components/nmap_tracker/translations/ca.json index e72c03cc63a..fd0cbadf71a 100644 --- a/homeassistant/components/nmap_tracker/translations/ca.json +++ b/homeassistant/components/nmap_tracker/translations/ca.json @@ -30,8 +30,7 @@ "home_interval": "Nombre m\u00ednim de minuts entre escanejos de dispositius actius (conserva la bateria)", "hosts": "Adreces de xarxa a escanejar (separades per comes)", "interval_seconds": "Interval d'escaneig", - "scan_options": "Opcions de configuraci\u00f3 d'escaneig d'Nmap en brut", - "track_new_devices": "Segueix dispositius nous" + "scan_options": "Opcions de configuraci\u00f3 d'escaneig d'Nmap en brut" }, "description": "Configura els amfitrions a explorar per Nmap. L'adre\u00e7a de xarxa i les exclusions poden ser adreces IP (192.168.1.1), xarxes IP (192.168.0.0/24) o intervals IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/cs.json b/homeassistant/components/nmap_tracker/translations/cs.json index ac5f913d8e6..95f0bfc3ae8 100644 --- a/homeassistant/components/nmap_tracker/translations/cs.json +++ b/homeassistant/components/nmap_tracker/translations/cs.json @@ -8,8 +8,7 @@ "step": { "init": { "data": { - "interval_seconds": "Interval skenov\u00e1n\u00ed", - "track_new_devices": "Sledovat nov\u00e1 za\u0159\u00edzen\u00ed" + "interval_seconds": "Interval skenov\u00e1n\u00ed" } } } diff --git a/homeassistant/components/nmap_tracker/translations/de.json b/homeassistant/components/nmap_tracker/translations/de.json index 48f06bf320b..cfab5b828fd 100644 --- a/homeassistant/components/nmap_tracker/translations/de.json +++ b/homeassistant/components/nmap_tracker/translations/de.json @@ -30,8 +30,7 @@ "home_interval": "Mindestanzahl von Minuten zwischen den Scans aktiver Ger\u00e4te (Batterie schonen)", "hosts": "Netzwerkadressen (kommagetrennt) zum Scannen", "interval_seconds": "Scanintervall", - "scan_options": "Raw konfigurierbare Scan-Optionen f\u00fcr Nmap", - "track_new_devices": "Neue Ger\u00e4te verfolgen" + "scan_options": "Raw konfigurierbare Scan-Optionen f\u00fcr Nmap" }, "description": "Konfiguriere die Hosts, die von Nmap gescannt werden sollen. Netzwerkadresse und Ausschl\u00fcsse k\u00f6nnen IP-Adressen (192.168.1.1), IP-Netzwerke (192.168.0.0/24) oder IP-Bereiche (192.168.1.0-32) sein." } diff --git a/homeassistant/components/nmap_tracker/translations/el.json b/homeassistant/components/nmap_tracker/translations/el.json index 202bc8e0722..fe1a448a8be 100644 --- a/homeassistant/components/nmap_tracker/translations/el.json +++ b/homeassistant/components/nmap_tracker/translations/el.json @@ -30,8 +30,7 @@ "home_interval": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf\u03c2 \u03b1\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03bb\u03b5\u03c0\u03c4\u03ce\u03bd \u03bc\u03b5\u03c4\u03b1\u03be\u03cd \u03c4\u03c9\u03bd \u03c3\u03b1\u03c1\u03ce\u03c3\u03b5\u03c9\u03bd \u03b5\u03bd\u03b5\u03c1\u03b3\u03ce\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd (\u03b4\u03b9\u03b1\u03c4\u03ae\u03c1\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03bc\u03c0\u03b1\u03c4\u03b1\u03c1\u03af\u03b1\u03c2)", "hosts": "\u0394\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 (\u03b4\u03b9\u03b1\u03c7\u03c9\u03c1\u03b9\u03c3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03bc\u03b5 \u03ba\u03cc\u03bc\u03bc\u03b1) \u03b3\u03b9\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7", "interval_seconds": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2", - "scan_options": "\u0391\u03ba\u03b1\u03c4\u03ad\u03c1\u03b3\u03b1\u03c3\u03c4\u03b5\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03c4\u03c1\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b9\u03bc\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf Nmap", - "track_new_devices": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7 \u03bd\u03ad\u03c9\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd" + "scan_options": "\u0391\u03ba\u03b1\u03c4\u03ad\u03c1\u03b3\u03b1\u03c3\u03c4\u03b5\u03c2 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03c4\u03c1\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b9\u03bc\u03b5\u03c2 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03bf Nmap" }, "description": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03ce\u03bd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7 \u03b1\u03c0\u03cc \u03c4\u03bf Nmap. \u0397 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 \u03ba\u03b1\u03b9 \u03bf\u03b9 \u03b5\u03be\u03b1\u03b9\u03c1\u03ad\u03c3\u03b5\u03b9\u03c2 \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u0394\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 IP (192.168.1.1), \u0394\u03af\u03ba\u03c4\u03c5\u03b1 IP (192.168.0.0/24) \u03ae \u0395\u03cd\u03c1\u03bf\u03c2 IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/en.json b/homeassistant/components/nmap_tracker/translations/en.json index ee615b87e91..ae4175e0f14 100644 --- a/homeassistant/components/nmap_tracker/translations/en.json +++ b/homeassistant/components/nmap_tracker/translations/en.json @@ -30,8 +30,7 @@ "home_interval": "Minimum number of minutes between scans of active devices (preserve battery)", "hosts": "Network addresses (comma separated) to scan", "interval_seconds": "Scan interval", - "scan_options": "Raw configurable scan options for Nmap", - "track_new_devices": "Track new devices" + "scan_options": "Raw configurable scan options for Nmap" }, "description": "Configure hosts to be scanned by Nmap. Network address and excludes can be IP Addresses (192.168.1.1), IP Networks (192.168.0.0/24) or IP Ranges (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/es.json b/homeassistant/components/nmap_tracker/translations/es.json index e68d8ff59ab..9b943edb310 100644 --- a/homeassistant/components/nmap_tracker/translations/es.json +++ b/homeassistant/components/nmap_tracker/translations/es.json @@ -30,8 +30,7 @@ "home_interval": "N\u00famero m\u00ednimo de minutos entre los escaneos de los dispositivos activos (preservar la bater\u00eda)", "hosts": "Direcciones de red a escanear (separadas por comas)", "interval_seconds": "Intervalo de exploraci\u00f3n", - "scan_options": "Opciones de escaneo configurables sin procesar para Nmap", - "track_new_devices": "Seguimiento de nuevos dispositivos" + "scan_options": "Opciones de escaneo configurables sin procesar para Nmap" }, "description": "Configure los hosts que ser\u00e1n escaneados por Nmap. Las direcciones de red y los excluidos pueden ser direcciones IP (192.168.1.1), redes IP (192.168.0.0/24) o rangos IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/et.json b/homeassistant/components/nmap_tracker/translations/et.json index ac23cb7004f..e0b922fd007 100644 --- a/homeassistant/components/nmap_tracker/translations/et.json +++ b/homeassistant/components/nmap_tracker/translations/et.json @@ -30,8 +30,7 @@ "home_interval": "Minimaalne sk\u00e4nnimiste intervall minutites (eeldus on aku s\u00e4\u00e4stmine)", "hosts": "V\u00f5rguaadresside vahemik (komaga eraldatud)", "interval_seconds": "P\u00e4ringute intervall", - "scan_options": "Vaikimisi Nmap sk\u00e4nnimise valikud", - "track_new_devices": "Uute seadmete j\u00e4lgimine" + "scan_options": "Vaikimisi Nmap sk\u00e4nnimise valikud" }, "description": "Vali Nmap poolt sk\u00e4nnitavad hostid. Valikuks on IP aadressid (192.168.1.1), v\u00f5rgud (192.168.0.0/24) v\u00f5i IP vahemikud (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/fr.json b/homeassistant/components/nmap_tracker/translations/fr.json index 0a4e75a9813..b0362217d33 100644 --- a/homeassistant/components/nmap_tracker/translations/fr.json +++ b/homeassistant/components/nmap_tracker/translations/fr.json @@ -30,8 +30,7 @@ "home_interval": "Nombre minimum de minutes entre les analyses des appareils actifs (pr\u00e9server la batterie)", "hosts": "Adresses r\u00e9seau (s\u00e9par\u00e9es par des virgules) \u00e0 analyser", "interval_seconds": "Intervalle d\u2019analyse", - "scan_options": "Options d'analyse brutes configurables pour Nmap", - "track_new_devices": "Suivre les nouveaux appareils" + "scan_options": "Options d'analyse brutes configurables pour Nmap" }, "description": "Configurer les h\u00f4tes \u00e0 analyser par Nmap. L'adresse r\u00e9seau et les exclusions peuvent \u00eatre des adresses IP (192.168.1.1), des r\u00e9seaux IP (192.168.0.0/24) ou des plages IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/he.json b/homeassistant/components/nmap_tracker/translations/he.json index d57ca363944..22bc8fa7080 100644 --- a/homeassistant/components/nmap_tracker/translations/he.json +++ b/homeassistant/components/nmap_tracker/translations/he.json @@ -29,8 +29,7 @@ "home_interval": "\u05de\u05e1\u05e4\u05e8 \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9 \u05e9\u05dc \u05d3\u05e7\u05d5\u05ea \u05d1\u05d9\u05df \u05e1\u05e8\u05d9\u05e7\u05d5\u05ea \u05e9\u05dc \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05e4\u05e2\u05d9\u05dc\u05d9\u05dd (\u05e9\u05d9\u05de\u05d5\u05e8 \u05e1\u05d5\u05dc\u05dc\u05d4)", "hosts": "\u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05e8\u05e9\u05ea (\u05de\u05d5\u05e4\u05e8\u05d3\u05d5\u05ea \u05d1\u05e4\u05e1\u05d9\u05e7) \u05dc\u05e1\u05e8\u05d9\u05e7\u05d4", "interval_seconds": "\u05de\u05e8\u05d5\u05d5\u05d7 \u05d6\u05de\u05df \u05dc\u05e1\u05e8\u05d9\u05e7\u05d4", - "scan_options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e1\u05e8\u05d9\u05e7\u05d4 \u05d2\u05d5\u05dc\u05de\u05d9\u05d5\u05ea \u05d4\u05e0\u05d9\u05ea\u05e0\u05d5\u05ea \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4 \u05e2\u05d1\u05d5\u05e8 Nmap", - "track_new_devices": "\u05de\u05e2\u05e7\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d7\u05d3\u05e9\u05d9\u05dd" + "scan_options": "\u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05e1\u05e8\u05d9\u05e7\u05d4 \u05d2\u05d5\u05dc\u05de\u05d9\u05d5\u05ea \u05d4\u05e0\u05d9\u05ea\u05e0\u05d5\u05ea \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4 \u05e2\u05d1\u05d5\u05e8 Nmap" }, "description": "\u05e7\u05d1\u05e2 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05de\u05d0\u05e8\u05d7\u05d9\u05dd \u05e9\u05d9\u05e1\u05e8\u05e7\u05d5 \u05e2\u05dc \u05d9\u05d3\u05d9 Nmap. \u05db\u05ea\u05d5\u05d1\u05ea \u05e8\u05e9\u05ea \u05d5\u05d0\u05d9 \u05d4\u05db\u05dc\u05dc\u05d4 \u05d9\u05db\u05d5\u05dc \u05dc\u05d4\u05d9\u05d5\u05ea \u05db\u05ea\u05d5\u05d1\u05d5\u05ea IP (192.168.1.1), \u05e8\u05e9\u05ea\u05d5\u05ea IP (192.168.0.0/24) \u05d0\u05d5 \u05d8\u05d5\u05d5\u05d7\u05d9 IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/hu.json b/homeassistant/components/nmap_tracker/translations/hu.json index f54cf208e92..6c802e6d25c 100644 --- a/homeassistant/components/nmap_tracker/translations/hu.json +++ b/homeassistant/components/nmap_tracker/translations/hu.json @@ -30,8 +30,7 @@ "home_interval": "Minim\u00e1lis percsz\u00e1m az akt\u00edv eszk\u00f6z\u00f6k vizsg\u00e1lata k\u00f6z\u00f6tt (akkumul\u00e1tor k\u00edm\u00e9l\u00e9se)", "hosts": "H\u00e1l\u00f3zati c\u00edmek (vessz\u0151vel elv\u00e1lasztva) a beolvas\u00e1shoz", "interval_seconds": "Szkennel\u00e9si intervallum", - "scan_options": "Nyersen konfigur\u00e1lhat\u00f3 beolvas\u00e1si lehet\u0151s\u00e9gek az Nmap sz\u00e1m\u00e1ra", - "track_new_devices": "\u00daj eszk\u00f6z\u00f6k nyomon k\u00f6vet\u00e9se" + "scan_options": "Nyersen konfigur\u00e1lhat\u00f3 beolvas\u00e1si lehet\u0151s\u00e9gek az Nmap sz\u00e1m\u00e1ra" }, "description": "\u00c1ll\u00edtsa be a hogy milyen c\u00edmeket szkenneljen az Nmap. A h\u00e1l\u00f3zati c\u00edm lehet IP-c\u00edm (pl. 192.168.1.1), IP-h\u00e1l\u00f3zat (pl. 192.168.0.0/24) vagy IP-tartom\u00e1ny (pl. 192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/id.json b/homeassistant/components/nmap_tracker/translations/id.json index e1894aff49b..fae1fdfe1bd 100644 --- a/homeassistant/components/nmap_tracker/translations/id.json +++ b/homeassistant/components/nmap_tracker/translations/id.json @@ -30,8 +30,7 @@ "home_interval": "Jumlah menit minimum antara pemindaian perangkat aktif (menghemat baterai)", "hosts": "Alamat jaringan (dipisahkan koma) untuk pemindaian", "interval_seconds": "Interval pindai", - "scan_options": "Opsi pemindaian mentah yang dapat dikonfigurasi untuk Nmap", - "track_new_devices": "Lacak perangkat baru" + "scan_options": "Opsi pemindaian mentah yang dapat dikonfigurasi untuk Nmap" }, "description": "Konfigurasikan host untuk dipindai oleh Nmap. Alamat jaringan dan pengecualian dapat berupa alamat IP (192.168.1.1), jaringan IP (192.168.0.0/24), atau rentang IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/it.json b/homeassistant/components/nmap_tracker/translations/it.json index 8a7de165778..e6d5a41656f 100644 --- a/homeassistant/components/nmap_tracker/translations/it.json +++ b/homeassistant/components/nmap_tracker/translations/it.json @@ -30,8 +30,7 @@ "home_interval": "Numero minimo di minuti tra le scansioni dei dispositivi attivi (preserva la batteria)", "hosts": "Indirizzi di rete (separati da virgole) da scansionare", "interval_seconds": "Intervallo di scansione", - "scan_options": "Opzioni di scansione configurabili non elaborate per Nmap", - "track_new_devices": "Traccia nuovi dispositivi" + "scan_options": "Opzioni di scansione configurabili non elaborate per Nmap" }, "description": "Configura gli host da scansionare con Nmap. L'indirizzo di rete e le esclusioni possono essere indirizzi IP (192.168.1.1), reti IP (192.168.0.0/24) o intervalli IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/ja.json b/homeassistant/components/nmap_tracker/translations/ja.json index 2e60a814468..60de8477859 100644 --- a/homeassistant/components/nmap_tracker/translations/ja.json +++ b/homeassistant/components/nmap_tracker/translations/ja.json @@ -30,8 +30,7 @@ "home_interval": "\u30a2\u30af\u30c6\u30a3\u30d6\u306a\u30c7\u30d0\u30a4\u30b9\u306e\u30b9\u30ad\u30e3\u30f3\u9593\u9694(\u5206)\u306e\u6700\u5c0f\u6642\u9593(\u30d0\u30c3\u30c6\u30ea\u30fc\u3092\u7bc0\u7d04)", "hosts": "\u30b9\u30ad\u30e3\u30f3\u3059\u308b\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30a2\u30c9\u30ec\u30b9(\u30b3\u30f3\u30de\u533a\u5207\u308a)", "interval_seconds": "\u30b9\u30ad\u30e3\u30f3\u9593\u9694", - "scan_options": "Nmap\u306b\u672a\u52a0\u5de5\u3067\u305d\u306e\u307e\u307e\u6e21\u3055\u308c\u308b\u30b9\u30ad\u30e3\u30f3\u8a2d\u5b9a\u306e\u30aa\u30d7\u30b7\u30e7\u30f3", - "track_new_devices": "\u65b0\u3057\u3044\u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u8de1" + "scan_options": "Nmap\u306b\u672a\u52a0\u5de5\u3067\u305d\u306e\u307e\u307e\u6e21\u3055\u308c\u308b\u30b9\u30ad\u30e3\u30f3\u8a2d\u5b9a\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" }, "description": "Nmap\u3067\u30b9\u30ad\u30e3\u30f3\u3055\u308c\u308b\u30db\u30b9\u30c8\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30a2\u30c9\u30ec\u30b9\u304a\u3088\u3073\u9664\u5916\u5bfe\u8c61\u306f\u3001IP\u30a2\u30c9\u30ec\u30b9(192.168.1.1)\u3001IP\u30cd\u30c3\u30c8\u30ef\u30fc\u30af(192.168.0.0/24)\u3001\u307e\u305f\u306f\u3001IP\u7bc4\u56f2(192.168.1.0-32)\u3067\u3059\u3002" } diff --git a/homeassistant/components/nmap_tracker/translations/nl.json b/homeassistant/components/nmap_tracker/translations/nl.json index 8385aca1ffe..2331d7fda9f 100644 --- a/homeassistant/components/nmap_tracker/translations/nl.json +++ b/homeassistant/components/nmap_tracker/translations/nl.json @@ -30,8 +30,7 @@ "home_interval": "Minimum aantal minuten tussen scans van actieve apparaten (batterij sparen)", "hosts": "Netwerkadressen (gescheiden door komma's) om te scannen", "interval_seconds": "Scaninterval", - "scan_options": "Ruwe configureerbare scanopties voor Nmap", - "track_new_devices": "Volg nieuwe apparaten" + "scan_options": "Ruwe configureerbare scanopties voor Nmap" }, "description": "Configureer hosts die moeten worden gescand door Nmap. Netwerkadres en uitsluitingen kunnen IP-adressen (192.168.1.1), IP-netwerken (192.168.0.0/24) of IP-bereiken (192.168.1.0-32) zijn." } diff --git a/homeassistant/components/nmap_tracker/translations/no.json b/homeassistant/components/nmap_tracker/translations/no.json index 1de32bed121..de8ca5898aa 100644 --- a/homeassistant/components/nmap_tracker/translations/no.json +++ b/homeassistant/components/nmap_tracker/translations/no.json @@ -30,8 +30,7 @@ "home_interval": "Minimum antall minutter mellom skanninger av aktive enheter (lagre batteri)", "hosts": "Nettverksadresser (atskilt med komma) som skal skannes", "interval_seconds": "Skanneintervall", - "scan_options": "R\u00e5 konfigurerbare skannealternativer for Nmap", - "track_new_devices": "Spor nye enheter" + "scan_options": "R\u00e5 konfigurerbare skannealternativer for Nmap" }, "description": "Konfigurer verter som skal skannes av Nmap. Nettverksadresse og ekskluderer kan v\u00e6re IP-adresser (192.168.1.1), IP-nettverk (192.168.0.0/24) eller IP-omr\u00e5der (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/pl.json b/homeassistant/components/nmap_tracker/translations/pl.json index 98f27dcdd4b..98b16043d4d 100644 --- a/homeassistant/components/nmap_tracker/translations/pl.json +++ b/homeassistant/components/nmap_tracker/translations/pl.json @@ -30,8 +30,7 @@ "home_interval": "Minimalna liczba minut mi\u0119dzy skanami aktywnych urz\u0105dze\u0144 (oszcz\u0119dzanie baterii)", "hosts": "Adresy sieciowe (oddzielone przecinkami) do skanowania", "interval_seconds": "Cz\u0119stotliwo\u015b\u0107 skanowania", - "scan_options": "Surowe konfigurowalne opcje skanowania dla Nmap", - "track_new_devices": "\u015aled\u017a nowe urz\u0105dzenia" + "scan_options": "Surowe konfigurowalne opcje skanowania dla Nmap" }, "description": "Skonfiguruj hosta do skanowania przez Nmap. Adresy sieciowe i te wykluczone mog\u0105 by\u0107 adresami IP (192.168.1.1), sieciami IP (192.168.0.0/24) lub zakresami IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/pt-BR.json b/homeassistant/components/nmap_tracker/translations/pt-BR.json index a80213ce5d6..786d5d4df0b 100644 --- a/homeassistant/components/nmap_tracker/translations/pt-BR.json +++ b/homeassistant/components/nmap_tracker/translations/pt-BR.json @@ -30,8 +30,7 @@ "home_interval": "N\u00famero m\u00ednimo de minutos entre escaneamento de dispositivos ativos (preservar bateria)", "hosts": "Endere\u00e7os de rede (separados por v\u00edrgula) para digitalizar", "interval_seconds": "Intervalo de varredura", - "scan_options": "Op\u00e7\u00f5es de escaneamento bruto configur\u00e1veis para Nmap", - "track_new_devices": "Rastrear novos dispositivos" + "scan_options": "Op\u00e7\u00f5es de escaneamento bruto configur\u00e1veis para Nmap" }, "description": "Configure os hosts a serem verificados pelo Nmap. O endere\u00e7o de rede e as exclus\u00f5es podem ser endere\u00e7os IP (192.168.1.1), redes IP (192.168.0.0/24) ou intervalos de IP (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/ru.json b/homeassistant/components/nmap_tracker/translations/ru.json index ba143e20d01..125f394fc23 100644 --- a/homeassistant/components/nmap_tracker/translations/ru.json +++ b/homeassistant/components/nmap_tracker/translations/ru.json @@ -30,8 +30,7 @@ "home_interval": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043c\u0438\u043d\u0443\u0442 \u043c\u0435\u0436\u0434\u0443 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 (\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u044f \u0437\u0430\u0440\u044f\u0434\u0430 \u0431\u0430\u0442\u0430\u0440\u0435\u0438)", "hosts": "\u0421\u0435\u0442\u0435\u0432\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430 \u0434\u043b\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e)", "interval_seconds": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f", - "scan_options": "\u041d\u0435\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f Nmap", - "track_new_devices": "\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" + "scan_options": "\u041d\u0435\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f Nmap" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0445\u043e\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f Nmap. \u0421\u0435\u0442\u0435\u0432\u044b\u043c \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c IP-\u0430\u0434\u0440\u0435\u0441\u0430 (192.168.1.1), IP-\u0441\u0435\u0442\u0438 (192.168.0.0/24) \u0438\u043b\u0438 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u044b IP-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 (192.168.1.0-32)." } diff --git a/homeassistant/components/nmap_tracker/translations/tr.json b/homeassistant/components/nmap_tracker/translations/tr.json index 8e5dadb8100..f4d984f2bdb 100644 --- a/homeassistant/components/nmap_tracker/translations/tr.json +++ b/homeassistant/components/nmap_tracker/translations/tr.json @@ -30,8 +30,7 @@ "home_interval": "Aktif cihazlar\u0131n taranmas\u0131 aras\u0131ndaki minimum dakika say\u0131s\u0131 (pilden tasarruf edin)", "hosts": "Taranacak a\u011f adresleri (virg\u00fclle ayr\u0131lm\u0131\u015f)", "interval_seconds": "Tarama aral\u0131\u011f\u0131", - "scan_options": "Nmap i\u00e7in ham yap\u0131land\u0131r\u0131labilir tarama se\u00e7enekleri", - "track_new_devices": "Yeni cihazlar\u0131 izle" + "scan_options": "Nmap i\u00e7in ham yap\u0131land\u0131r\u0131labilir tarama se\u00e7enekleri" }, "description": "Nmap taraf\u0131ndan taranacak ana bilgisayarlar\u0131 yap\u0131land\u0131r\u0131n. A\u011f adresi ve hari\u00e7 tutulanlar, IP Adresleri (192.168.1.1), IP A\u011flar\u0131 (192.168.0.0/24) veya IP Aral\u0131klar\u0131 (192.168.1.0-32) olabilir." } diff --git a/homeassistant/components/nmap_tracker/translations/zh-Hans.json b/homeassistant/components/nmap_tracker/translations/zh-Hans.json index 5b1be2f497d..31f4568a848 100644 --- a/homeassistant/components/nmap_tracker/translations/zh-Hans.json +++ b/homeassistant/components/nmap_tracker/translations/zh-Hans.json @@ -27,8 +27,7 @@ "home_interval": "\u626b\u63cf\u8bbe\u5907\u7684\u6700\u5c0f\u95f4\u9694\u5206\u949f\u6570\uff08\u7528\u4e8e\u8282\u7701\u7535\u91cf\uff09", "hosts": "\u8981\u626b\u63cf\u7684\u7f51\u7edc\u5730\u5740\uff08\u4ee5\u9017\u53f7\u5206\u9694\uff09", "interval_seconds": "\u626b\u63cf\u95f4\u9694\uff08\u79d2\uff09", - "scan_options": "Nmap \u7684\u539f\u59cb\u53ef\u914d\u7f6e\u626b\u63cf\u9009\u9879", - "track_new_devices": "\u8ddf\u8e2a\u65b0\u8bbe\u5907" + "scan_options": "Nmap \u7684\u539f\u59cb\u53ef\u914d\u7f6e\u626b\u63cf\u9009\u9879" }, "description": "\u914d\u7f6e\u901a\u8fc7 Nmap \u626b\u63cf\u7684\u4e3b\u673a\u3002\u7f51\u7edc\u5730\u5740\u548c\u6392\u9664\u9879\u53ef\u4ee5\u662f IP \u5730\u5740 (192.168.1.1)\u3001IP \u5730\u5740\u5757 (192.168.0.0/24) \u6216 IP \u8303\u56f4 (192.168.1.0-32)\u3002" } diff --git a/homeassistant/components/nmap_tracker/translations/zh-Hant.json b/homeassistant/components/nmap_tracker/translations/zh-Hant.json index 65b0b7afed4..acee979e8b8 100644 --- a/homeassistant/components/nmap_tracker/translations/zh-Hant.json +++ b/homeassistant/components/nmap_tracker/translations/zh-Hant.json @@ -30,8 +30,7 @@ "home_interval": "\u6383\u63cf\u6d3b\u52d5\u88dd\u7f6e\u7684\u6700\u4f4e\u9593\u9694\u5206\u9418\uff08\u8003\u91cf\u7701\u96fb\uff09", "hosts": "\u6240\u8981\u6383\u63cf\u7684\u7db2\u8def\u4f4d\u5740\uff08\u4ee5\u9017\u865f\u5206\u9694\uff09", "interval_seconds": "\u6383\u63cf\u9593\u8ddd", - "scan_options": "Nmap \u539f\u59cb\u8a2d\u5b9a\u6383\u63cf\u9078\u9805", - "track_new_devices": "\u8ffd\u8e64\u65b0\u88dd\u7f6e" + "scan_options": "Nmap \u539f\u59cb\u8a2d\u5b9a\u6383\u63cf\u9078\u9805" }, "description": "\u8a2d\u5b9a Nmap \u6383\u63cf\u4e3b\u6a5f\u3002\u7db2\u8def\u4f4d\u5740\u8207\u6392\u9664\u4f4d\u5740\u53ef\u4ee5\u662f IP \u4f4d\u5740\uff08192.168.1.1\uff09\u3001IP \u7db2\u8def\uff08192.168.0.0/24\uff09\u6216 IP \u7bc4\u570d\uff08192.168.1.0-32\uff09\u3002" } diff --git a/homeassistant/components/notion/translations/bg.json b/homeassistant/components/notion/translations/bg.json index ef4a919401e..1df2ad13a33 100644 --- a/homeassistant/components/notion/translations/bg.json +++ b/homeassistant/components/notion/translations/bg.json @@ -4,7 +4,6 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { - "no_devices": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043f\u0440\u043e\u0444\u0438\u043b\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, "step": { diff --git a/homeassistant/components/notion/translations/ca.json b/homeassistant/components/notion/translations/ca.json index 51ca461f854..4047a5dbd30 100644 --- a/homeassistant/components/notion/translations/ca.json +++ b/homeassistant/components/notion/translations/ca.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "no_devices": "No s'han trobat dispositius al compte", "unknown": "Error inesperat" }, "step": { diff --git a/homeassistant/components/notion/translations/cs.json b/homeassistant/components/notion/translations/cs.json index 53cae8ab73b..57ce0a06b95 100644 --- a/homeassistant/components/notion/translations/cs.json +++ b/homeassistant/components/notion/translations/cs.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "no_devices": "V \u00fa\u010dtu nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { diff --git a/homeassistant/components/notion/translations/da.json b/homeassistant/components/notion/translations/da.json index e8a1d35417a..add31a51324 100644 --- a/homeassistant/components/notion/translations/da.json +++ b/homeassistant/components/notion/translations/da.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Dette brugernavn er allerede i brug." }, - "error": { - "no_devices": "Ingen enheder fundet i konto" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/de.json b/homeassistant/components/notion/translations/de.json index 59ab1fdc1be..6ee08a8a732 100644 --- a/homeassistant/components/notion/translations/de.json +++ b/homeassistant/components/notion/translations/de.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Ung\u00fcltige Authentifizierung", - "no_devices": "Keine Ger\u00e4te im Konto gefunden", "unknown": "Unerwarteter Fehler" }, "step": { diff --git a/homeassistant/components/notion/translations/el.json b/homeassistant/components/notion/translations/el.json index ee39e67ae73..89c175337ce 100644 --- a/homeassistant/components/notion/translations/el.json +++ b/homeassistant/components/notion/translations/el.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "no_devices": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { diff --git a/homeassistant/components/notion/translations/en.json b/homeassistant/components/notion/translations/en.json index afd58a4d404..0eb4689bce6 100644 --- a/homeassistant/components/notion/translations/en.json +++ b/homeassistant/components/notion/translations/en.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Invalid authentication", - "no_devices": "No devices found in account", "unknown": "Unexpected error" }, "step": { diff --git a/homeassistant/components/notion/translations/es-419.json b/homeassistant/components/notion/translations/es-419.json index 489df80f736..ec520bd89df 100644 --- a/homeassistant/components/notion/translations/es-419.json +++ b/homeassistant/components/notion/translations/es-419.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Este nombre de usuario ya est\u00e1 en uso." }, - "error": { - "no_devices": "No se han encontrado dispositivos en la cuenta." - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/es.json b/homeassistant/components/notion/translations/es.json index b537bd5bd3b..62a21b06022 100644 --- a/homeassistant/components/notion/translations/es.json +++ b/homeassistant/components/notion/translations/es.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "no_devices": "No se han encontrado dispositivos en la cuenta", "unknown": "Error inesperado" }, "step": { diff --git a/homeassistant/components/notion/translations/et.json b/homeassistant/components/notion/translations/et.json index 7639901201d..488e66c57a3 100644 --- a/homeassistant/components/notion/translations/et.json +++ b/homeassistant/components/notion/translations/et.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Tuvastamise viga", - "no_devices": "Kontolt ei leitud \u00fchtegi seadet", "unknown": "Ootamatu t\u00f5rge" }, "step": { diff --git a/homeassistant/components/notion/translations/fr.json b/homeassistant/components/notion/translations/fr.json index c5c0145e972..2340ad7620e 100644 --- a/homeassistant/components/notion/translations/fr.json +++ b/homeassistant/components/notion/translations/fr.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Authentification non valide", - "no_devices": "Aucun appareil trouv\u00e9 sur le compte", "unknown": "Erreur inattendue" }, "step": { diff --git a/homeassistant/components/notion/translations/hr.json b/homeassistant/components/notion/translations/hr.json index 237845502dd..75f9880c4e0 100644 --- a/homeassistant/components/notion/translations/hr.json +++ b/homeassistant/components/notion/translations/hr.json @@ -1,8 +1,5 @@ { "config": { - "error": { - "no_devices": "Nisu prona\u0111eni ure\u0111aji na ra\u010dunu" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/hu.json b/homeassistant/components/notion/translations/hu.json index ef10a451a84..82e7df49eed 100644 --- a/homeassistant/components/notion/translations/hu.json +++ b/homeassistant/components/notion/translations/hu.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "no_devices": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a fi\u00f3kban", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { diff --git a/homeassistant/components/notion/translations/id.json b/homeassistant/components/notion/translations/id.json index bf8cb8e1186..8191e9bc85a 100644 --- a/homeassistant/components/notion/translations/id.json +++ b/homeassistant/components/notion/translations/id.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Autentikasi tidak valid", - "no_devices": "Tidak ada perangkat yang ditemukan di akun", "unknown": "Kesalahan yang tidak diharapkan" }, "step": { diff --git a/homeassistant/components/notion/translations/it.json b/homeassistant/components/notion/translations/it.json index 811884dddb4..48cda247e03 100644 --- a/homeassistant/components/notion/translations/it.json +++ b/homeassistant/components/notion/translations/it.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Autenticazione non valida", - "no_devices": "Nessun dispositivo trovato nell'account", "unknown": "Errore imprevisto" }, "step": { diff --git a/homeassistant/components/notion/translations/ja.json b/homeassistant/components/notion/translations/ja.json index fb764116ecf..8cd2a58e1e0 100644 --- a/homeassistant/components/notion/translations/ja.json +++ b/homeassistant/components/notion/translations/ja.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "no_devices": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { diff --git a/homeassistant/components/notion/translations/ko.json b/homeassistant/components/notion/translations/ko.json index c81805446f9..991553a1531 100644 --- a/homeassistant/components/notion/translations/ko.json +++ b/homeassistant/components/notion/translations/ko.json @@ -4,8 +4,7 @@ "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { - "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "no_devices": "\uacc4\uc815\uc5d0 \ub4f1\ub85d\ub41c \uae30\uae30\uac00 \uc874\uc7ac\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4" + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/notion/translations/lb.json b/homeassistant/components/notion/translations/lb.json index a1bc1601e00..9a7ca2855de 100644 --- a/homeassistant/components/notion/translations/lb.json +++ b/homeassistant/components/notion/translations/lb.json @@ -4,8 +4,7 @@ "already_configured": "Kont ass scho konfigur\u00e9iert" }, "error": { - "invalid_auth": "Ong\u00eblteg Authentifikatioun", - "no_devices": "Keng Apparater am Kont fonnt" + "invalid_auth": "Ong\u00eblteg Authentifikatioun" }, "step": { "user": { diff --git a/homeassistant/components/notion/translations/nl.json b/homeassistant/components/notion/translations/nl.json index 81da85f6240..3a86ffd2264 100644 --- a/homeassistant/components/notion/translations/nl.json +++ b/homeassistant/components/notion/translations/nl.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Ongeldige authenticatie", - "no_devices": "Geen apparaten gevonden in account", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/notion/translations/no.json b/homeassistant/components/notion/translations/no.json index 0bbbeb9c0dd..b8ffb36e040 100644 --- a/homeassistant/components/notion/translations/no.json +++ b/homeassistant/components/notion/translations/no.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Ugyldig godkjenning", - "no_devices": "Ingen enheter funnet i kontoen", "unknown": "Uventet feil" }, "step": { diff --git a/homeassistant/components/notion/translations/pl.json b/homeassistant/components/notion/translations/pl.json index edd7b608b3d..bda5f89b4e1 100644 --- a/homeassistant/components/notion/translations/pl.json +++ b/homeassistant/components/notion/translations/pl.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Niepoprawne uwierzytelnienie", - "no_devices": "Nie znaleziono urz\u0105dze\u0144 na koncie", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { diff --git a/homeassistant/components/notion/translations/pt-BR.json b/homeassistant/components/notion/translations/pt-BR.json index d778a301ee1..d7d15016852 100644 --- a/homeassistant/components/notion/translations/pt-BR.json +++ b/homeassistant/components/notion/translations/pt-BR.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "no_devices": "Nenhum dispositivo encontrado na conta", "unknown": "Erro inesperado" }, "step": { diff --git a/homeassistant/components/notion/translations/ru.json b/homeassistant/components/notion/translations/ru.json index bebd8a66e0e..9ad8a0a8626 100644 --- a/homeassistant/components/notion/translations/ru.json +++ b/homeassistant/components/notion/translations/ru.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "no_devices": "\u041d\u0435\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { diff --git a/homeassistant/components/notion/translations/sl.json b/homeassistant/components/notion/translations/sl.json index 9e4a4c8d77e..8b9dc856382 100644 --- a/homeassistant/components/notion/translations/sl.json +++ b/homeassistant/components/notion/translations/sl.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "To uporabni\u0161ko ime je \u017ee v uporabi." }, - "error": { - "no_devices": "V ra\u010dunu ni najdene nobene naprave" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/sv.json b/homeassistant/components/notion/translations/sv.json index 0cbf4a937b9..71836f79877 100644 --- a/homeassistant/components/notion/translations/sv.json +++ b/homeassistant/components/notion/translations/sv.json @@ -3,9 +3,6 @@ "abort": { "already_configured": "Det h\u00e4r anv\u00e4ndarnamnet anv\u00e4nds redan." }, - "error": { - "no_devices": "Inga enheter hittades p\u00e5 kontot" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/tr.json b/homeassistant/components/notion/translations/tr.json index cd95e502c8d..51ba57e9fa6 100644 --- a/homeassistant/components/notion/translations/tr.json +++ b/homeassistant/components/notion/translations/tr.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "no_devices": "Hesapta cihaz bulunamad\u0131", "unknown": "Beklenmeyen hata" }, "step": { diff --git a/homeassistant/components/notion/translations/uk.json b/homeassistant/components/notion/translations/uk.json index 6dc969c3609..1dd26aaf2a7 100644 --- a/homeassistant/components/notion/translations/uk.json +++ b/homeassistant/components/notion/translations/uk.json @@ -4,8 +4,7 @@ "already_configured": "\u0426\u0435\u0439 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant." }, "error": { - "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", - "no_devices": "\u041d\u0435\u043c\u0430\u0454 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432, \u043f\u043e\u0432'\u044f\u0437\u0430\u043d\u0438\u0445 \u0437 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u043c \u0437\u0430\u043f\u0438\u0441\u043e\u043c." + "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f." }, "step": { "user": { diff --git a/homeassistant/components/notion/translations/zh-Hans.json b/homeassistant/components/notion/translations/zh-Hans.json index 2d670456235..0b54dcf7771 100644 --- a/homeassistant/components/notion/translations/zh-Hans.json +++ b/homeassistant/components/notion/translations/zh-Hans.json @@ -1,8 +1,5 @@ { "config": { - "error": { - "no_devices": "\u5e10\u6237\u4e2d\u627e\u4e0d\u5230\u8bbe\u5907" - }, "step": { "user": { "data": { diff --git a/homeassistant/components/notion/translations/zh-Hant.json b/homeassistant/components/notion/translations/zh-Hant.json index 951ec07c8ad..7feae143a22 100644 --- a/homeassistant/components/notion/translations/zh-Hant.json +++ b/homeassistant/components/notion/translations/zh-Hant.json @@ -6,7 +6,6 @@ }, "error": { "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "no_devices": "\u5e33\u865f\u4e2d\u627e\u4e0d\u5230\u4efb\u4f55\u88dd\u7f6e", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { diff --git a/homeassistant/components/nut/translations/bg.json b/homeassistant/components/nut/translations/bg.json index 1413a7f101c..4983c9a14b2 100644 --- a/homeassistant/components/nut/translations/bg.json +++ b/homeassistant/components/nut/translations/bg.json @@ -1,16 +1,6 @@ { "config": { "step": { - "resources": { - "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u0438" - } - }, - "ups": { - "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u0438" - } - }, "user": { "data": { "port": "\u041f\u043e\u0440\u0442" diff --git a/homeassistant/components/nut/translations/ca.json b/homeassistant/components/nut/translations/ca.json index fc8b5b719a0..c90f6fa8a12 100644 --- a/homeassistant/components/nut/translations/ca.json +++ b/homeassistant/components/nut/translations/ca.json @@ -8,16 +8,9 @@ "unknown": "Error inesperat" }, "step": { - "resources": { - "data": { - "resources": "Recursos" - }, - "title": "Selecciona els recursos a monitoritzar" - }, "ups": { "data": { - "alias": "\u00c0lies", - "resources": "Recursos" + "alias": "\u00c0lies" }, "title": "Selecciona el SAI (UPS) a monitoritzar" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "unknown": "Error inesperat" - }, "step": { "init": { "data": { - "resources": "Recursos", "scan_interval": "Interval d'escaneig (segons)" - }, - "description": "Selecciona els recursos del sensor." + } } } } diff --git a/homeassistant/components/nut/translations/cs.json b/homeassistant/components/nut/translations/cs.json index 37d73391ecf..839b01d622d 100644 --- a/homeassistant/components/nut/translations/cs.json +++ b/homeassistant/components/nut/translations/cs.json @@ -8,16 +8,9 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "resources": { - "data": { - "resources": "Zdroje" - }, - "title": "Vyberte zdroje, kter\u00e9 chcete sledovat" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Zdroje" + "alias": "Alias" }, "title": "Vyberte UPS, kterou chcete sledovat" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" - }, "step": { "init": { "data": { - "resources": "Zdroje", "scan_interval": "Interval sledov\u00e1n\u00ed (v sekund\u00e1ch)" - }, - "description": "Zvolte zdroje senzoru." + } } } } diff --git a/homeassistant/components/nut/translations/de.json b/homeassistant/components/nut/translations/de.json index e82dca2506b..2e2d16a94d7 100644 --- a/homeassistant/components/nut/translations/de.json +++ b/homeassistant/components/nut/translations/de.json @@ -8,16 +8,9 @@ "unknown": "Unerwarteter Fehler" }, "step": { - "resources": { - "data": { - "resources": "Ressourcen" - }, - "title": "W\u00e4hle die zu \u00fcberwachenden Ressourcen aus" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Ressourcen" + "alias": "Alias" }, "title": "W\u00e4hle die zu \u00fcberwachende USV" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "unknown": "Unerwarteter Fehler" - }, "step": { "init": { "data": { - "resources": "Ressourcen", "scan_interval": "Scan-Intervall (Sekunden)" - }, - "description": "W\u00e4hle Sensorressourcen." + } } } } diff --git a/homeassistant/components/nut/translations/el.json b/homeassistant/components/nut/translations/el.json index 971d6476a23..a42670f70d8 100644 --- a/homeassistant/components/nut/translations/el.json +++ b/homeassistant/components/nut/translations/el.json @@ -8,16 +8,9 @@ "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "step": { - "resources": { - "data": { - "resources": "\u03a0\u03cc\u03c1\u03bf\u03b9" - }, - "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03c0\u03cc\u03c1\u03bf\u03c5\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" - }, "ups": { "data": { - "alias": "\u03a8\u03b5\u03c5\u03b4\u03ce\u03bd\u03c5\u03bc\u03bf", - "resources": "\u03a0\u03cc\u03c1\u03bf\u03b9" + "alias": "\u03a8\u03b5\u03c5\u03b4\u03ce\u03bd\u03c5\u03bc\u03bf" }, "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf UPS \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, "step": { "init": { "data": { - "resources": "\u03a0\u03cc\u03c1\u03bf\u03b9", "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c0\u03cc\u03c1\u03bf\u03c5\u03c2 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03c9\u03bd." + } } } } diff --git a/homeassistant/components/nut/translations/en.json b/homeassistant/components/nut/translations/en.json index 3d57189f7a5..fb8e548902b 100644 --- a/homeassistant/components/nut/translations/en.json +++ b/homeassistant/components/nut/translations/en.json @@ -8,16 +8,9 @@ "unknown": "Unexpected error" }, "step": { - "resources": { - "data": { - "resources": "Resources" - }, - "title": "Choose the Resources to Monitor" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Resources" + "alias": "Alias" }, "title": "Choose the UPS to Monitor" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Failed to connect", - "unknown": "Unexpected error" - }, "step": { "init": { "data": { - "resources": "Resources", "scan_interval": "Scan Interval (seconds)" - }, - "description": "Choose Sensor Resources." + } } } } diff --git a/homeassistant/components/nut/translations/es-419.json b/homeassistant/components/nut/translations/es-419.json index b33bc854b23..992abf2d7c3 100644 --- a/homeassistant/components/nut/translations/es-419.json +++ b/homeassistant/components/nut/translations/es-419.json @@ -8,16 +8,9 @@ "unknown": "Error inesperado" }, "step": { - "resources": { - "data": { - "resources": "Recursos" - }, - "title": "Seleccione los recursos para monitorear" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Recursos" + "alias": "Alias" }, "title": "Seleccione el UPS para monitorear" }, @@ -36,10 +29,8 @@ "step": { "init": { "data": { - "resources": "Recursos", "scan_interval": "Intervalo de escaneo (segundos)" - }, - "description": "Seleccione los Recursos del sensor." + } } } } diff --git a/homeassistant/components/nut/translations/es.json b/homeassistant/components/nut/translations/es.json index 234f34082e1..898bf1f027d 100644 --- a/homeassistant/components/nut/translations/es.json +++ b/homeassistant/components/nut/translations/es.json @@ -8,16 +8,9 @@ "unknown": "Error inesperado" }, "step": { - "resources": { - "data": { - "resources": "Recursos" - }, - "title": "Selecciona los recursos a monitorizar" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Recursos" + "alias": "Alias" }, "title": "Selecciona el UPS a monitorizar" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "No se pudo conectar", - "unknown": "Error inesperado" - }, "step": { "init": { "data": { - "resources": "Recursos", "scan_interval": "Intervalo de escaneo (segundos)" - }, - "description": "Elige los recursos del sensor." + } } } } diff --git a/homeassistant/components/nut/translations/et.json b/homeassistant/components/nut/translations/et.json index 83d793eb22c..3b52b9856c7 100644 --- a/homeassistant/components/nut/translations/et.json +++ b/homeassistant/components/nut/translations/et.json @@ -8,16 +8,9 @@ "unknown": "Tundmatu viga" }, "step": { - "resources": { - "data": { - "resources": "Ressursid" - }, - "title": "Vali j\u00e4lgitavad ressursid" - }, "ups": { "data": { - "alias": "", - "resources": "Ressursid" + "alias": "" }, "title": "Vali j\u00e4lgitava UPS" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u00dchendus nurjus", - "unknown": "Tundmatu viga" - }, "step": { "init": { "data": { - "resources": "Ressursid", "scan_interval": "P\u00e4ringute intervall (sekundites)" - }, - "description": "Vali anduri ressursid." + } } } } diff --git a/homeassistant/components/nut/translations/fr.json b/homeassistant/components/nut/translations/fr.json index 3e1b345a8f1..18bde05e550 100644 --- a/homeassistant/components/nut/translations/fr.json +++ b/homeassistant/components/nut/translations/fr.json @@ -8,16 +8,9 @@ "unknown": "Erreur inattendue" }, "step": { - "resources": { - "data": { - "resources": "Ressources" - }, - "title": "Choisissez les ressources \u00e0 surveiller" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Ressources" + "alias": "Alias" }, "title": "Choisir l'UPS \u00e0 surveiller" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u00c9chec de connexion", - "unknown": "Erreur inattendue" - }, "step": { "init": { "data": { - "resources": "Ressources", "scan_interval": "Intervalle de balayage (secondes)" - }, - "description": "Choisissez les ressources des capteurs." + } } } } diff --git a/homeassistant/components/nut/translations/he.json b/homeassistant/components/nut/translations/he.json index 77c12630351..b3fb785d55e 100644 --- a/homeassistant/components/nut/translations/he.json +++ b/homeassistant/components/nut/translations/he.json @@ -17,11 +17,5 @@ } } } - }, - "options": { - "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" - } } } \ No newline at end of file diff --git a/homeassistant/components/nut/translations/hu.json b/homeassistant/components/nut/translations/hu.json index aa8f7c37105..498c12e2fcd 100644 --- a/homeassistant/components/nut/translations/hu.json +++ b/homeassistant/components/nut/translations/hu.json @@ -8,16 +8,9 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "step": { - "resources": { - "data": { - "resources": "Forr\u00e1sok" - }, - "title": "V\u00e1lassza ki a nyomon k\u00f6vetend\u0151 er\u0151forr\u00e1sokat" - }, "ups": { "data": { - "alias": "\u00c1ln\u00e9v", - "resources": "Forr\u00e1sok" + "alias": "\u00c1ln\u00e9v" }, "title": "V\u00e1lassza ki a fel\u00fcgyelni k\u00edv\u00e1nt UPS-t" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, "step": { "init": { "data": { - "resources": "Forr\u00e1sok", "scan_interval": "Szkennel\u00e9si intervallum (m\u00e1sodperc)" - }, - "description": "V\u00e1lassza az \u00c9rz\u00e9kel\u0151 er\u0151forr\u00e1sokat." + } } } } diff --git a/homeassistant/components/nut/translations/id.json b/homeassistant/components/nut/translations/id.json index fc23e34fe8e..709256b2bc2 100644 --- a/homeassistant/components/nut/translations/id.json +++ b/homeassistant/components/nut/translations/id.json @@ -8,16 +8,9 @@ "unknown": "Kesalahan yang tidak diharapkan" }, "step": { - "resources": { - "data": { - "resources": "Sumber Daya" - }, - "title": "Pilih Sumber Daya untuk Dipantau" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Sumber Daya" + "alias": "Alias" }, "title": "Pilih UPS untuk Dipantau" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Gagal terhubung", - "unknown": "Kesalahan yang tidak diharapkan" - }, "step": { "init": { "data": { - "resources": "Sumber Daya", "scan_interval": "Interval Pindai (detik)" - }, - "description": "Pilih Sumber Daya Sensor." + } } } } diff --git a/homeassistant/components/nut/translations/it.json b/homeassistant/components/nut/translations/it.json index 6b29911b28a..976c675a8b6 100644 --- a/homeassistant/components/nut/translations/it.json +++ b/homeassistant/components/nut/translations/it.json @@ -8,16 +8,9 @@ "unknown": "Errore imprevisto" }, "step": { - "resources": { - "data": { - "resources": "Risorse" - }, - "title": "Scegli le risorse da monitorare" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Risorse" + "alias": "Alias" }, "title": "Scegli l'UPS da monitorare" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Impossibile connettersi", - "unknown": "Errore imprevisto" - }, "step": { "init": { "data": { - "resources": "Risorse", "scan_interval": "Intervallo di scansione (secondi)" - }, - "description": "Scegli le risorse del sensore." + } } } } diff --git a/homeassistant/components/nut/translations/ja.json b/homeassistant/components/nut/translations/ja.json index 4c52cc74ed2..91f5aafb20d 100644 --- a/homeassistant/components/nut/translations/ja.json +++ b/homeassistant/components/nut/translations/ja.json @@ -8,16 +8,9 @@ "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "step": { - "resources": { - "data": { - "resources": "\u30ea\u30bd\u30fc\u30b9" - }, - "title": "\u76e3\u8996\u3059\u308b\u30ea\u30bd\u30fc\u30b9\u3092\u9078\u629e" - }, "ups": { "data": { - "alias": "\u30a8\u30a4\u30ea\u30a2\u30b9", - "resources": "\u30ea\u30bd\u30fc\u30b9" + "alias": "\u30a8\u30a4\u30ea\u30a2\u30b9" }, "title": "\u76e3\u8996\u3059\u308bUPS\u3092\u9078\u629e" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, "step": { "init": { "data": { - "resources": "\u30ea\u30bd\u30fc\u30b9", "scan_interval": "\u30b9\u30ad\u30e3\u30f3\u30a4\u30f3\u30bf\u30fc\u30d0\u30eb(\u79d2)" - }, - "description": "\u30bb\u30f3\u30b5\u30fc\u30ea\u30bd\u30fc\u30b9\u3092\u9078\u629e\u3057\u307e\u3059\u3002" + } } } } diff --git a/homeassistant/components/nut/translations/ko.json b/homeassistant/components/nut/translations/ko.json index a5680f4ed48..89bd39e35b5 100644 --- a/homeassistant/components/nut/translations/ko.json +++ b/homeassistant/components/nut/translations/ko.json @@ -8,16 +8,9 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { - "resources": { - "data": { - "resources": "\ub9ac\uc18c\uc2a4" - }, - "title": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \ub9ac\uc18c\uc2a4 \uc120\ud0dd\ud558\uae30" - }, "ups": { "data": { - "alias": "\ubcc4\uba85", - "resources": "\ub9ac\uc18c\uc2a4" + "alias": "\ubcc4\uba85" }, "title": "\ubaa8\ub2c8\ud130\ub9c1\ud560 UPS \uc120\ud0dd\ud558\uae30" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, "step": { "init": { "data": { - "resources": "\ub9ac\uc18c\uc2a4", "scan_interval": "\uc2a4\uce94 \uac04\uaca9 (\ucd08)" - }, - "description": "\uc13c\uc11c \ub9ac\uc18c\uc2a4\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694." + } } } } diff --git a/homeassistant/components/nut/translations/lb.json b/homeassistant/components/nut/translations/lb.json index da4840653c1..40a44ef19eb 100644 --- a/homeassistant/components/nut/translations/lb.json +++ b/homeassistant/components/nut/translations/lb.json @@ -8,16 +8,9 @@ "unknown": "Onerwaarte Feeler" }, "step": { - "resources": { - "data": { - "resources": "Ressourcen" - }, - "title": "Ressourcen auswielen fir z'iwwerwaachen" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Ressourcen" + "alias": "Alias" }, "title": "UPS fir z'iwwerwaachen auswielen" }, @@ -36,10 +29,8 @@ "step": { "init": { "data": { - "resources": "Ressourcen", "scan_interval": "Scan Intervall (sekonnen)" - }, - "description": "Sensor Ressourcen auswielen" + } } } } diff --git a/homeassistant/components/nut/translations/nl.json b/homeassistant/components/nut/translations/nl.json index d90b75b4bcc..ae8c91f75f9 100644 --- a/homeassistant/components/nut/translations/nl.json +++ b/homeassistant/components/nut/translations/nl.json @@ -8,16 +8,9 @@ "unknown": "Onverwachte fout" }, "step": { - "resources": { - "data": { - "resources": "Bronnen" - }, - "title": "Kies de te controleren bronnen" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Bronnen" + "alias": "Alias" }, "title": "Kies een UPS om uit te lezen" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Verbinden mislukt", - "unknown": "Onverwachte fout" - }, "step": { "init": { "data": { - "resources": "Bronnen", "scan_interval": "Scaninterval (seconden)" - }, - "description": "Kies Sensorbronnen." + } } } } diff --git a/homeassistant/components/nut/translations/no.json b/homeassistant/components/nut/translations/no.json index 887d3b6d30a..d657b67fd07 100644 --- a/homeassistant/components/nut/translations/no.json +++ b/homeassistant/components/nut/translations/no.json @@ -8,16 +8,9 @@ "unknown": "Uventet feil" }, "step": { - "resources": { - "data": { - "resources": "Ressurser" - }, - "title": "Velg ressurser som skal overv\u00e5kes" - }, "ups": { "data": { - "alias": "", - "resources": "Ressurser" + "alias": "" }, "title": "Velg UPS som skal overv\u00e5kes" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Tilkobling mislyktes", - "unknown": "Uventet feil" - }, "step": { "init": { "data": { - "resources": "Ressurser", "scan_interval": "Skanneintervall (sekunder)" - }, - "description": "Velg Sensor Ressurser." + } } } } diff --git a/homeassistant/components/nut/translations/pl.json b/homeassistant/components/nut/translations/pl.json index 2686a98e697..3f9d3c89b41 100644 --- a/homeassistant/components/nut/translations/pl.json +++ b/homeassistant/components/nut/translations/pl.json @@ -8,16 +8,9 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { - "resources": { - "data": { - "resources": "Zasoby" - }, - "title": "Wybierz zasoby do monitorowania" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Zasoby" + "alias": "Alias" }, "title": "Wybierz UPS do monitorowania" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, "step": { "init": { "data": { - "resources": "Zasoby", "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (sekundy)" - }, - "description": "Wybierz zasoby sensor\u00f3w." + } } } } diff --git a/homeassistant/components/nut/translations/pt-BR.json b/homeassistant/components/nut/translations/pt-BR.json index d6be7b41044..ee2c84a7777 100644 --- a/homeassistant/components/nut/translations/pt-BR.json +++ b/homeassistant/components/nut/translations/pt-BR.json @@ -8,16 +8,9 @@ "unknown": "Erro inesperado" }, "step": { - "resources": { - "data": { - "resources": "Recursos" - }, - "title": "Escolha os recursos para monitorar" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Recursos" + "alias": "Alias" }, "title": "Escolha o no-break (UPS) para monitorar" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Falha ao conectar", - "unknown": "Erro inesperado" - }, "step": { "init": { "data": { - "resources": "Recursos", "scan_interval": "Intervalo de escaneamento (segundos)" - }, - "description": "Escolha os recursos dos sensores." + } } } } diff --git a/homeassistant/components/nut/translations/pt.json b/homeassistant/components/nut/translations/pt.json index f5e8690e383..9f6c04e78a3 100644 --- a/homeassistant/components/nut/translations/pt.json +++ b/homeassistant/components/nut/translations/pt.json @@ -8,11 +8,6 @@ "unknown": "Erro inesperado" }, "step": { - "ups": { - "data": { - "resources": "Recursos" - } - }, "user": { "data": { "host": "Servidor", @@ -22,11 +17,5 @@ } } } - }, - "options": { - "error": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "unknown": "Erro inesperado" - } } } \ No newline at end of file diff --git a/homeassistant/components/nut/translations/ru.json b/homeassistant/components/nut/translations/ru.json index 071a7d0f09c..230c4d3d869 100644 --- a/homeassistant/components/nut/translations/ru.json +++ b/homeassistant/components/nut/translations/ru.json @@ -8,16 +8,9 @@ "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "step": { - "resources": { - "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u044b" - }, - "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430" - }, "ups": { "data": { - "alias": "\u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c", - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u044b" + "alias": "\u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c" }, "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0418\u0411\u041f \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, "step": { "init": { "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u044b", "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0441\u0435\u043d\u0441\u043e\u0440\u043e\u0432." + } } } } diff --git a/homeassistant/components/nut/translations/sk.json b/homeassistant/components/nut/translations/sk.json index 434ad4a26b2..d00d818716f 100644 --- a/homeassistant/components/nut/translations/sk.json +++ b/homeassistant/components/nut/translations/sk.json @@ -8,16 +8,9 @@ "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" }, "step": { - "resources": { - "data": { - "resources": "Prostriedky" - }, - "title": "Vyberte prostriedky, ktor\u00e9 chcete monitorova\u0165" - }, "ups": { "data": { - "alias": "Alias", - "resources": "Prostriedky" + "alias": "Alias" }, "title": "Vyberte UPS, ktor\u00fa chcete monitorova\u0165" }, @@ -32,17 +25,11 @@ } }, "options": { - "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165", - "unknown": "Neo\u010dak\u00e1van\u00e1 chyba" - }, "step": { "init": { "data": { - "resources": "Prostriedky", "scan_interval": "Skenovac\u00ed interval (v sekund\u00e1ch)" - }, - "description": "Vyberte senzory." + } } } } diff --git a/homeassistant/components/nut/translations/sl.json b/homeassistant/components/nut/translations/sl.json index 1f0b269562d..156d3dc7d92 100644 --- a/homeassistant/components/nut/translations/sl.json +++ b/homeassistant/components/nut/translations/sl.json @@ -8,16 +8,9 @@ "unknown": "Nepri\u010dakovana napaka" }, "step": { - "resources": { - "data": { - "resources": "Viri" - }, - "title": "Izberite vire za spremljanje" - }, "ups": { "data": { - "alias": "Vzdevek", - "resources": "Viri" + "alias": "Vzdevek" }, "title": "Izberite UPS za spremljanje" }, @@ -36,10 +29,8 @@ "step": { "init": { "data": { - "resources": "Viri", "scan_interval": "Interval skeniranja (sekunde)" - }, - "description": "Izberite vire senzorja." + } } } } diff --git a/homeassistant/components/nut/translations/sv.json b/homeassistant/components/nut/translations/sv.json index 45832197f68..9a3cb690744 100644 --- a/homeassistant/components/nut/translations/sv.json +++ b/homeassistant/components/nut/translations/sv.json @@ -8,12 +8,6 @@ "unknown": "Ov\u00e4ntat fel" }, "step": { - "resources": { - "data": { - "resources": "Resurser" - }, - "title": "V\u00e4lj resurser som ska \u00f6vervakas" - }, "ups": { "data": { "alias": "Alias" @@ -31,10 +25,6 @@ } }, "options": { - "error": { - "cannot_connect": "Kunde inte ansluta", - "unknown": "Ov\u00e4ntat fel" - }, "step": { "init": { "data": { diff --git a/homeassistant/components/nut/translations/tr.json b/homeassistant/components/nut/translations/tr.json index 7802c451fd0..e24f982268c 100644 --- a/homeassistant/components/nut/translations/tr.json +++ b/homeassistant/components/nut/translations/tr.json @@ -8,16 +8,9 @@ "unknown": "Beklenmeyen hata" }, "step": { - "resources": { - "data": { - "resources": "Kaynaklar" - }, - "title": "\u0130zlenecek Kaynaklar\u0131 Se\u00e7in" - }, "ups": { "data": { - "alias": "Takma ad", - "resources": "Kaynaklar" + "alias": "Takma ad" }, "title": "\u0130zlenecek UPS'i Se\u00e7in" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "unknown": "Beklenmeyen hata" - }, "step": { "init": { "data": { - "resources": "Kaynaklar", "scan_interval": "Tarama Aral\u0131\u011f\u0131 (saniye)" - }, - "description": "Sens\u00f6r Kaynaklar\u0131'n\u0131 se\u00e7in." + } } } } diff --git a/homeassistant/components/nut/translations/uk.json b/homeassistant/components/nut/translations/uk.json index b25fe854560..64c8a44d219 100644 --- a/homeassistant/components/nut/translations/uk.json +++ b/homeassistant/components/nut/translations/uk.json @@ -8,16 +8,9 @@ "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, "step": { - "resources": { - "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u0438" - }, - "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u0438 \u0434\u043b\u044f \u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u043d\u0433\u0443" - }, "ups": { "data": { - "alias": "\u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0456\u043c", - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u0438" + "alias": "\u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0456\u043c" }, "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c UPS \u0434\u043b\u044f \u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u043d\u0433\u0443" }, @@ -36,10 +29,8 @@ "step": { "init": { "data": { - "resources": "\u0420\u0435\u0441\u0443\u0440\u0441\u0438", "scan_interval": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043a\u0430\u043d\u0443\u0432\u0430\u043d\u043d\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0456\u0432." + } } } } diff --git a/homeassistant/components/nut/translations/zh-Hans.json b/homeassistant/components/nut/translations/zh-Hans.json index 4afd1ff0031..deb09194b47 100644 --- a/homeassistant/components/nut/translations/zh-Hans.json +++ b/homeassistant/components/nut/translations/zh-Hans.json @@ -8,16 +8,9 @@ "unknown": "\u672a\u77e5\u9519\u8bef" }, "step": { - "resources": { - "data": { - "resources": "\u8d44\u6e90" - }, - "title": "\u9009\u62e9\u8981\u76d1\u89c6\u7684\u8d44\u6e90" - }, "ups": { "data": { - "alias": "\u522b\u540d", - "resources": "\u8d44\u6e90" + "alias": "\u522b\u540d" }, "title": "\u9009\u62e9\u8981\u76d1\u63a7\u7684 UPS" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u8fde\u63a5\u5931\u8d25", - "unknown": "\u4e0d\u5728\u9884\u671f\u5185\u7684\u9519\u8bef" - }, "step": { "init": { "data": { - "resources": "\u8d44\u6e90", "scan_interval": "\u626b\u63cf\u95f4\u9694\uff08\u79d2\uff09" - }, - "description": "\u9009\u62e9\u8981\u76d1\u89c6\u7684\u8d44\u6e90" + } } } } diff --git a/homeassistant/components/nut/translations/zh-Hant.json b/homeassistant/components/nut/translations/zh-Hant.json index 3b1daf88bf1..71dc2f78402 100644 --- a/homeassistant/components/nut/translations/zh-Hant.json +++ b/homeassistant/components/nut/translations/zh-Hant.json @@ -8,16 +8,9 @@ "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "step": { - "resources": { - "data": { - "resources": "\u8cc7\u6e90" - }, - "title": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684\u8cc7\u6e90" - }, "ups": { "data": { - "alias": "\u5225\u540d", - "resources": "\u8cc7\u6e90" + "alias": "\u5225\u540d" }, "title": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684 UPS" }, @@ -33,17 +26,11 @@ } }, "options": { - "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, "step": { "init": { "data": { - "resources": "\u8cc7\u6e90", "scan_interval": "\u6383\u63cf\u9593\u8ddd\uff08\u79d2\uff09" - }, - "description": "\u9078\u64c7\u611f\u6e2c\u5668\u8cc7\u6e90\u3002" + } } } } diff --git a/homeassistant/components/onewire/translations/af.json b/homeassistant/components/onewire/translations/af.json deleted file mode 100644 index 779febe67cd..00000000000 --- a/homeassistant/components/onewire/translations/af.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "config": { - "error": { - "invalid_path": "Verzeichnis nicht gefunden." - }, - "step": { - "user": { - "data": { - "type": "Verbindungstyp" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/bg.json b/homeassistant/components/onewire/translations/bg.json index a14f7fa00a2..4893ecc7a5d 100644 --- a/homeassistant/components/onewire/translations/bg.json +++ b/homeassistant/components/onewire/translations/bg.json @@ -4,21 +4,13 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_path": "\u0414\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f\u0442\u0430 \u043d\u0435 \u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0430." + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { - "owserver": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442" - } - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "type": "\u0412\u0438\u0434 \u043d\u0430 \u0432\u0440\u044a\u0437\u043a\u0430\u0442\u0430" + "port": "\u041f\u043e\u0440\u0442" } } } diff --git a/homeassistant/components/onewire/translations/ca.json b/homeassistant/components/onewire/translations/ca.json index a8f4da41b6b..ae434bc5be4 100644 --- a/homeassistant/components/onewire/translations/ca.json +++ b/homeassistant/components/onewire/translations/ca.json @@ -4,22 +4,13 @@ "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_path": "No s'ha trobat el directori." + "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { - "owserver": { - "data": { - "host": "Amfitri\u00f3", - "port": "Port" - }, - "title": "Defineix els detalls d'owserver" - }, "user": { "data": { "host": "Amfitri\u00f3", - "port": "Port", - "type": "Tipus de connexi\u00f3" + "port": "Port" }, "title": "Detalls del servidor" } @@ -30,10 +21,6 @@ "device_not_selected": "Selecciona els dispositius a configurar" }, "step": { - "ack_no_options": { - "description": "No hi ha opcions per a la implementaci\u00f3 de SysBus", - "title": "Opcions SysBus de OneWire" - }, "configure_device": { "data": { "precision": "Precisi\u00f3 del sensor" diff --git a/homeassistant/components/onewire/translations/cs.json b/homeassistant/components/onewire/translations/cs.json index c5298d095d8..ae22bc1ce25 100644 --- a/homeassistant/components/onewire/translations/cs.json +++ b/homeassistant/components/onewire/translations/cs.json @@ -4,21 +4,10 @@ "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno" }, "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_path": "Adres\u00e1\u0159 nebyl nalezen." + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { - "owserver": { - "data": { - "host": "Hostitel", - "port": "Port" - }, - "title": "Nastaven\u00ed owserver" - }, "user": { - "data": { - "type": "Typ p\u0159ipojen\u00ed" - }, "title": "Nastaven\u00ed 1-Wire" } } diff --git a/homeassistant/components/onewire/translations/de.json b/homeassistant/components/onewire/translations/de.json index 14f321c8004..feab77f3ec7 100644 --- a/homeassistant/components/onewire/translations/de.json +++ b/homeassistant/components/onewire/translations/de.json @@ -4,22 +4,13 @@ "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_path": "Verzeichnis nicht gefunden." + "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Port" - }, - "title": "owserver-Details einstellen" - }, "user": { "data": { "host": "Host", - "port": "Port", - "type": "Verbindungstyp" + "port": "Port" }, "title": "Serverdetails festlegen" } @@ -30,10 +21,6 @@ "device_not_selected": "Zu konfigurierende Ger\u00e4te ausw\u00e4hlen" }, "step": { - "ack_no_options": { - "description": "Es gibt keine Optionen f\u00fcr die SysBus-Implementierung", - "title": "OneWire SysBus-Optionen" - }, "configure_device": { "data": { "precision": "Sensorgenauigkeit" diff --git a/homeassistant/components/onewire/translations/el.json b/homeassistant/components/onewire/translations/el.json index b0d37d679bc..1f70ecff348 100644 --- a/homeassistant/components/onewire/translations/el.json +++ b/homeassistant/components/onewire/translations/el.json @@ -4,22 +4,13 @@ "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_path": "\u039f \u03ba\u03b1\u03c4\u03ac\u03bb\u03bf\u03b3\u03bf\u03c2 \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5." + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { - "owserver": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1" - }, - "title": "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03b5\u03c1\u03b5\u03b9\u03ce\u03bd owserver" - }, "user": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1", - "type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" + "port": "\u0398\u03cd\u03c1\u03b1" }, "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 1-Wire" } @@ -30,10 +21,6 @@ "device_not_selected": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7" }, "step": { - "ack_no_options": { - "description": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c5\u03bb\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 SysBus", - "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 SysBus OneWire" - }, "configure_device": { "data": { "precision": "\u0391\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" diff --git a/homeassistant/components/onewire/translations/en.json b/homeassistant/components/onewire/translations/en.json index d8e2c157e0f..62b4dddf659 100644 --- a/homeassistant/components/onewire/translations/en.json +++ b/homeassistant/components/onewire/translations/en.json @@ -4,22 +4,13 @@ "already_configured": "Device is already configured" }, "error": { - "cannot_connect": "Failed to connect", - "invalid_path": "Directory not found." + "cannot_connect": "Failed to connect" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Port" - }, - "title": "Set owserver details" - }, "user": { "data": { "host": "Host", - "port": "Port", - "type": "Connection type" + "port": "Port" }, "title": "Set server details" } @@ -30,10 +21,6 @@ "device_not_selected": "Select devices to configure" }, "step": { - "ack_no_options": { - "description": "There are no options for the SysBus implementation", - "title": "OneWire SysBus Options" - }, "configure_device": { "data": { "precision": "Sensor Precision" diff --git a/homeassistant/components/onewire/translations/es.json b/homeassistant/components/onewire/translations/es.json index cb13e1992bd..9fa4912166f 100644 --- a/homeassistant/components/onewire/translations/es.json +++ b/homeassistant/components/onewire/translations/es.json @@ -4,22 +4,13 @@ "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { - "cannot_connect": "No se pudo conectar", - "invalid_path": "Directorio no encontrado." + "cannot_connect": "No se pudo conectar" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Puerto" - }, - "title": "Configurar los detalles del servidor" - }, "user": { "data": { "host": "Host", - "port": "Puerto", - "type": "Tipo de conexi\u00f3n" + "port": "Puerto" }, "title": "Configurar 1 cable" } @@ -30,9 +21,6 @@ "device_not_selected": "Seleccionar los dispositivos a configurar" }, "step": { - "ack_no_options": { - "title": "Opciones SysBus de OneWire" - }, "configure_device": { "data": { "precision": "Precisi\u00f3n del sensor" diff --git a/homeassistant/components/onewire/translations/et.json b/homeassistant/components/onewire/translations/et.json index 7e57911127e..cc3588be15a 100644 --- a/homeassistant/components/onewire/translations/et.json +++ b/homeassistant/components/onewire/translations/et.json @@ -4,22 +4,13 @@ "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { - "cannot_connect": "\u00dchendus nurjus", - "invalid_path": "Kausta ei leitud." + "cannot_connect": "\u00dchendus nurjus" }, "step": { - "owserver": { - "data": { - "host": "", - "port": "" - }, - "title": "M\u00e4\u00e4ra owserver-i \u00fcksikasjad" - }, "user": { "data": { "host": "Host", - "port": "Port", - "type": "\u00dchenduse t\u00fc\u00fcp" + "port": "Port" }, "title": "M\u00e4\u00e4ra serveri \u00fcksikasjad" } @@ -30,10 +21,6 @@ "device_not_selected": "Vali seadistatav seade" }, "step": { - "ack_no_options": { - "description": "SysBusi rakendamiseks pole v\u00f5imalusi", - "title": "OneWire SysBusi valikud" - }, "configure_device": { "data": { "precision": "Anduri t\u00e4psus" diff --git a/homeassistant/components/onewire/translations/fr.json b/homeassistant/components/onewire/translations/fr.json index 3d9e01916c3..937aa719f3d 100644 --- a/homeassistant/components/onewire/translations/fr.json +++ b/homeassistant/components/onewire/translations/fr.json @@ -4,22 +4,13 @@ "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { - "cannot_connect": "\u00c9chec de connexion", - "invalid_path": "R\u00e9pertoire introuvable." + "cannot_connect": "\u00c9chec de connexion" }, "step": { - "owserver": { - "data": { - "host": "H\u00f4te", - "port": "Port" - }, - "title": "D\u00e9finir les d\u00e9tails d'owserver" - }, "user": { "data": { "host": "H\u00f4te", - "port": "Port", - "type": "Type de connexion" + "port": "Port" }, "title": "Renseigner les informations du serveur" } @@ -30,10 +21,6 @@ "device_not_selected": "S\u00e9lectionnez les appareils \u00e0 configurer" }, "step": { - "ack_no_options": { - "description": "Il n'y a pas d'option pour l'impl\u00e9mentation de SysBus", - "title": "Options de bus syst\u00e8me OneWire" - }, "configure_device": { "data": { "precision": "Pr\u00e9cision du capteur" diff --git a/homeassistant/components/onewire/translations/he.json b/homeassistant/components/onewire/translations/he.json index d83d1f76175..c3a67844fdd 100644 --- a/homeassistant/components/onewire/translations/he.json +++ b/homeassistant/components/onewire/translations/he.json @@ -7,7 +7,7 @@ "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { - "owserver": { + "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7", "port": "\u05e4\u05ea\u05d7\u05d4" diff --git a/homeassistant/components/onewire/translations/hu.json b/homeassistant/components/onewire/translations/hu.json index e26721758cd..faae98589e3 100644 --- a/homeassistant/components/onewire/translations/hu.json +++ b/homeassistant/components/onewire/translations/hu.json @@ -4,22 +4,13 @@ "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_path": "A k\u00f6nyvt\u00e1r nem tal\u00e1lhat\u00f3." + "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { - "owserver": { - "data": { - "host": "C\u00edm", - "port": "Port" - }, - "title": "Owserver adatok be\u00e1ll\u00edt\u00e1sa" - }, "user": { "data": { "host": "C\u00edm", - "port": "Port", - "type": "Kapcsolat t\u00edpusa" + "port": "Port" }, "title": "A 1-Wire be\u00e1ll\u00edt\u00e1sa" } @@ -34,9 +25,7 @@ "data": { "one": "\u00dcres", "other": "\u00dcres" - }, - "description": "A SysBus implement\u00e1ci\u00f3j\u00e1nak nincsenek opci\u00f3i.", - "title": "OneWire SysBus opci\u00f3k" + } }, "configure_device": { "data": { diff --git a/homeassistant/components/onewire/translations/id.json b/homeassistant/components/onewire/translations/id.json index 203a38d1a4c..46d04ad89f4 100644 --- a/homeassistant/components/onewire/translations/id.json +++ b/homeassistant/components/onewire/translations/id.json @@ -4,22 +4,13 @@ "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { - "cannot_connect": "Gagal terhubung", - "invalid_path": "Direktori tidak ditemukan." + "cannot_connect": "Gagal terhubung" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Port" - }, - "title": "Tetapkan detail owserver" - }, "user": { "data": { "host": "Host", - "port": "Port", - "type": "Jenis koneksi" + "port": "Port" }, "title": "Setel detail server" } @@ -30,10 +21,6 @@ "device_not_selected": "Pilih perangkat untuk dikonfigurasi" }, "step": { - "ack_no_options": { - "description": "Tidak ada opsi untuk implementasi SysBus", - "title": "Opsi OneWire SysBus" - }, "configure_device": { "data": { "precision": "Presisi Sensor" diff --git a/homeassistant/components/onewire/translations/it.json b/homeassistant/components/onewire/translations/it.json index a6bffe1e5fc..9679f7f3e69 100644 --- a/homeassistant/components/onewire/translations/it.json +++ b/homeassistant/components/onewire/translations/it.json @@ -4,22 +4,13 @@ "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { - "cannot_connect": "Impossibile connettersi", - "invalid_path": "Cartella non trovata." + "cannot_connect": "Impossibile connettersi" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Porta" - }, - "title": "Imposta i dettagli dell'owserver" - }, "user": { "data": { "host": "Host", - "port": "Porta", - "type": "Tipo di connessione" + "port": "Porta" }, "title": "Imposta i dettagli del server" } @@ -34,9 +25,7 @@ "data": { "one": "Vuoto", "other": "Vuoti" - }, - "description": "Non ci sono opzioni per l'implementazione SysBus", - "title": "Opzioni SysBus OneWire" + } }, "configure_device": { "data": { diff --git a/homeassistant/components/onewire/translations/ja.json b/homeassistant/components/onewire/translations/ja.json index b4acd54cd41..75b01a0cbc9 100644 --- a/homeassistant/components/onewire/translations/ja.json +++ b/homeassistant/components/onewire/translations/ja.json @@ -4,22 +4,13 @@ "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_path": "\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { - "owserver": { - "data": { - "host": "\u30db\u30b9\u30c8", - "port": "\u30dd\u30fc\u30c8" - }, - "title": "owserver\u306e\u8a73\u7d30\u8a2d\u5b9a" - }, "user": { "data": { "host": "\u30db\u30b9\u30c8", - "port": "\u30dd\u30fc\u30c8", - "type": "\u63a5\u7d9a\u30bf\u30a4\u30d7" + "port": "\u30dd\u30fc\u30c8" }, "title": "1-Wire\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" } @@ -30,10 +21,6 @@ "device_not_selected": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e" }, "step": { - "ack_no_options": { - "description": "SysBus\u306e\u5b9f\u88c5\u306b\u95a2\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u3042\u308a\u307e\u305b\u3093", - "title": "OneWire SysBus\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" - }, "configure_device": { "data": { "precision": "\u30bb\u30f3\u30b5\u30fc\u306e\u7cbe\u5ea6" diff --git a/homeassistant/components/onewire/translations/ka.json b/homeassistant/components/onewire/translations/ka.json index 1b3c7e8ef5c..be486ef2f62 100644 --- a/homeassistant/components/onewire/translations/ka.json +++ b/homeassistant/components/onewire/translations/ka.json @@ -4,21 +4,10 @@ "already_configured": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d0 \u10e3\u10d9\u10d5\u10d4 \u10d3\u10d0\u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8\u10d0" }, "error": { - "cannot_connect": "\u10d3\u10d0\u10d9\u10d0\u10d5\u10e8\u10e0\u10d4\u10d1\u10d0 \u10d5\u10d4\u10e0 \u10db\u10dd\u10ee\u10d4\u10e0\u10ee\u10d3\u10d0", - "invalid_path": "\u10d3\u10d8\u10e0\u10d4\u10e5\u10e2\u10dd\u10e0\u10d8\u10d0 \u10d5\u10d4\u10e0 \u10db\u10dd\u10d8\u10eb\u10d4\u10d1\u10dc\u10d0." + "cannot_connect": "\u10d3\u10d0\u10d9\u10d0\u10d5\u10e8\u10e0\u10d4\u10d1\u10d0 \u10d5\u10d4\u10e0 \u10db\u10dd\u10ee\u10d4\u10e0\u10ee\u10d3\u10d0" }, "step": { - "owserver": { - "data": { - "host": "\u10f0\u10dd\u10e1\u10e2\u10d8", - "port": "\u10de\u10dd\u10e0\u10e2\u10d8" - }, - "title": "owserver \u10e1\u10d4\u10e0\u10d5\u10d4\u10e0\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0" - }, "user": { - "data": { - "type": "\u1c99\u10d0\u10d5\u10e8\u10d8\u10e0\u10d8\u10e1 \u10e2\u10d8\u10de\u10d8" - }, "title": "1-\u10db\u10d0\u10d5\u10d7\u10d8\u10da\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0" } } diff --git a/homeassistant/components/onewire/translations/ko.json b/homeassistant/components/onewire/translations/ko.json index e6665d83768..eeaa8f4c09d 100644 --- a/homeassistant/components/onewire/translations/ko.json +++ b/homeassistant/components/onewire/translations/ko.json @@ -4,22 +4,13 @@ "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "invalid_path": "\ub514\ub809\ud130\ub9ac\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { - "owserver": { - "data": { - "host": "\ud638\uc2a4\ud2b8", - "port": "\ud3ec\ud2b8" - }, - "title": "owserver \uc138\ubd80 \uc815\ubcf4 \uc124\uc815\ud558\uae30" - }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8", - "port": "\ud3ec\ud2b8", - "type": "\uc5f0\uacb0 \uc720\ud615" + "port": "\ud3ec\ud2b8" }, "title": "1-Wire \uc124\uc815\ud558\uae30" } diff --git a/homeassistant/components/onewire/translations/lb.json b/homeassistant/components/onewire/translations/lb.json index c7c302ab683..c403b09531a 100644 --- a/homeassistant/components/onewire/translations/lb.json +++ b/homeassistant/components/onewire/translations/lb.json @@ -4,21 +4,7 @@ "already_configured": "Apparat ass scho konfigur\u00e9iert" }, "error": { - "cannot_connect": "Feeler beim verbannen", - "invalid_path": "Dossier net fonnt" - }, - "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Port" - } - }, - "user": { - "data": { - "type": "Typ vun der Verbindung" - } - } + "cannot_connect": "Feeler beim verbannen" } } } \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/nl.json b/homeassistant/components/onewire/translations/nl.json index e6af2cebef3..652e5b17fd5 100644 --- a/homeassistant/components/onewire/translations/nl.json +++ b/homeassistant/components/onewire/translations/nl.json @@ -4,22 +4,13 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kan geen verbinding maken", - "invalid_path": "Directory niet gevonden." + "cannot_connect": "Kan geen verbinding maken" }, "step": { - "owserver": { - "data": { - "host": "Host", - "port": "Poort" - }, - "title": "Owserver-details instellen" - }, "user": { "data": { "host": "Host", - "port": "Poort", - "type": "Verbindingstype" + "port": "Poort" }, "title": "Serverdetails instellen" } @@ -34,9 +25,7 @@ "data": { "one": "Leeg", "other": "Ander" - }, - "description": "Er zijn geen opties voor de SysBus implementatie", - "title": "OneWire SysBus opties" + } }, "configure_device": { "data": { diff --git a/homeassistant/components/onewire/translations/no.json b/homeassistant/components/onewire/translations/no.json index 89529aa7810..13b3df80fde 100644 --- a/homeassistant/components/onewire/translations/no.json +++ b/homeassistant/components/onewire/translations/no.json @@ -4,22 +4,13 @@ "already_configured": "Enheten er allerede konfigurert" }, "error": { - "cannot_connect": "Tilkobling mislyktes", - "invalid_path": "Finner ikke mappen" + "cannot_connect": "Tilkobling mislyktes" }, "step": { - "owserver": { - "data": { - "host": "Vert", - "port": "Port" - }, - "title": "Angi detaljer om owserver" - }, "user": { "data": { "host": "Vert", - "port": "Port", - "type": "Tilkoblingstype" + "port": "Port" }, "title": "Angi serverdetaljer" } @@ -30,10 +21,6 @@ "device_not_selected": "Velg enheter som skal konfigureres" }, "step": { - "ack_no_options": { - "description": "Det er ingen alternativer for SysBus-implementeringen", - "title": "OneWire SysBus-alternativer" - }, "configure_device": { "data": { "precision": "Sensorpresisjon" diff --git a/homeassistant/components/onewire/translations/pl.json b/homeassistant/components/onewire/translations/pl.json index 743a1b28522..523afbd98d9 100644 --- a/homeassistant/components/onewire/translations/pl.json +++ b/homeassistant/components/onewire/translations/pl.json @@ -4,22 +4,13 @@ "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_path": "Nie znaleziono katalogu" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { - "owserver": { - "data": { - "host": "Nazwa hosta lub adres IP", - "port": "Port" - }, - "title": "Ustawienia owserver" - }, "user": { "data": { "host": "Nazwa hosta lub adres IP", - "port": "Port", - "type": "Rodzaj po\u0142\u0105czenia" + "port": "Port" }, "title": "Konfiguracja serwera" } @@ -30,10 +21,6 @@ "device_not_selected": "Wybierz urz\u0105dzenia do skonfigurowania" }, "step": { - "ack_no_options": { - "description": "Nie ma opcji dla implementacji magistrali SysBus", - "title": "Opcje magistrali OneWire SysBus" - }, "configure_device": { "data": { "precision": "Dok\u0142adno\u015b\u0107 sensora" diff --git a/homeassistant/components/onewire/translations/pt-BR.json b/homeassistant/components/onewire/translations/pt-BR.json index 74fe2918222..303d36f3cb2 100644 --- a/homeassistant/components/onewire/translations/pt-BR.json +++ b/homeassistant/components/onewire/translations/pt-BR.json @@ -4,22 +4,13 @@ "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { - "cannot_connect": "Falha ao conectar", - "invalid_path": "Diret\u00f3rio n\u00e3o encontrado." + "cannot_connect": "Falha ao conectar" }, "step": { - "owserver": { - "data": { - "host": "Nome do host", - "port": "Porta" - }, - "title": "Definir detalhes do servidor" - }, "user": { "data": { "host": "Host", - "port": "Porta", - "type": "Tipo de conex\u00e3o" + "port": "Porta" }, "title": "Definir detalhes do servidor" } @@ -30,10 +21,6 @@ "device_not_selected": "Selecione os dispositivos para configurar" }, "step": { - "ack_no_options": { - "description": "N\u00e3o h\u00e1 op\u00e7\u00f5es para a implementa\u00e7\u00e3o do SysBus", - "title": "Op\u00e7\u00f5es de OneWire SysBus" - }, "configure_device": { "data": { "precision": "Precis\u00e3o do Sensor" diff --git a/homeassistant/components/onewire/translations/pt.json b/homeassistant/components/onewire/translations/pt.json index bd1e14729e0..db0e0c2a137 100644 --- a/homeassistant/components/onewire/translations/pt.json +++ b/homeassistant/components/onewire/translations/pt.json @@ -5,14 +5,6 @@ }, "error": { "cannot_connect": "Falha na liga\u00e7\u00e3o" - }, - "step": { - "owserver": { - "data": { - "host": "Servidor", - "port": "Porta" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/onewire/translations/ru.json b/homeassistant/components/onewire/translations/ru.json index d0ab8b8a282..300deadb447 100644 --- a/homeassistant/components/onewire/translations/ru.json +++ b/homeassistant/components/onewire/translations/ru.json @@ -4,22 +4,13 @@ "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_path": "\u041a\u0430\u0442\u0430\u043b\u043e\u0433 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { - "owserver": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442" - }, - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a owserver" - }, "user": { "data": { "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "type": "\u0422\u0438\u043f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" + "port": "\u041f\u043e\u0440\u0442" }, "title": "1-Wire" } @@ -30,10 +21,6 @@ "device_not_selected": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" }, "step": { - "ack_no_options": { - "description": "\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 SysBus \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043a\u0430\u043a\u0438\u0435-\u043b\u0438\u0431\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 OneWire SysBus" - }, "configure_device": { "data": { "precision": "\u0422\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u0434\u0430\u0442\u0447\u0438\u043a\u0430" diff --git a/homeassistant/components/onewire/translations/sk.json b/homeassistant/components/onewire/translations/sk.json index bd43098e555..8b2a2f7343b 100644 --- a/homeassistant/components/onewire/translations/sk.json +++ b/homeassistant/components/onewire/translations/sk.json @@ -4,20 +4,10 @@ "already_configured": "Zariadenie u\u017e je nakonfigurovan\u00e9" }, "error": { - "cannot_connect": "Nepodarilo sa pripoji\u0165", - "invalid_path": "Adres\u00e1r sa nena\u0161iel." + "cannot_connect": "Nepodarilo sa pripoji\u0165" }, "step": { - "owserver": { - "data": { - "port": "Port" - }, - "title": "Nastavenie owserver" - }, "user": { - "data": { - "type": "Typ pripojenia" - }, "title": "Nastavenie 1-Wire" } } diff --git a/homeassistant/components/onewire/translations/sl.json b/homeassistant/components/onewire/translations/sl.json index 7011c57c099..8797272c16c 100644 --- a/homeassistant/components/onewire/translations/sl.json +++ b/homeassistant/components/onewire/translations/sl.json @@ -2,9 +2,6 @@ "config": { "step": { "user": { - "data": { - "type": "Vrsta povezave" - }, "title": "Nastavite 1-Wire" } } diff --git a/homeassistant/components/onewire/translations/sv.json b/homeassistant/components/onewire/translations/sv.json index 3265a54aeaf..1b100c60d97 100644 --- a/homeassistant/components/onewire/translations/sv.json +++ b/homeassistant/components/onewire/translations/sv.json @@ -1,13 +1,4 @@ { - "config": { - "step": { - "owserver": { - "data": { - "port": "Port" - } - } - } - }, "options": { "step": { "device_selection": { diff --git a/homeassistant/components/onewire/translations/tr.json b/homeassistant/components/onewire/translations/tr.json index 4859b5ec865..5c7257319c8 100644 --- a/homeassistant/components/onewire/translations/tr.json +++ b/homeassistant/components/onewire/translations/tr.json @@ -4,22 +4,13 @@ "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_path": "Dizin bulunamad\u0131." + "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { - "owserver": { - "data": { - "host": "Sunucu", - "port": "Port" - }, - "title": "Sunucu ayr\u0131nt\u0131lar\u0131n\u0131 ayarla" - }, "user": { "data": { "host": "Sunucu", - "port": "Port", - "type": "Ba\u011flant\u0131 t\u00fcr\u00fc" + "port": "Port" }, "title": "Sunucu ayr\u0131nt\u0131lar\u0131n\u0131 ayarla" } @@ -34,9 +25,7 @@ "data": { "one": "Bo\u015f", "other": "Bo\u015f" - }, - "description": "SysBus uygulamas\u0131 i\u00e7in se\u00e7enek yok", - "title": "OneWire SysBus Se\u00e7enekleri" + } }, "configure_device": { "data": { diff --git a/homeassistant/components/onewire/translations/uk.json b/homeassistant/components/onewire/translations/uk.json index 9c9705d2993..879ba4c4414 100644 --- a/homeassistant/components/onewire/translations/uk.json +++ b/homeassistant/components/onewire/translations/uk.json @@ -4,21 +4,10 @@ "already_configured": "\u0426\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant." }, "error": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", - "invalid_path": "\u041a\u0430\u0442\u0430\u043b\u043e\u0433 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e." + "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" }, "step": { - "owserver": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442" - }, - "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e owserver" - }, "user": { - "data": { - "type": "\u0422\u0438\u043f \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f" - }, "title": "1-Wire" } } diff --git a/homeassistant/components/onewire/translations/zh-Hant.json b/homeassistant/components/onewire/translations/zh-Hant.json index a77b86ea416..aa98353ca5a 100644 --- a/homeassistant/components/onewire/translations/zh-Hant.json +++ b/homeassistant/components/onewire/translations/zh-Hant.json @@ -4,22 +4,13 @@ "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_path": "\u672a\u627e\u5230\u88dd\u7f6e\u3002" + "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { - "owserver": { - "data": { - "host": "\u4e3b\u6a5f\u7aef", - "port": "\u901a\u8a0a\u57e0" - }, - "title": "\u8a2d\u5b9a owserver \u8a73\u7d30\u8cc7\u6599" - }, "user": { "data": { "host": "\u4e3b\u6a5f\u7aef", - "port": "\u901a\u8a0a\u57e0", - "type": "\u9023\u7dda\u985e\u5225" + "port": "\u901a\u8a0a\u57e0" }, "title": "\u8a2d\u5b9a\u4f3a\u670d\u5668\u8a73\u7d30\u8cc7\u8a0a" } @@ -30,10 +21,6 @@ "device_not_selected": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e" }, "step": { - "ack_no_options": { - "description": "SysBus implementation \u6c92\u6709\u8a2d\u5b9a\u9078\u9805", - "title": "OneWire SysBus \u9078\u9805" - }, "configure_device": { "data": { "precision": "\u611f\u6e2c\u5668\u7cbe\u6e96\u5ea6" diff --git a/homeassistant/components/onvif/translations/bg.json b/homeassistant/components/onvif/translations/bg.json index e45e78b79ee..ba0e0dd6277 100644 --- a/homeassistant/components/onvif/translations/bg.json +++ b/homeassistant/components/onvif/translations/bg.json @@ -4,12 +4,6 @@ "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "step": { - "auth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - } - }, "configure": { "data": { "host": "\u0425\u043e\u0441\u0442", @@ -18,13 +12,6 @@ "port": "\u041f\u043e\u0440\u0442", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" } - }, - "manual_input": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u0418\u043c\u0435", - "port": "\u041f\u043e\u0440\u0442" - } } } } diff --git a/homeassistant/components/onvif/translations/ca.json b/homeassistant/components/onvif/translations/ca.json index 7ede84d4845..222921a0599 100644 --- a/homeassistant/components/onvif/translations/ca.json +++ b/homeassistant/components/onvif/translations/ca.json @@ -11,13 +11,6 @@ "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { - "auth": { - "data": { - "password": "Contrasenya", - "username": "Nom d'usuari" - }, - "title": "Configuraci\u00f3 d'autenticaci\u00f3" - }, "configure": { "data": { "host": "Amfitri\u00f3", @@ -41,14 +34,6 @@ }, "title": "Selecci\u00f3 de dispositiu ONVIF" }, - "manual_input": { - "data": { - "host": "Amfitri\u00f3", - "name": "Nom", - "port": "Port" - }, - "title": "Configura el dispositiu ONVIF" - }, "user": { "data": { "auto": "Cerca autom\u00e0ticament" diff --git a/homeassistant/components/onvif/translations/cs.json b/homeassistant/components/onvif/translations/cs.json index 4ddb2091cc3..100c4eb3788 100644 --- a/homeassistant/components/onvif/translations/cs.json +++ b/homeassistant/components/onvif/translations/cs.json @@ -11,13 +11,6 @@ "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { - "auth": { - "data": { - "password": "Heslo", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, - "title": "Konfigurace ov\u011b\u0159ov\u00e1n\u00ed" - }, "configure": { "data": { "host": "Hostitel", @@ -40,14 +33,6 @@ }, "title": "Vyberte za\u0159\u00edzen\u00ed ONVIF" }, - "manual_input": { - "data": { - "host": "Hostitel", - "name": "Jm\u00e9no", - "port": "Port" - }, - "title": "Konfigurovat za\u0159\u00edzen\u00ed ONVIF" - }, "user": { "description": "Kliknut\u00edm na tla\u010d\u00edtko Odeslat vyhled\u00e1me ve va\u0161\u00ed s\u00edti za\u0159\u00edzen\u00ed ONVIF, kter\u00e1 podporuj\u00ed profil S. \n\nN\u011bkte\u0159\u00ed v\u00fdrobci vypli funkci ONVIF v z\u00e1kladn\u00edm nastaven\u00ed. Ujist\u011bte se, \u017ee je v konfiguraci kamery povolena funkce ONVIF.", "title": "Nastaven\u00ed za\u0159\u00edzen\u00ed ONVIF" diff --git a/homeassistant/components/onvif/translations/de.json b/homeassistant/components/onvif/translations/de.json index fd992c4db05..c4c745f1766 100644 --- a/homeassistant/components/onvif/translations/de.json +++ b/homeassistant/components/onvif/translations/de.json @@ -11,13 +11,6 @@ "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { - "auth": { - "data": { - "password": "Passwort", - "username": "Benutzername" - }, - "title": "Konfiguriere die Authentifizierung" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "W\u00e4hle ein ONVIF-Ger\u00e4t" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Name", - "port": "Port" - }, - "title": "Konfiguriere das ONVIF-Ger\u00e4t" - }, "user": { "data": { "auto": "Automatisch suchen" diff --git a/homeassistant/components/onvif/translations/el.json b/homeassistant/components/onvif/translations/el.json index 8bc93c4a3d3..911994c7344 100644 --- a/homeassistant/components/onvif/translations/el.json +++ b/homeassistant/components/onvif/translations/el.json @@ -11,13 +11,6 @@ "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { - "auth": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" - }, "configure": { "data": { "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", @@ -41,14 +34,6 @@ }, "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae ONVIF" }, - "manual_input": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", - "port": "\u0398\u03cd\u03c1\u03b1" - }, - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 ONVIF" - }, "user": { "data": { "auto": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03b6\u03ae\u03c4\u03b7\u03c3\u03b7" diff --git a/homeassistant/components/onvif/translations/en.json b/homeassistant/components/onvif/translations/en.json index c922fc18482..c3b328646ee 100644 --- a/homeassistant/components/onvif/translations/en.json +++ b/homeassistant/components/onvif/translations/en.json @@ -11,13 +11,6 @@ "cannot_connect": "Failed to connect" }, "step": { - "auth": { - "data": { - "password": "Password", - "username": "Username" - }, - "title": "Configure authentication" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Select ONVIF device" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Name", - "port": "Port" - }, - "title": "Configure ONVIF device" - }, "user": { "data": { "auto": "Search automatically" diff --git a/homeassistant/components/onvif/translations/es-419.json b/homeassistant/components/onvif/translations/es-419.json index d0db75beb96..55ff3166c66 100644 --- a/homeassistant/components/onvif/translations/es-419.json +++ b/homeassistant/components/onvif/translations/es-419.json @@ -8,13 +8,6 @@ "onvif_error": "Error al configurar el dispositivo ONVIF. Consulte los registros para obtener m\u00e1s informaci\u00f3n." }, "step": { - "auth": { - "data": { - "password": "Contrase\u00f1a", - "username": "Nombre de usuario" - }, - "title": "Configurar autenticaci\u00f3n" - }, "configure_profile": { "data": { "include": "Crear entidad de c\u00e1mara" @@ -28,13 +21,6 @@ }, "title": "Seleccionar dispositivo ONVIF" }, - "manual_input": { - "data": { - "host": "Host", - "port": "Puerto" - }, - "title": "Configurar dispositivo ONVIF" - }, "user": { "description": "Al hacer clic en enviar, buscaremos en su red dispositivos ONVIF que admitan Profile S. \n\nAlgunos fabricantes han comenzado a deshabilitar ONVIF por defecto. Aseg\u00farese de que ONVIF est\u00e9 habilitado en la configuraci\u00f3n de su c\u00e1mara.", "title": "Configuraci\u00f3n del dispositivo ONVIF" diff --git a/homeassistant/components/onvif/translations/es.json b/homeassistant/components/onvif/translations/es.json index d5c9688b875..6ba7dbe0ee8 100644 --- a/homeassistant/components/onvif/translations/es.json +++ b/homeassistant/components/onvif/translations/es.json @@ -11,13 +11,6 @@ "cannot_connect": "No se pudo conectar" }, "step": { - "auth": { - "data": { - "password": "Contrase\u00f1a", - "username": "Usuario" - }, - "title": "Configurar la autenticaci\u00f3n" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Seleccione el dispositivo ONVIF" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Nombre", - "port": "Puerto" - }, - "title": "Configurar el dispositivo ONVIF" - }, "user": { "data": { "auto": "Buscar autom\u00e1ticamente" diff --git a/homeassistant/components/onvif/translations/et.json b/homeassistant/components/onvif/translations/et.json index 61eefa84d12..1d7bede6657 100644 --- a/homeassistant/components/onvif/translations/et.json +++ b/homeassistant/components/onvif/translations/et.json @@ -11,13 +11,6 @@ "cannot_connect": "\u00dchendamine nurjus" }, "step": { - "auth": { - "data": { - "password": "Salas\u00f5na", - "username": "Kasutajanimi" - }, - "title": "Autentimise seadistamine" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Vali ONVIF-seade" }, - "manual_input": { - "data": { - "host": "", - "name": "Nimi", - "port": "" - }, - "title": "H\u00e4\u00e4lesta ONVIF-seade" - }, "user": { "data": { "auto": "Otsi automaatselt" diff --git a/homeassistant/components/onvif/translations/fi.json b/homeassistant/components/onvif/translations/fi.json index 47673eeaaa5..d32021cd4b5 100644 --- a/homeassistant/components/onvif/translations/fi.json +++ b/homeassistant/components/onvif/translations/fi.json @@ -1,24 +1,11 @@ { "config": { "step": { - "auth": { - "data": { - "password": "Salasana", - "username": "K\u00e4ytt\u00e4j\u00e4tunnus" - }, - "title": "M\u00e4\u00e4rit\u00e4 todennus" - }, "configure_profile": { "data": { "include": "Luo kamerakohde" }, "title": "M\u00e4\u00e4rit\u00e4 profiilit" - }, - "manual_input": { - "data": { - "host": "Palvelin", - "port": "Portti" - } } } } diff --git a/homeassistant/components/onvif/translations/fr.json b/homeassistant/components/onvif/translations/fr.json index ab772fcec2d..6939205b4bb 100644 --- a/homeassistant/components/onvif/translations/fr.json +++ b/homeassistant/components/onvif/translations/fr.json @@ -11,13 +11,6 @@ "cannot_connect": "\u00c9chec de connexion" }, "step": { - "auth": { - "data": { - "password": "Mot de passe", - "username": "Nom d'utilisateur" - }, - "title": "Configurer l'authentification" - }, "configure": { "data": { "host": "H\u00f4te", @@ -41,14 +34,6 @@ }, "title": "S\u00e9lectionnez l'appareil ONVIF" }, - "manual_input": { - "data": { - "host": "H\u00f4te", - "name": "Nom", - "port": "Port" - }, - "title": "Configurer l\u2019appareil ONVIF" - }, "user": { "data": { "auto": "Rechercher automatiquement" diff --git a/homeassistant/components/onvif/translations/he.json b/homeassistant/components/onvif/translations/he.json index 5883d1afc53..727457fac34 100644 --- a/homeassistant/components/onvif/translations/he.json +++ b/homeassistant/components/onvif/translations/he.json @@ -11,13 +11,6 @@ "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { - "auth": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - }, - "title": "\u05e7\u05d1\u05d9\u05e2\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d0\u05d9\u05de\u05d5\u05ea" - }, "configure": { "data": { "host": "\u05de\u05d0\u05e8\u05d7", @@ -41,14 +34,6 @@ }, "title": "\u05d1\u05d7\u05e8 \u05d4\u05ea\u05e7\u05df ONVIF" }, - "manual_input": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "name": "\u05e9\u05dd", - "port": "\u05e4\u05d5\u05e8\u05d8" - }, - "title": "\u05e7\u05d1\u05d9\u05e2\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d4\u05ea\u05e7\u05df ONVIF" - }, "user": { "data": { "auto": "\u05d7\u05d9\u05e4\u05d5\u05e9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9" diff --git a/homeassistant/components/onvif/translations/hu.json b/homeassistant/components/onvif/translations/hu.json index 3754243a740..4fcb62c26fc 100644 --- a/homeassistant/components/onvif/translations/hu.json +++ b/homeassistant/components/onvif/translations/hu.json @@ -11,13 +11,6 @@ "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { - "auth": { - "data": { - "password": "Jelsz\u00f3", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "title": "Hiteles\u00edt\u00e9s konfigur\u00e1l\u00e1sa" - }, "configure": { "data": { "host": "C\u00edm", @@ -41,14 +34,6 @@ }, "title": "ONVIF eszk\u00f6z kiv\u00e1laszt\u00e1sa" }, - "manual_input": { - "data": { - "host": "C\u00edm", - "name": "Elnevez\u00e9s", - "port": "Port" - }, - "title": "ONVIF eszk\u00f6z konfigur\u00e1l\u00e1sa" - }, "user": { "data": { "auto": "Automatikus keres\u00e9s" diff --git a/homeassistant/components/onvif/translations/id.json b/homeassistant/components/onvif/translations/id.json index 77e1353270f..383287db875 100644 --- a/homeassistant/components/onvif/translations/id.json +++ b/homeassistant/components/onvif/translations/id.json @@ -11,13 +11,6 @@ "cannot_connect": "Gagal terhubung" }, "step": { - "auth": { - "data": { - "password": "Kata Sandi", - "username": "Nama Pengguna" - }, - "title": "Konfigurasikan autentikasi" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Pilih perangkat ONVIF" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Nama", - "port": "Port" - }, - "title": "Konfigurasikan perangkat ONVIF" - }, "user": { "data": { "auto": "Cari secara otomatis" diff --git a/homeassistant/components/onvif/translations/it.json b/homeassistant/components/onvif/translations/it.json index 3590025a6ad..34d23e2f3fe 100644 --- a/homeassistant/components/onvif/translations/it.json +++ b/homeassistant/components/onvif/translations/it.json @@ -11,13 +11,6 @@ "cannot_connect": "Impossibile connettersi" }, "step": { - "auth": { - "data": { - "password": "Password", - "username": "Nome utente" - }, - "title": "Configura l'autenticazione" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Seleziona il dispositivo ONVIF" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Nome", - "port": "Porta" - }, - "title": "Configura il dispositivo ONVIF" - }, "user": { "data": { "auto": "Cerca automaticamente" diff --git a/homeassistant/components/onvif/translations/ja.json b/homeassistant/components/onvif/translations/ja.json index a2d863b54fa..ed8e3aa6bd5 100644 --- a/homeassistant/components/onvif/translations/ja.json +++ b/homeassistant/components/onvif/translations/ja.json @@ -11,13 +11,6 @@ "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { - "auth": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "title": "\u8a8d\u8a3c\u306e\u8a2d\u5b9a" - }, "configure": { "data": { "host": "\u30db\u30b9\u30c8", @@ -41,14 +34,6 @@ }, "title": "ONVIF\u30c7\u30d0\u30a4\u30b9\u306e\u9078\u629e" }, - "manual_input": { - "data": { - "host": "\u30db\u30b9\u30c8", - "name": "\u540d\u524d", - "port": "\u30dd\u30fc\u30c8" - }, - "title": "ONVIF\u30c7\u30d0\u30a4\u30b9\u306e\u8a2d\u5b9a" - }, "user": { "data": { "auto": "\u81ea\u52d5\u7684\u306b\u691c\u7d22" diff --git a/homeassistant/components/onvif/translations/ko.json b/homeassistant/components/onvif/translations/ko.json index 173cdb88512..5dcbd1df983 100644 --- a/homeassistant/components/onvif/translations/ko.json +++ b/homeassistant/components/onvif/translations/ko.json @@ -11,13 +11,6 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { - "auth": { - "data": { - "password": "\ube44\ubc00\ubc88\ud638", - "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - }, - "title": "\uc778\uc99d \uad6c\uc131\ud558\uae30" - }, "configure_profile": { "data": { "include": "\uce74\uba54\ub77c \uad6c\uc131\uc694\uc18c \ub9cc\ub4e4\uae30" @@ -31,14 +24,6 @@ }, "title": "ONVIF \uae30\uae30 \uc120\ud0dd\ud558\uae30" }, - "manual_input": { - "data": { - "host": "\ud638\uc2a4\ud2b8", - "name": "\uc774\ub984", - "port": "\ud3ec\ud2b8" - }, - "title": "ONVIF \uae30\uae30 \uad6c\uc131\ud558\uae30" - }, "user": { "description": "\ud655\uc778\uc744 \ud074\ub9ad\ud558\uba74 \ud504\ub85c\ud544 S \ub97c \uc9c0\uc6d0\ud558\ub294 ONVIF \uae30\uae30\ub97c \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uac80\uc0c9\ud569\ub2c8\ub2e4. \n\n\uc77c\ubd80 \uc81c\uc870\uc5c5\uccb4\ub294 \uae30\ubcf8\uac12\uc73c\ub85c ONVIF \ub97c \ube44\ud65c\uc131\ud654 \ud574 \ub193\uc740 \uacbd\uc6b0\uac00 \uc788\uc2b5\ub2c8\ub2e4. \uce74\uba54\ub77c \uad6c\uc131\uc5d0\uc11c ONVIF \uac00 \ud65c\uc131\ud654\ub418\uc5b4 \uc788\ub294\uc9c0 \ud655\uc778\ud574\uc8fc\uc138\uc694.", "title": "ONVIF \uae30\uae30 \uc124\uc815\ud558\uae30" diff --git a/homeassistant/components/onvif/translations/lb.json b/homeassistant/components/onvif/translations/lb.json index f9b2843b5cb..008b52b5338 100644 --- a/homeassistant/components/onvif/translations/lb.json +++ b/homeassistant/components/onvif/translations/lb.json @@ -11,13 +11,6 @@ "cannot_connect": "Feeler beim verbannen" }, "step": { - "auth": { - "data": { - "password": "Passwuert", - "username": "Benotzernumm" - }, - "title": "Authentifikatioun konfigur\u00e9ieren" - }, "configure_profile": { "data": { "include": "Kamera Entit\u00e9it erstellen" @@ -31,14 +24,6 @@ }, "title": "ONVIF Apparat auswielen" }, - "manual_input": { - "data": { - "host": "Apparat", - "name": "Numm", - "port": "Port" - }, - "title": "ONVIF Apparat ariichten" - }, "user": { "description": "Andeems du op ofsch\u00e9cke klicks, g\u00ebtt dain Netzwierk fir ONVIF Apparater duerchsicht d\u00e9i den Profile S. \u00ebnnerst\u00ebtzen.\n\nVerschidde Hiersteller hunn ugefaang ONVIF standardm\u00e9isseg ze d\u00e9aktiv\u00e9ieren. Stell s\u00e9cher dass ONVIF an denger Kamera ugeschalt ass.", "title": "ONVIF Apparat ariichten" diff --git a/homeassistant/components/onvif/translations/nl.json b/homeassistant/components/onvif/translations/nl.json index c48d2764672..f1101f759e9 100644 --- a/homeassistant/components/onvif/translations/nl.json +++ b/homeassistant/components/onvif/translations/nl.json @@ -11,13 +11,6 @@ "cannot_connect": "Kan geen verbinding maken" }, "step": { - "auth": { - "data": { - "password": "Wachtwoord", - "username": "Gebruikersnaam" - }, - "title": "Configureer authenticatie" - }, "configure": { "data": { "host": "Host", @@ -41,14 +34,6 @@ }, "title": "Selecteer ONVIF-apparaat" }, - "manual_input": { - "data": { - "host": "Host", - "name": "Naam", - "port": "Poort" - }, - "title": "Configureer ONVIF-apparaat" - }, "user": { "data": { "auto": "Automatisch zoeken" diff --git a/homeassistant/components/onvif/translations/no.json b/homeassistant/components/onvif/translations/no.json index 323f9aba5fe..a9087ba6be4 100644 --- a/homeassistant/components/onvif/translations/no.json +++ b/homeassistant/components/onvif/translations/no.json @@ -11,13 +11,6 @@ "cannot_connect": "Tilkobling mislyktes" }, "step": { - "auth": { - "data": { - "password": "Passord", - "username": "Brukernavn" - }, - "title": "Konfigurere godkjenning" - }, "configure": { "data": { "host": "Vert", @@ -41,14 +34,6 @@ }, "title": "Velg ONVIF-enhet" }, - "manual_input": { - "data": { - "host": "Vert", - "name": "Navn", - "port": "Port" - }, - "title": "Konfigurere ONVIF-enhet" - }, "user": { "data": { "auto": "S\u00f8k automatisk" diff --git a/homeassistant/components/onvif/translations/pl.json b/homeassistant/components/onvif/translations/pl.json index f45ad4064b2..2300f6d6041 100644 --- a/homeassistant/components/onvif/translations/pl.json +++ b/homeassistant/components/onvif/translations/pl.json @@ -11,13 +11,6 @@ "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { - "auth": { - "data": { - "password": "Has\u0142o", - "username": "Nazwa u\u017cytkownika" - }, - "title": "Konfiguracja uwierzytelniania" - }, "configure": { "data": { "host": "Nazwa hosta lub adres IP", @@ -41,14 +34,6 @@ }, "title": "Wybierz urz\u0105dzenie ONVIF" }, - "manual_input": { - "data": { - "host": "Nazwa hosta lub adres IP", - "name": "Nazwa", - "port": "Port" - }, - "title": "Konfiguracja urz\u0105dzenia ONVIF" - }, "user": { "data": { "auto": "Wyszukaj automatycznie" diff --git a/homeassistant/components/onvif/translations/pt-BR.json b/homeassistant/components/onvif/translations/pt-BR.json index d5586b2dd2b..f491f21d56b 100644 --- a/homeassistant/components/onvif/translations/pt-BR.json +++ b/homeassistant/components/onvif/translations/pt-BR.json @@ -11,13 +11,6 @@ "cannot_connect": "Falha ao conectar" }, "step": { - "auth": { - "data": { - "password": "Senha", - "username": "Usu\u00e1rio" - }, - "title": "Configurar autentica\u00e7\u00e3o" - }, "configure": { "data": { "host": "Nome do host", @@ -41,14 +34,6 @@ }, "title": "Selecionar dispositivo ONVIF" }, - "manual_input": { - "data": { - "host": "Nome do host", - "name": "Nome", - "port": "Porta" - }, - "title": "Configurar dispositivo ONVIF" - }, "user": { "data": { "auto": "Pesquisar automaticamente" diff --git a/homeassistant/components/onvif/translations/pt.json b/homeassistant/components/onvif/translations/pt.json index c3662a032a6..f79bbec3201 100644 --- a/homeassistant/components/onvif/translations/pt.json +++ b/homeassistant/components/onvif/translations/pt.json @@ -11,13 +11,6 @@ "cannot_connect": "Falha na liga\u00e7\u00e3o" }, "step": { - "auth": { - "data": { - "password": "Palavra-passe", - "username": "Utilizador" - }, - "title": "Configurar autentica\u00e7\u00e3o" - }, "configure_profile": { "data": { "include": "Criar entidade da c\u00e2mara" @@ -31,14 +24,6 @@ }, "title": "Selecione o dispositivo ONVIF" }, - "manual_input": { - "data": { - "host": "Servidor", - "name": "Nome", - "port": "Porta" - }, - "title": "Configurar dispositivo ONVIF" - }, "user": { "description": "Ao clickar submeter, iremos procurar na sua rede por dispositivos ONVIF que suportem o Perfil S.\n\nAlguns fabricantes come\u00e7aram a desabilitar o ONVIF por omiss\u00e3o. Por favor verifique que o ONVIF est\u00e1 activo na configura\u00e7\u00e3o da sua c\u00e2mara.", "title": "Configura\u00e7\u00e3o do dispositivo ONVIF" diff --git a/homeassistant/components/onvif/translations/ru.json b/homeassistant/components/onvif/translations/ru.json index c8643fa9ec3..d9b09be4d42 100644 --- a/homeassistant/components/onvif/translations/ru.json +++ b/homeassistant/components/onvif/translations/ru.json @@ -11,13 +11,6 @@ "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { - "auth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "title": "\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f" - }, "configure": { "data": { "host": "\u0425\u043e\u0441\u0442", @@ -41,14 +34,6 @@ }, "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e ONVIF" }, - "manual_input": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "port": "\u041f\u043e\u0440\u0442" - }, - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 ONVIF" - }, "user": { "data": { "auto": "\u0418\u0441\u043a\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438" diff --git a/homeassistant/components/onvif/translations/sk.json b/homeassistant/components/onvif/translations/sk.json index f9a7d7d07bf..f51c7e32bc8 100644 --- a/homeassistant/components/onvif/translations/sk.json +++ b/homeassistant/components/onvif/translations/sk.json @@ -5,23 +5,12 @@ "already_in_progress": "Konfigur\u00e1cia u\u017e prebieha" }, "step": { - "auth": { - "data": { - "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" - } - }, "configure": { "data": { "name": "N\u00e1zov", "port": "Port", "username": "Pou\u017e\u00edvate\u013esk\u00e9 meno" } - }, - "manual_input": { - "data": { - "name": "N\u00e1zov", - "port": "Port" - } } } } diff --git a/homeassistant/components/onvif/translations/sl.json b/homeassistant/components/onvif/translations/sl.json index 37994d88e14..cbc5104ac4f 100644 --- a/homeassistant/components/onvif/translations/sl.json +++ b/homeassistant/components/onvif/translations/sl.json @@ -8,13 +8,6 @@ "onvif_error": "Napaka pri nastavitvi naprave ONVIF. Za ve\u010d informacij preverite dnevnike." }, "step": { - "auth": { - "data": { - "password": "Geslo", - "username": "Uporabni\u0161ko ime" - }, - "title": "Nastavite preverjanje pristnosti" - }, "configure_profile": { "data": { "include": "Ustvari entiteto kamere" @@ -28,13 +21,6 @@ }, "title": "Izberite ONVIF napravo" }, - "manual_input": { - "data": { - "host": "Gostitelj", - "port": "Vrata" - }, - "title": "Konfigurirajte ONVIF napravo" - }, "user": { "description": "S klikom na oddajo bomo v omre\u017eju iskali naprave ONVIF, ki podpirajo profil S. \n\n Nekateri proizvajalci so ONVIF privzeto za\u010deli onemogo\u010dati. Prepri\u010dajte se, da je ONVIF omogo\u010den v konfiguraciji naprave.", "title": "Nastavitev naprave ONVIF" diff --git a/homeassistant/components/onvif/translations/sv.json b/homeassistant/components/onvif/translations/sv.json index 0eff403bf45..626daebc7b2 100644 --- a/homeassistant/components/onvif/translations/sv.json +++ b/homeassistant/components/onvif/translations/sv.json @@ -1,20 +1,8 @@ { "config": { "step": { - "auth": { - "data": { - "password": "L\u00f6senord", - "username": "Anv\u00e4ndarnamn" - } - }, "configure_profile": { "title": "Konfigurera Profiler" - }, - "manual_input": { - "data": { - "host": "V\u00e4rd", - "port": "Port" - } } } } diff --git a/homeassistant/components/onvif/translations/tr.json b/homeassistant/components/onvif/translations/tr.json index c471775e8fa..3659586fe44 100644 --- a/homeassistant/components/onvif/translations/tr.json +++ b/homeassistant/components/onvif/translations/tr.json @@ -11,13 +11,6 @@ "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { - "auth": { - "data": { - "password": "Parola", - "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "title": "Kimlik do\u011frulamay\u0131 yap\u0131land\u0131r" - }, "configure": { "data": { "host": "Sunucu", @@ -41,14 +34,6 @@ }, "title": "ONVIF cihaz\u0131n\u0131 se\u00e7in" }, - "manual_input": { - "data": { - "host": "Sunucu", - "name": "Ad", - "port": "Port" - }, - "title": "ONVIF cihaz\u0131n\u0131 yap\u0131land\u0131r\u0131n" - }, "user": { "data": { "auto": "Otomatik olarak ara" diff --git a/homeassistant/components/onvif/translations/uk.json b/homeassistant/components/onvif/translations/uk.json index 82a816add04..30381d900e3 100644 --- a/homeassistant/components/onvif/translations/uk.json +++ b/homeassistant/components/onvif/translations/uk.json @@ -11,13 +11,6 @@ "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" }, "step": { - "auth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - }, - "title": "\u0423\u0432\u0456\u0439\u0442\u0438" - }, "configure_profile": { "data": { "include": "\u0421\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043e\u0431'\u0454\u043a\u0442 \u043a\u0430\u043c\u0435\u0440\u0438" @@ -31,14 +24,6 @@ }, "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 ONVIF" }, - "manual_input": { - "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u041d\u0430\u0437\u0432\u0430", - "port": "\u041f\u043e\u0440\u0442" - }, - "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e ONVIF" - }, "user": { "description": "\u041a\u043e\u043b\u0438 \u0412\u0438 \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041d\u0430\u0434\u0456\u0441\u043b\u0430\u0442\u0438, \u043f\u043e\u0447\u043d\u0435\u0442\u044c\u0441\u044f \u043f\u043e\u0448\u0443\u043a \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432 ONVIF, \u044f\u043a\u0456 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u044e\u0442\u044c Profile S. \n\n\u0414\u0435\u044f\u043a\u0456 \u0432\u0438\u0440\u043e\u0431\u043d\u0438\u043a\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0437\u0430 \u0443\u043c\u043e\u0432\u0447\u0430\u043d\u043d\u044f\u043c \u0432\u0456\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u044c ONVIF. \u041f\u0435\u0440\u0435\u043a\u043e\u043d\u0430\u0439\u0442\u0435\u0441\u044f, \u0449\u043e ONVIF \u0443\u0432\u0456\u043c\u043a\u043d\u0435\u043d\u043e \u0432 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u0445 \u0412\u0430\u0448\u043e\u0457 \u043a\u0430\u043c\u0435\u0440\u0438.", "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e ONVIF" diff --git a/homeassistant/components/onvif/translations/zh-Hans.json b/homeassistant/components/onvif/translations/zh-Hans.json index 1f87cc0a8cb..7009e91b336 100644 --- a/homeassistant/components/onvif/translations/zh-Hans.json +++ b/homeassistant/components/onvif/translations/zh-Hans.json @@ -11,13 +11,6 @@ "cannot_connect": "\u8fde\u63a5\u5931\u8d25" }, "step": { - "auth": { - "data": { - "password": "\u5bc6\u7801", - "username": "\u7528\u6237\u540d" - }, - "title": "\u914d\u7f6e\u8ba4\u8bc1\u4fe1\u606f" - }, "configure": { "data": { "host": "\u4e3b\u673a\u5730\u5740", @@ -41,14 +34,6 @@ }, "title": "\u9009\u62e9 ONVIF \u8bbe\u5907" }, - "manual_input": { - "data": { - "host": "\u4e3b\u673a\u5730\u5740", - "name": "\u540d\u79f0", - "port": "\u7aef\u53e3" - }, - "title": "\u914d\u7f6e ONVIF \u8bbe\u5907" - }, "user": { "data": { "auto": "\u81ea\u52a8\u641c\u7d22" diff --git a/homeassistant/components/onvif/translations/zh-Hant.json b/homeassistant/components/onvif/translations/zh-Hant.json index 23d0f2d12f0..f4d2ddf036d 100644 --- a/homeassistant/components/onvif/translations/zh-Hant.json +++ b/homeassistant/components/onvif/translations/zh-Hant.json @@ -11,13 +11,6 @@ "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { - "auth": { - "data": { - "password": "\u5bc6\u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "title": "\u8a2d\u5b9a\u9a57\u8b49" - }, "configure": { "data": { "host": "\u4e3b\u6a5f\u7aef", @@ -41,14 +34,6 @@ }, "title": "\u9078\u64c7 ONVIF \u88dd\u7f6e" }, - "manual_input": { - "data": { - "host": "\u4e3b\u6a5f\u7aef", - "name": "\u540d\u7a31", - "port": "\u901a\u8a0a\u57e0" - }, - "title": "\u8a2d\u5b9a ONVIF \u88dd\u7f6e" - }, "user": { "data": { "auto": "\u81ea\u52d5\u641c\u5c0b" diff --git a/homeassistant/components/opentherm_gw/translations/bg.json b/homeassistant/components/opentherm_gw/translations/bg.json index a7c30b4a45a..b55da1b4098 100644 --- a/homeassistant/components/opentherm_gw/translations/bg.json +++ b/homeassistant/components/opentherm_gw/translations/bg.json @@ -11,8 +11,7 @@ "device": "\u041f\u044a\u0442 \u0438\u043b\u0438 URL \u0430\u0434\u0440\u0435\u0441", "id": "ID", "name": "\u0418\u043c\u0435" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -21,8 +20,7 @@ "init": { "data": { "floor_temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043d\u0430 \u043f\u043e\u0434\u0430" - }, - "description": "\u041e\u043f\u0446\u0438\u0438 \u0437\u0430 OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/ca.json b/homeassistant/components/opentherm_gw/translations/ca.json index c4caf76d233..6427c3c7633 100644 --- a/homeassistant/components/opentherm_gw/translations/ca.json +++ b/homeassistant/components/opentherm_gw/translations/ca.json @@ -11,8 +11,7 @@ "device": "Ruta o URL", "id": "ID", "name": "Nom" - }, - "title": "Passarel\u00b7la d'OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Llegeix precisi\u00f3", "set_precision": "Defineix precisi\u00f3", "temporary_override_mode": "Mode de sobreescriptura temporal" - }, - "description": "Opcions del la passarel\u00b7la d'enlla\u00e7 d'OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/cs.json b/homeassistant/components/opentherm_gw/translations/cs.json index 39d70d05cfc..5bf8d4fc385 100644 --- a/homeassistant/components/opentherm_gw/translations/cs.json +++ b/homeassistant/components/opentherm_gw/translations/cs.json @@ -11,8 +11,7 @@ "device": "Cesta nebo URL", "id": "ID", "name": "Jm\u00e9no" - }, - "title": "Br\u00e1na OpenTherm" + } } } }, @@ -21,8 +20,7 @@ "init": { "data": { "floor_temperature": "Teplota podlahy" - }, - "description": "Mo\u017enosti br\u00e1ny OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/da.json b/homeassistant/components/opentherm_gw/translations/da.json index cca7cb347db..a9d675c2c6c 100644 --- a/homeassistant/components/opentherm_gw/translations/da.json +++ b/homeassistant/components/opentherm_gw/translations/da.json @@ -10,8 +10,7 @@ "device": "Sti eller webadresse", "id": "Id", "name": "Navn" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -20,8 +19,7 @@ "init": { "data": { "floor_temperature": "Gulvtemperatur" - }, - "description": "Indstillinger for OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/de.json b/homeassistant/components/opentherm_gw/translations/de.json index e7be7815366..1217839eaed 100644 --- a/homeassistant/components/opentherm_gw/translations/de.json +++ b/homeassistant/components/opentherm_gw/translations/de.json @@ -11,8 +11,7 @@ "device": "Pfad oder URL", "id": "ID", "name": "Name" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Pr\u00e4zision abfragen", "set_precision": "Pr\u00e4zision einstellen", "temporary_override_mode": "Tempor\u00e4rer Sollwert\u00fcbersteuerungsmodus" - }, - "description": "Optionen f\u00fcr das OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/el.json b/homeassistant/components/opentherm_gw/translations/el.json index 92dac2e9b3d..82be1ff6ce9 100644 --- a/homeassistant/components/opentherm_gw/translations/el.json +++ b/homeassistant/components/opentherm_gw/translations/el.json @@ -11,8 +11,7 @@ "device": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03ae \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", "id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" - }, - "title": "\u03a0\u03cd\u03bb\u03b7 OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "\u0394\u03b9\u03ac\u03b2\u03b1\u03c3\u03b5 \u03c4\u03b7\u03bd \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1", "set_precision": "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b1\u03ba\u03c1\u03af\u03b2\u03b5\u03b9\u03b1\u03c2", "temporary_override_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c9\u03c1\u03b9\u03bd\u03ae\u03c2 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7\u03c2 \u03c3\u03b7\u03bc\u03b5\u03af\u03bf\u03c5 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7 OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/en.json b/homeassistant/components/opentherm_gw/translations/en.json index c1e897c631b..c84e9dcf0e2 100644 --- a/homeassistant/components/opentherm_gw/translations/en.json +++ b/homeassistant/components/opentherm_gw/translations/en.json @@ -11,8 +11,7 @@ "device": "Path or URL", "id": "ID", "name": "Name" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Read Precision", "set_precision": "Set Precision", "temporary_override_mode": "Temporary Setpoint Override Mode" - }, - "description": "Options for the OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/es-419.json b/homeassistant/components/opentherm_gw/translations/es-419.json index f9a6f60b463..6e28121d852 100644 --- a/homeassistant/components/opentherm_gw/translations/es-419.json +++ b/homeassistant/components/opentherm_gw/translations/es-419.json @@ -10,8 +10,7 @@ "device": "Ruta o URL", "id": "Identificaci\u00f3n", "name": "Nombre" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -22,8 +21,7 @@ "floor_temperature": "Temperatura del piso", "read_precision": "Leer precisi\u00f3n", "set_precision": "Establecer precisi\u00f3n" - }, - "description": "Opciones para OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/es.json b/homeassistant/components/opentherm_gw/translations/es.json index 8c773c8c1e2..2b6cc0afd7f 100644 --- a/homeassistant/components/opentherm_gw/translations/es.json +++ b/homeassistant/components/opentherm_gw/translations/es.json @@ -11,8 +11,7 @@ "device": "Ruta o URL", "id": "ID", "name": "Nombre" - }, - "title": "Gateway OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Leer precisi\u00f3n", "set_precision": "Establecer precisi\u00f3n", "temporary_override_mode": "Modo de anulaci\u00f3n temporal del punto de ajuste" - }, - "description": "Opciones para OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/et.json b/homeassistant/components/opentherm_gw/translations/et.json index 67528b034c5..2458c8f1d52 100644 --- a/homeassistant/components/opentherm_gw/translations/et.json +++ b/homeassistant/components/opentherm_gw/translations/et.json @@ -11,8 +11,7 @@ "device": "Rada v\u00f5i URL", "id": "", "name": "Nimi" - }, - "title": "" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Lugemi t\u00e4psus", "set_precision": "M\u00e4\u00e4ra lugemi t\u00e4psus", "temporary_override_mode": "Ajutine seadepunkti alistamine" - }, - "description": "OpenTherm Gateway suvandid" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/fr.json b/homeassistant/components/opentherm_gw/translations/fr.json index 32b642fa639..d0a058eacfa 100644 --- a/homeassistant/components/opentherm_gw/translations/fr.json +++ b/homeassistant/components/opentherm_gw/translations/fr.json @@ -11,8 +11,7 @@ "device": "Chemin ou URL", "id": "ID", "name": "Nom" - }, - "title": "Passerelle OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Pr\u00e9cision de lecture", "set_precision": "D\u00e9finir la pr\u00e9cision", "temporary_override_mode": "Mode de neutralisation du point de consigne temporaire" - }, - "description": "Options pour la passerelle OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/hu.json b/homeassistant/components/opentherm_gw/translations/hu.json index e2a284d7dd1..9e2b6478bec 100644 --- a/homeassistant/components/opentherm_gw/translations/hu.json +++ b/homeassistant/components/opentherm_gw/translations/hu.json @@ -11,8 +11,7 @@ "device": "El\u00e9r\u00e9si \u00fat vagy URL", "id": "ID", "name": "Elnevez\u00e9s" - }, - "title": "OpenTherm \u00e1tj\u00e1r\u00f3" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Pontoss\u00e1g olvas\u00e1sa", "set_precision": "Pontoss\u00e1g be\u00e1ll\u00edt\u00e1sa", "temporary_override_mode": "Ideiglenes be\u00e1ll\u00edt\u00e1s fel\u00fclb\u00edr\u00e1l\u00e1si m\u00f3dja" - }, - "description": "Opci\u00f3k az OpenTherm \u00e1tj\u00e1r\u00f3hoz" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/id.json b/homeassistant/components/opentherm_gw/translations/id.json index f3677b9de3b..5f87fd0ed4b 100644 --- a/homeassistant/components/opentherm_gw/translations/id.json +++ b/homeassistant/components/opentherm_gw/translations/id.json @@ -11,8 +11,7 @@ "device": "Jalur atau URL", "id": "ID", "name": "Nama" - }, - "title": "Gateway OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Tingkat Presisi Baca", "set_precision": "Atur Presisi", "temporary_override_mode": "Mode Penimpaan Setpoint Sementara" - }, - "description": "Pilihan untuk Gateway OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/it.json b/homeassistant/components/opentherm_gw/translations/it.json index 4b3407cd95f..4d1feac60dd 100644 --- a/homeassistant/components/opentherm_gw/translations/it.json +++ b/homeassistant/components/opentherm_gw/translations/it.json @@ -11,8 +11,7 @@ "device": "Percorso o URL", "id": "ID", "name": "Nome" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Leggi la precisione", "set_precision": "Imposta la precisione", "temporary_override_mode": "Modalit\u00e0 di esclusione temporanea del setpoint" - }, - "description": "Opzioni per OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/ja.json b/homeassistant/components/opentherm_gw/translations/ja.json index fa31eced5ab..16f4d55eac0 100644 --- a/homeassistant/components/opentherm_gw/translations/ja.json +++ b/homeassistant/components/opentherm_gw/translations/ja.json @@ -11,8 +11,7 @@ "device": "\u30d1\u30b9\u307e\u305f\u306fURL", "id": "ID", "name": "\u540d\u524d" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "\u7cbe\u5ea6\u3092\u8aad\u307f\u8fbc\u3080", "set_precision": "\u7cbe\u5ea6\u3092\u8a2d\u5b9a\u3059\u308b", "temporary_override_mode": "\u4e00\u6642\u7684\u306a\u30bb\u30c3\u30c8\u30dd\u30a4\u30f3\u30c8\u306e\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u30e2\u30fc\u30c9" - }, - "description": "OpenTherm Gateway\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/ko.json b/homeassistant/components/opentherm_gw/translations/ko.json index 178bf915a6b..01d1971ca6f 100644 --- a/homeassistant/components/opentherm_gw/translations/ko.json +++ b/homeassistant/components/opentherm_gw/translations/ko.json @@ -11,8 +11,7 @@ "device": "\uacbd\ub85c \ub610\ub294 URL", "id": "ID", "name": "\uc774\ub984" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "\uc77d\uae30 \uc815\ubc00\ub3c4", "set_precision": "\uc815\ubc00\ub3c4 \uc124\uc815\ud558\uae30", "temporary_override_mode": "\uc784\uc2dc \uc124\uc815\uac12 \uc7ac\uc815\uc758 \ubaa8\ub4dc" - }, - "description": "OpenTherm Gateway \uc635\uc158" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/lb.json b/homeassistant/components/opentherm_gw/translations/lb.json index e90a6c2a310..b9e28bbab0b 100644 --- a/homeassistant/components/opentherm_gw/translations/lb.json +++ b/homeassistant/components/opentherm_gw/translations/lb.json @@ -11,8 +11,7 @@ "device": "Pfad oder URL", "id": "ID", "name": "Numm" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -21,8 +20,7 @@ "init": { "data": { "floor_temperature": "Buedem Temperatur" - }, - "description": "Optioune fir OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/nl.json b/homeassistant/components/opentherm_gw/translations/nl.json index 025cdea128d..fd8c97c4371 100644 --- a/homeassistant/components/opentherm_gw/translations/nl.json +++ b/homeassistant/components/opentherm_gw/translations/nl.json @@ -11,8 +11,7 @@ "device": "Pad of URL", "id": "ID", "name": "Naam" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Lees Precisie", "set_precision": "Precisie instellen", "temporary_override_mode": "Tijdelijke setpoint-overschrijvingsmodus" - }, - "description": "Opties voor de OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/no.json b/homeassistant/components/opentherm_gw/translations/no.json index 9ef1aa0a5fb..5cf2e9732e9 100644 --- a/homeassistant/components/opentherm_gw/translations/no.json +++ b/homeassistant/components/opentherm_gw/translations/no.json @@ -11,8 +11,7 @@ "device": "Bane eller URL-adresse", "id": "", "name": "Navn" - }, - "title": "" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Les presisjon", "set_precision": "Angi presisjon", "temporary_override_mode": "Midlertidig overstyringsmodus for settpunkt" - }, - "description": "Alternativer for OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/pl.json b/homeassistant/components/opentherm_gw/translations/pl.json index 69dd6040093..fafc4fdc0d7 100644 --- a/homeassistant/components/opentherm_gw/translations/pl.json +++ b/homeassistant/components/opentherm_gw/translations/pl.json @@ -11,8 +11,7 @@ "device": "\u015acie\u017cka lub adres URL", "id": "Identyfikator", "name": "Nazwa" - }, - "title": "Bramka OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Odczytaj precyzj\u0119", "set_precision": "Ustaw precyzj\u0119", "temporary_override_mode": "Tryb tymczasowej zmiany nastawy" - }, - "description": "Opcje dla bramki OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/pt-BR.json b/homeassistant/components/opentherm_gw/translations/pt-BR.json index cf4ed0846d2..018aed87dc3 100644 --- a/homeassistant/components/opentherm_gw/translations/pt-BR.json +++ b/homeassistant/components/opentherm_gw/translations/pt-BR.json @@ -11,8 +11,7 @@ "device": "Caminho ou URL", "id": "ID", "name": "Nome" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Precis\u00e3o de leitura", "set_precision": "Definir precis\u00e3o", "temporary_override_mode": "Modo de substitui\u00e7\u00e3o tempor\u00e1ria do ponto de ajuste" - }, - "description": "Op\u00e7\u00f5es para o OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/ru.json b/homeassistant/components/opentherm_gw/translations/ru.json index 9f2bfff07cf..e743830624a 100644 --- a/homeassistant/components/opentherm_gw/translations/ru.json +++ b/homeassistant/components/opentherm_gw/translations/ru.json @@ -11,8 +11,7 @@ "device": "\u041f\u0443\u0442\u044c \u0438\u043b\u0438 URL-\u0430\u0434\u0440\u0435\u0441", "id": "ID", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" - }, - "title": "OpenTherm" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "\u0422\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u044f", "set_precision": "\u0422\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438", "temporary_override_mode": "\u0420\u0435\u0436\u0438\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0430\u0432\u043a\u0438" - }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0448\u043b\u044e\u0437\u0430 Opentherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/sl.json b/homeassistant/components/opentherm_gw/translations/sl.json index a54ceb5031e..9e58b21c4e0 100644 --- a/homeassistant/components/opentherm_gw/translations/sl.json +++ b/homeassistant/components/opentherm_gw/translations/sl.json @@ -10,8 +10,7 @@ "device": "Pot ali URL", "id": "ID", "name": "Ime" - }, - "title": "OpenTherm Prehod" + } } } }, @@ -20,8 +19,7 @@ "init": { "data": { "floor_temperature": "Temperatura nadstropja" - }, - "description": "Mo\u017enosti za prehod OpenTherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/sv.json b/homeassistant/components/opentherm_gw/translations/sv.json index 01aa96564ac..8ba8716897f 100644 --- a/homeassistant/components/opentherm_gw/translations/sv.json +++ b/homeassistant/components/opentherm_gw/translations/sv.json @@ -10,8 +10,7 @@ "device": "S\u00f6kv\u00e4g eller URL", "id": "ID", "name": "Namn" - }, - "title": "OpenTherm Gateway" + } } } }, @@ -20,8 +19,7 @@ "init": { "data": { "floor_temperature": "Golvetemperatur" - }, - "description": "Alternativ f\u00f6r OpenTherm Gateway" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/tr.json b/homeassistant/components/opentherm_gw/translations/tr.json index a969927ebe2..72a603cc827 100644 --- a/homeassistant/components/opentherm_gw/translations/tr.json +++ b/homeassistant/components/opentherm_gw/translations/tr.json @@ -11,8 +11,7 @@ "device": "Yol veya URL", "id": "ID", "name": "Ad" - }, - "title": "OpenTherm A\u011f Ge\u00e7idi" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "Hassas Okuma", "set_precision": "Hassasiyeti Ayarla", "temporary_override_mode": "Ge\u00e7ici Ayar Noktas\u0131 Ge\u00e7ersiz K\u0131lma Modu" - }, - "description": "OpenTherm A\u011f Ge\u00e7idi i\u00e7in Se\u00e7enekler" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/uk.json b/homeassistant/components/opentherm_gw/translations/uk.json index fdffbfec00e..89fa29a5bf2 100644 --- a/homeassistant/components/opentherm_gw/translations/uk.json +++ b/homeassistant/components/opentherm_gw/translations/uk.json @@ -11,8 +11,7 @@ "device": "\u0428\u043b\u044f\u0445 \u0430\u0431\u043e URL-\u0430\u0434\u0440\u0435\u0441\u0430", "id": "\u0406\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u043e\u0440", "name": "\u041d\u0430\u0437\u0432\u0430" - }, - "title": "OpenTherm" + } } } }, @@ -21,8 +20,7 @@ "init": { "data": { "floor_temperature": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 \u043f\u0456\u0434\u043b\u043e\u0433\u0438" - }, - "description": "\u0414\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0434\u043b\u044f \u0448\u043b\u044e\u0437\u0443 Opentherm" + } } } } diff --git a/homeassistant/components/opentherm_gw/translations/zh-Hant.json b/homeassistant/components/opentherm_gw/translations/zh-Hant.json index 7b8466aa424..d5b9d02de42 100644 --- a/homeassistant/components/opentherm_gw/translations/zh-Hant.json +++ b/homeassistant/components/opentherm_gw/translations/zh-Hant.json @@ -11,8 +11,7 @@ "device": "\u8def\u5f91\u6216 URL", "id": "ID", "name": "\u540d\u7a31" - }, - "title": "OpenTherm \u9598\u9053\u5668" + } } } }, @@ -24,8 +23,7 @@ "read_precision": "\u8b80\u53d6\u7cbe\u6e96\u5ea6", "set_precision": "\u8a2d\u5b9a\u7cbe\u6e96\u5ea6", "temporary_override_mode": "\u81e8\u6642 Setpoint \u8986\u84cb\u6a21\u5f0f" - }, - "description": "OpenTherm \u9598\u9053\u5668\u9078\u9805" + } } } } diff --git a/homeassistant/components/openweathermap/translations/ar.json b/homeassistant/components/openweathermap/translations/ar.json index 1f69a1cf739..5ff07ca51e8 100644 --- a/homeassistant/components/openweathermap/translations/ar.json +++ b/homeassistant/components/openweathermap/translations/ar.json @@ -6,8 +6,7 @@ "language": "\u0627\u0644\u0644\u063a\u0629", "name": "\u0627\u0633\u0645 \u0627\u0644\u062a\u0643\u0627\u0645\u0644" }, - "description": "\u0642\u0645 \u0628\u0625\u0639\u062f\u0627\u062f \u062a\u0643\u0627\u0645\u0644 OpenWeatherMap. \u0644\u0625\u0646\u0634\u0627\u0621 \u0645\u0641\u062a\u0627\u062d API \u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649 https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "\u0642\u0645 \u0628\u0625\u0639\u062f\u0627\u062f \u062a\u0643\u0627\u0645\u0644 OpenWeatherMap. \u0644\u0625\u0646\u0634\u0627\u0621 \u0645\u0641\u062a\u0627\u062d API \u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649 https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/bg.json b/homeassistant/components/openweathermap/translations/bg.json index c1816ddae03..a46653b9fed 100644 --- a/homeassistant/components/openweathermap/translations/bg.json +++ b/homeassistant/components/openweathermap/translations/bg.json @@ -16,8 +16,7 @@ "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", "mode": "\u0420\u0435\u0436\u0438\u043c", "name": "\u0418\u043c\u0435 \u043d\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430" - }, - "title": "OpenWeatherMap" + } } } }, diff --git a/homeassistant/components/openweathermap/translations/ca.json b/homeassistant/components/openweathermap/translations/ca.json index f304a8d4f9f..251bb11a386 100644 --- a/homeassistant/components/openweathermap/translations/ca.json +++ b/homeassistant/components/openweathermap/translations/ca.json @@ -17,8 +17,7 @@ "mode": "Mode", "name": "Nom" }, - "description": "Per generar la clau API, v\u00e9s a https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Per generar la clau API, v\u00e9s a https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/cs.json b/homeassistant/components/openweathermap/translations/cs.json index 42383643be1..c793e56916f 100644 --- a/homeassistant/components/openweathermap/translations/cs.json +++ b/homeassistant/components/openweathermap/translations/cs.json @@ -17,8 +17,7 @@ "mode": "Re\u017eim", "name": "N\u00e1zev integrace" }, - "description": "Nastaven\u00ed integrace OpenWeatherMap. Chcete-li vygenerovat API kl\u00ed\u010d, p\u0159ejd\u011bte na https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Nastaven\u00ed integrace OpenWeatherMap. Chcete-li vygenerovat API kl\u00ed\u010d, p\u0159ejd\u011bte na https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/de.json b/homeassistant/components/openweathermap/translations/de.json index f74065ca1a1..644956b1d49 100644 --- a/homeassistant/components/openweathermap/translations/de.json +++ b/homeassistant/components/openweathermap/translations/de.json @@ -17,8 +17,7 @@ "mode": "Modus", "name": "Name" }, - "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Um den API-Schl\u00fcssel zu generieren, besuche https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/el.json b/homeassistant/components/openweathermap/translations/el.json index 90b876033f6..dada058b430 100644 --- a/homeassistant/components/openweathermap/translations/el.json +++ b/homeassistant/components/openweathermap/translations/el.json @@ -17,8 +17,7 @@ "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1", "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 OpenWeatherMap. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2 OpenWeatherMap. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/en.json b/homeassistant/components/openweathermap/translations/en.json index 55f8d0a2338..27bfb9393d1 100644 --- a/homeassistant/components/openweathermap/translations/en.json +++ b/homeassistant/components/openweathermap/translations/en.json @@ -17,8 +17,7 @@ "mode": "Mode", "name": "Name" }, - "description": "To generate API key go to https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "To generate API key go to https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/es.json b/homeassistant/components/openweathermap/translations/es.json index 5d276f60cb6..295de30fe47 100644 --- a/homeassistant/components/openweathermap/translations/es.json +++ b/homeassistant/components/openweathermap/translations/es.json @@ -17,8 +17,7 @@ "mode": "Modo", "name": "Nombre" }, - "description": "Para generar la clave API, ve a https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Para generar la clave API, ve a https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/et.json b/homeassistant/components/openweathermap/translations/et.json index 0d991c99e3f..02a8731c73b 100644 --- a/homeassistant/components/openweathermap/translations/et.json +++ b/homeassistant/components/openweathermap/translations/et.json @@ -17,8 +17,7 @@ "mode": "Re\u017eiim", "name": "Nimi" }, - "description": "API-v\u00f5tme loomiseks mine aadressile https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "API-v\u00f5tme loomiseks mine aadressile https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/fr.json b/homeassistant/components/openweathermap/translations/fr.json index 387c3eefb28..6beac076ba0 100644 --- a/homeassistant/components/openweathermap/translations/fr.json +++ b/homeassistant/components/openweathermap/translations/fr.json @@ -17,8 +17,7 @@ "mode": "Mode", "name": "Nom" }, - "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/he.json b/homeassistant/components/openweathermap/translations/he.json index cadadeb1865..4de4cdf9e40 100644 --- a/homeassistant/components/openweathermap/translations/he.json +++ b/homeassistant/components/openweathermap/translations/he.json @@ -16,8 +16,7 @@ "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "mode": "\u05de\u05e6\u05d1", "name": "\u05e9\u05dd" - }, - "title": "\u05de\u05e4\u05ea OpenWeather" + } } } }, diff --git a/homeassistant/components/openweathermap/translations/hu.json b/homeassistant/components/openweathermap/translations/hu.json index f5a7dade833..8b28a50e771 100644 --- a/homeassistant/components/openweathermap/translations/hu.json +++ b/homeassistant/components/openweathermap/translations/hu.json @@ -17,8 +17,7 @@ "mode": "M\u00f3d", "name": "Elnevez\u00e9s" }, - "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://openweathermap.org/appid webhelyet", - "title": "OpenWeatherMap" + "description": "Az API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz keresse fel a https://openweathermap.org/appid webhelyet" } } }, diff --git a/homeassistant/components/openweathermap/translations/id.json b/homeassistant/components/openweathermap/translations/id.json index d8ea0344cea..efa891428c9 100644 --- a/homeassistant/components/openweathermap/translations/id.json +++ b/homeassistant/components/openweathermap/translations/id.json @@ -17,8 +17,7 @@ "mode": "Mode", "name": "Nama" }, - "description": "Untuk membuat kunci API, buka https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Untuk membuat kunci API, buka https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/it.json b/homeassistant/components/openweathermap/translations/it.json index 133fa704109..475ae84503c 100644 --- a/homeassistant/components/openweathermap/translations/it.json +++ b/homeassistant/components/openweathermap/translations/it.json @@ -17,8 +17,7 @@ "mode": "Modalit\u00e0", "name": "Nome" }, - "description": "Per generare la chiave API, vai su https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Per generare la chiave API, vai su https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/ja.json b/homeassistant/components/openweathermap/translations/ja.json index 1253ba5af21..511d59b2138 100644 --- a/homeassistant/components/openweathermap/translations/ja.json +++ b/homeassistant/components/openweathermap/translations/ja.json @@ -17,8 +17,7 @@ "mode": "\u30e2\u30fc\u30c9", "name": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u540d\u524d" }, - "description": "OpenWeatherMap\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://openweathermap.org/appid \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "OpenWeatherMap" + "description": "OpenWeatherMap\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://openweathermap.org/appid \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" } } }, diff --git a/homeassistant/components/openweathermap/translations/ko.json b/homeassistant/components/openweathermap/translations/ko.json index d2f5d9aa123..131caf44eff 100644 --- a/homeassistant/components/openweathermap/translations/ko.json +++ b/homeassistant/components/openweathermap/translations/ko.json @@ -17,8 +17,7 @@ "mode": "\ubaa8\ub4dc", "name": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc774\ub984" }, - "description": "OpenWeatherMap \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://openweathermap.org/appid \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694", - "title": "OpenWeatherMap" + "description": "OpenWeatherMap \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://openweathermap.org/appid \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694" } } }, diff --git a/homeassistant/components/openweathermap/translations/lb.json b/homeassistant/components/openweathermap/translations/lb.json index bed28c80575..0cb8299fdbf 100644 --- a/homeassistant/components/openweathermap/translations/lb.json +++ b/homeassistant/components/openweathermap/translations/lb.json @@ -17,8 +17,7 @@ "mode": "Modus", "name": "Numm vun der Integratioun" }, - "description": "OpenWeatherMap Integratioun ariichten. Fir een API Schl\u00ebssel z'erstelle g\u00e9i op https://openweathermap.org/appid", - "title": "OpenWeatherMap API Schl\u00ebssel" + "description": "OpenWeatherMap Integratioun ariichten. Fir een API Schl\u00ebssel z'erstelle g\u00e9i op https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/nl.json b/homeassistant/components/openweathermap/translations/nl.json index 1c72f10c6d9..74ea3532765 100644 --- a/homeassistant/components/openweathermap/translations/nl.json +++ b/homeassistant/components/openweathermap/translations/nl.json @@ -17,8 +17,7 @@ "mode": "Mode", "name": "Naam" }, - "description": "Om een API sleutel te genereren ga naar https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Om een API sleutel te genereren ga naar https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/no.json b/homeassistant/components/openweathermap/translations/no.json index d751553465a..de839e3987e 100644 --- a/homeassistant/components/openweathermap/translations/no.json +++ b/homeassistant/components/openweathermap/translations/no.json @@ -17,8 +17,7 @@ "mode": "Modus", "name": "Navn" }, - "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "For \u00e5 generere API-n\u00f8kkel, g\u00e5 til https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/pl.json b/homeassistant/components/openweathermap/translations/pl.json index de8d857f168..be8e052bb3e 100644 --- a/homeassistant/components/openweathermap/translations/pl.json +++ b/homeassistant/components/openweathermap/translations/pl.json @@ -17,8 +17,7 @@ "mode": "Tryb", "name": "Nazwa" }, - "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Aby wygenerowa\u0107 klucz API, przejd\u017a do https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/pt-BR.json b/homeassistant/components/openweathermap/translations/pt-BR.json index 795db96f36f..9178fb8ac0b 100644 --- a/homeassistant/components/openweathermap/translations/pt-BR.json +++ b/homeassistant/components/openweathermap/translations/pt-BR.json @@ -17,8 +17,7 @@ "mode": "Modo", "name": "Nome" }, - "description": "Para gerar a chave de API, acesse https://openweathermap.org/appid", - "title": "OpenWeatherMap" + "description": "Para gerar a chave de API, acesse https://openweathermap.org/appid" } } }, diff --git a/homeassistant/components/openweathermap/translations/ru.json b/homeassistant/components/openweathermap/translations/ru.json index a0724e90f01..ccbe560a3df 100644 --- a/homeassistant/components/openweathermap/translations/ru.json +++ b/homeassistant/components/openweathermap/translations/ru.json @@ -17,8 +17,7 @@ "mode": "\u0420\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, - "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://openweathermap.org/appid.", - "title": "OpenWeatherMap" + "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://openweathermap.org/appid." } } }, diff --git a/homeassistant/components/openweathermap/translations/sv.json b/homeassistant/components/openweathermap/translations/sv.json index cafa9c0fdd0..c0fdf3bbfdb 100644 --- a/homeassistant/components/openweathermap/translations/sv.json +++ b/homeassistant/components/openweathermap/translations/sv.json @@ -10,8 +10,7 @@ "language": "Spr\u00e5k", "mode": "L\u00e4ge", "name": "Integrationens namn" - }, - "title": "OpenWeatherMap" + } } } }, diff --git a/homeassistant/components/openweathermap/translations/tr.json b/homeassistant/components/openweathermap/translations/tr.json index 6458852712e..ad2da8b8102 100644 --- a/homeassistant/components/openweathermap/translations/tr.json +++ b/homeassistant/components/openweathermap/translations/tr.json @@ -17,8 +17,7 @@ "mode": "Mod", "name": "Ad" }, - "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://openweathermap.org/appid adresine gidin.", - "title": "OpenWeatherMap" + "description": "API anahtar\u0131 olu\u015fturmak i\u00e7in https://openweathermap.org/appid adresine gidin." } } }, diff --git a/homeassistant/components/openweathermap/translations/uk.json b/homeassistant/components/openweathermap/translations/uk.json index 7a39cfa078e..8b3db718680 100644 --- a/homeassistant/components/openweathermap/translations/uk.json +++ b/homeassistant/components/openweathermap/translations/uk.json @@ -17,8 +17,7 @@ "mode": "\u0420\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 OpenWeatherMap. \u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 https://openweathermap.org/appid.", - "title": "OpenWeatherMap" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 OpenWeatherMap. \u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 https://openweathermap.org/appid." } } }, diff --git a/homeassistant/components/openweathermap/translations/zh-Hant.json b/homeassistant/components/openweathermap/translations/zh-Hant.json index fcebc12fe31..407bd026291 100644 --- a/homeassistant/components/openweathermap/translations/zh-Hant.json +++ b/homeassistant/components/openweathermap/translations/zh-Hant.json @@ -17,8 +17,7 @@ "mode": "\u6a21\u5f0f", "name": "\u540d\u7a31" }, - "description": "\u8acb\u81f3 https://openweathermap.org/appid \u4ee5\u7522\u751f API \u91d1\u9470", - "title": "OpenWeatherMap" + "description": "\u8acb\u81f3 https://openweathermap.org/appid \u4ee5\u7522\u751f API \u91d1\u9470" } } }, diff --git a/homeassistant/components/p1_monitor/translations/bg.json b/homeassistant/components/p1_monitor/translations/bg.json index 5c4f6a7bdc3..acbebfb4d36 100644 --- a/homeassistant/components/p1_monitor/translations/bg.json +++ b/homeassistant/components/p1_monitor/translations/bg.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/ca.json b/homeassistant/components/p1_monitor/translations/ca.json index 8d83cfd5026..d82a93389fe 100644 --- a/homeassistant/components/p1_monitor/translations/ca.json +++ b/homeassistant/components/p1_monitor/translations/ca.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "unknown": "Error inesperat" + "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/cs.json b/homeassistant/components/p1_monitor/translations/cs.json index 7981cfc800e..7a27355056b 100644 --- a/homeassistant/components/p1_monitor/translations/cs.json +++ b/homeassistant/components/p1_monitor/translations/cs.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/de.json b/homeassistant/components/p1_monitor/translations/de.json index 31a19a7a195..8ac00192251 100644 --- a/homeassistant/components/p1_monitor/translations/de.json +++ b/homeassistant/components/p1_monitor/translations/de.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "unknown": "Unerwarteter Fehler" + "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/el.json b/homeassistant/components/p1_monitor/translations/el.json index 82c79d53acc..fd520c381f8 100644 --- a/homeassistant/components/p1_monitor/translations/el.json +++ b/homeassistant/components/p1_monitor/translations/el.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "unknown": "\u0391\u03bd\u03b5\u03c0\u03ac\u03bd\u03c4\u03b5\u03c7\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/en.json b/homeassistant/components/p1_monitor/translations/en.json index 34b64082b43..4bd61c19bdc 100644 --- a/homeassistant/components/p1_monitor/translations/en.json +++ b/homeassistant/components/p1_monitor/translations/en.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Failed to connect", - "unknown": "Unexpected error" + "cannot_connect": "Failed to connect" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/es.json b/homeassistant/components/p1_monitor/translations/es.json index 46bfbb59e4f..31976986ec4 100644 --- a/homeassistant/components/p1_monitor/translations/es.json +++ b/homeassistant/components/p1_monitor/translations/es.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "No se pudo conectar", - "unknown": "Error inesperado" + "cannot_connect": "No se pudo conectar" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/et.json b/homeassistant/components/p1_monitor/translations/et.json index 96ab4e46491..69090de7640 100644 --- a/homeassistant/components/p1_monitor/translations/et.json +++ b/homeassistant/components/p1_monitor/translations/et.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u00dchendamine nurjus", - "unknown": "Ootamatu t\u00f5rge" + "cannot_connect": "\u00dchendamine nurjus" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/fr.json b/homeassistant/components/p1_monitor/translations/fr.json index 34c0a37fb2e..34699234e0e 100644 --- a/homeassistant/components/p1_monitor/translations/fr.json +++ b/homeassistant/components/p1_monitor/translations/fr.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u00c9chec de connexion", - "unknown": "Erreur inattendue" + "cannot_connect": "\u00c9chec de connexion" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/he.json b/homeassistant/components/p1_monitor/translations/he.json index fbd52ea83ec..33660936e12 100644 --- a/homeassistant/components/p1_monitor/translations/he.json +++ b/homeassistant/components/p1_monitor/translations/he.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/hu.json b/homeassistant/components/p1_monitor/translations/hu.json index b0c30613234..af1f1e62fdb 100644 --- a/homeassistant/components/p1_monitor/translations/hu.json +++ b/homeassistant/components/p1_monitor/translations/hu.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Nem siker\u00fclt csatlakozni", - "unknown": "V\u00e1ratlan hiba" + "cannot_connect": "Nem siker\u00fclt csatlakozni" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/id.json b/homeassistant/components/p1_monitor/translations/id.json index a1241576b2e..2deaf4c09e0 100644 --- a/homeassistant/components/p1_monitor/translations/id.json +++ b/homeassistant/components/p1_monitor/translations/id.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Gagal terhubung", - "unknown": "Kesalahan yang tidak diharapkan" + "cannot_connect": "Gagal terhubung" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/it.json b/homeassistant/components/p1_monitor/translations/it.json index 25cac38aff6..61b6d9d75f0 100644 --- a/homeassistant/components/p1_monitor/translations/it.json +++ b/homeassistant/components/p1_monitor/translations/it.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Impossibile connettersi", - "unknown": "Errore imprevisto" + "cannot_connect": "Impossibile connettersi" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/ja.json b/homeassistant/components/p1_monitor/translations/ja.json index b47610f27e3..29bfd50332f 100644 --- a/homeassistant/components/p1_monitor/translations/ja.json +++ b/homeassistant/components/p1_monitor/translations/ja.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/ko.json b/homeassistant/components/p1_monitor/translations/ko.json index 40f9f3b7152..e2d6ae5c199 100644 --- a/homeassistant/components/p1_monitor/translations/ko.json +++ b/homeassistant/components/p1_monitor/translations/ko.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc5d0\ub7ec" + "cannot_connect": "\uc5f0\uacb0 \uc2e4\ud328" } } } \ No newline at end of file diff --git a/homeassistant/components/p1_monitor/translations/nl.json b/homeassistant/components/p1_monitor/translations/nl.json index fbb81d70c5d..1f5e9e5a422 100644 --- a/homeassistant/components/p1_monitor/translations/nl.json +++ b/homeassistant/components/p1_monitor/translations/nl.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Kan geen verbinding maken", - "unknown": "Onverwachte fout" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/no.json b/homeassistant/components/p1_monitor/translations/no.json index a6b967e4a46..d0827d0e357 100644 --- a/homeassistant/components/p1_monitor/translations/no.json +++ b/homeassistant/components/p1_monitor/translations/no.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Tilkobling mislyktes", - "unknown": "Uventet feil" + "cannot_connect": "Tilkobling mislyktes" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/pl.json b/homeassistant/components/p1_monitor/translations/pl.json index 5aacfb63336..9ec0f1bcef0 100644 --- a/homeassistant/components/p1_monitor/translations/pl.json +++ b/homeassistant/components/p1_monitor/translations/pl.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "unknown": "Nieoczekiwany b\u0142\u0105d" + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/pt-BR.json b/homeassistant/components/p1_monitor/translations/pt-BR.json index 777a1fc5633..dda88d419e6 100644 --- a/homeassistant/components/p1_monitor/translations/pt-BR.json +++ b/homeassistant/components/p1_monitor/translations/pt-BR.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Falha ao conectar", - "unknown": "Erro inesperado" + "cannot_connect": "Falha ao conectar" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/ru.json b/homeassistant/components/p1_monitor/translations/ru.json index 78277de42c1..4f118fe952c 100644 --- a/homeassistant/components/p1_monitor/translations/ru.json +++ b/homeassistant/components/p1_monitor/translations/ru.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/tr.json b/homeassistant/components/p1_monitor/translations/tr.json index 88684e7bcb0..f00060462fd 100644 --- a/homeassistant/components/p1_monitor/translations/tr.json +++ b/homeassistant/components/p1_monitor/translations/tr.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "unknown": "Beklenmeyen hata" + "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { "user": { diff --git a/homeassistant/components/p1_monitor/translations/zh-Hant.json b/homeassistant/components/p1_monitor/translations/zh-Hant.json index fafb7f9b7c2..a62ff38bbda 100644 --- a/homeassistant/components/p1_monitor/translations/zh-Hant.json +++ b/homeassistant/components/p1_monitor/translations/zh-Hant.json @@ -1,8 +1,7 @@ { "config": { "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { "user": { diff --git a/homeassistant/components/peco/translations/ca.json b/homeassistant/components/peco/translations/ca.json index 1cca8a08a7f..d66b3e67e3c 100644 --- a/homeassistant/components/peco/translations/ca.json +++ b/homeassistant/components/peco/translations/ca.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Comtat" - }, - "description": "Trieu el teu comtat a continuaci\u00f3.", - "title": "Comptador d'interrupcions PECO" + } } } } diff --git a/homeassistant/components/peco/translations/de.json b/homeassistant/components/peco/translations/de.json index 4eb10b3e5e9..2d2c9bca38c 100644 --- a/homeassistant/components/peco/translations/de.json +++ b/homeassistant/components/peco/translations/de.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Bundesland" - }, - "description": "Bitte w\u00e4hle unten dein Bundesland aus.", - "title": "PECO-St\u00f6rungsz\u00e4hler" + } } } } diff --git a/homeassistant/components/peco/translations/el.json b/homeassistant/components/peco/translations/el.json index 6b47cc3ccc1..9e912b952cc 100644 --- a/homeassistant/components/peco/translations/el.json +++ b/homeassistant/components/peco/translations/el.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "\u039a\u03bf\u03bc\u03b7\u03c4\u03b5\u03af\u03b1" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03ba\u03bf\u03bc\u03b7\u03c4\u03b5\u03af\u03b1 \u03c3\u03b1\u03c2 \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9.", - "title": "\u039c\u03b5\u03c4\u03c1\u03b7\u03c4\u03ae\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ce\u03bd PECO" + } } } } diff --git a/homeassistant/components/peco/translations/en.json b/homeassistant/components/peco/translations/en.json index 60483a1d65c..6f7ff2b0b12 100644 --- a/homeassistant/components/peco/translations/en.json +++ b/homeassistant/components/peco/translations/en.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "County" - }, - "description": "Please choose your county below.", - "title": "PECO Outage Counter" + } } } } diff --git a/homeassistant/components/peco/translations/et.json b/homeassistant/components/peco/translations/et.json index 117d0502ca3..0b9303808d8 100644 --- a/homeassistant/components/peco/translations/et.json +++ b/homeassistant/components/peco/translations/et.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Maakond" - }, - "description": "Vali allpool oma maakond.", - "title": "PECO katkestuste loendur" + } } } } diff --git a/homeassistant/components/peco/translations/fr.json b/homeassistant/components/peco/translations/fr.json index e78f828f1af..2063ae4e661 100644 --- a/homeassistant/components/peco/translations/fr.json +++ b/homeassistant/components/peco/translations/fr.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Comt\u00e9" - }, - "description": "Veuillez choisir votre comt\u00e9 ci-dessous.", - "title": "Compteur de pannes PECO" + } } } } diff --git a/homeassistant/components/peco/translations/hu.json b/homeassistant/components/peco/translations/hu.json index 2e1e3482eb5..4de9d9a122a 100644 --- a/homeassistant/components/peco/translations/hu.json +++ b/homeassistant/components/peco/translations/hu.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Megye" - }, - "description": "K\u00e9rj\u00fck, v\u00e1lassza ki az al\u00e1bbiakban a megy\u00e9t.", - "title": "PECO kimarad\u00e1s sz\u00e1ml\u00e1l\u00f3" + } } } } diff --git a/homeassistant/components/peco/translations/id.json b/homeassistant/components/peco/translations/id.json index 0879348f593..f222d4db975 100644 --- a/homeassistant/components/peco/translations/id.json +++ b/homeassistant/components/peco/translations/id.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Kota/kabupaten" - }, - "description": "Pilih kota/kabupaten Anda di bawah ini.", - "title": "Penghitung Pemadaman PECO" + } } } } diff --git a/homeassistant/components/peco/translations/it.json b/homeassistant/components/peco/translations/it.json index a7b76c27f43..fc18d732b56 100644 --- a/homeassistant/components/peco/translations/it.json +++ b/homeassistant/components/peco/translations/it.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Provincia" - }, - "description": "Scegli la tua provincia qui sotto.", - "title": "Contatore interruzioni PECO" + } } } } diff --git a/homeassistant/components/peco/translations/ja.json b/homeassistant/components/peco/translations/ja.json index 139e1f77bd9..b0da74e5c56 100644 --- a/homeassistant/components/peco/translations/ja.json +++ b/homeassistant/components/peco/translations/ja.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "\u90e1" - }, - "description": "\u4ee5\u4e0b\u304b\u3089\u304a\u4f4f\u307e\u3044\u306e\u56fd\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "PECO Outage\u30ab\u30a6\u30f3\u30bf\u30fc" + } } } } diff --git a/homeassistant/components/peco/translations/nl.json b/homeassistant/components/peco/translations/nl.json index 8e98f5077e4..088ae8f0c32 100644 --- a/homeassistant/components/peco/translations/nl.json +++ b/homeassistant/components/peco/translations/nl.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "County" - }, - "description": "Kies hieronder uw county", - "title": "PECO Uitval Teller" + } } } } diff --git a/homeassistant/components/peco/translations/no.json b/homeassistant/components/peco/translations/no.json index 00f9c025114..7b3906f4b67 100644 --- a/homeassistant/components/peco/translations/no.json +++ b/homeassistant/components/peco/translations/no.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "fylke" - }, - "description": "Velg ditt fylke nedenfor.", - "title": "PECO avbruddsteller" + } } } } diff --git a/homeassistant/components/peco/translations/pl.json b/homeassistant/components/peco/translations/pl.json index bdd8a7ffb7c..e2a56bf1916 100644 --- a/homeassistant/components/peco/translations/pl.json +++ b/homeassistant/components/peco/translations/pl.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Hrabstwo" - }, - "description": "Wybierz swoje hrabstwo poni\u017cej.", - "title": "Licznik awarii PECO" + } } } } diff --git a/homeassistant/components/peco/translations/pt-BR.json b/homeassistant/components/peco/translations/pt-BR.json index 845baf5b479..5d4e35de90b 100644 --- a/homeassistant/components/peco/translations/pt-BR.json +++ b/homeassistant/components/peco/translations/pt-BR.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "Munic\u00edpio" - }, - "description": "Por favor, escolha seu munic\u00edpio abaixo.", - "title": "Contador de Interrup\u00e7\u00e3o PECO" + } } } } diff --git a/homeassistant/components/peco/translations/ru.json b/homeassistant/components/peco/translations/ru.json index 6c2c8d85cc6..dc5b481fc7c 100644 --- a/homeassistant/components/peco/translations/ru.json +++ b/homeassistant/components/peco/translations/ru.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "\u041e\u043a\u0440\u0443\u0433" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0432\u043e\u0439 \u043e\u043a\u0440\u0443\u0433.", - "title": "\u0421\u0447\u0435\u0442\u0447\u0438\u043a \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 PECO" + } } } } diff --git a/homeassistant/components/peco/translations/tr.json b/homeassistant/components/peco/translations/tr.json index 6a76e600789..97375e1f61f 100644 --- a/homeassistant/components/peco/translations/tr.json +++ b/homeassistant/components/peco/translations/tr.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "\u0130l\u00e7e" - }, - "description": "L\u00fctfen a\u015fa\u011f\u0131dan il\u00e7enizi se\u00e7iniz.", - "title": "PECO Kesinti Sayac\u0131" + } } } } diff --git a/homeassistant/components/peco/translations/zh-Hant.json b/homeassistant/components/peco/translations/zh-Hant.json index 94318397f6f..69d9400d2f6 100644 --- a/homeassistant/components/peco/translations/zh-Hant.json +++ b/homeassistant/components/peco/translations/zh-Hant.json @@ -7,9 +7,7 @@ "user": { "data": { "county": "\u7e23\u5e02" - }, - "description": "\u8acb\u9078\u64c7\u7e23\u5e02\u3002", - "title": "PECO Outage \u8a08\u6578\u5668" + } } } } diff --git a/homeassistant/components/picnic/translations/ca.json b/homeassistant/components/picnic/translations/ca.json index 83c0b75f9d3..aefba6058c1 100644 --- a/homeassistant/components/picnic/translations/ca.json +++ b/homeassistant/components/picnic/translations/ca.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/de.json b/homeassistant/components/picnic/translations/de.json index 65b10f61df3..6079f023d91 100644 --- a/homeassistant/components/picnic/translations/de.json +++ b/homeassistant/components/picnic/translations/de.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/el.json b/homeassistant/components/picnic/translations/el.json index ba974da6225..42717397a6b 100644 --- a/homeassistant/components/picnic/translations/el.json +++ b/homeassistant/components/picnic/translations/el.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/en.json b/homeassistant/components/picnic/translations/en.json index 06b3018f88e..13b62c78757 100644 --- a/homeassistant/components/picnic/translations/en.json +++ b/homeassistant/components/picnic/translations/en.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/es.json b/homeassistant/components/picnic/translations/es.json index f7a170871ef..5054ae22f5b 100644 --- a/homeassistant/components/picnic/translations/es.json +++ b/homeassistant/components/picnic/translations/es.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/et.json b/homeassistant/components/picnic/translations/et.json index 41f5018079c..e8d45971d37 100644 --- a/homeassistant/components/picnic/translations/et.json +++ b/homeassistant/components/picnic/translations/et.json @@ -19,6 +19,5 @@ } } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/fr.json b/homeassistant/components/picnic/translations/fr.json index 77d9756b5b8..fa9699417b5 100644 --- a/homeassistant/components/picnic/translations/fr.json +++ b/homeassistant/components/picnic/translations/fr.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Pique-nique" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/he.json b/homeassistant/components/picnic/translations/he.json index 856ac220d64..f8c5c7a1b01 100644 --- a/homeassistant/components/picnic/translations/he.json +++ b/homeassistant/components/picnic/translations/he.json @@ -19,6 +19,5 @@ } } } - }, - "title": "\u05e4\u05d9\u05e7\u05e0\u05d9\u05e7" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/hu.json b/homeassistant/components/picnic/translations/hu.json index 3841b7ddbaf..99908bb47d2 100644 --- a/homeassistant/components/picnic/translations/hu.json +++ b/homeassistant/components/picnic/translations/hu.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Piknik" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/id.json b/homeassistant/components/picnic/translations/id.json index db97b991f6f..576f3b8a0e8 100644 --- a/homeassistant/components/picnic/translations/id.json +++ b/homeassistant/components/picnic/translations/id.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/it.json b/homeassistant/components/picnic/translations/it.json index 209b6d6fdb9..f0495ee8d17 100644 --- a/homeassistant/components/picnic/translations/it.json +++ b/homeassistant/components/picnic/translations/it.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/ja.json b/homeassistant/components/picnic/translations/ja.json index 194cffd7e6a..fd9fe67db73 100644 --- a/homeassistant/components/picnic/translations/ja.json +++ b/homeassistant/components/picnic/translations/ja.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/nl.json b/homeassistant/components/picnic/translations/nl.json index dc040fc03d0..1d007e5eb43 100644 --- a/homeassistant/components/picnic/translations/nl.json +++ b/homeassistant/components/picnic/translations/nl.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/no.json b/homeassistant/components/picnic/translations/no.json index ffd38bce705..1ebb4b8dece 100644 --- a/homeassistant/components/picnic/translations/no.json +++ b/homeassistant/components/picnic/translations/no.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/pl.json b/homeassistant/components/picnic/translations/pl.json index abf8a4f9469..f45a8520a14 100644 --- a/homeassistant/components/picnic/translations/pl.json +++ b/homeassistant/components/picnic/translations/pl.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/pt-BR.json b/homeassistant/components/picnic/translations/pt-BR.json index b864d13923d..296ca588e28 100644 --- a/homeassistant/components/picnic/translations/pt-BR.json +++ b/homeassistant/components/picnic/translations/pt-BR.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/ru.json b/homeassistant/components/picnic/translations/ru.json index 9d7a7fbdb23..2f912b07667 100644 --- a/homeassistant/components/picnic/translations/ru.json +++ b/homeassistant/components/picnic/translations/ru.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/tr.json b/homeassistant/components/picnic/translations/tr.json index b689f65ff96..a48dc699dd0 100644 --- a/homeassistant/components/picnic/translations/tr.json +++ b/homeassistant/components/picnic/translations/tr.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/picnic/translations/zh-Hant.json b/homeassistant/components/picnic/translations/zh-Hant.json index a82f4ace04d..99184422cef 100644 --- a/homeassistant/components/picnic/translations/zh-Hant.json +++ b/homeassistant/components/picnic/translations/zh-Hant.json @@ -19,6 +19,5 @@ } } } - }, - "title": "Picnic" + } } \ No newline at end of file diff --git a/homeassistant/components/plex/translations/bg.json b/homeassistant/components/plex/translations/bg.json index d1b1867c246..0e39e4b8b04 100644 --- a/homeassistant/components/plex/translations/bg.json +++ b/homeassistant/components/plex/translations/bg.json @@ -27,12 +27,6 @@ }, "description": "\u041d\u0430\u043b\u0438\u0447\u043d\u0438 \u0441\u0430 \u043d\u044f\u043a\u043e\u043b\u043a\u043e \u0441\u044a\u0440\u0432\u044a\u0440\u0430, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0435\u0434\u0438\u043d:", "title": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 Plex \u0441\u044a\u0440\u0432\u044a\u0440" - }, - "user": { - "title": "Plex Media Server" - }, - "user_advanced": { - "title": "Plex Media Server" } } }, diff --git a/homeassistant/components/plex/translations/ca.json b/homeassistant/components/plex/translations/ca.json index 6cad3cb9d6e..b917a81a772 100644 --- a/homeassistant/components/plex/translations/ca.json +++ b/homeassistant/components/plex/translations/ca.json @@ -35,14 +35,12 @@ "title": "Selecciona servidor Plex" }, "user": { - "description": "V\u00e9s a [plex.tv](https://plex.tv) per enlla\u00e7ar un servidor Plex.", - "title": "Servidor Multim\u00e8dia Plex" + "description": "V\u00e9s a [plex.tv](https://plex.tv) per enlla\u00e7ar un servidor Plex." }, "user_advanced": { "data": { "setup_method": "M\u00e8tode de configuraci\u00f3" - }, - "title": "Servidor Multim\u00e8dia Plex" + } } } }, diff --git a/homeassistant/components/plex/translations/cs.json b/homeassistant/components/plex/translations/cs.json index f5e7e538f84..851a8782023 100644 --- a/homeassistant/components/plex/translations/cs.json +++ b/homeassistant/components/plex/translations/cs.json @@ -35,14 +35,12 @@ "title": "Vyberte server Plex" }, "user": { - "description": "Pro propojen\u00ed Plex serveru, pokra\u010dujte na [plex.tv](https://plex.tv).", - "title": "Plex Media Server" + "description": "Pro propojen\u00ed Plex serveru, pokra\u010dujte na [plex.tv](https://plex.tv)." }, "user_advanced": { "data": { "setup_method": "Metoda nastaven\u00ed" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/de.json b/homeassistant/components/plex/translations/de.json index 130d34505d2..3eb1c1b7707 100644 --- a/homeassistant/components/plex/translations/de.json +++ b/homeassistant/components/plex/translations/de.json @@ -35,14 +35,12 @@ "title": "Plex-Server ausw\u00e4hlen" }, "user": { - "description": "Gehe zu [plex.tv] (https://plex.tv), um einen Plex-Server zu verbinden", - "title": "Plex Media Server" + "description": "Gehe zu [plex.tv] (https://plex.tv), um einen Plex-Server zu verbinden" }, "user_advanced": { "data": { "setup_method": "Einrichtungsmethode" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/el.json b/homeassistant/components/plex/translations/el.json index 679ef3d3937..cd2147b51f4 100644 --- a/homeassistant/components/plex/translations/el.json +++ b/homeassistant/components/plex/translations/el.json @@ -35,14 +35,12 @@ "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Plex" }, "user": { - "description": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03c3\u03c4\u03bf [plex.tv](https://plex.tv) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Plex.", - "title": "\u0394\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 Plex Media" + "description": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03c3\u03c4\u03bf [plex.tv](https://plex.tv) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Plex." }, "user_advanced": { "data": { "setup_method": "\u039c\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2" - }, - "title": "\u0394\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae\u03c2 Plex Media" + } } } }, diff --git a/homeassistant/components/plex/translations/en.json b/homeassistant/components/plex/translations/en.json index 834594e1d27..1bd738ea10e 100644 --- a/homeassistant/components/plex/translations/en.json +++ b/homeassistant/components/plex/translations/en.json @@ -35,14 +35,12 @@ "title": "Select Plex server" }, "user": { - "description": "Continue to [plex.tv](https://plex.tv) to link a Plex server.", - "title": "Plex Media Server" + "description": "Continue to [plex.tv](https://plex.tv) to link a Plex server." }, "user_advanced": { "data": { "setup_method": "Setup method" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/es.json b/homeassistant/components/plex/translations/es.json index 38f57b91c7b..7099f63a9e8 100644 --- a/homeassistant/components/plex/translations/es.json +++ b/homeassistant/components/plex/translations/es.json @@ -35,14 +35,12 @@ "title": "Seleccione el servidor Plex" }, "user": { - "description": "Continuar hacia [plex.tv](https://plex.tv) para vincular un servidor Plex.", - "title": "Plex Media Server" + "description": "Continuar hacia [plex.tv](https://plex.tv) para vincular un servidor Plex." }, "user_advanced": { "data": { "setup_method": "M\u00e9todo de configuraci\u00f3n" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/et.json b/homeassistant/components/plex/translations/et.json index d815a092bca..31e6084146d 100644 --- a/homeassistant/components/plex/translations/et.json +++ b/homeassistant/components/plex/translations/et.json @@ -35,14 +35,12 @@ "title": "Vali Plex'i server" }, "user": { - "description": "Plexi serveri linkimiseks mine lehele [plex.tv] (https://plex.tv).", - "title": "" + "description": "Plexi serveri linkimiseks mine lehele [plex.tv] (https://plex.tv)." }, "user_advanced": { "data": { "setup_method": "Seadistusmeetod" - }, - "title": "" + } } } }, diff --git a/homeassistant/components/plex/translations/fi.json b/homeassistant/components/plex/translations/fi.json index 2c5872fe85a..4900dbbbec9 100644 --- a/homeassistant/components/plex/translations/fi.json +++ b/homeassistant/components/plex/translations/fi.json @@ -10,14 +10,10 @@ }, "title": "Manuaalinen Plex-konfigurointi" }, - "user": { - "title": "Plex Media Server" - }, "user_advanced": { "data": { "setup_method": "Asennusmenetelm\u00e4" - }, - "title": "Plex-mediapalvelin" + } } } }, diff --git a/homeassistant/components/plex/translations/fr.json b/homeassistant/components/plex/translations/fr.json index 73704d31c7a..e4c711afd8a 100644 --- a/homeassistant/components/plex/translations/fr.json +++ b/homeassistant/components/plex/translations/fr.json @@ -35,14 +35,12 @@ "title": "S\u00e9lectionnez le serveur Plex" }, "user": { - "description": "Continuez sur [plex.tv] (https://plex.tv) pour lier un serveur Plex.", - "title": "Plex Media Server" + "description": "Continuez sur [plex.tv] (https://plex.tv) pour lier un serveur Plex." }, "user_advanced": { "data": { "setup_method": "M\u00e9thode de configuration" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/hu.json b/homeassistant/components/plex/translations/hu.json index 3be797cc04f..3015494af0a 100644 --- a/homeassistant/components/plex/translations/hu.json +++ b/homeassistant/components/plex/translations/hu.json @@ -35,14 +35,12 @@ "title": "Plex-kiszolg\u00e1l\u00f3 kiv\u00e1laszt\u00e1sa" }, "user": { - "description": "Folytassa a [plex.tv] (https://plex.tv) oldalt a Plex szerver \u00f6sszekapcsol\u00e1s\u00e1hoz.", - "title": "Plex Media Server" + "description": "Folytassa a [plex.tv] (https://plex.tv) oldalt a Plex szerver \u00f6sszekapcsol\u00e1s\u00e1hoz." }, "user_advanced": { "data": { "setup_method": "Be\u00e1ll\u00edt\u00e1si m\u00f3dszer" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/id.json b/homeassistant/components/plex/translations/id.json index 7c596835e03..ccc2786ae54 100644 --- a/homeassistant/components/plex/translations/id.json +++ b/homeassistant/components/plex/translations/id.json @@ -35,14 +35,12 @@ "title": "Pilih server Plex" }, "user": { - "description": "Lanjutkan [plex.tv](https://plex.tv) untuk menautkan server Plex.", - "title": "Server Media Plex" + "description": "Lanjutkan [plex.tv](https://plex.tv) untuk menautkan server Plex." }, "user_advanced": { "data": { "setup_method": "Metode penyiapan" - }, - "title": "Server Media Plex" + } } } }, diff --git a/homeassistant/components/plex/translations/it.json b/homeassistant/components/plex/translations/it.json index 8fb0603a8ee..876ab634523 100644 --- a/homeassistant/components/plex/translations/it.json +++ b/homeassistant/components/plex/translations/it.json @@ -35,14 +35,12 @@ "title": "Seleziona il server Plex" }, "user": { - "description": "Continua su [plex.tv](https://plex.tv) per collegare un server Plex.", - "title": "Plex Media Server" + "description": "Continua su [plex.tv](https://plex.tv) per collegare un server Plex." }, "user_advanced": { "data": { "setup_method": "Metodo di impostazione" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/ja.json b/homeassistant/components/plex/translations/ja.json index 4b48bdfe695..be42bab1d85 100644 --- a/homeassistant/components/plex/translations/ja.json +++ b/homeassistant/components/plex/translations/ja.json @@ -35,14 +35,12 @@ "title": "Plex\u30b5\u30fc\u30d0\u30fc\u3092\u9078\u629e" }, "user": { - "description": "[plex.tv](https://plex.tv) \u306b\u9032\u307f\u3001Plex server\u3092\u30ea\u30f3\u30af\u3057\u307e\u3059\u3002", - "title": "Plex Media Server" + "description": "[plex.tv](https://plex.tv) \u306b\u9032\u307f\u3001Plex server\u3092\u30ea\u30f3\u30af\u3057\u307e\u3059\u3002" }, "user_advanced": { "data": { "setup_method": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u65b9\u6cd5" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/ko.json b/homeassistant/components/plex/translations/ko.json index d6b0c6a2341..453f32ee15b 100644 --- a/homeassistant/components/plex/translations/ko.json +++ b/homeassistant/components/plex/translations/ko.json @@ -35,14 +35,12 @@ "title": "Plex \uc11c\ubc84 \uc120\ud0dd\ud558\uae30" }, "user": { - "description": "Plex \uc11c\ubc84\ub97c \uc5f0\uacb0\ud558\ub824\uba74 [plex.tv](https://plex.tv)\ub85c \uacc4\uc18d \uc9c4\ud589\ud574\uc8fc\uc138\uc694.", - "title": "Plex \ubbf8\ub514\uc5b4 \uc11c\ubc84" + "description": "Plex \uc11c\ubc84\ub97c \uc5f0\uacb0\ud558\ub824\uba74 [plex.tv](https://plex.tv)\ub85c \uacc4\uc18d \uc9c4\ud589\ud574\uc8fc\uc138\uc694." }, "user_advanced": { "data": { "setup_method": "\uc124\uc815 \ubc29\ubc95" - }, - "title": "Plex \ubbf8\ub514\uc5b4 \uc11c\ubc84" + } } } }, diff --git a/homeassistant/components/plex/translations/lb.json b/homeassistant/components/plex/translations/lb.json index 916bcbc9042..d623b7cc011 100644 --- a/homeassistant/components/plex/translations/lb.json +++ b/homeassistant/components/plex/translations/lb.json @@ -35,14 +35,12 @@ "title": "Plex Server auswielen" }, "user": { - "description": "Verbann dech mat [plex.tv](https://pley.tv) fir ee Plex Server ze verlinken.", - "title": "Plex Media Server" + "description": "Verbann dech mat [plex.tv](https://pley.tv) fir ee Plex Server ze verlinken." }, "user_advanced": { "data": { "setup_method": "Setup Method" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/nl.json b/homeassistant/components/plex/translations/nl.json index a196555e7ea..afdc67007bd 100644 --- a/homeassistant/components/plex/translations/nl.json +++ b/homeassistant/components/plex/translations/nl.json @@ -35,14 +35,12 @@ "title": "Selecteer Plex server" }, "user": { - "description": "Ga verder naar [plex.tv] (https://plex.tv) om een Plex-server te koppelen.", - "title": "Plex Media Server" + "description": "Ga verder naar [plex.tv] (https://plex.tv) om een Plex-server te koppelen." }, "user_advanced": { "data": { "setup_method": "Installatiemethode" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/no.json b/homeassistant/components/plex/translations/no.json index ddd6ef4cdb2..c34b4b1c257 100644 --- a/homeassistant/components/plex/translations/no.json +++ b/homeassistant/components/plex/translations/no.json @@ -35,14 +35,12 @@ "title": "Velg Plex-server" }, "user": { - "description": "Fortsett til [plex.tv] (https://plex.tv) for \u00e5 koble en Plex-server.", - "title": "" + "description": "Fortsett til [plex.tv] (https://plex.tv) for \u00e5 koble en Plex-server." }, "user_advanced": { "data": { "setup_method": "Oppsettmetode" - }, - "title": "" + } } } }, diff --git a/homeassistant/components/plex/translations/pl.json b/homeassistant/components/plex/translations/pl.json index bd3e45da688..d4648973a5f 100644 --- a/homeassistant/components/plex/translations/pl.json +++ b/homeassistant/components/plex/translations/pl.json @@ -35,14 +35,12 @@ "title": "Wybierz serwer Plex" }, "user": { - "description": "Przejd\u017a do [plex.tv](https://plex.tv), aby po\u0142\u0105czy\u0107 serwer Plex.", - "title": "Serwer medi\u00f3w Plex" + "description": "Przejd\u017a do [plex.tv](https://plex.tv), aby po\u0142\u0105czy\u0107 serwer Plex." }, "user_advanced": { "data": { "setup_method": "Metoda konfiguracji" - }, - "title": "Serwer medi\u00f3w Plex" + } } } }, diff --git a/homeassistant/components/plex/translations/pt-BR.json b/homeassistant/components/plex/translations/pt-BR.json index ea74d2b173b..f8e9aa7dea4 100644 --- a/homeassistant/components/plex/translations/pt-BR.json +++ b/homeassistant/components/plex/translations/pt-BR.json @@ -35,14 +35,12 @@ "title": "Selecione servidor Plex" }, "user": { - "description": "Continue para [plex.tv](https://plex.tv) para vincular um servidor Plex.", - "title": "Plex Media Server" + "description": "Continue para [plex.tv](https://plex.tv) para vincular um servidor Plex." }, "user_advanced": { "data": { "setup_method": "M\u00e9todo de configura\u00e7\u00e3o" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/ru.json b/homeassistant/components/plex/translations/ru.json index b1530f0808b..fa05661f29c 100644 --- a/homeassistant/components/plex/translations/ru.json +++ b/homeassistant/components/plex/translations/ru.json @@ -35,14 +35,12 @@ "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0435\u0440\u0432\u0435\u0440 Plex" }, "user": { - "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 [plex.tv](https://plex.tv), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Plex \u043a Home Assistant.", - "title": "Plex Media Server" + "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 [plex.tv](https://plex.tv), \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Plex \u043a Home Assistant." }, "user_advanced": { "data": { "setup_method": "\u0421\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/sl.json b/homeassistant/components/plex/translations/sl.json index b1622219402..7d47584aad6 100644 --- a/homeassistant/components/plex/translations/sl.json +++ b/homeassistant/components/plex/translations/sl.json @@ -33,14 +33,12 @@ "title": "Izberite stre\u017enik Plex" }, "user": { - "description": "Nadaljujte do [plex.tv] (https://plex.tv), da pove\u017eete stre\u017enik Plex.", - "title": "Plex medijski stre\u017enik" + "description": "Nadaljujte do [plex.tv] (https://plex.tv), da pove\u017eete stre\u017enik Plex." }, "user_advanced": { "data": { "setup_method": "Na\u010din nastavitve" - }, - "title": "Plex medijski stre\u017enik" + } } } }, diff --git a/homeassistant/components/plex/translations/sv.json b/homeassistant/components/plex/translations/sv.json index 6ca36d302af..63b12e70e40 100644 --- a/homeassistant/components/plex/translations/sv.json +++ b/homeassistant/components/plex/translations/sv.json @@ -25,14 +25,10 @@ "description": "V\u00e4lj flera servrar tillg\u00e4ngliga, v\u00e4lj en:", "title": "V\u00e4lj Plex-server" }, - "user": { - "title": "Plex Media Server" - }, "user_advanced": { "data": { "setup_method": "Inst\u00e4llningsmetod" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/tr.json b/homeassistant/components/plex/translations/tr.json index c052006687f..423fc1ddbcb 100644 --- a/homeassistant/components/plex/translations/tr.json +++ b/homeassistant/components/plex/translations/tr.json @@ -35,14 +35,12 @@ "title": "Plex sunucusunu se\u00e7in" }, "user": { - "description": "Bir Plex sunucusunu ba\u011flamak i\u00e7in [plex.tv](https://plex.tv) ile devam edin.", - "title": "Plex Medya Sunucusu" + "description": "Bir Plex sunucusunu ba\u011flamak i\u00e7in [plex.tv](https://plex.tv) ile devam edin." }, "user_advanced": { "data": { "setup_method": "Kurulum y\u00f6ntemi" - }, - "title": "Plex Medya Sunucusu" + } } } }, diff --git a/homeassistant/components/plex/translations/uk.json b/homeassistant/components/plex/translations/uk.json index 20351cf735a..16ac314c1ad 100644 --- a/homeassistant/components/plex/translations/uk.json +++ b/homeassistant/components/plex/translations/uk.json @@ -35,14 +35,12 @@ "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Plex" }, "user": { - "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 [plex.tv](https://plex.tv), \u0449\u043e\u0431 \u043f\u0440\u0438\u0432'\u044f\u0437\u0430\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 Plex \u0434\u043e Home Assistant.", - "title": "Plex Media Server" + "description": "\u041f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u043d\u0430 [plex.tv](https://plex.tv), \u0449\u043e\u0431 \u043f\u0440\u0438\u0432'\u044f\u0437\u0430\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 Plex \u0434\u043e Home Assistant." }, "user_advanced": { "data": { "setup_method": "\u0421\u043f\u043e\u0441\u0456\u0431 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f" - }, - "title": "Plex Media Server" + } } } }, diff --git a/homeassistant/components/plex/translations/zh-Hans.json b/homeassistant/components/plex/translations/zh-Hans.json index 02f548f2286..877543e90b0 100644 --- a/homeassistant/components/plex/translations/zh-Hans.json +++ b/homeassistant/components/plex/translations/zh-Hans.json @@ -32,14 +32,10 @@ "description": "\u6709\u591a\u4e2a\u53ef\u7528\u670d\u52a1\u5668\uff0c\u8bf7\u9009\u62e9\uff1a", "title": "\u9009\u62e9 Plex \u670d\u52a1\u5668" }, - "user": { - "title": "Plex \u5a92\u4f53\u670d\u52a1\u5668" - }, "user_advanced": { "data": { "setup_method": "\u8bbe\u7f6e\u65b9\u6cd5" - }, - "title": "Plex \u5a92\u4f53\u670d\u52a1\u5668" + } } } }, diff --git a/homeassistant/components/plex/translations/zh-Hant.json b/homeassistant/components/plex/translations/zh-Hant.json index 7f19fa0d035..7f05dc4b3be 100644 --- a/homeassistant/components/plex/translations/zh-Hant.json +++ b/homeassistant/components/plex/translations/zh-Hant.json @@ -35,14 +35,12 @@ "title": "\u9078\u64c7 Plex \u4f3a\u670d\u5668" }, "user": { - "description": "\u7e7c\u7e8c\u81f3 [plex.tv](https://plex.tv) \u4ee5\u9023\u7d50\u4e00\u7d44 Plex \u4f3a\u670d\u5668\u3002", - "title": "Plex \u5a92\u9ad4\u4f3a\u670d\u5668" + "description": "\u7e7c\u7e8c\u81f3 [plex.tv](https://plex.tv) \u4ee5\u9023\u7d50\u4e00\u7d44 Plex \u4f3a\u670d\u5668\u3002" }, "user_advanced": { "data": { "setup_method": "\u8a2d\u5b9a\u6a21\u5f0f" - }, - "title": "Plex \u5a92\u9ad4\u4f3a\u670d\u5668" + } } } }, diff --git a/homeassistant/components/poolsense/translations/bg.json b/homeassistant/components/poolsense/translations/bg.json index a89cca15270..eb033e74f0f 100644 --- a/homeassistant/components/poolsense/translations/bg.json +++ b/homeassistant/components/poolsense/translations/bg.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "\u041f\u0430\u0440\u043e\u043b\u0430" - }, - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0432\u0430\u043d\u0435\u0442\u043e?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/ca.json b/homeassistant/components/poolsense/translations/ca.json index 923aedcee66..d5d996f444a 100644 --- a/homeassistant/components/poolsense/translations/ca.json +++ b/homeassistant/components/poolsense/translations/ca.json @@ -11,9 +11,7 @@ "data": { "email": "Correu electr\u00f2nic", "password": "Contrasenya" - }, - "description": "Vols comen\u00e7ar la configuraci\u00f3?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/cs.json b/homeassistant/components/poolsense/translations/cs.json index d4d94bd7334..520720ed5a1 100644 --- a/homeassistant/components/poolsense/translations/cs.json +++ b/homeassistant/components/poolsense/translations/cs.json @@ -11,9 +11,7 @@ "data": { "email": "E-mail", "password": "Heslo" - }, - "description": "Chcete za\u010d\u00edt nastavovat?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/de.json b/homeassistant/components/poolsense/translations/de.json index 5ce9313b442..d19b110ff96 100644 --- a/homeassistant/components/poolsense/translations/de.json +++ b/homeassistant/components/poolsense/translations/de.json @@ -11,9 +11,7 @@ "data": { "email": "E-Mail", "password": "Passwort" - }, - "description": "M\u00f6chtest Du mit der Einrichtung beginnen?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/el.json b/homeassistant/components/poolsense/translations/el.json index bb70337158e..f3fe349d939 100644 --- a/homeassistant/components/poolsense/translations/el.json +++ b/homeassistant/components/poolsense/translations/el.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" - }, - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/en.json b/homeassistant/components/poolsense/translations/en.json index 5203d6228c4..9e78fd62910 100644 --- a/homeassistant/components/poolsense/translations/en.json +++ b/homeassistant/components/poolsense/translations/en.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "Password" - }, - "description": "Do you want to start set up?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/es.json b/homeassistant/components/poolsense/translations/es.json index 2d38dd670d3..d4b68388db2 100644 --- a/homeassistant/components/poolsense/translations/es.json +++ b/homeassistant/components/poolsense/translations/es.json @@ -11,9 +11,7 @@ "data": { "email": "Correo electr\u00f3nico", "password": "Contrase\u00f1a" - }, - "description": "\u00bfQuieres empezar la configuraci\u00f3n?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/et.json b/homeassistant/components/poolsense/translations/et.json index 4e1dc8db1ff..57c3b8aef6e 100644 --- a/homeassistant/components/poolsense/translations/et.json +++ b/homeassistant/components/poolsense/translations/et.json @@ -11,9 +11,7 @@ "data": { "email": "E-post", "password": "Salas\u00f5na" - }, - "description": "Kas alustan seadistamist?", - "title": "" + } } } } diff --git a/homeassistant/components/poolsense/translations/fr.json b/homeassistant/components/poolsense/translations/fr.json index 4a32776bccb..dda4b0da753 100644 --- a/homeassistant/components/poolsense/translations/fr.json +++ b/homeassistant/components/poolsense/translations/fr.json @@ -11,9 +11,7 @@ "data": { "email": "Courriel", "password": "Mot de passe" - }, - "description": "Voulez-vous commencer la configuration\u00a0?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/he.json b/homeassistant/components/poolsense/translations/he.json index f285e1ec479..8c832faf350 100644 --- a/homeassistant/components/poolsense/translations/he.json +++ b/homeassistant/components/poolsense/translations/he.json @@ -11,8 +11,7 @@ "data": { "email": "\u05d3\u05d5\u05d0\"\u05dc", "password": "\u05e1\u05d9\u05e1\u05de\u05d4" - }, - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" + } } } } diff --git a/homeassistant/components/poolsense/translations/hu.json b/homeassistant/components/poolsense/translations/hu.json index 39274e14c21..3a80610269b 100644 --- a/homeassistant/components/poolsense/translations/hu.json +++ b/homeassistant/components/poolsense/translations/hu.json @@ -11,9 +11,7 @@ "data": { "email": "E-mail", "password": "Jelsz\u00f3" - }, - "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/id.json b/homeassistant/components/poolsense/translations/id.json index 6e40f5f0925..3cf0dcd65bb 100644 --- a/homeassistant/components/poolsense/translations/id.json +++ b/homeassistant/components/poolsense/translations/id.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "Kata Sandi" - }, - "description": "Ingin memulai penyiapan?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/it.json b/homeassistant/components/poolsense/translations/it.json index e6ea298e31d..d4a55b4a768 100644 --- a/homeassistant/components/poolsense/translations/it.json +++ b/homeassistant/components/poolsense/translations/it.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "Password" - }, - "description": "Vuoi iniziare la configurazione?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/ja.json b/homeassistant/components/poolsense/translations/ja.json index 28d7e1d3d18..63dee3f0739 100644 --- a/homeassistant/components/poolsense/translations/ja.json +++ b/homeassistant/components/poolsense/translations/ja.json @@ -11,9 +11,7 @@ "data": { "email": "E\u30e1\u30fc\u30eb", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" - }, - "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/ko.json b/homeassistant/components/poolsense/translations/ko.json index ec8c7dfc90f..d649b93e3ae 100644 --- a/homeassistant/components/poolsense/translations/ko.json +++ b/homeassistant/components/poolsense/translations/ko.json @@ -11,9 +11,7 @@ "data": { "email": "\uc774\uba54\uc77c", "password": "\ube44\ubc00\ubc88\ud638" - }, - "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/lb.json b/homeassistant/components/poolsense/translations/lb.json index 4d7065e6380..2a671d8e1de 100644 --- a/homeassistant/components/poolsense/translations/lb.json +++ b/homeassistant/components/poolsense/translations/lb.json @@ -11,9 +11,7 @@ "data": { "email": "E-Mail", "password": "Passwuert" - }, - "description": "Soll den Ariichtungs Prozess gestart ginn?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/nl.json b/homeassistant/components/poolsense/translations/nl.json index 1fd59ebf2ea..46fc915d7bd 100644 --- a/homeassistant/components/poolsense/translations/nl.json +++ b/homeassistant/components/poolsense/translations/nl.json @@ -11,9 +11,7 @@ "data": { "email": "E-mail", "password": "Wachtwoord" - }, - "description": "Wilt u beginnen met instellen?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/no.json b/homeassistant/components/poolsense/translations/no.json index e2873a36080..3b1ab46d773 100644 --- a/homeassistant/components/poolsense/translations/no.json +++ b/homeassistant/components/poolsense/translations/no.json @@ -11,9 +11,7 @@ "data": { "email": "E-post", "password": "Passord" - }, - "description": "Vil du starte oppsettet?", - "title": "" + } } } } diff --git a/homeassistant/components/poolsense/translations/pl.json b/homeassistant/components/poolsense/translations/pl.json index 6f87cacce4f..e29ac245760 100644 --- a/homeassistant/components/poolsense/translations/pl.json +++ b/homeassistant/components/poolsense/translations/pl.json @@ -11,9 +11,7 @@ "data": { "email": "Adres e-mail", "password": "Has\u0142o" - }, - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/pt-BR.json b/homeassistant/components/poolsense/translations/pt-BR.json index 8b2e2bddda0..3fa0e51875e 100644 --- a/homeassistant/components/poolsense/translations/pt-BR.json +++ b/homeassistant/components/poolsense/translations/pt-BR.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "Senha" - }, - "description": "Deseja iniciar a configura\u00e7\u00e3o?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/pt.json b/homeassistant/components/poolsense/translations/pt.json index 0dc8303b485..cc2666db4c5 100644 --- a/homeassistant/components/poolsense/translations/pt.json +++ b/homeassistant/components/poolsense/translations/pt.json @@ -11,9 +11,7 @@ "data": { "email": "Email", "password": "Palavra-passe" - }, - "description": "[%key:common::config_flow::description%]", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/ru.json b/homeassistant/components/poolsense/translations/ru.json index 09c94368cda..90911c39d8d 100644 --- a/homeassistant/components/poolsense/translations/ru.json +++ b/homeassistant/components/poolsense/translations/ru.json @@ -11,9 +11,7 @@ "data": { "email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b", "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/sv.json b/homeassistant/components/poolsense/translations/sv.json deleted file mode 100644 index ad96ce6310d..00000000000 --- a/homeassistant/components/poolsense/translations/sv.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "description": "Vill du starta konfigurationen?" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/poolsense/translations/tr.json b/homeassistant/components/poolsense/translations/tr.json index e1b4f150e92..1e2e9d0c5b8 100644 --- a/homeassistant/components/poolsense/translations/tr.json +++ b/homeassistant/components/poolsense/translations/tr.json @@ -11,9 +11,7 @@ "data": { "email": "E-posta", "password": "Parola" - }, - "description": "Kuruluma ba\u015flamak ister misiniz?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/uk.json b/homeassistant/components/poolsense/translations/uk.json index 6ac3b97f741..9723921f479 100644 --- a/homeassistant/components/poolsense/translations/uk.json +++ b/homeassistant/components/poolsense/translations/uk.json @@ -11,9 +11,7 @@ "data": { "email": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438", "password": "\u041f\u0430\u0440\u043e\u043b\u044c" - }, - "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043f\u043e\u0447\u0430\u0442\u0438 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f?", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/poolsense/translations/zh-Hant.json b/homeassistant/components/poolsense/translations/zh-Hant.json index 62ffca35e9f..957a11cfd61 100644 --- a/homeassistant/components/poolsense/translations/zh-Hant.json +++ b/homeassistant/components/poolsense/translations/zh-Hant.json @@ -11,9 +11,7 @@ "data": { "email": "\u96fb\u5b50\u90f5\u4ef6", "password": "\u5bc6\u78bc" - }, - "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f", - "title": "PoolSense" + } } } } diff --git a/homeassistant/components/ps4/translations/bg.json b/homeassistant/components/ps4/translations/bg.json index 8ed9242fc3e..ac2b20066f4 100644 --- a/homeassistant/components/ps4/translations/bg.json +++ b/homeassistant/components/ps4/translations/bg.json @@ -14,8 +14,7 @@ }, "step": { "creds": { - "description": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0438 \u0441\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438. \u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"\u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435\" \u0438 \u0441\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u0432 PS4 2nd Screen App, \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"Refresh devices\" \u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \"Home-Assistant\" \u0437\u0430 \u0434\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435.", - "title": "PlayStation 4" + "description": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0438 \u0441\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438. \u041d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"\u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435\" \u0438 \u0441\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u0432 PS4 2nd Screen App, \u043d\u0430\u0442\u0438\u0441\u043d\u0435\u0442\u0435 \"Refresh devices\" \u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \"Home-Assistant\" \u0437\u0430 \u0434\u0430 \u043f\u0440\u043e\u0434\u044a\u043b\u0436\u0438\u0442\u0435." }, "link": { "data": { @@ -23,17 +22,13 @@ "ip_address": "IP \u0430\u0434\u0440\u0435\u0441", "name": "\u0418\u043c\u0435", "region": "\u0420\u0435\u0433\u0438\u043e\u043d" - }, - "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0432\u0430\u0448\u0430\u0442\u0430 PlayStation 4 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f. \u0417\u0430 \u201ePIN\u201c \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u0432 \u201eSettings\u201c \u043d\u0430 \u0412\u0430\u0448\u0430\u0442\u0430 PlayStation 4 \u043a\u043e\u043d\u0437\u043e\u043b\u0430. \u0421\u043b\u0435\u0434 \u0442\u043e\u0432\u0430 \u043f\u0440\u0435\u043c\u0438\u043d\u0435\u0442\u0435 \u043a\u044a\u043c \u201eMobile App Connection Settings\u201c \u0438 \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u201eAdd Device\u201c. \u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0438\u044f PIN \u043a\u043e\u0434. \u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0447\u0435\u0442\u0435\u0442\u0435 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430](https://www.home-assistant.io/components/ps4/) \u0437\u0430 \u0434\u043e\u043f\u044a\u043b\u043d\u0438\u0442\u0435\u043b\u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP \u0430\u0434\u0440\u0435\u0441 (\u041e\u0441\u0442\u0430\u0432\u0435\u0442\u0435 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0442\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0442\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435).", "mode": "\u0420\u0435\u0436\u0438\u043c \u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435" - }, - "description": "\u0418\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0440\u0435\u0436\u0438\u043c \u0437\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0435. \u041f\u043e\u043b\u0435\u0442\u043e IP \u0430\u0434\u0440\u0435\u0441 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0441\u0435 \u043e\u0441\u0442\u0430\u0432\u0438 \u043f\u0440\u0430\u0437\u043d\u043e, \u0430\u043a\u043e \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u043e\u0442\u043a\u0440\u0438\u0432\u0430\u043d\u0435, \u0442\u044a\u0439 \u043a\u0430\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0442\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0449\u0435 \u0431\u044a\u0434\u0430\u0442 \u043e\u0442\u043a\u0440\u0438\u0442\u0438.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/ca.json b/homeassistant/components/ps4/translations/ca.json index 6486c154360..37cfabecf05 100644 --- a/homeassistant/components/ps4/translations/ca.json +++ b/homeassistant/components/ps4/translations/ca.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Credencials necess\u00e0ries. Prem 'Envia' i, a continuaci\u00f3, a la segona pantalla de l'aplicaci\u00f3 de la PS4, actualitza els dispositius i selecciona 'Home-Assistant' per continuar.", - "title": "PlayStation 4" + "description": "Credencials necess\u00e0ries. Prem 'Envia' i, a continuaci\u00f3, a la segona pantalla de l'aplicaci\u00f3 de la PS4, actualitza els dispositius i selecciona 'Home-Assistant' per continuar." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "V\u00e9s a 'Configuraci\u00f3' de la teva consola PlayStation 4. A continuaci\u00f3, v\u00e9s a 'Configuraci\u00f3 de connexi\u00f3 de l'aplicaci\u00f3 m\u00f2bil' i selecciona 'Afegeix un dispositiu' per obtenir el PIN." - }, - "description": "Introdueix la informaci\u00f3 de la teva PlayStation 4. Per al Codi PIN, ves a 'Configuraci\u00f3' a la consola de la PlayStation 4. Despr\u00e9s navega fins a 'Configuraci\u00f3 de la connexi\u00f3 de l'aplicaci\u00f3 m\u00f2bil' i selecciona 'Afegir dispositiu'. Introdueix el Codi PIN que es mostra. Consulta la [documentaci\u00f3](https://www.home-assistant.io/components/ps4/) per a m\u00e9s informaci\u00f3.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Deixa-ho en blanc si selecciones el descobriment autom\u00e0tic." - }, - "description": "Selecciona el mode de configuraci\u00f3. El camp de l'Adre\u00e7a IP es pot deixar en blanc si selecciones descobriment autom\u00e0tic (els dispositius es descobriran autom\u00e0ticament).", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/cs.json b/homeassistant/components/ps4/translations/cs.json index 0ef5eb5afee..e4a256e7522 100644 --- a/homeassistant/components/ps4/translations/cs.json +++ b/homeassistant/components/ps4/translations/cs.json @@ -13,26 +13,19 @@ "no_ipaddress": "Zadejte IP adresu PlayStation 4, kter\u00fd chcete konfigurovat." }, "step": { - "creds": { - "title": "PlayStation 4" - }, "link": { "data": { "code": "PIN k\u00f3d", "ip_address": "IP adresa", "name": "Jm\u00e9no", "region": "Region" - }, - "description": "Zadejte sv\u00e9 informace o PlayStation 4. \"PIN\" naleznete v \"Nastaven\u00ed\" sv\u00e9 konzole PlayStation 4. Pot\u00e9 p\u0159ejd\u011bte do \"Nastaven\u00ed p\u0159ipojen\u00ed mobiln\u00ed aplikace\" a vyberte \"P\u0159idat za\u0159\u00edzen\u00ed\". Zadejte PIN. Dal\u0161\u00ed informace naleznete v [dokumentaci](https://www.home-assistant.io/components/ps4/).", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP adresa (Pokud pou\u017e\u00edv\u00e1te automatick\u00e9 zji\u0161\u0165ov\u00e1n\u00ed, ponechte pr\u00e1zdn\u00e9.)", "mode": "Re\u017eim nastaven\u00ed" - }, - "description": "Vyberte re\u017eim pro konfiguraci. Pole IP adresa m\u016f\u017ee b\u00fdt ponech\u00e1no pr\u00e1zdn\u00e9, pokud vyberete \"automatick\u00e9 zji\u0161\u0165ov\u00e1n\u00ed\", proto\u017ee za\u0159\u00edzen\u00ed budou automaticky objevena.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/da.json b/homeassistant/components/ps4/translations/da.json index 747e2d07146..2f95f6fa485 100644 --- a/homeassistant/components/ps4/translations/da.json +++ b/homeassistant/components/ps4/translations/da.json @@ -13,8 +13,7 @@ }, "step": { "creds": { - "description": "Der kr\u00e6ves legitimationsoplysninger. Tryk p\u00e5 'Indsend' og derefter i PS4 2. sk\u00e6rm-app, opdater enheder, og v\u00e6lg 'Home Assistant'-enhed for at forts\u00e6tte.", - "title": "PlayStation 4" + "description": "Der kr\u00e6ves legitimationsoplysninger. Tryk p\u00e5 'Indsend' og derefter i PS4 2. sk\u00e6rm-app, opdater enheder, og v\u00e6lg 'Home Assistant'-enhed for at forts\u00e6tte." }, "link": { "data": { @@ -22,17 +21,13 @@ "ip_address": "IP-adresse", "name": "Navn", "region": "Omr\u00e5de" - }, - "description": "Indtast dine PlayStation 4-oplysninger. For 'PIN' skal du navigere til 'Indstillinger' p\u00e5 din PlayStation 4-konsol. Naviger derefter til 'Mobile App Connection Settings' og v\u00e6lg 'Add Device'. Indtast den pinkode, der vises. Se [dokumentation](https://www.home-assistant.io/components/ps4/) for yderligere oplysninger.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP-adresse (lad det v\u00e6re tomt, hvis du bruger automatisk registrering).", "mode": "Konfigurationstilstand" - }, - "description": "V\u00e6lg tilstand for konfiguration. IP-adressefeltet kan v\u00e6re tomt, hvis du v\u00e6lger automatisk registrering, da enheder automatisk bliver fundet.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/de.json b/homeassistant/components/ps4/translations/de.json index 94012575c06..8a5d700f2fc 100644 --- a/homeassistant/components/ps4/translations/de.json +++ b/homeassistant/components/ps4/translations/de.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Anmeldeinformationen ben\u00f6tigt. Klicke auf \"Senden\" und dann in der PS4 2nd Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\"-Ger\u00e4t aus, um fortzufahren.", - "title": "PlayStation 4" + "description": "Anmeldeinformationen ben\u00f6tigt. Klicke auf \"Senden\" und dann in der PS4 2nd Screen App, aktualisiere die Ger\u00e4te und w\u00e4hle das \"Home-Assistant\"-Ger\u00e4t aus, um fortzufahren." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navigiere auf deiner PlayStation 4-Konsole zu \"Einstellungen\". Navigiere dann zu \"Mobile App-Verbindungseinstellungen\" und w\u00e4hle \"Ger\u00e4t hinzuf\u00fcgen\", um den Pin zu erhalten." - }, - "description": "Gib deine PlayStation 4-Informationen ein. Navigiere f\u00fcr den PIN-Code auf der PlayStation 4-Konsole zu \"Einstellungen\". Navigiere dann zu \"Mobile App-Verbindungseinstellungen\" und w\u00e4hle \"Ger\u00e4t hinzuf\u00fcgen\" aus. Gib die angezeigte PIN-Code ein. Weitere Informationen findest du in der [Dokumentation](https://www.home-assistant.io/components/ps4/).", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Lasse das Feld leer, wenn du die automatische Erkennung ausw\u00e4hlst." - }, - "description": "W\u00e4hle den Modus f\u00fcr die Konfiguration aus. Das Feld IP-Adresse kann leer bleiben, wenn die automatische Erkennung ausgew\u00e4hlt wird, da Ger\u00e4te automatisch erkannt werden.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/el.json b/homeassistant/components/ps4/translations/el.json index f3899b682f3..7b5350a0418 100644 --- a/homeassistant/components/ps4/translations/el.json +++ b/homeassistant/components/ps4/translations/el.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\u0391\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \"\u03a5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae\" \u03ba\u03b1\u03b9, \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae PS4 2nd Screen App, \u03b1\u03bd\u03b1\u03bd\u03b5\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \"Home-Assistant\" \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5.", - "title": "PlayStation 4" + "description": "\u0391\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1. \u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \"\u03a5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae\" \u03ba\u03b1\u03b9, \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae PS4 2nd Screen App, \u03b1\u03bd\u03b1\u03bd\u03b5\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \"Home-Assistant\" \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03b5\u03c4\u03b5." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "\u03a0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \"\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2\" \u03c3\u03c4\u03b7\u03bd \u03ba\u03bf\u03bd\u03c3\u03cc\u03bb\u03b1 PlayStation 4. \u03a3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf 'Mobile App Connection Settings' \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 'Add Device' \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c4\u03bf pin." - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c4\u03bf\u03c5 PlayStation 4. \u0393\u03b9\u03b1 \u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b9\u03c2 \"\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2\" \u03c3\u03c4\u03b7\u03bd \u03ba\u03bf\u03bd\u03c3\u03cc\u03bb\u03b1 PlayStation 4. \u03a3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c0\u03bb\u03bf\u03b7\u03b3\u03b7\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf 'Mobile App Connection Settings' (\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac) \u03ba\u03b1\u03b9 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 'Add Device' (\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2). \u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9. \u0391\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7](https://www.home-assistant.io/components/ps4/) \u03b3\u03b9\u03b1 \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "\u0391\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf \u03ba\u03b5\u03bd\u03cc \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2. \u03a4\u03bf \u03c0\u03b5\u03b4\u03af\u03bf \u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03af\u03bd\u03b5\u03b9 \u03ba\u03b5\u03bd\u03cc \u03b5\u03ac\u03bd \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03b5\u03c4\u03b5 Auto Discovery, \u03ba\u03b1\u03b8\u03ce\u03c2 \u03bf\u03b9 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03b8\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03bf\u03cd\u03bd \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/en.json b/homeassistant/components/ps4/translations/en.json index 1c8640efe2e..e8a0c58a566 100644 --- a/homeassistant/components/ps4/translations/en.json +++ b/homeassistant/components/ps4/translations/en.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue.", - "title": "PlayStation 4" + "description": "Credentials needed. Press 'Submit' and then in the PS4 2nd Screen App, refresh devices and select the 'Home-Assistant' device to continue." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device' to get the pin." - }, - "description": "Enter your PlayStation 4 information. For PIN Code, navigate to 'Settings' on your PlayStation 4 console. Then navigate to 'Mobile App Connection Settings' and select 'Add Device'. Enter the PIN Code that is displayed. Refer to the [documentation](https://www.home-assistant.io/components/ps4/) for additional info.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Leave blank if selecting auto-discovery." - }, - "description": "Select mode for configuration. The IP Address field can be left blank if selecting Auto Discovery, as devices will be automatically discovered.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/es-419.json b/homeassistant/components/ps4/translations/es-419.json index 220183775f4..c5d8fb80a54 100644 --- a/homeassistant/components/ps4/translations/es-419.json +++ b/homeassistant/components/ps4/translations/es-419.json @@ -13,8 +13,7 @@ }, "step": { "creds": { - "description": "Credenciales necesarias. Presione 'Enviar' y luego en la aplicaci\u00f3n de la segunda pantalla de PS4, actualice los dispositivos y seleccione el dispositivo 'Home-Assistant' para continuar.", - "title": "Playstation 4" + "description": "Credenciales necesarias. Presione 'Enviar' y luego en la aplicaci\u00f3n de la segunda pantalla de PS4, actualice los dispositivos y seleccione el dispositivo 'Home-Assistant' para continuar." }, "link": { "data": { @@ -22,17 +21,13 @@ "ip_address": "Direcci\u00f3n IP", "name": "Nombre", "region": "Regi\u00f3n" - }, - "description": "Ingresa tu informaci\u00f3n de PlayStation 4. Para 'PIN', navegue hasta 'Configuraci\u00f3n' en su consola PlayStation 4. Luego navegue a 'Configuraci\u00f3n de conexi\u00f3n de la aplicaci\u00f3n m\u00f3vil' y seleccione 'Agregar dispositivo'. Ingrese el PIN que se muestra.", - "title": "Playstation 4" + } }, "mode": { "data": { "ip_address": "Direcci\u00f3n IP (dejar en blanco si se utiliza el descubrimiento autom\u00e1tico).", "mode": "Modo de configuraci\u00f3n" - }, - "description": "Seleccione el modo para la configuraci\u00f3n. El campo Direcci\u00f3n IP puede dejarse en blanco si selecciona Descubrimiento autom\u00e1tico, ya que los dispositivos se descubrir\u00e1n autom\u00e1ticamente.", - "title": "Playstation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/es.json b/homeassistant/components/ps4/translations/es.json index 7a807953b5e..a2c6e6fd1f4 100644 --- a/homeassistant/components/ps4/translations/es.json +++ b/homeassistant/components/ps4/translations/es.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Credenciales necesarias. Pulsa 'Enviar' y, a continuaci\u00f3n, en la app de segunda pantalla de PS4, actualiza la lista de dispositivos y selecciona el dispositivo 'Home-Assistant' para continuar.", - "title": "PlayStation 4" + "description": "Credenciales necesarias. Pulsa 'Enviar' y, a continuaci\u00f3n, en la app de segunda pantalla de PS4, actualiza la lista de dispositivos y selecciona el dispositivo 'Home-Assistant' para continuar." }, "link": { "data": { @@ -24,17 +23,13 @@ "ip_address": "Direcci\u00f3n IP", "name": "Nombre", "region": "Regi\u00f3n" - }, - "description": "Introduce la informaci\u00f3n de tu PlayStation 4. Para el 'PIN', ve a los 'Ajustes' en tu PlayStation 4. Despu\u00e9s dir\u00edgete hasta 'Ajustes de conexi\u00f3n de la aplicaci\u00f3n para m\u00f3viles' y selecciona 'A\u00f1adir dispositivo'. Introduce el PIN mostrado. Consulta la [documentaci\u00f3n](https://www.home-assistant.io/components/ps4/) para m\u00e1s informaci\u00f3n.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "Direcci\u00f3n IP (d\u00e9jalo en blanco si usas la detecci\u00f3n autom\u00e1tica).", "mode": "Modo configuraci\u00f3n" - }, - "description": "Selecciona el modo de configuraci\u00f3n. El campo de direcci\u00f3n IP puede dejarse en blanco si se selecciona la detecci\u00f3n autom\u00e1tica, ya que los dispositivos se detectar\u00e1n autom\u00e1ticamente.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/et.json b/homeassistant/components/ps4/translations/et.json index 83ef7a6f14c..071d7813976 100644 --- a/homeassistant/components/ps4/translations/et.json +++ b/homeassistant/components/ps4/translations/et.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Vaja on volitusi. Vajuta nuppu Esita ja seej\u00e4rel v\u00e4rskenda PS4 2nd Screen rakenduses seadmeid ning vali j\u00e4tkamiseks seade \u201eHome-Assistant\u201d.", - "title": "" + "description": "Vaja on volitusi. Vajuta nuppu Esita ja seej\u00e4rel v\u00e4rskenda PS4 2nd Screen rakenduses seadmeid ning vali j\u00e4tkamiseks seade \u201eHome-Assistant\u201d." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navigeeri oma PlayStation 4 konsoolis jaotisse \u201eSeaded\u201d. Seej\u00e4rel navigeeri jaotisse \"Mobiilirakenduse \u00fchenduse s\u00e4tted\" ja vali PIN-koodi saamiseks \"Lisa seade\"." - }, - "description": "Sisesta oma PlayStation 4 teave. PIN koodi saamiseks mine PlayStation 4 konsooli \u201eSeaded\u201d. Seej\u00e4rel liigu jaotisse \u201eMobiilirakenduse \u00fchenduse seaded\u201d ja vali \u201eLisa seade\u201d. SisestaPIN kood . Lisateavet leiad [dokumentatsioonist] (https://www.home-assistant.io/components/ps4/).", - "title": "" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Automaatse avastamise valimiseks j\u00e4ta v\u00e4li t\u00fchjaks." - }, - "description": "Valikonfigureerimise re\u017eiim. V\u00e4lja IP-aadress saab automaatse tuvastamise valimisel t\u00fchjaks j\u00e4tta, kuna seadmed avastatakse automaatselt.", - "title": "" + } } } } diff --git a/homeassistant/components/ps4/translations/fi.json b/homeassistant/components/ps4/translations/fi.json index f61b9f0c162..8b179c48e4b 100644 --- a/homeassistant/components/ps4/translations/fi.json +++ b/homeassistant/components/ps4/translations/fi.json @@ -1,24 +1,19 @@ { "config": { "step": { - "creds": { - "title": "Playstation 4" - }, "link": { "data": { "code": "PIN", "ip_address": "IP-osoite", "name": "Nimi", "region": "Alue" - }, - "title": "Playstation 4" + } }, "mode": { "data": { "ip_address": "IP-osoite (j\u00e4t\u00e4 tyhj\u00e4ksi, jos k\u00e4yt\u00e4t automaattista etsint\u00e4\u00e4).", "mode": "Konfigurointitila" - }, - "title": "Playstation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/fr.json b/homeassistant/components/ps4/translations/fr.json index 8bc2575d246..73e2e18e22a 100644 --- a/homeassistant/components/ps4/translations/fr.json +++ b/homeassistant/components/ps4/translations/fr.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Informations d'identification requises. Appuyez sur \u00ab\u00a0Envoyer\u00a0\u00bb, puis dans l'application PS4 Second Screen, actualisez les appareils et s\u00e9lectionnez l'appareil \u00ab\u00a0Home Assistant\u00a0\u00bb pour continuer.", - "title": "PlayStation 4" + "description": "Informations d'identification requises. Appuyez sur \u00ab\u00a0Envoyer\u00a0\u00bb, puis dans l'application PS4 Second Screen, actualisez les appareils et s\u00e9lectionnez l'appareil \u00ab\u00a0Home Assistant\u00a0\u00bb pour continuer." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Acc\u00e9dez aux \u00ab\u00a0Param\u00e8tres\u00a0\u00bb sur votre console PlayStation\u00a04, puis acc\u00e9dez \u00e0 \u00ab\u00a0Param\u00e8tres de connexion de l'application mobile\u00a0\u00bb et s\u00e9lectionnez \u00ab\u00a0Ajouter un appareil\u00a0\u00bb afin d'obtenir le code PIN." - }, - "description": "Saisissez les informations de votre PlayStation\u00a04. Pour le Code PIN, acc\u00e9dez aux \u00ab\u00a0Param\u00e8tres\u00a0\u00bb sur votre console PlayStation\u00a04, puis acc\u00e9dez \u00e0 \u00ab\u00a0Param\u00e8tres de connexion de l'application mobile\u00a0\u00bb et s\u00e9lectionnez \u00ab\u00a0Ajouter un appareil\u00a0\u00bb. Entrez le Code PIN affich\u00e9. Consultez la [documentation](https://www.home-assistant.io/components/ps4/) pour plus d'informations.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Laissez le champ vide si vous s\u00e9lectionnez la d\u00e9couverte automatique." - }, - "description": "S\u00e9lectionnez le mode de configuration. Le champ Adresse IP peut \u00eatre laiss\u00e9 vide si vous s\u00e9lectionnez D\u00e9couverte automatique, car les appareils seront automatiquement d\u00e9couverts.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/he.json b/homeassistant/components/ps4/translations/he.json index 421f869d8c5..0aaba028b7e 100644 --- a/homeassistant/components/ps4/translations/he.json +++ b/homeassistant/components/ps4/translations/he.json @@ -8,20 +8,13 @@ "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "step": { - "creds": { - "title": "\u05e4\u05dc\u05d9\u05d9\u05e1\u05d8\u05d9\u05d9\u05e9\u05df 4" - }, "link": { "data": { "code": "\u05e7\u05d5\u05d3 PIN", "ip_address": "\u05db\u05ea\u05d5\u05d1\u05ea IP", "name": "\u05e9\u05dd", "region": "\u05d0\u05d9\u05d6\u05d5\u05e8" - }, - "title": "\u05e4\u05dc\u05d9\u05d9\u05e1\u05d8\u05d9\u05d9\u05e9\u05df 4" - }, - "mode": { - "title": "\u05e4\u05dc\u05d9\u05d9\u05e1\u05d8\u05d9\u05d9\u05e9\u05df 4" + } } } } diff --git a/homeassistant/components/ps4/translations/hu.json b/homeassistant/components/ps4/translations/hu.json index 24da7fc15f5..ab48f3177e4 100644 --- a/homeassistant/components/ps4/translations/hu.json +++ b/homeassistant/components/ps4/translations/hu.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Hiteles\u00edt\u0151 adatok sz\u00fcks\u00e9gesek. Nyomja meg a \u201eMehet\u201d gombot, majd a PS4 2. k\u00e9perny\u0151 alkalmaz\u00e1sban friss\u00edtse az eszk\u00f6z\u00f6ket, \u00e9s a folytat\u00e1shoz v\u00e1lassza a \u201eHome-Assistant\u201d eszk\u00f6zt.", - "title": "PlayStation 4" + "description": "Hiteles\u00edt\u0151 adatok sz\u00fcks\u00e9gesek. Nyomja meg a \u201eMehet\u201d gombot, majd a PS4 2. k\u00e9perny\u0151 alkalmaz\u00e1sban friss\u00edtse az eszk\u00f6z\u00f6ket, \u00e9s a folytat\u00e1shoz v\u00e1lassza a \u201eHome-Assistant\u201d eszk\u00f6zt." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Keresse meg a PlayStation 4 konzol \"Be\u00e1ll\u00edt\u00e1sok\" gombj\u00e1t. Ezut\u00e1n keresse meg a \"Mobilalkalmaz\u00e1s-kapcsolat be\u00e1ll\u00edt\u00e1sai\" lehet\u0151s\u00e9get, \u00e9s v\u00e1lassza az \"Eszk\u00f6z hozz\u00e1ad\u00e1sa\" lehet\u0151s\u00e9get a pin lek\u00e9r\u00e9s\u00e9hez." - }, - "description": "Adja meg a PlayStation 4 adatait. A PIN-k\u00f3d eset\u00e9ben keresse meg a PlayStation 4 konzol \u201eBe\u00e1ll\u00edt\u00e1sok\u201d elem\u00e9t. Ezut\u00e1n keresse meg a \u201eMobilalkalmaz\u00e1s-kapcsolat be\u00e1ll\u00edt\u00e1sai\u201d elemet, \u00e9s v\u00e1lassza az \u201eEszk\u00f6z hozz\u00e1ad\u00e1sa\u201d lehet\u0151s\u00e9get. \u00cdrja be a megjelen\u0151 PIN-k\u00f3d . Tov\u00e1bbi inform\u00e1ci\u00f3k a [dokument\u00e1ci\u00f3ban] tal\u00e1lhat\u00f3k (https://www.home-assistant.io/components/ps4/).", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Az automatikus felder\u00edt\u00e9s haszn\u00e1lat\u00e1hoz hagyja \u00fcresen" - }, - "description": "V\u00e1lassza ki a m\u00f3dot a konfigur\u00e1l\u00e1shoz. Az IP c\u00edm mez\u0151 \u00fcresen maradhat, ha az Automatikus felder\u00edt\u00e9s lehet\u0151s\u00e9get v\u00e1lasztja, mivel az eszk\u00f6z\u00f6k automatikusan felfedez\u0151dnek.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/id.json b/homeassistant/components/ps4/translations/id.json index beb15fb5c4c..67c25538464 100644 --- a/homeassistant/components/ps4/translations/id.json +++ b/homeassistant/components/ps4/translations/id.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Kredensial diperlukan. Tekan 'Kirim' dan kemudian di Aplikasi Layar ke-2 PS4, segarkan perangkat dan pilih perangkat 'Home-Assistant' untuk melanjutkan.", - "title": "PlayStation 4" + "description": "Kredensial diperlukan. Tekan 'Kirim' dan kemudian di Aplikasi Layar ke-2 PS4, segarkan perangkat dan pilih perangkat 'Home-Assistant' untuk melanjutkan." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navigasikan ke 'Pengaturan' di konsol PlayStation 4 Anda. Kemudian navigasikan ke 'Pengaturan Koneksi Aplikasi Seluler' dan pilih 'Tambahkan Perangkat' untuk mendapatkan PIN." - }, - "description": "Masukkan informasi PlayStation 4 Anda. Untuk Kode PIN, buka 'Pengaturan' di konsol PlayStation 4 Anda. Kemudian buka 'Pengaturan Koneksi Aplikasi Seluler' dan pilih 'Tambah Perangkat'. Masukkan Kode PIN yang ditampilkan. Lihat [dokumentasi] (https://www.home-assistant.io/components/ps4/) untuk info lebih lanjut.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Kosongkan jika memilih penemuan otomatis." - }, - "description": "Pilih mode untuk konfigurasi. Alamat IP dapat dikosongkan jika memilih Penemuan Otomatis karena perangkat akan ditemukan secara otomatis.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/it.json b/homeassistant/components/ps4/translations/it.json index a8a97ad1144..aec6d943d4c 100644 --- a/homeassistant/components/ps4/translations/it.json +++ b/homeassistant/components/ps4/translations/it.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Credenziali necessarie. Premi 'Invia' e poi, nell'applicazione PS4 Second Screen, aggiorna i dispositivi e seleziona il dispositivo 'Home-Assistant' per continuare.", - "title": "PlayStation 4" + "description": "Credenziali necessarie. Premi 'Invia' e poi, nell'applicazione PS4 Second Screen, aggiorna i dispositivi e seleziona il dispositivo 'Home-Assistant' per continuare." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Vai a 'Impostazioni' sulla tua console PlayStation 4. Quindi vai su 'Impostazioni connessione app mobili' e seleziona 'Aggiungi dispositivo' per ottenere il pin." - }, - "description": "Inserisci le informazioni per la tua PlayStation 4. Per il codice PIN, vai a \"Impostazioni\" sulla PlayStation 4. Quindi vai a 'Impostazioni di connessione Mobile App' e seleziona 'Aggiungi dispositivo'. Immettere il codice PIN visualizzato. Fai riferimento alla [documentazione](https://www.home-assistant.io/components/ps4/) per ulteriori informazioni.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Lascia vuoto se selezioni il rilevamento automatico." - }, - "description": "Seleziona la modalit\u00e0 per la configurazione. Il campo per l'indirizzo IP pu\u00f2 essere lasciato vuoto se si seleziona il rilevamento automatico, poich\u00e9 i dispositivi saranno rilevati automaticamente.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/ja.json b/homeassistant/components/ps4/translations/ja.json index 40ac55264a9..47d32431b54 100644 --- a/homeassistant/components/ps4/translations/ja.json +++ b/homeassistant/components/ps4/translations/ja.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\u8a8d\u8a3c\u60c5\u5831\u304c\u5fc5\u8981\u3067\u3059\u3002'\u9001\u4fe1(submit)' \u3092\u62bc\u3057\u3066\u3001PS4\u306e2nd Screen App\u3067\u30c7\u30d0\u30a4\u30b9\u3092\u66f4\u65b0\u3057\u3001'Home-Assistant' \u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u3066\u7d9a\u884c\u3057\u307e\u3059\u3002", - "title": "Play Station 4" + "description": "\u8a8d\u8a3c\u60c5\u5831\u304c\u5fc5\u8981\u3067\u3059\u3002'\u9001\u4fe1(submit)' \u3092\u62bc\u3057\u3066\u3001PS4\u306e2nd Screen App\u3067\u30c7\u30d0\u30a4\u30b9\u3092\u66f4\u65b0\u3057\u3001'Home-Assistant' \u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u3066\u7d9a\u884c\u3057\u307e\u3059\u3002" }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "PlayStation 4\u672c\u4f53\u306e' \u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u307e\u3059\u3002\u6b21\u306b\u3001' \u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u63a5\u7d9a\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u3001' \u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0' \u3092\u9078\u629e\u3057\u3066\u3001\u30d4\u30f3\u3092\u53d6\u5f97\u3057\u307e\u3059\u3002" - }, - "description": "PlayStation4\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002 PIN\u30b3\u30fc\u30c9(PIN CodePIN Code)\u306e\u5834\u5408\u306f\u3001PlayStation4\u672c\u4f53\u306e '\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u307e\u3059\u3002\u6b21\u306b\u3001'\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u63a5\u7d9a\u8a2d\u5b9a' \u306b\u79fb\u52d5\u3057\u3066\u3001'\u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0' \u3092\u9078\u629e\u3057\u307e\u3059\u3002\u8868\u793a\u3055\u308c\u305f PIN\u30b3\u30fc\u30c9(PIN CodePIN Code) \u3092\u5165\u529b\u3057\u307e\u3059\u3002\u8a73\u7d30\u306f\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/components/ps4/) \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Play Station 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "\u81ea\u52d5\u691c\u51fa\u3092\u9078\u629e\u3059\u308b\u5834\u5408\u306f\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u307e\u3059\u3002" - }, - "description": "\u30b3\u30f3\u30d5\u30a3\u30ae\u30e5\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30e2\u30fc\u30c9\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u81ea\u52d5\u691c\u51fa\u3092\u9078\u629e\u3059\u308b\u3068\u3001IP\u30a2\u30c9\u30ec\u30b9\u306e\u6b04\u304c\u7a7a\u767d\u306e\u307e\u307e\u3067\u3082\u30c7\u30d0\u30a4\u30b9\u306f\u81ea\u52d5\u7684\u306b\u691c\u51fa\u3055\u308c\u307e\u3059\u3002", - "title": "Play Station 4" + } } } } diff --git a/homeassistant/components/ps4/translations/ko.json b/homeassistant/components/ps4/translations/ko.json index 129927a9bab..b720db8241b 100644 --- a/homeassistant/components/ps4/translations/ko.json +++ b/homeassistant/components/ps4/translations/ko.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\uc790\uaca9 \uc99d\uba85\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. '\ud655\uc778'\uc744 \ud074\ub9ad\ud55c \ub2e4\uc74c PS4 \uc138\ucee8\ub4dc \uc2a4\ud06c\ub9b0 \uc571\uc5d0\uc11c \uae30\uae30\ub97c \uc0c8\ub85c\uace0\uce68 \ud558\uace0 'Home-Assistant' \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", - "title": "PlayStation 4" + "description": "\uc790\uaca9 \uc99d\uba85\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. '\ud655\uc778'\uc744 \ud074\ub9ad\ud55c \ub2e4\uc74c PS4 \uc138\ucee8\ub4dc \uc2a4\ud06c\ub9b0 \uc571\uc5d0\uc11c \uae30\uae30\ub97c \uc0c8\ub85c\uace0\uce68 \ud558\uace0 'Home-Assistant' \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694." }, "link": { "data": { @@ -24,17 +23,13 @@ "ip_address": "IP \uc8fc\uc18c", "name": "\uc774\ub984", "region": "\uc9c0\uc5ed" - }, - "description": "PlayStation 4 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. PIN \ucf54\ub4dc\ub97c \ud655\uc778\ud558\ub824\uba74, PlayStation 4 \ucf58\uc194\uc5d0\uc11c '\uc124\uc815'\uc73c\ub85c \uc774\ub3d9\ud55c \ub4a4 '\ubaa8\ubc14\uc77c \uc571 \uc811\uc18d \uc124\uc815'\uc73c\ub85c \uc774\ub3d9\ud558\uc5ec '\uae30\uae30 \ub4f1\ub85d\ud558\uae30'\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694. \ud654\uba74\uc5d0 \ud45c\uc2dc\ub41c PIN \ucf54\ub4dc(\uc744)\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. \ucd94\uac00 \uc815\ubcf4\ub294 [\uad00\ub828 \ubb38\uc11c](https://www.home-assistant.io/components/ps4/)\ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP \uc8fc\uc18c (\uc790\ub3d9 \uac80\uc0c9\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ube44\uc6cc\ub450\uc138\uc694)", "mode": "\uad6c\uc131 \ubaa8\ub4dc" - }, - "description": "\uad6c\uc131\ud560 \ubaa8\ub4dc\ub97c \uc120\ud0dd\ud569\ub2c8\ub2e4. \uc790\ub3d9 \uac80\uc0c9\uc744 \uc120\ud0dd\ud558\uba74 \uae30\uae30\uac00 \uc790\ub3d9\uc73c\ub85c \uac80\uc0c9\ub418\ubbc0\ub85c IP \uc8fc\uc18c \ud544\ub4dc\ub294 \ube44\uc6cc\ub450\uc154\ub3c4 \ub429\ub2c8\ub2e4.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/lb.json b/homeassistant/components/ps4/translations/lb.json index 444de75469c..b093b2bdd8e 100644 --- a/homeassistant/components/ps4/translations/lb.json +++ b/homeassistant/components/ps4/translations/lb.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Umeldungsinformatioun sinn n\u00e9ideg. Dr\u00e9ckt op 'Ofsch\u00e9cken' , dann an der PS4 App, 2ten Ecran, erneiert Apparater an wielt den Home-Assistant Apparat aus fir weider ze fueren.", - "title": "PlayStation 4" + "description": "Umeldungsinformatioun sinn n\u00e9ideg. Dr\u00e9ckt op 'Ofsch\u00e9cken' , dann an der PS4 App, 2ten Ecran, erneiert Apparater an wielt den Home-Assistant Apparat aus fir weider ze fueren." }, "link": { "data": { @@ -24,17 +23,13 @@ "ip_address": "IP Adresse", "name": "Numm", "region": "Regioun" - }, - "description": "G\u00ebff deng Playstation 4 Informatiounen an. Fir 'PIN Code', g\u00e9i an d'Astellunge vun der Playstation 4 Konsole. Dann op 'Mobile App Verbindungs Astellungen' a wiel \"Apparat dob\u00e4isetzen' aus. G\u00ebff de PIN Code an deen ugewise g\u00ebtt. Lies [Dokumentatioun](https://www.home-assistant.io/components/ps4/) fir w\u00e9ider Informatiounen.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP Address (Eidel loossen falls Auto Discovery benotzt g\u00ebtt)", "mode": "Konfiguratioun's Modus" - }, - "description": "Konfiguratioun's Modus auswielen. D'Feld IP Adress kann eidel bl\u00e9iwen wann Auto Discovery benotzt g\u00ebtt, well d'Apparaten automatesch entdeckt ginn.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/nl.json b/homeassistant/components/ps4/translations/nl.json index 1a6ddd26f7f..8bebf95d0f0 100644 --- a/homeassistant/components/ps4/translations/nl.json +++ b/homeassistant/components/ps4/translations/nl.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Aanmeldingsgegevens zijn nodig. Druk op 'Verzenden' en vervolgens in de PS4-app voor het 2e scherm, vernieuw apparaten en selecteer Home Assistant om door te gaan.", - "title": "PlayStation 4" + "description": "Aanmeldingsgegevens zijn nodig. Druk op 'Verzenden' en vervolgens in de PS4-app voor het 2e scherm, vernieuw apparaten en selecteer Home Assistant om door te gaan." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navigeer naar 'Instellingen' op je PlayStation 4-systeem. Navigeer vervolgens naar 'Instellingen mobiele app-verbinding' en selecteer 'Apparaat toevoegen' om de pin te krijgen." - }, - "description": "Voer je PlayStation 4-informatie in. Voor PIN-code navigeer je naar 'Instellingen' op je PlayStation 4-console. Ga vervolgens naar 'Verbindingsinstellingen mobiele app' en selecteer 'Apparaat toevoegen'. Voer de PIN-code in die wordt weergegeven. Raadpleeg de [documentatie](https://www.home-assistant.io/components/ps4/) voor meer informatie.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Leeg laten indien u auto-discovery selecteert." - }, - "description": "Selecteer modus voor configuratie. Het veld IP-adres kan leeg worden gelaten als Auto Discovery wordt geselecteerd, omdat apparaten dan automatisch worden ontdekt.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/nn.json b/homeassistant/components/ps4/translations/nn.json index a9b44db3731..31a3808910a 100644 --- a/homeassistant/components/ps4/translations/nn.json +++ b/homeassistant/components/ps4/translations/nn.json @@ -5,18 +5,11 @@ "port_997_bind_error": "Kunne ikkje binde til port 997. Sj\u00e5 [dokumentasjonen] (https://www.home-assistant.io/components/ps4/) for ytterlegare informasjon." }, "step": { - "creds": { - "title": "Playstation 4" - }, "link": { "data": { "code": "PIN", "name": "Namn" - }, - "title": "Playstation 4" - }, - "mode": { - "title": "Playstation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/no.json b/homeassistant/components/ps4/translations/no.json index 69332862a33..f2d61d0955f 100644 --- a/homeassistant/components/ps4/translations/no.json +++ b/homeassistant/components/ps4/translations/no.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Legitimasjon n\u00f8dvendig. Trykk 'Send' og deretter i PS4-ens andre skjerm app, kan du oppdatere enheter, og velg 'Home-Assistant' enheten for \u00e5 fortsette.", - "title": "" + "description": "Legitimasjon n\u00f8dvendig. Trykk 'Send' og deretter i PS4-ens andre skjerm app, kan du oppdatere enheter, og velg 'Home-Assistant' enheten for \u00e5 fortsette." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Naviger til \"Innstillinger\" p\u00e5 PlayStation 4-konsollen. Naviger deretter til \"Mobile App Connection Settings\" og velg \"Add Device\" for \u00e5 hente pinnen." - }, - "description": "Skriv inn PlayStation 4-informasjonen din. For PIN kode , naviger til 'Innstillinger' p\u00e5 PlayStation 4-konsollen. Naviger deretter til 'Innstillinger for tilkobling av mobilapp' og velg 'Legg til enhet'. Skriv inn PIN kode som vises. Se [dokumentasjon] (https://www.home-assistant.io/components/ps4/) for mer informasjon.", - "title": "" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "La st\u00e5 tomt hvis du velger automatisk oppdagelse." - }, - "description": "Velg modus for konfigurasjon. Feltet IP adresse kan st\u00e5 tomt hvis du velger automatisk oppdagelse, da enheter automatisk blir oppdaget.", - "title": "" + } } } } diff --git a/homeassistant/components/ps4/translations/pl.json b/homeassistant/components/ps4/translations/pl.json index af4d36b9bf2..0ebb2fa9267 100644 --- a/homeassistant/components/ps4/translations/pl.json +++ b/homeassistant/components/ps4/translations/pl.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Wymagane s\u0105 po\u015bwiadczenia. Naci\u015bnij przycisk \"Zatwierd\u017a\", a nast\u0119pnie w aplikacji PS4 Second Screen, od\u015bwie\u017c urz\u0105dzenia i wybierz urz\u0105dzenie 'Home-Assistant', aby kontynuowa\u0107.", - "title": "PlayStation 4" + "description": "Wymagane s\u0105 po\u015bwiadczenia. Naci\u015bnij przycisk \"Zatwierd\u017a\", a nast\u0119pnie w aplikacji PS4 Second Screen, od\u015bwie\u017c urz\u0105dzenia i wybierz urz\u0105dzenie 'Home-Assistant', aby kontynuowa\u0107." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Przejd\u017a do \u201eUstawienia\u201d na konsoli PlayStation 4. Nast\u0119pnie przejd\u017a do \u201eUstawienia po\u0142\u0105czenia aplikacji mobilnej\u201d i wybierz \u201eDodaj urz\u0105dzenie\u201d, aby uzyska\u0107 kod PIN." - }, - "description": "Wprowad\u017a informacje o PlayStation 4. Aby uzyska\u0107 'PIN', przejd\u017a do 'Ustawienia' na konsoli PlayStation 4. Nast\u0119pnie przejd\u017a do 'Ustawienia po\u0142\u0105czenia aplikacji mobilnej' i wybierz 'Dodaj urz\u0105dzenie'. Wprowad\u017a wy\u015bwietlony kod PIN. Zapoznaj si\u0119 z [dokumentacj\u0105](https://www.home-assistant.io/components/ps4/), by pozna\u0107 szczeg\u00f3\u0142y.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Pozostaw puste, je\u015bli wybierasz automatyczne wykrywanie." - }, - "description": "Wybierz tryb konfiguracji. Pole adresu IP mo\u017cna pozostawi\u0107 puste, je\u015bli wybierzesz opcj\u0119 Auto Discovery, poniewa\u017c urz\u0105dzenia zostan\u0105 automatycznie wykryte.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/pt-BR.json b/homeassistant/components/ps4/translations/pt-BR.json index 12d54264f78..f7731cc7ee8 100644 --- a/homeassistant/components/ps4/translations/pt-BR.json +++ b/homeassistant/components/ps4/translations/pt-BR.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Credenciais necess\u00e1rias. Pressione 'Enviar' e, em seguida, no PS4, na Tela do segundo aplicativo, atualize os dispositivos e selecione o dispositivo 'Home-Assistant' para continuar.", - "title": "Playstation 4" + "description": "Credenciais necess\u00e1rias. Pressione 'Enviar' e, em seguida, no PS4, na Tela do segundo aplicativo, atualize os dispositivos e selecione o dispositivo 'Home-Assistant' para continuar." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "Navegue at\u00e9 'Configura\u00e7\u00f5es' em seu console PlayStation 4. Em seguida, navegue at\u00e9 'Configura\u00e7\u00f5es de conex\u00e3o do aplicativo m\u00f3vel' e selecione 'Adicionar dispositivo' para obter o PIN." - }, - "description": "Digite suas informa\u00e7\u00f5es do PlayStation 4. Para C\u00f3digo PIN, navegue at\u00e9 'Configura\u00e7\u00f5es' no seu console PlayStation 4. Em seguida, navegue at\u00e9 \"Configura\u00e7\u00f5es de conex\u00e3o de aplicativos m\u00f3veis\" e selecione \"Adicionar dispositivo\". Digite o C\u00f3digo PIN exibido. Consulte a [documenta\u00e7\u00e3o] (https://www.home-assistant.io/components/ps4/) para informa\u00e7\u00f5es adicionais.", - "title": "Playstation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Deixe em branco se selecionar a descoberta autom\u00e1tica." - }, - "description": "Selecione o modo para configura\u00e7\u00e3o. O campo Endere\u00e7o IP pode ser deixado em branco se selecionar Detec\u00e7\u00e3o Autom\u00e1tica, pois os dispositivos ser\u00e3o descobertos automaticamente.", - "title": "Playstation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/pt.json b/homeassistant/components/ps4/translations/pt.json index 5956937ac2f..cf428838b0b 100644 --- a/homeassistant/components/ps4/translations/pt.json +++ b/homeassistant/components/ps4/translations/pt.json @@ -13,23 +13,18 @@ "login_failed": "Falha ao emparelhar com a PlayStation 4. Verifique se o PIN est\u00e1 correto." }, "step": { - "creds": { - "title": "PlayStation 4" - }, "link": { "data": { "code": "PIN", "ip_address": "Endere\u00e7o de IP", "name": "Nome", "region": "Regi\u00e3o" - }, - "title": "PlayStation 4" + } }, "mode": { "data": { "mode": "Modo de configura\u00e7\u00e3o" - }, - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/ru.json b/homeassistant/components/ps4/translations/ru.json index 646346095b0..2b50ad57490 100644 --- a/homeassistant/components/ps4/translations/ru.json +++ b/homeassistant/components/ps4/translations/ru.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c**, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 'PS4 Second Screen' \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e 'Home-Assistant'.", - "title": "PlayStation 4" + "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 **\u041f\u041e\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u042c**, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 'PS4 Second Screen' \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e 'Home-Assistant'." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**." - }, - "description": "\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f PIN-\u043a\u043e\u0434\u0430 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043a \u043f\u0443\u043d\u043a\u0442\u0443 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 PlayStation 4. \u0417\u0430\u0442\u0435\u043c \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 **\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f** \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 **\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e**. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](https://www.home-assistant.io/components/ps4/) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u041f\u043e\u043b\u0435 'IP-\u0430\u0434\u0440\u0435\u0441' \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c, \u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043e 'Auto Discovery', \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/sl.json b/homeassistant/components/ps4/translations/sl.json index 1d5752eb326..5e056d76687 100644 --- a/homeassistant/components/ps4/translations/sl.json +++ b/homeassistant/components/ps4/translations/sl.json @@ -13,8 +13,7 @@ }, "step": { "creds": { - "description": "Potrebne so poverilnice. Pritisnite 'Po\u0161lji' in nato v aplikaciji PS4 2nd Screen App, osve\u017eite naprave in izberite napravo 'Home-Assistant' za nadaljevanje.", - "title": "PlayStation 4" + "description": "Potrebne so poverilnice. Pritisnite 'Po\u0161lji' in nato v aplikaciji PS4 2nd Screen App, osve\u017eite naprave in izberite napravo 'Home-Assistant' za nadaljevanje." }, "link": { "data": { @@ -22,17 +21,13 @@ "ip_address": "IP naslov", "name": "Ime", "region": "Regija" - }, - "description": "Vnesite va\u0161e PlayStation 4 podatke. Za 'PIN' pojdite na 'Nastavitve' na konzoli PlayStation 4. Nato se pomaknite do mo\u017enosti \u00bbNastavitve povezave z mobilno aplikacijo\u00ab in izberite \u00bbDodaj napravo\u00ab. Vnesite prikazano kodo PIN. Dodatne informacije najdete v [dokumentaciji] (https://www.home-assistant.io/components/ps4/).", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "Naslov IP (Pustite prazno, \u010de uporabljate samodejno odkrivanje).", "mode": "Na\u010din konfiguracije" - }, - "description": "Izberite na\u010din za konfiguracijo. IP-Naslov, polje lahko pustite prazno, \u010de izberete samodejno odkrivanje, saj bodo naprave samodejno odkrite.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/sv.json b/homeassistant/components/ps4/translations/sv.json index ec117fb9b98..7435865c5d0 100644 --- a/homeassistant/components/ps4/translations/sv.json +++ b/homeassistant/components/ps4/translations/sv.json @@ -13,8 +13,7 @@ }, "step": { "creds": { - "description": "Autentiseringsuppgifter beh\u00f6vs. Tryck p\u00e5 'Skicka' och sedan uppdatera enheter i appen \"PS4 Second Screen\" p\u00e5 din mobiltelefon eller surfplatta och v\u00e4lj 'Home Assistent' enheten att forts\u00e4tta.", - "title": "PlayStation 4" + "description": "Autentiseringsuppgifter beh\u00f6vs. Tryck p\u00e5 'Skicka' och sedan uppdatera enheter i appen \"PS4 Second Screen\" p\u00e5 din mobiltelefon eller surfplatta och v\u00e4lj 'Home Assistent' enheten att forts\u00e4tta." }, "link": { "data": { @@ -22,17 +21,13 @@ "ip_address": "IP-adress", "name": "Namn", "region": "Region" - }, - "description": "Ange din PlayStation 4 information. F\u00f6r 'PIN', navigera till 'Inst\u00e4llningar' p\u00e5 din PlayStation 4 konsol. Navigera sedan till \"Inst\u00e4llningar f\u00f6r mobilappanslutning\" och v\u00e4lj \"L\u00e4gg till enhet\". Ange PIN-koden som visas.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP-adress (l\u00e4mna tom om du anv\u00e4nder automatisk uppt\u00e4ckt).", "mode": "Konfigureringsl\u00e4ge" - }, - "description": "V\u00e4lj l\u00e4ge f\u00f6r konfigurering. F\u00e4ltet IP-adress kan l\u00e4mnas tomt om du v\u00e4ljer Automatisk uppt\u00e4ckt, eftersom enheter d\u00e5 kommer att identifieras automatiskt.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/th.json b/homeassistant/components/ps4/translations/th.json index 2ffe2fb805a..bf910763725 100644 --- a/homeassistant/components/ps4/translations/th.json +++ b/homeassistant/components/ps4/translations/th.json @@ -1,20 +1,13 @@ { "config": { "step": { - "creds": { - "title": "PlayStation 4" - }, "link": { "data": { "code": "PIN", "ip_address": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48 IP", "name": "\u0e0a\u0e37\u0e48\u0e2d", "region": "\u0e20\u0e39\u0e21\u0e34\u0e20\u0e32\u0e04" - }, - "title": "PlayStation 4" - }, - "mode": { - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/tr.json b/homeassistant/components/ps4/translations/tr.json index a2e8149db7d..09cd66d6826 100644 --- a/homeassistant/components/ps4/translations/tr.json +++ b/homeassistant/components/ps4/translations/tr.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "Kimlik bilgileri gerekli. \"G\u00f6nder\"e bas\u0131n ve ard\u0131ndan PS4 2. Ekran Uygulamas\u0131nda cihazlar\u0131 yenileyin ve devam etmek i\u00e7in \"Home-Asistan\" cihaz\u0131n\u0131 se\u00e7in.", - "title": "PlayStation 4" + "description": "Kimlik bilgileri gerekli. \"G\u00f6nder\"e bas\u0131n ve ard\u0131ndan PS4 2. Ekran Uygulamas\u0131nda cihazlar\u0131 yenileyin ve devam etmek i\u00e7in \"Home-Asistan\" cihaz\u0131n\u0131 se\u00e7in." }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "PlayStation 4 konsolunuzda 'Ayarlar'a gidin. Ard\u0131ndan, \"Mobil Uygulama Ba\u011flant\u0131 Ayarlar\u0131\"na gidin ve PIN'i almak i\u00e7in \"Cihaz Ekle\"yi se\u00e7in." - }, - "description": "PlayStation 4 bilgilerinizi girin. PIN Kodu i\u00e7in PlayStation 4 konsolunuzda 'Ayarlar'a gidin. Ard\u0131ndan \"Mobil Uygulama Ba\u011flant\u0131 Ayarlar\u0131\"na gidin ve \"Cihaz Ekle\"yi se\u00e7in. PIN Kodu kodunu girin. Ek bilgi i\u00e7in [belgelere](https://www.home-assistant.io/components/ps4/) bak\u0131n.", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "Otomatik bulma se\u00e7iliyorsa bo\u015f b\u0131rak\u0131n." - }, - "description": "Yap\u0131land\u0131rma i\u00e7in modu se\u00e7in. IP Adresi alan\u0131, Otomatik Ke\u015fif se\u00e7ildi\u011finde cihazlar otomatik olarak ke\u015ffedilece\u011finden bo\u015f b\u0131rak\u0131labilir.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/uk.json b/homeassistant/components/ps4/translations/uk.json index 696a46bf8d7..bf6716649c0 100644 --- a/homeassistant/components/ps4/translations/uk.json +++ b/homeassistant/components/ps4/translations/uk.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ** \u041f\u0406\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u0418 **, \u0430 \u043f\u043e\u0442\u0456\u043c \u0432 \u0434\u043e\u0434\u0430\u0442\u043a\u0443 'PS4 Second Screen' \u043e\u043d\u043e\u0432\u0456\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432 \u0456 \u0432\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 'Home-Assistant'.", - "title": "PlayStation 4" + "description": "\u041d\u0430\u0442\u0438\u0441\u043d\u0456\u0442\u044c ** \u041f\u0406\u0414\u0422\u0412\u0415\u0420\u0414\u0418\u0422\u0418 **, \u0430 \u043f\u043e\u0442\u0456\u043c \u0432 \u0434\u043e\u0434\u0430\u0442\u043a\u0443 'PS4 Second Screen' \u043e\u043d\u043e\u0432\u0456\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432 \u0456 \u0432\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 'Home-Assistant'." }, "link": { "data": { @@ -24,17 +23,13 @@ "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441\u0430", "name": "\u041d\u0430\u0437\u0432\u0430", "region": "\u0420\u0435\u0433\u0456\u043e\u043d" - }, - "description": "\u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f PIN-\u043a\u043e\u0434\u0443 \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u0434\u043e \u043f\u0443\u043d\u043a\u0442\u0443 ** \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f ** \u043d\u0430 \u043a\u043e\u043d\u0441\u043e\u043b\u0456 PlayStation 4. \u041f\u043e\u0442\u0456\u043c \u0432\u0456\u0434\u043a\u0440\u0438\u0439\u0442\u0435 ** \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u043c\u043e\u0431\u0456\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u043e\u0434\u0430\u0442\u043a\u0430 ** \u0456 \u0432\u0438\u0431\u0435\u0440\u0456\u0442\u044c ** \u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 ** . \u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 [\u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438](https://www.home-assistant.io/components/ps4/) \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0431\u0456\u043b\u044c\u0448 \u0434\u043e\u043a\u043b\u0430\u0434\u043d\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457.", - "title": "PlayStation 4" + } }, "mode": { "data": { "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441\u0430 (\u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c \u043f\u0440\u0438 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u0430\u043d\u043d\u0456 \u0440\u0435\u0436\u0438\u043c\u0443 \u0430\u0432\u0442\u043e\u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f)", "mode": "\u0420\u0435\u0436\u0438\u043c" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0438\u043f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f. \u041f\u043e\u043b\u0435 'IP-\u0430\u0434\u0440\u0435\u0441\u0430' \u043c\u043e\u0436\u043d\u0430 \u0437\u0430\u043b\u0438\u0448\u0438\u0442\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c, \u044f\u043a\u0449\u043e \u0432\u0438\u0431\u0440\u0430\u043d\u043e 'Auto Discovery', \u043e\u0441\u043a\u0456\u043b\u044c\u043a\u0438 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u0431\u0443\u0434\u0443\u0442\u044c \u0434\u043e\u0434\u0430\u043d\u0456 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e.", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/zh-Hans.json b/homeassistant/components/ps4/translations/zh-Hans.json index 3c240d96131..69f0694f853 100644 --- a/homeassistant/components/ps4/translations/zh-Hans.json +++ b/homeassistant/components/ps4/translations/zh-Hans.json @@ -12,8 +12,7 @@ }, "step": { "creds": { - "description": "\u9700\u8981\u51ed\u636e\u3002\u8bf7\u70b9\u51fb\u201c\u63d0\u4ea4\u201d\u7136\u540e\u5728 PS4 \u7b2c\u4e8c\u5c4f\u5e55\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5237\u65b0\u8bbe\u5907\u5e76\u9009\u62e9\u201cHome-Assistant\u201d\u8bbe\u5907\u4ee5\u7ee7\u7eed\u3002", - "title": "PlayStation 4" + "description": "\u9700\u8981\u51ed\u636e\u3002\u8bf7\u70b9\u51fb\u201c\u63d0\u4ea4\u201d\u7136\u540e\u5728 PS4 \u7b2c\u4e8c\u5c4f\u5e55\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5237\u65b0\u8bbe\u5907\u5e76\u9009\u62e9\u201cHome-Assistant\u201d\u8bbe\u5907\u4ee5\u7ee7\u7eed\u3002" }, "link": { "data": { @@ -21,15 +20,12 @@ "ip_address": "IP \u5730\u5740", "name": "\u540d\u79f0", "region": "\u5730\u533a" - }, - "description": "\u8f93\u5165\u60a8\u7684 PlayStation 4 \u4fe1\u606f\u3002\u5bf9\u4e8e \"PIN\", \u8bf7\u5bfc\u822a\u5230 PlayStation 4 \u63a7\u5236\u53f0\u4e0a\u7684 \"\u8bbe\u7f6e\"\u3002\u7136\u540e\u5bfc\u822a\u5230 \"\u79fb\u52a8\u5e94\u7528\u8fde\u63a5\u8bbe\u7f6e\", \u7136\u540e\u9009\u62e9 \"\u6dfb\u52a0\u8bbe\u5907\"\u3002\u8f93\u5165\u663e\u793a\u7684 PIN\u3002", - "title": "PlayStation 4" + } }, "mode": { "data": { "mode": "\u8bbe\u7f6e\u6a21\u5f0f" - }, - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/ps4/translations/zh-Hant.json b/homeassistant/components/ps4/translations/zh-Hant.json index ae7e86377d8..231e3ecc5e0 100644 --- a/homeassistant/components/ps4/translations/zh-Hant.json +++ b/homeassistant/components/ps4/translations/zh-Hant.json @@ -15,8 +15,7 @@ }, "step": { "creds": { - "description": "\u9700\u8981\u6191\u8b49\u3002\u6309\u4e0b\u300c\u50b3\u9001\u300d\u5f8c\u3001\u65bc PS4 \u7b2c\u4e8c\u756b\u9762 App\uff0c\u66f4\u65b0\u88dd\u7f6e\u4e26\u9078\u64c7\u300cHome-Assistant\u300d\u4ee5\u7e7c\u7e8c\u3002", - "title": "PlayStation 4" + "description": "\u9700\u8981\u6191\u8b49\u3002\u6309\u4e0b\u300c\u50b3\u9001\u300d\u5f8c\u3001\u65bc PS4 \u7b2c\u4e8c\u756b\u9762 App\uff0c\u66f4\u65b0\u88dd\u7f6e\u4e26\u9078\u64c7\u300cHome-Assistant\u300d\u4ee5\u7e7c\u7e8c\u3002" }, "link": { "data": { @@ -27,9 +26,7 @@ }, "data_description": { "code": "\u5207\u63db\u81f3 PlayStation 4 \u4e3b\u6a5f\u7684\u300c\u8a2d\u5b9a\u300d\u5167\uff0c\u4e26\u65bc\u300c\u884c\u52d5\u7a0b\u5f0f\u9023\u7dda\u8a2d\u5b9a\uff08Mobile App Connection Settings\uff09\u300d\u4e2d\u9078\u64c7\u300c\u65b0\u589e\u88dd\u7f6e\u300d\u4ee5\u53d6\u5f97 PIN \u78bc\u3002" - }, - "description": "\u8f38\u5165\u60a8\u7684 PlayStation 4 \u8cc7\u8a0a\uff0c\u300cPIN \u78bc\u300d\u65bc PlayStation 4 \u4e3b\u6a5f\u7684\u300c\u8a2d\u5b9a\u300d\u5167\uff0c\u4e26\u65bc\u300c\u884c\u52d5\u7a0b\u5f0f\u9023\u7dda\u8a2d\u5b9a\uff08Mobile App Connection Settings\uff09\u300d\u4e2d\u9078\u64c7\u300c\u65b0\u589e\u88dd\u7f6e\u300d\u3002\u8f38\u5165\u6240\u986f\u793a\u7684 PIN \u78bc\u3002\u8acb\u53c3\u8003 [documentation](https://www.home-assistant.io/components/ps4/) \u4ee5\u7372\u5f97\u66f4\u591a\u8cc7\u8a0a\u3002", - "title": "PlayStation 4" + } }, "mode": { "data": { @@ -38,9 +35,7 @@ }, "data_description": { "ip_address": "\u5047\u5982\u9078\u64c7\u4f7f\u7528\u81ea\u52d5\u641c\u7d22\u8acb\u4fdd\u6301\u7a7a\u767d" - }, - "description": "\u9078\u64c7\u6a21\u5f0f\u4ee5\u9032\u884c\u8a2d\u5b9a\u3002\u5047\u5982\u9078\u64c7\u81ea\u52d5\u641c\u7d22\u6a21\u5f0f\u7684\u8a71\uff0c\u7531\u65bc\u6703\u81ea\u52d5\u9032\u884c\u88dd\u7f6e\u641c\u5c0b\uff0cIP \u4f4d\u5740\u53ef\u4fdd\u7559\u70ba\u7a7a\u767d\u3002", - "title": "PlayStation 4" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/ca.json b/homeassistant/components/pvpc_hourly_pricing/translations/ca.json index ef5e00e25c3..b435e12a90a 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/ca.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/ca.json @@ -10,9 +10,7 @@ "power": "Pot\u00e8ncia contractada (kW)", "power_p3": "Pot\u00e8ncia contractada del per\u00edode vall P3 (kW)", "tariff": "Tarifa aplicable per zona geogr\u00e0fica" - }, - "description": "Aquest sensor utilitza l'API oficial per obtenir els [preus per hora de l'electricitat (PVPC)](https://www.esios.ree.es/es/pvpc) a Espanya.\nPer a m\u00e9s informaci\u00f3, consulta la [documentaci\u00f3 de la integraci\u00f3](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configuraci\u00f3 del sensor" + } } } }, @@ -23,9 +21,7 @@ "power": "Pot\u00e8ncia contractada (kW)", "power_p3": "Pot\u00e8ncia contractada del per\u00edode vall P3 (kW)", "tariff": "Tarifa aplicable per zona geogr\u00e0fica" - }, - "description": "Aquest sensor utilitza l'API oficial per obtenir els [preus per hora de l'electricitat (PVPC)](https://www.esios.ree.es/es/pvpc) a Espanya.\nPer a m\u00e9s informaci\u00f3, consulta la [documentaci\u00f3 de la integraci\u00f3](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configuraci\u00f3 del sensor" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/cs.json b/homeassistant/components/pvpc_hourly_pricing/translations/cs.json index 2dc8bc03afa..69d5d867dfa 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/cs.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/cs.json @@ -7,8 +7,7 @@ "user": { "data": { "name": "Jm\u00e9no senzoru" - }, - "title": "V\u00fdb\u011br tarifu" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/de.json b/homeassistant/components/pvpc_hourly_pricing/translations/de.json index 545a3d2cd9f..49f54ee41d8 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/de.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/de.json @@ -10,9 +10,7 @@ "power": "Vertraglich vereinbarte Leistung (kW)", "power_p3": "Vertraglich vereinbarte Leistung f\u00fcr Talperiode P3 (kW)", "tariff": "Geltender Tarif nach geografischer Zone" - }, - "description": "Dieser Sensor verwendet die offizielle API, um [st\u00fcndliche Strompreise (PVPC)] (https://www.esios.ree.es/es/pvpc) in Spanien zu erhalten. Weitere Informationen findest du in den [Integrations-Dokumentation] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensoreinrichtung" + } } } }, @@ -23,9 +21,7 @@ "power": "Vertraglich vereinbarte Leistung (kW)", "power_p3": "Vertraglich vereinbarte Leistung f\u00fcr Talperiode P3 (kW)", "tariff": "Geltender Tarif nach geografischer Zone" - }, - "description": "Dieser Sensor verwendet die offizielle API, um [st\u00fcndliche Strompreise (PVPC)](https://www.esios.ree.es/es/pvpc) in Spanien zu erhalten.\nEine genauere Erkl\u00e4rung findest du in den [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensoreinrichtung" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/el.json b/homeassistant/components/pvpc_hourly_pricing/translations/el.json index af128ba3bb3..4c9d056daf7 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/el.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/el.json @@ -10,9 +10,7 @@ "power": "\u03a3\u03c5\u03bc\u03b2\u03b1\u03c4\u03b9\u03ba\u03ae \u03b9\u03c3\u03c7\u03cd\u03c2 (kW)", "power_p3": "\u03a3\u03c5\u03bc\u03b2\u03b1\u03c4\u03b9\u03ba\u03ae \u03b9\u03c3\u03c7\u03cd\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf \u03ba\u03bf\u03b9\u03bb\u03ac\u03b4\u03b1\u03c2 P3 (kW)", "tariff": "\u0399\u03c3\u03c7\u03cd\u03bf\u03bd \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf \u03b1\u03bd\u03ac \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03ae \u03b6\u03ce\u03bd\u03b7" - }, - "description": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf API \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03b9 [\u03c9\u03c1\u03b9\u03b1\u03af\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03b9\u03ba\u03ae\u03c2 \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1\u03c2 (PVPC)](https://www.esios.ree.es/es/pvpc) \u03c3\u03c4\u03b7\u03bd \u0399\u03c3\u03c0\u03b1\u03bd\u03af\u03b1.\n\u0393\u03b9\u03b1 \u03c0\u03b9\u03bf \u03b1\u03ba\u03c1\u03b9\u03b2\u03b5\u03af\u03c2 \u03b5\u03be\u03b7\u03b3\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b5\u03c0\u03b9\u03c3\u03ba\u03b5\u03c6\u03b8\u03b5\u03af\u03c4\u03b5 \u03c4\u03b1 [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } } } }, @@ -23,9 +21,7 @@ "power": "\u03a3\u03c5\u03bc\u03b2\u03b1\u03c4\u03b9\u03ba\u03ae \u03b9\u03c3\u03c7\u03cd\u03c2 (kW)", "power_p3": "\u03a3\u03c5\u03bc\u03b2\u03b1\u03c4\u03b9\u03ba\u03ae \u03b9\u03c3\u03c7\u03cd\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03b5\u03c1\u03af\u03bf\u03b4\u03bf \u03ba\u03bf\u03b9\u03bb\u03ac\u03b4\u03b1\u03c2 P3 (kW)", "tariff": "\u0399\u03c3\u03c7\u03cd\u03bf\u03bd \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b9\u03bf \u03b1\u03bd\u03ac \u03b3\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03ae \u03b6\u03ce\u03bd\u03b7" - }, - "description": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03bf \u03b5\u03c0\u03af\u03c3\u03b7\u03bc\u03bf API \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03b9 [\u03c9\u03c1\u03b9\u03b1\u03af\u03b1 \u03c4\u03b9\u03bc\u03bf\u03bb\u03cc\u03b3\u03b7\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03b7\u03bb\u03b5\u03ba\u03c4\u03c1\u03b9\u03ba\u03ae\u03c2 \u03b5\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b1\u03c2 (PVPC)](https://www.esios.ree.es/es/pvpc) \u03c3\u03c4\u03b7\u03bd \u0399\u03c3\u03c0\u03b1\u03bd\u03af\u03b1.\n\u0393\u03b9\u03b1 \u03c0\u03b9\u03bf \u03b1\u03ba\u03c1\u03b9\u03b2\u03b5\u03af\u03c2 \u03b5\u03be\u03b7\u03b3\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b5\u03c0\u03b9\u03c3\u03ba\u03b5\u03c6\u03b8\u03b5\u03af\u03c4\u03b5 \u03c4\u03b1 [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/en.json b/homeassistant/components/pvpc_hourly_pricing/translations/en.json index 38f45d36ab6..9667d14fd05 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/en.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/en.json @@ -10,9 +10,7 @@ "power": "Contracted power (kW)", "power_p3": "Contracted power for valley period P3 (kW)", "tariff": "Applicable tariff by geographic zone" - }, - "description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensor setup" + } } } }, @@ -23,9 +21,7 @@ "power": "Contracted power (kW)", "power_p3": "Contracted power for valley period P3 (kW)", "tariff": "Applicable tariff by geographic zone" - }, - "description": "This sensor uses official API to get [hourly pricing of electricity (PVPC)](https://www.esios.ree.es/es/pvpc) in Spain.\nFor more precise explanation visit the [integration docs](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensor setup" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/es-419.json b/homeassistant/components/pvpc_hourly_pricing/translations/es-419.json index ed6ab16c8b5..a07120f5147 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/es-419.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/es-419.json @@ -8,9 +8,7 @@ "data": { "name": "Nombre del sensor", "tariff": "Tarifa contratada (1, 2 o 3 per\u00edodos)" - }, - "description": "Este sensor utiliza la API oficial para obtener [precios por hora de la electricidad (PVPC)] (https://www.esios.ree.es/es/pvpc) en Espa\u00f1a. \n Para obtener una explicaci\u00f3n m\u00e1s precisa, visite los [documentos de integraci\u00f3n] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\nSeleccione la tarifa contratada en funci\u00f3n de la cantidad de per\u00edodos de facturaci\u00f3n por d\u00eda: \n - 1 per\u00edodo: normal \n - 2 per\u00edodos: discriminaci\u00f3n (tarifa nocturna) \n - 3 per\u00edodos: coche el\u00e9ctrico (tarifa nocturna de 3 per\u00edodos)", - "title": "Selecci\u00f3n de tarifa" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/es.json b/homeassistant/components/pvpc_hourly_pricing/translations/es.json index ec5634a160e..eb96606831c 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/es.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/es.json @@ -10,9 +10,7 @@ "power": "Potencia contratada (kW)", "power_p3": "Potencia contratada para el per\u00edodo valle P3 (kW)", "tariff": "Tarifa aplicable por zona geogr\u00e1fica" - }, - "description": "Este sensor utiliza la API oficial para obtener [el precio horario de la electricidad (PVPC)](https://www.esios.ree.es/es/pvpc) en Espa\u00f1a.\nPara obtener una explicaci\u00f3n m\u00e1s precisa, visita los [documentos de la integraci\u00f3n](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).\n\nSelecciona la tarifa contratada en funci\u00f3n del n\u00famero de per\u00edodos de facturaci\u00f3n por d\u00eda:\n- 1 per\u00edodo: normal\n- 2 per\u00edodos: discriminaci\u00f3n (tarifa nocturna)\n- 3 per\u00edodos: coche el\u00e9ctrico (tarifa nocturna de 3 per\u00edodos)", - "title": "Configuraci\u00f3n del sensor" + } } } }, @@ -23,9 +21,7 @@ "power": "Potencia contratada (kW)", "power_p3": "Potencia contratada para el per\u00edodo valle P3 (kW)", "tariff": "Tarifa aplicable por zona geogr\u00e1fica" - }, - "description": "Este sensor utiliza la API oficial para obtener el [precio horario de la electricidad (PVPC)](https://www.esios.ree.es/es/pvpc) en Espa\u00f1a.\nPara una explicaci\u00f3n m\u00e1s precisa visita los [documentos de integraci\u00f3n](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configuraci\u00f3n del sensor" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/et.json b/homeassistant/components/pvpc_hourly_pricing/translations/et.json index 5db4f9ec04b..8554ca18196 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/et.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/et.json @@ -10,9 +10,7 @@ "power": "Lepinguj\u00e4rgne v\u00f5imsus (kW)", "power_p3": "Lepinguj\u00e4rgne v\u00f5imsus soodusperioodil P3 (kW)", "tariff": "Kohaldatav tariif geograafilise tsooni j\u00e4rgi" - }, - "description": "See andur kasutab ametlikku API-d, et saada [elektri tunnihinda (PVPC)](https://www.esios.ree.es/es/pvpc) Hispaanias.\nT\u00e4psema selgituse saamiseks k\u00fclasta [integratsioonidokumente](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Anduri seadistamine" + } } } }, @@ -23,9 +21,7 @@ "power": "Lepinguj\u00e4rgne v\u00f5imsus (kW)", "power_p3": "Lepinguj\u00e4rgne v\u00f5imsus soodusperioodil P3 (kW)", "tariff": "Kohaldatav tariif geograafilise tsooni j\u00e4rgi" - }, - "description": "See andur kasutab ametlikku API-d, et saada [elektri tunnihinda (PVPC)](https://www.esios.ree.es/es/pvpc) Hispaanias.\nT\u00e4psema selgituse saamiseks k\u00fclasta [integratsioonidokumente](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Anduri seadistamine" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/fr.json b/homeassistant/components/pvpc_hourly_pricing/translations/fr.json index afbea0852c4..1f0c447ff0f 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/fr.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/fr.json @@ -10,9 +10,7 @@ "power": "Puissance souscrite (kW)", "power_p3": "Puissance souscrite pour la p\u00e9riode de vall\u00e9e P3 (kW)", "tariff": "Tarif applicable par zone g\u00e9ographique" - }, - "description": "Ce capteur utilise l'API officielle pour obtenir [tarification horaire de l'\u00e9lectricit\u00e9 (PVPC)](https://www.esios.ree.es/es/pvpc) en Espagne.\n Pour des explications plus pr\u00e9cises, visitez les [docs d'int\u00e9gration](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configuration du capteur" + } } } }, @@ -23,9 +21,7 @@ "power": "Puissance souscrite (kW)", "power_p3": "Puissance souscrite pour la p\u00e9riode de vall\u00e9e P3 (kW)", "tariff": "Tarif applicable par zone g\u00e9ographique" - }, - "description": "Ce capteur utilise l'API officielle pour obtenir [tarification horaire de l'\u00e9lectricit\u00e9 (PVPC)](https://www.esios.ree.es/es/pvpc) en Espagne.\n Pour des explications plus pr\u00e9cises, visitez les [docs d'int\u00e9gration](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configuration du capteur" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/he.json b/homeassistant/components/pvpc_hourly_pricing/translations/he.json index 951e9b21b2f..48a6eeeea33 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/he.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/he.json @@ -3,12 +3,5 @@ "abort": { "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" } - }, - "options": { - "step": { - "init": { - "title": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05d7\u05d9\u05d9\u05e9\u05df" - } - } } } \ No newline at end of file diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/hu.json b/homeassistant/components/pvpc_hourly_pricing/translations/hu.json index c654c92969a..121a87af124 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/hu.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/hu.json @@ -10,9 +10,7 @@ "power": "Szerz\u0151d\u00e9s szerinti teljes\u00edtm\u00e9ny (kW)", "power_p3": "Szerz\u0151d\u00f6tt teljes\u00edtm\u00e9ny P3 v\u00f6lgyid\u0151szakra (kW)", "tariff": "Alkalmazand\u00f3 tarifa f\u00f6ldrajzi z\u00f3n\u00e1nk\u00e9nt" - }, - "description": "Ez az \u00e9rz\u00e9kel\u0151 a hivatalos API-t haszn\u00e1lja a [villamos energia \u00f3r\u00e1nk\u00e9nti \u00e1raz\u00e1s\u00e1nak (PVPC)] (https://www.esios.ree.es/es/pvpc) megszerz\u00e9s\u00e9hez Spanyolorsz\u00e1gban.\n Pontosabb magyar\u00e1zat\u00e9rt keresse fel az [integr\u00e1ci\u00f3s dokumentumok] oldalt (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u00c9rz\u00e9kel\u0151 be\u00e1ll\u00edt\u00e1sa" + } } } }, @@ -23,9 +21,7 @@ "power": "Szerz\u0151d\u00e9s szerinti teljes\u00edtm\u00e9ny (kW)", "power_p3": "Szerz\u0151d\u00f6tt teljes\u00edtm\u00e9ny P3 v\u00f6lgyid\u0151szakra (kW)", "tariff": "Alkalmazand\u00f3 tarifa f\u00f6ldrajzi z\u00f3n\u00e1k szerint" - }, - "description": "Ez az \u00e9rz\u00e9kel\u0151 a hivatalos API-t haszn\u00e1lja a [villamos energia \u00f3r\u00e1nk\u00e9nti \u00e1raz\u00e1s\u00e1nak (PVPC)] (https://www.esios.ree.es/es/pvpc) megszerz\u00e9s\u00e9hez Spanyolorsz\u00e1gban.\nPontosabb magyar\u00e1zat\u00e9rt keresse fel az [integr\u00e1ci\u00f3s dokumentumok] oldalt (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u00c9rz\u00e9kel\u0151 be\u00e1ll\u00edt\u00e1sa" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/id.json b/homeassistant/components/pvpc_hourly_pricing/translations/id.json index 5705a2a4fcb..2bc5c67e533 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/id.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/id.json @@ -10,9 +10,7 @@ "power": "Daya terkontrak (kW)", "power_p3": "Daya terkontrak untuk periode lembah P3 (kW)", "tariff": "Tarif yang berlaku menurut zona geografis" - }, - "description": "Sensor ini menggunakan API resmi untuk mendapatkan [harga listrik per jam (PVPC)](https://www.esios.ree.es/es/pvpc) di Spanyol.\nUntuk penjelasan yang lebih tepat, kunjungi [dokumen integrasi](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Penyiapan sensor" + } } } }, @@ -23,9 +21,7 @@ "power": "Daya terkontrak (kW)", "power_p3": "Daya terkontrak untuk periode lembah P3 (kW)", "tariff": "Tarif yang berlaku menurut zona geografis" - }, - "description": "Sensor ini menggunakan API resmi untuk mendapatkan [harga listrik per jam (PVPC)](https://www.esios.ree.es/es/pvpc) di Spanyol.\nUntuk penjelasan yang lebih tepat, kunjungi [dokumen integrasi](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Penyiapan sensor" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/it.json b/homeassistant/components/pvpc_hourly_pricing/translations/it.json index 79e6e627a7e..bd6f8494d49 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/it.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/it.json @@ -10,9 +10,7 @@ "power": "Potenza contrattuale (kW)", "power_p3": "Potenza contrattuale per il periodo di valle P3 (kW)", "tariff": "Tariffa applicabile per zona geografica" - }, - "description": "Questo sensore utilizza l'API ufficiale per ottenere il [prezzo orario dell'elettricit\u00e0 (PVPC)](https://www.esios.ree.es/es/pvpc) in Spagna.\nPer una spiegazione pi\u00f9 precisa, visita i [documenti di integrazione](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configurazione del sensore" + } } } }, @@ -23,9 +21,7 @@ "power": "Potenza contrattuale (kW)", "power_p3": "Potenza contrattuale per il periodo di valle P3 (kW)", "tariff": "Tariffa applicabile per zona geografica" - }, - "description": "Questo sensore utilizza l'API ufficiale per ottenere il [prezzo orario dell'elettricit\u00e0 (PVPC)](https://www.esios.ree.es/es/pvpc) in Spagna.\nPer una spiegazione pi\u00f9 precisa, visita i [documenti di integrazione](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configurazione del sensore" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/ja.json b/homeassistant/components/pvpc_hourly_pricing/translations/ja.json index d88ee379678..6b85de978d3 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/ja.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/ja.json @@ -10,9 +10,7 @@ "power": "\u5951\u7d04\u96fb\u529b (kW)", "power_p3": "\u8c37\u9593(valley period) P3 (kW)\u306e\u5951\u7d04\u96fb\u529b", "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e(Applicable tariff)" - }, - "description": "\u3053\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u516c\u5f0fAPI\u3092\u4f7f\u7528\u3057\u3066\u3001\u30b9\u30da\u30a4\u30f3\u3067\u306e[\u96fb\u6c17\u306e\u6642\u9593\u4fa1\u683c((hourly pricing of electricity)PVPC)](https://www.esios.ree.es/es/pvpc) \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\n\u3088\u308a\u6b63\u78ba\u306a\u8aac\u660e\u306b\u3064\u3044\u3066\u306f\u3001[\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3 \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30bb\u30f3\u30b5\u30fc\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } } } }, @@ -23,9 +21,7 @@ "power": "\u5951\u7d04\u96fb\u529b (kW)", "power_p3": "\u8c37\u9593(valley period) P3 (kW)\u306e\u5951\u7d04\u96fb\u529b", "tariff": "\u5730\u57df\u5225\u9069\u7528\u95a2\u7a0e(Applicable tariff)" - }, - "description": "\u3053\u306e\u30bb\u30f3\u30b5\u30fc\u306f\u3001\u516c\u5f0fAPI\u3092\u4f7f\u7528\u3057\u3066\u3001\u30b9\u30da\u30a4\u30f3\u3067\u306e[\u96fb\u6c17\u306e\u6642\u9593\u4fa1\u683c(hourly pricing of electricity)PVPC)](https://www.esios.ree.es/es/pvpc) \u3092\u53d6\u5f97\u3057\u307e\u3059\u3002\n\u3088\u308a\u6b63\u78ba\u306a\u8aac\u660e\u306b\u3064\u3044\u3066\u306f\u3001[\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3 \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u30bb\u30f3\u30b5\u30fc\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/ko.json b/homeassistant/components/pvpc_hourly_pricing/translations/ko.json index c44d1217961..fe834a94f86 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/ko.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/ko.json @@ -8,9 +8,7 @@ "data": { "name": "\uc13c\uc11c \uc774\ub984", "tariff": "\uacc4\uc57d \uc694\uae08\uc81c (1, 2 \ub610\ub294 3 \uad6c\uac04)" - }, - "description": "\uc774 \uc13c\uc11c\ub294 \uacf5\uc2dd API\ub97c \uc0ac\uc6a9\ud558\uc5ec \uc2a4\ud398\uc778\uc758 [\uc2dc\uac04\ub2f9 \uc804\uae30 \uc694\uae08 (PVPC)](https://www.esios.ree.es/es/pvpc) \uc744 \uac00\uc838\uc635\ub2c8\ub2e4.\n\ubcf4\ub2e4 \uc790\uc138\ud55c \uc124\uba85\uc740 [\uad00\ub828 \ubb38\uc11c](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/)\ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694.\n\n1\uc77c\ub2f9 \uccad\uad6c \uad6c\uac04\uc5d0 \ub530\ub77c \uacc4\uc57d \uc694\uae08\uc81c\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.\n - 1 \uad6c\uac04: \uc77c\ubc18 \uc694\uae08\uc81c\n - 2 \uad6c\uac04: \ucc28\ub4f1 \uc694\uae08\uc81c (\uc57c\uac04 \uc694\uae08) \n - 3 \uad6c\uac04: \uc804\uae30\uc790\ub3d9\ucc28 (3 \uad6c\uac04 \uc57c\uac04 \uc694\uae08)", - "title": "\uc694\uae08\uc81c \uc120\ud0dd" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/lb.json b/homeassistant/components/pvpc_hourly_pricing/translations/lb.json index 78894094b72..d9e6d87437b 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/lb.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/lb.json @@ -8,9 +8,7 @@ "data": { "name": "Numm vum Sensor", "tariff": "Kontraktuellen Tarif (1, 2 oder 3 Perioden)" - }, - "description": "D\u00ebse Sensor benotzt d\u00e9i offiziell API fir de [Stonne Pr\u00e4is fir Elektrizit\u00e9it a Spuenien (PVPC)](https://www.esios.ree.es/es/pvpc) ze kr\u00e9ien. Fir m\u00e9i pr\u00e4zise Erkl\u00e4runge kuck [Dokumentatioun vun der Integratioun](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).\n\nWiel den Taux bas\u00e9ierend op der Unzuel vun de Rechnungsz\u00e4ite pro Dag aus:\n- 1 Period: Normal\n- 2 perioden: Nuets Tarif\n- 3 Perioden: Elektreschen Auto (Nuets Tarif fir 3 Perioden)", - "title": "Auswiel vum Tarif" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/nl.json b/homeassistant/components/pvpc_hourly_pricing/translations/nl.json index b4bc784dedf..9979909e040 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/nl.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/nl.json @@ -10,9 +10,7 @@ "power": "Gecontracteerd vermogen (kW)", "power_p3": "Gecontracteerd vermogen voor dalperiode P3 (kW)", "tariff": "Toepasselijk tarief per geografische zone" - }, - "description": "Deze sensor gebruikt de offici\u00eble API om [uurprijs van elektriciteit (PVPC)](https://www.esios.ree.es/es/pvpc) in Spanje te verkrijgen.\n Ga voor een nauwkeurigere uitleg naar de [integratiedocumenten](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensor instellen" + } } } }, @@ -23,9 +21,7 @@ "power": "Gecontracteerd vermogen (kW)", "power_p3": "Gecontracteerd vermogen voor dalperiode P3 (kW)", "tariff": "Toepasselijk tarief per geografische zone" - }, - "description": "Deze sensor maakt gebruik van offici\u00eble API om [uurprijzen van elektriciteit (PVPC)](https://www.esios.ree.es/es/pvpc) in Spanje te krijgen.\nGa voor een preciezere uitleg naar de [integratiedocumenten](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Sensor setup" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/no.json b/homeassistant/components/pvpc_hourly_pricing/translations/no.json index 7429626674e..77dadcbcffd 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/no.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/no.json @@ -10,9 +10,7 @@ "power": "Kontrahert effekt (kW)", "power_p3": "Kontraktstr\u00f8m for dalperiode P3 (kW)", "tariff": "Gjeldende tariff etter geografisk sone" - }, - "description": "Denne sensoren bruker offisiell API for \u00e5 f\u00e5 [timeprisering av elektrisitet (PVPC)] (https://www.esios.ree.es/es/pvpc) i Spania.\n For mer presis forklaring bes\u00f8k [integrasjonsdokumentene] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Oppsett av sensor" + } } } }, @@ -23,9 +21,7 @@ "power": "Kontrahert effekt (kW)", "power_p3": "Kontrahert kraft for dalperioden P3 (kW)", "tariff": "Gjeldende tariff etter geografisk sone" - }, - "description": "Denne sensoren bruker offisiell API for \u00e5 f\u00e5 [timeprisering av elektrisitet (PVPC)] (https://www.esios.ree.es/es/pvpc) i Spania.\n For mer presis forklaring bes\u00f8k [integrasjonsdokumentene] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Oppsett av sensor" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/pl.json b/homeassistant/components/pvpc_hourly_pricing/translations/pl.json index 052cf66c1a6..52fb03fa985 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/pl.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/pl.json @@ -10,9 +10,7 @@ "power": "Moc zakontraktowana (kW)", "power_p3": "Moc zakontraktowana dla okresu zni\u017ckowego P3 (kW)", "tariff": "Obowi\u0105zuj\u0105ca taryfa wed\u0142ug strefy geograficznej" - }, - "description": "Ten sensor u\u017cywa oficjalnego interfejsu API w celu uzyskania [godzinowej ceny energii elektrycznej (PVPC)] (https://www.esios.ree.es/es/pvpc) w Hiszpanii. \n Aby uzyska\u0107 bardziej szczeg\u00f3\u0142owe wyja\u015bnienia, odwied\u017a [dokumentacj\u0119 dotycz\u0105c\u0105 integracji] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\n Wybierz stawk\u0119 umown\u0105 na podstawie liczby okres\u00f3w rozliczeniowych dziennie: \n - 1 okres: normalny \n - 2 okresy: dyskryminacja (nocna stawka) \n - 3 okresy: samoch\u00f3d elektryczny (stawka nocna za 3 okresy)", - "title": "Konfiguracja sensora" + } } } }, @@ -23,9 +21,7 @@ "power": "Moc zakontraktowana (kW)", "power_p3": "Moc zakontraktowana dla okresu zni\u017ckowego P3 (kW)", "tariff": "Obowi\u0105zuj\u0105ca taryfa wed\u0142ug strefy geograficznej" - }, - "description": "Ten sensor u\u017cywa oficjalnego interfejsu API w celu uzyskania [godzinowej ceny energii elektrycznej (PVPC)] (https://www.esios.ree.es/es/pvpc) w Hiszpanii. \n Aby uzyska\u0107 bardziej szczeg\u00f3\u0142owe wyja\u015bnienia, odwied\u017a [dokumentacj\u0119 dotycz\u0105c\u0105 integracji] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\n Wybierz stawk\u0119 umown\u0105 na podstawie liczby okres\u00f3w rozliczeniowych dziennie: \n - 1 okres: normalny \n - 2 okresy: dyskryminacja (nocna stawka) \n - 3 okresy: samoch\u00f3d elektryczny (stawka nocna za 3 okresy)", - "title": "Konfiguracja sensora" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/pt-BR.json b/homeassistant/components/pvpc_hourly_pricing/translations/pt-BR.json index e5754180a7c..7d66f5af13a 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/pt-BR.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/pt-BR.json @@ -10,9 +10,7 @@ "power": "Pot\u00eancia contratada (kW)", "power_p3": "Pot\u00eancia contratada para o per\u00edodo de vale P3 (kW)", "tariff": "Tarifa aplic\u00e1vel por zona geogr\u00e1fica" - }, - "description": "Esse sensor usa a API oficial para obter [pre\u00e7os por hora de eletricidade (PVPC)](https://www.esios.ree.es/es/pvpc) na Espanha. \nPara uma explica\u00e7\u00e3o mais precisa, visite os [documentos de integra\u00e7\u00e3o](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\nSelecione a taxa contratada com base no n\u00famero de per\u00edodos de cobran\u00e7a por dia: \n- 1 per\u00edodo: normal \n- 2 per\u00edodos: discrimina\u00e7\u00e3o (taxa noturna) \n- 3 per\u00edodos: carro el\u00e9trico (taxa noturna de 3 per\u00edodos)", - "title": "Configura\u00e7\u00e3o do sensor" + } } } }, @@ -23,9 +21,7 @@ "power": "Pot\u00eancia contratada (kW)", "power_p3": "Pot\u00eancia contratada para o per\u00edodo de vale P3 (kW)", "tariff": "Tarifa aplic\u00e1vel por zona geogr\u00e1fica" - }, - "description": "Este sensor usa a API oficial para obter [pre\u00e7os por hora de eletricidade (PVPC)](https://www.esios.ree.es/es/pvpc) na Espanha.\n Para uma explica\u00e7\u00e3o mais precisa, visite os [documentos de integra\u00e7\u00e3o](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "Configura\u00e7\u00e3o do sensor" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/ru.json b/homeassistant/components/pvpc_hourly_pricing/translations/ru.json index e68bc7e6289..7994c217216 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/ru.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/ru.json @@ -10,9 +10,7 @@ "power": "\u0414\u043e\u0433\u043e\u0432\u043e\u0440\u043d\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c (\u043a\u0412\u0442)", "power_p3": "\u0414\u043e\u0433\u043e\u0432\u043e\u0440\u043d\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u043d\u0430 \u043f\u0435\u0440\u0438\u043e\u0434 P3 (\u043a\u0412\u0442)", "tariff": "\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u043e\u043d\u0435" - }, - "description": "\u042d\u0442\u043e\u0442 \u0441\u0435\u043d\u0441\u043e\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f [\u043f\u043e\u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0446\u0435\u043d\u044b \u0437\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u044d\u043d\u0435\u0440\u0433\u0438\u044e (PVPC)](https://www.esios.ree.es/es/pvpc) \u0432 \u0418\u0441\u043f\u0430\u043d\u0438\u0438.\n\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430" + } } } }, @@ -23,9 +21,7 @@ "power": "\u0414\u043e\u0433\u043e\u0432\u043e\u0440\u043d\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c (\u043a\u0412\u0442)", "power_p3": "\u0414\u043e\u0433\u043e\u0432\u043e\u0440\u043d\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u043d\u0430 \u043f\u0435\u0440\u0438\u043e\u0434 P3 (\u043a\u0412\u0442)", "tariff": "\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u043f\u043e \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0437\u043e\u043d\u0435" - }, - "description": "\u042d\u0442\u043e\u0442 \u0441\u0435\u043d\u0441\u043e\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f [\u043f\u043e\u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0446\u0435\u043d\u044b \u0437\u0430 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u044d\u043d\u0435\u0440\u0433\u0438\u044e (PVPC)](https://www.esios.ree.es/es/pvpc) \u0432 \u0418\u0441\u043f\u0430\u043d\u0438\u0438.\n\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/).", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0435\u043d\u0441\u043e\u0440\u0430" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/sl.json b/homeassistant/components/pvpc_hourly_pricing/translations/sl.json index c6765575c07..cc41e12b4b2 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/sl.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/sl.json @@ -8,9 +8,7 @@ "data": { "name": "Ime tipala", "tariff": "Pogodbena tarifa (1, 2 ali 3 obdobja)" - }, - "description": "Ta senzor uporablja uradni API za [urno dolo\u010danje cen elektri\u010dne energije (PVPC)] (https://www.esios.ree.es/es/pvpc) v \u0160paniji. \n Za natan\u010dnej\u0161o razlago obi\u0161\u010dite [integracijski dokumenti] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\n Izberite pogodbeno tarifo glede na \u0161tevilo obra\u010dunskih obdobij na dan: \n - 1 obdobje: normalno \n - 2 obdobji: diskriminacija (no\u010dna cena) \n - 3 obdobja: elektri\u010dni avtomobil (no\u010dna cena 3 obdobja)", - "title": "Izbira tarife" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/tr.json b/homeassistant/components/pvpc_hourly_pricing/translations/tr.json index 5d27e2bb033..908f04f6622 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/tr.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/tr.json @@ -10,9 +10,7 @@ "power": "S\u00f6zle\u015fmeli g\u00fc\u00e7 (kW)", "power_p3": "Vadi d\u00f6nemi i\u00e7in taahh\u00fct edilen g\u00fc\u00e7 P3 (kW)", "tariff": "Co\u011frafi b\u00f6lgeye g\u00f6re ge\u00e7erli tarife" - }, - "description": "Bu sens\u00f6r, \u0130spanya'da [saatlik elektrik fiyatland\u0131rmas\u0131 (PVPC)](https://www.esios.ree.es/es/pvpc) almak i\u00e7in resmi API'yi kullan\u0131r.\n Daha kesin a\u00e7\u0131klama i\u00e7in [entegrasyon belgelerini](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) ziyaret edin.", - "title": "Sens\u00f6r kurulumu" + } } } }, @@ -23,9 +21,7 @@ "power": "S\u00f6zle\u015fmeli g\u00fc\u00e7 (kW)", "power_p3": "Vadi d\u00f6nemi i\u00e7in taahh\u00fct edilen g\u00fc\u00e7 P3 (kW)", "tariff": "Co\u011frafi b\u00f6lgeye g\u00f6re ge\u00e7erli tarife" - }, - "description": "Bu sens\u00f6r, \u0130spanya'da [saatlik elektrik fiyatland\u0131rmas\u0131 (PVPC)](https://www.esios.ree.es/es/pvpc) almak i\u00e7in resmi API'yi kullan\u0131r.\n Daha kesin a\u00e7\u0131klama i\u00e7in [entegrasyon belgelerini](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/) ziyaret edin.", - "title": "Sens\u00f6r kurulumu" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/uk.json b/homeassistant/components/pvpc_hourly_pricing/translations/uk.json index da2136d7765..bc6d06b1b04 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/uk.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/uk.json @@ -8,9 +8,7 @@ "data": { "name": "\u041d\u0430\u0437\u0432\u0430", "tariff": "\u041a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043d\u0438\u0439 \u0442\u0430\u0440\u0438\u0444 (1, 2 \u0430\u0431\u043e 3 \u043f\u0435\u0440\u0456\u043e\u0434\u0438)" - }, - "description": "\u0426\u0435\u0439 \u0441\u0435\u043d\u0441\u043e\u0440 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454 \u043e\u0444\u0456\u0446\u0456\u0439\u043d\u0438\u0439 API \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f [\u043f\u043e\u0433\u043e\u0434\u0438\u043d\u043d\u043e\u0457 \u0446\u0456\u043d\u0438 \u0437\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u0435\u043d\u0435\u0440\u0433\u0456\u044e (PVPC)] (https://www.esios.ree.es/es/pvpc) \u0432 \u0406\u0441\u043f\u0430\u043d\u0456\u0457.\n\u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0431\u0456\u043b\u044c\u0448 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457, \u043e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044c \u0437 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0456\u0454\u044e] (https://www.home-assistant.io/integrations/pvpc_hourly_pricing/). \n\n\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0442\u0430\u0440\u0438\u0444, \u0437\u0430\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0439 \u043d\u0430 \u043a\u0456\u043b\u044c\u043a\u043e\u0441\u0442\u0456 \u0440\u043e\u0437\u0440\u0430\u0445\u0443\u043d\u043a\u043e\u0432\u0438\u0445 \u043f\u0435\u0440\u0456\u043e\u0434\u0456\u0432 \u0432 \u0434\u0435\u043d\u044c:\n- 1 \u043f\u0435\u0440\u0456\u043e\u0434: normal\n- 2 \u043f\u0435\u0440\u0456\u043e\u0434\u0438: discrimination (nightly rate)\n- 3 \u043f\u0435\u0440\u0456\u043e\u0434\u0438: electric car (nightly rate of 3 periods)", - "title": "\u0412\u0438\u0431\u0456\u0440 \u0442\u0430\u0440\u0438\u0444\u0443" + } } } } diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/zh-Hant.json b/homeassistant/components/pvpc_hourly_pricing/translations/zh-Hant.json index ace4cce089a..35ace573ead 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/zh-Hant.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/zh-Hant.json @@ -10,9 +10,7 @@ "power": "\u5408\u7d04\u529f\u7387\uff08kW\uff09", "power_p3": "\u4f4e\u5cf0\u671f P3 \u5408\u7d04\u529f\u7387\uff08kW\uff09", "tariff": "\u5206\u5340\u9069\u7528\u8cbb\u7387" - }, - "description": "\u6b64\u611f\u6e2c\u5668\u4f7f\u7528\u4e86\u975e\u5b98\u65b9 API \u4ee5\u53d6\u5f97\u897f\u73ed\u7259 [\u8a08\u6642\u96fb\u50f9\uff08PVPC\uff09](https://www.esios.ree.es/es/pvpc)\u3002\n\u95dc\u65bc\u66f4\u8a73\u7d30\u7684\u8aaa\u660e\uff0c\u8acb\u53c3\u95b1 [\u6574\u5408\u6587\u4ef6](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/)\u3002", - "title": "\u611f\u61c9\u5668\u8a2d\u5b9a" + } } } }, @@ -23,9 +21,7 @@ "power": "\u5408\u7d04\u529f\u7387\uff08kW\uff09", "power_p3": "\u4f4e\u5cf0\u671f P3 \u5408\u7d04\u529f\u7387\uff08kW\uff09", "tariff": "\u5206\u5340\u9069\u7528\u8cbb\u7387" - }, - "description": "\u6b64\u611f\u6e2c\u5668\u4f7f\u7528\u4e86\u975e\u5b98\u65b9 API \u4ee5\u53d6\u5f97\u897f\u73ed\u7259 [\u8a08\u6642\u96fb\u50f9\uff08PVPC\uff09](https://www.esios.ree.es/es/pvpc)\u3002\n\u95dc\u65bc\u66f4\u8a73\u7d30\u7684\u8aaa\u660e\uff0c\u8acb\u53c3\u95b1 [\u6574\u5408\u6587\u4ef6](https://www.home-assistant.io/integrations/pvpc_hourly_pricing/)\u3002", - "title": "\u611f\u61c9\u5668\u8a2d\u5b9a" + } } } } diff --git a/homeassistant/components/recorder/translations/ko.json b/homeassistant/components/recorder/translations/ko.json index b8cd6320a55..18cc73f3789 100644 --- a/homeassistant/components/recorder/translations/ko.json +++ b/homeassistant/components/recorder/translations/ko.json @@ -1,7 +1,9 @@ { "system_health": { "info": { - "estimated_db_size": "\uc608\uc0c1 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \ud06c\uae30(MiB)" + "current_recorder_run": "\ud604\uc7ac \uc2e4\ud589 \uc2dc\uc791 \uc2dc\uac04", + "estimated_db_size": "\uc608\uc0c1 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \ud06c\uae30(MiB)", + "oldest_recorder_run": "\uac00\uc7a5 \uc624\ub798\ub41c \uc2e4\ud589 \uc2dc\uc791 \uc2dc\uac04" } } } \ No newline at end of file diff --git a/homeassistant/components/remote/translations/ca.json b/homeassistant/components/remote/translations/ca.json index ae9184b8ea5..6bf9dc141e0 100644 --- a/homeassistant/components/remote/translations/ca.json +++ b/homeassistant/components/remote/translations/ca.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} s'enc\u00e9n o s'apaga", - "toggled": "{entity_name} s'activa o es desactiva", "turned_off": "{entity_name} s'ha apagat", "turned_on": "{entity_name} s'ha engegat" } diff --git a/homeassistant/components/remote/translations/cs.json b/homeassistant/components/remote/translations/cs.json index c2afd70cb95..6907021e393 100644 --- a/homeassistant/components/remote/translations/cs.json +++ b/homeassistant/components/remote/translations/cs.json @@ -10,7 +10,6 @@ "is_on": "{entity_name} je zapnuto" }, "trigger_type": { - "toggled": "{entity_name} zapnuto nebo vypnuto", "turned_off": "{entity_name} vypnuto", "turned_on": "{entity_name} zapnuto" } diff --git a/homeassistant/components/remote/translations/de.json b/homeassistant/components/remote/translations/de.json index d0ad3d23ba2..8c34008da15 100644 --- a/homeassistant/components/remote/translations/de.json +++ b/homeassistant/components/remote/translations/de.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ein- oder ausgeschaltet", - "toggled": "{entity_name} ein- oder ausgeschaltet", "turned_off": "{entity_name} ausgeschaltet", "turned_on": "{entity_name} eingeschaltet" } diff --git a/homeassistant/components/remote/translations/el.json b/homeassistant/components/remote/translations/el.json index 431d2e8af79..828f8b7fcb9 100644 --- a/homeassistant/components/remote/translations/el.json +++ b/homeassistant/components/remote/translations/el.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "\u03a4\u03bf {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", - "toggled": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_off": "{entity_name} \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_on": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5" } diff --git a/homeassistant/components/remote/translations/en.json b/homeassistant/components/remote/translations/en.json index a232919b38a..2574de7e74d 100644 --- a/homeassistant/components/remote/translations/en.json +++ b/homeassistant/components/remote/translations/en.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} turned on or off", - "toggled": "{entity_name} turned on or off", "turned_off": "{entity_name} turned off", "turned_on": "{entity_name} turned on" } diff --git a/homeassistant/components/remote/translations/es.json b/homeassistant/components/remote/translations/es.json index dfa90cb1cc8..31e68384b8b 100644 --- a/homeassistant/components/remote/translations/es.json +++ b/homeassistant/components/remote/translations/es.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} activado o desactivado", - "toggled": "{entity_name} activado o desactivado", "turned_off": "{entity_name} desactivado", "turned_on": "{entity_name} activado" } diff --git a/homeassistant/components/remote/translations/et.json b/homeassistant/components/remote/translations/et.json index 12918472f33..aea377b6cb0 100644 --- a/homeassistant/components/remote/translations/et.json +++ b/homeassistant/components/remote/translations/et.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", - "toggled": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "turned_off": "{entity_name} l\u00fclitus v\u00e4lja", "turned_on": "{entity_name} l\u00fclitus sisse" } diff --git a/homeassistant/components/remote/translations/fr.json b/homeassistant/components/remote/translations/fr.json index 1560c52cba0..6b81eea5a41 100644 --- a/homeassistant/components/remote/translations/fr.json +++ b/homeassistant/components/remote/translations/fr.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} activ\u00e9 ou d\u00e9sactiv\u00e9", - "toggled": "{entity_name} activ\u00e9 ou d\u00e9sactiv\u00e9", "turned_off": "{entity_name} s'est \u00e9teint", "turned_on": "{entity_name} s'est allum\u00e9" } diff --git a/homeassistant/components/remote/translations/he.json b/homeassistant/components/remote/translations/he.json index acdd036b2cf..6e4857c217c 100644 --- a/homeassistant/components/remote/translations/he.json +++ b/homeassistant/components/remote/translations/he.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", - "toggled": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", "turned_off": "{entity_name} \u05db\u05d5\u05d1\u05d4", "turned_on": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc" } diff --git a/homeassistant/components/remote/translations/hu.json b/homeassistant/components/remote/translations/hu.json index 52b24f79f08..039e67efb6c 100644 --- a/homeassistant/components/remote/translations/hu.json +++ b/homeassistant/components/remote/translations/hu.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} be- vagy kikapcsolt", - "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" } diff --git a/homeassistant/components/remote/translations/id.json b/homeassistant/components/remote/translations/id.json index 34eaa019be2..1853288f1ef 100644 --- a/homeassistant/components/remote/translations/id.json +++ b/homeassistant/components/remote/translations/id.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} diaktifkan atau dinonaktifkan", - "toggled": "{entity_name} diaktifkan atau dinonaktifkan", "turned_off": "{entity_name} dimatikan", "turned_on": "{entity_name} dinyalakan" } diff --git a/homeassistant/components/remote/translations/it.json b/homeassistant/components/remote/translations/it.json index 381894615d3..51fbdc6b755 100644 --- a/homeassistant/components/remote/translations/it.json +++ b/homeassistant/components/remote/translations/it.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} attivata o disattivata", - "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" } diff --git a/homeassistant/components/remote/translations/ja.json b/homeassistant/components/remote/translations/ja.json index 304a47ed6d5..5c541a16e90 100644 --- a/homeassistant/components/remote/translations/ja.json +++ b/homeassistant/components/remote/translations/ja.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", - "toggled": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_off": "{entity_name} \u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_on": "{entity_name} \u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u307e\u3059" } diff --git a/homeassistant/components/remote/translations/nl.json b/homeassistant/components/remote/translations/nl.json index 47ba3d7eda7..b512f1b55aa 100644 --- a/homeassistant/components/remote/translations/nl.json +++ b/homeassistant/components/remote/translations/nl.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", - "toggled": "{entity_name} in- of uitgeschakeld", "turned_off": "{entity_name} uitgeschakeld", "turned_on": "{entity_name} ingeschakeld" } diff --git a/homeassistant/components/remote/translations/no.json b/homeassistant/components/remote/translations/no.json index e563a6aa7d8..b6eba880981 100644 --- a/homeassistant/components/remote/translations/no.json +++ b/homeassistant/components/remote/translations/no.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} sl\u00e5tt p\u00e5 eller av", - "toggled": "{entity_name} sl\u00e5tt p\u00e5 eller av", "turned_off": "{entity_name} sl\u00e5tt av", "turned_on": "{entity_name} sl\u00e5tt p\u00e5" } diff --git a/homeassistant/components/remote/translations/pl.json b/homeassistant/components/remote/translations/pl.json index 2aaaf6dbd01..3c538ee2d03 100644 --- a/homeassistant/components/remote/translations/pl.json +++ b/homeassistant/components/remote/translations/pl.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", - "toggled": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "turned_off": "nast\u0105pi wy\u0142\u0105czenie {entity_name}", "turned_on": "nast\u0105pi w\u0142\u0105czenie {entity_name}" } diff --git a/homeassistant/components/remote/translations/pt-BR.json b/homeassistant/components/remote/translations/pt-BR.json index 15d9b0d7c76..c2cbe8991c9 100644 --- a/homeassistant/components/remote/translations/pt-BR.json +++ b/homeassistant/components/remote/translations/pt-BR.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ligado ou desligado", - "toggled": "{entity_name} ligado ou desligado", "turned_off": "{entity_name} for desligado", "turned_on": "{entity_name} for ligado" } diff --git a/homeassistant/components/remote/translations/ru.json b/homeassistant/components/remote/translations/ru.json index fe1407af417..4224eb7f2b5 100644 --- a/homeassistant/components/remote/translations/ru.json +++ b/homeassistant/components/remote/translations/ru.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", - "toggled": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_off": "{entity_name} \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_on": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f" } diff --git a/homeassistant/components/remote/translations/sv.json b/homeassistant/components/remote/translations/sv.json index eeb5d486d25..1b6584c5bf8 100644 --- a/homeassistant/components/remote/translations/sv.json +++ b/homeassistant/components/remote/translations/sv.json @@ -9,7 +9,6 @@ "is_on": "{entity_name} \u00e4r p\u00e5" }, "trigger_type": { - "toggled": "{entity_name} slogs p\u00e5 eller av", "turned_off": "{entity_name} st\u00e4ngdes av", "turned_on": "{entity_name} slogs p\u00e5" } diff --git a/homeassistant/components/remote/translations/tr.json b/homeassistant/components/remote/translations/tr.json index 0b4fde944de..dc8fbf1ecad 100644 --- a/homeassistant/components/remote/translations/tr.json +++ b/homeassistant/components/remote/translations/tr.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", - "toggled": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "turned_off": "{entity_name} kapat\u0131ld\u0131", "turned_on": "{entity_name} a\u00e7\u0131ld\u0131" } diff --git a/homeassistant/components/remote/translations/zh-Hans.json b/homeassistant/components/remote/translations/zh-Hans.json index b6c5d88102f..f6c509d4a08 100644 --- a/homeassistant/components/remote/translations/zh-Hans.json +++ b/homeassistant/components/remote/translations/zh-Hans.json @@ -10,7 +10,6 @@ "is_on": "{entity_name} \u5df2\u6253\u5f00" }, "trigger_type": { - "toggled": "{entity_name} \u88ab\u6253\u5f00\u6216\u5173\u95ed", "turned_off": "{entity_name} \u88ab\u5173\u95ed", "turned_on": "{entity_name} \u88ab\u6253\u5f00" } diff --git a/homeassistant/components/remote/translations/zh-Hant.json b/homeassistant/components/remote/translations/zh-Hant.json index e8a07049e29..259121d7a4f 100644 --- a/homeassistant/components/remote/translations/zh-Hant.json +++ b/homeassistant/components/remote/translations/zh-Hant.json @@ -11,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", - "toggled": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f" } diff --git a/homeassistant/components/rfxtrx/translations/ca.json b/homeassistant/components/rfxtrx/translations/ca.json index a8a4f958cb6..edcd3a7e558 100644 --- a/homeassistant/components/rfxtrx/translations/ca.json +++ b/homeassistant/components/rfxtrx/translations/ca.json @@ -61,8 +61,7 @@ "debug": "Activa la depuraci\u00f3", "device": "Selecciona el dispositiu a configurar", "event_code": "Introdueix el codi de l'esdeveniment a afegir", - "protocols": "Protocols", - "remove_device": "Selecciona el dispositiu a eliminar" + "protocols": "Protocols" }, "title": "Opcions de Rfxtrx" }, @@ -71,11 +70,9 @@ "command_off": "Valor dels bits de dades per a l'ordre off", "command_on": "Valor dels bits de dades per a l'ordre ON", "data_bit": "Nombre de bits de dades", - "fire_event": "Activa l'esdeveniment de dispositiu", "off_delay": "Retard off", "off_delay_enabled": "Activa el retard off", "replace_device": "Selecciona el dispositiu a substituir", - "signal_repetitions": "Nombre de repeticions del senyal", "venetian_blind_mode": "Mode persiana veneciana" }, "title": "Configuraci\u00f3 de les opcions del dispositiu" diff --git a/homeassistant/components/rfxtrx/translations/cs.json b/homeassistant/components/rfxtrx/translations/cs.json index 706d4499fb2..17fea91836c 100644 --- a/homeassistant/components/rfxtrx/translations/cs.json +++ b/homeassistant/components/rfxtrx/translations/cs.json @@ -50,18 +50,15 @@ "automatic_add": "Povolit automatick\u00e9 p\u0159id\u00e1n\u00ed", "debug": "Povolit lad\u011bn\u00ed", "device": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit", - "event_code": "Zadejte k\u00f3d ud\u00e1losti, kterou chcete p\u0159idat", - "remove_device": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e9 chcete odstranit" + "event_code": "Zadejte k\u00f3d ud\u00e1losti, kterou chcete p\u0159idat" }, "title": "Mo\u017enosti Rfxtrx" }, "set_device_options": { "data": { - "fire_event": "Povolit ud\u00e1lost za\u0159\u00edzen\u00ed", "off_delay": "Zpo\u017ed\u011bn\u00ed vypnut\u00ed", "off_delay_enabled": "Povolit zpo\u017ed\u011bn\u00ed vypnut\u00ed", - "replace_device": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e9 chcete vym\u011bnit", - "signal_repetitions": "Po\u010det opakov\u00e1n\u00ed sign\u00e1lu" + "replace_device": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e9 chcete vym\u011bnit" }, "title": "Nastaven\u00ed mo\u017enost\u00ed za\u0159\u00edzen\u00ed" } diff --git a/homeassistant/components/rfxtrx/translations/de.json b/homeassistant/components/rfxtrx/translations/de.json index a80bde85b0b..445a93b7e76 100644 --- a/homeassistant/components/rfxtrx/translations/de.json +++ b/homeassistant/components/rfxtrx/translations/de.json @@ -61,8 +61,7 @@ "debug": "Debugging aktivieren", "device": "Zu konfigurierendes Ger\u00e4t ausw\u00e4hlen", "event_code": "Ereigniscode zum Hinzuf\u00fcgen eingeben", - "protocols": "Protokolle", - "remove_device": "Zu l\u00f6schendes Ger\u00e4t ausw\u00e4hlen" + "protocols": "Protokolle" }, "title": "Rfxtrx Optionen" }, @@ -71,11 +70,9 @@ "command_off": "Datenbitwert f\u00fcr den Befehl \"aus\"", "command_on": "Datenbitwert f\u00fcr den Befehl \"ein\"", "data_bit": "Anzahl der Datenbits", - "fire_event": "Ger\u00e4teereignis aktivieren", "off_delay": "Ausschaltverz\u00f6gerung", "off_delay_enabled": "Ausschaltverz\u00f6gerung aktivieren", "replace_device": "W\u00e4hle ein Ger\u00e4t aus, das ersetzt werden soll", - "signal_repetitions": "Anzahl der Signalwiederholungen", "venetian_blind_mode": "Jalousie-Modus" }, "title": "Ger\u00e4teoptionen konfigurieren" diff --git a/homeassistant/components/rfxtrx/translations/el.json b/homeassistant/components/rfxtrx/translations/el.json index 8d55c7252a0..950982df50a 100644 --- a/homeassistant/components/rfxtrx/translations/el.json +++ b/homeassistant/components/rfxtrx/translations/el.json @@ -61,8 +61,7 @@ "debug": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03bf\u03cd \u03c3\u03c6\u03b1\u03bb\u03bc\u03ac\u03c4\u03c9\u03bd", "device": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd", "event_code": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03cd \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7", - "protocols": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03b1", - "remove_device": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae" + "protocols": "\u03a0\u03c1\u03c9\u03c4\u03cc\u03ba\u03bf\u03bb\u03bb\u03b1" }, "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 Rfxtrx" }, @@ -71,11 +70,9 @@ "command_off": "\u03a4\u03b9\u03bc\u03ae bit \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c4\u03bf\u03bb\u03ae off", "command_on": "\u03a4\u03b9\u03bc\u03ae bit \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd \u03b3\u03b9\u03b1 \u03b5\u03bd\u03c4\u03bf\u03bb\u03ae on", "data_bit": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 bit \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", - "fire_event": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c3\u03c5\u03bc\u03b2\u03ac\u03bd\u03c4\u03bf\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", "off_delay": "\u039a\u03b1\u03b8\u03c5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2", "off_delay_enabled": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ba\u03b1\u03b8\u03c5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7\u03c2 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2", "replace_device": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b3\u03b9\u03b1 \u03b1\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", - "signal_repetitions": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b5\u03c0\u03b1\u03bd\u03b1\u03bb\u03ae\u03c8\u03b5\u03c9\u03bd \u03c3\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "venetian_blind_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b2\u03b5\u03bd\u03b5\u03c4\u03c3\u03b9\u03ac\u03bd\u03b9\u03ba\u03b7\u03c2 \u03c0\u03b5\u03c1\u03c3\u03af\u03b4\u03b1\u03c2" }, "title": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" diff --git a/homeassistant/components/rfxtrx/translations/en.json b/homeassistant/components/rfxtrx/translations/en.json index af042719f77..9a509246f5a 100644 --- a/homeassistant/components/rfxtrx/translations/en.json +++ b/homeassistant/components/rfxtrx/translations/en.json @@ -61,8 +61,7 @@ "debug": "Enable debugging", "device": "Select device to configure", "event_code": "Enter event code to add", - "protocols": "Protocols", - "remove_device": "Select device to delete" + "protocols": "Protocols" }, "title": "Rfxtrx Options" }, @@ -71,11 +70,9 @@ "command_off": "Data bits value for command off", "command_on": "Data bits value for command on", "data_bit": "Number of data bits", - "fire_event": "Enable device event", "off_delay": "Off delay", "off_delay_enabled": "Enable off delay", "replace_device": "Select device to replace", - "signal_repetitions": "Number of signal repetitions", "venetian_blind_mode": "Venetian blind mode" }, "title": "Configure device options" diff --git a/homeassistant/components/rfxtrx/translations/es.json b/homeassistant/components/rfxtrx/translations/es.json index ec7777ee0d4..997a6b0a0bf 100644 --- a/homeassistant/components/rfxtrx/translations/es.json +++ b/homeassistant/components/rfxtrx/translations/es.json @@ -61,8 +61,7 @@ "debug": "Activar la depuraci\u00f3n", "device": "Seleccionar dispositivo para configurar", "event_code": "Introducir el c\u00f3digo de evento para a\u00f1adir", - "protocols": "Protocolos", - "remove_device": "Selecciona el dispositivo a eliminar" + "protocols": "Protocolos" }, "title": "Opciones de Rfxtrx" }, @@ -71,11 +70,9 @@ "command_off": "Valor de bits de datos para comando apagado", "command_on": "Valor de bits de datos para comando activado", "data_bit": "N\u00famero de bits de datos", - "fire_event": "Habilitar evento del dispositivo", "off_delay": "Retraso de apagado", "off_delay_enabled": "Activar retardo de apagado", "replace_device": "Seleccione el dispositivo que desea reemplazar", - "signal_repetitions": "N\u00famero de repeticiones de la se\u00f1al", "venetian_blind_mode": "Modo de persiana veneciana" }, "title": "Configurar las opciones del dispositivo" diff --git a/homeassistant/components/rfxtrx/translations/et.json b/homeassistant/components/rfxtrx/translations/et.json index 2c4a36b1664..eff49a8a6c4 100644 --- a/homeassistant/components/rfxtrx/translations/et.json +++ b/homeassistant/components/rfxtrx/translations/et.json @@ -61,8 +61,7 @@ "debug": "Luba silumine", "device": "Vali seadistatav seade", "event_code": "Sisesta lisatava s\u00fcndmuse kood", - "protocols": "Protokollid", - "remove_device": "Vali eemaldatav seade" + "protocols": "Protokollid" }, "title": "Rfxtrx valikud" }, @@ -71,11 +70,9 @@ "command_off": "V\u00e4ljal\u00fclitamise k\u00e4su andmebittide v\u00e4\u00e4rtus", "command_on": "Sissel\u00fclitamise k\u00e4su andmebittide v\u00e4\u00e4rtus", "data_bit": "Andmebittide arv", - "fire_event": "Luba seadme s\u00fcndmus", "off_delay": "V\u00e4ljal\u00fclitamise viivitus", "off_delay_enabled": "Luba v\u00e4ljal\u00fclitusviivitus", "replace_device": "Vali asendav seade", - "signal_repetitions": "Signaali korduste arv", "venetian_blind_mode": "Ribikardinate juhtimine" }, "title": "Seadista seadme valikud" diff --git a/homeassistant/components/rfxtrx/translations/fr.json b/homeassistant/components/rfxtrx/translations/fr.json index 5eaff1ce406..9e31b450ce7 100644 --- a/homeassistant/components/rfxtrx/translations/fr.json +++ b/homeassistant/components/rfxtrx/translations/fr.json @@ -66,8 +66,7 @@ "debug": "Activer le d\u00e9bogage", "device": "S\u00e9lectionnez l'appareil \u00e0 configurer", "event_code": "Entrez le code d'\u00e9v\u00e9nement \u00e0 ajouter", - "protocols": "Protocoles", - "remove_device": "S\u00e9lectionnez l'appareil \u00e0 supprimer" + "protocols": "Protocoles" }, "title": "Options Rfxtrx" }, @@ -76,11 +75,9 @@ "command_off": "Valeur des bits de donn\u00e9es pour la commande \u00e9teindre", "command_on": "Valeur des bits de donn\u00e9es pour la commande allumer", "data_bit": "Nombre de bits de donn\u00e9es", - "fire_event": "Activer les \u00e9v\u00e9nements d'appareil", "off_delay": "D\u00e9lai d'arr\u00eat", "off_delay_enabled": "Activer le d\u00e9lai d'arr\u00eat", "replace_device": "S\u00e9lectionnez l'appareil \u00e0 remplacer", - "signal_repetitions": "Nombre de r\u00e9p\u00e9titions du signal", "venetian_blind_mode": "Mode store v\u00e9nitien" }, "title": "Configurer les options de l'appareil" diff --git a/homeassistant/components/rfxtrx/translations/hu.json b/homeassistant/components/rfxtrx/translations/hu.json index a6f1c925fbf..158411ef618 100644 --- a/homeassistant/components/rfxtrx/translations/hu.json +++ b/homeassistant/components/rfxtrx/translations/hu.json @@ -66,8 +66,7 @@ "debug": "Enged\u00e9lyezze a hibakeres\u00e9st", "device": "V\u00e1lassza ki a konfigur\u00e1lni k\u00edv\u00e1nt eszk\u00f6zt", "event_code": "\u00cdrja be a hozz\u00e1adni k\u00edv\u00e1nt esem\u00e9ny k\u00f3dj\u00e1t", - "protocols": "Protokollok", - "remove_device": "V\u00e1lassza ki a t\u00f6r\u00f6lni k\u00edv\u00e1nt eszk\u00f6zt" + "protocols": "Protokollok" }, "title": "Rfxtrx opci\u00f3k" }, @@ -76,11 +75,9 @@ "command_off": "Adatbitek \u00e9rt\u00e9ke a parancs kikapcsol\u00e1s\u00e1hoz", "command_on": "Adatbitek \u00e9rt\u00e9ke a parancshoz", "data_bit": "Adatbitek sz\u00e1ma", - "fire_event": "Eszk\u00f6zesem\u00e9ny enged\u00e9lyez\u00e9se", "off_delay": "Kikapcsol\u00e1si k\u00e9sleltet\u00e9s", "off_delay_enabled": "Kikapcsol\u00e1si k\u00e9sleltet\u00e9s enged\u00e9lyez\u00e9se", "replace_device": "V\u00e1lassza ki a cser\u00e9lni k\u00edv\u00e1nt eszk\u00f6zt", - "signal_repetitions": "A jelism\u00e9tl\u00e9sek sz\u00e1ma", "venetian_blind_mode": "Velencei red\u0151ny \u00fczemm\u00f3d" }, "title": "Konfigur\u00e1lja az eszk\u00f6z be\u00e1ll\u00edt\u00e1sait" diff --git a/homeassistant/components/rfxtrx/translations/id.json b/homeassistant/components/rfxtrx/translations/id.json index cea00e08f9c..fa39b4c4a2e 100644 --- a/homeassistant/components/rfxtrx/translations/id.json +++ b/homeassistant/components/rfxtrx/translations/id.json @@ -61,8 +61,7 @@ "debug": "Aktifkan debugging", "device": "Pilih perangkat untuk dikonfigurasi", "event_code": "Masukkan kode event untuk ditambahkan", - "protocols": "Protokol", - "remove_device": "Pilih perangkat yang akan dihapus" + "protocols": "Protokol" }, "title": "Opsi Rfxtrx" }, @@ -71,11 +70,9 @@ "command_off": "Nilai bit data untuk perintah mematikan", "command_on": "Nilai bit data untuk perintah menyalakan", "data_bit": "Jumlah bit data", - "fire_event": "Aktifkan event perangkat", "off_delay": "Penundaan mematikan", "off_delay_enabled": "Aktifkan penundaan mematikan", "replace_device": "Pilih perangkat yang akan diganti", - "signal_repetitions": "Jumlah pengulangan sinyal", "venetian_blind_mode": "Mode penutup kerai Venesia" }, "title": "Konfigurasi opsi perangkat" diff --git a/homeassistant/components/rfxtrx/translations/it.json b/homeassistant/components/rfxtrx/translations/it.json index 5e5f07a013f..bcf9cebd99d 100644 --- a/homeassistant/components/rfxtrx/translations/it.json +++ b/homeassistant/components/rfxtrx/translations/it.json @@ -66,8 +66,7 @@ "debug": "Attiva il debug", "device": "Seleziona il dispositivo da configurare", "event_code": "Inserire il codice dell'evento da aggiungere", - "protocols": "Protocolli", - "remove_device": "Seleziona il dispositivo da eliminare" + "protocols": "Protocolli" }, "title": "Opzioni Rfxtrx" }, @@ -76,11 +75,9 @@ "command_off": "Valore dei bit di dati per il comando disattivato", "command_on": "Valore dei bit di dati per il comando attivato", "data_bit": "Numero di bit di dati", - "fire_event": "Abilita evento dispositivo", "off_delay": "Ritardo di spegnimento", "off_delay_enabled": "Attiva il ritardo di spegnimento", "replace_device": "Seleziona il dispositivo da sostituire", - "signal_repetitions": "Numero di ripetizioni del segnale", "venetian_blind_mode": "Modalit\u00e0 veneziana" }, "title": "Configura le opzioni del dispositivo" diff --git a/homeassistant/components/rfxtrx/translations/ja.json b/homeassistant/components/rfxtrx/translations/ja.json index cb79cc60a8e..9b22d34af58 100644 --- a/homeassistant/components/rfxtrx/translations/ja.json +++ b/homeassistant/components/rfxtrx/translations/ja.json @@ -61,8 +61,7 @@ "debug": "\u30c7\u30d0\u30c3\u30b0\u306e\u6709\u52b9\u5316", "device": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e", "event_code": "\u30a4\u30d9\u30f3\u30c8\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u8ffd\u52a0", - "protocols": "\u30d7\u30ed\u30c8\u30b3\u30eb", - "remove_device": "\u524a\u9664\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u306e\u9078\u629e" + "protocols": "\u30d7\u30ed\u30c8\u30b3\u30eb" }, "title": "Rfxtrx\u306e\u30aa\u30d7\u30b7\u30e7\u30f3" }, @@ -71,11 +70,9 @@ "command_off": "\u30b3\u30de\u30f3\u30c9\u30aa\u30d5\u306e\u30c7\u30fc\u30bf\u30d3\u30c3\u30c8\u5024", "command_on": "\u30b3\u30de\u30f3\u30c9\u30aa\u30f3\u306e\u30c7\u30fc\u30bf\u30d3\u30c3\u30c8\u5024", "data_bit": "\u30c7\u30fc\u30bf\u30d3\u30c3\u30c8\u6570", - "fire_event": "\u30c7\u30d0\u30a4\u30b9 \u30a4\u30d9\u30f3\u30c8\u3092\u6709\u52b9\u306b\u3059\u308b", "off_delay": "\u30aa\u30d5\u9045\u5ef6", "off_delay_enabled": "\u30aa\u30d5\u9045\u5ef6\u3092\u6709\u52b9\u306b\u3059\u308b", "replace_device": "\u4ea4\u63db\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e", - "signal_repetitions": "\u4fe1\u53f7\u306e\u30ea\u30d4\u30fc\u30c8\u6570", "venetian_blind_mode": "Venetian blind\u30e2\u30fc\u30c9" }, "title": "\u30c7\u30d0\u30a4\u30b9\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u8a2d\u5b9a" diff --git a/homeassistant/components/rfxtrx/translations/ko.json b/homeassistant/components/rfxtrx/translations/ko.json index 891926083dd..0434e5b75ab 100644 --- a/homeassistant/components/rfxtrx/translations/ko.json +++ b/homeassistant/components/rfxtrx/translations/ko.json @@ -50,8 +50,7 @@ "automatic_add": "\uc790\ub3d9 \ucd94\uac00 \ud65c\uc131\ud654\ud558\uae30", "debug": "\ub514\ubc84\uae45 \ud65c\uc131\ud654\ud558\uae30", "device": "\uad6c\uc131\ud560 \uae30\uae30 \uc120\ud0dd\ud558\uae30", - "event_code": "\ucd94\uac00\ud560 \uc774\ubca4\ud2b8 \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694", - "remove_device": "\uc0ad\uc81c\ud560 \uae30\uae30 \uc120\ud0dd\ud558\uae30" + "event_code": "\ucd94\uac00\ud560 \uc774\ubca4\ud2b8 \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694" }, "title": "Rfxtrx \uc635\uc158" }, @@ -60,11 +59,9 @@ "command_off": "\ub044\uae30 \uba85\ub839\uc5d0 \ub300\ud55c \ub370\uc774\ud130 \ube44\ud2b8 \uac12", "command_on": "\ucf1c\uae30 \uba85\ub839\uc5d0 \ub300\ud55c \ub370\uc774\ud130 \ube44\ud2b8 \uac12", "data_bit": "\ub370\uc774\ud130 \ube44\ud2b8 \uc218", - "fire_event": "\uae30\uae30 \uc774\ubca4\ud2b8 \ud65c\uc131\ud654\ud558\uae30", "off_delay": "\uc790\ub3d9 \uaebc\uc9c4 \uc0c1\ud0dc", "off_delay_enabled": "\uc790\ub3d9 \uaebc\uc9c4 \uc0c1\ud0dc(Off Delay) \ud65c\uc131\ud654\ud558\uae30", "replace_device": "\uad50\uccb4\ud560 \uae30\uae30 \uc120\ud0dd\ud558\uae30", - "signal_repetitions": "\uc2e0\ud638 \ubc18\ubcf5 \ud69f\uc218", "venetian_blind_mode": "\ubca0\ub124\uc2dc\uc548 \ube14\ub77c\uc778\ub4dc \ubaa8\ub4dc" }, "title": "\uae30\uae30 \uc635\uc158 \uad6c\uc131\ud558\uae30" diff --git a/homeassistant/components/rfxtrx/translations/lb.json b/homeassistant/components/rfxtrx/translations/lb.json index f5441eeb298..82535926bd4 100644 --- a/homeassistant/components/rfxtrx/translations/lb.json +++ b/homeassistant/components/rfxtrx/translations/lb.json @@ -47,8 +47,7 @@ "automatic_add": "Aktiv\u00e9ier automatesch dob\u00e4isetzen", "debug": "Aktiv\u00e9ier Debuggen", "device": "Wiel een Apparat fir ze konfigur\u00e9ieren", - "event_code": "Evenement Code aginn fir dob\u00e4izesetzen", - "remove_device": "Wiel een Apparat fir ze l\u00e4schen" + "event_code": "Evenement Code aginn fir dob\u00e4izesetzen" }, "title": "Rfxtrx Optioune" }, @@ -57,11 +56,9 @@ "command_off": "Data bits W\u00e4ert fir Kommando aus", "command_on": "Data bits W\u00e4ert fir Kommando un", "data_bit": "Unzuel vun Data Bits", - "fire_event": "Aktiv\u00e9ier Apparat Evenement", "off_delay": "Aus Verz\u00f6gerung", "off_delay_enabled": "Aktiv\u00e9iert Aus Verz\u00f6gerung", - "replace_device": "Wiel een Apparat fir ze ersetzen", - "signal_repetitions": "Zuel vu Signalwidderhuelungen" + "replace_device": "Wiel een Apparat fir ze ersetzen" }, "title": "Apparat Optioune konfigur\u00e9ieren" } diff --git a/homeassistant/components/rfxtrx/translations/nl.json b/homeassistant/components/rfxtrx/translations/nl.json index e9b4c1a6c04..5a0ced3f52d 100644 --- a/homeassistant/components/rfxtrx/translations/nl.json +++ b/homeassistant/components/rfxtrx/translations/nl.json @@ -61,8 +61,7 @@ "debug": "Foutopsporing inschakelen", "device": "Selecteer het apparaat om te configureren", "event_code": "Voer de gebeurteniscode in om toe te voegen", - "protocols": "Protocollen", - "remove_device": "Apparaat selecteren dat u wilt verwijderen" + "protocols": "Protocollen" }, "title": "Rfxtrx-opties" }, @@ -71,11 +70,9 @@ "command_off": "Waarde gegevensbits voor commando uit", "command_on": "Waarde gegevensbits voor commando aan", "data_bit": "Aantal databits", - "fire_event": "Schakel apparaatgebeurtenis in", "off_delay": "Uitschakelvertraging", "off_delay_enabled": "Schakel uitschakelvertraging in", "replace_device": "Selecteer apparaat dat u wilt vervangen", - "signal_repetitions": "Aantal signaalherhalingen", "venetian_blind_mode": "Venetiaanse jaloezie modus" }, "title": "Configureer apparaatopties" diff --git a/homeassistant/components/rfxtrx/translations/no.json b/homeassistant/components/rfxtrx/translations/no.json index 62a658d4067..acdca83016e 100644 --- a/homeassistant/components/rfxtrx/translations/no.json +++ b/homeassistant/components/rfxtrx/translations/no.json @@ -61,8 +61,7 @@ "debug": "Aktiver feils\u00f8king", "device": "Velg enhet du vil konfigurere", "event_code": "Angi hendelseskode for \u00e5 legge til", - "protocols": "Protokoller", - "remove_device": "Velg enhet du vil slette" + "protocols": "Protokoller" }, "title": "[%key:component::rfxtrx::title%] alternativer" }, @@ -71,11 +70,9 @@ "command_off": "Databiter-verdi for kommando av", "command_on": "Databiter-verdi for kommando p\u00e5", "data_bit": "Antall databiter", - "fire_event": "Aktiver enhetshendelse", "off_delay": "Av forsinkelse", "off_delay_enabled": "Aktiver av forsinkelse", "replace_device": "Velg enheten du vil erstatte", - "signal_repetitions": "Antall signalrepetisjoner", "venetian_blind_mode": "Persiennemodus" }, "title": "Konfigurer enhetsalternativer" diff --git a/homeassistant/components/rfxtrx/translations/pl.json b/homeassistant/components/rfxtrx/translations/pl.json index b9f83ded473..c081baeafff 100644 --- a/homeassistant/components/rfxtrx/translations/pl.json +++ b/homeassistant/components/rfxtrx/translations/pl.json @@ -72,8 +72,7 @@ "debug": "W\u0142\u0105cz debugowanie", "device": "Wybierz urz\u0105dzenie do skonfigurowania", "event_code": "Podaj kod zdarzenia do dodania", - "protocols": "Protoko\u0142y", - "remove_device": "Wybierz urz\u0105dzenie do usuni\u0119cia" + "protocols": "Protoko\u0142y" }, "title": "Opcje Rfxtrx" }, @@ -82,11 +81,9 @@ "command_off": "Warto\u015b\u0107 bit\u00f3w danych dla komendy \"wy\u0142\u0105cz\" (off)", "command_on": "Warto\u015b\u0107 bit\u00f3w danych dla komendy \"w\u0142\u0105cz\" (on)", "data_bit": "Liczba bit\u00f3w danych", - "fire_event": "W\u0142\u0105cz zdarzenia na urz\u0105dzeniu", "off_delay": "Op\u00f3\u017anienie stanu \"off\"", "off_delay_enabled": "W\u0142\u0105cz op\u00f3\u017anienie stanu \"off\"", "replace_device": "Wybierz urz\u0105dzenie do zast\u0105pienia", - "signal_repetitions": "Liczba powt\u00f3rze\u0144 sygna\u0142u", "venetian_blind_mode": "Tryb \u017caluzji weneckich" }, "title": "Konfiguracja opcji urz\u0105dzenia" diff --git a/homeassistant/components/rfxtrx/translations/pt-BR.json b/homeassistant/components/rfxtrx/translations/pt-BR.json index 83252586d6a..a43b7e15a2b 100644 --- a/homeassistant/components/rfxtrx/translations/pt-BR.json +++ b/homeassistant/components/rfxtrx/translations/pt-BR.json @@ -61,8 +61,7 @@ "debug": "Habilitar a depura\u00e7\u00e3o", "device": "Selecione o dispositivo para configurar", "event_code": "Insira o c\u00f3digo do evento para adicionar", - "protocols": "Protocolos", - "remove_device": "Selecione o dispositivo para excluir" + "protocols": "Protocolos" }, "title": "Op\u00e7\u00f5es de Rfxtrx" }, @@ -71,11 +70,9 @@ "command_off": "Valor de bits de dados para comando desligado", "command_on": "Valor de bits de dados para comando ligado", "data_bit": "N\u00famero de bits de dados", - "fire_event": "Ativar evento do dispositivo", "off_delay": "Atraso de desligamento", "off_delay_enabled": "Ativar atraso de desligamento", "replace_device": "Selecione o dispositivo para substituir", - "signal_repetitions": "N\u00famero de repeti\u00e7\u00f5es de sinal", "venetian_blind_mode": "Modo de persianas" }, "title": "Configurar op\u00e7\u00f5es do dispositivo" diff --git a/homeassistant/components/rfxtrx/translations/ru.json b/homeassistant/components/rfxtrx/translations/ru.json index cb0fa979197..74dc2e218c9 100644 --- a/homeassistant/components/rfxtrx/translations/ru.json +++ b/homeassistant/components/rfxtrx/translations/ru.json @@ -61,8 +61,7 @@ "debug": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0435\u0436\u0438\u043c \u043e\u0442\u043b\u0430\u0434\u043a\u0438", "device": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", "event_code": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u0441\u043e\u0431\u044b\u0442\u0438\u044f", - "protocols": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b", - "remove_device": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f" + "protocols": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b" }, "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438" }, @@ -71,11 +70,9 @@ "command_off": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0438\u0442\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "command_on": "\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u0438\u0442\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "data_bit": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0442 \u0434\u0430\u043d\u043d\u044b\u0445", - "fire_event": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "off_delay": "\u0417\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "off_delay_enabled": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "replace_device": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u0437\u0430\u043c\u0435\u043d\u044b", - "signal_repetitions": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043e\u0432 \u0441\u0438\u0433\u043d\u0430\u043b\u0430", "venetian_blind_mode": "\u0420\u0435\u0436\u0438\u043c \u0432\u0435\u043d\u0435\u0446\u0438\u0430\u043d\u0441\u043a\u0438\u0445 \u0436\u0430\u043b\u044e\u0437\u0438" }, "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" diff --git a/homeassistant/components/rfxtrx/translations/sv.json b/homeassistant/components/rfxtrx/translations/sv.json index bdafd86c9a0..6cab9bd0b37 100644 --- a/homeassistant/components/rfxtrx/translations/sv.json +++ b/homeassistant/components/rfxtrx/translations/sv.json @@ -43,8 +43,7 @@ "automatic_add": "Aktivera automatisk till\u00e4gg av enheter", "debug": "Aktivera fels\u00f6kning", "device": "V\u00e4lj enhet att konfigurera", - "event_code": "Ange h\u00e4ndelsekod att l\u00e4gga till", - "remove_device": "V\u00e4lj enhet som ska tas bort" + "event_code": "Ange h\u00e4ndelsekod att l\u00e4gga till" }, "title": "Rfxtrx-alternativ" }, @@ -53,11 +52,9 @@ "command_off": "Databitv\u00e4rde f\u00f6r av-kommando", "command_on": "Databitv\u00e4rde f\u00f6r p\u00e5-kommando", "data_bit": "Antal databitar", - "fire_event": "Aktivera enhetsh\u00e4ndelse", "off_delay": "Avst\u00e4ngningsf\u00f6rdr\u00f6jning", "off_delay_enabled": "Aktivera avst\u00e4ngningsf\u00f6rdr\u00f6jning", - "replace_device": "V\u00e4lj enhet att ers\u00e4tta", - "signal_repetitions": "Antal signalrepetitioner" + "replace_device": "V\u00e4lj enhet att ers\u00e4tta" }, "title": "Konfigurera enhetsalternativ" } diff --git a/homeassistant/components/rfxtrx/translations/tr.json b/homeassistant/components/rfxtrx/translations/tr.json index fd29e034e31..999536c6b31 100644 --- a/homeassistant/components/rfxtrx/translations/tr.json +++ b/homeassistant/components/rfxtrx/translations/tr.json @@ -66,8 +66,7 @@ "debug": "Hata ay\u0131klamay\u0131 etkinle\u015ftir", "device": "Yap\u0131land\u0131rmak i\u00e7in cihaz\u0131 se\u00e7in", "event_code": "Eklemek i\u00e7in etkinlik kodunu girin", - "protocols": "Protokoller", - "remove_device": "Silinecek cihaz\u0131 se\u00e7in" + "protocols": "Protokoller" }, "title": "Rfxtrx Se\u00e7enekleri" }, @@ -76,11 +75,9 @@ "command_off": "Komut kapatma i\u00e7in veri bitleri de\u011feri", "command_on": "Komut i\u00e7in veri bitleri de\u011feri", "data_bit": "Veri biti say\u0131s\u0131", - "fire_event": "Cihaz etkinli\u011fini etkinle\u015ftir", "off_delay": "Kapanma gecikmesi", "off_delay_enabled": "Kapatma gecikmesini etkinle\u015ftir", "replace_device": "De\u011fi\u015ftirilecek cihaz\u0131 se\u00e7in", - "signal_repetitions": "Sinyal tekrar\u0131 say\u0131s\u0131", "venetian_blind_mode": "Jaluzi modu" }, "title": "Cihaz se\u00e7eneklerini yap\u0131land\u0131r\u0131n" diff --git a/homeassistant/components/rfxtrx/translations/uk.json b/homeassistant/components/rfxtrx/translations/uk.json index 65cca65679c..42ab18699ee 100644 --- a/homeassistant/components/rfxtrx/translations/uk.json +++ b/homeassistant/components/rfxtrx/translations/uk.json @@ -50,8 +50,7 @@ "automatic_add": "\u0423\u0432\u0456\u043c\u043a\u043d\u0443\u0442\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u0435 \u0434\u043e\u0434\u0430\u0432\u0430\u043d\u043d\u044f", "debug": "\u0423\u0432\u0456\u043c\u043a\u043d\u0443\u0442\u0438 \u0440\u0435\u0436\u0438\u043c \u043d\u0430\u043b\u0430\u0433\u043e\u0434\u0436\u0435\u043d\u043d\u044f", "device": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0434\u043b\u044f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f", - "event_code": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043a\u043e\u0434 \u043f\u043e\u0434\u0456\u0457", - "remove_device": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0434\u043b\u044f \u0432\u0438\u0434\u0430\u043b\u0435\u043d\u043d\u044f" + "event_code": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043a\u043e\u0434 \u043f\u043e\u0434\u0456\u0457" }, "title": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438" }, @@ -60,11 +59,9 @@ "command_off": "\u0417\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u0431\u0456\u0442\u0456\u0432 \u0434\u0430\u043d\u0438\u0445 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0438 \u0432\u0438\u043c\u043a\u043d\u0435\u043d\u043d\u044f", "command_on": "\u0417\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u0431\u0456\u0442\u0456\u0432 \u0434\u0430\u043d\u0438\u0445 \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0438 \u0443\u0432\u0456\u043c\u043a\u043d\u0435\u043d\u043d\u044f", "data_bit": "\u041a\u0456\u043b\u044c\u043a\u0456\u0441\u0442\u044c \u0431\u0456\u0442\u0456\u0432 \u0434\u0430\u043d\u0438\u0445", - "fire_event": "\u0423\u0432\u0456\u043c\u043a\u043d\u0443\u0442\u0438 \u043f\u043e\u0434\u0456\u0457 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e", "off_delay": "\u0417\u0430\u0442\u0440\u0438\u043c\u043a\u0430 \u0432\u0438\u043c\u043a\u043d\u0435\u043d\u043d\u044f", "off_delay_enabled": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0438 \u0437\u0430\u0442\u0440\u0438\u043c\u043a\u0443 \u0432\u0438\u043c\u0438\u043a\u0430\u043d\u043d\u044f", "replace_device": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u0434\u043b\u044f \u0437\u0430\u043c\u0456\u043d\u0438", - "signal_repetitions": "\u041a\u0456\u043b\u044c\u043a\u0456\u0441\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u0456\u0432 \u0441\u0438\u0433\u043d\u0430\u043b\u0443", "venetian_blind_mode": "\u0420\u0435\u0436\u0438\u043c \u0436\u0430\u043b\u044e\u0437\u0456" }, "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0456\u0432 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" diff --git a/homeassistant/components/rfxtrx/translations/zh-Hant.json b/homeassistant/components/rfxtrx/translations/zh-Hant.json index 3f84f0a5f98..e477e6628ae 100644 --- a/homeassistant/components/rfxtrx/translations/zh-Hant.json +++ b/homeassistant/components/rfxtrx/translations/zh-Hant.json @@ -61,8 +61,7 @@ "debug": "\u958b\u555f\u9664\u932f", "device": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e", "event_code": "\u8f38\u5165\u4e8b\u4ef6\u4ee3\u78bc\u4ee5\u65b0\u589e", - "protocols": "\u901a\u8a0a\u5354\u5b9a", - "remove_device": "\u9078\u64c7\u88dd\u7f6e\u4ee5\u522a\u9664" + "protocols": "\u901a\u8a0a\u5354\u5b9a" }, "title": "Rfxtrx \u9078\u9805" }, @@ -71,11 +70,9 @@ "command_off": "\u547d\u4ee4\u95dc\u9589\u7684\u8cc7\u6599\u4f4d\u5143\u503c", "command_on": "\u547d\u4ee4\u958b\u555f\u7684\u8cc7\u6599\u4f4d\u5143\u503c", "data_bit": "\u8cc7\u6599\u4f4d\u5143\u6578", - "fire_event": "\u958b\u555f\u88dd\u7f6e\u4e8b\u4ef6", "off_delay": "\u5ef6\u9072", "off_delay_enabled": "\u958b\u555f\u5ef6\u9072", "replace_device": "\u9078\u64c7\u88dd\u7f6e\u4ee5\u53d6\u4ee3", - "signal_repetitions": "\u8a0a\u865f\u91cd\u8907\u6b21\u6578", "venetian_blind_mode": "\u767e\u8449\u7a97\u6a21\u5f0f" }, "title": "\u8a2d\u5b9a\u88dd\u7f6e\u9078\u9805" diff --git a/homeassistant/components/roku/translations/ca.json b/homeassistant/components/roku/translations/ca.json index be84d78fbff..df9d6fe48c7 100644 --- a/homeassistant/components/roku/translations/ca.json +++ b/homeassistant/components/roku/translations/ca.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Vols configurar {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Vols configurar {name}?", - "title": "Roku" + "description": "Vols configurar {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/cs.json b/homeassistant/components/roku/translations/cs.json index 6914a519285..ee5ad4d600a 100644 --- a/homeassistant/components/roku/translations/cs.json +++ b/homeassistant/components/roku/translations/cs.json @@ -11,12 +11,7 @@ "flow_title": "Roku: {name}", "step": { "discovery_confirm": { - "description": "Chcete nastavit {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Chcete nastavit {name}?", - "title": "Roku" + "description": "Chcete nastavit {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/de.json b/homeassistant/components/roku/translations/de.json index 5ff560809c8..25972162593 100644 --- a/homeassistant/components/roku/translations/de.json +++ b/homeassistant/components/roku/translations/de.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "M\u00f6chtest du {name} einrichten?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "M\u00f6chtest du {name} einrichten?", - "title": "Roku" + "description": "M\u00f6chtest du {name} einrichten?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/el.json b/homeassistant/components/roku/translations/el.json index 91087c73a58..bf498993096 100644 --- a/homeassistant/components/roku/translations/el.json +++ b/homeassistant/components/roku/translations/el.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};", - "title": "Roku" + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name};" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/en.json b/homeassistant/components/roku/translations/en.json index 192b9b23085..a4d931a8bcf 100644 --- a/homeassistant/components/roku/translations/en.json +++ b/homeassistant/components/roku/translations/en.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Do you want to set up {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Do you want to set up {name}?", - "title": "Roku" + "description": "Do you want to set up {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/es-419.json b/homeassistant/components/roku/translations/es-419.json index 00b69c53c72..bff168d0084 100644 --- a/homeassistant/components/roku/translations/es-419.json +++ b/homeassistant/components/roku/translations/es-419.json @@ -9,10 +9,6 @@ }, "flow_title": "Roku: {name}", "step": { - "ssdp_confirm": { - "description": "\u00bfDesea configurar {name}?", - "title": "Roku" - }, "user": { "data": { "host": "Host o direcci\u00f3n IP" diff --git a/homeassistant/components/roku/translations/es.json b/homeassistant/components/roku/translations/es.json index 202c7f6c8ff..817f1d970cc 100644 --- a/homeassistant/components/roku/translations/es.json +++ b/homeassistant/components/roku/translations/es.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "\u00bfQuieres configurar {name} ?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "\u00bfQuieres configurar {name}?", - "title": "Roku" + "description": "\u00bfQuieres configurar {name} ?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/et.json b/homeassistant/components/roku/translations/et.json index bb496ab8716..78ce0995ebd 100644 --- a/homeassistant/components/roku/translations/et.json +++ b/homeassistant/components/roku/translations/et.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Kas soovid seadistada {name}?", - "title": "" - }, - "ssdp_confirm": { - "description": "Kas soovid seadistada {name}?", - "title": "" + "description": "Kas soovid seadistada {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/fr.json b/homeassistant/components/roku/translations/fr.json index 64a928de567..2d5ab38f828 100644 --- a/homeassistant/components/roku/translations/fr.json +++ b/homeassistant/components/roku/translations/fr.json @@ -15,16 +15,13 @@ "one": "Vide", "other": "Vide" }, - "description": "Voulez-vous configurer {name} ?", - "title": "Roku" + "description": "Voulez-vous configurer {name} ?" }, "ssdp_confirm": { "data": { "one": "Vide", "other": "Vide" - }, - "description": "Voulez-vous configurer {name} ?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/hu.json b/homeassistant/components/roku/translations/hu.json index c28b0a712ea..e38c9ada483 100644 --- a/homeassistant/components/roku/translations/hu.json +++ b/homeassistant/components/roku/translations/hu.json @@ -15,16 +15,13 @@ "one": "Egy", "other": "Egy\u00e9b" }, - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?", - "title": "Roku felfedezve" + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?" }, "ssdp_confirm": { "data": { "one": "\u00dcres", "other": "\u00dcres" - }, - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name}?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/id.json b/homeassistant/components/roku/translations/id.json index 3a227e80eaf..69d40e4aabb 100644 --- a/homeassistant/components/roku/translations/id.json +++ b/homeassistant/components/roku/translations/id.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Ingin menyiapkan {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Ingin menyiapkan {name}?", - "title": "Roku" + "description": "Ingin menyiapkan {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/it.json b/homeassistant/components/roku/translations/it.json index 9f42c49c74f..4ebf0784669 100644 --- a/homeassistant/components/roku/translations/it.json +++ b/homeassistant/components/roku/translations/it.json @@ -15,16 +15,13 @@ "one": "Vuoto", "other": "Vuoti" }, - "description": "Vuoi configurare {name}?", - "title": "Roku" + "description": "Vuoi configurare {name}?" }, "ssdp_confirm": { "data": { "one": "Vuoto", "other": "Vuoti" - }, - "description": "Vuoi impostare {name}?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/ja.json b/homeassistant/components/roku/translations/ja.json index 65f2ac2272a..59026c4d204 100644 --- a/homeassistant/components/roku/translations/ja.json +++ b/homeassistant/components/roku/translations/ja.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "{name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "{name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f", - "title": "Roku" + "description": "{name} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/ko.json b/homeassistant/components/roku/translations/ko.json index cb127234601..93fb292c458 100644 --- a/homeassistant/components/roku/translations/ko.json +++ b/homeassistant/components/roku/translations/ko.json @@ -11,12 +11,7 @@ "flow_title": "Roku: {name}", "step": { "discovery_confirm": { - "description": "{name}\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "{name}\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", - "title": "Roku" + "description": "{name}\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/lb.json b/homeassistant/components/roku/translations/lb.json index 04ad814c6b4..0a293006b9b 100644 --- a/homeassistant/components/roku/translations/lb.json +++ b/homeassistant/components/roku/translations/lb.json @@ -10,12 +10,7 @@ "flow_title": "Roku: {name}", "step": { "discovery_confirm": { - "description": "Soll {name} konfigur\u00e9iert ginn?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Soll {name} konfigur\u00e9iert ginn?", - "title": "Roku" + "description": "Soll {name} konfigur\u00e9iert ginn?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/nl.json b/homeassistant/components/roku/translations/nl.json index 6bf3435a19b..7f413121fa7 100644 --- a/homeassistant/components/roku/translations/nl.json +++ b/homeassistant/components/roku/translations/nl.json @@ -15,16 +15,13 @@ "one": "Een", "other": "Ander" }, - "description": "Wilt u {name} instellen?", - "title": "Roku" + "description": "Wilt u {name} instellen?" }, "ssdp_confirm": { "data": { "one": "Leeg", "other": "Leeg" - }, - "description": "Wilt u {name} instellen?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/no.json b/homeassistant/components/roku/translations/no.json index 1bbd5bbea86..8449aaabfdd 100644 --- a/homeassistant/components/roku/translations/no.json +++ b/homeassistant/components/roku/translations/no.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Vil du konfigurere {name}?", - "title": "" - }, - "ssdp_confirm": { - "description": "Vil du sette opp {name} ?", - "title": "" + "description": "Vil du konfigurere {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/pl.json b/homeassistant/components/roku/translations/pl.json index 41ea348543e..59fb1aaa673 100644 --- a/homeassistant/components/roku/translations/pl.json +++ b/homeassistant/components/roku/translations/pl.json @@ -17,8 +17,7 @@ "one": "jeden", "other": "inne" }, - "description": "Czy chcesz skonfigurowa\u0107 {name}?", - "title": "Roku" + "description": "Czy chcesz skonfigurowa\u0107 {name}?" }, "ssdp_confirm": { "data": { @@ -26,9 +25,7 @@ "many": "wiele", "one": "jeden", "other": "inne" - }, - "description": "Czy chcesz skonfigurowa\u0107 {name}?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/pt-BR.json b/homeassistant/components/roku/translations/pt-BR.json index 408bbc915c0..1970fbd0886 100644 --- a/homeassistant/components/roku/translations/pt-BR.json +++ b/homeassistant/components/roku/translations/pt-BR.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "Deseja configurar {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "Voc\u00ea quer configurar o {name}?", - "title": "Roku" + "description": "Deseja configurar {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/pt.json b/homeassistant/components/roku/translations/pt.json index e67de509456..1d4aea3279b 100644 --- a/homeassistant/components/roku/translations/pt.json +++ b/homeassistant/components/roku/translations/pt.json @@ -9,9 +9,6 @@ }, "flow_title": "Roku: {name}", "step": { - "ssdp_confirm": { - "title": "Roku" - }, "user": { "data": { "host": "Servidor" diff --git a/homeassistant/components/roku/translations/ru.json b/homeassistant/components/roku/translations/ru.json index 4ba55bc8e1a..76ac3a7f188 100644 --- a/homeassistant/components/roku/translations/ru.json +++ b/homeassistant/components/roku/translations/ru.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?", - "title": "Roku" + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/sl.json b/homeassistant/components/roku/translations/sl.json index 4a198b3b5c9..bb39edf9753 100644 --- a/homeassistant/components/roku/translations/sl.json +++ b/homeassistant/components/roku/translations/sl.json @@ -15,9 +15,7 @@ "one": "ena", "other": "drugo", "two": "dva" - }, - "description": "Ali \u017eelite nastaviti {name}?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/sv.json b/homeassistant/components/roku/translations/sv.json index 524e7753548..4272f65b2ae 100644 --- a/homeassistant/components/roku/translations/sv.json +++ b/homeassistant/components/roku/translations/sv.json @@ -5,10 +5,6 @@ }, "flow_title": "Roku: {name}", "step": { - "ssdp_confirm": { - "description": "Vill du konfigurera {name}?", - "title": "Roku" - }, "user": { "data": { "host": "V\u00e4rd" diff --git a/homeassistant/components/roku/translations/tr.json b/homeassistant/components/roku/translations/tr.json index 3e930a6c1b2..23cc6e56bd5 100644 --- a/homeassistant/components/roku/translations/tr.json +++ b/homeassistant/components/roku/translations/tr.json @@ -15,16 +15,13 @@ "one": "Bo\u015f", "other": "Bo\u015f" }, - "description": "{name} kurmak istiyor musunuz?", - "title": "Roku" + "description": "{name} kurmak istiyor musunuz?" }, "ssdp_confirm": { "data": { "one": "Bo\u015f", "other": "Bo\u015f" - }, - "description": "{name} kurmak istiyor musunuz?", - "title": "Roku" + } }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/uk.json b/homeassistant/components/roku/translations/uk.json index b7db8875f8e..8fd1ae7f630 100644 --- a/homeassistant/components/roku/translations/uk.json +++ b/homeassistant/components/roku/translations/uk.json @@ -10,12 +10,7 @@ "flow_title": "Roku: {name}", "step": { "discovery_confirm": { - "description": "\u0412\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 {name}?", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 {name}?", - "title": "Roku" + "description": "\u0412\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 {name}?" }, "user": { "data": { diff --git a/homeassistant/components/roku/translations/zh-Hant.json b/homeassistant/components/roku/translations/zh-Hant.json index 5cfe9232301..13c5886b46d 100644 --- a/homeassistant/components/roku/translations/zh-Hant.json +++ b/homeassistant/components/roku/translations/zh-Hant.json @@ -11,12 +11,7 @@ "flow_title": "{name}", "step": { "discovery_confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f", - "title": "Roku" - }, - "ssdp_confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f", - "title": "Roku" + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name}\uff1f" }, "user": { "data": { diff --git a/homeassistant/components/roomba/translations/bg.json b/homeassistant/components/roomba/translations/bg.json index 74075c671a1..3da613d9394 100644 --- a/homeassistant/components/roomba/translations/bg.json +++ b/homeassistant/components/roomba/translations/bg.json @@ -4,9 +4,6 @@ "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "step": { - "init": { - "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" - }, "link_manual": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u0430" @@ -18,8 +15,7 @@ }, "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430" + "host": "\u0425\u043e\u0441\u0442" }, "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" } diff --git a/homeassistant/components/roomba/translations/ca.json b/homeassistant/components/roomba/translations/ca.json index ba23e30e3f0..de0f673e971 100644 --- a/homeassistant/components/roomba/translations/ca.json +++ b/homeassistant/components/roomba/translations/ca.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Amfitri\u00f3" - }, - "description": "Selecciona un/a Roomba o Braava.", - "title": "Connexi\u00f3 autom\u00e0tica amb el dispositiu" - }, "link": { "description": "Mant\u00e9 premut el bot\u00f3 d'inici a {name} fins que el dispositiu emeti un so (aproximadament dos segons) despr\u00e9s, envia en els seg\u00fcents 30 segons.", "title": "Recupera la contrasenya" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Amfitri\u00f3" }, "description": "No s'ha descobert cap Roomba o Braava a la teva xarxa.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Cont\u00ednua", - "delay": "Retard", - "host": "Amfitri\u00f3", - "password": "Contrasenya" + "host": "Amfitri\u00f3" }, "description": "Selecciona un/a Roomba o Braava.", "title": "Connexi\u00f3 autom\u00e0tica amb el dispositiu" diff --git a/homeassistant/components/roomba/translations/cs.json b/homeassistant/components/roomba/translations/cs.json index d94d39f8136..0d42bf1b5e5 100644 --- a/homeassistant/components/roomba/translations/cs.json +++ b/homeassistant/components/roomba/translations/cs.json @@ -9,11 +9,6 @@ }, "flow_title": "iRobot {name} ({host})", "step": { - "init": { - "data": { - "host": "Hostitel" - } - }, "link_manual": { "data": { "password": "Heslo" @@ -26,9 +21,7 @@ }, "user": { "data": { - "delay": "Zpo\u017ed\u011bn\u00ed", - "host": "Hostitel", - "password": "Heslo" + "host": "Hostitel" }, "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed" } diff --git a/homeassistant/components/roomba/translations/da.json b/homeassistant/components/roomba/translations/da.json index 2a8057ffc95..97ea58b2c51 100644 --- a/homeassistant/components/roomba/translations/da.json +++ b/homeassistant/components/roomba/translations/da.json @@ -2,9 +2,6 @@ "config": { "step": { "user": { - "data": { - "delay": "Forsinkelse" - }, "title": "Tilslut automatisk til enheden" } } diff --git a/homeassistant/components/roomba/translations/de.json b/homeassistant/components/roomba/translations/de.json index ae4ef7d9dc7..1717c07a735 100644 --- a/homeassistant/components/roomba/translations/de.json +++ b/homeassistant/components/roomba/translations/de.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "W\u00e4hle einen Roomba oder Braava aus.", - "title": "Automatisch mit dem Ger\u00e4t verbinden" - }, "link": { "description": "Halte die Home-Taste von {name} gedr\u00fcckt, bis das Ger\u00e4t einen Ton erzeugt (ca. zwei Sekunden) und sende die Best\u00e4tigung innerhalb von 30 Sekunden ab.", "title": "Passwort abrufen" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "Es wurde kein Roomba oder Braava in deinem Netzwerk entdeckt.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Kontinuierlich", - "delay": "Verz\u00f6gerung", - "host": "Host", - "password": "Passwort" + "host": "Host" }, "description": "W\u00e4hle einen Roomba oder Braava aus.", "title": "Automatisch mit dem Ger\u00e4t verbinden" diff --git a/homeassistant/components/roomba/translations/el.json b/homeassistant/components/roomba/translations/el.json index e59ce426deb..cb8683f2be2 100644 --- a/homeassistant/components/roomba/translations/el.json +++ b/homeassistant/components/roomba/translations/el.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 Roomba \u03ae Braava.", - "title": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" - }, "link": { "description": "\u03a0\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03ba\u03b1\u03b9 \u03ba\u03c1\u03b1\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c0\u03b1\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03c4\u03bf \u03c0\u03bb\u03ae\u03ba\u03c4\u03c1\u03bf Home \u03c3\u03c4\u03bf {name} \u03bc\u03ad\u03c7\u03c1\u03b9 \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03bd\u03b1 \u03c0\u03b1\u03c1\u03ac\u03b3\u03b5\u03b9 \u03ad\u03bd\u03b1\u03bd \u03ae\u03c7\u03bf (\u03c0\u03b5\u03c1\u03af\u03c0\u03bf\u03c5 \u03b4\u03cd\u03bf \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1) \u03ba\u03b1\u03b9, \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1, \u03c5\u03c0\u03bf\u03b2\u03ac\u03bb\u03b5\u03c4\u03b5 \u03b5\u03bd\u03c4\u03cc\u03c2 30 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03bf\u03bb\u03ad\u03c0\u03c4\u03c9\u03bd.", "title": "\u0391\u03bd\u03ac\u03ba\u03c4\u03b7\u03c3\u03b7 \u03ba\u03c9\u03b4\u03b9\u03ba\u03bf\u03cd" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, "description": "\u0394\u03b5\u03bd \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bd\u03b1\u03ba\u03b1\u03bb\u03c5\u03c6\u03b8\u03b5\u03af Roomba \u03ae Braava \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03cc \u03c3\u03b1\u03c2.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03ae\u03c2", - "delay": "\u039a\u03b1\u03b8\u03c5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7", - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" + "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" }, "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 Roomba \u03ae Braava.", "title": "\u0391\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" diff --git a/homeassistant/components/roomba/translations/en.json b/homeassistant/components/roomba/translations/en.json index facd127985a..703ccebbb11 100644 --- a/homeassistant/components/roomba/translations/en.json +++ b/homeassistant/components/roomba/translations/en.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Select a Roomba or Braava.", - "title": "Automatically connect to the device" - }, "link": { "description": "Press and hold the Home button on {name} until the device generates a sound (about two seconds), then submit within 30 seconds.", "title": "Retrieve Password" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "No Roomba or Braava have been discovered on your network.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Continuous", - "delay": "Delay", - "host": "Host", - "password": "Password" + "host": "Host" }, "description": "Select a Roomba or Braava.", "title": "Automatically connect to the device" diff --git a/homeassistant/components/roomba/translations/es-419.json b/homeassistant/components/roomba/translations/es-419.json index 6d0295f7ce0..4fe61c8950c 100644 --- a/homeassistant/components/roomba/translations/es-419.json +++ b/homeassistant/components/roomba/translations/es-419.json @@ -6,11 +6,7 @@ "step": { "user": { "data": { - "blid": "BLID", - "continuous": "Continuo", - "delay": "Retraso", - "host": "Nombre de host o direcci\u00f3n IP", - "password": "Contrase\u00f1a" + "host": "Nombre de host o direcci\u00f3n IP" }, "description": "Actualmente recuperar el BLID y la contrase\u00f1a es un proceso manual. Siga los pasos descritos en la documentaci\u00f3n en: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "Conectarse al dispositivo" diff --git a/homeassistant/components/roomba/translations/es.json b/homeassistant/components/roomba/translations/es.json index c8760f75fdd..f2664baee31 100644 --- a/homeassistant/components/roomba/translations/es.json +++ b/homeassistant/components/roomba/translations/es.json @@ -11,13 +11,6 @@ }, "flow_title": "iRobot {name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Selecciona una Roomba o Braava.", - "title": "Conexi\u00f3n autom\u00e1tica con el dispositivo" - }, "link": { "description": "Mant\u00e9n pulsado el bot\u00f3n Inicio en {name} hasta que el dispositivo genere un sonido (aproximadamente dos segundos).", "title": "Recuperar la contrase\u00f1a" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "No se ha descubierto ning\u00fan dispositivo Roomba ni Braava en tu red.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Continuo", - "delay": "Retardo", - "host": "Host", - "password": "Contrase\u00f1a" + "host": "Host" }, "description": "Actualmente recuperar el BLID y la contrase\u00f1a es un proceso manual. Sigue los pasos descritos en la documentaci\u00f3n en: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "Conexi\u00f3n autom\u00e1tica con el dispositivo" diff --git a/homeassistant/components/roomba/translations/et.json b/homeassistant/components/roomba/translations/et.json index 43715399ef1..2cc599ca8ee 100644 --- a/homeassistant/components/roomba/translations/et.json +++ b/homeassistant/components/roomba/translations/et.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ( {host} )", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Vali Roomba v\u00f5i Braava seade.", - "title": "\u00dchenda seadmega automaatselt" - }, "link": { "description": "Vajuta ja hoia all seadme {name} nuppu Home kuni seade teeb piiksu (umbes kaks sekundit), edasta 30 sekundi jooksul.", "title": "Hangi salas\u00f5na" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "", "host": "Host" }, "description": "V\u00f5rgus ei tuvastatud \u00fchtegi Roomba ega Braava seadet.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "", - "continuous": "Pidev", - "delay": "Viivitus", - "host": "", - "password": "Salas\u00f5na" + "host": "" }, "description": "Vali Roomba v\u00f5i Braava seade", "title": "\u00dchenda seadmega automaatselt" diff --git a/homeassistant/components/roomba/translations/fr.json b/homeassistant/components/roomba/translations/fr.json index 5cbb1090cc0..0f00f6fae61 100644 --- a/homeassistant/components/roomba/translations/fr.json +++ b/homeassistant/components/roomba/translations/fr.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "H\u00f4te" - }, - "description": "S\u00e9lectionnez un Roomba ou un Braava.", - "title": "Se connecter automatiquement \u00e0 l'appareil" - }, "link": { "description": "Appuyez sur le bouton Accueil et maintenez-le enfonc\u00e9 jusqu'\u00e0 ce que l'appareil \u00e9mette un son (environ deux secondes).", "title": "R\u00e9cup\u00e9rer le mot de passe" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "H\u00f4te" }, "description": "Aucun Roomba ou Braava n'a \u00e9t\u00e9 d\u00e9couvert sur votre r\u00e9seau.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "En continu", - "delay": "D\u00e9lai", - "host": "H\u00f4te", - "password": "Mot de passe" + "host": "H\u00f4te" }, "description": "La r\u00e9cup\u00e9ration du BLID et du mot de passe est actuellement un processus manuel. Veuillez suivre les \u00e9tapes d\u00e9crites dans la documentation \u00e0 l'adresse: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "Se connecter automatiquement \u00e0 l'appareil" diff --git a/homeassistant/components/roomba/translations/he.json b/homeassistant/components/roomba/translations/he.json index 4520671eedb..d61ade0479a 100644 --- a/homeassistant/components/roomba/translations/he.json +++ b/homeassistant/components/roomba/translations/he.json @@ -9,11 +9,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7" - } - }, "link": { "title": "\u05d0\u05d7\u05d6\u05e8 \u05e1\u05d9\u05e1\u05de\u05d4" }, @@ -31,8 +26,7 @@ }, "user": { "data": { - "host": "\u05de\u05d0\u05e8\u05d7", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4" + "host": "\u05de\u05d0\u05e8\u05d7" }, "description": "\u05d1\u05d7\u05d9\u05e8\u05ea Roomba \u05d0\u05d5 Braava." } diff --git a/homeassistant/components/roomba/translations/hu.json b/homeassistant/components/roomba/translations/hu.json index dd1c74cc0b6..e109b6e7043 100644 --- a/homeassistant/components/roomba/translations/hu.json +++ b/homeassistant/components/roomba/translations/hu.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "C\u00edm" - }, - "description": "V\u00e1lasszon egy Roomba vagy Braava k\u00e9sz\u00fcl\u00e9ket.", - "title": "Automatikus csatlakoz\u00e1s az eszk\u00f6zh\u00f6z" - }, "link": { "description": "Nyomja meg \u00e9s tartsa lenyomva a {name} Home gombj\u00e1t, am\u00edg az eszk\u00f6z hangot ad (kb. k\u00e9t m\u00e1sodperc), majd engedje el 30 m\u00e1sodpercen bel\u00fcl.", "title": "Jelsz\u00f3 lek\u00e9r\u00e9se" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "C\u00edm" }, "description": "A h\u00e1l\u00f3zaton egyetlen Roomba vagy Braava sem ker\u00fclt el\u0151.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Folyamatos", - "delay": "K\u00e9sleltet\u00e9s", - "host": "C\u00edm", - "password": "Jelsz\u00f3" + "host": "C\u00edm" }, "description": "V\u00e1lasszon Roomba-t vagy Braava-t.", "title": "Automatikus csatlakoz\u00e1s az eszk\u00f6zh\u00f6z" diff --git a/homeassistant/components/roomba/translations/id.json b/homeassistant/components/roomba/translations/id.json index 1ade232fd70..9b8e9ef8bbe 100644 --- a/homeassistant/components/roomba/translations/id.json +++ b/homeassistant/components/roomba/translations/id.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Pilih Roomba atau Braava.", - "title": "Sambungkan secara otomatis ke perangkat" - }, "link": { "description": "Tekan dan tahan tombol Home pada {name} hingga perangkat mengeluarkan suara (sekitar dua detik), lalu kirim dalam waktu 30 detik.", "title": "Ambil Kata Sandi" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "Tidak ada Roomba atau Braava yang ditemukan di jaringan Anda.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Terus menerus", - "delay": "Tunda", - "host": "Host", - "password": "Kata Sandi" + "host": "Host" }, "description": "Pilih Roomba atau Braava.", "title": "Sambungkan secara otomatis ke perangkat" diff --git a/homeassistant/components/roomba/translations/it.json b/homeassistant/components/roomba/translations/it.json index 4be1be53540..b87a7cb8ef6 100644 --- a/homeassistant/components/roomba/translations/it.json +++ b/homeassistant/components/roomba/translations/it.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Seleziona un Roomba o un Braava.", - "title": "Connettiti automaticamente al dispositivo" - }, "link": { "description": "Tieni premuto il pulsante Home su {name} fino a quando il dispositivo non genera un suono (circa due secondi), quindi invialo entro 30 secondi.", "title": "Recupera password" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "Nessun Roomba o Braava \u00e8 stato rilevato sulla rete.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Continuo", - "delay": "Ritardo", - "host": "Host", - "password": "Password" + "host": "Host" }, "description": "Seleziona un Roomba o un Braava.", "title": "Connetti automaticamente al dispositivo" diff --git a/homeassistant/components/roomba/translations/ja.json b/homeassistant/components/roomba/translations/ja.json index d7018e544ce..4cbdfd2a8bb 100644 --- a/homeassistant/components/roomba/translations/ja.json +++ b/homeassistant/components/roomba/translations/ja.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "\u30db\u30b9\u30c8" - }, - "description": "\u30eb\u30f3\u30d0\u307e\u305f\u306f\u30d6\u30e9\u30fc\u30d0\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "\u81ea\u52d5\u7684\u306b\u30c7\u30d0\u30a4\u30b9\u306b\u63a5\u7d9a\u3059\u308b" - }, "link": { "description": "{name} \u306e\u30db\u30fc\u30e0\u30dc\u30bf\u30f3\u3092\u3001\u30c7\u30d0\u30a4\u30b9\u304c\u97f3\u3092\u51fa\u3059\u307e\u3067(\u7d042\u79d2)\u62bc\u3057\u7d9a\u3051\u300130\u79d2\u4ee5\u5185\u306b\u9001\u4fe1(submit)\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u53d6\u5f97" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "\u30db\u30b9\u30c8" }, "description": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u3001\u30eb\u30f3\u30d0\u3084\u30d6\u30e9\u30fc\u30d0\u304c\u767a\u898b\u3055\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "\u9023\u7d9a", - "delay": "\u9045\u5ef6", - "host": "\u30db\u30b9\u30c8", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" + "host": "\u30db\u30b9\u30c8" }, "description": "\u30eb\u30f3\u30d0\u307e\u305f\u306f\u30d6\u30e9\u30fc\u30d0\u3092\u9078\u629e\u3057\u307e\u3059\u3002", "title": "\u81ea\u52d5\u7684\u306b\u30c7\u30d0\u30a4\u30b9\u306b\u63a5\u7d9a\u3059\u308b" diff --git a/homeassistant/components/roomba/translations/ko.json b/homeassistant/components/roomba/translations/ko.json index bb33287c9b8..70ebc3b6ef4 100644 --- a/homeassistant/components/roomba/translations/ko.json +++ b/homeassistant/components/roomba/translations/ko.json @@ -11,13 +11,6 @@ }, "flow_title": "\uc544\uc774\ub85c\ubd07: {name} ({host})", "step": { - "init": { - "data": { - "host": "\ud638\uc2a4\ud2b8" - }, - "description": "\ub8f8\ubc14 \ub610\ub294 \ube0c\ub77c\ubc14\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", - "title": "\uae30\uae30\uc5d0 \uc790\ub3d9\uc73c\ub85c \uc5f0\uacb0\ud558\uae30" - }, "link": { "description": "\uae30\uae30\uc5d0\uc11c \uc18c\ub9ac\uac00 \ub0a0 \ub54c\uae4c\uc9c0 {name}\uc758 \ud648 \ubc84\ud2bc\uc744 \uae38\uac8c \ub204\ub978 \ub2e4\uc74c(\uc57d 2\ucd08) 30\ucd08 \uc774\ub0b4\uc5d0 \ud655\uc778 \ubc84\ud2bc\uc744 \ub20c\ub7ec\uc8fc\uc138\uc694.", "title": "\ube44\ubc00\ubc88\ud638 \uac00\uc838\uc624\uae30" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "\ud638\uc2a4\ud2b8" }, "description": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub8f8\ubc14 \ub610\ub294 \ube0c\ub77c\ubc14\uac00 \ubc1c\uacac\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. BLID\ub294 `iRobot-` \ub610\ub294 `Roomba-` \ub4a4\uc5d0 \uc788\ub294 \uae30\uae30 \ud638\uc2a4\ud2b8 \uc774\ub984\uc758 \uc77c\ubd80\uc785\ub2c8\ub2e4. \uad00\ub828 \ubb38\uc11c\uc5d0 \uc124\uba85\ub41c \ub2e8\uacc4\ub97c \ub530\ub77c\uc8fc\uc138\uc694: {auth_help_url}", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "\uc5f0\uc18d", - "delay": "\uc9c0\uc5f0", - "host": "\ud638\uc2a4\ud2b8", - "password": "\ube44\ubc00\ubc88\ud638" + "host": "\ud638\uc2a4\ud2b8" }, "description": "\ud604\uc7ac BLID \ubc0f \ube44\ubc00\ubc88\ud638\ub294 \uc218\ub3d9\uc73c\ub85c \uac00\uc838\uc640\uc57c\ud569\ub2c8\ub2e4. \ub2e4\uc74c \uad00\ub828 \ubb38\uc11c\uc5d0 \ub098\uc640 \uc788\ub294 \ub2e8\uacc4\ub97c \ub530\ub77c\uc8fc\uc138\uc694: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "\uae30\uae30\uc5d0 \uc5f0\uacb0\ud558\uae30" diff --git a/homeassistant/components/roomba/translations/lb.json b/homeassistant/components/roomba/translations/lb.json index 5578a7710db..b2c0b85d16a 100644 --- a/homeassistant/components/roomba/translations/lb.json +++ b/homeassistant/components/roomba/translations/lb.json @@ -8,13 +8,6 @@ }, "flow_title": "iRobot {name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Ee Roomba oder Bravaa auswielen.", - "title": "Automatesch mam Apparat verbannen" - }, "link": { "title": "Passwuert ausliesen" }, @@ -26,18 +19,13 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "title": "Manuell mam Apparat verbannen" }, "user": { "data": { - "blid": "BLID", - "continuous": "Kontinu\u00e9ierlech", - "delay": "Delai", - "host": "Host", - "password": "Passwuert" + "host": "Host" }, "description": "De Prozess fir BLID an Passwuert opzeruffen ass fir de Moment manuell. Folleg w.e.g. de Schr\u00ebtt d\u00e9i an der Dokumentatioun op https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials beschriwwe sinn.", "title": "Mam Apparat verbannen" diff --git a/homeassistant/components/roomba/translations/nl.json b/homeassistant/components/roomba/translations/nl.json index bbb4fdbe765..68e18d7db93 100644 --- a/homeassistant/components/roomba/translations/nl.json +++ b/homeassistant/components/roomba/translations/nl.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Host" - }, - "description": "Kies een Roomba of Braava.", - "title": "Automatisch verbinding maken met het apparaat" - }, "link": { "description": "Houd de Home-knop op {name} ingedrukt totdat het apparaat een geluid genereert (ongeveer twee seconden), bevestig vervolgens binnen 30 seconden.", "title": "Wachtwoord opvragen" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Host" }, "description": "Er is geen Roomba of Braava ontdekt op uw netwerk.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Doorlopend", - "delay": "Vertraging", - "host": "Host", - "password": "Wachtwoord" + "host": "Host" }, "description": "Kies een Roomba of Braava.", "title": "Automatisch verbinding maken met het apparaat" diff --git a/homeassistant/components/roomba/translations/no.json b/homeassistant/components/roomba/translations/no.json index 3a5d95eb006..f378bfb127c 100644 --- a/homeassistant/components/roomba/translations/no.json +++ b/homeassistant/components/roomba/translations/no.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Vert" - }, - "description": "Velg en Roomba eller Braava", - "title": "Koble automatisk til enheten" - }, "link": { "description": "Trykk og hold nede Hjem-knappen p\u00e5 {name} til enheten genererer en lyd (omtrent to sekunder), og send deretter innen 30 sekunder.", "title": "Hent passord" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "", "host": "Vert" }, "description": "Ingen Roomba eller Braava er oppdaget p\u00e5 nettverket ditt.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "Blid", - "continuous": "Kontinuerlige", - "delay": "Forsinkelse", - "host": "Vert", - "password": "Passord" + "host": "Vert" }, "description": "Velg en Roomba eller Braava.", "title": "Koble automatisk til enheten" diff --git a/homeassistant/components/roomba/translations/pl.json b/homeassistant/components/roomba/translations/pl.json index d2d76bee5ff..a3440c97ed2 100644 --- a/homeassistant/components/roomba/translations/pl.json +++ b/homeassistant/components/roomba/translations/pl.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Nazwa hosta lub adres IP" - }, - "description": "Wybierz Roomb\u0119 lub Braava", - "title": "Automatyczne po\u0142\u0105czenie z urz\u0105dzeniem" - }, "link": { "description": "Naci\u015bnij i przytrzymaj przycisk Home na {name} a\u017c urz\u0105dzenie wygeneruje d\u017awi\u0119k (oko\u0142o dwie sekundy), a nast\u0119pnie zatwierd\u017a w ci\u0105gu 30 sekund.", "title": "Odzyskiwanie has\u0142a" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Nazwa hosta lub adres IP" }, "description": "W Twojej sieci nie wykryto urz\u0105dzenia Roomba ani Braava.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Ci\u0105g\u0142y", - "delay": "Op\u00f3\u017anienie", - "host": "Nazwa hosta lub adres IP", - "password": "Has\u0142o" + "host": "Nazwa hosta lub adres IP" }, "description": "Wybierz Roomb\u0119 lub Braava", "title": "Automatyczne po\u0142\u0105czenie z urz\u0105dzeniem" diff --git a/homeassistant/components/roomba/translations/pt-BR.json b/homeassistant/components/roomba/translations/pt-BR.json index ee0a29eac89..4db4e885ac7 100644 --- a/homeassistant/components/roomba/translations/pt-BR.json +++ b/homeassistant/components/roomba/translations/pt-BR.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ( {host} )", "step": { - "init": { - "data": { - "host": "Nome do host" - }, - "description": "Selecione um Roomba ou Braava.", - "title": "Conecte-se automaticamente ao dispositivo" - }, "link": { "description": "Pressione e segure o bot\u00e3o Home em {name} at\u00e9 que o dispositivo gere um som (cerca de dois segundos) e envie em 30 segundos.", "title": "Recuperar Senha" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Nome do host" }, "description": "Nenhum Roomba ou Braava foi descoberto em sua rede.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "Cont\u00ednuo", - "delay": "Atraso", - "host": "Nome do host", - "password": "Senha" + "host": "Nome do host" }, "description": "Selecione um Roomba ou Braava.", "title": "Conecte-se ao dispositivo" diff --git a/homeassistant/components/roomba/translations/pt.json b/homeassistant/components/roomba/translations/pt.json index 6036e870e6c..84fe0402f15 100644 --- a/homeassistant/components/roomba/translations/pt.json +++ b/homeassistant/components/roomba/translations/pt.json @@ -13,10 +13,7 @@ }, "user": { "data": { - "continuous": "Cont\u00ednuo", - "delay": "Atraso", - "host": "Servidor", - "password": "Palavra-passe" + "host": "Servidor" }, "title": "Conectar ao dispositivo" } diff --git a/homeassistant/components/roomba/translations/ru.json b/homeassistant/components/roomba/translations/ru.json index a8b31297f04..643da09744c 100644 --- a/homeassistant/components/roomba/translations/ru.json +++ b/homeassistant/components/roomba/translations/ru.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u044b\u043b\u0435\u0441\u043e\u0441 \u0438\u0437 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 Roomba \u0438\u043b\u0438 Braava.", - "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" - }, "link": { "description": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u0438 \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 Home \u043d\u0430 {name}, \u043f\u043e\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0438\u0437\u0434\u0430\u0441\u0442 \u0437\u0432\u0443\u043a (\u043e\u043a\u043e\u043b\u043e \u0434\u0432\u0443\u0445 \u0441\u0435\u043a\u0443\u043d\u0434). \u0417\u0430\u0442\u0435\u043c \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 30 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\".", "title": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "\u0425\u043e\u0441\u0442" }, "description": "\u0412 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 Roomba \u0438\u043b\u0438 Braava.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "\u041d\u0435\u043f\u0440\u0435\u0440\u044b\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", - "delay": "\u0417\u0430\u0434\u0435\u0440\u0436\u043a\u0430 (\u0441\u0435\u043a.)", - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + "host": "\u0425\u043e\u0441\u0442" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u044b\u043b\u0435\u0441\u043e\u0441 \u0438\u0437 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 Roomba \u0438\u043b\u0438 Braava.", "title": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443" diff --git a/homeassistant/components/roomba/translations/sl.json b/homeassistant/components/roomba/translations/sl.json index 9ad90ab82aa..ac046ac6ca7 100644 --- a/homeassistant/components/roomba/translations/sl.json +++ b/homeassistant/components/roomba/translations/sl.json @@ -6,11 +6,7 @@ "step": { "user": { "data": { - "blid": "BLID", - "continuous": "Nenehno", - "delay": "Zamik", - "host": "Ime gostitelja ali naslov IP", - "password": "Geslo" + "host": "Ime gostitelja ali naslov IP" }, "description": "Trenutno je pridobivanje BLID-a in gesla ro\u010dni postopek. Upo\u0161tevajte korake opisane v dokumentaciji na: https://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "Pove\u017eite se z napravo" diff --git a/homeassistant/components/roomba/translations/sv.json b/homeassistant/components/roomba/translations/sv.json index ee1f8972ef9..e2c491df80b 100644 --- a/homeassistant/components/roomba/translations/sv.json +++ b/homeassistant/components/roomba/translations/sv.json @@ -6,11 +6,7 @@ "step": { "user": { "data": { - "blid": "BLID", - "continuous": "Kontinuerlig", - "delay": "F\u00f6rdr\u00f6jning", - "host": "V\u00e4rdnamn eller IP-adress", - "password": "L\u00f6senord" + "host": "V\u00e4rdnamn eller IP-adress" }, "title": "Anslut till enheten" } diff --git a/homeassistant/components/roomba/translations/tr.json b/homeassistant/components/roomba/translations/tr.json index 97b4b61ada2..ecdaa8b97be 100644 --- a/homeassistant/components/roomba/translations/tr.json +++ b/homeassistant/components/roomba/translations/tr.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "Ana Bilgisayar" - }, - "description": "Roomba veya Braava'y\u0131 se\u00e7in.", - "title": "Cihaza otomatik olarak ba\u011flan" - }, "link": { "description": "Cihaz bir ses \u00e7\u0131karana kadar (yakla\u015f\u0131k iki saniye) {name} \u00fczerindeki Ana Sayfa d\u00fc\u011fmesini bas\u0131l\u0131 tutun, ard\u0131ndan 30 saniye i\u00e7inde g\u00f6nderin.", "title": "\u015eifre Al" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "Sunucu" }, "description": "A\u011f\u0131n\u0131zda Roomba veya Braava bulunamad\u0131.", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "S\u00fcrekli", - "delay": "Gecikme", - "host": "Sunucu", - "password": "Parola" + "host": "Sunucu" }, "description": "Roomba veya Braava'y\u0131 se\u00e7in.", "title": "Cihaza ba\u011flan\u0131n" diff --git a/homeassistant/components/roomba/translations/uk.json b/homeassistant/components/roomba/translations/uk.json index 833a35f62f3..722b1127051 100644 --- a/homeassistant/components/roomba/translations/uk.json +++ b/homeassistant/components/roomba/translations/uk.json @@ -6,11 +6,7 @@ "step": { "user": { "data": { - "blid": "BLID", - "continuous": "\u0411\u0435\u0437\u043f\u0435\u0440\u0435\u0440\u0432\u043d\u0438\u0439 \u0440\u0435\u0436\u0438\u043c", - "delay": "\u0417\u0430\u0442\u0440\u0438\u043c\u043a\u0430 (\u0441\u0435\u043a.)", - "host": "\u0425\u043e\u0441\u0442", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c" + "host": "\u0425\u043e\u0441\u0442" }, "description": "\u041e\u0437\u043d\u0430\u0439\u043e\u043c\u0442\u0435\u0441\u044f \u0437 \u0456\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0456\u044f\u043c\u0438, \u0449\u043e\u0431 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u044f\u043a \u043e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 BLID \u0456 \u043f\u0430\u0440\u043e\u043b\u044c:\nhttps://www.home-assistant.io/integrations/roomba/#retrieving-your-credentials", "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e" diff --git a/homeassistant/components/roomba/translations/zh-Hant.json b/homeassistant/components/roomba/translations/zh-Hant.json index c17607e8be4..d0c59422625 100644 --- a/homeassistant/components/roomba/translations/zh-Hant.json +++ b/homeassistant/components/roomba/translations/zh-Hant.json @@ -11,13 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "init": { - "data": { - "host": "\u4e3b\u6a5f\u7aef" - }, - "description": "\u9078\u64c7 Roomba \u6216 Braava\u3002", - "title": "\u81ea\u52d5\u9023\u7dda\u81f3\u88dd\u7f6e" - }, "link": { "description": "\u8acb\u6309\u4f4f {name} \u4e0a\u7684 Home \u9375\u76f4\u5230\u88dd\u7f6e\u767c\u51fa\u8072\u97f3\uff08\u7d04\u5169\u79d2\uff09\uff0c\u7136\u5f8c\u65bc 30 \u79d2\u5167\u50b3\u9001\u3002", "title": "\u91cd\u7f6e\u5bc6\u78bc" @@ -31,7 +24,6 @@ }, "manual": { "data": { - "blid": "BLID", "host": "\u4e3b\u6a5f\u7aef" }, "description": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230 Roomba \u6216 Braava\u3002", @@ -39,11 +31,7 @@ }, "user": { "data": { - "blid": "BLID", - "continuous": "\u9023\u7e8c", - "delay": "\u5ef6\u9072", - "host": "\u4e3b\u6a5f\u7aef", - "password": "\u5bc6\u78bc" + "host": "\u4e3b\u6a5f\u7aef" }, "description": "\u9078\u64c7 Roomba \u6216 Braava\u3002", "title": "\u81ea\u52d5\u9023\u7dda\u81f3\u88dd\u7f6e" diff --git a/homeassistant/components/roon/translations/bg.json b/homeassistant/components/roon/translations/bg.json index 5738012f715..54c68cff37a 100644 --- a/homeassistant/components/roon/translations/bg.json +++ b/homeassistant/components/roon/translations/bg.json @@ -13,11 +13,6 @@ "host": "\u0425\u043e\u0441\u0442", "port": "\u041f\u043e\u0440\u0442" } - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - } } } } diff --git a/homeassistant/components/roon/translations/ca.json b/homeassistant/components/roon/translations/ca.json index 3f601ded490..860ad4cff38 100644 --- a/homeassistant/components/roon/translations/ca.json +++ b/homeassistant/components/roon/translations/ca.json @@ -18,12 +18,6 @@ "link": { "description": "Has d'autoritzar Home Assistant a Roon. Despr\u00e9s de fer clic a envia, ves a l'aplicaci\u00f3 Roon Core, obre la Configuraci\u00f3 i activa Home Assistant a la pestanya d'Extensions.", "title": "Autoritza Home Assistant a Roon" - }, - "user": { - "data": { - "host": "Amfitri\u00f3" - }, - "description": "No s'ha pogut descobrir el servidor Roon, introdueix el nom d'amfitri\u00f3 o la IP." } } } diff --git a/homeassistant/components/roon/translations/cs.json b/homeassistant/components/roon/translations/cs.json index 20187f9d4d2..150b2f5f74a 100644 --- a/homeassistant/components/roon/translations/cs.json +++ b/homeassistant/components/roon/translations/cs.json @@ -11,11 +11,6 @@ "link": { "description": "Mus\u00edte povolit Home Assistant v Roon. Po kliknut\u00ed na Odeslat p\u0159ejd\u011bte do aplikace Roon Core, otev\u0159ete Nastaven\u00ed a na z\u00e1lo\u017ece Roz\u0161\u00ed\u0159en\u00ed povolte Home Assistant.", "title": "Autorizujte HomeAssistant v Roon" - }, - "user": { - "data": { - "host": "Hostitel" - } } } } diff --git a/homeassistant/components/roon/translations/de.json b/homeassistant/components/roon/translations/de.json index e41fe77c3cd..377cae8946f 100644 --- a/homeassistant/components/roon/translations/de.json +++ b/homeassistant/components/roon/translations/de.json @@ -18,12 +18,6 @@ "link": { "description": "Du musst den Home Assistant in Roon autorisieren. Nachdem du auf \"Submit\" geklickt hast, gehe zur Roon Core-Anwendung, \u00f6ffne die Einstellungen und aktiviere HomeAssistant auf der Registerkarte \"Extensions\".", "title": "HomeAssistant in Roon autorisieren" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "Roon-Server konnte nicht gefunden werden, bitte gib den Hostnamen oder die IP ein." } } } diff --git a/homeassistant/components/roon/translations/el.json b/homeassistant/components/roon/translations/el.json index 63cda0821b1..1216ff032a0 100644 --- a/homeassistant/components/roon/translations/el.json +++ b/homeassistant/components/roon/translations/el.json @@ -18,12 +18,6 @@ "link": { "description": "\u03a0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant \u03c3\u03c4\u03bf Roon. \u0391\u03c6\u03bf\u03cd \u03ba\u03ac\u03bd\u03b5\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03b2\u03bf\u03bb\u03ae, \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae Roon Core, \u03b1\u03bd\u03bf\u03af\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03b9 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf HomeAssistant \u03c3\u03c4\u03b7\u03bd \u03ba\u03b1\u03c1\u03c4\u03ad\u03bb\u03b1 \u0395\u03c0\u03b5\u03ba\u03c4\u03ac\u03c3\u03b5\u03b9\u03c2.", "title": "\u0395\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf HomeAssistant \u03c3\u03c4\u03bf Roon" - }, - "user": { - "data": { - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - }, - "description": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03cc\u03c2 \u03bf \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae Roon, \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03ba\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03bf\u03cd \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae \u03ae \u03c4\u03b7\u03bd IP \u03c3\u03b1\u03c2." } } } diff --git a/homeassistant/components/roon/translations/en.json b/homeassistant/components/roon/translations/en.json index b0affedc026..859f8c7307b 100644 --- a/homeassistant/components/roon/translations/en.json +++ b/homeassistant/components/roon/translations/en.json @@ -18,12 +18,6 @@ "link": { "description": "You must authorize Home Assistant in Roon. After you click submit, go to the Roon Core application, open Settings and enable HomeAssistant on the Extensions tab.", "title": "Authorize HomeAssistant in Roon" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "Could not discover Roon server, please enter your the Hostname or IP." } } } diff --git a/homeassistant/components/roon/translations/es.json b/homeassistant/components/roon/translations/es.json index 45038d8e7dd..098ba60234a 100644 --- a/homeassistant/components/roon/translations/es.json +++ b/homeassistant/components/roon/translations/es.json @@ -18,12 +18,6 @@ "link": { "description": "Debes autorizar Home Assistant en Roon. Despu\u00e9s de pulsar en Enviar, ve a la aplicaci\u00f3n Roon Core, abre Configuraci\u00f3n y activa HomeAssistant en la pesta\u00f1a Extensiones.", "title": "Autorizar HomeAssistant en Roon" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "No se ha podido descubrir el servidor Roon, introduce el nombre de host o la IP." } } } diff --git a/homeassistant/components/roon/translations/et.json b/homeassistant/components/roon/translations/et.json index d091f8e4064..3a7484680ea 100644 --- a/homeassistant/components/roon/translations/et.json +++ b/homeassistant/components/roon/translations/et.json @@ -18,12 +18,6 @@ "link": { "description": "Pead Roonis koduabilise volitama. Kui oled kl\u00f5psanud nuppu Esita, mine rakendusse Roon Core, ava Seaded ja luba vahekaardil Laiendused Home Assistant.", "title": "Volita HomeAssistant Roonis" - }, - "user": { - "data": { - "host": "" - }, - "description": "Rooni serverit ei leitud. Sisesta oma Rooni serveri hostinimi v\u00f5i IP." } } } diff --git a/homeassistant/components/roon/translations/fr.json b/homeassistant/components/roon/translations/fr.json index 7af98de188a..279ef703c59 100644 --- a/homeassistant/components/roon/translations/fr.json +++ b/homeassistant/components/roon/translations/fr.json @@ -18,12 +18,6 @@ "link": { "description": "Vous devez autoriser Home Assistant dans Roon. Apr\u00e8s avoir cliqu\u00e9 sur soumettre, acc\u00e9dez \u00e0 l'application Roon Core, ouvrez Param\u00e8tres et activez Home Assistant dans l'onglet Extensions.", "title": "Autoriser Home Assistant dans Roon" - }, - "user": { - "data": { - "host": "H\u00f4te" - }, - "description": "Veuillez entrer votre nom d\u2019h\u00f4te ou votre adresse IP sur votre serveur Roon." } } } diff --git a/homeassistant/components/roon/translations/he.json b/homeassistant/components/roon/translations/he.json index 05781910772..0c0d772740b 100644 --- a/homeassistant/components/roon/translations/he.json +++ b/homeassistant/components/roon/translations/he.json @@ -16,12 +16,6 @@ }, "link": { "description": "\u05e2\u05dc\u05d9\u05da \u05dc\u05d0\u05e9\u05e8 \u05dc-Home Assistant \u05d1-Roon. \u05dc\u05d0\u05d7\u05e8 \u05e9\u05ea\u05dc\u05d7\u05e5 \u05e2\u05dc \u05e9\u05dc\u05d7, \u05e2\u05d1\u05d5\u05e8 \u05dc\u05d9\u05d9\u05e9\u05d5\u05dd Roon Core, \u05e4\u05ea\u05d7 \u05d0\u05ea \u05d4\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d5\u05d4\u05e4\u05e2\u05dc \u05d0\u05ea HomeAssistant \u05d1\u05db\u05e8\u05d8\u05d9\u05e1\u05d9\u05d9\u05d4 \u05d4\u05e8\u05d7\u05d1\u05d5\u05ea." - }, - "user": { - "data": { - "host": "\u05de\u05d0\u05e8\u05d7" - }, - "description": "\u05dc\u05d0 \u05d4\u05d9\u05ea\u05d4 \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d2\u05dc\u05d5\u05ea \u05d0\u05ea \u05e9\u05e8\u05ea Roon, \u05d4\u05d6\u05df \u05d0\u05ea \u05e9\u05dd \u05d4\u05de\u05d0\u05e8\u05d7 \u05d0\u05d5 \u05d4-IP." } } } diff --git a/homeassistant/components/roon/translations/hu.json b/homeassistant/components/roon/translations/hu.json index 3a9bcf3f522..aa536aeeddd 100644 --- a/homeassistant/components/roon/translations/hu.json +++ b/homeassistant/components/roon/translations/hu.json @@ -20,10 +20,6 @@ "title": "Enged\u00e9lyezze a Home Assistant alkalmaz\u00e1st Roon-ban" }, "user": { - "data": { - "host": "C\u00edm" - }, - "description": "A Roon szerver nem tal\u00e1lhat\u00f3, adja meg a hosztnev\u00e9t vagy c\u00edm\u00e9t", "one": "\u00dcres", "other": "\u00dcres" } diff --git a/homeassistant/components/roon/translations/id.json b/homeassistant/components/roon/translations/id.json index 85c1519e7be..cb3aa92df8c 100644 --- a/homeassistant/components/roon/translations/id.json +++ b/homeassistant/components/roon/translations/id.json @@ -18,12 +18,6 @@ "link": { "description": "Anda harus mengotorisasi Home Assistant di Roon. Setelah Anda mengeklik kirim, buka aplikasi Roon Core, buka Pengaturan dan aktifkan HomeAssistant pada tab Ekstensi.", "title": "Otorisasi HomeAssistant di Roon" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "Tidak dapat menemukan server Roon, masukkan Nama Host atau IP Anda." } } } diff --git a/homeassistant/components/roon/translations/it.json b/homeassistant/components/roon/translations/it.json index 3236ebd0bb2..9a232281803 100644 --- a/homeassistant/components/roon/translations/it.json +++ b/homeassistant/components/roon/translations/it.json @@ -20,10 +20,6 @@ "title": "Autorizza HomeAssistant in Roon" }, "user": { - "data": { - "host": "Host" - }, - "description": "Impossibile individuare il server Roon, inserire l'hostname o l'IP.", "one": "Vuoto", "other": "Vuoti" } diff --git a/homeassistant/components/roon/translations/ja.json b/homeassistant/components/roon/translations/ja.json index 400d982d27c..12a1b074916 100644 --- a/homeassistant/components/roon/translations/ja.json +++ b/homeassistant/components/roon/translations/ja.json @@ -18,12 +18,6 @@ "link": { "description": "Roon\u3067Home Assistant\u3092\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u9001\u4fe1(submit) \u3092\u30af\u30ea\u30c3\u30af\u3057\u305f\u5f8c\u3001Roon Core\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u3001\u8a2d\u5b9a(Settings )\u3092\u958b\u304d\u3001\u6a5f\u80fd\u62e1\u5f35\u30bf\u30d6(extensions tab)\u3067Home Assistant\u3092\u6709\u52b9(enable )\u306b\u3057\u307e\u3059\u3002", "title": "Roon\u3067HomeAssistant\u3092\u8a8d\u8a3c\u3059\u308b" - }, - "user": { - "data": { - "host": "\u30db\u30b9\u30c8" - }, - "description": "Roon server\u3092\u691c\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3001 \u30db\u30b9\u30c8\u540d\u307e\u305f\u306f\u3001IP\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" } } } diff --git a/homeassistant/components/roon/translations/ko.json b/homeassistant/components/roon/translations/ko.json index a55249417af..96b9bf88e95 100644 --- a/homeassistant/components/roon/translations/ko.json +++ b/homeassistant/components/roon/translations/ko.json @@ -8,15 +8,16 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "fallback": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + }, + "description": "Roon \uc11c\ubc84\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ud638\uc2a4\ud2b8 \uc774\ub984\uacfc \ud3ec\ud2b8\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624." + }, "link": { "description": "Roon\uc5d0\uc11c Home Assistant\ub97c \uc778\uc99d\ud574\uc8fc\uc5b4\uc57c \ud569\ub2c8\ub2e4. \ud655\uc778\uc744 \ud074\ub9ad\ud55c \ud6c4 Roon Core \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc73c\ub85c \uc774\ub3d9\ud558\uc5ec \uc124\uc815\uc744 \uc5f4\uace0 \ud655\uc7a5 \ud0ed\uc5d0\uc11c Home Assistant\ub97c \ud65c\uc131\ud654\ud574\uc8fc\uc138\uc694.", "title": "Roon\uc5d0\uc11c HomeAssistant \uc778\uc99d\ud558\uae30" - }, - "user": { - "data": { - "host": "\ud638\uc2a4\ud2b8" - }, - "description": "Roon \uc11c\ubc84\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/roon/translations/lb.json b/homeassistant/components/roon/translations/lb.json index f5722ee75bb..6121d31b3ab 100644 --- a/homeassistant/components/roon/translations/lb.json +++ b/homeassistant/components/roon/translations/lb.json @@ -11,12 +11,6 @@ "link": { "description": "Du muss Home Assistant am Roon autoris\u00e9ieren. Nodeems Du op ofsch\u00e9cke geklickt hues, g\u00e9i an d'Roon Applikatioun, an d'Astellungen an aktiv\u00e9ier HomeAssistant an den Extensiounen.", "title": "HomeAssistant am Roon erlaaben" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "G\u00ebff den Numm oder IP-Adress vun dengem Roon Server un." } } } diff --git a/homeassistant/components/roon/translations/nl.json b/homeassistant/components/roon/translations/nl.json index 9aafadc918b..fb35f22ce27 100644 --- a/homeassistant/components/roon/translations/nl.json +++ b/homeassistant/components/roon/translations/nl.json @@ -18,12 +18,6 @@ "link": { "description": "U moet Home Assistant autoriseren in Roon. Nadat je op verzenden hebt geklikt, ga je naar de Roon Core-applicatie, open je Instellingen en schakel je Home Assistant in op het tabblad Extensies.", "title": "Autoriseer Home Assistant in Roon" - }, - "user": { - "data": { - "host": "Host" - }, - "description": "Kon de Roon-server niet vinden, voer de hostnaam of het IP-adres in." } } } diff --git a/homeassistant/components/roon/translations/no.json b/homeassistant/components/roon/translations/no.json index 11eb60a04ce..4d44ef15e87 100644 --- a/homeassistant/components/roon/translations/no.json +++ b/homeassistant/components/roon/translations/no.json @@ -18,12 +18,6 @@ "link": { "description": "Du m\u00e5 godkjenne Home Assistant i Roon. N\u00e5r du klikker send inn, g\u00e5r du til Roon Core-programmet, \u00e5pner innstillingene og aktiverer Home Assistant p\u00e5 utvidelser-fanen.", "title": "Autoriser Home Assistant i Roon" - }, - "user": { - "data": { - "host": "Vert" - }, - "description": "Kunne ikke oppdage Roon-serveren. Angi vertsnavnet eller IP-adressen." } } } diff --git a/homeassistant/components/roon/translations/pl.json b/homeassistant/components/roon/translations/pl.json index b45a7fcb562..068153e2572 100644 --- a/homeassistant/components/roon/translations/pl.json +++ b/homeassistant/components/roon/translations/pl.json @@ -18,12 +18,6 @@ "link": { "description": "Musisz autoryzowa\u0107 Home Assistant w Roon. Po klikni\u0119ciu przycisku \"Zatwierd\u017a\", przejd\u017a do aplikacji Roon Core, otw\u00f3rz \"Ustawienia\" i w\u0142\u0105cz Home Assistant w karcie \"Rozszerzenia\" (Extensions).", "title": "Autoryzuj Home Assistant w Roon" - }, - "user": { - "data": { - "host": "Nazwa hosta lub adres IP" - }, - "description": "Nie wykryto serwera Roon, wprowad\u017a nazw\u0119 hosta lub adres IP." } } } diff --git a/homeassistant/components/roon/translations/pt-BR.json b/homeassistant/components/roon/translations/pt-BR.json index 85628df4a7c..6875841568d 100644 --- a/homeassistant/components/roon/translations/pt-BR.json +++ b/homeassistant/components/roon/translations/pt-BR.json @@ -18,12 +18,6 @@ "link": { "description": "Voc\u00ea deve autorizar o Home Assistant no Roon. Depois de clicar em enviar, v\u00e1 para o aplicativo Roon principal, abra Configura\u00e7\u00f5es e habilite o HomeAssistant na aba Extens\u00f5es.", "title": "Autorizar HomeAssistant no Roon" - }, - "user": { - "data": { - "host": "Nome do host" - }, - "description": "Por favor, digite seu hostname ou IP do servidor Roon." } } } diff --git a/homeassistant/components/roon/translations/pt.json b/homeassistant/components/roon/translations/pt.json index 6e36ff769cd..1e12fdcfcba 100644 --- a/homeassistant/components/roon/translations/pt.json +++ b/homeassistant/components/roon/translations/pt.json @@ -6,13 +6,6 @@ "error": { "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" - }, - "step": { - "user": { - "data": { - "host": "Servidor" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/roon/translations/ru.json b/homeassistant/components/roon/translations/ru.json index cc89dc2d2dd..17a44c95cf9 100644 --- a/homeassistant/components/roon/translations/ru.json +++ b/homeassistant/components/roon/translations/ru.json @@ -18,12 +18,6 @@ "link": { "description": "\u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u00ab\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c\u00bb \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 Roon Core, \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u00ab\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\u00bb \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0435 HomeAssistant \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u00ab\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u00bb.", "title": "Roon" - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 Roon, \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0445\u043e\u0441\u0442\u0430 \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441." } } } diff --git a/homeassistant/components/roon/translations/tr.json b/homeassistant/components/roon/translations/tr.json index c18df90b9b8..a05dfed70f1 100644 --- a/homeassistant/components/roon/translations/tr.json +++ b/homeassistant/components/roon/translations/tr.json @@ -18,12 +18,6 @@ "link": { "description": "Roon'da HomeAssistant\u0131 yetkilendirmelisiniz. G\u00f6nder'e t\u0131klad\u0131ktan sonra, Roon Core uygulamas\u0131na gidin, Ayarlar'\u0131 a\u00e7\u0131n ve Uzant\u0131lar sekmesinde HomeAssistant'\u0131 etkinle\u015ftirin.", "title": "Roon'da HomeAssistant'\u0131 Yetkilendirme" - }, - "user": { - "data": { - "host": "Ana Bilgisayar" - }, - "description": "Roon sunucusu bulunamad\u0131, l\u00fctfen Ana Bilgisayar Ad\u0131n\u0131z\u0131 veya IP'nizi girin." } } } diff --git a/homeassistant/components/roon/translations/uk.json b/homeassistant/components/roon/translations/uk.json index a892e24692d..cdc6f369d43 100644 --- a/homeassistant/components/roon/translations/uk.json +++ b/homeassistant/components/roon/translations/uk.json @@ -11,12 +11,6 @@ "link": { "description": "\u041f\u0456\u0441\u043b\u044f \u043d\u0430\u0442\u0438\u0441\u043a\u0430\u043d\u043d\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u00ab\u041f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0438\u00bb \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c \u0432 \u0434\u043e\u0434\u0430\u0442\u043e\u043a Roon Core, \u0432\u0456\u0434\u043a\u0440\u0438\u0439\u0442\u0435 \u00ab\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f\u00bb \u0456 \u0443\u0432\u0456\u043c\u043a\u043d\u0456\u0442\u044c HomeAssistant \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u0446\u0456 \u00ab\u0420\u043e\u0437\u0448\u0438\u0440\u0435\u043d\u043d\u044f\u00bb.", "title": "Roon" - }, - "user": { - "data": { - "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u043d\u0430\u0437\u0432\u0443 \u0430\u0431\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 Roon" } } } diff --git a/homeassistant/components/roon/translations/zh-Hant.json b/homeassistant/components/roon/translations/zh-Hant.json index f887b2d9847..8ec7bdf4276 100644 --- a/homeassistant/components/roon/translations/zh-Hant.json +++ b/homeassistant/components/roon/translations/zh-Hant.json @@ -18,12 +18,6 @@ "link": { "description": "\u5fc5\u9808\u65bc Roon \u4e2d\u8a8d\u8b49 Home Assistant\u3002\u9ede\u9078\u50b3\u9001\u5f8c\u3001\u958b\u555f Roon Core \u61c9\u7528\u7a0b\u5f0f\u3001\u6253\u958b\u8a2d\u5b9a\u4e26\u65bc\u64f4\u5145\uff08Extensions\uff09\u4e2d\u555f\u7528 HomeAssistant\u3002", "title": "\u65bc Roon \u4e2d\u8a8d\u8b49 HomeAssistant" - }, - "user": { - "data": { - "host": "\u4e3b\u6a5f\u7aef" - }, - "description": "\u627e\u4e0d\u5230 Roon \u4f3a\u670d\u5668\uff0c\u8acb\u8f38\u5165\u4e3b\u6a5f\u540d\u7a31\u6216 IP\u3002" } } } diff --git a/homeassistant/components/sabnzbd/translations/ko.json b/homeassistant/components/sabnzbd/translations/ko.json new file mode 100644 index 00000000000..86887876ff5 --- /dev/null +++ b/homeassistant/components/sabnzbd/translations/ko.json @@ -0,0 +1,15 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/samsungtv/translations/bg.json b/homeassistant/components/samsungtv/translations/bg.json index 2246b4ad954..56d85b8ff5d 100644 --- a/homeassistant/components/samsungtv/translations/bg.json +++ b/homeassistant/components/samsungtv/translations/bg.json @@ -11,9 +11,6 @@ }, "flow_title": "{device}", "step": { - "confirm": { - "title": "Samsung TV" - }, "encrypted_pairing": { "description": "\u041c\u043e\u043b\u044f, \u0432\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u041f\u0418\u041d \u043a\u043e\u0434\u0430, \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u043d\u0430 {device}." }, diff --git a/homeassistant/components/samsungtv/translations/ca.json b/homeassistant/components/samsungtv/translations/ca.json index 95738ae65d9..ae4ead3f8e8 100644 --- a/homeassistant/components/samsungtv/translations/ca.json +++ b/homeassistant/components/samsungtv/translations/ca.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant no est\u00e0 autenticat per connectar-se amb aquest televisor Samsung. V\u00e9s a la configuraci\u00f3 de dispositius externs del televisor per autoritzar Home Assistant.", "cannot_connect": "Ha fallat la connexi\u00f3", "id_missing": "El dispositiu Samsung no t\u00e9 cap n\u00famero de s\u00e8rie.", - "missing_config_entry": "Aquest dispositiu Samsung no t\u00e9 cap entrada de configuraci\u00f3.", "not_supported": "Actualment aquest dispositiu Samsung no \u00e9s compatible.", "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", "unknown": "Error inesperat" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Vols configurar {device}? Si mai abans has connectat Home Assistant hauries de veure una finestra emergent a la pantalla del televisor demanant autenticaci\u00f3.", - "title": "Televisor Samsung" + "description": "Vols configurar {device}? Si mai abans has connectat Home Assistant hauries de veure una finestra emergent a la pantalla del televisor demanant autenticaci\u00f3." }, "encrypted_pairing": { "description": "Introdueix el PIN que es mostra a {device}." diff --git a/homeassistant/components/samsungtv/translations/cs.json b/homeassistant/components/samsungtv/translations/cs.json index 4c2241d4caa..8de7b1f9e13 100644 --- a/homeassistant/components/samsungtv/translations/cs.json +++ b/homeassistant/components/samsungtv/translations/cs.json @@ -16,8 +16,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Chcete nastavit {device}? Pokud jste Home Assistant doposud nikdy nep\u0159ipojili, m\u011bla by se v\u00e1m na televizi zobrazit \u017e\u00e1dost o povolen\u00ed.", - "title": "Samsung TV" + "description": "Chcete nastavit {device}? Pokud jste Home Assistant doposud nikdy nep\u0159ipojili, m\u011bla by se v\u00e1m na televizi zobrazit \u017e\u00e1dost o povolen\u00ed." }, "encrypted_pairing": { "description": "Zadejte pros\u00edm PIN zobrazen\u00fd na {device}." diff --git a/homeassistant/components/samsungtv/translations/da.json b/homeassistant/components/samsungtv/translations/da.json index d41a62c5e7b..f335dbb9b29 100644 --- a/homeassistant/components/samsungtv/translations/da.json +++ b/homeassistant/components/samsungtv/translations/da.json @@ -9,8 +9,7 @@ "flow_title": "Samsung-tv: {model}", "step": { "confirm": { - "description": "Vil du konfigurere Samsung-tv {device}? Hvis du aldrig har oprettet forbindelse til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 dit tv, der beder om godkendelse. Manuelle konfigurationer for dette tv vil blive overskrevet.", - "title": "Samsung-tv" + "description": "Vil du konfigurere Samsung-tv {device}? Hvis du aldrig har oprettet forbindelse til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 dit tv, der beder om godkendelse. Manuelle konfigurationer for dette tv vil blive overskrevet." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/de.json b/homeassistant/components/samsungtv/translations/de.json index bfacbcd4b3c..7668a940f99 100644 --- a/homeassistant/components/samsungtv/translations/de.json +++ b/homeassistant/components/samsungtv/translations/de.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant ist nicht berechtigt, eine Verbindung zu diesem Samsung TV herzustellen. \u00dcberpr\u00fcfe den Ger\u00e4teverbindungsmanager in den Einstellungen deines Fernsehger\u00e4ts, um Home Assistant zu autorisieren.", "cannot_connect": "Verbindung fehlgeschlagen", "id_missing": "Dieses Samsung-Ger\u00e4t hat keine Seriennummer.", - "missing_config_entry": "Dieses Samsung-Ger\u00e4t hat keinen Konfigurationseintrag.", "not_supported": "Dieses Samsung TV-Ger\u00e4t wird derzeit nicht unterst\u00fctzt.", "reauth_successful": "Die erneute Authentifizierung war erfolgreich", "unknown": "Unerwarteter Fehler" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "M\u00f6chtest du Samsung TV {device} einrichten? Wenn du noch nie eine Verbindung zum Home Assistant hergestellt hast, solltest du eine Meldung auf deinem Fernseher sehen, die nach einer Autorisierung fragt. Manuelle Konfigurationen f\u00fcr dieses Fernsehger\u00e4t werden \u00fcberschrieben.", - "title": "Samsung TV" + "description": "M\u00f6chtest du Samsung TV {device} einrichten? Wenn du noch nie eine Verbindung zum Home Assistant hergestellt hast, solltest du eine Meldung auf deinem Fernseher sehen, die nach einer Autorisierung fragt. Manuelle Konfigurationen f\u00fcr dieses Fernsehger\u00e4t werden \u00fcberschrieben." }, "encrypted_pairing": { "description": "Bitte gib die PIN ein, die auf {device} angezeigt wird." diff --git a/homeassistant/components/samsungtv/translations/el.json b/homeassistant/components/samsungtv/translations/el.json index 0e6c4df03bc..4388073f70b 100644 --- a/homeassistant/components/samsungtv/translations/el.json +++ b/homeassistant/components/samsungtv/translations/el.json @@ -6,7 +6,6 @@ "auth_missing": "\u03a4\u03bf Home Assistant \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03b7\u03bc\u03ad\u03bd\u03bf \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af \u03c3\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7 Samsung. \u0395\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03b7\u03c2 \u0394\u03b9\u03b1\u03c7\u03b5\u03af\u03c1\u03b9\u03c3\u03b7\u03c2 \u03b5\u03be\u03c9\u03c4\u03b5\u03c1\u03b9\u03ba\u03ce\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd \u03c4\u03b7\u03c2 \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03bf\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf Home Assistant.", "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "id_missing": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Samsung \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 SerialNumber.", - "missing_config_entry": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Samsung \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2.", "not_supported": "\u0391\u03c5\u03c4\u03ae \u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Samsung \u03b4\u03b5\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03c4\u03b9\u03b3\u03bc\u03ae.", "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {device}; \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9 \u03c0\u03bf\u03c4\u03ad \u03c4\u03bf Home Assistant \u03c0\u03c1\u03b9\u03bd, \u03b8\u03b1 \u03b4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7.", - "title": "Samsung TV" + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {device}; \u0395\u03ac\u03bd \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03b9 \u03c0\u03bf\u03c4\u03ad \u03c4\u03bf Home Assistant \u03c0\u03c1\u03b9\u03bd, \u03b8\u03b1 \u03b4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03b1\u03bd\u03b1\u03b4\u03c5\u03cc\u03bc\u03b5\u03bd\u03bf \u03c0\u03b1\u03c1\u03ac\u03b8\u03c5\u03c1\u03bf \u03c3\u03c4\u03b7\u03bd \u03c4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03ae \u03c3\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03b6\u03b7\u03c4\u03ac \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7." }, "encrypted_pairing": { "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf PIN \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7 {device}." diff --git a/homeassistant/components/samsungtv/translations/en.json b/homeassistant/components/samsungtv/translations/en.json index 827f9992e46..c4e0e181090 100644 --- a/homeassistant/components/samsungtv/translations/en.json +++ b/homeassistant/components/samsungtv/translations/en.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant is not authorized to connect to this Samsung TV. Check your TV's External Device Manager settings to authorize Home Assistant.", "cannot_connect": "Failed to connect", "id_missing": "This Samsung device doesn't have a SerialNumber.", - "missing_config_entry": "This Samsung device doesn't have a configuration entry.", "not_supported": "This Samsung device is currently not supported.", "reauth_successful": "Re-authentication was successful", "unknown": "Unexpected error" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Do you want to set up {device}? If you never connected Home Assistant before you should see a popup on your TV asking for authorization.", - "title": "Samsung TV" + "description": "Do you want to set up {device}? If you never connected Home Assistant before you should see a popup on your TV asking for authorization." }, "encrypted_pairing": { "description": "Please enter the PIN displayed on {device}." diff --git a/homeassistant/components/samsungtv/translations/es-419.json b/homeassistant/components/samsungtv/translations/es-419.json index 179965fda80..c1e5110c967 100644 --- a/homeassistant/components/samsungtv/translations/es-419.json +++ b/homeassistant/components/samsungtv/translations/es-419.json @@ -9,8 +9,7 @@ "flow_title": "Televisi\u00f3n Samsung: {model}", "step": { "confirm": { - "description": "\u00bfDesea configurar la televisi\u00f3n Samsung {device}? Si nunca conect\u00f3 Home Assistant antes, deber\u00eda ver una ventana emergente en su televisor pidiendo autorizaci\u00f3n. Las configuraciones manuales para este televisor se sobrescribir\u00e1n.", - "title": "Samsung TV" + "description": "\u00bfDesea configurar la televisi\u00f3n Samsung {device}? Si nunca conect\u00f3 Home Assistant antes, deber\u00eda ver una ventana emergente en su televisor pidiendo autorizaci\u00f3n. Las configuraciones manuales para este televisor se sobrescribir\u00e1n." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/es.json b/homeassistant/components/samsungtv/translations/es.json index 922ee0da4fe..1c2a81cb7c0 100644 --- a/homeassistant/components/samsungtv/translations/es.json +++ b/homeassistant/components/samsungtv/translations/es.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant no est\u00e1 autorizado para conectarse a este televisor Samsung. Revisa la configuraci\u00f3n de tu televisor para autorizar a Home Assistant.", "cannot_connect": "No se pudo conectar", "id_missing": "Este dispositivo Samsung no tiene un n\u00famero de serie.", - "missing_config_entry": "Este dispositivo de Samsung no est\u00e1 configurado.", "not_supported": "Esta televisi\u00f3n Samsung actualmente no es compatible.", "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", "unknown": "Error inesperado" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "\u00bfQuieres configurar {device}? Si nunca la has conectado a Home Assistant antes deber\u00edas ver una ventana en tu TV pidiendo autorizaci\u00f3n.", - "title": "Samsung TV" + "description": "\u00bfQuieres configurar {device}? Si nunca la has conectado a Home Assistant antes deber\u00edas ver una ventana en tu TV pidiendo autorizaci\u00f3n." }, "encrypted_pairing": { "description": "Introduce el PIN que se muestra en {device}." diff --git a/homeassistant/components/samsungtv/translations/et.json b/homeassistant/components/samsungtv/translations/et.json index 94e9db78ec2..e2dcf966ae9 100644 --- a/homeassistant/components/samsungtv/translations/et.json +++ b/homeassistant/components/samsungtv/translations/et.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistantil pole selle Samsungi teleriga \u00fchenduse loomiseks luba. Home Assistanti autoriseerimiseks kontrolli oma teleri seadeid.", "cannot_connect": "\u00dchendamine nurjus", "id_missing": "Sellel Samsungi seadmel puudub seerianumber.", - "missing_config_entry": "Sellel Samsungi seadmel puudub seadekirje.", "not_supported": "Seda Samsungi seadet praegu ei toetata.", "reauth_successful": "Taastuvastamine \u00f5nnestus", "unknown": "Tundmatu t\u00f5rge" @@ -18,8 +17,7 @@ "flow_title": "{devicel}", "step": { "confirm": { - "description": "Kas soovid seadistada {devicel} ? Kui seda pole kunagi enne Home Assistantiga \u00fchendatud, n\u00e4ed oma teleris h\u00fcpikakent, mis k\u00fcsib tuvastamist.", - "title": "" + "description": "Kas soovid seadistada {devicel} ? Kui seda pole kunagi enne Home Assistantiga \u00fchendatud, n\u00e4ed oma teleris h\u00fcpikakent, mis k\u00fcsib tuvastamist." }, "encrypted_pairing": { "description": "Sisesta seadmes {device} kuvatav PIN-kood." diff --git a/homeassistant/components/samsungtv/translations/fr.json b/homeassistant/components/samsungtv/translations/fr.json index 438336f2818..1d651f9ce79 100644 --- a/homeassistant/components/samsungtv/translations/fr.json +++ b/homeassistant/components/samsungtv/translations/fr.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant n'est pas autoris\u00e9 \u00e0 se connecter \u00e0 ce t\u00e9l\u00e9viseur Samsung. Veuillez v\u00e9rifier les param\u00e8tres de votre t\u00e9l\u00e9viseur pour autoriser Home Assistant.", "cannot_connect": "\u00c9chec de connexion", "id_missing": "Cet appareil Samsung n'a pas de num\u00e9ro de s\u00e9rie.", - "missing_config_entry": "Cet appareil Samsung n'a pas d'entr\u00e9e de configuration.", "not_supported": "Ce t\u00e9l\u00e9viseur Samsung n'est actuellement pas pris en charge.", "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", "unknown": "Erreur inattendue" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Voulez-vous configurer {device}\u00a0? Si vous n'avez jamais connect\u00e9 Home Assistant auparavant, une fen\u00eatre contextuelle d'autorisation devrait appara\u00eetre sur votre t\u00e9l\u00e9viseur.", - "title": "TV Samsung" + "description": "Voulez-vous configurer {device}\u00a0? Si vous n'avez jamais connect\u00e9 Home Assistant auparavant, une fen\u00eatre contextuelle d'autorisation devrait appara\u00eetre sur votre t\u00e9l\u00e9viseur." }, "encrypted_pairing": { "description": "Veuillez saisir le code PIN affich\u00e9 sur {device}." diff --git a/homeassistant/components/samsungtv/translations/he.json b/homeassistant/components/samsungtv/translations/he.json index 9f62de51b8c..2904c4c3012 100644 --- a/homeassistant/components/samsungtv/translations/he.json +++ b/homeassistant/components/samsungtv/translations/he.json @@ -13,9 +13,6 @@ }, "flow_title": "{device}", "step": { - "confirm": { - "title": "\u05d8\u05dc\u05d5\u05d5\u05d9\u05d6\u05d9\u05d4 \u05e9\u05dc \u05e1\u05de\u05e1\u05d5\u05e0\u05d2" - }, "user": { "data": { "host": "\u05de\u05d0\u05e8\u05d7", diff --git a/homeassistant/components/samsungtv/translations/hu.json b/homeassistant/components/samsungtv/translations/hu.json index dc42596e290..b53f50ff74a 100644 --- a/homeassistant/components/samsungtv/translations/hu.json +++ b/homeassistant/components/samsungtv/translations/hu.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant nem jogosult csatlakozni ehhez a Samsung TV-hez. Ellen\u0151rizze a TV be\u00e1ll\u00edt\u00e1sait Home Assistant enged\u00e9lyez\u00e9s\u00e9hez.", "cannot_connect": "Sikertelen csatlakoz\u00e1s", "id_missing": "Ennek a Samsung eszk\u00f6znek nincs sorsz\u00e1ma.", - "missing_config_entry": "Ez a Samsung eszk\u00f6z nem rendelkezik konfigur\u00e1ci\u00f3s bejegyz\u00e9ssel.", "not_supported": "Ez a Samsung k\u00e9sz\u00fcl\u00e9k jelenleg nem t\u00e1mogatott.", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {device} k\u00e9sz\u00fcl\u00e9k\u00e9t? Ha kor\u00e1bban m\u00e9g sosem csatlakoztatta Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ami j\u00f3v\u00e1hagy\u00e1sra v\u00e1r.", - "title": "Samsung TV" + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani {device} k\u00e9sz\u00fcl\u00e9k\u00e9t? Ha kor\u00e1bban m\u00e9g sosem csatlakoztatta Home Assistanthoz, akkor meg kell jelennie egy felugr\u00f3 ablaknak a TV k\u00e9perny\u0151j\u00e9n, ami j\u00f3v\u00e1hagy\u00e1sra v\u00e1r." }, "encrypted_pairing": { "description": "K\u00e9rj\u00fck, adja meg az eszk\u00f6z\u00f6n megjelen\u0151 PIN-k\u00f3dot: {device}" diff --git a/homeassistant/components/samsungtv/translations/id.json b/homeassistant/components/samsungtv/translations/id.json index 394cfc41ae1..2dd96798748 100644 --- a/homeassistant/components/samsungtv/translations/id.json +++ b/homeassistant/components/samsungtv/translations/id.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant tidak diizinkan untuk tersambung ke TV Samsung ini. Periksa Manajer Perangkat Eksternal TV Anda untuk mengotorisasi Home Assistant.", "cannot_connect": "Gagal terhubung", "id_missing": "Perangkat Samsung ini tidak memiliki SerialNumber.", - "missing_config_entry": "Perangkat Samsung ini tidak memiliki entri konfigurasi.", "not_supported": "Perangkat TV Samsung ini saat ini tidak didukung.", "reauth_successful": "Autentikasi ulang berhasil", "unknown": "Kesalahan yang tidak diharapkan" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Apakah Anda ingin menyiapkan {device}? Jika Anda belum pernah menyambungkan Home Assistant sebelumnya, Anda akan melihat dialog di TV yang meminta otorisasi.", - "title": "TV Samsung" + "description": "Apakah Anda ingin menyiapkan {device}? Jika Anda belum pernah menyambungkan Home Assistant sebelumnya, Anda akan melihat dialog di TV yang meminta otorisasi." }, "encrypted_pairing": { "description": "Masukkan PIN yang ditampilkan di {device} ." diff --git a/homeassistant/components/samsungtv/translations/it.json b/homeassistant/components/samsungtv/translations/it.json index 6bfc26fb445..2f96d7566a6 100644 --- a/homeassistant/components/samsungtv/translations/it.json +++ b/homeassistant/components/samsungtv/translations/it.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant non \u00e8 autorizzato a connettersi a questo televisore Samsung. Controlla le impostazioni di Gestione dispositivi esterni della tua TV per autorizzare Home Assistant.", "cannot_connect": "Impossibile connettersi", "id_missing": "Questo dispositivo Samsung non ha un SerialNumber.", - "missing_config_entry": "Questo dispositivo Samsung non ha una voce di configurazione.", "not_supported": "Questo dispositivo Samsung non \u00e8 attualmente supportato.", "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", "unknown": "Errore imprevisto" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Vuoi configurare {device}? Se non hai mai collegato Home Assistant, dovresti vedere un popup sulla tua TV che chiede l'autorizzazione.", - "title": "Samsung TV" + "description": "Vuoi configurare {device}? Se non hai mai collegato Home Assistant, dovresti vedere un popup sulla tua TV che chiede l'autorizzazione." }, "encrypted_pairing": { "description": "Digita il PIN visualizzato su {device}." diff --git a/homeassistant/components/samsungtv/translations/ja.json b/homeassistant/components/samsungtv/translations/ja.json index d1b5670e25c..6b0cae1ba4d 100644 --- a/homeassistant/components/samsungtv/translations/ja.json +++ b/homeassistant/components/samsungtv/translations/ja.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant\u306f\u3001\u3053\u306eSamsungTV\u3078\u306e\u63a5\u7d9a\u3092\u8a31\u53ef\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c6\u30ec\u30d3\u306e\u5916\u90e8\u30c7\u30d0\u30a4\u30b9\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u306e\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u3001Home Assistant\u3092\u8a8d\u8a3c\u3057\u307e\u3059\u3002", "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "id_missing": "\u3053\u306eSamsung\u30c7\u30d0\u30a4\u30b9\u306b\u306f\u30b7\u30ea\u30a2\u30eb\u756a\u53f7\u304c\u3042\u308a\u307e\u305b\u3093\u3002", - "missing_config_entry": "\u3053\u306eSamsung\u30c7\u30d0\u30a4\u30b9\u306b\u306f\u69cb\u6210\u30a8\u30f3\u30c8\u30ea\u30fc\u304c\u3042\u308a\u307e\u305b\u3093\u3002", "not_supported": "\u3053\u306eSamsung\u30c7\u30d0\u30a4\u30b9\u306f\u73fe\u5728\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002", "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "{device} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f\u3053\u308c\u307e\u3067\u306bHome Assistant\u3092\u4e00\u5ea6\u3082\u63a5\u7d9a\u3057\u305f\u3053\u3068\u304c\u306a\u3044\u5834\u5408\u306f\u3001\u30c6\u30ec\u30d3\u306b\u8a8d\u8a3c\u3092\u6c42\u3081\u308b\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002", - "title": "Samsung TV" + "description": "{device} \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f\u3053\u308c\u307e\u3067\u306bHome Assistant\u3092\u4e00\u5ea6\u3082\u63a5\u7d9a\u3057\u305f\u3053\u3068\u304c\u306a\u3044\u5834\u5408\u306f\u3001\u30c6\u30ec\u30d3\u306b\u8a8d\u8a3c\u3092\u6c42\u3081\u308b\u30dd\u30c3\u30d7\u30a2\u30c3\u30d7\u304c\u8868\u793a\u3055\u308c\u307e\u3059\u3002" }, "encrypted_pairing": { "description": "{device} \u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308bPIN\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" diff --git a/homeassistant/components/samsungtv/translations/ko.json b/homeassistant/components/samsungtv/translations/ko.json index 4f62bbf6237..8b129d8ab50 100644 --- a/homeassistant/components/samsungtv/translations/ko.json +++ b/homeassistant/components/samsungtv/translations/ko.json @@ -10,8 +10,10 @@ "flow_title": "\uc0bc\uc131 TV: {model}", "step": { "confirm": { - "description": "{device} \uc0bc\uc131 TV\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? Home Assistant\ub97c \uc5f0\uacb0\ud55c \uc801\uc774 \uc5c6\ub2e4\uba74 TV\uc5d0\uc11c \uc778\uc99d\uc744 \uc694\uccad\ud558\ub294 \ud31d\uc5c5\uc774 \ud45c\uc2dc\ub429\ub2c8\ub2e4. \uc774 TV\uc758 \uc218\ub3d9\uc73c\ub85c \uad6c\uc131\ub41c \ub0b4\uc6a9\uc740 \ub36e\uc5b4\uc50c\uc6cc\uc9d1\ub2c8\ub2e4.", - "title": "\uc0bc\uc131 TV" + "description": "{device} \uc0bc\uc131 TV\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? Home Assistant\ub97c \uc5f0\uacb0\ud55c \uc801\uc774 \uc5c6\ub2e4\uba74 TV\uc5d0\uc11c \uc778\uc99d\uc744 \uc694\uccad\ud558\ub294 \ud31d\uc5c5\uc774 \ud45c\uc2dc\ub429\ub2c8\ub2e4. \uc774 TV\uc758 \uc218\ub3d9\uc73c\ub85c \uad6c\uc131\ub41c \ub0b4\uc6a9\uc740 \ub36e\uc5b4\uc50c\uc6cc\uc9d1\ub2c8\ub2e4." + }, + "pairing": { + "description": "{device} \uc0bc\uc131 TV\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? Home Assistant\ub97c \uc5f0\uacb0\ud55c \uc801\uc774 \uc5c6\ub2e4\uba74 TV\uc5d0\uc11c \uc778\uc99d\uc744 \uc694\uccad\ud558\ub294 \ud31d\uc5c5\uc774 \ud45c\uc2dc\ub429\ub2c8\ub2e4. \uc774 TV\uc758 \uc218\ub3d9\uc73c\ub85c \uad6c\uc131\ub41c \ub0b4\uc6a9\uc740 \ub36e\uc5b4\uc50c\uc6cc\uc9d1\ub2c8\ub2e4." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/lb.json b/homeassistant/components/samsungtv/translations/lb.json index 0740ab119a4..732507d3bb1 100644 --- a/homeassistant/components/samsungtv/translations/lb.json +++ b/homeassistant/components/samsungtv/translations/lb.json @@ -10,8 +10,7 @@ "flow_title": "Samsnung TV:{model}", "step": { "confirm": { - "description": "W\u00ebllt dir de Samsung TV {device} ariichten?. Falls dir Home Assistant nach ni domat verbonnen hutt misst den TV eng Meldung mat enger Authentifiz\u00e9ierung uweisen. Manuell Konfiguratioun fir d\u00ebse TV g\u00ebtt iwwerschriwwen.", - "title": "Samsnung TV" + "description": "W\u00ebllt dir de Samsung TV {device} ariichten?. Falls dir Home Assistant nach ni domat verbonnen hutt misst den TV eng Meldung mat enger Authentifiz\u00e9ierung uweisen. Manuell Konfiguratioun fir d\u00ebse TV g\u00ebtt iwwerschriwwen." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/nl.json b/homeassistant/components/samsungtv/translations/nl.json index b75f9b5970e..9fe7e1dc3f9 100644 --- a/homeassistant/components/samsungtv/translations/nl.json +++ b/homeassistant/components/samsungtv/translations/nl.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant is niet gemachtigd om verbinding te maken met deze Samsung TV. Controleer de instellingen van Extern apparaatbeheer van uw tv om Home Assistant te machtigen.", "cannot_connect": "Kan geen verbinding maken", "id_missing": "Dit Samsung-apparaat heeft geen serienummer.", - "missing_config_entry": "Dit Samsung-apparaat heeft geen configuratie-invoer.", "not_supported": "Deze Samsung TV wordt momenteel niet ondersteund.", "reauth_successful": "Herauthenticatie was succesvol", "unknown": "Onverwachte fout" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Wilt u Samsung TV {device} instellen? Als u nooit eerder Home Assistant hebt verbonden dan zou u een popup op uw TV moeten zien waarin u om toestemming wordt vraagt. Handmatige configuraties voor deze TV worden overschreven", - "title": "Samsung TV" + "description": "Wilt u Samsung TV {device} instellen? Als u nooit eerder Home Assistant hebt verbonden dan zou u een popup op uw TV moeten zien waarin u om toestemming wordt vraagt. Handmatige configuraties voor deze TV worden overschreven" }, "encrypted_pairing": { "description": "Voer de pincode in die wordt weergegeven op {device} ." diff --git a/homeassistant/components/samsungtv/translations/no.json b/homeassistant/components/samsungtv/translations/no.json index 2125b407f29..3b515608128 100644 --- a/homeassistant/components/samsungtv/translations/no.json +++ b/homeassistant/components/samsungtv/translations/no.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant er ikke autorisert til \u00e5 koble til denne Samsung TV-en. Sjekk TV-ens innstillinger for ekstern enhetsbehandling for \u00e5 autorisere Home Assistant.", "cannot_connect": "Tilkobling mislyktes", "id_missing": "Denne Samsung-enheten har ikke serienummer.", - "missing_config_entry": "Denne Samsung -enheten har ingen konfigurasjonsoppf\u00f8ring.", "not_supported": "Denne Samsung-enheten st\u00f8ttes forel\u00f8pig ikke.", "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", "unknown": "Uventet feil" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Vil du konfigurere {device} ? Hvis du aldri har koblet til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 TV-en din som ber om autorisasjon.", - "title": "" + "description": "Vil du konfigurere {device} ? Hvis du aldri har koblet til Home Assistant f\u00f8r, b\u00f8r du se en popup p\u00e5 TV-en din som ber om autorisasjon." }, "encrypted_pairing": { "description": "Vennligst skriv inn PIN-koden som vises p\u00e5 {device} ." diff --git a/homeassistant/components/samsungtv/translations/pl.json b/homeassistant/components/samsungtv/translations/pl.json index 015f048dc60..f61c0eb8ced 100644 --- a/homeassistant/components/samsungtv/translations/pl.json +++ b/homeassistant/components/samsungtv/translations/pl.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant nie ma uprawnie\u0144 do po\u0142\u0105czenia si\u0119 z tym telewizorem Samsung. Sprawd\u017a ustawienia \"Mened\u017cera urz\u0105dze\u0144 zewn\u0119trznych\", aby autoryzowa\u0107 Home Assistant.", "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "id_missing": "To urz\u0105dzenie Samsung nie ma numeru seryjnego.", - "missing_config_entry": "To urz\u0105dzenie Samsung nie ma wpisu konfiguracyjnego.", "not_supported": "To urz\u0105dzenie Samsung nie jest obecnie obs\u0142ugiwane", "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", "unknown": "Nieoczekiwany b\u0142\u0105d" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Czy chcesz skonfigurowa\u0107 {device}? Je\u015bli nigdy wcze\u015bniej nie \u0142\u0105czy\u0142e\u015b go z Home Assistantem, na jego ekranie powinna pojawi\u0107 si\u0119 pro\u015bba o uwierzytelnienie.", - "title": "Samsung TV" + "description": "Czy chcesz skonfigurowa\u0107 {device}? Je\u015bli nigdy wcze\u015bniej nie \u0142\u0105czy\u0142e\u015b go z Home Assistantem, na jego ekranie powinna pojawi\u0107 si\u0119 pro\u015bba o uwierzytelnienie." }, "encrypted_pairing": { "description": "Wprowad\u017a kod PIN wy\u015bwietlony na {device}." diff --git a/homeassistant/components/samsungtv/translations/pt-BR.json b/homeassistant/components/samsungtv/translations/pt-BR.json index dbb4b50fe93..c1480bb93c9 100644 --- a/homeassistant/components/samsungtv/translations/pt-BR.json +++ b/homeassistant/components/samsungtv/translations/pt-BR.json @@ -6,7 +6,6 @@ "auth_missing": "O Home Assistant n\u00e3o est\u00e1 autorizado a se conectar a esta TV Samsung. Verifique as configura\u00e7\u00f5es do Gerenciador de dispositivos externos da sua TV para autorizar o Home Assistant.", "cannot_connect": "Falha ao conectar", "id_missing": "Este dispositivo Samsung n\u00e3o possui um SerialNumber.", - "missing_config_entry": "Este dispositivo Samsung n\u00e3o tem uma entrada de configura\u00e7\u00e3o.", "not_supported": "Este dispositivo Samsung n\u00e3o \u00e9 compat\u00edvel no momento.", "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", "unknown": "Erro inesperado" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Deseja configurar {device}? Se voc\u00ea nunca conectou o Home Assistant antes, aparecer\u00e1 um pop-up na sua TV pedindo autoriza\u00e7\u00e3o.", - "title": "TV Samsung" + "description": "Deseja configurar {device}? Se voc\u00ea nunca conectou o Home Assistant antes, aparecer\u00e1 um pop-up na sua TV pedindo autoriza\u00e7\u00e3o." }, "encrypted_pairing": { "description": "Insira o PIN exibido em {device}." diff --git a/homeassistant/components/samsungtv/translations/pt.json b/homeassistant/components/samsungtv/translations/pt.json index 15e61d23627..b2cd242c7da 100644 --- a/homeassistant/components/samsungtv/translations/pt.json +++ b/homeassistant/components/samsungtv/translations/pt.json @@ -7,9 +7,6 @@ }, "flow_title": "TV Samsung: {model}", "step": { - "confirm": { - "title": "TV Samsung" - }, "user": { "data": { "host": "Servidor", diff --git a/homeassistant/components/samsungtv/translations/ru.json b/homeassistant/components/samsungtv/translations/ru.json index ee73070cf0e..5d6a3cb8e12 100644 --- a/homeassistant/components/samsungtv/translations/ru.json +++ b/homeassistant/components/samsungtv/translations/ru.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Samsung TV. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 External Device Manager \u0412\u0430\u0448\u0435\u0433\u043e \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430.", "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "id_missing": "\u0423 \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Samsung \u043d\u0435\u0442 \u0441\u0435\u0440\u0438\u0439\u043d\u043e\u0433\u043e \u043d\u043e\u043c\u0435\u0440\u0430.", - "missing_config_entry": "\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Samsung.", "not_supported": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Samsung \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {device}? \u0415\u0441\u043b\u0438 \u0412\u044b \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u043b\u0438 \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043a Home Assistant, \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", - "title": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440 Samsung" + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {device}? \u0415\u0441\u043b\u0438 \u0412\u044b \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u0440\u0430\u043d\u044c\u0448\u0435 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u043b\u0438 \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043a Home Assistant, \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0442\u0435\u043b\u0435\u0432\u0438\u0437\u043e\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0435\u0435 \u043e\u043a\u043d\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438." }, "encrypted_pairing": { "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 PIN-\u043a\u043e\u0434, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u044b\u0439 \u043d\u0430 {device}." diff --git a/homeassistant/components/samsungtv/translations/sl.json b/homeassistant/components/samsungtv/translations/sl.json index 41536f72b16..5ec6f1aca76 100644 --- a/homeassistant/components/samsungtv/translations/sl.json +++ b/homeassistant/components/samsungtv/translations/sl.json @@ -9,8 +9,7 @@ "flow_title": "Samsung TV: {model}", "step": { "confirm": { - "description": "Vnesite podatke o televizorju Samsung {device}. \u010ce \u0161e nikoli niste povezali Home Assistant, bi morali na televizorju videli pojavno okno, ki zahteva va\u0161e dovoljenje. Ro\u010dna konfiguracija za ta TV bo prepisana.", - "title": "Samsung TV" + "description": "Vnesite podatke o televizorju Samsung {device}. \u010ce \u0161e nikoli niste povezali Home Assistant, bi morali na televizorju videli pojavno okno, ki zahteva va\u0161e dovoljenje. Ro\u010dna konfiguracija za ta TV bo prepisana." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/sv.json b/homeassistant/components/samsungtv/translations/sv.json index 38b3bea8a22..e9c0803c865 100644 --- a/homeassistant/components/samsungtv/translations/sv.json +++ b/homeassistant/components/samsungtv/translations/sv.json @@ -11,8 +11,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "Vill du st\u00e4lla in Samsung TV {device}? Om du aldrig har anslutit Home Assistant innan du ska se ett popup-f\u00f6nster p\u00e5 tv:n och be om auktorisering. Manuella konfigurationer f\u00f6r den h\u00e4r TV:n skrivs \u00f6ver.", - "title": "Samsung TV" + "description": "Vill du st\u00e4lla in Samsung TV {device}? Om du aldrig har anslutit Home Assistant innan du ska se ett popup-f\u00f6nster p\u00e5 tv:n och be om auktorisering. Manuella konfigurationer f\u00f6r den h\u00e4r TV:n skrivs \u00f6ver." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/tr.json b/homeassistant/components/samsungtv/translations/tr.json index 668b321e26d..172bd0e093e 100644 --- a/homeassistant/components/samsungtv/translations/tr.json +++ b/homeassistant/components/samsungtv/translations/tr.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant'\u0131n bu Samsung TV'ye ba\u011flanma izni yok. Home Assistant'\u0131 yetkilendirmek i\u00e7in l\u00fctfen TV'nin ayarlar\u0131n\u0131 kontrol et.", "cannot_connect": "Ba\u011flanma hatas\u0131", "id_missing": "Bu Samsung cihaz\u0131n\u0131n Seri Numaras\u0131 yok.", - "missing_config_entry": "Bu Samsung cihaz\u0131nda bir yap\u0131land\u0131rma giri\u015fi yok.", "not_supported": "Bu Samsung TV cihaz\u0131 \u015fu anda desteklenmiyor.", "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", "unknown": "Beklenmeyen hata" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "{device} kurulumunu yapmak istiyor musunuz? Home Assistant'\u0131 daha \u00f6nce hi\u00e7 ba\u011flamad\u0131ysan\u0131z, TV'nizde yetki isteyen bir a\u00e7\u0131l\u0131r pencere g\u00f6rmelisiniz.", - "title": "Samsung TV" + "description": "{device} kurulumunu yapmak istiyor musunuz? Home Assistant'\u0131 daha \u00f6nce hi\u00e7 ba\u011flamad\u0131ysan\u0131z, TV'nizde yetki isteyen bir a\u00e7\u0131l\u0131r pencere g\u00f6rmelisiniz." }, "encrypted_pairing": { "description": "L\u00fctfen {device} \u00fczerinde g\u00f6r\u00fcnt\u00fclenen PIN'i girin." diff --git a/homeassistant/components/samsungtv/translations/uk.json b/homeassistant/components/samsungtv/translations/uk.json index f6aa504ccc8..ae3f98e3aac 100644 --- a/homeassistant/components/samsungtv/translations/uk.json +++ b/homeassistant/components/samsungtv/translations/uk.json @@ -10,8 +10,7 @@ "flow_title": "Samsung TV: {model}", "step": { "confirm": { - "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 Samsung {device}? \u042f\u043a\u0449\u043e \u0446\u0435\u0439 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 \u0440\u0430\u043d\u0456\u0448\u0435 \u043d\u0435 \u0431\u0443\u0432 \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0434\u043e Home Assistant, \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0456 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440\u0430 \u043c\u0430\u0454 \u0437'\u044f\u0432\u0438\u0442\u0438\u0441\u044f \u0441\u043f\u043b\u0438\u0432\u0430\u044e\u0447\u0435 \u0432\u0456\u043a\u043d\u043e \u0456\u0437 \u0437\u0430\u043f\u0438\u0442\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457. \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0434\u043b\u044f \u0446\u044c\u043e\u0433\u043e \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440\u0430, \u0432\u0438\u043a\u043e\u043d\u0430\u043d\u0456 \u0432\u0440\u0443\u0447\u043d\u0443, \u0431\u0443\u0434\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u0456.", - "title": "\u0422\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 Samsung" + "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 Samsung {device}? \u042f\u043a\u0449\u043e \u0446\u0435\u0439 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440 \u0440\u0430\u043d\u0456\u0448\u0435 \u043d\u0435 \u0431\u0443\u0432 \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0434\u043e Home Assistant, \u043d\u0430 \u0435\u043a\u0440\u0430\u043d\u0456 \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440\u0430 \u043c\u0430\u0454 \u0437'\u044f\u0432\u0438\u0442\u0438\u0441\u044f \u0441\u043f\u043b\u0438\u0432\u0430\u044e\u0447\u0435 \u0432\u0456\u043a\u043d\u043e \u0456\u0437 \u0437\u0430\u043f\u0438\u0442\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457. \u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0434\u043b\u044f \u0446\u044c\u043e\u0433\u043e \u0442\u0435\u043b\u0435\u0432\u0456\u0437\u043e\u0440\u0430, \u0432\u0438\u043a\u043e\u043d\u0430\u043d\u0456 \u0432\u0440\u0443\u0447\u043d\u0443, \u0431\u0443\u0434\u0443\u0442\u044c \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u0456." }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/zh-Hans.json b/homeassistant/components/samsungtv/translations/zh-Hans.json index da6a5c3c9ba..3a18da7fa94 100644 --- a/homeassistant/components/samsungtv/translations/zh-Hans.json +++ b/homeassistant/components/samsungtv/translations/zh-Hans.json @@ -16,8 +16,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "\u662f\u5426\u8981\u914d\u7f6e {device} ?\n\u5982\u679c\u60a8\u4e4b\u524d\u4ece\u672a\u8fde\u63a5\u8fc7 Home Assistant \uff0c\u60a8\u5c06\u4f1a\u5728\u8be5\u7535\u89c6\u4e0a\u770b\u5230\u8bf7\u6c42\u6388\u6743\u7684\u5f39\u7a97\u3002", - "title": "\u4e09\u661f\u7535\u89c6" + "description": "\u662f\u5426\u8981\u914d\u7f6e {device} ?\n\u5982\u679c\u60a8\u4e4b\u524d\u4ece\u672a\u8fde\u63a5\u8fc7 Home Assistant \uff0c\u60a8\u5c06\u4f1a\u5728\u8be5\u7535\u89c6\u4e0a\u770b\u5230\u8bf7\u6c42\u6388\u6743\u7684\u5f39\u7a97\u3002" }, "reauth_confirm": { "description": "\u63d0\u4ea4\u4fe1\u606f\u540e\uff0c\u8bf7\u5728 30 \u79d2\u5185\u5728 {device} \u540c\u610f\u83b7\u53d6\u76f8\u5173\u6388\u6743\u3002" diff --git a/homeassistant/components/samsungtv/translations/zh-Hant.json b/homeassistant/components/samsungtv/translations/zh-Hant.json index 8f30090dd24..da3d0db877a 100644 --- a/homeassistant/components/samsungtv/translations/zh-Hant.json +++ b/homeassistant/components/samsungtv/translations/zh-Hant.json @@ -6,7 +6,6 @@ "auth_missing": "Home Assistant \u672a\u7372\u5f97\u9a57\u8b49\u4ee5\u9023\u7dda\u81f3\u6b64\u4e09\u661f\u96fb\u8996\u3002\u8acb\u6aa2\u67e5\u60a8\u7684\u96fb\u8996\u5916\u90e8\u88dd\u7f6e\u7ba1\u7406\u54e1\u8a2d\u5b9a\u4ee5\u9032\u884c\u9a57\u8b49\u3002", "cannot_connect": "\u9023\u7dda\u5931\u6557", "id_missing": "\u4e09\u661f\u88dd\u7f6e\u4e26\u672a\u5305\u542b\u5e8f\u865f\u3002", - "missing_config_entry": "\u6b64\u4e09\u661f\u88dd\u7f6e\u4e26\u672a\u5305\u542b\u8a2d\u5b9a\u3002", "not_supported": "\u4e0d\u652f\u63f4\u6b64\u6b3e\u4e09\u661f\u88dd\u7f6e\u3002", "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" @@ -18,8 +17,7 @@ "flow_title": "{device}", "step": { "confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a {device}\uff1f\u5047\u5982\u60a8\u4e4b\u524d\u672a\u66fe\u9023\u7dda\u81f3 Home Assistant\uff0c\u61c9\u8a72\u6703\u65bc\u96fb\u8996\u4e0a\u6536\u5230\u9a57\u8b49\u8a0a\u606f\u3002", - "title": "\u4e09\u661f\u96fb\u8996" + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {device}\uff1f\u5047\u5982\u60a8\u4e4b\u524d\u672a\u66fe\u9023\u7dda\u81f3 Home Assistant\uff0c\u61c9\u8a72\u6703\u65bc\u96fb\u8996\u4e0a\u6536\u5230\u9a57\u8b49\u8a0a\u606f\u3002" }, "encrypted_pairing": { "description": "\u8acb\u8f38\u5165\u986f\u793a\u65bc {device} \u4e0a\u7684 PIN \u78bc\u3002" diff --git a/homeassistant/components/sensibo/translations/bg.json b/homeassistant/components/sensibo/translations/bg.json index e600bafdb4a..1435a77a2fb 100644 --- a/homeassistant/components/sensibo/translations/bg.json +++ b/homeassistant/components/sensibo/translations/bg.json @@ -16,8 +16,7 @@ }, "user": { "data": { - "api_key": "API \u043a\u043b\u044e\u0447", - "name": "\u0418\u043c\u0435" + "api_key": "API \u043a\u043b\u044e\u0447" } } } diff --git a/homeassistant/components/sensibo/translations/ca.json b/homeassistant/components/sensibo/translations/ca.json index f062af9e519..1634e0b8e40 100644 --- a/homeassistant/components/sensibo/translations/ca.json +++ b/homeassistant/components/sensibo/translations/ca.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Clau API", - "name": "Nom" + "api_key": "Clau API" } } } diff --git a/homeassistant/components/sensibo/translations/cs.json b/homeassistant/components/sensibo/translations/cs.json index 8e25544e301..fcfba839932 100644 --- a/homeassistant/components/sensibo/translations/cs.json +++ b/homeassistant/components/sensibo/translations/cs.json @@ -9,8 +9,7 @@ "step": { "user": { "data": { - "api_key": "Kl\u00ed\u010d API", - "name": "Jm\u00e9no" + "api_key": "Kl\u00ed\u010d API" } } } diff --git a/homeassistant/components/sensibo/translations/de.json b/homeassistant/components/sensibo/translations/de.json index 9e3b02c726c..d5900f0f25e 100644 --- a/homeassistant/components/sensibo/translations/de.json +++ b/homeassistant/components/sensibo/translations/de.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API-Schl\u00fcssel", - "name": "Name" + "api_key": "API-Schl\u00fcssel" } } } diff --git a/homeassistant/components/sensibo/translations/el.json b/homeassistant/components/sensibo/translations/el.json index baa2545a7e0..7fd7ee26f97 100644 --- a/homeassistant/components/sensibo/translations/el.json +++ b/homeassistant/components/sensibo/translations/el.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API" } } } diff --git a/homeassistant/components/sensibo/translations/en.json b/homeassistant/components/sensibo/translations/en.json index 1b5d7cd9214..7b7c8aab7f1 100644 --- a/homeassistant/components/sensibo/translations/en.json +++ b/homeassistant/components/sensibo/translations/en.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API Key", - "name": "Name" + "api_key": "API Key" } } } diff --git a/homeassistant/components/sensibo/translations/es.json b/homeassistant/components/sensibo/translations/es.json index c5033bce962..b91e1b63f9b 100644 --- a/homeassistant/components/sensibo/translations/es.json +++ b/homeassistant/components/sensibo/translations/es.json @@ -18,8 +18,7 @@ }, "user": { "data": { - "api_key": "Clave API", - "name": "Nombre" + "api_key": "Clave API" } } } diff --git a/homeassistant/components/sensibo/translations/et.json b/homeassistant/components/sensibo/translations/et.json index de5a158c1ad..b216ecc3260 100644 --- a/homeassistant/components/sensibo/translations/et.json +++ b/homeassistant/components/sensibo/translations/et.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API v\u00f5ti", - "name": "Nimi" + "api_key": "API v\u00f5ti" } } } diff --git a/homeassistant/components/sensibo/translations/fr.json b/homeassistant/components/sensibo/translations/fr.json index 09e2e5e5045..f674ad2c9b2 100644 --- a/homeassistant/components/sensibo/translations/fr.json +++ b/homeassistant/components/sensibo/translations/fr.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Cl\u00e9 d'API", - "name": "Nom" + "api_key": "Cl\u00e9 d'API" } } } diff --git a/homeassistant/components/sensibo/translations/he.json b/homeassistant/components/sensibo/translations/he.json index 3486bd3c646..29f4fc720da 100644 --- a/homeassistant/components/sensibo/translations/he.json +++ b/homeassistant/components/sensibo/translations/he.json @@ -16,8 +16,7 @@ }, "user": { "data": { - "api_key": "\u05de\u05e4\u05ea\u05d7 API", - "name": "\u05e9\u05dd" + "api_key": "\u05de\u05e4\u05ea\u05d7 API" } } } diff --git a/homeassistant/components/sensibo/translations/hu.json b/homeassistant/components/sensibo/translations/hu.json index 065a7b982ed..14c7cd756d0 100644 --- a/homeassistant/components/sensibo/translations/hu.json +++ b/homeassistant/components/sensibo/translations/hu.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API kulcs", - "name": "Elnevez\u00e9s" + "api_key": "API kulcs" } } } diff --git a/homeassistant/components/sensibo/translations/id.json b/homeassistant/components/sensibo/translations/id.json index 479edabe69b..dfaf05cca33 100644 --- a/homeassistant/components/sensibo/translations/id.json +++ b/homeassistant/components/sensibo/translations/id.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Kunci API", - "name": "Nama" + "api_key": "Kunci API" } } } diff --git a/homeassistant/components/sensibo/translations/it.json b/homeassistant/components/sensibo/translations/it.json index c10adf2cf38..912bfee5114 100644 --- a/homeassistant/components/sensibo/translations/it.json +++ b/homeassistant/components/sensibo/translations/it.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Chiave API", - "name": "Nome" + "api_key": "Chiave API" } } } diff --git a/homeassistant/components/sensibo/translations/ja.json b/homeassistant/components/sensibo/translations/ja.json index 859722fbe73..4c74b6412ac 100644 --- a/homeassistant/components/sensibo/translations/ja.json +++ b/homeassistant/components/sensibo/translations/ja.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API\u30ad\u30fc", - "name": "\u540d\u524d" + "api_key": "API\u30ad\u30fc" } } } diff --git a/homeassistant/components/sensibo/translations/nl.json b/homeassistant/components/sensibo/translations/nl.json index cc6529c3540..7235aa80a80 100644 --- a/homeassistant/components/sensibo/translations/nl.json +++ b/homeassistant/components/sensibo/translations/nl.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API-sleutel", - "name": "Naam" + "api_key": "API-sleutel" } } } diff --git a/homeassistant/components/sensibo/translations/no.json b/homeassistant/components/sensibo/translations/no.json index 9d3098ccd9e..43f17f52f2e 100644 --- a/homeassistant/components/sensibo/translations/no.json +++ b/homeassistant/components/sensibo/translations/no.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API-n\u00f8kkel", - "name": "Navn" + "api_key": "API-n\u00f8kkel" } } } diff --git a/homeassistant/components/sensibo/translations/pl.json b/homeassistant/components/sensibo/translations/pl.json index 022aaf52039..830ab3399b9 100644 --- a/homeassistant/components/sensibo/translations/pl.json +++ b/homeassistant/components/sensibo/translations/pl.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Klucz API", - "name": "Nazwa" + "api_key": "Klucz API" } } } diff --git a/homeassistant/components/sensibo/translations/pt-BR.json b/homeassistant/components/sensibo/translations/pt-BR.json index ff0ac4883ba..89b8984ac49 100644 --- a/homeassistant/components/sensibo/translations/pt-BR.json +++ b/homeassistant/components/sensibo/translations/pt-BR.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "Chave da API", - "name": "Nome" + "api_key": "Chave da API" } } } diff --git a/homeassistant/components/sensibo/translations/ru.json b/homeassistant/components/sensibo/translations/ru.json index 96574822758..aafef088706 100644 --- a/homeassistant/components/sensibo/translations/ru.json +++ b/homeassistant/components/sensibo/translations/ru.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + "api_key": "\u041a\u043b\u044e\u0447 API" } } } diff --git a/homeassistant/components/sensibo/translations/sk.json b/homeassistant/components/sensibo/translations/sk.json index c3bf532e7a3..d2401d8b7a3 100644 --- a/homeassistant/components/sensibo/translations/sk.json +++ b/homeassistant/components/sensibo/translations/sk.json @@ -6,8 +6,7 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d", - "name": "N\u00e1zov" + "api_key": "API k\u013e\u00fa\u010d" } } } diff --git a/homeassistant/components/sensibo/translations/tr.json b/homeassistant/components/sensibo/translations/tr.json index b08f683b200..fbe466cfd8d 100644 --- a/homeassistant/components/sensibo/translations/tr.json +++ b/homeassistant/components/sensibo/translations/tr.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API Anahtar\u0131", - "name": "Ad" + "api_key": "API Anahtar\u0131" } } } diff --git a/homeassistant/components/sensibo/translations/zh-Hant.json b/homeassistant/components/sensibo/translations/zh-Hant.json index 39cc7dc9662..c2639ea2ab9 100644 --- a/homeassistant/components/sensibo/translations/zh-Hant.json +++ b/homeassistant/components/sensibo/translations/zh-Hant.json @@ -19,8 +19,7 @@ }, "user": { "data": { - "api_key": "API \u91d1\u9470", - "name": "\u540d\u7a31" + "api_key": "API \u91d1\u9470" } } } diff --git a/homeassistant/components/sentry/translations/af.json b/homeassistant/components/sentry/translations/af.json deleted file mode 100644 index 7db651b6b96..00000000000 --- a/homeassistant/components/sentry/translations/af.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Sentry" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/sentry/translations/ca.json b/homeassistant/components/sentry/translations/ca.json index d83abb16f1e..b353a3a5a0e 100644 --- a/homeassistant/components/sentry/translations/ca.json +++ b/homeassistant/components/sentry/translations/ca.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Introdueix el DSN de Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/cs.json b/homeassistant/components/sentry/translations/cs.json index 28a2d603dd0..815d7a9bd09 100644 --- a/homeassistant/components/sentry/translations/cs.json +++ b/homeassistant/components/sentry/translations/cs.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "Zadejte sv\u00e9 Sentry DSN", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/da.json b/homeassistant/components/sentry/translations/da.json index 689bdad67ac..8a6f6788302 100644 --- a/homeassistant/components/sentry/translations/da.json +++ b/homeassistant/components/sentry/translations/da.json @@ -3,12 +3,6 @@ "error": { "bad_dsn": "Ugyldigt DSN", "unknown": "Uventet fejl" - }, - "step": { - "user": { - "description": "Indtast dit Sentry-DSN", - "title": "Sentry" - } } } } \ No newline at end of file diff --git a/homeassistant/components/sentry/translations/de.json b/homeassistant/components/sentry/translations/de.json index d408af0d885..9af3d633924 100644 --- a/homeassistant/components/sentry/translations/de.json +++ b/homeassistant/components/sentry/translations/de.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry-DSN" - }, - "description": "Gib deine Sentry-DSN ein", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/el.json b/homeassistant/components/sentry/translations/el.json index fd64b2140e2..d416005a9c2 100644 --- a/homeassistant/components/sentry/translations/el.json +++ b/homeassistant/components/sentry/translations/el.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf DSN \u03c4\u03bf\u03c5 Sentry \u03c3\u03b1\u03c2", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/en.json b/homeassistant/components/sentry/translations/en.json index 16bdac6b510..e989d4811fd 100644 --- a/homeassistant/components/sentry/translations/en.json +++ b/homeassistant/components/sentry/translations/en.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Enter your Sentry DSN", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/es-419.json b/homeassistant/components/sentry/translations/es-419.json index dd6866f7610..6e6269f693b 100644 --- a/homeassistant/components/sentry/translations/es-419.json +++ b/homeassistant/components/sentry/translations/es-419.json @@ -3,12 +3,6 @@ "error": { "bad_dsn": "DSN inv\u00e1lido", "unknown": "Error inesperado" - }, - "step": { - "user": { - "description": "Ingrese su DSN Sentry", - "title": "Centinela" - } } } } \ No newline at end of file diff --git a/homeassistant/components/sentry/translations/es.json b/homeassistant/components/sentry/translations/es.json index a2b5cc31f9a..0bb2f70720c 100644 --- a/homeassistant/components/sentry/translations/es.json +++ b/homeassistant/components/sentry/translations/es.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "Introduzca su DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/et.json b/homeassistant/components/sentry/translations/et.json index 01386fa97e6..9e414a9d935 100644 --- a/homeassistant/components/sentry/translations/et.json +++ b/homeassistant/components/sentry/translations/et.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Sisesta oma Sentry DSN", - "title": "" + } } } }, diff --git a/homeassistant/components/sentry/translations/fr.json b/homeassistant/components/sentry/translations/fr.json index 6850bfe8a71..6f041e4acdd 100644 --- a/homeassistant/components/sentry/translations/fr.json +++ b/homeassistant/components/sentry/translations/fr.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN Sentry" - }, - "description": "Entrez votre DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/hu.json b/homeassistant/components/sentry/translations/hu.json index 0535657c3d0..5be91c081b7 100644 --- a/homeassistant/components/sentry/translations/hu.json +++ b/homeassistant/components/sentry/translations/hu.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Adja meg a Sentry DSN-t", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/id.json b/homeassistant/components/sentry/translations/id.json index 5f81eb1a445..1acc9574e82 100644 --- a/homeassistant/components/sentry/translations/id.json +++ b/homeassistant/components/sentry/translations/id.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Masukkan DSN Sentry Anda", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/it.json b/homeassistant/components/sentry/translations/it.json index 10a0d1bad2b..24738be06b8 100644 --- a/homeassistant/components/sentry/translations/it.json +++ b/homeassistant/components/sentry/translations/it.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN Sentry" - }, - "description": "Inserisci il tuo DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/ja.json b/homeassistant/components/sentry/translations/ja.json index 13ce3b4a5ff..8ac8ebf58a1 100644 --- a/homeassistant/components/sentry/translations/ja.json +++ b/homeassistant/components/sentry/translations/ja.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "Sentry DSN\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/ko.json b/homeassistant/components/sentry/translations/ko.json index 92e26b30c42..efd5e558edb 100644 --- a/homeassistant/components/sentry/translations/ko.json +++ b/homeassistant/components/sentry/translations/ko.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "Sentry DSN \uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/lb.json b/homeassistant/components/sentry/translations/lb.json index 3041a7eb156..8ab1be71190 100644 --- a/homeassistant/components/sentry/translations/lb.json +++ b/homeassistant/components/sentry/translations/lb.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "Gitt \u00e4r Sentry DSN un", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/nl.json b/homeassistant/components/sentry/translations/nl.json index 67be245ffde..0397ac0f609 100644 --- a/homeassistant/components/sentry/translations/nl.json +++ b/homeassistant/components/sentry/translations/nl.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Voer uw Sentry DSN in", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/no.json b/homeassistant/components/sentry/translations/no.json index c3bb4acd26e..11e358f4908 100644 --- a/homeassistant/components/sentry/translations/no.json +++ b/homeassistant/components/sentry/translations/no.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Fyll inn din Sentry DNS", - "title": "" + } } } }, diff --git a/homeassistant/components/sentry/translations/pl.json b/homeassistant/components/sentry/translations/pl.json index be5d008dcff..37489c9b619 100644 --- a/homeassistant/components/sentry/translations/pl.json +++ b/homeassistant/components/sentry/translations/pl.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Wprowad\u017a DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/pt-BR.json b/homeassistant/components/sentry/translations/pt-BR.json index c489de8ee6d..39a7448716e 100644 --- a/homeassistant/components/sentry/translations/pt-BR.json +++ b/homeassistant/components/sentry/translations/pt-BR.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentinela DSN" - }, - "description": "Digite seu DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/ru.json b/homeassistant/components/sentry/translations/ru.json index 6a7192a5385..903ca88bc6b 100644 --- a/homeassistant/components/sentry/translations/ru.json +++ b/homeassistant/components/sentry/translations/ru.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0412\u0430\u0448 DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/sl.json b/homeassistant/components/sentry/translations/sl.json index 5a448c5b673..57990b75063 100644 --- a/homeassistant/components/sentry/translations/sl.json +++ b/homeassistant/components/sentry/translations/sl.json @@ -3,12 +3,6 @@ "error": { "bad_dsn": "Neveljaven DSN", "unknown": "Nepri\u010dakovana napaka" - }, - "step": { - "user": { - "description": "Vpi\u0161ite va\u0161 Sentry DSN", - "title": "Sentry" - } } } } \ No newline at end of file diff --git a/homeassistant/components/sentry/translations/sv.json b/homeassistant/components/sentry/translations/sv.json index d04ea24b72a..45c4508ff9e 100644 --- a/homeassistant/components/sentry/translations/sv.json +++ b/homeassistant/components/sentry/translations/sv.json @@ -3,12 +3,6 @@ "error": { "bad_dsn": "Ogiltig DSN", "unknown": "Ov\u00e4ntat fel" - }, - "step": { - "user": { - "description": "Ange din Sentry DSN", - "title": "Sentry" - } } } } \ No newline at end of file diff --git a/homeassistant/components/sentry/translations/tr.json b/homeassistant/components/sentry/translations/tr.json index 6369f727d17..a32bf17f843 100644 --- a/homeassistant/components/sentry/translations/tr.json +++ b/homeassistant/components/sentry/translations/tr.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "Sentry DSN'nizi girin", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/uk.json b/homeassistant/components/sentry/translations/uk.json index 124ac4543ed..4246abba8dc 100644 --- a/homeassistant/components/sentry/translations/uk.json +++ b/homeassistant/components/sentry/translations/uk.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "DSN" - }, - "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0412\u0430\u0448 DSN Sentry", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/sentry/translations/zh-Hant.json b/homeassistant/components/sentry/translations/zh-Hant.json index 6d4615db892..99330fb4f12 100644 --- a/homeassistant/components/sentry/translations/zh-Hant.json +++ b/homeassistant/components/sentry/translations/zh-Hant.json @@ -11,9 +11,7 @@ "user": { "data": { "dsn": "Sentry DSN" - }, - "description": "\u8f38\u5165 Sentry DSN", - "title": "Sentry" + } } } }, diff --git a/homeassistant/components/senz/translations/ko.json b/homeassistant/components/senz/translations/ko.json new file mode 100644 index 00000000000..c3f709296a5 --- /dev/null +++ b/homeassistant/components/senz/translations/ko.json @@ -0,0 +1,16 @@ +{ + "config": { + "abort": { + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", + "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4." + }, + "create_entry": { + "default": "\uc131\uacf5\uc801\uc73c\ub85c \uc778\uc99d\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "pick_implementation": { + "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/shelly/translations/ca.json b/homeassistant/components/shelly/translations/ca.json index 6430bdcf6a6..a9b1347e62e 100644 --- a/homeassistant/components/shelly/translations/ca.json +++ b/homeassistant/components/shelly/translations/ca.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3", + "firmware_not_fully_provisioned": "Dispositiu no totalment aprovisionat. Posa't en contacte amb l'assist\u00e8ncia de Shelly", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "unknown": "Error inesperat" }, diff --git a/homeassistant/components/shelly/translations/fr.json b/homeassistant/components/shelly/translations/fr.json index cd19af62bad..3ed78b53b63 100644 --- a/homeassistant/components/shelly/translations/fr.json +++ b/homeassistant/components/shelly/translations/fr.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u00c9chec de connexion", + "firmware_not_fully_provisioned": "L'appareil n'est pas enti\u00e8rement provisionn\u00e9. Veuillez contacter le support Shelly", "invalid_auth": "Authentification non valide", "unknown": "Erreur inattendue" }, diff --git a/homeassistant/components/shelly/translations/hu.json b/homeassistant/components/shelly/translations/hu.json index bfaf591d7c2..18f53ca232d 100644 --- a/homeassistant/components/shelly/translations/hu.json +++ b/homeassistant/components/shelly/translations/hu.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "firmware_not_fully_provisioned": "Az eszk\u00f6z nincs teljesen be\u00fczemelve. K\u00e9rj\u00fck, vegye fel a kapcsolatot a Shelly \u00fcgyf\u00e9lszolg\u00e1lat\u00e1val", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, diff --git a/homeassistant/components/shelly/translations/it.json b/homeassistant/components/shelly/translations/it.json index c004141cac4..7bd26761ad8 100644 --- a/homeassistant/components/shelly/translations/it.json +++ b/homeassistant/components/shelly/translations/it.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Impossibile connettersi", + "firmware_not_fully_provisioned": "Dispositivo non completamente supportato. Si prega di contattare il supporto Shelly", "invalid_auth": "Autenticazione non valida", "unknown": "Errore imprevisto" }, diff --git a/homeassistant/components/shelly/translations/pt-BR.json b/homeassistant/components/shelly/translations/pt-BR.json index 8b8ec5ea020..125334b8d28 100644 --- a/homeassistant/components/shelly/translations/pt-BR.json +++ b/homeassistant/components/shelly/translations/pt-BR.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Falha ao conectar", + "firmware_not_fully_provisioned": "Dispositivo n\u00e3o totalmente provisionado. Entre em contato com o suporte da Shelly", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, diff --git a/homeassistant/components/sia/translations/ar.json b/homeassistant/components/sia/translations/ar.json index ebf7325b114..54e29ccfa5d 100644 --- a/homeassistant/components/sia/translations/ar.json +++ b/homeassistant/components/sia/translations/ar.json @@ -5,6 +5,5 @@ "title": "\u0625\u0646\u0634\u0627\u0621 \u0627\u062a\u0635\u0627\u0644 \u0644\u0623\u0646\u0638\u0645\u0629 \u0627\u0644\u0625\u0646\u0630\u0627\u0631 \u0627\u0644\u0642\u0627\u0626\u0645\u0629 \u0639\u0644\u0649 SIA." } } - }, - "title": "\u0623\u0646\u0638\u0645\u0629 \u0625\u0646\u0630\u0627\u0631 SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/ca.json b/homeassistant/components/sia/translations/ca.json index 904d1542c8f..ed51fec34cd 100644 --- a/homeassistant/components/sia/translations/ca.json +++ b/homeassistant/components/sia/translations/ca.json @@ -45,6 +45,5 @@ "title": "Opcions de configuraci\u00f3 de SIA." } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/de.json b/homeassistant/components/sia/translations/de.json index d2c9fc05040..385e19b9c64 100644 --- a/homeassistant/components/sia/translations/de.json +++ b/homeassistant/components/sia/translations/de.json @@ -45,6 +45,5 @@ "title": "Optionen f\u00fcr das SIA-Setup." } } - }, - "title": "SIA Alarmsysteme" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/el.json b/homeassistant/components/sia/translations/el.json index fe565e503a1..e6d2253301a 100644 --- a/homeassistant/components/sia/translations/el.json +++ b/homeassistant/components/sia/translations/el.json @@ -45,6 +45,5 @@ "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 SIA." } } - }, - "title": "\u03a3\u03c5\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03b1 \u03a3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/en.json b/homeassistant/components/sia/translations/en.json index 50a00a4bf23..9dea235b379 100644 --- a/homeassistant/components/sia/translations/en.json +++ b/homeassistant/components/sia/translations/en.json @@ -45,6 +45,5 @@ "title": "Options for the SIA Setup." } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/es.json b/homeassistant/components/sia/translations/es.json index 8e1bb05978d..8b8b7e97f1f 100644 --- a/homeassistant/components/sia/translations/es.json +++ b/homeassistant/components/sia/translations/es.json @@ -45,6 +45,5 @@ "title": "Opciones para la configuraci\u00f3n de SIA." } } - }, - "title": "Sistemas de alarma SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/et.json b/homeassistant/components/sia/translations/et.json index 931718d78d8..ff1f73d217e 100644 --- a/homeassistant/components/sia/translations/et.json +++ b/homeassistant/components/sia/translations/et.json @@ -45,6 +45,5 @@ "title": "SIA seadistuse valikud." } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/fr.json b/homeassistant/components/sia/translations/fr.json index 843c707ce19..e3f36f8930c 100644 --- a/homeassistant/components/sia/translations/fr.json +++ b/homeassistant/components/sia/translations/fr.json @@ -45,6 +45,5 @@ "title": "Options pour la configuration SIA." } } - }, - "title": "Syst\u00e8mes d'alarme SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/hu.json b/homeassistant/components/sia/translations/hu.json index 6a5c609e1f6..63a5208a44c 100644 --- a/homeassistant/components/sia/translations/hu.json +++ b/homeassistant/components/sia/translations/hu.json @@ -45,6 +45,5 @@ "title": "A SIA be\u00e1ll\u00edt\u00e1si lehet\u0151s\u00e9gek." } } - }, - "title": "SIA riaszt\u00f3rendszerek" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/id.json b/homeassistant/components/sia/translations/id.json index 4c80d089cbe..b9c927997f3 100644 --- a/homeassistant/components/sia/translations/id.json +++ b/homeassistant/components/sia/translations/id.json @@ -45,6 +45,5 @@ "title": "Opsi untuk Pengaturan SIA." } } - }, - "title": "Sistem Alarm SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/it.json b/homeassistant/components/sia/translations/it.json index 977b316c9a5..f3e1b02c55a 100644 --- a/homeassistant/components/sia/translations/it.json +++ b/homeassistant/components/sia/translations/it.json @@ -45,6 +45,5 @@ "title": "Opzioni per l'impostazione SIA." } } - }, - "title": "Sistemi di allarme SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/ja.json b/homeassistant/components/sia/translations/ja.json index 9286548ddb4..7e653b1e348 100644 --- a/homeassistant/components/sia/translations/ja.json +++ b/homeassistant/components/sia/translations/ja.json @@ -45,6 +45,5 @@ "title": "SIA\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3002" } } - }, - "title": "SIA\u30a2\u30e9\u30fc\u30e0\u30b7\u30b9\u30c6\u30e0" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/nl.json b/homeassistant/components/sia/translations/nl.json index 8afc0b88651..cd0d5183cb9 100644 --- a/homeassistant/components/sia/translations/nl.json +++ b/homeassistant/components/sia/translations/nl.json @@ -45,6 +45,5 @@ "title": "Opties voor de SIA Setup." } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/no.json b/homeassistant/components/sia/translations/no.json index 61ce61b7ee5..7f8cee998e5 100644 --- a/homeassistant/components/sia/translations/no.json +++ b/homeassistant/components/sia/translations/no.json @@ -45,6 +45,5 @@ "title": "Alternativer for SIA-oppsett." } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/pl.json b/homeassistant/components/sia/translations/pl.json index d41281519d4..8a7f5342e3f 100644 --- a/homeassistant/components/sia/translations/pl.json +++ b/homeassistant/components/sia/translations/pl.json @@ -45,6 +45,5 @@ "title": "Opcje dla konfiguracji SIA." } } - }, - "title": "Systemy alarmowe SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/pt-BR.json b/homeassistant/components/sia/translations/pt-BR.json index a113717859f..55738b7728e 100644 --- a/homeassistant/components/sia/translations/pt-BR.json +++ b/homeassistant/components/sia/translations/pt-BR.json @@ -45,6 +45,5 @@ "title": "Op\u00e7\u00f5es para a configura\u00e7\u00e3o SIA." } } - }, - "title": "Sistemas de alarme SIA" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/ru.json b/homeassistant/components/sia/translations/ru.json index f7cfd4b5152..807a7a80877 100644 --- a/homeassistant/components/sia/translations/ru.json +++ b/homeassistant/components/sia/translations/ru.json @@ -45,6 +45,5 @@ "title": "SIA Alarm Systems" } } - }, - "title": "SIA Alarm Systems" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/tr.json b/homeassistant/components/sia/translations/tr.json index 8d5a1d9dbcb..2fa88f6fbc6 100644 --- a/homeassistant/components/sia/translations/tr.json +++ b/homeassistant/components/sia/translations/tr.json @@ -45,6 +45,5 @@ "title": "SIA Kurulumu i\u00e7in se\u00e7enekler." } } - }, - "title": "SIA Alarm Sistemleri" + } } \ No newline at end of file diff --git a/homeassistant/components/sia/translations/zh-Hant.json b/homeassistant/components/sia/translations/zh-Hant.json index 81a0ad0fab4..310099e4882 100644 --- a/homeassistant/components/sia/translations/zh-Hant.json +++ b/homeassistant/components/sia/translations/zh-Hant.json @@ -45,6 +45,5 @@ "title": "SIA \u8a2d\u5b9a\u9078\u9805" } } - }, - "title": "SIA \u8b66\u5831\u7cfb\u7d71" + } } \ No newline at end of file diff --git a/homeassistant/components/simplisafe/translations/bg.json b/homeassistant/components/simplisafe/translations/bg.json index d193393c534..da70697442c 100644 --- a/homeassistant/components/simplisafe/translations/bg.json +++ b/homeassistant/components/simplisafe/translations/bg.json @@ -4,7 +4,6 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { - "identifier_exists": "\u041f\u0440\u043e\u0444\u0438\u043b\u044a\u0442 \u0435 \u0432\u0435\u0447\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d", "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, @@ -25,11 +24,9 @@ }, "user": { "data": { - "auth_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "username": "E-mail \u0430\u0434\u0440\u0435\u0441" - }, - "title": "\u041f\u043e\u043f\u044a\u043b\u043d\u0435\u0442\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\u0442\u0430 \u0441\u0438" + } } } } diff --git a/homeassistant/components/simplisafe/translations/ca.json b/homeassistant/components/simplisafe/translations/ca.json index 82ac0eabbff..a2fd356932c 100644 --- a/homeassistant/components/simplisafe/translations/ca.json +++ b/homeassistant/components/simplisafe/translations/ca.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Aquest compte SimpliSafe ja est\u00e0 en \u00fas.", "email_2fa_timed_out": "S'ha esgotat el temps d'espera de l'autenticaci\u00f3 de dos factors a trav\u00e9s de correu electr\u00f2nic.", - "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament", - "wrong_account": "Les credencials d'usuari proporcionades no coincideixen amb les d'aquest compte SimpliSafe." + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" }, "error": { - "2fa_timed_out": "S'ha esgotat el temps d'espera m\u00e0xim durant l'autenticaci\u00f3 de dos factors", - "identifier_exists": "Aquest compte ja est\u00e0 registrat", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "still_awaiting_mfa": "Esperant clic de l'enlla\u00e7 del correu MFA", "unknown": "Error inesperat" }, "progress": { "email_2fa": "Mira el correu electr\u00f2nic on hauries de trobar l'enlla\u00e7 de verificaci\u00f3 de Simplisafe." }, "step": { - "mfa": { - "title": "Autenticaci\u00f3 multi-factor SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Contrasenya" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Codi d'autoritzaci\u00f3", - "code": "Codi (utilitzat a la UI de Home Assistant)", "password": "Contrasenya", "username": "Nom d'usuari" }, - "description": "Introdueix el teu nom d'usuari i contrasenya.", - "title": "Introdueix la teva informaci\u00f3" + "description": "Introdueix el teu nom d'usuari i contrasenya." } } }, diff --git a/homeassistant/components/simplisafe/translations/cs.json b/homeassistant/components/simplisafe/translations/cs.json index 152c0282216..520dcc2567e 100644 --- a/homeassistant/components/simplisafe/translations/cs.json +++ b/homeassistant/components/simplisafe/translations/cs.json @@ -5,14 +5,10 @@ "reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9" }, "error": { - "identifier_exists": "\u00da\u010det je ji\u017e zaregistrov\u00e1n", "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "mfa": { - "title": "V\u00edcefaktorov\u00e9 ov\u011b\u0159ov\u00e1n\u00ed SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Heslo" @@ -22,12 +18,9 @@ }, "user": { "data": { - "auth_code": "Autoriza\u010dn\u00ed k\u00f3d", - "code": "K\u00f3d (pou\u017eit\u00fd v u\u017eivatelsk\u00e9m rozhran\u00ed Home Assistant)", "password": "Heslo", "username": "E-mail" - }, - "title": "Vypl\u0148te sv\u00e9 \u00fadaje." + } } } }, diff --git a/homeassistant/components/simplisafe/translations/da.json b/homeassistant/components/simplisafe/translations/da.json index a5119762f90..8133eee3ec9 100644 --- a/homeassistant/components/simplisafe/translations/da.json +++ b/homeassistant/components/simplisafe/translations/da.json @@ -3,16 +3,12 @@ "abort": { "already_configured": "Denne SimpliSafe-konto er allerede i brug." }, - "error": { - "identifier_exists": "Konto er allerede registreret" - }, "step": { "user": { "data": { "password": "Adgangskode", "username": "Emailadresse" - }, - "title": "Udfyld dine oplysninger" + } } } } diff --git a/homeassistant/components/simplisafe/translations/de.json b/homeassistant/components/simplisafe/translations/de.json index 1f7cd74043b..d224f3c2441 100644 --- a/homeassistant/components/simplisafe/translations/de.json +++ b/homeassistant/components/simplisafe/translations/de.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Dieses SimpliSafe-Konto wird bereits verwendet.", "email_2fa_timed_out": "Zeit\u00fcberschreitung beim Warten auf E-Mail-basierte Zwei-Faktor-Authentifizierung.", - "reauth_successful": "Die erneute Authentifizierung war erfolgreich", - "wrong_account": "Die angegebenen Benutzeranmeldeinformationen stimmen nicht mit diesem SimpliSafe-Konto \u00fcberein." + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" }, "error": { - "2fa_timed_out": "Zeit\u00fcberschreitung beim Warten auf Zwei-Faktor-Authentifizierung", - "identifier_exists": "Konto bereits registriert", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "still_awaiting_mfa": "Immernoch warten auf MFA-E-Mail-Klick", "unknown": "Unerwarteter Fehler" }, "progress": { "email_2fa": "\u00dcberpr\u00fcfe deine E-Mails auf einen Best\u00e4tigungslink von Simplisafe." }, "step": { - "mfa": { - "title": "SimpliSafe Multi-Faktor-Authentifizierung" - }, "reauth_confirm": { "data": { "password": "Passwort" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Autorisierungscode", - "code": "Code (wird in der Benutzeroberfl\u00e4che von Home Assistant verwendet)", "password": "Passwort", "username": "Benutzername" }, - "description": "Gib deinen Benutzernamen und Passwort ein.", - "title": "Gib deine Informationen ein" + "description": "Gib deinen Benutzernamen und Passwort ein." } } }, diff --git a/homeassistant/components/simplisafe/translations/el.json b/homeassistant/components/simplisafe/translations/el.json index 5253a1bfc52..d852664ce38 100644 --- a/homeassistant/components/simplisafe/translations/el.json +++ b/homeassistant/components/simplisafe/translations/el.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "\u0391\u03c5\u03c4\u03cc\u03c2 \u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 SimpliSafe \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7.", "email_2fa_timed_out": "\u03a4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03ad\u03bb\u03b7\u03be\u03b5 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd \u03c0\u03bf\u03c5 \u03b2\u03b1\u03c3\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 email.", - "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2", - "wrong_account": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03c0\u03b1\u03c1\u03ad\u03c7\u03bf\u03bd\u03c4\u03b1\u03b9 \u03b4\u03b5\u03bd \u03c4\u03b1\u03b9\u03c1\u03b9\u03ac\u03b6\u03bf\u03c5\u03bd \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc SimpliSafe." + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" }, "error": { - "2fa_timed_out": "\u0388\u03bb\u03b7\u03be\u03b5 \u03c4\u03bf \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bd\u03b1\u03bc\u03bf\u03bd\u03ae \u03b3\u03b9\u03b1 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd", - "identifier_exists": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ae\u03b4\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03b7\u03bc\u03ad\u03bd\u03bf\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "still_awaiting_mfa": "\u0391\u03bd\u03b1\u03bc\u03ad\u03bd\u03b5\u03c4\u03b1\u03b9 \u03b1\u03ba\u03cc\u03bc\u03b7 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf email \u03c4\u03bf\u03c5 \u03a5\u03c0\u03bf\u03c5\u03c1\u03b3\u03b5\u03af\u03bf\u03c5 \u039f\u03b9\u03ba\u03bf\u03bd\u03bf\u03bc\u03b9\u03ba\u03ce\u03bd", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, "progress": { "email_2fa": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b4\u03cd\u03bf \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd\n\u03c0\u03bf\u03c5 \u03c3\u03b1\u03c2 \u03b5\u03c3\u03c4\u03ac\u03bb\u03b7 \u03bc\u03ad\u03c3\u03c9 email." }, "step": { - "mfa": { - "title": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03c0\u03bf\u03bb\u03bb\u03b1\u03c0\u03bb\u03ce\u03bd \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd SimpliSafe" - }, "reauth_confirm": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2", - "code": "\u039a\u03ce\u03b4\u03b9\u03ba\u03b1\u03c2 (\u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf UI \u03c4\u03bf\u03c5 Home Assistant)", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "Email" }, - "description": "\u03a4\u03bf SimpliSafe \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03c4\u03bf Home Assistant \u03bc\u03ad\u03c3\u03c9 \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 SimpliSafe web. \u039b\u03cc\u03b3\u03c9 \u03c4\u03b5\u03c7\u03bd\u03b9\u03ba\u03ce\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd, \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03b2\u03ae\u03bc\u03b1 \u03c3\u03c4\u03bf \u03c4\u03ad\u03bb\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1\u03c2- \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03b4\u03b9\u03b1\u03b2\u03ac\u03c3\u03b5\u03b9 \u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7]({docs_url}) \u03c0\u03c1\u03b9\u03bd \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5.\n\n1. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf [\u03b5\u03b4\u03ce]({url}) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03bd\u03bf\u03af\u03be\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae SimpliSafe web \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2.\n\n2. \u038c\u03c4\u03b1\u03bd \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03ba\u03b1\u03b9 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2.", - "title": "\u03a3\u03c5\u03bc\u03c0\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03b1 \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03b1 \u03c3\u03b1\u03c2" + "description": "\u03a4\u03bf SimpliSafe \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03bc\u03b5 \u03c4\u03bf Home Assistant \u03bc\u03ad\u03c3\u03c9 \u03c4\u03b7\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 SimpliSafe web. \u039b\u03cc\u03b3\u03c9 \u03c4\u03b5\u03c7\u03bd\u03b9\u03ba\u03ce\u03bd \u03c0\u03b5\u03c1\u03b9\u03bf\u03c1\u03b9\u03c3\u03bc\u03ce\u03bd, \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ad\u03bd\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03b2\u03ae\u03bc\u03b1 \u03c3\u03c4\u03bf \u03c4\u03ad\u03bb\u03bf\u03c2 \u03b1\u03c5\u03c4\u03ae\u03c2 \u03c4\u03b7\u03c2 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1\u03c2- \u03b2\u03b5\u03b2\u03b1\u03b9\u03c9\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c4\u03b9 \u03ad\u03c7\u03b5\u03c4\u03b5 \u03b4\u03b9\u03b1\u03b2\u03ac\u03c3\u03b5\u03b9 \u03c4\u03b7\u03bd [\u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7]({docs_url}) \u03c0\u03c1\u03b9\u03bd \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5.\n\n1. \u039a\u03ac\u03bd\u03c4\u03b5 \u03ba\u03bb\u03b9\u03ba \u03c3\u03c4\u03bf [\u03b5\u03b4\u03ce]({url}) \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03bd\u03bf\u03af\u03be\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae SimpliSafe web \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2.\n\n2. \u038c\u03c4\u03b1\u03bd \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03c9\u03b8\u03b5\u03af \u03b7 \u03b4\u03b9\u03b1\u03b4\u03b9\u03ba\u03b1\u03c3\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2, \u03b5\u03c0\u03b9\u03c3\u03c4\u03c1\u03ad\u03c8\u03c4\u03b5 \u03b5\u03b4\u03ce \u03ba\u03b1\u03b9 \u03b5\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03ac\u03c4\u03c9 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2." } } }, diff --git a/homeassistant/components/simplisafe/translations/en.json b/homeassistant/components/simplisafe/translations/en.json index 0aa7cf4cec1..0da6f6442e4 100644 --- a/homeassistant/components/simplisafe/translations/en.json +++ b/homeassistant/components/simplisafe/translations/en.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "This SimpliSafe account is already in use.", "email_2fa_timed_out": "Timed out while waiting for email-based two-factor authentication.", - "reauth_successful": "Re-authentication was successful", - "wrong_account": "The user credentials provided do not match this SimpliSafe account." + "reauth_successful": "Re-authentication was successful" }, "error": { - "2fa_timed_out": "Timed out while waiting for two-factor authentication", - "identifier_exists": "Account already registered", "invalid_auth": "Invalid authentication", - "still_awaiting_mfa": "Still awaiting MFA email click", "unknown": "Unexpected error" }, "progress": { "email_2fa": "Check your email for a verification link from Simplisafe." }, "step": { - "mfa": { - "title": "SimpliSafe Multi-Factor Authentication" - }, "reauth_confirm": { "data": { "password": "Password" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Authorization Code", - "code": "Code (used in Home Assistant UI)", "password": "Password", "username": "Username" }, - "description": "Input your username and password.", - "title": "Fill in your information." + "description": "Input your username and password." } } }, diff --git a/homeassistant/components/simplisafe/translations/es-419.json b/homeassistant/components/simplisafe/translations/es-419.json index 7cdd07029eb..868d4d4f53d 100644 --- a/homeassistant/components/simplisafe/translations/es-419.json +++ b/homeassistant/components/simplisafe/translations/es-419.json @@ -3,17 +3,12 @@ "abort": { "already_configured": "Esta cuenta SimpliSafe ya est\u00e1 en uso." }, - "error": { - "identifier_exists": "Cuenta ya registrada" - }, "step": { "user": { "data": { - "code": "C\u00f3digo (utilizado en la interfaz de usuario de Home Assistant)", "password": "Contrase\u00f1a", "username": "Direcci\u00f3n de correo electr\u00f3nico" - }, - "title": "Completa tu informaci\u00f3n" + } } } }, diff --git a/homeassistant/components/simplisafe/translations/es.json b/homeassistant/components/simplisafe/translations/es.json index 632fc50e4c0..44716b87cec 100644 --- a/homeassistant/components/simplisafe/translations/es.json +++ b/homeassistant/components/simplisafe/translations/es.json @@ -2,23 +2,16 @@ "config": { "abort": { "already_configured": "Esta cuenta SimpliSafe ya est\u00e1 en uso.", - "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente", - "wrong_account": "Las credenciales de usuario proporcionadas no coinciden con esta cuenta de SimpliSafe." + "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente" }, "error": { - "2fa_timed_out": "Se ha agotado el tiempo de espera m\u00e1ximo durante la autenticaci\u00f3n de dos factores", - "identifier_exists": "Cuenta ya registrada", "invalid_auth": "Autenticaci\u00f3n inv\u00e1lida", - "still_awaiting_mfa": "Esperando todav\u00eda el clic en el correo electr\u00f3nico de MFA", "unknown": "Error inesperado" }, "progress": { "email_2fa": "Mira el correo electr\u00f3nico donde deber\u00edas encontrar el enlace de verificaci\u00f3n de Simplisafe." }, "step": { - "mfa": { - "title": "Autenticaci\u00f3n Multi-Factor SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Contrase\u00f1a" @@ -34,13 +27,10 @@ }, "user": { "data": { - "auth_code": "C\u00f3digo de Autorizaci\u00f3n", - "code": "C\u00f3digo (utilizado en el interfaz de usuario de Home Assistant)", "password": "Contrase\u00f1a", "username": "Correo electr\u00f3nico" }, - "description": "SimpliSafe se autentifica con Home Assistant a trav\u00e9s de la aplicaci\u00f3n web de SimpliSafe. Debido a limitaciones t\u00e9cnicas, hay un paso manual al final de este proceso; aseg\u00farese de leer la [documentaci\u00f3n]({docs_url}) antes de empezar.\n\n1. Haga clic en [aqu\u00ed]({url}) para abrir la aplicaci\u00f3n web de SimpliSafe e introduzca sus credenciales.\n\n2. Cuando el proceso de inicio de sesi\u00f3n haya finalizado, vuelva aqu\u00ed e introduzca el c\u00f3digo de autorizaci\u00f3n que aparece a continuaci\u00f3n.", - "title": "Introduce tu informaci\u00f3n" + "description": "SimpliSafe se autentifica con Home Assistant a trav\u00e9s de la aplicaci\u00f3n web de SimpliSafe. Debido a limitaciones t\u00e9cnicas, hay un paso manual al final de este proceso; aseg\u00farese de leer la [documentaci\u00f3n]({docs_url}) antes de empezar.\n\n1. Haga clic en [aqu\u00ed]({url}) para abrir la aplicaci\u00f3n web de SimpliSafe e introduzca sus credenciales.\n\n2. Cuando el proceso de inicio de sesi\u00f3n haya finalizado, vuelva aqu\u00ed e introduzca el c\u00f3digo de autorizaci\u00f3n que aparece a continuaci\u00f3n." } } }, diff --git a/homeassistant/components/simplisafe/translations/et.json b/homeassistant/components/simplisafe/translations/et.json index 75c908e60c0..0f01f4d9b9c 100644 --- a/homeassistant/components/simplisafe/translations/et.json +++ b/homeassistant/components/simplisafe/translations/et.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "See SimpliSafe'i konto on juba kasutusel.", "email_2fa_timed_out": "Meilip\u00f5hise kahefaktorilise autentimise ajal\u00f5pp.", - "reauth_successful": "Taastuvastamine \u00f5nnestus", - "wrong_account": "Esitatud kasutaja mandaadid ei \u00fchti selle SimpliSafe kontoga." + "reauth_successful": "Taastuvastamine \u00f5nnestus" }, "error": { - "2fa_timed_out": "Kahefaktoriline autentimine aegus", - "identifier_exists": "Konto on juba registreeritud", "invalid_auth": "Tuvastamise viga", - "still_awaiting_mfa": "Ootan endiselt MFA e-posti klikki", "unknown": "Tundmatu viga" }, "progress": { "email_2fa": "Kontrolli oma meili Simplisafe'i kinnituslingi saamiseks." }, "step": { - "mfa": { - "title": "SimpliSafe mitmeastmeline autentimine" - }, "reauth_confirm": { "data": { "password": "Salas\u00f5na" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Tuvastuskood", - "code": "Kood (kasutatakse Home Assistant'i kasutajaliideses)", "password": "Salas\u00f5na", "username": "Kasutajanimi" }, - "description": "Sisesta kasutajatunnus ja salas\u00f5na", - "title": "Sisesta oma teave." + "description": "Sisesta kasutajatunnus ja salas\u00f5na" } } }, diff --git a/homeassistant/components/simplisafe/translations/fi.json b/homeassistant/components/simplisafe/translations/fi.json index d3d18987b44..47a52e9a0c6 100644 --- a/homeassistant/components/simplisafe/translations/fi.json +++ b/homeassistant/components/simplisafe/translations/fi.json @@ -1,15 +1,11 @@ { "config": { - "error": { - "identifier_exists": "Tili on jo rekister\u00f6ity" - }, "step": { "user": { "data": { "password": "Salasana", "username": "S\u00e4hk\u00f6postiosoite" - }, - "title": "T\u00e4yt\u00e4 tietosi." + } } } } diff --git a/homeassistant/components/simplisafe/translations/fr.json b/homeassistant/components/simplisafe/translations/fr.json index 1abfb1c4638..4de2af95885 100644 --- a/homeassistant/components/simplisafe/translations/fr.json +++ b/homeassistant/components/simplisafe/translations/fr.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Ce compte SimpliSafe est d\u00e9j\u00e0 utilis\u00e9.", "email_2fa_timed_out": "D\u00e9lai d'attente de l'authentification \u00e0 deux facteurs par courriel expir\u00e9.", - "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi", - "wrong_account": "Les informations d'identification d'utilisateur fournies ne correspondent pas \u00e0 ce compte SimpliSafe." + "reauth_successful": "La r\u00e9-authentification a r\u00e9ussi" }, "error": { - "2fa_timed_out": "D\u00e9lai d'attente de l'authentification \u00e0 deux facteurs expir\u00e9", - "identifier_exists": "Compte d\u00e9j\u00e0 enregistr\u00e9", "invalid_auth": "Authentification non valide", - "still_awaiting_mfa": "En attente de clic sur le message \u00e9lectronique d'authentification multi facteur", "unknown": "Erreur inattendue" }, "progress": { "email_2fa": "Vous devriez recevoir un lien de v\u00e9rification par courriel envoy\u00e9 par Simplisafe." }, "step": { - "mfa": { - "title": "Authentification multi facteur SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Mot de passe" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Code d'autorisation", - "code": "Code (utilis\u00e9 dans l'interface Home Assistant)", "password": "Mot de passe", "username": "Nom d'utilisateur" }, - "description": "Saisissez votre nom d'utilisateur et votre mot de passe.", - "title": "Veuillez saisir vos informations" + "description": "Saisissez votre nom d'utilisateur et votre mot de passe." } } }, diff --git a/homeassistant/components/simplisafe/translations/hu.json b/homeassistant/components/simplisafe/translations/hu.json index 6a0e6d33642..df71ba7ca8b 100644 --- a/homeassistant/components/simplisafe/translations/hu.json +++ b/homeassistant/components/simplisafe/translations/hu.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Ez a SimpliSafe-fi\u00f3k m\u00e1r haszn\u00e1latban van.", "email_2fa_timed_out": "Az e-mail alap\u00fa k\u00e9tfaktoros hiteles\u00edt\u00e9sre val\u00f3 v\u00e1rakoz\u00e1s ideje lej\u00e1rt", - "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.", - "wrong_account": "A megadott felhaszn\u00e1l\u00f3i hiteles\u00edt\u0151 adatok nem j\u00f3k ehhez a SimpliSafe fi\u00f3khoz." + "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { - "2fa_timed_out": "A k\u00e9tfaktoros hiteles\u00edt\u00e9sre val\u00f3 v\u00e1rakoz\u00e1s ideje lej\u00e1rt", - "identifier_exists": "Fi\u00f3k m\u00e1r regisztr\u00e1lva van", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "still_awaiting_mfa": "M\u00e9g v\u00e1r az MFA e-mail kattint\u00e1sra", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "progress": { "email_2fa": "Adja meg a k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3dot amelyet e-mailben kapott." }, "step": { - "mfa": { - "title": "SimpliSafe t\u00f6bbt\u00e9nyez\u0151s hiteles\u00edt\u00e9s" - }, "reauth_confirm": { "data": { "password": "Jelsz\u00f3" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Enged\u00e9lyez\u00e9si k\u00f3d", - "code": "K\u00f3d (a Home Assistant felhaszn\u00e1l\u00f3i fel\u00fclet\u00e9n haszn\u00e1latos)", "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "Adja meg felhaszn\u00e1l\u00f3nev\u00e9t \u00e9s jelszav\u00e1t.", - "title": "T\u00f6ltse ki az adatait" + "description": "Adja meg felhaszn\u00e1l\u00f3nev\u00e9t \u00e9s jelszav\u00e1t." } } }, diff --git a/homeassistant/components/simplisafe/translations/id.json b/homeassistant/components/simplisafe/translations/id.json index 737831195f1..68da3b7689b 100644 --- a/homeassistant/components/simplisafe/translations/id.json +++ b/homeassistant/components/simplisafe/translations/id.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Akun SimpliSafe ini sudah digunakan.", "email_2fa_timed_out": "Tenggang waktu habis ketika menunggu autentikasi dua faktor berbasis email.", - "reauth_successful": "Autentikasi ulang berhasil", - "wrong_account": "Kredensial pengguna yang diberikan tidak cocok dengan akun SimpliSafe ini." + "reauth_successful": "Autentikasi ulang berhasil" }, "error": { - "2fa_timed_out": "Tenggang waktu habis saat menunggu autentikasi dua faktor", - "identifier_exists": "Akun sudah terdaftar", "invalid_auth": "Autentikasi tidak valid", - "still_awaiting_mfa": "Masih menunggu pengeklikan dari email MFA", "unknown": "Kesalahan yang tidak diharapkan" }, "progress": { "email_2fa": "Periksa email Anda untuk tautan verifikasi dari Simplisafe." }, "step": { - "mfa": { - "title": "Autentikasi Multi-Faktor SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Kata Sandi" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Kode Otorisasi", - "code": "Kode (digunakan di antarmuka Home Assistant)", "password": "Kata Sandi", "username": "Nama Pengguna" }, - "description": "Masukkan nama pengguna dan kata sandi Anda.", - "title": "Isi informasi Anda." + "description": "Masukkan nama pengguna dan kata sandi Anda." } } }, diff --git a/homeassistant/components/simplisafe/translations/it.json b/homeassistant/components/simplisafe/translations/it.json index 17a9a2ceb47..b9caae3f0a4 100644 --- a/homeassistant/components/simplisafe/translations/it.json +++ b/homeassistant/components/simplisafe/translations/it.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Questo account SimpliSafe \u00e8 gi\u00e0 in uso.", "email_2fa_timed_out": "Timeout durante l'attesa dell'autenticazione a due fattori basata su email.", - "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente", - "wrong_account": "Le credenziali utente fornite non corrispondono a questo account SimpliSafe." + "reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente" }, "error": { - "2fa_timed_out": "Timeout durante l'attesa dell'autenticazione a due fattori", - "identifier_exists": "Account gi\u00e0 registrato", "invalid_auth": "Autenticazione non valida", - "still_awaiting_mfa": "Ancora in attesa del clic sull'email MFA", "unknown": "Errore imprevisto" }, "progress": { "email_2fa": "Verifica la presenza nella tua email di un collegamento di verifica da Simplisafe." }, "step": { - "mfa": { - "title": "Autenticazione a pi\u00f9 fattori (MFA) SimpliSafe " - }, "reauth_confirm": { "data": { "password": "Password" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Codice di autorizzazione", - "code": "Codice (utilizzato nell'Interfaccia Utente di Home Assistant)", "password": "Password", "username": "Nome utente" }, - "description": "Digita il tuo nome utente e password.", - "title": "Inserisci le tue informazioni." + "description": "Digita il tuo nome utente e password." } } }, diff --git a/homeassistant/components/simplisafe/translations/ja.json b/homeassistant/components/simplisafe/translations/ja.json index 67e1630469e..57909d30f69 100644 --- a/homeassistant/components/simplisafe/translations/ja.json +++ b/homeassistant/components/simplisafe/translations/ja.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "\u3053\u306eSimpliSafe account\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002", "email_2fa_timed_out": "\u96fb\u5b50\u30e1\u30fc\u30eb\u306b\u3088\u308b2\u8981\u7d20\u8a8d\u8a3c\u306e\u5f85\u6a5f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\u3002", - "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f", - "wrong_account": "\u63d0\u4f9b\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u60c5\u5831\u304c\u3001\u3053\u306eSimpliSafe\u30a2\u30ab\u30a6\u30f3\u30c8\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093\u3002" + "reauth_successful": "\u518d\u8a8d\u8a3c\u306b\u6210\u529f\u3057\u307e\u3057\u305f" }, "error": { - "2fa_timed_out": "\u4e8c\u8981\u7d20\u8a8d\u8a3c\u306e\u5f85\u6a5f\u4e2d\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f", - "identifier_exists": "\u30a2\u30ab\u30a6\u30f3\u30c8\u767b\u9332\u6e08\u307f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "still_awaiting_mfa": "MFA email click\u3092\u307e\u3060\u5f85\u3063\u3066\u3044\u307e\u3059", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, "progress": { "email_2fa": "Simplisafe\u304b\u3089\u306e\u78ba\u8a8d\u30ea\u30f3\u30af\u304c\u306a\u3044\u304b\u30e1\u30fc\u30eb\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "step": { - "mfa": { - "title": "SimpliSafe\u591a\u8981\u7d20\u8a8d\u8a3c" - }, "reauth_confirm": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9", - "code": "\u30b3\u30fc\u30c9(Home Assistant UI\u3067\u4f7f\u7528)", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "E\u30e1\u30fc\u30eb" }, - "description": "2021\u5e74\u3088\u308a\u3001SimpliSafe\u306fWeb\u30a2\u30d7\u30ea\u306b\u3088\u308b\u65b0\u3057\u3044\u8a8d\u8a3c\u6a5f\u69cb\u306b\u79fb\u884c\u3057\u307e\u3057\u305f\u3002\u6280\u8853\u7684\u306a\u5236\u9650\u306e\u305f\u3081\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306e\u6700\u5f8c\u306b\u624b\u52d5\u3067\u306e\u624b\u9806\u304c\u3042\u308a\u307e\u3059\u3002\u958b\u59cb\u3059\u308b\u524d\u306b\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code)\u3092\u5fc5\u305a\u304a\u8aad\u307f\u304f\u3060\u3055\u3044\u3002\n\n\u6e96\u5099\u304c\u3067\u304d\u305f\u3089\u3001[\u3053\u3053]({url}) \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066SimpliSafe\u306eWeb\u30a2\u30d7\u30ea\u3092\u958b\u304d\u3001\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002\u51e6\u7406\u304c\u5b8c\u4e86\u3057\u305f\u3089\u3001\u3053\u3053\u306b\u623b\u3063\u3066\u304d\u3066\u9001\u4fe1(submit) \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059\u3002", - "title": "\u3042\u306a\u305f\u306e\u60c5\u5831\u3092\u8a18\u5165\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + "description": "2021\u5e74\u3088\u308a\u3001SimpliSafe\u306fWeb\u30a2\u30d7\u30ea\u306b\u3088\u308b\u65b0\u3057\u3044\u8a8d\u8a3c\u6a5f\u69cb\u306b\u79fb\u884c\u3057\u307e\u3057\u305f\u3002\u6280\u8853\u7684\u306a\u5236\u9650\u306e\u305f\u3081\u3001\u3053\u306e\u30d7\u30ed\u30bb\u30b9\u306e\u6700\u5f8c\u306b\u624b\u52d5\u3067\u306e\u624b\u9806\u304c\u3042\u308a\u307e\u3059\u3002\u958b\u59cb\u3059\u308b\u524d\u306b\u3001[\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](http://home-assistant.io/integrations/simplisafe#getting-an-authorization-code)\u3092\u5fc5\u305a\u304a\u8aad\u307f\u304f\u3060\u3055\u3044\u3002\n\n\u6e96\u5099\u304c\u3067\u304d\u305f\u3089\u3001[\u3053\u3053]({url}) \u3092\u30af\u30ea\u30c3\u30af\u3057\u3066SimpliSafe\u306eWeb\u30a2\u30d7\u30ea\u3092\u958b\u304d\u3001\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u307e\u3059\u3002\u51e6\u7406\u304c\u5b8c\u4e86\u3057\u305f\u3089\u3001\u3053\u3053\u306b\u623b\u3063\u3066\u304d\u3066\u9001\u4fe1(submit) \u3092\u30af\u30ea\u30c3\u30af\u3057\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/simplisafe/translations/ko.json b/homeassistant/components/simplisafe/translations/ko.json index 5c98e8abaa6..4cbc24533e0 100644 --- a/homeassistant/components/simplisafe/translations/ko.json +++ b/homeassistant/components/simplisafe/translations/ko.json @@ -5,15 +5,13 @@ "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { - "identifier_exists": "\uacc4\uc815\uc774 \uc774\ubbf8 \ub4f1\ub85d\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "still_awaiting_mfa": "\uc544\uc9c1 \ub2e4\ub2e8\uacc4 \uc778\uc99d(MFA) \uc774\uba54\uc77c\uc758 \ub9c1\ud06c \ud074\ub9ad\uc744 \uae30\ub2e4\ub9ac\uace0\uc788\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, + "progress": { + "email_2fa": "\uc774\uba54\uc77c\uc5d0\uc11c Simplisafe\uc758 \uc778\uc99d \ub9c1\ud06c\ub97c \ud655\uc778\ud558\uc2ed\uc2dc\uc624." + }, "step": { - "mfa": { - "title": "SimpliSafe \ub2e4\ub2e8\uacc4 \uc778\uc99d" - }, "reauth_confirm": { "data": { "password": "\ube44\ubc00\ubc88\ud638" @@ -23,11 +21,9 @@ }, "user": { "data": { - "code": "\ucf54\ub4dc (Home Assistant UI \uc5d0\uc11c \uc0ac\uc6a9\ub428)", "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc774\uba54\uc77c" - }, - "title": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694." + } } } }, diff --git a/homeassistant/components/simplisafe/translations/lb.json b/homeassistant/components/simplisafe/translations/lb.json index 080d8afd394..e9034565e93 100644 --- a/homeassistant/components/simplisafe/translations/lb.json +++ b/homeassistant/components/simplisafe/translations/lb.json @@ -5,15 +5,10 @@ "reauth_successful": "Erfollegr\u00e4ich re-authentifiz\u00e9iert." }, "error": { - "identifier_exists": "Konto ass scho registr\u00e9iert", "invalid_auth": "Ong\u00eblteg Authentifikatioun", - "still_awaiting_mfa": "Waart nach den MFA E-Mail Klick.", "unknown": "Onerwaarte Feeler" }, "step": { - "mfa": { - "title": "SimpliSafe Multi-Faktor Authentifikatioun" - }, "reauth_confirm": { "data": { "password": "Passwuert" @@ -23,11 +18,9 @@ }, "user": { "data": { - "code": "Code (benotzt am Home Assistant Benotzer Interface)", "password": "Passwuert", "username": "E-Mail" - }, - "title": "F\u00ebllt \u00e4r Informatiounen aus" + } } } }, diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index ff282832e4c..55e7c3c7832 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Dit SimpliSafe-account is al in gebruik.", "email_2fa_timed_out": "Timed out tijdens het wachten op email-gebaseerde twee-factor authenticatie.", - "reauth_successful": "Herauthenticatie was succesvol", - "wrong_account": "De opgegeven gebruikersgegevens komen niet overeen met deze SimpliSafe-account." + "reauth_successful": "Herauthenticatie was succesvol" }, "error": { - "2fa_timed_out": "Timed out tijdens het wachten op twee-factor authenticatie", - "identifier_exists": "Account bestaat al", "invalid_auth": "Ongeldige authenticatie", - "still_awaiting_mfa": "Wacht nog steeds op MFA-e-mailklik", "unknown": "Onverwachte fout" }, "progress": { "email_2fa": "Controleer uw e-mail voor een verificatielink van Simplisafe." }, "step": { - "mfa": { - "title": "SimpliSafe Multi-Factor Authenticatie" - }, "reauth_confirm": { "data": { "password": "Wachtwoord" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Autorisatie Code", - "code": "Code (gebruikt in Home Assistant)", "password": "Wachtwoord", "username": "Gebruikersnaam" }, - "description": "Voer uw gebruikersnaam en wachtwoord in.", - "title": "Vul uw gegevens in" + "description": "Voer uw gebruikersnaam en wachtwoord in." } } }, diff --git a/homeassistant/components/simplisafe/translations/no.json b/homeassistant/components/simplisafe/translations/no.json index 06a7260fd0e..7e6875a2590 100644 --- a/homeassistant/components/simplisafe/translations/no.json +++ b/homeassistant/components/simplisafe/translations/no.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Denne SimpliSafe-kontoen er allerede i bruk.", "email_2fa_timed_out": "Tidsavbrudd mens du ventet p\u00e5 e-postbasert tofaktorautentisering.", - "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket", - "wrong_account": "Brukerlegitimasjonen som er oppgitt, samsvarer ikke med denne SimpliSafe -kontoen." + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" }, "error": { - "2fa_timed_out": "Tidsavbrutt mens du ventet p\u00e5 tofaktorautentisering", - "identifier_exists": "Konto er allerede registrert", "invalid_auth": "Ugyldig godkjenning", - "still_awaiting_mfa": "Forventer fortsatt MFA-e-postklikk", "unknown": "Uventet feil" }, "progress": { "email_2fa": "Sjekk e-posten din for en bekreftelseslenke fra Simplisafe." }, "step": { - "mfa": { - "title": "SimpliSafe flertrinnsbekreftelse" - }, "reauth_confirm": { "data": { "password": "Passord" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Autorisasjonskode", - "code": "Kode (brukt i Home Assistant brukergrensesnittet)", "password": "Passord", "username": "Brukernavn" }, - "description": "Skriv inn brukernavn og passord.", - "title": "Fyll ut informasjonen din." + "description": "Skriv inn brukernavn og passord." } } }, diff --git a/homeassistant/components/simplisafe/translations/pl.json b/homeassistant/components/simplisafe/translations/pl.json index ec574217c58..22c4739b7dd 100644 --- a/homeassistant/components/simplisafe/translations/pl.json +++ b/homeassistant/components/simplisafe/translations/pl.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "To konto SimpliSafe jest ju\u017c w u\u017cyciu", "email_2fa_timed_out": "Przekroczono limit czasu oczekiwania na e-mailowe uwierzytelnianie dwusk\u0142adnikowe.", - "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119", - "wrong_account": "Podane dane uwierzytelniaj\u0105ce u\u017cytkownika nie pasuj\u0105 do tego konta SimpliSafe." + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" }, "error": { - "2fa_timed_out": "Przekroczono limit czasu oczekiwania na uwierzytelnianie dwusk\u0142adnikowe", - "identifier_exists": "Konto jest ju\u017c zarejestrowane", "invalid_auth": "Niepoprawne uwierzytelnienie", - "still_awaiting_mfa": "Wci\u0105\u017c nie potwierdzono linka w e-mailu od SimpliSafe", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "progress": { "email_2fa": "Sprawd\u017a e-mail z linkiem weryfikacyjnym od Simplisafe." }, "step": { - "mfa": { - "title": "Uwierzytelnianie wielosk\u0142adnikowe SimpliSafe" - }, "reauth_confirm": { "data": { "password": "Has\u0142o" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Kod autoryzacji", - "code": "Kod (u\u017cywany w interfejsie Home Assistant)", "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" }, - "description": "Wprowad\u017a swoj\u0105 nazw\u0119 u\u017cytkownika i has\u0142o.", - "title": "Wprowad\u017a dane" + "description": "Wprowad\u017a swoj\u0105 nazw\u0119 u\u017cytkownika i has\u0142o." } } }, diff --git a/homeassistant/components/simplisafe/translations/pt-BR.json b/homeassistant/components/simplisafe/translations/pt-BR.json index 3cc1703e11c..ccfb13b6cc1 100644 --- a/homeassistant/components/simplisafe/translations/pt-BR.json +++ b/homeassistant/components/simplisafe/translations/pt-BR.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "A conta j\u00e1 foi configurada", "email_2fa_timed_out": "Expirou enquanto aguardava a autentica\u00e7\u00e3o de dois fatores enviada por e-mail.", - "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida", - "wrong_account": "As credenciais de usu\u00e1rio fornecidas n\u00e3o correspondem a esta conta SimpliSafe." + "reauth_successful": "A reautentica\u00e7\u00e3o foi bem-sucedida" }, "error": { - "2fa_timed_out": "Expirou enquanto aguardava a autentica\u00e7\u00e3o de dois fatores", - "identifier_exists": "Conta j\u00e1 cadastrada", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "still_awaiting_mfa": "Ainda aguardando clique no e-mail da MFA", "unknown": "Erro inesperado" }, "progress": { "email_2fa": "Verifique seu e-mail para obter um link de verifica\u00e7\u00e3o do Simplisafe." }, "step": { - "mfa": { - "title": "Autentica\u00e7\u00e3o SimpliSafe multifator" - }, "reauth_confirm": { "data": { "password": "Senha" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "C\u00f3digo de autoriza\u00e7\u00e3o", - "code": "C\u00f3digo (usado na IU do Home Assistant)", "password": "Senha", "username": "Usu\u00e1rio" }, - "description": "Insira seu nome de usu\u00e1rio e senha.", - "title": "Preencha suas informa\u00e7\u00f5es." + "description": "Insira seu nome de usu\u00e1rio e senha." } } }, diff --git a/homeassistant/components/simplisafe/translations/pt.json b/homeassistant/components/simplisafe/translations/pt.json index 9b5df6cf934..ba84255bc9d 100644 --- a/homeassistant/components/simplisafe/translations/pt.json +++ b/homeassistant/components/simplisafe/translations/pt.json @@ -4,7 +4,6 @@ "reauth_successful": "Reautentica\u00e7\u00e3o bem sucedida" }, "error": { - "identifier_exists": "Conta j\u00e1 registada", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "unknown": "Erro inesperado" }, @@ -19,8 +18,7 @@ "data": { "password": "Palavra-passe", "username": "Email" - }, - "title": "Preencha as suas informa\u00e7\u00f5es" + } } } } diff --git a/homeassistant/components/simplisafe/translations/ro.json b/homeassistant/components/simplisafe/translations/ro.json index 7531bef0a8e..efbbe49f38a 100644 --- a/homeassistant/components/simplisafe/translations/ro.json +++ b/homeassistant/components/simplisafe/translations/ro.json @@ -1,15 +1,11 @@ { "config": { - "error": { - "identifier_exists": "Contul este deja \u00eenregistrat" - }, "step": { "user": { "data": { "password": "Parola", "username": "Adresa de email" - }, - "title": "Completa\u021bi informa\u021biile dvs." + } } } } diff --git a/homeassistant/components/simplisafe/translations/ru.json b/homeassistant/components/simplisafe/translations/ru.json index 0d2a36085e4..198d5225983 100644 --- a/homeassistant/components/simplisafe/translations/ru.json +++ b/homeassistant/components/simplisafe/translations/ru.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", "email_2fa_timed_out": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e.", - "wrong_account": "\u0423\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u044d\u0442\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 SimpliSafe." + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." }, "error": { - "2fa_timed_out": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "identifier_exists": "\u0423\u0447\u0435\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "still_awaiting_mfa": "\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u0435.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, "progress": { "email_2fa": "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0441\u0432\u043e\u044e \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443 \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043e\u0442 Simplisafe." }, "step": { - "mfa": { - "title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f SimpliSafe" - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438", - "code": "\u041a\u043e\u0434 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 Home Assistant)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.", - "title": "SimpliSafe" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c." } } }, diff --git a/homeassistant/components/simplisafe/translations/sl.json b/homeassistant/components/simplisafe/translations/sl.json index 6f326bc5b68..d98ad9c20c7 100644 --- a/homeassistant/components/simplisafe/translations/sl.json +++ b/homeassistant/components/simplisafe/translations/sl.json @@ -1,20 +1,14 @@ { "config": { "abort": { - "already_configured": "Ta ra\u010dun SimpliSafe je \u017ee v uporabi.", - "wrong_account": "Navedene uporabni\u0161ke poverilnice se ne ujemajo s tem ra\u010dunom SimpliSafe." - }, - "error": { - "identifier_exists": "Ra\u010dun je \u017ee registriran" + "already_configured": "Ta ra\u010dun SimpliSafe je \u017ee v uporabi." }, "step": { "user": { "data": { - "code": "Koda (uporablja se v uporabni\u0161kem vmesniku Home Assistant)", "password": "Geslo", "username": "E-po\u0161tni naslov" - }, - "title": "Izpolnite svoje podatke" + } } } }, diff --git a/homeassistant/components/simplisafe/translations/sv.json b/homeassistant/components/simplisafe/translations/sv.json index 760d092e113..2b2d675f2b5 100644 --- a/homeassistant/components/simplisafe/translations/sv.json +++ b/homeassistant/components/simplisafe/translations/sv.json @@ -3,10 +3,6 @@ "abort": { "already_configured": "Det h\u00e4r SimpliSafe-kontot har redan konfigurerats." }, - "error": { - "2fa_timed_out": "Tidsgr\u00e4nsen tog slut i v\u00e4ntan p\u00e5 tv\u00e5faktorsautentisering", - "identifier_exists": "Kontot \u00e4r redan registrerat" - }, "progress": { "email_2fa": "Kontrollera din e-post f\u00f6r en verifieringsl\u00e4nk fr\u00e5n Simplisafe." }, @@ -21,8 +17,7 @@ "data": { "password": "L\u00f6senord", "username": "E-postadress" - }, - "title": "Fyll i din information" + } } } } diff --git a/homeassistant/components/simplisafe/translations/tr.json b/homeassistant/components/simplisafe/translations/tr.json index 156b3575756..74c260e2d3d 100644 --- a/homeassistant/components/simplisafe/translations/tr.json +++ b/homeassistant/components/simplisafe/translations/tr.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "Bu SimpliSafe hesab\u0131 zaten kullan\u0131mda.", "email_2fa_timed_out": "E-posta tabanl\u0131 iki fakt\u00f6rl\u00fc kimlik do\u011frulama i\u00e7in beklerken zaman a\u015f\u0131m\u0131na u\u011frad\u0131.", - "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu", - "wrong_account": "Sa\u011flanan kullan\u0131c\u0131 kimlik bilgileri bu SimpliSafe hesab\u0131yla e\u015fle\u015fmiyor." + "reauth_successful": "Yeniden kimlik do\u011frulama ba\u015far\u0131l\u0131 oldu" }, "error": { - "2fa_timed_out": "\u0130ki fakt\u00f6rl\u00fc kimlik do\u011frulama i\u00e7in beklerken zaman a\u015f\u0131m\u0131na u\u011frad\u0131", - "identifier_exists": "Hesap zaten kay\u0131tl\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "still_awaiting_mfa": "Hala MFA e-posta t\u0131klamas\u0131 bekleniyor", "unknown": "Beklenmeyen hata" }, "progress": { "email_2fa": "Simplisafe'den bir do\u011frulama ba\u011flant\u0131s\u0131 i\u00e7in e-postan\u0131z\u0131 kontrol edin." }, "step": { - "mfa": { - "title": "SimpliSafe \u00c7ok Fakt\u00f6rl\u00fc Kimlik Do\u011frulama" - }, "reauth_confirm": { "data": { "password": "Parola" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "Yetkilendirme Kodu", - "code": "Kod (Home Assistant kullan\u0131c\u0131 aray\u00fcz\u00fcnde kullan\u0131l\u0131r)", "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" }, - "description": "Kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 ve \u015fifrenizi girin.", - "title": "Bilgilerinizi doldurun." + "description": "Kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 ve \u015fifrenizi girin." } } }, diff --git a/homeassistant/components/simplisafe/translations/uk.json b/homeassistant/components/simplisafe/translations/uk.json index 8ca29743c5b..76e0fb397cb 100644 --- a/homeassistant/components/simplisafe/translations/uk.json +++ b/homeassistant/components/simplisafe/translations/uk.json @@ -5,15 +5,10 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430 \u0430\u0432\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f \u043f\u0440\u043e\u0439\u0448\u043b\u0430 \u0443\u0441\u043f\u0456\u0448\u043d\u043e" }, "error": { - "identifier_exists": "\u041e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441 \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u043e\u0432\u0430\u043d\u043e.", "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", - "still_awaiting_mfa": "\u041e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0456\u0434\u0442\u0432\u0435\u0440\u0434\u0436\u0435\u043d\u043d\u044f, \u0432\u0456\u0434\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e\u0433\u043e \u043f\u043e \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0456\u0439 \u043f\u043e\u0448\u0442\u0456.", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, "step": { - "mfa": { - "title": "\u0414\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f SimpliSafe" - }, "reauth_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c" @@ -23,11 +18,9 @@ }, "user": { "data": { - "code": "\u041a\u043e\u0434 (\u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u0432 \u0456\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0456 Home Assistant)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438" - }, - "title": "\u0417\u0430\u043f\u043e\u0432\u043d\u0456\u0442\u044c \u0432\u0430\u0448\u0443 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u044e" + } } } }, diff --git a/homeassistant/components/simplisafe/translations/zh-Hans.json b/homeassistant/components/simplisafe/translations/zh-Hans.json index 134eee33bfe..d28951425ec 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hans.json +++ b/homeassistant/components/simplisafe/translations/zh-Hans.json @@ -1,17 +1,14 @@ { "config": { "error": { - "identifier_exists": "\u8d26\u6237\u5df2\u6ce8\u518c", "invalid_auth": "\u65e0\u6548\u7684\u8eab\u4efd\u9a8c\u8bc1" }, "step": { "user": { "data": { - "auth_code": "\u6388\u6743\u7801", "password": "\u5bc6\u7801", "username": "\u7535\u5b50\u90ae\u4ef6\u5730\u5740" - }, - "title": "\u586b\u5199\u60a8\u7684\u4fe1\u606f" + } } } } diff --git a/homeassistant/components/simplisafe/translations/zh-Hant.json b/homeassistant/components/simplisafe/translations/zh-Hant.json index e3cc230962c..ce763da538e 100644 --- a/homeassistant/components/simplisafe/translations/zh-Hant.json +++ b/homeassistant/components/simplisafe/translations/zh-Hant.json @@ -3,23 +3,16 @@ "abort": { "already_configured": "\u6b64 SimpliSafe \u5e33\u865f\u5df2\u88ab\u4f7f\u7528\u3002", "email_2fa_timed_out": "\u7b49\u5f85\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u90f5\u4ef6\u903e\u6642\u3002", - "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f", - "wrong_account": "\u6240\u4ee5\u63d0\u4f9b\u7684\u6191\u8b49\u8207 Simplisafe \u5e33\u865f\u4e0d\u7b26\u3002" + "reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f" }, "error": { - "2fa_timed_out": "\u7b49\u5f85\u5169\u6b65\u9a5f\u9a57\u8b49\u78bc\u903e\u6642", - "identifier_exists": "\u5e33\u865f\u5df2\u8a3b\u518a", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "still_awaiting_mfa": "\u4ecd\u5728\u7b49\u5019\u9ede\u64ca\u591a\u6b65\u9a5f\u8a8d\u8b49\u90f5\u4ef6", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, "progress": { "email_2fa": "\u8f38\u5165\u90f5\u4ef6\u6240\u6536\u5230 \u7684Simplisafe \u9a57\u8b49\u9023\u7d50\u3002" }, "step": { - "mfa": { - "title": "SimpliSafe \u591a\u6b65\u9a5f\u9a57\u8b49" - }, "reauth_confirm": { "data": { "password": "\u5bc6\u78bc" @@ -35,13 +28,10 @@ }, "user": { "data": { - "auth_code": "\u8a8d\u8b49\u78bc", - "code": "\u9a57\u8b49\u78bc\uff08\u4f7f\u7528\u65bc Home Assistant UI\uff09", "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, - "description": "\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31\u8207\u5bc6\u78bc\u3002", - "title": "\u586b\u5beb\u8cc7\u8a0a\u3002" + "description": "\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31\u8207\u5bc6\u78bc\u3002" } } }, diff --git a/homeassistant/components/siren/translations/cs.json b/homeassistant/components/siren/translations/cs.json new file mode 100644 index 00000000000..50303faf105 --- /dev/null +++ b/homeassistant/components/siren/translations/cs.json @@ -0,0 +1,3 @@ +{ + "title": "Sir\u00e9na" +} \ No newline at end of file diff --git a/homeassistant/components/siren/translations/no.json b/homeassistant/components/siren/translations/no.json new file mode 100644 index 00000000000..bcea16c56f1 --- /dev/null +++ b/homeassistant/components/siren/translations/no.json @@ -0,0 +1,3 @@ +{ + "title": "Sirene" +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/cs.json b/homeassistant/components/slack/translations/cs.json new file mode 100644 index 00000000000..a4b5fb71cbe --- /dev/null +++ b/homeassistant/components/slack/translations/cs.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Slu\u017eba je ji\u017e nastavena" + }, + "error": { + "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + }, + "step": { + "user": { + "data": { + "api_key": "Kl\u00ed\u010d API", + "icon": "Ikona", + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/he.json b/homeassistant/components/slack/translations/he.json similarity index 52% rename from homeassistant/components/climacell/translations/he.json rename to homeassistant/components/slack/translations/he.json index b663a5e0a0f..b0201cf2445 100644 --- a/homeassistant/components/climacell/translations/he.json +++ b/homeassistant/components/slack/translations/he.json @@ -1,18 +1,19 @@ { "config": { + "abort": { + "already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8" + }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "invalid_api_key": "\u05de\u05e4\u05ea\u05d7 API \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, "step": { "user": { "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", - "api_version": "\u05d2\u05e8\u05e1\u05ea API", - "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", - "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", - "name": "\u05e9\u05dd" + "icon": "\u05e1\u05de\u05dc\u05d9\u05dc", + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" } } } diff --git a/homeassistant/components/slack/translations/id.json b/homeassistant/components/slack/translations/id.json index 3180294b97e..3897704b540 100644 --- a/homeassistant/components/slack/translations/id.json +++ b/homeassistant/components/slack/translations/id.json @@ -12,11 +12,15 @@ "user": { "data": { "api_key": "Kunci API", + "default_channel": "Saluran Baku", "icon": "Ikon", "username": "Nama Pengguna" }, "data_description": { - "api_key": "Token API Slack yang digunakan untuk mengirim pesan Slack." + "api_key": "Token API Slack yang digunakan untuk mengirim pesan Slack.", + "default_channel": "Saluran tujuan posting jika tidak ada saluran yang ditentukan saat mengirim pesan.", + "icon": "Gunakan salah satu emoji Slack sebagai Ikon untuk nama pengguna yang disediakan.", + "username": "Home Assistant akan memposting ke Slack menggunakan nama pengguna yang ditentukan." }, "description": "Lihat dokumentasi tentang mendapatkan kunci API Slack Anda." } diff --git a/homeassistant/components/smhi/translations/bg.json b/homeassistant/components/smhi/translations/bg.json index bcd30370ad4..d1dfdde7a32 100644 --- a/homeassistant/components/smhi/translations/bg.json +++ b/homeassistant/components/smhi/translations/bg.json @@ -4,15 +4,13 @@ "already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u044a\u0442 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d" }, "error": { - "name_exists": "\u0418\u043c\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430", "wrong_location": "\u041f\u043e\u0434\u0434\u044a\u0440\u0436\u0430\u0442 \u0441\u0435 \u0441\u0430\u043c\u043e \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0428\u0432\u0435\u0446\u0438\u044f" }, "step": { "user": { "data": { "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", - "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", - "name": "\u0418\u043c\u0435" + "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430" }, "title": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0428\u0432\u0435\u0446\u0438\u044f" } diff --git a/homeassistant/components/smhi/translations/ca.json b/homeassistant/components/smhi/translations/ca.json index 4755e35f2ba..c3f53542274 100644 --- a/homeassistant/components/smhi/translations/ca.json +++ b/homeassistant/components/smhi/translations/ca.json @@ -4,15 +4,13 @@ "already_configured": "El compte ja est\u00e0 configurat" }, "error": { - "name_exists": "El nom ja existeix", "wrong_location": "La ubicaci\u00f3 ha d'estar a Su\u00e8cia" }, "step": { "user": { "data": { "latitude": "Latitud", - "longitude": "Longitud", - "name": "Nom" + "longitude": "Longitud" }, "title": "Ubicaci\u00f3 a Su\u00e8cia" } diff --git a/homeassistant/components/smhi/translations/cs.json b/homeassistant/components/smhi/translations/cs.json index 5721edc084b..6d65b6411c9 100644 --- a/homeassistant/components/smhi/translations/cs.json +++ b/homeassistant/components/smhi/translations/cs.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "N\u00e1zev ji\u017e existuje", "wrong_location": "Lokalita pouze pro \u0160v\u00e9dsko" }, "step": { "user": { "data": { "latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka", - "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka", - "name": "Jm\u00e9no" + "longitude": "Zem\u011bpisn\u00e1 d\u00e9lka" }, "title": "Lokalita ve \u0160v\u00e9dsku" } diff --git a/homeassistant/components/smhi/translations/da.json b/homeassistant/components/smhi/translations/da.json index 979f4b0f7b6..9b07283456b 100644 --- a/homeassistant/components/smhi/translations/da.json +++ b/homeassistant/components/smhi/translations/da.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Navnet findes allerede", "wrong_location": "Lokalitet kun i Sverige" }, "step": { "user": { "data": { "latitude": "Breddegrad", - "longitude": "L\u00e6ngdegrad", - "name": "Navn" + "longitude": "L\u00e6ngdegrad" }, "title": "Lokalitet i Sverige" } diff --git a/homeassistant/components/smhi/translations/de.json b/homeassistant/components/smhi/translations/de.json index 716f9693e96..7434d9d5800 100644 --- a/homeassistant/components/smhi/translations/de.json +++ b/homeassistant/components/smhi/translations/de.json @@ -4,15 +4,13 @@ "already_configured": "Konto wurde bereits konfiguriert" }, "error": { - "name_exists": "Name existiert bereits", "wrong_location": "Standort nur in Schweden" }, "step": { "user": { "data": { "latitude": "Breitengrad", - "longitude": "L\u00e4ngengrad", - "name": "Name" + "longitude": "L\u00e4ngengrad" }, "title": "Standort in Schweden" } diff --git a/homeassistant/components/smhi/translations/el.json b/homeassistant/components/smhi/translations/el.json index c852bd3a08e..736dc0c026a 100644 --- a/homeassistant/components/smhi/translations/el.json +++ b/homeassistant/components/smhi/translations/el.json @@ -4,15 +4,13 @@ "already_configured": "\u039f \u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2" }, "error": { - "name_exists": "\u03a4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7", "wrong_location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u039c\u03cc\u03bd\u03bf \u03a3\u03bf\u03c5\u03b7\u03b4\u03af\u03b1" }, "step": { "user": { "data": { "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2", - "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2" }, "title": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 \u03c3\u03c4\u03b7 \u03a3\u03bf\u03c5\u03b7\u03b4\u03af\u03b1" } diff --git a/homeassistant/components/smhi/translations/en.json b/homeassistant/components/smhi/translations/en.json index 5400122be8a..5c8f4f6ae24 100644 --- a/homeassistant/components/smhi/translations/en.json +++ b/homeassistant/components/smhi/translations/en.json @@ -4,15 +4,13 @@ "already_configured": "Account is already configured" }, "error": { - "name_exists": "Name already exists", "wrong_location": "Location Sweden only" }, "step": { "user": { "data": { "latitude": "Latitude", - "longitude": "Longitude", - "name": "Name" + "longitude": "Longitude" }, "title": "Location in Sweden" } diff --git a/homeassistant/components/smhi/translations/es-419.json b/homeassistant/components/smhi/translations/es-419.json index 3db65a34ad9..c73d574c5be 100644 --- a/homeassistant/components/smhi/translations/es-419.json +++ b/homeassistant/components/smhi/translations/es-419.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "El nombre ya existe", "wrong_location": "Ubicaci\u00f3n Suecia solamente" }, "step": { "user": { "data": { "latitude": "Latitud", - "longitude": "Longitud", - "name": "Nombre" + "longitude": "Longitud" }, "title": "Ubicaci\u00f3n en Suecia" } diff --git a/homeassistant/components/smhi/translations/es.json b/homeassistant/components/smhi/translations/es.json index ab34438b12d..430f04d59fc 100644 --- a/homeassistant/components/smhi/translations/es.json +++ b/homeassistant/components/smhi/translations/es.json @@ -4,15 +4,13 @@ "already_configured": "La cuenta ya est\u00e1 configurada" }, "error": { - "name_exists": "Nombre ya existe", "wrong_location": "Ubicaci\u00f3n Suecia solamente" }, "step": { "user": { "data": { "latitude": "Latitud", - "longitude": "Longitud", - "name": "Nombre" + "longitude": "Longitud" }, "title": "Ubicaci\u00f3n en Suecia" } diff --git a/homeassistant/components/smhi/translations/et.json b/homeassistant/components/smhi/translations/et.json index a32f38e28ca..d07ec7d3391 100644 --- a/homeassistant/components/smhi/translations/et.json +++ b/homeassistant/components/smhi/translations/et.json @@ -4,15 +4,13 @@ "already_configured": "Konto on juba h\u00e4\u00e4lestatud" }, "error": { - "name_exists": "Nimi on juba olemas", "wrong_location": "Asukoht saab olla ainult Rootsis" }, "step": { "user": { "data": { "latitude": "Laiuskraad", - "longitude": "Pikkuskraad", - "name": "Nimi" + "longitude": "Pikkuskraad" }, "title": "Asukoht Rootsis" } diff --git a/homeassistant/components/smhi/translations/fi.json b/homeassistant/components/smhi/translations/fi.json index 2a05c47dcbc..cc5377e3654 100644 --- a/homeassistant/components/smhi/translations/fi.json +++ b/homeassistant/components/smhi/translations/fi.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Nimi on jo olemassa", "wrong_location": "Sijainti vain Ruotsi" }, "step": { "user": { "data": { "latitude": "Leveysaste", - "longitude": "Pituusaste", - "name": "Nimi" + "longitude": "Pituusaste" }, "title": "Sijainti Ruotsissa" } diff --git a/homeassistant/components/smhi/translations/fr.json b/homeassistant/components/smhi/translations/fr.json index 6bf27915f97..84d5c6cfa7c 100644 --- a/homeassistant/components/smhi/translations/fr.json +++ b/homeassistant/components/smhi/translations/fr.json @@ -4,15 +4,13 @@ "already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9" }, "error": { - "name_exists": "Ce nom est d\u00e9j\u00e0 utilis\u00e9", "wrong_location": "En Su\u00e8de uniquement" }, "step": { "user": { "data": { "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nom" + "longitude": "Longitude" }, "title": "Localisation en Su\u00e8de" } diff --git a/homeassistant/components/smhi/translations/he.json b/homeassistant/components/smhi/translations/he.json index cb8ff44626c..622d75d6c6c 100644 --- a/homeassistant/components/smhi/translations/he.json +++ b/homeassistant/components/smhi/translations/he.json @@ -7,8 +7,7 @@ "user": { "data": { "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", - "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", - "name": "\u05e9\u05dd" + "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da" } } } diff --git a/homeassistant/components/smhi/translations/hu.json b/homeassistant/components/smhi/translations/hu.json index e83ea03a193..22307044da2 100644 --- a/homeassistant/components/smhi/translations/hu.json +++ b/homeassistant/components/smhi/translations/hu.json @@ -4,15 +4,13 @@ "already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van" }, "error": { - "name_exists": "A n\u00e9v m\u00e1r l\u00e9tezik", "wrong_location": "Csak sv\u00e9dorsz\u00e1gi helysz\u00edn megengedett" }, "step": { "user": { "data": { "latitude": "Sz\u00e9less\u00e9g", - "longitude": "Hossz\u00fas\u00e1g", - "name": "Elnevez\u00e9s" + "longitude": "Hossz\u00fas\u00e1g" }, "title": "Helysz\u00edn Sv\u00e9dorsz\u00e1gban" } diff --git a/homeassistant/components/smhi/translations/id.json b/homeassistant/components/smhi/translations/id.json index 842580dac79..b1b9450dc05 100644 --- a/homeassistant/components/smhi/translations/id.json +++ b/homeassistant/components/smhi/translations/id.json @@ -4,15 +4,13 @@ "already_configured": "Akun sudah dikonfigurasi" }, "error": { - "name_exists": "Nama sudah ada", "wrong_location": "Hanya untuk lokasi di Swedia" }, "step": { "user": { "data": { "latitude": "Lintang", - "longitude": "Bujur", - "name": "Nama" + "longitude": "Bujur" }, "title": "Lokasi di Swedia" } diff --git a/homeassistant/components/smhi/translations/it.json b/homeassistant/components/smhi/translations/it.json index 3df53d13ec1..830107967dd 100644 --- a/homeassistant/components/smhi/translations/it.json +++ b/homeassistant/components/smhi/translations/it.json @@ -4,15 +4,13 @@ "already_configured": "L'account \u00e8 gi\u00e0 configurato" }, "error": { - "name_exists": "Il nome \u00e8 gi\u00e0 esistente", "wrong_location": "Localit\u00e0 solamente della Svezia" }, "step": { "user": { "data": { "latitude": "Latitudine", - "longitude": "Logitudine", - "name": "Nome" + "longitude": "Logitudine" }, "title": "Localit\u00e0 in Svezia" } diff --git a/homeassistant/components/smhi/translations/ja.json b/homeassistant/components/smhi/translations/ja.json index 38e4ccad389..390732d7db4 100644 --- a/homeassistant/components/smhi/translations/ja.json +++ b/homeassistant/components/smhi/translations/ja.json @@ -4,15 +4,13 @@ "already_configured": "\u30a2\u30ab\u30a6\u30f3\u30c8\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { - "name_exists": "\u540d\u524d\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059", "wrong_location": "\u6240\u5728\u5730 \u30b9\u30a6\u30a7\u30fc\u30c7\u30f3\u306e\u307f" }, "step": { "user": { "data": { "latitude": "\u7def\u5ea6", - "longitude": "\u7d4c\u5ea6", - "name": "\u540d\u524d" + "longitude": "\u7d4c\u5ea6" }, "title": "\u30b9\u30a6\u30a7\u30fc\u30c7\u30f3\u3067\u306e\u4f4d\u7f6e" } diff --git a/homeassistant/components/smhi/translations/ko.json b/homeassistant/components/smhi/translations/ko.json index dac6c9018b2..6f1cbeda859 100644 --- a/homeassistant/components/smhi/translations/ko.json +++ b/homeassistant/components/smhi/translations/ko.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "\uc774\ub984\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4", "wrong_location": "\uc2a4\uc6e8\ub374 \uc9c0\uc5ed \uc804\uc6a9\uc785\ub2c8\ub2e4" }, "step": { "user": { "data": { "latitude": "\uc704\ub3c4", - "longitude": "\uacbd\ub3c4", - "name": "\uc774\ub984" + "longitude": "\uacbd\ub3c4" }, "title": "\uc2a4\uc6e8\ub374 \uc9c0\uc5ed \uc704\uce58" } diff --git a/homeassistant/components/smhi/translations/lb.json b/homeassistant/components/smhi/translations/lb.json index 9444569c4a1..9abb503923d 100644 --- a/homeassistant/components/smhi/translations/lb.json +++ b/homeassistant/components/smhi/translations/lb.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Numm g\u00ebtt et schonn", "wrong_location": "N\u00ebmmen Uertschaften an Schweden" }, "step": { "user": { "data": { "latitude": "Breedegrad", - "longitude": "L\u00e4ngegrad", - "name": "Numm" + "longitude": "L\u00e4ngegrad" }, "title": "Uertschaft an Schweden" } diff --git a/homeassistant/components/smhi/translations/nl.json b/homeassistant/components/smhi/translations/nl.json index 597e106ab7e..abf19b47ef9 100644 --- a/homeassistant/components/smhi/translations/nl.json +++ b/homeassistant/components/smhi/translations/nl.json @@ -4,15 +4,13 @@ "already_configured": "Account is al geconfigureerd" }, "error": { - "name_exists": "Naam bestaat al", "wrong_location": "Locatie alleen Zweden" }, "step": { "user": { "data": { "latitude": "Breedtegraad", - "longitude": "Lengtegraad", - "name": "Naam" + "longitude": "Lengtegraad" }, "title": "Locatie in Zweden" } diff --git a/homeassistant/components/smhi/translations/no.json b/homeassistant/components/smhi/translations/no.json index 914baaca9ce..b2ec9e8732a 100644 --- a/homeassistant/components/smhi/translations/no.json +++ b/homeassistant/components/smhi/translations/no.json @@ -4,15 +4,13 @@ "already_configured": "Kontoen er allerede konfigurert" }, "error": { - "name_exists": "Navnet eksisterer allerede", "wrong_location": "Bare plassering i Sverige" }, "step": { "user": { "data": { "latitude": "Breddegrad", - "longitude": "Lengdegrad", - "name": "Navn" + "longitude": "Lengdegrad" }, "title": "Plassering i Sverige" } diff --git a/homeassistant/components/smhi/translations/pl.json b/homeassistant/components/smhi/translations/pl.json index 18e9156a936..65fc02e0c7c 100644 --- a/homeassistant/components/smhi/translations/pl.json +++ b/homeassistant/components/smhi/translations/pl.json @@ -4,15 +4,13 @@ "already_configured": "Konto jest ju\u017c skonfigurowane" }, "error": { - "name_exists": "Nazwa ju\u017c istnieje", "wrong_location": "Lokalizacja w Szwecji" }, "step": { "user": { "data": { "latitude": "Szeroko\u015b\u0107 geograficzna", - "longitude": "D\u0142ugo\u015b\u0107 geograficzna", - "name": "Nazwa" + "longitude": "D\u0142ugo\u015b\u0107 geograficzna" }, "title": "Lokalizacja w Szwecji" } diff --git a/homeassistant/components/smhi/translations/pt-BR.json b/homeassistant/components/smhi/translations/pt-BR.json index 235008c7c31..2f335a58aa6 100644 --- a/homeassistant/components/smhi/translations/pt-BR.json +++ b/homeassistant/components/smhi/translations/pt-BR.json @@ -4,15 +4,13 @@ "already_configured": "A conta j\u00e1 foi configurada" }, "error": { - "name_exists": "O nome j\u00e1 existe", "wrong_location": "Localiza\u00e7\u00e3o apenas na Su\u00e9cia" }, "step": { "user": { "data": { "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nome" + "longitude": "Longitude" }, "title": "Localiza\u00e7\u00e3o na Su\u00e9cia" } diff --git a/homeassistant/components/smhi/translations/pt.json b/homeassistant/components/smhi/translations/pt.json index c23a33b6e19..d5cd5e83a13 100644 --- a/homeassistant/components/smhi/translations/pt.json +++ b/homeassistant/components/smhi/translations/pt.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Nome j\u00e1 existe", "wrong_location": "Localiza\u00e7\u00e3o apenas na Su\u00e9cia" }, "step": { "user": { "data": { "latitude": "Latitude", - "longitude": "Longitude", - "name": "Nome" + "longitude": "Longitude" }, "title": "Localiza\u00e7\u00e3o na Su\u00e9cia" } diff --git a/homeassistant/components/smhi/translations/ro.json b/homeassistant/components/smhi/translations/ro.json index 0261289fd31..f1ad756f798 100644 --- a/homeassistant/components/smhi/translations/ro.json +++ b/homeassistant/components/smhi/translations/ro.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Numele exist\u0103 deja", "wrong_location": "Loca\u021bia numai \u00een Suedia" }, "step": { "user": { "data": { "latitude": "Latitudine", - "longitude": "Longitudine", - "name": "Nume" + "longitude": "Longitudine" }, "title": "Loca\u021bie \u00een Suedia" } diff --git a/homeassistant/components/smhi/translations/ru.json b/homeassistant/components/smhi/translations/ru.json index b771dfcf961..41e009b85d3 100644 --- a/homeassistant/components/smhi/translations/ru.json +++ b/homeassistant/components/smhi/translations/ru.json @@ -4,15 +4,13 @@ "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant." }, "error": { - "name_exists": "\u042d\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f.", "wrong_location": "\u0422\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0428\u0432\u0435\u0446\u0438\u0438." }, "step": { "user": { "data": { "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", - "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430" }, "title": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0428\u0432\u0435\u0446\u0438\u0438" } diff --git a/homeassistant/components/smhi/translations/sk.json b/homeassistant/components/smhi/translations/sk.json index 81532ef4801..e6945904d90 100644 --- a/homeassistant/components/smhi/translations/sk.json +++ b/homeassistant/components/smhi/translations/sk.json @@ -4,8 +4,7 @@ "user": { "data": { "latitude": "Zemepisn\u00e1 \u0161\u00edrka", - "longitude": "Zemepisn\u00e1 d\u013a\u017eka", - "name": "N\u00e1zov" + "longitude": "Zemepisn\u00e1 d\u013a\u017eka" } } } diff --git a/homeassistant/components/smhi/translations/sl.json b/homeassistant/components/smhi/translations/sl.json index 91fe7d8375f..07e249a2245 100644 --- a/homeassistant/components/smhi/translations/sl.json +++ b/homeassistant/components/smhi/translations/sl.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Ime \u017ee obstaja", "wrong_location": "Lokacija le na \u0160vedskem" }, "step": { "user": { "data": { "latitude": "Zemljepisna \u0161irina", - "longitude": "Zemljepisna dol\u017eina", - "name": "Ime" + "longitude": "Zemljepisna dol\u017eina" }, "title": "Lokacija na \u0160vedskem" } diff --git a/homeassistant/components/smhi/translations/sv.json b/homeassistant/components/smhi/translations/sv.json index b6afc479b70..0bb597e393b 100644 --- a/homeassistant/components/smhi/translations/sv.json +++ b/homeassistant/components/smhi/translations/sv.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "Namnet finns redan", "wrong_location": "Plats i Sverige endast" }, "step": { "user": { "data": { "latitude": "Latitud", - "longitude": "Longitud", - "name": "Namn" + "longitude": "Longitud" }, "title": "Plats i Sverige" } diff --git a/homeassistant/components/smhi/translations/th.json b/homeassistant/components/smhi/translations/th.json index 0c08363fca6..85494a9dc6b 100644 --- a/homeassistant/components/smhi/translations/th.json +++ b/homeassistant/components/smhi/translations/th.json @@ -4,8 +4,7 @@ "user": { "data": { "latitude": "\u0e25\u0e30\u0e15\u0e34\u0e08\u0e39\u0e14", - "longitude": "\u0e25\u0e2d\u0e07\u0e08\u0e34\u0e08\u0e39\u0e14", - "name": "\u0e0a\u0e37\u0e48\u0e2d" + "longitude": "\u0e25\u0e2d\u0e07\u0e08\u0e34\u0e08\u0e39\u0e14" } } } diff --git a/homeassistant/components/smhi/translations/tr.json b/homeassistant/components/smhi/translations/tr.json index 206573ae69b..613ac6e691e 100644 --- a/homeassistant/components/smhi/translations/tr.json +++ b/homeassistant/components/smhi/translations/tr.json @@ -4,15 +4,13 @@ "already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { - "name_exists": "Bu ad zaten var", "wrong_location": "Konum sadece \u0130sve\u00e7" }, "step": { "user": { "data": { "latitude": "Enlem", - "longitude": "Boylam", - "name": "Ad" + "longitude": "Boylam" }, "title": "\u0130sve\u00e7'teki konum" } diff --git a/homeassistant/components/smhi/translations/uk.json b/homeassistant/components/smhi/translations/uk.json index 24af32172ba..e7de5db5d1b 100644 --- a/homeassistant/components/smhi/translations/uk.json +++ b/homeassistant/components/smhi/translations/uk.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "\u0426\u044f \u043d\u0430\u0437\u0432\u0430 \u0432\u0436\u0435 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f.", "wrong_location": "\u0422\u0456\u043b\u044c\u043a\u0438 \u0434\u043b\u044f \u0428\u0432\u0435\u0446\u0456\u0457." }, "step": { "user": { "data": { "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", - "longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430", - "name": "\u041d\u0430\u0437\u0432\u0430" + "longitude": "\u0414\u043e\u0432\u0433\u043e\u0442\u0430" }, "title": "\u041c\u0456\u0441\u0446\u0435\u0437\u043d\u0430\u0445\u043e\u0434\u0436\u0435\u043d\u043d\u044f \u0432 \u0428\u0432\u0435\u0446\u0456\u0457" } diff --git a/homeassistant/components/smhi/translations/zh-Hans.json b/homeassistant/components/smhi/translations/zh-Hans.json index b2ddc3aa1d7..64421e462d3 100644 --- a/homeassistant/components/smhi/translations/zh-Hans.json +++ b/homeassistant/components/smhi/translations/zh-Hans.json @@ -1,15 +1,13 @@ { "config": { "error": { - "name_exists": "\u540d\u79f0\u5df2\u5b58\u5728", "wrong_location": "\u4ec5\u9650\u745e\u5178\u7684\u4f4d\u7f6e" }, "step": { "user": { "data": { "latitude": "\u7eac\u5ea6", - "longitude": "\u7ecf\u5ea6", - "name": "\u540d\u79f0" + "longitude": "\u7ecf\u5ea6" }, "title": "\u5728\u745e\u5178\u7684\u4f4d\u7f6e" } diff --git a/homeassistant/components/smhi/translations/zh-Hant.json b/homeassistant/components/smhi/translations/zh-Hant.json index f5d993ab6c9..8b68a30a4b2 100644 --- a/homeassistant/components/smhi/translations/zh-Hant.json +++ b/homeassistant/components/smhi/translations/zh-Hant.json @@ -4,15 +4,13 @@ "already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { - "name_exists": "\u8a72\u540d\u7a31\u5df2\u5b58\u5728", "wrong_location": "\u50c5\u9650\u745e\u5178\u5ea7\u6a19" }, "step": { "user": { "data": { "latitude": "\u7def\u5ea6", - "longitude": "\u7d93\u5ea6", - "name": "\u540d\u7a31" + "longitude": "\u7d93\u5ea6" }, "title": "\u745e\u5178\u5ea7\u6a19" } diff --git a/homeassistant/components/somfy_mylink/translations/ca.json b/homeassistant/components/somfy_mylink/translations/ca.json index a9181aa4605..d25ba195027 100644 --- a/homeassistant/components/somfy_mylink/translations/ca.json +++ b/homeassistant/components/somfy_mylink/translations/ca.json @@ -25,17 +25,8 @@ "cannot_connect": "Ha fallat la connexi\u00f3" }, "step": { - "entity_config": { - "data": { - "reverse": "La coberta est\u00e0 invertida" - }, - "description": "Opcions de configuraci\u00f3 de `{entity_id}`", - "title": "Configura l'entitat" - }, "init": { "data": { - "default_reverse": "Estat d'inversi\u00f3 predeterminat per a cobertes sense configurar", - "entity_id": "Configura una entitat espec\u00edfica.", "target_id": "Opcions de configuraci\u00f3 de la coberta." }, "title": "Configura opcions de MyLink" @@ -48,6 +39,5 @@ "title": "Configura coberta MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/de.json b/homeassistant/components/somfy_mylink/translations/de.json index 9fc1af6d92c..303337542f0 100644 --- a/homeassistant/components/somfy_mylink/translations/de.json +++ b/homeassistant/components/somfy_mylink/translations/de.json @@ -25,17 +25,8 @@ "cannot_connect": "Verbindung fehlgeschlagen" }, "step": { - "entity_config": { - "data": { - "reverse": "Jalousie ist invertiert" - }, - "description": "Optionen f\u00fcr `{entity_id}` konfigurieren", - "title": "Entit\u00e4t konfigurieren" - }, "init": { "data": { - "default_reverse": "Standardinvertierungsstatus f\u00fcr nicht konfigurierte Abdeckungen", - "entity_id": "Konfiguriere eine bestimmte Entit\u00e4t.", "target_id": "Konfigurieren der Optionen f\u00fcr eine Jalousie." }, "title": "MyLink-Optionen konfigurieren" @@ -48,6 +39,5 @@ "title": "MyLink-Cover konfigurieren" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/el.json b/homeassistant/components/somfy_mylink/translations/el.json index 59ffb563687..565f8459a3f 100644 --- a/homeassistant/components/somfy_mylink/translations/el.json +++ b/homeassistant/components/somfy_mylink/translations/el.json @@ -25,17 +25,8 @@ "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "step": { - "entity_config": { - "data": { - "reverse": "\u03a4\u03bf \u03ba\u03ac\u03bb\u03c5\u03bc\u03bc\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b1\u03bd\u03c4\u03b5\u03c3\u03c4\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf" - }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 `{entity_id}`", - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" - }, "init": { "data": { - "default_reverse": "\u03a0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b1\u03bd\u03c4\u03b9\u03c3\u03c4\u03c1\u03bf\u03c6\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03bc\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03bc\u03ad\u03bd\u03b1 \u03ba\u03b1\u03bb\u03cd\u03bc\u03bc\u03b1\u03c4\u03b1", - "entity_id": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03bc\u03b9\u03b1\u03c2 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7\u03c2 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.", "target_id": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03ad\u03bd\u03b1 \u03ba\u03ac\u03bb\u03c5\u03bc\u03bc\u03b1." }, "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd MyLink" @@ -48,6 +39,5 @@ "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 MyLink Cover" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/en.json b/homeassistant/components/somfy_mylink/translations/en.json index cb373008778..e646bf9abcf 100644 --- a/homeassistant/components/somfy_mylink/translations/en.json +++ b/homeassistant/components/somfy_mylink/translations/en.json @@ -25,17 +25,8 @@ "cannot_connect": "Failed to connect" }, "step": { - "entity_config": { - "data": { - "reverse": "Cover is reversed" - }, - "description": "Configure options for `{entity_id}`", - "title": "Configure Entity" - }, "init": { "data": { - "default_reverse": "Default reversal status for unconfigured covers", - "entity_id": "Configure a specific entity.", "target_id": "Configure options for a cover." }, "title": "Configure MyLink Options" @@ -48,6 +39,5 @@ "title": "Configure MyLink Cover" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/es.json b/homeassistant/components/somfy_mylink/translations/es.json index 40d82a4522a..8647f7bb8fd 100644 --- a/homeassistant/components/somfy_mylink/translations/es.json +++ b/homeassistant/components/somfy_mylink/translations/es.json @@ -25,17 +25,8 @@ "cannot_connect": "No se pudo conectar" }, "step": { - "entity_config": { - "data": { - "reverse": "La cubierta est\u00e1 invertida" - }, - "description": "Configurar opciones para `{entity_id}`", - "title": "Configurar entidad" - }, "init": { "data": { - "default_reverse": "Estado de inversi\u00f3n predeterminado para cubiertas no configuradas", - "entity_id": "Configurar una entidad espec\u00edfica.", "target_id": "Configurar opciones para una cubierta." }, "title": "Configurar opciones de MyLink" @@ -48,6 +39,5 @@ "title": "Configurar la cubierta MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/et.json b/homeassistant/components/somfy_mylink/translations/et.json index 17122eb449e..f3d2e4c4a9a 100644 --- a/homeassistant/components/somfy_mylink/translations/et.json +++ b/homeassistant/components/somfy_mylink/translations/et.json @@ -25,17 +25,8 @@ "cannot_connect": "\u00dchendamine nurjus" }, "step": { - "entity_config": { - "data": { - "reverse": "(Akna)kate t\u00f6\u00f6tab vastupidi" - }, - "description": "Olemi {entity_id} suvandite seadmine", - "title": "Seadista olem" - }, "init": { "data": { - "default_reverse": "Seadistamata (akna)katete vaikep\u00f6\u00f6rduse olek", - "entity_id": "Seadista konkreetne olem.", "target_id": "Seadista (akna)katte suvandid" }, "title": "Seadista MyLinki suvandid" @@ -48,6 +39,5 @@ "title": "Seadista MyLink Cover" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/fr.json b/homeassistant/components/somfy_mylink/translations/fr.json index cc257e16451..8dc822976fe 100644 --- a/homeassistant/components/somfy_mylink/translations/fr.json +++ b/homeassistant/components/somfy_mylink/translations/fr.json @@ -25,17 +25,8 @@ "cannot_connect": "\u00c9chec de connexion" }, "step": { - "entity_config": { - "data": { - "reverse": "La couverture est invers\u00e9e" - }, - "description": "Configurer les options pour \u00ab {entity_id} \u00bb", - "title": "Configurez une entit\u00e9 sp\u00e9cifique" - }, "init": { "data": { - "default_reverse": "Statut d'inversion par d\u00e9faut pour les couvertures non configur\u00e9es", - "entity_id": "Configurez une entit\u00e9 sp\u00e9cifique.", "target_id": "Configurez les options pour la couverture." }, "title": "Configurer les options MyLink" @@ -48,6 +39,5 @@ "title": "Configurer la couverture MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/hu.json b/homeassistant/components/somfy_mylink/translations/hu.json index 2b91ccd8676..c3348b1f628 100644 --- a/homeassistant/components/somfy_mylink/translations/hu.json +++ b/homeassistant/components/somfy_mylink/translations/hu.json @@ -25,17 +25,8 @@ "cannot_connect": "Sikertelen csatlakoz\u00e1s" }, "step": { - "entity_config": { - "data": { - "reverse": "A bor\u00edt\u00f3 megfordult" - }, - "description": "Konfigur\u00e1lja az \u201e {entity_id} \u201d be\u00e1ll\u00edt\u00e1sait", - "title": "Entit\u00e1s konfigur\u00e1l\u00e1sa" - }, "init": { "data": { - "default_reverse": "A konfigur\u00e1latlan bor\u00edt\u00f3k alap\u00e9rtelmezett megford\u00edt\u00e1si \u00e1llapota", - "entity_id": "Konfigur\u00e1ljon egy adott entit\u00e1st.", "target_id": "Az \u00e1rny\u00e9kol\u00f3 be\u00e1ll\u00edt\u00e1sainak konfigur\u00e1l\u00e1sa." }, "title": "Mylink be\u00e1ll\u00edt\u00e1sok konfigur\u00e1l\u00e1sa" @@ -48,6 +39,5 @@ "title": "MyLink \u00e1rny\u00e9kol\u00f3 konfigur\u00e1l\u00e1sa" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/id.json b/homeassistant/components/somfy_mylink/translations/id.json index c4b2269ef2c..38d0d550bf4 100644 --- a/homeassistant/components/somfy_mylink/translations/id.json +++ b/homeassistant/components/somfy_mylink/translations/id.json @@ -25,17 +25,8 @@ "cannot_connect": "Gagal terhubung" }, "step": { - "entity_config": { - "data": { - "reverse": "Penutup dibalik" - }, - "description": "Konfigurasikan opsi untuk `{entity_id}`", - "title": "Konfigurasikan Entitas" - }, "init": { "data": { - "default_reverse": "Status pembalikan baku untuk penutup yang belum dikonfigurasi", - "entity_id": "Konfigurasikan entitas tertentu.", "target_id": "Konfigurasikan opsi untuk penutup." }, "title": "Konfigurasikan Opsi MyLink" @@ -48,6 +39,5 @@ "title": "Konfigurasikan Cover MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/it.json b/homeassistant/components/somfy_mylink/translations/it.json index cfd3041d248..d4fcb7513c4 100644 --- a/homeassistant/components/somfy_mylink/translations/it.json +++ b/homeassistant/components/somfy_mylink/translations/it.json @@ -25,17 +25,8 @@ "cannot_connect": "Impossibile connettersi" }, "step": { - "entity_config": { - "data": { - "reverse": "La serranda \u00e8 invertita" - }, - "description": "Configura le opzioni per `{entity_id}`", - "title": "Configura entit\u00e0" - }, "init": { "data": { - "default_reverse": "Stato d'inversione predefinito per le serrande non configurate", - "entity_id": "Configura un'entit\u00e0 specifica.", "target_id": "Configura opzioni per una tapparella" }, "title": "Configura le opzioni MyLink" @@ -48,6 +39,5 @@ "title": "Configura serranda MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/ja.json b/homeassistant/components/somfy_mylink/translations/ja.json index 4482dd37db2..49f819659b4 100644 --- a/homeassistant/components/somfy_mylink/translations/ja.json +++ b/homeassistant/components/somfy_mylink/translations/ja.json @@ -25,17 +25,8 @@ "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "step": { - "entity_config": { - "data": { - "reverse": "\u30ab\u30d0\u30fc\u304c\u9006\u306b\u306a\u3063\u3066\u3044\u307e\u3059" - }, - "description": "{entity_id}} \u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a", - "title": "\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u306e\u8a2d\u5b9a" - }, "init": { "data": { - "default_reverse": "\u672a\u8a2d\u5b9a\u306e\u30ab\u30d0\u30fc\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u53cd\u8ee2\u72b6\u614b", - "entity_id": "\u7279\u5b9a\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002", "target_id": "\u30ab\u30d0\u30fc\u306e\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002" }, "title": "MyLink\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u8a2d\u5b9a" @@ -48,6 +39,5 @@ "title": "MyLink\u30ab\u30d0\u30fc\u306e\u8a2d\u5b9a" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/ko.json b/homeassistant/components/somfy_mylink/translations/ko.json index b099d3d6ed8..3ec70c148b3 100644 --- a/homeassistant/components/somfy_mylink/translations/ko.json +++ b/homeassistant/components/somfy_mylink/translations/ko.json @@ -25,17 +25,8 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { - "entity_config": { - "data": { - "reverse": "\uc5ec\ub2eb\uc774\uac00 \ubc18\uc804\ub418\uc5c8\uc2b5\ub2c8\ub2e4" - }, - "description": "`{entity_id}`\uc5d0 \ub300\ud55c \uc635\uc158 \uad6c\uc131\ud558\uae30", - "title": "\uad6c\uc131\uc694\uc18c \uad6c\uc131\ud558\uae30" - }, "init": { "data": { - "default_reverse": "\uad6c\uc131\ub418\uc9c0 \uc54a\uc740 \uc5ec\ub2eb\uc774\uc5d0 \ub300\ud55c \uae30\ubcf8 \ubc18\uc804 \uc0c1\ud0dc", - "entity_id": "\ud2b9\uc815 \uad6c\uc131\uc694\uc18c\ub97c \uad6c\uc131\ud569\ub2c8\ub2e4.", "target_id": "\uc5ec\ub2eb\uc774\uc5d0 \ub300\ud55c \uc635\uc158 \uad6c\uc131\ud558\uae30" }, "title": "MyLink \uc635\uc158 \uad6c\uc131\ud558\uae30" @@ -48,6 +39,5 @@ "title": "MyLink \uc5ec\ub2eb\uc774 \uad6c\uc131\ud558\uae30" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/lb.json b/homeassistant/components/somfy_mylink/translations/lb.json index efaba3ab497..0fe190e43ee 100644 --- a/homeassistant/components/somfy_mylink/translations/lb.json +++ b/homeassistant/components/somfy_mylink/translations/lb.json @@ -22,6 +22,5 @@ "abort": { "cannot_connect": "Feeler beim verbannen" } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/nl.json b/homeassistant/components/somfy_mylink/translations/nl.json index 279010485b6..43c71140cb9 100644 --- a/homeassistant/components/somfy_mylink/translations/nl.json +++ b/homeassistant/components/somfy_mylink/translations/nl.json @@ -25,17 +25,8 @@ "cannot_connect": "Kan geen verbinding maken" }, "step": { - "entity_config": { - "data": { - "reverse": "Rolluik is omgekeerd" - }, - "description": "Configureer opties voor `{entity_id}`", - "title": "Entiteit configureren" - }, "init": { "data": { - "default_reverse": "Standaard omkeerstatus voor niet-geconfigureerde rolluiken", - "entity_id": "Configureer een specifieke entiteit.", "target_id": "Configureer opties voor een rolluik." }, "title": "Configureer MyLink-opties" @@ -48,6 +39,5 @@ "title": "Configureer MyLink Cover" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/no.json b/homeassistant/components/somfy_mylink/translations/no.json index 65c89866f65..b826eabf45d 100644 --- a/homeassistant/components/somfy_mylink/translations/no.json +++ b/homeassistant/components/somfy_mylink/translations/no.json @@ -25,17 +25,8 @@ "cannot_connect": "Tilkobling mislyktes" }, "step": { - "entity_config": { - "data": { - "reverse": "Rullegardinet reverseres" - }, - "description": "Konfigurer alternativer for \"{entity_id}\"", - "title": "Konfigurer entitet" - }, "init": { "data": { - "default_reverse": "Standard tilbakef\u00f8ringsstatus for ukonfigurerte rullegardiner", - "entity_id": "Konfigurer en bestemt entitet.", "target_id": "Konfigurer alternativer for et rullgardin" }, "title": "Konfigurere MyLink-alternativer" @@ -48,6 +39,5 @@ "title": "Konfigurer MyLink-deksel" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/pl.json b/homeassistant/components/somfy_mylink/translations/pl.json index c4c10de4f9d..8c713ffd31a 100644 --- a/homeassistant/components/somfy_mylink/translations/pl.json +++ b/homeassistant/components/somfy_mylink/translations/pl.json @@ -25,17 +25,8 @@ "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "step": { - "entity_config": { - "data": { - "reverse": "Roleta/pokrywa jest odwr\u00f3cona" - }, - "description": "Konfiguracja opcji dla \"{entity_id}\"", - "title": "Konfiguracja encji" - }, "init": { "data": { - "default_reverse": "Domy\u015blny stan odwr\u00f3cenia nieskonfigurowanych rolet/pokryw", - "entity_id": "Skonfiguruj okre\u015blon\u0105 encj\u0119.", "target_id": "Konfiguracja opcji rolety" }, "title": "Konfiguracja opcji MyLink" @@ -48,6 +39,5 @@ "title": "Konfiguracja rolety MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/pt-BR.json b/homeassistant/components/somfy_mylink/translations/pt-BR.json index 12efd378984..7cbe3e77f6c 100644 --- a/homeassistant/components/somfy_mylink/translations/pt-BR.json +++ b/homeassistant/components/somfy_mylink/translations/pt-BR.json @@ -25,17 +25,8 @@ "cannot_connect": "Falha ao conectar" }, "step": { - "entity_config": { - "data": { - "reverse": "A cobertura est\u00e1 invertida" - }, - "description": "Configurar op\u00e7\u00f5es para ` {entity_id} `", - "title": "Configurar entidade" - }, "init": { "data": { - "default_reverse": "Status de revers\u00e3o padr\u00e3o para coberturas n\u00e3o configuradas", - "entity_id": "Configure uma entidade espec\u00edfica.", "target_id": "Configure as op\u00e7\u00f5es para uma cobertura." }, "title": "Configurar op\u00e7\u00f5es do MyLink" @@ -48,6 +39,5 @@ "title": "Configurar a MyLink Cover" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/ru.json b/homeassistant/components/somfy_mylink/translations/ru.json index d1ab5624ecf..0385609cb21 100644 --- a/homeassistant/components/somfy_mylink/translations/ru.json +++ b/homeassistant/components/somfy_mylink/translations/ru.json @@ -25,17 +25,8 @@ "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "step": { - "entity_config": { - "data": { - "reverse": "\u0418\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043b\u044f \u0448\u0442\u043e\u0440 \u0438 \u0436\u0430\u043b\u044e\u0437\u0438" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f `{entity_id}`", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430" - }, "init": { "data": { - "default_reverse": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0434\u043b\u044f \u0448\u0442\u043e\u0440 \u0438 \u0436\u0430\u043b\u044e\u0437\u0438", - "entity_id": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430", "target_id": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0448\u0442\u043e\u0440 \u0438\u043b\u0438 \u0436\u0430\u043b\u044e\u0437\u0438." }, "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 MyLink" @@ -48,6 +39,5 @@ "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 MyLink Cover" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/tr.json b/homeassistant/components/somfy_mylink/translations/tr.json index fb402493e3b..8893b7362d9 100644 --- a/homeassistant/components/somfy_mylink/translations/tr.json +++ b/homeassistant/components/somfy_mylink/translations/tr.json @@ -25,17 +25,8 @@ "cannot_connect": "Ba\u011flanma hatas\u0131" }, "step": { - "entity_config": { - "data": { - "reverse": "Kapak ters \u00e7evrildi" - }, - "description": "'{entity_id}' i\u00e7in se\u00e7enekleri yap\u0131land\u0131r\u0131n", - "title": "Varl\u0131\u011f\u0131 Yap\u0131land\u0131r" - }, "init": { "data": { - "default_reverse": "Yap\u0131land\u0131r\u0131lmam\u0131\u015f kapaklar i\u00e7in varsay\u0131lan geri alma durumu", - "entity_id": "Belirli bir varl\u0131\u011f\u0131 yap\u0131land\u0131r\u0131n.", "target_id": "Kapak i\u00e7in se\u00e7enekleri yap\u0131land\u0131r\u0131n." }, "title": "MyLink Se\u00e7eneklerini Yap\u0131land\u0131r\u0131n" @@ -48,6 +39,5 @@ "title": "MyLink Kapa\u011f\u0131n\u0131 Yap\u0131land\u0131r\u0131n" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/uk.json b/homeassistant/components/somfy_mylink/translations/uk.json index 2d251531340..f458c21bcf9 100644 --- a/homeassistant/components/somfy_mylink/translations/uk.json +++ b/homeassistant/components/somfy_mylink/translations/uk.json @@ -24,17 +24,9 @@ "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" }, "step": { - "entity_config": { - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0456\u0432 \u0434\u043b\u044f \"{entity_id}\"", - "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u0441\u0443\u0442\u043d\u0456\u0441\u0442\u044c" - }, "init": { - "data": { - "entity_id": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0441\u043f\u0435\u0446\u0438\u0444\u0456\u0447\u043d\u043e\u0457 \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0456." - }, "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0441\u0443\u0442\u043d\u043e\u0441\u0442\u0435\u0439 MyLink" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/somfy_mylink/translations/zh-Hant.json b/homeassistant/components/somfy_mylink/translations/zh-Hant.json index 0aa72fb0c61..15a3927877d 100644 --- a/homeassistant/components/somfy_mylink/translations/zh-Hant.json +++ b/homeassistant/components/somfy_mylink/translations/zh-Hant.json @@ -25,17 +25,8 @@ "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "step": { - "entity_config": { - "data": { - "reverse": "\u7a97\u7c3e\u53cd\u5411" - }, - "description": "`{entity_id}` \u8a2d\u5b9a\u9078\u9805", - "title": "\u8a2d\u5b9a\u5be6\u9ad4" - }, "init": { "data": { - "default_reverse": "\u672a\u8a2d\u5b9a\u7a97\u7c3e\u9810\u8a2d\u70ba\u53cd\u5411", - "entity_id": "\u8a2d\u5b9a\u7279\u5b9a\u5be6\u9ad4\u3002", "target_id": "\u7a97\u7c3e\u8a2d\u5b9a\u9078\u9805\u3002" }, "title": "MyLink \u8a2d\u5b9a\u9078\u9805" @@ -48,6 +39,5 @@ "title": "\u8a2d\u5b9a MyLink \u7a97\u7c3e" } } - }, - "title": "Somfy MyLink" + } } \ No newline at end of file diff --git a/homeassistant/components/sonarr/translations/bg.json b/homeassistant/components/sonarr/translations/bg.json index d866b1d1813..05d96cf621c 100644 --- a/homeassistant/components/sonarr/translations/bg.json +++ b/homeassistant/components/sonarr/translations/bg.json @@ -17,9 +17,6 @@ "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447", - "base_path": "\u041f\u044a\u0442 \u0434\u043e API", - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", "url": "URL" } } diff --git a/homeassistant/components/sonarr/translations/ca.json b/homeassistant/components/sonarr/translations/ca.json index d3cb5720875..49170885242 100644 --- a/homeassistant/components/sonarr/translations/ca.json +++ b/homeassistant/components/sonarr/translations/ca.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Clau API", - "base_path": "Ruta a l'API", - "host": "Amfitri\u00f3", - "port": "Port", - "ssl": "Utilitza un certificat SSL", "url": "URL", "verify_ssl": "Verifica el certificat SSL" } diff --git a/homeassistant/components/sonarr/translations/cs.json b/homeassistant/components/sonarr/translations/cs.json index 9be2106b4e2..27c3a92bc2e 100644 --- a/homeassistant/components/sonarr/translations/cs.json +++ b/homeassistant/components/sonarr/translations/cs.json @@ -18,9 +18,6 @@ "user": { "data": { "api_key": "Kl\u00ed\u010d API", - "host": "Hostitel", - "port": "Port", - "ssl": "Pou\u017e\u00edv\u00e1 SSL certifik\u00e1t", "url": "URL", "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" } diff --git a/homeassistant/components/sonarr/translations/de.json b/homeassistant/components/sonarr/translations/de.json index eb521c1c237..4559b75c655 100644 --- a/homeassistant/components/sonarr/translations/de.json +++ b/homeassistant/components/sonarr/translations/de.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API-Schl\u00fcssel", - "base_path": "Pfad zur API", - "host": "Host", - "port": "Port", - "ssl": "Verwendet ein SSL-Zertifikat", "url": "URL", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" } diff --git a/homeassistant/components/sonarr/translations/el.json b/homeassistant/components/sonarr/translations/el.json index 22895b3a38a..542225c0960 100644 --- a/homeassistant/components/sonarr/translations/el.json +++ b/homeassistant/components/sonarr/translations/el.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "base_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c0\u03c1\u03bf\u03c2 \u03c4\u03bf API", - "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2", - "port": "\u0398\u03cd\u03c1\u03b1", - "ssl": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ad\u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL", "url": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" } diff --git a/homeassistant/components/sonarr/translations/en.json b/homeassistant/components/sonarr/translations/en.json index f676005dcfe..61b1158d9a3 100644 --- a/homeassistant/components/sonarr/translations/en.json +++ b/homeassistant/components/sonarr/translations/en.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API Key", - "base_path": "Path to API", - "host": "Host", - "port": "Port", - "ssl": "Uses an SSL certificate", "url": "URL", "verify_ssl": "Verify SSL certificate" } diff --git a/homeassistant/components/sonarr/translations/es.json b/homeassistant/components/sonarr/translations/es.json index f7d55cc5353..3c1b7c0fa9a 100644 --- a/homeassistant/components/sonarr/translations/es.json +++ b/homeassistant/components/sonarr/translations/es.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Clave API", - "base_path": "Ruta a la API", - "host": "Host", - "port": "Puerto", - "ssl": "Utiliza un certificado SSL", "url": "URL", "verify_ssl": "Verificar certificado SSL" } diff --git a/homeassistant/components/sonarr/translations/et.json b/homeassistant/components/sonarr/translations/et.json index 4629e59e68d..8fa1d128455 100644 --- a/homeassistant/components/sonarr/translations/et.json +++ b/homeassistant/components/sonarr/translations/et.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API v\u00f5ti", - "base_path": "API asukoht", - "host": "", - "port": "", - "ssl": "Kasutab SSL serti", "url": "URL", "verify_ssl": "Kontrolli SSL sertifikaati" } diff --git a/homeassistant/components/sonarr/translations/fr.json b/homeassistant/components/sonarr/translations/fr.json index 0793adc1b9f..dc88f6770bc 100644 --- a/homeassistant/components/sonarr/translations/fr.json +++ b/homeassistant/components/sonarr/translations/fr.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Cl\u00e9 d'API", - "base_path": "Chemin vers l'API", - "host": "H\u00f4te", - "port": "Port", - "ssl": "Utilise un certificat SSL", "url": "URL", "verify_ssl": "V\u00e9rifier le certificat SSL" } diff --git a/homeassistant/components/sonarr/translations/he.json b/homeassistant/components/sonarr/translations/he.json index 53b98ae4139..3fbaf2d2684 100644 --- a/homeassistant/components/sonarr/translations/he.json +++ b/homeassistant/components/sonarr/translations/he.json @@ -17,9 +17,6 @@ "user": { "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", - "host": "\u05de\u05d0\u05e8\u05d7", - "port": "\u05e4\u05ea\u05d7\u05d4", - "ssl": "\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d0\u05d9\u05e9\u05d5\u05e8 SSL", "url": "\u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8", "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" } diff --git a/homeassistant/components/sonarr/translations/hu.json b/homeassistant/components/sonarr/translations/hu.json index 5aabd38a974..9e77eecfb6f 100644 --- a/homeassistant/components/sonarr/translations/hu.json +++ b/homeassistant/components/sonarr/translations/hu.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API kulcs", - "base_path": "El\u00e9r\u00e9si \u00fat az API-hoz", - "host": "C\u00edm", - "port": "Port", - "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "url": "URL", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" } diff --git a/homeassistant/components/sonarr/translations/id.json b/homeassistant/components/sonarr/translations/id.json index ec76bf44491..a84276eab01 100644 --- a/homeassistant/components/sonarr/translations/id.json +++ b/homeassistant/components/sonarr/translations/id.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Kunci API", - "base_path": "Jalur ke API", - "host": "Host", - "port": "Port", - "ssl": "Menggunakan sertifikat SSL", "url": "URL", "verify_ssl": "Verifikasi sertifikat SSL" } diff --git a/homeassistant/components/sonarr/translations/it.json b/homeassistant/components/sonarr/translations/it.json index ba78810d928..727e556bb3c 100644 --- a/homeassistant/components/sonarr/translations/it.json +++ b/homeassistant/components/sonarr/translations/it.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Chiave API", - "base_path": "Percorso dell'API", - "host": "Host", - "port": "Porta", - "ssl": "Utilizza un certificato SSL", "url": "URL", "verify_ssl": "Verifica il certificato SSL" } diff --git a/homeassistant/components/sonarr/translations/ja.json b/homeassistant/components/sonarr/translations/ja.json index 53a659d9bde..cbe230bbc94 100644 --- a/homeassistant/components/sonarr/translations/ja.json +++ b/homeassistant/components/sonarr/translations/ja.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API\u30ad\u30fc", - "base_path": "API\u3078\u306e\u30d1\u30b9", - "host": "\u30db\u30b9\u30c8", - "port": "\u30dd\u30fc\u30c8", - "ssl": "SSL\u8a3c\u660e\u66f8\u3092\u4f7f\u7528\u3059\u308b", "url": "URL", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" } diff --git a/homeassistant/components/sonarr/translations/ko.json b/homeassistant/components/sonarr/translations/ko.json index fbff72e46f2..b7389a03043 100644 --- a/homeassistant/components/sonarr/translations/ko.json +++ b/homeassistant/components/sonarr/translations/ko.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API \ud0a4", - "base_path": "API \uacbd\ub85c", - "host": "\ud638\uc2a4\ud2b8", - "port": "\ud3ec\ud2b8", - "ssl": "SSL \uc778\uc99d\uc11c \uc0ac\uc6a9", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" } } diff --git a/homeassistant/components/sonarr/translations/lb.json b/homeassistant/components/sonarr/translations/lb.json index 392b3f7d4e2..dfde55e9924 100644 --- a/homeassistant/components/sonarr/translations/lb.json +++ b/homeassistant/components/sonarr/translations/lb.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API Schl\u00ebssel", - "base_path": "Pad zur API", - "host": "Host", - "port": "Port", - "ssl": "Benotzt ee SSL Zertifikat", "verify_ssl": "SSL Zertifikat iwwerpr\u00e9iwen" } } diff --git a/homeassistant/components/sonarr/translations/nl.json b/homeassistant/components/sonarr/translations/nl.json index 58bf04e283d..d6782781090 100644 --- a/homeassistant/components/sonarr/translations/nl.json +++ b/homeassistant/components/sonarr/translations/nl.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API-sleutel", - "base_path": "Pad naar API", - "host": "Host", - "port": "Poort", - "ssl": "Maakt gebruik van een SSL-certificaat", "url": "URL", "verify_ssl": "SSL-certificaat verifi\u00ebren" } diff --git a/homeassistant/components/sonarr/translations/no.json b/homeassistant/components/sonarr/translations/no.json index 5ee028b8f02..3d64a9199a4 100644 --- a/homeassistant/components/sonarr/translations/no.json +++ b/homeassistant/components/sonarr/translations/no.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API-n\u00f8kkel", - "base_path": "Bane til API", - "host": "Vert", - "port": "Port", - "ssl": "Bruker et SSL-sertifikat", "url": "URL", "verify_ssl": "Verifisere SSL-sertifikat" } diff --git a/homeassistant/components/sonarr/translations/pl.json b/homeassistant/components/sonarr/translations/pl.json index e5be700a752..3f93623af76 100644 --- a/homeassistant/components/sonarr/translations/pl.json +++ b/homeassistant/components/sonarr/translations/pl.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Klucz API", - "base_path": "\u015acie\u017cka do API", - "host": "Nazwa hosta lub adres IP", - "port": "Port", - "ssl": "Certyfikat SSL", "url": "URL", "verify_ssl": "Weryfikacja certyfikatu SSL" } diff --git a/homeassistant/components/sonarr/translations/pt-BR.json b/homeassistant/components/sonarr/translations/pt-BR.json index 4c474ef2349..1aa3de5b209 100644 --- a/homeassistant/components/sonarr/translations/pt-BR.json +++ b/homeassistant/components/sonarr/translations/pt-BR.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "Chave da API", - "base_path": "Caminho para a API", - "host": "Nome do host", - "port": "Porta", - "ssl": "Usar um certificado SSL", "url": "URL", "verify_ssl": "Verifique o certificado SSL" } diff --git a/homeassistant/components/sonarr/translations/pt.json b/homeassistant/components/sonarr/translations/pt.json index bb36cb63dfc..9001b9c2c3f 100644 --- a/homeassistant/components/sonarr/translations/pt.json +++ b/homeassistant/components/sonarr/translations/pt.json @@ -17,9 +17,6 @@ "user": { "data": { "api_key": "Chave da API", - "host": "Servidor", - "port": "Porta", - "ssl": "Utiliza um certificado SSL", "verify_ssl": "Verificar o certificado SSL" } } diff --git a/homeassistant/components/sonarr/translations/ru.json b/homeassistant/components/sonarr/translations/ru.json index 531058f3fff..47843b028db 100644 --- a/homeassistant/components/sonarr/translations/ru.json +++ b/homeassistant/components/sonarr/translations/ru.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", - "base_path": "\u041f\u0443\u0442\u044c \u043a API", - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "ssl": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL", "url": "URL-\u0430\u0434\u0440\u0435\u0441", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" } diff --git a/homeassistant/components/sonarr/translations/sk.json b/homeassistant/components/sonarr/translations/sk.json index 6e1cd075aa9..64731388e98 100644 --- a/homeassistant/components/sonarr/translations/sk.json +++ b/homeassistant/components/sonarr/translations/sk.json @@ -9,8 +9,7 @@ "step": { "user": { "data": { - "api_key": "API k\u013e\u00fa\u010d", - "port": "Port" + "api_key": "API k\u013e\u00fa\u010d" } } } diff --git a/homeassistant/components/sonarr/translations/sl.json b/homeassistant/components/sonarr/translations/sl.json index b8c5332be9c..fd6fbd9cd2a 100644 --- a/homeassistant/components/sonarr/translations/sl.json +++ b/homeassistant/components/sonarr/translations/sl.json @@ -3,7 +3,6 @@ "step": { "user": { "data": { - "ssl": "Uporablja SSL certifikat", "verify_ssl": "Preverite SSL certifikat" } } diff --git a/homeassistant/components/sonarr/translations/tr.json b/homeassistant/components/sonarr/translations/tr.json index c064e4947c3..c72d84767d1 100644 --- a/homeassistant/components/sonarr/translations/tr.json +++ b/homeassistant/components/sonarr/translations/tr.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API Anahtar\u0131", - "base_path": "API yolu", - "host": "Ana Bilgisayar", - "port": "Port", - "ssl": "SSL sertifikas\u0131 kullan\u0131r", "url": "URL", "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" } diff --git a/homeassistant/components/sonarr/translations/uk.json b/homeassistant/components/sonarr/translations/uk.json index 0b6b7acf26d..85f1c42cb42 100644 --- a/homeassistant/components/sonarr/translations/uk.json +++ b/homeassistant/components/sonarr/translations/uk.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", - "base_path": "\u0428\u043b\u044f\u0445 \u0434\u043e API", - "host": "\u0425\u043e\u0441\u0442", - "port": "\u041f\u043e\u0440\u0442", - "ssl": "\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442 SSL", "verify_ssl": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u0430 SSL" } } diff --git a/homeassistant/components/sonarr/translations/zh-Hans.json b/homeassistant/components/sonarr/translations/zh-Hans.json index 265928213f5..045eb27df30 100644 --- a/homeassistant/components/sonarr/translations/zh-Hans.json +++ b/homeassistant/components/sonarr/translations/zh-Hans.json @@ -17,9 +17,6 @@ "user": { "data": { "api_key": "API \u5bc6\u94a5", - "host": "\u4e3b\u673a\u5730\u5740", - "port": "\u7aef\u53e3", - "ssl": "\u4f7f\u7528 SSL \u8bc1\u4e66", "verify_ssl": "\u9a8c\u8bc1 SSL \u8bc1\u4e66" } } diff --git a/homeassistant/components/sonarr/translations/zh-Hant.json b/homeassistant/components/sonarr/translations/zh-Hant.json index 4688ec2e438..6d9be9dd294 100644 --- a/homeassistant/components/sonarr/translations/zh-Hant.json +++ b/homeassistant/components/sonarr/translations/zh-Hant.json @@ -18,10 +18,6 @@ "user": { "data": { "api_key": "API \u91d1\u9470", - "base_path": "API \u8def\u5f91", - "host": "\u4e3b\u6a5f\u7aef", - "port": "\u901a\u8a0a\u57e0", - "ssl": "\u4f7f\u7528 SSL \u8a8d\u8b49", "url": "\u7db2\u5740", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" } diff --git a/homeassistant/components/speedtestdotnet/translations/ar.json b/homeassistant/components/speedtestdotnet/translations/ar.json index 527ad2ecd96..943182c47a4 100644 --- a/homeassistant/components/speedtestdotnet/translations/ar.json +++ b/homeassistant/components/speedtestdotnet/translations/ar.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "wrong_server_id": "\u0645\u0639\u0631\u0641 \u0627\u0644\u062e\u0627\u062f\u0645 \u063a\u064a\u0631 \u0635\u0627\u0644\u062d" - }, "step": { "user": { "description": "\u0647\u0644 \u0623\u0646\u062a \u0645\u062a\u0623\u0643\u062f \u0645\u0646 \u0623\u0646\u0643 \u062a\u0631\u064a\u062f \u0625\u0639\u062f\u0627\u062f SpeedTest\u061f" diff --git a/homeassistant/components/speedtestdotnet/translations/ca.json b/homeassistant/components/speedtestdotnet/translations/ca.json index 57caf0ed69b..c1c5dda71cf 100644 --- a/homeassistant/components/speedtestdotnet/translations/ca.json +++ b/homeassistant/components/speedtestdotnet/translations/ca.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3.", - "wrong_server_id": "L'identificador del servidor no \u00e9s v\u00e0lid" + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/cs.json b/homeassistant/components/speedtestdotnet/translations/cs.json index 7564e3ab4f7..22ad2f23322 100644 --- a/homeassistant/components/speedtestdotnet/translations/cs.json +++ b/homeassistant/components/speedtestdotnet/translations/cs.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.", - "wrong_server_id": "ID serveru nen\u00ed platn\u00e9." + "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/de.json b/homeassistant/components/speedtestdotnet/translations/de.json index eff51fab0b4..81910cb9c70 100644 --- a/homeassistant/components/speedtestdotnet/translations/de.json +++ b/homeassistant/components/speedtestdotnet/translations/de.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich.", - "wrong_server_id": "Server-ID ist ung\u00fcltig" + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/el.json b/homeassistant/components/speedtestdotnet/translations/el.json index 096e339732c..25b5e23ab69 100644 --- a/homeassistant/components/speedtestdotnet/translations/el.json +++ b/homeassistant/components/speedtestdotnet/translations/el.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ae\u03b4\u03b7. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03c4\u03c1\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae.", - "wrong_server_id": "\u03a4\u03bf \u03b1\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf" + "single_instance_allowed": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b7\u03ba\u03b5 \u03ae\u03b4\u03b7. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03c0\u03b1\u03c1\u03b1\u03bc\u03b5\u03c4\u03c1\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/en.json b/homeassistant/components/speedtestdotnet/translations/en.json index b56ff193e33..eab480073bc 100644 --- a/homeassistant/components/speedtestdotnet/translations/en.json +++ b/homeassistant/components/speedtestdotnet/translations/en.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Already configured. Only a single configuration possible.", - "wrong_server_id": "Server ID is not valid" + "single_instance_allowed": "Already configured. Only a single configuration possible." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/es.json b/homeassistant/components/speedtestdotnet/translations/es.json index 1727f4d2a6c..42f263971ba 100644 --- a/homeassistant/components/speedtestdotnet/translations/es.json +++ b/homeassistant/components/speedtestdotnet/translations/es.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n.", - "wrong_server_id": "El identificador del servidor no es v\u00e1lido" + "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/et.json b/homeassistant/components/speedtestdotnet/translations/et.json index 834581a9941..ac1915e760a 100644 --- a/homeassistant/components/speedtestdotnet/translations/et.json +++ b/homeassistant/components/speedtestdotnet/translations/et.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine.", - "wrong_server_id": "Serveri ID ei sobi" + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/fr.json b/homeassistant/components/speedtestdotnet/translations/fr.json index 4ad47bb6a3e..d2efebd0eb1 100644 --- a/homeassistant/components/speedtestdotnet/translations/fr.json +++ b/homeassistant/components/speedtestdotnet/translations/fr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.", - "wrong_server_id": "L'ID du serveur n'est pas valide" + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/he.json b/homeassistant/components/speedtestdotnet/translations/he.json index ea3dd80aa94..08506bf3437 100644 --- a/homeassistant/components/speedtestdotnet/translations/he.json +++ b/homeassistant/components/speedtestdotnet/translations/he.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea.", - "wrong_server_id": "\u05de\u05d6\u05d4\u05d4 \u05d4\u05e9\u05e8\u05ea \u05d0\u05d9\u05e0\u05d5 \u05d7\u05d5\u05e7\u05d9" + "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/hu.json b/homeassistant/components/speedtestdotnet/translations/hu.json index 9e602652e5b..c223e8b9376 100644 --- a/homeassistant/components/speedtestdotnet/translations/hu.json +++ b/homeassistant/components/speedtestdotnet/translations/hu.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges.", - "wrong_server_id": "A szerver azonos\u00edt\u00f3 \u00e9rv\u00e9nytelen" + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/id.json b/homeassistant/components/speedtestdotnet/translations/id.json index 24e78609380..f609c3d384a 100644 --- a/homeassistant/components/speedtestdotnet/translations/id.json +++ b/homeassistant/components/speedtestdotnet/translations/id.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan.", - "wrong_server_id": "ID server tidak valid" + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/it.json b/homeassistant/components/speedtestdotnet/translations/it.json index d0c44329313..07615fe093c 100644 --- a/homeassistant/components/speedtestdotnet/translations/it.json +++ b/homeassistant/components/speedtestdotnet/translations/it.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione.", - "wrong_server_id": "L'ID Server non \u00e8 valido" + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/ja.json b/homeassistant/components/speedtestdotnet/translations/ja.json index 4e254367593..5712139b6b9 100644 --- a/homeassistant/components/speedtestdotnet/translations/ja.json +++ b/homeassistant/components/speedtestdotnet/translations/ja.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002", - "wrong_server_id": "\u30b5\u30fc\u30d0\u30fcID\u304c\u7121\u52b9\u3067\u3059" + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/ko.json b/homeassistant/components/speedtestdotnet/translations/ko.json index e3b65208317..dbef7c3b7d3 100644 --- a/homeassistant/components/speedtestdotnet/translations/ko.json +++ b/homeassistant/components/speedtestdotnet/translations/ko.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", - "wrong_server_id": "\uc11c\ubc84 ID\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/lb.json b/homeassistant/components/speedtestdotnet/translations/lb.json index 3081080d1fb..4007faf71e7 100644 --- a/homeassistant/components/speedtestdotnet/translations/lb.json +++ b/homeassistant/components/speedtestdotnet/translations/lb.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun ass m\u00e9iglech.", - "wrong_server_id": "Server ID ass ong\u00eblteg" + "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun ass m\u00e9iglech." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/nl.json b/homeassistant/components/speedtestdotnet/translations/nl.json index 3a112d48e9d..8cd6cb960c8 100644 --- a/homeassistant/components/speedtestdotnet/translations/nl.json +++ b/homeassistant/components/speedtestdotnet/translations/nl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "wrong_server_id": "Server-ID is niet geldig" + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/no.json b/homeassistant/components/speedtestdotnet/translations/no.json index a079bc1fa7b..01909d39f06 100644 --- a/homeassistant/components/speedtestdotnet/translations/no.json +++ b/homeassistant/components/speedtestdotnet/translations/no.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig.", - "wrong_server_id": "Server ID er ikke gyldig" + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/pl.json b/homeassistant/components/speedtestdotnet/translations/pl.json index d64a7920bea..b06d7cdd285 100644 --- a/homeassistant/components/speedtestdotnet/translations/pl.json +++ b/homeassistant/components/speedtestdotnet/translations/pl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja.", - "wrong_server_id": "Identyfikator serwera jest nieprawid\u0142owy" + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/pt-BR.json b/homeassistant/components/speedtestdotnet/translations/pt-BR.json index 0498a2fad6d..739b3b41875 100644 --- a/homeassistant/components/speedtestdotnet/translations/pt-BR.json +++ b/homeassistant/components/speedtestdotnet/translations/pt-BR.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel.", - "wrong_server_id": "O ID do servidor n\u00e3o \u00e9 v\u00e1lido" + "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/ru.json b/homeassistant/components/speedtestdotnet/translations/ru.json index c26c63d8511..3ffcd10bf31 100644 --- a/homeassistant/components/speedtestdotnet/translations/ru.json +++ b/homeassistant/components/speedtestdotnet/translations/ru.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e.", - "wrong_server_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043d\u0435\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d." + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/tr.json b/homeassistant/components/speedtestdotnet/translations/tr.json index b13be7c5e0c..5becafdf153 100644 --- a/homeassistant/components/speedtestdotnet/translations/tr.json +++ b/homeassistant/components/speedtestdotnet/translations/tr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr.", - "wrong_server_id": "Sunucu kimli\u011fi ge\u00e7erli de\u011fil" + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/uk.json b/homeassistant/components/speedtestdotnet/translations/uk.json index e54ed1da92e..0456d600e59 100644 --- a/homeassistant/components/speedtestdotnet/translations/uk.json +++ b/homeassistant/components/speedtestdotnet/translations/uk.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f.", - "wrong_server_id": "\u041d\u0435\u043f\u0440\u0438\u043f\u0443\u0441\u0442\u0438\u043c\u0438\u0439 \u0456\u0434\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0440\u0432\u0435\u0440\u0430." + "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f." }, "step": { "user": { diff --git a/homeassistant/components/speedtestdotnet/translations/zh-Hant.json b/homeassistant/components/speedtestdotnet/translations/zh-Hant.json index 49b8b0cfb20..43d30d4aeb8 100644 --- a/homeassistant/components/speedtestdotnet/translations/zh-Hant.json +++ b/homeassistant/components/speedtestdotnet/translations/zh-Hant.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002", - "wrong_server_id": "\u4f3a\u670d\u5668 ID \u7121\u6548" + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" }, "step": { "user": { diff --git a/homeassistant/components/sql/translations/ca.json b/homeassistant/components/sql/translations/ca.json index a579253e8d7..48498362c89 100644 --- a/homeassistant/components/sql/translations/ca.json +++ b/homeassistant/components/sql/translations/ca.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "URL de la base de dades inv\u00e0lid", - "query_invalid": "Consulta SQL inv\u00e0lida", - "value_template_invalid": "Plantilla de valor inv\u00e0lida" + "query_invalid": "Consulta SQL inv\u00e0lida" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "URL de la base de dades inv\u00e0lid", - "query_invalid": "Consulta SQL inv\u00e0lida", - "value_template_invalid": "Plantilla de valor inv\u00e0lida" + "query_invalid": "Consulta SQL inv\u00e0lida" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/de.json b/homeassistant/components/sql/translations/de.json index 53642f5bede..258bd06f876 100644 --- a/homeassistant/components/sql/translations/de.json +++ b/homeassistant/components/sql/translations/de.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Datenbank-URL ung\u00fcltig", - "query_invalid": "SQL-Abfrage ung\u00fcltig", - "value_template_invalid": "Wertvorlage ung\u00fcltig" + "query_invalid": "SQL-Abfrage ung\u00fcltig" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Datenbank-URL ung\u00fcltig", - "query_invalid": "SQL-Abfrage ung\u00fcltig", - "value_template_invalid": "Wertvorlage ung\u00fcltig" + "query_invalid": "SQL-Abfrage ung\u00fcltig" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/el.json b/homeassistant/components/sql/translations/el.json index 85326cb886e..9f568eb0b03 100644 --- a/homeassistant/components/sql/translations/el.json +++ b/homeassistant/components/sql/translations/el.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", - "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL", - "value_template_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL \u03b2\u03ac\u03c3\u03b7\u03c2 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03c9\u03bd", - "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL", - "value_template_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03c0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf \u03c4\u03b9\u03bc\u03ae\u03c2" + "query_invalid": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03b5\u03c1\u03ce\u03c4\u03b7\u03bc\u03b1 SQL" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/en.json b/homeassistant/components/sql/translations/en.json index 4b72d024df4..9d05f771853 100644 --- a/homeassistant/components/sql/translations/en.json +++ b/homeassistant/components/sql/translations/en.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid", - "value_template_invalid": "Value Template invalid" + "query_invalid": "SQL Query invalid" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Database URL invalid", - "query_invalid": "SQL Query invalid", - "value_template_invalid": "Value Template invalid" + "query_invalid": "SQL Query invalid" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/es.json b/homeassistant/components/sql/translations/es.json index 3d794607feb..7117115efc7 100644 --- a/homeassistant/components/sql/translations/es.json +++ b/homeassistant/components/sql/translations/es.json @@ -28,8 +28,7 @@ "options": { "error": { "db_url_invalid": "URL de la base de datos inv\u00e1lido", - "query_invalid": "Consulta SQL inv\u00e1lida", - "value_template_invalid": "Plantilla de valor inv\u00e1lida" + "query_invalid": "Consulta SQL inv\u00e1lida" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/et.json b/homeassistant/components/sql/translations/et.json index 62bbd96b63e..701c0e6d490 100644 --- a/homeassistant/components/sql/translations/et.json +++ b/homeassistant/components/sql/translations/et.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Andmebaasi URL on vigane", - "query_invalid": "SQL-p\u00e4ring on kehtetu", - "value_template_invalid": "V\u00e4\u00e4rtuse mall on kehtetu" + "query_invalid": "SQL-p\u00e4ring on kehtetu" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Andmebaasi URL on vigane", - "query_invalid": "V\u00e4\u00e4rtuse mall on kehtetu", - "value_template_invalid": "V\u00e4\u00e4rtuse mall on kehtetu" + "query_invalid": "V\u00e4\u00e4rtuse mall on kehtetu" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/fr.json b/homeassistant/components/sql/translations/fr.json index f7607845119..9d791062798 100644 --- a/homeassistant/components/sql/translations/fr.json +++ b/homeassistant/components/sql/translations/fr.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "URL de la base de donn\u00e9es non valide", - "query_invalid": "Requ\u00eate SQL non valide", - "value_template_invalid": "Mod\u00e8le de valeur non valide" + "query_invalid": "Requ\u00eate SQL non valide" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "URL de la base de donn\u00e9es non valide", - "query_invalid": "Requ\u00eate SQL non valide", - "value_template_invalid": "Mod\u00e8le de valeur non valide" + "query_invalid": "Requ\u00eate SQL non valide" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/hu.json b/homeassistant/components/sql/translations/hu.json index c7f8b02a2b6..2e7f74c8fae 100644 --- a/homeassistant/components/sql/translations/hu.json +++ b/homeassistant/components/sql/translations/hu.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "\u00c9rv\u00e9nytelen adatb\u00e1zis URL-c\u00edm", - "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s", - "value_template_invalid": "\u00c9rv\u00e9nytelen \u00e9rt\u00e9ksablon" + "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "\u00c9rv\u00e9nytelen adatb\u00e1zis URL-c\u00edm", - "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s", - "value_template_invalid": "\u00c9rv\u00e9nytelen \u00e9rt\u00e9ksablon" + "query_invalid": "\u00c9rv\u00e9nytelen SQL-lek\u00e9rdez\u00e9s" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/id.json b/homeassistant/components/sql/translations/id.json index 8680824e467..08810e24b2c 100644 --- a/homeassistant/components/sql/translations/id.json +++ b/homeassistant/components/sql/translations/id.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "URL database tidak valid", - "query_invalid": "Kueri SQL tidak valid", - "value_template_invalid": "Templat Nilai tidak valid" + "query_invalid": "Kueri SQL tidak valid" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "URL database tidak valid", - "query_invalid": "Kueri SQL tidak valid", - "value_template_invalid": "Templat Nilai tidak valid" + "query_invalid": "Kueri SQL tidak valid" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/it.json b/homeassistant/components/sql/translations/it.json index 3d7bee5454e..04cfb5744bf 100644 --- a/homeassistant/components/sql/translations/it.json +++ b/homeassistant/components/sql/translations/it.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "URL database non valido", - "query_invalid": "Query SQL non valida", - "value_template_invalid": "Modello di valore non valido" + "query_invalid": "Query SQL non valida" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "URL database non valido", - "query_invalid": "Query SQL non valida", - "value_template_invalid": "Modello di valore non valido" + "query_invalid": "Query SQL non valida" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/ja.json b/homeassistant/components/sql/translations/ja.json index 3b9fdfc8eaa..4b6e6fec7bb 100644 --- a/homeassistant/components/sql/translations/ja.json +++ b/homeassistant/components/sql/translations/ja.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL\u304c\u7121\u52b9\u3067\u3059", - "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059", - "value_template_invalid": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u7121\u52b9\u3067\u3059" + "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306eURL\u304c\u7121\u52b9\u3067\u3059", - "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059", - "value_template_invalid": "\u5024\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u7121\u52b9\u3067\u3059" + "query_invalid": "SQL\u30af\u30a8\u30ea\u304c\u7121\u52b9\u3067\u3059" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/ko.json b/homeassistant/components/sql/translations/ko.json new file mode 100644 index 00000000000..3d4baeb6e35 --- /dev/null +++ b/homeassistant/components/sql/translations/ko.json @@ -0,0 +1,51 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "db_url_invalid": "\ub370\uc774\ud130\ubca0\uc774\uc2a4 URL\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "query_invalid": "SQL \ucffc\ub9ac\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "step": { + "user": { + "data": { + "column": "\uc5f4", + "db_url": "\ub370\uc774\ud130\ubca0\uc774\uc2a4 URL", + "name": "\uc774\ub984", + "query": "\ucffc\ub9ac \uc120\ud0dd", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704", + "value_template": "\uac12 \ud15c\ud50c\ub9bf" + }, + "data_description": { + "column": "\ubc18\ud658\ub41c \ucffc\ub9ac\uac00 \uc0c1\ud0dc\ub85c \ud45c\uc2dc\ub418\ub3c4\ub85d \ud558\ub294 \uc5f4", + "db_url": "\ub370\uc774\ud130\ubca0\uc774\uc2a4 URL, \uae30\ubcf8 HA \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \ube44\uc6cc \ub461\ub2c8\ub2e4.", + "name": "\ud1b5\ud569\uad6c\uc131\uc694\uc18c \ubc0f \uc13c\uc11c\uc5d0 \uc0ac\uc6a9\ub420 \uc774\ub984", + "query": "\uc2e4\ud589\ud560 \ucffc\ub9ac\ub294 'SELECT'\ub85c \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4.", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704(\uc120\ud0dd \uc0ac\ud56d)", + "value_template": "\uac12 \ud15c\ud50c\ub9bf(\uc120\ud0dd \uc0ac\ud56d)" + } + } + } + }, + "options": { + "error": { + "db_url_invalid": "\ub370\uc774\ud130\ubca0\uc774\uc2a4 URL\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4." + }, + "step": { + "init": { + "data": { + "name": "\uc774\ub984" + }, + "data_description": { + "column": "\ubc18\ud658\ub41c \ucffc\ub9ac\uac00 \uc0c1\ud0dc\ub85c \ud45c\uc2dc\ub418\ub3c4\ub85d \ud558\ub294 \uc5f4", + "db_url": "\ub370\uc774\ud130\ubca0\uc774\uc2a4 URL, \uae30\ubcf8 HA \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 \ube44\uc6cc \ub461\ub2c8\ub2e4.", + "name": "\ud1b5\ud569\uad6c\uc131\uc694\uc18c \ubc0f \uc13c\uc11c\uc5d0 \uc0ac\uc6a9\ub420 \uc774\ub984", + "query": "\uc2e4\ud589\ud560 \ucffc\ub9ac\ub294 'SELECT'\ub85c \uc2dc\uc791\ud574\uc57c \ud569\ub2c8\ub2e4.", + "unit_of_measurement": "\uce21\uc815 \ub2e8\uc704(\uc120\ud0dd \uc0ac\ud56d)", + "value_template": "\uac12 \ud15c\ud50c\ub9bf(\uc120\ud0dd \uc0ac\ud56d)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sql/translations/nl.json b/homeassistant/components/sql/translations/nl.json index 68aa1230432..e93b63df296 100644 --- a/homeassistant/components/sql/translations/nl.json +++ b/homeassistant/components/sql/translations/nl.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Database URL ongeldig", - "query_invalid": "SQL Query ongeldig", - "value_template_invalid": "Waarde Template ongeldig" + "query_invalid": "SQL Query ongeldig" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Database URL ongeldig", - "query_invalid": "SQL Query ongeldig", - "value_template_invalid": "Waarde Template ongeldig" + "query_invalid": "SQL Query ongeldig" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/no.json b/homeassistant/components/sql/translations/no.json index ab2554c4fb9..907a618da75 100644 --- a/homeassistant/components/sql/translations/no.json +++ b/homeassistant/components/sql/translations/no.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Database-URL er ugyldig", - "query_invalid": "SQL-sp\u00f8rringen er ugyldig", - "value_template_invalid": "Verdimalen er ugyldig" + "query_invalid": "SQL-sp\u00f8rringen er ugyldig" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Database-URL er ugyldig", - "query_invalid": "SQL-sp\u00f8rringen er ugyldig", - "value_template_invalid": "Verdimalen er ugyldig" + "query_invalid": "SQL-sp\u00f8rringen er ugyldig" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/pl.json b/homeassistant/components/sql/translations/pl.json index 2527a8239b3..3f4211c72a5 100644 --- a/homeassistant/components/sql/translations/pl.json +++ b/homeassistant/components/sql/translations/pl.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Nieprawid\u0142owy adres URL bazy danych", - "query_invalid": "Nieprawid\u0142owe zapytanie SQL", - "value_template_invalid": "Nieprawid\u0142owy szablon warto\u015bci" + "query_invalid": "Nieprawid\u0142owe zapytanie SQL" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Nieprawid\u0142owy adres URL bazy danych", - "query_invalid": "Nieprawid\u0142owe zapytanie SQL", - "value_template_invalid": "Nieprawid\u0142owy szablon warto\u015bci" + "query_invalid": "Nieprawid\u0142owe zapytanie SQL" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/pt-BR.json b/homeassistant/components/sql/translations/pt-BR.json index 1846b8d5488..89231f88f2f 100644 --- a/homeassistant/components/sql/translations/pt-BR.json +++ b/homeassistant/components/sql/translations/pt-BR.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "URL do banco de dados inv\u00e1lida", - "query_invalid": "Consulta SQL inv\u00e1lida", - "value_template_invalid": "Modelo do valor inv\u00e1lido" + "query_invalid": "Consulta SQL inv\u00e1lida" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "URL do banco de dados inv\u00e1lida", - "query_invalid": "Consulta SQL inv\u00e1lida", - "value_template_invalid": "Modelo do valor inv\u00e1lido" + "query_invalid": "Consulta SQL inv\u00e1lida" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/ru.json b/homeassistant/components/sql/translations/ru.json index 2d993776fa6..fbc3c00874a 100644 --- a/homeassistant/components/sql/translations/ru.json +++ b/homeassistant/components/sql/translations/ru.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.", - "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441.", - "value_template_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f." + "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441." }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 URL-\u0430\u0434\u0440\u0435\u0441 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.", - "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441.", - "value_template_invalid": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f." + "query_invalid": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441." }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/tr.json b/homeassistant/components/sql/translations/tr.json index 5757f50cdfb..e37b603162d 100644 --- a/homeassistant/components/sql/translations/tr.json +++ b/homeassistant/components/sql/translations/tr.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "Veritaban\u0131 URL'si ge\u00e7ersiz", - "query_invalid": "SQL Sorgusu ge\u00e7ersiz", - "value_template_invalid": "De\u011fer \u015eablonu ge\u00e7ersiz" + "query_invalid": "SQL Sorgusu ge\u00e7ersiz" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "Veritaban\u0131 URL'si ge\u00e7ersiz", - "query_invalid": "SQL Sorgusu ge\u00e7ersiz", - "value_template_invalid": "De\u011fer \u015eablonu ge\u00e7ersiz" + "query_invalid": "SQL Sorgusu ge\u00e7ersiz" }, "step": { "init": { diff --git a/homeassistant/components/sql/translations/zh-Hant.json b/homeassistant/components/sql/translations/zh-Hant.json index 43349c39295..3e746b78fff 100644 --- a/homeassistant/components/sql/translations/zh-Hant.json +++ b/homeassistant/components/sql/translations/zh-Hant.json @@ -5,8 +5,7 @@ }, "error": { "db_url_invalid": "\u8cc7\u6599\u5eab URL \u7121\u6548", - "query_invalid": "SQL \u67e5\u8a62\u7121\u6548", - "value_template_invalid": "\u6578\u503c\u6a21\u677f\u7121\u6548" + "query_invalid": "SQL \u67e5\u8a62\u7121\u6548" }, "step": { "user": { @@ -32,8 +31,7 @@ "options": { "error": { "db_url_invalid": "\u8cc7\u6599\u5eab URL \u7121\u6548", - "query_invalid": "SQL \u67e5\u8a62\u7121\u6548", - "value_template_invalid": "\u6578\u503c\u6a21\u677f\u7121\u6548" + "query_invalid": "SQL \u67e5\u8a62\u7121\u6548" }, "step": { "init": { diff --git a/homeassistant/components/steam_online/translations/ko.json b/homeassistant/components/steam_online/translations/ko.json new file mode 100644 index 00000000000..d106b141eb2 --- /dev/null +++ b/homeassistant/components/steam_online/translations/ko.json @@ -0,0 +1,36 @@ +{ + "config": { + "abort": { + "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_account": "\uc798\ubabb\ub41c \uacc4\uc815 ID", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "description": "Steam \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c\ud569\ub2c8\ub2e4.\n\n\uc5ec\uae30\uc5d0\uc11c \ud0a4\ub97c \ucc3e\uc744 \uc218 \uc788\uc2b5\ub2c8\ub2e4: {api_key_url}", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "account": "Steam \uacc4\uc815 ID", + "api_key": "API \ud0a4" + }, + "description": "{account_id_url} \uc5d0\uc11c Steam \uacc4\uc815 ID\ub97c \ucc3e\uc73c\uc138\uc694." + } + } + }, + "options": { + "step": { + "init": { + "data": { + "accounts": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \uacc4\uc815 \uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sun/translations/ko.json b/homeassistant/components/sun/translations/ko.json index d9d6f6ff081..f918b879acf 100644 --- a/homeassistant/components/sun/translations/ko.json +++ b/homeassistant/components/sun/translations/ko.json @@ -1,4 +1,11 @@ { + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + }, "state": { "_": { "above_horizon": "\uc8fc\uac04", diff --git a/homeassistant/components/switch/translations/ca.json b/homeassistant/components/switch/translations/ca.json index 49667b9f60a..a029be79d38 100644 --- a/homeassistant/components/switch/translations/ca.json +++ b/homeassistant/components/switch/translations/ca.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entitat d'interruptor" - }, - "description": "Selecciona l'interruptor del llum." - } - } - }, "device_automation": { "action_type": { "toggle": "Commuta {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} s'enc\u00e9n o s'apaga", - "toggled": "{entity_name} s'activa o es desactiva", "turned_off": "{entity_name} desactivat", "turned_on": "{entity_name} activat" } diff --git a/homeassistant/components/switch/translations/cs.json b/homeassistant/components/switch/translations/cs.json index a7b7f35033e..b82eaab9e4f 100644 --- a/homeassistant/components/switch/translations/cs.json +++ b/homeassistant/components/switch/translations/cs.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entita vyp\u00edna\u010de" - }, - "description": "Vyberte vyp\u00edna\u010d pro sv\u011btlo." - } - } - }, "device_automation": { "action_type": { "toggle": "P\u0159epnout {entity_name}", @@ -20,7 +10,6 @@ "is_on": "{entity_name} je zapnuto" }, "trigger_type": { - "toggled": "{entity_name} zapnuto nebo vypnuto", "turned_off": "{entity_name} bylo vypnuto", "turned_on": "{entity_name} bylo zapnuto" } diff --git a/homeassistant/components/switch/translations/de.json b/homeassistant/components/switch/translations/de.json index 12784fab206..886426619dc 100644 --- a/homeassistant/components/switch/translations/de.json +++ b/homeassistant/components/switch/translations/de.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Switch-Entit\u00e4t" - }, - "description": "W\u00e4hle den Schalter f\u00fcr den Lichtschalter aus." - } - } - }, "device_automation": { "action_type": { "toggle": "{entity_name} umschalten", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ein- oder ausgeschaltet", - "toggled": "{entity_name} ein- oder ausgeschaltet", "turned_off": "{entity_name} ausgeschaltet", "turned_on": "{entity_name} eingeschaltet" } diff --git a/homeassistant/components/switch/translations/el.json b/homeassistant/components/switch/translations/el.json index 986ee7cff92..280ddddcfed 100644 --- a/homeassistant/components/switch/translations/el.json +++ b/homeassistant/components/switch/translations/el.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03c6\u03ce\u03c4\u03c9\u03bd." - } - } - }, "device_automation": { "action_type": { "toggle": "\u0395\u03bd\u03b1\u03bb\u03bb\u03b1\u03b3\u03ae {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "\u03a4\u03bf {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", - "toggled": "To {entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5 \u03ae \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_off": "{entity_name} \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5", "turned_on": "{entity_name} \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03ae\u03b8\u03b7\u03ba\u03b5" } diff --git a/homeassistant/components/switch/translations/en.json b/homeassistant/components/switch/translations/en.json index ae04ed42520..1e4fe8e4ad8 100644 --- a/homeassistant/components/switch/translations/en.json +++ b/homeassistant/components/switch/translations/en.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Switch entity" - }, - "description": "Select the switch for the light switch." - } - } - }, "device_automation": { "action_type": { "toggle": "Toggle {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} turned on or off", - "toggled": "{entity_name} turned on or off", "turned_off": "{entity_name} turned off", "turned_on": "{entity_name} turned on" } diff --git a/homeassistant/components/switch/translations/es.json b/homeassistant/components/switch/translations/es.json index 2c1f6050ce6..e6190f32c8b 100644 --- a/homeassistant/components/switch/translations/es.json +++ b/homeassistant/components/switch/translations/es.json @@ -1,11 +1,4 @@ { - "config": { - "step": { - "init": { - "description": "Seleccione el interruptor de la l\u00e1mpara." - } - } - }, "device_automation": { "action_type": { "toggle": "Alternar {entity_name}", @@ -18,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} activado o desactivado", - "toggled": "{entity_name} activado o desactivado", "turned_off": "{entity_name} apagado", "turned_on": "{entity_name} encendido" } diff --git a/homeassistant/components/switch/translations/et.json b/homeassistant/components/switch/translations/et.json index e9c9928c89d..aaf1da62b5c 100644 --- a/homeassistant/components/switch/translations/et.json +++ b/homeassistant/components/switch/translations/et.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "L\u00fcliti olem" - }, - "description": "Vali valgusti l\u00fcliti" - } - } - }, "device_automation": { "action_type": { "toggle": "Muuda {entity_name} olekut", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", - "toggled": "{entity_name} l\u00fclitus sisse v\u00f5i v\u00e4lja", "turned_off": "{entity_name} l\u00fclitus v\u00e4lja", "turned_on": "{entity_name} l\u00fclitus sisse" } diff --git a/homeassistant/components/switch/translations/fr.json b/homeassistant/components/switch/translations/fr.json index ec357e10c72..bbe40ea7732 100644 --- a/homeassistant/components/switch/translations/fr.json +++ b/homeassistant/components/switch/translations/fr.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entit\u00e9 du commutateur" - }, - "description": "S\u00e9lectionnez le commutateur correspondant \u00e0 l'interrupteur d'\u00e9clairage." - } - } - }, "device_automation": { "action_type": { "toggle": "Basculer {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a \u00e9t\u00e9 activ\u00e9 ou d\u00e9sactiv\u00e9", - "toggled": "{entity_name} a \u00e9t\u00e9 activ\u00e9 ou d\u00e9sactiv\u00e9", "turned_off": "{entity_name} a \u00e9t\u00e9 d\u00e9sactiv\u00e9", "turned_on": "{entity_name} a \u00e9t\u00e9 activ\u00e9" } diff --git a/homeassistant/components/switch/translations/he.json b/homeassistant/components/switch/translations/he.json index c3d76bb6f9f..991f45744ca 100644 --- a/homeassistant/components/switch/translations/he.json +++ b/homeassistant/components/switch/translations/he.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05d2" - }, - "description": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05de\u05ea\u05d2 \u05e2\u05d1\u05d5\u05e8 \u05de\u05ea\u05d2 \u05d4\u05d0\u05d5\u05e8." - } - } - }, "device_automation": { "action_type": { "toggle": "\u05d4\u05d7\u05dc\u05e4\u05ea \u05de\u05e6\u05d1 {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", - "toggled": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05db\u05d5\u05d1\u05d4", "turned_off": "{entity_name} \u05db\u05d5\u05d1\u05d4", "turned_on": "{entity_name} \u05d4\u05d5\u05e4\u05e2\u05dc" } diff --git a/homeassistant/components/switch/translations/hu.json b/homeassistant/components/switch/translations/hu.json index 2dff00040ca..86d77afaa43 100644 --- a/homeassistant/components/switch/translations/hu.json +++ b/homeassistant/components/switch/translations/hu.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Kapcsol\u00f3 entit\u00e1s" - }, - "description": "V\u00e1lassza ki a kapcsol\u00f3t a vil\u00e1g\u00edt\u00e1shoz." - } - } - }, "device_automation": { "action_type": { "toggle": "{entity_name} kapcsol\u00e1sa", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} be- vagy kikapcsolt", - "toggled": "{entity_name} \u00e1tkapcsolt", "turned_off": "{entity_name} ki lett kapcsolva", "turned_on": "{entity_name} be lett kapcsolva" } diff --git a/homeassistant/components/switch/translations/id.json b/homeassistant/components/switch/translations/id.json index c49cab13a9c..589985509d0 100644 --- a/homeassistant/components/switch/translations/id.json +++ b/homeassistant/components/switch/translations/id.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entitas saklar" - }, - "description": "Pilih saklar mana untuk saklar lampu." - } - } - }, "device_automation": { "action_type": { "toggle": "Nyala/matikan {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} diaktifkan atau dinonaktifkan", - "toggled": "{entity_name} diaktifkan atau dinonaktifkan", "turned_off": "{entity_name} dimatikan", "turned_on": "{entity_name} dinyalakan" } diff --git a/homeassistant/components/switch/translations/it.json b/homeassistant/components/switch/translations/it.json index b71a46e48be..6168b6bdf4e 100644 --- a/homeassistant/components/switch/translations/it.json +++ b/homeassistant/components/switch/translations/it.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entit\u00e0 dell'interruttore" - }, - "description": "Seleziona l'interruttore per l'interruttore della luce." - } - } - }, "device_automation": { "action_type": { "toggle": "Attiva/Disattiva {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} attivata o disattivata", - "toggled": "{entity_name} attiva o disattiva", "turned_off": "{entity_name} disattivato", "turned_on": "{entity_name} attivato" } diff --git a/homeassistant/components/switch/translations/ja.json b/homeassistant/components/switch/translations/ja.json index d4e335257ad..071f47dbd58 100644 --- a/homeassistant/components/switch/translations/ja.json +++ b/homeassistant/components/switch/translations/ja.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "\u30b9\u30a4\u30c3\u30c1\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3" - }, - "description": "\u7167\u660e\u30b9\u30a4\u30c3\u30c1\u306e\u30b9\u30a4\u30c3\u30c1\u3092\u9078\u629e\u3057\u307e\u3059\u3002" - } - } - }, "device_automation": { "action_type": { "toggle": "\u30c8\u30b0\u30eb {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", - "toggled": "{entity_name} \u304c\u30aa\u30f3\u307e\u305f\u306f\u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_off": "{entity_name} \u30aa\u30d5\u306b\u306a\u308a\u307e\u3057\u305f", "turned_on": "{entity_name} \u30aa\u30f3\u306b\u306a\u3063\u3066\u3044\u307e\u3059" } diff --git a/homeassistant/components/switch/translations/nl.json b/homeassistant/components/switch/translations/nl.json index 87636c8a1f1..28398d9d8a6 100644 --- a/homeassistant/components/switch/translations/nl.json +++ b/homeassistant/components/switch/translations/nl.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entiteit wijzigen" - }, - "description": "Kies de schakelaar voor de lichtschakelaar." - } - } - }, "device_automation": { "action_type": { "toggle": "Omschakelen {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", - "toggled": "{entity_name} in-of uitgeschakeld", "turned_off": "{entity_name} uitgeschakeld", "turned_on": "{entity_name} ingeschakeld" } diff --git a/homeassistant/components/switch/translations/no.json b/homeassistant/components/switch/translations/no.json index 802df4e6a42..964723c68b6 100644 --- a/homeassistant/components/switch/translations/no.json +++ b/homeassistant/components/switch/translations/no.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Bytt enhet" - }, - "description": "Velg bryteren for lysbryteren." - } - } - }, "device_automation": { "action_type": { "toggle": "Veksle {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} sl\u00e5tt p\u00e5 eller av", - "toggled": "{entity_name} sl\u00e5tt p\u00e5 eller av", "turned_off": "{entity_name} sl\u00e5tt av", "turned_on": "{entity_name} sl\u00e5tt p\u00e5" } diff --git a/homeassistant/components/switch/translations/pl.json b/homeassistant/components/switch/translations/pl.json index 51ecfc44c5d..3d87d8572df 100644 --- a/homeassistant/components/switch/translations/pl.json +++ b/homeassistant/components/switch/translations/pl.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Encja prze\u0142\u0105cznika" - }, - "description": "Wybierz prze\u0142\u0105cznik do w\u0142\u0105cznika \u015bwiat\u0142a." - } - } - }, "device_automation": { "action_type": { "toggle": "prze\u0142\u0105cz {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", - "toggled": "{entity_name} zostanie w\u0142\u0105czony lub wy\u0142\u0105czony", "turned_off": "nast\u0105pi wy\u0142\u0105czenie {entity_name}", "turned_on": "nast\u0105pi w\u0142\u0105czenie {entity_name}" } diff --git a/homeassistant/components/switch/translations/pt-BR.json b/homeassistant/components/switch/translations/pt-BR.json index 0d24cd74bfe..d2c2c3be5a0 100644 --- a/homeassistant/components/switch/translations/pt-BR.json +++ b/homeassistant/components/switch/translations/pt-BR.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Entidade de interruptor" - }, - "description": "Selecione o interruptor para a l\u00e2mpada." - } - } - }, "device_automation": { "action_type": { "toggle": "Alternar {entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} ligado ou desligado", - "toggled": "{entity_name} ligado ou desligado", "turned_off": "{entity_name} desligado", "turned_on": "{entity_name} ligado" } diff --git a/homeassistant/components/switch/translations/ru.json b/homeassistant/components/switch/translations/ru.json index 2602a1ac20b..a1483e071e8 100644 --- a/homeassistant/components/switch/translations/ru.json +++ b/homeassistant/components/switch/translations/ru.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u043a\u0430\u043a \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u044f." - } - } - }, "device_automation": { "action_type": { "toggle": "{entity_name}: \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", - "toggled": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_off": "{entity_name} \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f", "turned_on": "{entity_name} \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f" } diff --git a/homeassistant/components/switch/translations/sv.json b/homeassistant/components/switch/translations/sv.json index 2db2a2718e0..a2cd74434eb 100644 --- a/homeassistant/components/switch/translations/sv.json +++ b/homeassistant/components/switch/translations/sv.json @@ -10,7 +10,6 @@ "is_on": "{entity_name} \u00e4r p\u00e5" }, "trigger_type": { - "toggled": "{entity_name} slogs p\u00e5 eller av", "turned_off": "{entity_name} st\u00e4ngdes av", "turned_on": "{entity_name} slogs p\u00e5" } diff --git a/homeassistant/components/switch/translations/tr.json b/homeassistant/components/switch/translations/tr.json index 2a98d2e972f..8387983017f 100644 --- a/homeassistant/components/switch/translations/tr.json +++ b/homeassistant/components/switch/translations/tr.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "Varl\u0131\u011f\u0131 de\u011fi\u015ftir" - }, - "description": "I\u015f\u0131k anahtar\u0131 i\u00e7in anahtar\u0131 se\u00e7in." - } - } - }, "device_automation": { "action_type": { "toggle": "{entity_name} de\u011fi\u015ftir", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", - "toggled": "{entity_name} a\u00e7\u0131ld\u0131 veya kapat\u0131ld\u0131", "turned_off": "{entity_name} kapat\u0131ld\u0131", "turned_on": "{entity_name} a\u00e7\u0131ld\u0131" } diff --git a/homeassistant/components/switch/translations/zh-Hans.json b/homeassistant/components/switch/translations/zh-Hans.json index 822ca2795df..afe9f58db8f 100644 --- a/homeassistant/components/switch/translations/zh-Hans.json +++ b/homeassistant/components/switch/translations/zh-Hans.json @@ -10,7 +10,6 @@ "is_on": "{entity_name} \u5df2\u6253\u5f00" }, "trigger_type": { - "toggled": "{entity_name} \u88ab\u6253\u5f00\u6216\u5173\u95ed", "turned_off": "{entity_name} \u88ab\u5173\u95ed", "turned_on": "{entity_name} \u88ab\u6253\u5f00" } diff --git a/homeassistant/components/switch/translations/zh-Hant.json b/homeassistant/components/switch/translations/zh-Hant.json index 631db326ae0..a1f38544f67 100644 --- a/homeassistant/components/switch/translations/zh-Hant.json +++ b/homeassistant/components/switch/translations/zh-Hant.json @@ -1,14 +1,4 @@ { - "config": { - "step": { - "init": { - "data": { - "entity_id": "\u958b\u95dc\u5be6\u9ad4" - }, - "description": "\u9078\u64c7\u6307\u5b9a\u70ba\u71c8\u5149\u4e4b\u958b\u95dc\u3002" - } - } - }, "device_automation": { "action_type": { "toggle": "\u5207\u63db{entity_name}", @@ -21,7 +11,6 @@ }, "trigger_type": { "changed_states": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", - "toggled": "{entity_name}\u5df2\u958b\u555f\u6216\u95dc\u9589", "turned_off": "{entity_name}\u5df2\u95dc\u9589", "turned_on": "{entity_name}\u5df2\u958b\u555f" } diff --git a/homeassistant/components/switch_as_x/translations/ca.json b/homeassistant/components/switch_as_x/translations/ca.json index f48a744066c..ea568634dc1 100644 --- a/homeassistant/components/switch_as_x/translations/ca.json +++ b/homeassistant/components/switch_as_x/translations/ca.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entitat d'interruptor" - } - }, "user": { "data": { "entity_id": "Commutador", "target_domain": "Nou tipus" }, - "description": "Tria un commutador que vulguis que aparegui a Home Assistant com a llum, coberta o qualsevol altra cosa. El commutador original s'amagar\u00e0.", - "title": "Canvia el tipus de dispositiu commutador" + "description": "Tria un commutador que vulguis que aparegui a Home Assistant com a llum, coberta o qualsevol altra cosa. El commutador original s'amagar\u00e0." } } }, diff --git a/homeassistant/components/switch_as_x/translations/cs.json b/homeassistant/components/switch_as_x/translations/cs.json index 11f8471a62a..c521a79e1d9 100644 --- a/homeassistant/components/switch_as_x/translations/cs.json +++ b/homeassistant/components/switch_as_x/translations/cs.json @@ -1,16 +1,10 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entita vyp\u00edna\u010de" - } - }, "user": { "data": { "target_domain": "Typ" - }, - "title": "Ud\u011blejte vyp\u00edna\u010dem ..." + } } } }, diff --git a/homeassistant/components/switch_as_x/translations/de.json b/homeassistant/components/switch_as_x/translations/de.json index 1125f4f5a8f..6278ddc9988 100644 --- a/homeassistant/components/switch_as_x/translations/de.json +++ b/homeassistant/components/switch_as_x/translations/de.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Switch-Entit\u00e4t" - } - }, "user": { "data": { "entity_id": "Schalter", "target_domain": "Neuer Typ" }, - "description": "W\u00e4hle einen Schalter, der im Home Assistant als Licht, Abdeckung oder sonstiges angezeigt werden soll. Der urspr\u00fcngliche Schalter wird ausgeblendet.", - "title": "Switch-Ger\u00e4tetyp \u00e4ndern" + "description": "W\u00e4hle einen Schalter, der im Home Assistant als Licht, Abdeckung oder sonstiges angezeigt werden soll. Der urspr\u00fcngliche Schalter wird ausgeblendet." } } }, diff --git a/homeassistant/components/switch_as_x/translations/el.json b/homeassistant/components/switch_as_x/translations/el.json index ed10f241927..cf5925abc35 100644 --- a/homeassistant/components/switch_as_x/translations/el.json +++ b/homeassistant/components/switch_as_x/translations/el.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "\u039f\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7" - } - }, "user": { "data": { "entity_id": "\u0394\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2", "target_domain": "\u03a4\u03cd\u03c0\u03bf\u03c2" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf Home Assistant \u03c9\u03c2 \u03c6\u03c9\u03c2, \u03ba\u03ac\u03bb\u03c5\u03bc\u03bc\u03b1 \u03ae \u03bf\u03c4\u03b9\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03ac\u03bb\u03bb\u03bf. \u039f \u03b1\u03c1\u03c7\u03b9\u03ba\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2 \u03b8\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03c1\u03c5\u03bc\u03bc\u03ad\u03bd\u03bf\u03c2.", - "title": "\u039a\u03ac\u03bd\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03ad\u03bd\u03b1 ..." + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf Home Assistant \u03c9\u03c2 \u03c6\u03c9\u03c2, \u03ba\u03ac\u03bb\u03c5\u03bc\u03bc\u03b1 \u03ae \u03bf\u03c4\u03b9\u03b4\u03ae\u03c0\u03bf\u03c4\u03b5 \u03ac\u03bb\u03bb\u03bf. \u039f \u03b1\u03c1\u03c7\u03b9\u03ba\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03cc\u03c0\u03c4\u03b7\u03c2 \u03b8\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03ba\u03c1\u03c5\u03bc\u03bc\u03ad\u03bd\u03bf\u03c2." } } }, diff --git a/homeassistant/components/switch_as_x/translations/en.json b/homeassistant/components/switch_as_x/translations/en.json index 4253f0506ef..7709a27cf35 100644 --- a/homeassistant/components/switch_as_x/translations/en.json +++ b/homeassistant/components/switch_as_x/translations/en.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Switch entity" - } - }, "user": { "data": { "entity_id": "Switch", "target_domain": "New Type" }, - "description": "Pick a switch that you want to show up in Home Assistant as a light, cover or anything else. The original switch will be hidden.", - "title": "Change switch device type" + "description": "Pick a switch that you want to show up in Home Assistant as a light, cover or anything else. The original switch will be hidden." } } }, diff --git a/homeassistant/components/switch_as_x/translations/et.json b/homeassistant/components/switch_as_x/translations/et.json index 9d5a5839fbf..bb3ee78e75c 100644 --- a/homeassistant/components/switch_as_x/translations/et.json +++ b/homeassistant/components/switch_as_x/translations/et.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "L\u00fcliti olem" - } - }, "user": { "data": { "entity_id": "L\u00fcliti", "target_domain": "Uus t\u00fc\u00fcp" }, - "description": "Vali l\u00fcliti mida soovid Home Assistantis valgustina, avakattena v\u00f5i millegi muuna kuvada. Algne l\u00fcliti peidetakse.", - "title": "Muuda l\u00fclitusseadme t\u00fc\u00fcpi" + "description": "Vali l\u00fcliti mida soovid Home Assistantis valgustina, avakattena v\u00f5i millegi muuna kuvada. Algne l\u00fcliti peidetakse." } } }, diff --git a/homeassistant/components/switch_as_x/translations/fr.json b/homeassistant/components/switch_as_x/translations/fr.json index fa14bd80885..484881e222a 100644 --- a/homeassistant/components/switch_as_x/translations/fr.json +++ b/homeassistant/components/switch_as_x/translations/fr.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entit\u00e9 du commutateur" - } - }, "user": { "data": { "entity_id": "Interrupteur", "target_domain": "Nouveau type" }, - "description": "Choisissez un interrupteur que vous voulez faire appara\u00eetre dans Home Assistant comme une lumi\u00e8re, une fermeture ou autre. L'interrupteur original sera cach\u00e9.", - "title": "Modifier le type d'appareil de l'interrupteur" + "description": "Choisissez un interrupteur que vous voulez faire appara\u00eetre dans Home Assistant comme une lumi\u00e8re, une fermeture ou autre. L'interrupteur original sera cach\u00e9." } } }, diff --git a/homeassistant/components/switch_as_x/translations/he.json b/homeassistant/components/switch_as_x/translations/he.json deleted file mode 100644 index 39889dab709..00000000000 --- a/homeassistant/components/switch_as_x/translations/he.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "config": { - "user": { - "entity_id": "\u05d9\u05e9\u05d5\u05ea \u05de\u05ea\u05d2" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/hu.json b/homeassistant/components/switch_as_x/translations/hu.json index 0d919f5ecd3..9f3d49ec348 100644 --- a/homeassistant/components/switch_as_x/translations/hu.json +++ b/homeassistant/components/switch_as_x/translations/hu.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Kapcsol\u00f3 entit\u00e1s" - } - }, "user": { "data": { "entity_id": "Kapcsol\u00f3", "target_domain": "\u00daj t\u00edpus" }, - "description": "V\u00e1lassza ki azt a kapcsol\u00f3t, amelyet meg szeretne jelen\u00edteni a Home Assistantban l\u00e1mpak\u00e9nt, red\u0151nyk\u00e9nt vagy b\u00e1rmi m\u00e1sk\u00e9nt. Az eredeti kapcsol\u00f3 el lesz rejtve.", - "title": "Kapcsol\u00f3 eszk\u00f6zt\u00edpus m\u00f3dos\u00edt\u00e1sa" + "description": "V\u00e1lassza ki azt a kapcsol\u00f3t, amelyet meg szeretne jelen\u00edteni a Home Assistantban l\u00e1mpak\u00e9nt, red\u0151nyk\u00e9nt vagy b\u00e1rmi m\u00e1sk\u00e9nt. Az eredeti kapcsol\u00f3 el lesz rejtve." } } }, diff --git a/homeassistant/components/switch_as_x/translations/id.json b/homeassistant/components/switch_as_x/translations/id.json index 47d31e26c03..e8e86db884f 100644 --- a/homeassistant/components/switch_as_x/translations/id.json +++ b/homeassistant/components/switch_as_x/translations/id.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entitas saklar" - } - }, "user": { "data": { "entity_id": "Sakelar", "target_domain": "Tipe Baru" }, - "description": "Pilih sakelar yang ingin Anda tampilkan di Home Assistant sebagai lampu, penutup, atau apa pun. Sakelar asli akan disembunyikan.", - "title": "Ubah jenis perangkat sakelar" + "description": "Pilih sakelar yang ingin Anda tampilkan di Home Assistant sebagai lampu, penutup, atau apa pun. Sakelar asli akan disembunyikan." } } }, diff --git a/homeassistant/components/switch_as_x/translations/it.json b/homeassistant/components/switch_as_x/translations/it.json index 4706b4d9ece..9d7f3c798bf 100644 --- a/homeassistant/components/switch_as_x/translations/it.json +++ b/homeassistant/components/switch_as_x/translations/it.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Cambia entit\u00e0" - } - }, "user": { "data": { "entity_id": "Interruttore", "target_domain": "Nuovo tipo" }, - "description": "Scegli un interruttore che vuoi mostrare in Home Assistant come luce, copertura o qualsiasi altra cosa. L'interruttore originale sar\u00e0 nascosto.", - "title": "Cambia tipo di dispositivo dell'interruttore" + "description": "Scegli un interruttore che vuoi mostrare in Home Assistant come luce, copertura o qualsiasi altra cosa. L'interruttore originale sar\u00e0 nascosto." } } }, diff --git a/homeassistant/components/switch_as_x/translations/ja.json b/homeassistant/components/switch_as_x/translations/ja.json index 434e1b588d5..41af80fa2b8 100644 --- a/homeassistant/components/switch_as_x/translations/ja.json +++ b/homeassistant/components/switch_as_x/translations/ja.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "\u30b9\u30a4\u30c3\u30c1\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3" - } - }, "user": { "data": { "entity_id": "\u30b9\u30a4\u30c3\u30c1", "target_domain": "\u30bf\u30a4\u30d7" }, - "description": "Home Assistant\u306b\u3001\u30e9\u30a4\u30c8\u3084\u30ab\u30d0\u30fc\u306a\u3069\u306e\u8868\u793a\u3055\u305b\u305f\u3044\u30b9\u30a4\u30c3\u30c1\u3092\u9078\u3073\u307e\u3059\u3002\u5143\u306e\u30b9\u30a4\u30c3\u30c1\u306f\u975e\u8868\u793a\u306b\u306a\u308a\u307e\u3059\u3002", - "title": "\u30b9\u30a4\u30c3\u30c1\u3092..." + "description": "Home Assistant\u306b\u3001\u30e9\u30a4\u30c8\u3084\u30ab\u30d0\u30fc\u306a\u3069\u306e\u8868\u793a\u3055\u305b\u305f\u3044\u30b9\u30a4\u30c3\u30c1\u3092\u9078\u3073\u307e\u3059\u3002\u5143\u306e\u30b9\u30a4\u30c3\u30c1\u306f\u975e\u8868\u793a\u306b\u306a\u308a\u307e\u3059\u3002" } } }, diff --git a/homeassistant/components/switch_as_x/translations/ko.json b/homeassistant/components/switch_as_x/translations/ko.json new file mode 100644 index 00000000000..bd0b909895d --- /dev/null +++ b/homeassistant/components/switch_as_x/translations/ko.json @@ -0,0 +1,14 @@ +{ + "config": { + "step": { + "user": { + "data": { + "entity_id": "\uc2a4\uc704\uce58", + "target_domain": "\uc0c8\ub85c\uc6b4 \uc720\ud615" + }, + "description": "Home Assistant\uc5d0\uc11c \uc870\uba85, \ucee4\ubc84 \ub610\ub294 \ub2e4\ub978 \uac83\uc73c\ub85c \ud45c\uc2dc\ud558\ub824\ub294 \uc2a4\uc704\uce58\ub97c \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624. \uc6d0\ub798 \uc2a4\uc704\uce58\ub294 \uc228\uaca8\uc9d1\ub2c8\ub2e4." + } + } + }, + "title": "\uc2a4\uc704\uce58\uc758 \uc7a5\uce58 \uc720\ud615 \ubcc0\uacbd" +} \ No newline at end of file diff --git a/homeassistant/components/switch_as_x/translations/nl.json b/homeassistant/components/switch_as_x/translations/nl.json index 4fcea818b9a..2a6043fbe75 100644 --- a/homeassistant/components/switch_as_x/translations/nl.json +++ b/homeassistant/components/switch_as_x/translations/nl.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entiteit wijzigen" - } - }, "user": { "data": { "entity_id": "Schakelaar", "target_domain": "Nieuw type" }, - "description": "Kies een schakelaar die u in Home Assistant wilt laten verschijnen als licht, klep of iets anders. De oorspronkelijke schakelaar wordt verborgen.", - "title": "Type schakelapparaat wijzigen" + "description": "Kies een schakelaar die u in Home Assistant wilt laten verschijnen als licht, klep of iets anders. De oorspronkelijke schakelaar wordt verborgen." } } }, diff --git a/homeassistant/components/switch_as_x/translations/no.json b/homeassistant/components/switch_as_x/translations/no.json index 5958c87f201..6c72f96873e 100644 --- a/homeassistant/components/switch_as_x/translations/no.json +++ b/homeassistant/components/switch_as_x/translations/no.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Bytt enhet" - } - }, "user": { "data": { "entity_id": "Bryter", "target_domain": "Ny type" }, - "description": "Velg en bryter du vil vise i Home Assistant som lys, deksel eller noe annet. Den opprinnelige bryteren vil v\u00e6re skjult.", - "title": "Endre bryterenhetstype" + "description": "Velg en bryter du vil vise i Home Assistant som lys, deksel eller noe annet. Den opprinnelige bryteren vil v\u00e6re skjult." } } }, diff --git a/homeassistant/components/switch_as_x/translations/pl.json b/homeassistant/components/switch_as_x/translations/pl.json index 53a81fec1a5..92e642e746e 100644 --- a/homeassistant/components/switch_as_x/translations/pl.json +++ b/homeassistant/components/switch_as_x/translations/pl.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Encja prze\u0142\u0105cznika" - } - }, "user": { "data": { "entity_id": "Prze\u0142\u0105cznik", "target_domain": "Nowy rodzaj" }, - "description": "Wybierz prze\u0142\u0105cznik, kt\u00f3ry chcesz pokaza\u0107 w Home Assistant jako \u015bwiat\u0142o, rolet\u0119 lub cokolwiek innego. Oryginalny prze\u0142\u0105cznik zostanie ukryty.", - "title": "Zmiana typu urz\u0105dzenia prze\u0142\u0105cznika" + "description": "Wybierz prze\u0142\u0105cznik, kt\u00f3ry chcesz pokaza\u0107 w Home Assistant jako \u015bwiat\u0142o, rolet\u0119 lub cokolwiek innego. Oryginalny prze\u0142\u0105cznik zostanie ukryty." } } }, diff --git a/homeassistant/components/switch_as_x/translations/pt-BR.json b/homeassistant/components/switch_as_x/translations/pt-BR.json index bf8bc276781..436a99a17f9 100644 --- a/homeassistant/components/switch_as_x/translations/pt-BR.json +++ b/homeassistant/components/switch_as_x/translations/pt-BR.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Entidade de interruptor" - } - }, "user": { "data": { "entity_id": "Interruptor", "target_domain": "Novo tipo" }, - "description": "Escolha um interruptor que voc\u00ea deseja que apare\u00e7a no Home Assistant como luz, cortina ou qualquer outra coisa. A op\u00e7\u00e3o original ficar\u00e1 oculta.", - "title": "Alterar o tipo de dispositivo do interruptor" + "description": "Escolha um interruptor que voc\u00ea deseja que apare\u00e7a no Home Assistant como luz, cortina ou qualquer outra coisa. A op\u00e7\u00e3o original ficar\u00e1 oculta." } } }, diff --git a/homeassistant/components/switch_as_x/translations/ru.json b/homeassistant/components/switch_as_x/translations/ru.json index 3074365dd76..731a5c31c3c 100644 --- a/homeassistant/components/switch_as_x/translations/ru.json +++ b/homeassistant/components/switch_as_x/translations/ru.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c" - } - }, "user": { "data": { "entity_id": "\u0412\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c", "target_domain": "\u041d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432 Home Assistant \u043a\u0430\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435, \u0448\u0442\u043e\u0440\u044b \u0438\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0451. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0440\u044b\u0442.", - "title": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u0438\u043f \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044f" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432 Home Assistant \u043a\u0430\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435, \u0448\u0442\u043e\u0440\u044b \u0438\u043b\u0438 \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0451. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u0441\u043a\u0440\u044b\u0442." } } }, diff --git a/homeassistant/components/switch_as_x/translations/sv.json b/homeassistant/components/switch_as_x/translations/sv.json index 96419c39bf1..d21eefa5a1a 100644 --- a/homeassistant/components/switch_as_x/translations/sv.json +++ b/homeassistant/components/switch_as_x/translations/sv.json @@ -1,16 +1,10 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Kontakt-entitet" - } - }, "user": { "data": { "target_domain": "Typ" - }, - "title": "G\u00f6r en kontakt till ..." + } } } }, diff --git a/homeassistant/components/switch_as_x/translations/tr.json b/homeassistant/components/switch_as_x/translations/tr.json index b793be6baf0..39fd95784a4 100644 --- a/homeassistant/components/switch_as_x/translations/tr.json +++ b/homeassistant/components/switch_as_x/translations/tr.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "Varl\u0131\u011f\u0131 de\u011fi\u015ftir" - } - }, "user": { "data": { "entity_id": "Anahtar", "target_domain": "Yeni T\u00fcr" }, - "description": "Home Assistant'ta \u0131\u015f\u0131k, \u00f6rt\u00fc veya ba\u015fka bir \u015fey olarak g\u00f6r\u00fcnmesini istedi\u011finiz bir anahtar se\u00e7in. Orijinal anahtar gizlenecektir.", - "title": "Anahtar cihaz t\u00fcr\u00fcn\u00fc de\u011fi\u015ftir" + "description": "Home Assistant'ta \u0131\u015f\u0131k, \u00f6rt\u00fc veya ba\u015fka bir \u015fey olarak g\u00f6r\u00fcnmesini istedi\u011finiz bir anahtar se\u00e7in. Orijinal anahtar gizlenecektir." } } }, diff --git a/homeassistant/components/switch_as_x/translations/zh-Hans.json b/homeassistant/components/switch_as_x/translations/zh-Hans.json index e765a436849..244fab0c4e9 100644 --- a/homeassistant/components/switch_as_x/translations/zh-Hans.json +++ b/homeassistant/components/switch_as_x/translations/zh-Hans.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "\u5f00\u5173\u5b9e\u4f53" - } - }, "user": { "data": { "entity_id": "\u5f00\u5173", "target_domain": "\u65b0\u7c7b\u578b" }, - "description": "\u9009\u62e9\u4e00\u4e2a\u5f00\u5173\uff0c\u8ba9\u5b83\u5728 Home Assistant \u4e2d\u663e\u793a\u4e3a\u706f\u3001\u5377\u5e18\u7b49\u5404\u79cd\u7c7b\u578b\u3002\u539f\u6765\u7684\u5f00\u5173\u5c06\u88ab\u9690\u85cf\u3002", - "title": "\u66f4\u6539\u5f00\u5173\u7684\u8bbe\u5907\u7c7b\u578b" + "description": "\u9009\u62e9\u4e00\u4e2a\u5f00\u5173\uff0c\u8ba9\u5b83\u5728 Home Assistant \u4e2d\u663e\u793a\u4e3a\u706f\u3001\u5377\u5e18\u7b49\u5404\u79cd\u7c7b\u578b\u3002\u539f\u6765\u7684\u5f00\u5173\u5c06\u88ab\u9690\u85cf\u3002" } } }, diff --git a/homeassistant/components/switch_as_x/translations/zh-Hant.json b/homeassistant/components/switch_as_x/translations/zh-Hant.json index bd6a1e15ba0..17813905e2a 100644 --- a/homeassistant/components/switch_as_x/translations/zh-Hant.json +++ b/homeassistant/components/switch_as_x/translations/zh-Hant.json @@ -1,18 +1,12 @@ { "config": { "step": { - "config": { - "user": { - "entity_id": "\u958b\u95dc\u5be6\u9ad4" - } - }, "user": { "data": { "entity_id": "\u958b\u95dc", "target_domain": "\u65b0\u589e\u985e\u5225" }, - "description": "\u9078\u64c7\u6240\u8981\u65bc Home Assistant \u4e2d\u986f\u793a\u70ba\u71c8\u5149\u7684\u958b\u95dc\u3001\u7a97\u7c3e\u6216\u5176\u4ed6\u5be6\u9ad4\u3002\u539f\u59cb\u958b\u95dc\u5c07\u6703\u9032\u884c\u96b1\u85cf\u3002", - "title": "\u8b8a\u66f4\u958b\u95dc\u985e\u5225" + "description": "\u9078\u64c7\u6240\u8981\u65bc Home Assistant \u4e2d\u986f\u793a\u70ba\u71c8\u5149\u7684\u958b\u95dc\u3001\u7a97\u7c3e\u6216\u5176\u4ed6\u5be6\u9ad4\u3002\u539f\u59cb\u958b\u95dc\u5c07\u6703\u9032\u884c\u96b1\u85cf\u3002" } } }, diff --git a/homeassistant/components/switchbot/translations/bg.json b/homeassistant/components/switchbot/translations/bg.json index 5a5b74b2318..05b3a4459bb 100644 --- a/homeassistant/components/switchbot/translations/bg.json +++ b/homeassistant/components/switchbot/translations/bg.json @@ -6,9 +6,6 @@ "no_unconfigured_devices": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u043d\u0435\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430.", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, - "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/ca.json b/homeassistant/components/switchbot/translations/ca.json index 6409efcbab7..576aeda3b16 100644 --- a/homeassistant/components/switchbot/translations/ca.json +++ b/homeassistant/components/switchbot/translations/ca.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Tipus de Switchbot no compatible.", "unknown": "Error inesperat" }, - "error": { - "cannot_connect": "Ha fallat la connexi\u00f3" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/cs.json b/homeassistant/components/switchbot/translations/cs.json index 6f16b6faac5..f92951a0e15 100644 --- a/homeassistant/components/switchbot/translations/cs.json +++ b/homeassistant/components/switchbot/translations/cs.json @@ -5,9 +5,6 @@ "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, - "error": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/de.json b/homeassistant/components/switchbot/translations/de.json index f499712718e..439524c8aa6 100644 --- a/homeassistant/components/switchbot/translations/de.json +++ b/homeassistant/components/switchbot/translations/de.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Nicht unterst\u00fctzter Switchbot-Typ.", "unknown": "Unerwarteter Fehler" }, - "error": { - "cannot_connect": "Verbindung fehlgeschlagen" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/el.json b/homeassistant/components/switchbot/translations/el.json index fe3b7448ac8..b191c90f53c 100644 --- a/homeassistant/components/switchbot/translations/el.json +++ b/homeassistant/components/switchbot/translations/el.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "\u039c\u03b7 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03bf\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c2 Switchbot.", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, - "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/en.json b/homeassistant/components/switchbot/translations/en.json index 4ea3d21de65..1cfaee8750f 100644 --- a/homeassistant/components/switchbot/translations/en.json +++ b/homeassistant/components/switchbot/translations/en.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Unsupported Switchbot Type.", "unknown": "Unexpected error" }, - "error": { - "cannot_connect": "Failed to connect" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/es.json b/homeassistant/components/switchbot/translations/es.json index 324c7200244..6fc4d59f693 100644 --- a/homeassistant/components/switchbot/translations/es.json +++ b/homeassistant/components/switchbot/translations/es.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Tipo de Switchbot no compatible.", "unknown": "Error inesperado" }, - "error": { - "cannot_connect": "Fall\u00f3 al conectar" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/et.json b/homeassistant/components/switchbot/translations/et.json index cc746796195..358a4748724 100644 --- a/homeassistant/components/switchbot/translations/et.json +++ b/homeassistant/components/switchbot/translations/et.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Toetamata Switchboti t\u00fc\u00fcp.", "unknown": "Ootamatu t\u00f5rge" }, - "error": { - "cannot_connect": "\u00dchendamine nurjus" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/fr.json b/homeassistant/components/switchbot/translations/fr.json index aca51b4e4c4..75eff0a9b7c 100644 --- a/homeassistant/components/switchbot/translations/fr.json +++ b/homeassistant/components/switchbot/translations/fr.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Type Switchbot non pris en charge.", "unknown": "Erreur inattendue" }, - "error": { - "cannot_connect": "\u00c9chec de connexion" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/he.json b/homeassistant/components/switchbot/translations/he.json index 09f62069706..836cd8b06b4 100644 --- a/homeassistant/components/switchbot/translations/he.json +++ b/homeassistant/components/switchbot/translations/he.json @@ -5,9 +5,6 @@ "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" }, - "error": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/hu.json b/homeassistant/components/switchbot/translations/hu.json index 2b80fbedbd8..b870e577426 100644 --- a/homeassistant/components/switchbot/translations/hu.json +++ b/homeassistant/components/switchbot/translations/hu.json @@ -8,7 +8,6 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", "one": "\u00dcres", "other": "\u00dcres" }, diff --git a/homeassistant/components/switchbot/translations/id.json b/homeassistant/components/switchbot/translations/id.json index 4619f421811..f3a9cd169ef 100644 --- a/homeassistant/components/switchbot/translations/id.json +++ b/homeassistant/components/switchbot/translations/id.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Jenis Switchbot yang tidak didukung.", "unknown": "Kesalahan yang tidak diharapkan" }, - "error": { - "cannot_connect": "Gagal terhubung" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/it.json b/homeassistant/components/switchbot/translations/it.json index f589046d4db..b8997f9247b 100644 --- a/homeassistant/components/switchbot/translations/it.json +++ b/homeassistant/components/switchbot/translations/it.json @@ -8,7 +8,6 @@ "unknown": "Errore imprevisto" }, "error": { - "cannot_connect": "Impossibile connettersi", "one": "Vuoto", "other": "Vuoti" }, diff --git a/homeassistant/components/switchbot/translations/ja.json b/homeassistant/components/switchbot/translations/ja.json index 41fb320428f..91d87431774 100644 --- a/homeassistant/components/switchbot/translations/ja.json +++ b/homeassistant/components/switchbot/translations/ja.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u306a\u3044\u7a2e\u985e\u306eSwitchbot", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, - "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/nl.json b/homeassistant/components/switchbot/translations/nl.json index fb1e55f6b9d..becb7173194 100644 --- a/homeassistant/components/switchbot/translations/nl.json +++ b/homeassistant/components/switchbot/translations/nl.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Niet-ondersteund Switchbot-type.", "unknown": "Onverwachte fout" }, - "error": { - "cannot_connect": "Kan geen verbinding maken" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/no.json b/homeassistant/components/switchbot/translations/no.json index 4d8cb95061a..1d7836f6776 100644 --- a/homeassistant/components/switchbot/translations/no.json +++ b/homeassistant/components/switchbot/translations/no.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Switchbot-type st\u00f8ttes ikke.", "unknown": "Uventet feil" }, - "error": { - "cannot_connect": "Tilkobling mislyktes" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/pl.json b/homeassistant/components/switchbot/translations/pl.json index 15e64b86e5a..156ddbb9924 100644 --- a/homeassistant/components/switchbot/translations/pl.json +++ b/homeassistant/components/switchbot/translations/pl.json @@ -8,7 +8,6 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "few": "Puste", "many": "Pustych", "one": "Pusty", diff --git a/homeassistant/components/switchbot/translations/pt-BR.json b/homeassistant/components/switchbot/translations/pt-BR.json index bf9cb746dcb..3959425cbd3 100644 --- a/homeassistant/components/switchbot/translations/pt-BR.json +++ b/homeassistant/components/switchbot/translations/pt-BR.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "Tipo de Switchbot sem suporte.", "unknown": "Erro inesperado" }, - "error": { - "cannot_connect": "Falha ao conectar" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/ru.json b/homeassistant/components/switchbot/translations/ru.json index 5eaa1cdbc4f..9ca076ff499 100644 --- a/homeassistant/components/switchbot/translations/ru.json +++ b/homeassistant/components/switchbot/translations/ru.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "\u041d\u0435\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u0442\u0438\u043f Switchbot.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switchbot/translations/tr.json b/homeassistant/components/switchbot/translations/tr.json index 77a2921fa38..40b80dc4a3c 100644 --- a/homeassistant/components/switchbot/translations/tr.json +++ b/homeassistant/components/switchbot/translations/tr.json @@ -8,7 +8,6 @@ "unknown": "Beklenmeyen hata" }, "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", "one": "Bo\u015f", "other": "Bo\u015f" }, diff --git a/homeassistant/components/switchbot/translations/zh-Hant.json b/homeassistant/components/switchbot/translations/zh-Hant.json index 8e7b4495328..617129167ed 100644 --- a/homeassistant/components/switchbot/translations/zh-Hant.json +++ b/homeassistant/components/switchbot/translations/zh-Hant.json @@ -7,9 +7,6 @@ "switchbot_unsupported_type": "\u4e0d\u652f\u6301\u7684 Switchbot \u985e\u5225\u3002", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, - "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" - }, "flow_title": "{name}", "step": { "user": { diff --git a/homeassistant/components/switcher_kis/translations/ko.json b/homeassistant/components/switcher_kis/translations/ko.json new file mode 100644 index 00000000000..20ad990e862 --- /dev/null +++ b/homeassistant/components/switcher_kis/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/synology_dsm/translations/bg.json b/homeassistant/components/synology_dsm/translations/bg.json index acf35c4c2a4..a3a107a36e2 100644 --- a/homeassistant/components/synology_dsm/translations/bg.json +++ b/homeassistant/components/synology_dsm/translations/bg.json @@ -22,15 +22,7 @@ "port": "\u041f\u043e\u0440\u0442", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" }, - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - }, - "description": "\u041f\u0440\u0438\u0447\u0438\u043d\u0430: {details}" + "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name} ({host})?" }, "reauth_confirm": { "data": { @@ -44,8 +36,7 @@ "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "port": "\u041f\u043e\u0440\u0442", "username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/ca.json b/homeassistant/components/synology_dsm/translations/ca.json index bad95d30836..110e1cabdae 100644 --- a/homeassistant/components/synology_dsm/translations/ca.json +++ b/homeassistant/components/synology_dsm/translations/ca.json @@ -28,16 +28,7 @@ "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL" }, - "description": "Vols configurar {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Contrasenya", - "username": "Nom d'usuari" - }, - "description": "Motiu: {details}", - "title": "Reautenticaci\u00f3 de la integraci\u00f3 Synology DSM" + "description": "Vols configurar {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Utilitza un certificat SSL", "username": "Nom d'usuari", "verify_ssl": "Verifica el certificat SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/cs.json b/homeassistant/components/synology_dsm/translations/cs.json index ae77a93a790..dfa0a5fd347 100644 --- a/homeassistant/components/synology_dsm/translations/cs.json +++ b/homeassistant/components/synology_dsm/translations/cs.json @@ -26,15 +26,7 @@ "username": "U\u017eivatelsk\u00e9 jm\u00e9no", "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" }, - "description": "Chcete nastavit {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Heslo", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, - "title": "Synology DSM Znovu ov\u011b\u0159it integraci" + "description": "Chcete nastavit {name} ({host})?" }, "reauth_confirm": { "data": { @@ -50,8 +42,7 @@ "ssl": "Pou\u017e\u00edv\u00e1 SSL certifik\u00e1t", "username": "U\u017eivatelsk\u00e9 jm\u00e9no", "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/de.json b/homeassistant/components/synology_dsm/translations/de.json index 6b945c7bda2..247eba408c9 100644 --- a/homeassistant/components/synology_dsm/translations/de.json +++ b/homeassistant/components/synology_dsm/translations/de.json @@ -28,16 +28,7 @@ "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" }, - "description": "M\u00f6chtest du {name} ({host}) einrichten?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Passwort", - "username": "Benutzername" - }, - "description": "Grund: {details}", - "title": "Synology DSM Integration erneut authentifizieren" + "description": "M\u00f6chtest du {name} ({host}) einrichten?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Verwendet ein SSL-Zertifikat", "username": "Benutzername", "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/el.json b/homeassistant/components/synology_dsm/translations/el.json index 1cf10eb6175..fc4e0124056 100644 --- a/homeassistant/components/synology_dsm/translations/el.json +++ b/homeassistant/components/synology_dsm/translations/el.json @@ -28,16 +28,7 @@ "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" }, - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} ({host});", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "description": "\u0391\u03b9\u03c4\u03af\u03b1: {details}", - "title": "Synology DSM \u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} ({host});" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03ad\u03bd\u03b1 \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/en.json b/homeassistant/components/synology_dsm/translations/en.json index ce3b8083965..267eb772e13 100644 --- a/homeassistant/components/synology_dsm/translations/en.json +++ b/homeassistant/components/synology_dsm/translations/en.json @@ -28,16 +28,7 @@ "username": "Username", "verify_ssl": "Verify SSL certificate" }, - "description": "Do you want to setup {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Password", - "username": "Username" - }, - "description": "Reason: {details}", - "title": "Synology DSM Reauthenticate Integration" + "description": "Do you want to setup {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Uses an SSL certificate", "username": "Username", "verify_ssl": "Verify SSL certificate" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/es-419.json b/homeassistant/components/synology_dsm/translations/es-419.json index 2886a8ef624..8b319773410 100644 --- a/homeassistant/components/synology_dsm/translations/es-419.json +++ b/homeassistant/components/synology_dsm/translations/es-419.json @@ -23,8 +23,7 @@ "ssl": "Utilice SSL/TLS para conectarse a su NAS", "username": "Nombre de usuario" }, - "description": "\u00bfDesea configurar {name} ({host})?", - "title": "Synology DSM" + "description": "\u00bfDesea configurar {name} ({host})?" }, "user": { "data": { @@ -33,8 +32,7 @@ "port": "Puerto (opcional)", "ssl": "Utilice SSL/TLS para conectarse a su NAS", "username": "Nombre de usuario" - }, - "title": "Synology DSM" + } } } } diff --git a/homeassistant/components/synology_dsm/translations/es.json b/homeassistant/components/synology_dsm/translations/es.json index 382797dc81e..779996d7023 100644 --- a/homeassistant/components/synology_dsm/translations/es.json +++ b/homeassistant/components/synology_dsm/translations/es.json @@ -28,16 +28,7 @@ "username": "Usuario", "verify_ssl": "Verificar certificado SSL" }, - "description": "\u00bfQuieres configurar {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Contrase\u00f1a", - "username": "Usuario" - }, - "description": "Raz\u00f3n: {details}", - "title": "Volver a autenticar la integraci\u00f3n Synology DSM" + "description": "\u00bfQuieres configurar {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Usar SSL/TLS para conectar con tu NAS", "username": "Usuario", "verify_ssl": "Verificar certificado SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/et.json b/homeassistant/components/synology_dsm/translations/et.json index 691059a56e6..69341cab488 100644 --- a/homeassistant/components/synology_dsm/translations/et.json +++ b/homeassistant/components/synology_dsm/translations/et.json @@ -28,16 +28,7 @@ "username": "Kasutajanimi", "verify_ssl": "Kontrolli SSL sertifikaati" }, - "description": "Kas soovid seadistada {name}({host})?", - "title": "" - }, - "reauth": { - "data": { - "password": "Salas\u00f5na", - "username": "Kasutajanimi" - }, - "description": "P\u00f5hjus: {details}", - "title": "Synology DSM: Taastuvasta sidumine" + "description": "Kas soovid seadistada {name}({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Kasutab SSL sertifikaati", "username": "Kasutajanimi", "verify_ssl": "Kontrolli SSL sertifikaati" - }, - "title": "" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/fi.json b/homeassistant/components/synology_dsm/translations/fi.json index 8e1cb61abce..4f5f2cc19fa 100644 --- a/homeassistant/components/synology_dsm/translations/fi.json +++ b/homeassistant/components/synology_dsm/translations/fi.json @@ -8,9 +8,6 @@ "data": { "otp_code": "Koodi" } - }, - "reauth": { - "description": "Syy: {details}" } } }, diff --git a/homeassistant/components/synology_dsm/translations/fr.json b/homeassistant/components/synology_dsm/translations/fr.json index c6488ea7356..3917d9f800e 100644 --- a/homeassistant/components/synology_dsm/translations/fr.json +++ b/homeassistant/components/synology_dsm/translations/fr.json @@ -28,16 +28,7 @@ "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL" }, - "description": "Voulez-vous configurer {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Mot de passe", - "username": "Nom d'utilisateur" - }, - "description": "Raison: {details}", - "title": "Synology DSM R\u00e9-authentifier l'int\u00e9gration" + "description": "Voulez-vous configurer {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Utilise un certificat SSL", "username": "Nom d'utilisateur", "verify_ssl": "V\u00e9rifier le certificat SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/he.json b/homeassistant/components/synology_dsm/translations/he.json index 7adcac9af84..2e7772fc56f 100644 --- a/homeassistant/components/synology_dsm/translations/he.json +++ b/homeassistant/components/synology_dsm/translations/he.json @@ -23,12 +23,6 @@ }, "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name} ({host})?" }, - "reauth": { - "data": { - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" - } - }, "reauth_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4", diff --git a/homeassistant/components/synology_dsm/translations/hu.json b/homeassistant/components/synology_dsm/translations/hu.json index f23702ba33f..12f2bae011e 100644 --- a/homeassistant/components/synology_dsm/translations/hu.json +++ b/homeassistant/components/synology_dsm/translations/hu.json @@ -28,16 +28,7 @@ "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" }, - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Jelsz\u00f3", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "description": "Indokl\u00e1s: {details}", - "title": "Synology DSM Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "SSL tan\u00fas\u00edtv\u00e1ny haszn\u00e1lata", "username": "Felhaszn\u00e1l\u00f3n\u00e9v", "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/id.json b/homeassistant/components/synology_dsm/translations/id.json index 7f0242fedd0..204b7b372fa 100644 --- a/homeassistant/components/synology_dsm/translations/id.json +++ b/homeassistant/components/synology_dsm/translations/id.json @@ -28,16 +28,7 @@ "username": "Nama Pengguna", "verify_ssl": "Verifikasi sertifikat SSL" }, - "description": "Ingin menyiapkan {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Kata Sandi", - "username": "Nama Pengguna" - }, - "description": "Alasan: {details}", - "title": "Autentikasi Ulang Integrasi Synology DSM" + "description": "Ingin menyiapkan {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Menggunakan sertifikat SSL", "username": "Nama Pengguna", "verify_ssl": "Verifikasi sertifikat SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/it.json b/homeassistant/components/synology_dsm/translations/it.json index 54d119c55b0..e0337ffaea9 100644 --- a/homeassistant/components/synology_dsm/translations/it.json +++ b/homeassistant/components/synology_dsm/translations/it.json @@ -28,16 +28,7 @@ "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" }, - "description": "Vuoi impostare {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Password", - "username": "Nome utente" - }, - "description": "Motivo: {details}", - "title": "Synology DSM Autentica nuovamente l'integrazione" + "description": "Vuoi impostare {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Utilizza un certificato SSL", "username": "Nome utente", "verify_ssl": "Verifica il certificato SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/ja.json b/homeassistant/components/synology_dsm/translations/ja.json index 2cf84781cc0..47245b2ceb8 100644 --- a/homeassistant/components/synology_dsm/translations/ja.json +++ b/homeassistant/components/synology_dsm/translations/ja.json @@ -28,16 +28,7 @@ "username": "\u30e6\u30fc\u30b6\u30fc\u540d", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" }, - "description": "{name} ({host})\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "description": "\u7406\u7531: {details}", - "title": "Synology DSM \u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + "description": "{name} ({host})\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "SSL\u8a3c\u660e\u66f8\u3092\u4f7f\u7528\u3059\u308b", "username": "\u30e6\u30fc\u30b6\u30fc\u540d", "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/ko.json b/homeassistant/components/synology_dsm/translations/ko.json index da61e46731e..5de99988192 100644 --- a/homeassistant/components/synology_dsm/translations/ko.json +++ b/homeassistant/components/synology_dsm/translations/ko.json @@ -26,8 +26,7 @@ "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" }, - "description": "{name} ({host})\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", - "title": "Synology DSM" + "description": "{name} ({host})\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" }, "user": { "data": { @@ -37,8 +36,7 @@ "ssl": "SSL \uc778\uc99d\uc11c \uc0ac\uc6a9", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/lb.json b/homeassistant/components/synology_dsm/translations/lb.json index 4360b8685b3..d1faf4dca2f 100644 --- a/homeassistant/components/synology_dsm/translations/lb.json +++ b/homeassistant/components/synology_dsm/translations/lb.json @@ -26,8 +26,7 @@ "username": "Benotzernumm", "verify_ssl": "SSL Zertifikat iwwerpr\u00e9iwen" }, - "description": "Soll {name} ({host}) konfigur\u00e9iert ginn?", - "title": "Synology DSM" + "description": "Soll {name} ({host}) konfigur\u00e9iert ginn?" }, "user": { "data": { @@ -37,8 +36,7 @@ "ssl": "Benotzt ee SSL Zertifikat", "username": "Benotzernumm", "verify_ssl": "SSL Zertifikat iwwerpr\u00e9iwen" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/nb.json b/homeassistant/components/synology_dsm/translations/nb.json index 3a397e1f7d3..2ba01a2ddc4 100644 --- a/homeassistant/components/synology_dsm/translations/nb.json +++ b/homeassistant/components/synology_dsm/translations/nb.json @@ -1,11 +1,6 @@ { "config": { "step": { - "reauth": { - "data": { - "username": "Brukernavn" - } - }, "reauth_confirm": { "data": { "username": "Brukernavn" diff --git a/homeassistant/components/synology_dsm/translations/nl.json b/homeassistant/components/synology_dsm/translations/nl.json index 801c1d7fe82..c685137738a 100644 --- a/homeassistant/components/synology_dsm/translations/nl.json +++ b/homeassistant/components/synology_dsm/translations/nl.json @@ -28,16 +28,7 @@ "username": "Gebruikersnaam", "verify_ssl": "Controleer het SSL-certificaat" }, - "description": "Wil je {name} ({host}) instellen?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Wachtwoord", - "username": "Gebruikersnaam" - }, - "description": "Reden: {details}", - "title": "Synology DSM Verifieer de integratie opnieuw" + "description": "Wil je {name} ({host}) instellen?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Gebruik een SSL-certificaat", "username": "Gebruikersnaam", "verify_ssl": "Controleer het SSL-certificaat" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/no.json b/homeassistant/components/synology_dsm/translations/no.json index 41f56c135aa..89ca80b168e 100644 --- a/homeassistant/components/synology_dsm/translations/no.json +++ b/homeassistant/components/synology_dsm/translations/no.json @@ -28,16 +28,7 @@ "username": "Brukernavn", "verify_ssl": "Verifisere SSL-sertifikat" }, - "description": "Vil du konfigurere {name} ({host})?", - "title": "" - }, - "reauth": { - "data": { - "password": "Passord", - "username": "Brukernavn" - }, - "description": "\u00c5rsak: {details}", - "title": "Synology DSM Godkjenne integrering p\u00e5 nytt" + "description": "Vil du konfigurere {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Bruker et SSL-sertifikat", "username": "Brukernavn", "verify_ssl": "Verifisere SSL-sertifikat" - }, - "title": "" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/pl.json b/homeassistant/components/synology_dsm/translations/pl.json index 9e285f65f84..a5c78b4390f 100644 --- a/homeassistant/components/synology_dsm/translations/pl.json +++ b/homeassistant/components/synology_dsm/translations/pl.json @@ -28,16 +28,7 @@ "username": "Nazwa u\u017cytkownika", "verify_ssl": "Weryfikacja certyfikatu SSL" }, - "description": "Czy chcesz skonfigurowa\u0107 {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Has\u0142o", - "username": "Nazwa u\u017cytkownika" - }, - "description": "Pow\u00f3d: {details}", - "title": "Ponownie uwierzytelnij integracj\u0119 Synology DSM" + "description": "Czy chcesz skonfigurowa\u0107 {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Certyfikat SSL", "username": "Nazwa u\u017cytkownika", "verify_ssl": "Weryfikacja certyfikatu SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/pt-BR.json b/homeassistant/components/synology_dsm/translations/pt-BR.json index ddde3e1e29d..3410658fafe 100644 --- a/homeassistant/components/synology_dsm/translations/pt-BR.json +++ b/homeassistant/components/synology_dsm/translations/pt-BR.json @@ -28,16 +28,7 @@ "username": "Usu\u00e1rio", "verify_ssl": "Verifique o certificado SSL" }, - "description": "Voc\u00ea quer configurar o {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Senha", - "username": "Usu\u00e1rio" - }, - "description": "Motivo: {details}", - "title": "Synology DSM Reautenticar Integra\u00e7\u00e3o" + "description": "Voc\u00ea quer configurar o {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "Usar um certificado SSL", "username": "Usu\u00e1rio", "verify_ssl": "Verifique o certificado SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/ru.json b/homeassistant/components/synology_dsm/translations/ru.json index 3f73d41d740..007504c4828 100644 --- a/homeassistant/components/synology_dsm/translations/ru.json +++ b/homeassistant/components/synology_dsm/translations/ru.json @@ -28,16 +28,7 @@ "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" }, - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} ({host})?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "description": "\u041f\u0440\u0438\u0447\u0438\u043d\u0430: {details}", - "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f Synology DSM" + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} ({host})?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/sk.json b/homeassistant/components/synology_dsm/translations/sk.json index 59f0e333d63..83965998f0e 100644 --- a/homeassistant/components/synology_dsm/translations/sk.json +++ b/homeassistant/components/synology_dsm/translations/sk.json @@ -13,11 +13,6 @@ "port": "Port" } }, - "reauth": { - "data": { - "password": "Heslo" - } - }, "user": { "data": { "port": "Port" diff --git a/homeassistant/components/synology_dsm/translations/sl.json b/homeassistant/components/synology_dsm/translations/sl.json index 164abf7e2cc..91d32273e62 100644 --- a/homeassistant/components/synology_dsm/translations/sl.json +++ b/homeassistant/components/synology_dsm/translations/sl.json @@ -23,8 +23,7 @@ "ssl": "Uporabite SSL/TLS za povezavo z va\u0161im NAS-om", "username": "Uporabni\u0161ko ime" }, - "description": "Ali \u017eelite nastaviti {name} ({host})?", - "title": "Synology DSM" + "description": "Ali \u017eelite nastaviti {name} ({host})?" }, "user": { "data": { @@ -33,8 +32,7 @@ "port": "Vrata (Izbirno)", "ssl": "Uporabite SSL/TLS za povezavo z va\u0161im NAS-om", "username": "Uporabni\u0161ko ime" - }, - "title": "Synology DSM" + } } } } diff --git a/homeassistant/components/synology_dsm/translations/sv.json b/homeassistant/components/synology_dsm/translations/sv.json index a6f5c496f22..04814596518 100644 --- a/homeassistant/components/synology_dsm/translations/sv.json +++ b/homeassistant/components/synology_dsm/translations/sv.json @@ -10,8 +10,7 @@ "port": "Port (Valfri)", "username": "Anv\u00e4ndarnamn" }, - "description": "Do vill du konfigurera {name} ({host})?", - "title": "Synology DSM" + "description": "Do vill du konfigurera {name} ({host})?" }, "user": { "data": { diff --git a/homeassistant/components/synology_dsm/translations/tr.json b/homeassistant/components/synology_dsm/translations/tr.json index 0d527a34504..bcd2085218f 100644 --- a/homeassistant/components/synology_dsm/translations/tr.json +++ b/homeassistant/components/synology_dsm/translations/tr.json @@ -28,16 +28,7 @@ "username": "Kullan\u0131c\u0131 Ad\u0131", "verify_ssl": "SSL sertifikalar\u0131n\u0131 do\u011frula" }, - "description": "{name} ( {host} ) kurulumu yapmak istiyor musunuz?", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "Parola", - "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "description": "Sebep: {details}", - "title": "Synology DSM Entegrasyonu Yeniden Do\u011frula" + "description": "{name} ( {host} ) kurulumu yapmak istiyor musunuz?" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "SSL sertifikas\u0131 kullan\u0131r", "username": "Kullan\u0131c\u0131 Ad\u0131", "verify_ssl": "SSL sertifikalar\u0131n\u0131 do\u011frula" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/uk.json b/homeassistant/components/synology_dsm/translations/uk.json index 4d80350989f..21b0f4a68f0 100644 --- a/homeassistant/components/synology_dsm/translations/uk.json +++ b/homeassistant/components/synology_dsm/translations/uk.json @@ -26,8 +26,7 @@ "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430", "verify_ssl": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u0430 SSL" }, - "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 {name} ({host})?", - "title": "Synology DSM" + "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 {name} ({host})?" }, "user": { "data": { @@ -37,8 +36,7 @@ "ssl": "\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442 SSL", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430", "verify_ssl": "\u041f\u0435\u0440\u0435\u0432\u0456\u0440\u043a\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u0430 SSL" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/zh-Hans.json b/homeassistant/components/synology_dsm/translations/zh-Hans.json index 862f526c38d..a62a79ae868 100644 --- a/homeassistant/components/synology_dsm/translations/zh-Hans.json +++ b/homeassistant/components/synology_dsm/translations/zh-Hans.json @@ -26,14 +26,7 @@ "ssl": "\u4f7f\u7528 SSL \u8bc1\u4e66", "username": "\u7528\u6237\u540d" }, - "description": "\u60a8\u60f3\u8981\u914d\u7f6e {name} ({host}) \u5417\uff1f", - "title": "Synology DSM" - }, - "reauth": { - "data": { - "password": "\u5bc6\u7801", - "username": "\u7528\u6237\u540d" - } + "description": "\u60a8\u60f3\u8981\u914d\u7f6e {name} ({host}) \u5417\uff1f" }, "user": { "data": { @@ -43,8 +36,7 @@ "ssl": "\u4f7f\u7528 SSL \u8bc1\u4e66", "username": "\u7528\u6237\u540d", "verify_ssl": "\u9a8c\u8bc1 SSL \u8bc1\u4e66" - }, - "title": "Synology DSM" + } } } }, diff --git a/homeassistant/components/synology_dsm/translations/zh-Hant.json b/homeassistant/components/synology_dsm/translations/zh-Hant.json index 33211d9a5a3..504e1bec32d 100644 --- a/homeassistant/components/synology_dsm/translations/zh-Hant.json +++ b/homeassistant/components/synology_dsm/translations/zh-Hant.json @@ -28,16 +28,7 @@ "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" }, - "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} ({host})\uff1f", - "title": "\u7fa4\u6689 DSM" - }, - "reauth": { - "data": { - "password": "\u5bc6\u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "description": "\u8a73\u7d30\u8cc7\u8a0a\uff1a{details}", - "title": "Synology DSM \u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} ({host})\uff1f" }, "reauth_confirm": { "data": { @@ -54,8 +45,7 @@ "ssl": "\u4f7f\u7528 SSL \u8a8d\u8b49", "username": "\u4f7f\u7528\u8005\u540d\u7a31", "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" - }, - "title": "\u7fa4\u6689 DSM" + } } } }, diff --git a/homeassistant/components/tasmota/translations/bg.json b/homeassistant/components/tasmota/translations/bg.json index a2321080a6a..425bec1cad5 100644 --- a/homeassistant/components/tasmota/translations/bg.json +++ b/homeassistant/components/tasmota/translations/bg.json @@ -4,9 +4,6 @@ "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." }, "step": { - "config": { - "title": "Tasmota" - }, "confirm": { "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Tasmota?" } diff --git a/homeassistant/components/tasmota/translations/ca.json b/homeassistant/components/tasmota/translations/ca.json index 0a414bc5cfe..f5adb5b0694 100644 --- a/homeassistant/components/tasmota/translations/ca.json +++ b/homeassistant/components/tasmota/translations/ca.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefix del topic de descoberta (discovery)" - }, - "description": "Introdureix la configuraci\u00f3 de Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Vols configurar Tasmota?" diff --git a/homeassistant/components/tasmota/translations/cs.json b/homeassistant/components/tasmota/translations/cs.json index 4fb0d555c63..673126f1cf0 100644 --- a/homeassistant/components/tasmota/translations/cs.json +++ b/homeassistant/components/tasmota/translations/cs.json @@ -4,10 +4,6 @@ "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." }, "step": { - "config": { - "description": "Pros\u00edm zajdete nastaven\u00ed pro Tasmota.", - "title": "Tasmota" - }, "confirm": { "description": "Chcete nastavit Tasmota?" } diff --git a/homeassistant/components/tasmota/translations/de.json b/homeassistant/components/tasmota/translations/de.json index 7e654d982c5..128b99a75b9 100644 --- a/homeassistant/components/tasmota/translations/de.json +++ b/homeassistant/components/tasmota/translations/de.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Themenpr\u00e4fix f\u00fcr die Erkennung" - }, - "description": "Bitte die Tasmota-Konfiguration einstellen.", - "title": "Tasmota" + } }, "confirm": { "description": "M\u00f6chtest du Tasmota einrichten?" diff --git a/homeassistant/components/tasmota/translations/el.json b/homeassistant/components/tasmota/translations/el.json index cb9bc10730c..732724ab2ab 100644 --- a/homeassistant/components/tasmota/translations/el.json +++ b/homeassistant/components/tasmota/translations/el.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\u03a0\u03c1\u03cc\u03b8\u03b5\u03bc\u03b1 \u03b8\u03ad\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7\u03c2" - }, - "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7\u03bd Tasmota;" diff --git a/homeassistant/components/tasmota/translations/en.json b/homeassistant/components/tasmota/translations/en.json index ddd7e726079..3e8b0b43bce 100644 --- a/homeassistant/components/tasmota/translations/en.json +++ b/homeassistant/components/tasmota/translations/en.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Discovery topic prefix" - }, - "description": "Please enter the Tasmota configuration.", - "title": "Tasmota" + } }, "confirm": { "description": "Do you want to set up Tasmota?" diff --git a/homeassistant/components/tasmota/translations/es.json b/homeassistant/components/tasmota/translations/es.json index f5a5532f180..0d3b7317330 100644 --- a/homeassistant/components/tasmota/translations/es.json +++ b/homeassistant/components/tasmota/translations/es.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefijo del tema para descubrimiento" - }, - "description": "Introduce la configuraci\u00f3n de Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "\u00bfQuieres configurar Tasmota?" diff --git a/homeassistant/components/tasmota/translations/et.json b/homeassistant/components/tasmota/translations/et.json index e689dcf7e43..09ba6e5c328 100644 --- a/homeassistant/components/tasmota/translations/et.json +++ b/homeassistant/components/tasmota/translations/et.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Tuvastusteema eesliide" - }, - "description": "Sisesta Tasmota konfiguratsioon.", - "title": "Tasmota" + } }, "confirm": { "description": "Kas soovid seadistada Tasmota sidumist?" diff --git a/homeassistant/components/tasmota/translations/fr.json b/homeassistant/components/tasmota/translations/fr.json index 901de884bcd..7521004ba2f 100644 --- a/homeassistant/components/tasmota/translations/fr.json +++ b/homeassistant/components/tasmota/translations/fr.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Pr\u00e9fixe du sujet de d\u00e9couverte" - }, - "description": "Veuillez entrer la configuration Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Voulez-vous configurer Tasmota ?" diff --git a/homeassistant/components/tasmota/translations/he.json b/homeassistant/components/tasmota/translations/he.json index a2a5db62b37..2bc04cca267 100644 --- a/homeassistant/components/tasmota/translations/he.json +++ b/homeassistant/components/tasmota/translations/he.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\u05d2\u05d9\u05dc\u05d5\u05d9 \u05dc\u05e4\u05d9 \u05e7\u05d9\u05d3\u05d5\u05de\u05ea \u05e0\u05d5\u05e9\u05d0" - }, - "description": "\u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05d0\u05ea \u05ea\u05e6\u05d5\u05e8\u05ea Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea Tasmota?" diff --git a/homeassistant/components/tasmota/translations/hu.json b/homeassistant/components/tasmota/translations/hu.json index 7c77caadc8e..990bde11d58 100644 --- a/homeassistant/components/tasmota/translations/hu.json +++ b/homeassistant/components/tasmota/translations/hu.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Felder\u00edt\u00e9si (discovery) topik el\u0151tagja" - }, - "description": "Adja meg a Tasmota konfigur\u00e1ci\u00f3t.", - "title": "Tasmota" + } }, "confirm": { "description": "Szeretn\u00e9 b\u00e1ll\u00edtani a Tasmota-t?" diff --git a/homeassistant/components/tasmota/translations/id.json b/homeassistant/components/tasmota/translations/id.json index a11acf50390..23ca02192db 100644 --- a/homeassistant/components/tasmota/translations/id.json +++ b/homeassistant/components/tasmota/translations/id.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefiks topik penemuan" - }, - "description": "Masukkan konfigurasi Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Ingin menyiapkan Tasmota?" diff --git a/homeassistant/components/tasmota/translations/it.json b/homeassistant/components/tasmota/translations/it.json index 6cb52a616b0..22269eafd9f 100644 --- a/homeassistant/components/tasmota/translations/it.json +++ b/homeassistant/components/tasmota/translations/it.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefisso dell'argomento di individuazione" - }, - "description": "Inserire la configurazione Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Vuoi configurare Tasmota?" diff --git a/homeassistant/components/tasmota/translations/ja.json b/homeassistant/components/tasmota/translations/ja.json index 353b3020e8c..8aad41de32f 100644 --- a/homeassistant/components/tasmota/translations/ja.json +++ b/homeassistant/components/tasmota/translations/ja.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "(\u691c\u51fa)Discovery topic prefix" - }, - "description": "Tasmota\u306e\u8a2d\u5b9a\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Tasmota" + } }, "confirm": { "description": "Tasmota\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" diff --git a/homeassistant/components/tasmota/translations/ko.json b/homeassistant/components/tasmota/translations/ko.json index 45cac13f622..d647088c55d 100644 --- a/homeassistant/components/tasmota/translations/ko.json +++ b/homeassistant/components/tasmota/translations/ko.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\uac80\uc0c9 \ud1a0\ud53d \uc811\ub450\uc0ac" - }, - "description": "Tasmota \uad6c\uc131\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694.", - "title": "Tasmota" + } }, "confirm": { "description": "Tasmota\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" diff --git a/homeassistant/components/tasmota/translations/lb.json b/homeassistant/components/tasmota/translations/lb.json index a7b8d6d0ce6..6208f54b7c0 100644 --- a/homeassistant/components/tasmota/translations/lb.json +++ b/homeassistant/components/tasmota/translations/lb.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Discovery topic prefix" - }, - "description": "F\u00ebll Tasmota Konfiguratioun aus.", - "title": "Tasmota" + } }, "confirm": { "description": "Soll Tasmota konfigur\u00e9iert ginn?" diff --git a/homeassistant/components/tasmota/translations/nl.json b/homeassistant/components/tasmota/translations/nl.json index da16eb72bc3..474ed82b9d6 100644 --- a/homeassistant/components/tasmota/translations/nl.json +++ b/homeassistant/components/tasmota/translations/nl.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Discovery-onderwerpvoorvoegsel" - }, - "description": "Vul de Tasmota gegevens in", - "title": "Tasmota" + } }, "confirm": { "description": "Wil je Tasmota instellen?" diff --git a/homeassistant/components/tasmota/translations/no.json b/homeassistant/components/tasmota/translations/no.json index 3c68c280086..7f0a67e7c9f 100644 --- a/homeassistant/components/tasmota/translations/no.json +++ b/homeassistant/components/tasmota/translations/no.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefiks for oppdagelsesemne" - }, - "description": "Vennligst skriv inn Tasmota-konfigurasjonen.", - "title": "" + } }, "confirm": { "description": "Vil du sette opp Tasmota?" diff --git a/homeassistant/components/tasmota/translations/pl.json b/homeassistant/components/tasmota/translations/pl.json index b6bbf3fe953..70ffeb5c7e2 100644 --- a/homeassistant/components/tasmota/translations/pl.json +++ b/homeassistant/components/tasmota/translations/pl.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefiks tematu wykrywania" - }, - "description": "Wprowad\u017a konfiguracj\u0119 dla Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" diff --git a/homeassistant/components/tasmota/translations/pt-BR.json b/homeassistant/components/tasmota/translations/pt-BR.json index 6fa1f064e88..afd1c76c25f 100644 --- a/homeassistant/components/tasmota/translations/pt-BR.json +++ b/homeassistant/components/tasmota/translations/pt-BR.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefixo do t\u00f3pico de descoberta" - }, - "description": "Por favor, insira a configura\u00e7\u00e3o do Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "Deseja configurar o Tasmota?" diff --git a/homeassistant/components/tasmota/translations/pt.json b/homeassistant/components/tasmota/translations/pt.json index 3df19f11fa9..508ab27abb0 100644 --- a/homeassistant/components/tasmota/translations/pt.json +++ b/homeassistant/components/tasmota/translations/pt.json @@ -10,8 +10,7 @@ "config": { "data": { "discovery_prefix": "Prefixo do t\u00f3pico para descoberta" - }, - "title": "" + } } } } diff --git a/homeassistant/components/tasmota/translations/ru.json b/homeassistant/components/tasmota/translations/ru.json index 4f01d164030..14e336a9b58 100644 --- a/homeassistant/components/tasmota/translations/ru.json +++ b/homeassistant/components/tasmota/translations/ru.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\u041f\u0440\u0435\u0444\u0438\u043a\u0441 \u0442\u043e\u043f\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Tasmota?" diff --git a/homeassistant/components/tasmota/translations/tr.json b/homeassistant/components/tasmota/translations/tr.json index 95e4e41b11e..71c38ef1bc0 100644 --- a/homeassistant/components/tasmota/translations/tr.json +++ b/homeassistant/components/tasmota/translations/tr.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "Ba\u015fl\u0131k Ke\u015ffet" - }, - "description": "L\u00fctfen Tasmota yap\u0131land\u0131rmas\u0131n\u0131 girin.", - "title": "Tasmota" + } }, "confirm": { "description": "Tasmota'y\u0131 kurmak istiyor musunuz?" diff --git a/homeassistant/components/tasmota/translations/uk.json b/homeassistant/components/tasmota/translations/uk.json index 5b57f950866..d1445e5c0e7 100644 --- a/homeassistant/components/tasmota/translations/uk.json +++ b/homeassistant/components/tasmota/translations/uk.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\u041f\u0440\u0435\u0444\u0456\u043a\u0441 \u0442\u0435\u043c\u0438 \u0430\u0432\u0442\u043e\u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f" - }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Tasmota.", - "title": "Tasmota" + } }, "confirm": { "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 Tasmota?" diff --git a/homeassistant/components/tasmota/translations/zh-Hant.json b/homeassistant/components/tasmota/translations/zh-Hant.json index e823937f972..58833747f46 100644 --- a/homeassistant/components/tasmota/translations/zh-Hant.json +++ b/homeassistant/components/tasmota/translations/zh-Hant.json @@ -10,9 +10,7 @@ "config": { "data": { "discovery_prefix": "\u63a2\u7d22\u4e3b\u984c prefix" - }, - "description": "\u8acb\u8f38\u5165 Tasmota \u8a2d\u5b9a\u3002", - "title": "Tasmota" + } }, "confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a Tasmota\uff1f" diff --git a/homeassistant/components/tautulli/translations/ko.json b/homeassistant/components/tautulli/translations/ko.json new file mode 100644 index 00000000000..effc12fad5a --- /dev/null +++ b/homeassistant/components/tautulli/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "step": { + "user": { + "data": { + "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" + }, + "description": "API \ud0a4\ub97c \ucc3e\uc73c\ub824\uba74 Tautulli \uc6f9 \ud398\uc774\uc9c0\ub97c \uc5f4\uace0 \uc124\uc815\uc73c\ub85c \uc774\ub3d9\ud55c \ub2e4\uc74c \uc6f9 \uc778\ud130\ud398\uc774\uc2a4\ub85c \uc774\ub3d9\ud569\ub2c8\ub2e4. API \ud0a4\ub294 \ud574\ub2f9 \ud398\uc774\uc9c0\uc758 \ub9e8 \uc544\ub798\uc5d0 \uc788\uc2b5\ub2c8\ub2e4.\n\nURL\uc758 \uc608: '''http://192.168.0.10:8181''''(\uae30\ubcf8\ud3ec\ud2b8: 8181)" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/ca.json b/homeassistant/components/threshold/translations/ca.json index b33bd8a04ed..b933b85b080 100644 --- a/homeassistant/components/threshold/translations/ca.json +++ b/homeassistant/components/threshold/translations/ca.json @@ -9,7 +9,6 @@ "entity_id": "Sensor d'entrada", "hysteresis": "Hist\u00e8resi", "lower": "L\u00edmit inferior", - "mode": "Mode llindar", "name": "Nom", "upper": "L\u00edmit superior" }, @@ -28,7 +27,6 @@ "entity_id": "Sensor d'entrada", "hysteresis": "Hist\u00e8resi", "lower": "L\u00edmit inferior", - "mode": "Mode llindar", "name": "Nom", "upper": "L\u00edmit superior" }, diff --git a/homeassistant/components/threshold/translations/de.json b/homeassistant/components/threshold/translations/de.json index 579dd4f49b0..f8f805742ca 100644 --- a/homeassistant/components/threshold/translations/de.json +++ b/homeassistant/components/threshold/translations/de.json @@ -9,7 +9,6 @@ "entity_id": "Eingangssensor", "hysteresis": "Hysterese", "lower": "Untere Grenze", - "mode": "Schwellenwertmodus", "name": "Name", "upper": "Obergrenze" }, @@ -28,7 +27,6 @@ "entity_id": "Eingangssensor", "hysteresis": "Hysterese", "lower": "Untere Grenze", - "mode": "Schwellenwertmodus", "name": "Name", "upper": "Obergrenze" }, diff --git a/homeassistant/components/threshold/translations/el.json b/homeassistant/components/threshold/translations/el.json index 2ad5f25e5f8..11735c53908 100644 --- a/homeassistant/components/threshold/translations/el.json +++ b/homeassistant/components/threshold/translations/el.json @@ -9,7 +9,6 @@ "entity_id": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", "hysteresis": "\u03a5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7", "lower": "\u039a\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf", - "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03c1\u03af\u03bf\u03c5", "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "upper": "\u0386\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf" }, @@ -28,7 +27,6 @@ "entity_id": "\u0391\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5", "hysteresis": "\u03a5\u03c3\u03c4\u03ad\u03c1\u03b7\u03c3\u03b7", "lower": "\u039a\u03ac\u03c4\u03c9 \u03cc\u03c1\u03b9\u03bf", - "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bf\u03c1\u03af\u03bf\u03c5", "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "upper": "\u0386\u03bd\u03c9 \u03cc\u03c1\u03b9\u03bf" }, diff --git a/homeassistant/components/threshold/translations/en.json b/homeassistant/components/threshold/translations/en.json index 66a2bb33ddb..461ca244353 100644 --- a/homeassistant/components/threshold/translations/en.json +++ b/homeassistant/components/threshold/translations/en.json @@ -9,7 +9,6 @@ "entity_id": "Input sensor", "hysteresis": "Hysteresis", "lower": "Lower limit", - "mode": "Threshold mode", "name": "Name", "upper": "Upper limit" }, @@ -28,7 +27,6 @@ "entity_id": "Input sensor", "hysteresis": "Hysteresis", "lower": "Lower limit", - "mode": "Threshold mode", "name": "Name", "upper": "Upper limit" }, diff --git a/homeassistant/components/threshold/translations/et.json b/homeassistant/components/threshold/translations/et.json index 7a41c0bfc3f..c98f370e47d 100644 --- a/homeassistant/components/threshold/translations/et.json +++ b/homeassistant/components/threshold/translations/et.json @@ -9,7 +9,6 @@ "entity_id": "Sisendandur", "hysteresis": "H\u00fcsterees", "lower": "Alampiir", - "mode": "L\u00e4vendi kasutamine", "name": "Nimi", "upper": "\u00dclempiir" }, @@ -28,7 +27,6 @@ "entity_id": "Sisendandur", "hysteresis": "H\u00fcsterees", "lower": "Alampiir", - "mode": "L\u00e4vendi kasutamine", "name": "Nimi", "upper": "\u00dclempiir" }, diff --git a/homeassistant/components/threshold/translations/fr.json b/homeassistant/components/threshold/translations/fr.json index 29bc21e9cd5..8c6015f29a7 100644 --- a/homeassistant/components/threshold/translations/fr.json +++ b/homeassistant/components/threshold/translations/fr.json @@ -9,7 +9,6 @@ "entity_id": "Capteur d'entr\u00e9e", "hysteresis": "Hyst\u00e9r\u00e9sis", "lower": "Limite inf\u00e9rieure", - "mode": "Mode de seuil", "name": "Nom", "upper": "Limite sup\u00e9rieure" }, @@ -28,7 +27,6 @@ "entity_id": "Capteur d'entr\u00e9e", "hysteresis": "Hyst\u00e9r\u00e9sis", "lower": "Limite inf\u00e9rieure", - "mode": "Mode de seuil", "name": "Nom", "upper": "Limite sup\u00e9rieure" }, diff --git a/homeassistant/components/threshold/translations/hu.json b/homeassistant/components/threshold/translations/hu.json index 944a13680d3..3c582792d69 100644 --- a/homeassistant/components/threshold/translations/hu.json +++ b/homeassistant/components/threshold/translations/hu.json @@ -9,7 +9,6 @@ "entity_id": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", "hysteresis": "Hiszter\u00e9zis", "lower": "Als\u00f3 hat\u00e1r", - "mode": "K\u00fcsz\u00f6b\u00e9rt\u00e9k m\u00f3d", "name": "Elnevez\u00e9s", "upper": "Fels\u0151 hat\u00e1r" }, @@ -28,7 +27,6 @@ "entity_id": "Forr\u00e1s \u00e9rz\u00e9kel\u0151", "hysteresis": "Hiszter\u00e9zis", "lower": "Als\u00f3 hat\u00e1r", - "mode": "K\u00fcsz\u00f6b\u00e9rt\u00e9k m\u00f3d", "name": "Elnevez\u00e9s", "upper": "Fels\u0151 hat\u00e1r" }, diff --git a/homeassistant/components/threshold/translations/id.json b/homeassistant/components/threshold/translations/id.json index 7356dd772e1..474f742324b 100644 --- a/homeassistant/components/threshold/translations/id.json +++ b/homeassistant/components/threshold/translations/id.json @@ -9,7 +9,6 @@ "entity_id": "Sensor input", "hysteresis": "Histeresis", "lower": "Batas bawah", - "mode": "Mode ambang batas", "name": "Nama", "upper": "Batas atas" }, @@ -28,7 +27,6 @@ "entity_id": "Sensor input", "hysteresis": "Histeresis", "lower": "Batas bawah", - "mode": "Mode ambang batas", "name": "Nama", "upper": "Batas atas" }, diff --git a/homeassistant/components/threshold/translations/it.json b/homeassistant/components/threshold/translations/it.json index 6bc62dce765..79c0e24b2af 100644 --- a/homeassistant/components/threshold/translations/it.json +++ b/homeassistant/components/threshold/translations/it.json @@ -9,7 +9,6 @@ "entity_id": "Sensore di ingresso", "hysteresis": "Isteresi", "lower": "Limite inferiore", - "mode": "Modalit\u00e0 soglia", "name": "Nome", "upper": "Limite superiore" }, @@ -28,7 +27,6 @@ "entity_id": "Sensore di ingresso", "hysteresis": "Isteresi", "lower": "Limite inferiore", - "mode": "Modalit\u00e0 soglia", "name": "Nome", "upper": "Limite superiore" }, diff --git a/homeassistant/components/threshold/translations/ja.json b/homeassistant/components/threshold/translations/ja.json index 75330ac830c..1978fa4f3c5 100644 --- a/homeassistant/components/threshold/translations/ja.json +++ b/homeassistant/components/threshold/translations/ja.json @@ -9,7 +9,6 @@ "entity_id": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", "hysteresis": "\u30d2\u30b9\u30c6\u30ea\u30b7\u30b9", "lower": "\u4e0b\u9650\u5024", - "mode": "\u3057\u304d\u3044\u5024\u30e2\u30fc\u30c9", "name": "\u540d\u524d", "upper": "\u4e0a\u9650\u5024" }, @@ -28,7 +27,6 @@ "entity_id": "\u5165\u529b\u30bb\u30f3\u30b5\u30fc", "hysteresis": "\u30d2\u30b9\u30c6\u30ea\u30b7\u30b9", "lower": "\u4e0b\u9650\u5024", - "mode": "\u3057\u304d\u3044\u5024\u30e2\u30fc\u30c9", "name": "\u540d\u524d", "upper": "\u4e0a\u9650\u5024" }, diff --git a/homeassistant/components/threshold/translations/ko.json b/homeassistant/components/threshold/translations/ko.json new file mode 100644 index 00000000000..fb30f1e5086 --- /dev/null +++ b/homeassistant/components/threshold/translations/ko.json @@ -0,0 +1,38 @@ +{ + "config": { + "error": { + "need_lower_upper": "\ud558\ud55c\uacfc \uc0c1\ud55c\uc744 \ubaa8\ub450 \ube44\uc6cc\ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + }, + "step": { + "user": { + "data": { + "entity_id": "\uc785\ub825 \uc13c\uc11c", + "hysteresis": "\ud788\uc2a4\ud14c\ub9ac\uc2dc\uc2a4", + "lower": "\ud558\ud55c", + "name": "\uc774\ub984", + "upper": "\uc0c1\ud55c" + }, + "description": "\uc13c\uc11c \uac12\uc5d0 \ub530\ub77c \ucf1c\uc9c0\uace0 \uaebc\uc9c0\ub294 \uc774\uc9c4 \uc13c\uc11c\ub97c \uc0dd\uc131\ud569\ub2c8\ub2e4.\n\n\ud558\ud55c\ub9cc \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 \ud558\ud55c \uc774\ud558\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4.\n\uc0c1\ud55c\ub9cc \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 \uc0c1\ud55c \uc774\uc0c1\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4.\n\uc0c1\ud55c, \ud558\ud55c \ubaa8\ub450 \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 [\ud558\ud55c .. \uc0c1\ud55c] \ubc94\uc704\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4.", + "title": "\uc784\uacc4\uac12 \uc13c\uc11c \ucd94\uac00" + } + } + }, + "options": { + "error": { + "need_lower_upper": "\ud558\ud55c\uacfc \uc0c1\ud55c\uc744 \ubaa8\ub450 \ube44\uc6cc\ub458 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." + }, + "step": { + "init": { + "data": { + "entity_id": "\uc785\ub825 \uc13c\uc11c", + "hysteresis": "\ud788\uc2a4\ud14c\ub9ac\uc2dc\uc2a4", + "lower": "\ud558\ud55c", + "name": "\uc774\ub984", + "upper": "\uc0c1\ud55c" + }, + "description": "\ud558\ud55c\ub9cc \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 \ud558\ud55c \uc774\ud558\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4.\n\uc0c1\ud55c\ub9cc \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 \uc0c1\ud55c \uc774\uc0c1\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4.\n\uc0c1\ud55c, \ud558\ud55c \ubaa8\ub450 \uad6c\uc131\ub41c \uacbd\uc6b0 - \uc13c\uc11c \uac12\uc774 [\ud558\ud55c .. \uc0c1\ud55c] \ubc94\uc704\uc77c \ub54c \ucf1c\uc9d1\ub2c8\ub2e4." + } + } + }, + "title": "\uc784\uacc4\uac12 \uc13c\uc11c" +} \ No newline at end of file diff --git a/homeassistant/components/threshold/translations/nl.json b/homeassistant/components/threshold/translations/nl.json index c49b00eee0d..3bcf8bf599e 100644 --- a/homeassistant/components/threshold/translations/nl.json +++ b/homeassistant/components/threshold/translations/nl.json @@ -9,7 +9,6 @@ "entity_id": "Invoer sensor", "hysteresis": "Hyseterise", "lower": "Ondergrens", - "mode": "Drempelmodus", "name": "Naam", "upper": "Bovengrens" }, @@ -28,7 +27,6 @@ "entity_id": "Invoer sensor", "hysteresis": "Hyseterise", "lower": "Ondergrens", - "mode": "Drempelmodus", "name": "Naam", "upper": "Bovengrens" }, diff --git a/homeassistant/components/threshold/translations/no.json b/homeassistant/components/threshold/translations/no.json index 66c8461d8cc..1800e628f31 100644 --- a/homeassistant/components/threshold/translations/no.json +++ b/homeassistant/components/threshold/translations/no.json @@ -9,7 +9,6 @@ "entity_id": "Inngangssensor", "hysteresis": "Hysterese", "lower": "Nedre grense", - "mode": "Terskelverdi-modus", "name": "Navn", "upper": "\u00d8vre grense" }, @@ -28,7 +27,6 @@ "entity_id": "Inngangssensor", "hysteresis": "Hysterese", "lower": "Nedre grense", - "mode": "Terskelverdi-modus", "name": "Navn", "upper": "\u00d8vre grense" }, diff --git a/homeassistant/components/threshold/translations/pl.json b/homeassistant/components/threshold/translations/pl.json index 5db231947c2..e8f04d4a00b 100644 --- a/homeassistant/components/threshold/translations/pl.json +++ b/homeassistant/components/threshold/translations/pl.json @@ -9,7 +9,6 @@ "entity_id": "Sensor wej\u015bciowy", "hysteresis": "Op\u00f3\u017anienie", "lower": "Dolny limit", - "mode": "Tryb progowy", "name": "Nazwa", "upper": "G\u00f3rny limit" }, @@ -28,7 +27,6 @@ "entity_id": "Sensor wej\u015bciowy", "hysteresis": "Op\u00f3\u017anienie", "lower": "Dolny limit", - "mode": "Tryb progowy", "name": "Nazwa", "upper": "G\u00f3rny limit" }, diff --git a/homeassistant/components/threshold/translations/pt-BR.json b/homeassistant/components/threshold/translations/pt-BR.json index 1aa7358086a..fb919043313 100644 --- a/homeassistant/components/threshold/translations/pt-BR.json +++ b/homeassistant/components/threshold/translations/pt-BR.json @@ -9,7 +9,6 @@ "entity_id": "Sensor de entrada", "hysteresis": "Histerese", "lower": "Limite inferior", - "mode": "Modo Threshold", "name": "Nome", "upper": "Limite superior" }, @@ -28,7 +27,6 @@ "entity_id": "Sensor de entrada", "hysteresis": "Histerese", "lower": "Limite inferior", - "mode": "Modo Threshold", "name": "Nome", "upper": "Limite superior" }, diff --git a/homeassistant/components/threshold/translations/ru.json b/homeassistant/components/threshold/translations/ru.json index 5b8c4546823..f1be2c4f26c 100644 --- a/homeassistant/components/threshold/translations/ru.json +++ b/homeassistant/components/threshold/translations/ru.json @@ -9,7 +9,6 @@ "entity_id": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", "hysteresis": "\u0413\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", "lower": "\u041d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b", - "mode": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "upper": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b" }, @@ -28,7 +27,6 @@ "entity_id": "\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440", "hysteresis": "\u0413\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441", "lower": "\u041d\u0438\u0436\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b", - "mode": "\u041f\u043e\u0440\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0436\u0438\u043c", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "upper": "\u0412\u0435\u0440\u0445\u043d\u0438\u0439 \u043f\u0440\u0435\u0434\u0435\u043b" }, diff --git a/homeassistant/components/threshold/translations/tr.json b/homeassistant/components/threshold/translations/tr.json index 0cb338ef681..0954ea81d3f 100644 --- a/homeassistant/components/threshold/translations/tr.json +++ b/homeassistant/components/threshold/translations/tr.json @@ -9,7 +9,6 @@ "entity_id": "Giri\u015f sens\u00f6r\u00fc", "hysteresis": "Histeresis", "lower": "Alt s\u0131n\u0131r", - "mode": "E\u015fik modu", "name": "Ad", "upper": "\u00dcst s\u0131n\u0131r" }, @@ -28,7 +27,6 @@ "entity_id": "Giri\u015f sens\u00f6r\u00fc", "hysteresis": "Histeresis", "lower": "Alt s\u0131n\u0131r", - "mode": "E\u015fik modu", "name": "Ad", "upper": "\u00dcst s\u0131n\u0131r" }, diff --git a/homeassistant/components/threshold/translations/zh-Hans.json b/homeassistant/components/threshold/translations/zh-Hans.json index 35bc919f0d1..ae46afaa740 100644 --- a/homeassistant/components/threshold/translations/zh-Hans.json +++ b/homeassistant/components/threshold/translations/zh-Hans.json @@ -9,7 +9,6 @@ "entity_id": "\u8f93\u5165\u4f20\u611f\u5668", "hysteresis": "\u9632\u6296\u8303\u56f4", "lower": "\u4e0b\u9650", - "mode": "\u9608\u503c\u6a21\u5f0f", "name": "\u540d\u79f0", "upper": "\u4e0a\u9650" }, @@ -28,7 +27,6 @@ "entity_id": "\u8f93\u5165\u4f20\u611f\u5668", "hysteresis": "\u9632\u6296\u8303\u56f4", "lower": "\u4e0b\u9650", - "mode": "\u9608\u503c\u6a21\u5f0f", "name": "\u540d\u79f0", "upper": "\u4e0a\u9650" }, diff --git a/homeassistant/components/threshold/translations/zh-Hant.json b/homeassistant/components/threshold/translations/zh-Hant.json index 66bdfe6915a..45ff245bb02 100644 --- a/homeassistant/components/threshold/translations/zh-Hant.json +++ b/homeassistant/components/threshold/translations/zh-Hant.json @@ -9,7 +9,6 @@ "entity_id": "\u8f38\u5165\u611f\u6e2c\u5668", "hysteresis": "\u9072\u6eef", "lower": "\u4e0b\u9650", - "mode": "\u81e8\u754c\u9ede\u5f0f", "name": "\u540d\u7a31", "upper": "\u4e0a\u9650" }, @@ -28,7 +27,6 @@ "entity_id": "\u8f38\u5165\u611f\u6e2c\u5668", "hysteresis": "\u9072\u6eef", "lower": "\u4e0b\u9650", - "mode": "\u81e8\u754c\u9ede\u5f0f", "name": "\u540d\u7a31", "upper": "\u4e0a\u9650" }, diff --git a/homeassistant/components/tibber/translations/ca.json b/homeassistant/components/tibber/translations/ca.json index bfeb2416e57..c149bd67098 100644 --- a/homeassistant/components/tibber/translations/ca.json +++ b/homeassistant/components/tibber/translations/ca.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token d'acc\u00e9s" }, - "description": "Introdueix el token d'acc\u00e9s de https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Introdueix el token d'acc\u00e9s de https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/cs.json b/homeassistant/components/tibber/translations/cs.json index 278300caa53..441c716c0a0 100644 --- a/homeassistant/components/tibber/translations/cs.json +++ b/homeassistant/components/tibber/translations/cs.json @@ -12,8 +12,7 @@ "user": { "data": { "access_token": "P\u0159\u00edstupov\u00fd token" - }, - "title": "Tibber" + } } } } diff --git a/homeassistant/components/tibber/translations/de.json b/homeassistant/components/tibber/translations/de.json index f3f722ae835..2d8c853fcfd 100644 --- a/homeassistant/components/tibber/translations/de.json +++ b/homeassistant/components/tibber/translations/de.json @@ -13,8 +13,7 @@ "data": { "access_token": "Zugangstoken" }, - "description": "Gib dein Zugangsk\u00fcrzel von https://developer.tibber.com/settings/accesstoken ein.", - "title": "Tibber" + "description": "Gib dein Zugangsk\u00fcrzel von https://developer.tibber.com/settings/accesstoken ein." } } } diff --git a/homeassistant/components/tibber/translations/el.json b/homeassistant/components/tibber/translations/el.json index 0ad70ba6ac7..ea4cda01a8c 100644 --- a/homeassistant/components/tibber/translations/el.json +++ b/homeassistant/components/tibber/translations/el.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2" }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03ae\u03c2 \u03c3\u03b1\u03c2 \u03b1\u03c0\u03cc \u03c4\u03bf https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/en.json b/homeassistant/components/tibber/translations/en.json index 63df31dca07..64c8cf0ab1e 100644 --- a/homeassistant/components/tibber/translations/en.json +++ b/homeassistant/components/tibber/translations/en.json @@ -13,8 +13,7 @@ "data": { "access_token": "Access Token" }, - "description": "Enter your access token from https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Enter your access token from https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/es.json b/homeassistant/components/tibber/translations/es.json index cdf80422023..0840339ac24 100644 --- a/homeassistant/components/tibber/translations/es.json +++ b/homeassistant/components/tibber/translations/es.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token de acceso" }, - "description": "Introduzca su token de acceso desde https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Introduzca su token de acceso desde https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/et.json b/homeassistant/components/tibber/translations/et.json index 5edaac058c6..c00ab6821ad 100644 --- a/homeassistant/components/tibber/translations/et.json +++ b/homeassistant/components/tibber/translations/et.json @@ -13,8 +13,7 @@ "data": { "access_token": "Juurdep\u00e4\u00e4sut\u00f5end" }, - "description": "Sisesta oma juurdep\u00e4\u00e4suluba saidilt https://developer.tibber.com/settings/accesstoken", - "title": "" + "description": "Sisesta oma juurdep\u00e4\u00e4suluba saidilt https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/fi.json b/homeassistant/components/tibber/translations/fi.json deleted file mode 100644 index 87e0bef29b2..00000000000 --- a/homeassistant/components/tibber/translations/fi.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "config": { - "step": { - "user": { - "title": "Tibber" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/tibber/translations/fr.json b/homeassistant/components/tibber/translations/fr.json index 256516c44a6..1834878d731 100644 --- a/homeassistant/components/tibber/translations/fr.json +++ b/homeassistant/components/tibber/translations/fr.json @@ -13,8 +13,7 @@ "data": { "access_token": "Jeton d'acc\u00e8s" }, - "description": "Entrez votre jeton d'acc\u00e8s depuis https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Entrez votre jeton d'acc\u00e8s depuis https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/he.json b/homeassistant/components/tibber/translations/he.json index 3deeeef2e9c..d599be8e8cd 100644 --- a/homeassistant/components/tibber/translations/he.json +++ b/homeassistant/components/tibber/translations/he.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4" }, - "description": "\u05d4\u05d6\u05df \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d4\u05d2\u05d9\u05e9\u05d4 \u05e9\u05dc\u05da \u05de\u05behttps://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "\u05d4\u05d6\u05df \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d4\u05d2\u05d9\u05e9\u05d4 \u05e9\u05dc\u05da \u05de\u05behttps://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/hu.json b/homeassistant/components/tibber/translations/hu.json index 1ff558b7280..57731668a90 100644 --- a/homeassistant/components/tibber/translations/hu.json +++ b/homeassistant/components/tibber/translations/hu.json @@ -13,8 +13,7 @@ "data": { "access_token": "Hozz\u00e1f\u00e9r\u00e9si token" }, - "description": "Adja meg a hozz\u00e1f\u00e9r\u00e9si tokent a https://developer.tibber.com/settings/accesstoken c\u00edmr\u0151l", - "title": "Tibber" + "description": "Adja meg a hozz\u00e1f\u00e9r\u00e9si tokent a https://developer.tibber.com/settings/accesstoken c\u00edmr\u0151l" } } } diff --git a/homeassistant/components/tibber/translations/id.json b/homeassistant/components/tibber/translations/id.json index 479cf83f8c7..a80aec5ee3f 100644 --- a/homeassistant/components/tibber/translations/id.json +++ b/homeassistant/components/tibber/translations/id.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token Akses" }, - "description": "Masukkan token akses Anda dari https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Masukkan token akses Anda dari https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/it.json b/homeassistant/components/tibber/translations/it.json index aca7d94083a..2058c2260ad 100644 --- a/homeassistant/components/tibber/translations/it.json +++ b/homeassistant/components/tibber/translations/it.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token di accesso" }, - "description": "Immettere il token di accesso da https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Immettere il token di accesso da https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/ja.json b/homeassistant/components/tibber/translations/ja.json index ed3c2f71357..0d16d720159 100644 --- a/homeassistant/components/tibber/translations/ja.json +++ b/homeassistant/components/tibber/translations/ja.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3" }, - "description": "https://developer.tibber.com/settings/accesstoken \u304b\u3089\u306e\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u3092\u5165\u529b\u3057\u307e\u3059", - "title": "Tibber" + "description": "https://developer.tibber.com/settings/accesstoken \u304b\u3089\u306e\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u3092\u5165\u529b\u3057\u307e\u3059" } } } diff --git a/homeassistant/components/tibber/translations/ko.json b/homeassistant/components/tibber/translations/ko.json index 5ba1f62e4ed..ff15748a948 100644 --- a/homeassistant/components/tibber/translations/ko.json +++ b/homeassistant/components/tibber/translations/ko.json @@ -13,8 +13,7 @@ "data": { "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070" }, - "description": "https://developer.tibber.com/settings/accesstoken \uc5d0\uc11c \uc0dd\uc131\ud55c \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694", - "title": "Tibber" + "description": "https://developer.tibber.com/settings/accesstoken \uc5d0\uc11c \uc0dd\uc131\ud55c \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694" } } } diff --git a/homeassistant/components/tibber/translations/lb.json b/homeassistant/components/tibber/translations/lb.json index fc84bd1af6f..33ef47fe2de 100644 --- a/homeassistant/components/tibber/translations/lb.json +++ b/homeassistant/components/tibber/translations/lb.json @@ -13,8 +13,7 @@ "data": { "access_token": "Acc\u00e8ss Jeton" }, - "description": "F\u00ebll d\u00e4in Acc\u00e8s Jeton vun https://developer.tibber.com/settings/accesstoken aus", - "title": "Tibber" + "description": "F\u00ebll d\u00e4in Acc\u00e8s Jeton vun https://developer.tibber.com/settings/accesstoken aus" } } } diff --git a/homeassistant/components/tibber/translations/nl.json b/homeassistant/components/tibber/translations/nl.json index 4a5e518f306..622e35baea4 100644 --- a/homeassistant/components/tibber/translations/nl.json +++ b/homeassistant/components/tibber/translations/nl.json @@ -13,8 +13,7 @@ "data": { "access_token": "Toegangstoken" }, - "description": "Voer uw toegangstoken in van https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Voer uw toegangstoken in van https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/no.json b/homeassistant/components/tibber/translations/no.json index 14bf482e381..00d56edf5d4 100644 --- a/homeassistant/components/tibber/translations/no.json +++ b/homeassistant/components/tibber/translations/no.json @@ -13,8 +13,7 @@ "data": { "access_token": "Tilgangstoken" }, - "description": "Fyll inn din tilgangstoken fra [https://developer.tibber.com/settings/accesstoken](https://developer.tibber.com/settings/accesstoken)", - "title": "" + "description": "Fyll inn din tilgangstoken fra [https://developer.tibber.com/settings/accesstoken](https://developer.tibber.com/settings/accesstoken)" } } } diff --git a/homeassistant/components/tibber/translations/pl.json b/homeassistant/components/tibber/translations/pl.json index d62027da3d9..7019bb3d17b 100644 --- a/homeassistant/components/tibber/translations/pl.json +++ b/homeassistant/components/tibber/translations/pl.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token dost\u0119pu" }, - "description": "Wprowad\u017a token dost\u0119pu z https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Wprowad\u017a token dost\u0119pu z https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/pt-BR.json b/homeassistant/components/tibber/translations/pt-BR.json index 7ff66347009..57eb82ff948 100644 --- a/homeassistant/components/tibber/translations/pt-BR.json +++ b/homeassistant/components/tibber/translations/pt-BR.json @@ -13,8 +13,7 @@ "data": { "access_token": "Token de acesso" }, - "description": "Insira seu token de acesso em https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Insira seu token de acesso em https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/pt.json b/homeassistant/components/tibber/translations/pt.json index 941089ee0cb..50b5806457b 100644 --- a/homeassistant/components/tibber/translations/pt.json +++ b/homeassistant/components/tibber/translations/pt.json @@ -11,8 +11,7 @@ "user": { "data": { "access_token": "" - }, - "title": "" + } } } } diff --git a/homeassistant/components/tibber/translations/ru.json b/homeassistant/components/tibber/translations/ru.json index 7519f581352..6453e65830e 100644 --- a/homeassistant/components/tibber/translations/ru.json +++ b/homeassistant/components/tibber/translations/ru.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430" }, - "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/sl.json b/homeassistant/components/tibber/translations/sl.json index 33ec5ab773b..285c07434ce 100644 --- a/homeassistant/components/tibber/translations/sl.json +++ b/homeassistant/components/tibber/translations/sl.json @@ -12,8 +12,7 @@ "data": { "access_token": "Dostopni \u017eeton" }, - "description": "Vnesite svoj dostopni \u017eeton s strani https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "Vnesite svoj dostopni \u017eeton s strani https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/tr.json b/homeassistant/components/tibber/translations/tr.json index a34aab70d6c..d7a98f49760 100644 --- a/homeassistant/components/tibber/translations/tr.json +++ b/homeassistant/components/tibber/translations/tr.json @@ -13,8 +13,7 @@ "data": { "access_token": "Eri\u015fim Belirteci" }, - "description": "https://developer.tibber.com/settings/accesstoken adresinden eri\u015fim anahtar\u0131n\u0131z\u0131 girin", - "title": "Tibber" + "description": "https://developer.tibber.com/settings/accesstoken adresinden eri\u015fim anahtar\u0131n\u0131z\u0131 girin" } } } diff --git a/homeassistant/components/tibber/translations/uk.json b/homeassistant/components/tibber/translations/uk.json index b1240116856..ba38a372472 100644 --- a/homeassistant/components/tibber/translations/uk.json +++ b/homeassistant/components/tibber/translations/uk.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443" }, - "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443, \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u0438\u0439 \u043d\u0430 \u0441\u0430\u0439\u0442\u0456 https://developer.tibber.com/settings/accesstoken", - "title": "Tibber" + "description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443, \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u0438\u0439 \u043d\u0430 \u0441\u0430\u0439\u0442\u0456 https://developer.tibber.com/settings/accesstoken" } } } diff --git a/homeassistant/components/tibber/translations/zh-Hant.json b/homeassistant/components/tibber/translations/zh-Hant.json index e4d0ec10e23..3bc02f810d5 100644 --- a/homeassistant/components/tibber/translations/zh-Hant.json +++ b/homeassistant/components/tibber/translations/zh-Hant.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u5b58\u53d6\u6b0a\u6756" }, - "description": "\u8f38\u5165\u7531 https://developer.tibber.com/settings/accesstoken \u6240\u7372\u5f97\u7684\u5b58\u53d6\u6b0a\u6756", - "title": "Tibber" + "description": "\u8f38\u5165\u7531 https://developer.tibber.com/settings/accesstoken \u6240\u7372\u5f97\u7684\u5b58\u53d6\u6b0a\u6756" } } } diff --git a/homeassistant/components/tod/translations/ca.json b/homeassistant/components/tod/translations/ca.json index 3908cb7761f..2f62e5c8fb4 100644 --- a/homeassistant/components/tod/translations/ca.json +++ b/homeassistant/components/tod/translations/ca.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Activa despr\u00e9s de", "after_time": "Temps d'activaci\u00f3", - "before": "Desactiva despr\u00e9s de", "before_time": "Temps de desactivaci\u00f3", "name": "Nom" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Activa despr\u00e9s de", "after_time": "Temps d'activaci\u00f3", - "before": "Desactiva despr\u00e9s de", "before_time": "Temps de desactivaci\u00f3" - }, - "description": "Crea un sensor binari que s'activa o es desactiva en funci\u00f3 de l'hora." + } } } }, diff --git a/homeassistant/components/tod/translations/de.json b/homeassistant/components/tod/translations/de.json index eeb3d4dd8f9..663dc21c993 100644 --- a/homeassistant/components/tod/translations/de.json +++ b/homeassistant/components/tod/translations/de.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Angeschaltet nach", "after_time": "Einschaltzeit", - "before": "Ausgeschaltet nach", "before_time": "Ausschaltzeit", "name": "Name" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Angeschaltet nach", "after_time": "Einschaltzeit", - "before": "Ausgeschaltet nach", "before_time": "Ausschaltzeit" - }, - "description": "Erstelle einen bin\u00e4ren Sensor, der sich je nach Uhrzeit ein- oder ausschaltet." + } } } }, diff --git a/homeassistant/components/tod/translations/el.json b/homeassistant/components/tod/translations/el.json index ffa426365a4..453e36c4020 100644 --- a/homeassistant/components/tod/translations/el.json +++ b/homeassistant/components/tod/translations/el.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", "after_time": "\u0395\u03bd\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", - "before": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", "before_time": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", "after_time": "\u0395\u03bd\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5", - "before": "\u0391\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bc\u03b5\u03c4\u03ac", "before_time": "\u0395\u03ba\u03c4\u03cc\u03c2 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c0\u03cc\u03c4\u03b5 \u03bf \u03b1\u03b9\u03c3\u03b8\u03b7\u03c4\u03ae\u03c1\u03b1\u03c2 \u03b8\u03b1 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03ba\u03b1\u03b9 \u03bd\u03b1 \u03b1\u03c0\u03b5\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9." + } } } }, diff --git a/homeassistant/components/tod/translations/en.json b/homeassistant/components/tod/translations/en.json index ced14151519..2ecb2c695c8 100644 --- a/homeassistant/components/tod/translations/en.json +++ b/homeassistant/components/tod/translations/en.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "On after", "after_time": "On time", - "before": "Off after", "before_time": "Off time", "name": "Name" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "On after", "after_time": "On time", - "before": "Off after", "before_time": "Off time" - }, - "description": "Create a binary sensor that turns on or off depending on the time." + } } } }, diff --git a/homeassistant/components/tod/translations/es.json b/homeassistant/components/tod/translations/es.json index 899b4ce18f0..302dc6cfdd9 100644 --- a/homeassistant/components/tod/translations/es.json +++ b/homeassistant/components/tod/translations/es.json @@ -16,8 +16,7 @@ "init": { "data": { "after_time": "Tiempo de activaci\u00f3n" - }, - "description": "Crea un sensor binario que se activa o desactiva en funci\u00f3n de la hora." + } } } }, diff --git a/homeassistant/components/tod/translations/et.json b/homeassistant/components/tod/translations/et.json index 06b40e0ce72..9d3fb544309 100644 --- a/homeassistant/components/tod/translations/et.json +++ b/homeassistant/components/tod/translations/et.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Kestus sissel\u00fclitumisest", "after_time": "Seesoleku aeg", - "before": "Kestus v\u00e4ljal\u00fclitumisest", "before_time": "V\u00e4ljasoleku aeg", "name": "Nimi" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Kestus sissel\u00fclitumisest", "after_time": "Seesoleku aeg", - "before": "Kestus v\u00e4ljal\u00fclitumisest", "before_time": "V\u00e4ljasoleku aeg" - }, - "description": "Vali millal andur on sisse v\u00f5i v\u00e4lja l\u00fclitatud." + } } } }, diff --git a/homeassistant/components/tod/translations/fr.json b/homeassistant/components/tod/translations/fr.json index 799e286343b..d430ede1295 100644 --- a/homeassistant/components/tod/translations/fr.json +++ b/homeassistant/components/tod/translations/fr.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Activ\u00e9 apr\u00e8s", "after_time": "Heure d'activation", - "before": "D\u00e9sactiv\u00e9 apr\u00e8s", "before_time": "Heure de d\u00e9sactivation", "name": "Nom" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Activ\u00e9 apr\u00e8s", "after_time": "Heure d'activation", - "before": "D\u00e9sactiv\u00e9 apr\u00e8s", "before_time": "Heure de d\u00e9sactivation" - }, - "description": "Cr\u00e9ez un capteur binaire qui s'active et se d\u00e9sactive en fonction de l'heure." + } } } }, diff --git a/homeassistant/components/tod/translations/he.json b/homeassistant/components/tod/translations/he.json index b10f9e2b1ca..efa0a9dc244 100644 --- a/homeassistant/components/tod/translations/he.json +++ b/homeassistant/components/tod/translations/he.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u05d4\u05e4\u05e2\u05dc\u05d4 \u05dc\u05d0\u05d7\u05e8", "after_time": "\u05d6\u05de\u05df \u05d4\u05e4\u05e2\u05dc\u05d4", - "before": "\u05db\u05d9\u05d1\u05d5\u05d9 \u05dc\u05d0\u05d7\u05e8", "before_time": "\u05d6\u05de\u05df \u05db\u05d9\u05d1\u05d5\u05d9", "name": "\u05e9\u05dd" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u05d4\u05e4\u05e2\u05dc\u05d4 \u05dc\u05d0\u05d7\u05e8", "after_time": "\u05d6\u05de\u05df \u05d4\u05e4\u05e2\u05dc\u05d4", - "before": "\u05db\u05d9\u05d1\u05d5\u05d9 \u05dc\u05d0\u05d7\u05e8", "before_time": "\u05d6\u05de\u05df \u05db\u05d9\u05d1\u05d5\u05d9" - }, - "description": "\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d9\u05e9\u05df \u05d1\u05d9\u05e0\u05d0\u05e8\u05d9 \u05d4\u05de\u05d5\u05e4\u05e2\u05dc \u05d0\u05d5 \u05e0\u05db\u05d1\u05d4 \u05d1\u05d4\u05ea\u05d0\u05dd \u05dc\u05d6\u05de\u05df." + } } } } diff --git a/homeassistant/components/tod/translations/hu.json b/homeassistant/components/tod/translations/hu.json index 28af029f230..feb4d66e40e 100644 --- a/homeassistant/components/tod/translations/hu.json +++ b/homeassistant/components/tod/translations/hu.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "BE ut\u00e1n", "after_time": "BE id\u0151pont", - "before": "KI ut\u00e1n", "before_time": "KI id\u0151pont", "name": "Elnevez\u00e9s" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "BE ut\u00e1n", "after_time": "BE id\u0151pont", - "before": "KI ut\u00e1n", "before_time": "KI id\u0151pont" - }, - "description": "\u00c1ll\u00edtsa be, hogy az \u00e9rz\u00e9kel\u0151 mikor kapcsoljon be \u00e9s ki." + } } } }, diff --git a/homeassistant/components/tod/translations/id.json b/homeassistant/components/tod/translations/id.json index 2ceef6c3a40..2a3681e7d78 100644 --- a/homeassistant/components/tod/translations/id.json +++ b/homeassistant/components/tod/translations/id.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Nyala setelah", "after_time": "Nyala pada", - "before": "Mati setelah", "before_time": "Mati pada", "name": "Nama" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Nyala setelah", "after_time": "Nyala pada", - "before": "Mati setelah", "before_time": "Mati pada" - }, - "description": "Buat sensor biner yang nyala atau mati tergantung waktu." + } } } }, diff --git a/homeassistant/components/tod/translations/it.json b/homeassistant/components/tod/translations/it.json index 072cc80f5ff..69015bf02f7 100644 --- a/homeassistant/components/tod/translations/it.json +++ b/homeassistant/components/tod/translations/it.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Acceso dopo", "after_time": "Ora di accensione", - "before": "Spento dopo", "before_time": "Ora di spegnimento", "name": "Nome" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Acceso dopo", "after_time": "Ora di accensione", - "before": "Spento dopo", "before_time": "Ora di spegnimento" - }, - "description": "Crea un sensore binario che si accende o si spegne a seconda dell'ora." + } } } }, diff --git a/homeassistant/components/tod/translations/ja.json b/homeassistant/components/tod/translations/ja.json index a497f3e3c98..10bad2407d0 100644 --- a/homeassistant/components/tod/translations/ja.json +++ b/homeassistant/components/tod/translations/ja.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u5f8c\u306b\u30aa\u30f3(On after)", "after_time": "\u30aa\u30f3\u30bf\u30a4\u30e0(On time)", - "before": "\u30aa\u30d5\u5f8c(Off after)", "before_time": "\u30aa\u30d5\u30bf\u30a4\u30e0(Off time)", "name": "\u540d\u524d" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u5f8c\u306b\u30aa\u30f3(On after)", "after_time": "\u30aa\u30f3\u30bf\u30a4\u30e0(On time)", - "before": "\u30aa\u30d5\u5f8c(Off after)", "before_time": "\u30aa\u30d5\u30bf\u30a4\u30e0(Off time)" - }, - "description": "\u30bb\u30f3\u30b5\u30fc\u306e\u30aa\u30f3\u3068\u30aa\u30d5\u3092\u5207\u308a\u66ff\u3048\u308b\u30bf\u30a4\u30df\u30f3\u30b0\u3092\u69cb\u6210\u3057\u307e\u3059\u3002" + } } } }, diff --git a/homeassistant/components/tod/translations/ko.json b/homeassistant/components/tod/translations/ko.json new file mode 100644 index 00000000000..474497f6557 --- /dev/null +++ b/homeassistant/components/tod/translations/ko.json @@ -0,0 +1,3 @@ +{ + "title": "\ubc94\uc704\uc2dc\uac04 \uc13c\uc11c" +} \ No newline at end of file diff --git a/homeassistant/components/tod/translations/nl.json b/homeassistant/components/tod/translations/nl.json index 82f06ea3f4f..374e295f691 100644 --- a/homeassistant/components/tod/translations/nl.json +++ b/homeassistant/components/tod/translations/nl.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Aan na", "after_time": "Op tijd", - "before": "Uit na", "before_time": "Uit tijd", "name": "Naam\n" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Aan na", "after_time": "Op tijd", - "before": "Uit na", "before_time": "Uit tijd" - }, - "description": "Maak een binaire sensor die afhankelijk van de tijd in- of uitgeschakeld wordt." + } } } }, diff --git a/homeassistant/components/tod/translations/no.json b/homeassistant/components/tod/translations/no.json index a2cbf7e6427..196c00663fe 100644 --- a/homeassistant/components/tod/translations/no.json +++ b/homeassistant/components/tod/translations/no.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "P\u00e5 etter", "after_time": "P\u00e5 tide", - "before": "Av etter", "before_time": "Utenfor arbeidstid", "name": "Navn" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "P\u00e5 etter", "after_time": "P\u00e5 tide", - "before": "Av etter", "before_time": "Utenfor arbeidstid" - }, - "description": "Lag en bin\u00e6r sensor som sl\u00e5s av eller p\u00e5 avhengig av tiden." + } } } }, diff --git a/homeassistant/components/tod/translations/pl.json b/homeassistant/components/tod/translations/pl.json index 8b03a798f5c..14d74f12b27 100644 --- a/homeassistant/components/tod/translations/pl.json +++ b/homeassistant/components/tod/translations/pl.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "W\u0142\u0105cz po", "after_time": "Czas w\u0142\u0105czenia", - "before": "Wy\u0142\u0105cz po", "before_time": "Czas wy\u0142\u0105czenia", "name": "Nazwa" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "W\u0142\u0105cz po", "after_time": "Czas w\u0142\u0105czenia", - "before": "Wy\u0142\u0105cz po", "before_time": "Czas wy\u0142\u0105czenia" - }, - "description": "Utw\u00f3rz sensor binarny, kt\u00f3ry w\u0142\u0105cza si\u0119 lub wy\u0142\u0105cza w zale\u017cno\u015bci od czasu." + } } } }, diff --git a/homeassistant/components/tod/translations/pt-BR.json b/homeassistant/components/tod/translations/pt-BR.json index e9784076fd7..0275ad3e148 100644 --- a/homeassistant/components/tod/translations/pt-BR.json +++ b/homeassistant/components/tod/translations/pt-BR.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Ligado depois", "after_time": "Ligado na hora", - "before": "Desligado depois", "before_time": "Desligado na hora", "name": "Nome" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Ligado depois", "after_time": "Ligado na hora", - "before": "Desligado depois", "before_time": "Desligado na hora" - }, - "description": "Crie um sensor bin\u00e1rio que liga ou desliga dependendo do tempo." + } } } }, diff --git a/homeassistant/components/tod/translations/ru.json b/homeassistant/components/tod/translations/ru.json index eda3e4efd77..c470dea348d 100644 --- a/homeassistant/components/tod/translations/ru.json +++ b/homeassistant/components/tod/translations/ru.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", "after_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", - "before": "\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", "before_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", "after_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f", - "before": "\u0412\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435", "before_time": "\u0412\u0440\u0435\u043c\u044f \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f" - }, - "description": "\u0411\u0438\u043d\u0430\u0440\u043d\u044b\u0439 \u0441\u0435\u043d\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0438\u043b\u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438." + } } } }, diff --git a/homeassistant/components/tod/translations/tr.json b/homeassistant/components/tod/translations/tr.json index e2f04757369..8013dff322b 100644 --- a/homeassistant/components/tod/translations/tr.json +++ b/homeassistant/components/tod/translations/tr.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "Daha sonra", "after_time": "Vaktinde", - "before": "Sonra kapat", "before_time": "Kapatma zaman\u0131", "name": "Ad" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "Daha sonra", "after_time": "Vaktinde", - "before": "Sonra kapat", "before_time": "Kapatma zaman\u0131" - }, - "description": "Zamana ba\u011fl\u0131 olarak a\u00e7\u0131l\u0131p kapanan bir ikili sens\u00f6r olu\u015fturun." + } } } }, diff --git a/homeassistant/components/tod/translations/zh-Hans.json b/homeassistant/components/tod/translations/zh-Hans.json index 1c0b6ba1597..32306b99659 100644 --- a/homeassistant/components/tod/translations/zh-Hans.json +++ b/homeassistant/components/tod/translations/zh-Hans.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u5728\u6b64\u65f6\u95f4\u540e\u6253\u5f00", "after_time": "\u6253\u5f00\u65f6\u95f4", - "before": "\u5728\u6b64\u65f6\u95f4\u540e\u5173\u95ed", "before_time": "\u5173\u95ed\u65f6\u95f4", "name": "\u540d\u79f0" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u5728\u6b64\u65f6\u95f4\u540e\u6253\u5f00", "after_time": "\u6253\u5f00\u65f6\u95f4", - "before": "\u5728\u6b64\u65f6\u95f4\u540e\u5173\u95ed", "before_time": "\u5173\u95ed\u65f6\u95f4" - }, - "description": "\u521b\u5efa\u6839\u636e\u65f6\u95f4\u6539\u53d8\u5f00\u5173\u72b6\u6001\u7684\u4e8c\u5143\u4f20\u611f\u5668\u3002" + } } } }, diff --git a/homeassistant/components/tod/translations/zh-Hant.json b/homeassistant/components/tod/translations/zh-Hant.json index 0a47c33a318..333d092a613 100644 --- a/homeassistant/components/tod/translations/zh-Hant.json +++ b/homeassistant/components/tod/translations/zh-Hant.json @@ -3,9 +3,7 @@ "step": { "user": { "data": { - "after": "\u958b\u555f\u6642\u9577\u5f8c", "after_time": "\u958b\u555f\u6642\u9593", - "before": "\u95dc\u9589\u6642\u9577\u5f8c", "before_time": "\u95dc\u9589\u6642\u9593", "name": "\u540d\u7a31" }, @@ -18,12 +16,9 @@ "step": { "init": { "data": { - "after": "\u958b\u555f\u6642\u9577\u5f8c", "after_time": "\u958b\u555f\u6642\u9593", - "before": "\u95dc\u9589\u6642\u9577\u5f8c", "before_time": "\u95dc\u9589\u6642\u9593" - }, - "description": "\u65b0\u589e\u6839\u64da\u6642\u9593\u6c7a\u5b9a\u958b\u95dc\u4e4b\u6642\u9593\u611f\u6e2c\u5668\u3002" + } } } }, diff --git a/homeassistant/components/tolo/translations/bg.json b/homeassistant/components/tolo/translations/bg.json index f1c33573305..5f89c6bd4bd 100644 --- a/homeassistant/components/tolo/translations/bg.json +++ b/homeassistant/components/tolo/translations/bg.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "no_devices_found": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430" + "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e" }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" diff --git a/homeassistant/components/tolo/translations/ca.json b/homeassistant/components/tolo/translations/ca.json index 06d201141a5..567f4467f0b 100644 --- a/homeassistant/components/tolo/translations/ca.json +++ b/homeassistant/components/tolo/translations/ca.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositiu ja est\u00e0 configurat", - "no_devices_found": "No s'han trobat dispositius a la xarxa" + "already_configured": "El dispositiu ja est\u00e0 configurat" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3" diff --git a/homeassistant/components/tolo/translations/de.json b/homeassistant/components/tolo/translations/de.json index 6002d2ada8b..3fc78255507 100644 --- a/homeassistant/components/tolo/translations/de.json +++ b/homeassistant/components/tolo/translations/de.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden" + "already_configured": "Ger\u00e4t ist bereits konfiguriert" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" diff --git a/homeassistant/components/tolo/translations/el.json b/homeassistant/components/tolo/translations/el.json index 6b745e37a8e..16604a30baa 100644 --- a/homeassistant/components/tolo/translations/el.json +++ b/homeassistant/components/tolo/translations/el.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf" + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" diff --git a/homeassistant/components/tolo/translations/en.json b/homeassistant/components/tolo/translations/en.json index 488c2f7ae69..dea5a3b30df 100644 --- a/homeassistant/components/tolo/translations/en.json +++ b/homeassistant/components/tolo/translations/en.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Device is already configured", - "no_devices_found": "No devices found on the network" + "already_configured": "Device is already configured" }, "error": { "cannot_connect": "Failed to connect" diff --git a/homeassistant/components/tolo/translations/es.json b/homeassistant/components/tolo/translations/es.json index 573d6b7c344..76cb6c73275 100644 --- a/homeassistant/components/tolo/translations/es.json +++ b/homeassistant/components/tolo/translations/es.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "El dispositivo ya est\u00e1 configurado", - "no_devices_found": "No se encontraron dispositivos en la red" + "already_configured": "El dispositivo ya est\u00e1 configurado" }, "error": { "cannot_connect": "No se pudo conectar" diff --git a/homeassistant/components/tolo/translations/et.json b/homeassistant/components/tolo/translations/et.json index 57d59b85713..ed09907df82 100644 --- a/homeassistant/components/tolo/translations/et.json +++ b/homeassistant/components/tolo/translations/et.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "no_devices_found": "V\u00f5rgust ei leitud \u00fchtegi seadet" + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" }, "error": { "cannot_connect": "\u00dchendamine nurjus" diff --git a/homeassistant/components/tolo/translations/fr.json b/homeassistant/components/tolo/translations/fr.json index 40b61d012a2..c27da305516 100644 --- a/homeassistant/components/tolo/translations/fr.json +++ b/homeassistant/components/tolo/translations/fr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", - "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau" + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" }, "error": { "cannot_connect": "\u00c9chec de connexion" diff --git a/homeassistant/components/tolo/translations/he.json b/homeassistant/components/tolo/translations/he.json index 9da8a69a4fe..39ea73680a3 100644 --- a/homeassistant/components/tolo/translations/he.json +++ b/homeassistant/components/tolo/translations/he.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" diff --git a/homeassistant/components/tolo/translations/hu.json b/homeassistant/components/tolo/translations/hu.json index 55239599c16..246edba220d 100644 --- a/homeassistant/components/tolo/translations/hu.json +++ b/homeassistant/components/tolo/translations/hu.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", - "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton" + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" }, "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s" diff --git a/homeassistant/components/tolo/translations/id.json b/homeassistant/components/tolo/translations/id.json index 53ea0e46cb1..88e22f45b0b 100644 --- a/homeassistant/components/tolo/translations/id.json +++ b/homeassistant/components/tolo/translations/id.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Perangkat sudah dikonfigurasi", - "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan" + "already_configured": "Perangkat sudah dikonfigurasi" }, "error": { "cannot_connect": "Gagal terhubung" diff --git a/homeassistant/components/tolo/translations/it.json b/homeassistant/components/tolo/translations/it.json index 83704e3e767..169aaa7ebce 100644 --- a/homeassistant/components/tolo/translations/it.json +++ b/homeassistant/components/tolo/translations/it.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "no_devices_found": "Nessun dispositivo trovato sulla rete" + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" }, "error": { "cannot_connect": "Impossibile connettersi" diff --git a/homeassistant/components/tolo/translations/ja.json b/homeassistant/components/tolo/translations/ja.json index f8d4a1646ae..f901986b6d2 100644 --- a/homeassistant/components/tolo/translations/ja.json +++ b/homeassistant/components/tolo/translations/ja.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093" + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" diff --git a/homeassistant/components/tolo/translations/ko.json b/homeassistant/components/tolo/translations/ko.json new file mode 100644 index 00000000000..20ad990e862 --- /dev/null +++ b/homeassistant/components/tolo/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/tolo/translations/nl.json b/homeassistant/components/tolo/translations/nl.json index f65f6bae7c1..dcc6f1936d0 100644 --- a/homeassistant/components/tolo/translations/nl.json +++ b/homeassistant/components/tolo/translations/nl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd", - "no_devices_found": "Geen apparaten gevonden op het netwerk" + "already_configured": "Apparaat is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/tolo/translations/no.json b/homeassistant/components/tolo/translations/no.json index 20311dd8f69..16b9c0f2ead 100644 --- a/homeassistant/components/tolo/translations/no.json +++ b/homeassistant/components/tolo/translations/no.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Enheten er allerede konfigurert", - "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket" + "already_configured": "Enheten er allerede konfigurert" }, "error": { "cannot_connect": "Tilkobling mislyktes" diff --git a/homeassistant/components/tolo/translations/pl.json b/homeassistant/components/tolo/translations/pl.json index 4809e00e29f..0bf5b5f75f5 100644 --- a/homeassistant/components/tolo/translations/pl.json +++ b/homeassistant/components/tolo/translations/pl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci" + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" diff --git a/homeassistant/components/tolo/translations/pt-BR.json b/homeassistant/components/tolo/translations/pt-BR.json index c86e10b4e8e..457fb339b2d 100644 --- a/homeassistant/components/tolo/translations/pt-BR.json +++ b/homeassistant/components/tolo/translations/pt-BR.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "no_devices_found": "Nenhum dispositivo encontrado na rede" + "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado" }, "error": { "cannot_connect": "Falha ao conectar" diff --git a/homeassistant/components/tolo/translations/ru.json b/homeassistant/components/tolo/translations/ru.json index 82fdffdb8b1..0243a40cf7e 100644 --- a/homeassistant/components/tolo/translations/ru.json +++ b/homeassistant/components/tolo/translations/ru.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438." + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." diff --git a/homeassistant/components/tolo/translations/sl.json b/homeassistant/components/tolo/translations/sl.json index e32b3eb95ca..62f8746a028 100644 --- a/homeassistant/components/tolo/translations/sl.json +++ b/homeassistant/components/tolo/translations/sl.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Naprava je \u017ee konfigurirana", - "no_devices_found": "V omre\u017eju ni mogo\u010de najti nobene naprave" + "already_configured": "Naprava je \u017ee konfigurirana" }, "error": { "cannot_connect": "Povezava ni uspela" diff --git a/homeassistant/components/tolo/translations/tr.json b/homeassistant/components/tolo/translations/tr.json index f5dd98e93ba..8f2b5ee1939 100644 --- a/homeassistant/components/tolo/translations/tr.json +++ b/homeassistant/components/tolo/translations/tr.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "no_devices_found": "A\u011fda cihaz bulunamad\u0131" + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131" diff --git a/homeassistant/components/tolo/translations/zh-Hant.json b/homeassistant/components/tolo/translations/zh-Hant.json index d887eb212a1..96cef3b9605 100644 --- a/homeassistant/components/tolo/translations/zh-Hant.json +++ b/homeassistant/components/tolo/translations/zh-Hant.json @@ -1,8 +1,7 @@ { "config": { "abort": { - "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e" + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557" diff --git a/homeassistant/components/tomorrowio/translations/bg.json b/homeassistant/components/tomorrowio/translations/bg.json index 783408dab84..261841d75d1 100644 --- a/homeassistant/components/tomorrowio/translations/bg.json +++ b/homeassistant/components/tomorrowio/translations/bg.json @@ -9,9 +9,7 @@ "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447", - "latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430", "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", - "longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430", "name": "\u0418\u043c\u0435" } } diff --git a/homeassistant/components/tomorrowio/translations/ca.json b/homeassistant/components/tomorrowio/translations/ca.json index fc351430ffb..6a1289fd7a4 100644 --- a/homeassistant/components/tomorrowio/translations/ca.json +++ b/homeassistant/components/tomorrowio/translations/ca.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Clau API", - "latitude": "Latitud", "location": "Ubicaci\u00f3", - "longitude": "Longitud", "name": "Nom" }, "description": "Per obtenir una clau API, registra't a [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/de.json b/homeassistant/components/tomorrowio/translations/de.json index 03739ce5c2f..d65b1115e4b 100644 --- a/homeassistant/components/tomorrowio/translations/de.json +++ b/homeassistant/components/tomorrowio/translations/de.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API-Schl\u00fcssel", - "latitude": "Breitengrad", "location": "Standort", - "longitude": "L\u00e4ngengrad", "name": "Name" }, "description": "Um einen API-Schl\u00fcssel zu erhalten, melde dich bei [Tomorrow.io] (https://app.tomorrow.io/signup) an." diff --git a/homeassistant/components/tomorrowio/translations/el.json b/homeassistant/components/tomorrowio/translations/el.json index 28e3f56c379..aecdca57c9f 100644 --- a/homeassistant/components/tomorrowio/translations/el.json +++ b/homeassistant/components/tomorrowio/translations/el.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "latitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03c0\u03bb\u03ac\u03c4\u03bf\u03c2", "location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1", - "longitude": "\u0393\u03b5\u03c9\u03b3\u03c1\u03b1\u03c6\u03b9\u03ba\u03cc \u03bc\u03ae\u03ba\u03bf\u03c2", "name": "\u038c\u03bd\u03bf\u03bc\u03b1" }, "description": "\u0393\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API, \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/en.json b/homeassistant/components/tomorrowio/translations/en.json index 103f1c81679..f3706dd6a73 100644 --- a/homeassistant/components/tomorrowio/translations/en.json +++ b/homeassistant/components/tomorrowio/translations/en.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API Key", - "latitude": "Latitude", "location": "Location", - "longitude": "Longitude", "name": "Name" }, "description": "To get an API key, sign up at [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/es.json b/homeassistant/components/tomorrowio/translations/es.json index 9ad330dab15..e2f36a0949e 100644 --- a/homeassistant/components/tomorrowio/translations/es.json +++ b/homeassistant/components/tomorrowio/translations/es.json @@ -9,7 +9,6 @@ "user": { "data": { "api_key": "Clave API", - "latitude": "Latitud", "location": "Ubicaci\u00f3n", "name": "Nombre" }, diff --git a/homeassistant/components/tomorrowio/translations/et.json b/homeassistant/components/tomorrowio/translations/et.json index 7ac38239349..0ae1ea43448 100644 --- a/homeassistant/components/tomorrowio/translations/et.json +++ b/homeassistant/components/tomorrowio/translations/et.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API v\u00f5ti", - "latitude": "Laiuskraad", "location": "Asukoht", - "longitude": "Pikkuskraad", "name": "Nimi" }, "description": "API v\u00f5tme saamiseks registreeru aadressil [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/fr.json b/homeassistant/components/tomorrowio/translations/fr.json index bcb97eb3fc5..559c734612f 100644 --- a/homeassistant/components/tomorrowio/translations/fr.json +++ b/homeassistant/components/tomorrowio/translations/fr.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Cl\u00e9 d'API", - "latitude": "Latitude", "location": "Emplacement", - "longitude": "Longitude", "name": "Nom" }, "description": "Pour obtenir une cl\u00e9 d'API, inscrivez-vous sur [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/he.json b/homeassistant/components/tomorrowio/translations/he.json index 20b48520f18..153e6fcd5f5 100644 --- a/homeassistant/components/tomorrowio/translations/he.json +++ b/homeassistant/components/tomorrowio/translations/he.json @@ -9,9 +9,7 @@ "user": { "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", - "latitude": "\u05e7\u05d5 \u05e8\u05d5\u05d7\u05d1", "location": "\u05de\u05d9\u05e7\u05d5\u05dd", - "longitude": "\u05e7\u05d5 \u05d0\u05d5\u05e8\u05da", "name": "\u05e9\u05dd" } } diff --git a/homeassistant/components/tomorrowio/translations/hu.json b/homeassistant/components/tomorrowio/translations/hu.json index 8f90392234e..d619b6346a4 100644 --- a/homeassistant/components/tomorrowio/translations/hu.json +++ b/homeassistant/components/tomorrowio/translations/hu.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API kulcs", - "latitude": "Sz\u00e9less\u00e9g", "location": "Elhelyezked\u00e9s", - "longitude": "Hossz\u00fas\u00e1g", "name": "Elnevez\u00e9s" }, "description": "API-kulcs beszerz\u00e9s\u00e9hez regisztr\u00e1ljon a [Tomorrow.io] (https://app.tomorrow.io/signup) oldalon." diff --git a/homeassistant/components/tomorrowio/translations/id.json b/homeassistant/components/tomorrowio/translations/id.json index b428648e799..a1a6f41888a 100644 --- a/homeassistant/components/tomorrowio/translations/id.json +++ b/homeassistant/components/tomorrowio/translations/id.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Kunci API", - "latitude": "Lintang", "location": "Lokasi", - "longitude": "Bujur", "name": "Nama" }, "description": "Untuk mendapatkan kunci API, daftar di [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/it.json b/homeassistant/components/tomorrowio/translations/it.json index 9a79896a3d8..8446eba5336 100644 --- a/homeassistant/components/tomorrowio/translations/it.json +++ b/homeassistant/components/tomorrowio/translations/it.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Chiave API", - "latitude": "Latitudine", "location": "Posizione", - "longitude": "Logitudine", "name": "Nome" }, "description": "Per ottenere una chiave API, registrati su [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/ja.json b/homeassistant/components/tomorrowio/translations/ja.json index 17d31f74214..c935416c89b 100644 --- a/homeassistant/components/tomorrowio/translations/ja.json +++ b/homeassistant/components/tomorrowio/translations/ja.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API\u30ad\u30fc", - "latitude": "\u7def\u5ea6", "location": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3", - "longitude": "\u7d4c\u5ea6", "name": "\u540d\u524d" }, "description": "API\u30ad\u30fc\u3092\u53d6\u5f97\u3059\u308b\u306b\u306f\u3001 [Tomorrow.io](https://app.tomorrow.io/signup) \u306b\u30b5\u30a4\u30f3\u30a2\u30c3\u30d7\u3057\u3066\u304f\u3060\u3055\u3044\u3002" diff --git a/homeassistant/components/tomorrowio/translations/nl.json b/homeassistant/components/tomorrowio/translations/nl.json index d1efb7d75c3..8b6b585ef11 100644 --- a/homeassistant/components/tomorrowio/translations/nl.json +++ b/homeassistant/components/tomorrowio/translations/nl.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API-sleutel", - "latitude": "Breedtegraad", "location": "Locatie", - "longitude": "Lengtegraad", "name": "Naam" }, "description": "Om een API sleutel te krijgen, meld je aan bij [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/no.json b/homeassistant/components/tomorrowio/translations/no.json index bf366895a70..acab85a03a4 100644 --- a/homeassistant/components/tomorrowio/translations/no.json +++ b/homeassistant/components/tomorrowio/translations/no.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API-n\u00f8kkel", - "latitude": "Breddegrad", "location": "Plassering", - "longitude": "Lengdegrad", "name": "Navn" }, "description": "For \u00e5 f\u00e5 en API-n\u00f8kkel, registrer deg p\u00e5 [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/pl.json b/homeassistant/components/tomorrowio/translations/pl.json index 4637a553aa4..b715d9e2cab 100644 --- a/homeassistant/components/tomorrowio/translations/pl.json +++ b/homeassistant/components/tomorrowio/translations/pl.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Klucz API", - "latitude": "Szeroko\u015b\u0107 geograficzna", "location": "Lokalizacja", - "longitude": "D\u0142ugo\u015b\u0107 geograficzna", "name": "Nazwa" }, "description": "Aby uzyska\u0107 klucz API, zarejestruj si\u0119 na [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/pt-BR.json b/homeassistant/components/tomorrowio/translations/pt-BR.json index 83f78e31b8e..d0b7a33abc9 100644 --- a/homeassistant/components/tomorrowio/translations/pt-BR.json +++ b/homeassistant/components/tomorrowio/translations/pt-BR.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "Chave da API", - "latitude": "Latitude", "location": "Localiza\u00e7\u00e3o", - "longitude": "Longitude", "name": "Nome" }, "description": "Para obter uma chave de API, inscreva-se em [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/ru.json b/homeassistant/components/tomorrowio/translations/ru.json index 08e24c5a47a..eeb7eb58488 100644 --- a/homeassistant/components/tomorrowio/translations/ru.json +++ b/homeassistant/components/tomorrowio/translations/ru.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", - "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", - "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" }, "description": "\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043b\u044e\u0447 API, \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435\u0441\u044c \u043d\u0430 [Tomorrow.io](https://app.tomorrow.io/signup)." diff --git a/homeassistant/components/tomorrowio/translations/tr.json b/homeassistant/components/tomorrowio/translations/tr.json index 4193428459c..61802d7f328 100644 --- a/homeassistant/components/tomorrowio/translations/tr.json +++ b/homeassistant/components/tomorrowio/translations/tr.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API Anahtar\u0131", - "latitude": "Enlem", "location": "Konum", - "longitude": "Boylam", "name": "Ad" }, "description": "API anahtar\u0131 almak i\u00e7in [Tomorrow.io](https://app.tomorrow.io/signup) adresinden kaydolun." diff --git a/homeassistant/components/tomorrowio/translations/zh-Hant.json b/homeassistant/components/tomorrowio/translations/zh-Hant.json index bf7043b0225..00f5d5e0fcc 100644 --- a/homeassistant/components/tomorrowio/translations/zh-Hant.json +++ b/homeassistant/components/tomorrowio/translations/zh-Hant.json @@ -10,9 +10,7 @@ "user": { "data": { "api_key": "API \u91d1\u9470", - "latitude": "\u7def\u5ea6", "location": "\u5ea7\u6a19", - "longitude": "\u7d93\u5ea6", "name": "\u540d\u7a31" }, "description": "\u8acb\u53c3\u95b1\u7db2\u5740\u4ee5\u4e86\u89e3\u5982\u4f55\u53d6\u5f97 API \u91d1\u9470\uff1a[Tomorrow.io](https://app.tomorrow.io/signup)\u3002" diff --git a/homeassistant/components/totalconnect/translations/ca.json b/homeassistant/components/totalconnect/translations/ca.json index 0edad920cdf..404e07d6b69 100644 --- a/homeassistant/components/totalconnect/translations/ca.json +++ b/homeassistant/components/totalconnect/translations/ca.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Ubicaci\u00f3", "usercode": "Codi d'usuari" }, "description": "Introdueix el codi de l'usuari en la ubicaci\u00f3 {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Contrasenya", "username": "Nom d'usuari" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/cs.json b/homeassistant/components/totalconnect/translations/cs.json index ad3b8dd6618..ed81d8cafd1 100644 --- a/homeassistant/components/totalconnect/translations/cs.json +++ b/homeassistant/components/totalconnect/translations/cs.json @@ -8,11 +8,6 @@ "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "step": { - "locations": { - "data": { - "location": "Um\u00edst\u011bn\u00ed" - } - }, "reauth_confirm": { "title": "Znovu ov\u011b\u0159it integraci" }, @@ -20,8 +15,7 @@ "data": { "password": "Heslo", "username": "U\u017eivatelsk\u00e9 jm\u00e9no" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/de.json b/homeassistant/components/totalconnect/translations/de.json index b89b99fc2d4..890bbb753d9 100644 --- a/homeassistant/components/totalconnect/translations/de.json +++ b/homeassistant/components/totalconnect/translations/de.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Standort", "usercode": "Benutzercode" }, "description": "Gib den Benutzercode f\u00fcr den Benutzer {location_id} an dieser Stelle ein", @@ -26,8 +25,7 @@ "data": { "password": "Passwort", "username": "Benutzername" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/el.json b/homeassistant/components/totalconnect/translations/el.json index e2e19a56b0a..4f60b082484 100644 --- a/homeassistant/components/totalconnect/translations/el.json +++ b/homeassistant/components/totalconnect/translations/el.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "\u03a4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1", "usercode": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c3\u03c4\u03b7\u03bd \u03c4\u03bf\u03c0\u03bf\u03b8\u03b5\u03c3\u03af\u03b1 {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/en.json b/homeassistant/components/totalconnect/translations/en.json index f3a96550cba..8df2b00a936 100644 --- a/homeassistant/components/totalconnect/translations/en.json +++ b/homeassistant/components/totalconnect/translations/en.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Location", "usercode": "Usercode" }, "description": "Enter the usercode for this user at location {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Password", "username": "Username" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/es-419.json b/homeassistant/components/totalconnect/translations/es-419.json index b8507f2b8a7..7560aaa35d2 100644 --- a/homeassistant/components/totalconnect/translations/es-419.json +++ b/homeassistant/components/totalconnect/translations/es-419.json @@ -8,8 +8,7 @@ "data": { "password": "Contrase\u00f1a", "username": "Nombre de usuario" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/es.json b/homeassistant/components/totalconnect/translations/es.json index 97822a10300..7983aa3a11e 100644 --- a/homeassistant/components/totalconnect/translations/es.json +++ b/homeassistant/components/totalconnect/translations/es.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Localizaci\u00f3n", "usercode": "Codigo de usuario" }, "description": "Introduce el c\u00f3digo de usuario para este usuario en la ubicaci\u00f3n {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Contrase\u00f1a", "username": "Usuario" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/et.json b/homeassistant/components/totalconnect/translations/et.json index e2df8dd51e8..c4bca75a558 100644 --- a/homeassistant/components/totalconnect/translations/et.json +++ b/homeassistant/components/totalconnect/translations/et.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Asukoht", "usercode": "Kasutajakood" }, "description": "Sisesta kasutaja kood asukohale {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Salas\u00f5na", "username": "Kasutajanimi" - }, - "title": "" + } } } } diff --git a/homeassistant/components/totalconnect/translations/fr.json b/homeassistant/components/totalconnect/translations/fr.json index f6dcefac2bb..fcda553a018 100644 --- a/homeassistant/components/totalconnect/translations/fr.json +++ b/homeassistant/components/totalconnect/translations/fr.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Emplacement", "usercode": "Code d'utilisateur" }, "description": "Entrez le code utilisateur pour cet utilisateur \u00e0 l'emplacement {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Mot de passe", "username": "Nom d'utilisateur" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/he.json b/homeassistant/components/totalconnect/translations/he.json index 712c12a1062..ec280bc5f69 100644 --- a/homeassistant/components/totalconnect/translations/he.json +++ b/homeassistant/components/totalconnect/translations/he.json @@ -10,7 +10,6 @@ "step": { "locations": { "data": { - "location": "\u05de\u05d9\u05e7\u05d5\u05dd", "usercode": "\u05e7\u05d5\u05d3 \u05de\u05e9\u05ea\u05de\u05e9" } }, diff --git a/homeassistant/components/totalconnect/translations/hu.json b/homeassistant/components/totalconnect/translations/hu.json index 3d40f84d262..3bb2b4136c9 100644 --- a/homeassistant/components/totalconnect/translations/hu.json +++ b/homeassistant/components/totalconnect/translations/hu.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Elhelyezked\u00e9s", "usercode": "Felhaszn\u00e1l\u00f3i k\u00f3d" }, "description": "Adja meg ennek a felhaszn\u00e1l\u00f3i k\u00f3dj\u00e1t a k\u00f6vetkez\u0151 helyen: {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Jelsz\u00f3", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/id.json b/homeassistant/components/totalconnect/translations/id.json index 0c2cbbfc6e2..1702ceb5688 100644 --- a/homeassistant/components/totalconnect/translations/id.json +++ b/homeassistant/components/totalconnect/translations/id.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Lokasi", "usercode": "Kode pengguna" }, "description": "Masukkan kode pengguna untuk pengguna ini di lokasi {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Kata Sandi", "username": "Nama Pengguna" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/it.json b/homeassistant/components/totalconnect/translations/it.json index ee66d81d83d..32f0815d380 100644 --- a/homeassistant/components/totalconnect/translations/it.json +++ b/homeassistant/components/totalconnect/translations/it.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Posizione", "usercode": "Codice utente" }, "description": "Inserisci il codice utente per questo utente nella posizione {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Password", "username": "Nome utente" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/ja.json b/homeassistant/components/totalconnect/translations/ja.json index 7eefbc3a873..c20b55d5583 100644 --- a/homeassistant/components/totalconnect/translations/ja.json +++ b/homeassistant/components/totalconnect/translations/ja.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3", "usercode": "\u30e6\u30fc\u30b6\u30fc\u30b3\u30fc\u30c9" }, "description": "\u3053\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30e6\u30fc\u30b6\u30fc\u30b3\u30fc\u30c9\u3092\u5834\u6240 {location_id} \u306b\u5165\u529b\u3057\u307e\u3059", @@ -26,8 +25,7 @@ "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", "username": "\u30e6\u30fc\u30b6\u30fc\u540d" - }, - "title": "\u30c8\u30fc\u30bf\u30eb\u30b3\u30cd\u30af\u30c8" + } } } } diff --git a/homeassistant/components/totalconnect/translations/ko.json b/homeassistant/components/totalconnect/translations/ko.json index 246b62d5500..e88b9aeb451 100644 --- a/homeassistant/components/totalconnect/translations/ko.json +++ b/homeassistant/components/totalconnect/translations/ko.json @@ -11,9 +11,6 @@ }, "step": { "locations": { - "data": { - "location": "\uc704\uce58" - }, "description": "\uc774 \uc704\uce58\uc758 \ud574\ub2f9 \uc0ac\uc6a9\uc790\uc5d0 \ub300\ud55c \uc0ac\uc6a9\uc790 \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694", "title": "\uc704\uce58 \uc0ac\uc6a9\uc790 \ucf54\ub4dc" }, @@ -25,8 +22,7 @@ "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/lb.json b/homeassistant/components/totalconnect/translations/lb.json index af76c38d918..025e5f657da 100644 --- a/homeassistant/components/totalconnect/translations/lb.json +++ b/homeassistant/components/totalconnect/translations/lb.json @@ -11,8 +11,7 @@ "data": { "password": "Passwuert", "username": "Benotzernumm" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/nl.json b/homeassistant/components/totalconnect/translations/nl.json index 674818d8428..186e2f60d50 100644 --- a/homeassistant/components/totalconnect/translations/nl.json +++ b/homeassistant/components/totalconnect/translations/nl.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Locatie", "usercode": "Gebruikerscode" }, "description": "Voer de gebruikerscode voor deze gebruiker in op locatie {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Wachtwoord", "username": "Gebruikersnaam" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/no.json b/homeassistant/components/totalconnect/translations/no.json index c1624f08259..86cfedf51d4 100644 --- a/homeassistant/components/totalconnect/translations/no.json +++ b/homeassistant/components/totalconnect/translations/no.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Plassering", "usercode": "Brukerkode" }, "description": "Angi brukerkoden for denne brukeren p\u00e5 plasseringen {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Passord", "username": "Brukernavn" - }, - "title": "" + } } } } diff --git a/homeassistant/components/totalconnect/translations/pl.json b/homeassistant/components/totalconnect/translations/pl.json index 5174d717c26..d3927212c82 100644 --- a/homeassistant/components/totalconnect/translations/pl.json +++ b/homeassistant/components/totalconnect/translations/pl.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Lokalizacja", "usercode": "Kod u\u017cytkownika" }, "description": "Wprowad\u017a kod u\u017cytkownika dla u\u017cytkownika w lokalizacji {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Has\u0142o", "username": "Nazwa u\u017cytkownika" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/pt-BR.json b/homeassistant/components/totalconnect/translations/pt-BR.json index 47dfe5437e0..1ffeb1337ec 100644 --- a/homeassistant/components/totalconnect/translations/pt-BR.json +++ b/homeassistant/components/totalconnect/translations/pt-BR.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Localiza\u00e7\u00e3o", "usercode": "C\u00f3digo de usu\u00e1rio" }, "description": "Insira o c\u00f3digo de usu\u00e1rio para este usu\u00e1rio no local {location_id}", @@ -26,8 +25,7 @@ "data": { "password": "Senha", "username": "Usu\u00e1rio" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/pt.json b/homeassistant/components/totalconnect/translations/pt.json index 11ac8262267..0957880ae6c 100644 --- a/homeassistant/components/totalconnect/translations/pt.json +++ b/homeassistant/components/totalconnect/translations/pt.json @@ -8,11 +8,6 @@ "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, "step": { - "locations": { - "data": { - "location": "Localiza\u00e7\u00e3o" - } - }, "reauth_confirm": { "title": "Reautenticar integra\u00e7\u00e3o" }, diff --git a/homeassistant/components/totalconnect/translations/ru.json b/homeassistant/components/totalconnect/translations/ru.json index a46c37032a1..ee82564033c 100644 --- a/homeassistant/components/totalconnect/translations/ru.json +++ b/homeassistant/components/totalconnect/translations/ru.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435", "usercode": "\u041a\u043e\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043a\u043e\u0434 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 {location_id}.", @@ -26,8 +25,7 @@ "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/sk.json b/homeassistant/components/totalconnect/translations/sk.json index 59d045e7603..71a7aea5018 100644 --- a/homeassistant/components/totalconnect/translations/sk.json +++ b/homeassistant/components/totalconnect/translations/sk.json @@ -5,13 +5,6 @@ }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" - }, - "step": { - "locations": { - "data": { - "location": "Umiestnenie" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/totalconnect/translations/sl.json b/homeassistant/components/totalconnect/translations/sl.json index 7214ff6aeb5..bbddcdf2459 100644 --- a/homeassistant/components/totalconnect/translations/sl.json +++ b/homeassistant/components/totalconnect/translations/sl.json @@ -8,8 +8,7 @@ "data": { "password": "Geslo", "username": "Uporabni\u0161ko ime" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/tr.json b/homeassistant/components/totalconnect/translations/tr.json index 925353f05a4..ef50457f846 100644 --- a/homeassistant/components/totalconnect/translations/tr.json +++ b/homeassistant/components/totalconnect/translations/tr.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "Konum", "usercode": "Kullan\u0131c\u0131 kodu" }, "description": "Bu kullan\u0131c\u0131n\u0131n kullan\u0131c\u0131 kodunu {location_id} konumuna girin", @@ -26,8 +25,7 @@ "data": { "password": "Parola", "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "title": "Toplam Ba\u011flant\u0131" + } } } } diff --git a/homeassistant/components/totalconnect/translations/uk.json b/homeassistant/components/totalconnect/translations/uk.json index f34a279d598..afe3dfab539 100644 --- a/homeassistant/components/totalconnect/translations/uk.json +++ b/homeassistant/components/totalconnect/translations/uk.json @@ -11,8 +11,7 @@ "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/totalconnect/translations/zh-Hant.json b/homeassistant/components/totalconnect/translations/zh-Hant.json index beaeaa5d9bf..3b960a8bc43 100644 --- a/homeassistant/components/totalconnect/translations/zh-Hant.json +++ b/homeassistant/components/totalconnect/translations/zh-Hant.json @@ -12,7 +12,6 @@ "step": { "locations": { "data": { - "location": "\u5ea7\u6a19", "usercode": "\u4f7f\u7528\u8005\u4ee3\u78bc" }, "description": "\u8f38\u5165\u4f7f\u7528\u8005\u65bc\u6b64\u5ea7\u6a19 {location_id} \u4e4b\u4f7f\u7528\u8005\u4ee3\u78bc", @@ -26,8 +25,7 @@ "data": { "password": "\u5bc6\u78bc", "username": "\u4f7f\u7528\u8005\u540d\u7a31" - }, - "title": "Total Connect" + } } } } diff --git a/homeassistant/components/tplink/translations/bg.json b/homeassistant/components/tplink/translations/bg.json index 33ae523d8cf..b31dd804044 100644 --- a/homeassistant/components/tplink/translations/bg.json +++ b/homeassistant/components/tplink/translations/bg.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e", - "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 TP-Link \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430.", - "single_instance_allowed": "\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." + "no_devices_found": "\u041d\u0435 \u0441\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 TP-Link \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0432 \u043c\u0440\u0435\u0436\u0430\u0442\u0430." }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 TP-Link \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430?" - }, "discovery_confirm": { "description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/ca.json b/homeassistant/components/tplink/translations/ca.json index 4dfb749a9d7..490dbf5f56b 100644 --- a/homeassistant/components/tplink/translations/ca.json +++ b/homeassistant/components/tplink/translations/ca.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "El dispositiu ja est\u00e0 configurat", - "no_devices_found": "No s'han trobat dispositius a la xarxa", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + "no_devices_found": "No s'han trobat dispositius a la xarxa" }, "error": { "cannot_connect": "Ha fallat la connexi\u00f3" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Vols configurar dispositius intel\u00b7ligents TP-Link?" - }, "discovery_confirm": { "description": "Vols configurar {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/cs.json b/homeassistant/components/tplink/translations/cs.json index 5ee79c8e6f6..bb063ffbcc7 100644 --- a/homeassistant/components/tplink/translations/cs.json +++ b/homeassistant/components/tplink/translations/cs.json @@ -2,16 +2,12 @@ "config": { "abort": { "already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno", - "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed", - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." + "no_devices_found": "V s\u00edti nebyla nalezena \u017e\u00e1dn\u00e1 za\u0159\u00edzen\u00ed" }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" }, "step": { - "confirm": { - "description": "Chcete nastavit inteligentn\u00ed za\u0159\u00edzen\u00ed TP-Link?" - }, "pick_device": { "data": { "device": "Za\u0159\u00edzen\u00ed" diff --git a/homeassistant/components/tplink/translations/da.json b/homeassistant/components/tplink/translations/da.json index e6fc3c895a3..e9cd7b269e2 100644 --- a/homeassistant/components/tplink/translations/da.json +++ b/homeassistant/components/tplink/translations/da.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Ingen TP-Link enheder kunne findes p\u00e5 netv\u00e6rket.", - "single_instance_allowed": "Det er kun n\u00f8dvendigt med en ops\u00e6tning." - }, - "step": { - "confirm": { - "description": "Vil du konfigurere TP-Link-smartenheder?" - } + "no_devices_found": "Ingen TP-Link enheder kunne findes p\u00e5 netv\u00e6rket." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/de.json b/homeassistant/components/tplink/translations/de.json index 4d6a07b881e..1fd05c1325a 100644 --- a/homeassistant/components/tplink/translations/de.json +++ b/homeassistant/components/tplink/translations/de.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Ger\u00e4t ist bereits konfiguriert", - "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + "no_devices_found": "Keine Ger\u00e4te im Netzwerk gefunden" }, "error": { "cannot_connect": "Verbindung fehlgeschlagen" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "M\u00f6chtest du TP-Link Smart Devices einrichten?" - }, "discovery_confirm": { "description": "M\u00f6chtest du {name} {model} ({host}) einrichten?" }, diff --git a/homeassistant/components/tplink/translations/el.json b/homeassistant/components/tplink/translations/el.json index bea659e039a..d7c4825b514 100644 --- a/homeassistant/components/tplink/translations/el.json +++ b/homeassistant/components/tplink/translations/el.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", - "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + "no_devices_found": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03c4\u03bf \u03b4\u03af\u03ba\u03c4\u03c5\u03bf" }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03ad\u03be\u03c5\u03c0\u03bd\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 TP-Link;" - }, "discovery_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} {model} ({host});" }, diff --git a/homeassistant/components/tplink/translations/en.json b/homeassistant/components/tplink/translations/en.json index da4681145d8..0697974e708 100644 --- a/homeassistant/components/tplink/translations/en.json +++ b/homeassistant/components/tplink/translations/en.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Device is already configured", - "no_devices_found": "No devices found on the network", - "single_instance_allowed": "Already configured. Only a single configuration possible." + "no_devices_found": "No devices found on the network" }, "error": { "cannot_connect": "Failed to connect" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Do you want to setup TP-Link smart devices?" - }, "discovery_confirm": { "description": "Do you want to setup {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/es-419.json b/homeassistant/components/tplink/translations/es-419.json index 4113e802e1a..302d49fa29e 100644 --- a/homeassistant/components/tplink/translations/es-419.json +++ b/homeassistant/components/tplink/translations/es-419.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "No se encontraron dispositivos TP-Link en la red.", - "single_instance_allowed": "Solo es necesaria una \u00fanica configuraci\u00f3n." - }, - "step": { - "confirm": { - "description": "\u00bfDesea configurar dispositivos inteligentes TP-Link?" - } + "no_devices_found": "No se encontraron dispositivos TP-Link en la red." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/es.json b/homeassistant/components/tplink/translations/es.json index 81bb3f47433..e083acf61ed 100644 --- a/homeassistant/components/tplink/translations/es.json +++ b/homeassistant/components/tplink/translations/es.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "El dispositivo ya est\u00e1 configurado", - "no_devices_found": "No se encontraron dispositivos en la red", - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." + "no_devices_found": "No se encontraron dispositivos en la red" }, "error": { "cannot_connect": "No se pudo conectar" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u00bfQuieres configurar dispositivos inteligentes de TP-Link?" - }, "discovery_confirm": { "description": "\u00bfQuieres configurar {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/et.json b/homeassistant/components/tplink/translations/et.json index 12c4f3d6f84..01dabe57ea7 100644 --- a/homeassistant/components/tplink/translations/et.json +++ b/homeassistant/components/tplink/translations/et.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Seade on juba h\u00e4\u00e4lestatud", - "no_devices_found": "V\u00f5rgust ei leitud seadmeid", - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + "no_devices_found": "V\u00f5rgust ei leitud seadmeid" }, "error": { "cannot_connect": "\u00dchendamine nurjus" }, "flow_title": "{name} {model} ( {host} )", "step": { - "confirm": { - "description": "Kas soovid seadistada TP-Linki nutiseadmeid?" - }, "discovery_confirm": { "description": "Kas seadistada {name}{model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/fr.json b/homeassistant/components/tplink/translations/fr.json index d3a5fad5939..e1105ea00e0 100644 --- a/homeassistant/components/tplink/translations/fr.json +++ b/homeassistant/components/tplink/translations/fr.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9", - "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + "no_devices_found": "Aucun appareil trouv\u00e9 sur le r\u00e9seau" }, "error": { "cannot_connect": "\u00c9chec de connexion" }, "flow_title": "{name} {model} ( {host} )", "step": { - "confirm": { - "description": "Voulez-vous configurer TP-Link smart devices?" - }, "discovery_confirm": { "description": "Voulez-vous configurer {name} {model} ( {host} )\u00a0?" }, diff --git a/homeassistant/components/tplink/translations/he.json b/homeassistant/components/tplink/translations/he.json index 6ac16c36476..fc44b0d7ae7 100644 --- a/homeassistant/components/tplink/translations/he.json +++ b/homeassistant/components/tplink/translations/he.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", - "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea", - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." + "no_devices_found": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05d1\u05e8\u05e9\u05ea" }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d7\u05db\u05de\u05d9\u05dd \u05e9\u05dc TP-Link ?" - }, "pick_device": { "data": { "device": "\u05d4\u05ea\u05e7\u05df" diff --git a/homeassistant/components/tplink/translations/hu.json b/homeassistant/components/tplink/translations/hu.json index c00744d0dcf..ba9ba65a1c6 100644 --- a/homeassistant/components/tplink/translations/hu.json +++ b/homeassistant/components/tplink/translations/hu.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Az eszk\u00f6z m\u00e1r be van konfigur\u00e1lva", - "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + "no_devices_found": "Nem tal\u00e1lhat\u00f3 eszk\u00f6z a h\u00e1l\u00f3zaton" }, "error": { "cannot_connect": "A csatlakoz\u00e1s sikertelen" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Szeretn\u00e9 be\u00e1ll\u00edtani a TP-Link intelligens eszk\u00f6zeit?" - }, "discovery_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/id.json b/homeassistant/components/tplink/translations/id.json index 2a435ac1ac1..b7c0be074e7 100644 --- a/homeassistant/components/tplink/translations/id.json +++ b/homeassistant/components/tplink/translations/id.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Perangkat sudah dikonfigurasi", - "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + "no_devices_found": "Tidak ada perangkat yang ditemukan di jaringan" }, "error": { "cannot_connect": "Gagal terhubung" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Ingin menyiapkan perangkat cerdas TP-Link?" - }, "discovery_confirm": { "description": "Ingin menyiapkan {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/it.json b/homeassistant/components/tplink/translations/it.json index 6ab46615373..4a35356d604 100644 --- a/homeassistant/components/tplink/translations/it.json +++ b/homeassistant/components/tplink/translations/it.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato", - "no_devices_found": "Nessun dispositivo trovato sulla rete", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + "no_devices_found": "Nessun dispositivo trovato sulla rete" }, "error": { "cannot_connect": "Impossibile connettersi" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Vuoi configurare i dispositivi intelligenti TP-Link?" - }, "discovery_confirm": { "description": "Vuoi configurare {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/ja.json b/homeassistant/components/tplink/translations/ja.json index 45e9854b431..1df87f71518 100644 --- a/homeassistant/components/tplink/translations/ja.json +++ b/homeassistant/components/tplink/translations/ja.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059", - "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + "no_devices_found": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093" }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "TP-Link\u30b9\u30de\u30fc\u30c8\u30c7\u30d0\u30a4\u30b9\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" - }, "discovery_confirm": { "description": "{name} {model} ({host}) \u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u884c\u3044\u307e\u3059\u304b\uff1f" }, diff --git a/homeassistant/components/tplink/translations/ko.json b/homeassistant/components/tplink/translations/ko.json index 01ae48031e4..f063f54207c 100644 --- a/homeassistant/components/tplink/translations/ko.json +++ b/homeassistant/components/tplink/translations/ko.json @@ -1,13 +1,9 @@ { "config": { "abort": { - "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" }, "step": { - "confirm": { - "description": "TP-Link \uc2a4\ub9c8\ud2b8 \uae30\uae30\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" - }, "discovery_confirm": { "description": "{name} {model} ( {host} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" } diff --git a/homeassistant/components/tplink/translations/lb.json b/homeassistant/components/tplink/translations/lb.json index 1560b68d3e4..47a897ef157 100644 --- a/homeassistant/components/tplink/translations/lb.json +++ b/homeassistant/components/tplink/translations/lb.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Keng Apparater am Netzwierk fonnt.", - "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun m\u00e9iglech." - }, - "step": { - "confirm": { - "description": "Soll TP-Link Smart Home konfigur\u00e9iert ginn?" - } + "no_devices_found": "Keng Apparater am Netzwierk fonnt." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/nl.json b/homeassistant/components/tplink/translations/nl.json index f6cf6a21e72..bceae9f3fff 100644 --- a/homeassistant/components/tplink/translations/nl.json +++ b/homeassistant/components/tplink/translations/nl.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "no_devices_found": "Geen apparaten gevonden op het netwerk" }, "error": { "cannot_connect": "Kon geen verbinding maken" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Wil je TP-Link slimme apparaten instellen?" - }, "discovery_confirm": { "description": "Wilt u {name} {model} ({host}) instellen?" }, diff --git a/homeassistant/components/tplink/translations/no.json b/homeassistant/components/tplink/translations/no.json index 6c7bd7dcbf4..8ae66b3dce1 100644 --- a/homeassistant/components/tplink/translations/no.json +++ b/homeassistant/components/tplink/translations/no.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Enheten er allerede konfigurert", - "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + "no_devices_found": "Ingen enheter funnet p\u00e5 nettverket" }, "error": { "cannot_connect": "Tilkobling mislyktes" }, "flow_title": "{name} {model} ( {host} )", "step": { - "confirm": { - "description": "Vil du konfigurere TP-Link smart enheter?" - }, "discovery_confirm": { "description": "Vil du konfigurere {name} {model} ( {host} )?" }, diff --git a/homeassistant/components/tplink/translations/pl.json b/homeassistant/components/tplink/translations/pl.json index 35e1e7f5354..3e6283a6d5e 100644 --- a/homeassistant/components/tplink/translations/pl.json +++ b/homeassistant/components/tplink/translations/pl.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", - "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + "no_devices_found": "Nie znaleziono urz\u0105dze\u0144 w sieci" }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" - }, "discovery_confirm": { "description": "Czy chcesz skonfigurowa\u0107 {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/pt-BR.json b/homeassistant/components/tplink/translations/pt-BR.json index a034b44b761..7ce4d557844 100644 --- a/homeassistant/components/tplink/translations/pt-BR.json +++ b/homeassistant/components/tplink/translations/pt-BR.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Dispositivo j\u00e1 est\u00e1 configurado", - "no_devices_found": "Nenhum dispositivo encontrado na rede", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." + "no_devices_found": "Nenhum dispositivo encontrado na rede" }, "error": { "cannot_connect": "Falha ao conectar" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "Deseja configurar dispositivos inteligentes TP-Link?" - }, "discovery_confirm": { "description": "Deseja configurar {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/pt.json b/homeassistant/components/tplink/translations/pt.json index 9df803c325e..90afdfcf10a 100644 --- a/homeassistant/components/tplink/translations/pt.json +++ b/homeassistant/components/tplink/translations/pt.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Nenhum dispositivo TP-Link encontrado na rede.", - "single_instance_allowed": "S\u00f3 \u00e9 necess\u00e1ria uma \u00fanica configura\u00e7\u00e3o." - }, - "step": { - "confirm": { - "description": "Deseja configurar os dispositivos inteligentes TP-Link?" - } + "no_devices_found": "Nenhum dispositivo TP-Link encontrado na rede." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/ru.json b/homeassistant/components/tplink/translations/ru.json index 47f1459e572..b622a77c77d 100644 --- a/homeassistant/components/tplink/translations/ru.json +++ b/homeassistant/components/tplink/translations/ru.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", - "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + "no_devices_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0441\u0435\u0442\u0438." }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u0412\u044b \u0443\u0432\u0435\u0440\u0435\u043d\u044b, \u0447\u0442\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c TP-Link Smart Home?" - }, "discovery_confirm": { "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} {model} ({host})?" }, diff --git a/homeassistant/components/tplink/translations/sl.json b/homeassistant/components/tplink/translations/sl.json index 6c49eaf8d0a..039bf9966a7 100644 --- a/homeassistant/components/tplink/translations/sl.json +++ b/homeassistant/components/tplink/translations/sl.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "TP-Link naprav ni mogo\u010de najti v omre\u017eju.", - "single_instance_allowed": "Potrebna je samo ena konfiguracija." - }, - "step": { - "confirm": { - "description": "\u017delite namestiti pametne naprave TP-Link?" - } + "no_devices_found": "TP-Link naprav ni mogo\u010de najti v omre\u017eju." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/sv.json b/homeassistant/components/tplink/translations/sv.json index 70ab1740f0d..c11ec674c36 100644 --- a/homeassistant/components/tplink/translations/sv.json +++ b/homeassistant/components/tplink/translations/sv.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "Inga TP-Link enheter hittades p\u00e5 n\u00e4tverket.", - "single_instance_allowed": "Endast en enda konfiguration \u00e4r n\u00f6dv\u00e4ndig." - }, - "step": { - "confirm": { - "description": "Vill du konfigurera TP-Link smart enheter?" - } + "no_devices_found": "Inga TP-Link enheter hittades p\u00e5 n\u00e4tverket." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/tr.json b/homeassistant/components/tplink/translations/tr.json index 5e4dca23c37..616997a0976 100644 --- a/homeassistant/components/tplink/translations/tr.json +++ b/homeassistant/components/tplink/translations/tr.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f", - "no_devices_found": "A\u011fda cihaz bulunamad\u0131", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + "no_devices_found": "A\u011fda cihaz bulunamad\u0131" }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "TP-Link ak\u0131ll\u0131 cihazlar\u0131 kurmak istiyor musunuz?" - }, "discovery_confirm": { "description": "{name} {model} ( {host} ) kurulumu yapmak istiyor musunuz?" }, diff --git a/homeassistant/components/tplink/translations/uk.json b/homeassistant/components/tplink/translations/uk.json index abbfc076f7e..1efd10692f9 100644 --- a/homeassistant/components/tplink/translations/uk.json +++ b/homeassistant/components/tplink/translations/uk.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456.", - "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f." - }, - "step": { - "confirm": { - "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 TP-Link Smart Home?" - } + "no_devices_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u0456 \u0432 \u043c\u0435\u0440\u0435\u0436\u0456." } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/zh-Hans.json b/homeassistant/components/tplink/translations/zh-Hans.json index 710cc0fe166..9b46cb915c5 100644 --- a/homeassistant/components/tplink/translations/zh-Hans.json +++ b/homeassistant/components/tplink/translations/zh-Hans.json @@ -1,13 +1,7 @@ { "config": { "abort": { - "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230 TP-Link \u8bbe\u5907\u3002", - "single_instance_allowed": "\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002" - }, - "step": { - "confirm": { - "description": "\u60a8\u60f3\u8981\u914d\u7f6e TP-Link \u667a\u80fd\u8bbe\u5907\u5417\uff1f" - } + "no_devices_found": "\u6ca1\u6709\u5728\u7f51\u7edc\u4e0a\u627e\u5230 TP-Link \u8bbe\u5907\u3002" } } } \ No newline at end of file diff --git a/homeassistant/components/tplink/translations/zh-Hant.json b/homeassistant/components/tplink/translations/zh-Hant.json index 31bfeea42a2..47e976a7e30 100644 --- a/homeassistant/components/tplink/translations/zh-Hant.json +++ b/homeassistant/components/tplink/translations/zh-Hant.json @@ -2,17 +2,13 @@ "config": { "abort": { "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210", - "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + "no_devices_found": "\u7db2\u8def\u4e0a\u627e\u4e0d\u5230\u88dd\u7f6e" }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557" }, "flow_title": "{name} {model} ({host})", "step": { - "confirm": { - "description": "\u662f\u5426\u8981\u8a2d\u5b9a TP-Link \u667a\u80fd\u88dd\u7f6e\uff1f" - }, "discovery_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} {model} ({host})\uff1f" }, diff --git a/homeassistant/components/trafikverket_ferry/translations/ko.json b/homeassistant/components/trafikverket_ferry/translations/ko.json new file mode 100644 index 00000000000..c50ce7212d1 --- /dev/null +++ b/homeassistant/components/trafikverket_ferry/translations/ko.json @@ -0,0 +1,28 @@ +{ + "config": { + "abort": { + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "incorrect_api_key": "\uc120\ud0dd\ud55c \uacc4\uc815\uc5d0 \ub300\ud55c \uc798\ubabb\ub41c API \ud0a4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "invalid_route": "\uc81c\uacf5\ub41c \uc815\ubcf4\ub85c \uacbd\ub85c\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" + }, + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "time": "Time", + "weekday": "\ud3c9\uc77c" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_weatherstation/translations/bg.json b/homeassistant/components/trafikverket_weatherstation/translations/bg.json index 2e96cb420e6..07c54468c03 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/bg.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/bg.json @@ -11,7 +11,6 @@ "user": { "data": { "api_key": "API \u043a\u043b\u044e\u0447", - "name": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435", "station": "\u0421\u0442\u0430\u043d\u0446\u0438\u044f" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/ca.json b/homeassistant/components/trafikverket_weatherstation/translations/ca.json index 80fe04ba83f..2ca00e2b4df 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/ca.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/ca.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Clau API", - "conditions": "Condicions monitoritzades", - "name": "Nom d'usuari", "station": "Estaci\u00f3" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/de.json b/homeassistant/components/trafikverket_weatherstation/translations/de.json index 47d3dfc2e9b..c57b913e42a 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/de.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/de.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API-Schl\u00fcssel", - "conditions": "\u00dcberwachte Bedingungen", - "name": "Benutzername", "station": "Station" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/el.json b/homeassistant/components/trafikverket_weatherstation/translations/el.json index e790e3d3d97..83f046e2c41 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/el.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/el.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", - "conditions": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03bf\u03cd\u03bc\u03b5\u03bd\u03b5\u03c2 \u03c3\u03c5\u03bd\u03b8\u03ae\u03ba\u03b5\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", "station": "\u03a3\u03c4\u03b1\u03b8\u03bc\u03cc\u03c2/\u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/en.json b/homeassistant/components/trafikverket_weatherstation/translations/en.json index 0c0c15f5bb9..c2b5ba0b598 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/en.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/en.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API Key", - "conditions": "Monitored conditions", - "name": "Username", "station": "Station" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/es.json b/homeassistant/components/trafikverket_weatherstation/translations/es.json index 009c57bb275..9513ae65221 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/es.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/es.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Clave API", - "conditions": "Condiciones monitoreadas", - "name": "Nombre de usuario", "station": "Estaci\u00f3n" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/et.json b/homeassistant/components/trafikverket_weatherstation/translations/et.json index 7350031bd36..8485525e233 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/et.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/et.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API v\u00f5ti", - "conditions": "J\u00e4lgitavad elemendid", - "name": "Kasutajanimi", "station": "Seirejaam" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/fr.json b/homeassistant/components/trafikverket_weatherstation/translations/fr.json index 55a88c6a746..6a92e2249d9 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/fr.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/fr.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Cl\u00e9 d'API", - "conditions": "Conditions surveill\u00e9es", - "name": "Nom d'utilisateur", "station": "Station" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/he.json b/homeassistant/components/trafikverket_weatherstation/translations/he.json index e6eceda994c..6801953a80c 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/he.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/he.json @@ -11,7 +11,6 @@ "user": { "data": { "api_key": "\u05de\u05e4\u05ea\u05d7 API", - "name": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", "station": "\u05ea\u05d7\u05e0\u05d4" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/hu.json b/homeassistant/components/trafikverket_weatherstation/translations/hu.json index c4f830b20fa..57c2bf2e526 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/hu.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/hu.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API kulcs", - "conditions": "Megfigyelt k\u00f6r\u00fclm\u00e9nyek", - "name": "Felhaszn\u00e1l\u00f3n\u00e9v", "station": "\u00c1llom\u00e1s" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/id.json b/homeassistant/components/trafikverket_weatherstation/translations/id.json index 0db961235d4..e3d04cc2a45 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/id.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/id.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Kunci API", - "conditions": "Kondisi yang dipantau", - "name": "Nama Pengguna", "station": "Stasiun" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/it.json b/homeassistant/components/trafikverket_weatherstation/translations/it.json index a073a528586..927b8bdd19a 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/it.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/it.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Chiave API", - "conditions": "Condizioni monitorate", - "name": "Nome utente", "station": "Stazione" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/ja.json b/homeassistant/components/trafikverket_weatherstation/translations/ja.json index 5a022f011e2..5710ea7b693 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/ja.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/ja.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API\u30ad\u30fc", - "conditions": "\u30e2\u30cb\u30bf\u30fc\u306e\u72b6\u614b", - "name": "\u30e6\u30fc\u30b6\u30fc\u540d", "station": "\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/lt.json b/homeassistant/components/trafikverket_weatherstation/translations/lt.json deleted file mode 100644 index 87076aabdf1..00000000000 --- a/homeassistant/components/trafikverket_weatherstation/translations/lt.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "name": "Prisijungimo vardas" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_weatherstation/translations/nb.json b/homeassistant/components/trafikverket_weatherstation/translations/nb.json index 19fbf894f8d..0d3a32ccb1c 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/nb.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/nb.json @@ -10,8 +10,7 @@ "step": { "user": { "data": { - "api_key": "API-n\u00f8kkel", - "name": "Brukernavn" + "api_key": "API-n\u00f8kkel" } } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/nl.json b/homeassistant/components/trafikverket_weatherstation/translations/nl.json index 1e8d8ae9f3a..ce958cb3d86 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/nl.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/nl.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API-sleutel", - "conditions": "Gemonitorde condities", - "name": "Gebruikersnaam", "station": "Station" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/no.json b/homeassistant/components/trafikverket_weatherstation/translations/no.json index fea917f9c38..7b39c2f9b58 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/no.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/no.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API-n\u00f8kkel", - "conditions": "Overv\u00e5kede forhold", - "name": "Brukernavn", "station": "Stasjon" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/pl.json b/homeassistant/components/trafikverket_weatherstation/translations/pl.json index 2dfd13bf268..2500bcc2b51 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/pl.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/pl.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Klucz API", - "conditions": "Monitorowane warunki pogodowe", - "name": "Nazwa u\u017cytkownika", "station": "Stacja" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/pt-BR.json b/homeassistant/components/trafikverket_weatherstation/translations/pt-BR.json index f73ab8555da..70d870a6dba 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/pt-BR.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/pt-BR.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "Chave da API", - "conditions": "Condi\u00e7\u00f5es monitoradas", - "name": "Usu\u00e1rio", "station": "Esta\u00e7\u00e3o" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/ru.json b/homeassistant/components/trafikverket_weatherstation/translations/ru.json index d7658cda81b..7a903d8f624 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/ru.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/ru.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "\u041a\u043b\u044e\u0447 API", - "conditions": "\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u044f", - "name": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "station": "\u0421\u0442\u0430\u043d\u0446\u0438\u044f" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/tr.json b/homeassistant/components/trafikverket_weatherstation/translations/tr.json index e999f7b50d1..89c8288a9ee 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/tr.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/tr.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API Anahtar\u0131", - "conditions": "\u0130zlenen ko\u015fullar", - "name": "Kullan\u0131c\u0131 Ad\u0131", "station": "\u0130stasyon" } } diff --git a/homeassistant/components/trafikverket_weatherstation/translations/zh-Hant.json b/homeassistant/components/trafikverket_weatherstation/translations/zh-Hant.json index 47394b08af9..f0d928c3aba 100644 --- a/homeassistant/components/trafikverket_weatherstation/translations/zh-Hant.json +++ b/homeassistant/components/trafikverket_weatherstation/translations/zh-Hant.json @@ -13,8 +13,6 @@ "user": { "data": { "api_key": "API \u91d1\u9470", - "conditions": "\u5df2\u76e3\u63a7\u72c0\u614b", - "name": "\u4f7f\u7528\u8005\u540d\u7a31", "station": "\u76e3\u63a7\u7ad9" } } diff --git a/homeassistant/components/tuya/translations/af.json b/homeassistant/components/tuya/translations/af.json deleted file mode 100644 index 71ac741b6b8..00000000000 --- a/homeassistant/components/tuya/translations/af.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "options": { - "error": { - "dev_not_config": "Ger\u00e4tetyp nicht konfigurierbar", - "dev_not_found": "Ger\u00e4t nicht gefunden" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/bg.json b/homeassistant/components/tuya/translations/bg.json index 7a393100351..5bd4cc0b745 100644 --- a/homeassistant/components/tuya/translations/bg.json +++ b/homeassistant/components/tuya/translations/bg.json @@ -1,51 +1,17 @@ { "config": { - "abort": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f." - }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", "login_error": "\u0413\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0432\u043b\u0438\u0437\u0430\u043d\u0435 ({code}): {msg}" }, - "flow_title": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 Tuya", "step": { - "login": { - "data": { - "country_code": "\u041a\u043e\u0434 \u043d\u0430 \u0434\u044a\u0440\u0436\u0430\u0432\u0430\u0442\u0430", - "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "tuya_app_type": "\u041c\u043e\u0431\u0438\u043b\u043d\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435", - "username": "\u0410\u043a\u0430\u0443\u043d\u0442" - }, - "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438\u0442\u0435 \u0441\u0438 \u0434\u0430\u043d\u043d\u0438 \u0437\u0430 Tuya", - "title": "Tuya" - }, "user": { "data": { "country_code": "\u0414\u044a\u0440\u0436\u0430\u0432\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "platform": "\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u0442\u043e, \u0432 \u043a\u043e\u0435\u0442\u043e \u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u043d \u0412\u0430\u0448\u0438\u044f\u0442 \u0430\u043a\u0430\u0443\u043d\u0442", - "region": "\u0420\u0435\u0433\u0438\u043e\u043d", "username": "\u0410\u043a\u0430\u0443\u043d\u0442" }, - "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438 \u0437\u0430 Tuya", - "title": "Tuya \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" - }, - "error": { - "dev_not_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u043d\u0435 \u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043e" - }, - "step": { - "device": { - "data": { - "unit_of_measurement": "\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0430, \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u043d\u0430 \u043e\u0442 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" - } + "description": "\u0412\u044a\u0432\u0435\u0434\u0435\u0442\u0435 \u0432\u0430\u0448\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u0438 \u0434\u0430\u043d\u043d\u0438 \u0437\u0430 Tuya" } } } diff --git a/homeassistant/components/tuya/translations/ca.json b/homeassistant/components/tuya/translations/ca.json index 5a9dd4ac44b..52ef20e69b9 100644 --- a/homeassistant/components/tuya/translations/ca.json +++ b/homeassistant/components/tuya/translations/ca.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." - }, "error": { "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", "login_error": "Error d'inici de sessi\u00f3 ({code}): {msg}" }, - "flow_title": "Configuraci\u00f3 de Tuya", "step": { - "login": { - "data": { - "access_id": "ID d'acc\u00e9s", - "access_secret": "Secret d'acc\u00e9s", - "country_code": "Codi de pa\u00eds", - "endpoint": "Zona de disponibilitat", - "password": "Contrasenya", - "tuya_app_type": "Aplicaci\u00f3 per a m\u00f2bil", - "username": "Compte" - }, - "description": "Introdueix la credencial de Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "ID d'acc\u00e9s de Tuya IoT", "access_secret": "Secret d'acc\u00e9s de Tuya IoT", "country_code": "Pa\u00eds", "password": "Contrasenya", - "platform": "L'aplicaci\u00f3 on es registra el teu compte", - "region": "Regi\u00f3", - "tuya_project_type": "Tipus de projecte al n\u00favol de Tuya", "username": "Compte" }, - "description": "Introdueix les teves credencial de Tuya", - "title": "Integraci\u00f3 Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Ha fallat la connexi\u00f3" - }, - "error": { - "dev_multi_type": "Per configurar una selecci\u00f3 de m\u00faltiples dispositius, aquests han de ser del mateix tipus", - "dev_not_config": "El tipus d'aquest dispositiu no \u00e9s configurable", - "dev_not_found": "No s'ha trobat el dispositiu." - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Rang de brillantor utilitzat pel dispositiu", - "curr_temp_divider": "Divisor del valor de temperatura actual (0 = predeterminat)", - "max_kelvin": "Temperatura del color m\u00e0xima suportada, en Kelvin", - "max_temp": "Temperatura desitjada m\u00e0xima (utilitza min i max = 0 per defecte)", - "min_kelvin": "Temperatura del color m\u00ednima suportada, en Kelvin", - "min_temp": "Temperatura desitjada m\u00ednima (utilitza min i max = 0 per defecte)", - "set_temp_divided": "Utilitza el valor de temperatura dividit per a ordres de configuraci\u00f3 de temperatura", - "support_color": "For\u00e7a el suport de color", - "temp_divider": "Divisor del valor de temperatura (0 = predeterminat)", - "temp_step_override": "Pas de temperatura objectiu", - "tuya_max_coltemp": "Temperatura de color m\u00e0xima enviada pel dispositiu", - "unit_of_measurement": "Unitat de temperatura utilitzada pel dispositiu" - }, - "description": "Configura les opcions per ajustar la informaci\u00f3 mostrada pel dispositiu {device_type} `{device_name}`", - "title": "Configuraci\u00f3 de dispositiu Tuya" - }, - "init": { - "data": { - "discovery_interval": "Interval de sondeig del dispositiu de descoberta, en segons", - "list_devices": "Selecciona els dispositius a configurar o deixa-ho buit per desar la configuraci\u00f3", - "query_device": "Selecciona el dispositiu que utilitzar\u00e0 m\u00e8tode de consulta, per actualitzacions d'estat m\u00e9s freq\u00fcents", - "query_interval": "Interval de sondeig de consultes del dispositiu, en segons" - }, - "description": "No estableixis valors d'interval de sondeig massa baixos ja que les crides fallaran i generaran missatges d'error al registre", - "title": "Configuraci\u00f3 d'opcions de Tuya" + "description": "Introdueix les teves credencial de Tuya" } } } diff --git a/homeassistant/components/tuya/translations/cs.json b/homeassistant/components/tuya/translations/cs.json index 9a406ffcb4b..0364baac560 100644 --- a/homeassistant/components/tuya/translations/cs.json +++ b/homeassistant/components/tuya/translations/cs.json @@ -1,65 +1,16 @@ { "config": { - "abort": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace." - }, "error": { "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, - "flow_title": "Konfigurace Tuya", "step": { - "login": { - "data": { - "country_code": "K\u00f3d zem\u011b" - } - }, "user": { "data": { "country_code": "Zem\u011b", "password": "Heslo", - "platform": "Aplikace, ve kter\u00e9 m\u00e1te zaregistrovan\u00fd \u00fa\u010det", - "region": "Region", "username": "\u00da\u010det" }, - "description": "Zadejte sv\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje k Tuya.", - "title": "Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit" - }, - "error": { - "dev_multi_type": "V\u00edce vybran\u00fdch za\u0159\u00edzen\u00ed k nastaven\u00ed mus\u00ed b\u00fdt stejn\u00e9ho typu", - "dev_not_config": "Typ za\u0159\u00edzen\u00ed nelze nastavit", - "dev_not_found": "Za\u0159\u00edzen\u00ed nenalezeno" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Rozsah jasu pou\u017e\u00edvan\u00fd za\u0159\u00edzen\u00edm", - "max_kelvin": "Maxim\u00e1ln\u00ed podporovan\u00e1 teplota barev v kelvinech", - "max_temp": "Maxim\u00e1ln\u00ed c\u00edlov\u00e1 teplota (pou\u017eijte min a max = 0 jako v\u00fdchoz\u00ed)", - "min_kelvin": "Maxim\u00e1ln\u00ed podporovan\u00e1 teplota barev v kelvinech", - "min_temp": "Minim\u00e1ln\u00ed c\u00edlov\u00e1 teplota (pou\u017eijte min a max = 0 jako v\u00fdchoz\u00ed)", - "support_color": "Vynutit podporu barev", - "tuya_max_coltemp": "Maxim\u00e1ln\u00ed teplota barev nahl\u00e1\u0161en\u00e1 za\u0159\u00edzen\u00edm", - "unit_of_measurement": "Jednotka teploty pou\u017e\u00edvan\u00e1 za\u0159\u00edzen\u00edm" - }, - "title": "Nastavte za\u0159\u00edzen\u00ed Tuya" - }, - "init": { - "data": { - "discovery_interval": "Interval objevov\u00e1n\u00ed za\u0159\u00edzen\u00ed v sekund\u00e1ch", - "list_devices": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e1 chcete nastavit, nebo ponechte pr\u00e1zdn\u00e9, abyste konfiguraci ulo\u017eili", - "query_device": "Vyberte za\u0159\u00edzen\u00ed, kter\u00e9 bude pou\u017e\u00edvat metodu dotaz\u016f pro rychlej\u0161\u00ed aktualizaci stavu", - "query_interval": "Interval dotazov\u00e1n\u00ed za\u0159\u00edzen\u00ed v sekund\u00e1ch" - }, - "description": "Nenastavujte intervalu dotazov\u00e1n\u00ed p\u0159\u00edli\u0161 n\u00edzk\u00e9 hodnoty, jinak se dotazov\u00e1n\u00ed nezda\u0159\u00ed a bude generovat chybov\u00e9 zpr\u00e1vy do logu", - "title": "Nastavte mo\u017enosti Tuya" + "description": "Zadejte sv\u00e9 p\u0159ihla\u0161ovac\u00ed \u00fadaje k Tuya." } } } diff --git a/homeassistant/components/tuya/translations/de.json b/homeassistant/components/tuya/translations/de.json index 61bdf308246..492159d4d6c 100644 --- a/homeassistant/components/tuya/translations/de.json +++ b/homeassistant/components/tuya/translations/de.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_auth": "Ung\u00fcltige Authentifizierung", - "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." - }, "error": { "invalid_auth": "Ung\u00fcltige Authentifizierung", "login_error": "Anmeldefehler ({code}): {msg}" }, - "flow_title": "Tuya Konfiguration", "step": { - "login": { - "data": { - "access_id": "Zugangs-ID", - "access_secret": "Zugangsgeheimnis", - "country_code": "L\u00e4ndercode", - "endpoint": "Verf\u00fcgbarkeitsbereich", - "password": "Passwort", - "tuya_app_type": "Mobile App", - "username": "Konto" - }, - "description": "Gib deine Tuya-Anmeldedaten ein", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT-Zugriffs-ID", "access_secret": "Tuya IoT-Zugriffsgeheimnis", "country_code": "Land", "password": "Passwort", - "platform": "Die App, in der dein Konto registriert ist", - "region": "Region", - "tuya_project_type": "Tuya Cloud Projekttyp", "username": "Konto" }, - "description": "Gib deine Tuya-Anmeldeinformationen ein.", - "title": "Tuya-Integration" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Verbindung fehlgeschlagen" - }, - "error": { - "dev_multi_type": "Mehrere ausgew\u00e4hlte Ger\u00e4te zur Konfiguration m\u00fcssen vom gleichen Typ sein", - "dev_not_config": "Ger\u00e4tetyp nicht konfigurierbar", - "dev_not_found": "Ger\u00e4t nicht gefunden" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Vom Ger\u00e4t genutzter Helligkeitsbereich", - "curr_temp_divider": "Aktueller Temperaturwert-Teiler (0 = Standard verwenden)", - "max_kelvin": "Maximal unterst\u00fctzte Farbtemperatur in Kelvin", - "max_temp": "Maximale Solltemperatur (f\u00fcr Voreinstellung min und max = 0 verwenden)", - "min_kelvin": "Minimale unterst\u00fctzte Farbtemperatur in Kelvin", - "min_temp": "Minimal Solltemperatur (f\u00fcr Voreinstellung min und max = 0 verwenden)", - "set_temp_divided": "Geteilten Temperaturwert f\u00fcr Solltemperaturbefehl verwenden", - "support_color": "Farbunterst\u00fctzung erzwingen", - "temp_divider": "Teiler f\u00fcr Temperaturwerte (0 = Standard verwenden)", - "temp_step_override": "Zieltemperaturschritt", - "tuya_max_coltemp": "Vom Ger\u00e4t gemeldete maximale Farbtemperatur", - "unit_of_measurement": "Vom Ger\u00e4t verwendete Temperatureinheit" - }, - "description": "Optionen zur Anpassung der angezeigten Informationen f\u00fcr das Ger\u00e4t `{device_name}` vom Typ: {device_type}konfigurieren", - "title": "Tuya-Ger\u00e4t konfigurieren" - }, - "init": { - "data": { - "discovery_interval": "Abfrageintervall f\u00fcr Ger\u00e4teabruf in Sekunden", - "list_devices": "W\u00e4hle die zu konfigurierenden Ger\u00e4te aus oder lasse sie leer, um die Konfiguration zu speichern", - "query_device": "W\u00e4hle ein Ger\u00e4t aus, das die Abfragemethode f\u00fcr eine schnellere Statusaktualisierung verwendet.", - "query_interval": "Ger\u00e4teabrufintervall in Sekunden" - }, - "description": "Stelle das Abfrageintervall nicht zu niedrig ein, sonst schlagen die Aufrufe fehl und erzeugen eine Fehlermeldung im Protokoll", - "title": "Tuya-Optionen konfigurieren" + "description": "Gib deine Tuya-Anmeldeinformationen ein." } } } diff --git a/homeassistant/components/tuya/translations/el.json b/homeassistant/components/tuya/translations/el.json index 63ba3b2696a..7bbf8790316 100644 --- a/homeassistant/components/tuya/translations/el.json +++ b/homeassistant/components/tuya/translations/el.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." - }, "error": { "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "login_error": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2 ({code}): {msg}" }, - "flow_title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 Tuya", "step": { - "login": { - "data": { - "access_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "access_secret": "\u039c\u03c5\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "country_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c7\u03ce\u03c1\u03b1\u03c2", - "endpoint": "\u0396\u03ce\u03bd\u03b7 \u03b4\u03b9\u03b1\u03b8\u03b5\u03c3\u03b9\u03bc\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "tuya_app_type": "Mobile App", - "username": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2" - }, - "description": "\u0395\u03b9\u03c3\u03b1\u03b3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2 Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "\u0391\u03bd\u03b1\u03b3\u03bd\u03c9\u03c1\u03b9\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 Tuya IoT", "access_secret": "\u039c\u03c5\u03c3\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 Tuya IoT", "country_code": "\u03a7\u03ce\u03c1\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "platform": "\u0397 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03b3\u03b5\u03b3\u03c1\u03b1\u03bc\u03bc\u03ad\u03bd\u03bf\u03c2 \u03bf \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c3\u03b1\u03c2", - "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae", - "tuya_project_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03ad\u03c1\u03b3\u03bf\u03c5 Tuya cloud", "username": "\u039b\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2" }, - "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2 Tuya", - "title": "\u0395\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2" - }, - "error": { - "dev_multi_type": "\u03a0\u03bf\u03bb\u03bb\u03ad\u03c2 \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03c4\u03c1\u03c9\u03bd \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03ad\u03c7\u03bf\u03c5\u03bd \u03c4\u03bf\u03bd \u03af\u03b4\u03b9\u03bf \u03c4\u03cd\u03c0\u03bf", - "dev_not_config": "\u039f \u03c4\u03cd\u03c0\u03bf\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03b5\u03af \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", - "dev_not_found": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u0395\u03cd\u03c1\u03bf\u03c2 \u03c6\u03c9\u03c4\u03b5\u03b9\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", - "curr_temp_divider": "\u0394\u03b9\u03b1\u03b9\u03c1\u03ad\u03c4\u03b7\u03c2 \u03c4\u03c1\u03ad\u03c7\u03bf\u03c5\u03c3\u03b1\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 (0 = \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae\u03c2)", - "max_kelvin": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03c7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03bf kelvin", - "max_temp": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1-\u03c3\u03c4\u03cc\u03c7\u03bf\u03c2 (\u03c7\u03c1\u03ae\u03c3\u03b7 min \u03ba\u03b1\u03b9 max = 0 \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae)", - "min_kelvin": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03c7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03b5 kelvin", - "min_temp": "\u0395\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1-\u03c3\u03c4\u03cc\u03c7\u03bf\u03c2 (\u03c7\u03c1\u03ae\u03c3\u03b7 min \u03ba\u03b1\u03b9 max = 0 \u03b3\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae)", - "set_temp_divided": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03b4\u03b9\u03b1\u03b9\u03c1\u03b5\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ae\u03c2 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c4\u03bf\u03bb\u03ae \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7\u03c2 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2", - "support_color": "\u0391\u03bd\u03b1\u03b3\u03ba\u03b1\u03c3\u03c4\u03b9\u03ba\u03ae \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2", - "temp_divider": "\u0394\u03b9\u03b1\u03b9\u03c1\u03ad\u03c4\u03b7\u03c2 \u03c4\u03b9\u03bc\u03ce\u03bd \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 (0 = \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae\u03c2)", - "temp_step_override": "\u0392\u03ae\u03bc\u03b1 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03c3\u03c4\u03cc\u03c7\u03bf\u03c5", - "tuya_max_coltemp": "\u039c\u03ad\u03b3\u03b9\u03c3\u03c4\u03b7 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1 \u03c7\u03c1\u03ce\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c0\u03bf\u03c5 \u03b1\u03bd\u03b1\u03c6\u03ad\u03c1\u03b5\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", - "unit_of_measurement": "\u039c\u03bf\u03bd\u03ac\u03b4\u03b1 \u03b8\u03b5\u03c1\u03bc\u03bf\u03ba\u03c1\u03b1\u03c3\u03af\u03b1\u03c2 \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" - }, - "description": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae \u03c4\u03c9\u03bd \u03b5\u03bc\u03c6\u03b1\u03bd\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03c9\u03bd \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03b9\u03ce\u03bd \u03b3\u03b9\u03b1 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae {device_type} `{device_name}`", - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 Tuya" - }, - "init": { - "data": { - "discovery_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b7\u03bc\u03bf\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03bc\u03bf\u03cd \u03c3\u03b5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1", - "list_devices": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03ce\u03c3\u03b5\u03c4\u03b5 \u03ae \u03b1\u03c6\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03ba\u03b5\u03bd\u03ad\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b1\u03c0\u03bf\u03b8\u03b7\u03ba\u03b5\u03cd\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7", - "query_device": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03c0\u03bf\u03c5 \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af \u03c4\u03b7 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf \u03b5\u03c1\u03c9\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b3\u03b9\u03b1 \u03c4\u03b1\u03c7\u03cd\u03c4\u03b5\u03c1\u03b7 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", - "query_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b4\u03b7\u03bc\u03bf\u03c3\u03ba\u03cc\u03c0\u03b7\u03c3\u03b7\u03c2 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03c3\u03b5 \u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1" - }, - "description": "\u039c\u03b7\u03bd \u03bf\u03c1\u03af\u03b6\u03b5\u03c4\u03b5 \u03c0\u03bf\u03bb\u03cd \u03c7\u03b1\u03bc\u03b7\u03bb\u03ad\u03c2 \u03c4\u03b9\u03bc\u03ad\u03c2 \u03b4\u03b9\u03b1\u03c3\u03c4\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b4\u03b7\u03bc\u03bf\u03c3\u03ba\u03bf\u03c0\u03ae\u03c3\u03b5\u03c9\u03bd, \u03b1\u03bb\u03bb\u03b9\u03ce\u03c2 \u03bf\u03b9 \u03ba\u03bb\u03ae\u03c3\u03b5\u03b9\u03c2 \u03b8\u03b1 \u03b1\u03c0\u03bf\u03c4\u03cd\u03c7\u03bf\u03c5\u03bd \u03ba\u03b1\u03b9 \u03b8\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03bc\u03ae\u03bd\u03c5\u03bc\u03b1 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1\u03c4\u03bf\u03c2 \u03c3\u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2.", - "title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03bf\u03b3\u03ce\u03bd Tuya" + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03ac \u03c3\u03b1\u03c2 Tuya" } } } diff --git a/homeassistant/components/tuya/translations/en.json b/homeassistant/components/tuya/translations/en.json index e928dc37d57..e69872fd309 100644 --- a/homeassistant/components/tuya/translations/en.json +++ b/homeassistant/components/tuya/translations/en.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", - "single_instance_allowed": "Already configured. Only a single configuration possible." - }, "error": { "invalid_auth": "Invalid authentication", "login_error": "Login error ({code}): {msg}" }, - "flow_title": "Tuya configuration", "step": { - "login": { - "data": { - "access_id": "Access ID", - "access_secret": "Access Secret", - "country_code": "Country Code", - "endpoint": "Availability Zone", - "password": "Password", - "tuya_app_type": "Mobile App", - "username": "Account" - }, - "description": "Enter your Tuya credential", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT Access Secret", "country_code": "Country", "password": "Password", - "platform": "The app where your account is registered", - "region": "Region", - "tuya_project_type": "Tuya cloud project type", "username": "Account" }, - "description": "Enter your Tuya credentials", - "title": "Tuya Integration" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Failed to connect" - }, - "error": { - "dev_multi_type": "Multiple selected devices to configure must be of the same type", - "dev_not_config": "Device type not configurable", - "dev_not_found": "Device not found" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Brightness range used by device", - "curr_temp_divider": "Current Temperature value divider (0 = use default)", - "max_kelvin": "Max color temperature supported in kelvin", - "max_temp": "Max target temperature (use min and max = 0 for default)", - "min_kelvin": "Min color temperature supported in kelvin", - "min_temp": "Min target temperature (use min and max = 0 for default)", - "set_temp_divided": "Use divided Temperature value for set temperature command", - "support_color": "Force color support", - "temp_divider": "Temperature values divider (0 = use default)", - "temp_step_override": "Target Temperature step", - "tuya_max_coltemp": "Max color temperature reported by device", - "unit_of_measurement": "Temperature unit used by device" - }, - "description": "Configure options to adjust displayed information for {device_type} device `{device_name}`", - "title": "Configure Tuya Device" - }, - "init": { - "data": { - "discovery_interval": "Discovery device polling interval in seconds", - "list_devices": "Select the devices to configure or leave empty to save configuration", - "query_device": "Select device that will use query method for faster status update", - "query_interval": "Query device polling interval in seconds" - }, - "description": "Do not set pollings interval values too low or the calls will fail generating error message in the log", - "title": "Configure Tuya Options" + "description": "Enter your Tuya credentials" } } } diff --git a/homeassistant/components/tuya/translations/en_GB.json b/homeassistant/components/tuya/translations/en_GB.json deleted file mode 100644 index 90df003a190..00000000000 --- a/homeassistant/components/tuya/translations/en_GB.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "options": { - "step": { - "device": { - "data": { - "max_kelvin": "Max colour temperature supported in Kelvin", - "min_kelvin": "Min colour temperature supported in Kelvin", - "support_color": "Force colour support", - "tuya_max_coltemp": "Max colour temperature reported by device" - } - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/es.json b/homeassistant/components/tuya/translations/es.json index 235f87d5374..a15408bf96d 100644 --- a/homeassistant/components/tuya/translations/es.json +++ b/homeassistant/components/tuya/translations/es.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "No se pudo conectar", - "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." - }, "error": { "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "login_error": "Error de inicio de sesi\u00f3n ({code}): {msg}" }, - "flow_title": "Configuraci\u00f3n Tuya", "step": { - "login": { - "data": { - "access_id": "ID de acceso", - "access_secret": "Acceso secreto", - "country_code": "C\u00f3digo de pa\u00eds", - "endpoint": "Zona de disponibilidad", - "password": "Contrase\u00f1a", - "tuya_app_type": "Aplicaci\u00f3n m\u00f3vil", - "username": "Cuenta" - }, - "description": "Ingrese su credencial Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "ID de acceso de Tuya IoT", "access_secret": "Tuya IoT Access Secret", "country_code": "C\u00f3digo de pais de tu cuenta (por ejemplo, 1 para USA o 86 para China)", "password": "Contrase\u00f1a", - "platform": "La aplicaci\u00f3n donde se registra tu cuenta", - "region": "Regi\u00f3n", - "tuya_project_type": "Tipo de proyecto en la nube de Tuya", "username": "Usuario" }, - "description": "Introduce tus credencial de Tuya", - "title": "Integraci\u00f3n Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "No se pudo conectar" - }, - "error": { - "dev_multi_type": "Los m\u00faltiples dispositivos seleccionados para configurar deben ser del mismo tipo", - "dev_not_config": "Tipo de dispositivo no configurable", - "dev_not_found": "Dispositivo no encontrado" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Rango de brillo utilizado por el dispositivo", - "curr_temp_divider": "Divisor del valor de la temperatura actual (0 = usar valor por defecto)", - "max_kelvin": "Temperatura de color m\u00e1xima admitida en kelvin", - "max_temp": "Temperatura objetivo m\u00e1xima (usa m\u00edn. y m\u00e1x. = 0 por defecto)", - "min_kelvin": "Temperatura de color m\u00ednima soportada en kelvin", - "min_temp": "Temperatura objetivo m\u00ednima (usa m\u00edn. y m\u00e1x. = 0 por defecto)", - "set_temp_divided": "Use el valor de temperatura dividido para el comando de temperatura establecida", - "support_color": "Forzar soporte de color", - "temp_divider": "Divisor de los valores de temperatura (0 = usar valor por defecto)", - "temp_step_override": "Temperatura deseada", - "tuya_max_coltemp": "Temperatura de color m\u00e1xima notificada por dispositivo", - "unit_of_measurement": "Unidad de temperatura utilizada por el dispositivo" - }, - "description": "Configura las opciones para ajustar la informaci\u00f3n mostrada para {device_type} dispositivo `{device_name}`", - "title": "Configurar dispositivo Tuya" - }, - "init": { - "data": { - "discovery_interval": "Intervalo de sondeo del descubrimiento al dispositivo en segundos", - "list_devices": "Selecciona los dispositivos a configurar o d\u00e9jalos en blanco para guardar la configuraci\u00f3n", - "query_device": "Selecciona el dispositivo que utilizar\u00e1 el m\u00e9todo de consulta para una actualizaci\u00f3n de estado m\u00e1s r\u00e1pida", - "query_interval": "Intervalo de sondeo de la consulta al dispositivo en segundos" - }, - "description": "No establezcas valores de intervalo de sondeo demasiado bajos o las llamadas fallar\u00e1n generando un mensaje de error en el registro", - "title": "Configurar opciones de Tuya" + "description": "Introduce tus credencial de Tuya" } } } diff --git a/homeassistant/components/tuya/translations/et.json b/homeassistant/components/tuya/translations/et.json index bd23136cb1b..de84ef4aa2f 100644 --- a/homeassistant/components/tuya/translations/et.json +++ b/homeassistant/components/tuya/translations/et.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u00dchendamine nurjus", - "invalid_auth": "Tuvastamise viga", - "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks sidumine." - }, "error": { "invalid_auth": "Tuvastamise viga", "login_error": "Sisenemine nurjus ( {code} ): {msg}" }, - "flow_title": "Tuya seaded", "step": { - "login": { - "data": { - "access_id": "Juurdep\u00e4\u00e4su ID", - "access_secret": "API salas\u00f5na", - "country_code": "Riigi kood", - "endpoint": "Seadmete regioon", - "password": "Salas\u00f5na", - "tuya_app_type": "Mobiilirakendus", - "username": "Konto" - }, - "description": "Sisesta oma Tuya mandaat", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT kasutajatunnus", "access_secret": "Tuya IoT salas\u00f5na", "country_code": "Riik", "password": "Salas\u00f5na", - "platform": "\u00c4pp kus konto registreeriti", - "region": "Piirkond", - "tuya_project_type": "Tuya pilveprojekti t\u00fc\u00fcp", "username": "Kasutajanimi" }, - "description": "Sisesta oma Tuya konto andmed.", - "title": "Tuya sidumine" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u00dchendamine nurjus" - }, - "error": { - "dev_multi_type": "Mitu h\u00e4\u00e4lestatavat seadet peavad olema sama t\u00fc\u00fcpi", - "dev_not_config": "Seda t\u00fc\u00fcpi seade pole seadistatav", - "dev_not_found": "Seadet ei leitud" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Seadme kasutatav heledusvahemik", - "curr_temp_divider": "Praeguse temperatuuri v\u00e4\u00e4rtuse eraldaja (0 = kasuta vaikev\u00e4\u00e4rtust)", - "max_kelvin": "Maksimaalne v\u00f5imalik v\u00e4rvitemperatuur (Kelvinites)", - "max_temp": "Maksimaalne sihttemperatuur (vaikimisi kasuta min ja max = 0)", - "min_kelvin": "Minimaalne v\u00f5imalik v\u00e4rvitemperatuur (Kelvinites)", - "min_temp": "Minimaalne sihttemperatuur (vaikimisi kasuta min ja max = 0)", - "set_temp_divided": "M\u00e4\u00e4ratud temperatuuri k\u00e4su jaoks kasuta jagatud temperatuuri v\u00e4\u00e4rtust", - "support_color": "Luba v\u00e4rvuse juhtimine", - "temp_divider": "Temperatuuri v\u00e4\u00e4rtuse eraldaja (0 = kasuta vaikev\u00e4\u00e4rtust)", - "temp_step_override": "Sihttemperatuuri samm", - "tuya_max_coltemp": "Seadme teatatud maksimaalne v\u00e4rvitemperatuur", - "unit_of_measurement": "Seadme temperatuuri\u00fchik" - }, - "description": "Suvandid \u00fcksuse {device_type} {device_name} kuvatava teabe muutmiseks", - "title": "H\u00e4\u00e4lesta Tuya seade" - }, - "init": { - "data": { - "discovery_interval": "Seadme leidmisp\u00e4ringute intervall (sekundites)", - "list_devices": "Vali seadistatavad seadmed v\u00f5i j\u00e4ta s\u00e4tete salvestamiseks t\u00fchjaks", - "query_device": "Vali seade, mis kasutab oleku kiiremaks v\u00e4rskendamiseks p\u00e4ringumeetodit", - "query_interval": "P\u00e4ringute intervall (sekundites)" - }, - "description": "\u00c4ra m\u00e4\u00e4ra k\u00fcsitlusintervalli v\u00e4\u00e4rtusi liiga madalaks, vastasel korral v\u00f5ivad p\u00e4ringud logis t\u00f5rketeate genereerida", - "title": "Tuya suvandite seadistamine" + "description": "Sisesta oma Tuya konto andmed." } } } diff --git a/homeassistant/components/tuya/translations/fi.json b/homeassistant/components/tuya/translations/fi.json index 3c74a9b8eeb..aeceadba0fc 100644 --- a/homeassistant/components/tuya/translations/fi.json +++ b/homeassistant/components/tuya/translations/fi.json @@ -1,16 +1,13 @@ { "config": { - "flow_title": "Tuya-asetukset", "step": { "user": { "data": { "country_code": "Tilisi maakoodi (esim. 1 Yhdysvalloissa, 358 Suomessa)", "password": "Salasana", - "platform": "Sovellus, johon tili rekister\u00f6id\u00e4\u00e4n", "username": "K\u00e4ytt\u00e4j\u00e4tunnus" }, - "description": "Anna Tuya-tunnistetietosi.", - "title": "Tuya" + "description": "Anna Tuya-tunnistetietosi." } } } diff --git a/homeassistant/components/tuya/translations/fr.json b/homeassistant/components/tuya/translations/fr.json index c28e62e7ab4..a8049917f41 100644 --- a/homeassistant/components/tuya/translations/fr.json +++ b/homeassistant/components/tuya/translations/fr.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u00c9chec de connexion", - "invalid_auth": "Authentification non valide", - "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." - }, "error": { "invalid_auth": "Authentification non valide", "login_error": "Erreur de connexion ( {code} ): {msg}" }, - "flow_title": "Configuration Tuya", "step": { - "login": { - "data": { - "access_id": "Identifiant d'acc\u00e8s", - "access_secret": "Access Secret", - "country_code": "Code pays", - "endpoint": "Zone de disponibilit\u00e9", - "password": "Mot de passe", - "tuya_app_type": "Application mobile", - "username": "Compte" - }, - "description": "Entrez votre identifiant Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "Identifiant d'acc\u00e8s Tuya IoT", "access_secret": "Tuya IoT Access Secret", "country_code": "Pays", "password": "Mot de passe", - "platform": "L'application dans laquelle votre compte est enregistr\u00e9", - "region": "R\u00e9gion", - "tuya_project_type": "Type de projet cloud Tuya", "username": "Compte" }, - "description": "Saisissez vos informations d'identification Tuya.", - "title": "Int\u00e9gration de Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u00c9chec de connexion" - }, - "error": { - "dev_multi_type": "Si plusieurs appareils sont s\u00e9lectionn\u00e9s pour \u00eatre configur\u00e9s, ils doivent tous \u00eatre du m\u00eame type", - "dev_not_config": "Type d'appareil non configurable", - "dev_not_found": "Appareil non trouv\u00e9" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Plage de luminosit\u00e9 utilis\u00e9e par l'appareil", - "curr_temp_divider": "Diviseur de valeur de temp\u00e9rature actuelle (0 = utiliser la valeur par d\u00e9faut)", - "max_kelvin": "Temp\u00e9rature de couleur maximale prise en charge en Kelvin", - "max_temp": "Temp\u00e9rature cible maximale (utilisez min et max = 0 par d\u00e9faut)", - "min_kelvin": "Temp\u00e9rature de couleur minimale prise en charge en kelvin", - "min_temp": "Temp\u00e9rature cible minimale (utilisez min et max = 0 par d\u00e9faut)", - "set_temp_divided": "Utilisez la valeur de temp\u00e9rature divis\u00e9e pour la commande de temp\u00e9rature d\u00e9finie", - "support_color": "Forcer la prise en charge des couleurs", - "temp_divider": "Diviseur de valeurs de temp\u00e9rature (0 = utiliser la valeur par d\u00e9faut)", - "temp_step_override": "Pas de temp\u00e9rature cible", - "tuya_max_coltemp": "Temp\u00e9rature de couleur maximale rapport\u00e9e par l'appareil", - "unit_of_measurement": "Unit\u00e9 de temp\u00e9rature utilis\u00e9e par l'appareil" - }, - "description": "Configurer les options pour ajuster les informations affich\u00e9es pour l'appareil {device_type} ` {device_name} `", - "title": "Configurer l'appareil Tuya" - }, - "init": { - "data": { - "discovery_interval": "Intervalle de d\u00e9couverte de l'appareil en secondes", - "list_devices": "S\u00e9lectionnez les appareils \u00e0 configurer ou laissez vide pour enregistrer la configuration", - "query_device": "S\u00e9lectionnez l'appareil qui utilisera la m\u00e9thode de requ\u00eate pour une mise \u00e0 jour plus rapide de l'\u00e9tat", - "query_interval": "Intervalle d'interrogation de l'appareil en secondes" - }, - "description": "Ne d\u00e9finissez pas des valeurs d'intervalle d'interrogation trop faibles ou les appels \u00e9choueront \u00e0 g\u00e9n\u00e9rer un message d'erreur dans le journal", - "title": "Configurer les options de Tuya" + "description": "Saisissez vos informations d'identification Tuya." } } } diff --git a/homeassistant/components/tuya/translations/he.json b/homeassistant/components/tuya/translations/he.json index 02e2dc94776..737ca754202 100644 --- a/homeassistant/components/tuya/translations/he.json +++ b/homeassistant/components/tuya/translations/he.json @@ -1,67 +1,17 @@ { "config": { - "abort": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea." - }, "error": { "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", "login_error": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea ({code}): {msg}" }, - "flow_title": "\u05ea\u05e6\u05d5\u05e8\u05ea Tuya", "step": { - "login": { - "data": { - "access_id": "\u05de\u05d6\u05d4\u05d4 \u05d2\u05d9\u05e9\u05d4", - "access_secret": "\u05e1\u05d5\u05d3 \u05d2\u05d9\u05e9\u05d4", - "country_code": "\u05e7\u05d5\u05d3 \u05de\u05d3\u05d9\u05e0\u05d4", - "endpoint": "\u05d0\u05d6\u05d5\u05e8 \u05d6\u05de\u05d9\u05e0\u05d5\u05ea", - "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "tuya_app_type": "\u05d9\u05d9\u05e9\u05d5\u05dd \u05dc\u05e0\u05d9\u05d9\u05d3", - "username": "\u05d7\u05e9\u05d1\u05d5\u05df" - }, - "description": "\u05d4\u05d6\u05e0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 \u05d4-Tuya \u05e9\u05dc\u05da", - "title": "Tuya" - }, "user": { "data": { "country_code": "\u05de\u05d3\u05d9\u05e0\u05d4", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "platform": "\u05d4\u05d9\u05d9\u05e9\u05d5\u05dd \u05d1\u05d5 \u05e8\u05e9\u05d5\u05dd \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da", - "region": "\u05d0\u05d9\u05d6\u05d5\u05e8", - "tuya_project_type": "\u05e1\u05d5\u05d2 \u05e4\u05e8\u05d5\u05d9\u05d9\u05e7\u05d8 \u05d4\u05e2\u05e0\u05df \u05e9\u05dc Tuya", "username": "\u05d7\u05e9\u05d1\u05d5\u05df" }, - "description": "\u05d4\u05d6\u05e0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8\u05d9 \u05d4-Tuya \u05e9\u05dc\u05da.", - "title": "\u05e9\u05d9\u05dc\u05d5\u05d1 Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4" - }, - "error": { - "dev_multi_type": "\u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05e0\u05d1\u05d7\u05e8\u05d9\u05dd \u05de\u05e8\u05d5\u05d1\u05d9\u05dd \u05dc\u05e7\u05d1\u05d9\u05e2\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05d7\u05d9\u05d9\u05d1\u05d9\u05dd \u05dc\u05d4\u05d9\u05d5\u05ea \u05de\u05d0\u05d5\u05ea\u05d5 \u05e1\u05d5\u05d2", - "dev_not_config": "\u05e1\u05d5\u05d2 \u05d4\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05e0\u05d9\u05ea\u05df \u05dc\u05d4\u05d2\u05d3\u05e8\u05d4", - "dev_not_found": "\u05d4\u05d4\u05ea\u05e7\u05df \u05dc\u05d0 \u05e0\u05de\u05e6\u05d0" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u05d8\u05d5\u05d5\u05d7 \u05d1\u05d4\u05d9\u05e8\u05d5\u05ea \u05d4\u05de\u05e9\u05de\u05e9 \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05df", - "max_kelvin": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05e6\u05d1\u05e2 \u05de\u05e8\u05d1\u05d9\u05ea \u05d4\u05e0\u05ea\u05de\u05db\u05ea \u05d1\u05e7\u05dc\u05d5\u05d5\u05d9\u05df", - "min_kelvin": "\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05e6\u05d1\u05e2 \u05de\u05d9\u05e0\u05d9\u05de\u05dc\u05d9\u05ea \u05d4\u05e0\u05ea\u05de\u05db\u05ea \u05d1\u05e7\u05dc\u05d5\u05d5\u05d9\u05df", - "set_temp_divided": "\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e2\u05e8\u05da \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4 \u05de\u05d7\u05d5\u05dc\u05e7 \u05e2\u05d1\u05d5\u05e8 \u05d4\u05e4\u05e7\u05d5\u05d3\u05d4 '\u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4 \u05de\u05d5\u05d2\u05d3\u05e8\u05ea'", - "support_color": "\u05db\u05e4\u05d4 \u05ea\u05de\u05d9\u05db\u05d4 \u05d1\u05e6\u05d1\u05e2", - "temp_step_override": "\u05e9\u05dc\u05d1 \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05ea \u05d4\u05d9\u05e2\u05d3", - "unit_of_measurement": "\u05d9\u05d7\u05d9\u05d3\u05ea \u05d8\u05de\u05e4\u05e8\u05d8\u05d5\u05e8\u05d4 \u05d4\u05de\u05e9\u05de\u05e9\u05ea \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05df" - }, - "title": "\u05e7\u05d1\u05d9\u05e2\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d4\u05ea\u05e7\u05df \u05d8\u05d5\u05d9\u05d4" - }, - "init": { - "title": "\u05e7\u05d1\u05d9\u05e2\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05e9\u05dc \u05d0\u05e4\u05e9\u05e8\u05d5\u05d9\u05d5\u05ea \u05d8\u05d5\u05d9\u05d4" + "description": "\u05d4\u05d6\u05e0\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8\u05d9 \u05d4-Tuya \u05e9\u05dc\u05da." } } } diff --git a/homeassistant/components/tuya/translations/hu.json b/homeassistant/components/tuya/translations/hu.json index 2c2969e589c..37414cbebf9 100644 --- a/homeassistant/components/tuya/translations/hu.json +++ b/homeassistant/components/tuya/translations/hu.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." - }, "error": { "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", "login_error": "Bejelentkez\u00e9si hiba ({code}): {msg}" }, - "flow_title": "Tuya konfigur\u00e1ci\u00f3", "step": { - "login": { - "data": { - "access_id": "Hozz\u00e1f\u00e9r\u00e9si azonos\u00edt\u00f3", - "access_secret": "Hozz\u00e1f\u00e9r\u00e9si token", - "country_code": "Orsz\u00e1g k\u00f3d", - "endpoint": "El\u00e9rhet\u0151s\u00e9gi z\u00f3na", - "password": "Jelsz\u00f3", - "tuya_app_type": "Mobil app", - "username": "Fi\u00f3k" - }, - "description": "Adja meg Tuya hiteles\u00edt\u0151 adatait.", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT azonos\u00edt\u00f3", "access_secret": "Tuya IoT hozz\u00e1f\u00e9r\u00e9si jelsz\u00f3", "country_code": "A fi\u00f3k orsz\u00e1gk\u00f3dja (pl. 1 USA, 36 Magyarorsz\u00e1g, vagy 86 K\u00edna)", "password": "Jelsz\u00f3", - "platform": "Az alkalmaz\u00e1s, ahol a fi\u00f3k regisztr\u00e1lt", - "region": "R\u00e9gi\u00f3", - "tuya_project_type": "Tuya felh\u0151 projekt t\u00edpusa", "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, - "description": "Adja meg Tuya hiteles\u00edt\u0151 adatait.", - "title": "Tuya integr\u00e1ci\u00f3" - } - } - }, - "options": { - "abort": { - "cannot_connect": "A kapcsol\u00f3d\u00e1s nem siker\u00fclt" - }, - "error": { - "dev_multi_type": "T\u00f6bb kiv\u00e1lasztott konfigur\u00e1land\u00f3 eszk\u00f6z eset\u00e9n, azonos t\u00edpus\u00fanak kell lennie", - "dev_not_config": "Ez az eszk\u00f6zt\u00edpus nem konfigur\u00e1lhat\u00f3", - "dev_not_found": "Eszk\u00f6z nem tal\u00e1lhat\u00f3" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Az eszk\u00f6z \u00e1ltal haszn\u00e1lt f\u00e9nyer\u0151 tartom\u00e1ny", - "curr_temp_divider": "Aktu\u00e1lis h\u0151m\u00e9rs\u00e9klet-\u00e9rt\u00e9k oszt\u00f3 (0 = alap\u00e9rtelmezetten)", - "max_kelvin": "Maxim\u00e1lis t\u00e1mogatott sz\u00ednh\u0151m\u00e9rs\u00e9klet kelvinben", - "max_temp": "Maxim\u00e1lis k\u00edv\u00e1nt h\u0151m\u00e9rs\u00e9klet (alap\u00e9rtelmezettnek min \u00e9s max 0)", - "min_kelvin": "Minimum t\u00e1mogatott sz\u00ednh\u0151m\u00e9rs\u00e9klet kelvinben", - "min_temp": "Minim\u00e1lis k\u00edv\u00e1nt h\u0151m\u00e9rs\u00e9klet (alap\u00e9rtelmezettnek min \u00e9s max 0)", - "set_temp_divided": "A h\u0151m\u00e9rs\u00e9klet be\u00e1ll\u00edt\u00e1s\u00e1hoz osztott h\u0151m\u00e9rs\u00e9kleti \u00e9rt\u00e9ket haszn\u00e1ljon", - "support_color": "Sz\u00ednt\u00e1mogat\u00e1s k\u00e9nyszer\u00edt\u00e9se", - "temp_divider": "Sz\u00ednh\u0151m\u00e9rs\u00e9klet-\u00e9rt\u00e9kek oszt\u00f3ja (0 = alap\u00e9rtelmezett)", - "temp_step_override": "C\u00e9lh\u0151m\u00e9rs\u00e9klet l\u00e9pcs\u0151", - "tuya_max_coltemp": "Az eszk\u00f6z \u00e1ltal megadott maxim\u00e1lis sz\u00ednh\u0151m\u00e9rs\u00e9klet", - "unit_of_measurement": "Az eszk\u00f6z \u00e1ltal haszn\u00e1lt h\u0151m\u00e9rs\u00e9kleti egys\u00e9g" - }, - "description": "Konfigur\u00e1l\u00e1si lehet\u0151s\u00e9gek {device_type} t\u00edpus\u00fa `{device_name}` eszk\u00f6z megjelen\u00edtett inform\u00e1ci\u00f3inak be\u00e1ll\u00edt\u00e1s\u00e1hoz", - "title": "Tuya eszk\u00f6z konfigur\u00e1l\u00e1sa" - }, - "init": { - "data": { - "discovery_interval": "Felfedez\u0151 eszk\u00f6z lek\u00e9rdez\u00e9si intervalluma m\u00e1sodpercben", - "list_devices": "V\u00e1lassza ki a konfigur\u00e1lni k\u00edv\u00e1nt eszk\u00f6z\u00f6ket, vagy hagyja \u00fcresen a konfigur\u00e1ci\u00f3 ment\u00e9s\u00e9hez", - "query_device": "V\u00e1lassza ki azt az eszk\u00f6zt, amely a lek\u00e9rdez\u00e9si m\u00f3dszert haszn\u00e1lja a gyorsabb \u00e1llapotfriss\u00edt\u00e9shez", - "query_interval": "Eszk\u00f6z lek\u00e9rdez\u00e9si id\u0151k\u00f6ze m\u00e1sodpercben" - }, - "description": "Ne \u00e1ll\u00edtsa t\u00fal alacsonyra a lek\u00e9rdez\u00e9si intervallum \u00e9rt\u00e9keit, k\u00fcl\u00f6nben a h\u00edv\u00e1sok nem fognak hiba\u00fczenetet gener\u00e1lni a napl\u00f3ban", - "title": "Tuya be\u00e1ll\u00edt\u00e1sok konfigur\u00e1l\u00e1sa" + "description": "Adja meg Tuya hiteles\u00edt\u0151 adatait." } } } diff --git a/homeassistant/components/tuya/translations/id.json b/homeassistant/components/tuya/translations/id.json index 91bf29f3ec8..bbbd74822eb 100644 --- a/homeassistant/components/tuya/translations/id.json +++ b/homeassistant/components/tuya/translations/id.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Gagal terhubung", - "invalid_auth": "Autentikasi tidak valid", - "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." - }, "error": { "invalid_auth": "Autentikasi tidak valid", "login_error": "Kesalahan masuk ({code}): {msg}" }, - "flow_title": "Konfigurasi Tuya", "step": { - "login": { - "data": { - "access_id": "ID Akses", - "access_secret": "Kode Rahasia Akses", - "country_code": "Kode Negara", - "endpoint": "Zona Ketersediaan", - "password": "Kata Sandi", - "tuya_app_type": "Aplikasi Seluler", - "username": "Akun" - }, - "description": "Masukkan kredensial Tuya Anda", - "title": "Tuya" - }, "user": { "data": { "access_id": "ID Akses Tuya IoT", "access_secret": "Kode Rahasia Akses Tuya IoT", "country_code": "Negara", "password": "Kata Sandi", - "platform": "Aplikasi tempat akun Anda terdaftar", - "region": "Wilayah", - "tuya_project_type": "Jenis proyek awan Tuya", "username": "Akun" }, - "description": "Masukkan kredensial Tuya Anda.", - "title": "Integrasi Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Gagal terhubung" - }, - "error": { - "dev_multi_type": "Untuk konfigurasi sekaligus, beberapa perangkat yang dipilih harus berjenis sama", - "dev_not_config": "Jenis perangkat tidak dapat dikonfigurasi", - "dev_not_found": "Perangkat tidak ditemukan" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Rentang kecerahan yang digunakan oleh perangkat", - "curr_temp_divider": "Pembagi nilai suhu saat ini (0 = gunakan bawaan)", - "max_kelvin": "Suhu warna maksimal yang didukung dalam Kelvin", - "max_temp": "Suhu target maksimal (gunakan min dan maks = 0 untuk bawaan)", - "min_kelvin": "Suhu warna minimal yang didukung dalam Kelvin", - "min_temp": "Suhu target minimal (gunakan min dan maks = 0 untuk bawaan)", - "set_temp_divided": "Gunakan nilai suhu terbagi untuk mengirimkan perintah mengatur suhu", - "support_color": "Paksa dukungan warna", - "temp_divider": "Pembagi nilai suhu (0 = gunakan bawaan)", - "temp_step_override": "Langkah Suhu Target", - "tuya_max_coltemp": "Suhu warna maksimal yang dilaporkan oleh perangkat", - "unit_of_measurement": "Satuan suhu yang digunakan oleh perangkat" - }, - "description": "Konfigurasikan opsi untuk menyesuaikan informasi yang ditampilkan untuk perangkat {device_type} `{device_name}`", - "title": "Konfigurasi Perangkat Tuya" - }, - "init": { - "data": { - "discovery_interval": "Interval polling penemuan perangkat dalam detik", - "list_devices": "Pilih perangkat yang akan dikonfigurasi atau biarkan kosong untuk menyimpan konfigurasi", - "query_device": "Pilih perangkat yang akan menggunakan metode kueri untuk pembaruan status lebih cepat", - "query_interval": "Interval polling perangkat kueri dalam detik" - }, - "description": "Jangan atur nilai interval polling terlalu rendah karena panggilan akan gagal menghasilkan pesan kesalahan dalam log", - "title": "Konfigurasikan Opsi Tuya" + "description": "Masukkan kredensial Tuya Anda." } } } diff --git a/homeassistant/components/tuya/translations/is.json b/homeassistant/components/tuya/translations/is.json index fc7a4878a25..ce1e5122e65 100644 --- a/homeassistant/components/tuya/translations/is.json +++ b/homeassistant/components/tuya/translations/is.json @@ -4,23 +4,10 @@ "login_error": "Innskr\u00e1ningarvilla ( {code} ): {msg}" }, "step": { - "login": { - "data": { - "access_id": "A\u00f0gangsau\u00f0kenni", - "access_secret": "A\u00f0gangsleyndarm\u00e1l", - "country_code": "Landsn\u00famer", - "endpoint": "Frambo\u00f0ssv\u00e6\u00f0i", - "password": "Lykilor\u00f0", - "tuya_app_type": "App", - "username": "Reikningur" - }, - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT a\u00f0gangsau\u00f0kenni", - "access_secret": "Tuya IoT a\u00f0gangsleyndarm\u00e1l", - "region": "Landsv\u00e6\u00f0i" + "access_secret": "Tuya IoT a\u00f0gangsleyndarm\u00e1l" } } } diff --git a/homeassistant/components/tuya/translations/it.json b/homeassistant/components/tuya/translations/it.json index 0ab73ab3b60..f8013882fb2 100644 --- a/homeassistant/components/tuya/translations/it.json +++ b/homeassistant/components/tuya/translations/it.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Impossibile connettersi", - "invalid_auth": "Autenticazione non valida", - "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." - }, "error": { "invalid_auth": "Autenticazione non valida", "login_error": "Errore di accesso ({code}): {msg}" }, - "flow_title": "Configurazione di Tuya", "step": { - "login": { - "data": { - "access_id": "Access ID", - "access_secret": "Access Secret", - "country_code": "Prefisso internazionale", - "endpoint": "Zona di disponibilit\u00e0", - "password": "Password", - "tuya_app_type": "App per dispositivi mobili", - "username": "Account" - }, - "description": "Inserisci le tue credenziali Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT Access Secret", "country_code": "Nazione", "password": "Password", - "platform": "L'app in cui \u00e8 registrato il tuo account", - "region": "Area geografica", - "tuya_project_type": "Tipo di progetto Tuya cloud", "username": "Account" }, - "description": "Inserisci le tue credenziali Tuya", - "title": "Integrazione Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Impossibile connettersi" - }, - "error": { - "dev_multi_type": "I dispositivi multipli selezionati da configurare devono essere dello stesso tipo", - "dev_not_config": "Tipo di dispositivo non configurabile", - "dev_not_found": "Dispositivo non trovato" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Intervallo di luminosit\u00e0 utilizzato dal dispositivo", - "curr_temp_divider": "Divisore del valore della temperatura corrente (0 = usa il valore predefinito)", - "max_kelvin": "Temperatura colore massima supportata in kelvin", - "max_temp": "Temperatura di destinazione massima (utilizzare min e max = 0 per impostazione predefinita)", - "min_kelvin": "Temperatura colore minima supportata in kelvin", - "min_temp": "Temperatura di destinazione minima (utilizzare min e max = 0 per impostazione predefinita)", - "set_temp_divided": "Utilizzare il valore temperatura diviso per impostare il comando temperatura", - "support_color": "Forza il supporto del colore", - "temp_divider": "Divisore dei valori di temperatura (0 = utilizzare il valore predefinito)", - "temp_step_override": "Passo della temperatura da raggiungere", - "tuya_max_coltemp": "Temperatura di colore massima riportata dal dispositivo", - "unit_of_measurement": "Unit\u00e0 di temperatura utilizzata dal dispositivo" - }, - "description": "Configura le opzioni per regolare le informazioni visualizzate per il dispositivo {device_type} `{device_name}`", - "title": "Configura il dispositivo Tuya" - }, - "init": { - "data": { - "discovery_interval": "Intervallo di scansione di rilevamento dispositivo in secondi", - "list_devices": "Seleziona i dispositivi da configurare o lascia vuoto per salvare la configurazione", - "query_device": "Seleziona il dispositivo che utilizzer\u00e0 il metodo di interrogazione per un pi\u00f9 rapido aggiornamento dello stato", - "query_interval": "Intervallo di scansione di interrogazione dispositivo in secondi" - }, - "description": "Non impostare valori dell'intervallo di scansione troppo bassi o le chiamate non riusciranno a generare un messaggio di errore nel registro", - "title": "Configura le opzioni Tuya" + "description": "Inserisci le tue credenziali Tuya" } } } diff --git a/homeassistant/components/tuya/translations/ja.json b/homeassistant/components/tuya/translations/ja.json index 1b5d29584cc..d54a17f7eff 100644 --- a/homeassistant/components/tuya/translations/ja.json +++ b/homeassistant/components/tuya/translations/ja.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" - }, "error": { "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "login_error": "\u30ed\u30b0\u30a4\u30f3\u30a8\u30e9\u30fc ({code}): {msg}" }, - "flow_title": "Tuya\u306e\u8a2d\u5b9a", "step": { - "login": { - "data": { - "access_id": "\u30a2\u30af\u30bb\u30b9ID", - "access_secret": "\u30a2\u30af\u30bb\u30b9\u30b7\u30fc\u30af\u30ec\u30c3\u30c8", - "country_code": "\u56fd\u5225\u30b3\u30fc\u30c9", - "endpoint": "\u5229\u7528\u53ef\u80fd\u30be\u30fc\u30f3", - "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "tuya_app_type": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea", - "username": "\u30a2\u30ab\u30a6\u30f3\u30c8" - }, - "description": "Tuya\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT Access Secret", "country_code": "\u56fd\u5225\u30b3\u30fc\u30c9", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "platform": "\u30a2\u30ab\u30a6\u30f3\u30c8\u304c\u767b\u9332\u3055\u308c\u3066\u3044\u308b\u30a2\u30d7\u30ea", - "region": "\u30ea\u30fc\u30b8\u30e7\u30f3", - "tuya_project_type": "Tuya Cloud\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30bf\u30a4\u30d7", "username": "\u30a2\u30ab\u30a6\u30f3\u30c8" }, - "description": "Tuya\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "Tuya\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f" - }, - "error": { - "dev_multi_type": "\u69cb\u6210\u3059\u308b\u8907\u6570\u306e\u9078\u629e\u3055\u308c\u305f\u30c7\u30d0\u30a4\u30b9\u306f\u3001\u540c\u3058\u30bf\u30a4\u30d7\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059", - "dev_not_config": "\u30c7\u30d0\u30a4\u30b9\u30bf\u30a4\u30d7\u304c\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093", - "dev_not_found": "\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3059\u308b\u8f1d\u5ea6\u7bc4\u56f2", - "curr_temp_divider": "\u73fe\u5728\u306e\u6e29\u5ea6\u5024\u306e\u533a\u5207\u308a(0 = \u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u4f7f\u7528)", - "max_kelvin": "\u30b1\u30eb\u30d3\u30f3\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u6700\u5927\u8272\u6e29\u5ea6", - "max_temp": "\u6700\u5927\u76ee\u6a19\u6e29\u5ea6(\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6700\u5c0f\u304a\u3088\u3073\u6700\u5927 = 0\u3092\u4f7f\u7528)", - "min_kelvin": "\u30b1\u30eb\u30d3\u30f3\u3067\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u6700\u5c0f\u8272\u6e29\u5ea6", - "min_temp": "\u6700\u5c0f\u76ee\u6a19\u6e29\u5ea6(\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u306f\u6700\u5c0f\u304a\u3088\u3073\u6700\u5927 = 0\u3092\u4f7f\u7528)", - "set_temp_divided": "\u8a2d\u5b9a\u6e29\u5ea6\u30b3\u30de\u30f3\u30c9\u306b\u533a\u5207\u3089\u308c\u305f\u6e29\u5ea6\u5024\u3092\u4f7f\u7528", - "support_color": "\u5f37\u5236\u7684\u306b\u30ab\u30e9\u30fc\u3092\u30b5\u30dd\u30fc\u30c8", - "temp_divider": "\u6e29\u5ea6\u5024\u306e\u533a\u5207\u308a(0 = \u30c7\u30d5\u30a9\u30eb\u30c8\u3092\u4f7f\u7528)", - "temp_step_override": "\u76ee\u6a19\u6e29\u5ea6\u30b9\u30c6\u30c3\u30d7", - "tuya_max_coltemp": "\u30c7\u30d0\u30a4\u30b9\u306b\u3088\u3063\u3066\u5831\u544a\u3055\u308c\u305f\u6700\u5927\u8272\u6e29\u5ea6", - "unit_of_measurement": "\u30c7\u30d0\u30a4\u30b9\u304c\u4f7f\u7528\u3059\u308b\u6e29\u5ea6\u5358\u4f4d" - }, - "description": "{device_type} \u30c7\u30d0\u30a4\u30b9 `{device_name}` \u306e\u8868\u793a\u60c5\u5831\u3092\u8abf\u6574\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u69cb\u6210\u3057\u307e\u3059", - "title": "Tuya\u30c7\u30d0\u30a4\u30b9\u306e\u8a2d\u5b9a" - }, - "init": { - "data": { - "discovery_interval": "\u30c7\u30d0\u30a4\u30b9\u691c\u51fa\u306e\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694(\u79d2\u5358\u4f4d)", - "list_devices": "\u8a2d\u5b9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3059\u308b\u304b\u3001\u7a7a\u767d\u306e\u307e\u307e\u306b\u3057\u3066\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3059", - "query_device": "\u30b9\u30c6\u30fc\u30bf\u30b9\u306e\u66f4\u65b0\u3092\u9ad8\u901f\u5316\u3059\u308b\u305f\u3081\u306b\u30af\u30a8\u30ea\u306e\u65b9\u6cd5(query method)\u3092\u4f7f\u7528\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u307e\u3059", - "query_interval": "\u30af\u30a8\u30ea\u30c7\u30d0\u30a4\u30b9\u306e\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694(\u79d2)" - }, - "description": "\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694\u306e\u5024\u3092\u4f4e\u304f\u8a2d\u5b9a\u3057\u3059\u304e\u306a\u3044\u3067\u304f\u3060\u3055\u3044\u3002\u30b3\u30fc\u30eb\u306b\u5931\u6557\u3057\u3066\u30ed\u30b0\u306b\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8\u304c\u751f\u6210\u3055\u308c\u307e\u3059\u3002", - "title": "Tuya\u30aa\u30d7\u30b7\u30e7\u30f3\u306e\u8a2d\u5b9a" + "description": "Tuya\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044" } } } diff --git a/homeassistant/components/tuya/translations/ka.json b/homeassistant/components/tuya/translations/ka.json deleted file mode 100644 index 7c80ef1ffba..00000000000 --- a/homeassistant/components/tuya/translations/ka.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "options": { - "error": { - "dev_multi_type": "\u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d0\u10ea\u10d8\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1 \u10e8\u10d4\u10e0\u10e9\u10d4\u10e3\u10da\u10d8 \u10db\u10e0\u10d0\u10d5\u10da\u10dd\u10d1\u10d8\u10d7\u10d8 \u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d0 \u10e3\u10dc\u10d3\u10d0 \u10d8\u10e7\u10dd\u10e1 \u10d4\u10e0\u10d7\u10dc\u10d0\u10d8\u10e0\u10d8 \u10e2\u10d8\u10de\u10d8\u10e1", - "dev_not_config": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10e2\u10d8\u10de\u10d8 \u10d0\u10e0 \u10d0\u10e0\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0\u10d3\u10d8", - "dev_not_found": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d0 \u10d5\u10d4\u10e0 \u10db\u10dd\u10d8\u10eb\u10d4\u10d1\u10dc\u10d0" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10db\u10d8\u10d4\u10e0 \u10d2\u10d0\u10db\u10dd\u10e7\u10d4\u10dc\u10d4\u10d1\u10e3\u10da\u10d8 \u10e1\u10d8\u10d9\u10d0\u10e8\u10d9\u10d0\u10e8\u10d8\u10e1 \u10d3\u10d8\u10d0\u10de\u10d0\u10d6\u10dd\u10dc\u10d8", - "curr_temp_divider": "\u10db\u10d8\u10db\u10d3\u10d8\u10dc\u10d0\u10e0\u10d4 \u10e2\u10d4\u10db\u10d4\u10de\u10e0\u10d0\u10e2\u10e3\u10e0\u10d8\u10e1 \u10db\u10dc\u10d8\u10e8\u10d5\u10dc\u10d4\u10da\u10d8\u10e1 \u10d2\u10d0\u10db\u10e7\u10dd\u10e4\u10d8 (0 - \u10dc\u10d0\u10d2\u10e3\u10da\u10d8\u10e1\u10ee\u10db\u10d4\u10d5\u10d8)", - "max_kelvin": "\u10db\u10ee\u10d0\u10e0\u10d3\u10d0\u10ed\u10d4\u10e0\u10d8\u10da\u10d8 \u10db\u10d0\u10e5\u10e1\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10d8\u10e1 \u10e4\u10d4\u10e0\u10d8 \u10d9\u10d4\u10da\u10d5\u10d8\u10dc\u10d4\u10d1\u10e8\u10d8", - "max_temp": "\u10db\u10d0\u10e5\u10e1\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10db\u10d8\u10d6\u10dc\u10dd\u10d1\u10e0\u10d8\u10d5\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10d0 (\u10db\u10d8\u10dc\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10d3\u10d0 \u10db\u10d0\u10e5\u10e1\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8\u10e1\u10d0\u10d7\u10d5\u10d8\u10e1 \u10dc\u10d0\u10d2\u10e3\u10da\u10d8\u10e1\u10ee\u10db\u10d4\u10d5\u10d8\u10d0 0)", - "min_kelvin": "\u10db\u10ee\u10d0\u10e0\u10d3\u10d0\u10ed\u10d4\u10e0\u10d8\u10da\u10d8 \u10db\u10d8\u10dc\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10d8\u10e1 \u10e4\u10d4\u10e0\u10d8 \u10d9\u10d4\u10da\u10d5\u10d8\u10dc\u10d4\u10d1\u10e8\u10d8", - "min_temp": "\u10db\u10d8\u10dc\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10db\u10d8\u10d6\u10dc\u10dd\u10d1\u10e0\u10d8\u10d5\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10d0 (\u10db\u10d8\u10dc\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10d3\u10d0 \u10db\u10d0\u10e5\u10e1\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8\u10e1\u10d0\u10d7\u10d5\u10d8\u10e1 \u10dc\u10d0\u10d2\u10e3\u10da\u10d8\u10e1\u10ee\u10db\u10d4\u10d5\u10d8\u10d0 0)", - "support_color": "\u10e4\u10d4\u10e0\u10d8\u10e1 \u10db\u10ee\u10d0\u10e0\u10d3\u10d0\u10ed\u10d4\u10e0\u10d0 \u10d8\u10eb\u10e3\u10da\u10d4\u10d1\u10d8\u10d7", - "temp_divider": "\u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10e3\u10da\u10d8 \u10db\u10dc\u10d8\u10e8\u10d5\u10dc\u10d4\u10da\u10d8\u10e1 \u10d2\u10d0\u10db\u10e7\u10dd\u10e4\u10d8 (0 = \u10dc\u10d0\u10d2\u10e3\u10da\u10d8\u10e1\u10ee\u10db\u10d4\u10d5\u10d8)", - "tuya_max_coltemp": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10db\u10d8\u10d4\u10e0 \u10db\u10dd\u10ec\u10dd\u10d3\u10d4\u10d1\u10e3\u10da\u10d8 \u10e4\u10d4\u10e0\u10d8\u10e1 \u10db\u10d0\u10e5\u10e1\u10d8\u10db\u10d0\u10da\u10e3\u10e0\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10d0", - "unit_of_measurement": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10db\u10d8\u10d4\u10e0 \u10d2\u10d0\u10db\u10dd\u10e7\u10d4\u10dc\u10d4\u10d1\u10e3\u10da\u10d8 \u10e2\u10d4\u10db\u10de\u10d4\u10e0\u10d0\u10e2\u10e3\u10e0\u10e3\u10da\u10d8 \u10d4\u10e0\u10d7\u10d4\u10e3\u10da\u10d8" - }, - "description": "\u10d3\u10d0\u10d0\u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d3 {device_type} `{device_name}` \u10de\u10d0\u10e0\u10d0\u10db\u10d4\u10e2\u10d4\u10e0\u10d1\u10d8 \u10d8\u10dc\u10e4\u10dd\u10e0\u10db\u10d0\u10ea\u10d8\u10d8\u10e1 \u10e9\u10d5\u10d4\u10dc\u10d4\u10d1\u10d8\u10e1 \u10db\u10dd\u10e1\u10d0\u10e0\u10d2\u10d4\u10d1\u10d0\u10d3", - "title": "Tuya-\u10e1 \u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0" - }, - "init": { - "data": { - "discovery_interval": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10d0\u10e6\u10db\u10dd\u10e9\u10d4\u10dc\u10d8\u10e1 \u10d2\u10d0\u10db\u10dd\u10d9\u10d8\u10d7\u10ee\u10d5\u10d8\u10e1 \u10d8\u10dc\u10e2\u10d4\u10e0\u10d5\u10d0\u10da\u10d8 \u10ec\u10d0\u10db\u10d4\u10d1\u10e8\u10d8", - "list_devices": "\u10d0\u10d8\u10e0\u10e9\u10d8\u10d4\u10d7 \u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d0 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d0\u10ea\u10d8\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1 \u10d0\u10dc \u10d3\u10d0\u10e2\u10dd\u10d5\u10d4\u10d7 \u10ea\u10d0\u10e0\u10d8\u10d4\u10da\u10d8 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d0\u10ea\u10d8\u10d8\u10e1 \u10e8\u10d4\u10e1\u10d0\u10dc\u10d0\u10ee\u10d0\u10d3", - "query_device": "\u10d0\u10d8\u10e0\u10e9\u10d8\u10d4\u10d7 \u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d0, \u10e0\u10dd\u10db\u10d4\u10da\u10d8\u10ea \u10d2\u10d0\u10db\u10dd\u10d8\u10e7\u10d4\u10dc\u10d4\u10d1\u10e1 \u10db\u10dd\u10d7\u10ee\u10dd\u10d5\u10dc\u10d8\u10e1 \u10db\u10d4\u10d7\u10dd\u10d3\u10e1 \u10e1\u10e2\u10d0\u10e2\u10e3\u10e1\u10d8\u10e1 \u10e1\u10ec\u10e0\u10d0\u10e4\u10d8 \u10d2\u10d0\u10dc\u10d0\u10ee\u10da\u10d4\u10d1\u10d8\u10e1\u10d7\u10d5\u10d8\u10e1", - "query_interval": "\u10db\u10dd\u10ec\u10e7\u10dd\u10d1\u10d8\u10da\u10dd\u10d1\u10d8\u10e1 \u10d2\u10d0\u10db\u10dd\u10d9\u10d8\u10d7\u10ee\u10d5\u10d8\u10e1 \u10db\u10dd\u10d7\u10ee\u10dd\u10d5\u10dc\u10d8\u10e1 \u10d8\u10dc\u10e2\u10d4\u10e0\u10d5\u10d0\u10da\u10d8 \u10ec\u10d0\u10db\u10d4\u10d1\u10e8\u10d8" - }, - "description": "\u10d0\u10e0 \u10d3\u10d0\u10d0\u10e7\u10d4\u10dc\u10dd\u10d7 \u10d2\u10d0\u10db\u10dd\u10d9\u10d8\u10d7\u10ee\u10d5\u10d8\u10e1 \u10d8\u10dc\u10e2\u10d4\u10e0\u10d5\u10d0\u10da\u10d8\u10e1 \u10db\u10dc\u10d8\u10e8\u10d5\u10dc\u10d4\u10da\u10dd\u10d1\u10d4\u10d1\u10d8 \u10eb\u10d0\u10da\u10d8\u10d0\u10dc \u10db\u10ea\u10d8\u10e0\u10d4 \u10d7\u10dd\u10e0\u10d4\u10d1 \u10d2\u10d0\u10db\u10dd\u10eb\u10d0\u10ee\u10d4\u10d1\u10d4\u10d1\u10d8 \u10d3\u10d0\u10d0\u10d2\u10d4\u10dc\u10d4\u10e0\u10d8\u10e0\u10d4\u10d1\u10d4\u10dc \u10e8\u10d4\u10ea\u10d3\u10dd\u10db\u10d4\u10d1\u10e1 \u10da\u10dd\u10d2\u10e8\u10d8", - "title": "Tuya-\u10e1 \u10de\u10d0\u10e0\u10d0\u10db\u10d4\u10e2\u10e0\u10d4\u10d1\u10d8\u10e1 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10d0" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/ko.json b/homeassistant/components/tuya/translations/ko.json index afa2541e7b9..be389e13b06 100644 --- a/homeassistant/components/tuya/translations/ko.json +++ b/homeassistant/components/tuya/translations/ko.json @@ -1,64 +1,16 @@ { "config": { - "abort": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." - }, "error": { "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, - "flow_title": "Tuya \uad6c\uc131\ud558\uae30", "step": { "user": { "data": { "country_code": "\uacc4\uc815 \uad6d\uac00 \ucf54\ub4dc (\uc608 : \ubbf8\uad6d\uc758 \uacbd\uc6b0 1, \uc911\uad6d\uc758 \uacbd\uc6b0 86)", "password": "\ube44\ubc00\ubc88\ud638", - "platform": "\uacc4\uc815\uc774 \ub4f1\ub85d\ub41c \uc571", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" }, - "description": "Tuya \uc790\uaca9 \uc99d\uba85\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694.", - "title": "Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" - }, - "error": { - "dev_multi_type": "\uc120\ud0dd\ud55c \uc5ec\ub7ec \uae30\uae30\ub97c \uad6c\uc131\ud558\ub824\uba74 \uc720\ud615\uc774 \ub3d9\uc77c\ud574\uc57c \ud569\ub2c8\ub2e4", - "dev_not_config": "\uae30\uae30 \uc720\ud615\uc744 \uad6c\uc131\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", - "dev_not_found": "\uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\uae30\uae30\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \ubc1d\uae30 \ubc94\uc704", - "curr_temp_divider": "\ud604\uc7ac \uc628\ub3c4 \uac12 \ubd84\ud560 (0 = \uae30\ubcf8\uac12 \uc0ac\uc6a9)", - "max_kelvin": "\uce98\ube48 \ub2e8\uc704\uc758 \ucd5c\ub300 \uc0c9\uc628\ub3c4", - "max_temp": "\ucd5c\ub300 \ubaa9\ud45c \uc628\ub3c4 (\uae30\ubcf8\uac12\uc758 \uacbd\uc6b0 \ucd5c\uc19f\uac12 \ubc0f \ucd5c\ub313\uac12 = 0)", - "min_kelvin": "\uce98\ube48 \ub2e8\uc704\uc758 \ucd5c\uc18c \uc0c9\uc628\ub3c4", - "min_temp": "\ucd5c\uc18c \ubaa9\ud45c \uc628\ub3c4 (\uae30\ubcf8\uac12\uc758 \uacbd\uc6b0 \ucd5c\uc19f\uac12 \ubc0f \ucd5c\ub313\uac12 = 0)", - "set_temp_divided": "\uc124\uc815 \uc628\ub3c4 \uba85\ub839\uc5d0 \ubd84\ud560\ub41c \uc628\ub3c4 \uac12 \uc0ac\uc6a9\ud558\uae30", - "support_color": "\uc0c9\uc0c1 \uc9c0\uc6d0 \uac15\uc81c \uc801\uc6a9\ud558\uae30", - "temp_divider": "\uc628\ub3c4 \uac12 \ubd84\ud560 (0 = \uae30\ubcf8\uac12 \uc0ac\uc6a9)", - "temp_step_override": "\ud76c\ub9dd \uc628\ub3c4 \ub2e8\uacc4", - "tuya_max_coltemp": "\uae30\uae30\uc5d0\uc11c \ubcf4\uace0\ud55c \ucd5c\ub300 \uc0c9\uc628\ub3c4", - "unit_of_measurement": "\uae30\uae30\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uc628\ub3c4 \ub2e8\uc704" - }, - "description": "{device_type} `{device_name}` \uae30\uae30\uc5d0 \ub300\ud574 \ud45c\uc2dc\ub418\ub294 \uc815\ubcf4\ub97c \uc870\uc815\ud558\ub294 \uc635\uc158 \uad6c\uc131\ud558\uae30", - "title": "Tuya \uae30\uae30 \uad6c\uc131\ud558\uae30" - }, - "init": { - "data": { - "discovery_interval": "\uae30\uae30 \uac80\uc0c9 \ud3f4\ub9c1 \uac04\uaca9 (\ucd08)", - "list_devices": "\uad6c\uc131\uc744 \uc800\uc7a5\ud558\ub824\uba74 \uad6c\uc131\ud560 \uae30\uae30\ub97c \uc120\ud0dd\ud558\uac70\ub098 \ube44\uc6cc \ub450\uc138\uc694", - "query_device": "\ube60\ub978 \uc0c1\ud0dc \uc5c5\ub370\uc774\ud2b8\ub97c \uc704\ud574 \ucffc\ub9ac \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560 \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694", - "query_interval": "\uae30\uae30 \ucffc\ub9ac \ud3f4\ub9c1 \uac04\uaca9 (\ucd08)" - }, - "description": "\ud3f4\ub9c1 \uac04\uaca9 \uac12\uc744 \ub108\ubb34 \ub0ae\uac8c \uc124\uc815\ud558\uc9c0 \ub9d0\uc544 \uc8fc\uc138\uc694. \uadf8\ub807\uc9c0 \uc54a\uc73c\uba74 \ud638\ucd9c\uc5d0 \uc2e4\ud328\ud558\uace0 \ub85c\uadf8\uc5d0 \uc624\ub958 \uba54\uc2dc\uc9c0\uac00 \uc0dd\uc131\ub429\ub2c8\ub2e4.", - "title": "Tuya \uc635\uc158 \uad6c\uc131\ud558\uae30" + "description": "Tuya \uc790\uaca9 \uc99d\uba85\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694." } } } diff --git a/homeassistant/components/tuya/translations/lb.json b/homeassistant/components/tuya/translations/lb.json index 0000f9ef6e6..8d0b16a3ff8 100644 --- a/homeassistant/components/tuya/translations/lb.json +++ b/homeassistant/components/tuya/translations/lb.json @@ -1,58 +1,16 @@ { "config": { - "abort": { - "cannot_connect": "Feeler beim verbannen", - "invalid_auth": "Ong\u00eblteg Authentifikatioun", - "single_instance_allowed": "Scho konfigur\u00e9iert. N\u00ebmmen eng eenzeg Konfiguratioun ass m\u00e9iglech." - }, "error": { "invalid_auth": "Ong\u00eblteg Authentifikatioun" }, - "flow_title": "Tuya Konfiguratioun", "step": { "user": { "data": { "country_code": "De L\u00e4nner Code fir d\u00e4i Kont (beispill 1 fir USA oder 86 fir China)", "password": "Passwuert", - "platform": "d'App wou den Kont registr\u00e9iert ass", "username": "Benotzernumm" }, - "description": "F\u00ebll deng Tuya Umeldungs Informatiounen aus.", - "title": "Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Feeler beim verbannen" - }, - "error": { - "dev_multi_type": "Multiple ausgewielte Ger\u00e4ter fir ze konfigur\u00e9ieren musse vum selwechten Typ sinn", - "dev_not_config": "Typ vun Apparat net konfigur\u00e9ierbar", - "dev_not_found": "Apparat net fonnt" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Hellegkeetsber\u00e4ich vum Apparat", - "curr_temp_divider": "Aktuell Temperatur W\u00e4erter Deeler (0= benotz Standard)", - "max_kelvin": "Maximal Faarftemperatur \u00ebnnerst\u00ebtzt a Kelvin", - "max_temp": "Maximal Zil Temperatur (benotz min a max = 0 fir standard)", - "min_kelvin": "Minimal Faarftemperatur \u00ebnnerst\u00ebtzt a Kelvin", - "min_temp": "Minimal Zil Temperatur (benotz min a max = 0 fir standard)", - "support_color": "Forc\u00e9ier Faarf \u00cbnnerst\u00ebtzung", - "temp_divider": "Temperatur W\u00e4erter Deeler (0= benotz Standard)", - "tuya_max_coltemp": "Max Faarftemperatur vum Apparat gemellt", - "unit_of_measurement": "Temperatur Eenheet vum Apparat" - }, - "description": "Konfigur\u00e9ier Optioune fir ugewisen Informatioune fir {device_type} Apparat `{device_name}` unzepassen", - "title": "Tuya Apparat ariichten" - }, - "init": { - "data": { - "list_devices": "Wiel d'Apparater fir ze konfigur\u00e9ieren aus oder loss se eidel fir d'Konfiguratioun ze sp\u00e4icheren" - }, - "title": "Tuya Optioune konfigur\u00e9ieren" + "description": "F\u00ebll deng Tuya Umeldungs Informatiounen aus." } } } diff --git a/homeassistant/components/tuya/translations/nl.json b/homeassistant/components/tuya/translations/nl.json index e12c3ba29f2..ed2ce07c226 100644 --- a/homeassistant/components/tuya/translations/nl.json +++ b/homeassistant/components/tuya/translations/nl.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Kan geen verbinding maken", - "invalid_auth": "Ongeldige authenticatie", - "single_instance_allowed": "Al geconfigureerd. Er is maar een configuratie mogelijk." - }, "error": { "invalid_auth": "Ongeldige authenticatie", "login_error": "Aanmeldingsfout ({code}): {msg}" }, - "flow_title": "Tuya-configuratie", "step": { - "login": { - "data": { - "access_id": "Toegangs-ID", - "access_secret": "Access Secret", - "country_code": "Landcode", - "endpoint": "Beschikbaarheidszone", - "password": "Wachtwoord", - "tuya_app_type": "Mobiele app", - "username": "Account" - }, - "description": "Voer uw Tuya-inloggegevens in", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT-toegangs-ID", "access_secret": "Tuya IoT Access Secret", "country_code": "Land", "password": "Wachtwoord", - "platform": "De app waar uw account is geregistreerd", - "region": "Regio", - "tuya_project_type": "Tuya cloud project type", "username": "Gebruikersnaam" }, - "description": "Voer uw Tuya-inloggegevens in.", - "title": "Tuya-integratie" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Kan geen verbinding maken" - }, - "error": { - "dev_multi_type": "Meerdere geselecteerde apparaten om te configureren moeten van hetzelfde type zijn", - "dev_not_config": "Apparaattype kan niet worden geconfigureerd", - "dev_not_found": "Apparaat niet gevonden" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Helderheidsbereik gebruikt door apparaat", - "curr_temp_divider": "Huidige temperatuurwaarde deler (0 = standaardwaarde)", - "max_kelvin": "Max kleurtemperatuur in kelvin", - "max_temp": "Maximale doeltemperatuur (gebruik min en max = 0 voor standaardwaarde)", - "min_kelvin": "Minimaal ondersteunde kleurtemperatuur in kelvin", - "min_temp": "Min. gewenste temperatuur (gebruik min en max = 0 voor standaard)", - "set_temp_divided": "Gedeelde temperatuurwaarde gebruiken voor ingestelde temperatuuropdracht", - "support_color": "Forceer kleurenondersteuning", - "temp_divider": "Temperatuurwaarde deler (0 = standaardwaarde)", - "temp_step_override": "Doeltemperatuur stap", - "tuya_max_coltemp": "Max. kleurtemperatuur gerapporteerd door apparaat", - "unit_of_measurement": "Temperatuureenheid gebruikt door apparaat" - }, - "description": "Configureer opties om weergegeven informatie aan te passen voor {device_type} apparaat `{device_name}`", - "title": "Configureer Tuya Apparaat" - }, - "init": { - "data": { - "discovery_interval": "Polling-interval van nieuwe apparaten in seconden", - "list_devices": "Selecteer de te configureren apparaten of laat leeg om de configuratie op te slaan", - "query_device": "Selecteer apparaat dat query-methode zal gebruiken voor snellere statusupdate", - "query_interval": "Ververstijd van het apparaat in seconden" - }, - "description": "Stel de waarden voor het pollinginterval niet te laag in, anders zullen de oproepen geen foutmelding in het logboek genereren", - "title": "Configureer Tuya opties" + "description": "Voer uw Tuya-inloggegevens in." } } } diff --git a/homeassistant/components/tuya/translations/no.json b/homeassistant/components/tuya/translations/no.json index 16657972322..7c0137b80a6 100644 --- a/homeassistant/components/tuya/translations/no.json +++ b/homeassistant/components/tuya/translations/no.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Tilkobling mislyktes", - "invalid_auth": "Ugyldig godkjenning", - "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." - }, "error": { "invalid_auth": "Ugyldig godkjenning", "login_error": "P\u00e5loggingsfeil ( {code} ): {msg}" }, - "flow_title": "Tuya konfigurasjon", "step": { - "login": { - "data": { - "access_id": "Tilgangs -ID", - "access_secret": "Tilgangshemmelighet", - "country_code": "Landskode", - "endpoint": "Tilgjengelighetssone", - "password": "Passord", - "tuya_app_type": "Mobilapp", - "username": "Konto" - }, - "description": "Skriv inn Tuya-legitimasjonen din", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT Access Secret", "country_code": "Land", "password": "Passord", - "platform": "Appen der kontoen din er registrert", - "region": "Region", - "tuya_project_type": "Tuya -skyprosjekttype", "username": "Account" }, - "description": "Skriv inn Tuya -legitimasjonen din", - "title": "Tuya Integrasjon" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Tilkobling mislyktes" - }, - "error": { - "dev_multi_type": "Flere valgte enheter som skal konfigureres, m\u00e5 v\u00e6re av samme type", - "dev_not_config": "Enhetstype kan ikke konfigureres", - "dev_not_found": "Finner ikke enheten" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Lysstyrkeomr\u00e5de som brukes av enheten", - "curr_temp_divider": "N\u00e5v\u00e6rende temperaturverdi (0 = bruk standard)", - "max_kelvin": "Maks fargetemperatur st\u00f8ttet i kelvin", - "max_temp": "Maks m\u00e5ltemperatur (bruk min og maks = 0 for standard)", - "min_kelvin": "Min fargetemperatur st\u00f8ttet i kelvin", - "min_temp": "Min m\u00e5ltemperatur (bruk min og maks = 0 for standard)", - "set_temp_divided": "Bruk delt temperaturverdi for innstilt temperaturkommando", - "support_color": "Tving fargest\u00f8tte", - "temp_divider": "Deler temperaturverdier (0 = bruk standard)", - "temp_step_override": "Trinn for m\u00e5ltemperatur", - "tuya_max_coltemp": "Maks fargetemperatur rapportert av enheten", - "unit_of_measurement": "Temperaturenhet som brukes av enheten" - }, - "description": "Konfigurer alternativer for \u00e5 justere vist informasjon for {device_type} device ` {device_name} `", - "title": "Konfigurere Tuya-enhet" - }, - "init": { - "data": { - "discovery_interval": "Avsp\u00f8rringsintervall for discovery-enheten i l\u00f8pet av sekunder", - "list_devices": "Velg enhetene du vil konfigurere, eller la de v\u00e6re tomme for \u00e5 lagre konfigurasjonen", - "query_device": "Velg enhet som skal bruke sp\u00f8rringsmetode for raskere statusoppdatering", - "query_interval": "Sp\u00f8rringsintervall for intervall i sekunder" - }, - "description": "Ikke angi pollingsintervallverdiene for lave, ellers vil ikke anropene generere feilmelding i loggen", - "title": "Konfigurer Tuya-alternativer" + "description": "Skriv inn Tuya -legitimasjonen din" } } } diff --git a/homeassistant/components/tuya/translations/pl.json b/homeassistant/components/tuya/translations/pl.json index a191c40ca3f..e7fe9caf65a 100644 --- a/homeassistant/components/tuya/translations/pl.json +++ b/homeassistant/components/tuya/translations/pl.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_auth": "Niepoprawne uwierzytelnienie", - "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." - }, "error": { "invalid_auth": "Niepoprawne uwierzytelnienie", "login_error": "B\u0142\u0105d logowania ({code}): {msg}" }, - "flow_title": "Konfiguracja integracji Tuya", "step": { - "login": { - "data": { - "access_id": "Identyfikator dost\u0119pu", - "access_secret": "Has\u0142o dost\u0119pu", - "country_code": "Kod kraju", - "endpoint": "Strefa dost\u0119pno\u015bci", - "password": "Has\u0142o", - "tuya_app_type": "Aplikacja mobilna", - "username": "Konto" - }, - "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce Tuya", - "title": "Tuya" - }, "user": { "data": { "access_id": "Identyfikator dost\u0119pu do Tuya IoT", "access_secret": "Has\u0142o dost\u0119pu do Tuya IoT", "country_code": "Kraj", "password": "Has\u0142o", - "platform": "Aplikacja, w kt\u00f3rej zarejestrowane jest Twoje konto", - "region": "Region", - "tuya_project_type": "Typ projektu chmury Tuya", "username": "Konto" }, - "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce Tuya", - "title": "Integracja Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" - }, - "error": { - "dev_multi_type": "Wybrane urz\u0105dzenia do skonfigurowania musz\u0105 by\u0107 tego samego typu", - "dev_not_config": "Typ urz\u0105dzenia nie jest konfigurowalny", - "dev_not_found": "Nie znaleziono urz\u0105dzenia" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Zakres jasno\u015bci u\u017cywany przez urz\u0105dzenie", - "curr_temp_divider": "Dzielnik aktualnej warto\u015bci temperatury (0 = u\u017cyj warto\u015bci domy\u015blnej)", - "max_kelvin": "Maksymalna obs\u0142ugiwana temperatura barwy w kelwinach", - "max_temp": "Maksymalna temperatura docelowa (u\u017cyj min i max = 0 dla warto\u015bci domy\u015blnej)", - "min_kelvin": "Minimalna obs\u0142ugiwana temperatura barwy w kelwinach", - "min_temp": "Minimalna temperatura docelowa (u\u017cyj min i max = 0 dla warto\u015bci domy\u015blnej)", - "set_temp_divided": "U\u017cyj podzielonej warto\u015bci temperatury dla polecenia ustawienia temperatury", - "support_color": "Wymu\u015b obs\u0142ug\u0119 kolor\u00f3w", - "temp_divider": "Dzielnik warto\u015bci temperatury (0 = u\u017cyj warto\u015bci domy\u015blnej)", - "temp_step_override": "Krok docelowej temperatury", - "tuya_max_coltemp": "Maksymalna temperatura barwy raportowana przez urz\u0105dzenie", - "unit_of_measurement": "Jednostka temperatury u\u017cywana przez urz\u0105dzenie" - }, - "description": "Skonfiguruj opcje, aby dostosowa\u0107 wy\u015bwietlane informacje dla urz\u0105dzenia {device_type} `{device_name}'", - "title": "Konfiguracja urz\u0105dzenia Tuya" - }, - "init": { - "data": { - "discovery_interval": "Cz\u0119stotliwo\u015b\u0107 skanowania nowych urz\u0105dze\u0144 (w sekundach)", - "list_devices": "Wybierz urz\u0105dzenia do skonfigurowania lub pozostaw puste, aby zapisa\u0107 konfiguracj\u0119", - "query_device": "Wybierz urz\u0105dzenie, kt\u00f3re b\u0119dzie u\u017cywa\u0107 metody odpytywania w celu szybszej aktualizacji statusu", - "query_interval": "Cz\u0119stotliwo\u015b\u0107 skanowania odpytywanego urz\u0105dzenia w sekundach" - }, - "description": "Nie ustawiaj zbyt niskich warto\u015bci skanowania, bo zako\u0144cz\u0105 si\u0119 niepowodzeniem, generuj\u0105c komunikat o b\u0142\u0119dzie w logu", - "title": "Konfiguracja opcji Tuya" + "description": "Wprowad\u017a dane uwierzytelniaj\u0105ce Tuya" } } } diff --git a/homeassistant/components/tuya/translations/pt-BR.json b/homeassistant/components/tuya/translations/pt-BR.json index 242d1e9ee08..5ae0382d0b8 100644 --- a/homeassistant/components/tuya/translations/pt-BR.json +++ b/homeassistant/components/tuya/translations/pt-BR.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Falha ao conectar", - "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma configura\u00e7\u00e3o \u00e9 poss\u00edvel." - }, "error": { "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", "login_error": "Erro de login ({code}): {msg}" }, - "flow_title": "Configura\u00e7\u00e3o Tuya", "step": { - "login": { - "data": { - "access_id": "Tuya IoT Access ID", - "access_secret": "Tuya IoT Access Secret", - "country_code": "Pa\u00eds", - "endpoint": "Regi\u00e3o", - "password": "Senha do Aplicativo", - "tuya_app_type": "O aplicativo onde sua conta \u00e9 registrada", - "username": "Usu\u00e1rio do Aplicativo" - }, - "description": "Digite sua credencial Tuya", - "title": "Integra\u00e7\u00e3o Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT Access Secret", "country_code": "Pa\u00eds", "password": "Senha do Aplicativo", - "platform": "O aplicativo onde sua conta \u00e9 registrada", - "region": "Regi\u00e3o", - "tuya_project_type": "Tipo de projeto de Tuya Cloud", "username": "Usu\u00e1rio do Aplicativo" }, - "description": "Digite sua credencial Tuya", - "title": "Integra\u00e7\u00e3o Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Falha ao conectar" - }, - "error": { - "dev_multi_type": "V\u00e1rios dispositivos selecionados para configurar devem ser do mesmo tipo", - "dev_not_config": "Tipo de dispositivo n\u00e3o configur\u00e1vel", - "dev_not_found": "Dispositivo n\u00e3o encontrado" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Faixa de brilho usada pelo dispositivo", - "curr_temp_divider": "Divisor do valor da temperatura atual (0 = usar o padr\u00e3o)", - "max_kelvin": "Temperatura m\u00e1xima de cor suportada em Kelvin", - "max_temp": "Temperatura m\u00e1xima do alvo (use min e max = 0 para padr\u00e3o)", - "min_kelvin": "Temperatura m\u00ednima de cor suportada em kelvin", - "min_temp": "Temperatura m\u00ednima desejada (use m\u00edn e m\u00e1x = 0 para o padr\u00e3o)", - "set_temp_divided": "Use o valor de temperatura dividido para o comando de temperatura definido", - "support_color": "For\u00e7ar suporte de cores", - "temp_divider": "Divisor de valores de temperatura (0 = usar padr\u00e3o)", - "temp_step_override": "Etapa de temperatura alvo", - "tuya_max_coltemp": "Temperatura m\u00e1xima de cor relatada pelo dispositivo", - "unit_of_measurement": "Unidade de temperatura usada pelo dispositivo" - }, - "description": "Configure as op\u00e7\u00f5es para ajustar as informa\u00e7\u00f5es exibidas para o dispositivo {device_type} `{device_name}`", - "title": "Configurar dispositivo Tuya" - }, - "init": { - "data": { - "discovery_interval": "Intervalo de pesquisa do dispositivo de descoberta em segundos", - "list_devices": "Selecione os dispositivos para configurar ou deixe em branco para salvar a configura\u00e7\u00e3o", - "query_device": "Selecione o dispositivo que usar\u00e1 o m\u00e9todo de consulta para atualiza\u00e7\u00e3o de status mais r\u00e1pida", - "query_interval": "Intervalo de sondagem do dispositivo de consulta em segundos" - }, - "description": "N\u00e3o defina valores de intervalo de sondagens muito baixos ou as chamadas falhar\u00e3o gerando mensagem de erro no log", - "title": "Configurar op\u00e7\u00f5es do Tuya" + "description": "Digite sua credencial Tuya" } } } diff --git a/homeassistant/components/tuya/translations/pt.json b/homeassistant/components/tuya/translations/pt.json index 566746538c0..f9de53a43fe 100644 --- a/homeassistant/components/tuya/translations/pt.json +++ b/homeassistant/components/tuya/translations/pt.json @@ -1,10 +1,5 @@ { "config": { - "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o", - "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "single_instance_allowed": "J\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." - }, "error": { "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida" }, @@ -16,10 +11,5 @@ } } } - }, - "options": { - "abort": { - "cannot_connect": "Falha na liga\u00e7\u00e3o" - } } } \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/ru.json b/homeassistant/components/tuya/translations/ru.json index e13914683f1..f58a78d1bb5 100644 --- a/homeassistant/components/tuya/translations/ru.json +++ b/homeassistant/components/tuya/translations/ru.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." - }, "error": { "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "login_error": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 ({code}): {msg}" }, - "flow_title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Tuya", "step": { - "login": { - "data": { - "access_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043e\u0441\u0442\u0443\u043f\u0430", - "access_secret": "\u0421\u0435\u043a\u0440\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430", - "country_code": "\u041a\u043e\u0434 \u0441\u0442\u0440\u0430\u043d\u044b", - "endpoint": "\u0417\u043e\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438", - "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "tuya_app_type": "\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435", - "username": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Tuya.", - "title": "Tuya" - }, "user": { "data": { "access_id": "\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 Tuya IoT", "access_secret": "\u0421\u0435\u043a\u0440\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 Tuya IoT", "country_code": "\u0421\u0442\u0440\u0430\u043d\u0430", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "platform": "\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c", - "region": "\u0420\u0435\u0433\u0438\u043e\u043d", - "tuya_project_type": "\u0422\u0438\u043f \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Tuya", "username": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Tuya.", - "title": "Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f." - }, - "error": { - "dev_multi_type": "\u041d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.", - "dev_not_config": "\u0422\u0438\u043f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f.", - "dev_not_found": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e." - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u044f\u0440\u043a\u043e\u0441\u0442\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c", - "curr_temp_divider": "\u0414\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b (0 = \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)", - "max_kelvin": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0432 \u043a\u0435\u043b\u044c\u0432\u0438\u043d\u0430\u0445)", - "max_temp": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 min \u0438 max = 0)", - "min_kelvin": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u0430\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0432 \u043a\u0435\u043b\u044c\u0432\u0438\u043d\u0430\u0445)", - "min_temp": "\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0435\u043b\u0435\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 min \u0438 max = 0)", - "set_temp_divided": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b \u0434\u043b\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b", - "support_color": "\u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0446\u0432\u0435\u0442\u0430", - "temp_divider": "\u0414\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b (0 = \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)", - "temp_step_override": "\u0428\u0430\u0433 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b", - "tuya_max_coltemp": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u0430\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430, \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u043c\u0430\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c", - "unit_of_measurement": "\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c" - }, - "description": "\u041e\u043f\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0434\u043b\u044f {device_type} \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 `{device_name}`", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 Tuya" - }, - "init": { - "data": { - "discovery_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)", - "list_devices": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438", - "query_device": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u0430", - "query_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u041d\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0439\u0442\u0435 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043d\u0438\u0437\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0430 \u043e\u043f\u0440\u043e\u0441\u0430, \u0438\u043d\u0430\u0447\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0432 \u0436\u0443\u0440\u043d\u0430\u043b\u0435.", - "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Tuya" + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Tuya." } } } diff --git a/homeassistant/components/tuya/translations/sk.json b/homeassistant/components/tuya/translations/sk.json index 23d8822116c..93fa886f796 100644 --- a/homeassistant/components/tuya/translations/sk.json +++ b/homeassistant/components/tuya/translations/sk.json @@ -1,21 +1,12 @@ { "config": { - "abort": { - "invalid_auth": "Neplatn\u00e9 overenie" - }, "error": { "invalid_auth": "Neplatn\u00e9 overenie" }, "step": { - "login": { - "data": { - "country_code": "K\u00f3d krajiny" - } - }, "user": { "data": { - "country_code": "Krajina", - "region": "Oblas\u0165" + "country_code": "Krajina" } } } diff --git a/homeassistant/components/tuya/translations/sl.json b/homeassistant/components/tuya/translations/sl.json deleted file mode 100644 index 52d2fd3e973..00000000000 --- a/homeassistant/components/tuya/translations/sl.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "config": { - "step": { - "user": { - "data": { - "region": "Regija" - } - } - } - }, - "options": { - "abort": { - "cannot_connect": "Povezovanje ni uspelo." - }, - "error": { - "dev_not_config": "Vrsta naprave ni nastavljiva", - "dev_not_found": "Naprave ni mogo\u010de najti" - } - } -} \ No newline at end of file diff --git a/homeassistant/components/tuya/translations/sv.json b/homeassistant/components/tuya/translations/sv.json index 85cc9c57fd3..344aea60f6f 100644 --- a/homeassistant/components/tuya/translations/sv.json +++ b/homeassistant/components/tuya/translations/sv.json @@ -1,16 +1,13 @@ { "config": { - "flow_title": "Tuya-konfiguration", "step": { "user": { "data": { "country_code": "Landskod f\u00f6r ditt konto (t.ex. 1 f\u00f6r USA eller 86 f\u00f6r Kina)", "password": "L\u00f6senord", - "platform": "Appen d\u00e4r ditt konto registreras", "username": "Anv\u00e4ndarnamn" }, - "description": "Ange dina Tuya anv\u00e4ndaruppgifter.", - "title": "Tuya" + "description": "Ange dina Tuya anv\u00e4ndaruppgifter." } } } diff --git a/homeassistant/components/tuya/translations/tr.json b/homeassistant/components/tuya/translations/tr.json index 1bcc2bc627e..fe1b81831a6 100644 --- a/homeassistant/components/tuya/translations/tr.json +++ b/homeassistant/components/tuya/translations/tr.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." - }, "error": { "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "login_error": "Giri\u015f hatas\u0131 ( {code} ): {msg}" }, - "flow_title": "Tuya yap\u0131land\u0131rmas\u0131", "step": { - "login": { - "data": { - "access_id": "Eri\u015fim Kimli\u011fi", - "access_secret": "Eri\u015fim Anahtar\u0131", - "country_code": "\u00dclke Kodu", - "endpoint": "Kullan\u0131labilirlik Alan\u0131", - "password": "\u015eifre", - "tuya_app_type": "Tuya uygulama tipi", - "username": "Kullan\u0131c\u0131 Ad\u0131" - }, - "description": "Tuya kimlik bilgilerinizi girin", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Eri\u015fim Kimli\u011fi", "access_secret": "Tuya IoT Eri\u015fim Anahtar\u0131", "country_code": "\u00dclke", "password": "Parola", - "platform": "Hesab\u0131n\u0131z\u0131n kay\u0131tl\u0131 oldu\u011fu uygulama", - "region": "B\u00f6lge", - "tuya_project_type": "Tuya bulut proje t\u00fcr\u00fc", "username": "Hesap" }, - "description": "Tuya kimlik bilgilerinizi girin.", - "title": "Tuya Entegrasyonu" - } - } - }, - "options": { - "abort": { - "cannot_connect": "Ba\u011flanma hatas\u0131" - }, - "error": { - "dev_multi_type": "Yap\u0131land\u0131r\u0131lacak birden \u00e7ok se\u00e7ili cihaz ayn\u0131 t\u00fcrde olmal\u0131d\u0131r", - "dev_not_config": "Cihaz t\u00fcr\u00fc yap\u0131land\u0131r\u0131lamaz", - "dev_not_found": "Cihaz bulunamad\u0131" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "Cihaz\u0131n kulland\u0131\u011f\u0131 parlakl\u0131k aral\u0131\u011f\u0131", - "curr_temp_divider": "Mevcut S\u0131cakl\u0131k de\u011feri b\u00f6l\u00fcc\u00fc (0 = varsay\u0131lan\u0131 kullan)", - "max_kelvin": "Kelvin'de desteklenen maksimum renk s\u0131cakl\u0131\u011f\u0131", - "max_temp": "Maksimum hedef s\u0131cakl\u0131k (varsay\u0131lan olarak min ve maks = 0 kullan\u0131n)", - "min_kelvin": "Kelvin destekli min renk s\u0131cakl\u0131\u011f\u0131", - "min_temp": "Minimum hedef s\u0131cakl\u0131k (varsay\u0131lan i\u00e7in min ve maks = 0 kullan\u0131n)", - "set_temp_divided": "Ayarlanan s\u0131cakl\u0131k komutu i\u00e7in b\u00f6l\u00fcnm\u00fc\u015f S\u0131cakl\u0131k de\u011ferini kullan\u0131n", - "support_color": "Vurgu rengi", - "temp_divider": "S\u0131cakl\u0131k de\u011ferleri ay\u0131r\u0131c\u0131 (0 = varsay\u0131lan\u0131 kullan)", - "temp_step_override": "Hedef S\u0131cakl\u0131k ad\u0131m\u0131", - "tuya_max_coltemp": "Cihaz taraf\u0131ndan bildirilen maksimum renk s\u0131cakl\u0131\u011f\u0131", - "unit_of_measurement": "Cihaz\u0131n kulland\u0131\u011f\u0131 s\u0131cakl\u0131k birimi" - }, - "description": "{device_type} cihaz\u0131 ` {device_name} device_name} ` i\u00e7in g\u00f6r\u00fcnt\u00fclenen bilgileri ayarlamak \u00fczere se\u00e7enekleri yap\u0131land\u0131r\u0131n", - "title": "Tuya Cihaz\u0131n\u0131 Yap\u0131land\u0131r\u0131n" - }, - "init": { - "data": { - "discovery_interval": "Cihaz\u0131 yoklama aral\u0131\u011f\u0131 saniye cinsinden", - "list_devices": "Yap\u0131land\u0131rmay\u0131 kaydetmek i\u00e7in yap\u0131land\u0131r\u0131lacak veya bo\u015f b\u0131rak\u0131lacak cihazlar\u0131 se\u00e7in", - "query_device": "Daha h\u0131zl\u0131 durum g\u00fcncellemesi i\u00e7in sorgu y\u00f6ntemini kullanacak cihaz\u0131 se\u00e7in", - "query_interval": "Ayg\u0131t yoklama aral\u0131\u011f\u0131 saniye cinsinden" - }, - "description": "Yoklama aral\u0131\u011f\u0131 de\u011ferlerini \u00e7ok d\u00fc\u015f\u00fck ayarlamay\u0131n, aksi takdirde \u00e7a\u011fr\u0131lar g\u00fcnl\u00fckte hata mesaj\u0131 olu\u015fturarak ba\u015far\u0131s\u0131z olur", - "title": "Tuya Se\u00e7eneklerini Konfig\u00fcre Et" + "description": "Tuya kimlik bilgilerinizi girin." } } } diff --git a/homeassistant/components/tuya/translations/uk.json b/homeassistant/components/tuya/translations/uk.json index 97616e5f388..0e1535d9681 100644 --- a/homeassistant/components/tuya/translations/uk.json +++ b/homeassistant/components/tuya/translations/uk.json @@ -1,62 +1,16 @@ { "config": { - "abort": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", - "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f.", - "single_instance_allowed": "\u0412\u0436\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0430\u043d\u043e. \u041c\u043e\u0436\u043b\u0438\u0432\u0430 \u043b\u0438\u0448\u0435 \u043e\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u044f." - }, "error": { "invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f." }, - "flow_title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f Tuya", "step": { "user": { "data": { "country_code": "\u041a\u043e\u0434 \u043a\u0440\u0430\u0457\u043d\u0438 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443 (1 \u0434\u043b\u044f \u0421\u0428\u0410 \u0430\u0431\u043e 86 \u0434\u043b\u044f \u041a\u0438\u0442\u0430\u044e)", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "platform": "\u0414\u043e\u0434\u0430\u0442\u043e\u043a, \u0432 \u044f\u043a\u043e\u043c\u0443 \u0437\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u043e\u0432\u0430\u043d\u043e \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441", "username": "\u0406\u043c'\u044f \u043a\u043e\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Tuya.", - "title": "Tuya" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" - }, - "error": { - "dev_multi_type": "\u041a\u0456\u043b\u044c\u043a\u0430 \u043e\u0431\u0440\u0430\u043d\u0438\u0445 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432 \u0434\u043b\u044f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u043e\u0432\u0438\u043d\u043d\u0456 \u0431\u0443\u0442\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0443.", - "dev_not_config": "\u0422\u0438\u043f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e \u043d\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f.", - "dev_not_found": "\u041f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043d\u0435 \u0437\u043d\u0430\u0439\u0434\u0435\u043d\u043e." - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u0414\u0456\u0430\u043f\u0430\u0437\u043e\u043d \u044f\u0441\u043a\u0440\u0430\u0432\u043e\u0441\u0442\u0456, \u044f\u043a\u0438\u0439 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0454\u043c", - "curr_temp_divider": "\u0414\u0456\u043b\u044c\u043d\u0438\u043a \u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0438 (0 = \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c)", - "max_kelvin": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0432\u0430\u043d\u0430 \u043a\u043e\u043b\u0456\u0440\u043d\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0432 \u043a\u0435\u043b\u044c\u0432\u0456\u043d\u0430\u0445)", - "max_temp": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430 \u0446\u0456\u043b\u044c\u043e\u0432\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 min \u0456 max = 0)", - "min_kelvin": "\u041c\u0456\u043d\u0456\u043c\u0430\u043b\u044c\u043d\u0430 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0432\u0430\u043d\u0430 \u043a\u043e\u043b\u0456\u0440\u043d\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0432 \u043a\u0435\u043b\u044c\u0432\u0456\u043d\u0430\u0445)", - "min_temp": "\u041c\u0456\u043d\u0456\u043c\u0430\u043b\u044c\u043d\u0430 \u0446\u0456\u043b\u044c\u043e\u0432\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0439\u0442\u0435 min \u0456 max = 0)", - "support_color": "\u041f\u0440\u0438\u043c\u0443\u0441\u043e\u0432\u0430 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u043a\u0430 \u043a\u043e\u043b\u044c\u043e\u0440\u0443", - "temp_divider": "\u0414\u0456\u043b\u044c\u043d\u0438\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u044c \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0438 (0 = \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0437\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c)", - "tuya_max_coltemp": "\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430 \u043a\u043e\u043b\u0456\u0440\u043d\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430, \u044f\u043a\u0430 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u044f\u0454\u0442\u044c\u0441\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0454\u043c", - "unit_of_measurement": "\u041e\u0434\u0438\u043d\u0438\u0446\u044f \u0432\u0438\u043c\u0456\u0440\u0443 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0438, \u044f\u043a\u0430 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0454\u043c" - }, - "description": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 \u0434\u043b\u044f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0438\u0434\u0438\u043c\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u0434\u043b\u044f {device_type} \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e '{device_name}'", - "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e Tuya" - }, - "init": { - "data": { - "discovery_interval": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)", - "list_devices": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457 \u0434\u043b\u044f \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0430\u0431\u043e \u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c \u0434\u043b\u044f \u0437\u0431\u0435\u0440\u0435\u0436\u0435\u043d\u043d\u044f \u043a\u043e\u043d\u0444\u0456\u0433\u0443\u0440\u0430\u0446\u0456\u0457", - "query_device": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439, \u044f\u043a\u0438\u0439 \u0431\u0443\u0434\u0435 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430\u043f\u0438\u0442\u0443 \u0434\u043b\u044f \u0431\u0456\u043b\u044c\u0448 \u0448\u0432\u0438\u0434\u043a\u043e\u0433\u043e \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u0443", - "query_interval": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u044e (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)" - }, - "description": "\u041d\u0435 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u044e\u0439\u0442\u0435 \u0437\u0430\u043d\u0430\u0434\u0442\u043e \u043d\u0438\u0437\u044c\u043a\u0456 \u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u0456\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u0443 \u043e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u043d\u044f, \u0456\u043d\u0430\u043a\u0448\u0435 \u0432\u0438\u043a\u043b\u0438\u043a\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442\u044c \u0433\u0435\u043d\u0435\u0440\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u043d\u044f \u043f\u0440\u043e \u043f\u043e\u043c\u0438\u043b\u043a\u0443 \u0432 \u0436\u0443\u0440\u043d\u0430\u043b\u0456.", - "title": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f Tuya" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437 Tuya." } } } diff --git a/homeassistant/components/tuya/translations/zh-Hans.json b/homeassistant/components/tuya/translations/zh-Hans.json index b990d5bb8a1..b5d00c5586a 100644 --- a/homeassistant/components/tuya/translations/zh-Hans.json +++ b/homeassistant/components/tuya/translations/zh-Hans.json @@ -1,81 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u8fde\u63a5\u5931\u8d25", - "invalid_auth": "\u8eab\u4efd\u8ba4\u8bc1\u65e0\u6548", - "single_instance_allowed": "\u8be5\u96c6\u6210\u5df2\u7ecf\u914d\u7f6e\u8fc7\u4e86\uff0c\u4e14\u53ea\u80fd\u914d\u7f6e\u4e00\u6b21\u3002\u82e5\u8981\u91cd\u65b0\u914d\u7f6e\uff0c\u8bf7\u5148\u5220\u9664\u65e7\u96c6\u6210\u3002" - }, "error": { "invalid_auth": "\u8eab\u4efd\u8ba4\u8bc1\u65e0\u6548", "login_error": "\u767b\u5f55\u5931\u8d25({code}): {msg}" }, - "flow_title": "\u6d82\u9e26\u914d\u7f6e", "step": { - "login": { - "data": { - "access_id": "\u8bbf\u95ee ID", - "access_secret": "\u8bbf\u95ee\u5bc6\u94a5", - "country_code": "\u56fd\u5bb6\u4ee3\u7801", - "endpoint": "\u53ef\u7528\u533a\u57df", - "password": "\u5bc6\u7801", - "tuya_app_type": "\u79fb\u52a8\u5e94\u7528", - "username": "\u5e10\u6237" - }, - "description": "\u8bf7\u8f93\u5165\u60a8\u7684\u6d82\u9e26\u8d26\u6237\u4fe1\u606f", - "title": "\u6d82\u9e26" - }, "user": { "data": { "access_id": "\u6d82\u9e26\u7269\u8054\u7f51\u8bbe\u5907\u63a5\u5165 ID", "access_secret": "\u6d82\u9e26\u7269\u8054\u7f51\u8bbe\u5907\u63a5\u5165\u5bc6\u94a5", "country_code": "\u60a8\u7684\u5e10\u6237\u56fd\u5bb6(\u5730\u533a)\u4ee3\u7801\uff08\u4f8b\u5982\u4e2d\u56fd\u4e3a 86\uff0c\u7f8e\u56fd\u4e3a 1\uff09", "password": "\u5bc6\u7801", - "platform": "\u60a8\u6ce8\u518c\u5e10\u6237\u7684\u5e94\u7528", - "region": "\u5730\u533a", - "tuya_project_type": "\u6d82\u9e26\u4e91\u9879\u76ee\u7c7b\u578b", "username": "\u7528\u6237\u540d" }, - "description": "\u8bf7\u8f93\u5165\u6d82\u9e26\u8d26\u6237\u4fe1\u606f\u3002", - "title": "\u6d82\u9e26" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u8fde\u63a5\u5931\u8d25" - }, - "error": { - "dev_multi_type": "\u591a\u4e2a\u8981\u914d\u7f6e\u7684\u8bbe\u5907\u5fc5\u987b\u5177\u6709\u76f8\u540c\u7684\u7c7b\u578b", - "dev_not_config": "\u8bbe\u5907\u7c7b\u578b\u4e0d\u53ef\u914d\u7f6e", - "dev_not_found": "\u672a\u627e\u5230\u8bbe\u5907" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u8bbe\u5907\u4f7f\u7528\u7684\u4eae\u5ea6\u8303\u56f4", - "curr_temp_divider": "\u5f53\u524d\u6e29\u5ea6\u503c\u9664\u6570\uff080 = \u4f7f\u7528\u9ed8\u8ba4\u503c\uff09", - "max_kelvin": "\u6700\u9ad8\u652f\u6301\u8272\u6e29\uff08\u5f00\u6c0f\uff09", - "max_temp": "\u6700\u9ad8\u76ee\u6807\u6e29\u5ea6\uff08min \u548c max \u4e3a 0 \u65f6\u4f7f\u7528\u9ed8\u8ba4\uff09", - "min_kelvin": "\u6700\u4f4e\u652f\u6301\u8272\u6e29\uff08\u5f00\u6c0f\uff09", - "min_temp": "\u6700\u4f4e\u76ee\u6807\u6e29\u5ea6\uff08min \u548c max \u4e3a 0 \u65f6\u4f7f\u7528\u9ed8\u8ba4\uff09", - "set_temp_divided": "\u5bf9\u8bbe\u7f6e\u6e29\u5ea6\u547d\u4ee4\u4f7f\u7528\u7ecf\u8fc7\u9664\u6cd5\u7684\u6e29\u5ea6\u503c", - "support_color": "\u5f3a\u5236\u652f\u6301\u8c03\u8272", - "temp_divider": "\u6e29\u5ea6\u503c\u9664\u6570\uff080 = \u4f7f\u7528\u9ed8\u8ba4\u503c\uff09", - "temp_step_override": "\u76ee\u6807\u6e29\u5ea6\u6b65\u957f", - "tuya_max_coltemp": "\u8bbe\u5907\u62a5\u544a\u7684\u6700\u9ad8\u8272\u6e29", - "unit_of_measurement": "\u8bbe\u5907\u4f7f\u7528\u7684\u6e29\u5ea6\u5355\u4f4d" - }, - "title": "\u914d\u7f6e\u6d82\u9e26\u8bbe\u5907" - }, - "init": { - "data": { - "discovery_interval": "\u53d1\u73b0\u8bbe\u5907\u8f6e\u8be2\u95f4\u9694\uff08\u4ee5\u79d2\u4e3a\u5355\u4f4d\uff09", - "list_devices": "\u8bf7\u9009\u62e9\u8981\u914d\u7f6e\u7684\u8bbe\u5907\uff0c\u6216\u7559\u7a7a\u4ee5\u4fdd\u5b58\u914d\u7f6e", - "query_device": "\u8bf7\u9009\u62e9\u4f7f\u7528\u67e5\u8be2\u65b9\u6cd5\u7684\u8bbe\u5907\uff0c\u4ee5\u4fbf\u66f4\u5feb\u5730\u66f4\u65b0\u72b6\u6001", - "query_interval": "\u67e5\u8be2\u8bbe\u5907\u8f6e\u8be2\u95f4\u9694\uff08\u4ee5\u79d2\u4e3a\u5355\u4f4d\uff09" - }, - "description": "\u8bf7\u4e0d\u8981\u5c06\u8f6e\u8be2\u95f4\u9694\u8bbe\u7f6e\u5f97\u592a\u4f4e\uff0c\u5426\u5219\u5c06\u8c03\u7528\u5931\u8d25\u5e76\u5728\u65e5\u5fd7\u751f\u6210\u9519\u8bef\u6d88\u606f", - "title": "\u6d82\u9e26\u914d\u7f6e\u9009\u9879" + "description": "\u8bf7\u8f93\u5165\u6d82\u9e26\u8d26\u6237\u4fe1\u606f\u3002" } } } diff --git a/homeassistant/components/tuya/translations/zh-Hant.json b/homeassistant/components/tuya/translations/zh-Hant.json index e9898f782af..c255cb723a9 100644 --- a/homeassistant/components/tuya/translations/zh-Hant.json +++ b/homeassistant/components/tuya/translations/zh-Hant.json @@ -1,82 +1,19 @@ { "config": { - "abort": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" - }, "error": { "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "login_error": "\u767b\u5165\u932f\u8aa4\uff08{code}\uff09\uff1a{msg}" }, - "flow_title": "Tuya \u8a2d\u5b9a", "step": { - "login": { - "data": { - "access_id": "Access ID", - "access_secret": "\u5b58\u53d6\u79c1\u9470", - "country_code": "\u570b\u78bc", - "endpoint": "\u53ef\u7528\u5340\u57df", - "password": "\u5bc6\u78bc", - "tuya_app_type": "\u624b\u6a5f App", - "username": "\u5e33\u865f" - }, - "description": "\u8f38\u5165 Tuya \u6191\u8b49", - "title": "Tuya" - }, "user": { "data": { "access_id": "Tuya IoT Access ID", "access_secret": "Tuya IoT \u5b58\u53d6\u79c1\u9470", "country_code": "\u570b\u5bb6", "password": "\u5bc6\u78bc", - "platform": "\u5e33\u6236\u8a3b\u518a\u6240\u5728\u4f4d\u7f6e", - "region": "\u5340\u57df", - "tuya_project_type": "Tuya \u96f2\u5c08\u6848\u985e\u5225", "username": "\u5e33\u865f" }, - "description": "\u8f38\u5165 Tuya \u6191\u8b49", - "title": "Tuya \u6574\u5408" - } - } - }, - "options": { - "abort": { - "cannot_connect": "\u9023\u7dda\u5931\u6557" - }, - "error": { - "dev_multi_type": "\u591a\u91cd\u9078\u64c7\u8a2d\u88dd\u7f6e\u4ee5\u8a2d\u5b9a\u4f7f\u7528\u76f8\u540c\u985e\u5225", - "dev_not_config": "\u88dd\u7f6e\u985e\u5225\u7121\u6cd5\u8a2d\u5b9a", - "dev_not_found": "\u627e\u4e0d\u5230\u88dd\u7f6e" - }, - "step": { - "device": { - "data": { - "brightness_range_mode": "\u88dd\u7f6e\u6240\u4f7f\u7528\u4e4b\u4eae\u5ea6\u7bc4\u570d", - "curr_temp_divider": "\u76ee\u524d\u8272\u6eab\u503c\u5206\u914d\u5668\uff080 = \u4f7f\u7528\u9810\u8a2d\uff09", - "max_kelvin": "Kelvin \u652f\u63f4\u6700\u9ad8\u8272\u6eab", - "max_temp": "\u6700\u9ad8\u76ee\u6a19\u8272\u6eab\uff08\u4f7f\u7528\u6700\u4f4e\u8207\u6700\u9ad8 = 0 \u4f7f\u7528\u9810\u8a2d\uff09", - "min_kelvin": "Kelvin \u652f\u63f4\u6700\u4f4e\u8272\u6eab", - "min_temp": "\u6700\u4f4e\u76ee\u6a19\u8272\u6eab\uff08\u4f7f\u7528\u6700\u4f4e\u8207\u6700\u9ad8 = 0 \u4f7f\u7528\u9810\u8a2d\uff09", - "set_temp_divided": "\u4f7f\u7528\u5206\u9694\u865f\u6eab\u5ea6\u503c\u4ee5\u57f7\u884c\u8a2d\u5b9a\u6eab\u5ea6\u6307\u4ee4", - "support_color": "\u5f37\u5236\u8272\u6eab\u652f\u63f4", - "temp_divider": "\u8272\u6eab\u503c\u5206\u914d\u5668\uff080 = \u4f7f\u7528\u9810\u8a2d\uff09", - "temp_step_override": "\u76ee\u6a19\u6eab\u5ea6\u8a2d\u5b9a", - "tuya_max_coltemp": "\u88dd\u7f6e\u56de\u5831\u6700\u9ad8\u8272\u6eab", - "unit_of_measurement": "\u88dd\u7f6e\u6240\u4f7f\u7528\u4e4b\u6eab\u5ea6\u55ae\u4f4d" - }, - "description": "\u8a2d\u5b9a\u9078\u9805\u4ee5\u8abf\u6574 {device_type} \u88dd\u7f6e `{device_name}` \u986f\u793a\u8cc7\u8a0a", - "title": "\u8a2d\u5b9a Tuya \u88dd\u7f6e" - }, - "init": { - "data": { - "discovery_interval": "\u641c\u7d22\u88dd\u7f6e\u66f4\u65b0\u79d2\u9593\u8ddd", - "list_devices": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684\u88dd\u7f6e\u3001\u6216\u4fdd\u6301\u7a7a\u767d\u4ee5\u5132\u5b58\u8a2d\u5b9a", - "query_device": "\u9078\u64c7\u88dd\u7f6e\u5c07\u4f7f\u7528\u67e5\u8a62\u65b9\u5f0f\u4ee5\u7372\u5f97\u66f4\u5feb\u7684\u72c0\u614b\u66f4\u65b0", - "query_interval": "\u67e5\u8a62\u88dd\u7f6e\u66f4\u65b0\u79d2\u9593\u8ddd" - }, - "description": "\u66f4\u65b0\u9593\u8ddd\u4e0d\u8981\u8a2d\u5b9a\u7684\u904e\u4f4e\u3001\u53ef\u80fd\u6703\u5c0e\u81f4\u65bc\u65e5\u8a8c\u4e2d\u7522\u751f\u932f\u8aa4\u8a0a\u606f", - "title": "\u8a2d\u5b9a Tuya \u9078\u9805" + "description": "\u8f38\u5165 Tuya \u6191\u8b49" } } } diff --git a/homeassistant/components/twentemilieu/translations/nl.json b/homeassistant/components/twentemilieu/translations/nl.json index 575c642a777..740c38da286 100644 --- a/homeassistant/components/twentemilieu/translations/nl.json +++ b/homeassistant/components/twentemilieu/translations/nl.json @@ -5,7 +5,7 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_address": "Adres niet gevonden in servicegebied Twente Milieu." + "invalid_address": "Adres niet gevonden in Twente Milieu servicegebied." }, "step": { "user": { diff --git a/homeassistant/components/ukraine_alarm/translations/bg.json b/homeassistant/components/ukraine_alarm/translations/bg.json index 181f28745b9..ed64f99c6b2 100644 --- a/homeassistant/components/ukraine_alarm/translations/bg.json +++ b/homeassistant/components/ukraine_alarm/translations/bg.json @@ -6,11 +6,6 @@ "max_regions": "\u041c\u043e\u0433\u0430\u0442 \u0434\u0430 \u0441\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 5 \u0440\u0435\u0433\u0438\u043e\u043d\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" }, - "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" - }, "step": { "community": { "data": { @@ -22,14 +17,8 @@ "region": "\u0420\u0435\u0433\u0438\u043e\u043d" } }, - "state": { - "data": { - "region": "\u0420\u0435\u0433\u0438\u043e\u043d" - } - }, "user": { "data": { - "api_key": "API \u043a\u043b\u044e\u0447", "region": "\u0420\u0435\u0433\u0438\u043e\u043d" }, "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f\u0442\u0430 Ukraine Alarm. \u0417\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447, \u043e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}" diff --git a/homeassistant/components/ukraine_alarm/translations/ca.json b/homeassistant/components/ukraine_alarm/translations/ca.json index c650423723d..ea03b64dd3b 100644 --- a/homeassistant/components/ukraine_alarm/translations/ca.json +++ b/homeassistant/components/ukraine_alarm/translations/ca.json @@ -8,12 +8,6 @@ "timeout": "Temps m\u00e0xim d'espera per establir la connexi\u00f3 esgotat", "unknown": "Error inesperat" }, - "error": { - "cannot_connect": "Ha fallat la connexi\u00f3", - "invalid_api_key": "Clau API inv\u00e0lida", - "timeout": "Temps m\u00e0xim d'espera per establir la connexi\u00f3 esgotat", - "unknown": "Error inesperat" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Si vols monitorar no nom\u00e9s l'estat, tria el districte espec\u00edfic" }, - "state": { - "data": { - "region": "Regi\u00f3" - }, - "description": "Escull l'estat a monitorar" - }, "user": { "data": { - "api_key": "Clau API", "region": "Regi\u00f3" }, "description": "Escull l'estat a monitorar" diff --git a/homeassistant/components/ukraine_alarm/translations/de.json b/homeassistant/components/ukraine_alarm/translations/de.json index 2eb99ee692f..7c7c093c7cd 100644 --- a/homeassistant/components/ukraine_alarm/translations/de.json +++ b/homeassistant/components/ukraine_alarm/translations/de.json @@ -8,12 +8,6 @@ "timeout": "Zeit\u00fcberschreitung beim Verbindungsaufbau", "unknown": "Unerwarteter Fehler" }, - "error": { - "cannot_connect": "Verbindung fehlgeschlagen", - "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel", - "timeout": "Zeit\u00fcberschreitung beim Verbindungsaufbau", - "unknown": "Unerwarteter Fehler" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Wenn du nicht nur das Bundesland \u00fcberwachen m\u00f6chtest, w\u00e4hle seinen bestimmten Bezirk" }, - "state": { - "data": { - "region": "Region" - }, - "description": "Zu \u00fcberwachendes Bundesland ausw\u00e4hlen" - }, "user": { "data": { - "api_key": "API-Schl\u00fcssel", "region": "Region" }, "description": "Zu \u00fcberwachendes Bundesland ausw\u00e4hlen" diff --git a/homeassistant/components/ukraine_alarm/translations/el.json b/homeassistant/components/ukraine_alarm/translations/el.json index 61b40412415..779c7dc1102 100644 --- a/homeassistant/components/ukraine_alarm/translations/el.json +++ b/homeassistant/components/ukraine_alarm/translations/el.json @@ -8,12 +8,6 @@ "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, - "error": { - "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "invalid_api_key": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API", - "timeout": "\u03a7\u03c1\u03bf\u03bd\u03b9\u03ba\u03cc \u03cc\u03c1\u03b9\u03bf \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "\u0391\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03b5\u03af\u03c4\u03b5 \u03cc\u03c7\u03b9 \u03bc\u03cc\u03bd\u03bf \u03c4\u03b7\u03bd \u03c0\u03bf\u03bb\u03b9\u03c4\u03b5\u03af\u03b1, \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03b3\u03ba\u03b5\u03ba\u03c1\u03b9\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae \u03c4\u03b7\u03c2." }, - "state": { - "data": { - "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7 \u03b3\u03b9\u03b1 \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7" - }, "user": { "data": { - "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", "region": "\u03a0\u03b5\u03c1\u03b9\u03bf\u03c7\u03ae\u03c2" }, "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd \u03c4\u03b7\u03c2 \u039f\u03c5\u03ba\u03c1\u03b1\u03bd\u03af\u03b1\u03c2. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03ad\u03bd\u03b1 \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 {api_url}" diff --git a/homeassistant/components/ukraine_alarm/translations/en.json b/homeassistant/components/ukraine_alarm/translations/en.json index 6397f652aee..857311ea3e7 100644 --- a/homeassistant/components/ukraine_alarm/translations/en.json +++ b/homeassistant/components/ukraine_alarm/translations/en.json @@ -8,12 +8,6 @@ "timeout": "Timeout establishing connection", "unknown": "Unexpected error" }, - "error": { - "cannot_connect": "Failed to connect", - "invalid_api_key": "Invalid API key", - "timeout": "Timeout establishing connection", - "unknown": "Unexpected error" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "If you want to monitor not only state, choose its specific district" }, - "state": { - "data": { - "region": "Region" - }, - "description": "Choose state to monitor" - }, "user": { "data": { - "api_key": "API Key", "region": "Region" }, "description": "Choose state to monitor" diff --git a/homeassistant/components/ukraine_alarm/translations/es.json b/homeassistant/components/ukraine_alarm/translations/es.json index 7f8db3877c0..a9efcd0d536 100644 --- a/homeassistant/components/ukraine_alarm/translations/es.json +++ b/homeassistant/components/ukraine_alarm/translations/es.json @@ -6,9 +6,6 @@ "timeout": "Tiempo m\u00e1ximo de espera para establecer la conexi\u00f3n agotado", "unknown": "Error inesperado" }, - "error": { - "unknown": "Error inesperado" - }, "step": { "district": { "data": { @@ -16,15 +13,8 @@ }, "description": "Si quieres monitorear no s\u00f3lo el estado, elige el distrito espec\u00edfico" }, - "state": { - "data": { - "region": "Regi\u00f3n" - }, - "description": "Escoja el estado a monitorear" - }, "user": { "data": { - "api_key": "Clave API", "region": "Regi\u00f3n" }, "description": "Escoja el estado a monitorear" diff --git a/homeassistant/components/ukraine_alarm/translations/et.json b/homeassistant/components/ukraine_alarm/translations/et.json index 452b9d8cd11..1c5d74db071 100644 --- a/homeassistant/components/ukraine_alarm/translations/et.json +++ b/homeassistant/components/ukraine_alarm/translations/et.json @@ -8,12 +8,6 @@ "timeout": "\u00dchenduse ajal\u00f5pp", "unknown": "Ootamatu t\u00f5rge" }, - "error": { - "cannot_connect": "\u00dchendamine nurjus", - "invalid_api_key": "Vigane API v\u00f5ti", - "timeout": "\u00dchendamise ajal\u00f5pp", - "unknown": "Ootamatu t\u00f5rge" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Kui soovid j\u00e4lgida mitte ainult oblastit vali konkreetne piirkond" }, - "state": { - "data": { - "region": "Piirkond" - }, - "description": "Vali j\u00e4lgitav oblast" - }, "user": { "data": { - "api_key": "API v\u00f5ti", "region": "Piirkond" }, "description": "Vali j\u00e4lgitav oblast" diff --git a/homeassistant/components/ukraine_alarm/translations/fr.json b/homeassistant/components/ukraine_alarm/translations/fr.json index 6cafde0e4d0..9a8bd95da63 100644 --- a/homeassistant/components/ukraine_alarm/translations/fr.json +++ b/homeassistant/components/ukraine_alarm/translations/fr.json @@ -8,12 +8,6 @@ "timeout": "D\u00e9lai d'attente pour \u00e9tablir la connexion expir\u00e9", "unknown": "Erreur inattendue" }, - "error": { - "cannot_connect": "\u00c9chec de connexion", - "invalid_api_key": "Cl\u00e9 d'API non valide", - "timeout": "D\u00e9lai d'attente pour \u00e9tablir la connexion expir\u00e9", - "unknown": "Erreur inattendue" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Si vous ne d\u00e9sirez pas uniquement surveiller l'\u00c9tat, s\u00e9lectionnez son district sp\u00e9cifique" }, - "state": { - "data": { - "region": "R\u00e9gion" - }, - "description": "Choisissez l'\u00c9tat \u00e0 surveiller" - }, "user": { "data": { - "api_key": "Cl\u00e9 d'API", "region": "R\u00e9gion" }, "description": "Choisissez l'\u00c9tat \u00e0 surveiller" diff --git a/homeassistant/components/ukraine_alarm/translations/he.json b/homeassistant/components/ukraine_alarm/translations/he.json new file mode 100644 index 00000000000..6f3d95e2bb4 --- /dev/null +++ b/homeassistant/components/ukraine_alarm/translations/he.json @@ -0,0 +1,27 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05de\u05d9\u05e7\u05d5\u05dd \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05d7\u05d9\u05d1\u05d5\u05e8", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "community": { + "data": { + "region": "\u05d0\u05d9\u05d6\u05d5\u05e8" + } + }, + "district": { + "data": { + "region": "\u05d0\u05d9\u05d6\u05d5\u05e8" + } + }, + "user": { + "data": { + "region": "\u05d0\u05d9\u05d6\u05d5\u05e8" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ukraine_alarm/translations/hu.json b/homeassistant/components/ukraine_alarm/translations/hu.json index e72d6140c01..64273f1348b 100644 --- a/homeassistant/components/ukraine_alarm/translations/hu.json +++ b/homeassistant/components/ukraine_alarm/translations/hu.json @@ -8,34 +8,21 @@ "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a kapcsolat l\u00e9trehoz\u00e1sa sor\u00e1n", "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, - "error": { - "cannot_connect": "Sikertelen csatlakoz\u00e1s", - "invalid_api_key": "\u00c9rv\u00e9nytelen API kulcs", - "timeout": "Id\u0151t\u00fall\u00e9p\u00e9s a kapcsolat l\u00e9trehoz\u00e1sa sor\u00e1n", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" - }, "step": { "community": { "data": { - "region": "R\u00e9gi\u00f3" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Ha nem csak a megy\u00e9t \u00e9s a ker\u00fcletet szeretn\u00e9 figyelni, v\u00e1lassza ki az adott k\u00f6z\u00f6ss\u00e9get" }, "district": { "data": { - "region": "R\u00e9gi\u00f3" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Ha nem csak megy\u00e9t szeretne figyelni, v\u00e1lassza ki az adott ker\u00fcletet" }, - "state": { - "data": { - "region": "R\u00e9gi\u00f3" - }, - "description": "V\u00e1lassza ki a megfigyelni k\u00edv\u00e1nt megy\u00e9t" - }, "user": { "data": { - "api_key": "API kulcs", "region": "R\u00e9gi\u00f3" }, "description": "\u00c1ll\u00edtsa be az Ukraine Alarm integr\u00e1ci\u00f3t. API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}" diff --git a/homeassistant/components/ukraine_alarm/translations/id.json b/homeassistant/components/ukraine_alarm/translations/id.json index bd453d78e06..1751c132e9d 100644 --- a/homeassistant/components/ukraine_alarm/translations/id.json +++ b/homeassistant/components/ukraine_alarm/translations/id.json @@ -8,12 +8,6 @@ "timeout": "Tenggang waktu membuat koneksi habis", "unknown": "Kesalahan yang tidak diharapkan" }, - "error": { - "cannot_connect": "Gagal terhubung", - "invalid_api_key": "Kunci API tidak valid", - "timeout": "Tenggang waktu membuat koneksi habis", - "unknown": "Kesalahan yang tidak diharapkan" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Jika ingin memantau tidak hanya negara bagian, pilih distrik tertentu" }, - "state": { - "data": { - "region": "Wilayah" - }, - "description": "Pilih negara bagian untuk dipantau" - }, "user": { "data": { - "api_key": "Kunci API", "region": "Wilayah" }, "description": "Pilih negara bagian untuk dipantau" diff --git a/homeassistant/components/ukraine_alarm/translations/it.json b/homeassistant/components/ukraine_alarm/translations/it.json index 4994cf45d33..7c02193a43b 100644 --- a/homeassistant/components/ukraine_alarm/translations/it.json +++ b/homeassistant/components/ukraine_alarm/translations/it.json @@ -8,34 +8,21 @@ "timeout": "Tempo scaduto per stabile la connessione.", "unknown": "Errore imprevisto" }, - "error": { - "cannot_connect": "Impossibile connettersi", - "invalid_api_key": "Chiave API non valida", - "timeout": "Tempo scaduto per stabile la connessione.", - "unknown": "Errore imprevisto" - }, "step": { "community": { "data": { - "region": "Regione" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Se vuoi monitorare non solo stato e distretto, scegli la sua comunit\u00e0 specifica" }, "district": { "data": { - "region": "Regione" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Se vuoi monitorare non solo lo stato, scegli il suo distretto specifico" }, - "state": { - "data": { - "region": "Regione" - }, - "description": "Scegli lo stato da monitorare" - }, "user": { "data": { - "api_key": "Chiave API", "region": "Regione" }, "description": "Scegli lo stato da monitorare" diff --git a/homeassistant/components/ukraine_alarm/translations/ja.json b/homeassistant/components/ukraine_alarm/translations/ja.json index b879cf4541e..81bdabdc8d3 100644 --- a/homeassistant/components/ukraine_alarm/translations/ja.json +++ b/homeassistant/components/ukraine_alarm/translations/ja.json @@ -8,34 +8,21 @@ "timeout": "\u63a5\u7d9a\u78ba\u7acb\u6642\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, - "error": { - "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", - "invalid_api_key": "\u7121\u52b9\u306aAPI\u30ad\u30fc", - "timeout": "\u63a5\u7d9a\u78ba\u7acb\u6642\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" - }, "step": { "community": { "data": { - "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "\u5dde\u3084\u5730\u533a\u3060\u3051\u3067\u306a\u304f\u3001\u7279\u5b9a\u306e\u5730\u57df\u3092\u76e3\u8996\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u305d\u306e\u5730\u57df\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" }, "district": { "data": { - "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "\u5dde\u3060\u3051\u3067\u306a\u304f\u3001\u7279\u5b9a\u306e\u5730\u533a\u3092\u76e3\u8996\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u305d\u306e\u5730\u533a\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044" }, - "state": { - "data": { - "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" - }, - "description": "\u30e2\u30cb\u30bf\u30fc\u3059\u308b\u72b6\u614b\u3092\u9078\u629e" - }, "user": { "data": { - "api_key": "API\u30ad\u30fc", "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" }, "description": "\u30a6\u30af\u30e9\u30a4\u30ca\u306e\u8b66\u5831\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001 {api_url} \u306b\u79fb\u52d5\u3057\u3066\u304f\u3060\u3055\u3044" diff --git a/homeassistant/components/ukraine_alarm/translations/ko.json b/homeassistant/components/ukraine_alarm/translations/ko.json index ab66909f96a..e9da77f866b 100644 --- a/homeassistant/components/ukraine_alarm/translations/ko.json +++ b/homeassistant/components/ukraine_alarm/translations/ko.json @@ -7,11 +7,6 @@ "rate_limit": "\uc694\uccad\uc774 \ub108\ubb34 \ub9ce\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, - "error": { - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", - "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" - }, "step": { "community": { "data": { @@ -25,15 +20,8 @@ }, "description": "\uc8fc\ubfd0\ub9cc \uc544\ub2c8\ub77c \ud2b9\uc815 \uc9c0\uc5ed\uc744 \ubaa8\ub2c8\ud130\ub9c1\ud558\ub824\uba74 \ud574\ub2f9 \uc9c0\uc5ed\uc744 \uc120\ud0dd\ud558\uc2ed\uc2dc\uc624" }, - "state": { - "data": { - "region": "\uc9c0\uc5ed" - }, - "description": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \uc8fc \uc120\ud0dd" - }, "user": { "data": { - "api_key": "API \ud0a4", "region": "\uc9c0\uc5ed" }, "description": "\ubaa8\ub2c8\ud130\ub9c1\ud560 \uc8fc \uc120\ud0dd" diff --git a/homeassistant/components/ukraine_alarm/translations/nl.json b/homeassistant/components/ukraine_alarm/translations/nl.json index ab9973a0618..6f09f794ac4 100644 --- a/homeassistant/components/ukraine_alarm/translations/nl.json +++ b/homeassistant/components/ukraine_alarm/translations/nl.json @@ -8,34 +8,21 @@ "timeout": "Time-out bij het maken van verbinding", "unknown": "Onverwachte fout" }, - "error": { - "cannot_connect": "Kan geen verbinding maken", - "invalid_api_key": "Ongeldige API-sleutel", - "timeout": "Time-out bij het maken van verbinding", - "unknown": "Onverwachte fout" - }, "step": { "community": { "data": { - "region": "Regio" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Als u niet alleen de staat en het district wilt volgen, kiest u de specifieke gemeenschap ervan" }, "district": { "data": { - "region": "Regio" + "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" }, "description": "Als u niet alleen de staat wilt controleren, kies dan het specifieke district" }, - "state": { - "data": { - "region": "Regio" - }, - "description": "Kies staat om te monitoren" - }, "user": { "data": { - "api_key": "API-sleutel", "region": "Regio" }, "description": "Kies staat om te monitoren" diff --git a/homeassistant/components/ukraine_alarm/translations/no.json b/homeassistant/components/ukraine_alarm/translations/no.json index 10a717c72ec..de5e4284cb0 100644 --- a/homeassistant/components/ukraine_alarm/translations/no.json +++ b/homeassistant/components/ukraine_alarm/translations/no.json @@ -8,12 +8,6 @@ "timeout": "Tidsavbrudd oppretter forbindelse", "unknown": "Uventet feil" }, - "error": { - "cannot_connect": "Tilkobling mislyktes", - "invalid_api_key": "Ugyldig API-n\u00f8kkel", - "timeout": "Tidsavbrudd oppretter forbindelse", - "unknown": "Uventet feil" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Hvis du ikke bare vil overv\u00e5ke staten, velg dens spesifikke distrikt" }, - "state": { - "data": { - "region": "Region" - }, - "description": "Velg tilstand \u00e5 overv\u00e5ke" - }, "user": { "data": { - "api_key": "API-n\u00f8kkel", "region": "Region" }, "description": "Velg tilstand \u00e5 overv\u00e5ke" diff --git a/homeassistant/components/ukraine_alarm/translations/pl.json b/homeassistant/components/ukraine_alarm/translations/pl.json index 6b970b759ed..7d432900372 100644 --- a/homeassistant/components/ukraine_alarm/translations/pl.json +++ b/homeassistant/components/ukraine_alarm/translations/pl.json @@ -8,12 +8,6 @@ "timeout": "Limit czasu na nawi\u0105zanie po\u0142\u0105czenia", "unknown": "Nieoczekiwany b\u0142\u0105d" }, - "error": { - "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", - "invalid_api_key": "Nieprawid\u0142owy klucz API", - "timeout": "Limit czasu na nawi\u0105zanie po\u0142\u0105czenia", - "unknown": "Nieoczekiwany b\u0142\u0105d" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Je\u015bli nie chcesz monitorowa\u0107 tylko rejonu, wybierz jego konkretny dystrykt" }, - "state": { - "data": { - "region": "Region" - }, - "description": "Wybierz rejon do monitorowania" - }, "user": { "data": { - "api_key": "Klucz API", "region": "Region" }, "description": "Wybierz rejon do monitorowania" diff --git a/homeassistant/components/ukraine_alarm/translations/pt-BR.json b/homeassistant/components/ukraine_alarm/translations/pt-BR.json index 316a8e96563..64b371196e3 100644 --- a/homeassistant/components/ukraine_alarm/translations/pt-BR.json +++ b/homeassistant/components/ukraine_alarm/translations/pt-BR.json @@ -8,12 +8,6 @@ "timeout": "Tempo limite estabelecendo conex\u00e3o", "unknown": "Erro inesperado" }, - "error": { - "cannot_connect": "Falhou ao conectar", - "invalid_api_key": "Chave de API inv\u00e1lida", - "timeout": "Tempo limite estabelecendo conex\u00e3o", - "unknown": "Erro inesperado" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Se voc\u00ea deseja monitorar n\u00e3o apenas o estado, escolha seu distrito espec\u00edfico" }, - "state": { - "data": { - "region": "Regi\u00e3o" - }, - "description": "Escolha o estado para monitorar" - }, "user": { "data": { - "api_key": "Chave de API", "region": "Regi\u00e3o" }, "description": "Escolha o estado para monitorar" diff --git a/homeassistant/components/ukraine_alarm/translations/ru.json b/homeassistant/components/ukraine_alarm/translations/ru.json index fdb9d4b3f23..8944129497a 100644 --- a/homeassistant/components/ukraine_alarm/translations/ru.json +++ b/homeassistant/components/ukraine_alarm/translations/ru.json @@ -8,12 +8,6 @@ "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, - "error": { - "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", - "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", - "timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 \u043e\u0431\u043b\u0430\u0441\u0442\u044c\u044e, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0451 \u0433\u0440\u043e\u043c\u0430\u0434\u0443" }, - "state": { - "data": { - "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" - }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" }, "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f" diff --git a/homeassistant/components/ukraine_alarm/translations/tr.json b/homeassistant/components/ukraine_alarm/translations/tr.json index 3662a32b1fa..2b36f5b73f0 100644 --- a/homeassistant/components/ukraine_alarm/translations/tr.json +++ b/homeassistant/components/ukraine_alarm/translations/tr.json @@ -8,12 +8,6 @@ "timeout": "Ba\u011flant\u0131 kurulurken zaman a\u015f\u0131m\u0131", "unknown": "Beklenmeyen hata" }, - "error": { - "cannot_connect": "Ba\u011flanma hatas\u0131", - "invalid_api_key": "Ge\u00e7ersiz API anahtar\u0131", - "timeout": "Ba\u011flant\u0131 kurulurken zaman a\u015f\u0131m\u0131", - "unknown": "Beklenmeyen hata" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "Sadece eyaleti izlemek istemiyorsan\u0131z, belirli bir b\u00f6lgeyi se\u00e7in" }, - "state": { - "data": { - "region": "B\u00f6lge" - }, - "description": "\u0130zlenecek durumu se\u00e7in" - }, "user": { "data": { - "api_key": "API Anahtar\u0131", "region": "B\u00f6lge" }, "description": "\u0130zlenecek durumu se\u00e7in" diff --git a/homeassistant/components/ukraine_alarm/translations/uk.json b/homeassistant/components/ukraine_alarm/translations/uk.json index 2d5e881e41b..930f9ab31de 100644 --- a/homeassistant/components/ukraine_alarm/translations/uk.json +++ b/homeassistant/components/ukraine_alarm/translations/uk.json @@ -8,12 +8,6 @@ "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0437\u2019\u0454\u0434\u043d\u0430\u043d\u043d\u044f", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" }, - "error": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", - "invalid_api_key": "\u0425\u0438\u0431\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 API", - "timeout": "\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u0432\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f \u0437\u2019\u0454\u0434\u043d\u0430\u043d\u043d\u044f", - "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "\u042f\u043a\u0449\u043e \u0432\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0443\u0432\u0430\u0442\u0438 \u043d\u0435 \u0442\u0456\u043b\u044c\u043a\u0438 \u043e\u0431\u043b\u0430\u0441\u0442\u044c, \u043e\u0431\u0435\u0440\u0456\u0442\u044c \u0457\u0457 \u0433\u0440\u043e\u043c\u0430\u0434\u0443" }, - "state": { - "data": { - "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" - }, - "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0435\u043d\u043d\u044f" - }, "user": { "data": { - "api_key": "\u041a\u043b\u044e\u0447 API", "region": "\u041e\u0431\u043b\u0430\u0441\u0442\u044c" }, "description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u0432\u0456\u0434\u0441\u0442\u0435\u0436\u0435\u043d\u043d\u044f" diff --git a/homeassistant/components/ukraine_alarm/translations/zh-Hant.json b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json index 33e75d23df0..73b5ff2cce3 100644 --- a/homeassistant/components/ukraine_alarm/translations/zh-Hant.json +++ b/homeassistant/components/ukraine_alarm/translations/zh-Hant.json @@ -8,12 +8,6 @@ "timeout": "\u5efa\u7acb\u9023\u7dda\u903e\u6642", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, - "error": { - "cannot_connect": "\u9023\u7dda\u5931\u6557", - "invalid_api_key": "API \u91d1\u9470\u7121\u6548", - "timeout": "\u5efa\u7acb\u9023\u7dda\u903e\u6642", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" - }, "step": { "community": { "data": { @@ -27,15 +21,8 @@ }, "description": "\u5047\u5982\u4f60\u4e0d\u53ea\u8981\u76e3\u770b\u72c0\u614b\u3001\u8acb\u9078\u64c7\u7279\u5b9a\u5340\u57df" }, - "state": { - "data": { - "region": "\u5340\u57df" - }, - "description": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684\u72c0\u614b" - }, "user": { "data": { - "api_key": "API \u91d1\u9470", "region": "\u5340\u57df" }, "description": "\u9078\u64c7\u6240\u8981\u76e3\u8996\u7684\u72c0\u614b" diff --git a/homeassistant/components/unifi/translations/ko.json b/homeassistant/components/unifi/translations/ko.json index 3a13b420097..30d7559e4f3 100644 --- a/homeassistant/components/unifi/translations/ko.json +++ b/homeassistant/components/unifi/translations/ko.json @@ -26,6 +26,9 @@ } }, "options": { + "abort": { + "integration_not_setup": "UniFi \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4." + }, "step": { "client_control": { "data": { diff --git a/homeassistant/components/unifiprotect/translations/bg.json b/homeassistant/components/unifiprotect/translations/bg.json index adc8d7ef699..a3987858b93 100644 --- a/homeassistant/components/unifiprotect/translations/bg.json +++ b/homeassistant/components/unifiprotect/translations/bg.json @@ -5,8 +5,7 @@ }, "error": { "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435", - "unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" + "invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435" }, "flow_title": "{name} ({ip_address})", "step": { diff --git a/homeassistant/components/unifiprotect/translations/ca.json b/homeassistant/components/unifiprotect/translations/ca.json index 2227011d113..a3d873ead5b 100644 --- a/homeassistant/components/unifiprotect/translations/ca.json +++ b/homeassistant/components/unifiprotect/translations/ca.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Ha fallat la connexi\u00f3", "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", - "protect_version": "La versi\u00f3 m\u00ednima necess\u00e0ria \u00e9s la v1.20.0. Actualitza UniFi Protect i torna-ho a provar.", - "unknown": "Error inesperat" + "protect_version": "La versi\u00f3 m\u00ednima necess\u00e0ria \u00e9s la v1.20.0. Actualitza UniFi Protect i torna-ho a provar." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Contrasenya", - "username": "Nom d'usuari", - "verify_ssl": "Verifica el certificat SSL" + "username": "Nom d'usuari" }, "description": "Vols configurar {name} ({ip_address})? Necessites un usuari local per iniciar sessi\u00f3, creat mitjan\u00e7ant la consola d'UniFi OS. Els usuaris d'Ubiquiti Cloud no funcionen. Per a m\u00e9s informaci\u00f3: {local_user_documentation_url}", "title": "UniFi Protect descobert" diff --git a/homeassistant/components/unifiprotect/translations/cs.json b/homeassistant/components/unifiprotect/translations/cs.json index 3668652797e..855cc6da6da 100644 --- a/homeassistant/components/unifiprotect/translations/cs.json +++ b/homeassistant/components/unifiprotect/translations/cs.json @@ -5,16 +5,14 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed", - "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" + "invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed" }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Heslo", - "username": "U\u017eivatelsk\u00e9 jm\u00e9no", - "verify_ssl": "Ov\u011b\u0159it certifik\u00e1t SSL" + "username": "U\u017eivatelsk\u00e9 jm\u00e9no" } }, "reauth_confirm": { diff --git a/homeassistant/components/unifiprotect/translations/de.json b/homeassistant/components/unifiprotect/translations/de.json index 668a8c7b749..0f6ffd77793 100644 --- a/homeassistant/components/unifiprotect/translations/de.json +++ b/homeassistant/components/unifiprotect/translations/de.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Verbindung fehlgeschlagen", "invalid_auth": "Ung\u00fcltige Authentifizierung", - "protect_version": "Die erforderliche Mindestversion ist v1.20.0. Bitte aktualisiere UniFi Protect und versuche es dann erneut.", - "unknown": "Unerwarteter Fehler" + "protect_version": "Die erforderliche Mindestversion ist v1.20.0. Bitte aktualisiere UniFi Protect und versuche es dann erneut." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Passwort", - "username": "Benutzername", - "verify_ssl": "SSL-Zertifikat \u00fcberpr\u00fcfen" + "username": "Benutzername" }, "description": "M\u00f6chtest du {name} ({ip_address}) einrichten? Du ben\u00f6tigst einen lokalen Benutzer, den du in deiner UniFi OS-Konsole angelegt hast, um sich damit anzumelden. Ubiquiti Cloud-Benutzer funktionieren nicht. F\u00fcr weitere Informationen: {local_user_documentation_url}", "title": "UniFi Protect erkannt" diff --git a/homeassistant/components/unifiprotect/translations/el.json b/homeassistant/components/unifiprotect/translations/el.json index c2f5d57d36d..8cede72d32b 100644 --- a/homeassistant/components/unifiprotect/translations/el.json +++ b/homeassistant/components/unifiprotect/translations/el.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", - "protect_version": "\u0397 \u03b5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 v1.20.0. \u0391\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf UniFi Protect \u03ba\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.", - "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + "protect_version": "\u0397 \u03b5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03b7 \u03b1\u03c0\u03b1\u03b9\u03c4\u03bf\u03cd\u03bc\u03b5\u03bd\u03b7 \u03ad\u03ba\u03b4\u03bf\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 v1.20.0. \u0391\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03bf UniFi Protect \u03ba\u03b1\u03b9 \u03c3\u03c4\u03b7 \u03c3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03b1\u03b8\u03ae\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac." }, "flow_title": "{name} ( {ip_address} )", "step": { "discovery_confirm": { "data": { "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7", - "verify_ssl": "\u0395\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03c4\u03b5 \u03c4\u03bf \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc SSL" + "username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7" }, "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} ({ip_address}); \u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03ad\u03bd\u03b1\u03bd \u03c4\u03bf\u03c0\u03b9\u03ba\u03cc \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 \u03c0\u03bf\u03c5 \u03ad\u03c7\u03b5\u03b9 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03b7\u03b8\u03b5\u03af \u03c3\u03c4\u03b7\u03bd \u039a\u03bf\u03bd\u03c3\u03cc\u03bb\u03b1 UniFi OS \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5. \u039f\u03b9 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b5\u03c2 \u03c4\u03bf\u03c5 Ubiquiti Cloud \u03b4\u03b5\u03bd \u03b8\u03b1 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03bf\u03c5\u03bd. \u0393\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2: {local_user_documentation_url}", "title": "\u0391\u03bd\u03b1\u03ba\u03b1\u03bb\u03cd\u03c6\u03b8\u03b7\u03ba\u03b5 \u03c4\u03bf UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/en.json b/homeassistant/components/unifiprotect/translations/en.json index 45e8fb8ea2e..b9d787b382e 100644 --- a/homeassistant/components/unifiprotect/translations/en.json +++ b/homeassistant/components/unifiprotect/translations/en.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", - "protect_version": "Minimum required version is v1.20.0. Please upgrade UniFi Protect and then retry.", - "unknown": "Unexpected error" + "protect_version": "Minimum required version is v1.20.0. Please upgrade UniFi Protect and then retry." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Password", - "username": "Username", - "verify_ssl": "Verify SSL certificate" + "username": "Username" }, "description": "Do you want to setup {name} ({ip_address})? You will need a local user created in your UniFi OS Console to log in with. Ubiquiti Cloud Users will not work. For more information: {local_user_documentation_url}", "title": "UniFi Protect Discovered" diff --git a/homeassistant/components/unifiprotect/translations/es.json b/homeassistant/components/unifiprotect/translations/es.json index 901f1707de5..9ca1b56cf01 100644 --- a/homeassistant/components/unifiprotect/translations/es.json +++ b/homeassistant/components/unifiprotect/translations/es.json @@ -7,14 +7,12 @@ "error": { "cannot_connect": "No se pudo conectar", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", - "protect_version": "La versi\u00f3n m\u00ednima requerida es v1.20.0. Actualice UniFi Protect y vuelva a intentarlo.", - "unknown": "Error inesperado" + "protect_version": "La versi\u00f3n m\u00ednima requerida es v1.20.0. Actualice UniFi Protect y vuelva a intentarlo." }, "step": { "discovery_confirm": { "data": { - "username": "Usuario", - "verify_ssl": "Verifica el certificado SSL" + "username": "Usuario" } }, "reauth_confirm": { diff --git a/homeassistant/components/unifiprotect/translations/et.json b/homeassistant/components/unifiprotect/translations/et.json index 607edbe52bc..9abd63559ee 100644 --- a/homeassistant/components/unifiprotect/translations/et.json +++ b/homeassistant/components/unifiprotect/translations/et.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u00dchendamine nurjus", "invalid_auth": "Tuvastamine nurjus", - "protect_version": "Minimaalne n\u00f5utav versioon on v1.20.0. Uuenda UniFi Protecti ja proovi seej\u00e4rel uuesti.", - "unknown": "Ootamatu t\u00f5rge" + "protect_version": "Minimaalne n\u00f5utav versioon on v1.20.0. Uuenda UniFi Protecti ja proovi seej\u00e4rel uuesti." }, "flow_title": "{name} ( {ip_address} )", "step": { "discovery_confirm": { "data": { "password": "Salas\u00f5na", - "username": "Kasutajanimi", - "verify_ssl": "Kontrolli SSL \u00fchendust" + "username": "Kasutajanimi" }, "description": "Kas soovid seadistada kasutaja {name} ( {ip_address} )? Sisselogimiseks on vaja UniFi OS-i konsoolis loodud kohalikku kasutajat. Ubiquiti pilve kasutajad ei t\u00f6\u00f6ta. Lisateabe saamiseks: {local_user_documentation_url}", "title": "Avastati UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/fr.json b/homeassistant/components/unifiprotect/translations/fr.json index 592a18427b7..e1f189a55d5 100644 --- a/homeassistant/components/unifiprotect/translations/fr.json +++ b/homeassistant/components/unifiprotect/translations/fr.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u00c9chec de connexion", "invalid_auth": "Authentification non valide", - "protect_version": "La version minimale requise est la v1.20.0. Veuillez mettre \u00e0 jour UniFi Protect, puis r\u00e9essayer.", - "unknown": "Erreur inattendue" + "protect_version": "La version minimale requise est la v1.20.0. Veuillez mettre \u00e0 jour UniFi Protect, puis r\u00e9essayer." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Mot de passe", - "username": "Nom d'utilisateur", - "verify_ssl": "V\u00e9rifier le certificat SSL" + "username": "Nom d'utilisateur" }, "description": "Voulez-vous configurer {name} ({ip_address})\u00a0? Vous aurez besoin d'un utilisateur local cr\u00e9\u00e9 dans votre console UniFi OS pour vous connecter. Les utilisateurs Ubiquiti Cloud ne fonctionneront pas. Pour plus d'informations\u00a0: {local_user_documentation_url}", "title": "UniFi Protect d\u00e9couvert" diff --git a/homeassistant/components/unifiprotect/translations/he.json b/homeassistant/components/unifiprotect/translations/he.json index 7940beb3986..63974aff3ec 100644 --- a/homeassistant/components/unifiprotect/translations/he.json +++ b/homeassistant/components/unifiprotect/translations/he.json @@ -5,16 +5,14 @@ }, "error": { "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", - "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9", - "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + "invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9" }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "\u05e1\u05d9\u05e1\u05de\u05d4", - "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9", - "verify_ssl": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d0\u05d9\u05e9\u05d5\u05e8 SSL" + "username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9" } }, "reauth_confirm": { diff --git a/homeassistant/components/unifiprotect/translations/hu.json b/homeassistant/components/unifiprotect/translations/hu.json index c3cd3560a9d..11e0151f165 100644 --- a/homeassistant/components/unifiprotect/translations/hu.json +++ b/homeassistant/components/unifiprotect/translations/hu.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Sikertelen csatlakoz\u00e1s", "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", - "protect_version": "A minim\u00e1lisan sz\u00fcks\u00e9ges verzi\u00f3 a v1.20.0. Friss\u00edtse az UniFi Protect-et, majd pr\u00f3b\u00e1lkozzon \u00fajra.", - "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + "protect_version": "A minim\u00e1lisan sz\u00fcks\u00e9ges verzi\u00f3 a v1.20.0. Friss\u00edtse az UniFi Protect-et, majd pr\u00f3b\u00e1lkozzon \u00fajra." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Jelsz\u00f3", - "username": "Felhaszn\u00e1l\u00f3n\u00e9v", - "verify_ssl": "SSL-tan\u00fas\u00edtv\u00e1ny ellen\u0151rz\u00e9se" + "username": "Felhaszn\u00e1l\u00f3n\u00e9v" }, "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} ({ipaddress})? Egy helyi felhaszn\u00e1l\u00f3t kell l\u00e9trehoznia Unifi OS-ben.", "title": "UniFi Protect felfedezve" diff --git a/homeassistant/components/unifiprotect/translations/id.json b/homeassistant/components/unifiprotect/translations/id.json index b2f8e2541fe..a0a3b9751d3 100644 --- a/homeassistant/components/unifiprotect/translations/id.json +++ b/homeassistant/components/unifiprotect/translations/id.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Gagal terhubung", "invalid_auth": "Autentikasi tidak valid", - "protect_version": "Versi minimum yang diperlukan adalah v1.20.0. Tingkatkan UniFi Protect lalu coba lagi.", - "unknown": "Kesalahan yang tidak diharapkan" + "protect_version": "Versi minimum yang diperlukan adalah v1.20.0. Tingkatkan UniFi Protect lalu coba lagi." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Kata Sandi", - "username": "Nama Pengguna", - "verify_ssl": "Verifikasi sertifikat SSL" + "username": "Nama Pengguna" }, "description": "Ingin menyiapkan {name} ({ip_address})? Anda akan memerlukan pengguna lokal yang dibuat di Konsol OS UniFi Anda untuk masuk. Pengguna Ubiquiti Cloud tidak akan berfungsi. Untuk informasi lebih lanjut: {local_user_documentation_url}", "title": "UniFi Protect Ditemukan" diff --git a/homeassistant/components/unifiprotect/translations/it.json b/homeassistant/components/unifiprotect/translations/it.json index 747647b3d26..1d0fff95c3a 100644 --- a/homeassistant/components/unifiprotect/translations/it.json +++ b/homeassistant/components/unifiprotect/translations/it.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Impossibile connettersi", "invalid_auth": "Autenticazione non valida", - "protect_version": "La versione minima richiesta \u00e8 v1.20.0. Aggiorna UniFi Protect e riprova.", - "unknown": "Errore imprevisto" + "protect_version": "La versione minima richiesta \u00e8 v1.20.0. Aggiorna UniFi Protect e riprova." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Password", - "username": "Nome utente", - "verify_ssl": "Verifica il certificato SSL" + "username": "Nome utente" }, "description": "Vuoi configurare {name} ({ip_address})? Avrai bisogno di un utente locale creato nella tua console UniFi OS con cui accedere. Gli utenti Ubiquiti Cloud non funzioneranno. Per ulteriori informazioni: {local_user_documentation_url}", "title": "Rilevato UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/ja.json b/homeassistant/components/unifiprotect/translations/ja.json index e4ad1b3f231..12275699260 100644 --- a/homeassistant/components/unifiprotect/translations/ja.json +++ b/homeassistant/components/unifiprotect/translations/ja.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", - "protect_version": "\u6700\u4f4e\u9650\u5fc5\u8981\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u306fv1.20.0\u3067\u3059\u3002UniFi Protect\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u304b\u3089\u518d\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + "protect_version": "\u6700\u4f4e\u9650\u5fc5\u8981\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u306fv1.20.0\u3067\u3059\u3002UniFi Protect\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u304b\u3089\u518d\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "username": "\u30e6\u30fc\u30b6\u30fc\u540d", - "verify_ssl": "SSL\u8a3c\u660e\u66f8\u3092\u78ba\u8a8d\u3059\u308b" + "username": "\u30e6\u30fc\u30b6\u30fc\u540d" }, "description": "{name} ({ip_address}) \u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f", "title": "UniFi Protect\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f" diff --git a/homeassistant/components/unifiprotect/translations/ko.json b/homeassistant/components/unifiprotect/translations/ko.json new file mode 100644 index 00000000000..36be85fd61a --- /dev/null +++ b/homeassistant/components/unifiprotect/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "discovery_confirm": { + "description": "{name} ( {ip_address} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? " + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/unifiprotect/translations/nl.json b/homeassistant/components/unifiprotect/translations/nl.json index 090624f2009..a5b834fc4d4 100644 --- a/homeassistant/components/unifiprotect/translations/nl.json +++ b/homeassistant/components/unifiprotect/translations/nl.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", - "protect_version": "Minimaal vereiste versie is v1.20.0. Upgrade UniFi Protect en probeer het opnieuw.", - "unknown": "Onverwachte fout" + "protect_version": "Minimaal vereiste versie is v1.20.0. Upgrade UniFi Protect en probeer het opnieuw." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Wachtwoord", - "username": "Gebruikersnaam", - "verify_ssl": "SSL-certificaat verifi\u00ebren" + "username": "Gebruikersnaam" }, "description": "Wilt u {name} ({ip_address}) instellen? U heeft een lokale gebruiker nodig die is aangemaakt in uw UniFi OS Console om mee in te loggen. Ubiquiti Cloud gebruikers zullen niet werken. Voor meer informatie: {local_user_documentation_url}", "title": "UniFi Protect ontdekt" diff --git a/homeassistant/components/unifiprotect/translations/no.json b/homeassistant/components/unifiprotect/translations/no.json index 9b45080b8f1..e11ed432313 100644 --- a/homeassistant/components/unifiprotect/translations/no.json +++ b/homeassistant/components/unifiprotect/translations/no.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Tilkobling mislyktes", "invalid_auth": "Ugyldig godkjenning", - "protect_version": "Minimum p\u00e5krevd versjon er v1.20.0. Vennligst oppgrader UniFi Protect og pr\u00f8v deretter p\u00e5 nytt.", - "unknown": "Uventet feil" + "protect_version": "Minimum p\u00e5krevd versjon er v1.20.0. Vennligst oppgrader UniFi Protect og pr\u00f8v deretter p\u00e5 nytt." }, "flow_title": "{name} ( {ip_address} )", "step": { "discovery_confirm": { "data": { "password": "Passord", - "username": "Brukernavn", - "verify_ssl": "Verifisere SSL-sertifikat" + "username": "Brukernavn" }, "description": "Vil du konfigurere {name} ( {ip_address} )? Du trenger en lokal bruker opprettet i UniFi OS-konsollen for \u00e5 logge p\u00e5. Ubiquiti Cloud-brukere vil ikke fungere. For mer informasjon: {local_user_documentation_url}", "title": "UniFi Protect oppdaget" diff --git a/homeassistant/components/unifiprotect/translations/pl.json b/homeassistant/components/unifiprotect/translations/pl.json index ef879805546..82aa3c91ee3 100644 --- a/homeassistant/components/unifiprotect/translations/pl.json +++ b/homeassistant/components/unifiprotect/translations/pl.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", - "protect_version": "Minimalna wymagana wersja to v1.20.0. Zaktualizuj UniFi Protect, a nast\u0119pnie spr\u00f3buj ponownie.", - "unknown": "Nieoczekiwany b\u0142\u0105d" + "protect_version": "Minimalna wymagana wersja to v1.20.0. Zaktualizuj UniFi Protect, a nast\u0119pnie spr\u00f3buj ponownie." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Has\u0142o", - "username": "Nazwa u\u017cytkownika", - "verify_ssl": "Weryfikacja certyfikatu SSL" + "username": "Nazwa u\u017cytkownika" }, "description": "Czy chcesz skonfigurowa\u0107 {name} ({ip_address})? Aby si\u0119 zalogowa\u0107, b\u0119dziesz potrzebowa\u0107 lokalnego u\u017cytkownika utworzonego w konsoli UniFi OS. U\u017cytkownicy Ubiquiti Cloud nie b\u0119d\u0105 dzia\u0142a\u0107. Wi\u0119cej informacji: {local_user_documentation_url}", "title": "Wykryto UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/pt-BR.json b/homeassistant/components/unifiprotect/translations/pt-BR.json index 1f26b952998..2c0c1270add 100644 --- a/homeassistant/components/unifiprotect/translations/pt-BR.json +++ b/homeassistant/components/unifiprotect/translations/pt-BR.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Falha ao conectar", "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", - "protect_version": "A vers\u00e3o m\u00ednima exigida \u00e9 v1.20.0. Atualize o UniFi Protect e tente novamente.", - "unknown": "Erro inesperado" + "protect_version": "A vers\u00e3o m\u00ednima exigida \u00e9 v1.20.0. Atualize o UniFi Protect e tente novamente." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Senha", - "username": "Usu\u00e1rio", - "verify_ssl": "Verifique o certificado SSL" + "username": "Usu\u00e1rio" }, "description": "Deseja configurar {name} ({ip_address})?\nVoc\u00ea precisar\u00e1 de um usu\u00e1rio local criado no console do sistema operacional UniFi para fazer login. Usu\u00e1rios da Ubiquiti Cloud n\u00e3o funcionar\u00e3o. Para mais informa\u00e7\u00f5es: {local_user_documentation_url}", "title": "Descoberta UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/ru.json b/homeassistant/components/unifiprotect/translations/ru.json index ea9810962f8..2e06e887cf4 100644 --- a/homeassistant/components/unifiprotect/translations/ru.json +++ b/homeassistant/components/unifiprotect/translations/ru.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", - "protect_version": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u044f 1.20.0 \u0438\u043b\u0438 \u0432\u044b\u0448\u0435. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 UniFi Protect \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443.", - "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + "protect_version": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u044f 1.20.0 \u0438\u043b\u0438 \u0432\u044b\u0448\u0435. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 UniFi Protect \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", - "verify_ssl": "\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 SSL" + "username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f" }, "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} ({ip_address})? \u0414\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 UniFi OS \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 Ubiquiti Cloud \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438: {local_user_documentation_url}", "title": "\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e UniFi Protect" diff --git a/homeassistant/components/unifiprotect/translations/tr.json b/homeassistant/components/unifiprotect/translations/tr.json index d7b87b13f31..869c13608ce 100644 --- a/homeassistant/components/unifiprotect/translations/tr.json +++ b/homeassistant/components/unifiprotect/translations/tr.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", - "protect_version": "Minimum gerekli s\u00fcr\u00fcm v1.20.0'd\u0131r. L\u00fctfen UniFi Protect'i y\u00fckseltin ve ard\u0131ndan yeniden deneyin.", - "unknown": "Beklenmeyen hata" + "protect_version": "Minimum gerekli s\u00fcr\u00fcm v1.20.0'd\u0131r. L\u00fctfen UniFi Protect'i y\u00fckseltin ve ard\u0131ndan yeniden deneyin." }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "Parola", - "username": "Kullan\u0131c\u0131 Ad\u0131", - "verify_ssl": "SSL sertifikas\u0131n\u0131 do\u011frulay\u0131n" + "username": "Kullan\u0131c\u0131 Ad\u0131" }, "description": "{name} ( {ip_address} ) kurulumu yapmak istiyor musunuz? Oturum a\u00e7mak i\u00e7in UniFi OS Konsolunuzda olu\u015fturulmu\u015f yerel bir kullan\u0131c\u0131ya ihtiyac\u0131n\u0131z olacak. Ubiquiti Bulut Kullan\u0131c\u0131lar\u0131 \u00e7al\u0131\u015fmayacakt\u0131r. Daha fazla bilgi i\u00e7in: {local_user_documentation_url}", "title": "UniFi Protect Ke\u015ffedildi" diff --git a/homeassistant/components/unifiprotect/translations/zh-Hant.json b/homeassistant/components/unifiprotect/translations/zh-Hant.json index 33a447e9f4c..d0c23849e12 100644 --- a/homeassistant/components/unifiprotect/translations/zh-Hant.json +++ b/homeassistant/components/unifiprotect/translations/zh-Hant.json @@ -7,16 +7,14 @@ "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", - "protect_version": "\u6240\u9700\u6700\u4f4e\u7248\u672c\u70ba V1.20.0\u3002\u8acb\u66f4\u65b0 UniFi \u76e3\u63a7\u5f8c\u518d\u91cd\u8a66\u4e00\u6b21\u3002", - "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + "protect_version": "\u6240\u9700\u6700\u4f4e\u7248\u672c\u70ba V1.20.0\u3002\u8acb\u66f4\u65b0 UniFi \u76e3\u63a7\u5f8c\u518d\u91cd\u8a66\u4e00\u6b21\u3002" }, "flow_title": "{name} ({ip_address})", "step": { "discovery_confirm": { "data": { "password": "\u5bc6\u78bc", - "username": "\u4f7f\u7528\u8005\u540d\u7a31", - "verify_ssl": "\u78ba\u8a8d SSL \u8a8d\u8b49" + "username": "\u4f7f\u7528\u8005\u540d\u7a31" }, "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} ({ip_address})\uff1f\u9700\u8981\u65bc UniFi OS Console \u65b0\u589e\u672c\u5730\u4f7f\u7528\u8005\u4ee5\u9032\u884c\u767b\u5165\u3001Ubiquiti \u96f2\u7aef\u4f7f\u7528\u8005\u7121\u6cd5\u4f7f\u7528\u3002\u66f4\u591a\u8a73\u7d30\u8cc7\u8a0a\uff1a{local_user_documentation_url}", "title": "\u767c\u73fe UniFi \u76e3\u63a7" diff --git a/homeassistant/components/upnp/translations/bg.json b/homeassistant/components/upnp/translations/bg.json index 82632bc19b5..8bc4e9b740a 100644 --- a/homeassistant/components/upnp/translations/bg.json +++ b/homeassistant/components/upnp/translations/bg.json @@ -15,8 +15,7 @@ }, "user": { "data": { - "unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", - "usn": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + "unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" } } } diff --git a/homeassistant/components/upnp/translations/ca.json b/homeassistant/components/upnp/translations/ca.json index 0fc68f8a9b7..94ffd69cbf4 100644 --- a/homeassistant/components/upnp/translations/ca.json +++ b/homeassistant/components/upnp/translations/ca.json @@ -16,9 +16,7 @@ }, "user": { "data": { - "scan_interval": "Interval d'actualitzaci\u00f3 (en segons, m\u00ednim 30)", - "unique_id": "Dispositiu", - "usn": "Dispositiu" + "unique_id": "Dispositiu" } } } diff --git a/homeassistant/components/upnp/translations/cs.json b/homeassistant/components/upnp/translations/cs.json index 319dd28c11e..c684856b52b 100644 --- a/homeassistant/components/upnp/translations/cs.json +++ b/homeassistant/components/upnp/translations/cs.json @@ -9,12 +9,6 @@ "step": { "ssdp_confirm": { "description": "Chcete nastavit toto za\u0159\u00edzen\u00ed UPnP/IGD?" - }, - "user": { - "data": { - "scan_interval": "Interval aktualizace (v sekund\u00e1ch, minim\u00e1ln\u011b 30)", - "usn": "Za\u0159\u00edzen\u00ed" - } } } } diff --git a/homeassistant/components/upnp/translations/de.json b/homeassistant/components/upnp/translations/de.json index b63d17947ae..6c99c42fd8e 100644 --- a/homeassistant/components/upnp/translations/de.json +++ b/homeassistant/components/upnp/translations/de.json @@ -16,9 +16,7 @@ }, "user": { "data": { - "scan_interval": "Aktualisierungsintervall (Sekunden, mindestens 30)", - "unique_id": "Ger\u00e4t", - "usn": "Ger\u00e4t" + "unique_id": "Ger\u00e4t" } } } diff --git a/homeassistant/components/upnp/translations/el.json b/homeassistant/components/upnp/translations/el.json index 9a84ba81ced..8e98495d4b7 100644 --- a/homeassistant/components/upnp/translations/el.json +++ b/homeassistant/components/upnp/translations/el.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1, \u03b5\u03bb\u03ac\u03c7\u03b9\u03c3\u03c4\u03bf 30)", - "unique_id": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae", - "usn": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" + "unique_id": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae" } } } diff --git a/homeassistant/components/upnp/translations/en.json b/homeassistant/components/upnp/translations/en.json index c62ebfd2a87..aa22348e308 100644 --- a/homeassistant/components/upnp/translations/en.json +++ b/homeassistant/components/upnp/translations/en.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "Update interval (seconds, minimal 30)", - "unique_id": "Device", - "usn": "Device" + "unique_id": "Device" } } } diff --git a/homeassistant/components/upnp/translations/es.json b/homeassistant/components/upnp/translations/es.json index 7a76ab44bf0..9a8a9e0160b 100644 --- a/homeassistant/components/upnp/translations/es.json +++ b/homeassistant/components/upnp/translations/es.json @@ -16,9 +16,7 @@ }, "user": { "data": { - "scan_interval": "Intervalo de actualizaci\u00f3n (segundos, m\u00ednimo 30)", - "unique_id": "Dispositivo", - "usn": "Dispositivo" + "unique_id": "Dispositivo" } } } diff --git a/homeassistant/components/upnp/translations/et.json b/homeassistant/components/upnp/translations/et.json index 2ac4884c9ea..f090cebb31a 100644 --- a/homeassistant/components/upnp/translations/et.json +++ b/homeassistant/components/upnp/translations/et.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "P\u00e4ringute intervall (sekundites, v\u00e4hemalt 30)", - "unique_id": "Seade", - "usn": "Seade" + "unique_id": "Seade" } } } diff --git a/homeassistant/components/upnp/translations/fi.json b/homeassistant/components/upnp/translations/fi.json index aaf44e6c730..479b3be83de 100644 --- a/homeassistant/components/upnp/translations/fi.json +++ b/homeassistant/components/upnp/translations/fi.json @@ -2,14 +2,6 @@ "config": { "abort": { "already_configured": "Laite on jo m\u00e4\u00e4ritetty" - }, - "step": { - "user": { - "data": { - "scan_interval": "P\u00e4ivitysv\u00e4li (sekuntia, v\u00e4hint\u00e4\u00e4n 30)", - "usn": "Laite" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/fr.json b/homeassistant/components/upnp/translations/fr.json index 1c5a1c61309..b1f7ffb03fd 100644 --- a/homeassistant/components/upnp/translations/fr.json +++ b/homeassistant/components/upnp/translations/fr.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "Intervalle de mise \u00e0 jour (secondes, minimum 30)", - "unique_id": "Appareil", - "usn": "Appareil" + "unique_id": "Appareil" } } } diff --git a/homeassistant/components/upnp/translations/he.json b/homeassistant/components/upnp/translations/he.json index 6395b5f029f..a8501a7de41 100644 --- a/homeassistant/components/upnp/translations/he.json +++ b/homeassistant/components/upnp/translations/he.json @@ -8,8 +8,7 @@ "step": { "user": { "data": { - "unique_id": "\u05d4\u05ea\u05e7\u05df", - "usn": "\u05de\u05db\u05e9\u05d9\u05e8" + "unique_id": "\u05d4\u05ea\u05e7\u05df" } } } diff --git a/homeassistant/components/upnp/translations/hu.json b/homeassistant/components/upnp/translations/hu.json index 46c6bd2de1f..141d5ae8d8d 100644 --- a/homeassistant/components/upnp/translations/hu.json +++ b/homeassistant/components/upnp/translations/hu.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "Friss\u00edt\u00e9si intervallum (m\u00e1sodperc, minimum 30)", - "unique_id": "Eszk\u00f6z", - "usn": "Eszk\u00f6z" + "unique_id": "Eszk\u00f6z" } } } diff --git a/homeassistant/components/upnp/translations/id.json b/homeassistant/components/upnp/translations/id.json index f70fca145e8..2f2c195759e 100644 --- a/homeassistant/components/upnp/translations/id.json +++ b/homeassistant/components/upnp/translations/id.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "Interval pembaruan (dalam detik, minimal 30)", - "unique_id": "Perangkat", - "usn": "Perangkat" + "unique_id": "Perangkat" } } } diff --git a/homeassistant/components/upnp/translations/it.json b/homeassistant/components/upnp/translations/it.json index a57429ac78a..7b6a22de5c8 100644 --- a/homeassistant/components/upnp/translations/it.json +++ b/homeassistant/components/upnp/translations/it.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "Intervallo di aggiornamento (secondi, minimo 30)", - "unique_id": "Dispositivo", - "usn": "Dispositivo" + "unique_id": "Dispositivo" } } } diff --git a/homeassistant/components/upnp/translations/ja.json b/homeassistant/components/upnp/translations/ja.json index 2148b0d8a16..8c4d8d4695e 100644 --- a/homeassistant/components/upnp/translations/ja.json +++ b/homeassistant/components/upnp/translations/ja.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "\u66f4\u65b0\u9593\u9694(\u79d2\u3001\u6700\u5c0f30)", - "unique_id": "\u30c7\u30d0\u30a4\u30b9", - "usn": "\u30c7\u30d0\u30a4\u30b9" + "unique_id": "\u30c7\u30d0\u30a4\u30b9" } } } diff --git a/homeassistant/components/upnp/translations/ko.json b/homeassistant/components/upnp/translations/ko.json index ab80ceb9caa..8782bc180dd 100644 --- a/homeassistant/components/upnp/translations/ko.json +++ b/homeassistant/components/upnp/translations/ko.json @@ -9,12 +9,6 @@ "step": { "ssdp_confirm": { "description": "\uc774 UPnP/IGD \uae30\uae30\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" - }, - "user": { - "data": { - "scan_interval": "\uc5c5\ub370\uc774\ud2b8 \uac04\uaca9 (\ucd08, \ucd5c\uc19f\uac12 30)", - "usn": "\uae30\uae30" - } } } } diff --git a/homeassistant/components/upnp/translations/lb.json b/homeassistant/components/upnp/translations/lb.json index ac1eecebf99..a1f8cf85c3b 100644 --- a/homeassistant/components/upnp/translations/lb.json +++ b/homeassistant/components/upnp/translations/lb.json @@ -13,12 +13,6 @@ "step": { "ssdp_confirm": { "description": "Soll d\u00ebsen UPnP/IGD Apparat konfigur\u00e9iert ginn?" - }, - "user": { - "data": { - "scan_interval": "Update Intervall (Sekonnen, minimum 30)", - "usn": "Apparat" - } } } } diff --git a/homeassistant/components/upnp/translations/nl.json b/homeassistant/components/upnp/translations/nl.json index b4d690ca58c..5b176c4b22f 100644 --- a/homeassistant/components/upnp/translations/nl.json +++ b/homeassistant/components/upnp/translations/nl.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "Update-interval (seconden, minimaal 30)", - "unique_id": "Apparaat", - "usn": "Apparaat" + "unique_id": "Apparaat" } } } diff --git a/homeassistant/components/upnp/translations/no.json b/homeassistant/components/upnp/translations/no.json index c92144bf40d..8eb74395fa2 100644 --- a/homeassistant/components/upnp/translations/no.json +++ b/homeassistant/components/upnp/translations/no.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "Oppdateringsintervall (sekunder, minimum 30)", - "unique_id": "Enhet", - "usn": "Enhet" + "unique_id": "Enhet" } } } diff --git a/homeassistant/components/upnp/translations/pl.json b/homeassistant/components/upnp/translations/pl.json index 30213436d27..e8c8d72b74c 100644 --- a/homeassistant/components/upnp/translations/pl.json +++ b/homeassistant/components/upnp/translations/pl.json @@ -24,9 +24,7 @@ }, "user": { "data": { - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (sekundy, minimum 30)", - "unique_id": "Urz\u0105dzenie", - "usn": "Urz\u0105dzenie" + "unique_id": "Urz\u0105dzenie" } } } diff --git a/homeassistant/components/upnp/translations/pt-BR.json b/homeassistant/components/upnp/translations/pt-BR.json index a1544981ea9..3070be8a1d0 100644 --- a/homeassistant/components/upnp/translations/pt-BR.json +++ b/homeassistant/components/upnp/translations/pt-BR.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "Intervalo de atualiza\u00e7\u00e3o (segundos, m\u00ednimo 30)", - "unique_id": "Dispositivo", - "usn": "Dispositivo" + "unique_id": "Dispositivo" } } } diff --git a/homeassistant/components/upnp/translations/pt.json b/homeassistant/components/upnp/translations/pt.json index 8985d608033..022d1c823c1 100644 --- a/homeassistant/components/upnp/translations/pt.json +++ b/homeassistant/components/upnp/translations/pt.json @@ -12,11 +12,6 @@ "step": { "ssdp_confirm": { "description": "Deseja configurar este dispositivo UPnP/IGD?" - }, - "user": { - "data": { - "usn": "Dispositivo" - } } } } diff --git a/homeassistant/components/upnp/translations/ru.json b/homeassistant/components/upnp/translations/ru.json index 20c652fa1e0..40a8585ac95 100644 --- a/homeassistant/components/upnp/translations/ru.json +++ b/homeassistant/components/upnp/translations/ru.json @@ -18,9 +18,7 @@ }, "user": { "data": { - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445, \u043c\u0438\u043d\u0438\u043c\u0443\u043c 30)", - "unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", - "usn": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" + "unique_id": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e" } } } diff --git a/homeassistant/components/upnp/translations/sl.json b/homeassistant/components/upnp/translations/sl.json index ee25689fd51..c6258ec4774 100644 --- a/homeassistant/components/upnp/translations/sl.json +++ b/homeassistant/components/upnp/translations/sl.json @@ -14,11 +14,6 @@ "step": { "ssdp_confirm": { "description": "Ali \u017eelite nastaviti to UPnP/IGD napravo?" - }, - "user": { - "data": { - "usn": "Naprava" - } } } } diff --git a/homeassistant/components/upnp/translations/sv.json b/homeassistant/components/upnp/translations/sv.json index b6702d976d0..5ffe4a62f26 100644 --- a/homeassistant/components/upnp/translations/sv.json +++ b/homeassistant/components/upnp/translations/sv.json @@ -7,13 +7,6 @@ "error": { "one": "En", "other": "Andra" - }, - "step": { - "user": { - "data": { - "usn": "Enheten" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/upnp/translations/tr.json b/homeassistant/components/upnp/translations/tr.json index 8176c0541c7..4742549eaff 100644 --- a/homeassistant/components/upnp/translations/tr.json +++ b/homeassistant/components/upnp/translations/tr.json @@ -20,9 +20,7 @@ }, "user": { "data": { - "scan_interval": "G\u00fcncelleme aral\u0131\u011f\u0131 (saniye, minimum 30)", - "unique_id": "Cihaz", - "usn": "Cihaz" + "unique_id": "Cihaz" } } } diff --git a/homeassistant/components/upnp/translations/uk.json b/homeassistant/components/upnp/translations/uk.json index 905958eeca9..870c3d38ffd 100644 --- a/homeassistant/components/upnp/translations/uk.json +++ b/homeassistant/components/upnp/translations/uk.json @@ -9,12 +9,6 @@ "step": { "ssdp_confirm": { "description": "\u0425\u043e\u0447\u0435\u0442\u0435 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u0442\u0438 \u0446\u0435\u0439 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 UPnP / IGD?" - }, - "user": { - "data": { - "scan_interval": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043e\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044f (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445, \u043c\u0456\u043d\u0456\u043c\u0443\u043c 30)", - "usn": "\u041f\u0440\u0438\u0441\u0442\u0440\u0456\u0439" - } } } } diff --git a/homeassistant/components/upnp/translations/zh-Hans.json b/homeassistant/components/upnp/translations/zh-Hans.json index 09cf281418b..73ee0d983c1 100644 --- a/homeassistant/components/upnp/translations/zh-Hans.json +++ b/homeassistant/components/upnp/translations/zh-Hans.json @@ -11,8 +11,7 @@ }, "user": { "data": { - "unique_id": "\u8bbe\u5907", - "usn": "\u8bbe\u5907" + "unique_id": "\u8bbe\u5907" } } } diff --git a/homeassistant/components/upnp/translations/zh-Hant.json b/homeassistant/components/upnp/translations/zh-Hant.json index 80c92662d22..df91540c0a0 100644 --- a/homeassistant/components/upnp/translations/zh-Hant.json +++ b/homeassistant/components/upnp/translations/zh-Hant.json @@ -12,9 +12,7 @@ }, "user": { "data": { - "scan_interval": "\u66f4\u65b0\u9593\u9694\uff08\u79d2\u3001\u6700\u5c11 30 \u79d2\uff09", - "unique_id": "\u88dd\u7f6e", - "usn": "\u88dd\u7f6e" + "unique_id": "\u88dd\u7f6e" } } } diff --git a/homeassistant/components/uptime/translations/ko.json b/homeassistant/components/uptime/translations/ko.json new file mode 100644 index 00000000000..758f3336cd4 --- /dev/null +++ b/homeassistant/components/uptime/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/utility_meter/translations/ko.json b/homeassistant/components/utility_meter/translations/ko.json new file mode 100644 index 00000000000..00df29f1e0b --- /dev/null +++ b/homeassistant/components/utility_meter/translations/ko.json @@ -0,0 +1,35 @@ +{ + "config": { + "step": { + "user": { + "data": { + "cycle": "\ubbf8\ud130\uae30 \ub9ac\uc14b \uc8fc\uae30", + "delta_values": "\ub378\ud0c0 \uac12", + "name": "\uc774\ub984", + "net_consumption": "\uc21c \uc18c\ube44\ub7c9", + "offset": "\ubbf8\ud130 \ub9ac\uc14b \uc624\ud504\uc14b", + "source": "\uc785\ub825 \uc13c\uc11c", + "tariffs": "\uc9c0\uc6d0\ub418\ub294 \uc694\uae08\uccb4\uacc4" + }, + "data_description": { + "delta_values": "\uc18c\uc2a4 \uac12\uc774 \uc808\ub300\uac12\uc774 \uc544\ub2cc \ub9c8\uc9c0\ub9c9 \uac12 \uc774\ud6c4\uc758 \ub378\ud0c0 \uac12\uc778 \uacbd\uc6b0 \ud65c\uc131\ud654\ud569\ub2c8\ub2e4.", + "net_consumption": "\uc18c\uc2a4\uac00 \ub124\ud2b8 \ubbf8\ud130\uc778 \uacbd\uc6b0 \ud65c\uc131\ud654\ud569\ub2c8\ub2e4. \uc989, \uc99d\uac00\ud558\uac70\ub098 \uac10\uc18c\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "offset": "\uc6d4\ubcc4 \ubbf8\ud130\uae30 \ub9ac\uc14b \ub0a0\uc9dc\ub97c \uc624\ud504\uc14b\ud569\ub2c8\ub2e4.", + "tariffs": "\uc9c0\uc6d0\ub418\ub294 \uc694\uae08\uccb4\uacc4, \ub2e8\uc77c \uc694\uae08\uccb4\uacc4\ub9cc \ud544\uc694\ud55c \uacbd\uc6b0 \ube44\uc6cc \ub461\ub2c8\ub2e4." + }, + "description": "\uae30\uac04 \ub3d9\uc548 \uc18c\ube44\ub41c \uc790\uc6d0\ub7c9(\uc608: \uc5d0\ub108\uc9c0, \uac00\uc2a4, \ubb3c, \ub09c\ubc29)\uc744 \ucd94\uc801\ud558\ub294 \uc13c\uc11c\ub97c \ub9cc\ub4ed\ub2c8\ub2e4. \uc720\ud2f8\ub9ac\ud2f0 \ubbf8\ud130 \uc13c\uc11c\ub294 \uc694\uae08\uccb4\uacc4\uc5d0 \ub530\ub978 \ubd84\ub958\ub97c \uc9c0\uc6d0\ud569\ub2c8\ub2e4. \uc774 \uacbd\uc6b0 \uac01 \uc694\uae08\uccb4\uacc4\uc5d0 \ub300\ud574 \uac01\uac01\uc758 \uc13c\uc11c\uac00 \uc0dd\uc131\ub418\uace0 \ud574\ub2f9 \uc694\uae08\uccb4\uacc4\ub97c \uc0ac\uc6a9\ud560 \uad6c\uc131\uc694\uc18c\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.", + "title": "\uc720\ud2f8\ub9ac\ud2f0 \ubbf8\ud130 \ucd94\uac00" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "source": "\uc785\ub825 \uc13c\uc11c" + } + } + } + }, + "title": "\uc720\ud2f8\ub9ac\ud2f0 \ubbf8\ud130" +} \ No newline at end of file diff --git a/homeassistant/components/vicare/translations/af.json b/homeassistant/components/vicare/translations/af.json index 92321a6be86..74536dab793 100644 --- a/homeassistant/components/vicare/translations/af.json +++ b/homeassistant/components/vicare/translations/af.json @@ -4,7 +4,6 @@ "user": { "data": { "password": "\u5bc6\u7801", - "scan_interval": "\u626b\u63cf\u95f4\u9694\u65f6\u95f4(\u79d2)", "username": "\u7535\u5b50\u90ae\u7bb1" } } diff --git a/homeassistant/components/vicare/translations/bg.json b/homeassistant/components/vicare/translations/bg.json index c3178df6b87..242339c3815 100644 --- a/homeassistant/components/vicare/translations/bg.json +++ b/homeassistant/components/vicare/translations/bg.json @@ -12,12 +12,9 @@ "user": { "data": { "client_id": "API \u043a\u043b\u044e\u0447", - "name": "\u0418\u043c\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u0430", - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0430 \u0441\u043a\u0430\u043d\u0438\u0440\u0430\u043d\u0435 (\u0441\u0435\u043a\u0443\u043d\u0434\u0438)", "username": "Email" - }, - "title": "{name}" + } } } } diff --git a/homeassistant/components/vicare/translations/ca.json b/homeassistant/components/vicare/translations/ca.json index a850cdf27fa..0fef2889375 100644 --- a/homeassistant/components/vicare/translations/ca.json +++ b/homeassistant/components/vicare/translations/ca.json @@ -13,13 +13,10 @@ "data": { "client_id": "Clau API", "heating_type": "Tipus d'escalfador", - "name": "Nom", "password": "Contrasenya", - "scan_interval": "Interval d'escaneig (segons)", "username": "Correu electr\u00f2nic" }, - "description": "Configura la integraci\u00f3 ViCare. Per generar la clau API, v\u00e9s a https://developer.viessmann.com", - "title": "{name}" + "description": "Configura la integraci\u00f3 ViCare. Per generar la clau API, v\u00e9s a https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/cs.json b/homeassistant/components/vicare/translations/cs.json index 5f29c738f07..332b6206db2 100644 --- a/homeassistant/components/vicare/translations/cs.json +++ b/homeassistant/components/vicare/translations/cs.json @@ -12,7 +12,6 @@ "user": { "data": { "client_id": "Kl\u00ed\u010d API", - "name": "Jm\u00e9no", "password": "Heslo", "username": "E-mail" } diff --git a/homeassistant/components/vicare/translations/de.json b/homeassistant/components/vicare/translations/de.json index 854c1cadead..1ef07bd2fc4 100644 --- a/homeassistant/components/vicare/translations/de.json +++ b/homeassistant/components/vicare/translations/de.json @@ -13,13 +13,10 @@ "data": { "client_id": "API-Schl\u00fcssel", "heating_type": "Art der Heizung", - "name": "Name", "password": "Passwort", - "scan_interval": "Scanintervall (Sekunden)", "username": "E-Mail" }, - "description": "Richte die ViCare-Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe auf https://developer.viessmann.com", - "title": "{name}" + "description": "Richte die ViCare-Integration ein. Um einen API-Schl\u00fcssel zu generieren, gehe auf https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/el.json b/homeassistant/components/vicare/translations/el.json index 4c4385ec88b..61ac6bdbb8f 100644 --- a/homeassistant/components/vicare/translations/el.json +++ b/homeassistant/components/vicare/translations/el.json @@ -13,13 +13,10 @@ "data": { "client_id": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", "heating_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03b8\u03ad\u03c1\u03bc\u03b1\u03bd\u03c3\u03b7\u03c2", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1", "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", - "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03c3\u03ac\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03b4\u03b5\u03c5\u03c4\u03b5\u03c1\u03cc\u03bb\u03b5\u03c0\u03c4\u03b1)", "username": "Email" }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 ViCare. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://developer.viessmann.com", - "title": "{name}" + "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 ViCare. \u0393\u03b9\u03b1 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af API \u03bc\u03b5\u03c4\u03b1\u03b2\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/en.json b/homeassistant/components/vicare/translations/en.json index ef7235f86f8..89fc4caeeaf 100644 --- a/homeassistant/components/vicare/translations/en.json +++ b/homeassistant/components/vicare/translations/en.json @@ -13,13 +13,10 @@ "data": { "client_id": "API Key", "heating_type": "Heating type", - "name": "Name", "password": "Password", - "scan_interval": "Scan Interval (seconds)", "username": "Email" }, - "description": "Set up ViCare integration. To generate API key go to https://developer.viessmann.com", - "title": "{name}" + "description": "Set up ViCare integration. To generate API key go to https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/es.json b/homeassistant/components/vicare/translations/es.json index a921723e893..6653fa3198e 100644 --- a/homeassistant/components/vicare/translations/es.json +++ b/homeassistant/components/vicare/translations/es.json @@ -13,13 +13,10 @@ "data": { "client_id": "Clave API", "heating_type": "Tipo de calefacci\u00f3n", - "name": "Nombre", "password": "Contrase\u00f1a", - "scan_interval": "Intervalo de exploraci\u00f3n (segundos)", "username": "Email" }, - "description": "Configure la integraci\u00f3n de ViCare. Para generar la clave API, vaya a https://developer.viessmann.com", - "title": "{name}" + "description": "Configure la integraci\u00f3n de ViCare. Para generar la clave API, vaya a https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/et.json b/homeassistant/components/vicare/translations/et.json index 9214a527580..453b9b691bb 100644 --- a/homeassistant/components/vicare/translations/et.json +++ b/homeassistant/components/vicare/translations/et.json @@ -13,13 +13,10 @@ "data": { "client_id": "API v\u00f5ti", "heating_type": "K\u00fcttere\u017eiim", - "name": "Nimi", "password": "Salas\u00f5na", - "scan_interval": "P\u00e4ringute intervall (sekundites)", "username": "E-posti aadress" }, - "description": "Seadista ViCare sidumine. API-v\u00f5tme loomiseks mine aadressile https://developer.viessmann.com", - "title": "{name}" + "description": "Seadista ViCare sidumine. API-v\u00f5tme loomiseks mine aadressile https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/fr.json b/homeassistant/components/vicare/translations/fr.json index b3dce5ec826..bdf85ff8007 100644 --- a/homeassistant/components/vicare/translations/fr.json +++ b/homeassistant/components/vicare/translations/fr.json @@ -13,13 +13,10 @@ "data": { "client_id": "Cl\u00e9 d'API", "heating_type": "Type de chauffage", - "name": "Nom", "password": "Mot de passe", - "scan_interval": "Intervalle de balayage (secondes)", "username": "Courriel" }, - "description": "Configurer l'int\u00e9gration ViCare. Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.viessmann.com", - "title": "{name}" + "description": "Configurer l'int\u00e9gration ViCare. Pour g\u00e9n\u00e9rer une cl\u00e9 d'API, rendez-vous sur https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/he.json b/homeassistant/components/vicare/translations/he.json index db5a392f540..c64540395cd 100644 --- a/homeassistant/components/vicare/translations/he.json +++ b/homeassistant/components/vicare/translations/he.json @@ -12,11 +12,9 @@ "user": { "data": { "client_id": "\u05de\u05e4\u05ea\u05d7 API", - "name": "\u05e9\u05dd", "password": "\u05e1\u05d9\u05e1\u05de\u05d4", "username": "\u05d3\u05d5\u05d0\"\u05dc" - }, - "title": "{name}" + } } } } diff --git a/homeassistant/components/vicare/translations/hu.json b/homeassistant/components/vicare/translations/hu.json index 0e69a395a90..cc94705aad8 100644 --- a/homeassistant/components/vicare/translations/hu.json +++ b/homeassistant/components/vicare/translations/hu.json @@ -13,13 +13,10 @@ "data": { "client_id": "API kulcs", "heating_type": "F\u0171t\u00e9s t\u00edpusa", - "name": "Elnevez\u00e9s", "password": "Jelsz\u00f3", - "scan_interval": "Beolvas\u00e1si id\u0151k\u00f6z (m\u00e1sodperc)", "username": "E-mail" }, - "description": "A ViCare integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa. Az API-kulcs gener\u00e1l\u00e1s\u00e1hoz keresse fel a https://developer.viessmann.com webhelyet.", - "title": "{name}" + "description": "A ViCare integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa. Az API-kulcs gener\u00e1l\u00e1s\u00e1hoz keresse fel a https://developer.viessmann.com webhelyet." } } } diff --git a/homeassistant/components/vicare/translations/id.json b/homeassistant/components/vicare/translations/id.json index 7c69ec28d85..32d0813b672 100644 --- a/homeassistant/components/vicare/translations/id.json +++ b/homeassistant/components/vicare/translations/id.json @@ -13,13 +13,10 @@ "data": { "client_id": "Kunci API", "heating_type": "Jenis pemanasan", - "name": "Nama", "password": "Kata Sandi", - "scan_interval": "Interval Pindai (detik)", "username": "Email" }, - "description": "Siapkan integrasi ViCare. Untuk menghasilkan kunci API, buka https://developer.viessmann.com", - "title": "{name}" + "description": "Siapkan integrasi ViCare. Untuk menghasilkan kunci API, buka https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/it.json b/homeassistant/components/vicare/translations/it.json index 151e0c84509..c6917117a1f 100644 --- a/homeassistant/components/vicare/translations/it.json +++ b/homeassistant/components/vicare/translations/it.json @@ -13,13 +13,10 @@ "data": { "client_id": "Chiave API", "heating_type": "Modalit\u00e0 di riscaldamento", - "name": "Nome", "password": "Password", - "scan_interval": "Intervallo di scansione (secondi)", "username": "Email" }, - "description": "Configura l'integrazione ViCare. Per generare la chiave API vai su https://developer.viessmann.com", - "title": "{name}" + "description": "Configura l'integrazione ViCare. Per generare la chiave API vai su https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/ja.json b/homeassistant/components/vicare/translations/ja.json index f88edf8f359..aa7e159778c 100644 --- a/homeassistant/components/vicare/translations/ja.json +++ b/homeassistant/components/vicare/translations/ja.json @@ -13,13 +13,10 @@ "data": { "client_id": "API\u30ad\u30fc", "heating_type": "\u6696\u623f(\u52a0\u71b1)\u30bf\u30a4\u30d7", - "name": "\u540d\u524d", "password": "\u30d1\u30b9\u30ef\u30fc\u30c9", - "scan_interval": "\u30b9\u30ad\u30e3\u30f3\u30a4\u30f3\u30bf\u30fc\u30d0\u30eb(\u79d2)", "username": "E\u30e1\u30fc\u30eb" }, - "description": "ViCare\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://developer.viessmann.com \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "{name}" + "description": "ViCare\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002 API\u30ad\u30fc\u3092\u751f\u6210\u3059\u308b\u306b\u306f\u3001https://developer.viessmann.com \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044" } } } diff --git a/homeassistant/components/vicare/translations/nl.json b/homeassistant/components/vicare/translations/nl.json index d16758be360..1e5fc68f9e3 100644 --- a/homeassistant/components/vicare/translations/nl.json +++ b/homeassistant/components/vicare/translations/nl.json @@ -13,13 +13,10 @@ "data": { "client_id": "API-sleutel", "heating_type": "Verwarmingstype", - "name": "Naam", "password": "Wachtwoord", - "scan_interval": "Scaninterval (seconden)", "username": "E-mail" }, - "description": "ViCare integratie instellen. Ga naar https://developer.viessmann.com om een API-sleutel te genereren.", - "title": "{name}" + "description": "ViCare integratie instellen. Ga naar https://developer.viessmann.com om een API-sleutel te genereren." } } } diff --git a/homeassistant/components/vicare/translations/no.json b/homeassistant/components/vicare/translations/no.json index 6f2cde73484..dda5a48434b 100644 --- a/homeassistant/components/vicare/translations/no.json +++ b/homeassistant/components/vicare/translations/no.json @@ -13,13 +13,10 @@ "data": { "client_id": "API-n\u00f8kkel", "heating_type": "Oppvarmingstype", - "name": "Navn", "password": "Passord", - "scan_interval": "Skanneintervall (sekunder)", "username": "E-post" }, - "description": "Sett opp ViCare-integrasjon. Hvis du vil generere API-n\u00f8kkel, g\u00e5r du til https://developer.viessmann.com", - "title": "" + "description": "Sett opp ViCare-integrasjon. Hvis du vil generere API-n\u00f8kkel, g\u00e5r du til https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/pl.json b/homeassistant/components/vicare/translations/pl.json index e8374d7cc46..ae7bf6e5672 100644 --- a/homeassistant/components/vicare/translations/pl.json +++ b/homeassistant/components/vicare/translations/pl.json @@ -13,13 +13,10 @@ "data": { "client_id": "Klucz API", "heating_type": "Typ ogrzewania", - "name": "Nazwa", "password": "Has\u0142o", - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 skanowania (w sekundach)", "username": "Adres e-mail" }, - "description": "Skonfiguruj integracj\u0119 ViCare. Aby wygenerowa\u0107 klucz API wejd\u017a na https://developer.viessmann.com", - "title": "{name}" + "description": "Skonfiguruj integracj\u0119 ViCare. Aby wygenerowa\u0107 klucz API wejd\u017a na https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/pt-BR.json b/homeassistant/components/vicare/translations/pt-BR.json index 01504272dac..1fe4ffe5848 100644 --- a/homeassistant/components/vicare/translations/pt-BR.json +++ b/homeassistant/components/vicare/translations/pt-BR.json @@ -13,13 +13,10 @@ "data": { "client_id": "Chave da API", "heating_type": "Tipo de aquecimento", - "name": "Nome", "password": "Senha", - "scan_interval": "Intervalo de varredura (segundos)", "username": "Email" }, - "description": "Configure a integra\u00e7\u00e3o do ViCare. Para gerar a chave de API, acesse https://developer.viessmann.com", - "title": "{name}" + "description": "Configure a integra\u00e7\u00e3o do ViCare. Para gerar a chave de API, acesse https://developer.viessmann.com" } } } diff --git a/homeassistant/components/vicare/translations/ru.json b/homeassistant/components/vicare/translations/ru.json index 9ce8c37f956..434daeb8e4b 100644 --- a/homeassistant/components/vicare/translations/ru.json +++ b/homeassistant/components/vicare/translations/ru.json @@ -13,13 +13,10 @@ "data": { "client_id": "\u041a\u043b\u044e\u0447 API", "heating_type": "\u0422\u0438\u043f \u043e\u0442\u043e\u043f\u043b\u0435\u043d\u0438\u044f", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "password": "\u041f\u0430\u0440\u043e\u043b\u044c", - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 (\u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)", "username": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b" }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 ViCare. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://developer.viessmann.com.", - "title": "{name}" + "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 ViCare. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 https://developer.viessmann.com." } } } diff --git a/homeassistant/components/vicare/translations/sk.json b/homeassistant/components/vicare/translations/sk.json index d68d88fd2cd..1a2c2a47260 100644 --- a/homeassistant/components/vicare/translations/sk.json +++ b/homeassistant/components/vicare/translations/sk.json @@ -7,7 +7,6 @@ "user": { "data": { "client_id": "API k\u013e\u00fa\u010d", - "name": "N\u00e1zov", "username": "Email" } } diff --git a/homeassistant/components/vicare/translations/tr.json b/homeassistant/components/vicare/translations/tr.json index 0e3bd45bdf7..87cc440733d 100644 --- a/homeassistant/components/vicare/translations/tr.json +++ b/homeassistant/components/vicare/translations/tr.json @@ -13,13 +13,10 @@ "data": { "client_id": "API Anahtar\u0131", "heating_type": "Is\u0131tma tipi", - "name": "Ad", "password": "Parola", - "scan_interval": "Tarama Aral\u0131\u011f\u0131 (saniye)", "username": "E-posta" }, - "description": "ViCare entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.viessmann.com adresine gidin.", - "title": "{name}" + "description": "ViCare entegrasyonunu ayarlay\u0131n. API anahtar\u0131 olu\u015fturmak i\u00e7in https://developer.viessmann.com adresine gidin." } } } diff --git a/homeassistant/components/vicare/translations/zh-Hant.json b/homeassistant/components/vicare/translations/zh-Hant.json index c3b8fc02c4f..4d18a4f11df 100644 --- a/homeassistant/components/vicare/translations/zh-Hant.json +++ b/homeassistant/components/vicare/translations/zh-Hant.json @@ -13,13 +13,10 @@ "data": { "client_id": "API \u91d1\u9470", "heating_type": "\u6696\u6c23\u985e\u5225", - "name": "\u540d\u7a31", "password": "\u5bc6\u78bc", - "scan_interval": "\u6383\u63cf\u9593\u8ddd\uff08\u79d2\uff09", "username": "\u96fb\u5b50\u90f5\u4ef6" }, - "description": "\u6b32\u8a2d\u5b9a ViCare \u6574\u5408\u3002\u8acb\u81f3 https://developer.viessmann.com \u7522\u751f API \u91d1\u9470", - "title": "{name}" + "description": "\u6b32\u8a2d\u5b9a ViCare \u6574\u5408\u3002\u8acb\u81f3 https://developer.viessmann.com \u7522\u751f API \u91d1\u9470" } } } diff --git a/homeassistant/components/vilfo/translations/ca.json b/homeassistant/components/vilfo/translations/ca.json index 827545f5b54..b068144df09 100644 --- a/homeassistant/components/vilfo/translations/ca.json +++ b/homeassistant/components/vilfo/translations/ca.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token d'acc\u00e9s", "host": "Amfitri\u00f3" - }, - "description": "Configura la integraci\u00f3 de l'encaminador Vilfo. Necessites la seva IP o nom d'amfitri\u00f3 i el token d'acc\u00e9s de l'API. Per a m\u00e9s informaci\u00f3, visita: https://www.home-assistant.io/integrations/vilfo", - "title": "Connexi\u00f3 amb l'encaminador Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/cs.json b/homeassistant/components/vilfo/translations/cs.json index b6735bd64cb..77566bbc59e 100644 --- a/homeassistant/components/vilfo/translations/cs.json +++ b/homeassistant/components/vilfo/translations/cs.json @@ -13,9 +13,7 @@ "data": { "access_token": "P\u0159\u00edstupov\u00fd token", "host": "Hostitel" - }, - "description": "Nastaven\u00ed integrace routeru Vilfo. Pot\u0159ebujete n\u00e1zev hostitele/IP adresu routeru Vilfo a p\u0159\u00edstupov\u00fd API token. Dal\u0161\u00ed informace o t\u00e9to integraci a o tom, jak tyto podrobnosti z\u00edskat, najdete na adrese: https://www.home-assistant.io/integrations/vilfo", - "title": "P\u0159ipojen\u00ed k routeru Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/da.json b/homeassistant/components/vilfo/translations/da.json index 0ef9c619c91..014c8aee190 100644 --- a/homeassistant/components/vilfo/translations/da.json +++ b/homeassistant/components/vilfo/translations/da.json @@ -13,9 +13,7 @@ "data": { "access_token": "Adgangstoken til Vilfo-router-API", "host": "Router-v\u00e6rtsnavn eller IP" - }, - "description": "Indstil Vilfo-routerintegration. Du har brug for dit Vilfo-routerv\u00e6rtsnavn/IP og et API-adgangstoken. For yderligere information om denne integration og hvordan du f\u00e5r disse detaljer, kan du bes\u00f8ge: https://www.home-assistant.io/integrations/vilfo", - "title": "Opret forbindelse til Vilfo-router" + } } } } diff --git a/homeassistant/components/vilfo/translations/de.json b/homeassistant/components/vilfo/translations/de.json index 798410c56e3..6e20a86a64a 100644 --- a/homeassistant/components/vilfo/translations/de.json +++ b/homeassistant/components/vilfo/translations/de.json @@ -13,9 +13,7 @@ "data": { "access_token": "Zugangstoken", "host": "Host" - }, - "description": "Richte die Vilfo Router-Integration ein. Du ben\u00f6tigst deinen Vilfo Router-Hostnamen / deine IP-Adresse und ein API-Zugriffstoken. Weitere Informationen zu dieser Integration und wie du diese Details erh\u00e4ltst, findest du unter: https://www.home-assistant.io/integrations/vilfo", - "title": "Stelle eine Verbindung zum Vilfo Router her" + } } } } diff --git a/homeassistant/components/vilfo/translations/el.json b/homeassistant/components/vilfo/translations/el.json index 7af0b69e97e..dad6b11a492 100644 --- a/homeassistant/components/vilfo/translations/el.json +++ b/homeassistant/components/vilfo/translations/el.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2" - }, - "description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae Vilfo. \u03a7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c3\u03c4\u03b5 \u03c4\u03bf hostname/IP \u03c4\u03bf\u03c5 Vilfo Router \u03ba\u03b1\u03b9 \u03ad\u03bd\u03b1 \u03ba\u03bf\u03c5\u03c0\u03cc\u03bd\u03b9 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 API. \u0393\u03b9\u03b1 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b5\u03c2 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03ae \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03ba\u03b1\u03b9 \u03c0\u03ce\u03c2 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ad\u03c2 \u03c4\u03b9\u03c2 \u03bb\u03b5\u03c0\u03c4\u03bf\u03bc\u03ad\u03c1\u03b5\u03b9\u03b5\u03c2, \u03b5\u03c0\u03b9\u03c3\u03ba\u03b5\u03c6\u03b8\u03b5\u03af\u03c4\u03b5: https://www.home-assistant.io/integrations/vilfo", - "title": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf \u03b4\u03c1\u03bf\u03bc\u03bf\u03bb\u03bf\u03b3\u03b7\u03c4\u03ae Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/en.json b/homeassistant/components/vilfo/translations/en.json index 03f9a9bd23a..f26c34aa917 100644 --- a/homeassistant/components/vilfo/translations/en.json +++ b/homeassistant/components/vilfo/translations/en.json @@ -13,9 +13,7 @@ "data": { "access_token": "Access Token", "host": "Host" - }, - "description": "Set up the Vilfo Router integration. You need your Vilfo Router hostname/IP and an API access token. For additional information on this integration and how to get those details, visit: https://www.home-assistant.io/integrations/vilfo", - "title": "Connect to the Vilfo Router" + } } } } diff --git a/homeassistant/components/vilfo/translations/es-419.json b/homeassistant/components/vilfo/translations/es-419.json index 9c62c327235..03bd5f05446 100644 --- a/homeassistant/components/vilfo/translations/es-419.json +++ b/homeassistant/components/vilfo/translations/es-419.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token de acceso para la API del enrutador Vilfo", "host": "Nombre de host o IP del enrutador" - }, - "description": "Configure la integraci\u00f3n del enrutador Vilfo. Necesita su nombre de host/IP de Vilfo Router y un token de acceso API. Para obtener informaci\u00f3n adicional sobre esta integraci\u00f3n y c\u00f3mo obtener esos detalles, visite: https://www.home-assistant.io/integrations/vilfo", - "title": "Conectar con el Router Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/es.json b/homeassistant/components/vilfo/translations/es.json index 863e0cfb6ce..eb5219b01a1 100644 --- a/homeassistant/components/vilfo/translations/es.json +++ b/homeassistant/components/vilfo/translations/es.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token de acceso", "host": "Host" - }, - "description": "Configure la integraci\u00f3n del Router Vilfo. Necesita su nombre de host/IP del Router Vilfo y un token de acceso a la API. Para obtener informaci\u00f3n adicional sobre esta integraci\u00f3n y c\u00f3mo obtener esos detalles, visite: https://www.home-assistant.io/integrations/vilfo", - "title": "Conectar con el Router Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/et.json b/homeassistant/components/vilfo/translations/et.json index 8ed6aa9e14c..2274221a6bb 100644 --- a/homeassistant/components/vilfo/translations/et.json +++ b/homeassistant/components/vilfo/translations/et.json @@ -13,9 +13,7 @@ "data": { "access_token": "Juurdep\u00e4\u00e4sut\u00f5end", "host": "" - }, - "description": "Seadista Vilfo ruuteri sidumine. Vaja on oma Vilfo ruuteri hosti nime / IP-d ja API juurdep\u00e4\u00e4suluba. Lisateavet selle integreerimise ja selle kohta, kuidas neid \u00fcksikasju saada, leiad aadressilt https://www.home-assistant.io/integrations/vilfo", - "title": "\u00dchenda Vilfo ruuteriga" + } } } } diff --git a/homeassistant/components/vilfo/translations/fr.json b/homeassistant/components/vilfo/translations/fr.json index 10289500383..55658b0f1ca 100644 --- a/homeassistant/components/vilfo/translations/fr.json +++ b/homeassistant/components/vilfo/translations/fr.json @@ -13,9 +13,7 @@ "data": { "access_token": "Jeton d'acc\u00e8s", "host": "H\u00f4te" - }, - "description": "Configurez l'int\u00e9gration du routeur Vilfo. Vous avez besoin du nom d'h\u00f4te / IP de votre routeur Vilfo et d'un jeton d'acc\u00e8s API. Pour plus d'informations sur cette int\u00e9gration et comment obtenir ces d\u00e9tails, visitez: https://www.home-assistant.io/integrations/vilfo", - "title": "Connectez-vous au routeur Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/he.json b/homeassistant/components/vilfo/translations/he.json index 642f482d14e..87a65e1af93 100644 --- a/homeassistant/components/vilfo/translations/he.json +++ b/homeassistant/components/vilfo/translations/he.json @@ -13,8 +13,7 @@ "data": { "access_token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05d4", "host": "\u05de\u05d0\u05e8\u05d7" - }, - "description": "\u05d4\u05d2\u05d3\u05e8 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d4\u05e0\u05ea\u05d1 \u05e9\u05dc Vilfo. \u05d0\u05ea\u05d4 \u05e6\u05e8\u05d9\u05da \u05d0\u05ea \u05e9\u05dd \u05d4\u05de\u05d0\u05e8\u05d7/IP \u05e9\u05dc \u05e0\u05ea\u05d1 Vilfo \u05e9\u05dc\u05da \u05d5\u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05d2\u05d9\u05e9\u05ea API. \u05dc\u05de\u05d9\u05d3\u05e2 \u05e0\u05d5\u05e1\u05e3 \u05e2\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1 \u05d6\u05d4 \u05d5\u05db\u05d9\u05e6\u05d3 \u05dc\u05e7\u05d1\u05dc \u05e4\u05e8\u05d8\u05d9\u05dd \u05d0\u05dc\u05d4, \u05d1\u05e7\u05e8 \u05d1\u05db\u05ea\u05d5\u05d1\u05ea: https://www.home-assistant.io/integrations/vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/hu.json b/homeassistant/components/vilfo/translations/hu.json index 66e0b4d7d77..f2b23836336 100644 --- a/homeassistant/components/vilfo/translations/hu.json +++ b/homeassistant/components/vilfo/translations/hu.json @@ -13,9 +13,7 @@ "data": { "access_token": "Hozz\u00e1f\u00e9r\u00e9si token", "host": "C\u00edm" - }, - "description": "\u00c1ll\u00edtsa be a Vilfo Router integr\u00e1ci\u00f3t. Sz\u00fcks\u00e9ge van a Vilfo Router g\u00e9pnev\u00e9re/IP -c\u00edm\u00e9re \u00e9s egy API hozz\u00e1f\u00e9r\u00e9si jogkivonatra. Ha tov\u00e1bbi inform\u00e1ci\u00f3ra van sz\u00fcks\u00e9ge az integr\u00e1ci\u00f3r\u00f3l \u00e9s a r\u00e9szletekr\u0151l, l\u00e1togasson el a k\u00f6vetkez\u0151 webhelyre: https://www.home-assistant.io/integrations/vilfo", - "title": "Csatlakoz\u00e1s a Vilfo routerhez" + } } } } diff --git a/homeassistant/components/vilfo/translations/id.json b/homeassistant/components/vilfo/translations/id.json index 3871670a1cd..9f8adda9737 100644 --- a/homeassistant/components/vilfo/translations/id.json +++ b/homeassistant/components/vilfo/translations/id.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token Akses", "host": "Host" - }, - "description": "Siapkan integrasi Router Vilfo. Anda memerlukan nama host/IP Router Vilfo dan token akses API. Untuk informasi lebih lanjut tentang integrasi ini dan cara mendapatkan data tersebut, kunjungi: https://www.home-assistant.io/integrations/vilfo", - "title": "Hubungkan ke Router Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/it.json b/homeassistant/components/vilfo/translations/it.json index 7d30b0017fb..8c813c64226 100644 --- a/homeassistant/components/vilfo/translations/it.json +++ b/homeassistant/components/vilfo/translations/it.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token di accesso", "host": "Host" - }, - "description": "Configura l'integrazione del Vilfo Router. Hai bisogno del tuo nome host/IP del Vilfo Router e un token di accesso API. Per ulteriori informazioni su questa integrazione e su come ottenere tali dettagli, visita il sito: https://www.home-assistant.io/integrations/vilfo", - "title": "Collegamento al Vilfo Router" + } } } } diff --git a/homeassistant/components/vilfo/translations/ja.json b/homeassistant/components/vilfo/translations/ja.json index 6f98d264f24..6a41c1168cb 100644 --- a/homeassistant/components/vilfo/translations/ja.json +++ b/homeassistant/components/vilfo/translations/ja.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3", "host": "\u30db\u30b9\u30c8" - }, - "description": "Vilfo Router\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u3002Vilfo Router\u306e\u30db\u30b9\u30c8\u540d/IP\u3068API\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u304c\u5fc5\u8981\u3067\u3059\u3002\u3053\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306b\u95a2\u3059\u308b\u8a73\u7d30\u3068\u305d\u308c\u3089\u306e\u53d6\u5f97\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001https://www.home-assistant.io/integrations/vilfo \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3066\u304f\u3060\u3055\u3044", - "title": "Vilfo\u30eb\u30fc\u30bf\u30fc\u306b\u63a5\u7d9a" + } } } } diff --git a/homeassistant/components/vilfo/translations/ko.json b/homeassistant/components/vilfo/translations/ko.json index 980ee36d7a5..d2db1a7fb7a 100644 --- a/homeassistant/components/vilfo/translations/ko.json +++ b/homeassistant/components/vilfo/translations/ko.json @@ -13,9 +13,7 @@ "data": { "access_token": "\uc561\uc138\uc2a4 \ud1a0\ud070", "host": "\ud638\uc2a4\ud2b8" - }, - "description": "Vilfo \ub77c\uc6b0\ud130 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. Vilfo \ub77c\uc6b0\ud130 \ud638\uc2a4\ud2b8 \uc774\ub984/IP \uc640 API \uc561\uc138\uc2a4 \ud1a0\ud070\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. \uc774 \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\uc5d0 \ub300\ud55c \ucd94\uac00 \uc815\ubcf4\uc640 \uc138\ubd80 \uc0ac\ud56d\uc740 https://www.home-assistant.io/integrations/vilfo \ub97c \ucc38\uc870\ud574\uc8fc\uc138\uc694", - "title": "Vilfo \ub77c\uc6b0\ud130\uc5d0 \uc5f0\uacb0\ud558\uae30" + } } } } diff --git a/homeassistant/components/vilfo/translations/lb.json b/homeassistant/components/vilfo/translations/lb.json index cdf16438469..ebbe55e48eb 100644 --- a/homeassistant/components/vilfo/translations/lb.json +++ b/homeassistant/components/vilfo/translations/lb.json @@ -13,9 +13,7 @@ "data": { "access_token": "Acc\u00e8s Jeton", "host": "Host" - }, - "description": "Vilfo Router Integratioun ariichten. Dir braucht \u00e4re Vilfo Router Numm/IP an een API Acc\u00e8s Jeton. Fir weider Informatiounen zu d\u00ebser Integratioun a w\u00e9i een zu d\u00ebsen n\u00e9idegen Informatioune k\u00ebnnt, gitt op: https://www.home-assistant.io/integrations/vilfo", - "title": "Mam Vilfo Router verbannen" + } } } } diff --git a/homeassistant/components/vilfo/translations/nl.json b/homeassistant/components/vilfo/translations/nl.json index 304871cc145..fc49ddbf7b8 100644 --- a/homeassistant/components/vilfo/translations/nl.json +++ b/homeassistant/components/vilfo/translations/nl.json @@ -13,9 +13,7 @@ "data": { "access_token": "Toegangstoken", "host": "Host" - }, - "description": "Stel de Vilfo Router-integratie in. U heeft de hostnaam/IP van uw Vilfo Router en een API-toegangstoken nodig. Voor meer informatie over deze integratie en hoe u die details kunt verkrijgen, gaat u naar: https://www.home-assistant.io/integrations/vilfo", - "title": "Maak verbinding met de Vilfo Router" + } } } } diff --git a/homeassistant/components/vilfo/translations/no.json b/homeassistant/components/vilfo/translations/no.json index b1f1cd8950f..6550778a853 100644 --- a/homeassistant/components/vilfo/translations/no.json +++ b/homeassistant/components/vilfo/translations/no.json @@ -13,9 +13,7 @@ "data": { "access_token": "Tilgangstoken", "host": "Vert" - }, - "description": "Sett opp Vilfo Router-integrasjonen. Du trenger ditt Vilfo Router vertsnavn/IP og et API-tilgangstoken. For ytterligere informasjon om denne integrasjonen og hvordan du f\u00e5r disse detaljene, bes\u00f8k [https://www.home-assistant.io/integrations/vilfo](https://www.home-assistant.io/integrations/vilfo)", - "title": "Koble til Vilfo Ruteren" + } } } } diff --git a/homeassistant/components/vilfo/translations/pl.json b/homeassistant/components/vilfo/translations/pl.json index 43b94ffd103..a5016c989f5 100644 --- a/homeassistant/components/vilfo/translations/pl.json +++ b/homeassistant/components/vilfo/translations/pl.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token dost\u0119pu", "host": "Nazwa hosta lub adres IP" - }, - "description": "Skonfiguruj integracj\u0119 routera Vilfo. Potrzebujesz nazwy hosta/adresu IP routera Vilfo i tokena dost\u0119pu do interfejsu API. Aby uzyska\u0107 dodatkowe informacje na temat tej integracji i sposobu uzyskania niezb\u0119dnych danych do konfiguracji, odwied\u017a: https://www.home-assistant.io/integrations/vilfo", - "title": "Po\u0142\u0105czenie z routerem Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/pt-BR.json b/homeassistant/components/vilfo/translations/pt-BR.json index 8766261955f..f772ec37472 100644 --- a/homeassistant/components/vilfo/translations/pt-BR.json +++ b/homeassistant/components/vilfo/translations/pt-BR.json @@ -13,9 +13,7 @@ "data": { "access_token": "Token de acesso", "host": "Nome do host" - }, - "description": "Configure a integra\u00e7\u00e3o do roteador Vilfo. Voc\u00ea precisa do seu nome de host/IP do roteador Vilfo e um token de acesso \u00e0 API. Para obter informa\u00e7\u00f5es adicionais sobre essa integra\u00e7\u00e3o e como obter esses detalhes, visite: https://www.home-assistant.io/integrations/vilfo", - "title": "Conecte-se ao roteador Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/ru.json b/homeassistant/components/vilfo/translations/ru.json index 62ec2fe5dae..6a2a2163313 100644 --- a/homeassistant/components/vilfo/translations/ru.json +++ b/homeassistant/components/vilfo/translations/ru.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430", "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 Vilfo. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u043e\u043c\u0435\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0438\u043b\u0438 IP-\u0430\u0434\u0440\u0435\u0441 \u0440\u043e\u0443\u0442\u0435\u0440\u0430 \u0438 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 API. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u044d\u0442\u043e\u0439 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u0435 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442: https://www.home-assistant.io/integrations/vilfo.", - "title": "Vilfo Router" + } } } } diff --git a/homeassistant/components/vilfo/translations/sl.json b/homeassistant/components/vilfo/translations/sl.json index c815698099f..d31dc23a3f6 100644 --- a/homeassistant/components/vilfo/translations/sl.json +++ b/homeassistant/components/vilfo/translations/sl.json @@ -13,9 +13,7 @@ "data": { "access_token": "Dostopni \u017eeton za API Vilfo Router", "host": "Ime gostitelja usmerjevalnika ali IP" - }, - "description": "Nastavite integracijo Vilfo Router. Potrebujete ime gostitelja ali IP Vilfo usmerjevalnika in dostopni \u017eeton API. Za dodatne informacije o tej integraciji in kako do teh podrobnosti obi\u0161\u010dite: https://www.home-assistant.io/integrations/vilfo", - "title": "Pove\u017eite se z usmerjevalnikom Vilfo" + } } } } diff --git a/homeassistant/components/vilfo/translations/sv.json b/homeassistant/components/vilfo/translations/sv.json index 7192f91ee1a..2594b350d1c 100644 --- a/homeassistant/components/vilfo/translations/sv.json +++ b/homeassistant/components/vilfo/translations/sv.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u00c5tkomstnyckel f\u00f6r Vilfo-routerns API", "host": "Routerns v\u00e4rdnamn eller IP-adress" - }, - "description": "St\u00e4ll in Vilfo Router-integrationen. Du beh\u00f6ver din Vilfo-routers v\u00e4rdnamn eller IP-adress och en \u00e5tkomstnyckel till dess API. F\u00f6r ytterligare information om den h\u00e4r integrationen och hur du f\u00e5r fram den n\u00f6dv\u00e4ndiga informationen, bes\u00f6k: https://www.home-assistant.io/integrations/vilfo", - "title": "Anslut till Vilfo-routern" + } } } } diff --git a/homeassistant/components/vilfo/translations/tr.json b/homeassistant/components/vilfo/translations/tr.json index 2e27132afb6..dc66041e35a 100644 --- a/homeassistant/components/vilfo/translations/tr.json +++ b/homeassistant/components/vilfo/translations/tr.json @@ -13,9 +13,7 @@ "data": { "access_token": "Eri\u015fim Belirteci", "host": "Ana Bilgisayar" - }, - "description": "Vilfo Router entegrasyonunu ayarlay\u0131n. Vilfo Router ana bilgisayar ad\u0131n\u0131za/IP'nize ve bir API eri\u015fim anahtar\u0131na ihtiyac\u0131n\u0131z var. Bu entegrasyon ve bu ayr\u0131nt\u0131lar\u0131n nas\u0131l al\u0131naca\u011f\u0131 hakk\u0131nda ek bilgi i\u00e7in \u015fu adresi ziyaret edin: https://www.home-assistant.io/integrations/vilfo", - "title": "Vilfo Router'a ba\u011flan\u0131n" + } } } } diff --git a/homeassistant/components/vilfo/translations/uk.json b/homeassistant/components/vilfo/translations/uk.json index 1a93176f290..4688b58e187 100644 --- a/homeassistant/components/vilfo/translations/uk.json +++ b/homeassistant/components/vilfo/translations/uk.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443", "host": "\u0425\u043e\u0441\u0442" - }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430 Vilfo. \u0412\u043a\u0430\u0436\u0456\u0442\u044c \u0434\u043e\u043c\u0435\u043d\u043d\u0435 \u0456\u043c'\u044f \u0430\u0431\u043e IP-\u0430\u0434\u0440\u0435\u0441\u0443 \u0440\u043e\u0443\u0442\u0435\u0440\u0430 \u0456 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0443 API. \u0414\u043b\u044f \u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f \u0431\u0456\u043b\u044c\u0448 \u0434\u043e\u043a\u043b\u0430\u0434\u043d\u043e\u0457 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u0457 \u043f\u0440\u043e \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0446\u0456\u0454\u0457 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457, \u0432\u0456\u0434\u0432\u0456\u0434\u0430\u0439\u0442\u0435 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442: https://www.home-assistant.io/integrations/vilfo.", - "title": "Vilfo Router" + } } } } diff --git a/homeassistant/components/vilfo/translations/zh-Hans.json b/homeassistant/components/vilfo/translations/zh-Hans.json index f3fec441d4c..756c7e68c98 100644 --- a/homeassistant/components/vilfo/translations/zh-Hans.json +++ b/homeassistant/components/vilfo/translations/zh-Hans.json @@ -9,9 +9,7 @@ "data": { "access_token": "Vilfo \u8def\u7531\u5668 API \u5b58\u53d6\u5bc6\u94a5", "host": "\u8def\u7531\u5668\u4e3b\u673a\u540d\u6216 IP \u5730\u5740" - }, - "description": "\u8bbe\u7f6e Vilfo \u8def\u7531\u5668\u6574\u5408\u3002\u60a8\u9700\u8981\u8f93\u5165 Vilfo \u8def\u7531\u5668\u4e3b\u673a\u540d/IP \u5730\u5740\u3001API\u5b58\u53d6\u5bc6\u94a5\u3002\u5176\u4ed6\u6574\u5408\u7684\u76f8\u5173\u4fe1\u606f\uff0c\u8bf7\u8bbf\u95ee\uff1ahttps://www.home-assistant.io/integrations/vilfo", - "title": "\u8fde\u63a5\u5230 Vilfo \u8def\u7531\u5668" + } } } } diff --git a/homeassistant/components/vilfo/translations/zh-Hant.json b/homeassistant/components/vilfo/translations/zh-Hant.json index eb3dba826be..171fa5b16e8 100644 --- a/homeassistant/components/vilfo/translations/zh-Hant.json +++ b/homeassistant/components/vilfo/translations/zh-Hant.json @@ -13,9 +13,7 @@ "data": { "access_token": "\u5b58\u53d6\u6b0a\u6756", "host": "\u4e3b\u6a5f\u7aef" - }, - "description": "\u8a2d\u5b9a Vilfo \u8def\u7531\u5668\u6574\u5408\u3002\u9700\u8981\u8f38\u5165 Vilfo \u8def\u7531\u5668\u4e3b\u6a5f\u540d\u7a31/IP \u4f4d\u5740\u3001API \u5b58\u53d6\u6b0a\u6756\u3002\u5176\u4ed6\u6574\u5408\u76f8\u95dc\u8cc7\u8a0a\uff0c\u8acb\u53c3\u8003\uff1ahttps://www.home-assistant.io/integrations/vilfo", - "title": "\u9023\u7dda\u81f3 Vilfo \u8def\u7531\u5668" + } } } } diff --git a/homeassistant/components/vulcan/translations/bg.json b/homeassistant/components/vulcan/translations/bg.json index 27893871c14..187ad8cff4b 100644 --- a/homeassistant/components/vulcan/translations/bg.json +++ b/homeassistant/components/vulcan/translations/bg.json @@ -13,18 +13,5 @@ } } } - }, - "options": { - "error": { - "error": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430" - }, - "step": { - "init": { - "data": { - "message_notify": "\u041f\u043e\u043a\u0430\u0437\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0432\u0430\u043d\u0435 \u043d\u0430 \u043d\u043e\u0432\u043e \u0441\u044a\u043e\u0431\u0449\u0435\u043d\u0438\u0435", - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u044f\u0432\u0430\u043d\u0435 (\u0432 \u043c\u0438\u043d\u0443\u0442\u0438)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ca.json b/homeassistant/components/vulcan/translations/ca.json index a9bfe95310b..2f7e454be6b 100644 --- a/homeassistant/components/vulcan/translations/ca.json +++ b/homeassistant/components/vulcan/translations/ca.json @@ -30,14 +30,6 @@ }, "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "S\u00edmbol", - "token": "Token" - }, - "description": "Inicia sessi\u00f3 al teu compte de Vulcan mitjan\u00e7ant la p\u00e0gina de registre de l'aplicaci\u00f3 m\u00f2bil." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "Selecciona l'alumne, pots afegir m\u00e9s alumnes tornant a afegir la integraci\u00f3 de nou." } } - }, - "options": { - "error": { - "error": "S'ha produ\u00eft un error" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Mostra les notificacions sobre les darreres assist\u00e8ncies", - "grade_notify": "Mostra notificacions de les \u00faltimes qualificacions", - "message_notify": "Mostra notificacions quan es rebi un missatge nou", - "scan_interval": "Interval d'actualitzaci\u00f3 (minuts)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/de.json b/homeassistant/components/vulcan/translations/de.json index 94f25858119..66de1c8ec75 100644 --- a/homeassistant/components/vulcan/translations/de.json +++ b/homeassistant/components/vulcan/translations/de.json @@ -30,14 +30,6 @@ }, "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "Symbol", - "token": "Token" - }, - "description": "Melde dich bei deinem Vulcan-Konto \u00fcber die Registrierungsseite der mobilen App an." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "W\u00e4hle Sch\u00fcler aus, Du kannst weitere Sch\u00fcler hinzuf\u00fcgen, indem du die Integration erneut hinzuf\u00fcgst." } } - }, - "options": { - "error": { - "error": "Ein Fehler ist aufgetreten" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Benachrichtigungen \u00fcber die neuesten Anwesenheitseintr\u00e4ge anzeigen", - "grade_notify": "Benachrichtigungen \u00fcber die neuesten Noten anzeigen", - "message_notify": "Benachrichtigungen anzeigen, wenn eine neue Nachricht eingegangen ist", - "scan_interval": "Aktualisierungsintervall (in Minuten)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/el.json b/homeassistant/components/vulcan/translations/el.json index abd3a264463..a041679919a 100644 --- a/homeassistant/components/vulcan/translations/el.json +++ b/homeassistant/components/vulcan/translations/el.json @@ -30,14 +30,6 @@ }, "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." }, - "reauth": { - "data": { - "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", - "region": "\u03a3\u03cd\u03bc\u03b2\u03bf\u03bb\u03bf", - "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc" - }, - "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 Vulcan \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ce\u03bd\u03c4\u03b1\u03c2 \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1 \u03b5\u03b3\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2 \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae\u03c2 \u03b3\u03b9\u03b1 \u03ba\u03b9\u03bd\u03b7\u03c4\u03ac." - }, "reauth_confirm": { "data": { "pin": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN", @@ -59,20 +51,5 @@ "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ae, \u03bc\u03c0\u03bf\u03c1\u03b5\u03af\u03c4\u03b5 \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03c4\u03b5 \u03c0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03bf\u03c5\u03c2 \u03bc\u03b1\u03b8\u03b7\u03c4\u03ad\u03c2 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c4\u03bf\u03bd\u03c4\u03b1\u03c2 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7." } } - }, - "options": { - "error": { - "error": "\u03a0\u03c1\u03bf\u03ad\u03ba\u03c5\u03c8\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" - }, - "step": { - "init": { - "data": { - "attendance_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03b9\u03c2 \u03c0\u03b9\u03bf \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03b5\u03c2 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03ae\u03c3\u03b5\u03b9\u03c2 \u03c0\u03b1\u03c1\u03bf\u03c5\u03c3\u03b9\u03ce\u03bd", - "grade_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03c0\u03b9\u03bf \u03c0\u03c1\u03cc\u03c3\u03c6\u03b1\u03c4\u03bf\u03c5\u03c2 \u03b2\u03b1\u03b8\u03bc\u03bf\u03cd\u03c2", - "message_notify": "\u0395\u03bc\u03c6\u03ac\u03bd\u03b9\u03c3\u03b7 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03cc\u03c4\u03b1\u03bd \u03bb\u03b1\u03bc\u03b2\u03ac\u03bd\u03b5\u03c4\u03b1\u03b9 \u03bd\u03ad\u03bf \u03bc\u03ae\u03bd\u03c5\u03bc\u03b1", - "scan_interval": "\u0394\u03b9\u03ac\u03c3\u03c4\u03b7\u03bc\u03b1 \u03b5\u03bd\u03b7\u03bc\u03ad\u03c1\u03c9\u03c3\u03b7\u03c2 (\u03c3\u03b5 \u03bb\u03b5\u03c0\u03c4\u03ac)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/en.json b/homeassistant/components/vulcan/translations/en.json index 8ea9fc59964..65b9616e1e7 100644 --- a/homeassistant/components/vulcan/translations/en.json +++ b/homeassistant/components/vulcan/translations/en.json @@ -30,14 +30,6 @@ }, "description": "Login to your Vulcan Account using mobile app registration page." }, - "reauth": { - "data": { - "pin": "Pin", - "region": "Symbol", - "token": "Token" - }, - "description": "Login to your Vulcan Account using mobile app registration page." - }, "reauth_confirm": { "data": { "pin": "Pin", @@ -59,20 +51,5 @@ "description": "Select student, you can add more students by adding integration again." } } - }, - "options": { - "error": { - "error": "Error occurred" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Show notifications about the latest attendance entries", - "grade_notify": "Show notifications about the latest grades", - "message_notify": "Show notifications when new message received", - "scan_interval": "Update interval (in minutes)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/es.json b/homeassistant/components/vulcan/translations/es.json index fa303806a76..a92a8878730 100644 --- a/homeassistant/components/vulcan/translations/es.json +++ b/homeassistant/components/vulcan/translations/es.json @@ -15,14 +15,6 @@ "pin": "PIN" } }, - "reauth": { - "data": { - "pin": "PIN", - "region": "S\u00edmbolo", - "token": "Token" - }, - "description": "Inicia sesi\u00f3n en tu cuenta de Vulcan mediante la p\u00e1gina de registro de la aplicaci\u00f3n m\u00f3vil." - }, "reauth_confirm": { "data": { "region": "S\u00edmbolo" @@ -34,18 +26,5 @@ } } } - }, - "options": { - "error": { - "error": "Se ha producido un error" - }, - "step": { - "init": { - "data": { - "message_notify": "Muestra notificaciones cuando se reciba un mensaje nuevo", - "scan_interval": "Intervalo de actualizaci\u00f3n (minutos)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/et.json b/homeassistant/components/vulcan/translations/et.json index 80c97d1c4ed..f50fddae8f4 100644 --- a/homeassistant/components/vulcan/translations/et.json +++ b/homeassistant/components/vulcan/translations/et.json @@ -30,14 +30,6 @@ }, "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." }, - "reauth": { - "data": { - "pin": "PIN kood", - "region": "S\u00fcmbol", - "token": "Token" - }, - "description": "Logi Vulcani kontole sisse mobiilirakenduse registreerimislehe kaudu." - }, "reauth_confirm": { "data": { "pin": "PIN kood", @@ -59,20 +51,5 @@ "description": "Vali \u00f5pilane, saad lisada rohkem \u00f5pilasi lisades sidumise veelkord." } } - }, - "options": { - "error": { - "error": "Ilmnes t\u00f5rge" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Kuva m\u00e4rguanded viimaste osalemiskirjete kohta", - "grade_notify": "Kuva m\u00e4rguanded viimaste hinnete kohta", - "message_notify": "Kuva teated uue s\u00f5numi saabumisel", - "scan_interval": "V\u00e4rskendamise intervall (minutites)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/fr.json b/homeassistant/components/vulcan/translations/fr.json index 9a850d85796..0a1f16b8406 100644 --- a/homeassistant/components/vulcan/translations/fr.json +++ b/homeassistant/components/vulcan/translations/fr.json @@ -30,14 +30,6 @@ }, "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "Symbole", - "token": "Jeton" - }, - "description": "Connectez-vous \u00e0 votre compte Vulcan en utilisant la page d'inscription de l'application mobile." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "S\u00e9lectionnez l'\u00e9tudiant\u00a0; vous pouvez ajouter d'autres \u00e9tudiants en ajoutant \u00e0 nouveau l'int\u00e9gration." } } - }, - "options": { - "error": { - "error": "Une erreur est survenue" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Afficher les notifications au sujet des derni\u00e8res entr\u00e9es de pr\u00e9sence", - "grade_notify": "Afficher les notifications au sujet des derni\u00e8res notes", - "message_notify": "Afficher les notifications lors de la r\u00e9ception d'un nouveau message", - "scan_interval": "Intervalle de mise \u00e0 jour (en minutes)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/hu.json b/homeassistant/components/vulcan/translations/hu.json index 90566b69fe8..65d90d6e24a 100644 --- a/homeassistant/components/vulcan/translations/hu.json +++ b/homeassistant/components/vulcan/translations/hu.json @@ -30,14 +30,6 @@ }, "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "Szimb\u00f3lum", - "token": "Token" - }, - "description": "Jelentkezzen be Vulcan fi\u00f3kj\u00e1ba a mobilalkalmaz\u00e1s regisztr\u00e1ci\u00f3s oldal\u00e1n." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "V\u00e1lassza ki a tanul\u00f3t, tov\u00e1bbi tanul\u00f3kat vehet fel az integr\u00e1ci\u00f3 \u00fajb\u00f3li hozz\u00e1ad\u00e1s\u00e1val." } } - }, - "options": { - "error": { - "error": "Hiba t\u00f6rt\u00e9nt" - }, - "step": { - "init": { - "data": { - "attendance_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se a legut\u00f3bbi r\u00e9szv\u00e9teli bejegyz\u00e9sekr\u0151l", - "grade_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se a leg\u00fajabb oszt\u00e1lyzatokr\u00f3l", - "message_notify": "\u00c9rtes\u00edt\u00e9sek megjelen\u00edt\u00e9se \u00faj \u00fczenet \u00e9rkez\u00e9sekor", - "scan_interval": "Friss\u00edt\u00e9si id\u0151k\u00f6z (percben)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/id.json b/homeassistant/components/vulcan/translations/id.json index c372ca0ebe5..6aa1e0d7fa6 100644 --- a/homeassistant/components/vulcan/translations/id.json +++ b/homeassistant/components/vulcan/translations/id.json @@ -30,14 +30,6 @@ }, "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "Simbol", - "token": "Token" - }, - "description": "Masuk ke Akun Vulcan Anda menggunakan halaman pendaftaran aplikasi seluler." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "Pilih siswa, Anda dapat menambahkan lebih banyak siswa dengan menambahkan integrasi lagi." } } - }, - "options": { - "error": { - "error": "Terjadi kesalahan" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Tampilkan notifikasi tentang entri kehadiran terbaru", - "grade_notify": "Tampilkan notifikasi tentang nilai terbaru", - "message_notify": "Tampilkan saat pesan baru diterima", - "scan_interval": "Interval pembaruan (dalam menit)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/it.json b/homeassistant/components/vulcan/translations/it.json index 8fe26ddfba6..affc791af72 100644 --- a/homeassistant/components/vulcan/translations/it.json +++ b/homeassistant/components/vulcan/translations/it.json @@ -30,14 +30,6 @@ }, "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." }, - "reauth": { - "data": { - "pin": "PIN", - "region": "Simbolo", - "token": "Token" - }, - "description": "Accedi al tuo account Vulcan utilizzando la pagina di registrazione dell'applicazione mobile." - }, "reauth_confirm": { "data": { "pin": "PIN", @@ -59,20 +51,5 @@ "description": "Seleziona studente, puoi aggiungere pi\u00f9 studenti aggiungendo nuovamente l'integrazione." } } - }, - "options": { - "error": { - "error": "Si \u00e8 verificato un errore" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Mostra le notifiche sulle ultime voci di partecipazione", - "grade_notify": "Mostra le notifiche sugli ultimi voti", - "message_notify": "Mostra le notifiche quando viene ricevuto un nuovo messaggio", - "scan_interval": "Intervallo di aggiornamento (in minuti)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ja.json b/homeassistant/components/vulcan/translations/ja.json index 2f83f44ab53..4f18d47b7ea 100644 --- a/homeassistant/components/vulcan/translations/ja.json +++ b/homeassistant/components/vulcan/translations/ja.json @@ -30,14 +30,6 @@ }, "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" }, - "reauth": { - "data": { - "pin": "\u30d4\u30f3", - "region": "\u30b7\u30f3\u30dc\u30eb", - "token": "\u30c8\u30fc\u30af\u30f3" - }, - "description": "\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u767b\u9332\u30da\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u3001Vulcan\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002" - }, "reauth_confirm": { "data": { "pin": "\u30d4\u30f3", @@ -59,20 +51,5 @@ "description": "\u751f\u5f92\u3092\u9078\u629e\u3057\u307e\u3059(Select student)\u3002\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u5ea6\u8ffd\u52a0\u3059\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u591a\u304f\u306e\u751f\u5f92\u3092\u8ffd\u52a0\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002" } } - }, - "options": { - "error": { - "error": "\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f" - }, - "step": { - "init": { - "data": { - "attendance_notify": "\u6700\u65b0\u306e\u51fa\u5e2d\u30a8\u30f3\u30c8\u30ea\u306b\u95a2\u3059\u308b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", - "grade_notify": "\u6700\u65b0\u306e\u6210\u7e3e\u306b\u95a2\u3059\u308b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", - "message_notify": "\u65b0\u3057\u3044\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u53d7\u4fe1\u6642\u306b\u901a\u77e5\u3092\u8868\u793a\u3059\u308b", - "scan_interval": "\u66f4\u65b0\u9593\u9694(\u5206\u5358\u4f4d)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/nl.json b/homeassistant/components/vulcan/translations/nl.json index df1990ce1a0..4b95ff00f0d 100644 --- a/homeassistant/components/vulcan/translations/nl.json +++ b/homeassistant/components/vulcan/translations/nl.json @@ -30,14 +30,6 @@ }, "description": "Log in op uw Vulcan-account via de registratiepagina voor mobiele apps." }, - "reauth": { - "data": { - "pin": "Pin", - "region": "Symbool", - "token": "Token" - }, - "description": "Log in op uw Vulcan-account via de registratiepagina voor mobiele apps." - }, "reauth_confirm": { "data": { "pin": "Pincode", @@ -59,20 +51,5 @@ "description": "Selecteer student, u kunt meer studenten toevoegen door integratie opnieuw toe te voegen." } } - }, - "options": { - "error": { - "error": "Er is een fout opgetreden." - }, - "step": { - "init": { - "data": { - "attendance_notify": "Toon meldingen over de laatste aanwezigheidsgegevens", - "grade_notify": "Toon meldingen over de laatste cijfers", - "message_notify": "Meldingen weergeven wanneer een nieuw bericht is ontvangen", - "scan_interval": "Update-interval (in minuten)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/no.json b/homeassistant/components/vulcan/translations/no.json index 78444957ff1..8dafd8b80ba 100644 --- a/homeassistant/components/vulcan/translations/no.json +++ b/homeassistant/components/vulcan/translations/no.json @@ -30,14 +30,6 @@ }, "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." }, - "reauth": { - "data": { - "pin": "Pin", - "region": "Symbol", - "token": "Token" - }, - "description": "Logg p\u00e5 Vulcan-kontoen din ved \u00e5 bruke registreringssiden for mobilappen." - }, "reauth_confirm": { "data": { "pin": "Pin", @@ -59,20 +51,5 @@ "description": "Velg student, du kan legge til flere studenter ved \u00e5 legge til integrering p\u00e5 nytt." } } - }, - "options": { - "error": { - "error": "Det oppstod en feil" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Vis varsler om de siste fremm\u00f8teoppf\u00f8ringene", - "grade_notify": "Vis varsler om de nyeste vurderingene", - "message_notify": "Vis varsler n\u00e5r ny melding mottas", - "scan_interval": "Oppdateringsintervall (i minutter)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/pl.json b/homeassistant/components/vulcan/translations/pl.json index 220b05b6972..c8b1f4cf13a 100644 --- a/homeassistant/components/vulcan/translations/pl.json +++ b/homeassistant/components/vulcan/translations/pl.json @@ -30,14 +30,6 @@ }, "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." }, - "reauth": { - "data": { - "pin": "Kod pin", - "region": "Symbol", - "token": "Token" - }, - "description": "Zaloguj si\u0119 do swojego konta Vulcan za pomoc\u0105 strony rejestracji aplikacji mobilnej." - }, "reauth_confirm": { "data": { "pin": "Kod PIN", @@ -59,20 +51,5 @@ "description": "Wybierz ucznia. Mo\u017cesz doda\u0107 wi\u0119cej uczni\u00f3w, ponownie dodaj\u0105c integracj\u0119." } } - }, - "options": { - "error": { - "error": "Wyst\u0105pi\u0142 b\u0142\u0105d" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Poka\u017c powiadomienia o najnowszych wpisach o obecno\u015bci", - "grade_notify": "Poka\u017c powiadomienia o najnowszych ocenach", - "message_notify": "Pokazuj powiadomienia po otrzymaniu nowej wiadomo\u015bci", - "scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (w minutach)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/pt-BR.json b/homeassistant/components/vulcan/translations/pt-BR.json index 98aaec3c33f..1413cccffc9 100644 --- a/homeassistant/components/vulcan/translations/pt-BR.json +++ b/homeassistant/components/vulcan/translations/pt-BR.json @@ -30,14 +30,6 @@ }, "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo m\u00f3vel." }, - "reauth": { - "data": { - "pin": "Pin", - "region": "S\u00edmbolo", - "token": "Token" - }, - "description": "Fa\u00e7a login na sua conta Vulcan usando a p\u00e1gina de registro do aplicativo m\u00f3vel." - }, "reauth_confirm": { "data": { "pin": "C\u00f3digo PIN", @@ -59,20 +51,5 @@ "description": "Selecione aluno, voc\u00ea pode adicionar mais alunos adicionando integra\u00e7\u00e3o novamente." } } - }, - "options": { - "error": { - "error": "Ocorreu um erro" - }, - "step": { - "init": { - "data": { - "attendance_notify": "Mostrar notifica\u00e7\u00f5es sobre as entradas de participa\u00e7\u00e3o mais recentes", - "grade_notify": "Mostrar notifica\u00e7\u00f5es sobre as notas mais recentes", - "message_notify": "Mostrar notifica\u00e7\u00f5es quando uma nova mensagem for recebida", - "scan_interval": "Intervalo de atualiza\u00e7\u00e3o (em minutos)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/ru.json b/homeassistant/components/vulcan/translations/ru.json index 5ce94bd932f..7692a97fc63 100644 --- a/homeassistant/components/vulcan/translations/ru.json +++ b/homeassistant/components/vulcan/translations/ru.json @@ -30,14 +30,6 @@ }, "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." }, - "reauth": { - "data": { - "pin": "PIN-\u043a\u043e\u0434", - "region": "\u0421\u0438\u043c\u0432\u043e\u043b", - "token": "\u0422\u043e\u043a\u0435\u043d" - }, - "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u0441\u0432\u043e\u044e \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Vulcan, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f." - }, "reauth_confirm": { "data": { "pin": "PIN-\u043a\u043e\u0434", @@ -59,20 +51,5 @@ "description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u043e\u0432, \u0441\u043d\u043e\u0432\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e." } } - }, - "options": { - "error": { - "error": "\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430." - }, - "step": { - "init": { - "data": { - "attendance_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u044f\u0445 \u043e \u043f\u043e\u0441\u0435\u0449\u0430\u0435\u043c\u043e\u0441\u0442\u0438", - "grade_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u043e\u0446\u0435\u043d\u043a\u0430\u0445", - "message_notify": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f", - "scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/tr.json b/homeassistant/components/vulcan/translations/tr.json index e103d8626ab..6d79e4d80a1 100644 --- a/homeassistant/components/vulcan/translations/tr.json +++ b/homeassistant/components/vulcan/translations/tr.json @@ -30,14 +30,6 @@ }, "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." }, - "reauth": { - "data": { - "pin": "Pin", - "region": "Sembol", - "token": "Anahtar" - }, - "description": "Mobil uygulama kay\u0131t sayfas\u0131n\u0131 kullanarak Vulcan Hesab\u0131n\u0131za giri\u015f yap\u0131n." - }, "reauth_confirm": { "data": { "pin": "Pin", @@ -59,20 +51,5 @@ "description": "\u00d6\u011frenciyi se\u00e7in, yeniden t\u00fcmle\u015ftirme ekleyerek daha fazla \u00f6\u011frenci ekleyebilirsiniz." } } - }, - "options": { - "error": { - "error": "Hata olu\u015ftu" - }, - "step": { - "init": { - "data": { - "attendance_notify": "En son kat\u0131l\u0131m giri\u015fleriyle ilgili bildirimleri g\u00f6ster", - "grade_notify": "En son notlarla ilgili bildirimleri g\u00f6ster", - "message_notify": "Yeni mesaj al\u0131nd\u0131\u011f\u0131nda bildirimleri g\u00f6ster", - "scan_interval": "G\u00fcncelleme aral\u0131\u011f\u0131 (dakika olarak)" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/vulcan/translations/zh-Hant.json b/homeassistant/components/vulcan/translations/zh-Hant.json index b26ef1f5cc5..24adfff6e02 100644 --- a/homeassistant/components/vulcan/translations/zh-Hant.json +++ b/homeassistant/components/vulcan/translations/zh-Hant.json @@ -30,14 +30,6 @@ }, "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" }, - "reauth": { - "data": { - "pin": "Pin", - "region": "\u7b26\u865f", - "token": "\u6b0a\u6756" - }, - "description": "\u4f7f\u7528\u884c\u52d5 App \u8a3b\u518a\u9801\u9762\u767b\u5165 Vulcan \u5e33\u865f\u3002" - }, "reauth_confirm": { "data": { "pin": "Pin", @@ -59,20 +51,5 @@ "description": "\u9078\u64c7\u5b78\u751f\u3001\u53ef\u4ee5\u518d\u900f\u904e\u65b0\u589e\u6574\u5408\u65b0\u589e\u5176\u4ed6\u5b78\u751f\u3002" } } - }, - "options": { - "error": { - "error": "\u767c\u751f\u932f\u8aa4" - }, - "step": { - "init": { - "data": { - "attendance_notify": "\u95dc\u65bc\u6700\u65b0\u51fa\u52e4\u5be6\u9ad4\u986f\u793a\u901a\u77e5", - "grade_notify": "\u95dc\u65bc\u6700\u65b0\u6210\u7e3e\u986f\u793a\u901a\u77e5", - "message_notify": "\u7576\u6536\u5230\u65b0\u8a0a\u606f\u6642\u986f\u793a\u901a\u77e5", - "scan_interval": "\u66f4\u65b0\u983b\u7387\uff08\u5206\uff09" - } - } - } } } \ No newline at end of file diff --git a/homeassistant/components/webostv/translations/ko.json b/homeassistant/components/webostv/translations/ko.json new file mode 100644 index 00000000000..e79ddbd7b3f --- /dev/null +++ b/homeassistant/components/webostv/translations/ko.json @@ -0,0 +1,47 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "error_pairing": "LG webOS TV\uc5d0 \uc5f0\uacb0\ub418\uc5c8\uc9c0\ub9cc \ud398\uc5b4\ub9c1\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. TV\ub97c \ucf1c\uac70\ub098 IP \uc8fc\uc18c\ub97c \ud655\uc778\ud558\uc138\uc694." + }, + "flow_title": "LG webOS \uc2a4\ub9c8\ud2b8 TV", + "step": { + "pairing": { + "description": "\ud655\uc778\uc744 \ub204\ub974\uace0 TV\uc5d0\uc11c \ud398\uc5b4\ub9c1 \uc694\uccad\uc744 \uc218\ub77d\ud558\uc138\uc694.\n\n![Image](/static/images/config_webos.png)", + "title": "webOS TV \ud398\uc5b4\ub9c1" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "name": "\uc774\ub984" + }, + "description": "TV\ub97c \ucf1c\uace0 \ub2e4\uc74c \ud544\ub4dc\ub97c \ucc44\uc6b0\uace0 \uc81c\ucd9c\uc744 \ud074\ub9ad\ud558\uc138\uc694.", + "title": "webOS TV\uc5d0 \uc5f0\uacb0" + } + } + }, + "device_automation": { + "trigger_type": { + "webostv.turn_on": "\uae30\uae30\uac00 \ucf1c\uc9c0\ub3c4\ub85d \uc694\uccad\ub418\uc5c8\uc744 \ub54c" + } + }, + "options": { + "error": { + "cannot_retrieve": "\uc18c\uc2a4 \ubaa9\ub85d\uc744 \uac80\uc0c9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc7a5\uce58\uac00 \ucf1c\uc838 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uc138\uc694", + "script_not_found": "\uc2a4\ud06c\ub9bd\ud2b8\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc74c" + }, + "step": { + "init": { + "data": { + "sources": "\uc18c\uc2a4 \ubaa9\ub85d" + }, + "description": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc18c\uc2a4 \uc120\ud0dd", + "title": "webOS \uc2a4\ub9c8\ud2b8 TV \uc635\uc158" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/wilight/translations/ar.json b/homeassistant/components/wilight/translations/ar.json index 033cc81d349..f6b22194fe1 100644 --- a/homeassistant/components/wilight/translations/ar.json +++ b/homeassistant/components/wilight/translations/ar.json @@ -7,8 +7,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u0647\u0644 \u062a\u0631\u064a\u062f \u0625\u0639\u062f\u0627\u062f WiLight {name} \u061f \n\n \u064a\u062f\u0639\u0645: {components}", - "title": "WiLight" + "description": "\u0647\u0644 \u062a\u0631\u064a\u062f \u0625\u0639\u062f\u0627\u062f WiLight {name} \u061f \n\n \u064a\u062f\u0639\u0645: {components}" } } } diff --git a/homeassistant/components/wilight/translations/ca.json b/homeassistant/components/wilight/translations/ca.json index 22044549b0c..e9acff0a91e 100644 --- a/homeassistant/components/wilight/translations/ca.json +++ b/homeassistant/components/wilight/translations/ca.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "S'admeten els seg\u00fcents components: {components}", - "title": "WiLight" + "description": "S'admeten els seg\u00fcents components: {components}" } } } diff --git a/homeassistant/components/wilight/translations/cs.json b/homeassistant/components/wilight/translations/cs.json index 0903e0e3a65..bd61d73667a 100644 --- a/homeassistant/components/wilight/translations/cs.json +++ b/homeassistant/components/wilight/translations/cs.json @@ -8,8 +8,7 @@ "flow_title": "WiLight: {name}", "step": { "confirm": { - "description": "Chcete nastavit WiLight {name} ? \n\n Podporuje: {components}", - "title": "WiLight" + "description": "Chcete nastavit WiLight {name} ? \n\n Podporuje: {components}" } } } diff --git a/homeassistant/components/wilight/translations/de.json b/homeassistant/components/wilight/translations/de.json index 27d48a90e58..84fcf14c2a4 100644 --- a/homeassistant/components/wilight/translations/de.json +++ b/homeassistant/components/wilight/translations/de.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Die folgenden Komponenten werden unterst\u00fctzt: {components}", - "title": "WiLight" + "description": "Die folgenden Komponenten werden unterst\u00fctzt: {components}" } } } diff --git a/homeassistant/components/wilight/translations/el.json b/homeassistant/components/wilight/translations/el.json index 5bb3130753a..79bb1d443c4 100644 --- a/homeassistant/components/wilight/translations/el.json +++ b/homeassistant/components/wilight/translations/el.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf WiLight {name} ;\n\n \u03a5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03b9: {components}", - "title": "WiLight" + "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf WiLight {name} ;\n\n \u03a5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03af\u03b6\u03b5\u03b9: {components}" } } } diff --git a/homeassistant/components/wilight/translations/en.json b/homeassistant/components/wilight/translations/en.json index 1f0733d28f3..01f80fc6e85 100644 --- a/homeassistant/components/wilight/translations/en.json +++ b/homeassistant/components/wilight/translations/en.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "The following components are supported: {components}", - "title": "WiLight" + "description": "The following components are supported: {components}" } } } diff --git a/homeassistant/components/wilight/translations/es.json b/homeassistant/components/wilight/translations/es.json index 23dfdfcc728..5836867c0e7 100644 --- a/homeassistant/components/wilight/translations/es.json +++ b/homeassistant/components/wilight/translations/es.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Se admiten los siguientes componentes: {componentes}", - "title": "WiLight" + "description": "Se admiten los siguientes componentes: {componentes}" } } } diff --git a/homeassistant/components/wilight/translations/et.json b/homeassistant/components/wilight/translations/et.json index dae085d1084..f21fb75e796 100644 --- a/homeassistant/components/wilight/translations/et.json +++ b/homeassistant/components/wilight/translations/et.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Toetatud on j\u00e4rgmised komponendid: {components}", - "title": "" + "description": "Toetatud on j\u00e4rgmised komponendid: {components}" } } } diff --git a/homeassistant/components/wilight/translations/fr.json b/homeassistant/components/wilight/translations/fr.json index ddd85a5cc04..2fc169c5891 100644 --- a/homeassistant/components/wilight/translations/fr.json +++ b/homeassistant/components/wilight/translations/fr.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Les composants suivants sont pris en charge\u00a0: {components}", - "title": "WiLight" + "description": "Les composants suivants sont pris en charge\u00a0: {components}" } } } diff --git a/homeassistant/components/wilight/translations/he.json b/homeassistant/components/wilight/translations/he.json index 977167ec765..48ae2e01df1 100644 --- a/homeassistant/components/wilight/translations/he.json +++ b/homeassistant/components/wilight/translations/he.json @@ -3,11 +3,6 @@ "abort": { "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" }, - "flow_title": "{name}", - "step": { - "confirm": { - "title": "WiLight" - } - } + "flow_title": "{name}" } } \ No newline at end of file diff --git a/homeassistant/components/wilight/translations/hu.json b/homeassistant/components/wilight/translations/hu.json index 02db2f1b7df..bae04810d43 100644 --- a/homeassistant/components/wilight/translations/hu.json +++ b/homeassistant/components/wilight/translations/hu.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "A k\u00f6vetkez\u0151 komponensek t\u00e1mogatottak: {components}", - "title": "WiLight" + "description": "A k\u00f6vetkez\u0151 komponensek t\u00e1mogatottak: {components}" } } } diff --git a/homeassistant/components/wilight/translations/id.json b/homeassistant/components/wilight/translations/id.json index 0489a0e96bb..1597c4a2c72 100644 --- a/homeassistant/components/wilight/translations/id.json +++ b/homeassistant/components/wilight/translations/id.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Komponen berikut didukung: {components}", - "title": "WiLight" + "description": "Komponen berikut didukung: {components}" } } } diff --git a/homeassistant/components/wilight/translations/it.json b/homeassistant/components/wilight/translations/it.json index 7b560b7bfdb..3f425fafbb1 100644 --- a/homeassistant/components/wilight/translations/it.json +++ b/homeassistant/components/wilight/translations/it.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Sono supportati i seguenti componenti: {components}", - "title": "WiLight" + "description": "Sono supportati i seguenti componenti: {components}" } } } diff --git a/homeassistant/components/wilight/translations/ja.json b/homeassistant/components/wilight/translations/ja.json index 0a01f69d43a..1f09be66bf1 100644 --- a/homeassistant/components/wilight/translations/ja.json +++ b/homeassistant/components/wilight/translations/ja.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "WiLight {name} \u3092\u8a2d\u5b9a\u3057\u307e\u3059\u304b\uff1f\n\n \u5bfe\u5fdc\u3057\u3066\u3044\u308b: {components}", - "title": "WiLight" + "description": "WiLight {name} \u3092\u8a2d\u5b9a\u3057\u307e\u3059\u304b\uff1f\n\n \u5bfe\u5fdc\u3057\u3066\u3044\u308b: {components}" } } } diff --git a/homeassistant/components/wilight/translations/ko.json b/homeassistant/components/wilight/translations/ko.json index 1b53a1ba544..ef740d1ca2c 100644 --- a/homeassistant/components/wilight/translations/ko.json +++ b/homeassistant/components/wilight/translations/ko.json @@ -8,8 +8,7 @@ "flow_title": "WiLight: {name}", "step": { "confirm": { - "description": "WiLight {name}\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?\n\n\uc9c0\uc6d0 \uae30\uae30: {components}", - "title": "WiLight" + "description": "WiLight {name}\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?\n\n\uc9c0\uc6d0 \uae30\uae30: {components}" } } } diff --git a/homeassistant/components/wilight/translations/lb.json b/homeassistant/components/wilight/translations/lb.json index 2c0b831098e..002fb238482 100644 --- a/homeassistant/components/wilight/translations/lb.json +++ b/homeassistant/components/wilight/translations/lb.json @@ -8,8 +8,7 @@ "flow_title": "Wilight: {name}", "step": { "confirm": { - "description": "Soll WiLight {name} konfigur\u00e9iert ginn?\n\nEt \u00ebnnerst\u00ebtzt: {components}", - "title": "WiLight" + "description": "Soll WiLight {name} konfigur\u00e9iert ginn?\n\nEt \u00ebnnerst\u00ebtzt: {components}" } } } diff --git a/homeassistant/components/wilight/translations/nl.json b/homeassistant/components/wilight/translations/nl.json index 51cefe8ce28..c1afd57e34d 100644 --- a/homeassistant/components/wilight/translations/nl.json +++ b/homeassistant/components/wilight/translations/nl.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "De volgende componenten worden ondersteund: {components}", - "title": "WiLight" + "description": "De volgende componenten worden ondersteund: {components}" } } } diff --git a/homeassistant/components/wilight/translations/no.json b/homeassistant/components/wilight/translations/no.json index 582b2289a32..aacb91e5638 100644 --- a/homeassistant/components/wilight/translations/no.json +++ b/homeassistant/components/wilight/translations/no.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "F\u00f8lgende komponenter st\u00f8ttes: {components}", - "title": "" + "description": "F\u00f8lgende komponenter st\u00f8ttes: {components}" } } } diff --git a/homeassistant/components/wilight/translations/pl.json b/homeassistant/components/wilight/translations/pl.json index c1f636b9d80..3880698b61c 100644 --- a/homeassistant/components/wilight/translations/pl.json +++ b/homeassistant/components/wilight/translations/pl.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Obs\u0142ugiwane s\u0105 nast\u0119puj\u0105ce komponenty: {components}", - "title": "WiLight" + "description": "Obs\u0142ugiwane s\u0105 nast\u0119puj\u0105ce komponenty: {components}" } } } diff --git a/homeassistant/components/wilight/translations/pt-BR.json b/homeassistant/components/wilight/translations/pt-BR.json index e70a286e812..48678c2d850 100644 --- a/homeassistant/components/wilight/translations/pt-BR.json +++ b/homeassistant/components/wilight/translations/pt-BR.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Os seguintes componentes s\u00e3o compat\u00edveis: {components}", - "title": "WiLight" + "description": "Os seguintes componentes s\u00e3o compat\u00edveis: {components}" } } } diff --git a/homeassistant/components/wilight/translations/pt.json b/homeassistant/components/wilight/translations/pt.json index 5b1005a95f5..7604ea73337 100644 --- a/homeassistant/components/wilight/translations/pt.json +++ b/homeassistant/components/wilight/translations/pt.json @@ -8,8 +8,7 @@ "flow_title": "WiLight: {name}", "step": { "confirm": { - "description": "Deseja configurar o WiLight {name} ? \n\n Suporta: {components}", - "title": "WiLight" + "description": "Deseja configurar o WiLight {name} ? \n\n Suporta: {components}" } } } diff --git a/homeassistant/components/wilight/translations/ru.json b/homeassistant/components/wilight/translations/ru.json index 7873b8ca326..afe612cd77e 100644 --- a/homeassistant/components/wilight/translations/ru.json +++ b/homeassistant/components/wilight/translations/ru.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b: {components}.", - "title": "WiLight" + "description": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b: {components}." } } } diff --git a/homeassistant/components/wilight/translations/tr.json b/homeassistant/components/wilight/translations/tr.json index 4762a678254..012326dc9df 100644 --- a/homeassistant/components/wilight/translations/tr.json +++ b/homeassistant/components/wilight/translations/tr.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "A\u015fa\u011f\u0131daki bile\u015fenler desteklenir: {components}", - "title": "WiLight" + "description": "A\u015fa\u011f\u0131daki bile\u015fenler desteklenir: {components}" } } } diff --git a/homeassistant/components/wilight/translations/uk.json b/homeassistant/components/wilight/translations/uk.json index 7517538499e..f06c8b51d51 100644 --- a/homeassistant/components/wilight/translations/uk.json +++ b/homeassistant/components/wilight/translations/uk.json @@ -8,8 +8,7 @@ "flow_title": "WiLight: {name}", "step": { "confirm": { - "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 WiLight {name}? \n\n \u0426\u0435 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0454: {components}", - "title": "WiLight" + "description": "\u0412\u0438 \u0432\u043f\u0435\u0432\u043d\u0435\u043d\u0456, \u0449\u043e \u0445\u043e\u0447\u0435\u0442\u0435 \u0434\u043e\u0434\u0430\u0442\u0438 WiLight {name}? \n\n \u0426\u0435 \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0454: {components}" } } } diff --git a/homeassistant/components/wilight/translations/zh-Hant.json b/homeassistant/components/wilight/translations/zh-Hant.json index 70a9bff0550..f3f7d9439a9 100644 --- a/homeassistant/components/wilight/translations/zh-Hant.json +++ b/homeassistant/components/wilight/translations/zh-Hant.json @@ -8,8 +8,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "\u652f\u63f4\u4ee5\u4e0b\u5143\u4ef6\uff1a{components}", - "title": "WiLight" + "description": "\u652f\u63f4\u4ee5\u4e0b\u5143\u4ef6\uff1a{components}" } } } diff --git a/homeassistant/components/wiz/translations/bg.json b/homeassistant/components/wiz/translations/bg.json index eb0697ca13d..059a8fb9358 100644 --- a/homeassistant/components/wiz/translations/bg.json +++ b/homeassistant/components/wiz/translations/bg.json @@ -20,8 +20,7 @@ }, "user": { "data": { - "host": "\u0425\u043e\u0441\u0442", - "name": "\u0418\u043c\u0435" + "host": "\u0425\u043e\u0441\u0442" } } } diff --git a/homeassistant/components/wiz/translations/ca.json b/homeassistant/components/wiz/translations/ca.json index 60ca2a8a884..157f2e21dff 100644 --- a/homeassistant/components/wiz/translations/ca.json +++ b/homeassistant/components/wiz/translations/ca.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Vols comen\u00e7ar la configuraci\u00f3?" - }, "discovery_confirm": { "description": "Vols configurar {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Adre\u00e7a IP", - "name": "Nom" + "host": "Adre\u00e7a IP" }, "description": "Si deixes l'adre\u00e7a IP buida, s'utilitzar\u00e0 el descobriment per cercar dispositius." } diff --git a/homeassistant/components/wiz/translations/cs.json b/homeassistant/components/wiz/translations/cs.json index 0656e47c8be..b6b916d5d3a 100644 --- a/homeassistant/components/wiz/translations/cs.json +++ b/homeassistant/components/wiz/translations/cs.json @@ -10,13 +10,9 @@ "unknown": "Neo\u010dek\u00e1van\u00e1 chyba" }, "step": { - "confirm": { - "description": "Chcete za\u010d\u00edt nastavovat?" - }, "user": { "data": { - "host": "IP adresa", - "name": "Jm\u00e9no" + "host": "IP adresa" } } } diff --git a/homeassistant/components/wiz/translations/de.json b/homeassistant/components/wiz/translations/de.json index 73b933fefcf..af503506c59 100644 --- a/homeassistant/components/wiz/translations/de.json +++ b/homeassistant/components/wiz/translations/de.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "M\u00f6chtest Du mit der Einrichtung beginnen?" - }, "discovery_confirm": { "description": "M\u00f6chtest du {name} ({host}) einrichten?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP-Adresse", - "name": "Name" + "host": "IP-Adresse" }, "description": "Wenn du die IP-Adresse leer l\u00e4sst, wird die Erkennung verwendet, um Ger\u00e4te zu finden." } diff --git a/homeassistant/components/wiz/translations/el.json b/homeassistant/components/wiz/translations/el.json index aa137e84fe0..c92d036d5f6 100644 --- a/homeassistant/components/wiz/translations/el.json +++ b/homeassistant/components/wiz/translations/el.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03be\u03b5\u03ba\u03b9\u03bd\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7;" - }, "discovery_confirm": { "description": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf {name} ({host});" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" }, "description": "\u0391\u03bd \u03b1\u03c6\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP \u03ba\u03b5\u03bd\u03ae, \u03b8\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b7\u03b8\u03b5\u03af \u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7 \u03b3\u03b9\u03b1 \u03c4\u03b7\u03bd \u03b5\u03cd\u03c1\u03b5\u03c3\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ce\u03bd." } diff --git a/homeassistant/components/wiz/translations/en.json b/homeassistant/components/wiz/translations/en.json index a612ea165a4..97bb7fc25ba 100644 --- a/homeassistant/components/wiz/translations/en.json +++ b/homeassistant/components/wiz/translations/en.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Do you want to start set up?" - }, "discovery_confirm": { "description": "Do you want to setup {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP Address", - "name": "Name" + "host": "IP Address" }, "description": "If you leave the IP Address empty, discovery will be used to find devices." } diff --git a/homeassistant/components/wiz/translations/es.json b/homeassistant/components/wiz/translations/es.json index bc06bff8053..b86f60649a2 100644 --- a/homeassistant/components/wiz/translations/es.json +++ b/homeassistant/components/wiz/translations/es.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u00bfDesea iniciar la configuraci\u00f3n?" - }, "discovery_confirm": { "description": "\u00bfDesea configurar {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Direcci\u00f3n IP", - "name": "Nombre" + "host": "Direcci\u00f3n IP" }, "description": "Si deja la direcci\u00f3n IP vac\u00eda, la detecci\u00f3n se utilizar\u00e1 para buscar dispositivos." } diff --git a/homeassistant/components/wiz/translations/et.json b/homeassistant/components/wiz/translations/et.json index 9cb84a0e7bd..e220b5beb9f 100644 --- a/homeassistant/components/wiz/translations/et.json +++ b/homeassistant/components/wiz/translations/et.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Kas alustan seadistamist?" - }, "discovery_confirm": { "description": "Kas soovid seadistada {name}({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP aadress", - "name": "Nimi" + "host": "IP aadress" }, "description": "Kui j\u00e4tad IP aadressi t\u00fchjaks kasutatakse seadmete leidmiseks avastamist." } diff --git a/homeassistant/components/wiz/translations/fr.json b/homeassistant/components/wiz/translations/fr.json index 290bda2405c..058e751242d 100644 --- a/homeassistant/components/wiz/translations/fr.json +++ b/homeassistant/components/wiz/translations/fr.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Voulez-vous commencer la configuration\u00a0?" - }, "discovery_confirm": { "description": "Voulez-vous configurer {name} ({host})\u00a0?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Adresse IP", - "name": "Nom" + "host": "Adresse IP" }, "description": "Si vous laissez l'adresse IP vide, la d\u00e9couverte sera utilis\u00e9e pour trouver des appareils." } diff --git a/homeassistant/components/wiz/translations/he.json b/homeassistant/components/wiz/translations/he.json index 8d4e41401c8..81b954067f7 100644 --- a/homeassistant/components/wiz/translations/he.json +++ b/homeassistant/components/wiz/translations/he.json @@ -11,9 +11,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d9\u05dc \u05d1\u05d4\u05d2\u05d3\u05e8\u05d4?" - }, "discovery_confirm": { "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name} ({host})?" }, @@ -24,8 +21,7 @@ }, "user": { "data": { - "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP", - "name": "\u05e9\u05dd" + "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP" } } } diff --git a/homeassistant/components/wiz/translations/hu.json b/homeassistant/components/wiz/translations/hu.json index 63ae8479bb9..0c27ee730b3 100644 --- a/homeassistant/components/wiz/translations/hu.json +++ b/homeassistant/components/wiz/translations/hu.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "El szeretn\u00e9 kezdeni a be\u00e1ll\u00edt\u00e1st?" - }, "discovery_confirm": { "description": "Szeretn\u00e9 be\u00e1ll\u00edtani: {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP c\u00edm", - "name": "Elnevez\u00e9s" + "host": "IP c\u00edm" }, "description": "Ha az IP-c\u00edmet \u00fcresen hagyja, akkor az eszk\u00f6z\u00f6k keres\u00e9se a felder\u00edt\u00e9ssel t\u00f6rt\u00e9nik." } diff --git a/homeassistant/components/wiz/translations/id.json b/homeassistant/components/wiz/translations/id.json index 694973f8ffa..2455999d1fd 100644 --- a/homeassistant/components/wiz/translations/id.json +++ b/homeassistant/components/wiz/translations/id.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Ingin memulai penyiapan?" - }, "discovery_confirm": { "description": "Ingin menyiapkan {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Alamat IP", - "name": "Nama" + "host": "Alamat IP" }, "description": "Jika Alamat IP dibiarkan kosong, proses penemuan akan digunakan untuk menemukan perangkat." } diff --git a/homeassistant/components/wiz/translations/it.json b/homeassistant/components/wiz/translations/it.json index ebd093c22f7..ae87f88a151 100644 --- a/homeassistant/components/wiz/translations/it.json +++ b/homeassistant/components/wiz/translations/it.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Vuoi iniziare la configurazione?" - }, "discovery_confirm": { "description": "Vuoi configurare {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Indirizzo IP", - "name": "Nome" + "host": "Indirizzo IP" }, "description": "Se lasci vuoto l'indirizzo IP, il rilevamento sar\u00e0 utilizzato per trovare i dispositivi." } diff --git a/homeassistant/components/wiz/translations/ja.json b/homeassistant/components/wiz/translations/ja.json index 8d9cbd0c055..e062fbdb75b 100644 --- a/homeassistant/components/wiz/translations/ja.json +++ b/homeassistant/components/wiz/translations/ja.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u958b\u59cb\u3057\u307e\u3059\u304b\uff1f" - }, "discovery_confirm": { "description": "{name} ({host})\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3057\u307e\u3059\u304b\uff1f" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "\u30db\u30b9\u30c8", - "name": "\u540d\u524d" + "host": "\u30db\u30b9\u30c8" }, "description": "\u65b0\u3057\u3044\u96fb\u7403(bulb)\u3092\u8ffd\u52a0\u3059\u308b\u306b\u306f\u3001\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9\u3068\u540d\u524d\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044:" } diff --git a/homeassistant/components/wiz/translations/nl.json b/homeassistant/components/wiz/translations/nl.json index 3b59dd6a672..3a8d7be6ec6 100644 --- a/homeassistant/components/wiz/translations/nl.json +++ b/homeassistant/components/wiz/translations/nl.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Wilt u beginnen met instellen?" - }, "discovery_confirm": { "description": "Wilt u {name} ({host}) instellen?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP-adres", - "name": "Naam" + "host": "IP-adres" }, "description": "Als u het IP-adres leeg laat, zal discovery worden gebruikt om apparaten te vinden." } diff --git a/homeassistant/components/wiz/translations/no.json b/homeassistant/components/wiz/translations/no.json index 7f2090b5b71..031b184ae84 100644 --- a/homeassistant/components/wiz/translations/no.json +++ b/homeassistant/components/wiz/translations/no.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Vil du starte oppsettet?" - }, "discovery_confirm": { "description": "Vil du konfigurere {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP adresse", - "name": "Navn" + "host": "IP adresse" }, "description": "Hvis du lar IP-adressen st\u00e5 tom, vil oppdagelse bli brukt til \u00e5 finne enheter." } diff --git a/homeassistant/components/wiz/translations/pl.json b/homeassistant/components/wiz/translations/pl.json index 30d1455c470..ea3ea48868c 100644 --- a/homeassistant/components/wiz/translations/pl.json +++ b/homeassistant/components/wiz/translations/pl.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Czy chcesz rozpocz\u0105\u0107 konfiguracj\u0119?" - }, "discovery_confirm": { "description": "Czy chcesz skonfigurowa\u0107 {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Adres IP", - "name": "Nazwa" + "host": "Adres IP" }, "description": "Je\u015bli nie podasz adresu IP, zostanie u\u017cyte wykrywanie do odnalezienia urz\u0105dze\u0144." } diff --git a/homeassistant/components/wiz/translations/pt-BR.json b/homeassistant/components/wiz/translations/pt-BR.json index ce27ea82abc..0c2ee8b7c04 100644 --- a/homeassistant/components/wiz/translations/pt-BR.json +++ b/homeassistant/components/wiz/translations/pt-BR.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Deseja iniciar a configura\u00e7\u00e3o?" - }, "discovery_confirm": { "description": "Deseja configurar {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "Endere\u00e7o IP", - "name": "Nome" + "host": "Endere\u00e7o IP" }, "description": "Se voc\u00ea deixar o endere\u00e7o IP vazio, a descoberta ser\u00e1 usada para localizar dispositivos." } diff --git a/homeassistant/components/wiz/translations/ru.json b/homeassistant/components/wiz/translations/ru.json index dd422f29fea..c9fe68b3ce9 100644 --- a/homeassistant/components/wiz/translations/ru.json +++ b/homeassistant/components/wiz/translations/ru.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443?" - }, "discovery_confirm": { "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} ({host})?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + "host": "IP-\u0430\u0434\u0440\u0435\u0441" }, "description": "\u0415\u0441\u043b\u0438 \u043d\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c IP-\u0430\u0434\u0440\u0435\u0441, \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438." } diff --git a/homeassistant/components/wiz/translations/sk.json b/homeassistant/components/wiz/translations/sk.json index f97ee8e1837..641bd9c13ee 100644 --- a/homeassistant/components/wiz/translations/sk.json +++ b/homeassistant/components/wiz/translations/sk.json @@ -2,13 +2,6 @@ "config": { "abort": { "cannot_connect": "Nepodarilo sa pripoji\u0165" - }, - "step": { - "user": { - "data": { - "name": "N\u00e1zov" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/wiz/translations/tr.json b/homeassistant/components/wiz/translations/tr.json index 3f6b1f68dc5..15b2b683a50 100644 --- a/homeassistant/components/wiz/translations/tr.json +++ b/homeassistant/components/wiz/translations/tr.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "Kuruluma ba\u015flamak ister misiniz?" - }, "discovery_confirm": { "description": "{name} ( {host} ) kurulumu yapmak istiyor musunuz?" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP Adresi", - "name": "Ad" + "host": "IP Adresi" }, "description": "IP Adresini bo\u015f b\u0131rak\u0131rsan\u0131z, cihazlar\u0131 bulmak i\u00e7in ke\u015fif kullan\u0131lacakt\u0131r." } diff --git a/homeassistant/components/wiz/translations/zh-Hant.json b/homeassistant/components/wiz/translations/zh-Hant.json index 9e716b4928e..3bce0700569 100644 --- a/homeassistant/components/wiz/translations/zh-Hant.json +++ b/homeassistant/components/wiz/translations/zh-Hant.json @@ -14,9 +14,6 @@ }, "flow_title": "{name} ({host})", "step": { - "confirm": { - "description": "\u662f\u5426\u8981\u958b\u59cb\u8a2d\u5b9a\uff1f" - }, "discovery_confirm": { "description": "\u662f\u5426\u8981\u8a2d\u5b9a {name} ({host})\uff1f" }, @@ -27,8 +24,7 @@ }, "user": { "data": { - "host": "IP \u4f4d\u5740", - "name": "\u540d\u7a31" + "host": "IP \u4f4d\u5740" }, "description": "\u5047\u5982 IP \u4f4d\u5740\u6b04\u4f4d\u70ba\u7a7a\u767d\uff0c\u5c07\u6703\u641c\u7d22\u6240\u6709\u53ef\u7528\u88dd\u7f6e\u3002" } diff --git a/homeassistant/components/ws66i/translations/he.json b/homeassistant/components/ws66i/translations/he.json new file mode 100644 index 00000000000..fa770b28bf4 --- /dev/null +++ b/homeassistant/components/ws66i/translations/he.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4" + }, + "error": { + "cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4", + "unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4" + }, + "step": { + "user": { + "data": { + "ip_address": "\u05db\u05ea\u05d5\u05d1\u05ea IP" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_aqara/translations/ca.json b/homeassistant/components/xiaomi_aqara/translations/ca.json index 78f5affd556..fe0e0d7c096 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ca.json +++ b/homeassistant/components/xiaomi_aqara/translations/ca.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Adre\u00e7a IP" }, - "description": "Selecciona la passarel\u00b7la Xiaomi Aqara a la qual connectar-te", - "title": "Selecciona la passarel\u00b7la Xiaomi Aqara a la qual connectar-te" + "description": "Selecciona la passarel\u00b7la Xiaomi Aqara a la qual connectar-te" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Interf\u00edcie de xarxa a utilitzar", "mac": "Adre\u00e7a MAC (opcional)" }, - "description": "Si les adreces IP i MAC es deixen buides s'utilitzar\u00e0 el descobriment autom\u00e0tic", - "title": "Passarel\u00b7la Xiaomi Aqara" + "description": "Si les adreces IP i MAC es deixen buides s'utilitzar\u00e0 el descobriment autom\u00e0tic" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/cs.json b/homeassistant/components/xiaomi_aqara/translations/cs.json index b344be8c647..f2fd489987f 100644 --- a/homeassistant/components/xiaomi_aqara/translations/cs.json +++ b/homeassistant/components/xiaomi_aqara/translations/cs.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP adresa" }, - "description": "Chcete-li p\u0159ipojit dal\u0161\u00ed br\u00e1ny, spus\u0165te znovu instalaci", - "title": "Vyberte br\u00e1nu Xiaomi Aqara, kterou chcete p\u0159ipojit" + "description": "Chcete-li p\u0159ipojit dal\u0161\u00ed br\u00e1ny, spus\u0165te znovu instalaci" }, "settings": { "data": { @@ -34,8 +33,7 @@ "host": "IP adresa (voliteln\u011b)", "mac": "MAC adresa (voliteln\u00e1)" }, - "description": "P\u0159ipojte se k va\u0161\u00ed br\u00e1n\u011b Xiaomi Aqara - pokud z\u016fstanou pr\u00e1zdn\u00e9 adresy IP a MAC, pou\u017eije se automatick\u00e9 zji\u0161\u0165ov\u00e1n\u00ed", - "title": "Br\u00e1na Xiaomi Aqara" + "description": "P\u0159ipojte se k va\u0161\u00ed br\u00e1n\u011b Xiaomi Aqara - pokud z\u016fstanou pr\u00e1zdn\u00e9 adresy IP a MAC, pou\u017eije se automatick\u00e9 zji\u0161\u0165ov\u00e1n\u00ed" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/de.json b/homeassistant/components/xiaomi_aqara/translations/de.json index 170442fc856..f9e5ea39abe 100644 --- a/homeassistant/components/xiaomi_aqara/translations/de.json +++ b/homeassistant/components/xiaomi_aqara/translations/de.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP-Adresse" }, - "description": "W\u00e4hle das Xiaomi Aqara Gateway, das du verbinden m\u00f6chtest", - "title": "W\u00e4hle das Xiaomi Aqara Gateway, das du verbinden m\u00f6chtest" + "description": "W\u00e4hle das Xiaomi Aqara Gateway, das du verbinden m\u00f6chtest" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Die zu verwendende Netzwerkschnittstelle", "mac": "MAC-Adresse (optional)" }, - "description": "Wenn die IP- und MAC-Adressen leer gelassen werden, wird die automatische Erkennung verwendet", - "title": "Xiaomi Aqara Gateway" + "description": "Wenn die IP- und MAC-Adressen leer gelassen werden, wird die automatische Erkennung verwendet" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/el.json b/homeassistant/components/xiaomi_aqara/translations/el.json index 83923b660c2..8a2f85be99d 100644 --- a/homeassistant/components/xiaomi_aqara/translations/el.json +++ b/homeassistant/components/xiaomi_aqara/translations/el.json @@ -18,8 +18,7 @@ "data": { "select_ip": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP" }, - "description": "\u0395\u03ba\u03c4\u03b5\u03bb\u03ad\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03b5\u03c0\u03b9\u03c0\u03bb\u03ad\u03bf\u03bd \u03c0\u03cd\u03bb\u03b5\u03c2", - "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7 Xiaomi Aqara \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5" + "description": "\u0395\u03ba\u03c4\u03b5\u03bb\u03ad\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac \u03c4\u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03b5\u03ac\u03bd \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03b5\u03c4\u03b5 \u03b5\u03c0\u03b9\u03c0\u03bb\u03ad\u03bf\u03bd \u03c0\u03cd\u03bb\u03b5\u03c2" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u0397 \u03b4\u03b9\u03b5\u03c0\u03b1\u03c6\u03ae \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5 \u03c0\u03c1\u03bf\u03c2 \u03c7\u03c1\u03ae\u03c3\u03b7", "mac": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 Mac (\u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)" }, - "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7 Xiaomi Aqara Gateway, \u03b5\u03ac\u03bd \u03bf\u03b9 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 IP \u03ba\u03b1\u03b9 MAC \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03bd\u03bf\u03c5\u03bd \u03ba\u03b5\u03bd\u03ad\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7.", - "title": "\u03a0\u03cd\u03bb\u03b7 Xiaomi Aqara" + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03b7\u03bd \u03c0\u03cd\u03bb\u03b7 Xiaomi Aqara Gateway, \u03b5\u03ac\u03bd \u03bf\u03b9 \u03b4\u03b9\u03b5\u03c5\u03b8\u03cd\u03bd\u03c3\u03b5\u03b9\u03c2 IP \u03ba\u03b1\u03b9 MAC \u03c0\u03b1\u03c1\u03b1\u03bc\u03ad\u03bd\u03bf\u03c5\u03bd \u03ba\u03b5\u03bd\u03ad\u03c2, \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b1\u03bd\u03b1\u03ba\u03ac\u03bb\u03c5\u03c8\u03b7." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/en.json b/homeassistant/components/xiaomi_aqara/translations/en.json index 72949820bc0..ab6da1c7bfa 100644 --- a/homeassistant/components/xiaomi_aqara/translations/en.json +++ b/homeassistant/components/xiaomi_aqara/translations/en.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP Address" }, - "description": "Select the Xiaomi Aqara Gateway that you wish to connect", - "title": "Select the Xiaomi Aqara Gateway that you wish to connect" + "description": "Select the Xiaomi Aqara Gateway that you wish to connect" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "The network interface to use", "mac": "Mac Address (optional)" }, - "description": "If the IP and MAC addresses are left empty, auto-discovery is used", - "title": "Xiaomi Aqara Gateway" + "description": "If the IP and MAC addresses are left empty, auto-discovery is used" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/es.json b/homeassistant/components/xiaomi_aqara/translations/es.json index 671797b914f..e06806bbd9a 100644 --- a/homeassistant/components/xiaomi_aqara/translations/es.json +++ b/homeassistant/components/xiaomi_aqara/translations/es.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Direcci\u00f3n IP" }, - "description": "Ejecuta la configuraci\u00f3n de nuevo si deseas conectar gateways adicionales", - "title": "Selecciona el Xiaomi Aqara Gateway que quieres conectar" + "description": "Ejecuta la configuraci\u00f3n de nuevo si deseas conectar gateways adicionales" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "La interfaz de la red a usar", "mac": "Direcci\u00f3n Mac (opcional)" }, - "description": "Con\u00e9ctate a tu Xiaomi Aqara Gateway, si las direcciones IP y mac se dejan vac\u00edas, se utiliza el auto-descubrimiento.", - "title": "Xiaomi Aqara Gateway" + "description": "Con\u00e9ctate a tu Xiaomi Aqara Gateway, si las direcciones IP y mac se dejan vac\u00edas, se utiliza el auto-descubrimiento." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/et.json b/homeassistant/components/xiaomi_aqara/translations/et.json index 087c59dd2e2..90702af0a7c 100644 --- a/homeassistant/components/xiaomi_aqara/translations/et.json +++ b/homeassistant/components/xiaomi_aqara/translations/et.json @@ -18,8 +18,7 @@ "data": { "select_ip": "L\u00fc\u00fcsi IP aadress" }, - "description": "Vali Xiaomi Aqara Gateway mida soovid \u00fchendada.", - "title": "Vali Xiaomi Aqara l\u00fc\u00fcs mida soovid \u00fchendada" + "description": "Vali Xiaomi Aqara Gateway mida soovid \u00fchendada." }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Kasutatav v\u00f5rguliides", "mac": "MAC aadress (valikuline)" }, - "description": "Kui IP- ja MAC-aadressid j\u00e4etakse t\u00fchjaks, kasutatakse automaatset avastamist", - "title": "" + "description": "Kui IP- ja MAC-aadressid j\u00e4etakse t\u00fchjaks, kasutatakse automaatset avastamist" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/fr.json b/homeassistant/components/xiaomi_aqara/translations/fr.json index ab042cb5f0e..3c5544cfff3 100644 --- a/homeassistant/components/xiaomi_aqara/translations/fr.json +++ b/homeassistant/components/xiaomi_aqara/translations/fr.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Adresse IP" }, - "description": "S\u00e9lectionnez la passerelle Xiaomi Aqara que vous souhaitez connecter", - "title": "S\u00e9lectionnez la passerelle Xiaomi Aqara que vous souhaitez connecter" + "description": "S\u00e9lectionnez la passerelle Xiaomi Aqara que vous souhaitez connecter" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Interface r\u00e9seau \u00e0 utiliser", "mac": "Adresse MAC (facultatif)" }, - "description": "Si les adresses IP et MAC sont laiss\u00e9es vides, la d\u00e9couverte automatique sera utilis\u00e9e", - "title": "Passerelle Xiaomi Aqara" + "description": "Si les adresses IP et MAC sont laiss\u00e9es vides, la d\u00e9couverte automatique sera utilis\u00e9e" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/he.json b/homeassistant/components/xiaomi_aqara/translations/he.json index ea6cd8e278b..8d123517560 100644 --- a/homeassistant/components/xiaomi_aqara/translations/he.json +++ b/homeassistant/components/xiaomi_aqara/translations/he.json @@ -18,8 +18,7 @@ "data": { "select_ip": "\u05db\u05ea\u05d5\u05d1\u05ea IP" }, - "description": "\u05d9\u05e9 \u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05d5\u05d1 \u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d7\u05d1\u05e8 \u05e9\u05e2\u05e8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd", - "title": "\u05d1\u05d7\u05d9\u05e8\u05ea \u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05d0\u05e7\u05d0\u05e8\u05d4 \u05e9\u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d7\u05d1\u05e8" + "description": "\u05d9\u05e9 \u05dc\u05d4\u05e4\u05e2\u05d9\u05dc \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4 \u05e9\u05d5\u05d1 \u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d7\u05d1\u05e8 \u05e9\u05e2\u05e8\u05d9\u05dd \u05e0\u05d5\u05e1\u05e4\u05d9\u05dd" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u05de\u05de\u05e9\u05e7 \u05d4\u05e8\u05e9\u05ea \u05d1\u05d5 \u05d9\u05e9 \u05dc\u05d4\u05e9\u05ea\u05de\u05e9", "mac": "\u05db\u05ea\u05d5\u05d1\u05ea Mac (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)" }, - "description": "\u05d0\u05dd \u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d4-IP \u05d5\u05d4-MAC \u05e0\u05d5\u05ea\u05e8\u05d5\u05ea \u05e8\u05d9\u05e7\u05d5\u05ea, \u05e0\u05e2\u05e9\u05d4 \u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9", - "title": "\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05d0\u05e7\u05d0\u05e8\u05d4" + "description": "\u05d0\u05dd \u05db\u05ea\u05d5\u05d1\u05d5\u05ea \u05d4-IP \u05d5\u05d4-MAC \u05e0\u05d5\u05ea\u05e8\u05d5\u05ea \u05e8\u05d9\u05e7\u05d5\u05ea, \u05e0\u05e2\u05e9\u05d4 \u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05d2\u05d9\u05dc\u05d5\u05d9 \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/hu.json b/homeassistant/components/xiaomi_aqara/translations/hu.json index f0a1b076750..e3319baa594 100644 --- a/homeassistant/components/xiaomi_aqara/translations/hu.json +++ b/homeassistant/components/xiaomi_aqara/translations/hu.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP c\u00edm" }, - "description": "V\u00e1lassza ki a csatlakoztatni k\u00edv\u00e1nt Xiaomi Aqara K\u00f6zponti egys\u00e9get", - "title": "V\u00e1lassza ki a csatlakoztatni k\u00edv\u00e1nt Xiaomi Aqara K\u00f6zponti egys\u00e9get" + "description": "V\u00e1lassza ki a csatlakoztatni k\u00edv\u00e1nt Xiaomi Aqara K\u00f6zponti egys\u00e9get" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "A haszn\u00e1lni k\u00edv\u00e1nt h\u00e1l\u00f3zati interf\u00e9sz", "mac": "Mac-c\u00edm (opcion\u00e1lis)" }, - "description": "Ha az IP- \u00e9s MAC-c\u00edm mez\u0151ket \u00fcresen hagyja, akkor az automatikus felder\u00edt\u00e9s ker\u00fcl alkalmaz\u00e1sra.", - "title": "Xiaomi Aqara k\u00f6zponti egys\u00e9g" + "description": "Ha az IP- \u00e9s MAC-c\u00edm mez\u0151ket \u00fcresen hagyja, akkor az automatikus felder\u00edt\u00e9s ker\u00fcl alkalmaz\u00e1sra." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/id.json b/homeassistant/components/xiaomi_aqara/translations/id.json index 2b33af8237f..746815075b4 100644 --- a/homeassistant/components/xiaomi_aqara/translations/id.json +++ b/homeassistant/components/xiaomi_aqara/translations/id.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Alamat IP" }, - "description": "Pilih Gateway Xiaomi Aqara yang ingin disambungkan", - "title": "Pilih Gateway Xiaomi Aqara yang ingin dihubungkan" + "description": "Pilih Gateway Xiaomi Aqara yang ingin disambungkan" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Antarmuka jaringan yang akan digunakan", "mac": "Alamat MAC (opsional)" }, - "description": "Jika alamat IP dan MAC dikosongkan, penemuan otomatis digunakan", - "title": "Xiaomi Aqara Gateway" + "description": "Jika alamat IP dan MAC dikosongkan, penemuan otomatis digunakan" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/it.json b/homeassistant/components/xiaomi_aqara/translations/it.json index ac5eff78190..2ea75af7f4a 100644 --- a/homeassistant/components/xiaomi_aqara/translations/it.json +++ b/homeassistant/components/xiaomi_aqara/translations/it.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Indirizzo IP" }, - "description": "Seleziona lo Xiaomi Aqara Gateway che desideri connettere", - "title": "Seleziona il Gateway Xiaomi Aqara che si desidera collegare" + "description": "Seleziona lo Xiaomi Aqara Gateway che desideri connettere" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "L'interfaccia di rete da utilizzare", "mac": "Indirizzo Mac (opzionale)" }, - "description": "Se gli indirizzi IP e MAC vengono lasciati vuoti, viene utilizzato il rilevamento automatico", - "title": "Xiaomi Aqara Gateway" + "description": "Se gli indirizzi IP e MAC vengono lasciati vuoti, viene utilizzato il rilevamento automatico" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/ja.json b/homeassistant/components/xiaomi_aqara/translations/ja.json index 65e1870efe2..becb37c00f0 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ja.json +++ b/homeassistant/components/xiaomi_aqara/translations/ja.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP\u30a2\u30c9\u30ec\u30b9" }, - "description": "\u8ffd\u52a0\u306e\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u63a5\u7d9a\u3059\u308b\u5834\u5408\u306f\u3001\u518d\u5ea6\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "\u63a5\u7d9a\u3057\u305f\u3044Xiaomi Aqara Gateway\u3092\u9078\u629e\u3057\u307e\u3059\u3002" + "description": "\u8ffd\u52a0\u306e\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u63a5\u7d9a\u3059\u308b\u5834\u5408\u306f\u3001\u518d\u5ea6\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u4f7f\u7528\u3059\u308b\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30a4\u30b9", "mac": "Mac\u30a2\u30c9\u30ec\u30b9 (\u30aa\u30d7\u30b7\u30e7\u30f3)" }, - "description": "Xiaomi Aqara Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u3068MAC\u30a2\u30c9\u30ec\u30b9\u304c\u7a7a\u767d\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059", - "title": "Xiaomi Aqara Gateway" + "description": "Xiaomi Aqara Gateway\u306b\u63a5\u7d9a\u3057\u307e\u3059\u3002IP\u30a2\u30c9\u30ec\u30b9\u3068MAC\u30a2\u30c9\u30ec\u30b9\u304c\u7a7a\u767d\u306e\u307e\u307e\u306e\u5834\u5408\u3001\u81ea\u52d5\u691c\u51fa\u304c\u4f7f\u7528\u3055\u308c\u307e\u3059" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/ko.json b/homeassistant/components/xiaomi_aqara/translations/ko.json index dd8a9ae5ede..1538f2adcb5 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ko.json +++ b/homeassistant/components/xiaomi_aqara/translations/ko.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP \uc8fc\uc18c" }, - "description": "\uac8c\uc774\ud2b8\uc6e8\uc774\ub97c \ucd94\uac00 \uc5f0\uacb0\ud558\ub824\uba74 \uc124\uc815\uc744 \ub2e4\uc2dc \uc2e4\ud589\ud574\uc8fc\uc138\uc694", - "title": "\uc5f0\uacb0\ud560 Xiaomi Aqara \uac8c\uc774\ud2b8\uc6e8\uc774 \uc120\ud0dd\ud558\uae30" + "description": "\uac8c\uc774\ud2b8\uc6e8\uc774\ub97c \ucd94\uac00 \uc5f0\uacb0\ud558\ub824\uba74 \uc124\uc815\uc744 \ub2e4\uc2dc \uc2e4\ud589\ud574\uc8fc\uc138\uc694" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\uc0ac\uc6a9\ud560 \ub124\ud2b8\uc6cc\ud06c \uc778\ud130\ud398\uc774\uc2a4", "mac": "Mac \uc8fc\uc18c (\uc120\ud0dd \uc0ac\ud56d)" }, - "description": "Xiaomi Aqara \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c \ubc0f MAC \uc8fc\uc18c\ub97c \ube44\uc6cc\ub450\uba74 \uc790\ub3d9 \uac80\uc0c9\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4", - "title": "Xiaomi Aqara \uac8c\uc774\ud2b8\uc6e8\uc774" + "description": "Xiaomi Aqara \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud569\ub2c8\ub2e4. IP \uc8fc\uc18c \ubc0f MAC \uc8fc\uc18c\ub97c \ube44\uc6cc\ub450\uba74 \uc790\ub3d9 \uac80\uc0c9\uc774 \uc0ac\uc6a9\ub429\ub2c8\ub2e4" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/lb.json b/homeassistant/components/xiaomi_aqara/translations/lb.json index 1d3c5f6ed76..3bb703ffb34 100644 --- a/homeassistant/components/xiaomi_aqara/translations/lb.json +++ b/homeassistant/components/xiaomi_aqara/translations/lb.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP Adresse" }, - "description": "Setup nach emol ausf\u00e9ieren fir eng zous\u00e4tzlech Gateway dob\u00e4i ze setzen", - "title": "Xiaomi Aqara Gateway auswielen mat der sech soll verbonne ginn" + "description": "Setup nach emol ausf\u00e9ieren fir eng zous\u00e4tzlech Gateway dob\u00e4i ze setzen" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Netzwierk Interface dee soll benotzt ginn", "mac": "Mac Adress (Optionell)" }, - "description": "Mat denger Xiaomi Aqara Gateway verbannen, falls keng IP oder MAC Adress uginn ass g\u00ebtt auto-discovery benotzt", - "title": "Xiaomi Aqara Gateway" + "description": "Mat denger Xiaomi Aqara Gateway verbannen, falls keng IP oder MAC Adress uginn ass g\u00ebtt auto-discovery benotzt" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/nl.json b/homeassistant/components/xiaomi_aqara/translations/nl.json index c2042850fdb..a549d01313b 100644 --- a/homeassistant/components/xiaomi_aqara/translations/nl.json +++ b/homeassistant/components/xiaomi_aqara/translations/nl.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP-adres" }, - "description": "Selecteer de Xiaomi Aqara Gateway die u wilt verbinden", - "title": "Selecteer de Xiaomi Aqara Gateway waarmee u verbinding wilt maken" + "description": "Selecteer de Xiaomi Aqara Gateway die u wilt verbinden" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "De netwerkinterface die moet worden gebruikt", "mac": "MAC-adres (optioneel)" }, - "description": "Als de IP- en MAC-adressen leeg worden gelaten, wordt auto-discovery gebruikt", - "title": "Xiaomi Aqara Gateway" + "description": "Als de IP- en MAC-adressen leeg worden gelaten, wordt auto-discovery gebruikt" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/no.json b/homeassistant/components/xiaomi_aqara/translations/no.json index c325886dd22..8cb351dc7e2 100644 --- a/homeassistant/components/xiaomi_aqara/translations/no.json +++ b/homeassistant/components/xiaomi_aqara/translations/no.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP adresse" }, - "description": "Velg Xiaomi Aqara Gateway som du \u00f8nsker \u00e5 koble til", - "title": "Velg Xiaomi Aqara Gateway som du \u00f8nsker \u00e5 koble til" + "description": "Velg Xiaomi Aqara Gateway som du \u00f8nsker \u00e5 koble til" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Nettverksgrensesnittet som skal brukes", "mac": "MAC-adresse (valgfritt)" }, - "description": "Hvis IP- og MAC-adressene er tomme, brukes automatisk oppdagelse", - "title": "" + "description": "Hvis IP- og MAC-adressene er tomme, brukes automatisk oppdagelse" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/pl.json b/homeassistant/components/xiaomi_aqara/translations/pl.json index e680415320c..dc7391166f5 100644 --- a/homeassistant/components/xiaomi_aqara/translations/pl.json +++ b/homeassistant/components/xiaomi_aqara/translations/pl.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Adres IP" }, - "description": "Wybierz bramk\u0119 Xiaomi Aqara, z kt\u00f3r\u0105 chcesz si\u0119 po\u0142\u0105czy\u0107", - "title": "Wybierz bramk\u0119 Xiaomi Aqara, z kt\u00f3r\u0105 chcesz si\u0119 po\u0142\u0105czy\u0107" + "description": "Wybierz bramk\u0119 Xiaomi Aqara, z kt\u00f3r\u0105 chcesz si\u0119 po\u0142\u0105czy\u0107" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Interfejs sieciowy", "mac": "Adres MAC (opcjonalnie)" }, - "description": "Je\u015bli zostawisz Adres IP oraz MAC puste to bramka zostanie automatycznie wykryta.", - "title": "Bramka Xiaomi Aqara" + "description": "Je\u015bli zostawisz Adres IP oraz MAC puste to bramka zostanie automatycznie wykryta." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/pt-BR.json b/homeassistant/components/xiaomi_aqara/translations/pt-BR.json index 62deb3ba97c..f83050acfc7 100644 --- a/homeassistant/components/xiaomi_aqara/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_aqara/translations/pt-BR.json @@ -18,8 +18,7 @@ "data": { "select_ip": "Endere\u00e7o IP" }, - "description": "Selecione o Xiaomi Aqara Gateway que voc\u00ea deseja conectar", - "title": "Selecione o Xiaomi Aqara Gateway que voc\u00ea deseja conectar" + "description": "Selecione o Xiaomi Aqara Gateway que voc\u00ea deseja conectar" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "A interface de rede a ser usada", "mac": "Endere\u00e7o Mac (opcional)" }, - "description": "Se os endere\u00e7os IP e MAC forem deixados vazios, a descoberta autom\u00e1tica \u00e9 usada", - "title": "Gateway Xiaomi Aqara" + "description": "Se os endere\u00e7os IP e MAC forem deixados vazios, a descoberta autom\u00e1tica \u00e9 usada" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/ru.json b/homeassistant/components/xiaomi_aqara/translations/ru.json index 499837889df..3483bf2d3f8 100644 --- a/homeassistant/components/xiaomi_aqara/translations/ru.json +++ b/homeassistant/components/xiaomi_aqara/translations/ru.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP-\u0430\u0434\u0440\u0435\u0441" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 Xiaomi Aqara.", - "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 Xiaomi Aqara" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0448\u043b\u044e\u0437 Xiaomi Aqara." }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u0421\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441", "mac": "MAC-\u0430\u0434\u0440\u0435\u0441 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" }, - "description": "\u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0438 MAC \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043f\u0443\u0441\u0442\u044b\u043c\u0438.", - "title": "\u0428\u043b\u044e\u0437 Xiaomi Aqara" + "description": "\u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0448\u043b\u044e\u0437\u0430, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0438 MAC \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u043f\u0443\u0441\u0442\u044b\u043c\u0438." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/tr.json b/homeassistant/components/xiaomi_aqara/translations/tr.json index 0c961a34a3c..0d8b78fc3a8 100644 --- a/homeassistant/components/xiaomi_aqara/translations/tr.json +++ b/homeassistant/components/xiaomi_aqara/translations/tr.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP Adresi" }, - "description": "Ba\u011flamak istedi\u011finiz Xiaomi Aqara A\u011f Ge\u00e7idini se\u00e7in", - "title": "Ba\u011flamak istedi\u011finiz Xiaomi Aqara A\u011f Ge\u00e7idini se\u00e7in" + "description": "Ba\u011flamak istedi\u011finiz Xiaomi Aqara A\u011f Ge\u00e7idini se\u00e7in" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "Kullan\u0131lacak a\u011f aray\u00fcz\u00fc", "mac": "Mac Adresi (iste\u011fe ba\u011fl\u0131)" }, - "description": "IP ve MAC adresleri bo\u015f b\u0131rak\u0131l\u0131rsa otomatik ke\u015fif kullan\u0131l\u0131r", - "title": "Xiaomi Aqara A\u011f Ge\u00e7idi" + "description": "IP ve MAC adresleri bo\u015f b\u0131rak\u0131l\u0131rsa otomatik ke\u015fif kullan\u0131l\u0131r" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/uk.json b/homeassistant/components/xiaomi_aqara/translations/uk.json index 1598e96b38e..7c598b29fc7 100644 --- a/homeassistant/components/xiaomi_aqara/translations/uk.json +++ b/homeassistant/components/xiaomi_aqara/translations/uk.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP-\u0430\u0434\u0440\u0435\u0441\u0430" }, - "description": "\u041f\u043e\u0447\u043d\u0456\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u044e\u0432\u0430\u043d\u043d\u044f \u0437\u043d\u043e\u0432\u0443, \u044f\u043a\u0449\u043e \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0434\u043e\u0434\u0430\u0442\u0438 \u0456\u043d\u0448\u0438\u0439 \u0448\u043b\u044e\u0437", - "title": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0448\u043b\u044e\u0437 Xiaomi Aqara" + "description": "\u041f\u043e\u0447\u043d\u0456\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u044e\u0432\u0430\u043d\u043d\u044f \u0437\u043d\u043e\u0432\u0443, \u044f\u043a\u0449\u043e \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e \u0434\u043e\u0434\u0430\u0442\u0438 \u0456\u043d\u0448\u0438\u0439 \u0448\u043b\u044e\u0437" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u041c\u0435\u0440\u0435\u0436\u0435\u0432\u0438\u0439 \u0456\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441", "mac": "MAC-\u0430\u0434\u0440\u0435\u0441\u0430 (\u043d\u0435\u043e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u043e)" }, - "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437\u0456 \u0448\u043b\u044e\u0437\u043e\u043c Xiaomi Aqara. \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f \u0448\u043b\u044e\u0437\u0443, \u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0456 MAC-\u0430\u0434\u0440\u0435\u0441\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c\u0438.", - "title": "\u0428\u043b\u044e\u0437 Xiaomi Aqara" + "description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 \u0437\u0456 \u0448\u043b\u044e\u0437\u043e\u043c Xiaomi Aqara. \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u0438\u044f\u0432\u043b\u0435\u043d\u043d\u044f \u0448\u043b\u044e\u0437\u0443, \u0437\u0430\u043b\u0438\u0448\u0442\u0435 \u043f\u043e\u043b\u044f IP \u0456 MAC-\u0430\u0434\u0440\u0435\u0441\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456\u043c\u0438." } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/zh-Hans.json b/homeassistant/components/xiaomi_aqara/translations/zh-Hans.json index a0d0159e71c..1532e2eb05f 100644 --- a/homeassistant/components/xiaomi_aqara/translations/zh-Hans.json +++ b/homeassistant/components/xiaomi_aqara/translations/zh-Hans.json @@ -18,8 +18,7 @@ "data": { "select_ip": "\u7f51\u5173 IP" }, - "description": "\u5982\u679c\u8981\u8fde\u63a5\u5176\u4ed6\u7f51\u5173\uff0c\u8bf7\u518d\u6b21\u8fd0\u884c\u914d\u7f6e\u7a0b\u5e8f", - "title": "\u9009\u62e9\u8981\u8fde\u63a5\u7684\u5c0f\u7c73 Aqara \u7f51\u5173" + "description": "\u5982\u679c\u8981\u8fde\u63a5\u5176\u4ed6\u7f51\u5173\uff0c\u8bf7\u518d\u6b21\u8fd0\u884c\u914d\u7f6e\u7a0b\u5e8f" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u8981\u4f7f\u7528\u7684\u7f51\u7edc\u63a5\u53e3", "mac": "MAC \u5730\u5740 (\u53ef\u9009)" }, - "description": "\u8fde\u63a5\u5230\u60a8\u7684\u5c0f\u7c73 Aqara \u7f51\u5173\uff0c\u5982\u679c IP \u548c MAC \u5730\u5740\u7559\u7a7a\uff0c\u5219\u4f7f\u7528\u81ea\u52a8\u53d1\u73b0", - "title": "\u5c0f\u7c73 Aqara \u7f51\u5173" + "description": "\u8fde\u63a5\u5230\u60a8\u7684\u5c0f\u7c73 Aqara \u7f51\u5173\uff0c\u5982\u679c IP \u548c MAC \u5730\u5740\u7559\u7a7a\uff0c\u5219\u4f7f\u7528\u81ea\u52a8\u53d1\u73b0" } } } diff --git a/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json b/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json index 5ffe34d5d39..d70d060efa4 100644 --- a/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_aqara/translations/zh-Hant.json @@ -18,8 +18,7 @@ "data": { "select_ip": "IP \u4f4d\u5740" }, - "description": "\u5982\u679c\u6240\u8981\u9023\u7dda\u7684\u5c0f\u7c73 Aqara \u7db2\u95dc", - "title": "\u9078\u64c7\u6240\u8981\u9023\u7dda\u7684\u5c0f\u7c73 Aqara \u7db2\u95dc" + "description": "\u5982\u679c\u6240\u8981\u9023\u7dda\u7684\u5c0f\u7c73 Aqara \u7db2\u95dc" }, "settings": { "data": { @@ -35,8 +34,7 @@ "interface": "\u4f7f\u7528\u7684\u7db2\u8def\u4ecb\u9762", "mac": "Mac \u4f4d\u5740\uff08\u9078\u9805\uff09" }, - "description": "\u5047\u5982 IP \u6216 Mac \u4f4d\u5740\u70ba\u7a7a\u767d\u3001\u5c07\u9032\u884c\u81ea\u52d5\u641c\u7d22", - "title": "\u5c0f\u7c73 Aqara \u7db2\u95dc" + "description": "\u5047\u5982 IP \u6216 Mac \u4f4d\u5740\u70ba\u7a7a\u767d\u3001\u5c07\u9032\u884c\u81ea\u52d5\u641c\u7d22" } } } diff --git a/homeassistant/components/xiaomi_miio/translations/bg.json b/homeassistant/components/xiaomi_miio/translations/bg.json index a73df3c5609..2339d5bc830 100644 --- a/homeassistant/components/xiaomi_miio/translations/bg.json +++ b/homeassistant/components/xiaomi_miio/translations/bg.json @@ -5,8 +5,7 @@ "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e" }, "error": { - "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435", - "no_device_selected": "\u041d\u0435 \u0435 \u0438\u0437\u0431\u0440\u0430\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u043c\u043e\u043b\u044f, \u0438\u0437\u0431\u0435\u0440\u0435\u0442\u0435 \u0435\u0434\u043d\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e." + "cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435" }, "flow_title": "{name}", "step": { @@ -20,17 +19,6 @@ "model": "\u041c\u043e\u0434\u0435\u043b \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e" } }, - "device": { - "data": { - "host": "IP \u0430\u0434\u0440\u0435\u0441" - } - }, - "gateway": { - "data": { - "host": "IP \u0430\u0434\u0440\u0435\u0441", - "name": "\u0418\u043c\u0435 \u043d\u0430 \u0448\u043b\u044e\u0437\u0430" - } - }, "manual": { "data": { "host": "IP \u0430\u0434\u0440\u0435\u0441" diff --git a/homeassistant/components/xiaomi_miio/translations/ca.json b/homeassistant/components/xiaomi_miio/translations/ca.json index cb36fbbb2e0..614238a54b4 100644 --- a/homeassistant/components/xiaomi_miio/translations/ca.json +++ b/homeassistant/components/xiaomi_miio/translations/ca.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Credencials del n\u00favol incompletes, introdueix el nom d'usuari, la contrasenya i el pa\u00eds", "cloud_login_error": "No s'ha pogut iniciar sessi\u00f3 a Xiaomi Miio Cloud, comprova les credencials.", "cloud_no_devices": "No s'han trobat dispositius en aquest compte al n\u00favol de Xiaomi Miio.", - "no_device_selected": "No hi ha cap dispositiu seleccionat, selecciona'n un.", "unknown_device": "No es reconeix el model del dispositiu, no es pot configurar el dispositiu mitjan\u00e7ant el flux de configuraci\u00f3.", "wrong_token": "Error de verificaci\u00f3, token erroni" }, @@ -25,42 +24,19 @@ "cloud_username": "Nom d'usuari del n\u00favol", "manual": "Configuraci\u00f3 manual (no recomanada)" }, - "description": "Inicia sessi\u00f3 al n\u00favol Xiaomi Miio, consulta https://www.openhab.org/addons/bindings/miio/#country-servers per obtenir el servidor al n\u00favol.", - "title": "Connexi\u00f3 amb un dispositiu Xiaomi Miio o una passarel\u00b7la Xiaomi" + "description": "Inicia sessi\u00f3 al n\u00favol Xiaomi Miio, consulta https://www.openhab.org/addons/bindings/miio/#country-servers per obtenir el servidor al n\u00favol." }, "connect": { "data": { "model": "Model de dispositiu" - }, - "description": "Selecciona manualment el model de dispositiu entre els models compatibles.", - "title": "Connexi\u00f3 amb un dispositiu Xiaomi Miio o una passarel\u00b7la Xiaomi" - }, - "device": { - "data": { - "host": "Adre\u00e7a IP", - "model": "Model del dispositiu (opcional)", - "name": "Nom del dispositiu", - "token": "Token d'API" - }, - "description": "Necessitar\u00e0s el Token d'API de 32 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Tingues en compte que aquest Token d'API \u00e9s diferent a la clau utilitzada per la integraci\u00f3 Xiaomi Aqara.", - "title": "Connexi\u00f3 amb un dispositiu Xiaomi Miio o una passarel\u00b7la de Xiaomi" - }, - "gateway": { - "data": { - "host": "Adre\u00e7a IP", - "name": "Nom de la passarel\u00b7la", - "token": "Token d'API" - }, - "description": "Necessitar\u00e0s el Token d'API de 32 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. Tingues en compte que aquest Token d'API \u00e9s diferent a la clau utilitzada per la integraci\u00f3 Xiaomi Aqara.", - "title": "Connexi\u00f3 amb la passarel\u00b7la de Xiaomi" + } }, "manual": { "data": { "host": "Adre\u00e7a IP", "token": "Token d'API" }, - "description": "Necessitar\u00e0s el Token d'API de 32 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/xiaomi_miio/#retrieving-the-access-token. Tingues en compte que aquest Token d'API \u00e9s diferent a la clau utilitzada per la integraci\u00f3 Xiaomi Aqara.", - "title": "Connexi\u00f3 amb un dispositiu Xiaomi Miio o una passarel\u00b7la Xiaomi" + "description": "Necessitar\u00e0s el Token d'API de 32 car\u00e0cters, consulta les instruccions a https://www.home-assistant.io/integrations/xiaomi_miio/#retrieving-the-access-token. Tingues en compte que aquest Token d'API \u00e9s diferent a la clau utilitzada per la integraci\u00f3 Xiaomi Aqara." }, "reauth_confirm": { "description": "La integraci\u00f3 Xiaomi Miio ha de tornar a autenticar-se amb el teu compte per poder actualitzar els tokens o afegir credencials pel n\u00favol.", @@ -70,15 +46,7 @@ "data": { "select_device": "Dispositiu Miio" }, - "description": "Selecciona el dispositiu Xiaomi Miio a configurar.", - "title": "Connexi\u00f3 amb un dispositiu Xiaomi Miio o una passarel\u00b7la Xiaomi" - }, - "user": { - "data": { - "gateway": "Connexi\u00f3 amb la passarel\u00b7la de Xiaomi" - }, - "description": "Selecciona a quin dispositiu vols connectar-te.", - "title": "Xiaomi Miio" + "description": "Selecciona el dispositiu Xiaomi Miio a configurar." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Utilitza el n\u00favol per obtenir subdispositius connectats" - }, - "description": "Especifica par\u00e0metres opcionals", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/cs.json b/homeassistant/components/xiaomi_miio/translations/cs.json index 69b6cd221ee..858546d915f 100644 --- a/homeassistant/components/xiaomi_miio/translations/cs.json +++ b/homeassistant/components/xiaomi_miio/translations/cs.json @@ -9,7 +9,6 @@ }, "error": { "cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit", - "no_device_selected": "Nebylo vybr\u00e1no \u017e\u00e1dn\u00e9 za\u0159\u00edzen\u00ed, vyberte jedno za\u0159\u00edzen\u00ed.", "unknown_device": "Model za\u0159\u00edzen\u00ed nen\u00ed zn\u00e1m, nastaven\u00ed za\u0159\u00edzen\u00ed nen\u00ed mo\u017en\u00e9 dokon\u010dit.", "wrong_token": "Chyba kontroln\u00edho sou\u010dtu, \u0161patn\u00fd token" }, @@ -18,42 +17,19 @@ "cloud": { "data": { "manual": "Nastavit ru\u010dn\u011b (nedoporu\u010deno)" - }, - "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed Xiaomi Miio nebo k br\u00e1n\u011b Xiaomi" + } }, "connect": { "data": { "model": "Model za\u0159\u00edzen\u00ed" - }, - "description": "Ru\u010dn\u011b vyberte model za\u0159\u00edzen\u00ed ze seznamu podporovan\u00fdch za\u0159\u00edzen\u00ed.", - "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed Xiaomi Miio nebo k br\u00e1n\u011b Xiaomi" - }, - "device": { - "data": { - "host": "IP adresa", - "model": "Model za\u0159\u00edzen\u00ed (voliteln\u00e9)", - "name": "Jm\u00e9no za\u0159\u00edzen\u00ed", - "token": "API token" - }, - "description": "Budete pot\u0159ebovat 32 znakov\u00fd API token, pokyny naleznete na https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Upozor\u0148ujeme, \u017ee tento token se li\u0161\u00ed od kl\u00ed\u010de pou\u017e\u00edvan\u00e9ho v integraci Xiaomi Aqara.", - "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed Xiaomi Miio nebo k br\u00e1n\u011b Xiaomi" - }, - "gateway": { - "data": { - "host": "IP adresa", - "name": "N\u00e1zev br\u00e1ny", - "token": "API token" - }, - "description": "Budete pot\u0159ebovat 32 znakov\u00fd API token, pokyny naleznete na https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. Upozor\u0148ujeme, \u017ee tento token se li\u0161\u00ed od kl\u00ed\u010de pou\u017e\u00edvan\u00e9ho v integraci Xiaomi Aqara.", - "title": "P\u0159ipojen\u00ed k br\u00e1n\u011b Xiaomi" + } }, "manual": { "data": { "host": "IP adresa", "token": "API token" }, - "description": "Budete pot\u0159ebovat 32 znakov\u00fd API token, pokyny naleznete na https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Upozor\u0148ujeme, \u017ee tento token se li\u0161\u00ed od kl\u00ed\u010de pou\u017e\u00edvan\u00e9ho v integraci Xiaomi Aqara.", - "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed Xiaomi Miio nebo k br\u00e1n\u011b Xiaomi" + "description": "Budete pot\u0159ebovat 32 znakov\u00fd API token, pokyny naleznete na https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Upozor\u0148ujeme, \u017ee tento token se li\u0161\u00ed od kl\u00ed\u010de pou\u017e\u00edvan\u00e9ho v integraci Xiaomi Aqara." }, "reauth_confirm": { "title": "Znovu ov\u011b\u0159it integraci" @@ -62,15 +38,7 @@ "data": { "select_device": "Za\u0159\u00edzen\u00ed Miio" }, - "description": "Vyberte Xiaomi Miio za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit.", - "title": "P\u0159ipojen\u00ed k za\u0159\u00edzen\u00ed Xiaomi Miio nebo k br\u00e1n\u011b Xiaomi" - }, - "user": { - "data": { - "gateway": "P\u0159ipojen\u00ed k br\u00e1n\u011b Xiaomi" - }, - "description": "Vyberte, ke kter\u00e9mu za\u0159\u00edzen\u00ed se chcete p\u0159ipojit.", - "title": "Xiaomi Miio" + "description": "Vyberte Xiaomi Miio za\u0159\u00edzen\u00ed, kter\u00e9 chcete nastavit." } } }, @@ -79,9 +47,7 @@ "init": { "data": { "cloud_subdevices": "Pou\u017e\u00edt cloud pro z\u00edsk\u00e1n\u00ed p\u0159ipojen\u00fdch podru\u017en\u00fdch za\u0159\u00edzen\u00ed" - }, - "description": "Zadejte voliteln\u00e9 nastaven\u00ed", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/de.json b/homeassistant/components/xiaomi_miio/translations/de.json index 291323f31bd..70a630f90f3 100644 --- a/homeassistant/components/xiaomi_miio/translations/de.json +++ b/homeassistant/components/xiaomi_miio/translations/de.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Cloud-Anmeldeinformationen unvollst\u00e4ndig, bitte Benutzernamen, Passwort und Land eingeben", "cloud_login_error": "Die Anmeldung bei Xiaomi Miio Cloud ist fehlgeschlagen, \u00fcberpr\u00fcfe die Anmeldedaten.", "cloud_no_devices": "Keine Ger\u00e4te in diesem Xiaomi Miio Cloud-Konto gefunden.", - "no_device_selected": "Kein Ger\u00e4t ausgew\u00e4hlt, bitte w\u00e4hle ein Ger\u00e4t aus.", "unknown_device": "Das Ger\u00e4temodell ist nicht bekannt und das Ger\u00e4t kann nicht mithilfe des Assistenten eingerichtet werden.", "wrong_token": "Pr\u00fcfsummenfehler, falscher Token" }, @@ -25,42 +24,19 @@ "cloud_username": "Cloud-Benutzername", "manual": "Manuell konfigurieren (nicht empfohlen)" }, - "description": "Melde dich bei der Xiaomi Miio Cloud an, siehe https://www.openhab.org/addons/bindings/miio/#country-servers f\u00fcr den zu verwendenden Cloud-Server.", - "title": "Verbinden mit einem Xiaomi Miio Ger\u00e4t oder Xiaomi Gateway" + "description": "Melde dich bei der Xiaomi Miio Cloud an, siehe https://www.openhab.org/addons/bindings/miio/#country-servers f\u00fcr den zu verwendenden Cloud-Server." }, "connect": { "data": { "model": "Ger\u00e4temodell" - }, - "description": "W\u00e4hle das Ger\u00e4temodell manuell aus den unterst\u00fctzten Modellen aus.", - "title": "Verbinden mit einem Xiaomi Miio Ger\u00e4t oder Xiaomi Gateway" - }, - "device": { - "data": { - "host": "IP-Adresse", - "model": "Ger\u00e4temodell (optional)", - "name": "Name des Ger\u00e4ts", - "token": "API-Token" - }, - "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token, siehe https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token f\u00fcr eine Anleitung. Dieser unterscheidet sich vom API-Token, den die Xiaomi Aqara-Integration nutzt.", - "title": "Herstellen einer Verbindung mit einem Xiaomi Miio-Ger\u00e4t oder Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "IP-Adresse", - "name": "Name des Gateways", - "token": "API-Token" - }, - "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token. Anweisungen findest du unter https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. Bitte beachte, dass sich dieser API-Token von dem Schl\u00fcssel unterscheidet, der von der Xiaomi Aqara Integration verwendet wird.", - "title": "Stelle eine Verbindung zu einem Xiaomi Gateway her" + } }, "manual": { "data": { "host": "IP-Adresse", "token": "API-Token" }, - "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token, siehe https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token f\u00fcr Anweisungen. Bitte beachte, dass sich dieser API-Token von dem Schl\u00fcssel unterscheidet, der von der Xiaomi Aqara-Integration verwendet wird.", - "title": "Verbinden mit einem Xiaomi Miio Ger\u00e4t oder Xiaomi Gateway" + "description": "Du ben\u00f6tigst den 32 Zeichen langen API-Token, siehe https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token f\u00fcr Anweisungen. Bitte beachte, dass sich dieser API-Token von dem Schl\u00fcssel unterscheidet, der von der Xiaomi Aqara-Integration verwendet wird." }, "reauth_confirm": { "description": "Die Xiaomi Miio-Integration muss dein Konto neu authentifizieren, um die Token zu aktualisieren oder fehlende Cloud-Anmeldedaten hinzuzuf\u00fcgen.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio-Ger\u00e4t" }, - "description": "W\u00e4hle das einzurichtende Xiaomi Miio-Ger\u00e4t aus.", - "title": "Verbinden mit einem Xiaomi Miio Ger\u00e4t oder Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Stelle eine Verbindung zu einem Xiaomi Gateway her" - }, - "description": "W\u00e4hle aus, mit welchem Ger\u00e4t du eine Verbindung herstellen m\u00f6chtest.", - "title": "Xiaomi Miio" + "description": "W\u00e4hle das einzurichtende Xiaomi Miio-Ger\u00e4t aus." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Cloud verwenden, um verbundene Subdevices zu erhalten" - }, - "description": "Optionale Einstellungen angeben", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/el.json b/homeassistant/components/xiaomi_miio/translations/el.json index abd099a404b..48f55af9246 100644 --- a/homeassistant/components/xiaomi_miio/translations/el.json +++ b/homeassistant/components/xiaomi_miio/translations/el.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u03a4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 Cloud \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03bb\u03bb\u03b9\u03c0\u03ae, \u03c3\u03c5\u03bc\u03c0\u03bb\u03b7\u03c1\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf \u03cc\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7, \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03ba\u03b1\u03b9 \u03c4\u03b7 \u03c7\u03ce\u03c1\u03b1", "cloud_login_error": "\u0394\u03b5\u03bd \u03ae\u03c4\u03b1\u03bd \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf Xiaomi Miio Cloud, \u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1.", "cloud_no_devices": "\u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b1\u03bd \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2 \u03c3\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc Xiaomi Miio cloud.", - "no_device_selected": "\u0394\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03b5\u03af \u03ba\u03b1\u03bc\u03af\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae, \u03c0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03bc\u03af\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae.", "unknown_device": "\u03a4\u03bf \u03bc\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b3\u03bd\u03c9\u03c3\u03c4\u03cc, \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03bc\u03b5 \u03c4\u03b7 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03c4\u03b7\u03c2 \u03c1\u03bf\u03ae\u03c2 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd.", "wrong_token": "\u03a3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03b1\u03b8\u03c1\u03bf\u03af\u03c3\u03bc\u03b1\u03c4\u03bf\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5, \u03bb\u03ac\u03b8\u03bf\u03c2 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc" }, @@ -25,42 +24,19 @@ "cloud_username": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 Cloud", "manual": "\u039c\u03b7 \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 (\u03b4\u03b5\u03bd \u03c3\u03c5\u03bd\u03b9\u03c3\u03c4\u03ac\u03c4\u03b1\u03b9)" }, - "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf cloud Xiaomi Miio, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.openhab.org/addons/bindings/miio/#country-servers \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae cloud \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03ae \u03c0\u03cd\u03bb\u03b7 Xiaomi" + "description": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03c4\u03bf cloud Xiaomi Miio, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.openhab.org/addons/bindings/miio/#country-servers \u03b3\u03b9\u03b1 \u03c4\u03bf\u03bd \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae cloud \u03c0\u03bf\u03c5 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c4\u03b5." }, "connect": { "data": { "model": "\u039c\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03b1 \u03c4\u03bf \u03bc\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 \u03b1\u03c0\u03cc \u03c4\u03b1 \u03c5\u03c0\u03bf\u03c3\u03c4\u03b7\u03c1\u03b9\u03b6\u03cc\u03bc\u03b5\u03bd\u03b1 \u03bc\u03bf\u03bd\u03c4\u03ad\u03bb\u03b1.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03ae \u03c0\u03cd\u03bb\u03b7 Xiaomi" - }, - "device": { - "data": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", - "model": "\u039c\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2 (\u03a0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc)", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2", - "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API" - }, - "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf\u03bd 32 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03b5\u03c2 \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2. \u039b\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c5\u03c0\u03cc\u03c8\u03b7 \u03cc\u03c4\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xiaomi Aqara.\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf\u03bd 32 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03b5\u03c2 \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2. \u039b\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c5\u03c0\u03cc\u03c8\u03b7 \u03cc\u03c4\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xiaomi Aqara.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03ae \u03c0\u03cd\u03bb\u03b7 Xiaomi" - }, - "gateway": { - "data": { - "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", - "name": "\u038c\u03bd\u03bf\u03bc\u03b1 \u03c4\u03b7\u03c2 \u03c0\u03cd\u03bb\u03b7\u03c2", - "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API" - }, - "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf\u03c5\u03c2 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03b5\u03c2 32 \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API , \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2. \u039b\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c5\u03c0\u03cc\u03c8\u03b7 \u03cc\u03c4\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xiaomi Aqara.", - "title": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 Xiaomi" + } }, "manual": { "data": { "host": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP", "token": "\u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API" }, - "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf 32 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2. \u039b\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c5\u03c0\u03cc\u03c8\u03b7 \u03cc\u03c4\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xiaomi Aqara.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03ae \u03c0\u03cd\u03bb\u03b7 Xiaomi" + "description": "\u0398\u03b1 \u03c7\u03c1\u03b5\u03b9\u03b1\u03c3\u03c4\u03b5\u03af\u03c4\u03b5 \u03c4\u03bf 32 \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API, \u03b1\u03bd\u03b1\u03c4\u03c1\u03ad\u03be\u03c4\u03b5 \u03c3\u03c4\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u03b3\u03b9\u03b1 \u03bf\u03b4\u03b7\u03b3\u03af\u03b5\u03c2. \u039b\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c5\u03c0\u03cc\u03c8\u03b7 \u03cc\u03c4\u03b9 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u0394\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03cc API \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03b9\u03b1\u03c6\u03bf\u03c1\u03b5\u03c4\u03b9\u03ba\u03cc \u03b1\u03c0\u03cc \u03c4\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af \u03c0\u03bf\u03c5 \u03c7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03b5\u03af\u03c4\u03b1\u03b9 \u03b1\u03c0\u03cc \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Xiaomi Aqara." }, "reauth_confirm": { "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 xiaomi Miio \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03cd\u03c3\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2 \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03b5\u03bd\u03b7\u03bc\u03b5\u03c1\u03ce\u03c3\u03b5\u03b9 \u03c4\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03ac \u03ae \u03bd\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c3\u03b5\u03b9 \u03b4\u03b9\u03b1\u03c0\u03b9\u03c3\u03c4\u03b5\u03c5\u03c4\u03ae\u03c1\u03b9\u03b1 cloud \u03c0\u03bf\u03c5 \u03bb\u03b5\u03af\u03c0\u03bf\u03c5\u03bd.", @@ -70,15 +46,7 @@ "data": { "select_device": "\u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Miio" }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7.", - "title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03b5 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03ae \u03c0\u03cd\u03bb\u03b7 Xiaomi" - }, - "user": { - "data": { - "gateway": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5 \u03c3\u03b5 \u03bc\u03b9\u03b1 \u03c0\u03cd\u03bb\u03b7 Xiaomi" - }, - "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c3\u03b5 \u03c0\u03bf\u03b9\u03b1 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5.", - "title": "Xiaomi Miio" + "description": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03c4\u03b7 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae Xiaomi Miio \u03b3\u03b9\u03b1 \u03c1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u03a7\u03c1\u03b7\u03c3\u03b9\u03bc\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03c4\u03b5 \u03c4\u03bf cloud \u03b3\u03b9\u03b1 \u03bd\u03b1 \u03bb\u03ac\u03b2\u03b5\u03c4\u03b5 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03b5\u03c2 \u03c5\u03c0\u03bf\u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ad\u03c2" - }, - "description": "\u039a\u03b1\u03b8\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03b1\u03b9\u03c1\u03b5\u03c4\u03b9\u03ba\u03ce\u03bd \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/en.json b/homeassistant/components/xiaomi_miio/translations/en.json index 8bd35f3e7d9..c37be0a7f74 100644 --- a/homeassistant/components/xiaomi_miio/translations/en.json +++ b/homeassistant/components/xiaomi_miio/translations/en.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Cloud credentials incomplete, please fill in username, password and country", "cloud_login_error": "Could not login to Xiaomi Miio Cloud, check the credentials.", "cloud_no_devices": "No devices found in this Xiaomi Miio cloud account.", - "no_device_selected": "No device selected, please select one device.", "unknown_device": "The device model is not known, not able to setup the device using config flow.", "wrong_token": "Checksum error, wrong token" }, @@ -25,42 +24,19 @@ "cloud_username": "Cloud username", "manual": "Configure manually (not recommended)" }, - "description": "Log in to the Xiaomi Miio cloud, see https://www.openhab.org/addons/bindings/miio/#country-servers for the cloud server to use.", - "title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway" + "description": "Log in to the Xiaomi Miio cloud, see https://www.openhab.org/addons/bindings/miio/#country-servers for the cloud server to use." }, "connect": { "data": { "model": "Device model" - }, - "description": "Manually select the device model from the supported models.", - "title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway" - }, - "device": { - "data": { - "host": "IP Address", - "model": "Device model (Optional)", - "name": "Name of the device", - "token": "API Token" - }, - "description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration.", - "title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "IP Address", - "name": "Name of the Gateway", - "token": "API Token" - }, - "description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration.", - "title": "Connect to a Xiaomi Gateway" + } }, "manual": { "data": { "host": "IP Address", "token": "API Token" }, - "description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration.", - "title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway" + "description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration." }, "reauth_confirm": { "description": "The Xiaomi Miio integration needs to re-authenticate your account in order to update the tokens or add missing cloud credentials.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio device" }, - "description": "Select the Xiaomi Miio device to setup.", - "title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Connect to a Xiaomi Gateway" - }, - "description": "Select to which device you want to connect.", - "title": "Xiaomi Miio" + "description": "Select the Xiaomi Miio device to setup." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Use cloud to get connected subdevices" - }, - "description": "Specify optional settings", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/es-419.json b/homeassistant/components/xiaomi_miio/translations/es-419.json deleted file mode 100644 index b3bbc25ebba..00000000000 --- a/homeassistant/components/xiaomi_miio/translations/es-419.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "config": { - "error": { - "no_device_selected": "Ning\u00fan dispositivo seleccionado, seleccione un dispositivo." - }, - "step": { - "gateway": { - "data": { - "host": "Direcci\u00f3n IP", - "name": "Nombre de la puerta de enlace", - "token": "Token API" - }, - "description": "Necesitar\u00e1 el token API, consulte https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token para obtener instrucciones.", - "title": "Conectarse a una puerta de enlace Xiaomi" - }, - "user": { - "data": { - "gateway": "Conectarse a una puerta de enlace Xiaomi" - }, - "description": "Seleccione a qu\u00e9 dispositivo desea conectarse.", - "title": "Xiaomi Miio" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/es.json b/homeassistant/components/xiaomi_miio/translations/es.json index 2c1dad04111..c1a3eae784b 100644 --- a/homeassistant/components/xiaomi_miio/translations/es.json +++ b/homeassistant/components/xiaomi_miio/translations/es.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Las credenciales de la nube est\u00e1n incompletas, por favor, rellene el nombre de usuario, la contrase\u00f1a y el pa\u00eds", "cloud_login_error": "No se ha podido iniciar sesi\u00f3n en Xiaomi Miio Cloud, comprueba las credenciales.", "cloud_no_devices": "No se han encontrado dispositivos en esta cuenta de Xiaomi Miio.", - "no_device_selected": "No se ha seleccionado ning\u00fan dispositivo, por favor, seleccione un dispositivo.", "unknown_device": "No se conoce el modelo del dispositivo, no se puede configurar el dispositivo mediante el flujo de configuraci\u00f3n.", "wrong_token": "Error de suma de comprobaci\u00f3n, token err\u00f3neo" }, @@ -25,42 +24,19 @@ "cloud_username": "Nombre de usuario de la nube", "manual": "Configurar manualmente (no recomendado)" }, - "description": "Inicie sesi\u00f3n en la nube de Xiaomi Miio, consulte https://www.openhab.org/addons/bindings/miio/#country-servers para conocer el servidor de la nube que debe utilizar.", - "title": "Con\u00e9ctese a un dispositivo Xiaomi Miio o una puerta de enlace Xiaomi" + "description": "Inicie sesi\u00f3n en la nube de Xiaomi Miio, consulte https://www.openhab.org/addons/bindings/miio/#country-servers para conocer el servidor de la nube que debe utilizar." }, "connect": { "data": { "model": "Modelo del dispositivo" - }, - "description": "Seleccione manualmente el modelo de dispositivo entre los modelos admitidos.", - "title": "Con\u00e9ctese a un dispositivo Xiaomi Miio o una puerta de enlace Xiaomi" - }, - "device": { - "data": { - "host": "Direcci\u00f3n IP", - "model": "Modelo de dispositivo (opcional)", - "name": "Nombre del dispositivo", - "token": "Token API" - }, - "description": "Necesitar\u00e1 la clave de 32 caracteres Token API, consulte https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token para obtener instrucciones. Tenga en cuenta que esta Token API es diferente de la clave utilizada por la integraci\u00f3n de Xiaomi Aqara.", - "title": "Con\u00e9ctese a un dispositivo Xiaomi Miio o Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "Direcci\u00f3n IP", - "name": "Nombre del Gateway", - "token": "Token API" - }, - "description": "Necesitar\u00e1s el token de la API de 32 caracteres, revisa https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token para m\u00e1s instrucciones. Por favor, ten en cuenta que este token es diferente de la clave utilizada por la integraci\u00f3n de Xiaomi Aqara.", - "title": "Conectar con un Xiaomi Gateway" + } }, "manual": { "data": { "host": "Direcci\u00f3n IP", "token": "Token API" }, - "description": "Necesitar\u00e1s la clave de 32 caracteres Token API, consulta https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token para obtener instrucciones. Ten en cuenta que esta Token API es diferente de la clave utilizada por la integraci\u00f3n de Xiaomi Aqara.", - "title": "Con\u00e9ctate a un dispositivo Xiaomi Miio o una puerta de enlace Xiaomi" + "description": "Necesitar\u00e1s la clave de 32 caracteres Token API, consulta https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token para obtener instrucciones. Ten en cuenta que esta Token API es diferente de la clave utilizada por la integraci\u00f3n de Xiaomi Aqara." }, "reauth_confirm": { "description": "La integraci\u00f3n de Xiaomi Miio necesita volver a autenticar tu cuenta para actualizar los tokens o a\u00f1adir las credenciales de la nube que faltan.", @@ -70,15 +46,7 @@ "data": { "select_device": "Dispositivo Miio" }, - "description": "Selecciona el dispositivo Xiaomi Miio para configurarlo.", - "title": "Con\u00e9ctese a un dispositivo Xiaomi Miio o una puerta de enlace Xiaomi" - }, - "user": { - "data": { - "gateway": "Conectar con un Xiaomi Gateway" - }, - "description": "Selecciona a qu\u00e9 dispositivo quieres conectar.", - "title": "Xiaomi Miio" + "description": "Selecciona el dispositivo Xiaomi Miio para configurarlo." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Utiliza la nube para conectar subdispositivos" - }, - "description": "Especifica los ajustes opcionales", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/et.json b/homeassistant/components/xiaomi_miio/translations/et.json index 6fd3c4257ef..fef6a73622a 100644 --- a/homeassistant/components/xiaomi_miio/translations/et.json +++ b/homeassistant/components/xiaomi_miio/translations/et.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Pilve mandaat on poolik, palun t\u00e4ida kasutajanimi, salas\u00f5na ja riik", "cloud_login_error": "Xiaomi Miio Cloudi ei saanud sisse logida, kontrolli mandaati.", "cloud_no_devices": "Xiaomi Miio pilvekontolt ei leitud \u00fchtegi seadet.", - "no_device_selected": "Seadmeid pole valitud, vali \u00fcks seade.", "unknown_device": "Seadme mudel pole teada, seadet ei saa seadistamisvoo abil seadistada.", "wrong_token": "Kontrollsumma t\u00f5rge, vigane r\u00e4si" }, @@ -25,42 +24,19 @@ "cloud_username": "Pilve kasutajatunnus", "manual": "Seadista k\u00e4sitsi (pole soovitatav)" }, - "description": "Logi sisse Xiaomi Miio pilve, vaata https://www.openhab.org/addons/bindings/miio/#country-servers pilveserveri kasutamiseks.", - "title": "\u00dchenda Xiaomi Miio seade v\u00f5i Xiaomi Gateway" + "description": "Logi sisse Xiaomi Miio pilve, vaata https://www.openhab.org/addons/bindings/miio/#country-servers pilveserveri kasutamiseks." }, "connect": { "data": { "model": "Seadme mudel" - }, - "description": "Vali seadme mudel k\u00e4sitsi toetatud mudelite hulgast.", - "title": "\u00dchenda Xiaomi Miio seade v\u00f5i Xiaomi Gateway" - }, - "device": { - "data": { - "host": "IP-aadress", - "model": "Seadme mudel (valikuline)", - "name": "Seadme nimi", - "token": "API v\u00f5ti" - }, - "description": "Vaja on 32 t\u00e4hem\u00e4rgilist v\u00f5tit API v\u00f5ti , juhiste saamiseks vaata https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. Pane t\u00e4hele, et see v\u00f5ti API v\u00f5ti erineb Xiaomi Aqara sidumises kasutatavast v\u00f5tmest.", - "title": "\u00dchenda Xiaomi Miio seade v\u00f5i Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "IP aadress", - "name": "L\u00fc\u00fcsi nimi", - "token": "API string" - }, - "description": "On vaja 32-kohalist API-tokenti, juhiste saamiseks vaata lehte https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. Pane t\u00e4hele, et see token erineb Xiaomi Aqara sidumisel kasutatavast v\u00f5tmest.", - "title": "Loo \u00fchendus Xiaomi l\u00fc\u00fcsiga" + } }, "manual": { "data": { "host": "IP aadress", "token": "API v\u00f5ti" }, - "description": "On vajalik 32 t\u00e4hem\u00e4rki API v\u00f5ti, vt. https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token juhiseid. Pane t\u00e4hele, et see API v\u00f5ti erineb Xiaomi Aqara sidumises kasutatavast v\u00f5tmest.", - "title": "\u00dchenda Xiaomi Miio seade v\u00f5i Xiaomi Gateway" + "description": "On vajalik 32 t\u00e4hem\u00e4rki API v\u00f5ti, vt. https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token juhiseid. Pane t\u00e4hele, et see API v\u00f5ti erineb Xiaomi Aqara sidumises kasutatavast v\u00f5tmest." }, "reauth_confirm": { "description": "Xiaomi Miio sidumine peab konto uuesti tuvastama, et v\u00e4rskendada p\u00e4\u00e4sulube v\u00f5i lisada puuduv pilvemandaat.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio seade" }, - "description": "Vali seadistamiseks Xiaomi Miio seade.", - "title": "\u00dchenda Xiaomi Miio seade v\u00f5i Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Loo \u00fchendus Xiaomi l\u00fc\u00fcsiga" - }, - "description": "Vali seade millega soovid \u00fchenduse luua.", - "title": "Xiaomi Miio" + "description": "Vali seadistamiseks Xiaomi Miio seade." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u00dchendatud alamseadmete hankimiseks kasuta pilve" - }, - "description": "Valikuliste s\u00e4tete m\u00e4\u00e4ramine", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/fi.json b/homeassistant/components/xiaomi_miio/translations/fi.json deleted file mode 100644 index ab360a82a8f..00000000000 --- a/homeassistant/components/xiaomi_miio/translations/fi.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "config": { - "error": { - "no_device_selected": "Ei valittuja laitteita. Ole hyv\u00e4 ja valitse yksi." - }, - "step": { - "gateway": { - "data": { - "host": "IP-osoite", - "name": "Yhdysk\u00e4yt\u00e4v\u00e4n nimi", - "token": "API-tunnus" - }, - "title": "Yhdist\u00e4 Xiaomi Gatewayhin" - }, - "user": { - "data": { - "gateway": "Yhdist\u00e4 Xiaomi Gatewayhin" - }, - "description": "Valitse laite, johon haluat muodostaa yhteyden.", - "title": "Xiaomi Miio" - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/fr.json b/homeassistant/components/xiaomi_miio/translations/fr.json index 2a9d8d65c77..91c6b745816 100644 --- a/homeassistant/components/xiaomi_miio/translations/fr.json +++ b/homeassistant/components/xiaomi_miio/translations/fr.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Identifiants cloud incomplets, veuillez renseigner le nom d'utilisateur, le mot de passe et le pays", "cloud_login_error": "Impossible de se connecter \u00e0 Xioami Miio Cloud, v\u00e9rifiez les informations d'identification.", "cloud_no_devices": "Aucun appareil trouv\u00e9 dans ce compte cloud Xiaomi Miio.", - "no_device_selected": "Aucun appareil s\u00e9lectionn\u00e9, veuillez s\u00e9lectionner un appareil.", "unknown_device": "Le mod\u00e8le d'appareil n'est pas connu, impossible de configurer l'appareil \u00e0 l'aide du flux de configuration.", "wrong_token": "Erreur de somme de contr\u00f4le, jeton incorrect" }, @@ -25,42 +24,19 @@ "cloud_username": "Nom d'utilisateur cloud", "manual": "Configurer manuellement (non recommand\u00e9)" }, - "description": "Connectez-vous au cloud Xiaomi Miio, voir https://www.openhab.org/addons/bindings/miio/#country-servers pour le serveur cloud \u00e0 utiliser.", - "title": "Se connecter \u00e0 un appareil Xiaomi Miio ou \u00e0 une passerelle Xiaomi" + "description": "Connectez-vous au cloud Xiaomi Miio, voir https://www.openhab.org/addons/bindings/miio/#country-servers pour le serveur cloud \u00e0 utiliser." }, "connect": { "data": { "model": "Mod\u00e8le d'appareil" - }, - "description": "S\u00e9lectionner manuellement le mod\u00e8le d'appareil parmi les mod\u00e8les pris en charge.", - "title": "Se connecter \u00e0 un appareil Xiaomi Miio ou \u00e0 une passerelle Xiaomi" - }, - "device": { - "data": { - "host": "Adresse IP", - "model": "Mod\u00e8le d'appareil (facultatif)", - "name": "Nom de l'appareil", - "token": "Jeton d'API" - }, - "description": "Vous aurez besoin des 32 caract\u00e8res Jeton d'API , voir https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token pour les instructions. Veuillez noter que cette Jeton d'API est diff\u00e9rente de la cl\u00e9 utilis\u00e9e par l'int\u00e9gration Xiaomi Aqara.", - "title": "Connectez-vous \u00e0 un appareil Xiaomi Miio ou \u00e0 une passerelle Xiaomi" - }, - "gateway": { - "data": { - "host": "Adresse IP", - "name": "Nom de la passerelle", - "token": "Jeton d'API" - }, - "description": "Vous aurez besoin du jeton API, voir https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token pour les instructions.", - "title": "Se connecter \u00e0 la passerelle Xiaomi" + } }, "manual": { "data": { "host": "Adresse IP", "token": "Jeton d'API" }, - "description": "Vous aurez besoin du Jeton d'API de 32 caract\u00e8res, voir https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token pour les instructions. Veuillez noter que ce Jeton d'API est diff\u00e9rent de la cl\u00e9 utilis\u00e9e par l'int\u00e9gration Xiaomi Aqara.", - "title": "Se connecter \u00e0 un appareil Xiaomi Miio ou \u00e0 une passerelle Xiaomi" + "description": "Vous aurez besoin du Jeton d'API de 32 caract\u00e8res, voir https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token pour les instructions. Veuillez noter que ce Jeton d'API est diff\u00e9rent de la cl\u00e9 utilis\u00e9e par l'int\u00e9gration Xiaomi Aqara." }, "reauth_confirm": { "description": "L'int\u00e9gration de Xiaomi Miio doit r\u00e9-authentifier votre compte afin de mettre \u00e0 jour les jetons ou d'ajouter les informations d'identification cloud manquantes.", @@ -70,15 +46,7 @@ "data": { "select_device": "Appareil Miio" }, - "description": "S\u00e9lectionner l'appareil Xiaomi Miio \u00e0 configurer.", - "title": "Se connecter \u00e0 un appareil Xiaomi Miio ou \u00e0 une passerelle Xiaomi" - }, - "user": { - "data": { - "gateway": "Se connecter \u00e0 la passerelle Xiaomi" - }, - "description": "S\u00e9lectionnez \u00e0 quel appareil vous souhaitez vous connecter.", - "title": "Xiaomi Miio" + "description": "S\u00e9lectionner l'appareil Xiaomi Miio \u00e0 configurer." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Utiliser le cloud pour connecter des sous-appareils" - }, - "description": "Sp\u00e9cifiez les param\u00e8tres optionnels", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/he.json b/homeassistant/components/xiaomi_miio/translations/he.json index db0cee16884..1a21bd9840a 100644 --- a/homeassistant/components/xiaomi_miio/translations/he.json +++ b/homeassistant/components/xiaomi_miio/translations/he.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u05d0\u05d9\u05e9\u05d5\u05e8\u05d9 \u05e2\u05e0\u05df \u05d0\u05d9\u05e0\u05dd \u05de\u05dc\u05d0\u05d9\u05dd, \u05e0\u05d0 \u05dc\u05de\u05dc\u05d0 \u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9, \u05e1\u05d9\u05e1\u05de\u05d4 \u05d5\u05de\u05d3\u05d9\u05e0\u05d4", "cloud_login_error": "\u05dc\u05d0 \u05d4\u05d9\u05ea\u05d4 \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05e2\u05e0\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5, \u05e0\u05d0 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea \u05d4\u05d0\u05d9\u05e9\u05d5\u05e8\u05d9\u05dd.", "cloud_no_devices": "\u05dc\u05d0 \u05e0\u05de\u05e6\u05d0\u05d5 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05d1\u05d7\u05e9\u05d1\u05d5\u05df \u05d4\u05e2\u05e0\u05df \u05d4\u05d6\u05d4 \u05e9\u05dc \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5.", - "no_device_selected": "\u05dc\u05d0 \u05e0\u05d1\u05d7\u05e8 \u05d4\u05ea\u05e7\u05df, \u05e0\u05d0 \u05d1\u05d7\u05e8 \u05d4\u05ea\u05e7\u05df \u05d0\u05d7\u05d3.", "unknown_device": "\u05d3\u05d2\u05dd \u05d4\u05d4\u05ea\u05e7\u05df \u05d0\u05d9\u05e0\u05d5 \u05d9\u05d3\u05d5\u05e2, \u05d0\u05d9\u05df \u05d0\u05e4\u05e9\u05e8\u05d5\u05ea \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea \u05d6\u05e8\u05d9\u05de\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4.", "wrong_token": "\u05e9\u05d2\u05d9\u05d0\u05ea \u05d1\u05d3\u05d9\u05e7\u05ea \u05e1\u05d9\u05db\u05d5\u05dd, \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05e9\u05d2\u05d5\u05d9" }, @@ -25,42 +24,19 @@ "cloud_username": "\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9 \u05e2\u05e0\u05df", "manual": "\u05d4\u05d2\u05d3\u05e8\u05ea \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d9\u05d3\u05e0\u05d9\u05ea (\u05dc\u05d0 \u05de\u05d5\u05de\u05dc\u05e5)" }, - "description": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5 \u05dc\u05e2\u05e0\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5, \u05e8\u05d0\u05d5 https://www.openhab.org/addons/bindings/miio/#country-servers \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05e9\u05e8\u05ea \u05d4\u05e2\u05e0\u05df.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05db\u05e9\u05d9\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5 \u05d0\u05d5 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" + "description": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5 \u05dc\u05e2\u05e0\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5, \u05e8\u05d0\u05d5 https://www.openhab.org/addons/bindings/miio/#country-servers \u05dc\u05e9\u05d9\u05de\u05d5\u05e9 \u05d1\u05e9\u05e8\u05ea \u05d4\u05e2\u05e0\u05df." }, "connect": { "data": { "model": "\u05d3\u05d2\u05dd \u05d4\u05ea\u05e7\u05df" - }, - "description": "\u05d1\u05d7\u05e8 \u05d9\u05d3\u05e0\u05d9\u05ea \u05d0\u05ea \u05d3\u05d2\u05dd \u05d4\u05d4\u05ea\u05e7\u05df \u05de\u05d4\u05d3\u05d2\u05de\u05d9\u05dd \u05d4\u05e0\u05ea\u05de\u05db\u05d9\u05dd.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05db\u05e9\u05d9\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5 \u05d0\u05d5 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" - }, - "device": { - "data": { - "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP", - "model": "\u05d3\u05d2\u05dd \u05d4\u05ea\u05e7\u05df (\u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9)", - "name": "\u05e9\u05dd \u05d4\u05d4\u05ea\u05e7\u05df", - "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" - }, - "description": "\u05d0\u05ea\u05d4 \u05d6\u05e7\u05d5\u05e7 \u05dc-32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API , \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05db\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05db\u05e9\u05d9\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5 \u05d0\u05d5 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" - }, - "gateway": { - "data": { - "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP", - "name": "\u05e9\u05dd \u05d4\u05e9\u05e2\u05e8", - "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" - }, - "description": "\u05e0\u05d3\u05e8\u05e9\u05d9\u05dd 32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API, \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05db\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05d0\u05ea \u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" + } }, "manual": { "data": { "host": "\u05db\u05ea\u05d5\u05d1\u05ea IP", "token": "\u05d0\u05e1\u05d9\u05de\u05d5\u05df API" }, - "description": "\u05ea\u05d6\u05d3\u05e7\u05e7 \u05dc-32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API, \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05d6\u05d4 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05dc\u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05db\u05e9\u05d9\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5 \u05d0\u05d5 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" + "description": "\u05ea\u05d6\u05d3\u05e7\u05e7 \u05dc-32 \u05ea\u05d5\u05d5\u05d9 \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05df API, \u05e8\u05d0\u05d4 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u05dc\u05d4\u05d5\u05e8\u05d0\u05d5\u05ea. \u05e9\u05d9\u05dd \u05dc\u05d1, \u05d6\u05d4 \u05d0\u05e1\u05d9\u05de\u05d5\u05df API \u05e9\u05d5\u05e0\u05d4 \u05de\u05d4\u05de\u05e4\u05ea\u05d7 \u05d4\u05de\u05e9\u05de\u05e9 \u05dc\u05e9\u05d9\u05dc\u05d5\u05d1 Xiaomi Aqara." }, "reauth_confirm": { "description": "\u05d4\u05e9\u05d9\u05dc\u05d5\u05d1 \u05e9\u05dc \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5 \u05e6\u05e8\u05d9\u05db\u05d4 \u05dc\u05d0\u05de\u05ea \u05de\u05d7\u05d3\u05e9 \u05d0\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05e9\u05dc\u05da \u05db\u05d3\u05d9 \u05dc\u05e2\u05d3\u05db\u05df \u05d0\u05ea \u05d4\u05d0\u05e1\u05d9\u05de\u05d5\u05e0\u05d9\u05dd \u05d0\u05d5 \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05d9\u05e9\u05d5\u05e8\u05d9 \u05e2\u05e0\u05df \u05d7\u05e1\u05e8\u05d9\u05dd.", @@ -70,15 +46,7 @@ "data": { "select_device": "\u05d4\u05ea\u05e7\u05df \u05de\u05d9\u05d5" }, - "description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d4\u05ea\u05e7\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5 \u05dc\u05d4\u05ea\u05e7\u05e0\u05d4.", - "title": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05de\u05db\u05e9\u05d9\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5 \u05d0\u05d5 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" - }, - "user": { - "data": { - "gateway": "\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05e9\u05e2\u05e8 \u05e9\u05d9\u05d0\u05d5\u05de\u05d9" - }, - "description": "\u05d1\u05d7\u05e8 \u05dc\u05d0\u05d9\u05d6\u05d4 \u05d4\u05ea\u05e7\u05df \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8.", - "title": "\u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5" + "description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d4\u05ea\u05e7\u05df \u05e9\u05d9\u05d5\u05d0\u05de\u05d9 \u05de\u05d9\u05d5 \u05dc\u05d4\u05ea\u05e7\u05e0\u05d4." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05e2\u05e0\u05df \u05db\u05d3\u05d9 \u05dc\u05d4\u05ea\u05d7\u05d1\u05e8 \u05dc\u05ea\u05ea-\u05d4\u05ea\u05e7\u05e0\u05d9\u05dd \u05de\u05d7\u05d5\u05d1\u05e8\u05d9\u05dd" - }, - "description": "\u05e6\u05d9\u05d9\u05df \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05d0\u05d5\u05e4\u05e6\u05d9\u05d5\u05e0\u05dc\u05d9\u05d5\u05ea", - "title": "\u05e9\u05d9\u05d0\u05d5\u05de\u05d9 \u05de\u05d9\u05d5" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/hu.json b/homeassistant/components/xiaomi_miio/translations/hu.json index 189e9906e24..51f093b871c 100644 --- a/homeassistant/components/xiaomi_miio/translations/hu.json +++ b/homeassistant/components/xiaomi_miio/translations/hu.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "A felh\u0151alap\u00fa hiteles\u00edt\u0151 adatok hi\u00e1nyosak, k\u00e9rj\u00fck, adja meg a felhaszn\u00e1l\u00f3nevet, a jelsz\u00f3t \u00e9s az orsz\u00e1got", "cloud_login_error": "Nem siker\u00fclt bejelentkezni a Xioami Miio Cloud szolg\u00e1ltat\u00e1sba, ellen\u0151rizze a hiteles\u00edt\u0151 adatokat.", "cloud_no_devices": "Nincs eszk\u00f6z ebben a Xiaomi Miio felh\u0151fi\u00f3kban.", - "no_device_selected": "Nincs kiv\u00e1lasztva eszk\u00f6z, k\u00e9rj\u00fck, v\u00e1lasszon egyet.", "unknown_device": "Az eszk\u00f6z modell nem ismert, nem tudja be\u00e1ll\u00edtani az eszk\u00f6zt a konfigur\u00e1ci\u00f3s folyamat seg\u00edts\u00e9g\u00e9vel.", "wrong_token": "Ellen\u0151rz\u0151\u00f6sszeg-hiba, hib\u00e1s token" }, @@ -25,42 +24,19 @@ "cloud_username": "Felh\u0151 felhaszn\u00e1l\u00f3neve", "manual": "Konfigur\u00e1l\u00e1s manu\u00e1lisan (nem aj\u00e1nlott)" }, - "description": "Jelentkezzen be a Xiaomi Miio felh\u0151be, a felh\u0151szerver haszn\u00e1lat\u00e1hoz l\u00e1sd: https://www.openhab.org/addons/bindings/miio/#country-servers.", - "title": "Csatlakozzon egy Xiaomi Miio eszk\u00f6zh\u00f6z vagy a Xiaomi Gateway-hez" + "description": "Jelentkezzen be a Xiaomi Miio felh\u0151be, a felh\u0151szerver haszn\u00e1lat\u00e1hoz l\u00e1sd: https://www.openhab.org/addons/bindings/miio/#country-servers." }, "connect": { "data": { "model": "Eszk\u00f6z modell" - }, - "description": "V\u00e1lassza ki manu\u00e1lisan a modellt a t\u00e1mogatott modellek k\u00f6z\u00fcl.", - "title": "Csatlakozzon egy Xiaomi Miio eszk\u00f6zh\u00f6z vagy a Xiaomi Gateway-hez" - }, - "device": { - "data": { - "host": "IP c\u00edm", - "model": "Eszk\u00f6z modell (opcion\u00e1lis)", - "name": "Eszk\u00f6z neve", - "token": "API Token" - }, - "description": "Sz\u00fcks\u00e9ge lesz a 32 karakteres API Tokenre, k\u00f6vesse a https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token oldal instrukci\u00f3it. Vegye figyelembe, hogy ez az API Token k\u00fcl\u00f6nb\u00f6zik a Xiaomi Aqara integr\u00e1ci\u00f3 \u00e1ltal haszn\u00e1lt kulcst\u00f3l.", - "title": "Csatlakoz\u00e1s Xiaomi Miio eszk\u00f6zh\u00f6z vagy Xiaomi Gateway-hez" - }, - "gateway": { - "data": { - "host": "IP c\u00edm", - "name": "K\u00f6zponti egys\u00e9g neve", - "token": "API Token" - }, - "description": "Sz\u00fcks\u00e9ge lesz az API Tokenre, tov\u00e1bbi inforaciok: https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token. K\u00e9rj\u00fck, vegye figyelembe, hogy ez az API Token k\u00fcl\u00f6nb\u00f6zik a Xiaomi Aqara integr\u00e1ci\u00f3 \u00e1ltal haszn\u00e1lt kulcst\u00f3l.", - "title": "Csatlakozzon egy Xiaomi K\u00f6zponti egys\u00e9ghez" + } }, "manual": { "data": { "host": "IP c\u00edm", "token": "API Token" }, - "description": "Sz\u00fcks\u00e9ge lesz a 32 karakteres API Token -re. Az utas\u00edt\u00e1sok\u00e9rt l\u00e1sd: https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Felh\u00edvjuk figyelm\u00e9t, hogy ez a API Token elt\u00e9r a Xiaomi Aqara integr\u00e1ci\u00f3 \u00e1ltal haszn\u00e1lt kulcst\u00f3l.", - "title": "Csatlakozzon egy Xiaomi Miio eszk\u00f6zh\u00f6z vagy a Xiaomi Gateway-hez" + "description": "Sz\u00fcks\u00e9ge lesz a 32 karakteres API Token -re. Az utas\u00edt\u00e1sok\u00e9rt l\u00e1sd: https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Felh\u00edvjuk figyelm\u00e9t, hogy ez a API Token elt\u00e9r a Xiaomi Aqara integr\u00e1ci\u00f3 \u00e1ltal haszn\u00e1lt kulcst\u00f3l." }, "reauth_confirm": { "description": "A tokenek friss\u00edt\u00e9s\u00e9hez vagy hi\u00e1nyz\u00f3 felh\u0151alap\u00fa hiteles\u00edt\u0151 adatok hozz\u00e1ad\u00e1s\u00e1hoz a Xiaomi Miio integr\u00e1ci\u00f3nak \u00fajra hiteles\u00edtenie kell a fi\u00f3kj\u00e1t.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio eszk\u00f6z" }, - "description": "V\u00e1lassza ki a be\u00e1ll\u00edtand\u00f3 Xiaomi Miio eszk\u00f6zt.", - "title": "Csatlakoz\u00e1s Xiaomi Miio eszk\u00f6zh\u00f6z vagy Xiaomi Gateway-hez" - }, - "user": { - "data": { - "gateway": "Csatlakozzon egy Xiaomi K\u00f6zponti egys\u00e9ghez" - }, - "description": "V\u00e1lassza ki, melyik k\u00e9sz\u00fcl\u00e9khez szeretne csatlakozni.", - "title": "Xiaomi Miio" + "description": "V\u00e1lassza ki a be\u00e1ll\u00edtand\u00f3 Xiaomi Miio eszk\u00f6zt." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Haszn\u00e1lja a felh\u0151t a csatlakoztatott alegys\u00e9gek megszerz\u00e9s\u00e9hez" - }, - "description": "Adja meg az opcion\u00e1lis be\u00e1ll\u00edt\u00e1sokat", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/id.json b/homeassistant/components/xiaomi_miio/translations/id.json index 4aae8d6396c..8d1d70642aa 100644 --- a/homeassistant/components/xiaomi_miio/translations/id.json +++ b/homeassistant/components/xiaomi_miio/translations/id.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Kredensial cloud tidak lengkap, isi nama pengguna, kata sandi, dan negara", "cloud_login_error": "Tidak dapat masuk ke Xiaomi Miio Cloud, periksa kredensialnya.", "cloud_no_devices": "Tidak ada perangkat yang ditemukan di akun cloud Xiaomi Miio ini.", - "no_device_selected": "Tidak ada perangkat yang dipilih, pilih satu perangkat.", "unknown_device": "Model perangkat tidak diketahui, tidak dapat menyiapkan perangkat menggunakan alur konfigurasi.", "wrong_token": "Kesalahan checksum, token salah" }, @@ -25,42 +24,19 @@ "cloud_username": "Nama pengguna cloud", "manual": "Konfigurasi secara manual (tidak disarankan)" }, - "description": "Masuk ke cloud Xiaomi Miio, lihat https://www.openhab.org/addons/bindings/miio/#country-servers untuk menemukan server cloud yang digunakan.", - "title": "Hubungkan ke Perangkat Xiaomi Miio atau Xiaomi Gateway" + "description": "Masuk ke cloud Xiaomi Miio, lihat https://www.openhab.org/addons/bindings/miio/#country-servers untuk menemukan server cloud yang digunakan." }, "connect": { "data": { "model": "Model perangkat" - }, - "description": "Pilih model perangkat secara manual dari model yang didukung.", - "title": "Hubungkan ke Perangkat Xiaomi Miio atau Xiaomi Gateway" - }, - "device": { - "data": { - "host": "Alamat IP", - "model": "Model perangkat (Opsional)", - "name": "Nama perangkat", - "token": "Token API" - }, - "description": "Anda akan membutuhkan Token API 32 karakter, lihat https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token untuk mendapatkan petunjuknya. Perhatikan bahwa Token API ini berbeda dari kunci yang digunakan oleh integrasi Xiaomi Aqara.", - "title": "Hubungkan ke Perangkat Xiaomi Miio atau Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "Alamat IP", - "name": "Nama Gateway", - "token": "Token API" - }, - "description": "Anda akan membutuhkan Token API 32 karakter, lihat https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token untuk mendapatkan petunjuknya. Perhatikan bahwa Token API ini berbeda dari kunci yang digunakan oleh integrasi Xiaomi Aqara.", - "title": "Hubungkan ke Xiaomi Gateway" + } }, "manual": { "data": { "host": "Alamat IP", "token": "Token API" }, - "description": "Anda akan membutuhkan Token API 32 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Perhatikan bahwa Token API ini berbeda dengan kunci yang digunakan untuk integrasi Xiaomi Aqara.", - "title": "Hubungkan ke Perangkat Xiaomi Miio atau Xiaomi Gateway" + "description": "Anda akan membutuhkan Token API 32 karakter, baca petunjuknya di https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token. Perhatikan bahwa Token API ini berbeda dengan kunci yang digunakan untuk integrasi Xiaomi Aqara." }, "reauth_confirm": { "description": "Integrasi Xiaomi Miio perlu mengautentikasi ulang akun Anda untuk memperbarui token atau menambahkan kredensial cloud yang hilang.", @@ -70,15 +46,7 @@ "data": { "select_device": "Perangkat Miio" }, - "description": "Pilih perangkat Xiaomi Miio untuk disiapkan.", - "title": "Hubungkan ke Perangkat Xiaomi Miio atau Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Hubungkan ke Xiaomi Gateway" - }, - "description": "Pilih perangkat mana yang ingin disambungkan.", - "title": "Xiaomi Miio" + "description": "Pilih perangkat Xiaomi Miio untuk disiapkan." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Gunakan cloud untuk mendapatkan subperangkat yang tersambung" - }, - "description": "Tentukan pengaturan opsional", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/it.json b/homeassistant/components/xiaomi_miio/translations/it.json index b643f7df4de..51b5f108751 100644 --- a/homeassistant/components/xiaomi_miio/translations/it.json +++ b/homeassistant/components/xiaomi_miio/translations/it.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Credenziali cloud incomplete, inserisci nome utente, password e paese", "cloud_login_error": "Impossibile accedere a Xioami Miio Cloud, controlla le credenziali.", "cloud_no_devices": "Nessun dispositivo trovato in questo account cloud Xiaomi Miio.", - "no_device_selected": "Nessun dispositivo selezionato, seleziona un dispositivo.", "unknown_device": "Il modello del dispositivo non \u00e8 noto, non \u00e8 possibile configurare il dispositivo utilizzando il flusso di configurazione.", "wrong_token": "Errore del codice di controllo, token errato" }, @@ -25,42 +24,19 @@ "cloud_username": "Nome utente cloud", "manual": "Configura manualmente (non consigliato)" }, - "description": "Accedi al cloud Xiaomi Miio, vedi https://www.openhab.org/addons/bindings/miio/#country-servers per utilizzare il server cloud.", - "title": "Connettiti a un dispositivo Xiaomi Miio o Xiaomi Gateway" + "description": "Accedi al cloud Xiaomi Miio, vedi https://www.openhab.org/addons/bindings/miio/#country-servers per utilizzare il server cloud." }, "connect": { "data": { "model": "Modello del dispositivo" - }, - "description": "Seleziona manualmente il modello del dispositivo tra i modelli supportati.", - "title": "Connettiti a un dispositivo Xiaomi Miio o Xiaomi Gateway" - }, - "device": { - "data": { - "host": "Indirizzo IP", - "model": "Modello del dispositivo (opzionale)", - "name": "Nome del dispositivo", - "token": "Token API" - }, - "description": "Avrai bisogno dei 32 caratteri del Token API, vedi https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token per istruzioni. Tieni presente che questo Token API \u00e8 diverso dalla chiave utilizzata dall'integrazione Xiaomi Aqara.", - "title": "Connettiti a un dispositivo Xiaomi Miio o Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "Indirizzo IP", - "name": "Nome del Gateway", - "token": "Token API" - }, - "description": "\u00c8 necessario il Token API di 32 caratteri, vedi https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token per le istruzioni. Nota che questo Token API \u00e8 diverso dalla chiave usata dall'integrazione di Xiaomi Aqara.", - "title": "Connettiti a un Xiaomi Gateway " + } }, "manual": { "data": { "host": "Indirizzo IP", "token": "Token API" }, - "description": "Avrai bisogno dei 32 caratteri del Token API, vedi https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token per istruzioni. Tieni presente che questo Token API \u00e8 diverso dalla chiave utilizzata dall'integrazione Xiaomi Aqara.", - "title": "Connettiti a un dispositivo Xiaomi Miio o Xiaomi Gateway" + "description": "Avrai bisogno dei 32 caratteri del Token API, vedi https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token per istruzioni. Tieni presente che questo Token API \u00e8 diverso dalla chiave utilizzata dall'integrazione Xiaomi Aqara." }, "reauth_confirm": { "description": "L'integrazione di Xiaomi Miio deve autenticare nuovamente il tuo account per aggiornare i token o aggiungere credenziali cloud mancanti.", @@ -70,15 +46,7 @@ "data": { "select_device": "Dispositivo Miio" }, - "description": "Seleziona il dispositivo Xiaomi Miio da configurare.", - "title": "Connettiti a un dispositivo Xiaomi Miio o Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Connettiti a un Xiaomi Gateway" - }, - "description": "Seleziona a quale dispositivo desideri collegarti.", - "title": "Xiaomi Miio" + "description": "Seleziona il dispositivo Xiaomi Miio da configurare." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Usa il cloud per connettere i sottodispositivi" - }, - "description": "Specifica le impostazioni opzionali", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/ja.json b/homeassistant/components/xiaomi_miio/translations/ja.json index 44c6f7f149e..0877850f754 100644 --- a/homeassistant/components/xiaomi_miio/translations/ja.json +++ b/homeassistant/components/xiaomi_miio/translations/ja.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u30af\u30e9\u30a6\u30c9\u8a8d\u8a3c\u304c\u4e0d\u5b8c\u5168\u3067\u3059\u3002\u30e6\u30fc\u30b6\u30fc\u540d\u3001\u30d1\u30b9\u30ef\u30fc\u30c9\u3001\u56fd\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "cloud_login_error": "Xiaomi Miio Cloud\u306b\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u8a8d\u8a3c\u60c5\u5831\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "cloud_no_devices": "\u3053\u306eXiaomi Miio cloud\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u306b\u30c7\u30d0\u30a4\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002", - "no_device_selected": "\u30c7\u30d0\u30a4\u30b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30c7\u30d0\u30a4\u30b9\u30921\u3064\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "unknown_device": "\u30c7\u30d0\u30a4\u30b9\u30e2\u30c7\u30eb\u304c\u4e0d\u660e\u306a\u306e\u3067\u3001\u69cb\u6210\u30d5\u30ed\u30fc\u3092\u4f7f\u7528\u3057\u3066\u30c7\u30d0\u30a4\u30b9\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3067\u304d\u307e\u305b\u3093\u3002", "wrong_token": "\u30c1\u30a7\u30c3\u30af\u30b5\u30e0\u30a8\u30e9\u30fc\u3001\u9593\u9055\u3063\u305f\u30c8\u30fc\u30af\u30f3" }, @@ -25,42 +24,19 @@ "cloud_username": "\u30af\u30e9\u30a6\u30c9\u306e\u30e6\u30fc\u30b6\u30fc\u540d", "manual": "\u624b\u52d5\u3067\u8a2d\u5b9a\u3059\u308b(\u975e\u63a8\u5968)" }, - "description": "Xiaomi Miio cloud\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002\u4f7f\u7528\u3059\u308b\u30af\u30e9\u30a6\u30c9\u30b5\u30fc\u30d0\u30fc\u306b\u3064\u3044\u3066\u306f\u3001https://www.openhab.org/addons/bindings/miio/#country-servers \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002", - "title": "Xiaomi Miio\u30c7\u30d0\u30a4\u30b9\u307e\u305f\u306f\u3001Xiaomi Gateway\u306b\u63a5\u7d9a" + "description": "Xiaomi Miio cloud\u306b\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3059\u3002\u4f7f\u7528\u3059\u308b\u30af\u30e9\u30a6\u30c9\u30b5\u30fc\u30d0\u30fc\u306b\u3064\u3044\u3066\u306f\u3001https://www.openhab.org/addons/bindings/miio/#country-servers \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002" }, "connect": { "data": { "model": "\u30c7\u30d0\u30a4\u30b9\u30e2\u30c7\u30eb" - }, - "description": "\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u30e2\u30c7\u30eb\u304b\u3089\u30c7\u30d0\u30a4\u30b9 \u30e2\u30c7\u30eb\u3092\u624b\u52d5\u3067\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Xiaomi Miio\u30c7\u30d0\u30a4\u30b9\u307e\u305f\u306f\u3001Xiaomi Gateway\u306b\u63a5\u7d9a" - }, - "device": { - "data": { - "host": "IP\u30a2\u30c9\u30ec\u30b9", - "model": "\u30c7\u30d0\u30a4\u30b9\u30e2\u30c7\u30eb(\u30aa\u30d7\u30b7\u30e7\u30f3)", - "name": "\u30c7\u30d0\u30a4\u30b9\u306e\u540d\u524d", - "token": "API\u30c8\u30fc\u30af\u30f3" - }, - "description": "32\u6587\u5b57\u306eAPI\u30c8\u30fc\u30af\u30f3\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6ce8\u610f\u4e8b\u9805: \u3053\u306eAPI\u30c8\u30fc\u30af\u30f3\u306f\u3001Xiaomi Aqara\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u3068\u306f\u7570\u306a\u308a\u307e\u3059\u3002", - "title": "Xiaomi Miio\u30c7\u30d0\u30a4\u30b9\u307e\u305f\u306f\u3001Xiaomi Gateway\u306b\u63a5\u7d9a" - }, - "gateway": { - "data": { - "host": "IP\u30a2\u30c9\u30ec\u30b9", - "name": "\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u306e\u540d\u524d", - "token": "API\u30c8\u30fc\u30af\u30f3" - }, - "description": "32\u6587\u5b57\u306eAPI\u30c8\u30fc\u30af\u30f3\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6ce8\u610f\u4e8b\u9805: \u3053\u306eAPI\u30c8\u30fc\u30af\u30f3\u306f\u3001Xiaomi Aqara\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u3068\u306f\u7570\u306a\u308a\u307e\u3059\u3002", - "title": "Xiaomi Gateway\u306b\u63a5\u7d9a" + } }, "manual": { "data": { "host": "IP\u30a2\u30c9\u30ec\u30b9", "token": "API\u30c8\u30fc\u30af\u30f3" }, - "description": "32\u6587\u5b57\u306eAPI\u30c8\u30fc\u30af\u30f3\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6ce8\u610f\u4e8b\u9805: \u3053\u306eAPI\u30c8\u30fc\u30af\u30f3\u306f\u3001Xiaomi Aqara\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u3068\u306f\u7570\u306a\u308a\u307e\u3059\u3002", - "title": "Xiaomi Miio\u30c7\u30d0\u30a4\u30b9\u307e\u305f\u306f\u3001Xiaomi Gateway\u306b\u63a5\u7d9a" + "description": "32\u6587\u5b57\u306eAPI\u30c8\u30fc\u30af\u30f3\u304c\u5fc5\u8981\u306b\u306a\u308a\u307e\u3059\u3002\u624b\u9806\u306f\u3001https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u6ce8\u610f\u4e8b\u9805: \u3053\u306eAPI\u30c8\u30fc\u30af\u30f3\u306f\u3001Xiaomi Aqara\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u3068\u306f\u7570\u306a\u308a\u307e\u3059\u3002" }, "reauth_confirm": { "description": "Xiaomi Miio\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c8\u30fc\u30af\u30f3\u3092\u66f4\u65b0\u3057\u305f\u308a\u3001\u4e0d\u8db3\u3057\u3066\u3044\u308b\u30af\u30e9\u30a6\u30c9\u306e\u8a8d\u8a3c\u60c5\u5831\u3092\u8ffd\u52a0\u3059\u308b\u305f\u3081\u306b\u3001\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio\u30c7\u30d0\u30a4\u30b9" }, - "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3059\u308bXiaomi Miio\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Xiaomi Miio\u30c7\u30d0\u30a4\u30b9\u307e\u305f\u306f\u3001Xiaomi Gateway\u306b\u63a5\u7d9a" - }, - "user": { - "data": { - "gateway": "Xiaomi Gateway\u306b\u63a5\u7d9a" - }, - "description": "\u63a5\u7d9a\u3059\u308b\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u307e\u3059\u3002", - "title": "Xiaomi Miio" + "description": "\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u3059\u308bXiaomi Miio\u30c7\u30d0\u30a4\u30b9\u3092\u9078\u629e\u3057\u307e\u3059\u3002" } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u30af\u30e9\u30a6\u30c9\u3092\u4f7f\u7528\u3057\u3066\u63a5\u7d9a\u3055\u308c\u305f\u30b5\u30d6\u30c7\u30d0\u30a4\u30b9\u3092\u53d6\u5f97\u3059\u308b" - }, - "description": "\u30aa\u30d7\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6307\u5b9a", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/ko.json b/homeassistant/components/xiaomi_miio/translations/ko.json index 03043f92957..2c188fa670b 100644 --- a/homeassistant/components/xiaomi_miio/translations/ko.json +++ b/homeassistant/components/xiaomi_miio/translations/ko.json @@ -6,37 +6,8 @@ }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "no_device_selected": "\uc120\ud0dd\ub41c \uae30\uae30\uac00 \uc5c6\uc2b5\ub2c8\ub2e4. \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", "unknown_device": "\uae30\uae30\uc758 \ubaa8\ub378\uc744 \uc54c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uad6c\uc131 \ud750\ub984\uc5d0\uc11c \uae30\uae30\ub97c \uc124\uc815\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." }, - "flow_title": "Xiaomi Miio: {name}", - "step": { - "device": { - "data": { - "host": "IP \uc8fc\uc18c", - "model": "\uae30\uae30 \ubaa8\ub378 (\uc120\ud0dd \uc0ac\ud56d)", - "name": "\uae30\uae30 \uc774\ub984", - "token": "API \ud1a0\ud070" - }, - "description": "32\uac1c\uc758 \ubb38\uc790\uc5f4\ub85c \uad6c\uc131\ub41c API \ud1a0\ud070\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \uc815\ubcf4\ub294 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \uc744 \ucc38\uc870\ud574\uc8fc\uc138\uc694. \ucc38\uace0\ub85c \uc774 API \ud1a0\ud070\uc740 Xiaomi Aqara \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 \ud0a4\uc640 \ub2e4\ub985\ub2c8\ub2e4.", - "title": "Xiaomi Miio \uae30\uae30 \ub610\ub294 Xiaomi \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud558\uae30" - }, - "gateway": { - "data": { - "host": "IP \uc8fc\uc18c", - "name": "\uac8c\uc774\ud2b8\uc6e8\uc774 \uc774\ub984", - "token": "API \ud1a0\ud070" - }, - "description": "32\uac1c\uc758 \ubb38\uc790\uc5f4\ub85c \uad6c\uc131\ub41c API \ud1a0\ud070\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \uc815\ubcf4\ub294 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \uc744 \ucc38\uc870\ud574\uc8fc\uc138\uc694. \ucc38\uace0\ub85c \uc774 API \ud1a0\ud070\uc740 Xiaomi Aqara \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 \ud0a4\uc640 \ub2e4\ub985\ub2c8\ub2e4.", - "title": "Xiaomi \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0\ud558\uae30" - }, - "user": { - "data": { - "gateway": "Xiaomi \uac8c\uc774\ud2b8\uc6e8\uc774\uc5d0 \uc5f0\uacb0" - }, - "description": "\uc5f0\uacb0\ud560 \uae30\uae30\ub97c \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", - "title": "Xiaomi Miio" - } - } + "flow_title": "Xiaomi Miio: {name}" } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/lb.json b/homeassistant/components/xiaomi_miio/translations/lb.json index 67e286d4fa1..9a747e8e315 100644 --- a/homeassistant/components/xiaomi_miio/translations/lb.json +++ b/homeassistant/components/xiaomi_miio/translations/lb.json @@ -5,27 +5,8 @@ "already_in_progress": "Konfiguratioun's Oflaf ass schonn am gaangen." }, "error": { - "cannot_connect": "Feeler beim verbannen", - "no_device_selected": "Keen Apparat ausgewielt, wiel een Apparat aus w.e.g." + "cannot_connect": "Feeler beim verbannen" }, - "flow_title": "Xiaomi Miio: {name}", - "step": { - "gateway": { - "data": { - "host": "IP Adresse", - "name": "Numm vum Gateway", - "token": "API Jeton" - }, - "description": "Du brauchs den 32 stellegen API Jeton, kuck https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token fir Instruktiounen. Remarque, d\u00ebst ass een aaneren API Jeton w\u00e9i en vun der Xiaomi Aqara Integratioun benotzt g\u00ebtt.", - "title": "Mat enger Xiaomi Gateway verbannen" - }, - "user": { - "data": { - "gateway": "Mat enger Xiaomi Gateway verbannen" - }, - "description": "Wielt den Apparat aus dee soll verbonne ginn", - "title": "Xiaomi Miio" - } - } + "flow_title": "Xiaomi Miio: {name}" } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/nl.json b/homeassistant/components/xiaomi_miio/translations/nl.json index 8ef2ef70fae..219f7da0b4f 100644 --- a/homeassistant/components/xiaomi_miio/translations/nl.json +++ b/homeassistant/components/xiaomi_miio/translations/nl.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Cloud-inloggegevens onvolledig, vul gebruikersnaam, wachtwoord en land in", "cloud_login_error": "Kan niet inloggen op Xioami Miio Cloud, controleer de inloggegevens.", "cloud_no_devices": "Geen apparaten gevonden in dit Xiaomi Miio-cloudaccount.", - "no_device_selected": "Geen apparaat geselecteerd, selecteer 1 apparaat alstublieft", "unknown_device": "Het apparaatmodel is niet bekend, niet in staat om het apparaat in te stellen met config flow.", "wrong_token": "Checksum-fout, verkeerd token" }, @@ -25,42 +24,19 @@ "cloud_username": "Cloud gebruikersnaam", "manual": "Handmatig configureren (niet aanbevolen)" }, - "description": "Log in op de Xiaomi Miio-cloud, zie https://www.openhab.org/addons/bindings/miio/#country-servers voor de te gebruiken cloudserver.", - "title": "Maak verbinding met een Xiaomi Miio-apparaat of Xiaomi Gateway" + "description": "Log in op de Xiaomi Miio-cloud, zie https://www.openhab.org/addons/bindings/miio/#country-servers voor de te gebruiken cloudserver." }, "connect": { "data": { "model": "Apparaatmodel" - }, - "description": "Selecteer handmatig het apparaatmodel uit de ondersteunde modellen.", - "title": "Maak verbinding met een Xiaomi Miio-apparaat of Xiaomi Gateway" - }, - "device": { - "data": { - "host": "IP-adres", - "model": "Apparaatmodel (Optioneel)", - "name": "Naam van het apparaat", - "token": "API-token" - }, - "description": "U hebt de 32 karakter API-token nodig, zie https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token voor instructies. Let op, deze API-token is anders dan de sleutel die wordt gebruikt door de Xiaomi Aqara integratie.", - "title": "Verbinding maken met een Xiaomi Miio-apparaat of Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "IP-adres", - "name": "Naam van de gateway", - "token": "API-token" - }, - "description": "U heeft het API-token nodig, zie https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token voor instructies.", - "title": "Maak verbinding met een Xiaomi Gateway" + } }, "manual": { "data": { "host": "IP-adres", "token": "API-token" }, - "description": "U hebt het 32-teken API-token , zie https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token voor instructies. Houd er rekening mee dat deze API-token verschilt van de sleutel die wordt gebruikt door de Xiaomi Aqara-integratie.", - "title": "Maak verbinding met een Xiaomi Miio-apparaat of Xiaomi Gateway" + "description": "U hebt het 32-teken API-token , zie https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token voor instructies. Houd er rekening mee dat deze API-token verschilt van de sleutel die wordt gebruikt door de Xiaomi Aqara-integratie." }, "reauth_confirm": { "description": "De Xiaomi Miio-integratie moet uw account opnieuw verifi\u00ebren om de tokens bij te werken of ontbrekende cloudreferenties toe te voegen.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio-apparaat" }, - "description": "Selecteer het Xiaomi Miio apparaat dat u wilt instellen.", - "title": "Maak verbinding met een Xiaomi Miio-apparaat of Xiaomi-gateway" - }, - "user": { - "data": { - "gateway": "Maak verbinding met een Xiaomi Gateway" - }, - "description": "Selecteer het apparaat waarmee u verbinding wilt maken", - "title": "Xiaomi Miio" + "description": "Selecteer het Xiaomi Miio apparaat dat u wilt instellen." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Gebruik de cloud om aangesloten subapparaten te krijgen" - }, - "description": "Optionele instellingen opgeven", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/no.json b/homeassistant/components/xiaomi_miio/translations/no.json index 9094cce023a..3d831df207c 100644 --- a/homeassistant/components/xiaomi_miio/translations/no.json +++ b/homeassistant/components/xiaomi_miio/translations/no.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Utskriftsinformasjon for skyen er fullstendig. Fyll ut brukernavn, passord og land", "cloud_login_error": "Kunne ikke logge inn p\u00e5 Xiaomi Miio Cloud, sjekk legitimasjonen.", "cloud_no_devices": "Ingen enheter funnet i denne Xiaomi Miio-skykontoen.", - "no_device_selected": "Ingen enhet valgt, vennligst velg en enhet.", "unknown_device": "Enhetsmodellen er ikke kjent, kan ikke konfigurere enheten ved hjelp av konfigurasjonsflyt.", "wrong_token": "Kontrollsumfeil, feil token" }, @@ -25,42 +24,19 @@ "cloud_username": "Brukernavn i skyen", "manual": "Konfigurer manuelt (anbefales ikke)" }, - "description": "Logg deg p\u00e5 Xiaomi Miio-skyen, se https://www.openhab.org/addons/bindings/miio/#country-servers for skyserveren du kan bruke.", - "title": "Koble til en Xiaomi Miio-enhet eller Xiaomi Gateway" + "description": "Logg deg p\u00e5 Xiaomi Miio-skyen, se https://www.openhab.org/addons/bindings/miio/#country-servers for skyserveren du kan bruke." }, "connect": { "data": { "model": "Enhetsmodell" - }, - "description": "Velg enhetsmodellen manuelt fra de st\u00f8ttede modellene.", - "title": "Koble til en Xiaomi Miio-enhet eller Xiaomi Gateway" - }, - "device": { - "data": { - "host": "IP adresse", - "model": "Enhetsmodell (valgfritt)", - "name": "Navnet p\u00e5 enheten", - "token": "API-token" - }, - "description": "Du trenger 32 tegn API-token , se https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instruksjoner. V\u00e6r oppmerksom p\u00e5 at denne API-token er forskjellig fra n\u00f8kkelen som brukes av Xiaomi Aqara-integrasjonen.", - "title": "Koble til en Xiaomi Miio-enhet eller Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "IP adresse", - "name": "Navnet p\u00e5 gatewayen", - "token": "API-token" - }, - "description": "Du trenger 32 tegn API-token , se https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token for instruksjoner. V\u00e6r oppmerksom p\u00e5 at denne API-token er forskjellig fra n\u00f8kkelen som brukes av Xiaomi Aqara-integrasjonen.", - "title": "Koble til en Xiaomi Gateway" + } }, "manual": { "data": { "host": "IP adresse", "token": "API-token" }, - "description": "Du trenger 32 tegn API-token , se https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instruksjoner. V\u00e6r oppmerksom p\u00e5 at denne API-token er forskjellig fra n\u00f8kkelen som brukes av Xiaomi Aqara-integrasjonen.", - "title": "Koble til en Xiaomi Miio-enhet eller Xiaomi Gateway" + "description": "Du trenger 32 tegn API-token , se https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token for instruksjoner. V\u00e6r oppmerksom p\u00e5 at denne API-token er forskjellig fra n\u00f8kkelen som brukes av Xiaomi Aqara-integrasjonen." }, "reauth_confirm": { "description": "Xiaomi Miio-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt for \u00e5 oppdatere tokens eller legge til manglende skylegitimasjon.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio-enhet" }, - "description": "Velg Xiaomi Miio-enheten du vil installere.", - "title": "Koble til en Xiaomi Miio-enhet eller Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Koble til en Xiaomi Gateway" - }, - "description": "Velg hvilken enhet du vil koble til.", - "title": "" + "description": "Velg Xiaomi Miio-enheten du vil installere." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Bruk skyen for \u00e5 f\u00e5 tilkoblede underenheter" - }, - "description": "Spesifiser valgfrie innstillinger", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/pl.json b/homeassistant/components/xiaomi_miio/translations/pl.json index 60c82020a77..d0f3cc9a4a8 100644 --- a/homeassistant/components/xiaomi_miio/translations/pl.json +++ b/homeassistant/components/xiaomi_miio/translations/pl.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Dane logowania do chmury niekompletne, prosz\u0119 poda\u0107 nazw\u0119 u\u017cytkownika, has\u0142o i kraj", "cloud_login_error": "Nie mo\u017cna zalogowa\u0107 si\u0119 do chmury Xiaomi Miio, sprawd\u017a po\u015bwiadczenia.", "cloud_no_devices": "Na tym koncie Xiaomi Miio nie znaleziono \u017cadnych urz\u0105dze\u0144.", - "no_device_selected": "Nie wybrano \u017cadnego urz\u0105dzenia, wybierz jedno urz\u0105dzenie", "unknown_device": "Model urz\u0105dzenia nie jest znany, nie mo\u017cna skonfigurowa\u0107 urz\u0105dzenia przy u\u017cyciu interfejsu u\u017cytkownika.", "wrong_token": "B\u0142\u0105d sumy kontrolnej, niew\u0142a\u015bciwy token" }, @@ -25,42 +24,19 @@ "cloud_username": "Nazwa u\u017cytkownika do chmury", "manual": "Skonfiguruj r\u0119cznie (niezalecane)" }, - "description": "Zaloguj si\u0119 do chmury Xiaomi Miio, zobacz https://www.openhab.org/addons/bindings/miio/#country-servers, aby zobaczy\u0107, kt\u00f3rego serwera u\u017cy\u0107.", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi lub urz\u0105dzeniem Xiaomi Miio" + "description": "Zaloguj si\u0119 do chmury Xiaomi Miio, zobacz https://www.openhab.org/addons/bindings/miio/#country-servers, aby zobaczy\u0107, kt\u00f3rego serwera u\u017cy\u0107." }, "connect": { "data": { "model": "Model urz\u0105dzenia" - }, - "description": "Wybierz r\u0119cznie model urz\u0105dzenia z listy obs\u0142ugiwanych modeli.", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi lub urz\u0105dzeniem Xiaomi Miio" - }, - "device": { - "data": { - "host": "Adres IP", - "model": "Model urz\u0105dzenia (opcjonalnie)", - "name": "Nazwa urz\u0105dzenia", - "token": "Token API" - }, - "description": "B\u0119dziesz potrzebowa\u0107 tokenu API (32 znaki), odwied\u017a https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token, aby uzyska\u0107 instrukcje. Zauwa\u017c i\u017c jest to inny token ni\u017c w integracji Xiaomi Aqara.", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi lub urz\u0105dzeniem Xiaomi Miio" - }, - "gateway": { - "data": { - "host": "Adres IP", - "name": "Nazwa bramki", - "token": "Token API" - }, - "description": "B\u0119dziesz potrzebowa\u0107 tokenu API (32 znaki), odwied\u017a https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token, aby uzyska\u0107 instrukcje. Zauwa\u017c i\u017c jest to inny token ni\u017c w integracji Xiaomi Aqara .", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi" + } }, "manual": { "data": { "host": "Adres IP", "token": "Token API" }, - "description": "B\u0119dziesz potrzebowa\u0107 tokenu API (32-znaki), zobacz https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token, aby uzyska\u0107 instrukcje. Pami\u0119taj, \u017ce ten token API r\u00f3\u017cni si\u0119 od klucza u\u017cywanego przez integracj\u0119 Xiaomi Aqara.", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi lub urz\u0105dzeniem Xiaomi Miio" + "description": "B\u0119dziesz potrzebowa\u0107 tokenu API (32-znaki), zobacz https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token, aby uzyska\u0107 instrukcje. Pami\u0119taj, \u017ce ten token API r\u00f3\u017cni si\u0119 od klucza u\u017cywanego przez integracj\u0119 Xiaomi Aqara." }, "reauth_confirm": { "description": "Integracja Xiaomi Miio wymaga ponownego uwierzytelnienia Twoje konta, aby zaktualizowa\u0107 tokeny lub doda\u0107 brakuj\u0105ce dane uwierzytelniaj\u0105ce do chmury.", @@ -70,15 +46,7 @@ "data": { "select_device": "Urz\u0105dzenie Miio" }, - "description": "Wybierz urz\u0105dzenie Xiaomi Miio do skonfigurowania.", - "title": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi lub urz\u0105dzeniem Xiaomi Miio" - }, - "user": { - "data": { - "gateway": "Po\u0142\u0105czenie z bramk\u0105 Xiaomi" - }, - "description": "Wybierz urz\u0105dzenie, z kt\u00f3rym chcesz si\u0119 po\u0142\u0105czy\u0107.", - "title": "Xiaomi Miio" + "description": "Wybierz urz\u0105dzenie Xiaomi Miio do skonfigurowania." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "U\u017cyj chmury, aby uzyska\u0107 pod\u0142\u0105czone podurz\u0105dzenia" - }, - "description": "Ustawienia opcjonalne", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/pt-BR.json b/homeassistant/components/xiaomi_miio/translations/pt-BR.json index 1116de258fa..ce67173f9b2 100644 --- a/homeassistant/components/xiaomi_miio/translations/pt-BR.json +++ b/homeassistant/components/xiaomi_miio/translations/pt-BR.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Credenciais da nuvem incompletas, preencha o nome de usu\u00e1rio, a senha e o pa\u00eds", "cloud_login_error": "N\u00e3o foi poss\u00edvel fazer login no Xiaomi Miio Cloud, verifique as credenciais.", "cloud_no_devices": "Nenhum dispositivo encontrado nesta conta de nuvem Xiaomi Miio.", - "no_device_selected": "Nenhum dispositivo selecionado, selecione um dispositivo.", "unknown_device": "O modelo do dispositivo n\u00e3o \u00e9 conhecido, n\u00e3o \u00e9 poss\u00edvel configurar o dispositivo usando o fluxo de configura\u00e7\u00e3o.", "wrong_token": "Erro de checksum, token errado" }, @@ -25,42 +24,19 @@ "cloud_username": "Usu\u00e1rio da Cloud", "manual": "Configurar manualmente (n\u00e3o recomendado)" }, - "description": "Coloque o login da Cloud Xiaomi Miio e consulte https://www.openhab.org/addons/bindings/miio/#country-servers para saber qual servidor cloud usar.", - "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" + "description": "Coloque o login da Cloud Xiaomi Miio e consulte https://www.openhab.org/addons/bindings/miio/#country-servers para saber qual servidor cloud usar." }, "connect": { "data": { "model": "Modelo do dispositivo" - }, - "description": "Selecione manualmente o modelo do dispositivo entre os modelos suportados.", - "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" - }, - "device": { - "data": { - "host": "Endere\u00e7o IP", - "model": "Modelo do dispositivo (opcional)", - "name": "Nome do dispositivo", - "token": "Token da API" - }, - "description": "Voc\u00ea precisar\u00e1 do Token da API com 32 caracteres, consulte https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token para obter instru\u00e7\u00f5es. Observe que o Token da API \u00e9 diferente da chave usada pela integra\u00e7\u00e3o Xiaomi Aqara.", - "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" - }, - "gateway": { - "data": { - "host": "Endere\u00e7o IP", - "name": "Nome do Gateway", - "token": "Token da API" - }, - "description": "Voc\u00ea precisar\u00e1 do Token da API com 32 caracteres, consulte https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token para obter instru\u00e7\u00f5es. Observe que o Token da API \u00e9 diferente da chave usada pela integra\u00e7\u00e3o Xiaomi Aqara.", - "title": "Conecte-se a um Xiaomi Gateway" + } }, "manual": { "data": { "host": "Endere\u00e7o IP", "token": "Token da API" }, - "description": "Voc\u00ea precisar\u00e1 do Token da API com 32 caracteres, consulte https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token para obter instru\u00e7\u00f5es. Observe que o Token da API \u00e9 diferente da chave usada pela integra\u00e7\u00e3o Xiaomi Aqara.", - "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" + "description": "Voc\u00ea precisar\u00e1 do Token da API com 32 caracteres, consulte https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token para obter instru\u00e7\u00f5es. Observe que o Token da API \u00e9 diferente da chave usada pela integra\u00e7\u00e3o Xiaomi Aqara." }, "reauth_confirm": { "description": "A integra\u00e7\u00e3o do Xiaomi Miio precisa autenticar novamente sua conta para atualizar os tokens ou adicionar credenciais de nuvem ausentes.", @@ -70,15 +46,7 @@ "data": { "select_device": "Dispositivo Miio" }, - "description": "Selecione o dispositivo Xiaomi Miio para configurar.", - "title": "Conecte-se a um dispositivo Xiaomi Miio ou Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Conecte-se a um Xiaomi Gateway" - }, - "description": "Selecione a qual dispositivo voc\u00ea deseja se conectar.", - "title": "Xiaomi Miio" + "description": "Selecione o dispositivo Xiaomi Miio para configurar." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Use a cloud para obter subdispositivos conectados" - }, - "description": "Especificar configura\u00e7\u00f5es opcionais", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/pt.json b/homeassistant/components/xiaomi_miio/translations/pt.json index 922c2441c3d..db0e0c2a137 100644 --- a/homeassistant/components/xiaomi_miio/translations/pt.json +++ b/homeassistant/components/xiaomi_miio/translations/pt.json @@ -5,20 +5,6 @@ }, "error": { "cannot_connect": "Falha na liga\u00e7\u00e3o" - }, - "step": { - "device": { - "data": { - "host": "Endere\u00e7o IP", - "token": "API Token" - } - }, - "gateway": { - "data": { - "host": "Endere\u00e7o IP", - "token": "API Token" - } - } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/ru.json b/homeassistant/components/xiaomi_miio/translations/ru.json index 18bbc4271ad..1432666cb44 100644 --- a/homeassistant/components/xiaomi_miio/translations/ru.json +++ b/homeassistant/components/xiaomi_miio/translations/ru.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u0423\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043e\u0431\u043b\u0430\u043a\u0435 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435. \u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u0441\u0442\u0440\u0430\u043d\u0443.", "cloud_login_error": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u043e\u0439\u0442\u0438 \u0432 Xiaomi Miio Cloud, \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.", "cloud_no_devices": "\u0412 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0439 \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Xiaomi Miio \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u044b.", - "no_device_selected": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0434\u043d\u043e \u0438\u0437 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432.", "unknown_device": "\u041c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430, \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0430\u0441\u0442\u0435\u0440\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.", "wrong_token": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b, \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d." }, @@ -25,42 +24,19 @@ "cloud_username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043b\u044f \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430", "manual": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u0440\u0443\u0447\u043d\u0443\u044e (\u043d\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f)" }, - "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e Xiaomi Miio, \u0441\u043c. https://www.openhab.org/addons/bindings/miio/#country-servers \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Xiaomi Miio \u0438\u043b\u0438 \u0448\u043b\u044e\u0437\u0443 Xiaomi" + "description": "\u0412\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e Xiaomi Miio, \u0441\u043c. https://www.openhab.org/addons/bindings/miio/#country-servers \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0431\u043b\u0430\u0447\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430." }, "connect": { "data": { "model": "\u041c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Xiaomi Miio \u0438\u043b\u0438 \u0448\u043b\u044e\u0437\u0443 Xiaomi" - }, - "device": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441", - "model": "\u041c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", - "token": "\u0422\u043e\u043a\u0435\u043d API" - }, - "description": "\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f 32-\u0445 \u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u0422\u043e\u043a\u0435\u043d API. \u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0437\u0434\u0435\u0441\u044c: \nhttps://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token.\n\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043a\u043b\u044e\u0447\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Xiaomi Aqara.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Xiaomi Miio \u0438\u043b\u0438 \u0448\u043b\u044e\u0437\u0443 Xiaomi" - }, - "gateway": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", - "token": "\u0422\u043e\u043a\u0435\u043d API" - }, - "description": "\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f 32-\u0445 \u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u0422\u043e\u043a\u0435\u043d API. \u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0437\u0434\u0435\u0441\u044c: \nhttps://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token.\n\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043a\u043b\u044e\u0447\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Xiaomi Aqara.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0448\u043b\u044e\u0437\u0443 Xiaomi" + } }, "manual": { "data": { "host": "IP-\u0430\u0434\u0440\u0435\u0441", "token": "\u0422\u043e\u043a\u0435\u043d API" }, - "description": "\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f 32-\u0445 \u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u0422\u043e\u043a\u0435\u043d API. \u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0437\u0434\u0435\u0441\u044c: \nhttps://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token\n\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043a\u043b\u044e\u0447\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Xiaomi Aqara.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Xiaomi Miio \u0438\u043b\u0438 \u0448\u043b\u044e\u0437\u0443 Xiaomi" + "description": "\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f 32-\u0445 \u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u0422\u043e\u043a\u0435\u043d API. \u041e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0437\u043d\u0430\u0442\u044c \u0437\u0434\u0435\u0441\u044c: \nhttps://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token\n\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u043a\u043b\u044e\u0447\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Xiaomi Aqara." }, "reauth_confirm": { "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Xiaomi Miio, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d\u044b \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0443\u0447\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430", @@ -70,15 +46,7 @@ "data": { "select_device": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Miio" }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Xiaomi Miio \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.", - "title": "\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 Xiaomi Miio \u0438\u043b\u0438 \u0448\u043b\u044e\u0437\u0443 Xiaomi" - }, - "user": { - "data": { - "gateway": "\u0428\u043b\u044e\u0437 Xiaomi" - }, - "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c.", - "title": "Xiaomi Miio" + "description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e Xiaomi Miio \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u043b\u0430\u043a\u043e \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432" - }, - "description": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/sk.json b/homeassistant/components/xiaomi_miio/translations/sk.json index 022e4103fb7..fca4223f4ac 100644 --- a/homeassistant/components/xiaomi_miio/translations/sk.json +++ b/homeassistant/components/xiaomi_miio/translations/sk.json @@ -6,16 +6,6 @@ "reauth_successful": "Op\u00e4tovn\u00e9 overenie bolo \u00faspe\u0161n\u00e9" }, "step": { - "device": { - "data": { - "token": "API token" - } - }, - "gateway": { - "data": { - "token": "API token" - } - }, "manual": { "data": { "token": "API token" diff --git a/homeassistant/components/xiaomi_miio/translations/sl.json b/homeassistant/components/xiaomi_miio/translations/sl.json index 472317182bf..8596992fe39 100644 --- a/homeassistant/components/xiaomi_miio/translations/sl.json +++ b/homeassistant/components/xiaomi_miio/translations/sl.json @@ -4,26 +4,7 @@ "already_configured": "Naprava je \u017ee konfigurirana" }, "error": { - "no_device_selected": "Izbrana ni nobena naprava, izberite eno napravo.", "wrong_token": "Napaka kontrolne vsote, napa\u010den \u017eeton" - }, - "step": { - "gateway": { - "data": { - "host": "IP naslov", - "name": "Ime prehoda", - "token": "API \u017eeton" - }, - "description": "Potrebujete API \u017deton, glej https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token za navodila.", - "title": "Pove\u017eite se s prehodom Xiaomi" - }, - "user": { - "data": { - "gateway": "Pove\u017eite se s prehodom Xiaomi" - }, - "description": "Izberite, s katero napravo se \u017eelite povezati.", - "title": "Xiaomi Miio" - } } } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/sv.json b/homeassistant/components/xiaomi_miio/translations/sv.json deleted file mode 100644 index fcd6e641091..00000000000 --- a/homeassistant/components/xiaomi_miio/translations/sv.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "config": { - "error": { - "no_device_selected": "Ingen enhet har valts, v\u00e4lj en enhet." - }, - "step": { - "gateway": { - "data": { - "host": "IP-adress", - "name": "Namnet p\u00e5 Gatewayen", - "token": "API Token" - }, - "description": "Du beh\u00f6ver en API token, se https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token f\u00f6r mer instruktioner.", - "title": "Anslut till en Xiaomi Gateway" - }, - "user": { - "data": { - "gateway": "Anslut till en Xiaomi Gateway" - }, - "description": "V\u00e4lj den enhet som du vill ansluta till." - } - } - } -} \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/tr.json b/homeassistant/components/xiaomi_miio/translations/tr.json index 097ccdab6ac..8c81f08ee9f 100644 --- a/homeassistant/components/xiaomi_miio/translations/tr.json +++ b/homeassistant/components/xiaomi_miio/translations/tr.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "Bulut kimlik bilgileri eksik, l\u00fctfen kullan\u0131c\u0131 ad\u0131n\u0131, \u015fifreyi ve \u00fclkeyi girin", "cloud_login_error": "Xiaomi Miio Cloud'da oturum a\u00e7\u0131lamad\u0131, kimlik bilgilerini kontrol edin.", "cloud_no_devices": "Bu Xiaomi Miio bulut hesab\u0131nda cihaz bulunamad\u0131.", - "no_device_selected": "Cihaz se\u00e7ilmedi, l\u00fctfen bir cihaz se\u00e7in.", "unknown_device": "Cihaz modeli bilinmiyor, cihaz yap\u0131land\u0131rma ak\u0131\u015f\u0131n\u0131 kullanarak kurulam\u0131yor.", "wrong_token": "Sa\u011flama toplam\u0131 hatas\u0131, yanl\u0131\u015f anahtar" }, @@ -25,42 +24,19 @@ "cloud_username": "Bulut kullan\u0131c\u0131 ad\u0131", "manual": "Manuel olarak yap\u0131land\u0131r\u0131n (\u00f6nerilmez)" }, - "description": "Xiaomi Miio bulutunda oturum a\u00e7\u0131n, bulut sunucusunun kullanmas\u0131 i\u00e7in https://www.openhab.org/addons/bindings/miio/#country-servers adresine bak\u0131n.", - "title": "Bir Xiaomi Miio Cihaz\u0131na veya Xiaomi A\u011f Ge\u00e7idine Ba\u011flan" + "description": "Xiaomi Miio bulutunda oturum a\u00e7\u0131n, bulut sunucusunun kullanmas\u0131 i\u00e7in https://www.openhab.org/addons/bindings/miio/#country-servers adresine bak\u0131n." }, "connect": { "data": { "model": "Cihaz modeli" - }, - "description": "Desteklenen modellerden cihaz modelini manuel olarak se\u00e7in.", - "title": "Bir Xiaomi Miio Cihaz\u0131na veya Xiaomi A\u011f Ge\u00e7idine Ba\u011flan" - }, - "device": { - "data": { - "host": "IP Adresi", - "model": "Cihaz modeli (Opsiyonel)", - "name": "Cihaz\u0131n ad\u0131", - "token": "API Anahtar\u0131" - }, - "description": "32 karaktere API Anahtar\u0131 , talimatlar i\u00e7in https://www.home-assistant.io/integrations/xiaomi_miio#retriiving-the-access-token adresine bak\u0131n. L\u00fctfen bu API Anahtar\u0131 \u00f6\u011fesinin Xiaomi Aqara entegrasyonu taraf\u0131ndan kullan\u0131lan anahtardan farkl\u0131 oldu\u011funu unutmay\u0131n.", - "title": "Bir Xiaomi Miio Cihaz\u0131na veya Xiaomi A\u011f Ge\u00e7idine Ba\u011flan" - }, - "gateway": { - "data": { - "host": "\u0130p Adresi", - "name": "A\u011f Ge\u00e7idinin Ad\u0131", - "token": "API Belirteci" - }, - "description": "32 karaktere API Anahtar\u0131 , bkz. talimatlar i\u00e7in. L\u00fctfen bu API Anahtar\u0131 \u00f6\u011fesinin Xiaomi Aqara entegrasyonu taraf\u0131ndan kullan\u0131lan anahtardan farkl\u0131 oldu\u011funu unutmay\u0131n.", - "title": "Bir Xiaomi A\u011f Ge\u00e7idine ba\u011flan\u0131n" + } }, "manual": { "data": { "host": "IP Adresi", "token": "API Anahtar\u0131" }, - "description": "32 karaktere API Anahtar\u0131 , talimatlar i\u00e7in https://www.home-assistant.io/integrations/xiaomi_miio#retriiving-the-access-token adresine bak\u0131n. L\u00fctfen bu API Anahtar\u0131 \u00f6\u011fesinin Xiaomi Aqara entegrasyonu taraf\u0131ndan kullan\u0131lan anahtardan farkl\u0131 oldu\u011funu unutmay\u0131n.", - "title": "Bir Xiaomi Miio Cihaz\u0131na veya Xiaomi A\u011f Ge\u00e7idine Ba\u011flan" + "description": "32 karaktere API Anahtar\u0131 , talimatlar i\u00e7in https://www.home-assistant.io/integrations/xiaomi_miio#retriiving-the-access-token adresine bak\u0131n. L\u00fctfen bu API Anahtar\u0131 \u00f6\u011fesinin Xiaomi Aqara entegrasyonu taraf\u0131ndan kullan\u0131lan anahtardan farkl\u0131 oldu\u011funu unutmay\u0131n." }, "reauth_confirm": { "description": "Anahtarlar\u0131 g\u00fcncellemek veya eksik bulut kimlik bilgilerini eklemek i\u00e7in Xiaomi Miio entegrasyonunun hesab\u0131n\u0131z\u0131 yeniden do\u011frulamas\u0131 gerekir.", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio cihaz\u0131" }, - "description": "Kurulumu i\u00e7in Xiaomi Miio cihaz\u0131n\u0131 se\u00e7in.", - "title": "Bir Xiaomi Miio Cihaz\u0131na veya Xiaomi A\u011f Ge\u00e7idine Ba\u011flan" - }, - "user": { - "data": { - "gateway": "Bir Xiaomi A\u011f Ge\u00e7idine ba\u011flan\u0131n" - }, - "description": "Hangi cihaza ba\u011flanmak istedi\u011finizi se\u00e7in.", - "title": "Xiaomi Miio" + "description": "Kurulumu i\u00e7in Xiaomi Miio cihaz\u0131n\u0131 se\u00e7in." } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "Ba\u011fl\u0131 alt cihazlar almak i\u00e7in bulutu kullan\u0131n" - }, - "description": "\u0130ste\u011fe ba\u011fl\u0131 ayarlar\u0131 belirtin", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/uk.json b/homeassistant/components/xiaomi_miio/translations/uk.json index f32105589f6..bf1b8126e38 100644 --- a/homeassistant/components/xiaomi_miio/translations/uk.json +++ b/homeassistant/components/xiaomi_miio/translations/uk.json @@ -5,27 +5,8 @@ "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0436\u0435 \u0442\u0440\u0438\u0432\u0430\u0454." }, "error": { - "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f", - "no_device_selected": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043e\u0434\u0438\u043d \u0437 \u043f\u0440\u0438\u0441\u0442\u0440\u043e\u0457\u0432." + "cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f" }, - "flow_title": "Xiaomi Miio: {name}", - "step": { - "gateway": { - "data": { - "host": "IP-\u0430\u0434\u0440\u0435\u0441\u0430", - "name": "\u041d\u0430\u0437\u0432\u0430", - "token": "\u0422\u043e\u043a\u0435\u043d API" - }, - "description": "\u0414\u043b\u044f \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u043f\u043e\u0442\u0440\u0456\u0431\u043d\u043e 32-\u0445 \u0437\u043d\u0430\u0447\u043d\u0438\u0439 \u0422\u043e\u043a\u0435\u043d API . \u041f\u0440\u043e \u0442\u0435, \u044f\u043a \u043e\u0442\u0440\u0438\u043c\u0430\u0442\u0438 \u0442\u043e\u043a\u0435\u043d, \u0412\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0456\u0437\u043d\u0430\u0442\u0438\u0441\u044f \u0442\u0443\u0442:\nhttps://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token.\n\u0417\u0432\u0435\u0440\u043d\u0456\u0442\u044c \u0443\u0432\u0430\u0433\u0443, \u0449\u043e \u0446\u0435\u0439 \u0442\u043e\u043a\u0435\u043d \u0432\u0456\u0434\u0440\u0456\u0437\u043d\u044f\u0454\u0442\u044c\u0441\u044f \u0432\u0456\u0434 \u043a\u043b\u044e\u0447\u0430, \u044f\u043a\u0438\u0439 \u0432\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0454\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457 Xiaomi Aqara.", - "title": "\u041f\u0456\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044f \u0434\u043e \u0448\u043b\u044e\u0437\u0443 Xiaomi" - }, - "user": { - "data": { - "gateway": "\u0428\u043b\u044e\u0437 Xiaomi" - }, - "description": "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0438\u0441\u0442\u0440\u0456\u0439, \u044f\u043a\u0438\u0439 \u0412\u0438 \u0445\u043e\u0447\u0435\u0442\u0435 \u043f\u0456\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0438.", - "title": "Xiaomi Miio" - } - } + "flow_title": "Xiaomi Miio: {name}" } } \ No newline at end of file diff --git a/homeassistant/components/xiaomi_miio/translations/zh-Hans.json b/homeassistant/components/xiaomi_miio/translations/zh-Hans.json index f488618b945..0dda09965fd 100644 --- a/homeassistant/components/xiaomi_miio/translations/zh-Hans.json +++ b/homeassistant/components/xiaomi_miio/translations/zh-Hans.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u4e91\u7aef\u51ed\u636e\u4e0d\u5b8c\u6574\uff0c\u8bf7\u8f93\u5165\u7528\u6237\u540d\u3001\u5bc6\u7801\u548c\u56fd\u5bb6/\u5730\u533a", "cloud_login_error": "\u65e0\u6cd5\u767b\u5f55\u5c0f\u7c73\u4e91\u670d\u52a1\uff0c\u8bf7\u68c0\u67e5\u51ed\u636e\u3002", "cloud_no_devices": "\u672a\u5728\u5c0f\u7c73\u5e10\u6237\u4e2d\u53d1\u73b0\u8bbe\u5907\u3002", - "no_device_selected": "\u672a\u9009\u62e9\u8bbe\u5907\uff0c\u8bf7\u9009\u62e9\u4e00\u4e2a\u8bbe\u5907\u3002", "unknown_device": "\u8be5\u8bbe\u5907\u578b\u53f7\u6682\u672a\u9002\u914d\uff0c\u56e0\u6b64\u65e0\u6cd5\u901a\u8fc7\u914d\u7f6e\u5411\u5bfc\u6dfb\u52a0\u8bbe\u5907\u3002", "wrong_token": "\u6821\u9a8c\u548c\u9519\u8bef\uff0ctoken \u9519\u8bef" }, @@ -25,34 +24,12 @@ "cloud_username": "\u7528\u6237\u540d", "manual": "\u624b\u52a8\u914d\u7f6e\uff08\u4e0d\u63a8\u8350\uff09" }, - "description": "\u767b\u5f55\u5c0f\u7c73\u4e91\u670d\u52a1\u3002\u6709\u5173\u56fd\u5bb6/\u5730\u533a\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 https://www.openhab.org/addons/bindings/miio/#country-servers \u3002", - "title": "\u8fde\u63a5\u5230\u5c0f\u7c73 Miio \u8bbe\u5907\u6216\u5c0f\u7c73\u7f51\u5173" + "description": "\u767b\u5f55\u5c0f\u7c73\u4e91\u670d\u52a1\u3002\u6709\u5173\u56fd\u5bb6/\u5730\u533a\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 https://www.openhab.org/addons/bindings/miio/#country-servers \u3002" }, "connect": { "data": { "model": "\u8bbe\u5907 model" - }, - "description": "\u4ece\u652f\u6301\u7684\u578b\u53f7\u4e2d\u624b\u52a8\u9009\u62e9\u3002", - "title": "\u8fde\u63a5\u5230\u5c0f\u7c73 Miio \u8bbe\u5907\u6216\u5c0f\u7c73\u7f51\u5173" - }, - "device": { - "data": { - "host": "IP \u5730\u5740", - "model": "\u8bbe\u5907 model\uff08\u53ef\u9009\uff09", - "name": "\u8bbe\u5907\u540d\u79f0", - "token": "API Token" - }, - "description": "\u60a8\u9700\u8981\u83b7\u53d6\u4e00\u4e2a 32 \u4f4d\u7684 API Token\u3002\u5982\u9700\u5e2e\u52a9\uff0c\u8bf7\u53c2\u9605\u4ee5\u4e0b\u94fe\u63a5: https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u3002\u8bf7\u6ce8\u610f\u6b64 token \u4e0d\u540c\u4e8e\u201cXiaomi Aqara\u201d\u96c6\u6210\u6240\u9700\u7684 key\u3002", - "title": "\u8fde\u63a5\u5230\u5c0f\u7c73 Miio \u8bbe\u5907\u6216\u5c0f\u7c73\u7f51\u5173" - }, - "gateway": { - "data": { - "host": "IP \u5730\u5740", - "name": "\u7f51\u5173\u540d\u79f0", - "token": "API Token" - }, - "description": "\u60a8\u9700\u8981\u83b7\u53d6\u4e00\u4e2a 32 \u4f4d\u7684 API Token\u3002\u5982\u9700\u5e2e\u52a9\uff0c\u8bf7\u53c2\u9605\u4ee5\u4e0b\u94fe\u63a5: https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u3002\u8bf7\u6ce8\u610f\u6b64 token \u4e0d\u540c\u4e8e\u201cXiaomi Aqara\u201d\u96c6\u6210\u6240\u9700\u7684 key\u3002", - "title": "\u8fde\u63a5\u5230\u5c0f\u7c73\u7f51\u5173" + } }, "manual": { "data": { @@ -69,15 +46,7 @@ "data": { "select_device": "Miio \u8bbe\u5907" }, - "description": "\u9009\u62e9\u8981\u6dfb\u52a0\u7684\u5c0f\u7c73 Miio \u8bbe\u5907\u3002", - "title": "\u8fde\u63a5\u5230\u5c0f\u7c73 Miio \u8bbe\u5907\u6216\u5c0f\u7c73\u7f51\u5173" - }, - "user": { - "data": { - "gateway": "\u8fde\u63a5\u5230\u5c0f\u7c73\u7f51\u5173" - }, - "description": "\u8bf7\u9009\u62e9\u8981\u8fde\u63a5\u7684\u8bbe\u5907\u3002", - "title": "Xiaomi Miio" + "description": "\u9009\u62e9\u8981\u6dfb\u52a0\u7684\u5c0f\u7c73 Miio \u8bbe\u5907\u3002" } } }, @@ -89,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u901a\u8fc7\u4e91\u7aef\u83b7\u53d6\u8fde\u63a5\u7684\u5b50\u8bbe\u5907" - }, - "description": "\u6307\u5b9a\u53ef\u9009\u8bbe\u7f6e", - "title": "Xiaomi Miio" + } } } } diff --git a/homeassistant/components/xiaomi_miio/translations/zh-Hant.json b/homeassistant/components/xiaomi_miio/translations/zh-Hant.json index 2812f91be7e..e38567ea37c 100644 --- a/homeassistant/components/xiaomi_miio/translations/zh-Hant.json +++ b/homeassistant/components/xiaomi_miio/translations/zh-Hant.json @@ -12,7 +12,6 @@ "cloud_credentials_incomplete": "\u96f2\u7aef\u6191\u8b49\u672a\u5b8c\u6210\uff0c\u8acb\u586b\u5beb\u4f7f\u7528\u8005\u540d\u7a31\u3001\u5bc6\u78bc\u8207\u570b\u5bb6", "cloud_login_error": "\u7121\u6cd5\u767b\u5165\u5c0f\u7c73 Miio \u96f2\u670d\u52d9\uff0c\u8acb\u6aa2\u67e5\u6191\u8b49\u3002", "cloud_no_devices": "\u5c0f\u7c73 Miio \u96f2\u7aef\u5e33\u865f\u672a\u627e\u5230\u4efb\u4f55\u88dd\u7f6e\u3002", - "no_device_selected": "\u672a\u9078\u64c7\u88dd\u7f6e\uff0c\u8acb\u9078\u64c7\u4e00\u9805\u88dd\u7f6e\u3002", "unknown_device": "\u88dd\u7f6e\u578b\u865f\u672a\u77e5\uff0c\u7121\u6cd5\u4f7f\u7528\u8a2d\u5b9a\u6d41\u7a0b\u3002", "wrong_token": "\u6838\u5c0d\u548c\u932f\u8aa4\u3001\u6b0a\u6756\u932f\u8aa4" }, @@ -25,42 +24,19 @@ "cloud_username": "\u96f2\u7aef\u670d\u52d9\u4f7f\u7528\u8005\u540d\u7a31", "manual": "\u624b\u52d5\u8a2d\u5b9a (\u4e0d\u5efa\u8b70)" }, - "description": "\u767b\u5165\u81f3\u5c0f\u7c73 Miio \u96f2\u670d\u52d9\uff0c\u8acb\u53c3\u95b1 https://www.openhab.org/addons/bindings/miio/#country-servers \u4ee5\u4e86\u89e3\u9078\u64c7\u54ea\u4e00\u7d44\u96f2\u7aef\u4f3a\u670d\u5668\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73 MIIO \u88dd\u7f6e\u6216\u5c0f\u7c73\u7db2\u95dc" + "description": "\u767b\u5165\u81f3\u5c0f\u7c73 Miio \u96f2\u670d\u52d9\uff0c\u8acb\u53c3\u95b1 https://www.openhab.org/addons/bindings/miio/#country-servers \u4ee5\u4e86\u89e3\u9078\u64c7\u54ea\u4e00\u7d44\u96f2\u7aef\u4f3a\u670d\u5668\u3002" }, "connect": { "data": { "model": "\u88dd\u7f6e\u578b\u865f" - }, - "description": "\u5f9e\u652f\u63f4\u7684\u578b\u865f\u4e2d\u624b\u52d5\u9078\u64c7\u88dd\u7f6e\u578b\u865f\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73 MIIO \u88dd\u7f6e\u6216\u5c0f\u7c73\u7db2\u95dc" - }, - "device": { - "data": { - "host": "IP \u4f4d\u5740", - "model": "\u88dd\u7f6e\u578b\u865f\uff08\u9078\u9805\uff09", - "name": "\u88dd\u7f6e\u540d\u7a31", - "token": "API \u6b0a\u6756" - }, - "description": "\u5c07\u9700\u8981\u8f38\u5165 32 \u4f4d\u5b57\u5143 API \u6b0a\u6756\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u4ee5\u7372\u5f97\u7372\u53d6\u6b0a\u6756\u7684\u6559\u5b78\u3002\u8acb\u6ce8\u610f\uff1a\u6b64 API \u6b0a\u6756\u8207 Xiaomi Aqara \u6574\u5408\u6240\u4f7f\u7528\u4e4b\u6b0a\u6756\u4e0d\u540c\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73 MIIO \u88dd\u7f6e\u6216\u5c0f\u7c73\u7db2\u95dc" - }, - "gateway": { - "data": { - "host": "IP \u4f4d\u5740", - "name": "\u7db2\u95dc\u540d\u7a31", - "token": "API \u6b0a\u6756" - }, - "description": "\u5c07\u9700\u8981\u8f38\u5165 32 \u4f4d\u5b57\u5143 API \u6b0a\u6756\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token \u4ee5\u7372\u5f97\u7372\u53d6\u6b0a\u6756\u7684\u6559\u5b78\u3002\u8acb\u6ce8\u610f\uff1a\u6b64API \u6b0a\u6756\u8207 Xiaomi Aqara \u6574\u5408\u6240\u4f7f\u7528\u4e4b\u6b0a\u6756\u4e0d\u540c\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73\u7db2\u95dc" + } }, "manual": { "data": { "host": "IP \u4f4d\u5740", "token": "API \u6b0a\u6756" }, - "description": "\u5c07\u9700\u8981\u8f38\u5165 32 \u4f4d\u5b57\u5143 API \u6b0a\u6756\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u4ee5\u7372\u5f97\u7372\u53d6\u91d1\u9470\u7684\u6559\u5b78\u3002\u8acb\u6ce8\u610f\uff1a\u6b64 API \u6b0a\u6756\u8207\u5c0f\u7c73 Aqara \u6574\u5408\u6240\u4f7f\u7528\u4e4b\u91d1\u9470\u4e0d\u540c\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73 MIIO \u88dd\u7f6e\u6216\u5c0f\u7c73\u7db2\u95dc" + "description": "\u5c07\u9700\u8981\u8f38\u5165 32 \u4f4d\u5b57\u5143 API \u6b0a\u6756\uff0c\u8acb\u53c3\u95b1 https://www.home-assistant.io/integrations/xiaomi_miio#retrieving-the-access-token \u4ee5\u7372\u5f97\u7372\u53d6\u91d1\u9470\u7684\u6559\u5b78\u3002\u8acb\u6ce8\u610f\uff1a\u6b64 API \u6b0a\u6756\u8207\u5c0f\u7c73 Aqara \u6574\u5408\u6240\u4f7f\u7528\u4e4b\u91d1\u9470\u4e0d\u540c\u3002" }, "reauth_confirm": { "description": "\u5c0f\u7c73 Miio \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f\u3001\u65b9\u80fd\u66f4\u65b0\u6b0a\u6756\u6216\u65b0\u589e\u907a\u5931\u7684\u96f2\u7aef\u6191\u8b49\u3002", @@ -70,15 +46,7 @@ "data": { "select_device": "Miio \u88dd\u7f6e" }, - "description": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684 \u5c0f\u7c73 Miio \u88dd\u7f6e\u3002", - "title": "\u9023\u7dda\u81f3\u5c0f\u7c73 MIIO \u88dd\u7f6e\u6216\u5c0f\u7c73\u7db2\u95dc" - }, - "user": { - "data": { - "gateway": "\u9023\u7dda\u81f3\u5c0f\u7c73\u7db2\u95dc" - }, - "description": "\u9078\u64c7\u6240\u8981\u9023\u7dda\u7684\u88dd\u7f6e\u3002", - "title": "\u5c0f\u7c73 Miio" + "description": "\u9078\u64c7\u6240\u8981\u8a2d\u5b9a\u7684 \u5c0f\u7c73 Miio \u88dd\u7f6e\u3002" } } }, @@ -90,9 +58,7 @@ "init": { "data": { "cloud_subdevices": "\u4f7f\u7528\u96f2\u7aef\u53d6\u5f97\u9023\u7dda\u5b50\u88dd\u7f6e" - }, - "description": "\u6307\u5b9a\u9078\u9805\u8a2d\u5b9a", - "title": "\u5c0f\u7c73 Miio" + } } } } diff --git a/homeassistant/components/yale_smart_alarm/translations/nl.json b/homeassistant/components/yale_smart_alarm/translations/nl.json index 04dbe9245c1..8d8697bbc59 100644 --- a/homeassistant/components/yale_smart_alarm/translations/nl.json +++ b/homeassistant/components/yale_smart_alarm/translations/nl.json @@ -11,7 +11,7 @@ "step": { "reauth_confirm": { "data": { - "area_id": "Area ID", + "area_id": "Area-ID", "name": "Naam", "password": "Wachtwoord", "username": "Gebruikersnaam" @@ -19,7 +19,7 @@ }, "user": { "data": { - "area_id": "Area ID", + "area_id": "Area-ID", "name": "Naam", "password": "Wachtwoord", "username": "Gebruikersnaam" diff --git a/homeassistant/components/yamaha_musiccast/translations/ko.json b/homeassistant/components/yamaha_musiccast/translations/ko.json new file mode 100644 index 00000000000..20ad990e862 --- /dev/null +++ b/homeassistant/components/yamaha_musiccast/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "confirm": { + "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yeelight/translations/ar.json b/homeassistant/components/yeelight/translations/ar.json index e4146138625..5838a65a334 100644 --- a/homeassistant/components/yeelight/translations/ar.json +++ b/homeassistant/components/yeelight/translations/ar.json @@ -12,8 +12,7 @@ "init": { "data": { "use_music_mode": "\u062a\u0645\u0643\u064a\u0646 \u0648\u0636\u0639 \u0627\u0644\u0645\u0648\u0633\u064a\u0642\u0649" - }, - "description": "\u0625\u0630\u0627 \u062a\u0631\u0643\u062a \u0627\u0644\u0646\u0645\u0648\u0630\u062c \u0641\u0627\u0631\u063a\u064b\u0627 \u060c \u0641\u0633\u064a\u062a\u0645 \u0627\u0643\u062a\u0634\u0627\u0641\u0647 \u062a\u0644\u0642\u0627\u0626\u064a\u064b\u0627." + } } } } diff --git a/homeassistant/components/yeelight/translations/bg.json b/homeassistant/components/yeelight/translations/bg.json index a53214e40e4..4a962bbf3d0 100644 --- a/homeassistant/components/yeelight/translations/bg.json +++ b/homeassistant/components/yeelight/translations/bg.json @@ -26,8 +26,7 @@ "init": { "data": { "model": "\u041c\u043e\u0434\u0435\u043b (\u043f\u043e \u0438\u0437\u0431\u043e\u0440)" - }, - "description": "\u0410\u043a\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u0435 \u043c\u043e\u0434\u0435\u043b\u0430 \u043f\u0440\u0430\u0437\u0435\u043d, \u0442\u043e\u0439 \u0449\u0435 \u0431\u044a\u0434\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u0440\u0430\u0437\u043f\u043e\u0437\u043d\u0430\u0442." + } } } } diff --git a/homeassistant/components/yeelight/translations/ca.json b/homeassistant/components/yeelight/translations/ca.json index 0b62bebf1be..882c118b824 100644 --- a/homeassistant/components/yeelight/translations/ca.json +++ b/homeassistant/components/yeelight/translations/ca.json @@ -34,8 +34,7 @@ "save_on_change": "Desa l'estat en canviar", "transition": "Temps de transici\u00f3 (ms)", "use_music_mode": "Activa el mode M\u00fasica" - }, - "description": "Si deixes el model buit, es detectar\u00e0 autom\u00e0ticament." + } } } } diff --git a/homeassistant/components/yeelight/translations/cs.json b/homeassistant/components/yeelight/translations/cs.json index 2a3084fd3eb..5f91548bd4f 100644 --- a/homeassistant/components/yeelight/translations/cs.json +++ b/homeassistant/components/yeelight/translations/cs.json @@ -31,8 +31,7 @@ "save_on_change": "Ulo\u017eit stav p\u0159i zm\u011bn\u011b", "transition": "\u010cas p\u0159echodu (v ms)", "use_music_mode": "Povolit hudebn\u00ed re\u017eim" - }, - "description": "Pokud ponech\u00e1te model pr\u00e1zdn\u00fd, bude automaticky rozpozn\u00e1n." + } } } } diff --git a/homeassistant/components/yeelight/translations/de.json b/homeassistant/components/yeelight/translations/de.json index 5108b075968..a2323edbbdb 100644 --- a/homeassistant/components/yeelight/translations/de.json +++ b/homeassistant/components/yeelight/translations/de.json @@ -34,8 +34,7 @@ "save_on_change": "Status bei \u00c4nderung speichern", "transition": "\u00dcbergangszeit (ms)", "use_music_mode": "Musik-Modus aktivieren" - }, - "description": "Wenn du das Modell leer l\u00e4sst, wird es automatisch erkannt." + } } } } diff --git a/homeassistant/components/yeelight/translations/el.json b/homeassistant/components/yeelight/translations/el.json index 8b6c0ee2b9c..e69edd2cf74 100644 --- a/homeassistant/components/yeelight/translations/el.json +++ b/homeassistant/components/yeelight/translations/el.json @@ -34,8 +34,7 @@ "save_on_change": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac \u03c4\u03b7\u03bd \u03b1\u03bb\u03bb\u03b1\u03b3\u03ae", "transition": "\u03a7\u03c1\u03cc\u03bd\u03bf\u03c2 \u03bc\u03b5\u03c4\u03ac\u03b2\u03b1\u03c3\u03b7\u03c2 (ms)", "use_music_mode": "\u0395\u03bd\u03b5\u03c1\u03b3\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03bb\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae\u03c2" - }, - "description": "\u0395\u03ac\u03bd \u03b1\u03c6\u03ae\u03c3\u03b5\u03c4\u03b5 \u03c4\u03bf \u03bc\u03bf\u03bd\u03c4\u03ad\u03bb\u03bf \u03ba\u03b5\u03bd\u03cc, \u03b8\u03b1 \u03b5\u03bd\u03c4\u03bf\u03c0\u03b9\u03c3\u03c4\u03b5\u03af \u03b1\u03c5\u03c4\u03cc\u03bc\u03b1\u03c4\u03b1." + } } } } diff --git a/homeassistant/components/yeelight/translations/en.json b/homeassistant/components/yeelight/translations/en.json index 5e452dcf098..b49e3d7658e 100644 --- a/homeassistant/components/yeelight/translations/en.json +++ b/homeassistant/components/yeelight/translations/en.json @@ -34,8 +34,7 @@ "save_on_change": "Save Status On Change", "transition": "Transition Time (ms)", "use_music_mode": "Enable Music Mode" - }, - "description": "If you leave model empty, it will be automatically detected." + } } } } diff --git a/homeassistant/components/yeelight/translations/es.json b/homeassistant/components/yeelight/translations/es.json index bc9f26f665a..ef93c17e72c 100644 --- a/homeassistant/components/yeelight/translations/es.json +++ b/homeassistant/components/yeelight/translations/es.json @@ -34,8 +34,7 @@ "save_on_change": "Guardar estado al cambiar", "transition": "Tiempo de transici\u00f3n (ms)", "use_music_mode": "Activar el Modo M\u00fasica" - }, - "description": "Si dejas el modelo vac\u00edo, se detectar\u00e1 autom\u00e1ticamente." + } } } } diff --git a/homeassistant/components/yeelight/translations/et.json b/homeassistant/components/yeelight/translations/et.json index 70fdc7c8dbf..b994119bb46 100644 --- a/homeassistant/components/yeelight/translations/et.json +++ b/homeassistant/components/yeelight/translations/et.json @@ -34,8 +34,7 @@ "save_on_change": "Salvesta olek peale muutmist", "transition": "\u00dclemineku aeg (ms)", "use_music_mode": "Luba muusikare\u017eiim" - }, - "description": "Kui j\u00e4tad mudeli m\u00e4\u00e4ramata tuvastatakse see automaatselt." + } } } } diff --git a/homeassistant/components/yeelight/translations/fr.json b/homeassistant/components/yeelight/translations/fr.json index a319f15e36a..5b443a45530 100644 --- a/homeassistant/components/yeelight/translations/fr.json +++ b/homeassistant/components/yeelight/translations/fr.json @@ -34,8 +34,7 @@ "save_on_change": "Enregistrer l'\u00e9tat lors d'un changement", "transition": "Dur\u00e9e de la transition (en millisecondes)", "use_music_mode": "Activer le mode musique" - }, - "description": "Si vous ne pr\u00e9cisez pas le mod\u00e8le, il sera automatiquement d\u00e9tect\u00e9." + } } } } diff --git a/homeassistant/components/yeelight/translations/he.json b/homeassistant/components/yeelight/translations/he.json index 0fe8dd0f06e..94e0e87d87a 100644 --- a/homeassistant/components/yeelight/translations/he.json +++ b/homeassistant/components/yeelight/translations/he.json @@ -34,8 +34,7 @@ "save_on_change": "\u05e9\u05de\u05d5\u05e8 \u05e1\u05d8\u05d8\u05d5\u05e1 \u05d1\u05e9\u05d9\u05e0\u05d5\u05d9", "transition": "\u05d6\u05de\u05df \u05de\u05e2\u05d1\u05e8 (\u05d0\u05dc\u05e4\u05d9\u05d5\u05ea \u05e9\u05e0\u05d9\u05d4)", "use_music_mode": "\u05d4\u05e4\u05e2\u05dc\u05ea \u05de\u05e6\u05d1 \u05de\u05d5\u05e1\u05d9\u05e7\u05d4" - }, - "description": "\u05d0\u05dd \u05ea\u05e9\u05d0\u05d9\u05e8 \u05d0\u05ea \u05d4\u05d3\u05d2\u05dd \u05e8\u05d9\u05e7, \u05d4\u05d5\u05d0 \u05d9\u05d6\u05d5\u05d4\u05d4 \u05d1\u05d0\u05d5\u05e4\u05df \u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9." + } } } } diff --git a/homeassistant/components/yeelight/translations/hu.json b/homeassistant/components/yeelight/translations/hu.json index 6cf10422c28..b3969a44f99 100644 --- a/homeassistant/components/yeelight/translations/hu.json +++ b/homeassistant/components/yeelight/translations/hu.json @@ -34,8 +34,7 @@ "save_on_change": "\u00c1llapot ment\u00e9se m\u00f3dos\u00edt\u00e1s ut\u00e1n", "transition": "\u00c1tmeneti id\u0151 (ms)", "use_music_mode": "Zene m\u00f3d enged\u00e9lyez\u00e9se" - }, - "description": "Ha modellt \u00fcresen hagyja, a rendszer automatikusan \u00e9rz\u00e9keli." + } } } } diff --git a/homeassistant/components/yeelight/translations/id.json b/homeassistant/components/yeelight/translations/id.json index 19537d658a1..9209e2d917b 100644 --- a/homeassistant/components/yeelight/translations/id.json +++ b/homeassistant/components/yeelight/translations/id.json @@ -34,8 +34,7 @@ "save_on_change": "Simpan Status Saat Berubah", "transition": "Waktu Transisi (milidetik)", "use_music_mode": "Aktifkan Mode Musik" - }, - "description": "Jika model dibiarkan kosong, model akan dideteksi secara otomatis." + } } } } diff --git a/homeassistant/components/yeelight/translations/it.json b/homeassistant/components/yeelight/translations/it.json index 7022a016ce8..25a56dfb6c2 100644 --- a/homeassistant/components/yeelight/translations/it.json +++ b/homeassistant/components/yeelight/translations/it.json @@ -34,8 +34,7 @@ "save_on_change": "Salva stato su modifica", "transition": "Tempo di transizione (ms)", "use_music_mode": "Abilita la modalit\u00e0 musica" - }, - "description": "Se lasci il modello vuoto, sar\u00e0 rilevato automaticamente." + } } } } diff --git a/homeassistant/components/yeelight/translations/ja.json b/homeassistant/components/yeelight/translations/ja.json index 001cde41977..5a290043963 100644 --- a/homeassistant/components/yeelight/translations/ja.json +++ b/homeassistant/components/yeelight/translations/ja.json @@ -34,8 +34,7 @@ "save_on_change": "\u5909\u66f4\u6642\u306b\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u4fdd\u5b58", "transition": "\u9077\u79fb\u6642\u9593(Transition Time)(ms)", "use_music_mode": "\u97f3\u697d\u30e2\u30fc\u30c9\u3092\u6709\u52b9\u306b\u3059\u308b" - }, - "description": "\u30e2\u30c7\u30eb\u3092\u7a7a\u767d\u306b\u3057\u3066\u304a\u304f\u3068\u3001\u81ea\u52d5\u7684\u306b\u691c\u51fa\u3055\u308c\u307e\u3059\u3002" + } } } } diff --git a/homeassistant/components/yeelight/translations/ko.json b/homeassistant/components/yeelight/translations/ko.json index 4abb8fcbbff..b8a4385bfdf 100644 --- a/homeassistant/components/yeelight/translations/ko.json +++ b/homeassistant/components/yeelight/translations/ko.json @@ -30,8 +30,7 @@ "save_on_change": "\ubcc0\uacbd \uc2dc \uc0c1\ud0dc\ub97c \uc800\uc7a5\ud558\uae30", "transition": "\uc804\ud658 \uc2dc\uac04(ms)", "use_music_mode": "\uc74c\uc545 \ubaa8\ub4dc \ud65c\uc131\ud654\ud558\uae30" - }, - "description": "\ubaa8\ub378\uc744 \ube44\uc6cc \ub450\uba74 \uc790\ub3d9\uc73c\ub85c \uac80\uc0c9\ub429\ub2c8\ub2e4." + } } } } diff --git a/homeassistant/components/yeelight/translations/lb.json b/homeassistant/components/yeelight/translations/lb.json index 482cbf23e48..5ffa8e23a18 100644 --- a/homeassistant/components/yeelight/translations/lb.json +++ b/homeassistant/components/yeelight/translations/lb.json @@ -30,8 +30,7 @@ "save_on_change": "Status sp\u00e4icheren bei \u00c4nnerung", "transition": "Iwwergangsz\u00e4it (ms)", "use_music_mode": "Musek Modus aktiv\u00e9ieren" - }, - "description": "Falls Modell eidel gelass g\u00ebtt, g\u00ebtt et automatesch erkannt." + } } } } diff --git a/homeassistant/components/yeelight/translations/nl.json b/homeassistant/components/yeelight/translations/nl.json index a83ef72695c..7767c56e7fb 100644 --- a/homeassistant/components/yeelight/translations/nl.json +++ b/homeassistant/components/yeelight/translations/nl.json @@ -34,8 +34,7 @@ "save_on_change": "Bewaar status bij wijziging", "transition": "Overgangstijd (ms)", "use_music_mode": "Schakel de muziekmodus in" - }, - "description": "Als u model leeg laat, wordt het automatisch gedetecteerd." + } } } } diff --git a/homeassistant/components/yeelight/translations/no.json b/homeassistant/components/yeelight/translations/no.json index ea4436d7769..a7ef2e05a5d 100644 --- a/homeassistant/components/yeelight/translations/no.json +++ b/homeassistant/components/yeelight/translations/no.json @@ -34,8 +34,7 @@ "save_on_change": "Lagre status ved endring", "transition": "Overgangstid (ms)", "use_music_mode": "Aktiver musikkmodus" - }, - "description": "Hvis du lar modellen v\u00e6re tom, blir den automatisk oppdaget." + } } } } diff --git a/homeassistant/components/yeelight/translations/pl.json b/homeassistant/components/yeelight/translations/pl.json index 818ff0946c4..612fab44a62 100644 --- a/homeassistant/components/yeelight/translations/pl.json +++ b/homeassistant/components/yeelight/translations/pl.json @@ -34,8 +34,7 @@ "save_on_change": "Zachowaj status po zmianie", "transition": "Czas przej\u015bcia (ms)", "use_music_mode": "W\u0142\u0105cz tryb muzyczny" - }, - "description": "Je\u015bli nie podasz modelu urz\u0105dzenia, zostanie on automatycznie wykryty." + } } } } diff --git a/homeassistant/components/yeelight/translations/pt-BR.json b/homeassistant/components/yeelight/translations/pt-BR.json index 2c54af41b25..482733df343 100644 --- a/homeassistant/components/yeelight/translations/pt-BR.json +++ b/homeassistant/components/yeelight/translations/pt-BR.json @@ -34,8 +34,7 @@ "save_on_change": "Salvar status na altera\u00e7\u00e3o", "transition": "Tempo de transi\u00e7\u00e3o (ms)", "use_music_mode": "Ativar o modo de m\u00fasica" - }, - "description": "Se voc\u00ea deixar o modelo vazio, ele ser\u00e1 detectado automaticamente." + } } } } diff --git a/homeassistant/components/yeelight/translations/pt.json b/homeassistant/components/yeelight/translations/pt.json index 6d350188989..b03d5b8fc6b 100644 --- a/homeassistant/components/yeelight/translations/pt.json +++ b/homeassistant/components/yeelight/translations/pt.json @@ -30,8 +30,7 @@ "save_on_change": "Salvar status ao alterar", "transition": "Tempo de transi\u00e7\u00e3o (ms)", "use_music_mode": "Ativar modo de m\u00fasica" - }, - "description": "Se voc\u00ea deixar o modelo vazio, ele ser\u00e1 detectado automaticamente." + } } } } diff --git a/homeassistant/components/yeelight/translations/ru.json b/homeassistant/components/yeelight/translations/ru.json index 70694147baa..88ab0a73735 100644 --- a/homeassistant/components/yeelight/translations/ru.json +++ b/homeassistant/components/yeelight/translations/ru.json @@ -34,8 +34,7 @@ "save_on_change": "\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438", "transition": "\u0412\u0440\u0435\u043c\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 (\u0432 \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)", "use_music_mode": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c" - }, - "description": "\u0415\u0441\u043b\u0438 \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u0430, \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438." + } } } } diff --git a/homeassistant/components/yeelight/translations/tr.json b/homeassistant/components/yeelight/translations/tr.json index 4eed4f477b4..21ec60cde68 100644 --- a/homeassistant/components/yeelight/translations/tr.json +++ b/homeassistant/components/yeelight/translations/tr.json @@ -34,8 +34,7 @@ "save_on_change": "De\u011fi\u015fiklikte Durumu Kaydet", "transition": "Ge\u00e7i\u015f S\u00fcresi (ms)", "use_music_mode": "M\u00fczik Modunu Etkinle\u015ftir" - }, - "description": "Modeli bo\u015f b\u0131rak\u0131rsan\u0131z, otomatik olarak alg\u0131lanacakt\u0131r." + } } } } diff --git a/homeassistant/components/yeelight/translations/uk.json b/homeassistant/components/yeelight/translations/uk.json index 0a173ccb6e4..2149302e940 100644 --- a/homeassistant/components/yeelight/translations/uk.json +++ b/homeassistant/components/yeelight/translations/uk.json @@ -30,8 +30,7 @@ "save_on_change": "\u0417\u0431\u0435\u0440\u0456\u0433\u0430\u0442\u0438 \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0438 \u0437\u043c\u0456\u043d\u0456", "transition": "\u0427\u0430\u0441 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0443 (\u0432 \u043c\u0456\u043b\u0456\u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445)", "use_music_mode": "\u041c\u0443\u0437\u0438\u0447\u043d\u0438\u0439 \u0440\u0435\u0436\u0438\u043c" - }, - "description": "\u042f\u043a\u0449\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u0432\u0438\u0431\u0440\u0430\u043d\u043e, \u0432\u043e\u043d\u0430 \u0431\u0443\u0434\u0435 \u0432\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u043d\u043e." + } } } } diff --git a/homeassistant/components/yeelight/translations/zh-Hans.json b/homeassistant/components/yeelight/translations/zh-Hans.json index 36add653d1d..846324d143c 100644 --- a/homeassistant/components/yeelight/translations/zh-Hans.json +++ b/homeassistant/components/yeelight/translations/zh-Hans.json @@ -34,8 +34,7 @@ "save_on_change": "\u4fdd\u5b58\u66f4\u6539\u72b6\u6001", "transition": "\u8fc7\u6e21\u65f6\u95f4\uff08\u6beb\u79d2\uff09", "use_music_mode": "\u542f\u7528\u97f3\u4e50\u6a21\u5f0f" - }, - "description": "\u5982\u679c\u5c06\u4fe1\u53f7\u680f\u7559\u7a7a\uff0c\u96c6\u6210\u5c06\u4f1a\u81ea\u52a8\u68c0\u6d4b\u76f8\u5173\u4fe1\u606f" + } } } } diff --git a/homeassistant/components/yeelight/translations/zh-Hant.json b/homeassistant/components/yeelight/translations/zh-Hant.json index 7601a0b9552..816e2b02937 100644 --- a/homeassistant/components/yeelight/translations/zh-Hant.json +++ b/homeassistant/components/yeelight/translations/zh-Hant.json @@ -34,8 +34,7 @@ "save_on_change": "\u65bc\u8b8a\u66f4\u6642\u5132\u5b58\u72c0\u614b", "transition": "\u8f49\u63db\u6642\u9593\uff08\u6beb\u79d2\uff09", "use_music_mode": "\u958b\u555f\u97f3\u6a02\u6a21\u5f0f" - }, - "description": "\u5047\u5982\u578b\u865f\u6b04\u4f4d\u70ba\u7a7a\u767d\uff0c\u5c07\u6703\u81ea\u52d5\u5075\u6e2c\u578b\u865f\u3002" + } } } } diff --git a/homeassistant/components/yolink/translations/ca.json b/homeassistant/components/yolink/translations/ca.json new file mode 100644 index 00000000000..db2adafa3f9 --- /dev/null +++ b/homeassistant/components/yolink/translations/ca.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "El compte ja est\u00e0 configurat", + "already_in_progress": "El flux de configuraci\u00f3 ja est\u00e0 en curs", + "authorize_url_timeout": "Temps d'espera esgotat durant la generaci\u00f3 de l'URL d'autoritzaci\u00f3.", + "missing_configuration": "El component no est\u00e0 configurat. Mira'n la documentaci\u00f3.", + "no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})", + "oauth_error": "S'han rebut dades token inv\u00e0lides.", + "reauth_successful": "Re-autenticaci\u00f3 realitzada correctament" + }, + "create_entry": { + "default": "Autenticaci\u00f3 exitosa" + }, + "step": { + "pick_implementation": { + "title": "Selecciona el m\u00e8tode d'autenticaci\u00f3" + }, + "reauth_confirm": { + "description": "La integraci\u00f3 yolink ha de tornar a autenticar-se amb el teu compte.", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/de.json b/homeassistant/components/yolink/translations/de.json new file mode 100644 index 00000000000..d9d5c9a6efa --- /dev/null +++ b/homeassistant/components/yolink/translations/de.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Konto wurde bereits konfiguriert", + "already_in_progress": "Der Konfigurationsablauf wird bereits ausgef\u00fchrt", + "authorize_url_timeout": "Zeit\u00fcberschreitung beim Erstellen der Authorisierungs-URL.", + "missing_configuration": "Die Komponente ist nicht konfiguriert. Bitte der Dokumentation folgen.", + "no_url_available": "Keine URL verf\u00fcgbar. Informationen zu diesem Fehler findest du [im Hilfebereich]({docs_url}).", + "oauth_error": "Ung\u00fcltige Token-Daten empfangen.", + "reauth_successful": "Die erneute Authentifizierung war erfolgreich" + }, + "create_entry": { + "default": "Erfolgreich authentifiziert" + }, + "step": { + "pick_implementation": { + "title": "W\u00e4hle die Authentifizierungsmethode" + }, + "reauth_confirm": { + "description": "Die yolink-Integration muss dein Konto neu authentifizieren", + "title": "Integration erneut authentifizieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/he.json b/homeassistant/components/yolink/translations/he.json new file mode 100644 index 00000000000..525624782ac --- /dev/null +++ b/homeassistant/components/yolink/translations/he.json @@ -0,0 +1,24 @@ +{ + "config": { + "abort": { + "already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4", + "already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea", + "authorize_url_timeout": "\u05e4\u05e1\u05e7 \u05d6\u05de\u05df \u05dc\u05d9\u05e6\u05d9\u05e8\u05ea \u05db\u05ea\u05d5\u05d1\u05ea URL \u05dc\u05d0\u05d9\u05e9\u05d5\u05e8.", + "missing_configuration": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05e8\u05db\u05d9\u05d1 \u05dc\u05d0 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e0\u05d0 \u05e2\u05e7\u05d5\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05d9\u05e2\u05d5\u05d3.", + "no_url_available": "\u05d0\u05d9\u05df \u05db\u05ea\u05d5\u05d1\u05ea \u05d0\u05ea\u05e8 \u05d6\u05de\u05d9\u05e0\u05d4. \u05e7\u05d1\u05dc\u05ea \u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05e9\u05d2\u05d9\u05d0\u05d4 \u05d6\u05d5, [\u05e2\u05d9\u05d9\u05df \u05d1\u05e1\u05e2\u05d9\u05e3 \u05d4\u05e2\u05d6\u05e8\u05d4] ({docs_url})", + "oauth_error": "\u05d4\u05ea\u05e7\u05d1\u05dc\u05d5 \u05e0\u05ea\u05d5\u05e0\u05d9 \u05d0\u05e1\u05d9\u05de\u05d5\u05df \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05d9\u05dd.", + "reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7" + }, + "create_entry": { + "default": "\u05d0\u05d5\u05de\u05ea \u05d1\u05d4\u05e6\u05dc\u05d7\u05d4" + }, + "step": { + "pick_implementation": { + "title": "\u05d1\u05d7\u05e8 \u05e9\u05d9\u05d8\u05ea \u05d0\u05d9\u05de\u05d5\u05ea" + }, + "reauth_confirm": { + "title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05e9\u05dc \u05e9\u05d9\u05dc\u05d5\u05d1" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/yolink/translations/id.json b/homeassistant/components/yolink/translations/id.json index e2c5ce51b91..a8211681abf 100644 --- a/homeassistant/components/yolink/translations/id.json +++ b/homeassistant/components/yolink/translations/id.json @@ -17,6 +17,7 @@ "title": "Pilih Metode Autentikasi" }, "reauth_confirm": { + "description": "Integrasi yolink perlu mengautentikasi ulang akun Anda", "title": "Autentikasi Ulang Integrasi" } } diff --git a/homeassistant/components/yolink/translations/ko.json b/homeassistant/components/yolink/translations/ko.json index 6ead9fca594..5a613bf437e 100644 --- a/homeassistant/components/yolink/translations/ko.json +++ b/homeassistant/components/yolink/translations/ko.json @@ -4,7 +4,9 @@ "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "authorize_url_timeout": "\uc778\uc99d URL \uc0dd\uc131 \uc2dc\uac04\uc774 \ucd08\uacfc\ub418\uc5c8\uc2b5\ub2c8\ub2e4.", + "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694.", "no_url_available": "\uc0ac\uc6a9 \uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc624\ub958\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 [\ub3c4\uc6c0\ub9d0 \uc139\uc158]({docs_url}) \uc744(\ub97c) \ucc38\uc870\ud574\uc8fc\uc138\uc694.", + "oauth_error": "\uc798\ubabb\ub41c \ud1a0\ud070 \ub370\uc774\ud130\ub97c \ubc1b\uc558\uc2b5\ub2c8\ub2e4.", "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "create_entry": { diff --git a/homeassistant/components/yolink/translations/no.json b/homeassistant/components/yolink/translations/no.json new file mode 100644 index 00000000000..b5e26ac910d --- /dev/null +++ b/homeassistant/components/yolink/translations/no.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Kontoen er allerede konfigurert", + "already_in_progress": "Konfigurasjonsflyten p\u00e5g\u00e5r allerede", + "authorize_url_timeout": "Tidsavbrudd ved oppretting av godkjenningsadresse", + "missing_configuration": "Komponenten er ikke konfigurert, vennligst f\u00f8lg dokumentasjonen", + "no_url_available": "Ingen URL tilgjengelig. For informasjon om denne feilen, [sjekk hjelpseksjonen]({docs_url})", + "oauth_error": "Mottatt ugyldige token data.", + "reauth_successful": "Godkjenning p\u00e5 nytt var vellykket" + }, + "create_entry": { + "default": "Vellykket godkjenning" + }, + "step": { + "pick_implementation": { + "title": "Velg godkjenningsmetode" + }, + "reauth_confirm": { + "description": "Yolink-integrasjonen m\u00e5 autentisere kontoen din p\u00e5 nytt", + "title": "Godkjenne integrering p\u00e5 nytt" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/bg.json b/homeassistant/components/zwave_js/translations/bg.json index 59fcf968740..5d2486acbc1 100644 --- a/homeassistant/components/zwave_js/translations/bg.json +++ b/homeassistant/components/zwave_js/translations/bg.json @@ -35,7 +35,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\u041c\u0440\u0435\u0436\u043e\u0432 \u043a\u043b\u044e\u0447", "s0_legacy_key": "S0 \u043a\u043b\u044e\u0447 (\u043d\u0430\u0441\u043b\u0435\u0434\u0435\u043d)", "s2_access_control_key": "S2 \u043a\u043b\u044e\u0447 \u0437\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b \u043d\u0430 \u0434\u043e\u0441\u0442\u044a\u043f\u0430", "s2_authenticated_key": "S2 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0435\u043d \u043a\u043b\u044e\u0447", diff --git a/homeassistant/components/zwave_js/translations/ca.json b/homeassistant/components/zwave_js/translations/ca.json index 6691894b79d..de21cc3f232 100644 --- a/homeassistant/components/zwave_js/translations/ca.json +++ b/homeassistant/components/zwave_js/translations/ca.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Clau de xarxa", "s0_legacy_key": "Clau d'S0 (est\u00e0ndard)", "s2_access_control_key": "Clau de control d'acc\u00e9s d'S2", "s2_authenticated_key": "Clau d'S2 autenticat", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emula maquinari", "log_level": "Nivell dels registres", - "network_key": "Clau de xarxa", "s0_legacy_key": "Clau d'S0 (est\u00e0ndard)", "s2_access_control_key": "Clau de control d'acc\u00e9s d'S2", "s2_authenticated_key": "Clau d'S2 autenticat", @@ -146,6 +144,5 @@ "title": "El complement Z-Wave JS s'est\u00e0 iniciant." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/cs.json b/homeassistant/components/zwave_js/translations/cs.json index 5c16938b23b..6417d5a587f 100644 --- a/homeassistant/components/zwave_js/translations/cs.json +++ b/homeassistant/components/zwave_js/translations/cs.json @@ -45,8 +45,7 @@ "step": { "configure_addon": { "data": { - "log_level": "\u00darove\u0148 protokolu", - "network_key": "S\u00ed\u0165ov\u00fd kl\u00ed\u010d" + "log_level": "\u00darove\u0148 protokolu" } }, "manual": { diff --git a/homeassistant/components/zwave_js/translations/de.json b/homeassistant/components/zwave_js/translations/de.json index 02418f44306..ec0157f9a66 100644 --- a/homeassistant/components/zwave_js/translations/de.json +++ b/homeassistant/components/zwave_js/translations/de.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Netzwerk-Schl\u00fcssel", "s0_legacy_key": "S0 Schl\u00fcssel (Legacy)", "s2_access_control_key": "S2 Zugangskontrollschl\u00fcssel", "s2_authenticated_key": "S2 Authentifizierter Schl\u00fcssel", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Hardware emulieren", "log_level": "Protokollstufe", - "network_key": "Netzwerkschl\u00fcssel", "s0_legacy_key": "S0 Schl\u00fcssel (Legacy)", "s2_access_control_key": "S2 Zugangskontrollschl\u00fcssel", "s2_authenticated_key": "S2 Authentifizierter Schl\u00fcssel", @@ -146,6 +144,5 @@ "title": "Das Z-Wave JS Add-on wird gestartet." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/el.json b/homeassistant/components/zwave_js/translations/el.json index 66330206b60..930c82aa891 100644 --- a/homeassistant/components/zwave_js/translations/el.json +++ b/homeassistant/components/zwave_js/translations/el.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5", "s0_legacy_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af S0 (\u03c0\u03b1\u03bb\u03b1\u03b9\u03bf\u03cd \u03c4\u03cd\u03c0\u03bf\u03c5)", "s2_access_control_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 S2", "s2_authenticated_key": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "\u0395\u03be\u03bf\u03bc\u03bf\u03af\u03c9\u03c3\u03b7 \u03c5\u03bb\u03b9\u03ba\u03bf\u03cd", "log_level": "\u0395\u03c0\u03af\u03c0\u03b5\u03b4\u03bf \u03ba\u03b1\u03c4\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae\u03c2", - "network_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03b4\u03b9\u03ba\u03c4\u03cd\u03bf\u03c5", "s0_legacy_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af S0 (\u03c0\u03b1\u03bb\u03b1\u03b9\u03bf\u03cd \u03c4\u03cd\u03c0\u03bf\u03c5)", "s2_access_control_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 S2", "s2_authenticated_key": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03bc\u03ad\u03bd\u03bf \u03ba\u03bb\u03b5\u03b9\u03b4\u03af S2", @@ -146,6 +144,5 @@ "title": "\u03a4\u03bf \u03c0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf Z-Wave JS \u03be\u03b5\u03ba\u03b9\u03bd\u03ac." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/en.json b/homeassistant/components/zwave_js/translations/en.json index fa9794ed847..843f2aaf284 100644 --- a/homeassistant/components/zwave_js/translations/en.json +++ b/homeassistant/components/zwave_js/translations/en.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Network Key", "s0_legacy_key": "S0 Key (Legacy)", "s2_access_control_key": "S2 Access Control Key", "s2_authenticated_key": "S2 Authenticated Key", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emulate Hardware", "log_level": "Log level", - "network_key": "Network Key", "s0_legacy_key": "S0 Key (Legacy)", "s2_access_control_key": "S2 Access Control Key", "s2_authenticated_key": "S2 Authenticated Key", @@ -146,6 +144,5 @@ "title": "The Z-Wave JS add-on is starting." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/es.json b/homeassistant/components/zwave_js/translations/es.json index dfe8c1de296..5d3efb0e7f4 100644 --- a/homeassistant/components/zwave_js/translations/es.json +++ b/homeassistant/components/zwave_js/translations/es.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Clave de red", "s0_legacy_key": "Clave S0 (heredada)", "s2_access_control_key": "Clave de control de acceso S2", "s2_authenticated_key": "Clave autenticada de S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emular el hardware", "log_level": "Nivel de registro", - "network_key": "Clave de red", "s0_legacy_key": "Tecla S0 (heredada)", "s2_access_control_key": "Clave de control de acceso S2", "s2_authenticated_key": "Clave autenticada de S2", @@ -146,6 +144,5 @@ "title": "Se est\u00e1 iniciando el complemento Z-Wave JS." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/et.json b/homeassistant/components/zwave_js/translations/et.json index 29f50ccb290..ea0686e424f 100644 --- a/homeassistant/components/zwave_js/translations/et.json +++ b/homeassistant/components/zwave_js/translations/et.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "V\u00f5rgu v\u00f5ti", "s0_legacy_key": "S0 vana t\u00fc\u00fcpi v\u00f5ti", "s2_access_control_key": "S2 juurdep\u00e4\u00e4suv\u00f5ti", "s2_authenticated_key": "Autenditud S2 v\u00f5ti", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Riistvara emuleerimine", "log_level": "Logimise tase", - "network_key": "V\u00f5rgu v\u00f5ti", "s0_legacy_key": "S0 vana t\u00fc\u00fcpi v\u00f5ti", "s2_access_control_key": "S2 juurdep\u00e4\u00e4suv\u00f5ti", "s2_authenticated_key": "Autenditud S2 v\u00f5ti", @@ -146,6 +144,5 @@ "title": "Z-Wave JS lisandmoodul k\u00e4ivitub." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/fr.json b/homeassistant/components/zwave_js/translations/fr.json index 22e8ed19e32..47c3086489c 100644 --- a/homeassistant/components/zwave_js/translations/fr.json +++ b/homeassistant/components/zwave_js/translations/fr.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Cl\u00e9 r\u00e9seau", "s0_legacy_key": "Cl\u00e9 S0 (h\u00e9rit\u00e9e)", "s2_access_control_key": "Cl\u00e9 de contr\u00f4le d'acc\u00e8s S2", "s2_authenticated_key": "Cl\u00e9 d'authentification S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "\u00c9muler le mat\u00e9riel", "log_level": "Niveau du journal", - "network_key": "Cl\u00e9 r\u00e9seau", "s0_legacy_key": "Cl\u00e9 S0 (h\u00e9rit\u00e9e)", "s2_access_control_key": "Cl\u00e9 de contr\u00f4le d'acc\u00e8s S2", "s2_authenticated_key": "Cl\u00e9 d'authentification S2", @@ -146,6 +144,5 @@ "title": "Le module compl\u00e9mentaire Z-Wave JS d\u00e9marre." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/he.json b/homeassistant/components/zwave_js/translations/he.json index 041e1cafec6..0d6b2a9f619 100644 --- a/homeassistant/components/zwave_js/translations/he.json +++ b/homeassistant/components/zwave_js/translations/he.json @@ -48,7 +48,6 @@ "configure_addon": { "data": { "log_level": "\u05e8\u05de\u05ea \u05d9\u05d5\u05de\u05df \u05e8\u05d9\u05e9\u05d5\u05dd", - "network_key": "\u05de\u05e4\u05ea\u05d7 \u05e8\u05e9\u05ea", "usb_path": "\u05e0\u05ea\u05d9\u05d1 \u05d4\u05ea\u05e7\u05df USB" } }, @@ -64,6 +63,5 @@ "description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05e9\u05ea\u05de\u05e9 \u05d1\u05d4\u05e8\u05d7\u05d1\u05d4 \u05de\u05e4\u05e7\u05d7 Z-Wave JS?" } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/hu.json b/homeassistant/components/zwave_js/translations/hu.json index 07dbb93b703..37e19e5471f 100644 --- a/homeassistant/components/zwave_js/translations/hu.json +++ b/homeassistant/components/zwave_js/translations/hu.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "H\u00e1l\u00f3zati kulcs", "s0_legacy_key": "S0 kulcs (r\u00e9gi)", "s2_access_control_key": "S2 Hozz\u00e1f\u00e9r\u00e9s kulcs", "s2_authenticated_key": "S2 hiteles\u00edtett kulcs", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Hardver emul\u00e1ci\u00f3", "log_level": "Napl\u00f3szint", - "network_key": "H\u00e1l\u00f3zati kulcs", "s0_legacy_key": "S0 kulcs (r\u00e9gi)", "s2_access_control_key": "S2 hozz\u00e1f\u00e9r\u00e9si ", "s2_authenticated_key": "S2 hiteles\u00edtett kulcs", @@ -146,6 +144,5 @@ "title": "Indul a Z-Wave JS b\u0151v\u00edtm\u00e9ny." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/id.json b/homeassistant/components/zwave_js/translations/id.json index ef14a550210..340266e9a9b 100644 --- a/homeassistant/components/zwave_js/translations/id.json +++ b/homeassistant/components/zwave_js/translations/id.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Kunci Jaringan", "s0_legacy_key": "Kunci S0 (Warisan)", "s2_access_control_key": "Kunci Kontrol Akses S2", "s2_authenticated_key": "Kunci Autentikasi S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emulasikan Perangkat Keras", "log_level": "Tingkat log", - "network_key": "Kunci Jaringan", "s0_legacy_key": "Kunci S0 (Warisan)", "s2_access_control_key": "Kunci Kontrol Akses S2", "s2_authenticated_key": "Kunci Autentikasi S2", @@ -146,6 +144,5 @@ "title": "Add-on Z-Wave JS sedang dimulai." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/it.json b/homeassistant/components/zwave_js/translations/it.json index e82ccfe8476..eb1e829a52c 100644 --- a/homeassistant/components/zwave_js/translations/it.json +++ b/homeassistant/components/zwave_js/translations/it.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Chiave di rete", "s0_legacy_key": "Chiave S0 (Obsoleta)", "s2_access_control_key": "Chiave di controllo di accesso S2", "s2_authenticated_key": "Chiave S2 autenticata", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emula l'hardware", "log_level": "Livello di registro", - "network_key": "Chiave di rete", "s0_legacy_key": "Chiave S0 (Obsoleta)", "s2_access_control_key": "Chiave di controllo di accesso S2", "s2_authenticated_key": "Chiave S2 autenticata", @@ -146,6 +144,5 @@ "title": "Il componente aggiuntivo Z-Wave JS si sta avviando." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/ja.json b/homeassistant/components/zwave_js/translations/ja.json index 9b00f062993..902955c1b9e 100644 --- a/homeassistant/components/zwave_js/translations/ja.json +++ b/homeassistant/components/zwave_js/translations/ja.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u30ad\u30fc", "s0_legacy_key": "S0\u30ad\u30fc (\u30ec\u30ac\u30b7\u30fc)", "s2_access_control_key": "S2\u30a2\u30af\u30bb\u30b9\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30ad\u30fc", "s2_authenticated_key": "S2\u8a8d\u8a3c\u6e08\u307f\u306a\u30ad\u30fc", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u306e\u30a8\u30df\u30e5\u30ec\u30fc\u30b7\u30e7\u30f3", "log_level": "\u30ed\u30b0\u30ec\u30d9\u30eb", - "network_key": "\u30cd\u30c3\u30c8\u30ef\u30fc\u30af", "s0_legacy_key": "S0\u30ad\u30fc (\u30ec\u30ac\u30b7\u30fc)", "s2_access_control_key": "S2\u30a2\u30af\u30bb\u30b9\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30ad\u30fc", "s2_authenticated_key": "S2\u8a8d\u8a3c\u6e08\u307f\u306a\u30ad\u30fc", @@ -146,6 +144,5 @@ "title": "Z-Wave JS \u30a2\u30c9\u30aa\u30f3\u304c\u8d77\u52d5\u3057\u3066\u3044\u307e\u3059\u3002" } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/ko.json b/homeassistant/components/zwave_js/translations/ko.json index 2c14397177e..7bf9dc8f062 100644 --- a/homeassistant/components/zwave_js/translations/ko.json +++ b/homeassistant/components/zwave_js/translations/ko.json @@ -23,7 +23,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\ub124\ud2b8\uc6cc\ud06c \ud0a4", "usb_path": "USB \uc7a5\uce58 \uacbd\ub85c" }, "title": "Z-Wave JS \uc560\ub4dc\uc628\uc758 \uad6c\uc131\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694" @@ -56,7 +55,9 @@ "set_config_parameter": "\uad6c\uc131 \ub9e4\uac1c\ubcc0\uc218 {subtype} \uc758 \uac12 \uc124\uc815", "set_lock_usercode": "{entity_name} \uc5d0 \uc0ac\uc6a9\uc790 \ucf54\ub4dc \uc124\uc815", "set_value": "Z-Wave Value\uc758 \uc124\uc815\uac12" + }, + "trigger_type": { + "event.value_notification.scene_activation": "{subtype} \uc5d0\uc11c \uc7a5\uba74 \ud65c\uc131\ud654" } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/lb.json b/homeassistant/components/zwave_js/translations/lb.json index d84c5323fb9..04259de2303 100644 --- a/homeassistant/components/zwave_js/translations/lb.json +++ b/homeassistant/components/zwave_js/translations/lb.json @@ -8,6 +8,5 @@ "invalid_ws_url": "Ong\u00eblteg Websocket URL", "unknown": "Onerwaarte Feeler" } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/nl.json b/homeassistant/components/zwave_js/translations/nl.json index 2d3ce5421ed..53bf45e2f4d 100644 --- a/homeassistant/components/zwave_js/translations/nl.json +++ b/homeassistant/components/zwave_js/translations/nl.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Netwerksleutel", "s0_legacy_key": "S0 Sleutel (Legacy)", "s2_access_control_key": "S2 Toegangscontrolesleutel", "s2_authenticated_key": "S2 geverifieerde sleutel", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emulate Hardware", "log_level": "Log level", - "network_key": "Netwerksleutel", "s0_legacy_key": "S0 Sleutel (Legacy)", "s2_access_control_key": "S2 Toegangscontrolesleutel", "s2_authenticated_key": "S2 geverifieerde sleutel", @@ -146,6 +144,5 @@ "title": "The Z-Wave JS add-on is aan het starten." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/no.json b/homeassistant/components/zwave_js/translations/no.json index 854bd307abc..c89e5582677 100644 --- a/homeassistant/components/zwave_js/translations/no.json +++ b/homeassistant/components/zwave_js/translations/no.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Nettverksn\u00f8kkel", "s0_legacy_key": "S0-n\u00f8kkel (eldre)", "s2_access_control_key": "N\u00f8kkel for S2-tilgangskontroll", "s2_authenticated_key": "S2 Autentisert n\u00f8kkel", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emuler maskinvare", "log_level": "Loggniv\u00e5", - "network_key": "Nettverksn\u00f8kkel", "s0_legacy_key": "S0-n\u00f8kkel (eldre)", "s2_access_control_key": "N\u00f8kkel for S2-tilgangskontroll", "s2_authenticated_key": "S2 Autentisert n\u00f8kkel", @@ -146,6 +144,5 @@ "title": "Z-Wave JS-tillegget starter" } } - }, - "title": "" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/pl.json b/homeassistant/components/zwave_js/translations/pl.json index 2edb9fd8c1f..3929aa668ef 100644 --- a/homeassistant/components/zwave_js/translations/pl.json +++ b/homeassistant/components/zwave_js/translations/pl.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Klucz sieci", "s0_legacy_key": "Klucz S0 (Legacy)", "s2_access_control_key": "Klucz kontroli dost\u0119pu S2", "s2_authenticated_key": "Klucz uwierzytelniony S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emulacja sprz\u0119tu", "log_level": "Poziom loga", - "network_key": "Klucz sieci", "s0_legacy_key": "Klucz S0 (Legacy)", "s2_access_control_key": "Klucz kontroli dost\u0119pu S2", "s2_authenticated_key": "Klucz uwierzytelniony S2", @@ -146,6 +144,5 @@ "title": "Dodatek Z-Wave JS uruchamia si\u0119..." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/pt-BR.json b/homeassistant/components/zwave_js/translations/pt-BR.json index 5e1e8610fc7..e8587aa6f94 100644 --- a/homeassistant/components/zwave_js/translations/pt-BR.json +++ b/homeassistant/components/zwave_js/translations/pt-BR.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "Chave de rede", "s0_legacy_key": "Chave S0 (Legado)", "s2_access_control_key": "Chave de controle de acesso S2", "s2_authenticated_key": "Chave autenticada S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Emular hardware", "log_level": "N\u00edvel de registro", - "network_key": "Chave de rede", "s0_legacy_key": "Chave S0 (Legado)", "s2_access_control_key": "Chave de controle de acesso S2", "s2_authenticated_key": "Chave autenticada S2", @@ -146,6 +144,5 @@ "title": "O add-on Z-Wave JS est\u00e1 iniciando." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/ru.json b/homeassistant/components/zwave_js/translations/ru.json index 14011c78517..44979a6cfe9 100644 --- a/homeassistant/components/zwave_js/translations/ru.json +++ b/homeassistant/components/zwave_js/translations/ru.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\u041a\u043b\u044e\u0447 \u0441\u0435\u0442\u0438", "s0_legacy_key": "\u041a\u043b\u044e\u0447 S0 (\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439)", "s2_access_control_key": "\u041a\u043b\u044e\u0447 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 S2", "s2_authenticated_key": "\u041a\u043b\u044e\u0447 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 S2", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "\u042d\u043c\u0443\u043b\u044f\u0446\u0438\u044f \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u044f", "log_level": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c \u0436\u0443\u0440\u043d\u0430\u043b\u0430", - "network_key": "\u041a\u043b\u044e\u0447 \u0441\u0435\u0442\u0438", "s0_legacy_key": "\u041a\u043b\u044e\u0447 S0 (\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439)", "s2_access_control_key": "\u041a\u043b\u044e\u0447 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 S2", "s2_authenticated_key": "\u041a\u043b\u044e\u0447 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 S2", @@ -146,6 +144,5 @@ "title": "\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 Z-Wave JS \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f" } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/tr.json b/homeassistant/components/zwave_js/translations/tr.json index a3b5f6fb9f4..21d8f03bec6 100644 --- a/homeassistant/components/zwave_js/translations/tr.json +++ b/homeassistant/components/zwave_js/translations/tr.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "A\u011f Anahtar\u0131", "s0_legacy_key": "S0 Anahtar\u0131 (Eski)", "s2_access_control_key": "S2 Eri\u015fim Kontrol Anahtar\u0131", "s2_authenticated_key": "S2 Kimli\u011fi Do\u011frulanm\u0131\u015f Anahtar", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "Donan\u0131m\u0131 Taklit Et", "log_level": "G\u00fcnl\u00fck d\u00fczeyi", - "network_key": "A\u011f Anahtar\u0131", "s0_legacy_key": "S0 Anahtar\u0131 (Eski)", "s2_access_control_key": "S2 Eri\u015fim Kontrol Anahtar\u0131", "s2_authenticated_key": "S2 Kimli\u011fi Do\u011frulanm\u0131\u015f Anahtar", @@ -146,6 +144,5 @@ "title": "Z-Wave JS eklentisi ba\u015fl\u0131yor." } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/uk.json b/homeassistant/components/zwave_js/translations/uk.json index fe77655ac29..95c9b44fb86 100644 --- a/homeassistant/components/zwave_js/translations/uk.json +++ b/homeassistant/components/zwave_js/translations/uk.json @@ -8,6 +8,5 @@ "invalid_ws_url": "\u041d\u0435\u0434\u0456\u0439\u0441\u043d\u0430 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432\u0435\u0431-\u0441\u043e\u043a\u0435\u0442\u0430", "unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430" } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file diff --git a/homeassistant/components/zwave_js/translations/zh-Hant.json b/homeassistant/components/zwave_js/translations/zh-Hant.json index 2ea728a591e..246800d1048 100644 --- a/homeassistant/components/zwave_js/translations/zh-Hant.json +++ b/homeassistant/components/zwave_js/translations/zh-Hant.json @@ -26,7 +26,6 @@ "step": { "configure_addon": { "data": { - "network_key": "\u7db2\u8def\u91d1\u9470", "s0_legacy_key": "S0 \u91d1\u9470\uff08\u820a\u7248\uff09", "s2_access_control_key": "S2 \u5b58\u53d6\u63a7\u5236\u91d1\u9470", "s2_authenticated_key": "S2 \u9a57\u8b49\u91d1\u9470", @@ -117,7 +116,6 @@ "data": { "emulate_hardware": "\u6a21\u64ec\u786c\u9ad4", "log_level": "\u65e5\u8a8c\u8a18\u9304\u7b49\u7d1a", - "network_key": "\u7db2\u8def\u91d1\u9470", "s0_legacy_key": "S0 \u91d1\u9470\uff08\u820a\u7248\uff09", "s2_access_control_key": "S2 \u5b58\u53d6\u63a7\u5236\u91d1\u9470", "s2_authenticated_key": "S2 \u9a57\u8b49\u91d1\u9470", @@ -146,6 +144,5 @@ "title": "Z-Wave JS \u9644\u52a0\u5143\u4ef6\u555f\u59cb\u4e2d\u3002" } } - }, - "title": "Z-Wave JS" + } } \ No newline at end of file From edd7a3427ca0522b907df91c9bd9cae40bbb9f33 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 19 May 2022 04:52:38 +0200 Subject: [PATCH 600/930] Remove support for databases without ROW_NUMBER (#72092) --- homeassistant/components/recorder/core.py | 1 - .../components/recorder/statistics.py | 97 +++++-------------- homeassistant/components/recorder/util.py | 15 --- tests/components/recorder/test_util.py | 49 ++++------ tests/components/sensor/test_recorder.py | 13 +-- 5 files changed, 45 insertions(+), 130 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 5a3a41568a1..5e6fafdfa61 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -180,7 +180,6 @@ class Recorder(threading.Thread): self._completed_first_database_setup: bool | None = None self.async_migration_event = asyncio.Event() self.migration_in_progress = False - self._db_supports_row_number = True self._database_lock_task: DatabaseLockTask | None = None self._db_executor: DBInterruptibleThreadPoolExecutor | None = None self._exclude_attributes_by_domain = exclude_attributes_by_domain diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 77f56bc59fa..2c6c51a31f4 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -437,22 +437,6 @@ def _compile_hourly_statistics_summary_mean_stmt( return stmt -def _compile_hourly_statistics_summary_sum_legacy_stmt( - start_time: datetime, end_time: datetime -) -> StatementLambdaElement: - """Generate the legacy sum statement for hourly statistics. - - This is used for databases not supporting row number. - """ - stmt = lambda_stmt(lambda: select(*QUERY_STATISTICS_SUMMARY_SUM_LEGACY)) - stmt += ( - lambda q: q.filter(StatisticsShortTerm.start >= start_time) - .filter(StatisticsShortTerm.start < end_time) - .order_by(StatisticsShortTerm.metadata_id, StatisticsShortTerm.start.desc()) - ) - return stmt - - def compile_hourly_statistics( instance: Recorder, session: Session, start: datetime ) -> None: @@ -481,66 +465,37 @@ def compile_hourly_statistics( } # Get last hour's last sum - if instance._db_supports_row_number: # pylint: disable=[protected-access] - subquery = ( - session.query(*QUERY_STATISTICS_SUMMARY_SUM) - .filter(StatisticsShortTerm.start >= bindparam("start_time")) - .filter(StatisticsShortTerm.start < bindparam("end_time")) - .subquery() - ) - query = ( - session.query(subquery) - .filter(subquery.c.rownum == 1) - .order_by(subquery.c.metadata_id) - ) - stats = execute(query.params(start_time=start_time, end_time=end_time)) + subquery = ( + session.query(*QUERY_STATISTICS_SUMMARY_SUM) + .filter(StatisticsShortTerm.start >= bindparam("start_time")) + .filter(StatisticsShortTerm.start < bindparam("end_time")) + .subquery() + ) + query = ( + session.query(subquery) + .filter(subquery.c.rownum == 1) + .order_by(subquery.c.metadata_id) + ) + stats = execute(query.params(start_time=start_time, end_time=end_time)) - if stats: - for stat in stats: - metadata_id, start, last_reset, state, _sum, _ = stat - if metadata_id in summary: - summary[metadata_id].update( - { - "last_reset": process_timestamp(last_reset), - "state": state, - "sum": _sum, - } - ) - else: - summary[metadata_id] = { - "start": start_time, - "last_reset": process_timestamp(last_reset), - "state": state, - "sum": _sum, - } - else: - stmt = _compile_hourly_statistics_summary_sum_legacy_stmt(start_time, end_time) - stats = execute_stmt_lambda_element(session, stmt) - - if stats: - for metadata_id, group in groupby(stats, lambda stat: stat["metadata_id"]): # type: ignore[no-any-return] - ( - metadata_id, - last_reset, - state, - _sum, - ) = next(group) - if metadata_id in summary: - summary[metadata_id].update( - { - "start": start_time, - "last_reset": process_timestamp(last_reset), - "state": state, - "sum": _sum, - } - ) - else: - summary[metadata_id] = { - "start": start_time, + if stats: + for stat in stats: + metadata_id, start, last_reset, state, _sum, _ = stat + if metadata_id in summary: + summary[metadata_id].update( + { "last_reset": process_timestamp(last_reset), "state": state, "sum": _sum, } + ) + else: + summary[metadata_id] = { + "start": start_time, + "last_reset": process_timestamp(last_reset), + "state": state, + "sum": _sum, + } # Insert compiled hourly statistics in the database for metadata_id, stat in summary.items(): diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index ce8812a653e..b010ddfa853 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -52,12 +52,9 @@ SQLITE3_POSTFIXES = ["", "-wal", "-shm"] DEFAULT_YIELD_STATES_ROWS = 32768 MIN_VERSION_MARIA_DB = AwesomeVersion("10.3.0", AwesomeVersionStrategy.SIMPLEVER) -MIN_VERSION_MARIA_DB_ROWNUM = AwesomeVersion("10.2.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_MYSQL = AwesomeVersion("8.0.0", AwesomeVersionStrategy.SIMPLEVER) -MIN_VERSION_MYSQL_ROWNUM = AwesomeVersion("5.8.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_PGSQL = AwesomeVersion("12.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_SQLITE = AwesomeVersion("3.31.0", AwesomeVersionStrategy.SIMPLEVER) -MIN_VERSION_SQLITE_ROWNUM = AwesomeVersion("3.25.0", AwesomeVersionStrategy.SIMPLEVER) # This is the maximum time after the recorder ends the session # before we no longer consider startup to be a "restart" and we @@ -414,10 +411,6 @@ def setup_connection_for_dialect( version_string = result[0][0] version = _extract_version_from_server_response(version_string) - if version and version < MIN_VERSION_SQLITE_ROWNUM: - instance._db_supports_row_number = ( # pylint: disable=[protected-access] - False - ) if not version or version < MIN_VERSION_SQLITE: _fail_unsupported_version( version or version_string, "SQLite", MIN_VERSION_SQLITE @@ -448,19 +441,11 @@ def setup_connection_for_dialect( is_maria_db = "mariadb" in version_string.lower() if is_maria_db: - if version and version < MIN_VERSION_MARIA_DB_ROWNUM: - instance._db_supports_row_number = ( # pylint: disable=[protected-access] - False - ) if not version or version < MIN_VERSION_MARIA_DB: _fail_unsupported_version( version or version_string, "MariaDB", MIN_VERSION_MARIA_DB ) else: - if version and version < MIN_VERSION_MYSQL_ROWNUM: - instance._db_supports_row_number = ( # pylint: disable=[protected-access] - False - ) if not version or version < MIN_VERSION_MYSQL: _fail_unsupported_version( version or version_string, "MySQL", MIN_VERSION_MYSQL diff --git a/tests/components/recorder/test_util.py b/tests/components/recorder/test_util.py index e7685a93ff2..343c57045cf 100644 --- a/tests/components/recorder/test_util.py +++ b/tests/components/recorder/test_util.py @@ -166,15 +166,12 @@ async def test_last_run_was_recently_clean( @pytest.mark.parametrize( - "mysql_version, db_supports_row_number", - [ - ("10.3.0-MariaDB", True), - ("8.0.0", True), - ], + "mysql_version", + ["10.3.0-MariaDB", "8.0.0"], ) -def test_setup_connection_for_dialect_mysql(mysql_version, db_supports_row_number): +def test_setup_connection_for_dialect_mysql(mysql_version): """Test setting up the connection for a mysql dialect.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -199,18 +196,14 @@ def test_setup_connection_for_dialect_mysql(mysql_version, db_supports_row_numbe assert execute_args[0] == "SET session wait_timeout=28800" assert execute_args[1] == "SELECT VERSION()" - assert instance_mock._db_supports_row_number == db_supports_row_number - @pytest.mark.parametrize( - "sqlite_version, db_supports_row_number", - [ - ("3.31.0", True), - ], + "sqlite_version", + ["3.31.0"], ) -def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_number): +def test_setup_connection_for_dialect_sqlite(sqlite_version): """Test setting up the connection for a sqlite dialect.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -246,20 +239,16 @@ def test_setup_connection_for_dialect_sqlite(sqlite_version, db_supports_row_num assert execute_args[1] == "PRAGMA synchronous=NORMAL" assert execute_args[2] == "PRAGMA foreign_keys=ON" - assert instance_mock._db_supports_row_number == db_supports_row_number - @pytest.mark.parametrize( - "sqlite_version, db_supports_row_number", - [ - ("3.31.0", True), - ], + "sqlite_version", + ["3.31.0"], ) def test_setup_connection_for_dialect_sqlite_zero_commit_interval( - sqlite_version, db_supports_row_number + sqlite_version, ): """Test setting up the connection for a sqlite dialect with a zero commit interval.""" - instance_mock = MagicMock(_db_supports_row_number=True, commit_interval=0) + instance_mock = MagicMock(commit_interval=0) execute_args = [] close_mock = MagicMock() @@ -295,8 +284,6 @@ def test_setup_connection_for_dialect_sqlite_zero_commit_interval( assert execute_args[1] == "PRAGMA synchronous=FULL" assert execute_args[2] == "PRAGMA foreign_keys=ON" - assert instance_mock._db_supports_row_number == db_supports_row_number - @pytest.mark.parametrize( "mysql_version,message", @@ -317,7 +304,7 @@ def test_setup_connection_for_dialect_sqlite_zero_commit_interval( ) def test_fail_outdated_mysql(caplog, mysql_version, message): """Test setting up the connection for an outdated mysql version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -353,7 +340,7 @@ def test_fail_outdated_mysql(caplog, mysql_version, message): ) def test_supported_mysql(caplog, mysql_version): """Test setting up the connection for a supported mysql version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -396,7 +383,7 @@ def test_supported_mysql(caplog, mysql_version): ) def test_fail_outdated_pgsql(caplog, pgsql_version, message): """Test setting up the connection for an outdated PostgreSQL version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -429,7 +416,7 @@ def test_fail_outdated_pgsql(caplog, pgsql_version, message): ) def test_supported_pgsql(caplog, pgsql_version): """Test setting up the connection for a supported PostgreSQL version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -474,7 +461,7 @@ def test_supported_pgsql(caplog, pgsql_version): ) def test_fail_outdated_sqlite(caplog, sqlite_version, message): """Test setting up the connection for an outdated sqlite version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() @@ -510,7 +497,7 @@ def test_fail_outdated_sqlite(caplog, sqlite_version, message): ) def test_supported_sqlite(caplog, sqlite_version): """Test setting up the connection for a supported sqlite version.""" - instance_mock = MagicMock(_db_supports_row_number=True) + instance_mock = MagicMock() execute_args = [] close_mock = MagicMock() diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 9165fbc4354..66ed0032201 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -2279,13 +2279,7 @@ def test_compile_hourly_statistics_changing_statistics( assert "Error while processing event StatisticsTask" not in caplog.text -@pytest.mark.parametrize( - "db_supports_row_number,in_log,not_in_log", - [(True, "row_number", None), (False, None, "row_number")], -) -def test_compile_statistics_hourly_daily_monthly_summary( - hass_recorder, caplog, db_supports_row_number, in_log, not_in_log -): +def test_compile_statistics_hourly_daily_monthly_summary(hass_recorder, caplog): """Test compiling hourly statistics + monthly and daily summary.""" zero = dt_util.utcnow() # August 31st, 23:00 local time @@ -2299,7 +2293,6 @@ def test_compile_statistics_hourly_daily_monthly_summary( # Remove this after dropping the use of the hass_recorder fixture hass.config.set_time_zone("America/Regina") recorder = hass.data[DATA_INSTANCE] - recorder._db_supports_row_number = db_supports_row_number setup_component(hass, "sensor", {}) wait_recording_done(hass) # Wait for the sensor recorder platform to be added attributes = { @@ -2693,10 +2686,6 @@ def test_compile_statistics_hourly_daily_monthly_summary( assert stats == expected_stats assert "Error while processing event StatisticsTask" not in caplog.text - if in_log: - assert in_log in caplog.text - if not_in_log: - assert not_in_log not in caplog.text def record_states(hass, zero, entity_id, attributes, seq=None): From a6402697bb888cc61724d7ff8aa4379f14cff5a4 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 18 May 2022 21:02:30 -0700 Subject: [PATCH 601/930] Add display name for application credentials (#72053) * Add display name for application credentials * Rename display name to name * Improve test coverage for importing a named credential * Add a default credential name on import --- .../application_credentials/__init__.py | 19 ++++- .../application_credentials/test_init.py | 84 ++++++++++++++++++- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/application_credentials/__init__.py b/homeassistant/components/application_credentials/__init__.py index 9117a91c33d..1a128c5c378 100644 --- a/homeassistant/components/application_credentials/__init__.py +++ b/homeassistant/components/application_credentials/__init__.py @@ -15,7 +15,13 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.components.websocket_api.connection import ActiveConnection -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN, CONF_ID +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_DOMAIN, + CONF_ID, + CONF_NAME, +) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import collection, config_entry_oauth2_flow @@ -39,12 +45,14 @@ STORAGE_KEY = DOMAIN STORAGE_VERSION = 1 DATA_STORAGE = "storage" CONF_AUTH_DOMAIN = "auth_domain" +DEFAULT_IMPORT_NAME = "Import from configuration.yaml" CREATE_FIELDS = { vol.Required(CONF_DOMAIN): cv.string, vol.Required(CONF_CLIENT_ID): cv.string, vol.Required(CONF_CLIENT_SECRET): cv.string, vol.Optional(CONF_AUTH_DOMAIN): cv.string, + vol.Optional(CONF_NAME): cv.string, } UPDATE_FIELDS: dict = {} # Not supported @@ -55,6 +63,7 @@ class ClientCredential: client_id: str client_secret: str + name: str | None = None @dataclass @@ -122,7 +131,9 @@ class ApplicationCredentialsStorageCollection(collection.StorageCollection): item[CONF_AUTH_DOMAIN] if CONF_AUTH_DOMAIN in item else item[CONF_ID] ) credentials[auth_domain] = ClientCredential( - item[CONF_CLIENT_ID], item[CONF_CLIENT_SECRET] + client_id=item[CONF_CLIENT_ID], + client_secret=item[CONF_CLIENT_SECRET], + name=item.get(CONF_NAME), ) return credentials @@ -169,6 +180,7 @@ async def async_import_client_credential( CONF_CLIENT_SECRET: credential.client_secret, CONF_AUTH_DOMAIN: auth_domain if auth_domain else domain, } + item[CONF_NAME] = credential.name if credential.name else DEFAULT_IMPORT_NAME await storage_collection.async_import_item(item) @@ -191,11 +203,12 @@ class AuthImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation): authorization_server.authorize_url, authorization_server.token_url, ) + self._name = credential.name @property def name(self) -> str: """Name of the implementation.""" - return self.client_id + return self._name or self.client_id async def _async_provide_implementation( diff --git a/tests/components/application_credentials/test_init.py b/tests/components/application_credentials/test_init.py index b62f8a0139c..b89a60f42e4 100644 --- a/tests/components/application_credentials/test_init.py +++ b/tests/components/application_credentials/test_init.py @@ -13,13 +13,19 @@ import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components.application_credentials import ( CONF_AUTH_DOMAIN, + DEFAULT_IMPORT_NAME, DOMAIN, AuthImplementation, AuthorizationServer, ClientCredential, async_import_client_credential, ) -from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DOMAIN +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_DOMAIN, + CONF_NAME, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.setup import async_setup_component @@ -29,11 +35,13 @@ from tests.common import mock_platform CLIENT_ID = "some-client-id" CLIENT_SECRET = "some-client-secret" DEVELOPER_CREDENTIAL = ClientCredential(CLIENT_ID, CLIENT_SECRET) +NAMED_CREDENTIAL = ClientCredential(CLIENT_ID, CLIENT_SECRET, "Name") ID = "fake_integration_some_client_id" AUTHORIZE_URL = "https://example.com/auth" TOKEN_URL = "https://example.com/oauth2/v4/token" REFRESH_TOKEN = "mock-refresh-token" ACCESS_TOKEN = "mock-access-token" +NAME = "Name" TEST_DOMAIN = "fake_integration" @@ -118,6 +126,7 @@ class OAuthFixture: self.hass_client = hass_client self.aioclient_mock = aioclient_mock self.client_id = CLIENT_ID + self.title = CLIENT_ID async def complete_external_step( self, result: data_entry_flow.FlowResult @@ -152,7 +161,7 @@ class OAuthFixture: result = await self.hass.config_entries.flow.async_configure(result["flow_id"]) assert result.get("type") == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result.get("title") == self.client_id + assert result.get("title") == self.title assert "data" in result assert "token" in result["data"] return result @@ -348,6 +357,7 @@ async def test_websocket_import_config( CONF_CLIENT_SECRET: CLIENT_SECRET, "id": ID, CONF_AUTH_DOMAIN: TEST_DOMAIN, + CONF_NAME: DEFAULT_IMPORT_NAME, } ] @@ -375,6 +385,29 @@ async def test_import_duplicate_credentials( CONF_CLIENT_SECRET: CLIENT_SECRET, "id": ID, CONF_AUTH_DOMAIN: TEST_DOMAIN, + CONF_NAME: DEFAULT_IMPORT_NAME, + } + ] + + +@pytest.mark.parametrize("config_credential", [NAMED_CREDENTIAL]) +async def test_import_named_credential( + ws_client: ClientFixture, + config_credential: ClientCredential, + import_config_credential: Any, +): + """Test websocket list command for an imported credential.""" + client = await ws_client() + + # Imported creds returned from websocket + assert await client.cmd_result("list") == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + "id": ID, + CONF_AUTH_DOMAIN: TEST_DOMAIN, + CONF_NAME: NAME, } ] @@ -487,6 +520,7 @@ async def test_config_flow_multiple_entries( ) assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP oauth_fixture.client_id = CLIENT_ID + "2" + oauth_fixture.title = CLIENT_ID + "2" result = await oauth_fixture.complete_external_step(result) assert ( result["data"].get("auth_implementation") == "fake_integration_some_client_id2" @@ -532,6 +566,7 @@ async def test_config_flow_with_config_credential( TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + oauth_fixture.title = DEFAULT_IMPORT_NAME result = await oauth_fixture.complete_external_step(result) # Uses the imported auth domain for compatibility assert result["data"].get("auth_implementation") == TEST_DOMAIN @@ -653,6 +688,7 @@ async def test_platform_with_auth_implementation( TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + oauth_fixture.title = DEFAULT_IMPORT_NAME result = await oauth_fixture.complete_external_step(result) # Uses the imported auth domain for compatibility assert result["data"].get("auth_implementation") == TEST_DOMAIN @@ -667,3 +703,47 @@ async def test_websocket_integration_list(ws_client: ClientFixture): assert await client.cmd_result("config") == { "domains": ["example1", "example2"] } + + +async def test_name( + hass: HomeAssistant, ws_client: ClientFixture, oauth_fixture: OAuthFixture +): + """Test a credential with a name set.""" + client = await ws_client() + result = await client.cmd_result( + "create", + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + CONF_NAME: NAME, + }, + ) + assert result == { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + CONF_NAME: NAME, + "id": ID, + } + + result = await client.cmd_result("list") + assert result == [ + { + CONF_DOMAIN: TEST_DOMAIN, + CONF_CLIENT_ID: CLIENT_ID, + CONF_CLIENT_SECRET: CLIENT_SECRET, + CONF_NAME: NAME, + "id": ID, + } + ] + + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + oauth_fixture.title = NAME + result = await oauth_fixture.complete_external_step(result) + assert ( + result["data"].get("auth_implementation") == "fake_integration_some_client_id" + ) From 272e65f56db7affe84958d679ff3288a5b148d1f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 May 2022 23:27:31 -0500 Subject: [PATCH 602/930] Fix device_ids being filtered when entities also specified in the logbook (#72122) --- homeassistant/components/logbook/__init__.py | 5 ++--- tests/components/logbook/test_init.py | 9 ++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 806ba00d2c8..e2073800c25 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -59,7 +59,6 @@ from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, EntityFilter, convert_include_exclude_filter, - generate_filter, ) from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, @@ -579,8 +578,8 @@ def _get_events( ] = hass.data.get(DOMAIN, {}) format_time = _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat entity_name_cache = EntityNameCache(hass) - if entity_ids is not None: - entities_filter = generate_filter([], entity_ids, [], []) + if entity_ids or device_ids: + entities_filter = None def yield_rows(query: Query) -> Generator[Row, None, None]: """Yield rows from the database.""" diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 7657ebf2b83..a7420df9c5d 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -2546,10 +2546,13 @@ async def test_get_events_with_device_ids(hass, hass_ws_client, recorder_mock): assert response["id"] == 2 results = response["result"] - assert results[0]["entity_id"] == "light.kitchen" - assert results[0]["state"] == "on" + assert results[0]["domain"] == "test" + assert results[0]["message"] == "is on fire" + assert results[0]["name"] == "device name" assert results[1]["entity_id"] == "light.kitchen" - assert results[1]["state"] == "off" + assert results[1]["state"] == "on" + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "off" await client.send_json( { From c4f6fcc3d2e4872554d8c75ebfc28b315206f90a Mon Sep 17 00:00:00 2001 From: Adam James Date: Thu, 19 May 2022 07:28:55 +0100 Subject: [PATCH 603/930] Increase range of valid source IDs in nad (#72086) nad: Increase max source ID to 12 Tested on a NAD C658 with an MDC HDM-2 card installed. --- homeassistant/components/nad/media_player.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/nad/media_player.py b/homeassistant/components/nad/media_player.py index 2bcd7958de9..f031175a321 100644 --- a/homeassistant/components/nad/media_player.py +++ b/homeassistant/components/nad/media_player.py @@ -45,7 +45,8 @@ CONF_MAX_VOLUME = "max_volume" CONF_VOLUME_STEP = "volume_step" # for NADReceiverTCP CONF_SOURCE_DICT = "sources" # for NADReceiver -SOURCE_DICT_SCHEMA = vol.Schema({vol.Range(min=1, max=10): cv.string}) +# Max value based on a C658 with an MDC HDM-2 card installed +SOURCE_DICT_SCHEMA = vol.Schema({vol.Range(min=1, max=12): cv.string}) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { From be809980a119bdd8c4a9bfeddb276e6b4f683a79 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 08:57:02 +0200 Subject: [PATCH 604/930] Adjust device_automation type hints in deconz (#72129) --- homeassistant/components/deconz/device_trigger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index c92ad7f46dc..43ed3b2cdc4 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -703,7 +703,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str, -) -> list | None: +) -> list[dict[str, str]]: """List device triggers. Make sure device is a supported remote model. @@ -714,7 +714,7 @@ async def async_get_triggers( device = device_registry.devices[device_id] if device.model not in REMOTES: - return None + return [] triggers = [] for trigger, subtype in REMOTES[device.model].keys(): From 7e2f5968ccc5095a0d1707680a0f32d4bf23d3f2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 09:12:54 +0200 Subject: [PATCH 605/930] Adjust device_automation type hints in shelly (#72139) --- homeassistant/components/shelly/device_trigger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index 3e839507127..c7d0f92336c 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -108,9 +108,9 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Shelly devices.""" - triggers: list[dict[str, Any]] = [] + triggers: list[dict[str, str]] = [] if rpc_wrapper := get_rpc_device_wrapper(hass, device_id): input_triggers = get_rpc_input_triggers(rpc_wrapper.device) From 694dc97ac555d51c1769e7a3361038c9f7742b5a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 09:15:41 +0200 Subject: [PATCH 606/930] Adjust device_automation type hints in tasmota (#72140) --- homeassistant/components/tasmota/device_trigger.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/tasmota/device_trigger.py b/homeassistant/components/tasmota/device_trigger.py index aca5a2848e3..512fa5dd547 100644 --- a/homeassistant/components/tasmota/device_trigger.py +++ b/homeassistant/components/tasmota/device_trigger.py @@ -3,7 +3,6 @@ from __future__ import annotations from collections.abc import Callable import logging -from typing import Any import attr from hatasmota.models import DiscoveryHashType @@ -265,7 +264,7 @@ async def async_remove_triggers(hass: HomeAssistant, device_id: str) -> None: async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for a Tasmota device.""" triggers: list[dict[str, str]] = [] From 453c6af5956ee5c6e296cc69bc17613ee5f78804 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 09:16:46 +0200 Subject: [PATCH 607/930] Adjust device_automation type hints in arcam_fmj (#72128) --- homeassistant/components/arcam_fmj/device_trigger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index d1fca811fcc..593250e4983 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Arcam FMJ Receiver control.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -35,7 +33,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Arcam FMJ Receiver control devices.""" registry = entity_registry.async_get(hass) triggers = [] From f144b518b21a86a64445d19b9b8b87bd64b56ed6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 09:18:01 +0200 Subject: [PATCH 608/930] Adjust device_automation type hints in kodi (#72131) --- homeassistant/components/kodi/device_trigger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/kodi/device_trigger.py b/homeassistant/components/kodi/device_trigger.py index e4fe6cfa103..11d0b1567f9 100644 --- a/homeassistant/components/kodi/device_trigger.py +++ b/homeassistant/components/kodi/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Kodi.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -36,7 +34,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Kodi devices.""" registry = entity_registry.async_get(hass) triggers = [] From 1d6e40451274cbe8ccc6be256146193bcfe97df5 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 09:19:43 +0200 Subject: [PATCH 609/930] Adjust device_automation type hints in homekit_controller (#72130) --- homeassistant/components/homekit_controller/device_trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index aa2765d9be5..3ba2abe3339 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -240,7 +240,7 @@ def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], Any]): async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for homekit devices.""" if device_id not in hass.data.get(TRIGGERS, {}): From 9be7b02613ed22e8adfd139e5493e471c24a70a1 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 19 May 2022 10:36:22 +0200 Subject: [PATCH 610/930] Fix Motion Blinds checking interface for multiple gateways (#71474) * fix checking interface for multiple gateways * fix black * setup lock for shared multicast listener * fix black * bump motionblinds to 0.6.7 * compensate for extra Lock_key * unregister gateway when unloading * unsubscribe stop listener * fix black * only unsubscribe listener on last gateway remove * catch OSError for invalid interfaces * test coverage * make stop listen on last config entry more robust * also check ConfigEntryState * fix black --- .../components/motion_blinds/__init__.py | 72 ++++++++++++------- .../components/motion_blinds/const.py | 2 + .../components/motion_blinds/gateway.py | 2 + .../motion_blinds/test_config_flow.py | 12 ++-- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/motion_blinds/__init__.py b/homeassistant/components/motion_blinds/__init__.py index 63c80d33dba..95ac1c5fd44 100644 --- a/homeassistant/components/motion_blinds/__init__.py +++ b/homeassistant/components/motion_blinds/__init__.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from motionblinds import DEVICE_TYPES_WIFI, AsyncMotionMulticast, ParseException -from homeassistant.config_entries import ConfigEntry +from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -25,6 +25,8 @@ from .const import ( KEY_COORDINATOR, KEY_GATEWAY, KEY_MULTICAST_LISTENER, + KEY_SETUP_LOCK, + KEY_UNSUB_STOP, KEY_VERSION, MANUFACTURER, PLATFORMS, @@ -106,6 +108,7 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the motion_blinds components from a config entry.""" hass.data.setdefault(DOMAIN, {}) + setup_lock = hass.data[DOMAIN].setdefault(KEY_SETUP_LOCK, asyncio.Lock()) host = entry.data[CONF_HOST] key = entry.data[CONF_API_KEY] multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE) @@ -113,33 +116,41 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: entry.async_on_unload(entry.add_update_listener(update_listener)) - # check multicast interface - check_multicast_class = ConnectMotionGateway(hass, interface=multicast_interface) - working_interface = await check_multicast_class.async_check_interface(host, key) - if working_interface != multicast_interface: - data = {**entry.data, CONF_INTERFACE: working_interface} - hass.config_entries.async_update_entry(entry, data=data) - _LOGGER.debug( - "Motion Blinds interface updated from %s to %s, " - "this should only occur after a network change", - multicast_interface, - working_interface, - ) - # Create multicast Listener - if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]: - multicast = AsyncMotionMulticast(interface=working_interface) - hass.data[DOMAIN][KEY_MULTICAST_LISTENER] = multicast - # start listening for local pushes (only once) - await multicast.Start_listen() + async with setup_lock: + if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]: + # check multicast interface + check_multicast_class = ConnectMotionGateway( + hass, interface=multicast_interface + ) + working_interface = await check_multicast_class.async_check_interface( + host, key + ) + if working_interface != multicast_interface: + data = {**entry.data, CONF_INTERFACE: working_interface} + hass.config_entries.async_update_entry(entry, data=data) + _LOGGER.debug( + "Motion Blinds interface updated from %s to %s, " + "this should only occur after a network change", + multicast_interface, + working_interface, + ) - # register stop callback to shutdown listening for local pushes - def stop_motion_multicast(event): - """Stop multicast thread.""" - _LOGGER.debug("Shutting down Motion Listener") - multicast.Stop_listen() + multicast = AsyncMotionMulticast(interface=working_interface) + hass.data[DOMAIN][KEY_MULTICAST_LISTENER] = multicast + # start listening for local pushes (only once) + await multicast.Start_listen() - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_motion_multicast) + # register stop callback to shutdown listening for local pushes + def stop_motion_multicast(event): + """Stop multicast thread.""" + _LOGGER.debug("Shutting down Motion Listener") + multicast.Stop_listen() + + unsub = hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_STOP, stop_motion_multicast + ) + hass.data[DOMAIN][KEY_UNSUB_STOP] = unsub # Connect to motion gateway multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER] @@ -205,10 +216,19 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> ) if unload_ok: + multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER] + multicast.Unregister_motion_gateway(config_entry.data[CONF_HOST]) hass.data[DOMAIN].pop(config_entry.entry_id) - if len(hass.data[DOMAIN]) == 1: + loaded_entries = [ + entry + for entry in hass.config_entries.async_entries(DOMAIN) + if entry.state == ConfigEntryState.LOADED + ] + if len(loaded_entries) == 1: # No motion gateways left, stop Motion multicast + unsub_stop = hass.data[DOMAIN].pop(KEY_UNSUB_STOP) + unsub_stop() _LOGGER.debug("Shutting down Motion Listener") multicast = hass.data[DOMAIN].pop(KEY_MULTICAST_LISTENER) multicast.Stop_listen() diff --git a/homeassistant/components/motion_blinds/const.py b/homeassistant/components/motion_blinds/const.py index a35aeb6cd89..332a30a5e5f 100644 --- a/homeassistant/components/motion_blinds/const.py +++ b/homeassistant/components/motion_blinds/const.py @@ -16,6 +16,8 @@ KEY_GATEWAY = "gateway" KEY_API_LOCK = "api_lock" KEY_COORDINATOR = "coordinator" KEY_MULTICAST_LISTENER = "multicast_listener" +KEY_SETUP_LOCK = "setup_lock" +KEY_UNSUB_STOP = "unsub_stop" KEY_VERSION = "version" ATTR_WIDTH = "width" diff --git a/homeassistant/components/motion_blinds/gateway.py b/homeassistant/components/motion_blinds/gateway.py index 1775f7deb7d..218da9f625c 100644 --- a/homeassistant/components/motion_blinds/gateway.py +++ b/homeassistant/components/motion_blinds/gateway.py @@ -101,6 +101,8 @@ class ConnectMotionGateway: await check_multicast.Start_listen() except socket.gaierror: continue + except OSError: + continue # trigger test multicast self._gateway_device = MotionGateway( diff --git a/tests/components/motion_blinds/test_config_flow.py b/tests/components/motion_blinds/test_config_flow.py index 57ad3c20779..57ab45d9dbb 100644 --- a/tests/components/motion_blinds/test_config_flow.py +++ b/tests/components/motion_blinds/test_config_flow.py @@ -336,10 +336,14 @@ async def test_dhcp_flow(hass): assert result["step_id"] == "connect" assert result["errors"] == {} - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_API_KEY: TEST_API_KEY}, - ) + with patch( + "homeassistant.components.motion_blinds.gateway.AsyncMotionMulticast.Start_listen", + side_effect=OSError, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: TEST_API_KEY}, + ) assert result["type"] == "create_entry" assert result["title"] == DEFAULT_GATEWAY_NAME From bad245a856088c09c0159f76b2fa3790c3793f0c Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Thu, 19 May 2022 12:06:57 +0300 Subject: [PATCH 611/930] Fix Shelly triggers type hints (#72146) --- homeassistant/components/shelly/device_trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index c7d0f92336c..ae2eb7d6440 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -54,7 +54,7 @@ TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend( def append_input_triggers( - triggers: list[dict[str, Any]], + triggers: list[dict[str, str]], input_triggers: list[tuple[str, str]], device_id: str, ) -> None: From cfe9ea033ac9b75834cddf0e0f92177e05743532 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 19 May 2022 11:14:07 +0200 Subject: [PATCH 612/930] Split miio gateway coordinator (#69755) --- .../components/xiaomi_miio/__init__.py | 43 +++++++++---------- .../components/xiaomi_miio/gateway.py | 2 +- homeassistant/components/xiaomi_miio/light.py | 4 +- .../components/xiaomi_miio/sensor.py | 4 +- .../components/xiaomi_miio/switch.py | 4 +- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index 68d3fe04653..e2dc36ff568 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -405,36 +405,35 @@ async def async_setup_gateway_entry(hass: HomeAssistant, entry: ConfigEntry) -> hw_version=gateway_info.hardware_version, ) - def update_data(): - """Fetch data from the subdevice.""" - data = {} - for sub_device in gateway.gateway_device.devices.values(): + def update_data_factory(sub_device): + """Create update function for a subdevice.""" + + async def async_update_data(): + """Fetch data from the subdevice.""" try: - sub_device.update() + await hass.async_add_executor_job(sub_device.update) except GatewayException as ex: _LOGGER.error("Got exception while fetching the state: %s", ex) - data[sub_device.sid] = {ATTR_AVAILABLE: False} - else: - data[sub_device.sid] = {ATTR_AVAILABLE: True} - return data + return {ATTR_AVAILABLE: False} + return {ATTR_AVAILABLE: True} - async def async_update_data(): - """Fetch data from the subdevice using async_add_executor_job.""" - return await hass.async_add_executor_job(update_data) + return async_update_data - # Create update coordinator - coordinator = DataUpdateCoordinator( - hass, - _LOGGER, - name=name, - update_method=async_update_data, - # Polling interval. Will only be polled if there are subscribers. - update_interval=UPDATE_INTERVAL, - ) + coordinator_dict = {} + for sub_device in gateway.gateway_device.devices.values(): + # Create update coordinator + coordinator_dict[sub_device.sid] = DataUpdateCoordinator( + hass, + _LOGGER, + name=name, + update_method=update_data_factory(sub_device), + # Polling interval. Will only be polled if there are subscribers. + update_interval=UPDATE_INTERVAL, + ) hass.data[DOMAIN][entry.entry_id] = { CONF_GATEWAY: gateway.gateway_device, - KEY_COORDINATOR: coordinator, + KEY_COORDINATOR: coordinator_dict, } for platform in GATEWAY_PLATFORMS: diff --git a/homeassistant/components/xiaomi_miio/gateway.py b/homeassistant/components/xiaomi_miio/gateway.py index ebfb37ab8fb..6d0984a9aeb 100644 --- a/homeassistant/components/xiaomi_miio/gateway.py +++ b/homeassistant/components/xiaomi_miio/gateway.py @@ -169,4 +169,4 @@ class XiaomiGatewayDevice(CoordinatorEntity, Entity): if self.coordinator.data is None: return False - return self.coordinator.data[self._sub_device.sid][ATTR_AVAILABLE] + return self.coordinator.data[ATTR_AVAILABLE] diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index acb726e7c05..e97c6e76503 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -132,9 +132,11 @@ async def async_setup_entry( ) # Gateway sub devices sub_devices = gateway.devices - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] for sub_device in sub_devices.values(): if sub_device.device_type == "LightBulb": + coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][ + sub_device.sid + ] entities.append( XiaomiGatewayBulb(coordinator, sub_device, config_entry) ) diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index 0beac2c0041..df0c953d62a 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -681,8 +681,10 @@ async def async_setup_entry( ) # Gateway sub devices sub_devices = gateway.devices - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] for sub_device in sub_devices.values(): + coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][ + sub_device.sid + ] for sensor, description in SENSOR_TYPES.items(): if sensor not in sub_device.status: continue diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index 968abceb57c..05d6543e93d 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -369,10 +369,12 @@ async def async_setup_other_entry(hass, config_entry, async_add_entities): gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY] # Gateway sub devices sub_devices = gateway.devices - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] for sub_device in sub_devices.values(): if sub_device.device_type != "Switch": continue + coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR][ + sub_device.sid + ] switch_variables = set(sub_device.status) & set(GATEWAY_SWITCH_VARS) if switch_variables: entities.extend( From f7b96c87d02eb520bbdc9e30ff0c89b137d6b663 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 19 May 2022 13:06:56 +0200 Subject: [PATCH 613/930] Netgear test coverage (#72150) --- .../components/netgear/config_flow.py | 10 +++---- tests/components/netgear/test_config_flow.py | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/netgear/config_flow.py b/homeassistant/components/netgear/config_flow.py index 79053c712fc..9024d0510dd 100644 --- a/homeassistant/components/netgear/config_flow.py +++ b/homeassistant/components/netgear/config_flow.py @@ -191,11 +191,6 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): if errors: return await self._show_setup_form(user_input, errors) - # Check if already configured - info = await self.hass.async_add_executor_job(api.get_info) - await self.async_set_unique_id(info["SerialNumber"], raise_on_progress=False) - self._abort_if_unique_id_configured() - config_data = { CONF_USERNAME: username, CONF_PASSWORD: password, @@ -204,6 +199,11 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_SSL: api.ssl, } + # Check if already configured + info = await self.hass.async_add_executor_job(api.get_info) + await self.async_set_unique_id(info["SerialNumber"], raise_on_progress=False) + self._abort_if_unique_id_configured(updates=config_data) + if info.get("ModelName") is not None and info.get("DeviceName") is not None: name = f"{info['ModelName']} - {info['DeviceName']}" else: diff --git a/tests/components/netgear/test_config_flow.py b/tests/components/netgear/test_config_flow.py index 33c634e250a..d46284f5049 100644 --- a/tests/components/netgear/test_config_flow.py +++ b/tests/components/netgear/test_config_flow.py @@ -59,6 +59,7 @@ SSL = False USERNAME = "Home_Assistant" PASSWORD = "password" SSDP_URL = f"http://{HOST}:{PORT}/rootDesc.xml" +SSDP_URLipv6 = f"http://[::ffff:a00:1]:{PORT}/rootDesc.xml" SSDP_URL_SLL = f"https://{HOST}:{PORT}/rootDesc.xml" @@ -234,6 +235,32 @@ async def test_ssdp_already_configured(hass): assert result["reason"] == "already_configured" +async def test_ssdp_ipv6(hass): + """Test ssdp abort when using a ipv6 address.""" + MockConfigEntry( + domain=DOMAIN, + data={CONF_PASSWORD: PASSWORD}, + unique_id=SERIAL, + ).add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_SSDP}, + data=ssdp.SsdpServiceInfo( + ssdp_usn="mock_usn", + ssdp_st="mock_st", + ssdp_location=SSDP_URLipv6, + upnp={ + ssdp.ATTR_UPNP_MODEL_NUMBER: "RBR20", + ssdp.ATTR_UPNP_PRESENTATION_URL: URL, + ssdp.ATTR_UPNP_SERIAL: SERIAL, + }, + ), + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "not_ipv4_address" + + async def test_ssdp(hass, service): """Test ssdp step.""" result = await hass.config_entries.flow.async_init( From 9d377aabdbb0a5e3c84adffd5043c2717ac68011 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 19 May 2022 14:11:29 +0200 Subject: [PATCH 614/930] Fix Google tests (#72158) --- tests/components/google/test_config_flow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 9339ca9988f..88a090773bc 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -99,7 +99,7 @@ async def test_full_flow_yaml_creds( ) assert result.get("type") == "create_entry" - assert result.get("title") == "client-id" + assert result.get("title") == "Import from configuration.yaml" assert "data" in result data = result["data"] assert "token" in data @@ -161,7 +161,7 @@ async def test_full_flow_application_creds( ) assert result.get("type") == "create_entry" - assert result.get("title") == "client-id" + assert result.get("title") == "Import from configuration.yaml" assert "data" in result data = result["data"] assert "token" in data @@ -278,7 +278,7 @@ async def test_exchange_error( ) assert result.get("type") == "create_entry" - assert result.get("title") == "client-id" + assert result.get("title") == "Import from configuration.yaml" assert "data" in result data = result["data"] assert "token" in data From ed1c2ea2b8e4d2368c0b18a27c4e54c67f11a9e9 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 19 May 2022 15:04:53 +0200 Subject: [PATCH 615/930] Move manual configuration of MQTT fan and light to the integration key (#71676) * Processing yaml config through entry setup * Setup all platforms * Update homeassistant/components/mqtt/__init__.py Co-authored-by: Martin Hjelmare * adjust mock_mqtt - reference config from cache * Fix test config entry override * Add tests yaml setup * additional tests * Introduce PLATFORM_SCHEMA_MODERN * recover temporary MQTT_BASE_PLATFORM_SCHEMA * Allow extra key in light base schema, restore test * Fix test for exception on platform key * One deprecation message per platform * Remove deprecation checks from modern schema * Update homeassistant/components/mqtt/fan.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/fan.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/light/__init__.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/light/__init__.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/light/schema_json.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/light/schema_template.py Co-authored-by: Erik Montnemery * Update homeassistant/components/mqtt/mixins.py Co-authored-by: Erik Montnemery * rename validate_modern_schema * Do not fail platform if a single config is broken * Update homeassistant/components/mqtt/__init__.py Co-authored-by: Erik Montnemery * Fix tests on asserting log * Update log. Make helper transparant, remove patch * Perform parallel processing * Update tests/components/mqtt/test_init.py Co-authored-by: Martin Hjelmare * Apply suggestions from code review Co-authored-by: Martin Hjelmare * Update homeassistant/components/mqtt/mixins.py Co-authored-by: Martin Hjelmare * black * Fix tests and add #new_format anchor Co-authored-by: Martin Hjelmare Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 47 ++++++++++-- homeassistant/components/mqtt/const.py | 2 + homeassistant/components/mqtt/discovery.py | 6 +- homeassistant/components/mqtt/fan.py | 28 +++++++- .../components/mqtt/light/__init__.py | 57 +++++++++++++-- .../components/mqtt/light/schema_basic.py | 7 +- .../components/mqtt/light/schema_json.py | 10 ++- .../components/mqtt/light/schema_template.py | 7 +- homeassistant/components/mqtt/mixins.py | 52 ++++++++++++++ tests/components/mqtt/test_common.py | 21 ++++++ tests/components/mqtt/test_fan.py | 13 ++++ tests/components/mqtt/test_init.py | 71 ++++++++++++++++++- tests/components/mqtt/test_light.py | 13 ++++ tests/components/mqtt/test_light_json.py | 13 ++++ tests/components/mqtt/test_light_template.py | 13 ++++ tests/conftest.py | 7 +- 16 files changed, 339 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 162e344f852..e17a31480b1 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -81,6 +81,8 @@ from .const import ( CONF_TLS_VERSION, CONF_TOPIC, CONF_WILL_MESSAGE, + CONFIG_ENTRY_IS_SETUP, + DATA_CONFIG_ENTRY_LOCK, DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, DEFAULT_BIRTH, @@ -171,7 +173,6 @@ PLATFORMS = [ Platform.VACUUM, ] - CLIENT_KEY_AUTH_MSG = ( "client_key and client_cert must both be present in " "the MQTT broker configuration" @@ -187,7 +188,14 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema( required=True, ) -CONFIG_SCHEMA_BASE = vol.Schema( +PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( + { + vol.Optional(Platform.FAN.value): cv.ensure_list, + vol.Optional(Platform.LIGHT.value): cv.ensure_list, + } +) + +CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( { vol.Optional(CONF_CLIENT_ID): cv.string, vol.Optional(CONF_KEEPALIVE, default=DEFAULT_KEEPALIVE): vol.All( @@ -253,10 +261,28 @@ SCHEMA_BASE = { vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, } +MQTT_BASE_SCHEMA = vol.Schema(SCHEMA_BASE) + +# Will be removed when all platforms support a modern platform schema MQTT_BASE_PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(SCHEMA_BASE) +# Will be removed when all platforms support a modern platform schema +MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, + vol.Optional(CONF_VALUE_TEMPLATE): cv.template, + } +) +# Will be removed when all platforms support a modern platform schema +MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( + { + vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, + vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, + vol.Optional(CONF_STATE_TOPIC): valid_subscribe_topic, + } +) # Sensor type platforms subscribe to MQTT events -MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( +MQTT_RO_SCHEMA = MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_STATE_TOPIC): valid_subscribe_topic, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, @@ -264,7 +290,7 @@ MQTT_RO_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( ) # Switch type platforms publish to MQTT and may subscribe -MQTT_RW_PLATFORM_SCHEMA = MQTT_BASE_PLATFORM_SCHEMA.extend( +MQTT_RW_SCHEMA = MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_COMMAND_TOPIC): valid_publish_topic, vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean, @@ -774,6 +800,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ), ) + # setup platforms and discovery + hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() + hass.data[CONFIG_ENTRY_IS_SETUP] = set() + + async with hass.data[DATA_CONFIG_ENTRY_LOCK]: + for component in PLATFORMS: + config_entries_key = f"{component}.mqtt" + if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: + hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, component) + ) + if conf.get(CONF_DISCOVERY): await _async_setup_discovery(hass, conf, entry) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 69865733763..106d0310158 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -28,6 +28,8 @@ CONF_CLIENT_CERT = "client_cert" CONF_TLS_INSECURE = "tls_insecure" CONF_TLS_VERSION = "tls_version" +CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" +DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" DATA_MQTT_CONFIG = "mqtt_config" DATA_MQTT_RELOAD_NEEDED = "mqtt_reload_needed" diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index fae443dc411..8685c790fd2 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -27,6 +27,8 @@ from .const import ( ATTR_DISCOVERY_TOPIC, CONF_AVAILABILITY, CONF_TOPIC, + CONFIG_ENTRY_IS_SETUP, + DATA_CONFIG_ENTRY_LOCK, DOMAIN, ) @@ -62,8 +64,6 @@ SUPPORTED_COMPONENTS = [ ALREADY_DISCOVERED = "mqtt_discovered_components" PENDING_DISCOVERED = "mqtt_pending_components" -CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" -DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" DATA_CONFIG_FLOW_LOCK = "mqtt_discovery_config_flow_lock" DISCOVERY_UNSUBSCRIBE = "mqtt_discovery_unsubscribe" INTEGRATION_UNSUBSCRIBE = "mqtt_integration_discovery_unsubscribe" @@ -258,9 +258,7 @@ async def async_start( # noqa: C901 hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None ) - hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() hass.data[DATA_CONFIG_FLOW_LOCK] = asyncio.Lock() - hass.data[CONFIG_ENTRY_IS_SETUP] = set() hass.data[ALREADY_DISCOVERED] = {} hass.data[PENDING_DISCOVERED] = {} diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index ff2eab7a68f..f2b738cd2bb 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,6 +1,7 @@ """Support for MQTT fans.""" from __future__ import annotations +import asyncio import functools import logging import math @@ -49,8 +50,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) CONF_PERCENTAGE_STATE_TOPIC = "percentage_state_topic" @@ -122,7 +125,7 @@ def valid_preset_mode_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -172,7 +175,15 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) +# Configuring MQTT Fans under the fan platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), + valid_speed_range_configuration, + valid_preset_mode_configuration, + warn_for_legacy_schema(fan.DOMAIN), +) + +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, valid_speed_range_configuration, valid_preset_mode_configuration, @@ -201,7 +212,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT fan through configuration.yaml.""" + """Set up MQTT fans configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, fan.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -212,7 +224,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT fan dynamically through MQTT discovery.""" + """Set up MQTT fan through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, fan.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/light/__init__.py b/homeassistant/components/mqtt/light/__init__.py index d78cd5e7baa..ab2a3462615 100644 --- a/homeassistant/components/mqtt/light/__init__.py +++ b/homeassistant/components/mqtt/light/__init__.py @@ -1,36 +1,47 @@ """Support for MQTT lights.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol from homeassistant.components import light +from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from ..mixins import async_setup_entry_helper, async_setup_platform_helper +from ..mixins import ( + async_get_platform_config_from_yaml, + async_setup_entry_helper, + async_setup_platform_helper, + warn_for_legacy_schema, +) from .schema import CONF_SCHEMA, MQTT_LIGHT_SCHEMA_SCHEMA from .schema_basic import ( DISCOVERY_SCHEMA_BASIC, PLATFORM_SCHEMA_BASIC, + PLATFORM_SCHEMA_MODERN_BASIC, async_setup_entity_basic, ) from .schema_json import ( DISCOVERY_SCHEMA_JSON, PLATFORM_SCHEMA_JSON, + PLATFORM_SCHEMA_MODERN_JSON, async_setup_entity_json, ) from .schema_template import ( DISCOVERY_SCHEMA_TEMPLATE, + PLATFORM_SCHEMA_MODERN_TEMPLATE, PLATFORM_SCHEMA_TEMPLATE, async_setup_entity_template, ) def validate_mqtt_light_discovery(value): - """Validate MQTT light schema.""" + """Validate MQTT light schema for.""" schemas = { "basic": DISCOVERY_SCHEMA_BASIC, "json": DISCOVERY_SCHEMA_JSON, @@ -49,14 +60,31 @@ def validate_mqtt_light(value): return schemas[value[CONF_SCHEMA]](value) +def validate_mqtt_light_modern(value): + """Validate MQTT light schema.""" + schemas = { + "basic": PLATFORM_SCHEMA_MODERN_BASIC, + "json": PLATFORM_SCHEMA_MODERN_JSON, + "template": PLATFORM_SCHEMA_MODERN_TEMPLATE, + } + return schemas[value[CONF_SCHEMA]](value) + + DISCOVERY_SCHEMA = vol.All( MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light_discovery, ) - +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( - MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_light + cv.PLATFORM_SCHEMA.extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema, extra=vol.ALLOW_EXTRA), + validate_mqtt_light, + warn_for_legacy_schema(light.DOMAIN), +) + +PLATFORM_SCHEMA_MODERN = vol.All( + MQTT_LIGHT_SCHEMA_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), + validate_mqtt_light_modern, ) @@ -66,14 +94,29 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT light through configuration.yaml.""" + """Set up MQTT light through configuration.yaml (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, light.DOMAIN, config, async_add_entities, _async_setup_entity ) -async def async_setup_entry(hass, config_entry, async_add_entities): - """Set up MQTT light dynamically through MQTT discovery.""" +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up MQTT lights configured under the light platform key (deprecated).""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, light.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index 497e8186fd0..eb4ec264981 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -156,7 +156,7 @@ VALUE_TEMPLATE_KEYS = [ ] _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic, @@ -220,13 +220,14 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_BASIC = vol.All( # CONF_WHITE_VALUE_* is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE_COMMAND_TOPIC), cv.deprecated(CONF_WHITE_VALUE_SCALE), cv.deprecated(CONF_WHITE_VALUE_STATE_TOPIC), cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) DISCOVERY_SCHEMA_BASIC = vol.All( @@ -240,6 +241,8 @@ DISCOVERY_SCHEMA_BASIC = vol.All( _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) +PLATFORM_SCHEMA_MODERN_BASIC = _PLATFORM_SCHEMA_BASE + async def async_setup_entity_basic( hass, config, async_add_entities, config_entry, discovery_data=None diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index c1e0d7467e0..2049818ab31 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -103,7 +103,7 @@ def valid_color_configuration(config): _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BRIGHTNESS, default=DEFAULT_BRIGHTNESS): cv.boolean, vol.Optional( @@ -146,10 +146,11 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_JSON = vol.All( # CONF_WHITE_VALUE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), valid_color_configuration, ) @@ -160,6 +161,11 @@ DISCOVERY_SCHEMA_JSON = vol.All( valid_color_configuration, ) +PLATFORM_SCHEMA_MODERN_JSON = vol.All( + _PLATFORM_SCHEMA_BASE, + valid_color_configuration, +) + async def async_setup_entity_json( hass, config: ConfigType, async_add_entities, config_entry, discovery_data diff --git a/homeassistant/components/mqtt/light/schema_template.py b/homeassistant/components/mqtt/light/schema_template.py index a98f634642d..0165bfc8efa 100644 --- a/homeassistant/components/mqtt/light/schema_template.py +++ b/homeassistant/components/mqtt/light/schema_template.py @@ -67,7 +67,7 @@ CONF_RED_TEMPLATE = "red_template" CONF_WHITE_VALUE_TEMPLATE = "white_value_template" _PLATFORM_SCHEMA_BASE = ( - mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( + mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_BLUE_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_TEMPLATE): cv.template, @@ -90,10 +90,11 @@ _PLATFORM_SCHEMA_BASE = ( .extend(MQTT_LIGHT_SCHEMA_SCHEMA.schema) ) +# Configuring MQTT Lights under the light platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA_TEMPLATE = vol.All( # CONF_WHITE_VALUE_TEMPLATE is deprecated, support will be removed in release 2022.9 cv.deprecated(CONF_WHITE_VALUE_TEMPLATE), - _PLATFORM_SCHEMA_BASE, + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), ) DISCOVERY_SCHEMA_TEMPLATE = vol.All( @@ -102,6 +103,8 @@ DISCOVERY_SCHEMA_TEMPLATE = vol.All( _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), ) +PLATFORM_SCHEMA_MODERN_TEMPLATE = _PLATFORM_SCHEMA_BASE + async def async_setup_entity_template( hass, config, async_add_entities, config_entry, discovery_data diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 43f75f08459..a46debeae54 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -9,6 +9,7 @@ from typing import Any, Protocol, cast, final import voluptuous as vol +from homeassistant.config import async_log_exception from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CONFIGURATION_URL, @@ -64,6 +65,7 @@ from .const import ( CONF_ENCODING, CONF_QOS, CONF_TOPIC, + DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_NEEDED, DEFAULT_ENCODING, DEFAULT_PAYLOAD_AVAILABLE, @@ -223,6 +225,31 @@ MQTT_ENTITY_COMMON_SCHEMA = MQTT_AVAILABILITY_SCHEMA.extend( ) +def warn_for_legacy_schema(domain: str) -> Callable: + """Warn once when a legacy platform schema is used.""" + warned = set() + + def validator(config: ConfigType) -> ConfigType: + """Return a validator.""" + nonlocal warned + + if domain in warned: + return config + + _LOGGER.warning( + "Manually configured MQTT %s(s) found under platform key '%s', " + "please move to the mqtt integration key, see " + "https://www.home-assistant.io/integrations/%s.mqtt/#new_format", + domain, + domain, + domain, + ) + warned.add(domain) + return config + + return validator + + class SetupEntity(Protocol): """Protocol type for async_setup_entities.""" @@ -237,6 +264,31 @@ class SetupEntity(Protocol): """Define setup_entities type.""" +async def async_get_platform_config_from_yaml( + hass: HomeAssistant, domain: str, schema: vol.Schema +) -> list[ConfigType]: + """Return a list of validated configurations for the domain.""" + + def async_validate_config( + hass: HomeAssistant, + config: list[ConfigType], + ) -> list[ConfigType]: + """Validate config.""" + validated_config = [] + for config_item in config: + try: + validated_config.append(schema(config_item)) + except vol.MultipleInvalid as err: + async_log_exception(err, domain, config_item, hass) + + return validated_config + + config_yaml: ConfigType = hass.data.get(DATA_MQTT_CONFIG, {}) + if not (platform_configs := config_yaml.get(domain)): + return [] + return async_validate_config(hass, platform_configs) + + async def async_setup_entry_helper(hass, domain, async_setup, schema): """Set up entity, automation or tag creation dynamically through MQTT discovery.""" diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index 8cf7353d196..b5bb5732617 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -1690,3 +1690,24 @@ async def help_test_reloadable_late(hass, caplog, tmp_path, domain, config): assert hass.states.get(f"{domain}.test_new_1") assert hass.states.get(f"{domain}.test_new_2") assert hass.states.get(f"{domain}.test_new_3") + + +async def help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + platform, + config, +): + """Help to test setup from yaml through configuration entry.""" + config_structure = {mqtt.DOMAIN: {platform: config}} + + await async_setup_component(hass, mqtt.DOMAIN, config_structure) + # Mock config entry + entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) + entry.add_to_hass(hass) + + with patch("paho.mqtt.client.Client") as mock_client: + mock_client().connect = lambda *args: 0 + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 64b5d272af8..1a533db63c0 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -55,6 +55,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1805,3 +1806,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = fan.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = fan.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 543174653ec..a370bd67ec1 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1,5 +1,6 @@ """The tests for the MQTT component.""" import asyncio +import copy from datetime import datetime, timedelta from functools import partial import json @@ -30,6 +31,8 @@ from homeassistant.helpers.entity import Entity from homeassistant.setup import async_setup_component from homeassistant.util.dt import utcnow +from .test_common import help_test_setup_manual_entity_from_yaml + from tests.common import ( MockConfigEntry, async_fire_mqtt_message, @@ -1279,6 +1282,51 @@ async def test_setup_override_configuration(hass, caplog, tmp_path): assert calls_username_password_set[0][1] == "somepassword" +async def test_setup_manual_mqtt_with_platform_key(hass, caplog, tmp_path): + """Test set up a manual MQTT item with a platform key.""" + config = {"platform": "mqtt", "name": "test", "command_topic": "test-topic"} + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + ) + assert ( + "Invalid config for [light]: [platform] is an invalid option for [light]. " + "Check: light->platform. (See ?, line ?)" in caplog.text + ) + + +async def test_setup_manual_mqtt_with_invalid_config(hass, caplog, tmp_path): + """Test set up a manual MQTT item with an invalid config.""" + config = {"name": "test"} + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + ) + assert ( + "Invalid config for [light]: required key not provided @ data['command_topic']." + " Got None. (See ?, line ?)" in caplog.text + ) + + +async def test_setup_manual_mqtt_empty_platform(hass, caplog, tmp_path): + """Test set up a manual MQTT platform without items.""" + config = None + await help_test_setup_manual_entity_from_yaml( + hass, + caplog, + tmp_path, + "light", + config, + ) + assert "voluptuous.error.MultipleInvalid" not in caplog.text + + async def test_setup_mqtt_client_protocol(hass): """Test MQTT client protocol setup.""" entry = MockConfigEntry( @@ -1628,7 +1676,8 @@ async def test_setup_entry_with_config_override(hass, device_reg, mqtt_client_mo # User sets up a config entry entry = MockConfigEntry(domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}) entry.add_to_hass(hass) - assert await hass.config_entries.async_setup(entry.entry_id) + with patch("homeassistant.components.mqtt.PLATFORMS", []): + assert await hass.config_entries.async_setup(entry.entry_id) # Discover a device to verify the entry was setup correctly async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) @@ -2413,3 +2462,23 @@ async def test_subscribe_connection_status(hass, mqtt_mock, mqtt_client_mock): assert len(mqtt_connected_calls) == 2 assert mqtt_connected_calls[0] is True assert mqtt_connected_calls[1] is False + + +async def test_one_deprecation_warning_per_platform(hass, mqtt_mock, caplog): + """Test a deprecation warning is is logged once per platform.""" + platform = "light" + config = {"platform": "mqtt", "command_topic": "test-topic"} + config1 = copy.deepcopy(config) + config1["name"] = "test1" + config2 = copy.deepcopy(config) + config2["name"] = "test2" + await async_setup_component(hass, platform, {platform: [config1, config2]}) + await hass.async_block_till_done() + count = 0 + for record in caplog.records: + if record.levelname == "WARNING" and ( + f"Manually configured MQTT {platform}(s) found under platform key '{platform}'" + in record.message + ): + count += 1 + assert count == 1 diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index ee59928c0c8..957178da14f 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -237,6 +237,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -3674,3 +3675,15 @@ async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): state = hass.states.get("light.test") assert state.state == STATE_ON assert state.attributes.get("effect") == "colorloop" + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_light_json.py b/tests/components/mqtt/test_light_json.py index 93bb9b0f573..962bf534370 100644 --- a/tests/components/mqtt/test_light_json.py +++ b/tests/components/mqtt/test_light_json.py @@ -131,6 +131,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -2038,3 +2039,15 @@ async def test_encoding_subscribable_topics( init_payload, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_light_template.py b/tests/components/mqtt/test_light_template.py index 4461cf14ef4..a88fc094f6d 100644 --- a/tests/components/mqtt/test_light_template.py +++ b/tests/components/mqtt/test_light_template.py @@ -69,6 +69,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1213,3 +1214,15 @@ async def test_encoding_subscribable_topics( attribute_value, init_payload, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = light.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/conftest.py b/tests/conftest.py index 37bdb05faf7..8ea6e114e9a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -561,9 +561,10 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config): ) entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + # Do not forward the entry setup to the components here + with patch("homeassistant.components.mqtt.PLATFORMS", []): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() mqtt_component_mock = MagicMock( return_value=hass.data["mqtt"], From f166a47df38a7dceb3ab677ecb33e4ea873a25c7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 15:50:32 +0200 Subject: [PATCH 616/930] Adjust device_automation type hints in nest (#72135) --- homeassistant/components/nest/device_trigger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index 1f7f2b642dc..7bdbab214b8 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Nest.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -66,7 +64,7 @@ async def async_get_device_trigger_types( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for a Nest device.""" nest_device_id = async_get_nest_device_id(hass, device_id) if not nest_device_id: From c27d5631643bd09e0ca965674d6545f5cb31a1c2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 19 May 2022 16:19:39 +0200 Subject: [PATCH 617/930] Make changes to application_credentials trigger full CI run (#72157) --- .core_files.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.core_files.yaml b/.core_files.yaml index 654730e5613..2928d450ce2 100644 --- a/.core_files.yaml +++ b/.core_files.yaml @@ -48,6 +48,7 @@ base_platforms: &base_platforms components: &components - homeassistant/components/alert/** - homeassistant/components/alexa/** + - homeassistant/components/application_credentials/** - homeassistant/components/auth/** - homeassistant/components/automation/** - homeassistant/components/backup/** From 1e1016aa5fcc8ddb2bfd83530c1dc081333be0b6 Mon Sep 17 00:00:00 2001 From: Matrix Date: Thu, 19 May 2022 22:43:32 +0800 Subject: [PATCH 618/930] Add yolink binary sensor (#72000) * Add binary sensor platform * Add untest file to .coveragerc * change attr default --- .coveragerc | 1 + homeassistant/components/yolink/__init__.py | 2 +- .../components/yolink/binary_sensor.py | 90 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/yolink/binary_sensor.py diff --git a/.coveragerc b/.coveragerc index 5fabf2851fb..6cc9ffda714 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1472,6 +1472,7 @@ omit = homeassistant/components/yi/camera.py homeassistant/components/yolink/__init__.py homeassistant/components/yolink/api.py + homeassistant/components/yolink/binary_sensor.py homeassistant/components/yolink/const.py homeassistant/components/yolink/coordinator.py homeassistant/components/yolink/entity.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index d31a082f82f..8a8afb2f1b3 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -21,7 +21,7 @@ SCAN_INTERVAL = timedelta(minutes=5) _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/yolink/binary_sensor.py b/homeassistant/components/yolink/binary_sensor.py new file mode 100644 index 00000000000..c3f2d10cf4c --- /dev/null +++ b/homeassistant/components/yolink/binary_sensor.py @@ -0,0 +1,90 @@ +"""YoLink BinarySensor.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from yolink.device import YoLinkDevice + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATOR, ATTR_DEVICE_DOOR_SENSOR, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +@dataclass +class YoLinkBinarySensorEntityDescription(BinarySensorEntityDescription): + """YoLink BinarySensorEntityDescription.""" + + exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True + value: Callable[[str], bool | None] = lambda _: None + + +SENSOR_DEVICE_TYPE = [ATTR_DEVICE_DOOR_SENSOR] + +SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( + YoLinkBinarySensorEntityDescription( + key="state", + icon="mdi:door", + device_class=BinarySensorDeviceClass.DOOR, + name="state", + value=lambda value: value == "open", + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink Sensor from a config entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] + sensor_devices = [ + device + for device in coordinator.yl_devices + if device.device_type in SENSOR_DEVICE_TYPE + ] + entities = [] + for sensor_device in sensor_devices: + for description in SENSOR_TYPES: + if description.exists_fn(sensor_device): + entities.append( + YoLinkBinarySensorEntity(coordinator, description, sensor_device) + ) + async_add_entities(entities) + + +class YoLinkBinarySensorEntity(YoLinkEntity, BinarySensorEntity): + """YoLink Sensor Entity.""" + + entity_description: YoLinkBinarySensorEntityDescription + + def __init__( + self, + coordinator: YoLinkCoordinator, + description: YoLinkBinarySensorEntityDescription, + device: YoLinkDevice, + ) -> None: + """Init YoLink Sensor.""" + super().__init__(coordinator, device) + self.entity_description = description + self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" + self._attr_name = f"{device.device_name} ({self.entity_description.name})" + + @callback + def update_entity_state(self, state: dict) -> None: + """Update HA Entity State.""" + self._attr_is_on = self.entity_description.value( + state[self.entity_description.key] + ) + self.async_write_ha_state() From b65b47f25ee9b44501f9865300628043eb004948 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 17:05:25 +0200 Subject: [PATCH 619/930] Cleanup zha async method which is not awaiting (#72093) * Cleanup zha async method which is not awaiting * Missed a commit --- homeassistant/components/zha/core/helpers.py | 3 ++- homeassistant/components/zha/device_action.py | 4 ++-- homeassistant/components/zha/device_trigger.py | 6 +++--- homeassistant/components/zha/diagnostics.py | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index 19a1d3fef9a..a101720e8df 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -160,7 +160,8 @@ def async_cluster_exists(hass, cluster_id): return False -async def async_get_zha_device(hass, device_id): +@callback +def async_get_zha_device(hass, device_id): """Get a ZHA device for the given device registry id.""" device_registry = dr.async_get(hass) registry_device = device_registry.async_get(device_id) diff --git a/homeassistant/components/zha/device_action.py b/homeassistant/components/zha/device_action.py index 36696517eb6..f941b559cc7 100644 --- a/homeassistant/components/zha/device_action.py +++ b/homeassistant/components/zha/device_action.py @@ -61,7 +61,7 @@ async def async_get_actions( ) -> list[dict[str, str]]: """List device actions.""" try: - zha_device = await async_get_zha_device(hass, device_id) + zha_device = async_get_zha_device(hass, device_id) except (KeyError, AttributeError): return [] cluster_channels = [ @@ -89,7 +89,7 @@ async def _execute_service_based_action( action_type = config[CONF_TYPE] service_name = SERVICE_NAMES[action_type] try: - zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID]) + zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) except (KeyError, AttributeError): return diff --git a/homeassistant/components/zha/device_trigger.py b/homeassistant/components/zha/device_trigger.py index 03bdc32e6a6..d81d8164bd4 100644 --- a/homeassistant/components/zha/device_trigger.py +++ b/homeassistant/components/zha/device_trigger.py @@ -28,7 +28,7 @@ async def async_validate_trigger_config(hass, config): if "zha" in hass.config.components: trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) try: - zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID]) + zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) except (KeyError, AttributeError) as err: raise InvalidDeviceAutomationConfig from err if ( @@ -44,7 +44,7 @@ async def async_attach_trigger(hass, config, action, automation_info): """Listen for state changes based on configuration.""" trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) try: - zha_device = await async_get_zha_device(hass, config[CONF_DEVICE_ID]) + zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) except (KeyError, AttributeError): return None @@ -71,7 +71,7 @@ async def async_get_triggers(hass, device_id): Make sure the device supports device automations and if it does return the trigger list. """ - zha_device = await async_get_zha_device(hass, device_id) + zha_device = async_get_zha_device(hass, device_id) if not zha_device.device_automation_triggers: return diff --git a/homeassistant/components/zha/diagnostics.py b/homeassistant/components/zha/diagnostics.py index ec8dab241de..5ae2ff23e96 100644 --- a/homeassistant/components/zha/diagnostics.py +++ b/homeassistant/components/zha/diagnostics.py @@ -76,5 +76,5 @@ async def async_get_device_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry, device: dr.DeviceEntry ) -> dict: """Return diagnostics for a device.""" - zha_device: ZHADevice = await async_get_zha_device(hass, device.id) + zha_device: ZHADevice = async_get_zha_device(hass, device.id) return async_redact_data(zha_device.zha_device_info, KEYS_TO_REDACT) From 3526252cfce4b6ca2728c73271020b82d0858447 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 19 May 2022 08:16:49 -0700 Subject: [PATCH 620/930] Bump gcal-sync to 0.8.1 (#72164) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index fde04d96baf..03d15597c44 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.8.0", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.8.1", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 05403b5d3aa..15bd902f9a0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.8.0 +gcal-sync==0.8.1 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d11047f4c95..a5ea5832e65 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -492,7 +492,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.8.0 +gcal-sync==0.8.1 # homeassistant.components.geocaching geocachingapi==0.2.1 From f06f94ea853701b210f1288cf2d04a11037d6728 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 May 2022 10:59:06 -0500 Subject: [PATCH 621/930] Add coverage to ensure we reject 0 length logbook filters (#72124) --- tests/components/logbook/test_init.py | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index a7420df9c5d..05df4f2ab3b 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -2442,6 +2442,34 @@ async def test_get_events_bad_end_time(hass, hass_ws_client, recorder_mock): assert response["error"]["code"] == "invalid_end_time" +async def test_get_events_invalid_filters(hass, hass_ws_client, recorder_mock): + """Test get_events invalid filters.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "entity_ids": [], + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_format" + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "device_ids": [], + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_format" + + async def test_get_events_with_device_ids(hass, hass_ws_client, recorder_mock): """Test logbook get_events for device ids.""" now = dt_util.utcnow() From 3ece5965a8846d645e7c1d436b1d2db17ea42cdf Mon Sep 17 00:00:00 2001 From: Marcio Granzotto Rodrigues Date: Thu, 19 May 2022 13:04:48 -0300 Subject: [PATCH 622/930] Update bond-api to 0.1.18 (#72166) --- homeassistant/components/bond/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bond/manifest.json b/homeassistant/components/bond/manifest.json index e5f8b004502..b011a5f2dae 100644 --- a/homeassistant/components/bond/manifest.json +++ b/homeassistant/components/bond/manifest.json @@ -3,7 +3,7 @@ "name": "Bond", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bond", - "requirements": ["bond-api==0.1.16"], + "requirements": ["bond-api==0.1.18"], "zeroconf": ["_bond._tcp.local."], "codeowners": ["@bdraco", "@prystupa", "@joshs85"], "quality_scale": "platinum", diff --git a/requirements_all.txt b/requirements_all.txt index 15bd902f9a0..13b3b172803 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -420,7 +420,7 @@ blockchain==1.4.4 # bluepy==1.3.0 # homeassistant.components.bond -bond-api==0.1.16 +bond-api==0.1.18 # homeassistant.components.bosch_shc boschshcpy==0.2.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a5ea5832e65..86f49132bc0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -321,7 +321,7 @@ blebox_uniapi==1.3.3 blinkpy==0.19.0 # homeassistant.components.bond -bond-api==0.1.16 +bond-api==0.1.18 # homeassistant.components.bosch_shc boschshcpy==0.2.30 From 17bb5034506d03f7a717d6ffd5b4eea8eca74376 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 18:23:23 +0200 Subject: [PATCH 623/930] Add new methods to DeviceAutomationActionProtocol (#72163) * Add composite type * Extend action protocol * Drop return/raise --- .../components/device_automation/__init__.py | 2 ++ .../components/device_automation/action.py | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/device_automation/__init__.py b/homeassistant/components/device_automation/__init__.py index d7b75d4fcd4..e24ae15f9ff 100644 --- a/homeassistant/components/device_automation/__init__.py +++ b/homeassistant/components/device_automation/__init__.py @@ -44,6 +44,8 @@ if TYPE_CHECKING: ] # mypy: allow-untyped-calls, allow-untyped-defs +GetAutomationsResult = list[dict[str, Any]] +GetAutomationCapabilitiesResult = dict[str, vol.Schema] DOMAIN = "device_automation" diff --git a/homeassistant/components/device_automation/action.py b/homeassistant/components/device_automation/action.py index 5261757c645..b15ca12a927 100644 --- a/homeassistant/components/device_automation/action.py +++ b/homeassistant/components/device_automation/action.py @@ -1,6 +1,7 @@ """Device action validator.""" from __future__ import annotations +from collections.abc import Awaitable from typing import Any, Protocol, cast import voluptuous as vol @@ -9,7 +10,12 @@ from homeassistant.const import CONF_DOMAIN from homeassistant.core import Context, HomeAssistant from homeassistant.helpers.typing import ConfigType -from . import DeviceAutomationType, async_get_device_automation_platform +from . import ( + DeviceAutomationType, + GetAutomationCapabilitiesResult, + GetAutomationsResult, + async_get_device_automation_platform, +) from .exceptions import InvalidDeviceAutomationConfig @@ -25,7 +31,6 @@ class DeviceAutomationActionProtocol(Protocol): self, hass: HomeAssistant, config: ConfigType ) -> ConfigType: """Validate config.""" - raise NotImplementedError async def async_call_action_from_config( self, @@ -35,7 +40,16 @@ class DeviceAutomationActionProtocol(Protocol): context: Context | None, ) -> None: """Execute a device action.""" - raise NotImplementedError + + def async_get_action_capabilities( + self, hass: HomeAssistant, config: ConfigType + ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + """List action capabilities.""" + + def async_get_actions( + self, hass: HomeAssistant, device_id: str + ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + """List actions.""" async def async_validate_action_config( From 8a00281eaab9e4aeb9632427b8152dc4ebe881b6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 18:23:40 +0200 Subject: [PATCH 624/930] Add new methods to DeviceAutomationTriggerProtocol (#72168) * Add composite type * Add new methods to DeviceAutomationTriggerProtocol --- .../components/device_automation/trigger.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_automation/trigger.py b/homeassistant/components/device_automation/trigger.py index 933c5c4c60a..e4a740d6599 100644 --- a/homeassistant/components/device_automation/trigger.py +++ b/homeassistant/components/device_automation/trigger.py @@ -1,4 +1,7 @@ """Offer device oriented automation.""" +from __future__ import annotations + +from collections.abc import Awaitable from typing import Protocol, cast import voluptuous as vol @@ -14,6 +17,8 @@ from homeassistant.helpers.typing import ConfigType from . import ( DEVICE_TRIGGER_BASE_SCHEMA, DeviceAutomationType, + GetAutomationCapabilitiesResult, + GetAutomationsResult, async_get_device_automation_platform, ) from .exceptions import InvalidDeviceAutomationConfig @@ -33,7 +38,6 @@ class DeviceAutomationTriggerProtocol(Protocol): self, hass: HomeAssistant, config: ConfigType ) -> ConfigType: """Validate config.""" - raise NotImplementedError async def async_attach_trigger( self, @@ -43,7 +47,16 @@ class DeviceAutomationTriggerProtocol(Protocol): automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Attach a trigger.""" - raise NotImplementedError + + def async_get_trigger_capabilities( + self, hass: HomeAssistant, config: ConfigType + ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + """List trigger capabilities.""" + + def async_get_triggers( + self, hass: HomeAssistant, device_id: str + ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + """List triggers.""" async def async_validate_trigger_config( From 8ef39c3cfd99235caf951c9db8d875b95e4297f9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 18:24:01 +0200 Subject: [PATCH 625/930] Add new methods to DeviceAutomationConditionProtocol (#72169) * Add composite type * Add new methods to DeviceAutomationConditionProtocol --- .../components/device_automation/condition.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/device_automation/condition.py b/homeassistant/components/device_automation/condition.py index 1c226ee8c29..7ff3216c702 100644 --- a/homeassistant/components/device_automation/condition.py +++ b/homeassistant/components/device_automation/condition.py @@ -1,6 +1,7 @@ """Validate device conditions.""" from __future__ import annotations +from collections.abc import Awaitable from typing import TYPE_CHECKING, Protocol, cast import voluptuous as vol @@ -10,7 +11,12 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType -from . import DeviceAutomationType, async_get_device_automation_platform +from . import ( + DeviceAutomationType, + GetAutomationCapabilitiesResult, + GetAutomationsResult, + async_get_device_automation_platform, +) from .exceptions import InvalidDeviceAutomationConfig if TYPE_CHECKING: @@ -29,13 +35,21 @@ class DeviceAutomationConditionProtocol(Protocol): self, hass: HomeAssistant, config: ConfigType ) -> ConfigType: """Validate config.""" - raise NotImplementedError def async_condition_from_config( self, hass: HomeAssistant, config: ConfigType ) -> condition.ConditionCheckerType: """Evaluate state based on configuration.""" - raise NotImplementedError + + def async_get_condition_capabilities( + self, hass: HomeAssistant, config: ConfigType + ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + """List condition capabilities.""" + + def async_get_conditions( + self, hass: HomeAssistant, device_id: str + ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + """List conditions.""" async def async_validate_condition_config( From 0422d7f2569c78c33be87dbb475764769b2f382c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 18:34:51 +0200 Subject: [PATCH 626/930] Add type hints to homekit_controller (#72155) --- .../components/homekit_controller/device_trigger.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index 3ba2abe3339..dcac7238c8e 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -1,6 +1,7 @@ """Provides device automations for homekit devices.""" from __future__ import annotations +from collections.abc import Generator from typing import TYPE_CHECKING, Any from aiohomekit.model.characteristics import CharacteristicsTypes @@ -63,7 +64,7 @@ class TriggerSource: self._hass = connection.hass self._connection = connection self._aid = aid - self._triggers = {} + self._triggers: dict[tuple[str, str], dict[str, Any]] = {} for trigger in triggers: self._triggers[(trigger["type"], trigger["subtype"])] = trigger self._callbacks = {} @@ -73,7 +74,7 @@ class TriggerSource: for event_handler in self._callbacks.get(iid, []): event_handler(value) - def async_get_triggers(self): + def async_get_triggers(self) -> Generator[tuple[str, str], None, None]: """List device triggers for homekit devices.""" yield from self._triggers @@ -246,7 +247,7 @@ async def async_get_triggers( if device_id not in hass.data.get(TRIGGERS, {}): return [] - device = hass.data[TRIGGERS][device_id] + device: TriggerSource = hass.data[TRIGGERS][device_id] return [ { From 1c4c0f1eb33302b983cb39f6800cd0fe6943fc4a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 May 2022 11:50:54 -0500 Subject: [PATCH 627/930] Small fixes and cleanups to legacy nexia code (#72176) --- homeassistant/components/nexia/climate.py | 32 ++++------ homeassistant/components/nexia/const.py | 9 +-- homeassistant/components/nexia/coordinator.py | 2 +- homeassistant/components/nexia/entity.py | 60 ++++++------------- homeassistant/components/nexia/scene.py | 33 ++++------ tests/components/nexia/test_binary_sensor.py | 4 +- tests/components/nexia/test_climate.py | 6 +- tests/components/nexia/test_scene.py | 6 +- tests/components/nexia/test_sensor.py | 18 +++--- 9 files changed, 60 insertions(+), 110 deletions(-) diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 0c27b1497f5..20fcf5c6b85 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -40,7 +40,6 @@ from .const import ( ATTR_DEHUMIDIFY_SETPOINT, ATTR_HUMIDIFY_SETPOINT, ATTR_RUN_MODE, - ATTR_ZONE_STATUS, DOMAIN, ) from .coordinator import NexiaDataUpdateCoordinator @@ -344,31 +343,26 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): self._signal_zone_update() @property - def is_aux_heat(self): + def is_aux_heat(self) -> bool: """Emergency heat state.""" return self._thermostat.is_emergency_heat_active() @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, str] | None: """Return the device specific state attributes.""" - data = super().extra_state_attributes - - data[ATTR_ZONE_STATUS] = self._zone.get_status() - if not self._has_relative_humidity: - return data + return None + attrs = {} if self._has_dehumidify_support: dehumdify_setpoint = percent_conv( self._thermostat.get_dehumidify_setpoint() ) - data[ATTR_DEHUMIDIFY_SETPOINT] = dehumdify_setpoint - + attrs[ATTR_DEHUMIDIFY_SETPOINT] = dehumdify_setpoint if self._has_humidify_support: humdify_setpoint = percent_conv(self._thermostat.get_humidify_setpoint()) - data[ATTR_HUMIDIFY_SETPOINT] = humdify_setpoint - - return data + attrs[ATTR_HUMIDIFY_SETPOINT] = humdify_setpoint + return attrs async def async_set_preset_mode(self, preset_mode: str): """Set the preset mode.""" @@ -376,23 +370,23 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): self._signal_zone_update() async def async_turn_aux_heat_off(self): - """Turn. Aux Heat off.""" + """Turn Aux Heat off.""" await self._thermostat.set_emergency_heat(False) self._signal_thermostat_update() async def async_turn_aux_heat_on(self): - """Turn. Aux Heat on.""" + """Turn Aux Heat on.""" self._thermostat.set_emergency_heat(True) self._signal_thermostat_update() async def async_turn_off(self): - """Turn. off the zone.""" - await self.set_hvac_mode(OPERATION_MODE_OFF) + """Turn off the zone.""" + await self.async_set_hvac_mode(OPERATION_MODE_OFF) self._signal_zone_update() async def async_turn_on(self): - """Turn. on the zone.""" - await self.set_hvac_mode(OPERATION_MODE_AUTO) + """Turn on the zone.""" + await self.async_set_hvac_mode(OPERATION_MODE_AUTO) self._signal_zone_update() async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: diff --git a/homeassistant/components/nexia/const.py b/homeassistant/components/nexia/const.py index 5d84e8a3075..493fdd8a403 100644 --- a/homeassistant/components/nexia/const.py +++ b/homeassistant/components/nexia/const.py @@ -9,17 +9,11 @@ PLATFORMS = [ Platform.SWITCH, ] -ATTRIBUTION = "Data provided by mynexia.com" - -NOTIFICATION_ID = "nexia_notification" -NOTIFICATION_TITLE = "Nexia Setup" +ATTRIBUTION = "Data provided by Trane Technologies" CONF_BRAND = "brand" -NEXIA_SCAN_INTERVAL = "scan_interval" - DOMAIN = "nexia" -DEFAULT_ENTITY_NAMESPACE = "nexia" ATTR_DESCRIPTION = "description" @@ -27,7 +21,6 @@ ATTR_AIRCLEANER_MODE = "aircleaner_mode" ATTR_RUN_MODE = "run_mode" -ATTR_ZONE_STATUS = "zone_status" ATTR_HUMIDIFY_SETPOINT = "humidify_setpoint" ATTR_DEHUMIDIFY_SETPOINT = "dehumidify_setpoint" diff --git a/homeassistant/components/nexia/coordinator.py b/homeassistant/components/nexia/coordinator.py index ba61a3591f0..d44e1827f5a 100644 --- a/homeassistant/components/nexia/coordinator.py +++ b/homeassistant/components/nexia/coordinator.py @@ -1,4 +1,4 @@ -"""Component to embed TP-Link smart home devices.""" +"""Component to embed nexia devices.""" from __future__ import annotations from datetime import timedelta diff --git a/homeassistant/components/nexia/entity.py b/homeassistant/components/nexia/entity.py index dde0e5ae8f3..4f806d03eda 100644 --- a/homeassistant/components/nexia/entity.py +++ b/homeassistant/components/nexia/entity.py @@ -1,8 +1,14 @@ """The nexia integration base entity.""" + from nexia.thermostat import NexiaThermostat from nexia.zone import NexiaThermostatZone -from homeassistant.const import ATTR_ATTRIBUTION +from homeassistant.const import ( + ATTR_IDENTIFIERS, + ATTR_NAME, + ATTR_SUGGESTED_AREA, + ATTR_VIA_DEVICE, +) from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -20,33 +26,18 @@ from .const import ( from .coordinator import NexiaDataUpdateCoordinator -class NexiaEntity(CoordinatorEntity): +class NexiaEntity(CoordinatorEntity[NexiaDataUpdateCoordinator]): """Base class for nexia entities.""" + _attr_attribution = ATTRIBUTION + def __init__( self, coordinator: NexiaDataUpdateCoordinator, name: str, unique_id: str ) -> None: """Initialize the entity.""" super().__init__(coordinator) - self._unique_id = unique_id - self._name = name - - @property - def unique_id(self): - """Return the unique id.""" - return self._unique_id - - @property - def name(self): - """Return the name.""" - return self._name - - @property - def extra_state_attributes(self): - """Return the device specific state attributes.""" - return { - ATTR_ATTRIBUTION: ATTRIBUTION, - } + self._attr_unique_id = unique_id + self._attr_name = name class NexiaThermostatEntity(NexiaEntity): @@ -56,12 +47,7 @@ class NexiaThermostatEntity(NexiaEntity): """Initialize the entity.""" super().__init__(coordinator, name, unique_id) self._thermostat: NexiaThermostat = thermostat - - @property - def device_info(self) -> DeviceInfo: - """Return the device_info of the device.""" - assert isinstance(self.coordinator, NexiaDataUpdateCoordinator) - return DeviceInfo( + self._attr_device_info = DeviceInfo( configuration_url=self.coordinator.nexia_home.root_url, identifiers={(DOMAIN, self._thermostat.thermostat_id)}, manufacturer=MANUFACTURER, @@ -102,21 +88,13 @@ class NexiaThermostatZoneEntity(NexiaThermostatEntity): """Initialize the entity.""" super().__init__(coordinator, zone.thermostat, name, unique_id) self._zone: NexiaThermostatZone = zone - - @property - def device_info(self): - """Return the device_info of the device.""" - data = super().device_info zone_name = self._zone.get_name() - data.update( - { - "identifiers": {(DOMAIN, self._zone.zone_id)}, - "name": zone_name, - "suggested_area": zone_name, - "via_device": (DOMAIN, self._zone.thermostat.thermostat_id), - } - ) - return data + self._attr_device_info |= { + ATTR_IDENTIFIERS: {(DOMAIN, self._zone.zone_id)}, + ATTR_NAME: zone_name, + ATTR_SUGGESTED_AREA: zone_name, + ATTR_VIA_DEVICE: (DOMAIN, self._zone.thermostat.thermostat_id), + } async def async_added_to_hass(self): """Listen for signals for services.""" diff --git a/homeassistant/components/nexia/scene.py b/homeassistant/components/nexia/scene.py index 28c892fe8e5..941785f8221 100644 --- a/homeassistant/components/nexia/scene.py +++ b/homeassistant/components/nexia/scene.py @@ -24,21 +24,19 @@ async def async_setup_entry( """Set up automations for a Nexia device.""" coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] nexia_home = coordinator.nexia_home - - entities = [] - - # Automation switches - for automation_id in nexia_home.get_automation_ids(): - automation = nexia_home.get_automation_by_id(automation_id) - - entities.append(NexiaAutomationScene(coordinator, automation)) - - async_add_entities(entities) + async_add_entities( + NexiaAutomationScene( + coordinator, nexia_home.get_automation_by_id(automation_id) + ) + for automation_id in nexia_home.get_automation_ids() + ) class NexiaAutomationScene(NexiaEntity, Scene): """Provides Nexia automation support.""" + _attr_icon = "mdi:script-text-outline" + def __init__( self, coordinator: NexiaDataUpdateCoordinator, automation: NexiaAutomation ) -> None: @@ -48,19 +46,8 @@ class NexiaAutomationScene(NexiaEntity, Scene): name=automation.name, unique_id=automation.automation_id, ) - self._automation = automation - - @property - def extra_state_attributes(self): - """Return the scene specific state attributes.""" - data = super().extra_state_attributes - data[ATTR_DESCRIPTION] = self._automation.description - return data - - @property - def icon(self): - """Return the icon of the automation scene.""" - return "mdi:script-text-outline" + self._automation: NexiaAutomation = automation + self._attr_extra_state_attributes = {ATTR_DESCRIPTION: automation.description} async def async_activate(self, **kwargs: Any) -> None: """Activate an automation scene.""" diff --git a/tests/components/nexia/test_binary_sensor.py b/tests/components/nexia/test_binary_sensor.py index 64b2946ee2f..966f49bbb15 100644 --- a/tests/components/nexia/test_binary_sensor.py +++ b/tests/components/nexia/test_binary_sensor.py @@ -13,7 +13,7 @@ async def test_create_binary_sensors(hass): state = hass.states.get("binary_sensor.master_suite_blower_active") assert state.state == STATE_ON expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Master Suite Blower Active", } # Only test for a subset of attributes in case @@ -25,7 +25,7 @@ async def test_create_binary_sensors(hass): state = hass.states.get("binary_sensor.downstairs_east_wing_blower_active") assert state.state == STATE_OFF expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Downstairs East Wing Blower Active", } # Only test for a subset of attributes in case diff --git a/tests/components/nexia/test_climate.py b/tests/components/nexia/test_climate.py index 312d78ac256..797fbb4014c 100644 --- a/tests/components/nexia/test_climate.py +++ b/tests/components/nexia/test_climate.py @@ -12,7 +12,7 @@ async def test_climate_zones(hass): state = hass.states.get("climate.nick_office") assert state.state == HVACMode.HEAT_COOL expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "current_humidity": 52.0, "current_temperature": 22.8, "dehumidify_setpoint": 45.0, @@ -33,7 +33,6 @@ async def test_climate_zones(hass): "target_temp_low": 17.2, "target_temp_step": 1.0, "temperature": None, - "zone_status": "Relieving Air", } # Only test for a subset of attributes in case # HA changes the implementation and a new one appears @@ -45,7 +44,7 @@ async def test_climate_zones(hass): assert state.state == HVACMode.HEAT_COOL expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "current_humidity": 36.0, "current_temperature": 25.0, "dehumidify_setpoint": 50.0, @@ -66,7 +65,6 @@ async def test_climate_zones(hass): "target_temp_low": 17.2, "target_temp_step": 1.0, "temperature": None, - "zone_status": "Idle", } # Only test for a subset of attributes in case diff --git a/tests/components/nexia/test_scene.py b/tests/components/nexia/test_scene.py index 4a325552e80..309af6b523d 100644 --- a/tests/components/nexia/test_scene.py +++ b/tests/components/nexia/test_scene.py @@ -10,7 +10,7 @@ async def test_automation_scenes(hass): state = hass.states.get("scene.away_short") expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "description": "When IFTTT activates the automation Upstairs " "West Wing will permanently hold the heat to 63.0 " "and cool to 80.0 AND Downstairs East Wing will " @@ -37,7 +37,7 @@ async def test_automation_scenes(hass): state = hass.states.get("scene.power_outage") expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "description": "When IFTTT activates the automation Upstairs " "West Wing will permanently hold the heat to 55.0 " "and cool to 90.0 AND Downstairs East Wing will " @@ -56,7 +56,7 @@ async def test_automation_scenes(hass): state = hass.states.get("scene.power_restored") expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "description": "When IFTTT activates the automation Upstairs " "West Wing will Run Schedule AND Downstairs East " "Wing will Run Schedule AND Downstairs West Wing " diff --git a/tests/components/nexia/test_sensor.py b/tests/components/nexia/test_sensor.py index 289947fbdf6..ad15fc308f4 100644 --- a/tests/components/nexia/test_sensor.py +++ b/tests/components/nexia/test_sensor.py @@ -14,7 +14,7 @@ async def test_create_sensors(hass): assert state.state == "23" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "device_class": "temperature", "friendly_name": "Nick Office Temperature", "unit_of_measurement": TEMP_CELSIUS, @@ -28,7 +28,7 @@ async def test_create_sensors(hass): state = hass.states.get("sensor.nick_office_zone_setpoint_status") assert state.state == "Permanent Hold" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Nick Office Zone Setpoint Status", } # Only test for a subset of attributes in case @@ -41,7 +41,7 @@ async def test_create_sensors(hass): assert state.state == "Relieving Air" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Nick Office Zone Status", } # Only test for a subset of attributes in case @@ -54,7 +54,7 @@ async def test_create_sensors(hass): assert state.state == "auto" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Master Suite Air Cleaner Mode", } # Only test for a subset of attributes in case @@ -67,7 +67,7 @@ async def test_create_sensors(hass): assert state.state == "69.0" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Master Suite Current Compressor Speed", "unit_of_measurement": PERCENTAGE, } @@ -81,7 +81,7 @@ async def test_create_sensors(hass): assert state.state == "30.6" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "device_class": "temperature", "friendly_name": "Master Suite Outdoor Temperature", "unit_of_measurement": TEMP_CELSIUS, @@ -96,7 +96,7 @@ async def test_create_sensors(hass): assert state.state == "52.0" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "device_class": "humidity", "friendly_name": "Master Suite Relative Humidity", "unit_of_measurement": PERCENTAGE, @@ -111,7 +111,7 @@ async def test_create_sensors(hass): assert state.state == "69.0" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Master Suite Requested Compressor Speed", "unit_of_measurement": PERCENTAGE, } @@ -125,7 +125,7 @@ async def test_create_sensors(hass): assert state.state == "Cooling" expected_attributes = { - "attribution": "Data provided by mynexia.com", + "attribution": "Data provided by Trane Technologies", "friendly_name": "Master Suite System Status", } # Only test for a subset of attributes in case From 487819bbe5cf00547c1c2157c64b8b438ef0224a Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 19 May 2022 13:31:24 -0400 Subject: [PATCH 628/930] Use device ID as input for zwave_js WS device cmds (#71667) * Use device ID as input for zwave_js WS device cmds * Additionally missed commands, update network_status command to include node status data * revert change to removed function * Revert register device change --- homeassistant/components/zwave_js/api.py | 148 +++----- homeassistant/components/zwave_js/helpers.py | 33 +- tests/components/zwave_js/test_api.py | 370 ++++++------------- 3 files changed, 179 insertions(+), 372 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 7d1c4622d50..d83b7261b5f 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -68,7 +68,11 @@ from .const import ( EVENT_DEVICE_ADDED_TO_REGISTRY, LOGGER, ) -from .helpers import async_enable_statistics, update_data_collection_preference +from .helpers import ( + async_enable_statistics, + async_get_node_from_device_id, + update_data_collection_preference, +) from .migrate import ( ZWaveMigrationData, async_get_migration_data, @@ -83,6 +87,7 @@ ID = "id" ENTRY_ID = "entry_id" ERR_NOT_LOADED = "not_loaded" NODE_ID = "node_id" +DEVICE_ID = "device_id" COMMAND_CLASS_ID = "command_class_id" TYPE = "type" PROPERTY = "property" @@ -254,21 +259,20 @@ def async_get_entry(orig_func: Callable) -> Callable: def async_get_node(orig_func: Callable) -> Callable: """Decorate async function to get node.""" - @async_get_entry @wraps(orig_func) async def async_get_node_func( - hass: HomeAssistant, - connection: ActiveConnection, - msg: dict, - entry: ConfigEntry, - client: Client, + hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Provide user specific data and store to function.""" - node_id = msg[NODE_ID] - node = client.driver.controller.nodes.get(node_id) + device_id = msg[DEVICE_ID] - if node is None: - connection.send_error(msg[ID], ERR_NOT_FOUND, f"Node {node_id} not found") + try: + node = async_get_node_from_device_id(hass, device_id) + except ValueError as err: + error_code = ERR_NOT_FOUND + if "loaded" in err.args[0]: + error_code = ERR_NOT_LOADED + connection.send_error(msg[ID], error_code, err.args[0]) return await orig_func(hass, connection, msg, node) @@ -299,13 +303,26 @@ def async_handle_failed_command(orig_func: Callable) -> Callable: return async_handle_failed_command_func +def node_status(node: Node) -> dict[str, Any]: + """Get node status.""" + return { + "node_id": node.node_id, + "is_routing": node.is_routing, + "status": node.status, + "is_secure": node.is_secure, + "ready": node.ready, + "zwave_plus_version": node.zwave_plus_version, + "highest_security_class": node.highest_security_class, + "is_controller_node": node.is_controller_node, + } + + @callback def async_register_api(hass: HomeAssistant) -> None: """Register all of our api endpoints.""" websocket_api.async_register_command(hass, websocket_network_status) websocket_api.async_register_command(hass, websocket_node_status) websocket_api.async_register_command(hass, websocket_node_metadata) - websocket_api.async_register_command(hass, websocket_ping_node) websocket_api.async_register_command(hass, websocket_add_node) websocket_api.async_register_command(hass, websocket_grant_security_classes) websocket_api.async_register_command(hass, websocket_validate_dsk_and_enter_pin) @@ -395,7 +412,9 @@ async def websocket_network_status( "supports_timers": controller.supports_timers, "is_heal_network_active": controller.is_heal_network_active, "inclusion_state": controller.inclusion_state, - "nodes": list(client.driver.controller.nodes), + "nodes": [ + node_status(node) for node in client.driver.controller.nodes.values() + ], }, } connection.send_result( @@ -407,8 +426,7 @@ async def websocket_network_status( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/node_ready", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -443,8 +461,7 @@ async def websocket_node_ready( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/node_status", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -456,27 +473,13 @@ async def websocket_node_status( node: Node, ) -> None: """Get the status of a Z-Wave JS node.""" - data = { - "node_id": node.node_id, - "is_routing": node.is_routing, - "status": node.status, - "is_secure": node.is_secure, - "ready": node.ready, - "zwave_plus_version": node.zwave_plus_version, - "highest_security_class": node.highest_security_class, - "is_controller_node": node.is_controller_node, - } - connection.send_result( - msg[ID], - data, - ) + connection.send_result(msg[ID], node_status(node)) @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/node_metadata", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -504,30 +507,6 @@ async def websocket_node_metadata( ) -@websocket_api.websocket_command( - { - vol.Required(TYPE): "zwave_js/ping_node", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, - } -) -@websocket_api.async_response -@async_handle_failed_command -@async_get_node -async def websocket_ping_node( - hass: HomeAssistant, - connection: ActiveConnection, - msg: dict, - node: Node, -) -> None: - """Ping a Z-Wave JS node.""" - result = await node.async_ping() - connection.send_result( - msg[ID], - result, - ) - - @websocket_api.require_admin @websocket_api.websocket_command( { @@ -1189,23 +1168,20 @@ async def websocket_replace_failed_node( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/remove_failed_node", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @async_handle_failed_command -@async_get_entry +@async_get_node async def websocket_remove_failed_node( hass: HomeAssistant, connection: ActiveConnection, msg: dict, - entry: ConfigEntry, - client: Client, + node: Node, ) -> None: """Remove a failed node from the Z-Wave network.""" - controller = client.driver.controller - node_id = msg[NODE_ID] + controller = node.client.driver.controller @callback def async_cleanup() -> None: @@ -1215,10 +1191,7 @@ async def websocket_remove_failed_node( @callback def node_removed(event: dict) -> None: - node = event["node"] - node_details = { - "node_id": node.node_id, - } + node_details = {"node_id": event["node"].node_id} connection.send_message( websocket_api.event_message( @@ -1229,7 +1202,7 @@ async def websocket_remove_failed_node( connection.subscriptions[msg["id"]] = async_cleanup msg[DATA_UNSUBSCRIBE] = unsubs = [controller.on("node removed", node_removed)] - result = await controller.async_remove_failed_node(node_id) + result = await controller.async_remove_failed_node(node.node_id) connection.send_result( msg[ID], result, @@ -1335,24 +1308,21 @@ async def websocket_stop_healing_network( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/heal_node", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @async_handle_failed_command -@async_get_entry +@async_get_node async def websocket_heal_node( hass: HomeAssistant, connection: ActiveConnection, msg: dict, - entry: ConfigEntry, - client: Client, + node: Node, ) -> None: """Heal a node on the Z-Wave network.""" - controller = client.driver.controller - node_id = msg[NODE_ID] - result = await controller.async_heal_node(node_id) + controller = node.client.driver.controller + result = await controller.async_heal_node(node.node_id) connection.send_result( msg[ID], result, @@ -1363,8 +1333,7 @@ async def websocket_heal_node( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/refresh_node_info", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, }, ) @websocket_api.async_response @@ -1414,8 +1383,7 @@ async def websocket_refresh_node_info( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/refresh_node_values", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, }, ) @websocket_api.async_response @@ -1436,8 +1404,7 @@ async def websocket_refresh_node_values( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/refresh_node_cc_values", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, vol.Required(COMMAND_CLASS_ID): int, }, ) @@ -1469,8 +1436,7 @@ async def websocket_refresh_node_cc_values( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/set_config_parameter", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, vol.Required(PROPERTY): int, vol.Optional(PROPERTY_KEY): int, vol.Required(VALUE): vol.Any(int, BITMASK_SCHEMA), @@ -1521,8 +1487,7 @@ async def websocket_set_config_parameter( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/get_config_parameters", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -1762,8 +1727,7 @@ async def websocket_data_collection_status( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/abort_firmware_update", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -1794,8 +1758,7 @@ def _get_firmware_update_progress_dict( @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/subscribe_firmware_update_status", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response @@ -2047,8 +2010,7 @@ def _get_node_statistics_dict(statistics: NodeStatistics) -> dict[str, int]: @websocket_api.websocket_command( { vol.Required(TYPE): "zwave_js/subscribe_node_statistics", - vol.Required(ENTRY_ID): str, - vol.Required(NODE_ID): int, + vol.Required(DEVICE_ID): str, } ) @websocket_api.async_response diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index c4eb0396287..68ff0c89b15 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -179,24 +179,22 @@ def async_get_node_from_device_id( # Use device config entry ID's to validate that this is a valid zwave_js device # and to get the client config_entry_ids = device_entry.config_entries - config_entry_id = next( + entry = next( ( - config_entry_id - for config_entry_id in config_entry_ids - if cast( - ConfigEntry, - hass.config_entries.async_get_entry(config_entry_id), - ).domain - == DOMAIN + entry + for entry in hass.config_entries.async_entries(DOMAIN) + if entry.entry_id in config_entry_ids ), None, ) - if config_entry_id is None or config_entry_id not in hass.data[DOMAIN]: + if entry and entry.state != ConfigEntryState.LOADED: + raise ValueError(f"Device {device_id} config entry is not loaded") + if entry is None or entry.entry_id not in hass.data[DOMAIN]: raise ValueError( f"Device {device_id} is not from an existing zwave_js config entry" ) - client = hass.data[DOMAIN][config_entry_id][DATA_CLIENT] + client = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] # Get node ID from device identifier, perform some validation, and then get the # node @@ -390,21 +388,6 @@ def copy_available_params( ) -@callback -def async_is_device_config_entry_not_loaded( - hass: HomeAssistant, device_id: str -) -> bool: - """Return whether device's config entries are not loaded.""" - dev_reg = dr.async_get(hass) - if (device := dev_reg.async_get(device_id)) is None: - raise ValueError(f"Device {device_id} not found") - return any( - (entry := hass.config_entries.async_get_entry(entry_id)) - and entry.state != ConfigEntryState.LOADED - for entry_id in device.config_entries - ) - - def get_value_state_schema( value: ZwaveValue, ) -> vol.Schema | None: diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index d8a36fc6509..60b83630add 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -35,6 +35,7 @@ from homeassistant.components.zwave_js.api import ( CLIENT_SIDE_AUTH, COMMAND_CLASS_ID, CONFIG, + DEVICE_ID, DSK, ENABLED, ENTRY_ID, @@ -70,9 +71,17 @@ from homeassistant.components.zwave_js.const import ( CONF_DATA_COLLECTION_OPTED_IN, DOMAIN, ) +from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.helpers import device_registry as dr +def get_device(hass, node): + """Get device ID for a node.""" + dev_reg = dr.async_get(hass) + device_id = get_device_id(node.client, node) + return dev_reg.async_get_device({device_id}) + + async def test_network_status(hass, integration, hass_ws_client): """Test the network status websocket command.""" entry = integration @@ -117,12 +126,16 @@ async def test_node_ready( node.data["ready"] = False client.driver.controller.nodes[node.node_id] = node + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_or_create( + config_entry_id=entry.entry_id, identifiers={get_device_id(client, node)} + ) + await ws_client.send_json( { ID: 3, TYPE: "zwave_js/node_ready", - ENTRY_ID: entry.entry_id, - "node_id": node.node_id, + DEVICE_ID: device.id, } ) @@ -153,12 +166,12 @@ async def test_node_status(hass, multisensor_6, integration, hass_ws_client): ws_client = await hass_ws_client(hass) node = multisensor_6 + device = get_device(hass, node) await ws_client.send_json( { ID: 3, TYPE: "zwave_js/node_status", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -178,8 +191,7 @@ async def test_node_status(hass, multisensor_6, integration, hass_ws_client): { ID: 4, TYPE: "zwave_js/node_status", - ENTRY_ID: entry.entry_id, - NODE_ID: 99999, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -194,8 +206,7 @@ async def test_node_status(hass, multisensor_6, integration, hass_ws_client): { ID: 5, TYPE: "zwave_js/node_status", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -210,12 +221,12 @@ async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_ ws_client = await hass_ws_client(hass) node = wallmote_central_scene + device = get_device(hass, node) await ws_client.send_json( { ID: 3, TYPE: "zwave_js/node_metadata", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -256,8 +267,7 @@ async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_ { ID: 4, TYPE: "zwave_js/node_metadata", - ENTRY_ID: entry.entry_id, - NODE_ID: 99999, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -272,81 +282,7 @@ async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_ { ID: 5, TYPE: "zwave_js/node_metadata", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, - } - ) - msg = await ws_client.receive_json() - - assert not msg["success"] - assert msg["error"]["code"] == ERR_NOT_LOADED - - -async def test_ping_node( - hass, wallmote_central_scene, integration, client, hass_ws_client -): - """Test the ping_node websocket command.""" - entry = integration - ws_client = await hass_ws_client(hass) - node = wallmote_central_scene - - client.async_send_command.return_value = {"responded": True} - - await ws_client.send_json( - { - ID: 3, - TYPE: "zwave_js/ping_node", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, - } - ) - - msg = await ws_client.receive_json() - assert msg["success"] - assert msg["result"] - - # Test getting non-existent node fails - await ws_client.send_json( - { - ID: 4, - TYPE: "zwave_js/ping_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 99999, - } - ) - msg = await ws_client.receive_json() - assert not msg["success"] - assert msg["error"]["code"] == ERR_NOT_FOUND - - # Test FailedZWaveCommand is caught - with patch( - "zwave_js_server.model.node.Node.async_ping", - side_effect=FailedZWaveCommand("failed_command", 1, "error message"), - ): - await ws_client.send_json( - { - ID: 5, - TYPE: "zwave_js/ping_node", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, - } - ) - msg = await ws_client.receive_json() - - assert not msg["success"] - assert msg["error"]["code"] == "zwave_error" - assert msg["error"]["message"] == "Z-Wave error 1: error message" - - # Test sending command with not loaded entry fails - await hass.config_entries.async_unload(entry.entry_id) - await hass.async_block_till_done() - - await ws_client.send_json( - { - ID: 6, - TYPE: "zwave_js/ping_node", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -1812,19 +1748,38 @@ async def test_remove_failed_node( client, hass_ws_client, nortek_thermostat_removed_event, + nortek_thermostat_added_event, ): """Test the remove_failed_node websocket command.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, nortek_thermostat) client.async_send_command.return_value = {"success": True} + # Test FailedZWaveCommand is caught + with patch( + "zwave_js_server.model.controller.Controller.async_remove_failed_node", + side_effect=FailedZWaveCommand("failed_command", 1, "error message"), + ): + await ws_client.send_json( + { + ID: 1, + TYPE: "zwave_js/remove_failed_node", + DEVICE_ID: device.id, + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == "zwave_error" + assert msg["error"]["message"] == "Z-Wave error 1: error message" + await ws_client.send_json( { - ID: 3, + ID: 2, TYPE: "zwave_js/remove_failed_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, + DEVICE_ID: device.id, } ) @@ -1846,29 +1801,15 @@ async def test_remove_failed_node( assert msg["event"]["event"] == "node removed" # Verify device was removed from device registry - device = dev_reg.async_get_device( - identifiers={(DOMAIN, "3245146787-67")}, - ) - assert device is None - - # Test FailedZWaveCommand is caught - with patch( - "zwave_js_server.model.controller.Controller.async_remove_failed_node", - side_effect=FailedZWaveCommand("failed_command", 1, "error message"), - ): - await ws_client.send_json( - { - ID: 4, - TYPE: "zwave_js/remove_failed_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, - } + assert ( + dev_reg.async_get_device( + identifiers={(DOMAIN, "3245146787-67")}, ) - msg = await ws_client.receive_json() + is None + ) - assert not msg["success"] - assert msg["error"]["code"] == "zwave_error" - assert msg["error"]["message"] == "Z-Wave error 1: error message" + # Re-add node so we can test config entry not loaded + client.driver.receive_event(nortek_thermostat_added_event) # Test sending command with not loaded entry fails await hass.config_entries.async_unload(entry.entry_id) @@ -1876,10 +1817,9 @@ async def test_remove_failed_node( await ws_client.send_json( { - ID: 5, + ID: 3, TYPE: "zwave_js/remove_failed_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2091,6 +2031,7 @@ async def test_stop_healing_network( async def test_heal_node( hass, + multisensor_6, integration, client, hass_ws_client, @@ -2098,6 +2039,7 @@ async def test_heal_node( """Test the heal_node websocket command.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command.return_value = {"success": True} @@ -2105,8 +2047,7 @@ async def test_heal_node( { ID: 3, TYPE: "zwave_js/heal_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, + DEVICE_ID: device.id, } ) @@ -2123,8 +2064,7 @@ async def test_heal_node( { ID: 4, TYPE: "zwave_js/heal_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2141,8 +2081,7 @@ async def test_heal_node( { ID: 5, TYPE: "zwave_js/heal_node", - ENTRY_ID: entry.entry_id, - NODE_ID: 67, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2158,13 +2097,14 @@ async def test_refresh_node_info( entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) + client.async_send_command_no_wait.return_value = None await ws_client.send_json( { ID: 1, TYPE: "zwave_js/refresh_node_info", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2232,8 +2172,7 @@ async def test_refresh_node_info( { ID: 2, TYPE: "zwave_js/refresh_node_info", - ENTRY_ID: entry.entry_id, - NODE_ID: 9999, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -2249,8 +2188,7 @@ async def test_refresh_node_info( { ID: 3, TYPE: "zwave_js/refresh_node_info", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2267,8 +2205,7 @@ async def test_refresh_node_info( { ID: 4, TYPE: "zwave_js/refresh_node_info", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2283,14 +2220,14 @@ async def test_refresh_node_values( """Test that the refresh_node_values WS API call works.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command_no_wait.return_value = None await ws_client.send_json( { ID: 1, TYPE: "zwave_js/refresh_node_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2303,26 +2240,12 @@ async def test_refresh_node_values( client.async_send_command_no_wait.reset_mock() - # Test getting non-existent node fails + # Test getting non-existent device fails await ws_client.send_json( { ID: 2, TYPE: "zwave_js/refresh_node_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 99999, - } - ) - msg = await ws_client.receive_json() - assert not msg["success"] - assert msg["error"]["code"] == ERR_NOT_FOUND - - # Test getting non-existent entry fails - await ws_client.send_json( - { - ID: 3, - TYPE: "zwave_js/refresh_node_values", - ENTRY_ID: "fake_entry_id", - NODE_ID: 52, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -2338,8 +2261,7 @@ async def test_refresh_node_values( { ID: 4, TYPE: "zwave_js/refresh_node_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2356,8 +2278,7 @@ async def test_refresh_node_values( { ID: 5, TYPE: "zwave_js/refresh_node_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2367,19 +2288,19 @@ async def test_refresh_node_values( async def test_refresh_node_cc_values( - hass, client, multisensor_6, integration, hass_ws_client + hass, multisensor_6, client, integration, hass_ws_client ): """Test that the refresh_node_cc_values WS API call works.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command_no_wait.return_value = None await ws_client.send_json( { ID: 1, TYPE: "zwave_js/refresh_node_cc_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, COMMAND_CLASS_ID: 112, } ) @@ -2399,8 +2320,7 @@ async def test_refresh_node_cc_values( { ID: 2, TYPE: "zwave_js/refresh_node_cc_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, COMMAND_CLASS_ID: 9999, } ) @@ -2408,13 +2328,12 @@ async def test_refresh_node_cc_values( assert not msg["success"] assert msg["error"]["code"] == ERR_NOT_FOUND - # Test getting non-existent node fails + # Test getting non-existent device fails await ws_client.send_json( { ID: 3, TYPE: "zwave_js/refresh_node_cc_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 9999, + DEVICE_ID: "fake_device", COMMAND_CLASS_ID: 112, } ) @@ -2431,8 +2350,7 @@ async def test_refresh_node_cc_values( { ID: 4, TYPE: "zwave_js/refresh_node_cc_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, COMMAND_CLASS_ID: 112, } ) @@ -2450,8 +2368,7 @@ async def test_refresh_node_cc_values( { ID: 5, TYPE: "zwave_js/refresh_node_cc_values", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, COMMAND_CLASS_ID: 112, } ) @@ -2462,11 +2379,12 @@ async def test_refresh_node_cc_values( async def test_set_config_parameter( - hass, client, hass_ws_client, multisensor_6, integration + hass, multisensor_6, client, hass_ws_client, integration ): """Test the set_config_parameter service.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command_no_wait.return_value = None @@ -2474,8 +2392,7 @@ async def test_set_config_parameter( { ID: 1, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2523,8 +2440,7 @@ async def test_set_config_parameter( { ID: 2, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: "0x1", @@ -2573,8 +2489,7 @@ async def test_set_config_parameter( { ID: 3, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2593,8 +2508,7 @@ async def test_set_config_parameter( { ID: 4, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2613,8 +2527,7 @@ async def test_set_config_parameter( { ID: 5, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2633,8 +2546,7 @@ async def test_set_config_parameter( { ID: 6, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 9999, + DEVICE_ID: "fake_device", PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2653,8 +2565,7 @@ async def test_set_config_parameter( { ID: 7, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2674,8 +2585,7 @@ async def test_set_config_parameter( { ID: 8, TYPE: "zwave_js/set_config_parameter", - ENTRY_ID: entry.entry_id, - NODE_ID: 52, + DEVICE_ID: device.id, PROPERTY: 102, PROPERTY_KEY: 1, VALUE: 1, @@ -2693,14 +2603,14 @@ async def test_get_config_parameters(hass, multisensor_6, integration, hass_ws_c entry = integration ws_client = await hass_ws_client(hass) node = multisensor_6 + device = get_device(hass, node) # Test getting configuration parameter values await ws_client.send_json( { ID: 4, TYPE: "zwave_js/get_config_parameters", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -2722,8 +2632,7 @@ async def test_get_config_parameters(hass, multisensor_6, integration, hass_ws_c { ID: 5, TYPE: "zwave_js/get_config_parameters", - ENTRY_ID: entry.entry_id, - NODE_ID: 99999, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -2738,8 +2647,7 @@ async def test_get_config_parameters(hass, multisensor_6, integration, hass_ws_c { ID: 6, TYPE: "zwave_js/get_config_parameters", - ENTRY_ID: entry.entry_id, - NODE_ID: node.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3276,14 +3184,14 @@ async def test_abort_firmware_update( """Test that the abort_firmware_update WS API call works.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command_no_wait.return_value = {} await ws_client.send_json( { ID: 1, TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3303,8 +3211,7 @@ async def test_abort_firmware_update( { ID: 2, TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3321,8 +3228,7 @@ async def test_abort_firmware_update( { ID: 3, TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3332,32 +3238,19 @@ async def test_abort_firmware_update( async def test_abort_firmware_update_failures( - hass, integration, multisensor_6, client, hass_ws_client + hass, multisensor_6, client, integration, hass_ws_client ): """Test failures for the abort_firmware_update websocket command.""" entry = integration ws_client = await hass_ws_client(hass) - # Test sending command with improper entry ID fails - await ws_client.send_json( - { - ID: 1, - TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: "fake_entry_id", - NODE_ID: multisensor_6.node_id, - } - ) - msg = await ws_client.receive_json() + device = get_device(hass, multisensor_6) - assert not msg["success"] - assert msg["error"]["code"] == ERR_NOT_FOUND - - # Test sending command with improper node ID fails + # Test sending command with improper device ID fails await ws_client.send_json( { ID: 2, TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id + 100, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -3373,8 +3266,7 @@ async def test_abort_firmware_update_failures( { ID: 3, TYPE: "zwave_js/abort_firmware_update", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3384,11 +3276,11 @@ async def test_abort_firmware_update_failures( async def test_subscribe_firmware_update_status( - hass, integration, multisensor_6, client, hass_ws_client + hass, multisensor_6, integration, client, hass_ws_client ): """Test the subscribe_firmware_update_status websocket command.""" - entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) client.async_send_command_no_wait.return_value = {} @@ -3396,8 +3288,7 @@ async def test_subscribe_firmware_update_status( { ID: 1, TYPE: "zwave_js/subscribe_firmware_update_status", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) @@ -3445,11 +3336,11 @@ async def test_subscribe_firmware_update_status( async def test_subscribe_firmware_update_status_initial_value( - hass, integration, multisensor_6, client, hass_ws_client + hass, multisensor_6, client, integration, hass_ws_client ): """Test subscribe_firmware_update_status websocket command with in progress update.""" - entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) assert multisensor_6.firmware_update_progress is None @@ -3472,8 +3363,7 @@ async def test_subscribe_firmware_update_status_initial_value( { ID: 1, TYPE: "zwave_js/subscribe_firmware_update_status", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) @@ -3490,32 +3380,18 @@ async def test_subscribe_firmware_update_status_initial_value( async def test_subscribe_firmware_update_status_failures( - hass, integration, multisensor_6, client, hass_ws_client + hass, multisensor_6, client, integration, hass_ws_client ): """Test failures for the subscribe_firmware_update_status websocket command.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) # Test sending command with improper entry ID fails await ws_client.send_json( { ID: 1, TYPE: "zwave_js/subscribe_firmware_update_status", - ENTRY_ID: "fake_entry_id", - NODE_ID: multisensor_6.node_id, - } - ) - msg = await ws_client.receive_json() - - assert not msg["success"] - assert msg["error"]["code"] == ERR_NOT_FOUND - - # Test sending command with improper node ID fails - await ws_client.send_json( - { - ID: 2, - TYPE: "zwave_js/subscribe_firmware_update_status", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id + 100, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -3531,8 +3407,7 @@ async def test_subscribe_firmware_update_status_failures( { ID: 3, TYPE: "zwave_js/subscribe_firmware_update_status", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() @@ -3783,13 +3658,13 @@ async def test_subscribe_node_statistics( """Test the subscribe_node_statistics command.""" entry = integration ws_client = await hass_ws_client(hass) + device = get_device(hass, multisensor_6) await ws_client.send_json( { ID: 1, TYPE: "zwave_js/subscribe_node_statistics", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) @@ -3843,8 +3718,7 @@ async def test_subscribe_node_statistics( { ID: 2, TYPE: "zwave_js/subscribe_node_statistics", - ENTRY_ID: "fake_entry_id", - NODE_ID: multisensor_6.node_id, + DEVICE_ID: "fake_device", } ) msg = await ws_client.receive_json() @@ -3852,17 +3726,6 @@ async def test_subscribe_node_statistics( assert not msg["success"] assert msg["error"]["code"] == ERR_NOT_FOUND - # Test sending command with improper node ID fails - await ws_client.send_json( - { - ID: 3, - TYPE: "zwave_js/subscribe_node_statistics", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id + 100, - } - ) - msg = await ws_client.receive_json() - # Test sending command with not loaded entry fails await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() @@ -3871,8 +3734,7 @@ async def test_subscribe_node_statistics( { ID: 4, TYPE: "zwave_js/subscribe_node_statistics", - ENTRY_ID: entry.entry_id, - NODE_ID: multisensor_6.node_id, + DEVICE_ID: device.id, } ) msg = await ws_client.receive_json() From a57697d6e98a5a267f8eb60cb333b6a8dcc360c3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 21:16:06 +0200 Subject: [PATCH 629/930] Adjust device_automation type hints in deconz (#72194) --- homeassistant/components/deconz/device_trigger.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 43ed3b2cdc4..2ab1b2e6544 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -1,16 +1,16 @@ """Provides device automations for deconz events.""" - from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -642,9 +642,8 @@ def _get_deconz_event_from_device( async def async_validate_trigger_config( - hass: HomeAssistant, - config: dict[str, Any], -) -> vol.Schema: + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) @@ -703,7 +702,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str, -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers. Make sure device is a supported remote model. From 700552689270020eb56bf43de937554ba5755929 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 21:33:01 +0200 Subject: [PATCH 630/930] Adjust device_automation type hints in webostv (#72200) --- homeassistant/components/webostv/device_trigger.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/webostv/device_trigger.py b/homeassistant/components/webostv/device_trigger.py index feb5bae98fe..9836f561b9d 100644 --- a/homeassistant/components/webostv/device_trigger.py +++ b/homeassistant/components/webostv/device_trigger.py @@ -7,7 +7,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -58,7 +61,7 @@ async def async_validate_trigger_config( async def async_get_triggers( _hass: HomeAssistant, device_id: str -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers for device.""" triggers = [] base_trigger = { @@ -77,7 +80,7 @@ async def async_attach_trigger( config: ConfigType, action: AutomationActionType, automation_info: AutomationTriggerInfo, -) -> CALLBACK_TYPE | None: +) -> CALLBACK_TYPE: """Attach a trigger.""" if (trigger_type := config[CONF_TYPE]) == TURN_ON_PLATFORM_TYPE: trigger_config = { From 7ce3e9ad7b3834f8c054058d9c42f1e51b687b1f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 21:33:50 +0200 Subject: [PATCH 631/930] Adjust device_automation type hints in shelly (#72196) --- homeassistant/components/shelly/device_trigger.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index ae2eb7d6440..1231f670d49 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -1,7 +1,7 @@ """Provides device triggers for Shelly.""" from __future__ import annotations -from typing import Any, Final +from typing import Final import voluptuous as vol @@ -9,7 +9,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -72,8 +75,8 @@ def append_input_triggers( async def async_validate_trigger_config( - hass: HomeAssistant, config: dict[str, Any] -) -> dict[str, Any]: + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) @@ -108,9 +111,9 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers for Shelly devices.""" - triggers: list[dict[str, str]] = [] + triggers: GetAutomationsResult = [] if rpc_wrapper := get_rpc_device_wrapper(hass, device_id): input_triggers = get_rpc_input_triggers(rpc_wrapper.device) From 3604bb4c667e547b962ad50b70d3a4063c63dc47 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 22:21:43 +0200 Subject: [PATCH 632/930] Adjust device_automation type hints in lutron_caseta (#72133) --- .../components/lutron_caseta/device_trigger.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/lutron_caseta/device_trigger.py b/homeassistant/components/lutron_caseta/device_trigger.py index 4974a10d9ba..b6f01a5e49e 100644 --- a/homeassistant/components/lutron_caseta/device_trigger.py +++ b/homeassistant/components/lutron_caseta/device_trigger.py @@ -1,15 +1,16 @@ """Provides device triggers for lutron caseta.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -329,7 +330,9 @@ TRIGGER_SCHEMA = vol.Any( ) -async def async_validate_trigger_config(hass: HomeAssistant, config: ConfigType): +async def async_validate_trigger_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" # if device is available verify parameters against device capabilities device = get_button_device_by_dr_id(hass, config[CONF_DEVICE_ID]) @@ -347,14 +350,14 @@ async def async_validate_trigger_config(hass: HomeAssistant, config: ConfigType) async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> GetAutomationsResult: """List device triggers for lutron caseta devices.""" triggers = [] if not (device := get_button_device_by_dr_id(hass, device_id)): raise InvalidDeviceAutomationConfig(f"Device not found: {device_id}") - valid_buttons = DEVICE_TYPE_SUBTYPE_MAP_TO_LIP.get(device["type"], []) + valid_buttons = DEVICE_TYPE_SUBTYPE_MAP_TO_LIP.get(device["type"], {}) for trigger in SUPPORTED_INPUTS_EVENTS_TYPES: for subtype in valid_buttons: From ebc883b43ff9ca00d3426bbd0bdfdab707576252 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 19 May 2022 22:22:09 +0200 Subject: [PATCH 633/930] Adjust device_automation type hints in homekit_controller (#72199) --- .../components/homekit_controller/device_trigger.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index dcac7238c8e..5c46aee6ca2 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -14,7 +14,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.typing import ConfigType @@ -241,7 +244,7 @@ def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], Any]): async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers for homekit devices.""" if device_id not in hass.data.get(TRIGGERS, {}): From 68b278d17099b50f46c5f6803ffdbb2c628ef05b Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 19 May 2022 18:29:28 -0400 Subject: [PATCH 634/930] Remove legacy zwave migration logic (#72206) * Remove legacy zwave migration logic * Update test_migrate.py --- homeassistant/components/zwave_js/api.py | 77 --- homeassistant/components/zwave_js/entity.py | 6 - homeassistant/components/zwave_js/migrate.py | 338 +------------- tests/components/zwave_js/test_migrate.py | 466 ------------------- 4 files changed, 4 insertions(+), 883 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index d83b7261b5f..d27541fc61c 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -66,19 +66,12 @@ from .const import ( DATA_CLIENT, DOMAIN, EVENT_DEVICE_ADDED_TO_REGISTRY, - LOGGER, ) from .helpers import ( async_enable_statistics, async_get_node_from_device_id, update_data_collection_preference, ) -from .migrate import ( - ZWaveMigrationData, - async_get_migration_data, - async_map_legacy_zwave_values, - async_migrate_legacy_zwave, -) DATA_UNSUBSCRIBE = "unsubs" @@ -365,7 +358,6 @@ def async_register_api(hass: HomeAssistant) -> None: ) websocket_api.async_register_command(hass, websocket_subscribe_node_statistics) websocket_api.async_register_command(hass, websocket_node_ready) - websocket_api.async_register_command(hass, websocket_migrate_zwave) hass.http.register_view(FirmwareUploadView()) @@ -2059,72 +2051,3 @@ async def websocket_subscribe_node_statistics( }, ) ) - - -@websocket_api.require_admin -@websocket_api.websocket_command( - { - vol.Required(TYPE): "zwave_js/migrate_zwave", - vol.Required(ENTRY_ID): str, - vol.Optional(DRY_RUN, default=True): bool, - } -) -@websocket_api.async_response -@async_get_entry -async def websocket_migrate_zwave( - hass: HomeAssistant, - connection: ActiveConnection, - msg: dict, - entry: ConfigEntry, - client: Client, -) -> None: - """Migrate Z-Wave device and entity data to Z-Wave JS integration.""" - if "zwave" not in hass.config.components: - connection.send_message( - websocket_api.error_message( - msg["id"], "zwave_not_loaded", "Integration zwave is not loaded" - ) - ) - return - - zwave = hass.components.zwave - zwave_config_entries = hass.config_entries.async_entries("zwave") - zwave_config_entry = zwave_config_entries[0] # zwave only has a single config entry - zwave_data: dict[str, ZWaveMigrationData] = await zwave.async_get_migration_data( - hass, zwave_config_entry - ) - LOGGER.debug("Migration zwave data: %s", zwave_data) - - zwave_js_config_entry = entry - zwave_js_data = await async_get_migration_data(hass, zwave_js_config_entry) - LOGGER.debug("Migration zwave_js data: %s", zwave_js_data) - - migration_map = async_map_legacy_zwave_values(zwave_data, zwave_js_data) - - zwave_entity_ids = [entry["entity_id"] for entry in zwave_data.values()] - zwave_js_entity_ids = [entry["entity_id"] for entry in zwave_js_data.values()] - migration_device_map = { - zwave_device_id: zwave_js_device_id - for zwave_js_device_id, zwave_device_id in migration_map.device_entries.items() - } - migration_entity_map = { - zwave_entry["entity_id"]: zwave_js_entity_id - for zwave_js_entity_id, zwave_entry in migration_map.entity_entries.items() - } - LOGGER.debug("Migration entity map: %s", migration_entity_map) - - if not msg[DRY_RUN]: - await async_migrate_legacy_zwave( - hass, zwave_config_entry, zwave_js_config_entry, migration_map - ) - - connection.send_result( - msg[ID], - { - "migration_device_map": migration_device_map, - "zwave_entity_ids": zwave_entity_ids, - "zwave_js_entity_ids": zwave_js_entity_ids, - "migration_entity_map": migration_entity_map, - "migrated": not msg[DRY_RUN], - }, - ) diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index c6ed1902568..b89b4c9c9de 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -15,7 +15,6 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN from .discovery import ZwaveDiscoveryInfo from .helpers import get_device_id, get_unique_id -from .migrate import async_add_migration_entity_value LOGGER = logging.getLogger(__name__) @@ -117,11 +116,6 @@ class ZWaveBaseEntity(Entity): ) ) - # Add legacy Z-Wave migration data. - await async_add_migration_entity_value( - self.hass, self.config_entry, self.entity_id, self.info - ) - def generate_name( self, include_value_name: bool = False, diff --git a/homeassistant/components/zwave_js/migrate.py b/homeassistant/components/zwave_js/migrate.py index 204b5d0aebd..1413bf8e5b4 100644 --- a/homeassistant/components/zwave_js/migrate.py +++ b/homeassistant/components/zwave_js/migrate.py @@ -1,357 +1,27 @@ """Functions used to migrate unique IDs for Z-Wave JS entities.""" from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass import logging -from typing import TypedDict, cast from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.model.value import Value as ZwaveValue -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import LIGHT_LUX, STATE_UNAVAILABLE +from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.device_registry import ( - DeviceEntry, - async_get as async_get_device_registry, -) +from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.entity_registry import ( EntityRegistry, RegistryEntry, async_entries_for_device, - async_get as async_get_entity_registry, ) -from homeassistant.helpers.singleton import singleton -from homeassistant.helpers.storage import Store from .const import DOMAIN from .discovery import ZwaveDiscoveryInfo -from .helpers import get_device_id, get_unique_id +from .helpers import get_unique_id _LOGGER = logging.getLogger(__name__) -LEGACY_ZWAVE_MIGRATION = f"{DOMAIN}_legacy_zwave_migration" -MIGRATED = "migrated" -STORAGE_WRITE_DELAY = 30 -STORAGE_KEY = f"{DOMAIN}.legacy_zwave_migration" -STORAGE_VERSION = 1 - -NOTIFICATION_CC_LABEL_TO_PROPERTY_NAME = { - "Smoke": "Smoke Alarm", - "Carbon Monoxide": "CO Alarm", - "Carbon Dioxide": "CO2 Alarm", - "Heat": "Heat Alarm", - "Flood": "Water Alarm", - "Access Control": "Access Control", - "Burglar": "Home Security", - "Power Management": "Power Management", - "System": "System", - "Emergency": "Siren", - "Clock": "Clock", - "Appliance": "Appliance", - "HomeHealth": "Home Health", -} - -SENSOR_MULTILEVEL_CC_LABEL_TO_PROPERTY_NAME = { - "Temperature": "Air temperature", - "General": "General purpose", - "Luminance": "Illuminance", - "Power": "Power", - "Relative Humidity": "Humidity", - "Velocity": "Velocity", - "Direction": "Direction", - "Atmospheric Pressure": "Atmospheric pressure", - "Barometric Pressure": "Barometric pressure", - "Solar Radiation": "Solar radiation", - "Dew Point": "Dew point", - "Rain Rate": "Rain rate", - "Tide Level": "Tide level", - "Weight": "Weight", - "Voltage": "Voltage", - "Current": "Current", - "CO2 Level": "Carbon dioxide (CO₂) level", - "Air Flow": "Air flow", - "Tank Capacity": "Tank capacity", - "Distance": "Distance", - "Angle Position": "Angle position", - "Rotation": "Rotation", - "Water Temperature": "Water temperature", - "Soil Temperature": "Soil temperature", - "Seismic Intensity": "Seismic Intensity", - "Seismic Magnitude": "Seismic magnitude", - "Ultraviolet": "Ultraviolet", - "Electrical Resistivity": "Electrical resistivity", - "Electrical Conductivity": "Electrical conductivity", - "Loudness": "Loudness", - "Moisture": "Moisture", -} - -CC_ID_LABEL_TO_PROPERTY = { - 49: SENSOR_MULTILEVEL_CC_LABEL_TO_PROPERTY_NAME, - 113: NOTIFICATION_CC_LABEL_TO_PROPERTY_NAME, -} - -UNIT_LEGACY_MIGRATION_MAP = {LIGHT_LUX: "Lux"} - - -class ZWaveMigrationData(TypedDict): - """Represent the Z-Wave migration data dict.""" - - node_id: int - node_instance: int - command_class: int - command_class_label: str - value_index: int - device_id: str - domain: str - entity_id: str - unique_id: str - unit_of_measurement: str | None - - -class ZWaveJSMigrationData(TypedDict): - """Represent the Z-Wave JS migration data dict.""" - - node_id: int - endpoint_index: int - command_class: int - value_property_name: str - value_property_key_name: str | None - value_id: str - device_id: str - domain: str - entity_id: str - unique_id: str - unit_of_measurement: str | None - - -@dataclass -class LegacyZWaveMappedData: - """Represent the mapped data between Z-Wave and Z-Wave JS.""" - - entity_entries: dict[str, ZWaveMigrationData] = field(default_factory=dict) - device_entries: dict[str, str] = field(default_factory=dict) - - -async def async_add_migration_entity_value( - hass: HomeAssistant, - config_entry: ConfigEntry, - entity_id: str, - discovery_info: ZwaveDiscoveryInfo, -) -> None: - """Add Z-Wave JS entity value for legacy Z-Wave migration.""" - migration_handler: LegacyZWaveMigration = await get_legacy_zwave_migration(hass) - migration_handler.add_entity_value(config_entry, entity_id, discovery_info) - - -async def async_get_migration_data( - hass: HomeAssistant, config_entry: ConfigEntry -) -> dict[str, ZWaveJSMigrationData]: - """Return Z-Wave JS migration data.""" - migration_handler: LegacyZWaveMigration = await get_legacy_zwave_migration(hass) - return await migration_handler.get_data(config_entry) - - -@singleton(LEGACY_ZWAVE_MIGRATION) -async def get_legacy_zwave_migration(hass: HomeAssistant) -> LegacyZWaveMigration: - """Return legacy Z-Wave migration handler.""" - migration_handler = LegacyZWaveMigration(hass) - await migration_handler.load_data() - return migration_handler - - -class LegacyZWaveMigration: - """Handle the migration from zwave to zwave_js.""" - - def __init__(self, hass: HomeAssistant) -> None: - """Set up migration instance.""" - self._hass = hass - self._store = Store(hass, STORAGE_VERSION, STORAGE_KEY) - self._data: dict[str, dict[str, ZWaveJSMigrationData]] = {} - - async def load_data(self) -> None: - """Load Z-Wave JS migration data.""" - stored = cast(dict, await self._store.async_load()) - if stored: - self._data = stored - - @callback - def save_data( - self, config_entry_id: str, entity_id: str, data: ZWaveJSMigrationData - ) -> None: - """Save Z-Wave JS migration data.""" - if config_entry_id not in self._data: - self._data[config_entry_id] = {} - self._data[config_entry_id][entity_id] = data - self._store.async_delay_save(self._data_to_save, STORAGE_WRITE_DELAY) - - @callback - def _data_to_save(self) -> dict[str, dict[str, ZWaveJSMigrationData]]: - """Return data to save.""" - return self._data - - @callback - def add_entity_value( - self, - config_entry: ConfigEntry, - entity_id: str, - discovery_info: ZwaveDiscoveryInfo, - ) -> None: - """Add info for one entity and Z-Wave JS value.""" - ent_reg = async_get_entity_registry(self._hass) - dev_reg = async_get_device_registry(self._hass) - - node = discovery_info.node - primary_value = discovery_info.primary_value - entity_entry = ent_reg.async_get(entity_id) - assert entity_entry - device_identifier = get_device_id(node.client, node) - device_entry = dev_reg.async_get_device({device_identifier}, set()) - assert device_entry - - # Normalize unit of measurement. - if unit := entity_entry.unit_of_measurement: - _unit = UNIT_LEGACY_MIGRATION_MAP.get(unit, unit) - unit = _unit.lower() - if unit == "": - unit = None - - data: ZWaveJSMigrationData = { - "node_id": node.node_id, - "endpoint_index": node.index, - "command_class": primary_value.command_class, - "value_property_name": primary_value.property_name, - "value_property_key_name": primary_value.property_key_name, - "value_id": primary_value.value_id, - "device_id": device_entry.id, - "domain": entity_entry.domain, - "entity_id": entity_id, - "unique_id": entity_entry.unique_id, - "unit_of_measurement": unit, - } - - self.save_data(config_entry.entry_id, entity_id, data) - - async def get_data( - self, config_entry: ConfigEntry - ) -> dict[str, ZWaveJSMigrationData]: - """Return Z-Wave JS migration data for a config entry.""" - await self.load_data() - data = self._data.get(config_entry.entry_id) - return data or {} - - -@callback -def async_map_legacy_zwave_values( - zwave_data: dict[str, ZWaveMigrationData], - zwave_js_data: dict[str, ZWaveJSMigrationData], -) -> LegacyZWaveMappedData: - """Map Z-Wave node values onto Z-Wave JS node values.""" - migration_map = LegacyZWaveMappedData() - zwave_proc_data: dict[ - tuple[int, int, int, str, str | None, str | None], - ZWaveMigrationData | None, - ] = {} - zwave_js_proc_data: dict[ - tuple[int, int, int, str, str | None, str | None], - ZWaveJSMigrationData | None, - ] = {} - - for zwave_item in zwave_data.values(): - zwave_js_property_name = CC_ID_LABEL_TO_PROPERTY.get( - zwave_item["command_class"], {} - ).get(zwave_item["command_class_label"]) - item_id = ( - zwave_item["node_id"], - zwave_item["command_class"], - zwave_item["node_instance"] - 1, - zwave_item["domain"], - zwave_item["unit_of_measurement"], - zwave_js_property_name, - ) - - # Filter out duplicates that are not resolvable. - if item_id in zwave_proc_data: - zwave_proc_data[item_id] = None - continue - - zwave_proc_data[item_id] = zwave_item - - for zwave_js_item in zwave_js_data.values(): - # Only identify with property name if there is a command class label map. - if zwave_js_item["command_class"] in CC_ID_LABEL_TO_PROPERTY: - zwave_js_property_name = zwave_js_item["value_property_name"] - else: - zwave_js_property_name = None - item_id = ( - zwave_js_item["node_id"], - zwave_js_item["command_class"], - zwave_js_item["endpoint_index"], - zwave_js_item["domain"], - zwave_js_item["unit_of_measurement"], - zwave_js_property_name, - ) - - # Filter out duplicates that are not resolvable. - if item_id in zwave_js_proc_data: - zwave_js_proc_data[item_id] = None - continue - - zwave_js_proc_data[item_id] = zwave_js_item - - for item_id, zwave_entry in zwave_proc_data.items(): - zwave_js_entry = zwave_js_proc_data.pop(item_id, None) - - if zwave_entry is None or zwave_js_entry is None: - continue - - migration_map.entity_entries[zwave_js_entry["entity_id"]] = zwave_entry - migration_map.device_entries[zwave_js_entry["device_id"]] = zwave_entry[ - "device_id" - ] - - return migration_map - - -async def async_migrate_legacy_zwave( - hass: HomeAssistant, - zwave_config_entry: ConfigEntry, - zwave_js_config_entry: ConfigEntry, - migration_map: LegacyZWaveMappedData, -) -> None: - """Perform Z-Wave to Z-Wave JS migration.""" - dev_reg = async_get_device_registry(hass) - for zwave_js_device_id, zwave_device_id in migration_map.device_entries.items(): - zwave_device_entry = dev_reg.async_get(zwave_device_id) - if not zwave_device_entry: - continue - dev_reg.async_update_device( - zwave_js_device_id, - area_id=zwave_device_entry.area_id, - name_by_user=zwave_device_entry.name_by_user, - ) - - ent_reg = async_get_entity_registry(hass) - for zwave_js_entity_id, zwave_entry in migration_map.entity_entries.items(): - zwave_entity_id = zwave_entry["entity_id"] - if not (entity_entry := ent_reg.async_get(zwave_entity_id)): - continue - ent_reg.async_remove(zwave_entity_id) - ent_reg.async_update_entity( - zwave_js_entity_id, - new_entity_id=entity_entry.entity_id, - name=entity_entry.name, - icon=entity_entry.icon, - ) - - await hass.config_entries.async_remove(zwave_config_entry.entry_id) - - updates = { - **zwave_js_config_entry.data, - MIGRATED: True, - } - hass.config_entries.async_update_entry(zwave_js_config_entry, data=updates) - @dataclass class ValueID: diff --git a/tests/components/zwave_js/test_migrate.py b/tests/components/zwave_js/test_migrate.py index 79201ebbede..37c53700d95 100644 --- a/tests/components/zwave_js/test_migrate.py +++ b/tests/components/zwave_js/test_migrate.py @@ -1,481 +1,15 @@ """Test the Z-Wave JS migration module.""" import copy -from unittest.mock import patch import pytest from zwave_js_server.model.node import Node -from homeassistant.components.zwave_js.api import ENTRY_ID, ID, TYPE from homeassistant.components.zwave_js.const import DOMAIN from homeassistant.components.zwave_js.helpers import get_device_id -from homeassistant.const import LIGHT_LUX from homeassistant.helpers import device_registry as dr, entity_registry as er from .common import AIR_TEMPERATURE_SENSOR, NOTIFICATION_MOTION_BINARY_SENSOR -from tests.common import MockConfigEntry, mock_device_registry, mock_registry - -# Switch device -ZWAVE_SWITCH_DEVICE_ID = "zwave_switch_device_id" -ZWAVE_SWITCH_DEVICE_NAME = "Z-Wave Switch Device" -ZWAVE_SWITCH_DEVICE_AREA = "Z-Wave Switch Area" -ZWAVE_SWITCH_ENTITY = "switch.zwave_switch_node" -ZWAVE_SWITCH_UNIQUE_ID = "102-6789" -ZWAVE_SWITCH_NAME = "Z-Wave Switch" -ZWAVE_SWITCH_ICON = "mdi:zwave-test-switch" -ZWAVE_POWER_ENTITY = "sensor.zwave_power" -ZWAVE_POWER_UNIQUE_ID = "102-5678" -ZWAVE_POWER_NAME = "Z-Wave Power" -ZWAVE_POWER_ICON = "mdi:zwave-test-power" - -# Multisensor device -ZWAVE_MULTISENSOR_DEVICE_ID = "zwave_multisensor_device_id" -ZWAVE_MULTISENSOR_DEVICE_NAME = "Z-Wave Multisensor Device" -ZWAVE_MULTISENSOR_DEVICE_AREA = "Z-Wave Multisensor Area" -ZWAVE_SOURCE_NODE_ENTITY = "sensor.zwave_source_node" -ZWAVE_SOURCE_NODE_UNIQUE_ID = "52-4321" -ZWAVE_LUMINANCE_ENTITY = "sensor.zwave_luminance" -ZWAVE_LUMINANCE_UNIQUE_ID = "52-6543" -ZWAVE_LUMINANCE_NAME = "Z-Wave Luminance" -ZWAVE_LUMINANCE_ICON = "mdi:zwave-test-luminance" -ZWAVE_BATTERY_ENTITY = "sensor.zwave_battery_level" -ZWAVE_BATTERY_UNIQUE_ID = "52-1234" -ZWAVE_BATTERY_NAME = "Z-Wave Battery Level" -ZWAVE_BATTERY_ICON = "mdi:zwave-test-battery" -ZWAVE_TAMPERING_ENTITY = "sensor.zwave_tampering" -ZWAVE_TAMPERING_UNIQUE_ID = "52-3456" -ZWAVE_TAMPERING_NAME = "Z-Wave Tampering" -ZWAVE_TAMPERING_ICON = "mdi:zwave-test-tampering" - - -@pytest.fixture(name="zwave_migration_data") -def zwave_migration_data_fixture(hass): - """Return mock zwave migration data.""" - zwave_switch_device = dr.DeviceEntry( - id=ZWAVE_SWITCH_DEVICE_ID, - name_by_user=ZWAVE_SWITCH_DEVICE_NAME, - area_id=ZWAVE_SWITCH_DEVICE_AREA, - ) - zwave_switch_entry = er.RegistryEntry( - entity_id=ZWAVE_SWITCH_ENTITY, - unique_id=ZWAVE_SWITCH_UNIQUE_ID, - platform="zwave", - name=ZWAVE_SWITCH_NAME, - icon=ZWAVE_SWITCH_ICON, - ) - zwave_multisensor_device = dr.DeviceEntry( - id=ZWAVE_MULTISENSOR_DEVICE_ID, - name_by_user=ZWAVE_MULTISENSOR_DEVICE_NAME, - area_id=ZWAVE_MULTISENSOR_DEVICE_AREA, - ) - zwave_source_node_entry = er.RegistryEntry( - entity_id=ZWAVE_SOURCE_NODE_ENTITY, - unique_id=ZWAVE_SOURCE_NODE_UNIQUE_ID, - platform="zwave", - name="Z-Wave Source Node", - ) - zwave_luminance_entry = er.RegistryEntry( - entity_id=ZWAVE_LUMINANCE_ENTITY, - unique_id=ZWAVE_LUMINANCE_UNIQUE_ID, - platform="zwave", - name=ZWAVE_LUMINANCE_NAME, - icon=ZWAVE_LUMINANCE_ICON, - unit_of_measurement="lux", - ) - zwave_battery_entry = er.RegistryEntry( - entity_id=ZWAVE_BATTERY_ENTITY, - unique_id=ZWAVE_BATTERY_UNIQUE_ID, - platform="zwave", - name=ZWAVE_BATTERY_NAME, - icon=ZWAVE_BATTERY_ICON, - unit_of_measurement="%", - ) - zwave_power_entry = er.RegistryEntry( - entity_id=ZWAVE_POWER_ENTITY, - unique_id=ZWAVE_POWER_UNIQUE_ID, - platform="zwave", - name=ZWAVE_POWER_NAME, - icon=ZWAVE_POWER_ICON, - unit_of_measurement="W", - ) - zwave_tampering_entry = er.RegistryEntry( - entity_id=ZWAVE_TAMPERING_ENTITY, - unique_id=ZWAVE_TAMPERING_UNIQUE_ID, - platform="zwave", - name=ZWAVE_TAMPERING_NAME, - icon=ZWAVE_TAMPERING_ICON, - unit_of_measurement="", # Test empty string unit normalization. - ) - - zwave_migration_data = { - ZWAVE_SWITCH_ENTITY: { - "node_id": 102, - "node_instance": 1, - "command_class": 37, - "command_class_label": "", - "value_index": 1, - "device_id": zwave_switch_device.id, - "domain": zwave_switch_entry.domain, - "entity_id": zwave_switch_entry.entity_id, - "unique_id": ZWAVE_SWITCH_UNIQUE_ID, - "unit_of_measurement": zwave_switch_entry.unit_of_measurement, - }, - ZWAVE_POWER_ENTITY: { - "node_id": 102, - "node_instance": 1, - "command_class": 50, - "command_class_label": "Power", - "value_index": 8, - "device_id": zwave_switch_device.id, - "domain": zwave_power_entry.domain, - "entity_id": zwave_power_entry.entity_id, - "unique_id": ZWAVE_POWER_UNIQUE_ID, - "unit_of_measurement": zwave_power_entry.unit_of_measurement, - }, - ZWAVE_SOURCE_NODE_ENTITY: { - "node_id": 52, - "node_instance": 1, - "command_class": 113, - "command_class_label": "SourceNodeId", - "value_index": 1, - "device_id": zwave_multisensor_device.id, - "domain": zwave_source_node_entry.domain, - "entity_id": zwave_source_node_entry.entity_id, - "unique_id": ZWAVE_SOURCE_NODE_UNIQUE_ID, - "unit_of_measurement": zwave_source_node_entry.unit_of_measurement, - }, - ZWAVE_LUMINANCE_ENTITY: { - "node_id": 52, - "node_instance": 1, - "command_class": 49, - "command_class_label": "Luminance", - "value_index": 3, - "device_id": zwave_multisensor_device.id, - "domain": zwave_luminance_entry.domain, - "entity_id": zwave_luminance_entry.entity_id, - "unique_id": ZWAVE_LUMINANCE_UNIQUE_ID, - "unit_of_measurement": zwave_luminance_entry.unit_of_measurement, - }, - ZWAVE_BATTERY_ENTITY: { - "node_id": 52, - "node_instance": 1, - "command_class": 128, - "command_class_label": "Battery Level", - "value_index": 0, - "device_id": zwave_multisensor_device.id, - "domain": zwave_battery_entry.domain, - "entity_id": zwave_battery_entry.entity_id, - "unique_id": ZWAVE_BATTERY_UNIQUE_ID, - "unit_of_measurement": zwave_battery_entry.unit_of_measurement, - }, - ZWAVE_TAMPERING_ENTITY: { - "node_id": 52, - "node_instance": 1, - "command_class": 113, - "command_class_label": "Burglar", - "value_index": 10, - "device_id": zwave_multisensor_device.id, - "domain": zwave_tampering_entry.domain, - "entity_id": zwave_tampering_entry.entity_id, - "unique_id": ZWAVE_TAMPERING_UNIQUE_ID, - "unit_of_measurement": zwave_tampering_entry.unit_of_measurement, - }, - } - - mock_device_registry( - hass, - { - zwave_switch_device.id: zwave_switch_device, - zwave_multisensor_device.id: zwave_multisensor_device, - }, - ) - mock_registry( - hass, - { - ZWAVE_SWITCH_ENTITY: zwave_switch_entry, - ZWAVE_SOURCE_NODE_ENTITY: zwave_source_node_entry, - ZWAVE_LUMINANCE_ENTITY: zwave_luminance_entry, - ZWAVE_BATTERY_ENTITY: zwave_battery_entry, - ZWAVE_POWER_ENTITY: zwave_power_entry, - ZWAVE_TAMPERING_ENTITY: zwave_tampering_entry, - }, - ) - - return zwave_migration_data - - -@pytest.fixture(name="zwave_integration") -def zwave_integration_fixture(hass, zwave_migration_data): - """Mock the zwave integration.""" - hass.config.components.add("zwave") - zwave_config_entry = MockConfigEntry(domain="zwave", data={"usb_path": "/dev/test"}) - zwave_config_entry.add_to_hass(hass) - with patch( - "homeassistant.components.zwave.async_get_migration_data", - return_value=zwave_migration_data, - ): - yield zwave_config_entry - - -@pytest.mark.skip(reason="The old zwave integration has been removed.") -async def test_migrate_zwave( - hass, - zwave_integration, - aeon_smart_switch_6, - multisensor_6, - integration, - hass_ws_client, -): - """Test the Z-Wave to Z-Wave JS migration websocket api.""" - entry = integration - client = await hass_ws_client(hass) - - assert hass.config_entries.async_entries("zwave") - - await client.send_json( - { - ID: 5, - TYPE: "zwave_js/migrate_zwave", - ENTRY_ID: entry.entry_id, - "dry_run": False, - } - ) - msg = await client.receive_json() - result = msg["result"] - - migration_entity_map = { - ZWAVE_SWITCH_ENTITY: "switch.smart_switch_6", - ZWAVE_LUMINANCE_ENTITY: "sensor.multisensor_6_illuminance", - ZWAVE_BATTERY_ENTITY: "sensor.multisensor_6_battery_level", - } - - assert result["zwave_entity_ids"] == [ - ZWAVE_SWITCH_ENTITY, - ZWAVE_POWER_ENTITY, - ZWAVE_SOURCE_NODE_ENTITY, - ZWAVE_LUMINANCE_ENTITY, - ZWAVE_BATTERY_ENTITY, - ZWAVE_TAMPERING_ENTITY, - ] - expected_zwave_js_entities = [ - "switch.smart_switch_6", - "sensor.multisensor_6_air_temperature", - "sensor.multisensor_6_illuminance", - "sensor.multisensor_6_humidity", - "sensor.multisensor_6_ultraviolet", - "binary_sensor.multisensor_6_home_security_tampering_product_cover_removed", - "binary_sensor.multisensor_6_home_security_motion_detection", - "sensor.multisensor_6_battery_level", - "binary_sensor.multisensor_6_low_battery_level", - "light.smart_switch_6", - "sensor.smart_switch_6_electric_consumed_kwh", - "sensor.smart_switch_6_electric_consumed_w", - "sensor.smart_switch_6_electric_consumed_v", - "sensor.smart_switch_6_electric_consumed_a", - ] - # Assert that both lists have the same items without checking order - assert not set(result["zwave_js_entity_ids"]) ^ set(expected_zwave_js_entities) - assert result["migration_entity_map"] == migration_entity_map - assert result["migrated"] is True - - dev_reg = dr.async_get(hass) - ent_reg = er.async_get(hass) - - # check the device registry migration - - # check that the migrated entries have correct attributes - multisensor_device_entry = dev_reg.async_get_device( - identifiers={("zwave_js", "3245146787-52")}, connections=set() - ) - assert multisensor_device_entry - assert multisensor_device_entry.name_by_user == ZWAVE_MULTISENSOR_DEVICE_NAME - assert multisensor_device_entry.area_id == ZWAVE_MULTISENSOR_DEVICE_AREA - switch_device_entry = dev_reg.async_get_device( - identifiers={("zwave_js", "3245146787-102")}, connections=set() - ) - assert switch_device_entry - assert switch_device_entry.name_by_user == ZWAVE_SWITCH_DEVICE_NAME - assert switch_device_entry.area_id == ZWAVE_SWITCH_DEVICE_AREA - - migration_device_map = { - ZWAVE_SWITCH_DEVICE_ID: switch_device_entry.id, - ZWAVE_MULTISENSOR_DEVICE_ID: multisensor_device_entry.id, - } - - assert result["migration_device_map"] == migration_device_map - - # check the entity registry migration - - # this should have been migrated and no longer present under that id - assert not ent_reg.async_is_registered("sensor.multisensor_6_battery_level") - assert not ent_reg.async_is_registered("sensor.multisensor_6_illuminance") - - # these should not have been migrated and is still in the registry - assert ent_reg.async_is_registered(ZWAVE_SOURCE_NODE_ENTITY) - source_entry = ent_reg.async_get(ZWAVE_SOURCE_NODE_ENTITY) - assert source_entry.unique_id == ZWAVE_SOURCE_NODE_UNIQUE_ID - assert ent_reg.async_is_registered(ZWAVE_POWER_ENTITY) - source_entry = ent_reg.async_get(ZWAVE_POWER_ENTITY) - assert source_entry.unique_id == ZWAVE_POWER_UNIQUE_ID - assert ent_reg.async_is_registered(ZWAVE_TAMPERING_ENTITY) - tampering_entry = ent_reg.async_get(ZWAVE_TAMPERING_ENTITY) - assert tampering_entry.unique_id == ZWAVE_TAMPERING_UNIQUE_ID - assert ent_reg.async_is_registered("sensor.smart_switch_6_electric_consumed_w") - - # this is the new entity_ids of the zwave_js entities - assert ent_reg.async_is_registered(ZWAVE_SWITCH_ENTITY) - assert ent_reg.async_is_registered(ZWAVE_BATTERY_ENTITY) - assert ent_reg.async_is_registered(ZWAVE_LUMINANCE_ENTITY) - - # check that the migrated entries have correct attributes - switch_entry = ent_reg.async_get(ZWAVE_SWITCH_ENTITY) - assert switch_entry - assert switch_entry.unique_id == "3245146787.102-37-0-currentValue" - assert switch_entry.name == ZWAVE_SWITCH_NAME - assert switch_entry.icon == ZWAVE_SWITCH_ICON - battery_entry = ent_reg.async_get(ZWAVE_BATTERY_ENTITY) - assert battery_entry - assert battery_entry.unique_id == "3245146787.52-128-0-level" - assert battery_entry.name == ZWAVE_BATTERY_NAME - assert battery_entry.icon == ZWAVE_BATTERY_ICON - luminance_entry = ent_reg.async_get(ZWAVE_LUMINANCE_ENTITY) - assert luminance_entry - assert luminance_entry.unique_id == "3245146787.52-49-0-Illuminance" - assert luminance_entry.name == ZWAVE_LUMINANCE_NAME - assert luminance_entry.icon == ZWAVE_LUMINANCE_ICON - assert luminance_entry.unit_of_measurement == LIGHT_LUX - - # check that the zwave config entry has been removed - assert not hass.config_entries.async_entries("zwave") - - # Check that the zwave integration fails entry setup after migration - zwave_config_entry = MockConfigEntry(domain="zwave") - zwave_config_entry.add_to_hass(hass) - assert not await hass.config_entries.async_setup(zwave_config_entry.entry_id) - - -@pytest.mark.skip(reason="The old zwave integration has been removed.") -async def test_migrate_zwave_dry_run( - hass, - zwave_integration, - aeon_smart_switch_6, - multisensor_6, - integration, - hass_ws_client, -): - """Test the zwave to zwave_js migration websocket api dry run.""" - entry = integration - client = await hass_ws_client(hass) - - await client.send_json( - {ID: 5, TYPE: "zwave_js/migrate_zwave", ENTRY_ID: entry.entry_id} - ) - msg = await client.receive_json() - result = msg["result"] - - migration_entity_map = { - ZWAVE_SWITCH_ENTITY: "switch.smart_switch_6", - ZWAVE_BATTERY_ENTITY: "sensor.multisensor_6_battery_level", - } - - assert result["zwave_entity_ids"] == [ - ZWAVE_SWITCH_ENTITY, - ZWAVE_POWER_ENTITY, - ZWAVE_SOURCE_NODE_ENTITY, - ZWAVE_BATTERY_ENTITY, - ZWAVE_TAMPERING_ENTITY, - ] - expected_zwave_js_entities = [ - "switch.smart_switch_6", - "sensor.multisensor_6_air_temperature", - "sensor.multisensor_6_illuminance", - "sensor.multisensor_6_humidity", - "sensor.multisensor_6_ultraviolet", - "binary_sensor.multisensor_6_home_security_tampering_product_cover_removed", - "binary_sensor.multisensor_6_home_security_motion_detection", - "sensor.multisensor_6_battery_level", - "binary_sensor.multisensor_6_low_battery_level", - "light.smart_switch_6", - "sensor.smart_switch_6_electric_consumed_kwh", - "sensor.smart_switch_6_electric_consumed_w", - "sensor.smart_switch_6_electric_consumed_v", - "sensor.smart_switch_6_electric_consumed_a", - ] - # Assert that both lists have the same items without checking order - assert not set(result["zwave_js_entity_ids"]) ^ set(expected_zwave_js_entities) - assert result["migration_entity_map"] == migration_entity_map - - dev_reg = dr.async_get(hass) - - multisensor_device_entry = dev_reg.async_get_device( - identifiers={("zwave_js", "3245146787-52")}, connections=set() - ) - assert multisensor_device_entry - assert multisensor_device_entry.name_by_user is None - assert multisensor_device_entry.area_id is None - switch_device_entry = dev_reg.async_get_device( - identifiers={("zwave_js", "3245146787-102")}, connections=set() - ) - assert switch_device_entry - assert switch_device_entry.name_by_user is None - assert switch_device_entry.area_id is None - - migration_device_map = { - ZWAVE_SWITCH_DEVICE_ID: switch_device_entry.id, - ZWAVE_MULTISENSOR_DEVICE_ID: multisensor_device_entry.id, - } - - assert result["migration_device_map"] == migration_device_map - - assert result["migrated"] is False - - ent_reg = er.async_get(hass) - - # no real migration should have been done - assert ent_reg.async_is_registered("switch.smart_switch_6") - assert ent_reg.async_is_registered("sensor.multisensor_6_battery_level") - assert ent_reg.async_is_registered("sensor.smart_switch_6_electric_consumed_w") - - assert ent_reg.async_is_registered(ZWAVE_SOURCE_NODE_ENTITY) - source_entry = ent_reg.async_get(ZWAVE_SOURCE_NODE_ENTITY) - assert source_entry - assert source_entry.unique_id == ZWAVE_SOURCE_NODE_UNIQUE_ID - - assert ent_reg.async_is_registered(ZWAVE_BATTERY_ENTITY) - battery_entry = ent_reg.async_get(ZWAVE_BATTERY_ENTITY) - assert battery_entry - assert battery_entry.unique_id == ZWAVE_BATTERY_UNIQUE_ID - - assert ent_reg.async_is_registered(ZWAVE_POWER_ENTITY) - power_entry = ent_reg.async_get(ZWAVE_POWER_ENTITY) - assert power_entry - assert power_entry.unique_id == ZWAVE_POWER_UNIQUE_ID - - # check that the zwave config entry has not been removed - assert hass.config_entries.async_entries("zwave") - - # Check that the zwave integration can be setup after dry run - zwave_config_entry = zwave_integration - with patch("openzwave.option.ZWaveOption"), patch("openzwave.network.ZWaveNetwork"): - assert await hass.config_entries.async_setup(zwave_config_entry.entry_id) - - -async def test_migrate_zwave_not_setup( - hass, aeon_smart_switch_6, multisensor_6, integration, hass_ws_client -): - """Test the zwave to zwave_js migration websocket without zwave setup.""" - entry = integration - client = await hass_ws_client(hass) - - await client.send_json( - {ID: 5, TYPE: "zwave_js/migrate_zwave", ENTRY_ID: entry.entry_id} - ) - msg = await client.receive_json() - - assert not msg["success"] - assert msg["error"]["code"] == "zwave_not_loaded" - assert msg["error"]["message"] == "Integration zwave is not loaded" - async def test_unique_id_migration_dupes( hass, multisensor_6_state, client, integration From 3681596430cb0b065ce0c5117f6efd89d2460556 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 May 2022 18:33:49 -0500 Subject: [PATCH 635/930] Bump nexia to 1.0.1 (#72185) - Fixes blower state when system is turned off - Changelog: https://github.com/bdraco/nexia/compare/1.0.0...1.0.1 - Fixes #71949 --- homeassistant/components/nexia/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index bd1cd58c00b..f9ca21d9e0b 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia/American Standard/Trane", - "requirements": ["nexia==1.0.0"], + "requirements": ["nexia==1.0.1"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 13b3b172803..55765f9d139 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1077,7 +1077,7 @@ nettigo-air-monitor==1.2.4 neurio==0.3.1 # homeassistant.components.nexia -nexia==1.0.0 +nexia==1.0.1 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 86f49132bc0..fdbe51fbb49 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -742,7 +742,7 @@ netmap==0.7.0.2 nettigo-air-monitor==1.2.4 # homeassistant.components.nexia -nexia==1.0.0 +nexia==1.0.1 # homeassistant.components.discord nextcord==2.0.0a8 From 9a3ecacf6bb03d227c7c10d0db01705c23dd3660 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 20 May 2022 00:21:27 +0000 Subject: [PATCH 636/930] [ci skip] Translation update --- .../aladdin_connect/translations/pl.json | 1 + .../components/baf/translations/nl.json | 4 +-- .../components/cover/translations/nl.json | 2 +- .../derivative/translations/nl.json | 4 +-- .../components/esphome/translations/nl.json | 6 ++--- .../components/group/translations/nl.json | 2 +- .../litterrobot/translations/sensor.pl.json | 21 +++++++++++++++- .../motion_blinds/translations/pl.json | 2 +- .../components/shelly/translations/de.json | 1 + .../components/shelly/translations/el.json | 1 + .../components/shelly/translations/et.json | 1 + .../shelly/translations/zh-Hant.json | 1 + .../components/slack/translations/nl.json | 2 +- .../components/slack/translations/pl.json | 5 ++++ .../somfy_mylink/translations/nl.json | 4 +-- .../components/yolink/translations/el.json | 25 +++++++++++++++++++ .../components/zone/translations/nl.json | 2 +- .../components/zwave_js/translations/nl.json | 4 +-- 18 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/yolink/translations/el.json diff --git a/homeassistant/components/aladdin_connect/translations/pl.json b/homeassistant/components/aladdin_connect/translations/pl.json index 3ebc9a4ff92..14528332e1e 100644 --- a/homeassistant/components/aladdin_connect/translations/pl.json +++ b/homeassistant/components/aladdin_connect/translations/pl.json @@ -13,6 +13,7 @@ "data": { "password": "Has\u0142o" }, + "description": "Integracja Aladdin Connect wymaga ponownego uwierzytelnienia Twojego konta", "title": "Ponownie uwierzytelnij integracj\u0119" }, "user": { diff --git a/homeassistant/components/baf/translations/nl.json b/homeassistant/components/baf/translations/nl.json index 9b619390165..a05aedd9381 100644 --- a/homeassistant/components/baf/translations/nl.json +++ b/homeassistant/components/baf/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "ipv6_not_supported": "IPv6 wordt niet ondersteund" + "ipv6_not_supported": "IPv6 wordt niet ondersteund." }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -11,7 +11,7 @@ "flow_title": "{name} - {model} ({ip_address})", "step": { "discovery_confirm": { - "description": "Wilt u {name} - {model} ({ip_address}) instellen?" + "description": "Wilt je {name} - {model} ({ip_address}) instellen?" }, "user": { "data": { diff --git a/homeassistant/components/cover/translations/nl.json b/homeassistant/components/cover/translations/nl.json index c3998187c4f..80acc874379 100644 --- a/homeassistant/components/cover/translations/nl.json +++ b/homeassistant/components/cover/translations/nl.json @@ -35,5 +35,5 @@ "stopped": "Gestopt" } }, - "title": "Rolluik" + "title": "Afdekkingen" } \ No newline at end of file diff --git a/homeassistant/components/derivative/translations/nl.json b/homeassistant/components/derivative/translations/nl.json index f3dc9c07de7..c6d485e7548 100644 --- a/homeassistant/components/derivative/translations/nl.json +++ b/homeassistant/components/derivative/translations/nl.json @@ -5,7 +5,7 @@ "data": { "name": "Naam", "round": "Nauwkeurigheid", - "source": "Invoersensor", + "source": "Bronsensor", "time_window": "Tijdsvenster", "unit_prefix": "Metrisch voorvoegsel", "unit_time": "Tijdseenheid" @@ -26,7 +26,7 @@ "data": { "name": "Naam", "round": "Nauwkeurigheid", - "source": "Invoersensor", + "source": "Bronsensor", "time_window": "Tijdsvenster", "unit_prefix": "Metrisch voorvoegsel", "unit_time": "Tijdseenheid" diff --git a/homeassistant/components/esphome/translations/nl.json b/homeassistant/components/esphome/translations/nl.json index 7f6f821104c..9c757e917e4 100644 --- a/homeassistant/components/esphome/translations/nl.json +++ b/homeassistant/components/esphome/translations/nl.json @@ -20,8 +20,8 @@ "description": "Voer het wachtwoord in dat u in uw configuratie heeft ingesteld voor {name}." }, "discovery_confirm": { - "description": "Wil je de ESPHome-node `{name}` toevoegen aan de Home Assistant?", - "title": "ESPHome node ontdekt" + "description": "Wil je de ESPHome-apparaat `{name}` toevoegen aan de Home Assistant?", + "title": "ESPHome apparaat ontdekt" }, "encryption_key": { "data": { @@ -40,7 +40,7 @@ "host": "Host", "port": "Poort" }, - "description": "Voer de verbindingsinstellingen in van uw [ESPHome](https://esphomelib.com/) node." + "description": "Voer de verbindingsinstellingen van je [ESPHome](https://esphome.io/) apparaat in." } } } diff --git a/homeassistant/components/group/translations/nl.json b/homeassistant/components/group/translations/nl.json index e78e3462c60..2b78d41e201 100644 --- a/homeassistant/components/group/translations/nl.json +++ b/homeassistant/components/group/translations/nl.json @@ -63,7 +63,7 @@ "description": "Met groepen kunt u een nieuwe entiteit cre\u00ebren die meerdere entiteiten van hetzelfde type vertegenwoordigt.", "menu_options": { "binary_sensor": "Binaire-sensorgroep", - "cover": "Rolluikgroep", + "cover": "Afdekkingengroep", "fan": "Ventilatorgroep", "light": "Lichtgroep", "lock": "Slotgroep", diff --git a/homeassistant/components/litterrobot/translations/sensor.pl.json b/homeassistant/components/litterrobot/translations/sensor.pl.json index 4f12cd1f30c..5e75c2ff68a 100644 --- a/homeassistant/components/litterrobot/translations/sensor.pl.json +++ b/homeassistant/components/litterrobot/translations/sensor.pl.json @@ -1,9 +1,28 @@ { "state": { "litterrobot__status_code": { + "br": "pokrywa otwarta", + "ccc": "cykl czyszczenia zako\u0144czony", + "ccp": "cykl czyszczenia w toku", + "csf": "b\u0142\u0105d sensora obecno\u015bci", + "csi": "przerwa w pracy sensora", + "cst": "czas pracy sensora", + "df1": "szuflada prawie pe\u0142na - pozosta\u0142y 2 cykle", + "df2": "szuflada prawie pe\u0142na - pozosta\u0142 1 cykl", + "dfs": "szuflada pe\u0142na", + "dhf": "b\u0142\u0105d pozycji wyj\u015bciowej + odchod\u00f3w", + "dpf": "b\u0142\u0105d pozycji odchod\u00f3w", + "ec": "cykl opr\u00f3\u017cniania", + "hpf": "b\u0142\u0105d pozycji wyj\u015bciowej", "off": "wy\u0142.", "offline": "Offline", - "p": "wstrzymany" + "otf": "b\u0142\u0105d nadmiernego momentu obrotowego", + "p": "wstrzymany", + "pd": "detektor obecno\u015bci", + "rdy": "gotowy", + "scf": "b\u0142\u0105d sensora obecno\u015bci podczas uruchamiania", + "sdf": "szuflada pe\u0142na podczas uruchamiania", + "spf": "detekcja obecno\u015bci podczas uruchamiania" } } } \ No newline at end of file diff --git a/homeassistant/components/motion_blinds/translations/pl.json b/homeassistant/components/motion_blinds/translations/pl.json index 60277ba9360..6dfef34f4ab 100644 --- a/homeassistant/components/motion_blinds/translations/pl.json +++ b/homeassistant/components/motion_blinds/translations/pl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane, ustawienia po\u0142\u0105czenia zosta\u0142y zaktualizowane", + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", "already_in_progress": "Konfiguracja jest ju\u017c w toku", "connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia" }, diff --git a/homeassistant/components/shelly/translations/de.json b/homeassistant/components/shelly/translations/de.json index a8d8bdbdf99..6d4c0e92110 100644 --- a/homeassistant/components/shelly/translations/de.json +++ b/homeassistant/components/shelly/translations/de.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Verbindung fehlgeschlagen", + "firmware_not_fully_provisioned": "Das Ger\u00e4t ist nicht vollst\u00e4ndig eingerichtet. Bitte kontaktiere den Shelly Support", "invalid_auth": "Ung\u00fcltige Authentifizierung", "unknown": "Unerwarteter Fehler" }, diff --git a/homeassistant/components/shelly/translations/el.json b/homeassistant/components/shelly/translations/el.json index e83971e34fe..a5680f9343a 100644 --- a/homeassistant/components/shelly/translations/el.json +++ b/homeassistant/components/shelly/translations/el.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "firmware_not_fully_provisioned": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03c0\u03bb\u03ae\u03c1\u03c9\u03c2 \u03b5\u03c6\u03bf\u03b4\u03b9\u03b1\u03c3\u03bc\u03ad\u03bd\u03b7. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03c0\u03b9\u03ba\u03bf\u03b9\u03bd\u03c9\u03bd\u03ae\u03c3\u03c4\u03b5 \u03bc\u03b5 \u03c4\u03b7\u03bd \u03c5\u03c0\u03bf\u03c3\u03c4\u03ae\u03c1\u03b9\u03be\u03b7 \u03c4\u03b7\u03c2 Shelly", "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" }, diff --git a/homeassistant/components/shelly/translations/et.json b/homeassistant/components/shelly/translations/et.json index 7db0eaad4ac..e248ee9eba4 100644 --- a/homeassistant/components/shelly/translations/et.json +++ b/homeassistant/components/shelly/translations/et.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u00dchendamine nurjus", + "firmware_not_fully_provisioned": "Seade pole t\u00e4ielikult toetatud. V\u00f5ta \u00fchendust Shelly toega", "invalid_auth": "Tuvastamine nurjus", "unknown": "Tundmatu viga" }, diff --git a/homeassistant/components/shelly/translations/zh-Hant.json b/homeassistant/components/shelly/translations/zh-Hant.json index 62d1bd18850..a2727fcc933 100644 --- a/homeassistant/components/shelly/translations/zh-Hant.json +++ b/homeassistant/components/shelly/translations/zh-Hant.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u9023\u7dda\u5931\u6557", + "firmware_not_fully_provisioned": "\u88dd\u7f6e\u4e26\u672a\u5b8c\u5168\u652f\u63f4\uff0c\u8acb\u806f\u7e6b Shelly \u5c0b\u6c42\u652f\u63f4", "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", "unknown": "\u672a\u9810\u671f\u932f\u8aa4" }, diff --git a/homeassistant/components/slack/translations/nl.json b/homeassistant/components/slack/translations/nl.json index 8022048798e..f531a126e9b 100644 --- a/homeassistant/components/slack/translations/nl.json +++ b/homeassistant/components/slack/translations/nl.json @@ -19,7 +19,7 @@ "data_description": { "api_key": "Het Slack API-token dat moet worden gebruikt voor het verzenden van Slack-berichten.", "default_channel": "Het kanaal waarnaar moet worden gepost als er geen kanaal is opgegeven bij het verzenden van een bericht.", - "icon": "Gebruik een van de Slack emoji's als pictogram voor de opgegeven gebruikersnaam.", + "icon": "Gebruik een van de Slack emoji's als icoon voor de opgegeven gebruikersnaam.", "username": "Home Assistant plaatst berichten op Slack met de opgegeven gebruikersnaam." }, "description": "Raadpleeg de documentatie over het verkrijgen van uw Slack API-sleutel." diff --git a/homeassistant/components/slack/translations/pl.json b/homeassistant/components/slack/translations/pl.json index 3ce8f4bb311..fd1cb508e4a 100644 --- a/homeassistant/components/slack/translations/pl.json +++ b/homeassistant/components/slack/translations/pl.json @@ -12,7 +12,12 @@ "user": { "data": { "api_key": "Klucz API", + "default_channel": "Domy\u015blny kana\u0142", + "icon": "Ikona", "username": "Nazwa u\u017cytkownika" + }, + "data_description": { + "api_key": "Token API Slack u\u017cywany do wysy\u0142ania wiadomo\u015bci Slack." } } } diff --git a/homeassistant/components/somfy_mylink/translations/nl.json b/homeassistant/components/somfy_mylink/translations/nl.json index 43c71140cb9..6fae9b7404a 100644 --- a/homeassistant/components/somfy_mylink/translations/nl.json +++ b/homeassistant/components/somfy_mylink/translations/nl.json @@ -27,13 +27,13 @@ "step": { "init": { "data": { - "target_id": "Configureer opties voor een rolluik." + "target_id": "Configureer opties voor een afdekking." }, "title": "Configureer MyLink-opties" }, "target_config": { "data": { - "reverse": "Rolluik is omgekeerd" + "reverse": "Afdekking is omgekeerd" }, "description": "Configureer opties voor ' {target_name} '", "title": "Configureer MyLink Cover" diff --git a/homeassistant/components/yolink/translations/el.json b/homeassistant/components/yolink/translations/el.json new file mode 100644 index 00000000000..87f7c0d4d41 --- /dev/null +++ b/homeassistant/components/yolink/translations/el.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af", + "already_in_progress": "\u0397 \u03c1\u03bf\u03ae \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7\u03c2 \u03b2\u03c1\u03af\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03ae\u03b4\u03b7 \u03c3\u03b5 \u03b5\u03be\u03ad\u03bb\u03b9\u03be\u03b7", + "authorize_url_timeout": "\u039b\u03ae\u03be\u03b7 \u03c7\u03c1\u03bf\u03bd\u03b9\u03ba\u03bf\u03cd \u03bf\u03c1\u03af\u03bf\u03c5 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1\u03c2 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7\u03c2 URL \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2.", + "missing_configuration": "\u03a4\u03bf \u03c3\u03c4\u03bf\u03b9\u03c7\u03b5\u03af\u03bf \u03b4\u03b5\u03bd \u03ad\u03c7\u03b5\u03b9 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c4\u03b5\u03ba\u03bc\u03b7\u03c1\u03af\u03c9\u03c3\u03b7.", + "no_url_available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03b4\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 URL. \u0393\u03b9\u03b1 \u03c0\u03bb\u03b7\u03c1\u03bf\u03c6\u03bf\u03c1\u03af\u03b5\u03c2 \u03c3\u03c7\u03b5\u03c4\u03b9\u03ba\u03ac \u03bc\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1, [\u03b5\u03bb\u03ad\u03b3\u03be\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03cc\u03c4\u03b7\u03c4\u03b1 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1\u03c2] ( {docs_url} )", + "oauth_error": "\u039b\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03bc\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b1 \u03b4\u03b5\u03b4\u03bf\u03bc\u03ad\u03bd\u03b1 \u03b4\u03b9\u03b1\u03ba\u03c1\u03b9\u03c4\u03b9\u03ba\u03bf\u03cd.", + "reauth_successful": "\u039f \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2" + }, + "create_entry": { + "default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "step": { + "pick_implementation": { + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03bc\u03b5\u03b8\u03cc\u03b4\u03bf\u03c5 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 yolink \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03bb\u03ad\u03b3\u03be\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03c4\u03b7\u03bd \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1 \u03c4\u03bf\u03c5 \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03bf\u03cd \u03c3\u03b1\u03c2", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zone/translations/nl.json b/homeassistant/components/zone/translations/nl.json index 6dcf565ada6..021fbfb837c 100644 --- a/homeassistant/components/zone/translations/nl.json +++ b/homeassistant/components/zone/translations/nl.json @@ -6,7 +6,7 @@ "step": { "init": { "data": { - "icon": "Pictogram", + "icon": "Icoon", "latitude": "Breedtegraad", "longitude": "Lengtegraad", "name": "Naam", diff --git a/homeassistant/components/zwave_js/translations/nl.json b/homeassistant/components/zwave_js/translations/nl.json index 53bf45e2f4d..69481c2a98c 100644 --- a/homeassistant/components/zwave_js/translations/nl.json +++ b/homeassistant/components/zwave_js/translations/nl.json @@ -77,7 +77,7 @@ }, "condition_type": { "config_parameter": "Config parameter {subtype} waarde", - "node_status": "Knooppuntstatus", + "node_status": "Apparaatstatus", "value": "Huidige waarde van een Z-Wave-waarde" }, "trigger_type": { @@ -86,7 +86,7 @@ "event.value_notification.basic": "Basis CC-evenement op {subtype}", "event.value_notification.central_scene": "Centrale Sc\u00e8ne actie op {subtype}", "event.value_notification.scene_activation": "Sc\u00e8ne-activering op {subtype}", - "state.node_status": "Knooppuntstatus gewijzigd", + "state.node_status": "Apparaatstatus gewijzigd", "zwave_js.value_updated.config_parameter": "Waardeverandering op configuratieparameter {subtype}", "zwave_js.value_updated.value": "Waardeverandering op een Z-Wave JS-waarde" } From 5c2c6026864b05750e0a320c6797060e97a2694f Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Thu, 19 May 2022 21:34:58 -0400 Subject: [PATCH 637/930] Bumps pyunifiprotect to 3.6.0 (#72188) --- homeassistant/components/unifiprotect/button.py | 2 +- homeassistant/components/unifiprotect/camera.py | 3 +-- homeassistant/components/unifiprotect/config_flow.py | 2 +- homeassistant/components/unifiprotect/const.py | 2 +- homeassistant/components/unifiprotect/data.py | 3 ++- homeassistant/components/unifiprotect/entity.py | 2 +- homeassistant/components/unifiprotect/lock.py | 3 +-- homeassistant/components/unifiprotect/manifest.json | 2 +- homeassistant/components/unifiprotect/number.py | 2 +- homeassistant/components/unifiprotect/select.py | 3 ++- homeassistant/components/unifiprotect/switch.py | 8 ++++++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/unifiprotect/conftest.py | 2 +- tests/components/unifiprotect/test_camera.py | 4 +--- tests/components/unifiprotect/test_config_flow.py | 2 +- tests/components/unifiprotect/test_lock.py | 3 +-- tests/components/unifiprotect/test_select.py | 11 +++++++---- tests/components/unifiprotect/test_sensor.py | 10 ++++++++-- tests/components/unifiprotect/test_switch.py | 9 +++++++-- 20 files changed, 46 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/unifiprotect/button.py b/homeassistant/components/unifiprotect/button.py index 731b1eaf86a..fe82c0afbe0 100644 --- a/homeassistant/components/unifiprotect/button.py +++ b/homeassistant/components/unifiprotect/button.py @@ -4,7 +4,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import Final -from pyunifiprotect.data.base import ProtectAdoptableDeviceModel +from pyunifiprotect.data import ProtectAdoptableDeviceModel from homeassistant.components.button import ( ButtonDeviceClass, diff --git a/homeassistant/components/unifiprotect/camera.py b/homeassistant/components/unifiprotect/camera.py index 928d82f317b..ca076e490a2 100644 --- a/homeassistant/components/unifiprotect/camera.py +++ b/homeassistant/components/unifiprotect/camera.py @@ -5,8 +5,7 @@ from collections.abc import Generator import logging from pyunifiprotect.api import ProtectApiClient -from pyunifiprotect.data import Camera as UFPCamera, StateType -from pyunifiprotect.data.devices import CameraChannel +from pyunifiprotect.data import Camera as UFPCamera, CameraChannel, StateType from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/unifiprotect/config_flow.py b/homeassistant/components/unifiprotect/config_flow.py index 27108d24eaa..daaae214df9 100644 --- a/homeassistant/components/unifiprotect/config_flow.py +++ b/homeassistant/components/unifiprotect/config_flow.py @@ -6,7 +6,7 @@ from typing import Any from aiohttp import CookieJar from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient -from pyunifiprotect.data.nvr import NVR +from pyunifiprotect.data import NVR import voluptuous as vol from homeassistant import config_entries diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index 7d2842977b6..0fe4ca98afa 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -1,6 +1,6 @@ """Constant definitions for UniFi Protect Integration.""" -from pyunifiprotect.data.types import ModelType, Version +from pyunifiprotect.data import ModelType, Version from homeassistant.const import Platform diff --git a/homeassistant/components/unifiprotect/data.py b/homeassistant/components/unifiprotect/data.py index 371c1c7831b..e20f956acd2 100644 --- a/homeassistant/components/unifiprotect/data.py +++ b/homeassistant/components/unifiprotect/data.py @@ -12,9 +12,10 @@ from pyunifiprotect.data import ( Event, Liveview, ModelType, + ProtectAdoptableDeviceModel, + ProtectDeviceModel, WSSubscriptionMessage, ) -from pyunifiprotect.data.base import ProtectAdoptableDeviceModel, ProtectDeviceModel from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback diff --git a/homeassistant/components/unifiprotect/entity.py b/homeassistant/components/unifiprotect/entity.py index 045a9c4fd6d..45e52db5963 100644 --- a/homeassistant/components/unifiprotect/entity.py +++ b/homeassistant/components/unifiprotect/entity.py @@ -6,6 +6,7 @@ import logging from typing import Any from pyunifiprotect.data import ( + NVR, Camera, Doorlock, Event, @@ -16,7 +17,6 @@ from pyunifiprotect.data import ( StateType, Viewer, ) -from pyunifiprotect.data.nvr import NVR from homeassistant.core import callback import homeassistant.helpers.device_registry as dr diff --git a/homeassistant/components/unifiprotect/lock.py b/homeassistant/components/unifiprotect/lock.py index 6fb70a2523f..c4d56dd1e71 100644 --- a/homeassistant/components/unifiprotect/lock.py +++ b/homeassistant/components/unifiprotect/lock.py @@ -4,8 +4,7 @@ from __future__ import annotations import logging from typing import Any -from pyunifiprotect.data import Doorlock -from pyunifiprotect.data.types import LockStatusType +from pyunifiprotect.data import Doorlock, LockStatusType from homeassistant.components.lock import LockEntity, LockEntityDescription from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/unifiprotect/manifest.json b/homeassistant/components/unifiprotect/manifest.json index 36b6011e8fc..898abd73a6f 100644 --- a/homeassistant/components/unifiprotect/manifest.json +++ b/homeassistant/components/unifiprotect/manifest.json @@ -3,7 +3,7 @@ "name": "UniFi Protect", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifiprotect", - "requirements": ["pyunifiprotect==3.5.1", "unifi-discovery==1.1.3"], + "requirements": ["pyunifiprotect==3.6.0", "unifi-discovery==1.1.3"], "dependencies": ["http"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "quality_scale": "platinum", diff --git a/homeassistant/components/unifiprotect/number.py b/homeassistant/components/unifiprotect/number.py index d6dfba0c38f..58ae0502caa 100644 --- a/homeassistant/components/unifiprotect/number.py +++ b/homeassistant/components/unifiprotect/number.py @@ -4,7 +4,7 @@ from __future__ import annotations from dataclasses import dataclass from datetime import timedelta -from pyunifiprotect.data.devices import Camera, Doorlock, Light +from pyunifiprotect.data import Camera, Doorlock, Light from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index f0500ea54e5..2c6c5fa4cc6 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -11,17 +11,18 @@ from typing import Any, Final from pyunifiprotect.api import ProtectApiClient from pyunifiprotect.data import ( Camera, + ChimeType, DoorbellMessageType, Doorlock, IRLEDMode, Light, LightModeEnableType, LightModeType, + MountType, RecordingMode, Sensor, Viewer, ) -from pyunifiprotect.data.types import ChimeType, MountType import voluptuous as vol from homeassistant.components.select import SelectEntity, SelectEntityDescription diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index 6051e4e596f..971c637a8c2 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -5,8 +5,12 @@ from dataclasses import dataclass import logging from typing import Any -from pyunifiprotect.data import Camera, RecordingMode, VideoMode -from pyunifiprotect.data.base import ProtectAdoptableDeviceModel +from pyunifiprotect.data import ( + Camera, + ProtectAdoptableDeviceModel, + RecordingMode, + VideoMode, +) from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription from homeassistant.config_entries import ConfigEntry diff --git a/requirements_all.txt b/requirements_all.txt index 55765f9d139..4ac46752539 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1984,7 +1984,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.5.1 +pyunifiprotect==3.6.0 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index fdbe51fbb49..0327dcfa142 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1313,7 +1313,7 @@ pytrafikverket==0.2.0.1 pyudev==0.22.0 # homeassistant.components.unifiprotect -pyunifiprotect==3.5.1 +pyunifiprotect==3.6.0 # homeassistant.components.uptimerobot pyuptimerobot==22.2.0 diff --git a/tests/components/unifiprotect/conftest.py b/tests/components/unifiprotect/conftest.py index f495b2bc8f7..c83c6eb72cc 100644 --- a/tests/components/unifiprotect/conftest.py +++ b/tests/components/unifiprotect/conftest.py @@ -17,11 +17,11 @@ from pyunifiprotect.data import ( Doorlock, Light, Liveview, + ProtectAdoptableDeviceModel, Sensor, Viewer, WSSubscriptionMessage, ) -from pyunifiprotect.data.base import ProtectAdoptableDeviceModel from homeassistant.components.unifiprotect.const import DOMAIN from homeassistant.const import Platform diff --git a/tests/components/unifiprotect/test_camera.py b/tests/components/unifiprotect/test_camera.py index 362481bfb03..d7fc2a62325 100644 --- a/tests/components/unifiprotect/test_camera.py +++ b/tests/components/unifiprotect/test_camera.py @@ -6,9 +6,7 @@ from copy import copy from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import Camera as ProtectCamera -from pyunifiprotect.data.devices import CameraChannel -from pyunifiprotect.data.types import StateType +from pyunifiprotect.data import Camera as ProtectCamera, CameraChannel, StateType from pyunifiprotect.exceptions import NvrError from homeassistant.components.camera import ( diff --git a/tests/components/unifiprotect/test_config_flow.py b/tests/components/unifiprotect/test_config_flow.py index 59304ce915f..80e845591b1 100644 --- a/tests/components/unifiprotect/test_config_flow.py +++ b/tests/components/unifiprotect/test_config_flow.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest from pyunifiprotect import NotAuthorized, NvrError -from pyunifiprotect.data.nvr import NVR +from pyunifiprotect.data import NVR from homeassistant import config_entries from homeassistant.components import dhcp, ssdp diff --git a/tests/components/unifiprotect/test_lock.py b/tests/components/unifiprotect/test_lock.py index 740cb61b3c4..0a02fcb22a4 100644 --- a/tests/components/unifiprotect/test_lock.py +++ b/tests/components/unifiprotect/test_lock.py @@ -6,8 +6,7 @@ from copy import copy from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import Doorlock -from pyunifiprotect.data.types import LockStatusType +from pyunifiprotect.data import Doorlock, LockStatusType from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.const import ( diff --git a/tests/components/unifiprotect/test_select.py b/tests/components/unifiprotect/test_select.py index d09fa421ec3..fc0abbe29ca 100644 --- a/tests/components/unifiprotect/test_select.py +++ b/tests/components/unifiprotect/test_select.py @@ -7,16 +7,19 @@ from datetime import timedelta from unittest.mock import AsyncMock, Mock, patch import pytest -from pyunifiprotect.data import Camera, Light -from pyunifiprotect.data.devices import LCDMessage, Viewer -from pyunifiprotect.data.nvr import DoorbellMessage, Liveview -from pyunifiprotect.data.types import ( +from pyunifiprotect.data import ( + Camera, DoorbellMessageType, IRLEDMode, + LCDMessage, + Light, LightModeEnableType, LightModeType, + Liveview, RecordingMode, + Viewer, ) +from pyunifiprotect.data.nvr import DoorbellMessage from homeassistant.components.select.const import ATTR_OPTIONS from homeassistant.components.unifiprotect.const import ( diff --git a/tests/components/unifiprotect/test_sensor.py b/tests/components/unifiprotect/test_sensor.py index 971ee47405b..dff746c167f 100644 --- a/tests/components/unifiprotect/test_sensor.py +++ b/tests/components/unifiprotect/test_sensor.py @@ -7,10 +7,16 @@ from datetime import datetime, timedelta from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import NVR, Camera, Event, Sensor +from pyunifiprotect.data import ( + NVR, + Camera, + Event, + EventType, + Sensor, + SmartDetectObjectType, +) from pyunifiprotect.data.base import WifiConnectionState, WiredConnectionState from pyunifiprotect.data.nvr import EventMetadata -from pyunifiprotect.data.types import EventType, SmartDetectObjectType from homeassistant.components.unifiprotect.const import ( ATTR_EVENT_SCORE, diff --git a/tests/components/unifiprotect/test_switch.py b/tests/components/unifiprotect/test_switch.py index 498f4c6b3b7..7918ea0b6cf 100644 --- a/tests/components/unifiprotect/test_switch.py +++ b/tests/components/unifiprotect/test_switch.py @@ -5,8 +5,13 @@ from __future__ import annotations from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import Camera, Light -from pyunifiprotect.data.types import RecordingMode, SmartDetectObjectType, VideoMode +from pyunifiprotect.data import ( + Camera, + Light, + RecordingMode, + SmartDetectObjectType, + VideoMode, +) from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.components.unifiprotect.switch import ( From 1001f9e39a3f2204f566337a7750825eabd5f65d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 19 May 2022 22:58:32 -0500 Subject: [PATCH 638/930] Fix last state in history minimal respones when all the states at the end are skipped (#72203) --- homeassistant/components/recorder/history.py | 8 ---- tests/components/history/test_init.py | 44 +++++++++++++------- tests/components/recorder/test_history.py | 33 +++++++++------ 3 files changed, 49 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 3df444faccc..845a2af62bf 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -695,8 +695,6 @@ def _sorted_states_to_dict( prev_state = first_state.state ent_results.append(state_class(first_state, attr_cache)) - initial_state_count = len(ent_results) - row = None for row in group: # With minimal response we do not care about attribute # changes so we can filter out duplicate states @@ -716,12 +714,6 @@ def _sorted_states_to_dict( ) prev_state = state - if row and len(ent_results) != initial_state_count: - # There was at least one state change - # replace the last minimal state with - # a full state - ent_results[-1] = state_class(row, attr_cache) - # If there are no states beyond the initial state, # the state a was never popped from initial_states for ent_id, row in initial_states.items(): diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index 0425c9bc2e7..a2626ab2004 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -61,23 +61,30 @@ def test_get_significant_states_minimal_response(hass_history): hist = get_significant_states( hass, zero, four, filters=history.Filters(), minimal_response=True ) + entites_with_reducable_states = [ + "media_player.test", + "media_player.test3", + ] - # The second media_player.test state is reduced + # All states for media_player.test state are reduced # down to last_changed and state when minimal_response + # is set except for the first state. # is set. We use JSONEncoder to make sure that are # pre-encoded last_changed is always the same as what # will happen with encoding a native state - input_state = states["media_player.test"][1] - orig_last_changed = json.dumps( - process_timestamp(input_state.last_changed), - cls=JSONEncoder, - ).replace('"', "") - orig_state = input_state.state - states["media_player.test"][1] = { - "last_changed": orig_last_changed, - "state": orig_state, - } - + for entity_id in entites_with_reducable_states: + entity_states = states[entity_id] + for state_idx in range(1, len(entity_states)): + input_state = entity_states[state_idx] + orig_last_changed = orig_last_changed = json.dumps( + process_timestamp(input_state.last_changed), + cls=JSONEncoder, + ).replace('"', "") + orig_state = input_state.state + entity_states[state_idx] = { + "last_changed": orig_last_changed, + "state": orig_state, + } assert states == hist @@ -616,6 +623,9 @@ async def test_fetch_period_api_with_minimal_response(hass, recorder_mock, hass_ hass.states.async_set("sensor.power", 50, {"attr": "any"}) await async_wait_recording_done(hass) hass.states.async_set("sensor.power", 23, {"attr": "any"}) + last_changed = hass.states.get("sensor.power").last_changed + await async_wait_recording_done(hass) + hass.states.async_set("sensor.power", 23, {"attr": "any"}) await async_wait_recording_done(hass) client = await hass_client() response = await client.get( @@ -634,9 +644,13 @@ async def test_fetch_period_api_with_minimal_response(hass, recorder_mock, hass_ assert "entity_id" not in state_list[1] assert state_list[1]["state"] == "50" - assert state_list[2]["entity_id"] == "sensor.power" - assert state_list[2]["attributes"] == {} + assert "attributes" not in state_list[2] + assert "entity_id" not in state_list[2] assert state_list[2]["state"] == "23" + assert state_list[2]["last_changed"] == json.dumps( + process_timestamp(last_changed), + cls=JSONEncoder, + ).replace('"', "") async def test_fetch_period_api_with_no_timestamp(hass, hass_client, recorder_mock): @@ -1131,7 +1145,7 @@ async def test_history_during_period(hass, hass_ws_client, recorder_mock): assert "lc" not in sensor_test_history[1] # skipped if the same a last_updated (lu) assert sensor_test_history[2]["s"] == "on" - assert sensor_test_history[2]["a"] == {} + assert "a" not in sensor_test_history[2] await client.send_json( { diff --git a/tests/components/recorder/test_history.py b/tests/components/recorder/test_history.py index 1d59893745b..da6c3a8af35 100644 --- a/tests/components/recorder/test_history.py +++ b/tests/components/recorder/test_history.py @@ -388,23 +388,30 @@ def test_get_significant_states_minimal_response(hass_recorder): hass = hass_recorder() zero, four, states = record_states(hass) hist = history.get_significant_states(hass, zero, four, minimal_response=True) + entites_with_reducable_states = [ + "media_player.test", + "media_player.test3", + ] - # The second media_player.test state is reduced + # All states for media_player.test state are reduced # down to last_changed and state when minimal_response + # is set except for the first state. # is set. We use JSONEncoder to make sure that are # pre-encoded last_changed is always the same as what # will happen with encoding a native state - input_state = states["media_player.test"][1] - orig_last_changed = json.dumps( - process_timestamp(input_state.last_changed), - cls=JSONEncoder, - ).replace('"', "") - orig_state = input_state.state - states["media_player.test"][1] = { - "last_changed": orig_last_changed, - "state": orig_state, - } - + for entity_id in entites_with_reducable_states: + entity_states = states[entity_id] + for state_idx in range(1, len(entity_states)): + input_state = entity_states[state_idx] + orig_last_changed = orig_last_changed = json.dumps( + process_timestamp(input_state.last_changed), + cls=JSONEncoder, + ).replace('"', "") + orig_state = input_state.state + entity_states[state_idx] = { + "last_changed": orig_last_changed, + "state": orig_state, + } assert states == hist @@ -565,7 +572,7 @@ def test_get_significant_states_only(hass_recorder): assert states == hist[entity_id] -def record_states(hass): +def record_states(hass) -> tuple[datetime, datetime, dict[str, list[State]]]: """Record some test states. We inject a bunch of state updates from media player, zone and From 36e9088e6b50686db13e4562f628ef15ebd98826 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 20 May 2022 06:22:15 +0200 Subject: [PATCH 639/930] Allow frontend to store metadata in homeassistant scenes (#72178) --- homeassistant/components/homeassistant/scene.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/homeassistant/scene.py b/homeassistant/components/homeassistant/scene.py index bf8affd902e..21c364ba65b 100644 --- a/homeassistant/components/homeassistant/scene.py +++ b/homeassistant/components/homeassistant/scene.py @@ -101,6 +101,7 @@ PLATFORM_SCHEMA = vol.Schema( vol.Required(CONF_NAME): cv.string, vol.Optional(CONF_ICON): cv.icon, vol.Required(CONF_ENTITIES): STATES_SCHEMA, + vol.Optional("metadata"): dict, } ) ], From c028db00de35ac4d758e9690562402bd5ca5c6c4 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Thu, 19 May 2022 21:22:37 -0700 Subject: [PATCH 640/930] Add Neato application credentials platform and deprecate configuration.yaml (#72175) --- homeassistant/components/neato/__init__.py | 44 ++++++++++++------- homeassistant/components/neato/api.py | 3 +- .../neato/application_credentials.py | 28 ++++++++++++ homeassistant/components/neato/manifest.json | 2 +- .../generated/application_credentials.py | 1 + tests/components/neato/test_config_flow.py | 2 - 6 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 homeassistant/components/neato/application_credentials.py diff --git a/homeassistant/components/neato/__init__.py b/homeassistant/components/neato/__init__.py index c1e193b7406..cbfd860a0b1 100644 --- a/homeassistant/components/neato/__init__.py +++ b/homeassistant/components/neato/__init__.py @@ -2,10 +2,14 @@ import logging import aiohttp -from pybotvac import Account, Neato +from pybotvac import Account from pybotvac.exceptions import NeatoException import voluptuous as vol +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_TOKEN, Platform from homeassistant.core import HomeAssistant @@ -13,7 +17,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import config_entry_oauth2_flow, config_validation as cv from homeassistant.helpers.typing import ConfigType -from . import api, config_flow +from . import api from .const import NEATO_CONFIG, NEATO_DOMAIN, NEATO_LOGIN from .hub import NeatoHub @@ -21,14 +25,17 @@ _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( - { - NEATO_DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(NEATO_DOMAIN), + { + NEATO_DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -43,18 +50,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True hass.data[NEATO_CONFIG] = config[NEATO_DOMAIN] - vendor = Neato() - config_flow.OAuth2FlowHandler.async_register_implementation( + await async_import_client_credential( hass, - api.NeatoImplementation( - hass, - NEATO_DOMAIN, + NEATO_DOMAIN, + ClientCredential( config[NEATO_DOMAIN][CONF_CLIENT_ID], config[NEATO_DOMAIN][CONF_CLIENT_SECRET], - vendor.auth_endpoint, - vendor.token_endpoint, ), ) + _LOGGER.warning( + "Configuration of Neato integration in YAML is deprecated and " + "will be removed in a future release; Your existing OAuth " + "Application Credentials have been imported into the UI " + "automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/neato/api.py b/homeassistant/components/neato/api.py index cd26b009040..f3a4324b7ed 100644 --- a/homeassistant/components/neato/api.py +++ b/homeassistant/components/neato/api.py @@ -7,6 +7,7 @@ from typing import Any import pybotvac from homeassistant import config_entries, core +from homeassistant.components.application_credentials import AuthImplementation from homeassistant.helpers import config_entry_oauth2_flow @@ -35,7 +36,7 @@ class ConfigEntryAuth(pybotvac.OAuthSession): # type: ignore[misc] return self.session.token["access_token"] # type: ignore[no-any-return] -class NeatoImplementation(config_entry_oauth2_flow.LocalOAuth2Implementation): +class NeatoImplementation(AuthImplementation): """Neato implementation of LocalOAuth2Implementation. We need this class because we have to add client_secret and scope to the authorization request. diff --git a/homeassistant/components/neato/application_credentials.py b/homeassistant/components/neato/application_credentials.py new file mode 100644 index 00000000000..2abdb6bcc81 --- /dev/null +++ b/homeassistant/components/neato/application_credentials.py @@ -0,0 +1,28 @@ +"""Application credentials platform for neato.""" + +from pybotvac import Neato + +from homeassistant.components.application_credentials import ( + AuthorizationServer, + ClientCredential, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from . import api + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return auth implementation for a custom auth implementation.""" + vendor = Neato() + return api.NeatoImplementation( + hass, + auth_domain, + credential, + AuthorizationServer( + authorize_url=vendor.auth_endpoint, + token_url=vendor.token_endpoint, + ), + ) diff --git a/homeassistant/components/neato/manifest.json b/homeassistant/components/neato/manifest.json index f12f79c77a8..64c10abec06 100644 --- a/homeassistant/components/neato/manifest.json +++ b/homeassistant/components/neato/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/neato", "requirements": ["pybotvac==0.0.23"], "codeowners": ["@dshokouhi", "@Santobert"], - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "iot_class": "cloud_polling", "loggers": ["pybotvac"] } diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 7521c5c30b8..41541943086 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -9,6 +9,7 @@ APPLICATION_CREDENTIALS = [ "geocaching", "google", "home_connect", + "neato", "netatmo", "spotify", "xbox", diff --git a/tests/components/neato/test_config_flow.py b/tests/components/neato/test_config_flow.py index 48bdf247f51..7e187f1e2fd 100644 --- a/tests/components/neato/test_config_flow.py +++ b/tests/components/neato/test_config_flow.py @@ -27,7 +27,6 @@ async def test_full_flow( "neato", { "neato": {"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, - "http": {"base_url": "https://example.com"}, }, ) @@ -99,7 +98,6 @@ async def test_reauth( "neato", { "neato": {"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, - "http": {"base_url": "https://example.com"}, }, ) From ba4031718d8ec3d983b7f4fc322e184d76d1ec8e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 06:25:29 +0200 Subject: [PATCH 641/930] Enforce application_credentials type hints (#72214) --- pylint/plugins/hass_enforce_type_hints.py | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index cb90499b6ca..6cf521addac 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -40,12 +40,16 @@ _MODULE_FILTERS: dict[str, re.Pattern] = { "any_platform": re.compile( f"^homeassistant\\.components\\.\\w+\\.({'|'.join([platform.value for platform in Platform])})$" ), + # application_credentials matches only in the package root (application_credentials.py) + "application_credentials": re.compile( + r"^homeassistant\.components\.\w+\.(application_credentials)$" + ), # device_tracker matches only in the package root (device_tracker.py) "device_tracker": re.compile(r"^homeassistant\.components\.\w+\.(device_tracker)$"), # diagnostics matches only in the package root (diagnostics.py) "diagnostics": re.compile(r"^homeassistant\.components\.\w+\.(diagnostics)$"), # config_flow matches only in the package root (config_flow.py) - "config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$") + "config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$"), } _METHOD_MATCH: list[TypeHintMatch] = [ @@ -135,6 +139,24 @@ _METHOD_MATCH: list[TypeHintMatch] = [ }, return_type=None, ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["application_credentials"], + function_name="async_get_auth_implementation", + arg_types={ + 0: "HomeAssistant", + 1: "str", + 2: "ClientCredential", + }, + return_type="AbstractOAuth2Implementation", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["application_credentials"], + function_name="async_get_authorization_server", + arg_types={ + 0: "HomeAssistant", + }, + return_type="AuthorizationServer", + ), TypeHintMatch( module_filter=_MODULE_FILTERS["device_tracker"], function_name="setup_scanner", From 7cad1571a2e046f1d1a991344503dd07a2b33385 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 20 May 2022 00:21:09 -0500 Subject: [PATCH 642/930] Describe hue events in the logbook (#72220) Co-authored-by: Paulus Schoutsen --- homeassistant/components/hue/logbook.py | 73 +++++++++++++++++ tests/components/hue/test_logbook.py | 104 ++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 homeassistant/components/hue/logbook.py create mode 100644 tests/components/hue/test_logbook.py diff --git a/homeassistant/components/hue/logbook.py b/homeassistant/components/hue/logbook.py new file mode 100644 index 00000000000..40abce7e2d3 --- /dev/null +++ b/homeassistant/components/hue/logbook.py @@ -0,0 +1,73 @@ +"""Describe hue logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.const import CONF_DEVICE_ID, CONF_EVENT, CONF_ID, CONF_TYPE +from homeassistant.core import Event, HomeAssistant, callback +from homeassistant.helpers import device_registry as dr + +from .const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN + +TRIGGER_SUBTYPE = { + "button_1": "first button", + "button_2": "second button", + "button_3": "third button", + "button_4": "fourth button", + "double_buttons_1_3": "first and third buttons", + "double_buttons_2_4": "second and fourth buttons", + "dim_down": "dim down", + "dim_up": "dim up", + "turn_off": "turn off", + "turn_on": "turn on", + "1": "first button", + "2": "second button", + "3": "third button", + "4": "fourth button", +} +TRIGGER_TYPE = { + "remote_button_long_release": "{subtype} released after long press", + "remote_button_short_press": "{subtype} pressed", + "remote_button_short_release": "{subtype} released", + "remote_double_button_long_press": "both {subtype} released after long press", + "remote_double_button_short_press": "both {subtype} released", + "initial_press": "{subtype} pressed initially", + "repeat": "{subtype} held down", + "short_release": "{subtype} released after short press", + "long_release": "{subtype} released after long press", + "double_short_release": "both {subtype} released", +} + +UNKNOWN_TYPE = "unknown type" +UNKNOWN_SUB_TYPE = "unknown sub type" + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe hue logbook events.""" + + @callback + def async_describe_hue_event(event: Event) -> dict[str, str]: + """Describe hue logbook event.""" + data = event.data + name: str | None = None + if dev_ent := dr.async_get(hass).async_get(data[CONF_DEVICE_ID]): + name = dev_ent.name + if name is None: + name = data[CONF_ID] + if CONF_TYPE in data: # v2 + subtype = TRIGGER_SUBTYPE.get(str(data[CONF_SUBTYPE]), UNKNOWN_SUB_TYPE) + message = TRIGGER_TYPE.get(data[CONF_TYPE], UNKNOWN_TYPE).format( + subtype=subtype + ) + else: + message = f"Event {data[CONF_EVENT]}" # v1 + return { + "name": name, + "message": str(message), + } + + async_describe_event(DOMAIN, ATTR_HUE_EVENT, async_describe_hue_event) diff --git a/tests/components/hue/test_logbook.py b/tests/components/hue/test_logbook.py new file mode 100644 index 00000000000..fb7934da126 --- /dev/null +++ b/tests/components/hue/test_logbook.py @@ -0,0 +1,104 @@ +"""The tests for hue logbook.""" + +from homeassistant.components.hue.const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN +from homeassistant.components.hue.v1.hue_event import CONF_LAST_UPDATED +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_DEVICE_ID, + CONF_EVENT, + CONF_ID, + CONF_TYPE, + CONF_UNIQUE_ID, +) +from homeassistant.helpers import device_registry +from homeassistant.setup import async_setup_component + +from .conftest import setup_platform + +from tests.components.logbook.common import MockRow, mock_humanify + +# v1 event +SAMPLE_V1_EVENT = { + CONF_DEVICE_ID: "fe346f17a9f8c15be633f9cc3f3d6631", + CONF_EVENT: 18, + CONF_ID: "hue_tap", + CONF_LAST_UPDATED: "2019-12-28T22:58:03", + CONF_UNIQUE_ID: "00:00:00:00:00:44:23:08-f2", +} +# v2 event +SAMPLE_V2_EVENT = { + CONF_DEVICE_ID: "f974028e7933aea703a2199a855bc4a3", + CONF_ID: "wall_switch_with_2_controls_button", + CONF_SUBTYPE: 1, + CONF_TYPE: "initial_press", + CONF_UNIQUE_ID: "c658d3d8-a013-4b81-8ac6-78b248537e70", +} + + +async def test_humanify_hue_events(hass, mock_bridge_v2): + """Test hue events when the devices are present in the registry.""" + await setup_platform(hass, mock_bridge_v2, "sensor") + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + await hass.async_block_till_done() + entry: ConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] + + dev_reg = device_registry.async_get(hass) + v1_device = dev_reg.async_get_or_create( + identifiers={(DOMAIN, "v1")}, name="Remote 1", config_entry_id=entry.entry_id + ) + v2_device = dev_reg.async_get_or_create( + identifiers={(DOMAIN, "v2")}, name="Remote 2", config_entry_id=entry.entry_id + ) + + (v1_event, v2_event) = mock_humanify( + hass, + [ + MockRow( + ATTR_HUE_EVENT, + {**SAMPLE_V1_EVENT, CONF_DEVICE_ID: v1_device.id}, + ), + MockRow( + ATTR_HUE_EVENT, + {**SAMPLE_V2_EVENT, CONF_DEVICE_ID: v2_device.id}, + ), + ], + ) + + assert v1_event["name"] == "Remote 1" + assert v1_event["domain"] == DOMAIN + assert v1_event["message"] == "Event 18" + + assert v2_event["name"] == "Remote 2" + assert v2_event["domain"] == DOMAIN + assert v2_event["message"] == "first button pressed initially" + + +async def test_humanify_hue_events_devices_removed(hass, mock_bridge_v2): + """Test hue events when the devices have been removed from the registry.""" + await setup_platform(hass, mock_bridge_v2, "sensor") + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + await hass.async_block_till_done() + + (v1_event, v2_event) = mock_humanify( + hass, + [ + MockRow( + ATTR_HUE_EVENT, + SAMPLE_V1_EVENT, + ), + MockRow( + ATTR_HUE_EVENT, + SAMPLE_V2_EVENT, + ), + ], + ) + + assert v1_event["name"] == "hue_tap" + assert v1_event["domain"] == DOMAIN + assert v1_event["message"] == "Event 18" + + assert v2_event["name"] == "wall_switch_with_2_controls_button" + assert v2_event["domain"] == DOMAIN + assert v2_event["message"] == "first button pressed initially" From 5f7594268a3137b1fe140cd4ba7a2d9246cb3b6c Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Fri, 20 May 2022 01:50:13 -0400 Subject: [PATCH 643/930] Switch zwave_js firmware upload view to use device ID (#72219) * Switch zwave_js firmware upload view to use device ID * Store device registry in view --- homeassistant/components/zwave_js/api.py | 38 ++++++++++++++++-------- tests/components/zwave_js/test_api.py | 36 ++++++++++++---------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index d27541fc61c..66c497a791f 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -57,7 +57,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import Unauthorized from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.device_registry import DeviceEntry +import homeassistant.helpers.device_registry as dr from homeassistant.helpers.dispatcher import async_dispatcher_connect from .config_validation import BITMASK_SCHEMA @@ -607,7 +607,7 @@ async def websocket_add_node( ) @callback - def device_registered(device: DeviceEntry) -> None: + def device_registered(device: dr.DeviceEntry) -> None: device_details = { "name": device.name, "id": device.id, @@ -1108,7 +1108,7 @@ async def websocket_replace_failed_node( ) @callback - def device_registered(device: DeviceEntry) -> None: + def device_registered(device: dr.DeviceEntry) -> None: device_details = { "name": device.name, "id": device.id, @@ -1819,25 +1819,37 @@ async def websocket_subscribe_firmware_update_status( class FirmwareUploadView(HomeAssistantView): """View to upload firmware.""" - url = r"/api/zwave_js/firmware/upload/{config_entry_id}/{node_id:\d+}" + url = r"/api/zwave_js/firmware/upload/{device_id}" name = "api:zwave_js:firmware:upload" - async def post( - self, request: web.Request, config_entry_id: str, node_id: str - ) -> web.Response: + def __init__(self) -> None: + """Initialize view.""" + super().__init__() + self._dev_reg: dr.DeviceRegistry | None = None + + async def post(self, request: web.Request, device_id: str) -> web.Response: """Handle upload.""" if not request["hass_user"].is_admin: raise Unauthorized() hass = request.app["hass"] - if config_entry_id not in hass.data[DOMAIN]: - raise web_exceptions.HTTPBadRequest - entry = hass.config_entries.async_get_entry(config_entry_id) - client: Client = hass.data[DOMAIN][config_entry_id][DATA_CLIENT] - node = client.driver.controller.nodes.get(int(node_id)) - if not node: + try: + node = async_get_node_from_device_id(hass, device_id) + except ValueError as err: + if "not loaded" in err.args[0]: + raise web_exceptions.HTTPBadRequest raise web_exceptions.HTTPNotFound + if not self._dev_reg: + self._dev_reg = dr.async_get(hass) + device = self._dev_reg.async_get(device_id) + assert device + entry = next( + entry + for entry in hass.config_entries.async_entries(DOMAIN) + if entry.entry_id in device.config_entries + ) + # Increase max payload request._client_max_size = 1024 * 1024 * 10 # pylint: disable=protected-access diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 60b83630add..3d491b98f93 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -2661,11 +2661,12 @@ async def test_firmware_upload_view( ): """Test the HTTP firmware upload view.""" client = await hass_client() + device = get_device(hass, multisensor_6) with patch( "homeassistant.components.zwave_js.api.begin_firmware_update", ) as mock_cmd: resp = await client.post( - f"/api/zwave_js/firmware/upload/{integration.entry_id}/{multisensor_6.node_id}", + f"/api/zwave_js/firmware/upload/{device.id}", data={"file": firmware_file}, ) assert mock_cmd.call_args[0][1:4] == (multisensor_6, "file", bytes(10)) @@ -2677,12 +2678,13 @@ async def test_firmware_upload_view_failed_command( ): """Test failed command for the HTTP firmware upload view.""" client = await hass_client() + device = get_device(hass, multisensor_6) with patch( "homeassistant.components.zwave_js.api.begin_firmware_update", side_effect=FailedCommand("test", "test"), ): resp = await client.post( - f"/api/zwave_js/firmware/upload/{integration.entry_id}/{multisensor_6.node_id}", + f"/api/zwave_js/firmware/upload/{device.id}", data={"file": firmware_file}, ) assert resp.status == HTTPStatus.BAD_REQUEST @@ -2692,9 +2694,10 @@ async def test_firmware_upload_view_invalid_payload( hass, multisensor_6, integration, hass_client ): """Test an invalid payload for the HTTP firmware upload view.""" + device = get_device(hass, multisensor_6) client = await hass_client() resp = await client.post( - f"/api/zwave_js/firmware/upload/{integration.entry_id}/{multisensor_6.node_id}", + f"/api/zwave_js/firmware/upload/{device.id}", data={"wrong_key": bytes(10)}, ) assert resp.status == HTTPStatus.BAD_REQUEST @@ -2702,40 +2705,43 @@ async def test_firmware_upload_view_invalid_payload( @pytest.mark.parametrize( "method, url", - [("post", "/api/zwave_js/firmware/upload/{}/{}")], + [("post", "/api/zwave_js/firmware/upload/{}")], ) async def test_node_view_non_admin_user( - multisensor_6, integration, hass_client, hass_admin_user, method, url + hass, multisensor_6, integration, hass_client, hass_admin_user, method, url ): """Test node level views for non-admin users.""" client = await hass_client() + device = get_device(hass, multisensor_6) # Verify we require admin user hass_admin_user.groups = [] - resp = await client.request( - method, url.format(integration.entry_id, multisensor_6.node_id) - ) + resp = await client.request(method, url.format(device.id)) assert resp.status == HTTPStatus.UNAUTHORIZED @pytest.mark.parametrize( "method, url", [ - ("post", "/api/zwave_js/firmware/upload/INVALID/1"), + ("post", "/api/zwave_js/firmware/upload/{}"), ], ) -async def test_view_invalid_entry_id(integration, hass_client, method, url): - """Test an invalid config entry id parameter.""" +async def test_view_unloaded_config_entry( + hass, multisensor_6, integration, hass_client, method, url +): + """Test an unloaded config entry raises Bad Request.""" client = await hass_client() - resp = await client.request(method, url) + device = get_device(hass, multisensor_6) + await hass.config_entries.async_unload(integration.entry_id) + resp = await client.request(method, url.format(device.id)) assert resp.status == HTTPStatus.BAD_REQUEST @pytest.mark.parametrize( "method, url", - [("post", "/api/zwave_js/firmware/upload/{}/111")], + [("post", "/api/zwave_js/firmware/upload/INVALID")], ) -async def test_view_invalid_node_id(integration, hass_client, method, url): - """Test an invalid config entry id parameter.""" +async def test_view_invalid_device_id(integration, hass_client, method, url): + """Test an invalid device id parameter.""" client = await hass_client() resp = await client.request(method, url.format(integration.entry_id)) assert resp.status == HTTPStatus.NOT_FOUND From 85ef0e3bd6bf59124b4942ee9c559ecfb017781c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 08:27:24 +0200 Subject: [PATCH 644/930] Adjust device_automation type hints in nest (#72197) --- homeassistant/components/nest/device_trigger.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index 7bdbab214b8..9078f97f135 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -7,7 +7,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -64,7 +67,7 @@ async def async_get_device_trigger_types( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers for a Nest device.""" nest_device_id = async_get_nest_device_id(hass, device_id) if not nest_device_id: From 4fb3a01c36b7c90f0b56ec4b34d49cea1fbcea36 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 08:27:49 +0200 Subject: [PATCH 645/930] Adjust device_automation type hints in netatmo (#72136) * Adjust device_automation type hints in netatmo * Improve type hints --- homeassistant/components/netatmo/device_trigger.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/netatmo/device_trigger.py b/homeassistant/components/netatmo/device_trigger.py index ae62956b691..ce03aa91905 100644 --- a/homeassistant/components/netatmo/device_trigger.py +++ b/homeassistant/components/netatmo/device_trigger.py @@ -1,15 +1,16 @@ """Provides device automations for Netatmo.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -100,7 +101,7 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> GetAutomationsResult: """List device triggers for Netatmo devices.""" registry = entity_registry.async_get(hass) device_registry = dr.async_get(hass) From 654b095498052a080c81687392710cd3da6ca8a3 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 20 May 2022 08:37:53 +0200 Subject: [PATCH 646/930] Move manual configuration of MQTT alarm control panel to the integration key (#72165) Add alarm_control_panel --- homeassistant/components/mqtt/__init__.py | 1 + .../components/mqtt/alarm_control_panel.py | 28 ++++++++++++++++--- .../mqtt/test_alarm_control_panel.py | 13 +++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index e17a31480b1..e1de984f4df 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -190,6 +190,7 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema( PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( { + vol.Optional(Platform.ALARM_CONTROL_PANEL.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, } diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index a58e71e78d6..06c013ec744 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -1,6 +1,7 @@ """This platform enables the possibility to control a MQTT alarm.""" from __future__ import annotations +import asyncio import functools import logging import re @@ -44,8 +45,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -82,7 +85,7 @@ DEFAULT_NAME = "MQTT Alarm" REMOTE_CODE = "REMOTE_CODE" REMOTE_CODE_TEXT = "REMOTE_CODE_TEXT" -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_CODE): cv.string, vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, @@ -110,7 +113,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT alarm control panels under the alarm_control_panel platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(alarm.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -119,7 +128,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT alarm control panel through configuration.yaml.""" + """Set up MQTT alarm control panel configured under the alarm_control_panel key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, alarm.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -130,7 +140,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT alarm control panel dynamically through MQTT discovery.""" + """Set up MQTT alarm control panel through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, alarm.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index 5c73463924e..f4d76d5474c 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -57,6 +57,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -848,3 +849,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = alarm_control_panel.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = alarm_control_panel.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 71d6a17073f4b29a150b52c3e4c61a1358243f4a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 08:42:22 +0200 Subject: [PATCH 647/930] Adjust device_automation type hints in arcam_fmj (#72193) --- homeassistant/components/arcam_fmj/device_trigger.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index 593250e4983..46c51789dfb 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -7,7 +7,10 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA +from homeassistant.components.device_automation import ( + DEVICE_TRIGGER_BASE_SCHEMA, + GetAutomationsResult, +) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_DEVICE_ID, @@ -33,7 +36,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, str]]: +) -> GetAutomationsResult: """List device triggers for Arcam FMJ Receiver control devices.""" registry = entity_registry.async_get(hass) triggers = [] From 99ad785d0a0d174da0d3a68169ba543565e6e69a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 09:19:01 +0200 Subject: [PATCH 648/930] Adjust setup type hints in mqtt (#72227) --- .../components/mqtt/vacuum/__init__.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index f64a67820d4..4898a7b3351 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -1,9 +1,15 @@ """Support for MQTT vacuums.""" +from __future__ import annotations + import functools import voluptuous as vol from homeassistant.components import vacuum +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from ..mixins import async_setup_entry_helper, async_setup_platform_helper from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE @@ -40,14 +46,23 @@ PLATFORM_SCHEMA = vol.All( ) -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): +async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: """Set up MQTT vacuum through configuration.yaml.""" await async_setup_platform_helper( hass, vacuum.DOMAIN, config, async_add_entities, _async_setup_entity ) -async def async_setup_entry(hass, config_entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: """Set up MQTT vacuum dynamically through MQTT discovery.""" setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry From 74e8b076e5b9cc45c4e4bb6ac087c8e8f140c8bd Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Fri, 20 May 2022 12:05:53 +0300 Subject: [PATCH 649/930] Fix Shelly missing key config flow (#72116) --- .../components/shelly/config_flow.py | 64 +++++++------ tests/components/shelly/test_config_flow.py | 94 +++++++++++++++++-- 2 files changed, 117 insertions(+), 41 deletions(-) diff --git a/homeassistant/components/shelly/config_flow.py b/homeassistant/components/shelly/config_flow.py index abcfe689e93..41e0bd3031a 100644 --- a/homeassistant/components/shelly/config_flow.py +++ b/homeassistant/components/shelly/config_flow.py @@ -58,10 +58,12 @@ async def validate_input( options, ) await rpc_device.shutdown() + assert rpc_device.shelly + return { "title": get_rpc_device_name(rpc_device), CONF_SLEEP_PERIOD: 0, - "model": rpc_device.model, + "model": rpc_device.shelly.get("model"), "gen": 2, } @@ -119,21 +121,21 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) except HTTP_CONNECT_ERRORS: errors["base"] = "cannot_connect" - except KeyError: - errors["base"] = "firmware_not_fully_provisioned" except Exception: # pylint: disable=broad-except LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - return self.async_create_entry( - title=device_info["title"], - data={ - **user_input, - CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD], - "model": device_info["model"], - "gen": device_info["gen"], - }, - ) + if device_info["model"]: + return self.async_create_entry( + title=device_info["title"], + data={ + **user_input, + CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD], + "model": device_info["model"], + "gen": device_info["gen"], + }, + ) + errors["base"] = "firmware_not_fully_provisioned" return self.async_show_form( step_id="user", data_schema=HOST_SCHEMA, errors=errors @@ -162,22 +164,22 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "cannot_connect" except aioshelly.exceptions.JSONRPCError: errors["base"] = "cannot_connect" - except KeyError: - errors["base"] = "firmware_not_fully_provisioned" except Exception: # pylint: disable=broad-except LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - return self.async_create_entry( - title=device_info["title"], - data={ - **user_input, - CONF_HOST: self.host, - CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD], - "model": device_info["model"], - "gen": device_info["gen"], - }, - ) + if device_info["model"]: + return self.async_create_entry( + title=device_info["title"], + data={ + **user_input, + CONF_HOST: self.host, + CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD], + "model": device_info["model"], + "gen": device_info["gen"], + }, + ) + errors["base"] = "firmware_not_fully_provisioned" else: user_input = {} @@ -223,8 +225,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: self.device_info = await validate_input(self.hass, self.host, self.info, {}) - except KeyError: - LOGGER.debug("Shelly host %s firmware not fully provisioned", self.host) except HTTP_CONNECT_ERRORS: return self.async_abort(reason="cannot_connect") @@ -235,7 +235,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Handle discovery confirm.""" errors: dict[str, str] = {} - try: + + if not self.device_info["model"]: + errors["base"] = "firmware_not_fully_provisioned" + model = "Shelly" + else: + model = get_model_name(self.info) if user_input is not None: return self.async_create_entry( title=self.device_info["title"], @@ -246,15 +251,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): "gen": self.device_info["gen"], }, ) - except KeyError: - errors["base"] = "firmware_not_fully_provisioned" - else: self._set_confirm_only() return self.async_show_form( step_id="confirm_discovery", description_placeholders={ - "model": get_model_name(self.info), + "model": model, "host": self.host, }, errors=errors, diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 713999de36f..145bcbb3566 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -58,7 +58,7 @@ async def test_form(hass, gen): "aioshelly.rpc_device.RpcDevice.create", new=AsyncMock( return_value=Mock( - model="SHSW-1", + shelly={"model": "SHSW-1", "gen": gen}, config=MOCK_CONFIG, shutdown=AsyncMock(), ) @@ -175,7 +175,7 @@ async def test_form_auth(hass, test_data): "aioshelly.rpc_device.RpcDevice.create", new=AsyncMock( return_value=Mock( - model="SHSW-1", + shelly={"model": "SHSW-1", "gen": gen}, config=MOCK_CONFIG, shutdown=AsyncMock(), ) @@ -225,19 +225,23 @@ async def test_form_errors_get_info(hass, error): assert result2["errors"] == {"base": base_error} -@pytest.mark.parametrize("error", [(KeyError, "firmware_not_fully_provisioned")]) -async def test_form_missing_key_get_info(hass, error): - """Test we handle missing key.""" - exc, base_error = error +async def test_form_missing_model_key(hass): + """Test we handle missing Shelly model key.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "aioshelly.common.get_info", - return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False, "gen": "2"}, + return_value={"mac": "test-mac", "auth": False, "gen": "2"}, ), patch( - "homeassistant.components.shelly.config_flow.validate_input", - side_effect=KeyError, + "aioshelly.rpc_device.RpcDevice.create", + new=AsyncMock( + return_value=Mock( + shelly={"gen": 2}, + config=MOCK_CONFIG, + shutdown=AsyncMock(), + ) + ), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -245,7 +249,77 @@ async def test_form_missing_key_get_info(hass, error): ) assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result2["errors"] == {"base": base_error} + assert result2["errors"] == {"base": "firmware_not_fully_provisioned"} + + +async def test_form_missing_model_key_auth_enabled(hass): + """Test we handle missing Shelly model key when auth enabled.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["errors"] == {} + + with patch( + "aioshelly.common.get_info", + return_value={"mac": "test-mac", "auth": True, "gen": 2}, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"host": "1.1.1.1"}, + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["errors"] == {} + + with patch( + "aioshelly.rpc_device.RpcDevice.create", + new=AsyncMock( + return_value=Mock( + shelly={"gen": 2}, + config=MOCK_CONFIG, + shutdown=AsyncMock(), + ) + ), + ): + result3 = await hass.config_entries.flow.async_configure( + result2["flow_id"], {"password": "1234"} + ) + + assert result3["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result3["errors"] == {"base": "firmware_not_fully_provisioned"} + + +async def test_form_missing_model_key_zeroconf(hass, caplog): + """Test we handle missing Shelly model key via zeroconf.""" + + with patch( + "aioshelly.common.get_info", + return_value={"mac": "test-mac", "auth": False, "gen": 2}, + ), patch( + "aioshelly.rpc_device.RpcDevice.create", + new=AsyncMock( + return_value=Mock( + shelly={"gen": 2}, + config=MOCK_CONFIG, + shutdown=AsyncMock(), + ) + ), + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + data=DISCOVERY_INFO, + context={"source": config_entries.SOURCE_ZEROCONF}, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["errors"] == {"base": "firmware_not_fully_provisioned"} + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "firmware_not_fully_provisioned"} @pytest.mark.parametrize( From a72ce2415b41c02f4dabf327867dcf16917ed06e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 20 May 2022 11:15:08 +0200 Subject: [PATCH 650/930] Adjust setup type hints in agent_dvr (#72224) --- .../components/agent_dvr/alarm_control_panel.py | 9 +++++++-- homeassistant/components/agent_dvr/camera.py | 15 +++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/agent_dvr/alarm_control_panel.py b/homeassistant/components/agent_dvr/alarm_control_panel.py index 3e264a1985d..8978be97c1d 100644 --- a/homeassistant/components/agent_dvr/alarm_control_panel.py +++ b/homeassistant/components/agent_dvr/alarm_control_panel.py @@ -3,13 +3,16 @@ from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntity, AlarmControlPanelEntityFeature, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, ) +from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import CONNECTION, DOMAIN as AGENT_DOMAIN @@ -23,8 +26,10 @@ CONST_ALARM_CONTROL_PANEL_NAME = "Alarm Panel" async def async_setup_entry( - hass, config_entry, async_add_entities, discovery_info=None -): + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: """Set up the Agent DVR Alarm Control Panels.""" async_add_entities( [AgentBaseStation(hass.data[AGENT_DOMAIN][config_entry.entry_id][CONNECTION])] diff --git a/homeassistant/components/agent_dvr/camera.py b/homeassistant/components/agent_dvr/camera.py index 49ca342ca09..9aab33efd5a 100644 --- a/homeassistant/components/agent_dvr/camera.py +++ b/homeassistant/components/agent_dvr/camera.py @@ -6,9 +6,14 @@ from agent import AgentError from homeassistant.components.camera import CameraEntityFeature from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION -from homeassistant.helpers import entity_platform +from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, + async_get_current_platform, +) from .const import ( ATTRIBUTION, @@ -37,8 +42,10 @@ CAMERA_SERVICES = { async def async_setup_entry( - hass, config_entry, async_add_entities, discovery_info=None -): + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: """Set up the Agent cameras.""" filter_urllib3_logging() cameras = [] @@ -55,7 +62,7 @@ async def async_setup_entry( async_add_entities(cameras) - platform = entity_platform.async_get_current_platform() + platform = async_get_current_platform() for service, method in CAMERA_SERVICES.items(): platform.async_register_entity_service(service, {}, method) From cbcf832436bd194e2a59df828ad14c1621e0128e Mon Sep 17 00:00:00 2001 From: SNoof85 Date: Fri, 20 May 2022 15:22:38 +0200 Subject: [PATCH 651/930] Fix for Xiaomi miio fan speed (#72027) Update fan.py --- homeassistant/components/xiaomi_miio/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/xiaomi_miio/fan.py b/homeassistant/components/xiaomi_miio/fan.py index 1e525887c56..969093545f0 100644 --- a/homeassistant/components/xiaomi_miio/fan.py +++ b/homeassistant/components/xiaomi_miio/fan.py @@ -1041,7 +1041,7 @@ class XiaomiFanMiot(XiaomiGenericFan): self._preset_mode = self.coordinator.data.mode.name self._oscillating = self.coordinator.data.oscillate if self.coordinator.data.is_on: - self._percentage = self.coordinator.data.fan_speed + self._percentage = self.coordinator.data.speed else: self._percentage = 0 From d459a5c66ecd1ad3fd769f42079220a05aa417d1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 20 May 2022 09:46:01 -0500 Subject: [PATCH 652/930] Include context state in logbook responses to improve localization (#72222) * Include context state in logbook responses to improve localization * reduce payload, dont send context_event_type if sending context_state --- homeassistant/components/logbook/__init__.py | 3 +- tests/components/logbook/test_init.py | 63 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index e2073800c25..635868310f6 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -87,6 +87,7 @@ CONTEXT_ENTITY_ID = "context_entity_id" CONTEXT_ENTITY_ID_NAME = "context_entity_id_name" CONTEXT_EVENT_TYPE = "context_event_type" CONTEXT_DOMAIN = "context_domain" +CONTEXT_STATE = "context_state" CONTEXT_SERVICE = "context_service" CONTEXT_NAME = "context_name" CONTEXT_MESSAGE = "context_message" @@ -674,12 +675,12 @@ class ContextAugmenter: # State change if context_entity_id := context_row.entity_id: + data[CONTEXT_STATE] = context_row.state data[CONTEXT_ENTITY_ID] = context_entity_id if self.include_entity_name: data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( context_entity_id, context_row ) - data[CONTEXT_EVENT_TYPE] = event_type return # Call service diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 05df4f2ab3b..3b18c594e6e 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -2738,3 +2738,66 @@ async def test_logbook_select_entities_context_id(hass, recorder_mock, hass_clie assert json_dict[3]["context_domain"] == "light" assert json_dict[3]["context_service"] == "turn_off" assert json_dict[3]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474" + + +async def test_get_events_with_context_state(hass, hass_ws_client, recorder_mock): + """Test logbook get_events with a context state.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + await async_recorder_block_till_done(hass) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("light.kitchen1", STATE_OFF) + hass.states.async_set("light.kitchen2", STATE_OFF) + + context = ha.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + hass.states.async_set("binary_sensor.is_light", STATE_OFF, context=context) + await hass.async_block_till_done() + hass.states.async_set( + "light.kitchen1", STATE_ON, {"brightness": 100}, context=context + ) + await hass.async_block_till_done() + hass.states.async_set( + "light.kitchen2", STATE_ON, {"brightness": 200}, context=context + ) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + + client = await hass_ws_client() + + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + results = response["result"] + assert results[1]["entity_id"] == "binary_sensor.is_light" + assert results[1]["state"] == "off" + assert "context_state" not in results[1] + assert results[2]["entity_id"] == "light.kitchen1" + assert results[2]["state"] == "on" + assert results[2]["context_entity_id"] == "binary_sensor.is_light" + assert results[2]["context_state"] == "off" + assert results[2]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + assert "context_event_type" not in results[2] + assert results[3]["entity_id"] == "light.kitchen2" + assert results[3]["state"] == "on" + assert results[3]["context_entity_id"] == "binary_sensor.is_light" + assert results[3]["context_state"] == "off" + assert results[3]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474" + assert "context_event_type" not in results[3] From 775be354a6935394013094835c999f015a85b8c7 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 20 May 2022 07:47:18 -0700 Subject: [PATCH 653/930] Cleanup nest async methods that do not need to actually await (#72170) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/nest/__init__.py | 9 ++++-- homeassistant/components/nest/camera_sdm.py | 14 +++------ homeassistant/components/nest/climate_sdm.py | 12 ++------ homeassistant/components/nest/const.py | 1 + .../components/nest/device_trigger.py | 13 ++++---- homeassistant/components/nest/diagnostics.py | 27 +++++++---------- homeassistant/components/nest/media_source.py | 21 ++++++------- homeassistant/components/nest/sensor_sdm.py | 13 ++------ tests/components/nest/test_diagnostics.py | 30 +------------------ 9 files changed, 43 insertions(+), 97 deletions(-) diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 7d8353738ff..0920b37e6ef 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -54,6 +54,7 @@ from . import api, config_flow from .const import ( CONF_PROJECT_ID, CONF_SUBSCRIBER_ID, + DATA_DEVICE_MANAGER, DATA_NEST_CONFIG, DATA_SDM, DATA_SUBSCRIBER, @@ -63,8 +64,8 @@ from .events import EVENT_NAME_MAP, NEST_EVENT from .legacy import async_setup_legacy, async_setup_legacy_entry from .media_source import ( async_get_media_event_store, + async_get_media_source_devices, async_get_transcoder, - get_media_source_devices, ) _LOGGER = logging.getLogger(__name__) @@ -205,7 +206,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady from err try: - await subscriber.async_get_device_manager() + device_manager = await subscriber.async_get_device_manager() except ApiException as err: if DATA_NEST_UNAVAILABLE not in hass.data[DOMAIN]: _LOGGER.error("Device manager error: %s", err) @@ -215,6 +216,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN].pop(DATA_NEST_UNAVAILABLE, None) hass.data[DOMAIN][DATA_SUBSCRIBER] = subscriber + hass.data[DOMAIN][DATA_DEVICE_MANAGER] = device_manager hass.config_entries.async_setup_platforms(entry, PLATFORMS) @@ -232,6 +234,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: hass.data[DOMAIN].pop(DATA_SUBSCRIBER) + hass.data[DOMAIN].pop(DATA_DEVICE_MANAGER) hass.data[DOMAIN].pop(DATA_NEST_UNAVAILABLE, None) return unload_ok @@ -275,7 +278,7 @@ class NestEventViewBase(HomeAssistantView, ABC): if not user.permissions.check_entity(entry.entity_id, POLICY_READ): raise Unauthorized(entity_id=entry.entity_id) - devices = await get_media_source_devices(self.hass) + devices = async_get_media_source_devices(self.hass) if not (nest_device := devices.get(device_id)): return self._json_error( f"No Nest Device found for '{device_id}'", HTTPStatus.NOT_FOUND diff --git a/homeassistant/components/nest/camera_sdm.py b/homeassistant/components/nest/camera_sdm.py index a46af2979f4..6e14100e881 100644 --- a/homeassistant/components/nest/camera_sdm.py +++ b/homeassistant/components/nest/camera_sdm.py @@ -15,19 +15,20 @@ from google_nest_sdm.camera_traits import ( StreamingProtocol, ) from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.exceptions import ApiException from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.camera.const import StreamType from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError, PlatformNotReady +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.util.dt import utcnow -from .const import DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DOMAIN from .device_info import NestDeviceInfo _LOGGER = logging.getLogger(__name__) @@ -43,14 +44,7 @@ async def async_setup_sdm_entry( ) -> None: """Set up the cameras.""" - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - try: - device_manager = await subscriber.async_get_device_manager() - except ApiException as err: - raise PlatformNotReady from err - - # Fetch initial data so we have data when entities subscribe. - + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] entities = [] for device in device_manager.devices.values(): if ( diff --git a/homeassistant/components/nest/climate_sdm.py b/homeassistant/components/nest/climate_sdm.py index 89048b9f624..bbbf83501f7 100644 --- a/homeassistant/components/nest/climate_sdm.py +++ b/homeassistant/components/nest/climate_sdm.py @@ -4,8 +4,8 @@ from __future__ import annotations from typing import Any, cast from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.device_traits import FanTrait, TemperatureTrait -from google_nest_sdm.exceptions import ApiException from google_nest_sdm.thermostat_traits import ( ThermostatEcoTrait, ThermostatHeatCoolTrait, @@ -30,11 +30,10 @@ from homeassistant.components.climate.const import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DOMAIN from .device_info import NestDeviceInfo # Mapping for sdm.devices.traits.ThermostatMode mode field @@ -80,12 +79,7 @@ async def async_setup_sdm_entry( ) -> None: """Set up the client entities.""" - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - try: - device_manager = await subscriber.async_get_device_manager() - except ApiException as err: - raise PlatformNotReady from err - + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] entities = [] for device in device_manager.devices.values(): if ThermostatHvacTrait.NAME in device.traits: diff --git a/homeassistant/components/nest/const.py b/homeassistant/components/nest/const.py index 7aabcfe8d77..bd951756eae 100644 --- a/homeassistant/components/nest/const.py +++ b/homeassistant/components/nest/const.py @@ -3,6 +3,7 @@ DOMAIN = "nest" DATA_SDM = "sdm" DATA_SUBSCRIBER = "subscriber" +DATA_DEVICE_MANAGER = "device_manager" DATA_NEST_CONFIG = "nest_config" WEB_AUTH_DOMAIN = DOMAIN diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index 9078f97f135..e2f4288d122 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -1,6 +1,7 @@ """Provides device automations for Nest.""" from __future__ import annotations +from google_nest_sdm.device_manager import DeviceManager import voluptuous as vol from homeassistant.components.automation import ( @@ -20,7 +21,7 @@ from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType -from .const import DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DOMAIN from .events import DEVICE_TRAIT_TRIGGER_MAP, NEST_EVENT DEVICE = "device" @@ -45,14 +46,12 @@ def async_get_nest_device_id(hass: HomeAssistant, device_id: str) -> str | None: return None -async def async_get_device_trigger_types( +@callback +def async_get_device_trigger_types( hass: HomeAssistant, nest_device_id: str ) -> list[str]: """List event triggers supported for a Nest device.""" - # All devices should have already been loaded so any failures here are - # "shouldn't happen" cases - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - device_manager = await subscriber.async_get_device_manager() + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] if not (nest_device := device_manager.devices.get(nest_device_id)): raise InvalidDeviceAutomationConfig(f"Nest device not found {nest_device_id}") @@ -72,7 +71,7 @@ async def async_get_triggers( nest_device_id = async_get_nest_device_id(hass, device_id) if not nest_device_id: raise InvalidDeviceAutomationConfig(f"Device not found {device_id}") - trigger_types = await async_get_device_trigger_types(hass, nest_device_id) + trigger_types = async_get_device_trigger_types(hass, nest_device_id) return [ { CONF_PLATFORM: DEVICE, diff --git a/homeassistant/components/nest/diagnostics.py b/homeassistant/components/nest/diagnostics.py index d178d52393e..c21842d5939 100644 --- a/homeassistant/components/nest/diagnostics.py +++ b/homeassistant/components/nest/diagnostics.py @@ -6,43 +6,39 @@ from typing import Any from google_nest_sdm import diagnostics from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.device_traits import InfoTrait -from google_nest_sdm.exceptions import ApiException from homeassistant.components.camera import diagnostics as camera_diagnostics from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.device_registry import DeviceEntry -from .const import DATA_SDM, DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DATA_SDM, DOMAIN REDACT_DEVICE_TRAITS = {InfoTrait.NAME} -async def _get_nest_devices( +@callback +def _async_get_nest_devices( hass: HomeAssistant, config_entry: ConfigEntry ) -> dict[str, Device]: """Return dict of available devices.""" if DATA_SDM not in config_entry.data: return {} - if DATA_SUBSCRIBER not in hass.data[DOMAIN]: + if DATA_DEVICE_MANAGER not in hass.data[DOMAIN]: return {} - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - device_manager = await subscriber.async_get_device_manager() - devices: dict[str, Device] = device_manager.devices - return devices + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] + return device_manager.devices async def async_get_config_entry_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry ) -> dict: """Return diagnostics for a config entry.""" - try: - nest_devices = await _get_nest_devices(hass, config_entry) - except ApiException as err: - return {"error": str(err)} + nest_devices = _async_get_nest_devices(hass, config_entry) if not nest_devices: return {} data: dict[str, Any] = { @@ -65,10 +61,7 @@ async def async_get_device_diagnostics( device: DeviceEntry, ) -> dict: """Return diagnostics for a device.""" - try: - nest_devices = await _get_nest_devices(hass, config_entry) - except ApiException as err: - return {"error": str(err)} + nest_devices = _async_get_nest_devices(hass, config_entry) nest_device_id = next(iter(device.identifiers))[1] nest_device = nest_devices.get(nest_device_id) return nest_device.get_diagnostics() if nest_device else {} diff --git a/homeassistant/components/nest/media_source.py b/homeassistant/components/nest/media_source.py index 7a8ae49bdbc..e4e26153b3a 100644 --- a/homeassistant/components/nest/media_source.py +++ b/homeassistant/components/nest/media_source.py @@ -25,6 +25,7 @@ import os from google_nest_sdm.camera_traits import CameraClipPreviewTrait, CameraEventImageTrait from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.event import EventImageType, ImageEventBase from google_nest_sdm.event_media import ( ClipPreviewSession, @@ -50,13 +51,13 @@ from homeassistant.components.media_source.models import ( MediaSourceItem, PlayMedia, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr from homeassistant.helpers.storage import Store from homeassistant.helpers.template import DATE_STR_FORMAT from homeassistant.util import dt as dt_util -from .const import DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DOMAIN from .device_info import NestDeviceInfo from .events import EVENT_NAME_MAP, MEDIA_SOURCE_EVENT_TITLE_MAP @@ -267,13 +268,13 @@ async def async_get_media_source(hass: HomeAssistant) -> MediaSource: return NestMediaSource(hass) -async def get_media_source_devices(hass: HomeAssistant) -> Mapping[str, Device]: +@callback +def async_get_media_source_devices(hass: HomeAssistant) -> Mapping[str, Device]: """Return a mapping of device id to eligible Nest event media devices.""" - if DATA_SUBSCRIBER not in hass.data[DOMAIN]: + if DATA_DEVICE_MANAGER not in hass.data[DOMAIN]: # Integration unloaded, or is legacy nest integration return {} - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - device_manager = await subscriber.async_get_device_manager() + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] device_registry = dr.async_get(hass) devices = {} for device in device_manager.devices.values(): @@ -339,7 +340,7 @@ class NestMediaSource(MediaSource): media_id: MediaId | None = parse_media_id(item.identifier) if not media_id: raise Unresolvable("No identifier specified for MediaSourceItem") - devices = await self.devices() + devices = async_get_media_source_devices(self.hass) if not (device := devices.get(media_id.device_id)): raise Unresolvable( "Unable to find device with identifier: %s" % item.identifier @@ -376,7 +377,7 @@ class NestMediaSource(MediaSource): _LOGGER.debug( "Browsing media for identifier=%s, media_id=%s", item.identifier, media_id ) - devices = await self.devices() + devices = async_get_media_source_devices(self.hass) if media_id is None: # Browse the root and return child devices browse_root = _browse_root() @@ -443,10 +444,6 @@ class NestMediaSource(MediaSource): ) return _browse_image_event(media_id, device, single_image) - async def devices(self) -> Mapping[str, Device]: - """Return all event media related devices.""" - return await get_media_source_devices(self.hass) - async def _async_get_clip_preview_sessions( device: Device, diff --git a/homeassistant/components/nest/sensor_sdm.py b/homeassistant/components/nest/sensor_sdm.py index b9c13f6b9c7..d33aa3eff8b 100644 --- a/homeassistant/components/nest/sensor_sdm.py +++ b/homeassistant/components/nest/sensor_sdm.py @@ -4,8 +4,8 @@ from __future__ import annotations import logging from google_nest_sdm.device import Device +from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.device_traits import HumidityTrait, TemperatureTrait -from google_nest_sdm.exceptions import ApiException from homeassistant.components.sensor import ( SensorDeviceClass, @@ -15,10 +15,9 @@ from homeassistant.components.sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant -from homeassistant.exceptions import PlatformNotReady from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DATA_SUBSCRIBER, DOMAIN +from .const import DATA_DEVICE_MANAGER, DOMAIN from .device_info import NestDeviceInfo _LOGGER = logging.getLogger(__name__) @@ -37,13 +36,7 @@ async def async_setup_sdm_entry( ) -> None: """Set up the sensors.""" - subscriber = hass.data[DOMAIN][DATA_SUBSCRIBER] - try: - device_manager = await subscriber.async_get_device_manager() - except ApiException as err: - _LOGGER.warning("Failed to get devices: %s", err) - raise PlatformNotReady from err - + device_manager: DeviceManager = hass.data[DOMAIN][DATA_DEVICE_MANAGER] entities: list[SensorEntity] = [] for device in device_manager.devices.values(): if TemperatureTrait.NAME in device.traits: diff --git a/tests/components/nest/test_diagnostics.py b/tests/components/nest/test_diagnostics.py index b69f5970c2d..8e28222e356 100644 --- a/tests/components/nest/test_diagnostics.py +++ b/tests/components/nest/test_diagnostics.py @@ -2,7 +2,7 @@ from unittest.mock import patch -from google_nest_sdm.exceptions import ApiException, SubscriberException +from google_nest_sdm.exceptions import SubscriberException import pytest from homeassistant.components.nest.const import DOMAIN @@ -139,34 +139,6 @@ async def test_setup_susbcriber_failure( assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {} -async def test_device_manager_failure( - hass, - hass_client, - config_entry, - setup_platform, - create_device, -): - """Test configuration error.""" - create_device.create(raw_data=DEVICE_API_DATA) - await setup_platform() - assert config_entry.state is ConfigEntryState.LOADED - - device_registry = dr.async_get(hass) - device = device_registry.async_get_device({(DOMAIN, NEST_DEVICE_ID)}) - assert device is not None - - with patch( - "homeassistant.components.nest.diagnostics._get_nest_devices", - side_effect=ApiException("Device manager failure"), - ): - assert await get_diagnostics_for_config_entry( - hass, hass_client, config_entry - ) == {"error": "Device manager failure"} - assert await get_diagnostics_for_device( - hass, hass_client, config_entry, device - ) == {"error": "Device manager failure"} - - @pytest.mark.parametrize("nest_test_config", [TEST_CONFIG_LEGACY]) async def test_legacy_config_entry_diagnostics( hass, hass_client, config_entry, setup_base_platform From 614c44b9f0a4c1e1daa1b50f5bee181a1e1325ee Mon Sep 17 00:00:00 2001 From: jjlawren Date: Fri, 20 May 2022 09:48:22 -0500 Subject: [PATCH 654/930] Bump plexapi to 4.11.1 (#72121) --- homeassistant/components/plex/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 084356abf7b..2e2db01de77 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/plex", "requirements": [ - "plexapi==4.10.1", + "plexapi==4.11.1", "plexauth==0.0.6", "plexwebsocket==0.0.13" ], diff --git a/requirements_all.txt b/requirements_all.txt index 4ac46752539..a5aae3b6880 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1233,7 +1233,7 @@ pillow==9.1.0 pizzapi==0.0.3 # homeassistant.components.plex -plexapi==4.10.1 +plexapi==4.11.1 # homeassistant.components.plex plexauth==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0327dcfa142..9fefab5b2d5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -835,7 +835,7 @@ pilight==0.1.1 pillow==9.1.0 # homeassistant.components.plex -plexapi==4.10.1 +plexapi==4.11.1 # homeassistant.components.plex plexauth==0.0.6 From e9c861f2b2536690c567a2f7d76a5d1c0a37dbe2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 20 May 2022 09:49:26 -0500 Subject: [PATCH 655/930] Add support for cover positions in bond (#72180) --- homeassistant/components/bond/button.py | 14 +++++ homeassistant/components/bond/cover.py | 24 +++++++- homeassistant/components/bond/utils.py | 4 ++ tests/components/bond/test_cover.py | 79 ++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bond/button.py b/homeassistant/components/bond/button.py index 700c6b5f407..0152bedde23 100644 --- a/homeassistant/components/bond/button.py +++ b/homeassistant/components/bond/button.py @@ -223,6 +223,20 @@ BUTTONS: tuple[BondButtonEntityDescription, ...] = ( mutually_exclusive=Action.OPEN, argument=None, ), + BondButtonEntityDescription( + key=Action.INCREASE_POSITION, + name="Increase Position", + icon="mdi:plus-box", + mutually_exclusive=Action.SET_POSITION, + argument=STEP_SIZE, + ), + BondButtonEntityDescription( + key=Action.DECREASE_POSITION, + name="Decrease Position", + icon="mdi:minus-box", + mutually_exclusive=Action.SET_POSITION, + argument=STEP_SIZE, + ), ) diff --git a/homeassistant/components/bond/cover.py b/homeassistant/components/bond/cover.py index 664431a3145..a50f7b93bbb 100644 --- a/homeassistant/components/bond/cover.py +++ b/homeassistant/components/bond/cover.py @@ -6,6 +6,7 @@ from typing import Any from bond_api import Action, BPUPSubscriptions, DeviceType from homeassistant.components.cover import ( + ATTR_POSITION, CoverDeviceClass, CoverEntity, CoverEntityFeature, @@ -20,6 +21,16 @@ from .entity import BondEntity from .utils import BondDevice, BondHub +def _bond_to_hass_position(bond_position: int) -> int: + """Convert bond 0-open 100-closed to hass 0-closed 100-open.""" + return abs(bond_position - 100) + + +def _hass_to_bond_position(hass_position: int) -> int: + """Convert hass 0-closed 100-open to bond 0-open 100-closed.""" + return 100 - hass_position + + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, @@ -50,6 +61,8 @@ class BondCover(BondEntity, CoverEntity): """Create HA entity representing Bond cover.""" super().__init__(hub, device, bpup_subs) supported_features = 0 + if self._device.supports_set_position(): + supported_features |= CoverEntityFeature.SET_POSITION if self._device.supports_open(): supported_features |= CoverEntityFeature.OPEN if self._device.supports_close(): @@ -67,8 +80,15 @@ class BondCover(BondEntity, CoverEntity): def _apply_state(self, state: dict) -> None: cover_open = state.get("open") - self._attr_is_closed = ( - True if cover_open == 0 else False if cover_open == 1 else None + self._attr_is_closed = None if cover_open is None else cover_open == 0 + if (bond_position := state.get("position")) is not None: + self._attr_current_cover_position = _bond_to_hass_position(bond_position) + + async def async_set_cover_position(self, **kwargs: Any) -> None: + """Set the cover position.""" + await self._hub.bond.action( + self._device.device_id, + Action.set_position(_hass_to_bond_position(kwargs[ATTR_POSITION])), ) async def async_open_cover(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/bond/utils.py b/homeassistant/components/bond/utils.py index 785d7dfbd00..fc78c5758c1 100644 --- a/homeassistant/components/bond/utils.py +++ b/homeassistant/components/bond/utils.py @@ -82,6 +82,10 @@ class BondDevice: """Return True if this device supports any of the direction related commands.""" return self._has_any_action({Action.SET_DIRECTION}) + def supports_set_position(self) -> bool: + """Return True if this device supports setting the position.""" + return self._has_any_action({Action.SET_POSITION}) + def supports_open(self) -> bool: """Return True if this device supports opening.""" return self._has_any_action({Action.OPEN}) diff --git a/tests/components/bond/test_cover.py b/tests/components/bond/test_cover.py index 7a27617d607..ca467d4a38d 100644 --- a/tests/components/bond/test_cover.py +++ b/tests/components/bond/test_cover.py @@ -4,15 +4,22 @@ from datetime import timedelta from bond_api import Action, DeviceType from homeassistant import core -from homeassistant.components.cover import DOMAIN as COVER_DOMAIN, STATE_CLOSED +from homeassistant.components.cover import ( + ATTR_CURRENT_POSITION, + ATTR_POSITION, + DOMAIN as COVER_DOMAIN, + STATE_CLOSED, +) from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_CLOSE_COVER, SERVICE_CLOSE_COVER_TILT, SERVICE_OPEN_COVER, SERVICE_OPEN_COVER_TILT, + SERVICE_SET_COVER_POSITION, SERVICE_STOP_COVER, SERVICE_STOP_COVER_TILT, + STATE_OPEN, STATE_UNKNOWN, ) from homeassistant.helpers import entity_registry as er @@ -38,6 +45,15 @@ def shades(name: str): } +def shades_with_position(name: str): + """Create motorized shades that supports set position.""" + return { + "name": name, + "type": DeviceType.MOTORIZED_SHADES, + "actions": [Action.OPEN, Action.CLOSE, Action.HOLD, Action.SET_POSITION], + } + + def tilt_only_shades(name: str): """Create motorized shades that only tilt.""" return { @@ -236,3 +252,64 @@ async def test_cover_available(hass: core.HomeAssistant): await help_test_entity_available( hass, COVER_DOMAIN, shades("name-1"), "cover.name_1" ) + + +async def test_set_position_cover(hass: core.HomeAssistant): + """Tests that set position cover command delegates to API.""" + await setup_platform( + hass, + COVER_DOMAIN, + shades_with_position("name-1"), + bond_device_id="test-device-id", + ) + + with patch_bond_action() as mock_hold, patch_bond_device_state( + return_value={"position": 0, "open": 1} + ): + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 100}, + blocking=True, + ) + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + mock_hold.assert_called_once_with("test-device-id", Action.set_position(0)) + entity_state = hass.states.get("cover.name_1") + assert entity_state.state == STATE_OPEN + assert entity_state.attributes[ATTR_CURRENT_POSITION] == 100 + + with patch_bond_action() as mock_hold, patch_bond_device_state( + return_value={"position": 100, "open": 0} + ): + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 0}, + blocking=True, + ) + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + mock_hold.assert_called_once_with("test-device-id", Action.set_position(100)) + entity_state = hass.states.get("cover.name_1") + assert entity_state.state == STATE_CLOSED + assert entity_state.attributes[ATTR_CURRENT_POSITION] == 0 + + with patch_bond_action() as mock_hold, patch_bond_device_state( + return_value={"position": 40, "open": 1} + ): + await hass.services.async_call( + COVER_DOMAIN, + SERVICE_SET_COVER_POSITION, + {ATTR_ENTITY_ID: "cover.name_1", ATTR_POSITION: 60}, + blocking=True, + ) + async_fire_time_changed(hass, utcnow() + timedelta(seconds=30)) + await hass.async_block_till_done() + + mock_hold.assert_called_once_with("test-device-id", Action.set_position(40)) + entity_state = hass.states.get("cover.name_1") + assert entity_state.state == STATE_OPEN + assert entity_state.attributes[ATTR_CURRENT_POSITION] == 60 From 5866bf48da27a0d769b2d340ce69b64659fadf77 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 20 May 2022 09:07:02 -0700 Subject: [PATCH 656/930] Bump gcal_sync to 0.9.0 (#72237) --- homeassistant/components/google/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index 03d15597c44..081eae34a95 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["application_credentials"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.8.1", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.9.0", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index a5aae3b6880..d74741129d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.8.1 +gcal-sync==0.9.0 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9fefab5b2d5..9ca019992c7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -492,7 +492,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.8.1 +gcal-sync==0.9.0 # homeassistant.components.geocaching geocachingapi==0.2.1 From 1e7b187fc6d9d7fd20f174d7307d3fa6a31dc05a Mon Sep 17 00:00:00 2001 From: Matrix Date: Sat, 21 May 2022 01:06:47 +0800 Subject: [PATCH 657/930] Add yolink sensors (#72186) * Add more sensor support * change codeowner to active account * fix suggest --- CODEOWNERS | 4 +-- .../components/yolink/binary_sensor.py | 31 +++++++++++++--- homeassistant/components/yolink/const.py | 3 ++ homeassistant/components/yolink/manifest.json | 2 +- homeassistant/components/yolink/sensor.py | 35 ++++++++++++++++--- 5 files changed, 64 insertions(+), 11 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index a1d329cbee0..c3e36d1fef7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1201,8 +1201,8 @@ build.json @home-assistant/supervisor /tests/components/yeelight/ @zewelor @shenxn @starkillerOG @alexyao2015 /homeassistant/components/yeelightsunflower/ @lindsaymarkward /homeassistant/components/yi/ @bachya -/homeassistant/components/yolink/ @YoSmart-Inc -/tests/components/yolink/ @YoSmart-Inc +/homeassistant/components/yolink/ @matrixd2 +/tests/components/yolink/ @matrixd2 /homeassistant/components/youless/ @gjong /tests/components/youless/ @gjong /homeassistant/components/zengge/ @emontnemery diff --git a/homeassistant/components/yolink/binary_sensor.py b/homeassistant/components/yolink/binary_sensor.py index c3f2d10cf4c..494a7ff10eb 100644 --- a/homeassistant/components/yolink/binary_sensor.py +++ b/homeassistant/components/yolink/binary_sensor.py @@ -15,7 +15,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_COORDINATOR, ATTR_DEVICE_DOOR_SENSOR, DOMAIN +from .const import ( + ATTR_COORDINATOR, + ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_LEAK_SENSOR, + ATTR_DEVICE_MOTION_SENSOR, + DOMAIN, +) from .coordinator import YoLinkCoordinator from .entity import YoLinkEntity @@ -25,20 +31,37 @@ class YoLinkBinarySensorEntityDescription(BinarySensorEntityDescription): """YoLink BinarySensorEntityDescription.""" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True + key: str = "state" value: Callable[[str], bool | None] = lambda _: None -SENSOR_DEVICE_TYPE = [ATTR_DEVICE_DOOR_SENSOR] +SENSOR_DEVICE_TYPE = [ + ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_MOTION_SENSOR, + ATTR_DEVICE_LEAK_SENSOR, +] SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( YoLinkBinarySensorEntityDescription( - key="state", icon="mdi:door", device_class=BinarySensorDeviceClass.DOOR, - name="state", + name="State", value=lambda value: value == "open", exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], ), + YoLinkBinarySensorEntityDescription( + device_class=BinarySensorDeviceClass.MOTION, + name="Motion", + value=lambda value: value == "alert", + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_MOTION_SENSOR], + ), + YoLinkBinarySensorEntityDescription( + name="Leak", + icon="mdi:water", + device_class=BinarySensorDeviceClass.MOISTURE, + value=lambda value: value == "alert", + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_LEAK_SENSOR], + ), ) diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index 1804838e8b3..303fd684c02 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -14,3 +14,6 @@ ATTR_CLIENT = "client" ATTR_MQTT_CLIENT = "mqtt_client" ATTR_DEVICE_ID = "deviceId" ATTR_DEVICE_DOOR_SENSOR = "DoorSensor" +ATTR_DEVICE_TH_SENSOR = "THSensor" +ATTR_DEVICE_MOTION_SENSOR = "MotionSensor" +ATTR_DEVICE_LEAK_SENSOR = "LeakSensor" diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index d0072145c19..a89934154e9 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -5,6 +5,6 @@ "documentation": "https://www.home-assistant.io/integrations/yolink", "requirements": ["yolink-api==0.0.5"], "dependencies": ["auth", "application_credentials"], - "codeowners": ["@YoSmart-Inc"], + "codeowners": ["@matrixd2"], "iot_class": "cloud_push" } diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index 075cfc24179..e33772c24be 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -13,12 +13,18 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE +from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import percentage -from .const import ATTR_COORDINATOR, ATTR_DEVICE_DOOR_SENSOR, DOMAIN +from .const import ( + ATTR_COORDINATOR, + ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_MOTION_SENSOR, + ATTR_DEVICE_TH_SENSOR, + DOMAIN, +) from .coordinator import YoLinkCoordinator from .entity import YoLinkEntity @@ -49,11 +55,32 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( value=lambda value: percentage.ordered_list_item_to_percentage( [1, 2, 3, 4], value ), - exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], + exists_fn=lambda device: device.device_type + in [ATTR_DEVICE_DOOR_SENSOR, ATTR_DEVICE_TH_SENSOR, ATTR_DEVICE_MOTION_SENSOR], + ), + YoLinkSensorEntityDescription( + key="humidity", + device_class=SensorDeviceClass.HUMIDITY, + native_unit_of_measurement=PERCENTAGE, + name="Humidity", + state_class=SensorStateClass.MEASUREMENT, + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_TH_SENSOR], + ), + YoLinkSensorEntityDescription( + key="temperature", + device_class=SensorDeviceClass.TEMPERATURE, + native_unit_of_measurement=TEMP_CELSIUS, + name="Temperature", + state_class=SensorStateClass.MEASUREMENT, + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_TH_SENSOR], ), ) -SENSOR_DEVICE_TYPE = [ATTR_DEVICE_DOOR_SENSOR] +SENSOR_DEVICE_TYPE = [ + ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_MOTION_SENSOR, + ATTR_DEVICE_TH_SENSOR, +] async def async_setup_entry( From ad5dbae42557809c6f46dcafdc3d5f561c25fbfc Mon Sep 17 00:00:00 2001 From: jrester <31157644+jrester@users.noreply.github.com> Date: Fri, 20 May 2022 21:53:43 +0200 Subject: [PATCH 658/930] Fix reauthentication for powerwall integration (#72174) --- .../components/powerwall/__init__.py | 30 ++------- homeassistant/components/powerwall/const.py | 1 - homeassistant/components/powerwall/models.py | 1 - tests/components/powerwall/test_init.py | 66 +++++++++++++++++++ 4 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 tests/components/powerwall/test_init.py diff --git a/homeassistant/components/powerwall/__init__.py b/homeassistant/components/powerwall/__init__.py index af75ace8bd4..31e249ec806 100644 --- a/homeassistant/components/powerwall/__init__.py +++ b/homeassistant/components/powerwall/__init__.py @@ -29,7 +29,6 @@ from .const import ( POWERWALL_API_CHANGED, POWERWALL_COORDINATOR, POWERWALL_HTTP_SESSION, - POWERWALL_LOGIN_FAILED_COUNT, UPDATE_INTERVAL, ) from .models import PowerwallBaseInfo, PowerwallData, PowerwallRuntimeData @@ -40,8 +39,6 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] _LOGGER = logging.getLogger(__name__) -MAX_LOGIN_FAILURES = 5 - API_CHANGED_ERROR_BODY = ( "It seems like your powerwall uses an unsupported version. " "Please update the software of your powerwall or if it is " @@ -68,29 +65,15 @@ class PowerwallDataManager: self.runtime_data = runtime_data self.power_wall = power_wall - @property - def login_failed_count(self) -> int: - """Return the current number of failed logins.""" - return self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT] - @property def api_changed(self) -> int: """Return true if the api has changed out from under us.""" return self.runtime_data[POWERWALL_API_CHANGED] - def _increment_failed_logins(self) -> None: - self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT] += 1 - - def _clear_failed_logins(self) -> None: - self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT] = 0 - def _recreate_powerwall_login(self) -> None: """Recreate the login on auth failure.""" - http_session = self.runtime_data[POWERWALL_HTTP_SESSION] - http_session.close() - http_session = requests.Session() - self.runtime_data[POWERWALL_HTTP_SESSION] = http_session - self.power_wall = Powerwall(self.ip_address, http_session=http_session) + if self.power_wall.is_authenticated(): + self.power_wall.logout() self.power_wall.login(self.password or "") async def async_update_data(self) -> PowerwallData: @@ -121,17 +104,15 @@ class PowerwallDataManager: raise UpdateFailed("The powerwall api has changed") from err except AccessDeniedError as err: if attempt == 1: - self._increment_failed_logins() + # failed to authenticate => the credentials must be wrong raise ConfigEntryAuthFailed from err if self.password is None: raise ConfigEntryAuthFailed from err - raise UpdateFailed( - f"Login attempt {self.login_failed_count}/{MAX_LOGIN_FAILURES} failed, will retry: {err}" - ) from err + _LOGGER.debug("Access denied, trying to reauthenticate") + # there is still an attempt left to authenticate, so we continue in the loop except APIError as err: raise UpdateFailed(f"Updated failed due to {err}, will retry") from err else: - self._clear_failed_logins() return data raise RuntimeError("unreachable") @@ -174,7 +155,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: api_changed=False, base_info=base_info, http_session=http_session, - login_failed_count=0, coordinator=None, ) diff --git a/homeassistant/components/powerwall/const.py b/homeassistant/components/powerwall/const.py index b1791c4300e..9df710e2df4 100644 --- a/homeassistant/components/powerwall/const.py +++ b/homeassistant/components/powerwall/const.py @@ -7,7 +7,6 @@ POWERWALL_BASE_INFO: Final = "base_info" POWERWALL_COORDINATOR: Final = "coordinator" POWERWALL_API_CHANGED: Final = "api_changed" POWERWALL_HTTP_SESSION: Final = "http_session" -POWERWALL_LOGIN_FAILED_COUNT: Final = "login_failed_count" UPDATE_INTERVAL = 30 diff --git a/homeassistant/components/powerwall/models.py b/homeassistant/components/powerwall/models.py index 6f8ccb98459..522dc2806bf 100644 --- a/homeassistant/components/powerwall/models.py +++ b/homeassistant/components/powerwall/models.py @@ -45,7 +45,6 @@ class PowerwallRuntimeData(TypedDict): """Run time data for the powerwall.""" coordinator: DataUpdateCoordinator | None - login_failed_count: int base_info: PowerwallBaseInfo api_changed: bool http_session: Session diff --git a/tests/components/powerwall/test_init.py b/tests/components/powerwall/test_init.py new file mode 100644 index 00000000000..d5fa92fc23f --- /dev/null +++ b/tests/components/powerwall/test_init.py @@ -0,0 +1,66 @@ +"""Tests for the PowerwallDataManager.""" + +import datetime +from unittest.mock import MagicMock, patch + +from tesla_powerwall import AccessDeniedError, LoginResponse + +from homeassistant.components.powerwall.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD +from homeassistant.core import HomeAssistant +from homeassistant.util.dt import utcnow + +from .mocks import _mock_powerwall_with_fixtures + +from tests.common import MockConfigEntry, async_fire_time_changed + + +async def test_update_data_reauthenticate_on_access_denied(hass: HomeAssistant): + """Test if _update_data of PowerwallDataManager reauthenticates on AccessDeniedError.""" + + mock_powerwall = await _mock_powerwall_with_fixtures(hass) + # login responses for the different tests: + # 1. login success on entry setup + # 2. login success after reauthentication + # 3. login failure after reauthentication + mock_powerwall.login = MagicMock(name="login", return_value=LoginResponse({})) + mock_powerwall.get_charge = MagicMock(name="get_charge", return_value=90.0) + mock_powerwall.is_authenticated = MagicMock( + name="is_authenticated", return_value=True + ) + mock_powerwall.logout = MagicMock(name="logout") + + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4", CONF_PASSWORD: "password"} + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.powerwall.config_flow.Powerwall", + return_value=mock_powerwall, + ), patch( + "homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + mock_powerwall.login.reset_mock(return_value=True) + mock_powerwall.get_charge.side_effect = [AccessDeniedError("test"), 90.0] + + async_fire_time_changed(hass, utcnow() + datetime.timedelta(minutes=1)) + await hass.async_block_till_done() + flows = hass.config_entries.flow.async_progress(DOMAIN) + assert len(flows) == 0 + + mock_powerwall.login.reset_mock() + mock_powerwall.login.side_effect = AccessDeniedError("test") + mock_powerwall.get_charge.side_effect = [AccessDeniedError("test"), 90.0] + + async_fire_time_changed(hass, utcnow() + datetime.timedelta(minutes=1)) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.LOADED + + flows = hass.config_entries.flow.async_progress(DOMAIN) + assert len(flows) == 1 + reauth_flow = flows[0] + assert reauth_flow["context"]["source"] == "reauth" From 267266c7c353d4005cc03da59f6d9cd3bad14f3e Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Fri, 20 May 2022 16:16:01 -0400 Subject: [PATCH 659/930] Adds UP Chime support for UniFi Protect (#71874) Co-authored-by: J. Nick Koston --- .../components/unifiprotect/button.py | 18 ++- .../components/unifiprotect/const.py | 1 + homeassistant/components/unifiprotect/data.py | 3 +- .../components/unifiprotect/entity.py | 6 +- .../components/unifiprotect/number.py | 15 ++ .../components/unifiprotect/sensor.py | 11 ++ .../components/unifiprotect/services.py | 147 ++++++++++++------ .../components/unifiprotect/services.yaml | 25 +++ tests/components/unifiprotect/conftest.py | 12 ++ .../unifiprotect/fixtures/sample_chime.json | 48 ++++++ tests/components/unifiprotect/test_button.py | 61 +++++--- tests/components/unifiprotect/test_init.py | 57 ++++++- .../components/unifiprotect/test_services.py | 75 ++++++++- 13 files changed, 398 insertions(+), 81 deletions(-) create mode 100644 tests/components/unifiprotect/fixtures/sample_chime.json diff --git a/homeassistant/components/unifiprotect/button.py b/homeassistant/components/unifiprotect/button.py index fe82c0afbe0..3728b6b4224 100644 --- a/homeassistant/components/unifiprotect/button.py +++ b/homeassistant/components/unifiprotect/button.py @@ -43,6 +43,22 @@ ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = ( ), ) +CHIME_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = ( + ProtectButtonEntityDescription( + key="play", + name="Play Chime", + device_class=DEVICE_CLASS_CHIME_BUTTON, + icon="mdi:play", + ufp_press="play", + ), + ProtectButtonEntityDescription( + key="play_buzzer", + name="Play Buzzer", + icon="mdi:play", + ufp_press="play_buzzer", + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -53,7 +69,7 @@ async def async_setup_entry( data: ProtectData = hass.data[DOMAIN][entry.entry_id] entities: list[ProtectDeviceEntity] = async_all_device_entities( - data, ProtectButton, all_descs=ALL_DEVICE_BUTTONS + data, ProtectButton, all_descs=ALL_DEVICE_BUTTONS, chime_descs=CHIME_BUTTONS ) async_add_entities(entities) diff --git a/homeassistant/components/unifiprotect/const.py b/homeassistant/components/unifiprotect/const.py index 0fe4ca98afa..3ba22e6b85b 100644 --- a/homeassistant/components/unifiprotect/const.py +++ b/homeassistant/components/unifiprotect/const.py @@ -38,6 +38,7 @@ DEVICES_THAT_ADOPT = { ModelType.VIEWPORT, ModelType.SENSOR, ModelType.DOORLOCK, + ModelType.CHIME, } DEVICES_WITH_ENTITIES = DEVICES_THAT_ADOPT | {ModelType.NVR} DEVICES_FOR_SUBSCRIBE = DEVICES_WITH_ENTITIES | {ModelType.EVENT} diff --git a/homeassistant/components/unifiprotect/data.py b/homeassistant/components/unifiprotect/data.py index e20f956acd2..371c1c7831b 100644 --- a/homeassistant/components/unifiprotect/data.py +++ b/homeassistant/components/unifiprotect/data.py @@ -12,10 +12,9 @@ from pyunifiprotect.data import ( Event, Liveview, ModelType, - ProtectAdoptableDeviceModel, - ProtectDeviceModel, WSSubscriptionMessage, ) +from pyunifiprotect.data.base import ProtectAdoptableDeviceModel, ProtectDeviceModel from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback diff --git a/homeassistant/components/unifiprotect/entity.py b/homeassistant/components/unifiprotect/entity.py index 45e52db5963..f8ceaeec9e6 100644 --- a/homeassistant/components/unifiprotect/entity.py +++ b/homeassistant/components/unifiprotect/entity.py @@ -8,6 +8,7 @@ from typing import Any from pyunifiprotect.data import ( NVR, Camera, + Chime, Doorlock, Event, Light, @@ -42,7 +43,7 @@ def _async_device_entities( entities: list[ProtectDeviceEntity] = [] for device in data.get_by_types({model_type}): - assert isinstance(device, (Camera, Light, Sensor, Viewer, Doorlock)) + assert isinstance(device, (Camera, Light, Sensor, Viewer, Doorlock, Chime)) for description in descs: if description.ufp_required_field: required_field = get_nested_attr(device, description.ufp_required_field) @@ -75,6 +76,7 @@ def async_all_device_entities( sense_descs: Sequence[ProtectRequiredKeysMixin] | None = None, viewer_descs: Sequence[ProtectRequiredKeysMixin] | None = None, lock_descs: Sequence[ProtectRequiredKeysMixin] | None = None, + chime_descs: Sequence[ProtectRequiredKeysMixin] | None = None, all_descs: Sequence[ProtectRequiredKeysMixin] | None = None, ) -> list[ProtectDeviceEntity]: """Generate a list of all the device entities.""" @@ -84,6 +86,7 @@ def async_all_device_entities( sense_descs = list(sense_descs or []) + all_descs viewer_descs = list(viewer_descs or []) + all_descs lock_descs = list(lock_descs or []) + all_descs + chime_descs = list(chime_descs or []) + all_descs return ( _async_device_entities(data, klass, ModelType.CAMERA, camera_descs) @@ -91,6 +94,7 @@ def async_all_device_entities( + _async_device_entities(data, klass, ModelType.SENSOR, sense_descs) + _async_device_entities(data, klass, ModelType.VIEWPORT, viewer_descs) + _async_device_entities(data, klass, ModelType.DOORLOCK, lock_descs) + + _async_device_entities(data, klass, ModelType.CHIME, chime_descs) ) diff --git a/homeassistant/components/unifiprotect/number.py b/homeassistant/components/unifiprotect/number.py index 58ae0502caa..4ebdd17f5c9 100644 --- a/homeassistant/components/unifiprotect/number.py +++ b/homeassistant/components/unifiprotect/number.py @@ -149,6 +149,20 @@ DOORLOCK_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( ), ) +CHIME_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( + ProtectNumberEntityDescription( + key="volume", + name="Volume", + icon="mdi:speaker", + entity_category=EntityCategory.CONFIG, + ufp_min=0, + ufp_max=100, + ufp_step=1, + ufp_value="volume", + ufp_set_method="set_volume", + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -164,6 +178,7 @@ async def async_setup_entry( light_descs=LIGHT_NUMBERS, sense_descs=SENSE_NUMBERS, lock_descs=DOORLOCK_NUMBERS, + chime_descs=CHIME_NUMBERS, ) async_add_entities(entities) diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index c3b49fbdf9d..c30cc7fb80f 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -450,6 +450,16 @@ MOTION_TRIP_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ), ) +CHIME_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( + ProtectSensorEntityDescription( + key="last_ring", + name="Last Ring", + device_class=SensorDeviceClass.TIMESTAMP, + icon="mdi:bell", + ufp_value="last_ring", + ), +) + async def async_setup_entry( hass: HomeAssistant, @@ -466,6 +476,7 @@ async def async_setup_entry( sense_descs=SENSE_SENSORS, light_descs=LIGHT_SENSORS, lock_descs=DOORLOCK_SENSORS, + chime_descs=CHIME_SENSORS, ) entities += _async_motion_entities(data) entities += _async_nvr_entities(data) diff --git a/homeassistant/components/unifiprotect/services.py b/homeassistant/components/unifiprotect/services.py index a149f516d2f..f8aa446f857 100644 --- a/homeassistant/components/unifiprotect/services.py +++ b/homeassistant/components/unifiprotect/services.py @@ -10,25 +10,32 @@ from pyunifiprotect.api import ProtectApiClient from pyunifiprotect.exceptions import BadRequest import voluptuous as vol +from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import ATTR_DEVICE_ID +from homeassistant.const import ATTR_DEVICE_ID, Platform from homeassistant.core import HomeAssistant, ServiceCall, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv, device_registry as dr +from homeassistant.helpers import ( + config_validation as cv, + device_registry as dr, + entity_registry as er, +) from homeassistant.helpers.service import async_extract_referenced_entity_ids +from homeassistant.util.read_only_dict import ReadOnlyDict from .const import ATTR_MESSAGE, DOMAIN from .data import ProtectData -from .utils import _async_unifi_mac_from_hass SERVICE_ADD_DOORBELL_TEXT = "add_doorbell_text" SERVICE_REMOVE_DOORBELL_TEXT = "remove_doorbell_text" SERVICE_SET_DEFAULT_DOORBELL_TEXT = "set_default_doorbell_text" +SERVICE_SET_CHIME_PAIRED = "set_chime_paired_doorbells" ALL_GLOBAL_SERIVCES = [ SERVICE_ADD_DOORBELL_TEXT, SERVICE_REMOVE_DOORBELL_TEXT, SERVICE_SET_DEFAULT_DOORBELL_TEXT, + SERVICE_SET_CHIME_PAIRED, ] DOORBELL_TEXT_SCHEMA = vol.All( @@ -41,70 +48,68 @@ DOORBELL_TEXT_SCHEMA = vol.All( cv.has_at_least_one_key(ATTR_DEVICE_ID), ) +CHIME_PAIRED_SCHEMA = vol.All( + vol.Schema( + { + **cv.ENTITY_SERVICE_FIELDS, + "doorbells": cv.TARGET_SERVICE_FIELDS, + }, + ), + cv.has_at_least_one_key(ATTR_DEVICE_ID), +) -def _async_all_ufp_instances(hass: HomeAssistant) -> list[ProtectApiClient]: - """All active UFP instances.""" - return [ - data.api for data in hass.data[DOMAIN].values() if isinstance(data, ProtectData) - ] + +def _async_ufp_instance_for_config_entry_ids( + hass: HomeAssistant, config_entry_ids: set[str] +) -> ProtectApiClient | None: + """Find the UFP instance for the config entry ids.""" + domain_data = hass.data[DOMAIN] + for config_entry_id in config_entry_ids: + if config_entry_id in domain_data: + protect_data: ProtectData = domain_data[config_entry_id] + return protect_data.api + return None @callback -def _async_get_macs_for_device(device_entry: dr.DeviceEntry) -> list[str]: - return [ - _async_unifi_mac_from_hass(cval) - for ctype, cval in device_entry.connections - if ctype == dr.CONNECTION_NETWORK_MAC - ] - - -@callback -def _async_get_ufp_instances( - hass: HomeAssistant, device_id: str -) -> tuple[dr.DeviceEntry, ProtectApiClient]: +def _async_get_ufp_instance(hass: HomeAssistant, device_id: str) -> ProtectApiClient: device_registry = dr.async_get(hass) if not (device_entry := device_registry.async_get(device_id)): raise HomeAssistantError(f"No device found for device id: {device_id}") if device_entry.via_device_id is not None: - return _async_get_ufp_instances(hass, device_entry.via_device_id) + return _async_get_ufp_instance(hass, device_entry.via_device_id) - macs = _async_get_macs_for_device(device_entry) - ufp_instances = [ - i for i in _async_all_ufp_instances(hass) if i.bootstrap.nvr.mac in macs - ] + config_entry_ids = device_entry.config_entries + if ufp_instance := _async_ufp_instance_for_config_entry_ids(hass, config_entry_ids): + return ufp_instance - if not ufp_instances: - # should not be possible unless user manually enters a bad device ID - raise HomeAssistantError( # pragma: no cover - f"No UniFi Protect NVR found for device ID: {device_id}" - ) - - return device_entry, ufp_instances[0] + raise HomeAssistantError(f"No device found for device id: {device_id}") @callback def _async_get_protect_from_call( hass: HomeAssistant, call: ServiceCall -) -> list[tuple[dr.DeviceEntry, ProtectApiClient]]: - referenced = async_extract_referenced_entity_ids(hass, call) - - instances: list[tuple[dr.DeviceEntry, ProtectApiClient]] = [] - for device_id in referenced.referenced_devices: - instances.append(_async_get_ufp_instances(hass, device_id)) - - return instances +) -> set[ProtectApiClient]: + return { + _async_get_ufp_instance(hass, device_id) + for device_id in async_extract_referenced_entity_ids( + hass, call + ).referenced_devices + } -async def _async_call_nvr( - instances: list[tuple[dr.DeviceEntry, ProtectApiClient]], +async def _async_service_call_nvr( + hass: HomeAssistant, + call: ServiceCall, method: str, *args: Any, **kwargs: Any, ) -> None: + instances = _async_get_protect_from_call(hass, call) try: await asyncio.gather( - *(getattr(i.bootstrap.nvr, method)(*args, **kwargs) for _, i in instances) + *(getattr(i.bootstrap.nvr, method)(*args, **kwargs) for i in instances) ) except (BadRequest, ValidationError) as err: raise HomeAssistantError(str(err)) from err @@ -113,22 +118,61 @@ async def _async_call_nvr( async def add_doorbell_text(hass: HomeAssistant, call: ServiceCall) -> None: """Add a custom doorbell text message.""" message: str = call.data[ATTR_MESSAGE] - instances = _async_get_protect_from_call(hass, call) - await _async_call_nvr(instances, "add_custom_doorbell_message", message) + await _async_service_call_nvr(hass, call, "add_custom_doorbell_message", message) async def remove_doorbell_text(hass: HomeAssistant, call: ServiceCall) -> None: """Remove a custom doorbell text message.""" message: str = call.data[ATTR_MESSAGE] - instances = _async_get_protect_from_call(hass, call) - await _async_call_nvr(instances, "remove_custom_doorbell_message", message) + await _async_service_call_nvr(hass, call, "remove_custom_doorbell_message", message) async def set_default_doorbell_text(hass: HomeAssistant, call: ServiceCall) -> None: """Set the default doorbell text message.""" message: str = call.data[ATTR_MESSAGE] - instances = _async_get_protect_from_call(hass, call) - await _async_call_nvr(instances, "set_default_doorbell_message", message) + await _async_service_call_nvr(hass, call, "set_default_doorbell_message", message) + + +@callback +def _async_unique_id_to_ufp_device_id(unique_id: str) -> str: + """Extract the UFP device id from the registry entry unique id.""" + return unique_id.split("_")[0] + + +async def set_chime_paired_doorbells(hass: HomeAssistant, call: ServiceCall) -> None: + """Set paired doorbells on chime.""" + ref = async_extract_referenced_entity_ids(hass, call) + entity_registry = er.async_get(hass) + + entity_id = ref.indirectly_referenced.pop() + chime_button = entity_registry.async_get(entity_id) + assert chime_button is not None + assert chime_button.device_id is not None + chime_ufp_device_id = _async_unique_id_to_ufp_device_id(chime_button.unique_id) + + instance = _async_get_ufp_instance(hass, chime_button.device_id) + chime = instance.bootstrap.chimes[chime_ufp_device_id] + + call.data = ReadOnlyDict(call.data.get("doorbells") or {}) + doorbell_refs = async_extract_referenced_entity_ids(hass, call) + doorbell_ids: set[str] = set() + for camera_id in doorbell_refs.referenced | doorbell_refs.indirectly_referenced: + doorbell_sensor = entity_registry.async_get(camera_id) + assert doorbell_sensor is not None + if ( + doorbell_sensor.platform != DOMAIN + or doorbell_sensor.domain != Platform.BINARY_SENSOR + or doorbell_sensor.original_device_class + != BinarySensorDeviceClass.OCCUPANCY + ): + continue + doorbell_ufp_device_id = _async_unique_id_to_ufp_device_id( + doorbell_sensor.unique_id + ) + camera = instance.bootstrap.cameras[doorbell_ufp_device_id] + doorbell_ids.add(camera.id) + chime.camera_ids = sorted(doorbell_ids) + await chime.save_device() def async_setup_services(hass: HomeAssistant) -> None: @@ -149,6 +193,11 @@ def async_setup_services(hass: HomeAssistant) -> None: functools.partial(set_default_doorbell_text, hass), DOORBELL_TEXT_SCHEMA, ), + ( + SERVICE_SET_CHIME_PAIRED, + functools.partial(set_chime_paired_doorbells, hass), + CHIME_PAIRED_SCHEMA, + ), ] for name, method, schema in services: if hass.services.has_service(DOMAIN, name): diff --git a/homeassistant/components/unifiprotect/services.yaml b/homeassistant/components/unifiprotect/services.yaml index 410dcae4699..037c10627ad 100644 --- a/homeassistant/components/unifiprotect/services.yaml +++ b/homeassistant/components/unifiprotect/services.yaml @@ -84,3 +84,28 @@ set_doorbell_message: step: 1 mode: slider unit_of_measurement: minutes +set_chime_paired_doorbells: + name: Set Chime Paired Doorbells + description: > + Use to set the paired doorbell(s) with a smart chime. + fields: + device_id: + name: Chime + description: The Chimes to link to the doorbells to + required: true + selector: + device: + integration: unifiprotect + entity: + device_class: unifiprotect__chime_button + doorbells: + name: Doorbells + description: The Doorbells to link to the chime + example: "binary_sensor.front_doorbell_doorbell" + required: false + selector: + target: + entity: + integration: unifiprotect + domain: binary_sensor + device_class: occupancy diff --git a/tests/components/unifiprotect/conftest.py b/tests/components/unifiprotect/conftest.py index c83c6eb72cc..6eeef02e817 100644 --- a/tests/components/unifiprotect/conftest.py +++ b/tests/components/unifiprotect/conftest.py @@ -14,6 +14,7 @@ import pytest from pyunifiprotect.data import ( NVR, Camera, + Chime, Doorlock, Light, Liveview, @@ -49,6 +50,7 @@ class MockBootstrap: liveviews: dict[str, Any] events: dict[str, Any] doorlocks: dict[str, Any] + chimes: dict[str, Any] def reset_objects(self) -> None: """Reset all devices on bootstrap for tests.""" @@ -59,6 +61,7 @@ class MockBootstrap: self.liveviews = {} self.events = {} self.doorlocks = {} + self.chimes = {} def process_ws_packet(self, msg: WSSubscriptionMessage) -> None: """Fake process method for tests.""" @@ -127,6 +130,7 @@ def mock_bootstrap_fixture(mock_nvr: NVR): liveviews={}, events={}, doorlocks={}, + chimes={}, ) @@ -220,6 +224,14 @@ def mock_doorlock(): return Doorlock.from_unifi_dict(**data) +@pytest.fixture +def mock_chime(): + """Mock UniFi Protect Chime device.""" + + data = json.loads(load_fixture("sample_chime.json", integration=DOMAIN)) + return Chime.from_unifi_dict(**data) + + @pytest.fixture def now(): """Return datetime object that will be consistent throughout test.""" diff --git a/tests/components/unifiprotect/fixtures/sample_chime.json b/tests/components/unifiprotect/fixtures/sample_chime.json new file mode 100644 index 00000000000..975cfcebaea --- /dev/null +++ b/tests/components/unifiprotect/fixtures/sample_chime.json @@ -0,0 +1,48 @@ +{ + "mac": "BEEEE2FBE413", + "host": "192.168.144.146", + "connectionHost": "192.168.234.27", + "type": "UP Chime", + "name": "Xaorvu Tvsv", + "upSince": 1651882870009, + "uptime": 567870, + "lastSeen": 1652450740009, + "connectedSince": 1652448904587, + "state": "CONNECTED", + "hardwareRevision": null, + "firmwareVersion": "1.3.4", + "latestFirmwareVersion": "1.3.4", + "firmwareBuild": "58bd350.220401.1859", + "isUpdating": false, + "isAdopting": false, + "isAdopted": true, + "isAdoptedByOther": false, + "isProvisioned": false, + "isRebooting": false, + "isSshEnabled": true, + "canAdopt": false, + "isAttemptingToConnect": false, + "volume": 100, + "isProbingForWifi": false, + "apMac": null, + "apRssi": null, + "elementInfo": null, + "lastRing": 1652116059940, + "isWirelessUplinkEnabled": true, + "wiredConnectionState": { + "phyRate": null + }, + "wifiConnectionState": { + "channel": null, + "frequency": null, + "phyRate": null, + "signalQuality": 100, + "signalStrength": -44, + "ssid": null + }, + "cameraIds": [], + "id": "cf1a330397c08f919d02bd7c", + "isConnected": true, + "marketName": "UP Chime", + "modelKey": "chime" +} diff --git a/tests/components/unifiprotect/test_button.py b/tests/components/unifiprotect/test_button.py index 64677dd1d77..f3b76cb7abb 100644 --- a/tests/components/unifiprotect/test_button.py +++ b/tests/components/unifiprotect/test_button.py @@ -5,7 +5,7 @@ from __future__ import annotations from unittest.mock import AsyncMock import pytest -from pyunifiprotect.data import Camera +from pyunifiprotect.data.devices import Chime from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, Platform @@ -15,42 +15,39 @@ from homeassistant.helpers import entity_registry as er from .conftest import MockEntityFixture, assert_entity_counts, enable_entity -@pytest.fixture(name="camera") -async def camera_fixture( - hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera +@pytest.fixture(name="chime") +async def chime_fixture( + hass: HomeAssistant, mock_entry: MockEntityFixture, mock_chime: Chime ): """Fixture for a single camera for testing the button platform.""" - camera_obj = mock_camera.copy(deep=True) - camera_obj._api = mock_entry.api - camera_obj.channels[0]._api = mock_entry.api - camera_obj.channels[1]._api = mock_entry.api - camera_obj.channels[2]._api = mock_entry.api - camera_obj.name = "Test Camera" + chime_obj = mock_chime.copy(deep=True) + chime_obj._api = mock_entry.api + chime_obj.name = "Test Chime" - mock_entry.api.bootstrap.cameras = { - camera_obj.id: camera_obj, + mock_entry.api.bootstrap.chimes = { + chime_obj.id: chime_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() - assert_entity_counts(hass, Platform.BUTTON, 1, 0) + assert_entity_counts(hass, Platform.BUTTON, 3, 2) - return (camera_obj, "button.test_camera_reboot_device") + return chime_obj -async def test_button( +async def test_reboot_button( hass: HomeAssistant, mock_entry: MockEntityFixture, - camera: tuple[Camera, str], + chime: Chime, ): """Test button entity.""" mock_entry.api.reboot_device = AsyncMock() - unique_id = f"{camera[0].id}_reboot" - entity_id = camera[1] + unique_id = f"{chime.id}_reboot" + entity_id = "button.test_chime_reboot_device" entity_registry = er.async_get(hass) entity = entity_registry.async_get(entity_id) @@ -67,3 +64,31 @@ async def test_button( "button", "press", {ATTR_ENTITY_ID: entity_id}, blocking=True ) mock_entry.api.reboot_device.assert_called_once() + + +async def test_chime_button( + hass: HomeAssistant, + mock_entry: MockEntityFixture, + chime: Chime, +): + """Test button entity.""" + + mock_entry.api.play_speaker = AsyncMock() + + unique_id = f"{chime.id}_play" + entity_id = "button.test_chime_play_chime" + + entity_registry = er.async_get(hass) + entity = entity_registry.async_get(entity_id) + assert entity + assert not entity.disabled + assert entity.unique_id == unique_id + + state = hass.states.get(entity_id) + assert state + assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION + + await hass.services.async_call( + "button", "press", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + mock_entry.api.play_speaker.assert_called_once() diff --git a/tests/components/unifiprotect/test_init.py b/tests/components/unifiprotect/test_init.py index 53588984e25..95c2ee0b511 100644 --- a/tests/components/unifiprotect/test_init.py +++ b/tests/components/unifiprotect/test_init.py @@ -202,11 +202,11 @@ async def test_migrate_reboot_button( registry = er.async_get(hass) registry.async_get_or_create( - Platform.BUTTON, Platform.BUTTON, light1.id, config_entry=mock_entry.entry + Platform.BUTTON, DOMAIN, light1.id, config_entry=mock_entry.entry ) registry.async_get_or_create( Platform.BUTTON, - Platform.BUTTON, + DOMAIN, f"{light2.id}_reboot", config_entry=mock_entry.entry, ) @@ -218,24 +218,67 @@ async def test_migrate_reboot_button( assert mock_entry.api.update.called assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac + buttons = [] + for entity in er.async_entries_for_config_entry( + registry, mock_entry.entry.entry_id + ): + if entity.domain == Platform.BUTTON.value: + buttons.append(entity) + print(entity.entity_id) + assert len(buttons) == 2 + + assert registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device") is None assert registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device_2") is None - light = registry.async_get(f"{Platform.BUTTON}.test_light_1_reboot_device") + light = registry.async_get(f"{Platform.BUTTON}.unifiprotect_lightid1") assert light is not None assert light.unique_id == f"{light1.id}_reboot" + assert registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device") is None assert registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device_2") is None - light = registry.async_get(f"{Platform.BUTTON}.test_light_2_reboot_device") + light = registry.async_get(f"{Platform.BUTTON}.unifiprotect_lightid2_reboot") assert light is not None assert light.unique_id == f"{light2.id}_reboot" + +async def test_migrate_reboot_button_no_device( + hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light +): + """Test migrating unique ID of reboot button if UniFi Protect device ID changed.""" + + light1 = mock_light.copy() + light1._api = mock_entry.api + light1.name = "Test Light 1" + light1.id = "lightid1" + + mock_entry.api.bootstrap.lights = { + light1.id: light1, + } + mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap) + + registry = er.async_get(hass) + registry.async_get_or_create( + Platform.BUTTON, DOMAIN, "lightid2", config_entry=mock_entry.entry + ) + + await hass.config_entries.async_setup(mock_entry.entry.entry_id) + await hass.async_block_till_done() + + assert mock_entry.entry.state == ConfigEntryState.LOADED + assert mock_entry.api.update.called + assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac + buttons = [] for entity in er.async_entries_for_config_entry( registry, mock_entry.entry.entry_id ): - if entity.platform == Platform.BUTTON.value: + if entity.domain == Platform.BUTTON.value: buttons.append(entity) assert len(buttons) == 2 + light = registry.async_get(f"{Platform.BUTTON}.unifiprotect_lightid2") + assert light is not None + assert light.unique_id == "lightid2" + async def test_migrate_reboot_button_fail( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light @@ -255,14 +298,14 @@ async def test_migrate_reboot_button_fail( registry = er.async_get(hass) registry.async_get_or_create( Platform.BUTTON, - Platform.BUTTON, + DOMAIN, light1.id, config_entry=mock_entry.entry, suggested_object_id=light1.name, ) registry.async_get_or_create( Platform.BUTTON, - Platform.BUTTON, + DOMAIN, f"{light1.id}_reboot", config_entry=mock_entry.entry, suggested_object_id=light1.name, diff --git a/tests/components/unifiprotect/test_services.py b/tests/components/unifiprotect/test_services.py index 0230bc3d36c..22f7fdd1a6c 100644 --- a/tests/components/unifiprotect/test_services.py +++ b/tests/components/unifiprotect/test_services.py @@ -5,19 +5,21 @@ from __future__ import annotations from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import Light +from pyunifiprotect.data import Camera, Light, ModelType +from pyunifiprotect.data.devices import Chime from pyunifiprotect.exceptions import BadRequest from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN from homeassistant.components.unifiprotect.services import ( SERVICE_ADD_DOORBELL_TEXT, SERVICE_REMOVE_DOORBELL_TEXT, + SERVICE_SET_CHIME_PAIRED, SERVICE_SET_DEFAULT_DOORBELL_TEXT, ) -from homeassistant.const import ATTR_DEVICE_ID +from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, entity_registry as er from .conftest import MockEntityFixture @@ -143,3 +145,70 @@ async def test_set_default_doorbell_text( blocking=True, ) nvr.set_default_doorbell_message.assert_called_once_with("Test Message") + + +async def test_set_chime_paired_doorbells( + hass: HomeAssistant, + mock_entry: MockEntityFixture, + mock_chime: Chime, + mock_camera: Camera, +): + """Test set_chime_paired_doorbells.""" + + mock_entry.api.update_device = AsyncMock() + + mock_chime._api = mock_entry.api + mock_chime.name = "Test Chime" + mock_chime._initial_data = mock_chime.dict() + mock_entry.api.bootstrap.chimes = { + mock_chime.id: mock_chime, + } + + camera1 = mock_camera.copy() + camera1.id = "cameraid1" + camera1.name = "Test Camera 1" + camera1._api = mock_entry.api + camera1.channels[0]._api = mock_entry.api + camera1.channels[1]._api = mock_entry.api + camera1.channels[2]._api = mock_entry.api + camera1.feature_flags.has_chime = True + + camera2 = mock_camera.copy() + camera2.id = "cameraid2" + camera2.name = "Test Camera 2" + camera2._api = mock_entry.api + camera2.channels[0]._api = mock_entry.api + camera2.channels[1]._api = mock_entry.api + camera2.channels[2]._api = mock_entry.api + camera2.feature_flags.has_chime = True + + mock_entry.api.bootstrap.cameras = { + camera1.id: camera1, + camera2.id: camera2, + } + + await hass.config_entries.async_setup(mock_entry.entry.entry_id) + await hass.async_block_till_done() + + registry = er.async_get(hass) + chime_entry = registry.async_get("button.test_chime_play_chime") + camera_entry = registry.async_get("binary_sensor.test_camera_2_doorbell") + assert chime_entry is not None + assert camera_entry is not None + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_CHIME_PAIRED, + { + ATTR_DEVICE_ID: chime_entry.device_id, + "doorbells": { + ATTR_ENTITY_ID: ["binary_sensor.test_camera_1_doorbell"], + ATTR_DEVICE_ID: [camera_entry.device_id], + }, + }, + blocking=True, + ) + + mock_entry.api.update_device.assert_called_once_with( + ModelType.CHIME, mock_chime.id, {"cameraIds": [camera1.id, camera2.id]} + ) From ceb8bb47455764aeccaa0a7a6e0fc2c296d48ea6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 21 May 2022 00:22:49 +0000 Subject: [PATCH 660/930] [ci skip] Translation update --- .../components/abode/translations/nl.json | 6 +-- .../accuweather/translations/nl.json | 4 +- .../components/adguard/translations/nl.json | 4 +- .../components/aemet/translations/ko.json | 9 +++++ .../components/aemet/translations/nl.json | 2 +- .../components/agent_dvr/translations/nl.json | 2 +- .../components/airly/translations/nl.json | 2 +- .../components/airvisual/translations/nl.json | 12 +++--- .../components/airzone/translations/nl.json | 2 +- .../aladdin_connect/translations/nl.json | 4 +- .../alarm_control_panel/translations/nl.json | 8 ++-- .../components/almond/translations/nl.json | 4 +- .../components/ambee/translations/ko.json | 18 +++++++++ .../components/ambee/translations/nl.json | 2 +- .../ambiclimate/translations/nl.json | 4 +- .../components/apple_tv/translations/ko.json | 1 + .../components/apple_tv/translations/nl.json | 6 +-- .../components/arcam_fmj/translations/nl.json | 2 +- .../components/asuswrt/translations/nl.json | 2 +- .../components/atag/translations/nl.json | 4 +- .../components/august/translations/nl.json | 2 +- .../aussie_broadband/translations/nl.json | 4 +- .../components/awair/translations/nl.json | 4 +- .../components/axis/translations/nl.json | 2 +- .../azure_devops/translations/nl.json | 2 +- .../azure_event_hub/translations/nl.json | 2 +- .../components/baf/translations/pl.json | 7 +++- .../components/blebox/translations/nl.json | 2 +- .../components/bond/translations/nl.json | 2 +- .../components/bosch_shc/translations/ko.json | 37 +++++++++++++++++++ .../components/bosch_shc/translations/nl.json | 4 +- .../components/braviatv/translations/nl.json | 2 +- .../components/broadlink/translations/nl.json | 6 +-- .../components/brother/translations/nl.json | 2 +- .../components/brunt/translations/nl.json | 4 +- .../buienradar/translations/ko.json | 29 +++++++++++++++ .../buienradar/translations/nl.json | 4 +- .../components/camera/translations/nl.json | 2 +- .../components/canary/translations/nl.json | 4 +- .../components/cast/translations/ko.json | 5 +++ .../components/cast/translations/nl.json | 4 +- .../climacell/translations/sensor.ko.json | 7 ++++ .../climacell/translations/sensor.nl.json | 2 +- .../components/climate/translations/nl.json | 4 +- .../cloudflare/translations/nl.json | 4 +- .../configurator/translations/nl.json | 2 +- .../components/cover/translations/nl.json | 2 +- .../components/cpuspeed/translations/nl.json | 4 +- .../components/daikin/translations/nl.json | 2 +- .../components/deconz/translations/nl.json | 2 +- .../demo/translations/select.nl.json | 2 +- .../components/denonavr/translations/ko.json | 1 + .../components/denonavr/translations/nl.json | 2 +- .../devolo_home_control/translations/nl.json | 2 +- .../devolo_home_network/translations/ko.json | 3 ++ .../devolo_home_network/translations/nl.json | 2 +- .../dialogflow/translations/nl.json | 2 +- .../components/discord/translations/nl.json | 2 +- .../components/dlna_dmr/translations/ko.json | 3 ++ .../components/dlna_dmr/translations/nl.json | 2 +- .../components/dlna_dms/translations/ko.json | 3 ++ .../components/dlna_dms/translations/nl.json | 4 +- .../components/dunehd/translations/nl.json | 4 +- .../components/ecobee/translations/nl.json | 2 +- .../components/efergy/translations/nl.json | 2 +- .../components/elkm1/translations/ko.json | 8 ++++ .../components/elkm1/translations/nl.json | 2 +- .../components/elmax/translations/ko.json | 11 ++++++ .../components/enocean/translations/nl.json | 2 +- .../enphase_envoy/translations/nl.json | 2 +- .../components/esphome/translations/nl.json | 4 +- .../components/ezviz/translations/ko.json | 13 +++++-- .../components/fibaro/translations/nl.json | 2 +- .../fireservicerota/translations/nl.json | 4 +- .../fjaraskupan/translations/nl.json | 2 +- .../flick_electric/translations/nl.json | 2 +- .../components/flo/translations/nl.json | 2 +- .../components/flume/translations/ko.json | 10 ++++- .../components/flume/translations/nl.json | 2 +- .../flunearyou/translations/nl.json | 2 +- .../components/flux_led/translations/nl.json | 2 +- .../components/fritz/translations/ko.json | 24 +++++++++++- .../components/fritz/translations/nl.json | 6 +-- .../components/fritzbox/translations/nl.json | 4 +- .../garages_amsterdam/translations/ko.json | 8 ++++ .../components/generic/translations/nl.json | 4 +- .../geocaching/translations/nl.json | 16 ++++---- .../components/geofency/translations/nl.json | 2 +- .../components/gios/translations/nl.json | 2 +- .../components/glances/translations/nl.json | 2 +- .../components/goalzero/translations/nl.json | 2 +- .../components/gogogate2/translations/ko.json | 1 + .../components/gogogate2/translations/nl.json | 4 +- .../components/goodwe/translations/nl.json | 2 +- .../components/google/translations/ko.json | 5 +++ .../components/google/translations/nl.json | 14 +++---- .../google_travel_time/translations/nl.json | 2 +- .../components/gpslogger/translations/nl.json | 2 +- .../components/gree/translations/nl.json | 4 +- .../growatt_server/translations/ko.json | 3 ++ .../components/guardian/translations/ko.json | 3 ++ .../components/guardian/translations/nl.json | 2 +- .../hisense_aehw4a1/translations/nl.json | 2 +- .../components/hive/translations/nl.json | 2 +- .../home_connect/translations/nl.json | 8 ++-- .../home_plus_control/translations/nl.json | 12 +++--- .../homekit_controller/translations/ko.json | 2 + .../homekit_controller/translations/nl.json | 2 +- .../homematicip_cloud/translations/nl.json | 4 +- .../huawei_lte/translations/ko.json | 3 +- .../components/hue/translations/nl.json | 4 +- .../translations/nl.json | 2 +- .../hvv_departures/translations/nl.json | 2 +- .../components/hyperion/translations/nl.json | 4 +- .../components/icloud/translations/nl.json | 4 +- .../components/ifttt/translations/nl.json | 2 +- .../components/insteon/translations/ko.json | 1 + .../components/insteon/translations/nl.json | 6 +-- .../intellifire/translations/nl.json | 2 +- .../components/ios/translations/nl.json | 4 +- .../components/iotawatt/translations/nl.json | 2 +- .../components/ipma/translations/nl.json | 2 +- .../components/ipp/translations/nl.json | 2 +- .../components/iss/translations/nl.json | 2 +- .../components/isy994/translations/nl.json | 4 +- .../components/izone/translations/nl.json | 2 +- .../components/jellyfin/translations/nl.json | 2 +- .../components/juicenet/translations/nl.json | 2 +- .../kaleidescape/translations/nl.json | 4 +- .../keenetic_ndms2/translations/ko.json | 3 +- .../components/knx/translations/ko.json | 5 +++ .../components/knx/translations/nl.json | 2 +- .../components/knx/translations/pl.json | 2 +- .../components/kodi/translations/nl.json | 6 +-- .../components/konnected/translations/nl.json | 2 +- .../components/kraken/translations/nl.json | 4 +- .../components/kulersky/translations/nl.json | 4 +- .../launch_library/translations/nl.json | 2 +- .../components/lifx/translations/nl.json | 2 +- .../components/litejet/translations/nl.json | 2 +- .../litterrobot/translations/sensor.pl.json | 2 +- .../components/local_ip/translations/nl.json | 4 +- .../components/locative/translations/nl.json | 4 +- .../logi_circle/translations/nl.json | 4 +- .../components/lookin/translations/nl.json | 2 +- .../lutron_caseta/translations/nl.json | 4 +- .../components/lyric/translations/ko.json | 1 + .../components/lyric/translations/nl.json | 10 ++--- .../components/mailgun/translations/nl.json | 2 +- .../components/mazda/translations/nl.json | 2 +- .../media_player/translations/nl.json | 2 +- .../components/met/translations/ko.json | 3 ++ .../meteo_france/translations/nl.json | 2 +- .../components/metoffice/translations/nl.json | 2 +- .../components/min_max/translations/nl.json | 4 +- .../modem_callerid/translations/nl.json | 2 +- .../modern_forms/translations/ko.json | 20 ++++++++++ .../components/moon/translations/nl.json | 4 +- .../motion_blinds/translations/nl.json | 2 +- .../components/motioneye/translations/nl.json | 2 +- .../components/mqtt/translations/ko.json | 6 ++- .../components/mqtt/translations/nl.json | 6 +-- .../components/myq/translations/ko.json | 4 ++ .../components/myq/translations/nl.json | 2 +- .../components/mysensors/translations/ko.json | 1 + .../components/nam/translations/ko.json | 1 + .../components/nam/translations/nl.json | 2 +- .../components/nanoleaf/translations/nl.json | 2 +- .../components/neato/translations/nl.json | 12 +++--- .../components/nest/translations/nl.json | 18 ++++----- .../components/netatmo/translations/nl.json | 16 ++++---- .../components/netgear/translations/nl.json | 2 +- .../components/nina/translations/nl.json | 2 +- .../nmap_tracker/translations/nl.json | 2 +- .../components/notion/translations/nl.json | 4 +- .../components/nuki/translations/ko.json | 1 + .../components/nuki/translations/nl.json | 4 +- .../components/nzbget/translations/nl.json | 2 +- .../components/omnilogic/translations/nl.json | 4 +- .../ondilo_ico/translations/nl.json | 6 +-- .../components/onvif/translations/nl.json | 2 +- .../opentherm_gw/translations/nl.json | 2 +- .../components/openuv/translations/nl.json | 2 +- .../openweathermap/translations/nl.json | 8 ++-- .../components/overkiz/translations/nl.json | 2 +- .../overkiz/translations/select.ko.json | 7 ++++ .../panasonic_viera/translations/nl.json | 6 +-- .../philips_js/translations/nl.json | 2 +- .../components/pi_hole/translations/nl.json | 4 +- .../components/picnic/translations/nl.json | 2 +- .../components/plaato/translations/nl.json | 4 +- .../components/plex/translations/nl.json | 6 +-- .../components/plugwise/translations/nl.json | 4 +- .../plum_lightpad/translations/nl.json | 2 +- .../components/point/translations/nl.json | 10 ++--- .../components/powerwall/translations/nl.json | 2 +- .../components/profiler/translations/nl.json | 2 +- .../components/prosegur/translations/nl.json | 2 +- .../components/ps4/translations/nl.json | 4 +- .../pure_energie/translations/ko.json | 9 +++++ .../components/pvoutput/translations/nl.json | 4 +- .../radio_browser/translations/nl.json | 2 +- .../rainmachine/translations/ko.json | 1 + .../components/renault/translations/nl.json | 4 +- .../components/rfxtrx/translations/nl.json | 2 +- .../components/ridwell/translations/nl.json | 4 +- .../components/risco/translations/nl.json | 8 ++-- .../components/roku/translations/nl.json | 2 +- .../components/roon/translations/nl.json | 4 +- .../components/rpi_power/translations/nl.json | 2 +- .../rtsp_to_webrtc/translations/nl.json | 2 +- .../components/samsungtv/translations/ko.json | 11 +++++- .../components/samsungtv/translations/nl.json | 4 +- .../components/season/translations/nl.json | 2 +- .../components/sense/translations/ko.json | 3 ++ .../components/sense/translations/nl.json | 4 +- .../components/sensibo/translations/ko.json | 7 ++++ .../components/sensibo/translations/nl.json | 2 +- .../components/sensor/translations/ko.json | 1 + .../components/senz/translations/nl.json | 12 +++--- .../components/sharkiq/translations/nl.json | 4 +- .../components/shelly/translations/ja.json | 1 + .../components/shelly/translations/no.json | 1 + .../components/shelly/translations/pl.json | 1 + .../components/sia/translations/ko.json | 12 ++++++ .../components/sia/translations/nl.json | 2 +- .../simplisafe/translations/nl.json | 4 +- .../components/siren/translations/pl.json | 3 ++ .../components/slack/translations/pl.json | 8 +++- .../components/sleepiq/translations/nl.json | 4 +- .../components/slimproto/translations/nl.json | 2 +- .../components/sma/translations/ko.json | 6 ++- .../components/sma/translations/nl.json | 4 +- .../components/smappee/translations/nl.json | 6 +-- .../smart_meter_texas/translations/nl.json | 4 +- .../components/smarttub/translations/ko.json | 1 + .../components/smarttub/translations/nl.json | 4 +- .../components/sms/translations/nl.json | 4 +- .../components/soma/translations/nl.json | 6 +-- .../components/somfy/translations/nl.json | 8 ++-- .../components/sonarr/translations/nl.json | 6 +-- .../components/songpal/translations/nl.json | 2 +- .../components/sonos/translations/ko.json | 1 + .../components/sonos/translations/nl.json | 2 +- .../speedtestdotnet/translations/nl.json | 2 +- .../components/spider/translations/nl.json | 2 +- .../components/spotify/translations/nl.json | 4 +- .../squeezebox/translations/nl.json | 2 +- .../srp_energy/translations/nl.json | 2 +- .../steam_online/translations/nl.json | 4 +- .../components/steamist/translations/nl.json | 2 +- .../components/sun/translations/nl.json | 4 +- .../switcher_kis/translations/nl.json | 4 +- .../synology_dsm/translations/nl.json | 12 +++--- .../system_bridge/translations/ko.json | 31 ++++++++++++++++ .../system_bridge/translations/nl.json | 2 +- .../components/tailscale/translations/nl.json | 2 +- .../tankerkoenig/translations/nl.json | 2 +- .../components/tasmota/translations/nl.json | 2 +- .../components/tautulli/translations/nl.json | 4 +- .../tellduslive/translations/nl.json | 2 +- .../components/tile/translations/nl.json | 4 +- .../components/tolo/translations/nl.json | 2 +- .../tomorrowio/translations/sensor.ko.json | 7 ++++ .../components/toon/translations/nl.json | 6 +-- .../totalconnect/translations/nl.json | 4 +- .../components/tplink/translations/nl.json | 2 +- .../components/traccar/translations/nl.json | 2 +- .../components/tractive/translations/nl.json | 2 +- .../components/tradfri/translations/nl.json | 2 +- .../trafikverket_ferry/translations/nl.json | 2 +- .../trafikverket_train/translations/ko.json | 11 ++++++ .../trafikverket_train/translations/nl.json | 2 +- .../tuya/translations/select.nl.json | 8 ++-- .../components/twilio/translations/nl.json | 4 +- .../ukraine_alarm/translations/nl.json | 6 +-- .../components/unifi/translations/nl.json | 2 +- .../unifiprotect/translations/ko.json | 10 ++++- .../components/upb/translations/nl.json | 2 +- .../components/uptime/translations/nl.json | 4 +- .../uptimerobot/translations/nl.json | 4 +- .../components/vallox/translations/nl.json | 2 +- .../components/venstar/translations/nl.json | 4 +- .../components/verisure/translations/nl.json | 2 +- .../components/vesync/translations/nl.json | 2 +- .../components/vicare/translations/nl.json | 2 +- .../components/vizio/translations/nl.json | 6 +-- .../vlc_telnet/translations/nl.json | 6 +-- .../components/vulcan/translations/ko.json | 3 ++ .../components/wallbox/translations/nl.json | 2 +- .../water_heater/translations/nl.json | 4 +- .../components/watttime/translations/nl.json | 4 +- .../waze_travel_time/translations/ko.json | 20 +++++++++- .../waze_travel_time/translations/nl.json | 2 +- .../components/weather/translations/nl.json | 2 +- .../components/webostv/translations/nl.json | 2 +- .../components/wemo/translations/nl.json | 2 +- .../components/withings/translations/nl.json | 8 ++-- .../components/wiz/translations/ko.json | 9 +++++ .../components/xbox/translations/nl.json | 8 ++-- .../xiaomi_aqara/translations/nl.json | 2 +- .../xiaomi_miio/translations/nl.json | 6 +-- .../yale_smart_alarm/translations/nl.json | 2 +- .../yamaha_musiccast/translations/nl.json | 2 +- .../components/yeelight/translations/ko.json | 4 ++ .../components/yeelight/translations/nl.json | 2 +- .../components/yolink/translations/nl.json | 16 ++++---- .../components/yolink/translations/pl.json | 25 +++++++++++++ .../components/zerproc/translations/nl.json | 4 +- .../components/zha/translations/nl.json | 2 +- .../zodiac/translations/sensor.nl.json | 2 +- .../zoneminder/translations/nl.json | 6 +-- .../components/zwave_js/translations/ko.json | 7 +++- .../components/zwave_js/translations/nl.json | 2 +- 314 files changed, 899 insertions(+), 473 deletions(-) create mode 100644 homeassistant/components/ambee/translations/ko.json create mode 100644 homeassistant/components/bosch_shc/translations/ko.json create mode 100644 homeassistant/components/buienradar/translations/ko.json create mode 100644 homeassistant/components/climacell/translations/sensor.ko.json create mode 100644 homeassistant/components/elmax/translations/ko.json create mode 100644 homeassistant/components/garages_amsterdam/translations/ko.json create mode 100644 homeassistant/components/growatt_server/translations/ko.json create mode 100644 homeassistant/components/modern_forms/translations/ko.json create mode 100644 homeassistant/components/overkiz/translations/select.ko.json create mode 100644 homeassistant/components/pure_energie/translations/ko.json create mode 100644 homeassistant/components/sensibo/translations/ko.json create mode 100644 homeassistant/components/sia/translations/ko.json create mode 100644 homeassistant/components/siren/translations/pl.json create mode 100644 homeassistant/components/system_bridge/translations/ko.json create mode 100644 homeassistant/components/tomorrowio/translations/sensor.ko.json create mode 100644 homeassistant/components/trafikverket_train/translations/ko.json create mode 100644 homeassistant/components/wiz/translations/ko.json create mode 100644 homeassistant/components/yolink/translations/pl.json diff --git a/homeassistant/components/abode/translations/nl.json b/homeassistant/components/abode/translations/nl.json index 7b6a8b5aace..0b6b67c363c 100644 --- a/homeassistant/components/abode/translations/nl.json +++ b/homeassistant/components/abode/translations/nl.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol", - "single_instance_allowed": "Slechts een enkele configuratie van Abode is toegestaan." + "reauth_successful": "Herauthenticatie geslaagd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -26,7 +26,7 @@ "user": { "data": { "password": "Wachtwoord", - "username": "E-mailadres" + "username": "E-mail" }, "title": "Vul uw Abode-inloggegevens in" } diff --git a/homeassistant/components/accuweather/translations/nl.json b/homeassistant/components/accuweather/translations/nl.json index e83b3d8355a..df5c71dcf45 100644 --- a/homeassistant/components/accuweather/translations/nl.json +++ b/homeassistant/components/accuweather/translations/nl.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "create_entry": { "default": "Sommige sensoren zijn standaard niet ingeschakeld. U kunt ze inschakelen in het entiteitenregister na de integratieconfiguratie.\nWeersvoorspelling is niet standaard ingeschakeld. U kunt deze inschakelen in de integratieopties." }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_api_key": "API-sleutel", + "invalid_api_key": "Ongeldige API-sleutel", "requests_exceeded": "Het toegestane aantal verzoeken aan de Accuweather API is overschreden. U moet wachten of de API-sleutel wijzigen." }, "step": { diff --git a/homeassistant/components/adguard/translations/nl.json b/homeassistant/components/adguard/translations/nl.json index 9f991cbd407..38a64997e73 100644 --- a/homeassistant/components/adguard/translations/nl.json +++ b/homeassistant/components/adguard/translations/nl.json @@ -17,9 +17,9 @@ "host": "Host", "password": "Wachtwoord", "port": "Poort", - "ssl": "AdGuard Home maakt gebruik van een SSL certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", - "verify_ssl": "AdGuard Home maakt gebruik van een goed certificaat" + "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "description": "Stel uw AdGuard Home-instantie in om toezicht en controle mogelijk te maken." } diff --git a/homeassistant/components/aemet/translations/ko.json b/homeassistant/components/aemet/translations/ko.json index 23ca054f5be..06d713755b6 100644 --- a/homeassistant/components/aemet/translations/ko.json +++ b/homeassistant/components/aemet/translations/ko.json @@ -17,5 +17,14 @@ "description": "AEMET OpenData \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\ub97c \uc124\uc815\ud569\ub2c8\ub2e4. API \ud0a4\ub97c \uc0dd\uc131\ud558\ub824\uba74 https://opendata.aemet.es/centrodedescargas/altaUsuario \ub85c \uc774\ub3d9\ud574\uc8fc\uc138\uc694" } } + }, + "options": { + "step": { + "init": { + "data": { + "station_updates": "AEMET \uae30\uc0c1 \uad00\uce21\uc18c\uc5d0\uc11c \ub370\uc774\ud130 \uc218\uc9d1" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/aemet/translations/nl.json b/homeassistant/components/aemet/translations/nl.json index 5479d206fa9..95062239ac2 100644 --- a/homeassistant/components/aemet/translations/nl.json +++ b/homeassistant/components/aemet/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "invalid_api_key": "Ongeldige API-sleutel" diff --git a/homeassistant/components/agent_dvr/translations/nl.json b/homeassistant/components/agent_dvr/translations/nl.json index 7c679f66c11..3999d18f7a7 100644 --- a/homeassistant/components/agent_dvr/translations/nl.json +++ b/homeassistant/components/agent_dvr/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken" }, "step": { diff --git a/homeassistant/components/airly/translations/nl.json b/homeassistant/components/airly/translations/nl.json index 7cfa0c5776f..9bfce7e7708 100644 --- a/homeassistant/components/airly/translations/nl.json +++ b/homeassistant/components/airly/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "invalid_api_key": "Ongeldige API-sleutel", diff --git a/homeassistant/components/airvisual/translations/nl.json b/homeassistant/components/airvisual/translations/nl.json index ddbcc6e6009..c857a51ba61 100644 --- a/homeassistant/components/airvisual/translations/nl.json +++ b/homeassistant/components/airvisual/translations/nl.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd. of Node/Pro IDis al geregistreerd.", - "reauth_successful": "Herauthenticatie was succesvol" + "already_configured": "Locatie is al geconfigureerd of Node/Pro IDis al geregistreerd.", + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", - "general_error": "Er is een onbekende fout opgetreden.", + "general_error": "Onverwachte fout", "invalid_api_key": "Ongeldige API-sleutel", "location_not_found": "Locatie niet gevonden" }, @@ -25,15 +25,15 @@ "api_key": "API-sleutel", "city": "Stad", "country": "Land", - "state": "staat" + "state": "status" }, "description": "Gebruik de AirVisual-cloud-API om een stad/staat/land te bewaken.", "title": "Configureer een geografie" }, "node_pro": { "data": { - "ip_address": "IP adres/hostname van component", - "password": "Wachtwoord van component" + "ip_address": "Host", + "password": "Wachtwoord" }, "description": "Monitor een persoonlijke AirVisual-eenheid. Het wachtwoord kan worden opgehaald uit de gebruikersinterface van het apparaat.", "title": "Configureer een AirVisual Node / Pro" diff --git a/homeassistant/components/airzone/translations/nl.json b/homeassistant/components/airzone/translations/nl.json index 0e84f756de1..e182d71963d 100644 --- a/homeassistant/components/airzone/translations/nl.json +++ b/homeassistant/components/airzone/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_system_id": "Ongeldige Airzone systeem ID" }, "step": { diff --git a/homeassistant/components/aladdin_connect/translations/nl.json b/homeassistant/components/aladdin_connect/translations/nl.json index 314df5712f8..90d1fb67029 100644 --- a/homeassistant/components/aladdin_connect/translations/nl.json +++ b/homeassistant/components/aladdin_connect/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "De Aladdin Connect-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/alarm_control_panel/translations/nl.json b/homeassistant/components/alarm_control_panel/translations/nl.json index 5527101589b..a2a0eefb149 100644 --- a/homeassistant/components/alarm_control_panel/translations/nl.json +++ b/homeassistant/components/alarm_control_panel/translations/nl.json @@ -28,15 +28,15 @@ "state": { "_": { "armed": "Ingeschakeld", - "armed_away": "Ingeschakeld voor vertrek", + "armed_away": "Ingeschakeld afwezig", "armed_custom_bypass": "Ingeschakeld met overbrugging", - "armed_home": "Ingeschakeld voor thuis", - "armed_night": "Ingeschakeld voor 's nachts", + "armed_home": "Ingeschakeld thuis", + "armed_night": "Ingeschakeld nacht", "armed_vacation": "Vakantie ingeschakeld", "arming": "Schakelt in", "disarmed": "Uitgeschakeld", "disarming": "Schakelt uit", - "pending": "In wacht", + "pending": "In afwachting", "triggered": "Gaat af" } }, diff --git a/homeassistant/components/almond/translations/nl.json b/homeassistant/components/almond/translations/nl.json index 4c507cfab69..e548206e23e 100644 --- a/homeassistant/components/almond/translations/nl.json +++ b/homeassistant/components/almond/translations/nl.json @@ -2,8 +2,8 @@ "config": { "abort": { "cannot_connect": "Kan geen verbinding maken", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { diff --git a/homeassistant/components/ambee/translations/ko.json b/homeassistant/components/ambee/translations/ko.json new file mode 100644 index 00000000000..574b7cd0976 --- /dev/null +++ b/homeassistant/components/ambee/translations/ko.json @@ -0,0 +1,18 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_api_key": "API \ud0a4\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "api_key": "API \ud0a4", + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4", + "name": "\uc774\ub984" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ambee/translations/nl.json b/homeassistant/components/ambee/translations/nl.json index 837e39a72d7..5d356037652 100644 --- a/homeassistant/components/ambee/translations/nl.json +++ b/homeassistant/components/ambee/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/ambiclimate/translations/nl.json b/homeassistant/components/ambiclimate/translations/nl.json index 6d3b3822224..e0679131de4 100644 --- a/homeassistant/components/ambiclimate/translations/nl.json +++ b/homeassistant/components/ambiclimate/translations/nl.json @@ -3,10 +3,10 @@ "abort": { "access_token": "Onbekende fout bij het genereren van een toegangstoken.", "already_configured": "Account is al geconfigureerd", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen." + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "error": { "follow_link": "Gelieve de link te volgen en te verifi\u00ebren voordat u op Verzenden drukt.", diff --git a/homeassistant/components/apple_tv/translations/ko.json b/homeassistant/components/apple_tv/translations/ko.json index fb2373eac4f..9bddc31c56f 100644 --- a/homeassistant/components/apple_tv/translations/ko.json +++ b/homeassistant/components/apple_tv/translations/ko.json @@ -4,6 +4,7 @@ "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "backoff": "\uae30\uae30\uac00 \ud604\uc7ac \ud398\uc5b4\ub9c1 \uc694\uccad\uc744 \uc218\ub77d\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4(\uc798\ubabb\ub41c PIN \ucf54\ub4dc\ub97c \ub108\ubb34 \ub9ce\uc774 \uc785\ub825\ud588\uc744 \uc218 \uc788\uc74c). \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", "device_did_not_pair": "\uae30\uae30\uc5d0\uc11c \ud398\uc5b4\ub9c1 \ud504\ub85c\uc138\uc2a4\ub97c \uc644\ub8cc\ud558\ub824\uace0 \uc2dc\ub3c4\ud558\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", + "inconsistent_device": "\uc7a5\uce58\uac80\uc0c9 \uc911\uc5d0 \ud574\ub2f9 \ud504\ub85c\ud1a0\ucf5c\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774\ub294 \uc77c\ubc18\uc801\uc73c\ub85c \uba40\ud2f0\uce90\uc2a4\ud2b8 DNS(Zeroconf)\uc5d0 \ubb38\uc81c\uac00 \uc788\uc74c\uc744 \ub098\ud0c0\ub0c5\ub2c8\ub2e4. \uc7a5\uce58\ub97c \ub2e4\uc2dc \ucd94\uac00\ud574 \ubcf4\uc2ed\uc2dc\uc624.", "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, diff --git a/homeassistant/components/apple_tv/translations/nl.json b/homeassistant/components/apple_tv/translations/nl.json index 36bebc02761..c7adfa6d756 100644 --- a/homeassistant/components/apple_tv/translations/nl.json +++ b/homeassistant/components/apple_tv/translations/nl.json @@ -2,14 +2,14 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "backoff": "Het apparaat accepteert op dit moment geen koppelingsverzoeken (u heeft mogelijk te vaak een ongeldige pincode ingevoerd), probeer het later opnieuw.", "device_did_not_pair": "Er is geen poging gedaan om het koppelingsproces te voltooien vanaf het apparaat.", "device_not_found": "Apparaat werd niet gevonden tijdens het zoeken, probeer het opnieuw toe te voegen.", "inconsistent_device": "De verwachte protocollen zijn niet gevonden tijdens het zoeken. Dit wijst gewoonlijk op een probleem met multicast DNS (Zeroconf). Probeer het apparaat opnieuw toe te voegen.", "ipv6_not_supported": "IPv6 wordt niet ondersteund.", "no_devices_found": "Geen apparaten gevonden op het netwerk", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "setup_failed": "Kan het apparaat niet instellen.", "unknown": "Onverwachte fout" }, @@ -31,7 +31,7 @@ }, "pair_with_pin": { "data": { - "pin": "PIN-code" + "pin": "Pincode" }, "description": "Koppelen is vereist voor het `{protocol}` protocol. Voer de PIN-code in die op het scherm wordt getoond. Beginnende nullen moeten worden weggelaten, d.w.z. voer 123 in als de getoonde code 0123 is.", "title": "Koppelen" diff --git a/homeassistant/components/arcam_fmj/translations/nl.json b/homeassistant/components/arcam_fmj/translations/nl.json index 45a7be867b9..76d3c93739a 100644 --- a/homeassistant/components/arcam_fmj/translations/nl.json +++ b/homeassistant/components/arcam_fmj/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken" }, "error": { diff --git a/homeassistant/components/asuswrt/translations/nl.json b/homeassistant/components/asuswrt/translations/nl.json index 17be650bf07..5e85888843a 100644 --- a/homeassistant/components/asuswrt/translations/nl.json +++ b/homeassistant/components/asuswrt/translations/nl.json @@ -16,7 +16,7 @@ "user": { "data": { "host": "Host", - "mode": "Mode", + "mode": "Modus", "name": "Naam", "password": "Wachtwoord", "port": "Poort (leeg laten voor protocol standaard)", diff --git a/homeassistant/components/atag/translations/nl.json b/homeassistant/components/atag/translations/nl.json index 98200dd3f6e..da53d96e88f 100644 --- a/homeassistant/components/atag/translations/nl.json +++ b/homeassistant/components/atag/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Er kan slechts \u00e9\u00e9n Atag-apparaat worden toegevoegd aan Home Assistant " + "already_configured": "Apparaat is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -11,7 +11,7 @@ "user": { "data": { "host": "Host", - "port": "Poort " + "port": "Poort" }, "title": "Verbinding maken met het apparaat" } diff --git a/homeassistant/components/august/translations/nl.json b/homeassistant/components/august/translations/nl.json index 2d9a4202f74..e2bbc4d636d 100644 --- a/homeassistant/components/august/translations/nl.json +++ b/homeassistant/components/august/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/aussie_broadband/translations/nl.json b/homeassistant/components/aussie_broadband/translations/nl.json index ec95145c67b..efb553cca25 100644 --- a/homeassistant/components/aussie_broadband/translations/nl.json +++ b/homeassistant/components/aussie_broadband/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Account is al geconfigureerd", "no_services_found": "Er zijn geen services gevonden voor dit account", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -16,7 +16,7 @@ "password": "Wachtwoord" }, "description": "Update wachtwoord voor {username}", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "service": { "data": { diff --git a/homeassistant/components/awair/translations/nl.json b/homeassistant/components/awair/translations/nl.json index d41b85cc09b..1b58405af32 100644 --- a/homeassistant/components/awair/translations/nl.json +++ b/homeassistant/components/awair/translations/nl.json @@ -2,8 +2,8 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "no_devices_found": "Geen apparaten op het netwerk gevonden", - "reauth_successful": "Herauthenticatie was succesvol" + "no_devices_found": "Geen apparaten gevonden op het netwerk", + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_access_token": "Ongeldig toegangstoken", diff --git a/homeassistant/components/axis/translations/nl.json b/homeassistant/components/axis/translations/nl.json index 3b41c1184ba..f51f59f775f 100644 --- a/homeassistant/components/axis/translations/nl.json +++ b/homeassistant/components/axis/translations/nl.json @@ -7,7 +7,7 @@ }, "error": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie" }, diff --git a/homeassistant/components/azure_devops/translations/nl.json b/homeassistant/components/azure_devops/translations/nl.json index a57dd85c495..3267ea89c19 100644 --- a/homeassistant/components/azure_devops/translations/nl.json +++ b/homeassistant/components/azure_devops/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/azure_event_hub/translations/nl.json b/homeassistant/components/azure_event_hub/translations/nl.json index 646d820c2ad..e0aabd1c971 100644 --- a/homeassistant/components/azure_event_hub/translations/nl.json +++ b/homeassistant/components/azure_event_hub/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Service is al geconfigureerd", "cannot_connect": "Verbinding maken met de credentials uit de configuration.yaml is mislukt, verwijder deze uit yaml en gebruik de config flow.", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown": "Verbinding maken met de credentials uit de configuration.yaml is mislukt met een onbekende fout, verwijder deze uit yaml en gebruik de config flow." }, "error": { diff --git a/homeassistant/components/baf/translations/pl.json b/homeassistant/components/baf/translations/pl.json index 5c64afd30a8..b0347c8318c 100644 --- a/homeassistant/components/baf/translations/pl.json +++ b/homeassistant/components/baf/translations/pl.json @@ -1,13 +1,18 @@ { "config": { "abort": { - "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane", + "ipv6_not_supported": "IPv6 nie jest obs\u0142ugiwany." }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "unknown": "Nieoczekiwany b\u0142\u0105d" }, + "flow_title": "{name} - {model} ({ip_address})", "step": { + "discovery_confirm": { + "description": "Czy chcesz skonfigurowa\u0107 {name} - {model} ({ip_address})?" + }, "user": { "data": { "ip_address": "Adres IP" diff --git a/homeassistant/components/blebox/translations/nl.json b/homeassistant/components/blebox/translations/nl.json index 65a775e6f72..a9acfc5f71e 100644 --- a/homeassistant/components/blebox/translations/nl.json +++ b/homeassistant/components/blebox/translations/nl.json @@ -5,7 +5,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "unknown": "Onverwachte fout", "unsupported_version": "BleBox-apparaat heeft verouderde firmware. Upgrade het eerst." }, diff --git a/homeassistant/components/bond/translations/nl.json b/homeassistant/components/bond/translations/nl.json index fcf519d681d..b2ba43c8275 100644 --- a/homeassistant/components/bond/translations/nl.json +++ b/homeassistant/components/bond/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "old_firmware": "Niet-ondersteunde oude firmware op het Bond-apparaat - voer een upgrade uit voordat u doorgaat", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/bosch_shc/translations/ko.json b/homeassistant/components/bosch_shc/translations/ko.json new file mode 100644 index 00000000000..3289cb23ce1 --- /dev/null +++ b/homeassistant/components/bosch_shc/translations/ko.json @@ -0,0 +1,37 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "pairing_failed": "\ud398\uc5b4\ub9c1\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. Bosch Smart Home Controller\uac00 \ud398\uc5b4\ub9c1 \ubaa8\ub4dc(LED \uae5c\ubc15\uc784)\uc774\uace0 \ube44\ubc00\ubc88\ud638\uac00 \uc62c\ubc14\ub978\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", + "session_error": "\uc138\uc158 \uc624\ub958: API\uac00 \ube44\uc815\uc0c1 \uacb0\uacfc\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4.", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "flow_title": "Bosch SHC: {name}", + "step": { + "confirm_discovery": { + "description": "LED\uac00 \uae5c\ubc15\uc774\uae30 \uc2dc\uc791\ud560 \ub54c\uae4c\uc9c0 Bosch Smart Home Controller\uc758 \uc804\uba74 \ubc84\ud2bc\uc744 \ub204\ub974\uc2ed\uc2dc\uc624.\nHome Assistant\ub85c {model} @ {host} \ub97c \uacc4\uc18d \uc124\uc815\ud560 \uc900\ube44\uac00 \ub418\uc168\uc2b5\ub2c8\uae4c?" + }, + "credentials": { + "data": { + "password": "Smart Home Controller\uc758 \ube44\ubc00\ubc88\ud638" + } + }, + "reauth_confirm": { + "description": "bosch_shc \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + }, + "description": "Home Assistant\ub85c \ubaa8\ub2c8\ud130\ub9c1\ud558\uace0 \uc81c\uc5b4\ud560 \uc218 \uc788\ub3c4\ub85d Bosch Smart Home Controller\ub97c \uc124\uc815\ud558\uc2ed\uc2dc\uc624.", + "title": "SHC \uc778\uc99d \ub9e4\uac1c\ubcc0\uc218" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/bosch_shc/translations/nl.json b/homeassistant/components/bosch_shc/translations/nl.json index 50aa82e2c7b..dd134e54c06 100644 --- a/homeassistant/components/bosch_shc/translations/nl.json +++ b/homeassistant/components/bosch_shc/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -23,7 +23,7 @@ }, "reauth_confirm": { "description": "De bosch_shc integratie moet uw account herauthenticeren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/braviatv/translations/nl.json b/homeassistant/components/braviatv/translations/nl.json index f839b83e883..d3696024643 100644 --- a/homeassistant/components/braviatv/translations/nl.json +++ b/homeassistant/components/braviatv/translations/nl.json @@ -12,7 +12,7 @@ "step": { "authorize": { "data": { - "pin": "PIN-code" + "pin": "Pincode" }, "description": "Voer de pincode in die wordt weergegeven op de Sony Bravia tv. \n\nAls de pincode niet wordt weergegeven, moet u de Home Assistant op uw tv afmelden, ga naar: Instellingen -> Netwerk -> Instellingen extern apparaat -> Afmelden extern apparaat.", "title": "Autoriseer Sony Bravia tv" diff --git a/homeassistant/components/broadlink/translations/nl.json b/homeassistant/components/broadlink/translations/nl.json index da75118d5b1..6be9db286e2 100644 --- a/homeassistant/components/broadlink/translations/nl.json +++ b/homeassistant/components/broadlink/translations/nl.json @@ -2,14 +2,14 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "cannot_connect": "Kon niet verbinden", + "already_in_progress": "De configuratie is momenteel al bezig", + "cannot_connect": "Kan geen verbinding maken", "invalid_host": "Ongeldige hostnaam of IP-adres", "not_supported": "Apparaat wordt niet ondersteund", "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_host": "Ongeldige hostnaam of IP-adres", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/brother/translations/nl.json b/homeassistant/components/brother/translations/nl.json index 97c506299a7..2021acef608 100644 --- a/homeassistant/components/brother/translations/nl.json +++ b/homeassistant/components/brother/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Deze printer is al geconfigureerd.", + "already_configured": "Apparaat is al geconfigureerd", "unsupported_model": "Dit printermodel wordt niet ondersteund." }, "error": { diff --git a/homeassistant/components/brunt/translations/nl.json b/homeassistant/components/brunt/translations/nl.json index 86a7df6a585..9ea207d1eae 100644 --- a/homeassistant/components/brunt/translations/nl.json +++ b/homeassistant/components/brunt/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -15,7 +15,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord opnieuw in voor: {username}", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/buienradar/translations/ko.json b/homeassistant/components/buienradar/translations/ko.json new file mode 100644 index 00000000000..8fe5422ea3e --- /dev/null +++ b/homeassistant/components/buienradar/translations/ko.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "error": { + "already_configured": "\uc704\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + }, + "step": { + "user": { + "data": { + "latitude": "\uc704\ub3c4", + "longitude": "\uacbd\ub3c4" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "country_code": "\uce74\uba54\ub77c \uc774\ubbf8\uc9c0\ub97c \ud45c\uc2dc\ud560 \uad6d\uac00\uc758 \uad6d\uac00 \ucf54\ub4dc\uc785\ub2c8\ub2e4.", + "delta": "\uce74\uba54\ub77c \uc774\ubbf8\uc9c0 \uc5c5\ub370\uc774\ud2b8 \uc0ac\uc774\uc758 \uc2dc\uac04 \uac04\uaca9(\ucd08)", + "timeframe": "\uac15\uc218 \uc608\ubcf4\ub97c \uc704\ud574 \ubbf8\ub9ac \ubcfc \uc2dc\uac04(\ubd84)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/buienradar/translations/nl.json b/homeassistant/components/buienradar/translations/nl.json index 0d022f1c61e..c39ee638e08 100644 --- a/homeassistant/components/buienradar/translations/nl.json +++ b/homeassistant/components/buienradar/translations/nl.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/camera/translations/nl.json b/homeassistant/components/camera/translations/nl.json index 976d8e651fb..29dd5038b8f 100644 --- a/homeassistant/components/camera/translations/nl.json +++ b/homeassistant/components/camera/translations/nl.json @@ -3,7 +3,7 @@ "_": { "idle": "Inactief", "recording": "Opnemen", - "streaming": "Streamen" + "streaming": "Streaming" } }, "title": "Camera" diff --git a/homeassistant/components/canary/translations/nl.json b/homeassistant/components/canary/translations/nl.json index ed64af346ea..18cdee2c7a7 100644 --- a/homeassistant/components/canary/translations/nl.json +++ b/homeassistant/components/canary/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n enkele configuratie mogelijk.", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/cast/translations/ko.json b/homeassistant/components/cast/translations/ko.json index 2f1ee52675f..806a05a01f4 100644 --- a/homeassistant/components/cast/translations/ko.json +++ b/homeassistant/components/cast/translations/ko.json @@ -22,6 +22,11 @@ "options": { "error": { "invalid_known_hosts": "\uc54c\ub824\uc9c4 \ud638\uc2a4\ud2b8\ub294 \uc27c\ud45c\ub85c \uad6c\ubd84\ub41c \ud638\uc2a4\ud2b8 \ubaa9\ub85d\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4." + }, + "step": { + "basic_options": { + "description": "\uc54c\ub824\uc9c4 \ud638\uc2a4\ud2b8 - \uce90\uc2a4\ud2b8 \uc7a5\uce58\uc758 \ud638\uc2a4\ud2b8 \uc774\ub984 \ub610\ub294 IP \uc8fc\uc18c\uc758 \uc27c\ud45c\ub85c \uad6c\ubd84\ub41c \ubaa9\ub85d\uc73c\ub85c, mDNS \uac80\uc0c9\uc774 \uc791\ub3d9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0\uc5d0 \uc0ac\uc6a9\ud569\ub2c8\ub2e4" + } } } } \ No newline at end of file diff --git a/homeassistant/components/cast/translations/nl.json b/homeassistant/components/cast/translations/nl.json index 26dc954ef13..b9dba7ef3e4 100644 --- a/homeassistant/components/cast/translations/nl.json +++ b/homeassistant/components/cast/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_known_hosts": "Bekende hosts moet een door komma's gescheiden lijst van hosts zijn." @@ -15,7 +15,7 @@ "title": "Google Cast configuratie" }, "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/climacell/translations/sensor.ko.json b/homeassistant/components/climacell/translations/sensor.ko.json new file mode 100644 index 00000000000..e5ec616959e --- /dev/null +++ b/homeassistant/components/climacell/translations/sensor.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "climacell__precipitation_type": { + "snow": "\ub208" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/climacell/translations/sensor.nl.json b/homeassistant/components/climacell/translations/sensor.nl.json index c457988681b..710198156d1 100644 --- a/homeassistant/components/climacell/translations/sensor.nl.json +++ b/homeassistant/components/climacell/translations/sensor.nl.json @@ -18,7 +18,7 @@ }, "climacell__precipitation_type": { "freezing_rain": "IJzel", - "ice_pellets": "Hagel", + "ice_pellets": "IJskorrels", "none": "Geen", "rain": "Regen", "snow": "Sneeuw" diff --git a/homeassistant/components/climate/translations/nl.json b/homeassistant/components/climate/translations/nl.json index 0237d2bbd9a..016131858a4 100644 --- a/homeassistant/components/climate/translations/nl.json +++ b/homeassistant/components/climate/translations/nl.json @@ -18,8 +18,8 @@ "_": { "auto": "Auto", "cool": "Koelen", - "dry": "Droog", - "fan_only": "Alleen ventilatie", + "dry": "Drogen", + "fan_only": "Alleen ventilator", "heat": "Verwarmen", "heat_cool": "Verwarmen/Koelen", "off": "Uit" diff --git a/homeassistant/components/cloudflare/translations/nl.json b/homeassistant/components/cloudflare/translations/nl.json index 5a1bf188a29..6be4bf1e0a8 100644 --- a/homeassistant/components/cloudflare/translations/nl.json +++ b/homeassistant/components/cloudflare/translations/nl.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "reauth_successful": "Herauthenticatie geslaagd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/configurator/translations/nl.json b/homeassistant/components/configurator/translations/nl.json index d8ad5061e0f..ea1bd002c5d 100644 --- a/homeassistant/components/configurator/translations/nl.json +++ b/homeassistant/components/configurator/translations/nl.json @@ -1,7 +1,7 @@ { "state": { "_": { - "configure": "Configureer", + "configure": "Configureren", "configured": "Geconfigureerd" } }, diff --git a/homeassistant/components/cover/translations/nl.json b/homeassistant/components/cover/translations/nl.json index 80acc874379..31c8c90d2b7 100644 --- a/homeassistant/components/cover/translations/nl.json +++ b/homeassistant/components/cover/translations/nl.json @@ -29,7 +29,7 @@ "state": { "_": { "closed": "Gesloten", - "closing": "Sluiten", + "closing": "Sluitend", "open": "Open", "opening": "Opent", "stopped": "Gestopt" diff --git a/homeassistant/components/cpuspeed/translations/nl.json b/homeassistant/components/cpuspeed/translations/nl.json index c3d09b0b8a6..fab4a86c645 100644 --- a/homeassistant/components/cpuspeed/translations/nl.json +++ b/homeassistant/components/cpuspeed/translations/nl.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "already_configured": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "already_configured": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "not_compatible": "Kan geen CPU-informatie ophalen, deze integratie is niet compatibel met uw systeem" }, "step": { "user": { - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "CPU-snelheid" } } diff --git a/homeassistant/components/daikin/translations/nl.json b/homeassistant/components/daikin/translations/nl.json index 33659797e7a..47b65415437 100644 --- a/homeassistant/components/daikin/translations/nl.json +++ b/homeassistant/components/daikin/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "error": { "api_password": "Ongeldige authenticatie, gebruik API-sleutel of wachtwoord.", diff --git a/homeassistant/components/deconz/translations/nl.json b/homeassistant/components/deconz/translations/nl.json index caa6ca461fc..474973b9594 100644 --- a/homeassistant/components/deconz/translations/nl.json +++ b/homeassistant/components/deconz/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Bridge is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "no_bridges": "Geen deCONZ bridges ontdekt", "no_hardware_available": "Geen radiohardware aangesloten op deCONZ", "updated_instance": "DeCONZ-instantie bijgewerkt met nieuw host-adres" diff --git a/homeassistant/components/demo/translations/select.nl.json b/homeassistant/components/demo/translations/select.nl.json index 4312d8c4d34..1d7e7f9be13 100644 --- a/homeassistant/components/demo/translations/select.nl.json +++ b/homeassistant/components/demo/translations/select.nl.json @@ -2,7 +2,7 @@ "state": { "demo__speed": { "light_speed": "Lichtsnelheid", - "ludicrous_speed": "Lachwekkende snelheid", + "ludicrous_speed": "Belachelijke snelheid", "ridiculous_speed": "Belachelijke snelheid" } } diff --git a/homeassistant/components/denonavr/translations/ko.json b/homeassistant/components/denonavr/translations/ko.json index 573011186aa..4a880f6757f 100644 --- a/homeassistant/components/denonavr/translations/ko.json +++ b/homeassistant/components/denonavr/translations/ko.json @@ -34,6 +34,7 @@ "init": { "data": { "show_all_sources": "\ubaa8\ub4e0 \uc785\ub825\uc18c\uc2a4 \ud45c\uc2dc", + "update_audyssey": "Audyssey \uc124\uc815 \uc5c5\ub370\uc774\ud2b8", "zone2": "Zone 2 \uc124\uc815", "zone3": "Zone 3 \uc124\uc815" }, diff --git a/homeassistant/components/denonavr/translations/nl.json b/homeassistant/components/denonavr/translations/nl.json index f3683561686..6ebb0d845d7 100644 --- a/homeassistant/components/denonavr/translations/nl.json +++ b/homeassistant/components/denonavr/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Verbinding mislukt, probeer het opnieuw, de stroom- en ethernetkabels loskoppelen en opnieuw aansluiten kan helpen", "not_denonavr_manufacturer": "Geen Denon AVR Netwerk Receiver, ontdekte fabrikant komt niet overeen", "not_denonavr_missing": "Geen Denon AVR netwerkontvanger, zoekinformatie niet compleet" diff --git a/homeassistant/components/devolo_home_control/translations/nl.json b/homeassistant/components/devolo_home_control/translations/nl.json index c85c685597d..18ad384342b 100644 --- a/homeassistant/components/devolo_home_control/translations/nl.json +++ b/homeassistant/components/devolo_home_control/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", diff --git a/homeassistant/components/devolo_home_network/translations/ko.json b/homeassistant/components/devolo_home_network/translations/ko.json index 758f3336cd4..0e7d5b287f2 100644 --- a/homeassistant/components/devolo_home_network/translations/ko.json +++ b/homeassistant/components/devolo_home_network/translations/ko.json @@ -3,6 +3,9 @@ "step": { "user": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "zeroconf_confirm": { + "title": "devolo \ud648 \ub124\ud2b8\uc6cc\ud06c \uc7a5\uce58 \ubc1c\uacac" } } } diff --git a/homeassistant/components/devolo_home_network/translations/nl.json b/homeassistant/components/devolo_home_network/translations/nl.json index e8730f44b5e..af3d6e55365 100644 --- a/homeassistant/components/devolo_home_network/translations/nl.json +++ b/homeassistant/components/devolo_home_network/translations/nl.json @@ -14,7 +14,7 @@ "data": { "ip_address": "IP-adres" }, - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "zeroconf_confirm": { "description": "Wilt u het devolo-thuisnetwerkapparaat met de hostnaam ` {host_name} ` aan Home Assistant toevoegen?", diff --git a/homeassistant/components/dialogflow/translations/nl.json b/homeassistant/components/dialogflow/translations/nl.json index 3d2617d25e5..7403e2c36dd 100644 --- a/homeassistant/components/dialogflow/translations/nl.json +++ b/homeassistant/components/dialogflow/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om evenementen naar de Home Assistant te verzenden, moet u [webhookintegratie van Dialogflow]({dialogflow_url}) instellen. \n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Method: POST \n - Content Type: application/json \n\nZie [de documentatie]({docs_url}) voor verdere informatie." diff --git a/homeassistant/components/discord/translations/nl.json b/homeassistant/components/discord/translations/nl.json index 55fb894031d..303b22fdfad 100644 --- a/homeassistant/components/discord/translations/nl.json +++ b/homeassistant/components/discord/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/dlna_dmr/translations/ko.json b/homeassistant/components/dlna_dmr/translations/ko.json index c3c5f1a74b2..c25d0fcabfc 100644 --- a/homeassistant/components/dlna_dmr/translations/ko.json +++ b/homeassistant/components/dlna_dmr/translations/ko.json @@ -9,6 +9,9 @@ }, "manual": { "description": "\uc7a5\uce58 \uc124\uba85 XML \ud30c\uc77c\uc758 URL" + }, + "user": { + "title": "DLNA DMR \uc7a5\uce58 \ubc1c\uacac" } } }, diff --git a/homeassistant/components/dlna_dmr/translations/nl.json b/homeassistant/components/dlna_dmr/translations/nl.json index 360d24fd590..adc554b0fbc 100644 --- a/homeassistant/components/dlna_dmr/translations/nl.json +++ b/homeassistant/components/dlna_dmr/translations/nl.json @@ -16,7 +16,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "import_turn_on": { "description": "Zet het apparaat aan en klik op verzenden om door te gaan met de migratie" diff --git a/homeassistant/components/dlna_dms/translations/ko.json b/homeassistant/components/dlna_dms/translations/ko.json index 20ad990e862..853a3eb2b1a 100644 --- a/homeassistant/components/dlna_dms/translations/ko.json +++ b/homeassistant/components/dlna_dms/translations/ko.json @@ -3,6 +3,9 @@ "step": { "confirm": { "description": "\uc124\uc815\uc744 \uc2dc\uc791\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, + "user": { + "title": "DLNA DMA \uc7a5\uce58 \ubc1c\uacac" } } } diff --git a/homeassistant/components/dlna_dms/translations/nl.json b/homeassistant/components/dlna_dms/translations/nl.json index d480118b76a..2edd4c5d2a6 100644 --- a/homeassistant/components/dlna_dms/translations/nl.json +++ b/homeassistant/components/dlna_dms/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "bad_ssdp": "SSDP-gegevens missen een vereiste waarde", "no_devices_found": "Geen apparaten gevonden op het netwerk", "not_dms": "Apparaat is geen ondersteunde mediaserver" @@ -10,7 +10,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "user": { "data": { diff --git a/homeassistant/components/dunehd/translations/nl.json b/homeassistant/components/dunehd/translations/nl.json index 3ee04091a1c..9f75cd5a91a 100644 --- a/homeassistant/components/dunehd/translations/nl.json +++ b/homeassistant/components/dunehd/translations/nl.json @@ -5,8 +5,8 @@ }, "error": { "already_configured": "Apparaat is al geconfigureerd", - "cannot_connect": "Kon niet verbinden", - "invalid_host": "ongeldige host of IP adres" + "cannot_connect": "Kan geen verbinding maken", + "invalid_host": "Ongeldige hostnaam of IP-adres" }, "step": { "user": { diff --git a/homeassistant/components/ecobee/translations/nl.json b/homeassistant/components/ecobee/translations/nl.json index 957d2f8244d..03ba56253bf 100644 --- a/homeassistant/components/ecobee/translations/nl.json +++ b/homeassistant/components/ecobee/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "pin_request_failed": "Fout bij het aanvragen van pincode bij ecobee; Controleer of de API-sleutel correct is.", diff --git a/homeassistant/components/efergy/translations/nl.json b/homeassistant/components/efergy/translations/nl.json index 0a51ce58a9c..ea0ec0c62a9 100644 --- a/homeassistant/components/efergy/translations/nl.json +++ b/homeassistant/components/efergy/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/elkm1/translations/ko.json b/homeassistant/components/elkm1/translations/ko.json index 6ffa3679946..5b16671c05b 100644 --- a/homeassistant/components/elkm1/translations/ko.json +++ b/homeassistant/components/elkm1/translations/ko.json @@ -10,6 +10,14 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "discovered_connection": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "description": "\uc790\ub3d9\uac80\uc0c9\ub41c \uc2dc\uc2a4\ud15c\uc5d0 \uc5f0\uacb0: {mac_address} ({host})", + "title": "Elk-M1 \uc81c\uc5b4\uc5d0 \uc5f0\uacb0\ud558\uae30" + }, "user": { "description": "\uc8fc\uc18c \ubb38\uc790\uc5f4\uc740 '\ubcf4\uc548' \ubc0f '\ube44\ubcf4\uc548'\uc5d0 \ub300\ud574 'address[:port]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '192.168.1.1'. \ud3ec\ud2b8\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 '\ube44\ubcf4\uc548' \uc758 \uacbd\uc6b0 2101 \uc774\uace0 '\ubcf4\uc548' \uc758 \uacbd\uc6b0 2601 \uc785\ub2c8\ub2e4. \uc2dc\ub9ac\uc5bc \ud504\ub85c\ud1a0\ucf5c\uc758 \uacbd\uc6b0 \uc8fc\uc18c\ub294 'tty[:baud]' \ud615\uc2dd\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc608: '/dev/ttyS1'. \uc804\uc1a1 \uc18d\ub3c4\ub294 \uc120\ud0dd \uc0ac\ud56d\uc774\uba70 \uae30\ubcf8\uac12\uc740 115200 \uc785\ub2c8\ub2e4.", "title": "Elk-M1 \uc81c\uc5b4\uc5d0 \uc5f0\uacb0\ud558\uae30" diff --git a/homeassistant/components/elkm1/translations/nl.json b/homeassistant/components/elkm1/translations/nl.json index c27293ce050..c33aa601433 100644 --- a/homeassistant/components/elkm1/translations/nl.json +++ b/homeassistant/components/elkm1/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "address_already_configured": "Een ElkM1 met dit adres is al geconfigureerd", "already_configured": "Een ElkM1 met dit voorvoegsel is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/elmax/translations/ko.json b/homeassistant/components/elmax/translations/ko.json new file mode 100644 index 00000000000..b9f74068f56 --- /dev/null +++ b/homeassistant/components/elmax/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "panels": { + "data": { + "panel_pin": "PIN \ucf54\ub4dc" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/enocean/translations/nl.json b/homeassistant/components/enocean/translations/nl.json index 79e0ab6dfec..6641dfeaaa8 100644 --- a/homeassistant/components/enocean/translations/nl.json +++ b/homeassistant/components/enocean/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "invalid_dongle_path": "Ongeldig dongle-pad", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_dongle_path": "Geen geldige dongle gevonden voor dit pad" diff --git a/homeassistant/components/enphase_envoy/translations/nl.json b/homeassistant/components/enphase_envoy/translations/nl.json index 26a96d5bde2..32975a82f7a 100644 --- a/homeassistant/components/enphase_envoy/translations/nl.json +++ b/homeassistant/components/enphase_envoy/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/esphome/translations/nl.json b/homeassistant/components/esphome/translations/nl.json index 9c757e917e4..aae5b639eb8 100644 --- a/homeassistant/components/esphome/translations/nl.json +++ b/homeassistant/components/esphome/translations/nl.json @@ -2,8 +2,8 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al begonnen", - "reauth_successful": "Herauthenticatie was succesvol" + "already_in_progress": "De configuratie is momenteel al bezig", + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "connection_error": "Kan geen verbinding maken met ESP. Zorg ervoor dat uw YAML-bestand een regel 'api:' bevat.", diff --git a/homeassistant/components/ezviz/translations/ko.json b/homeassistant/components/ezviz/translations/ko.json index 8ed11a874b0..8b44c79ce40 100644 --- a/homeassistant/components/ezviz/translations/ko.json +++ b/homeassistant/components/ezviz/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured_account": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "ezviz_cloud_account_missing": "Ezviz \ud074\ub77c\uc6b0\ub4dc \uacc4\uc815\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. Ezviz \ud074\ub77c\uc6b0\ub4dc \uacc4\uc815\uc744 \ub2e4\uc2dc \uad6c\uc131\ud558\uc2ed\uc2dc\uc624.", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "error": { @@ -15,21 +16,26 @@ "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } + }, + "description": "IP\uac00 {ip_address} \uc778 Ezviz \uce74\uba54\ub77c {serial} \uc5d0 \ub300\ud55c RTSP \uc790\uaca9 \uc99d\uba85\uc744 \uc785\ub825\ud558\uc138\uc694.", + "title": "Ezviz \uce74\uba54\ub77c \ubc1c\uacac" }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", "url": "URL \uc8fc\uc18c", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } + }, + "title": "Ezviz \ud074\ub77c\uc6b0\ub4dc\uc5d0 \uc5f0\uacb0" }, "user_custom_url": { "data": { "password": "\ube44\ubc00\ubc88\ud638", "url": "URL \uc8fc\uc18c", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } + }, + "description": "URL\uc744 \uc218\ub3d9\uc73c\ub85c \uc785\ub825\ud558\uc138\uc694", + "title": "\uc0ac\uc6a9\uc790 \uc9c0\uc815 Ezviz URL\uc5d0 \uc5f0\uacb0" } } }, @@ -37,6 +43,7 @@ "step": { "init": { "data": { + "ffmpeg_arguments": "\uce74\uba54\ub77c\uc5d0 \ub300\ud55c ffmpeg \uc804\ub2ec \uc778\uc218", "timeout": "\uc694\uccad \uc81c\ud55c \uc2dc\uac04 (\ucd08)" } } diff --git a/homeassistant/components/fibaro/translations/nl.json b/homeassistant/components/fibaro/translations/nl.json index 72b8588d1e4..049168c73d8 100644 --- a/homeassistant/components/fibaro/translations/nl.json +++ b/homeassistant/components/fibaro/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/fireservicerota/translations/nl.json b/homeassistant/components/fireservicerota/translations/nl.json index 3a6ba936dee..62085c9a333 100644 --- a/homeassistant/components/fireservicerota/translations/nl.json +++ b/homeassistant/components/fireservicerota/translations/nl.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie" diff --git a/homeassistant/components/fjaraskupan/translations/nl.json b/homeassistant/components/fjaraskupan/translations/nl.json index 498ef7af1be..1c973e28d69 100644 --- a/homeassistant/components/fjaraskupan/translations/nl.json +++ b/homeassistant/components/fjaraskupan/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/flick_electric/translations/nl.json b/homeassistant/components/flick_electric/translations/nl.json index f90ee71301b..c7c6e2bec81 100644 --- a/homeassistant/components/flick_electric/translations/nl.json +++ b/homeassistant/components/flick_electric/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Account is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/flo/translations/nl.json b/homeassistant/components/flo/translations/nl.json index d4c409802c1..8ad15260b0d 100644 --- a/homeassistant/components/flo/translations/nl.json +++ b/homeassistant/components/flo/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/flume/translations/ko.json b/homeassistant/components/flume/translations/ko.json index c82f0a990d8..fc3970e69ad 100644 --- a/homeassistant/components/flume/translations/ko.json +++ b/homeassistant/components/flume/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4" }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", @@ -9,6 +10,13 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "data": { + "password": "\ube44\ubc00\ubc88\ud638" + }, + "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\uac00 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "title": "Flume \uacc4\uc815 \uc7ac\uc778\uc99d" + }, "user": { "data": { "client_id": "\ud074\ub77c\uc774\uc5b8\ud2b8 ID", diff --git a/homeassistant/components/flume/translations/nl.json b/homeassistant/components/flume/translations/nl.json index de0e225be03..b08b37ca21c 100644 --- a/homeassistant/components/flume/translations/nl.json +++ b/homeassistant/components/flume/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/flunearyou/translations/nl.json b/homeassistant/components/flunearyou/translations/nl.json index d78abfcc187..0938bd45206 100644 --- a/homeassistant/components/flunearyou/translations/nl.json +++ b/homeassistant/components/flunearyou/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "unknown": "Onverwachte fout" diff --git a/homeassistant/components/flux_led/translations/nl.json b/homeassistant/components/flux_led/translations/nl.json index fd9e04bd475..2b64353e94c 100644 --- a/homeassistant/components/flux_led/translations/nl.json +++ b/homeassistant/components/flux_led/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "no_devices_found": "Geen apparaten gevonden op het netwerk" }, "error": { diff --git a/homeassistant/components/fritz/translations/ko.json b/homeassistant/components/fritz/translations/ko.json index 99c42938d6f..314e8cf7729 100644 --- a/homeassistant/components/fritz/translations/ko.json +++ b/homeassistant/components/fritz/translations/ko.json @@ -9,6 +9,7 @@ "error": { "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, "flow_title": "{name}", @@ -17,13 +18,34 @@ "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" - } + }, + "description": "\ubc1c\uacac\ub41c FRITZ!Box: {name} \n\n FRITZ!Box Tool\uc744 \uc124\uc815\ud558\uc5ec \ub2e4\uc74c\uc744 \uc81c\uc5b4\ud574\ubcf4\uc138\uc694: {name}", + "title": "FRITZ!Box Tool \uc124\uc815" }, "reauth_confirm": { "data": { "password": "\ube44\ubc00\ubc88\ud638", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" } + }, + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8", + "password": "\ube44\ubc00\ubc88\ud638", + "port": "\ud3ec\ud2b8", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "description": "FRITZ!Box Tool\uc744 \uc124\uc815\ud558\uc5ec FRITZ!Box\ub97c \uc81c\uc5b4\ud569\ub2c8\ub2e4.\n \ud544\uc218 \uc785\ub825\uc0ac\ud56d: \uc0ac\uc6a9\uc790 \uc774\ub984, \ube44\ubc00\ubc88\ud638.", + "title": "FRITZ!Box Tools \uc124\uc815" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "old_discovery": "\uc774\uc804 \uc7a5\uce58\uac80\uc0c9 \ubc29\ubc95 \uc0ac\uc6a9" + } } } } diff --git a/homeassistant/components/fritz/translations/nl.json b/homeassistant/components/fritz/translations/nl.json index 80aebf4fc8a..6bceac5dcc5 100644 --- a/homeassistant/components/fritz/translations/nl.json +++ b/homeassistant/components/fritz/translations/nl.json @@ -2,13 +2,13 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "ignore_ip6_link_local": "Lokaal IPv6-linkadres wordt niet ondersteund.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "upnp_not_configured": "Ontbrekende UPnP instellingen op apparaat." diff --git a/homeassistant/components/fritzbox/translations/nl.json b/homeassistant/components/fritzbox/translations/nl.json index 43d51df760d..524cf40f018 100644 --- a/homeassistant/components/fritzbox/translations/nl.json +++ b/homeassistant/components/fritzbox/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "ignore_ip6_link_local": "IPv6 link lokaal adres wordt niet ondersteund.", "no_devices_found": "Geen apparaten gevonden op het netwerk", "not_supported": "Verbonden met AVM FRITZ! Box, maar het kan geen Smart Home-apparaten bedienen.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie" diff --git a/homeassistant/components/garages_amsterdam/translations/ko.json b/homeassistant/components/garages_amsterdam/translations/ko.json new file mode 100644 index 00000000000..f5088fca3e1 --- /dev/null +++ b/homeassistant/components/garages_amsterdam/translations/ko.json @@ -0,0 +1,8 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/generic/translations/nl.json b/homeassistant/components/generic/translations/nl.json index 2b5bc828727..354f944148f 100644 --- a/homeassistant/components/generic/translations/nl.json +++ b/homeassistant/components/generic/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "already_exists": "Een camera met deze URL instellingen bestaat al.", @@ -21,7 +21,7 @@ }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "content_type": { "data": { diff --git a/homeassistant/components/geocaching/translations/nl.json b/homeassistant/components/geocaching/translations/nl.json index 82d40849146..e4cfc747c62 100644 --- a/homeassistant/components/geocaching/translations/nl.json +++ b/homeassistant/components/geocaching/translations/nl.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "oauth_error": "Ongeldige token data ontvangen.", - "reauth_successful": "Herauthenticatie was succesvol" + "already_in_progress": "De configuratie is momenteel al bezig", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "oauth_error": "Ongeldige tokengegevens ontvangen.", + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { @@ -18,7 +18,7 @@ }, "reauth_confirm": { "description": "De Geocaching integratie moet uw account herauthenticeren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } } diff --git a/homeassistant/components/geofency/translations/nl.json b/homeassistant/components/geofency/translations/nl.json index 30a2acfdadd..096817de9a6 100644 --- a/homeassistant/components/geofency/translations/nl.json +++ b/homeassistant/components/geofency/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om locaties naar Home Assistant te sturen, moet u de Webhook-functie instellen in Geofency.\n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Methode: POST \n\n Zie [de documentatie]({docs_url}) voor meer informatie." diff --git a/homeassistant/components/gios/translations/nl.json b/homeassistant/components/gios/translations/nl.json index 26c83cfff7b..3f714a0fd1e 100644 --- a/homeassistant/components/gios/translations/nl.json +++ b/homeassistant/components/gios/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/glances/translations/nl.json b/homeassistant/components/glances/translations/nl.json index 6cb7fb445bb..ca414a92ab9 100644 --- a/homeassistant/components/glances/translations/nl.json +++ b/homeassistant/components/glances/translations/nl.json @@ -14,7 +14,7 @@ "name": "Naam", "password": "Wachtwoord", "port": "Poort", - "ssl": "Gebruik een SSL-certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", "verify_ssl": "SSL-certificaat verifi\u00ebren", "version": "Glances API-versie (2 of 3)" diff --git a/homeassistant/components/goalzero/translations/nl.json b/homeassistant/components/goalzero/translations/nl.json index 9f719d74efe..ab70229ace1 100644 --- a/homeassistant/components/goalzero/translations/nl.json +++ b/homeassistant/components/goalzero/translations/nl.json @@ -7,7 +7,7 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_host": "Onjuiste hostnaam of IP-adres", + "invalid_host": "Ongeldige hostnaam of IP-adres", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/gogogate2/translations/ko.json b/homeassistant/components/gogogate2/translations/ko.json index dc37928db76..e8df5fa95dc 100644 --- a/homeassistant/components/gogogate2/translations/ko.json +++ b/homeassistant/components/gogogate2/translations/ko.json @@ -7,6 +7,7 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, + "flow_title": "{device} ({ip_address})", "step": { "user": { "data": { diff --git a/homeassistant/components/gogogate2/translations/nl.json b/homeassistant/components/gogogate2/translations/nl.json index a32bb1af69b..c194a1c21da 100644 --- a/homeassistant/components/gogogate2/translations/nl.json +++ b/homeassistant/components/gogogate2/translations/nl.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie" }, "flow_title": "{device} ({ip_address})", diff --git a/homeassistant/components/goodwe/translations/nl.json b/homeassistant/components/goodwe/translations/nl.json index 8986006fdeb..cb4d0e7b0b7 100644 --- a/homeassistant/components/goodwe/translations/nl.json +++ b/homeassistant/components/goodwe/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang" + "already_in_progress": "De configuratie is momenteel al bezig" }, "error": { "connection_error": "Kan geen verbinding maken" diff --git a/homeassistant/components/google/translations/ko.json b/homeassistant/components/google/translations/ko.json index 2906ebc5114..e4fc1875308 100644 --- a/homeassistant/components/google/translations/ko.json +++ b/homeassistant/components/google/translations/ko.json @@ -2,6 +2,11 @@ "config": { "abort": { "missing_configuration": "\uad6c\uc131\uc694\uc18c\uac00 \uad6c\uc131\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uc124\uba85\uc11c\ub97c \ucc38\uace0\ud574\uc8fc\uc138\uc694." + }, + "step": { + "reauth_confirm": { + "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" + } } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json index e732fd7cd19..dca192c59d4 100644 --- a/homeassistant/components/google/translations/nl.json +++ b/homeassistant/components/google/translations/nl.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "already_in_progress": "Configuratie flow is al in bewerking", + "already_in_progress": "De configuratie is momenteel al bezig", "code_expired": "De authenticatiecode is verlopen of de instelling van de inloggegevens is ongeldig, probeer het opnieuw.", - "invalid_access_token": "Ongeldige toegang token", - "missing_configuration": "Het component is niet geconfigureerd. Volg a.u.b. de documentatie", - "oauth_error": "Ongeldige token data ontvangen", - "reauth_successful": "Herauthentiecatie was succesvol" + "invalid_access_token": "Ongeldig toegangstoken", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "oauth_error": "Ongeldige tokengegevens ontvangen.", + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Authenticatie succesvol" + "default": "Authenticatie geslaagd" }, "progress": { "exchange": "Om uw Google-account te koppelen, gaat u naar de [ {url} ]( {url} ) en voert u de code in: \n\n {user_code}" @@ -24,7 +24,7 @@ }, "reauth_confirm": { "description": "De Google Agenda-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Herauthentiseer integratie" + "title": "Integratie herauthentiseren" } } } diff --git a/homeassistant/components/google_travel_time/translations/nl.json b/homeassistant/components/google_travel_time/translations/nl.json index cb43d8afeae..bb088c97ca5 100644 --- a/homeassistant/components/google_travel_time/translations/nl.json +++ b/homeassistant/components/google_travel_time/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/gpslogger/translations/nl.json b/homeassistant/components/gpslogger/translations/nl.json index 26bfba4eaea..f2b5d3158ad 100644 --- a/homeassistant/components/gpslogger/translations/nl.json +++ b/homeassistant/components/gpslogger/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om evenementen naar Home Assistant te verzenden, moet u de webhook-functie instellen in GPSLogger. \n\n Vul de volgende info in: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n\n Zie [de documentatie] ( {docs_url} ) voor meer informatie." diff --git a/homeassistant/components/gree/translations/nl.json b/homeassistant/components/gree/translations/nl.json index 0671f0b3674..6fc4a03e824 100644 --- a/homeassistant/components/gree/translations/nl.json +++ b/homeassistant/components/gree/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/growatt_server/translations/ko.json b/homeassistant/components/growatt_server/translations/ko.json new file mode 100644 index 00000000000..676542812e7 --- /dev/null +++ b/homeassistant/components/growatt_server/translations/ko.json @@ -0,0 +1,3 @@ +{ + "title": "Growatt \uc11c\ubc84" +} \ No newline at end of file diff --git a/homeassistant/components/guardian/translations/ko.json b/homeassistant/components/guardian/translations/ko.json index 8c3c5103c3c..3faa244b246 100644 --- a/homeassistant/components/guardian/translations/ko.json +++ b/homeassistant/components/guardian/translations/ko.json @@ -6,6 +6,9 @@ "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "discovery_confirm": { + "description": "Guardian \uae30\uae30\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, "user": { "data": { "ip_address": "IP \uc8fc\uc18c", diff --git a/homeassistant/components/guardian/translations/nl.json b/homeassistant/components/guardian/translations/nl.json index 42d499315bd..d8ed1242e92 100644 --- a/homeassistant/components/guardian/translations/nl.json +++ b/homeassistant/components/guardian/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken" }, "step": { diff --git a/homeassistant/components/hisense_aehw4a1/translations/nl.json b/homeassistant/components/hisense_aehw4a1/translations/nl.json index c1f353558b6..0d7661a07b6 100644 --- a/homeassistant/components/hisense_aehw4a1/translations/nl.json +++ b/homeassistant/components/hisense_aehw4a1/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/hive/translations/nl.json b/homeassistant/components/hive/translations/nl.json index 206aa661735..2cbc7c94fc6 100644 --- a/homeassistant/components/hive/translations/nl.json +++ b/homeassistant/components/hive/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown_entry": "Kan bestaand item niet vinden." }, "error": { diff --git a/homeassistant/components/home_connect/translations/nl.json b/homeassistant/components/home_connect/translations/nl.json index 25a81209607..72bbb5a0e91 100644 --- a/homeassistant/components/home_connect/translations/nl.json +++ b/homeassistant/components/home_connect/translations/nl.json @@ -1,15 +1,15 @@ { "config": { "abort": { - "missing_configuration": "Het component is niet geconfigureerd. Volg de documentatie.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})" + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})" }, "create_entry": { - "default": "Succesvol geverifieerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { - "title": "Kies de verificatiemethode" + "title": "Kies een authenticatie methode" } } } diff --git a/homeassistant/components/home_plus_control/translations/nl.json b/homeassistant/components/home_plus_control/translations/nl.json index ffebcf58d60..f1471f36273 100644 --- a/homeassistant/components/home_plus_control/translations/nl.json +++ b/homeassistant/components/home_plus_control/translations/nl.json @@ -2,14 +2,14 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "already_in_progress": "De configuratie is momenteel al bezig", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/homekit_controller/translations/ko.json b/homeassistant/components/homekit_controller/translations/ko.json index c28573ee6ac..fd40288a7e3 100644 --- a/homeassistant/components/homekit_controller/translations/ko.json +++ b/homeassistant/components/homekit_controller/translations/ko.json @@ -12,6 +12,7 @@ }, "error": { "authentication_error": "HomeKit \ucf54\ub4dc\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud655\uc778 \ud6c4 \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", + "insecure_setup_code": "\uc694\uccad\ud55c \uc124\uc815 \ucf54\ub4dc\ub294 \uc548\uc804\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uc774 \uc561\uc138\uc11c\ub9ac\ub294 \uae30\ubcf8 \ubcf4\uc548 \uc694\uad6c \uc0ac\ud56d\uc744 \ucda9\uc871\ud558\uc9c0 \ubabb\ud569\ub2c8\ub2e4.", "max_peers_error": "\uae30\uae30\uc5d0 \ube44\uc5b4\uc788\ub294 \ud398\uc5b4\ub9c1 \uc7a5\uc18c\uac00 \uc5c6\uc5b4 \ud398\uc5b4\ub9c1\uc744 \ucd94\uac00\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "pairing_failed": "\uc774 \uae30\uae30\uc640 \ud398\uc5b4\ub9c1\uc744 \uc2dc\ub3c4\ud558\ub294 \uc911 \ucc98\ub9ac\ub418\uc9c0 \uc54a\uc740 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4. \uc77c\uc2dc\uc801\uc778 \uc624\ub958\uc774\uac70\ub098 \ud604\uc7ac \uc9c0\uc6d0\ub418\uc9c0 \uc54a\ub294 \uae30\uae30 \uc77c \uc218 \uc788\uc2b5\ub2c8\ub2e4.", "unable_to_pair": "\ud398\uc5b4\ub9c1 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.", @@ -29,6 +30,7 @@ }, "pair": { "data": { + "allow_insecure_setup_codes": "\uc548\uc804\ud558\uc9c0 \uc54a\uc740 \uc124\uc815 \ucf54\ub4dc\uc640\uc758 \ud398\uc5b4\ub9c1\uc744 \ud5c8\uc6a9\ud569\ub2c8\ub2e4.", "pairing_code": "\ud398\uc5b4\ub9c1 \ucf54\ub4dc" }, "description": "HomeKit \ucee8\ud2b8\ub864\ub7ec\ub294 \ubcc4\ub3c4\uc758 HomeKit \ucee8\ud2b8\ub864\ub7ec \ub610\ub294 iCloud \uc5c6\uc774 \uc554\ud638\ud654\ub41c \ubcf4\uc548 \uc5f0\uacb0\uc744 \uc0ac\uc6a9\ud558\uc5ec \ub85c\uceec \uc601\uc5ed \ub124\ud2b8\uc6cc\ud06c \uc0c1\uc5d0\uc11c {name}\uacfc(\uc640) \ud1b5\uc2e0\ud569\ub2c8\ub2e4. \uc774 \uc561\uc138\uc11c\ub9ac\ub97c \uc0ac\uc6a9\ud558\ub824\uba74 HomeKit \ud398\uc5b4\ub9c1 \ucf54\ub4dc(XX-XX-XXX \ud615\uc2dd)\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. \uc774 \ucf54\ub4dc\ub294 \uc77c\ubc18\uc801\uc73c\ub85c \uae30\uae30\ub098 \ud3ec\uc7a5 \ubc15\uc2a4\uc5d0 \ud45c\uc2dc\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.", diff --git a/homeassistant/components/homekit_controller/translations/nl.json b/homeassistant/components/homekit_controller/translations/nl.json index 4312fdc033c..fd966ad43fa 100644 --- a/homeassistant/components/homekit_controller/translations/nl.json +++ b/homeassistant/components/homekit_controller/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "accessory_not_found_error": "Kan geen koppeling toevoegen omdat het apparaat niet langer kan worden gevonden.", "already_configured": "Accessoire is al geconfigureerd met deze controller.", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "already_paired": "Dit accessoire is al gekoppeld aan een ander apparaat. Reset het accessoire en probeer het opnieuw.", "ignored_model": "HomeKit-ondersteuning voor dit model is geblokkeerd omdat er een meer functie volledige native integratie beschikbaar is.", "invalid_config_entry": "Dit apparaat geeft aan dat het gereed is om te koppelen, maar er is al een conflicterend configuratie-item voor in de Home Assistant dat eerst moet worden verwijderd.", diff --git a/homeassistant/components/homematicip_cloud/translations/nl.json b/homeassistant/components/homematicip_cloud/translations/nl.json index cb65dee7bd1..d5945293089 100644 --- a/homeassistant/components/homematicip_cloud/translations/nl.json +++ b/homeassistant/components/homematicip_cloud/translations/nl.json @@ -6,7 +6,7 @@ "unknown": "Onverwachte fout" }, "error": { - "invalid_sgtin_or_pin": "Ongeldige SGTIN of PIN-code, probeer het opnieuw.", + "invalid_sgtin_or_pin": "Ongeldige SGTIN of Pincode, probeer het opnieuw.", "press_the_button": "Druk op de blauwe knop.", "register_failed": "Kan niet registreren, gelieve opnieuw te proberen.", "timeout_button": "Blauwe knop druk op timeout, probeer het opnieuw." @@ -16,7 +16,7 @@ "data": { "hapid": "Accesspoint ID (SGTIN)", "name": "Naam (optioneel, gebruikt als naamvoorvoegsel voor alle apparaten)", - "pin": "PIN-code" + "pin": "Pincode" }, "title": "Kies HomematicIP accesspoint" }, diff --git a/homeassistant/components/huawei_lte/translations/ko.json b/homeassistant/components/huawei_lte/translations/ko.json index 930431d6c87..2a29418e67d 100644 --- a/homeassistant/components/huawei_lte/translations/ko.json +++ b/homeassistant/components/huawei_lte/translations/ko.json @@ -31,7 +31,8 @@ "init": { "data": { "name": "\uc54c\ub9bc \uc11c\ube44\uc2a4 \uc774\ub984 (\ubcc0\uacbd \uc2dc \ub2e4\uc2dc \uc2dc\uc791\ud574\uc57c \ud568)", - "recipient": "SMS \uc54c\ub9bc \uc218\uc2e0\uc790" + "recipient": "SMS \uc54c\ub9bc \uc218\uc2e0\uc790", + "track_wired_clients": "\uc720\uc120 \ub124\ud2b8\uc6cc\ud06c \ud074\ub77c\uc774\uc5b8\ud2b8 \ucd94\uc801" } } } diff --git a/homeassistant/components/hue/translations/nl.json b/homeassistant/components/hue/translations/nl.json index 56bac6b89d8..57c52c4156f 100644 --- a/homeassistant/components/hue/translations/nl.json +++ b/homeassistant/components/hue/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "all_configured": "Alle Philips Hue bridges zijn al geconfigureerd", "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "discover_timeout": "Hue bridges kunnen niet worden gevonden", "invalid_host": "Ongeldige host", @@ -70,7 +70,7 @@ "data": { "allow_hue_groups": "Sta Hue-groepen toe", "allow_hue_scenes": "Sta Hue sc\u00e8nes toe", - "allow_unreachable": "Onbereikbare lampen toestaan hun status correct te melden", + "allow_unreachable": "Onbereikbare lampen toestaan hun status te melden", "ignore_availability": "Verbindingsstatus negeren voor de opgegeven apparaten" } } diff --git a/homeassistant/components/hunterdouglas_powerview/translations/nl.json b/homeassistant/components/hunterdouglas_powerview/translations/nl.json index 588fa21c813..d8d305383f8 100644 --- a/homeassistant/components/hunterdouglas_powerview/translations/nl.json +++ b/homeassistant/components/hunterdouglas_powerview/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Verbinding mislukt, probeer het opnieuw", + "cannot_connect": "Kan geen verbinding maken", "unknown": "Onverwachte fout" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/hvv_departures/translations/nl.json b/homeassistant/components/hvv_departures/translations/nl.json index 8782499ee05..aab03a0e576 100644 --- a/homeassistant/components/hvv_departures/translations/nl.json +++ b/homeassistant/components/hvv_departures/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "no_results": "Geen resultaten. Probeer het met een ander station/adres" }, diff --git a/homeassistant/components/hyperion/translations/nl.json b/homeassistant/components/hyperion/translations/nl.json index 992d705a533..ff2cd5f4f6b 100644 --- a/homeassistant/components/hyperion/translations/nl.json +++ b/homeassistant/components/hyperion/translations/nl.json @@ -2,13 +2,13 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "auth_new_token_not_granted_error": "Nieuw aangemaakte token is niet goedgekeurd in Hyperion UI", "auth_new_token_not_work_error": "Verificatie met nieuw aangemaakt token mislukt", "auth_required_error": "Kan niet bepalen of autorisatie vereist is", "cannot_connect": "Kan geen verbinding maken", "no_id": "De Hyperion Ambilight instantie heeft zijn id niet gerapporteerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/icloud/translations/nl.json b/homeassistant/components/icloud/translations/nl.json index 7260f954c38..b93ebab4665 100644 --- a/homeassistant/components/icloud/translations/nl.json +++ b/homeassistant/components/icloud/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Account is al geconfigureerd", "no_device": "Op geen van uw apparaten is \"Find my iPhone\" geactiveerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -16,7 +16,7 @@ "password": "Wachtwoord" }, "description": "Uw eerder ingevoerde wachtwoord voor {username} werkt niet meer. Update uw wachtwoord om deze integratie te blijven gebruiken.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "trusted_device": { "data": { diff --git a/homeassistant/components/ifttt/translations/nl.json b/homeassistant/components/ifttt/translations/nl.json index 727b21f43be..93cdd225b98 100644 --- a/homeassistant/components/ifttt/translations/nl.json +++ b/homeassistant/components/ifttt/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om evenementen naar de Home Assistant te verzenden, moet u de actie \"Een webverzoek doen\" gebruiken vanuit de [IFTTT Webhook-applet]({applet_url}). \n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Method: POST \n - Content Type: application/json \n\nZie [the documentation]({docs_url}) voor informatie over het configureren van automatiseringen om inkomende gegevens te verwerken." diff --git a/homeassistant/components/insteon/translations/ko.json b/homeassistant/components/insteon/translations/ko.json index d1a4624f7bf..927839a6080 100644 --- a/homeassistant/components/insteon/translations/ko.json +++ b/homeassistant/components/insteon/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "not_insteon_device": "\ubc1c\uacac\ub41c \uc7a5\uce58\uac00 Insteon \uc7a5\uce58\uac00 \uc544\ub2d9\ub2c8\ub2e4", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "error": { diff --git a/homeassistant/components/insteon/translations/nl.json b/homeassistant/components/insteon/translations/nl.json index 59e799a084c..2683342ad02 100644 --- a/homeassistant/components/insteon/translations/nl.json +++ b/homeassistant/components/insteon/translations/nl.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "not_insteon_device": "Ontdekt apparaat is geen Insteon apparaat", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "select_single": "Selecteer een optie." }, "flow_title": "{name}", @@ -49,7 +49,7 @@ }, "options": { "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "input_error": "Ongeldige invoer, controleer uw waarden.", "select_single": "Selecteer \u00e9\u00e9n optie." }, diff --git a/homeassistant/components/intellifire/translations/nl.json b/homeassistant/components/intellifire/translations/nl.json index 7356c4a0d50..2af11edede8 100644 --- a/homeassistant/components/intellifire/translations/nl.json +++ b/homeassistant/components/intellifire/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "not_intellifire_device": "Niet een IntelliFire apparaat.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "api_error": "Inloggen mislukt", diff --git a/homeassistant/components/ios/translations/nl.json b/homeassistant/components/ios/translations/nl.json index 1e660ec2f5d..2e50721297a 100644 --- a/homeassistant/components/ios/translations/nl.json +++ b/homeassistant/components/ios/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/iotawatt/translations/nl.json b/homeassistant/components/iotawatt/translations/nl.json index 617073e91c0..04c1bb9a262 100644 --- a/homeassistant/components/iotawatt/translations/nl.json +++ b/homeassistant/components/iotawatt/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/ipma/translations/nl.json b/homeassistant/components/ipma/translations/nl.json index 154aafe1033..ddd00b40e51 100644 --- a/homeassistant/components/ipma/translations/nl.json +++ b/homeassistant/components/ipma/translations/nl.json @@ -8,7 +8,7 @@ "data": { "latitude": "Breedtegraad", "longitude": "Lengtegraad", - "mode": "Mode", + "mode": "Modus", "name": "Naam" }, "description": "Instituto Portugu\u00eas do Mar e Atmosfera", diff --git a/homeassistant/components/ipp/translations/nl.json b/homeassistant/components/ipp/translations/nl.json index 5296fcac36f..2a97724a596 100644 --- a/homeassistant/components/ipp/translations/nl.json +++ b/homeassistant/components/ipp/translations/nl.json @@ -20,7 +20,7 @@ "base_path": "Relatief pad naar de printer", "host": "Host", "port": "Poort", - "ssl": "Printer ondersteunt communicatie via SSL / TLS", + "ssl": "Maakt gebruik van een SSL-certificaat", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "description": "Stel uw printer in via Internet Printing Protocol (IPP) om te integreren met Home Assistant.", diff --git a/homeassistant/components/iss/translations/nl.json b/homeassistant/components/iss/translations/nl.json index cbe31ac6674..9e8d1f6bf95 100644 --- a/homeassistant/components/iss/translations/nl.json +++ b/homeassistant/components/iss/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "latitude_longitude_not_defined": "Breedte- en lengtegraad zijn niet gedefinieerd in Home Assistant.", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { diff --git a/homeassistant/components/isy994/translations/nl.json b/homeassistant/components/isy994/translations/nl.json index 580bd307a5c..261be2929fc 100644 --- a/homeassistant/components/isy994/translations/nl.json +++ b/homeassistant/components/isy994/translations/nl.json @@ -4,10 +4,10 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "invalid_host": "De hostvermelding had de niet volledig URL-indeling, bijvoorbeeld http://192.168.10.100:80", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "flow_title": "{name} ({host})", diff --git a/homeassistant/components/izone/translations/nl.json b/homeassistant/components/izone/translations/nl.json index b70bb738df0..707f6fb279d 100644 --- a/homeassistant/components/izone/translations/nl.json +++ b/homeassistant/components/izone/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/jellyfin/translations/nl.json b/homeassistant/components/jellyfin/translations/nl.json index 1072cfff418..947f89dcb81 100644 --- a/homeassistant/components/jellyfin/translations/nl.json +++ b/homeassistant/components/jellyfin/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/juicenet/translations/nl.json b/homeassistant/components/juicenet/translations/nl.json index 5f1b0d37e25..907ac3a68da 100644 --- a/homeassistant/components/juicenet/translations/nl.json +++ b/homeassistant/components/juicenet/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Account is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/kaleidescape/translations/nl.json b/homeassistant/components/kaleidescape/translations/nl.json index b3498db12a3..50a81b361cf 100644 --- a/homeassistant/components/kaleidescape/translations/nl.json +++ b/homeassistant/components/kaleidescape/translations/nl.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "Configuratiestroom is al bezig", + "already_in_progress": "De configuratie is momenteel al bezig", "unknown": "Onverwachte fout", "unsupported": "Niet-ondersteund apparaat" }, "error": { - "cannot_connect": "Verbinding mislukt", + "cannot_connect": "Kan geen verbinding maken", "unsupported": "Niet-ondersteund apparaat" }, "flow_title": "{model} ({name})", diff --git a/homeassistant/components/keenetic_ndms2/translations/ko.json b/homeassistant/components/keenetic_ndms2/translations/ko.json index 9444b447d37..e20111fd722 100644 --- a/homeassistant/components/keenetic_ndms2/translations/ko.json +++ b/homeassistant/components/keenetic_ndms2/translations/ko.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" + "already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "not_keenetic_ndms2": "\ubc1c\uacac\ub41c \uc7a5\uce58\uac00 Keenetic \ub77c\uc6b0\ud130\uac00 \uc544\ub2d9\ub2c8\ub2e4." }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" diff --git a/homeassistant/components/knx/translations/ko.json b/homeassistant/components/knx/translations/ko.json index 0762b130455..0aac9be9341 100644 --- a/homeassistant/components/knx/translations/ko.json +++ b/homeassistant/components/knx/translations/ko.json @@ -1,6 +1,11 @@ { "config": { "step": { + "secure_knxkeys": { + "data": { + "knxkeys_filename": "`.knxkeys` \ud30c\uc77c\uc758 \ud30c\uc77c \uc774\ub984(\ud655\uc7a5\uc790 \ud3ec\ud568)" + } + }, "secure_manual": { "description": "IP \ubcf4\uc548 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." } diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index 4c342d2741b..1c597507c67 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/knx/translations/pl.json b/homeassistant/components/knx/translations/pl.json index d972d9b7061..23185a0ae49 100644 --- a/homeassistant/components/knx/translations/pl.json +++ b/homeassistant/components/knx/translations/pl.json @@ -102,7 +102,7 @@ "multicast_group": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `224.0.23.12`", "multicast_port": "U\u017cywany do routingu i wykrywania. Domy\u015blnie: `3671`", "rate_limit": "Maksymalna liczba wychodz\u0105cych wiadomo\u015bci na sekund\u0119.\nZalecane: od 20 do 40", - "state_updater": "Globalnie w\u0142\u0105czaj lub wy\u0142\u0105czaj odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w z magistrali KNX, opcje encji `sync_state` nie b\u0119d\u0105 mia\u0142y \u017cadnego efektu." + "state_updater": "Ustaw domy\u015blne odczytywanie stan\u00f3w z magistrali KNX. Po wy\u0142\u0105czeniu, Home Assistant nie b\u0119dzie aktywnie pobiera\u0107 stan\u00f3w encji z magistrali KNX. Mo\u017cna to zast\u0105pi\u0107 przez opcj\u0119 encji `sync_state`." } }, "tunnel": { diff --git a/homeassistant/components/kodi/translations/nl.json b/homeassistant/components/kodi/translations/nl.json index 879ca83051f..6f3bac1e2e0 100644 --- a/homeassistant/components/kodi/translations/nl.json +++ b/homeassistant/components/kodi/translations/nl.json @@ -2,13 +2,13 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "no_uuid": "Kodi-instantie heeft geen unieke ID. Dit komt waarschijnlijk door een oude Kodi-versie (17.x of lager). U kunt de integratie handmatig configureren of upgraden naar een recentere Kodi-versie.", "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, @@ -29,7 +29,7 @@ "data": { "host": "Host", "port": "Poort", - "ssl": "Gebruik een SSL-certificaat" + "ssl": "Maakt gebruik van een SSL-certificaat" }, "description": "Kodi-verbindingsinformatie. Zorg ervoor dat u \"Controle van Kodi via HTTP toestaan\" in Systeem / Instellingen / Netwerk / Services inschakelt." }, diff --git a/homeassistant/components/konnected/translations/nl.json b/homeassistant/components/konnected/translations/nl.json index 1680431a35b..6e46ef3e2bc 100644 --- a/homeassistant/components/konnected/translations/nl.json +++ b/homeassistant/components/konnected/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "not_konn_panel": "Geen herkend Konnected.io apparaat", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/kraken/translations/nl.json b/homeassistant/components/kraken/translations/nl.json index 09b93b205e3..0ecf8885785 100644 --- a/homeassistant/components/kraken/translations/nl.json +++ b/homeassistant/components/kraken/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "already_configured": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "one": "Leeg", @@ -9,7 +9,7 @@ }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/kulersky/translations/nl.json b/homeassistant/components/kulersky/translations/nl.json index 0671f0b3674..6fc4a03e824 100644 --- a/homeassistant/components/kulersky/translations/nl.json +++ b/homeassistant/components/kulersky/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/launch_library/translations/nl.json b/homeassistant/components/launch_library/translations/nl.json index 4dd9a218aa5..cc2333416d8 100644 --- a/homeassistant/components/launch_library/translations/nl.json +++ b/homeassistant/components/launch_library/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { diff --git a/homeassistant/components/lifx/translations/nl.json b/homeassistant/components/lifx/translations/nl.json index 0e0a6190f0d..c8d0ca83dd8 100644 --- a/homeassistant/components/lifx/translations/nl.json +++ b/homeassistant/components/lifx/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/litejet/translations/nl.json b/homeassistant/components/litejet/translations/nl.json index e2743b4165f..6831d710bad 100644 --- a/homeassistant/components/litejet/translations/nl.json +++ b/homeassistant/components/litejet/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "open_failed": "Kan de opgegeven seri\u00eble poort niet openen." diff --git a/homeassistant/components/litterrobot/translations/sensor.pl.json b/homeassistant/components/litterrobot/translations/sensor.pl.json index 5e75c2ff68a..b3063c7ca62 100644 --- a/homeassistant/components/litterrobot/translations/sensor.pl.json +++ b/homeassistant/components/litterrobot/translations/sensor.pl.json @@ -15,7 +15,7 @@ "ec": "cykl opr\u00f3\u017cniania", "hpf": "b\u0142\u0105d pozycji wyj\u015bciowej", "off": "wy\u0142.", - "offline": "Offline", + "offline": "offline", "otf": "b\u0142\u0105d nadmiernego momentu obrotowego", "p": "wstrzymany", "pd": "detektor obecno\u015bci", diff --git a/homeassistant/components/local_ip/translations/nl.json b/homeassistant/components/local_ip/translations/nl.json index 4b2672d2a3b..0d09c23ea97 100644 --- a/homeassistant/components/local_ip/translations/nl.json +++ b/homeassistant/components/local_ip/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "Lokaal IP-adres" } } diff --git a/homeassistant/components/locative/translations/nl.json b/homeassistant/components/locative/translations/nl.json index 0a459e566c5..11ccfc059bc 100644 --- a/homeassistant/components/locative/translations/nl.json +++ b/homeassistant/components/locative/translations/nl.json @@ -3,14 +3,14 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om locaties naar Home Assistant te sturen, moet u de Webhook-functie instellen in de Locative app. \n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Methode: POST \n\n Zie [de documentatie]({docs_url}) voor meer informatie." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "Stel de Locative Webhook in" } } diff --git a/homeassistant/components/logi_circle/translations/nl.json b/homeassistant/components/logi_circle/translations/nl.json index 96231086830..fcf867036db 100644 --- a/homeassistant/components/logi_circle/translations/nl.json +++ b/homeassistant/components/logi_circle/translations/nl.json @@ -4,10 +4,10 @@ "already_configured": "Account is al geconfigureerd", "external_error": "Uitzondering opgetreden uit een andere stroom.", "external_setup": "Logi Circle is met succes geconfigureerd vanuit een andere stroom.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen." + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie." }, "error": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "follow_link": "Volg de link en authenticeer voordat u op Verzenden drukt.", "invalid_auth": "Ongeldige authenticatie" }, diff --git a/homeassistant/components/lookin/translations/nl.json b/homeassistant/components/lookin/translations/nl.json index 89f94de97ca..67ccb5da847 100644 --- a/homeassistant/components/lookin/translations/nl.json +++ b/homeassistant/components/lookin/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "no_devices_found": "Geen apparaten gevonden op het netwerk" }, diff --git a/homeassistant/components/lutron_caseta/translations/nl.json b/homeassistant/components/lutron_caseta/translations/nl.json index c2a342123d0..0d1063eee8c 100644 --- a/homeassistant/components/lutron_caseta/translations/nl.json +++ b/homeassistant/components/lutron_caseta/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "not_lutron_device": "Ontdekt apparaat is geen Lutron-apparaat" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/lyric/translations/ko.json b/homeassistant/components/lyric/translations/ko.json index 37093d340df..9a4b1cce222 100644 --- a/homeassistant/components/lyric/translations/ko.json +++ b/homeassistant/components/lyric/translations/ko.json @@ -13,6 +13,7 @@ "title": "\uc778\uc99d \ubc29\ubc95 \uc120\ud0dd\ud558\uae30" }, "reauth_confirm": { + "description": "Lyric \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" } } diff --git a/homeassistant/components/lyric/translations/nl.json b/homeassistant/components/lyric/translations/nl.json index 0d1f9da12e8..a2569f18dc2 100644 --- a/homeassistant/components/lyric/translations/nl.json +++ b/homeassistant/components/lyric/translations/nl.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "reauth_successful": "Herauthenticatie was succesvol" + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { @@ -14,7 +14,7 @@ }, "reauth_confirm": { "description": "De Lyric-integratie moet uw account opnieuw verifi\u00ebren.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } } diff --git a/homeassistant/components/mailgun/translations/nl.json b/homeassistant/components/mailgun/translations/nl.json index 5e84c62a314..309c5b502dc 100644 --- a/homeassistant/components/mailgun/translations/nl.json +++ b/homeassistant/components/mailgun/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om evenementen naar Home Assistant te verzenden, moet u [Webhooks with Mailgun]({mailgun_url}) instellen. \n\n Vul de volgende info in: \n\n - URL: `{webhook_url}` \n - Methode: POST \n - Inhoudstype: application/json \n\n Zie [de documentatie]({docs_url}) voor informatie over het configureren van automatiseringen om binnenkomende gegevens te verwerken." diff --git a/homeassistant/components/mazda/translations/nl.json b/homeassistant/components/mazda/translations/nl.json index f7532b9fc45..5b267b914cb 100644 --- a/homeassistant/components/mazda/translations/nl.json +++ b/homeassistant/components/mazda/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "account_locked": "Account vergrendeld. Probeer het later nog eens.", diff --git a/homeassistant/components/media_player/translations/nl.json b/homeassistant/components/media_player/translations/nl.json index 23fe64d452a..f3ad43fb5ec 100644 --- a/homeassistant/components/media_player/translations/nl.json +++ b/homeassistant/components/media_player/translations/nl.json @@ -25,7 +25,7 @@ "off": "Uit", "on": "Aan", "paused": "Gepauzeerd", - "playing": "Afspelen", + "playing": "Speelt", "standby": "Stand-by" } }, diff --git a/homeassistant/components/met/translations/ko.json b/homeassistant/components/met/translations/ko.json index 17175c196c0..b8e89380d52 100644 --- a/homeassistant/components/met/translations/ko.json +++ b/homeassistant/components/met/translations/ko.json @@ -1,5 +1,8 @@ { "config": { + "abort": { + "no_home": "Home Assistant\uc5d0 \ud648 \uc88c\ud45c\uac00 \uc124\uc815\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + }, "error": { "already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, diff --git a/homeassistant/components/meteo_france/translations/nl.json b/homeassistant/components/meteo_france/translations/nl.json index 5e36e0585c7..1272d2c11e8 100644 --- a/homeassistant/components/meteo_france/translations/nl.json +++ b/homeassistant/components/meteo_france/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd.", + "already_configured": "Locatie is al geconfigureerd", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/metoffice/translations/nl.json b/homeassistant/components/metoffice/translations/nl.json index a6ba36f07af..b6fbbfdbba4 100644 --- a/homeassistant/components/metoffice/translations/nl.json +++ b/homeassistant/components/metoffice/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Service is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/min_max/translations/nl.json b/homeassistant/components/min_max/translations/nl.json index 75d8d69357b..eb2106cc366 100644 --- a/homeassistant/components/min_max/translations/nl.json +++ b/homeassistant/components/min_max/translations/nl.json @@ -3,7 +3,7 @@ "step": { "user": { "data": { - "entity_ids": "Entiteiten invoeren", + "entity_ids": "Invoerentiteiten", "name": "Naam", "round_digits": "Precisie", "type": "statistisch kenmerk" @@ -20,7 +20,7 @@ "step": { "init": { "data": { - "entity_ids": "Entiteiten invoeren", + "entity_ids": "Invoerentiteiten", "round_digits": "Precisie", "type": "statistisch kenmerk" }, diff --git a/homeassistant/components/modem_callerid/translations/nl.json b/homeassistant/components/modem_callerid/translations/nl.json index 6e26dd3d1c0..c5107ce6e53 100644 --- a/homeassistant/components/modem_callerid/translations/nl.json +++ b/homeassistant/components/modem_callerid/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "no_devices_found": "Geen resterende apparaten gevonden" }, "error": { diff --git a/homeassistant/components/modern_forms/translations/ko.json b/homeassistant/components/modern_forms/translations/ko.json new file mode 100644 index 00000000000..6079edcd0da --- /dev/null +++ b/homeassistant/components/modern_forms/translations/ko.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + }, + "flow_title": "{name}", + "step": { + "user": { + "data": { + "host": "\ud638\uc2a4\ud2b8" + }, + "description": "Home Assistant\uc640 \uc5f0\uacb0\ub418\ub3c4\ub85d Modern Forms \ud32c\uc744 \uc124\uc815\ud558\uc2ed\uc2dc\uc624." + }, + "zeroconf_confirm": { + "description": "` {name} ` Modern Forms \ud32c\uc744 Home Assistant\uc5d0 \ucd94\uac00\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", + "title": "Modern Forms \ud32c \uc7a5\uce58 \ubc1c\uacac" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/moon/translations/nl.json b/homeassistant/components/moon/translations/nl.json index ebcef695e04..0d626abfd7b 100644 --- a/homeassistant/components/moon/translations/nl.json +++ b/homeassistant/components/moon/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/motion_blinds/translations/nl.json b/homeassistant/components/motion_blinds/translations/nl.json index 73522480df5..316e2077796 100644 --- a/homeassistant/components/motion_blinds/translations/nl.json +++ b/homeassistant/components/motion_blinds/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "connection_error": "Kan geen verbinding maken" }, "error": { diff --git a/homeassistant/components/motioneye/translations/nl.json b/homeassistant/components/motioneye/translations/nl.json index dce66fcb5d1..a2e053e2493 100644 --- a/homeassistant/components/motioneye/translations/nl.json +++ b/homeassistant/components/motioneye/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/mqtt/translations/ko.json b/homeassistant/components/mqtt/translations/ko.json index dccd49b2ef3..454b1e0368f 100644 --- a/homeassistant/components/mqtt/translations/ko.json +++ b/homeassistant/components/mqtt/translations/ko.json @@ -62,7 +62,8 @@ "port": "\ud3ec\ud2b8", "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" }, - "description": "MQTT \ube0c\ub85c\ucee4\uc640\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694." + "description": "MQTT \ube0c\ub85c\ucee4\uc640\uc758 \uc5f0\uacb0 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694.", + "title": "\ube0c\ub85c\ucee4 \uc635\uc158" }, "options": { "data": { @@ -78,7 +79,8 @@ "will_retain": "Will \uba54\uc2dc\uc9c0 \ub9ac\ud14c\uc778", "will_topic": "Will \uba54\uc2dc\uc9c0 \ud1a0\ud53d" }, - "description": "MQTT \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694." + "description": "MQTT \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.", + "title": "MQTT \uc635\uc158" } } } diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index 40448942a9f..d948cc43df7 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "already_configured": "Dienst is al geconfigureerd", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "already_configured": "Service is al geconfigureerd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken" @@ -53,7 +53,7 @@ "error": { "bad_birth": "Ongeldig birth topic", "bad_will": "Ongeldig will topic", - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "broker": { diff --git a/homeassistant/components/myq/translations/ko.json b/homeassistant/components/myq/translations/ko.json index 23ba2eecea7..1218ce0e4f4 100644 --- a/homeassistant/components/myq/translations/ko.json +++ b/homeassistant/components/myq/translations/ko.json @@ -9,6 +9,10 @@ "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { + "reauth_confirm": { + "description": "{username} \uc758 \ube44\ubc00\ubc88\ud638\uac00 \ub354 \uc774\uc0c1 \uc720\ud6a8\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "title": "MyQ \uacc4\uc815 \uc7ac\uc778\uc99d" + }, "user": { "data": { "password": "\ube44\ubc00\ubc88\ud638", diff --git a/homeassistant/components/myq/translations/nl.json b/homeassistant/components/myq/translations/nl.json index 09a36665414..8a3020679e9 100644 --- a/homeassistant/components/myq/translations/nl.json +++ b/homeassistant/components/myq/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/mysensors/translations/ko.json b/homeassistant/components/mysensors/translations/ko.json index 7a6f3e856a7..4d5be0f44e3 100644 --- a/homeassistant/components/mysensors/translations/ko.json +++ b/homeassistant/components/mysensors/translations/ko.json @@ -33,6 +33,7 @@ "invalid_serial": "\uc2dc\ub9ac\uc5bc \ud3ec\ud2b8\uac00 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_subscribe_topic": "\uad6c\ub3c5 \ud1a0\ud53d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "invalid_version": "MySensors \ubc84\uc804\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "mqtt_required": "MQTT \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \uc124\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.", "not_a_number": "\uc22b\uc790\ub85c \uc785\ub825\ud574\uc8fc\uc138\uc694", "port_out_of_range": "\ud3ec\ud2b8 \ubc88\ud638\ub294 1 \uc774\uc0c1 65535 \uc774\ud558\uc774\uc5b4\uc57c \ud569\ub2c8\ub2e4", "same_topic": "\uad6c\ub3c5 \ubc0f \ubc1c\ud589 \ud1a0\ud53d\uc740 \ub3d9\uc77c\ud569\ub2c8\ub2e4", diff --git a/homeassistant/components/nam/translations/ko.json b/homeassistant/components/nam/translations/ko.json index a4cc4c01810..8112e2968c5 100644 --- a/homeassistant/components/nam/translations/ko.json +++ b/homeassistant/components/nam/translations/ko.json @@ -1,6 +1,7 @@ { "config": { "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "reauth_unsuccessful": "\uc7ac\uc778\uc99d\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4. \uad6c\uc131\uc744 \uc81c\uac70\ud558\uace0 \ub2e4\uc2dc \uc124\uc815\ud558\uc2ed\uc2dc\uc624." }, "step": { diff --git a/homeassistant/components/nam/translations/nl.json b/homeassistant/components/nam/translations/nl.json index f900dccc295..a3d7925479f 100644 --- a/homeassistant/components/nam/translations/nl.json +++ b/homeassistant/components/nam/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "device_unsupported": "Het apparaat wordt niet ondersteund.", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "reauth_unsuccessful": "Herauthenticatie is mislukt, verwijder de integratie en stel het opnieuw in." }, "error": { diff --git a/homeassistant/components/nanoleaf/translations/nl.json b/homeassistant/components/nanoleaf/translations/nl.json index 76c471769e3..292eb066bf3 100644 --- a/homeassistant/components/nanoleaf/translations/nl.json +++ b/homeassistant/components/nanoleaf/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_token": "Ongeldig toegangstoken", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/neato/translations/nl.json b/homeassistant/components/neato/translations/nl.json index 0bf8009b88e..53cf562a748 100644 --- a/homeassistant/components/neato/translations/nl.json +++ b/homeassistant/components/neato/translations/nl.json @@ -2,20 +2,20 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "reauth_successful": "Herauthenticatie was succesvol" + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { "title": "Kies een authenticatie methode" }, "reauth_confirm": { - "title": "Wilt u beginnen met instellen?" + "title": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/nest/translations/nl.json b/homeassistant/components/nest/translations/nl.json index 0e2984972af..2b486ce79e2 100644 --- a/homeassistant/components/nest/translations/nl.json +++ b/homeassistant/components/nest/translations/nl.json @@ -1,21 +1,21 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "invalid_access_token": "Ongeldig toegangstoken", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "reauth_successful": "Herauthenticatie was succesvol", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "reauth_successful": "Herauthenticatie geslaagd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "error": { "bad_project_id": "Voer een geldige Cloud Project ID in (controleer Cloud Console)", "internal_error": "Interne foutvalidatiecode", - "invalid_pin": "Ongeldige PIN-code", + "invalid_pin": "Ongeldige Pincode", "subscriber_error": "Onbekende abonneefout, zie logs", "timeout": "Time-out validatie van code", "unknown": "Onverwachte fout", @@ -38,7 +38,7 @@ }, "link": { "data": { - "code": "PIN-code" + "code": "Pincode" }, "description": "Als je je Nest-account wilt koppelen, [autoriseer je account] ( {url} ). \n\nNa autorisatie, kopieer en plak de voorziene pincode hieronder.", "title": "Koppel Nest-account" @@ -55,7 +55,7 @@ }, "reauth_confirm": { "description": "De Nest-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } }, diff --git a/homeassistant/components/netatmo/translations/nl.json b/homeassistant/components/netatmo/translations/nl.json index a5ddba5772c..e0b8998d652 100644 --- a/homeassistant/components/netatmo/translations/nl.json +++ b/homeassistant/components/netatmo/translations/nl.json @@ -1,22 +1,22 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out genereren autorisatie-URL.", - "missing_configuration": "Het component is niet geconfigureerd. Volg de documentatie.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "reauth_successful": "Herauthenticatie was succesvol", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "reauth_successful": "Herauthenticatie geslaagd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { - "title": "Kies de verificatiemethode" + "title": "Kies een authenticatie methode" }, "reauth_confirm": { "description": "De Netatmo-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } }, diff --git a/homeassistant/components/netgear/translations/nl.json b/homeassistant/components/netgear/translations/nl.json index 95e824ef591..8a613738ffd 100644 --- a/homeassistant/components/netgear/translations/nl.json +++ b/homeassistant/components/netgear/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Apparaat is al ingesteld" + "already_configured": "Apparaat is al geconfigureerd" }, "error": { "config": "Verbindings- of inlogfout; controleer uw configuratie" diff --git a/homeassistant/components/nina/translations/nl.json b/homeassistant/components/nina/translations/nl.json index 3531cd8bb72..4b0100f9f07 100644 --- a/homeassistant/components/nina/translations/nl.json +++ b/homeassistant/components/nina/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/nmap_tracker/translations/nl.json b/homeassistant/components/nmap_tracker/translations/nl.json index 2331d7fda9f..6221162035a 100644 --- a/homeassistant/components/nmap_tracker/translations/nl.json +++ b/homeassistant/components/nmap_tracker/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "invalid_hosts": "Ongeldige hosts" diff --git a/homeassistant/components/notion/translations/nl.json b/homeassistant/components/notion/translations/nl.json index 3a86ffd2264..130d45635b8 100644 --- a/homeassistant/components/notion/translations/nl.json +++ b/homeassistant/components/notion/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/nuki/translations/ko.json b/homeassistant/components/nuki/translations/ko.json index 3015596e7d4..75e9ab53e84 100644 --- a/homeassistant/components/nuki/translations/ko.json +++ b/homeassistant/components/nuki/translations/ko.json @@ -13,6 +13,7 @@ "data": { "token": "\uc561\uc138\uc2a4 \ud1a0\ud070" }, + "description": "Nuki \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \ube0c\ub9ac\uc9c0\ub85c \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" }, "user": { diff --git a/homeassistant/components/nuki/translations/nl.json b/homeassistant/components/nuki/translations/nl.json index 4157d21ffd3..b5c41a05421 100644 --- a/homeassistant/components/nuki/translations/nl.json +++ b/homeassistant/components/nuki/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -14,7 +14,7 @@ "token": "Toegangstoken" }, "description": "De Nuki integratie moet opnieuw authenticeren met uw bridge.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/nzbget/translations/nl.json b/homeassistant/components/nzbget/translations/nl.json index 5f98d7435ec..80c96a509f2 100644 --- a/homeassistant/components/nzbget/translations/nl.json +++ b/homeassistant/components/nzbget/translations/nl.json @@ -5,7 +5,7 @@ "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/omnilogic/translations/nl.json b/homeassistant/components/omnilogic/translations/nl.json index b94599f93f4..ca6bd42b2b3 100644 --- a/homeassistant/components/omnilogic/translations/nl.json +++ b/homeassistant/components/omnilogic/translations/nl.json @@ -5,14 +5,14 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_auth": "Onjuiste gebruikersgegevens", + "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, "step": { "user": { "data": { "password": "Wachtwoord", - "username": "Benutzername" + "username": "Gebruikersnaam" } } } diff --git a/homeassistant/components/ondilo_ico/translations/nl.json b/homeassistant/components/ondilo_ico/translations/nl.json index 0613d559fce..bf5ad718617 100644 --- a/homeassistant/components/ondilo_ico/translations/nl.json +++ b/homeassistant/components/ondilo_ico/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen." + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/onvif/translations/nl.json b/homeassistant/components/onvif/translations/nl.json index f1101f759e9..f76aef12557 100644 --- a/homeassistant/components/onvif/translations/nl.json +++ b/homeassistant/components/onvif/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al begonnen", + "already_in_progress": "De configuratie is momenteel al bezig", "no_h264": "Er waren geen H264-streams beschikbaar. Controleer de profielconfiguratie op uw apparaat.", "no_mac": "Kan geen unieke ID configureren voor ONVIF-apparaat.", "onvif_error": "Fout bij het instellen van ONVIF-apparaat. Controleer de logboeken voor meer informatie." diff --git a/homeassistant/components/opentherm_gw/translations/nl.json b/homeassistant/components/opentherm_gw/translations/nl.json index fd8c97c4371..97f392075d8 100644 --- a/homeassistant/components/opentherm_gw/translations/nl.json +++ b/homeassistant/components/opentherm_gw/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "error": { - "already_configured": "Gateway al geconfigureerd", + "already_configured": "Apparaat is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "id_exists": "Gateway id bestaat al" }, diff --git a/homeassistant/components/openuv/translations/nl.json b/homeassistant/components/openuv/translations/nl.json index bd56a1fa4e0..a85799658e3 100644 --- a/homeassistant/components/openuv/translations/nl.json +++ b/homeassistant/components/openuv/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "invalid_api_key": "Ongeldige API-sleutel" diff --git a/homeassistant/components/openweathermap/translations/nl.json b/homeassistant/components/openweathermap/translations/nl.json index 74ea3532765..85a685590e2 100644 --- a/homeassistant/components/openweathermap/translations/nl.json +++ b/homeassistant/components/openweathermap/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "OpenWeatherMap-integratie voor deze co\u00f6rdinaten is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -10,11 +10,11 @@ "step": { "user": { "data": { - "api_key": "OpenWeatherMap API-sleutel", + "api_key": "API-sleutel", "language": "Taal", "latitude": "Breedtegraad", "longitude": "Lengtegraad", - "mode": "Mode", + "mode": "Modus", "name": "Naam" }, "description": "Om een API sleutel te genereren ga naar https://openweathermap.org/appid" @@ -26,7 +26,7 @@ "init": { "data": { "language": "Taal", - "mode": "Mode" + "mode": "Modus" } } } diff --git a/homeassistant/components/overkiz/translations/nl.json b/homeassistant/components/overkiz/translations/nl.json index 5057f12afa6..957ca0862fc 100644 --- a/homeassistant/components/overkiz/translations/nl.json +++ b/homeassistant/components/overkiz/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "reauth_wrong_account": "U kunt deze invoer alleen opnieuw verifi\u00ebren met hetzelfde Overkiz account en hub" }, "error": { diff --git a/homeassistant/components/overkiz/translations/select.ko.json b/homeassistant/components/overkiz/translations/select.ko.json new file mode 100644 index 00000000000..c2af52fab9c --- /dev/null +++ b/homeassistant/components/overkiz/translations/select.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "overkiz__open_closed_pedestrian": { + "closed": "\ub2eb\ud798" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/panasonic_viera/translations/nl.json b/homeassistant/components/panasonic_viera/translations/nl.json index fa63892730e..7e778c1e932 100644 --- a/homeassistant/components/panasonic_viera/translations/nl.json +++ b/homeassistant/components/panasonic_viera/translations/nl.json @@ -7,14 +7,14 @@ }, "error": { "cannot_connect": "Kan geen verbinding maken", - "invalid_pin_code": "De PIN-code die u hebt ingevoerd is ongeldig" + "invalid_pin_code": "De Pincode die u hebt ingevoerd is ongeldig" }, "step": { "pairing": { "data": { - "pin": "PIN-code" + "pin": "Pincode" }, - "description": "Voer de PIN-code in die op uw TV wordt weergegeven", + "description": "Voer de Pincode in die op uw TV wordt weergegeven", "title": "Koppelen" }, "user": { diff --git a/homeassistant/components/philips_js/translations/nl.json b/homeassistant/components/philips_js/translations/nl.json index 0b172e24f56..09f6b3b8f3d 100644 --- a/homeassistant/components/philips_js/translations/nl.json +++ b/homeassistant/components/philips_js/translations/nl.json @@ -12,7 +12,7 @@ "step": { "pair": { "data": { - "pin": "PIN-code" + "pin": "Pincode" }, "description": "Voer de pincode in die op uw tv wordt weergegeven", "title": "Koppel" diff --git a/homeassistant/components/pi_hole/translations/nl.json b/homeassistant/components/pi_hole/translations/nl.json index 156a248a80b..4c3919506ef 100644 --- a/homeassistant/components/pi_hole/translations/nl.json +++ b/homeassistant/components/pi_hole/translations/nl.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "already_configured": "Service al geconfigureerd" + "already_configured": "Service is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "api_key": { diff --git a/homeassistant/components/picnic/translations/nl.json b/homeassistant/components/picnic/translations/nl.json index 1d007e5eb43..7f7a2199d27 100644 --- a/homeassistant/components/picnic/translations/nl.json +++ b/homeassistant/components/picnic/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/plaato/translations/nl.json b/homeassistant/components/plaato/translations/nl.json index 83e2874ed73..938402d8ea9 100644 --- a/homeassistant/components/plaato/translations/nl.json +++ b/homeassistant/components/plaato/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Account is al geconfigureerd", "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Uw Plaato {device_type} met naam **{device_name}** is succesvol ingesteld!" @@ -28,7 +28,7 @@ "device_name": "Geef uw apparaat een naam", "device_type": "Type Plaato-apparaat" }, - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "Stel de Plaato-apparaten in" }, "webhook": { diff --git a/homeassistant/components/plex/translations/nl.json b/homeassistant/components/plex/translations/nl.json index afdc67007bd..4d9f30056bd 100644 --- a/homeassistant/components/plex/translations/nl.json +++ b/homeassistant/components/plex/translations/nl.json @@ -3,8 +3,8 @@ "abort": { "all_configured": "Alle gekoppelde servers zijn al geconfigureerd", "already_configured": "Deze Plex-server is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "reauth_successful": "Herauthenticatie was succesvol", + "already_in_progress": "De configuratie is momenteel al bezig", + "reauth_successful": "Herauthenticatie geslaagd", "token_request_timeout": "Time-out verkrijgen van token", "unknown": "Onverwachte fout" }, @@ -23,7 +23,7 @@ "port": "Poort", "ssl": "Maakt gebruik van een SSL-certificaat", "token": "Token (optioneel)", - "verify_ssl": "Controleer het SSL-certificaat" + "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "title": "Handmatige Plex-configuratie" }, diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index d295695016f..4dfd7d3b4e4 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "De service is al geconfigureerd" + "already_configured": "Service is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -23,7 +23,7 @@ "host": "IP-adres", "password": "Smile-ID", "port": "Poort", - "username": "Smile Gebruikersnaam" + "username": "Smile gebruikersnaam" }, "description": "Voer in", "title": "Maak verbinding met de Smile" diff --git a/homeassistant/components/plum_lightpad/translations/nl.json b/homeassistant/components/plum_lightpad/translations/nl.json index 8410cabbbb9..6009e1fb384 100644 --- a/homeassistant/components/plum_lightpad/translations/nl.json +++ b/homeassistant/components/plum_lightpad/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Account is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "step": { "user": { diff --git a/homeassistant/components/point/translations/nl.json b/homeassistant/components/point/translations/nl.json index f0ab4d8696e..9a0f0c940a8 100644 --- a/homeassistant/components/point/translations/nl.json +++ b/homeassistant/components/point/translations/nl.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "already_setup": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "already_setup": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "external_setup": "Punt succesvol geconfigureerd vanuit een andere stroom.", - "no_flows": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", + "no_flows": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "error": { "follow_link": "Volg de link en verifieer voordat je op Verzenden klikt", @@ -23,7 +23,7 @@ "data": { "flow_impl": "Leverancier" }, - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "Kies een authenticatie methode" } } diff --git a/homeassistant/components/powerwall/translations/nl.json b/homeassistant/components/powerwall/translations/nl.json index 007d4e9e86b..45172d177b9 100644 --- a/homeassistant/components/powerwall/translations/nl.json +++ b/homeassistant/components/powerwall/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/profiler/translations/nl.json b/homeassistant/components/profiler/translations/nl.json index 8b99a128bd3..8690611b1c9 100644 --- a/homeassistant/components/profiler/translations/nl.json +++ b/homeassistant/components/profiler/translations/nl.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/prosegur/translations/nl.json b/homeassistant/components/prosegur/translations/nl.json index d87556b0742..db84f7df172 100644 --- a/homeassistant/components/prosegur/translations/nl.json +++ b/homeassistant/components/prosegur/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/ps4/translations/nl.json b/homeassistant/components/ps4/translations/nl.json index 8bebf95d0f0..c6e16281c19 100644 --- a/homeassistant/components/ps4/translations/nl.json +++ b/homeassistant/components/ps4/translations/nl.json @@ -10,7 +10,7 @@ "error": { "cannot_connect": "Kan geen verbinding maken", "credential_timeout": "Time-out van inlog service. Druk op Submit om opnieuw te starten.", - "login_failed": "Kan Playstation 4 niet koppelen. Controleer of de PIN-code correct is.", + "login_failed": "Kan Playstation 4 niet koppelen. Controleer of de Pincode correct is.", "no_ipaddress": "Voer het IP-adres in van de PlayStation 4 die u wilt configureren." }, "step": { @@ -19,7 +19,7 @@ }, "link": { "data": { - "code": "PIN-code", + "code": "Pincode", "ip_address": "IP-adres", "name": "Naam", "region": "Regio" diff --git a/homeassistant/components/pure_energie/translations/ko.json b/homeassistant/components/pure_energie/translations/ko.json new file mode 100644 index 00000000000..b4373b03a25 --- /dev/null +++ b/homeassistant/components/pure_energie/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "zeroconf_confirm": { + "title": "Pure Energie Meter \uae30\uae30 \ubc1c\uacac" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/pvoutput/translations/nl.json b/homeassistant/components/pvoutput/translations/nl.json index 1619f899b73..e991fe84e32 100644 --- a/homeassistant/components/pvoutput/translations/nl.json +++ b/homeassistant/components/pvoutput/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -10,7 +10,7 @@ "step": { "reauth_confirm": { "data": { - "api_key": "API sleutel" + "api_key": "API-sleutel" }, "description": "Om opnieuw te verifi\u00ebren met PVOutput, moet u de API-sleutel op {account_url} ophalen." }, diff --git a/homeassistant/components/radio_browser/translations/nl.json b/homeassistant/components/radio_browser/translations/nl.json index d8f46a3130b..a4e4d7a6a33 100644 --- a/homeassistant/components/radio_browser/translations/nl.json +++ b/homeassistant/components/radio_browser/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { diff --git a/homeassistant/components/rainmachine/translations/ko.json b/homeassistant/components/rainmachine/translations/ko.json index 0e38f4c4dfa..220cb38d8c5 100644 --- a/homeassistant/components/rainmachine/translations/ko.json +++ b/homeassistant/components/rainmachine/translations/ko.json @@ -6,6 +6,7 @@ "error": { "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4" }, + "flow_title": "{ip}", "step": { "user": { "data": { diff --git a/homeassistant/components/renault/translations/nl.json b/homeassistant/components/renault/translations/nl.json index c2e02b03166..212dd860874 100644 --- a/homeassistant/components/renault/translations/nl.json +++ b/homeassistant/components/renault/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Account is al geconfigureerd", "kamereon_no_account": "Kan Kamereon-account niet vinden.", - "reauth_successful": "Opnieuw verifi\u00ebren is gelukt" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_credentials": "Ongeldige authenticatie" @@ -20,7 +20,7 @@ "password": "Wachtwoord" }, "description": "Werk uw wachtwoord voor {gebruikersnaam} bij", - "title": "Integratie opnieuw verifi\u00ebren" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/rfxtrx/translations/nl.json b/homeassistant/components/rfxtrx/translations/nl.json index 5a0ced3f52d..ee5e8b4a463 100644 --- a/homeassistant/components/rfxtrx/translations/nl.json +++ b/homeassistant/components/rfxtrx/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/ridwell/translations/nl.json b/homeassistant/components/ridwell/translations/nl.json index afec8a578f5..e230ffec6f8 100644 --- a/homeassistant/components/ridwell/translations/nl.json +++ b/homeassistant/components/ridwell/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in:", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/risco/translations/nl.json b/homeassistant/components/risco/translations/nl.json index fdf1ee5ad23..acab8c172c3 100644 --- a/homeassistant/components/risco/translations/nl.json +++ b/homeassistant/components/risco/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, @@ -12,7 +12,7 @@ "user": { "data": { "password": "Wachtwoord", - "pin": "PIN-code", + "pin": "Pincode", "username": "Gebruikersnaam" } } @@ -32,8 +32,8 @@ }, "init": { "data": { - "code_arm_required": "PIN-code vereist om in te schakelen", - "code_disarm_required": "PIN-code vereist om uit te schakelen", + "code_arm_required": "Pincode vereist om in te schakelen", + "code_disarm_required": "Pincode vereist om uit te schakelen", "scan_interval": "Hoe vaak moet Risco worden ververst (in seconden)" }, "title": "Configureer opties" diff --git a/homeassistant/components/roku/translations/nl.json b/homeassistant/components/roku/translations/nl.json index 7f413121fa7..e809c854060 100644 --- a/homeassistant/components/roku/translations/nl.json +++ b/homeassistant/components/roku/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/roon/translations/nl.json b/homeassistant/components/roon/translations/nl.json index fb35f22ce27..ad07fb6bc15 100644 --- a/homeassistant/components/roon/translations/nl.json +++ b/homeassistant/components/roon/translations/nl.json @@ -1,10 +1,10 @@ { "config": { "abort": { - "already_configured": "Apparaat is al toegevoegd" + "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "invalid_auth": "Ongeldige authencatie", + "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/rpi_power/translations/nl.json b/homeassistant/components/rpi_power/translations/nl.json index d9e42ef11f3..5529aa39f20 100644 --- a/homeassistant/components/rpi_power/translations/nl.json +++ b/homeassistant/components/rpi_power/translations/nl.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/rtsp_to_webrtc/translations/nl.json b/homeassistant/components/rtsp_to_webrtc/translations/nl.json index 6b3344f2b6b..6454c48c22f 100644 --- a/homeassistant/components/rtsp_to_webrtc/translations/nl.json +++ b/homeassistant/components/rtsp_to_webrtc/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "server_failure": "RtsPtoWebRTC-server retourneerde een fout. Raadpleeg de logboeken voor meer informatie.", "server_unreachable": "Kan niet communiceren met de RTSPtoWebRTC-server. Controleer logboeken voor meer informatie.", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_url": "Moet een geldige RTSPtoWebRTC server URL zijn, b.v. https://example.com", diff --git a/homeassistant/components/samsungtv/translations/ko.json b/homeassistant/components/samsungtv/translations/ko.json index 8b129d8ab50..5a3c64f120b 100644 --- a/homeassistant/components/samsungtv/translations/ko.json +++ b/homeassistant/components/samsungtv/translations/ko.json @@ -5,7 +5,13 @@ "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", "auth_missing": "Home Assistant\uac00 \ud574\ub2f9 \uc0bc\uc131 TV\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. TV \uc124\uc815\uc744 \ud655\uc778\ud558\uc5ec Home Assistant\ub97c \uc2b9\uc778\ud574\uc8fc\uc138\uc694.", "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", - "not_supported": "\uc774 \uc0bc\uc131 TV \ubaa8\ub378\uc740 \ud604\uc7ac \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4." + "id_missing": "\ud574\ub2f9 \uc0bc\uc131 \uae30\uae30\uc5d0 \uc77c\ub828\ubc88\ud638\uac00 \uc5c6\uc2b5\ub2c8\ub2e4.", + "not_supported": "\uc774 \uc0bc\uc131 TV \ubaa8\ub378\uc740 \ud604\uc7ac \uc9c0\uc6d0\ub418\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "auth_missing": "Home Assistant\uac00 \ud574\ub2f9 \uc0bc\uc131 TV\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. TV \uc124\uc815\uc744 \ud655\uc778\ud558\uc5ec Home Assistant\ub97c \uc2b9\uc778\ud574\uc8fc\uc138\uc694." }, "flow_title": "\uc0bc\uc131 TV: {model}", "step": { @@ -15,6 +21,9 @@ "pairing": { "description": "{device} \uc0bc\uc131 TV\ub97c \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? Home Assistant\ub97c \uc5f0\uacb0\ud55c \uc801\uc774 \uc5c6\ub2e4\uba74 TV\uc5d0\uc11c \uc778\uc99d\uc744 \uc694\uccad\ud558\ub294 \ud31d\uc5c5\uc774 \ud45c\uc2dc\ub429\ub2c8\ub2e4. \uc774 TV\uc758 \uc218\ub3d9\uc73c\ub85c \uad6c\uc131\ub41c \ub0b4\uc6a9\uc740 \ub36e\uc5b4\uc50c\uc6cc\uc9d1\ub2c8\ub2e4." }, + "reauth_confirm": { + "description": "\ud655\uc778 \ud6c4 30\ucd08 \uc774\ub0b4\uc5d0 \uc2b9\uc778\uc744 \uc694\uccad\ud558\ub294 {device} \ud31d\uc5c5\uc744 \uc218\ub77d\ud558\uac70\ub098 PIN\uc744 \uc785\ub825\ud558\uc138\uc694." + }, "user": { "data": { "host": "\ud638\uc2a4\ud2b8", diff --git a/homeassistant/components/samsungtv/translations/nl.json b/homeassistant/components/samsungtv/translations/nl.json index 9fe7e1dc3f9..4b69c8e812c 100644 --- a/homeassistant/components/samsungtv/translations/nl.json +++ b/homeassistant/components/samsungtv/translations/nl.json @@ -2,12 +2,12 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "auth_missing": "Home Assistant is niet gemachtigd om verbinding te maken met deze Samsung TV. Controleer de instellingen van Extern apparaatbeheer van uw tv om Home Assistant te machtigen.", "cannot_connect": "Kan geen verbinding maken", "id_missing": "Dit Samsung-apparaat heeft geen serienummer.", "not_supported": "Deze Samsung TV wordt momenteel niet ondersteund.", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/season/translations/nl.json b/homeassistant/components/season/translations/nl.json index 63067c8a814..1da54d9c360 100644 --- a/homeassistant/components/season/translations/nl.json +++ b/homeassistant/components/season/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Dienst is al geconfigureerd" + "already_configured": "Service is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/sense/translations/ko.json b/homeassistant/components/sense/translations/ko.json index 269d8a76fea..b28f41f0d64 100644 --- a/homeassistant/components/sense/translations/ko.json +++ b/homeassistant/components/sense/translations/ko.json @@ -15,6 +15,9 @@ "password": "\ube44\ubc00\ubc88\ud638" }, "title": "Sense Energy Monitor\uc5d0 \uc5f0\uacb0\ud558\uae30" + }, + "validation": { + "title": "Sense \ub2e4\ub2e8\uacc4 \uc778\uc99d" } } } diff --git a/homeassistant/components/sense/translations/nl.json b/homeassistant/components/sense/translations/nl.json index 11b1c07350c..1f2f9e3d26b 100644 --- a/homeassistant/components/sense/translations/nl.json +++ b/homeassistant/components/sense/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -15,7 +15,7 @@ "password": "Wachtwoord" }, "description": "De Sense-integratie moet uw account {email} opnieuw verifi\u00ebren.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/sensibo/translations/ko.json b/homeassistant/components/sensibo/translations/ko.json new file mode 100644 index 00000000000..7f03247bb3d --- /dev/null +++ b/homeassistant/components/sensibo/translations/ko.json @@ -0,0 +1,7 @@ +{ + "config": { + "error": { + "no_devices": "\uac80\uc0c9\ub41c \uae30\uae30 \uc5c6\uc74c" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensibo/translations/nl.json b/homeassistant/components/sensibo/translations/nl.json index 7235aa80a80..fe04b6b64a8 100644 --- a/homeassistant/components/sensibo/translations/nl.json +++ b/homeassistant/components/sensibo/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/sensor/translations/ko.json b/homeassistant/components/sensor/translations/ko.json index 69ff6adb5e7..b01e5d95b9e 100644 --- a/homeassistant/components/sensor/translations/ko.json +++ b/homeassistant/components/sensor/translations/ko.json @@ -27,6 +27,7 @@ "power": "{entity_name}\uc758 \uc18c\ube44 \uc804\ub825\uc774 \ubcc0\ud560 \ub54c", "power_factor": "{entity_name}\uc758 \uc5ed\ub960\uc774 \ubcc0\ud560 \ub54c", "pressure": "{entity_name}\uc758 \uc555\ub825\uc774 \ubcc0\ud560 \ub54c", + "reactive_power": "{entity_name} \ubb34\ud6a8 \uc804\ub825\uc774 \ubcc0\ud560 \ub54c", "signal_strength": "{entity_name}\uc758 \uc2e0\ud638 \uac15\ub3c4\uac00 \ubcc0\ud560 \ub54c", "temperature": "{entity_name}\uc758 \uc628\ub3c4\uac00 \ubcc0\ud560 \ub54c", "value": "{entity_name}\uc758 \uac12\uc774 \ubcc0\ud560 \ub54c", diff --git a/homeassistant/components/senz/translations/nl.json b/homeassistant/components/senz/translations/nl.json index 0f50cb0f918..7f1eaccf89c 100644 --- a/homeassistant/components/senz/translations/nl.json +++ b/homeassistant/components/senz/translations/nl.json @@ -2,14 +2,14 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "oauth_error": "Ongeldige token data ontvangen." + "already_in_progress": "De configuratie is momenteel al bezig", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "oauth_error": "Ongeldige tokengegevens ontvangen." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/sharkiq/translations/nl.json b/homeassistant/components/sharkiq/translations/nl.json index 3acfdbdf074..c76fc24ea92 100644 --- a/homeassistant/components/sharkiq/translations/nl.json +++ b/homeassistant/components/sharkiq/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Account is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { @@ -14,7 +14,7 @@ "step": { "reauth": { "data": { - "password": "Paswoord", + "password": "Wachtwoord", "username": "Gebruikersnaam" } }, diff --git a/homeassistant/components/shelly/translations/ja.json b/homeassistant/components/shelly/translations/ja.json index 12a97c8508a..748e70fd32a 100644 --- a/homeassistant/components/shelly/translations/ja.json +++ b/homeassistant/components/shelly/translations/ja.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "firmware_not_fully_provisioned": "\u30c7\u30d0\u30a4\u30b9\u304c\u5b8c\u5168\u306b\u30d7\u30ed\u30d3\u30b8\u30e7\u30cb\u30f3\u30b0(provisioned)\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002Shelly\u30b5\u30dd\u30fc\u30c8\u306b\u9023\u7d61\u3057\u3066\u304f\u3060\u3055\u3044", "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" }, diff --git a/homeassistant/components/shelly/translations/no.json b/homeassistant/components/shelly/translations/no.json index fe7d30c098f..072d0bf4ee7 100644 --- a/homeassistant/components/shelly/translations/no.json +++ b/homeassistant/components/shelly/translations/no.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Tilkobling mislyktes", + "firmware_not_fully_provisioned": "Enheten er ikke fullstendig klargjort. Vennligst kontakt Shelly support", "invalid_auth": "Ugyldig godkjenning", "unknown": "Uventet feil" }, diff --git a/homeassistant/components/shelly/translations/pl.json b/homeassistant/components/shelly/translations/pl.json index d1c658d7816..71d7f047e5f 100644 --- a/homeassistant/components/shelly/translations/pl.json +++ b/homeassistant/components/shelly/translations/pl.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "firmware_not_fully_provisioned": "Urz\u0105dzenie nie jest w pe\u0142ni udost\u0119pnione. Skontaktuj si\u0119 z pomoc\u0105 techniczn\u0105 Shelly.", "invalid_auth": "Niepoprawne uwierzytelnienie", "unknown": "Nieoczekiwany b\u0142\u0105d" }, diff --git a/homeassistant/components/sia/translations/ko.json b/homeassistant/components/sia/translations/ko.json new file mode 100644 index 00000000000..cb0897c7873 --- /dev/null +++ b/homeassistant/components/sia/translations/ko.json @@ -0,0 +1,12 @@ +{ + "config": { + "error": { + "invalid_account_format": "\uacc4\uc815\uc774 16\uc9c4\uc218 \uac12\uc774 \uc544\ub2d9\ub2c8\ub2e4. 0-9 \ubc0f A-F\ub9cc \uc0ac\uc6a9\ud558\uc2ed\uc2dc\uc624.", + "invalid_account_length": "\uacc4\uc815\uc758 \uae38\uc774\uac00 \uc801\uc808\uce58 \uc54a\uc2b5\ub2c8\ub2e4. 3~16\uc790 \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4.", + "invalid_key_format": "\ud0a4\uac12\uc774 16\uc9c4\uc218 \uac12\uc774 \uc544\ub2d9\ub2c8\ub2e4. 0-9 \ubc0f A-F\ub9cc \uc0ac\uc6a9\ud558\uc2ed\uc2dc\uc624.", + "invalid_key_length": "\ud0a4\uac12\uc758 \uae38\uc774\uac00 \uc801\uc808\uce58 \uc54a\uc2b5\ub2c8\ub2e4. 16, 24 \ub610\ub294 32\uac1c\uc758 16\uc9c4\uc218\ubb38\uc790\uc5ec\uc57c \ub429\ub2c8\ub2e4.", + "invalid_ping": "\ud551 \uac04\uaca9\uc740 1\ubd84\uc5d0\uc11c 1440\ubd84 \uc0ac\uc774\uc5ec\uc57c \ud569\ub2c8\ub2e4.", + "invalid_zones": "\ucd5c\uc18c\ud55c 1\uac1c\uc758 \uc601\uc5ed\uc774 \uc788\uc5b4\uc57c \ud569\ub2c8\ub2e4." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sia/translations/nl.json b/homeassistant/components/sia/translations/nl.json index cd0d5183cb9..0b3790d53e2 100644 --- a/homeassistant/components/sia/translations/nl.json +++ b/homeassistant/components/sia/translations/nl.json @@ -26,7 +26,7 @@ "additional_account": "Extra accounts", "encryption_key": "Encryptiesleutel", "ping_interval": "Ping Interval (min)", - "port": "Port", + "port": "Poort", "protocol": "Protocol", "zones": "Aantal zones voor het account" }, diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index 55e7c3c7832..34db358c02b 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Dit SimpliSafe-account is al in gebruik.", "email_2fa_timed_out": "Timed out tijdens het wachten op email-gebaseerde twee-factor authenticatie.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -18,7 +18,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "sms_2fa": { "data": { diff --git a/homeassistant/components/siren/translations/pl.json b/homeassistant/components/siren/translations/pl.json new file mode 100644 index 00000000000..965b368e83b --- /dev/null +++ b/homeassistant/components/siren/translations/pl.json @@ -0,0 +1,3 @@ +{ + "title": "Syrena" +} \ No newline at end of file diff --git a/homeassistant/components/slack/translations/pl.json b/homeassistant/components/slack/translations/pl.json index fd1cb508e4a..33e8155a7f2 100644 --- a/homeassistant/components/slack/translations/pl.json +++ b/homeassistant/components/slack/translations/pl.json @@ -17,8 +17,12 @@ "username": "Nazwa u\u017cytkownika" }, "data_description": { - "api_key": "Token API Slack u\u017cywany do wysy\u0142ania wiadomo\u015bci Slack." - } + "api_key": "Token API Slack u\u017cywany do wysy\u0142ania wiadomo\u015bci Slack.", + "default_channel": "Kana\u0142, do kt\u00f3rego zostanie wys\u0142ana wiadomo\u015b\u0107, je\u015bli podczas wysy\u0142ania wiadomo\u015bci nie zosta\u0142 okre\u015blony \u017caden kana\u0142.", + "icon": "U\u017cyj jednej z emotikonek Slack jako ikony dla podanej nazwy u\u017cytkownika.", + "username": "Home Assistant wy\u015ble wiadomo\u015bci, u\u017cywaj\u0105c podanej nazwy u\u017cytkownika." + }, + "description": "Zapoznaj si\u0119 z dokumentacj\u0105 dotycz\u0105c\u0105 uzyskiwania klucza API Slack." } } } diff --git a/homeassistant/components/sleepiq/translations/nl.json b/homeassistant/components/sleepiq/translations/nl.json index 09029be118e..9b829caa025 100644 --- a/homeassistant/components/sleepiq/translations/nl.json +++ b/homeassistant/components/sleepiq/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "De SleepIQ-integratie moet uw account {username} opnieuw verifi\u00ebren.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/slimproto/translations/nl.json b/homeassistant/components/slimproto/translations/nl.json index 79aaec23123..703ac8614c4 100644 --- a/homeassistant/components/slimproto/translations/nl.json +++ b/homeassistant/components/slimproto/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." } } } \ No newline at end of file diff --git a/homeassistant/components/sma/translations/ko.json b/homeassistant/components/sma/translations/ko.json index 5e5aa899d96..bb627d5c622 100644 --- a/homeassistant/components/sma/translations/ko.json +++ b/homeassistant/components/sma/translations/ko.json @@ -6,17 +6,21 @@ }, "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "cannot_retrieve_device_info": "\uc131\uacf5\uc801\uc73c\ub85c \uc5f0\uacb0\ub418\uc5c8\uc9c0\ub9cc \uc7a5\uce58 \uc815\ubcf4\ub97c \uac80\uc0c9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" }, "step": { "user": { "data": { + "group": "\uadf8\ub8f9", "host": "\ud638\uc2a4\ud2b8", "password": "\ube44\ubc00\ubc88\ud638", "ssl": "SSL \uc778\uc99d\uc11c \uc0ac\uc6a9", "verify_ssl": "SSL \uc778\uc99d\uc11c \ud655\uc778" - } + }, + "description": "SMA \uc7a5\uce58 \uc815\ubcf4\ub97c \uc785\ub825\ud569\ub2c8\ub2e4.", + "title": "SMA Solar \uc124\uc815" } } } diff --git a/homeassistant/components/sma/translations/nl.json b/homeassistant/components/sma/translations/nl.json index d860518a18c..24298fe4bb3 100644 --- a/homeassistant/components/sma/translations/nl.json +++ b/homeassistant/components/sma/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang" + "already_in_progress": "De configuratie is momenteel al bezig" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -16,7 +16,7 @@ "group": "Groep", "host": "Host", "password": "Wachtwoord", - "ssl": "Gebruik een SSL-certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "description": "Voer uw SMA-apparaatgegevens in.", diff --git a/homeassistant/components/smappee/translations/nl.json b/homeassistant/components/smappee/translations/nl.json index ed9ef6e5e9f..8881aa1f047 100644 --- a/homeassistant/components/smappee/translations/nl.json +++ b/homeassistant/components/smappee/translations/nl.json @@ -3,11 +3,11 @@ "abort": { "already_configured_device": "Apparaat is al geconfigureerd", "already_configured_local_device": "Lokale apparaten zijn al geconfigureerd. Verwijder deze eerst voordat u een cloudapparaat configureert.", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "cannot_connect": "Kan geen verbinding maken", "invalid_mdns": "Niet-ondersteund apparaat voor de Smappee-integratie.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})" + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})" }, "flow_title": "{name}", "step": { diff --git a/homeassistant/components/smart_meter_texas/translations/nl.json b/homeassistant/components/smart_meter_texas/translations/nl.json index d25cf575b44..50b4c3f2fe6 100644 --- a/homeassistant/components/smart_meter_texas/translations/nl.json +++ b/homeassistant/components/smart_meter_texas/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, @@ -12,7 +12,7 @@ "user": { "data": { "password": "Wachtwoord", - "username": "Benutzername" + "username": "Gebruikersnaam" } } } diff --git a/homeassistant/components/smarttub/translations/ko.json b/homeassistant/components/smarttub/translations/ko.json index ff50dadc63e..6ba95c931bd 100644 --- a/homeassistant/components/smarttub/translations/ko.json +++ b/homeassistant/components/smarttub/translations/ko.json @@ -9,6 +9,7 @@ }, "step": { "reauth_confirm": { + "description": "SmartTub \ud1b5\ud569\uad6c\uc131\uc694\uc18c\ub294 \uacc4\uc815\uc744 \ub2e4\uc2dc \uc778\uc99d\ud574\uc57c \ud569\ub2c8\ub2e4.", "title": "\ud1b5\ud569 \uad6c\uc131\uc694\uc18c \uc7ac\uc778\uc99d\ud558\uae30" }, "user": { diff --git a/homeassistant/components/smarttub/translations/nl.json b/homeassistant/components/smarttub/translations/nl.json index 6d1e605d315..cfe5b601491 100644 --- a/homeassistant/components/smarttub/translations/nl.json +++ b/homeassistant/components/smarttub/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie" @@ -10,7 +10,7 @@ "step": { "reauth_confirm": { "description": "De SmartTub-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/sms/translations/nl.json b/homeassistant/components/sms/translations/nl.json index 7c2bcb0568c..7e05de163cf 100644 --- a/homeassistant/components/sms/translations/nl.json +++ b/homeassistant/components/sms/translations/nl.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "unknown": "Onverwachte fout" }, "step": { diff --git a/homeassistant/components/soma/translations/nl.json b/homeassistant/components/soma/translations/nl.json index 32db40348ba..33d9452950b 100644 --- a/homeassistant/components/soma/translations/nl.json +++ b/homeassistant/components/soma/translations/nl.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "already_setup": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "already_setup": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "connection_error": "Kan geen verbinding maken", "missing_configuration": "De Soma-component is niet geconfigureerd. Gelieve de documentatie te volgen.", "result_error": "SOMA Connect reageerde met een foutstatus." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "user": { diff --git a/homeassistant/components/somfy/translations/nl.json b/homeassistant/components/somfy/translations/nl.json index 11b3de442dd..efd07952467 100644 --- a/homeassistant/components/somfy/translations/nl.json +++ b/homeassistant/components/somfy/translations/nl.json @@ -1,13 +1,13 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/sonarr/translations/nl.json b/homeassistant/components/sonarr/translations/nl.json index d6782781090..a5f289a1428 100644 --- a/homeassistant/components/sonarr/translations/nl.json +++ b/homeassistant/components/sonarr/translations/nl.json @@ -2,18 +2,18 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie" }, "flow_title": "{name}", "step": { "reauth_confirm": { "description": "De Sonarr-integratie moet handmatig opnieuw worden geverifieerd met de Sonarr-API die wordt gehost op: {url}", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/songpal/translations/nl.json b/homeassistant/components/songpal/translations/nl.json index 0550b154ca4..aa4be57dda2 100644 --- a/homeassistant/components/songpal/translations/nl.json +++ b/homeassistant/components/songpal/translations/nl.json @@ -5,7 +5,7 @@ "not_songpal_device": "Geen Songpal-apparaat" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{name} ({host})", "step": { diff --git a/homeassistant/components/sonos/translations/ko.json b/homeassistant/components/sonos/translations/ko.json index f85f3c5cab4..95a2c1ccb5a 100644 --- a/homeassistant/components/sonos/translations/ko.json +++ b/homeassistant/components/sonos/translations/ko.json @@ -2,6 +2,7 @@ "config": { "abort": { "no_devices_found": "\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \uae30\uae30\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4", + "not_sonos_device": "\ubc1c\uacac\ub41c \uc7a5\uce58\uac00 Sonos \uc7a5\uce58\uac00 \uc544\ub2d9\ub2c8\ub2e4", "single_instance_allowed": "\uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ud558\ub098\uc758 \uc778\uc2a4\ud134\uc2a4\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." }, "step": { diff --git a/homeassistant/components/sonos/translations/nl.json b/homeassistant/components/sonos/translations/nl.json index 0147c5c5382..615654c98ad 100644 --- a/homeassistant/components/sonos/translations/nl.json +++ b/homeassistant/components/sonos/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", "not_sonos_device": "Gevonden apparaat is geen Sonos-apparaat", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/speedtestdotnet/translations/nl.json b/homeassistant/components/speedtestdotnet/translations/nl.json index 8cd6cb960c8..b5af5dbc78d 100644 --- a/homeassistant/components/speedtestdotnet/translations/nl.json +++ b/homeassistant/components/speedtestdotnet/translations/nl.json @@ -5,7 +5,7 @@ }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/spider/translations/nl.json b/homeassistant/components/spider/translations/nl.json index 373d203aed7..7540b821163 100644 --- a/homeassistant/components/spider/translations/nl.json +++ b/homeassistant/components/spider/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_auth": "Ongeldige authenticatie", diff --git a/homeassistant/components/spotify/translations/nl.json b/homeassistant/components/spotify/translations/nl.json index 0d1e63fde5d..577e70bb0d1 100644 --- a/homeassistant/components/spotify/translations/nl.json +++ b/homeassistant/components/spotify/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", "missing_configuration": "De Spotify integratie is niet geconfigureerd. Gelieve de documentatie te volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout [check the help section] ({docs_url})", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", "reauth_account_mismatch": "Het Spotify account waarmee er is geverifieerd, komt niet overeen met het account dat opnieuw moet worden geverifieerd." }, "create_entry": { @@ -15,7 +15,7 @@ }, "reauth_confirm": { "description": "De Spotify integratie moet opnieuw worden geverifieerd met Spotify voor account: {account}", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } }, diff --git a/homeassistant/components/squeezebox/translations/nl.json b/homeassistant/components/squeezebox/translations/nl.json index fc05f6d4807..ad18c117bb1 100644 --- a/homeassistant/components/squeezebox/translations/nl.json +++ b/homeassistant/components/squeezebox/translations/nl.json @@ -5,7 +5,7 @@ "no_server_found": "Geen LMS server gevonden." }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "no_server_found": "Kan server niet automatisch vinden.", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/srp_energy/translations/nl.json b/homeassistant/components/srp_energy/translations/nl.json index ce4ac90c223..b52bda50d98 100644 --- a/homeassistant/components/srp_energy/translations/nl.json +++ b/homeassistant/components/srp_energy/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json index d9281038e76..9255b0e06ff 100644 --- a/homeassistant/components/steam_online/translations/nl.json +++ b/homeassistant/components/steam_online/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -13,7 +13,7 @@ "step": { "reauth_confirm": { "description": "De Steam integratie moet handmatig opnieuw geauthenticeerd worden\n\nU kunt uw sleutel hier vinden: {api_key_url}", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/steamist/translations/nl.json b/homeassistant/components/steamist/translations/nl.json index 345ec01dce3..62c492f5e02 100644 --- a/homeassistant/components/steamist/translations/nl.json +++ b/homeassistant/components/steamist/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "no_devices_found": "Geen apparaten gevonden op het netwerk", "not_steamist_device": "Geen steamist-apparaat" diff --git a/homeassistant/components/sun/translations/nl.json b/homeassistant/components/sun/translations/nl.json index 8c284e4e43d..57d17476331 100644 --- a/homeassistant/components/sun/translations/nl.json +++ b/homeassistant/components/sun/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/switcher_kis/translations/nl.json b/homeassistant/components/switcher_kis/translations/nl.json index 0671f0b3674..6fc4a03e824 100644 --- a/homeassistant/components/switcher_kis/translations/nl.json +++ b/homeassistant/components/switcher_kis/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/synology_dsm/translations/nl.json b/homeassistant/components/synology_dsm/translations/nl.json index c685137738a..c43f41b4277 100644 --- a/homeassistant/components/synology_dsm/translations/nl.json +++ b/homeassistant/components/synology_dsm/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "reconfigure_successful": "Herconfiguratie was succesvol" }, "error": { @@ -10,7 +10,7 @@ "invalid_auth": "Ongeldige authenticatie", "missing_data": "Ontbrekende gegevens: probeer het later opnieuw of een andere configuratie", "otp_failed": "Tweestapsverificatie is mislukt, probeer het opnieuw met een nieuwe toegangscode", - "unknown": "Onbekende fout: controleer de logs voor meer informatie" + "unknown": "Onverwachte fout" }, "flow_title": "{name} ({host})", "step": { @@ -24,9 +24,9 @@ "data": { "password": "Wachtwoord", "port": "Poort", - "ssl": "Gebruik een SSL-certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", - "verify_ssl": "Controleer het SSL-certificaat" + "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "description": "Wil je {name} ({host}) instellen?" }, @@ -42,9 +42,9 @@ "host": "Host", "password": "Wachtwoord", "port": "Poort", - "ssl": "Gebruik een SSL-certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", - "verify_ssl": "Controleer het SSL-certificaat" + "verify_ssl": "SSL-certificaat verifi\u00ebren" } } } diff --git a/homeassistant/components/system_bridge/translations/ko.json b/homeassistant/components/system_bridge/translations/ko.json new file mode 100644 index 00000000000..c40a7a55814 --- /dev/null +++ b/homeassistant/components/system_bridge/translations/ko.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "error": { + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4", + "unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4" + }, + "flow_title": "{name}", + "step": { + "authenticate": { + "data": { + "api_key": "API \ud0a4" + }, + "description": "{name} \uc5d0 \ub300\ud55c \uad6c\uc131\uc5d0\uc11c \uc124\uc815\ud55c API \ud0a4\ub97c \uc785\ub825\ud558\uc2ed\uc2dc\uc624." + }, + "user": { + "data": { + "api_key": "API \ud0a4", + "host": "\ud638\uc2a4\ud2b8", + "port": "\ud3ec\ud2b8" + }, + "description": "\uc5f0\uacb0 \uc138\ubd80 \uc815\ubcf4\ub97c \uc785\ub825\ud558\uc138\uc694." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/system_bridge/translations/nl.json b/homeassistant/components/system_bridge/translations/nl.json index c419123e50d..c9d1c45d6a3 100644 --- a/homeassistant/components/system_bridge/translations/nl.json +++ b/homeassistant/components/system_bridge/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/tailscale/translations/nl.json b/homeassistant/components/tailscale/translations/nl.json index 1a59a3ca029..5e05b0ebcb6 100644 --- a/homeassistant/components/tailscale/translations/nl.json +++ b/homeassistant/components/tailscale/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/tankerkoenig/translations/nl.json b/homeassistant/components/tankerkoenig/translations/nl.json index 96de058ad74..66d442a71f6 100644 --- a/homeassistant/components/tankerkoenig/translations/nl.json +++ b/homeassistant/components/tankerkoenig/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "invalid_auth": "Ongeldige authenticatie", diff --git a/homeassistant/components/tasmota/translations/nl.json b/homeassistant/components/tasmota/translations/nl.json index 474ed82b9d6..a116e840977 100644 --- a/homeassistant/components/tasmota/translations/nl.json +++ b/homeassistant/components/tasmota/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Is al geconfigureerd. Er is maar een configuratie mogelijk" + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_discovery_topic": "Invalid discovery topic prefix." diff --git a/homeassistant/components/tautulli/translations/nl.json b/homeassistant/components/tautulli/translations/nl.json index 9f10f666030..5e791e33a8f 100644 --- a/homeassistant/components/tautulli/translations/nl.json +++ b/homeassistant/components/tautulli/translations/nl.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "reauth_successful": "Herauthenticatie was succesvol", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "reauth_successful": "Herauthenticatie geslaagd", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/tellduslive/translations/nl.json b/homeassistant/components/tellduslive/translations/nl.json index d0c03a341f0..0f718304059 100644 --- a/homeassistant/components/tellduslive/translations/nl.json +++ b/homeassistant/components/tellduslive/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "unknown": "Onverwachte fout", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." }, diff --git a/homeassistant/components/tile/translations/nl.json b/homeassistant/components/tile/translations/nl.json index d2c66b4fe65..8a6270009a8 100644 --- a/homeassistant/components/tile/translations/nl.json +++ b/homeassistant/components/tile/translations/nl.json @@ -1,8 +1,8 @@ { "config": { "abort": { - "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "already_configured": "Account is al geconfigureerd", + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie" diff --git a/homeassistant/components/tolo/translations/nl.json b/homeassistant/components/tolo/translations/nl.json index dcc6f1936d0..31d417dd9e7 100644 --- a/homeassistant/components/tolo/translations/nl.json +++ b/homeassistant/components/tolo/translations/nl.json @@ -9,7 +9,7 @@ "flow_title": "{name}", "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "user": { "data": { diff --git a/homeassistant/components/tomorrowio/translations/sensor.ko.json b/homeassistant/components/tomorrowio/translations/sensor.ko.json new file mode 100644 index 00000000000..de8aaf71402 --- /dev/null +++ b/homeassistant/components/tomorrowio/translations/sensor.ko.json @@ -0,0 +1,7 @@ +{ + "state": { + "tomorrowio__precipitation_type": { + "freezing_rain": "\uc5b4\ub294 \ube44" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/toon/translations/nl.json b/homeassistant/components/toon/translations/nl.json index 021c6276b1a..176fa50e932 100644 --- a/homeassistant/components/toon/translations/nl.json +++ b/homeassistant/components/toon/translations/nl.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "De geselecteerde overeenkomst is al geconfigureerd.", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", "no_agreements": "Dit account heeft geen Toon schermen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout [check the help section] ( {docs_url} )", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." }, "step": { diff --git a/homeassistant/components/totalconnect/translations/nl.json b/homeassistant/components/totalconnect/translations/nl.json index 186e2f60d50..e80030ec06b 100644 --- a/homeassistant/components/totalconnect/translations/nl.json +++ b/homeassistant/components/totalconnect/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Account is al geconfigureerd", "no_locations": "Er zijn geen locaties beschikbaar voor deze gebruiker, controleer de instellingen van TotalConnect", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -19,7 +19,7 @@ }, "reauth_confirm": { "description": "Total Connect moet uw account opnieuw verifi\u00ebren", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/tplink/translations/nl.json b/homeassistant/components/tplink/translations/nl.json index bceae9f3fff..67f79e90223 100644 --- a/homeassistant/components/tplink/translations/nl.json +++ b/homeassistant/components/tplink/translations/nl.json @@ -5,7 +5,7 @@ "no_devices_found": "Geen apparaten gevonden op het netwerk" }, "error": { - "cannot_connect": "Kon geen verbinding maken" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{name} {model} ({host})", "step": { diff --git a/homeassistant/components/traccar/translations/nl.json b/homeassistant/components/traccar/translations/nl.json index 45a70a42b43..098b2c9376f 100644 --- a/homeassistant/components/traccar/translations/nl.json +++ b/homeassistant/components/traccar/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Voor het verzenden van gebeurtenissen naar Home Assistant, moet u de webhook-functie in Traccar instellen.\n\nGebruik de volgende URL: ' {webhook_url} '\n\nZie [de documentatie] ({docs_url}) voor meer informatie." diff --git a/homeassistant/components/tractive/translations/nl.json b/homeassistant/components/tractive/translations/nl.json index b0e1f17cdc3..910131316da 100644 --- a/homeassistant/components/tractive/translations/nl.json +++ b/homeassistant/components/tractive/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "reauth_failed_existing": "Kon het configuratie-item niet bijwerken, verwijder de integratie en stel deze opnieuw in.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", diff --git a/homeassistant/components/tradfri/translations/nl.json b/homeassistant/components/tradfri/translations/nl.json index 752270e78c6..02bffc6b60d 100644 --- a/homeassistant/components/tradfri/translations/nl.json +++ b/homeassistant/components/tradfri/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang" + "already_in_progress": "De configuratie is momenteel al bezig" }, "error": { "cannot_authenticate": "Kan niet authenticeren, is Gateway gekoppeld met een andere server zoals bijv. Homekit?", diff --git a/homeassistant/components/trafikverket_ferry/translations/nl.json b/homeassistant/components/trafikverket_ferry/translations/nl.json index f84232839c8..edaeb14fd48 100644 --- a/homeassistant/components/trafikverket_ferry/translations/nl.json +++ b/homeassistant/components/trafikverket_ferry/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/trafikverket_train/translations/ko.json b/homeassistant/components/trafikverket_train/translations/ko.json new file mode 100644 index 00000000000..82cb120d44d --- /dev/null +++ b/homeassistant/components/trafikverket_train/translations/ko.json @@ -0,0 +1,11 @@ +{ + "config": { + "step": { + "reauth_confirm": { + "data": { + "api_key": "API \ud0a4" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/trafikverket_train/translations/nl.json b/homeassistant/components/trafikverket_train/translations/nl.json index d076f8b2961..3143a28cf16 100644 --- a/homeassistant/components/trafikverket_train/translations/nl.json +++ b/homeassistant/components/trafikverket_train/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/tuya/translations/select.nl.json b/homeassistant/components/tuya/translations/select.nl.json index e645694ae55..521a9c908ba 100644 --- a/homeassistant/components/tuya/translations/select.nl.json +++ b/homeassistant/components/tuya/translations/select.nl.json @@ -77,7 +77,7 @@ }, "tuya__light_mode": { "none": "Uit", - "pos": "Plaats van schakelaar aangeven", + "pos": "Schakelpositie aangeven", "relay": "Aan/uit-toestand aangeven" }, "tuya__motion_sensitivity": { @@ -90,8 +90,8 @@ "2": "Continu opnemen" }, "tuya__relay_status": { - "last": "Onthoud laatste staat", - "memory": "Onthoud laatste staat", + "last": "Laatste status onthouden", + "memory": "Laatste status onthouden", "off": "Uit", "on": "Aan", "power_off": "Uit", @@ -116,7 +116,7 @@ "mop": "Dweil", "part": "Deel", "partial_bow": "Boog gedeeltelijk", - "pick_zone": "Kies zone", + "pick_zone": "Zone kiezen", "point": "Punt", "pose": "Houding", "random": "Willekeurig", diff --git a/homeassistant/components/twilio/translations/nl.json b/homeassistant/components/twilio/translations/nl.json index cf180123c98..a63d0301dcd 100644 --- a/homeassistant/components/twilio/translations/nl.json +++ b/homeassistant/components/twilio/translations/nl.json @@ -3,14 +3,14 @@ "abort": { "cloud_not_connected": "Niet verbonden met Home Assistant Cloud.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", - "webhook_not_internet_accessible": "Uw Home Assistant-instantie moet toegankelijk zijn via internet om webhook-berichten te ontvangen." + "webhook_not_internet_accessible": "Je Home Assistant-instantie moet toegankelijk zijn vanaf internet om webhook-berichten te ontvangen." }, "create_entry": { "default": "Om evenementen naar de Home Assistant te verzenden, moet u [Webhooks with Twilio] ( {twilio_url} ) instellen. \n\n Vul de volgende info in: \n\n - URL: ` {webhook_url} ` \n - Methode: POST \n - Inhoudstype: application / x-www-form-urlencoded \n\n Zie [de documentatie] ( {docs_url} ) voor informatie over het configureren van automatiseringen om binnenkomende gegevens te verwerken." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?", + "description": "Wil je beginnen met instellen?", "title": "Stel de Twilio Webhook in" } } diff --git a/homeassistant/components/ukraine_alarm/translations/nl.json b/homeassistant/components/ukraine_alarm/translations/nl.json index 6f09f794ac4..97fd17e23e0 100644 --- a/homeassistant/components/ukraine_alarm/translations/nl.json +++ b/homeassistant/components/ukraine_alarm/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd.", + "already_configured": "Locatie is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "max_regions": "Er kunnen maximaal 5 regio's worden geconfigureerd", "rate_limit": "Te veel verzoeken", @@ -11,13 +11,13 @@ "step": { "community": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "Regio" }, "description": "Als u niet alleen de staat en het district wilt volgen, kiest u de specifieke gemeenschap ervan" }, "district": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "Regio" }, "description": "Als u niet alleen de staat wilt controleren, kies dan het specifieke district" }, diff --git a/homeassistant/components/unifi/translations/nl.json b/homeassistant/components/unifi/translations/nl.json index 637dbd480f3..6e9b0ec7ede 100644 --- a/homeassistant/components/unifi/translations/nl.json +++ b/homeassistant/components/unifi/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Unifi Network site is al geconfigureerd", "configuration_updated": "Configuratie bijgewerkt", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "faulty_credentials": "Ongeldige authenticatie", diff --git a/homeassistant/components/unifiprotect/translations/ko.json b/homeassistant/components/unifiprotect/translations/ko.json index 36be85fd61a..916dcc8a7e9 100644 --- a/homeassistant/components/unifiprotect/translations/ko.json +++ b/homeassistant/components/unifiprotect/translations/ko.json @@ -1,8 +1,16 @@ { "config": { + "abort": { + "discovery_started": "\uc7a5\uce58\uac80\uc0c9 \uc2dc\uc791" + }, "step": { "discovery_confirm": { - "description": "{name} ( {ip_address} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? " + "data": { + "password": "\ube44\ubc00\ubc88\ud638", + "username": "\uc0ac\uc6a9\uc790 \uc774\ub984" + }, + "description": "{name} ( {ip_address} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? ", + "title": "UniFi Protect \ubc1c\uacac" } } } diff --git a/homeassistant/components/upb/translations/nl.json b/homeassistant/components/upb/translations/nl.json index 56ba7eae754..b3284eed134 100644 --- a/homeassistant/components/upb/translations/nl.json +++ b/homeassistant/components/upb/translations/nl.json @@ -4,7 +4,7 @@ "already_configured": "Apparaat is al geconfigureerd" }, "error": { - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_upb_file": "Ontbrekend of ongeldig UPB UPStart-exportbestand, controleer de naam en het pad van het bestand.", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/uptime/translations/nl.json b/homeassistant/components/uptime/translations/nl.json index 786fec37f6b..b4ed0a1db36 100644 --- a/homeassistant/components/uptime/translations/nl.json +++ b/homeassistant/components/uptime/translations/nl.json @@ -1,11 +1,11 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "user": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } }, diff --git a/homeassistant/components/uptimerobot/translations/nl.json b/homeassistant/components/uptimerobot/translations/nl.json index d3ee1f7515a..61a2e8137e5 100644 --- a/homeassistant/components/uptimerobot/translations/nl.json +++ b/homeassistant/components/uptimerobot/translations/nl.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Apparaat is al geconfigureerd", "reauth_failed_existing": "Kon de config entry niet updaten, gelieve de integratie te verwijderen en het opnieuw op te zetten.", - "reauth_successful": "Herauthenticatie was succesvol", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { @@ -19,7 +19,7 @@ "api_key": "API-sleutel" }, "description": "U moet een nieuwe 'hoofd'-API-sleutel van UptimeRobot opgeven", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/vallox/translations/nl.json b/homeassistant/components/vallox/translations/nl.json index b8b5b6bdc57..9b064500704 100644 --- a/homeassistant/components/vallox/translations/nl.json +++ b/homeassistant/components/vallox/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Service is al geconfigureerd", - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "invalid_host": "Ongeldige hostnaam of IP-adres", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/venstar/translations/nl.json b/homeassistant/components/venstar/translations/nl.json index 3c8a61faf20..b39a0f356e1 100644 --- a/homeassistant/components/venstar/translations/nl.json +++ b/homeassistant/components/venstar/translations/nl.json @@ -12,8 +12,8 @@ "data": { "host": "Host", "password": "Wachtwoord", - "pin": "PIN-code", - "ssl": "Gebruik een SSL-certificaat", + "pin": "Pincode", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam" }, "title": "Maak verbinding met de Venstar-thermostaat" diff --git a/homeassistant/components/verisure/translations/nl.json b/homeassistant/components/verisure/translations/nl.json index d0519e584fd..1a23da57319 100644 --- a/homeassistant/components/verisure/translations/nl.json +++ b/homeassistant/components/verisure/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", diff --git a/homeassistant/components/vesync/translations/nl.json b/homeassistant/components/vesync/translations/nl.json index ab330237afd..36e661eca21 100644 --- a/homeassistant/components/vesync/translations/nl.json +++ b/homeassistant/components/vesync/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slecht \u00e9\u00e9n configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { "invalid_auth": "Ongeldige authenticatie" diff --git a/homeassistant/components/vicare/translations/nl.json b/homeassistant/components/vicare/translations/nl.json index 1e5fc68f9e3..a3efaf0661e 100644 --- a/homeassistant/components/vicare/translations/nl.json +++ b/homeassistant/components/vicare/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/vizio/translations/nl.json b/homeassistant/components/vizio/translations/nl.json index f17e0470799..871aa4e3e69 100644 --- a/homeassistant/components/vizio/translations/nl.json +++ b/homeassistant/components/vizio/translations/nl.json @@ -1,19 +1,19 @@ { "config": { "abort": { - "already_configured_device": "Dit apparaat is al geconfigureerd", + "already_configured_device": "Apparaat is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "updated_entry": "Dit item is al ingesteld, maar de naam en/of opties die zijn gedefinieerd in de configuratie komen niet overeen met de eerder ge\u00efmporteerde configuratie, dus het configuratie-item is dienovereenkomstig bijgewerkt." }, "error": { - "cannot_connect": "Verbinding mislukt", + "cannot_connect": "Kan geen verbinding maken", "complete_pairing_failed": "Kan het koppelen niet voltooien. Zorg ervoor dat de door u opgegeven pincode correct is en dat de tv nog steeds van stroom wordt voorzien en is verbonden met het netwerk voordat u opnieuw verzendt.", "existing_config_entry_found": "Een bestaande VIZIO SmartCast-apparaat config entry met hetzelfde serienummer is reeds geconfigureerd. U moet de bestaande entry verwijderen om deze te kunnen configureren." }, "step": { "pair_tv": { "data": { - "pin": "PIN-code" + "pin": "Pincode" }, "description": "Uw TV zou een code moeten weergeven. Voer die code in het formulier in en ga dan door naar de volgende stap om de koppeling te voltooien.", "title": "Voltooi het koppelingsproces" diff --git a/homeassistant/components/vlc_telnet/translations/nl.json b/homeassistant/components/vlc_telnet/translations/nl.json index 538421263c9..771ce98964d 100644 --- a/homeassistant/components/vlc_telnet/translations/nl.json +++ b/homeassistant/components/vlc_telnet/translations/nl.json @@ -1,14 +1,14 @@ { "config": { "abort": { - "already_configured": "Deze service is al geconfigureerd", + "already_configured": "Service is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", - "reauth_successful": "Het opnieuw verifi\u00ebren is geslaagd", + "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, "error": { - "cannot_connect": "Verbinding tot stand brengen is mislukt", + "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/vulcan/translations/ko.json b/homeassistant/components/vulcan/translations/ko.json index c98f79dd84b..f65f8ada294 100644 --- a/homeassistant/components/vulcan/translations/ko.json +++ b/homeassistant/components/vulcan/translations/ko.json @@ -1,6 +1,9 @@ { "config": { "step": { + "auth": { + "description": "\ubaa8\ubc14\uc77c \uc571 \ub4f1\ub85d \ud398\uc774\uc9c0\ub97c \uc0ac\uc6a9\ud558\uc5ec Vulcan \uacc4\uc815\uc5d0 \ub85c\uadf8\uc778\ud569\ub2c8\ub2e4." + }, "reauth_confirm": { "data": { "token": "\ud1a0\ud070" diff --git a/homeassistant/components/wallbox/translations/nl.json b/homeassistant/components/wallbox/translations/nl.json index 5cde7830b85..bedf5851a36 100644 --- a/homeassistant/components/wallbox/translations/nl.json +++ b/homeassistant/components/wallbox/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/water_heater/translations/nl.json b/homeassistant/components/water_heater/translations/nl.json index 4a2d1357188..05c8eaffc0e 100644 --- a/homeassistant/components/water_heater/translations/nl.json +++ b/homeassistant/components/water_heater/translations/nl.json @@ -11,9 +11,9 @@ "electric": "Elektriciteit", "gas": "Gas", "heat_pump": "Warmtepomp", - "high_demand": "Hoge vraag", + "high_demand": "Grote vraag", "off": "Uit", - "performance": "Prestaties" + "performance": "Prestatie" } } } \ No newline at end of file diff --git a/homeassistant/components/watttime/translations/nl.json b/homeassistant/components/watttime/translations/nl.json index 72758f4a0d7..47e2fb9c600 100644 --- a/homeassistant/components/watttime/translations/nl.json +++ b/homeassistant/components/watttime/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "invalid_auth": "Ongeldige authenticatie", @@ -28,7 +28,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in:", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "user": { "data": { diff --git a/homeassistant/components/waze_travel_time/translations/ko.json b/homeassistant/components/waze_travel_time/translations/ko.json index 3596754ca04..5b2dbd7ac46 100644 --- a/homeassistant/components/waze_travel_time/translations/ko.json +++ b/homeassistant/components/waze_travel_time/translations/ko.json @@ -16,5 +16,23 @@ "description": "\ucd9c\ubc1c\uc9c0 \ubc0f \ub3c4\ucc29\uc9c0\uc758 \uacbd\uc6b0 \uc704\uce58\uc758 \uc8fc\uc18c \ub610\ub294 GPS \uc88c\ud45c\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694. (GPS \uc88c\ud45c\ub294 \uc27c\ud45c\ub85c \uad6c\ubd84\ud574\uc57c \ud569\ub2c8\ub2e4) \ub610\ub294 \uc774\ub7ec\ud55c \uc815\ubcf4\ub97c \ud574\ub2f9 \uc0c1\ud0dc\ub85c \uc81c\uacf5\ud558\ub294 \uad6c\uc131\uc694\uc18c ID\ub098 \uc704\ub3c4 \ubc0f \uacbd\ub3c4 \uc18d\uc131\uc744 \uac00\uc9c4 \uad6c\uc131\uc694\uc18c ID \ub610\ub294 \uc9c0\uc5ed \uc774\ub984\uc744 \uc785\ub825\ud560 \uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4." } } - } + }, + "options": { + "step": { + "init": { + "data": { + "avoid_ferries": "\ud398\ub9ac\ub97c \ud53c\ud558\uaca0\uc5b4\uc694?", + "avoid_subscription_roads": "\uace0\uc18d\ub3c4\ub85c/\uc720\ub8cc\ub3c4\ub85c\ub97c \ud53c\ud558\uaca0\uc5b4\uc694?", + "avoid_toll_roads": "\uc720\ub8cc\ub3c4\ub85c\ub97c \ud53c\ud558\uaca0\uc5b4\uc694?", + "excl_filter": "\uc120\ud0dd\ud55c \uacbd\ub85c\uc5d0\uc11c \ud558\uc704\ubb38\uc790\uc5f4\uc744 \uc81c\uc678\ud569\ub2c8\ub2e4.", + "incl_filter": "\uc120\ud0dd\ud55c \uacbd\ub85c\uc5d0\uc11c \ud558\uc704\ubb38\uc790\uc5f4\uc744 \ud3ec\ud568\ud569\ub2c8\ub2e4.", + "realtime": "\uc2e4\uc2dc\uac04 \uc774\ub3d9 \uc2dc\uac04?", + "units": "\ub2e8\uc704", + "vehicle_type": "\ucc28\ub7c9 \uc720\ud615" + }, + "description": "'\ud558\uc704 \ubb38\uc790\uc5f4'\uc785\ub825\uc744 \uc0ac\uc6a9\ud558\uba74 \ud1b5\ud569\uad6c\uc131\uc694\uc18c\uac00 \ud2b9\uc815 \uacbd\ub85c\ub97c \uc0ac\uc6a9\ud558\uac70\ub098 \ud2b9\uc815 \uacbd\ub85c\ub97c \ud53c\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4." + } + } + }, + "title": "Waze Travel Time" } \ No newline at end of file diff --git a/homeassistant/components/waze_travel_time/translations/nl.json b/homeassistant/components/waze_travel_time/translations/nl.json index 223d69625d4..249b42f3063 100644 --- a/homeassistant/components/waze_travel_time/translations/nl.json +++ b/homeassistant/components/waze_travel_time/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Locatie is al geconfigureerd." + "already_configured": "Locatie is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/weather/translations/nl.json b/homeassistant/components/weather/translations/nl.json index bab37782936..2fff10215ab 100644 --- a/homeassistant/components/weather/translations/nl.json +++ b/homeassistant/components/weather/translations/nl.json @@ -9,7 +9,7 @@ "lightning": "Bliksem", "lightning-rainy": "Bliksem, regenachtig", "partlycloudy": "Gedeeltelijk bewolkt", - "pouring": "Regen", + "pouring": "Gieten", "rainy": "Regenachtig", "snowy": "Sneeuwachtig", "snowy-rainy": "Sneeuw-, regenachtig", diff --git a/homeassistant/components/webostv/translations/nl.json b/homeassistant/components/webostv/translations/nl.json index 2a9fb59e1c1..f914287ce16 100644 --- a/homeassistant/components/webostv/translations/nl.json +++ b/homeassistant/components/webostv/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "error_pairing": "Verbonden met LG webOS TV maar niet gekoppeld" }, "error": { diff --git a/homeassistant/components/wemo/translations/nl.json b/homeassistant/components/wemo/translations/nl.json index 7fde5a7ef42..78b3b5f5a05 100644 --- a/homeassistant/components/wemo/translations/nl.json +++ b/homeassistant/components/wemo/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { diff --git a/homeassistant/components/withings/translations/nl.json b/homeassistant/components/withings/translations/nl.json index a7400e3a693..04da8b7eb0b 100644 --- a/homeassistant/components/withings/translations/nl.json +++ b/homeassistant/components/withings/translations/nl.json @@ -2,9 +2,9 @@ "config": { "abort": { "already_configured": "Configuratie bijgewerkt voor profiel.", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})" + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})" }, "create_entry": { "default": "Succesvol geverifieerd met Withings voor het geselecteerde profiel." @@ -26,7 +26,7 @@ }, "reauth": { "description": "Het {profile} \" moet opnieuw worden geverifieerd om Withings-gegevens te blijven ontvangen.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } } diff --git a/homeassistant/components/wiz/translations/ko.json b/homeassistant/components/wiz/translations/ko.json new file mode 100644 index 00000000000..9c08b2d78b0 --- /dev/null +++ b/homeassistant/components/wiz/translations/ko.json @@ -0,0 +1,9 @@ +{ + "config": { + "step": { + "user": { + "description": "IP \uc8fc\uc18c\ub97c \ube44\uc6cc\ub450\uba74 \uc7a5\uce58\ub97c \uc790\ub3d9\uc73c\ub85c \ucc3e\uc2b5\ub2c8\ub2e4." + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/xbox/translations/nl.json b/homeassistant/components/xbox/translations/nl.json index 1e567e954d2..9d68863cc27 100644 --- a/homeassistant/components/xbox/translations/nl.json +++ b/homeassistant/components/xbox/translations/nl.json @@ -1,12 +1,12 @@ { "config": { "abort": { - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { diff --git a/homeassistant/components/xiaomi_aqara/translations/nl.json b/homeassistant/components/xiaomi_aqara/translations/nl.json index a549d01313b..bfdd88a30de 100644 --- a/homeassistant/components/xiaomi_aqara/translations/nl.json +++ b/homeassistant/components/xiaomi_aqara/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "not_xiaomi_aqara": "Geen Xiaomi Aqara Gateway, ontdekt apparaat kwam niet overeen met bekende gateways" }, "error": { diff --git a/homeassistant/components/xiaomi_miio/translations/nl.json b/homeassistant/components/xiaomi_miio/translations/nl.json index 219f7da0b4f..abb63ec15c5 100644 --- a/homeassistant/components/xiaomi_miio/translations/nl.json +++ b/homeassistant/components/xiaomi_miio/translations/nl.json @@ -2,10 +2,10 @@ "config": { "abort": { "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "incomplete_info": "Onvolledige informatie voor het instellen van het apparaat, geen host of token opgegeven.", "not_xiaomi_miio": "Apparaat wordt (nog) niet ondersteund door Xiaomi Miio.", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -40,7 +40,7 @@ }, "reauth_confirm": { "description": "De Xiaomi Miio-integratie moet uw account opnieuw verifi\u00ebren om de tokens bij te werken of ontbrekende cloudreferenties toe te voegen.", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" }, "select": { "data": { diff --git a/homeassistant/components/yale_smart_alarm/translations/nl.json b/homeassistant/components/yale_smart_alarm/translations/nl.json index 8d8697bbc59..15956572847 100644 --- a/homeassistant/components/yale_smart_alarm/translations/nl.json +++ b/homeassistant/components/yale_smart_alarm/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "reauth_successful": "Herauthenticatie was succesvol" + "reauth_successful": "Herauthenticatie geslaagd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/yamaha_musiccast/translations/nl.json b/homeassistant/components/yamaha_musiccast/translations/nl.json index 8cb8265a1f0..e1e31149c06 100644 --- a/homeassistant/components/yamaha_musiccast/translations/nl.json +++ b/homeassistant/components/yamaha_musiccast/translations/nl.json @@ -10,7 +10,7 @@ "flow_title": "MusicCast: {name}", "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" }, "user": { "data": { diff --git a/homeassistant/components/yeelight/translations/ko.json b/homeassistant/components/yeelight/translations/ko.json index b8a4385bfdf..d12c41b423e 100644 --- a/homeassistant/components/yeelight/translations/ko.json +++ b/homeassistant/components/yeelight/translations/ko.json @@ -7,7 +7,11 @@ "error": { "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" }, + "flow_title": "{model} {id} ({host})", "step": { + "discovery_confirm": { + "description": "{model} ( {host} )\uc744(\ub97c) \uc124\uc815\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?" + }, "pick_device": { "data": { "device": "\uae30\uae30" diff --git a/homeassistant/components/yeelight/translations/nl.json b/homeassistant/components/yeelight/translations/nl.json index 7767c56e7fb..540191e9222 100644 --- a/homeassistant/components/yeelight/translations/nl.json +++ b/homeassistant/components/yeelight/translations/nl.json @@ -5,7 +5,7 @@ "no_devices_found": "Geen apparaten gevonden op het netwerk" }, "error": { - "cannot_connect": "Kon niet verbinden" + "cannot_connect": "Kan geen verbinding maken" }, "flow_title": "{model} {id} ({host})", "step": { diff --git a/homeassistant/components/yolink/translations/nl.json b/homeassistant/components/yolink/translations/nl.json index ae9739b3bd6..401738dc381 100644 --- a/homeassistant/components/yolink/translations/nl.json +++ b/homeassistant/components/yolink/translations/nl.json @@ -2,15 +2,15 @@ "config": { "abort": { "already_configured": "Account is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", - "authorize_url_timeout": "Time-out tijdens genereren autorisatie url.", - "missing_configuration": "De component is niet geconfigureerd. Gelieve de documentatie volgen.", - "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})", - "oauth_error": "Ongeldige token data ontvangen.", - "reauth_successful": "Herauthenticatie was succesvol" + "already_in_progress": "De configuratie is momenteel al bezig", + "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", + "missing_configuration": "Integratie niet geconfigureerd. Raadpleeg de documentatie.", + "no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [raadpleeg de documentatie]({docs_url})", + "oauth_error": "Ongeldige tokengegevens ontvangen.", + "reauth_successful": "Herauthenticatie geslaagd" }, "create_entry": { - "default": "Succesvol geauthenticeerd" + "default": "Authenticatie geslaagd" }, "step": { "pick_implementation": { @@ -18,7 +18,7 @@ }, "reauth_confirm": { "description": "De yolink-integratie moet opnieuw inloggen bij uw account", - "title": "Verifieer de integratie opnieuw" + "title": "Integratie herauthentiseren" } } } diff --git a/homeassistant/components/yolink/translations/pl.json b/homeassistant/components/yolink/translations/pl.json new file mode 100644 index 00000000000..5cddd2a4944 --- /dev/null +++ b/homeassistant/components/yolink/translations/pl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "Konto jest ju\u017c skonfigurowane", + "already_in_progress": "Konfiguracja jest ju\u017c w toku", + "authorize_url_timeout": "Przekroczono limit czasu generowania URL autoryzacji", + "missing_configuration": "Komponent nie jest skonfigurowany. Post\u0119puj zgodnie z dokumentacj\u0105.", + "no_url_available": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})", + "oauth_error": "Brak dost\u0119pnego adresu URL. Aby uzyska\u0107 informacje na temat tego b\u0142\u0119du, [sprawd\u017a sekcj\u0119 pomocy] ({docs_url})", + "reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119" + }, + "create_entry": { + "default": "Pomy\u015blnie uwierzytelniono" + }, + "step": { + "pick_implementation": { + "title": "Wybierz metod\u0119 uwierzytelniania" + }, + "reauth_confirm": { + "description": "Integracja yolink wymaga ponownego uwierzytelnienia Twojego konta.", + "title": "Ponownie uwierzytelnij integracj\u0119" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/zerproc/translations/nl.json b/homeassistant/components/zerproc/translations/nl.json index 0671f0b3674..6fc4a03e824 100644 --- a/homeassistant/components/zerproc/translations/nl.json +++ b/homeassistant/components/zerproc/translations/nl.json @@ -2,11 +2,11 @@ "config": { "abort": { "no_devices_found": "Geen apparaten gevonden op het netwerk", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk." + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "step": { "confirm": { - "description": "Wilt u beginnen met instellen?" + "description": "Wil je beginnen met instellen?" } } } diff --git a/homeassistant/components/zha/translations/nl.json b/homeassistant/components/zha/translations/nl.json index 54692b22598..1de0e24def3 100644 --- a/homeassistant/components/zha/translations/nl.json +++ b/homeassistant/components/zha/translations/nl.json @@ -2,7 +2,7 @@ "config": { "abort": { "not_zha_device": "Dit apparaat is niet een zha-apparaat.", - "single_instance_allowed": "Al geconfigureerd. Slechts een enkele configuratie mogelijk.", + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "usb_probe_failed": "Kon het USB apparaat niet onderzoeken" }, "error": { diff --git a/homeassistant/components/zodiac/translations/sensor.nl.json b/homeassistant/components/zodiac/translations/sensor.nl.json index 6dba645ed83..23088661aec 100644 --- a/homeassistant/components/zodiac/translations/sensor.nl.json +++ b/homeassistant/components/zodiac/translations/sensor.nl.json @@ -6,7 +6,7 @@ "cancer": "Kreeft", "capricorn": "Steenbok", "gemini": "Tweelingen", - "leo": "Leo", + "leo": "Leeuw", "libra": "Weegschaal", "pisces": "Vissen", "sagittarius": "Boogschutter", diff --git a/homeassistant/components/zoneminder/translations/nl.json b/homeassistant/components/zoneminder/translations/nl.json index 8aed5085391..5dbf8ca0d3d 100644 --- a/homeassistant/components/zoneminder/translations/nl.json +++ b/homeassistant/components/zoneminder/translations/nl.json @@ -11,7 +11,7 @@ }, "error": { "auth_fail": "Gebruikersnaam of wachtwoord is onjuist.", - "cannot_connect": "Kon niet verbinden", + "cannot_connect": "Kan geen verbinding maken", "connection_error": "Kan geen verbinding maken met een ZoneMinder-server.", "invalid_auth": "Ongeldige authenticatie" }, @@ -23,9 +23,9 @@ "password": "Wachtwoord", "path": "ZM-pad", "path_zms": "ZMS-pad", - "ssl": "Gebruik een SSL-certificaat", + "ssl": "Maakt gebruik van een SSL-certificaat", "username": "Gebruikersnaam", - "verify_ssl": "Verifieer SSL-certificaat" + "verify_ssl": "SSL-certificaat verifi\u00ebren" }, "title": "Voeg ZoneMinder server toe." } diff --git a/homeassistant/components/zwave_js/translations/ko.json b/homeassistant/components/zwave_js/translations/ko.json index 7bf9dc8f062..0d00d27fea2 100644 --- a/homeassistant/components/zwave_js/translations/ko.json +++ b/homeassistant/components/zwave_js/translations/ko.json @@ -8,7 +8,9 @@ "addon_start_failed": "Z-Wave JS \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4.", "already_configured": "\uae30\uae30\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4", "already_in_progress": "\uae30\uae30 \uad6c\uc131\uc774 \uc774\ubbf8 \uc9c4\ud589 \uc911\uc785\ub2c8\ub2e4", - "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4" + "cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4", + "discovery_requires_supervisor": "\uc7a5\uce58\uac80\uc0c9\uc744 \uc704\ud574 supervisor\uac00 \ud544\uc694\ud569\ub2c8\ub2e4.", + "not_zwave_device": "\ubc1c\uacac\ub41c \uc7a5\uce58\uac00 Z-Wave \uc7a5\uce58\uac00 \uc544\ub2d9\ub2c8\ub2e4." }, "error": { "addon_start_failed": "Z-Wave JS \uc560\ub4dc\uc628\uc744 \uc2dc\uc791\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. \uad6c\uc131 \ub0b4\uc6a9\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.", @@ -47,6 +49,9 @@ }, "start_addon": { "title": "Z-Wave JS \uc560\ub4dc\uc628\uc774 \uc2dc\uc791\ud558\ub294 \uc911\uc785\ub2c8\ub2e4." + }, + "zeroconf_confirm": { + "title": "Z-Wave JS \uc11c\ubc84 \ubc1c\uacac" } } }, diff --git a/homeassistant/components/zwave_js/translations/nl.json b/homeassistant/components/zwave_js/translations/nl.json index 69481c2a98c..f87b7a701e3 100644 --- a/homeassistant/components/zwave_js/translations/nl.json +++ b/homeassistant/components/zwave_js/translations/nl.json @@ -7,7 +7,7 @@ "addon_set_config_failed": "Instellen van de Z-Wave JS configuratie is mislukt.", "addon_start_failed": "Kan de Z-Wave JS add-on niet starten.", "already_configured": "Apparaat is al geconfigureerd", - "already_in_progress": "De configuratiestroom is al aan de gang", + "already_in_progress": "De configuratie is momenteel al bezig", "cannot_connect": "Kan geen verbinding maken", "discovery_requires_supervisor": "Ontdekking vereist de Supervisor.", "not_zwave_device": "Het ontdekte apparaat is niet een Z-Wave apparaat." From eac872331aa1b49a1ed77495d1125ed8b2f87cb1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 May 2022 18:06:31 -0700 Subject: [PATCH 661/930] Bump frontend to 20220521.0 (#72257) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 5cf48e76d4b..215796d2df8 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220518.0"], + "requirements": ["home-assistant-frontend==20220521.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 4e6174db561..09b3aca9d01 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220518.0 +home-assistant-frontend==20220521.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index d74741129d3..5747a6d0709 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220518.0 +home-assistant-frontend==20220521.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9ca019992c7..c22a565006e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220518.0 +home-assistant-frontend==20220521.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 3918059033e41b1fe44b8e38a733960ad56eed5b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 21 May 2022 09:54:51 +0200 Subject: [PATCH 662/930] Move manual configuration of MQTT button to the integration key (#72167) Add button Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/button.py | 29 +++++++++++++++++++---- tests/components/mqtt/test_button.py | 13 ++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index e1de984f4df..02a98f6ce90 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -191,6 +191,7 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema( PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( { vol.Optional(Platform.ALARM_CONTROL_PANEL.value): cv.ensure_list, + vol.Optional(Platform.BUTTON.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, } diff --git a/homeassistant/components/mqtt/button.py b/homeassistant/components/mqtt/button.py index 22ee7b6d5ae..47e96ff3e1a 100644 --- a/homeassistant/components/mqtt/button.py +++ b/homeassistant/components/mqtt/button.py @@ -1,6 +1,7 @@ """Support for MQTT buttons.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -26,15 +27,17 @@ from .const import ( from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) CONF_PAYLOAD_PRESS = "payload_press" DEFAULT_NAME = "MQTT Button" DEFAULT_PAYLOAD_PRESS = "PRESS" -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, @@ -45,7 +48,14 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Buttons under the button platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(button.DOMAIN), +) + + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -54,7 +64,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT button through configuration.yaml.""" + """Set up MQTT button configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, button.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -65,7 +76,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT button dynamically through MQTT discovery.""" + """Set up MQTT button through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, button.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_button.py b/tests/components/mqtt/test_button.py index 83ef7a42705..941f08e541c 100644 --- a/tests/components/mqtt/test_button.py +++ b/tests/components/mqtt/test_button.py @@ -30,6 +30,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -413,3 +414,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = button.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = button.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 4710ad07c4da52590d805334c30878a10cd4db7d Mon Sep 17 00:00:00 2001 From: Marcio Granzotto Rodrigues Date: Sat, 21 May 2022 05:27:59 -0300 Subject: [PATCH 663/930] Add marciogranzotto as a Bond codeowner (#72238) --- CODEOWNERS | 4 ++-- homeassistant/components/bond/manifest.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index c3e36d1fef7..4e1dcd5d93e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -137,8 +137,8 @@ build.json @home-assistant/supervisor /homeassistant/components/bluesound/ @thrawnarn /homeassistant/components/bmw_connected_drive/ @gerard33 @rikroe /tests/components/bmw_connected_drive/ @gerard33 @rikroe -/homeassistant/components/bond/ @bdraco @prystupa @joshs85 -/tests/components/bond/ @bdraco @prystupa @joshs85 +/homeassistant/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto +/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto /homeassistant/components/bosch_shc/ @tschamm /tests/components/bosch_shc/ @tschamm /homeassistant/components/braviatv/ @bieniu @Drafteed diff --git a/homeassistant/components/bond/manifest.json b/homeassistant/components/bond/manifest.json index b011a5f2dae..187602057c0 100644 --- a/homeassistant/components/bond/manifest.json +++ b/homeassistant/components/bond/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/bond", "requirements": ["bond-api==0.1.18"], "zeroconf": ["_bond._tcp.local."], - "codeowners": ["@bdraco", "@prystupa", "@joshs85"], + "codeowners": ["@bdraco", "@prystupa", "@joshs85", "@marciogranzotto"], "quality_scale": "platinum", "iot_class": "local_push", "loggers": ["bond_api"] From 72dbca4f5bc3a40ac96b2c8fc355ce85bedd003c Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sat, 21 May 2022 05:20:37 -0400 Subject: [PATCH 664/930] Address late feedback on Deluge config flow (#71497) Address late feedback on Deluge --- homeassistant/components/deluge/const.py | 10 ++- .../components/deluge/coordinator.py | 12 +-- homeassistant/components/deluge/sensor.py | 76 +++++++++++-------- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/homeassistant/components/deluge/const.py b/homeassistant/components/deluge/const.py index 505c20e860f..704c024c41b 100644 --- a/homeassistant/components/deluge/const.py +++ b/homeassistant/components/deluge/const.py @@ -1,12 +1,16 @@ """Constants for the Deluge integration.""" import logging +from typing import Final CONF_WEB_PORT = "web_port" +CURRENT_STATUS = "current_status" +DATA_KEYS = ["upload_rate", "download_rate", "dht_upload_rate", "dht_download_rate"] DEFAULT_NAME = "Deluge" DEFAULT_RPC_PORT = 58846 DEFAULT_WEB_PORT = 8112 -DHT_UPLOAD = 1000 -DHT_DOWNLOAD = 1000 -DOMAIN = "deluge" +DOMAIN: Final = "deluge" +DOWNLOAD_SPEED = "download_speed" LOGGER = logging.getLogger(__package__) + +UPLOAD_SPEED = "upload_speed" diff --git a/homeassistant/components/deluge/coordinator.py b/homeassistant/components/deluge/coordinator.py index 0ac97e77674..89f9afc31ad 100644 --- a/homeassistant/components/deluge/coordinator.py +++ b/homeassistant/components/deluge/coordinator.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import LOGGER +from .const import DATA_KEYS, LOGGER class DelugeDataUpdateCoordinator(DataUpdateCoordinator): @@ -38,16 +38,12 @@ class DelugeDataUpdateCoordinator(DataUpdateCoordinator): """Get the latest data from Deluge and updates the state.""" data = {} try: - data[Platform.SENSOR] = await self.hass.async_add_executor_job( + _data = await self.hass.async_add_executor_job( self.api.call, "core.get_session_status", - [ - "upload_rate", - "download_rate", - "dht_upload_rate", - "dht_download_rate", - ], + DATA_KEYS, ) + data[Platform.SENSOR] = {k.decode(): v for k, v in _data.items()} data[Platform.SWITCH] = await self.hass.async_add_executor_job( self.api.call, "core.get_torrents_status", {}, ["paused"] ) diff --git a/homeassistant/components/deluge/sensor.py b/homeassistant/components/deluge/sensor.py index bad535def96..8a8e8f64657 100644 --- a/homeassistant/components/deluge/sensor.py +++ b/homeassistant/components/deluge/sensor.py @@ -1,6 +1,10 @@ """Support for monitoring the Deluge BitTorrent client API.""" from __future__ import annotations +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + from homeassistant.components.sensor import ( SensorEntity, SensorEntityDescription, @@ -13,25 +17,52 @@ from homeassistant.helpers import entity_platform from homeassistant.helpers.typing import StateType from . import DelugeEntity -from .const import DOMAIN +from .const import CURRENT_STATUS, DATA_KEYS, DOMAIN, DOWNLOAD_SPEED, UPLOAD_SPEED from .coordinator import DelugeDataUpdateCoordinator -SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( - SensorEntityDescription( - key="current_status", + +def get_state(data: dict[str, float], key: str) -> str | float: + """Get current download/upload state.""" + upload = data[DATA_KEYS[0]] - data[DATA_KEYS[2]] + download = data[DATA_KEYS[1]] - data[DATA_KEYS[3]] + if key == CURRENT_STATUS: + if upload > 0 and download > 0: + return "Up/Down" + if upload > 0 and download == 0: + return "Seeding" + if upload == 0 and download > 0: + return "Downloading" + return STATE_IDLE + kb_spd = float(upload if key == UPLOAD_SPEED else download) / 1024 + return round(kb_spd, 2 if kb_spd < 0.1 else 1) + + +@dataclass +class DelugeSensorEntityDescription(SensorEntityDescription): + """Class to describe a Deluge sensor.""" + + value: Callable[[dict[str, float]], Any] = lambda val: val + + +SENSOR_TYPES: tuple[DelugeSensorEntityDescription, ...] = ( + DelugeSensorEntityDescription( + key=CURRENT_STATUS, name="Status", + value=lambda data: get_state(data, CURRENT_STATUS), ), - SensorEntityDescription( - key="download_speed", + DelugeSensorEntityDescription( + key=DOWNLOAD_SPEED, name="Down Speed", native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, + value=lambda data: get_state(data, DOWNLOAD_SPEED), ), - SensorEntityDescription( - key="upload_speed", + DelugeSensorEntityDescription( + key=UPLOAD_SPEED, name="Up Speed", native_unit_of_measurement=DATA_RATE_KILOBYTES_PER_SECOND, state_class=SensorStateClass.MEASUREMENT, + value=lambda data: get_state(data, UPLOAD_SPEED), ), ) @@ -51,10 +82,12 @@ async def async_setup_entry( class DelugeSensor(DelugeEntity, SensorEntity): """Representation of a Deluge sensor.""" + entity_description: DelugeSensorEntityDescription + def __init__( self, coordinator: DelugeDataUpdateCoordinator, - description: SensorEntityDescription, + description: DelugeSensorEntityDescription, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -65,27 +98,4 @@ class DelugeSensor(DelugeEntity, SensorEntity): @property def native_value(self) -> StateType: """Return the state of the sensor.""" - if self.coordinator.data: - data = self.coordinator.data[Platform.SENSOR] - upload = data[b"upload_rate"] - data[b"dht_upload_rate"] - download = data[b"download_rate"] - data[b"dht_download_rate"] - if self.entity_description.key == "current_status": - if data: - if upload > 0 and download > 0: - return "Up/Down" - if upload > 0 and download == 0: - return "Seeding" - if upload == 0 and download > 0: - return "Downloading" - return STATE_IDLE - - if data: - if self.entity_description.key == "download_speed": - kb_spd = float(download) - kb_spd = kb_spd / 1024 - return round(kb_spd, 2 if kb_spd < 0.1 else 1) - if self.entity_description.key == "upload_speed": - kb_spd = float(upload) - kb_spd = kb_spd / 1024 - return round(kb_spd, 2 if kb_spd < 0.1 else 1) - return None + return self.entity_description.value(self.coordinator.data[Platform.SENSOR]) From 6c4ba07bd1fffd0be2b76da51f18bd092b2d54df Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 21 May 2022 12:37:47 +0200 Subject: [PATCH 665/930] Remove combined translations / optional markers from Konnected (#72252) --- homeassistant/components/konnected/strings.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/konnected/strings.json b/homeassistant/components/konnected/strings.json index 905597035d5..cd08638c775 100644 --- a/homeassistant/components/konnected/strings.json +++ b/homeassistant/components/konnected/strings.json @@ -63,7 +63,7 @@ "description": "{zone} options", "data": { "type": "Binary Sensor Type", - "name": "[%key:common::config_flow::data::name%] (optional)", + "name": "[%key:common::config_flow::data::name%]", "inverse": "Invert the open/close state" } }, @@ -72,19 +72,19 @@ "description": "{zone} options", "data": { "type": "Sensor Type", - "name": "[%key:common::config_flow::data::name%] (optional)", - "poll_interval": "Poll Interval (minutes) (optional)" + "name": "[%key:common::config_flow::data::name%]", + "poll_interval": "Poll Interval (minutes)" } }, "options_switch": { "title": "Configure Switchable Output", "description": "{zone} options: state {state}", "data": { - "name": "[%key:common::config_flow::data::name%] (optional)", + "name": "[%key:common::config_flow::data::name%]", "activation": "Output when on", - "momentary": "Pulse duration (ms) (optional)", - "pause": "Pause between pulses (ms) (optional)", - "repeat": "Times to repeat (-1=infinite) (optional)", + "momentary": "Pulse duration (ms)", + "pause": "Pause between pulses (ms)", + "repeat": "Times to repeat (-1=infinite)", "more_states": "Configure additional states for this zone" } }, @@ -95,7 +95,7 @@ "discovery": "Respond to discovery requests on your network", "blink": "Blink panel LED on when sending state change", "override_api_host": "Override default Home Assistant API host panel URL", - "api_host": "Override API host URL (optional)" + "api_host": "Override API host URL" } } }, From a152449b72ee2db3f06ae2f87ed9b3d5202d04eb Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sat, 21 May 2022 07:31:24 -0400 Subject: [PATCH 666/930] Fix steam yaml import (#72245) * Fix steam yaml import * uno mas --- homeassistant/components/steam_online/config_flow.py | 12 ++++++++++-- homeassistant/components/steam_online/coordinator.py | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/steam_online/config_flow.py b/homeassistant/components/steam_online/config_flow.py index 246d54c0bff..91965685969 100644 --- a/homeassistant/components/steam_online/config_flow.py +++ b/homeassistant/components/steam_online/config_flow.py @@ -23,11 +23,16 @@ from .const import ( ) -def validate_input(user_input: dict[str, str | int]) -> list[dict[str, str | int]]: +def validate_input( + user_input: dict[str, str | int], multi: bool = False +) -> list[dict[str, str | int]]: """Handle common flow input validation.""" steam.api.key.set(user_input[CONF_API_KEY]) interface = steam.api.interface("ISteamUser") - names = interface.GetPlayerSummaries(steamids=user_input[CONF_ACCOUNT]) + if multi: + names = interface.GetPlayerSummaries(steamids=user_input[CONF_ACCOUNTS]) + else: + names = interface.GetPlayerSummaries(steamids=user_input[CONF_ACCOUNT]) return names["response"]["players"]["player"] @@ -75,6 +80,9 @@ class SteamFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="reauth_successful") self._abort_if_unique_id_configured() if self.source == config_entries.SOURCE_IMPORT: + res = await self.hass.async_add_executor_job( + validate_input, user_input, True + ) accounts_data = { CONF_ACCOUNTS: { acc["steamid"]: acc["personaname"] for acc in res diff --git a/homeassistant/components/steam_online/coordinator.py b/homeassistant/components/steam_online/coordinator.py index 78c850b0ac9..f210c449fa6 100644 --- a/homeassistant/components/steam_online/coordinator.py +++ b/homeassistant/components/steam_online/coordinator.py @@ -56,7 +56,7 @@ class SteamDataUpdateCoordinator(DataUpdateCoordinator): } for k in players: data = self.player_interface.GetSteamLevel(steamid=players[k]["steamid"]) - players[k]["level"] = data["response"]["player_level"] + players[k]["level"] = data["response"].get("player_level") return players async def _async_update_data(self) -> dict[str, dict[str, str | int]]: From 17588c39a44e510653268266d9a167643f2e874b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 21 May 2022 16:14:24 +0200 Subject: [PATCH 667/930] Move manual configuration of MQTT binary_sensor to the integration key (#72183) Add binary_sensor Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + .../components/mqtt/binary_sensor.py | 28 ++++++++++++++++--- tests/components/mqtt/test_binary_sensor.py | 13 +++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 02a98f6ce90..f723d8da3ca 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -191,6 +191,7 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema( PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( { vol.Optional(Platform.ALARM_CONTROL_PANEL.value): cv.ensure_list, + vol.Optional(Platform.BINARY_SENSOR.value): cv.ensure_list, vol.Optional(Platform.BUTTON.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index 5d0da99d786..b9ab190cc9b 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -1,6 +1,7 @@ """Support for MQTT binary sensors.""" from __future__ import annotations +import asyncio from datetime import timedelta import functools import logging @@ -41,8 +42,10 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttAvailability, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -54,7 +57,7 @@ DEFAULT_PAYLOAD_ON = "ON" DEFAULT_FORCE_UPDATE = False CONF_EXPIRE_AFTER = "expire_after" -PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, @@ -66,7 +69,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Binary sensors under the binary_sensor platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(binary_sensor.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -75,7 +84,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT binary sensor through configuration.yaml.""" + """Set up MQTT binary sensor configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, binary_sensor.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -86,7 +96,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT binary sensor dynamically through MQTT discovery.""" + """Set up MQTT binary sensor through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, binary_sensor.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index 5055550be7c..e4a48b07940 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -41,6 +41,7 @@ from .test_common import ( help_test_reloadable_late, help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -973,3 +974,15 @@ async def test_skip_restoring_state_with_over_due_expire_trigger( assert await async_setup_component(hass, domain, {domain: config3}) await hass.async_block_till_done() assert "Skip state recovery after reload for binary_sensor.test3" in caplog.text + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = binary_sensor.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From dd0f9350ac52e31d598b36ddbda049b22c8096c9 Mon Sep 17 00:00:00 2001 From: Michael Chisholm Date: Sun, 22 May 2022 00:18:47 +1000 Subject: [PATCH 668/930] Update async-upnp-client to 0.30.0 (#72269) --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/dlna_dms/manifest.json | 2 +- homeassistant/components/samsungtv/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index e8cdb282118..cae136cd8fd 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.29.0"], + "requirements": ["async-upnp-client==0.30.0"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dms/manifest.json b/homeassistant/components/dlna_dms/manifest.json index 20c38a5be3f..58c79a048b4 100644 --- a/homeassistant/components/dlna_dms/manifest.json +++ b/homeassistant/components/dlna_dms/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Server", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dms", - "requirements": ["async-upnp-client==0.29.0"], + "requirements": ["async-upnp-client==0.30.0"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index 9391fe5e311..bef293185a6 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -7,7 +7,7 @@ "samsungctl[websocket]==0.7.1", "samsungtvws[async,encrypted]==2.5.0", "wakeonlan==2.0.1", - "async-upnp-client==0.29.0" + "async-upnp-client==0.30.0" ], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index 993a0033b1b..cacce09e1f2 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.29.0"], + "requirements": ["async-upnp-client==0.30.0"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 94d9c8ab058..2bf1786ccbe 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.29.0", "getmac==0.8.2"], + "requirements": ["async-upnp-client==0.30.0", "getmac==0.8.2"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman", "@ehendrix23"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 3df38a41995..853c95abcbb 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.10", "async-upnp-client==0.29.0"], + "requirements": ["yeelight==0.7.10", "async-upnp-client==0.30.0"], "codeowners": ["@zewelor", "@shenxn", "@starkillerOG", "@alexyao2015"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 09b3aca9d01..1a61d2ad2b0 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.11 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.29.0 +async-upnp-client==0.30.0 async_timeout==4.0.2 atomicwrites==1.4.0 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 5747a6d0709..4c88b57b9f4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -339,7 +339,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.29.0 +async-upnp-client==0.30.0 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c22a565006e..ca6b5fd1628 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -281,7 +281,7 @@ arcam-fmj==0.12.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.29.0 +async-upnp-client==0.30.0 # homeassistant.components.sleepiq asyncsleepiq==1.2.3 From f1ac9f8ccaf9ac052e3d542c4a89cf8ba7e9c75b Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Sat, 21 May 2022 16:36:02 +0200 Subject: [PATCH 669/930] Add ConfigFlow for here_travel_time (#69212) * Add ConfigFlow for here_travel_time * Use Selectors and Menu * Use separate config flow steps for menus * Move time options together * Update homeassistant/components/here_travel_time/config_flow.py Co-authored-by: Allen Porter * Blacken config_flow * Initialize _config * Only catch HERE errors * Fix unknown error test * Implement async_step_import * Only catch errors for validate_api_key * Split lat/lon * Add additional test coverage * Use TimeSelector in option flow * Assert config entry data/option Co-authored-by: Allen Porter --- .../components/here_travel_time/__init__.py | 146 ++++- .../here_travel_time/config_flow.py | 369 +++++++++++ .../components/here_travel_time/const.py | 15 +- .../components/here_travel_time/manifest.json | 1 + .../components/here_travel_time/model.py | 8 +- .../components/here_travel_time/sensor.py | 191 ++---- .../components/here_travel_time/strings.json | 82 +++ homeassistant/generated/config_flows.py | 1 + tests/components/here_travel_time/conftest.py | 15 + .../fixtures/empty_attribution_response.json | 131 ++++ .../here_travel_time/test_config_flow.py | 589 ++++++++++++++++++ .../components/here_travel_time/test_init.py | 48 ++ .../here_travel_time/test_sensor.py | 484 +++++++------- 13 files changed, 1686 insertions(+), 394 deletions(-) create mode 100644 homeassistant/components/here_travel_time/config_flow.py create mode 100644 homeassistant/components/here_travel_time/strings.json create mode 100644 tests/components/here_travel_time/fixtures/empty_attribution_response.json create mode 100644 tests/components/here_travel_time/test_config_flow.py create mode 100644 tests/components/here_travel_time/test_init.py diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index 0ad344e3fdf..310fe97fad8 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -8,7 +8,15 @@ import async_timeout from herepy import NoRouteFoundError, RouteMode, RoutingApi, RoutingResponse import voluptuous as vol -from homeassistant.const import ATTR_ATTRIBUTION, CONF_UNIT_SYSTEM_IMPERIAL, Platform +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + ATTR_ATTRIBUTION, + CONF_API_KEY, + CONF_MODE, + CONF_UNIT_SYSTEM, + CONF_UNIT_SYSTEM_IMPERIAL, + Platform, +) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.location import find_coordinates @@ -24,9 +32,20 @@ from .const import ( ATTR_ORIGIN, ATTR_ORIGIN_NAME, ATTR_ROUTE, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE_TIME, + CONF_DESTINATION_ENTITY_ID, + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_ENTITY_ID, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + CONF_ROUTE_MODE, + CONF_TRAFFIC_MODE, DEFAULT_SCAN_INTERVAL, DOMAIN, NO_ROUTE_ERROR_MESSAGE, + ROUTE_MODE_FASTEST, TRAFFIC_MODE_ENABLED, TRAVEL_MODES_VEHICLE, ) @@ -37,6 +56,74 @@ PLATFORMS = [Platform.SENSOR] _LOGGER = logging.getLogger(__name__) +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Set up HERE Travel Time from a config entry.""" + api_key = config_entry.data[CONF_API_KEY] + here_client = RoutingApi(api_key) + setup_options(hass, config_entry) + + arrival = ( + dt.parse_time(config_entry.options[CONF_ARRIVAL_TIME]) + if config_entry.options[CONF_ARRIVAL_TIME] is not None + else None + ) + departure = ( + dt.parse_time(config_entry.options[CONF_DEPARTURE_TIME]) + if config_entry.options[CONF_DEPARTURE_TIME] is not None + else None + ) + + here_travel_time_config = HERETravelTimeConfig( + destination_latitude=config_entry.data.get(CONF_DESTINATION_LATITUDE), + destination_longitude=config_entry.data.get(CONF_DESTINATION_LONGITUDE), + destination_entity_id=config_entry.data.get(CONF_DESTINATION_ENTITY_ID), + origin_latitude=config_entry.data.get(CONF_ORIGIN_LATITUDE), + origin_longitude=config_entry.data.get(CONF_ORIGIN_LONGITUDE), + origin_entity_id=config_entry.data.get(CONF_ORIGIN_ENTITY_ID), + travel_mode=config_entry.data[CONF_MODE], + route_mode=config_entry.options[CONF_ROUTE_MODE], + units=config_entry.options[CONF_UNIT_SYSTEM], + arrival=arrival, + departure=departure, + ) + + coordinator = HereTravelTimeDataUpdateCoordinator( + hass, + here_client, + here_travel_time_config, + ) + hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator + hass.config_entries.async_setup_platforms(config_entry, PLATFORMS) + + return True + + +def setup_options(hass: HomeAssistant, config_entry: ConfigEntry) -> None: + """Set up options for a config entry if not set.""" + if not config_entry.options: + hass.config_entries.async_update_entry( + config_entry, + options={ + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: None, + CONF_DEPARTURE_TIME: None, + CONF_UNIT_SYSTEM: hass.config.units.name, + }, + ) + + +async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Unload a config entry.""" + unload_ok = await hass.config_entries.async_unload_platforms( + config_entry, PLATFORMS + ) + if unload_ok: + hass.data[DOMAIN].pop(config_entry.entry_id) + + return unload_ok + + class HereTravelTimeDataUpdateCoordinator(DataUpdateCoordinator): """HERETravelTime DataUpdateCoordinator.""" @@ -135,33 +222,40 @@ class HereTravelTimeDataUpdateCoordinator(DataUpdateCoordinator): ) -> tuple[list[str], list[str], str | None, str | None]: """Prepare parameters for the HERE api.""" - if self.config.origin_entity_id is not None: - origin = find_coordinates(self.hass, self.config.origin_entity_id) - else: - origin = self.config.origin + def _from_entity_id(entity_id: str) -> list[str]: + coordinates = find_coordinates(self.hass, entity_id) + if coordinates is None: + raise InvalidCoordinatesException( + f"No coordinatnes found for {entity_id}" + ) + try: + here_formatted_coordinates = coordinates.split(",") + vol.Schema(cv.gps(here_formatted_coordinates)) + except (AttributeError, vol.Invalid) as ex: + raise InvalidCoordinatesException( + f"{coordinates} are not valid coordinates" + ) from ex + return here_formatted_coordinates + # Destination if self.config.destination_entity_id is not None: - destination = find_coordinates(self.hass, self.config.destination_entity_id) + destination = _from_entity_id(self.config.destination_entity_id) else: - destination = self.config.destination - if destination is None: - raise InvalidCoordinatesException("Destination must be configured") - try: - here_formatted_destination = destination.split(",") - vol.Schema(cv.gps(here_formatted_destination)) - except (vol.Invalid) as ex: - raise InvalidCoordinatesException( - f"{destination} are not valid coordinates" - ) from ex - if origin is None: - raise InvalidCoordinatesException("Origin must be configured") - try: - here_formatted_origin = origin.split(",") - vol.Schema(cv.gps(here_formatted_origin)) - except (AttributeError, vol.Invalid) as ex: - raise InvalidCoordinatesException( - f"{origin} are not valid coordinates" - ) from ex + destination = [ + str(self.config.destination_latitude), + str(self.config.destination_longitude), + ] + + # Origin + if self.config.origin_entity_id is not None: + origin = _from_entity_id(self.config.origin_entity_id) + else: + origin = [ + str(self.config.origin_latitude), + str(self.config.origin_longitude), + ] + + # Arrival/Departure arrival: str | None = None departure: str | None = None if self.config.arrival is not None: @@ -172,7 +266,7 @@ class HereTravelTimeDataUpdateCoordinator(DataUpdateCoordinator): if arrival is None and departure is None: departure = "now" - return (here_formatted_origin, here_formatted_destination, arrival, departure) + return (origin, destination, arrival, departure) def build_hass_attribution(source_attribution: dict) -> str | None: diff --git a/homeassistant/components/here_travel_time/config_flow.py b/homeassistant/components/here_travel_time/config_flow.py new file mode 100644 index 00000000000..bc6d57aa892 --- /dev/null +++ b/homeassistant/components/here_travel_time/config_flow.py @@ -0,0 +1,369 @@ +"""Config flow for HERE Travel Time integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from herepy import HEREError, InvalidCredentialsError, RouteMode, RoutingApi +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME, CONF_UNIT_SYSTEM +from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.selector import ( + EntitySelector, + LocationSelector, + TimeSelector, + selector, +) + +from .const import ( + CONF_ARRIVAL, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE, + CONF_DEPARTURE_TIME, + CONF_ROUTE_MODE, + CONF_TRAFFIC_MODE, + DEFAULT_NAME, + DOMAIN, + ROUTE_MODE_FASTEST, + ROUTE_MODES, + TRAFFIC_MODE_DISABLED, + TRAFFIC_MODE_ENABLED, + TRAFFIC_MODES, + TRAVEL_MODE_CAR, + TRAVEL_MODE_PUBLIC_TIME_TABLE, + TRAVEL_MODES, + UNITS, +) +from .sensor import ( + CONF_DESTINATION_ENTITY_ID, + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_ENTITY_ID, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, +) + +_LOGGER = logging.getLogger(__name__) + + +def is_dupe_import( + entry: config_entries.ConfigEntry, + user_input: dict[str, Any], + options: dict[str, Any], +) -> bool: + """Return whether imported config already exists.""" + # Check the main data keys + if any( + user_input[key] != entry.data[key] + for key in (CONF_API_KEY, CONF_MODE, CONF_NAME) + ): + return False + + # Check origin/destination + for key in ( + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + CONF_DESTINATION_ENTITY_ID, + CONF_ORIGIN_ENTITY_ID, + ): + if user_input.get(key) != entry.data.get(key): + return False + + # We have to check for options that don't have defaults + for key in ( + CONF_TRAFFIC_MODE, + CONF_UNIT_SYSTEM, + CONF_ROUTE_MODE, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE_TIME, + ): + if options.get(key) != entry.options.get(key): + return False + + return True + + +def validate_api_key(api_key: str) -> None: + """Validate the user input allows us to connect.""" + known_working_origin = [38.9, -77.04833] + known_working_destination = [39.0, -77.1] + RoutingApi(api_key).public_transport_timetable( + known_working_origin, + known_working_destination, + True, + [ + RouteMode[ROUTE_MODE_FASTEST], + RouteMode[TRAVEL_MODE_CAR], + RouteMode[TRAFFIC_MODE_ENABLED], + ], + arrival=None, + departure="now", + ) + + +def get_user_step_schema(data: dict[str, Any]) -> vol.Schema: + """Get a populated schema or default.""" + return vol.Schema( + { + vol.Optional( + CONF_NAME, default=data.get(CONF_NAME, DEFAULT_NAME) + ): cv.string, + vol.Required(CONF_API_KEY, default=data.get(CONF_API_KEY)): cv.string, + vol.Optional( + CONF_MODE, default=data.get(CONF_MODE, TRAVEL_MODE_CAR) + ): vol.In(TRAVEL_MODES), + } + ) + + +class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for HERE Travel Time.""" + + VERSION = 1 + + _config: dict[str, Any] = {} + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> HERETravelTimeOptionsFlow: + """Get the options flow.""" + return HERETravelTimeOptionsFlow(config_entry) + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + errors = {} + user_input = user_input or {} + if user_input: + try: + await self.hass.async_add_executor_job( + validate_api_key, user_input[CONF_API_KEY] + ) + except InvalidCredentialsError: + errors["base"] = "invalid_auth" + except HEREError as error: + _LOGGER.exception("Unexpected exception: %s", error) + errors["base"] = "unknown" + if not errors: + self._config = user_input + return self.async_show_menu( + step_id="origin_menu", + menu_options=["origin_coordinates", "origin_entity"], + ) + return self.async_show_form( + step_id="user", data_schema=get_user_step_schema(user_input), errors=errors + ) + + async def async_step_origin_coordinates( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure origin by using gps coordinates.""" + if user_input is not None: + self._config[CONF_ORIGIN_LATITUDE] = user_input["origin"]["latitude"] + self._config[CONF_ORIGIN_LONGITUDE] = user_input["origin"]["longitude"] + return self.async_show_menu( + step_id="destination_menu", + menu_options=["destination_coordinates", "destination_entity"], + ) + schema = vol.Schema({"origin": selector({LocationSelector.selector_type: {}})}) + return self.async_show_form(step_id="origin_coordinates", data_schema=schema) + + async def async_step_origin_entity( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure origin by using an entity.""" + if user_input is not None: + self._config[CONF_ORIGIN_ENTITY_ID] = user_input[CONF_ORIGIN_ENTITY_ID] + return self.async_show_menu( + step_id="destination_menu", + menu_options=["destination_coordinates", "destination_entity"], + ) + schema = vol.Schema( + {CONF_ORIGIN_ENTITY_ID: selector({EntitySelector.selector_type: {}})} + ) + return self.async_show_form(step_id="origin_entity", data_schema=schema) + + async def async_step_destination_coordinates( + self, + user_input: dict[str, Any] | None = None, + ) -> FlowResult: + """Configure destination by using gps coordinates.""" + if user_input is not None: + self._config[CONF_DESTINATION_LATITUDE] = user_input["destination"][ + "latitude" + ] + self._config[CONF_DESTINATION_LONGITUDE] = user_input["destination"][ + "longitude" + ] + return self.async_create_entry( + title=self._config[CONF_NAME], data=self._config + ) + schema = vol.Schema( + {"destination": selector({LocationSelector.selector_type: {}})} + ) + return self.async_show_form( + step_id="destination_coordinates", data_schema=schema + ) + + async def async_step_destination_entity( + self, + user_input: dict[str, Any] | None = None, + ) -> FlowResult: + """Configure destination by using an entity.""" + if user_input is not None: + self._config[CONF_DESTINATION_ENTITY_ID] = user_input[ + CONF_DESTINATION_ENTITY_ID + ] + return self.async_create_entry( + title=self._config[CONF_NAME], data=self._config + ) + schema = vol.Schema( + {CONF_DESTINATION_ENTITY_ID: selector({EntitySelector.selector_type: {}})} + ) + return self.async_show_form(step_id="destination_entity", data_schema=schema) + + async def async_step_import( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Import from configuration.yaml.""" + options: dict[str, Any] = {} + user_input, options = self._transform_import_input(user_input) + # We need to prevent duplicate imports + if any( + is_dupe_import(entry, user_input, options) + for entry in self.hass.config_entries.async_entries(DOMAIN) + if entry.source == config_entries.SOURCE_IMPORT + ): + return self.async_abort(reason="already_configured") + return self.async_create_entry( + title=user_input[CONF_NAME], data=user_input, options=options + ) + + def _transform_import_input( + self, user_input + ) -> tuple[dict[str, Any], dict[str, Any]]: + """Transform platform schema input to new model.""" + options: dict[str, Any] = {} + if user_input.get(CONF_ORIGIN_LATITUDE) is not None: + user_input[CONF_ORIGIN_LATITUDE] = user_input.pop(CONF_ORIGIN_LATITUDE) + user_input[CONF_ORIGIN_LONGITUDE] = user_input.pop(CONF_ORIGIN_LONGITUDE) + else: + user_input[CONF_ORIGIN_ENTITY_ID] = user_input.pop(CONF_ORIGIN_ENTITY_ID) + + if user_input.get(CONF_DESTINATION_LATITUDE) is not None: + user_input[CONF_DESTINATION_LATITUDE] = user_input.pop( + CONF_DESTINATION_LATITUDE + ) + user_input[CONF_DESTINATION_LONGITUDE] = user_input.pop( + CONF_DESTINATION_LONGITUDE + ) + else: + user_input[CONF_DESTINATION_ENTITY_ID] = user_input.pop( + CONF_DESTINATION_ENTITY_ID + ) + + options[CONF_TRAFFIC_MODE] = ( + TRAFFIC_MODE_ENABLED + if user_input.pop(CONF_TRAFFIC_MODE, False) + else TRAFFIC_MODE_DISABLED + ) + options[CONF_ROUTE_MODE] = user_input.pop(CONF_ROUTE_MODE) + options[CONF_UNIT_SYSTEM] = user_input.pop( + CONF_UNIT_SYSTEM, self.hass.config.units.name + ) + options[CONF_ARRIVAL_TIME] = user_input.pop(CONF_ARRIVAL, None) + options[CONF_DEPARTURE_TIME] = user_input.pop(CONF_DEPARTURE, None) + + return user_input, options + + +class HERETravelTimeOptionsFlow(config_entries.OptionsFlow): + """Handle HERE Travel Time options.""" + + _config: dict[str, Any] = {} + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize HERE Travel Time options flow.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the HERE Travel Time options.""" + if user_input is not None: + self._config = user_input + if self.config_entry.data[CONF_MODE] == TRAVEL_MODE_PUBLIC_TIME_TABLE: + return self.async_show_menu( + step_id="time_menu", + menu_options=["departure_time", "arrival_time", "no_time"], + ) + return self.async_show_menu( + step_id="time_menu", + menu_options=["departure_time", "no_time"], + ) + + options = { + vol.Optional( + CONF_TRAFFIC_MODE, + default=self.config_entry.options.get( + CONF_TRAFFIC_MODE, TRAFFIC_MODE_ENABLED + ), + ): vol.In(TRAFFIC_MODES), + vol.Optional( + CONF_ROUTE_MODE, + default=self.config_entry.options.get( + CONF_ROUTE_MODE, ROUTE_MODE_FASTEST + ), + ): vol.In(ROUTE_MODES), + vol.Optional( + CONF_UNIT_SYSTEM, + default=self.config_entry.options.get( + CONF_UNIT_SYSTEM, self.hass.config.units.name + ), + ): vol.In(UNITS), + } + + return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) + + async def async_step_no_time( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Create Options Entry.""" + return self.async_create_entry(title="", data=self._config) + + async def async_step_arrival_time( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure arrival time.""" + if user_input is not None: + self._config[CONF_ARRIVAL_TIME] = user_input[CONF_ARRIVAL_TIME] + return self.async_create_entry(title="", data=self._config) + + options = {"arrival_time": selector({TimeSelector.selector_type: {}})} + + return self.async_show_form( + step_id="arrival_time", data_schema=vol.Schema(options) + ) + + async def async_step_departure_time( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Configure departure time.""" + if user_input is not None: + self._config[CONF_DEPARTURE_TIME] = user_input[CONF_DEPARTURE_TIME] + return self.async_create_entry(title="", data=self._config) + + options = {"departure_time": selector({TimeSelector.selector_type: {}})} + + return self.async_show_form( + step_id="departure_time", data_schema=vol.Schema(options) + ) diff --git a/homeassistant/components/here_travel_time/const.py b/homeassistant/components/here_travel_time/const.py index a6b958ebf5e..bde17f5c306 100644 --- a/homeassistant/components/here_travel_time/const.py +++ b/homeassistant/components/here_travel_time/const.py @@ -8,20 +8,19 @@ from homeassistant.const import ( DOMAIN = "here_travel_time" DEFAULT_SCAN_INTERVAL = 300 -CONF_DESTINATION = "destination" -CONF_ORIGIN = "origin" + +CONF_DESTINATION_LATITUDE = "destination_latitude" +CONF_DESTINATION_LONGITUDE = "destination_longitude" +CONF_DESTINATION_ENTITY_ID = "destination_entity_id" +CONF_ORIGIN_LATITUDE = "origin_latitude" +CONF_ORIGIN_LONGITUDE = "origin_longitude" +CONF_ORIGIN_ENTITY_ID = "origin_entity_id" CONF_TRAFFIC_MODE = "traffic_mode" CONF_ROUTE_MODE = "route_mode" CONF_ARRIVAL = "arrival" CONF_DEPARTURE = "departure" CONF_ARRIVAL_TIME = "arrival_time" CONF_DEPARTURE_TIME = "departure_time" -CONF_TIME_TYPE = "time_type" -CONF_TIME = "time" - -ARRIVAL_TIME = "Arrival Time" -DEPARTURE_TIME = "Departure Time" -TIME_TYPES = [ARRIVAL_TIME, DEPARTURE_TIME] DEFAULT_NAME = "HERE Travel Time" diff --git a/homeassistant/components/here_travel_time/manifest.json b/homeassistant/components/here_travel_time/manifest.json index b620153bba7..68370311254 100644 --- a/homeassistant/components/here_travel_time/manifest.json +++ b/homeassistant/components/here_travel_time/manifest.json @@ -1,6 +1,7 @@ { "domain": "here_travel_time", "name": "HERE Travel Time", + "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/here_travel_time", "requirements": ["herepy==2.0.0"], "codeowners": ["@eifinger"], diff --git a/homeassistant/components/here_travel_time/model.py b/homeassistant/components/here_travel_time/model.py index eb85e966edf..65673a1e8b6 100644 --- a/homeassistant/components/here_travel_time/model.py +++ b/homeassistant/components/here_travel_time/model.py @@ -24,10 +24,12 @@ class HERERoutingData(TypedDict): class HERETravelTimeConfig: """Configuration for HereTravelTimeDataUpdateCoordinator.""" - origin: str | None - destination: str | None - origin_entity_id: str | None + destination_latitude: float | None + destination_longitude: float | None destination_entity_id: str | None + origin_latitude: float | None + origin_longitude: float | None + origin_entity_id: str | None travel_mode: str route_mode: str units: str diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 304c49b6bed..4a09252f068 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -4,11 +4,10 @@ from __future__ import annotations from datetime import timedelta import logging -import herepy -from herepy.here_enum import RouteMode import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_MODE, @@ -16,8 +15,6 @@ from homeassistant.const import ( CONF_MODE, CONF_NAME, CONF_UNIT_SYSTEM, - CONF_UNIT_SYSTEM_IMPERIAL, - CONF_UNIT_SYSTEM_METRIC, TIME_MINUTES, ) from homeassistant.core import HomeAssistant @@ -28,74 +25,47 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import HereTravelTimeDataUpdateCoordinator -from .model import HERETravelTimeConfig - -_LOGGER = logging.getLogger(__name__) - -CONF_DESTINATION_LATITUDE = "destination_latitude" -CONF_DESTINATION_LONGITUDE = "destination_longitude" -CONF_DESTINATION_ENTITY_ID = "destination_entity_id" -CONF_ORIGIN_LATITUDE = "origin_latitude" -CONF_ORIGIN_LONGITUDE = "origin_longitude" -CONF_ORIGIN_ENTITY_ID = "origin_entity_id" -CONF_TRAFFIC_MODE = "traffic_mode" -CONF_ROUTE_MODE = "route_mode" -CONF_ARRIVAL = "arrival" -CONF_DEPARTURE = "departure" - -DEFAULT_NAME = "HERE Travel Time" - -TRAVEL_MODE_BICYCLE = "bicycle" -TRAVEL_MODE_CAR = "car" -TRAVEL_MODE_PEDESTRIAN = "pedestrian" -TRAVEL_MODE_PUBLIC = "publicTransport" -TRAVEL_MODE_PUBLIC_TIME_TABLE = "publicTransportTimeTable" -TRAVEL_MODE_TRUCK = "truck" -TRAVEL_MODE = [ +from .const import ( + ATTR_DURATION, + ATTR_DURATION_IN_TRAFFIC, + ATTR_TRAFFIC_MODE, + ATTR_UNIT_SYSTEM, + CONF_ARRIVAL, + CONF_DEPARTURE, + CONF_DESTINATION_ENTITY_ID, + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_ENTITY_ID, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + CONF_ROUTE_MODE, + CONF_TRAFFIC_MODE, + DEFAULT_NAME, + DOMAIN, + ICON_BICYCLE, + ICON_CAR, + ICON_PEDESTRIAN, + ICON_PUBLIC, + ICON_TRUCK, + ROUTE_MODE_FASTEST, + ROUTE_MODES, + TRAFFIC_MODE_ENABLED, TRAVEL_MODE_BICYCLE, TRAVEL_MODE_CAR, TRAVEL_MODE_PEDESTRIAN, TRAVEL_MODE_PUBLIC, TRAVEL_MODE_PUBLIC_TIME_TABLE, TRAVEL_MODE_TRUCK, -] + TRAVEL_MODES, + TRAVEL_MODES_PUBLIC, + UNITS, +) -TRAVEL_MODES_PUBLIC = [TRAVEL_MODE_PUBLIC, TRAVEL_MODE_PUBLIC_TIME_TABLE] -TRAVEL_MODES_VEHICLE = [TRAVEL_MODE_CAR, TRAVEL_MODE_TRUCK] -TRAVEL_MODES_NON_VEHICLE = [TRAVEL_MODE_BICYCLE, TRAVEL_MODE_PEDESTRIAN] +_LOGGER = logging.getLogger(__name__) -TRAFFIC_MODE_ENABLED = "traffic_enabled" -TRAFFIC_MODE_DISABLED = "traffic_disabled" - -ROUTE_MODE_FASTEST = "fastest" -ROUTE_MODE_SHORTEST = "shortest" -ROUTE_MODE = [ROUTE_MODE_FASTEST, ROUTE_MODE_SHORTEST] - -ICON_BICYCLE = "mdi:bike" -ICON_CAR = "mdi:car" -ICON_PEDESTRIAN = "mdi:walk" -ICON_PUBLIC = "mdi:bus" -ICON_TRUCK = "mdi:truck" - -UNITS = [CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL] - -ATTR_DURATION = "duration" -ATTR_DISTANCE = "distance" -ATTR_ROUTE = "route" -ATTR_ORIGIN = "origin" -ATTR_DESTINATION = "destination" - -ATTR_UNIT_SYSTEM = CONF_UNIT_SYSTEM -ATTR_TRAFFIC_MODE = CONF_TRAFFIC_MODE - -ATTR_DURATION_IN_TRAFFIC = "duration_in_traffic" -ATTR_ORIGIN_NAME = "origin_name" -ATTR_DESTINATION_NAME = "destination_name" SCAN_INTERVAL = timedelta(minutes=5) -NO_ROUTE_ERROR_MESSAGE = "HERE could not find a route based on the input" - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_API_KEY): cv.string, @@ -113,8 +83,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Exclusive(CONF_ORIGIN_ENTITY_ID, "origin"): cv.entity_id, vol.Optional(CONF_DEPARTURE): cv.time, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_MODE, default=TRAVEL_MODE_CAR): vol.In(TRAVEL_MODE), - vol.Optional(CONF_ROUTE_MODE, default=ROUTE_MODE_FASTEST): vol.In(ROUTE_MODE), + vol.Optional(CONF_MODE, default=TRAVEL_MODE_CAR): vol.In(TRAVEL_MODES), + vol.Optional(CONF_ROUTE_MODE, default=ROUTE_MODE_FASTEST): vol.In(ROUTE_MODES), vol.Optional(CONF_TRAFFIC_MODE, default=False): cv.boolean, vol.Optional(CONF_UNIT_SYSTEM): vol.In(UNITS), } @@ -150,79 +120,36 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the HERE travel time platform.""" - api_key = config[CONF_API_KEY] - here_client = herepy.RoutingApi(api_key) - - if not await hass.async_add_executor_job( - _are_valid_client_credentials, here_client - ): - _LOGGER.error( - "Invalid credentials. This error is returned if the specified token was invalid or no contract could be found for this token" + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data=config, ) - return - - if config.get(CONF_ORIGIN_LATITUDE) is not None: - origin = f"{config[CONF_ORIGIN_LATITUDE]},{config[CONF_ORIGIN_LONGITUDE]}" - origin_entity_id = None - else: - origin = None - origin_entity_id = config[CONF_ORIGIN_ENTITY_ID] - - if config.get(CONF_DESTINATION_LATITUDE) is not None: - destination = ( - f"{config[CONF_DESTINATION_LATITUDE]},{config[CONF_DESTINATION_LONGITUDE]}" - ) - destination_entity_id = None - else: - destination = None - destination_entity_id = config[CONF_DESTINATION_ENTITY_ID] - - traffic_mode = config[CONF_TRAFFIC_MODE] - name = config[CONF_NAME] - - here_travel_time_config = HERETravelTimeConfig( - origin=origin, - destination=destination, - origin_entity_id=origin_entity_id, - destination_entity_id=destination_entity_id, - travel_mode=config[CONF_MODE], - route_mode=config[CONF_ROUTE_MODE], - units=config.get(CONF_UNIT_SYSTEM, hass.config.units.name), - arrival=config.get(CONF_ARRIVAL), - departure=config.get(CONF_DEPARTURE), ) - coordinator = HereTravelTimeDataUpdateCoordinator( - hass, - here_client, - here_travel_time_config, + _LOGGER.warning( + "Your HERE travel time configuration has been imported into the UI; " + "please remove it from configuration.yaml as support for it will be " + "removed in a future release" ) - sensor = HERETravelTimeSensor(name, traffic_mode, coordinator) - async_add_entities([sensor]) - - -def _are_valid_client_credentials(here_client: herepy.RoutingApi) -> bool: - """Check if the provided credentials are correct using defaults.""" - known_working_origin = [38.9, -77.04833] - known_working_destination = [39.0, -77.1] - try: - here_client.public_transport_timetable( - known_working_origin, - known_working_destination, - True, - [ - RouteMode[ROUTE_MODE_FASTEST], - RouteMode[TRAVEL_MODE_CAR], - RouteMode[TRAFFIC_MODE_ENABLED], - ], - arrival=None, - departure="now", - ) - except herepy.InvalidCredentialsError: - return False - return True +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Add HERE travel time entities from a config_entry.""" + async_add_entities( + [ + HERETravelTimeSensor( + config_entry.data[CONF_NAME], + config_entry.options[CONF_TRAFFIC_MODE], + hass.data[DOMAIN][config_entry.entry_id], + ) + ], + ) class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): @@ -231,12 +158,12 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): def __init__( self, name: str, - traffic_mode: bool, + traffic_mode: str, coordinator: HereTravelTimeDataUpdateCoordinator, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) - self._traffic_mode = traffic_mode + self._traffic_mode = traffic_mode == TRAFFIC_MODE_ENABLED self._attr_native_unit_of_measurement = TIME_MINUTES self._attr_name = name diff --git a/homeassistant/components/here_travel_time/strings.json b/homeassistant/components/here_travel_time/strings.json new file mode 100644 index 00000000000..e4a20a38d6b --- /dev/null +++ b/homeassistant/components/here_travel_time/strings.json @@ -0,0 +1,82 @@ +{ + "config": { + "step": { + "user": { + "data": { + "name": "[%key:common::config_flow::data::name%]", + "api_key": "[%key:common::config_flow::data::api_key%]", + "mode": "Travel Mode" + } + }, + "origin_coordinates": { + "title": "Choose Origin", + "data": { + "origin": "Origin as GPS coordinates" + } + }, + "origin_entity_id": { + "title": "Choose Origin", + "data": { + "origin_entity_id": "Origin using an entity" + } + }, + "destination_menu": { + "title": "Choose Destination", + "menu_options": { + "destination_coordinates": "Using a map location", + "destination_entity": "Using an entity" + } + }, + "destination_coordinates": { + "title": "Choose Destination", + "data": { + "destination": "Destination as GPS coordinates" + } + }, + "destination_entity_id": { + "title": "Choose Destination", + "data": { + "destination_entity_id": "Destination using an entity" + } + } + }, + "error": { + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + }, + "options": { + "step": { + "init": { + "data": { + "traffic_mode": "Traffic Mode", + "route_mode": "Route Mode", + "unit_system": "Unit system" + } + }, + "time_menu": { + "title": "Choose Time Type", + "menu_options": { + "departure_time": "Configure a departure time", + "arrival_time": "Configure an arrival time", + "no_time": "Do not configure a time" + } + }, + "departure_time": { + "title": "Choose Departure Time", + "data": { + "departure_time": "Departure Time" + } + }, + "arrival_time": { + "title": "Choose Arrival Time", + "data": { + "arrival_time": "Arrival Time" + } + } + } + } +} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 7f0059b2da9..67299f403c2 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -143,6 +143,7 @@ FLOWS = { "hangouts", "harmony", "heos", + "here_travel_time", "hisense_aehw4a1", "hive", "hlk_sw16", diff --git a/tests/components/here_travel_time/conftest.py b/tests/components/here_travel_time/conftest.py index 83f0659f516..368b070428e 100644 --- a/tests/components/here_travel_time/conftest.py +++ b/tests/components/here_travel_time/conftest.py @@ -12,6 +12,11 @@ RESPONSE = RoutingResponse.new_from_jsondict( ) RESPONSE.route_short = "US-29 - K St NW; US-29 - Whitehurst Fwy; I-495 N - Capital Beltway; MD-187 S - Old Georgetown Rd" +EMPTY_ATTRIBUTION_RESPONSE = RoutingResponse.new_from_jsondict( + json.loads(load_fixture("here_travel_time/empty_attribution_response.json")) +) +EMPTY_ATTRIBUTION_RESPONSE.route_short = "US-29 - K St NW; US-29 - Whitehurst Fwy; I-495 N - Capital Beltway; MD-187 S - Old Georgetown Rd" + @pytest.fixture(name="valid_response") def valid_response_fixture(): @@ -21,3 +26,13 @@ def valid_response_fixture(): return_value=RESPONSE, ) as mock: yield mock + + +@pytest.fixture(name="empty_attribution_response") +def empty_attribution_response_fixture(): + """Return valid api response with an empty attribution.""" + with patch( + "herepy.RoutingApi.public_transport_timetable", + return_value=EMPTY_ATTRIBUTION_RESPONSE, + ) as mock: + yield mock diff --git a/tests/components/here_travel_time/fixtures/empty_attribution_response.json b/tests/components/here_travel_time/fixtures/empty_attribution_response.json new file mode 100644 index 00000000000..cc1bb20a373 --- /dev/null +++ b/tests/components/here_travel_time/fixtures/empty_attribution_response.json @@ -0,0 +1,131 @@ +{ + "response": { + "metaInfo": { + "timestamp": "2019-07-19T07:38:39Z", + "mapVersion": "8.30.98.154", + "moduleVersion": "7.2.201928-4446", + "interfaceVersion": "2.6.64", + "availableMapVersion": ["8.30.98.154"] + }, + "route": [ + { + "waypoint": [ + { + "linkId": "+732182239", + "mappedPosition": { + "latitude": 38.9, + "longitude": -77.0488358 + }, + "originalPosition": { + "latitude": 38.9, + "longitude": -77.0483301 + }, + "type": "stopOver", + "spot": 0.4946237, + "sideOfStreet": "right", + "mappedRoadName": "22nd St NW", + "label": "22nd St NW", + "shapeIndex": 0, + "source": "user" + }, + { + "linkId": "+942865877", + "mappedPosition": { + "latitude": 38.9999735, + "longitude": -77.100141 + }, + "originalPosition": { + "latitude": 38.9999999, + "longitude": -77.1000001 + }, + "type": "stopOver", + "spot": 1, + "sideOfStreet": "left", + "mappedRoadName": "Service Rd S", + "label": "Service Rd S", + "shapeIndex": 279, + "source": "user" + } + ], + "mode": { + "type": "fastest", + "transportModes": ["car"], + "trafficMode": "enabled", + "feature": [] + }, + "leg": [ + { + "start": { + "linkId": "+732182239", + "mappedPosition": { + "latitude": 38.9, + "longitude": -77.0488358 + }, + "originalPosition": { + "latitude": 38.9, + "longitude": -77.0483301 + }, + "type": "stopOver", + "spot": 0.4946237, + "sideOfStreet": "right", + "mappedRoadName": "22nd St NW", + "label": "22nd St NW", + "shapeIndex": 0, + "source": "user" + }, + "end": { + "linkId": "+942865877", + "mappedPosition": { + "latitude": 38.9999735, + "longitude": -77.100141 + }, + "originalPosition": { + "latitude": 38.9999999, + "longitude": -77.1000001 + }, + "type": "stopOver", + "spot": 1, + "sideOfStreet": "left", + "mappedRoadName": "Service Rd S", + "label": "Service Rd S", + "shapeIndex": 279, + "source": "user" + }, + "length": 23903, + "travelTime": 1884, + "maneuver": [ + { + "position": { + "latitude": 38.9999735, + "longitude": -77.100141 + }, + "instruction": "Arrive at Service Rd S. Your destination is on the left.", + "travelTime": 0, + "length": 0, + "id": "M16", + "_type": "PrivateTransportManeuverType" + } + ] + } + ], + "summary": { + "distance": 23903, + "trafficTime": 1861, + "baseTime": 1803, + "flags": [ + "noThroughRoad", + "motorway", + "builtUpArea", + "park", + "privateRoad" + ], + "text": "The trip takes 23.9 km and 31 mins.", + "travelTime": 1861, + "_type": "RouteSummaryType" + } + } + ], + "language": "en-us", + "sourceAttribution": {} + } +} diff --git a/tests/components/here_travel_time/test_config_flow.py b/tests/components/here_travel_time/test_config_flow.py new file mode 100644 index 00000000000..105128c7cfb --- /dev/null +++ b/tests/components/here_travel_time/test_config_flow.py @@ -0,0 +1,589 @@ +"""Test the HERE Travel Time config flow.""" +from unittest.mock import patch + +from herepy import HEREError +from herepy.routing_api import InvalidCredentialsError +import pytest + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.here_travel_time.const import ( + CONF_ARRIVAL, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE, + CONF_DEPARTURE_TIME, + CONF_ROUTE_MODE, + CONF_TRAFFIC_MODE, + DOMAIN, + ROUTE_MODE_FASTEST, + TRAFFIC_MODE_ENABLED, + TRAVEL_MODE_CAR, + TRAVEL_MODE_PUBLIC_TIME_TABLE, +) +from homeassistant.components.here_travel_time.sensor import ( + CONF_DESTINATION_ENTITY_ID, + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_ENTITY_ID, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, +) +from homeassistant.const import ( + CONF_API_KEY, + CONF_MODE, + CONF_NAME, + CONF_UNIT_SYSTEM, + CONF_UNIT_SYSTEM_IMPERIAL, + CONF_UNIT_SYSTEM_METRIC, +) +from homeassistant.core import HomeAssistant + +from .const import ( + API_KEY, + CAR_DESTINATION_LATITUDE, + CAR_DESTINATION_LONGITUDE, + CAR_ORIGIN_LATITUDE, + CAR_ORIGIN_LONGITUDE, +) + +from tests.common import MockConfigEntry + + +@pytest.fixture(name="user_step_result") +async def user_step_result_fixture(hass: HomeAssistant) -> data_entry_flow.FlowResult: + """Provide the result of a completed user step.""" + init_result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + user_step_result = await hass.config_entries.flow.async_configure( + init_result["flow_id"], + { + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + await hass.async_block_till_done() + yield user_step_result + + +@pytest.fixture(name="option_init_result") +async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.FlowResult: + """Provide the result of a completed options init step.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_PUBLIC_TIME_TABLE, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + flow = await hass.config_entries.options.async_init(entry.entry_id) + result = await hass.config_entries.options.async_configure( + flow["flow_id"], + user_input={ + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + }, + ) + yield result + + +@pytest.fixture(name="origin_step_result") +async def origin_step_result_fixture( + hass: HomeAssistant, user_step_result: data_entry_flow.FlowResult +) -> data_entry_flow.FlowResult: + """Provide the result of a completed origin by coordinates step.""" + origin_menu_result = await hass.config_entries.flow.async_configure( + user_step_result["flow_id"], {"next_step_id": "origin_coordinates"} + ) + + location_selector_result = await hass.config_entries.flow.async_configure( + origin_menu_result["flow_id"], + { + "origin": { + "latitude": float(CAR_ORIGIN_LATITUDE), + "longitude": float(CAR_ORIGIN_LONGITUDE), + "radius": 3.0, + } + }, + ) + yield location_selector_result + + +@pytest.mark.parametrize( + "menu_options", + (["origin_coordinates", "origin_entity"],), +) +@pytest.mark.usefixtures("valid_response") +async def test_step_user(hass: HomeAssistant, menu_options) -> None: + """Test the user step.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["errors"] == {} + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.RESULT_TYPE_MENU + assert result2["menu_options"] == menu_options + + +@pytest.mark.usefixtures("valid_response") +async def test_step_origin_coordinates( + hass: HomeAssistant, user_step_result: data_entry_flow.FlowResult +) -> None: + """Test the origin coordinates step.""" + menu_result = await hass.config_entries.flow.async_configure( + user_step_result["flow_id"], {"next_step_id": "origin_coordinates"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + + location_selector_result = await hass.config_entries.flow.async_configure( + menu_result["flow_id"], + { + "origin": { + "latitude": float(CAR_ORIGIN_LATITUDE), + "longitude": float(CAR_ORIGIN_LONGITUDE), + "radius": 3.0, + } + }, + ) + assert location_selector_result["type"] == data_entry_flow.RESULT_TYPE_MENU + + +@pytest.mark.usefixtures("valid_response") +async def test_step_origin_entity( + hass: HomeAssistant, user_step_result: data_entry_flow.FlowResult +) -> None: + """Test the origin coordinates step.""" + menu_result = await hass.config_entries.flow.async_configure( + user_step_result["flow_id"], {"next_step_id": "origin_entity"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + + entity_selector_result = await hass.config_entries.flow.async_configure( + menu_result["flow_id"], + {"origin_entity_id": "zone.home"}, + ) + assert entity_selector_result["type"] == data_entry_flow.RESULT_TYPE_MENU + + +@pytest.mark.usefixtures("valid_response") +async def test_step_destination_coordinates( + hass: HomeAssistant, origin_step_result: data_entry_flow.FlowResult +) -> None: + """Test the origin coordinates step.""" + menu_result = await hass.config_entries.flow.async_configure( + origin_step_result["flow_id"], {"next_step_id": "destination_coordinates"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + + location_selector_result = await hass.config_entries.flow.async_configure( + menu_result["flow_id"], + { + "destination": { + "latitude": float(CAR_DESTINATION_LATITUDE), + "longitude": float(CAR_DESTINATION_LONGITUDE), + "radius": 3.0, + } + }, + ) + assert location_selector_result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.data == { + CONF_NAME: "test", + CONF_API_KEY: API_KEY, + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_MODE: TRAVEL_MODE_CAR, + } + + +@pytest.mark.usefixtures("valid_response") +async def test_step_destination_entity( + hass: HomeAssistant, origin_step_result: data_entry_flow.FlowResult +) -> None: + """Test the origin coordinates step.""" + menu_result = await hass.config_entries.flow.async_configure( + origin_step_result["flow_id"], {"next_step_id": "destination_entity"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + + entity_selector_result = await hass.config_entries.flow.async_configure( + menu_result["flow_id"], + {"destination_entity_id": "zone.home"}, + ) + assert entity_selector_result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.data == { + CONF_NAME: "test", + CONF_API_KEY: API_KEY, + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_ENTITY_ID: "zone.home", + CONF_MODE: TRAVEL_MODE_CAR, + } + + +async def test_form_invalid_auth(hass: HomeAssistant) -> None: + """Test we handle invalid auth.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "herepy.RoutingApi.public_transport_timetable", + side_effect=InvalidCredentialsError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_form_unknown_error(hass: HomeAssistant) -> None: + """Test we handle invalid auth.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "herepy.RoutingApi.public_transport_timetable", + side_effect=HEREError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "unknown"} + + +@pytest.mark.usefixtures("valid_response") +async def test_options_flow(hass: HomeAssistant) -> None: + """Test the options flow.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(entry.entry_id) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_MENU + + +@pytest.mark.usefixtures("valid_response") +async def test_options_flow_arrival_time_step( + hass: HomeAssistant, option_init_result: data_entry_flow.FlowResult +) -> None: + """Test the options flow arrival time type.""" + menu_result = await hass.config_entries.options.async_configure( + option_init_result["flow_id"], {"next_step_id": "arrival_time"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + time_selector_result = await hass.config_entries.options.async_configure( + option_init_result["flow_id"], + user_input={ + "arrival_time": "08:00:00", + }, + ) + + assert time_selector_result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.options == { + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ARRIVAL_TIME: "08:00:00", + } + + +@pytest.mark.usefixtures("valid_response") +async def test_options_flow_departure_time_step( + hass: HomeAssistant, option_init_result: data_entry_flow.FlowResult +) -> None: + """Test the options flow departure time type.""" + menu_result = await hass.config_entries.options.async_configure( + option_init_result["flow_id"], {"next_step_id": "departure_time"} + ) + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_FORM + time_selector_result = await hass.config_entries.options.async_configure( + option_init_result["flow_id"], + user_input={ + "departure_time": "08:00:00", + }, + ) + + assert time_selector_result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.options == { + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_DEPARTURE_TIME: "08:00:00", + } + + +@pytest.mark.usefixtures("valid_response") +async def test_options_flow_no_time_step( + hass: HomeAssistant, option_init_result: data_entry_flow.FlowResult +) -> None: + """Test the options flow arrival time type.""" + menu_result = await hass.config_entries.options.async_configure( + option_init_result["flow_id"], {"next_step_id": "no_time"} + ) + + assert menu_result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.options == { + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + } + + +@pytest.mark.usefixtures("valid_response") +async def test_import_flow_entity_id(hass: HomeAssistant) -> None: + """Test import_flow with entity ids.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_ENTITY_ID: "sensor.origin", + CONF_DESTINATION_ENTITY_ID: "sensor.destination", + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_DEPARTURE: "08:00:00", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "test_name" + + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.data == { + CONF_NAME: "test_name", + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_ENTITY_ID: "sensor.origin", + CONF_DESTINATION_ENTITY_ID: "sensor.destination", + CONF_MODE: TRAVEL_MODE_CAR, + } + assert entry.options == { + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_DEPARTURE_TIME: "08:00:00", + CONF_ARRIVAL_TIME: None, + } + + +@pytest.mark.usefixtures("valid_response") +async def test_import_flow_coordinates(hass: HomeAssistant) -> None: + """Test import_flow with coordinates.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:00", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "test_name" + + entry = hass.config_entries.async_entries(DOMAIN)[0] + assert entry.data == { + CONF_NAME: "test_name", + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_MODE: TRAVEL_MODE_CAR, + } + assert entry.options == { + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_DEPARTURE_TIME: None, + CONF_ARRIVAL_TIME: "08:00:00", + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + } + + +@pytest.mark.usefixtures("valid_response") +async def test_dupe_import(hass: HomeAssistant) -> None: + """Test duplicate import.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:00", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name2", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:00", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:01", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: "40.0", + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:01", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_IMPORT}, + data={ + CONF_API_KEY: CONF_API_KEY, + CONF_ORIGIN_LATITUDE: CAR_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE: CAR_ORIGIN_LONGITUDE, + CONF_DESTINATION_LATITUDE: CAR_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE: CAR_DESTINATION_LONGITUDE, + CONF_NAME: "test_name", + CONF_MODE: TRAVEL_MODE_CAR, + CONF_ARRIVAL: "08:00:00", + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + }, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" diff --git a/tests/components/here_travel_time/test_init.py b/tests/components/here_travel_time/test_init.py new file mode 100644 index 00000000000..02827acc4df --- /dev/null +++ b/tests/components/here_travel_time/test_init.py @@ -0,0 +1,48 @@ +"""The test for the HERE Travel Time integration.""" + +import pytest + +from homeassistant.components.here_travel_time.const import ( + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + DOMAIN, + TRAVEL_MODE_CAR, +) +from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME +from homeassistant.core import HomeAssistant + +from .const import ( + API_KEY, + CAR_DESTINATION_LATITUDE, + CAR_DESTINATION_LONGITUDE, + CAR_ORIGIN_LATITUDE, + CAR_ORIGIN_LONGITUDE, +) + +from tests.common import MockConfigEntry + + +@pytest.mark.usefixtures("valid_response") +async def test_unload_entry(hass: HomeAssistant) -> None: + """Test that unloading an entry works.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_CAR, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert await hass.config_entries.async_unload(entry.entry_id) + assert not hass.data[DOMAIN] diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 4ad2f757c77..585d5adc9d3 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -2,15 +2,10 @@ from unittest.mock import MagicMock, patch from herepy.here_enum import RouteMode -from herepy.routing_api import InvalidCredentialsError, NoRouteFoundError +from herepy.routing_api import NoRouteFoundError import pytest from homeassistant.components.here_travel_time.const import ( - ROUTE_MODE_FASTEST, - TRAFFIC_MODE_ENABLED, -) -from homeassistant.components.here_travel_time.sensor import ( - ATTR_ATTRIBUTION, ATTR_DESTINATION, ATTR_DESTINATION_NAME, ATTR_DISTANCE, @@ -19,16 +14,27 @@ from homeassistant.components.here_travel_time.sensor import ( ATTR_ORIGIN, ATTR_ORIGIN_NAME, ATTR_ROUTE, - CONF_MODE, + CONF_ARRIVAL_TIME, + CONF_DEPARTURE_TIME, + CONF_DESTINATION_ENTITY_ID, + CONF_DESTINATION_LATITUDE, + CONF_DESTINATION_LONGITUDE, + CONF_ORIGIN_ENTITY_ID, + CONF_ORIGIN_LATITUDE, + CONF_ORIGIN_LONGITUDE, + CONF_ROUTE_MODE, CONF_TRAFFIC_MODE, CONF_UNIT_SYSTEM, + DOMAIN, ICON_BICYCLE, ICON_CAR, ICON_PEDESTRIAN, ICON_PUBLIC, ICON_TRUCK, NO_ROUTE_ERROR_MESSAGE, - TIME_MINUTES, + ROUTE_MODE_FASTEST, + TRAFFIC_MODE_DISABLED, + TRAFFIC_MODE_ENABLED, TRAVEL_MODE_BICYCLE, TRAVEL_MODE_CAR, TRAVEL_MODE_PEDESTRIAN, @@ -36,11 +42,20 @@ from homeassistant.components.here_travel_time.sensor import ( TRAVEL_MODE_TRUCK, TRAVEL_MODES_VEHICLE, ) -from homeassistant.const import ATTR_ICON, EVENT_HOMEASSISTANT_START +from homeassistant.const import ( + ATTR_ATTRIBUTION, + ATTR_ICON, + CONF_API_KEY, + CONF_MODE, + CONF_NAME, + EVENT_HOMEASSISTANT_START, + TIME_MINUTES, +) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.components.here_travel_time.const import ( +from .const import ( API_KEY, CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE, @@ -48,21 +63,41 @@ from tests.components.here_travel_time.const import ( CAR_ORIGIN_LONGITUDE, ) -DOMAIN = "sensor" - -PLATFORM = "here_travel_time" +from tests.common import MockConfigEntry @pytest.mark.parametrize( - "mode,icon,traffic_mode,unit_system,expected_state,expected_distance,expected_duration_in_traffic", + "mode,icon,traffic_mode,unit_system,arrival_time,departure_time,expected_state,expected_distance,expected_duration_in_traffic", [ - (TRAVEL_MODE_CAR, ICON_CAR, True, "metric", "31", 23.903, 31.016666666666666), - (TRAVEL_MODE_BICYCLE, ICON_BICYCLE, False, "metric", "30", 23.903, 30.05), + ( + TRAVEL_MODE_CAR, + ICON_CAR, + TRAFFIC_MODE_ENABLED, + "metric", + None, + None, + "31", + 23.903, + 31.016666666666666, + ), + ( + TRAVEL_MODE_BICYCLE, + ICON_BICYCLE, + TRAFFIC_MODE_DISABLED, + "metric", + None, + None, + "30", + 23.903, + 30.05, + ), ( TRAVEL_MODE_PEDESTRIAN, ICON_PEDESTRIAN, - False, + TRAFFIC_MODE_DISABLED, "imperial", + None, + None, "30", 14.852635608048994, 30.05, @@ -70,8 +105,10 @@ PLATFORM = "here_travel_time" ( TRAVEL_MODE_PUBLIC_TIME_TABLE, ICON_PUBLIC, - False, + TRAFFIC_MODE_DISABLED, "imperial", + "08:00:00", + None, "30", 14.852635608048994, 30.05, @@ -79,41 +116,52 @@ PLATFORM = "here_travel_time" ( TRAVEL_MODE_TRUCK, ICON_TRUCK, - True, + TRAFFIC_MODE_ENABLED, "metric", + None, + "08:00:00", "31", 23.903, 31.016666666666666, ), ], ) +@pytest.mark.usefixtures("valid_response") async def test_sensor( - hass, + hass: HomeAssistant, mode, icon, traffic_mode, unit_system, + arrival_time, + departure_time, expected_state, expected_distance, expected_duration_in_traffic, - valid_response, ): """Test that sensor works.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - "traffic_mode": traffic_mode, - "unit_system": unit_system, - "mode": mode, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: mode, + CONF_NAME: "test", + }, + options={ + CONF_TRAFFIC_MODE: traffic_mode, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: arrival_time, + CONF_DEPARTURE_TIME: departure_time, + CONF_UNIT_SYSTEM: unit_system, + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() @@ -145,7 +193,9 @@ async def test_sensor( assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "22nd St NW" assert sensor.attributes.get(ATTR_DESTINATION_NAME) == "Service Rd S" assert sensor.attributes.get(CONF_MODE) == mode - assert sensor.attributes.get(CONF_TRAFFIC_MODE) is traffic_mode + assert sensor.attributes.get(CONF_TRAFFIC_MODE) is ( + traffic_mode == TRAFFIC_MODE_ENABLED + ) assert sensor.attributes.get(ATTR_ICON) == icon @@ -156,7 +206,63 @@ async def test_sensor( ) -async def test_entity_ids(hass, valid_response: MagicMock): +@pytest.mark.usefixtures("valid_response") +async def test_circular_ref(hass: HomeAssistant, caplog): + """Test that a circular ref is handled.""" + hass.states.async_set( + "test.first", + "test.second", + ) + hass.states.async_set("test.second", "test.first") + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_ENTITY_ID: "test.first", + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert "No coordinatnes found for test.first" in caplog.text + + +@pytest.mark.usefixtures("empty_attribution_response") +async def test_no_attribution(hass: HomeAssistant): + """Test that an empty attribution is handled.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + assert hass.states.get("sensor.test").attributes.get(ATTR_ATTRIBUTION) is None + + +async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): """Test that origin/destination supplied by entities works.""" utcnow = dt_util.utcnow() # Patching 'utcnow' to gain more control over the timed update. @@ -181,17 +287,19 @@ async def test_entity_ids(hass, valid_response: MagicMock): "longitude": float(CAR_DESTINATION_LONGITUDE), }, ) - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_entity_id": "zone.origin", - "destination_entity_id": "device_tracker.test", - "api_key": API_KEY, - "mode": TRAVEL_MODE_TRUCK, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_ENTITY_ID: "zone.origin", + CONF_DESTINATION_ENTITY_ID: "device_tracker.test", + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -214,20 +322,23 @@ async def test_entity_ids(hass, valid_response: MagicMock): ) -async def test_destination_entity_not_found(hass, caplog, valid_response: MagicMock): +@pytest.mark.usefixtures("valid_response") +async def test_destination_entity_not_found(hass: HomeAssistant, caplog): """Test that a not existing destination_entity_id is caught.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_entity_id": "device_tracker.test", - "api_key": API_KEY, - "mode": TRAVEL_MODE_TRUCK, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_ENTITY_ID: "device_tracker.test", + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -236,20 +347,23 @@ async def test_destination_entity_not_found(hass, caplog, valid_response: MagicM assert "device_tracker.test are not valid coordinates" in caplog.text -async def test_origin_entity_not_found(hass, caplog, valid_response: MagicMock): +@pytest.mark.usefixtures("valid_response") +async def test_origin_entity_not_found(hass: HomeAssistant, caplog): """Test that a not existing origin_entity_id is caught.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_entity_id": "device_tracker.test", - "destination_latitude": CAR_ORIGIN_LATITUDE, - "destination_longitude": CAR_ORIGIN_LONGITUDE, - "api_key": API_KEY, - "mode": TRAVEL_MODE_TRUCK, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_ENTITY_ID: "device_tracker.test", + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -258,26 +372,27 @@ async def test_origin_entity_not_found(hass, caplog, valid_response: MagicMock): assert "device_tracker.test are not valid coordinates" in caplog.text -async def test_invalid_destination_entity_state( - hass, caplog, valid_response: MagicMock -): +@pytest.mark.usefixtures("valid_response") +async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog): """Test that an invalid state of the destination_entity_id is caught.""" hass.states.async_set( "device_tracker.test", "test_state", ) - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_entity_id": "device_tracker.test", - "api_key": API_KEY, - "mode": TRAVEL_MODE_TRUCK, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_ENTITY_ID: "device_tracker.test", + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -286,24 +401,27 @@ async def test_invalid_destination_entity_state( assert "test_state are not valid coordinates" in caplog.text -async def test_invalid_origin_entity_state(hass, caplog, valid_response: MagicMock): +@pytest.mark.usefixtures("valid_response") +async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): """Test that an invalid state of the origin_entity_id is caught.""" hass.states.async_set( "device_tracker.test", "test_state", ) - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_entity_id": "device_tracker.test", - "destination_latitude": CAR_ORIGIN_LATITUDE, - "destination_longitude": CAR_ORIGIN_LONGITUDE, - "api_key": API_KEY, - "mode": TRAVEL_MODE_TRUCK, - } - } - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_ENTITY_ID: "device_tracker.test", + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) @@ -312,27 +430,30 @@ async def test_invalid_origin_entity_state(hass, caplog, valid_response: MagicMo assert "test_state are not valid coordinates" in caplog.text -async def test_route_not_found(hass, caplog): +async def test_route_not_found(hass: HomeAssistant, caplog): """Test that route not found error is correctly handled.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - } - } with patch( - "homeassistant.components.here_travel_time.sensor._are_valid_client_credentials", - return_value=True, + "homeassistant.components.here_travel_time.config_flow.validate_api_key", + return_value=None, ), patch( "herepy.RoutingApi.public_transport_timetable", side_effect=NoRouteFoundError, ): - assert await async_setup_component(hass, DOMAIN, config) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(CAR_ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(CAR_ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(CAR_DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(CAR_DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() @@ -340,113 +461,26 @@ async def test_route_not_found(hass, caplog): assert NO_ROUTE_ERROR_MESSAGE in caplog.text -async def test_invalid_credentials(hass, caplog): - """Test that invalid credentials error is correctly handled.""" +@pytest.mark.usefixtures("valid_response") +async def test_setup_platform(hass: HomeAssistant, caplog): + """Test that setup platform migration works.""" + config = { + "sensor": { + "platform": DOMAIN, + "name": "test", + "origin_latitude": CAR_ORIGIN_LATITUDE, + "origin_longitude": CAR_ORIGIN_LONGITUDE, + "destination_latitude": CAR_DESTINATION_LATITUDE, + "destination_longitude": CAR_DESTINATION_LONGITUDE, + "api_key": API_KEY, + } + } with patch( - "herepy.RoutingApi.public_transport_timetable", - side_effect=InvalidCredentialsError, + "homeassistant.components.here_travel_time.async_setup_entry", return_value=True ): - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - } - } - assert await async_setup_component(hass, DOMAIN, config) + await async_setup_component(hass, "sensor", config) await hass.async_block_till_done() - assert "Invalid credentials" in caplog.text - - -async def test_arrival(hass, valid_response): - """Test that arrival works.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - "mode": TRAVEL_MODE_PUBLIC_TIME_TABLE, - "arrival": "01:00:00", - } - } - assert await async_setup_component(hass, DOMAIN, config) - await hass.async_block_till_done() - - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() - - sensor = hass.states.get("sensor.test") - assert sensor.state == "30" - - -async def test_departure(hass, valid_response): - """Test that departure works.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - "mode": TRAVEL_MODE_PUBLIC_TIME_TABLE, - "departure": "23:00:00", - } - } - assert await async_setup_component(hass, DOMAIN, config) - await hass.async_block_till_done() - - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() - - sensor = hass.states.get("sensor.test") - assert sensor.state == "30" - - -async def test_arrival_only_allowed_for_timetable(hass, caplog): - """Test that arrival is only allowed when mode is publicTransportTimeTable.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - "arrival": "01:00:00", - } - } - assert await async_setup_component(hass, DOMAIN, config) - await hass.async_block_till_done() - assert "[arrival] is an invalid option" in caplog.text - - -async def test_exclusive_arrival_and_departure(hass, caplog): - """Test that arrival and departure are exclusive.""" - config = { - DOMAIN: { - "platform": PLATFORM, - "name": "test", - "origin_latitude": CAR_ORIGIN_LATITUDE, - "origin_longitude": CAR_ORIGIN_LONGITUDE, - "destination_latitude": CAR_DESTINATION_LATITUDE, - "destination_longitude": CAR_DESTINATION_LONGITUDE, - "api_key": API_KEY, - "arrival": "01:00:00", - "mode": TRAVEL_MODE_PUBLIC_TIME_TABLE, - "departure": "01:00:00", - } - } - assert await async_setup_component(hass, DOMAIN, config) - await hass.async_block_till_done() - assert "two or more values in the same group of exclusion" in caplog.text + assert ( + "Your HERE travel time configuration has been imported into the UI" + in caplog.text + ) From b38289ee507d164e15dc3021b6aaae6134534eba Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 21 May 2022 08:39:41 -0700 Subject: [PATCH 670/930] Remove google found_calendar service (#72260) * Remove google found_calendar service * Partial revert of yaml changes * Store calendar id in a local variable --- homeassistant/components/google/__init__.py | 32 +++++++++---------- homeassistant/components/google/services.yaml | 3 -- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 61b25a9c027..73b7f8d8ae6 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -10,7 +10,7 @@ from typing import Any import aiohttp from gcal_sync.api import GoogleCalendarService from gcal_sync.exceptions import ApiException -from gcal_sync.model import DateOrDatetime, Event +from gcal_sync.model import Calendar, DateOrDatetime, Event from oauth2client.file import Storage import voluptuous as vol from voluptuous.error import Error as VoluptuousError @@ -87,7 +87,6 @@ NOTIFICATION_TITLE = "Google Calendar Setup" GROUP_NAME_ALL_CALENDARS = "Google Calendar Sensors" SERVICE_SCAN_CALENDARS = "scan_for_calendars" -SERVICE_FOUND_CALENDARS = "found_calendar" SERVICE_ADD_EVENT = "add_event" YAML_DEVICES = f"{DOMAIN}_calendars.yaml" @@ -250,19 +249,19 @@ async def async_setup_services( ) -> None: """Set up the service listeners.""" - created_calendars = set() calendars = await hass.async_add_executor_job( load_config, hass.config.path(YAML_DEVICES) ) - async def _found_calendar(call: ServiceCall) -> None: - calendar = get_calendar_info(hass, call.data) - calendar_id = calendar[CONF_CAL_ID] - - if calendar_id in created_calendars: - return - created_calendars.add(calendar_id) - + async def _found_calendar(calendar_item: Calendar) -> None: + calendar = get_calendar_info( + hass, + { + **calendar_item.dict(exclude_unset=True), + CONF_TRACK: track_new, + }, + ) + calendar_id = calendar_item.id # Populate the yaml file with all discovered calendars if calendar_id not in calendars: calendars[calendar_id] = calendar @@ -274,7 +273,7 @@ async def async_setup_services( calendar = calendars[calendar_id] async_dispatcher_send(hass, DISCOVER_CALENDAR, calendar) - hass.services.async_register(DOMAIN, SERVICE_FOUND_CALENDARS, _found_calendar) + created_calendars = set() async def _scan_for_calendars(call: ServiceCall) -> None: """Scan for new calendars.""" @@ -284,11 +283,10 @@ async def async_setup_services( raise HomeAssistantError(str(err)) from err tasks = [] for calendar_item in result.items: - calendar = calendar_item.dict(exclude_unset=True) - calendar[CONF_TRACK] = track_new - tasks.append( - hass.services.async_call(DOMAIN, SERVICE_FOUND_CALENDARS, calendar) - ) + if calendar_item.id in created_calendars: + continue + created_calendars.add(calendar_item.id) + tasks.append(_found_calendar(calendar_item)) await asyncio.gather(*tasks) hass.services.async_register(DOMAIN, SERVICE_SCAN_CALENDARS, _scan_for_calendars) diff --git a/homeassistant/components/google/services.yaml b/homeassistant/components/google/services.yaml index ff053f1be33..21df763374f 100644 --- a/homeassistant/components/google/services.yaml +++ b/homeassistant/components/google/services.yaml @@ -1,6 +1,3 @@ -found_calendar: - name: Found Calendar - description: Add calendar if it has not been already discovered. scan_for_calendars: name: Scan for calendars description: Scan for new calendars. From 809808dd80055c4cd4abc08556f7bead41049efa Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 21 May 2022 19:04:36 +0200 Subject: [PATCH 671/930] Move manual configuration of MQTT camera to the integration key (#72249) Add camera Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/camera.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_camera.py | 14 ++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index f723d8da3ca..0cdba89f25e 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -193,6 +193,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.ALARM_CONTROL_PANEL.value): cv.ensure_list, vol.Optional(Platform.BINARY_SENSOR.value): cv.ensure_list, vol.Optional(Platform.BUTTON.value): cv.ensure_list, + vol.Optional(Platform.CAMERA.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, } diff --git a/homeassistant/components/mqtt/camera.py b/homeassistant/components/mqtt/camera.py index 176e8e86b6b..2e5d95ebda4 100644 --- a/homeassistant/components/mqtt/camera.py +++ b/homeassistant/components/mqtt/camera.py @@ -1,6 +1,7 @@ """Camera that loads a picture from an MQTT topic.""" from __future__ import annotations +import asyncio from base64 import b64decode import functools @@ -22,8 +23,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) DEFAULT_NAME = "MQTT Camera" @@ -37,14 +40,20 @@ MQTT_CAMERA_ATTRIBUTES_BLOCKED = frozenset( } ) -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_TOPIC): mqtt.valid_subscribe_topic, } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Camera under the camera platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(camera.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -53,7 +62,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT camera through configuration.yaml.""" + """Set up MQTT camera configured under the camera platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, camera.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -64,7 +74,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT camera dynamically through MQTT discovery.""" + """Set up MQTT camera through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, camera.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_camera.py b/tests/components/mqtt/test_camera.py index 48f73a504bf..204103152a7 100644 --- a/tests/components/mqtt/test_camera.py +++ b/tests/components/mqtt/test_camera.py @@ -1,5 +1,6 @@ """The tests for mqtt camera component.""" from base64 import b64encode +import copy from http import HTTPStatus import json from unittest.mock import patch @@ -32,6 +33,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -289,3 +291,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = camera.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = camera.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 17669a728c5817392557436b586147773b445346 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sat, 21 May 2022 19:07:26 +0200 Subject: [PATCH 672/930] Move manual configuration of MQTT lock to the integration key (#72271) Add lock --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/lock.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_lock.py | 14 ++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 0cdba89f25e..dfef057a7f9 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -196,6 +196,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.CAMERA.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, + vol.Optional(Platform.LOCK.value): cv.ensure_list, } ) diff --git a/homeassistant/components/mqtt/lock.py b/homeassistant/components/mqtt/lock.py index 66efe9fece7..5dc0a974d26 100644 --- a/homeassistant/components/mqtt/lock.py +++ b/homeassistant/components/mqtt/lock.py @@ -1,6 +1,7 @@ """Support for MQTT locks.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -27,8 +28,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) CONF_PAYLOAD_LOCK = "payload_lock" @@ -53,7 +56,7 @@ MQTT_LOCK_ATTRIBUTES_BLOCKED = frozenset( } ) -PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -66,7 +69,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Locks under the lock platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(lock.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -75,7 +84,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT lock panel through configuration.yaml.""" + """Set up MQTT locks configured under the lock platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, lock.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -86,7 +96,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT lock dynamically through MQTT discovery.""" + """Set up MQTT lock through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, lock.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_lock.py b/tests/components/mqtt/test_lock.py index 86e21a261a3..ef752ef8749 100644 --- a/tests/components/mqtt/test_lock.py +++ b/tests/components/mqtt/test_lock.py @@ -1,4 +1,5 @@ """The tests for the MQTT lock platform.""" +import copy from unittest.mock import patch import pytest @@ -44,6 +45,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -675,3 +677,15 @@ async def test_encoding_subscribable_topics( attribute, attribute_value, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = LOCK_DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 3f8c896cb20526ef59e285b1f0eeb9b4e734efee Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 21 May 2022 11:22:27 -0700 Subject: [PATCH 673/930] Set user friendly name for Google Calendar config entry (#72243) * Set user friendly name for Google Calendar config entry * Add a new auth implementation for use during the config flow --- homeassistant/components/google/api.py | 22 +++++++ .../components/google/config_flow.py | 18 ++++- tests/components/google/conftest.py | 30 +++++++-- tests/components/google/test_config_flow.py | 66 ++++++++++++++++++- 4 files changed, 128 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index dceeb6ba6a4..3ce21fbb03d 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -173,3 +173,25 @@ class ApiAuthImpl(AbstractAuth): """Return a valid access token.""" await self._session.async_ensure_token_valid() return self._session.token["access_token"] + + +class AccessTokenAuthImpl(AbstractAuth): + """Authentication implementation used during config flow, without refresh. + + This exists to allow the config flow to use the API before it has fully + created a config entry required by OAuth2Session. This does not support + refreshing tokens, which is fine since it should have been just created. + """ + + def __init__( + self, + websession: aiohttp.ClientSession, + access_token: str, + ) -> None: + """Init the Google Calendar client library auth implementation.""" + super().__init__(websession) + self._access_token = access_token + + async def async_get_access_token(self) -> str: + """Return the access token.""" + return self._access_token diff --git a/homeassistant/components/google/config_flow.py b/homeassistant/components/google/config_flow.py index f5e567a5272..3b513f17197 100644 --- a/homeassistant/components/google/config_flow.py +++ b/homeassistant/components/google/config_flow.py @@ -4,13 +4,17 @@ from __future__ import annotations import logging from typing import Any +from gcal_sync.api import GoogleCalendarService +from gcal_sync.exceptions import ApiException from oauth2client.client import Credentials from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_entry_oauth2_flow +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .api import ( DEVICE_AUTH_CREDS, + AccessTokenAuthImpl, DeviceAuth, DeviceFlow, OAuthError, @@ -130,7 +134,19 @@ class OAuth2FlowHandler( self.hass.config_entries.async_update_entry(entry, data=data) await self.hass.config_entries.async_reload(entry.entry_id) return self.async_abort(reason="reauth_successful") - return self.async_create_entry(title=self.flow_impl.name, data=data) + + calendar_service = GoogleCalendarService( + AccessTokenAuthImpl( + async_get_clientsession(self.hass), data["token"]["access_token"] + ) + ) + try: + primary_calendar = await calendar_service.async_get_calendar("primary") + except ApiException as err: + _LOGGER.debug("Error reading calendar primary calendar: %s", err) + primary_calendar = None + title = primary_calendar.id if primary_calendar else self.flow_impl.name + return self.async_create_entry(title=title, data=data) async def async_step_reauth( self, user_input: dict[str, Any] | None = None diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index f48996de72a..48badbc3ab5 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -6,6 +6,7 @@ import datetime from typing import Any, Generator, TypeVar from unittest.mock import mock_open, patch +from aiohttp.client_exceptions import ClientError from gcal_sync.auth import API_BASE_URL from oauth2client.client import Credentials, OAuth2Credentials import pytest @@ -207,7 +208,9 @@ def mock_events_list( """Fixture to construct a fake event list API response.""" def _put_result( - response: dict[str, Any], calendar_id: str = None, exc: Exception = None + response: dict[str, Any], + calendar_id: str = None, + exc: ClientError | None = None, ) -> None: if calendar_id is None: calendar_id = CALENDAR_ID @@ -240,7 +243,7 @@ def mock_calendars_list( ) -> ApiResult: """Fixture to construct a fake calendar list API response.""" - def _put_result(response: dict[str, Any], exc=None) -> None: + def _result(response: dict[str, Any], exc: ClientError | None = None) -> None: aioclient_mock.get( f"{API_BASE_URL}/users/me/calendarList", json=response, @@ -248,13 +251,32 @@ def mock_calendars_list( ) return - return _put_result + return _result + + +@pytest.fixture +def mock_calendar_get( + aioclient_mock: AiohttpClientMocker, +) -> Callable[[...], None]: + """Fixture for returning a calendar get response.""" + + def _result( + calendar_id: str, response: dict[str, Any], exc: ClientError | None = None + ) -> None: + aioclient_mock.get( + f"{API_BASE_URL}/calendars/{calendar_id}", + json=response, + exc=exc, + ) + return + + return _result @pytest.fixture def mock_insert_event( aioclient_mock: AiohttpClientMocker, -) -> Callable[[..., dict[str, Any]], None]: +) -> Callable[[...], None]: """Fixture for capturing event creation.""" def _expect_result(calendar_id: str = CALENDAR_ID) -> None: diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 88a090773bc..625d1faa937 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -1,9 +1,13 @@ """Test the google config flow.""" +from __future__ import annotations + +from collections.abc import Callable import datetime from typing import Any from unittest.mock import Mock, patch +from aiohttp.client_exceptions import ClientError from oauth2client.client import ( FlowExchangeError, OAuth2Credentials, @@ -27,6 +31,7 @@ from tests.common import MockConfigEntry, async_fire_time_changed CODE_CHECK_INTERVAL = 1 CODE_CHECK_ALARM_TIMEDELTA = datetime.timedelta(seconds=CODE_CHECK_INTERVAL * 2) +EMAIL_ADDRESS = "user@gmail.com" @pytest.fixture(autouse=True) @@ -63,6 +68,24 @@ async def mock_exchange(creds: OAuth2Credentials) -> YieldFixture[Mock]: yield mock +@pytest.fixture +async def primary_calendar_error() -> ClientError | None: + """Fixture for tests to inject an error during calendar lookup.""" + return None + + +@pytest.fixture(autouse=True) +async def primary_calendar( + mock_calendar_get: Callable[[...], None], primary_calendar_error: ClientError | None +) -> None: + """Fixture to return the primary calendar.""" + mock_calendar_get( + "primary", + {"id": EMAIL_ADDRESS, "summary": "Personal"}, + exc=primary_calendar_error, + ) + + async def fire_alarm(hass, point_in_time): """Fire an alarm and wait for callbacks to run.""" with patch("homeassistant.util.dt.utcnow", return_value=point_in_time): @@ -99,7 +122,7 @@ async def test_full_flow_yaml_creds( ) assert result.get("type") == "create_entry" - assert result.get("title") == "Import from configuration.yaml" + assert result.get("title") == EMAIL_ADDRESS assert "data" in result data = result["data"] assert "token" in data @@ -161,7 +184,7 @@ async def test_full_flow_application_creds( ) assert result.get("type") == "create_entry" - assert result.get("title") == "Import from configuration.yaml" + assert result.get("title") == EMAIL_ADDRESS assert "data" in result data = result["data"] assert "token" in data @@ -278,7 +301,7 @@ async def test_exchange_error( ) assert result.get("type") == "create_entry" - assert result.get("title") == "Import from configuration.yaml" + assert result.get("title") == EMAIL_ADDRESS assert "data" in result data = result["data"] assert "token" in data @@ -463,3 +486,40 @@ async def test_reauth_flow( } assert len(mock_setup.mock_calls) == 1 + + +@pytest.mark.parametrize("primary_calendar_error", [ClientError()]) +async def test_title_lookup_failure( + hass: HomeAssistant, + mock_code_flow: Mock, + mock_exchange: Mock, + component_setup: ComponentSetup, +) -> None: + """Test successful config flow and title fetch fails gracefully.""" + assert await component_setup() + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result.get("type") == "progress" + assert result.get("step_id") == "auth" + assert "description_placeholders" in result + assert "url" in result["description_placeholders"] + + with patch( + "homeassistant.components.google.async_setup_entry", return_value=True + ) as mock_setup: + # Run one tick to invoke the credential exchange check + now = utcnow() + await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA) + await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + flow_id=result["flow_id"] + ) + + assert result.get("type") == "create_entry" + assert result.get("title") == "Import from configuration.yaml" + + assert len(mock_setup.mock_calls) == 1 + entries = hass.config_entries.async_entries(DOMAIN) + assert len(entries) == 1 From abf9aab18f9a6953b49c4f8aee1ca7e560911e36 Mon Sep 17 00:00:00 2001 From: xLarry Date: Sat, 21 May 2022 21:18:01 +0200 Subject: [PATCH 674/930] Add laundrify integration (#65090) * First version of laundrify integration * Code cleanup * Code cleanup after review #2 * Move coordinator to its own file * Save devices as dict and implement available prop as fn * Validate token on init, abort if already configured * Some more cleanup after review * Add strict type hints * Minor changes after code review * Remove OptionsFlow (use default poll interval instead) * Fix CODEOWNERS to pass hassfest job * Fix formatting to pass prettier job * Fix mypy typing error * Update internal device property after fetching data * Call parental update handler and remove obsolete code * Add coordinator tests and fix some config flow tests * Refactor tests * Refactor fixtures * Device unavailable if polling fails --- .strict-typing | 1 + CODEOWNERS | 2 + .../components/laundrify/__init__.py | 52 +++++++ .../components/laundrify/binary_sensor.py | 84 ++++++++++++ .../components/laundrify/config_flow.py | 94 +++++++++++++ homeassistant/components/laundrify/const.py | 10 ++ .../components/laundrify/coordinator.py | 46 +++++++ .../components/laundrify/manifest.json | 9 ++ homeassistant/components/laundrify/model.py | 13 ++ .../components/laundrify/strings.json | 25 ++++ .../components/laundrify/translations/en.json | 25 ++++ homeassistant/generated/config_flows.py | 1 + mypy.ini | 11 ++ requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/laundrify/__init__.py | 22 +++ tests/components/laundrify/conftest.py | 51 +++++++ tests/components/laundrify/const.py | 11 ++ .../laundrify/fixtures/machines.json | 8 ++ .../components/laundrify/test_config_flow.py | 129 ++++++++++++++++++ .../components/laundrify/test_coordinator.py | 50 +++++++ tests/components/laundrify/test_init.py | 59 ++++++++ 22 files changed, 709 insertions(+) create mode 100644 homeassistant/components/laundrify/__init__.py create mode 100644 homeassistant/components/laundrify/binary_sensor.py create mode 100644 homeassistant/components/laundrify/config_flow.py create mode 100644 homeassistant/components/laundrify/const.py create mode 100644 homeassistant/components/laundrify/coordinator.py create mode 100644 homeassistant/components/laundrify/manifest.json create mode 100644 homeassistant/components/laundrify/model.py create mode 100644 homeassistant/components/laundrify/strings.json create mode 100644 homeassistant/components/laundrify/translations/en.json create mode 100644 tests/components/laundrify/__init__.py create mode 100644 tests/components/laundrify/conftest.py create mode 100644 tests/components/laundrify/const.py create mode 100644 tests/components/laundrify/fixtures/machines.json create mode 100644 tests/components/laundrify/test_config_flow.py create mode 100644 tests/components/laundrify/test_coordinator.py create mode 100644 tests/components/laundrify/test_init.py diff --git a/.strict-typing b/.strict-typing index 4ec4fbff5ee..563e6e9f91a 100644 --- a/.strict-typing +++ b/.strict-typing @@ -138,6 +138,7 @@ homeassistant.components.kaleidescape.* homeassistant.components.knx.* homeassistant.components.kraken.* homeassistant.components.lametric.* +homeassistant.components.laundrify.* homeassistant.components.lcn.* homeassistant.components.light.* homeassistant.components.local_ip.* diff --git a/CODEOWNERS b/CODEOWNERS index 4e1dcd5d93e..05e0d0b4377 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -556,6 +556,8 @@ build.json @home-assistant/supervisor /homeassistant/components/lametric/ @robbiet480 @frenck /homeassistant/components/launch_library/ @ludeeus @DurgNomis-drol /tests/components/launch_library/ @ludeeus @DurgNomis-drol +/homeassistant/components/laundrify/ @xLarry +/tests/components/laundrify/ @xLarry /homeassistant/components/lcn/ @alengwenus /tests/components/lcn/ @alengwenus /homeassistant/components/lg_netcast/ @Drafteed diff --git a/homeassistant/components/laundrify/__init__.py b/homeassistant/components/laundrify/__init__.py new file mode 100644 index 00000000000..27fc412abab --- /dev/null +++ b/homeassistant/components/laundrify/__init__.py @@ -0,0 +1,52 @@ +"""The laundrify integration.""" +from __future__ import annotations + +from laundrify_aio import LaundrifyAPI +from laundrify_aio.exceptions import ApiConnectionException, UnauthorizedException + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_ACCESS_TOKEN, Platform +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DEFAULT_POLL_INTERVAL, DOMAIN +from .coordinator import LaundrifyUpdateCoordinator + +PLATFORMS = [Platform.BINARY_SENSOR] + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up laundrify from a config entry.""" + + session = async_get_clientsession(hass) + api_client = LaundrifyAPI(entry.data[CONF_ACCESS_TOKEN], session) + + try: + await api_client.validate_token() + except UnauthorizedException as err: + raise ConfigEntryAuthFailed("Invalid authentication") from err + except ApiConnectionException as err: + raise ConfigEntryNotReady("Cannot reach laundrify API") from err + + coordinator = LaundrifyUpdateCoordinator(hass, api_client, DEFAULT_POLL_INTERVAL) + + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { + "api": api_client, + "coordinator": coordinator, + } + + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/laundrify/binary_sensor.py b/homeassistant/components/laundrify/binary_sensor.py new file mode 100644 index 00000000000..2f64d8cee78 --- /dev/null +++ b/homeassistant/components/laundrify/binary_sensor.py @@ -0,0 +1,84 @@ +"""Platform for binary sensor integration.""" +from __future__ import annotations + +import logging + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN, MANUFACTURER, MODEL +from .coordinator import LaundrifyUpdateCoordinator +from .model import LaundrifyDevice + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, config: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up sensors from a config entry created in the integrations UI.""" + + coordinator = hass.data[DOMAIN][config.entry_id]["coordinator"] + + async_add_entities( + LaundrifyPowerPlug(coordinator, device) for device in coordinator.data.values() + ) + + +class LaundrifyPowerPlug( + CoordinatorEntity[LaundrifyUpdateCoordinator], BinarySensorEntity +): + """Representation of a laundrify Power Plug.""" + + _attr_device_class = BinarySensorDeviceClass.RUNNING + _attr_icon = "mdi:washing-machine" + + def __init__( + self, coordinator: LaundrifyUpdateCoordinator, device: LaundrifyDevice + ) -> None: + """Pass coordinator to CoordinatorEntity.""" + super().__init__(coordinator) + self._device = device + self._attr_unique_id = device["_id"] + + @property + def device_info(self) -> DeviceInfo: + """Configure the Device of this Entity.""" + return DeviceInfo( + identifiers={(DOMAIN, self._device["_id"])}, + name=self.name, + manufacturer=MANUFACTURER, + model=MODEL, + sw_version=self._device["firmwareVersion"], + ) + + @property + def available(self) -> bool: + """Check if the device is available.""" + return ( + self.unique_id in self.coordinator.data + and self.coordinator.last_update_success + ) + + @property + def name(self) -> str: + """Name of the entity.""" + return self._device["name"] + + @property + def is_on(self) -> bool: + """Return entity state.""" + return self._device["status"] == "ON" + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._device = self.coordinator.data[self.unique_id] + super()._handle_coordinator_update() diff --git a/homeassistant/components/laundrify/config_flow.py b/homeassistant/components/laundrify/config_flow.py new file mode 100644 index 00000000000..d8230863d7c --- /dev/null +++ b/homeassistant/components/laundrify/config_flow.py @@ -0,0 +1,94 @@ +"""Config flow for laundrify integration.""" +from __future__ import annotations + +import logging +from typing import Any + +from laundrify_aio import LaundrifyAPI +from laundrify_aio.exceptions import ( + ApiConnectionException, + InvalidFormat, + UnknownAuthCode, +) +from voluptuous import Required, Schema + +from homeassistant.config_entries import ConfigFlow +from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CODE +from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = Schema({Required(CONF_CODE): str}) + + +class LaundrifyConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for laundrify.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle a flow initialized by the user.""" + return await self.async_step_init(user_input) + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + if user_input is None: + return self.async_show_form(step_id="init", data_schema=CONFIG_SCHEMA) + + errors = {} + + try: + access_token = await LaundrifyAPI.exchange_auth_code(user_input[CONF_CODE]) + + session = async_get_clientsession(self.hass) + api_client = LaundrifyAPI(access_token, session) + + account_id = await api_client.get_account_id() + except InvalidFormat: + errors[CONF_CODE] = "invalid_format" + except UnknownAuthCode: + errors[CONF_CODE] = "invalid_auth" + except ApiConnectionException: + errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + entry_data = {CONF_ACCESS_TOKEN: access_token} + + await self.async_set_unique_id(account_id) + self._abort_if_unique_id_configured() + + # Create a new entry if it doesn't exist + return self.async_create_entry( + title=DOMAIN, + data=entry_data, + ) + + return self.async_show_form( + step_id="init", data_schema=CONFIG_SCHEMA, errors=errors + ) + + async def async_step_reauth( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Perform reauth upon an API authentication error.""" + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Dialog that informs the user that reauth is required.""" + if user_input is None: + return self.async_show_form( + step_id="reauth_confirm", + data_schema=Schema({}), + ) + return await self.async_step_init() diff --git a/homeassistant/components/laundrify/const.py b/homeassistant/components/laundrify/const.py new file mode 100644 index 00000000000..c312b895234 --- /dev/null +++ b/homeassistant/components/laundrify/const.py @@ -0,0 +1,10 @@ +"""Constants for the laundrify integration.""" + +DOMAIN = "laundrify" + +MANUFACTURER = "laundrify" +MODEL = "WLAN-Adapter (SU02)" + +DEFAULT_POLL_INTERVAL = 60 + +REQUEST_TIMEOUT = 10 diff --git a/homeassistant/components/laundrify/coordinator.py b/homeassistant/components/laundrify/coordinator.py new file mode 100644 index 00000000000..31447490506 --- /dev/null +++ b/homeassistant/components/laundrify/coordinator.py @@ -0,0 +1,46 @@ +"""Custom DataUpdateCoordinator for the laundrify integration.""" +from datetime import timedelta +import logging + +import async_timeout +from laundrify_aio import LaundrifyAPI +from laundrify_aio.exceptions import ApiConnectionException, UnauthorizedException + +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DOMAIN, REQUEST_TIMEOUT +from .model import LaundrifyDevice + +_LOGGER = logging.getLogger(__name__) + + +class LaundrifyUpdateCoordinator(DataUpdateCoordinator): + """Class to manage fetching laundrify API data.""" + + def __init__( + self, hass: HomeAssistant, laundrify_api: LaundrifyAPI, poll_interval: int + ) -> None: + """Initialize laundrify coordinator.""" + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=timedelta(seconds=poll_interval), + ) + self.laundrify_api = laundrify_api + + async def _async_update_data(self) -> dict[str, LaundrifyDevice]: + """Fetch data from laundrify API.""" + try: + # Note: asyncio.TimeoutError and aiohttp.ClientError are already + # handled by the data update coordinator. + async with async_timeout.timeout(REQUEST_TIMEOUT): + return {m["_id"]: m for m in await self.laundrify_api.get_machines()} + except UnauthorizedException as err: + # Raising ConfigEntryAuthFailed will cancel future updates + # and start a config flow with SOURCE_REAUTH (async_step_reauth) + raise ConfigEntryAuthFailed from err + except ApiConnectionException as err: + raise UpdateFailed(f"Error communicating with API: {err}") from err diff --git a/homeassistant/components/laundrify/manifest.json b/homeassistant/components/laundrify/manifest.json new file mode 100644 index 00000000000..6a61446d31c --- /dev/null +++ b/homeassistant/components/laundrify/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "laundrify", + "name": "laundrify", + "config_flow": true, + "documentation": "https://www.home-assistant.io/integrations/laundrify", + "requirements": ["laundrify_aio==1.1.1"], + "codeowners": ["@xLarry"], + "iot_class": "cloud_polling" +} diff --git a/homeassistant/components/laundrify/model.py b/homeassistant/components/laundrify/model.py new file mode 100644 index 00000000000..aa6bf77509f --- /dev/null +++ b/homeassistant/components/laundrify/model.py @@ -0,0 +1,13 @@ +"""Models for laundrify platform.""" +from __future__ import annotations + +from typing import TypedDict + + +class LaundrifyDevice(TypedDict): + """laundrify Power Plug.""" + + _id: str + name: str + status: str + firmwareVersion: str diff --git a/homeassistant/components/laundrify/strings.json b/homeassistant/components/laundrify/strings.json new file mode 100644 index 00000000000..b2fea9f307f --- /dev/null +++ b/homeassistant/components/laundrify/strings.json @@ -0,0 +1,25 @@ +{ + "config": { + "step": { + "init": { + "description": "Please enter your personal Auth Code that is shown in the laundrify-App.", + "data": { + "code": "Auth Code (xxx-xxx)" + } + }, + "reauth_confirm": { + "title": "[%key:common::config_flow::title::reauth%]", + "description": "The laundrify integration needs to re-authenticate." + } + }, + "error": { + "invalid_format": "Invalid format. Please specify as xxx-xxx.", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" + } + } +} diff --git a/homeassistant/components/laundrify/translations/en.json b/homeassistant/components/laundrify/translations/en.json new file mode 100644 index 00000000000..2bfb07d4041 --- /dev/null +++ b/homeassistant/components/laundrify/translations/en.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible." + }, + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "invalid_format": "Invalid format. Please specify as xxx-xxx.", + "unknown": "Unexpected error" + }, + "step": { + "init": { + "data": { + "code": "Auth Code (xxx-xxx)" + }, + "description": "Please enter your personal AuthCode that is shown in the laundrify-App." + }, + "reauth_confirm": { + "description": "The laundrify integration needs to re-authenticate.", + "title": "Reauthenticate Integration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 67299f403c2..b0bbd3e1b36 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -187,6 +187,7 @@ FLOWS = { "kraken", "kulersky", "launch_library", + "laundrify", "life360", "lifx", "litejet", diff --git a/mypy.ini b/mypy.ini index 8f0b1868bce..ed073141fe1 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1281,6 +1281,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.laundrify.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.lcn.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index 4c88b57b9f4..ab4a232e864 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -926,6 +926,9 @@ krakenex==2.1.0 # homeassistant.components.eufy lakeside==0.12 +# homeassistant.components.laundrify +laundrify_aio==1.1.1 + # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ca6b5fd1628..5159abb4f43 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -654,6 +654,9 @@ kostal_plenticore==0.2.0 # homeassistant.components.kraken krakenex==2.1.0 +# homeassistant.components.laundrify +laundrify_aio==1.1.1 + # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/tests/components/laundrify/__init__.py b/tests/components/laundrify/__init__.py new file mode 100644 index 00000000000..c09c6290adf --- /dev/null +++ b/tests/components/laundrify/__init__.py @@ -0,0 +1,22 @@ +"""Tests for the laundrify integration.""" + +from homeassistant.components.laundrify import DOMAIN +from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.core import HomeAssistant + +from .const import VALID_ACCESS_TOKEN, VALID_ACCOUNT_ID + +from tests.common import MockConfigEntry + + +def create_entry( + hass: HomeAssistant, access_token: str = VALID_ACCESS_TOKEN +) -> MockConfigEntry: + """Create laundrify entry in Home Assistant.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=VALID_ACCOUNT_ID, + data={CONF_ACCESS_TOKEN: access_token}, + ) + entry.add_to_hass(hass) + return entry diff --git a/tests/components/laundrify/conftest.py b/tests/components/laundrify/conftest.py new file mode 100644 index 00000000000..13e408b61a9 --- /dev/null +++ b/tests/components/laundrify/conftest.py @@ -0,0 +1,51 @@ +"""Configure py.test.""" +import json +from unittest.mock import patch + +import pytest + +from .const import VALID_ACCESS_TOKEN, VALID_ACCOUNT_ID + +from tests.common import load_fixture + + +@pytest.fixture(name="laundrify_setup_entry") +def laundrify_setup_entry_fixture(): + """Mock laundrify setup entry function.""" + with patch( + "homeassistant.components.laundrify.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + +@pytest.fixture(name="laundrify_exchange_code") +def laundrify_exchange_code_fixture(): + """Mock laundrify exchange_auth_code function.""" + with patch( + "laundrify_aio.LaundrifyAPI.exchange_auth_code", + return_value=VALID_ACCESS_TOKEN, + ) as exchange_code_mock: + yield exchange_code_mock + + +@pytest.fixture(name="laundrify_validate_token") +def laundrify_validate_token_fixture(): + """Mock laundrify validate_token function.""" + with patch( + "laundrify_aio.LaundrifyAPI.validate_token", + return_value=True, + ) as validate_token_mock: + yield validate_token_mock + + +@pytest.fixture(name="laundrify_api_mock", autouse=True) +def laundrify_api_fixture(laundrify_exchange_code, laundrify_validate_token): + """Mock valid laundrify API responses.""" + with patch( + "laundrify_aio.LaundrifyAPI.get_account_id", + return_value=VALID_ACCOUNT_ID, + ), patch( + "laundrify_aio.LaundrifyAPI.get_machines", + return_value=json.loads(load_fixture("laundrify/machines.json")), + ) as get_machines_mock: + yield get_machines_mock diff --git a/tests/components/laundrify/const.py b/tests/components/laundrify/const.py new file mode 100644 index 00000000000..644631917c6 --- /dev/null +++ b/tests/components/laundrify/const.py @@ -0,0 +1,11 @@ +"""Constants for the laundrify tests.""" + +from homeassistant.const import CONF_CODE + +VALID_AUTH_CODE = "999-001" +VALID_ACCESS_TOKEN = "validAccessToken1234" +VALID_ACCOUNT_ID = "1234" + +VALID_USER_INPUT = { + CONF_CODE: VALID_AUTH_CODE, +} diff --git a/tests/components/laundrify/fixtures/machines.json b/tests/components/laundrify/fixtures/machines.json new file mode 100644 index 00000000000..ab1a737cb45 --- /dev/null +++ b/tests/components/laundrify/fixtures/machines.json @@ -0,0 +1,8 @@ +[ + { + "_id": "14", + "name": "Demo Waschmaschine", + "status": "OFF", + "firmwareVersion": "2.1.0" + } +] diff --git a/tests/components/laundrify/test_config_flow.py b/tests/components/laundrify/test_config_flow.py new file mode 100644 index 00000000000..5ee3efe1e45 --- /dev/null +++ b/tests/components/laundrify/test_config_flow.py @@ -0,0 +1,129 @@ +"""Test the laundrify config flow.""" + +from laundrify_aio import exceptions + +from homeassistant.components.laundrify.const import DOMAIN +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER +from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CODE, CONF_SOURCE +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) + +from . import create_entry +from .const import VALID_ACCESS_TOKEN, VALID_AUTH_CODE, VALID_USER_INPUT + + +async def test_form(hass: HomeAssistant, laundrify_setup_entry) -> None: + """Test we get the form.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] is None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input=VALID_USER_INPUT, + ) + await hass.async_block_till_done() + + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == DOMAIN + assert result["data"] == { + CONF_ACCESS_TOKEN: VALID_ACCESS_TOKEN, + } + assert len(laundrify_setup_entry.mock_calls) == 1 + + +async def test_form_invalid_format( + hass: HomeAssistant, laundrify_exchange_code +) -> None: + """Test we handle invalid format.""" + laundrify_exchange_code.side_effect = exceptions.InvalidFormat + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={CONF_SOURCE: SOURCE_USER}, + data={CONF_CODE: "invalidFormat"}, + ) + + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {CONF_CODE: "invalid_format"} + + +async def test_form_invalid_auth(hass: HomeAssistant, laundrify_exchange_code) -> None: + """Test we handle invalid auth.""" + laundrify_exchange_code.side_effect = exceptions.UnknownAuthCode + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={CONF_SOURCE: SOURCE_USER}, + data=VALID_USER_INPUT, + ) + + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {CONF_CODE: "invalid_auth"} + + +async def test_form_cannot_connect(hass: HomeAssistant, laundrify_exchange_code): + """Test we handle cannot connect error.""" + laundrify_exchange_code.side_effect = exceptions.ApiConnectionException + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={CONF_SOURCE: SOURCE_USER}, + data=VALID_USER_INPUT, + ) + + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {"base": "cannot_connect"} + + +async def test_form_unkown_exception(hass: HomeAssistant, laundrify_exchange_code): + """Test we handle all other errors.""" + laundrify_exchange_code.side_effect = Exception + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={CONF_SOURCE: SOURCE_USER}, + data=VALID_USER_INPUT, + ) + + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {"base": "unknown"} + + +async def test_step_reauth(hass: HomeAssistant) -> None: + """Test the reauth form is shown.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_REAUTH} + ) + + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] is None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result["type"] == RESULT_TYPE_FORM + + +async def test_integration_already_exists(hass: HomeAssistant): + """Test we only allow a single config flow.""" + create_entry(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={CONF_SOURCE: SOURCE_USER} + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_CODE: VALID_AUTH_CODE, + }, + ) + + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" diff --git a/tests/components/laundrify/test_coordinator.py b/tests/components/laundrify/test_coordinator.py new file mode 100644 index 00000000000..58bc297cc42 --- /dev/null +++ b/tests/components/laundrify/test_coordinator.py @@ -0,0 +1,50 @@ +"""Test the laundrify coordinator.""" + +from laundrify_aio import exceptions + +from homeassistant.components.laundrify.const import DOMAIN +from homeassistant.core import HomeAssistant + +from . import create_entry + + +async def test_coordinator_update_success(hass: HomeAssistant): + """Test the coordinator update is performed successfully.""" + config_entry = create_entry(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert coordinator.last_update_success + + +async def test_coordinator_update_unauthorized(hass: HomeAssistant, laundrify_api_mock): + """Test the coordinator update fails if an UnauthorizedException is thrown.""" + config_entry = create_entry(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + laundrify_api_mock.side_effect = exceptions.UnauthorizedException + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert not coordinator.last_update_success + + +async def test_coordinator_update_connection_failed( + hass: HomeAssistant, laundrify_api_mock +): + """Test the coordinator update fails if an ApiConnectionException is thrown.""" + config_entry = create_entry(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"] + laundrify_api_mock.side_effect = exceptions.ApiConnectionException + await coordinator.async_refresh() + await hass.async_block_till_done() + + assert not coordinator.last_update_success diff --git a/tests/components/laundrify/test_init.py b/tests/components/laundrify/test_init.py new file mode 100644 index 00000000000..129848e8808 --- /dev/null +++ b/tests/components/laundrify/test_init.py @@ -0,0 +1,59 @@ +"""Test the laundrify init file.""" + +from laundrify_aio import exceptions + +from homeassistant.components.laundrify.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant + +from . import create_entry + + +async def test_setup_entry_api_unauthorized( + hass: HomeAssistant, laundrify_validate_token +): + """Test that ConfigEntryAuthFailed is thrown when authentication fails.""" + laundrify_validate_token.side_effect = exceptions.UnauthorizedException + config_entry = create_entry(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert config_entry.state == ConfigEntryState.SETUP_ERROR + assert not hass.data.get(DOMAIN) + + +async def test_setup_entry_api_cannot_connect( + hass: HomeAssistant, laundrify_validate_token +): + """Test that ApiConnectionException is thrown when connection fails.""" + laundrify_validate_token.side_effect = exceptions.ApiConnectionException + config_entry = create_entry(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert config_entry.state == ConfigEntryState.SETUP_RETRY + assert not hass.data.get(DOMAIN) + + +async def test_setup_entry_successful(hass: HomeAssistant): + """Test entry can be setup successfully.""" + config_entry = create_entry(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert config_entry.state == ConfigEntryState.LOADED + + +async def test_setup_entry_unload(hass: HomeAssistant): + """Test unloading the laundrify entry.""" + config_entry = create_entry(hass) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.config_entries.async_unload(config_entry.entry_id) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert config_entry.state == ConfigEntryState.NOT_LOADED From 654c59c19414c52f95f56603c460f63edeb60959 Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sat, 21 May 2022 19:35:27 -0400 Subject: [PATCH 675/930] Add diagnostics for UniFi Protect (#72280) --- .../components/unifiprotect/diagnostics.py | 21 +++++++ tests/components/unifiprotect/conftest.py | 13 +++++ .../unifiprotect/test_diagnostics.py | 56 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 homeassistant/components/unifiprotect/diagnostics.py create mode 100644 tests/components/unifiprotect/test_diagnostics.py diff --git a/homeassistant/components/unifiprotect/diagnostics.py b/homeassistant/components/unifiprotect/diagnostics.py new file mode 100644 index 00000000000..b76c9eba1e7 --- /dev/null +++ b/homeassistant/components/unifiprotect/diagnostics.py @@ -0,0 +1,21 @@ +"""Diagnostics support for UniFi Network.""" +from __future__ import annotations + +from typing import Any, cast + +from pyunifiprotect.test_util.anonymize import anonymize_data + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import DOMAIN +from .data import ProtectData + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + + data: ProtectData = hass.data[DOMAIN][config_entry.entry_id] + return cast(dict[str, Any], anonymize_data(data.api.bootstrap.unifi_dict())) diff --git a/tests/components/unifiprotect/conftest.py b/tests/components/unifiprotect/conftest.py index 6eeef02e817..3986e4cd5a3 100644 --- a/tests/components/unifiprotect/conftest.py +++ b/tests/components/unifiprotect/conftest.py @@ -67,6 +67,19 @@ class MockBootstrap: """Fake process method for tests.""" pass + def unifi_dict(self) -> dict[str, Any]: + """Return UniFi formatted dict representation of the NVR.""" + return { + "nvr": self.nvr.unifi_dict(), + "cameras": [c.unifi_dict() for c in self.cameras.values()], + "lights": [c.unifi_dict() for c in self.lights.values()], + "sensors": [c.unifi_dict() for c in self.sensors.values()], + "viewers": [c.unifi_dict() for c in self.viewers.values()], + "liveviews": [c.unifi_dict() for c in self.liveviews.values()], + "doorlocks": [c.unifi_dict() for c in self.doorlocks.values()], + "chimes": [c.unifi_dict() for c in self.chimes.values()], + } + @dataclass class MockEntityFixture: diff --git a/tests/components/unifiprotect/test_diagnostics.py b/tests/components/unifiprotect/test_diagnostics.py new file mode 100644 index 00000000000..b58e164e913 --- /dev/null +++ b/tests/components/unifiprotect/test_diagnostics.py @@ -0,0 +1,56 @@ +"""Test UniFi Protect diagnostics.""" + +from pyunifiprotect.data import NVR, Light + +from homeassistant.core import HomeAssistant + +from .conftest import MockEntityFixture + +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light, hass_client +): + """Test generating diagnostics for a config entry.""" + + light1 = mock_light.copy() + light1._api = mock_entry.api + light1.name = "Test Light 1" + light1.id = "lightid1" + + mock_entry.api.bootstrap.lights = { + light1.id: light1, + } + await hass.config_entries.async_setup(mock_entry.entry.entry_id) + await hass.async_block_till_done() + + diag = await get_diagnostics_for_config_entry(hass, hass_client, mock_entry.entry) + + nvr_obj: NVR = mock_entry.api.bootstrap.nvr + # validate some of the data + assert "nvr" in diag and isinstance(diag["nvr"], dict) + nvr = diag["nvr"] + # should have been anonymized + assert nvr["id"] != nvr_obj.id + assert nvr["mac"] != nvr_obj.mac + assert nvr["host"] != str(nvr_obj.host) + # should have been kept + assert nvr["firmwareVersion"] == nvr_obj.firmware_version + assert nvr["version"] == str(nvr_obj.version) + assert nvr["type"] == nvr_obj.type + + assert ( + "lights" in diag + and isinstance(diag["lights"], list) + and len(diag["lights"]) == 1 + ) + light = diag["lights"][0] + # should have been anonymized + assert light["id"] != light1.id + assert light["name"] != light1.mac + assert light["mac"] != light1.mac + assert light["host"] != str(light1.host) + # should have been kept + assert light["firmwareVersion"] == light1.firmware_version + assert light["type"] == light1.type From b6b72f50ec4a21e0c9cc5681bd09193e1a4ec256 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 22 May 2022 00:22:43 +0000 Subject: [PATCH 676/930] [ci skip] Translation update --- .../components/adguard/translations/nl.json | 2 +- .../ambient_station/translations/nl.json | 2 +- .../azure_event_hub/translations/nl.json | 2 +- .../cert_expiry/translations/nl.json | 2 +- .../coronavirus/translations/nl.json | 2 +- .../components/deluge/translations/nl.json | 2 +- .../components/discord/translations/nl.json | 2 +- .../components/filesize/translations/nl.json | 2 +- .../components/fivem/translations/nl.json | 2 +- .../components/gdacs/translations/nl.json | 2 +- .../geonetnz_quakes/translations/nl.json | 2 +- .../components/github/translations/nl.json | 2 +- .../google_travel_time/translations/fr.json | 2 +- .../components/hangouts/translations/nl.json | 2 +- .../here_travel_time/translations/ca.json | 82 +++++++++++++++++++ .../here_travel_time/translations/de.json | 82 +++++++++++++++++++ .../here_travel_time/translations/el.json | 82 +++++++++++++++++++ .../here_travel_time/translations/en.json | 82 +++++++++++++++++++ .../here_travel_time/translations/fr.json | 81 ++++++++++++++++++ .../here_travel_time/translations/pt-BR.json | 82 +++++++++++++++++++ .../humidifier/translations/nl.json | 2 +- .../components/hyperion/translations/nl.json | 2 +- .../components/insteon/translations/nl.json | 2 +- .../components/iqvia/translations/nl.json | 2 +- .../components/knx/translations/hu.json | 2 +- .../components/knx/translations/nl.json | 2 +- .../components/konnected/translations/ca.json | 16 ++-- .../components/konnected/translations/de.json | 16 ++-- .../components/konnected/translations/en.json | 16 ++-- .../components/konnected/translations/fr.json | 16 ++-- .../components/konnected/translations/pl.json | 16 ++-- .../konnected/translations/pt-BR.json | 16 ++-- .../konnected/translations/zh-Hant.json | 16 ++-- .../components/laundrify/translations/de.json | 25 ++++++ .../components/laundrify/translations/en.json | 42 +++++----- .../components/laundrify/translations/fr.json | 25 ++++++ .../laundrify/translations/pt-BR.json | 25 ++++++ .../components/luftdaten/translations/nl.json | 2 +- .../components/met/translations/nl.json | 2 +- .../met_eireann/translations/nl.json | 2 +- .../components/metoffice/translations/nl.json | 2 +- .../minecraft_server/translations/nl.json | 2 +- .../motion_blinds/translations/hu.json | 2 +- .../components/motioneye/translations/nl.json | 2 +- .../components/mqtt/translations/nl.json | 2 +- .../components/myq/translations/nl.json | 2 +- .../components/nws/translations/nl.json | 2 +- .../components/onewire/translations/hu.json | 2 +- .../components/peco/translations/nl.json | 2 +- .../components/pi_hole/translations/nl.json | 2 +- .../components/plugwise/translations/nl.json | 2 +- .../pvpc_hourly_pricing/translations/nl.json | 2 +- .../components/remote/translations/nl.json | 4 +- .../components/season/translations/nl.json | 2 +- .../components/shelly/translations/id.json | 1 + .../shopping_list/translations/nl.json | 2 +- .../simplisafe/translations/hu.json | 2 +- .../components/slack/translations/nl.json | 2 +- .../components/sonarr/translations/nl.json | 2 +- .../components/sql/translations/nl.json | 8 +- .../steam_online/translations/nl.json | 2 +- .../stookalert/translations/nl.json | 2 +- .../components/syncthing/translations/nl.json | 2 +- .../tellduslive/translations/nl.json | 2 +- .../components/tibber/translations/nl.json | 2 +- .../ukraine_alarm/translations/hu.json | 6 +- .../ukraine_alarm/translations/nl.json | 2 +- .../components/unifi/translations/hu.json | 2 +- .../components/vallox/translations/nl.json | 2 +- .../vlc_telnet/translations/nl.json | 2 +- .../components/whois/translations/nl.json | 2 +- .../wled/translations/select.nl.json | 2 +- 72 files changed, 704 insertions(+), 137 deletions(-) create mode 100644 homeassistant/components/here_travel_time/translations/ca.json create mode 100644 homeassistant/components/here_travel_time/translations/de.json create mode 100644 homeassistant/components/here_travel_time/translations/el.json create mode 100644 homeassistant/components/here_travel_time/translations/en.json create mode 100644 homeassistant/components/here_travel_time/translations/fr.json create mode 100644 homeassistant/components/here_travel_time/translations/pt-BR.json create mode 100644 homeassistant/components/laundrify/translations/de.json create mode 100644 homeassistant/components/laundrify/translations/fr.json create mode 100644 homeassistant/components/laundrify/translations/pt-BR.json diff --git a/homeassistant/components/adguard/translations/nl.json b/homeassistant/components/adguard/translations/nl.json index 38a64997e73..345bc914ec5 100644 --- a/homeassistant/components/adguard/translations/nl.json +++ b/homeassistant/components/adguard/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "existing_instance_updated": "Bestaande configuratie bijgewerkt." }, "error": { diff --git a/homeassistant/components/ambient_station/translations/nl.json b/homeassistant/components/ambient_station/translations/nl.json index 008bc10e084..332ff9f0a33 100644 --- a/homeassistant/components/ambient_station/translations/nl.json +++ b/homeassistant/components/ambient_station/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "invalid_key": "Ongeldige API-sleutel", diff --git a/homeassistant/components/azure_event_hub/translations/nl.json b/homeassistant/components/azure_event_hub/translations/nl.json index e0aabd1c971..e75443dbc88 100644 --- a/homeassistant/components/azure_event_hub/translations/nl.json +++ b/homeassistant/components/azure_event_hub/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "cannot_connect": "Verbinding maken met de credentials uit de configuration.yaml is mislukt, verwijder deze uit yaml en gebruik de config flow.", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk.", "unknown": "Verbinding maken met de credentials uit de configuration.yaml is mislukt met een onbekende fout, verwijder deze uit yaml en gebruik de config flow." diff --git a/homeassistant/components/cert_expiry/translations/nl.json b/homeassistant/components/cert_expiry/translations/nl.json index e3cd3d7983b..e330a8c01dd 100644 --- a/homeassistant/components/cert_expiry/translations/nl.json +++ b/homeassistant/components/cert_expiry/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "import_failed": "Importeren vanuit configuratie is mislukt" }, "error": { diff --git a/homeassistant/components/coronavirus/translations/nl.json b/homeassistant/components/coronavirus/translations/nl.json index fec0b6462eb..9b0872dafa2 100644 --- a/homeassistant/components/coronavirus/translations/nl.json +++ b/homeassistant/components/coronavirus/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken" }, "step": { diff --git a/homeassistant/components/deluge/translations/nl.json b/homeassistant/components/deluge/translations/nl.json index 6c824130708..d96824b10de 100644 --- a/homeassistant/components/deluge/translations/nl.json +++ b/homeassistant/components/deluge/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/discord/translations/nl.json b/homeassistant/components/discord/translations/nl.json index 303b22fdfad..b84af57af18 100644 --- a/homeassistant/components/discord/translations/nl.json +++ b/homeassistant/components/discord/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "reauth_successful": "Herauthenticatie geslaagd" }, "error": { diff --git a/homeassistant/components/filesize/translations/nl.json b/homeassistant/components/filesize/translations/nl.json index d8a35446044..4b72bc53292 100644 --- a/homeassistant/components/filesize/translations/nl.json +++ b/homeassistant/components/filesize/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "not_allowed": "Pad is niet toegestaan", diff --git a/homeassistant/components/fivem/translations/nl.json b/homeassistant/components/fivem/translations/nl.json index cac4b7ed12f..ef5952cb60a 100644 --- a/homeassistant/components/fivem/translations/nl.json +++ b/homeassistant/components/fivem/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken. Controleer de host en poort en probeer het opnieuw. Zorg er ook voor dat u de nieuwste FiveM-server gebruikt.", diff --git a/homeassistant/components/gdacs/translations/nl.json b/homeassistant/components/gdacs/translations/nl.json index 6dd3e5aa196..d46f8dc0a50 100644 --- a/homeassistant/components/gdacs/translations/nl.json +++ b/homeassistant/components/gdacs/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/geonetnz_quakes/translations/nl.json b/homeassistant/components/geonetnz_quakes/translations/nl.json index 74766300e11..62f4649f429 100644 --- a/homeassistant/components/geonetnz_quakes/translations/nl.json +++ b/homeassistant/components/geonetnz_quakes/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/github/translations/nl.json b/homeassistant/components/github/translations/nl.json index f7cb2ac22e4..8a53a0b38d4 100644 --- a/homeassistant/components/github/translations/nl.json +++ b/homeassistant/components/github/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "could_not_register": "Kan integratie niet met GitHub registreren" }, "progress": { diff --git a/homeassistant/components/google_travel_time/translations/fr.json b/homeassistant/components/google_travel_time/translations/fr.json index 790fe9117bd..86761315d58 100644 --- a/homeassistant/components/google_travel_time/translations/fr.json +++ b/homeassistant/components/google_travel_time/translations/fr.json @@ -24,7 +24,7 @@ "data": { "avoid": "\u00c9viter de", "language": "Langue", - "mode": "Mode voyage", + "mode": "Mode de d\u00e9placement", "time": "Temps", "time_type": "Type de temps", "transit_mode": "Mode de transit", diff --git a/homeassistant/components/hangouts/translations/nl.json b/homeassistant/components/hangouts/translations/nl.json index 276c310da5a..826bc560045 100644 --- a/homeassistant/components/hangouts/translations/nl.json +++ b/homeassistant/components/hangouts/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "unknown": "Onverwachte fout" }, "error": { diff --git a/homeassistant/components/here_travel_time/translations/ca.json b/homeassistant/components/here_travel_time/translations/ca.json new file mode 100644 index 00000000000..08dfb0d970d --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/ca.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "El dispositiu ja est\u00e0 configurat" + }, + "error": { + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "unknown": "Error inesperat" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destinaci\u00f3 com a coordenades GPS" + }, + "title": "Tria destinaci\u00f3" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destinaci\u00f3 utilitzant entitat" + }, + "title": "Tria destinaci\u00f3" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Utilitzant una ubicaci\u00f3 de mapa", + "destination_entity": "Utilitzant una entitat" + }, + "title": "Tria destinaci\u00f3" + }, + "origin_coordinates": { + "data": { + "origin": "Origen com a coordenades GPS" + }, + "title": "Tria origen" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Origen utilitzant entitat" + }, + "title": "Tria origen" + }, + "user": { + "data": { + "api_key": "Clau API", + "mode": "Mode de viatge", + "name": "Nom" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Hora d'arribada" + }, + "title": "Tria l'hora d'arribada" + }, + "departure_time": { + "data": { + "departure_time": "Hora de sortida" + }, + "title": "Tria l'hora de sortida" + }, + "init": { + "data": { + "route_mode": "Mode ruta", + "traffic_mode": "Mode tr\u00e0nsit", + "unit_system": "Sistema d'unitats" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Configura hora d'arribada", + "departure_time": "Configura hora de sortida", + "no_time": "No configurar hora" + }, + "title": "Tria tipus d'hora" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/de.json b/homeassistant/components/here_travel_time/translations/de.json new file mode 100644 index 00000000000..b50ef028089 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/de.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Ger\u00e4t ist bereits konfiguriert" + }, + "error": { + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Ziel als GPS-Koordinaten" + }, + "title": "Ziel w\u00e4hlen" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Ziel mit einer Entit\u00e4t" + }, + "title": "Ziel w\u00e4hlen" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Verwendung einer Kartenposition", + "destination_entity": "Verwenden einer Entit\u00e4t" + }, + "title": "Ziel w\u00e4hlen" + }, + "origin_coordinates": { + "data": { + "origin": "Herkunft als GPS-Koordinaten" + }, + "title": "Herkunft w\u00e4hlen" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Herkunft \u00fcber eine Entit\u00e4t" + }, + "title": "Herkunft w\u00e4hlen" + }, + "user": { + "data": { + "api_key": "API-Schl\u00fcssel", + "mode": "Reisemodus", + "name": "Name" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Ankunftszeit" + }, + "title": "Ankunftszeit w\u00e4hlen" + }, + "departure_time": { + "data": { + "departure_time": "Abfahrtszeit" + }, + "title": "Abfahrtszeit w\u00e4hlen" + }, + "init": { + "data": { + "route_mode": "Routenmodus", + "traffic_mode": "Verkehrsmodus", + "unit_system": "Einheitssystem" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Ankunftszeit konfigurieren", + "departure_time": "Abfahrtszeit konfigurieren", + "no_time": "Keine Zeit konfigurieren" + }, + "title": "Zeitart w\u00e4hlen" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/el.json b/homeassistant/components/here_travel_time/translations/el.json new file mode 100644 index 00000000000..cd4d986ed3f --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/el.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af" + }, + "error": { + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "\u03a0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c9\u03c2 \u03c3\u03c5\u03bd\u03c4\u03b5\u03c4\u03b1\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 GPS" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "\u03a0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03b8\u03ad\u03c3\u03b7\u03c2 \u03c7\u03ac\u03c1\u03c4\u03b7", + "destination_entity": "\u03a7\u03c1\u03ae\u03c3\u03b7 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03c1\u03bf\u03bf\u03c1\u03b9\u03c3\u03bc\u03bf\u03cd" + }, + "origin_coordinates": { + "data": { + "origin": "\u03a0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7 \u03c9\u03c2 \u03c3\u03c5\u03bd\u03c4\u03b5\u03c4\u03b1\u03b3\u03bc\u03ad\u03bd\u03b5\u03c2 GPS" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7\u03c2" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "\u03a0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7 \u03bc\u03b5 \u03c7\u03c1\u03ae\u03c3\u03b7 \u03bf\u03bd\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c0\u03c1\u03bf\u03ad\u03bb\u03b5\u03c5\u03c3\u03b7\u03c2" + }, + "user": { + "data": { + "api_key": "\u039a\u03bb\u03b5\u03b9\u03b4\u03af API", + "mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03c4\u03b1\u03be\u03b9\u03b4\u03b9\u03bf\u03cd", + "name": "\u038c\u03bd\u03bf\u03bc\u03b1" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u038f\u03c1\u03b1 \u03ac\u03c6\u03b9\u03be\u03b7\u03c2" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ce\u03c1\u03b1 \u03ac\u03c6\u03b9\u03be\u03b7\u03c2" + }, + "departure_time": { + "data": { + "departure_time": "\u038f\u03c1\u03b1 \u03b1\u03bd\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03ad\u03be\u03c4\u03b5 \u03ce\u03c1\u03b1 \u03b1\u03bd\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2" + }, + "init": { + "data": { + "route_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b4\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae\u03c2", + "traffic_mode": "\u039b\u03b5\u03b9\u03c4\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03ba\u03c5\u03ba\u03bb\u03bf\u03c6\u03bf\u03c1\u03af\u03b1\u03c2", + "unit_system": "\u03a3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1 \u03bc\u03bf\u03bd\u03ac\u03b4\u03c9\u03bd" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03ce\u03c1\u03b1\u03c2 \u03ac\u03c6\u03b9\u03be\u03b7\u03c2", + "departure_time": "\u03a1\u03cd\u03b8\u03bc\u03b9\u03c3\u03b7 \u03ce\u03c1\u03b1\u03c2 \u03b1\u03bd\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7\u03c2", + "no_time": "\u039c\u03b7 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03c7\u03c1\u03cc\u03bd\u03bf\u03c5" + }, + "title": "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ae \u03c4\u03cd\u03c0\u03bf\u03c5 \u03ce\u03c1\u03b1\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/en.json b/homeassistant/components/here_travel_time/translations/en.json new file mode 100644 index 00000000000..d4f9984d945 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/en.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "invalid_auth": "Invalid authentication", + "unknown": "Unexpected error" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destination as GPS coordinates" + }, + "title": "Choose Destination" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destination using an entity" + }, + "title": "Choose Destination" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Using a map location", + "destination_entity": "Using an entity" + }, + "title": "Choose Destination" + }, + "origin_coordinates": { + "data": { + "origin": "Origin as GPS coordinates" + }, + "title": "Choose Origin" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Origin using an entity" + }, + "title": "Choose Origin" + }, + "user": { + "data": { + "api_key": "API Key", + "mode": "Travel Mode", + "name": "Name" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Arrival Time" + }, + "title": "Choose Arrival Time" + }, + "departure_time": { + "data": { + "departure_time": "Departure Time" + }, + "title": "Choose Departure Time" + }, + "init": { + "data": { + "route_mode": "Route Mode", + "traffic_mode": "Traffic Mode", + "unit_system": "Unit system" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Configure an arrival time", + "departure_time": "Configure a departure time", + "no_time": "Do not configure a time" + }, + "title": "Choose Time Type" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/fr.json b/homeassistant/components/here_travel_time/translations/fr.json new file mode 100644 index 00000000000..063a4008779 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/fr.json @@ -0,0 +1,81 @@ +{ + "config": { + "abort": { + "already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9" + }, + "error": { + "invalid_auth": "Authentification non valide", + "unknown": "Erreur inattendue" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destination sous forme de coordonn\u00e9es GPS" + }, + "title": "Choix de la destination" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destination avec utilisation d'une entit\u00e9" + }, + "title": "Choix de la destination" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Utilisation d'un emplacement sur la carte", + "destination_entity": "Utilisation d'une entit\u00e9" + }, + "title": "Choix de la destination" + }, + "origin_coordinates": { + "data": { + "origin": "Origine sous forme de coordonn\u00e9es GPS" + }, + "title": "Choix de l'origine" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Origine avec utilisation d'une entit\u00e9" + }, + "title": "Choix de l'origine" + }, + "user": { + "data": { + "api_key": "Cl\u00e9 d'API", + "mode": "Mode de d\u00e9placement", + "name": "Nom" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Heure d'arriv\u00e9e" + }, + "title": "Choix de l'heure d'arriv\u00e9e" + }, + "departure_time": { + "data": { + "departure_time": "Heure de d\u00e9part" + }, + "title": "Choix de l'heure de d\u00e9part" + }, + "init": { + "data": { + "route_mode": "Mode itin\u00e9raire", + "traffic_mode": "Mode circulation", + "unit_system": "Syst\u00e8me d'unit\u00e9s" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Configurer une heure d'arriv\u00e9e", + "departure_time": "Configurer une heure de d\u00e9part", + "no_time": "Ne configurez pas d'heure" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/pt-BR.json b/homeassistant/components/here_travel_time/translations/pt-BR.json new file mode 100644 index 00000000000..78996561564 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/pt-BR.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo j\u00e1 est\u00e1 configurado" + }, + "error": { + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "unknown": "Erro inesperado" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destino como coordenadas GPS" + }, + "title": "Escolha o destino" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destino usando uma entidade" + }, + "title": "Escolha o destino" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Usando uma localiza\u00e7\u00e3o no mapa", + "destination_entity": "Usando uma entidade" + }, + "title": "Escolha o destino" + }, + "origin_coordinates": { + "data": { + "origin": "Origem como coordenadas GPS" + }, + "title": "Escolha a Origem" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Origem usando uma entidade" + }, + "title": "Escolha a Origem" + }, + "user": { + "data": { + "api_key": "Chave API", + "mode": "Modo de viagem", + "name": "Nome" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Hora da chegada" + }, + "title": "Escolha a hora de chegada" + }, + "departure_time": { + "data": { + "departure_time": "Hora de partida" + }, + "title": "Escolha o hor\u00e1rio de partida" + }, + "init": { + "data": { + "route_mode": "Modo de rota", + "traffic_mode": "Modo de tr\u00e2nsito", + "unit_system": "Sistema de unidades" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Configurar um hor\u00e1rio de chegada", + "departure_time": "Configurar um hor\u00e1rio de partida", + "no_time": "N\u00e3o configure um hor\u00e1rio" + }, + "title": "Escolha o tipo de hor\u00e1rio" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/humidifier/translations/nl.json b/homeassistant/components/humidifier/translations/nl.json index 1cdf417e6ef..4ede3b9cb98 100644 --- a/homeassistant/components/humidifier/translations/nl.json +++ b/homeassistant/components/humidifier/translations/nl.json @@ -10,7 +10,7 @@ "condition_type": { "is_mode": "{entity_name} is ingesteld op een specifieke modus", "is_off": "{entity_name} is uitgeschakeld", - "is_on": "{entity_name} staat aan" + "is_on": "{entity_name} is ingeschakeld" }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", diff --git a/homeassistant/components/hyperion/translations/nl.json b/homeassistant/components/hyperion/translations/nl.json index ff2cd5f4f6b..c3d1684c250 100644 --- a/homeassistant/components/hyperion/translations/nl.json +++ b/homeassistant/components/hyperion/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "already_in_progress": "De configuratie is momenteel al bezig", "auth_new_token_not_granted_error": "Nieuw aangemaakte token is niet goedgekeurd in Hyperion UI", "auth_new_token_not_work_error": "Verificatie met nieuw aangemaakt token mislukt", diff --git a/homeassistant/components/insteon/translations/nl.json b/homeassistant/components/insteon/translations/nl.json index 2683342ad02..3d05a8ed011 100644 --- a/homeassistant/components/insteon/translations/nl.json +++ b/homeassistant/components/insteon/translations/nl.json @@ -12,7 +12,7 @@ "flow_title": "{name}", "step": { "confirm_usb": { - "description": "Wilt u {name} instellen?" + "description": "Wil je {name} instellen?" }, "hubv1": { "data": { diff --git a/homeassistant/components/iqvia/translations/nl.json b/homeassistant/components/iqvia/translations/nl.json index 13d7e0ce4d3..cea9df26bac 100644 --- a/homeassistant/components/iqvia/translations/nl.json +++ b/homeassistant/components/iqvia/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "invalid_zip_code": "Postcode is ongeldig" diff --git a/homeassistant/components/knx/translations/hu.json b/homeassistant/components/knx/translations/hu.json index ccb9eb63c61..28cab2cea5f 100644 --- a/homeassistant/components/knx/translations/hu.json +++ b/homeassistant/components/knx/translations/hu.json @@ -102,7 +102,7 @@ "multicast_group": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `224.0.23.12`.", "multicast_port": "\u00datv\u00e1laszt\u00e1shoz \u00e9s felder\u00edt\u00e9shez haszn\u00e1latos. Alap\u00e9rtelmezett: `3671`", "rate_limit": "Maxim\u00e1lis kimen\u0151 \u00fczenet m\u00e1sodpercenk\u00e9nt.\nAj\u00e1nlott: 20 \u00e9s 40 k\u00f6z\u00f6tt", - "state_updater": "Glob\u00e1lisan enged\u00e9lyezze vagy tiltsa le az olvas\u00e1si \u00e1llapotokat a KNX-buszr\u00f3l. Ha le van tiltva, a Home Assistant nem fogja akt\u00edvan lek\u00e9rni az \u00e1llapotokat a KNX-buszr\u00f3l, a \"sync_state\" entit\u00e1sopci\u00f3knak nincs hat\u00e1sa." + "state_updater": "Alap\u00e9rtelmezett be\u00e1ll\u00edt\u00e1s a KNX busz \u00e1llapotainak olvas\u00e1s\u00e1hoz. Ha le va tiltva, Home Assistant nem fog akt\u00edvan lek\u00e9rdezni egys\u00e9g\u00e1llapotokat a KNX buszr\u00f3l. Fel\u00fclb\u00edr\u00e1lhat\u00f3 a `sync_state` entit\u00e1s opci\u00f3kkal." } }, "tunnel": { diff --git a/homeassistant/components/knx/translations/nl.json b/homeassistant/components/knx/translations/nl.json index 1c597507c67..bc8f03eb06c 100644 --- a/homeassistant/components/knx/translations/nl.json +++ b/homeassistant/components/knx/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { diff --git a/homeassistant/components/konnected/translations/ca.json b/homeassistant/components/konnected/translations/ca.json index c9bf5414d86..8d769749325 100644 --- a/homeassistant/components/konnected/translations/ca.json +++ b/homeassistant/components/konnected/translations/ca.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Inverteix l'estat obert/tancat", - "name": "Nom (opcional)", + "name": "Nom", "type": "Tipus de sensor binari" }, "description": "Opcions de {zone}", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Nom (opcional)", - "poll_interval": "Interval de sondeig (minuts) (opcional)", + "name": "Nom", + "poll_interval": "Interval de sondeig (minuts)", "type": "Tipus de sensor" }, "description": "Opcions de {zone}", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Substitueix l'URL d'amfitri\u00f3 d'API (opcional)", + "api_host": "Substitueix l'URL d'amfitri\u00f3 d'API", "blink": "Parpelleja el LED del panell quan s'envien canvis d'estat", "discovery": "Respon a peticions de descobriment a la teva xarxa", "override_api_host": "Substitueix l'URL per defecte del panell d'amfitri\u00f3 de l'API de Home Assistant" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Sortida quan estigui ON", - "momentary": "Durada del pols (ms) (opcional)", + "momentary": "Durada del pols (ms)", "more_states": "Configura estats addicionals per a aquesta zona", - "name": "Nom (opcional)", - "pause": "Pausa entre polsos (ms) (opcional)", - "repeat": "Repeticions (-1 = infinit) (opcional)" + "name": "Nom", + "pause": "Pausa entre polsos (ms)", + "repeat": "Repeticions (-1 = infinit)" }, "description": "Opcions de {zone}: estat {state}", "title": "Configuraci\u00f3 de sortida commutable" diff --git a/homeassistant/components/konnected/translations/de.json b/homeassistant/components/konnected/translations/de.json index 937425be1dc..82c99839f25 100644 --- a/homeassistant/components/konnected/translations/de.json +++ b/homeassistant/components/konnected/translations/de.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Invertiere den \u00d6ffnungs- / Schlie\u00dfzustand", - "name": "Name (optional)", + "name": "Name", "type": "Bin\u00e4rer Sensortyp" }, "description": "Bitte w\u00e4hle die Optionen f\u00fcr den an {zone} angeschlossenen Bin\u00e4rsensor", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Name (optional)", - "poll_interval": "Abfrageintervall (Minuten) (optional)", + "name": "Name", + "poll_interval": "Abfrageintervall (Minuten)", "type": "Sensortyp" }, "description": "Bitte w\u00e4hle die Optionen f\u00fcr den an {zone} angeschlossenen digitalen Sensor aus", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "API-Host-URL \u00fcberschreiben (optional)", + "api_host": "API-Host-URL \u00fcberschreiben", "blink": "LED Panel blinkt beim senden von Status\u00e4nderungen", "discovery": "Reagieren auf Suchanfragen in deinem Netzwerk", "override_api_host": "\u00dcberschreibe die Standard-Host-Panel-URL der Home Assistant-API" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Ausgabe, wenn eingeschaltet", - "momentary": "Impulsdauer (ms) (optional)", + "momentary": "Impulsdauer (ms)", "more_states": "Konfiguriere zus\u00e4tzliche Zust\u00e4nde f\u00fcr diese Zone", - "name": "Name (optional)", - "pause": "Pause zwischen Impulsen (ms) (optional)", - "repeat": "Mal wiederholen (-1 = unendlich) (optional)" + "name": "Name", + "pause": "Pause zwischen Impulsen (ms)", + "repeat": "Mal wiederholen (-1 = unendlich)" }, "description": "Bitte w\u00e4hlen die Ausgabeoptionen f\u00fcr {zone} : Status {state}", "title": "Konfiguriere den schaltbaren Ausgang" diff --git a/homeassistant/components/konnected/translations/en.json b/homeassistant/components/konnected/translations/en.json index b5e6340b562..0015a81ef31 100644 --- a/homeassistant/components/konnected/translations/en.json +++ b/homeassistant/components/konnected/translations/en.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Invert the open/close state", - "name": "Name (optional)", + "name": "Name", "type": "Binary Sensor Type" }, "description": "{zone} options", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Name (optional)", - "poll_interval": "Poll Interval (minutes) (optional)", + "name": "Name", + "poll_interval": "Poll Interval (minutes)", "type": "Sensor Type" }, "description": "{zone} options", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Override API host URL (optional)", + "api_host": "Override API host URL", "blink": "Blink panel LED on when sending state change", "discovery": "Respond to discovery requests on your network", "override_api_host": "Override default Home Assistant API host panel URL" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Output when on", - "momentary": "Pulse duration (ms) (optional)", + "momentary": "Pulse duration (ms)", "more_states": "Configure additional states for this zone", - "name": "Name (optional)", - "pause": "Pause between pulses (ms) (optional)", - "repeat": "Times to repeat (-1=infinite) (optional)" + "name": "Name", + "pause": "Pause between pulses (ms)", + "repeat": "Times to repeat (-1=infinite)" }, "description": "{zone} options: state {state}", "title": "Configure Switchable Output" diff --git a/homeassistant/components/konnected/translations/fr.json b/homeassistant/components/konnected/translations/fr.json index 4b84efa351d..42b62ea490d 100644 --- a/homeassistant/components/konnected/translations/fr.json +++ b/homeassistant/components/konnected/translations/fr.json @@ -41,7 +41,7 @@ "options_binary": { "data": { "inverse": "Inverser l'\u00e9tat ouvert / ferm\u00e9", - "name": "Nom (facultatif)", + "name": "Nom", "type": "Type de capteur binaire" }, "description": "Veuillez s\u00e9lectionner les options du capteur binaire attach\u00e9 \u00e0 {zone}", @@ -49,8 +49,8 @@ }, "options_digital": { "data": { - "name": "Nom (facultatif)", - "poll_interval": "Intervalle d'interrogation (minutes) (facultatif)", + "name": "Nom", + "poll_interval": "Intervalle d'interrogation (en minutes)", "type": "Type de capteur" }, "description": "Veuillez s\u00e9lectionner les options du capteur digital attach\u00e9 \u00e0 {zone}", @@ -86,7 +86,7 @@ }, "options_misc": { "data": { - "api_host": "Remplacer l'URL de l'h\u00f4te de l'API (facultatif)", + "api_host": "Remplacer l'URL de l'h\u00f4te de l'API", "blink": "Voyant du panneau clignotant lors de l'envoi d'un changement d'\u00e9tat", "discovery": "R\u00e9pondre aux demandes de d\u00e9couverte sur votre r\u00e9seau", "override_api_host": "Remplacer l'URL par d\u00e9faut du panneau h\u00f4te de l'API Home Assistant" @@ -97,11 +97,11 @@ "options_switch": { "data": { "activation": "Sortie lorsque activ\u00e9", - "momentary": "Dur\u00e9e de l'impulsion (en millisecondes, facultatif)", + "momentary": "Dur\u00e9e de l'impulsion (en millisecondes)", "more_states": "Configurer des \u00e9tats suppl\u00e9mentaires pour cette zone", - "name": "Nom (facultatif)", - "pause": "Pause entre les impulsions (ms) (facultatif)", - "repeat": "Nombre de r\u00e9p\u00e9tition (-1=infini) (facultatif)" + "name": "Nom", + "pause": "Pause entre les impulsions (en millisecondes)", + "repeat": "Nombre de r\u00e9p\u00e9titions (-1\u00a0=\u00a0infini)" }, "description": "Veuillez s\u00e9lectionner les options de sortie pour {zone} : \u00e9tat {state}", "title": "Configurer la sortie commutable" diff --git a/homeassistant/components/konnected/translations/pl.json b/homeassistant/components/konnected/translations/pl.json index 7352bc82344..3eb24ac2cab 100644 --- a/homeassistant/components/konnected/translations/pl.json +++ b/homeassistant/components/konnected/translations/pl.json @@ -43,7 +43,7 @@ "options_binary": { "data": { "inverse": "Odwr\u00f3\u0107 stan otwarty/zamkni\u0119ty", - "name": "Nazwa (opcjonalnie)", + "name": "Nazwa", "type": "Typ sensora binarnego" }, "description": "Opcje {zone}", @@ -51,8 +51,8 @@ }, "options_digital": { "data": { - "name": "Nazwa (opcjonalnie)", - "poll_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (minuty) (opcjonalnie)", + "name": "Nazwa", + "poll_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji (minuty)", "type": "Typ sensora" }, "description": "Opcje {zone}", @@ -88,7 +88,7 @@ }, "options_misc": { "data": { - "api_host": "Zast\u0119powanie adresu URL hosta API (opcjonalnie)", + "api_host": "Zast\u0119powanie adresu URL hosta API", "blink": "Miganie diody LED panelu podczas wysy\u0142ania zmiany stanu", "discovery": "Odpowiadaj na \u017c\u0105dania wykrywania w Twojej sieci", "override_api_host": "Zast\u0105p domy\u015blny adres URL API Home Assistanta" @@ -99,11 +99,11 @@ "options_switch": { "data": { "activation": "Wyj\u015bcie, gdy w\u0142\u0105czone", - "momentary": "Czas trwania impulsu (ms) (opcjonalnie)", + "momentary": "Czas trwania impulsu (ms)", "more_states": "Konfigurowanie dodatkowych stan\u00f3w dla tej strefy", - "name": "Nazwa (opcjonalnie)", - "pause": "Przerwa mi\u0119dzy impulsami (ms) (opcjonalnie)", - "repeat": "Ilo\u015b\u0107 powt\u00f3rze\u0144 (-1=niesko\u0144czenie) (opcjonalnie)" + "name": "Nazwa", + "pause": "Przerwa mi\u0119dzy impulsami (ms)", + "repeat": "Ilo\u015b\u0107 powt\u00f3rze\u0144 (-1=niesko\u0144czenie)" }, "description": "Opcje {zone}: stan {state}", "title": "Konfiguracja prze\u0142\u0105czalne wyj\u015bcie" diff --git a/homeassistant/components/konnected/translations/pt-BR.json b/homeassistant/components/konnected/translations/pt-BR.json index e3079fc50f7..92aaf3ce368 100644 --- a/homeassistant/components/konnected/translations/pt-BR.json +++ b/homeassistant/components/konnected/translations/pt-BR.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Inverter o estado aberto/fechado", - "name": "Nome (opcional)", + "name": "Nome", "type": "Tipo de sensor bin\u00e1rio" }, "description": "Selecione as op\u00e7\u00f5es para o sensor bin\u00e1rio conectado a {zone}", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Nome (opcional)", - "poll_interval": "Intervalo de vota\u00e7\u00e3o (minutos) (opcional)", + "name": "Nome", + "poll_interval": "Intervalo de vota\u00e7\u00e3o (minutos)", "type": "Tipo de sensor" }, "description": "Selecione as op\u00e7\u00f5es para o sensor digital conectado a {zone}", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Substituir URL do host da API (opcional)", + "api_host": "Substituir URL do host da API", "blink": "LED do painel piscando ao enviar mudan\u00e7a de estado", "discovery": "Responder \u00e0s solicita\u00e7\u00f5es de descoberta em sua rede", "override_api_host": "Substituir o URL padr\u00e3o do painel do host da API do Home Assistant" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Sa\u00edda quando ligado", - "momentary": "Dura\u00e7\u00e3o do pulso (ms) (opcional)", + "momentary": "Dura\u00e7\u00e3o do pulso (ms)", "more_states": "Configurar estados adicionais para esta zona", - "name": "Nome (opcional)", - "pause": "Pausa entre pulsos (ms) (opcional)", - "repeat": "Intervalo para repetir (-1=infinito) (opcional)" + "name": "Nome", + "pause": "Pausa entre pulsos (ms)", + "repeat": "Intervalo para repetir (-1=infinito)" }, "description": "Selecione as op\u00e7\u00f5es para o switch conectado a {zone}: estado {state}", "title": "Configure o interruptor de sa\u00edda" diff --git a/homeassistant/components/konnected/translations/zh-Hant.json b/homeassistant/components/konnected/translations/zh-Hant.json index 9d6d522b7e1..fbce1455270 100644 --- a/homeassistant/components/konnected/translations/zh-Hant.json +++ b/homeassistant/components/konnected/translations/zh-Hant.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "\u53cd\u8f49\u958b\u555f/\u95dc\u9589\u72c0\u614b", - "name": "\u540d\u7a31 \uff08\u9078\u9805\uff09", + "name": "\u540d\u7a31", "type": "\u4e8c\u9032\u4f4d\u611f\u61c9\u5668\u985e\u5225" }, "description": "{zone}\u9078\u9805", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "\u540d\u7a31 \uff08\u9078\u9805\uff09", - "poll_interval": "\u66f4\u65b0\u9593\u8ddd\uff08\u5206\u9418\uff09\uff08\u9078\u9805\uff09", + "name": "\u540d\u7a31", + "poll_interval": "\u66f4\u65b0\u9593\u8ddd\uff08\u5206\u9418\uff09", "type": "\u611f\u61c9\u5668\u985e\u5225" }, "description": "{zone}\u9078\u9805", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "\u8986\u5beb API \u4e3b\u6a5f\u7aef URL\uff08\u9078\u9805\uff09", + "api_host": "\u8986\u5beb API \u4e3b\u6a5f\u7aef URL", "blink": "\u7576\u50b3\u9001\u72c0\u614b\u8b8a\u66f4\u6642\u3001\u9583\u720d\u9762\u677f LED", "discovery": "\u56de\u61c9\u7db2\u8def\u4e0a\u7684\u63a2\u7d22\u8acb\u6c42", "override_api_host": "\u8986\u5beb\u9810\u8a2d Home Assistant API \u4e3b\u6a5f\u7aef\u9762\u677f URL" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "\u958b\u555f\u6642\u8f38\u51fa", - "momentary": "\u6301\u7e8c\u6642\u9593\uff08ms\uff09\uff08\u9078\u9805\uff09", + "momentary": "\u6301\u7e8c\u6642\u9593\uff08ms\uff09", "more_states": "\u8a2d\u5b9a\u6b64\u5340\u57df\u7684\u9644\u52a0\u72c0\u614b", - "name": "\u540d\u7a31 \uff08\u9078\u9805\uff09", - "pause": "\u66ab\u505c\u9593\u8ddd\uff08ms\uff09\uff08\u9078\u9805\uff09", - "repeat": "\u91cd\u8907\u6642\u9593\uff08-1=\u7121\u9650\uff09\uff08\u9078\u9805\uff09" + "name": "\u540d\u7a31", + "pause": "\u66ab\u505c\u9593\u8ddd\uff08ms\uff09", + "repeat": "\u91cd\u8907\u6642\u9593\uff08-1=\u7121\u9650\uff09" }, "description": "{zone}\u9078\u9805\uff1a\u72c0\u614b {state}", "title": "\u8a2d\u5b9a Switchable \u8f38\u51fa" diff --git a/homeassistant/components/laundrify/translations/de.json b/homeassistant/components/laundrify/translations/de.json new file mode 100644 index 00000000000..016f345e13d --- /dev/null +++ b/homeassistant/components/laundrify/translations/de.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Bereits konfiguriert. Nur eine einzige Konfiguration m\u00f6glich." + }, + "error": { + "cannot_connect": "Verbindung fehlgeschlagen", + "invalid_auth": "Ung\u00fcltige Authentifizierung", + "invalid_format": "Ung\u00fcltiges Format. Bitte als xxx-xxx angeben.", + "unknown": "Unerwarteter Fehler" + }, + "step": { + "init": { + "data": { + "code": "Auth-Code (xxx-xxx)" + }, + "description": "Bitte gib deinen pers\u00f6nlichen Auth-Code ein, der in der laundrify-App angezeigt wird." + }, + "reauth_confirm": { + "description": "Die laundrify-Integration muss sich neu authentifizieren.", + "title": "Integration erneut authentifizieren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/en.json b/homeassistant/components/laundrify/translations/en.json index 2bfb07d4041..0c8375746ed 100644 --- a/homeassistant/components/laundrify/translations/en.json +++ b/homeassistant/components/laundrify/translations/en.json @@ -1,25 +1,25 @@ { - "config": { - "abort": { - "single_instance_allowed": "Already configured. Only a single configuration possible." - }, - "error": { - "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", - "invalid_format": "Invalid format. Please specify as xxx-xxx.", - "unknown": "Unexpected error" - }, - "step": { - "init": { - "data": { - "code": "Auth Code (xxx-xxx)" + "config": { + "abort": { + "single_instance_allowed": "Already configured. Only a single configuration possible." }, - "description": "Please enter your personal AuthCode that is shown in the laundrify-App." - }, - "reauth_confirm": { - "description": "The laundrify integration needs to re-authenticate.", - "title": "Reauthenticate Integration" - } + "error": { + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "invalid_format": "Invalid format. Please specify as xxx-xxx.", + "unknown": "Unexpected error" + }, + "step": { + "init": { + "data": { + "code": "Auth Code (xxx-xxx)" + }, + "description": "Please enter your personal Auth Code that is shown in the laundrify-App." + }, + "reauth_confirm": { + "description": "The laundrify integration needs to re-authenticate.", + "title": "Reauthenticate Integration" + } + } } - } } \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/fr.json b/homeassistant/components/laundrify/translations/fr.json new file mode 100644 index 00000000000..91674bdd50c --- /dev/null +++ b/homeassistant/components/laundrify/translations/fr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." + }, + "error": { + "cannot_connect": "\u00c9chec de connexion", + "invalid_auth": "Authentification non valide", + "invalid_format": "Format non valide. Veuillez sp\u00e9cifier au format xxx-xxx.", + "unknown": "Erreur inattendue" + }, + "step": { + "init": { + "data": { + "code": "Code d'authentification (xxx-xxx)" + }, + "description": "Veuillez saisir votre code d'authentification personnel tel qu'affich\u00e9 dans l'application laundrify." + }, + "reauth_confirm": { + "description": "L'int\u00e9gration laundrify doit \u00eatre r\u00e9-authentifi\u00e9e.", + "title": "R\u00e9-authentifier l'int\u00e9gration" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/pt-BR.json b/homeassistant/components/laundrify/translations/pt-BR.json new file mode 100644 index 00000000000..c053ae34fe0 --- /dev/null +++ b/homeassistant/components/laundrify/translations/pt-BR.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "J\u00e1 est\u00e1 configurado. Apenas uma \u00fanica configura\u00e7\u00e3o \u00e9 poss\u00edvel." + }, + "error": { + "cannot_connect": "Falhou ao se conectar", + "invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida", + "invalid_format": "Formato Inv\u00e1lido. Especifique como xxx-xxx.", + "unknown": "Erro inesperado" + }, + "step": { + "init": { + "data": { + "code": "C\u00f3digo de autentica\u00e7\u00e3o (xxx-xxx)" + }, + "description": "Por favor, insira seu c\u00f3digo de autentica\u00e7\u00e3o pessoal que \u00e9 mostrado no aplicativo laundrify." + }, + "reauth_confirm": { + "description": "A integra\u00e7\u00e3o da laundrify precisa ser autenticada novamente.", + "title": "Reautenticar Integra\u00e7\u00e3o" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/luftdaten/translations/nl.json b/homeassistant/components/luftdaten/translations/nl.json index adfdb1101a0..9da8bb945e7 100644 --- a/homeassistant/components/luftdaten/translations/nl.json +++ b/homeassistant/components/luftdaten/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "error": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_sensor": "Sensor niet beschikbaar of ongeldig" }, diff --git a/homeassistant/components/met/translations/nl.json b/homeassistant/components/met/translations/nl.json index 7c3d03fdb1f..1d64c174e07 100644 --- a/homeassistant/components/met/translations/nl.json +++ b/homeassistant/components/met/translations/nl.json @@ -4,7 +4,7 @@ "no_home": "Er zijn geen thuisco\u00f6rdinaten ingesteld in de Home Assistant-configuratie" }, "error": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/met_eireann/translations/nl.json b/homeassistant/components/met_eireann/translations/nl.json index b67c167ca8d..13b008b6a45 100644 --- a/homeassistant/components/met_eireann/translations/nl.json +++ b/homeassistant/components/met_eireann/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "error": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/metoffice/translations/nl.json b/homeassistant/components/metoffice/translations/nl.json index b6fbbfdbba4..00f9400a78b 100644 --- a/homeassistant/components/metoffice/translations/nl.json +++ b/homeassistant/components/metoffice/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/minecraft_server/translations/nl.json b/homeassistant/components/minecraft_server/translations/nl.json index 0170964d5b0..b6dbe56016e 100644 --- a/homeassistant/components/minecraft_server/translations/nl.json +++ b/homeassistant/components/minecraft_server/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken met de server. Controleer de host en de poort en probeer het opnieuw. Zorg er ook voor dat u minimaal Minecraft versie 1.7 op uw server uitvoert.", diff --git a/homeassistant/components/motion_blinds/translations/hu.json b/homeassistant/components/motion_blinds/translations/hu.json index 8ab58169d05..cef4a0426da 100644 --- a/homeassistant/components/motion_blinds/translations/hu.json +++ b/homeassistant/components/motion_blinds/translations/hu.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van, a csatlakoz\u00e1si be\u00e1ll\u00edt\u00e1sai friss\u00edtve vannak", + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van", "already_in_progress": "A be\u00e1ll\u00edt\u00e1si folyamat m\u00e1r el lett kezdve", "connection_error": "Sikertelen csatlakoz\u00e1s" }, diff --git a/homeassistant/components/motioneye/translations/nl.json b/homeassistant/components/motioneye/translations/nl.json index a2e053e2493..bd62a7037ac 100644 --- a/homeassistant/components/motioneye/translations/nl.json +++ b/homeassistant/components/motioneye/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "reauth_successful": "Herauthenticatie geslaagd" }, "error": { diff --git a/homeassistant/components/mqtt/translations/nl.json b/homeassistant/components/mqtt/translations/nl.json index d948cc43df7..155cf0bf07a 100644 --- a/homeassistant/components/mqtt/translations/nl.json +++ b/homeassistant/components/mqtt/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." }, "error": { diff --git a/homeassistant/components/myq/translations/nl.json b/homeassistant/components/myq/translations/nl.json index 8a3020679e9..ea41554ff75 100644 --- a/homeassistant/components/myq/translations/nl.json +++ b/homeassistant/components/myq/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "reauth_successful": "Herauthenticatie geslaagd" }, "error": { diff --git a/homeassistant/components/nws/translations/nl.json b/homeassistant/components/nws/translations/nl.json index 5332f43f4c7..4aa958f385b 100644 --- a/homeassistant/components/nws/translations/nl.json +++ b/homeassistant/components/nws/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/onewire/translations/hu.json b/homeassistant/components/onewire/translations/hu.json index faae98589e3..94581202b99 100644 --- a/homeassistant/components/onewire/translations/hu.json +++ b/homeassistant/components/onewire/translations/hu.json @@ -12,7 +12,7 @@ "host": "C\u00edm", "port": "Port" }, - "title": "A 1-Wire be\u00e1ll\u00edt\u00e1sa" + "title": "A szerver be\u00e1ll\u00edt\u00e1sa" } } }, diff --git a/homeassistant/components/peco/translations/nl.json b/homeassistant/components/peco/translations/nl.json index 088ae8f0c32..5b8333a9c6d 100644 --- a/homeassistant/components/peco/translations/nl.json +++ b/homeassistant/components/peco/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/pi_hole/translations/nl.json b/homeassistant/components/pi_hole/translations/nl.json index 4c3919506ef..8199969aae8 100644 --- a/homeassistant/components/pi_hole/translations/nl.json +++ b/homeassistant/components/pi_hole/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken" diff --git a/homeassistant/components/plugwise/translations/nl.json b/homeassistant/components/plugwise/translations/nl.json index 4dfd7d3b4e4..8109386013c 100644 --- a/homeassistant/components/plugwise/translations/nl.json +++ b/homeassistant/components/plugwise/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/pvpc_hourly_pricing/translations/nl.json b/homeassistant/components/pvpc_hourly_pricing/translations/nl.json index 9979909e040..a7d1ac0e743 100644 --- a/homeassistant/components/pvpc_hourly_pricing/translations/nl.json +++ b/homeassistant/components/pvpc_hourly_pricing/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/remote/translations/nl.json b/homeassistant/components/remote/translations/nl.json index b512f1b55aa..0c866181814 100644 --- a/homeassistant/components/remote/translations/nl.json +++ b/homeassistant/components/remote/translations/nl.json @@ -6,8 +6,8 @@ "turn_on": "{entity_name} inschakelen" }, "condition_type": { - "is_off": "{entity_name} staat uit", - "is_on": "{entity_name} staat aan" + "is_off": "{entity_name} is uitgeschakeld", + "is_on": "{entity_name} is ingeschakeld" }, "trigger_type": { "changed_states": "{entity_name} in- of uitgeschakeld", diff --git a/homeassistant/components/season/translations/nl.json b/homeassistant/components/season/translations/nl.json index 1da54d9c360..63067c8a814 100644 --- a/homeassistant/components/season/translations/nl.json +++ b/homeassistant/components/season/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/shelly/translations/id.json b/homeassistant/components/shelly/translations/id.json index 9ed75694de0..59af237d5d3 100644 --- a/homeassistant/components/shelly/translations/id.json +++ b/homeassistant/components/shelly/translations/id.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Gagal terhubung", + "firmware_not_fully_provisioned": "Perangkat belum sepenuhnya disediakan. Hubungi dukungan Shelly.", "invalid_auth": "Autentikasi tidak valid", "unknown": "Kesalahan yang tidak diharapkan" }, diff --git a/homeassistant/components/shopping_list/translations/nl.json b/homeassistant/components/shopping_list/translations/nl.json index e8de5fbae1d..6eb39857046 100644 --- a/homeassistant/components/shopping_list/translations/nl.json +++ b/homeassistant/components/shopping_list/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/simplisafe/translations/hu.json b/homeassistant/components/simplisafe/translations/hu.json index df71ba7ca8b..12f745b3b69 100644 --- a/homeassistant/components/simplisafe/translations/hu.json +++ b/homeassistant/components/simplisafe/translations/hu.json @@ -10,7 +10,7 @@ "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" }, "progress": { - "email_2fa": "Adja meg a k\u00e9tfaktoros hiteles\u00edt\u00e9si k\u00f3dot amelyet e-mailben kapott." + "email_2fa": "Ellen\u0151rizze az e-mailj\u00e9t a Simplisafe \u00e1ltal k\u00fcld\u00f6tt ellen\u0151rz\u0151 link\u00e9rt." }, "step": { "reauth_confirm": { diff --git a/homeassistant/components/slack/translations/nl.json b/homeassistant/components/slack/translations/nl.json index f531a126e9b..bb04a7dbf18 100644 --- a/homeassistant/components/slack/translations/nl.json +++ b/homeassistant/components/slack/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/sonarr/translations/nl.json b/homeassistant/components/sonarr/translations/nl.json index a5f289a1428..0ef71c521b0 100644 --- a/homeassistant/components/sonarr/translations/nl.json +++ b/homeassistant/components/sonarr/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "reauth_successful": "Herauthenticatie geslaagd", "unknown": "Onverwachte fout" }, diff --git a/homeassistant/components/sql/translations/nl.json b/homeassistant/components/sql/translations/nl.json index e93b63df296..594f589018d 100644 --- a/homeassistant/components/sql/translations/nl.json +++ b/homeassistant/components/sql/translations/nl.json @@ -15,7 +15,7 @@ "name": "Naam", "query": "Selecteer Query", "unit_of_measurement": "Meeteenheid", - "value_template": "Waarde Template" + "value_template": "Waardesjabloon" }, "data_description": { "column": "Kolom voor teruggestuurde query om als status te presenteren", @@ -23,7 +23,7 @@ "name": "Naam die zal worden gebruikt voor Config Entry en ook de Sensor", "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", "unit_of_measurement": "Meeteenheid (optioneel)", - "value_template": "Waarde Template (optioneel)" + "value_template": "Waardesjabloon (optioneel)" } } } @@ -41,7 +41,7 @@ "name": "Naam", "query": "Selecteer Query", "unit_of_measurement": "Meeteenheid", - "value_template": "Waarde Template" + "value_template": "Waardesjabloon" }, "data_description": { "column": "Kolom voor teruggestuurde query om als status te presenteren", @@ -49,7 +49,7 @@ "name": "Naam die zal worden gebruikt voor Config Entry en ook de Sensor", "query": "Query die moet worden uitgevoerd, moet beginnen met 'SELECT'", "unit_of_measurement": "Meeteenheid (optioneel)", - "value_template": "Waarde Template (optioneel)" + "value_template": "Waardesjabloon (optioneel)" } } } diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json index 9255b0e06ff..f94d083b76a 100644 --- a/homeassistant/components/steam_online/translations/nl.json +++ b/homeassistant/components/steam_online/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "reauth_successful": "Herauthenticatie geslaagd" }, "error": { diff --git a/homeassistant/components/stookalert/translations/nl.json b/homeassistant/components/stookalert/translations/nl.json index b6bd443c31b..fe8f84469a5 100644 --- a/homeassistant/components/stookalert/translations/nl.json +++ b/homeassistant/components/stookalert/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "step": { "user": { diff --git a/homeassistant/components/syncthing/translations/nl.json b/homeassistant/components/syncthing/translations/nl.json index 4f66222ada8..dd941e4bc75 100644 --- a/homeassistant/components/syncthing/translations/nl.json +++ b/homeassistant/components/syncthing/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/tellduslive/translations/nl.json b/homeassistant/components/tellduslive/translations/nl.json index 0f718304059..07ba94774f1 100644 --- a/homeassistant/components/tellduslive/translations/nl.json +++ b/homeassistant/components/tellduslive/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "authorize_url_timeout": "Time-out bij het genereren van autorisatie-URL.", "unknown": "Onverwachte fout", "unknown_authorize_url_generation": "Onbekende fout bij het genereren van een autorisatie-URL." diff --git a/homeassistant/components/tibber/translations/nl.json b/homeassistant/components/tibber/translations/nl.json index 622e35baea4..efa896e08e6 100644 --- a/homeassistant/components/tibber/translations/nl.json +++ b/homeassistant/components/tibber/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", diff --git a/homeassistant/components/ukraine_alarm/translations/hu.json b/homeassistant/components/ukraine_alarm/translations/hu.json index 64273f1348b..6bbbae1eca2 100644 --- a/homeassistant/components/ukraine_alarm/translations/hu.json +++ b/homeassistant/components/ukraine_alarm/translations/hu.json @@ -11,13 +11,13 @@ "step": { "community": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "R\u00e9gi\u00f3" }, "description": "Ha nem csak a megy\u00e9t \u00e9s a ker\u00fcletet szeretn\u00e9 figyelni, v\u00e1lassza ki az adott k\u00f6z\u00f6ss\u00e9get" }, "district": { "data": { - "region": "[%key:component::ukraine_alarm::config::step::state::data::region%]" + "region": "R\u00e9gi\u00f3" }, "description": "Ha nem csak megy\u00e9t szeretne figyelni, v\u00e1lassza ki az adott ker\u00fcletet" }, @@ -25,7 +25,7 @@ "data": { "region": "R\u00e9gi\u00f3" }, - "description": "\u00c1ll\u00edtsa be az Ukraine Alarm integr\u00e1ci\u00f3t. API-kulcs l\u00e9trehoz\u00e1s\u00e1hoz l\u00e1togasson el ide: {api_url}" + "description": "V\u00e1lassza ki a megfigyelni k\u00edv\u00e1nt megy\u00e9t" } } } diff --git a/homeassistant/components/ukraine_alarm/translations/nl.json b/homeassistant/components/ukraine_alarm/translations/nl.json index 97fd17e23e0..d9caa461588 100644 --- a/homeassistant/components/ukraine_alarm/translations/nl.json +++ b/homeassistant/components/ukraine_alarm/translations/nl.json @@ -25,7 +25,7 @@ "data": { "region": "Regio" }, - "description": "Kies staat om te monitoren" + "description": "De status kiezen om te monitoren" } } } diff --git a/homeassistant/components/unifi/translations/hu.json b/homeassistant/components/unifi/translations/hu.json index f06c30d5343..be817737b5a 100644 --- a/homeassistant/components/unifi/translations/hu.json +++ b/homeassistant/components/unifi/translations/hu.json @@ -2,7 +2,7 @@ "config": { "abort": { "already_configured": "Az UniFi Network webhely m\u00e1r konfigur\u00e1lva van", - "configuration_updated": "A konfigur\u00e1ci\u00f3 friss\u00edtve.", + "configuration_updated": "Konfigur\u00e1ci\u00f3 friss\u00edtve", "reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt." }, "error": { diff --git a/homeassistant/components/vallox/translations/nl.json b/homeassistant/components/vallox/translations/nl.json index 9b064500704..867efc3ddc9 100644 --- a/homeassistant/components/vallox/translations/nl.json +++ b/homeassistant/components/vallox/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_host": "Ongeldige hostnaam of IP-adres", "unknown": "Onverwachte fout" diff --git a/homeassistant/components/vlc_telnet/translations/nl.json b/homeassistant/components/vlc_telnet/translations/nl.json index 771ce98964d..87ddaffab2c 100644 --- a/homeassistant/components/vlc_telnet/translations/nl.json +++ b/homeassistant/components/vlc_telnet/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd", + "already_configured": "Dienst is al geconfigureerd", "cannot_connect": "Kan geen verbinding maken", "invalid_auth": "Ongeldige authenticatie", "reauth_successful": "Herauthenticatie geslaagd", diff --git a/homeassistant/components/whois/translations/nl.json b/homeassistant/components/whois/translations/nl.json index a6ad64881ef..714b9c9ddd9 100644 --- a/homeassistant/components/whois/translations/nl.json +++ b/homeassistant/components/whois/translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Service is al geconfigureerd" + "already_configured": "Dienst is al geconfigureerd" }, "error": { "unexpected_response": "Onverwacht antwoord van whois-server", diff --git a/homeassistant/components/wled/translations/select.nl.json b/homeassistant/components/wled/translations/select.nl.json index f2ac9da54cb..e93c165520a 100644 --- a/homeassistant/components/wled/translations/select.nl.json +++ b/homeassistant/components/wled/translations/select.nl.json @@ -3,7 +3,7 @@ "wled__live_override": { "0": "Uit", "1": "Aan", - "2": "Totdat het apparaat opnieuw opstart" + "2": "Totdat het apparaat opnieuw wordt opgestart" } } } \ No newline at end of file From d1afbbfb095a610e095b9f35904a52375a9a7a85 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 22 May 2022 11:27:25 +0200 Subject: [PATCH 677/930] Move manual configuration of MQTT vacuum to the integration key (#72281) Add vacuum Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + .../components/mqtt/vacuum/__init__.py | 39 +++++++++++++++++-- .../components/mqtt/vacuum/schema_legacy.py | 17 ++++++-- .../components/mqtt/vacuum/schema_state.py | 14 +++++-- tests/components/mqtt/test_legacy_vacuum.py | 13 +++++++ tests/components/mqtt/test_state_vacuum.py | 13 +++++++ 6 files changed, 86 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index dfef057a7f9..f2d826412ce 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -197,6 +197,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, + vol.Optional(Platform.VACUUM.value): cv.ensure_list, } ) diff --git a/homeassistant/components/mqtt/vacuum/__init__.py b/homeassistant/components/mqtt/vacuum/__init__.py index 4898a7b3351..34205ab7780 100644 --- a/homeassistant/components/mqtt/vacuum/__init__.py +++ b/homeassistant/components/mqtt/vacuum/__init__.py @@ -1,6 +1,7 @@ """Support for MQTT vacuums.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -11,16 +12,22 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from ..mixins import async_setup_entry_helper, async_setup_platform_helper +from ..mixins import ( + async_get_platform_config_from_yaml, + async_setup_entry_helper, + async_setup_platform_helper, +) from .schema import CONF_SCHEMA, LEGACY, MQTT_VACUUM_SCHEMA, STATE from .schema_legacy import ( DISCOVERY_SCHEMA_LEGACY, PLATFORM_SCHEMA_LEGACY, + PLATFORM_SCHEMA_LEGACY_MODERN, async_setup_entity_legacy, ) from .schema_state import ( DISCOVERY_SCHEMA_STATE, PLATFORM_SCHEMA_STATE, + PLATFORM_SCHEMA_STATE_MODERN, async_setup_entity_state, ) @@ -31,20 +38,35 @@ def validate_mqtt_vacuum_discovery(value): return schemas[value[CONF_SCHEMA]](value) +# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 def validate_mqtt_vacuum(value): - """Validate MQTT vacuum schema.""" + """Validate MQTT vacuum schema (deprecated).""" schemas = {LEGACY: PLATFORM_SCHEMA_LEGACY, STATE: PLATFORM_SCHEMA_STATE} return schemas[value[CONF_SCHEMA]](value) +def validate_mqtt_vacuum_modern(value): + """Validate MQTT vacuum modern schema.""" + schemas = { + LEGACY: PLATFORM_SCHEMA_LEGACY_MODERN, + STATE: PLATFORM_SCHEMA_STATE_MODERN, + } + return schemas[value[CONF_SCHEMA]](value) + + DISCOVERY_SCHEMA = vol.All( MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum_discovery ) +# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum ) +PLATFORM_SCHEMA_MODERN = vol.All( + MQTT_VACUUM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA), validate_mqtt_vacuum_modern +) + async def async_setup_platform( hass: HomeAssistant, @@ -53,6 +75,7 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up MQTT vacuum through configuration.yaml.""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, vacuum.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -63,7 +86,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT vacuum dynamically through MQTT discovery.""" + """Set up MQTT vacuum through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, vacuum.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/homeassistant/components/mqtt/vacuum/schema_legacy.py b/homeassistant/components/mqtt/vacuum/schema_legacy.py index 41d0c9faf17..eb5e01b6251 100644 --- a/homeassistant/components/mqtt/vacuum/schema_legacy.py +++ b/homeassistant/components/mqtt/vacuum/schema_legacy.py @@ -5,6 +5,7 @@ import voluptuous as vol from homeassistant.components.vacuum import ( ATTR_STATUS, + DOMAIN as VACUUM_DOMAIN, ENTITY_ID_FORMAT, VacuumEntity, VacuumEntityFeature, @@ -18,7 +19,7 @@ from .. import MqttValueTemplate, subscription from ... import mqtt from ..const import CONF_COMMAND_TOPIC, CONF_ENCODING, CONF_QOS, CONF_RETAIN from ..debug_info import log_messages -from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -94,8 +95,8 @@ MQTT_LEGACY_VACUUM_ATTRIBUTES_BLOCKED = MQTT_VACUUM_ATTRIBUTES_BLOCKED | frozens {ATTR_STATUS} ) -PLATFORM_SCHEMA_LEGACY = ( - mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_LEGACY_MODERN = ( + mqtt.MQTT_BASE_SCHEMA.extend( { vol.Inclusive(CONF_BATTERY_LEVEL_TEMPLATE, "battery"): cv.template, vol.Inclusive( @@ -147,7 +148,15 @@ PLATFORM_SCHEMA_LEGACY = ( .extend(MQTT_VACUUM_SCHEMA.schema) ) -DISCOVERY_SCHEMA_LEGACY = PLATFORM_SCHEMA_LEGACY.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA_LEGACY = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_LEGACY_MODERN.schema), + warn_for_legacy_schema(VACUUM_DOMAIN), +) + +DISCOVERY_SCHEMA_LEGACY = PLATFORM_SCHEMA_LEGACY_MODERN.extend( + {}, extra=vol.REMOVE_EXTRA +) async def async_setup_entity_legacy( diff --git a/homeassistant/components/mqtt/vacuum/schema_state.py b/homeassistant/components/mqtt/vacuum/schema_state.py index 7a5424d7cbf..7aa7be07797 100644 --- a/homeassistant/components/mqtt/vacuum/schema_state.py +++ b/homeassistant/components/mqtt/vacuum/schema_state.py @@ -4,6 +4,7 @@ import json import voluptuous as vol from homeassistant.components.vacuum import ( + DOMAIN as VACUUM_DOMAIN, ENTITY_ID_FORMAT, STATE_CLEANING, STATE_DOCKED, @@ -31,7 +32,7 @@ from ..const import ( CONF_STATE_TOPIC, ) from ..debug_info import log_messages -from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity +from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, warn_for_legacy_schema from .const import MQTT_VACUUM_ATTRIBUTES_BLOCKED from .schema import MQTT_VACUUM_SCHEMA, services_to_strings, strings_to_services @@ -103,8 +104,8 @@ DEFAULT_PAYLOAD_LOCATE = "locate" DEFAULT_PAYLOAD_START = "start" DEFAULT_PAYLOAD_PAUSE = "pause" -PLATFORM_SCHEMA_STATE = ( - mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_STATE_MODERN = ( + mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_FAN_SPEED_LIST, default=[]): vol.All( cv.ensure_list, [cv.string] @@ -136,8 +137,13 @@ PLATFORM_SCHEMA_STATE = ( .extend(MQTT_VACUUM_SCHEMA.schema) ) +# Configuring MQTT Vacuums under the vacuum platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA_STATE = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_STATE_MODERN.schema), + warn_for_legacy_schema(VACUUM_DOMAIN), +) -DISCOVERY_SCHEMA_STATE = PLATFORM_SCHEMA_STATE.extend({}, extra=vol.REMOVE_EXTRA) +DISCOVERY_SCHEMA_STATE = PLATFORM_SCHEMA_STATE_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_entity_state( diff --git a/tests/components/mqtt/test_legacy_vacuum.py b/tests/components/mqtt/test_legacy_vacuum.py index 992275105c5..f451079e0f0 100644 --- a/tests/components/mqtt/test_legacy_vacuum.py +++ b/tests/components/mqtt/test_legacy_vacuum.py @@ -56,6 +56,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -901,3 +902,15 @@ async def test_encoding_subscribable_topics( attribute_value, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = vacuum.DOMAIN + config = deepcopy(DEFAULT_CONFIG) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None diff --git a/tests/components/mqtt/test_state_vacuum.py b/tests/components/mqtt/test_state_vacuum.py index 8691aa73323..3f752f1b528 100644 --- a/tests/components/mqtt/test_state_vacuum.py +++ b/tests/components/mqtt/test_state_vacuum.py @@ -58,6 +58,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -648,3 +649,15 @@ async def test_encoding_subscribable_topics( attribute_value, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = vacuum.DOMAIN + config = deepcopy(DEFAULT_CONFIG) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From e8664ce1aee95bb71f941be9de83f8f2bb6a74de Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 22 May 2022 11:28:10 +0200 Subject: [PATCH 678/930] Move manual configuration of MQTT climate to the integration key (#72251) Add climate Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/climate.py | 42 +++++++++++++++-------- tests/components/mqtt/test_climate.py | 13 +++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index f2d826412ce..8a27ff7d759 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -194,6 +194,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.BINARY_SENSOR.value): cv.ensure_list, vol.Optional(Platform.BUTTON.value): cv.ensure_list, vol.Optional(Platform.CAMERA.value): cv.ensure_list, + vol.Optional(Platform.CLIMATE.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/climate.py b/homeassistant/components/mqtt/climate.py index c095054ae9b..52465bbba24 100644 --- a/homeassistant/components/mqtt/climate.py +++ b/homeassistant/components/mqtt/climate.py @@ -1,16 +1,14 @@ """Support for MQTT climate devices.""" from __future__ import annotations +import asyncio import functools import logging import voluptuous as vol from homeassistant.components import climate -from homeassistant.components.climate import ( - PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA, - ClimateEntity, -) +from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate.const import ( ATTR_HVAC_MODE, ATTR_TARGET_TEMP_HIGH, @@ -46,20 +44,17 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import ( - MQTT_BASE_PLATFORM_SCHEMA, - MqttCommandTemplate, - MqttValueTemplate, - subscription, -) +from . import MqttCommandTemplate, MqttValueTemplate, subscription from .. import mqtt from .const import CONF_ENCODING, CONF_QOS, CONF_RETAIN, PAYLOAD_NONE from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -237,8 +232,7 @@ def valid_preset_mode_configuration(config): return config -SCHEMA_BASE = CLIMATE_PLATFORM_SCHEMA.extend(MQTT_BASE_PLATFORM_SCHEMA.schema) -_PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_AUX_STATE_TEMPLATE): cv.template, @@ -330,8 +324,14 @@ _PLATFORM_SCHEMA_BASE = SCHEMA_BASE.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -PLATFORM_SCHEMA = vol.All( +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, + valid_preset_mode_configuration, +) + +# Configuring MQTT Climate under the climate platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), # CONF_SEND_IF_OFF is deprecated, support will be removed with release 2022.9 cv.deprecated(CONF_SEND_IF_OFF), # AWAY and HOLD mode topics and templates are deprecated, support will be removed with release 2022.9 @@ -344,6 +344,7 @@ PLATFORM_SCHEMA = vol.All( cv.deprecated(CONF_HOLD_STATE_TOPIC), cv.deprecated(CONF_HOLD_LIST), valid_preset_mode_configuration, + warn_for_legacy_schema(climate.DOMAIN), ) _DISCOVERY_SCHEMA_BASE = _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA) @@ -371,7 +372,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT climate device through configuration.yaml.""" + """Set up MQTT climate configured under the fan platform key (deprecated).""" + # The use of PLATFORM_SCHEMA is deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, climate.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -382,7 +384,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT climate device dynamically through MQTT discovery.""" + """Set up MQTT climate device through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, climate.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_climate.py b/tests/components/mqtt/test_climate.py index af6d65ac490..98af86248e4 100644 --- a/tests/components/mqtt/test_climate.py +++ b/tests/components/mqtt/test_climate.py @@ -53,6 +53,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1761,3 +1762,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = CLIMATE_DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = CLIMATE_DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 87d895929f4b586a6648ce86bd0a76b4ca51efd1 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 22 May 2022 13:06:49 +0200 Subject: [PATCH 679/930] Move manual configuration of MQTT switch to the integration key (#72279) Add switch Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/switch.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_switch.py | 13 +++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 8a27ff7d759..54d7dc8feb9 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -198,6 +198,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, + vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, } ) diff --git a/homeassistant/components/mqtt/switch.py b/homeassistant/components/mqtt/switch.py index db54ae08953..f5f8363eb33 100644 --- a/homeassistant/components/mqtt/switch.py +++ b/homeassistant/components/mqtt/switch.py @@ -1,6 +1,7 @@ """Support for MQTT switches.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -37,8 +38,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) DEFAULT_NAME = "MQTT Switch" @@ -48,7 +51,7 @@ DEFAULT_OPTIMISTIC = False CONF_STATE_ON = "state_on" CONF_STATE_OFF = "state_off" -PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, @@ -61,7 +64,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Switches under the switch platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(switch.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -70,7 +79,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT switch through configuration.yaml.""" + """Set up MQTT switch configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, switch.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -81,7 +91,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT switch dynamically through MQTT discovery.""" + """Set up MQTT switch through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, switch.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_switch.py b/tests/components/mqtt/test_switch.py index a7dd0d8c31e..699f0de87f0 100644 --- a/tests/components/mqtt/test_switch.py +++ b/tests/components/mqtt/test_switch.py @@ -39,6 +39,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -585,3 +586,15 @@ async def test_encoding_subscribable_topics( attribute, attribute_value, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = switch.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 3c3e394972af7dc5080ba439dee4d93865fa15d6 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 22 May 2022 13:07:14 +0200 Subject: [PATCH 680/930] Move manual configuration of MQTT cover to the integration key (#72268) Add cover Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/cover.py | 29 +++++++++++++++++++---- tests/components/mqtt/test_cover.py | 15 ++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 54d7dc8feb9..857580a1f88 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -195,6 +195,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.BUTTON.value): cv.ensure_list, vol.Optional(Platform.CAMERA.value): cv.ensure_list, vol.Optional(Platform.CLIMATE.value): cv.ensure_list, + vol.Optional(Platform.COVER.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/cover.py b/homeassistant/components/mqtt/cover.py index 76f984f1bc9..8e36329946a 100644 --- a/homeassistant/components/mqtt/cover.py +++ b/homeassistant/components/mqtt/cover.py @@ -1,6 +1,7 @@ """Support for MQTT cover devices.""" from __future__ import annotations +import asyncio import functools from json import JSONDecodeError, loads as json_loads import logging @@ -45,8 +46,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -149,7 +152,7 @@ def validate_options(value): return value -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, @@ -194,11 +197,18 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -PLATFORM_SCHEMA = vol.All( +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, validate_options, ) +# Configuring MQTT Covers under the cover platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), + validate_options, + warn_for_legacy_schema(cover.DOMAIN), +) + DISCOVERY_SCHEMA = vol.All( cv.removed("tilt_invert_state"), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), @@ -212,7 +222,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT cover through configuration.yaml.""" + """Set up MQTT covers configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, cover.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -223,7 +234,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT cover dynamically through MQTT discovery.""" + """Set up MQTT cover through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, cover.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_cover.py b/tests/components/mqtt/test_cover.py index aad6fa5d9ca..285af765ab4 100644 --- a/tests/components/mqtt/test_cover.py +++ b/tests/components/mqtt/test_cover.py @@ -1,4 +1,6 @@ """The tests for the MQTT cover platform.""" + +import copy from unittest.mock import patch import pytest @@ -68,6 +70,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -3206,3 +3209,15 @@ async def test_encoding_subscribable_topics( attribute_value, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = cover.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 4153edbcdd9660120e148f49f7b1be6bf1fac3e1 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Sun, 22 May 2022 13:07:49 +0200 Subject: [PATCH 681/930] Move manual configuration of MQTT humidifier to the integration key (#72270) Add humidifier --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/humidifier.py | 27 ++++++++++++++++++--- tests/components/mqtt/test_humidifier.py | 13 ++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 857580a1f88..426fad45db1 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -197,6 +197,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.CLIMATE.value): cv.ensure_list, vol.Optional(Platform.COVER.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, + vol.Optional(Platform.HUMIDIFIER.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index f5a8b372532..f6d4aa01dab 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -1,6 +1,7 @@ """Support for MQTT humidifiers.""" from __future__ import annotations +import asyncio import functools import logging @@ -45,8 +46,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) CONF_AVAILABLE_MODES_LIST = "modes" @@ -100,7 +103,7 @@ def valid_humidity_range_configuration(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( { # CONF_AVAIALABLE_MODES_LIST and CONF_MODE_COMMAND_TOPIC must be used together vol.Inclusive( @@ -140,7 +143,15 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) +# Configuring MQTT Humidifiers under the humidifier platform key is deprecated in HA Core 2022.6 PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), + valid_humidity_range_configuration, + valid_mode_configuration, + warn_for_legacy_schema(humidifier.DOMAIN), +) + +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, valid_humidity_range_configuration, valid_mode_configuration, @@ -159,7 +170,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT humidifier through configuration.yaml.""" + """Set up MQTT humidifier configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, humidifier.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -170,7 +182,16 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT humidifier dynamically through MQTT discovery.""" + """Set up MQTT humidifier through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, humidifier.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_humidifier.py b/tests/components/mqtt/test_humidifier.py index 4aa5ff2350b..bc00d3afffb 100644 --- a/tests/components/mqtt/test_humidifier.py +++ b/tests/components/mqtt/test_humidifier.py @@ -56,6 +56,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1182,3 +1183,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = humidifier.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = humidifier.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From d9732ec78702fc0e5786b9cc7bb2512f58660aa5 Mon Sep 17 00:00:00 2001 From: Matrix Date: Sun, 22 May 2022 19:20:29 +0800 Subject: [PATCH 682/930] Add yolink outlet (#72247) * Add yolink outlet * add .coveragerc * buf fix * suggest fix * suggest fix --- .coveragerc | 1 + homeassistant/components/yolink/__init__.py | 2 +- homeassistant/components/yolink/const.py | 1 + homeassistant/components/yolink/switch.py | 114 ++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/yolink/switch.py diff --git a/.coveragerc b/.coveragerc index 6cc9ffda714..2d530ab8f45 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1477,6 +1477,7 @@ omit = homeassistant/components/yolink/coordinator.py homeassistant/components/yolink/entity.py homeassistant/components/yolink/sensor.py + homeassistant/components/yolink/switch.py homeassistant/components/youless/__init__.py homeassistant/components/youless/const.py homeassistant/components/youless/sensor.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 8a8afb2f1b3..089f0cc79b9 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -21,7 +21,7 @@ SCAN_INTERVAL = timedelta(minutes=5) _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index 303fd684c02..b94727cb948 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -17,3 +17,4 @@ ATTR_DEVICE_DOOR_SENSOR = "DoorSensor" ATTR_DEVICE_TH_SENSOR = "THSensor" ATTR_DEVICE_MOTION_SENSOR = "MotionSensor" ATTR_DEVICE_LEAK_SENSOR = "LeakSensor" +ATTR_DEVICE_OUTLET = "Outlet" diff --git a/homeassistant/components/yolink/switch.py b/homeassistant/components/yolink/switch.py new file mode 100644 index 00000000000..b3756efb74c --- /dev/null +++ b/homeassistant/components/yolink/switch.py @@ -0,0 +1,114 @@ +"""YoLink Switch.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + +from yolink.device import YoLinkDevice +from yolink.exception import YoLinkAuthFailError, YoLinkClientError + +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATOR, ATTR_DEVICE_OUTLET, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +@dataclass +class YoLinkSwitchEntityDescription(SwitchEntityDescription): + """YoLink SwitchEntityDescription.""" + + exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True + value: Callable[[str], bool | None] = lambda _: None + + +DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = ( + YoLinkSwitchEntityDescription( + key="state", + device_class=SwitchDeviceClass.OUTLET, + name="State", + value=lambda value: value == "open", + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_OUTLET], + ), +) + +DEVICE_TYPE = [ATTR_DEVICE_OUTLET] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink Sensor from a config entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] + devices = [ + device for device in coordinator.yl_devices if device.device_type in DEVICE_TYPE + ] + entities = [] + for device in devices: + for description in DEVICE_TYPES: + if description.exists_fn(device): + entities.append( + YoLinkSwitchEntity(config_entry, coordinator, description, device) + ) + async_add_entities(entities) + + +class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity): + """YoLink Switch Entity.""" + + entity_description: YoLinkSwitchEntityDescription + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: YoLinkCoordinator, + description: YoLinkSwitchEntityDescription, + device: YoLinkDevice, + ) -> None: + """Init YoLink Outlet.""" + super().__init__(coordinator, device) + self.config_entry = config_entry + self.entity_description = description + self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" + self._attr_name = f"{device.device_name} ({self.entity_description.name})" + + @callback + def update_entity_state(self, state: dict) -> None: + """Update HA Entity State.""" + self._attr_is_on = self.entity_description.value( + state[self.entity_description.key] + ) + self.async_write_ha_state() + + async def call_state_change(self, state: str) -> None: + """Call setState api to change outlet state.""" + try: + # call_device_http_api will check result, fail by raise YoLinkClientError + await self.device.call_device_http_api("setState", {"state": state}) + except YoLinkAuthFailError as yl_auth_err: + self.config_entry.async_start_reauth(self.hass) + raise HomeAssistantError(yl_auth_err) from yl_auth_err + except YoLinkClientError as yl_client_err: + self.coordinator.last_update_success = False + raise HomeAssistantError(yl_client_err) from yl_client_err + self._attr_is_on = self.entity_description.value(state) + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the entity on.""" + await self.call_state_change("open") + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity off.""" + await self.call_state_change("close") From 16088915ebe35e5d287955a20a2fee6db8c0904d Mon Sep 17 00:00:00 2001 From: Matrix Date: Mon, 23 May 2022 00:57:09 +0800 Subject: [PATCH 683/930] Fix yolink binary sensor (#72304) * Fix yolink binary sensor * suggest fix --- homeassistant/components/yolink/binary_sensor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/yolink/binary_sensor.py b/homeassistant/components/yolink/binary_sensor.py index 494a7ff10eb..42899e08a2c 100644 --- a/homeassistant/components/yolink/binary_sensor.py +++ b/homeassistant/components/yolink/binary_sensor.py @@ -31,7 +31,7 @@ class YoLinkBinarySensorEntityDescription(BinarySensorEntityDescription): """YoLink BinarySensorEntityDescription.""" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True - key: str = "state" + state_key: str = "state" value: Callable[[str], bool | None] = lambda _: None @@ -43,6 +43,7 @@ SENSOR_DEVICE_TYPE = [ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( YoLinkBinarySensorEntityDescription( + key="door_state", icon="mdi:door", device_class=BinarySensorDeviceClass.DOOR, name="State", @@ -50,12 +51,14 @@ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], ), YoLinkBinarySensorEntityDescription( + key="motion_state", device_class=BinarySensorDeviceClass.MOTION, name="Motion", value=lambda value: value == "alert", exists_fn=lambda device: device.device_type in [ATTR_DEVICE_MOTION_SENSOR], ), YoLinkBinarySensorEntityDescription( + key="leak_state", name="Leak", icon="mdi:water", device_class=BinarySensorDeviceClass.MOISTURE, @@ -108,6 +111,6 @@ class YoLinkBinarySensorEntity(YoLinkEntity, BinarySensorEntity): def update_entity_state(self, state: dict) -> None: """Update HA Entity State.""" self._attr_is_on = self.entity_description.value( - state[self.entity_description.key] + state[self.entity_description.state_key] ) self.async_write_ha_state() From ead6e7da1f0855963f2b3b4fa6739ea1cbd39d16 Mon Sep 17 00:00:00 2001 From: Andre Lengwenus Date: Sun, 22 May 2022 18:59:44 +0200 Subject: [PATCH 684/930] Purge entity and device registries when importing lcn from configuration.yaml (#54266) * Identify LCN orphans in entity registry and device registry and remove them * Fix typing issues * Revert "Fix typing issues" This reverts commit eccd067b3b5f23135e6c8a79d25f7f2cbc2d0ae9. * Fix removal of devices which do not belong to given config_entry * Use helper for getting entities for config_entry * Rename variable --- homeassistant/components/lcn/config_flow.py | 17 ++--- homeassistant/components/lcn/helpers.py | 71 ++++++++++++++++++++- tests/components/lcn/test_config_flow.py | 4 ++ 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/lcn/config_flow.py b/homeassistant/components/lcn/config_flow.py index 924ff5b278c..a8c5a311971 100644 --- a/homeassistant/components/lcn/config_flow.py +++ b/homeassistant/components/lcn/config_flow.py @@ -2,7 +2,6 @@ from __future__ import annotations import logging -from typing import Any import pypck @@ -16,15 +15,16 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResult -from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.typing import ConfigType from .const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DOMAIN +from .helpers import purge_device_registry, purge_entity_registry _LOGGER = logging.getLogger(__name__) def get_config_entry( - hass: HomeAssistant, data: dict[str, Any] + hass: HomeAssistant, data: ConfigType ) -> config_entries.ConfigEntry | None: """Check config entries for already configured entries based on the ip address/port.""" return next( @@ -38,7 +38,7 @@ def get_config_entry( ) -async def validate_connection(host_name: str, data: dict[str, Any]) -> dict[str, Any]: +async def validate_connection(host_name: str, data: ConfigType) -> ConfigType: """Validate if a connection to LCN can be established.""" host = data[CONF_IP_ADDRESS] port = data[CONF_PORT] @@ -70,7 +70,7 @@ class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def async_step_import(self, data: dict[str, Any]) -> FlowResult: + async def async_step_import(self, data: ConfigType) -> FlowResult: """Import existing configuration from LCN.""" host_name = data[CONF_HOST] # validate the imported connection parameters @@ -93,13 +93,10 @@ class LcnFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # check if we already have a host with the same address configured if entry := get_config_entry(self.hass, data): entry.source = config_entries.SOURCE_IMPORT - # Cleanup entity and device registry, if we imported from configuration.yaml to # remove orphans when entities were removed from configuration - entity_registry = er.async_get(self.hass) - entity_registry.async_clear_config_entry(entry.entry_id) - device_registry = dr.async_get(self.hass) - device_registry.async_clear_config_entry(entry.entry_id) + purge_entity_registry(self.hass, entry.entry_id, data) + purge_device_registry(self.hass, entry.entry_id, data) self.hass.config_entries.async_update_entry(entry, data=data) return self.async_abort(reason="existing_configuration_updated") diff --git a/homeassistant/components/lcn/helpers.py b/homeassistant/components/lcn/helpers.py index 0c8edd9b852..e9fb2683f4f 100644 --- a/homeassistant/components/lcn/helpers.py +++ b/homeassistant/components/lcn/helpers.py @@ -30,7 +30,7 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.typing import ConfigType from .const import ( @@ -247,6 +247,75 @@ def import_lcn_config(lcn_config: ConfigType) -> list[ConfigType]: return list(data.values()) +def purge_entity_registry( + hass: HomeAssistant, entry_id: str, imported_entry_data: ConfigType +) -> None: + """Remove orphans from entity registry which are not in entry data.""" + entity_registry = er.async_get(hass) + + # Find all entities that are referenced in the config entry. + references_config_entry = { + entity_entry.entity_id + for entity_entry in er.async_entries_for_config_entry(entity_registry, entry_id) + } + + # Find all entities that are referenced by the entry_data. + references_entry_data = set() + for entity_data in imported_entry_data[CONF_ENTITIES]: + entity_unique_id = generate_unique_id( + entry_id, entity_data[CONF_ADDRESS], entity_data[CONF_RESOURCE] + ) + entity_id = entity_registry.async_get_entity_id( + entity_data[CONF_DOMAIN], DOMAIN, entity_unique_id + ) + if entity_id is not None: + references_entry_data.add(entity_id) + + orphaned_ids = references_config_entry - references_entry_data + for orphaned_id in orphaned_ids: + entity_registry.async_remove(orphaned_id) + + +def purge_device_registry( + hass: HomeAssistant, entry_id: str, imported_entry_data: ConfigType +) -> None: + """Remove orphans from device registry which are not in entry data.""" + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) + + # Find all devices that are referenced in the entity registry. + references_entities = { + entry.device_id for entry in entity_registry.entities.values() + } + + # Find device that references the host. + references_host = set() + host_device = device_registry.async_get_device({(DOMAIN, entry_id)}) + if host_device is not None: + references_host.add(host_device.id) + + # Find all devices that are referenced by the entry_data. + references_entry_data = set() + for device_data in imported_entry_data[CONF_DEVICES]: + device_unique_id = generate_unique_id(entry_id, device_data[CONF_ADDRESS]) + device = device_registry.async_get_device({(DOMAIN, device_unique_id)}) + if device is not None: + references_entry_data.add(device.id) + + orphaned_ids = ( + { + entry.id + for entry in dr.async_entries_for_config_entry(device_registry, entry_id) + } + - references_entities + - references_host + - references_entry_data + ) + + for device_id in orphaned_ids: + device_registry.async_remove_device(device_id) + + def register_lcn_host_device(hass: HomeAssistant, config_entry: ConfigEntry) -> None: """Register LCN host for given config_entry in device registry.""" device_registry = dr.async_get(hass) diff --git a/tests/components/lcn/test_config_flow.py b/tests/components/lcn/test_config_flow.py index 49351b023b6..36b5d23739a 100644 --- a/tests/components/lcn/test_config_flow.py +++ b/tests/components/lcn/test_config_flow.py @@ -7,6 +7,8 @@ import pytest from homeassistant import config_entries, data_entry_flow from homeassistant.components.lcn.const import CONF_DIM_MODE, CONF_SK_NUM_TRIES, DOMAIN from homeassistant.const import ( + CONF_DEVICES, + CONF_ENTITIES, CONF_HOST, CONF_IP_ADDRESS, CONF_PASSWORD, @@ -24,6 +26,8 @@ IMPORT_DATA = { CONF_PASSWORD: "lcn", CONF_SK_NUM_TRIES: 0, CONF_DIM_MODE: "STEPS200", + CONF_DEVICES: [], + CONF_ENTITIES: [], } From 3390d62b3dffb13fc729016bb470cc6cac6a5831 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Sun, 22 May 2022 20:53:00 +0200 Subject: [PATCH 685/930] Revert "Adjust device_automation type hints in deconz" (#72323) Revert "Adjust device_automation type hints in deconz (#72194)" This reverts commit a57697d6e98a5a267f8eb60cb333b6a8dcc360c3. --- homeassistant/components/deconz/device_trigger.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 2ab1b2e6544..43ed3b2cdc4 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -1,16 +1,16 @@ """Provides device automations for deconz events.""" + from __future__ import annotations +from typing import Any + import voluptuous as vol from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -642,8 +642,9 @@ def _get_deconz_event_from_device( async def async_validate_trigger_config( - hass: HomeAssistant, config: ConfigType -) -> ConfigType: + hass: HomeAssistant, + config: dict[str, Any], +) -> vol.Schema: """Validate config.""" config = TRIGGER_SCHEMA(config) @@ -702,7 +703,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str, -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers. Make sure device is a supported remote model. From 9c3f9491651f409e8b4d0d645115b55b14f06165 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 May 2022 14:57:54 -0500 Subject: [PATCH 686/930] Add live streaming logbook websocket endpoint (#72258) Co-authored-by: Paulus Schoutsen --- homeassistant/components/logbook/__init__.py | 732 +---------- homeassistant/components/logbook/const.py | 39 + homeassistant/components/logbook/helpers.py | 193 +++ homeassistant/components/logbook/models.py | 113 ++ homeassistant/components/logbook/processor.py | 488 +++++++ homeassistant/components/logbook/rest_api.py | 120 ++ .../components/logbook/websocket_api.py | 319 +++++ homeassistant/components/recorder/core.py | 7 + homeassistant/components/recorder/tasks.py | 14 + .../components/websocket_api/messages.py | 2 +- homeassistant/core.py | 5 +- tests/components/logbook/common.py | 21 +- tests/components/logbook/test_init.py | 22 +- .../components/logbook/test_websocket_api.py | 1166 +++++++++++++++++ tests/components/recorder/common.py | 31 +- tests/components/recorder/test_init.py | 23 + tests/test_core.py | 30 + 17 files changed, 2592 insertions(+), 733 deletions(-) create mode 100644 homeassistant/components/logbook/const.py create mode 100644 homeassistant/components/logbook/helpers.py create mode 100644 homeassistant/components/logbook/models.py create mode 100644 homeassistant/components/logbook/processor.py create mode 100644 homeassistant/components/logbook/rest_api.py create mode 100644 homeassistant/components/logbook/websocket_api.py create mode 100644 tests/components/logbook/test_websocket_api.py diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 635868310f6..d9893781748 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -1,63 +1,25 @@ """Event parser and human readable log generator.""" from __future__ import annotations -from collections.abc import Callable, Generator -from contextlib import suppress -from datetime import datetime as dt, timedelta -from http import HTTPStatus -import json -import logging -import re -from typing import Any, cast +from collections.abc import Callable +from typing import Any -from aiohttp import web -from sqlalchemy.engine.row import Row -from sqlalchemy.orm.query import Query import voluptuous as vol -from homeassistant.components import frontend, websocket_api -from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED -from homeassistant.components.http import HomeAssistantView -from homeassistant.components.recorder import get_instance +from homeassistant.components import frontend from homeassistant.components.recorder.filters import ( - Filters, sqlalchemy_filter_from_include_exclude_conf, ) -from homeassistant.components.recorder.models import ( - process_datetime_to_timestamp, - process_timestamp_to_utc_isoformat, -) -from homeassistant.components.recorder.util import session_scope -from homeassistant.components.script import EVENT_SCRIPT_STARTED -from homeassistant.components.sensor import ATTR_STATE_CLASS, DOMAIN as SENSOR_DOMAIN -from homeassistant.components.websocket_api import messages -from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, - ATTR_FRIENDLY_NAME, ATTR_NAME, - ATTR_SERVICE, - EVENT_CALL_SERVICE, EVENT_LOGBOOK_ENTRY, ) -from homeassistant.core import ( - Context, - Event, - HomeAssistant, - ServiceCall, - callback, - split_entity_id, -) -from homeassistant.exceptions import InvalidEntityFormatError -from homeassistant.helpers import ( - config_validation as cv, - device_registry as dr, - entity_registry as er, -) +from homeassistant.core import Context, Event, HomeAssistant, ServiceCall, callback +from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entityfilter import ( INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, - EntityFilter, convert_include_exclude_filter, ) from homeassistant.helpers.integration_platform import ( @@ -65,47 +27,25 @@ from homeassistant.helpers.integration_platform import ( ) from homeassistant.helpers.typing import ConfigType from homeassistant.loader import bind_hass -import homeassistant.util.dt as dt_util -from .queries import statement_for_request -from .queries.common import PSUEDO_EVENT_STATE_CHANGED - -_LOGGER = logging.getLogger(__name__) - -ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') -DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') -ATTR_MESSAGE = "message" - -DOMAIN = "logbook" +from . import rest_api, websocket_api +from .const import ( + ATTR_MESSAGE, + DOMAIN, + LOGBOOK_ENTITIES_FILTER, + LOGBOOK_ENTRY_DOMAIN, + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, + LOGBOOK_FILTERS, +) +from .const import LOGBOOK_ENTRY_ICON # noqa: F401 +from .models import LazyEventPartialState # noqa: F401 CONFIG_SCHEMA = vol.Schema( {DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA ) -CONTEXT_USER_ID = "context_user_id" -CONTEXT_ENTITY_ID = "context_entity_id" -CONTEXT_ENTITY_ID_NAME = "context_entity_id_name" -CONTEXT_EVENT_TYPE = "context_event_type" -CONTEXT_DOMAIN = "context_domain" -CONTEXT_STATE = "context_state" -CONTEXT_SERVICE = "context_service" -CONTEXT_NAME = "context_name" -CONTEXT_MESSAGE = "context_message" - -LOGBOOK_ENTRY_DOMAIN = "domain" -LOGBOOK_ENTRY_ENTITY_ID = "entity_id" -LOGBOOK_ENTRY_ICON = "icon" -LOGBOOK_ENTRY_MESSAGE = "message" -LOGBOOK_ENTRY_NAME = "name" -LOGBOOK_ENTRY_STATE = "state" -LOGBOOK_ENTRY_WHEN = "when" - -ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE} -ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY = { - EVENT_LOGBOOK_ENTRY, - EVENT_AUTOMATION_TRIGGERED, - EVENT_SCRIPT_STARTED, -} LOG_MESSAGE_SCHEMA = vol.Schema( { @@ -117,10 +57,6 @@ LOG_MESSAGE_SCHEMA = vol.Schema( ) -LOGBOOK_FILTERS = "logbook_filters" -LOGBOOK_ENTITIES_FILTER = "entities_filter" - - @bind_hass def log_entry( hass: HomeAssistant, @@ -186,13 +122,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: else: filters = None entities_filter = None - hass.data[LOGBOOK_FILTERS] = filters hass.data[LOGBOOK_ENTITIES_FILTER] = entities_filter - - hass.http.register_view(LogbookView(conf, filters, entities_filter)) - websocket_api.async_register_command(hass, ws_get_events) - + websocket_api.async_setup(hass) + rest_api.async_setup(hass, config, filters, entities_filter) hass.services.async_register(DOMAIN, "log", log_message, schema=LOG_MESSAGE_SCHEMA) await async_process_integration_platforms(hass, DOMAIN, _process_logbook_platform) @@ -215,628 +148,3 @@ async def _process_logbook_platform( hass.data[DOMAIN][event_name] = (domain, describe_callback) platform.async_describe_events(hass, _async_describe_event) - - -def _async_determine_event_types( - hass: HomeAssistant, entity_ids: list[str] | None, device_ids: list[str] | None -) -> tuple[str, ...]: - """Reduce the event types based on the entity ids and device ids.""" - external_events: dict[ - str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] - ] = hass.data.get(DOMAIN, {}) - if not entity_ids and not device_ids: - return (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) - config_entry_ids: set[str] = set() - intrested_event_types: set[str] = set() - - if entity_ids: - # - # Home Assistant doesn't allow firing events from - # entities so we have a limited list to check - # - # automations and scripts can refer to entities - # but they do not have a config entry so we need - # to add them. - # - # We also allow entity_ids to be recorded via - # manual logbook entries. - # - intrested_event_types |= ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY - - if device_ids: - dev_reg = dr.async_get(hass) - for device_id in device_ids: - if (device := dev_reg.async_get(device_id)) and device.config_entries: - config_entry_ids |= device.config_entries - interested_domains: set[str] = set() - for entry_id in config_entry_ids: - if entry := hass.config_entries.async_get_entry(entry_id): - interested_domains.add(entry.domain) - for external_event, domain_call in external_events.items(): - if domain_call[0] in interested_domains: - intrested_event_types.add(external_event) - - return tuple( - event_type - for event_type in (EVENT_LOGBOOK_ENTRY, *external_events) - if event_type in intrested_event_types - ) - - -def _ws_formatted_get_events( - hass: HomeAssistant, - msg_id: int, - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - ent_reg: er.EntityRegistry, - entity_ids: list[str] | None = None, - device_ids: list[str] | None = None, - filters: Filters | None = None, - entities_filter: EntityFilter | Callable[[str], bool] | None = None, - context_id: str | None = None, -) -> str: - """Fetch events and convert them to json in the executor.""" - return JSON_DUMP( - messages.result_message( - msg_id, - _get_events( - hass, - start_day, - end_day, - event_types, - ent_reg, - entity_ids, - device_ids, - filters, - entities_filter, - context_id, - True, - False, - ), - ) - ) - - -@websocket_api.websocket_command( - { - vol.Required("type"): "logbook/get_events", - vol.Required("start_time"): str, - vol.Optional("end_time"): str, - vol.Optional("entity_ids"): [str], - vol.Optional("device_ids"): [str], - vol.Optional("context_id"): str, - } -) -@websocket_api.async_response -async def ws_get_events( - hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict -) -> None: - """Handle logbook get events websocket command.""" - start_time_str = msg["start_time"] - end_time_str = msg.get("end_time") - utc_now = dt_util.utcnow() - - if start_time := dt_util.parse_datetime(start_time_str): - start_time = dt_util.as_utc(start_time) - else: - connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") - return - - if not end_time_str: - end_time = utc_now - elif parsed_end_time := dt_util.parse_datetime(end_time_str): - end_time = dt_util.as_utc(parsed_end_time) - else: - connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") - return - - if start_time > utc_now: - connection.send_result(msg["id"], []) - return - - device_ids = msg.get("device_ids") - entity_ids = msg.get("entity_ids") - context_id = msg.get("context_id") - event_types = _async_determine_event_types(hass, entity_ids, device_ids) - ent_reg = er.async_get(hass) - - connection.send_message( - await get_instance(hass).async_add_executor_job( - _ws_formatted_get_events, - hass, - msg["id"], - start_time, - end_time, - event_types, - ent_reg, - entity_ids, - device_ids, - hass.data[LOGBOOK_FILTERS], - hass.data[LOGBOOK_ENTITIES_FILTER], - context_id, - ) - ) - - -class LogbookView(HomeAssistantView): - """Handle logbook view requests.""" - - url = "/api/logbook" - name = "api:logbook" - extra_urls = ["/api/logbook/{datetime}"] - - def __init__( - self, - config: dict[str, Any], - filters: Filters | None, - entities_filter: EntityFilter | None, - ) -> None: - """Initialize the logbook view.""" - self.config = config - self.filters = filters - self.entities_filter = entities_filter - - async def get( - self, request: web.Request, datetime: str | None = None - ) -> web.Response: - """Retrieve logbook entries.""" - if datetime: - if (datetime_dt := dt_util.parse_datetime(datetime)) is None: - return self.json_message("Invalid datetime", HTTPStatus.BAD_REQUEST) - else: - datetime_dt = dt_util.start_of_local_day() - - if (period_str := request.query.get("period")) is None: - period: int = 1 - else: - period = int(period_str) - - if entity_ids_str := request.query.get("entity"): - try: - entity_ids = cv.entity_ids(entity_ids_str) - except vol.Invalid: - raise InvalidEntityFormatError( - f"Invalid entity id(s) encountered: {entity_ids_str}. " - "Format should be ." - ) from vol.Invalid - else: - entity_ids = None - - if (end_time_str := request.query.get("end_time")) is None: - start_day = dt_util.as_utc(datetime_dt) - timedelta(days=period - 1) - end_day = start_day + timedelta(days=period) - else: - start_day = datetime_dt - if (end_day_dt := dt_util.parse_datetime(end_time_str)) is None: - return self.json_message("Invalid end_time", HTTPStatus.BAD_REQUEST) - end_day = end_day_dt - - hass = request.app["hass"] - - context_id = request.query.get("context_id") - - if entity_ids and context_id: - return self.json_message( - "Can't combine entity with context_id", HTTPStatus.BAD_REQUEST - ) - - event_types = _async_determine_event_types(hass, entity_ids, None) - ent_reg = er.async_get(hass) - - def json_events() -> web.Response: - """Fetch events and generate JSON.""" - return self.json( - _get_events( - hass, - start_day, - end_day, - event_types, - ent_reg, - entity_ids, - None, - self.filters, - self.entities_filter, - context_id, - False, - True, - ) - ) - - return cast( - web.Response, await get_instance(hass).async_add_executor_job(json_events) - ) - - -def _humanify( - rows: Generator[Row, None, None], - entities_filter: EntityFilter | Callable[[str], bool] | None, - ent_reg: er.EntityRegistry, - external_events: dict[ - str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] - ], - entity_name_cache: EntityNameCache, - format_time: Callable[[Row], Any], - include_entity_name: bool = True, -) -> Generator[dict[str, Any], None, None]: - """Generate a converted list of events into entries.""" - # Continuous sensors, will be excluded from the logbook - continuous_sensors: dict[str, bool] = {} - event_data_cache: dict[str, dict[str, Any]] = {} - context_lookup: dict[str | None, Row | None] = {None: None} - event_cache = EventCache(event_data_cache) - context_augmenter = ContextAugmenter( - context_lookup, - entity_name_cache, - external_events, - event_cache, - include_entity_name, - ) - - def _keep_row(row: Row, event_type: str) -> bool: - """Check if the entity_filter rejects a row.""" - assert entities_filter is not None - if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): - return entities_filter(entity_id) - - if event_type in external_events: - # If the entity_id isn't described, use the domain that describes - # the event for filtering. - domain: str | None = external_events[event_type][0] - else: - domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) - - return domain is not None and entities_filter(f"{domain}._") - - # Process rows - for row in rows: - context_id = row.context_id - context_lookup.setdefault(context_id, row) - if row.context_only: - continue - event_type = row.event_type - if event_type == EVENT_CALL_SERVICE or ( - event_type is not PSUEDO_EVENT_STATE_CHANGED - and entities_filter is not None - and not _keep_row(row, event_type) - ): - continue - - if event_type is PSUEDO_EVENT_STATE_CHANGED: - entity_id = row.entity_id - assert entity_id is not None - # Skip continuous sensors - if ( - is_continuous := continuous_sensors.get(entity_id) - ) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN: - is_continuous = _is_sensor_continuous(ent_reg, entity_id) - continuous_sensors[entity_id] = is_continuous - if is_continuous: - continue - - data = { - LOGBOOK_ENTRY_WHEN: format_time(row), - LOGBOOK_ENTRY_STATE: row.state, - LOGBOOK_ENTRY_ENTITY_ID: entity_id, - } - if include_entity_name: - data[LOGBOOK_ENTRY_NAME] = entity_name_cache.get(entity_id, row) - if icon := row.icon or row.old_format_icon: - data[LOGBOOK_ENTRY_ICON] = icon - - context_augmenter.augment(data, row, context_id) - yield data - - elif event_type in external_events: - domain, describe_event = external_events[event_type] - data = describe_event(event_cache.get(row)) - data[LOGBOOK_ENTRY_WHEN] = format_time(row) - data[LOGBOOK_ENTRY_DOMAIN] = domain - context_augmenter.augment(data, row, context_id) - yield data - - elif event_type == EVENT_LOGBOOK_ENTRY: - event = event_cache.get(row) - if not (event_data := event.data): - continue - entry_domain = event_data.get(ATTR_DOMAIN) - entry_entity_id = event_data.get(ATTR_ENTITY_ID) - if entry_domain is None and entry_entity_id is not None: - with suppress(IndexError): - entry_domain = split_entity_id(str(entry_entity_id))[0] - - data = { - LOGBOOK_ENTRY_WHEN: format_time(row), - LOGBOOK_ENTRY_NAME: event_data.get(ATTR_NAME), - LOGBOOK_ENTRY_MESSAGE: event_data.get(ATTR_MESSAGE), - LOGBOOK_ENTRY_DOMAIN: entry_domain, - LOGBOOK_ENTRY_ENTITY_ID: entry_entity_id, - } - context_augmenter.augment(data, row, context_id) - yield data - - -def _get_events( - hass: HomeAssistant, - start_day: dt, - end_day: dt, - event_types: tuple[str, ...], - ent_reg: er.EntityRegistry, - entity_ids: list[str] | None = None, - device_ids: list[str] | None = None, - filters: Filters | None = None, - entities_filter: EntityFilter | Callable[[str], bool] | None = None, - context_id: str | None = None, - timestamp: bool = False, - include_entity_name: bool = True, -) -> list[dict[str, Any]]: - """Get events for a period of time.""" - assert not ( - context_id and (entity_ids or device_ids) - ), "can't pass in both context_id and (entity_ids or device_ids)" - external_events: dict[ - str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] - ] = hass.data.get(DOMAIN, {}) - format_time = _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat - entity_name_cache = EntityNameCache(hass) - if entity_ids or device_ids: - entities_filter = None - - def yield_rows(query: Query) -> Generator[Row, None, None]: - """Yield rows from the database.""" - # end_day - start_day intentionally checks .days and not .total_seconds() - # since we don't want to switch over to buffered if they go - # over one day by a few hours since the UI makes it so easy to do that. - if entity_ids or context_id or (end_day - start_day).days <= 1: - return query.all() # type: ignore[no-any-return] - # Only buffer rows to reduce memory pressure - # if we expect the result set is going to be very large. - # What is considered very large is going to differ - # based on the hardware Home Assistant is running on. - # - # sqlalchemy suggests that is at least 10k, but for - # even and RPi3 that number seems higher in testing - # so we don't switch over until we request > 1 day+ of data. - # - return query.yield_per(1024) # type: ignore[no-any-return] - - stmt = statement_for_request( - start_day, - end_day, - event_types, - entity_ids, - device_ids, - filters, - context_id, - ) - if _LOGGER.isEnabledFor(logging.DEBUG): - _LOGGER.debug( - "Literal statement: %s", - stmt.compile(compile_kwargs={"literal_binds": True}), - ) - - with session_scope(hass=hass) as session: - return list( - _humanify( - yield_rows(session.execute(stmt)), - entities_filter, - ent_reg, - external_events, - entity_name_cache, - format_time, - include_entity_name, - ) - ) - - -class ContextAugmenter: - """Augment data with context trace.""" - - def __init__( - self, - context_lookup: dict[str | None, Row | None], - entity_name_cache: EntityNameCache, - external_events: dict[ - str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] - ], - event_cache: EventCache, - include_entity_name: bool, - ) -> None: - """Init the augmenter.""" - self.context_lookup = context_lookup - self.entity_name_cache = entity_name_cache - self.external_events = external_events - self.event_cache = event_cache - self.include_entity_name = include_entity_name - - def augment(self, data: dict[str, Any], row: Row, context_id: str) -> None: - """Augment data from the row and cache.""" - if context_user_id := row.context_user_id: - data[CONTEXT_USER_ID] = context_user_id - - if not (context_row := self.context_lookup.get(context_id)): - return - - if _rows_match(row, context_row): - # This is the first event with the given ID. Was it directly caused by - # a parent event? - if ( - not row.context_parent_id - or (context_row := self.context_lookup.get(row.context_parent_id)) - is None - ): - return - # Ensure the (parent) context_event exists and is not the root cause of - # this log entry. - if _rows_match(row, context_row): - return - - event_type = context_row.event_type - - # State change - if context_entity_id := context_row.entity_id: - data[CONTEXT_STATE] = context_row.state - data[CONTEXT_ENTITY_ID] = context_entity_id - if self.include_entity_name: - data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( - context_entity_id, context_row - ) - return - - # Call service - if event_type == EVENT_CALL_SERVICE: - event = self.event_cache.get(context_row) - event_data = event.data - data[CONTEXT_DOMAIN] = event_data.get(ATTR_DOMAIN) - data[CONTEXT_SERVICE] = event_data.get(ATTR_SERVICE) - data[CONTEXT_EVENT_TYPE] = event_type - return - - if event_type not in self.external_events: - return - - domain, describe_event = self.external_events[event_type] - data[CONTEXT_EVENT_TYPE] = event_type - data[CONTEXT_DOMAIN] = domain - event = self.event_cache.get(context_row) - described = describe_event(event) - if name := described.get(ATTR_NAME): - data[CONTEXT_NAME] = name - if message := described.get(ATTR_MESSAGE): - data[CONTEXT_MESSAGE] = message - if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): - return - data[CONTEXT_ENTITY_ID] = attr_entity_id - if self.include_entity_name: - data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( - attr_entity_id, context_row - ) - - -def _is_sensor_continuous(ent_reg: er.EntityRegistry, entity_id: str) -> bool: - """Determine if a sensor is continuous by checking its state class. - - Sensors with a unit_of_measurement are also considered continuous, but are filtered - already by the SQL query generated by _get_events - """ - if not (entry := ent_reg.async_get(entity_id)): - # Entity not registered, so can't have a state class - return False - return ( - entry.capabilities is not None - and entry.capabilities.get(ATTR_STATE_CLASS) is not None - ) - - -def _rows_match(row: Row, other_row: Row) -> bool: - """Check of rows match by using the same method as Events __hash__.""" - if ( - (state_id := row.state_id) is not None - and state_id == other_row.state_id - or (event_id := row.event_id) is not None - and event_id == other_row.event_id - ): - return True - return False - - -def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None: - """Extract from event_data row.""" - result = extractor.search(row.shared_data or row.event_data or "") - return result.group(1) if result else None - - -def _row_time_fired_isoformat(row: Row) -> str: - """Convert the row timed_fired to isoformat.""" - return process_timestamp_to_utc_isoformat(row.time_fired or dt_util.utcnow()) - - -def _row_time_fired_timestamp(row: Row) -> float: - """Convert the row timed_fired to timestamp.""" - return process_datetime_to_timestamp(row.time_fired or dt_util.utcnow()) - - -class LazyEventPartialState: - """A lazy version of core Event with limited State joined in.""" - - __slots__ = [ - "row", - "_event_data", - "_event_data_cache", - "event_type", - "entity_id", - "state", - "context_id", - "context_user_id", - "context_parent_id", - "data", - ] - - def __init__( - self, - row: Row, - event_data_cache: dict[str, dict[str, Any]], - ) -> None: - """Init the lazy event.""" - self.row = row - self._event_data: dict[str, Any] | None = None - self._event_data_cache = event_data_cache - self.event_type: str = self.row.event_type - self.entity_id: str | None = self.row.entity_id - self.state = self.row.state - self.context_id: str | None = self.row.context_id - self.context_user_id: str | None = self.row.context_user_id - self.context_parent_id: str | None = self.row.context_parent_id - source: str = self.row.shared_data or self.row.event_data - if not source: - self.data = {} - elif event_data := self._event_data_cache.get(source): - self.data = event_data - else: - self.data = self._event_data_cache[source] = cast( - dict[str, Any], json.loads(source) - ) - - -class EntityNameCache: - """A cache to lookup the name for an entity. - - This class should not be used to lookup attributes - that are expected to change state. - """ - - def __init__(self, hass: HomeAssistant) -> None: - """Init the cache.""" - self._hass = hass - self._names: dict[str, str] = {} - - def get(self, entity_id: str, row: Row) -> str: - """Lookup an the friendly name.""" - if entity_id in self._names: - return self._names[entity_id] - if (current_state := self._hass.states.get(entity_id)) and ( - friendly_name := current_state.attributes.get(ATTR_FRIENDLY_NAME) - ): - self._names[entity_id] = friendly_name - else: - return split_entity_id(entity_id)[1].replace("_", " ") - - return self._names[entity_id] - - -class EventCache: - """Cache LazyEventPartialState by row.""" - - def __init__(self, event_data_cache: dict[str, dict[str, Any]]) -> None: - """Init the cache.""" - self._event_data_cache = event_data_cache - self.event_cache: dict[Row, LazyEventPartialState] = {} - - def get(self, row: Row) -> LazyEventPartialState: - """Get the event from the row.""" - if event := self.event_cache.get(row): - return event - event = self.event_cache[row] = LazyEventPartialState( - row, self._event_data_cache - ) - return event diff --git a/homeassistant/components/logbook/const.py b/homeassistant/components/logbook/const.py new file mode 100644 index 00000000000..406406e9dd8 --- /dev/null +++ b/homeassistant/components/logbook/const.py @@ -0,0 +1,39 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED +from homeassistant.components.script import EVENT_SCRIPT_STARTED +from homeassistant.const import EVENT_CALL_SERVICE, EVENT_LOGBOOK_ENTRY + +ATTR_MESSAGE = "message" + +DOMAIN = "logbook" + +CONTEXT_USER_ID = "context_user_id" +CONTEXT_ENTITY_ID = "context_entity_id" +CONTEXT_ENTITY_ID_NAME = "context_entity_id_name" +CONTEXT_EVENT_TYPE = "context_event_type" +CONTEXT_DOMAIN = "context_domain" +CONTEXT_STATE = "context_state" +CONTEXT_SERVICE = "context_service" +CONTEXT_NAME = "context_name" +CONTEXT_MESSAGE = "context_message" + +LOGBOOK_ENTRY_DOMAIN = "domain" +LOGBOOK_ENTRY_ENTITY_ID = "entity_id" +LOGBOOK_ENTRY_ICON = "icon" +LOGBOOK_ENTRY_MESSAGE = "message" +LOGBOOK_ENTRY_NAME = "name" +LOGBOOK_ENTRY_STATE = "state" +LOGBOOK_ENTRY_WHEN = "when" + +ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE} +ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY = { + EVENT_LOGBOOK_ENTRY, + EVENT_AUTOMATION_TRIGGERED, + EVENT_SCRIPT_STARTED, +} + + +LOGBOOK_FILTERS = "logbook_filters" +LOGBOOK_ENTITIES_FILTER = "entities_filter" diff --git a/homeassistant/components/logbook/helpers.py b/homeassistant/components/logbook/helpers.py new file mode 100644 index 00000000000..cc9ea238f8b --- /dev/null +++ b/homeassistant/components/logbook/helpers.py @@ -0,0 +1,193 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +from collections.abc import Callable +from typing import Any + +from homeassistant.components.sensor import ATTR_STATE_CLASS +from homeassistant.const import ( + ATTR_DEVICE_ID, + ATTR_ENTITY_ID, + ATTR_UNIT_OF_MEASUREMENT, + EVENT_LOGBOOK_ENTRY, + EVENT_STATE_CHANGED, +) +from homeassistant.core import ( + CALLBACK_TYPE, + Event, + HomeAssistant, + State, + callback, + is_callback, +) +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.event import async_track_state_change_event + +from .const import ( + ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, + DOMAIN, + ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY, +) +from .models import LazyEventPartialState + + +def async_filter_entities(hass: HomeAssistant, entity_ids: list[str]) -> list[str]: + """Filter out any entities that logbook will not produce results for.""" + ent_reg = er.async_get(hass) + return [ + entity_id + for entity_id in entity_ids + if not _is_entity_id_filtered(hass, ent_reg, entity_id) + ] + + +def async_determine_event_types( + hass: HomeAssistant, entity_ids: list[str] | None, device_ids: list[str] | None +) -> tuple[str, ...]: + """Reduce the event types based on the entity ids and device ids.""" + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ] = hass.data.get(DOMAIN, {}) + if not entity_ids and not device_ids: + return (*ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED, *external_events) + config_entry_ids: set[str] = set() + intrested_event_types: set[str] = set() + + if entity_ids: + # + # Home Assistant doesn't allow firing events from + # entities so we have a limited list to check + # + # automations and scripts can refer to entities + # but they do not have a config entry so we need + # to add them. + # + # We also allow entity_ids to be recorded via + # manual logbook entries. + # + intrested_event_types |= ENTITY_EVENTS_WITHOUT_CONFIG_ENTRY + + if device_ids: + dev_reg = dr.async_get(hass) + for device_id in device_ids: + if (device := dev_reg.async_get(device_id)) and device.config_entries: + config_entry_ids |= device.config_entries + interested_domains: set[str] = set() + for entry_id in config_entry_ids: + if entry := hass.config_entries.async_get_entry(entry_id): + interested_domains.add(entry.domain) + for external_event, domain_call in external_events.items(): + if domain_call[0] in interested_domains: + intrested_event_types.add(external_event) + + return tuple( + event_type + for event_type in (EVENT_LOGBOOK_ENTRY, *external_events) + if event_type in intrested_event_types + ) + + +@callback +def async_subscribe_events( + hass: HomeAssistant, + subscriptions: list[CALLBACK_TYPE], + target: Callable[[Event], None], + event_types: tuple[str, ...], + entity_ids: list[str] | None, + device_ids: list[str] | None, +) -> None: + """Subscribe to events for the entities and devices or all. + + These are the events we need to listen for to do + the live logbook stream. + """ + ent_reg = er.async_get(hass) + assert is_callback(target), "target must be a callback" + event_forwarder = target + + if entity_ids or device_ids: + entity_ids_set = set(entity_ids) if entity_ids else set() + device_ids_set = set(device_ids) if device_ids else set() + + @callback + def _forward_events_filtered(event: Event) -> None: + event_data = event.data + if ( + entity_ids_set and event_data.get(ATTR_ENTITY_ID) in entity_ids_set + ) or (device_ids_set and event_data.get(ATTR_DEVICE_ID) in device_ids_set): + target(event) + + event_forwarder = _forward_events_filtered + + for event_type in event_types: + subscriptions.append( + hass.bus.async_listen(event_type, event_forwarder, run_immediately=True) + ) + + @callback + def _forward_state_events_filtered(event: Event) -> None: + if event.data.get("old_state") is None or event.data.get("new_state") is None: + return + state: State = event.data["new_state"] + if not _is_state_filtered(ent_reg, state): + target(event) + + if entity_ids: + subscriptions.append( + async_track_state_change_event( + hass, entity_ids, _forward_state_events_filtered + ) + ) + return + + # We want the firehose + subscriptions.append( + hass.bus.async_listen( + EVENT_STATE_CHANGED, + _forward_state_events_filtered, + run_immediately=True, + ) + ) + + +def is_sensor_continuous(ent_reg: er.EntityRegistry, entity_id: str) -> bool: + """Determine if a sensor is continuous by checking its state class. + + Sensors with a unit_of_measurement are also considered continuous, but are filtered + already by the SQL query generated by _get_events + """ + if not (entry := ent_reg.async_get(entity_id)): + # Entity not registered, so can't have a state class + return False + return ( + entry.capabilities is not None + and entry.capabilities.get(ATTR_STATE_CLASS) is not None + ) + + +def _is_state_filtered(ent_reg: er.EntityRegistry, state: State) -> bool: + """Check if the logbook should filter a state. + + Used when we are in live mode to ensure + we only get significant changes (state.last_changed != state.last_updated) + """ + return bool( + state.last_changed != state.last_updated + or ATTR_UNIT_OF_MEASUREMENT in state.attributes + or is_sensor_continuous(ent_reg, state.entity_id) + ) + + +def _is_entity_id_filtered( + hass: HomeAssistant, ent_reg: er.EntityRegistry, entity_id: str +) -> bool: + """Check if the logbook should filter an entity. + + Used to setup listeners and which entities to select + from the database when a list of entities is requested. + """ + return bool( + (state := hass.states.get(entity_id)) + and (ATTR_UNIT_OF_MEASUREMENT in state.attributes) + or is_sensor_continuous(ent_reg, entity_id) + ) diff --git a/homeassistant/components/logbook/models.py b/homeassistant/components/logbook/models.py new file mode 100644 index 00000000000..e4844751c6a --- /dev/null +++ b/homeassistant/components/logbook/models.py @@ -0,0 +1,113 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime as dt +import json +from typing import Any, cast + +from sqlalchemy.engine.row import Row + +from homeassistant.const import ATTR_ICON, EVENT_STATE_CHANGED +from homeassistant.core import Context, Event, State, callback + + +class LazyEventPartialState: + """A lazy version of core Event with limited State joined in.""" + + __slots__ = [ + "row", + "_event_data", + "_event_data_cache", + "event_type", + "entity_id", + "state", + "context_id", + "context_user_id", + "context_parent_id", + "data", + ] + + def __init__( + self, + row: Row | EventAsRow, + event_data_cache: dict[str, dict[str, Any]], + ) -> None: + """Init the lazy event.""" + self.row = row + self._event_data: dict[str, Any] | None = None + self._event_data_cache = event_data_cache + self.event_type: str | None = self.row.event_type + self.entity_id: str | None = self.row.entity_id + self.state = self.row.state + self.context_id: str | None = self.row.context_id + self.context_user_id: str | None = self.row.context_user_id + self.context_parent_id: str | None = self.row.context_parent_id + if data := getattr(row, "data", None): + # If its an EventAsRow we can avoid the whole + # json decode process as we already have the data + self.data = data + return + source = cast(str, self.row.shared_data or self.row.event_data) + if not source: + self.data = {} + elif event_data := self._event_data_cache.get(source): + self.data = event_data + else: + self.data = self._event_data_cache[source] = cast( + dict[str, Any], json.loads(source) + ) + + +@dataclass +class EventAsRow: + """Convert an event to a row.""" + + data: dict[str, Any] + context: Context + context_id: str + time_fired: dt + state_id: int + event_data: str | None = None + old_format_icon: None = None + event_id: None = None + entity_id: str | None = None + icon: str | None = None + context_user_id: str | None = None + context_parent_id: str | None = None + event_type: str | None = None + state: str | None = None + shared_data: str | None = None + context_only: None = None + + +@callback +def async_event_to_row(event: Event) -> EventAsRow | None: + """Convert an event to a row.""" + if event.event_type != EVENT_STATE_CHANGED: + return EventAsRow( + data=event.data, + context=event.context, + event_type=event.event_type, + context_id=event.context.id, + context_user_id=event.context.user_id, + context_parent_id=event.context.parent_id, + time_fired=event.time_fired, + state_id=hash(event), + ) + # States are prefiltered so we never get states + # that are missing new_state or old_state + # since the logbook does not show these + new_state: State = event.data["new_state"] + return EventAsRow( + data=event.data, + context=event.context, + entity_id=new_state.entity_id, + state=new_state.state, + context_id=new_state.context.id, + context_user_id=new_state.context.user_id, + context_parent_id=new_state.context.parent_id, + time_fired=new_state.last_updated, + state_id=hash(event), + icon=new_state.attributes.get(ATTR_ICON), + ) diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py new file mode 100644 index 00000000000..c9f20f27f7c --- /dev/null +++ b/homeassistant/components/logbook/processor.py @@ -0,0 +1,488 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +from collections.abc import Callable, Generator +from contextlib import suppress +from dataclasses import dataclass +from datetime import datetime as dt +import logging +import re +from typing import Any + +from sqlalchemy.engine.row import Row +from sqlalchemy.orm.query import Query + +from homeassistant.components.recorder.filters import Filters +from homeassistant.components.recorder.models import ( + process_datetime_to_timestamp, + process_timestamp_to_utc_isoformat, +) +from homeassistant.components.recorder.util import session_scope +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import ( + ATTR_DOMAIN, + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + ATTR_NAME, + ATTR_SERVICE, + EVENT_CALL_SERVICE, + EVENT_LOGBOOK_ENTRY, +) +from homeassistant.core import HomeAssistant, split_entity_id +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.entityfilter import EntityFilter +import homeassistant.util.dt as dt_util + +from .const import ( + ATTR_MESSAGE, + CONTEXT_DOMAIN, + CONTEXT_ENTITY_ID, + CONTEXT_ENTITY_ID_NAME, + CONTEXT_EVENT_TYPE, + CONTEXT_MESSAGE, + CONTEXT_NAME, + CONTEXT_SERVICE, + CONTEXT_STATE, + CONTEXT_USER_ID, + DOMAIN, + LOGBOOK_ENTITIES_FILTER, + LOGBOOK_ENTRY_DOMAIN, + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_ICON, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, + LOGBOOK_ENTRY_STATE, + LOGBOOK_ENTRY_WHEN, + LOGBOOK_FILTERS, +) +from .helpers import is_sensor_continuous +from .models import EventAsRow, LazyEventPartialState, async_event_to_row +from .queries import statement_for_request +from .queries.common import PSUEDO_EVENT_STATE_CHANGED + +_LOGGER = logging.getLogger(__name__) + +ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') +DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') + + +@dataclass +class LogbookRun: + """A logbook run which may be a long running event stream or single request.""" + + context_lookup: ContextLookup + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ] + event_cache: EventCache + entity_name_cache: EntityNameCache + include_entity_name: bool + format_time: Callable[[Row], Any] + + +class EventProcessor: + """Stream into logbook format.""" + + def __init__( + self, + hass: HomeAssistant, + event_types: tuple[str, ...], + entity_ids: list[str] | None = None, + device_ids: list[str] | None = None, + context_id: str | None = None, + timestamp: bool = False, + include_entity_name: bool = True, + ) -> None: + """Init the event stream.""" + assert not ( + context_id and (entity_ids or device_ids) + ), "can't pass in both context_id and (entity_ids or device_ids)" + self.hass = hass + self.ent_reg = er.async_get(hass) + self.event_types = event_types + self.entity_ids = entity_ids + self.device_ids = device_ids + self.context_id = context_id + self.filters: Filters | None = hass.data[LOGBOOK_FILTERS] + if self.limited_select: + self.entities_filter: EntityFilter | Callable[[str], bool] | None = None + else: + self.entities_filter = hass.data[LOGBOOK_ENTITIES_FILTER] + format_time = ( + _row_time_fired_timestamp if timestamp else _row_time_fired_isoformat + ) + external_events: dict[ + str, tuple[str, Callable[[LazyEventPartialState], dict[str, Any]]] + ] = hass.data.get(DOMAIN, {}) + self.logbook_run = LogbookRun( + context_lookup=ContextLookup(hass), + external_events=external_events, + event_cache=EventCache({}), + entity_name_cache=EntityNameCache(self.hass), + include_entity_name=include_entity_name, + format_time=format_time, + ) + self.context_augmenter = ContextAugmenter(self.logbook_run) + + @property + def limited_select(self) -> bool: + """Check if the stream is limited by entities context or device ids.""" + return bool(self.entity_ids or self.context_id or self.device_ids) + + def switch_to_live(self) -> None: + """Switch to live stream. + + Clear caches so we can reduce memory pressure. + """ + self.logbook_run.event_cache.clear() + self.logbook_run.context_lookup.clear() + + def get_events( + self, + start_day: dt, + end_day: dt, + ) -> list[dict[str, Any]]: + """Get events for a period of time.""" + + def yield_rows(query: Query) -> Generator[Row, None, None]: + """Yield rows from the database.""" + # end_day - start_day intentionally checks .days and not .total_seconds() + # since we don't want to switch over to buffered if they go + # over one day by a few hours since the UI makes it so easy to do that. + if self.limited_select or (end_day - start_day).days <= 1: + return query.all() # type: ignore[no-any-return] + # Only buffer rows to reduce memory pressure + # if we expect the result set is going to be very large. + # What is considered very large is going to differ + # based on the hardware Home Assistant is running on. + # + # sqlalchemy suggests that is at least 10k, but for + # even and RPi3 that number seems higher in testing + # so we don't switch over until we request > 1 day+ of data. + # + return query.yield_per(1024) # type: ignore[no-any-return] + + stmt = statement_for_request( + start_day, + end_day, + self.event_types, + self.entity_ids, + self.device_ids, + self.filters, + self.context_id, + ) + if _LOGGER.isEnabledFor(logging.DEBUG): + _LOGGER.debug( + "Literal statement: %s", + stmt.compile(compile_kwargs={"literal_binds": True}), + ) + + with session_scope(hass=self.hass) as session: + return self.humanify(yield_rows(session.execute(stmt))) + + def humanify( + self, row_generator: Generator[Row | EventAsRow, None, None] + ) -> list[dict[str, str]]: + """Humanify rows.""" + return list( + _humanify( + row_generator, + self.entities_filter, + self.ent_reg, + self.logbook_run, + self.context_augmenter, + ) + ) + + +def _humanify( + rows: Generator[Row | EventAsRow, None, None], + entities_filter: EntityFilter | Callable[[str], bool] | None, + ent_reg: er.EntityRegistry, + logbook_run: LogbookRun, + context_augmenter: ContextAugmenter, +) -> Generator[dict[str, Any], None, None]: + """Generate a converted list of events into entries.""" + # Continuous sensors, will be excluded from the logbook + continuous_sensors: dict[str, bool] = {} + context_lookup = logbook_run.context_lookup + external_events = logbook_run.external_events + event_cache = logbook_run.event_cache + entity_name_cache = logbook_run.entity_name_cache + include_entity_name = logbook_run.include_entity_name + format_time = logbook_run.format_time + + def _keep_row(row: Row | EventAsRow, event_type: str) -> bool: + """Check if the entity_filter rejects a row.""" + assert entities_filter is not None + if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): + return entities_filter(entity_id) + + if event_type in external_events: + # If the entity_id isn't described, use the domain that describes + # the event for filtering. + domain: str | None = external_events[event_type][0] + else: + domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) + + return domain is not None and entities_filter(f"{domain}._") + + # Process rows + for row in rows: + context_id = context_lookup.memorize(row) + if row.context_only: + continue + event_type = row.event_type + if event_type == EVENT_CALL_SERVICE or ( + event_type is not PSUEDO_EVENT_STATE_CHANGED + and entities_filter is not None + and not _keep_row(row, event_type) + ): + continue + + if event_type is PSUEDO_EVENT_STATE_CHANGED: + entity_id = row.entity_id + assert entity_id is not None + # Skip continuous sensors + if ( + is_continuous := continuous_sensors.get(entity_id) + ) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN: + is_continuous = is_sensor_continuous(ent_reg, entity_id) + continuous_sensors[entity_id] = is_continuous + if is_continuous: + continue + + data = { + LOGBOOK_ENTRY_WHEN: format_time(row), + LOGBOOK_ENTRY_STATE: row.state, + LOGBOOK_ENTRY_ENTITY_ID: entity_id, + } + if include_entity_name: + data[LOGBOOK_ENTRY_NAME] = entity_name_cache.get(entity_id) + if icon := row.icon or row.old_format_icon: + data[LOGBOOK_ENTRY_ICON] = icon + + context_augmenter.augment(data, row, context_id) + yield data + + elif event_type in external_events: + domain, describe_event = external_events[event_type] + data = describe_event(event_cache.get(row)) + data[LOGBOOK_ENTRY_WHEN] = format_time(row) + data[LOGBOOK_ENTRY_DOMAIN] = domain + context_augmenter.augment(data, row, context_id) + yield data + + elif event_type == EVENT_LOGBOOK_ENTRY: + event = event_cache.get(row) + if not (event_data := event.data): + continue + entry_domain = event_data.get(ATTR_DOMAIN) + entry_entity_id = event_data.get(ATTR_ENTITY_ID) + if entry_domain is None and entry_entity_id is not None: + with suppress(IndexError): + entry_domain = split_entity_id(str(entry_entity_id))[0] + data = { + LOGBOOK_ENTRY_WHEN: format_time(row), + LOGBOOK_ENTRY_NAME: event_data.get(ATTR_NAME), + LOGBOOK_ENTRY_MESSAGE: event_data.get(ATTR_MESSAGE), + LOGBOOK_ENTRY_DOMAIN: entry_domain, + LOGBOOK_ENTRY_ENTITY_ID: entry_entity_id, + } + context_augmenter.augment(data, row, context_id) + yield data + + +class ContextLookup: + """A lookup class for context origins.""" + + def __init__(self, hass: HomeAssistant) -> None: + """Memorize context origin.""" + self.hass = hass + self._memorize_new = True + self._lookup: dict[str | None, Row | EventAsRow | None] = {None: None} + + def memorize(self, row: Row) -> str | None: + """Memorize a context from the database.""" + if self._memorize_new: + context_id: str = row.context_id + self._lookup.setdefault(context_id, row) + return context_id + return None + + def clear(self) -> None: + """Clear the context origins and stop recording new ones.""" + self._lookup.clear() + self._memorize_new = False + + def get(self, context_id: str) -> Row | None: + """Get the context origin.""" + return self._lookup.get(context_id) + + +class ContextAugmenter: + """Augment data with context trace.""" + + def __init__(self, logbook_run: LogbookRun) -> None: + """Init the augmenter.""" + self.context_lookup = logbook_run.context_lookup + self.entity_name_cache = logbook_run.entity_name_cache + self.external_events = logbook_run.external_events + self.event_cache = logbook_run.event_cache + self.include_entity_name = logbook_run.include_entity_name + + def _get_context_row( + self, context_id: str | None, row: Row | EventAsRow + ) -> Row | EventAsRow: + """Get the context row from the id or row context.""" + if context_id: + return self.context_lookup.get(context_id) + if (context := getattr(row, "context", None)) is not None and ( + origin_event := context.origin_event + ) is not None: + return async_event_to_row(origin_event) + return None + + def augment( + self, data: dict[str, Any], row: Row | EventAsRow, context_id: str | None + ) -> None: + """Augment data from the row and cache.""" + if context_user_id := row.context_user_id: + data[CONTEXT_USER_ID] = context_user_id + + if not (context_row := self._get_context_row(context_id, row)): + return + + if _rows_match(row, context_row): + # This is the first event with the given ID. Was it directly caused by + # a parent event? + if ( + not row.context_parent_id + or ( + context_row := self._get_context_row( + row.context_parent_id, context_row + ) + ) + is None + ): + return + # Ensure the (parent) context_event exists and is not the root cause of + # this log entry. + if _rows_match(row, context_row): + return + event_type = context_row.event_type + # State change + if context_entity_id := context_row.entity_id: + data[CONTEXT_STATE] = context_row.state + data[CONTEXT_ENTITY_ID] = context_entity_id + if self.include_entity_name: + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get( + context_entity_id + ) + return + + # Call service + if event_type == EVENT_CALL_SERVICE: + event = self.event_cache.get(context_row) + event_data = event.data + data[CONTEXT_DOMAIN] = event_data.get(ATTR_DOMAIN) + data[CONTEXT_SERVICE] = event_data.get(ATTR_SERVICE) + data[CONTEXT_EVENT_TYPE] = event_type + return + + if event_type not in self.external_events: + return + + domain, describe_event = self.external_events[event_type] + data[CONTEXT_EVENT_TYPE] = event_type + data[CONTEXT_DOMAIN] = domain + event = self.event_cache.get(context_row) + described = describe_event(event) + if name := described.get(ATTR_NAME): + data[CONTEXT_NAME] = name + if message := described.get(ATTR_MESSAGE): + data[CONTEXT_MESSAGE] = message + if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): + return + data[CONTEXT_ENTITY_ID] = attr_entity_id + if self.include_entity_name: + data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get(attr_entity_id) + + +def _rows_match(row: Row | EventAsRow, other_row: Row | EventAsRow) -> bool: + """Check of rows match by using the same method as Events __hash__.""" + if ( + (state_id := row.state_id) is not None + and state_id == other_row.state_id + or (event_id := row.event_id) is not None + and event_id == other_row.event_id + ): + return True + return False + + +def _row_event_data_extract(row: Row | EventAsRow, extractor: re.Pattern) -> str | None: + """Extract from event_data row.""" + result = extractor.search(row.shared_data or row.event_data or "") + return result.group(1) if result else None + + +def _row_time_fired_isoformat(row: Row | EventAsRow) -> str: + """Convert the row timed_fired to isoformat.""" + return process_timestamp_to_utc_isoformat(row.time_fired or dt_util.utcnow()) + + +def _row_time_fired_timestamp(row: Row | EventAsRow) -> float: + """Convert the row timed_fired to timestamp.""" + return process_datetime_to_timestamp(row.time_fired or dt_util.utcnow()) + + +class EntityNameCache: + """A cache to lookup the name for an entity. + + This class should not be used to lookup attributes + that are expected to change state. + """ + + def __init__(self, hass: HomeAssistant) -> None: + """Init the cache.""" + self._hass = hass + self._names: dict[str, str] = {} + + def get(self, entity_id: str) -> str: + """Lookup an the friendly name.""" + if entity_id in self._names: + return self._names[entity_id] + if (current_state := self._hass.states.get(entity_id)) and ( + friendly_name := current_state.attributes.get(ATTR_FRIENDLY_NAME) + ): + self._names[entity_id] = friendly_name + else: + return split_entity_id(entity_id)[1].replace("_", " ") + + return self._names[entity_id] + + +class EventCache: + """Cache LazyEventPartialState by row.""" + + def __init__(self, event_data_cache: dict[str, dict[str, Any]]) -> None: + """Init the cache.""" + self._event_data_cache = event_data_cache + self.event_cache: dict[Row | EventAsRow, LazyEventPartialState] = {} + + def get(self, row: EventAsRow | Row) -> LazyEventPartialState: + """Get the event from the row.""" + if isinstance(row, EventAsRow): + return LazyEventPartialState(row, self._event_data_cache) + if event := self.event_cache.get(row): + return event + self.event_cache[row] = lazy_event = LazyEventPartialState( + row, self._event_data_cache + ) + return lazy_event + + def clear(self) -> None: + """Clear the event cache.""" + self._event_data_cache = {} + self.event_cache = {} diff --git a/homeassistant/components/logbook/rest_api.py b/homeassistant/components/logbook/rest_api.py new file mode 100644 index 00000000000..a1a7db3ed2c --- /dev/null +++ b/homeassistant/components/logbook/rest_api.py @@ -0,0 +1,120 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +from datetime import timedelta +from http import HTTPStatus +from typing import Any, cast + +from aiohttp import web +import voluptuous as vol + +from homeassistant.components.http import HomeAssistantView +from homeassistant.components.recorder import get_instance +from homeassistant.components.recorder.filters import Filters +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import InvalidEntityFormatError +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.entityfilter import EntityFilter +from homeassistant.helpers.typing import ConfigType +import homeassistant.util.dt as dt_util + +from .helpers import async_determine_event_types +from .processor import EventProcessor + + +@callback +def async_setup( + hass: HomeAssistant, + conf: ConfigType, + filters: Filters | None, + entities_filter: EntityFilter | None, +) -> None: + """Set up the logbook rest API.""" + hass.http.register_view(LogbookView(conf, filters, entities_filter)) + + +class LogbookView(HomeAssistantView): + """Handle logbook view requests.""" + + url = "/api/logbook" + name = "api:logbook" + extra_urls = ["/api/logbook/{datetime}"] + + def __init__( + self, + config: dict[str, Any], + filters: Filters | None, + entities_filter: EntityFilter | None, + ) -> None: + """Initialize the logbook view.""" + self.config = config + self.filters = filters + self.entities_filter = entities_filter + + async def get( + self, request: web.Request, datetime: str | None = None + ) -> web.Response: + """Retrieve logbook entries.""" + if datetime: + if (datetime_dt := dt_util.parse_datetime(datetime)) is None: + return self.json_message("Invalid datetime", HTTPStatus.BAD_REQUEST) + else: + datetime_dt = dt_util.start_of_local_day() + + if (period_str := request.query.get("period")) is None: + period: int = 1 + else: + period = int(period_str) + + if entity_ids_str := request.query.get("entity"): + try: + entity_ids = cv.entity_ids(entity_ids_str) + except vol.Invalid: + raise InvalidEntityFormatError( + f"Invalid entity id(s) encountered: {entity_ids_str}. " + "Format should be ." + ) from vol.Invalid + else: + entity_ids = None + + if (end_time_str := request.query.get("end_time")) is None: + start_day = dt_util.as_utc(datetime_dt) - timedelta(days=period - 1) + end_day = start_day + timedelta(days=period) + else: + start_day = datetime_dt + if (end_day_dt := dt_util.parse_datetime(end_time_str)) is None: + return self.json_message("Invalid end_time", HTTPStatus.BAD_REQUEST) + end_day = end_day_dt + + hass = request.app["hass"] + + context_id = request.query.get("context_id") + + if entity_ids and context_id: + return self.json_message( + "Can't combine entity with context_id", HTTPStatus.BAD_REQUEST + ) + + event_types = async_determine_event_types(hass, entity_ids, None) + event_processor = EventProcessor( + hass, + event_types, + entity_ids, + None, + context_id, + timestamp=False, + include_entity_name=True, + ) + + def json_events() -> web.Response: + """Fetch events and generate JSON.""" + return self.json( + event_processor.get_events( + start_day, + end_day, + ) + ) + + return cast( + web.Response, await get_instance(hass).async_add_executor_job(json_events) + ) diff --git a/homeassistant/components/logbook/websocket_api.py b/homeassistant/components/logbook/websocket_api.py new file mode 100644 index 00000000000..efb15efe97b --- /dev/null +++ b/homeassistant/components/logbook/websocket_api.py @@ -0,0 +1,319 @@ +"""Event parser and human readable log generator.""" +from __future__ import annotations + +import asyncio +from collections.abc import Callable +from datetime import datetime as dt, timedelta +import logging +from typing import Any + +import voluptuous as vol + +from homeassistant.components import websocket_api +from homeassistant.components.recorder import get_instance +from homeassistant.components.websocket_api import messages +from homeassistant.components.websocket_api.connection import ActiveConnection +from homeassistant.components.websocket_api.const import JSON_DUMP +from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback +import homeassistant.util.dt as dt_util + +from .helpers import ( + async_determine_event_types, + async_filter_entities, + async_subscribe_events, +) +from .models import async_event_to_row +from .processor import EventProcessor + +MAX_PENDING_LOGBOOK_EVENTS = 2048 +EVENT_COALESCE_TIME = 0.5 +MAX_RECORDER_WAIT = 10 + +_LOGGER = logging.getLogger(__name__) + + +@callback +def async_setup(hass: HomeAssistant) -> None: + """Set up the logbook websocket API.""" + websocket_api.async_register_command(hass, ws_get_events) + websocket_api.async_register_command(hass, ws_event_stream) + + +async def _async_get_ws_formatted_events( + hass: HomeAssistant, + msg_id: int, + start_time: dt, + end_time: dt, + formatter: Callable[[int, Any], dict[str, Any]], + event_processor: EventProcessor, +) -> tuple[str, dt | None]: + """Async wrapper around _ws_formatted_get_events.""" + return await get_instance(hass).async_add_executor_job( + _ws_formatted_get_events, + msg_id, + start_time, + end_time, + formatter, + event_processor, + ) + + +def _ws_formatted_get_events( + msg_id: int, + start_day: dt, + end_day: dt, + formatter: Callable[[int, Any], dict[str, Any]], + event_processor: EventProcessor, +) -> tuple[str, dt | None]: + """Fetch events and convert them to json in the executor.""" + events = event_processor.get_events(start_day, end_day) + last_time = None + if events: + last_time = dt_util.utc_from_timestamp(events[-1]["when"]) + result = formatter(msg_id, events) + return JSON_DUMP(result), last_time + + +async def _async_events_consumer( + setup_complete_future: asyncio.Future[dt], + connection: ActiveConnection, + msg_id: int, + stream_queue: asyncio.Queue[Event], + event_processor: EventProcessor, +) -> None: + """Stream events from the queue.""" + subscriptions_setup_complete_time = await setup_complete_future + event_processor.switch_to_live() + + while True: + events: list[Event] = [await stream_queue.get()] + # If the event is older than the last db + # event we already sent it so we skip it. + if events[0].time_fired <= subscriptions_setup_complete_time: + continue + # We sleep for the EVENT_COALESCE_TIME so + # we can group events together to minimize + # the number of websocket messages when the + # system is overloaded with an event storm + await asyncio.sleep(EVENT_COALESCE_TIME) + while not stream_queue.empty(): + events.append(stream_queue.get_nowait()) + + if logbook_events := event_processor.humanify( + async_event_to_row(e) for e in events + ): + connection.send_message( + JSON_DUMP( + messages.event_message( + msg_id, + logbook_events, + ) + ) + ) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "logbook/event_stream", + vol.Required("start_time"): str, + vol.Optional("entity_ids"): [str], + vol.Optional("device_ids"): [str], + } +) +@websocket_api.async_response +async def ws_event_stream( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict +) -> None: + """Handle logbook stream events websocket command.""" + start_time_str = msg["start_time"] + utc_now = dt_util.utcnow() + + if start_time := dt_util.parse_datetime(start_time_str): + start_time = dt_util.as_utc(start_time) + + if not start_time or start_time > utc_now: + connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") + return + + device_ids = msg.get("device_ids") + entity_ids = msg.get("entity_ids") + if entity_ids: + entity_ids = async_filter_entities(hass, entity_ids) + event_types = async_determine_event_types(hass, entity_ids, device_ids) + + event_processor = EventProcessor( + hass, + event_types, + entity_ids, + device_ids, + None, + timestamp=True, + include_entity_name=False, + ) + + stream_queue: asyncio.Queue[Event] = asyncio.Queue(MAX_PENDING_LOGBOOK_EVENTS) + subscriptions: list[CALLBACK_TYPE] = [] + setup_complete_future: asyncio.Future[dt] = asyncio.Future() + task = asyncio.create_task( + _async_events_consumer( + setup_complete_future, + connection, + msg["id"], + stream_queue, + event_processor, + ) + ) + + def _unsub() -> None: + """Unsubscribe from all events.""" + for subscription in subscriptions: + subscription() + subscriptions.clear() + if task: + task.cancel() + + @callback + def _queue_or_cancel(event: Event) -> None: + """Queue an event to be processed or cancel.""" + try: + stream_queue.put_nowait(event) + except asyncio.QueueFull: + _LOGGER.debug( + "Client exceeded max pending messages of %s", + MAX_PENDING_LOGBOOK_EVENTS, + ) + _unsub() + + async_subscribe_events( + hass, subscriptions, _queue_or_cancel, event_types, entity_ids, device_ids + ) + subscriptions_setup_complete_time = dt_util.utcnow() + connection.subscriptions[msg["id"]] = _unsub + connection.send_result(msg["id"]) + + # Fetch everything from history + message, last_event_time = await _async_get_ws_formatted_events( + hass, + msg["id"], + start_time, + subscriptions_setup_complete_time, + messages.event_message, + event_processor, + ) + # If there is no last_time there are no historical + # results, but we still send an empty message so + # consumers of the api know their request was + # answered but there were no results + connection.send_message(message) + try: + await asyncio.wait_for( + get_instance(hass).async_block_till_done(), MAX_RECORDER_WAIT + ) + except asyncio.TimeoutError: + _LOGGER.debug( + "Recorder is behind more than %s seconds, starting live stream; Some results may be missing" + ) + + if setup_complete_future.cancelled(): + # Unsubscribe happened while waiting for recorder + return + + # + # Fetch any events from the database that have + # not been committed since the original fetch + # so we can switch over to using the subscriptions + # + # We only want events that happened after the last event + # we had from the last database query or the maximum + # time we allow the recorder to be behind + # + max_recorder_behind = subscriptions_setup_complete_time - timedelta( + seconds=MAX_RECORDER_WAIT + ) + second_fetch_start_time = max( + last_event_time or max_recorder_behind, max_recorder_behind + ) + message, final_cutoff_time = await _async_get_ws_formatted_events( + hass, + msg["id"], + second_fetch_start_time, + subscriptions_setup_complete_time, + messages.event_message, + event_processor, + ) + if final_cutoff_time: # Only sends results if we have them + connection.send_message(message) + + if not setup_complete_future.cancelled(): + # Unsubscribe happened while waiting for formatted events + setup_complete_future.set_result(subscriptions_setup_complete_time) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "logbook/get_events", + vol.Required("start_time"): str, + vol.Optional("end_time"): str, + vol.Optional("entity_ids"): [str], + vol.Optional("device_ids"): [str], + vol.Optional("context_id"): str, + } +) +@websocket_api.async_response +async def ws_get_events( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict +) -> None: + """Handle logbook get events websocket command.""" + start_time_str = msg["start_time"] + end_time_str = msg.get("end_time") + utc_now = dt_util.utcnow() + + if start_time := dt_util.parse_datetime(start_time_str): + start_time = dt_util.as_utc(start_time) + else: + connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") + return + + if not end_time_str: + end_time = utc_now + elif parsed_end_time := dt_util.parse_datetime(end_time_str): + end_time = dt_util.as_utc(parsed_end_time) + else: + connection.send_error(msg["id"], "invalid_end_time", "Invalid end_time") + return + + if start_time > utc_now: + connection.send_result(msg["id"], []) + return + + device_ids = msg.get("device_ids") + entity_ids = msg.get("entity_ids") + context_id = msg.get("context_id") + if entity_ids: + entity_ids = async_filter_entities(hass, entity_ids) + if not entity_ids and not device_ids: + # Everything has been filtered away + connection.send_result(msg["id"], []) + return + + event_types = async_determine_event_types(hass, entity_ids, device_ids) + + event_processor = EventProcessor( + hass, + event_types, + entity_ids, + device_ids, + context_id, + timestamp=True, + include_entity_name=False, + ) + + message, _ = await _async_get_ws_formatted_events( + hass, + msg["id"], + start_time, + end_time, + messages.result_message, + event_processor, + ) + connection.send_message(message) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 5e6fafdfa61..716e3a19d09 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -75,6 +75,7 @@ from .tasks import ( RecorderTask, StatisticsTask, StopTask, + SynchronizeTask, UpdateStatisticsMetadataTask, WaitTask, ) @@ -928,6 +929,12 @@ class Recorder(threading.Thread): if self._async_event_filter(event): self.queue_task(EventTask(event)) + async def async_block_till_done(self) -> None: + """Async version of block_till_done.""" + event = asyncio.Event() + self.queue_task(SynchronizeTask(event)) + await event.wait() + def block_till_done(self) -> None: """Block till all events processed. diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py index e12526b316a..07855d27dff 100644 --- a/homeassistant/components/recorder/tasks.py +++ b/homeassistant/components/recorder/tasks.py @@ -248,3 +248,17 @@ class AddRecorderPlatformTask(RecorderTask): platforms[domain] = platform if hasattr(self.platform, "exclude_attributes"): hass.data[EXCLUDE_ATTRIBUTES][domain] = platform.exclude_attributes(hass) + + +@dataclass +class SynchronizeTask(RecorderTask): + """Ensure all pending data has been committed.""" + + # commit_before is the default + event: asyncio.Event + + def run(self, instance: Recorder) -> None: + """Handle the task.""" + # Does not use a tracked task to avoid + # blocking shutdown if the recorder is broken + instance.hass.loop.call_soon_threadsafe(self.event.set) diff --git a/homeassistant/components/websocket_api/messages.py b/homeassistant/components/websocket_api/messages.py index 1c09bc1d567..f546ba5eec6 100644 --- a/homeassistant/components/websocket_api/messages.py +++ b/homeassistant/components/websocket_api/messages.py @@ -61,7 +61,7 @@ def error_message(iden: int | None, code: str, message: str) -> dict[str, Any]: } -def event_message(iden: JSON_TYPE, event: Any) -> dict[str, Any]: +def event_message(iden: JSON_TYPE | int, event: Any) -> dict[str, Any]: """Return an event message.""" return {"id": iden, "type": "event", "event": event} diff --git a/homeassistant/core.py b/homeassistant/core.py index 7fcf07d9c66..2753b801347 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -716,13 +716,14 @@ class HomeAssistant: self._stopped.set() -@attr.s(slots=True, frozen=True) +@attr.s(slots=True, frozen=False) class Context: """The context that triggered something.""" user_id: str | None = attr.ib(default=None) parent_id: str | None = attr.ib(default=None) id: str = attr.ib(factory=ulid_util.ulid) + origin_event: Event | None = attr.ib(default=None, eq=False) def as_dict(self) -> dict[str, str | None]: """Return a dictionary representation of the context.""" @@ -866,6 +867,8 @@ class EventBus: listeners = match_all_listeners + listeners event = Event(event_type, event_data, origin, time_fired, context) + if not event.context.origin_event: + event.context.origin_event = event _LOGGER.debug("Bus:Handling %s", event) diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py index 9d6f35eb702..b88c3854967 100644 --- a/tests/components/logbook/common.py +++ b/tests/components/logbook/common.py @@ -5,6 +5,7 @@ import json from typing import Any from homeassistant.components import logbook +from homeassistant.components.logbook import processor from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat from homeassistant.core import Context from homeassistant.helpers import entity_registry as er @@ -50,16 +51,26 @@ class MockRow: def mock_humanify(hass_, rows): """Wrap humanify with mocked logbook objects.""" - entity_name_cache = logbook.EntityNameCache(hass_) + entity_name_cache = processor.EntityNameCache(hass_) ent_reg = er.async_get(hass_) + event_cache = processor.EventCache({}) + context_lookup = processor.ContextLookup(hass_) external_events = hass_.data.get(logbook.DOMAIN, {}) + logbook_run = processor.LogbookRun( + context_lookup, + external_events, + event_cache, + entity_name_cache, + include_entity_name=True, + format_time=processor._row_time_fired_isoformat, + ) + context_augmenter = processor.ContextAugmenter(logbook_run) return list( - logbook._humanify( + processor._humanify( rows, None, ent_reg, - external_events, - entity_name_cache, - logbook._row_time_fired_isoformat, + logbook_run, + context_augmenter, ), ) diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 3b18c594e6e..101fb74e690 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -14,6 +14,9 @@ import voluptuous as vol from homeassistant.components import logbook from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED +from homeassistant.components.logbook.models import LazyEventPartialState +from homeassistant.components.logbook.processor import EventProcessor +from homeassistant.components.logbook.queries.common import PSUEDO_EVENT_STATE_CHANGED from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.sensor import SensorStateClass from homeassistant.const import ( @@ -95,15 +98,12 @@ async def test_service_call_create_logbook_entry(hass_): # Our service call will unblock when the event listeners have been # scheduled. This means that they may not have been processed yet. await async_wait_recording_done(hass_) - ent_reg = er.async_get(hass_) + event_processor = EventProcessor(hass_, (EVENT_LOGBOOK_ENTRY,)) events = list( - logbook._get_events( - hass_, + event_processor.get_events( dt_util.utcnow() - timedelta(hours=1), dt_util.utcnow() + timedelta(hours=1), - (EVENT_LOGBOOK_ENTRY,), - ent_reg, ) ) assert len(events) == 2 @@ -137,15 +137,11 @@ async def test_service_call_create_logbook_entry_invalid_entity_id(hass, recorde }, ) await async_wait_recording_done(hass) - ent_reg = er.async_get(hass) - + event_processor = EventProcessor(hass, (EVENT_LOGBOOK_ENTRY,)) events = list( - logbook._get_events( - hass, + event_processor.get_events( dt_util.utcnow() - timedelta(hours=1), dt_util.utcnow() + timedelta(hours=1), - (EVENT_LOGBOOK_ENTRY,), - ent_reg, ) ) assert len(events) == 1 @@ -335,7 +331,7 @@ def create_state_changed_event_from_old_new( ], ) - row.event_type = logbook.PSUEDO_EVENT_STATE_CHANGED + row.event_type = PSUEDO_EVENT_STATE_CHANGED row.event_data = "{}" row.shared_data = "{}" row.attributes = attributes_json @@ -353,7 +349,7 @@ def create_state_changed_event_from_old_new( row.context_parent_id = None row.old_state_id = old_state and 1 row.state_id = new_state and 1 - return logbook.LazyEventPartialState(row, {}) + return LazyEventPartialState(row, {}) async def test_logbook_view(hass, hass_client, recorder_mock): diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py new file mode 100644 index 00000000000..585732b66f1 --- /dev/null +++ b/tests/components/logbook/test_websocket_api.py @@ -0,0 +1,1166 @@ +"""The tests for the logbook component.""" +import asyncio +from collections.abc import Callable +from datetime import timedelta +from unittest.mock import ANY, patch + +import pytest + +from homeassistant import core +from homeassistant.components import logbook, recorder +from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED +from homeassistant.components.logbook import websocket_api +from homeassistant.components.script import EVENT_SCRIPT_STARTED +from homeassistant.components.websocket_api.const import TYPE_RESULT +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_FRIENDLY_NAME, + ATTR_NAME, + ATTR_UNIT_OF_MEASUREMENT, + EVENT_HOMEASSISTANT_START, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import Event, HomeAssistant, State +from homeassistant.helpers import device_registry +from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util + +from tests.common import MockConfigEntry, SetupRecorderInstanceT +from tests.components.recorder.common import ( + async_block_recorder, + async_recorder_block_till_done, + async_wait_recording_done, +) + + +@pytest.fixture() +def set_utc(hass): + """Set timezone to UTC.""" + hass.config.set_time_zone("UTC") + + +async def _async_mock_device_with_logbook_platform(hass): + """Mock an integration that provides a device that are described by the logbook.""" + entry = MockConfigEntry(domain="test", data={"first": True}, options=None) + entry.add_to_hass(hass) + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_or_create( + config_entry_id=entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + identifiers={("bridgeid", "0123")}, + sw_version="sw-version", + name="device name", + manufacturer="manufacturer", + model="model", + suggested_area="Game Room", + ) + + class MockLogbookPlatform: + """Mock a logbook platform.""" + + @core.callback + def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[ + [str, str, Callable[[Event], dict[str, str]]], None + ], + ) -> None: + """Describe logbook events.""" + + @core.callback + def async_describe_test_event(event: Event) -> dict[str, str]: + """Describe mock logbook event.""" + return { + "name": "device name", + "message": event.data.get("message", "is on fire"), + } + + async_describe_event("test", "mock_event", async_describe_test_event) + + await logbook._process_logbook_platform(hass, "test", MockLogbookPlatform) + return device + + +async def test_get_events(hass, hass_ws_client, recorder_mock): + """Test logbook get_events.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + await async_recorder_block_till_done(hass) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + + hass.states.async_set("light.kitchen", STATE_OFF) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 100}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 200}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 300}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 400}) + await hass.async_block_till_done() + context = core.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + + hass.states.async_set("light.kitchen", STATE_OFF, context=context) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "end_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["result"] == [] + + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["sensor.test"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + assert response["result"] == [] + + await client.send_json( + { + "id": 3, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + + results = response["result"] + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "on" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "off" + + await client.send_json( + { + "id": 4, + "type": "logbook/get_events", + "start_time": now.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 4 + + results = response["result"] + assert len(results) == 3 + assert results[0]["message"] == "started" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "on" + assert isinstance(results[1]["when"], float) + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "off" + assert isinstance(results[2]["when"], float) + + await client.send_json( + { + "id": 5, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 5 + + results = response["result"] + assert len(results) == 1 + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "off" + assert isinstance(results[0]["when"], float) + + +async def test_get_events_entities_filtered_away(hass, hass_ws_client, recorder_mock): + """Test logbook get_events all entities filtered away.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + await async_recorder_block_till_done(hass) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + + hass.states.async_set("light.kitchen", STATE_ON) + await hass.async_block_till_done() + hass.states.async_set( + "light.filtered", STATE_ON, {"brightness": 100, ATTR_UNIT_OF_MEASUREMENT: "any"} + ) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_OFF, {"brightness": 200}) + await hass.async_block_till_done() + hass.states.async_set( + "light.filtered", + STATE_OFF, + {"brightness": 300, ATTR_UNIT_OF_MEASUREMENT: "any"}, + ) + + await async_wait_recording_done(hass) + client = await hass_ws_client() + + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + results = response["result"] + assert results[0]["entity_id"] == "light.kitchen" + assert results[0]["state"] == "off" + + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.filtered"], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + + results = response["result"] + assert len(results) == 0 + + +async def test_get_events_future_start_time(hass, hass_ws_client, recorder_mock): + """Test get_events with a future start time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + future = dt_util.utcnow() + timedelta(hours=10) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": future.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + results = response["result"] + assert isinstance(results, list) + assert len(results) == 0 + + +async def test_get_events_bad_start_time(hass, hass_ws_client, recorder_mock): + """Test get_events bad start time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": "cats", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_start_time" + + +async def test_get_events_bad_end_time(hass, hass_ws_client, recorder_mock): + """Test get_events bad end time.""" + now = dt_util.utcnow() + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "end_time": "dogs", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_end_time" + + +async def test_get_events_invalid_filters(hass, hass_ws_client, recorder_mock): + """Test get_events invalid filters.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "entity_ids": [], + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_format" + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "device_ids": [], + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_format" + + +async def test_get_events_with_device_ids(hass, hass_ws_client, recorder_mock): + """Test logbook get_events for device ids.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook") + ] + ) + + device = await _async_mock_device_with_logbook_platform(hass) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + hass.bus.async_fire("mock_event", {"device_id": device.id}) + + hass.states.async_set("light.kitchen", STATE_OFF) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 100}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 200}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 300}) + await hass.async_block_till_done() + hass.states.async_set("light.kitchen", STATE_ON, {"brightness": 400}) + await hass.async_block_till_done() + context = core.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + + hass.states.async_set("light.kitchen", STATE_OFF, context=context) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + client = await hass_ws_client() + + await client.send_json( + { + "id": 1, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "device_ids": [device.id], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 1 + + results = response["result"] + assert len(results) == 1 + assert results[0]["name"] == "device name" + assert results[0]["message"] == "is on fire" + assert isinstance(results[0]["when"], float) + + await client.send_json( + { + "id": 2, + "type": "logbook/get_events", + "start_time": now.isoformat(), + "entity_ids": ["light.kitchen"], + "device_ids": [device.id], + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 2 + + results = response["result"] + assert results[0]["domain"] == "test" + assert results[0]["message"] == "is on fire" + assert results[0]["name"] == "device name" + assert results[1]["entity_id"] == "light.kitchen" + assert results[1]["state"] == "on" + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "off" + + await client.send_json( + { + "id": 3, + "type": "logbook/get_events", + "start_time": now.isoformat(), + } + ) + response = await client.receive_json() + assert response["success"] + assert response["id"] == 3 + + results = response["result"] + assert len(results) == 4 + assert results[0]["message"] == "started" + assert results[1]["name"] == "device name" + assert results[1]["message"] == "is on fire" + assert isinstance(results[1]["when"], float) + assert results[2]["entity_id"] == "light.kitchen" + assert results[2]["state"] == "on" + assert isinstance(results[2]["when"], float) + assert results[3]["entity_id"] == "light.kitchen" + assert results[3]["state"] == "off" + assert isinstance(results[3]["when"], float) + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + {"id": 7, "type": "logbook/event_stream", "start_time": now.isoformat()} + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + + hass.states.async_set("light.alpha", "on") + hass.states.async_set("light.alpha", "off") + alpha_off_state: State = hass.states.get("light.alpha") + hass.states.async_set("light.zulu", "on", {"color": "blue"}) + hass.states.async_set("light.zulu", "off", {"effect": "help"}) + zulu_off_state: State = hass.states.get("light.zulu") + hass.states.async_set( + "light.zulu", "on", {"effect": "help", "color": ["blue", "green"]} + ) + zulu_on_state: State = hass.states.get("light.zulu") + await hass.async_block_till_done() + + hass.states.async_remove("light.zulu") + await hass.async_block_till_done() + + hass.states.async_set("light.zulu", "on", {"effect": "help", "color": "blue"}) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "light.alpha", + "state": "off", + "when": alpha_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "off", + "when": zulu_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "on", + "when": zulu_on_state.last_updated.timestamp(), + }, + ] + + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: "automation.mock_automation"}, + ) + hass.bus.async_fire( + EVENT_SCRIPT_STARTED, + {ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"}, + ) + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "context_id": ANY, + "domain": "automation", + "entity_id": "automation.mock_automation", + "message": "triggered", + "name": "Mock automation", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "script", + "entity_id": "script.mock_script", + "message": "started", + "name": "Mock script", + "when": ANY, + }, + { + "domain": "homeassistant", + "icon": "mdi:home-assistant", + "message": "started", + "name": "Home Assistant", + "when": ANY, + }, + ] + + context = core.Context( + id="ac5bd62de45711eaaeb351041eec8dd9", + user_id="b400facee45711eaa9308bfd3d19e474", + ) + automation_entity_id_test = "automation.alarm" + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: automation_entity_id_test}, + context=context, + ) + hass.bus.async_fire( + EVENT_SCRIPT_STARTED, + {ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"}, + context=context, + ) + hass.states.async_set( + automation_entity_id_test, + STATE_ON, + {ATTR_FRIENDLY_NAME: "Alarm Automation"}, + context=context, + ) + entity_id_test = "alarm_control_panel.area_001" + hass.states.async_set(entity_id_test, STATE_OFF, context=context) + hass.states.async_set(entity_id_test, STATE_ON, context=context) + entity_id_second = "alarm_control_panel.area_002" + hass.states.async_set(entity_id_second, STATE_OFF, context=context) + hass.states.async_set(entity_id_second, STATE_ON, context=context) + + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "domain": "automation", + "entity_id": "automation.alarm", + "message": "triggered", + "name": "Mock automation", + "source": None, + "when": ANY, + }, + { + "context_domain": "automation", + "context_entity_id": "automation.alarm", + "context_event_type": "automation_triggered", + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + "context_message": "triggered", + "context_name": "Mock automation", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "domain": "script", + "entity_id": "script.mock_script", + "message": "started", + "name": "Mock script", + "when": ANY, + }, + { + "context_domain": "automation", + "context_entity_id": "automation.alarm", + "context_event_type": "automation_triggered", + "context_message": "triggered", + "context_name": "Mock automation", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "entity_id": "alarm_control_panel.area_001", + "state": "on", + "when": ANY, + }, + { + "context_domain": "automation", + "context_entity_id": "automation.alarm", + "context_event_type": "automation_triggered", + "context_message": "triggered", + "context_name": "Mock automation", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "entity_id": "alarm_control_panel.area_002", + "state": "on", + "when": ANY, + }, + ] + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 2", ATTR_ENTITY_ID: automation_entity_id_test}, + context=context, + ) + + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "context_domain": "automation", + "context_entity_id": "automation.alarm", + "context_event_type": "automation_triggered", + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + "context_message": "triggered", + "context_name": "Mock automation", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "domain": "automation", + "entity_id": "automation.alarm", + "message": "triggered", + "name": "Mock automation 2", + "source": None, + "when": ANY, + } + ] + + await async_wait_recording_done(hass) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: automation_entity_id_test}, + context=context, + ) + + await hass.async_block_till_done() + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "context_domain": "automation", + "context_entity_id": "automation.alarm", + "context_event_type": "automation_triggered", + "context_id": "ac5bd62de45711eaaeb351041eec8dd9", + "context_message": "triggered", + "context_name": "Mock automation", + "context_user_id": "b400facee45711eaa9308bfd3d19e474", + "domain": "automation", + "entity_id": "automation.alarm", + "message": "triggered", + "name": "Mock automation 3", + "source": None, + "when": ANY, + } + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await websocket_client.receive_json() + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_entities( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with specific entities.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "entity_ids": ["light.small", "binary_sensor.is_light"], + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + + hass.states.async_set("light.alpha", STATE_ON) + hass.states.async_set("light.alpha", STATE_OFF) + hass.states.async_set("light.small", STATE_OFF, {"effect": "help", "color": "blue"}) + + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "light.small", + "state": "off", + "when": ANY, + }, + ] + + hass.states.async_remove("light.alpha") + hass.states.async_remove("light.small") + await hass.async_block_till_done() + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await websocket_client.receive_json() + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_device( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with a device.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + device = await _async_mock_device_with_logbook_platform(hass) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "device_ids": [device.id], + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # There are no answers to our initial query + # so we get an empty reply. This is to ensure + # consumers of the api know there are no results + # and its not a failure case. This is useful + # in the frontend so we can tell the user there + # are no results vs waiting for them to appear + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [] + + hass.bus.async_fire("mock_event", {"device_id": device.id}) + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + {"domain": "test", "message": "is on fire", "name": "device name", "when": ANY} + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await websocket_client.receive_json() + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +async def test_event_stream_bad_start_time(hass, hass_ws_client, recorder_mock): + """Test event_stream bad start time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/event_stream", + "start_time": "cats", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_start_time" + + +async def test_live_stream_with_one_second_commit_interval( + hass: HomeAssistant, + async_setup_recorder_instance: SetupRecorderInstanceT, + hass_ws_client, +): + """Test the recorder with a 1s commit interval.""" + config = {recorder.CONF_COMMIT_INTERVAL: 0.5} + await async_setup_recorder_instance(hass, config) + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + device = await _async_mock_device_with_logbook_platform(hass) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "1"}) + + await async_wait_recording_done(hass) + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "2"}) + + await hass.async_block_till_done() + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "3"}) + + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "device_ids": [device.id], + } + ) + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "4"}) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "5"}) + + recieved_rows = [] + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + recieved_rows.extend(msg["event"]) + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "6"}) + + await hass.async_block_till_done() + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "7"}) + + while not len(recieved_rows) == 7: + msg = await asyncio.wait_for(websocket_client.receive_json(), 2.5) + assert msg["id"] == 7 + assert msg["type"] == "event" + recieved_rows.extend(msg["event"]) + + # Make sure we get rows back in order + assert recieved_rows == [ + {"domain": "test", "message": "1", "name": "device name", "when": ANY}, + {"domain": "test", "message": "2", "name": "device name", "when": ANY}, + {"domain": "test", "message": "3", "name": "device name", "when": ANY}, + {"domain": "test", "message": "4", "name": "device name", "when": ANY}, + {"domain": "test", "message": "5", "name": "device name", "when": ANY}, + {"domain": "test", "message": "6", "name": "device name", "when": ANY}, + {"domain": "test", "message": "7", "name": "device name", "when": ANY}, + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await websocket_client.receive_json() + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_disconnected(hass, recorder_mock, hass_ws_client): + """Test subscribe/unsubscribe logbook stream gets disconnected.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + await async_wait_recording_done(hass) + + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "entity_ids": ["light.small", "binary_sensor.is_light"], + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + + await websocket_client.close() + await hass.async_block_till_done() + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_stream_consumer_stop_processing(hass, recorder_mock, hass_ws_client): + """Test we unsubscribe if the stream consumer fails or is canceled.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + await async_wait_recording_done(hass) + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + + after_ws_created_count = sum(hass.bus.async_listeners().values()) + + with patch.object(websocket_api, "MAX_PENDING_LOGBOOK_EVENTS", 5), patch.object( + websocket_api, "_async_events_consumer" + ): + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "entity_ids": ["light.small", "binary_sensor.is_light"], + } + ) + await async_wait_recording_done(hass) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + assert sum(hass.bus.async_listeners().values()) != init_count + for _ in range(5): + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + await async_wait_recording_done(hass) + + # Check our listener got unsubscribed because + # the queue got full and the overload safety tripped + assert sum(hass.bus.async_listeners().values()) == after_ws_created_count + await websocket_client.close() + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +@patch("homeassistant.components.logbook.websocket_api.MAX_RECORDER_WAIT", 0.15) +async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplog): + """Test we still start live streaming if the recorder is far behind.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + await async_wait_recording_done(hass) + device = await _async_mock_device_with_logbook_platform(hass) + await async_wait_recording_done(hass) + + # Block the recorder queue + await async_block_recorder(hass, 0.3) + await hass.async_block_till_done() + + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "device_ids": [device.id], + } + ) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # There are no answers to our initial query + # so we get an empty reply. This is to ensure + # consumers of the api know there are no results + # and its not a failure case. This is useful + # in the frontend so we can tell the user there + # are no results vs waiting for them to appear + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [] + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "1"}) + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + {"domain": "test", "message": "1", "name": "device name", "when": ANY} + ] + + hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "2"}) + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + {"domain": "test", "message": "2", "name": "device name", "when": ANY} + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await websocket_client.receive_json() + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + assert "Recorder is behind" in caplog.text diff --git a/tests/components/recorder/common.py b/tests/components/recorder/common.py index b36547309cd..39cde4c2e7c 100644 --- a/tests/components/recorder/common.py +++ b/tests/components/recorder/common.py @@ -1,7 +1,10 @@ """Common test utils for working with recorder.""" from __future__ import annotations +import asyncio +from dataclasses import dataclass from datetime import datetime, timedelta +import time from typing import Any, cast from sqlalchemy import create_engine @@ -10,8 +13,9 @@ from sqlalchemy.orm.session import Session from homeassistant import core as ha from homeassistant.components import recorder from homeassistant.components.recorder import get_instance, statistics +from homeassistant.components.recorder.core import Recorder from homeassistant.components.recorder.models import RecorderRuns -from homeassistant.components.recorder.tasks import StatisticsTask +from homeassistant.components.recorder.tasks import RecorderTask, StatisticsTask from homeassistant.core import HomeAssistant from homeassistant.util import dt as dt_util @@ -21,6 +25,31 @@ from tests.components.recorder import models_schema_0 DEFAULT_PURGE_TASKS = 3 +@dataclass +class BlockRecorderTask(RecorderTask): + """A task to block the recorder for testing only.""" + + event: asyncio.Event + seconds: float + + def run(self, instance: Recorder) -> None: + """Block the recorders event loop.""" + instance.hass.loop.call_soon_threadsafe(self.event.set) + time.sleep(self.seconds) + + +async def async_block_recorder(hass: HomeAssistant, seconds: float) -> None: + """Block the recorders event loop for testing. + + Returns as soon as the recorder has started the block. + + Does not wait for the block to finish. + """ + event = asyncio.Event() + get_instance(hass).queue_task(BlockRecorderTask(event, seconds)) + await event.wait() + + def do_adhoc_statistics(hass: HomeAssistant, **kwargs: Any) -> None: """Trigger an adhoc statistics run.""" if not (start := kwargs.get("start")): diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index 49133dd67bd..41c4428ee5e 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -55,6 +55,7 @@ from homeassistant.setup import async_setup_component, setup_component from homeassistant.util import dt as dt_util from .common import ( + async_block_recorder, async_wait_recording_done, corrupt_db_file, run_information_with_session, @@ -1537,3 +1538,25 @@ def test_deduplication_state_attributes_inside_commit_interval(hass_recorder, ca first_attributes_id = states[0].attributes_id last_attributes_id = states[-1].attributes_id assert first_attributes_id == last_attributes_id + + +async def test_async_block_till_done(hass, async_setup_recorder_instance): + """Test we can block until recordering is done.""" + instance = await async_setup_recorder_instance(hass) + await async_wait_recording_done(hass) + + entity_id = "test.recorder" + attributes = {"test_attr": 5, "test_attr_10": "nice"} + + hass.states.async_set(entity_id, "on", attributes) + hass.states.async_set(entity_id, "off", attributes) + + def _fetch_states(): + with session_scope(hass=hass) as session: + return list(session.query(States).filter(States.entity_id == entity_id)) + + await async_block_recorder(hass, 0.1) + await instance.async_block_till_done() + states = await instance.async_add_executor_job(_fetch_states) + assert len(states) == 2 + await hass.async_block_till_done() diff --git a/tests/test_core.py b/tests/test_core.py index 1ac7002cb7b..ee1005a60b0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1799,3 +1799,33 @@ async def test_state_firing_event_matches_context_id_ulid_time(hass): assert _ulid_timestamp(event.context.id) == int( events[0].time_fired.timestamp() * 1000 ) + + +async def test_event_context(hass): + """Test we can lookup the origin of a context from an event.""" + events = [] + + @ha.callback + def capture_events(event): + nonlocal events + events.append(event) + + cancel = hass.bus.async_listen("dummy_event", capture_events) + cancel2 = hass.bus.async_listen("dummy_event_2", capture_events) + + hass.bus.async_fire("dummy_event") + await hass.async_block_till_done() + + dummy_event: ha.Event = events[0] + + hass.bus.async_fire("dummy_event_2", context=dummy_event.context) + await hass.async_block_till_done() + context_id = dummy_event.context.id + + dummy_event2: ha.Event = events[1] + assert dummy_event2.context == dummy_event.context + assert dummy_event2.context.id == context_id + cancel() + cancel2() + + assert dummy_event2.context.origin_event == dummy_event From e6ffae8bd3570c61ce3b8d9f15a3d3b678537650 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 22 May 2022 14:29:11 -0700 Subject: [PATCH 687/930] Deprecate google calendar configuration.yaml (#72288) * Deprecate google calendar configuration.yaml * Remove unused translations * Enable strict type checking and address pr feedback * Move default hass.data init to `async_setup` --- .strict-typing | 1 + homeassistant/components/google/__init__.py | 88 ++++++++++++++----- homeassistant/components/google/api.py | 23 ++--- homeassistant/components/google/calendar.py | 30 +++++-- .../components/google/config_flow.py | 68 ++++++++++++-- homeassistant/components/google/strings.json | 9 ++ .../components/google/translations/en.json | 9 ++ mypy.ini | 11 +++ tests/components/google/test_config_flow.py | 73 ++++++++++++++- tests/components/google/test_init.py | 2 +- 10 files changed, 267 insertions(+), 47 deletions(-) diff --git a/.strict-typing b/.strict-typing index 563e6e9f91a..2b6295b9157 100644 --- a/.strict-typing +++ b/.strict-typing @@ -101,6 +101,7 @@ homeassistant.components.geo_location.* homeassistant.components.geocaching.* homeassistant.components.gios.* homeassistant.components.goalzero.* +homeassistant.components.google.* homeassistant.components.greeneye_monitor.* homeassistant.components.group.* homeassistant.components.guardian.* diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 73b7f8d8ae6..70732af1fd8 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -97,23 +97,27 @@ PLATFORMS = ["calendar"] CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - vol.Optional(CONF_TRACK_NEW, default=True): cv.boolean, - vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum( - FeatureAccess - ), - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_TRACK_NEW, default=True): cv.boolean, + vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum( + FeatureAccess + ), + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) _SINGLE_CALSEARCH_CONFIG = vol.All( cv.deprecated(CONF_MAX_RESULTS), + cv.deprecated(CONF_TRACK), vol.Schema( { vol.Required(CONF_NAME): cv.string, @@ -160,6 +164,9 @@ ADD_EVENT_SERVICE_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Google component.""" + if DOMAIN not in config: + return True + conf = config.get(DOMAIN, {}) hass.data[DOMAIN] = {DATA_CONFIG: conf} @@ -189,11 +196,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: }, ) ) + + _LOGGER.warning( + "Configuration of Google Calendar in YAML in configuration.yaml is " + "is deprecated and will be removed in a future release; Your existing " + "OAuth Application Credentials and other settings have been imported " + "into the UI automatically and can be safely removed from your " + "configuration.yaml file" + ) + return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Google from a config entry.""" + hass.data.setdefault(DOMAIN, {}) + async_upgrade_entry(hass, entry) implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry @@ -216,8 +234,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except aiohttp.ClientError as err: raise ConfigEntryNotReady from err - access = get_feature_access(hass) - if access.scope not in session.token.get("scope", []): + access = FeatureAccess[entry.options[CONF_CALENDAR_ACCESS]] + token_scopes = session.token.get("scope", []) + if access.scope not in token_scopes: + _LOGGER.debug("Scope '%s' not in scopes '%s'", access.scope, token_scopes) raise ConfigEntryAuthFailed( "Required scopes are not available, reauth required" ) @@ -226,25 +246,53 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) hass.data[DOMAIN][DATA_SERVICE] = calendar_service - track_new = hass.data[DOMAIN][DATA_CONFIG].get(CONF_TRACK_NEW, True) - await async_setup_services(hass, track_new, calendar_service) + await async_setup_services(hass, calendar_service) # Only expose the add event service if we have the correct permissions if access is FeatureAccess.read_write: await async_setup_add_event_service(hass, calendar_service) hass.config_entries.async_setup_platforms(entry, PLATFORMS) + # Reload entry when options are updated + entry.async_on_unload(entry.add_update_listener(async_reload_entry)) + return True +def async_upgrade_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Upgrade the config entry if needed.""" + if DATA_CONFIG not in hass.data[DOMAIN] and entry.options: + return + + options = ( + entry.options + if entry.options + else { + CONF_CALENDAR_ACCESS: get_feature_access(hass).name, + } + ) + disable_new_entities = ( + not hass.data[DOMAIN].get(DATA_CONFIG, {}).get(CONF_TRACK_NEW, True) + ) + hass.config_entries.async_update_entry( + entry, + options=options, + pref_disable_new_entities=disable_new_entities, + ) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) +async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Reload the config entry when it changed.""" + await hass.config_entries.async_reload(entry.entry_id) + + async def async_setup_services( hass: HomeAssistant, - track_new: bool, calendar_service: GoogleCalendarService, ) -> None: """Set up the service listeners.""" @@ -256,10 +304,7 @@ async def async_setup_services( async def _found_calendar(calendar_item: Calendar) -> None: calendar = get_calendar_info( hass, - { - **calendar_item.dict(exclude_unset=True), - CONF_TRACK: track_new, - }, + calendar_item.dict(exclude_unset=True), ) calendar_id = calendar_item.id # Populate the yaml file with all discovered calendars @@ -363,7 +408,6 @@ def get_calendar_info( CONF_CAL_ID: calendar["id"], CONF_ENTITIES: [ { - CONF_TRACK: calendar["track"], CONF_NAME: calendar["summary"], CONF_DEVICE_ID: generate_entity_id( "{}", calendar["summary"], hass=hass diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index 3ce21fbb03d..eeac854a2ae 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -6,7 +6,7 @@ from collections.abc import Awaitable, Callable import datetime import logging import time -from typing import Any +from typing import Any, cast import aiohttp from gcal_sync.auth import AbstractAuth @@ -76,12 +76,12 @@ class DeviceFlow: @property def verification_url(self) -> str: """Return the verification url that the user should visit to enter the code.""" - return self._device_flow_info.verification_url + return self._device_flow_info.verification_url # type: ignore[no-any-return] @property def user_code(self) -> str: """Return the code that the user should enter at the verification url.""" - return self._device_flow_info.user_code + return self._device_flow_info.user_code # type: ignore[no-any-return] async def start_exchange_task( self, finished_cb: Callable[[Credentials | None], Awaitable[None]] @@ -131,10 +131,13 @@ def get_feature_access(hass: HomeAssistant) -> FeatureAccess: """Return the desired calendar feature access.""" # This may be called during config entry setup without integration setup running when there # is no google entry in configuration.yaml - return ( - hass.data.get(DOMAIN, {}) - .get(DATA_CONFIG, {}) - .get(CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS) + return cast( + FeatureAccess, + ( + hass.data.get(DOMAIN, {}) + .get(DATA_CONFIG, {}) + .get(CONF_CALENDAR_ACCESS, DEFAULT_FEATURE_ACCESS) + ), ) @@ -157,7 +160,7 @@ async def async_create_device_flow( return DeviceFlow(hass, oauth_flow, device_flow_info) -class ApiAuthImpl(AbstractAuth): +class ApiAuthImpl(AbstractAuth): # type: ignore[misc] """Authentication implementation for google calendar api library.""" def __init__( @@ -172,10 +175,10 @@ class ApiAuthImpl(AbstractAuth): async def async_get_access_token(self) -> str: """Return a valid access token.""" await self._session.async_ensure_token_valid() - return self._session.token["access_token"] + return cast(str, self._session.token["access_token"]) -class AccessTokenAuthImpl(AbstractAuth): +class AccessTokenAuthImpl(AbstractAuth): # type: ignore[misc] """Authentication implementation used during config flow, without refresh. This exists to allow the config flow to use the API before it has fully diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index b0d13c0c0c6..01780702b7f 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -1,4 +1,5 @@ """Support for Google Calendar Search binary sensors.""" + from __future__ import annotations import copy @@ -89,14 +90,25 @@ def _async_setup_entities( ) -> None: calendar_service = hass.data[DOMAIN][DATA_SERVICE] entities = [] + num_entities = len(disc_info[CONF_ENTITIES]) for data in disc_info[CONF_ENTITIES]: - if not data[CONF_TRACK]: - continue - entity_id = generate_entity_id( - ENTITY_ID_FORMAT, data[CONF_DEVICE_ID], hass=hass - ) + entity_enabled = data.get(CONF_TRACK, True) + entity_name = data[CONF_DEVICE_ID] + entity_id = generate_entity_id(ENTITY_ID_FORMAT, entity_name, hass=hass) + calendar_id = disc_info[CONF_CAL_ID] + if num_entities > 1: + # The google_calendars.yaml file lets users add multiple entities for + # the same calendar id and needs additional disambiguation + unique_id = f"{calendar_id}-{entity_name}" + else: + unique_id = calendar_id entity = GoogleCalendarEntity( - calendar_service, disc_info[CONF_CAL_ID], data, entity_id + calendar_service, + disc_info[CONF_CAL_ID], + data, + entity_id, + unique_id, + entity_enabled, ) entities.append(entity) @@ -112,6 +124,8 @@ class GoogleCalendarEntity(CalendarEntity): calendar_id: str, data: dict[str, Any], entity_id: str, + unique_id: str, + entity_enabled: bool, ) -> None: """Create the Calendar event device.""" self._calendar_service = calendar_service @@ -123,6 +137,8 @@ class GoogleCalendarEntity(CalendarEntity): self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET) self._offset_value: timedelta | None = None self.entity_id = entity_id + self._attr_unique_id = unique_id + self._attr_entity_registry_enabled_default = entity_enabled @property def extra_state_attributes(self) -> dict[str, bool]: @@ -152,7 +168,7 @@ class GoogleCalendarEntity(CalendarEntity): """Return True if the event is visible.""" if self._ignore_availability: return True - return event.transparency == OPAQUE + return event.transparency == OPAQUE # type: ignore[no-any-return] async def async_get_events( self, hass: HomeAssistant, start_date: datetime, end_date: datetime diff --git a/homeassistant/components/google/config_flow.py b/homeassistant/components/google/config_flow.py index 3b513f17197..be516230d2b 100644 --- a/homeassistant/components/google/config_flow.py +++ b/homeassistant/components/google/config_flow.py @@ -7,7 +7,10 @@ from typing import Any from gcal_sync.api import GoogleCalendarService from gcal_sync.exceptions import ApiException from oauth2client.client import Credentials +import voluptuous as vol +from homeassistant import config_entries +from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -21,7 +24,7 @@ from .api import ( async_create_device_flow, get_feature_access, ) -from .const import DOMAIN +from .const import CONF_CALENDAR_ACCESS, DOMAIN, FeatureAccess _LOGGER = logging.getLogger(__name__) @@ -36,7 +39,7 @@ class OAuth2FlowHandler( def __init__(self) -> None: """Set up instance.""" super().__init__() - self._reauth = False + self._reauth_config_entry: config_entries.ConfigEntry | None = None self._device_flow: DeviceFlow | None = None @property @@ -60,7 +63,7 @@ class OAuth2FlowHandler( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle external yaml configuration.""" - if not self._reauth and self._async_current_entries(): + if not self._reauth_config_entry and self._async_current_entries(): return self.async_abort(reason="already_configured") return await super().async_step_user(user_input) @@ -84,12 +87,17 @@ class OAuth2FlowHandler( self.flow_impl, ) return self.async_abort(reason="oauth_error") + calendar_access = get_feature_access(self.hass) + if self._reauth_config_entry and self._reauth_config_entry.options: + calendar_access = FeatureAccess[ + self._reauth_config_entry.options[CONF_CALENDAR_ACCESS] + ] try: device_flow = await async_create_device_flow( self.hass, self.flow_impl.client_id, self.flow_impl.client_secret, - get_feature_access(self.hass), + calendar_access, ) except OAuthError as err: _LOGGER.error("Error initializing device flow: %s", str(err)) @@ -146,13 +154,21 @@ class OAuth2FlowHandler( _LOGGER.debug("Error reading calendar primary calendar: %s", err) primary_calendar = None title = primary_calendar.id if primary_calendar else self.flow_impl.name - return self.async_create_entry(title=title, data=data) + return self.async_create_entry( + title=title, + data=data, + options={ + CONF_CALENDAR_ACCESS: get_feature_access(self.hass).name, + }, + ) async def async_step_reauth( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Perform reauth upon an API authentication error.""" - self._reauth = True + self._reauth_config_entry = self.hass.config_entries.async_get_entry( + self.context["entry_id"] + ) return await self.async_step_reauth_confirm() async def async_step_reauth_confirm( @@ -162,3 +178,43 @@ class OAuth2FlowHandler( if user_input is None: return self.async_show_form(step_id="reauth_confirm") return await self.async_step_user() + + @staticmethod + @callback + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> config_entries.OptionsFlow: + """Create an options flow.""" + return OptionsFlowHandler(config_entry) + + +class OptionsFlowHandler(config_entries.OptionsFlow): + """Google Calendar options flow.""" + + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Manage the options.""" + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_CALENDAR_ACCESS, + default=self.config_entry.options.get(CONF_CALENDAR_ACCESS), + ): vol.In( + { + "read_write": "Read/Write access (can create events)", + "read_only": "Read-only access", + } + ) + } + ), + ) diff --git a/homeassistant/components/google/strings.json b/homeassistant/components/google/strings.json index e8ec7091030..e32223627be 100644 --- a/homeassistant/components/google/strings.json +++ b/homeassistant/components/google/strings.json @@ -27,5 +27,14 @@ "progress": { "exchange": "To link your Google account, visit the [{url}]({url}) and enter code:\n\n{user_code}" } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant access to Google Calendar" + } + } + } } } diff --git a/homeassistant/components/google/translations/en.json b/homeassistant/components/google/translations/en.json index 02c8e6d7029..58c89834ca5 100644 --- a/homeassistant/components/google/translations/en.json +++ b/homeassistant/components/google/translations/en.json @@ -27,5 +27,14 @@ "title": "Reauthenticate Integration" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant access to Google Calendar" + } + } + } } } \ No newline at end of file diff --git a/mypy.ini b/mypy.ini index ed073141fe1..3cc9653e27a 100644 --- a/mypy.ini +++ b/mypy.ini @@ -874,6 +874,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.google.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.greeneye_monitor.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 625d1faa937..77ebe1e56cd 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -21,6 +21,7 @@ from homeassistant.components.application_credentials import ( async_import_client_credential, ) from homeassistant.components.google.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.util.dt import utcnow @@ -143,6 +144,7 @@ async def test_full_flow_yaml_creds( "token_type": "Bearer", }, } + assert result.get("options") == {"calendar_access": "read_write"} assert len(mock_setup.mock_calls) == 1 entries = hass.config_entries.async_entries(DOMAIN) @@ -205,6 +207,7 @@ async def test_full_flow_application_creds( "token_type": "Bearer", }, } + assert result.get("options") == {"calendar_access": "read_write"} assert len(mock_setup.mock_calls) == 1 entries = hass.config_entries.async_entries(DOMAIN) @@ -441,7 +444,12 @@ async def test_reauth_flow( assert await component_setup() result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_REAUTH}, data=config_entry.data + DOMAIN, + context={ + "source": config_entries.SOURCE_REAUTH, + "entry_id": config_entry.entry_id, + }, + data=config_entry.data, ) assert result["type"] == "form" assert result["step_id"] == "reauth_confirm" @@ -523,3 +531,66 @@ async def test_title_lookup_failure( assert len(mock_setup.mock_calls) == 1 entries = hass.config_entries.async_entries(DOMAIN) assert len(entries) == 1 + + +async def test_options_flow_triggers_reauth( + hass: HomeAssistant, + component_setup: ComponentSetup, + config_entry: MockConfigEntry, +) -> None: + """Test load and unload of a ConfigEntry.""" + config_entry.add_to_hass(hass) + await component_setup() + assert config_entry.state is ConfigEntryState.LOADED + assert config_entry.options == {"calendar_access": "read_write"} + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "init" + data_schema = result["data_schema"].schema + assert set(data_schema) == {"calendar_access"} + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "calendar_access": "read_only", + }, + ) + assert result["type"] == "create_entry" + + await hass.async_block_till_done() + assert config_entry.options == {"calendar_access": "read_only"} + # Re-auth flow was initiated because access level changed + assert config_entry.state is ConfigEntryState.SETUP_ERROR + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + assert flows[0]["step_id"] == "reauth_confirm" + + +async def test_options_flow_no_changes( + hass: HomeAssistant, + component_setup: ComponentSetup, + config_entry: MockConfigEntry, +) -> None: + """Test load and unload of a ConfigEntry.""" + config_entry.add_to_hass(hass) + await component_setup() + assert config_entry.state is ConfigEntryState.LOADED + assert config_entry.options == {"calendar_access": "read_write"} + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + "calendar_access": "read_write", + }, + ) + assert result["type"] == "create_entry" + + await hass.async_block_till_done() + assert config_entry.options == {"calendar_access": "read_write"} + # Re-auth flow was initiated because access level changed + assert config_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index 511e8545b40..4709379e840 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -46,7 +46,7 @@ HassApi = Callable[[], Awaitable[dict[str, Any]]] def assert_state(actual: State | None, expected: State | None) -> None: """Assert that the two states are equal.""" - if actual is None: + if actual is None or expected is None: assert actual == expected return assert actual.entity_id == expected.entity_id From 39ce25f76dd0ded7db2f1609fc457fe8a37fe8c1 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 22 May 2022 14:30:57 -0700 Subject: [PATCH 688/930] Add Withings application_credentials platform (#71990) * Add Withings application_credentials platform * Update withings auth implementation --- homeassistant/components/withings/__init__.py | 39 ++++++++++++------- .../withings/application_credentials.py | 28 +++++++++++++ homeassistant/components/withings/common.py | 4 +- .../components/withings/manifest.json | 2 +- .../generated/application_credentials.py | 1 + tests/components/withings/test_init.py | 2 - 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 homeassistant/components/withings/application_credentials.py diff --git a/homeassistant/components/withings/__init__.py b/homeassistant/components/withings/__init__.py index 9d1fe1a600f..47702090cc0 100644 --- a/homeassistant/components/withings/__init__.py +++ b/homeassistant/components/withings/__init__.py @@ -9,10 +9,13 @@ import asyncio from aiohttp.web import Request, Response import voluptuous as vol -from withings_api import AbstractWithingsApi, WithingsAuth from withings_api.common import NotifyAppli from homeassistant.components import webhook +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.webhook import ( async_unregister as async_unregister_webhook, ) @@ -28,10 +31,9 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import async_call_later from homeassistant.helpers.typing import ConfigType -from . import config_flow, const +from . import const from .common import ( _LOGGER, - WithingsLocalOAuth2Implementation, async_get_data_manager, async_remove_data_manager, get_data_manager_by_webhook_id, @@ -45,10 +47,12 @@ CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.All( cv.deprecated(const.CONF_PROFILES), + cv.deprecated(CONF_CLIENT_ID), + cv.deprecated(CONF_CLIENT_SECRET), vol.Schema( { - vol.Required(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(min=1)), - vol.Required(CONF_CLIENT_SECRET): vol.All( + vol.Optional(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(min=1)), + vol.Optional(CONF_CLIENT_SECRET): vol.All( cv.string, vol.Length(min=1) ), vol.Optional(const.CONF_USE_WEBHOOK, default=False): cv.boolean, @@ -76,17 +80,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.data[DOMAIN] = {const.CONFIG: conf} # Setup the oauth2 config flow. - config_flow.WithingsFlowHandler.async_register_implementation( - hass, - WithingsLocalOAuth2Implementation( + if CONF_CLIENT_ID in conf: + await async_import_client_credential( hass, - const.DOMAIN, - conf[CONF_CLIENT_ID], - conf[CONF_CLIENT_SECRET], - f"{WithingsAuth.URL}/oauth2_user/authorize2", - f"{AbstractWithingsApi.URL}/v2/oauth2", - ), - ) + DOMAIN, + ClientCredential( + conf[CONF_CLIENT_ID], + conf[CONF_CLIENT_SECRET], + ), + ) + _LOGGER.warning( + "Configuration of Withings integration OAuth2 credentials in YAML " + "is deprecated and will be removed in a future release; Your " + "existing OAuth Application Credentials have been imported into " + "the UI automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/withings/application_credentials.py b/homeassistant/components/withings/application_credentials.py new file mode 100644 index 00000000000..e5c401d5e74 --- /dev/null +++ b/homeassistant/components/withings/application_credentials.py @@ -0,0 +1,28 @@ +"""application_credentials platform for Withings.""" + +from withings_api import AbstractWithingsApi, WithingsAuth + +from homeassistant.components.application_credentials import ( + AuthorizationServer, + ClientCredential, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .common import WithingsLocalOAuth2Implementation +from .const import DOMAIN + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return auth implementation.""" + return WithingsLocalOAuth2Implementation( + hass, + DOMAIN, + credential, + authorization_server=AuthorizationServer( + authorize_url=f"{WithingsAuth.URL}/oauth2_user/authorize2", + token_url=f"{AbstractWithingsApi.URL}/v2/oauth2", + ), + ) diff --git a/homeassistant/components/withings/common.py b/homeassistant/components/withings/common.py index 78ae375b4bc..ad40378eafb 100644 --- a/homeassistant/components/withings/common.py +++ b/homeassistant/components/withings/common.py @@ -28,6 +28,7 @@ from withings_api.common import ( ) from homeassistant.components import webhook +from homeassistant.components.application_credentials import AuthImplementation from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.http import HomeAssistantView from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN @@ -45,7 +46,6 @@ from homeassistant.helpers import config_entry_oauth2_flow, entity_registry as e from homeassistant.helpers.config_entry_oauth2_flow import ( AUTH_CALLBACK_PATH, AbstractOAuth2Implementation, - LocalOAuth2Implementation, OAuth2Session, ) from homeassistant.helpers.entity import Entity @@ -1106,7 +1106,7 @@ def get_platform_attributes(platform: str) -> tuple[WithingsAttribute, ...]: ) -class WithingsLocalOAuth2Implementation(LocalOAuth2Implementation): +class WithingsLocalOAuth2Implementation(AuthImplementation): """Oauth2 implementation that only uses the external url.""" @property diff --git a/homeassistant/components/withings/manifest.json b/homeassistant/components/withings/manifest.json index 2e089bdbcc9..1437b7f2198 100644 --- a/homeassistant/components/withings/manifest.json +++ b/homeassistant/components/withings/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/withings", "requirements": ["withings-api==2.4.0"], - "dependencies": ["auth", "http", "webhook"], + "dependencies": ["application_credentials", "http", "webhook"], "codeowners": ["@vangorra"], "iot_class": "cloud_polling", "loggers": ["withings_api"] diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 41541943086..d7b3259a158 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -12,6 +12,7 @@ APPLICATION_CREDENTIALS = [ "neato", "netatmo", "spotify", + "withings", "xbox", "yolink" ] diff --git a/tests/components/withings/test_init.py b/tests/components/withings/test_init.py index 465c26deb32..db26f7328ce 100644 --- a/tests/components/withings/test_init.py +++ b/tests/components/withings/test_init.py @@ -59,7 +59,6 @@ def test_config_schema_basic_config() -> None: def test_config_schema_client_id() -> None: """Test schema.""" - config_schema_assert_fail({CONF_CLIENT_SECRET: "my_client_secret"}) config_schema_assert_fail( {CONF_CLIENT_SECRET: "my_client_secret", CONF_CLIENT_ID: ""} ) @@ -70,7 +69,6 @@ def test_config_schema_client_id() -> None: def test_config_schema_client_secret() -> None: """Test schema.""" - config_schema_assert_fail({CONF_CLIENT_ID: "my_client_id"}) config_schema_assert_fail({CONF_CLIENT_ID: "my_client_id", CONF_CLIENT_SECRET: ""}) config_schema_validate( {CONF_CLIENT_ID: "my_client_id", CONF_CLIENT_SECRET: "my_client_secret"} From 7df7e33d17bdcaef419e49428f42a5de489502c5 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 23 May 2022 00:25:42 +0000 Subject: [PATCH 689/930] [ci skip] Translation update --- .../aladdin_connect/translations/nl.json | 2 +- .../translations/nl.json | 2 +- .../aussie_broadband/translations/nl.json | 2 +- .../components/auth/translations/id.json | 2 +- .../components/bosch_shc/translations/nl.json | 2 +- .../components/brunt/translations/nl.json | 2 +- .../components/climacell/translations/id.json | 2 +- .../components/coinbase/translations/id.json | 2 +- .../geocaching/translations/nl.json | 2 +- .../components/google/translations/id.json | 2 +- .../components/google/translations/it.json | 9 ++ .../components/google/translations/nl.json | 2 +- .../components/google/translations/pt-BR.json | 9 ++ .../google_travel_time/translations/id.json | 4 +- .../here_travel_time/translations/id.json | 82 +++++++++++++++++++ .../here_travel_time/translations/it.json | 82 +++++++++++++++++++ .../here_travel_time/translations/nl.json | 82 +++++++++++++++++++ .../here_travel_time/translations/pl.json | 19 +++++ .../translations/zh-Hant.json | 82 +++++++++++++++++++ .../components/icloud/translations/nl.json | 2 +- .../components/knx/translations/id.json | 4 +- .../components/konnected/translations/id.json | 4 +- .../components/konnected/translations/it.json | 14 ++-- .../components/konnected/translations/nl.json | 10 +-- .../components/laundrify/translations/ca.json | 25 ++++++ .../components/laundrify/translations/el.json | 25 ++++++ .../components/laundrify/translations/id.json | 25 ++++++ .../components/laundrify/translations/it.json | 25 ++++++ .../components/laundrify/translations/nl.json | 25 ++++++ .../components/laundrify/translations/pl.json | 22 +++++ .../laundrify/translations/zh-Hant.json | 25 ++++++ .../components/lyric/translations/nl.json | 2 +- .../components/nest/translations/nl.json | 2 +- .../components/netatmo/translations/nl.json | 2 +- .../components/notion/translations/nl.json | 2 +- .../components/nuki/translations/nl.json | 2 +- .../components/nws/translations/id.json | 2 +- .../components/plaato/translations/id.json | 2 +- .../components/plex/translations/nl.json | 2 +- .../components/renault/translations/nl.json | 2 +- .../components/ridwell/translations/nl.json | 2 +- .../components/samsungtv/translations/id.json | 2 +- .../components/sense/translations/nl.json | 2 +- .../simplisafe/translations/nl.json | 2 +- .../components/sleepiq/translations/nl.json | 2 +- .../components/smarttub/translations/nl.json | 2 +- .../components/sonarr/translations/nl.json | 2 +- .../components/spotify/translations/nl.json | 2 +- .../steam_online/translations/nl.json | 2 +- .../components/subaru/translations/id.json | 2 +- .../tomorrowio/translations/id.json | 2 +- .../totalconnect/translations/nl.json | 2 +- .../uptimerobot/translations/nl.json | 2 +- .../components/watttime/translations/nl.json | 2 +- .../components/withings/translations/nl.json | 2 +- .../xiaomi_miio/translations/nl.json | 2 +- .../components/yolink/translations/nl.json | 2 +- 57 files changed, 593 insertions(+), 56 deletions(-) create mode 100644 homeassistant/components/here_travel_time/translations/id.json create mode 100644 homeassistant/components/here_travel_time/translations/it.json create mode 100644 homeassistant/components/here_travel_time/translations/nl.json create mode 100644 homeassistant/components/here_travel_time/translations/pl.json create mode 100644 homeassistant/components/here_travel_time/translations/zh-Hant.json create mode 100644 homeassistant/components/laundrify/translations/ca.json create mode 100644 homeassistant/components/laundrify/translations/el.json create mode 100644 homeassistant/components/laundrify/translations/id.json create mode 100644 homeassistant/components/laundrify/translations/it.json create mode 100644 homeassistant/components/laundrify/translations/nl.json create mode 100644 homeassistant/components/laundrify/translations/pl.json create mode 100644 homeassistant/components/laundrify/translations/zh-Hant.json diff --git a/homeassistant/components/aladdin_connect/translations/nl.json b/homeassistant/components/aladdin_connect/translations/nl.json index 90d1fb67029..bd3384bd0f1 100644 --- a/homeassistant/components/aladdin_connect/translations/nl.json +++ b/homeassistant/components/aladdin_connect/translations/nl.json @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "De Aladdin Connect-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/application_credentials/translations/nl.json b/homeassistant/components/application_credentials/translations/nl.json index c186d95ce27..3638594ac2e 100644 --- a/homeassistant/components/application_credentials/translations/nl.json +++ b/homeassistant/components/application_credentials/translations/nl.json @@ -1,3 +1,3 @@ { - "title": "Applicatiegegevens" + "title": "Applicatie inloggegevens" } \ No newline at end of file diff --git a/homeassistant/components/aussie_broadband/translations/nl.json b/homeassistant/components/aussie_broadband/translations/nl.json index efb553cca25..77e22db0a48 100644 --- a/homeassistant/components/aussie_broadband/translations/nl.json +++ b/homeassistant/components/aussie_broadband/translations/nl.json @@ -16,7 +16,7 @@ "password": "Wachtwoord" }, "description": "Update wachtwoord voor {username}", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "service": { "data": { diff --git a/homeassistant/components/auth/translations/id.json b/homeassistant/components/auth/translations/id.json index ed7bede5fff..22b562e7c97 100644 --- a/homeassistant/components/auth/translations/id.json +++ b/homeassistant/components/auth/translations/id.json @@ -5,7 +5,7 @@ "no_available_service": "Tidak ada layanan notifikasi yang tersedia." }, "error": { - "invalid_code": "Kode tifak valid, coba lagi." + "invalid_code": "Kode tidak valid, coba lagi." }, "step": { "init": { diff --git a/homeassistant/components/bosch_shc/translations/nl.json b/homeassistant/components/bosch_shc/translations/nl.json index dd134e54c06..8e966ff9bbe 100644 --- a/homeassistant/components/bosch_shc/translations/nl.json +++ b/homeassistant/components/bosch_shc/translations/nl.json @@ -23,7 +23,7 @@ }, "reauth_confirm": { "description": "De bosch_shc integratie moet uw account herauthenticeren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/brunt/translations/nl.json b/homeassistant/components/brunt/translations/nl.json index 9ea207d1eae..a5aa018a1d0 100644 --- a/homeassistant/components/brunt/translations/nl.json +++ b/homeassistant/components/brunt/translations/nl.json @@ -15,7 +15,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord opnieuw in voor: {username}", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/climacell/translations/id.json b/homeassistant/components/climacell/translations/id.json index 57f88557a69..4d020351665 100644 --- a/homeassistant/components/climacell/translations/id.json +++ b/homeassistant/components/climacell/translations/id.json @@ -5,7 +5,7 @@ "data": { "timestep": "Jarak Interval Prakiraan NowCast dalam Menit" }, - "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan 'nowcast', Anda dapat mengonfigurasi jarak interval prakiraan dalam menit. Jumlah prakiraan yang diberikan tergantung pada nilai interval yang dipilih.", + "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan `nowcast`, Anda dapat mengonfigurasi jarak interval prakiraan dalam menit. Jumlah prakiraan yang diberikan tergantung pada nilai interval yang dipilih.", "title": "Perbarui Opsi ClimaCell" } } diff --git a/homeassistant/components/coinbase/translations/id.json b/homeassistant/components/coinbase/translations/id.json index ba2d2de0753..114c69acce2 100644 --- a/homeassistant/components/coinbase/translations/id.json +++ b/homeassistant/components/coinbase/translations/id.json @@ -16,7 +16,7 @@ "api_key": "Kunci API", "api_token": "Kode Rahasia API" }, - "description": "Silakan masukkan detail kunci API Anda sesuai yang disediakan oleh Coinbase.", + "description": "Masukkan detail kunci API Anda sesuai yang disediakan oleh Coinbase.", "title": "Detail Kunci API Coinbase" } } diff --git a/homeassistant/components/geocaching/translations/nl.json b/homeassistant/components/geocaching/translations/nl.json index e4cfc747c62..f221532a77e 100644 --- a/homeassistant/components/geocaching/translations/nl.json +++ b/homeassistant/components/geocaching/translations/nl.json @@ -18,7 +18,7 @@ }, "reauth_confirm": { "description": "De Geocaching integratie moet uw account herauthenticeren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } } diff --git a/homeassistant/components/google/translations/id.json b/homeassistant/components/google/translations/id.json index 371fa7551b5..fd1002d41be 100644 --- a/homeassistant/components/google/translations/id.json +++ b/homeassistant/components/google/translations/id.json @@ -3,7 +3,7 @@ "abort": { "already_configured": "Akun sudah dikonfigurasi", "already_in_progress": "Alur konfigurasi sedang berlangsung", - "code_expired": "Kode autentikasi kedaluwarsa atau penyiapan kredensial tidak valid, silakan coba lagi.", + "code_expired": "Kode autentikasi kedaluwarsa atau penyiapan kredensial tidak valid, coba lagi.", "invalid_access_token": "Token akses tidak valid", "missing_configuration": "Komponen tidak dikonfigurasi. Ikuti petunjuk dalam dokumentasi.", "oauth_error": "Menerima respons token yang tidak valid.", diff --git a/homeassistant/components/google/translations/it.json b/homeassistant/components/google/translations/it.json index 4530a690ed6..d57998894d9 100644 --- a/homeassistant/components/google/translations/it.json +++ b/homeassistant/components/google/translations/it.json @@ -27,5 +27,14 @@ "title": "Autentica nuovamente l'integrazione" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Accesso di Home Assistant a Google Calendar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json index dca192c59d4..baf0064ee63 100644 --- a/homeassistant/components/google/translations/nl.json +++ b/homeassistant/components/google/translations/nl.json @@ -24,7 +24,7 @@ }, "reauth_confirm": { "description": "De Google Agenda-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } } diff --git a/homeassistant/components/google/translations/pt-BR.json b/homeassistant/components/google/translations/pt-BR.json index 8ab124f1b5c..85c7254b9a7 100644 --- a/homeassistant/components/google/translations/pt-BR.json +++ b/homeassistant/components/google/translations/pt-BR.json @@ -27,5 +27,14 @@ "title": "Reautenticar Integra\u00e7\u00e3o" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Acesso do Home Assistant ao Google Calendar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/id.json b/homeassistant/components/google_travel_time/translations/id.json index 16b60148aa9..e960d03c341 100644 --- a/homeassistant/components/google_travel_time/translations/id.json +++ b/homeassistant/components/google_travel_time/translations/id.json @@ -14,7 +14,7 @@ "name": "Nama", "origin": "Asal" }, - "description": "Saat menentukan asal dan tujuan, Anda dapat menyediakan satu atau beberapa lokasi yang dipisahkan oleh karakter pipe, dalam bentuk alamat, koordinat lintang/bujur, atau ID tempat Google. Saat menentukan lokasi menggunakan ID tempat Google, ID harus diawali dengan \"place_id:'." + "description": "Saat menentukan asal dan tujuan, Anda dapat menyediakan satu atau beberapa lokasi yang dipisahkan oleh karakter pipe, dalam bentuk alamat, koordinat lintang/bujur, atau ID tempat Google. Saat menentukan lokasi menggunakan ID tempat Google, ID harus diawali dengan `place_id:`." } } }, @@ -31,7 +31,7 @@ "transit_routing_preference": "Preferensi Perutean Transit", "units": "Unit" }, - "description": "Anda dapat menentukan Waktu Keberangkatan atau Waktu Kedatangan secara opsional. Jika menentukan waktu keberangkatan, Anda dapat memasukkan 'sekarang', stempel waktu Unix, atau string waktu 24 jam seperti 08:00:00`. Jika menentukan waktu kedatangan, Anda dapat menggunakan stempel waktu Unix atau string waktu 24 jam seperti 08:00:00`" + "description": "Anda dapat menentukan Waktu Keberangkatan atau Waktu Kedatangan secara opsional. Jika menentukan waktu keberangkatan, Anda dapat memasukkan `now`, stempel waktu Unix, atau string waktu 24 jam seperti `08:00:00`. Jika menentukan waktu kedatangan, Anda dapat menggunakan stempel waktu Unix atau string waktu 24 jam seperti `08:00:00`." } } }, diff --git a/homeassistant/components/here_travel_time/translations/id.json b/homeassistant/components/here_travel_time/translations/id.json new file mode 100644 index 00000000000..f03910d9adf --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/id.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Perangkat sudah dikonfigurasi" + }, + "error": { + "invalid_auth": "Autentikasi tidak valid", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Tujuan dalam koordinat GPS" + }, + "title": "Pilih Tujuan" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Tujuan menggunakan entitas" + }, + "title": "Pilih Tujuan" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Menggunakan lokasi pada peta", + "destination_entity": "Menggunakan entitas" + }, + "title": "Pilih Tujuan" + }, + "origin_coordinates": { + "data": { + "origin": "Asal dalam koordinat GPS" + }, + "title": "Pilih Asal" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Asal menggunakan entitas" + }, + "title": "Pilih Asal" + }, + "user": { + "data": { + "api_key": "Kunci API", + "mode": "Mode Perjalanan", + "name": "Nama" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Waktu Kedatangan" + }, + "title": "Pilih Waktu Kedatangan" + }, + "departure_time": { + "data": { + "departure_time": "Waktu Keberangkatan" + }, + "title": "Pilih Waktu Keberangkatan" + }, + "init": { + "data": { + "route_mode": "Mode Rute", + "traffic_mode": "Modus Lalu Lintas", + "unit_system": "Sistem unit" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Konfigurasikan waktu kedatangan", + "departure_time": "Konfigurasikan waktu keberangkatan", + "no_time": "Jangan mengonfigurasi waktu" + }, + "title": "Pilih Jenis Waktu" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/it.json b/homeassistant/components/here_travel_time/translations/it.json new file mode 100644 index 00000000000..e9716318adb --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/it.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato" + }, + "error": { + "invalid_auth": "Autenticazione non valida", + "unknown": "Errore imprevisto" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destinazione come coordinate GPS" + }, + "title": "Scegli la destinazione" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destinazione utilizzando un'entit\u00e0" + }, + "title": "Scegli la destinazione" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Utilizzando una posizione sulla mappa", + "destination_entity": "Utilizzando un'entit\u00e0" + }, + "title": "Scegli la destinazione" + }, + "origin_coordinates": { + "data": { + "origin": "Partenza come coordinate GPS" + }, + "title": "Scegli la partenza" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Partenza utilizzando un'entit\u00e0" + }, + "title": "Scegli la partenza" + }, + "user": { + "data": { + "api_key": "Chiave API", + "mode": "Modalit\u00e0 di viaggio", + "name": "Nome" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Orario di arrivo" + }, + "title": "Scegli l'orario di arrivo" + }, + "departure_time": { + "data": { + "departure_time": "Orario di partenza" + }, + "title": "Scegli l'orario di partenza" + }, + "init": { + "data": { + "route_mode": "Modalit\u00e0 percorso", + "traffic_mode": "Modalit\u00e0 traffico", + "unit_system": "Sistema di unit\u00e0" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Configura un orario di arrivo", + "departure_time": "Configura un orario di partenza", + "no_time": "Non configurare un orario" + }, + "title": "Scegli il tipo di orario" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/nl.json b/homeassistant/components/here_travel_time/translations/nl.json new file mode 100644 index 00000000000..cbec21776e5 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/nl.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Apparaat is al geconfigureerd" + }, + "error": { + "invalid_auth": "Ongeldige authenticatie", + "unknown": "Onverwachte fout" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Bestemming als GPS-co\u00f6rdinaten" + }, + "title": "Bestemming kiezen" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Bestemming van een entiteit" + }, + "title": "Bestemming kiezen" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Een kaartlocatie gebruiken", + "destination_entity": "Een entiteit gebruiken" + }, + "title": "Bestemming kiezen" + }, + "origin_coordinates": { + "data": { + "origin": "Herkomst als GPS-co\u00f6rdinaten" + }, + "title": "Herkomst kiezen" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Herkomst van een entiteit" + }, + "title": "Herkomst kiezen" + }, + "user": { + "data": { + "api_key": "API-sleutel", + "mode": "Reismodus", + "name": "Naam" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Aankomsttijd" + }, + "title": "Aankomsttijd kiezen" + }, + "departure_time": { + "data": { + "departure_time": "Vertrektijd" + }, + "title": "Vertrektijd kiezen" + }, + "init": { + "data": { + "route_mode": "Routemodus", + "traffic_mode": "Verkeersmodus", + "unit_system": "Eenheidssysteem" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Een aankomsttijd configureren", + "departure_time": "Een vertrektijd configureren", + "no_time": "Geen tijd configureren" + }, + "title": "Kies tijdtype" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/pl.json b/homeassistant/components/here_travel_time/translations/pl.json new file mode 100644 index 00000000000..f04290714e4 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/pl.json @@ -0,0 +1,19 @@ +{ + "config": { + "abort": { + "already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane" + }, + "error": { + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "user": { + "data": { + "api_key": "Klucz API", + "name": "Nazwa" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/zh-Hant.json b/homeassistant/components/here_travel_time/translations/zh-Hant.json new file mode 100644 index 00000000000..53d5eae18fd --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/zh-Hant.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210" + }, + "error": { + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "\u76ee\u7684\u5730\u70ba GPS \u5ea7\u6a19" + }, + "title": "\u9078\u64c7\u76ee\u7684\u5730" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "\u4f7f\u7528\u76ee\u7684\u5730\u70ba\u5be6\u9ad4" + }, + "title": "\u9078\u64c7\u76ee\u7684\u5730" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "\u4f7f\u7528\u5730\u5716\u5ea7\u6a19", + "destination_entity": "\u4f7f\u7528\u70ba\u5be6\u9ad4" + }, + "title": "\u9078\u64c7\u76ee\u7684\u5730" + }, + "origin_coordinates": { + "data": { + "origin": "\u51fa\u767c\u5730\u70ba GPS \u5ea7\u6a19" + }, + "title": "\u9078\u64c7\u51fa\u767c\u5730" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "\u4f7f\u7528\u51fa\u767c\u5730\u70ba\u5be6\u9ad4" + }, + "title": "\u9078\u64c7\u51fa\u767c\u5730" + }, + "user": { + "data": { + "api_key": "API \u91d1\u9470", + "mode": "\u65c5\u884c\u6a21\u5f0f", + "name": "\u540d\u7a31" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u62b5\u9054\u6642\u9593" + }, + "title": "\u9078\u64c7\u62b5\u9054\u6642\u9593" + }, + "departure_time": { + "data": { + "departure_time": "\u51fa\u767c\u6642\u9593" + }, + "title": "\u9078\u64c7\u51fa\u767c\u6642\u9593" + }, + "init": { + "data": { + "route_mode": "\u8def\u5f91\u6a21\u5f0f", + "traffic_mode": "\u4ea4\u901a\u6a21\u5f0f", + "unit_system": "\u55ae\u4f4d\u7cfb\u7d71" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "\u8a2d\u5b9a\u62b5\u9054\u6642\u9593", + "departure_time": "\u8a2d\u5b9a\u51fa\u767c\u6642\u9593", + "no_time": "\u4e0d\u8981\u8a2d\u5b9a\u6642\u9593" + }, + "title": "\u9078\u64c7\u6642\u9593\u985e\u578b" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/icloud/translations/nl.json b/homeassistant/components/icloud/translations/nl.json index b93ebab4665..522df78a737 100644 --- a/homeassistant/components/icloud/translations/nl.json +++ b/homeassistant/components/icloud/translations/nl.json @@ -16,7 +16,7 @@ "password": "Wachtwoord" }, "description": "Uw eerder ingevoerde wachtwoord voor {username} werkt niet meer. Update uw wachtwoord om deze integratie te blijven gebruiken.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "trusted_device": { "data": { diff --git a/homeassistant/components/knx/translations/id.json b/homeassistant/components/knx/translations/id.json index 4c55a3ef392..bbf9a1b7862 100644 --- a/homeassistant/components/knx/translations/id.json +++ b/homeassistant/components/knx/translations/id.json @@ -41,7 +41,7 @@ }, "secure_knxkeys": { "data": { - "knxkeys_filename": "Nama file '.knxkeys' Anda (termasuk ekstensi)", + "knxkeys_filename": "Nama file `.knxkeys` Anda (termasuk ekstensi)", "knxkeys_password": "Kata sandi untuk mendekripsi file `.knxkeys`" }, "data_description": { @@ -102,7 +102,7 @@ "multicast_group": "Digunakan untuk perutean dan penemuan. Bawaan: `224.0.23.12`", "multicast_port": "Digunakan untuk perutean dan penemuan. Bawaan: `3671`", "rate_limit": "Telegram keluar maksimum per detik.\nDirekomendasikan: 20 hingga 40", - "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas 'sync_state'." + "state_updater": "Menyetel default untuk status pembacaan KNX Bus. Saat dinonaktifkan, Home Assistant tidak akan secara aktif mengambil status entitas dari KNX Bus. Hal ini bisa ditimpa dengan opsi entitas `sync_state`." } }, "tunnel": { diff --git a/homeassistant/components/konnected/translations/id.json b/homeassistant/components/konnected/translations/id.json index f2e2035ca06..38443f483ca 100644 --- a/homeassistant/components/konnected/translations/id.json +++ b/homeassistant/components/konnected/translations/id.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Balikkan status buka/tutup", - "name": "Nama (opsional)", + "name": "Nama", "type": "Jenis Sensor Biner" }, "description": "Opsi {zone}", @@ -47,7 +47,7 @@ }, "options_digital": { "data": { - "name": "Nama (opsional)", + "name": "Nama", "poll_interval": "Interval Polling (dalam menit) (opsional)", "type": "Jenis Sensor" }, diff --git a/homeassistant/components/konnected/translations/it.json b/homeassistant/components/konnected/translations/it.json index 682a27b50d0..4872dedcae4 100644 --- a/homeassistant/components/konnected/translations/it.json +++ b/homeassistant/components/konnected/translations/it.json @@ -41,7 +41,7 @@ "options_binary": { "data": { "inverse": "Invertire lo stato di apertura/chiusura", - "name": "Nome (opzionale)", + "name": "Nome", "type": "Tipo di sensore binario" }, "description": "Opzioni {zone}", @@ -49,8 +49,8 @@ }, "options_digital": { "data": { - "name": "Nome (opzionale)", - "poll_interval": "Intervallo di sondaggio (minuti) (opzionale)", + "name": "Nome", + "poll_interval": "Intervallo di sondaggio (minuti)", "type": "Tipo di sensore" }, "description": "Opzioni {zone}", @@ -97,11 +97,11 @@ "options_switch": { "data": { "activation": "Uscita quando acceso", - "momentary": "Durata impulso (ms) (opzionale)", + "momentary": "Durata impulso (ms)", "more_states": "Configura stati aggiuntivi per questa zona", - "name": "Nome (opzionale)", - "pause": "Pausa tra gli impulsi (ms) (opzionale)", - "repeat": "Numero di volte da ripetere (-1 = infinito) (opzionale)" + "name": "Nome", + "pause": "Pausa tra gli impulsi (ms)", + "repeat": "Numero di volte da ripetere (-1 = infinito)" }, "description": "Opzioni {zone}: stato {state}", "title": "Configurare l'uscita commutabile" diff --git a/homeassistant/components/konnected/translations/nl.json b/homeassistant/components/konnected/translations/nl.json index 6e46ef3e2bc..8f0548296b4 100644 --- a/homeassistant/components/konnected/translations/nl.json +++ b/homeassistant/components/konnected/translations/nl.json @@ -50,7 +50,7 @@ "options_digital": { "data": { "name": "Naam (optional)", - "poll_interval": "Poll interval (minuten) (optioneel)", + "poll_interval": "Poll interval (minuten)", "type": "Type sensor" }, "description": "{zone} opties", @@ -86,7 +86,7 @@ }, "options_misc": { "data": { - "api_host": "API host-URL overschrijven (optioneel)", + "api_host": "API host-URL overschrijven", "blink": "Led knipperen bij het verzenden van statuswijziging", "discovery": "Reageer op detectieverzoeken op uw netwerk", "override_api_host": "Overschrijf standaard Home Assistant API hostpaneel-URL" @@ -97,11 +97,11 @@ "options_switch": { "data": { "activation": "Uitvoer wanneer ingeschakeld", - "momentary": "Pulsduur (ms) (optioneel)", + "momentary": "Pulsduur (ms)", "more_states": "Aanvullende statussen voor deze zone configureren", "name": "Naam (optioneel)", - "pause": "Pauze tussen de pulsen (ms) (optioneel)", - "repeat": "Aantal herhalingen (-1=oneindig) (optioneel)" + "pause": "Pauze tussen de pulsen (ms)", + "repeat": "Aantal herhalingen (-1=oneindig)" }, "description": "{zone} opties: status {state}", "title": "Schakelbare uitgang configureren" diff --git a/homeassistant/components/laundrify/translations/ca.json b/homeassistant/components/laundrify/translations/ca.json new file mode 100644 index 00000000000..99b94ecfd77 --- /dev/null +++ b/homeassistant/components/laundrify/translations/ca.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ja configurat. Nom\u00e9s \u00e9s possible una sola configuraci\u00f3." + }, + "error": { + "cannot_connect": "Ha fallat la connexi\u00f3", + "invalid_auth": "Autenticaci\u00f3 inv\u00e0lida", + "invalid_format": "Format inv\u00e0lid. Escriu-lo com a xxx-xxx.", + "unknown": "Error inesperat" + }, + "step": { + "init": { + "data": { + "code": "Codi d'autenticaci\u00f3 (xxx-xxx)" + }, + "description": "Introdueix codi d'autenticaci\u00f3 personal que es mostra a l'aplicaci\u00f3 de Laundrify." + }, + "reauth_confirm": { + "description": "La integraci\u00f3 Laundrify ha de tornar a autenticar-se amb el teu compte.", + "title": "Reautenticaci\u00f3 de la integraci\u00f3" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/el.json b/homeassistant/components/laundrify/translations/el.json new file mode 100644 index 00000000000..fae492f678a --- /dev/null +++ b/homeassistant/components/laundrify/translations/el.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae.\u0388\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af. \u039c\u03cc\u03bd\u03bf \u03bc\u03af\u03b1 \u03b4\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae." + }, + "error": { + "cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2", + "invalid_auth": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2", + "invalid_format": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03bc\u03bf\u03c1\u03c6\u03ae. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03ba\u03b1\u03b8\u03bf\u03c1\u03af\u03c3\u03c4\u03b5 \u03c9\u03c2 xxx-xxx.", + "unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1" + }, + "step": { + "init": { + "data": { + "code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2 (xxx-xxx)" + }, + "description": "\u0395\u03b9\u03c3\u03ac\u03b3\u03b5\u03c4\u03b5 \u03c4\u03bf\u03bd \u03c0\u03c1\u03bf\u03c3\u03c9\u03c0\u03b9\u03ba\u03cc \u03c3\u03b1\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2 \u03c0\u03bf\u03c5 \u03b5\u03bc\u03c6\u03b1\u03bd\u03af\u03b6\u03b5\u03c4\u03b1\u03b9 \u03c3\u03c4\u03b7\u03bd \u03b5\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae laundrify-App." + }, + "reauth_confirm": { + "description": "\u0397 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 laundrify \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b3\u03af\u03bd\u03b5\u03b9 \u03b5\u03ba \u03bd\u03ad\u03bf\u03c5 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2.", + "title": "\u0395\u03c0\u03b1\u03bd\u03b1\u03bb\u03b7\u03c0\u03c4\u03b9\u03ba\u03cc\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7\u03c2" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/id.json b/homeassistant/components/laundrify/translations/id.json new file mode 100644 index 00000000000..ec80b01a84a --- /dev/null +++ b/homeassistant/components/laundrify/translations/id.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Sudah dikonfigurasi. Hanya satu konfigurasi yang diizinkan." + }, + "error": { + "cannot_connect": "Gagal terhubung", + "invalid_auth": "Autentikasi tidak valid", + "invalid_format": "Format yang tidak valid. Tentukan dengan format xxx-xxx.", + "unknown": "Kesalahan yang tidak diharapkan" + }, + "step": { + "init": { + "data": { + "code": "Kode Autentikasi (xxx-xxx)" + }, + "description": "Masukkan Kode Autentikasi pribadi Anda yang ditampilkan di aplikasi laundrify." + }, + "reauth_confirm": { + "description": "Integrasi laundrify perlu mengautentikasi ulang akun Anda", + "title": "Autentikasi Ulang Integrasi" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/it.json b/homeassistant/components/laundrify/translations/it.json new file mode 100644 index 00000000000..08fd2dcb87a --- /dev/null +++ b/homeassistant/components/laundrify/translations/it.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Gi\u00e0 configurato. \u00c8 possibile una sola configurazione." + }, + "error": { + "cannot_connect": "Impossibile connettersi", + "invalid_auth": "Autenticazione non valida", + "invalid_format": "Formato non valido. Specificare come xxx-xxx.", + "unknown": "Errore imprevisto" + }, + "step": { + "init": { + "data": { + "code": "Codice di autorizzazione (xxx-xxx)" + }, + "description": "Inserisci il tuo codice di autorizzazione personale che viene mostrato nell'app di laundrify." + }, + "reauth_confirm": { + "description": "L'integrazione laundrify deve eseguire nuovamente l'autenticazione.", + "title": "Autentica nuovamente l'integrazione" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/nl.json b/homeassistant/components/laundrify/translations/nl.json new file mode 100644 index 00000000000..1fdb9f35870 --- /dev/null +++ b/homeassistant/components/laundrify/translations/nl.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Al geconfigureerd. Slechts \u00e9\u00e9n configuratie mogelijk." + }, + "error": { + "cannot_connect": "Kan geen verbinding maken", + "invalid_auth": "Ongeldige authenticatie", + "invalid_format": "Ongeldig formaat. Specificeer als xxx-xxx.", + "unknown": "Onverwachte fout" + }, + "step": { + "init": { + "data": { + "code": "Verificatiecode (xxx-xxx)" + }, + "description": "Voer de verificatiecode in die wordt weergegeven in de laundrify-app." + }, + "reauth_confirm": { + "description": "De laundrify-integratie moet opnieuw worden geauthenticeerd.", + "title": "Integratie herauthenticeren" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/pl.json b/homeassistant/components/laundrify/translations/pl.json new file mode 100644 index 00000000000..28145b20fce --- /dev/null +++ b/homeassistant/components/laundrify/translations/pl.json @@ -0,0 +1,22 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja." + }, + "error": { + "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", + "invalid_auth": "Niepoprawne uwierzytelnienie", + "unknown": "Nieoczekiwany b\u0142\u0105d" + }, + "step": { + "init": { + "data": { + "code": "Kod autoryzacyjny (xxx-xxx)" + } + }, + "reauth_confirm": { + "title": "Ponownie uwierzytelnij integracj\u0119" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/zh-Hant.json b/homeassistant/components/laundrify/translations/zh-Hant.json new file mode 100644 index 00000000000..834ebb9125b --- /dev/null +++ b/homeassistant/components/laundrify/translations/zh-Hant.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210\u3001\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44\u88dd\u7f6e\u3002" + }, + "error": { + "cannot_connect": "\u9023\u7dda\u5931\u6557", + "invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548", + "invalid_format": "\u683c\u5f0f\u932f\u8aa4\u3001\u8acb\u4f7f\u7528 xxx-xxx\u3002", + "unknown": "\u672a\u9810\u671f\u932f\u8aa4" + }, + "step": { + "init": { + "data": { + "code": "\u8a8d\u8b49\u78bc\uff08xxx-xxx\uff09" + }, + "description": "\u8acb\u8f38\u5165\u65bc laundrify App \u4e0a\u6240\u986f\u793a\u4e4b\u8a8d\u8b49\u78bc\u3002" + }, + "reauth_confirm": { + "description": "Laundrify \u6574\u5408\u9700\u8981\u91cd\u65b0\u8a8d\u8b49\u60a8\u7684\u5e33\u865f", + "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/lyric/translations/nl.json b/homeassistant/components/lyric/translations/nl.json index a2569f18dc2..e820174aa5a 100644 --- a/homeassistant/components/lyric/translations/nl.json +++ b/homeassistant/components/lyric/translations/nl.json @@ -14,7 +14,7 @@ }, "reauth_confirm": { "description": "De Lyric-integratie moet uw account opnieuw verifi\u00ebren.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } } diff --git a/homeassistant/components/nest/translations/nl.json b/homeassistant/components/nest/translations/nl.json index 2b486ce79e2..a2f8ba77d78 100644 --- a/homeassistant/components/nest/translations/nl.json +++ b/homeassistant/components/nest/translations/nl.json @@ -55,7 +55,7 @@ }, "reauth_confirm": { "description": "De Nest-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } }, diff --git a/homeassistant/components/netatmo/translations/nl.json b/homeassistant/components/netatmo/translations/nl.json index e0b8998d652..80e3379b321 100644 --- a/homeassistant/components/netatmo/translations/nl.json +++ b/homeassistant/components/netatmo/translations/nl.json @@ -16,7 +16,7 @@ }, "reauth_confirm": { "description": "De Netatmo-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } }, diff --git a/homeassistant/components/notion/translations/nl.json b/homeassistant/components/notion/translations/nl.json index 130d45635b8..fbc4b6ac7be 100644 --- a/homeassistant/components/notion/translations/nl.json +++ b/homeassistant/components/notion/translations/nl.json @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/nuki/translations/nl.json b/homeassistant/components/nuki/translations/nl.json index b5c41a05421..3577fad5a01 100644 --- a/homeassistant/components/nuki/translations/nl.json +++ b/homeassistant/components/nuki/translations/nl.json @@ -14,7 +14,7 @@ "token": "Toegangstoken" }, "description": "De Nuki integratie moet opnieuw authenticeren met uw bridge.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/nws/translations/id.json b/homeassistant/components/nws/translations/id.json index 3c92ebaed64..e4e73efe685 100644 --- a/homeassistant/components/nws/translations/id.json +++ b/homeassistant/components/nws/translations/id.json @@ -15,7 +15,7 @@ "longitude": "Bujur", "station": "Kode stasiun METAR" }, - "description": "Jika kode stasiun METAR tidak ditentukan, informasi lintang dan bujur akan digunakan untuk menemukan stasiun terdekat. Untuk saat ini, Kunci API bisa berupa nilai sebarang. Disarankan untuk menggunakan alamat email yang valid.", + "description": "Jika kode stasiun METAR tidak ditentukan, informasi lintang dan bujur akan digunakan untuk menemukan stasiun terdekat. Untuk saat ini, Kunci API bisa berupa nilai sebarang. Disarankan untuk menggunakan alamat email yang valid.fsilak", "title": "Hubungkan ke National Weather Service" } } diff --git a/homeassistant/components/plaato/translations/id.json b/homeassistant/components/plaato/translations/id.json index 99783fd4a63..b159dec5a13 100644 --- a/homeassistant/components/plaato/translations/id.json +++ b/homeassistant/components/plaato/translations/id.json @@ -20,7 +20,7 @@ "token": "Tempel Token Auth di sini", "use_webhook": "Gunakan webhook" }, - "description": "Untuk dapat melakukan kueri API diperlukan 'auth_token'. Nilai token dapat diperoleh dengan mengikuti [petunjuk ini](https://plaato.zendesk.com/hc/en-us/articles/360003234717-Auth-token)\n\nPerangkat yang dipilih: **{device_type}** \n\nJika Anda lebih memilih untuk menggunakan metode webhook bawaan (hanya Airlock), centang centang kotak di bawah ini dan kosongkan nilai Auth Token'", + "description": "Untuk dapat melakukan kueri API diperlukan `auth_token`. Nilai token dapat diperoleh dengan mengikuti [petunjuk ini](https://plaato.zendesk.com/hc/en-us/articles/360003234717-Auth-token)\n\nPerangkat yang dipilih: **{device_type}** \n\nJika Anda lebih memilih untuk menggunakan metode webhook bawaan (hanya Airlock), centang centang kotak di bawah ini dan kosongkan nilai Auth Token'", "title": "Pilih metode API" }, "user": { diff --git a/homeassistant/components/plex/translations/nl.json b/homeassistant/components/plex/translations/nl.json index 4d9f30056bd..4f299e8b5c1 100644 --- a/homeassistant/components/plex/translations/nl.json +++ b/homeassistant/components/plex/translations/nl.json @@ -50,7 +50,7 @@ "data": { "ignore_new_shared_users": "Negeer nieuwe beheerde/gedeelde gebruikers", "ignore_plex_web_clients": "Negeer Plex-webclients", - "monitored_users": "Gecontroleerde gebruikers", + "monitored_users": "Gemonitorde gebruikers", "use_episode_art": "Gebruik aflevering kunst" }, "description": "Opties voor Plex-mediaspelers" diff --git a/homeassistant/components/renault/translations/nl.json b/homeassistant/components/renault/translations/nl.json index 212dd860874..1d2066dbdfc 100644 --- a/homeassistant/components/renault/translations/nl.json +++ b/homeassistant/components/renault/translations/nl.json @@ -20,7 +20,7 @@ "password": "Wachtwoord" }, "description": "Werk uw wachtwoord voor {gebruikersnaam} bij", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/ridwell/translations/nl.json b/homeassistant/components/ridwell/translations/nl.json index e230ffec6f8..0bd599ccf19 100644 --- a/homeassistant/components/ridwell/translations/nl.json +++ b/homeassistant/components/ridwell/translations/nl.json @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in:", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/samsungtv/translations/id.json b/homeassistant/components/samsungtv/translations/id.json index 2dd96798748..9ed190df2b7 100644 --- a/homeassistant/components/samsungtv/translations/id.json +++ b/homeassistant/components/samsungtv/translations/id.json @@ -12,7 +12,7 @@ }, "error": { "auth_missing": "Home Assistant tidak diizinkan untuk tersambung ke TV Samsung ini. Periksa Manajer Perangkat Eksternal TV Anda untuk mengotorisasi Home Assistant.", - "invalid_pin": "PIN tidak valid, silakan coba lagi." + "invalid_pin": "PIN tidak valid, coba lagi." }, "flow_title": "{device}", "step": { diff --git a/homeassistant/components/sense/translations/nl.json b/homeassistant/components/sense/translations/nl.json index 1f2f9e3d26b..3558f4a0c07 100644 --- a/homeassistant/components/sense/translations/nl.json +++ b/homeassistant/components/sense/translations/nl.json @@ -15,7 +15,7 @@ "password": "Wachtwoord" }, "description": "De Sense-integratie moet uw account {email} opnieuw verifi\u00ebren.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/simplisafe/translations/nl.json b/homeassistant/components/simplisafe/translations/nl.json index 34db358c02b..8c356ed9e12 100644 --- a/homeassistant/components/simplisafe/translations/nl.json +++ b/homeassistant/components/simplisafe/translations/nl.json @@ -18,7 +18,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "sms_2fa": { "data": { diff --git a/homeassistant/components/sleepiq/translations/nl.json b/homeassistant/components/sleepiq/translations/nl.json index 9b829caa025..4b32ac233b0 100644 --- a/homeassistant/components/sleepiq/translations/nl.json +++ b/homeassistant/components/sleepiq/translations/nl.json @@ -14,7 +14,7 @@ "password": "Wachtwoord" }, "description": "De SleepIQ-integratie moet uw account {username} opnieuw verifi\u00ebren.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/smarttub/translations/nl.json b/homeassistant/components/smarttub/translations/nl.json index cfe5b601491..ad351ba42e3 100644 --- a/homeassistant/components/smarttub/translations/nl.json +++ b/homeassistant/components/smarttub/translations/nl.json @@ -10,7 +10,7 @@ "step": { "reauth_confirm": { "description": "De SmartTub-integratie moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/sonarr/translations/nl.json b/homeassistant/components/sonarr/translations/nl.json index 0ef71c521b0..bb310896bed 100644 --- a/homeassistant/components/sonarr/translations/nl.json +++ b/homeassistant/components/sonarr/translations/nl.json @@ -13,7 +13,7 @@ "step": { "reauth_confirm": { "description": "De Sonarr-integratie moet handmatig opnieuw worden geverifieerd met de Sonarr-API die wordt gehost op: {url}", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/spotify/translations/nl.json b/homeassistant/components/spotify/translations/nl.json index 577e70bb0d1..2e478de73ab 100644 --- a/homeassistant/components/spotify/translations/nl.json +++ b/homeassistant/components/spotify/translations/nl.json @@ -15,7 +15,7 @@ }, "reauth_confirm": { "description": "De Spotify integratie moet opnieuw worden geverifieerd met Spotify voor account: {account}", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } }, diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json index f94d083b76a..7eafa00e53c 100644 --- a/homeassistant/components/steam_online/translations/nl.json +++ b/homeassistant/components/steam_online/translations/nl.json @@ -13,7 +13,7 @@ "step": { "reauth_confirm": { "description": "De Steam integratie moet handmatig opnieuw geauthenticeerd worden\n\nU kunt uw sleutel hier vinden: {api_key_url}", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/subaru/translations/id.json b/homeassistant/components/subaru/translations/id.json index cf0d37a0c30..35395be56b5 100644 --- a/homeassistant/components/subaru/translations/id.json +++ b/homeassistant/components/subaru/translations/id.json @@ -11,7 +11,7 @@ "incorrect_pin": "PIN salah", "incorrect_validation_code": "Kode validasi salah", "invalid_auth": "Autentikasi tidak valid", - "two_factor_request_failed": "Permintaan kode 2FA gagal, silakan coba lagi" + "two_factor_request_failed": "Permintaan kode 2FA gagal, coba lagi" }, "step": { "pin": { diff --git a/homeassistant/components/tomorrowio/translations/id.json b/homeassistant/components/tomorrowio/translations/id.json index a1a6f41888a..364e697783f 100644 --- a/homeassistant/components/tomorrowio/translations/id.json +++ b/homeassistant/components/tomorrowio/translations/id.json @@ -23,7 +23,7 @@ "data": { "timestep": "Jarak Interval Prakiraan NowCast dalam Menit" }, - "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan 'nowcast', Anda dapat mengonfigurasi jumlah menit antar-prakiraan. Jumlah prakiraan yang diberikan tergantung pada jumlah menit yang dipilih antar-prakiraan.", + "description": "Jika Anda memilih untuk mengaktifkan entitas prakiraan `nowcast`, Anda dapat mengonfigurasi jumlah menit antar-prakiraan. Jumlah prakiraan yang diberikan tergantung pada jumlah menit yang dipilih antar-prakiraan.", "title": "Perbarui Opsi Tomorrow.io" } } diff --git a/homeassistant/components/totalconnect/translations/nl.json b/homeassistant/components/totalconnect/translations/nl.json index e80030ec06b..cf9a6bb10a1 100644 --- a/homeassistant/components/totalconnect/translations/nl.json +++ b/homeassistant/components/totalconnect/translations/nl.json @@ -19,7 +19,7 @@ }, "reauth_confirm": { "description": "Total Connect moet uw account opnieuw verifi\u00ebren", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/uptimerobot/translations/nl.json b/homeassistant/components/uptimerobot/translations/nl.json index 61a2e8137e5..96db2ba1f23 100644 --- a/homeassistant/components/uptimerobot/translations/nl.json +++ b/homeassistant/components/uptimerobot/translations/nl.json @@ -19,7 +19,7 @@ "api_key": "API-sleutel" }, "description": "U moet een nieuwe 'hoofd'-API-sleutel van UptimeRobot opgeven", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/watttime/translations/nl.json b/homeassistant/components/watttime/translations/nl.json index 47e2fb9c600..09fbfe6307a 100644 --- a/homeassistant/components/watttime/translations/nl.json +++ b/homeassistant/components/watttime/translations/nl.json @@ -28,7 +28,7 @@ "password": "Wachtwoord" }, "description": "Voer het wachtwoord voor {username} opnieuw in:", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "user": { "data": { diff --git a/homeassistant/components/withings/translations/nl.json b/homeassistant/components/withings/translations/nl.json index 04da8b7eb0b..cca755effbb 100644 --- a/homeassistant/components/withings/translations/nl.json +++ b/homeassistant/components/withings/translations/nl.json @@ -26,7 +26,7 @@ }, "reauth": { "description": "Het {profile} \" moet opnieuw worden geverifieerd om Withings-gegevens te blijven ontvangen.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } } diff --git a/homeassistant/components/xiaomi_miio/translations/nl.json b/homeassistant/components/xiaomi_miio/translations/nl.json index abb63ec15c5..cc077839808 100644 --- a/homeassistant/components/xiaomi_miio/translations/nl.json +++ b/homeassistant/components/xiaomi_miio/translations/nl.json @@ -40,7 +40,7 @@ }, "reauth_confirm": { "description": "De Xiaomi Miio-integratie moet uw account opnieuw verifi\u00ebren om de tokens bij te werken of ontbrekende cloudreferenties toe te voegen.", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" }, "select": { "data": { diff --git a/homeassistant/components/yolink/translations/nl.json b/homeassistant/components/yolink/translations/nl.json index 401738dc381..55e8a016ff5 100644 --- a/homeassistant/components/yolink/translations/nl.json +++ b/homeassistant/components/yolink/translations/nl.json @@ -18,7 +18,7 @@ }, "reauth_confirm": { "description": "De yolink-integratie moet opnieuw inloggen bij uw account", - "title": "Integratie herauthentiseren" + "title": "Integratie herauthenticeren" } } } From 7649adde5f28bbc2d05d57a6f4cdd80d88cd6b76 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Mon, 23 May 2022 03:41:54 +0200 Subject: [PATCH 690/930] Fix here_travel_time config_flow import (#72313) * Handle import of entity_namespace * Update homeassistant/components/here_travel_time/config_flow.py Co-authored-by: Allen Porter * Fix mypy Co-authored-by: Allen Porter --- .../here_travel_time/config_flow.py | 60 +++++++++++-------- .../here_travel_time/test_config_flow.py | 8 ++- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/here_travel_time/config_flow.py b/homeassistant/components/here_travel_time/config_flow.py index bc6d57aa892..502eee68546 100644 --- a/homeassistant/components/here_travel_time/config_flow.py +++ b/homeassistant/components/here_travel_time/config_flow.py @@ -8,7 +8,13 @@ from herepy import HEREError, InvalidCredentialsError, RouteMode, RoutingApi import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME, CONF_UNIT_SYSTEM +from homeassistant.const import ( + CONF_API_KEY, + CONF_ENTITY_NAMESPACE, + CONF_MODE, + CONF_NAME, + CONF_UNIT_SYSTEM, +) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult import homeassistant.helpers.config_validation as cv @@ -231,9 +237,7 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) return self.async_show_form(step_id="destination_entity", data_schema=schema) - async def async_step_import( - self, user_input: dict[str, Any] | None = None - ) -> FlowResult: + async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult: """Import from configuration.yaml.""" options: dict[str, Any] = {} user_input, options = self._transform_import_input(user_input) @@ -249,39 +253,47 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) def _transform_import_input( - self, user_input + self, import_input: dict[str, Any] ) -> tuple[dict[str, Any], dict[str, Any]]: """Transform platform schema input to new model.""" options: dict[str, Any] = {} - if user_input.get(CONF_ORIGIN_LATITUDE) is not None: - user_input[CONF_ORIGIN_LATITUDE] = user_input.pop(CONF_ORIGIN_LATITUDE) - user_input[CONF_ORIGIN_LONGITUDE] = user_input.pop(CONF_ORIGIN_LONGITUDE) - else: - user_input[CONF_ORIGIN_ENTITY_ID] = user_input.pop(CONF_ORIGIN_ENTITY_ID) + user_input: dict[str, Any] = {} - if user_input.get(CONF_DESTINATION_LATITUDE) is not None: - user_input[CONF_DESTINATION_LATITUDE] = user_input.pop( - CONF_DESTINATION_LATITUDE - ) - user_input[CONF_DESTINATION_LONGITUDE] = user_input.pop( - CONF_DESTINATION_LONGITUDE - ) + if import_input.get(CONF_ORIGIN_LATITUDE) is not None: + user_input[CONF_ORIGIN_LATITUDE] = import_input[CONF_ORIGIN_LATITUDE] + user_input[CONF_ORIGIN_LONGITUDE] = import_input[CONF_ORIGIN_LONGITUDE] else: - user_input[CONF_DESTINATION_ENTITY_ID] = user_input.pop( + user_input[CONF_ORIGIN_ENTITY_ID] = import_input[CONF_ORIGIN_ENTITY_ID] + + if import_input.get(CONF_DESTINATION_LATITUDE) is not None: + user_input[CONF_DESTINATION_LATITUDE] = import_input[ + CONF_DESTINATION_LATITUDE + ] + user_input[CONF_DESTINATION_LONGITUDE] = import_input[ + CONF_DESTINATION_LONGITUDE + ] + else: + user_input[CONF_DESTINATION_ENTITY_ID] = import_input[ CONF_DESTINATION_ENTITY_ID - ) + ] + + user_input[CONF_API_KEY] = import_input[CONF_API_KEY] + user_input[CONF_MODE] = import_input[CONF_MODE] + user_input[CONF_NAME] = import_input[CONF_NAME] + if (namespace := import_input.get(CONF_ENTITY_NAMESPACE)) is not None: + user_input[CONF_NAME] = f"{namespace} {user_input[CONF_NAME]}" options[CONF_TRAFFIC_MODE] = ( TRAFFIC_MODE_ENABLED - if user_input.pop(CONF_TRAFFIC_MODE, False) + if import_input.get(CONF_TRAFFIC_MODE, False) else TRAFFIC_MODE_DISABLED ) - options[CONF_ROUTE_MODE] = user_input.pop(CONF_ROUTE_MODE) - options[CONF_UNIT_SYSTEM] = user_input.pop( + options[CONF_ROUTE_MODE] = import_input.get(CONF_ROUTE_MODE) + options[CONF_UNIT_SYSTEM] = import_input.get( CONF_UNIT_SYSTEM, self.hass.config.units.name ) - options[CONF_ARRIVAL_TIME] = user_input.pop(CONF_ARRIVAL, None) - options[CONF_DEPARTURE_TIME] = user_input.pop(CONF_DEPARTURE, None) + options[CONF_ARRIVAL_TIME] = import_input.get(CONF_ARRIVAL, None) + options[CONF_DEPARTURE_TIME] = import_input.get(CONF_DEPARTURE, None) return user_input, options diff --git a/tests/components/here_travel_time/test_config_flow.py b/tests/components/here_travel_time/test_config_flow.py index 105128c7cfb..350e1ae36f6 100644 --- a/tests/components/here_travel_time/test_config_flow.py +++ b/tests/components/here_travel_time/test_config_flow.py @@ -29,8 +29,10 @@ from homeassistant.components.here_travel_time.sensor import ( ) from homeassistant.const import ( CONF_API_KEY, + CONF_ENTITY_NAMESPACE, CONF_MODE, CONF_NAME, + CONF_SCAN_INTERVAL, CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC, @@ -416,16 +418,18 @@ async def test_import_flow_entity_id(hass: HomeAssistant) -> None: CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ENTITY_NAMESPACE: "namespace", + CONF_SCAN_INTERVAL: 2678400, }, ) await hass.async_block_till_done() assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == "test_name" + assert result["title"] == "namespace test_name" entry = hass.config_entries.async_entries(DOMAIN)[0] assert entry.data == { - CONF_NAME: "test_name", + CONF_NAME: "namespace test_name", CONF_API_KEY: CONF_API_KEY, CONF_ORIGIN_ENTITY_ID: "sensor.origin", CONF_DESTINATION_ENTITY_ID: "sensor.destination", From 3ee5445b26a8c48b869a36c5a465734f419af56f Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Mon, 23 May 2022 04:43:21 +0200 Subject: [PATCH 691/930] Fix camera entity update coordinator subscription in Synology DSM (#72248) fix camera entity subscription to coordinator --- homeassistant/components/synology_dsm/camera.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/synology_dsm/camera.py b/homeassistant/components/synology_dsm/camera.py index cab2536187c..0a6934b45a7 100644 --- a/homeassistant/components/synology_dsm/camera.py +++ b/homeassistant/components/synology_dsm/camera.py @@ -154,6 +154,7 @@ class SynoDSMCamera(SynologyDSMBaseEntity, Camera): async def async_added_to_hass(self) -> None: """Subscribe to signal.""" self._listen_source_updates() + await super().async_added_to_hass() def camera_image( self, width: int | None = None, height: int | None = None From 4e4c745c4f0ccb130a23f8122eaa68a7a1c95b9e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 04:44:11 +0200 Subject: [PATCH 692/930] Adjust setup type hints in honeywell (#72226) --- homeassistant/components/honeywell/climate.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 5c238d73e0b..11b899dc0f5 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -21,7 +21,10 @@ from homeassistant.components.climate.const import ( HVACAction, HVACMode, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( _LOGGER, @@ -70,12 +73,14 @@ HW_FAN_MODE_TO_HA = { PARALLEL_UPDATES = 1 -async def async_setup_entry(hass, config, async_add_entities, discovery_info=None): +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: """Set up the Honeywell thermostat.""" - cool_away_temp = config.options.get(CONF_COOL_AWAY_TEMPERATURE) - heat_away_temp = config.options.get(CONF_HEAT_AWAY_TEMPERATURE) + cool_away_temp = entry.options.get(CONF_COOL_AWAY_TEMPERATURE) + heat_away_temp = entry.options.get(CONF_HEAT_AWAY_TEMPERATURE) - data = hass.data[DOMAIN][config.entry_id] + data = hass.data[DOMAIN][entry.entry_id] async_add_entities( [ From e9ffcad4d1c6fd30babad6d3b2554f4fe33b93ca Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 04:46:46 +0200 Subject: [PATCH 693/930] Adjust setup type hints in opnsense (#72225) --- homeassistant/components/opnsense/device_tracker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/opnsense/device_tracker.py b/homeassistant/components/opnsense/device_tracker.py index 9a024b2b260..e726a0484f9 100644 --- a/homeassistant/components/opnsense/device_tracker.py +++ b/homeassistant/components/opnsense/device_tracker.py @@ -1,10 +1,12 @@ """Device tracker support for OPNSense routers.""" from homeassistant.components.device_tracker import DeviceScanner +from homeassistant.core import HomeAssistant +from homeassistant.helpers.typing import ConfigType from . import CONF_TRACKER_INTERFACE, OPNSENSE_DATA -async def async_get_scanner(hass, config, discovery_info=None): +async def async_get_scanner(hass: HomeAssistant, config: ConfigType) -> DeviceScanner: """Configure the OPNSense device_tracker.""" interface_client = hass.data[OPNSENSE_DATA]["interfaces"] scanner = OPNSenseDeviceScanner( From 4dbc1ed7a79cf43f08ea948b03fa9c29cbf94d67 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 May 2022 23:29:30 -0500 Subject: [PATCH 694/930] Add dialect (database engine) and version to recorder system health data (#72339) --- homeassistant/components/recorder/core.py | 7 ++++-- .../components/recorder/strings.json | 4 +++- .../recorder/system_health/__init__.py | 23 +++++++++++++++---- .../components/recorder/translations/en.json | 2 ++ homeassistant/components/recorder/util.py | 5 +++- .../components/recorder/test_system_health.py | 6 +++++ 6 files changed, 39 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 716e3a19d09..2ba07e42f49 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -12,6 +12,7 @@ import threading import time from typing import Any, TypeVar, cast +from awesomeversion import AwesomeVersion from lru import LRU # pylint: disable=no-name-in-module from sqlalchemy import create_engine, event as sqlalchemy_event, exc, func, select from sqlalchemy.engine import Engine @@ -159,6 +160,7 @@ class Recorder(threading.Thread): self.db_url = uri self.db_max_retries = db_max_retries self.db_retry_wait = db_retry_wait + self.engine_version: AwesomeVersion | None = None self.async_db_ready: asyncio.Future[bool] = asyncio.Future() self.async_recorder_ready = asyncio.Event() self._queue_watch = threading.Event() @@ -1009,12 +1011,13 @@ class Recorder(threading.Thread): ) -> None: """Dbapi specific connection settings.""" assert self.engine is not None - setup_connection_for_dialect( + if version := setup_connection_for_dialect( self, self.engine.dialect.name, dbapi_connection, not self._completed_first_database_setup, - ) + ): + self.engine_version = version self._completed_first_database_setup = True if self.db_url == SQLITE_URL_PREFIX or ":memory:" in self.db_url: diff --git a/homeassistant/components/recorder/strings.json b/homeassistant/components/recorder/strings.json index b475b29a16a..9b616372adf 100644 --- a/homeassistant/components/recorder/strings.json +++ b/homeassistant/components/recorder/strings.json @@ -3,7 +3,9 @@ "info": { "oldest_recorder_run": "Oldest Run Start Time", "current_recorder_run": "Current Run Start Time", - "estimated_db_size": "Estimated Database Size (MiB)" + "estimated_db_size": "Estimated Database Size (MiB)", + "database_engine": "Database Engine", + "database_version": "Database Version" } } } diff --git a/homeassistant/components/recorder/system_health/__init__.py b/homeassistant/components/recorder/system_health/__init__.py index 3250aecf356..8ba68a1649b 100644 --- a/homeassistant/components/recorder/system_health/__init__.py +++ b/homeassistant/components/recorder/system_health/__init__.py @@ -44,17 +44,32 @@ def _get_db_stats(instance: Recorder, database_name: str) -> dict[str, Any]: return db_stats +@callback +def _async_get_db_engine_info(instance: Recorder) -> dict[str, Any]: + """Get database engine info.""" + db_engine_info: dict[str, Any] = {} + if dialect_name := instance.dialect_name: + db_engine_info["database_engine"] = dialect_name.value + if engine_version := instance.engine_version: + db_engine_info["database_version"] = str(engine_version) + return db_engine_info + + async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: """Get info for the info page.""" instance = get_instance(hass) + run_history = instance.run_history database_name = URL(instance.db_url).path.lstrip("/") + db_engine_info = _async_get_db_engine_info(instance) db_stats: dict[str, Any] = {} + if instance.async_db_ready.done(): db_stats = await instance.async_add_executor_job( _get_db_stats, instance, database_name ) - return { - "oldest_recorder_run": run_history.first.start, - "current_recorder_run": run_history.current.start, - } | db_stats + db_runs = { + "oldest_recorder_run": run_history.first.start, + "current_recorder_run": run_history.current.start, + } + return db_runs | db_stats | db_engine_info diff --git a/homeassistant/components/recorder/translations/en.json b/homeassistant/components/recorder/translations/en.json index fb802ff7ff9..c9ceffc7397 100644 --- a/homeassistant/components/recorder/translations/en.json +++ b/homeassistant/components/recorder/translations/en.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Current Run Start Time", + "database_engine": "Database Engine", + "database_version": "Database Version", "estimated_db_size": "Estimated Database Size (MiB)", "oldest_recorder_run": "Oldest Run Start Time" } diff --git a/homeassistant/components/recorder/util.py b/homeassistant/components/recorder/util.py index b010ddfa853..843f0e4b185 100644 --- a/homeassistant/components/recorder/util.py +++ b/homeassistant/components/recorder/util.py @@ -396,8 +396,9 @@ def setup_connection_for_dialect( dialect_name: str, dbapi_connection: Any, first_connection: bool, -) -> None: +) -> AwesomeVersion | None: """Execute statements needed for dialect connection.""" + version: AwesomeVersion | None = None if dialect_name == SupportedDialect.SQLITE: if first_connection: old_isolation = dbapi_connection.isolation_level @@ -465,6 +466,8 @@ def setup_connection_for_dialect( else: _fail_unsupported_dialect(dialect_name) + return version + def end_incomplete_runs(session: Session, start_time: datetime) -> None: """End any incomplete recorder runs.""" diff --git a/tests/components/recorder/test_system_health.py b/tests/components/recorder/test_system_health.py index 8a8d9ea4e80..80997b9df36 100644 --- a/tests/components/recorder/test_system_health.py +++ b/tests/components/recorder/test_system_health.py @@ -24,6 +24,8 @@ async def test_recorder_system_health(hass, recorder_mock): "current_recorder_run": instance.run_history.current.start, "oldest_recorder_run": instance.run_history.first.start, "estimated_db_size": ANY, + "database_engine": SupportedDialect.SQLITE.value, + "database_version": ANY, } @@ -46,6 +48,8 @@ async def test_recorder_system_health_alternate_dbms(hass, recorder_mock, dialec "current_recorder_run": instance.run_history.current.start, "oldest_recorder_run": instance.run_history.first.start, "estimated_db_size": "1.00 MiB", + "database_engine": dialect_name.value, + "database_version": ANY, } @@ -62,4 +66,6 @@ async def test_recorder_system_health_crashed_recorder_runs_table( "current_recorder_run": instance.run_history.current.start, "oldest_recorder_run": instance.run_history.current.start, "estimated_db_size": ANY, + "database_engine": SupportedDialect.SQLITE.value, + "database_version": ANY, } From 20960e182d213f6ae7e364f9ce0742cadbf83ba3 Mon Sep 17 00:00:00 2001 From: Zac West <74188+zacwest@users.noreply.github.com> Date: Sun, 22 May 2022 22:41:51 -0700 Subject: [PATCH 695/930] Log unknown websocket commands at info instead of error (#72336) --- homeassistant/components/websocket_api/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/websocket_api/connection.py b/homeassistant/components/websocket_api/connection.py index cdcee408070..0280863f83e 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -93,7 +93,7 @@ class ActiveConnection: return if msg["type"] not in handlers: - self.logger.error("Received invalid command: {}".format(msg["type"])) + self.logger.info("Received unknown command: {}".format(msg["type"])) self.send_message( messages.error_message( cur_id, const.ERR_UNKNOWN_COMMAND, "Unknown command." From 79fb5e1bec90a9b1b313419702ea620172dd0849 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Mon, 23 May 2022 13:54:49 +0800 Subject: [PATCH 696/930] Add use wallclock as timestamps option to onvif (#71983) --- homeassistant/components/onvif/camera.py | 8 ++++++- homeassistant/components/onvif/config_flow.py | 21 ++++++++++++++++++- tests/components/onvif/test_config_flow.py | 6 +++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/onvif/camera.py b/homeassistant/components/onvif/camera.py index e46e1a2ac59..6aa7ae42767 100644 --- a/homeassistant/components/onvif/camera.py +++ b/homeassistant/components/onvif/camera.py @@ -9,7 +9,10 @@ from yarl import URL from homeassistant.components import ffmpeg from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, get_ffmpeg_manager -from homeassistant.components.stream import CONF_RTSP_TRANSPORT +from homeassistant.components.stream import ( + CONF_RTSP_TRANSPORT, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import HTTP_BASIC_AUTHENTICATION from homeassistant.core import HomeAssistant @@ -97,6 +100,9 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera): self.stream_options[CONF_RTSP_TRANSPORT] = device.config_entry.options.get( CONF_RTSP_TRANSPORT ) + self.stream_options[ + CONF_USE_WALLCLOCK_AS_TIMESTAMPS + ] = device.config_entry.options.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS, False) self._basic_auth = ( device.config_entry.data.get(CONF_SNAPSHOT_AUTH) == HTTP_BASIC_AUTHENTICATION diff --git a/homeassistant/components/onvif/config_flow.py b/homeassistant/components/onvif/config_flow.py index 894f2fee3df..1ee0be18467 100644 --- a/homeassistant/components/onvif/config_flow.py +++ b/homeassistant/components/onvif/config_flow.py @@ -13,7 +13,11 @@ from zeep.exceptions import Fault from homeassistant import config_entries from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS -from homeassistant.components.stream import CONF_RTSP_TRANSPORT, RTSP_TRANSPORTS +from homeassistant.components.stream import ( + CONF_RTSP_TRANSPORT, + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + RTSP_TRANSPORTS, +) from homeassistant.const import ( CONF_HOST, CONF_NAME, @@ -272,8 +276,22 @@ class OnvifOptionsFlowHandler(config_entries.OptionsFlow): if user_input is not None: self.options[CONF_EXTRA_ARGUMENTS] = user_input[CONF_EXTRA_ARGUMENTS] self.options[CONF_RTSP_TRANSPORT] = user_input[CONF_RTSP_TRANSPORT] + self.options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = user_input.get( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + self.config_entry.options.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS, False), + ) return self.async_create_entry(title="", data=self.options) + advanced_options = {} + if self.show_advanced_options: + advanced_options[ + vol.Optional( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, + default=self.config_entry.options.get( + CONF_USE_WALLCLOCK_AS_TIMESTAMPS, False + ), + ) + ] = bool return self.async_show_form( step_id="onvif_devices", data_schema=vol.Schema( @@ -290,6 +308,7 @@ class OnvifOptionsFlowHandler(config_entries.OptionsFlow): CONF_RTSP_TRANSPORT, next(iter(RTSP_TRANSPORTS)) ), ): vol.In(RTSP_TRANSPORTS), + **advanced_options, } ), ) diff --git a/tests/components/onvif/test_config_flow.py b/tests/components/onvif/test_config_flow.py index 1e4dabfe7cf..fec1e2b8132 100644 --- a/tests/components/onvif/test_config_flow.py +++ b/tests/components/onvif/test_config_flow.py @@ -314,7 +314,9 @@ async def test_option_flow(hass): """Test config flow options.""" entry, _, _ = await setup_onvif_integration(hass) - result = await hass.config_entries.options.async_init(entry.entry_id) + result = await hass.config_entries.options.async_init( + entry.entry_id, context={"show_advanced_options": True} + ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "onvif_devices" @@ -324,6 +326,7 @@ async def test_option_flow(hass): user_input={ config_flow.CONF_EXTRA_ARGUMENTS: "", config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1], + config_flow.CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True, }, ) @@ -331,4 +334,5 @@ async def test_option_flow(hass): assert result["data"] == { config_flow.CONF_EXTRA_ARGUMENTS: "", config_flow.CONF_RTSP_TRANSPORT: list(config_flow.RTSP_TRANSPORTS)[1], + config_flow.CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True, } From f965f542a316c9ed60e11d9fdb9a14d031ab21b0 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Mon, 23 May 2022 13:56:03 +0800 Subject: [PATCH 697/930] Always set use wallclock as timestamps in ezviz (#71984) --- homeassistant/components/ezviz/camera.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/ezviz/camera.py b/homeassistant/components/ezviz/camera.py index 2fa410e268c..91bab5f83af 100644 --- a/homeassistant/components/ezviz/camera.py +++ b/homeassistant/components/ezviz/camera.py @@ -9,6 +9,7 @@ import voluptuous as vol from homeassistant.components import ffmpeg from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.ffmpeg import get_ffmpeg_manager +from homeassistant.components.stream import CONF_USE_WALLCLOCK_AS_TIMESTAMPS from homeassistant.config_entries import ( SOURCE_IGNORE, SOURCE_INTEGRATION_DISCOVERY, @@ -186,6 +187,7 @@ class EzvizCamera(EzvizEntity, Camera): """Initialize a Ezviz security camera.""" super().__init__(coordinator, serial) Camera.__init__(self) + self.stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True self._username = camera_username self._password = camera_password self._rtsp_stream = camera_rtsp_stream From c2612d1adec64fc530819e864c503d6661c096b0 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Mon, 23 May 2022 13:56:26 +0800 Subject: [PATCH 698/930] Remove cache control headers from stream (#71996) --- homeassistant/components/stream/hls.py | 24 +----------------------- tests/components/stream/conftest.py | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/stream/hls.py b/homeassistant/components/stream/hls.py index 44b19d2cc85..23584b59fb9 100644 --- a/homeassistant/components/stream/hls.py +++ b/homeassistant/components/stream/hls.py @@ -215,16 +215,6 @@ class HlsPlaylistView(StreamView): return web.Response( body=None, status=HTTPStatus.BAD_REQUEST, - # From Appendix B.1 of the RFC: - # Successful responses to blocking Playlist requests should be cached - # for six Target Durations. Unsuccessful responses (such as 404s) should - # be cached for four Target Durations. Successful responses to non-blocking - # Playlist requests should be cached for half the Target Duration. - # Unsuccessful responses to non-blocking Playlist requests should be - # cached for for one Target Duration. - headers={ - "Cache-Control": f"max-age={(4 if blocking else 1)*target_duration:.0f}" - }, ) @staticmethod @@ -233,9 +223,6 @@ class HlsPlaylistView(StreamView): return web.Response( body=None, status=HTTPStatus.NOT_FOUND, - headers={ - "Cache-Control": f"max-age={(4 if blocking else 1)*target_duration:.0f}" - }, ) async def handle( @@ -318,7 +305,6 @@ class HlsPlaylistView(StreamView): body=self.render(track).encode("utf-8"), headers={ "Content-Type": FORMAT_CONTENT_TYPE[HLS_PROVIDER], - "Cache-Control": f"max-age={(6 if blocking_request else 0.5)*track.target_duration:.0f}", }, ) response.enable_compression(web.ContentCoding.gzip) @@ -373,22 +359,16 @@ class HlsPartView(StreamView): return web.Response( body=None, status=HTTPStatus.NOT_FOUND, - headers={"Cache-Control": f"max-age={track.target_duration:.0f}"}, ) # If the part is ready or has been hinted, if int(part_num) == len(segment.parts): await track.part_recv(timeout=track.stream_settings.hls_part_timeout) if int(part_num) >= len(segment.parts): - return web.HTTPRequestRangeNotSatisfiable( - headers={ - "Cache-Control": f"max-age={track.target_duration:.0f}", - } - ) + return web.HTTPRequestRangeNotSatisfiable() return web.Response( body=segment.parts[int(part_num)].data, headers={ "Content-Type": "video/iso.segment", - "Cache-Control": f"max-age={6*track.target_duration:.0f}", }, ) @@ -421,12 +401,10 @@ class HlsSegmentView(StreamView): return web.Response( body=None, status=HTTPStatus.NOT_FOUND, - headers={"Cache-Control": f"max-age={track.target_duration:.0f}"}, ) return web.Response( body=segment.get_data(), headers={ "Content-Type": "video/iso.segment", - "Cache-Control": f"max-age={6*track.target_duration:.0f}", }, ) diff --git a/tests/components/stream/conftest.py b/tests/components/stream/conftest.py index 407b144267b..a3d2da8bd52 100644 --- a/tests/components/stream/conftest.py +++ b/tests/components/stream/conftest.py @@ -176,7 +176,7 @@ class HLSSync: self.check_requests_ready() return self._original_not_found() - def response(self, body, headers, status=HTTPStatus.OK): + def response(self, body, headers=None, status=HTTPStatus.OK): """Intercept the Response call so we know when the web handler is finished.""" self._num_finished += 1 self.check_requests_ready() From 623abb11474e8b69314bbc062a5b3391f11eef06 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 08:03:09 +0200 Subject: [PATCH 699/930] Move manual configuration of MQTT number to the integration key (#72272) Add number --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/number.py | 29 +++++++++++++++++++---- tests/components/mqtt/test_number.py | 14 +++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 426fad45db1..36c39e5b782 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -202,6 +202,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.LOCK.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, + vol.Optional(Platform.NUMBER.value): cv.ensure_list, } ) diff --git a/homeassistant/components/mqtt/number.py b/homeassistant/components/mqtt/number.py index e13ae4ded84..001f9f4f668 100644 --- a/homeassistant/components/mqtt/number.py +++ b/homeassistant/components/mqtt/number.py @@ -1,6 +1,7 @@ """Configure number in a device through MQTT topic.""" from __future__ import annotations +import asyncio import functools import logging @@ -40,8 +41,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -72,7 +75,7 @@ def validate_config(config): return config -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): vol.Coerce(float), @@ -88,11 +91,18 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -PLATFORM_SCHEMA = vol.All( +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, validate_config, ) +# Configuring MQTT Number under the number platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), + validate_config, + warn_for_legacy_schema(number.DOMAIN), +) + DISCOVERY_SCHEMA = vol.All( _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), validate_config, @@ -105,7 +115,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT number through configuration.yaml.""" + """Set up MQTT number configured under the number platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, number.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -116,7 +127,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT number dynamically through MQTT discovery.""" + """Set up MQTT number through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, number.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_number.py b/tests/components/mqtt/test_number.py index 73c1da357fa..4eb8fdec351 100644 --- a/tests/components/mqtt/test_number.py +++ b/tests/components/mqtt/test_number.py @@ -1,4 +1,5 @@ """The tests for mqtt number component.""" +import copy import json from unittest.mock import patch @@ -50,6 +51,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -729,3 +731,15 @@ async def test_encoding_subscribable_topics( attribute, attribute_value, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = number.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From c4893f96f60ab9f05215040c21526aa47ea0803f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 08:08:08 +0200 Subject: [PATCH 700/930] Move manual configuration of MQTT siren to the integration key (#72278) Add siren --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/siren.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_siren.py | 13 +++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 36c39e5b782..dff5e75fa23 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -200,6 +200,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.HUMIDIFIER.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, + vol.Optional(Platform.SIREN.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, vol.Optional(Platform.NUMBER.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index beed5b51e20..c3a41c3618e 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -1,6 +1,7 @@ """Support for MQTT sirens.""" from __future__ import annotations +import asyncio import copy import functools import json @@ -51,8 +52,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) DEFAULT_NAME = "MQTT Siren" @@ -71,7 +74,7 @@ CONF_SUPPORT_VOLUME_SET = "support_volume_set" STATE = "state" -PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_AVAILABLE_TONES): cv.ensure_list, vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, @@ -88,7 +91,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = vol.All(PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA)) +# Configuring MQTT Sirens under the siren platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(siren.DOMAIN), +) + +DISCOVERY_SCHEMA = vol.All(PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)) MQTT_SIREN_ATTRIBUTES_BLOCKED = frozenset( { @@ -116,7 +125,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT siren through configuration.yaml.""" + """Set up MQTT sirens configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, siren.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -127,7 +137,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT siren dynamically through MQTT discovery.""" + """Set up MQTT siren through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, siren.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_siren.py b/tests/components/mqtt/test_siren.py index f39154badc9..197ed34b7e4 100644 --- a/tests/components/mqtt/test_siren.py +++ b/tests/components/mqtt/test_siren.py @@ -42,6 +42,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -895,3 +896,15 @@ async def test_encoding_subscribable_topics( attribute, attribute_value, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = siren.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From cf5e21a996818d4273cb107f1de5c91ac69ab4e9 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Sun, 22 May 2022 23:43:42 -0700 Subject: [PATCH 701/930] Use properties of wemo Insight device (#72316) --- .../components/wemo/binary_sensor.py | 5 +- homeassistant/components/wemo/switch.py | 43 +++++-------- tests/components/wemo/conftest.py | 27 +++++--- tests/components/wemo/test_binary_sensor.py | 21 ++----- tests/components/wemo/test_sensor.py | 15 ----- tests/components/wemo/test_switch.py | 63 ++++++++++++++++++- 6 files changed, 103 insertions(+), 71 deletions(-) diff --git a/homeassistant/components/wemo/binary_sensor.py b/homeassistant/components/wemo/binary_sensor.py index cde13d632fe..3e7b159dc63 100644 --- a/homeassistant/components/wemo/binary_sensor.py +++ b/homeassistant/components/wemo/binary_sensor.py @@ -2,7 +2,7 @@ import asyncio from typing import cast -from pywemo import Insight, Maker +from pywemo import Insight, Maker, StandbyState from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.config_entries import ConfigEntry @@ -64,4 +64,5 @@ class InsightBinarySensor(WemoBinarySensor): @property def is_on(self) -> bool: """Return true device connected to the Insight Switch is on.""" - return super().is_on and self.wemo.insight_params["state"] == "1" + # Note: wemo.get_standby_state is a @property. + return super().is_on and self.wemo.get_standby_state == StandbyState.ON diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index d3c6264ab89..48cc1d92d67 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -5,7 +5,7 @@ import asyncio from datetime import datetime, timedelta from typing import Any, cast -from pywemo import CoffeeMaker, Insight, Maker +from pywemo import CoffeeMaker, Insight, Maker, StandbyState from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry @@ -13,7 +13,6 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_STANDBY, STATE_UNKNOW from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util import convert from .const import DOMAIN as WEMO_DOMAIN from .entity import WemoBinaryStateEntity @@ -22,19 +21,18 @@ from .wemo_device import DeviceCoordinator SCAN_INTERVAL = timedelta(seconds=10) PARALLEL_UPDATES = 0 -# The WEMO_ constants below come from pywemo itself +ATTR_COFFEMAKER_MODE = "coffeemaker_mode" +ATTR_CURRENT_STATE_DETAIL = "state_detail" +ATTR_ON_LATEST_TIME = "on_latest_time" +ATTR_ON_TODAY_TIME = "on_today_time" +ATTR_ON_TOTAL_TIME = "on_total_time" +ATTR_POWER_THRESHOLD = "power_threshold_w" ATTR_SENSOR_STATE = "sensor_state" ATTR_SWITCH_MODE = "switch_mode" -ATTR_CURRENT_STATE_DETAIL = "state_detail" -ATTR_COFFEMAKER_MODE = "coffeemaker_mode" MAKER_SWITCH_MOMENTARY = "momentary" MAKER_SWITCH_TOGGLE = "toggle" -WEMO_ON = 1 -WEMO_OFF = 0 -WEMO_STANDBY = 8 - async def async_setup_entry( hass: HomeAssistant, @@ -83,20 +81,10 @@ class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): attr[ATTR_CURRENT_STATE_DETAIL] = self.detail_state if isinstance(self.wemo, Insight): - attr["on_latest_time"] = WemoSwitch.as_uptime( - self.wemo.insight_params.get("onfor", 0) - ) - attr["on_today_time"] = WemoSwitch.as_uptime( - self.wemo.insight_params.get("ontoday", 0) - ) - attr["on_total_time"] = WemoSwitch.as_uptime( - self.wemo.insight_params.get("ontotal", 0) - ) - threshold = convert( - self.wemo.insight_params.get("powerthreshold"), float, 0.0 - ) - assert isinstance(threshold, float) - attr["power_threshold_w"] = threshold / 1000.0 + attr[ATTR_ON_LATEST_TIME] = self.as_uptime(self.wemo.on_for) + attr[ATTR_ON_TODAY_TIME] = self.as_uptime(self.wemo.today_on_time) + attr[ATTR_ON_TOTAL_TIME] = self.as_uptime(self.wemo.total_on_time) + attr[ATTR_POWER_THRESHOLD] = self.wemo.threshold_power_watts if isinstance(self.wemo, CoffeeMaker): attr[ATTR_COFFEMAKER_MODE] = self.wemo.mode @@ -117,12 +105,13 @@ class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): if isinstance(self.wemo, CoffeeMaker): return cast(str, self.wemo.mode_string) if isinstance(self.wemo, Insight): - standby_state = int(self.wemo.insight_params.get("state", 0)) - if standby_state == WEMO_ON: + # Note: wemo.get_standby_state is a @property. + standby_state = self.wemo.get_standby_state + if standby_state == StandbyState.ON: return STATE_ON - if standby_state == WEMO_OFF: + if standby_state == StandbyState.OFF: return STATE_OFF - if standby_state == WEMO_STANDBY: + if standby_state == StandbyState.STANDBY: return STATE_STANDBY return STATE_UNKNOWN assert False # Unreachable code statement. diff --git a/tests/components/wemo/conftest.py b/tests/components/wemo/conftest.py index af7002a2500..fbb2f186cf5 100644 --- a/tests/components/wemo/conftest.py +++ b/tests/components/wemo/conftest.py @@ -1,5 +1,6 @@ """Fixtures for pywemo.""" import asyncio +import contextlib from unittest.mock import create_autospec, patch import pytest @@ -52,9 +53,9 @@ def pywemo_discovery_responder_fixture(): yield -@pytest.fixture(name="pywemo_device") -def pywemo_device_fixture(pywemo_registry, pywemo_model): - """Fixture for WeMoDevice instances.""" +@contextlib.contextmanager +def create_pywemo_device(pywemo_registry, pywemo_model): + """Create a WeMoDevice instance.""" cls = getattr(pywemo, pywemo_model) device = create_autospec(cls, instance=True) device.host = MOCK_HOST @@ -83,15 +84,21 @@ def pywemo_device_fixture(pywemo_registry, pywemo_model): yield device +@pytest.fixture(name="pywemo_device") +def pywemo_device_fixture(pywemo_registry, pywemo_model): + """Fixture for WeMoDevice instances.""" + with create_pywemo_device(pywemo_registry, pywemo_model) as pywemo_device: + yield pywemo_device + + @pytest.fixture(name="wemo_entity_suffix") def wemo_entity_suffix_fixture(): """Fixture to select a specific entity for wemo_entity.""" return "" -@pytest.fixture(name="wemo_entity") -async def async_wemo_entity_fixture(hass, pywemo_device, wemo_entity_suffix): - """Fixture for a Wemo entity in hass.""" +async def async_create_wemo_entity(hass, pywemo_device, wemo_entity_suffix): + """Create a hass entity for a wemo device.""" assert await async_setup_component( hass, DOMAIN, @@ -106,7 +113,13 @@ async def async_wemo_entity_fixture(hass, pywemo_device, wemo_entity_suffix): entity_registry = er.async_get(hass) for entry in entity_registry.entities.values(): - if entry.entity_id.endswith(wemo_entity_suffix): + if entry.entity_id.endswith(wemo_entity_suffix or pywemo_device.name.lower()): return entry return None + + +@pytest.fixture(name="wemo_entity") +async def async_wemo_entity_fixture(hass, pywemo_device, wemo_entity_suffix): + """Fixture for a Wemo entity in hass.""" + return await async_create_wemo_entity(hass, pywemo_device, wemo_entity_suffix) diff --git a/tests/components/wemo/test_binary_sensor.py b/tests/components/wemo/test_binary_sensor.py index 481a6348688..eccc1b180a5 100644 --- a/tests/components/wemo/test_binary_sensor.py +++ b/tests/components/wemo/test_binary_sensor.py @@ -1,6 +1,7 @@ """Tests for the Wemo binary_sensor entity.""" import pytest +from pywemo import StandbyState from homeassistant.components.homeassistant import ( DOMAIN as HA_DOMAIN, @@ -123,41 +124,27 @@ class TestInsight(EntityTestHelpers): """Select the InsightBinarySensor entity.""" return InsightBinarySensor._name_suffix.lower() - @pytest.fixture(name="pywemo_device") - def pywemo_device_fixture(self, pywemo_device): - """Fixture for WeMoDevice instances.""" - pywemo_device.insight_params = { - "currentpower": 1.0, - "todaymw": 200000000.0, - "state": "0", - "onfor": 0, - "ontoday": 0, - "ontotal": 0, - "powerthreshold": 0, - } - yield pywemo_device - async def test_registry_state_callback( self, hass, pywemo_registry, pywemo_device, wemo_entity ): """Verify that the binary_sensor receives state updates from the registry.""" # On state. pywemo_device.get_state.return_value = 1 - pywemo_device.insight_params["state"] = "1" + pywemo_device.get_standby_state = StandbyState.ON pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_ON # Standby (Off) state. pywemo_device.get_state.return_value = 1 - pywemo_device.insight_params["state"] = "8" + pywemo_device.get_standby_state = StandbyState.STANDBY pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF # Off state. pywemo_device.get_state.return_value = 0 - pywemo_device.insight_params["state"] = "1" + pywemo_device.get_standby_state = StandbyState.OFF pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF diff --git a/tests/components/wemo/test_sensor.py b/tests/components/wemo/test_sensor.py index ab6753975f1..7e0c8fa72f0 100644 --- a/tests/components/wemo/test_sensor.py +++ b/tests/components/wemo/test_sensor.py @@ -12,21 +12,6 @@ def pywemo_model(): return "Insight" -@pytest.fixture(name="pywemo_device") -def pywemo_device_fixture(pywemo_device): - """Fixture for WeMoDevice instances.""" - pywemo_device.insight_params = { - "currentpower": 1.0, - "todaymw": 200000000.0, - "state": 0, - "onfor": 0, - "ontoday": 0, - "ontotal": 0, - "powerthreshold": 0, - } - yield pywemo_device - - class InsightTestTemplate(EntityTestHelpers): """Base class for testing WeMo Insight Sensors.""" diff --git a/tests/components/wemo/test_switch.py b/tests/components/wemo/test_switch.py index 9c1dc804645..d54099e5e4a 100644 --- a/tests/components/wemo/test_switch.py +++ b/tests/components/wemo/test_switch.py @@ -1,17 +1,35 @@ """Tests for the Wemo switch entity.""" import pytest -from pywemo.exceptions import ActionException +import pywemo from homeassistant.components.homeassistant import ( DOMAIN as HA_DOMAIN, SERVICE_UPDATE_ENTITY, ) from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN -from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON +from homeassistant.components.wemo.switch import ( + ATTR_CURRENT_STATE_DETAIL, + ATTR_ON_LATEST_TIME, + ATTR_ON_TODAY_TIME, + ATTR_ON_TOTAL_TIME, + ATTR_POWER_THRESHOLD, +) +from homeassistant.const import ( + ATTR_ENTITY_ID, + STATE_OFF, + STATE_ON, + STATE_STANDBY, + STATE_UNKNOWN, +) from homeassistant.setup import async_setup_component from . import entity_test_helpers +from .conftest import ( + MOCK_INSIGHT_STATE_THRESHOLD_POWER, + async_create_wemo_entity, + create_pywemo_device, +) @pytest.fixture @@ -80,7 +98,7 @@ async def test_available_after_update( hass, pywemo_registry, pywemo_device, wemo_entity ): """Test the avaliability when an On call fails and after an update.""" - pywemo_device.on.side_effect = ActionException + pywemo_device.on.side_effect = pywemo.exceptions.ActionException pywemo_device.get_state.return_value = 1 await entity_test_helpers.test_avaliable_after_update( hass, pywemo_registry, pywemo_device, wemo_entity, SWITCH_DOMAIN @@ -90,3 +108,42 @@ async def test_available_after_update( async def test_turn_off_state(hass, wemo_entity): """Test that the device state is updated after turning off.""" await entity_test_helpers.test_turn_off_state(hass, wemo_entity, SWITCH_DOMAIN) + + +async def test_insight_state_attributes(hass, pywemo_registry): + """Verify the switch attributes are set for the Insight device.""" + await async_setup_component(hass, HA_DOMAIN, {}) + with create_pywemo_device(pywemo_registry, "Insight") as insight: + wemo_entity = await async_create_wemo_entity(hass, insight, "") + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_ON_LATEST_TIME] == "00d 00h 20m 34s" + assert attributes[ATTR_ON_TODAY_TIME] == "00d 01h 34m 38s" + assert attributes[ATTR_ON_TOTAL_TIME] == "00d 02h 30m 12s" + assert attributes[ATTR_POWER_THRESHOLD] == MOCK_INSIGHT_STATE_THRESHOLD_POWER + assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_OFF + + async def async_update(): + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: [wemo_entity.entity_id]}, + blocking=True, + ) + + # Test 'ON' state detail value. + insight.get_standby_state = pywemo.StandbyState.ON + await async_update() + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_ON + + # Test 'STANDBY' state detail value. + insight.get_standby_state = pywemo.StandbyState.STANDBY + await async_update() + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_STANDBY + + # Test 'UNKNOWN' state detail value. + insight.get_standby_state = None + await async_update() + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_UNKNOWN From eb988f77921be9fbc9f6dab1c96f9569dcd27107 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 09:03:30 +0200 Subject: [PATCH 702/930] Fix race in MQTT platform setup (#72344) Make sure MQTT platforms setup locks discovery --- homeassistant/components/mqtt/__init__.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index dff5e75fa23..9191b22064c 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -816,14 +816,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock() hass.data[CONFIG_ENTRY_IS_SETUP] = set() - async with hass.data[DATA_CONFIG_ENTRY_LOCK]: - for component in PLATFORMS: - config_entries_key = f"{component}.mqtt" - if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: - hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) - hass.async_create_task( - hass.config_entries.async_forward_entry_setup(entry, component) - ) + async def async_forward_entry_setup(): + """Forward the config entry setup to the platforms.""" + async with hass.data[DATA_CONFIG_ENTRY_LOCK]: + for component in PLATFORMS: + config_entries_key = f"{component}.mqtt" + if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: + hass.data[CONFIG_ENTRY_IS_SETUP].add(config_entries_key) + await hass.config_entries.async_forward_entry_setup( + entry, component + ) + + hass.async_create_task(async_forward_entry_setup()) if conf.get(CONF_DISCOVERY): await _async_setup_discovery(hass, conf, entry) From 673f43fbec52b2ef5c7ece311298af5f021ca803 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 09:04:03 +0200 Subject: [PATCH 703/930] Move manual configuration of MQTT scene to the integration key (#72273) Add scene Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/scene.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_scene.py | 13 +++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 9191b22064c..c22029f59f8 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -200,6 +200,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.HUMIDIFIER.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, + vol.Optional(Platform.SCENE.value): cv.ensure_list, vol.Optional(Platform.SIREN.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/scene.py b/homeassistant/components/mqtt/scene.py index c9bcae2dac1..98c692ceaff 100644 --- a/homeassistant/components/mqtt/scene.py +++ b/homeassistant/components/mqtt/scene.py @@ -1,6 +1,7 @@ """Support for MQTT scenes.""" from __future__ import annotations +import asyncio import functools import voluptuous as vol @@ -21,14 +22,16 @@ from .mixins import ( CONF_OBJECT_ID, MQTT_AVAILABILITY_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) DEFAULT_NAME = "MQTT Scene" DEFAULT_RETAIN = False -PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_BASE_SCHEMA.extend( { vol.Required(CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_ICON): cv.icon, @@ -42,7 +45,13 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( } ).extend(MQTT_AVAILABILITY_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA.extend({}, extra=vol.REMOVE_EXTRA) +# Configuring MQTT Scenes under the scene platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(scene.DOMAIN), +) + +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_platform( @@ -51,7 +60,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT scene through configuration.yaml.""" + """Set up MQTT scene configured under the scene platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, scene.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -62,7 +72,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT scene dynamically through MQTT discovery.""" + """Set up MQTT scene through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, scene.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_scene.py b/tests/components/mqtt/test_scene.py index 1ccacc1c5ee..15bbd3964e6 100644 --- a/tests/components/mqtt/test_scene.py +++ b/tests/components/mqtt/test_scene.py @@ -20,6 +20,7 @@ from .test_common import ( help_test_discovery_update_unchanged, help_test_reloadable, help_test_reloadable_late, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, ) @@ -191,3 +192,15 @@ async def test_reloadable_late(hass, mqtt_client_mock, caplog, tmp_path): domain = scene.DOMAIN config = DEFAULT_CONFIG[domain] await help_test_reloadable_late(hass, caplog, tmp_path, domain, config) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = scene.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 01c211a7f9bb055afb1226c46d1e1608cfc51560 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Mon, 23 May 2022 09:06:09 +0200 Subject: [PATCH 704/930] Bump async-upnp-client to 0.30.1 (#72332) Co-authored-by: J. Nick Koston --- homeassistant/components/dlna_dmr/manifest.json | 2 +- homeassistant/components/dlna_dms/manifest.json | 2 +- homeassistant/components/samsungtv/manifest.json | 2 +- homeassistant/components/ssdp/manifest.json | 2 +- homeassistant/components/upnp/manifest.json | 2 +- homeassistant/components/yeelight/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/dlna_dmr/manifest.json b/homeassistant/components/dlna_dmr/manifest.json index cae136cd8fd..15beea714da 100644 --- a/homeassistant/components/dlna_dmr/manifest.json +++ b/homeassistant/components/dlna_dmr/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Renderer", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dmr", - "requirements": ["async-upnp-client==0.30.0"], + "requirements": ["async-upnp-client==0.30.1"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/dlna_dms/manifest.json b/homeassistant/components/dlna_dms/manifest.json index 58c79a048b4..21329440788 100644 --- a/homeassistant/components/dlna_dms/manifest.json +++ b/homeassistant/components/dlna_dms/manifest.json @@ -3,7 +3,7 @@ "name": "DLNA Digital Media Server", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/dlna_dms", - "requirements": ["async-upnp-client==0.30.0"], + "requirements": ["async-upnp-client==0.30.1"], "dependencies": ["ssdp"], "after_dependencies": ["media_source"], "ssdp": [ diff --git a/homeassistant/components/samsungtv/manifest.json b/homeassistant/components/samsungtv/manifest.json index bef293185a6..fd97eb12e54 100644 --- a/homeassistant/components/samsungtv/manifest.json +++ b/homeassistant/components/samsungtv/manifest.json @@ -7,7 +7,7 @@ "samsungctl[websocket]==0.7.1", "samsungtvws[async,encrypted]==2.5.0", "wakeonlan==2.0.1", - "async-upnp-client==0.30.0" + "async-upnp-client==0.30.1" ], "ssdp": [ { diff --git a/homeassistant/components/ssdp/manifest.json b/homeassistant/components/ssdp/manifest.json index cacce09e1f2..5cbd3d0d10e 100644 --- a/homeassistant/components/ssdp/manifest.json +++ b/homeassistant/components/ssdp/manifest.json @@ -2,7 +2,7 @@ "domain": "ssdp", "name": "Simple Service Discovery Protocol (SSDP)", "documentation": "https://www.home-assistant.io/integrations/ssdp", - "requirements": ["async-upnp-client==0.30.0"], + "requirements": ["async-upnp-client==0.30.1"], "dependencies": ["network"], "after_dependencies": ["zeroconf"], "codeowners": [], diff --git a/homeassistant/components/upnp/manifest.json b/homeassistant/components/upnp/manifest.json index 2bf1786ccbe..2e76dac4adb 100644 --- a/homeassistant/components/upnp/manifest.json +++ b/homeassistant/components/upnp/manifest.json @@ -3,7 +3,7 @@ "name": "UPnP/IGD", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/upnp", - "requirements": ["async-upnp-client==0.30.0", "getmac==0.8.2"], + "requirements": ["async-upnp-client==0.30.1", "getmac==0.8.2"], "dependencies": ["network", "ssdp"], "codeowners": ["@StevenLooman", "@ehendrix23"], "ssdp": [ diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 853c95abcbb..5ec224498be 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -2,7 +2,7 @@ "domain": "yeelight", "name": "Yeelight", "documentation": "https://www.home-assistant.io/integrations/yeelight", - "requirements": ["yeelight==0.7.10", "async-upnp-client==0.30.0"], + "requirements": ["yeelight==0.7.10", "async-upnp-client==0.30.1"], "codeowners": ["@zewelor", "@shenxn", "@starkillerOG", "@alexyao2015"], "config_flow": true, "dependencies": ["network"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 1a61d2ad2b0..f10cea82655 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -4,7 +4,7 @@ aiodiscover==1.4.11 aiohttp==3.8.1 aiohttp_cors==0.7.0 astral==2.2 -async-upnp-client==0.30.0 +async-upnp-client==0.30.1 async_timeout==4.0.2 atomicwrites==1.4.0 attrs==21.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index ab4a232e864..ddda9476be4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -339,7 +339,7 @@ asterisk_mbox==0.5.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.30.0 +async-upnp-client==0.30.1 # homeassistant.components.supla asyncpysupla==0.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5159abb4f43..69535dc6f37 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -281,7 +281,7 @@ arcam-fmj==0.12.0 # homeassistant.components.ssdp # homeassistant.components.upnp # homeassistant.components.yeelight -async-upnp-client==0.30.0 +async-upnp-client==0.30.1 # homeassistant.components.sleepiq asyncsleepiq==1.2.3 From 911fc83606c31ab4c9593d7827fd4ffd33713236 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 May 2022 09:21:59 +0200 Subject: [PATCH 705/930] Bump actions/upload-artifact from 3.0.0 to 3.1.0 (#72343) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/wheels.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0fb77fdc789..4995864b377 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -827,7 +827,7 @@ jobs: -p no:sugar \ tests/components/${{ matrix.group }} - name: Upload coverage artifact - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v3.1.0 with: name: coverage-${{ matrix.python-version }}-${{ matrix.group }} path: coverage.xml diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 70b6534df5e..6019e533530 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -50,13 +50,13 @@ jobs: ) > .env_file - name: Upload env_file - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v3.1.0 with: name: env_file path: ./.env_file - name: Upload requirements_diff - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v3.1.0 with: name: requirements_diff path: ./requirements_diff.txt From c52e8f7cfe08731d2d7bfaa9c6a060d5070e6ccc Mon Sep 17 00:00:00 2001 From: Felipe Martins Diel <41558831+felipediel@users.noreply.github.com> Date: Mon, 23 May 2022 04:45:05 -0300 Subject: [PATCH 706/930] Bump broadlink to 0.18.2 (#72346) --- homeassistant/components/broadlink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/broadlink/manifest.json b/homeassistant/components/broadlink/manifest.json index f291ba83afa..949f8add20b 100644 --- a/homeassistant/components/broadlink/manifest.json +++ b/homeassistant/components/broadlink/manifest.json @@ -2,7 +2,7 @@ "domain": "broadlink", "name": "Broadlink", "documentation": "https://www.home-assistant.io/integrations/broadlink", - "requirements": ["broadlink==0.18.1"], + "requirements": ["broadlink==0.18.2"], "codeowners": ["@danielhiversen", "@felipediel", "@L-I-Am"], "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index ddda9476be4..02a895c703a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -433,7 +433,7 @@ boto3==1.20.24 bravia-tv==1.0.11 # homeassistant.components.broadlink -broadlink==0.18.1 +broadlink==0.18.2 # homeassistant.components.brother brother==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 69535dc6f37..f7e948b333a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -330,7 +330,7 @@ boschshcpy==0.2.30 bravia-tv==1.0.11 # homeassistant.components.broadlink -broadlink==0.18.1 +broadlink==0.18.2 # homeassistant.components.brother brother==1.1.0 From bc6451bd64b8a552078f781d262e28b93961391f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 10:08:44 +0200 Subject: [PATCH 707/930] Move manual configuration of MQTT select to the integration key (#72274) Add select Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/select.py | 28 +++++++++++++++++++---- tests/components/mqtt/test_select.py | 13 +++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index c22029f59f8..52204b31dcf 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -201,6 +201,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, vol.Optional(Platform.SCENE.value): cv.ensure_list, + vol.Optional(Platform.SELECT.value): cv.ensure_list, vol.Optional(Platform.SIREN.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/select.py b/homeassistant/components/mqtt/select.py index f873a32a5de..0765eb7f176 100644 --- a/homeassistant/components/mqtt/select.py +++ b/homeassistant/components/mqtt/select.py @@ -1,6 +1,7 @@ """Configure select in a device through MQTT topic.""" from __future__ import annotations +import asyncio import functools import logging @@ -30,8 +31,10 @@ from .debug_info import log_messages from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -48,7 +51,7 @@ MQTT_SELECT_ATTRIBUTES_BLOCKED = frozenset( ) -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RW_SCHEMA.extend( { vol.Optional(CONF_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -58,9 +61,13 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( }, ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -PLATFORM_SCHEMA = vol.All(_PLATFORM_SCHEMA_BASE) +# Configuring MQTT Select under the select platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.PLATFORM_SCHEMA.extend(PLATFORM_SCHEMA_MODERN.schema), + warn_for_legacy_schema(select.DOMAIN), +) -DISCOVERY_SCHEMA = vol.All(_PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA)) +DISCOVERY_SCHEMA = vol.All(PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA)) async def async_setup_platform( @@ -69,7 +76,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT select through configuration.yaml.""" + """Set up MQTT select configured under the select platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, select.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -80,7 +88,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT select dynamically through MQTT discovery.""" + """Set up MQTT select through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, select.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_select.py b/tests/components/mqtt/test_select.py index 069c0dfe4c9..cf5abf55854 100644 --- a/tests/components/mqtt/test_select.py +++ b/tests/components/mqtt/test_select.py @@ -41,6 +41,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -610,3 +611,15 @@ async def test_encoding_subscribable_topics( attribute, attribute_value, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = select.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 793ad568eb0006ba4b0fd10c3e0e95941980ea95 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Mon, 23 May 2022 10:47:40 +0200 Subject: [PATCH 708/930] here_travel_time: Add unique_id and DeviceInfo (#72352) Co-authored-by: Franck Nijhof --- homeassistant/components/here_travel_time/sensor.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py index 4a09252f068..75c9fd2ea3b 100644 --- a/homeassistant/components/here_travel_time/sensor.py +++ b/homeassistant/components/here_travel_time/sensor.py @@ -19,6 +19,8 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.start import async_at_start from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -144,6 +146,7 @@ async def async_setup_entry( async_add_entities( [ HERETravelTimeSensor( + config_entry.entry_id, config_entry.data[CONF_NAME], config_entry.options[CONF_TRAFFIC_MODE], hass.data[DOMAIN][config_entry.entry_id], @@ -157,6 +160,7 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): def __init__( self, + unique_id_prefix: str, name: str, traffic_mode: str, coordinator: HereTravelTimeDataUpdateCoordinator, @@ -166,6 +170,13 @@ class HERETravelTimeSensor(SensorEntity, CoordinatorEntity): self._traffic_mode = traffic_mode == TRAFFIC_MODE_ENABLED self._attr_native_unit_of_measurement = TIME_MINUTES self._attr_name = name + self._attr_unique_id = unique_id_prefix + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, unique_id_prefix)}, + entry_type=DeviceEntryType.SERVICE, + name=name, + manufacturer="HERE Technologies", + ) async def async_added_to_hass(self) -> None: """Wait for start so origin and destination entities can be resolved.""" From 800410ddf09dad2bab842e7fa40c59568e40ff98 Mon Sep 17 00:00:00 2001 From: Matrix Date: Mon, 23 May 2022 16:52:05 +0800 Subject: [PATCH 709/930] Add yolink siren (#72341) * Add yolink siren * Add .coveragerc * fix wrong comments --- .coveragerc | 1 + homeassistant/components/yolink/__init__.py | 2 +- homeassistant/components/yolink/const.py | 1 + homeassistant/components/yolink/siren.py | 118 ++++++++++++++++++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/yolink/siren.py diff --git a/.coveragerc b/.coveragerc index 2d530ab8f45..ed1e0b0bce4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1477,6 +1477,7 @@ omit = homeassistant/components/yolink/coordinator.py homeassistant/components/yolink/entity.py homeassistant/components/yolink/sensor.py + homeassistant/components/yolink/siren.py homeassistant/components/yolink/switch.py homeassistant/components/youless/__init__.py homeassistant/components/youless/const.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 089f0cc79b9..2c85344c54b 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -21,7 +21,7 @@ SCAN_INTERVAL = timedelta(minutes=5) _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SIREN, Platform.SWITCH] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index b94727cb948..00d6d6d028e 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -18,3 +18,4 @@ ATTR_DEVICE_TH_SENSOR = "THSensor" ATTR_DEVICE_MOTION_SENSOR = "MotionSensor" ATTR_DEVICE_LEAK_SENSOR = "LeakSensor" ATTR_DEVICE_OUTLET = "Outlet" +ATTR_DEVICE_SIREN = "Siren" diff --git a/homeassistant/components/yolink/siren.py b/homeassistant/components/yolink/siren.py new file mode 100644 index 00000000000..7a621db6eca --- /dev/null +++ b/homeassistant/components/yolink/siren.py @@ -0,0 +1,118 @@ +"""YoLink Siren.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + +from yolink.device import YoLinkDevice +from yolink.exception import YoLinkAuthFailError, YoLinkClientError + +from homeassistant.components.siren import ( + SirenEntity, + SirenEntityDescription, + SirenEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATOR, ATTR_DEVICE_SIREN, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +@dataclass +class YoLinkSirenEntityDescription(SirenEntityDescription): + """YoLink SirenEntityDescription.""" + + exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True + value: Callable[[str], bool | None] = lambda _: None + + +DEVICE_TYPES: tuple[YoLinkSirenEntityDescription, ...] = ( + YoLinkSirenEntityDescription( + key="state", + name="State", + value=lambda value: value == "alert", + exists_fn=lambda device: device.device_type in [ATTR_DEVICE_SIREN], + ), +) + +DEVICE_TYPE = [ATTR_DEVICE_SIREN] + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink siren from a config entry.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] + devices = [ + device for device in coordinator.yl_devices if device.device_type in DEVICE_TYPE + ] + entities = [] + for device in devices: + for description in DEVICE_TYPES: + if description.exists_fn(device): + entities.append( + YoLinkSirenEntity(config_entry, coordinator, description, device) + ) + async_add_entities(entities) + + +class YoLinkSirenEntity(YoLinkEntity, SirenEntity): + """YoLink Siren Entity.""" + + entity_description: YoLinkSirenEntityDescription + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: YoLinkCoordinator, + description: YoLinkSirenEntityDescription, + device: YoLinkDevice, + ) -> None: + """Init YoLink Siren.""" + super().__init__(coordinator, device) + self.config_entry = config_entry + self.entity_description = description + self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" + self._attr_name = f"{device.device_name} ({self.entity_description.name})" + self._attr_supported_features = ( + SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF + ) + + @callback + def update_entity_state(self, state: dict) -> None: + """Update HA Entity State.""" + self._attr_is_on = self.entity_description.value( + state[self.entity_description.key] + ) + self.async_write_ha_state() + + async def call_state_change(self, state: bool) -> None: + """Call setState api to change siren state.""" + try: + # call_device_http_api will check result, fail by raise YoLinkClientError + await self.device.call_device_http_api( + "setState", {"state": {"alarm": state}} + ) + except YoLinkAuthFailError as yl_auth_err: + self.config_entry.async_start_reauth(self.hass) + raise HomeAssistantError(yl_auth_err) from yl_auth_err + except YoLinkClientError as yl_client_err: + self.coordinator.last_update_success = False + raise HomeAssistantError(yl_client_err) from yl_client_err + self._attr_is_on = self.entity_description.value("alert" if state else "normal") + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the entity on.""" + await self.call_state_change(True) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity off.""" + await self.call_state_change(False) From e17a653cf0a84de374cc215737bfea11ded38fee Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Mon, 23 May 2022 10:59:10 +0200 Subject: [PATCH 710/930] Bump plugwise to v0.18.4 (#72263) --- homeassistant/components/plugwise/climate.py | 4 +--- .../components/plugwise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../all_data.json | 5 ----- .../fixtures/anna_heatpump/all_data.json | 9 ++++----- tests/components/plugwise/test_climate.py | 19 ++++--------------- tests/components/plugwise/test_diagnostics.py | 5 ----- 8 files changed, 12 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/plugwise/climate.py b/homeassistant/components/plugwise/climate.py index 09f5181090c..c74a1baa9be 100644 --- a/homeassistant/components/plugwise/climate.py +++ b/homeassistant/components/plugwise/climate.py @@ -105,6 +105,7 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity): return HVACAction.HEATING if heater_central_data["binary_sensors"].get("cooling_state"): return HVACAction.COOLING + return HVACAction.IDLE @property @@ -132,9 +133,6 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity): @plugwise_command async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set the hvac mode.""" - if hvac_mode == HVACMode.AUTO and not self.device.get("schedule_temperature"): - raise ValueError("Cannot set HVAC mode to Auto: No schedule available") - await self.coordinator.api.set_schedule_state( self.device["location"], self.device.get("last_used"), diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index abf6f27b3fa..2b7f21cd106 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.18.2"], + "requirements": ["plugwise==0.18.4"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 02a895c703a..28417beb21b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1245,7 +1245,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.18.2 +plugwise==0.18.4 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f7e948b333a..da7577fa7d3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -847,7 +847,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.18.2 +plugwise==0.18.4 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json b/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json index 85e5abb0b17..6ef0716e4b6 100644 --- a/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json +++ b/tests/components/plugwise/fixtures/adam_multiple_devices_per_zone/all_data.json @@ -34,7 +34,6 @@ ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 16.5, @@ -104,7 +103,6 @@ ], "selected_schedule": "GF7 Woonkamer", "last_used": "GF7 Woonkamer", - "schedule_temperature": 15.0, "mode": "auto", "sensors": { "temperature": 20.9, @@ -301,7 +299,6 @@ ], "selected_schedule": "CV Jessie", "last_used": "CV Jessie", - "schedule_temperature": 15.0, "mode": "auto", "sensors": { "temperature": 17.2, @@ -352,7 +349,6 @@ ], "selected_schedule": "Badkamer Schema", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, "mode": "auto", "sensors": { "temperature": 18.9, @@ -402,7 +398,6 @@ ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 15.6, diff --git a/tests/components/plugwise/fixtures/anna_heatpump/all_data.json b/tests/components/plugwise/fixtures/anna_heatpump/all_data.json index cc3f8d8f385..60bc4c35668 100644 --- a/tests/components/plugwise/fixtures/anna_heatpump/all_data.json +++ b/tests/components/plugwise/fixtures/anna_heatpump/all_data.json @@ -63,11 +63,10 @@ "resolution": 0.1, "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "active_preset": "home", - "available_schedules": ["None"], - "selected_schedule": "None", - "last_used": null, - "schedule_temperature": null, - "mode": "heat", + "available_schedules": ["standaard"], + "selected_schedule": "standaard", + "last_used": "standaard", + "mode": "auto", "sensors": { "temperature": 19.3, "setpoint": 21.0, diff --git a/tests/components/plugwise/test_climate.py b/tests/components/plugwise/test_climate.py index 34a7ea50d34..bf110cd8a91 100644 --- a/tests/components/plugwise/test_climate.py +++ b/tests/components/plugwise/test_climate.py @@ -150,10 +150,11 @@ async def test_anna_climate_entity_attributes( """Test creation of anna climate device environment.""" state = hass.states.get("climate.anna") assert state - assert state.state == HVACMode.HEAT + assert state.state == HVACMode.AUTO assert state.attributes["hvac_modes"] == [ HVACMode.HEAT, HVACMode.COOL, + HVACMode.AUTO, ] assert "no_frost" in state.attributes["preset_modes"] assert "home" in state.attributes["preset_modes"] @@ -199,24 +200,12 @@ async def test_anna_climate_entity_climate_changes( await hass.services.async_call( "climate", "set_hvac_mode", - {"entity_id": "climate.anna", "hvac_mode": "heat_cool"}, + {"entity_id": "climate.anna", "hvac_mode": "heat"}, blocking=True, ) assert mock_smile_anna.set_temperature.call_count == 1 assert mock_smile_anna.set_schedule_state.call_count == 1 mock_smile_anna.set_schedule_state.assert_called_with( - "c784ee9fdab44e1395b8dee7d7a497d5", None, "off" + "c784ee9fdab44e1395b8dee7d7a497d5", "standaard", "off" ) - - # Auto mode is not available, no schedules - with pytest.raises(ValueError): - await hass.services.async_call( - "climate", - "set_hvac_mode", - {"entity_id": "climate.anna", "hvac_mode": "auto"}, - blocking=True, - ) - - assert mock_smile_anna.set_temperature.call_count == 1 - assert mock_smile_anna.set_schedule_state.call_count == 1 diff --git a/tests/components/plugwise/test_diagnostics.py b/tests/components/plugwise/test_diagnostics.py index c2bb91746ae..372f410cd81 100644 --- a/tests/components/plugwise/test_diagnostics.py +++ b/tests/components/plugwise/test_diagnostics.py @@ -54,7 +54,6 @@ async def test_diagnostics( ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 0.0, "mode": "heat", "sensors": {"temperature": 16.5, "setpoint": 13.0, "battery": 67}, }, @@ -120,7 +119,6 @@ async def test_diagnostics( ], "selected_schedule": "GF7 Woonkamer", "last_used": "GF7 Woonkamer", - "schedule_temperature": 15.0, "mode": "auto", "sensors": {"temperature": 20.9, "setpoint": 21.5, "battery": 34}, }, @@ -290,7 +288,6 @@ async def test_diagnostics( ], "selected_schedule": "CV Jessie", "last_used": "CV Jessie", - "schedule_temperature": 15.0, "mode": "auto", "sensors": {"temperature": 17.2, "setpoint": 15.0, "battery": 37}, }, @@ -337,7 +334,6 @@ async def test_diagnostics( ], "selected_schedule": "Badkamer Schema", "last_used": "Badkamer Schema", - "schedule_temperature": 15.0, "mode": "auto", "sensors": {"temperature": 18.9, "setpoint": 14.0, "battery": 92}, }, @@ -380,7 +376,6 @@ async def test_diagnostics( ], "selected_schedule": "None", "last_used": "Badkamer Schema", - "schedule_temperature": 0.0, "mode": "heat", "sensors": { "temperature": 15.6, From 373ad21ce38eaba1c65e3f6458fae13d61e09c1d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 May 2022 02:00:17 -0700 Subject: [PATCH 711/930] Fix translations clean up script (#72114) --- script/translations/clean.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/script/translations/clean.py b/script/translations/clean.py index 63281b36229..0dcf40941ef 100644 --- a/script/translations/clean.py +++ b/script/translations/clean.py @@ -65,7 +65,7 @@ def find_frontend(): raise ExitApp(f"Unable to find frontend at {FRONTEND_DIR}") source = FRONTEND_DIR / "src/translations/en.json" - translated = FRONTEND_DIR / "translations/en.json" + translated = FRONTEND_DIR / "translations/frontend/en.json" missing_keys = [] find_extra( @@ -91,6 +91,8 @@ def run(): print("No missing translations!") return 0 + print(f"Found {len(missing_keys)} extra keys") + # We can't query too many keys at once, so limit the number to 50. for i in range(0, len(missing_keys), 50): chunk = missing_keys[i : i + 50] @@ -100,11 +102,13 @@ def run(): print( f"Lookin up key in Lokalise returns {len(key_data)} results, expected {len(chunk)}" ) - return 1 - print(f"Deleting {len(chunk)} keys:") - for key in chunk: - print(" -", key) + if not key_data: + continue + + print(f"Deleting {len(key_data)} keys:") + for key in key_data: + print(" -", key["key_name"]["web"]) print() while input("Type YES to delete these keys: ") != "YES": pass From ea05bd8c2ed0eb6acaea722e3a2432f5371d994d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 23 May 2022 11:58:19 +0200 Subject: [PATCH 712/930] Allow for using pip 22.1(.x) (#72348) --- .github/workflows/ci.yaml | 4 ++-- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- tox.ini | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4995864b377..fb659cf21d2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -203,7 +203,7 @@ jobs: python -m venv venv . venv/bin/activate python --version - pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.1" setuptools wheel + pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.2" setuptools wheel pip install --cache-dir=$PIP_CACHE -r requirements.txt -r requirements_test.txt --use-deprecated=legacy-resolver - name: Generate partial pre-commit restore key id: generate-pre-commit-key @@ -608,7 +608,7 @@ jobs: python -m venv venv . venv/bin/activate python --version - pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.1" setuptools wheel + pip install --cache-dir=$PIP_CACHE -U "pip>=21.0,<22.2" setuptools wheel pip install --cache-dir=$PIP_CACHE -r requirements_all.txt --use-deprecated=legacy-resolver pip install --cache-dir=$PIP_CACHE -r requirements_test.txt --use-deprecated=legacy-resolver pip install -e . diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f10cea82655..354b06d2c10 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -22,7 +22,7 @@ jinja2==3.1.2 lru-dict==1.1.7 paho-mqtt==1.6.1 pillow==9.1.0 -pip>=21.0,<22.1 +pip>=21.0,<22.2 pyserial==3.5 python-slugify==4.0.1 pyudev==0.22.0 diff --git a/requirements.txt b/requirements.txt index f2308908a30..0c13b9c319b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ ifaddr==0.1.7 jinja2==3.1.2 PyJWT==2.4.0 cryptography==36.0.2 -pip>=21.0,<22.1 +pip>=21.0,<22.2 python-slugify==4.0.1 pyyaml==6.0 requests==2.27.1 diff --git a/setup.cfg b/setup.cfg index 9d4fad881b6..e8688293683 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,7 +48,7 @@ install_requires = PyJWT==2.4.0 # PyJWT has loose dependency. We want the latest one. cryptography==36.0.2 - pip>=21.0,<22.1 + pip>=21.0,<22.2 python-slugify==4.0.1 pyyaml==6.0 requests==2.27.1 diff --git a/tox.ini b/tox.ini index 441960fbbf5..b39caacf471 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,7 @@ isolated_build = True [testenv] basepython = {env:PYTHON3_PATH:python3} # pip version duplicated in homeassistant/package_constraints.txt -pip_version = pip>=21.0,<22.1 +pip_version = pip>=21.0,<22.2 install_command = python -m pip install --use-deprecated legacy-resolver {opts} {packages} commands = {envpython} -X dev -m pytest --timeout=9 --durations=10 -n auto --dist=loadfile -qq -o console_output_style=count -p no:sugar {posargs} From 19781d285c8f046b9cf9cb35cf2b5bb0acc901a0 Mon Sep 17 00:00:00 2001 From: Thibaut Date: Mon, 23 May 2022 11:59:11 +0200 Subject: [PATCH 713/930] Add missing min and max values for some numbers in Overkiz (#72229) --- homeassistant/components/overkiz/number.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/overkiz/number.py b/homeassistant/components/overkiz/number.py index e6e64162dd8..741c666a42a 100644 --- a/homeassistant/components/overkiz/number.py +++ b/homeassistant/components/overkiz/number.py @@ -38,6 +38,8 @@ NUMBER_DESCRIPTIONS: list[OverkizNumberDescription] = [ name="My Position", icon="mdi:content-save-cog", command=OverkizCommand.SET_MEMORIZED_1_POSITION, + min_value=0, + max_value=100, entity_category=EntityCategory.CONFIG, ), # WaterHeater: Expected Number Of Shower (2 - 4) @@ -84,6 +86,8 @@ NUMBER_DESCRIPTIONS: list[OverkizNumberDescription] = [ key=OverkizState.CORE_LEVEL, icon="mdi:patio-heater", command=OverkizCommand.SET_LEVEL, + min_value=0, + max_value=100, inverted=True, ), ] From c770a81160c242f3e086fb224cf53094c7f05f64 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 23 May 2022 12:00:01 +0200 Subject: [PATCH 714/930] Use pydeconz interface controls for alarm control panel (#72317) --- .../components/deconz/alarm_control_panel.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index 2e825dafd65..e635b11972e 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -1,7 +1,7 @@ """Support for deCONZ alarm control panel devices.""" from __future__ import annotations -from pydeconz.models.alarm_system import AlarmSystem +from pydeconz.interfaces.alarm_systems import ArmAction from pydeconz.models.event import EventType from pydeconz.models.sensor.ancillary_control import ( ANCILLARY_CONTROL_ARMED_AWAY, @@ -53,13 +53,13 @@ DECONZ_TO_ALARM_STATE = { } -def get_alarm_system_for_unique_id( +def get_alarm_system_id_for_unique_id( gateway: DeconzGateway, unique_id: str -) -> AlarmSystem | None: - """Retrieve alarm system unique ID is registered to.""" +) -> str | None: + """Retrieve alarm system ID the unique ID is registered to.""" for alarm_system in gateway.api.alarmsystems.values(): if unique_id in alarm_system.devices: - return alarm_system + return alarm_system.resource_id return None @@ -76,8 +76,12 @@ async def async_setup_entry( def async_add_sensor(_: EventType, sensor_id: str) -> None: """Add alarm control panel devices from deCONZ.""" sensor = gateway.api.sensors.ancillary_control[sensor_id] - if alarm_system := get_alarm_system_for_unique_id(gateway, sensor.unique_id): - async_add_entities([DeconzAlarmControlPanel(sensor, gateway, alarm_system)]) + if alarm_system_id := get_alarm_system_id_for_unique_id( + gateway, sensor.unique_id + ): + async_add_entities( + [DeconzAlarmControlPanel(sensor, gateway, alarm_system_id)] + ) config_entry.async_on_unload( gateway.api.sensors.ancillary_control.subscribe( @@ -107,11 +111,11 @@ class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity): self, device: AncillaryControl, gateway: DeconzGateway, - alarm_system: AlarmSystem, + alarm_system_id: str, ) -> None: """Set up alarm control panel device.""" super().__init__(device, gateway) - self.alarm_system = alarm_system + self.alarm_system_id = alarm_system_id @callback def async_update_callback(self) -> None: @@ -133,19 +137,27 @@ class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity): async def async_alarm_arm_away(self, code: str | None = None) -> None: """Send arm away command.""" if code: - await self.alarm_system.arm_away(code) + await self.gateway.api.alarmsystems.arm( + self.alarm_system_id, ArmAction.AWAY, code + ) async def async_alarm_arm_home(self, code: str | None = None) -> None: """Send arm home command.""" if code: - await self.alarm_system.arm_stay(code) + await self.gateway.api.alarmsystems.arm( + self.alarm_system_id, ArmAction.STAY, code + ) async def async_alarm_arm_night(self, code: str | None = None) -> None: """Send arm night command.""" if code: - await self.alarm_system.arm_night(code) + await self.gateway.api.alarmsystems.arm( + self.alarm_system_id, ArmAction.NIGHT, code + ) async def async_alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" if code: - await self.alarm_system.disarm(code) + await self.gateway.api.alarmsystems.arm( + self.alarm_system_id, ArmAction.DISARM, code + ) From 31b53e7fc61cc0738d7432a483d01974ed6715a6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 May 2022 05:03:49 -0500 Subject: [PATCH 715/930] Remove superfluous underscore from lutron_caseta entity and device names (#72337) --- homeassistant/components/lutron_caseta/__init__.py | 14 +++++--------- homeassistant/components/lutron_caseta/scene.py | 7 +------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/lutron_caseta/__init__.py b/homeassistant/components/lutron_caseta/__init__.py index 3daa45d30ee..dd64ed4ec6f 100644 --- a/homeassistant/components/lutron_caseta/__init__.py +++ b/homeassistant/components/lutron_caseta/__init__.py @@ -197,15 +197,15 @@ def _async_register_button_devices( if "serial" not in device or device["serial"] in seen: continue seen.add(device["serial"]) + area, name = _area_and_name_from_name(device["name"]) device_args = { - "name": device["name"], + "name": f"{area} {name}", "manufacturer": MANUFACTURER, "config_entry_id": config_entry_id, "identifiers": {(DOMAIN, device["serial"])}, "model": f"{device['model']} ({device['type']})", "via_device": (DOMAIN, bridge_device["serial"]), } - area, _ = _area_and_name_from_name(device["name"]) if area != UNASSIGNED_AREA: device_args["suggested_area"] = area @@ -312,15 +312,16 @@ class LutronCasetaDevice(Entity): self._bridge_device = bridge_device if "serial" not in self._device: return + area, name = _area_and_name_from_name(device["name"]) + self._attr_name = full_name = f"{area} {name}" info = DeviceInfo( identifiers={(DOMAIN, self.serial)}, manufacturer=MANUFACTURER, model=f"{device['model']} ({device['type']})", - name=self.name, + name=full_name, via_device=(DOMAIN, self._bridge_device["serial"]), configuration_url=CONFIG_URL, ) - area, _ = _area_and_name_from_name(device["name"]) if area != UNASSIGNED_AREA: info[ATTR_SUGGESTED_AREA] = area self._attr_device_info = info @@ -334,11 +335,6 @@ class LutronCasetaDevice(Entity): """Return the device ID used for calling pylutron_caseta.""" return self._device["device_id"] - @property - def name(self): - """Return the name of the device.""" - return self._device["name"] - @property def serial(self): """Return the serial number of the device.""" diff --git a/homeassistant/components/lutron_caseta/scene.py b/homeassistant/components/lutron_caseta/scene.py index bb932c61316..d73d8011481 100644 --- a/homeassistant/components/lutron_caseta/scene.py +++ b/homeassistant/components/lutron_caseta/scene.py @@ -30,15 +30,10 @@ class LutronCasetaScene(Scene): def __init__(self, scene, bridge): """Initialize the Lutron Caseta scene.""" - self._scene_name = scene["name"] + self._attr_name = scene["name"] self._scene_id = scene["scene_id"] self._bridge = bridge - @property - def name(self): - """Return the name of the scene.""" - return self._scene_name - async def async_activate(self, **kwargs: Any) -> None: """Activate the scene.""" await self._bridge.activate_scene(self._scene_id) From 421167c548bad838b40e33ca78f473f6e675298c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 12:18:17 +0200 Subject: [PATCH 716/930] Drop GetAutomationsResult and GetAutomationCapabilitiesResult aliases (#72328) --- .../components/arcam_fmj/device_trigger.py | 7 ++----- .../components/device_automation/__init__.py | 3 --- .../components/device_automation/action.py | 11 +++-------- .../components/device_automation/condition.py | 13 ++++--------- .../components/device_automation/trigger.py | 8 +++----- .../components/homekit_controller/device_trigger.py | 7 ++----- .../components/lutron_caseta/device_trigger.py | 7 ++----- homeassistant/components/nest/device_trigger.py | 7 ++----- homeassistant/components/netatmo/device_trigger.py | 7 ++----- homeassistant/components/shelly/device_trigger.py | 9 +++------ homeassistant/components/webostv/device_trigger.py | 7 ++----- 11 files changed, 25 insertions(+), 61 deletions(-) diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index 46c51789dfb..593250e4983 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -7,10 +7,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.const import ( ATTR_ENTITY_ID, CONF_DEVICE_ID, @@ -36,7 +33,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for Arcam FMJ Receiver control devices.""" registry = entity_registry.async_get(hass) triggers = [] diff --git a/homeassistant/components/device_automation/__init__.py b/homeassistant/components/device_automation/__init__.py index e24ae15f9ff..61fd93354fe 100644 --- a/homeassistant/components/device_automation/__init__.py +++ b/homeassistant/components/device_automation/__init__.py @@ -44,9 +44,6 @@ if TYPE_CHECKING: ] # mypy: allow-untyped-calls, allow-untyped-defs -GetAutomationsResult = list[dict[str, Any]] -GetAutomationCapabilitiesResult = dict[str, vol.Schema] - DOMAIN = "device_automation" DEVICE_TRIGGER_BASE_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( diff --git a/homeassistant/components/device_automation/action.py b/homeassistant/components/device_automation/action.py index b15ca12a927..5737fbc5bf3 100644 --- a/homeassistant/components/device_automation/action.py +++ b/homeassistant/components/device_automation/action.py @@ -10,12 +10,7 @@ from homeassistant.const import CONF_DOMAIN from homeassistant.core import Context, HomeAssistant from homeassistant.helpers.typing import ConfigType -from . import ( - DeviceAutomationType, - GetAutomationCapabilitiesResult, - GetAutomationsResult, - async_get_device_automation_platform, -) +from . import DeviceAutomationType, async_get_device_automation_platform from .exceptions import InvalidDeviceAutomationConfig @@ -43,12 +38,12 @@ class DeviceAutomationActionProtocol(Protocol): def async_get_action_capabilities( self, hass: HomeAssistant, config: ConfigType - ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + ) -> dict[str, vol.Schema] | Awaitable[dict[str, vol.Schema]]: """List action capabilities.""" def async_get_actions( self, hass: HomeAssistant, device_id: str - ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + ) -> list[dict[str, Any]] | Awaitable[list[dict[str, Any]]]: """List actions.""" diff --git a/homeassistant/components/device_automation/condition.py b/homeassistant/components/device_automation/condition.py index 7ff3216c702..1f1f8e94832 100644 --- a/homeassistant/components/device_automation/condition.py +++ b/homeassistant/components/device_automation/condition.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Awaitable -from typing import TYPE_CHECKING, Protocol, cast +from typing import TYPE_CHECKING, Any, Protocol, cast import voluptuous as vol @@ -11,12 +11,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType -from . import ( - DeviceAutomationType, - GetAutomationCapabilitiesResult, - GetAutomationsResult, - async_get_device_automation_platform, -) +from . import DeviceAutomationType, async_get_device_automation_platform from .exceptions import InvalidDeviceAutomationConfig if TYPE_CHECKING: @@ -43,12 +38,12 @@ class DeviceAutomationConditionProtocol(Protocol): def async_get_condition_capabilities( self, hass: HomeAssistant, config: ConfigType - ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + ) -> dict[str, vol.Schema] | Awaitable[dict[str, vol.Schema]]: """List condition capabilities.""" def async_get_conditions( self, hass: HomeAssistant, device_id: str - ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + ) -> list[dict[str, Any]] | Awaitable[list[dict[str, Any]]]: """List conditions.""" diff --git a/homeassistant/components/device_automation/trigger.py b/homeassistant/components/device_automation/trigger.py index e4a740d6599..c5f42b3e813 100644 --- a/homeassistant/components/device_automation/trigger.py +++ b/homeassistant/components/device_automation/trigger.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Awaitable -from typing import Protocol, cast +from typing import Any, Protocol, cast import voluptuous as vol @@ -17,8 +17,6 @@ from homeassistant.helpers.typing import ConfigType from . import ( DEVICE_TRIGGER_BASE_SCHEMA, DeviceAutomationType, - GetAutomationCapabilitiesResult, - GetAutomationsResult, async_get_device_automation_platform, ) from .exceptions import InvalidDeviceAutomationConfig @@ -50,12 +48,12 @@ class DeviceAutomationTriggerProtocol(Protocol): def async_get_trigger_capabilities( self, hass: HomeAssistant, config: ConfigType - ) -> GetAutomationCapabilitiesResult | Awaitable[GetAutomationCapabilitiesResult]: + ) -> dict[str, vol.Schema] | Awaitable[dict[str, vol.Schema]]: """List trigger capabilities.""" def async_get_triggers( self, hass: HomeAssistant, device_id: str - ) -> GetAutomationsResult | Awaitable[GetAutomationsResult]: + ) -> list[dict[str, Any]] | Awaitable[list[dict[str, Any]]]: """List triggers.""" diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index 5c46aee6ca2..dcac7238c8e 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -14,10 +14,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.typing import ConfigType @@ -244,7 +241,7 @@ def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], Any]): async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for homekit devices.""" if device_id not in hass.data.get(TRIGGERS, {}): diff --git a/homeassistant/components/lutron_caseta/device_trigger.py b/homeassistant/components/lutron_caseta/device_trigger.py index b6f01a5e49e..68394667764 100644 --- a/homeassistant/components/lutron_caseta/device_trigger.py +++ b/homeassistant/components/lutron_caseta/device_trigger.py @@ -7,10 +7,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -350,7 +347,7 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for lutron caseta devices.""" triggers = [] diff --git a/homeassistant/components/nest/device_trigger.py b/homeassistant/components/nest/device_trigger.py index e2f4288d122..05769a407f2 100644 --- a/homeassistant/components/nest/device_trigger.py +++ b/homeassistant/components/nest/device_trigger.py @@ -8,10 +8,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -66,7 +63,7 @@ def async_get_device_trigger_types( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for a Nest device.""" nest_device_id = async_get_nest_device_id(hass, device_id) if not nest_device_id: diff --git a/homeassistant/components/netatmo/device_trigger.py b/homeassistant/components/netatmo/device_trigger.py index ce03aa91905..25f76307b5f 100644 --- a/homeassistant/components/netatmo/device_trigger.py +++ b/homeassistant/components/netatmo/device_trigger.py @@ -7,10 +7,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -101,7 +98,7 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for Netatmo devices.""" registry = entity_registry.async_get(hass) device_registry = dr.async_get(hass) diff --git a/homeassistant/components/shelly/device_trigger.py b/homeassistant/components/shelly/device_trigger.py index 1231f670d49..0f9fc55ed71 100644 --- a/homeassistant/components/shelly/device_trigger.py +++ b/homeassistant/components/shelly/device_trigger.py @@ -9,10 +9,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -111,9 +108,9 @@ async def async_validate_trigger_config( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for Shelly devices.""" - triggers: GetAutomationsResult = [] + triggers: list[dict[str, str]] = [] if rpc_wrapper := get_rpc_device_wrapper(hass, device_id): input_triggers = get_rpc_input_triggers(rpc_wrapper.device) diff --git a/homeassistant/components/webostv/device_trigger.py b/homeassistant/components/webostv/device_trigger.py index 9836f561b9d..9ce49bbe79e 100644 --- a/homeassistant/components/webostv/device_trigger.py +++ b/homeassistant/components/webostv/device_trigger.py @@ -7,10 +7,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) -from homeassistant.components.device_automation import ( - DEVICE_TRIGGER_BASE_SCHEMA, - GetAutomationsResult, -) +from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) @@ -61,7 +58,7 @@ async def async_validate_trigger_config( async def async_get_triggers( _hass: HomeAssistant, device_id: str -) -> GetAutomationsResult: +) -> list[dict[str, str]]: """List device triggers for device.""" triggers = [] base_trigger = { From 9ecc96e31c290f714817f4561279ec8e3cb40d4a Mon Sep 17 00:00:00 2001 From: Maikel Punie Date: Mon, 23 May 2022 12:29:21 +0200 Subject: [PATCH 717/930] Bump velbus-aio to 2022.5.1 (#72355) --- homeassistant/components/velbus/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/velbus/manifest.json b/homeassistant/components/velbus/manifest.json index ae50609baf1..f759eea0a34 100644 --- a/homeassistant/components/velbus/manifest.json +++ b/homeassistant/components/velbus/manifest.json @@ -2,7 +2,7 @@ "domain": "velbus", "name": "Velbus", "documentation": "https://www.home-assistant.io/integrations/velbus", - "requirements": ["velbus-aio==2022.2.4"], + "requirements": ["velbus-aio==2022.5.1"], "config_flow": true, "codeowners": ["@Cereal2nd", "@brefra"], "dependencies": ["usb"], diff --git a/requirements_all.txt b/requirements_all.txt index 28417beb21b..ca2028b8eaa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2384,7 +2384,7 @@ vallox-websocket-api==2.11.0 vehicle==0.4.0 # homeassistant.components.velbus -velbus-aio==2022.2.4 +velbus-aio==2022.5.1 # homeassistant.components.venstar venstarcolortouch==0.15 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index da7577fa7d3..25270998ab4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1566,7 +1566,7 @@ vallox-websocket-api==2.11.0 vehicle==0.4.0 # homeassistant.components.velbus -velbus-aio==2022.2.4 +velbus-aio==2022.5.1 # homeassistant.components.venstar venstarcolortouch==0.15 From f6fab65f60bfd09f01b838cb0588f789bbf56ea6 Mon Sep 17 00:00:00 2001 From: Steve HOLWEG Date: Mon, 23 May 2022 12:33:25 +0200 Subject: [PATCH 718/930] Add support for com.fibaro.binarySensor to fibaro (#65446) --- homeassistant/components/fibaro/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/fibaro/__init__.py b/homeassistant/components/fibaro/__init__.py index c9b0cb345e1..fdb0f894a40 100644 --- a/homeassistant/components/fibaro/__init__.py +++ b/homeassistant/components/fibaro/__init__.py @@ -78,6 +78,7 @@ FIBARO_TYPEMAP = { "com.fibaro.FGT001": Platform.CLIMATE, "com.fibaro.thermostatDanfoss": Platform.CLIMATE, "com.fibaro.doorLock": Platform.LOCK, + "com.fibaro.binarySensor": Platform.BINARY_SENSOR, } DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema( From 0c58f813c55413f72deddbd8cbb7b5c70d8796b0 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 12:55:26 +0200 Subject: [PATCH 719/930] Cleanup trigger type hint in deconz (#72358) --- homeassistant/components/deconz/device_trigger.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/deconz/device_trigger.py b/homeassistant/components/deconz/device_trigger.py index 43ed3b2cdc4..e15513bddbf 100644 --- a/homeassistant/components/deconz/device_trigger.py +++ b/homeassistant/components/deconz/device_trigger.py @@ -1,9 +1,6 @@ """Provides device automations for deconz events.""" - from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -643,8 +640,8 @@ def _get_deconz_event_from_device( async def async_validate_trigger_config( hass: HomeAssistant, - config: dict[str, Any], -) -> vol.Schema: + config: ConfigType, +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) From fad766322cae4b76eb416842fa8a8a22b4776da6 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Mon, 23 May 2022 13:48:44 +0200 Subject: [PATCH 720/930] Do not track Netgear AP or Bridge devices (#69102) --- homeassistant/components/netgear/__init__.py | 31 +++++++++++-- homeassistant/components/netgear/const.py | 3 ++ homeassistant/components/netgear/router.py | 48 +++++++++++--------- 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py index 72a56427e17..518a9051847 100644 --- a/homeassistant/components/netgear/__init__.py +++ b/homeassistant/components/netgear/__init__.py @@ -9,7 +9,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SSL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import device_registry as dr +from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import ( @@ -17,6 +17,7 @@ from .const import ( KEY_COORDINATOR, KEY_COORDINATOR_TRAFFIC, KEY_ROUTER, + MODE_ROUTER, PLATFORMS, ) from .errors import CannotLoginException @@ -69,7 +70,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_update_devices() -> bool: """Fetch data from the router.""" - return await router.async_update_device_trackers() + if router.mode == MODE_ROUTER: + return await router.async_update_device_trackers() + return False async def async_update_traffic_meter() -> dict[str, Any] | None: """Fetch data from the router.""" @@ -91,7 +94,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: update_interval=SCAN_INTERVAL, ) - await coordinator.async_config_entry_first_refresh() + if router.mode == MODE_ROUTER: + await coordinator.async_config_entry_first_refresh() await coordinator_traffic_meter.async_config_entry_first_refresh() hass.data[DOMAIN][entry.entry_id] = { @@ -109,11 +113,32 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] + if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) if not hass.data[DOMAIN]: hass.data.pop(DOMAIN) + if router.mode != MODE_ROUTER: + router_id = None + # Remove devices that are no longer tracked + device_registry = dr.async_get(hass) + devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id) + for device_entry in devices: + if device_entry.via_device_id is None: + router_id = device_entry.id + continue # do not remove the router itself + device_registry.async_update_device( + device_entry.id, remove_config_entry_id=entry.entry_id + ) + # Remove entities that are no longer tracked + entity_registry = er.async_get(hass) + entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id) + for entity_entry in entries: + if entity_entry.device_id is not router_id: + entity_registry.async_remove(entity_entry.entity_id) + return unload_ok diff --git a/homeassistant/components/netgear/const.py b/homeassistant/components/netgear/const.py index f2e0263a4e4..bc4f37114fd 100644 --- a/homeassistant/components/netgear/const.py +++ b/homeassistant/components/netgear/const.py @@ -16,6 +16,9 @@ KEY_COORDINATOR_TRAFFIC = "coordinator_traffic" DEFAULT_CONSIDER_HOME = timedelta(seconds=180) DEFAULT_NAME = "Netgear router" +MODE_ROUTER = "0" +MODE_AP = "1" + # models using port 80 instead of 5000 MODELS_PORT_80 = [ "Orbi", diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index f543ba8a5f3..6fb44e569d6 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -32,6 +32,7 @@ from .const import ( DEFAULT_CONSIDER_HOME, DEFAULT_NAME, DOMAIN, + MODE_ROUTER, MODELS_V2, ) from .errors import CannotLoginException @@ -73,6 +74,7 @@ class NetgearRouter: self._info = None self.model = "" + self.mode = MODE_ROUTER self.device_name = "" self.firmware_version = "" self.hardware_version = "" @@ -108,12 +110,13 @@ class NetgearRouter: self.firmware_version = self._info.get("Firmwareversion") self.hardware_version = self._info.get("Hardwareversion") self.serial_number = self._info["SerialNumber"] + self.mode = self._info.get("DeviceMode", MODE_ROUTER) for model in MODELS_V2: if self.model.startswith(model): self.method_version = 2 - if self.method_version == 2: + if self.method_version == 2 and self.mode == MODE_ROUTER: if not self._api.get_attached_devices_2(): _LOGGER.error( "Netgear Model '%s' in MODELS_V2 list, but failed to get attached devices using V2", @@ -130,28 +133,29 @@ class NetgearRouter: return False # set already known devices to away instead of unavailable - device_registry = dr.async_get(self.hass) - devices = dr.async_entries_for_config_entry(device_registry, self.entry_id) - for device_entry in devices: - if device_entry.via_device_id is None: - continue # do not add the router itself + if self.mode == MODE_ROUTER: + device_registry = dr.async_get(self.hass) + devices = dr.async_entries_for_config_entry(device_registry, self.entry_id) + for device_entry in devices: + if device_entry.via_device_id is None: + continue # do not add the router itself - device_mac = dict(device_entry.connections)[dr.CONNECTION_NETWORK_MAC] - self.devices[device_mac] = { - "mac": device_mac, - "name": device_entry.name, - "active": False, - "last_seen": dt_util.utcnow() - timedelta(days=365), - "device_model": None, - "device_type": None, - "type": None, - "link_rate": None, - "signal": None, - "ip": None, - "ssid": None, - "conn_ap_mac": None, - "allow_or_block": None, - } + device_mac = dict(device_entry.connections)[dr.CONNECTION_NETWORK_MAC] + self.devices[device_mac] = { + "mac": device_mac, + "name": device_entry.name, + "active": False, + "last_seen": dt_util.utcnow() - timedelta(days=365), + "device_model": None, + "device_type": None, + "type": None, + "link_rate": None, + "signal": None, + "ip": None, + "ssid": None, + "conn_ap_mac": None, + "allow_or_block": None, + } return True From 071f6d7099d1e755f0800b4145d042a47b64f7d2 Mon Sep 17 00:00:00 2001 From: mkmer Date: Mon, 23 May 2022 08:13:21 -0400 Subject: [PATCH 721/930] Aladdin connect unload cleanup (#71948) --- .../components/aladdin_connect/__init__.py | 15 ++-- .../components/aladdin_connect/config_flow.py | 14 +-- .../components/aladdin_connect/cover.py | 11 +-- .../aladdin_connect/test_config_flow.py | 90 +------------------ .../components/aladdin_connect/test_cover.py | 82 +++++++---------- tests/components/aladdin_connect/test_init.py | 30 +------ 6 files changed, 51 insertions(+), 191 deletions(-) diff --git a/homeassistant/components/aladdin_connect/__init__.py b/homeassistant/components/aladdin_connect/__init__.py index cbd4a195a3a..048624641bd 100644 --- a/homeassistant/components/aladdin_connect/__init__.py +++ b/homeassistant/components/aladdin_connect/__init__.py @@ -7,7 +7,7 @@ from aladdin_connect import AladdinConnectClient from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed from .const import DOMAIN @@ -21,12 +21,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] acc = AladdinConnectClient(username, password) - try: - if not await hass.async_add_executor_job(acc.login): - raise ConfigEntryAuthFailed("Incorrect Password") - except (TypeError, KeyError, NameError, ValueError) as ex: - _LOGGER.error("%s", ex) - raise ConfigEntryNotReady from ex + if not await hass.async_add_executor_job(acc.login): + raise ConfigEntryAuthFailed("Incorrect Password") hass.data.setdefault(DOMAIN, {})[entry.entry_id] = acc hass.config_entries.async_setup_platforms(entry, PLATFORMS) @@ -35,4 +31,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/aladdin_connect/config_flow.py b/homeassistant/components/aladdin_connect/config_flow.py index f912d36a3f0..153a63ffb06 100644 --- a/homeassistant/components/aladdin_connect/config_flow.py +++ b/homeassistant/components/aladdin_connect/config_flow.py @@ -33,13 +33,9 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. """ acc = AladdinConnectClient(data[CONF_USERNAME], data[CONF_PASSWORD]) - try: - login = await hass.async_add_executor_job(acc.login) - except (TypeError, KeyError, NameError, ValueError) as ex: - raise ConnectionError from ex - else: - if not login: - raise InvalidAuth + login = await hass.async_add_executor_job(acc.login) + if not login: + raise InvalidAuth class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @@ -72,8 +68,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: await validate_input(self.hass, data) - except ConnectionError: - errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" else: @@ -107,8 +101,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): try: await validate_input(self.hass, user_input) - except ConnectionError: - errors["base"] = "cannot_connect" except InvalidAuth: errors["base"] = "invalid_auth" diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 9e18abe21f6..da3e6b81663 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -21,7 +21,7 @@ from homeassistant.const import ( STATE_OPENING, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -63,13 +63,10 @@ async def async_setup_entry( ) -> None: """Set up the Aladdin Connect platform.""" acc = hass.data[DOMAIN][config_entry.entry_id] - try: - doors = await hass.async_add_executor_job(acc.get_doors) - - except (TypeError, KeyError, NameError, ValueError) as ex: - _LOGGER.error("%s", ex) - raise ConfigEntryNotReady from ex + doors = await hass.async_add_executor_job(acc.get_doors) + if doors is None: + raise PlatformNotReady("Error from Aladdin Connect getting doors") async_add_entities( (AladdinDevice(acc, door) for door in doors), update_before_add=True, diff --git a/tests/components/aladdin_connect/test_config_flow.py b/tests/components/aladdin_connect/test_config_flow.py index 37ec64ba6f0..899aa0a7e55 100644 --- a/tests/components/aladdin_connect/test_config_flow.py +++ b/tests/components/aladdin_connect/test_config_flow.py @@ -2,7 +2,6 @@ from unittest.mock import patch from homeassistant import config_entries -from homeassistant.components.aladdin_connect.config_flow import InvalidAuth from homeassistant.components.aladdin_connect.const import DOMAIN from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant @@ -48,15 +47,15 @@ async def test_form(hass: HomeAssistant) -> None: assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_invalid_auth(hass: HomeAssistant) -> None: - """Test we handle invalid auth.""" +async def test_form_failed_auth(hass: HomeAssistant) -> None: + """Test we handle failed authentication error.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", - side_effect=InvalidAuth, + return_value=False, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -69,43 +68,6 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None: assert result2["type"] == RESULT_TYPE_FORM assert result2["errors"] == {"base": "invalid_auth"} - -async def test_form_cannot_connect(hass: HomeAssistant) -> None: - """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", - side_effect=ConnectionError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_USERNAME: "test-username", - CONF_PASSWORD: "test-password", - }, - ) - - assert result2["type"] == RESULT_TYPE_FORM - assert result2["errors"] == {"base": "cannot_connect"} - - with patch( - "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", - side_effect=TypeError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_USERNAME: "test-username", - CONF_PASSWORD: "test-password", - }, - ) - - assert result2["type"] == RESULT_TYPE_FORM - assert result2["errors"] == {"base": "cannot_connect"} - with patch( "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", return_value=False, @@ -236,7 +198,7 @@ async def test_reauth_flow(hass: HomeAssistant) -> None: async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None: - """Test a successful reauth flow.""" + """Test an authorization error reauth flow.""" mock_entry = MockConfigEntry( domain=DOMAIN, @@ -277,47 +239,3 @@ async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None: assert result2["type"] == RESULT_TYPE_FORM assert result2["errors"] == {"base": "invalid_auth"} - - -async def test_reauth_flow_other_error(hass: HomeAssistant) -> None: - """Test an unsuccessful reauth flow.""" - - mock_entry = MockConfigEntry( - domain=DOMAIN, - data={"username": "test-username", "password": "test-password"}, - unique_id="test-username", - ) - mock_entry.add_to_hass(hass) - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={ - "source": config_entries.SOURCE_REAUTH, - "unique_id": mock_entry.unique_id, - "entry_id": mock_entry.entry_id, - }, - data={"username": "test-username", "password": "new-password"}, - ) - - assert result["step_id"] == "reauth_confirm" - assert result["type"] == RESULT_TYPE_FORM - assert result["errors"] == {} - - with patch( - "homeassistant.components.aladdin_connect.cover.async_setup_platform", - return_value=True, - ), patch( - "homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login", - side_effect=ValueError, - ), patch( - "homeassistant.components.aladdin_connect.cover.async_setup_entry", - return_value=True, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_PASSWORD: "new-password"}, - ) - await hass.async_block_till_done() - - assert result2["type"] == RESULT_TYPE_FORM - assert result2["errors"] == {"base": "cannot_connect"} diff --git a/tests/components/aladdin_connect/test_cover.py b/tests/components/aladdin_connect/test_cover.py index 4904d904ad4..c1571ed9fa2 100644 --- a/tests/components/aladdin_connect/test_cover.py +++ b/tests/components/aladdin_connect/test_cover.py @@ -4,7 +4,6 @@ from unittest.mock import patch import pytest from homeassistant.components.aladdin_connect.const import DOMAIN -import homeassistant.components.aladdin_connect.cover as cover from homeassistant.components.cover import DOMAIN as COVER_DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( @@ -77,18 +76,7 @@ DEVICE_CONFIG_BAD_NO_DOOR = { } -@pytest.mark.parametrize( - "side_effect", - [ - (TypeError), - (KeyError), - (NameError), - (ValueError), - ], -) -async def test_setup_get_doors_errors( - hass: HomeAssistant, side_effect: Exception -) -> None: +async def test_setup_get_doors_errors(hass: HomeAssistant) -> None: """Test component setup Get Doors Errors.""" config_entry = MockConfigEntry( domain=DOMAIN, @@ -101,23 +89,14 @@ async def test_setup_get_doors_errors( return_value=True, ), patch( "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", - side_effect=side_effect, + return_value=None, ): assert await hass.config_entries.async_setup(config_entry.entry_id) is True await hass.async_block_till_done() assert len(hass.states.async_all()) == 0 -@pytest.mark.parametrize( - "side_effect", - [ - (TypeError), - (KeyError), - (NameError), - (ValueError), - ], -) -async def test_setup_login_error(hass: HomeAssistant, side_effect: Exception) -> None: +async def test_setup_login_error(hass: HomeAssistant) -> None: """Test component setup Login Errors.""" config_entry = MockConfigEntry( domain=DOMAIN, @@ -127,11 +106,9 @@ async def test_setup_login_error(hass: HomeAssistant, side_effect: Exception) -> config_entry.add_to_hass(hass) with patch( "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", - side_effect=side_effect, + return_value=False, ): assert await hass.config_entries.async_setup(config_entry.entry_id) is False - await hass.async_block_till_done() - assert len(hass.states.async_all()) == 0 async def test_setup_component_noerror(hass: HomeAssistant) -> None: @@ -183,36 +160,27 @@ async def test_cover_operation(hass: HomeAssistant) -> None: with patch( "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.open_door", return_value=True, - ): - await hass.services.async_call( - "cover", "open_cover", {"entity_id": "cover.home"}, blocking=True - ) - - with patch( - "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.close_door", - return_value=True, - ): - await hass.services.async_call( - "cover", "close_cover", {"entity_id": "cover.home"}, blocking=True - ) - with patch( - "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", - return_value=[DEVICE_CONFIG_CLOSED], - ): - await hass.services.async_call( - "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True - ) - assert hass.states.get("cover.home").state == STATE_CLOSED - - with patch( + ), patch( "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", return_value=[DEVICE_CONFIG_OPEN], ): await hass.services.async_call( - "homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True + "cover", "open_cover", {"entity_id": "cover.home"}, blocking=True ) assert hass.states.get("cover.home").state == STATE_OPEN + with patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.close_door", + return_value=True, + ), patch( + "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", + return_value=[DEVICE_CONFIG_CLOSED], + ): + await hass.services.async_call( + "cover", "close_cover", {"entity_id": "cover.home"}, blocking=True + ) + assert hass.states.get("cover.home").state == STATE_CLOSED + with patch( "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", return_value=[DEVICE_CONFIG_OPENING], @@ -261,9 +229,19 @@ async def test_yaml_import(hass: HomeAssistant, caplog: pytest.LogCaptureFixture "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors", return_value=[DEVICE_CONFIG_CLOSED], ): - await cover.async_setup_platform(hass, YAML_CONFIG, None) + await async_setup_component( + hass, + COVER_DOMAIN, + { + COVER_DOMAIN: { + "platform": DOMAIN, + "username": "test-user", + "password": "test-password", + } + }, + ) await hass.async_block_till_done() - + assert hass.config_entries.async_entries(DOMAIN) assert "Configuring Aladdin Connect through yaml is deprecated" in caplog.text assert hass.config_entries.async_entries(DOMAIN) diff --git a/tests/components/aladdin_connect/test_init.py b/tests/components/aladdin_connect/test_init.py index c9814adb051..0ba9b317dfb 100644 --- a/tests/components/aladdin_connect/test_init.py +++ b/tests/components/aladdin_connect/test_init.py @@ -4,39 +4,14 @@ from unittest.mock import patch from homeassistant.components.aladdin_connect.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry YAML_CONFIG = {"username": "test-user", "password": "test-password"} -async def test_unload_entry(hass: HomeAssistant): - """Test successful unload of entry.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={"username": "test-user", "password": "test-password"}, - ) - entry.add_to_hass(hass) - - with patch( - "homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login", - return_value=True, - ): - - assert (await async_setup_component(hass, DOMAIN, entry)) is True - - assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert entry.state is ConfigEntryState.LOADED - - assert await hass.config_entries.async_unload(entry.entry_id) - await hass.async_block_till_done() - - assert entry.state is ConfigEntryState.NOT_LOADED - - async def test_entry_password_fail(hass: HomeAssistant): - """Test successful unload of entry.""" + """Test password fail during entry.""" entry = MockConfigEntry( domain=DOMAIN, data={"username": "test-user", "password": "test-password"}, @@ -48,7 +23,8 @@ async def test_entry_password_fail(hass: HomeAssistant): return_value=False, ): - assert (await async_setup_component(hass, DOMAIN, entry)) is True + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() assert entry.state is ConfigEntryState.SETUP_ERROR From 6ec755a79d4597116967cf7de1fdf41b6c91498e Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 23 May 2022 14:28:56 +0200 Subject: [PATCH 722/930] Update board file list to reflect currently available boards (#72085) * Update board file list to reflect currently available boards * Warn if board does not exist and migrate Intel NUC to Generic x86-64 * Remove integration in case a old board is referenced * Don't remove the config entry automatically/fix logging message * Address pylint issue --- homeassistant/components/version/__init__.py | 17 ++++++++++++++++- homeassistant/components/version/const.py | 6 +++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/version/__init__.py b/homeassistant/components/version/__init__.py index 75545adb8db..22dbad36d7b 100644 --- a/homeassistant/components/version/__init__.py +++ b/homeassistant/components/version/__init__.py @@ -1,6 +1,8 @@ """The Version integration.""" from __future__ import annotations +import logging + from pyhaversion import HaVersion from homeassistant.config_entries import ConfigEntry @@ -18,16 +20,29 @@ from .const import ( ) from .coordinator import VersionDataUpdateCoordinator +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up the version integration from a config entry.""" + + board = entry.data[CONF_BOARD] + + if board not in BOARD_MAP: + _LOGGER.error( + 'Board "%s" is (no longer) valid. Please remove the integration "%s"', + board, + entry.title, + ) + return False + coordinator = VersionDataUpdateCoordinator( hass=hass, api=HaVersion( session=async_get_clientsession(hass), source=entry.data[CONF_SOURCE], image=entry.data[CONF_IMAGE], - board=BOARD_MAP[entry.data[CONF_BOARD]], + board=BOARD_MAP[board], channel=entry.data[CONF_CHANNEL].lower(), ), ) diff --git a/homeassistant/components/version/const.py b/homeassistant/components/version/const.py index 419e49d7240..1693f79ec64 100644 --- a/homeassistant/components/version/const.py +++ b/homeassistant/components/version/const.py @@ -61,8 +61,6 @@ HA_VERSION_SOURCES: Final[list[str]] = [source.value for source in HaVersionSour BOARD_MAP: Final[dict[str, str]] = { "OVA": "ova", - "RaspberryPi": "rpi", - "RaspberryPi Zero-W": "rpi0-w", "RaspberryPi 2": "rpi2", "RaspberryPi 3": "rpi3", "RaspberryPi 3 64bit": "rpi3-64", @@ -73,8 +71,10 @@ BOARD_MAP: Final[dict[str, str]] = { "ODROID C4": "odroid-c4", "ODROID N2": "odroid-n2", "ODROID XU4": "odroid-xu4", + "Generic AArch64": "generic-aarch64", "Generic x86-64": "generic-x86-64", - "Intel NUC": "intel-nuc", + "Home Assistant Yellow": "yellow", + "Khadas VIM3": "khadas-vim3", } VALID_BOARDS: Final[list[str]] = list(BOARD_MAP) From dfc8dee2d65aace19ec16932d7ac740ca354030c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:32:47 +0200 Subject: [PATCH 723/930] Adjust device_automation type hints in rfxtrx (#72138) --- .../components/rfxtrx/device_action.py | 18 +++++++++++++----- .../components/rfxtrx/device_trigger.py | 10 +++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/rfxtrx/device_action.py b/homeassistant/components/rfxtrx/device_action.py index 37fb39cb499..e8fc8c6707d 100644 --- a/homeassistant/components/rfxtrx/device_action.py +++ b/homeassistant/components/rfxtrx/device_action.py @@ -9,6 +9,7 @@ from homeassistant.components.device_automation.exceptions import ( from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_TYPE from homeassistant.core import Context, HomeAssistant import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DATA_RFXOBJECT, DOMAIN from .helpers import async_get_device_object @@ -37,7 +38,9 @@ ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend( ) -async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: +async def async_get_actions( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: """List device actions for RFXCOM RFXtrx devices.""" try: @@ -48,8 +51,8 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: actions = [] for action_type in ACTION_TYPES: if hasattr(device, action_type): - values = getattr(device, ACTION_SELECTION[action_type], {}) - for value in values.values(): + data: dict[int, str] = getattr(device, ACTION_SELECTION[action_type], {}) + for value in data.values(): actions.append( { CONF_DEVICE_ID: device_id, @@ -69,7 +72,9 @@ def _get_commands(hass, device_id, action_type): return commands, send_fun -async def async_validate_action_config(hass, config): +async def async_validate_action_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" config = ACTION_SCHEMA(config) commands, _ = _get_commands(hass, config[CONF_DEVICE_ID], config[CONF_TYPE]) @@ -84,7 +89,10 @@ async def async_validate_action_config(hass, config): async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" config = ACTION_SCHEMA(config) diff --git a/homeassistant/components/rfxtrx/device_trigger.py b/homeassistant/components/rfxtrx/device_trigger.py index 9ab10ca7f2b..196377dd60f 100644 --- a/homeassistant/components/rfxtrx/device_trigger.py +++ b/homeassistant/components/rfxtrx/device_trigger.py @@ -47,13 +47,15 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( ) -async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]: +async def async_get_triggers( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: """List device triggers for RFXCOM RFXtrx devices.""" device = async_get_device_object(hass, device_id) triggers = [] for conf_type in TRIGGER_TYPES: - data = getattr(device, TRIGGER_SELECTION[conf_type], {}) + data: dict[int, str] = getattr(device, TRIGGER_SELECTION[conf_type], {}) for command in data.values(): triggers.append( { @@ -67,7 +69,9 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]: return triggers -async def async_validate_trigger_config(hass, config): +async def async_validate_trigger_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) From df3e3b52a0ca221f029a880fb5bfa0786c3b72b9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:34:55 +0200 Subject: [PATCH 724/930] Adjust device_automation type hints in lcn (#72132) --- homeassistant/components/lcn/device_trigger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lcn/device_trigger.py b/homeassistant/components/lcn/device_trigger.py index 35575a442b2..a6fc17759b8 100644 --- a/homeassistant/components/lcn/device_trigger.py +++ b/homeassistant/components/lcn/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for LCN.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -53,7 +51,7 @@ TYPE_SCHEMAS = { async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for LCN devices.""" device_registry = dr.async_get(hass) if (device := device_registry.async_get(device_id)) is None: @@ -101,6 +99,8 @@ async def async_attach_trigger( ) -async def async_get_trigger_capabilities(hass: HomeAssistant, config: dict) -> dict: +async def async_get_trigger_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List trigger capabilities.""" return TYPE_SCHEMAS.get(config[CONF_TYPE], {}) From 5bb39a1db576f99832764e94b99519dfd37faade Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:37:44 +0200 Subject: [PATCH 725/930] Adjust device_automation type hints in zwave_js (#72143) --- homeassistant/components/zwave_js/device_action.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index a67bd44e533..f001accd196 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -27,7 +27,7 @@ from homeassistant.core import Context, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .config_validation import VALUE_SCHEMA from .const import ( @@ -141,7 +141,9 @@ ACTION_SCHEMA = vol.Any( ) -async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: +async def async_get_actions( + hass: HomeAssistant, device_id: str +) -> list[dict[str, Any]]: """List device actions for Z-Wave JS devices.""" registry = entity_registry.async_get(hass) actions = [] @@ -238,7 +240,10 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" action_type = service = config[CONF_TYPE] From 9763b44357380b10cf7c36001cd1b77fc6aeb1cf Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Mon, 23 May 2022 14:38:40 +0200 Subject: [PATCH 726/930] Remove uneeded patch statements in here_travel_time (#72361) --- .../here_travel_time/test_sensor.py | 103 ++++++++---------- 1 file changed, 48 insertions(+), 55 deletions(-) diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 585d5adc9d3..70470aee9d3 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -53,7 +53,6 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -import homeassistant.util.dt as dt_util from .const import ( API_KEY, @@ -264,62 +263,59 @@ async def test_no_attribution(hass: HomeAssistant): async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): """Test that origin/destination supplied by entities works.""" - utcnow = dt_util.utcnow() - # Patching 'utcnow' to gain more control over the timed update. - with patch("homeassistant.util.dt.utcnow", return_value=utcnow): - zone_config = { - "zone": [ - { - "name": "Origin", - "latitude": CAR_ORIGIN_LATITUDE, - "longitude": CAR_ORIGIN_LONGITUDE, - "radius": 250, - "passive": False, - }, - ] - } - assert await async_setup_component(hass, "zone", zone_config) - hass.states.async_set( - "device_tracker.test", - "not_home", + zone_config = { + "zone": [ { - "latitude": float(CAR_DESTINATION_LATITUDE), - "longitude": float(CAR_DESTINATION_LONGITUDE), + "name": "Origin", + "latitude": CAR_ORIGIN_LATITUDE, + "longitude": CAR_ORIGIN_LONGITUDE, + "radius": 250, + "passive": False, }, - ) - entry = MockConfigEntry( - domain=DOMAIN, - unique_id="0123456789", - data={ - CONF_ORIGIN_ENTITY_ID: "zone.origin", - CONF_DESTINATION_ENTITY_ID: "device_tracker.test", - CONF_API_KEY: API_KEY, - CONF_MODE: TRAVEL_MODE_TRUCK, - CONF_NAME: "test", - }, - ) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + ] + } + assert await async_setup_component(hass, "zone", zone_config) + hass.states.async_set( + "device_tracker.test", + "not_home", + { + "latitude": float(CAR_DESTINATION_LATITUDE), + "longitude": float(CAR_DESTINATION_LONGITUDE), + }, + ) + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_ENTITY_ID: "zone.origin", + CONF_DESTINATION_ENTITY_ID: "device_tracker.test", + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_TRUCK, + CONF_NAME: "test", + }, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() - sensor = hass.states.get("sensor.test") - assert sensor.attributes.get(ATTR_DISTANCE) == 23.903 + sensor = hass.states.get("sensor.test") + assert sensor.attributes.get(ATTR_DISTANCE) == 23.903 - valid_response.assert_called_with( - [CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE], - [CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE], - True, - [ - RouteMode[ROUTE_MODE_FASTEST], - RouteMode[TRAVEL_MODE_TRUCK], - RouteMode[TRAFFIC_MODE_ENABLED], - ], - arrival=None, - departure="now", - ) + valid_response.assert_called_with( + [CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE], + [CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE], + True, + [ + RouteMode[ROUTE_MODE_FASTEST], + RouteMode[TRAVEL_MODE_TRUCK], + RouteMode[TRAFFIC_MODE_ENABLED], + ], + arrival=None, + departure="now", + ) @pytest.mark.usefixtures("valid_response") @@ -433,9 +429,6 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): async def test_route_not_found(hass: HomeAssistant, caplog): """Test that route not found error is correctly handled.""" with patch( - "homeassistant.components.here_travel_time.config_flow.validate_api_key", - return_value=None, - ), patch( "herepy.RoutingApi.public_transport_timetable", side_effect=NoRouteFoundError, ): From dc76cce96b8af4e8e02b3c9169e23d7da7bb4821 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 23 May 2022 05:44:45 -0700 Subject: [PATCH 727/930] Add SENZ application credentials platform (#72338) --- homeassistant/components/senz/__init__.py | 42 ++++++++++++------- .../senz/application_credentials.py | 14 +++++++ homeassistant/components/senz/manifest.json | 2 +- .../generated/application_credentials.py | 1 + tests/components/senz/test_config_flow.py | 1 - 5 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 homeassistant/components/senz/application_credentials.py diff --git a/homeassistant/components/senz/__init__.py b/homeassistant/components/senz/__init__.py index b6fc2422888..08aa26fa3c5 100644 --- a/homeassistant/components/senz/__init__.py +++ b/homeassistant/components/senz/__init__.py @@ -4,10 +4,14 @@ from __future__ import annotations from datetime import timedelta import logging -from aiosenz import AUTHORIZATION_ENDPOINT, SENZAPI, TOKEN_ENDPOINT, Thermostat +from aiosenz import SENZAPI, Thermostat from httpx import RequestError import voluptuous as vol +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform from homeassistant.core import HomeAssistant @@ -20,7 +24,6 @@ from homeassistant.helpers import ( from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from . import config_flow from .api import SENZConfigEntryAuth from .const import DOMAIN @@ -29,14 +32,17 @@ UPDATE_INTERVAL = timedelta(seconds=30) _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -52,17 +58,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in config: return True - config_flow.OAuth2FlowHandler.async_register_implementation( + await async_import_client_credential( hass, - config_entry_oauth2_flow.LocalOAuth2Implementation( - hass, - DOMAIN, + DOMAIN, + ClientCredential( config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], - AUTHORIZATION_ENDPOINT, - TOKEN_ENDPOINT, ), ) + _LOGGER.warning( + "Configuration of SENZ integration in YAML is deprecated " + "and will be removed in a future release; Your existing OAuth " + "Application Credentials have been imported into the UI " + "automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/senz/application_credentials.py b/homeassistant/components/senz/application_credentials.py new file mode 100644 index 00000000000..205f00ff33f --- /dev/null +++ b/homeassistant/components/senz/application_credentials.py @@ -0,0 +1,14 @@ +"""Application credentials platform for senz.""" + +from aiosenz import AUTHORIZATION_ENDPOINT, TOKEN_ENDPOINT + +from homeassistant.components.application_credentials import AuthorizationServer +from homeassistant.core import HomeAssistant + + +async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer: + """Return authorization server.""" + return AuthorizationServer( + authorize_url=AUTHORIZATION_ENDPOINT, + token_url=TOKEN_ENDPOINT, + ) diff --git a/homeassistant/components/senz/manifest.json b/homeassistant/components/senz/manifest.json index e9b6165cb26..937a20d8482 100644 --- a/homeassistant/components/senz/manifest.json +++ b/homeassistant/components/senz/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/senz", "requirements": ["aiosenz==1.0.0"], - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "codeowners": ["@milanmeu"], "iot_class": "cloud_polling" } diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index d7b3259a158..63c19fe10b8 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -11,6 +11,7 @@ APPLICATION_CREDENTIALS = [ "home_connect", "neato", "netatmo", + "senz", "spotify", "withings", "xbox", diff --git a/tests/components/senz/test_config_flow.py b/tests/components/senz/test_config_flow.py index 5143acea60a..3906f2c0320 100644 --- a/tests/components/senz/test_config_flow.py +++ b/tests/components/senz/test_config_flow.py @@ -24,7 +24,6 @@ async def test_full_flow( "senz", { "senz": {"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET}, - "http": {"base_url": "https://example.com"}, }, ) From 52686aae05bc2667863f592808767baaec300c90 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:53:12 +0200 Subject: [PATCH 728/930] Adjust device_automation type hints in nanoleaf (#72134) --- homeassistant/components/nanoleaf/device_trigger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/nanoleaf/device_trigger.py b/homeassistant/components/nanoleaf/device_trigger.py index 311d12506ba..68dc6326719 100644 --- a/homeassistant/components/nanoleaf/device_trigger.py +++ b/homeassistant/components/nanoleaf/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for Nanoleaf.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -38,7 +36,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Nanoleaf devices.""" device_registry = dr.async_get(hass) device_entry = device_registry.async_get(device_id) From bcc3c93b4ec96582fcd3c7d51ac51cd7207b64ef Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:56:17 +0200 Subject: [PATCH 729/930] Adjust device_automation type hints in wemo (#72141) --- homeassistant/components/wemo/device_trigger.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/wemo/device_trigger.py b/homeassistant/components/wemo/device_trigger.py index 1cdc29fa995..973a3358b1b 100644 --- a/homeassistant/components/wemo/device_trigger.py +++ b/homeassistant/components/wemo/device_trigger.py @@ -1,8 +1,6 @@ """Triggers for WeMo devices.""" from __future__ import annotations -from typing import Any - from pywemo.subscribe import EVENT_TYPE_LONG_PRESS import voluptuous as vol @@ -30,7 +28,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """Return a list of triggers.""" wemo_trigger = { From caa24121032f901896ff9da355f1b9e060368b50 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:57:09 +0200 Subject: [PATCH 730/930] Adjust device_automation type hints in philips_js (#72137) --- .../components/philips_js/device_trigger.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/philips_js/device_trigger.py b/homeassistant/components/philips_js/device_trigger.py index a48f9e58331..69a5932576f 100644 --- a/homeassistant/components/philips_js/device_trigger.py +++ b/homeassistant/components/philips_js/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for control of device.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -12,6 +10,7 @@ from homeassistant.components.automation import ( from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE from homeassistant.core import CALLBACK_TYPE, HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr from homeassistant.helpers.typing import ConfigType @@ -30,7 +29,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for device.""" triggers = [] triggers.append( @@ -50,18 +49,18 @@ async def async_attach_trigger( config: ConfigType, action: AutomationActionType, automation_info: AutomationTriggerInfo, -) -> CALLBACK_TYPE | None: +) -> CALLBACK_TYPE: """Attach a trigger.""" trigger_data = automation_info["trigger_data"] registry: dr.DeviceRegistry = dr.async_get(hass) - if config[CONF_TYPE] == TRIGGER_TYPE_TURN_ON: + if (trigger_type := config[CONF_TYPE]) == TRIGGER_TYPE_TURN_ON: variables = { "trigger": { **trigger_data, "platform": "device", "domain": DOMAIN, "device_id": config[CONF_DEVICE_ID], - "description": f"philips_js '{config[CONF_TYPE]}' event", + "description": f"philips_js '{trigger_type}' event", } } @@ -73,4 +72,4 @@ async def async_attach_trigger( if coordinator: return coordinator.turn_on.async_attach(action, variables) - return None + raise HomeAssistantError(f"Unhandled trigger type {trigger_type}") From ee5f1b15776e7c3008de8139f477ded63aee72e9 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 14:59:12 +0200 Subject: [PATCH 731/930] Adjust device_automation type hints in hue (#72144) --- .../components/hue/device_trigger.py | 18 +++++++---- .../components/hue/v1/device_trigger.py | 32 ++++++++++++++----- .../components/hue/v2/device_trigger.py | 12 ++++--- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/hue/device_trigger.py b/homeassistant/components/hue/device_trigger.py index 29df1fd2476..e8eb7695ed9 100644 --- a/homeassistant/components/hue/device_trigger.py +++ b/homeassistant/components/hue/device_trigger.py @@ -1,7 +1,7 @@ """Provides device automations for Philips Hue events.""" from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, @@ -33,7 +33,9 @@ if TYPE_CHECKING: from .bridge import HueBridge -async def async_validate_trigger_config(hass: "HomeAssistant", config: ConfigType): +async def async_validate_trigger_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" if DOMAIN not in hass.data: # happens at startup @@ -51,13 +53,14 @@ async def async_validate_trigger_config(hass: "HomeAssistant", config: ConfigTyp if bridge.api_version == 1: return await async_validate_trigger_config_v1(bridge, device_entry, config) return await async_validate_trigger_config_v2(bridge, device_entry, config) + return config async def async_attach_trigger( - hass: "HomeAssistant", + hass: HomeAssistant, config: ConfigType, - action: "AutomationActionType", - automation_info: "AutomationTriggerInfo", + action: AutomationActionType, + automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" device_id = config[CONF_DEVICE_ID] @@ -82,7 +85,9 @@ async def async_attach_trigger( ) -async def async_get_triggers(hass: "HomeAssistant", device_id: str): +async def async_get_triggers( + hass: HomeAssistant, device_id: str +) -> list[dict[str, Any]]: """Get device triggers for given (hass) device id.""" if DOMAIN not in hass.data: return [] @@ -101,3 +106,4 @@ async def async_get_triggers(hass: "HomeAssistant", device_id: str): if bridge.api_version == 1: return async_get_triggers_v1(bridge, device_entry) return async_get_triggers_v2(bridge, device_entry) + return [] diff --git a/homeassistant/components/hue/v1/device_trigger.py b/homeassistant/components/hue/v1/device_trigger.py index 7b58bf42089..579f4b71efb 100644 --- a/homeassistant/components/hue/v1/device_trigger.py +++ b/homeassistant/components/hue/v1/device_trigger.py @@ -3,6 +3,10 @@ from typing import TYPE_CHECKING import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, @@ -16,8 +20,9 @@ from homeassistant.const import ( CONF_TYPE, CONF_UNIQUE_ID, ) -from homeassistant.core import callback +from homeassistant.core import CALLBACK_TYPE, callback from homeassistant.helpers.device_registry import DeviceEntry +from homeassistant.helpers.typing import ConfigType from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN @@ -95,7 +100,7 @@ HUE_FOHSWITCH_REMOTE = { } -REMOTES = { +REMOTES: dict[str, dict[tuple[str, str], dict[str, int]]] = { HUE_DIMMER_REMOTE_MODEL: HUE_DIMMER_REMOTE, HUE_TAP_REMOTE_MODEL: HUE_TAP_REMOTE, HUE_BUTTON_REMOTE_MODEL: HUE_BUTTON_REMOTE, @@ -114,7 +119,9 @@ def _get_hue_event_from_device_id(hass, device_id): return None -async def async_validate_trigger_config(bridge, device_entry, config): +async def async_validate_trigger_config( + bridge: "HueBridge", device_entry: DeviceEntry, config: ConfigType +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) @@ -137,7 +144,13 @@ async def async_validate_trigger_config(bridge, device_entry, config): return config -async def async_attach_trigger(bridge, device_entry, config, action, automation_info): +async def async_attach_trigger( + bridge: "HueBridge", + device_entry: DeviceEntry, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" hass = bridge.hass @@ -145,9 +158,10 @@ async def async_attach_trigger(bridge, device_entry, config, action, automation_ if hue_event is None: raise InvalidDeviceAutomationConfig - trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) + trigger_key: tuple[str, str] = (config[CONF_TYPE], config[CONF_SUBTYPE]) - trigger = REMOTES[device_entry.model][trigger] + assert device_entry.model + trigger = REMOTES[device_entry.model][trigger_key] event_config = { event_trigger.CONF_PLATFORM: "event", @@ -162,7 +176,9 @@ async def async_attach_trigger(bridge, device_entry, config, action, automation_ @callback -def async_get_triggers(bridge: "HueBridge", device: DeviceEntry): +def async_get_triggers( + bridge: "HueBridge", device: DeviceEntry +) -> list[dict[str, str]]: """Return device triggers for device on `v1` bridge. Make sure device is a supported remote model. @@ -170,7 +186,7 @@ def async_get_triggers(bridge: "HueBridge", device: DeviceEntry): Generate device trigger list. """ if device.model not in REMOTES: - return + return [] triggers = [] for trigger, subtype in REMOTES[device.model]: diff --git a/homeassistant/components/hue/v2/device_trigger.py b/homeassistant/components/hue/v2/device_trigger.py index 8c5da8febb9..2b868a4685c 100644 --- a/homeassistant/components/hue/v2/device_trigger.py +++ b/homeassistant/components/hue/v2/device_trigger.py @@ -1,7 +1,7 @@ """Provides device automations for Philips Hue events.""" from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from aiohue.v2.models.button import ButtonEvent from aiohue.v2.models.resource import ResourceTypes @@ -86,7 +86,7 @@ async def async_validate_trigger_config( bridge: "HueBridge", device_entry: DeviceEntry, config: ConfigType, -): +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) check_invalid_device_trigger(bridge, config, device_entry) @@ -97,8 +97,8 @@ async def async_attach_trigger( bridge: "HueBridge", device_entry: DeviceEntry, config: ConfigType, - action: "AutomationActionType", - automation_info: "AutomationTriggerInfo", + action: AutomationActionType, + automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" hass = bridge.hass @@ -120,7 +120,9 @@ async def async_attach_trigger( @callback -def async_get_triggers(bridge: HueBridge, device_entry: DeviceEntry): +def async_get_triggers( + bridge: HueBridge, device_entry: DeviceEntry +) -> list[dict[str, Any]]: """Return device triggers for device on `v2` bridge.""" api: HueBridgeV2 = bridge.api From 5137e6b18d9df386649fdcb635662217dcf03567 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 15:00:24 +0200 Subject: [PATCH 732/930] Adjust device_automation type hints in tasmota (#72201) --- homeassistant/components/tasmota/device_trigger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/tasmota/device_trigger.py b/homeassistant/components/tasmota/device_trigger.py index 512fa5dd547..26b1d9debf1 100644 --- a/homeassistant/components/tasmota/device_trigger.py +++ b/homeassistant/components/tasmota/device_trigger.py @@ -291,7 +291,7 @@ async def async_get_triggers( async def async_attach_trigger( hass: HomeAssistant, config: ConfigType, - action: Callable, + action: AutomationActionType, automation_info: AutomationTriggerInfo, ) -> CALLBACK_TYPE: """Attach a device trigger.""" From 82d4d96672c3c3a568bf0eed84b45a05eb2f9c6f Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 15:38:55 +0200 Subject: [PATCH 733/930] Adjust config_flow type hints in amberelectric (#72236) --- .../components/amberelectric/config_flow.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/amberelectric/config_flow.py b/homeassistant/components/amberelectric/config_flow.py index efb5ddfb931..0258fdf4cb4 100644 --- a/homeassistant/components/amberelectric/config_flow.py +++ b/homeassistant/components/amberelectric/config_flow.py @@ -1,8 +1,6 @@ """Config flow for the Amber Electric integration.""" from __future__ import annotations -from typing import Any - import amberelectric from amberelectric.api import amber_api from amberelectric.model.site import Site @@ -10,6 +8,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_API_TOKEN +from homeassistant.data_entry_flow import FlowResult from .const import CONF_SITE_ID, CONF_SITE_NAME, CONF_SITE_NMI, DOMAIN @@ -44,7 +43,9 @@ class AmberElectricConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._errors[CONF_API_TOKEN] = "unknown_error" return None - async def async_step_user(self, user_input: dict[str, Any] | None = None): + async def async_step_user( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Step when user initializes a integration.""" self._errors = {} self._sites = None @@ -76,11 +77,14 @@ class AmberElectricConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=self._errors, ) - async def async_step_site(self, user_input: dict[str, Any] = None): + async def async_step_site( + self, user_input: dict[str, str] | None = None + ) -> FlowResult: """Step to select site.""" self._errors = {} assert self._sites is not None + assert self._api_token is not None api_token = self._api_token if user_input is not None: From cc7a0e3c245d7296bf82233ae5a9d6828836390b Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 23 May 2022 15:41:56 +0200 Subject: [PATCH 734/930] Streamline setup of deCONZ sensor platform (#71905) --- homeassistant/components/deconz/gateway.py | 47 +------ homeassistant/components/deconz/sensor.py | 133 +++++++------------- homeassistant/components/deconz/services.py | 3 - 3 files changed, 49 insertions(+), 134 deletions(-) diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index d59c5e2d160..1f6a45f3ad6 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -9,12 +9,7 @@ from typing import TYPE_CHECKING, Any, cast import async_timeout from pydeconz import DeconzSession, errors -from pydeconz.models import ResourceGroup -from pydeconz.models.alarm_system import AlarmSystem as DeconzAlarmSystem from pydeconz.models.event import EventType -from pydeconz.models.group import Group as DeconzGroup -from pydeconz.models.light import LightBase as DeconzLight -from pydeconz.models.sensor import SensorBase as DeconzSensor from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT @@ -57,7 +52,6 @@ class DeconzGateway: self.config_entry = config_entry self.api = api - api.add_device_callback = self.async_add_device_callback api.connection_status_callback = self.async_connection_status_callback self.available = True @@ -67,14 +61,6 @@ class DeconzGateway: self.signal_reload_groups = f"deconz_reload_group_{config_entry.entry_id}" self.signal_reload_clip_sensors = f"deconz_reload_clip_{config_entry.entry_id}" - self.signal_new_light = f"deconz_new_light_{config_entry.entry_id}" - self.signal_new_sensor = f"deconz_new_sensor_{config_entry.entry_id}" - - self.deconz_resource_type_to_signal_new_device = { - ResourceGroup.LIGHT.value: self.signal_new_light, - ResourceGroup.SENSOR.value: self.signal_new_sensor, - } - self.deconz_ids: dict[str, str] = {} self.entities: dict[str, set[str]] = {} self.events: list[DeconzAlarmEvent | DeconzEvent] = [] @@ -153,37 +139,6 @@ class DeconzGateway: self.ignore_state_updates = False async_dispatcher_send(self.hass, self.signal_reachable) - @callback - def async_add_device_callback( - self, - resource_type: str, - device: DeconzAlarmSystem - | DeconzGroup - | DeconzLight - | DeconzSensor - | list[DeconzAlarmSystem | DeconzGroup | DeconzLight | DeconzSensor] - | None = None, - force: bool = False, - ) -> None: - """Handle event of new device creation in deCONZ.""" - if ( - not force - and not self.option_allow_new_devices - or resource_type not in self.deconz_resource_type_to_signal_new_device - ): - return - - args = [] - - if device is not None and not isinstance(device, list): - args.append([device]) - - async_dispatcher_send( - self.hass, - self.deconz_resource_type_to_signal_new_device[resource_type], - *args, # Don't send device if None, it would override default value in listeners - ) - async def async_update_device_registry(self) -> None: """Update device registry.""" if self.api.config.mac is None: @@ -237,7 +192,6 @@ class DeconzGateway: deconz_ids = [] if self.option_allow_clip_sensor: - self.async_add_device_callback(ResourceGroup.SENSOR.value) async_dispatcher_send(self.hass, self.signal_reload_clip_sensors) else: @@ -314,6 +268,7 @@ async def get_deconz_session( config[CONF_HOST], config[CONF_PORT], config[CONF_API_KEY], + legacy_add_device=False, ) try: async with async_timeout.timeout(10): diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index bf29e8db478..021dcf7168a 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -6,6 +6,7 @@ from dataclasses import dataclass from datetime import datetime from pydeconz.interfaces.sensors import SensorResources +from pydeconz.models.event import EventType from pydeconz.models.sensor.air_quality import AirQuality from pydeconz.models.sensor.consumption import Consumption from pydeconz.models.sensor.daylight import Daylight @@ -38,10 +39,7 @@ from homeassistant.const import ( TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers.dispatcher import ( - async_dispatcher_connect, - async_dispatcher_send, -) +from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -244,61 +242,52 @@ async def async_setup_entry( gateway = get_gateway_from_config_entry(hass, config_entry) gateway.entities[DOMAIN] = set() - battery_handler = DeconzBatteryHandler(gateway) - @callback - def async_add_sensor(sensors: list[SensorResources] | None = None) -> None: - """Add sensors from deCONZ. + def async_add_sensor(_: EventType, sensor_id: str) -> None: + """Add sensor from deCONZ.""" + sensor = gateway.api.sensors[sensor_id] - Create DeconzBattery if sensor has a battery attribute. - Create DeconzSensor if not a battery, switch or thermostat and not a binary sensor. - """ - entities: list[DeconzSensor] = [] + if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + return - if sensors is None: - sensors = gateway.api.sensors.values() + if sensor.battery is None: + DeconzBatteryTracker(sensor_id, gateway, async_add_entities) - for sensor in sensors: - - if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): + for description in ( + ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS + ): + if ( + not hasattr(sensor, description.key) + or description.value_fn(sensor) is None + ): continue - if sensor.battery is None: - battery_handler.create_tracker(sensor) + async_add_entities([DeconzSensor(sensor, gateway, description)]) - known_entities = set(gateway.entities[DOMAIN]) - for description in ( - ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS - ): + config_entry.async_on_unload( + gateway.api.sensors.subscribe( + gateway.evaluate_add_device(async_add_sensor), + EventType.ADDED, + ) + ) + for sensor_id in gateway.api.sensors: + async_add_sensor(EventType.ADDED, sensor_id) - if ( - not hasattr(sensor, description.key) - or description.value_fn(sensor) is None - ): - continue - - new_entity = DeconzSensor(sensor, gateway, description) - if new_entity.unique_id not in known_entities: - entities.append(new_entity) - - if description.key == "battery": - battery_handler.remove_tracker(sensor) - - if entities: - async_add_entities(entities) + @callback + def async_reload_clip_sensors() -> None: + """Load clip sensor sensors from deCONZ.""" + for sensor_id, sensor in gateway.api.sensors.items(): + if sensor.type.startswith("CLIP"): + async_add_sensor(EventType.ADDED, sensor_id) config_entry.async_on_unload( async_dispatcher_connect( hass, - gateway.signal_new_sensor, - async_add_sensor, + gateway.signal_reload_clip_sensors, + async_reload_clip_sensors, ) ) - async_add_sensor( - [gateway.api.sensors[key] for key in sorted(gateway.api.sensors, key=int)] - ) - class DeconzSensor(DeconzDevice, SensorEntity): """Representation of a deCONZ sensor.""" @@ -398,52 +387,26 @@ class DeconzSensor(DeconzDevice, SensorEntity): return attr -class DeconzSensorStateTracker: - """Track sensors without a battery state and signal when battery state exist.""" +class DeconzBatteryTracker: + """Track sensors without a battery state and add entity when battery state exist.""" - def __init__(self, sensor: SensorResources, gateway: DeconzGateway) -> None: + def __init__( + self, + sensor_id: str, + gateway: DeconzGateway, + async_add_entities: AddEntitiesCallback, + ) -> None: """Set up tracker.""" - self.sensor = sensor + self.sensor = gateway.api.sensors[sensor_id] self.gateway = gateway - sensor.register_callback(self.async_update_callback) - - @callback - def close(self) -> None: - """Clean up tracker.""" - self.sensor.remove_callback(self.async_update_callback) + self.async_add_entities = async_add_entities + self.unsub = self.sensor.subscribe(self.async_update_callback) @callback def async_update_callback(self) -> None: - """Sensor state updated.""" + """Update the device's state.""" if "battery" in self.sensor.changed_keys: - async_dispatcher_send( - self.gateway.hass, - self.gateway.signal_new_sensor, - [self.sensor], + self.unsub() + self.async_add_entities( + [DeconzSensor(self.sensor, self.gateway, SENSOR_DESCRIPTIONS[0])] ) - - -class DeconzBatteryHandler: - """Creates and stores trackers for sensors without a battery state.""" - - def __init__(self, gateway: DeconzGateway) -> None: - """Set up battery handler.""" - self.gateway = gateway - self._trackers: set[DeconzSensorStateTracker] = set() - - @callback - def create_tracker(self, sensor: SensorResources) -> None: - """Create new tracker for battery state.""" - for tracker in self._trackers: - if sensor == tracker.sensor: - return - self._trackers.add(DeconzSensorStateTracker(sensor, self.gateway)) - - @callback - def remove_tracker(self, sensor: SensorResources) -> None: - """Remove tracker of battery state.""" - for tracker in self._trackers: - if sensor == tracker.sensor: - tracker.close() - self._trackers.remove(tracker) - break diff --git a/homeassistant/components/deconz/services.py b/homeassistant/components/deconz/services.py index a9a5172d72e..e4399e53524 100644 --- a/homeassistant/components/deconz/services.py +++ b/homeassistant/components/deconz/services.py @@ -147,9 +147,6 @@ async def async_refresh_devices_service(gateway: DeconzGateway) -> None: gateway.load_ignored_devices() gateway.ignore_state_updates = False - for resource_type in gateway.deconz_resource_type_to_signal_new_device: - gateway.async_add_device_callback(resource_type, force=True) - async def async_remove_orphaned_entries_service(gateway: DeconzGateway) -> None: """Remove orphaned deCONZ entries from device and entity registries.""" From 30bf727dfe28c9b1e80ffc68231808678b800411 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 15:56:13 +0200 Subject: [PATCH 735/930] Adjust device_automation type hints in litejet (#72195) --- homeassistant/components/litejet/trigger.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/litejet/trigger.py b/homeassistant/components/litejet/trigger.py index 21b7927ebe2..5aff5dbc66c 100644 --- a/homeassistant/components/litejet/trigger.py +++ b/homeassistant/components/litejet/trigger.py @@ -1,12 +1,19 @@ """Trigger an automation when a LiteJet switch is released.""" +from __future__ import annotations + from collections.abc import Callable import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_PLATFORM -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_point_in_utc_time +from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util from .const import DOMAIN @@ -29,14 +36,19 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for events based on configuration.""" trigger_data = automation_info["trigger_data"] number = config.get(CONF_NUMBER) held_more_than = config.get(CONF_HELD_MORE_THAN) held_less_than = config.get(CONF_HELD_LESS_THAN) pressed_time = None - cancel_pressed_more_than: Callable = None + cancel_pressed_more_than: Callable | None = None job = HassJob(action) @callback From 571c90b8cf5218b19389777e076c3e946bb72be7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 15:59:15 +0200 Subject: [PATCH 736/930] Adjust pylint plugin for climate HVACMode (#71727) Co-authored-by: J. Nick Koston --- pylint/plugins/hass_imports.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index afbd930ec30..c6f6c25c7b6 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -62,6 +62,10 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { ), ], "homeassistant.components.climate": [ + ObsoleteImportMatch( + reason="replaced by HVACMode enum", + constant=re.compile(r"^HVAC_MODE_(\w*)$"), + ), ObsoleteImportMatch( reason="replaced by ClimateEntityFeature enum", constant=re.compile(r"^SUPPORT_(\w*)$"), @@ -72,6 +76,10 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { reason="replaced by HVACAction enum", constant=re.compile(r"^CURRENT_HVAC_(\w*)$"), ), + ObsoleteImportMatch( + reason="replaced by HVACMode enum", + constant=re.compile(r"^HVAC_MODE_(\w*)$"), + ), ObsoleteImportMatch( reason="replaced by ClimateEntityFeature enum", constant=re.compile(r"^SUPPORT_(\w*)$"), From b10ee779f935028ca45b580c0e2347d6c5b4887e Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 16:01:40 +0200 Subject: [PATCH 737/930] Adjust device_automation type hints in core platforms 3/3 (#72211) --- .../components/remote/device_action.py | 2 +- .../components/remote/device_trigger.py | 4 +--- .../components/select/device_action.py | 7 ++++-- .../components/select/device_trigger.py | 4 +--- .../components/sensor/device_condition.py | 4 +++- .../components/sensor/device_trigger.py | 23 +++++++++++++++---- .../components/switch/device_action.py | 2 +- .../components/switch/device_trigger.py | 4 +--- .../components/update/device_trigger.py | 4 +--- .../components/vacuum/device_action.py | 6 ++++- .../components/vacuum/device_trigger.py | 4 +--- .../components/water_heater/device_action.py | 6 ++++- 12 files changed, 44 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/remote/device_action.py b/homeassistant/components/remote/device_action.py index a337f3275eb..09c540b5e01 100644 --- a/homeassistant/components/remote/device_action.py +++ b/homeassistant/components/remote/device_action.py @@ -19,7 +19,7 @@ async def async_call_action_from_config( hass: HomeAssistant, config: ConfigType, variables: TemplateVarsType, - context: Context, + context: Context | None, ) -> None: """Change state based on configuration.""" await toggle_entity.async_call_action_from_config( diff --git a/homeassistant/components/remote/device_trigger.py b/homeassistant/components/remote/device_trigger.py index c358d86b176..127f07827e2 100644 --- a/homeassistant/components/remote/device_trigger.py +++ b/homeassistant/components/remote/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for remotes.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -36,7 +34,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" return await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/select/device_action.py b/homeassistant/components/select/device_action.py index f55a12da62a..1212b9dea6b 100644 --- a/homeassistant/components/select/device_action.py +++ b/homeassistant/components/select/device_action.py @@ -15,7 +15,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_capability -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .const import ATTR_OPTION, ATTR_OPTIONS, CONF_OPTION, DOMAIN, SERVICE_SELECT_OPTION @@ -48,7 +48,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" await hass.services.async_call( diff --git a/homeassistant/components/select/device_trigger.py b/homeassistant/components/select/device_trigger.py index 6d0378946e8..574acfc6893 100644 --- a/homeassistant/components/select/device_trigger.py +++ b/homeassistant/components/select/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for Select.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -47,7 +45,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Select devices.""" registry = entity_registry.async_get(hass) return [ diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index a77105764d5..808d6367cdf 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -195,7 +195,9 @@ def async_condition_from_config( return condition.async_numeric_state_from_config(numeric_state_config) -async def async_get_condition_capabilities(hass, config): +async def async_get_condition_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List condition capabilities.""" try: unit_of_measurement = get_unit_of_measurement(hass, config[CONF_ENTITY_ID]) diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index d760b92b31c..741c0281e0d 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -1,6 +1,10 @@ """Provides device triggers for sensors.""" import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, @@ -15,6 +19,7 @@ from homeassistant.const import ( CONF_FOR, CONF_TYPE, ) +from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity import ( @@ -22,6 +27,7 @@ from homeassistant.helpers.entity import ( get_device_class, get_unit_of_measurement, ) +from homeassistant.helpers.typing import ConfigType from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass @@ -134,7 +140,12 @@ TRIGGER_SCHEMA = vol.All( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" numeric_state_config = { numeric_state_trigger.CONF_PLATFORM: "numeric_state", @@ -155,9 +166,11 @@ async def async_attach_trigger(hass, config, action, automation_info): ) -async def async_get_triggers(hass, device_id): +async def async_get_triggers( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: """List device triggers.""" - triggers = [] + triggers: list[dict[str, str]] = [] entity_registry = er.async_get(hass) entries = [ @@ -192,7 +205,9 @@ async def async_get_triggers(hass, device_id): return triggers -async def async_get_trigger_capabilities(hass, config): +async def async_get_trigger_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List trigger capabilities.""" try: unit_of_measurement = get_unit_of_measurement(hass, config[CONF_ENTITY_ID]) diff --git a/homeassistant/components/switch/device_action.py b/homeassistant/components/switch/device_action.py index 6947656406b..1aed2fa2467 100644 --- a/homeassistant/components/switch/device_action.py +++ b/homeassistant/components/switch/device_action.py @@ -19,7 +19,7 @@ async def async_call_action_from_config( hass: HomeAssistant, config: ConfigType, variables: TemplateVarsType, - context: Context, + context: Context | None, ) -> None: """Change state based on configuration.""" await toggle_entity.async_call_action_from_config( diff --git a/homeassistant/components/switch/device_trigger.py b/homeassistant/components/switch/device_trigger.py index 533ee8bd54d..9f56d7a09d2 100644 --- a/homeassistant/components/switch/device_trigger.py +++ b/homeassistant/components/switch/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for switches.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -36,7 +34,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" return await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/update/device_trigger.py b/homeassistant/components/update/device_trigger.py index 690e67cce56..ac8113d5708 100644 --- a/homeassistant/components/update/device_trigger.py +++ b/homeassistant/components/update/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for update entities.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -36,7 +34,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" return await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/vacuum/device_action.py b/homeassistant/components/vacuum/device_action.py index e8dac646153..e8fe53b08ae 100644 --- a/homeassistant/components/vacuum/device_action.py +++ b/homeassistant/components/vacuum/device_action.py @@ -13,6 +13,7 @@ from homeassistant.const import ( from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN, SERVICE_RETURN_TO_BASE, SERVICE_START @@ -51,7 +52,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" config = ACTION_SCHEMA(config) diff --git a/homeassistant/components/vacuum/device_trigger.py b/homeassistant/components/vacuum/device_trigger.py index 502f9f01410..4b8ec2fc08d 100644 --- a/homeassistant/components/vacuum/device_trigger.py +++ b/homeassistant/components/vacuum/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Vacuum.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -38,7 +36,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Vacuum devices.""" registry = entity_registry.async_get(hass) triggers = [] diff --git a/homeassistant/components/water_heater/device_action.py b/homeassistant/components/water_heater/device_action.py index f200498c350..6bc7e1ca635 100644 --- a/homeassistant/components/water_heater/device_action.py +++ b/homeassistant/components/water_heater/device_action.py @@ -15,6 +15,7 @@ from homeassistant.const import ( from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN @@ -52,7 +53,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} From 3a0e816f1b8173fafc5b5afdcc09fb430284bcd6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 16:02:36 +0200 Subject: [PATCH 738/930] Adjust device_automation type hints in core platforms 2/3 (#72210) --- homeassistant/components/fan/device_action.py | 6 +++++- homeassistant/components/fan/device_trigger.py | 4 +--- homeassistant/components/humidifier/device_action.py | 11 ++++++----- .../components/humidifier/device_condition.py | 4 +++- homeassistant/components/humidifier/device_trigger.py | 4 +--- homeassistant/components/light/device_action.py | 2 +- homeassistant/components/light/device_trigger.py | 4 +--- homeassistant/components/lock/device_action.py | 6 +++++- homeassistant/components/lock/device_trigger.py | 4 +--- .../components/media_player/device_trigger.py | 4 +--- homeassistant/components/mobile_app/device_action.py | 10 ++++++++-- homeassistant/components/number/device_action.py | 7 +++++-- 12 files changed, 38 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/fan/device_action.py b/homeassistant/components/fan/device_action.py index fd38c69c3da..55bd862349b 100644 --- a/homeassistant/components/fan/device_action.py +++ b/homeassistant/components/fan/device_action.py @@ -6,6 +6,7 @@ import voluptuous as vol from homeassistant.components.device_automation import toggle_entity from homeassistant.const import CONF_DOMAIN from homeassistant.core import Context, HomeAssistant +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN @@ -20,7 +21,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" await toggle_entity.async_call_action_from_config( diff --git a/homeassistant/components/fan/device_trigger.py b/homeassistant/components/fan/device_trigger.py index 1d81ef18f4d..35eb5a9f50c 100644 --- a/homeassistant/components/fan/device_trigger.py +++ b/homeassistant/components/fan/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Fan.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -24,7 +22,7 @@ TRIGGER_SCHEMA = vol.All( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Fan devices.""" return await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/humidifier/device_action.py b/homeassistant/components/humidifier/device_action.py index 03f0bb1fea1..773caa72f95 100644 --- a/homeassistant/components/humidifier/device_action.py +++ b/homeassistant/components/humidifier/device_action.py @@ -1,8 +1,6 @@ """Provides device actions for Humidifier.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.device_automation import toggle_entity @@ -19,6 +17,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_capability, get_supported_features +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN, const @@ -74,8 +73,8 @@ async def async_get_actions( async def async_call_action_from_config( hass: HomeAssistant, - config: dict[str, Any], - variables: dict[str, Any], + config: ConfigType, + variables: TemplateVarsType, context: Context | None, ) -> None: """Execute a device action.""" @@ -97,7 +96,9 @@ async def async_call_action_from_config( ) -async def async_get_action_capabilities(hass, config): +async def async_get_action_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List action capabilities.""" action_type = config[CONF_TYPE] diff --git a/homeassistant/components/humidifier/device_condition.py b/homeassistant/components/humidifier/device_condition.py index b8e8d2a13ae..949b25fdd15 100644 --- a/homeassistant/components/humidifier/device_condition.py +++ b/homeassistant/components/humidifier/device_condition.py @@ -85,7 +85,9 @@ def async_condition_from_config( return test_is_state -async def async_get_condition_capabilities(hass, config): +async def async_get_condition_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List condition capabilities.""" condition_type = config[CONF_TYPE] diff --git a/homeassistant/components/humidifier/device_trigger.py b/homeassistant/components/humidifier/device_trigger.py index a2d15097335..e8f3a0ac446 100644 --- a/homeassistant/components/humidifier/device_trigger.py +++ b/homeassistant/components/humidifier/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Climate.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -59,7 +57,7 @@ TRIGGER_SCHEMA = vol.All( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Humidifier devices.""" registry = entity_registry.async_get(hass) triggers = await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/light/device_action.py b/homeassistant/components/light/device_action.py index 285e34ebe08..2583bfa972f 100644 --- a/homeassistant/components/light/device_action.py +++ b/homeassistant/components/light/device_action.py @@ -55,7 +55,7 @@ async def async_call_action_from_config( hass: HomeAssistant, config: ConfigType, variables: TemplateVarsType, - context: Context, + context: Context | None, ) -> None: """Change state based on configuration.""" if ( diff --git a/homeassistant/components/light/device_trigger.py b/homeassistant/components/light/device_trigger.py index 5b4083f41a6..6f5f5fd7f52 100644 --- a/homeassistant/components/light/device_trigger.py +++ b/homeassistant/components/light/device_trigger.py @@ -1,8 +1,6 @@ """Provides device trigger for lights.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -36,7 +34,7 @@ async def async_attach_trigger( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" return await toggle_entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/lock/device_action.py b/homeassistant/components/lock/device_action.py index 038c2f1c0da..3ff8d10c7a2 100644 --- a/homeassistant/components/lock/device_action.py +++ b/homeassistant/components/lock/device_action.py @@ -17,6 +17,7 @@ from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_supported_features +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN, LockEntityFeature @@ -61,7 +62,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} diff --git a/homeassistant/components/lock/device_trigger.py b/homeassistant/components/lock/device_trigger.py index d875cadb446..b55a2ac254b 100644 --- a/homeassistant/components/lock/device_trigger.py +++ b/homeassistant/components/lock/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Lock.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -43,7 +41,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Lock devices.""" registry = entity_registry.async_get(hass) triggers = [] diff --git a/homeassistant/components/media_player/device_trigger.py b/homeassistant/components/media_player/device_trigger.py index 9644e5e0010..e0c88489841 100644 --- a/homeassistant/components/media_player/device_trigger.py +++ b/homeassistant/components/media_player/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Media player.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -55,7 +53,7 @@ TRIGGER_SCHEMA = vol.All( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Media player entities.""" registry = entity_registry.async_get(hass) triggers = await entity.async_get_triggers(hass, device_id, DOMAIN) diff --git a/homeassistant/components/mobile_app/device_action.py b/homeassistant/components/mobile_app/device_action.py index d17702ec24f..f6e870fc7d6 100644 --- a/homeassistant/components/mobile_app/device_action.py +++ b/homeassistant/components/mobile_app/device_action.py @@ -9,6 +9,7 @@ from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_TYPE from homeassistant.core import Context, HomeAssistant from homeassistant.exceptions import TemplateError from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .const import DOMAIN from .util import get_notify_service, supports_push, webhook_id_from_device_id @@ -36,7 +37,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" webhook_id = webhook_id_from_device_id(hass, config[CONF_DEVICE_ID]) @@ -73,7 +77,9 @@ async def async_call_action_from_config( ) -async def async_get_action_capabilities(hass, config): +async def async_get_action_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List action capabilities.""" if config[CONF_TYPE] != "notify": return {} diff --git a/homeassistant/components/number/device_action.py b/homeassistant/components/number/device_action.py index 3d449ffc213..e4311f50dd2 100644 --- a/homeassistant/components/number/device_action.py +++ b/homeassistant/components/number/device_action.py @@ -13,7 +13,7 @@ from homeassistant.const import ( from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .const import ATTR_VALUE, DOMAIN, SERVICE_SET_VALUE @@ -53,7 +53,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" await hass.services.async_call( From fb53e39f05831ef796e133de59354b5a97021655 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 16:03:21 +0200 Subject: [PATCH 739/930] Adjust device_automation type hints in core platforms 1/3 (#72209) --- .../alarm_control_panel/device_action.py | 7 ++++-- .../alarm_control_panel/device_trigger.py | 4 ++-- .../binary_sensor/device_trigger.py | 23 +++++++++++++++---- .../components/button/device_action.py | 6 ++++- .../components/button/device_trigger.py | 4 +--- .../components/climate/device_action.py | 10 ++++++-- .../components/climate/device_condition.py | 4 +++- .../components/climate/device_trigger.py | 4 +--- .../components/cover/device_action.py | 7 ++++-- .../components/cover/device_trigger.py | 4 +--- .../device_tracker/device_trigger.py | 4 ++-- 11 files changed, 52 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/device_action.py b/homeassistant/components/alarm_control_panel/device_action.py index ff14b851df8..dd0c3d03a43 100644 --- a/homeassistant/components/alarm_control_panel/device_action.py +++ b/homeassistant/components/alarm_control_panel/device_action.py @@ -24,7 +24,7 @@ from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_supported_features -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import ATTR_CODE_ARM_REQUIRED, DOMAIN from .const import ( @@ -90,7 +90,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: ConfigType, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} diff --git a/homeassistant/components/alarm_control_panel/device_trigger.py b/homeassistant/components/alarm_control_panel/device_trigger.py index 18508661034..840b5eba6f3 100644 --- a/homeassistant/components/alarm_control_panel/device_trigger.py +++ b/homeassistant/components/alarm_control_panel/device_trigger.py @@ -1,7 +1,7 @@ """Provides device automations for Alarm control panel.""" from __future__ import annotations -from typing import Any, Final +from typing import Final import voluptuous as vol @@ -58,7 +58,7 @@ TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Alarm control panel devices.""" registry = entity_registry.async_get(hass) triggers: list[dict[str, str]] = [] diff --git a/homeassistant/components/binary_sensor/device_trigger.py b/homeassistant/components/binary_sensor/device_trigger.py index 5b20b991403..12b620b8c4f 100644 --- a/homeassistant/components/binary_sensor/device_trigger.py +++ b/homeassistant/components/binary_sensor/device_trigger.py @@ -1,6 +1,10 @@ """Provides device triggers for binary sensors.""" import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.const import ( CONF_TURNED_OFF, @@ -8,8 +12,10 @@ from homeassistant.components.device_automation.const import ( ) from homeassistant.components.homeassistant.triggers import state as state_trigger from homeassistant.const import CONF_ENTITY_ID, CONF_FOR, CONF_TYPE +from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.entity import get_device_class +from homeassistant.helpers.typing import ConfigType from . import DOMAIN, BinarySensorDeviceClass @@ -254,7 +260,12 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_type = config[CONF_TYPE] if trigger_type in TURNED_ON: @@ -276,9 +287,11 @@ async def async_attach_trigger(hass, config, action, automation_info): ) -async def async_get_triggers(hass, device_id): +async def async_get_triggers( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: """List device triggers.""" - triggers = [] + triggers: list[dict[str, str]] = [] entity_registry = er.async_get(hass) entries = [ @@ -308,7 +321,9 @@ async def async_get_triggers(hass, device_id): return triggers -async def async_get_trigger_capabilities(hass, config): +async def async_get_trigger_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List trigger capabilities.""" return { "extra_fields": vol.Schema( diff --git a/homeassistant/components/button/device_action.py b/homeassistant/components/button/device_action.py index 2dffd9c600f..70033729692 100644 --- a/homeassistant/components/button/device_action.py +++ b/homeassistant/components/button/device_action.py @@ -13,6 +13,7 @@ from homeassistant.const import ( from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from .const import DOMAIN, SERVICE_PRESS @@ -44,7 +45,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" await hass.services.async_call( diff --git a/homeassistant/components/button/device_trigger.py b/homeassistant/components/button/device_trigger.py index ccad50dec05..1418039b2e8 100644 --- a/homeassistant/components/button/device_trigger.py +++ b/homeassistant/components/button/device_trigger.py @@ -1,8 +1,6 @@ """Provides device triggers for Button.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -39,7 +37,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for button devices.""" registry = entity_registry.async_get(hass) return [ diff --git a/homeassistant/components/climate/device_action.py b/homeassistant/components/climate/device_action.py index 45160eed8eb..3c9934d5cbf 100644 --- a/homeassistant/components/climate/device_action.py +++ b/homeassistant/components/climate/device_action.py @@ -15,6 +15,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_capability, get_supported_features +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import DOMAIN, const @@ -67,7 +68,10 @@ async def async_get_actions( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} @@ -84,7 +88,9 @@ async def async_call_action_from_config( ) -async def async_get_action_capabilities(hass, config): +async def async_get_action_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List action capabilities.""" action_type = config[CONF_TYPE] diff --git a/homeassistant/components/climate/device_condition.py b/homeassistant/components/climate/device_condition.py index 56bac123d28..c6179d82215 100644 --- a/homeassistant/components/climate/device_condition.py +++ b/homeassistant/components/climate/device_condition.py @@ -92,7 +92,9 @@ def async_condition_from_config( return test_is_state -async def async_get_condition_capabilities(hass, config): +async def async_get_condition_capabilities( + hass: HomeAssistant, config: ConfigType +) -> dict[str, vol.Schema]: """List condition capabilities.""" condition_type = config[CONF_TYPE] diff --git a/homeassistant/components/climate/device_trigger.py b/homeassistant/components/climate/device_trigger.py index e40cf32f2a9..d8d46342603 100644 --- a/homeassistant/components/climate/device_trigger.py +++ b/homeassistant/components/climate/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Climate.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -65,7 +63,7 @@ TRIGGER_SCHEMA = vol.Any(HVAC_MODE_TRIGGER_SCHEMA, CURRENT_TRIGGER_SCHEMA) async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Climate devices.""" registry = entity_registry.async_get(hass) triggers = [] diff --git a/homeassistant/components/cover/device_action.py b/homeassistant/components/cover/device_action.py index 9bdf21f168e..c3c0e928f0f 100644 --- a/homeassistant/components/cover/device_action.py +++ b/homeassistant/components/cover/device_action.py @@ -21,7 +21,7 @@ from homeassistant.core import Context, HomeAssistant from homeassistant.helpers import entity_registry import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_supported_features -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, TemplateVarsType from . import ( ATTR_POSITION, @@ -120,7 +120,10 @@ async def async_get_action_capabilities( async def async_call_action_from_config( - hass: HomeAssistant, config: dict, variables: dict, context: Context | None + hass: HomeAssistant, + config: ConfigType, + variables: TemplateVarsType, + context: Context | None, ) -> None: """Execute a device action.""" service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]} diff --git a/homeassistant/components/cover/device_trigger.py b/homeassistant/components/cover/device_trigger.py index f7b2f54f4dd..a6ed7785486 100644 --- a/homeassistant/components/cover/device_trigger.py +++ b/homeassistant/components/cover/device_trigger.py @@ -1,8 +1,6 @@ """Provides device automations for Cover.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -74,7 +72,7 @@ TRIGGER_SCHEMA = vol.Any(POSITION_TRIGGER_SCHEMA, STATE_TRIGGER_SCHEMA) async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Cover devices.""" registry = entity_registry.async_get(hass) triggers = [] diff --git a/homeassistant/components/device_tracker/device_trigger.py b/homeassistant/components/device_tracker/device_trigger.py index 134e9d8dca3..1d9dc4548b3 100644 --- a/homeassistant/components/device_tracker/device_trigger.py +++ b/homeassistant/components/device_tracker/device_trigger.py @@ -1,7 +1,7 @@ """Provides device automations for Device Tracker.""" from __future__ import annotations -from typing import Any, Final +from typing import Final import voluptuous as vol @@ -39,7 +39,7 @@ TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend( async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for Device Tracker devices.""" registry = entity_registry.async_get(hass) triggers = [] From 5cfb31d28a54357c047e46a74f273c17cf25745d Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 16:07:34 +0200 Subject: [PATCH 740/930] Adjust device_automation type hints in core components (#72207) --- .../components/device_automation/entity.py | 4 +-- .../device_automation/toggle_entity.py | 4 +-- .../components/geo_location/trigger.py | 16 ++++++++-- .../components/homeassistant/trigger.py | 22 +++++++++++-- .../homeassistant/triggers/homeassistant.py | 14 +++++++-- .../homeassistant/triggers/numeric_state.py | 13 ++++++-- .../homeassistant/triggers/state.py | 10 ++++-- .../components/homeassistant/triggers/time.py | 16 ++++++++-- .../homeassistant/triggers/time_pattern.py | 14 +++++++-- .../components/mqtt/device_trigger.py | 6 ++-- homeassistant/components/mqtt/trigger.py | 14 +++++++-- homeassistant/components/sun/trigger.py | 14 +++++++-- homeassistant/components/template/trigger.py | 20 +++++++++--- homeassistant/components/webhook/trigger.py | 16 ++++++++-- homeassistant/components/zone/trigger.py | 13 ++++++-- homeassistant/helpers/trigger.py | 31 +++++++++++++------ 16 files changed, 176 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/device_automation/entity.py b/homeassistant/components/device_automation/entity.py index 9fa878ea038..4bc77370150 100644 --- a/homeassistant/components/device_automation/entity.py +++ b/homeassistant/components/device_automation/entity.py @@ -1,8 +1,6 @@ """Device automation helpers for entity.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -91,7 +89,7 @@ async def _async_get_automations( async def async_get_triggers( hass: HomeAssistant, device_id: str, domain: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" return await _async_get_automations(hass, device_id, ENTITY_TRIGGERS, domain) diff --git a/homeassistant/components/device_automation/toggle_entity.py b/homeassistant/components/device_automation/toggle_entity.py index 4ae32927bf4..af97de85f70 100644 --- a/homeassistant/components/device_automation/toggle_entity.py +++ b/homeassistant/components/device_automation/toggle_entity.py @@ -1,8 +1,6 @@ """Device automation helpers for toggle entity.""" from __future__ import annotations -from typing import Any - import voluptuous as vol from homeassistant.components.automation import ( @@ -228,7 +226,7 @@ async def async_get_conditions( async def async_get_triggers( hass: HomeAssistant, device_id: str, domain: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers.""" triggers = await entity.async_get_triggers(hass, device_id, domain) triggers.extend( diff --git a/homeassistant/components/geo_location/trigger.py b/homeassistant/components/geo_location/trigger.py index e57c7a9aec6..9f2b56a31c6 100644 --- a/homeassistant/components/geo_location/trigger.py +++ b/homeassistant/components/geo_location/trigger.py @@ -3,11 +3,16 @@ import logging import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_EVENT, CONF_PLATFORM, CONF_SOURCE, CONF_ZONE -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import condition, config_validation as cv from homeassistant.helpers.config_validation import entity_domain from homeassistant.helpers.event import TrackStates, async_track_state_change_filtered +from homeassistant.helpers.typing import ConfigType from . import DOMAIN @@ -36,10 +41,15 @@ def source_match(state, source): return state and state.attributes.get("source") == source -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] - source = config.get(CONF_SOURCE).lower() + source: str = config[CONF_SOURCE].lower() zone_entity_id = config.get(CONF_ZONE) trigger_event = config.get(CONF_EVENT) job = HassJob(action) diff --git a/homeassistant/components/homeassistant/trigger.py b/homeassistant/components/homeassistant/trigger.py index ca77747cd96..42b0e30af1d 100644 --- a/homeassistant/components/homeassistant/trigger.py +++ b/homeassistant/components/homeassistant/trigger.py @@ -1,14 +1,25 @@ """Home Assistant trigger dispatcher.""" import importlib +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) +from homeassistant.components.device_automation.trigger import ( + DeviceAutomationTriggerProtocol, +) from homeassistant.const import CONF_PLATFORM +from homeassistant.core import CALLBACK_TYPE, HomeAssistant +from homeassistant.helpers.typing import ConfigType -def _get_trigger_platform(config): +def _get_trigger_platform(config: ConfigType) -> DeviceAutomationTriggerProtocol: return importlib.import_module(f"..triggers.{config[CONF_PLATFORM]}", __name__) -async def async_validate_trigger_config(hass, config): +async def async_validate_trigger_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" platform = _get_trigger_platform(config) if hasattr(platform, "async_validate_trigger_config"): @@ -17,7 +28,12 @@ async def async_validate_trigger_config(hass, config): return platform.TRIGGER_SCHEMA(config) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Attach trigger of specified platform.""" platform = _get_trigger_platform(config) return await platform.async_attach_trigger(hass, config, action, automation_info) diff --git a/homeassistant/components/homeassistant/triggers/homeassistant.py b/homeassistant/components/homeassistant/triggers/homeassistant.py index 6f2ec75e313..c9a5a780e88 100644 --- a/homeassistant/components/homeassistant/triggers/homeassistant.py +++ b/homeassistant/components/homeassistant/triggers/homeassistant.py @@ -1,9 +1,14 @@ """Offer Home Assistant core automation rules.""" import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_EVENT, CONF_PLATFORM, EVENT_HOMEASSISTANT_STOP -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.typing import ConfigType # mypy: allow-untyped-defs @@ -18,7 +23,12 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for events based on configuration.""" trigger_data = automation_info["trigger_data"] event = config.get(CONF_EVENT) diff --git a/homeassistant/components/homeassistant/triggers/numeric_state.py b/homeassistant/components/homeassistant/triggers/numeric_state.py index 2d73f38d110..934cc99993a 100644 --- a/homeassistant/components/homeassistant/triggers/numeric_state.py +++ b/homeassistant/components/homeassistant/triggers/numeric_state.py @@ -4,6 +4,10 @@ import logging import voluptuous as vol from homeassistant import exceptions +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import ( CONF_ABOVE, CONF_ATTRIBUTE, @@ -81,10 +85,15 @@ async def async_validate_trigger_config( async def async_attach_trigger( - hass, config, action, automation_info, *, platform_type="numeric_state" + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, + *, + platform_type: str = "numeric_state", ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" - entity_ids = config.get(CONF_ENTITY_ID) + entity_ids: list[str] = config[CONF_ENTITY_ID] below = config.get(CONF_BELOW) above = config.get(CONF_ABOVE) time_delta = config.get(CONF_FOR) diff --git a/homeassistant/components/homeassistant/triggers/state.py b/homeassistant/components/homeassistant/triggers/state.py index e6a4b90dbe8..4f1e823c90f 100644 --- a/homeassistant/components/homeassistant/triggers/state.py +++ b/homeassistant/components/homeassistant/triggers/state.py @@ -7,6 +7,10 @@ import logging import voluptuous as vol from homeassistant import exceptions +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_ATTRIBUTE, CONF_FOR, CONF_PLATFORM, MATCH_ALL from homeassistant.core import ( CALLBACK_TYPE, @@ -92,9 +96,9 @@ async def async_validate_trigger_config( async def async_attach_trigger( hass: HomeAssistant, - config, - action, - automation_info, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, *, platform_type: str = "state", ) -> CALLBACK_TYPE: diff --git a/homeassistant/components/homeassistant/triggers/time.py b/homeassistant/components/homeassistant/triggers/time.py index 49a42d3843d..619ef0e207c 100644 --- a/homeassistant/components/homeassistant/triggers/time.py +++ b/homeassistant/components/homeassistant/triggers/time.py @@ -5,6 +5,10 @@ from functools import partial import voluptuous as vol from homeassistant.components import sensor +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import ( ATTR_DEVICE_CLASS, CONF_AT, @@ -12,13 +16,14 @@ from homeassistant.const import ( STATE_UNAVAILABLE, STATE_UNKNOWN, ) -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import ( async_track_point_in_time, async_track_state_change_event, async_track_time_change, ) +from homeassistant.helpers.typing import ConfigType import homeassistant.util.dt as dt_util # mypy: allow-untyped-defs, no-check-untyped-defs @@ -37,10 +42,15 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] - entities = {} + entities: dict[str, CALLBACK_TYPE] = {} removes = [] job = HassJob(action) diff --git a/homeassistant/components/homeassistant/triggers/time_pattern.py b/homeassistant/components/homeassistant/triggers/time_pattern.py index 000d73b6cd1..7ee1d218171 100644 --- a/homeassistant/components/homeassistant/triggers/time_pattern.py +++ b/homeassistant/components/homeassistant/triggers/time_pattern.py @@ -1,10 +1,15 @@ """Offer time listening automation rules.""" import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_PLATFORM -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import async_track_time_change +from homeassistant.helpers.typing import ConfigType # mypy: allow-untyped-defs, no-check-untyped-defs @@ -55,7 +60,12 @@ TRIGGER_SCHEMA = vol.All( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] hours = config.get(CONF_HOURS) diff --git a/homeassistant/components/mqtt/device_trigger.py b/homeassistant/components/mqtt/device_trigger.py index 56cfc3efc6b..42ffcee1644 100644 --- a/homeassistant/components/mqtt/device_trigger.py +++ b/homeassistant/components/mqtt/device_trigger.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable import logging -from typing import Any, cast +from typing import cast import attr import voluptuous as vol @@ -290,9 +290,9 @@ async def async_removed_from_device(hass: HomeAssistant, device_id: str) -> None async def async_get_triggers( hass: HomeAssistant, device_id: str -) -> list[dict[str, Any]]: +) -> list[dict[str, str]]: """List device triggers for MQTT devices.""" - triggers: list[dict] = [] + triggers: list[dict[str, str]] = [] if DEVICE_TRIGGERS not in hass.data: return triggers diff --git a/homeassistant/components/mqtt/trigger.py b/homeassistant/components/mqtt/trigger.py index db366010bb2..7d1f93d30eb 100644 --- a/homeassistant/components/mqtt/trigger.py +++ b/homeassistant/components/mqtt/trigger.py @@ -5,9 +5,14 @@ import logging import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_PAYLOAD, CONF_PLATFORM, CONF_VALUE_TEMPLATE -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers.typing import ConfigType from .. import mqtt from .const import CONF_ENCODING, CONF_QOS, CONF_TOPIC, DEFAULT_ENCODING, DEFAULT_QOS @@ -31,7 +36,12 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( _LOGGER = logging.getLogger(__name__) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] topic = config[CONF_TOPIC] diff --git a/homeassistant/components/sun/trigger.py b/homeassistant/components/sun/trigger.py index 266df1f6a3b..75f5b36f8f5 100644 --- a/homeassistant/components/sun/trigger.py +++ b/homeassistant/components/sun/trigger.py @@ -3,15 +3,20 @@ from datetime import timedelta import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import ( CONF_EVENT, CONF_OFFSET, CONF_PLATFORM, SUN_EVENT_SUNRISE, ) -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import async_track_sunrise, async_track_sunset +from homeassistant.helpers.typing import ConfigType # mypy: allow-untyped-defs, no-check-untyped-defs @@ -24,7 +29,12 @@ TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( ) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for events based on configuration.""" trigger_data = automation_info["trigger_data"] event = config.get(CONF_EVENT) diff --git a/homeassistant/components/template/trigger.py b/homeassistant/components/template/trigger.py index d2e50de53fd..33ac90079b7 100644 --- a/homeassistant/components/template/trigger.py +++ b/homeassistant/components/template/trigger.py @@ -4,15 +4,20 @@ import logging import voluptuous as vol from homeassistant import exceptions +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_FOR, CONF_PLATFORM, CONF_VALUE_TEMPLATE -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv, template from homeassistant.helpers.event import ( TrackTemplate, async_call_later, async_track_template_result, ) -from homeassistant.helpers.template import result_as_boolean +from homeassistant.helpers.template import Template, result_as_boolean +from homeassistant.helpers.typing import ConfigType # mypy: allow-untyped-defs, no-check-untyped-defs @@ -28,11 +33,16 @@ TRIGGER_SCHEMA = IF_ACTION_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend( async def async_attach_trigger( - hass, config, action, automation_info, *, platform_type="template" -): + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, + *, + platform_type: str = "template", +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] - value_template = config.get(CONF_VALUE_TEMPLATE) + value_template: Template = config[CONF_VALUE_TEMPLATE] value_template.hass = hass time_delta = config.get(CONF_FOR) template.attach(hass, time_delta) diff --git a/homeassistant/components/webhook/trigger.py b/homeassistant/components/webhook/trigger.py index 4eaf60595a5..3f790b1ec42 100644 --- a/homeassistant/components/webhook/trigger.py +++ b/homeassistant/components/webhook/trigger.py @@ -4,9 +4,14 @@ from functools import partial from aiohttp import hdrs import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import CONF_PLATFORM, CONF_WEBHOOK_ID -from homeassistant.core import HassJob, callback +from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.typing import ConfigType from . import async_register, async_unregister @@ -37,10 +42,15 @@ async def _handle_webhook(job, trigger_data, hass, webhook_id, request): hass.async_run_hass_job(job, {"trigger": result}) -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Trigger based on incoming webhooks.""" trigger_data = automation_info["trigger_data"] - webhook_id = config.get(CONF_WEBHOOK_ID) + webhook_id: str = config[CONF_WEBHOOK_ID] job = HassJob(action) async_register( hass, diff --git a/homeassistant/components/zone/trigger.py b/homeassistant/components/zone/trigger.py index 8c5af3a0ac2..0865182df80 100644 --- a/homeassistant/components/zone/trigger.py +++ b/homeassistant/components/zone/trigger.py @@ -3,6 +3,10 @@ import logging import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.const import ( ATTR_FRIENDLY_NAME, CONF_ENTITY_ID, @@ -56,11 +60,16 @@ async def async_validate_trigger_config( async def async_attach_trigger( - hass, config, action, automation_info, *, platform_type: str = "zone" + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, + *, + platform_type: str = "zone", ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" trigger_data = automation_info["trigger_data"] - entity_id = config.get(CONF_ENTITY_ID) + entity_id: list[str] = config[CONF_ENTITY_ID] zone_entity_id = config.get(CONF_ZONE) event = config.get(CONF_EVENT) job = HassJob(action) diff --git a/homeassistant/helpers/trigger.py b/homeassistant/helpers/trigger.py index a3cb2f9421d..e90c684365d 100644 --- a/homeassistant/helpers/trigger.py +++ b/homeassistant/helpers/trigger.py @@ -5,7 +5,7 @@ import asyncio from collections.abc import Callable import functools import logging -from typing import Any +from typing import TYPE_CHECKING, Any import voluptuous as vol @@ -16,13 +16,20 @@ from homeassistant.loader import IntegrationNotFound, async_get_integration from .typing import ConfigType, TemplateVarsType +if TYPE_CHECKING: + from homeassistant.components.device_automation.trigger import ( + DeviceAutomationTriggerProtocol, + ) + _PLATFORM_ALIASES = { "device_automation": ("device",), "homeassistant": ("event", "numeric_state", "state", "time_pattern", "time"), } -async def _async_get_trigger_platform(hass: HomeAssistant, config: ConfigType) -> Any: +async def _async_get_trigger_platform( + hass: HomeAssistant, config: ConfigType +) -> DeviceAutomationTriggerProtocol: platform_and_sub_type = config[CONF_PLATFORM].split(".") platform = platform_and_sub_type[0] for alias, triggers in _PLATFORM_ALIASES.items(): @@ -86,6 +93,10 @@ async def async_initialize_triggers( variables: TemplateVarsType = None, ) -> CALLBACK_TYPE | None: """Initialize triggers.""" + from homeassistant.components.automation import ( # pylint:disable=[import-outside-toplevel] + AutomationTriggerData, + AutomationTriggerInfo, + ) triggers = [] for idx, conf in enumerate(trigger_config): @@ -96,14 +107,14 @@ async def async_initialize_triggers( platform = await _async_get_trigger_platform(hass, conf) trigger_id = conf.get(CONF_ID, f"{idx}") trigger_idx = f"{idx}" - trigger_data = {"id": trigger_id, "idx": trigger_idx} - info = { - "domain": domain, - "name": name, - "home_assistant_start": home_assistant_start, - "variables": variables, - "trigger_data": trigger_data, - } + trigger_data = AutomationTriggerData(id=trigger_id, idx=trigger_idx) + info = AutomationTriggerInfo( + domain=domain, + name=name, + home_assistant_start=home_assistant_start, + variables=variables, + trigger_data=trigger_data, + ) triggers.append( platform.async_attach_trigger( From 204e26a1b534764ed31fa85447d0deed725fda00 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Mon, 23 May 2022 10:11:17 -0400 Subject: [PATCH 741/930] Warn user if Steam friends list is restricted (#72285) --- .../components/steam_online/config_flow.py | 14 ++++++++++--- .../components/steam_online/strings.json | 3 +++ .../steam_online/translations/en.json | 3 +++ tests/components/steam_online/__init__.py | 15 ++++++++++++++ .../steam_online/test_config_flow.py | 20 +++++++++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/steam_online/config_flow.py b/homeassistant/components/steam_online/config_flow.py index 91965685969..338b0a80fb6 100644 --- a/homeassistant/components/steam_online/config_flow.py +++ b/homeassistant/components/steam_online/config_flow.py @@ -174,11 +174,14 @@ class SteamOptionsFlowHandler(config_entries.OptionsFlow): } await self.hass.config_entries.async_reload(self.entry.entry_id) return self.async_create_entry(title="", data=channel_data) + error = None try: users = { name["steamid"]: name["personaname"] for name in await self.hass.async_add_executor_job(self.get_accounts) } + if not users: + error = {"base": "unauthorized"} except steam.api.HTTPTimeoutError: users = self.options[CONF_ACCOUNTS] @@ -191,12 +194,17 @@ class SteamOptionsFlowHandler(config_entries.OptionsFlow): } self.options[CONF_ACCOUNTS] = users | self.options[CONF_ACCOUNTS] - return self.async_show_form(step_id="init", data_schema=vol.Schema(options)) + return self.async_show_form( + step_id="init", data_schema=vol.Schema(options), errors=error + ) def get_accounts(self) -> list[dict[str, str | int]]: """Get accounts.""" interface = steam.api.interface("ISteamUser") - friends = interface.GetFriendList(steamid=self.entry.data[CONF_ACCOUNT]) - _users_str = [user["steamid"] for user in friends["friendslist"]["friends"]] + try: + friends = interface.GetFriendList(steamid=self.entry.data[CONF_ACCOUNT]) + _users_str = [user["steamid"] for user in friends["friendslist"]["friends"]] + except steam.api.HTTPError: + return [] names = interface.GetPlayerSummaries(steamids=_users_str) return names["response"]["players"]["player"] diff --git a/homeassistant/components/steam_online/strings.json b/homeassistant/components/steam_online/strings.json index 6d80bb77f1b..1b431795ea4 100644 --- a/homeassistant/components/steam_online/strings.json +++ b/homeassistant/components/steam_online/strings.json @@ -31,6 +31,9 @@ "accounts": "Names of accounts to be monitored" } } + }, + "error": { + "unauthorized": "Friends list restricted: Please refer to the documentation on how to see all other friends" } } } diff --git a/homeassistant/components/steam_online/translations/en.json b/homeassistant/components/steam_online/translations/en.json index 990a33dbeff..f93a517b241 100644 --- a/homeassistant/components/steam_online/translations/en.json +++ b/homeassistant/components/steam_online/translations/en.json @@ -31,6 +31,9 @@ "accounts": "Names of accounts to be monitored" } } + }, + "error": { + "unauthorized": "Friends list restricted: Please refer to the documentation on how to see all other friends" } } } \ No newline at end of file diff --git a/tests/components/steam_online/__init__.py b/tests/components/steam_online/__init__.py index 27958b76576..4c8c398502f 100644 --- a/tests/components/steam_online/__init__.py +++ b/tests/components/steam_online/__init__.py @@ -1,6 +1,8 @@ """Tests for Steam integration.""" from unittest.mock import patch +import steam + from homeassistant.components.steam_online import DOMAIN from homeassistant.components.steam_online.const import CONF_ACCOUNT, CONF_ACCOUNTS from homeassistant.const import CONF_API_KEY @@ -96,11 +98,24 @@ class MockedInterface(dict): return {"response": {"player_level": 10}} +class MockedInterfacePrivate(MockedInterface): + """Mocked interface for private friends list.""" + + def GetFriendList(self, steamid: str) -> None: + """Get friend list.""" + raise steam.api.HTTPError + + def patch_interface() -> MockedInterface: """Patch interface.""" return patch("steam.api.interface", return_value=MockedInterface()) +def patch_interface_private() -> MockedInterfacePrivate: + """Patch interface for private friends list.""" + return patch("steam.api.interface", return_value=MockedInterfacePrivate()) + + def patch_user_interface_null() -> MockedUserInterfaceNull: """Patch player interface with no players.""" return patch("steam.api.interface", return_value=MockedUserInterfaceNull()) diff --git a/tests/components/steam_online/test_config_flow.py b/tests/components/steam_online/test_config_flow.py index 6d8a16f35f7..c4504bf1641 100644 --- a/tests/components/steam_online/test_config_flow.py +++ b/tests/components/steam_online/test_config_flow.py @@ -21,6 +21,7 @@ from . import ( CONF_OPTIONS_2, create_entry, patch_interface, + patch_interface_private, patch_user_interface_null, ) @@ -225,3 +226,22 @@ async def test_options_flow_timeout(hass: HomeAssistant) -> None: assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["data"] == CONF_OPTIONS + + +async def test_options_flow_unauthorized(hass: HomeAssistant) -> None: + """Test updating options when user's friends list is not public.""" + entry = create_entry(hass) + with patch_interface_private(): + result = await hass.config_entries.options.async_init(entry.entry_id) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={CONF_ACCOUNTS: [ACCOUNT_1]}, + ) + await hass.async_block_till_done() + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["data"] == CONF_OPTIONS From d0556e6dd165e936e91e41f970f76cc236af46b7 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 23 May 2022 16:29:45 +0200 Subject: [PATCH 742/930] Move manual configuration of MQTT sensor to the integration key (#72276) Add sensor --- homeassistant/components/mqtt/__init__.py | 1 + homeassistant/components/mqtt/sensor.py | 31 +++++++++++++++++++---- tests/components/mqtt/test_sensor.py | 13 ++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 52204b31dcf..df5d52c443a 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -203,6 +203,7 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.SCENE.value): cv.ensure_list, vol.Optional(Platform.SELECT.value): cv.ensure_list, vol.Optional(Platform.SIREN.value): cv.ensure_list, + vol.Optional(Platform.SENSOR.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, vol.Optional(Platform.NUMBER.value): cv.ensure_list, diff --git a/homeassistant/components/mqtt/sensor.py b/homeassistant/components/mqtt/sensor.py index a13f58f95ea..d865d90c4ee 100644 --- a/homeassistant/components/mqtt/sensor.py +++ b/homeassistant/components/mqtt/sensor.py @@ -1,6 +1,7 @@ """Support for MQTT sensors.""" from __future__ import annotations +import asyncio from datetime import timedelta import functools import logging @@ -41,8 +42,10 @@ from .mixins import ( MQTT_ENTITY_COMMON_SCHEMA, MqttAvailability, MqttEntity, + async_get_platform_config_from_yaml, async_setup_entry_helper, async_setup_platform_helper, + warn_for_legacy_schema, ) _LOGGER = logging.getLogger(__name__) @@ -86,7 +89,7 @@ def validate_options(conf): return conf -_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( +_PLATFORM_SCHEMA_BASE = mqtt.MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_EXPIRE_AFTER): cv.positive_int, @@ -99,12 +102,19 @@ _PLATFORM_SCHEMA_BASE = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -PLATFORM_SCHEMA = vol.All( - cv.deprecated(CONF_LAST_RESET_TOPIC), +PLATFORM_SCHEMA_MODERN = vol.All( _PLATFORM_SCHEMA_BASE, validate_options, ) +# Configuring MQTT Sensors under the sensor platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + cv.deprecated(CONF_LAST_RESET_TOPIC), + cv.PLATFORM_SCHEMA.extend(_PLATFORM_SCHEMA_BASE.schema), + validate_options, + warn_for_legacy_schema(sensor.DOMAIN), +) + DISCOVERY_SCHEMA = vol.All( cv.deprecated(CONF_LAST_RESET_TOPIC), _PLATFORM_SCHEMA_BASE.extend({}, extra=vol.REMOVE_EXTRA), @@ -118,7 +128,8 @@ async def async_setup_platform( async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: - """Set up MQTT sensors through configuration.yaml.""" + """Set up MQTT sensors configured under the fan platform key (deprecated).""" + # Deprecated in HA Core 2022.6 await async_setup_platform_helper( hass, sensor.DOMAIN, config, async_add_entities, _async_setup_entity ) @@ -129,7 +140,17 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up MQTT sensors dynamically through MQTT discovery.""" + """Set up MQTT sensor through configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, sensor.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index b653e04c82e..befb5785cdd 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -55,6 +55,7 @@ from .test_common import ( help_test_setting_attribute_via_mqtt_json_message, help_test_setting_attribute_with_template, help_test_setting_blocked_attribute_via_mqtt_json_message, + help_test_setup_manual_entity_from_yaml, help_test_unique_id, help_test_update_with_json_attrs_bad_JSON, help_test_update_with_json_attrs_not_dict, @@ -1106,3 +1107,15 @@ async def test_encoding_subscribable_topics( attribute_value, skip_raw_test=True, ) + + +async def test_setup_manual_entity_from_yaml(hass, caplog, tmp_path): + """Test setup manual configured MQTT entity.""" + platform = sensor.DOMAIN + config = copy.deepcopy(DEFAULT_CONFIG[platform]) + config["name"] = "test" + del config["platform"] + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, platform, config + ) + assert hass.states.get(f"{platform}.test") is not None From 967f4efc56c11f292105f47f7a2323f17170930b Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Mon, 23 May 2022 16:50:05 +0200 Subject: [PATCH 743/930] Cleanup config flow and tests for here_travel_time (#72364) --- .../components/here_travel_time/__init__.py | 18 ------------ .../here_travel_time/config_flow.py | 28 +++++++++++++++---- .../here_travel_time/test_config_flow.py | 16 +++++++++-- .../components/here_travel_time/test_init.py | 2 ++ .../here_travel_time/test_sensor.py | 9 ++++++ 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index 310fe97fad8..da091e0bdbf 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -41,11 +41,9 @@ from .const import ( CONF_ORIGIN_LATITUDE, CONF_ORIGIN_LONGITUDE, CONF_ROUTE_MODE, - CONF_TRAFFIC_MODE, DEFAULT_SCAN_INTERVAL, DOMAIN, NO_ROUTE_ERROR_MESSAGE, - ROUTE_MODE_FASTEST, TRAFFIC_MODE_ENABLED, TRAVEL_MODES_VEHICLE, ) @@ -60,7 +58,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b """Set up HERE Travel Time from a config entry.""" api_key = config_entry.data[CONF_API_KEY] here_client = RoutingApi(api_key) - setup_options(hass, config_entry) arrival = ( dt.parse_time(config_entry.options[CONF_ARRIVAL_TIME]) @@ -98,21 +95,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b return True -def setup_options(hass: HomeAssistant, config_entry: ConfigEntry) -> None: - """Set up options for a config entry if not set.""" - if not config_entry.options: - hass.config_entries.async_update_entry( - config_entry, - options={ - CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, - CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, - CONF_ARRIVAL_TIME: None, - CONF_DEPARTURE_TIME: None, - CONF_UNIT_SYSTEM: hass.config.units.name, - }, - ) - - async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms( diff --git a/homeassistant/components/here_travel_time/config_flow.py b/homeassistant/components/here_travel_time/config_flow.py index 502eee68546..e8a05796b66 100644 --- a/homeassistant/components/here_travel_time/config_flow.py +++ b/homeassistant/components/here_travel_time/config_flow.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONF_NAME, CONF_UNIT_SYSTEM, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult import homeassistant.helpers.config_validation as cv from homeassistant.helpers.selector import ( @@ -128,12 +128,25 @@ def get_user_step_schema(data: dict[str, Any]) -> vol.Schema: ) +def default_options(hass: HomeAssistant) -> dict[str, str | None]: + """Get the default options.""" + return { + CONF_TRAFFIC_MODE: TRAFFIC_MODE_ENABLED, + CONF_ROUTE_MODE: ROUTE_MODE_FASTEST, + CONF_ARRIVAL_TIME: None, + CONF_DEPARTURE_TIME: None, + CONF_UNIT_SYSTEM: hass.config.units.name, + } + + class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for HERE Travel Time.""" VERSION = 1 - _config: dict[str, Any] = {} + def __init__(self) -> None: + """Init Config Flow.""" + self._config: dict[str, Any] = {} @staticmethod @callback @@ -211,7 +224,9 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): "longitude" ] return self.async_create_entry( - title=self._config[CONF_NAME], data=self._config + title=self._config[CONF_NAME], + data=self._config, + options=default_options(self.hass), ) schema = vol.Schema( {"destination": selector({LocationSelector.selector_type: {}})} @@ -230,7 +245,9 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): CONF_DESTINATION_ENTITY_ID ] return self.async_create_entry( - title=self._config[CONF_NAME], data=self._config + title=self._config[CONF_NAME], + data=self._config, + options=default_options(self.hass), ) schema = vol.Schema( {CONF_DESTINATION_ENTITY_ID: selector({EntitySelector.selector_type: {}})} @@ -301,11 +318,10 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class HERETravelTimeOptionsFlow(config_entries.OptionsFlow): """Handle HERE Travel Time options.""" - _config: dict[str, Any] = {} - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize HERE Travel Time options flow.""" self.config_entry = config_entry + self._config: dict[str, Any] = {} async def async_step_init( self, user_input: dict[str, Any] | None = None diff --git a/tests/components/here_travel_time/test_config_flow.py b/tests/components/here_travel_time/test_config_flow.py index 350e1ae36f6..c1ce6f823ae 100644 --- a/tests/components/here_travel_time/test_config_flow.py +++ b/tests/components/here_travel_time/test_config_flow.py @@ -50,6 +50,16 @@ from .const import ( from tests.common import MockConfigEntry +@pytest.fixture(autouse=True) +def bypass_setup_fixture(): + """Prevent setup.""" + with patch( + "homeassistant.components.here_travel_time.async_setup_entry", + return_value=True, + ): + yield + + @pytest.fixture(name="user_step_result") async def user_step_result_fixture(hass: HomeAssistant) -> data_entry_flow.FlowResult: """Provide the result of a completed user step.""" @@ -65,7 +75,7 @@ async def user_step_result_fixture(hass: HomeAssistant) -> data_entry_flow.FlowR }, ) await hass.async_block_till_done() - yield user_step_result + return user_step_result @pytest.fixture(name="option_init_result") @@ -96,7 +106,7 @@ async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.Flo CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC, }, ) - yield result + return result @pytest.fixture(name="origin_step_result") @@ -118,7 +128,7 @@ async def origin_step_result_fixture( } }, ) - yield location_selector_result + return location_selector_result @pytest.mark.parametrize( diff --git a/tests/components/here_travel_time/test_init.py b/tests/components/here_travel_time/test_init.py index 02827acc4df..05b7f6983db 100644 --- a/tests/components/here_travel_time/test_init.py +++ b/tests/components/here_travel_time/test_init.py @@ -2,6 +2,7 @@ import pytest +from homeassistant.components.here_travel_time.config_flow import default_options from homeassistant.components.here_travel_time.const import ( CONF_DESTINATION_LATITUDE, CONF_DESTINATION_LONGITUDE, @@ -39,6 +40,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: CONF_MODE: TRAVEL_MODE_CAR, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 70470aee9d3..9a15f14f53e 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -5,6 +5,7 @@ from herepy.here_enum import RouteMode from herepy.routing_api import NoRouteFoundError import pytest +from homeassistant.components.here_travel_time.config_flow import default_options from homeassistant.components.here_travel_time.const import ( ATTR_DESTINATION, ATTR_DESTINATION_NAME, @@ -224,6 +225,7 @@ async def test_circular_ref(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -250,6 +252,7 @@ async def test_no_attribution(hass: HomeAssistant): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -293,6 +296,7 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -332,6 +336,7 @@ async def test_destination_entity_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -357,6 +362,7 @@ async def test_origin_entity_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -386,6 +392,7 @@ async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -415,6 +422,7 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) @@ -444,6 +452,7 @@ async def test_route_not_found(hass: HomeAssistant, caplog): CONF_MODE: TRAVEL_MODE_TRUCK, CONF_NAME: "test", }, + options=default_options(hass), ) entry.add_to_hass(hass) await hass.config_entries.async_setup(entry.entry_id) From 5b4fdb081ec765cd5a2b6da2550f88d9b1099853 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Mon, 23 May 2022 17:24:28 +0200 Subject: [PATCH 744/930] Add climate tests for devolo_home_control (#72230) --- .coveragerc | 3 - tests/components/devolo_home_control/mocks.py | 52 ++++++++++++ .../devolo_home_control/test_climate.py | 81 +++++++++++++++++++ 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 tests/components/devolo_home_control/test_climate.py diff --git a/.coveragerc b/.coveragerc index ed1e0b0bce4..1eb922b52e5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -207,12 +207,9 @@ omit = homeassistant/components/denonavr/media_player.py homeassistant/components/denonavr/receiver.py homeassistant/components/deutsche_bahn/sensor.py - homeassistant/components/devolo_home_control/climate.py - homeassistant/components/devolo_home_control/const.py homeassistant/components/devolo_home_control/cover.py homeassistant/components/devolo_home_control/light.py homeassistant/components/devolo_home_control/sensor.py - homeassistant/components/devolo_home_control/subscriber.py homeassistant/components/devolo_home_control/switch.py homeassistant/components/digital_ocean/* homeassistant/components/discogs/sensor.py diff --git a/tests/components/devolo_home_control/mocks.py b/tests/components/devolo_home_control/mocks.py index 79bf94b8fc3..b43cb77ad71 100644 --- a/tests/components/devolo_home_control/mocks.py +++ b/tests/components/devolo_home_control/mocks.py @@ -8,6 +8,9 @@ from devolo_home_control_api.homecontrol import HomeControl from devolo_home_control_api.properties.binary_sensor_property import ( BinarySensorProperty, ) +from devolo_home_control_api.properties.multi_level_sensor_property import ( + MultiLevelSensorProperty, +) from devolo_home_control_api.properties.multi_level_switch_property import ( MultiLevelSwitchProperty, ) @@ -28,6 +31,31 @@ class BinarySensorPropertyMock(BinarySensorProperty): self.state = False +class MultiLevelSensorPropertyMock(MultiLevelSensorProperty): + """devolo Home Control multi level sensor mock.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + self.element_uid = "Test" + self.sensor_type = "temperature" + self._unit = "°C" + self._value = 20 + self._logger = MagicMock() + + +class MultiLevelSwitchPropertyMock(MultiLevelSwitchProperty): + """devolo Home Control multi level switch mock.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + self.element_uid = "Test" + self.min = 4 + self.max = 24 + self.switch_type = "temperature" + self._value = 20 + self._logger = MagicMock() + + class SirenPropertyMock(MultiLevelSwitchProperty): """devolo Home Control siren mock.""" @@ -84,6 +112,17 @@ class BinarySensorMockOverload(DeviceMock): self.binary_sensor_property["Overload"].sensor_type = "overload" +class ClimateMock(DeviceMock): + """devolo Home Control climate device mock.""" + + def __init__(self) -> None: + """Initialize the mock.""" + super().__init__() + self.device_model_uid = "devolo.model.Room:Thermostat" + self.multi_level_switch_property = {"Test": MultiLevelSwitchPropertyMock()} + self.multi_level_sensor_property = {"Test": MultiLevelSensorPropertyMock()} + + class RemoteControlMock(DeviceMock): """devolo Home Control remote control device mock.""" @@ -143,6 +182,19 @@ class HomeControlMockBinarySensor(HomeControlMock): self.publisher.unregister = MagicMock() +class HomeControlMockClimate(HomeControlMock): + """devolo Home Control gateway mock with climate devices.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + super().__init__() + self.devices = { + "Test": ClimateMock(), + } + self.publisher = Publisher(self.devices.keys()) + self.publisher.unregister = MagicMock() + + class HomeControlMockRemoteControl(HomeControlMock): """devolo Home Control gateway mock with remote control device.""" diff --git a/tests/components/devolo_home_control/test_climate.py b/tests/components/devolo_home_control/test_climate.py new file mode 100644 index 00000000000..4fca1825459 --- /dev/null +++ b/tests/components/devolo_home_control/test_climate.py @@ -0,0 +1,81 @@ +"""Tests for the devolo Home Control climate.""" +from unittest.mock import patch + +from homeassistant.components.climate import DOMAIN +from homeassistant.components.climate.const import ( + ATTR_HVAC_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant + +from . import configure_integration +from .mocks import HomeControlMock, HomeControlMockClimate + + +async def test_climate(hass: HomeAssistant): + """Test setup and state change of a climate device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockClimate() + test_gateway.devices["Test"].value = 20 + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test") + assert state is not None + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == test_gateway.devices["Test"].value + + # Emulate websocket message: temperature changed + test_gateway.publisher.dispatch("Test", ("Test", 21.0)) + await hass.async_block_till_done() + state = hass.states.get(f"{DOMAIN}.test") + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 21.0 + + # Test setting temperature + with patch( + "devolo_home_control_api.properties.multi_level_switch_property.MultiLevelSwitchProperty.set" + ) as set_value: + await hass.services.async_call( + DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: f"{DOMAIN}.test", + ATTR_HVAC_MODE: HVACMode.HEAT, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) # In reality, this leads to a websocket message like already tested above + set_value.assert_called_once_with(20.0) + + # Emulate websocket message: device went offline + test_gateway.devices["Test"].status = 1 + test_gateway.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + + +async def test_remove_from_hass(hass: HomeAssistant): + """Test removing entity.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockClimate() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test") + assert state is not None + await hass.config_entries.async_remove(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + assert test_gateway.publisher.unregister.call_count == 2 From 1b5a46a5ba751a455549c01aa7030e0ed6e5e82b Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 17:35:35 +0200 Subject: [PATCH 745/930] Adjust device_automation type hints in zha (#72142) --- homeassistant/components/zha/core/helpers.py | 12 ++++-- homeassistant/components/zha/device_action.py | 2 +- .../components/zha/device_trigger.py | 38 ++++++++++++++----- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/zha/core/helpers.py b/homeassistant/components/zha/core/helpers.py index a101720e8df..33d68822b9f 100644 --- a/homeassistant/components/zha/core/helpers.py +++ b/homeassistant/components/zha/core/helpers.py @@ -15,7 +15,7 @@ import itertools import logging from random import uniform import re -from typing import Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar import voluptuous as vol import zigpy.exceptions @@ -24,7 +24,7 @@ import zigpy.util import zigpy.zdo.types as zdo_types from homeassistant.config_entries import ConfigEntry -from homeassistant.core import State, callback +from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers import device_registry as dr from .const import ( @@ -37,6 +37,10 @@ from .const import ( from .registries import BINDABLE_CLUSTERS from .typing import ZhaDeviceType, ZigpyClusterType +if TYPE_CHECKING: + from .device import ZHADevice + from .gateway import ZHAGateway + _T = TypeVar("_T") @@ -161,11 +165,11 @@ def async_cluster_exists(hass, cluster_id): @callback -def async_get_zha_device(hass, device_id): +def async_get_zha_device(hass: HomeAssistant, device_id: str) -> ZHADevice: """Get a ZHA device for the given device registry id.""" device_registry = dr.async_get(hass) registry_device = device_registry.async_get(device_id) - zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] + zha_gateway: ZHAGateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] ieee_address = list(list(registry_device.identifiers)[0])[1] ieee = zigpy.types.EUI64.convert(ieee_address) return zha_gateway.devices[ieee] diff --git a/homeassistant/components/zha/device_action.py b/homeassistant/components/zha/device_action.py index f941b559cc7..049ffbd40f3 100644 --- a/homeassistant/components/zha/device_action.py +++ b/homeassistant/components/zha/device_action.py @@ -48,7 +48,7 @@ async def async_call_action_from_config( hass: HomeAssistant, config: ConfigType, variables: TemplateVarsType, - context: Context, + context: Context | None, ) -> None: """Perform an action based on configuration.""" await ZHA_ACTION_TYPES[DEVICE_ACTION_TYPES[config[CONF_TYPE]]]( diff --git a/homeassistant/components/zha/device_trigger.py b/homeassistant/components/zha/device_trigger.py index d81d8164bd4..670b1cc1477 100644 --- a/homeassistant/components/zha/device_trigger.py +++ b/homeassistant/components/zha/device_trigger.py @@ -1,12 +1,19 @@ """Provides device automations for ZHA devices that emit events.""" import voluptuous as vol +from homeassistant.components.automation import ( + AutomationActionType, + AutomationTriggerInfo, +) from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation.exceptions import ( InvalidDeviceAutomationConfig, ) from homeassistant.components.homeassistant.triggers import event as event_trigger from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE +from homeassistant.core import CALLBACK_TYPE, HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.typing import ConfigType from . import DOMAIN from .core.helpers import async_get_zha_device @@ -21,7 +28,9 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend( ) -async def async_validate_trigger_config(hass, config): +async def async_validate_trigger_config( + hass: HomeAssistant, config: ConfigType +) -> ConfigType: """Validate config.""" config = TRIGGER_SCHEMA(config) @@ -40,18 +49,25 @@ async def async_validate_trigger_config(hass, config): return config -async def async_attach_trigger(hass, config, action, automation_info): +async def async_attach_trigger( + hass: HomeAssistant, + config: ConfigType, + action: AutomationActionType, + automation_info: AutomationTriggerInfo, +) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" - trigger = (config[CONF_TYPE], config[CONF_SUBTYPE]) + trigger_key: tuple[str, str] = (config[CONF_TYPE], config[CONF_SUBTYPE]) try: zha_device = async_get_zha_device(hass, config[CONF_DEVICE_ID]) - except (KeyError, AttributeError): - return None + except (KeyError, AttributeError) as err: + raise HomeAssistantError( + f"Unable to get zha device {config[CONF_DEVICE_ID]}" + ) from err - if trigger not in zha_device.device_automation_triggers: - return None + if trigger_key not in zha_device.device_automation_triggers: + raise HomeAssistantError(f"Unable to find trigger {trigger_key}") - trigger = zha_device.device_automation_triggers[trigger] + trigger = zha_device.device_automation_triggers[trigger_key] event_config = { event_trigger.CONF_PLATFORM: "event", @@ -65,7 +81,9 @@ async def async_attach_trigger(hass, config, action, automation_info): ) -async def async_get_triggers(hass, device_id): +async def async_get_triggers( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: """List device triggers. Make sure the device supports device automations and @@ -74,7 +92,7 @@ async def async_get_triggers(hass, device_id): zha_device = async_get_zha_device(hass, device_id) if not zha_device.device_automation_triggers: - return + return [] triggers = [] for trigger, subtype in zha_device.device_automation_triggers.keys(): From c10d456d116443a92b5c14be118a599b0e144274 Mon Sep 17 00:00:00 2001 From: Keilin Bickar Date: Mon, 23 May 2022 11:45:01 -0400 Subject: [PATCH 746/930] Handle Sense timeout exceptions from initial authentication (#72369) --- homeassistant/components/sense/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/sense/__init__.py b/homeassistant/components/sense/__init__.py index aaf3630ae19..2e0ed4622e8 100644 --- a/homeassistant/components/sense/__init__.py +++ b/homeassistant/components/sense/__init__.py @@ -80,6 +80,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (SenseAuthenticationException, SenseMFARequiredException) as err: _LOGGER.warning("Sense authentication expired") raise ConfigEntryAuthFailed(err) from err + except SENSE_TIMEOUT_EXCEPTIONS as err: + raise ConfigEntryNotReady( + str(err) or "Timed out during authentication" + ) from err sense_devices_data = SenseDevicesData() try: From 7da9cac1f866a10c25d7ee5e33c7b807182da41c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 17:48:41 +0200 Subject: [PATCH 747/930] Log SamsungTV state changes (#71989) --- homeassistant/components/samsungtv/media_player.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/samsungtv/media_player.py b/homeassistant/components/samsungtv/media_player.py index 6a884c59a87..cf3bfcd64a1 100644 --- a/homeassistant/components/samsungtv/media_player.py +++ b/homeassistant/components/samsungtv/media_player.py @@ -197,12 +197,15 @@ class SamsungTVDevice(MediaPlayerEntity): """Update state of device.""" if self._auth_failed or self.hass.is_stopping: return + old_state = self._attr_state if self._power_off_in_progress(): self._attr_state = STATE_OFF else: self._attr_state = ( STATE_ON if await self._bridge.async_is_on() else STATE_OFF ) + if self._attr_state != old_state: + LOGGER.debug("TV %s state updated to %s", self._host, self._attr_state) if self._attr_state != STATE_ON: if self._dmr_device and self._dmr_device.is_subscribed: From deedbca46c163f0c4c5342888877b769d0e6f758 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 23 May 2022 17:52:21 +0200 Subject: [PATCH 748/930] Mark unused sync toggle method from ToggleEntity as final (#72370) --- homeassistant/components/hdmi_cec/switch.py | 9 --------- homeassistant/helpers/entity.py | 17 +++++++++++------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index a5d64b2a7fa..076bedde0b2 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -52,15 +52,6 @@ class CecSwitchEntity(CecEntity, SwitchEntity): self._state = STATE_OFF self.schedule_update_ha_state(force_refresh=False) - def toggle(self, **kwargs): - """Toggle the entity.""" - self._device.toggle() - if self._state == STATE_ON: - self._state = STATE_OFF - else: - self._state = STATE_ON - self.schedule_update_ha_state(force_refresh=False) - @property def is_on(self) -> bool: """Return True if entity is on.""" diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index c8faad53b0e..1c03e2334fe 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -1025,15 +1025,20 @@ class ToggleEntity(Entity): """Turn the entity off.""" await self.hass.async_add_executor_job(ft.partial(self.turn_off, **kwargs)) + @final def toggle(self, **kwargs: Any) -> None: - """Toggle the entity.""" - if self.is_on: - self.turn_off(**kwargs) - else: - self.turn_on(**kwargs) + """Toggle the entity. + + This method will never be called by Home Assistant and should not be implemented + by integrations. + """ async def async_toggle(self, **kwargs: Any) -> None: - """Toggle the entity.""" + """Toggle the entity. + + This method should typically not be implemented by integrations, it's enough to + implement async_turn_on + async_turn_off or turn_on + turn_off. + """ if self.is_on: await self.async_turn_off(**kwargs) else: From f90effc29988cece1b3f6acebc99f3563e5f313a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 23 May 2022 18:22:49 +0200 Subject: [PATCH 749/930] Add agent version to Supervisor system health (#72360) Co-authored-by: Stefan Agner --- homeassistant/components/hassio/strings.json | 1 + homeassistant/components/hassio/system_health.py | 1 + tests/components/hassio/test_system_health.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/homeassistant/components/hassio/strings.json b/homeassistant/components/hassio/strings.json index 875a79a60d7..90142bd453f 100644 --- a/homeassistant/components/hassio/strings.json +++ b/homeassistant/components/hassio/strings.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agent Version", "board": "Board", "disk_total": "Disk Total", "disk_used": "Disk Used", diff --git a/homeassistant/components/hassio/system_health.py b/homeassistant/components/hassio/system_health.py index 5ce38b0e121..1039a0237a8 100644 --- a/homeassistant/components/hassio/system_health.py +++ b/homeassistant/components/hassio/system_health.py @@ -44,6 +44,7 @@ async def system_health_info(hass: HomeAssistant): "host_os": host_info.get("operating_system"), "update_channel": info.get("channel"), "supervisor_version": f"supervisor-{info.get('supervisor')}", + "agent_version": host_info.get("agent_version"), "docker_version": info.get("docker"), "disk_total": f"{host_info.get('disk_total')} GB", "disk_used": f"{host_info.get('disk_used')} GB", diff --git a/tests/components/hassio/test_system_health.py b/tests/components/hassio/test_system_health.py index dcf18f7bc13..344ac9d63be 100644 --- a/tests/components/hassio/test_system_health.py +++ b/tests/components/hassio/test_system_health.py @@ -35,6 +35,7 @@ async def test_hassio_system_health(hass, aioclient_mock): } hass.data["hassio_host_info"] = { "operating_system": "Home Assistant OS 5.9", + "agent_version": "1337", "disk_total": "32.0", "disk_used": "30.0", } @@ -52,6 +53,7 @@ async def test_hassio_system_health(hass, aioclient_mock): info[key] = await val assert info == { + "agent_version": "1337", "board": "odroid-n2", "disk_total": "32.0 GB", "disk_used": "30.0 GB", From 3cdc5c8429338dfe0d0ffb88866052dc7ecd5ce1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 May 2022 11:38:59 -0500 Subject: [PATCH 750/930] Add climate platform to Big Ass Fans (#72117) --- .coveragerc | 1 + homeassistant/components/baf/__init__.py | 7 ++- homeassistant/components/baf/climate.py | 60 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/baf/climate.py diff --git a/.coveragerc b/.coveragerc index 1eb922b52e5..d884e6e8c13 100644 --- a/.coveragerc +++ b/.coveragerc @@ -94,6 +94,7 @@ omit = homeassistant/components/azure_devops/sensor.py homeassistant/components/azure_service_bus/* homeassistant/components/baf/__init__.py + homeassistant/components/baf/climate.py homeassistant/components/baf/entity.py homeassistant/components/baf/fan.py homeassistant/components/baf/sensor.py diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py index 6e76014f0b7..30de2582af1 100644 --- a/homeassistant/components/baf/__init__.py +++ b/homeassistant/components/baf/__init__.py @@ -14,7 +14,12 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import DOMAIN, QUERY_INTERVAL, RUN_TIMEOUT from .models import BAFData -PLATFORMS: list[Platform] = [Platform.FAN, Platform.SENSOR, Platform.SWITCH] +PLATFORMS: list[Platform] = [ + Platform.CLIMATE, + Platform.FAN, + Platform.SENSOR, + Platform.SWITCH, +] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/baf/climate.py b/homeassistant/components/baf/climate.py new file mode 100644 index 00000000000..f785d18e06f --- /dev/null +++ b/homeassistant/components/baf/climate.py @@ -0,0 +1,60 @@ +"""Support for Big Ass Fans auto comfort.""" +from __future__ import annotations + +from typing import Any + +from homeassistant import config_entries +from homeassistant.components.climate import ( + ClimateEntity, + ClimateEntityFeature, + HVACAction, + HVACMode, +) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .entity import BAFEntity +from .models import BAFData + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up BAF fan auto comfort.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + if data.device.has_fan: + async_add_entities( + [BAFAutoComfort(data.device, f"{data.device.name} Auto Comfort")] + ) + + +class BAFAutoComfort(BAFEntity, ClimateEntity): + """BAF climate auto comfort.""" + + _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE + _attr_temperature_unit = TEMP_CELSIUS + _attr_hvac_modes = [HVACMode.OFF, HVACMode.FAN_ONLY] + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + device = self._device + auto_on = device.auto_comfort_enable + self._attr_hvac_mode = HVACMode.FAN_ONLY if auto_on else HVACMode.OFF + self._attr_hvac_action = HVACAction.FAN if device.speed else HVACAction.OFF + self._attr_target_temperature = device.comfort_ideal_temperature + self._attr_current_temperature = device.temperature + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set the HVAC mode.""" + self._device.auto_comfort_enable = hvac_mode == HVACMode.FAN_ONLY + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set the target temperature.""" + if not self._device.auto_comfort_enable: + self._device.auto_comfort_enable = True + self._device.comfort_ideal_temperature = kwargs[ATTR_TEMPERATURE] From f25663067cfbcc98b7307fd501ce67cc76a81937 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 18:51:40 +0200 Subject: [PATCH 751/930] Enforce type hints on device_automation platform (#72126) --- pylint/plugins/hass_enforce_type_hints.py | 158 ++++++++++++++++++++-- tests/pylint/test_enforce_type_hints.py | 75 ++++++++++ 2 files changed, 221 insertions(+), 12 deletions(-) diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 6cf521addac..9d35d07fab2 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -31,6 +31,8 @@ _TYPE_HINT_MATCHERS: dict[str, re.Pattern] = { "x_of_y": re.compile(r"^(\w+)\[(.*?]*)\]$"), # x_of_y_comma_z matches items such as "Callable[..., Awaitable[None]]" "x_of_y_comma_z": re.compile(r"^(\w+)\[(.*?]*), (.*?]*)\]$"), + # x_of_y_of_z_comma_a matches items such as "list[dict[str, Any]]" + "x_of_y_of_z_comma_a": re.compile(r"^(\w+)\[(\w+)\[(.*?]*), (.*?]*)\]\]$"), } _MODULE_FILTERS: dict[str, re.Pattern] = { @@ -44,12 +46,20 @@ _MODULE_FILTERS: dict[str, re.Pattern] = { "application_credentials": re.compile( r"^homeassistant\.components\.\w+\.(application_credentials)$" ), - # device_tracker matches only in the package root (device_tracker.py) - "device_tracker": re.compile(r"^homeassistant\.components\.\w+\.(device_tracker)$"), - # diagnostics matches only in the package root (diagnostics.py) - "diagnostics": re.compile(r"^homeassistant\.components\.\w+\.(diagnostics)$"), # config_flow matches only in the package root (config_flow.py) "config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$"), + # device_action matches only in the package root (device_action.py) + "device_action": re.compile(r"^homeassistant\.components\.\w+\.(device_action)$"), + # device_condition matches only in the package root (device_condition.py) + "device_condition": re.compile( + r"^homeassistant\.components\.\w+\.(device_condition)$" + ), + # device_tracker matches only in the package root (device_tracker.py) + "device_tracker": re.compile(r"^homeassistant\.components\.\w+\.(device_tracker)$"), + # device_trigger matches only in the package root (device_trigger.py) + "device_trigger": re.compile(r"^homeassistant\.components\.\w+\.(device_trigger)$"), + # diagnostics matches only in the package root (diagnostics.py) + "diagnostics": re.compile(r"^homeassistant\.components\.\w+\.(diagnostics)$"), } _METHOD_MATCH: list[TypeHintMatch] = [ @@ -157,6 +167,88 @@ _METHOD_MATCH: list[TypeHintMatch] = [ }, return_type="AuthorizationServer", ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["config_flow"], + function_name="_async_has_devices", + arg_types={ + 0: "HomeAssistant", + }, + return_type="bool", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_action"], + function_name="async_validate_action_config", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="ConfigType", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_action"], + function_name="async_call_action_from_config", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + 2: "TemplateVarsType", + 3: "Context | None", + }, + return_type=None, + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_action"], + function_name="async_get_action_capabilities", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="dict[str, Schema]", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_action"], + function_name="async_get_actions", + arg_types={ + 0: "HomeAssistant", + 1: "str", + }, + return_type=["list[dict[str, str]]", "list[dict[str, Any]]"], + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_condition"], + function_name="async_validate_condition_config", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="ConfigType", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_condition"], + function_name="async_condition_from_config", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="ConditionCheckerType", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_condition"], + function_name="async_get_condition_capabilities", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="dict[str, Schema]", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_condition"], + function_name="async_get_conditions", + arg_types={ + 0: "HomeAssistant", + 1: "str", + }, + return_type=["list[dict[str, str]]", "list[dict[str, Any]]"], + ), TypeHintMatch( module_filter=_MODULE_FILTERS["device_tracker"], function_name="setup_scanner", @@ -197,6 +289,44 @@ _METHOD_MATCH: list[TypeHintMatch] = [ }, return_type=["DeviceScanner", "DeviceScanner | None"], ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_trigger"], + function_name="async_validate_condition_config", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="ConfigType", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_trigger"], + function_name="async_attach_trigger", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + 2: "AutomationActionType", + 3: "AutomationTriggerInfo", + }, + return_type="CALLBACK_TYPE", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_trigger"], + function_name="async_get_trigger_capabilities", + arg_types={ + 0: "HomeAssistant", + 1: "ConfigType", + }, + return_type="dict[str, Schema]", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["device_trigger"], + function_name="async_get_triggers", + arg_types={ + 0: "HomeAssistant", + 1: "str", + }, + return_type=["list[dict[str, str]]", "list[dict[str, Any]]"], + ), TypeHintMatch( module_filter=_MODULE_FILTERS["diagnostics"], function_name="async_get_config_entry_diagnostics", @@ -216,14 +346,6 @@ _METHOD_MATCH: list[TypeHintMatch] = [ }, return_type=UNDEFINED, ), - TypeHintMatch( - module_filter=_MODULE_FILTERS["config_flow"], - function_name="_async_has_devices", - arg_types={ - 0: "HomeAssistant", - }, - return_type="bool", - ), ] @@ -254,6 +376,18 @@ def _is_valid_type(expected_type: list[str] | str | None, node: astroid.NodeNG) and _is_valid_type(match.group(2), node.right) ) + # Special case for xxx[yyy[zzz, aaa]]` + if match := _TYPE_HINT_MATCHERS["x_of_y_of_z_comma_a"].match(expected_type): + return ( + isinstance(node, astroid.Subscript) + and _is_valid_type(match.group(1), node.value) + and isinstance(subnode := node.slice, astroid.Subscript) + and _is_valid_type(match.group(2), subnode.value) + and isinstance(subnode.slice, astroid.Tuple) + and _is_valid_type(match.group(3), subnode.slice.elts[0]) + and _is_valid_type(match.group(4), subnode.slice.elts[1]) + ) + # Special case for xxx[yyy, zzz]` if match := _TYPE_HINT_MATCHERS["x_of_y_comma_z"].match(expected_type): return ( diff --git a/tests/pylint/test_enforce_type_hints.py b/tests/pylint/test_enforce_type_hints.py index 64be85c9a44..feb3b6b341c 100644 --- a/tests/pylint/test_enforce_type_hints.py +++ b/tests/pylint/test_enforce_type_hints.py @@ -14,6 +14,32 @@ import pytest from . import assert_adds_messages, assert_no_messages +@pytest.mark.parametrize( + ("string", "expected_x", "expected_y", "expected_z", "expected_a"), + [ + ("list[dict[str, str]]", "list", "dict", "str", "str"), + ("list[dict[str, Any]]", "list", "dict", "str", "Any"), + ], +) +def test_regex_x_of_y_of_z_comma_a( + hass_enforce_type_hints: ModuleType, + string: str, + expected_x: str, + expected_y: str, + expected_z: str, + expected_a: str, +) -> None: + """Test x_of_y_of_z_comma_a regexes.""" + matchers: dict[str, re.Pattern] = hass_enforce_type_hints._TYPE_HINT_MATCHERS + + assert (match := matchers["x_of_y_of_z_comma_a"].match(string)) + assert match.group(0) == string + assert match.group(1) == expected_x + assert match.group(2) == expected_y + assert match.group(3) == expected_z + assert match.group(4) == expected_a + + @pytest.mark.parametrize( ("string", "expected_x", "expected_y", "expected_z"), [ @@ -165,3 +191,52 @@ def test_valid_discovery_info( with assert_no_messages(linter): type_hint_checker.visit_asyncfunctiondef(func_node) + + +def test_invalid_list_dict_str_any( + linter: UnittestLinter, type_hint_checker: BaseChecker +) -> None: + """Ensure invalid hints are rejected for discovery_info.""" + type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger" + func_node = astroid.extract_node( + """ + async def async_get_triggers( #@ + hass: HomeAssistant, + device_id: str + ) -> list: + pass + """ + ) + + with assert_adds_messages( + linter, + pylint.testutils.MessageTest( + msg_id="hass-return-type", + node=func_node, + args=["list[dict[str, str]]", "list[dict[str, Any]]"], + line=2, + col_offset=0, + end_line=2, + end_col_offset=28, + ), + ): + type_hint_checker.visit_asyncfunctiondef(func_node) + + +def test_valid_list_dict_str_any( + linter: UnittestLinter, type_hint_checker: BaseChecker +) -> None: + """Ensure valid hints are accepted for discovery_info.""" + type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger" + func_node = astroid.extract_node( + """ + async def async_get_triggers( #@ + hass: HomeAssistant, + device_id: str + ) -> list[dict[str, Any]]: + pass + """ + ) + + with assert_no_messages(linter): + type_hint_checker.visit_asyncfunctiondef(func_node) From e2a80e7a45d5d09c7eba00e4a620fc0320aa15ba Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Mon, 23 May 2022 10:02:42 -0700 Subject: [PATCH 752/930] Remove unnecessary class from wemo test_fan (#72377) --- tests/components/wemo/test_fan.py | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/tests/components/wemo/test_fan.py b/tests/components/wemo/test_fan.py index 56bf8939181..d58f8edbc3d 100644 --- a/tests/components/wemo/test_fan.py +++ b/tests/components/wemo/test_fan.py @@ -19,6 +19,7 @@ from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON, STATE_OFF, STAT from homeassistant.setup import async_setup_component from . import entity_test_helpers +from .conftest import async_create_wemo_entity @pytest.fixture @@ -161,21 +162,14 @@ async def test_fan_set_percentage( pywemo_device.set_state.assert_called_with(expected_fan_mode) -class TestInitialFanMode: - """Test that the FanMode is set to High when turned on the first time.""" - - @pytest.fixture - def pywemo_device(self, pywemo_device): - """Set the FanMode to off initially.""" - pywemo_device.fan_mode = FanMode.Off - yield pywemo_device - - async def test_fan_mode_high_initially(self, hass, pywemo_device, wemo_entity): - """Verify the FanMode is set to High when turned on.""" - assert await hass.services.async_call( - FAN_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: [wemo_entity.entity_id]}, - blocking=True, - ) - pywemo_device.set_state.assert_called_with(FanMode.High) +async def test_fan_mode_high_initially(hass, pywemo_device): + """Verify the FanMode is set to High when turned on.""" + pywemo_device.fan_mode = FanMode.Off + wemo_entity = await async_create_wemo_entity(hass, pywemo_device, "") + assert await hass.services.async_call( + FAN_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: [wemo_entity.entity_id]}, + blocking=True, + ) + pywemo_device.set_state.assert_called_with(FanMode.High) From 4baf59666ade3cb4a7d1efadf8fa298eda0f3706 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 May 2022 12:26:08 -0500 Subject: [PATCH 753/930] Remove sqlite 3.34.1 downgrade workaround by reverting "Downgrade sqlite-libs on docker image (#55591)" (#72342) --- Dockerfile | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1d6ce675e74..13552d55a3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,21 +25,6 @@ RUN \ -e ./homeassistant --use-deprecated=legacy-resolver \ && python3 -m compileall homeassistant/homeassistant -# Fix Bug with Alpine 3.14 and sqlite 3.35 -# https://gitlab.alpinelinux.org/alpine/aports/-/issues/12524 -ARG BUILD_ARCH -RUN \ - if [ "${BUILD_ARCH}" = "amd64" ]; then \ - export APK_ARCH=x86_64; \ - elif [ "${BUILD_ARCH}" = "i386" ]; then \ - export APK_ARCH=x86; \ - else \ - export APK_ARCH=${BUILD_ARCH}; \ - fi \ - && curl -O http://dl-cdn.alpinelinux.org/alpine/v3.13/main/${APK_ARCH}/sqlite-libs-3.34.1-r0.apk \ - && apk add --no-cache sqlite-libs-3.34.1-r0.apk \ - && rm -f sqlite-libs-3.34.1-r0.apk - # Home Assistant S6-Overlay COPY rootfs / From 92582beeff7a5d1e9fa6cfae3afa41f596b5e3c2 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Mon, 23 May 2022 10:28:16 -0700 Subject: [PATCH 754/930] Use properties of wemo Maker device (#72378) --- homeassistant/components/wemo/switch.py | 6 ++--- tests/components/wemo/conftest.py | 6 +++++ tests/components/wemo/test_binary_sensor.py | 13 ----------- tests/components/wemo/test_switch.py | 25 +++++++++++++++++++++ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index 48cc1d92d67..c3d5bcb0462 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -64,15 +64,15 @@ class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): attr: dict[str, Any] = {} if isinstance(self.wemo, Maker): # Is the maker sensor on or off. - if self.wemo.maker_params["hassensor"]: + if self.wemo.has_sensor: # Note a state of 1 matches the WeMo app 'not triggered'! - if self.wemo.maker_params["sensorstate"]: + if self.wemo.sensor_state: attr[ATTR_SENSOR_STATE] = STATE_OFF else: attr[ATTR_SENSOR_STATE] = STATE_ON # Is the maker switch configured as toggle(0) or momentary (1). - if self.wemo.maker_params["switchmode"]: + if self.wemo.switch_mode: attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_MOMENTARY else: attr[ATTR_SWITCH_MODE] = MAKER_SWITCH_TOGGLE diff --git a/tests/components/wemo/conftest.py b/tests/components/wemo/conftest.py index fbb2f186cf5..ae900e2522f 100644 --- a/tests/components/wemo/conftest.py +++ b/tests/components/wemo/conftest.py @@ -77,6 +77,12 @@ def create_pywemo_device(pywemo_registry, pywemo_model): device.today_on_time = 5678 device.total_on_time = 9012 + if issubclass(cls, pywemo.Maker): + device.has_sensor = 1 + device.sensor_state = 1 + device.switch_mode = 1 + device.switch_state = 0 + url = f"http://{MOCK_HOST}:{MOCK_PORT}/setup.xml" with patch("pywemo.setup_url_for_address", return_value=url), patch( "pywemo.discovery.device_from_description", return_value=device diff --git a/tests/components/wemo/test_binary_sensor.py b/tests/components/wemo/test_binary_sensor.py index eccc1b180a5..f26428bf32b 100644 --- a/tests/components/wemo/test_binary_sensor.py +++ b/tests/components/wemo/test_binary_sensor.py @@ -81,19 +81,6 @@ class TestMaker(EntityTestHelpers): """Select the MakerBinarySensor entity.""" return MakerBinarySensor._name_suffix.lower() - @pytest.fixture(name="pywemo_device") - def pywemo_device_fixture(self, pywemo_device): - """Fixture for WeMoDevice instances.""" - pywemo_device.maker_params = { - "hassensor": 1, - "sensorstate": 1, - "switchmode": 1, - "switchstate": 0, - } - pywemo_device.has_sensor = pywemo_device.maker_params["hassensor"] - pywemo_device.sensor_state = pywemo_device.maker_params["sensorstate"] - yield pywemo_device - async def test_registry_state_callback( self, hass, pywemo_registry, pywemo_device, wemo_entity ): diff --git a/tests/components/wemo/test_switch.py b/tests/components/wemo/test_switch.py index d54099e5e4a..0c024295632 100644 --- a/tests/components/wemo/test_switch.py +++ b/tests/components/wemo/test_switch.py @@ -14,6 +14,9 @@ from homeassistant.components.wemo.switch import ( ATTR_ON_TODAY_TIME, ATTR_ON_TOTAL_TIME, ATTR_POWER_THRESHOLD, + ATTR_SENSOR_STATE, + ATTR_SWITCH_MODE, + MAKER_SWITCH_MOMENTARY, ) from homeassistant.const import ( ATTR_ENTITY_ID, @@ -147,3 +150,25 @@ async def test_insight_state_attributes(hass, pywemo_registry): await async_update() attributes = hass.states.get(wemo_entity.entity_id).attributes assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_UNKNOWN + + +async def test_maker_state_attributes(hass, pywemo_registry): + """Verify the switch attributes are set for the Insight device.""" + await async_setup_component(hass, HA_DOMAIN, {}) + with create_pywemo_device(pywemo_registry, "Maker") as maker: + wemo_entity = await async_create_wemo_entity(hass, maker, "") + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_SENSOR_STATE] == STATE_OFF + assert attributes[ATTR_SWITCH_MODE] == MAKER_SWITCH_MOMENTARY + + # Test 'ON' sensor state and 'TOGGLE' switch mode values. + maker.sensor_state = 0 + maker.switch_mode = 0 + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: [wemo_entity.entity_id]}, + blocking=True, + ) + attributes = hass.states.get(wemo_entity.entity_id).attributes + assert attributes[ATTR_SENSOR_STATE] == STATE_ON From 90e5d691848dc2eb327023504bebc8fc86fd44b9 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Mon, 23 May 2022 19:32:22 +0200 Subject: [PATCH 755/930] Add template as_timedelta (#71801) --- homeassistant/helpers/template.py | 7 +++ homeassistant/util/dt.py | 72 +++++++++++++++++++++++++++++++ tests/helpers/test_template.py | 12 ++++++ tests/util/test_dt.py | 28 ++++++++++++ 4 files changed, 119 insertions(+) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index d1ed9d06db9..1a8febf5ac2 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -1535,6 +1535,11 @@ def as_datetime(value): return dt_util.parse_datetime(value) +def as_timedelta(value: str) -> timedelta | None: + """Parse a ISO8601 duration like 'PT10M' to a timedelta.""" + return dt_util.parse_duration(value) + + def strptime(string, fmt, default=_SENTINEL): """Parse a time string to datetime.""" try: @@ -1902,6 +1907,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.filters["atan2"] = arc_tangent2 self.filters["sqrt"] = square_root self.filters["as_datetime"] = as_datetime + self.filters["as_timedelta"] = as_timedelta self.filters["as_timestamp"] = forgiving_as_timestamp self.filters["today_at"] = today_at self.filters["as_local"] = dt_util.as_local @@ -1947,6 +1953,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment): self.globals["float"] = forgiving_float self.globals["as_datetime"] = as_datetime self.globals["as_local"] = dt_util.as_local + self.globals["as_timedelta"] = as_timedelta self.globals["as_timestamp"] = forgiving_as_timestamp self.globals["today_at"] = today_at self.globals["relative_time"] = relative_time diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index c7073c0306f..80b322c1a14 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -28,6 +28,49 @@ DATETIME_RE = re.compile( r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" ) +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# https://github.com/django/django/blob/master/LICENSE +STANDARD_DURATION_RE = re.compile( + r"^" + r"(?:(?P-?\d+) (days?, )?)?" + r"(?P-?)" + r"((?:(?P\d+):)(?=\d+:\d+))?" + r"(?:(?P\d+):)?" + r"(?P\d+)" + r"(?:[\.,](?P\d{1,6})\d{0,6})?" + r"$" +) + +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# https://github.com/django/django/blob/master/LICENSE +ISO8601_DURATION_RE = re.compile( + r"^(?P[-+]?)" + r"P" + r"(?:(?P\d+([\.,]\d+)?)D)?" + r"(?:T" + r"(?:(?P\d+([\.,]\d+)?)H)?" + r"(?:(?P\d+([\.,]\d+)?)M)?" + r"(?:(?P\d+([\.,]\d+)?)S)?" + r")?" + r"$" +) + +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# https://github.com/django/django/blob/master/LICENSE +POSTGRES_INTERVAL_RE = re.compile( + r"^" + r"(?:(?P-?\d+) (days? ?))?" + r"(?:(?P[-+])?" + r"(?P\d+):" + r"(?P\d\d):" + r"(?P\d\d)" + r"(?:\.(?P\d{1,6}))?" + r")?$" +) + def set_default_time_zone(time_zone: dt.tzinfo) -> None: """Set a default time zone to be used when none is specified. @@ -171,6 +214,35 @@ def parse_date(dt_str: str) -> dt.date | None: return None +# Copyright (c) Django Software Foundation and individual contributors. +# All rights reserved. +# https://github.com/django/django/blob/master/LICENSE +def parse_duration(value: str) -> dt.timedelta | None: + """Parse a duration string and return a datetime.timedelta. + + Also supports ISO 8601 representation and PostgreSQL's day-time interval + format. + """ + match = ( + STANDARD_DURATION_RE.match(value) + or ISO8601_DURATION_RE.match(value) + or POSTGRES_INTERVAL_RE.match(value) + ) + if match: + kws = match.groupdict() + sign = -1 if kws.pop("sign", "+") == "-" else 1 + if kws.get("microseconds"): + kws["microseconds"] = kws["microseconds"].ljust(6, "0") + time_delta_args: dict[str, float] = { + k: float(v.replace(",", ".")) for k, v in kws.items() if v is not None + } + days = dt.timedelta(float(time_delta_args.pop("days", 0.0) or 0.0)) + if match.re == ISO8601_DURATION_RE: + days *= sign + return days + sign * dt.timedelta(**time_delta_args) + return None + + def parse_time(time_str: str) -> dt.time | None: """Parse a time string (00:20:00) into Time object. diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index b2448bc1b5c..ddda17c20ac 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -3388,6 +3388,18 @@ def test_urlencode(hass): assert tpl.async_render() == "the%20quick%20brown%20fox%20%3D%20true" +def test_as_timedelta(hass: HomeAssistant) -> None: + """Test the as_timedelta function/filter.""" + tpl = template.Template("{{ as_timedelta('PT10M') }}", hass) + assert tpl.async_render() == "0:10:00" + + tpl = template.Template("{{ 'PT10M' | as_timedelta }}", hass) + assert tpl.async_render() == "0:10:00" + + tpl = template.Template("{{ 'T10M' | as_timedelta }}", hass) + assert tpl.async_render() is None + + def test_iif(hass: HomeAssistant) -> None: """Test the immediate if function/filter.""" tpl = template.Template("{{ (1 == 1) | iif }}", hass) diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index c7992e05068..79cd4e5e0df 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -1,4 +1,6 @@ """Test Home Assistant date util methods.""" +from __future__ import annotations + from datetime import datetime, timedelta import pytest @@ -142,6 +144,32 @@ def test_parse_datetime_returns_none_for_incorrect_format(): assert dt_util.parse_datetime("not a datetime string") is None +@pytest.mark.parametrize( + "duration_string,expected_result", + [ + ("PT10M", timedelta(minutes=10)), + ("PT0S", timedelta(0)), + ("P10DT11H11M01S", timedelta(days=10, hours=11, minutes=11, seconds=1)), + ( + "4 1:20:30.111111", + timedelta(days=4, hours=1, minutes=20, seconds=30, microseconds=111111), + ), + ("4 1:2:30", timedelta(days=4, hours=1, minutes=2, seconds=30)), + ("3 days 04:05:06", timedelta(days=3, hours=4, minutes=5, seconds=6)), + ("P1YT10M", None), + ("P1MT10M", None), + ("1MT10M", None), + ("P1MT100M", None), + ("P1234", None), + ], +) +def test_parse_duration( + duration_string: str, expected_result: timedelta | None +) -> None: + """Test that parse_duration returns the expected result.""" + assert dt_util.parse_duration(duration_string) == expected_result + + def test_get_age(): """Test get_age.""" diff = dt_util.now() - timedelta(seconds=0) From f6370d052273facede196ff92678c58d653d0a65 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 23 May 2022 11:16:21 -0700 Subject: [PATCH 756/930] Add Honeywell Lyric application credentials platform and deprecate configuration in yaml (#72335) Add Honeywell Lyric application credentials platform and deprecate config yaml --- homeassistant/components/lyric/__init__.py | 49 ++++++++++++------- homeassistant/components/lyric/api.py | 3 +- .../lyric/application_credentials.py | 26 ++++++++++ homeassistant/components/lyric/manifest.json | 2 +- .../generated/application_credentials.py | 1 + tests/components/lyric/test_config_flow.py | 2 +- 6 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 homeassistant/components/lyric/application_credentials.py diff --git a/homeassistant/components/lyric/__init__.py b/homeassistant/components/lyric/__init__.py index 3709fa5445f..2eaee440ae7 100644 --- a/homeassistant/components/lyric/__init__.py +++ b/homeassistant/components/lyric/__init__.py @@ -13,6 +13,10 @@ from aiolyric.objects.location import LyricLocation import async_timeout import voluptuous as vol +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, Platform from homeassistant.core import HomeAssistant @@ -36,18 +40,20 @@ from .api import ( LyricLocalOAuth2Implementation, OAuth2SessionLyric, ) -from .config_flow import OAuth2FlowHandler -from .const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN +from .const import DOMAIN CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) @@ -63,20 +69,23 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if DOMAIN not in config: return True - hass.data[DOMAIN][CONF_CLIENT_ID] = config[DOMAIN][CONF_CLIENT_ID] - - OAuth2FlowHandler.async_register_implementation( + await async_import_client_credential( hass, - LyricLocalOAuth2Implementation( - hass, - DOMAIN, + DOMAIN, + ClientCredential( config[DOMAIN][CONF_CLIENT_ID], config[DOMAIN][CONF_CLIENT_SECRET], - OAUTH2_AUTHORIZE, - OAUTH2_TOKEN, ), ) + _LOGGER.warning( + "Configuration of Honeywell Lyric integration in YAML is deprecated " + "and will be removed in a future release; Your existing OAuth " + "Application Credentials have been imported into the UI " + "automatically and can be safely removed from your " + "configuration.yaml file" + ) + return True @@ -87,13 +96,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass, entry ) ) + if not isinstance(implementation, LyricLocalOAuth2Implementation): + raise ValueError("Unexpected auth implementation; can't find oauth client id") session = aiohttp_client.async_get_clientsession(hass) oauth_session = OAuth2SessionLyric(hass, entry, implementation) client = ConfigEntryLyricClient(session, oauth_session) - client_id = hass.data[DOMAIN][CONF_CLIENT_ID] + client_id = implementation.client_id lyric = Lyric(client, client_id) async def async_update_data(force_refresh_token: bool = False) -> Lyric: diff --git a/homeassistant/components/lyric/api.py b/homeassistant/components/lyric/api.py index 4a8aa44417f..171e010137a 100644 --- a/homeassistant/components/lyric/api.py +++ b/homeassistant/components/lyric/api.py @@ -4,6 +4,7 @@ from typing import cast from aiohttp import BasicAuth, ClientSession from aiolyric.client import LyricClient +from homeassistant.components.application_credentials import AuthImplementation from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -41,7 +42,7 @@ class ConfigEntryLyricClient(LyricClient): class LyricLocalOAuth2Implementation( - config_entry_oauth2_flow.LocalOAuth2Implementation + AuthImplementation, ): """Lyric Local OAuth2 implementation.""" diff --git a/homeassistant/components/lyric/application_credentials.py b/homeassistant/components/lyric/application_credentials.py new file mode 100644 index 00000000000..2ccdca72bb6 --- /dev/null +++ b/homeassistant/components/lyric/application_credentials.py @@ -0,0 +1,26 @@ +"""Application credentials platform for the Honeywell Lyric integration.""" + +from homeassistant.components.application_credentials import ( + AuthorizationServer, + ClientCredential, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .api import LyricLocalOAuth2Implementation +from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return custom auth implementation.""" + return LyricLocalOAuth2Implementation( + hass, + auth_domain, + credential, + AuthorizationServer( + authorize_url=OAUTH2_AUTHORIZE, + token_url=OAUTH2_TOKEN, + ), + ) diff --git a/homeassistant/components/lyric/manifest.json b/homeassistant/components/lyric/manifest.json index da60b046eb7..c0d9168f46f 100644 --- a/homeassistant/components/lyric/manifest.json +++ b/homeassistant/components/lyric/manifest.json @@ -3,7 +3,7 @@ "name": "Honeywell Lyric", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/lyric", - "dependencies": ["auth"], + "dependencies": ["application_credentials"], "requirements": ["aiolyric==1.0.8"], "codeowners": ["@timmo001"], "quality_scale": "silver", diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 63c19fe10b8..6d40b3fdef7 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -9,6 +9,7 @@ APPLICATION_CREDENTIALS = [ "geocaching", "google", "home_connect", + "lyric", "neato", "netatmo", "senz", diff --git a/tests/components/lyric/test_config_flow.py b/tests/components/lyric/test_config_flow.py index 25cc49c6c09..d9262e215fb 100644 --- a/tests/components/lyric/test_config_flow.py +++ b/tests/components/lyric/test_config_flow.py @@ -41,7 +41,7 @@ async def test_abort_if_no_configuration(hass): DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "missing_configuration" + assert result["reason"] == "missing_credentials" async def test_full_flow( From 0248a8710f347401dbafa47d350657781a66dba1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 May 2022 13:35:45 -0500 Subject: [PATCH 757/930] Always pass the source of the trigger for logbook context messages (#72333) --- homeassistant/components/alexa/logbook.py | 11 ++++- .../components/automation/logbook.py | 17 +++++--- homeassistant/components/deconz/logbook.py | 24 ++++++----- homeassistant/components/doorbird/logbook.py | 15 ++++--- homeassistant/components/elkm1/logbook.py | 8 +++- .../components/google_assistant/logbook.py | 6 ++- .../components/homeassistant/logbook.py | 2 +- homeassistant/components/homekit/logbook.py | 11 +++-- homeassistant/components/hue/logbook.py | 8 +++- homeassistant/components/logbook/__init__.py | 1 - homeassistant/components/logbook/const.py | 3 ++ homeassistant/components/logbook/processor.py | 11 +++-- .../components/lutron_caseta/logbook.py | 8 +++- .../components/mobile_app/logbook.py | 14 ++++-- homeassistant/components/script/logbook.py | 14 ++++-- homeassistant/components/shelly/logbook.py | 8 +++- .../components/logbook/test_websocket_api.py | 43 +++++++++++++------ 17 files changed, 145 insertions(+), 59 deletions(-) diff --git a/homeassistant/components/alexa/logbook.py b/homeassistant/components/alexa/logbook.py index 65fb410c601..b72b884ec29 100644 --- a/homeassistant/components/alexa/logbook.py +++ b/homeassistant/components/alexa/logbook.py @@ -1,4 +1,9 @@ """Describe logbook events.""" +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.core import callback from .const import DOMAIN, EVENT_ALEXA_SMART_HOME @@ -22,6 +27,10 @@ def async_describe_events(hass, async_describe_event): f"sent command {data['request']['namespace']}/{data['request']['name']}" ) - return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id} + return { + LOGBOOK_ENTRY_NAME: "Amazon Alexa", + LOGBOOK_ENTRY_MESSAGE: message, + LOGBOOK_ENTRY_ENTITY_ID: entity_id, + } async_describe_event(DOMAIN, EVENT_ALEXA_SMART_HOME, async_describe_logbook_event) diff --git a/homeassistant/components/automation/logbook.py b/homeassistant/components/automation/logbook.py index 97a859d25b0..529fed80d26 100644 --- a/homeassistant/components/automation/logbook.py +++ b/homeassistant/components/automation/logbook.py @@ -1,5 +1,12 @@ """Describe logbook events.""" from homeassistant.components.logbook import LazyEventPartialState +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_CONTEXT_ID, + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, + LOGBOOK_ENTRY_SOURCE, +) from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME from homeassistant.core import HomeAssistant, callback @@ -20,11 +27,11 @@ def async_describe_events(hass: HomeAssistant, async_describe_event): # type: i message = f"{message} by {data[ATTR_SOURCE]}" return { - "name": data.get(ATTR_NAME), - "message": message, - "source": data.get(ATTR_SOURCE), - "entity_id": data.get(ATTR_ENTITY_ID), - "context_id": event.context_id, + LOGBOOK_ENTRY_NAME: data.get(ATTR_NAME), + LOGBOOK_ENTRY_MESSAGE: message, + LOGBOOK_ENTRY_SOURCE: data.get(ATTR_SOURCE), + LOGBOOK_ENTRY_ENTITY_ID: data.get(ATTR_ENTITY_ID), + LOGBOOK_ENTRY_CONTEXT_ID: event.context_id, } async_describe_event( diff --git a/homeassistant/components/deconz/logbook.py b/homeassistant/components/deconz/logbook.py index e67e07d2222..07dc7cb0124 100644 --- a/homeassistant/components/deconz/logbook.py +++ b/homeassistant/components/deconz/logbook.py @@ -3,6 +3,10 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_DEVICE_ID, CONF_EVENT from homeassistant.core import Event, HomeAssistant, callback import homeassistant.helpers.device_registry as dr @@ -135,8 +139,8 @@ def async_describe_events( data = event.data[CONF_EVENT] return { - "name": f"{deconz_alarm_event.device.name}", - "message": f"fired event '{data}'", + LOGBOOK_ENTRY_NAME: f"{deconz_alarm_event.device.name}", + LOGBOOK_ENTRY_MESSAGE: f"fired event '{data}'", } @callback @@ -157,27 +161,27 @@ def async_describe_events( # Unknown event if not data: return { - "name": f"{deconz_event.device.name}", - "message": "fired an unknown event", + LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_MESSAGE: "fired an unknown event", } # No device event match if not action: return { - "name": f"{deconz_event.device.name}", - "message": f"fired event '{data}'", + LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_MESSAGE: f"fired event '{data}'", } # Gesture event if not interface: return { - "name": f"{deconz_event.device.name}", - "message": f"fired event '{ACTIONS[action]}'", + LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_MESSAGE: f"fired event '{ACTIONS[action]}'", } return { - "name": f"{deconz_event.device.name}", - "message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired", + LOGBOOK_ENTRY_NAME: f"{deconz_event.device.name}", + LOGBOOK_ENTRY_MESSAGE: f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired", } async_describe_event( diff --git a/homeassistant/components/doorbird/logbook.py b/homeassistant/components/doorbird/logbook.py index fbd6c670a8d..110d3f22fbf 100644 --- a/homeassistant/components/doorbird/logbook.py +++ b/homeassistant/components/doorbird/logbook.py @@ -1,5 +1,10 @@ """Describe logbook events.""" +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import callback @@ -16,11 +21,11 @@ def async_describe_events(hass, async_describe_event): doorbird_event = event.event_type.split("_", 1)[1] return { - "name": "Doorbird", - "message": f"Event {event.event_type} was fired", - "entity_id": hass.data[DOMAIN][DOOR_STATION_EVENT_ENTITY_IDS].get( - doorbird_event, event.data.get(ATTR_ENTITY_ID) - ), + LOGBOOK_ENTRY_NAME: "Doorbird", + LOGBOOK_ENTRY_MESSAGE: f"Event {event.event_type} was fired", + LOGBOOK_ENTRY_ENTITY_ID: hass.data[DOMAIN][ + DOOR_STATION_EVENT_ENTITY_IDS + ].get(doorbird_event, event.data.get(ATTR_ENTITY_ID)), } domain_data = hass.data[DOMAIN] diff --git a/homeassistant/components/elkm1/logbook.py b/homeassistant/components/elkm1/logbook.py index 01019ce77e0..9aa85b599e0 100644 --- a/homeassistant/components/elkm1/logbook.py +++ b/homeassistant/components/elkm1/logbook.py @@ -3,6 +3,10 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.core import Event, HomeAssistant, callback from .const import ( @@ -30,8 +34,8 @@ def async_describe_events( ATTR_KEYPAD_NAME, data[ATTR_KEYPAD_ID] ) # added in 2022.6 return { - "name": f"Elk Keypad {keypad_name}", - "message": f"pressed {data[ATTR_KEY_NAME]} ({data[ATTR_KEY]})", + LOGBOOK_ENTRY_NAME: f"Elk Keypad {keypad_name}", + LOGBOOK_ENTRY_MESSAGE: f"pressed {data[ATTR_KEY_NAME]} ({data[ATTR_KEY]})", } async_describe_event( diff --git a/homeassistant/components/google_assistant/logbook.py b/homeassistant/components/google_assistant/logbook.py index 86caa8a9e6c..0ed5745004d 100644 --- a/homeassistant/components/google_assistant/logbook.py +++ b/homeassistant/components/google_assistant/logbook.py @@ -1,4 +1,8 @@ """Describe logbook events.""" +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.core import callback from .const import DOMAIN, EVENT_COMMAND_RECEIVED, SOURCE_CLOUD @@ -25,6 +29,6 @@ def async_describe_events(hass, async_describe_event): if event.data["source"] != SOURCE_CLOUD: message += f" (via {event.data['source']})" - return {"name": "Google Assistant", "message": message} + return {LOGBOOK_ENTRY_NAME: "Google Assistant", LOGBOOK_ENTRY_MESSAGE: message} async_describe_event(DOMAIN, EVENT_COMMAND_RECEIVED, async_describe_logbook_event) diff --git a/homeassistant/components/homeassistant/logbook.py b/homeassistant/components/homeassistant/logbook.py index 229fb24cb27..548753982a8 100644 --- a/homeassistant/components/homeassistant/logbook.py +++ b/homeassistant/components/homeassistant/logbook.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable -from homeassistant.components.logbook import ( +from homeassistant.components.logbook.const import ( LOGBOOK_ENTRY_ICON, LOGBOOK_ENTRY_MESSAGE, LOGBOOK_ENTRY_NAME, diff --git a/homeassistant/components/homekit/logbook.py b/homeassistant/components/homekit/logbook.py index b6805f8cf6c..a513b31b232 100644 --- a/homeassistant/components/homekit/logbook.py +++ b/homeassistant/components/homekit/logbook.py @@ -2,6 +2,11 @@ from collections.abc import Callable from typing import Any +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_ENTITY_ID, ATTR_SERVICE from homeassistant.core import Event, HomeAssistant, callback @@ -26,9 +31,9 @@ def async_describe_events( message = f"send command {data[ATTR_SERVICE]}{value_msg} for {data[ATTR_DISPLAY_NAME]}" return { - "name": "HomeKit", - "message": message, - "entity_id": entity_id, + LOGBOOK_ENTRY_NAME: "HomeKit", + LOGBOOK_ENTRY_MESSAGE: message, + LOGBOOK_ENTRY_ENTITY_ID: entity_id, } async_describe_event(DOMAIN, EVENT_HOMEKIT_CHANGED, async_describe_logbook_event) diff --git a/homeassistant/components/hue/logbook.py b/homeassistant/components/hue/logbook.py index 40abce7e2d3..e98a99f1861 100644 --- a/homeassistant/components/hue/logbook.py +++ b/homeassistant/components/hue/logbook.py @@ -3,6 +3,10 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import CONF_DEVICE_ID, CONF_EVENT, CONF_ID, CONF_TYPE from homeassistant.core import Event, HomeAssistant, callback from homeassistant.helpers import device_registry as dr @@ -66,8 +70,8 @@ def async_describe_events( else: message = f"Event {data[CONF_EVENT]}" # v1 return { - "name": name, - "message": str(message), + LOGBOOK_ENTRY_NAME: name, + LOGBOOK_ENTRY_MESSAGE: message, } async_describe_event(DOMAIN, ATTR_HUE_EVENT, async_describe_hue_event) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index d9893781748..f66f1d5e920 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -39,7 +39,6 @@ from .const import ( LOGBOOK_ENTRY_NAME, LOGBOOK_FILTERS, ) -from .const import LOGBOOK_ENTRY_ICON # noqa: F401 from .models import LazyEventPartialState # noqa: F401 CONFIG_SCHEMA = vol.Schema( diff --git a/homeassistant/components/logbook/const.py b/homeassistant/components/logbook/const.py index 406406e9dd8..3f0c6599724 100644 --- a/homeassistant/components/logbook/const.py +++ b/homeassistant/components/logbook/const.py @@ -15,13 +15,16 @@ CONTEXT_ENTITY_ID_NAME = "context_entity_id_name" CONTEXT_EVENT_TYPE = "context_event_type" CONTEXT_DOMAIN = "context_domain" CONTEXT_STATE = "context_state" +CONTEXT_SOURCE = "context_source" CONTEXT_SERVICE = "context_service" CONTEXT_NAME = "context_name" CONTEXT_MESSAGE = "context_message" +LOGBOOK_ENTRY_CONTEXT_ID = "context_id" LOGBOOK_ENTRY_DOMAIN = "domain" LOGBOOK_ENTRY_ENTITY_ID = "entity_id" LOGBOOK_ENTRY_ICON = "icon" +LOGBOOK_ENTRY_SOURCE = "source" LOGBOOK_ENTRY_MESSAGE = "message" LOGBOOK_ENTRY_NAME = "name" LOGBOOK_ENTRY_STATE = "state" diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py index c9f20f27f7c..03506695700 100644 --- a/homeassistant/components/logbook/processor.py +++ b/homeassistant/components/logbook/processor.py @@ -42,6 +42,7 @@ from .const import ( CONTEXT_MESSAGE, CONTEXT_NAME, CONTEXT_SERVICE, + CONTEXT_SOURCE, CONTEXT_STATE, CONTEXT_USER_ID, DOMAIN, @@ -51,6 +52,7 @@ from .const import ( LOGBOOK_ENTRY_ICON, LOGBOOK_ENTRY_MESSAGE, LOGBOOK_ENTRY_NAME, + LOGBOOK_ENTRY_SOURCE, LOGBOOK_ENTRY_STATE, LOGBOOK_ENTRY_WHEN, LOGBOOK_FILTERS, @@ -398,11 +400,14 @@ class ContextAugmenter: data[CONTEXT_DOMAIN] = domain event = self.event_cache.get(context_row) described = describe_event(event) - if name := described.get(ATTR_NAME): + if name := described.get(LOGBOOK_ENTRY_NAME): data[CONTEXT_NAME] = name - if message := described.get(ATTR_MESSAGE): + if message := described.get(LOGBOOK_ENTRY_MESSAGE): data[CONTEXT_MESSAGE] = message - if not (attr_entity_id := described.get(ATTR_ENTITY_ID)): + # In 2022.12 and later drop `CONTEXT_MESSAGE` if `CONTEXT_SOURCE` is available + if source := described.get(LOGBOOK_ENTRY_SOURCE): + data[CONTEXT_SOURCE] = source + if not (attr_entity_id := described.get(LOGBOOK_ENTRY_ENTITY_ID)): return data[CONTEXT_ENTITY_ID] = attr_entity_id if self.include_entity_name: diff --git a/homeassistant/components/lutron_caseta/logbook.py b/homeassistant/components/lutron_caseta/logbook.py index 28342090a21..bcca548f64b 100644 --- a/homeassistant/components/lutron_caseta/logbook.py +++ b/homeassistant/components/lutron_caseta/logbook.py @@ -3,6 +3,10 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.core import Event, HomeAssistant, callback from .const import ( @@ -33,8 +37,8 @@ def async_describe_events( button_map = LEAP_TO_DEVICE_TYPE_SUBTYPE_MAP[device_type] button_description = button_map[leap_button_number] return { - "name": f"{data[ATTR_AREA_NAME]} {data[ATTR_DEVICE_NAME]}", - "message": f"{data[ATTR_ACTION]} {button_description}", + LOGBOOK_ENTRY_NAME: f"{data[ATTR_AREA_NAME]} {data[ATTR_DEVICE_NAME]}", + LOGBOOK_ENTRY_MESSAGE: f"{data[ATTR_ACTION]} {button_description}", } async_describe_event( diff --git a/homeassistant/components/mobile_app/logbook.py b/homeassistant/components/mobile_app/logbook.py index 6dd4d007e3e..6f7c2e4e99c 100644 --- a/homeassistant/components/mobile_app/logbook.py +++ b/homeassistant/components/mobile_app/logbook.py @@ -3,6 +3,12 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_ICON, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON from homeassistant.core import Event, HomeAssistant, callback @@ -42,12 +48,12 @@ def async_describe_events( zone_name = zone_state.attributes.get(ATTR_FRIENDLY_NAME) zone_icon = zone_state.attributes.get(ATTR_ICON) description = { - "name": source_device_name, - "message": f"{event_description} {zone_name or zone_entity_id}", - "icon": zone_icon or "mdi:crosshairs-gps", + LOGBOOK_ENTRY_NAME: source_device_name, + LOGBOOK_ENTRY_MESSAGE: f"{event_description} {zone_name or zone_entity_id}", + LOGBOOK_ENTRY_ICON: zone_icon or "mdi:crosshairs-gps", } if zone_entity_id: - description["entity_id"] = zone_entity_id + description[LOGBOOK_ENTRY_ENTITY_ID] = zone_entity_id return description async_describe_event(DOMAIN, IOS_EVENT_ZONE_ENTERED, async_describe_zone_event) diff --git a/homeassistant/components/script/logbook.py b/homeassistant/components/script/logbook.py index 250b7231b32..7fcbad07479 100644 --- a/homeassistant/components/script/logbook.py +++ b/homeassistant/components/script/logbook.py @@ -1,4 +1,10 @@ """Describe logbook events.""" +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_CONTEXT_ID, + LOGBOOK_ENTRY_ENTITY_ID, + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME from homeassistant.core import callback @@ -14,10 +20,10 @@ def async_describe_events(hass, async_describe_event): """Describe the logbook event.""" data = event.data return { - "name": data.get(ATTR_NAME), - "message": "started", - "entity_id": data.get(ATTR_ENTITY_ID), - "context_id": event.context_id, + LOGBOOK_ENTRY_NAME: data.get(ATTR_NAME), + LOGBOOK_ENTRY_MESSAGE: "started", + LOGBOOK_ENTRY_ENTITY_ID: data.get(ATTR_ENTITY_ID), + LOGBOOK_ENTRY_CONTEXT_ID: event.context_id, } async_describe_event(DOMAIN, EVENT_SCRIPT_STARTED, async_describe_logbook_event) diff --git a/homeassistant/components/shelly/logbook.py b/homeassistant/components/shelly/logbook.py index 504dfe90791..a91f4e1cf56 100644 --- a/homeassistant/components/shelly/logbook.py +++ b/homeassistant/components/shelly/logbook.py @@ -3,6 +3,10 @@ from __future__ import annotations from collections.abc import Callable +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) from homeassistant.const import ATTR_DEVICE_ID from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.typing import EventType @@ -48,8 +52,8 @@ def async_describe_events( input_name = f"{device_name} channel {channel}" return { - "name": "Shelly", - "message": f"'{click_type}' click event for {input_name} Input was fired", + LOGBOOK_ENTRY_NAME: "Shelly", + LOGBOOK_ENTRY_MESSAGE: f"'{click_type}' click event for {input_name} Input was fired", } async_describe_event(DOMAIN, EVENT_SHELLY_CLICK, async_describe_shelly_click_event) diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 585732b66f1..e1fc1defe09 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -8,7 +8,7 @@ import pytest from homeassistant import core from homeassistant.components import logbook, recorder -from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED +from homeassistant.components.automation import ATTR_SOURCE, EVENT_AUTOMATION_TRIGGERED from homeassistant.components.logbook import websocket_api from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.websocket_api.const import TYPE_RESULT @@ -535,11 +535,19 @@ async def test_subscribe_unsubscribe_logbook_stream( hass.bus.async_fire( EVENT_AUTOMATION_TRIGGERED, - {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: "automation.mock_automation"}, + { + ATTR_NAME: "Mock automation", + ATTR_ENTITY_ID: "automation.mock_automation", + ATTR_SOURCE: "numeric state of sensor.hungry_dogs", + }, ) hass.bus.async_fire( EVENT_SCRIPT_STARTED, - {ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"}, + { + ATTR_NAME: "Mock script", + ATTR_ENTITY_ID: "script.mock_script", + ATTR_SOURCE: "numeric state of sensor.hungry_dogs", + }, ) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() @@ -552,9 +560,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_id": ANY, "domain": "automation", "entity_id": "automation.mock_automation", - "message": "triggered", + "message": "triggered by numeric state of sensor.hungry_dogs", "name": "Mock automation", - "source": None, + "source": "numeric state of sensor.hungry_dogs", "when": ANY, }, { @@ -581,7 +589,11 @@ async def test_subscribe_unsubscribe_logbook_stream( automation_entity_id_test = "automation.alarm" hass.bus.async_fire( EVENT_AUTOMATION_TRIGGERED, - {ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: automation_entity_id_test}, + { + ATTR_NAME: "Mock automation", + ATTR_ENTITY_ID: automation_entity_id_test, + ATTR_SOURCE: "state of binary_sensor.dog_food_ready", + }, context=context, ) hass.bus.async_fire( @@ -613,9 +625,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_user_id": "b400facee45711eaa9308bfd3d19e474", "domain": "automation", "entity_id": "automation.alarm", - "message": "triggered", + "message": "triggered by state of binary_sensor.dog_food_ready", "name": "Mock automation", - "source": None, + "source": "state of binary_sensor.dog_food_ready", "when": ANY, }, { @@ -623,8 +635,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_entity_id": "automation.alarm", "context_event_type": "automation_triggered", "context_id": "ac5bd62de45711eaaeb351041eec8dd9", - "context_message": "triggered", + "context_message": "triggered by state of " "binary_sensor.dog_food_ready", "context_name": "Mock automation", + "context_source": "state of binary_sensor.dog_food_ready", "context_user_id": "b400facee45711eaa9308bfd3d19e474", "domain": "script", "entity_id": "script.mock_script", @@ -636,8 +649,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_domain": "automation", "context_entity_id": "automation.alarm", "context_event_type": "automation_triggered", - "context_message": "triggered", + "context_message": "triggered by state of " "binary_sensor.dog_food_ready", "context_name": "Mock automation", + "context_source": "state of binary_sensor.dog_food_ready", "context_user_id": "b400facee45711eaa9308bfd3d19e474", "entity_id": "alarm_control_panel.area_001", "state": "on", @@ -647,8 +661,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_domain": "automation", "context_entity_id": "automation.alarm", "context_event_type": "automation_triggered", - "context_message": "triggered", + "context_message": "triggered by state of " "binary_sensor.dog_food_ready", "context_name": "Mock automation", + "context_source": "state of binary_sensor.dog_food_ready", "context_user_id": "b400facee45711eaa9308bfd3d19e474", "entity_id": "alarm_control_panel.area_002", "state": "on", @@ -672,8 +687,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_entity_id": "automation.alarm", "context_event_type": "automation_triggered", "context_id": "ac5bd62de45711eaaeb351041eec8dd9", - "context_message": "triggered", + "context_message": "triggered by state of binary_sensor.dog_food_ready", "context_name": "Mock automation", + "context_source": "state of binary_sensor.dog_food_ready", "context_user_id": "b400facee45711eaa9308bfd3d19e474", "domain": "automation", "entity_id": "automation.alarm", @@ -701,8 +717,9 @@ async def test_subscribe_unsubscribe_logbook_stream( "context_entity_id": "automation.alarm", "context_event_type": "automation_triggered", "context_id": "ac5bd62de45711eaaeb351041eec8dd9", - "context_message": "triggered", + "context_message": "triggered by state of binary_sensor.dog_food_ready", "context_name": "Mock automation", + "context_source": "state of binary_sensor.dog_food_ready", "context_user_id": "b400facee45711eaa9308bfd3d19e474", "domain": "automation", "entity_id": "automation.alarm", From 53d7eaa1a6f77a590886be7a413ec00e8b6c3dae Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 23 May 2022 20:50:39 +0200 Subject: [PATCH 758/930] Update Pillow to 9.1.1 (#72376) --- homeassistant/components/doods/manifest.json | 2 +- homeassistant/components/generic/manifest.json | 2 +- homeassistant/components/image/manifest.json | 2 +- homeassistant/components/proxy/manifest.json | 2 +- homeassistant/components/qrcode/manifest.json | 2 +- homeassistant/components/seven_segments/manifest.json | 2 +- homeassistant/components/sighthound/manifest.json | 2 +- homeassistant/components/tensorflow/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/doods/manifest.json b/homeassistant/components/doods/manifest.json index 2ea550c3792..ec170733e44 100644 --- a/homeassistant/components/doods/manifest.json +++ b/homeassistant/components/doods/manifest.json @@ -2,7 +2,7 @@ "domain": "doods", "name": "DOODS - Dedicated Open Object Detection Service", "documentation": "https://www.home-assistant.io/integrations/doods", - "requirements": ["pydoods==1.0.2", "pillow==9.1.0"], + "requirements": ["pydoods==1.0.2", "pillow==9.1.1"], "codeowners": [], "iot_class": "local_polling", "loggers": ["pydoods"] diff --git a/homeassistant/components/generic/manifest.json b/homeassistant/components/generic/manifest.json index ae14c3c4d03..c590ddfffcd 100644 --- a/homeassistant/components/generic/manifest.json +++ b/homeassistant/components/generic/manifest.json @@ -2,7 +2,7 @@ "domain": "generic", "name": "Generic Camera", "config_flow": true, - "requirements": ["av==9.2.0", "pillow==9.1.0"], + "requirements": ["av==9.2.0", "pillow==9.1.1"], "documentation": "https://www.home-assistant.io/integrations/generic", "codeowners": ["@davet2001"], "iot_class": "local_push" diff --git a/homeassistant/components/image/manifest.json b/homeassistant/components/image/manifest.json index 4ddba8f31e0..d2e08b77b23 100644 --- a/homeassistant/components/image/manifest.json +++ b/homeassistant/components/image/manifest.json @@ -3,7 +3,7 @@ "name": "Image", "config_flow": false, "documentation": "https://www.home-assistant.io/integrations/image", - "requirements": ["pillow==9.1.0"], + "requirements": ["pillow==9.1.1"], "dependencies": ["http"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal" diff --git a/homeassistant/components/proxy/manifest.json b/homeassistant/components/proxy/manifest.json index ff3e610323f..d12f5ed92fd 100644 --- a/homeassistant/components/proxy/manifest.json +++ b/homeassistant/components/proxy/manifest.json @@ -2,6 +2,6 @@ "domain": "proxy", "name": "Camera Proxy", "documentation": "https://www.home-assistant.io/integrations/proxy", - "requirements": ["pillow==9.1.0"], + "requirements": ["pillow==9.1.1"], "codeowners": [] } diff --git a/homeassistant/components/qrcode/manifest.json b/homeassistant/components/qrcode/manifest.json index 66a50bf98d9..6715d1ba6db 100644 --- a/homeassistant/components/qrcode/manifest.json +++ b/homeassistant/components/qrcode/manifest.json @@ -2,7 +2,7 @@ "domain": "qrcode", "name": "QR Code", "documentation": "https://www.home-assistant.io/integrations/qrcode", - "requirements": ["pillow==9.1.0", "pyzbar==0.1.7"], + "requirements": ["pillow==9.1.1", "pyzbar==0.1.7"], "codeowners": [], "iot_class": "calculated", "loggers": ["pyzbar"] diff --git a/homeassistant/components/seven_segments/manifest.json b/homeassistant/components/seven_segments/manifest.json index b5a83dfd747..caee1d72c38 100644 --- a/homeassistant/components/seven_segments/manifest.json +++ b/homeassistant/components/seven_segments/manifest.json @@ -2,7 +2,7 @@ "domain": "seven_segments", "name": "Seven Segments OCR", "documentation": "https://www.home-assistant.io/integrations/seven_segments", - "requirements": ["pillow==9.1.0"], + "requirements": ["pillow==9.1.1"], "codeowners": ["@fabaff"], "iot_class": "local_polling" } diff --git a/homeassistant/components/sighthound/manifest.json b/homeassistant/components/sighthound/manifest.json index 3afe8052e03..e87e1d37304 100644 --- a/homeassistant/components/sighthound/manifest.json +++ b/homeassistant/components/sighthound/manifest.json @@ -2,7 +2,7 @@ "domain": "sighthound", "name": "Sighthound", "documentation": "https://www.home-assistant.io/integrations/sighthound", - "requirements": ["pillow==9.1.0", "simplehound==0.3"], + "requirements": ["pillow==9.1.1", "simplehound==0.3"], "codeowners": ["@robmarkcole"], "iot_class": "cloud_polling", "loggers": ["simplehound"] diff --git a/homeassistant/components/tensorflow/manifest.json b/homeassistant/components/tensorflow/manifest.json index 0f53dd61cb4..efd4f3d76d0 100644 --- a/homeassistant/components/tensorflow/manifest.json +++ b/homeassistant/components/tensorflow/manifest.json @@ -7,7 +7,7 @@ "tf-models-official==2.5.0", "pycocotools==2.0.1", "numpy==1.21.6", - "pillow==9.1.0" + "pillow==9.1.1" ], "codeowners": [], "iot_class": "local_polling", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 354b06d2c10..e4bed71458f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -21,7 +21,7 @@ ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.7 paho-mqtt==1.6.1 -pillow==9.1.0 +pillow==9.1.1 pip>=21.0,<22.2 pyserial==3.5 python-slugify==4.0.1 diff --git a/requirements_all.txt b/requirements_all.txt index ca2028b8eaa..12bd9a705b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1230,7 +1230,7 @@ pilight==0.1.1 # homeassistant.components.seven_segments # homeassistant.components.sighthound # homeassistant.components.tensorflow -pillow==9.1.0 +pillow==9.1.1 # homeassistant.components.dominos pizzapi==0.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 25270998ab4..1b392014a50 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -835,7 +835,7 @@ pilight==0.1.1 # homeassistant.components.seven_segments # homeassistant.components.sighthound # homeassistant.components.tensorflow -pillow==9.1.0 +pillow==9.1.1 # homeassistant.components.plex plexapi==4.11.1 From 52808562ab236ada838b94e4f17cad420ffc55aa Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 23 May 2022 21:32:03 +0200 Subject: [PATCH 759/930] Improve DEVICE_TRIGGERS typing in tasmota (#72149) * Improve DEVICE_TRIGGERS typing in tasmota * fix CI * Simplify * Simplify some more --- .../components/tasmota/device_trigger.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/tasmota/device_trigger.py b/homeassistant/components/tasmota/device_trigger.py index 26b1d9debf1..69cb3a0bd72 100644 --- a/homeassistant/components/tasmota/device_trigger.py +++ b/homeassistant/components/tasmota/device_trigger.py @@ -188,11 +188,13 @@ async def async_setup_trigger( _LOGGER.debug( "Got update for trigger with hash: %s '%s'", discovery_hash, trigger_config ) + device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS] if not trigger_config.is_active: # Empty trigger_config: Remove trigger _LOGGER.debug("Removing trigger: %s", discovery_hash) - if discovery_id in hass.data[DEVICE_TRIGGERS]: - device_trigger = hass.data[DEVICE_TRIGGERS][discovery_id] + if discovery_id in device_triggers: + device_trigger = device_triggers[discovery_id] + assert device_trigger.tasmota_trigger await device_trigger.tasmota_trigger.unsubscribe_topics() device_trigger.detach_trigger() clear_discovery_hash(hass, discovery_hash) @@ -200,7 +202,8 @@ async def async_setup_trigger( remove_update_signal() return - device_trigger = hass.data[DEVICE_TRIGGERS][discovery_id] + device_trigger = device_triggers[discovery_id] + assert device_trigger.tasmota_trigger if device_trigger.tasmota_trigger.config_same(trigger_config): # Unchanged payload: Ignore to avoid unnecessary unsubscribe / subscribe _LOGGER.debug("Ignoring unchanged update for: %s", discovery_hash) @@ -209,6 +212,7 @@ async def async_setup_trigger( # Non-empty, changed trigger_config: Update trigger _LOGGER.debug("Updating trigger: %s", discovery_hash) device_trigger.tasmota_trigger.config_update(trigger_config) + assert remove_update_signal await device_trigger.update_tasmota_trigger( trigger_config, remove_update_signal ) @@ -230,7 +234,8 @@ async def async_setup_trigger( if DEVICE_TRIGGERS not in hass.data: hass.data[DEVICE_TRIGGERS] = {} - if discovery_id not in hass.data[DEVICE_TRIGGERS]: + device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS] + if discovery_id not in device_triggers: device_trigger = Trigger( hass=hass, device_id=device.id, @@ -240,10 +245,10 @@ async def async_setup_trigger( type=tasmota_trigger.cfg.type, remove_update_signal=remove_update_signal, ) - hass.data[DEVICE_TRIGGERS][discovery_id] = device_trigger + device_triggers[discovery_id] = device_trigger else: # This Tasmota trigger is wanted by device trigger(s), set them up - device_trigger = hass.data[DEVICE_TRIGGERS][discovery_id] + device_trigger = device_triggers[discovery_id] await device_trigger.set_tasmota_trigger(tasmota_trigger, remove_update_signal) await device_trigger.arm_tasmota_trigger() @@ -251,14 +256,20 @@ async def async_setup_trigger( async def async_remove_triggers(hass: HomeAssistant, device_id: str) -> None: """Cleanup any device triggers for a Tasmota device.""" triggers = await async_get_triggers(hass, device_id) + + if not triggers: + return + device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS] for trig in triggers: - device_trigger = hass.data[DEVICE_TRIGGERS].pop(trig[CONF_DISCOVERY_ID]) + device_trigger = device_triggers.pop(trig[CONF_DISCOVERY_ID]) if device_trigger: discovery_hash = device_trigger.discovery_hash + assert device_trigger.tasmota_trigger await device_trigger.tasmota_trigger.unsubscribe_topics() device_trigger.detach_trigger() clear_discovery_hash(hass, discovery_hash) + assert device_trigger.remove_update_signal device_trigger.remove_update_signal() @@ -271,7 +282,8 @@ async def async_get_triggers( if DEVICE_TRIGGERS not in hass.data: return triggers - for discovery_id, trig in hass.data[DEVICE_TRIGGERS].items(): + device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS] + for discovery_id, trig in device_triggers.items(): if trig.device_id != device_id or trig.tasmota_trigger is None: continue @@ -297,12 +309,13 @@ async def async_attach_trigger( """Attach a device trigger.""" if DEVICE_TRIGGERS not in hass.data: hass.data[DEVICE_TRIGGERS] = {} + device_triggers: dict[str, Trigger] = hass.data[DEVICE_TRIGGERS] device_id = config[CONF_DEVICE_ID] discovery_id = config[CONF_DISCOVERY_ID] - if discovery_id not in hass.data[DEVICE_TRIGGERS]: + if discovery_id not in device_triggers: # The trigger has not (yet) been discovered, prepare it for later - hass.data[DEVICE_TRIGGERS][discovery_id] = Trigger( + device_triggers[discovery_id] = Trigger( hass=hass, device_id=device_id, discovery_hash=None, @@ -311,5 +324,5 @@ async def async_attach_trigger( subtype=config[CONF_SUBTYPE], tasmota_trigger=None, ) - trigger: Trigger = hass.data[DEVICE_TRIGGERS][discovery_id] + trigger: Trigger = device_triggers[discovery_id] return await trigger.add_trigger(action, automation_info) From 9d95b9ab053b2106dc99f58805a2bfb862a2fb93 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 May 2022 17:40:00 -0500 Subject: [PATCH 760/930] Chunk large logbook queries and add an end_time to the api so we stop sending events (#72351) --- homeassistant/components/logbook/models.py | 2 +- .../components/logbook/websocket_api.py | 206 +++++++++-- .../components/logbook/test_websocket_api.py | 340 ++++++++++++++++-- 3 files changed, 483 insertions(+), 65 deletions(-) diff --git a/homeassistant/components/logbook/models.py b/homeassistant/components/logbook/models.py index e4844751c6a..591781745f7 100644 --- a/homeassistant/components/logbook/models.py +++ b/homeassistant/components/logbook/models.py @@ -59,7 +59,7 @@ class LazyEventPartialState: ) -@dataclass +@dataclass(frozen=True) class EventAsRow: """Convert an event to a row.""" diff --git a/homeassistant/components/logbook/websocket_api.py b/homeassistant/components/logbook/websocket_api.py index efb15efe97b..edfe3177f26 100644 --- a/homeassistant/components/logbook/websocket_api.py +++ b/homeassistant/components/logbook/websocket_api.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable +from dataclasses import dataclass from datetime import datetime as dt, timedelta import logging from typing import Any @@ -15,6 +16,7 @@ from homeassistant.components.websocket_api import messages from homeassistant.components.websocket_api.connection import ActiveConnection from homeassistant.components.websocket_api.const import JSON_DUMP from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback +from homeassistant.helpers.event import async_track_point_in_utc_time import homeassistant.util.dt as dt_util from .helpers import ( @@ -26,12 +28,26 @@ from .models import async_event_to_row from .processor import EventProcessor MAX_PENDING_LOGBOOK_EVENTS = 2048 -EVENT_COALESCE_TIME = 0.5 +EVENT_COALESCE_TIME = 0.35 MAX_RECORDER_WAIT = 10 +# minimum size that we will split the query +BIG_QUERY_HOURS = 25 +# how many hours to deliver in the first chunk when we split the query +BIG_QUERY_RECENT_HOURS = 24 _LOGGER = logging.getLogger(__name__) +@dataclass +class LogbookLiveStream: + """Track a logbook live stream.""" + + stream_queue: asyncio.Queue[Event] + subscriptions: list[CALLBACK_TYPE] + end_time_unsub: CALLBACK_TYPE | None = None + task: asyncio.Task | None = None + + @callback def async_setup(hass: HomeAssistant) -> None: """Set up the logbook websocket API.""" @@ -39,6 +55,94 @@ def async_setup(hass: HomeAssistant) -> None: websocket_api.async_register_command(hass, ws_event_stream) +async def _async_wait_for_recorder_sync(hass: HomeAssistant) -> None: + """Wait for the recorder to sync.""" + try: + await asyncio.wait_for( + get_instance(hass).async_block_till_done(), MAX_RECORDER_WAIT + ) + except asyncio.TimeoutError: + _LOGGER.debug( + "Recorder is behind more than %s seconds, starting live stream; Some results may be missing" + ) + + +async def _async_send_historical_events( + hass: HomeAssistant, + connection: ActiveConnection, + msg_id: int, + start_time: dt, + end_time: dt, + formatter: Callable[[int, Any], dict[str, Any]], + event_processor: EventProcessor, +) -> dt | None: + """Select historical data from the database and deliver it to the websocket. + + If the query is considered a big query we will split the request into + two chunks so that they get the recent events first and the select + that is expected to take a long time comes in after to ensure + they are not stuck at a loading screen and can start looking at + the data right away. + + This function returns the time of the most recent event we sent to the + websocket. + """ + is_big_query = ( + not event_processor.entity_ids + and not event_processor.device_ids + and ((end_time - start_time) > timedelta(hours=BIG_QUERY_HOURS)) + ) + + if not is_big_query: + message, last_event_time = await _async_get_ws_formatted_events( + hass, + msg_id, + start_time, + end_time, + formatter, + event_processor, + ) + # If there is no last_time, there are no historical + # results, but we still send an empty message so + # consumers of the api know their request was + # answered but there were no results + connection.send_message(message) + return last_event_time + + # This is a big query so we deliver + # the first three hours and then + # we fetch the old data + recent_query_start = end_time - timedelta(hours=BIG_QUERY_RECENT_HOURS) + recent_message, recent_query_last_event_time = await _async_get_ws_formatted_events( + hass, + msg_id, + recent_query_start, + end_time, + formatter, + event_processor, + ) + if recent_query_last_event_time: + connection.send_message(recent_message) + + older_message, older_query_last_event_time = await _async_get_ws_formatted_events( + hass, + msg_id, + start_time, + recent_query_start, + formatter, + event_processor, + ) + # If there is no last_time, there are no historical + # results, but we still send an empty message so + # consumers of the api know their request was + # answered but there were no results + if older_query_last_event_time or not recent_query_last_event_time: + connection.send_message(older_message) + + # Returns the time of the newest event + return recent_query_last_event_time or older_query_last_event_time + + async def _async_get_ws_formatted_events( hass: HomeAssistant, msg_id: int, @@ -75,14 +179,13 @@ def _ws_formatted_get_events( async def _async_events_consumer( - setup_complete_future: asyncio.Future[dt], + subscriptions_setup_complete_time: dt, connection: ActiveConnection, msg_id: int, stream_queue: asyncio.Queue[Event], event_processor: EventProcessor, ) -> None: """Stream events from the queue.""" - subscriptions_setup_complete_time = await setup_complete_future event_processor.switch_to_live() while True: @@ -116,6 +219,7 @@ async def _async_events_consumer( { vol.Required("type"): "logbook/event_stream", vol.Required("start_time"): str, + vol.Optional("end_time"): str, vol.Optional("entity_ids"): [str], vol.Optional("device_ids"): [str], } @@ -126,21 +230,32 @@ async def ws_event_stream( ) -> None: """Handle logbook stream events websocket command.""" start_time_str = msg["start_time"] + msg_id: int = msg["id"] utc_now = dt_util.utcnow() if start_time := dt_util.parse_datetime(start_time_str): start_time = dt_util.as_utc(start_time) if not start_time or start_time > utc_now: - connection.send_error(msg["id"], "invalid_start_time", "Invalid start_time") + connection.send_error(msg_id, "invalid_start_time", "Invalid start_time") return + end_time_str = msg.get("end_time") + end_time: dt | None = None + if end_time_str: + if not (end_time := dt_util.parse_datetime(end_time_str)): + connection.send_error(msg_id, "invalid_end_time", "Invalid end_time") + return + end_time = dt_util.as_utc(end_time) + if end_time < start_time: + connection.send_error(msg_id, "invalid_end_time", "Invalid end_time") + return + device_ids = msg.get("device_ids") entity_ids = msg.get("entity_ids") if entity_ids: entity_ids = async_filter_entities(hass, entity_ids) event_types = async_determine_event_types(hass, entity_ids, device_ids) - event_processor = EventProcessor( hass, event_types, @@ -151,26 +266,43 @@ async def ws_event_stream( include_entity_name=False, ) - stream_queue: asyncio.Queue[Event] = asyncio.Queue(MAX_PENDING_LOGBOOK_EVENTS) - subscriptions: list[CALLBACK_TYPE] = [] - setup_complete_future: asyncio.Future[dt] = asyncio.Future() - task = asyncio.create_task( - _async_events_consumer( - setup_complete_future, + if end_time and end_time <= utc_now: + # Not live stream but we it might be a big query + connection.subscriptions[msg_id] = callback(lambda: None) + connection.send_result(msg_id) + # Fetch everything from history + await _async_send_historical_events( + hass, connection, - msg["id"], - stream_queue, + msg_id, + start_time, + end_time, + messages.event_message, event_processor, ) + return + + subscriptions: list[CALLBACK_TYPE] = [] + stream_queue: asyncio.Queue[Event] = asyncio.Queue(MAX_PENDING_LOGBOOK_EVENTS) + live_stream = LogbookLiveStream( + subscriptions=subscriptions, stream_queue=stream_queue ) - def _unsub() -> None: + @callback + def _unsub(*time: Any) -> None: """Unsubscribe from all events.""" for subscription in subscriptions: subscription() subscriptions.clear() - if task: - task.cancel() + if live_stream.task: + live_stream.task.cancel() + if live_stream.end_time_unsub: + live_stream.end_time_unsub() + + if end_time: + live_stream.end_time_unsub = async_track_point_in_utc_time( + hass, _unsub, end_time + ) @callback def _queue_or_cancel(event: Event) -> None: @@ -188,33 +320,21 @@ async def ws_event_stream( hass, subscriptions, _queue_or_cancel, event_types, entity_ids, device_ids ) subscriptions_setup_complete_time = dt_util.utcnow() - connection.subscriptions[msg["id"]] = _unsub - connection.send_result(msg["id"]) - + connection.subscriptions[msg_id] = _unsub + connection.send_result(msg_id) # Fetch everything from history - message, last_event_time = await _async_get_ws_formatted_events( + last_event_time = await _async_send_historical_events( hass, - msg["id"], + connection, + msg_id, start_time, subscriptions_setup_complete_time, messages.event_message, event_processor, ) - # If there is no last_time there are no historical - # results, but we still send an empty message so - # consumers of the api know their request was - # answered but there were no results - connection.send_message(message) - try: - await asyncio.wait_for( - get_instance(hass).async_block_till_done(), MAX_RECORDER_WAIT - ) - except asyncio.TimeoutError: - _LOGGER.debug( - "Recorder is behind more than %s seconds, starting live stream; Some results may be missing" - ) - if setup_complete_future.cancelled(): + await _async_wait_for_recorder_sync(hass) + if not subscriptions: # Unsubscribe happened while waiting for recorder return @@ -235,7 +355,7 @@ async def ws_event_stream( ) message, final_cutoff_time = await _async_get_ws_formatted_events( hass, - msg["id"], + msg_id, second_fetch_start_time, subscriptions_setup_complete_time, messages.event_message, @@ -244,9 +364,19 @@ async def ws_event_stream( if final_cutoff_time: # Only sends results if we have them connection.send_message(message) - if not setup_complete_future.cancelled(): + if not subscriptions: # Unsubscribe happened while waiting for formatted events - setup_complete_future.set_result(subscriptions_setup_complete_time) + return + + live_stream.task = asyncio.create_task( + _async_events_consumer( + subscriptions_setup_complete_time, + connection, + msg_id, + stream_queue, + event_processor, + ) + ) @websocket_api.websocket_command( diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index e1fc1defe09..26ad7127296 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -4,6 +4,7 @@ from collections.abc import Callable from datetime import timedelta from unittest.mock import ANY, patch +from freezegun import freeze_time import pytest from homeassistant import core @@ -26,7 +27,11 @@ from homeassistant.helpers import device_registry from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util -from tests.common import MockConfigEntry, SetupRecorderInstanceT +from tests.common import ( + MockConfigEntry, + SetupRecorderInstanceT, + async_fire_time_changed, +) from tests.components.recorder.common import ( async_block_recorder, async_recorder_block_till_done, @@ -479,12 +484,12 @@ async def test_subscribe_unsubscribe_logbook_stream( {"id": 7, "type": "logbook/event_stream", "start_time": now.isoformat()} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -512,7 +517,7 @@ async def test_subscribe_unsubscribe_logbook_stream( hass.states.async_set("light.zulu", "on", {"effect": "help", "color": "blue"}) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -552,7 +557,7 @@ async def test_subscribe_unsubscribe_logbook_stream( hass.bus.async_fire(EVENT_HOMEASSISTANT_START) await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -616,7 +621,7 @@ async def test_subscribe_unsubscribe_logbook_stream( await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -733,7 +738,7 @@ async def test_subscribe_unsubscribe_logbook_stream( await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 8 assert msg["type"] == TYPE_RESULT @@ -775,12 +780,12 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( } ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -797,7 +802,7 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -815,7 +820,258 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_entities_with_end_time( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with specific entities and an end_time.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "end_time": (now + timedelta(minutes=10)).isoformat(), + "entity_ids": ["light.small", "binary_sensor.is_light"], + } + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + + hass.states.async_set("light.alpha", STATE_ON) + hass.states.async_set("light.alpha", STATE_OFF) + hass.states.async_set("light.small", STATE_OFF, {"effect": "help", "color": "blue"}) + + await hass.async_block_till_done() + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "light.small", + "state": "off", + "when": ANY, + }, + ] + + hass.states.async_remove("light.alpha") + hass.states.async_remove("light.small") + await hass.async_block_till_done() + + async_fire_time_changed(hass, now + timedelta(minutes=11)) + await hass.async_block_till_done() + + # These states should not be sent since we should be unsubscribed + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("light.small", STATE_OFF) + await hass.async_block_till_done() + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) <= init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_entities_past_only( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with specific entities in the past.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "end_time": (dt_util.utcnow() - timedelta(microseconds=1)).isoformat(), + "entity_ids": ["light.small", "binary_sensor.is_light"], + } + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + + # These states should not be sent since we should be unsubscribed + # since we only asked for the past + hass.states.async_set("light.small", STATE_ON) + hass.states.async_set("light.small", STATE_OFF) + await hass.async_block_till_done() + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_big_query( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream and ask for a large time frame. + + We should get the data for the first 24 hours in the first message, and + anything older will come in a followup message. + """ + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + four_days_ago = now - timedelta(days=4) + five_days_ago = now - timedelta(days=5) + + with freeze_time(four_days_ago): + hass.states.async_set("binary_sensor.four_days_ago", STATE_ON) + hass.states.async_set("binary_sensor.four_days_ago", STATE_OFF) + four_day_old_state: State = hass.states.get("binary_sensor.four_days_ago") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + # Verify our state was recorded in the past + assert (now - four_day_old_state.last_updated).total_seconds() > 86400 * 3 + + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + current_state: State = hass.states.get("binary_sensor.is_light") + + # Verify our new state was recorded in the recent timeframe + assert (now - current_state.last_updated).total_seconds() < 2 + + await async_wait_recording_done(hass) + + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": five_days_ago.isoformat(), + } + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # With a big query we get the current state first + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "on", + "when": current_state.last_updated.timestamp(), + } + ] + + # With a big query we get the old states second + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"] == [ + { + "entity_id": "binary_sensor.four_days_ago", + "state": "off", + "when": four_day_old_state.last_updated.timestamp(), + } + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 8 assert msg["type"] == TYPE_RESULT @@ -853,7 +1109,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( } ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] @@ -864,7 +1120,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( # and its not a failure case. This is useful # in the frontend so we can tell the user there # are no results vs waiting for them to appear - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [] @@ -872,7 +1128,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( hass.bus.async_fire("mock_event", {"device_id": device.id}) await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -882,7 +1138,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 8 assert msg["type"] == TYPE_RESULT @@ -910,6 +1166,38 @@ async def test_event_stream_bad_start_time(hass, hass_ws_client, recorder_mock): assert response["error"]["code"] == "invalid_start_time" +async def test_event_stream_bad_end_time(hass, hass_ws_client, recorder_mock): + """Test event_stream bad end time.""" + await async_setup_component(hass, "logbook", {}) + await async_recorder_block_till_done(hass) + utc_now = dt_util.utcnow() + + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "logbook/event_stream", + "start_time": utc_now.isoformat(), + "end_time": "cats", + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_end_time" + + await client.send_json( + { + "id": 2, + "type": "logbook/event_stream", + "start_time": utc_now.isoformat(), + "end_time": (utc_now - timedelta(hours=5)).isoformat(), + } + ) + response = await client.receive_json() + assert not response["success"] + assert response["error"]["code"] == "invalid_end_time" + + async def test_live_stream_with_one_second_commit_interval( hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT, @@ -951,7 +1239,7 @@ async def test_live_stream_with_one_second_commit_interval( ) hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "4"}) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] @@ -959,7 +1247,7 @@ async def test_live_stream_with_one_second_commit_interval( hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "5"}) recieved_rows = [] - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" recieved_rows.extend(msg["event"]) @@ -990,7 +1278,7 @@ async def test_live_stream_with_one_second_commit_interval( await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 8 assert msg["type"] == TYPE_RESULT @@ -1030,12 +1318,12 @@ async def test_subscribe_disconnected(hass, recorder_mock, hass_ws_client): } ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -1088,7 +1376,7 @@ async def test_stream_consumer_stop_processing(hass, recorder_mock, hass_ws_clie ) await async_wait_recording_done(hass) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] @@ -1135,7 +1423,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo } ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == TYPE_RESULT assert msg["success"] @@ -1146,7 +1434,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo # and its not a failure case. This is useful # in the frontend so we can tell the user there # are no results vs waiting for them to appear - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [] @@ -1154,7 +1442,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "1"}) await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -1164,7 +1452,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "2"}) await hass.async_block_till_done() - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" assert msg["event"] == [ @@ -1174,7 +1462,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) - msg = await websocket_client.receive_json() + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 8 assert msg["type"] == TYPE_RESULT From 1eaf3bb817fb6bd0649084fe91fd72ef77b77b4e Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Mon, 23 May 2022 19:01:07 -0500 Subject: [PATCH 761/930] Bump Frontend to 20220523.0 (#72397) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 215796d2df8..f6ede0676ca 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220521.0"], + "requirements": ["home-assistant-frontend==20220523.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e4bed71458f..a281d3cb30d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220521.0 +home-assistant-frontend==20220523.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 12bd9a705b2..bf4f2d556ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220521.0 +home-assistant-frontend==20220523.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1b392014a50..f735b555859 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220521.0 +home-assistant-frontend==20220523.0 # homeassistant.components.home_connect homeconnect==0.7.0 From abbfed24a4fb08de12d83cfe431b0f723938ae38 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 24 May 2022 00:25:27 +0000 Subject: [PATCH 762/930] [ci skip] Translation update --- .../components/baf/translations/ru.json | 23 ++++++ .../components/google/translations/de.json | 9 ++ .../components/google/translations/et.json | 9 ++ .../components/google/translations/fr.json | 9 ++ .../components/google/translations/id.json | 9 ++ .../components/google/translations/ja.json | 9 ++ .../components/google/translations/nl.json | 9 ++ .../components/google/translations/no.json | 9 ++ .../components/google/translations/ru.json | 9 ++ .../google/translations/zh-Hant.json | 9 ++ .../google_travel_time/translations/ja.json | 2 +- .../components/hassio/translations/de.json | 1 + .../components/hassio/translations/en.json | 1 + .../components/hassio/translations/fr.json | 1 + .../components/hassio/translations/id.json | 1 + .../components/hassio/translations/nl.json | 1 + .../components/hassio/translations/pt-BR.json | 1 + .../here_travel_time/translations/et.json | 82 +++++++++++++++++++ .../here_travel_time/translations/ja.json | 82 +++++++++++++++++++ .../here_travel_time/translations/no.json | 82 +++++++++++++++++++ .../here_travel_time/translations/ru.json | 82 +++++++++++++++++++ .../components/konnected/translations/et.json | 16 ++-- .../components/konnected/translations/id.json | 12 +-- .../components/konnected/translations/no.json | 16 ++-- .../components/konnected/translations/ru.json | 16 ++-- .../components/laundrify/translations/et.json | 25 ++++++ .../components/laundrify/translations/ja.json | 25 ++++++ .../components/laundrify/translations/no.json | 25 ++++++ .../components/laundrify/translations/ru.json | 25 ++++++ .../components/recorder/translations/de.json | 2 + .../components/recorder/translations/et.json | 2 + .../components/recorder/translations/fr.json | 2 + .../components/recorder/translations/id.json | 2 + .../components/recorder/translations/it.json | 2 + .../components/recorder/translations/ja.json | 2 + .../components/recorder/translations/nl.json | 2 + .../components/recorder/translations/no.json | 2 + .../recorder/translations/pt-BR.json | 2 + .../components/recorder/translations/ru.json | 2 + .../recorder/translations/zh-Hant.json | 2 + .../components/shelly/translations/ru.json | 1 + .../components/siren/translations/ru.json | 3 + .../steam_online/translations/de.json | 3 + .../steam_online/translations/en.json | 6 +- .../steam_online/translations/fr.json | 3 + .../steam_online/translations/id.json | 3 + .../steam_online/translations/nl.json | 3 + .../steam_online/translations/pt-BR.json | 3 + .../waze_travel_time/translations/ja.json | 2 +- .../components/yolink/translations/ru.json | 25 ++++++ 50 files changed, 639 insertions(+), 35 deletions(-) create mode 100644 homeassistant/components/baf/translations/ru.json create mode 100644 homeassistant/components/here_travel_time/translations/et.json create mode 100644 homeassistant/components/here_travel_time/translations/ja.json create mode 100644 homeassistant/components/here_travel_time/translations/no.json create mode 100644 homeassistant/components/here_travel_time/translations/ru.json create mode 100644 homeassistant/components/laundrify/translations/et.json create mode 100644 homeassistant/components/laundrify/translations/ja.json create mode 100644 homeassistant/components/laundrify/translations/no.json create mode 100644 homeassistant/components/laundrify/translations/ru.json create mode 100644 homeassistant/components/siren/translations/ru.json create mode 100644 homeassistant/components/yolink/translations/ru.json diff --git a/homeassistant/components/baf/translations/ru.json b/homeassistant/components/baf/translations/ru.json new file mode 100644 index 00000000000..e2e5d43123e --- /dev/null +++ b/homeassistant/components/baf/translations/ru.json @@ -0,0 +1,23 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant.", + "ipv6_not_supported": "IPv6 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "flow_title": "{name} - {model} ({ip_address})", + "step": { + "discovery_confirm": { + "description": "\u0425\u043e\u0442\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c {name} - {model} ({ip_address})?" + }, + "user": { + "data": { + "ip_address": "IP-\u0430\u0434\u0440\u0435\u0441" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/google/translations/de.json b/homeassistant/components/google/translations/de.json index 1b15c465497..c75cc90eb13 100644 --- a/homeassistant/components/google/translations/de.json +++ b/homeassistant/components/google/translations/de.json @@ -27,5 +27,14 @@ "title": "Integration erneut authentifizieren" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant-Zugriff auf Google Kalender" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/et.json b/homeassistant/components/google/translations/et.json index 27dfa3f5290..a115378f3a2 100644 --- a/homeassistant/components/google/translations/et.json +++ b/homeassistant/components/google/translations/et.json @@ -27,5 +27,14 @@ "title": "Taastuvasta sidumine" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistanti juurdep\u00e4\u00e4s Google'i kalendrile" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/fr.json b/homeassistant/components/google/translations/fr.json index 1224ba76c9b..06903bdff07 100644 --- a/homeassistant/components/google/translations/fr.json +++ b/homeassistant/components/google/translations/fr.json @@ -27,5 +27,14 @@ "title": "R\u00e9-authentifier l'int\u00e9gration" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Acc\u00e8s de Home Assistant \u00e0 Google Agenda" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/id.json b/homeassistant/components/google/translations/id.json index fd1002d41be..25c3e9fa1e6 100644 --- a/homeassistant/components/google/translations/id.json +++ b/homeassistant/components/google/translations/id.json @@ -27,5 +27,14 @@ "title": "Autentikasi Ulang Integrasi" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Akses Home Assistant ke Google Kalender" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/ja.json b/homeassistant/components/google/translations/ja.json index 81f2d941a73..854e7ba1961 100644 --- a/homeassistant/components/google/translations/ja.json +++ b/homeassistant/components/google/translations/ja.json @@ -27,5 +27,14 @@ "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant\u304b\u3089\u3001Google\u30ab\u30ec\u30f3\u30c0\u30fc\u3078\u306e\u30a2\u30af\u30bb\u30b9" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/nl.json b/homeassistant/components/google/translations/nl.json index baf0064ee63..419259d66f8 100644 --- a/homeassistant/components/google/translations/nl.json +++ b/homeassistant/components/google/translations/nl.json @@ -27,5 +27,14 @@ "title": "Integratie herauthenticeren" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant-toegang tot Google Agenda" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/no.json b/homeassistant/components/google/translations/no.json index 4065583192c..ef65c7fe9a5 100644 --- a/homeassistant/components/google/translations/no.json +++ b/homeassistant/components/google/translations/no.json @@ -27,5 +27,14 @@ "title": "Godkjenne integrering p\u00e5 nytt" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant tilgang til Google Kalender" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/ru.json b/homeassistant/components/google/translations/ru.json index a7db4b2f48b..51badc7226d 100644 --- a/homeassistant/components/google/translations/ru.json +++ b/homeassistant/components/google/translations/ru.json @@ -27,5 +27,14 @@ "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "\u0414\u043e\u0441\u0442\u0443\u043f Home Assistant \u043a Google \u041a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044e" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/zh-Hant.json b/homeassistant/components/google/translations/zh-Hant.json index 70e7d81c01e..988c6629af7 100644 --- a/homeassistant/components/google/translations/zh-Hant.json +++ b/homeassistant/components/google/translations/zh-Hant.json @@ -27,5 +27,14 @@ "title": "\u91cd\u65b0\u8a8d\u8b49\u6574\u5408" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant \u5b58\u53d6 Google Calendar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google_travel_time/translations/ja.json b/homeassistant/components/google_travel_time/translations/ja.json index 2fb8ae2883c..002a9cdd3b0 100644 --- a/homeassistant/components/google_travel_time/translations/ja.json +++ b/homeassistant/components/google_travel_time/translations/ja.json @@ -12,7 +12,7 @@ "api_key": "API\u30ad\u30fc", "destination": "\u76ee\u7684\u5730", "name": "\u540d\u524d", - "origin": "\u30aa\u30ea\u30b8\u30f3" + "origin": "\u539f\u70b9(Origin)" }, "description": "\u51fa\u767a\u5730\u3068\u76ee\u7684\u5730\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u3001\u4f4f\u6240\u3001\u7def\u5ea6/\u7d4c\u5ea6\u306e\u5ea7\u6a19\u3001\u307e\u305f\u306fGoogle place ID\u306e\u5f62\u5f0f\u3067\u3001\u30d1\u30a4\u30d7\u6587\u5b57\u3067\u533a\u5207\u3089\u308c\u305f1\u3064\u4ee5\u4e0a\u306e\u5834\u6240\u3092\u6307\u5b9a\u3067\u304d\u307e\u3059\u3002Google place ID\u3092\u4f7f\u7528\u3057\u3066\u5834\u6240\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\u3001ID\u306e\u524d\u306b\u3001`place_id:` \u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002" } diff --git a/homeassistant/components/hassio/translations/de.json b/homeassistant/components/hassio/translations/de.json index 99747512e97..f25ae73b423 100644 --- a/homeassistant/components/hassio/translations/de.json +++ b/homeassistant/components/hassio/translations/de.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agent-Version", "board": "Board", "disk_total": "Speicherplatz gesamt", "disk_used": "Speicherplatz genutzt", diff --git a/homeassistant/components/hassio/translations/en.json b/homeassistant/components/hassio/translations/en.json index bb5f8e6f01a..14d79f0d8d6 100644 --- a/homeassistant/components/hassio/translations/en.json +++ b/homeassistant/components/hassio/translations/en.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agent Version", "board": "Board", "disk_total": "Disk Total", "disk_used": "Disk Used", diff --git a/homeassistant/components/hassio/translations/fr.json b/homeassistant/components/hassio/translations/fr.json index 6e20e37a2b9..9a042b97e57 100644 --- a/homeassistant/components/hassio/translations/fr.json +++ b/homeassistant/components/hassio/translations/fr.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Version de l'agent", "board": "Tableau de bord", "disk_total": "Taille total du disque", "disk_used": "Taille du disque utilis\u00e9", diff --git a/homeassistant/components/hassio/translations/id.json b/homeassistant/components/hassio/translations/id.json index b87e1b47c44..250e6e7d4ad 100644 --- a/homeassistant/components/hassio/translations/id.json +++ b/homeassistant/components/hassio/translations/id.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Versi Agen", "board": "Board", "disk_total": "Total Disk", "disk_used": "Disk Digunakan", diff --git a/homeassistant/components/hassio/translations/nl.json b/homeassistant/components/hassio/translations/nl.json index e5541ff1d00..3a31897fd67 100644 --- a/homeassistant/components/hassio/translations/nl.json +++ b/homeassistant/components/hassio/translations/nl.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agent-versie", "board": "Bord", "disk_total": "Totale schijfruimte", "disk_used": "Gebruikte schijfruimte", diff --git a/homeassistant/components/hassio/translations/pt-BR.json b/homeassistant/components/hassio/translations/pt-BR.json index b157725600d..4f3e5d84ec1 100644 --- a/homeassistant/components/hassio/translations/pt-BR.json +++ b/homeassistant/components/hassio/translations/pt-BR.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Vers\u00e3o do Agent", "board": "Borda", "disk_total": "Total do disco", "disk_used": "Disco usado", diff --git a/homeassistant/components/here_travel_time/translations/et.json b/homeassistant/components/here_travel_time/translations/et.json new file mode 100644 index 00000000000..c9646af7544 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/et.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Seade on juba h\u00e4\u00e4lestatud" + }, + "error": { + "invalid_auth": "Tuvastamine nurjus", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Sihtkoha GPS koordinaadid" + }, + "title": "Vali sihtkoht" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Sihtkoha valik olemist" + }, + "title": "Vali sihtkoht" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Kasuta asukohta kaardil", + "destination_entity": "Kasuta olemit" + }, + "title": "Vali sihtkoht" + }, + "origin_coordinates": { + "data": { + "origin": "L\u00e4htekoha GPS asukoht" + }, + "title": "Vali l\u00e4htekoht" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "L\u00e4htekoha valik olemist" + }, + "title": "Vali l\u00e4htekoht" + }, + "user": { + "data": { + "api_key": "API v\u00f5ti", + "mode": "Vali reisimise viis", + "name": "Nimi" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Saabumisaeg" + }, + "title": "Vali saabumisaeg" + }, + "departure_time": { + "data": { + "departure_time": "V\u00e4ljumisaeg" + }, + "title": "Vali v\u00e4ljumisaeg" + }, + "init": { + "data": { + "route_mode": "Teekonna valik", + "traffic_mode": "S\u00f5iduvahend", + "unit_system": "\u00dchikute s\u00fcsteem" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Vali saabumisaeg", + "departure_time": "Vali v\u00e4ljumisaeg", + "no_time": "\u00c4ra seadista aega" + }, + "title": "Vali aja vorming" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/ja.json b/homeassistant/components/here_travel_time/translations/ja.json new file mode 100644 index 00000000000..6db262d829a --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/ja.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059" + }, + "error": { + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "GPS\u5ea7\u6a19\u3068\u3057\u3066\u306e\u76ee\u7684\u5730" + }, + "title": "\u76ee\u7684\u5730\u3092\u9078\u629e" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u4f7f\u7528\u3057\u305f\u76ee\u7684\u5730" + }, + "title": "\u76ee\u7684\u5730\u3092\u9078\u629e" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "\u5730\u56f3\u4e0a\u306e\u5834\u6240\u3092\u4f7f\u7528\u3059\u308b", + "destination_entity": "\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u4f7f\u7528\u3059\u308b" + }, + "title": "\u76ee\u7684\u5730\u3092\u9078\u629e" + }, + "origin_coordinates": { + "data": { + "origin": "GPS\u5ea7\u6a19\u3068\u3057\u3066\u306e\u539f\u70b9(Origin)" + }, + "title": "\u539f\u70b9(Origin)\u3092\u9078\u629e" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u3092\u4f7f\u7528\u3057\u305f\u539f\u70b9(Origin)" + }, + "title": "\u539f\u70b9(Origin)\u3092\u9078\u629e" + }, + "user": { + "data": { + "api_key": "API\u30ad\u30fc", + "mode": "\u30c8\u30e9\u30d9\u30eb\u30e2\u30fc\u30c9", + "name": "\u540d\u524d" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u5230\u7740\u6642\u523b" + }, + "title": "\u5230\u7740\u6642\u9593\u3092\u9078\u629e" + }, + "departure_time": { + "data": { + "departure_time": "\u51fa\u767a\u6642\u523b" + }, + "title": "\u51fa\u767a\u6642\u523b\u3092\u9078\u629e" + }, + "init": { + "data": { + "route_mode": "\u30eb\u30fc\u30c8\u30e2\u30fc\u30c9", + "traffic_mode": "\u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u30e2\u30fc\u30c9", + "unit_system": "\u5358\u4f4d\u30b7\u30b9\u30c6\u30e0" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "\u5230\u7740\u6642\u523b\u3092\u8a2d\u5b9a\u3059\u308b", + "departure_time": "\u51fa\u767a\u6642\u523b\u3092\u8a2d\u5b9a\u3059\u308b", + "no_time": "\u6642\u523b\u3092\u8a2d\u5b9a\u3057\u306a\u3044" + }, + "title": "\u6642\u9593\u306e\u7a2e\u985e\u3092\u9078\u629e" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/no.json b/homeassistant/components/here_travel_time/translations/no.json new file mode 100644 index 00000000000..52d4477f379 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/no.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Enheten er allerede konfigurert" + }, + "error": { + "invalid_auth": "Ugyldig godkjenning", + "unknown": "Uventet feil" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "Destinasjon som GPS-koordinater" + }, + "title": "Velg Destinasjon" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Destinasjon ved hjelp av en enhet" + }, + "title": "Velg Destinasjon" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Bruke en kartplassering", + "destination_entity": "Bruke en enhet" + }, + "title": "Velg Destinasjon" + }, + "origin_coordinates": { + "data": { + "origin": "Opprinnelse som GPS-koordinater" + }, + "title": "Velg Opprinnelse" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Opprinnelse ved hjelp av en enhet" + }, + "title": "Velg Opprinnelse" + }, + "user": { + "data": { + "api_key": "API-n\u00f8kkel", + "mode": "Reisemodus", + "name": "Navn" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Ankomsttid" + }, + "title": "Velg Ankomsttid" + }, + "departure_time": { + "data": { + "departure_time": "Avgangstid" + }, + "title": "Velg Avreisetid" + }, + "init": { + "data": { + "route_mode": "Rutemodus", + "traffic_mode": "Trafikkmodus", + "unit_system": "Enhetssystem" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Konfigurer en ankomsttid", + "departure_time": "Konfigurer en avgangstid", + "no_time": "Ikke konfigurer en tid" + }, + "title": "Velg Tidstype" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/ru.json b/homeassistant/components/here_travel_time/translations/ru.json new file mode 100644 index 00000000000..fc649d920df --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/ru.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant." + }, + "error": { + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "\u041f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 GPS-\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "\u041e\u0431\u044a\u0435\u043a\u0442 \u043f\u0443\u043d\u043a\u0442\u0430 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "\u0423\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u043a\u0430\u0440\u0442\u0435", + "destination_entity": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f" + }, + "origin_coordinates": { + "data": { + "origin": "\u041f\u0443\u043d\u043a\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 GPS-\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0443\u043d\u043a\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "\u041e\u0431\u044a\u0435\u043a\u0442 \u043f\u0443\u043d\u043a\u0442\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0443\u043d\u043a\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "user": { + "data": { + "api_key": "\u041a\u043b\u044e\u0447 API", + "mode": "\u0421\u043f\u043e\u0441\u043e\u0431 \u043f\u0435\u0440\u0435\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u0412\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u044f" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u044f" + }, + "departure_time": { + "data": { + "departure_time": "\u0412\u0440\u0435\u043c\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f" + }, + "init": { + "data": { + "route_mode": "\u0420\u0435\u0436\u0438\u043c \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430", + "traffic_mode": "\u0420\u0435\u0436\u0438\u043c \u0442\u0440\u0430\u0444\u0438\u043a\u0430", + "unit_system": "\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u0431\u044b\u0442\u0438\u044f", + "departure_time": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f", + "no_time": "\u041d\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f" + }, + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0442\u0438\u043f \u0432\u0440\u0435\u043c\u0435\u043d\u0438" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/konnected/translations/et.json b/homeassistant/components/konnected/translations/et.json index ddddea5c800..bbbb417bb9f 100644 --- a/homeassistant/components/konnected/translations/et.json +++ b/homeassistant/components/konnected/translations/et.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Vaheta avatud / suletud olek", - "name": "Nimi (valikuline)", + "name": "Nimi", "type": "Binaaranduri t\u00fc\u00fcp" }, "description": "{zone} suvandid", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Nimi (valikuline)", - "poll_interval": "P\u00e4ringute intervall (sekundites) (valikuline)", + "name": "Nimi", + "poll_interval": "P\u00e4ringute intervall (minutites)", "type": "Anduri t\u00fc\u00fcp" }, "description": "{zone} valikud", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Alista API hosti URL (valikuline)", + "api_host": "Alista API hosti URL", "blink": "Vilguts paneeli LEDi oleku muudatuse saatmisel", "discovery": "V\u00f5rgus esitatud tuvastusp\u00e4ringutele vastamine", "override_api_host": "Alista Home Assistanti API hostipaneeli vaikimisi URL" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "V\u00e4ljund sissel\u00fclitatuna", - "momentary": "Impulsi kestus (ms) (valikuline)", + "momentary": "Impulsi kestus (ms)", "more_states": "Selle tsooni t\u00e4iendavate olekute konfigureerimine", - "name": "Nimi (valikuline)", - "pause": "Paus impulsside vahel (ms) (valikuline)", - "repeat": "Korduste arv (-1 = l\u00f5pmatu) (valikuline)" + "name": "Nimi", + "pause": "Paus impulsside vahel (ms)", + "repeat": "Korduste arv (-1 = l\u00f5pmatu)" }, "description": "{tsooni} suvandid: olek {state}", "title": "Seadista l\u00fclitatav v\u00e4ljund" diff --git a/homeassistant/components/konnected/translations/id.json b/homeassistant/components/konnected/translations/id.json index 38443f483ca..0e64cb85032 100644 --- a/homeassistant/components/konnected/translations/id.json +++ b/homeassistant/components/konnected/translations/id.json @@ -48,7 +48,7 @@ "options_digital": { "data": { "name": "Nama", - "poll_interval": "Interval Polling (dalam menit) (opsional)", + "poll_interval": "Interval Polling (menit)", "type": "Jenis Sensor" }, "description": "Opsi {zone}", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Ganti URL host API (opsional)", + "api_host": "Timpa URL host API", "blink": "Kedipkan panel LED saat mengirim perubahan status", "discovery": "Tanggapi permintaan penemuan di jaringan Anda", "override_api_host": "Timpa URL panel host API Home Assistant bawaan" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Keluaran saat nyala", - "momentary": "Durasi pulsa (milidetik) (opsional)", + "momentary": "Durasi pulsa (milidetik)", "more_states": "Konfigurasikan status tambahan untuk zona ini", - "name": "Nama (opsional)", - "pause": "Jeda di antara pulsa (milidetik) (opsional)", - "repeat": "Waktu pengulangan (-1 = tak terbatas) (opsional)" + "name": "Nama", + "pause": "Jeda di antara pulsa (milidetik)", + "repeat": "Waktu pengulangan (-1 = tak terbatas)" }, "description": "Opsi {zone}: status {state}", "title": "Konfigurasikan Output yang Dapat Dialihkan" diff --git a/homeassistant/components/konnected/translations/no.json b/homeassistant/components/konnected/translations/no.json index 92d3efc4633..af9251f7c39 100644 --- a/homeassistant/components/konnected/translations/no.json +++ b/homeassistant/components/konnected/translations/no.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "Inverter \u00e5pen / lukk tilstand", - "name": "Navn (valgfritt)", + "name": "Navn", "type": "Bin\u00e6r sensortype" }, "description": "{zone}-alternativer", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "Navn (valgfritt)", - "poll_interval": "Avstemningsintervall (minutter) (valgfritt)", + "name": "Navn", + "poll_interval": "Avstemningsintervall (minutter)", "type": "Sensortype" }, "description": "{zone}-alternativer", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "Overstyre API-vert-URL (valgfritt)", + "api_host": "Overstyr API-verts-URL", "blink": "Blink p\u00e5 LED-lampen n\u00e5r du sender statusendring", "discovery": "Svar p\u00e5 oppdagelsesforesp\u00f8rsler i nettverket ditt", "override_api_host": "Overstyre standard Home Assistant API-vertspanel-URL" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "Utgang n\u00e5r den er p\u00e5", - "momentary": "Pulsvarighet (ms) (valgfritt)", + "momentary": "Pulsvarighet (ms)", "more_states": "Konfigurere flere tilstander for denne sonen", - "name": "Navn (valgfritt)", - "pause": "Pause mellom pulser (ms) (valgfritt)", - "repeat": "Tider \u00e5 gjenta (-1 = uendelig) (valgfritt)" + "name": "Navn", + "pause": "Pause mellom pulser (ms)", + "repeat": "Tider \u00e5 gjenta (-1=uendelig)" }, "description": "{zone} alternativer: tilstand {state}", "title": "Konfigurer utskiftbar utgang" diff --git a/homeassistant/components/konnected/translations/ru.json b/homeassistant/components/konnected/translations/ru.json index 79cc78e6a9a..eb93b825d04 100644 --- a/homeassistant/components/konnected/translations/ru.json +++ b/homeassistant/components/konnected/translations/ru.json @@ -39,7 +39,7 @@ "options_binary": { "data": { "inverse": "\u0418\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0435/\u0437\u0430\u043a\u0440\u044b\u0442\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", "type": "\u0422\u0438\u043f \u0431\u0438\u043d\u0430\u0440\u043d\u043e\u0433\u043e \u0441\u0435\u043d\u0441\u043e\u0440\u0430" }, "description": "\u041e\u043f\u0446\u0438\u0438 {zone}", @@ -47,8 +47,8 @@ }, "options_digital": { "data": { - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", - "poll_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 \u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "poll_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043e\u043f\u0440\u043e\u0441\u0430 (\u0432 \u043c\u0438\u043d\u0443\u0442\u0430\u0445)", "type": "\u0422\u0438\u043f \u0441\u0435\u043d\u0441\u043e\u0440\u0430" }, "description": "\u041e\u043f\u0446\u0438\u0438 {zone}", @@ -84,7 +84,7 @@ }, "options_misc": { "data": { - "api_host": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c URL \u0445\u043e\u0441\u0442\u0430 API (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "api_host": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c URL \u0445\u043e\u0441\u0442\u0430 API", "blink": "LED-\u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f", "discovery": "\u041e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0432 \u0412\u0430\u0448\u0435\u0439 \u0441\u0435\u0442\u0438", "override_api_host": "\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c URL-\u0430\u0434\u0440\u0435\u0441 \u0445\u043e\u0441\u0442-\u043f\u0430\u043d\u0435\u043b\u0438 Home Assistant API" @@ -95,11 +95,11 @@ "options_switch": { "data": { "activation": "\u0412\u044b\u0445\u043e\u0434 \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438", - "momentary": "\u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438\u043c\u043f\u0443\u043b\u044c\u0441\u0430 (\u043c\u0441) (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", + "momentary": "\u0414\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438\u043c\u043f\u0443\u043b\u044c\u0441\u0430 (\u043c\u0441)", "more_states": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u043e\u043d\u044b", - "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", - "pause": "\u041f\u0430\u0443\u0437\u0430 \u043c\u0435\u0436\u0434\u0443 \u0438\u043c\u043f\u0443\u043b\u044c\u0441\u0430\u043c\u0438 (\u043c\u0441) (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)", - "repeat": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u0439 (-1 = \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e) (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)" + "name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435", + "pause": "\u041f\u0430\u0443\u0437\u0430 \u043c\u0435\u0436\u0434\u0443 \u0438\u043c\u043f\u0443\u043b\u044c\u0441\u0430\u043c\u0438 (\u043c\u0441)", + "repeat": "\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u0439 (-1 = \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e)" }, "description": "{zone}: \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 {state}", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u043e\u0433\u043e \u0432\u044b\u0445\u043e\u0434\u0430" diff --git a/homeassistant/components/laundrify/translations/et.json b/homeassistant/components/laundrify/translations/et.json new file mode 100644 index 00000000000..cec7d1ec849 --- /dev/null +++ b/homeassistant/components/laundrify/translations/et.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Juba seadistatud. V\u00f5imalik on ainult \u00fcks seadistamine." + }, + "error": { + "cannot_connect": "\u00dchendamine nurjus", + "invalid_auth": "Tuvastamine nurjus", + "invalid_format": "Kehtetu vorming. Sisesta kui xxx-xxx.", + "unknown": "Ootamatu t\u00f5rge" + }, + "step": { + "init": { + "data": { + "code": "Auth kood (xxx-xxx)" + }, + "description": "Sisesta isiklik autentimiskood, mis kuvatakse pesumasina rakenduses." + }, + "reauth_confirm": { + "description": "laundrify sidumine tuleb uuesti autentida.", + "title": "Taastuvasta sidumine" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/ja.json b/homeassistant/components/laundrify/translations/ja.json new file mode 100644 index 00000000000..153fcf99afb --- /dev/null +++ b/homeassistant/components/laundrify/translations/ja.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5358\u4e00\u306e\u8a2d\u5b9a\u3057\u304b\u3067\u304d\u307e\u305b\u3093\u3002" + }, + "error": { + "cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f", + "invalid_auth": "\u7121\u52b9\u306a\u8a8d\u8a3c", + "invalid_format": "\u7121\u52b9\u306a\u5f62\u5f0f\u3067\u3059\u3002xxx-xxx\u3068\u3057\u3066\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc" + }, + "step": { + "init": { + "data": { + "code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9 (xxx-xxx)" + }, + "description": "Laundrify-App\u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u3001\u3042\u306a\u305f\u306e\u500b\u4eba\u8a8d\u8a3c\u30b3\u30fc\u30c9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002" + }, + "reauth_confirm": { + "description": "Laundrify\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306f\u3001\u518d\u8a8d\u8a3c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002", + "title": "\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u518d\u8a8d\u8a3c" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/no.json b/homeassistant/components/laundrify/translations/no.json new file mode 100644 index 00000000000..fc2a24414d1 --- /dev/null +++ b/homeassistant/components/laundrify/translations/no.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Allerede konfigurert. Bare \u00e9n enkelt konfigurasjon er mulig." + }, + "error": { + "cannot_connect": "Tilkobling mislyktes", + "invalid_auth": "Ugyldig godkjenning", + "invalid_format": "Ugyldig format. Vennligst spesifiser som xxx-xxx.", + "unknown": "Uventet feil" + }, + "step": { + "init": { + "data": { + "code": "Auth-kode (xxx-xxx)" + }, + "description": "Vennligst skriv inn din personlige autentiseringskode som vises i laundrify-appen." + }, + "reauth_confirm": { + "description": "Integrasjonen av vaskeri m\u00e5 autentiseres p\u00e5 nytt.", + "title": "Godkjenne integrering p\u00e5 nytt" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/ru.json b/homeassistant/components/laundrify/translations/ru.json new file mode 100644 index 00000000000..cceb04e1601 --- /dev/null +++ b/homeassistant/components/laundrify/translations/ru.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e." + }, + "error": { + "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", + "invalid_format": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0445\u0445\u0445-\u0445\u0445\u0445.", + "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." + }, + "step": { + "init": { + "data": { + "code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (xxx-xxx)" + }, + "description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043b\u0438\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 laundrify." + }, + "reauth_confirm": { + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 laundrify.", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/de.json b/homeassistant/components/recorder/translations/de.json index 27ba9f1da85..b05adc3c386 100644 --- a/homeassistant/components/recorder/translations/de.json +++ b/homeassistant/components/recorder/translations/de.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Aktuelle Startzeit der Ausf\u00fchrung", + "database_engine": "Datenbank-Engine", + "database_version": "Datenbankversion", "estimated_db_size": "Gesch\u00e4tzte Datenbankgr\u00f6\u00dfe (MiB)", "oldest_recorder_run": "\u00c4lteste Startzeit der Ausf\u00fchrung" } diff --git a/homeassistant/components/recorder/translations/et.json b/homeassistant/components/recorder/translations/et.json index 0dc6ae3ec62..b76e0f2e4da 100644 --- a/homeassistant/components/recorder/translations/et.json +++ b/homeassistant/components/recorder/translations/et.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Praegune k\u00e4ivitamise algusaeg", + "database_engine": "Andmebaasi mootor", + "database_version": "Andmebaasi versioon", "estimated_db_size": "Andmebaasi hinnanguline suurus (MB)", "oldest_recorder_run": "Vanim k\u00e4ivitamise algusaeg" } diff --git a/homeassistant/components/recorder/translations/fr.json b/homeassistant/components/recorder/translations/fr.json index 036fbdbb16c..24896a8e66f 100644 --- a/homeassistant/components/recorder/translations/fr.json +++ b/homeassistant/components/recorder/translations/fr.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution actuelle", + "database_engine": "Moteur de la base de donn\u00e9es", + "database_version": "Version de la base de donn\u00e9es", "estimated_db_size": "Taille estim\u00e9e de la base de donn\u00e9es (en Mio)", "oldest_recorder_run": "Heure de d\u00e9marrage de l'ex\u00e9cution la plus ancienne" } diff --git a/homeassistant/components/recorder/translations/id.json b/homeassistant/components/recorder/translations/id.json index ec9e87b39ab..5f391a24f04 100644 --- a/homeassistant/components/recorder/translations/id.json +++ b/homeassistant/components/recorder/translations/id.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Waktu Mulai Jalankan Saat Ini", + "database_engine": "Mesin Basis Data", + "database_version": "Versi Basis Data", "estimated_db_size": "Perkiraan Ukuran Basis Data (MiB)", "oldest_recorder_run": "Waktu Mulai Lari Terlama" } diff --git a/homeassistant/components/recorder/translations/it.json b/homeassistant/components/recorder/translations/it.json index ab0ac13772a..ed2bc900ec6 100644 --- a/homeassistant/components/recorder/translations/it.json +++ b/homeassistant/components/recorder/translations/it.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Ora di inizio esecuzione corrente", + "database_engine": "Motore del database", + "database_version": "Versione del database", "estimated_db_size": "Dimensione stimata del database (MiB)", "oldest_recorder_run": "Ora di inizio esecuzione meno recente" } diff --git a/homeassistant/components/recorder/translations/ja.json b/homeassistant/components/recorder/translations/ja.json index 8b54e4f544f..e8938f363bf 100644 --- a/homeassistant/components/recorder/translations/ja.json +++ b/homeassistant/components/recorder/translations/ja.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "\u73fe\u5728\u306e\u5b9f\u884c\u958b\u59cb\u6642\u9593", + "database_engine": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30a8\u30f3\u30b8\u30f3", + "database_version": "\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d0\u30fc\u30b8\u30e7\u30f3", "estimated_db_size": "\u63a8\u5b9a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30a4\u30ba(MiB)", "oldest_recorder_run": "\u6700\u3082\u53e4\u3044\u5b9f\u884c\u958b\u59cb\u6642\u9593" } diff --git a/homeassistant/components/recorder/translations/nl.json b/homeassistant/components/recorder/translations/nl.json index bb28428cc48..db1f5d6a2b4 100644 --- a/homeassistant/components/recorder/translations/nl.json +++ b/homeassistant/components/recorder/translations/nl.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Starttijd huidige run", + "database_engine": "Database-engine", + "database_version": "Databaseversie", "estimated_db_size": "Geschatte databasegrootte (MiB)", "oldest_recorder_run": "Starttijd oudste run" } diff --git a/homeassistant/components/recorder/translations/no.json b/homeassistant/components/recorder/translations/no.json index 982354a6e34..c2a9eae16b5 100644 --- a/homeassistant/components/recorder/translations/no.json +++ b/homeassistant/components/recorder/translations/no.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Gjeldende starttid for kj\u00f8ring", + "database_engine": "Databasemotor", + "database_version": "Database versjon", "estimated_db_size": "Estimert databasest\u00f8rrelse (MiB)", "oldest_recorder_run": "Eldste Run Start Time" } diff --git a/homeassistant/components/recorder/translations/pt-BR.json b/homeassistant/components/recorder/translations/pt-BR.json index 059dcc76779..4ebad0d43df 100644 --- a/homeassistant/components/recorder/translations/pt-BR.json +++ b/homeassistant/components/recorder/translations/pt-BR.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o atual", + "database_engine": "Motor do banco de dados", + "database_version": "Vers\u00e3o do banco de dados", "estimated_db_size": "Tamanho estimado do banco de dados (MiB)", "oldest_recorder_run": "Hora de in\u00edcio de execu\u00e7\u00e3o mais antiga" } diff --git a/homeassistant/components/recorder/translations/ru.json b/homeassistant/components/recorder/translations/ru.json index a637d37f720..052d442f15c 100644 --- a/homeassistant/components/recorder/translations/ru.json +++ b/homeassistant/components/recorder/translations/ru.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0446\u0438\u043a\u043b\u0430", + "database_engine": "\u0414\u0432\u0438\u0436\u043e\u043a \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", + "database_version": "\u0412\u0435\u0440\u0441\u0438\u044f \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445", "estimated_db_size": "\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (MiB)", "oldest_recorder_run": "\u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430" } diff --git a/homeassistant/components/recorder/translations/zh-Hant.json b/homeassistant/components/recorder/translations/zh-Hant.json index d9525c0826d..53715af2e92 100644 --- a/homeassistant/components/recorder/translations/zh-Hant.json +++ b/homeassistant/components/recorder/translations/zh-Hant.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "\u76ee\u524d\u57f7\u884c\u958b\u59cb\u6642\u9593", + "database_engine": "\u8cc7\u6599\u5eab\u5f15\u64ce", + "database_version": "\u8cc7\u6599\u5eab\u7248\u672c", "estimated_db_size": "\u9810\u4f30\u8cc7\u6599\u5eab\u6a94\u6848\u5927\u5c0f(MiB)", "oldest_recorder_run": "\u6700\u65e9\u57f7\u884c\u958b\u59cb\u6642\u9593" } diff --git a/homeassistant/components/shelly/translations/ru.json b/homeassistant/components/shelly/translations/ru.json index d3f38aa9eeb..b80e58aa905 100644 --- a/homeassistant/components/shelly/translations/ru.json +++ b/homeassistant/components/shelly/translations/ru.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.", + "firmware_not_fully_provisioned": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e \u043d\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044c \u0432 \u0441\u043b\u0443\u0436\u0431\u0443 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 Shelly", "invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.", "unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430." }, diff --git a/homeassistant/components/siren/translations/ru.json b/homeassistant/components/siren/translations/ru.json new file mode 100644 index 00000000000..bd7725464a7 --- /dev/null +++ b/homeassistant/components/siren/translations/ru.json @@ -0,0 +1,3 @@ +{ + "title": "\u0421\u0438\u0440\u0435\u043d\u0430" +} \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/de.json b/homeassistant/components/steam_online/translations/de.json index fe73cf71448..44fb7f8b08b 100644 --- a/homeassistant/components/steam_online/translations/de.json +++ b/homeassistant/components/steam_online/translations/de.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Freundesliste eingeschr\u00e4nkt: Bitte lies in der Dokumentation nach, wie du alle anderen Freunde sehen kannst" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/en.json b/homeassistant/components/steam_online/translations/en.json index f93a517b241..7226c5ee177 100644 --- a/homeassistant/components/steam_online/translations/en.json +++ b/homeassistant/components/steam_online/translations/en.json @@ -25,15 +25,15 @@ } }, "options": { + "error": { + "unauthorized": "Friends list restricted: Please refer to the documentation on how to see all other friends" + }, "step": { "init": { "data": { "accounts": "Names of accounts to be monitored" } } - }, - "error": { - "unauthorized": "Friends list restricted: Please refer to the documentation on how to see all other friends" } } } \ No newline at end of file diff --git a/homeassistant/components/steam_online/translations/fr.json b/homeassistant/components/steam_online/translations/fr.json index ae8055638f7..416927493d1 100644 --- a/homeassistant/components/steam_online/translations/fr.json +++ b/homeassistant/components/steam_online/translations/fr.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Liste d'amis restreinte\u00a0: Veuillez consulter la documentation afin d'afficher tous les autres amis" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/id.json b/homeassistant/components/steam_online/translations/id.json index abc61d42194..e944662fee1 100644 --- a/homeassistant/components/steam_online/translations/id.json +++ b/homeassistant/components/steam_online/translations/id.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Daftar teman dibatasi: Rujuk ke dokumentasi tentang cara melihat semua teman lain" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/nl.json b/homeassistant/components/steam_online/translations/nl.json index 7eafa00e53c..5512c18ee2b 100644 --- a/homeassistant/components/steam_online/translations/nl.json +++ b/homeassistant/components/steam_online/translations/nl.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Vriendenlijst beperkt: raadpleeg de documentatie over hoe je alle andere vrienden kunt zien" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/pt-BR.json b/homeassistant/components/steam_online/translations/pt-BR.json index cca660e891e..5aa9d0d2520 100644 --- a/homeassistant/components/steam_online/translations/pt-BR.json +++ b/homeassistant/components/steam_online/translations/pt-BR.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Lista restrita de amigos: consulte a documenta\u00e7\u00e3o sobre como ver todos os outros amigos" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/waze_travel_time/translations/ja.json b/homeassistant/components/waze_travel_time/translations/ja.json index 5ed0741a67e..545c0cdb0d6 100644 --- a/homeassistant/components/waze_travel_time/translations/ja.json +++ b/homeassistant/components/waze_travel_time/translations/ja.json @@ -11,7 +11,7 @@ "data": { "destination": "\u76ee\u7684\u5730", "name": "\u540d\u524d", - "origin": "\u30aa\u30ea\u30b8\u30f3", + "origin": "\u539f\u70b9(Origin)", "region": "\u30ea\u30fc\u30b8\u30e7\u30f3" }, "description": "\u51fa\u767a\u5730\u3068\u76ee\u7684\u5730\u306b\u3001\u5834\u6240\u306e\u4f4f\u6240\u307e\u305f\u306fGPS\u5ea7\u6a19\u3092\u5165\u529b\u3057\u307e\u3059(GPS\u306e\u5ea7\u6a19\u306f\u30b3\u30f3\u30de\u3067\u533a\u5207\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059)\u3002\u3053\u306e\u60c5\u5831\u3092\u72b6\u614b(state)\u3067\u63d0\u4f9b\u3059\u308b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3ID\u3001\u7def\u5ea6\u3068\u7d4c\u5ea6\u306e\u5c5e\u6027\u3092\u6301\u3064\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3ID\u3001\u307e\u305f\u306f\u30be\u30fc\u30f3\u306e\u30d5\u30ec\u30f3\u30c9\u30ea\u30fc\u540d\u3092\u5165\u529b\u3059\u308b\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u3002" diff --git a/homeassistant/components/yolink/translations/ru.json b/homeassistant/components/yolink/translations/ru.json new file mode 100644 index 00000000000..a1db7e744a8 --- /dev/null +++ b/homeassistant/components/yolink/translations/ru.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.", + "already_in_progress": "\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f.", + "authorize_url_timeout": "\u0418\u0441\u0442\u0435\u043a\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0441\u044b\u043b\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.", + "missing_configuration": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.", + "no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435.", + "oauth_error": "\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u0430.", + "reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "create_entry": { + "default": "\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0439\u0434\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e." + }, + "step": { + "pick_implementation": { + "title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438" + }, + "reauth_confirm": { + "description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 yolink", + "title": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f" + } + } + } +} \ No newline at end of file From 1c25e1d7b1d8c1827f3ed4e204c1e1682904be7f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 24 May 2022 00:37:47 -0500 Subject: [PATCH 763/930] Add metadata to logbook live stream websocket endpoint (#72394) --- .../components/logbook/websocket_api.py | 88 +++++++++++++------ .../components/logbook/test_websocket_api.py | 80 ++++++++++++----- 2 files changed, 121 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/logbook/websocket_api.py b/homeassistant/components/logbook/websocket_api.py index edfe3177f26..0bb7877b95b 100644 --- a/homeassistant/components/logbook/websocket_api.py +++ b/homeassistant/components/logbook/websocket_api.py @@ -75,6 +75,7 @@ async def _async_send_historical_events( end_time: dt, formatter: Callable[[int, Any], dict[str, Any]], event_processor: EventProcessor, + partial: bool, ) -> dt | None: """Select historical data from the database and deliver it to the websocket. @@ -94,88 +95,107 @@ async def _async_send_historical_events( ) if not is_big_query: - message, last_event_time = await _async_get_ws_formatted_events( + message, last_event_time = await _async_get_ws_stream_events( hass, msg_id, start_time, end_time, formatter, event_processor, + partial, ) - # If there is no last_time, there are no historical - # results, but we still send an empty message so + # If there is no last_event_time, there are no historical + # results, but we still send an empty message + # if its the last one (not partial) so # consumers of the api know their request was # answered but there were no results - connection.send_message(message) + if last_event_time or not partial: + connection.send_message(message) return last_event_time # This is a big query so we deliver # the first three hours and then # we fetch the old data recent_query_start = end_time - timedelta(hours=BIG_QUERY_RECENT_HOURS) - recent_message, recent_query_last_event_time = await _async_get_ws_formatted_events( + recent_message, recent_query_last_event_time = await _async_get_ws_stream_events( hass, msg_id, recent_query_start, end_time, formatter, event_processor, + partial=True, ) if recent_query_last_event_time: connection.send_message(recent_message) - older_message, older_query_last_event_time = await _async_get_ws_formatted_events( + older_message, older_query_last_event_time = await _async_get_ws_stream_events( hass, msg_id, start_time, recent_query_start, formatter, event_processor, + partial, ) - # If there is no last_time, there are no historical - # results, but we still send an empty message so + # If there is no last_event_time, there are no historical + # results, but we still send an empty message + # if its the last one (not partial) so # consumers of the api know their request was # answered but there were no results - if older_query_last_event_time or not recent_query_last_event_time: + if older_query_last_event_time or not partial: connection.send_message(older_message) # Returns the time of the newest event return recent_query_last_event_time or older_query_last_event_time -async def _async_get_ws_formatted_events( +async def _async_get_ws_stream_events( hass: HomeAssistant, msg_id: int, start_time: dt, end_time: dt, formatter: Callable[[int, Any], dict[str, Any]], event_processor: EventProcessor, + partial: bool, ) -> tuple[str, dt | None]: """Async wrapper around _ws_formatted_get_events.""" return await get_instance(hass).async_add_executor_job( - _ws_formatted_get_events, + _ws_stream_get_events, msg_id, start_time, end_time, formatter, event_processor, + partial, ) -def _ws_formatted_get_events( +def _ws_stream_get_events( msg_id: int, start_day: dt, end_day: dt, formatter: Callable[[int, Any], dict[str, Any]], event_processor: EventProcessor, + partial: bool, ) -> tuple[str, dt | None]: """Fetch events and convert them to json in the executor.""" events = event_processor.get_events(start_day, end_day) last_time = None if events: last_time = dt_util.utc_from_timestamp(events[-1]["when"]) - result = formatter(msg_id, events) - return JSON_DUMP(result), last_time + message = { + "events": events, + "start_time": dt_util.utc_to_timestamp(start_day), + "end_time": dt_util.utc_to_timestamp(end_day), + } + if partial: + # This is a hint to consumers of the api that + # we are about to send a another block of historical + # data in case the UI needs to show that historical + # data is still loading in the future + message["partial"] = True + return JSON_DUMP(formatter(msg_id, message)), last_time async def _async_events_consumer( @@ -209,7 +229,7 @@ async def _async_events_consumer( JSON_DUMP( messages.event_message( msg_id, - logbook_events, + {"events": logbook_events}, ) ) ) @@ -279,6 +299,7 @@ async def ws_event_stream( end_time, messages.event_message, event_processor, + partial=False, ) return @@ -331,6 +352,7 @@ async def ws_event_stream( subscriptions_setup_complete_time, messages.event_message, event_processor, + partial=True, ) await _async_wait_for_recorder_sync(hass) @@ -353,16 +375,16 @@ async def ws_event_stream( second_fetch_start_time = max( last_event_time or max_recorder_behind, max_recorder_behind ) - message, final_cutoff_time = await _async_get_ws_formatted_events( + await _async_send_historical_events( hass, + connection, msg_id, second_fetch_start_time, subscriptions_setup_complete_time, messages.event_message, event_processor, + partial=False, ) - if final_cutoff_time: # Only sends results if we have them - connection.send_message(message) if not subscriptions: # Unsubscribe happened while waiting for formatted events @@ -379,6 +401,20 @@ async def ws_event_stream( ) +def _ws_formatted_get_events( + msg_id: int, + start_time: dt, + end_time: dt, + event_processor: EventProcessor, +) -> str: + """Fetch events and convert them to json in the executor.""" + return JSON_DUMP( + messages.result_message( + msg_id, event_processor.get_events(start_time, end_time) + ) + ) + + @websocket_api.websocket_command( { vol.Required("type"): "logbook/get_events", @@ -438,12 +474,12 @@ async def ws_get_events( include_entity_name=False, ) - message, _ = await _async_get_ws_formatted_events( - hass, - msg["id"], - start_time, - end_time, - messages.result_message, - event_processor, + connection.send_message( + await hass.async_add_executor_job( + _ws_formatted_get_events, + msg["id"], + start_time, + end_time, + event_processor, + ) ) - connection.send_message(message) diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 26ad7127296..8706ccf7617 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -492,13 +492,16 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "off", "when": state.last_updated.timestamp(), } ] + assert msg["event"]["start_time"] == now.timestamp() + assert msg["event"]["end_time"] > msg["event"]["start_time"] + assert msg["event"]["partial"] is True hass.states.async_set("light.alpha", "on") hass.states.async_set("light.alpha", "off") @@ -520,7 +523,14 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [ { "entity_id": "light.alpha", "state": "off", @@ -560,7 +570,7 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "context_id": ANY, "domain": "automation", @@ -624,7 +634,7 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "context_id": "ac5bd62de45711eaaeb351041eec8dd9", "context_user_id": "b400facee45711eaa9308bfd3d19e474", @@ -686,7 +696,7 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await websocket_client.receive_json() assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "context_domain": "automation", "context_entity_id": "automation.alarm", @@ -716,7 +726,7 @@ async def test_subscribe_unsubscribe_logbook_stream( msg = await websocket_client.receive_json() assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "context_domain": "automation", "context_entity_id": "automation.alarm", @@ -788,7 +798,10 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert "start_time" in msg["event"] + assert "end_time" in msg["event"] + assert msg["event"]["partial"] is True + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "off", @@ -805,7 +818,16 @@ async def test_subscribe_unsubscribe_logbook_stream_entities( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert "start_time" in msg["event"] + assert "end_time" in msg["event"] + assert "partial" not in msg["event"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"] + assert msg["event"]["events"] == [ { "entity_id": "light.small", "state": "off", @@ -871,7 +893,8 @@ async def test_subscribe_unsubscribe_logbook_stream_entities_with_end_time( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["partial"] is True + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "off", @@ -888,7 +911,14 @@ async def test_subscribe_unsubscribe_logbook_stream_entities_with_end_time( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert "partial" not in msg["event"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"] + assert msg["event"]["events"] == [ { "entity_id": "light.small", "state": "off", @@ -962,7 +992,7 @@ async def test_subscribe_unsubscribe_logbook_stream_entities_past_only( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "off", @@ -1048,7 +1078,7 @@ async def test_subscribe_unsubscribe_logbook_stream_big_query( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "on", @@ -1060,7 +1090,8 @@ async def test_subscribe_unsubscribe_logbook_stream_big_query( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["partial"] is True + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.four_days_ago", "state": "off", @@ -1068,6 +1099,13 @@ async def test_subscribe_unsubscribe_logbook_stream_big_query( } ] + # And finally a response without partial set to indicate no more + # historical data is coming + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [] + await websocket_client.send_json( {"id": 8, "type": "unsubscribe_events", "subscription": 7} ) @@ -1123,7 +1161,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [] + assert msg["event"]["events"] == [] hass.bus.async_fire("mock_event", {"device_id": device.id}) await hass.async_block_till_done() @@ -1131,7 +1169,7 @@ async def test_subscribe_unsubscribe_logbook_stream_device( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ {"domain": "test", "message": "is on fire", "name": "device name", "when": ANY} ] @@ -1250,7 +1288,7 @@ async def test_live_stream_with_one_second_commit_interval( msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - recieved_rows.extend(msg["event"]) + recieved_rows.extend(msg["event"]["events"]) hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "6"}) @@ -1262,7 +1300,7 @@ async def test_live_stream_with_one_second_commit_interval( msg = await asyncio.wait_for(websocket_client.receive_json(), 2.5) assert msg["id"] == 7 assert msg["type"] == "event" - recieved_rows.extend(msg["event"]) + recieved_rows.extend(msg["event"]["events"]) # Make sure we get rows back in order assert recieved_rows == [ @@ -1326,7 +1364,7 @@ async def test_subscribe_disconnected(hass, recorder_mock, hass_ws_client): msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ { "entity_id": "binary_sensor.is_light", "state": "off", @@ -1437,7 +1475,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [] + assert msg["event"]["events"] == [] hass.bus.async_fire("mock_event", {"device_id": device.id, "message": "1"}) await hass.async_block_till_done() @@ -1445,7 +1483,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ {"domain": "test", "message": "1", "name": "device name", "when": ANY} ] @@ -1455,7 +1493,7 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo msg = await asyncio.wait_for(websocket_client.receive_json(), 2) assert msg["id"] == 7 assert msg["type"] == "event" - assert msg["event"] == [ + assert msg["event"]["events"] == [ {"domain": "test", "message": "2", "name": "device name", "when": ANY} ] From 070cb616317f4661e8558706ae025d3d9b2b4868 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 08:23:09 +0200 Subject: [PATCH 764/930] Adjust config-flow type hints in cloudflare (#72388) * Adjust config-flow type hints in cloudflare * Improve type hints --- .../components/cloudflare/config_flow.py | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/cloudflare/config_flow.py b/homeassistant/components/cloudflare/config_flow.py index 27a22dbc5bd..121e0fc9974 100644 --- a/homeassistant/components/cloudflare/config_flow.py +++ b/homeassistant/components/cloudflare/config_flow.py @@ -32,7 +32,7 @@ DATA_SCHEMA = vol.Schema( ) -def _zone_schema(zones: list | None = None): +def _zone_schema(zones: list[str] | None = None) -> vol.Schema: """Zone selection schema.""" zones_list = [] @@ -42,7 +42,7 @@ def _zone_schema(zones: list | None = None): return vol.Schema({vol.Required(CONF_ZONE): vol.In(zones_list)}) -def _records_schema(records: list | None = None): +def _records_schema(records: list[str] | None = None) -> vol.Schema: """Zone records selection schema.""" records_dict = {} @@ -52,13 +52,15 @@ def _records_schema(records: list | None = None): return vol.Schema({vol.Required(CONF_RECORDS): cv.multi_select(records_dict)}) -async def validate_input(hass: HomeAssistant, data: dict): +async def _validate_input( + hass: HomeAssistant, data: dict[str, Any] +) -> dict[str, list[str] | None]: """Validate the user input allows us to connect. Data has the keys from DATA_SCHEMA with values provided by the user. """ zone = data.get(CONF_ZONE) - records = None + records: list[str] | None = None cfupdate = CloudflareUpdater( async_get_clientsession(hass), @@ -68,7 +70,7 @@ async def validate_input(hass: HomeAssistant, data: dict): ) try: - zones = await cfupdate.get_zones() + zones: list[str] | None = await cfupdate.get_zones() if zone: zone_id = await cfupdate.get_zone_id() records = await cfupdate.get_zone_records(zone_id, "A") @@ -89,11 +91,11 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): entry: ConfigEntry | None = None - def __init__(self): + def __init__(self) -> None: """Initialize the Cloudflare config flow.""" - self.cloudflare_config = {} - self.zones = None - self.records = None + self.cloudflare_config: dict[str, Any] = {} + self.zones: list[str] | None = None + self.records: list[str] | None = None async def async_step_reauth(self, data: dict[str, Any]) -> FlowResult: """Handle initiation of re-authentication with Cloudflare.""" @@ -104,7 +106,7 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle re-authentication with Cloudflare.""" - errors = {} + errors: dict[str, str] = {} if user_input is not None and self.entry: _, errors = await self._async_validate_or_error(user_input) @@ -130,14 +132,16 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_user(self, user_input: dict | None = None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initiated by the user.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") persistent_notification.async_dismiss(self.hass, "cloudflare_setup") - errors = {} + errors: dict[str, str] = {} if user_input is not None: info, errors = await self._async_validate_or_error(user_input) @@ -151,9 +155,11 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): step_id="user", data_schema=DATA_SCHEMA, errors=errors ) - async def async_step_zone(self, user_input: dict | None = None): + async def async_step_zone( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the picking the zone.""" - errors = {} + errors: dict[str, str] = {} if user_input is not None: self.cloudflare_config.update(user_input) @@ -171,7 +177,9 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_records(self, user_input: dict | None = None): + async def async_step_records( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the picking the zone records.""" if user_input is not None: @@ -184,12 +192,14 @@ class CloudflareConfigFlow(ConfigFlow, domain=DOMAIN): data_schema=_records_schema(self.records), ) - async def _async_validate_or_error(self, config): - errors = {} + async def _async_validate_or_error( + self, config: dict[str, Any] + ) -> tuple[dict[str, list[str] | None], dict[str, str]]: + errors: dict[str, str] = {} info = {} try: - info = await validate_input(self.hass, config) + info = await _validate_input(self.hass, config) except CannotConnect: errors["base"] = "cannot_connect" except InvalidAuth: From f0b9aa7894e19da475c1e2debcd7f5189d7b1ebe Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Tue, 24 May 2022 00:53:01 -0700 Subject: [PATCH 765/930] Bump pywemo==0.8.1 (#72400) --- .../components/wemo/binary_sensor.py | 8 +++--- homeassistant/components/wemo/entity.py | 3 +-- homeassistant/components/wemo/fan.py | 2 +- homeassistant/components/wemo/light.py | 26 ++++++++++--------- homeassistant/components/wemo/manifest.json | 2 +- homeassistant/components/wemo/switch.py | 12 +++++---- homeassistant/components/wemo/wemo_device.py | 4 +-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/wemo/conftest.py | 2 +- tests/components/wemo/test_binary_sensor.py | 6 ++--- tests/components/wemo/test_switch.py | 6 ++--- 12 files changed, 39 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/wemo/binary_sensor.py b/homeassistant/components/wemo/binary_sensor.py index 3e7b159dc63..ce7dfc2fa11 100644 --- a/homeassistant/components/wemo/binary_sensor.py +++ b/homeassistant/components/wemo/binary_sensor.py @@ -1,6 +1,5 @@ """Support for WeMo binary sensors.""" import asyncio -from typing import cast from pywemo import Insight, Maker, StandbyState @@ -49,20 +48,21 @@ class MakerBinarySensor(WemoEntity, BinarySensorEntity): """Maker device's sensor port.""" _name_suffix = "Sensor" + wemo: Maker @property def is_on(self) -> bool: """Return true if the Maker's sensor is pulled low.""" - return cast(int, self.wemo.has_sensor) != 0 and self.wemo.sensor_state == 0 + return self.wemo.has_sensor != 0 and self.wemo.sensor_state == 0 class InsightBinarySensor(WemoBinarySensor): """Sensor representing the device connected to the Insight Switch.""" _name_suffix = "Device" + wemo: Insight @property def is_on(self) -> bool: """Return true device connected to the Insight Switch is on.""" - # Note: wemo.get_standby_state is a @property. - return super().is_on and self.wemo.get_standby_state == StandbyState.ON + return super().is_on and self.wemo.standby_state == StandbyState.ON diff --git a/homeassistant/components/wemo/entity.py b/homeassistant/components/wemo/entity.py index 6d94e203932..c8ed9cdde08 100644 --- a/homeassistant/components/wemo/entity.py +++ b/homeassistant/components/wemo/entity.py @@ -4,7 +4,6 @@ from __future__ import annotations from collections.abc import Generator import contextlib import logging -from typing import cast from pywemo.exceptions import ActionException @@ -89,4 +88,4 @@ class WemoBinaryStateEntity(WemoEntity): @property def is_on(self) -> bool: """Return true if the state is on.""" - return cast(int, self.wemo.get_state()) != 0 + return self.wemo.get_state() != 0 diff --git a/homeassistant/components/wemo/fan.py b/homeassistant/components/wemo/fan.py index d62a7a3b7e3..d24827bee96 100644 --- a/homeassistant/components/wemo/fan.py +++ b/homeassistant/components/wemo/fan.py @@ -6,7 +6,7 @@ from datetime import timedelta import math from typing import Any -from pywemo.ouimeaux_device.humidifier import DesiredHumidity, FanMode, Humidifier +from pywemo import DesiredHumidity, FanMode, Humidifier import voluptuous as vol from homeassistant.components.fan import FanEntity, FanEntityFeature diff --git a/homeassistant/components/wemo/light.py b/homeassistant/components/wemo/light.py index 87ebbd9e2c3..3ff0f115a04 100644 --- a/homeassistant/components/wemo/light.py +++ b/homeassistant/components/wemo/light.py @@ -2,9 +2,9 @@ from __future__ import annotations import asyncio -from typing import Any, Optional, cast +from typing import Any, cast -from pywemo.ouimeaux_device import bridge +from pywemo import Bridge, BridgeLight, Dimmer from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -40,7 +40,7 @@ async def async_setup_entry( async def _discovered_wemo(coordinator: DeviceCoordinator) -> None: """Handle a discovered Wemo device.""" - if isinstance(coordinator.wemo, bridge.Bridge): + if isinstance(coordinator.wemo, Bridge): async_setup_bridge(hass, config_entry, async_add_entities, coordinator) else: async_add_entities([WemoDimmer(coordinator)]) @@ -70,7 +70,8 @@ def async_setup_bridge( """Check to see if the bridge has any new lights.""" new_lights = [] - for light_id, light in coordinator.wemo.Lights.items(): + bridge = cast(Bridge, coordinator.wemo) + for light_id, light in bridge.Lights.items(): if light_id not in known_light_ids: known_light_ids.add(light_id) new_lights.append(WemoLight(coordinator, light)) @@ -87,7 +88,7 @@ class WemoLight(WemoEntity, LightEntity): _attr_supported_features = LightEntityFeature.TRANSITION - def __init__(self, coordinator: DeviceCoordinator, light: bridge.Light) -> None: + def __init__(self, coordinator: DeviceCoordinator, light: BridgeLight) -> None: """Initialize the WeMo light.""" super().__init__(coordinator) self.light = light @@ -97,17 +98,17 @@ class WemoLight(WemoEntity, LightEntity): @property def name(self) -> str: """Return the name of the device if any.""" - return cast(str, self.light.name) + return self.light.name @property def available(self) -> bool: """Return true if the device is available.""" - return super().available and self.light.state.get("available") + return super().available and self.light.state.get("available", False) @property def unique_id(self) -> str: """Return the ID of this light.""" - return cast(str, self.light.uniqueID) + return self.light.uniqueID @property def device_info(self) -> DeviceInfo: @@ -123,17 +124,17 @@ class WemoLight(WemoEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" - return cast(int, self.light.state.get("level", 255)) + return self.light.state.get("level", 255) @property def xy_color(self) -> tuple[float, float] | None: """Return the xy color value [float, float].""" - return self.light.state.get("color_xy") # type:ignore[no-any-return] + return self.light.state.get("color_xy") @property def color_temp(self) -> int | None: """Return the color temperature of this light in mireds.""" - return cast(Optional[int], self.light.state.get("temperature_mireds")) + return self.light.state.get("temperature_mireds") @property def color_mode(self) -> ColorMode: @@ -166,7 +167,7 @@ class WemoLight(WemoEntity, LightEntity): @property def is_on(self) -> bool: """Return true if device is on.""" - return cast(int, self.light.state.get("onoff")) != WEMO_OFF + return self.light.state.get("onoff", WEMO_OFF) != WEMO_OFF def turn_on(self, **kwargs: Any) -> None: """Turn the light on.""" @@ -210,6 +211,7 @@ class WemoDimmer(WemoBinaryStateEntity, LightEntity): _attr_supported_color_modes = {ColorMode.BRIGHTNESS} _attr_color_mode = ColorMode.BRIGHTNESS + wemo: Dimmer @property def brightness(self) -> int: diff --git a/homeassistant/components/wemo/manifest.json b/homeassistant/components/wemo/manifest.json index d048a59d38c..40bb8161d90 100644 --- a/homeassistant/components/wemo/manifest.json +++ b/homeassistant/components/wemo/manifest.json @@ -3,7 +3,7 @@ "name": "Belkin WeMo", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/wemo", - "requirements": ["pywemo==0.7.0"], + "requirements": ["pywemo==0.8.1"], "ssdp": [ { "manufacturer": "Belkin International Inc." diff --git a/homeassistant/components/wemo/switch.py b/homeassistant/components/wemo/switch.py index c3d5bcb0462..1f9f8bce01f 100644 --- a/homeassistant/components/wemo/switch.py +++ b/homeassistant/components/wemo/switch.py @@ -3,9 +3,9 @@ from __future__ import annotations import asyncio from datetime import datetime, timedelta -from typing import Any, cast +from typing import Any -from pywemo import CoffeeMaker, Insight, Maker, StandbyState +from pywemo import CoffeeMaker, Insight, Maker, StandbyState, Switch from homeassistant.components.switch import SwitchEntity from homeassistant.config_entries import ConfigEntry @@ -58,6 +58,9 @@ async def async_setup_entry( class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): """Representation of a WeMo switch.""" + # All wemo devices used with WemoSwitch are subclasses of Switch. + wemo: Switch + @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the device.""" @@ -103,10 +106,9 @@ class WemoSwitch(WemoBinaryStateEntity, SwitchEntity): def detail_state(self) -> str: """Return the state of the device.""" if isinstance(self.wemo, CoffeeMaker): - return cast(str, self.wemo.mode_string) + return self.wemo.mode_string if isinstance(self.wemo, Insight): - # Note: wemo.get_standby_state is a @property. - standby_state = self.wemo.get_standby_state + standby_state = self.wemo.standby_state if standby_state == StandbyState.ON: return STATE_ON if standby_state == StandbyState.OFF: diff --git a/homeassistant/components/wemo/wemo_device.py b/homeassistant/components/wemo/wemo_device.py index 3ca47544fd7..8f5e6864059 100644 --- a/homeassistant/components/wemo/wemo_device.py +++ b/homeassistant/components/wemo/wemo_device.py @@ -3,7 +3,7 @@ import asyncio from datetime import timedelta import logging -from pywemo import Insight, WeMoDevice +from pywemo import Insight, LongPressMixin, WeMoDevice from pywemo.exceptions import ActionException from pywemo.subscribe import EVENT_TYPE_LONG_PRESS @@ -159,7 +159,7 @@ async def async_register_device( registry.on(wemo, None, device.subscription_callback) await hass.async_add_executor_job(registry.register, wemo) - if device.supports_long_press: + if isinstance(wemo, LongPressMixin): try: await hass.async_add_executor_job(wemo.ensure_long_press_virtual_device) # Temporarily handling all exceptions for #52996 & pywemo/pywemo/issues/276 diff --git a/requirements_all.txt b/requirements_all.txt index bf4f2d556ca..b5c731367c5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2017,7 +2017,7 @@ pyvolumio==0.1.5 pywebpush==1.9.2 # homeassistant.components.wemo -pywemo==0.7.0 +pywemo==0.8.1 # homeassistant.components.wilight pywilight==0.0.70 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f735b555859..f862c719f50 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1337,7 +1337,7 @@ pyvolumio==0.1.5 pywebpush==1.9.2 # homeassistant.components.wemo -pywemo==0.7.0 +pywemo==0.8.1 # homeassistant.components.wilight pywilight==0.0.70 diff --git a/tests/components/wemo/conftest.py b/tests/components/wemo/conftest.py index ae900e2522f..1a5998c1f94 100644 --- a/tests/components/wemo/conftest.py +++ b/tests/components/wemo/conftest.py @@ -69,7 +69,7 @@ def create_pywemo_device(pywemo_registry, pywemo_model): device.supports_long_press.return_value = cls.supports_long_press() if issubclass(cls, pywemo.Insight): - device.get_standby_state = pywemo.StandbyState.OFF + device.standby_state = pywemo.StandbyState.OFF device.current_power_watts = MOCK_INSIGHT_CURRENT_WATTS device.today_kwh = MOCK_INSIGHT_TODAY_KWH device.threshold_power_watts = MOCK_INSIGHT_STATE_THRESHOLD_POWER diff --git a/tests/components/wemo/test_binary_sensor.py b/tests/components/wemo/test_binary_sensor.py index f26428bf32b..99a5df47e25 100644 --- a/tests/components/wemo/test_binary_sensor.py +++ b/tests/components/wemo/test_binary_sensor.py @@ -117,21 +117,21 @@ class TestInsight(EntityTestHelpers): """Verify that the binary_sensor receives state updates from the registry.""" # On state. pywemo_device.get_state.return_value = 1 - pywemo_device.get_standby_state = StandbyState.ON + pywemo_device.standby_state = StandbyState.ON pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_ON # Standby (Off) state. pywemo_device.get_state.return_value = 1 - pywemo_device.get_standby_state = StandbyState.STANDBY + pywemo_device.standby_state = StandbyState.STANDBY pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF # Off state. pywemo_device.get_state.return_value = 0 - pywemo_device.get_standby_state = StandbyState.OFF + pywemo_device.standby_state = StandbyState.OFF pywemo_registry.callbacks[pywemo_device.name](pywemo_device, "", "") await hass.async_block_till_done() assert hass.states.get(wemo_entity.entity_id).state == STATE_OFF diff --git a/tests/components/wemo/test_switch.py b/tests/components/wemo/test_switch.py index 0c024295632..0f23aa8b8b6 100644 --- a/tests/components/wemo/test_switch.py +++ b/tests/components/wemo/test_switch.py @@ -134,19 +134,19 @@ async def test_insight_state_attributes(hass, pywemo_registry): ) # Test 'ON' state detail value. - insight.get_standby_state = pywemo.StandbyState.ON + insight.standby_state = pywemo.StandbyState.ON await async_update() attributes = hass.states.get(wemo_entity.entity_id).attributes assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_ON # Test 'STANDBY' state detail value. - insight.get_standby_state = pywemo.StandbyState.STANDBY + insight.standby_state = pywemo.StandbyState.STANDBY await async_update() attributes = hass.states.get(wemo_entity.entity_id).attributes assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_STANDBY # Test 'UNKNOWN' state detail value. - insight.get_standby_state = None + insight.standby_state = None await async_update() attributes = hass.states.get(wemo_entity.entity_id).attributes assert attributes[ATTR_CURRENT_STATE_DETAIL] == STATE_UNKNOWN From 3cd398a5bdc7a6d2fc43752139a3c4bd9f8bbaaa Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 24 May 2022 01:26:25 -0700 Subject: [PATCH 766/930] Warn for old Google SDK version (#72403) --- .../components/google_assistant/helpers.py | 19 ++++- .../google_assistant/test_helpers.py | 82 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/google_assistant/helpers.py b/homeassistant/components/google_assistant/helpers.py index 529778ce1b6..b425367f5c3 100644 --- a/homeassistant/components/google_assistant/helpers.py +++ b/homeassistant/components/google_assistant/helpers.py @@ -10,6 +10,7 @@ import logging import pprint from aiohttp.web import json_response +from awesomeversion import AwesomeVersion from homeassistant.components import webhook from homeassistant.const import ( @@ -43,6 +44,8 @@ from .error import SmartHomeError SYNC_DELAY = 15 _LOGGER = logging.getLogger(__name__) +LOCAL_SDK_VERSION_HEADER = "HA-Cloud-Version" +LOCAL_SDK_MIN_VERSION = AwesomeVersion("2.1.5") @callback @@ -86,6 +89,7 @@ class AbstractConfig(ABC): self._google_sync_unsub = {} self._local_sdk_active = False self._local_last_active: datetime | None = None + self._local_sdk_version_warn = False async def async_initialize(self): """Perform async initialization of config.""" @@ -327,6 +331,18 @@ class AbstractConfig(ABC): from . import smart_home self._local_last_active = utcnow() + + # Check version local SDK. + version = request.headers.get("HA-Cloud-Version") + if not self._local_sdk_version_warn and ( + not version or AwesomeVersion(version) < LOCAL_SDK_MIN_VERSION + ): + _LOGGER.warning( + "Local SDK version is too old (%s), check documentation on how to update to the latest version", + version, + ) + self._local_sdk_version_warn = True + payload = await request.json() if _LOGGER.isEnabledFor(logging.DEBUG): @@ -577,8 +593,9 @@ class GoogleEntity: device["customData"] = { "webhookId": self.config.get_local_webhook_id(agent_user_id), "httpPort": self.hass.http.server_port, - "httpSSL": self.hass.config.api.use_ssl, "uuid": instance_uuid, + # Below can be removed in HA 2022.9 + "httpSSL": self.hass.config.api.use_ssl, "baseUrl": get_url(self.hass, prefer_external=True), "proxyDeviceId": agent_user_id, } diff --git a/tests/components/google_assistant/test_helpers.py b/tests/components/google_assistant/test_helpers.py index 4490f0e3963..1ab573baf2a 100644 --- a/tests/components/google_assistant/test_helpers.py +++ b/tests/components/google_assistant/test_helpers.py @@ -345,3 +345,85 @@ def test_request_data(): config, "test_user", SOURCE_CLOUD, "test_request_id", None ) assert data.is_local_request is False + + +async def test_config_local_sdk_allow_min_version(hass, hass_client, caplog): + """Test the local SDK.""" + version = str(helpers.LOCAL_SDK_MIN_VERSION) + assert await async_setup_component(hass, "webhook", {}) + + config = MockConfig( + hass=hass, + agent_user_ids={ + "mock-user-id": { + STORE_GOOGLE_LOCAL_WEBHOOK_ID: "mock-webhook-id", + }, + }, + ) + + client = await hass_client() + + assert config._local_sdk_version_warn is False + config.async_enable_local_sdk() + + await client.post( + "/api/webhook/mock-webhook-id", + headers={helpers.LOCAL_SDK_VERSION_HEADER: version}, + json={ + "inputs": [ + { + "context": {"locale_country": "US", "locale_language": "en"}, + "intent": "action.devices.SYNC", + } + ], + "requestId": "mock-req-id", + }, + ) + assert config._local_sdk_version_warn is False + assert ( + f"Local SDK version is too old ({version}), check documentation on how " + "to update to the latest version" + ) not in caplog.text + + +@pytest.mark.parametrize("version", (None, "2.1.4")) +async def test_config_local_sdk_warn_version(hass, hass_client, caplog, version): + """Test the local SDK.""" + assert await async_setup_component(hass, "webhook", {}) + + config = MockConfig( + hass=hass, + agent_user_ids={ + "mock-user-id": { + STORE_GOOGLE_LOCAL_WEBHOOK_ID: "mock-webhook-id", + }, + }, + ) + + client = await hass_client() + + assert config._local_sdk_version_warn is False + config.async_enable_local_sdk() + + headers = {} + if version: + headers[helpers.LOCAL_SDK_VERSION_HEADER] = version + + await client.post( + "/api/webhook/mock-webhook-id", + headers=headers, + json={ + "inputs": [ + { + "context": {"locale_country": "US", "locale_language": "en"}, + "intent": "action.devices.SYNC", + } + ], + "requestId": "mock-req-id", + }, + ) + assert config._local_sdk_version_warn is True + assert ( + f"Local SDK version is too old ({version}), check documentation on how " + "to update to the latest version" + ) in caplog.text From cc162bf691e7b470e7c848b2ea82548173a79131 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 10:49:05 +0200 Subject: [PATCH 767/930] Remove YAML configuration from Jandy iAqualink (#72404) --- .../components/iaqualink/__init__.py | 43 +++---------------- .../components/iaqualink/config_flow.py | 9 ++-- .../components/iaqualink/test_config_flow.py | 36 +++++----------- 3 files changed, 19 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/iaqualink/__init__.py b/homeassistant/components/iaqualink/__init__.py index 0d413b1ad7c..02e27d8e82f 100644 --- a/homeassistant/components/iaqualink/__init__.py +++ b/homeassistant/components/iaqualink/__init__.py @@ -19,9 +19,7 @@ from iaqualink.device import ( ) from iaqualink.exception import AqualinkServiceException from typing_extensions import Concatenate, ParamSpec -import voluptuous as vol -from homeassistant import config_entries from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN @@ -32,14 +30,12 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.typing import ConfigType from .const import DOMAIN, UPDATE_INTERVAL @@ -52,42 +48,13 @@ ATTR_CONFIG = "config" PARALLEL_UPDATES = 0 PLATFORMS = [ - BINARY_SENSOR_DOMAIN, - CLIMATE_DOMAIN, - LIGHT_DOMAIN, - SENSOR_DOMAIN, - SWITCH_DOMAIN, + Platform.BINARY_SENSOR, + Platform.CLIMATE, + Platform.LIGHT, + Platform.SENSOR, + Platform.SWITCH, ] -CONFIG_SCHEMA = vol.Schema( - vol.All( - cv.deprecated(DOMAIN), - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - } - ) - }, - ), - extra=vol.ALLOW_EXTRA, -) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the Aqualink component.""" - if (conf := config.get(DOMAIN)) is not None: - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=conf, - ) - ) - - return True - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Aqualink from a config entry.""" diff --git a/homeassistant/components/iaqualink/config_flow.py b/homeassistant/components/iaqualink/config_flow.py index 921102b85dc..3b3a99cac6e 100644 --- a/homeassistant/components/iaqualink/config_flow.py +++ b/homeassistant/components/iaqualink/config_flow.py @@ -12,6 +12,7 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.data_entry_flow import FlowResult from .const import DOMAIN @@ -21,7 +22,9 @@ class AqualinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def async_step_user(self, user_input: dict[str, Any] | None = None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow start.""" # Supporting a single account. entries = self._async_current_entries() @@ -54,7 +57,3 @@ class AqualinkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ), errors=errors, ) - - async def async_step_import(self, user_input: dict[str, Any] | None = None): - """Occurs when an entry is setup through config.""" - return await self.async_step_user(user_input) diff --git a/tests/components/iaqualink/test_config_flow.py b/tests/components/iaqualink/test_config_flow.py index 2d00284775d..df2add36b9c 100644 --- a/tests/components/iaqualink/test_config_flow.py +++ b/tests/components/iaqualink/test_config_flow.py @@ -5,13 +5,11 @@ from iaqualink.exception import ( AqualinkServiceException, AqualinkServiceUnauthorizedException, ) -import pytest from homeassistant.components.iaqualink import config_flow -@pytest.mark.parametrize("step", ["import", "user"]) -async def test_already_configured(hass, config_entry, config_data, step): +async def test_already_configured(hass, config_entry, config_data): """Test config flow when iaqualink component is already setup.""" config_entry.add_to_hass(hass) @@ -19,81 +17,67 @@ async def test_already_configured(hass, config_entry, config_data, step): flow.hass = hass flow.context = {} - fname = f"async_step_{step}" - func = getattr(flow, fname) - result = await func(config_data) + result = await flow.async_step_user(config_data) assert result["type"] == "abort" -@pytest.mark.parametrize("step", ["import", "user"]) -async def test_without_config(hass, step): +async def test_without_config(hass): """Test config flow with no configuration.""" flow = config_flow.AqualinkFlowHandler() flow.hass = hass flow.context = {} - fname = f"async_step_{step}" - func = getattr(flow, fname) - result = await func() + result = await flow.async_step_user() assert result["type"] == "form" assert result["step_id"] == "user" assert result["errors"] == {} -@pytest.mark.parametrize("step", ["import", "user"]) -async def test_with_invalid_credentials(hass, config_data, step): +async def test_with_invalid_credentials(hass, config_data): """Test config flow with invalid username and/or password.""" flow = config_flow.AqualinkFlowHandler() flow.hass = hass - fname = f"async_step_{step}" - func = getattr(flow, fname) with patch( "homeassistant.components.iaqualink.config_flow.AqualinkClient.login", side_effect=AqualinkServiceUnauthorizedException, ): - result = await func(config_data) + result = await flow.async_step_user(config_data) assert result["type"] == "form" assert result["step_id"] == "user" assert result["errors"] == {"base": "invalid_auth"} -@pytest.mark.parametrize("step", ["import", "user"]) -async def test_service_exception(hass, config_data, step): +async def test_service_exception(hass, config_data): """Test config flow encountering service exception.""" flow = config_flow.AqualinkFlowHandler() flow.hass = hass - fname = f"async_step_{step}" - func = getattr(flow, fname) with patch( "homeassistant.components.iaqualink.config_flow.AqualinkClient.login", side_effect=AqualinkServiceException, ): - result = await func(config_data) + result = await flow.async_step_user(config_data) assert result["type"] == "form" assert result["step_id"] == "user" assert result["errors"] == {"base": "cannot_connect"} -@pytest.mark.parametrize("step", ["import", "user"]) -async def test_with_existing_config(hass, config_data, step): +async def test_with_existing_config(hass, config_data): """Test config flow with existing configuration.""" flow = config_flow.AqualinkFlowHandler() flow.hass = hass flow.context = {} - fname = f"async_step_{step}" - func = getattr(flow, fname) with patch( "homeassistant.components.iaqualink.config_flow.AqualinkClient.login", return_value=None, ): - result = await func(config_data) + result = await flow.async_step_user(config_data) assert result["type"] == "create_entry" assert result["title"] == config_data["username"] From 54f5238ef660738c5fcdd6f1f904f568311613ac Mon Sep 17 00:00:00 2001 From: j-a-n Date: Tue, 24 May 2022 11:03:49 +0200 Subject: [PATCH 768/930] Moehlenhoff alpha2 sensors (#72161) Co-authored-by: Franck Nijhof --- .coveragerc | 2 + .../components/moehlenhoff_alpha2/__init__.py | 12 +++- .../moehlenhoff_alpha2/binary_sensor.py | 56 +++++++++++++++++++ .../components/moehlenhoff_alpha2/climate.py | 39 ++++++++----- .../moehlenhoff_alpha2/manifest.json | 2 +- .../components/moehlenhoff_alpha2/sensor.py | 56 +++++++++++++++++++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 8 files changed, 152 insertions(+), 19 deletions(-) create mode 100644 homeassistant/components/moehlenhoff_alpha2/binary_sensor.py create mode 100644 homeassistant/components/moehlenhoff_alpha2/sensor.py diff --git a/.coveragerc b/.coveragerc index d884e6e8c13..dd03c3782c0 100644 --- a/.coveragerc +++ b/.coveragerc @@ -724,8 +724,10 @@ omit = homeassistant/components/modem_callerid/button.py homeassistant/components/modem_callerid/sensor.py homeassistant/components/moehlenhoff_alpha2/__init__.py + homeassistant/components/moehlenhoff_alpha2/binary_sensor.py homeassistant/components/moehlenhoff_alpha2/climate.py homeassistant/components/moehlenhoff_alpha2/const.py + homeassistant/components/moehlenhoff_alpha2/sensor.py homeassistant/components/motion_blinds/__init__.py homeassistant/components/motion_blinds/const.py homeassistant/components/motion_blinds/cover.py diff --git a/homeassistant/components/moehlenhoff_alpha2/__init__.py b/homeassistant/components/moehlenhoff_alpha2/__init__.py index 62e18917dc6..93ddaa781ab 100644 --- a/homeassistant/components/moehlenhoff_alpha2/__init__.py +++ b/homeassistant/components/moehlenhoff_alpha2/__init__.py @@ -17,7 +17,7 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.CLIMATE] +PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.BINARY_SENSOR] UPDATE_INTERVAL = timedelta(seconds=60) @@ -65,10 +65,16 @@ class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]): update_interval=UPDATE_INTERVAL, ) - async def _async_update_data(self) -> dict[str, dict]: + async def _async_update_data(self) -> dict[str, dict[str, dict]]: """Fetch the latest data from the source.""" await self.base.update_data() - return {ha["ID"]: ha for ha in self.base.heat_areas if ha.get("ID")} + return { + "heat_areas": {ha["ID"]: ha for ha in self.base.heat_areas if ha.get("ID")}, + "heat_controls": { + hc["ID"]: hc for hc in self.base.heat_controls if hc.get("ID") + }, + "io_devices": {io["ID"]: io for io in self.base.io_devices if io.get("ID")}, + } def get_cooling(self) -> bool: """Return if cooling mode is enabled.""" diff --git a/homeassistant/components/moehlenhoff_alpha2/binary_sensor.py b/homeassistant/components/moehlenhoff_alpha2/binary_sensor.py new file mode 100644 index 00000000000..ddd92c3a70b --- /dev/null +++ b/homeassistant/components/moehlenhoff_alpha2/binary_sensor.py @@ -0,0 +1,56 @@ +"""Support for Alpha2 IO device battery sensors.""" + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import Alpha2BaseCoordinator +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Add Alpha2 sensor entities from a config_entry.""" + + coordinator: Alpha2BaseCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + async_add_entities( + Alpha2IODeviceBatterySensor(coordinator, io_device_id) + for io_device_id, io_device in coordinator.data["io_devices"].items() + if io_device["_HEATAREA_ID"] + ) + + +class Alpha2IODeviceBatterySensor( + CoordinatorEntity[Alpha2BaseCoordinator], BinarySensorEntity +): + """Alpha2 IO device battery binary sensor.""" + + _attr_device_class = BinarySensorDeviceClass.BATTERY + _attr_entity_category = EntityCategory.DIAGNOSTIC + + def __init__(self, coordinator: Alpha2BaseCoordinator, io_device_id: str) -> None: + """Initialize Alpha2IODeviceBatterySensor.""" + super().__init__(coordinator) + self.io_device_id = io_device_id + self._attr_unique_id = f"{io_device_id}:battery" + io_device = self.coordinator.data["io_devices"][io_device_id] + heat_area = self.coordinator.data["heat_areas"][io_device["_HEATAREA_ID"]] + self._attr_name = ( + f"{heat_area['HEATAREA_NAME']} IO device {io_device['NR']} battery" + ) + + @property + def is_on(self): + """Return the state of the sensor.""" + # 0=empty, 1=weak, 2=good + return self.coordinator.data["io_devices"][self.io_device_id]["BATTERY"] < 2 diff --git a/homeassistant/components/moehlenhoff_alpha2/climate.py b/homeassistant/components/moehlenhoff_alpha2/climate.py index 225735293ca..e14f4801661 100644 --- a/homeassistant/components/moehlenhoff_alpha2/climate.py +++ b/homeassistant/components/moehlenhoff_alpha2/climate.py @@ -29,7 +29,8 @@ async def async_setup_entry( coordinator: Alpha2BaseCoordinator = hass.data[DOMAIN][config_entry.entry_id] async_add_entities( - Alpha2Climate(coordinator, heat_area_id) for heat_area_id in coordinator.data + Alpha2Climate(coordinator, heat_area_id) + for heat_area_id in coordinator.data["heat_areas"] ) @@ -51,26 +52,34 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): super().__init__(coordinator) self.heat_area_id = heat_area_id self._attr_unique_id = heat_area_id - - @property - def name(self) -> str: - """Return the name of the climate device.""" - return self.coordinator.data[self.heat_area_id]["HEATAREA_NAME"] + self._attr_name = self.coordinator.data["heat_areas"][heat_area_id][ + "HEATAREA_NAME" + ] @property def min_temp(self) -> float: """Return the minimum temperature.""" - return float(self.coordinator.data[self.heat_area_id].get("T_TARGET_MIN", 0.0)) + return float( + self.coordinator.data["heat_areas"][self.heat_area_id].get( + "T_TARGET_MIN", 0.0 + ) + ) @property def max_temp(self) -> float: """Return the maximum temperature.""" - return float(self.coordinator.data[self.heat_area_id].get("T_TARGET_MAX", 30.0)) + return float( + self.coordinator.data["heat_areas"][self.heat_area_id].get( + "T_TARGET_MAX", 30.0 + ) + ) @property def current_temperature(self) -> float: """Return the current temperature.""" - return float(self.coordinator.data[self.heat_area_id].get("T_ACTUAL", 0.0)) + return float( + self.coordinator.data["heat_areas"][self.heat_area_id].get("T_ACTUAL", 0.0) + ) @property def hvac_mode(self) -> HVACMode: @@ -86,7 +95,9 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): @property def hvac_action(self) -> HVACAction: """Return the current running hvac operation.""" - if not self.coordinator.data[self.heat_area_id]["_HEATCTRL_STATE"]: + if not self.coordinator.data["heat_areas"][self.heat_area_id][ + "_HEATCTRL_STATE" + ]: return HVACAction.IDLE if self.coordinator.get_cooling(): return HVACAction.COOLING @@ -95,7 +106,9 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): @property def target_temperature(self) -> float: """Return the temperature we try to reach.""" - return float(self.coordinator.data[self.heat_area_id].get("T_TARGET", 0.0)) + return float( + self.coordinator.data["heat_areas"][self.heat_area_id].get("T_TARGET", 0.0) + ) async def async_set_temperature(self, **kwargs) -> None: """Set new target temperatures.""" @@ -109,9 +122,9 @@ class Alpha2Climate(CoordinatorEntity[Alpha2BaseCoordinator], ClimateEntity): @property def preset_mode(self) -> str: """Return the current preset mode.""" - if self.coordinator.data[self.heat_area_id]["HEATAREA_MODE"] == 1: + if self.coordinator.data["heat_areas"][self.heat_area_id]["HEATAREA_MODE"] == 1: return PRESET_DAY - if self.coordinator.data[self.heat_area_id]["HEATAREA_MODE"] == 2: + if self.coordinator.data["heat_areas"][self.heat_area_id]["HEATAREA_MODE"] == 2: return PRESET_NIGHT return PRESET_AUTO diff --git a/homeassistant/components/moehlenhoff_alpha2/manifest.json b/homeassistant/components/moehlenhoff_alpha2/manifest.json index db362163e72..12e7a927906 100644 --- a/homeassistant/components/moehlenhoff_alpha2/manifest.json +++ b/homeassistant/components/moehlenhoff_alpha2/manifest.json @@ -3,7 +3,7 @@ "name": "Möhlenhoff Alpha 2", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/moehlenhoff_alpha2", - "requirements": ["moehlenhoff-alpha2==1.1.2"], + "requirements": ["moehlenhoff-alpha2==1.2.1"], "iot_class": "local_push", "codeowners": ["@j-a-n"] } diff --git a/homeassistant/components/moehlenhoff_alpha2/sensor.py b/homeassistant/components/moehlenhoff_alpha2/sensor.py new file mode 100644 index 00000000000..d26786e1923 --- /dev/null +++ b/homeassistant/components/moehlenhoff_alpha2/sensor.py @@ -0,0 +1,56 @@ +"""Support for Alpha2 heat control valve opening sensors.""" + +from homeassistant.components.sensor import SensorEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import PERCENTAGE +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import Alpha2BaseCoordinator +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Add Alpha2 sensor entities from a config_entry.""" + + coordinator: Alpha2BaseCoordinator = hass.data[DOMAIN][config_entry.entry_id] + + # HEATCTRL attribute ACTOR_PERCENT is not available in older firmware versions + async_add_entities( + Alpha2HeatControlValveOpeningSensor(coordinator, heat_control_id) + for heat_control_id, heat_control in coordinator.data["heat_controls"].items() + if heat_control["INUSE"] + and heat_control["_HEATAREA_ID"] + and heat_control.get("ACTOR_PERCENT") is not None + ) + + +class Alpha2HeatControlValveOpeningSensor( + CoordinatorEntity[Alpha2BaseCoordinator], SensorEntity +): + """Alpha2 heat control valve opening sensor.""" + + _attr_native_unit_of_measurement = PERCENTAGE + + def __init__( + self, coordinator: Alpha2BaseCoordinator, heat_control_id: str + ) -> None: + """Initialize Alpha2HeatControlValveOpeningSensor.""" + super().__init__(coordinator) + self.heat_control_id = heat_control_id + self._attr_unique_id = f"{heat_control_id}:valve_opening" + heat_control = self.coordinator.data["heat_controls"][heat_control_id] + heat_area = self.coordinator.data["heat_areas"][heat_control["_HEATAREA_ID"]] + self._attr_name = f"{heat_area['HEATAREA_NAME']} heat control {heat_control['NR']} valve opening" + + @property + def native_value(self) -> int: + """Return the current valve opening percentage.""" + return self.coordinator.data["heat_controls"][self.heat_control_id][ + "ACTOR_PERCENT" + ] diff --git a/requirements_all.txt b/requirements_all.txt index b5c731367c5..9bfc8b3671e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1035,7 +1035,7 @@ minio==5.0.10 mitemp_bt==0.0.5 # homeassistant.components.moehlenhoff_alpha2 -moehlenhoff-alpha2==1.1.2 +moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds motionblinds==0.6.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f862c719f50..1e6700d3add 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -712,7 +712,7 @@ millheater==0.9.0 minio==5.0.10 # homeassistant.components.moehlenhoff_alpha2 -moehlenhoff-alpha2==1.1.2 +moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds motionblinds==0.6.7 From d620072585053e87c63b3413f63d00c08ba6bb95 Mon Sep 17 00:00:00 2001 From: Greg Dowling Date: Tue, 24 May 2022 14:16:02 +0100 Subject: [PATCH 769/930] Remove pavoni as vera codeowner (#72421) --- CODEOWNERS | 2 -- homeassistant/components/vera/manifest.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 05e0d0b4377..887534e3461 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1116,8 +1116,6 @@ build.json @home-assistant/supervisor /homeassistant/components/velux/ @Julius2342 /homeassistant/components/venstar/ @garbled1 /tests/components/venstar/ @garbled1 -/homeassistant/components/vera/ @pavoni -/tests/components/vera/ @pavoni /homeassistant/components/verisure/ @frenck /tests/components/verisure/ @frenck /homeassistant/components/versasense/ @flamm3blemuff1n diff --git a/homeassistant/components/vera/manifest.json b/homeassistant/components/vera/manifest.json index 5a87ae29483..517d19a89d9 100644 --- a/homeassistant/components/vera/manifest.json +++ b/homeassistant/components/vera/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/vera", "requirements": ["pyvera==0.3.13"], - "codeowners": ["@pavoni"], + "codeowners": [], "iot_class": "local_polling", "loggers": ["pyvera"] } From 23bd64b7a26459e3e19b2d7fc05228214dd65a23 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 24 May 2022 15:34:46 +0200 Subject: [PATCH 770/930] Prevent duplication of statistics metadata (#71637) * Prevent duplication of statistics metadata * Add models_schema_28.py * Handle entity renaming as a recorder job * Improve tests --- homeassistant/components/recorder/core.py | 13 +- .../components/recorder/migration.py | 23 +- homeassistant/components/recorder/models.py | 4 +- .../components/recorder/statistics.py | 106 ++- homeassistant/components/recorder/tasks.py | 9 +- .../components/recorder/websocket_api.py | 2 +- tests/components/recorder/models_schema_28.py | 753 ++++++++++++++++++ tests/components/recorder/test_statistics.py | 297 ++++++- 8 files changed, 1175 insertions(+), 32 deletions(-) create mode 100644 tests/components/recorder/models_schema_28.py diff --git a/homeassistant/components/recorder/core.py b/homeassistant/components/recorder/core.py index 2ba07e42f49..7df4cf57e56 100644 --- a/homeassistant/components/recorder/core.py +++ b/homeassistant/components/recorder/core.py @@ -35,6 +35,7 @@ from homeassistant.helpers.event import ( async_track_time_interval, async_track_utc_time_change, ) +from homeassistant.helpers.typing import UNDEFINED, UndefinedType import homeassistant.util.dt as dt_util from . import migration, statistics @@ -461,10 +462,18 @@ class Recorder(threading.Thread): @callback def async_update_statistics_metadata( - self, statistic_id: str, unit_of_measurement: str | None + self, + statistic_id: str, + *, + new_statistic_id: str | UndefinedType = UNDEFINED, + new_unit_of_measurement: str | None | UndefinedType = UNDEFINED, ) -> None: """Update statistics metadata for a statistic_id.""" - self.queue_task(UpdateStatisticsMetadataTask(statistic_id, unit_of_measurement)) + self.queue_task( + UpdateStatisticsMetadataTask( + statistic_id, new_statistic_id, new_unit_of_measurement + ) + ) @callback def async_external_statistics( diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index 5da31f18781..eadfc543b59 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -33,7 +33,11 @@ from .models import ( StatisticsShortTerm, process_timestamp, ) -from .statistics import delete_duplicates, get_start_time +from .statistics import ( + delete_statistics_duplicates, + delete_statistics_meta_duplicates, + get_start_time, +) from .util import session_scope _LOGGER = logging.getLogger(__name__) @@ -670,7 +674,7 @@ def _apply_update( # noqa: C901 # There may be duplicated statistics entries, delete duplicated statistics # and try again with session_scope(session=session_maker()) as session: - delete_duplicates(hass, session) + delete_statistics_duplicates(hass, session) _create_index( session_maker, "statistics", "ix_statistics_statistic_id_start" ) @@ -705,6 +709,21 @@ def _apply_update( # noqa: C901 _create_index(session_maker, "states", "ix_states_context_id") # Once there are no longer any state_changed events # in the events table we can drop the index on states.event_id + elif new_version == 29: + # Recreate statistics_meta index to block duplicated statistic_id + _drop_index(session_maker, "statistics_meta", "ix_statistics_meta_statistic_id") + try: + _create_index( + session_maker, "statistics_meta", "ix_statistics_meta_statistic_id" + ) + except DatabaseError: + # There may be duplicated statistics_meta entries, delete duplicates + # and try again + with session_scope(session=session_maker()) as session: + delete_statistics_meta_duplicates(session) + _create_index( + session_maker, "statistics_meta", "ix_statistics_meta_statistic_id" + ) else: raise ValueError(f"No schema migration defined for version {new_version}") diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 4dabd7899e0..90c2e5e5616 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -51,7 +51,7 @@ from .const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP # pylint: disable=invalid-name Base = declarative_base() -SCHEMA_VERSION = 28 +SCHEMA_VERSION = 29 _LOGGER = logging.getLogger(__name__) @@ -515,7 +515,7 @@ class StatisticsMeta(Base): # type: ignore[misc,valid-type] ) __tablename__ = TABLE_STATISTICS_META id = Column(Integer, Identity(), primary_key=True) - statistic_id = Column(String(255), index=True) + statistic_id = Column(String(255), index=True, unique=True) source = Column(String(32)) unit_of_measurement = Column(String(255)) has_mean = Column(Boolean) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 2c6c51a31f4..4bed39fee4a 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -33,6 +33,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.storage import STORAGE_DIR +from homeassistant.helpers.typing import UNDEFINED, UndefinedType import homeassistant.util.dt as dt_util import homeassistant.util.pressure as pressure_util import homeassistant.util.temperature as temperature_util @@ -208,18 +209,11 @@ class ValidationIssue: def async_setup(hass: HomeAssistant) -> None: """Set up the history hooks.""" - def _entity_id_changed(event: Event) -> None: - """Handle entity_id changed.""" - old_entity_id = event.data["old_entity_id"] - entity_id = event.data["entity_id"] - with session_scope(hass=hass) as session: - session.query(StatisticsMeta).filter( - (StatisticsMeta.statistic_id == old_entity_id) - & (StatisticsMeta.source == DOMAIN) - ).update({StatisticsMeta.statistic_id: entity_id}) - - async def _async_entity_id_changed(event: Event) -> None: - await hass.data[DATA_INSTANCE].async_add_executor_job(_entity_id_changed, event) + @callback + def _async_entity_id_changed(event: Event) -> None: + hass.data[DATA_INSTANCE].async_update_statistics_metadata( + event.data["old_entity_id"], new_statistic_id=event.data["entity_id"] + ) @callback def entity_registry_changed_filter(event: Event) -> bool: @@ -380,7 +374,7 @@ def _delete_duplicates_from_table( return (total_deleted_rows, all_non_identical_duplicates) -def delete_duplicates(hass: HomeAssistant, session: Session) -> None: +def delete_statistics_duplicates(hass: HomeAssistant, session: Session) -> None: """Identify and delete duplicated statistics. A backup will be made of duplicated statistics before it is deleted. @@ -423,6 +417,69 @@ def delete_duplicates(hass: HomeAssistant, session: Session) -> None: ) +def _find_statistics_meta_duplicates(session: Session) -> list[int]: + """Find duplicated statistics_meta.""" + subquery = ( + session.query( + StatisticsMeta.statistic_id, + literal_column("1").label("is_duplicate"), + ) + .group_by(StatisticsMeta.statistic_id) + .having(func.count() > 1) + .subquery() + ) + query = ( + session.query(StatisticsMeta) + .outerjoin( + subquery, + (subquery.c.statistic_id == StatisticsMeta.statistic_id), + ) + .filter(subquery.c.is_duplicate == 1) + .order_by(StatisticsMeta.statistic_id, StatisticsMeta.id.desc()) + .limit(1000 * MAX_ROWS_TO_PURGE) + ) + duplicates = execute(query) + statistic_id = None + duplicate_ids: list[int] = [] + + if not duplicates: + return duplicate_ids + + for duplicate in duplicates: + if statistic_id != duplicate.statistic_id: + statistic_id = duplicate.statistic_id + continue + duplicate_ids.append(duplicate.id) + + return duplicate_ids + + +def _delete_statistics_meta_duplicates(session: Session) -> int: + """Identify and delete duplicated statistics from a specified table.""" + total_deleted_rows = 0 + while True: + duplicate_ids = _find_statistics_meta_duplicates(session) + if not duplicate_ids: + break + for i in range(0, len(duplicate_ids), MAX_ROWS_TO_PURGE): + deleted_rows = ( + session.query(StatisticsMeta) + .filter(StatisticsMeta.id.in_(duplicate_ids[i : i + MAX_ROWS_TO_PURGE])) + .delete(synchronize_session=False) + ) + total_deleted_rows += deleted_rows + return total_deleted_rows + + +def delete_statistics_meta_duplicates(session: Session) -> None: + """Identify and delete duplicated statistics_meta.""" + deleted_statistics_rows = _delete_statistics_meta_duplicates(session) + if deleted_statistics_rows: + _LOGGER.info( + "Deleted %s duplicated statistics_meta rows", deleted_statistics_rows + ) + + def _compile_hourly_statistics_summary_mean_stmt( start_time: datetime, end_time: datetime ) -> StatementLambdaElement: @@ -736,13 +793,26 @@ def clear_statistics(instance: Recorder, statistic_ids: list[str]) -> None: def update_statistics_metadata( - instance: Recorder, statistic_id: str, unit_of_measurement: str | None + instance: Recorder, + statistic_id: str, + new_statistic_id: str | None | UndefinedType, + new_unit_of_measurement: str | None | UndefinedType, ) -> None: """Update statistics metadata for a statistic_id.""" - with session_scope(session=instance.get_session()) as session: - session.query(StatisticsMeta).filter( - StatisticsMeta.statistic_id == statistic_id - ).update({StatisticsMeta.unit_of_measurement: unit_of_measurement}) + if new_unit_of_measurement is not UNDEFINED: + with session_scope(session=instance.get_session()) as session: + session.query(StatisticsMeta).filter( + StatisticsMeta.statistic_id == statistic_id + ).update({StatisticsMeta.unit_of_measurement: new_unit_of_measurement}) + if new_statistic_id is not UNDEFINED: + with session_scope( + session=instance.get_session(), + exception_filter=_filter_unique_constraint_integrity_error(instance), + ) as session: + session.query(StatisticsMeta).filter( + (StatisticsMeta.statistic_id == statistic_id) + & (StatisticsMeta.source == DOMAIN) + ).update({StatisticsMeta.statistic_id: new_statistic_id}) def list_statistic_ids( diff --git a/homeassistant/components/recorder/tasks.py b/homeassistant/components/recorder/tasks.py index 07855d27dff..5ec83a3cefc 100644 --- a/homeassistant/components/recorder/tasks.py +++ b/homeassistant/components/recorder/tasks.py @@ -10,6 +10,7 @@ import threading from typing import TYPE_CHECKING, Any from homeassistant.core import Event +from homeassistant.helpers.typing import UndefinedType from . import purge, statistics from .const import DOMAIN, EXCLUDE_ATTRIBUTES @@ -46,12 +47,16 @@ class UpdateStatisticsMetadataTask(RecorderTask): """Object to store statistics_id and unit for update of statistics metadata.""" statistic_id: str - unit_of_measurement: str | None + new_statistic_id: str | None | UndefinedType + new_unit_of_measurement: str | None | UndefinedType def run(self, instance: Recorder) -> None: """Handle the task.""" statistics.update_statistics_metadata( - instance, self.statistic_id, self.unit_of_measurement + instance, + self.statistic_id, + self.new_statistic_id, + self.new_unit_of_measurement, ) diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index a851d2681f4..d0499fbf9cb 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -103,7 +103,7 @@ def ws_update_statistics_metadata( ) -> None: """Update statistics metadata for a statistic_id.""" hass.data[DATA_INSTANCE].async_update_statistics_metadata( - msg["statistic_id"], msg["unit_of_measurement"] + msg["statistic_id"], new_unit_of_measurement=msg["unit_of_measurement"] ) connection.send_result(msg["id"]) diff --git a/tests/components/recorder/models_schema_28.py b/tests/components/recorder/models_schema_28.py new file mode 100644 index 00000000000..8d2de0432ac --- /dev/null +++ b/tests/components/recorder/models_schema_28.py @@ -0,0 +1,753 @@ +"""Models for SQLAlchemy. + +This file contains the model definitions for schema version 28. +It is used to test the schema migration logic. +""" +from __future__ import annotations + +from datetime import datetime, timedelta +import json +import logging +from typing import Any, TypedDict, cast, overload + +from fnvhash import fnv1a_32 +from sqlalchemy import ( + BigInteger, + Boolean, + Column, + DateTime, + Float, + ForeignKey, + Identity, + Index, + Integer, + SmallInteger, + String, + Text, + distinct, +) +from sqlalchemy.dialects import mysql, oracle, postgresql +from sqlalchemy.engine.row import Row +from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm.session import Session + +from homeassistant.components.recorder.const import ALL_DOMAIN_EXCLUDE_ATTRS, JSON_DUMP +from homeassistant.const import ( + MAX_LENGTH_EVENT_CONTEXT_ID, + MAX_LENGTH_EVENT_EVENT_TYPE, + MAX_LENGTH_EVENT_ORIGIN, + MAX_LENGTH_STATE_ENTITY_ID, + MAX_LENGTH_STATE_STATE, +) +from homeassistant.core import Context, Event, EventOrigin, State, split_entity_id +import homeassistant.util.dt as dt_util + +# SQLAlchemy Schema +# pylint: disable=invalid-name +Base = declarative_base() + +SCHEMA_VERSION = 28 + +_LOGGER = logging.getLogger(__name__) + +DB_TIMEZONE = "+00:00" + +TABLE_EVENTS = "events" +TABLE_EVENT_DATA = "event_data" +TABLE_STATES = "states" +TABLE_STATE_ATTRIBUTES = "state_attributes" +TABLE_RECORDER_RUNS = "recorder_runs" +TABLE_SCHEMA_CHANGES = "schema_changes" +TABLE_STATISTICS = "statistics" +TABLE_STATISTICS_META = "statistics_meta" +TABLE_STATISTICS_RUNS = "statistics_runs" +TABLE_STATISTICS_SHORT_TERM = "statistics_short_term" + +ALL_TABLES = [ + TABLE_STATES, + TABLE_STATE_ATTRIBUTES, + TABLE_EVENTS, + TABLE_EVENT_DATA, + TABLE_RECORDER_RUNS, + TABLE_SCHEMA_CHANGES, + TABLE_STATISTICS, + TABLE_STATISTICS_META, + TABLE_STATISTICS_RUNS, + TABLE_STATISTICS_SHORT_TERM, +] + +TABLES_TO_CHECK = [ + TABLE_STATES, + TABLE_EVENTS, + TABLE_RECORDER_RUNS, + TABLE_SCHEMA_CHANGES, +] + + +EMPTY_JSON_OBJECT = "{}" + + +DATETIME_TYPE = DateTime(timezone=True).with_variant( + mysql.DATETIME(timezone=True, fsp=6), "mysql" +) +DOUBLE_TYPE = ( + Float() + .with_variant(mysql.DOUBLE(asdecimal=False), "mysql") + .with_variant(oracle.DOUBLE_PRECISION(), "oracle") + .with_variant(postgresql.DOUBLE_PRECISION(), "postgresql") +) +EVENT_ORIGIN_ORDER = [EventOrigin.local, EventOrigin.remote] +EVENT_ORIGIN_TO_IDX = {origin: idx for idx, origin in enumerate(EVENT_ORIGIN_ORDER)} + + +class Events(Base): # type: ignore[misc,valid-type] + """Event history data.""" + + __table_args__ = ( + # Used for fetching events at a specific time + # see logbook + Index("ix_events_event_type_time_fired", "event_type", "time_fired"), + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_EVENTS + event_id = Column(Integer, Identity(), primary_key=True) # no longer used + event_type = Column(String(MAX_LENGTH_EVENT_EVENT_TYPE)) + event_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + origin = Column(String(MAX_LENGTH_EVENT_ORIGIN)) # no longer used + origin_idx = Column(SmallInteger) + time_fired = Column(DATETIME_TYPE, index=True) + context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + data_id = Column(Integer, ForeignKey("event_data.data_id"), index=True) + event_data_rel = relationship("EventData") + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event: Event) -> Events: + """Create an event database object from a native event.""" + return Events( + event_type=event.event_type, + event_data=None, + origin_idx=EVENT_ORIGIN_TO_IDX.get(event.origin), + time_fired=event.time_fired, + context_id=event.context.id, + context_user_id=event.context.user_id, + context_parent_id=event.context.parent_id, + ) + + def to_native(self, validate_entity_id: bool = True) -> Event | None: + """Convert to a native HA Event.""" + context = Context( + id=self.context_id, + user_id=self.context_user_id, + parent_id=self.context_parent_id, + ) + try: + return Event( + self.event_type, + json.loads(self.event_data) if self.event_data else {}, + EventOrigin(self.origin) + if self.origin + else EVENT_ORIGIN_ORDER[self.origin_idx], + process_timestamp(self.time_fired), + context=context, + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting to event: %s", self) + return None + + +class EventData(Base): # type: ignore[misc,valid-type] + """Event data history.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_EVENT_DATA + data_id = Column(Integer, Identity(), primary_key=True) + hash = Column(BigInteger, index=True) + # Note that this is not named attributes to avoid confusion with the states table + shared_data = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event: Event) -> EventData: + """Create object from an event.""" + shared_data = JSON_DUMP(event.data) + return EventData( + shared_data=shared_data, hash=EventData.hash_shared_data(shared_data) + ) + + @staticmethod + def shared_data_from_event(event: Event) -> str: + """Create shared_attrs from an event.""" + return JSON_DUMP(event.data) + + @staticmethod + def hash_shared_data(shared_data: str) -> int: + """Return the hash of json encoded shared data.""" + return cast(int, fnv1a_32(shared_data.encode("utf-8"))) + + def to_native(self) -> dict[str, Any]: + """Convert to an HA state object.""" + try: + return cast(dict[str, Any], json.loads(self.shared_data)) + except ValueError: + _LOGGER.exception("Error converting row to event data: %s", self) + return {} + + +class States(Base): # type: ignore[misc,valid-type] + """State change history.""" + + __table_args__ = ( + # Used for fetching the state of entities at a specific time + # (get_states in history.py) + Index("ix_states_entity_id_last_updated", "entity_id", "last_updated"), + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_STATES + state_id = Column(Integer, Identity(), primary_key=True) + entity_id = Column(String(MAX_LENGTH_STATE_ENTITY_ID)) + state = Column(String(MAX_LENGTH_STATE_STATE)) + attributes = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + event_id = Column( + Integer, ForeignKey("events.event_id", ondelete="CASCADE"), index=True + ) + last_changed = Column(DATETIME_TYPE, default=dt_util.utcnow) + last_updated = Column(DATETIME_TYPE, default=dt_util.utcnow, index=True) + old_state_id = Column(Integer, ForeignKey("states.state_id"), index=True) + attributes_id = Column( + Integer, ForeignKey("state_attributes.attributes_id"), index=True + ) + context_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID), index=True) + context_user_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + context_parent_id = Column(String(MAX_LENGTH_EVENT_CONTEXT_ID)) + origin_idx = Column(SmallInteger) # 0 is local, 1 is remote + old_state = relationship("States", remote_side=[state_id]) + state_attributes = relationship("StateAttributes") + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event: Event) -> States: + """Create object from a state_changed event.""" + entity_id = event.data["entity_id"] + state: State | None = event.data.get("new_state") + dbstate = States( + entity_id=entity_id, + attributes=None, + context_id=event.context.id, + context_user_id=event.context.user_id, + context_parent_id=event.context.parent_id, + origin_idx=EVENT_ORIGIN_TO_IDX.get(event.origin), + ) + + # None state means the state was removed from the state machine + if state is None: + dbstate.state = "" + dbstate.last_changed = event.time_fired + dbstate.last_updated = event.time_fired + else: + dbstate.state = state.state + dbstate.last_changed = state.last_changed + dbstate.last_updated = state.last_updated + + return dbstate + + def to_native(self, validate_entity_id: bool = True) -> State | None: + """Convert to an HA state object.""" + context = Context( + id=self.context_id, + user_id=self.context_user_id, + parent_id=self.context_parent_id, + ) + try: + return State( + self.entity_id, + self.state, + # Join the state_attributes table on attributes_id to get the attributes + # for newer states + json.loads(self.attributes) if self.attributes else {}, + process_timestamp(self.last_changed), + process_timestamp(self.last_updated), + context=context, + validate_entity_id=validate_entity_id, + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting row to state: %s", self) + return None + + +class StateAttributes(Base): # type: ignore[misc,valid-type] + """State attribute change history.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_STATE_ATTRIBUTES + attributes_id = Column(Integer, Identity(), primary_key=True) + hash = Column(BigInteger, index=True) + # Note that this is not named attributes to avoid confusion with the states table + shared_attrs = Column(Text().with_variant(mysql.LONGTEXT, "mysql")) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + @staticmethod + def from_event(event: Event) -> StateAttributes: + """Create object from a state_changed event.""" + state: State | None = event.data.get("new_state") + # None state means the state was removed from the state machine + dbstate = StateAttributes( + shared_attrs="{}" if state is None else JSON_DUMP(state.attributes) + ) + dbstate.hash = StateAttributes.hash_shared_attrs(dbstate.shared_attrs) + return dbstate + + @staticmethod + def shared_attrs_from_event( + event: Event, exclude_attrs_by_domain: dict[str, set[str]] + ) -> str: + """Create shared_attrs from a state_changed event.""" + state: State | None = event.data.get("new_state") + # None state means the state was removed from the state machine + if state is None: + return "{}" + domain = split_entity_id(state.entity_id)[0] + exclude_attrs = ( + exclude_attrs_by_domain.get(domain, set()) | ALL_DOMAIN_EXCLUDE_ATTRS + ) + return JSON_DUMP( + {k: v for k, v in state.attributes.items() if k not in exclude_attrs} + ) + + @staticmethod + def hash_shared_attrs(shared_attrs: str) -> int: + """Return the hash of json encoded shared attributes.""" + return cast(int, fnv1a_32(shared_attrs.encode("utf-8"))) + + def to_native(self) -> dict[str, Any]: + """Convert to an HA state object.""" + try: + return cast(dict[str, Any], json.loads(self.shared_attrs)) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting row to state attributes: %s", self) + return {} + + +class StatisticResult(TypedDict): + """Statistic result data class. + + Allows multiple datapoints for the same statistic_id. + """ + + meta: StatisticMetaData + stat: StatisticData + + +class StatisticDataBase(TypedDict): + """Mandatory fields for statistic data class.""" + + start: datetime + + +class StatisticData(StatisticDataBase, total=False): + """Statistic data class.""" + + mean: float + min: float + max: float + last_reset: datetime | None + state: float + sum: float + + +class StatisticsBase: + """Statistics base class.""" + + id = Column(Integer, Identity(), primary_key=True) + created = Column(DATETIME_TYPE, default=dt_util.utcnow) + + @declared_attr # type: ignore[misc] + def metadata_id(self) -> Column: + """Define the metadata_id column for sub classes.""" + return Column( + Integer, + ForeignKey(f"{TABLE_STATISTICS_META}.id", ondelete="CASCADE"), + index=True, + ) + + start = Column(DATETIME_TYPE, index=True) + mean = Column(DOUBLE_TYPE) + min = Column(DOUBLE_TYPE) + max = Column(DOUBLE_TYPE) + last_reset = Column(DATETIME_TYPE) + state = Column(DOUBLE_TYPE) + sum = Column(DOUBLE_TYPE) + + @classmethod + def from_stats(cls, metadata_id: int, stats: StatisticData) -> StatisticsBase: + """Create object from a statistics.""" + return cls( # type: ignore[call-arg,misc] + metadata_id=metadata_id, + **stats, + ) + + +class Statistics(Base, StatisticsBase): # type: ignore[misc,valid-type] + """Long term statistics.""" + + duration = timedelta(hours=1) + + __table_args__ = ( + # Used for fetching statistics for a certain entity at a specific time + Index("ix_statistics_statistic_id_start", "metadata_id", "start", unique=True), + ) + __tablename__ = TABLE_STATISTICS + + +class StatisticsShortTerm(Base, StatisticsBase): # type: ignore[misc,valid-type] + """Short term statistics.""" + + duration = timedelta(minutes=5) + + __table_args__ = ( + # Used for fetching statistics for a certain entity at a specific time + Index( + "ix_statistics_short_term_statistic_id_start", + "metadata_id", + "start", + unique=True, + ), + ) + __tablename__ = TABLE_STATISTICS_SHORT_TERM + + +class StatisticMetaData(TypedDict): + """Statistic meta data class.""" + + has_mean: bool + has_sum: bool + name: str | None + source: str + statistic_id: str + unit_of_measurement: str | None + + +class StatisticsMeta(Base): # type: ignore[misc,valid-type] + """Statistics meta data.""" + + __table_args__ = ( + {"mysql_default_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}, + ) + __tablename__ = TABLE_STATISTICS_META + id = Column(Integer, Identity(), primary_key=True) + statistic_id = Column(String(255), index=True) + source = Column(String(32)) + unit_of_measurement = Column(String(255)) + has_mean = Column(Boolean) + has_sum = Column(Boolean) + name = Column(String(255)) + + @staticmethod + def from_meta(meta: StatisticMetaData) -> StatisticsMeta: + """Create object from meta data.""" + return StatisticsMeta(**meta) + + +class RecorderRuns(Base): # type: ignore[misc,valid-type] + """Representation of recorder run.""" + + __table_args__ = (Index("ix_recorder_runs_start_end", "start", "end"),) + __tablename__ = TABLE_RECORDER_RUNS + run_id = Column(Integer, Identity(), primary_key=True) + start = Column(DateTime(timezone=True), default=dt_util.utcnow) + end = Column(DateTime(timezone=True)) + closed_incorrect = Column(Boolean, default=False) + created = Column(DateTime(timezone=True), default=dt_util.utcnow) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + end = ( + f"'{self.end.isoformat(sep=' ', timespec='seconds')}'" if self.end else None + ) + return ( + f"" + ) + + def entity_ids(self, point_in_time: datetime | None = None) -> list[str]: + """Return the entity ids that existed in this run. + + Specify point_in_time if you want to know which existed at that point + in time inside the run. + """ + session = Session.object_session(self) + + assert session is not None, "RecorderRuns need to be persisted" + + query = session.query(distinct(States.entity_id)).filter( + States.last_updated >= self.start + ) + + if point_in_time is not None: + query = query.filter(States.last_updated < point_in_time) + elif self.end is not None: + query = query.filter(States.last_updated < self.end) + + return [row[0] for row in query] + + def to_native(self, validate_entity_id: bool = True) -> RecorderRuns: + """Return self, native format is this model.""" + return self + + +class SchemaChanges(Base): # type: ignore[misc,valid-type] + """Representation of schema version changes.""" + + __tablename__ = TABLE_SCHEMA_CHANGES + change_id = Column(Integer, Identity(), primary_key=True) + schema_version = Column(Integer) + changed = Column(DateTime(timezone=True), default=dt_util.utcnow) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + +class StatisticsRuns(Base): # type: ignore[misc,valid-type] + """Representation of statistics run.""" + + __tablename__ = TABLE_STATISTICS_RUNS + run_id = Column(Integer, Identity(), primary_key=True) + start = Column(DateTime(timezone=True), index=True) + + def __repr__(self) -> str: + """Return string representation of instance for debugging.""" + return ( + f"" + ) + + +@overload +def process_timestamp(ts: None) -> None: + ... + + +@overload +def process_timestamp(ts: datetime) -> datetime: + ... + + +def process_timestamp(ts: datetime | None) -> datetime | None: + """Process a timestamp into datetime object.""" + if ts is None: + return None + if ts.tzinfo is None: + return ts.replace(tzinfo=dt_util.UTC) + + return dt_util.as_utc(ts) + + +@overload +def process_timestamp_to_utc_isoformat(ts: None) -> None: + ... + + +@overload +def process_timestamp_to_utc_isoformat(ts: datetime) -> str: + ... + + +def process_timestamp_to_utc_isoformat(ts: datetime | None) -> str | None: + """Process a timestamp into UTC isotime.""" + if ts is None: + return None + if ts.tzinfo == dt_util.UTC: + return ts.isoformat() + if ts.tzinfo is None: + return f"{ts.isoformat()}{DB_TIMEZONE}" + return ts.astimezone(dt_util.UTC).isoformat() + + +class LazyState(State): + """A lazy version of core State.""" + + __slots__ = [ + "_row", + "_attributes", + "_last_changed", + "_last_updated", + "_context", + "_attr_cache", + ] + + def __init__( # pylint: disable=super-init-not-called + self, row: Row, attr_cache: dict[str, dict[str, Any]] | None = None + ) -> None: + """Init the lazy state.""" + self._row = row + self.entity_id: str = self._row.entity_id + self.state = self._row.state or "" + self._attributes: dict[str, Any] | None = None + self._last_changed: datetime | None = None + self._last_updated: datetime | None = None + self._context: Context | None = None + self._attr_cache = attr_cache + + @property # type: ignore[override] + def attributes(self) -> dict[str, Any]: # type: ignore[override] + """State attributes.""" + if self._attributes is None: + source = self._row.shared_attrs or self._row.attributes + if self._attr_cache is not None and ( + attributes := self._attr_cache.get(source) + ): + self._attributes = attributes + return attributes + if source == EMPTY_JSON_OBJECT or source is None: + self._attributes = {} + return self._attributes + try: + self._attributes = json.loads(source) + except ValueError: + # When json.loads fails + _LOGGER.exception( + "Error converting row to state attributes: %s", self._row + ) + self._attributes = {} + if self._attr_cache is not None: + self._attr_cache[source] = self._attributes + return self._attributes + + @attributes.setter + def attributes(self, value: dict[str, Any]) -> None: + """Set attributes.""" + self._attributes = value + + @property # type: ignore[override] + def context(self) -> Context: # type: ignore[override] + """State context.""" + if self._context is None: + self._context = Context(id=None) # type: ignore[arg-type] + return self._context + + @context.setter + def context(self, value: Context) -> None: + """Set context.""" + self._context = value + + @property # type: ignore[override] + def last_changed(self) -> datetime: # type: ignore[override] + """Last changed datetime.""" + if self._last_changed is None: + self._last_changed = process_timestamp(self._row.last_changed) + return self._last_changed + + @last_changed.setter + def last_changed(self, value: datetime) -> None: + """Set last changed datetime.""" + self._last_changed = value + + @property # type: ignore[override] + def last_updated(self) -> datetime: # type: ignore[override] + """Last updated datetime.""" + if self._last_updated is None: + if (last_updated := self._row.last_updated) is not None: + self._last_updated = process_timestamp(last_updated) + else: + self._last_updated = self.last_changed + return self._last_updated + + @last_updated.setter + def last_updated(self, value: datetime) -> None: + """Set last updated datetime.""" + self._last_updated = value + + def as_dict(self) -> dict[str, Any]: # type: ignore[override] + """Return a dict representation of the LazyState. + + Async friendly. + + To be used for JSON serialization. + """ + if self._last_changed is None and self._last_updated is None: + last_changed_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_changed + ) + if ( + self._row.last_updated is None + or self._row.last_changed == self._row.last_updated + ): + last_updated_isoformat = last_changed_isoformat + else: + last_updated_isoformat = process_timestamp_to_utc_isoformat( + self._row.last_updated + ) + else: + last_changed_isoformat = self.last_changed.isoformat() + if self.last_changed == self.last_updated: + last_updated_isoformat = last_changed_isoformat + else: + last_updated_isoformat = self.last_updated.isoformat() + return { + "entity_id": self.entity_id, + "state": self.state, + "attributes": self._attributes or self.attributes, + "last_changed": last_changed_isoformat, + "last_updated": last_updated_isoformat, + } + + def __eq__(self, other: Any) -> bool: + """Return the comparison.""" + return ( + other.__class__ in [self.__class__, State] + and self.entity_id == other.entity_id + and self.state == other.state + and self.attributes == other.attributes + ) diff --git a/tests/components/recorder/test_statistics.py b/tests/components/recorder/test_statistics.py index 28eba51a4f3..882f00d2940 100644 --- a/tests/components/recorder/test_statistics.py +++ b/tests/components/recorder/test_statistics.py @@ -1,21 +1,26 @@ """The tests for sensor recorder platform.""" # pylint: disable=protected-access,invalid-name from datetime import timedelta +import importlib +import sys from unittest.mock import patch, sentinel import pytest from pytest import approx +from sqlalchemy import create_engine +from sqlalchemy.orm import Session from homeassistant.components import recorder from homeassistant.components.recorder import history, statistics -from homeassistant.components.recorder.const import DATA_INSTANCE +from homeassistant.components.recorder.const import DATA_INSTANCE, SQLITE_URL_PREFIX from homeassistant.components.recorder.models import ( StatisticsShortTerm, process_timestamp_to_utc_isoformat, ) from homeassistant.components.recorder.statistics import ( async_add_external_statistics, - delete_duplicates, + delete_statistics_duplicates, + delete_statistics_meta_duplicates, get_last_short_term_statistics, get_last_statistics, get_latest_short_term_statistics, @@ -32,7 +37,7 @@ import homeassistant.util.dt as dt_util from .common import async_wait_recording_done, do_adhoc_statistics -from tests.common import mock_registry +from tests.common import get_test_home_assistant, mock_registry from tests.components.recorder.common import wait_recording_done ORIG_TZ = dt_util.DEFAULT_TIME_ZONE @@ -306,12 +311,92 @@ def test_rename_entity(hass_recorder): entity_reg.async_update_entity("sensor.test1", new_entity_id="sensor.test99") hass.add_job(rename_entry) - hass.block_till_done() + wait_recording_done(hass) stats = statistics_during_period(hass, zero, period="5minute") assert stats == {"sensor.test99": expected_stats99, "sensor.test2": expected_stats2} +def test_rename_entity_collision(hass_recorder, caplog): + """Test statistics is migrated when entity_id is changed.""" + hass = hass_recorder() + setup_component(hass, "sensor", {}) + + entity_reg = mock_registry(hass) + + @callback + def add_entry(): + reg_entry = entity_reg.async_get_or_create( + "sensor", + "test", + "unique_0000", + suggested_object_id="test1", + ) + assert reg_entry.entity_id == "sensor.test1" + + hass.add_job(add_entry) + hass.block_till_done() + + zero, four, states = record_states(hass) + hist = history.get_significant_states(hass, zero, four) + assert dict(states) == dict(hist) + + for kwargs in ({}, {"statistic_ids": ["sensor.test1"]}): + stats = statistics_during_period(hass, zero, period="5minute", **kwargs) + assert stats == {} + stats = get_last_short_term_statistics(hass, 0, "sensor.test1", True) + assert stats == {} + + do_adhoc_statistics(hass, start=zero) + wait_recording_done(hass) + expected_1 = { + "statistic_id": "sensor.test1", + "start": process_timestamp_to_utc_isoformat(zero), + "end": process_timestamp_to_utc_isoformat(zero + timedelta(minutes=5)), + "mean": approx(14.915254237288135), + "min": approx(10.0), + "max": approx(20.0), + "last_reset": None, + "state": None, + "sum": None, + } + expected_stats1 = [ + {**expected_1, "statistic_id": "sensor.test1"}, + ] + expected_stats2 = [ + {**expected_1, "statistic_id": "sensor.test2"}, + ] + + stats = statistics_during_period(hass, zero, period="5minute") + assert stats == {"sensor.test1": expected_stats1, "sensor.test2": expected_stats2} + + # Insert metadata for sensor.test99 + metadata_1 = { + "has_mean": True, + "has_sum": False, + "name": "Total imported energy", + "source": "test", + "statistic_id": "sensor.test99", + "unit_of_measurement": "kWh", + } + + with session_scope(hass=hass) as session: + session.add(recorder.models.StatisticsMeta.from_meta(metadata_1)) + + # Rename entity sensor.test1 to sensor.test99 + @callback + def rename_entry(): + entity_reg.async_update_entity("sensor.test1", new_entity_id="sensor.test99") + + hass.add_job(rename_entry) + wait_recording_done(hass) + + # Statistics failed to migrate due to the collision + stats = statistics_during_period(hass, zero, period="5minute") + assert stats == {"sensor.test1": expected_stats1, "sensor.test2": expected_stats2} + assert "Blocked attempt to insert duplicated statistic rows" in caplog.text + + def test_statistics_duplicated(hass_recorder, caplog): """Test statistics with same start time is not compiled.""" hass = hass_recorder() @@ -737,7 +822,7 @@ def test_delete_duplicates_no_duplicates(hass_recorder, caplog): hass = hass_recorder() wait_recording_done(hass) with session_scope(hass=hass) as session: - delete_duplicates(hass, session) + delete_statistics_duplicates(hass, session) assert "duplicated statistics rows" not in caplog.text assert "Found non identical" not in caplog.text assert "Found duplicated" not in caplog.text @@ -800,6 +885,208 @@ def test_duplicate_statistics_handle_integrity_error(hass_recorder, caplog): assert "Blocked attempt to insert duplicated statistic rows" in caplog.text +def _create_engine_28(*args, **kwargs): + """Test version of create_engine that initializes with old schema. + + This simulates an existing db with the old schema. + """ + module = "tests.components.recorder.models_schema_28" + importlib.import_module(module) + old_models = sys.modules[module] + engine = create_engine(*args, **kwargs) + old_models.Base.metadata.create_all(engine) + with Session(engine) as session: + session.add(recorder.models.StatisticsRuns(start=statistics.get_start_time())) + session.add( + recorder.models.SchemaChanges(schema_version=old_models.SCHEMA_VERSION) + ) + session.commit() + return engine + + +def test_delete_metadata_duplicates(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + module = "tests.components.recorder.models_schema_28" + importlib.import_module(module) + old_models = sys.modules[module] + + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_energy_metadata_2 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_co2_metadata = { + "has_mean": True, + "has_sum": False, + "name": "Fossil percentage", + "source": "test", + "statistic_id": "test:fossil_percentage", + "unit_of_measurement": "%", + } + + # Create some duplicated statistics_meta with schema version 28 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch( + "homeassistant.components.recorder.core.create_engine", new=_create_engine_28 + ): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) + + with session_scope(hass=hass) as session: + tmp = session.query(recorder.models.StatisticsMeta).all() + assert len(tmp) == 3 + assert tmp[0].id == 1 + assert tmp[0].statistic_id == "test:total_energy_import_tariff_1" + assert tmp[1].id == 2 + assert tmp[1].statistic_id == "test:total_energy_import_tariff_1" + assert tmp[2].id == 3 + assert tmp[2].statistic_id == "test:fossil_percentage" + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 28 + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + + assert "Deleted 1 duplicated statistics_meta rows" in caplog.text + with session_scope(hass=hass) as session: + tmp = session.query(recorder.models.StatisticsMeta).all() + assert len(tmp) == 2 + assert tmp[0].id == 2 + assert tmp[0].statistic_id == "test:total_energy_import_tariff_1" + assert tmp[1].id == 3 + assert tmp[1].statistic_id == "test:fossil_percentage" + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + +def test_delete_metadata_duplicates_many(caplog, tmpdir): + """Test removal of duplicated statistics.""" + test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db") + dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}" + + module = "tests.components.recorder.models_schema_28" + importlib.import_module(module) + old_models = sys.modules[module] + + external_energy_metadata_1 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_1", + "unit_of_measurement": "kWh", + } + external_energy_metadata_2 = { + "has_mean": False, + "has_sum": True, + "name": "Total imported energy", + "source": "test", + "statistic_id": "test:total_energy_import_tariff_2", + "unit_of_measurement": "kWh", + } + external_co2_metadata = { + "has_mean": True, + "has_sum": False, + "name": "Fossil percentage", + "source": "test", + "statistic_id": "test:fossil_percentage", + "unit_of_measurement": "%", + } + + # Create some duplicated statistics with schema version 28 + with patch.object(recorder, "models", old_models), patch.object( + recorder.migration, "SCHEMA_VERSION", old_models.SCHEMA_VERSION + ), patch( + "homeassistant.components.recorder.core.create_engine", new=_create_engine_28 + ): + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + wait_recording_done(hass) + wait_recording_done(hass) + + with session_scope(hass=hass) as session: + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + for _ in range(3000): + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_1) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + session.add( + recorder.models.StatisticsMeta.from_meta(external_energy_metadata_2) + ) + session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) + session.add(recorder.models.StatisticsMeta.from_meta(external_co2_metadata)) + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + # Test that the duplicates are removed during migration from schema 28 + hass = get_test_home_assistant() + setup_component(hass, "recorder", {"recorder": {"db_url": dburl}}) + hass.start() + wait_recording_done(hass) + wait_recording_done(hass) + + assert "Deleted 3002 duplicated statistics_meta rows" in caplog.text + with session_scope(hass=hass) as session: + tmp = session.query(recorder.models.StatisticsMeta).all() + assert len(tmp) == 3 + assert tmp[0].id == 3001 + assert tmp[0].statistic_id == "test:total_energy_import_tariff_1" + assert tmp[1].id == 3003 + assert tmp[1].statistic_id == "test:total_energy_import_tariff_2" + assert tmp[2].id == 3005 + assert tmp[2].statistic_id == "test:fossil_percentage" + + hass.stop() + dt_util.DEFAULT_TIME_ZONE = ORIG_TZ + + +def test_delete_metadata_duplicates_no_duplicates(hass_recorder, caplog): + """Test removal of duplicated statistics.""" + hass = hass_recorder() + wait_recording_done(hass) + with session_scope(hass=hass) as session: + delete_statistics_meta_duplicates(session) + assert "duplicated statistics_meta rows" not in caplog.text + + def record_states(hass): """Record some test states. From 6368df5e37cb391811ec2967bc53e77fe8d2f942 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 24 May 2022 15:51:06 +0200 Subject: [PATCH 771/930] Base Image: S6 overlay & jemalloc update (#72425) * Update base image with new s6-Overlay & jemalloc * add build version * Update finish --- build.yaml | 10 ++-- rootfs/etc/services.d/home-assistant/finish | 42 +++++++------ rootfs/etc/services.d/home-assistant/run | 0 rootfs/init | 66 ++++++++++++++------- 4 files changed, 75 insertions(+), 43 deletions(-) mode change 100644 => 100755 rootfs/etc/services.d/home-assistant/finish mode change 100644 => 100755 rootfs/etc/services.d/home-assistant/run diff --git a/build.yaml b/build.yaml index 3ced7b8d742..196277184a3 100644 --- a/build.yaml +++ b/build.yaml @@ -1,11 +1,11 @@ image: homeassistant/{arch}-homeassistant shadow_repository: ghcr.io/home-assistant build_from: - aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.02.0 - armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.02.0 - armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.02.0 - amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.02.0 - i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.02.0 + aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2022.05.0 + armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2022.05.0 + armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2022.05.0 + amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2022.05.0 + i386: ghcr.io/home-assistant/i386-homeassistant-base:2022.05.0 codenotary: signer: notary@home-assistant.io base_image: notary@home-assistant.io diff --git a/rootfs/etc/services.d/home-assistant/finish b/rootfs/etc/services.d/home-assistant/finish old mode 100644 new mode 100755 index 3691583ec81..115d8352618 --- a/rootfs/etc/services.d/home-assistant/finish +++ b/rootfs/etc/services.d/home-assistant/finish @@ -1,24 +1,30 @@ -#!/usr/bin/execlineb -S1 +#!/usr/bin/env bashio # ============================================================================== # Take down the S6 supervision tree when Home Assistant fails # ============================================================================== -define HA_RESTART_EXIT_CODE 100 -define SIGNAL_EXIT_CODE 256 -define SIGTERM 15 +declare RESTART_EXIT_CODE 100 +declare SIGNAL_EXIT_CODE 256 +declare SIGTERM 15 +declare APP_EXIT_CODE=${1} +declare SYS_EXIT_CODE=${2+x} +declare NEW_EXIT_CODE= -foreground { s6-echo "[finish] process exit code ${1}" } +bashio::log.info "Home Assistant Core finish process exit code ${1}" -if { s6-test ${1} -ne ${HA_RESTART_EXIT_CODE} } -ifelse { s6-test ${1} -eq ${SIGNAL_EXIT_CODE} } { - # Process terminated by a signal - define signal ${2} - foreground { s6-echo "[finish] process received signal ${signal}" } - backtick -n new_exit_code { s6-expr 128 + ${signal} } - importas -ui new_exit_code new_exit_code - foreground { redirfd -w 1 /var/run/s6/env-stage3/S6_STAGE2_EXITED s6-echo -n -- ${new_exit_code} } - if { s6-test ${signal} -ne ${SIGTERM} } - s6-svscanctl -t /var/run/s6/services -} +if [[ ${APP_EXIT_CODE} -eq ${RESTART_EXIT_CODE} ]]; then + exit 0 +elif [[ ${APP_EXIT_CODE} -eq ${SIGNAL_EXIT_CODE} ]]; then + bashio::log.info "Home Assistant Core finish process received signal ${APP_EXIT_CODE}" -foreground { redirfd -w 1 /var/run/s6/env-stage3/S6_STAGE2_EXITED s6-echo -n -- ${1} } -s6-svscanctl -t /var/run/s6/services + NEW_EXIT_CODE=$((128 + SYS_EXIT_CODE)) + echo ${NEW_EXIT_CODE} > /run/s6-linux-init-container-results/exitcode + + if [[ ${NEW_EXIT_CODE} -eq ${SIGTERM} ]]; then + /run/s6/basedir/bin/halt + fi +else + bashio::log.info "Home Assistant Core service shutdown" + + echo ${APP_EXIT_CODE} > /run/s6-linux-init-container-results/exitcode + /run/s6/basedir/bin/halt +fi diff --git a/rootfs/etc/services.d/home-assistant/run b/rootfs/etc/services.d/home-assistant/run old mode 100644 new mode 100755 diff --git a/rootfs/init b/rootfs/init index 7bea7ed88a9..bfc4802c858 100755 --- a/rootfs/init +++ b/rootfs/init @@ -1,23 +1,49 @@ -#!/bin/execlineb -S0 +#!/usr/bin/env bashio -## -## load default PATH (the same that Docker includes if not provided) if it doesn't exist, -## then go ahead with stage1. -## this was motivated due to this issue: -## - https://github.com/just-containers/s6-overlay/issues/108 -## +# This is the first program launched at container start. +# We don't know where our binaries are and we cannot guarantee +# that the default PATH can access them. +# So this script needs to be entirely self-contained until it has +# at least /command, /usr/bin and /bin in its PATH. -/bin/importas -D /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PATH PATH -export PATH ${PATH} - -## -## Skip further init if the user has a given CMD. -## This is to prevent Home Assistant from starting twice if the user -## decided to override/start via the CMD. -## - -ifelse { s6-test $# -ne 0 } -{ - $@ +addpath () { + x="$1" + IFS=: + set -- $PATH + IFS= + while test "$#" -gt 0 ; do + if test "$1" = "$x" ; then + return + fi + shift + done + PATH="${x}:$PATH" } -/etc/s6/init/init-stage1 $@ \ No newline at end of file + +if test -z "$PATH" ; then + PATH=/bin +fi + +addpath /bin +addpath /usr/bin +addpath /command +export PATH + +# Now we're good: s6-overlay-suexec is accessible via PATH, as are +# all our binaries. + +# Skip further init if the user has a given CMD. +# This is to prevent Home Assistant from starting twice if the user +# decided to override/start via the CMD. +if test $# -ne 0 ; then + exec "$@" +fi + +# Run preinit as root, then run stage0 as the container's user (can be +# root, can be a normal user). + +exec s6-overlay-suexec \ + ' /package/admin/s6-overlay-@VERSION@/libexec/preinit' \ + '' \ + /package/admin/s6-overlay-@VERSION@/libexec/stage0 \ + "$@" From 1113d9bea958e83b17aa0ec106c1c41121f0c499 Mon Sep 17 00:00:00 2001 From: rappenze Date: Tue, 24 May 2022 16:00:15 +0200 Subject: [PATCH 772/930] Support fibaro garage door devices (#72299) * Add proper support for garage door controller in fibaro cover entity * Add proper support for garage door controller in fibaro cover entity --- homeassistant/components/fibaro/cover.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/homeassistant/components/fibaro/cover.py b/homeassistant/components/fibaro/cover.py index e347d0368d2..fc6d0a67d3c 100644 --- a/homeassistant/components/fibaro/cover.py +++ b/homeassistant/components/fibaro/cover.py @@ -6,6 +6,7 @@ from homeassistant.components.cover import ( ATTR_TILT_POSITION, ENTITY_ID_FORMAT, CoverEntity, + CoverEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -41,6 +42,11 @@ class FibaroCover(FibaroDevice, CoverEntity): super().__init__(fibaro_device) self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id) + if self._is_open_close_only(): + self._attr_supported_features = ( + CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE + ) + @staticmethod def bound(position): """Normalize the position.""" @@ -53,6 +59,14 @@ class FibaroCover(FibaroDevice, CoverEntity): return 100 return position + def _is_open_close_only(self) -> bool: + """Return if only open / close is supported.""" + # Normally positionable devices report the position over value, + # so if it is missing we have a device which supports open / close only + if "value" not in self.fibaro_device.properties: + return True + return False + @property def current_cover_position(self): """Return current position of cover. 0 is closed, 100 is open.""" @@ -74,6 +88,9 @@ class FibaroCover(FibaroDevice, CoverEntity): @property def is_closed(self): """Return if the cover is closed.""" + if self._is_open_close_only(): + return self.fibaro_device.properties.state.lower() == "closed" + if self.current_cover_position is None: return None return self.current_cover_position == 0 From dc0d065901096612a608c91acf2d0ea50df19a41 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 24 May 2022 16:06:30 +0200 Subject: [PATCH 773/930] Fix UniFi device tracker on controllers only reporting events (#72240) Add events to UniFi client tracker in case controller does not report proper data --- .../components/unifi/device_tracker.py | 21 ++- tests/components/unifi/test_device_tracker.py | 141 +++++++++++++++++- 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 947e4523531..b2070362d02 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -3,7 +3,7 @@ from datetime import timedelta import logging -from aiounifi.api import SOURCE_DATA +from aiounifi.api import SOURCE_DATA, SOURCE_EVENT from aiounifi.events import ( ACCESS_POINT_UPGRADED, GATEWAY_UPGRADED, @@ -156,6 +156,8 @@ class UniFiClientTracker(UniFiClient, ScannerEntity): self._controller_connection_state_changed = False + self._only_listen_to_data_source = False + last_seen = client.last_seen or 0 self.schedule_update = self._is_connected = ( self.is_wired == client.is_wired @@ -224,6 +226,23 @@ class UniFiClientTracker(UniFiClient, ScannerEntity): ): self._is_connected = True self.schedule_update = True + self._only_listen_to_data_source = True + + elif ( + self.client.last_updated == SOURCE_EVENT + and not self._only_listen_to_data_source + ): + + if (self.is_wired and self.client.event.event in WIRED_CONNECTION) or ( + not self.is_wired and self.client.event.event in WIRELESS_CONNECTION + ): + self._is_connected = True + self.schedule_update = False + self.controller.async_heartbeat(self.unique_id) + super().async_update_callback() + + else: + self.schedule_update = True self._async_log_debug_data("update_callback") diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index 736e85d1b8c..e5d53a6d882 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -3,7 +3,12 @@ from datetime import timedelta from unittest.mock import patch -from aiounifi.controller import MESSAGE_CLIENT, MESSAGE_CLIENT_REMOVED, MESSAGE_DEVICE +from aiounifi.controller import ( + MESSAGE_CLIENT, + MESSAGE_CLIENT_REMOVED, + MESSAGE_DEVICE, + MESSAGE_EVENT, +) from aiounifi.websocket import STATE_DISCONNECTED, STATE_RUNNING from homeassistant import config_entries @@ -169,6 +174,140 @@ async def test_tracked_clients( assert hass.states.get("device_tracker.client_1").state == STATE_HOME +async def test_tracked_wireless_clients_event_source( + hass, aioclient_mock, mock_unifi_websocket, mock_device_registry +): + """Verify tracking of wireless clients based on event source.""" + client = { + "ap_mac": "00:00:00:00:02:01", + "essid": "ssid", + "hostname": "client", + "ip": "10.0.0.1", + "is_wired": False, + "last_seen": 1562600145, + "mac": "00:00:00:00:00:01", + } + config_entry = await setup_unifi_integration( + hass, aioclient_mock, clients_response=[client] + ) + controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id] + assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1 + assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME + + # State change signalling works with events + + # Connected event + + event = { + "user": client["mac"], + "ssid": client["essid"], + "ap": client["ap_mac"], + "radio": "na", + "channel": "44", + "hostname": client["hostname"], + "key": "EVT_WU_Connected", + "subsystem": "wlan", + "site_id": "name", + "time": 1587753456179, + "datetime": "2020-04-24T18:37:36Z", + "msg": f'User{[client["mac"]]} has connected to AP[{client["ap_mac"]}] with SSID "{client["essid"]}" on "channel 44(na)"', + "_id": "5ea331fa30c49e00f90ddc1a", + } + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_EVENT}, + "data": [event], + } + ) + await hass.async_block_till_done() + + assert hass.states.get("device_tracker.client").state == STATE_HOME + + # Disconnected event + + event = { + "user": client["mac"], + "ssid": client["essid"], + "hostname": client["hostname"], + "ap": client["ap_mac"], + "duration": 467, + "bytes": 459039, + "key": "EVT_WU_Disconnected", + "subsystem": "wlan", + "site_id": "name", + "time": 1587752927000, + "datetime": "2020-04-24T18:28:47Z", + "msg": f'User{[client["mac"]]} disconnected from "{client["essid"]}" (7m 47s connected, 448.28K bytes, last AP[{client["ap_mac"]}])', + "_id": "5ea32ff730c49e00f90dca1a", + } + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_EVENT}, + "data": [event], + } + ) + await hass.async_block_till_done() + assert hass.states.get("device_tracker.client").state == STATE_HOME + + # Change time to mark client as away + new_time = dt_util.utcnow() + controller.option_detection_time + with patch("homeassistant.util.dt.utcnow", return_value=new_time): + async_fire_time_changed(hass, new_time) + await hass.async_block_till_done() + + assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME + + # To limit false positives in client tracker + # data sources are prioritized when available + # once real data is received events will be ignored. + + # New data + + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_CLIENT}, + "data": [client], + } + ) + await hass.async_block_till_done() + + assert hass.states.get("device_tracker.client").state == STATE_HOME + + # Disconnection event will be ignored + + event = { + "user": client["mac"], + "ssid": client["essid"], + "hostname": client["hostname"], + "ap": client["ap_mac"], + "duration": 467, + "bytes": 459039, + "key": "EVT_WU_Disconnected", + "subsystem": "wlan", + "site_id": "name", + "time": 1587752927000, + "datetime": "2020-04-24T18:28:47Z", + "msg": f'User{[client["mac"]]} disconnected from "{client["essid"]}" (7m 47s connected, 448.28K bytes, last AP[{client["ap_mac"]}])', + "_id": "5ea32ff730c49e00f90dca1a", + } + mock_unifi_websocket( + data={ + "meta": {"message": MESSAGE_EVENT}, + "data": [event], + } + ) + await hass.async_block_till_done() + assert hass.states.get("device_tracker.client").state == STATE_HOME + + # Change time to mark client as away + new_time = dt_util.utcnow() + controller.option_detection_time + with patch("homeassistant.util.dt.utcnow", return_value=new_time): + async_fire_time_changed(hass, new_time) + await hass.async_block_till_done() + + assert hass.states.get("device_tracker.client").state == STATE_HOME + + async def test_tracked_devices( hass, aioclient_mock, mock_unifi_websocket, mock_device_registry ): From d40cd20692db6ba84c02c1e3733237d6962ab3e7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 16:07:18 +0200 Subject: [PATCH 774/930] Enforce type hints for backup and cast platforms (#72223) * Enforce backup type hints * Enforce cast type hints --- pylint/plugins/hass_enforce_type_hints.py | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index 9d35d07fab2..e230c27b4ee 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -46,6 +46,10 @@ _MODULE_FILTERS: dict[str, re.Pattern] = { "application_credentials": re.compile( r"^homeassistant\.components\.\w+\.(application_credentials)$" ), + # backup matches only in the package root (backup.py) + "backup": re.compile(r"^homeassistant\.components\.\w+\.(backup)$"), + # cast matches only in the package root (cast.py) + "cast": re.compile(r"^homeassistant\.components\.\w+\.(cast)$"), # config_flow matches only in the package root (config_flow.py) "config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$"), # device_action matches only in the package root (device_action.py) @@ -167,6 +171,54 @@ _METHOD_MATCH: list[TypeHintMatch] = [ }, return_type="AuthorizationServer", ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["backup"], + function_name="async_pre_backup", + arg_types={ + 0: "HomeAssistant", + }, + return_type=None, + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["backup"], + function_name="async_post_backup", + arg_types={ + 0: "HomeAssistant", + }, + return_type=None, + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["cast"], + function_name="async_get_media_browser_root_object", + arg_types={ + 0: "HomeAssistant", + 1: "str", + }, + return_type="list[BrowseMedia]", + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["cast"], + function_name="async_browse_media", + arg_types={ + 0: "HomeAssistant", + 1: "str", + 2: "str", + 3: "str", + }, + return_type=["BrowseMedia", "BrowseMedia | None"], + ), + TypeHintMatch( + module_filter=_MODULE_FILTERS["cast"], + function_name="async_play_media", + arg_types={ + 0: "HomeAssistant", + 1: "str", + 2: "Chromecast", + 3: "str", + 4: "str", + }, + return_type="bool", + ), TypeHintMatch( module_filter=_MODULE_FILTERS["config_flow"], function_name="_async_has_devices", From a5402d725fe1e92f7043fbe48b577c11e5e0d939 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 24 May 2022 09:20:13 -0500 Subject: [PATCH 775/930] Add light platform to Big Ass Fans (#72382) Co-authored-by: Erik Montnemery --- .coveragerc | 1 + homeassistant/components/baf/__init__.py | 1 + homeassistant/components/baf/light.py | 102 +++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 homeassistant/components/baf/light.py diff --git a/.coveragerc b/.coveragerc index dd03c3782c0..ca6c57fb341 100644 --- a/.coveragerc +++ b/.coveragerc @@ -97,6 +97,7 @@ omit = homeassistant/components/baf/climate.py homeassistant/components/baf/entity.py homeassistant/components/baf/fan.py + homeassistant/components/baf/light.py homeassistant/components/baf/sensor.py homeassistant/components/baf/switch.py homeassistant/components/baidu/tts.py diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py index 30de2582af1..7127228383a 100644 --- a/homeassistant/components/baf/__init__.py +++ b/homeassistant/components/baf/__init__.py @@ -17,6 +17,7 @@ from .models import BAFData PLATFORMS: list[Platform] = [ Platform.CLIMATE, Platform.FAN, + Platform.LIGHT, Platform.SENSOR, Platform.SWITCH, ] diff --git a/homeassistant/components/baf/light.py b/homeassistant/components/baf/light.py new file mode 100644 index 00000000000..b177d383cd5 --- /dev/null +++ b/homeassistant/components/baf/light.py @@ -0,0 +1,102 @@ +"""Support for Big Ass Fans lights.""" +from __future__ import annotations + +from typing import Any + +from aiobafi6 import Device, OffOnAuto + +from homeassistant import config_entries +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, + ATTR_COLOR_TEMP, + ColorMode, + LightEntity, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.util.color import ( + color_temperature_kelvin_to_mired, + color_temperature_mired_to_kelvin, +) + +from .const import DOMAIN +from .entity import BAFEntity +from .models import BAFData + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up BAF lights.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + if data.device.has_light: + klass = BAFFanLight if data.device.has_fan else BAFStandaloneLight + async_add_entities([klass(data.device)]) + + +class BAFLight(BAFEntity, LightEntity): + """Representation of a Big Ass Fans light.""" + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + self._attr_is_on = self._device.light_mode == OffOnAuto.ON + if self._device.light_brightness_level is not None: + self._attr_brightness = round( + self._device.light_brightness_level / 16 * 255 + ) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on the light.""" + if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None: + self._device.light_brightness_level = max(int(brightness / 255 * 16), 1) + else: + self._device.light_mode = OffOnAuto.ON + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn off the light.""" + self._device.light_mode = OffOnAuto.OFF + + +class BAFFanLight(BAFLight): + """Representation of a Big Ass Fans light on a fan.""" + + def __init__(self, device: Device) -> None: + """Init a fan light.""" + super().__init__(device, device.name) + self._attr_supported_color_modes = {ColorMode.BRIGHTNESS} + self._attr_color_mode = ColorMode.BRIGHTNESS + + +class BAFStandaloneLight(BAFLight): + """Representation of a Big Ass Fans light.""" + + def __init__(self, device: Device) -> None: + """Init a standalone light.""" + super().__init__(device, f"{device.name} Light") + self._attr_supported_color_modes = {ColorMode.COLOR_TEMP} + self._attr_color_mode = ColorMode.COLOR_TEMP + self._attr_min_mireds = color_temperature_kelvin_to_mired( + device.light_warmest_color_temperature + ) + self._attr_max_mireds = color_temperature_kelvin_to_mired( + device.light_coolest_color_temperature + ) + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + super()._async_update_attrs() + self._attr_color_temp = color_temperature_kelvin_to_mired( + self._device.light_color_temperature + ) + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn on the light.""" + if (color_temp := kwargs.get(ATTR_COLOR_TEMP)) is not None: + self._device.light_color_temperature = color_temperature_mired_to_kelvin( + color_temp + ) + await super().async_turn_on(**kwargs) From 652892e5357fd77b08ca60543d7d7a9e75f4a546 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 24 May 2022 16:30:41 +0200 Subject: [PATCH 776/930] Do not swallow WLED errors (#72407) --- homeassistant/components/wled/helpers.py | 6 +- tests/components/wled/test_button.py | 35 +++-- tests/components/wled/test_light.py | 35 +++-- tests/components/wled/test_number.py | 93 ++++++------ tests/components/wled/test_select.py | 185 +++++++++++------------ tests/components/wled/test_switch.py | 35 +++-- 6 files changed, 188 insertions(+), 201 deletions(-) diff --git a/homeassistant/components/wled/helpers.py b/homeassistant/components/wled/helpers.py index d5ca895390b..66cd8b13b42 100644 --- a/homeassistant/components/wled/helpers.py +++ b/homeassistant/components/wled/helpers.py @@ -2,7 +2,7 @@ from wled import WLEDConnectionError, WLEDError -from .const import LOGGER +from homeassistant.exceptions import HomeAssistantError def wled_exception_handler(func): @@ -18,11 +18,11 @@ def wled_exception_handler(func): self.coordinator.update_listeners() except WLEDConnectionError as error: - LOGGER.error("Error communicating with API: %s", error) self.coordinator.last_update_success = False self.coordinator.update_listeners() + raise HomeAssistantError("Error communicating with WLED API") from error except WLEDError as error: - LOGGER.error("Invalid response from API: %s", error) + raise HomeAssistantError("Invalid response from WLED API") from error return handler diff --git a/tests/components/wled/test_button.py b/tests/components/wled/test_button.py index 3cfa4f762ad..210d67a24a0 100644 --- a/tests/components/wled/test_button.py +++ b/tests/components/wled/test_button.py @@ -17,6 +17,7 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory @@ -55,43 +56,41 @@ async def test_button_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED buttons.""" mock_wled.reset.side_effect = WLEDError - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.wled_rgb_light_restart"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.wled_rgb_light_restart"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("button.wled_rgb_light_restart") assert state assert state.state == "2021-11-04T16:37:00+00:00" - assert "Invalid response from API" in caplog.text async def test_button_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED buttons.""" mock_wled.reset.side_effect = WLEDConnectionError - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - {ATTR_ENTITY_ID: "button.wled_rgb_light_restart"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.wled_rgb_light_restart"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("button.wled_rgb_light_restart") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text diff --git a/tests/components/wled/test_light.py b/tests/components/wled/test_light.py index fb2efce404a..b52cf91a11e 100644 --- a/tests/components/wled/test_light.py +++ b/tests/components/wled/test_light.py @@ -25,6 +25,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er import homeassistant.util.dt as dt_util @@ -305,23 +306,22 @@ async def test_light_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED lights.""" mock_wled.segment.side_effect = WLEDError - await hass.services.async_call( - LIGHT_DOMAIN, - SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "light.wled_rgb_light"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.wled_rgb_light"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("light.wled_rgb_light") assert state assert state.state == STATE_ON - assert "Invalid response from API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(on=False, segment_id=0, transition=None) @@ -330,23 +330,22 @@ async def test_light_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED switches.""" mock_wled.segment.side_effect = WLEDConnectionError - await hass.services.async_call( - LIGHT_DOMAIN, - SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: "light.wled_rgb_light"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "light.wled_rgb_light"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("light.wled_rgb_light") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(on=False, segment_id=0, transition=None) diff --git a/tests/components/wled/test_number.py b/tests/components/wled/test_number.py index e4b8958b077..5c9d562e0c0 100644 --- a/tests/components/wled/test_number.py +++ b/tests/components/wled/test_number.py @@ -14,6 +14,7 @@ from homeassistant.components.number.const import ( from homeassistant.components.wled.const import SCAN_INTERVAL from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er import homeassistant.util.dt as dt_util @@ -109,26 +110,25 @@ async def test_speed_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED numbers.""" mock_wled.segment.side_effect = WLEDError - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed", - ATTR_VALUE: 42, - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed", + ATTR_VALUE: 42, + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("number.wled_rgb_light_segment_1_speed") assert state assert state.state == "16" - assert "Invalid response from API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, speed=42) @@ -137,26 +137,25 @@ async def test_speed_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED numbers.""" mock_wled.segment.side_effect = WLEDConnectionError - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed", - ATTR_VALUE: 42, - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_speed", + ATTR_VALUE: 42, + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("number.wled_rgb_light_segment_1_speed") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, speed=42) @@ -250,26 +249,25 @@ async def test_intensity_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED numbers.""" mock_wled.segment.side_effect = WLEDError - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity", - ATTR_VALUE: 21, - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity", + ATTR_VALUE: 21, + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("number.wled_rgb_light_segment_1_intensity") assert state assert state.state == "64" - assert "Invalid response from API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, intensity=21) @@ -278,25 +276,24 @@ async def test_intensity_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED numbers.""" mock_wled.segment.side_effect = WLEDConnectionError - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - { - ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity", - ATTR_VALUE: 128, - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + { + ATTR_ENTITY_ID: "number.wled_rgb_light_segment_1_intensity", + ATTR_VALUE: 128, + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("number.wled_rgb_light_segment_1_intensity") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, intensity=128) diff --git a/tests/components/wled/test_select.py b/tests/components/wled/test_select.py index d798a723246..ebe3c7ca018 100644 --- a/tests/components/wled/test_select.py +++ b/tests/components/wled/test_select.py @@ -16,6 +16,7 @@ from homeassistant.const import ( STATE_UNKNOWN, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory import homeassistant.util.dt as dt_util @@ -161,26 +162,25 @@ async def test_color_palette_select_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.segment.side_effect = WLEDError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgb_light_segment_1_color_palette", - ATTR_OPTION: "Icefire", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgb_light_segment_1_color_palette", + ATTR_OPTION: "Icefire", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgb_light_segment_1_color_palette") assert state assert state.state == "Random Cycle" - assert "Invalid response from API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, palette="Icefire") @@ -189,26 +189,25 @@ async def test_color_palette_select_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.segment.side_effect = WLEDConnectionError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgb_light_segment_1_color_palette", - ATTR_OPTION: "Icefire", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgb_light_segment_1_color_palette", + ATTR_OPTION: "Icefire", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgb_light_segment_1_color_palette") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.segment.call_count == 1 mock_wled.segment.assert_called_with(segment_id=1, palette="Icefire") @@ -280,26 +279,25 @@ async def test_preset_select_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.preset.side_effect = WLEDError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgbw_light_preset", - ATTR_OPTION: "Preset 2", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgbw_light_preset", + ATTR_OPTION: "Preset 2", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgbw_light_preset") assert state assert state.state == "Preset 1" - assert "Invalid response from API" in caplog.text assert mock_wled.preset.call_count == 1 mock_wled.preset.assert_called_with(preset="Preset 2") @@ -309,26 +307,25 @@ async def test_preset_select_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.preset.side_effect = WLEDConnectionError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgbw_light_preset", - ATTR_OPTION: "Preset 2", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgbw_light_preset", + ATTR_OPTION: "Preset 2", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgbw_light_preset") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.preset.call_count == 1 mock_wled.preset.assert_called_with(preset="Preset 2") @@ -400,26 +397,25 @@ async def test_playlist_select_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.playlist.side_effect = WLEDError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgbw_light_playlist", - ATTR_OPTION: "Playlist 2", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgbw_light_playlist", + ATTR_OPTION: "Playlist 2", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgbw_light_playlist") assert state assert state.state == "Playlist 1" - assert "Invalid response from API" in caplog.text assert mock_wled.playlist.call_count == 1 mock_wled.playlist.assert_called_with(playlist="Playlist 2") @@ -429,26 +425,25 @@ async def test_playlist_select_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.playlist.side_effect = WLEDConnectionError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgbw_light_playlist", - ATTR_OPTION: "Playlist 2", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgbw_light_playlist", + ATTR_OPTION: "Playlist 2", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgbw_light_playlist") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.playlist.call_count == 1 mock_wled.playlist.assert_called_with(playlist="Playlist 2") @@ -489,26 +484,25 @@ async def test_live_select_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.live.side_effect = WLEDError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgb_light_live_override", - ATTR_OPTION: "1", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgb_light_live_override", + ATTR_OPTION: "1", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgb_light_live_override") assert state assert state.state == "0" - assert "Invalid response from API" in caplog.text assert mock_wled.live.call_count == 1 mock_wled.live.assert_called_with(live=1) @@ -517,25 +511,24 @@ async def test_live_select_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED selects.""" mock_wled.live.side_effect = WLEDConnectionError - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - { - ATTR_ENTITY_ID: "select.wled_rgb_light_live_override", - ATTR_OPTION: "2", - }, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.wled_rgb_light_live_override", + ATTR_OPTION: "2", + }, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("select.wled_rgb_light_live_override") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text assert mock_wled.live.call_count == 1 mock_wled.live.assert_called_with(live=2) diff --git a/tests/components/wled/test_switch.py b/tests/components/wled/test_switch.py index 2bf494284f5..1902a324764 100644 --- a/tests/components/wled/test_switch.py +++ b/tests/components/wled/test_switch.py @@ -23,6 +23,7 @@ from homeassistant.const import ( STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import EntityCategory import homeassistant.util.dt as dt_util @@ -175,46 +176,44 @@ async def test_switch_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED switches.""" mock_wled.nightlight.side_effect = WLEDError - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Invalid response from WLED API"): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("switch.wled_rgb_light_nightlight") assert state assert state.state == STATE_OFF - assert "Invalid response from API" in caplog.text async def test_switch_connection_error( hass: HomeAssistant, init_integration: MockConfigEntry, mock_wled: MagicMock, - caplog: pytest.LogCaptureFixture, ) -> None: """Test error handling of the WLED switches.""" mock_wled.nightlight.side_effect = WLEDConnectionError - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, - blocking=True, - ) - await hass.async_block_till_done() + with pytest.raises(HomeAssistantError, match="Error communicating with WLED API"): + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.wled_rgb_light_nightlight"}, + blocking=True, + ) + await hass.async_block_till_done() state = hass.states.get("switch.wled_rgb_light_nightlight") assert state assert state.state == STATE_UNAVAILABLE - assert "Error communicating with API" in caplog.text @pytest.mark.parametrize("mock_wled", ["wled/rgb_single_segment.json"], indirect=True) From b2e18682d2a7d64be0b4dd7885b5d8d1d00b0661 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 24 May 2022 16:31:03 +0200 Subject: [PATCH 777/930] Update coverage to 6.4 (#72347) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index f0cdac24e5f..29e7364059f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -8,7 +8,7 @@ -c homeassistant/package_constraints.txt -r requirements_test_pre_commit.txt codecov==2.1.12 -coverage==6.3.3 +coverage==6.4 freezegun==1.2.1 mock-open==1.4.0 mypy==0.950 From 1a43f107c492c6d8e531904188a532af1b251cd0 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Tue, 24 May 2022 07:40:14 -0700 Subject: [PATCH 778/930] All WeMo devices use the Sensor platform (#72396) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/wemo/__init__.py | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/wemo/__init__.py b/homeassistant/components/wemo/__init__.py index 3b08a39f2ce..2727c913fee 100644 --- a/homeassistant/components/wemo/__init__.py +++ b/homeassistant/components/wemo/__init__.py @@ -32,7 +32,7 @@ WEMO_MODEL_DISPATCH = { "CoffeeMaker": [Platform.SWITCH], "Dimmer": [Platform.LIGHT], "Humidifier": [Platform.FAN], - "Insight": [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH], + "Insight": [Platform.BINARY_SENSOR, Platform.SWITCH], "LightSwitch": [Platform.SWITCH], "Maker": [Platform.BINARY_SENSOR, Platform.SWITCH], "Motion": [Platform.BINARY_SENSOR], @@ -141,7 +141,7 @@ class WemoDispatcher: """Initialize the WemoDispatcher.""" self._config_entry = config_entry self._added_serial_numbers: set[str] = set() - self._loaded_components: set[str] = set() + self._loaded_platforms: set[Platform] = set() async def async_add_unique_device( self, hass: HomeAssistant, wemo: pywemo.WeMoDevice @@ -151,28 +151,30 @@ class WemoDispatcher: return coordinator = await async_register_device(hass, self._config_entry, wemo) - for component in WEMO_MODEL_DISPATCH.get(wemo.model_name, [Platform.SWITCH]): + platforms = set(WEMO_MODEL_DISPATCH.get(wemo.model_name, [Platform.SWITCH])) + platforms.add(Platform.SENSOR) + for platform in platforms: # Three cases: - # - First time we see component, we need to load it and initialize the backlog - # - Component is being loaded, add to backlog - # - Component is loaded, backlog is gone, dispatch discovery + # - First time we see platform, we need to load it and initialize the backlog + # - Platform is being loaded, add to backlog + # - Platform is loaded, backlog is gone, dispatch discovery - if component not in self._loaded_components: - hass.data[DOMAIN]["pending"][component] = [coordinator] - self._loaded_components.add(component) + if platform not in self._loaded_platforms: + hass.data[DOMAIN]["pending"][platform] = [coordinator] + self._loaded_platforms.add(platform) hass.async_create_task( hass.config_entries.async_forward_entry_setup( - self._config_entry, component + self._config_entry, platform ) ) - elif component in hass.data[DOMAIN]["pending"]: - hass.data[DOMAIN]["pending"][component].append(coordinator) + elif platform in hass.data[DOMAIN]["pending"]: + hass.data[DOMAIN]["pending"][platform].append(coordinator) else: async_dispatcher_send( hass, - f"{DOMAIN}.{component}", + f"{DOMAIN}.{platform}", coordinator, ) From 8c9d7b392b9b97f955d4ddaf157525d261d0457d Mon Sep 17 00:00:00 2001 From: Ruben Date: Tue, 24 May 2022 16:48:22 +0200 Subject: [PATCH 779/930] Change default name of motion blind TDBU entities so they can be auto renamed (#72284) Change the default name of motion blind TDBU entities so they can be automatically renamed --- homeassistant/components/motion_blinds/cover.py | 4 ++-- homeassistant/components/motion_blinds/sensor.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index e09bb531208..a9f6df82ae0 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -182,7 +182,7 @@ class MotionPositionDevice(CoordinatorEntity, CoverEntity): else: via_device = (DOMAIN, blind._gateway.mac) connections = {} - name = f"{blind.blind_type}-{blind.mac[12:]}" + name = f"{blind.blind_type} {blind.mac[12:]}" sw_version = None self._attr_device_class = device_class @@ -364,7 +364,7 @@ class MotionTDBUDevice(MotionPositionDevice): super().__init__(coordinator, blind, device_class, sw_version) self._motor = motor self._motor_key = motor[0] - self._attr_name = f"{blind.blind_type}-{motor}-{blind.mac[12:]}" + self._attr_name = f"{blind.blind_type} {blind.mac[12:]} {motor}" self._attr_unique_id = f"{blind.mac}-{motor}" if self._motor not in ["Bottom", "Top", "Combined"]: diff --git a/homeassistant/components/motion_blinds/sensor.py b/homeassistant/components/motion_blinds/sensor.py index 03e3a6e7618..ebaed95bcbf 100644 --- a/homeassistant/components/motion_blinds/sensor.py +++ b/homeassistant/components/motion_blinds/sensor.py @@ -55,9 +55,9 @@ class MotionBatterySensor(CoordinatorEntity, SensorEntity): super().__init__(coordinator) if blind.device_type in DEVICE_TYPES_WIFI: - name = f"{blind.blind_type}-battery" + name = f"{blind.blind_type} battery" else: - name = f"{blind.blind_type}-battery-{blind.mac[12:]}" + name = f"{blind.blind_type} {blind.mac[12:]} battery" self._blind = blind self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, blind.mac)}) @@ -104,9 +104,9 @@ class MotionTDBUBatterySensor(MotionBatterySensor): super().__init__(coordinator, blind) if blind.device_type in DEVICE_TYPES_WIFI: - name = f"{blind.blind_type}-{motor}-battery" + name = f"{blind.blind_type} {motor} battery" else: - name = f"{blind.blind_type}-{motor}-battery-{blind.mac[12:]}" + name = f"{blind.blind_type} {blind.mac[12:]} {motor} battery" self._motor = motor self._attr_unique_id = f"{blind.mac}-{motor}-battery" @@ -147,7 +147,7 @@ class MotionSignalStrengthSensor(CoordinatorEntity, SensorEntity): elif device.device_type in DEVICE_TYPES_WIFI: name = f"{device.blind_type} signal strength" else: - name = f"{device.blind_type} signal strength - {device.mac[12:]}" + name = f"{device.blind_type} {device.mac[12:]} signal strength" self._device = device self._device_type = device_type From 1482a8f73a62c2a822e696764cf5efaadabd9e65 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 17:01:08 +0200 Subject: [PATCH 780/930] Adjust config-flow type hints in axis (#72387) --- homeassistant/components/axis/config_flow.py | 48 ++++++++++++-------- homeassistant/components/axis/device.py | 4 +- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/axis/config_flow.py b/homeassistant/components/axis/config_flow.py index 23b1ba4c753..2c5239a8fb3 100644 --- a/homeassistant/components/axis/config_flow.py +++ b/homeassistant/components/axis/config_flow.py @@ -1,13 +1,16 @@ """Config flow to configure Axis devices.""" +from __future__ import annotations +from collections.abc import Mapping from ipaddress import ip_address +from typing import Any from urllib.parse import urlsplit import voluptuous as vol from homeassistant import config_entries from homeassistant.components import dhcp, ssdp, zeroconf -from homeassistant.config_entries import SOURCE_IGNORE +from homeassistant.config_entries import SOURCE_IGNORE, ConfigEntry from homeassistant.const import ( CONF_HOST, CONF_MAC, @@ -29,7 +32,7 @@ from .const import ( DEFAULT_VIDEO_SOURCE, DOMAIN as AXIS_DOMAIN, ) -from .device import get_device +from .device import AxisNetworkDevice, get_device from .errors import AuthenticationRequired, CannotConnect AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f"} @@ -43,18 +46,18 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow(config_entry: ConfigEntry) -> AxisOptionsFlowHandler: """Get the options flow for this handler.""" return AxisOptionsFlowHandler(config_entry) - def __init__(self): + def __init__(self) -> None: """Initialize the Axis config flow.""" - self.device_config = {} - self.discovery_schema = {} - self.import_schema = {} - self.serial = None + self.device_config: dict[str, Any] = {} + self.discovery_schema: dict[vol.Required, type[str | int]] | None = None - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a Axis config flow start. Manage device specific parameters. @@ -71,8 +74,8 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): password=user_input[CONF_PASSWORD], ) - self.serial = device.vapix.serial_number - await self.async_set_unique_id(format_mac(self.serial)) + serial = device.vapix.serial_number + await self.async_set_unique_id(format_mac(serial)) self._abort_if_unique_id_configured( updates={ @@ -91,7 +94,7 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): CONF_MODEL: device.vapix.product_number, } - return await self._create_entry() + return await self._create_entry(serial) except AuthenticationRequired: errors["base"] = "invalid_auth" @@ -113,7 +116,7 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): errors=errors, ) - async def _create_entry(self): + async def _create_entry(self, serial: str) -> FlowResult: """Create entry for device. Generate a name to be used as a prefix for device entities. @@ -133,10 +136,10 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): self.device_config[CONF_NAME] = name - title = f"{model} - {self.serial}" + title = f"{model} - {serial}" return self.async_create_entry(title=title, data=self.device_config) - async def async_step_reauth(self, device_config: dict): + async def async_step_reauth(self, device_config: Mapping[str, Any]) -> FlowResult: """Trigger a reauthentication flow.""" self.context["title_placeholders"] = { CONF_NAME: device_config[CONF_NAME], @@ -188,7 +191,7 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): } ) - async def _process_discovered_device(self, device: dict): + async def _process_discovered_device(self, device: dict[str, Any]) -> FlowResult: """Prepare configuration for a discovered Axis device.""" if device[CONF_MAC][:8] not in AXIS_OUI: return self.async_abort(reason="not_axis_device") @@ -228,18 +231,22 @@ class AxisFlowHandler(config_entries.ConfigFlow, domain=AXIS_DOMAIN): class AxisOptionsFlowHandler(config_entries.OptionsFlow): """Handle Axis device options.""" - def __init__(self, config_entry): + def __init__(self, config_entry: ConfigEntry) -> None: """Initialize Axis device options flow.""" self.config_entry = config_entry self.options = dict(config_entry.options) - self.device = None + self.device: AxisNetworkDevice | None = None - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage the Axis device options.""" self.device = self.hass.data[AXIS_DOMAIN][self.config_entry.unique_id] return await self.async_step_configure_stream() - async def async_step_configure_stream(self, user_input=None): + async def async_step_configure_stream( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage the Axis device stream options.""" if user_input is not None: self.options.update(user_input) @@ -247,6 +254,7 @@ class AxisOptionsFlowHandler(config_entries.OptionsFlow): schema = {} + assert self.device vapix = self.device.api.vapix # Stream profiles diff --git a/homeassistant/components/axis/device.py b/homeassistant/components/axis/device.py index 2338a90d562..d0d5e230d2f 100644 --- a/homeassistant/components/axis/device.py +++ b/homeassistant/components/axis/device.py @@ -274,7 +274,9 @@ class AxisNetworkDevice: ) -async def get_device(hass, host, port, username, password): +async def get_device( + hass: HomeAssistant, host: str, port: int, username: str, password: str +) -> axis.AxisDevice: """Create a Axis device.""" session = get_async_client(hass, verify_ssl=False) From 1d68934ae25821851ec7ed70e106a398337fb7a6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 18:12:25 +0200 Subject: [PATCH 781/930] Deprecate vera YAML configuration (#72418) --- homeassistant/components/vera/__init__.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/vera/__init__.py b/homeassistant/components/vera/__init__.py index d923861112b..2dda8fdb7c4 100644 --- a/homeassistant/components/vera/__init__.py +++ b/homeassistant/components/vera/__init__.py @@ -46,15 +46,18 @@ _LOGGER = logging.getLogger(__name__) VERA_ID_LIST_SCHEMA = vol.Schema([int]) CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CONTROLLER): cv.url, - vol.Optional(CONF_EXCLUDE, default=[]): VERA_ID_LIST_SCHEMA, - vol.Optional(CONF_LIGHTS, default=[]): VERA_ID_LIST_SCHEMA, - } - ) - }, + vol.All( + cv.deprecated(DOMAIN), + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CONTROLLER): cv.url, + vol.Optional(CONF_EXCLUDE, default=[]): VERA_ID_LIST_SCHEMA, + vol.Optional(CONF_LIGHTS, default=[]): VERA_ID_LIST_SCHEMA, + } + ) + }, + ), extra=vol.ALLOW_EXTRA, ) From ffbec553d194f13bec8310e0ff47036d49688c00 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk <11290930+bouwew@users.noreply.github.com> Date: Tue, 24 May 2022 19:50:02 +0200 Subject: [PATCH 782/930] Bump plugwise to v0.18.5 (#72441) --- homeassistant/components/plugwise/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plugwise/manifest.json b/homeassistant/components/plugwise/manifest.json index 2b7f21cd106..a311151f645 100644 --- a/homeassistant/components/plugwise/manifest.json +++ b/homeassistant/components/plugwise/manifest.json @@ -2,7 +2,7 @@ "domain": "plugwise", "name": "Plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise", - "requirements": ["plugwise==0.18.4"], + "requirements": ["plugwise==0.18.5"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "zeroconf": ["_plugwise._tcp.local."], "config_flow": true, diff --git a/requirements_all.txt b/requirements_all.txt index 9bfc8b3671e..e9438a330a5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1245,7 +1245,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.18.4 +plugwise==0.18.5 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1e6700d3add..e072289ac45 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -847,7 +847,7 @@ plexauth==0.0.6 plexwebsocket==0.0.13 # homeassistant.components.plugwise -plugwise==0.18.4 +plugwise==0.18.5 # homeassistant.components.plum_lightpad plumlightpad==0.0.11 From 6245d289078a499554a08671216b0b33d7613fe1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 20:28:09 +0200 Subject: [PATCH 783/930] Remove YAML configuration from vicare (#72408) --- homeassistant/components/vicare/__init__.py | 51 +-------- .../components/vicare/config_flow.py | 19 +--- tests/components/vicare/__init__.py | 6 - tests/components/vicare/test_config_flow.py | 106 +----------------- 4 files changed, 6 insertions(+), 176 deletions(-) diff --git a/homeassistant/components/vicare/__init__.py b/homeassistant/components/vicare/__init__.py index 4af36835ed2..221f3a4374a 100644 --- a/homeassistant/components/vicare/__init__.py +++ b/homeassistant/components/vicare/__init__.py @@ -7,19 +7,14 @@ import logging from PyViCare.PyViCare import PyViCare from PyViCare.PyViCareDevice import Device -import voluptuous as vol -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.storage import STORAGE_DIR -from homeassistant.helpers.typing import ConfigType from .const import ( - CONF_CIRCUIT, CONF_HEATING_TYPE, - DEFAULT_HEATING_TYPE, DEFAULT_SCAN_INTERVAL, DOMAIN, HEATING_TYPE_TO_CREATOR_METHOD, @@ -39,50 +34,6 @@ class ViCareRequiredKeysMixin: value_getter: Callable[[Device], bool] -CONFIG_SCHEMA = vol.Schema( - vol.All( - cv.deprecated(DOMAIN), - { - DOMAIN: vol.All( - cv.deprecated(CONF_CIRCUIT), - cv.deprecated(DOMAIN), - vol.Schema( - { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Optional( - CONF_CIRCUIT - ): int, # Ignored: All circuits are now supported. Will be removed when switching to Setup via UI. - vol.Optional( - CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE.value - ): vol.In([e.value for e in HeatingType]), - } - ), - ) - }, - ), - extra=vol.ALLOW_EXTRA, -) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Set up the ViCare component from yaml.""" - if DOMAIN not in config: - # Setup via UI. No need to continue yaml-based setup - return True - - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=config[DOMAIN], - ) - ) - - return True - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up from config entry.""" _LOGGER.debug("Setting up ViCare component") diff --git a/homeassistant/components/vicare/config_flow.py b/homeassistant/components/vicare/config_flow.py index 0f0e2a85b3c..a0feb8f38ea 100644 --- a/homeassistant/components/vicare/config_flow.py +++ b/homeassistant/components/vicare/config_flow.py @@ -16,7 +16,6 @@ from homeassistant.helpers.device_registry import format_mac from . import vicare_login from .const import ( - CONF_CIRCUIT, CONF_HEATING_TYPE, DEFAULT_HEATING_TYPE, DOMAIN, @@ -32,7 +31,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def async_step_user(self, user_input: dict[str, Any] | None = None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Invoke when a user initiates a flow via the user interface.""" if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -75,17 +76,3 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="single_instance_allowed") return await self.async_step_user() - - async def async_step_import(self, import_info): - """Handle a flow initiated by a YAML config import.""" - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - # Remove now unsupported config parameters - import_info.pop(CONF_CIRCUIT, None) - - # CONF_HEATING_TYPE is now required but was optional in yaml config. Add if missing. - if import_info.get(CONF_HEATING_TYPE) is None: - import_info[CONF_HEATING_TYPE] = DEFAULT_HEATING_TYPE.value - - return await self.async_step_user(import_info) diff --git a/tests/components/vicare/__init__.py b/tests/components/vicare/__init__.py index ae9df782886..66cbfdc1d26 100644 --- a/tests/components/vicare/__init__.py +++ b/tests/components/vicare/__init__.py @@ -13,10 +13,4 @@ ENTRY_CONFIG: Final[dict[str, str]] = { CONF_HEATING_TYPE: "auto", } -ENTRY_CONFIG_NO_HEATING_TYPE: Final[dict[str, str]] = { - CONF_USERNAME: "foo@bar.com", - CONF_PASSWORD: "1234", - CONF_CLIENT_ID: "5678", -} - MOCK_MAC = "B874241B7B9" diff --git a/tests/components/vicare/test_config_flow.py b/tests/components/vicare/test_config_flow.py index 0bedb0d73b8..3096f8a492c 100644 --- a/tests/components/vicare/test_config_flow.py +++ b/tests/components/vicare/test_config_flow.py @@ -5,10 +5,10 @@ from PyViCare.PyViCareUtils import PyViCareInvalidCredentialsError from homeassistant import config_entries, data_entry_flow from homeassistant.components import dhcp -from homeassistant.components.vicare.const import CONF_CIRCUIT, DOMAIN, VICARE_NAME +from homeassistant.components.vicare.const import DOMAIN from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME -from . import ENTRY_CONFIG, ENTRY_CONFIG_NO_HEATING_TYPE, MOCK_MAC +from . import ENTRY_CONFIG, MOCK_MAC from tests.common import MockConfigEntry @@ -25,8 +25,6 @@ async def test_form(hass): "homeassistant.components.vicare.config_flow.vicare_login", return_value=None, ), patch( - "homeassistant.components.vicare.async_setup", return_value=True - ) as mock_setup, patch( "homeassistant.components.vicare.async_setup_entry", return_value=True, ) as mock_setup_entry: @@ -43,89 +41,9 @@ async def test_form(hass): assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result2["title"] == "ViCare" assert result2["data"] == ENTRY_CONFIG - assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 -async def test_import(hass): - """Test that the import works.""" - - with patch( - "homeassistant.components.vicare.config_flow.vicare_login", - return_value=True, - ), patch( - "homeassistant.components.vicare.async_setup", return_value=True - ) as mock_setup, patch( - "homeassistant.components.vicare.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=ENTRY_CONFIG, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == VICARE_NAME - assert result["data"] == ENTRY_CONFIG - - await hass.async_block_till_done() - assert len(mock_setup.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_import_removes_circuit(hass): - """Test that the import works.""" - - with patch( - "homeassistant.components.vicare.config_flow.vicare_login", - return_value=True, - ), patch( - "homeassistant.components.vicare.async_setup", return_value=True - ) as mock_setup, patch( - "homeassistant.components.vicare.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - ENTRY_CONFIG[CONF_CIRCUIT] = 1 - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=ENTRY_CONFIG, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == VICARE_NAME - assert result["data"] == ENTRY_CONFIG - - await hass.async_block_till_done() - assert len(mock_setup.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_import_adds_heating_type(hass): - """Test that the import works.""" - - with patch( - "homeassistant.components.vicare.config_flow.vicare_login", - return_value=True, - ), patch( - "homeassistant.components.vicare.async_setup", return_value=True - ) as mock_setup, patch( - "homeassistant.components.vicare.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=ENTRY_CONFIG_NO_HEATING_TYPE, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == VICARE_NAME - assert result["data"] == ENTRY_CONFIG - - await hass.async_block_till_done() - assert len(mock_setup.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 - - async def test_invalid_login(hass) -> None: """Test a flow with an invalid Vicare login.""" result = await hass.config_entries.flow.async_init( @@ -171,8 +89,6 @@ async def test_form_dhcp(hass): "homeassistant.components.vicare.config_flow.vicare_login", return_value=None, ), patch( - "homeassistant.components.vicare.async_setup", return_value=True - ) as mock_setup, patch( "homeassistant.components.vicare.async_setup_entry", return_value=True, ) as mock_setup_entry: @@ -189,27 +105,9 @@ async def test_form_dhcp(hass): assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result2["title"] == "ViCare" assert result2["data"] == ENTRY_CONFIG - assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 -async def test_import_single_instance_allowed(hass): - """Test that configuring more than one instance is rejected.""" - mock_entry = MockConfigEntry( - domain=DOMAIN, - data=ENTRY_CONFIG, - ) - mock_entry.add_to_hass(hass) - - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_IMPORT}, - data=ENTRY_CONFIG, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "single_instance_allowed" - - async def test_dhcp_single_instance_allowed(hass): """Test that configuring more than one instance is rejected.""" mock_entry = MockConfigEntry( From a5e100176b76152ef40f4952eea6d13bdab4ad67 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 24 May 2022 20:46:06 +0200 Subject: [PATCH 784/930] Refactor zwave_js setup entry (#72414) * Refactor zwave_js setup entry * Improve message --- homeassistant/components/zwave_js/__init__.py | 244 +++++++++--------- 1 file changed, 121 insertions(+), 123 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index d12c7df6a79..0c716a39c75 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -9,6 +9,7 @@ from typing import Any from async_timeout import timeout from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.exceptions import BaseZwaveJSServerError, InvalidServerVersion +from zwave_js_server.model.driver import Driver from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.notification import ( EntryControlNotification, @@ -25,12 +26,6 @@ from homeassistant.const import ( ATTR_DEVICE_ID, ATTR_DOMAIN, ATTR_ENTITY_ID, - ATTR_IDENTIFIERS, - ATTR_MANUFACTURER, - ATTR_MODEL, - ATTR_NAME, - ATTR_SUGGESTED_AREA, - ATTR_SW_VERSION, CONF_URL, EVENT_HOMEASSISTANT_STOP, ) @@ -39,7 +34,7 @@ from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry, entity_registry from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.dispatcher import async_dispatcher_send -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import UNDEFINED, ConfigType from .addon import AddonError, AddonManager, AddonState, get_addon_manager from .api import async_register_api @@ -154,39 +149,105 @@ def register_node_in_dev_reg( else: ids = {device_id} - params = { - ATTR_IDENTIFIERS: ids, - ATTR_SW_VERSION: node.firmware_version, - ATTR_NAME: node.name - or node.device_config.description - or f"Node {node.node_id}", - ATTR_MODEL: node.device_config.label, - ATTR_MANUFACTURER: node.device_config.manufacturer, - } - if node.location: - params[ATTR_SUGGESTED_AREA] = node.location - device = dev_reg.async_get_or_create(config_entry_id=entry.entry_id, **params) + device = dev_reg.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers=ids, + sw_version=node.firmware_version, + name=node.name or node.device_config.description or f"Node {node.node_id}", + model=node.device_config.label, + manufacturer=node.device_config.manufacturer, + suggested_area=node.location if node.location else UNDEFINED, + ) async_dispatcher_send(hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device) return device -async def async_setup_entry( # noqa: C901 - hass: HomeAssistant, entry: ConfigEntry -) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Z-Wave JS from a config entry.""" if use_addon := entry.data.get(CONF_USE_ADDON): await async_ensure_addon_running(hass, entry) client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass)) + entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {}) + + # connect and throw error if connection failed + try: + async with timeout(CONNECT_TIMEOUT): + await client.connect() + except InvalidServerVersion as err: + if not entry_hass_data.get(DATA_INVALID_SERVER_VERSION_LOGGED): + LOGGER.error("Invalid server version: %s", err) + entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = True + if use_addon: + async_ensure_addon_updated(hass) + raise ConfigEntryNotReady from err + except (asyncio.TimeoutError, BaseZwaveJSServerError) as err: + if not entry_hass_data.get(DATA_CONNECT_FAILED_LOGGED): + LOGGER.error("Failed to connect: %s", err) + entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = True + raise ConfigEntryNotReady from err + else: + LOGGER.info("Connected to Zwave JS Server") + entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = False + entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = False + + dev_reg = device_registry.async_get(hass) + ent_reg = entity_registry.async_get(hass) + services = ZWaveServices(hass, ent_reg, dev_reg) + services.async_register() + + # Set up websocket API + async_register_api(hass) + + platform_task = hass.async_create_task(start_platforms(hass, entry, client)) + entry_hass_data[DATA_START_PLATFORM_TASK] = platform_task + + return True + + +async def start_platforms( + hass: HomeAssistant, entry: ConfigEntry, client: ZwaveClient +) -> None: + """Start platforms and perform discovery.""" + entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {}) + entry_hass_data[DATA_CLIENT] = client + entry_hass_data[DATA_PLATFORM_SETUP] = {} + driver_ready = asyncio.Event() + + async def handle_ha_shutdown(event: Event) -> None: + """Handle HA shutdown.""" + await disconnect_client(hass, entry) + + listen_task = asyncio.create_task(client_listen(hass, entry, client, driver_ready)) + entry_hass_data[DATA_CLIENT_LISTEN_TASK] = listen_task + entry.async_on_unload( + hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, handle_ha_shutdown) + ) + + try: + await driver_ready.wait() + except asyncio.CancelledError: + LOGGER.debug("Cancelling start platforms") + return + + LOGGER.info("Connection to Zwave JS Server initialized") + + if client.driver is None: + raise RuntimeError("Driver not ready.") + + await setup_driver(hass, entry, client, client.driver) + + +async def setup_driver( # noqa: C901 + hass: HomeAssistant, entry: ConfigEntry, client: ZwaveClient, driver: Driver +) -> None: + """Set up devices using the ready driver.""" dev_reg = device_registry.async_get(hass) ent_reg = entity_registry.async_get(hass) entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {}) - - entry_hass_data[DATA_CLIENT] = client - platform_setup_tasks = entry_hass_data[DATA_PLATFORM_SETUP] = {} - + platform_setup_tasks = entry_hass_data[DATA_PLATFORM_SETUP] registered_unique_ids: dict[str, dict[str, set[str]]] = defaultdict(dict) discovered_value_ids: dict[str, set[str]] = defaultdict(set) @@ -384,7 +445,7 @@ async def async_setup_entry( # noqa: C901 { ATTR_DOMAIN: DOMAIN, ATTR_NODE_ID: notification.node.node_id, - ATTR_HOME_ID: client.driver.controller.home_id, + ATTR_HOME_ID: driver.controller.home_id, ATTR_ENDPOINT: notification.endpoint, ATTR_DEVICE_ID: device.id, ATTR_COMMAND_CLASS: notification.command_class, @@ -414,7 +475,7 @@ async def async_setup_entry( # noqa: C901 event_data = { ATTR_DOMAIN: DOMAIN, ATTR_NODE_ID: notification.node.node_id, - ATTR_HOME_ID: client.driver.controller.home_id, + ATTR_HOME_ID: driver.controller.home_id, ATTR_DEVICE_ID: device.id, ATTR_COMMAND_CLASS: notification.command_class, } @@ -487,7 +548,7 @@ async def async_setup_entry( # noqa: C901 ZWAVE_JS_VALUE_UPDATED_EVENT, { ATTR_NODE_ID: value.node.node_id, - ATTR_HOME_ID: client.driver.controller.home_id, + ATTR_HOME_ID: driver.controller.home_id, ATTR_DEVICE_ID: device.id, ATTR_ENTITY_ID: entity_id, ATTR_COMMAND_CLASS: value.command_class, @@ -502,105 +563,42 @@ async def async_setup_entry( # noqa: C901 }, ) - # connect and throw error if connection failed - try: - async with timeout(CONNECT_TIMEOUT): - await client.connect() - except InvalidServerVersion as err: - if not entry_hass_data.get(DATA_INVALID_SERVER_VERSION_LOGGED): - LOGGER.error("Invalid server version: %s", err) - entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = True - if use_addon: - async_ensure_addon_updated(hass) - raise ConfigEntryNotReady from err - except (asyncio.TimeoutError, BaseZwaveJSServerError) as err: - if not entry_hass_data.get(DATA_CONNECT_FAILED_LOGGED): - LOGGER.error("Failed to connect: %s", err) - entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = True - raise ConfigEntryNotReady from err - else: - LOGGER.info("Connected to Zwave JS Server") - entry_hass_data[DATA_CONNECT_FAILED_LOGGED] = False - entry_hass_data[DATA_INVALID_SERVER_VERSION_LOGGED] = False + # If opt in preference hasn't been specified yet, we do nothing, otherwise + # we apply the preference + if opted_in := entry.data.get(CONF_DATA_COLLECTION_OPTED_IN): + await async_enable_statistics(client) + elif opted_in is False: + await driver.async_disable_statistics() - services = ZWaveServices(hass, ent_reg, dev_reg) - services.async_register() + # Check for nodes that no longer exist and remove them + stored_devices = device_registry.async_entries_for_config_entry( + dev_reg, entry.entry_id + ) + known_devices = [ + dev_reg.async_get_device({get_device_id(client, node)}) + for node in driver.controller.nodes.values() + ] - # Set up websocket API - async_register_api(hass) + # Devices that are in the device registry that are not known by the controller can be removed + for device in stored_devices: + if device not in known_devices: + dev_reg.async_remove_device(device.id) - async def start_platforms() -> None: - """Start platforms and perform discovery.""" - driver_ready = asyncio.Event() + # run discovery on all ready nodes + await asyncio.gather( + *(async_on_node_added(node) for node in driver.controller.nodes.values()) + ) - async def handle_ha_shutdown(event: Event) -> None: - """Handle HA shutdown.""" - await disconnect_client(hass, entry) - - listen_task = asyncio.create_task( - client_listen(hass, entry, client, driver_ready) + # listen for new nodes being added to the mesh + entry.async_on_unload( + driver.controller.on( + "node added", + lambda event: hass.async_create_task(async_on_node_added(event["node"])), ) - entry_hass_data[DATA_CLIENT_LISTEN_TASK] = listen_task - entry.async_on_unload( - hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, handle_ha_shutdown) - ) - - try: - await driver_ready.wait() - except asyncio.CancelledError: - LOGGER.debug("Cancelling start platforms") - return - - LOGGER.info("Connection to Zwave JS Server initialized") - - # If opt in preference hasn't been specified yet, we do nothing, otherwise - # we apply the preference - if opted_in := entry.data.get(CONF_DATA_COLLECTION_OPTED_IN): - await async_enable_statistics(client) - elif opted_in is False: - await client.driver.async_disable_statistics() - - # Check for nodes that no longer exist and remove them - stored_devices = device_registry.async_entries_for_config_entry( - dev_reg, entry.entry_id - ) - known_devices = [ - dev_reg.async_get_device({get_device_id(client, node)}) - for node in client.driver.controller.nodes.values() - ] - - # Devices that are in the device registry that are not known by the controller can be removed - for device in stored_devices: - if device not in known_devices: - dev_reg.async_remove_device(device.id) - - # run discovery on all ready nodes - await asyncio.gather( - *( - async_on_node_added(node) - for node in client.driver.controller.nodes.values() - ) - ) - - # listen for new nodes being added to the mesh - entry.async_on_unload( - client.driver.controller.on( - "node added", - lambda event: hass.async_create_task( - async_on_node_added(event["node"]) - ), - ) - ) - # listen for nodes being removed from the mesh - # NOTE: This will not remove nodes that were removed when HA was not running - entry.async_on_unload( - client.driver.controller.on("node removed", async_on_node_removed) - ) - - platform_task = hass.async_create_task(start_platforms()) - entry_hass_data[DATA_START_PLATFORM_TASK] = platform_task - - return True + ) + # listen for nodes being removed from the mesh + # NOTE: This will not remove nodes that were removed when HA was not running + entry.async_on_unload(driver.controller.on("node removed", async_on_node_removed)) async def client_listen( From 2e36a79357e09c3cdede1085c8ce5308f89a42f2 Mon Sep 17 00:00:00 2001 From: Steven Looman Date: Tue, 24 May 2022 21:37:37 +0200 Subject: [PATCH 785/930] Changes after late upnp review (#72241) * Changes after review of #70008, part 1 * Changes after review from #70008 * Revert to UpnpDataUpdateCoordinator._async_update_data --- homeassistant/components/upnp/__init__.py | 27 ++- homeassistant/components/upnp/config_flow.py | 2 +- homeassistant/components/upnp/device.py | 51 ++--- tests/components/upnp/conftest.py | 196 +++---------------- tests/components/upnp/test_binary_sensor.py | 34 ++-- tests/components/upnp/test_sensor.py | 141 ++++++------- 6 files changed, 132 insertions(+), 319 deletions(-) diff --git a/homeassistant/components/upnp/__init__.py b/homeassistant/components/upnp/__init__.py index b571a2b447f..07560f7413f 100644 --- a/homeassistant/components/upnp/__init__.py +++ b/homeassistant/components/upnp/__init__.py @@ -39,7 +39,7 @@ from .const import ( DOMAIN, LOGGER, ) -from .device import Device, async_get_mac_address_from_host +from .device import Device, async_create_device, async_get_mac_address_from_host NOTIFICATION_ID = "upnp_notification" NOTIFICATION_TITLE = "UPnP/IGD Setup" @@ -113,8 +113,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: await asyncio.wait_for(device_discovered_event.wait(), timeout=10) except asyncio.TimeoutError as err: - LOGGER.debug("Device not discovered: %s", usn) - raise ConfigEntryNotReady from err + raise ConfigEntryNotReady(f"Device not discovered: {usn}") from err finally: cancel_discovered_callback() @@ -123,12 +122,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: assert discovery_info.ssdp_location is not None location = discovery_info.ssdp_location try: - device = await Device.async_create_device(hass, location) + device = await async_create_device(hass, location) except UpnpConnectionError as err: - LOGGER.debug( - "Error connecting to device at location: %s, err: %s", location, err - ) - raise ConfigEntryNotReady from err + raise ConfigEntryNotReady( + f"Error connecting to device at location: {location}, err: {err}" + ) from err # Track the original UDN such that existing sensors do not change their unique_id. if CONFIG_ENTRY_ORIGINAL_UDN not in entry.data: @@ -255,21 +253,15 @@ class UpnpDataUpdateCoordinator(DataUpdateCoordinator): LOGGER, name=device.name, update_interval=update_interval, - update_method=self._async_fetch_data, ) - async def _async_fetch_data(self) -> Mapping[str, Any]: + async def _async_update_data(self) -> Mapping[str, Any]: """Update data.""" try: update_values = await asyncio.gather( self.device.async_get_traffic_data(), self.device.async_get_status(), ) - - return { - **update_values[0], - **update_values[1], - } except UpnpCommunicationError as exception: LOGGER.debug( "Caught exception when updating device: %s, exception: %s", @@ -280,6 +272,11 @@ class UpnpDataUpdateCoordinator(DataUpdateCoordinator): f"Unable to communicate with IGD at: {self.device.device_url}" ) from exception + return { + **update_values[0], + **update_values[1], + } + class UpnpEntity(CoordinatorEntity[UpnpDataUpdateCoordinator]): """Base class for UPnP/IGD entities.""" diff --git a/homeassistant/components/upnp/config_flow.py b/homeassistant/components/upnp/config_flow.py index b7208e6e6e2..b54098b6566 100644 --- a/homeassistant/components/upnp/config_flow.py +++ b/homeassistant/components/upnp/config_flow.py @@ -54,7 +54,7 @@ async def _async_wait_for_discoveries(hass: HomeAssistant) -> bool: async def device_discovered(info: SsdpServiceInfo, change: SsdpChange) -> None: if change != SsdpChange.BYEBYE: - LOGGER.info( + LOGGER.debug( "Device discovered: %s, at: %s", info.ssdp_usn, info.ssdp_location, diff --git a/homeassistant/components/upnp/device.py b/homeassistant/components/upnp/device.py index 0e7c7902bd9..334d870939f 100644 --- a/homeassistant/components/upnp/device.py +++ b/homeassistant/components/upnp/device.py @@ -9,7 +9,6 @@ from typing import Any from urllib.parse import urlparse from async_upnp_client.aiohttp import AiohttpSessionRequester -from async_upnp_client.client import UpnpDevice from async_upnp_client.client_factory import UpnpFactory from async_upnp_client.exceptions import UpnpError from async_upnp_client.profiles.igd import IgdDevice, StatusInfo @@ -47,15 +46,19 @@ async def async_get_mac_address_from_host(hass: HomeAssistant, host: str) -> str return mac_address -async def async_create_upnp_device( - hass: HomeAssistant, ssdp_location: str -) -> UpnpDevice: - """Create UPnP device.""" +async def async_create_device(hass: HomeAssistant, ssdp_location: str) -> Device: + """Create UPnP/IGD device.""" session = async_get_clientsession(hass) requester = AiohttpSessionRequester(session, with_sleep=True, timeout=20) factory = UpnpFactory(requester, disable_state_variable_validation=True) - return await factory.async_create_device(ssdp_location) + upnp_device = await factory.async_create_device(ssdp_location) + + # Create profile wrapper. + igd_device = IgdDevice(upnp_device, None) + device = Device(hass, igd_device) + + return device class Device: @@ -66,40 +69,8 @@ class Device: self.hass = hass self._igd_device = igd_device self.coordinator: DataUpdateCoordinator | None = None - self._mac_address: str | None = None - - @classmethod - async def async_create_device( - cls, hass: HomeAssistant, ssdp_location: str - ) -> Device: - """Create UPnP/IGD device.""" - upnp_device = await async_create_upnp_device(hass, ssdp_location) - - # Create profile wrapper. - igd_device = IgdDevice(upnp_device, None) - device = cls(hass, igd_device) - - return device - - @property - def mac_address(self) -> str | None: - """Get the mac address.""" - return self._mac_address - - @mac_address.setter - def mac_address(self, mac_address: str) -> None: - """Set the mac address.""" - self._mac_address = mac_address - - @property - def original_udn(self) -> str | None: - """Get the mac address.""" - return self._original_udn - - @original_udn.setter - def original_udn(self, original_udn: str) -> None: - """Set the original UDN.""" - self._original_udn = original_udn + self.mac_address: str | None = None + self.original_udn: str | None = None @property def udn(self) -> str: diff --git a/tests/components/upnp/conftest.py b/tests/components/upnp/conftest.py index dd22db878cf..687518bb46d 100644 --- a/tests/components/upnp/conftest.py +++ b/tests/components/upnp/conftest.py @@ -1,33 +1,23 @@ """Configuration for SSDP tests.""" from __future__ import annotations -from collections.abc import Sequence -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, create_autospec, patch from urllib.parse import urlparse from async_upnp_client.client import UpnpDevice -from async_upnp_client.event_handler import UpnpEventHandler -from async_upnp_client.profiles.igd import StatusInfo +from async_upnp_client.profiles.igd import IgdDevice, StatusInfo import pytest from homeassistant.components import ssdp from homeassistant.components.upnp.const import ( - BYTES_RECEIVED, - BYTES_SENT, CONFIG_ENTRY_LOCATION, CONFIG_ENTRY_MAC_ADDRESS, CONFIG_ENTRY_ORIGINAL_UDN, CONFIG_ENTRY_ST, CONFIG_ENTRY_UDN, DOMAIN, - PACKETS_RECEIVED, - PACKETS_SENT, - ROUTER_IP, - ROUTER_UPTIME, - WAN_STATUS, ) from homeassistant.core import HomeAssistant -from homeassistant.util import dt from tests.common import MockConfigEntry @@ -59,160 +49,37 @@ TEST_DISCOVERY = ssdp.SsdpServiceInfo( ) -class MockUpnpDevice: - """Mock async_upnp_client UpnpDevice.""" - - def __init__(self, location: str) -> None: - """Initialize.""" - self.device_url = location - - @property - def manufacturer(self) -> str: - """Get manufacturer.""" - return TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_MANUFACTURER] - - @property - def name(self) -> str: - """Get name.""" - return TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_FRIENDLY_NAME] - - @property - def model_name(self) -> str: - """Get the model name.""" - return TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_MODEL_NAME] - - @property - def device_type(self) -> str: - """Get the device type.""" - return TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_DEVICE_TYPE] - - @property - def udn(self) -> str: - """Get the UDN.""" - return TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_UDN] - - @property - def usn(self) -> str: - """Get the USN.""" - return f"{self.udn}::{self.device_type}" - - @property - def unique_id(self) -> str: - """Get the unique id.""" - return self.usn - - def reinit(self, new_upnp_device: UpnpDevice) -> None: - """Reinitialize.""" - self.device_url = new_upnp_device.device_url - - -class MockIgdDevice: - """Mock async_upnp_client IgdDevice.""" - - def __init__(self, device: MockUpnpDevice, event_handler: UpnpEventHandler) -> None: - """Initialize mock device.""" - self.device = device - self.profile_device = device - - self._timestamp = dt.utcnow() - self.traffic_times_polled = 0 - self.status_times_polled = 0 - - self.traffic_data = { - BYTES_RECEIVED: 0, - BYTES_SENT: 0, - PACKETS_RECEIVED: 0, - PACKETS_SENT: 0, - } - self.status_data = { - WAN_STATUS: "Connected", - ROUTER_UPTIME: 10, - ROUTER_IP: "8.9.10.11", - } - - @property - def name(self) -> str: - """Get the name of the device.""" - return self.profile_device.name - - @property - def manufacturer(self) -> str: - """Get the manufacturer of this device.""" - return self.profile_device.manufacturer - - @property - def model_name(self) -> str: - """Get the model name of this device.""" - return self.profile_device.model_name - - @property - def udn(self) -> str: - """Get the UDN of the device.""" - return self.profile_device.udn - - @property - def device_type(self) -> str: - """Get the device type of this device.""" - return self.profile_device.device_type - - async def async_get_total_bytes_received(self) -> int | None: - """Get total bytes received.""" - self.traffic_times_polled += 1 - return self.traffic_data[BYTES_RECEIVED] - - async def async_get_total_bytes_sent(self) -> int | None: - """Get total bytes sent.""" - return self.traffic_data[BYTES_SENT] - - async def async_get_total_packets_received(self) -> int | None: - """Get total packets received.""" - return self.traffic_data[PACKETS_RECEIVED] - - async def async_get_total_packets_sent(self) -> int | None: - """Get total packets sent.""" - return self.traffic_data[PACKETS_SENT] - - async def async_get_external_ip_address( - self, services: Sequence[str] | None = None - ) -> str | None: - """ - Get the external IP address. - - :param services List of service names to try to get action from, defaults to [WANIPC,WANPPP] - """ - return self.status_data[ROUTER_IP] - - async def async_get_status_info( - self, services: Sequence[str] | None = None - ) -> StatusInfo | None: - """ - Get status info. - - :param services List of service names to try to get action from, defaults to [WANIPC,WANPPP] - """ - self.status_times_polled += 1 - return StatusInfo( - self.status_data[WAN_STATUS], "", self.status_data[ROUTER_UPTIME] - ) - - @pytest.fixture(autouse=True) -def mock_upnp_device(): - """Mock homeassistant.components.upnp.Device.""" +def mock_igd_device() -> IgdDevice: + """Mock async_upnp_client device.""" + mock_upnp_device = create_autospec(UpnpDevice, instance=True) + mock_upnp_device.device_url = TEST_DISCOVERY.ssdp_location - async def mock_async_create_upnp_device( - hass: HomeAssistant, location: str - ) -> UpnpDevice: - """Create UPnP device.""" - return MockUpnpDevice(location) + mock_igd_device = create_autospec(IgdDevice) + mock_igd_device.name = TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_FRIENDLY_NAME] + mock_igd_device.manufacturer = TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_MANUFACTURER] + mock_igd_device.model_name = TEST_DISCOVERY.upnp[ssdp.ATTR_UPNP_MODEL_NAME] + mock_igd_device.udn = TEST_DISCOVERY.ssdp_udn + mock_igd_device.device = mock_upnp_device + + mock_igd_device.async_get_total_bytes_received.return_value = 0 + mock_igd_device.async_get_total_bytes_sent.return_value = 0 + mock_igd_device.async_get_total_packets_received.return_value = 0 + mock_igd_device.async_get_total_packets_sent.return_value = 0 + mock_igd_device.async_get_status_info.return_value = StatusInfo( + "Connected", + "", + 10, + ) + mock_igd_device.async_get_external_ip_address.return_value = "8.9.10.11" with patch( - "homeassistant.components.upnp.device.async_create_upnp_device", - side_effect=mock_async_create_upnp_device, - ) as mock_async_create_upnp_device, patch( - "homeassistant.components.upnp.device.IgdDevice", new=MockIgdDevice - ) as mock_igd_device: - yield mock_async_create_upnp_device, mock_igd_device + "homeassistant.components.upnp.device.UpnpFactory.async_create_device" + ), patch( + "homeassistant.components.upnp.device.IgdDevice.__new__", + return_value=mock_igd_device, + ): + yield mock_igd_device @pytest.fixture @@ -297,11 +164,11 @@ async def ssdp_no_discovery(): @pytest.fixture -async def config_entry( +async def mock_config_entry( hass: HomeAssistant, mock_get_source_ip, ssdp_instant_discovery, - mock_upnp_device, + mock_igd_device: IgdDevice, mock_mac_address_from_host, ): """Create an initialized integration.""" @@ -316,6 +183,7 @@ async def config_entry( CONFIG_ENTRY_MAC_ADDRESS: TEST_MAC_ADDRESS, }, ) + entry.igd_device = mock_igd_device # Load config_entry. entry.add_to_hass(hass) diff --git a/tests/components/upnp/test_binary_sensor.py b/tests/components/upnp/test_binary_sensor.py index 22264792420..24e5cdce47c 100644 --- a/tests/components/upnp/test_binary_sensor.py +++ b/tests/components/upnp/test_binary_sensor.py @@ -2,36 +2,34 @@ from datetime import timedelta -from homeassistant.components.upnp.const import ( - DOMAIN, - ROUTER_IP, - ROUTER_UPTIME, - WAN_STATUS, -) +from async_upnp_client.profiles.igd import StatusInfo + +from homeassistant.components.upnp.const import DEFAULT_SCAN_INTERVAL from homeassistant.core import HomeAssistant import homeassistant.util.dt as dt_util -from .conftest import MockIgdDevice - from tests.common import MockConfigEntry, async_fire_time_changed -async def test_upnp_binary_sensors(hass: HomeAssistant, config_entry: MockConfigEntry): +async def test_upnp_binary_sensors( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +): """Test normal sensors.""" # First poll. wan_status_state = hass.states.get("binary_sensor.mock_name_wan_status") assert wan_status_state.state == "on" # Second poll. - mock_device: MockIgdDevice = hass.data[DOMAIN][ - config_entry.entry_id - ].device._igd_device - mock_device.status_data = { - WAN_STATUS: "Disconnected", - ROUTER_UPTIME: 100, - ROUTER_IP: "", - } - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=31)) + mock_igd_device = mock_config_entry.igd_device + mock_igd_device.async_get_status_info.return_value = StatusInfo( + "Disconnected", + "", + 40, + ) + + async_fire_time_changed( + hass, dt_util.utcnow() + timedelta(seconds=DEFAULT_SCAN_INTERVAL) + ) await hass.async_block_till_done() wan_status_state = hass.states.get("binary_sensor.mock_name_wan_status") diff --git a/tests/components/upnp/test_sensor.py b/tests/components/upnp/test_sensor.py index 6c00f63a479..2abd357ac31 100644 --- a/tests/components/upnp/test_sensor.py +++ b/tests/components/upnp/test_sensor.py @@ -3,114 +3,93 @@ from datetime import timedelta from unittest.mock import patch +from async_upnp_client.profiles.igd import StatusInfo import pytest -from homeassistant.components.upnp import UpnpDataUpdateCoordinator -from homeassistant.components.upnp.const import ( - BYTES_RECEIVED, - BYTES_SENT, - DEFAULT_SCAN_INTERVAL, - DOMAIN, - PACKETS_RECEIVED, - PACKETS_SENT, - ROUTER_IP, - ROUTER_UPTIME, - WAN_STATUS, -) +from homeassistant.components.upnp.const import DEFAULT_SCAN_INTERVAL from homeassistant.core import HomeAssistant import homeassistant.util.dt as dt_util -from .conftest import MockIgdDevice - from tests.common import MockConfigEntry, async_fire_time_changed -async def test_upnp_sensors(hass: HomeAssistant, config_entry: MockConfigEntry): +async def test_upnp_sensors(hass: HomeAssistant, mock_config_entry: MockConfigEntry): """Test normal sensors.""" # First poll. - b_received_state = hass.states.get("sensor.mock_name_b_received") - b_sent_state = hass.states.get("sensor.mock_name_b_sent") - packets_received_state = hass.states.get("sensor.mock_name_packets_received") - packets_sent_state = hass.states.get("sensor.mock_name_packets_sent") - external_ip_state = hass.states.get("sensor.mock_name_external_ip") - wan_status_state = hass.states.get("sensor.mock_name_wan_status") - assert b_received_state.state == "0" - assert b_sent_state.state == "0" - assert packets_received_state.state == "0" - assert packets_sent_state.state == "0" - assert external_ip_state.state == "8.9.10.11" - assert wan_status_state.state == "Connected" + assert hass.states.get("sensor.mock_name_b_received").state == "0" + assert hass.states.get("sensor.mock_name_b_sent").state == "0" + assert hass.states.get("sensor.mock_name_packets_received").state == "0" + assert hass.states.get("sensor.mock_name_packets_sent").state == "0" + assert hass.states.get("sensor.mock_name_external_ip").state == "8.9.10.11" + assert hass.states.get("sensor.mock_name_wan_status").state == "Connected" # Second poll. - mock_device: MockIgdDevice = hass.data[DOMAIN][ - config_entry.entry_id - ].device._igd_device - mock_device.traffic_data = { - BYTES_RECEIVED: 10240, - BYTES_SENT: 20480, - PACKETS_RECEIVED: 30, - PACKETS_SENT: 40, - } - mock_device.status_data = { - WAN_STATUS: "Disconnected", - ROUTER_UPTIME: 100, - ROUTER_IP: "", - } + mock_igd_device = mock_config_entry.igd_device + mock_igd_device.async_get_total_bytes_received.return_value = 10240 + mock_igd_device.async_get_total_bytes_sent.return_value = 20480 + mock_igd_device.async_get_total_packets_received.return_value = 30 + mock_igd_device.async_get_total_packets_sent.return_value = 40 + mock_igd_device.async_get_status_info.return_value = StatusInfo( + "Disconnected", + "", + 40, + ) + mock_igd_device.async_get_external_ip_address.return_value = "" + now = dt_util.utcnow() async_fire_time_changed(hass, now + timedelta(seconds=DEFAULT_SCAN_INTERVAL)) await hass.async_block_till_done() - b_received_state = hass.states.get("sensor.mock_name_b_received") - b_sent_state = hass.states.get("sensor.mock_name_b_sent") - packets_received_state = hass.states.get("sensor.mock_name_packets_received") - packets_sent_state = hass.states.get("sensor.mock_name_packets_sent") - external_ip_state = hass.states.get("sensor.mock_name_external_ip") - wan_status_state = hass.states.get("sensor.mock_name_wan_status") - assert b_received_state.state == "10240" - assert b_sent_state.state == "20480" - assert packets_received_state.state == "30" - assert packets_sent_state.state == "40" - assert external_ip_state.state == "" - assert wan_status_state.state == "Disconnected" + assert hass.states.get("sensor.mock_name_b_received").state == "10240" + assert hass.states.get("sensor.mock_name_b_sent").state == "20480" + assert hass.states.get("sensor.mock_name_packets_received").state == "30" + assert hass.states.get("sensor.mock_name_packets_sent").state == "40" + assert hass.states.get("sensor.mock_name_external_ip").state == "" + assert hass.states.get("sensor.mock_name_wan_status").state == "Disconnected" -async def test_derived_upnp_sensors(hass: HomeAssistant, config_entry: MockConfigEntry): +async def test_derived_upnp_sensors( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +): """Test derived sensors.""" - coordinator: UpnpDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - # First poll. - kib_s_received_state = hass.states.get("sensor.mock_name_kib_s_received") - kib_s_sent_state = hass.states.get("sensor.mock_name_kib_s_sent") - packets_s_received_state = hass.states.get("sensor.mock_name_packets_s_received") - packets_s_sent_state = hass.states.get("sensor.mock_name_packets_s_sent") - assert kib_s_received_state.state == "unknown" - assert kib_s_sent_state.state == "unknown" - assert packets_s_received_state.state == "unknown" - assert packets_s_sent_state.state == "unknown" + assert hass.states.get("sensor.mock_name_kib_s_received").state == "unknown" + assert hass.states.get("sensor.mock_name_kib_s_sent").state == "unknown" + assert hass.states.get("sensor.mock_name_packets_s_received").state == "unknown" + assert hass.states.get("sensor.mock_name_packets_s_sent").state == "unknown" # Second poll. + mock_igd_device = mock_config_entry.igd_device + mock_igd_device.async_get_total_bytes_received.return_value = int( + 10240 * DEFAULT_SCAN_INTERVAL + ) + mock_igd_device.async_get_total_bytes_sent.return_value = int( + 20480 * DEFAULT_SCAN_INTERVAL + ) + mock_igd_device.async_get_total_packets_received.return_value = int( + 30 * DEFAULT_SCAN_INTERVAL + ) + mock_igd_device.async_get_total_packets_sent.return_value = int( + 40 * DEFAULT_SCAN_INTERVAL + ) + now = dt_util.utcnow() with patch( "homeassistant.components.upnp.device.utcnow", return_value=now + timedelta(seconds=DEFAULT_SCAN_INTERVAL), ): - mock_device: MockIgdDevice = coordinator.device._igd_device - mock_device.traffic_data = { - BYTES_RECEIVED: int(10240 * DEFAULT_SCAN_INTERVAL), - BYTES_SENT: int(20480 * DEFAULT_SCAN_INTERVAL), - PACKETS_RECEIVED: int(30 * DEFAULT_SCAN_INTERVAL), - PACKETS_SENT: int(40 * DEFAULT_SCAN_INTERVAL), - } async_fire_time_changed(hass, now + timedelta(seconds=DEFAULT_SCAN_INTERVAL)) await hass.async_block_till_done() - kib_s_received_state = hass.states.get("sensor.mock_name_kib_s_received") - kib_s_sent_state = hass.states.get("sensor.mock_name_kib_s_sent") - packets_s_received_state = hass.states.get( - "sensor.mock_name_packets_s_received" - ) - packets_s_sent_state = hass.states.get("sensor.mock_name_packets_s_sent") - assert float(kib_s_received_state.state) == pytest.approx(10.0, rel=0.1) - assert float(kib_s_sent_state.state) == pytest.approx(20.0, rel=0.1) - assert float(packets_s_received_state.state) == pytest.approx(30.0, rel=0.1) - assert float(packets_s_sent_state.state) == pytest.approx(40.0, rel=0.1) + assert float( + hass.states.get("sensor.mock_name_kib_s_received").state + ) == pytest.approx(10.0, rel=0.1) + assert float( + hass.states.get("sensor.mock_name_kib_s_sent").state + ) == pytest.approx(20.0, rel=0.1) + assert float( + hass.states.get("sensor.mock_name_packets_s_received").state + ) == pytest.approx(30.0, rel=0.1) + assert float( + hass.states.get("sensor.mock_name_packets_s_sent").state + ) == pytest.approx(40.0, rel=0.1) From f33151ff8b9457f19619d3c3a94ea7aad0b5f988 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 24 May 2022 21:42:11 +0200 Subject: [PATCH 786/930] Adjust config-flow type hints in unifi (#72411) * Adjust config-flow type hints in unifi * Use mapping * Use mapping * Fix tests * Fix tests * Revert "Use mapping" This reverts commit 126fedc84828dfa2badc1b6f673ab8a4e702d230. --- homeassistant/components/unifi/config_flow.py | 50 +++++++++++++------ tests/components/unifi/conftest.py | 2 +- tests/components/unifi/test_config_flow.py | 6 +-- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index d02f3f49a5e..50e578b1dae 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -5,7 +5,11 @@ Discovery of UniFi Network instances hosted on UDM and UDM Pro devices through SSDP. Reauthentication when issue with credentials are reported. Configuration of options through options flow. """ +from __future__ import annotations + +from collections.abc import Mapping import socket +from typing import Any from urllib.parse import urlparse import voluptuous as vol @@ -19,7 +23,7 @@ from homeassistant.const import ( CONF_USERNAME, CONF_VERIFY_SSL, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import format_mac @@ -63,11 +67,13 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> UnifiOptionsFlowHandler: """Get the options flow for this handler.""" return UnifiOptionsFlowHandler(config_entry) - def __init__(self): + def __init__(self) -> None: """Initialize the UniFi Network flow.""" self.config = {} self.site_ids = {} @@ -75,7 +81,9 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): self.reauth_config_entry = None self.reauth_schema = {} - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initialized by the user.""" errors = {} @@ -123,7 +131,7 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): return await self.async_step_site() - if not (host := self.config.get(CONF_HOST, "")) and await async_discover_unifi( + if not (host := self.config.get(CONF_HOST, "")) and await _async_discover_unifi( self.hass ): host = "unifi" @@ -144,7 +152,9 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): errors=errors, ) - async def async_step_site(self, user_input=None): + async def async_step_site( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Select site to control.""" errors = {} @@ -192,7 +202,7 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): errors=errors, ) - async def async_step_reauth(self, data: dict): + async def async_step_reauth(self, data: Mapping[str, Any]) -> FlowResult: """Trigger a reauthentication flow.""" config_entry = self.hass.config_entries.async_get_entry( self.context["entry_id"] @@ -248,13 +258,15 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): class UnifiOptionsFlowHandler(config_entries.OptionsFlow): """Handle Unifi Network options.""" - def __init__(self, config_entry): + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize UniFi Network options flow.""" self.config_entry = config_entry self.options = dict(config_entry.options) self.controller = None - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage the UniFi Network options.""" if self.config_entry.entry_id not in self.hass.data[UNIFI_DOMAIN]: return self.async_abort(reason="integration_not_setup") @@ -266,7 +278,9 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_simple_options() - async def async_step_simple_options(self, user_input=None): + async def async_step_simple_options( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """For users without advanced settings enabled.""" if user_input is not None: self.options.update(user_input) @@ -299,7 +313,9 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): last_step=True, ) - async def async_step_device_tracker(self, user_input=None): + async def async_step_device_tracker( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage the device tracker options.""" if user_input is not None: self.options.update(user_input) @@ -359,7 +375,9 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): last_step=False, ) - async def async_step_client_control(self, user_input=None): + async def async_step_client_control( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage configuration of network access controlled clients.""" errors = {} @@ -403,7 +421,9 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): last_step=False, ) - async def async_step_statistics_sensors(self, user_input=None): + async def async_step_statistics_sensors( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage the statistics sensors options.""" if user_input is not None: self.options.update(user_input) @@ -426,12 +446,12 @@ class UnifiOptionsFlowHandler(config_entries.OptionsFlow): last_step=True, ) - async def _update_options(self): + async def _update_options(self) -> FlowResult: """Update config entry options.""" return self.async_create_entry(title="", data=self.options) -async def async_discover_unifi(hass): +async def _async_discover_unifi(hass: HomeAssistant) -> str | None: """Discover UniFi Network address.""" try: return await hass.async_add_executor_job(socket.gethostbyname, "unifi") diff --git a/tests/components/unifi/conftest.py b/tests/components/unifi/conftest.py index e21c458386f..e2b77ac1ed1 100644 --- a/tests/components/unifi/conftest.py +++ b/tests/components/unifi/conftest.py @@ -34,7 +34,7 @@ def mock_unifi_websocket(): def mock_discovery(): """No real network traffic allowed.""" with patch( - "homeassistant.components.unifi.config_flow.async_discover_unifi", + "homeassistant.components.unifi.config_flow._async_discover_unifi", return_value=None, ) as mock: yield mock diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index 321cbdfd9e8..e774c5a551d 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -7,7 +7,7 @@ import aiounifi from homeassistant import config_entries, data_entry_flow from homeassistant.components import ssdp -from homeassistant.components.unifi.config_flow import async_discover_unifi +from homeassistant.components.unifi.config_flow import _async_discover_unifi from homeassistant.components.unifi.const import ( CONF_ALLOW_BANDWIDTH_SENSORS, CONF_ALLOW_UPTIME_SENSORS, @@ -686,10 +686,10 @@ async def test_form_ssdp_gets_form_with_ignored_entry(hass): async def test_discover_unifi_positive(hass): """Verify positive run of UniFi discovery.""" with patch("socket.gethostbyname", return_value=True): - assert await async_discover_unifi(hass) + assert await _async_discover_unifi(hass) async def test_discover_unifi_negative(hass): """Verify negative run of UniFi discovery.""" with patch("socket.gethostbyname", side_effect=socket.gaierror): - assert await async_discover_unifi(hass) is None + assert await _async_discover_unifi(hass) is None From cd769a55c2a06707dd1bb64ceb8e60ce7c9f78b4 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Tue, 24 May 2022 21:44:18 +0200 Subject: [PATCH 787/930] Update BMW connected drive to async (#71827) * Change BMW connected drive to async * Fix coordinator exceptions, fix tests * Fix using deprecated property * Write HA state directly * Remove code that cannnot throw Exceptions from try/except * Use public async_write_ha_state Co-authored-by: Martin Hjelmare * Fix login using refresh_token if token expired * MyPy fixes * Fix pytest, bump dependency * Replace OptionFlow listener with explicit refresh * Remove uneeded async_get_entry * Update test to include change on OptionsFlow * Bump bimmer_connected to 0.9.0 * Migrate renamed entitity unique_ids * Don't replace async_migrate_entries, add tests * Rename existing_entry to existing_entry_id Co-authored-by: Martin Hjelmare * Update tests * Import full EntityRegistry * Fix comment * Increase timeout to 60s * Rely on library timeout Co-authored-by: rikroe Co-authored-by: Martin Hjelmare --- .../bmw_connected_drive/__init__.py | 81 +++++++---- .../bmw_connected_drive/binary_sensor.py | 97 ++++++------- .../components/bmw_connected_drive/button.py | 38 +++-- .../bmw_connected_drive/config_flow.py | 46 +++--- .../components/bmw_connected_drive/const.py | 5 +- .../bmw_connected_drive/coordinator.py | 82 ++++++----- .../bmw_connected_drive/device_tracker.py | 20 +-- .../components/bmw_connected_drive/lock.py | 32 ++--- .../bmw_connected_drive/manifest.json | 2 +- .../components/bmw_connected_drive/notify.py | 8 +- .../components/bmw_connected_drive/sensor.py | 58 +++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../bmw_connected_drive/__init__.py | 27 ++++ .../bmw_connected_drive/test_config_flow.py | 37 ++--- .../bmw_connected_drive/test_init.py | 136 ++++++++++++++++++ 16 files changed, 436 insertions(+), 237 deletions(-) create mode 100644 tests/components/bmw_connected_drive/test_init.py diff --git a/homeassistant/components/bmw_connected_drive/__init__.py b/homeassistant/components/bmw_connected_drive/__init__.py index dae211f91a2..7023dd7481a 100644 --- a/homeassistant/components/bmw_connected_drive/__init__.py +++ b/homeassistant/components/bmw_connected_drive/__init__.py @@ -1,31 +1,27 @@ -"""Reads vehicle status from BMW connected drive portal.""" +"""Reads vehicle status from MyBMW portal.""" from __future__ import annotations +import logging from typing import Any -from bimmer_connected.vehicle import ConnectedDriveVehicle +from bimmer_connected.vehicle import MyBMWVehicle import voluptuous as vol from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - CONF_DEVICE_ID, - CONF_ENTITY_ID, - CONF_NAME, - CONF_PASSWORD, - CONF_REGION, - CONF_USERNAME, - Platform, -) +from homeassistant.const import CONF_DEVICE_ID, CONF_ENTITY_ID, CONF_NAME, Platform from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import discovery +from homeassistant.helpers import discovery, entity_registry as er import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import DeviceInfo, Entity +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ATTR_VIN, ATTRIBUTION, CONF_READ_ONLY, DATA_HASS_CONFIG, DOMAIN from .coordinator import BMWDataUpdateCoordinator +_LOGGER = logging.getLogger(__name__) + + CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) SERVICE_SCHEMA = vol.Schema( @@ -74,18 +70,56 @@ def _async_migrate_options_from_data_if_missing( hass.config_entries.async_update_entry(entry, data=data, options=options) +async def _async_migrate_entries( + hass: HomeAssistant, config_entry: ConfigEntry +) -> bool: + """Migrate old entry.""" + entity_registry = er.async_get(hass) + + @callback + def update_unique_id(entry: er.RegistryEntry) -> dict[str, str] | None: + replacements = { + "charging_level_hv": "remaining_battery_percent", + "fuel_percent": "remaining_fuel_percent", + } + if (key := entry.unique_id.split("-")[-1]) in replacements: + new_unique_id = entry.unique_id.replace(key, replacements[key]) + _LOGGER.debug( + "Migrating entity '%s' unique_id from '%s' to '%s'", + entry.entity_id, + entry.unique_id, + new_unique_id, + ) + if existing_entity_id := entity_registry.async_get_entity_id( + entry.domain, entry.platform, new_unique_id + ): + _LOGGER.debug( + "Cannot migrate to unique_id '%s', already exists for '%s'", + new_unique_id, + existing_entity_id, + ) + return None + return { + "new_unique_id": new_unique_id, + } + return None + + await er.async_migrate_entries(hass, config_entry.entry_id, update_unique_id) + + return True + + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up BMW Connected Drive from a config entry.""" _async_migrate_options_from_data_if_missing(hass, entry) + await _async_migrate_entries(hass, entry) + # Set up one data coordinator per account/config entry coordinator = BMWDataUpdateCoordinator( hass, - username=entry.data[CONF_USERNAME], - password=entry.data[CONF_PASSWORD], - region=entry.data[CONF_REGION], - read_only=entry.options[CONF_READ_ONLY], + entry=entry, ) await coordinator.async_config_entry_first_refresh() @@ -109,9 +143,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) ) - # Add event listener for option flow changes - entry.async_on_unload(entry.add_update_listener(async_update_options)) - return True @@ -127,20 +158,16 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -async def async_update_options(hass: HomeAssistant, config_entry: ConfigEntry) -> None: - """Handle options update.""" - await hass.config_entries.async_reload(config_entry.entry_id) - - -class BMWConnectedDriveBaseEntity(CoordinatorEntity[BMWDataUpdateCoordinator], Entity): +class BMWBaseEntity(CoordinatorEntity[BMWDataUpdateCoordinator]): """Common base for BMW entities.""" + coordinator: BMWDataUpdateCoordinator _attr_attribution = ATTRIBUTION def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, ) -> None: """Initialize entity.""" super().__init__(coordinator) diff --git a/homeassistant/components/bmw_connected_drive/binary_sensor.py b/homeassistant/components/bmw_connected_drive/binary_sensor.py index cae70f6de4b..a19ccc8f715 100644 --- a/homeassistant/components/bmw_connected_drive/binary_sensor.py +++ b/homeassistant/components/bmw_connected_drive/binary_sensor.py @@ -1,18 +1,15 @@ -"""Reads vehicle status from BMW connected drive portal.""" +"""Reads vehicle status from BMW MyBMW portal.""" from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass import logging -from typing import Any, cast +from typing import Any -from bimmer_connected.vehicle import ConnectedDriveVehicle -from bimmer_connected.vehicle_status import ( - ChargingState, - ConditionBasedServiceReport, - LockState, - VehicleStatus, -) +from bimmer_connected.vehicle import MyBMWVehicle +from bimmer_connected.vehicle.doors_windows import LockState +from bimmer_connected.vehicle.fuel_and_battery import ChargingState +from bimmer_connected.vehicle.reports import ConditionBasedService from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -24,7 +21,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util.unit_system import UnitSystem -from . import BMWConnectedDriveBaseEntity +from . import BMWBaseEntity from .const import DOMAIN, UNIT_MAP from .coordinator import BMWDataUpdateCoordinator @@ -32,20 +29,20 @@ _LOGGER = logging.getLogger(__name__) def _condition_based_services( - vehicle_state: VehicleStatus, unit_system: UnitSystem + vehicle: MyBMWVehicle, unit_system: UnitSystem ) -> dict[str, Any]: extra_attributes = {} - for report in vehicle_state.condition_based_services: + for report in vehicle.condition_based_services.messages: extra_attributes.update(_format_cbs_report(report, unit_system)) return extra_attributes -def _check_control_messages(vehicle_state: VehicleStatus) -> dict[str, Any]: +def _check_control_messages(vehicle: MyBMWVehicle) -> dict[str, Any]: extra_attributes: dict[str, Any] = {} - if vehicle_state.has_check_control_messages: + if vehicle.check_control_messages.has_check_control_messages: cbs_list = [ message.description_short - for message in vehicle_state.check_control_messages + for message in vehicle.check_control_messages.messages ] extra_attributes["check_control_messages"] = cbs_list else: @@ -54,18 +51,18 @@ def _check_control_messages(vehicle_state: VehicleStatus) -> dict[str, Any]: def _format_cbs_report( - report: ConditionBasedServiceReport, unit_system: UnitSystem + report: ConditionBasedService, unit_system: UnitSystem ) -> dict[str, Any]: result: dict[str, Any] = {} service_type = report.service_type.lower().replace("_", " ") result[f"{service_type} status"] = report.state.value if report.due_date is not None: result[f"{service_type} date"] = report.due_date.strftime("%Y-%m-%d") - if report.due_distance is not None: + if report.due_distance.value and report.due_distance.unit: distance = round( unit_system.length( - report.due_distance[0], - UNIT_MAP.get(report.due_distance[1], report.due_distance[1]), + report.due_distance.value, + UNIT_MAP.get(report.due_distance.unit, report.due_distance.unit), ) ) result[f"{service_type} distance"] = f"{distance} {unit_system.length_unit}" @@ -76,7 +73,7 @@ def _format_cbs_report( class BMWRequiredKeysMixin: """Mixin for required keys.""" - value_fn: Callable[[VehicleStatus], bool] + value_fn: Callable[[MyBMWVehicle], bool] @dataclass @@ -85,7 +82,7 @@ class BMWBinarySensorEntityDescription( ): """Describes BMW binary_sensor entity.""" - attr_fn: Callable[[VehicleStatus, UnitSystem], dict[str, Any]] | None = None + attr_fn: Callable[[MyBMWVehicle, UnitSystem], dict[str, Any]] | None = None SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( @@ -95,8 +92,10 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.OPENING, icon="mdi:car-door-lock", # device class opening: On means open, Off means closed - value_fn=lambda s: not s.all_lids_closed, - attr_fn=lambda s, u: {lid.name: lid.state.value for lid in s.lids}, + value_fn=lambda v: not v.doors_and_windows.all_lids_closed, + attr_fn=lambda v, u: { + lid.name: lid.state.value for lid in v.doors_and_windows.lids + }, ), BMWBinarySensorEntityDescription( key="windows", @@ -104,8 +103,10 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.OPENING, icon="mdi:car-door", # device class opening: On means open, Off means closed - value_fn=lambda s: not s.all_windows_closed, - attr_fn=lambda s, u: {window.name: window.state.value for window in s.windows}, + value_fn=lambda v: not v.doors_and_windows.all_windows_closed, + attr_fn=lambda v, u: { + window.name: window.state.value for window in v.doors_and_windows.windows + }, ), BMWBinarySensorEntityDescription( key="door_lock_state", @@ -114,29 +115,19 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( icon="mdi:car-key", # device class lock: On means unlocked, Off means locked # Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED - value_fn=lambda s: s.door_lock_state + value_fn=lambda v: v.doors_and_windows.door_lock_state not in {LockState.LOCKED, LockState.SECURED}, - attr_fn=lambda s, u: { - "door_lock_state": s.door_lock_state.value, - "last_update_reason": s.last_update_reason, + attr_fn=lambda v, u: { + "door_lock_state": v.doors_and_windows.door_lock_state.value }, ), - BMWBinarySensorEntityDescription( - key="lights_parking", - name="Parking lights", - device_class=BinarySensorDeviceClass.LIGHT, - icon="mdi:car-parking-lights", - # device class light: On means light detected, Off means no light - value_fn=lambda s: cast(bool, s.are_parking_lights_on), - attr_fn=lambda s, u: {"lights_parking": s.parking_lights.value}, - ), BMWBinarySensorEntityDescription( key="condition_based_services", name="Condition based services", device_class=BinarySensorDeviceClass.PROBLEM, icon="mdi:wrench", # device class problem: On means problem detected, Off means no problem - value_fn=lambda s: not s.are_all_cbs_ok, + value_fn=lambda v: v.condition_based_services.is_service_required, attr_fn=_condition_based_services, ), BMWBinarySensorEntityDescription( @@ -145,8 +136,8 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.PROBLEM, icon="mdi:car-tire-alert", # device class problem: On means problem detected, Off means no problem - value_fn=lambda s: cast(bool, s.has_check_control_messages), - attr_fn=lambda s, u: _check_control_messages(s), + value_fn=lambda v: v.check_control_messages.has_check_control_messages, + attr_fn=lambda v, u: _check_control_messages(v), ), # electric BMWBinarySensorEntityDescription( @@ -155,10 +146,9 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.BATTERY_CHARGING, icon="mdi:ev-station", # device class power: On means power detected, Off means no power - value_fn=lambda s: cast(bool, s.charging_status == ChargingState.CHARGING), - attr_fn=lambda s, u: { - "charging_status": s.charging_status.value, - "last_charging_end_result": s.last_charging_end_result, + value_fn=lambda v: v.fuel_and_battery.charging_status == ChargingState.CHARGING, + attr_fn=lambda v, u: { + "charging_status": str(v.fuel_and_battery.charging_status), }, ), BMWBinarySensorEntityDescription( @@ -166,8 +156,7 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = ( name="Connection status", device_class=BinarySensorDeviceClass.PLUG, icon="mdi:car-electric", - value_fn=lambda s: cast(str, s.connection_status) == "CONNECTED", - attr_fn=lambda s, u: {"connection_status": s.connection_status}, + value_fn=lambda v: v.fuel_and_battery.is_charger_connected, ), ) @@ -177,11 +166,11 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the BMW ConnectedDrive binary sensors from config entry.""" + """Set up the BMW binary sensors from config entry.""" coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] entities = [ - BMWConnectedDriveSensor(coordinator, vehicle, description, hass.config.units) + BMWBinarySensor(coordinator, vehicle, description, hass.config.units) for vehicle in coordinator.account.vehicles for description in SENSOR_TYPES if description.key in vehicle.available_attributes @@ -189,7 +178,7 @@ async def async_setup_entry( async_add_entities(entities) -class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity): +class BMWBinarySensor(BMWBaseEntity, BinarySensorEntity): """Representation of a BMW vehicle binary sensor.""" entity_description: BMWBinarySensorEntityDescription @@ -197,7 +186,7 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity): def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, description: BMWBinarySensorEntityDescription, unit_system: UnitSystem, ) -> None: @@ -217,14 +206,12 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity): self.entity_description.key, self.vehicle.name, ) - vehicle_state = self.vehicle.status - - self._attr_is_on = self.entity_description.value_fn(vehicle_state) + self._attr_is_on = self.entity_description.value_fn(self.vehicle) if self.entity_description.attr_fn: self._attr_extra_state_attributes = dict( self._attrs, - **self.entity_description.attr_fn(vehicle_state, self._unit_system), + **self.entity_description.attr_fn(self.vehicle, self._unit_system), ) super()._handle_coordinator_update() diff --git a/homeassistant/components/bmw_connected_drive/button.py b/homeassistant/components/bmw_connected_drive/button.py index 254fbebfdac..9cec9a73ce7 100644 --- a/homeassistant/components/bmw_connected_drive/button.py +++ b/homeassistant/components/bmw_connected_drive/button.py @@ -1,19 +1,20 @@ -"""Support for BMW connected drive button entities.""" +"""Support for MyBMW button entities.""" from __future__ import annotations from collections.abc import Callable, Coroutine from dataclasses import dataclass import logging -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any -from bimmer_connected.vehicle import ConnectedDriveVehicle +from bimmer_connected.vehicle import MyBMWVehicle +from bimmer_connected.vehicle.remote_services import RemoteServiceStatus from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BMWConnectedDriveBaseEntity +from . import BMWBaseEntity from .const import DOMAIN if TYPE_CHECKING: @@ -27,7 +28,9 @@ class BMWButtonEntityDescription(ButtonEntityDescription): """Class describing BMW button entities.""" enabled_when_read_only: bool = False - remote_function: str | None = None + remote_function: Callable[ + [MyBMWVehicle], Coroutine[Any, Any, RemoteServiceStatus] + ] | None = None account_function: Callable[[BMWDataUpdateCoordinator], Coroutine] | None = None @@ -36,31 +39,31 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = ( key="light_flash", icon="mdi:car-light-alert", name="Flash Lights", - remote_function="trigger_remote_light_flash", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_light_flash(), ), BMWButtonEntityDescription( key="sound_horn", icon="mdi:bullhorn", name="Sound Horn", - remote_function="trigger_remote_horn", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_horn(), ), BMWButtonEntityDescription( key="activate_air_conditioning", icon="mdi:hvac", name="Activate Air Conditioning", - remote_function="trigger_remote_air_conditioning", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(), ), BMWButtonEntityDescription( key="deactivate_air_conditioning", icon="mdi:hvac-off", name="Deactivate Air Conditioning", - remote_function="trigger_remote_air_conditioning_stop", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(), ), BMWButtonEntityDescription( key="find_vehicle", icon="mdi:crosshairs-question", name="Find Vehicle", - remote_function="trigger_remote_vehicle_finder", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_vehicle_finder(), ), BMWButtonEntityDescription( key="refresh", @@ -77,7 +80,7 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the BMW ConnectedDrive buttons from config entry.""" + """Set up the BMW buttons from config entry.""" coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] entities: list[BMWButton] = [] @@ -95,15 +98,15 @@ async def async_setup_entry( async_add_entities(entities) -class BMWButton(BMWConnectedDriveBaseEntity, ButtonEntity): - """Representation of a BMW Connected Drive button.""" +class BMWButton(BMWBaseEntity, ButtonEntity): + """Representation of a MyBMW button.""" entity_description: BMWButtonEntityDescription def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, description: BMWButtonEntityDescription, ) -> None: """Initialize BMW vehicle sensor.""" @@ -116,12 +119,7 @@ class BMWButton(BMWConnectedDriveBaseEntity, ButtonEntity): async def async_press(self) -> None: """Press the button.""" if self.entity_description.remote_function: - await self.hass.async_add_executor_job( - getattr( - self.vehicle.remote_services, - self.entity_description.remote_function, - ) - ) + await self.entity_description.remote_function(self.vehicle) elif self.entity_description.account_function: _LOGGER.warning( "The 'Refresh from cloud' button is deprecated. Use the 'homeassistant.update_entity' " diff --git a/homeassistant/components/bmw_connected_drive/config_flow.py b/homeassistant/components/bmw_connected_drive/config_flow.py index fec25390ff4..c07be4c8849 100644 --- a/homeassistant/components/bmw_connected_drive/config_flow.py +++ b/homeassistant/components/bmw_connected_drive/config_flow.py @@ -3,8 +3,9 @@ from __future__ import annotations from typing import Any -from bimmer_connected.account import ConnectedDriveAccount -from bimmer_connected.country_selector import get_region_from_name +from bimmer_connected.account import MyBMWAccount +from bimmer_connected.api.regions import get_region_from_name +from httpx import HTTPError import voluptuous as vol from homeassistant import config_entries, core, exceptions @@ -31,22 +32,23 @@ async def validate_input( Data has the keys from DATA_SCHEMA with values provided by the user. """ + account = MyBMWAccount( + data[CONF_USERNAME], + data[CONF_PASSWORD], + get_region_from_name(data[CONF_REGION]), + ) + try: - await hass.async_add_executor_job( - ConnectedDriveAccount, - data[CONF_USERNAME], - data[CONF_PASSWORD], - get_region_from_name(data[CONF_REGION]), - ) - except OSError as ex: + await account.get_vehicles() + except HTTPError as ex: raise CannotConnect from ex # Return info that you want to store in the config entry. return {"title": f"{data[CONF_USERNAME]}{data.get(CONF_SOURCE, '')}"} -class BMWConnectedDriveConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): - """Handle a config flow for BMW ConnectedDrive.""" +class BMWConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for MyBMW.""" VERSION = 1 @@ -78,16 +80,16 @@ class BMWConnectedDriveConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @callback def async_get_options_flow( config_entry: config_entries.ConfigEntry, - ) -> BMWConnectedDriveOptionsFlow: - """Return a BWM ConnectedDrive option flow.""" - return BMWConnectedDriveOptionsFlow(config_entry) + ) -> BMWOptionsFlow: + """Return a MyBMW option flow.""" + return BMWOptionsFlow(config_entry) -class BMWConnectedDriveOptionsFlow(config_entries.OptionsFlow): - """Handle a option flow for BMW ConnectedDrive.""" +class BMWOptionsFlow(config_entries.OptionsFlow): + """Handle a option flow for MyBMW.""" def __init__(self, config_entry: config_entries.ConfigEntry) -> None: - """Initialize BMW ConnectedDrive option flow.""" + """Initialize MyBMW option flow.""" self.config_entry = config_entry self.options = dict(config_entry.options) @@ -102,6 +104,16 @@ class BMWConnectedDriveOptionsFlow(config_entries.OptionsFlow): ) -> FlowResult: """Handle the initial step.""" if user_input is not None: + # Manually update & reload the config entry after options change. + # Required as each successful login will store the latest refresh_token + # using async_update_entry, which would otherwise trigger a full reload + # if the options would be refreshed using a listener. + changed = self.hass.config_entries.async_update_entry( + self.config_entry, + options=user_input, + ) + if changed: + await self.hass.config_entries.async_reload(self.config_entry.entry_id) return self.async_create_entry(title="", data=user_input) return self.async_show_form( step_id="account_options", diff --git a/homeassistant/components/bmw_connected_drive/const.py b/homeassistant/components/bmw_connected_drive/const.py index a2082c0bede..6a8f82ae22d 100644 --- a/homeassistant/components/bmw_connected_drive/const.py +++ b/homeassistant/components/bmw_connected_drive/const.py @@ -1,4 +1,4 @@ -"""Const file for the BMW Connected Drive integration.""" +"""Const file for the MyBMW integration.""" from homeassistant.const import ( LENGTH_KILOMETERS, LENGTH_MILES, @@ -7,7 +7,7 @@ from homeassistant.const import ( ) DOMAIN = "bmw_connected_drive" -ATTRIBUTION = "Data provided by BMW Connected Drive" +ATTRIBUTION = "Data provided by MyBMW" ATTR_DIRECTION = "direction" ATTR_VIN = "vin" @@ -15,6 +15,7 @@ ATTR_VIN = "vin" CONF_ALLOWED_REGIONS = ["china", "north_america", "rest_of_world"] CONF_READ_ONLY = "read_only" CONF_ACCOUNT = "account" +CONF_REFRESH_TOKEN = "refresh_token" DATA_HASS_CONFIG = "hass_config" diff --git a/homeassistant/components/bmw_connected_drive/coordinator.py b/homeassistant/components/bmw_connected_drive/coordinator.py index a02b4bdd27c..cff532ae3cb 100644 --- a/homeassistant/components/bmw_connected_drive/coordinator.py +++ b/homeassistant/components/bmw_connected_drive/coordinator.py @@ -4,14 +4,17 @@ from __future__ import annotations from datetime import timedelta import logging -import async_timeout -from bimmer_connected.account import ConnectedDriveAccount -from bimmer_connected.country_selector import get_region_from_name +from bimmer_connected.account import MyBMWAccount +from bimmer_connected.api.regions import get_region_from_name +from bimmer_connected.vehicle.models import GPSPosition +from httpx import HTTPError, TimeoutException +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import DOMAIN +from .const import CONF_READ_ONLY, CONF_REFRESH_TOKEN, DOMAIN SCAN_INTERVAL = timedelta(seconds=300) _LOGGER = logging.getLogger(__name__) @@ -20,53 +23,56 @@ _LOGGER = logging.getLogger(__name__) class BMWDataUpdateCoordinator(DataUpdateCoordinator): """Class to manage fetching BMW data.""" - account: ConnectedDriveAccount + account: MyBMWAccount - def __init__( - self, - hass: HomeAssistant, - *, - username: str, - password: str, - region: str, - read_only: bool = False, - ) -> None: + def __init__(self, hass: HomeAssistant, *, entry: ConfigEntry) -> None: """Initialize account-wide BMW data updater.""" - # Storing username & password in coordinator is needed until a new library version - # that does not do blocking IO on init. - self._username = username - self._password = password - self._region = get_region_from_name(region) + self.account = MyBMWAccount( + entry.data[CONF_USERNAME], + entry.data[CONF_PASSWORD], + get_region_from_name(entry.data[CONF_REGION]), + observer_position=GPSPosition(hass.config.latitude, hass.config.longitude), + ) + self.read_only = entry.options[CONF_READ_ONLY] + self._entry = entry - self.account = None - self.read_only = read_only + if CONF_REFRESH_TOKEN in entry.data: + self.account.set_refresh_token(entry.data[CONF_REFRESH_TOKEN]) super().__init__( hass, _LOGGER, - name=f"{DOMAIN}-{username}", + name=f"{DOMAIN}-{entry.data['username']}", update_interval=SCAN_INTERVAL, ) async def _async_update_data(self) -> None: """Fetch data from BMW.""" + old_refresh_token = self.account.refresh_token + try: - async with async_timeout.timeout(15): - if isinstance(self.account, ConnectedDriveAccount): - # pylint: disable=protected-access - await self.hass.async_add_executor_job(self.account._get_vehicles) - else: - self.account = await self.hass.async_add_executor_job( - ConnectedDriveAccount, - self._username, - self._password, - self._region, - ) - self.account.set_observer_position( - self.hass.config.latitude, self.hass.config.longitude - ) - except OSError as err: - raise UpdateFailed(f"Error communicating with API: {err}") from err + await self.account.get_vehicles() + except (HTTPError, TimeoutException) as err: + self._update_config_entry_refresh_token(None) + raise UpdateFailed(f"Error communicating with BMW API: {err}") from err + + if self.account.refresh_token != old_refresh_token: + self._update_config_entry_refresh_token(self.account.refresh_token) + _LOGGER.debug( + "bimmer_connected: refresh token %s > %s", + old_refresh_token, + self.account.refresh_token, + ) + + def _update_config_entry_refresh_token(self, refresh_token: str | None) -> None: + """Update or delete the refresh_token in the Config Entry.""" + data = { + **self._entry.data, + CONF_REFRESH_TOKEN: refresh_token, + } + if not refresh_token: + data.pop(CONF_REFRESH_TOKEN) + self.hass.config_entries.async_update_entry(self._entry, data=data) def notify_listeners(self) -> None: """Notify all listeners to refresh HA state machine.""" diff --git a/homeassistant/components/bmw_connected_drive/device_tracker.py b/homeassistant/components/bmw_connected_drive/device_tracker.py index b1fa429f5b9..dc71100455d 100644 --- a/homeassistant/components/bmw_connected_drive/device_tracker.py +++ b/homeassistant/components/bmw_connected_drive/device_tracker.py @@ -1,10 +1,10 @@ -"""Device tracker for BMW Connected Drive vehicles.""" +"""Device tracker for MyBMW vehicles.""" from __future__ import annotations import logging from typing import Literal -from bimmer_connected.vehicle import ConnectedDriveVehicle +from bimmer_connected.vehicle import MyBMWVehicle from homeassistant.components.device_tracker import SOURCE_TYPE_GPS from homeassistant.components.device_tracker.config_entry import TrackerEntity @@ -12,7 +12,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BMWConnectedDriveBaseEntity +from . import BMWBaseEntity from .const import ATTR_DIRECTION, DOMAIN from .coordinator import BMWDataUpdateCoordinator @@ -24,7 +24,7 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the BMW ConnectedDrive tracker from config entry.""" + """Set up the MyBMW tracker from config entry.""" coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] entities: list[BMWDeviceTracker] = [] @@ -39,8 +39,8 @@ async def async_setup_entry( async_add_entities(entities) -class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity): - """BMW Connected Drive device tracker.""" +class BMWDeviceTracker(BMWBaseEntity, TrackerEntity): + """MyBMW device tracker.""" _attr_force_update = False _attr_icon = "mdi:car" @@ -48,7 +48,7 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity): def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, ) -> None: """Initialize the Tracker.""" super().__init__(coordinator, vehicle) @@ -59,13 +59,13 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity): @property def extra_state_attributes(self) -> dict: """Return entity specific state attributes.""" - return dict(self._attrs, **{ATTR_DIRECTION: self.vehicle.status.gps_heading}) + return {**self._attrs, ATTR_DIRECTION: self.vehicle.vehicle_location.heading} @property def latitude(self) -> float | None: """Return latitude value of the device.""" return ( - self.vehicle.status.gps_position[0] + self.vehicle.vehicle_location.location[0] if self.vehicle.is_vehicle_tracking_enabled else None ) @@ -74,7 +74,7 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity): def longitude(self) -> float | None: """Return longitude value of the device.""" return ( - self.vehicle.status.gps_position[1] + self.vehicle.vehicle_location.location[1] if self.vehicle.is_vehicle_tracking_enabled else None ) diff --git a/homeassistant/components/bmw_connected_drive/lock.py b/homeassistant/components/bmw_connected_drive/lock.py index a395c80ebcc..0c2c5a1e832 100644 --- a/homeassistant/components/bmw_connected_drive/lock.py +++ b/homeassistant/components/bmw_connected_drive/lock.py @@ -4,15 +4,15 @@ from __future__ import annotations import logging from typing import Any -from bimmer_connected.vehicle import ConnectedDriveVehicle -from bimmer_connected.vehicle_status import LockState +from bimmer_connected.vehicle import MyBMWVehicle +from bimmer_connected.vehicle.doors_windows import LockState from homeassistant.components.lock import LockEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from . import BMWConnectedDriveBaseEntity +from . import BMWBaseEntity from .const import DOMAIN from .coordinator import BMWDataUpdateCoordinator @@ -25,7 +25,7 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the BMW ConnectedDrive binary sensors from config entry.""" + """Set up the MyBMW lock from config entry.""" coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] entities: list[BMWLock] = [] @@ -36,13 +36,13 @@ async def async_setup_entry( async_add_entities(entities) -class BMWLock(BMWConnectedDriveBaseEntity, LockEntity): - """Representation of a BMW vehicle lock.""" +class BMWLock(BMWBaseEntity, LockEntity): + """Representation of a MyBMW vehicle lock.""" def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, attribute: str, sensor_name: str, ) -> None: @@ -55,7 +55,7 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity): self._sensor_name = sensor_name self.door_lock_state_available = DOOR_LOCK_STATE in vehicle.available_attributes - def lock(self, **kwargs: Any) -> None: + async def async_lock(self, **kwargs: Any) -> None: """Lock the car.""" _LOGGER.debug("%s: locking doors", self.vehicle.name) # Only update the HA state machine if the vehicle reliably reports its lock state @@ -63,10 +63,10 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity): # Optimistic state set here because it takes some time before the # update callback response self._attr_is_locked = True - self.schedule_update_ha_state() - self.vehicle.remote_services.trigger_remote_door_lock() + self.async_write_ha_state() + await self.vehicle.remote_services.trigger_remote_door_lock() - def unlock(self, **kwargs: Any) -> None: + async def async_unlock(self, **kwargs: Any) -> None: """Unlock the car.""" _LOGGER.debug("%s: unlocking doors", self.vehicle.name) # Only update the HA state machine if the vehicle reliably reports its lock state @@ -74,8 +74,8 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity): # Optimistic state set here because it takes some time before the # update callback response self._attr_is_locked = False - self.schedule_update_ha_state() - self.vehicle.remote_services.trigger_remote_door_unlock() + self.async_write_ha_state() + await self.vehicle.remote_services.trigger_remote_door_unlock() @callback def _handle_coordinator_update(self) -> None: @@ -83,16 +83,14 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity): _LOGGER.debug("Updating lock data of %s", self.vehicle.name) # Only update the HA state machine if the vehicle reliably reports its lock state if self.door_lock_state_available: - vehicle_state = self.vehicle.status - self._attr_is_locked = vehicle_state.door_lock_state in { + self._attr_is_locked = self.vehicle.doors_and_windows.door_lock_state in { LockState.LOCKED, LockState.SECURED, } self._attr_extra_state_attributes = dict( self._attrs, **{ - "door_lock_state": vehicle_state.door_lock_state.value, - "last_update_reason": vehicle_state.last_update_reason, + "door_lock_state": self.vehicle.doors_and_windows.door_lock_state.value, }, ) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index 2d1b5e43984..d41a87ef2c1 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.8.12"], + "requirements": ["bimmer_connected==0.9.0"], "codeowners": ["@gerard33", "@rikroe"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/bmw_connected_drive/notify.py b/homeassistant/components/bmw_connected_drive/notify.py index 42e9c834459..14f6c94dff6 100644 --- a/homeassistant/components/bmw_connected_drive/notify.py +++ b/homeassistant/components/bmw_connected_drive/notify.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging from typing import Any, cast -from bimmer_connected.vehicle import ConnectedDriveVehicle +from bimmer_connected.vehicle import MyBMWVehicle from homeassistant.components.notify import ( ATTR_DATA, @@ -52,14 +52,14 @@ def get_service( class BMWNotificationService(BaseNotificationService): """Send Notifications to BMW.""" - def __init__(self, targets: dict[str, ConnectedDriveVehicle]) -> None: + def __init__(self, targets: dict[str, MyBMWVehicle]) -> None: """Set up the notification service.""" - self.targets: dict[str, ConnectedDriveVehicle] = targets + self.targets: dict[str, MyBMWVehicle] = targets def send_message(self, message: str = "", **kwargs: Any) -> None: """Send a message or POI to the car.""" for vehicle in kwargs[ATTR_TARGET]: - vehicle = cast(ConnectedDriveVehicle, vehicle) + vehicle = cast(MyBMWVehicle, vehicle) _LOGGER.debug("Sending message to %s", vehicle.name) # Extract params from data dict diff --git a/homeassistant/components/bmw_connected_drive/sensor.py b/homeassistant/components/bmw_connected_drive/sensor.py index 4a928870eb2..3021e180158 100644 --- a/homeassistant/components/bmw_connected_drive/sensor.py +++ b/homeassistant/components/bmw_connected_drive/sensor.py @@ -1,4 +1,4 @@ -"""Support for reading vehicle status from BMW connected drive portal.""" +"""Support for reading vehicle status from MyBMW portal.""" from __future__ import annotations from collections.abc import Callable @@ -6,7 +6,8 @@ from dataclasses import dataclass import logging from typing import cast -from bimmer_connected.vehicle import ConnectedDriveVehicle +from bimmer_connected.vehicle import MyBMWVehicle +from bimmer_connected.vehicle.models import ValueWithUnit from homeassistant.components.sensor import ( SensorDeviceClass, @@ -27,7 +28,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.util.unit_system import UnitSystem -from . import BMWConnectedDriveBaseEntity +from . import BMWBaseEntity from .const import DOMAIN, UNIT_MAP from .coordinator import BMWDataUpdateCoordinator @@ -38,44 +39,54 @@ _LOGGER = logging.getLogger(__name__) class BMWSensorEntityDescription(SensorEntityDescription): """Describes BMW sensor entity.""" + key_class: str | None = None unit_metric: str | None = None unit_imperial: str | None = None value: Callable = lambda x, y: x def convert_and_round( - state: tuple, + state: ValueWithUnit, converter: Callable[[float | None, str], float], precision: int, ) -> float | None: - """Safely convert and round a value from a Tuple[value, unit].""" - if state[0] is None: - return None - return round(converter(state[0], UNIT_MAP.get(state[1], state[1])), precision) + """Safely convert and round a value from ValueWithUnit.""" + if state.value and state.unit: + return round( + converter(state.value, UNIT_MAP.get(state.unit, state.unit)), precision + ) + if state.value: + return state.value + return None SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = { # --- Generic --- "charging_start_time": BMWSensorEntityDescription( key="charging_start_time", + key_class="fuel_and_battery", device_class=SensorDeviceClass.TIMESTAMP, entity_registry_enabled_default=False, ), "charging_end_time": BMWSensorEntityDescription( key="charging_end_time", + key_class="fuel_and_battery", device_class=SensorDeviceClass.TIMESTAMP, ), "charging_time_label": BMWSensorEntityDescription( key="charging_time_label", + key_class="fuel_and_battery", entity_registry_enabled_default=False, ), "charging_status": BMWSensorEntityDescription( key="charging_status", + key_class="fuel_and_battery", icon="mdi:ev-station", value=lambda x, y: x.value, ), - "charging_level_hv": BMWSensorEntityDescription( - key="charging_level_hv", + "remaining_battery_percent": BMWSensorEntityDescription( + key="remaining_battery_percent", + key_class="fuel_and_battery", unit_metric=PERCENTAGE, unit_imperial=PERCENTAGE, device_class=SensorDeviceClass.BATTERY, @@ -90,6 +101,7 @@ SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = { ), "remaining_range_total": BMWSensorEntityDescription( key="remaining_range_total", + key_class="fuel_and_battery", icon="mdi:map-marker-distance", unit_metric=LENGTH_KILOMETERS, unit_imperial=LENGTH_MILES, @@ -97,6 +109,7 @@ SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = { ), "remaining_range_electric": BMWSensorEntityDescription( key="remaining_range_electric", + key_class="fuel_and_battery", icon="mdi:map-marker-distance", unit_metric=LENGTH_KILOMETERS, unit_imperial=LENGTH_MILES, @@ -104,6 +117,7 @@ SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = { ), "remaining_range_fuel": BMWSensorEntityDescription( key="remaining_range_fuel", + key_class="fuel_and_battery", icon="mdi:map-marker-distance", unit_metric=LENGTH_KILOMETERS, unit_imperial=LENGTH_MILES, @@ -111,13 +125,15 @@ SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = { ), "remaining_fuel": BMWSensorEntityDescription( key="remaining_fuel", + key_class="fuel_and_battery", icon="mdi:gas-station", unit_metric=VOLUME_LITERS, unit_imperial=VOLUME_GALLONS, value=lambda x, hass: convert_and_round(x, hass.config.units.volume, 2), ), - "fuel_percent": BMWSensorEntityDescription( - key="fuel_percent", + "remaining_fuel_percent": BMWSensorEntityDescription( + key="remaining_fuel_percent", + key_class="fuel_and_battery", icon="mdi:gas-station", unit_metric=PERCENTAGE, unit_imperial=PERCENTAGE, @@ -130,16 +146,16 @@ async def async_setup_entry( config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: - """Set up the BMW ConnectedDrive sensors from config entry.""" + """Set up the MyBMW sensors from config entry.""" unit_system = hass.config.units coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - entities: list[BMWConnectedDriveSensor] = [] + entities: list[BMWSensor] = [] for vehicle in coordinator.account.vehicles: entities.extend( [ - BMWConnectedDriveSensor(coordinator, vehicle, description, unit_system) + BMWSensor(coordinator, vehicle, description, unit_system) for attribute_name in vehicle.available_attributes if (description := SENSOR_TYPES.get(attribute_name)) ] @@ -148,7 +164,7 @@ async def async_setup_entry( async_add_entities(entities) -class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): +class BMWSensor(BMWBaseEntity, SensorEntity): """Representation of a BMW vehicle sensor.""" entity_description: BMWSensorEntityDescription @@ -156,7 +172,7 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): def __init__( self, coordinator: BMWDataUpdateCoordinator, - vehicle: ConnectedDriveVehicle, + vehicle: MyBMWVehicle, description: BMWSensorEntityDescription, unit_system: UnitSystem, ) -> None: @@ -178,7 +194,13 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): _LOGGER.debug( "Updating sensor '%s' of %s", self.entity_description.key, self.vehicle.name ) - state = getattr(self.vehicle.status, self.entity_description.key) + if self.entity_description.key_class is None: + state = getattr(self.vehicle, self.entity_description.key) + else: + state = getattr( + getattr(self.vehicle, self.entity_description.key_class), + self.entity_description.key, + ) self._attr_native_value = cast( StateType, self.entity_description.value(state, self.hass) ) diff --git a/requirements_all.txt b/requirements_all.txt index e9438a330a5..448748afb71 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -397,7 +397,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.8.12 +bimmer_connected==0.9.0 # homeassistant.components.bizkaibus bizkaibus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e072289ac45..33b778e80d0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -312,7 +312,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.8.12 +bimmer_connected==0.9.0 # homeassistant.components.blebox blebox_uniapi==1.3.3 diff --git a/tests/components/bmw_connected_drive/__init__.py b/tests/components/bmw_connected_drive/__init__.py index e1243fe2c0a..4774032b409 100644 --- a/tests/components/bmw_connected_drive/__init__.py +++ b/tests/components/bmw_connected_drive/__init__.py @@ -1 +1,28 @@ """Tests for the for the BMW Connected Drive integration.""" + +from homeassistant import config_entries +from homeassistant.components.bmw_connected_drive.const import ( + CONF_READ_ONLY, + DOMAIN as BMW_DOMAIN, +) +from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME + +FIXTURE_USER_INPUT = { + CONF_USERNAME: "user@domain.com", + CONF_PASSWORD: "p4ssw0rd", + CONF_REGION: "rest_of_world", +} + +FIXTURE_CONFIG_ENTRY = { + "entry_id": "1", + "domain": BMW_DOMAIN, + "title": FIXTURE_USER_INPUT[CONF_USERNAME], + "data": { + CONF_USERNAME: FIXTURE_USER_INPUT[CONF_USERNAME], + CONF_PASSWORD: FIXTURE_USER_INPUT[CONF_PASSWORD], + CONF_REGION: FIXTURE_USER_INPUT[CONF_REGION], + }, + "options": {CONF_READ_ONLY: False}, + "source": config_entries.SOURCE_USER, + "unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}", +} diff --git a/tests/components/bmw_connected_drive/test_config_flow.py b/tests/components/bmw_connected_drive/test_config_flow.py index 644da56a91d..d3c7c64dc99 100644 --- a/tests/components/bmw_connected_drive/test_config_flow.py +++ b/tests/components/bmw_connected_drive/test_config_flow.py @@ -1,35 +1,20 @@ """Test the for the BMW Connected Drive config flow.""" from unittest.mock import patch +from httpx import HTTPError + from homeassistant import config_entries, data_entry_flow from homeassistant.components.bmw_connected_drive.config_flow import DOMAIN from homeassistant.components.bmw_connected_drive.const import CONF_READ_ONLY -from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME +from homeassistant.const import CONF_USERNAME + +from . import FIXTURE_CONFIG_ENTRY, FIXTURE_USER_INPUT from tests.common import MockConfigEntry -FIXTURE_USER_INPUT = { - CONF_USERNAME: "user@domain.com", - CONF_PASSWORD: "p4ssw0rd", - CONF_REGION: "rest_of_world", -} FIXTURE_COMPLETE_ENTRY = FIXTURE_USER_INPUT.copy() FIXTURE_IMPORT_ENTRY = FIXTURE_USER_INPUT.copy() -FIXTURE_CONFIG_ENTRY = { - "entry_id": "1", - "domain": DOMAIN, - "title": FIXTURE_USER_INPUT[CONF_USERNAME], - "data": { - CONF_USERNAME: FIXTURE_USER_INPUT[CONF_USERNAME], - CONF_PASSWORD: FIXTURE_USER_INPUT[CONF_PASSWORD], - CONF_REGION: FIXTURE_USER_INPUT[CONF_REGION], - }, - "options": {CONF_READ_ONLY: False}, - "source": config_entries.SOURCE_USER, - "unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}", -} - async def test_show_form(hass): """Test that the form is served with no input.""" @@ -48,8 +33,8 @@ async def test_connection_error(hass): pass with patch( - "bimmer_connected.account.ConnectedDriveAccount._get_oauth_token", - side_effect=OSError, + "bimmer_connected.api.authentication.MyBMWAuthentication.login", + side_effect=HTTPError("login failure"), ): result = await hass.config_entries.flow.async_init( DOMAIN, @@ -65,7 +50,7 @@ async def test_connection_error(hass): async def test_full_user_flow_implementation(hass): """Test registering an integration and finishing flow works.""" with patch( - "bimmer_connected.account.ConnectedDriveAccount._get_vehicles", + "bimmer_connected.account.MyBMWAccount.get_vehicles", return_value=[], ), patch( "homeassistant.components.bmw_connected_drive.async_setup_entry", @@ -86,7 +71,7 @@ async def test_full_user_flow_implementation(hass): async def test_options_flow_implementation(hass): """Test config flow options.""" with patch( - "bimmer_connected.account.ConnectedDriveAccount._get_vehicles", + "bimmer_connected.account.MyBMWAccount.get_vehicles", return_value=[], ), patch( "homeassistant.components.bmw_connected_drive.async_setup_entry", @@ -104,13 +89,13 @@ async def test_options_flow_implementation(hass): result = await hass.config_entries.options.async_configure( result["flow_id"], - user_input={CONF_READ_ONLY: False}, + user_input={CONF_READ_ONLY: True}, ) await hass.async_block_till_done() assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["data"] == { - CONF_READ_ONLY: False, + CONF_READ_ONLY: True, } assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/bmw_connected_drive/test_init.py b/tests/components/bmw_connected_drive/test_init.py new file mode 100644 index 00000000000..70a64090203 --- /dev/null +++ b/tests/components/bmw_connected_drive/test_init.py @@ -0,0 +1,136 @@ +"""Test Axis component setup process.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.bmw_connected_drive.const import DOMAIN as BMW_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import FIXTURE_CONFIG_ENTRY + +from tests.common import MockConfigEntry + +VIN = "WBYYYYYYYYYYYYYYY" +VEHICLE_NAME = "i3 (+ REX)" +VEHICLE_NAME_SLUG = "i3_rex" + + +@pytest.mark.parametrize( + "entitydata,old_unique_id,new_unique_id", + [ + ( + { + "domain": SENSOR_DOMAIN, + "platform": BMW_DOMAIN, + "unique_id": f"{VIN}-charging_level_hv", + "suggested_object_id": f"{VEHICLE_NAME} charging_level_hv", + "disabled_by": None, + }, + f"{VIN}-charging_level_hv", + f"{VIN}-remaining_battery_percent", + ), + ( + { + "domain": SENSOR_DOMAIN, + "platform": BMW_DOMAIN, + "unique_id": f"{VIN}-remaining_range_total", + "suggested_object_id": f"{VEHICLE_NAME} remaining_range_total", + "disabled_by": None, + }, + f"{VIN}-remaining_range_total", + f"{VIN}-remaining_range_total", + ), + ], +) +async def test_migrate_unique_ids( + hass: HomeAssistant, + entitydata: dict, + old_unique_id: str, + new_unique_id: str, +) -> None: + """Test successful migration of entity unique_ids.""" + mock_config_entry = MockConfigEntry(**FIXTURE_CONFIG_ENTRY) + mock_config_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + entity: er.RegistryEntry = entity_registry.async_get_or_create( + **entitydata, + config_entry=mock_config_entry, + ) + + assert entity.unique_id == old_unique_id + + with patch( + "bimmer_connected.account.MyBMWAccount.get_vehicles", + return_value=[], + ): + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + entity_migrated = entity_registry.async_get(entity.entity_id) + assert entity_migrated + assert entity_migrated.unique_id == new_unique_id + + +@pytest.mark.parametrize( + "entitydata,old_unique_id,new_unique_id", + [ + ( + { + "domain": SENSOR_DOMAIN, + "platform": BMW_DOMAIN, + "unique_id": f"{VIN}-charging_level_hv", + "suggested_object_id": f"{VEHICLE_NAME} charging_level_hv", + "disabled_by": None, + }, + f"{VIN}-charging_level_hv", + f"{VIN}-remaining_battery_percent", + ), + ], +) +async def test_dont_migrate_unique_ids( + hass: HomeAssistant, + entitydata: dict, + old_unique_id: str, + new_unique_id: str, +) -> None: + """Test successful migration of entity unique_ids.""" + mock_config_entry = MockConfigEntry(**FIXTURE_CONFIG_ENTRY) + mock_config_entry.add_to_hass(hass) + + entity_registry = er.async_get(hass) + + # create existing entry with new_unique_id + existing_entity = entity_registry.async_get_or_create( + SENSOR_DOMAIN, + BMW_DOMAIN, + unique_id=f"{VIN}-remaining_battery_percent", + suggested_object_id=f"{VEHICLE_NAME} remaining_battery_percent", + config_entry=mock_config_entry, + ) + + entity: er.RegistryEntry = entity_registry.async_get_or_create( + **entitydata, + config_entry=mock_config_entry, + ) + + assert entity.unique_id == old_unique_id + + with patch( + "bimmer_connected.account.MyBMWAccount.get_vehicles", + return_value=[], + ): + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + entity_migrated = entity_registry.async_get(entity.entity_id) + assert entity_migrated + assert entity_migrated.unique_id == old_unique_id + + entity_not_changed = entity_registry.async_get(existing_entity.entity_id) + assert entity_not_changed + assert entity_not_changed.unique_id == new_unique_id + + assert entity_migrated != entity_not_changed From c4ca10637990ec1123883d7889ba2a382829d5e2 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 24 May 2022 23:16:58 +0200 Subject: [PATCH 788/930] Clean zwave_js api driver access (#72419) --- homeassistant/components/zwave_js/__init__.py | 2 +- homeassistant/components/zwave_js/api.py | 107 ++++++++++++------ homeassistant/components/zwave_js/helpers.py | 17 ++- 3 files changed, 84 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 0c716a39c75..9f928eb7d3d 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -566,7 +566,7 @@ async def setup_driver( # noqa: C901 # If opt in preference hasn't been specified yet, we do nothing, otherwise # we apply the preference if opted_in := entry.data.get(CONF_DATA_COLLECTION_OPTED_IN): - await async_enable_statistics(client) + await async_enable_statistics(driver) elif opted_in is False: await driver.async_disable_statistics() diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 66c497a791f..15d3a68d4a4 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -32,6 +32,7 @@ from zwave_js_server.model.controller import ( ProvisioningEntry, QRProvisioningInformation, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.firmware import ( FirmwareUpdateFinished, FirmwareUpdateProgress, @@ -243,8 +244,17 @@ def async_get_entry(orig_func: Callable) -> Callable: ) return - client = hass.data[DOMAIN][entry_id][DATA_CLIENT] - await orig_func(hass, connection, msg, entry, client) + client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT] + + if client.driver is None: + connection.send_error( + msg[ID], + ERR_NOT_LOADED, + f"Config entry {entry_id} not loaded, driver not ready", + ) + return + + await orig_func(hass, connection, msg, entry, client, client.driver) return async_get_entry_func @@ -373,16 +383,20 @@ async def websocket_network_status( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Get the status of the Z-Wave JS network.""" - controller = client.driver.controller + controller = driver.controller + client_version_info = client.version + assert client_version_info # When client is connected version info is set. + await controller.async_get_state() data = { "client": { "ws_server_url": client.ws_server_url, "state": "connected" if client.connected else "disconnected", - "driver_version": client.version.driver_version, - "server_version": client.version.server_version, + "driver_version": client_version_info.driver_version, + "server_version": client_version_info.server_version, }, "controller": { "home_id": controller.home_id, @@ -404,9 +418,7 @@ async def websocket_network_status( "supports_timers": controller.supports_timers, "is_heal_network_active": controller.is_heal_network_active, "inclusion_state": controller.inclusion_state, - "nodes": [ - node_status(node) for node in client.driver.controller.nodes.values() - ], + "nodes": [node_status(node) for node in driver.controller.nodes.values()], }, } connection.send_result( @@ -533,9 +545,10 @@ async def websocket_add_node( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Add a node to the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY]) force_security = msg.get(FORCE_SECURITY) provisioning = ( @@ -672,13 +685,14 @@ async def websocket_grant_security_classes( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Choose SecurityClass grants as part of S2 inclusion process.""" inclusion_grant = InclusionGrant( [SecurityClass(sec_cls) for sec_cls in msg[SECURITY_CLASSES]], msg[CLIENT_SIDE_AUTH], ) - await client.driver.controller.async_grant_security_classes(inclusion_grant) + await driver.controller.async_grant_security_classes(inclusion_grant) connection.send_result(msg[ID]) @@ -699,9 +713,10 @@ async def websocket_validate_dsk_and_enter_pin( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Validate DSK and enter PIN as part of S2 inclusion process.""" - await client.driver.controller.async_validate_dsk_and_enter_pin(msg[PIN]) + await driver.controller.async_validate_dsk_and_enter_pin(msg[PIN]) connection.send_result(msg[ID]) @@ -728,6 +743,7 @@ async def websocket_provision_smart_start_node( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Pre-provision a smart start node.""" try: @@ -758,7 +774,7 @@ async def websocket_provision_smart_start_node( "QR code version S2 is not supported for this command", ) return - await client.driver.controller.async_provision_smart_start_node(provisioning_info) + await driver.controller.async_provision_smart_start_node(provisioning_info) connection.send_result(msg[ID]) @@ -780,6 +796,7 @@ async def websocket_unprovision_smart_start_node( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Unprovision a smart start node.""" try: @@ -792,7 +809,7 @@ async def websocket_unprovision_smart_start_node( ) return dsk_or_node_id = msg.get(DSK) or msg[NODE_ID] - await client.driver.controller.async_unprovision_smart_start_node(dsk_or_node_id) + await driver.controller.async_unprovision_smart_start_node(dsk_or_node_id) connection.send_result(msg[ID]) @@ -812,11 +829,10 @@ async def websocket_get_provisioning_entries( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Get provisioning entries (entries that have been pre-provisioned).""" - provisioning_entries = ( - await client.driver.controller.async_get_provisioning_entries() - ) + provisioning_entries = await driver.controller.async_get_provisioning_entries() connection.send_result( msg[ID], [dataclasses.asdict(entry) for entry in provisioning_entries] ) @@ -839,6 +855,7 @@ async def websocket_parse_qr_code_string( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Parse a QR Code String and return QRProvisioningInformation dict.""" qr_provisioning_information = await async_parse_qr_code_string( @@ -864,9 +881,10 @@ async def websocket_supports_feature( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Check if controller supports a particular feature.""" - supported = await client.driver.controller.async_supports_feature(msg[FEATURE]) + supported = await driver.controller.async_supports_feature(msg[FEATURE]) connection.send_result( msg[ID], {"supported": supported}, @@ -889,9 +907,10 @@ async def websocket_stop_inclusion( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Cancel adding a node to the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller result = await controller.async_stop_inclusion() connection.send_result( msg[ID], @@ -915,9 +934,10 @@ async def websocket_stop_exclusion( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Cancel removing a node from the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller result = await controller.async_stop_exclusion() connection.send_result( msg[ID], @@ -942,9 +962,10 @@ async def websocket_remove_node( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Remove a node from the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller @callback def async_cleanup() -> None: @@ -1021,9 +1042,10 @@ async def websocket_replace_failed_node( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Replace a failed node with a new node.""" - controller = client.driver.controller + controller = driver.controller node_id = msg[NODE_ID] inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY]) force_security = msg.get(FORCE_SECURITY) @@ -1173,7 +1195,9 @@ async def websocket_remove_failed_node( node: Node, ) -> None: """Remove a failed node from the Z-Wave network.""" - controller = node.client.driver.controller + driver = node.client.driver + assert driver is not None # The node comes from the driver instance. + controller = driver.controller @callback def async_cleanup() -> None: @@ -1217,9 +1241,10 @@ async def websocket_begin_healing_network( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Begin healing the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller result = await controller.async_begin_healing_network() connection.send_result( @@ -1243,9 +1268,10 @@ async def websocket_subscribe_heal_network_progress( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Subscribe to heal Z-Wave network status updates.""" - controller = client.driver.controller + controller = driver.controller @callback def async_cleanup() -> None: @@ -1286,9 +1312,10 @@ async def websocket_stop_healing_network( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Stop healing the Z-Wave network.""" - controller = client.driver.controller + controller = driver.controller result = await controller.async_stop_healing_network() connection.send_result( msg[ID], @@ -1313,7 +1340,10 @@ async def websocket_heal_node( node: Node, ) -> None: """Heal a node on the Z-Wave network.""" - controller = node.client.driver.controller + driver = node.client.driver + assert driver is not None # The node comes from the driver instance. + controller = driver.controller + result = await controller.async_heal_node(node.node_id) connection.send_result( msg[ID], @@ -1540,9 +1570,9 @@ async def websocket_subscribe_log_updates( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Subscribe to log message events from the server.""" - driver = client.driver @callback def async_cleanup() -> None: @@ -1627,9 +1657,10 @@ async def websocket_update_log_config( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Update the driver log config.""" - await client.driver.async_update_log_config(LogConfig(**msg[CONFIG])) + await driver.async_update_log_config(LogConfig(**msg[CONFIG])) connection.send_result( msg[ID], ) @@ -1650,11 +1681,12 @@ async def websocket_get_log_config( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Get log configuration for the Z-Wave JS driver.""" connection.send_result( msg[ID], - dataclasses.asdict(client.driver.log_config), + dataclasses.asdict(driver.log_config), ) @@ -1675,15 +1707,16 @@ async def websocket_update_data_collection_preference( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Update preference for data collection and enable/disable collection.""" opted_in = msg[OPTED_IN] update_data_collection_preference(hass, entry, opted_in) if opted_in: - await async_enable_statistics(client) + await async_enable_statistics(driver) else: - await client.driver.async_disable_statistics() + await driver.async_disable_statistics() connection.send_result( msg[ID], @@ -1706,11 +1739,12 @@ async def websocket_data_collection_status( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Return data collection preference and status.""" result = { OPTED_IN: entry.data.get(CONF_DATA_COLLECTION_OPTED_IN), - ENABLED: await client.driver.async_is_statistics_enabled(), + ENABLED: await driver.async_is_statistics_enabled(), } connection.send_result(msg[ID], result) @@ -1890,9 +1924,10 @@ async def websocket_check_for_config_updates( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Check for config updates.""" - config_update = await client.driver.async_check_for_config_updates() + config_update = await driver.async_check_for_config_updates() connection.send_result( msg[ID], { @@ -1918,9 +1953,10 @@ async def websocket_install_config_update( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Check for config updates.""" - success = await client.driver.async_install_config_update() + success = await driver.async_install_config_update() connection.send_result(msg[ID], success) @@ -1956,6 +1992,7 @@ async def websocket_subscribe_controller_statistics( msg: dict, entry: ConfigEntry, client: Client, + driver: Driver, ) -> None: """Subsribe to the statistics updates for a controller.""" @@ -1979,7 +2016,7 @@ async def websocket_subscribe_controller_statistics( ) ) - controller = client.driver.controller + controller = driver.controller msg[DATA_UNSUBSCRIBE] = unsubs = [ controller.on("statistics updated", forward_stats) diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 68ff0c89b15..0657d8531f3 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -9,6 +9,7 @@ from typing import Any, cast import voluptuous as vol from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import ConfigurationValueType +from zwave_js_server.model.driver import Driver from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import ( ConfigurationValue, @@ -92,10 +93,10 @@ def get_value_of_zwave_value(value: ZwaveValue | None) -> Any | None: return value.value if value else None -async def async_enable_statistics(client: ZwaveClient) -> None: +async def async_enable_statistics(driver: Driver) -> None: """Enable statistics on the driver.""" - await client.driver.async_enable_statistics("Home Assistant", HA_VERSION) - await client.driver.async_enable_error_reporting() + await driver.async_enable_statistics("Home Assistant", HA_VERSION) + await driver.async_enable_error_reporting() @callback @@ -194,7 +195,11 @@ def async_get_node_from_device_id( f"Device {device_id} is not from an existing zwave_js config entry" ) - client = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] + client: ZwaveClient = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] + driver = client.driver + + if driver is None: + raise ValueError("Driver is not ready.") # Get node ID from device identifier, perform some validation, and then get the # node @@ -202,10 +207,10 @@ def async_get_node_from_device_id( node_id = identifiers[1] if identifiers else None - if node_id is None or node_id not in client.driver.controller.nodes: + if node_id is None or node_id not in driver.controller.nodes: raise ValueError(f"Node for device {device_id} can't be found") - return client.driver.controller.nodes[node_id] + return driver.controller.nodes[node_id] @callback From f7475a5bdb9ad505a9edeccc3ed2928526450dee Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 24 May 2022 23:52:07 +0200 Subject: [PATCH 789/930] Clean zwave_js entity driver access (#72427) --- homeassistant/components/zwave_js/__init__.py | 28 ++++++------- .../components/zwave_js/binary_sensor.py | 21 ++++++---- homeassistant/components/zwave_js/button.py | 11 +++-- homeassistant/components/zwave_js/climate.py | 15 ++++--- homeassistant/components/zwave_js/cover.py | 21 ++++++---- homeassistant/components/zwave_js/entity.py | 19 +++++---- homeassistant/components/zwave_js/fan.py | 21 ++++++---- homeassistant/components/zwave_js/helpers.py | 16 +++---- .../components/zwave_js/humidifier.py | 11 +++-- homeassistant/components/zwave_js/light.py | 15 ++++--- homeassistant/components/zwave_js/lock.py | 4 +- homeassistant/components/zwave_js/migrate.py | 8 ++-- homeassistant/components/zwave_js/number.py | 15 ++++--- homeassistant/components/zwave_js/select.py | 21 ++++++---- homeassistant/components/zwave_js/sensor.py | 36 +++++++++------- homeassistant/components/zwave_js/siren.py | 9 ++-- homeassistant/components/zwave_js/switch.py | 15 ++++--- .../components/zwave_js/triggers/event.py | 4 +- .../zwave_js/triggers/value_updated.py | 4 +- tests/components/zwave_js/test_api.py | 7 ++-- tests/components/zwave_js/test_button.py | 5 ++- .../components/zwave_js/test_device_action.py | 32 ++++++++++---- tests/components/zwave_js/test_diagnostics.py | 2 +- tests/components/zwave_js/test_init.py | 8 ++-- tests/components/zwave_js/test_migrate.py | 42 ++++++++++++------- tests/components/zwave_js/test_sensor.py | 5 ++- tests/components/zwave_js/test_services.py | 20 ++++++--- 27 files changed, 250 insertions(+), 165 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 9f928eb7d3d..5c583d8321f 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -124,13 +124,13 @@ def register_node_in_dev_reg( hass: HomeAssistant, entry: ConfigEntry, dev_reg: device_registry.DeviceRegistry, - client: ZwaveClient, + driver: Driver, node: ZwaveNode, remove_device_func: Callable[[device_registry.DeviceEntry], None], ) -> device_registry.DeviceEntry: """Register node in dev reg.""" - device_id = get_device_id(client, node) - device_id_ext = get_device_id_ext(client, node) + device_id = get_device_id(driver, node) + device_id_ext = get_device_id_ext(driver, node) device = dev_reg.async_get_device({device_id}) # Replace the device if it can be determined that this node is not the @@ -281,7 +281,7 @@ async def setup_driver( # noqa: C901 ent_reg, registered_unique_ids[device.id][disc_info.platform], device, - client, + driver, disc_info, ) @@ -316,7 +316,7 @@ async def setup_driver( # noqa: C901 LOGGER.debug("Processing node %s", node) # register (or update) node in device registry device = register_node_in_dev_reg( - hass, entry, dev_reg, client, node, remove_device + hass, entry, dev_reg, driver, node, remove_device ) # We only want to create the defaultdict once, even on reinterviews if device.id not in registered_unique_ids: @@ -384,7 +384,7 @@ async def setup_driver( # noqa: C901 ) # we do submit the node to device registry so user has # some visual feedback that something is (in the process of) being added - register_node_in_dev_reg(hass, entry, dev_reg, client, node, remove_device) + register_node_in_dev_reg(hass, entry, dev_reg, driver, node, remove_device) async def async_on_value_added( value_updates_disc_info: dict[str, ZwaveDiscoveryInfo], value: Value @@ -393,7 +393,7 @@ async def setup_driver( # noqa: C901 # If node isn't ready or a device for this node doesn't already exist, we can # let the node ready event handler perform discovery. If a value has already # been processed, we don't need to do it again - device_id = get_device_id(client, value.node) + device_id = get_device_id(driver, value.node) if ( not value.node.ready or not (device := dev_reg.async_get_device({device_id})) @@ -417,7 +417,7 @@ async def setup_driver( # noqa: C901 node: ZwaveNode = event["node"] replaced: bool = event.get("replaced", False) # grab device in device registry attached to this node - dev_id = get_device_id(client, node) + dev_id = get_device_id(driver, node) device = dev_reg.async_get_device({dev_id}) # We assert because we know the device exists assert device @@ -426,7 +426,7 @@ async def setup_driver( # noqa: C901 async_dispatcher_send( hass, - f"{DOMAIN}_{get_valueless_base_unique_id(client, node)}_remove_entity", + f"{DOMAIN}_{get_valueless_base_unique_id(driver, node)}_remove_entity", ) else: remove_device(device) @@ -434,7 +434,7 @@ async def setup_driver( # noqa: C901 @callback def async_on_value_notification(notification: ValueNotification) -> None: """Relay stateless value notification events from Z-Wave nodes to hass.""" - device = dev_reg.async_get_device({get_device_id(client, notification.node)}) + device = dev_reg.async_get_device({get_device_id(driver, notification.node)}) # We assert because we know the device exists assert device raw_value = value = notification.value @@ -469,7 +469,7 @@ async def setup_driver( # noqa: C901 notification: EntryControlNotification | NotificationNotification | PowerLevelNotification | MultilevelSwitchNotification = event[ "notification" ] - device = dev_reg.async_get_device({get_device_id(client, notification.node)}) + device = dev_reg.async_get_device({get_device_id(driver, notification.node)}) # We assert because we know the device exists assert device event_data = { @@ -533,11 +533,11 @@ async def setup_driver( # noqa: C901 return disc_info = value_updates_disc_info[value.value_id] - device = dev_reg.async_get_device({get_device_id(client, value.node)}) + device = dev_reg.async_get_device({get_device_id(driver, value.node)}) # We assert because we know the device exists assert device - unique_id = get_unique_id(client, disc_info.primary_value.value_id) + unique_id = get_unique_id(driver, disc_info.primary_value.value_id) entity_id = ent_reg.async_get_entity_id(disc_info.platform, DOMAIN, unique_id) raw_value = value_ = value.value @@ -575,7 +575,7 @@ async def setup_driver( # noqa: C901 dev_reg, entry.entry_id ) known_devices = [ - dev_reg.async_get_device({get_device_id(client, node)}) + dev_reg.async_get_device({get_device_id(driver, node)}) for node in driver.controller.nodes.values() ] diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py index 8a86898239a..7e9882377bd 100644 --- a/homeassistant/components/zwave_js/binary_sensor.py +++ b/homeassistant/components/zwave_js/binary_sensor.py @@ -10,6 +10,7 @@ from zwave_js_server.const.command_class.lock import DOOR_STATUS_PROPERTY from zwave_js_server.const.command_class.notification import ( CC_SPECIFIC_NOTIFICATION_TYPE, ) +from zwave_js_server.model.driver import Driver from homeassistant.components.binary_sensor import ( DOMAIN as BINARY_SENSOR_DOMAIN, @@ -267,6 +268,8 @@ async def async_setup_entry( @callback def async_add_binary_sensor(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Binary Sensor.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[BinarySensorEntity] = [] if info.platform_hint == "notification": @@ -298,7 +301,7 @@ async def async_setup_entry( entities.append( ZWaveNotificationBinarySensor( - config_entry, client, info, state_key, notification_description + config_entry, driver, info, state_key, notification_description ) ) elif info.platform_hint == "property" and ( @@ -308,12 +311,12 @@ async def async_setup_entry( ): entities.append( ZWavePropertyBinarySensor( - config_entry, client, info, property_description + config_entry, driver, info, property_description ) ) else: # boolean sensor - entities.append(ZWaveBooleanBinarySensor(config_entry, client, info)) + entities.append(ZWaveBooleanBinarySensor(config_entry, driver, info)) async_add_entities(entities) @@ -332,11 +335,11 @@ class ZWaveBooleanBinarySensor(ZWaveBaseEntity, BinarySensorEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, ) -> None: """Initialize a ZWaveBooleanBinarySensor entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) # Entity class attributes self._attr_name = self.generate_name(include_value_name=True) @@ -359,13 +362,13 @@ class ZWaveNotificationBinarySensor(ZWaveBaseEntity, BinarySensorEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, state_key: str, description: NotificationZWaveJSEntityDescription | None = None, ) -> None: """Initialize a ZWaveNotificationBinarySensor entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.state_key = state_key if description: self.entity_description = description @@ -394,12 +397,12 @@ class ZWavePropertyBinarySensor(ZWaveBaseEntity, BinarySensorEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, description: PropertyZWaveJSEntityDescription, ) -> None: """Initialize a ZWavePropertyBinarySensor entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.entity_description = description self._attr_name = self.generate_name(include_value_name=True) diff --git a/homeassistant/components/zwave_js/button.py b/homeassistant/components/zwave_js/button.py index 49c2a76cccf..cef64f1724a 100644 --- a/homeassistant/components/zwave_js/button.py +++ b/homeassistant/components/zwave_js/button.py @@ -2,6 +2,7 @@ from __future__ import annotations from zwave_js_server.client import Client as ZwaveClient +from zwave_js_server.model.driver import Driver from zwave_js_server.model.node import Node as ZwaveNode from homeassistant.components.button import ButtonEntity @@ -28,7 +29,9 @@ async def async_setup_entry( @callback def async_add_ping_button_entity(node: ZwaveNode) -> None: """Add ping button entity.""" - async_add_entities([ZWaveNodePingButton(client, node)]) + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. + async_add_entities([ZWaveNodePingButton(driver, node)]) config_entry.async_on_unload( async_dispatcher_connect( @@ -45,7 +48,7 @@ class ZWaveNodePingButton(ButtonEntity): _attr_should_poll = False _attr_entity_category = EntityCategory.CONFIG - def __init__(self, client: ZwaveClient, node: ZwaveNode) -> None: + def __init__(self, driver: Driver, node: ZwaveNode) -> None: """Initialize a ping Z-Wave device button entity.""" self.node = node name: str = ( @@ -53,11 +56,11 @@ class ZWaveNodePingButton(ButtonEntity): ) # Entity class attributes self._attr_name = f"{name}: Ping" - self._base_unique_id = get_valueless_base_unique_id(client, node) + self._base_unique_id = get_valueless_base_unique_id(driver, node) self._attr_unique_id = f"{self._base_unique_id}.ping" # device is precreated in main handler self._attr_device_info = DeviceInfo( - identifiers={get_device_id(client, node)}, + identifiers={get_device_id(driver, node)}, ) async def async_poll_value(self, _: bool) -> None: diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 67def698fc2..f721db41d9f 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -17,6 +17,7 @@ from zwave_js_server.const.command_class.thermostat import ( ThermostatOperatingState, ThermostatSetpointType, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.components.climate import ( @@ -103,11 +104,13 @@ async def async_setup_entry( @callback def async_add_climate(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Climate.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "dynamic_current_temp": - entities.append(DynamicCurrentTempClimate(config_entry, client, info)) + entities.append(DynamicCurrentTempClimate(config_entry, driver, info)) else: - entities.append(ZWaveClimate(config_entry, client, info)) + entities.append(ZWaveClimate(config_entry, driver, info)) async_add_entities(entities) @@ -124,10 +127,10 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): """Representation of a Z-Wave climate.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize thermostat.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._hvac_modes: dict[HVACMode, int | None] = {} self._hvac_presets: dict[str, int | None] = {} self._unit_value: ZwaveValue | None = None @@ -479,10 +482,10 @@ class DynamicCurrentTempClimate(ZWaveClimate): """Representation of a thermostat that can dynamically use a different Zwave Value for current temp.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize thermostat.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.data_template = cast( DynamicCurrentTempClimateDataTemplate, self.info.platform_data_template ) diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index c7ba50ee7e7..f9a9990ed77 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -15,6 +15,7 @@ from zwave_js_server.const.command_class.multilevel_switch import ( COVER_OPEN_PROPERTY, COVER_UP_PROPERTY, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.components.cover import ( @@ -51,13 +52,15 @@ async def async_setup_entry( @callback def async_add_cover(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave cover.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "motorized_barrier": - entities.append(ZwaveMotorizedBarrier(config_entry, client, info)) + entities.append(ZwaveMotorizedBarrier(config_entry, driver, info)) elif info.platform_hint == "window_shutter_tilt": - entities.append(ZWaveTiltCover(config_entry, client, info)) + entities.append(ZWaveTiltCover(config_entry, driver, info)) else: - entities.append(ZWaveCover(config_entry, client, info)) + entities.append(ZWaveCover(config_entry, driver, info)) async_add_entities(entities) config_entry.async_on_unload( @@ -105,11 +108,11 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, ) -> None: """Initialize a ZWaveCover entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) # Entity class attributes self._attr_device_class = CoverDeviceClass.WINDOW @@ -188,11 +191,11 @@ class ZWaveTiltCover(ZWaveCover): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, ) -> None: """Initialize a ZWaveCover entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.data_template = cast( CoverTiltDataTemplate, self.info.platform_data_template ) @@ -233,11 +236,11 @@ class ZwaveMotorizedBarrier(ZWaveBaseEntity, CoverEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, ) -> None: """Initialize a ZwaveMotorizedBarrier entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._target_state: ZwaveValue = self.get_zwave_value( TARGET_STATE_PROPERTY, add_to_watched_value_ids=False ) diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index b89b4c9c9de..a4271ac1c02 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -3,8 +3,8 @@ from __future__ import annotations import logging -from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import NodeStatus +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue, get_value_id from homeassistant.config_entries import ConfigEntry @@ -30,11 +30,11 @@ class ZWaveBaseEntity(Entity): _attr_should_poll = False def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a generic Z-Wave device entity.""" self.config_entry = config_entry - self.client = client + self.driver = driver self.info = info # entities requiring additional values, can add extra ids to this list self.watched_value_ids = {self.info.primary_value.value_id} @@ -46,16 +46,14 @@ class ZWaveBaseEntity(Entity): # Entity class attributes self._attr_name = self.generate_name() - self._attr_unique_id = get_unique_id( - self.client, self.info.primary_value.value_id - ) + self._attr_unique_id = get_unique_id(driver, self.info.primary_value.value_id) self._attr_entity_registry_enabled_default = ( self.info.entity_registry_enabled_default ) self._attr_assumed_state = self.info.assumed_state # device is precreated in main handler self._attr_device_info = DeviceInfo( - identifiers={get_device_id(self.client, self.info.node)}, + identifiers={get_device_id(driver, self.info.node)}, ) @callback @@ -145,7 +143,10 @@ class ZWaveBaseEntity(Entity): if item: name += f" - {item}" # append endpoint if > 1 - if self.info.primary_value.endpoint > 1: + if ( + self.info.primary_value.endpoint is not None + and self.info.primary_value.endpoint > 1 + ): name += f" ({self.info.primary_value.endpoint})" return name @@ -154,7 +155,7 @@ class ZWaveBaseEntity(Entity): def available(self) -> bool: """Return entity availability.""" return ( - self.client.connected + self.driver.client.connected and bool(self.info.node.ready) and self.info.node.status != NodeStatus.DEAD ) diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index f571884ac80..eb6d053f958 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -10,6 +10,7 @@ from zwave_js_server.const.command_class.thermostat import ( THERMOSTAT_FAN_OFF_PROPERTY, THERMOSTAT_FAN_STATE_PROPERTY, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.components.fan import ( @@ -53,13 +54,15 @@ async def async_setup_entry( @callback def async_add_fan(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave fan.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "has_fan_value_mapping": - entities.append(ValueMappingZwaveFan(config_entry, client, info)) + entities.append(ValueMappingZwaveFan(config_entry, driver, info)) elif info.platform_hint == "thermostat_fan": - entities.append(ZwaveThermostatFan(config_entry, client, info)) + entities.append(ZwaveThermostatFan(config_entry, driver, info)) else: - entities.append(ZwaveFan(config_entry, client, info)) + entities.append(ZwaveFan(config_entry, driver, info)) async_add_entities(entities) @@ -78,10 +81,10 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity): _attr_supported_features = FanEntityFeature.SET_SPEED def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the fan.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) async def async_set_percentage(self, percentage: int) -> None: @@ -147,10 +150,10 @@ class ValueMappingZwaveFan(ZwaveFan): """A Zwave fan with a value mapping data (e.g., 1-24 is low).""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the fan.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.data_template = cast( FanValueMappingDataTemplate, self.info.platform_data_template ) @@ -300,10 +303,10 @@ class ZwaveThermostatFan(ZWaveBaseEntity, FanEntity): _fan_state: ZwaveValue | None = None def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the thermostat fan.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._fan_mode = self.info.primary_value diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 0657d8531f3..807ae0287eb 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -110,29 +110,29 @@ def update_data_collection_preference( @callback -def get_valueless_base_unique_id(client: ZwaveClient, node: ZwaveNode) -> str: +def get_valueless_base_unique_id(driver: Driver, node: ZwaveNode) -> str: """Return the base unique ID for an entity that is not based on a value.""" - return f"{client.driver.controller.home_id}.{node.node_id}" + return f"{driver.controller.home_id}.{node.node_id}" -def get_unique_id(client: ZwaveClient, value_id: str) -> str: +def get_unique_id(driver: Driver, value_id: str) -> str: """Get unique ID from client and value ID.""" - return f"{client.driver.controller.home_id}.{value_id}" + return f"{driver.controller.home_id}.{value_id}" @callback -def get_device_id(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str]: +def get_device_id(driver: Driver, node: ZwaveNode) -> tuple[str, str]: """Get device registry identifier for Z-Wave node.""" - return (DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}") + return (DOMAIN, f"{driver.controller.home_id}-{node.node_id}") @callback -def get_device_id_ext(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str] | None: +def get_device_id_ext(driver: Driver, node: ZwaveNode) -> tuple[str, str] | None: """Get extended device registry identifier for Z-Wave node.""" if None in (node.manufacturer_id, node.product_type, node.product_id): return None - domain, dev_id = get_device_id(client, node) + domain, dev_id = get_device_id(driver, node) return ( domain, f"{dev_id}-{node.manufacturer_id}:{node.product_type}:{node.product_id}", diff --git a/homeassistant/components/zwave_js/humidifier.py b/homeassistant/components/zwave_js/humidifier.py index 8cf0b6aec7c..5aeb6d0272f 100644 --- a/homeassistant/components/zwave_js/humidifier.py +++ b/homeassistant/components/zwave_js/humidifier.py @@ -11,6 +11,7 @@ from zwave_js_server.const.command_class.humidity_control import ( HumidityControlMode, HumidityControlSetpointType, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.components.humidifier import ( @@ -85,6 +86,8 @@ async def async_setup_entry( @callback def async_add_humidifier(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Humidifier.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if ( @@ -93,7 +96,7 @@ async def async_setup_entry( ): entities.append( ZWaveHumidifier( - config_entry, client, info, HUMIDIFIER_ENTITY_DESCRIPTION + config_entry, driver, info, HUMIDIFIER_ENTITY_DESCRIPTION ) ) @@ -103,7 +106,7 @@ async def async_setup_entry( ): entities.append( ZWaveHumidifier( - config_entry, client, info, DEHUMIDIFIER_ENTITY_DESCRIPTION + config_entry, driver, info, DEHUMIDIFIER_ENTITY_DESCRIPTION ) ) @@ -128,12 +131,12 @@ class ZWaveHumidifier(ZWaveBaseEntity, HumidifierEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, description: ZwaveHumidifierEntityDescription, ) -> None: """Initialize humidifier.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.entity_description = description diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index 534a86f1c86..d026868b418 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -23,6 +23,7 @@ from zwave_js_server.const.command_class.color_switch import ( TARGET_COLOR_PROPERTY, ColorComponent, ) +from zwave_js_server.model.driver import Driver from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -72,11 +73,13 @@ async def async_setup_entry( @callback def async_add_light(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Light.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. if info.platform_hint == "black_is_off": - async_add_entities([ZwaveBlackIsOffLight(config_entry, client, info)]) + async_add_entities([ZwaveBlackIsOffLight(config_entry, driver, info)]) else: - async_add_entities([ZwaveLight(config_entry, client, info)]) + async_add_entities([ZwaveLight(config_entry, driver, info)]) config_entry.async_on_unload( async_dispatcher_connect( @@ -101,10 +104,10 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): """Representation of a Z-Wave light.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the light.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._supports_color = False self._supports_rgbw = False self._supports_color_temp = False @@ -445,10 +448,10 @@ class ZwaveBlackIsOffLight(ZwaveLight): """ def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the light.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._last_color: dict[str, int] | None = None self._supported_color_modes.discard(ColorMode.BRIGHTNESS) diff --git a/homeassistant/components/zwave_js/lock.py b/homeassistant/components/zwave_js/lock.py index 3781821e4c7..e7fbbeb3f99 100644 --- a/homeassistant/components/zwave_js/lock.py +++ b/homeassistant/components/zwave_js/lock.py @@ -61,8 +61,10 @@ async def async_setup_entry( @callback def async_add_lock(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Lock.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] - entities.append(ZWaveLock(config_entry, client, info)) + entities.append(ZWaveLock(config_entry, driver, info)) async_add_entities(entities) diff --git a/homeassistant/components/zwave_js/migrate.py b/homeassistant/components/zwave_js/migrate.py index 1413bf8e5b4..400c2b3cffe 100644 --- a/homeassistant/components/zwave_js/migrate.py +++ b/homeassistant/components/zwave_js/migrate.py @@ -4,7 +4,7 @@ from __future__ import annotations from dataclasses import dataclass import logging -from zwave_js_server.client import Client as ZwaveClient +from zwave_js_server.model.driver import Driver from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.const import STATE_UNAVAILABLE @@ -138,12 +138,12 @@ def async_migrate_discovered_value( ent_reg: EntityRegistry, registered_unique_ids: set[str], device: DeviceEntry, - client: ZwaveClient, + driver: Driver, disc_info: ZwaveDiscoveryInfo, ) -> None: """Migrate unique ID for entity/entities tied to discovered value.""" - new_unique_id = get_unique_id(client, disc_info.primary_value.value_id) + new_unique_id = get_unique_id(driver, disc_info.primary_value.value_id) # On reinterviews, there is no point in going through this logic again for already # discovered values @@ -155,7 +155,7 @@ def async_migrate_discovered_value( # 2021.2.*, 2021.3.0b0, and 2021.3.0 formats old_unique_ids = [ - get_unique_id(client, value_id) + get_unique_id(driver, value_id) for value_id in get_old_value_ids(disc_info.primary_value) ] diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py index fb9266ac071..56a7ce33be6 100644 --- a/homeassistant/components/zwave_js/number.py +++ b/homeassistant/components/zwave_js/number.py @@ -3,6 +3,7 @@ from __future__ import annotations from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import TARGET_VALUE_PROPERTY +from zwave_js_server.model.driver import Driver from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, NumberEntity from homeassistant.config_entries import ConfigEntry @@ -28,11 +29,13 @@ async def async_setup_entry( @callback def async_add_number(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave number entity.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "volume": - entities.append(ZwaveVolumeNumberEntity(config_entry, client, info)) + entities.append(ZwaveVolumeNumberEntity(config_entry, driver, info)) else: - entities.append(ZwaveNumberEntity(config_entry, client, info)) + entities.append(ZwaveNumberEntity(config_entry, driver, info)) async_add_entities(entities) config_entry.async_on_unload( @@ -48,10 +51,10 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity): """Representation of a Z-Wave number entity.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveNumberEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) if self.info.primary_value.metadata.writeable: self._target_value = self.info.primary_value else: @@ -99,10 +102,10 @@ class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity): """Representation of a volume number entity.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveVolumeNumberEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.correction_factor = int( self.info.primary_value.metadata.max - self.info.primary_value.metadata.min ) diff --git a/homeassistant/components/zwave_js/select.py b/homeassistant/components/zwave_js/select.py index 085c694fc0e..f8cb294919e 100644 --- a/homeassistant/components/zwave_js/select.py +++ b/homeassistant/components/zwave_js/select.py @@ -6,6 +6,7 @@ from typing import cast from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import TARGET_VALUE_PROPERTY, CommandClass from zwave_js_server.const.command_class.sound_switch import ToneID +from zwave_js_server.model.driver import Driver from homeassistant.components.select import DOMAIN as SELECT_DOMAIN, SelectEntity from homeassistant.config_entries import ConfigEntry @@ -32,15 +33,17 @@ async def async_setup_entry( @callback def async_add_select(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave select entity.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "Default tone": - entities.append(ZwaveDefaultToneSelectEntity(config_entry, client, info)) + entities.append(ZwaveDefaultToneSelectEntity(config_entry, driver, info)) elif info.platform_hint == "multilevel_switch": entities.append( - ZwaveMultilevelSwitchSelectEntity(config_entry, client, info) + ZwaveMultilevelSwitchSelectEntity(config_entry, driver, info) ) else: - entities.append(ZwaveSelectEntity(config_entry, client, info)) + entities.append(ZwaveSelectEntity(config_entry, driver, info)) async_add_entities(entities) config_entry.async_on_unload( @@ -58,10 +61,10 @@ class ZwaveSelectEntity(ZWaveBaseEntity, SelectEntity): _attr_entity_category = EntityCategory.CONFIG def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveSelectEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) # Entity class attributes self._attr_name = self.generate_name(include_value_name=True) @@ -94,10 +97,10 @@ class ZwaveDefaultToneSelectEntity(ZWaveBaseEntity, SelectEntity): _attr_entity_category = EntityCategory.CONFIG def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveDefaultToneSelectEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._tones_value = self.get_zwave_value( "toneId", command_class=CommandClass.SOUND_SWITCH ) @@ -145,10 +148,10 @@ class ZwaveMultilevelSwitchSelectEntity(ZWaveBaseEntity, SelectEntity): """Representation of a Z-Wave Multilevel Switch CC select entity.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveSelectEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) assert self.info.platform_data_template self._lookup_map = cast( diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index 71e6bc48952..eb5bf778cc0 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -12,6 +12,7 @@ from zwave_js_server.const.command_class.meter import ( RESET_METER_OPTION_TARGET_VALUE, RESET_METER_OPTION_TYPE, ) +from zwave_js_server.model.driver import Driver from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.value import ConfigurationValue from zwave_js_server.util.command_class.meter import get_meter_type @@ -179,6 +180,8 @@ async def async_setup_entry( @callback def async_add_sensor(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Sensor.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_data: @@ -191,13 +194,13 @@ async def async_setup_entry( if info.platform_hint == "string_sensor": entities.append( - ZWaveStringSensor(config_entry, client, info, entity_description) + ZWaveStringSensor(config_entry, driver, info, entity_description) ) elif info.platform_hint == "numeric_sensor": entities.append( ZWaveNumericSensor( config_entry, - client, + driver, info, entity_description, data.unit_of_measurement, @@ -205,17 +208,17 @@ async def async_setup_entry( ) elif info.platform_hint == "list_sensor": entities.append( - ZWaveListSensor(config_entry, client, info, entity_description) + ZWaveListSensor(config_entry, driver, info, entity_description) ) elif info.platform_hint == "config_parameter": entities.append( ZWaveConfigParameterSensor( - config_entry, client, info, entity_description + config_entry, driver, info, entity_description ) ) elif info.platform_hint == "meter": entities.append( - ZWaveMeterSensor(config_entry, client, info, entity_description) + ZWaveMeterSensor(config_entry, driver, info, entity_description) ) else: LOGGER.warning( @@ -230,7 +233,9 @@ async def async_setup_entry( @callback def async_add_node_status_sensor(node: ZwaveNode) -> None: """Add node status sensor.""" - async_add_entities([ZWaveNodeStatusSensor(config_entry, client, node)]) + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. + async_add_entities([ZWaveNodeStatusSensor(config_entry, driver, node)]) config_entry.async_on_unload( async_dispatcher_connect( @@ -265,13 +270,13 @@ class ZwaveSensorBase(ZWaveBaseEntity, SensorEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, entity_description: SensorEntityDescription, unit_of_measurement: str | None = None, ) -> None: """Initialize a ZWaveSensorBase entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self.entity_description = entity_description self._attr_native_unit_of_measurement = unit_of_measurement @@ -370,14 +375,14 @@ class ZWaveListSensor(ZwaveSensorBase): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, entity_description: SensorEntityDescription, unit_of_measurement: str | None = None, ) -> None: """Initialize a ZWaveListSensor entity.""" super().__init__( - config_entry, client, info, entity_description, unit_of_measurement + config_entry, driver, info, entity_description, unit_of_measurement ) # Entity class attributes @@ -414,14 +419,14 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, entity_description: SensorEntityDescription, unit_of_measurement: str | None = None, ) -> None: """Initialize a ZWaveConfigParameterSensor entity.""" super().__init__( - config_entry, client, info, entity_description, unit_of_measurement + config_entry, driver, info, entity_description, unit_of_measurement ) self._primary_value = cast(ConfigurationValue, self.info.primary_value) @@ -466,11 +471,10 @@ class ZWaveNodeStatusSensor(SensorEntity): _attr_entity_category = EntityCategory.DIAGNOSTIC def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, node: ZwaveNode + self, config_entry: ConfigEntry, driver: Driver, node: ZwaveNode ) -> None: """Initialize a generic Z-Wave device entity.""" self.config_entry = config_entry - self.client = client self.node = node name: str = ( self.node.name @@ -479,11 +483,11 @@ class ZWaveNodeStatusSensor(SensorEntity): ) # Entity class attributes self._attr_name = f"{name}: Node Status" - self._base_unique_id = get_valueless_base_unique_id(client, node) + self._base_unique_id = get_valueless_base_unique_id(driver, node) self._attr_unique_id = f"{self._base_unique_id}.node_status" # device is precreated in main handler self._attr_device_info = DeviceInfo( - identifiers={get_device_id(self.client, self.node)}, + identifiers={get_device_id(driver, self.node)}, ) self._attr_native_value: str = node.status.name.lower() diff --git a/homeassistant/components/zwave_js/siren.py b/homeassistant/components/zwave_js/siren.py index e686c5446ca..67e6aa4afb4 100644 --- a/homeassistant/components/zwave_js/siren.py +++ b/homeassistant/components/zwave_js/siren.py @@ -5,6 +5,7 @@ from typing import Any from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const.command_class.sound_switch import ToneID +from zwave_js_server.model.driver import Driver from homeassistant.components.siren import ( DOMAIN as SIREN_DOMAIN, @@ -35,8 +36,10 @@ async def async_setup_entry( @callback def async_add_siren(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave siren entity.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] - entities.append(ZwaveSirenEntity(config_entry, client, info)) + entities.append(ZwaveSirenEntity(config_entry, driver, info)) async_add_entities(entities) config_entry.async_on_unload( @@ -52,10 +55,10 @@ class ZwaveSirenEntity(ZWaveBaseEntity, SirenEntity): """Representation of a Z-Wave siren entity.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize a ZwaveSirenEntity entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) # Entity class attributes self._attr_available_tones = { int(id): val for id, val in self.info.primary_value.metadata.states.items() diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py index 115f90b8e11..52b8f813326 100644 --- a/homeassistant/components/zwave_js/switch.py +++ b/homeassistant/components/zwave_js/switch.py @@ -9,6 +9,7 @@ from zwave_js_server.const import TARGET_VALUE_PROPERTY from zwave_js_server.const.command_class.barrier_operator import ( BarrierEventSignalingSubsystemState, ) +from zwave_js_server.model.driver import Driver from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity from homeassistant.config_entries import ConfigEntry @@ -36,13 +37,15 @@ async def async_setup_entry( @callback def async_add_switch(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Switch.""" + driver = client.driver + assert driver is not None # Driver is ready before platforms are loaded. entities: list[ZWaveBaseEntity] = [] if info.platform_hint == "barrier_event_signaling_state": entities.append( - ZWaveBarrierEventSignalingSwitch(config_entry, client, info) + ZWaveBarrierEventSignalingSwitch(config_entry, driver, info) ) else: - entities.append(ZWaveSwitch(config_entry, client, info)) + entities.append(ZWaveSwitch(config_entry, driver, info)) async_add_entities(entities) @@ -59,10 +62,10 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity): """Representation of a Z-Wave switch.""" def __init__( - self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo + self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo ) -> None: """Initialize the switch.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) @@ -91,11 +94,11 @@ class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity): def __init__( self, config_entry: ConfigEntry, - client: ZwaveClient, + driver: Driver, info: ZwaveDiscoveryInfo, ) -> None: """Initialize a ZWaveBarrierEventSignalingSwitch entity.""" - super().__init__(config_entry, client, info) + super().__init__(config_entry, driver, info) self._state: bool | None = None self._update_state() diff --git a/homeassistant/components/zwave_js/triggers/event.py b/homeassistant/components/zwave_js/triggers/event.py index fd46c89832b..83fd7570ab9 100644 --- a/homeassistant/components/zwave_js/triggers/event.py +++ b/homeassistant/components/zwave_js/triggers/event.py @@ -207,7 +207,9 @@ async def async_attach_trigger( unsubs.append(source.on(event_name, async_on_event)) for node in nodes: - device_identifier = get_device_id(node.client, node) + driver = node.client.driver + assert driver is not None # The node comes from the driver. + device_identifier = get_device_id(driver, node) device = dev_reg.async_get_device({device_identifier}) assert device # We need to store the device for the callback diff --git a/homeassistant/components/zwave_js/triggers/value_updated.py b/homeassistant/components/zwave_js/triggers/value_updated.py index 8a0b287c26b..38a19eaa377 100644 --- a/homeassistant/components/zwave_js/triggers/value_updated.py +++ b/homeassistant/components/zwave_js/triggers/value_updated.py @@ -162,7 +162,9 @@ async def async_attach_trigger( dev_reg = dr.async_get(hass) for node in nodes: - device_identifier = get_device_id(node.client, node) + driver = node.client.driver + assert driver is not None # The node comes from the driver. + device_identifier = get_device_id(driver, node) device = dev_reg.async_get_device({device_identifier}) assert device value_id = get_value_id(node, command_class, property_, endpoint, property_key) diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 3d491b98f93..58c26cd5797 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -78,7 +78,7 @@ from homeassistant.helpers import device_registry as dr def get_device(hass, node): """Get device ID for a node.""" dev_reg = dr.async_get(hass) - device_id = get_device_id(node.client, node) + device_id = get_device_id(node.client.driver, node) return dev_reg.async_get_device({device_id}) @@ -124,11 +124,12 @@ async def test_node_ready( node_data = deepcopy(multisensor_6_state) # Copy to allow modification in tests. node = Node(client, node_data) node.data["ready"] = False - client.driver.controller.nodes[node.node_id] = node + driver = client.driver + driver.controller.nodes[node.node_id] = node dev_reg = dr.async_get(hass) device = dev_reg.async_get_or_create( - config_entry_id=entry.entry_id, identifiers={get_device_id(client, node)} + config_entry_id=entry.entry_id, identifiers={get_device_id(driver, node)} ) await ws_client.send_json( diff --git a/tests/components/zwave_js/test_button.py b/tests/components/zwave_js/test_button.py index 29858e0eb97..5ae5f8e7254 100644 --- a/tests/components/zwave_js/test_button.py +++ b/tests/components/zwave_js/test_button.py @@ -49,11 +49,12 @@ async def test_ping_entity( assert "There is no value to refresh for this entity" in caplog.text # Assert a node ping button entity is not created for the controller - node = client.driver.controller.nodes[1] + driver = client.driver + node = driver.controller.nodes[1] assert node.is_controller_node assert ( async_get(hass).async_get_entity_id( - DOMAIN, "sensor", f"{get_valueless_base_unique_id(client, node)}.ping" + DOMAIN, "sensor", f"{get_valueless_base_unique_id(driver, node)}.ping" ) is None ) diff --git a/tests/components/zwave_js/test_device_action.py b/tests/components/zwave_js/test_device_action.py index b8fa43d9cec..ad9d61b3f33 100644 --- a/tests/components/zwave_js/test_device_action.py +++ b/tests/components/zwave_js/test_device_action.py @@ -30,7 +30,9 @@ async def test_get_actions( """Test we get the expected actions from a zwave_js node.""" node = lock_schlage_be469 dev_reg = device_registry.async_get(hass) - device = dev_reg.async_get_device({get_device_id(client, node)}) + driver = client.driver + assert driver + device = dev_reg.async_get_device({get_device_id(driver, node)}) assert device expected_actions = [ { @@ -99,7 +101,9 @@ async def test_get_actions_meter( """Test we get the expected meter actions from a zwave_js node.""" node = aeon_smart_switch_6 dev_reg = device_registry.async_get(hass) - device = dev_reg.async_get_device({get_device_id(client, node)}) + driver = client.driver + assert driver + device = dev_reg.async_get_device({get_device_id(driver, node)}) assert device actions = await async_get_device_automations( hass, DeviceAutomationType.ACTION, device.id @@ -116,7 +120,9 @@ async def test_actions( ) -> None: """Test actions.""" node = climate_radio_thermostat_ct100_plus - device_id = get_device_id(client, node) + driver = client.driver + assert driver + device_id = get_device_id(driver, node) dev_reg = device_registry.async_get(hass) device = dev_reg.async_get_device({device_id}) assert device @@ -236,7 +242,9 @@ async def test_actions_multiple_calls( ) -> None: """Test actions can be called multiple times and still work.""" node = climate_radio_thermostat_ct100_plus - device_id = get_device_id(client, node) + driver = client.driver + assert driver + device_id = get_device_id(driver, node) dev_reg = device_registry.async_get(hass) device = dev_reg.async_get_device({device_id}) assert device @@ -281,7 +289,9 @@ async def test_lock_actions( ) -> None: """Test actions for locks.""" node = lock_schlage_be469 - device_id = get_device_id(client, node) + driver = client.driver + assert driver + device_id = get_device_id(driver, node) dev_reg = device_registry.async_get(hass) device = dev_reg.async_get_device({device_id}) assert device @@ -350,7 +360,9 @@ async def test_reset_meter_action( ) -> None: """Test reset_meter action.""" node = aeon_smart_switch_6 - device_id = get_device_id(client, node) + driver = client.driver + assert driver + device_id = get_device_id(driver, node) dev_reg = device_registry.async_get(hass) device = dev_reg.async_get_device({device_id}) assert device @@ -613,7 +625,9 @@ async def test_get_action_capabilities_meter_triggers( """Test we get the expected action capabilities for meter triggers.""" node = aeon_smart_switch_6 dev_reg = device_registry.async_get(hass) - device = dev_reg.async_get_device({get_device_id(client, node)}) + driver = client.driver + assert driver + device = dev_reg.async_get_device({get_device_id(driver, node)}) assert device capabilities = await device_action.async_get_action_capabilities( hass, @@ -669,7 +683,9 @@ async def test_unavailable_entity_actions( await hass.async_block_till_done() node = lock_schlage_be469 dev_reg = device_registry.async_get(hass) - device = dev_reg.async_get_device({get_device_id(client, node)}) + driver = client.driver + assert driver + device = dev_reg.async_get_device({get_device_id(driver, node)}) assert device actions = await async_get_device_automations( hass, DeviceAutomationType.ACTION, device.id diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 99475cd63ec..8d00c9a2f64 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -51,7 +51,7 @@ async def test_device_diagnostics( ): """Test the device level diagnostics data dump.""" dev_reg = async_get(hass) - device = dev_reg.async_get_device({get_device_id(client, multisensor_6)}) + device = dev_reg.async_get_device({get_device_id(client.driver, multisensor_6)}) assert device # Update a value and ensure it is reflected in the node state diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index 7b3fd773839..a2962261ac3 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -800,8 +800,10 @@ async def test_removed_device( hass, client, climate_radio_thermostat_ct100_plus, lock_schlage_be469, integration ): """Test that the device registry gets updated when a device gets removed.""" + driver = client.driver + assert driver # Verify how many nodes are available - assert len(client.driver.controller.nodes) == 2 + assert len(driver.controller.nodes) == 2 # Make sure there are the same number of devices dev_reg = dr.async_get(hass) @@ -814,7 +816,7 @@ async def test_removed_device( assert len(entity_entries) == 29 # Remove a node and reload the entry - old_node = client.driver.controller.nodes.pop(13) + old_node = driver.controller.nodes.pop(13) await hass.config_entries.async_reload(integration.entry_id) await hass.async_block_till_done() @@ -824,7 +826,7 @@ async def test_removed_device( assert len(device_entries) == 1 entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id) assert len(entity_entries) == 17 - assert dev_reg.async_get_device({get_device_id(client, old_node)}) is None + assert dev_reg.async_get_device({get_device_id(driver, old_node)}) is None async def test_suggested_area(hass, client, eaton_rf9640_dimmer): diff --git a/tests/components/zwave_js/test_migrate.py b/tests/components/zwave_js/test_migrate.py index 37c53700d95..cf1e3ee7eaa 100644 --- a/tests/components/zwave_js/test_migrate.py +++ b/tests/components/zwave_js/test_migrate.py @@ -190,12 +190,14 @@ async def test_old_entity_migration( ): """Test old entity on a different endpoint is migrated to a new one.""" node = Node(client, copy.deepcopy(hank_binary_switch_state)) + driver = client.driver + assert driver ent_reg = er.async_get(hass) dev_reg = dr.async_get(hass) device = dev_reg.async_get_or_create( config_entry_id=integration.entry_id, - identifiers={get_device_id(client, node)}, + identifiers={get_device_id(driver, node)}, manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"], model=hank_binary_switch_state["deviceConfig"]["label"], ) @@ -204,7 +206,7 @@ async def test_old_entity_migration( entity_name = SENSOR_NAME.split(".")[1] # Create entity RegistryEntry using fake endpoint - old_unique_id = f"{client.driver.controller.home_id}.32-50-1-value-66049" + old_unique_id = f"{driver.controller.home_id}.32-50-1-value-66049" entity_entry = ent_reg.async_get_or_create( "sensor", DOMAIN, @@ -221,7 +223,7 @@ async def test_old_entity_migration( for i in range(0, 2): # Add a ready node, unique ID should be migrated event = {"node": node} - client.driver.controller.emit("node added", event) + driver.controller.emit("node added", event) await hass.async_block_till_done() # Check that new RegistryEntry is using new unique ID format @@ -236,12 +238,14 @@ async def test_different_endpoint_migration_status_sensor( ): """Test that the different endpoint migration logic skips over the status sensor.""" node = Node(client, copy.deepcopy(hank_binary_switch_state)) + driver = client.driver + assert driver ent_reg = er.async_get(hass) dev_reg = dr.async_get(hass) device = dev_reg.async_get_or_create( config_entry_id=integration.entry_id, - identifiers={get_device_id(client, node)}, + identifiers={get_device_id(driver, node)}, manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"], model=hank_binary_switch_state["deviceConfig"]["label"], ) @@ -250,7 +254,7 @@ async def test_different_endpoint_migration_status_sensor( entity_name = SENSOR_NAME.split(".")[1] # Create entity RegistryEntry using fake endpoint - old_unique_id = f"{client.driver.controller.home_id}.32.node_status" + old_unique_id = f"{driver.controller.home_id}.32.node_status" entity_entry = ent_reg.async_get_or_create( "sensor", DOMAIN, @@ -267,7 +271,7 @@ async def test_different_endpoint_migration_status_sensor( for i in range(0, 2): # Add a ready node, unique ID should be migrated event = {"node": node} - client.driver.controller.emit("node added", event) + driver.controller.emit("node added", event) await hass.async_block_till_done() # Check that the RegistryEntry is using the same unique ID @@ -280,12 +284,14 @@ async def test_skip_old_entity_migration_for_multiple( ): """Test that multiple entities of the same value but on a different endpoint get skipped.""" node = Node(client, copy.deepcopy(hank_binary_switch_state)) + driver = client.driver + assert driver ent_reg = er.async_get(hass) dev_reg = dr.async_get(hass) device = dev_reg.async_get_or_create( config_entry_id=integration.entry_id, - identifiers={get_device_id(client, node)}, + identifiers={get_device_id(driver, node)}, manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"], model=hank_binary_switch_state["deviceConfig"]["label"], ) @@ -294,7 +300,7 @@ async def test_skip_old_entity_migration_for_multiple( entity_name = SENSOR_NAME.split(".")[1] # Create two entity entrrys using different endpoints - old_unique_id_1 = f"{client.driver.controller.home_id}.32-50-1-value-66049" + old_unique_id_1 = f"{driver.controller.home_id}.32-50-1-value-66049" entity_entry = ent_reg.async_get_or_create( "sensor", DOMAIN, @@ -308,7 +314,7 @@ async def test_skip_old_entity_migration_for_multiple( assert entity_entry.unique_id == old_unique_id_1 # Create two entity entrrys using different endpoints - old_unique_id_2 = f"{client.driver.controller.home_id}.32-50-2-value-66049" + old_unique_id_2 = f"{driver.controller.home_id}.32-50-2-value-66049" entity_entry = ent_reg.async_get_or_create( "sensor", DOMAIN, @@ -322,12 +328,12 @@ async def test_skip_old_entity_migration_for_multiple( assert entity_entry.unique_id == old_unique_id_2 # Add a ready node, unique ID should be migrated event = {"node": node} - client.driver.controller.emit("node added", event) + driver.controller.emit("node added", event) await hass.async_block_till_done() # Check that new RegistryEntry is created using new unique ID format entity_entry = ent_reg.async_get(SENSOR_NAME) - new_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049" + new_unique_id = f"{driver.controller.home_id}.32-50-0-value-66049" assert entity_entry.unique_id == new_unique_id # Check that the old entities stuck around because we skipped the migration step @@ -340,12 +346,14 @@ async def test_old_entity_migration_notification_binary_sensor( ): """Test old entity on a different endpoint is migrated to a new one for a notification binary sensor.""" node = Node(client, copy.deepcopy(multisensor_6_state)) + driver = client.driver + assert driver ent_reg = er.async_get(hass) dev_reg = dr.async_get(hass) device = dev_reg.async_get_or_create( config_entry_id=integration.entry_id, - identifiers={get_device_id(client, node)}, + identifiers={get_device_id(driver, node)}, manufacturer=multisensor_6_state["deviceConfig"]["manufacturer"], model=multisensor_6_state["deviceConfig"]["label"], ) @@ -353,7 +361,9 @@ async def test_old_entity_migration_notification_binary_sensor( entity_name = NOTIFICATION_MOTION_BINARY_SENSOR.split(".")[1] # Create entity RegistryEntry using old unique ID format - old_unique_id = f"{client.driver.controller.home_id}.52-113-1-Home Security-Motion sensor status.8" + old_unique_id = ( + f"{driver.controller.home_id}.52-113-1-Home Security-Motion sensor status.8" + ) entity_entry = ent_reg.async_get_or_create( "binary_sensor", DOMAIN, @@ -370,12 +380,14 @@ async def test_old_entity_migration_notification_binary_sensor( for _ in range(0, 2): # Add a ready node, unique ID should be migrated event = {"node": node} - client.driver.controller.emit("node added", event) + driver.controller.emit("node added", event) await hass.async_block_till_done() # Check that new RegistryEntry is using new unique ID format entity_entry = ent_reg.async_get(NOTIFICATION_MOTION_BINARY_SENSOR) - new_unique_id = f"{client.driver.controller.home_id}.52-113-0-Home Security-Motion sensor status.8" + new_unique_id = ( + f"{driver.controller.home_id}.52-113-0-Home Security-Motion sensor status.8" + ) assert entity_entry.unique_id == new_unique_id assert ( ent_reg.async_get_entity_id("binary_sensor", DOMAIN, old_unique_id) is None diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py index 1d41e145a95..848c4d7b0e5 100644 --- a/tests/components/zwave_js/test_sensor.py +++ b/tests/components/zwave_js/test_sensor.py @@ -205,13 +205,14 @@ async def test_node_status_sensor( assert hass.states.get(NODE_STATUS_ENTITY).state != STATE_UNAVAILABLE # Assert a node status sensor entity is not created for the controller - node = client.driver.controller.nodes[1] + driver = client.driver + node = driver.controller.nodes[1] assert node.is_controller_node assert ( ent_reg.async_get_entity_id( DOMAIN, "sensor", - f"{get_valueless_base_unique_id(client, node)}.node_status", + f"{get_valueless_base_unique_id(driver, node)}.node_status", ) is None ) diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index e04ec569c9f..dec4c3c0a9d 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -1367,11 +1367,11 @@ async def test_multicast_set_value( # Test using area ID dev_reg = async_get_dev_reg(hass) device_eurotronic = dev_reg.async_get_device( - {get_device_id(client, climate_eurotronic_spirit_z)} + {get_device_id(client.driver, climate_eurotronic_spirit_z)} ) assert device_eurotronic device_danfoss = dev_reg.async_get_device( - {get_device_id(client, climate_danfoss_lc_13)} + {get_device_id(client.driver, climate_danfoss_lc_13)} ) assert device_danfoss area_reg = async_get_area_reg(hass) @@ -1655,11 +1655,15 @@ async def test_ping( """Test ping service.""" dev_reg = async_get_dev_reg(hass) device_radio_thermostat = dev_reg.async_get_device( - {get_device_id(client, climate_radio_thermostat_ct100_plus_different_endpoints)} + { + get_device_id( + client.driver, climate_radio_thermostat_ct100_plus_different_endpoints + ) + } ) assert device_radio_thermostat device_danfoss = dev_reg.async_get_device( - {get_device_id(client, climate_danfoss_lc_13)} + {get_device_id(client.driver, climate_danfoss_lc_13)} ) assert device_danfoss @@ -1789,11 +1793,15 @@ async def test_invoke_cc_api( """Test invoke_cc_api service.""" dev_reg = async_get_dev_reg(hass) device_radio_thermostat = dev_reg.async_get_device( - {get_device_id(client, climate_radio_thermostat_ct100_plus_different_endpoints)} + { + get_device_id( + client.driver, climate_radio_thermostat_ct100_plus_different_endpoints + ) + } ) assert device_radio_thermostat device_danfoss = dev_reg.async_get_device( - {get_device_id(client, climate_danfoss_lc_13)} + {get_device_id(client.driver, climate_danfoss_lc_13)} ) assert device_danfoss From 777c9c08ffeedb2c3f1ac44807336bb11b131ac2 Mon Sep 17 00:00:00 2001 From: Jon Benson Date: Wed, 25 May 2022 08:48:54 +1000 Subject: [PATCH 790/930] Update Rainforest Eagle to use eagle100 instead of uEagle (#70177) --- CODEOWNERS | 4 ++-- homeassistant/components/rainforest_eagle/data.py | 2 +- homeassistant/components/rainforest_eagle/manifest.json | 6 +++--- requirements_all.txt | 6 +++--- requirements_test_all.txt | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 887534e3461..9cc32503853 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -821,8 +821,8 @@ build.json @home-assistant/supervisor /homeassistant/components/radiotherm/ @vinnyfuria /homeassistant/components/rainbird/ @konikvranik /homeassistant/components/raincloud/ @vanstinator -/homeassistant/components/rainforest_eagle/ @gtdiehl @jcalbert -/tests/components/rainforest_eagle/ @gtdiehl @jcalbert +/homeassistant/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin +/tests/components/rainforest_eagle/ @gtdiehl @jcalbert @hastarin /homeassistant/components/rainmachine/ @bachya /tests/components/rainmachine/ @bachya /homeassistant/components/random/ @fabaff diff --git a/homeassistant/components/rainforest_eagle/data.py b/homeassistant/components/rainforest_eagle/data.py index 52f40e81d40..c7ef596bb61 100644 --- a/homeassistant/components/rainforest_eagle/data.py +++ b/homeassistant/components/rainforest_eagle/data.py @@ -7,8 +7,8 @@ import logging import aioeagle import aiohttp import async_timeout +from eagle100 import Eagle as Eagle100Reader from requests.exceptions import ConnectionError as ConnectError, HTTPError, Timeout -from uEagle import Eagle as Eagle100Reader from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_TYPE diff --git a/homeassistant/components/rainforest_eagle/manifest.json b/homeassistant/components/rainforest_eagle/manifest.json index b4fbc78f241..b875a7f1ff4 100644 --- a/homeassistant/components/rainforest_eagle/manifest.json +++ b/homeassistant/components/rainforest_eagle/manifest.json @@ -2,8 +2,8 @@ "domain": "rainforest_eagle", "name": "Rainforest Eagle", "documentation": "https://www.home-assistant.io/integrations/rainforest_eagle", - "requirements": ["aioeagle==1.1.0", "uEagle==0.0.2"], - "codeowners": ["@gtdiehl", "@jcalbert"], + "requirements": ["aioeagle==1.1.0", "eagle100==0.1.1"], + "codeowners": ["@gtdiehl", "@jcalbert", "@hastarin"], "iot_class": "local_polling", "config_flow": true, "dhcp": [ @@ -11,5 +11,5 @@ "macaddress": "D8D5B9*" } ], - "loggers": ["aioeagle", "uEagle"] + "loggers": ["aioeagle", "eagle100"] } diff --git a/requirements_all.txt b/requirements_all.txt index 448748afb71..222067c8028 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -571,6 +571,9 @@ dweepy==0.3.0 # homeassistant.components.dynalite dynalite_devices==0.1.46 +# homeassistant.components.rainforest_eagle +eagle100==0.1.1 + # homeassistant.components.ebusd ebusdpy==0.0.17 @@ -2348,9 +2351,6 @@ twilio==6.32.0 # homeassistant.components.twitch twitchAPI==2.5.2 -# homeassistant.components.rainforest_eagle -uEagle==0.0.2 - # homeassistant.components.ukraine_alarm uasiren==0.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 33b778e80d0..423122e079f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -420,6 +420,9 @@ dsmr_parser==0.33 # homeassistant.components.dynalite dynalite_devices==0.1.46 +# homeassistant.components.rainforest_eagle +eagle100==0.1.1 + # homeassistant.components.elgato elgato==3.0.0 @@ -1536,9 +1539,6 @@ twilio==6.32.0 # homeassistant.components.twitch twitchAPI==2.5.2 -# homeassistant.components.rainforest_eagle -uEagle==0.0.2 - # homeassistant.components.ukraine_alarm uasiren==0.0.1 From 6cac1dadeba6cb81285960db1ab6ec6239547cd9 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 25 May 2022 01:23:34 +0200 Subject: [PATCH 791/930] Clean zwave_js platform typing (#72439) * Fix binary sensor * Fix climate * Fix cover * Fix fan * Fix light * Fix lock * Fix number * Fix select * Fix sensor * Add back type ignore until library bump --- .../components/zwave_js/binary_sensor.py | 12 ++++++---- homeassistant/components/zwave_js/climate.py | 18 ++++++++------ homeassistant/components/zwave_js/cover.py | 17 ++++++++++--- homeassistant/components/zwave_js/fan.py | 24 ++++++++++++++----- homeassistant/components/zwave_js/light.py | 15 ++++++++---- homeassistant/components/zwave_js/lock.py | 7 +++--- homeassistant/components/zwave_js/number.py | 15 ++++++++---- homeassistant/components/zwave_js/select.py | 5 +++- homeassistant/components/zwave_js/sensor.py | 24 +++++++++++++------ 9 files changed, 97 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py index 7e9882377bd..fb085cffe62 100644 --- a/homeassistant/components/zwave_js/binary_sensor.py +++ b/homeassistant/components/zwave_js/binary_sensor.py @@ -248,7 +248,7 @@ PROPERTY_SENSOR_MAPPINGS: dict[str, PropertyZWaveJSEntityDescription] = { # Mappings for boolean sensors -BOOLEAN_SENSOR_MAPPINGS: dict[str, BinarySensorEntityDescription] = { +BOOLEAN_SENSOR_MAPPINGS: dict[int, BinarySensorEntityDescription] = { CommandClass.BATTERY: BinarySensorEntityDescription( key=str(CommandClass.BATTERY), device_class=BinarySensorDeviceClass.BATTERY, @@ -304,9 +304,13 @@ async def async_setup_entry( config_entry, driver, info, state_key, notification_description ) ) - elif info.platform_hint == "property" and ( - property_description := PROPERTY_SENSOR_MAPPINGS.get( - info.primary_value.property_name + elif ( + info.platform_hint == "property" + and info.primary_value.property_name + and ( + property_description := PROPERTY_SENSOR_MAPPINGS.get( + info.primary_value.property_name + ) ) ): entities.append( diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index f721db41d9f..cad0b6a1e5c 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -138,7 +138,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): self._current_mode = self.get_zwave_value( THERMOSTAT_MODE_PROPERTY, command_class=CommandClass.THERMOSTAT_MODE ) - self._setpoint_values: dict[ThermostatSetpointType, ZwaveValue] = {} + self._setpoint_values: dict[ThermostatSetpointType, ZwaveValue | None] = {} for enum in ThermostatSetpointType: self._setpoint_values[enum] = self.get_zwave_value( THERMOSTAT_SETPOINT_PROPERTY, @@ -233,9 +233,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): self._hvac_presets = all_presets @property - def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType | None]: + def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType]: """Return the list of enums that are relevant to the current thermostat mode.""" - if self._current_mode is None: + if self._current_mode is None or self._current_mode.value is None: # Thermostat(valve) with no support for setting a mode is considered heating-only return [ThermostatSetpointType.HEATING] return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) # type: ignore[no-any-return] @@ -329,12 +329,13 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): @property def preset_mode(self) -> str | None: """Return the current preset mode, e.g., home, away, temp.""" - if self._current_mode and self._current_mode.value is None: + if self._current_mode is None or self._current_mode.value is None: # guard missing value return None - if self._current_mode and int(self._current_mode.value) not in THERMOSTAT_MODES: - return_val: str = self._current_mode.metadata.states.get( - str(self._current_mode.value) + if int(self._current_mode.value) not in THERMOSTAT_MODES: + return_val: str = cast( + str, + self._current_mode.metadata.states.get(str(self._current_mode.value)), ) return return_val return PRESET_NONE @@ -468,6 +469,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new target preset mode.""" + if self._current_mode is None: + # Thermostat(valve) has no support for setting a mode, so we make it a no-op + return if preset_mode == PRESET_NONE: # try to restore to the (translated) main hvac mode await self.async_set_hvac_mode(self.hvac_mode) diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index f9a9990ed77..ee83db4578c 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -28,6 +28,7 @@ from homeassistant.components.cover import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -140,6 +141,8 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): async def async_set_cover_position(self, **kwargs: Any) -> None: """Move the cover to a specific position.""" target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) + if target_value is None: + raise HomeAssistantError("Missing target value on device.") await self.info.node.async_set_value( target_value, percent_to_zwave_position(kwargs[ATTR_POSITION]) ) @@ -147,11 +150,15 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) + if target_value is None: + raise HomeAssistantError("Missing target value on device.") await self.info.node.async_set_value(target_value, 99) async def async_close_cover(self, **kwargs: Any) -> None: """Close cover.""" target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY) + if target_value is None: + raise HomeAssistantError("Missing target value on device.") await self.info.node.async_set_value(target_value, 0) async def async_stop_cover(self, **kwargs: Any) -> None: @@ -207,7 +214,9 @@ class ZWaveTiltCover(ZWaveCover): None is unknown, 0 is closed, 100 is fully open. """ value = self.data_template.current_tilt_value(self.info.platform_data) - return zwave_tilt_to_percent(value.value) if value else None + if value is None or value.value is None: + return None + return zwave_tilt_to_percent(int(value.value)) async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: """Move the cover tilt to a specific position.""" @@ -241,8 +250,10 @@ class ZwaveMotorizedBarrier(ZWaveBaseEntity, CoverEntity): ) -> None: """Initialize a ZwaveMotorizedBarrier entity.""" super().__init__(config_entry, driver, info) - self._target_state: ZwaveValue = self.get_zwave_value( - TARGET_STATE_PROPERTY, add_to_watched_value_ids=False + # TARGET_STATE_PROPERTY is required in the discovery schema. + self._target_state = cast( + ZwaveValue, + self.get_zwave_value(TARGET_STATE_PROPERTY, add_to_watched_value_ids=False), ) @property diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index eb6d053f958..ae9df47b420 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -96,7 +96,9 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity): percentage_to_ranged_value(DEFAULT_SPEED_RANGE, percentage) ) - await self.info.node.async_set_value(self._target_value, zwave_speed) + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") + await self.info.node.async_set_value(target_value, zwave_speed) async def async_turn_on( self, @@ -110,12 +112,16 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity): elif preset_mode is not None: await self.async_set_preset_mode(preset_mode) else: + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") # Value 255 tells device to return to previous value - await self.info.node.async_set_value(self._target_value, 255) + await self.info.node.async_set_value(target_value, 255) async def async_turn_off(self, **kwargs: Any) -> None: """Turn the device off.""" - await self.info.node.async_set_value(self._target_value, 0) + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") + await self.info.node.async_set_value(target_value, 0) @property def is_on(self) -> bool | None: @@ -160,14 +166,18 @@ class ValueMappingZwaveFan(ZwaveFan): async def async_set_percentage(self, percentage: int) -> None: """Set the speed percentage of the fan.""" + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") zwave_speed = self.percentage_to_zwave_speed(percentage) - await self.info.node.async_set_value(self._target_value, zwave_speed) + await self.info.node.async_set_value(target_value, zwave_speed) async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") for zwave_value, mapped_preset_mode in self.fan_value_mapping.presets.items(): if preset_mode == mapped_preset_mode: - await self.info.node.async_set_value(self._target_value, zwave_value) + await self.info.node.async_set_value(target_value, zwave_value) return raise NotValidPresetModeError( @@ -210,7 +220,9 @@ class ValueMappingZwaveFan(ZwaveFan): @property def preset_mode(self) -> str | None: """Return the current preset mode.""" - return self.fan_value_mapping.presets.get(self.info.primary_value.value) + if (value := self.info.primary_value.value) is None: + return None + return self.fan_value_mapping.presets.get(value) @property def has_fan_value_mapping(self) -> bool: diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py index d026868b418..17293e85a21 100644 --- a/homeassistant/components/zwave_js/light.py +++ b/homeassistant/components/zwave_js/light.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging -from typing import Any +from typing import Any, cast from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import ( @@ -24,6 +24,7 @@ from zwave_js_server.const.command_class.color_switch import ( ColorComponent, ) from zwave_js_server.model.driver import Driver +from zwave_js_server.model.value import Value from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -301,10 +302,14 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity): """Set (multiple) defined colors to given value(s).""" # prefer the (new) combined color property # https://github.com/zwave-js/node-zwave-js/pull/1782 - combined_color_val = self.get_zwave_value( - "targetColor", - CommandClass.SWITCH_COLOR, - value_property_key=None, + # Setting colors is only done if there's a target color value. + combined_color_val = cast( + Value, + self.get_zwave_value( + "targetColor", + CommandClass.SWITCH_COLOR, + value_property_key=None, + ), ) zwave_transition = None diff --git a/homeassistant/components/zwave_js/lock.py b/homeassistant/components/zwave_js/lock.py index e7fbbeb3f99..ffe99373991 100644 --- a/homeassistant/components/zwave_js/lock.py +++ b/homeassistant/components/zwave_js/lock.py @@ -14,7 +14,6 @@ from zwave_js_server.const.command_class.lock import ( LOCK_CMD_CLASS_TO_PROPERTY_MAP, DoorLockMode, ) -from zwave_js_server.model.value import Value as ZwaveValue from zwave_js_server.util.lock import clear_usercode, set_usercode from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN, LockEntity @@ -111,8 +110,10 @@ class ZWaveLock(ZWaveBaseEntity, LockEntity): async def _set_lock_state(self, target_state: str, **kwargs: Any) -> None: """Set the lock state.""" - target_value: ZwaveValue = self.get_zwave_value( - LOCK_CMD_CLASS_TO_PROPERTY_MAP[self.info.primary_value.command_class] + target_value = self.get_zwave_value( + LOCK_CMD_CLASS_TO_PROPERTY_MAP[ + CommandClass(self.info.primary_value.command_class) + ] ) if target_value is not None: await self.info.node.async_set_value( diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py index 56a7ce33be6..737b872b7bc 100644 --- a/homeassistant/components/zwave_js/number.py +++ b/homeassistant/components/zwave_js/number.py @@ -1,13 +1,17 @@ """Support for Z-Wave controls using the number platform.""" from __future__ import annotations +from typing import cast + from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import TARGET_VALUE_PROPERTY from zwave_js_server.model.driver import Driver +from zwave_js_server.model.value import Value from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, NumberEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -55,6 +59,7 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity): ) -> None: """Initialize a ZwaveNumberEntity entity.""" super().__init__(config_entry, driver, info) + self._target_value: Value | None if self.info.primary_value.metadata.writeable: self._target_value = self.info.primary_value else: @@ -95,7 +100,9 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity): async def async_set_value(self, value: float) -> None: """Set new value.""" - await self.info.node.async_set_value(self._target_value, value) + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") + await self.info.node.async_set_value(target_value, value) class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity): @@ -106,9 +113,9 @@ class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity): ) -> None: """Initialize a ZwaveVolumeNumberEntity entity.""" super().__init__(config_entry, driver, info) - self.correction_factor = int( - self.info.primary_value.metadata.max - self.info.primary_value.metadata.min - ) + max_value = cast(int, self.info.primary_value.metadata.max) + min_value = cast(int, self.info.primary_value.metadata.min) + self.correction_factor = max_value - min_value # Fallback in case we can't properly calculate correction factor if self.correction_factor == 0: self.correction_factor = 1 diff --git a/homeassistant/components/zwave_js/select.py b/homeassistant/components/zwave_js/select.py index f8cb294919e..f61149e5de7 100644 --- a/homeassistant/components/zwave_js/select.py +++ b/homeassistant/components/zwave_js/select.py @@ -11,6 +11,7 @@ from zwave_js_server.model.driver import Driver from homeassistant.components.select import DOMAIN as SELECT_DOMAIN, SelectEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -174,5 +175,7 @@ class ZwaveMultilevelSwitchSelectEntity(ZWaveBaseEntity, SelectEntity): async def async_select_option(self, option: str) -> None: """Change the selected option.""" + if (target_value := self._target_value) is None: + raise HomeAssistantError("Missing target value on device.") key = next(key for key, val in self._lookup_map.items() if val == option) - await self.info.node.async_set_value(self._target_value, int(key)) + await self.info.node.async_set_value(target_value, int(key)) diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py index eb5bf778cc0..2b2e2a0de2b 100644 --- a/homeassistant/components/zwave_js/sensor.py +++ b/homeassistant/components/zwave_js/sensor.py @@ -26,6 +26,7 @@ from homeassistant.components.sensor import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, EntityCategory @@ -224,7 +225,7 @@ async def async_setup_entry( LOGGER.warning( "Sensor not implemented for %s/%s", info.platform_hint, - info.primary_value.propertyname, + info.primary_value.property_name, ) return @@ -352,13 +353,15 @@ class ZWaveMeterSensor(ZWaveNumericSensor): """Reset meter(s) on device.""" node = self.info.node primary_value = self.info.primary_value + if (endpoint := primary_value.endpoint) is None: + raise HomeAssistantError("Missing endpoint on device.") options = {} if meter_type is not None: options[RESET_METER_OPTION_TYPE] = meter_type if value is not None: options[RESET_METER_OPTION_TARGET_VALUE] = value args = [options] if options else [] - await node.endpoints[primary_value.endpoint].async_invoke_cc_api( + await node.endpoints[endpoint].async_invoke_cc_api( CommandClass.METER, "reset", *args, wait_for_result=False ) LOGGER.debug( @@ -385,11 +388,12 @@ class ZWaveListSensor(ZwaveSensorBase): config_entry, driver, info, entity_description, unit_of_measurement ) + property_key_name = self.info.primary_value.property_key_name # Entity class attributes self._attr_name = self.generate_name( include_value_name=True, alternate_value_name=self.info.primary_value.property_name, - additional_info=[self.info.primary_value.property_key_name], + additional_info=[property_key_name] if property_key_name else None, ) @property @@ -409,8 +413,10 @@ class ZWaveListSensor(ZwaveSensorBase): @property def extra_state_attributes(self) -> dict[str, str] | None: """Return the device specific state attributes.""" + if (value := self.info.primary_value.value) is None: + return None # add the value's int value as property for multi-value (list) items - return {ATTR_VALUE: self.info.primary_value.value} + return {ATTR_VALUE: value} class ZWaveConfigParameterSensor(ZwaveSensorBase): @@ -430,11 +436,12 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase): ) self._primary_value = cast(ConfigurationValue, self.info.primary_value) + property_key_name = self.info.primary_value.property_key_name # Entity class attributes self._attr_name = self.generate_name( include_value_name=True, alternate_value_name=self.info.primary_value.property_name, - additional_info=[self.info.primary_value.property_key_name], + additional_info=[property_key_name] if property_key_name else None, name_suffix="Config Parameter", ) @@ -458,10 +465,13 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase): @property def extra_state_attributes(self) -> dict[str, str] | None: """Return the device specific state attributes.""" - if self._primary_value.configuration_value_type == ConfigurationValueType.RANGE: + if ( + self._primary_value.configuration_value_type == ConfigurationValueType.RANGE + or (value := self.info.primary_value.value) is None + ): return None # add the value's int value as property for multi-value (list) items - return {ATTR_VALUE: self.info.primary_value.value} + return {ATTR_VALUE: value} class ZWaveNodeStatusSensor(SensorEntity): From 0c2f22d4780612545c483627da729e44d46ee9fd Mon Sep 17 00:00:00 2001 From: rforro Date: Wed, 25 May 2022 01:43:35 +0200 Subject: [PATCH 792/930] Add configurable zha switch entity (#71784) * add configurable zha switch entity * final zha configurable switch * fix codecov * replaced errorneous cluster with local quirk * test fix * minor changes --- homeassistant/components/zha/switch.py | 129 ++++++++++++++++- tests/components/zha/test_switch.py | 186 +++++++++++++++++++++++++ 2 files changed, 314 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 76c41093ed6..800c42eb932 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -2,8 +2,10 @@ from __future__ import annotations import functools -from typing import Any +import logging +from typing import TYPE_CHECKING, Any +import zigpy.exceptions from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.foundation import Status @@ -12,6 +14,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, Platform from homeassistant.core import HomeAssistant, State, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .core import discovery @@ -24,8 +27,17 @@ from .core.const import ( from .core.registries import ZHA_ENTITIES from .entity import ZhaEntity, ZhaGroupEntity +if TYPE_CHECKING: + from .core.channels.base import ZigbeeChannel + from .core.device import ZHADevice + STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.SWITCH) GROUP_MATCH = functools.partial(ZHA_ENTITIES.group_match, Platform.SWITCH) +CONFIG_DIAGNOSTIC_MATCH = functools.partial( + ZHA_ENTITIES.config_diagnostic_match, Platform.SWITCH +) + +_LOGGER = logging.getLogger(__name__) async def async_setup_entry( @@ -138,3 +150,118 @@ class SwitchGroup(ZhaGroupEntity, SwitchEntity): self._state = len(on_states) > 0 self._available = any(state.state != STATE_UNAVAILABLE for state in states) + + +class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): + """Representation of a ZHA switch configuration entity.""" + + _zcl_attribute: str + _zcl_inverter_attribute: str = "" + + @classmethod + def create_entity( + cls, + unique_id: str, + zha_device: ZHADevice, + channels: list[ZigbeeChannel], + **kwargs, + ) -> ZhaEntity | None: + """Entity Factory. + + Return entity if it is a supported configuration, otherwise return None + """ + channel = channels[0] + if ( + cls._zcl_attribute in channel.cluster.unsupported_attributes + or channel.cluster.get(cls._zcl_attribute) is None + ): + _LOGGER.debug( + "%s is not supported - skipping %s entity creation", + cls._zcl_attribute, + cls.__name__, + ) + return None + + return cls(unique_id, zha_device, channels, **kwargs) + + def __init__( + self, + unique_id: str, + zha_device: ZHADevice, + channels: list[ZigbeeChannel], + **kwargs, + ) -> None: + """Init this number configuration entity.""" + self._channel: ZigbeeChannel = channels[0] + super().__init__(unique_id, zha_device, channels, **kwargs) + + async def async_added_to_hass(self) -> None: + """Run when about to be added to hass.""" + await super().async_added_to_hass() + self.async_accept_signal( + self._channel, SIGNAL_ATTR_UPDATED, self.async_set_state + ) + + @callback + def async_set_state(self, attr_id: int, attr_name: str, value: Any): + """Handle state update from channel.""" + self.async_write_ha_state() + + @property + def is_on(self) -> bool: + """Return if the switch is on based on the statemachine.""" + val = bool(self._channel.cluster.get(self._zcl_attribute)) + invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute)) + return (not val) if invert else val + + async def async_turn_on_off(self, state) -> None: + """Turn the entity on or off.""" + try: + invert = bool(self._channel.cluster.get(self._zcl_inverter_attribute)) + result = await self._channel.cluster.write_attributes( + {self._zcl_attribute: not state if invert else state} + ) + except zigpy.exceptions.ZigbeeException as ex: + self.error("Could not set value: %s", ex) + return + if not isinstance(result, Exception) and all( + record.status == Status.SUCCESS for record in result[0] + ): + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs) -> None: + """Turn the entity on.""" + await self.async_turn_on_off(True) + + async def async_turn_off(self, **kwargs) -> None: + """Turn the entity off.""" + await self.async_turn_on_off(False) + + async def async_update(self) -> None: + """Attempt to retrieve the state of the entity.""" + await super().async_update() + _LOGGER.error("Polling current state") + if self._channel: + value = await self._channel.get_attribute_value( + self._zcl_attribute, from_cache=False + ) + invert = await self._channel.get_attribute_value( + self._zcl_inverter_attribute, from_cache=False + ) + _LOGGER.debug("read value=%s, inverter=%s", value, bool(invert)) + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_b6wax7g0", + }, +) +class OnOffWindowDetectionFunctionConfigurationEntity( + ZHASwitchConfigurationEntity, id_suffix="on_off_window_opened_detection" +): + """Representation of a ZHA on off transition time configuration entity.""" + + _attr_entity_category = EntityCategory.CONFIG + _zcl_attribute = "window_detection_function" + _zcl_inverter_attribute = "window_detection_function_inverter" diff --git a/tests/components/zha/test_switch.py b/tests/components/zha/test_switch.py index a624e5f2c73..99e8a681348 100644 --- a/tests/components/zha/test_switch.py +++ b/tests/components/zha/test_switch.py @@ -2,13 +2,25 @@ from unittest.mock import call, patch import pytest +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + OUTPUT_CLUSTERS, + PROFILE_ID, +) +from zigpy.exceptions import ZigbeeException import zigpy.profiles.zha as zha +from zigpy.quirks import CustomCluster, CustomDevice +import zigpy.types as t import zigpy.zcl.clusters.general as general +from zigpy.zcl.clusters.manufacturer_specific import ManufacturerSpecificCluster import zigpy.zcl.foundation as zcl_f from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.zha.core.group import GroupMember from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform +from homeassistant.setup import async_setup_component from .common import ( async_enable_traffic, @@ -174,6 +186,61 @@ async def test_switch(hass, zha_device_joined_restored, zigpy_device): await async_test_rejoin(hass, zigpy_device, [cluster], (1,)) +class WindowDetectionFunctionQuirk(CustomDevice): + """Quirk with window detection function attribute.""" + + class TuyaManufCluster(CustomCluster, ManufacturerSpecificCluster): + """Tuya manufacturer specific cluster.""" + + cluster_id = 0xEF00 + ep_attribute = "tuya_manufacturer" + + attributes = { + 0xEF01: ("window_detection_function", t.Bool), + 0xEF02: ("window_detection_function_inverter", t.Bool), + } + + def __init__(self, *args, **kwargs): + """Initialize with task.""" + super().__init__(*args, **kwargs) + self._attr_cache.update( + {0xEF01: False} + ) # entity won't be created without this + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [general.Basic.cluster_id, TuyaManufCluster], + OUTPUT_CLUSTERS: [], + }, + } + } + + +@pytest.fixture +async def zigpy_device_tuya(hass, zigpy_device_mock, zha_device_joined): + """Device tracker zigpy tuya device.""" + + zigpy_device = zigpy_device_mock( + { + 1: { + SIG_EP_INPUT: [general.Basic.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + } + }, + manufacturer="_TZE200_b6wax7g0", + quirk=WindowDetectionFunctionQuirk, + ) + + zha_device = await zha_device_joined(zigpy_device) + zha_device.available = True + await hass.async_block_till_done() + return zigpy_device + + @patch( "homeassistant.components.zha.entity.UPDATE_GROUP_FROM_CHILD_DELAY", new=0, @@ -292,3 +359,122 @@ async def test_zha_group_switch_entity( # test that group light is now back on assert hass.states.get(entity_id).state == STATE_ON + + +async def test_switch_configurable(hass, zha_device_joined_restored, zigpy_device_tuya): + """Test zha configurable switch platform.""" + + zha_device = await zha_device_joined_restored(zigpy_device_tuya) + cluster = zigpy_device_tuya.endpoints.get(1).tuya_manufacturer + entity_id = await find_entity_id(Platform.SWITCH, zha_device, hass) + assert entity_id is not None + + assert hass.states.get(entity_id).state == STATE_OFF + await async_enable_traffic(hass, [zha_device], enabled=False) + # test that the switch was created and that its state is unavailable + assert hass.states.get(entity_id).state == STATE_UNAVAILABLE + + # allow traffic to flow through the gateway and device + await async_enable_traffic(hass, [zha_device]) + + # test that the state has changed from unavailable to off + assert hass.states.get(entity_id).state == STATE_OFF + + # turn on at switch + await send_attributes_report(hass, cluster, {"window_detection_function": True}) + assert hass.states.get(entity_id).state == STATE_ON + + # turn off at switch + await send_attributes_report(hass, cluster, {"window_detection_function": False}) + assert hass.states.get(entity_id).state == STATE_OFF + + # turn on from HA + with patch( + "zigpy.zcl.Cluster.write_attributes", + return_value=mock_coro([zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS]), + ): + # turn on via UI + await hass.services.async_call( + SWITCH_DOMAIN, "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call( + {"window_detection_function": True} + ) + + # turn off from HA + with patch( + "zigpy.zcl.Cluster.write_attributes", + return_value=mock_coro([zcl_f.Status.SUCCESS, zcl_f.Status.SUCCESS]), + ): + # turn off via UI + await hass.services.async_call( + SWITCH_DOMAIN, "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(cluster.write_attributes.mock_calls) == 2 + assert cluster.write_attributes.call_args == call( + {"window_detection_function": False} + ) + + cluster.read_attributes.reset_mock() + await async_setup_component(hass, "homeassistant", {}) + await hass.async_block_till_done() + + await hass.services.async_call( + "homeassistant", "update_entity", {"entity_id": entity_id}, blocking=True + ) + # the mocking doesn't update the attr cache so this flips back to initial value + assert cluster.read_attributes.call_count == 2 + assert [ + call( + [ + "window_detection_function", + ], + allow_cache=False, + only_cache=False, + manufacturer=None, + ), + call( + [ + "window_detection_function_inverter", + ], + allow_cache=False, + only_cache=False, + manufacturer=None, + ), + ] == cluster.read_attributes.call_args_list + + cluster.write_attributes.reset_mock() + cluster.write_attributes.side_effect = ZigbeeException + + await hass.services.async_call( + SWITCH_DOMAIN, "turn_off", {"entity_id": entity_id}, blocking=True + ) + + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call( + {"window_detection_function": False} + ) + + # test inverter + cluster.write_attributes.reset_mock() + cluster._attr_cache.update({0xEF02: True}) + + await hass.services.async_call( + SWITCH_DOMAIN, "turn_off", {"entity_id": entity_id}, blocking=True + ) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call( + {"window_detection_function": True} + ) + + await hass.services.async_call( + SWITCH_DOMAIN, "turn_on", {"entity_id": entity_id}, blocking=True + ) + assert len(cluster.write_attributes.mock_calls) == 2 + assert cluster.write_attributes.call_args == call( + {"window_detection_function": False} + ) + + # test joining a new switch to the network and HA + await async_test_rejoin(hass, zigpy_device_tuya, [cluster], (0,)) From 7aca007a9a092075acaaf28b16d527bafb2d8115 Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Tue, 24 May 2022 19:44:33 -0400 Subject: [PATCH 793/930] Don't discover entities or initialize cluster channels for the coordinator in ZHA (#72442) don't discover coord entities or init channels --- homeassistant/components/zha/core/channels/__init__.py | 3 ++- homeassistant/components/zha/core/device.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zha/core/channels/__init__.py b/homeassistant/components/zha/core/channels/__init__.py index 00409794473..a0df976486f 100644 --- a/homeassistant/components/zha/core/channels/__init__.py +++ b/homeassistant/components/zha/core/channels/__init__.py @@ -277,7 +277,8 @@ class ChannelPool: pool = cls(channels, ep_id) pool.add_all_channels() pool.add_client_channels() - zha_disc.PROBE.discover_entities(pool) + if not channels.zha_device.is_coordinator: + zha_disc.PROBE.discover_entities(pool) return pool @callback diff --git a/homeassistant/components/zha/core/device.py b/homeassistant/components/zha/core/device.py index 854d80ffb78..e5b3403ba54 100644 --- a/homeassistant/components/zha/core/device.py +++ b/homeassistant/components/zha/core/device.py @@ -273,7 +273,7 @@ class ZHADevice(LogMixin): @property def skip_configuration(self) -> bool: """Return true if the device should not issue configuration related commands.""" - return self._zigpy_device.skip_configuration + return self._zigpy_device.skip_configuration or self.is_coordinator @property def gateway(self): From db815a7504cae47cee7dc9906eae66cc0f0a9fd5 Mon Sep 17 00:00:00 2001 From: rforro Date: Wed, 25 May 2022 01:56:03 +0200 Subject: [PATCH 794/930] ZHA Add entities for Lidl water valve quirk (#72307) * init * added timer number entity * added write attribute button entity * fixed missed errors * minor changes & fixed failing test * removed icon * unit and icons --- homeassistant/components/zha/binary_sensor.py | 13 +++ homeassistant/components/zha/button.py | 53 +++++++++ homeassistant/components/zha/number.py | 17 +++ homeassistant/components/zha/sensor.py | 16 +++ tests/components/zha/test_button.py | 106 +++++++++++++++++- 5 files changed, 204 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py index 730748d74ae..954c60fa895 100644 --- a/homeassistant/components/zha/binary_sensor.py +++ b/homeassistant/components/zha/binary_sensor.py @@ -171,3 +171,16 @@ class IASZone(BinarySensor): value = await self._channel.get_attribute_value("zone_status") if value is not None: self._state = value & 3 + + +@MULTI_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_htnnfasr", + }, +) +class FrostLock(BinarySensor, id_suffix="frost_lock"): + """ZHA BinarySensor.""" + + SENSOR_ATTR = "frost_lock" + _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.LOCK diff --git a/homeassistant/components/zha/button.py b/homeassistant/components/zha/button.py index f130936df02..9f241795267 100644 --- a/homeassistant/components/zha/button.py +++ b/homeassistant/components/zha/button.py @@ -6,6 +6,9 @@ import functools import logging from typing import Any +import zigpy.exceptions +from zigpy.zcl.foundation import Status + from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -21,6 +24,9 @@ from .core.typing import ChannelType, ZhaDeviceType from .entity import ZhaEntity MULTI_MATCH = functools.partial(ZHA_ENTITIES.multipass_match, Platform.BUTTON) +CONFIG_DIAGNOSTIC_MATCH = functools.partial( + ZHA_ENTITIES.config_diagnostic_match, Platform.BUTTON +) DEFAULT_DURATION = 5 # seconds _LOGGER = logging.getLogger(__name__) @@ -103,3 +109,50 @@ class ZHAIdentifyButton(ZHAButton): """Return the arguments to use in the command.""" return [DEFAULT_DURATION] + + +class ZHAAttributeButton(ZhaEntity, ButtonEntity): + """Defines a ZHA button, which stes value to an attribute.""" + + _attribute_name: str = None + _attribute_value: Any = None + + def __init__( + self, + unique_id: str, + zha_device: ZhaDeviceType, + channels: list[ChannelType], + **kwargs, + ) -> None: + """Init this button.""" + super().__init__(unique_id, zha_device, channels, **kwargs) + self._channel: ChannelType = channels[0] + + async def async_press(self) -> None: + """Write attribute with defined value.""" + try: + result = await self._channel.cluster.write_attributes( + {self._attribute_name: self._attribute_value} + ) + except zigpy.exceptions.ZigbeeException as ex: + self.error("Could not set value: %s", ex) + return + if not isinstance(result, Exception) and all( + record.status == Status.SUCCESS for record in result[0] + ): + self.async_write_ha_state() + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_htnnfasr", + }, +) +class FrostLockResetButton(ZHAAttributeButton, id_suffix="reset_frost_lock"): + """Defines a ZHA identify button.""" + + _attribute_name = "frost_lock_reset" + _attribute_value = 0 + _attr_device_class = ButtonDeviceClass.RESTART + _attr_entity_category = EntityCategory.CONFIG diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 22d086891ca..bece4bc894d 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -495,3 +495,20 @@ class StartUpCurrentLevelConfigurationEntity( _attr_min_value: float = 0x00 _attr_max_value: float = 0xFF _zcl_attribute: str = "start_up_current_level" + + +@CONFIG_DIAGNOSTIC_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_htnnfasr", + }, +) +class TimerDurationMinutes(ZHANumberConfigurationEntity, id_suffix="timer_duration"): + """Representation of a ZHA timer duration configuration entity.""" + + _attr_entity_category = EntityCategory.CONFIG + _attr_icon: str = ICONS[14] + _attr_min_value: float = 0x00 + _attr_max_value: float = 0x257 + _attr_unit_of_measurement: str | None = UNITS[72] + _zcl_attribute: str = "timer_duration" diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 249034ef068..3e3017f6fa9 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -27,6 +27,7 @@ from homeassistant.const import ( PRESSURE_HPA, TEMP_CELSIUS, TIME_HOURS, + TIME_MINUTES, TIME_SECONDS, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS, @@ -754,3 +755,18 @@ class RSSISensor(Sensor, id_suffix="rssi"): @MULTI_MATCH(channel_names=CHANNEL_BASIC) class LQISensor(RSSISensor, id_suffix="lqi"): """LQI sensor for a device.""" + + +@MULTI_MATCH( + channel_names="tuya_manufacturer", + manufacturers={ + "_TZE200_htnnfasr", + }, +) +class TimeLeft(Sensor, id_suffix="time_left"): + """Sensor that displays time left value.""" + + SENSOR_ATTR = "timer_time_left" + _attr_device_class: SensorDeviceClass = SensorDeviceClass.DURATION + _attr_icon = "mdi:timer" + _unit = TIME_MINUTES diff --git a/tests/components/zha/test_button.py b/tests/components/zha/test_button.py index 762d2d46e54..f692528203f 100644 --- a/tests/components/zha/test_button.py +++ b/tests/components/zha/test_button.py @@ -1,11 +1,22 @@ """Test ZHA button.""" -from unittest.mock import patch +from unittest.mock import call, patch from freezegun import freeze_time import pytest +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + OUTPUT_CLUSTERS, + PROFILE_ID, +) from zigpy.const import SIG_EP_PROFILE +from zigpy.exceptions import ZigbeeException import zigpy.profiles.zha as zha +from zigpy.quirks import CustomCluster, CustomDevice +import zigpy.types as t import zigpy.zcl.clusters.general as general +from zigpy.zcl.clusters.manufacturer_specific import ManufacturerSpecificCluster import zigpy.zcl.clusters.security as security import zigpy.zcl.foundation as zcl_f @@ -14,6 +25,7 @@ from homeassistant.components.button.const import SERVICE_PRESS from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, + ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_DIAGNOSTIC, STATE_UNKNOWN, ) @@ -48,6 +60,49 @@ async def contact_sensor(hass, zigpy_device_mock, zha_device_joined_restored): return zha_device, zigpy_device.endpoints[1].identify +class FrostLockQuirk(CustomDevice): + """Quirk with frost lock attribute.""" + + class TuyaManufCluster(CustomCluster, ManufacturerSpecificCluster): + """Tuya manufacturer specific cluster.""" + + cluster_id = 0xEF00 + ep_attribute = "tuya_manufacturer" + + attributes = {0xEF01: ("frost_lock_reset", t.Bool)} + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [general.Basic.cluster_id, TuyaManufCluster], + OUTPUT_CLUSTERS: [], + }, + } + } + + +@pytest.fixture +async def tuya_water_valve(hass, zigpy_device_mock, zha_device_joined_restored): + """Tuya Water Valve fixture.""" + + zigpy_device = zigpy_device_mock( + { + 1: { + SIG_EP_INPUT: [general.Basic.cluster_id], + SIG_EP_OUTPUT: [], + SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH, + } + }, + manufacturer="_TZE200_htnnfasr", + quirk=FrostLockQuirk, + ) + + zha_device = await zha_device_joined_restored(zigpy_device) + return zha_device, zigpy_device.endpoints[1].tuya_manufacturer + + @freeze_time("2021-11-04 17:37:00", tz_offset=-1) async def test_button(hass, contact_sensor): """Test zha button platform.""" @@ -87,3 +142,52 @@ async def test_button(hass, contact_sensor): assert state assert state.state == "2021-11-04T16:37:00+00:00" assert state.attributes[ATTR_DEVICE_CLASS] == ButtonDeviceClass.UPDATE + + +async def test_frost_unlock(hass, tuya_water_valve): + """Test custom frost unlock zha button.""" + + entity_registry = er.async_get(hass) + zha_device, cluster = tuya_water_valve + assert cluster is not None + entity_id = await find_entity_id(DOMAIN, zha_device, hass) + assert entity_id is not None + + state = hass.states.get(entity_id) + assert state + assert state.state == STATE_UNKNOWN + assert state.attributes[ATTR_DEVICE_CLASS] == ButtonDeviceClass.RESTART + + entry = entity_registry.async_get(entity_id) + assert entry + assert entry.entity_category == ENTITY_CATEGORY_CONFIG + + with patch( + "zigpy.zcl.Cluster.request", + return_value=mock_coro([0x00, zcl_f.Status.SUCCESS]), + ): + await hass.services.async_call( + DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call({"frost_lock_reset": 0}) + + state = hass.states.get(entity_id) + assert state + assert state.attributes[ATTR_DEVICE_CLASS] == ButtonDeviceClass.RESTART + + cluster.write_attributes.reset_mock() + cluster.write_attributes.side_effect = ZigbeeException + + await hass.services.async_call( + DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + assert len(cluster.write_attributes.mock_calls) == 1 + assert cluster.write_attributes.call_args == call({"frost_lock_reset": 0}) From 301341a49feb4150ef78fad4713dc9eb159bda6e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 25 May 2022 00:26:18 +0000 Subject: [PATCH 795/930] [ci skip] Translation update --- .../components/google/translations/ca.json | 9 ++ .../components/google/translations/hu.json | 9 ++ .../components/google/translations/pl.json | 9 ++ .../components/google/translations/tr.json | 9 ++ .../components/hassio/translations/ca.json | 1 + .../components/hassio/translations/et.json | 1 + .../components/hassio/translations/hu.json | 1 + .../components/hassio/translations/it.json | 1 + .../components/hassio/translations/ja.json | 1 + .../components/hassio/translations/no.json | 1 + .../components/hassio/translations/pl.json | 1 + .../components/hassio/translations/tr.json | 1 + .../hassio/translations/zh-Hant.json | 1 + .../components/heos/translations/ja.json | 2 +- .../here_travel_time/translations/hu.json | 82 +++++++++++++++++++ .../here_travel_time/translations/pl.json | 63 ++++++++++++++ .../here_travel_time/translations/tr.json | 82 +++++++++++++++++++ .../components/konnected/translations/hu.json | 16 ++-- .../components/konnected/translations/tr.json | 16 ++-- .../components/laundrify/translations/hu.json | 25 ++++++ .../components/laundrify/translations/pl.json | 5 +- .../components/laundrify/translations/tr.json | 25 ++++++ .../components/recorder/translations/ca.json | 2 + .../components/recorder/translations/hu.json | 2 + .../components/recorder/translations/pl.json | 2 + .../components/recorder/translations/tr.json | 2 + .../components/shelly/translations/tr.json | 1 + .../steam_online/translations/ca.json | 3 + .../steam_online/translations/et.json | 3 + .../steam_online/translations/hu.json | 3 + .../steam_online/translations/it.json | 3 + .../steam_online/translations/ja.json | 3 + .../steam_online/translations/no.json | 3 + .../steam_online/translations/pl.json | 3 + .../steam_online/translations/tr.json | 3 + .../steam_online/translations/zh-Hant.json | 3 + 36 files changed, 379 insertions(+), 18 deletions(-) create mode 100644 homeassistant/components/here_travel_time/translations/hu.json create mode 100644 homeassistant/components/here_travel_time/translations/tr.json create mode 100644 homeassistant/components/laundrify/translations/hu.json create mode 100644 homeassistant/components/laundrify/translations/tr.json diff --git a/homeassistant/components/google/translations/ca.json b/homeassistant/components/google/translations/ca.json index 829ce1413d5..2c9190e4bfd 100644 --- a/homeassistant/components/google/translations/ca.json +++ b/homeassistant/components/google/translations/ca.json @@ -27,5 +27,14 @@ "title": "Reautenticaci\u00f3 de la integraci\u00f3" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Acc\u00e9s de Home Assistant a Google Calendar" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/hu.json b/homeassistant/components/google/translations/hu.json index 2c552eca30e..66ea71c72ec 100644 --- a/homeassistant/components/google/translations/hu.json +++ b/homeassistant/components/google/translations/hu.json @@ -27,5 +27,14 @@ "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Home Assistant hozz\u00e1f\u00e9r\u00e9s a Google Napt\u00e1rhoz" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/pl.json b/homeassistant/components/google/translations/pl.json index b4f45c0abe4..fff2a20ee39 100644 --- a/homeassistant/components/google/translations/pl.json +++ b/homeassistant/components/google/translations/pl.json @@ -27,5 +27,14 @@ "title": "Ponownie uwierzytelnij integracj\u0119" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Dost\u0119p Home Assistanta do Kalendarza Google" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/google/translations/tr.json b/homeassistant/components/google/translations/tr.json index 9d5fc8d2416..f7b5b6d79ff 100644 --- a/homeassistant/components/google/translations/tr.json +++ b/homeassistant/components/google/translations/tr.json @@ -27,5 +27,14 @@ "title": "Entegrasyonu Yeniden Do\u011frula" } } + }, + "options": { + "step": { + "init": { + "data": { + "calendar_access": "Google Takvim'e Home Assistant eri\u015fimi" + } + } + } } } \ No newline at end of file diff --git a/homeassistant/components/hassio/translations/ca.json b/homeassistant/components/hassio/translations/ca.json index 6a6874662b2..2c4285d4908 100644 --- a/homeassistant/components/hassio/translations/ca.json +++ b/homeassistant/components/hassio/translations/ca.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Versi\u00f3 de l'agent", "board": "Placa", "disk_total": "Total del disc", "disk_used": "Emmagatzematge utilitzat", diff --git a/homeassistant/components/hassio/translations/et.json b/homeassistant/components/hassio/translations/et.json index 4449c058498..b86eef353b9 100644 --- a/homeassistant/components/hassio/translations/et.json +++ b/homeassistant/components/hassio/translations/et.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agendi versioon", "board": "Seade", "disk_total": "Kettaruum kokku", "disk_used": "Kasutatud kettaruum", diff --git a/homeassistant/components/hassio/translations/hu.json b/homeassistant/components/hassio/translations/hu.json index 8d2bdfd75ad..4c83b94935d 100644 --- a/homeassistant/components/hassio/translations/hu.json +++ b/homeassistant/components/hassio/translations/hu.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "\u00dcgyn\u00f6k verzi\u00f3", "board": "Alaplap", "disk_total": "\u00d6sszes hely", "disk_used": "Felhaszn\u00e1lt hely", diff --git a/homeassistant/components/hassio/translations/it.json b/homeassistant/components/hassio/translations/it.json index 44499d3f002..3dc55d0f525 100644 --- a/homeassistant/components/hassio/translations/it.json +++ b/homeassistant/components/hassio/translations/it.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Versione agente", "board": "Scheda di base", "disk_total": "Disco totale", "disk_used": "Disco utilizzato", diff --git a/homeassistant/components/hassio/translations/ja.json b/homeassistant/components/hassio/translations/ja.json index b7489852bdb..2561cf75310 100644 --- a/homeassistant/components/hassio/translations/ja.json +++ b/homeassistant/components/hassio/translations/ja.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "\u30a8\u30fc\u30b8\u30a7\u30f3\u30c8\u306e\u30d0\u30fc\u30b8\u30e7\u30f3", "board": "\u30dc\u30fc\u30c9", "disk_total": "\u30c7\u30a3\u30b9\u30af\u5408\u8a08", "disk_used": "\u4f7f\u7528\u6e08\u307f\u30c7\u30a3\u30b9\u30af", diff --git a/homeassistant/components/hassio/translations/no.json b/homeassistant/components/hassio/translations/no.json index 30ff8b903a9..1fa10a98921 100644 --- a/homeassistant/components/hassio/translations/no.json +++ b/homeassistant/components/hassio/translations/no.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agentversjon", "board": "Styret", "disk_total": "Disk totalt", "disk_used": "Disk brukt", diff --git a/homeassistant/components/hassio/translations/pl.json b/homeassistant/components/hassio/translations/pl.json index 2f6b5cab1dc..8850b7066fd 100644 --- a/homeassistant/components/hassio/translations/pl.json +++ b/homeassistant/components/hassio/translations/pl.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Wersja agenta", "board": "Uk\u0142ad", "disk_total": "Pojemno\u015b\u0107 dysku", "disk_used": "Pojemno\u015b\u0107 u\u017cyta", diff --git a/homeassistant/components/hassio/translations/tr.json b/homeassistant/components/hassio/translations/tr.json index 16504c32372..cf92d597e23 100644 --- a/homeassistant/components/hassio/translations/tr.json +++ b/homeassistant/components/hassio/translations/tr.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Arac\u0131 S\u00fcr\u00fcm\u00fc", "board": "Panel", "disk_total": "Disk Toplam\u0131", "disk_used": "Kullan\u0131lan Disk", diff --git a/homeassistant/components/hassio/translations/zh-Hant.json b/homeassistant/components/hassio/translations/zh-Hant.json index 91c7f64e39c..5a503e54937 100644 --- a/homeassistant/components/hassio/translations/zh-Hant.json +++ b/homeassistant/components/hassio/translations/zh-Hant.json @@ -1,6 +1,7 @@ { "system_health": { "info": { + "agent_version": "Agent \u7248\u672c", "board": "\u677f", "disk_total": "\u7e3d\u78c1\u789f\u7a7a\u9593", "disk_used": "\u5df2\u4f7f\u7528\u7a7a\u9593", diff --git a/homeassistant/components/heos/translations/ja.json b/homeassistant/components/heos/translations/ja.json index 0464bf98979..55e075a548a 100644 --- a/homeassistant/components/heos/translations/ja.json +++ b/homeassistant/components/heos/translations/ja.json @@ -11,7 +11,7 @@ "data": { "host": "\u30db\u30b9\u30c8" }, - "description": "Heos\u30c7\u30d0\u30a4\u30b9(\u3067\u304d\u308c\u3070\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306b\u6709\u7dda\u3067\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u30c7\u30d0\u30a4\u30b9)\u306e\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", + "description": "Heos\u30c7\u30d0\u30a4\u30b9(\u53ef\u80fd\u306a\u306e\u3067\u3042\u308c\u3070\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306b\u6709\u7dda\u3067\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u30c7\u30d0\u30a4\u30b9)\u306e\u30db\u30b9\u30c8\u540d\u307e\u305f\u306fIP\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "title": "Heos\u306b\u63a5\u7d9a" } } diff --git a/homeassistant/components/here_travel_time/translations/hu.json b/homeassistant/components/here_travel_time/translations/hu.json new file mode 100644 index 00000000000..cdd40f139d7 --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/hu.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van" + }, + "error": { + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "C\u00e9l GPS koordin\u00e1tak\u00e9nt" + }, + "title": "C\u00e9l kiv\u00e1laszt\u00e1sa" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "C\u00e9l egy entit\u00e1s haszn\u00e1lat\u00e1val" + }, + "title": "C\u00e9l kiv\u00e1laszt\u00e1sa" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "T\u00e9rk\u00e9pes hely haszn\u00e1lata", + "destination_entity": "Entit\u00e1s haszn\u00e1lata" + }, + "title": "C\u00e9l kiv\u00e1laszt\u00e1sa" + }, + "origin_coordinates": { + "data": { + "origin": "Eredet GPS koordin\u00e1t\u00e1kk\u00e9nt" + }, + "title": "Eredet kiv\u00e1laszt\u00e1sa" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Eredet egy entit\u00e1s haszn\u00e1lat\u00e1val" + }, + "title": "Eredet kiv\u00e1laszt\u00e1sa" + }, + "user": { + "data": { + "api_key": "API kulcs", + "mode": "Utaz\u00e1si m\u00f3d", + "name": "Elnevez\u00e9s" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "\u00c9rkez\u00e9s ideje" + }, + "title": "\u00c9rkez\u00e9si id\u0151 kiv\u00e1laszt\u00e1sa" + }, + "departure_time": { + "data": { + "departure_time": "Indul\u00e1si id\u0151" + }, + "title": "Indul\u00e1si id\u0151 kiv\u00e1laszt\u00e1sa" + }, + "init": { + "data": { + "route_mode": "\u00datvonaltervez\u00e9si m\u00f3d", + "traffic_mode": "Forgalmi m\u00f3d", + "unit_system": "Egys\u00e9grendszer" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "\u00c9rkez\u00e9si id\u0151 konfigur\u00e1l\u00e1sa", + "departure_time": "Indul\u00e1si id\u0151 be\u00e1ll\u00edt\u00e1sa", + "no_time": "Ne konfigur\u00e1ljon id\u0151pontot" + }, + "title": "Id\u0151t\u00edpus kiv\u00e1laszt\u00e1sa" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/pl.json b/homeassistant/components/here_travel_time/translations/pl.json index f04290714e4..3e4f41212a2 100644 --- a/homeassistant/components/here_travel_time/translations/pl.json +++ b/homeassistant/components/here_travel_time/translations/pl.json @@ -8,12 +8,75 @@ "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { + "destination_coordinates": { + "data": { + "destination": "Punkt docelowy jako wsp\u00f3\u0142rz\u0119dne GPS" + }, + "title": "Wybierz punkt docelowy" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Punkt docelowy przy u\u017cyciu encji" + }, + "title": "Wybierz punkt docelowy" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Lokalizacja na mapie", + "destination_entity": "Encja" + }, + "title": "Wybierz punkt docelowy" + }, + "origin_coordinates": { + "data": { + "origin": "Punkt pocz\u0105tkowy jako wsp\u00f3\u0142rz\u0119dne GPS" + }, + "title": "Wybierz punkt pocz\u0105tkowy" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Punkt pocz\u0105tkowy przy u\u017cyciu encji" + }, + "title": "Wybierz punkt pocz\u0105tkowy" + }, "user": { "data": { "api_key": "Klucz API", + "mode": "Tryb podr\u00f3\u017cy", "name": "Nazwa" } } } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Czas przyjazdu" + }, + "title": "Wybierz czas przyjazdu" + }, + "departure_time": { + "data": { + "departure_time": "Czas wyjazdu" + }, + "title": "Wybierz czas wyjazdu" + }, + "init": { + "data": { + "route_mode": "Tryb trasy", + "traffic_mode": "Tryb ruchu", + "unit_system": "System metryczny" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Skonfiguruj czas przyjazdu", + "departure_time": "Skonfiguruj czas wyjazdu", + "no_time": "Nie konfiguruj czasu" + }, + "title": "Wybierz typ czasu" + } + } } } \ No newline at end of file diff --git a/homeassistant/components/here_travel_time/translations/tr.json b/homeassistant/components/here_travel_time/translations/tr.json new file mode 100644 index 00000000000..181588ba54a --- /dev/null +++ b/homeassistant/components/here_travel_time/translations/tr.json @@ -0,0 +1,82 @@ +{ + "config": { + "abort": { + "already_configured": "Cihaz zaten yap\u0131land\u0131r\u0131lm\u0131\u015f" + }, + "error": { + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "unknown": "Beklenmeyen hata" + }, + "step": { + "destination_coordinates": { + "data": { + "destination": "GPS koordinatlar\u0131 olarak hedef" + }, + "title": "Hedef Se\u00e7" + }, + "destination_entity_id": { + "data": { + "destination_entity_id": "Bir varl\u0131k kullanarak hedef" + }, + "title": "Hedef Se\u00e7" + }, + "destination_menu": { + "menu_options": { + "destination_coordinates": "Bir harita konumu kullan\u0131n", + "destination_entity": "Bir varl\u0131\u011f\u0131 kullan\u0131n" + }, + "title": "Hedef Se\u00e7" + }, + "origin_coordinates": { + "data": { + "origin": "GPS koordinatlar\u0131 olarak kalk\u0131\u015f" + }, + "title": "Kalk\u0131\u015f Se\u00e7in" + }, + "origin_entity_id": { + "data": { + "origin_entity_id": "Bir varl\u0131k kullanarak kaynak" + }, + "title": "Kalk\u0131\u015f Se\u00e7in" + }, + "user": { + "data": { + "api_key": "API Anahtar\u0131", + "mode": "Seyahat Modu", + "name": "Ad" + } + } + } + }, + "options": { + "step": { + "arrival_time": { + "data": { + "arrival_time": "Var\u0131\u015f Zaman\u0131" + }, + "title": "Var\u0131\u015f Saatini Se\u00e7in" + }, + "departure_time": { + "data": { + "departure_time": "Hareket Saati" + }, + "title": "Kalk\u0131\u015f Saatini Se\u00e7in" + }, + "init": { + "data": { + "route_mode": "Rota Modu", + "traffic_mode": "Trafik Modu", + "unit_system": "\u00d6l\u00e7\u00fc sistemi" + } + }, + "time_menu": { + "menu_options": { + "arrival_time": "Var\u0131\u015f saatini yap\u0131land\u0131r\u0131n", + "departure_time": "Kalk\u0131\u015f saati yap\u0131land\u0131r\u0131n", + "no_time": "Bir zaman yap\u0131land\u0131rmay\u0131n" + }, + "title": "Zaman T\u00fcr\u00fcn\u00fc Se\u00e7in" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/konnected/translations/hu.json b/homeassistant/components/konnected/translations/hu.json index c40b1823424..57cbf0d9bb9 100644 --- a/homeassistant/components/konnected/translations/hu.json +++ b/homeassistant/components/konnected/translations/hu.json @@ -41,7 +41,7 @@ "options_binary": { "data": { "inverse": "Invert\u00e1lja a nyitott/z\u00e1rt \u00e1llapotot", - "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", + "name": "Elnevez\u00e9s", "type": "Bin\u00e1ris \u00e9rz\u00e9kel\u0151 t\u00edpusa" }, "description": "{zone} opci\u00f3k", @@ -49,8 +49,8 @@ }, "options_digital": { "data": { - "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", - "poll_interval": "Lek\u00e9rdez\u00e9si id\u0151k\u00f6z (perc) (opcion\u00e1lis)", + "name": "Elnevez\u00e9s", + "poll_interval": "Lek\u00e9rdez\u00e9si id\u0151k\u00f6z (perc)", "type": "\u00c9rz\u00e9kel\u0151 t\u00edpusa" }, "description": "{zone} opci\u00f3k", @@ -86,7 +86,7 @@ }, "options_misc": { "data": { - "api_host": "API host URL fel\u00fclb\u00edr\u00e1l\u00e1sa (opcion\u00e1lis)", + "api_host": "API host URL fel\u00fclb\u00edr\u00e1l\u00e1sa", "blink": "A panel LED villog\u00e1sa \u00e1llapotv\u00e1ltoz\u00e1skor", "discovery": "V\u00e1laszoljon a h\u00e1l\u00f3zaton \u00e9rkez\u0151 felder\u00edt\u00e9si k\u00e9r\u00e9sekre", "override_api_host": "Az alap\u00e9rtelmezett Home Assistant API host-URL fel\u00fcl\u00edr\u00e1sa" @@ -97,11 +97,11 @@ "options_switch": { "data": { "activation": "Kimenet bekapcsolt \u00e1llapotban", - "momentary": "Impulzus id\u0151tartama (ms) (opcion\u00e1lis)", + "momentary": "Impulzus id\u0151tartama (ms)", "more_states": "Tov\u00e1bbi \u00e1llapotok konfigur\u00e1l\u00e1sa ehhez a z\u00f3n\u00e1hoz", - "name": "Elnevez\u00e9s (nem k\u00f6telez\u0151)", - "pause": "Sz\u00fcnet impulzusok k\u00f6z\u00f6tt (ms) (opcion\u00e1lis)", - "repeat": "Ism\u00e9tl\u00e9si id\u0151k (-1 = v\u00e9gtelen) (opcion\u00e1lis)" + "name": "Elnevez\u00e9s", + "pause": "Sz\u00fcnet impulzusok k\u00f6z\u00f6tt (ms)", + "repeat": "Ism\u00e9tl\u00e9si id\u0151k (-1 = v\u00e9gtelen)" }, "description": "{zone} opci\u00f3k: \u00e1llapot {state}", "title": "Kapcsolhat\u00f3 kimenet konfigur\u00e1l\u00e1sa" diff --git a/homeassistant/components/konnected/translations/tr.json b/homeassistant/components/konnected/translations/tr.json index 3c23b26ab0d..f5cb48dc088 100644 --- a/homeassistant/components/konnected/translations/tr.json +++ b/homeassistant/components/konnected/translations/tr.json @@ -41,7 +41,7 @@ "options_binary": { "data": { "inverse": "A\u00e7\u0131k / kapal\u0131 durumunu tersine \u00e7evirin", - "name": "Ad (iste\u011fe ba\u011fl\u0131)", + "name": "Ad", "type": "\u0130kili Sens\u00f6r Tipi" }, "description": "{zone} se\u00e7enekleri", @@ -49,8 +49,8 @@ }, "options_digital": { "data": { - "name": "Ad (iste\u011fe ba\u011fl\u0131)", - "poll_interval": "Yoklama Aral\u0131\u011f\u0131 (dakika) (iste\u011fe ba\u011fl\u0131)", + "name": "Ad", + "poll_interval": "Yoklama Aral\u0131\u011f\u0131 (dakika)", "type": "Sens\u00f6r Tipi" }, "description": "{zone} se\u00e7enekleri", @@ -86,7 +86,7 @@ }, "options_misc": { "data": { - "api_host": "API ana makine URL'sini ge\u00e7ersiz k\u0131l (iste\u011fe ba\u011fl\u0131)", + "api_host": "API ana makine URL'sini ge\u00e7ersiz k\u0131l", "blink": "Durum de\u011fi\u015fikli\u011fi g\u00f6nderilirken panelin LED'ini yan\u0131p s\u00f6nd\u00fcr", "discovery": "A\u011f\u0131n\u0131zdaki ke\u015fif isteklerine yan\u0131t verin", "override_api_host": "Varsay\u0131lan Home Assistant API ana bilgisayar paneli URL'sini ge\u00e7ersiz k\u0131l" @@ -97,11 +97,11 @@ "options_switch": { "data": { "activation": "A\u00e7\u0131kken \u00e7\u0131kt\u0131", - "momentary": "Darbe s\u00fcresi (ms) (iste\u011fe ba\u011fl\u0131)", + "momentary": "Darbe s\u00fcresi (ms)", "more_states": "Bu b\u00f6lge i\u00e7in ek durumlar yap\u0131land\u0131r\u0131n", - "name": "Ad (iste\u011fe ba\u011fl\u0131)", - "pause": "Darbeler aras\u0131nda duraklama (ms) (iste\u011fe ba\u011fl\u0131)", - "repeat": "Tekrarlanacak zamanlar (-1=sonsuz) (iste\u011fe ba\u011fl\u0131)" + "name": "Ad", + "pause": "Darbeler aras\u0131nda duraklama (ms)", + "repeat": "Tekrarlanacak zamanlar (-1=sonsuz)" }, "description": "{zone} se\u00e7enekleri: durum {state}", "title": "De\u011fi\u015ftirilebilir \u00c7\u0131k\u0131\u015f\u0131 Yap\u0131land\u0131r" diff --git a/homeassistant/components/laundrify/translations/hu.json b/homeassistant/components/laundrify/translations/hu.json new file mode 100644 index 00000000000..aa22ddc3da3 --- /dev/null +++ b/homeassistant/components/laundrify/translations/hu.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges." + }, + "error": { + "cannot_connect": "Sikertelen csatlakoz\u00e1s", + "invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s", + "invalid_format": "\u00c9rv\u00e9nytelen form\u00e1tum. K\u00e9rj\u00fck, adja meg \u00edgy: xxx-xxx.", + "unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt" + }, + "step": { + "init": { + "data": { + "code": "Hiteles\u00edt\u00e9si k\u00f3d (xxx-xxx)" + }, + "description": "K\u00e9rj\u00fck, adja meg a laundrify-alkalmaz\u00e1sban megjelen\u0151 szem\u00e9lyes enged\u00e9lyez\u00e9si k\u00f3dj\u00e1t." + }, + "reauth_confirm": { + "description": "A laundrify integr\u00e1ci\u00f3nak \u00fajra kell hiteles\u00edtenie.", + "title": "Integr\u00e1ci\u00f3 \u00fajrahiteles\u00edt\u00e9se" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/laundrify/translations/pl.json b/homeassistant/components/laundrify/translations/pl.json index 28145b20fce..27b33ab5880 100644 --- a/homeassistant/components/laundrify/translations/pl.json +++ b/homeassistant/components/laundrify/translations/pl.json @@ -6,15 +6,18 @@ "error": { "cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia", "invalid_auth": "Niepoprawne uwierzytelnienie", + "invalid_format": "Niepoprawny format. Prosz\u0119 poda\u0107 jako xxx-xxx.", "unknown": "Nieoczekiwany b\u0142\u0105d" }, "step": { "init": { "data": { "code": "Kod autoryzacyjny (xxx-xxx)" - } + }, + "description": "Wprowad\u017a sw\u00f3j osobisty kod autoryzacyjny, kt\u00f3ry jest widoczny w aplikacji laundrify." }, "reauth_confirm": { + "description": "Integracja laundrify wymaga ponownego uwierzytelnienia Twojego konta.", "title": "Ponownie uwierzytelnij integracj\u0119" } } diff --git a/homeassistant/components/laundrify/translations/tr.json b/homeassistant/components/laundrify/translations/tr.json new file mode 100644 index 00000000000..97a1b13e192 --- /dev/null +++ b/homeassistant/components/laundrify/translations/tr.json @@ -0,0 +1,25 @@ +{ + "config": { + "abort": { + "single_instance_allowed": "Zaten yap\u0131land\u0131r\u0131lm\u0131\u015f. Yaln\u0131zca tek bir konfig\u00fcrasyon m\u00fcmk\u00fcnd\u00fcr." + }, + "error": { + "cannot_connect": "Ba\u011flanma hatas\u0131", + "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", + "invalid_format": "Ge\u00e7ersiz format. L\u00fctfen xxx-xxx olarak belirtin.", + "unknown": "Beklenmeyen hata" + }, + "step": { + "init": { + "data": { + "code": "Yetkilendirme Kodu (xxx-xxx)" + }, + "description": "L\u00fctfen laundrify-App g\u00f6sterilen ki\u015fisel Yetkilendirme Kodunuzu girin." + }, + "reauth_confirm": { + "description": "\u00c7ama\u015f\u0131r y\u0131kama entegrasyonunun yeniden kimlik do\u011frulamas\u0131 yapmas\u0131 gerekiyor.", + "title": "Entegrasyonu Yeniden Do\u011frula" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/recorder/translations/ca.json b/homeassistant/components/recorder/translations/ca.json index ee9efb50b21..4450be81574 100644 --- a/homeassistant/components/recorder/translations/ca.json +++ b/homeassistant/components/recorder/translations/ca.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Hora d'inici de l'execuci\u00f3 actual", + "database_engine": "Motor de bases de dades", + "database_version": "Versi\u00f3 de la base de dades", "estimated_db_size": "Mida estimada de la base de dades (MiB)", "oldest_recorder_run": "Hora d'inici de l'execuci\u00f3 m\u00e9s antiga" } diff --git a/homeassistant/components/recorder/translations/hu.json b/homeassistant/components/recorder/translations/hu.json index 96bb02c9c20..323c1d489f1 100644 --- a/homeassistant/components/recorder/translations/hu.json +++ b/homeassistant/components/recorder/translations/hu.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Aktu\u00e1lis futtat\u00e1s kezd\u00e9si id\u0151pontja", + "database_engine": "Adatb\u00e1zis motor", + "database_version": "Adatb\u00e1zis verzi\u00f3", "estimated_db_size": "Az adatb\u00e1zis becs\u00fclt m\u00e9rete (MiB)", "oldest_recorder_run": "Legr\u00e9gebbi futtat\u00e1s kezd\u00e9si id\u0151pontja" } diff --git a/homeassistant/components/recorder/translations/pl.json b/homeassistant/components/recorder/translations/pl.json index a91f92207f2..77c5f2750f7 100644 --- a/homeassistant/components/recorder/translations/pl.json +++ b/homeassistant/components/recorder/translations/pl.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Pocz\u0105tek aktualnej sesji rejestratora", + "database_engine": "Silnik bazy danych", + "database_version": "Wersja bazy danych", "estimated_db_size": "Szacowany rozmiar bazy danych (MiB)", "oldest_recorder_run": "Pocz\u0105tek najstarszej sesji rejestratora" } diff --git a/homeassistant/components/recorder/translations/tr.json b/homeassistant/components/recorder/translations/tr.json index 1fdc0e408b3..27606e9ab27 100644 --- a/homeassistant/components/recorder/translations/tr.json +++ b/homeassistant/components/recorder/translations/tr.json @@ -2,6 +2,8 @@ "system_health": { "info": { "current_recorder_run": "Mevcut \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131", + "database_engine": "Veritaban\u0131 Altyap\u0131s\u0131", + "database_version": "Veritaban\u0131 S\u00fcr\u00fcm\u00fc", "estimated_db_size": "Tahmini Veritaban\u0131 Boyutu (MB)", "oldest_recorder_run": "En Eski \u00c7al\u0131\u015ft\u0131rma Ba\u015flang\u0131\u00e7 Zaman\u0131" } diff --git a/homeassistant/components/shelly/translations/tr.json b/homeassistant/components/shelly/translations/tr.json index 0a70a067690..fac805e5134 100644 --- a/homeassistant/components/shelly/translations/tr.json +++ b/homeassistant/components/shelly/translations/tr.json @@ -6,6 +6,7 @@ }, "error": { "cannot_connect": "Ba\u011flanma hatas\u0131", + "firmware_not_fully_provisioned": "Cihaz tam olarak sa\u011flanmad\u0131. L\u00fctfen Shelly deste\u011fiyle ileti\u015fime ge\u00e7in", "invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama", "unknown": "Beklenmeyen hata" }, diff --git a/homeassistant/components/steam_online/translations/ca.json b/homeassistant/components/steam_online/translations/ca.json index 699630c3732..a9491e2e502 100644 --- a/homeassistant/components/steam_online/translations/ca.json +++ b/homeassistant/components/steam_online/translations/ca.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Llista d'amics restringida: consulta la documentaci\u00f3 sobre com veure tots els amics" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/et.json b/homeassistant/components/steam_online/translations/et.json index 7c09fa1de91..62e38eb4088 100644 --- a/homeassistant/components/steam_online/translations/et.json +++ b/homeassistant/components/steam_online/translations/et.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "S\u00f5prade nimekiri on piiratud: vaata dokumentatsiooni kuidas n\u00e4ha k\u00f5iki teisi s\u00f5pru" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/hu.json b/homeassistant/components/steam_online/translations/hu.json index edff854ab52..a7d6c6a7de4 100644 --- a/homeassistant/components/steam_online/translations/hu.json +++ b/homeassistant/components/steam_online/translations/hu.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Bar\u00e1ti lista korl\u00e1tozott: K\u00e9rj\u00fck, olvassa el a dokument\u00e1ci\u00f3t arr\u00f3l, hogyan l\u00e1thatja az \u00f6sszes t\u00f6bbi bar\u00e1tot." + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/it.json b/homeassistant/components/steam_online/translations/it.json index edb7878234d..3f2e9481301 100644 --- a/homeassistant/components/steam_online/translations/it.json +++ b/homeassistant/components/steam_online/translations/it.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Elenco amici limitato: consultare la documentazione su come vedere tutti gli altri amici." + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/ja.json b/homeassistant/components/steam_online/translations/ja.json index 935c975c801..e138d46319b 100644 --- a/homeassistant/components/steam_online/translations/ja.json +++ b/homeassistant/components/steam_online/translations/ja.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "\u30d5\u30ec\u30f3\u30c9\u30ea\u30b9\u30c8\u306e\u5236\u9650: \u4ed6\u306e\u3059\u3079\u3066\u306e\u30d5\u30ec\u30f3\u30c9\u3092\u8868\u793a\u3059\u308b\u65b9\u6cd5\u306b\u3064\u3044\u3066\u306f\u3001\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u3092\u53c2\u7167\u3057\u3066\u304f\u3060\u3055\u3044" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/no.json b/homeassistant/components/steam_online/translations/no.json index 021218823f5..1b30669fad4 100644 --- a/homeassistant/components/steam_online/translations/no.json +++ b/homeassistant/components/steam_online/translations/no.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Begrenset venneliste: Se dokumentasjonen for hvordan du ser alle andre venner" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/pl.json b/homeassistant/components/steam_online/translations/pl.json index 370b997b3fc..3ceff9e442f 100644 --- a/homeassistant/components/steam_online/translations/pl.json +++ b/homeassistant/components/steam_online/translations/pl.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Lista znajomych ograniczona: zapoznaj si\u0119 z dokumentacj\u0105, aby dowiedzie\u0107 si\u0119, jak zobaczy\u0107 wszystkich innych znajomych" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/tr.json b/homeassistant/components/steam_online/translations/tr.json index c378c90855e..a1717857080 100644 --- a/homeassistant/components/steam_online/translations/tr.json +++ b/homeassistant/components/steam_online/translations/tr.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "Arkada\u015f listesi k\u0131s\u0131tland\u0131: L\u00fctfen di\u011fer t\u00fcm arkada\u015flar\u0131 nas\u0131l g\u00f6rece\u011finizle ilgili belgelere bak\u0131n" + }, "step": { "init": { "data": { diff --git a/homeassistant/components/steam_online/translations/zh-Hant.json b/homeassistant/components/steam_online/translations/zh-Hant.json index 17cff11185e..7de1d6f1a3c 100644 --- a/homeassistant/components/steam_online/translations/zh-Hant.json +++ b/homeassistant/components/steam_online/translations/zh-Hant.json @@ -25,6 +25,9 @@ } }, "options": { + "error": { + "unauthorized": "\u597d\u53cb\u5217\u8868\u53d7\u9650\uff1a\u8acb\u53c3\u8003\u6587\u4ef6\u8cc7\u6599\u4ee5\u4e86\u89e3\u5982\u4f55\u986f\u793a\u6240\u6709\u597d\u53cb\u3002" + }, "step": { "init": { "data": { From a2a691f232da063b93bada9941c7bf332769012a Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 25 May 2022 02:40:26 +0200 Subject: [PATCH 796/930] Improve Sensibo terminology (#72451) --- homeassistant/components/sensibo/number.py | 4 ++-- homeassistant/components/sensibo/select.py | 4 ++-- homeassistant/components/sensibo/sensor.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sensibo/number.py b/homeassistant/components/sensibo/number.py index 69d9237da7a..fc18e28f1a3 100644 --- a/homeassistant/components/sensibo/number.py +++ b/homeassistant/components/sensibo/number.py @@ -29,7 +29,7 @@ class SensiboNumberEntityDescription( """Class describing Sensibo Number entities.""" -NUMBER_TYPES = ( +DEVICE_NUMBER_TYPES = ( SensiboNumberEntityDescription( key="calibration_temp", remote_key="temperature", @@ -65,7 +65,7 @@ async def async_setup_entry( async_add_entities( SensiboNumber(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() - for description in NUMBER_TYPES + for description in DEVICE_NUMBER_TYPES ) diff --git a/homeassistant/components/sensibo/select.py b/homeassistant/components/sensibo/select.py index c442fbc1374..56b8fbac4fd 100644 --- a/homeassistant/components/sensibo/select.py +++ b/homeassistant/components/sensibo/select.py @@ -29,7 +29,7 @@ class SensiboSelectEntityDescription( """Class describing Sensibo Number entities.""" -SELECT_TYPES = ( +DEVICE_SELECT_TYPES = ( SensiboSelectEntityDescription( key="horizontalSwing", remote_key="horizontal_swing_mode", @@ -57,7 +57,7 @@ async def async_setup_entry( async_add_entities( SensiboSelect(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() - for description in SELECT_TYPES + for description in DEVICE_SELECT_TYPES if description.key in device_data.full_features ) diff --git a/homeassistant/components/sensibo/sensor.py b/homeassistant/components/sensibo/sensor.py index 307cfac6003..7ac871a61eb 100644 --- a/homeassistant/components/sensibo/sensor.py +++ b/homeassistant/components/sensibo/sensor.py @@ -99,7 +99,7 @@ MOTION_SENSOR_TYPES: tuple[SensiboMotionSensorEntityDescription, ...] = ( value_fn=lambda data: data.temperature, ), ) -DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( +PURE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = ( SensiboDeviceSensorEntityDescription( key="pm25", device_class=SensorDeviceClass.PM25, @@ -137,8 +137,8 @@ async def async_setup_entry( entities.extend( SensiboDeviceSensor(coordinator, device_id, description) for device_id, device_data in coordinator.data.parsed.items() - for description in DEVICE_SENSOR_TYPES - if getattr(device_data, description.key) is not None + for description in PURE_SENSOR_TYPES + if device_data.model == "pure" ) async_add_entities(entities) From b9baaf573c551390667408b85b078dc166ca1b66 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Tue, 24 May 2022 20:54:11 -0500 Subject: [PATCH 797/930] Bump Frontend to 20220524.0 (#72467) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index f6ede0676ca..b5fb12b92f6 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220523.0"], + "requirements": ["home-assistant-frontend==20220524.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a281d3cb30d..7dd302fea28 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220523.0 +home-assistant-frontend==20220524.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 222067c8028..22f104e1c36 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -825,7 +825,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220523.0 +home-assistant-frontend==20220524.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 423122e079f..e2c1d0ab918 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -592,7 +592,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220523.0 +home-assistant-frontend==20220524.0 # homeassistant.components.home_connect homeconnect==0.7.0 From 38ad1ef2337638edc1f3c111adcc4dfc7e5995aa Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 25 May 2022 04:00:36 +0200 Subject: [PATCH 798/930] Use My Home Assistant for OAuth2 redirect callbacks (#72449) Co-authored-by: Paulus Schoutsen --- homeassistant/helpers/config_entry_oauth2_flow.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index 2d45269a82c..d369b872eb9 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -37,6 +37,7 @@ DATA_IMPLEMENTATIONS = "oauth2_impl" DATA_PROVIDERS = "oauth2_providers" AUTH_CALLBACK_PATH = "/auth/external/callback" HEADER_FRONTEND_BASE = "HA-Frontend-Base" +MY_AUTH_CALLBACK_PATH = "https://my.home-assistant.io/redirect/oauth" CLOCK_OUT_OF_SYNC_MAX_SEC = 20 @@ -129,6 +130,9 @@ class LocalOAuth2Implementation(AbstractOAuth2Implementation): @property def redirect_uri(self) -> str: """Return the redirect uri.""" + if "my" in self.hass.config.components: + return MY_AUTH_CALLBACK_PATH + if (req := http.current_request.get()) is None: raise RuntimeError("No current request in context") From d0008683beb2a21d0ae0bf2ed47c59f8fb2a5530 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 May 2022 04:01:27 +0200 Subject: [PATCH 799/930] Use new project metadata format [PEP 621] (#72422) --- pyproject.toml | 41 ++++++++++++++++++++++++++++++++++++++++- setup.cfg | 36 +----------------------------------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 203c3ef1686..e0db5324d02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,46 @@ [build-system] -requires = ["setuptools~=60.5", "wheel~=0.37.1"] +requires = ["setuptools~=62.3", "wheel~=0.37.1"] build-backend = "setuptools.build_meta" +[project] +name = "homeassistant" +license = {text = "Apache-2.0"} +description = "Open-source home automation platform running on Python 3." +readme = "README.rst" +authors = [ + {name = "The Home Assistant Authors", email = "hello@home-assistant.io"} +] +keywords = ["home", "automation"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Home Automation", +] +dynamic = ["version", "requires-python", "dependencies"] + +[project.urls] +"Source Code" = "https://github.com/home-assistant/core" +"Bug Reports" = "https://github.com/home-assistant/core/issues" +"Docs: Dev" = "https://developers.home-assistant.io/" +"Discord" = "https://www.home-assistant.io/join-chat/" +"Forum" = "https://community.home-assistant.io/" + +[project.scripts] +hass = "homeassistant.__main__:main" + +[tool.setuptools] +platforms = ["any"] +zip-safe = false +include-package-data = true + +[tool.setuptools.packages.find] +include = ["homeassistant*"] + [tool.black] target-version = ["py39", "py310"] exclude = 'generated' diff --git a/setup.cfg b/setup.cfg index e8688293683..825a4407012 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,34 +1,8 @@ [metadata] -name = homeassistant -version = 2022.6.0.dev0 -author = The Home Assistant Authors -author_email = hello@home-assistant.io -license = Apache-2.0 -platforms = any -description = Open-source home automation platform running on Python 3. -long_description = file: README.rst -long_description_content_type = text/x-rst -keywords = home, automation +version = 2022.6.0.dev0 url = https://www.home-assistant.io/ -project_urls = - Source Code = https://github.com/home-assistant/core - Bug Reports = https://github.com/home-assistant/core/issues - Docs: Dev = https://developers.home-assistant.io/ - Discord = https://discordapp.com/invite/c5DvZ4e - Forum = https://community.home-assistant.io/ -classifier = - Development Status :: 4 - Beta - Intended Audience :: End Users/Desktop - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python :: 3.9 - Topic :: Home Automation [options] -packages = find: -zip_safe = False -include_package_data = True python_requires = >=3.9.0 install_requires = aiohttp==3.8.1 @@ -57,14 +31,6 @@ install_requires = voluptuous-serialize==2.5.0 yarl==1.7.2 -[options.packages.find] -include = - homeassistant* - -[options.entry_points] -console_scripts = - hass = homeassistant.__main__:main - [flake8] exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build max-complexity = 25 From 352b7e86afb96bbee4eb8edf42560a3beca1c363 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 24 May 2022 22:11:43 -0400 Subject: [PATCH 800/930] Move zwave_js node metadata comments to separate WS API cmd (#71513) * Move zwave_js node metadata comments to separate WS API cmd * fix pr --- homeassistant/components/zwave_js/api.py | 23 ++++++++++++++++++++++- tests/components/zwave_js/test_api.py | 21 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 15d3a68d4a4..a4fca557242 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -326,6 +326,7 @@ def async_register_api(hass: HomeAssistant) -> None: websocket_api.async_register_command(hass, websocket_network_status) websocket_api.async_register_command(hass, websocket_node_status) websocket_api.async_register_command(hass, websocket_node_metadata) + websocket_api.async_register_command(hass, websocket_node_comments) websocket_api.async_register_command(hass, websocket_add_node) websocket_api.async_register_command(hass, websocket_grant_security_classes) websocket_api.async_register_command(hass, websocket_validate_dsk_and_enter_pin) @@ -503,7 +504,6 @@ async def websocket_node_metadata( "wakeup": node.device_config.metadata.wakeup, "reset": node.device_config.metadata.reset, "device_database_url": node.device_database_url, - "comments": node.device_config.metadata.comments, } connection.send_result( msg[ID], @@ -511,6 +511,27 @@ async def websocket_node_metadata( ) +@websocket_api.websocket_command( + { + vol.Required(TYPE): "zwave_js/node_comments", + vol.Required(DEVICE_ID): str, + } +) +@websocket_api.async_response +@async_get_node +async def websocket_node_comments( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict, + node: Node, +) -> None: + """Get the comments of a Z-Wave JS node.""" + connection.send_result( + msg[ID], + {"comments": node.device_config.metadata.comments}, + ) + + @websocket_api.require_admin @websocket_api.websocket_command( { diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 58c26cd5797..2309e7b02bf 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -261,7 +261,6 @@ async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_ result["device_database_url"] == "https://devices.zwave-js.io/?jumpTo=0x0086:0x0002:0x0082:0.0" ) - assert result["comments"] == [{"level": "info", "text": "test"}] # Test getting non-existent node fails await ws_client.send_json( @@ -292,6 +291,26 @@ async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_ assert msg["error"]["code"] == ERR_NOT_LOADED +async def test_node_comments(hass, wallmote_central_scene, integration, hass_ws_client): + """Test the node comments websocket command.""" + ws_client = await hass_ws_client(hass) + + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_device({(DOMAIN, "3245146787-35")}) + assert device + + await ws_client.send_json( + { + ID: 3, + TYPE: "zwave_js/node_comments", + DEVICE_ID: device.id, + } + ) + msg = await ws_client.receive_json() + result = msg["result"] + assert result["comments"] == [{"level": "info", "text": "test"}] + + async def test_add_node( hass, nortek_thermostat_added_event, integration, client, hass_ws_client ): From e4cd04f936214d67c4feee00a92c1670ebb385cd Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 25 May 2022 05:42:37 +0200 Subject: [PATCH 801/930] Use length_util for here_travel_time (#72458) --- homeassistant/components/here_travel_time/__init__.py | 4 +++- tests/components/here_travel_time/test_sensor.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py index da091e0bdbf..2b9853c3b10 100644 --- a/homeassistant/components/here_travel_time/__init__.py +++ b/homeassistant/components/here_travel_time/__init__.py @@ -15,6 +15,7 @@ from homeassistant.const import ( CONF_MODE, CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL, + LENGTH_METERS, Platform, ) from homeassistant.core import HomeAssistant @@ -22,6 +23,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.location import find_coordinates from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import dt +from homeassistant.util.unit_system import IMPERIAL_SYSTEM from .const import ( ATTR_DESTINATION, @@ -178,7 +180,7 @@ class HereTravelTimeDataUpdateCoordinator(DataUpdateCoordinator): traffic_time = summary["trafficTime"] if self.config.units == CONF_UNIT_SYSTEM_IMPERIAL: # Convert to miles. - distance = distance / 1609.344 + distance = IMPERIAL_SYSTEM.length(distance, LENGTH_METERS) else: # Convert to kilometers distance = distance / 1000 diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 9a15f14f53e..dc9ba128c35 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -99,7 +99,7 @@ from tests.common import MockConfigEntry None, None, "30", - 14.852635608048994, + 14.852631013, 30.05, ), ( @@ -110,7 +110,7 @@ from tests.common import MockConfigEntry "08:00:00", None, "30", - 14.852635608048994, + 14.852631013, 30.05, ), ( From b8e9b8f540cad8a6d346eebba4ff7bb6230728ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 24 May 2022 17:45:27 -1000 Subject: [PATCH 802/930] Add number platform to Big Ass Fans (#72435) --- .coveragerc | 1 + homeassistant/components/baf/__init__.py | 1 + homeassistant/components/baf/number.py | 151 +++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 homeassistant/components/baf/number.py diff --git a/.coveragerc b/.coveragerc index ca6c57fb341..4e903d68aa5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -98,6 +98,7 @@ omit = homeassistant/components/baf/entity.py homeassistant/components/baf/fan.py homeassistant/components/baf/light.py + homeassistant/components/baf/number.py homeassistant/components/baf/sensor.py homeassistant/components/baf/switch.py homeassistant/components/baidu/tts.py diff --git a/homeassistant/components/baf/__init__.py b/homeassistant/components/baf/__init__.py index 7127228383a..601215d4c61 100644 --- a/homeassistant/components/baf/__init__.py +++ b/homeassistant/components/baf/__init__.py @@ -18,6 +18,7 @@ PLATFORMS: list[Platform] = [ Platform.CLIMATE, Platform.FAN, Platform.LIGHT, + Platform.NUMBER, Platform.SENSOR, Platform.SWITCH, ] diff --git a/homeassistant/components/baf/number.py b/homeassistant/components/baf/number.py new file mode 100644 index 00000000000..84358e79669 --- /dev/null +++ b/homeassistant/components/baf/number.py @@ -0,0 +1,151 @@ +"""Support for Big Ass Fans number.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Optional, cast + +from aiobafi6 import Device + +from homeassistant import config_entries +from homeassistant.components.number import ( + NumberEntity, + NumberEntityDescription, + NumberMode, +) +from homeassistant.const import TIME_SECONDS +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN, HALF_DAY_SECS, ONE_DAY_SECS, ONE_MIN_SECS, SPEED_RANGE +from .entity import BAFEntity +from .models import BAFData + + +@dataclass +class BAFNumberDescriptionMixin: + """Required values for BAF sensors.""" + + value_fn: Callable[[Device], int | None] + mode: NumberMode + + +@dataclass +class BAFNumberDescription(NumberEntityDescription, BAFNumberDescriptionMixin): + """Class describing BAF sensor entities.""" + + +FAN_NUMBER_DESCRIPTIONS = ( + BAFNumberDescription( + key="return_to_auto_timeout", + name="Return to Auto Timeout", + min_value=ONE_MIN_SECS, + max_value=HALF_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast(Optional[int], device.return_to_auto_timeout), + mode=NumberMode.SLIDER, + ), + BAFNumberDescription( + key="motion_sense_timeout", + name="Motion Sense Timeout", + min_value=ONE_MIN_SECS, + max_value=ONE_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast(Optional[int], device.motion_sense_timeout), + mode=NumberMode.SLIDER, + ), + BAFNumberDescription( + key="comfort_min_speed", + name="Auto Comfort Minimum Speed", + min_value=0, + max_value=SPEED_RANGE[1] - 1, + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[int], device.comfort_min_speed), + mode=NumberMode.BOX, + ), + BAFNumberDescription( + key="comfort_max_speed", + name="Auto Comfort Maximum Speed", + min_value=1, + max_value=SPEED_RANGE[1], + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[int], device.comfort_max_speed), + mode=NumberMode.BOX, + ), + BAFNumberDescription( + key="comfort_heat_assist_speed", + name="Auto Comfort Heat Assist Speed", + min_value=SPEED_RANGE[0], + max_value=SPEED_RANGE[1], + entity_category=EntityCategory.CONFIG, + value_fn=lambda device: cast(Optional[int], device.comfort_heat_assist_speed), + mode=NumberMode.BOX, + ), +) + +LIGHT_NUMBER_DESCRIPTIONS = ( + BAFNumberDescription( + key="light_return_to_auto_timeout", + name="Light Return to Auto Timeout", + min_value=ONE_MIN_SECS, + max_value=HALF_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast( + Optional[int], device.light_return_to_auto_timeout + ), + mode=NumberMode.SLIDER, + ), + BAFNumberDescription( + key="light_auto_motion_timeout", + name="Light Motion Sense Timeout", + min_value=ONE_MIN_SECS, + max_value=ONE_DAY_SECS, + entity_category=EntityCategory.CONFIG, + unit_of_measurement=TIME_SECONDS, + value_fn=lambda device: cast(Optional[int], device.light_auto_motion_timeout), + mode=NumberMode.SLIDER, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up BAF numbers.""" + data: BAFData = hass.data[DOMAIN][entry.entry_id] + device = data.device + descriptions: list[BAFNumberDescription] = [] + if device.has_fan: + descriptions.extend(FAN_NUMBER_DESCRIPTIONS) + if device.has_light: + descriptions.extend(LIGHT_NUMBER_DESCRIPTIONS) + async_add_entities(BAFNumber(device, description) for description in descriptions) + + +class BAFNumber(BAFEntity, NumberEntity): + """BAF number.""" + + entity_description: BAFNumberDescription + + def __init__(self, device: Device, description: BAFNumberDescription) -> None: + """Initialize the entity.""" + self.entity_description = description + super().__init__(device, f"{device.name} {description.name}") + self._attr_unique_id = f"{self._device.mac_address}-{description.key}" + self._attr_mode = description.mode + + @callback + def _async_update_attrs(self) -> None: + """Update attrs from device.""" + if (value := self.entity_description.value_fn(self._device)) is not None: + self._attr_value = float(value) + + async def async_set_value(self, value: float) -> None: + """Set the value.""" + setattr(self._device, self.entity_description.key, int(value)) From fbeaf200e4d18ed2816913d915beb14583acf18f Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 24 May 2022 22:46:27 -0500 Subject: [PATCH 803/930] Handle Plex searches in URL media_content_id format (#72462) --- homeassistant/components/plex/services.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/services.py b/homeassistant/components/plex/services.py index b7eff8043f8..0847583635d 100644 --- a/homeassistant/components/plex/services.py +++ b/homeassistant/components/plex/services.py @@ -123,8 +123,10 @@ def process_plex_payload( plex_url = URL(content_id) if plex_url.name: if len(plex_url.parts) == 2: - # The path contains a single item, will always be a ratingKey - content = int(plex_url.name) + if plex_url.name == "search": + content = {} + else: + content = int(plex_url.name) else: # For "special" items like radio stations content = plex_url.path @@ -132,7 +134,10 @@ def process_plex_payload( plex_server = get_plex_server(hass, plex_server_id=server_id) else: # Handle legacy payloads without server_id in URL host position - content = int(plex_url.host) # type: ignore[arg-type] + if plex_url.host == "search": + content = {} + else: + content = int(plex_url.host) # type: ignore[arg-type] extra_params = dict(plex_url.query) else: content = json.loads(content_id) From e60b247b515f391d7f13768544b344cb0cfd9948 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 25 May 2022 05:48:09 +0200 Subject: [PATCH 804/930] Simplify setup of deCONZ platforms (#72453) --- .../components/deconz/alarm_control_panel.py | 11 ++-- .../components/deconz/binary_sensor.py | 10 ++-- homeassistant/components/deconz/button.py | 11 ++-- homeassistant/components/deconz/climate.py | 10 ++-- homeassistant/components/deconz/cover.py | 10 ++-- .../components/deconz/deconz_event.py | 23 +++----- homeassistant/components/deconz/fan.py | 10 ++-- homeassistant/components/deconz/gateway.py | 35 +++++++++--- homeassistant/components/deconz/light.py | 20 +++---- homeassistant/components/deconz/lock.py | 20 +++---- homeassistant/components/deconz/number.py | 10 ++-- homeassistant/components/deconz/scene.py | 11 ++-- homeassistant/components/deconz/sensor.py | 10 ++-- homeassistant/components/deconz/siren.py | 10 ++-- homeassistant/components/deconz/switch.py | 10 ++-- tests/components/deconz/test_binary_sensor.py | 53 ++++++++++++++++++- 16 files changed, 130 insertions(+), 134 deletions(-) diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index e635b11972e..90c34da0f12 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -83,16 +83,11 @@ async def async_setup_entry( [DeconzAlarmControlPanel(sensor, gateway, alarm_system_id)] ) - config_entry.async_on_unload( - gateway.api.sensors.ancillary_control.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.ancillary_control, ) - for sensor_id in gateway.api.sensors.ancillary_control: - async_add_sensor(EventType.ADDED, sensor_id) - class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity): """Representation of a deCONZ alarm control panel.""" diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index e57f0bde3a6..d109fb8b34d 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -207,14 +207,10 @@ async def async_setup_entry( async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/button.py b/homeassistant/components/deconz/button.py index ed61750af6f..498c88a2351 100644 --- a/homeassistant/components/deconz/button.py +++ b/homeassistant/components/deconz/button.py @@ -65,16 +65,11 @@ async def async_setup_entry( for description in ENTITY_DESCRIPTIONS.get(PydeconzScene, []) ) - config_entry.async_on_unload( - gateway.api.scenes.subscribe( - async_add_scene, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_scene, + gateway.api.scenes, ) - for scene_id in gateway.api.scenes: - async_add_scene(EventType.ADDED, scene_id) - class DeconzButton(DeconzSceneMixin, ButtonEntity): """Representation of a deCONZ button entity.""" diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py index 1d57288d275..6887b4238d2 100644 --- a/homeassistant/components/deconz/climate.py +++ b/homeassistant/components/deconz/climate.py @@ -104,14 +104,10 @@ async def async_setup_entry( return async_add_entities([DeconzThermostat(climate, gateway)]) - config_entry.async_on_unload( - gateway.api.sensors.thermostat.subscribe( - gateway.evaluate_add_device(async_add_climate), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_climate, + gateway.api.sensors.thermostat, ) - for climate_id in gateway.api.sensors.thermostat: - async_add_climate(EventType.ADDED, climate_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 1931ac78389..9efbeac366f 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -43,14 +43,10 @@ async def async_setup_entry( cover = gateway.api.lights.covers[cover_id] async_add_entities([DeconzCover(cover, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.covers.subscribe( - gateway.evaluate_add_device(async_add_cover), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_cover, + gateway.api.lights.covers, ) - for cover_id in gateway.api.lights.covers: - async_add_cover(EventType.ADDED, cover_id) class DeconzCover(DeconzDevice, CoverEntity): diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index cfd60a63ec9..fa53ef1b5bc 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -58,29 +58,18 @@ async def async_setup_events(gateway: DeconzGateway) -> None: elif isinstance(sensor, AncillaryControl): new_event = DeconzAlarmEvent(sensor, gateway) - else: - return None - gateway.hass.async_create_task(new_event.async_update_device_registry()) gateway.events.append(new_event) - gateway.config_entry.async_on_unload( - gateway.api.sensors.ancillary_control.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.switch, ) - - gateway.config_entry.async_on_unload( - gateway.api.sensors.switch.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.ancillary_control, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) - @callback def async_unload_events(gateway: DeconzGateway) -> None: diff --git a/homeassistant/components/deconz/fan.py b/homeassistant/components/deconz/fan.py index b0d6d7755a2..ab9a1ba6f4a 100644 --- a/homeassistant/components/deconz/fan.py +++ b/homeassistant/components/deconz/fan.py @@ -48,14 +48,10 @@ async def async_setup_entry( fan = gateway.api.lights.fans[fan_id] async_add_entities([DeconzFan(fan, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.fans.subscribe( - gateway.evaluate_add_device(async_add_fan), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_fan, + gateway.api.lights.fans, ) - for fan_id in gateway.api.lights.fans: - async_add_fan(EventType.ADDED, fan_id) class DeconzFan(DeconzDevice, FanEntity): diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 1f6a45f3ad6..5890e372e66 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, cast import async_timeout from pydeconz import DeconzSession, errors +from pydeconz.interfaces.api import APIItems, GroupedAPIItems from pydeconz.models.event import EventType from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry @@ -105,9 +106,11 @@ class DeconzGateway: ) @callback - def evaluate_add_device( - self, add_device_callback: Callable[[EventType, str], None] - ) -> Callable[[EventType, str], None]: + def register_platform_add_device_callback( + self, + add_device_callback: Callable[[EventType, str], None], + deconz_device_interface: APIItems | GroupedAPIItems, + ) -> None: """Wrap add_device_callback to check allow_new_devices option.""" def async_add_device(event: EventType, device_id: str) -> None: @@ -117,11 +120,19 @@ class DeconzGateway: Device_refresh is expected to load new devices. """ if not self.option_allow_new_devices and not self.ignore_state_updates: - self.ignored_devices.add((add_device_callback, device_id)) + self.ignored_devices.add((async_add_device, device_id)) return add_device_callback(event, device_id) - return async_add_device + self.config_entry.async_on_unload( + deconz_device_interface.subscribe( + async_add_device, + EventType.ADDED, + ) + ) + + for device_id in deconz_device_interface: + add_device_callback(EventType.ADDED, device_id) @callback def load_ignored_devices(self) -> None: @@ -191,6 +202,8 @@ class DeconzGateway: """Manage entities affected by config entry options.""" deconz_ids = [] + # Allow CLIP sensors + if self.option_allow_clip_sensor: async_dispatcher_send(self.hass, self.signal_reload_clip_sensors) @@ -201,6 +214,8 @@ class DeconzGateway: if sensor.type.startswith("CLIP") ] + # Allow Groups + if self.option_allow_deconz_groups: if not self._option_allow_deconz_groups: async_dispatcher_send(self.hass, self.signal_reload_groups) @@ -209,13 +224,17 @@ class DeconzGateway: self._option_allow_deconz_groups = self.option_allow_deconz_groups + # Allow adding new devices + option_allow_new_devices = self.config_entry.options.get( CONF_ALLOW_NEW_DEVICES, DEFAULT_ALLOW_NEW_DEVICES ) - if option_allow_new_devices and not self.option_allow_new_devices: - self.load_ignored_devices() + if option_allow_new_devices != self.option_allow_new_devices: + self.option_allow_new_devices = option_allow_new_devices + if option_allow_new_devices: + self.load_ignored_devices() - self.option_allow_new_devices = option_allow_new_devices + # Remove entities based on above categories entity_registry = er.async_get(self.hass) diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 0f23ff02831..53773369176 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -93,23 +93,15 @@ async def async_setup_entry( async_add_entities([DeconzLight(light, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.lights.subscribe( - gateway.evaluate_add_device(async_add_light), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_light, + gateway.api.lights.lights, ) - for light_id in gateway.api.lights.lights: - async_add_light(EventType.ADDED, light_id) - config_entry.async_on_unload( - gateway.api.lights.fans.subscribe( - async_add_light, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_light, + gateway.api.lights.fans, ) - for light_id in gateway.api.lights.fans: - async_add_light(EventType.ADDED, light_id) @callback def async_add_group(_: EventType, group_id: str) -> None: diff --git a/homeassistant/components/deconz/lock.py b/homeassistant/components/deconz/lock.py index dc3da3cbe8c..78ccae30441 100644 --- a/homeassistant/components/deconz/lock.py +++ b/homeassistant/components/deconz/lock.py @@ -32,14 +32,10 @@ async def async_setup_entry( lock = gateway.api.lights.locks[lock_id] async_add_entities([DeconzLock(lock, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.locks.subscribe( - gateway.evaluate_add_device(async_add_lock_from_light), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_lock_from_light, + gateway.api.lights.locks, ) - for lock_id in gateway.api.lights.locks: - async_add_lock_from_light(EventType.ADDED, lock_id) @callback def async_add_lock_from_sensor(_: EventType, lock_id: str) -> None: @@ -47,14 +43,10 @@ async def async_setup_entry( lock = gateway.api.sensors.door_lock[lock_id] async_add_entities([DeconzLock(lock, gateway)]) - config_entry.async_on_unload( - gateway.api.sensors.door_lock.subscribe( - gateway.evaluate_add_device(async_add_lock_from_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_lock_from_sensor, + gateway.api.sensors.door_lock, ) - for lock_id in gateway.api.sensors.door_lock: - async_add_lock_from_sensor(EventType.ADDED, lock_id) class DeconzLock(DeconzDevice, LockEntity): diff --git a/homeassistant/components/deconz/number.py b/homeassistant/components/deconz/number.py index 5d555797372..a7bb014d76a 100644 --- a/homeassistant/components/deconz/number.py +++ b/homeassistant/components/deconz/number.py @@ -75,14 +75,10 @@ async def async_setup_entry( continue async_add_entities([DeconzNumber(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.presence.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors.presence, ) - for sensor_id in gateway.api.sensors.presence: - async_add_sensor(EventType.ADDED, sensor_id) class DeconzNumber(DeconzDevice, NumberEntity): diff --git a/homeassistant/components/deconz/scene.py b/homeassistant/components/deconz/scene.py index 28448da4f75..dfbb6ae828b 100644 --- a/homeassistant/components/deconz/scene.py +++ b/homeassistant/components/deconz/scene.py @@ -30,16 +30,11 @@ async def async_setup_entry( scene = gateway.api.scenes[scene_id] async_add_entities([DeconzScene(scene, gateway)]) - config_entry.async_on_unload( - gateway.api.scenes.subscribe( - async_add_scene, - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_scene, + gateway.api.scenes, ) - for scene_id in gateway.api.scenes: - async_add_scene(EventType.ADDED, scene_id) - class DeconzScene(DeconzSceneMixin, Scene): """Representation of a deCONZ scene.""" diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 021dcf7168a..663aadde73f 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -264,14 +264,10 @@ async def async_setup_entry( async_add_entities([DeconzSensor(sensor, gateway, description)]) - config_entry.async_on_unload( - gateway.api.sensors.subscribe( - gateway.evaluate_add_device(async_add_sensor), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_sensor, + gateway.api.sensors, ) - for sensor_id in gateway.api.sensors: - async_add_sensor(EventType.ADDED, sensor_id) @callback def async_reload_clip_sensors() -> None: diff --git a/homeassistant/components/deconz/siren.py b/homeassistant/components/deconz/siren.py index 0c5b3010626..8427b6ce75d 100644 --- a/homeassistant/components/deconz/siren.py +++ b/homeassistant/components/deconz/siren.py @@ -35,14 +35,10 @@ async def async_setup_entry( siren = gateway.api.lights.sirens[siren_id] async_add_entities([DeconzSiren(siren, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.sirens.subscribe( - gateway.evaluate_add_device(async_add_siren), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_siren, + gateway.api.lights.sirens, ) - for siren_id in gateway.api.lights.sirens: - async_add_siren(EventType.ADDED, siren_id) class DeconzSiren(DeconzDevice, SirenEntity): diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index d6e12365407..d54ff1f36ba 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -37,14 +37,10 @@ async def async_setup_entry( return async_add_entities([DeconzPowerPlug(switch, gateway)]) - config_entry.async_on_unload( - gateway.api.lights.lights.subscribe( - gateway.evaluate_add_device(async_add_switch), - EventType.ADDED, - ) + gateway.register_platform_add_device_callback( + async_add_switch, + gateway.api.lights.lights, ) - for switch_id in gateway.api.lights.lights: - async_add_switch(EventType.ADDED, switch_id) class DeconzPowerPlug(DeconzDevice, SwitchEntity): diff --git a/tests/components/deconz/test_binary_sensor.py b/tests/components/deconz/test_binary_sensor.py index 8d3eb105896..b6a21fb9fa6 100644 --- a/tests/components/deconz/test_binary_sensor.py +++ b/tests/components/deconz/test_binary_sensor.py @@ -636,7 +636,7 @@ async def test_add_new_binary_sensor(hass, aioclient_mock, mock_deconz_websocket assert hass.states.get("binary_sensor.presence_sensor").state == STATE_OFF -async def test_add_new_binary_sensor_ignored( +async def test_add_new_binary_sensor_ignored_load_entities_on_service_call( hass, aioclient_mock, mock_deconz_websocket ): """Test that adding a new binary sensor is not allowed.""" @@ -683,3 +683,54 @@ async def test_add_new_binary_sensor_ignored( assert len(hass.states.async_all()) == 1 assert hass.states.get("binary_sensor.presence_sensor") + + +async def test_add_new_binary_sensor_ignored_load_entities_on_options_change( + hass, aioclient_mock, mock_deconz_websocket +): + """Test that adding a new binary sensor is not allowed.""" + sensor = { + "name": "Presence sensor", + "type": "ZHAPresence", + "state": {"presence": False}, + "config": {"on": True, "reachable": True}, + "uniqueid": "00:00:00:00:00:00:00:00-00", + } + event_added_sensor = { + "t": "event", + "e": "added", + "r": "sensors", + "id": "1", + "sensor": sensor, + } + + config_entry = await setup_deconz_integration( + hass, + aioclient_mock, + options={CONF_MASTER_GATEWAY: True, CONF_ALLOW_NEW_DEVICES: False}, + ) + + assert len(hass.states.async_all()) == 0 + + await mock_deconz_websocket(data=event_added_sensor) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + assert not hass.states.get("binary_sensor.presence_sensor") + + entity_registry = er.async_get(hass) + assert ( + len(async_entries_for_config_entry(entity_registry, config_entry.entry_id)) == 0 + ) + + aioclient_mock.clear_requests() + data = {"config": {}, "groups": {}, "lights": {}, "sensors": {"1": sensor}} + mock_deconz_request(aioclient_mock, config_entry.data, data) + + hass.config_entries.async_update_entry( + config_entry, options={CONF_ALLOW_NEW_DEVICES: True} + ) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 1 + assert hass.states.get("binary_sensor.presence_sensor") From 76146cf57cb4211cb25f28a61d300c780104d943 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 25 May 2022 05:51:02 +0200 Subject: [PATCH 805/930] Fix deCONZ does not generate unique IDs for battery sensors (#72455) --- homeassistant/components/deconz/sensor.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 663aadde73f..2aff2b12448 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -246,13 +246,16 @@ async def async_setup_entry( def async_add_sensor(_: EventType, sensor_id: str) -> None: """Add sensor from deCONZ.""" sensor = gateway.api.sensors[sensor_id] + entities: list[DeconzSensor] = [] if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): return - if sensor.battery is None: + if sensor.battery is None and not sensor.type.startswith("CLIP"): DeconzBatteryTracker(sensor_id, gateway, async_add_entities) + known_entities = set(gateway.entities[DOMAIN]) + for description in ( ENTITY_DESCRIPTIONS.get(type(sensor), []) + SENSOR_DESCRIPTIONS ): @@ -262,7 +265,11 @@ async def async_setup_entry( ): continue - async_add_entities([DeconzSensor(sensor, gateway, description)]) + entity = DeconzSensor(sensor, gateway, description) + if entity.unique_id not in known_entities: + entities.append(entity) + + async_add_entities(entities) gateway.register_platform_add_device_callback( async_add_sensor, @@ -403,6 +410,7 @@ class DeconzBatteryTracker: """Update the device's state.""" if "battery" in self.sensor.changed_keys: self.unsub() - self.async_add_entities( - [DeconzSensor(self.sensor, self.gateway, SENSOR_DESCRIPTIONS[0])] - ) + known_entities = set(self.gateway.entities[DOMAIN]) + entity = DeconzSensor(self.sensor, self.gateway, SENSOR_DESCRIPTIONS[0]) + if entity.unique_id not in known_entities: + self.async_add_entities([entity]) From 209f37196e779d96da8451a31d712313e3ae29e2 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 25 May 2022 05:51:19 +0200 Subject: [PATCH 806/930] Adjust path to version info in Github issue template (#72431) --- .github/ISSUE_TEMPLATE.md | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- homeassistant/components/motioneye/__init__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5a64b0d5b73..2783972953b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -16,7 +16,7 @@ - Home Assistant Core release with the issue: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9667775e8e9..6b13f0980b1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -31,7 +31,7 @@ body: label: What version of Home Assistant Core has the issue? placeholder: core- description: > - Can be found in: [Configuration panel -> Info](https://my.home-assistant.io/redirect/info/). + Can be found in: [Settings -> About](https://my.home-assistant.io/redirect/info/). [![Open your Home Assistant instance and show your Home Assistant version information.](https://my.home-assistant.io/badges/info.svg)](https://my.home-assistant.io/redirect/info/) - type: input @@ -46,7 +46,7 @@ body: attributes: label: What type of installation are you running? description: > - Can be found in: [Configuration panel -> Info](https://my.home-assistant.io/redirect/info/). + Can be found in: [Settings -> About](https://my.home-assistant.io/redirect/info/). [![Open your Home Assistant instance and show your Home Assistant version information.](https://my.home-assistant.io/badges/info.svg)](https://my.home-assistant.io/redirect/info/) options: diff --git a/homeassistant/components/motioneye/__init__.py b/homeassistant/components/motioneye/__init__.py index 37a15931920..279cc8bde70 100644 --- a/homeassistant/components/motioneye/__init__.py +++ b/homeassistant/components/motioneye/__init__.py @@ -177,7 +177,7 @@ def async_generate_motioneye_webhook( except NoURLAvailableError: _LOGGER.warning( "Unable to get Home Assistant URL. Have you set the internal and/or " - "external URLs in Configuration -> General?" + "external URLs in Settings -> System -> Network?" ) return None From eb7a521232791f6728079d5583dfad8b651ef1a1 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 25 May 2022 01:50:25 -0400 Subject: [PATCH 807/930] Fix more typing for zwave_js (#72472) * Fix more typing for zwave_js * Revert one change * reduce lines * Fix tests --- .../components/zwave_js/device_action.py | 9 ++- .../zwave_js/device_automation_helpers.py | 2 + .../components/zwave_js/device_trigger.py | 2 +- .../components/zwave_js/diagnostics.py | 55 +++++++++++++------ .../zwave_js/discovery_data_template.py | 6 +- homeassistant/components/zwave_js/helpers.py | 11 +--- homeassistant/components/zwave_js/services.py | 21 ++++--- .../components/zwave_js/triggers/event.py | 34 +++++++----- .../zwave_js/triggers/value_updated.py | 3 +- tests/components/zwave_js/test_diagnostics.py | 11 +++- tests/components/zwave_js/test_helpers.py | 10 ---- 11 files changed, 97 insertions(+), 67 deletions(-) delete mode 100644 tests/components/zwave_js/test_helpers.py diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index f001accd196..2494cb8216d 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -146,7 +146,7 @@ async def async_get_actions( ) -> list[dict[str, Any]]: """List device actions for Z-Wave JS devices.""" registry = entity_registry.async_get(hass) - actions = [] + actions: list[dict] = [] node = async_get_node_from_device_id(hass, device_id) @@ -207,10 +207,13 @@ async def async_get_actions( # If the value has the meterType CC specific value, we can add a reset_meter # action for it if CC_SPECIFIC_METER_TYPE in value.metadata.cc_specific: - meter_endpoints[value.endpoint].setdefault( + endpoint_idx = value.endpoint + if endpoint_idx is None: + endpoint_idx = 0 + meter_endpoints[endpoint_idx].setdefault( CONF_ENTITY_ID, entry.entity_id ) - meter_endpoints[value.endpoint].setdefault(ATTR_METER_TYPE, set()).add( + meter_endpoints[endpoint_idx].setdefault(ATTR_METER_TYPE, set()).add( get_meter_type(value) ) diff --git a/homeassistant/components/zwave_js/device_automation_helpers.py b/homeassistant/components/zwave_js/device_automation_helpers.py index f17ddccf03c..25cce978df1 100644 --- a/homeassistant/components/zwave_js/device_automation_helpers.py +++ b/homeassistant/components/zwave_js/device_automation_helpers.py @@ -44,6 +44,8 @@ def generate_config_parameter_subtype(config_value: ConfigurationValue) -> str: """Generate the config parameter name used in a device automation subtype.""" parameter = str(config_value.property_) if config_value.property_key: + # Property keys for config values are always an int + assert isinstance(config_value.property_key, int) parameter = f"{parameter}[{hex(config_value.property_key)}]" return f"{parameter} ({config_value.property_name})" diff --git a/homeassistant/components/zwave_js/device_trigger.py b/homeassistant/components/zwave_js/device_trigger.py index 75a9647a5ca..ad860089d1d 100644 --- a/homeassistant/components/zwave_js/device_trigger.py +++ b/homeassistant/components/zwave_js/device_trigger.py @@ -254,7 +254,7 @@ async def async_get_triggers( dev_reg = device_registry.async_get(hass) node = async_get_node_from_device_id(hass, device_id, dev_reg) - triggers = [] + triggers: list[dict] = [] base_trigger = { CONF_PLATFORM: "device", CONF_DEVICE_ID: device_id, diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index dfb6661b5c0..4e1abe37b1b 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -1,7 +1,8 @@ """Provides diagnostics for Z-Wave JS.""" from __future__ import annotations -from dataclasses import astuple +from copy import deepcopy +from dataclasses import astuple, dataclass from typing import Any from zwave_js_server.client import Client @@ -20,25 +21,43 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DATA_CLIENT, DOMAIN from .helpers import ( - ZwaveValueID, get_home_and_node_id_from_device_entry, get_state_key_from_unique_id, get_value_id_from_unique_id, ) + +@dataclass +class ZwaveValueMatcher: + """Class to allow matching a Z-Wave Value.""" + + property_: str | int | None = None + command_class: int | None = None + endpoint: int | None = None + property_key: str | int | None = None + + def __post_init__(self) -> None: + """Post initialization check.""" + if all(val is None for val in astuple(self)): + raise ValueError("At least one of the fields must be set.") + + KEYS_TO_REDACT = {"homeId", "location"} VALUES_TO_REDACT = ( - ZwaveValueID(property_="userCode", command_class=CommandClass.USER_CODE), + ZwaveValueMatcher(property_="userCode", command_class=CommandClass.USER_CODE), ) def redact_value_of_zwave_value(zwave_value: ValueDataType) -> ValueDataType: """Redact value of a Z-Wave value.""" for value_to_redact in VALUES_TO_REDACT: - zwave_value_id = ZwaveValueID( - property_=zwave_value["property"], - command_class=CommandClass(zwave_value["commandClass"]), + command_class = None + if "commandClass" in zwave_value: + command_class = CommandClass(zwave_value["commandClass"]) + zwave_value_id = ZwaveValueMatcher( + property_=zwave_value.get("property"), + command_class=command_class, endpoint=zwave_value.get("endpoint"), property_key=zwave_value.get("propertyKey"), ) @@ -48,19 +67,19 @@ def redact_value_of_zwave_value(zwave_value: ValueDataType) -> ValueDataType: astuple(value_to_redact), astuple(zwave_value_id) ) ): - return {**zwave_value, "value": REDACTED} + redacted_value: ValueDataType = deepcopy(zwave_value) + redacted_value["value"] = REDACTED + return redacted_value return zwave_value def redact_node_state(node_state: NodeDataType) -> NodeDataType: """Redact node state.""" - return { - **node_state, - "values": [ - redact_value_of_zwave_value(zwave_value) - for zwave_value in node_state["values"] - ], - } + redacted_state: NodeDataType = deepcopy(node_state) + redacted_state["values"] = [ + redact_value_of_zwave_value(zwave_value) for zwave_value in node_state["values"] + ] + return redacted_state def get_device_entities( @@ -125,15 +144,17 @@ async def async_get_config_entry_diagnostics( async def async_get_device_diagnostics( hass: HomeAssistant, config_entry: ConfigEntry, device: dr.DeviceEntry -) -> NodeDataType: +) -> dict: """Return diagnostics for a device.""" client: Client = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT] identifiers = get_home_and_node_id_from_device_entry(device) node_id = identifiers[1] if identifiers else None - if node_id is None or node_id not in client.driver.controller.nodes: + assert (driver := client.driver) + if node_id is None or node_id not in driver.controller.nodes: raise ValueError(f"Node for device {device.id} can't be found") - node = client.driver.controller.nodes[node_id] + node = driver.controller.nodes[node_id] entities = get_device_entities(hass, node, device) + assert client.version return { "versionInfo": { "driverVersion": client.version.driver_version, diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 9dc90a43f3d..512cfafa63a 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -406,7 +406,7 @@ class TiltValueMix: class CoverTiltDataTemplate(BaseDiscoverySchemaDataTemplate, TiltValueMix): """Tilt data template class for Z-Wave Cover entities.""" - def resolve_data(self, value: ZwaveValue) -> dict[str, Any]: + def resolve_data(self, value: ZwaveValue) -> dict[str, ZwaveValue | None]: """Resolve helper class data for a discovered value.""" return {"tilt_value": self._get_value_from_id(value.node, self.tilt_value_id)} @@ -415,7 +415,9 @@ class CoverTiltDataTemplate(BaseDiscoverySchemaDataTemplate, TiltValueMix): return [resolved_data["tilt_value"]] @staticmethod - def current_tilt_value(resolved_data: dict[str, Any]) -> ZwaveValue | None: + def current_tilt_value( + resolved_data: dict[str, ZwaveValue | None] + ) -> ZwaveValue | None: """Get current tilt ZwaveValue from resolved data.""" return resolved_data["tilt_value"] diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py index 807ae0287eb..c047a3a9903 100644 --- a/homeassistant/components/zwave_js/helpers.py +++ b/homeassistant/components/zwave_js/helpers.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Callable -from dataclasses import astuple, dataclass +from dataclasses import dataclass import logging from typing import Any, cast @@ -48,16 +48,11 @@ from .const import ( class ZwaveValueID: """Class to represent a value ID.""" - property_: str | int | None = None - command_class: int | None = None + property_: str | int + command_class: int endpoint: int | None = None property_key: str | int | None = None - def __post_init__(self) -> None: - """Post initialization check.""" - if all(val is None for val in astuple(self)): - raise ValueError("At least one of the fields must be set.") - @callback def get_value_id_from_unique_id(unique_id: str) -> str | None: diff --git a/homeassistant/components/zwave_js/services.py b/homeassistant/components/zwave_js/services.py index af8f3c4c4e7..3b56e0a073c 100644 --- a/homeassistant/components/zwave_js/services.py +++ b/homeassistant/components/zwave_js/services.py @@ -400,7 +400,7 @@ class ZWaveServices: async def async_set_config_parameter(self, service: ServiceCall) -> None: """Set a config value on a node.""" - nodes = service.data[const.ATTR_NODES] + nodes: set[ZwaveNode] = service.data[const.ATTR_NODES] property_or_property_name = service.data[const.ATTR_CONFIG_PARAMETER] property_key = service.data.get(const.ATTR_CONFIG_PARAMETER_BITMASK) new_value = service.data[const.ATTR_CONFIG_VALUE] @@ -434,7 +434,7 @@ class ZWaveServices: self, service: ServiceCall ) -> None: """Bulk set multiple partial config values on a node.""" - nodes = service.data[const.ATTR_NODES] + nodes: set[ZwaveNode] = service.data[const.ATTR_NODES] property_ = service.data[const.ATTR_CONFIG_PARAMETER] new_value = service.data[const.ATTR_CONFIG_VALUE] @@ -531,7 +531,7 @@ class ZWaveServices: async def async_multicast_set_value(self, service: ServiceCall) -> None: """Set a value via multicast to multiple nodes.""" - nodes = service.data[const.ATTR_NODES] + nodes: set[ZwaveNode] = service.data[const.ATTR_NODES] broadcast: bool = service.data[const.ATTR_BROADCAST] options = service.data.get(const.ATTR_OPTIONS) @@ -559,13 +559,15 @@ class ZWaveServices: # If there are no nodes, we can assume there is only one config entry due to # schema validation and can use that to get the client, otherwise we can just # get the client from the node. - client: ZwaveClient = None - first_node: ZwaveNode = next((node for node in nodes), None) - if first_node: + client: ZwaveClient + first_node: ZwaveNode + try: + first_node = next(node for node in nodes) client = first_node.client - else: + except StopIteration: entry_id = self._hass.config_entries.async_entries(const.DOMAIN)[0].entry_id client = self._hass.data[const.DOMAIN][entry_id][const.DATA_CLIENT] + assert client.driver first_node = next( node for node in client.driver.controller.nodes.values() @@ -688,6 +690,9 @@ class ZWaveServices: _LOGGER.warning("Skipping entity %s as it has no value ID", entity_id) continue - endpoints.add(node.endpoints[node.values[value_id].endpoint]) + endpoint_idx = node.values[value_id].endpoint + endpoints.add( + node.endpoints[endpoint_idx if endpoint_idx is not None else 0] + ) await _async_invoke_cc_api(endpoints) diff --git a/homeassistant/components/zwave_js/triggers/event.py b/homeassistant/components/zwave_js/triggers/event.py index 83fd7570ab9..94afd1e9117 100644 --- a/homeassistant/components/zwave_js/triggers/event.py +++ b/homeassistant/components/zwave_js/triggers/event.py @@ -39,12 +39,6 @@ from homeassistant.helpers.typing import ConfigType # Platform type should be . PLATFORM_TYPE = f"{DOMAIN}.{__name__.rsplit('.', maxsplit=1)[-1]}" -EVENT_MODEL_MAP = { - "controller": CONTROLLER_EVENT_MODEL_MAP, - "driver": DRIVER_EVENT_MODEL_MAP, - "node": NODE_EVENT_MODEL_MAP, -} - def validate_non_node_event_source(obj: dict) -> dict: """Validate that a trigger for a non node event source has a config entry.""" @@ -58,7 +52,12 @@ def validate_event_name(obj: dict) -> dict: event_source = obj[ATTR_EVENT_SOURCE] event_name = obj[ATTR_EVENT] # the keys to the event source's model map are the event names - vol.In(EVENT_MODEL_MAP[event_source])(event_name) + if event_source == "controller": + vol.In(CONTROLLER_EVENT_MODEL_MAP)(event_name) + elif event_source == "driver": + vol.In(DRIVER_EVENT_MODEL_MAP)(event_name) + else: + vol.In(NODE_EVENT_MODEL_MAP)(event_name) return obj @@ -68,11 +67,16 @@ def validate_event_data(obj: dict) -> dict: if ATTR_EVENT_DATA not in obj: return obj - event_source = obj[ATTR_EVENT_SOURCE] - event_name = obj[ATTR_EVENT] - event_data = obj[ATTR_EVENT_DATA] + event_source: str = obj[ATTR_EVENT_SOURCE] + event_name: str = obj[ATTR_EVENT] + event_data: dict = obj[ATTR_EVENT_DATA] try: - EVENT_MODEL_MAP[event_source][event_name](**event_data) + if event_source == "controller": + CONTROLLER_EVENT_MODEL_MAP[event_name](**event_data) + elif event_source == "driver": + DRIVER_EVENT_MODEL_MAP[event_name](**event_data) + else: + NODE_EVENT_MODEL_MAP[event_name](**event_data) except ValidationError as exc: # Filter out required field errors if keys can be missing, and if there are # still errors, raise an exception @@ -90,7 +94,7 @@ TRIGGER_SCHEMA = vol.All( vol.Optional(ATTR_CONFIG_ENTRY_ID): str, vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]), vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, - vol.Required(ATTR_EVENT_SOURCE): vol.In(EVENT_MODEL_MAP), + vol.Required(ATTR_EVENT_SOURCE): vol.In(["controller", "driver", "node"]), vol.Required(ATTR_EVENT): cv.string, vol.Optional(ATTR_EVENT_DATA): dict, vol.Optional(ATTR_PARTIAL_DICT_MATCH, default=False): bool, @@ -200,11 +204,11 @@ async def async_attach_trigger( if not nodes: entry_id = config[ATTR_CONFIG_ENTRY_ID] client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT] + assert client.driver if event_source == "controller": - source = client.driver.controller + unsubs.append(client.driver.controller.on(event_name, async_on_event)) else: - source = client.driver - unsubs.append(source.on(event_name, async_on_event)) + unsubs.append(client.driver.on(event_name, async_on_event)) for node in nodes: driver = node.client.driver diff --git a/homeassistant/components/zwave_js/triggers/value_updated.py b/homeassistant/components/zwave_js/triggers/value_updated.py index 38a19eaa377..ac3aae1efed 100644 --- a/homeassistant/components/zwave_js/triggers/value_updated.py +++ b/homeassistant/components/zwave_js/triggers/value_updated.py @@ -5,7 +5,6 @@ import functools import voluptuous as vol from zwave_js_server.const import CommandClass -from zwave_js_server.event import Event from zwave_js_server.model.node import Node from zwave_js_server.model.value import Value, get_value_id @@ -108,7 +107,7 @@ async def async_attach_trigger( @callback def async_on_value_updated( - value: Value, device: dr.DeviceEntry, event: Event + value: Value, device: dr.DeviceEntry, event: dict ) -> None: """Handle value update.""" event_value: Value = event["value"] diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 8d00c9a2f64..3fe3bdfeb89 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -5,7 +5,10 @@ import pytest from zwave_js_server.event import Event from homeassistant.components.diagnostics.const import REDACTED -from homeassistant.components.zwave_js.diagnostics import async_get_device_diagnostics +from homeassistant.components.zwave_js.diagnostics import ( + ZwaveValueMatcher, + async_get_device_diagnostics, +) from homeassistant.components.zwave_js.discovery import async_discover_node_values from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.helpers.device_registry import async_get @@ -100,3 +103,9 @@ async def test_device_diagnostics_error(hass, integration): ) with pytest.raises(ValueError): await async_get_device_diagnostics(hass, integration, device) + + +async def test_empty_zwave_value_matcher(): + """Test empty ZwaveValueMatcher is invalid.""" + with pytest.raises(ValueError): + ZwaveValueMatcher() diff --git a/tests/components/zwave_js/test_helpers.py b/tests/components/zwave_js/test_helpers.py deleted file mode 100644 index 290c93fa084..00000000000 --- a/tests/components/zwave_js/test_helpers.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Test Z-Wave JS helpers module.""" -import pytest - -from homeassistant.components.zwave_js.helpers import ZwaveValueID - - -async def test_empty_zwave_value_id(): - """Test empty ZwaveValueID is invalid.""" - with pytest.raises(ValueError): - ZwaveValueID() From 4c8a77fbd4ba41688a2e0e71c1e814ff34647b7b Mon Sep 17 00:00:00 2001 From: Hans Oischinger Date: Wed, 25 May 2022 08:30:21 +0200 Subject: [PATCH 808/930] Bump PyViCare==2.16.2 (#72448) --- homeassistant/components/vicare/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/vicare/manifest.json b/homeassistant/components/vicare/manifest.json index 3b3058058b6..575ca35729b 100644 --- a/homeassistant/components/vicare/manifest.json +++ b/homeassistant/components/vicare/manifest.json @@ -3,7 +3,7 @@ "name": "Viessmann ViCare", "documentation": "https://www.home-assistant.io/integrations/vicare", "codeowners": ["@oischinger"], - "requirements": ["PyViCare==2.16.1"], + "requirements": ["PyViCare==2.16.2"], "iot_class": "cloud_polling", "config_flow": true, "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 22f104e1c36..38cc91c2574 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -44,7 +44,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.6 # homeassistant.components.vicare -PyViCare==2.16.1 +PyViCare==2.16.2 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.13.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e2c1d0ab918..cefa305a5b9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -40,7 +40,7 @@ PyTransportNSW==0.1.1 PyTurboJPEG==1.6.6 # homeassistant.components.vicare -PyViCare==2.16.1 +PyViCare==2.16.2 # homeassistant.components.xiaomi_aqara PyXiaomiGateway==0.13.4 From 9514f491f0cc6bc6e813f8d5b8280c62784f7d33 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Wed, 25 May 2022 08:38:47 +0200 Subject: [PATCH 809/930] Add netgear speed test sensor (#72215) * implement speed_test * fix units * restore last speedtest result * fix import * fix restore state is None * fix styling * fix mypy * Use newer notation * correct unit * fix typing * fix pylint * fix issort * use RestoreSensor * fix import * fix sensor restore * do not extend SensorEntity * fix mypy * fix typing 2 --- homeassistant/components/netgear/__init__.py | 14 +++++ homeassistant/components/netgear/const.py | 1 + homeassistant/components/netgear/router.py | 7 +++ homeassistant/components/netgear/sensor.py | 63 ++++++++++++++++++-- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py index 518a9051847..7a4d0e7a8cd 100644 --- a/homeassistant/components/netgear/__init__.py +++ b/homeassistant/components/netgear/__init__.py @@ -15,6 +15,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import ( DOMAIN, KEY_COORDINATOR, + KEY_COORDINATOR_SPEED, KEY_COORDINATOR_TRAFFIC, KEY_ROUTER, MODE_ROUTER, @@ -26,6 +27,7 @@ from .router import NetgearRouter _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=30) +SPEED_TEST_INTERVAL = timedelta(seconds=1800) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -78,6 +80,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Fetch data from the router.""" return await router.async_get_traffic_meter() + async def async_update_speed_test() -> dict[str, Any] | None: + """Fetch data from the router.""" + return await router.async_get_speed_test() + # Create update coordinators coordinator = DataUpdateCoordinator( hass, @@ -93,6 +99,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: update_method=async_update_traffic_meter, update_interval=SCAN_INTERVAL, ) + coordinator_speed_test = DataUpdateCoordinator( + hass, + _LOGGER, + name=f"{router.device_name} Speed test", + update_method=async_update_speed_test, + update_interval=SPEED_TEST_INTERVAL, + ) if router.mode == MODE_ROUTER: await coordinator.async_config_entry_first_refresh() @@ -102,6 +115,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: KEY_ROUTER: router, KEY_COORDINATOR: coordinator, KEY_COORDINATOR_TRAFFIC: coordinator_traffic_meter, + KEY_COORDINATOR_SPEED: coordinator_speed_test, } hass.config_entries.async_setup_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/netgear/const.py b/homeassistant/components/netgear/const.py index bc4f37114fd..936777a7961 100644 --- a/homeassistant/components/netgear/const.py +++ b/homeassistant/components/netgear/const.py @@ -12,6 +12,7 @@ CONF_CONSIDER_HOME = "consider_home" KEY_ROUTER = "router" KEY_COORDINATOR = "coordinator" KEY_COORDINATOR_TRAFFIC = "coordinator_traffic" +KEY_COORDINATOR_SPEED = "coordinator_speed" DEFAULT_CONSIDER_HOME = timedelta(seconds=180) DEFAULT_NAME = "Netgear router" diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index 6fb44e569d6..301906f22b6 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -208,6 +208,13 @@ class NetgearRouter: async with self._api_lock: return await self.hass.async_add_executor_job(self._api.get_traffic_meter) + async def async_get_speed_test(self) -> dict[str, Any] | None: + """Perform a speed test and get the results from the router.""" + async with self._api_lock: + return await self.hass.async_add_executor_job( + self._api.get_new_speed_test_result + ) + async def async_allow_block_device(self, mac: str, allow_block: str) -> None: """Allow or block a device connected to the router.""" async with self._api_lock: diff --git a/homeassistant/components/netgear/sensor.py b/homeassistant/components/netgear/sensor.py index 16c63a8cdcb..9dec1ab3390 100644 --- a/homeassistant/components/netgear/sensor.py +++ b/homeassistant/components/netgear/sensor.py @@ -1,20 +1,37 @@ """Support for Netgear routers.""" +from __future__ import annotations + from collections.abc import Callable from dataclasses import dataclass +from datetime import date, datetime +from decimal import Decimal from homeassistant.components.sensor import ( + RestoreSensor, SensorDeviceClass, SensorEntity, SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import DATA_MEGABYTES, PERCENTAGE +from homeassistant.const import ( + DATA_MEGABYTES, + DATA_RATE_MEGABITS_PER_SECOND, + PERCENTAGE, + TIME_MILLISECONDS, +) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import DOMAIN, KEY_COORDINATOR, KEY_COORDINATOR_TRAFFIC, KEY_ROUTER +from .const import ( + DOMAIN, + KEY_COORDINATOR, + KEY_COORDINATOR_SPEED, + KEY_COORDINATOR_TRAFFIC, + KEY_ROUTER, +) from .router import NetgearDeviceEntity, NetgearRouter, NetgearRouterEntity SENSOR_TYPES = { @@ -200,6 +217,30 @@ SENSOR_TRAFFIC_TYPES = [ ), ] +SENSOR_SPEED_TYPES = [ + NetgearSensorEntityDescription( + key="NewOOKLAUplinkBandwidth", + name="Uplink Bandwidth", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + icon="mdi:upload", + ), + NetgearSensorEntityDescription( + key="NewOOKLADownlinkBandwidth", + name="Downlink Bandwidth", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=DATA_RATE_MEGABITS_PER_SECOND, + icon="mdi:download", + ), + NetgearSensorEntityDescription( + key="AveragePing", + name="Average Ping", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=TIME_MILLISECONDS, + icon="mdi:wan", + ), +] + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -208,6 +249,7 @@ async def async_setup_entry( router = hass.data[DOMAIN][entry.entry_id][KEY_ROUTER] coordinator = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR] coordinator_traffic = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_TRAFFIC] + coordinator_speed = hass.data[DOMAIN][entry.entry_id][KEY_COORDINATOR_SPEED] # Router entities router_entities = [] @@ -217,6 +259,11 @@ async def async_setup_entry( NetgearRouterSensorEntity(coordinator_traffic, router, description) ) + for description in SENSOR_SPEED_TYPES: + router_entities.append( + NetgearRouterSensorEntity(coordinator_speed, router, description) + ) + async_add_entities(router_entities) # Entities per network device @@ -288,7 +335,7 @@ class NetgearSensorEntity(NetgearDeviceEntity, SensorEntity): self._state = self._device[self._attribute] -class NetgearRouterSensorEntity(NetgearRouterEntity, SensorEntity): +class NetgearRouterSensorEntity(NetgearRouterEntity, RestoreSensor): """Representation of a device connected to a Netgear router.""" _attr_entity_registry_enabled_default = False @@ -306,7 +353,7 @@ class NetgearRouterSensorEntity(NetgearRouterEntity, SensorEntity): self._name = f"{router.device_name} {entity_description.name}" self._unique_id = f"{router.serial_number}-{entity_description.key}-{entity_description.index}" - self._value = None + self._value: StateType | date | datetime | Decimal = None self.async_update_device() @property @@ -314,6 +361,14 @@ class NetgearRouterSensorEntity(NetgearRouterEntity, SensorEntity): """Return the state of the sensor.""" return self._value + async def async_added_to_hass(self) -> None: + """Handle entity which will be added.""" + await super().async_added_to_hass() + if self.coordinator.data is None: + sensor_data = await self.async_get_last_sensor_data() + if sensor_data is not None: + self._value = sensor_data.native_value + @callback def async_update_device(self) -> None: """Update the Netgear device.""" From 5dfeb1e02a5bb1153086aa1ca2af6f8a3507d7f2 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 25 May 2022 08:39:05 +0200 Subject: [PATCH 810/930] Allow removing devices in devolo_home_control (#72190) Allow removing devices --- .../devolo_home_control/__init__.py | 8 ++++ .../devolo_home_control/test_init.py | 43 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/devolo_home_control/__init__.py b/homeassistant/components/devolo_home_control/__init__.py index 46b8f9dcaea..b79d0a5c8fe 100644 --- a/homeassistant/components/devolo_home_control/__init__.py +++ b/homeassistant/components/devolo_home_control/__init__.py @@ -15,6 +15,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.device_registry import DeviceEntry from .const import ( CONF_MYDEVOLO, @@ -92,6 +93,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + return True + + def configure_mydevolo(conf: dict[str, Any] | MappingProxyType[str, Any]) -> Mydevolo: """Configure mydevolo.""" mydevolo = Mydevolo() diff --git a/tests/components/devolo_home_control/test_init.py b/tests/components/devolo_home_control/test_init.py index 311da47aac0..f12ea9486b4 100644 --- a/tests/components/devolo_home_control/test_init.py +++ b/tests/components/devolo_home_control/test_init.py @@ -1,13 +1,20 @@ """Tests for the devolo Home Control integration.""" +from collections.abc import Awaitable, Callable from unittest.mock import patch +from aiohttp import ClientWebSocketResponse from devolo_home_control_api.exceptions.gateway import GatewayOfflineError import pytest +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.devolo_home_control import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component -from tests.components.devolo_home_control import configure_integration +from . import configure_integration +from .mocks import HomeControlMock, HomeControlMockBinarySensor async def test_setup_entry(hass: HomeAssistant, mock_zeroconf): @@ -53,3 +60,37 @@ async def test_unload_entry(hass: HomeAssistant): await hass.async_block_till_done() await hass.config_entries.async_unload(entry.entry_id) assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_remove_device( + hass: HomeAssistant, + hass_ws_client: Callable[[HomeAssistant], Awaitable[ClientWebSocketResponse]], +): + """Test removing a device.""" + assert await async_setup_component(hass, "config", {}) + entry = configure_integration(hass) + test_gateway = HomeControlMockBinarySensor() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + device_registry = dr.async_get(hass) + device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "Test")}) + assert device_entry + + client = await hass_ws_client(hass) + await client.send_json( + { + "id": 1, + "type": "config/device_registry/remove_config_entry", + "config_entry_id": entry.entry_id, + "device_id": device_entry.id, + } + ) + response = await client.receive_json() + assert response["success"] + assert device_registry.async_get_device(identifiers={(DOMAIN, "Test")}) is None + assert hass.states.get(f"{BINARY_SENSOR_DOMAIN}.test") is None From c1ddde376480b7353503a48c5765f4829a6e9d25 Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Wed, 25 May 2022 08:44:08 +0200 Subject: [PATCH 811/930] Check if attributes are present in new_state before accessing them (#71967) * Check if attributes are present in new_state before accessing them. * Early return if new state is None|Unknown|Unavailable * Removed whitespace at line endings. +black run * Update test for coverage --- homeassistant/components/integration/sensor.py | 15 +++++++++------ tests/components/integration/test_sensor.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/integration/sensor.py b/homeassistant/components/integration/sensor.py index 8cf5da5bc7c..5c982c6ec5e 100644 --- a/homeassistant/components/integration/sensor.py +++ b/homeassistant/components/integration/sensor.py @@ -191,11 +191,16 @@ class IntegrationSensor(RestoreEntity, SensorEntity): old_state = event.data.get("old_state") new_state = event.data.get("new_state") + if new_state is None or new_state.state in ( + STATE_UNKNOWN, + STATE_UNAVAILABLE, + ): + return + # We may want to update our state before an early return, # based on the source sensor's unit_of_measurement # or device_class. update_state = False - unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if unit is not None: new_unit_of_measurement = self._unit_template.format(unit) @@ -214,11 +219,9 @@ class IntegrationSensor(RestoreEntity, SensorEntity): if update_state: self.async_write_ha_state() - if ( - old_state is None - or new_state is None - or old_state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE) - or new_state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE) + if old_state is None or old_state.state in ( + STATE_UNKNOWN, + STATE_UNAVAILABLE, ): return diff --git a/tests/components/integration/test_sensor.py b/tests/components/integration/test_sensor.py index 90c9e6f0fbc..e4173d62eb4 100644 --- a/tests/components/integration/test_sensor.py +++ b/tests/components/integration/test_sensor.py @@ -9,6 +9,7 @@ from homeassistant.const import ( ENERGY_WATT_HOUR, POWER_KILO_WATT, POWER_WATT, + STATE_UNAVAILABLE, STATE_UNKNOWN, TIME_SECONDS, ) @@ -350,6 +351,15 @@ async def test_units(hass): # they became valid assert state.attributes.get("unit_of_measurement") == ENERGY_WATT_HOUR + # When source state goes to None / Unknown, expect an early exit without + # changes to the state or unit_of_measurement + hass.states.async_set(entity_id, STATE_UNAVAILABLE, None) + await hass.async_block_till_done() + + new_state = hass.states.get("sensor.integration") + assert state == new_state + assert state.attributes.get("unit_of_measurement") == ENERGY_WATT_HOUR + async def test_device_class(hass): """Test integration sensor units using a power source.""" From a98af2ad58f53fc4bce68fe446044f275b82778a Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Wed, 25 May 2022 00:51:58 -0600 Subject: [PATCH 812/930] Better handling of balboa spa connection (#71909) * Better handling of balboa spa connection * Send a single message for keep alive task rather than multiple --- homeassistant/components/balboa/__init__.py | 39 ++++++++++++------- .../components/balboa/config_flow.py | 33 +++++++++------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/balboa/__init__.py b/homeassistant/components/balboa/__init__.py index 731f7b2c2d1..60989ecc6d6 100644 --- a/homeassistant/components/balboa/__init__.py +++ b/homeassistant/components/balboa/__init__.py @@ -22,6 +22,7 @@ from .const import ( SIGNAL_UPDATE, ) +KEEP_ALIVE_INTERVAL = timedelta(minutes=1) SYNC_TIME_INTERVAL = timedelta(days=1) @@ -31,7 +32,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Attempting to connect to %s", host) spa = BalboaSpaWifi(host) - connected = await spa.connect() if not connected: _LOGGER.error("Failed to connect to spa at %s", host) @@ -39,11 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {})[entry.entry_id] = spa - # send config requests, and then listen until we are configured. - await spa.send_mod_ident_req() - await spa.send_panel_req(0, 1) - - async def _async_balboa_update_cb(): + async def _async_balboa_update_cb() -> None: """Primary update callback called from pybalboa.""" _LOGGER.debug("Primary update callback triggered") async_dispatcher_send(hass, SIGNAL_UPDATE.format(entry.entry_id)) @@ -52,13 +48,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: spa.new_data_cb = _async_balboa_update_cb _LOGGER.debug("Starting listener and monitor tasks") - asyncio.create_task(spa.listen()) + monitoring_tasks = [asyncio.create_task(spa.listen())] await spa.spa_configured() - asyncio.create_task(spa.check_connection_status()) + monitoring_tasks.append(asyncio.create_task(spa.check_connection_status())) + + def stop_monitoring() -> None: + """Stop monitoring the spa connection.""" + _LOGGER.debug("Canceling listener and monitor tasks") + for task in monitoring_tasks: + task.cancel() + + entry.async_on_unload(stop_monitoring) # At this point we have a configured spa. hass.config_entries.async_setup_platforms(entry, PLATFORMS) + async def keep_alive(now: datetime) -> None: + """Keep alive task.""" + _LOGGER.debug("Keep alive") + await spa.send_mod_ident_req() + + entry.async_on_unload( + async_track_time_interval(hass, keep_alive, KEEP_ALIVE_INTERVAL) + ) + # call update_listener on startup and for options change as well. await async_setup_time_sync(hass, entry) entry.async_on_unload(entry.add_update_listener(update_listener)) @@ -68,14 +81,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" - _LOGGER.debug("Disconnecting from spa") - spa = hass.data[DOMAIN][entry.entry_id] - await spa.disconnect() + spa: BalboaSpaWifi = hass.data[DOMAIN][entry.entry_id] if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): hass.data[DOMAIN].pop(entry.entry_id) + await spa.disconnect() + return unload_ok @@ -90,9 +103,9 @@ async def async_setup_time_sync(hass: HomeAssistant, entry: ConfigEntry) -> None return _LOGGER.debug("Setting up daily time sync") - spa = hass.data[DOMAIN][entry.entry_id] + spa: BalboaSpaWifi = hass.data[DOMAIN][entry.entry_id] - async def sync_time(now: datetime): + async def sync_time(now: datetime) -> None: _LOGGER.debug("Syncing time with Home Assistant") await spa.set_time(time.strptime(str(dt_util.now()), "%Y-%m-%d %H:%M:%S.%f%z")) diff --git a/homeassistant/components/balboa/config_flow.py b/homeassistant/components/balboa/config_flow.py index 42895e5ccd6..c0301bc9892 100644 --- a/homeassistant/components/balboa/config_flow.py +++ b/homeassistant/components/balboa/config_flow.py @@ -1,12 +1,16 @@ """Config flow for Balboa Spa Client integration.""" +from __future__ import annotations + import asyncio +from typing import Any from pybalboa import BalboaSpaWifi import voluptuous as vol -from homeassistant import config_entries, core, exceptions +from homeassistant import config_entries, exceptions from homeassistant.const import CONF_HOST from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.device_registry import format_mac from .const import _LOGGER, CONF_SYNC_TIME, DOMAIN @@ -14,9 +18,8 @@ from .const import _LOGGER, CONF_SYNC_TIME, DOMAIN DATA_SCHEMA = vol.Schema({vol.Required(CONF_HOST): str}) -async def validate_input(hass: core.HomeAssistant, data): +async def validate_input(data: dict[str, Any]) -> dict[str, str]: """Validate the user input allows us to connect.""" - _LOGGER.debug("Attempting to connect to %s", data[CONF_HOST]) spa = BalboaSpaWifi(data[CONF_HOST]) connected = await spa.connect() @@ -24,16 +27,12 @@ async def validate_input(hass: core.HomeAssistant, data): if not connected: raise CannotConnect - # send config requests, and then listen until we are configured. - await spa.send_mod_ident_req() - await spa.send_panel_req(0, 1) - - asyncio.create_task(spa.listen()) - + task = asyncio.create_task(spa.listen()) await spa.spa_configured() mac_addr = format_mac(spa.get_macaddr()) model = spa.get_model_name() + task.cancel() await spa.disconnect() return {"title": model, "formatted_mac": mac_addr} @@ -46,17 +45,21 @@ class BalboaSpaClientFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> config_entries.OptionsFlow: """Get the options flow for this handler.""" return BalboaSpaClientOptionsFlowHandler(config_entry) - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initialized by the user.""" errors = {} if user_input is not None: self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]}) try: - info = await validate_input(self.hass, user_input) + info = await validate_input(user_input) except CannotConnect: errors["base"] = "cannot_connect" except Exception: # pylint: disable=broad-except @@ -79,11 +82,13 @@ class CannotConnect(exceptions.HomeAssistantError): class BalboaSpaClientOptionsFlowHandler(config_entries.OptionsFlow): """Handle Balboa Spa Client options.""" - def __init__(self, config_entry): + def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize Balboa Spa Client options flow.""" self.config_entry = config_entry - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Manage Balboa Spa Client options.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) From 9591d5366efc783c580f6e5ab66bfa8ae7c7fc4e Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 25 May 2022 02:57:26 -0400 Subject: [PATCH 813/930] Add config entities for the Aqara P1 motion sensor to ZHA (#72466) * initial work for configurable detection interval * update opple cluster channel * detection interval * motion sensitivity * only set the init attributes for the right device * add trigger indicator configuration entity --- .../zha/core/channels/manufacturerspecific.py | 26 ++++++++++++++++++- homeassistant/components/zha/number.py | 11 ++++++++ homeassistant/components/zha/select.py | 17 ++++++++++++ homeassistant/components/zha/switch.py | 13 ++++++++-- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/core/channels/manufacturerspecific.py b/homeassistant/components/zha/core/channels/manufacturerspecific.py index f31c7f51371..3bcab38e026 100644 --- a/homeassistant/components/zha/core/channels/manufacturerspecific.py +++ b/homeassistant/components/zha/core/channels/manufacturerspecific.py @@ -1,7 +1,9 @@ """Manufacturer specific channels module for Zigbee Home Automation.""" +import logging + from homeassistant.core import callback -from .. import registries +from .. import registries, typing as zha_typing from ..const import ( ATTR_ATTRIBUTE_ID, ATTR_ATTRIBUTE_NAME, @@ -14,6 +16,8 @@ from ..const import ( ) from .base import ClientChannel, ZigbeeChannel +_LOGGER = logging.getLogger(__name__) + @registries.ZIGBEE_CHANNEL_REGISTRY.register(registries.SMARTTHINGS_HUMIDITY_CLUSTER) class SmartThingsHumidity(ZigbeeChannel): @@ -50,6 +54,26 @@ class OppleRemote(ZigbeeChannel): REPORT_CONFIG = [] + def __init__( + self, cluster: zha_typing.ZigpyClusterType, ch_pool: zha_typing.ChannelPoolType + ) -> None: + """Initialize Opple channel.""" + super().__init__(cluster, ch_pool) + if self.cluster.endpoint.model == "lumi.motion.ac02": + self.ZCL_INIT_ATTRS = { # pylint: disable=C0103 + "detection_interval": True, + "motion_sensitivity": True, + "trigger_indicator": True, + } + + async def async_initialize_channel_specific(self, from_cache: bool) -> None: + """Initialize channel specific.""" + if self.cluster.endpoint.model == "lumi.motion.ac02": + interval = self.cluster.get("detection_interval", self.cluster.get(0x0102)) + if interval is not None: + self.debug("Loaded detection interval at startup: %s", interval) + self.cluster.endpoint.ias_zone.reset_s = int(interval) + @registries.ZIGBEE_CHANNEL_REGISTRY.register( registries.SMARTTHINGS_ACCELERATION_CLUSTER diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index bece4bc894d..216b9974df6 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -433,6 +433,17 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity): _LOGGER.debug("read value=%s", value) +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"}) +class AqaraMotionDetectionInterval( + ZHANumberConfigurationEntity, id_suffix="detection_interval" +): + """Representation of a ZHA on off transition time configuration entity.""" + + _attr_min_value: float = 2 + _attr_max_value: float = 65535 + _zcl_attribute: str = "detection_interval" + + @CONFIG_DIAGNOSTIC_MATCH(channel_names=CHANNEL_LEVEL) class OnOffTransitionTimeConfigurationEntity( ZHANumberConfigurationEntity, id_suffix="on_off_transition_time" diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index afa2ee18da9..8714d804790 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -5,6 +5,7 @@ from enum import Enum import functools import logging +from zigpy import types from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.clusters.security import IasWd @@ -208,3 +209,19 @@ class ZHAStartupOnOffSelectEntity( _select_attr = "start_up_on_off" _enum: Enum = OnOff.StartUpOnOff + + +class AqaraMotionSensitivities(types.enum8): + """Aqara motion sensitivities.""" + + Low = 0x01 + Medium = 0x02 + High = 0x03 + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"}) +class AqaraMotionSensitivity(ZCLEnumSelectEntity, id_suffix="motion_sensitivity"): + """Representation of a ZHA on off transition time configuration entity.""" + + _select_attr = "motion_sensitivity" + _enum: Enum = AqaraMotionSensitivities diff --git a/homeassistant/components/zha/switch.py b/homeassistant/components/zha/switch.py index 800c42eb932..d9199ed77c8 100644 --- a/homeassistant/components/zha/switch.py +++ b/homeassistant/components/zha/switch.py @@ -155,6 +155,7 @@ class SwitchGroup(ZhaGroupEntity, SwitchEntity): class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): """Representation of a ZHA switch configuration entity.""" + _attr_entity_category = EntityCategory.CONFIG _zcl_attribute: str _zcl_inverter_attribute: str = "" @@ -260,8 +261,16 @@ class ZHASwitchConfigurationEntity(ZhaEntity, SwitchEntity): class OnOffWindowDetectionFunctionConfigurationEntity( ZHASwitchConfigurationEntity, id_suffix="on_off_window_opened_detection" ): - """Representation of a ZHA on off transition time configuration entity.""" + """Representation of a ZHA window detection configuration entity.""" - _attr_entity_category = EntityCategory.CONFIG _zcl_attribute = "window_detection_function" _zcl_inverter_attribute = "window_detection_function_inverter" + + +@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.motion.ac02"}) +class P1MotionTriggerIndicatorSwitch( + ZHASwitchConfigurationEntity, id_suffix="trigger_indicator" +): + """Representation of a ZHA motion triggering configuration entity.""" + + _zcl_attribute = "trigger_indicator" From 71bc650ac70a417cbe0d42be55abaf3a6da75bc6 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 24 May 2022 23:59:27 -0700 Subject: [PATCH 814/930] Stop updating google_calendars.yaml if it does not already exist (#72340) * Stop updating google_calendars.yaml if it does not already exist * Add additional test coverage to make CI pass * Add test for no updates to google_calendar.yaml * Add parameter to test for expecting write calls * Missing call argument * Remove conditional and replace with inline assert --- homeassistant/components/google/__init__.py | 23 +++++---- tests/components/google/conftest.py | 14 ++++-- tests/components/google/test_calendar.py | 4 +- tests/components/google/test_init.py | 53 ++++++++++++++++++++- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 70732af1fd8..f034d48b9c5 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -300,6 +300,7 @@ async def async_setup_services( calendars = await hass.async_add_executor_job( load_config, hass.config.path(YAML_DEVICES) ) + calendars_file_lock = asyncio.Lock() async def _found_calendar(calendar_item: Calendar) -> None: calendar = get_calendar_info( @@ -307,15 +308,19 @@ async def async_setup_services( calendar_item.dict(exclude_unset=True), ) calendar_id = calendar_item.id - # Populate the yaml file with all discovered calendars - if calendar_id not in calendars: - calendars[calendar_id] = calendar - await hass.async_add_executor_job( - update_config, hass.config.path(YAML_DEVICES), calendar - ) - else: - # Prefer entity/name information from yaml, overriding api - calendar = calendars[calendar_id] + # If the google_calendars.yaml file already exists, populate it for + # backwards compatibility, but otherwise do not create it if it does + # not exist. + if calendars: + if calendar_id not in calendars: + calendars[calendar_id] = calendar + async with calendars_file_lock: + await hass.async_add_executor_job( + update_config, hass.config.path(YAML_DEVICES), calendar + ) + else: + # Prefer entity/name information from yaml, overriding api + calendar = calendars[calendar_id] async_dispatcher_send(hass, DISCOVER_CALENDAR, calendar) created_calendars = set() diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index 48badbc3ab5..b5566450913 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Awaitable, Callable import datetime from typing import Any, Generator, TypeVar -from unittest.mock import mock_open, patch +from unittest.mock import Mock, mock_open, patch from aiohttp.client_exceptions import ClientError from gcal_sync.auth import API_BASE_URL @@ -104,11 +104,11 @@ def calendars_config(calendars_config_entity: dict[str, Any]) -> list[dict[str, def mock_calendars_yaml( hass: HomeAssistant, calendars_config: list[dict[str, Any]], -) -> None: +) -> Generator[Mock, None, None]: """Fixture that prepares the google_calendars.yaml mocks.""" mocked_open_function = mock_open(read_data=yaml.dump(calendars_config)) with patch("homeassistant.components.google.open", mocked_open_function): - yield + yield mocked_open_function class FakeStorage: @@ -170,10 +170,17 @@ def config_entry_token_expiry(token_expiry: datetime.datetime) -> float: return token_expiry.timestamp() +@pytest.fixture +def config_entry_options() -> dict[str, Any] | None: + """Fixture to set initial config entry options.""" + return None + + @pytest.fixture def config_entry( token_scopes: list[str], config_entry_token_expiry: float, + config_entry_options: dict[str, Any] | None, ) -> MockConfigEntry: """Fixture to create a config entry for the integration.""" return MockConfigEntry( @@ -188,6 +195,7 @@ def config_entry( "expires_at": config_entry_token_expiry, }, }, + options=config_entry_options, ) diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 6ae62f50914..794065ca09a 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -572,14 +572,16 @@ async def test_opaque_event( assert (len(events) > 0) == expect_visible_event +@pytest.mark.parametrize("mock_test_setup", [None]) async def test_scan_calendar_error( hass, component_setup, test_api_calendar, mock_calendars_list, + config_entry, ): """Test that the calendar update handles a server error.""" - + config_entry.add_to_hass(hass) mock_calendars_list({}, exc=ClientError()) assert await component_setup() diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index 4709379e840..93c0642514e 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -6,7 +6,7 @@ import datetime import http import time from typing import Any -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest @@ -19,6 +19,7 @@ from homeassistant.components.google import ( SERVICE_ADD_EVENT, SERVICE_SCAN_CALENDARS, ) +from homeassistant.components.google.const import CONF_CALENDAR_ACCESS from homeassistant.config_entries import ConfigEntryState from homeassistant.const import STATE_OFF from homeassistant.core import HomeAssistant, State @@ -229,7 +230,10 @@ async def test_found_calendar_from_api( assert not hass.states.get(TEST_YAML_ENTITY) -@pytest.mark.parametrize("calendars_config,google_config", [([], {})]) +@pytest.mark.parametrize( + "calendars_config,google_config,config_entry_options", + [([], {}, {CONF_CALENDAR_ACCESS: "read_write"})], +) async def test_load_application_credentials( hass: HomeAssistant, component_setup: ComponentSetup, @@ -604,3 +608,48 @@ async def test_expired_token_requires_reauth( flows = hass.config_entries.flow.async_progress() assert len(flows) == 1 assert flows[0]["step_id"] == "reauth_confirm" + + +@pytest.mark.parametrize( + "calendars_config,expect_write_calls", + [ + ( + [ + { + "cal_id": "ignored", + "entities": {"device_id": "existing", "name": "existing"}, + } + ], + True, + ), + ([], False), + ], + ids=["has_yaml", "no_yaml"], +) +async def test_calendar_yaml_update( + hass: HomeAssistant, + component_setup: ComponentSetup, + mock_calendars_yaml: Mock, + mock_calendars_list: ApiResult, + test_api_calendar: dict[str, Any], + mock_events_list: ApiResult, + setup_config_entry: MockConfigEntry, + calendars_config: dict[str, Any], + expect_write_calls: bool, +) -> None: + """Test updating the yaml file with a new calendar.""" + + mock_calendars_list({"items": [test_api_calendar]}) + mock_events_list({}) + assert await component_setup() + + mock_calendars_yaml().read.assert_called() + mock_calendars_yaml().write.called is expect_write_calls + + state = hass.states.get(TEST_API_ENTITY) + assert state + assert state.name == TEST_API_ENTITY_NAME + assert state.state == STATE_OFF + + # No yaml config loaded that overwrites the entity name + assert not hass.states.get(TEST_YAML_ENTITY) From 4f14d40072b37b409b87f08c0cfe85f5547c32f6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 25 May 2022 09:00:42 +0200 Subject: [PATCH 815/930] Adjust config-flow type hints in philips_js (#72443) --- .../components/philips_js/config_flow.py | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/philips_js/config_flow.py b/homeassistant/components/philips_js/config_flow.py index 29abbe5dd71..9785aaf54a3 100644 --- a/homeassistant/components/philips_js/config_flow.py +++ b/homeassistant/components/philips_js/config_flow.py @@ -15,12 +15,13 @@ from homeassistant.const import ( CONF_PIN, CONF_USERNAME, ) +from homeassistant.data_entry_flow import FlowResult from . import LOGGER from .const import CONF_ALLOW_NOTIFY, CONF_SYSTEM, CONST_APP_ID, CONST_APP_NAME, DOMAIN -async def validate_input( +async def _validate_input( hass: core.HomeAssistant, host: str, api_version: int ) -> tuple[dict, PhilipsTV]: """Validate the user input allows us to connect.""" @@ -47,7 +48,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._hub: PhilipsTV | None = None self._pair_state: Any = None - async def _async_create_current(self): + async def _async_create_current(self) -> FlowResult: system = self._current[CONF_SYSTEM] return self.async_create_entry( @@ -55,7 +56,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data=self._current, ) - async def async_step_pair(self, user_input: dict | None = None) -> dict: + async def async_step_pair( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Attempt to pair with device.""" assert self._hub @@ -106,13 +109,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._current[CONF_PASSWORD] = password return await self._async_create_current() - async def async_step_user(self, user_input: dict | None = None) -> dict: + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle the initial step.""" errors = {} if user_input: self._current = user_input try: - hub = await validate_input( + hub = await _validate_input( self.hass, user_input[CONF_HOST], user_input[CONF_API_VERSION] ) except ConnectionFailure as exc: @@ -146,7 +151,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @staticmethod @core.callback - def async_get_options_flow(config_entry): + def async_get_options_flow( + config_entry: config_entries.ConfigEntry, + ) -> OptionsFlowHandler: """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) @@ -158,7 +165,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow): """Initialize options flow.""" self.config_entry = config_entry - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle options flow.""" if user_input is not None: return self.async_create_entry(title="", data=user_input) From ce477e65ce5105fc0b42cfc196b70defac6d9bbf Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Wed, 25 May 2022 08:02:48 +0100 Subject: [PATCH 816/930] Render template during stream_url test for generic camera (#69716) --- homeassistant/components/generic/config_flow.py | 7 +++++++ homeassistant/components/generic/strings.json | 2 ++ homeassistant/components/generic/translations/en.json | 2 ++ tests/components/generic/test_config_flow.py | 10 ++++++++++ 4 files changed, 21 insertions(+) diff --git a/homeassistant/components/generic/config_flow.py b/homeassistant/components/generic/config_flow.py index 651ed87da39..272c7a2d98e 100644 --- a/homeassistant/components/generic/config_flow.py +++ b/homeassistant/components/generic/config_flow.py @@ -198,6 +198,13 @@ async def async_test_stream(hass, info) -> dict[str, str]: """Verify that the stream is valid before we create an entity.""" if not (stream_source := info.get(CONF_STREAM_SOURCE)): return {} + if not isinstance(stream_source, template_helper.Template): + stream_source = template_helper.Template(stream_source, hass) + try: + stream_source = stream_source.async_render(parse_result=False) + except TemplateError as err: + _LOGGER.warning("Problem rendering template %s: %s", stream_source, err) + return {CONF_STREAM_SOURCE: "template_error"} try: # For RTSP streams, prefer TCP. This code is duplicated from # homeassistant.components.stream.__init__.py:create_stream() diff --git a/homeassistant/components/generic/strings.json b/homeassistant/components/generic/strings.json index 0954656f71d..6b73c70cf3d 100644 --- a/homeassistant/components/generic/strings.json +++ b/homeassistant/components/generic/strings.json @@ -8,6 +8,7 @@ "invalid_still_image": "URL did not return a valid still image", "stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)", "stream_http_not_found": "HTTP 404 Not found while trying to connect to stream", + "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "stream_no_route_to_host": "Could not find host while trying to connect to stream", "stream_io_error": "Input/Output error while trying to connect to stream. Wrong RTSP transport protocol?", @@ -79,6 +80,7 @@ "invalid_still_image": "[%key:component::generic::config::error::invalid_still_image%]", "stream_file_not_found": "[%key:component::generic::config::error::stream_file_not_found%]", "stream_http_not_found": "[%key:component::generic::config::error::stream_http_not_found%]", + "template_error": "[%key:component::generic::config::error::template_error%]", "timeout": "[%key:component::generic::config::error::timeout%]", "stream_no_route_to_host": "[%key:component::generic::config::error::stream_no_route_to_host%]", "stream_io_error": "[%key:component::generic::config::error::stream_io_error%]", diff --git a/homeassistant/components/generic/translations/en.json b/homeassistant/components/generic/translations/en.json index b552c780d29..d01e6e59a4b 100644 --- a/homeassistant/components/generic/translations/en.json +++ b/homeassistant/components/generic/translations/en.json @@ -15,6 +15,7 @@ "stream_no_video": "Stream has no video", "stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?", "stream_unauthorised": "Authorisation failed while trying to connect to stream", + "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.", "unknown": "Unexpected error" @@ -57,6 +58,7 @@ "stream_no_video": "Stream has no video", "stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?", "stream_unauthorised": "Authorisation failed while trying to connect to stream", + "template_error": "Error rendering template. Review log for more info.", "timeout": "Timeout while loading URL", "unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.", "unknown": "Unexpected error" diff --git a/tests/components/generic/test_config_flow.py b/tests/components/generic/test_config_flow.py index b7f3bf73527..a525619d962 100644 --- a/tests/components/generic/test_config_flow.py +++ b/tests/components/generic/test_config_flow.py @@ -534,6 +534,16 @@ async def test_options_template_error(hass, fakeimgbytes_png, mock_av_open): assert result4.get("type") == data_entry_flow.RESULT_TYPE_FORM assert result4["errors"] == {"still_image_url": "template_error"} + # verify that an invalid template reports the correct UI error. + data[CONF_STILL_IMAGE_URL] = "http://127.0.0.1/testurl/1" + data[CONF_STREAM_SOURCE] = "http://127.0.0.2/testurl/{{1/0}}" + result5 = await hass.config_entries.options.async_configure( + result4["flow_id"], + user_input=data, + ) + assert result5.get("type") == data_entry_flow.RESULT_TYPE_FORM + assert result5["errors"] == {"stream_source": "template_error"} + @respx.mock async def test_options_only_stream(hass, fakeimgbytes_png, mock_av_open): From 88c49f034a01f9d1dae540c74e4b1003aa073f85 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Wed, 25 May 2022 09:05:11 +0200 Subject: [PATCH 817/930] Use 'python-homewizard-energy' dependency for HomeWizard (#71781) * Update requirement * Remove aiohwenergy and use python-homewizard-energy * Update test to work with python-homewizard-energy * Bumb python-homewizard-energy to 1.0.3 --- .../components/homewizard/__init__.py | 17 +- .../components/homewizard/config_flow.py | 37 ++-- homeassistant/components/homewizard/const.py | 6 +- .../components/homewizard/coordinator.py | 59 ++----- .../components/homewizard/manifest.json | 4 +- homeassistant/components/homewizard/sensor.py | 15 +- homeassistant/components/homewizard/switch.py | 16 +- requirements_all.txt | 6 +- requirements_test_all.txt | 6 +- tests/components/homewizard/generator.py | 21 ++- .../components/homewizard/test_config_flow.py | 104 +++++------- tests/components/homewizard/test_init.py | 30 ++-- tests/components/homewizard/test_sensor.py | 160 +++++++----------- tests/components/homewizard/test_switch.py | 73 ++++---- 14 files changed, 218 insertions(+), 336 deletions(-) diff --git a/homeassistant/components/homewizard/__init__.py b/homeassistant/components/homewizard/__init__.py index b50d87a940d..4a087988b61 100644 --- a/homeassistant/components/homewizard/__init__.py +++ b/homeassistant/components/homewizard/__init__.py @@ -1,14 +1,11 @@ """The Homewizard integration.""" import logging -from aiohwenergy import DisabledError - from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import entity_registry as er -from homeassistant.helpers.update_coordinator import UpdateFailed from .const import DOMAIN, PLATFORMS from .coordinator import HWEnergyDeviceUpdateCoordinator as Coordinator @@ -69,16 +66,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Create coordinator coordinator = Coordinator(hass, entry.data[CONF_IP_ADDRESS]) try: - await coordinator.initialize_api() - - except DisabledError: - _LOGGER.error("API is disabled, enable API in HomeWizard Energy app") - return False - - except UpdateFailed as ex: - raise ConfigEntryNotReady from ex - - await coordinator.async_config_entry_first_refresh() + await coordinator.async_config_entry_first_refresh() + except ConfigEntryNotReady: + await coordinator.api.close() + raise # Finalize hass.data.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/homewizard/config_flow.py b/homeassistant/components/homewizard/config_flow.py index 6ab485f534f..df883baf3b1 100644 --- a/homeassistant/components/homewizard/config_flow.py +++ b/homeassistant/components/homewizard/config_flow.py @@ -4,9 +4,8 @@ from __future__ import annotations import logging from typing import Any -import aiohwenergy -from aiohwenergy.hwenergy import SUPPORTED_DEVICES -import async_timeout +from homewizard_energy import HomeWizardEnergy +from homewizard_energy.errors import DisabledError, UnsupportedError from voluptuous import Required, Schema from homeassistant import config_entries @@ -175,16 +174,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # Make connection with device # This is to test the connection and to get info for unique_id - energy_api = aiohwenergy.HomeWizardEnergy(ip_address) + energy_api = HomeWizardEnergy(ip_address) try: - with async_timeout.timeout(10): - await energy_api.initialize() + device = await energy_api.device() - except aiohwenergy.DisabledError as ex: + except DisabledError as ex: _LOGGER.error("API disabled, API must be enabled in the app") raise AbortFlow("api_not_enabled") from ex + except UnsupportedError as ex: + _LOGGER.error("API version unsuppored") + raise AbortFlow("unsupported_api_version") from ex + except Exception as ex: _LOGGER.exception( "Error connecting with Energy Device at %s", @@ -195,25 +197,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): finally: await energy_api.close() - if energy_api.device is None: - _LOGGER.error("Initialization failed") - raise AbortFlow("unknown_error") - - # Validate metadata - if energy_api.device.api_version != "v1": - raise AbortFlow("unsupported_api_version") - - if energy_api.device.product_type not in SUPPORTED_DEVICES: - _LOGGER.error( - "Device (%s) not supported by integration", - energy_api.device.product_type, - ) - raise AbortFlow("device_not_supported") - return { - CONF_PRODUCT_NAME: energy_api.device.product_name, - CONF_PRODUCT_TYPE: energy_api.device.product_type, - CONF_SERIAL: energy_api.device.serial, + CONF_PRODUCT_NAME: device.product_name, + CONF_PRODUCT_TYPE: device.product_type, + CONF_SERIAL: device.serial, } async def _async_set_and_check_unique_id(self, entry_info: dict[str, Any]) -> None: diff --git a/homeassistant/components/homewizard/const.py b/homeassistant/components/homewizard/const.py index 75c522a211e..c1c788d371b 100644 --- a/homeassistant/components/homewizard/const.py +++ b/homeassistant/components/homewizard/const.py @@ -5,10 +5,9 @@ from datetime import timedelta from typing import TypedDict # Set up. -from aiohwenergy.device import Device +from homewizard_energy.models import Data, Device, State from homeassistant.const import Platform -from homeassistant.helpers.typing import StateType DOMAIN = "homewizard" PLATFORMS = [Platform.SENSOR, Platform.SWITCH] @@ -29,4 +28,5 @@ class DeviceResponseEntry(TypedDict): """Dict describing a single response entry.""" device: Device - data: dict[str, StateType] + data: Data + state: State diff --git a/homeassistant/components/homewizard/coordinator.py b/homeassistant/components/homewizard/coordinator.py index 2cce88cbe36..e12edda63ae 100644 --- a/homeassistant/components/homewizard/coordinator.py +++ b/homeassistant/components/homewizard/coordinator.py @@ -1,11 +1,10 @@ """Update coordinator for HomeWizard.""" from __future__ import annotations -import asyncio import logging -import aiohwenergy -import async_timeout +from homewizard_energy import HomeWizardEnergy +from homewizard_energy.errors import DisabledError from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -19,7 +18,7 @@ _LOGGER = logging.getLogger(__name__) class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]): """Gather data for the energy device.""" - api: aiohwenergy.HomeWizardEnergy + api: HomeWizardEnergy def __init__( self, @@ -29,56 +28,20 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry] """Initialize Update Coordinator.""" super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL) - - session = async_get_clientsession(hass) - self.api = aiohwenergy.HomeWizardEnergy(host, clientsession=session) + self.api = HomeWizardEnergy(host, clientsession=async_get_clientsession(hass)) async def _async_update_data(self) -> DeviceResponseEntry: """Fetch all device and sensor data from api.""" - async with async_timeout.timeout(10): - - if self.api.device is None: - await self.initialize_api() - - # Update all properties - try: - if not await self.api.update(): - raise UpdateFailed("Failed to communicate with device") - - except aiohwenergy.DisabledError as ex: - raise UpdateFailed( - "API disabled, API must be enabled in the app" - ) from ex - + # Update all properties + try: data: DeviceResponseEntry = { - "device": self.api.device, - "data": {}, + "device": await self.api.device(), + "data": await self.api.data(), + "state": await self.api.state(), } - for datapoint in self.api.data.available_datapoints: - data["data"][datapoint] = getattr(self.api.data, datapoint) + except DisabledError as ex: + raise UpdateFailed("API disabled, API must be enabled in the app") from ex return data - - async def initialize_api(self) -> aiohwenergy: - """Initialize API and validate connection.""" - - try: - await self.api.initialize() - - except (asyncio.TimeoutError, aiohwenergy.RequestError) as ex: - raise UpdateFailed( - f"Error connecting to the Energy device at {self.api.host}" - ) from ex - - except aiohwenergy.DisabledError as ex: - raise ex - - except aiohwenergy.AiohwenergyException as ex: - raise UpdateFailed("Unknown Energy API error occurred") from ex - - except Exception as ex: - raise UpdateFailed( - f"Unknown error connecting with Energy Device at {self.api.host}" - ) from ex diff --git a/homeassistant/components/homewizard/manifest.json b/homeassistant/components/homewizard/manifest.json index 1bd856334b7..e426234b449 100644 --- a/homeassistant/components/homewizard/manifest.json +++ b/homeassistant/components/homewizard/manifest.json @@ -4,9 +4,9 @@ "documentation": "https://www.home-assistant.io/integrations/homewizard", "codeowners": ["@DCSBL"], "dependencies": [], - "requirements": ["aiohwenergy==0.8.0"], + "requirements": ["python-homewizard-energy==1.0.3"], "zeroconf": ["_hwenergy._tcp.local."], "config_flow": true, "iot_class": "local_polling", - "loggers": ["aiohwenergy"] + "loggers": ["homewizard_energy"] } diff --git a/homeassistant/components/homewizard/sensor.py b/homeassistant/components/homewizard/sensor.py index 34479dd6b0a..b7f759f6a0e 100644 --- a/homeassistant/components/homewizard/sensor.py +++ b/homeassistant/components/homewizard/sensor.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging -from typing import Final +from typing import Final, cast from homeassistant.components.sensor import ( SensorDeviceClass, @@ -129,12 +129,9 @@ async def async_setup_entry( coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] entities = [] - if coordinator.api.data is not None: + if coordinator.data["data"] is not None: for description in SENSORS: - if ( - description.key in coordinator.api.data.available_datapoints - and getattr(coordinator.api.data, description.key) is not None - ): + if getattr(coordinator.data["data"], description.key) is not None: entities.append(HWEnergySensor(coordinator, entry, description)) async_add_entities(entities) @@ -165,7 +162,7 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE "total_power_export_t1_kwh", "total_power_export_t2_kwh", ]: - if self.data["data"][self.data_type] == 0: + if self.native_value == 0: self._attr_entity_registry_enabled_default = False @property @@ -187,9 +184,9 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE @property def native_value(self) -> StateType: """Return state of meter.""" - return self.data["data"][self.data_type] + return cast(StateType, getattr(self.data["data"], self.data_type)) @property def available(self) -> bool: """Return availability of meter.""" - return super().available and self.data_type in self.data["data"] + return super().available and self.native_value is not None diff --git a/homeassistant/components/homewizard/switch.py b/homeassistant/components/homewizard/switch.py index 3c6b1a1c5dc..eb2e9c49afe 100644 --- a/homeassistant/components/homewizard/switch.py +++ b/homeassistant/components/homewizard/switch.py @@ -22,7 +22,7 @@ async def async_setup_entry( """Set up switches.""" coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - if coordinator.api.state: + if coordinator.data["state"]: async_add_entities( [ HWEnergyMainSwitchEntity(coordinator, entry), @@ -70,12 +70,12 @@ class HWEnergyMainSwitchEntity(HWEnergySwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" - await self.coordinator.api.state.set(power_on=True) + await self.coordinator.api.state_set(power_on=True) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the switch off.""" - await self.coordinator.api.state.set(power_on=False) + await self.coordinator.api.state_set(power_on=False) await self.coordinator.async_refresh() @property @@ -85,12 +85,12 @@ class HWEnergyMainSwitchEntity(HWEnergySwitchEntity): This switch becomes unavailable when switch_lock is enabled. """ - return super().available and not self.coordinator.api.state.switch_lock + return super().available and not self.coordinator.data["state"].switch_lock @property def is_on(self) -> bool: """Return true if switch is on.""" - return bool(self.coordinator.api.state.power_on) + return bool(self.coordinator.data["state"].power_on) class HWEnergySwitchLockEntity(HWEnergySwitchEntity): @@ -115,15 +115,15 @@ class HWEnergySwitchLockEntity(HWEnergySwitchEntity): async def async_turn_on(self, **kwargs: Any) -> None: """Turn switch-lock on.""" - await self.coordinator.api.state.set(switch_lock=True) + await self.coordinator.api.state_set(switch_lock=True) await self.coordinator.async_refresh() async def async_turn_off(self, **kwargs: Any) -> None: """Turn switch-lock off.""" - await self.coordinator.api.state.set(switch_lock=False) + await self.coordinator.api.state_set(switch_lock=False) await self.coordinator.async_refresh() @property def is_on(self) -> bool: """Return true if switch is on.""" - return bool(self.coordinator.api.state.switch_lock) + return bool(self.coordinator.data["state"].switch_lock) diff --git a/requirements_all.txt b/requirements_all.txt index 38cc91c2574..a52d465fac6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,9 +171,6 @@ aiohttp_cors==0.7.0 # homeassistant.components.hue aiohue==4.4.1 -# homeassistant.components.homewizard -aiohwenergy==0.8.0 - # homeassistant.components.imap aioimaplib==0.9.0 @@ -1903,6 +1900,9 @@ python-gc100==1.0.3a0 # homeassistant.components.gitlab_ci python-gitlab==1.6.0 +# homeassistant.components.homewizard +python-homewizard-energy==1.0.3 + # homeassistant.components.hp_ilo python-hpilo==4.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cefa305a5b9..897ffe50074 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,9 +155,6 @@ aiohttp_cors==0.7.0 # homeassistant.components.hue aiohue==4.4.1 -# homeassistant.components.homewizard -aiohwenergy==0.8.0 - # homeassistant.components.apache_kafka aiokafka==0.6.0 @@ -1265,6 +1262,9 @@ python-ecobee-api==0.2.14 # homeassistant.components.darksky python-forecastio==1.4.0 +# homeassistant.components.homewizard +python-homewizard-energy==1.0.3 + # homeassistant.components.izone python-izone==1.2.3 diff --git a/tests/components/homewizard/generator.py b/tests/components/homewizard/generator.py index 74d33c9e609..0f94580ad84 100644 --- a/tests/components/homewizard/generator.py +++ b/tests/components/homewizard/generator.py @@ -2,6 +2,8 @@ from unittest.mock import AsyncMock +from homewizard_energy.models import Device + def get_mock_device( serial="aabbccddeeff", @@ -13,15 +15,18 @@ def get_mock_device( mock_device = AsyncMock() mock_device.host = host - mock_device.device.product_name = product_name - mock_device.device.product_type = product_type - mock_device.device.serial = serial - mock_device.device.api_version = "v1" - mock_device.device.firmware_version = "1.00" + mock_device.device = AsyncMock( + return_value=Device( + product_name=product_name, + product_type=product_type, + serial=serial, + api_version="V1", + firmware_version="1.00", + ) + ) + mock_device.data = AsyncMock(return_value=None) + mock_device.state = AsyncMock(return_value=None) - mock_device.state = None - - mock_device.initialize = AsyncMock() mock_device.close = AsyncMock() return mock_device diff --git a/tests/components/homewizard/test_config_flow.py b/tests/components/homewizard/test_config_flow.py index d0dc7d04509..d2e7d4c58ae 100644 --- a/tests/components/homewizard/test_config_flow.py +++ b/tests/components/homewizard/test_config_flow.py @@ -2,7 +2,7 @@ import logging from unittest.mock import patch -from aiohwenergy import DisabledError +from homewizard_energy.errors import DisabledError, UnsupportedError from homeassistant import config_entries from homeassistant.components import zeroconf @@ -33,7 +33,10 @@ async def test_manual_flow_works(hass, aioclient_mock): assert result["type"] == "form" assert result["step_id"] == "user" - with patch("aiohwenergy.HomeWizardEnergy", return_value=device,), patch( + with patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=device, + ), patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ) as mock_setup_entry: @@ -42,12 +45,12 @@ async def test_manual_flow_works(hass, aioclient_mock): ) assert result["type"] == "create_entry" - assert result["title"] == f"{device.device.product_name} (aabbccddeeff)" + assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "2.2.2.2" assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert len(device.initialize.mock_calls) == 1 + assert len(device.device.mock_calls) == 1 assert len(device.close.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 @@ -72,7 +75,10 @@ async def test_discovery_flow_works(hass, aioclient_mock): }, ) - with patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()): + with patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=get_mock_device(), + ): flow = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, @@ -82,7 +88,10 @@ async def test_discovery_flow_works(hass, aioclient_mock): with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, - ), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()): + ), patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=get_mock_device(), + ): result = await hass.config_entries.flow.async_configure( flow["flow_id"], user_input=None ) @@ -92,7 +101,10 @@ async def test_discovery_flow_works(hass, aioclient_mock): with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, - ), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()): + ), patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=get_mock_device(), + ): result = await hass.config_entries.flow.async_configure( flow["flow_id"], user_input={"ip_address": "192.168.43.183"} ) @@ -113,7 +125,10 @@ async def test_config_flow_imports_entry(aioclient_mock, hass): mock_entry = MockConfigEntry(domain="homewizard_energy", data={"host": "1.2.3.4"}) mock_entry.add_to_hass(hass) - with patch("aiohwenergy.HomeWizardEnergy", return_value=device,), patch( + with patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=device, + ), patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ) as mock_setup_entry: @@ -127,11 +142,11 @@ async def test_config_flow_imports_entry(aioclient_mock, hass): ) assert result["type"] == "create_entry" - assert result["title"] == f"{device.device.product_name} (aabbccddeeff)" + assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "1.2.3.4" assert len(hass.config_entries.async_entries(DOMAIN)) == 1 - assert len(device.initialize.mock_calls) == 1 + assert len(device.device.mock_calls) == 1 assert len(device.close.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 @@ -166,7 +181,10 @@ async def test_discovery_disabled_api(hass, aioclient_mock): with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, - ), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()): + ), patch( + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", + return_value=get_mock_device(), + ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"ip_address": "192.168.43.183"} ) @@ -240,7 +258,7 @@ async def test_check_disabled_api(hass, aioclient_mock): raise DisabledError device = get_mock_device() - device.initialize.side_effect = mock_initialize + device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -250,7 +268,7 @@ async def test_check_disabled_api(hass, aioclient_mock): assert result["step_id"] == "user" with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( @@ -268,7 +286,7 @@ async def test_check_error_handling_api(hass, aioclient_mock): raise Exception() device = get_mock_device() - device.initialize.side_effect = mock_initialize + device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -278,32 +296,7 @@ async def test_check_error_handling_api(hass, aioclient_mock): assert result["step_id"] == "user" with patch( - "aiohwenergy.HomeWizardEnergy", - return_value=device, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} - ) - - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "unknown_error" - - -async def test_check_detects_unexpected_api_response(hass, aioclient_mock): - """Test check detecting device endpoint failed fetching data.""" - - device = get_mock_device() - device.device = None - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == "form" - assert result["step_id"] == "user" - - with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( @@ -317,8 +310,11 @@ async def test_check_detects_unexpected_api_response(hass, aioclient_mock): async def test_check_detects_invalid_api(hass, aioclient_mock): """Test check detecting device endpoint failed fetching data.""" + def mock_initialize(): + raise UnsupportedError + device = get_mock_device() - device.device.api_version = "not_v1" + device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -328,7 +324,7 @@ async def test_check_detects_invalid_api(hass, aioclient_mock): assert result["step_id"] == "user" with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( @@ -337,27 +333,3 @@ async def test_check_detects_invalid_api(hass, aioclient_mock): assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "unsupported_api_version" - - -async def test_check_detects_unsuported_device(hass, aioclient_mock): - """Test check detecting device endpoint failed fetching data.""" - - device = get_mock_device(product_type="not_an_energy_device") - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == "form" - assert result["step_id"] == "user" - - with patch( - "aiohwenergy.HomeWizardEnergy", - return_value=device, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} - ) - - assert result["type"] == RESULT_TYPE_ABORT - assert result["reason"] == "device_not_supported" diff --git a/tests/components/homewizard/test_init.py b/tests/components/homewizard/test_init.py index 984a5431004..c7cc5bd7bdb 100644 --- a/tests/components/homewizard/test_init.py +++ b/tests/components/homewizard/test_init.py @@ -2,7 +2,7 @@ from asyncio import TimeoutError from unittest.mock import patch -from aiohwenergy import AiohwenergyException, DisabledError +from homewizard_energy.errors import DisabledError, HomeWizardEnergyException from homeassistant import config_entries from homeassistant.components.homewizard.const import DOMAIN @@ -28,7 +28,7 @@ async def test_load_unload(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) @@ -50,7 +50,7 @@ async def test_load_failed_host_unavailable(aioclient_mock, hass): raise TimeoutError() device = get_mock_device() - device.initialize.side_effect = MockInitialize + device.device.side_effect = MockInitialize entry = MockConfigEntry( domain=DOMAIN, @@ -60,7 +60,7 @@ async def test_load_failed_host_unavailable(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) @@ -127,7 +127,7 @@ async def test_init_accepts_and_migrates_old_entry(aioclient_mock, hass): # Add the entry_id to trigger migration with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(imported_entry.entry_id) @@ -168,7 +168,7 @@ async def test_load_detect_api_disabled(aioclient_mock, hass): raise DisabledError() device = get_mock_device() - device.initialize.side_effect = MockInitialize + device.device.side_effect = MockInitialize entry = MockConfigEntry( domain=DOMAIN, @@ -178,24 +178,24 @@ async def test_load_detect_api_disabled(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - assert entry.state is ConfigEntryState.SETUP_ERROR + assert entry.state is ConfigEntryState.SETUP_RETRY -async def test_load_handles_aiohwenergy_exception(aioclient_mock, hass): +async def test_load_handles_homewizardenergy_exception(aioclient_mock, hass): """Test setup handles exception from API.""" def MockInitialize(): - raise AiohwenergyException() + raise HomeWizardEnergyException() device = get_mock_device() - device.initialize.side_effect = MockInitialize + device.device.side_effect = MockInitialize entry = MockConfigEntry( domain=DOMAIN, @@ -205,7 +205,7 @@ async def test_load_handles_aiohwenergy_exception(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) @@ -222,7 +222,7 @@ async def test_load_handles_generic_exception(aioclient_mock, hass): raise Exception() device = get_mock_device() - device.initialize.side_effect = MockInitialize + device.device.side_effect = MockInitialize entry = MockConfigEntry( domain=DOMAIN, @@ -232,7 +232,7 @@ async def test_load_handles_generic_exception(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) @@ -256,7 +256,7 @@ async def test_load_handles_initialization_error(aioclient_mock, hass): entry.add_to_hass(hass) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=device, ): await hass.config_entries.async_setup(entry.entry_id) diff --git a/tests/components/homewizard/test_sensor.py b/tests/components/homewizard/test_sensor.py index c1a98c07108..8195aa11708 100644 --- a/tests/components/homewizard/test_sensor.py +++ b/tests/components/homewizard/test_sensor.py @@ -3,7 +3,8 @@ from datetime import timedelta from unittest.mock import AsyncMock, patch -from aiohwenergy.errors import DisabledError +from homewizard_energy.errors import DisabledError, RequestError +from homewizard_energy.models import Data from homeassistant.components.sensor import ( ATTR_STATE_CLASS, @@ -36,13 +37,10 @@ async def test_sensor_entity_smr_version( """Test entity loads smr version.""" api = get_mock_device() - api.data.available_datapoints = [ - "smr_version", - ] - api.data.smr_version = 50 + api.data = AsyncMock(return_value=Data.from_dict({"smr_version": 50})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -77,13 +75,10 @@ async def test_sensor_entity_meter_model( """Test entity loads meter model.""" api = get_mock_device() - api.data.available_datapoints = [ - "meter_model", - ] - api.data.meter_model = "Model X" + api.data = AsyncMock(return_value=Data.from_dict({"meter_model": "Model X"})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -118,13 +113,10 @@ async def test_sensor_entity_wifi_ssid(hass, mock_config_entry_data, mock_config """Test entity loads wifi ssid.""" api = get_mock_device() - api.data.available_datapoints = [ - "wifi_ssid", - ] - api.data.wifi_ssid = "My Wifi" + api.data = AsyncMock(return_value=Data.from_dict({"wifi_ssid": "My Wifi"})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -159,13 +151,10 @@ async def test_sensor_entity_wifi_strength( """Test entity loads wifi strength.""" api = get_mock_device() - api.data.available_datapoints = [ - "wifi_strength", - ] - api.data.wifi_strength = 42 + api.data = AsyncMock(return_value=Data.from_dict({"wifi_strength": 42})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -189,13 +178,12 @@ async def test_sensor_entity_total_power_import_t1_kwh( """Test entity loads total power import t1.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_import_t1_kwh", - ] - api.data.total_power_import_t1_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -232,13 +220,12 @@ async def test_sensor_entity_total_power_import_t2_kwh( """Test entity loads total power import t2.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_import_t2_kwh", - ] - api.data.total_power_import_t2_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_import_t2_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -275,13 +262,12 @@ async def test_sensor_entity_total_power_export_t1_kwh( """Test entity loads total power export t1.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_export_t1_kwh", - ] - api.data.total_power_export_t1_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_export_t1_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -318,13 +304,12 @@ async def test_sensor_entity_total_power_export_t2_kwh( """Test entity loads total power export t2.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_export_t2_kwh", - ] - api.data.total_power_export_t2_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_export_t2_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -361,13 +346,10 @@ async def test_sensor_entity_active_power( """Test entity loads active power.""" api = get_mock_device() - api.data.available_datapoints = [ - "active_power_w", - ] - api.data.active_power_w = 123.123 + api.data = AsyncMock(return_value=Data.from_dict({"active_power_w": 123.123})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -402,13 +384,10 @@ async def test_sensor_entity_active_power_l1( """Test entity loads active power l1.""" api = get_mock_device() - api.data.available_datapoints = [ - "active_power_l1_w", - ] - api.data.active_power_l1_w = 123.123 + api.data = AsyncMock(return_value=Data.from_dict({"active_power_l1_w": 123.123})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -445,13 +424,10 @@ async def test_sensor_entity_active_power_l2( """Test entity loads active power l2.""" api = get_mock_device() - api.data.available_datapoints = [ - "active_power_l2_w", - ] - api.data.active_power_l2_w = 456.456 + api.data = AsyncMock(return_value=Data.from_dict({"active_power_l2_w": 456.456})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -488,13 +464,10 @@ async def test_sensor_entity_active_power_l3( """Test entity loads active power l3.""" api = get_mock_device() - api.data.available_datapoints = [ - "active_power_l3_w", - ] - api.data.active_power_l3_w = 789.789 + api.data = AsyncMock(return_value=Data.from_dict({"active_power_l3_w": 789.789})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -529,13 +502,10 @@ async def test_sensor_entity_total_gas(hass, mock_config_entry_data, mock_config """Test entity loads total gas.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_gas_m3", - ] - api.data.total_gas_m3 = 50 + api.data = AsyncMock(return_value=Data.from_dict({"total_gas_m3": 50})) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -570,17 +540,14 @@ async def test_sensor_entity_disabled_when_null( """Test sensor disables data with null by default.""" api = get_mock_device() - api.data.available_datapoints = [ - "active_power_l2_w", - "active_power_l3_w", - "total_gas_m3", - ] - api.data.active_power_l2_w = None - api.data.active_power_l3_w = None - api.data.total_gas_m3 = None + api.data = AsyncMock( + return_value=Data.from_dict( + {"active_power_l2_w": None, "active_power_l3_w": None, "total_gas_m3": None} + ) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -612,15 +579,14 @@ async def test_sensor_entity_export_disabled_when_unused( """Test sensor disables export if value is 0.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_export_t1_kwh", - "total_power_export_t2_kwh", - ] - api.data.total_power_export_t1_kwh = 0 - api.data.total_power_export_t2_kwh = 0 + api.data = AsyncMock( + return_value=Data.from_dict( + {"total_power_export_t1_kwh": 0, "total_power_export_t2_kwh": 0} + ) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -649,17 +615,14 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent """Test sensor handles api unreachable.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_import_t1_kwh", - ] - api.data.total_power_import_t1_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): - api.update = AsyncMock(return_value=True) - entry = mock_config_entry entry.data = mock_config_entry_data entry.add_to_hass(hass) @@ -675,7 +638,7 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent == "1234.123" ) - api.update = AsyncMock(return_value=False) + api.data.side_effect = RequestError async_fire_time_changed(hass, utcnow + timedelta(seconds=5)) await hass.async_block_till_done() assert ( @@ -685,7 +648,7 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent == "unavailable" ) - api.update = AsyncMock(return_value=True) + api.data.side_effect = None async_fire_time_changed(hass, utcnow + timedelta(seconds=10)) await hass.async_block_till_done() assert ( @@ -700,17 +663,14 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry): """Test sensor handles api unreachable.""" api = get_mock_device() - api.data.available_datapoints = [ - "total_power_import_t1_kwh", - ] - api.data.total_power_import_t1_kwh = 1234.123 + api.data = AsyncMock( + return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): - api.update = AsyncMock(return_value=True) - entry = mock_config_entry entry.data = mock_config_entry_data entry.add_to_hass(hass) @@ -726,7 +686,7 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry): == "1234.123" ) - api.update = AsyncMock(side_effect=DisabledError) + api.data.side_effect = DisabledError async_fire_time_changed(hass, utcnow + timedelta(seconds=5)) await hass.async_block_till_done() assert ( @@ -736,7 +696,7 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry): == "unavailable" ) - api.update = AsyncMock(return_value=True) + api.data.side_effect = None async_fire_time_changed(hass, utcnow + timedelta(seconds=10)) await hass.async_block_till_done() assert ( diff --git a/tests/components/homewizard/test_switch.py b/tests/components/homewizard/test_switch.py index f3792a9d75b..118f0774a47 100644 --- a/tests/components/homewizard/test_switch.py +++ b/tests/components/homewizard/test_switch.py @@ -2,6 +2,8 @@ from unittest.mock import AsyncMock, patch +from homewizard_energy.models import State + from homeassistant.components import switch from homeassistant.components.switch import DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH from homeassistant.const import ( @@ -27,7 +29,7 @@ async def test_switch_entity_not_loaded_when_not_available( api = get_mock_device() with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -48,13 +50,12 @@ async def test_switch_loads_entities(hass, mock_config_entry_data, mock_config_e """Test entity loads smr version.""" api = get_mock_device() - api.state = AsyncMock() - - api.state.power_on = False - api.state.switch_lock = False + api.state = AsyncMock( + return_value=State.from_dict({"power_on": False, "switch_lock": False}) + ) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -104,17 +105,19 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent """Test entity turns switch on and off.""" api = get_mock_device() - api.state = AsyncMock() - api.state.power_on = False - api.state.switch_lock = False + api.state = AsyncMock( + return_value=State.from_dict({"power_on": False, "switch_lock": False}) + ) - def set_power_on(power_on): - api.state.power_on = power_on + def state_set(power_on): + api.state = AsyncMock( + return_value=State.from_dict({"power_on": power_on, "switch_lock": False}) + ) - api.state.set = AsyncMock(side_effect=set_power_on) + api.state_set = AsyncMock(side_effect=state_set) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -138,7 +141,7 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent ) await hass.async_block_till_done() - assert len(api.state.set.mock_calls) == 1 + assert len(api.state_set.mock_calls) == 1 assert ( hass.states.get("switch.product_name_aabbccddeeff_switch").state == STATE_ON ) @@ -156,7 +159,7 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent hass.states.get("switch.product_name_aabbccddeeff_switch").state == STATE_OFF ) - assert len(api.state.set.mock_calls) == 2 + assert len(api.state_set.mock_calls) == 2 async def test_switch_lock_power_on_off( @@ -165,17 +168,19 @@ async def test_switch_lock_power_on_off( """Test entity turns switch on and off.""" api = get_mock_device() - api.state = AsyncMock() - api.state.power_on = False - api.state.switch_lock = False + api.state = AsyncMock( + return_value=State.from_dict({"power_on": False, "switch_lock": False}) + ) - def set_switch_lock(switch_lock): - api.state.switch_lock = switch_lock + def state_set(switch_lock): + api.state = AsyncMock( + return_value=State.from_dict({"power_on": True, "switch_lock": switch_lock}) + ) - api.state.set = AsyncMock(side_effect=set_switch_lock) + api.state_set = AsyncMock(side_effect=state_set) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -199,7 +204,7 @@ async def test_switch_lock_power_on_off( ) await hass.async_block_till_done() - assert len(api.state.set.mock_calls) == 1 + assert len(api.state_set.mock_calls) == 1 assert ( hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state == STATE_ON @@ -218,7 +223,7 @@ async def test_switch_lock_power_on_off( hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state == STATE_OFF ) - assert len(api.state.set.mock_calls) == 2 + assert len(api.state_set.mock_calls) == 2 async def test_switch_lock_sets_power_on_unavailable( @@ -227,17 +232,19 @@ async def test_switch_lock_sets_power_on_unavailable( """Test entity turns switch on and off.""" api = get_mock_device() - api.state = AsyncMock() - api.state.power_on = True - api.state.switch_lock = False + api.state = AsyncMock( + return_value=State.from_dict({"power_on": True, "switch_lock": False}) + ) - def set_switch_lock(switch_lock): - api.state.switch_lock = switch_lock + def state_set(switch_lock): + api.state = AsyncMock( + return_value=State.from_dict({"power_on": True, "switch_lock": switch_lock}) + ) - api.state.set = AsyncMock(side_effect=set_switch_lock) + api.state_set = AsyncMock(side_effect=state_set) with patch( - "aiohwenergy.HomeWizardEnergy", + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", return_value=api, ): entry = mock_config_entry @@ -264,7 +271,7 @@ async def test_switch_lock_sets_power_on_unavailable( ) await hass.async_block_till_done() - assert len(api.state.set.mock_calls) == 1 + assert len(api.state_set.mock_calls) == 1 assert ( hass.states.get("switch.product_name_aabbccddeeff_switch").state == STATE_UNAVAILABLE @@ -290,4 +297,4 @@ async def test_switch_lock_sets_power_on_unavailable( hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state == STATE_OFF ) - assert len(api.state.set.mock_calls) == 2 + assert len(api.state_set.mock_calls) == 2 From 1e4690626ff7e0e978805d9fa7b4dc7270811711 Mon Sep 17 00:00:00 2001 From: rappenze Date: Wed, 25 May 2022 09:07:55 +0200 Subject: [PATCH 818/930] Better detection for brightness support in fibaro light (#71615) --- homeassistant/components/fibaro/light.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/fibaro/light.py b/homeassistant/components/fibaro/light.py index 0115e0301c3..300b7c4c5f8 100644 --- a/homeassistant/components/fibaro/light.py +++ b/homeassistant/components/fibaro/light.py @@ -86,7 +86,10 @@ class FibaroLight(FibaroDevice, LightEntity): or "RGBW" in fibaro_device.type or "rgbw" in fibaro_device.type ) - supports_dimming = "levelChange" in fibaro_device.interfaces + supports_dimming = ( + "levelChange" in fibaro_device.interfaces + and "setValue" in fibaro_device.actions + ) if supports_color and supports_white_v: self._attr_supported_color_modes = {ColorMode.RGBW} From 804c88809886823128d1cbcb385716a493e5ec1a Mon Sep 17 00:00:00 2001 From: Lars Date: Wed, 25 May 2022 09:28:36 +0200 Subject: [PATCH 819/930] Free color selection for Fritz!Smarthome lights (#66213) * Fritz light free color selection * Use setcolor as fallback * better debug log message Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> * change if-clause Co-authored-by: Michael <35783820+mib1185@users.noreply.github.com> --- homeassistant/components/fritzbox/light.py | 40 ++++++++++++++++------ tests/components/fritzbox/test_light.py | 28 ++++++++++++++- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/fritzbox/light.py b/homeassistant/components/fritzbox/light.py index 0edcb8ee260..e01fb76ec11 100644 --- a/homeassistant/components/fritzbox/light.py +++ b/homeassistant/components/fritzbox/light.py @@ -3,6 +3,8 @@ from __future__ import annotations from typing import Any +from requests.exceptions import HTTPError + from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, @@ -21,6 +23,7 @@ from .const import ( COLOR_TEMP_MODE, CONF_COORDINATOR, DOMAIN as FRITZBOX_DOMAIN, + LOGGER, ) from .coordinator import FritzboxDataUpdateCoordinator @@ -135,16 +138,33 @@ class FritzboxLight(FritzBoxEntity, LightEntity): level = kwargs[ATTR_BRIGHTNESS] await self.hass.async_add_executor_job(self.device.set_level, level) if kwargs.get(ATTR_HS_COLOR) is not None: - hass_hue = int(kwargs[ATTR_HS_COLOR][0]) - hass_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) - # find supported hs values closest to what user selected - hue = min(self._supported_hs.keys(), key=lambda x: abs(x - hass_hue)) - saturation = min( - self._supported_hs[hue], key=lambda x: abs(x - hass_saturation) - ) - await self.hass.async_add_executor_job( - self.device.set_color, (hue, saturation) - ) + # Try setunmappedcolor first. This allows free color selection, + # but we don't know if its supported by all devices. + try: + # HA gives 0..360 for hue, fritz light only supports 0..359 + unmapped_hue = int(kwargs[ATTR_HS_COLOR][0] % 360) + unmapped_saturation = round(kwargs[ATTR_HS_COLOR][1] * 255.0 / 100.0) + await self.hass.async_add_executor_job( + self.device.set_unmapped_color, (unmapped_hue, unmapped_saturation) + ) + # This will raise 400 BAD REQUEST if the setunmappedcolor is not available + except HTTPError as err: + if err.response.status_code != 400: + raise + LOGGER.debug( + "fritzbox does not support method 'setunmappedcolor', fallback to 'setcolor'" + ) + # find supported hs values closest to what user selected + hue = min( + self._supported_hs.keys(), key=lambda x: abs(x - unmapped_hue) + ) + saturation = min( + self._supported_hs[hue], + key=lambda x: abs(x - unmapped_saturation), + ) + await self.hass.async_add_executor_job( + self.device.set_color, (hue, saturation) + ) if kwargs.get(ATTR_COLOR_TEMP) is not None: kelvin = color.color_temperature_kelvin_to_mired(kwargs[ATTR_COLOR_TEMP]) diff --git a/tests/components/fritzbox/test_light.py b/tests/components/fritzbox/test_light.py index b63d19da7e0..0759beb6849 100644 --- a/tests/components/fritzbox/test_light.py +++ b/tests/components/fritzbox/test_light.py @@ -113,7 +113,34 @@ async def test_turn_on_color(hass: HomeAssistant, fritz: Mock): assert await setup_config_entry( hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz ) + assert await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: ENTITY_ID, ATTR_BRIGHTNESS: 100, ATTR_HS_COLOR: (100, 70)}, + True, + ) + assert device.set_state_on.call_count == 1 + assert device.set_level.call_count == 1 + assert device.set_unmapped_color.call_count == 1 + +async def test_turn_on_color_unsupported_api_method(hass: HomeAssistant, fritz: Mock): + """Test turn device on in mapped color mode if unmapped is not supported.""" + device = FritzDeviceLightMock() + device.get_color_temps.return_value = [2700, 6500] + device.get_colors.return_value = { + "Red": [("100", "70", "10"), ("100", "50", "10"), ("100", "30", "10")] + } + mockresponse = Mock() + mockresponse.status_code = 400 + + error = HTTPError("Bad Request") + error.response = mockresponse + device.set_unmapped_color.side_effect = error + + assert await setup_config_entry( + hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz + ) assert await hass.services.async_call( DOMAIN, SERVICE_TURN_ON, @@ -135,7 +162,6 @@ async def test_turn_off(hass: HomeAssistant, fritz: Mock): assert await setup_config_entry( hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz ) - assert await hass.services.async_call( DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: ENTITY_ID}, True ) From 1d5b8746feeb331e1e77fef46efa1294eda88e4b Mon Sep 17 00:00:00 2001 From: Bryton Hall Date: Wed, 25 May 2022 03:51:19 -0400 Subject: [PATCH 820/930] Add co2 and iaq entities to venstar component (#71467) --- homeassistant/components/venstar/sensor.py | 38 ++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/venstar/sensor.py b/homeassistant/components/venstar/sensor.py index 824774f3e31..723b26d1956 100644 --- a/homeassistant/components/venstar/sensor.py +++ b/homeassistant/components/venstar/sensor.py @@ -12,7 +12,13 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PERCENTAGE, TEMP_CELSIUS, TEMP_FAHRENHEIT, TIME_MINUTES +from homeassistant.const import ( + CONCENTRATION_PARTS_PER_MILLION, + PERCENTAGE, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, + TIME_MINUTES, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -58,7 +64,7 @@ class VenstarSensorTypeMixin: value_fn: Callable[[Any, Any], Any] name_fn: Callable[[Any, Any], str] - uom_fn: Callable[[Any], str] + uom_fn: Callable[[Any], str | None] @dataclass @@ -140,7 +146,7 @@ class VenstarSensor(VenstarEntity, SensorEntity): return self.entity_description.value_fn(self.coordinator, self.sensor_name) @property - def native_unit_of_measurement(self) -> str: + def native_unit_of_measurement(self) -> str | None: """Return unit of measurement the value is expressed in.""" return self.entity_description.uom_fn(self.coordinator) @@ -150,7 +156,7 @@ SENSOR_ENTITIES: tuple[VenstarSensorEntityDescription, ...] = ( key="hum", device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, - uom_fn=lambda coordinator: PERCENTAGE, + uom_fn=lambda _: PERCENTAGE, value_fn=lambda coordinator, sensor_name: coordinator.client.get_sensor( sensor_name, "hum" ), @@ -166,11 +172,31 @@ SENSOR_ENTITIES: tuple[VenstarSensorEntityDescription, ...] = ( ), name_fn=lambda coordinator, sensor_name: f"{coordinator.client.name} {sensor_name.replace(' Temp', '')} Temperature", ), + VenstarSensorEntityDescription( + key="co2", + device_class=SensorDeviceClass.CO2, + state_class=SensorStateClass.MEASUREMENT, + uom_fn=lambda _: CONCENTRATION_PARTS_PER_MILLION, + value_fn=lambda coordinator, sensor_name: coordinator.client.get_sensor( + sensor_name, "co2" + ), + name_fn=lambda coordinator, sensor_name: f"{coordinator.client.name} {sensor_name} CO2", + ), + VenstarSensorEntityDescription( + key="iaq", + device_class=SensorDeviceClass.AQI, + state_class=SensorStateClass.MEASUREMENT, + uom_fn=lambda _: None, + value_fn=lambda coordinator, sensor_name: coordinator.client.get_sensor( + sensor_name, "iaq" + ), + name_fn=lambda coordinator, sensor_name: f"{coordinator.client.name} {sensor_name} IAQ", + ), VenstarSensorEntityDescription( key="battery", device_class=SensorDeviceClass.BATTERY, state_class=SensorStateClass.MEASUREMENT, - uom_fn=lambda coordinator: PERCENTAGE, + uom_fn=lambda _: PERCENTAGE, value_fn=lambda coordinator, sensor_name: coordinator.client.get_sensor( sensor_name, "battery" ), @@ -181,7 +207,7 @@ SENSOR_ENTITIES: tuple[VenstarSensorEntityDescription, ...] = ( RUNTIME_ENTITY = VenstarSensorEntityDescription( key="runtime", state_class=SensorStateClass.MEASUREMENT, - uom_fn=lambda coordinator: TIME_MINUTES, + uom_fn=lambda _: TIME_MINUTES, value_fn=lambda coordinator, sensor_name: coordinator.runtimes[-1][sensor_name], name_fn=lambda coordinator, sensor_name: f"{coordinator.client.name} {RUNTIME_ATTRIBUTES[sensor_name]} Runtime", ) From cc7b6244181cb68931c324b960d81a2f22f1cc53 Mon Sep 17 00:00:00 2001 From: ollo69 <60491700+ollo69@users.noreply.github.com> Date: Wed, 25 May 2022 09:57:40 +0200 Subject: [PATCH 821/930] Improve AndroidTV typing (#71036) * Improve AndroidTV typing * Change input type in async_connect_androidtv --- .../components/androidtv/__init__.py | 30 ++++-- .../components/androidtv/config_flow.py | 96 ++++++++++++------- 2 files changed, 83 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/androidtv/__init__.py b/homeassistant/components/androidtv/__init__.py index c1875a883e3..8a34b8aa858 100644 --- a/homeassistant/components/androidtv/__init__.py +++ b/homeassistant/components/androidtv/__init__.py @@ -1,9 +1,17 @@ """Support for functionality to interact with Android TV/Fire TV devices.""" +from __future__ import annotations + +from collections.abc import Mapping import os +from typing import Any from adb_shell.auth.keygen import keygen -from androidtv.adb_manager.adb_manager_sync import ADBPythonSync -from androidtv.setup_async import setup as async_androidtv_setup +from androidtv.adb_manager.adb_manager_sync import ADBPythonSync, PythonRSASigner +from androidtv.setup_async import ( + AndroidTVAsync, + FireTVAsync, + setup as async_androidtv_setup, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -41,7 +49,7 @@ RELOAD_OPTIONS = [CONF_STATE_DETECTION_RULES] _INVALID_MACS = {"ff:ff:ff:ff:ff:ff"} -def get_androidtv_mac(dev_props): +def get_androidtv_mac(dev_props: dict[str, Any]) -> str | None: """Return formatted mac from device properties.""" for prop_mac in (PROP_ETHMAC, PROP_WIFIMAC): if if_mac := dev_props.get(prop_mac): @@ -51,9 +59,13 @@ def get_androidtv_mac(dev_props): return None -def _setup_androidtv(hass, config): +def _setup_androidtv( + hass: HomeAssistant, config: dict[str, Any] +) -> tuple[str, PythonRSASigner | None, str]: """Generate an ADB key (if needed) and load it.""" - adbkey = config.get(CONF_ADBKEY, hass.config.path(STORAGE_DIR, "androidtv_adbkey")) + adbkey: str = config.get( + CONF_ADBKEY, hass.config.path(STORAGE_DIR, "androidtv_adbkey") + ) if CONF_ADB_SERVER_IP not in config: # Use "adb_shell" (Python ADB implementation) if not os.path.isfile(adbkey): @@ -73,8 +85,12 @@ def _setup_androidtv(hass, config): async def async_connect_androidtv( - hass, config, *, state_detection_rules=None, timeout=30.0 -): + hass: HomeAssistant, + config: Mapping[str, Any], + *, + state_detection_rules: dict[str, Any] | None = None, + timeout: float = 30.0, +) -> tuple[AndroidTVAsync | FireTVAsync | None, str | None]: """Connect to Android device.""" address = f"{config[CONF_HOST]}:{config[CONF_PORT]}" diff --git a/homeassistant/components/androidtv/config_flow.py b/homeassistant/components/androidtv/config_flow.py index 9df87eaffe2..bdc067c4275 100644 --- a/homeassistant/components/androidtv/config_flow.py +++ b/homeassistant/components/androidtv/config_flow.py @@ -1,14 +1,18 @@ """Config flow to configure the Android TV integration.""" +from __future__ import annotations + import json import logging import os +from typing import Any from androidtv import state_detection_rules_validator import voluptuous as vol -from homeassistant import config_entries +from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow from homeassistant.const import CONF_DEVICE_CLASS, CONF_HOST, CONF_PORT from homeassistant.core import callback +from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv from . import async_connect_androidtv, get_androidtv_mac @@ -51,24 +55,28 @@ RESULT_UNKNOWN = "unknown" _LOGGER = logging.getLogger(__name__) -def _is_file(value): +def _is_file(value: str) -> bool: """Validate that the value is an existing file.""" - file_in = os.path.expanduser(str(value)) + file_in = os.path.expanduser(value) return os.path.isfile(file_in) and os.access(file_in, os.R_OK) -class AndroidTVFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): +class AndroidTVFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a config flow.""" VERSION = 1 @callback - def _show_setup_form(self, user_input=None, error=None): + def _show_setup_form( + self, + user_input: dict[str, Any] | None = None, + error: str | None = None, + ) -> FlowResult: """Show the setup form to the user.""" - user_input = user_input or {} + host = user_input.get(CONF_HOST, "") if user_input else "" data_schema = vol.Schema( { - vol.Required(CONF_HOST, default=user_input.get(CONF_HOST, "")): str, + vol.Required(CONF_HOST, default=host): str, vol.Required(CONF_DEVICE_CLASS, default=DEFAULT_DEVICE_CLASS): vol.In( DEVICE_CLASSES ), @@ -90,10 +98,12 @@ class AndroidTVFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="user", data_schema=data_schema, - errors={"base": error}, + errors={"base": error} if error else None, ) - async def _async_check_connection(self, user_input): + async def _async_check_connection( + self, user_input: dict[str, Any] + ) -> tuple[str | None, str | None]: """Attempt to connect the Android TV.""" try: @@ -121,7 +131,9 @@ class AndroidTVFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): await aftv.adb_close() return None, unique_id - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle a flow initiated by the user.""" error = None @@ -152,32 +164,31 @@ class AndroidTVFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data=user_input, ) - user_input = user_input or {} return self._show_setup_form(user_input, error) @staticmethod @callback - def async_get_options_flow(config_entry): + def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow: """Get the options flow for this handler.""" return OptionsFlowHandler(config_entry) -class OptionsFlowHandler(config_entries.OptionsFlow): +class OptionsFlowHandler(OptionsFlow): """Handle an option flow for Android TV.""" - def __init__(self, config_entry: config_entries.ConfigEntry) -> None: + def __init__(self, config_entry: ConfigEntry) -> None: """Initialize options flow.""" self.config_entry = config_entry apps = config_entry.options.get(CONF_APPS, {}) det_rules = config_entry.options.get(CONF_STATE_DETECTION_RULES, {}) - self._apps = apps.copy() - self._state_det_rules = det_rules.copy() - self._conf_app_id = None - self._conf_rule_id = None + self._apps: dict[str, Any] = apps.copy() + self._state_det_rules: dict[str, Any] = det_rules.copy() + self._conf_app_id: str | None = None + self._conf_rule_id: str | None = None @callback - def _save_config(self, data): + def _save_config(self, data: dict[str, Any]) -> FlowResult: """Save the updated options.""" new_data = { k: v @@ -191,7 +202,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return self.async_create_entry(title="", data=new_data) - async def async_step_init(self, user_input=None): + async def async_step_init( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: """Handle options flow.""" if user_input is not None: if sel_app := user_input.get(CONF_APPS): @@ -203,7 +216,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return self._async_init_form() @callback - def _async_init_form(self): + def _async_init_form(self) -> FlowResult: """Return initial configuration form.""" apps_list = {k: f"{v} ({k})" if v else k for k, v in self._apps.items()} @@ -246,7 +259,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return self.async_show_form(step_id="init", data_schema=data_schema) - async def async_step_apps(self, user_input=None, app_id=None): + async def async_step_apps( + self, user_input: dict[str, Any] | None = None, app_id: str | None = None + ) -> FlowResult: """Handle options flow for apps list.""" if app_id is not None: self._conf_app_id = app_id if app_id != APPS_NEW_ID else None @@ -263,28 +278,32 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_init() @callback - def _async_apps_form(self, app_id): + def _async_apps_form(self, app_id: str) -> FlowResult: """Return configuration form for apps.""" - data_schema = { + app_schema = { vol.Optional( CONF_APP_NAME, description={"suggested_value": self._apps.get(app_id, "")}, ): str, } if app_id == APPS_NEW_ID: - data_schema[vol.Optional(CONF_APP_ID)] = str + data_schema = vol.Schema({**app_schema, vol.Optional(CONF_APP_ID): str}) else: - data_schema[vol.Optional(CONF_APP_DELETE, default=False)] = bool + data_schema = vol.Schema( + {**app_schema, vol.Optional(CONF_APP_DELETE, default=False): bool} + ) return self.async_show_form( step_id="apps", - data_schema=vol.Schema(data_schema), + data_schema=data_schema, description_placeholders={ "app_id": f"`{app_id}`" if app_id != APPS_NEW_ID else "", }, ) - async def async_step_rules(self, user_input=None, rule_id=None): + async def async_step_rules( + self, user_input: dict[str, Any] | None = None, rule_id: str | None = None + ) -> FlowResult: """Handle options flow for detection rules.""" if rule_id is not None: self._conf_rule_id = rule_id if rule_id != RULES_NEW_ID else None @@ -308,21 +327,26 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_init() @callback - def _async_rules_form(self, rule_id, default_id="", errors=None): + def _async_rules_form( + self, rule_id: str, default_id: str = "", errors: dict[str, str] | None = None + ) -> FlowResult: """Return configuration form for detection rules.""" state_det_rule = self._state_det_rules.get(rule_id) str_det_rule = json.dumps(state_det_rule) if state_det_rule else "" - data_schema = {} + rule_schema = {vol.Optional(CONF_RULE_VALUES, default=str_det_rule): str} if rule_id == RULES_NEW_ID: - data_schema[vol.Optional(CONF_RULE_ID, default=default_id)] = str - data_schema[vol.Optional(CONF_RULE_VALUES, default=str_det_rule)] = str - if rule_id != RULES_NEW_ID: - data_schema[vol.Optional(CONF_RULE_DELETE, default=False)] = bool + data_schema = vol.Schema( + {vol.Optional(CONF_RULE_ID, default=default_id): str, **rule_schema} + ) + else: + data_schema = vol.Schema( + {**rule_schema, vol.Optional(CONF_RULE_DELETE, default=False): bool} + ) return self.async_show_form( step_id="rules", - data_schema=vol.Schema(data_schema), + data_schema=data_schema, description_placeholders={ "rule_id": f"`{rule_id}`" if rule_id != RULES_NEW_ID else "", }, @@ -330,7 +354,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): ) -def _validate_state_det_rules(state_det_rules): +def _validate_state_det_rules(state_det_rules: str) -> list[Any] | None: """Validate a string that contain state detection rules and return a dict.""" try: json_rules = json.loads(state_det_rules) From 72cb320ed769275424b739bf00890d4d33451f5c Mon Sep 17 00:00:00 2001 From: Abadede Date: Wed, 25 May 2022 10:33:05 +0200 Subject: [PATCH 822/930] Fix Hue SONOFF S31 Lite zb plug (#69589) * Update light.py Same issue as https://github.com/home-assistant/core/issues/46619 with SONOFF S13 Lite Zigbee plug. * Update light.py --- homeassistant/components/hue/v1/light.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/hue/v1/light.py b/homeassistant/components/hue/v1/light.py index 420256f421b..74fe25dafcf 100644 --- a/homeassistant/components/hue/v1/light.py +++ b/homeassistant/components/hue/v1/light.py @@ -341,6 +341,7 @@ class HueLight(CoordinatorEntity, LightEntity): self.is_innr = False self.is_ewelink = False self.is_livarno = False + self.is_s31litezb = False self.gamut_typ = GAMUT_TYPE_UNAVAILABLE self.gamut = None else: @@ -349,6 +350,7 @@ class HueLight(CoordinatorEntity, LightEntity): self.is_innr = light.manufacturername == "innr" self.is_ewelink = light.manufacturername == "eWeLink" self.is_livarno = light.manufacturername.startswith("_TZ3000_") + self.is_s31litezb = light.modelid == "S31 Lite zb" self.gamut_typ = self.light.colorgamuttype self.gamut = self.light.colorgamut LOGGER.debug("Color gamut of %s: %s", self.name, str(self.gamut)) @@ -554,7 +556,12 @@ class HueLight(CoordinatorEntity, LightEntity): elif flash == FLASH_SHORT: command["alert"] = "select" del command["on"] - elif not self.is_innr and not self.is_ewelink and not self.is_livarno: + elif ( + not self.is_innr + and not self.is_ewelink + and not self.is_livarno + and not self.is_s31litezb + ): command["alert"] = "none" if ATTR_EFFECT in kwargs: From bf75cb3cc5620d9996bf9534c234d855c1c762b7 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Wed, 25 May 2022 10:36:09 +0200 Subject: [PATCH 823/930] Improve tests of devolo_home_network (#71873) * Improve tests * Use entity_registry_enabled_by_default fixture --- .../devolo_home_network/test_binary_sensor.py | 10 +- .../devolo_home_network/test_sensor.py | 104 ++++++++---------- 2 files changed, 48 insertions(+), 66 deletions(-) diff --git a/tests/components/devolo_home_network/test_binary_sensor.py b/tests/components/devolo_home_network/test_binary_sensor.py index 3d181da6106..564e6e5ade6 100644 --- a/tests/components/devolo_home_network/test_binary_sensor.py +++ b/tests/components/devolo_home_network/test_binary_sensor.py @@ -21,7 +21,7 @@ from .const import PLCNET_ATTACHED from tests.common import async_fire_time_changed -@pytest.mark.usefixtures("mock_device", "mock_zeroconf") +@pytest.mark.usefixtures("mock_device") async def test_binary_sensor_setup(hass: HomeAssistant): """Test default setup of the binary sensor component.""" entry = configure_integration(hass) @@ -33,7 +33,7 @@ async def test_binary_sensor_setup(hass: HomeAssistant): await hass.config_entries.async_unload(entry.entry_id) -@pytest.mark.usefixtures("mock_device", "mock_zeroconf") +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "mock_device") async def test_update_attached_to_router(hass: HomeAssistant): """Test state change of a attached_to_router binary sensor device.""" state_key = f"{DOMAIN}.{CONNECTED_TO_ROUTER}" @@ -44,12 +44,6 @@ async def test_update_attached_to_router(hass: HomeAssistant): await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - # Enable entity - er.async_update_entity(state_key, disabled_by=None) - await hass.async_block_till_done() - async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) - await hass.async_block_till_done() - state = hass.states.get(state_key) assert state is not None assert state.state == STATE_OFF diff --git a/tests/components/devolo_home_network/test_sensor.py b/tests/components/devolo_home_network/test_sensor.py index a7b1be5f07d..582d6533802 100644 --- a/tests/components/devolo_home_network/test_sensor.py +++ b/tests/components/devolo_home_network/test_sensor.py @@ -21,7 +21,6 @@ from tests.common import async_fire_time_changed @pytest.mark.usefixtures("mock_device") -@pytest.mark.usefixtures("mock_zeroconf") async def test_sensor_setup(hass: HomeAssistant): """Test default setup of the sensor component.""" entry = configure_integration(hass) @@ -36,7 +35,6 @@ async def test_sensor_setup(hass: HomeAssistant): @pytest.mark.usefixtures("mock_device") -@pytest.mark.usefixtures("mock_zeroconf") async def test_update_connected_wifi_clients(hass: HomeAssistant): """Test state change of a connected_wifi_clients sensor device.""" state_key = f"{DOMAIN}.connected_wifi_clients" @@ -73,85 +71,75 @@ async def test_update_connected_wifi_clients(hass: HomeAssistant): await hass.config_entries.async_unload(entry.entry_id) -@pytest.mark.usefixtures("mock_device") -@pytest.mark.usefixtures("mock_zeroconf") +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "mock_device") async def test_update_neighboring_wifi_networks(hass: HomeAssistant): """Test state change of a neighboring_wifi_networks sensor device.""" state_key = f"{DOMAIN}.neighboring_wifi_networks" entry = configure_integration(hass) + er = entity_registry.async_get(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == "1" + assert er.async_get(state_key).entity_category is EntityCategory.DIAGNOSTIC + + # Emulate device failure with patch( - "homeassistant.helpers.entity.Entity.entity_registry_enabled_default", - return_value=True, + "devolo_plc_api.device_api.deviceapi.DeviceApi.async_get_wifi_neighbor_access_points", + side_effect=DeviceUnavailable, ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - state = hass.states.get(state_key) - assert state is not None - assert state.state == "1" - - er = entity_registry.async_get(hass) - assert er.async_get(state_key).entity_category is EntityCategory.DIAGNOSTIC - - # Emulate device failure - with patch( - "devolo_plc_api.device_api.deviceapi.DeviceApi.async_get_wifi_neighbor_access_points", - side_effect=DeviceUnavailable, - ): - async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) - await hass.async_block_till_done() - - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_UNAVAILABLE - - # Emulate state change async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) await hass.async_block_till_done() state = hass.states.get(state_key) assert state is not None - assert state.state == "1" + assert state.state == STATE_UNAVAILABLE - await hass.config_entries.async_unload(entry.entry_id) + # Emulate state change + async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == "1" + + await hass.config_entries.async_unload(entry.entry_id) -@pytest.mark.usefixtures("mock_device") -@pytest.mark.usefixtures("mock_zeroconf") +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "mock_device") async def test_update_connected_plc_devices(hass: HomeAssistant): """Test state change of a connected_plc_devices sensor device.""" state_key = f"{DOMAIN}.connected_plc_devices" entry = configure_integration(hass) + er = entity_registry.async_get(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == "1" + assert er.async_get(state_key).entity_category is EntityCategory.DIAGNOSTIC + + # Emulate device failure with patch( - "homeassistant.helpers.entity.Entity.entity_registry_enabled_default", - return_value=True, + "devolo_plc_api.plcnet_api.plcnetapi.PlcNetApi.async_get_network_overview", + side_effect=DeviceUnavailable, ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - state = hass.states.get(state_key) - assert state is not None - assert state.state == "1" - - er = entity_registry.async_get(hass) - assert er.async_get(state_key).entity_category is EntityCategory.DIAGNOSTIC - - # Emulate device failure - with patch( - "devolo_plc_api.plcnet_api.plcnetapi.PlcNetApi.async_get_network_overview", - side_effect=DeviceUnavailable, - ): - async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) - await hass.async_block_till_done() - - state = hass.states.get(state_key) - assert state is not None - assert state.state == STATE_UNAVAILABLE - - # Emulate state change async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) await hass.async_block_till_done() state = hass.states.get(state_key) assert state is not None - assert state.state == "1" + assert state.state == STATE_UNAVAILABLE - await hass.config_entries.async_unload(entry.entry_id) + # Emulate state change + async_fire_time_changed(hass, dt.utcnow() + LONG_UPDATE_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get(state_key) + assert state is not None + assert state.state == "1" + + await hass.config_entries.async_unload(entry.entry_id) From 53fb581bff2481cbd1e53d2a47d583071277f6c2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 25 May 2022 10:37:09 +0200 Subject: [PATCH 824/930] Adjust config-flow type hints in dynalite (#72476) --- homeassistant/components/dynalite/config_flow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/dynalite/config_flow.py b/homeassistant/components/dynalite/config_flow.py index d148d09354f..d723825319a 100644 --- a/homeassistant/components/dynalite/config_flow.py +++ b/homeassistant/components/dynalite/config_flow.py @@ -5,6 +5,7 @@ from typing import Any from homeassistant import config_entries from homeassistant.const import CONF_HOST +from homeassistant.data_entry_flow import FlowResult from .bridge import DynaliteBridge from .const import DOMAIN, LOGGER @@ -20,7 +21,7 @@ class DynaliteFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Initialize the Dynalite flow.""" self.host = None - async def async_step_import(self, import_info: dict[str, Any]) -> Any: + async def async_step_import(self, import_info: dict[str, Any]) -> FlowResult: """Import a new bridge as a config entry.""" LOGGER.debug("Starting async_step_import - %s", import_info) host = import_info[CONF_HOST] From 5b896b315eb1eea2b89e385ed1566d1ab1f22ef0 Mon Sep 17 00:00:00 2001 From: Austin Mroczek Date: Wed, 25 May 2022 01:49:53 -0700 Subject: [PATCH 825/930] Add TotalConnect options flow to auto-bypass low battery (#62458) * rebase * use bypass option in async_setup_entry * add test for options flow * default to False for AUTO_BYPASS * fix bypass defaults --- .../components/totalconnect/__init__.py | 16 ++++++- .../components/totalconnect/config_flow.py | 34 +++++++++++++- .../components/totalconnect/const.py | 2 + .../components/totalconnect/strings.json | 11 +++++ .../totalconnect/test_config_flow.py | 46 ++++++++++++++++++- 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/totalconnect/__init__.py b/homeassistant/components/totalconnect/__init__.py index 8a4aee0debb..87977e5c1db 100644 --- a/homeassistant/components/totalconnect/__init__.py +++ b/homeassistant/components/totalconnect/__init__.py @@ -17,7 +17,7 @@ from homeassistant.exceptions import ConfigEntryAuthFailed import homeassistant.helpers.config_validation as cv from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import CONF_USERCODES, DOMAIN +from .const import AUTO_BYPASS, CONF_USERCODES, DOMAIN PLATFORMS = [Platform.ALARM_CONTROL_PANEL, Platform.BINARY_SENSOR] @@ -31,6 +31,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: conf = entry.data username = conf[CONF_USERNAME] password = conf[CONF_PASSWORD] + bypass = entry.options.get(AUTO_BYPASS, False) if CONF_USERCODES not in conf: # should only happen for those who used UI before we added usercodes @@ -41,7 +42,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: try: client = await hass.async_add_executor_job( - TotalConnectClient, username, password, usercodes + TotalConnectClient, username, password, usercodes, bypass ) except AuthenticationError as exception: raise ConfigEntryAuthFailed( @@ -54,6 +55,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + entry.async_on_unload(entry.add_update_listener(update_listener)) + return True @@ -66,6 +70,14 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Update listener.""" + bypass = entry.options.get(AUTO_BYPASS, False) + client = hass.data[DOMAIN][entry.entry_id].client + for location_id in client.locations: + client.locations[location_id].auto_bypass_low_battery = bypass + + class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator): """Class to fetch data from TotalConnect.""" diff --git a/homeassistant/components/totalconnect/config_flow.py b/homeassistant/components/totalconnect/config_flow.py index 013b08b50be..49e60b5b46e 100644 --- a/homeassistant/components/totalconnect/config_flow.py +++ b/homeassistant/components/totalconnect/config_flow.py @@ -5,8 +5,9 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_LOCATION, CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import callback -from .const import CONF_USERCODES, DOMAIN +from .const import AUTO_BYPASS, CONF_USERCODES, DOMAIN PASSWORD_DATA_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) @@ -162,3 +163,34 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) return self.async_abort(reason="reauth_successful") + + @staticmethod + @callback + def async_get_options_flow(config_entry): + """Get options flow.""" + return TotalConnectOptionsFlowHandler(config_entry) + + +class TotalConnectOptionsFlowHandler(config_entries.OptionsFlow): + """TotalConnect options flow handler.""" + + def __init__(self, config_entry): + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init(self, user_input=None): + """Manage the options.""" + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + AUTO_BYPASS, + default=self.config_entry.options.get(AUTO_BYPASS, False), + ): bool + } + ), + ) diff --git a/homeassistant/components/totalconnect/const.py b/homeassistant/components/totalconnect/const.py index ba217bd4ca7..5012a303b69 100644 --- a/homeassistant/components/totalconnect/const.py +++ b/homeassistant/components/totalconnect/const.py @@ -2,6 +2,8 @@ DOMAIN = "totalconnect" CONF_USERCODES = "usercodes" +CONF_LOCATION = "location" +AUTO_BYPASS = "auto_bypass_low_battery" # Most TotalConnect alarms will work passing '-1' as usercode DEFAULT_USERCODE = "-1" diff --git a/homeassistant/components/totalconnect/strings.json b/homeassistant/components/totalconnect/strings.json index 64ca1beafd8..346ea7ef403 100644 --- a/homeassistant/components/totalconnect/strings.json +++ b/homeassistant/components/totalconnect/strings.json @@ -28,5 +28,16 @@ "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", "no_locations": "No locations are available for this user, check TotalConnect settings" } + }, + "options": { + "step": { + "init": { + "title": "TotalConnect Options", + "description": "Automatically bypass zones the moment they report a low battery.", + "data": { + "auto_bypass_low_battery": "Auto bypass low battery" + } + } + } } } diff --git a/tests/components/totalconnect/test_config_flow.py b/tests/components/totalconnect/test_config_flow.py index 631553a4af4..78b121dda77 100644 --- a/tests/components/totalconnect/test_config_flow.py +++ b/tests/components/totalconnect/test_config_flow.py @@ -4,9 +4,14 @@ from unittest.mock import patch from total_connect_client.exceptions import AuthenticationError from homeassistant import data_entry_flow -from homeassistant.components.totalconnect.const import CONF_USERCODES, DOMAIN +from homeassistant.components.totalconnect.const import ( + AUTO_BYPASS, + CONF_USERCODES, + DOMAIN, +) from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.const import CONF_PASSWORD +from homeassistant.core import HomeAssistant from .common import ( CONFIG_DATA, @@ -190,3 +195,42 @@ async def test_no_locations(hass): await hass.async_block_till_done() assert mock_request.call_count == 1 + + +async def test_options_flow(hass: HomeAssistant): + """Test config flow options.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data=CONFIG_DATA, + unique_id=USERNAME, + ) + config_entry.add_to_hass(hass) + + responses = [ + RESPONSE_AUTHENTICATE, + RESPONSE_PARTITION_DETAILS, + RESPONSE_GET_ZONE_DETAILS_SUCCESS, + RESPONSE_DISARMED, + RESPONSE_DISARMED, + RESPONSE_DISARMED, + ] + + with patch(TOTALCONNECT_REQUEST, side_effect=responses): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], user_input={AUTO_BYPASS: True} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert config_entry.options == {AUTO_BYPASS: True} + await hass.async_block_till_done() + + assert await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done() From 42c80dda85f567192c182da2b4c603408a890381 Mon Sep 17 00:00:00 2001 From: BigMoby Date: Wed, 25 May 2022 10:52:06 +0200 Subject: [PATCH 826/930] Create iAlarmXR integration (#67817) * Creating iAlarmXR integration * fixing after review code * fixing remaining review hints * fixing remaining review hints * updating underlying pyialarm library * Creating iAlarmXR integration * fixing after review code * fixing remaining review hints * fixing remaining review hints * updating underlying pyialarm library * fixing after iMicknl review * Improving exception handling * Updating pyialarmxr library * fixing after merge dev * fixing after iMicknl review * Update CODEOWNERS Co-authored-by: Ludovico de Nittis * fixing iot_class * Update homeassistant/components/ialarmxr/config_flow.py Co-authored-by: J. Nick Koston * fixing after bdraco review * Update homeassistant/components/ialarmxr/config_flow.py Co-authored-by: J. Nick Koston * reverting catching exception in setup step * Update homeassistant/components/ialarmxr/__init__.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/__init__.py Co-authored-by: J. Nick Koston * fixing after bdraco suggestions * Update homeassistant/components/ialarmxr/alarm_control_panel.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/alarm_control_panel.py Co-authored-by: Mick Vleeshouwer * Update homeassistant/components/ialarmxr/config_flow.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/config_flow.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/__init__.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/__init__.py Co-authored-by: J. Nick Koston * Update homeassistant/components/ialarmxr/utils.py Co-authored-by: J. Nick Koston * regenerate translation and rename function to async_get_ialarmxr_mac * removing and collapsing unused error messages * fixing tests * improve code coverage in tests * improve code coverage in tests * improve code coverage in tests * fixing retry policy with new pyalarmxr library * snake case fix * renaming integration in ialarm_xr * renaming control panel name Co-authored-by: Ludovico de Nittis Co-authored-by: J. Nick Koston Co-authored-by: Mick Vleeshouwer --- .coveragerc | 1 + .strict-typing | 1 + CODEOWNERS | 2 + .../components/ialarm_xr/__init__.py | 101 ++++++++++ .../ialarm_xr/alarm_control_panel.py | 63 ++++++ .../components/ialarm_xr/config_flow.py | 94 +++++++++ homeassistant/components/ialarm_xr/const.py | 18 ++ .../components/ialarm_xr/manifest.json | 10 + .../components/ialarm_xr/strings.json | 21 ++ .../components/ialarm_xr/translations/en.json | 21 ++ homeassistant/components/ialarm_xr/utils.py | 18 ++ homeassistant/generated/config_flows.py | 1 + mypy.ini | 11 ++ requirements_all.txt | 3 + requirements_test_all.txt | 3 + tests/components/ialarm_xr/__init__.py | 1 + .../components/ialarm_xr/test_config_flow.py | 185 ++++++++++++++++++ tests/components/ialarm_xr/test_init.py | 120 ++++++++++++ 18 files changed, 674 insertions(+) create mode 100644 homeassistant/components/ialarm_xr/__init__.py create mode 100644 homeassistant/components/ialarm_xr/alarm_control_panel.py create mode 100644 homeassistant/components/ialarm_xr/config_flow.py create mode 100644 homeassistant/components/ialarm_xr/const.py create mode 100644 homeassistant/components/ialarm_xr/manifest.json create mode 100644 homeassistant/components/ialarm_xr/strings.json create mode 100644 homeassistant/components/ialarm_xr/translations/en.json create mode 100644 homeassistant/components/ialarm_xr/utils.py create mode 100644 tests/components/ialarm_xr/__init__.py create mode 100644 tests/components/ialarm_xr/test_config_flow.py create mode 100644 tests/components/ialarm_xr/test_init.py diff --git a/.coveragerc b/.coveragerc index 4e903d68aa5..aced69a4714 100644 --- a/.coveragerc +++ b/.coveragerc @@ -514,6 +514,7 @@ omit = homeassistant/components/hvv_departures/__init__.py homeassistant/components/hydrawise/* homeassistant/components/ialarm/alarm_control_panel.py + homeassistant/components/ialarm_xr/alarm_control_panel.py homeassistant/components/iammeter/sensor.py homeassistant/components/iaqualink/binary_sensor.py homeassistant/components/iaqualink/climate.py diff --git a/.strict-typing b/.strict-typing index 2b6295b9157..e07d8b9cfc8 100644 --- a/.strict-typing +++ b/.strict-typing @@ -127,6 +127,7 @@ homeassistant.components.homewizard.* homeassistant.components.http.* homeassistant.components.huawei_lte.* homeassistant.components.hyperion.* +homeassistant.components.ialarm_xr.* homeassistant.components.image_processing.* homeassistant.components.input_button.* homeassistant.components.input_select.* diff --git a/CODEOWNERS b/CODEOWNERS index 9cc32503853..8a7a058e01f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -470,6 +470,8 @@ build.json @home-assistant/supervisor /tests/components/hyperion/ @dermotduffy /homeassistant/components/ialarm/ @RyuzakiKK /tests/components/ialarm/ @RyuzakiKK +/homeassistant/components/ialarm_xr/ @bigmoby +/tests/components/ialarm_xr/ @bigmoby /homeassistant/components/iammeter/ @lewei50 /homeassistant/components/iaqualink/ @flz /tests/components/iaqualink/ @flz diff --git a/homeassistant/components/ialarm_xr/__init__.py b/homeassistant/components/ialarm_xr/__init__.py new file mode 100644 index 00000000000..9a41b5ebab7 --- /dev/null +++ b/homeassistant/components/ialarm_xr/__init__.py @@ -0,0 +1,101 @@ +"""iAlarmXR integration.""" +from __future__ import annotations + +import asyncio +import logging + +from async_timeout import timeout +from pyialarmxr import ( + IAlarmXR, + IAlarmXRGenericException, + IAlarmXRSocketTimeoutException, +) + +from homeassistant.components.alarm_control_panel import SCAN_INTERVAL +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + CONF_HOST, + CONF_PASSWORD, + CONF_PORT, + CONF_USERNAME, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +from .const import DOMAIN, IALARMXR_TO_HASS +from .utils import async_get_ialarmxr_mac + +PLATFORMS = [Platform.ALARM_CONTROL_PANEL] +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up iAlarmXR config.""" + host = entry.data[CONF_HOST] + port = entry.data[CONF_PORT] + username = entry.data[CONF_USERNAME] + password = entry.data[CONF_PASSWORD] + + ialarmxr = IAlarmXR(username, password, host, port) + + try: + async with timeout(10): + ialarmxr_mac = await async_get_ialarmxr_mac(hass, ialarmxr) + except ( + asyncio.TimeoutError, + ConnectionError, + IAlarmXRGenericException, + IAlarmXRSocketTimeoutException, + ) as ex: + raise ConfigEntryNotReady from ex + + coordinator = IAlarmXRDataUpdateCoordinator(hass, ialarmxr, ialarmxr_mac) + await coordinator.async_config_entry_first_refresh() + + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload iAlarmXR config.""" + if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + hass.data[DOMAIN].pop(entry.entry_id) + return unload_ok + + +class IAlarmXRDataUpdateCoordinator(DataUpdateCoordinator): + """Class to manage fetching iAlarmXR data.""" + + def __init__(self, hass: HomeAssistant, ialarmxr: IAlarmXR, mac: str) -> None: + """Initialize global iAlarm data updater.""" + self.ialarmxr: IAlarmXR = ialarmxr + self.state: str | None = None + self.host: str = ialarmxr.host + self.mac: str = mac + + super().__init__( + hass, + _LOGGER, + name=DOMAIN, + update_interval=SCAN_INTERVAL, + ) + + def _update_data(self) -> None: + """Fetch data from iAlarmXR via sync functions.""" + status: int = self.ialarmxr.get_status() + _LOGGER.debug("iAlarmXR status: %s", status) + + self.state = IALARMXR_TO_HASS.get(status) + + async def _async_update_data(self) -> None: + """Fetch data from iAlarmXR.""" + try: + async with timeout(10): + await self.hass.async_add_executor_job(self._update_data) + except ConnectionError as error: + raise UpdateFailed(error) from error diff --git a/homeassistant/components/ialarm_xr/alarm_control_panel.py b/homeassistant/components/ialarm_xr/alarm_control_panel.py new file mode 100644 index 00000000000..7b47ce3d7fa --- /dev/null +++ b/homeassistant/components/ialarm_xr/alarm_control_panel.py @@ -0,0 +1,63 @@ +"""Interfaces with iAlarmXR control panels.""" +from __future__ import annotations + +from homeassistant.components.alarm_control_panel import ( + AlarmControlPanelEntity, + AlarmControlPanelEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from . import IAlarmXRDataUpdateCoordinator +from .const import DOMAIN + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up a iAlarmXR alarm control panel based on a config entry.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities([IAlarmXRPanel(coordinator)]) + + +class IAlarmXRPanel(CoordinatorEntity, AlarmControlPanelEntity): + """Representation of an iAlarmXR device.""" + + _attr_supported_features = ( + AlarmControlPanelEntityFeature.ARM_HOME + | AlarmControlPanelEntityFeature.ARM_AWAY + ) + _attr_name = "iAlarm_XR" + _attr_icon = "mdi:security" + + def __init__(self, coordinator: IAlarmXRDataUpdateCoordinator) -> None: + """Initialize the alarm panel.""" + super().__init__(coordinator) + self.coordinator: IAlarmXRDataUpdateCoordinator = coordinator + self._attr_unique_id = coordinator.mac + self._attr_device_info = DeviceInfo( + manufacturer="Antifurto365 - Meian", + name=self.name, + connections={(device_registry.CONNECTION_NETWORK_MAC, coordinator.mac)}, + ) + + @property + def state(self) -> str | None: + """Return the state of the device.""" + return self.coordinator.state + + def alarm_disarm(self, code: str | None = None) -> None: + """Send disarm command.""" + self.coordinator.ialarmxr.disarm() + + def alarm_arm_home(self, code: str | None = None) -> None: + """Send arm home command.""" + self.coordinator.ialarmxr.arm_stay() + + def alarm_arm_away(self, code: str | None = None) -> None: + """Send arm away command.""" + self.coordinator.ialarmxr.arm_away() diff --git a/homeassistant/components/ialarm_xr/config_flow.py b/homeassistant/components/ialarm_xr/config_flow.py new file mode 100644 index 00000000000..06509a82eb5 --- /dev/null +++ b/homeassistant/components/ialarm_xr/config_flow.py @@ -0,0 +1,94 @@ +"""Config flow for Antifurto365 iAlarmXR integration.""" +from __future__ import annotations + +import logging +from logging import Logger +from typing import Any + +from pyialarmxr import ( + IAlarmXR, + IAlarmXRGenericException, + IAlarmXRSocketTimeoutException, +) +import voluptuous as vol + +from homeassistant import config_entries, core +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN +from .utils import async_get_ialarmxr_mac + +_LOGGER: Logger = logging.getLogger(__name__) + +DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_HOST, default=IAlarmXR.IALARM_P2P_DEFAULT_HOST): str, + vol.Required(CONF_PORT, default=IAlarmXR.IALARM_P2P_DEFAULT_PORT): int, + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + } +) + + +async def _async_get_device_formatted_mac( + hass: core.HomeAssistant, username: str, password: str, host: str, port: int +) -> str: + """Return iAlarmXR mac address.""" + + ialarmxr = IAlarmXR(username, password, host, port) + return await async_get_ialarmxr_mac(hass, ialarmxr) + + +class IAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for Antifurto365 iAlarmXR.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle the initial step.""" + + errors = {} + + if user_input is not None: + mac = None + host = user_input[CONF_HOST] + port = user_input[CONF_PORT] + username = user_input[CONF_USERNAME] + password = user_input[CONF_PASSWORD] + + try: + # If we are able to get the MAC address, we are able to establish + # a connection to the device. + mac = await _async_get_device_formatted_mac( + self.hass, username, password, host, port + ) + except ConnectionError: + errors["base"] = "cannot_connect" + except IAlarmXRGenericException as ialarmxr_exception: + _LOGGER.debug( + "IAlarmXRGenericException with message: [ %s ]", + ialarmxr_exception.message, + ) + errors["base"] = "unknown" + except IAlarmXRSocketTimeoutException as ialarmxr_socket_timeout_exception: + _LOGGER.debug( + "IAlarmXRSocketTimeoutException with message: [ %s ]", + ialarmxr_socket_timeout_exception.message, + ) + errors["base"] = "unknown" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + + if not errors: + await self.async_set_unique_id(mac) + self._abort_if_unique_id_configured() + return self.async_create_entry( + title=user_input[CONF_HOST], data=user_input + ) + return self.async_show_form( + step_id="user", data_schema=DATA_SCHEMA, errors=errors + ) diff --git a/homeassistant/components/ialarm_xr/const.py b/homeassistant/components/ialarm_xr/const.py new file mode 100644 index 00000000000..a208f5290b6 --- /dev/null +++ b/homeassistant/components/ialarm_xr/const.py @@ -0,0 +1,18 @@ +"""Constants for the iAlarmXR integration.""" +from pyialarmxr import IAlarmXR + +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_HOME, + STATE_ALARM_DISARMED, + STATE_ALARM_TRIGGERED, +) + +DOMAIN = "ialarm_xr" + +IALARMXR_TO_HASS = { + IAlarmXR.ARMED_AWAY: STATE_ALARM_ARMED_AWAY, + IAlarmXR.ARMED_STAY: STATE_ALARM_ARMED_HOME, + IAlarmXR.DISARMED: STATE_ALARM_DISARMED, + IAlarmXR.TRIGGERED: STATE_ALARM_TRIGGERED, +} diff --git a/homeassistant/components/ialarm_xr/manifest.json b/homeassistant/components/ialarm_xr/manifest.json new file mode 100644 index 00000000000..4861e9c901f --- /dev/null +++ b/homeassistant/components/ialarm_xr/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "ialarm_xr", + "name": "Antifurto365 iAlarmXR", + "documentation": "https://www.home-assistant.io/integrations/ialarmxr", + "requirements": ["pyialarmxr==1.0.13"], + "codeowners": ["@bigmoby"], + "config_flow": true, + "iot_class": "cloud_polling", + "loggers": ["pyialarmxr"] +} diff --git a/homeassistant/components/ialarm_xr/strings.json b/homeassistant/components/ialarm_xr/strings.json new file mode 100644 index 00000000000..1650ae28c84 --- /dev/null +++ b/homeassistant/components/ialarm_xr/strings.json @@ -0,0 +1,21 @@ +{ + "config": { + "step": { + "user": { + "data": { + "host": "[%key:common::config_flow::data::host%]", + "port": "[%key:common::config_flow::data::port%]", + "username": "[%key:common::config_flow::data::username%]", + "password": "[%key:common::config_flow::data::password%]" + } + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + } + } +} diff --git a/homeassistant/components/ialarm_xr/translations/en.json b/homeassistant/components/ialarm_xr/translations/en.json new file mode 100644 index 00000000000..bf2bf989dcd --- /dev/null +++ b/homeassistant/components/ialarm_xr/translations/en.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Device is already configured" + }, + "error": { + "cannot_connect": "Failed to connect", + "unknown": "Unexpected error" + }, + "step": { + "user": { + "data": { + "host": "Host", + "password": "Password", + "port": "Port", + "username": "Username" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/ialarm_xr/utils.py b/homeassistant/components/ialarm_xr/utils.py new file mode 100644 index 00000000000..db82a3fcd44 --- /dev/null +++ b/homeassistant/components/ialarm_xr/utils.py @@ -0,0 +1,18 @@ +"""iAlarmXR utils.""" +import logging + +from pyialarmxr import IAlarmXR + +from homeassistant import core +from homeassistant.helpers.device_registry import format_mac + +_LOGGER = logging.getLogger(__name__) + + +async def async_get_ialarmxr_mac(hass: core.HomeAssistant, ialarmxr: IAlarmXR) -> str: + """Retrieve iAlarmXR MAC address.""" + _LOGGER.debug("Retrieving ialarmxr mac address") + + mac = await hass.async_add_executor_job(ialarmxr.get_mac) + + return format_mac(mac) diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index b0bbd3e1b36..e9ba5971e07 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -161,6 +161,7 @@ FLOWS = { "hvv_departures", "hyperion", "ialarm", + "ialarm_xr", "iaqualink", "icloud", "ifttt", diff --git a/mypy.ini b/mypy.ini index 3cc9653e27a..e0c512782fb 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1160,6 +1160,17 @@ no_implicit_optional = true warn_return_any = true warn_unreachable = true +[mypy-homeassistant.components.ialarm_xr.*] +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +no_implicit_optional = true +warn_return_any = true +warn_unreachable = true + [mypy-homeassistant.components.image_processing.*] check_untyped_defs = true disallow_incomplete_defs = true diff --git a/requirements_all.txt b/requirements_all.txt index a52d465fac6..0f11d8576bc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1549,6 +1549,9 @@ pyhomeworks==0.0.6 # homeassistant.components.ialarm pyialarm==1.9.0 +# homeassistant.components.ialarm_xr +pyialarmxr==1.0.13 + # homeassistant.components.icloud pyicloud==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 897ffe50074..f13a1a87dda 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1037,6 +1037,9 @@ pyhomematic==0.1.77 # homeassistant.components.ialarm pyialarm==1.9.0 +# homeassistant.components.ialarm_xr +pyialarmxr==1.0.13 + # homeassistant.components.icloud pyicloud==1.0.0 diff --git a/tests/components/ialarm_xr/__init__.py b/tests/components/ialarm_xr/__init__.py new file mode 100644 index 00000000000..4097867f70b --- /dev/null +++ b/tests/components/ialarm_xr/__init__.py @@ -0,0 +1 @@ +"""Tests for the Antifurto365 iAlarmXR integration.""" diff --git a/tests/components/ialarm_xr/test_config_flow.py b/tests/components/ialarm_xr/test_config_flow.py new file mode 100644 index 00000000000..22a70bda067 --- /dev/null +++ b/tests/components/ialarm_xr/test_config_flow.py @@ -0,0 +1,185 @@ +"""Test the Antifurto365 iAlarmXR config flow.""" + +from unittest.mock import patch + +from pyialarmxr import IAlarmXRGenericException, IAlarmXRSocketTimeoutException + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components.ialarm_xr.const import DOMAIN +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME + +from tests.common import MockConfigEntry + +TEST_DATA = { + CONF_HOST: "1.1.1.1", + CONF_PORT: 18034, + CONF_USERNAME: "000ZZZ0Z00", + CONF_PASSWORD: "00000000", +} + +TEST_MAC = "00:00:54:12:34:56" + + +async def test_form(hass): + """Test we get the form.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["handler"] == "ialarm_xr" + assert result["data_schema"].schema.get("host") == str + assert result["data_schema"].schema.get("port") == int + assert result["data_schema"].schema.get("password") == str + assert result["data_schema"].schema.get("username") == str + assert result["errors"] == {} + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_status", + return_value=1, + ), patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + return_value=TEST_MAC, + ), patch( + "homeassistant.components.ialarm_xr.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + await hass.async_block_till_done() + + assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == TEST_DATA["host"] + assert result2["data"] == TEST_DATA + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_cannot_connect(hass): + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + side_effect=ConnectionError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_form_exception(hass): + """Test we handle unknown exception.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + side_effect=Exception, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_form_cannot_connect_throwing_connection_error(hass): + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + side_effect=ConnectionError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_form_cannot_connect_throwing_socket_timeout_exception(hass): + """Test we handle cannot connect error because of socket timeout.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + side_effect=IAlarmXRSocketTimeoutException, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_form_cannot_connect_throwing_generic_exception(hass): + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + side_effect=IAlarmXRGenericException, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["errors"] == {"base": "unknown"} + + +async def test_form_already_exists(hass): + """Test that a flow with an existing host aborts.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id=TEST_MAC, + data=TEST_DATA, + ) + + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", + return_value=TEST_MAC, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_DATA + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result2["reason"] == "already_configured" + + +async def test_flow_user_step_no_input(hass): + """Test appropriate error when no input is provided.""" + _result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + result = await hass.config_entries.flow.async_configure( + _result["flow_id"], user_input=None + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == config_entries.SOURCE_USER + assert result["errors"] == {} diff --git a/tests/components/ialarm_xr/test_init.py b/tests/components/ialarm_xr/test_init.py new file mode 100644 index 00000000000..8486b7049e6 --- /dev/null +++ b/tests/components/ialarm_xr/test_init.py @@ -0,0 +1,120 @@ +"""Test the Antifurto365 iAlarmXR init.""" +import asyncio +from datetime import timedelta +from unittest.mock import Mock, patch +from uuid import uuid4 + +import pytest + +from homeassistant.components.ialarm_xr.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME +from homeassistant.util.dt import utcnow + +from tests.common import MockConfigEntry, async_fire_time_changed + + +@pytest.fixture(name="ialarmxr_api") +def ialarmxr_api_fixture(): + """Set up IAlarmXR API fixture.""" + with patch("homeassistant.components.ialarm_xr.IAlarmXR") as mock_ialarm_api: + yield mock_ialarm_api + + +@pytest.fixture(name="mock_config_entry") +def mock_config_fixture(): + """Return a fake config entry.""" + return MockConfigEntry( + domain=DOMAIN, + data={ + CONF_HOST: "192.168.10.20", + CONF_PORT: 18034, + CONF_USERNAME: "000ZZZ0Z00", + CONF_PASSWORD: "00000000", + }, + entry_id=str(uuid4()), + ) + + +async def test_setup_entry(hass, ialarmxr_api, mock_config_entry): + """Test setup entry.""" + ialarmxr_api.return_value.get_mac = Mock(return_value="00:00:54:12:34:56") + + mock_config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + ialarmxr_api.return_value.get_mac.assert_called_once() + assert mock_config_entry.state is ConfigEntryState.LOADED + + +async def test_setup_not_ready(hass, ialarmxr_api, mock_config_entry): + """Test setup failed because we can't connect to the alarm system.""" + ialarmxr_api.return_value.get_mac = Mock(side_effect=ConnectionError) + + mock_config_entry.add_to_hass(hass) + assert not await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_unload_entry(hass, ialarmxr_api, mock_config_entry): + """Test being able to unload an entry.""" + ialarmxr_api.return_value.get_mac = Mock(return_value="00:00:54:12:34:56") + + mock_config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert mock_config_entry.state is ConfigEntryState.LOADED + assert await hass.config_entries.async_unload(mock_config_entry.entry_id) + assert mock_config_entry.state is ConfigEntryState.NOT_LOADED + + +async def test_setup_not_ready_connection_error(hass, ialarmxr_api, mock_config_entry): + """Test setup failed because we can't connect to the alarm system.""" + ialarmxr_api.return_value.get_status = Mock(side_effect=ConnectionError) + + mock_config_entry.add_to_hass(hass) + assert not await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + future = utcnow() + timedelta(seconds=30) + async_fire_time_changed(hass, future) + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_setup_not_ready_timeout(hass, ialarmxr_api, mock_config_entry): + """Test setup failed because we can't connect to the alarm system.""" + ialarmxr_api.return_value.get_status = Mock(side_effect=asyncio.TimeoutError) + + mock_config_entry.add_to_hass(hass) + assert not await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + future = utcnow() + timedelta(seconds=30) + async_fire_time_changed(hass, future) + assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_setup_entry_and_then_fail_on_update( + hass, ialarmxr_api, mock_config_entry +): + """Test setup entry.""" + ialarmxr_api.return_value.get_mac = Mock(return_value="00:00:54:12:34:56") + ialarmxr_api.return_value.get_status = Mock(value=ialarmxr_api.DISARMED) + + mock_config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + ialarmxr_api.return_value.get_mac.assert_called_once() + ialarmxr_api.return_value.get_status.assert_called_once() + assert mock_config_entry.state is ConfigEntryState.LOADED + + ialarmxr_api.return_value.get_status = Mock(side_effect=asyncio.TimeoutError) + future = utcnow() + timedelta(seconds=60) + async_fire_time_changed(hass, future) + await hass.async_block_till_done() + ialarmxr_api.return_value.get_status.assert_called_once() + assert hass.states.get("alarm_control_panel.ialarm_xr").state == "unavailable" From 36ff15b038abef56a3750b4ce2d1fc0951b5d019 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 25 May 2022 12:15:33 +0200 Subject: [PATCH 827/930] Fix container init (#72478) --- rootfs/init | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rootfs/init b/rootfs/init index bfc4802c858..0aa246c33ab 100755 --- a/rootfs/init +++ b/rootfs/init @@ -1,4 +1,4 @@ -#!/usr/bin/env bashio +#!/bin/sh -e # This is the first program launched at container start. # We don't know where our binaries are and we cannot guarantee @@ -43,7 +43,7 @@ fi # root, can be a normal user). exec s6-overlay-suexec \ - ' /package/admin/s6-overlay-@VERSION@/libexec/preinit' \ + ' /package/admin/s6-overlay/libexec/preinit' \ '' \ - /package/admin/s6-overlay-@VERSION@/libexec/stage0 \ + /package/admin/s6-overlay/libexec/stage0 \ "$@" From 84d1e109483e1202a82714b9c0bddae2632c1f83 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 25 May 2022 12:53:12 +0200 Subject: [PATCH 828/930] Bump pychromecast to 12.1.3 (#72475) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index 10edc81e0fc..644a517c666 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==12.1.2"], + "requirements": ["pychromecast==12.1.3"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index 0f11d8576bc..9093246c7ff 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1405,7 +1405,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==12.1.2 +pychromecast==12.1.3 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f13a1a87dda..096db3431a1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -950,7 +950,7 @@ pybotvac==0.0.23 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==12.1.2 +pychromecast==12.1.3 # homeassistant.components.climacell pyclimacell==0.18.2 From 692a602aeabc2f11dfed9bdb7d6ac2acd20bde8d Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Wed, 25 May 2022 08:48:34 -0400 Subject: [PATCH 829/930] Bump ZHA quirks to 0.0.74 (#72482) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 8ea9e9d8d4a..8d6e6162d76 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.30.0", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.73", + "zha-quirks==0.0.74", "zigpy-deconz==0.16.0", "zigpy==0.45.1", "zigpy-xbee==0.14.0", diff --git a/requirements_all.txt b/requirements_all.txt index 9093246c7ff..9e6729d6af1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2501,7 +2501,7 @@ zengge==0.2 zeroconf==0.38.6 # homeassistant.components.zha -zha-quirks==0.0.73 +zha-quirks==0.0.74 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 096db3431a1..1d26030bedb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1647,7 +1647,7 @@ youless-api==0.16 zeroconf==0.38.6 # homeassistant.components.zha -zha-quirks==0.0.73 +zha-quirks==0.0.74 # homeassistant.components.zha zigpy-deconz==0.16.0 From 101b1489c8ca2da53412ffe9c2fa5314bf68ee1f Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 25 May 2022 18:09:53 +0200 Subject: [PATCH 830/930] Fix meater remaining time sensor (#72490) --- homeassistant/components/meater/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/meater/sensor.py b/homeassistant/components/meater/sensor.py index f6d06dc2b25..84ef3a2e2a9 100644 --- a/homeassistant/components/meater/sensor.py +++ b/homeassistant/components/meater/sensor.py @@ -52,7 +52,7 @@ def _remaining_time_to_timestamp(probe: MeaterProbe) -> datetime | None: """Convert remaining time to timestamp.""" if not probe.cook or probe.cook.time_remaining < 0: return None - return dt_util.utcnow() + timedelta(probe.cook.time_remaining) + return dt_util.utcnow() + timedelta(seconds=probe.cook.time_remaining) SENSOR_TYPES = ( From f9f87c607e7135c05ac68f4233c25e796b458c66 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 25 May 2022 18:35:54 +0200 Subject: [PATCH 831/930] Clean zwave_js api typing (#72484) * Clean zwave_js api typing * Add temporary type ignore --- homeassistant/components/zwave_js/api.py | 38 +++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index a4fca557242..1ab8e831b74 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable import dataclasses from functools import partial, wraps -from typing import Any +from typing import Any, Literal from aiohttp import web, web_exceptions, web_request import voluptuous as vol @@ -113,6 +113,22 @@ DRY_RUN = "dry_run" # constants for inclusion INCLUSION_STRATEGY = "inclusion_strategy" + +# Remove type ignore when bumping library to 0.37.0 +INCLUSION_STRATEGY_NOT_SMART_START: dict[ # type: ignore[misc] + int, + Literal[ + InclusionStrategy.DEFAULT, + InclusionStrategy.SECURITY_S0, + InclusionStrategy.SECURITY_S2, + InclusionStrategy.INSECURE, + ], +] = { + InclusionStrategy.DEFAULT.value: InclusionStrategy.DEFAULT, + InclusionStrategy.SECURITY_S0.value: InclusionStrategy.SECURITY_S0, + InclusionStrategy.SECURITY_S2.value: InclusionStrategy.SECURITY_S2, + InclusionStrategy.INSECURE.value: InclusionStrategy.INSECURE, +} PIN = "pin" FORCE_SECURITY = "force_security" PLANNED_PROVISIONING_ENTRY = "planned_provisioning_entry" @@ -143,20 +159,19 @@ MINIMUM_QR_STRING_LENGTH = 52 def convert_planned_provisioning_entry(info: dict) -> ProvisioningEntry: """Handle provisioning entry dict to ProvisioningEntry.""" - info = ProvisioningEntry( + return ProvisioningEntry( dsk=info[DSK], security_classes=[SecurityClass(sec_cls) for sec_cls in info[SECURITY_CLASSES]], additional_properties={ k: v for k, v in info.items() if k not in (DSK, SECURITY_CLASSES) }, ) - return info def convert_qr_provisioning_information(info: dict) -> QRProvisioningInformation: """Convert QR provisioning information dict to QRProvisioningInformation.""" protocols = [Protocols(proto) for proto in info.get(SUPPORTED_PROTOCOLS, [])] - info = QRProvisioningInformation( + return QRProvisioningInformation( version=QRCodeVersion(info[VERSION]), security_classes=[SecurityClass(sec_cls) for sec_cls in info[SECURITY_CLASSES]], dsk=info[DSK], @@ -172,7 +187,6 @@ def convert_qr_provisioning_information(info: dict) -> QRProvisioningInformation supported_protocols=protocols if protocols else None, additional_properties=info.get(ADDITIONAL_PROPERTIES, {}), ) - return info # Helper schemas @@ -655,7 +669,7 @@ async def websocket_add_node( ) connection.subscriptions[msg["id"]] = async_cleanup - msg[DATA_UNSUBSCRIBE] = unsubs = [ + unsubs: list[Callable[[], None]] = [ controller.on("inclusion started", forward_event), controller.on("inclusion failed", forward_event), controller.on("inclusion stopped", forward_event), @@ -666,10 +680,13 @@ async def websocket_add_node( hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device_registered ), ] + msg[DATA_UNSUBSCRIBE] = unsubs try: result = await controller.async_begin_inclusion( - inclusion_strategy, force_security=force_security, provisioning=provisioning + INCLUSION_STRATEGY_NOT_SMART_START[inclusion_strategy.value], + force_security=force_security, + provisioning=provisioning, ) except ValueError as err: connection.send_error( @@ -1165,7 +1182,7 @@ async def websocket_replace_failed_node( ) connection.subscriptions[msg["id"]] = async_cleanup - msg[DATA_UNSUBSCRIBE] = unsubs = [ + unsubs: list[Callable[[], None]] = [ controller.on("inclusion started", forward_event), controller.on("inclusion failed", forward_event), controller.on("inclusion stopped", forward_event), @@ -1177,11 +1194,12 @@ async def websocket_replace_failed_node( hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device_registered ), ] + msg[DATA_UNSUBSCRIBE] = unsubs try: result = await controller.async_replace_failed_node( node_id, - inclusion_strategy, + INCLUSION_STRATEGY_NOT_SMART_START[inclusion_strategy.value], force_security=force_security, provisioning=provisioning, ) @@ -1540,7 +1558,7 @@ async def websocket_get_config_parameters( ) -> None: """Get a list of configuration parameters for a Z-Wave node.""" values = node.get_configuration_values() - result = {} + result: dict[str, Any] = {} for value_id, zwave_value in values.items(): metadata = zwave_value.metadata result[value_id] = { From 10f0509ca3a2b1fd186bf7a801883b427ed40fc4 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 25 May 2022 18:39:42 +0200 Subject: [PATCH 832/930] Clean zwave_js services typing (#72485) Fix services --- homeassistant/components/zwave_js/services.py | 80 +++++++++++-------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/zwave_js/services.py b/homeassistant/components/zwave_js/services.py index 3b56e0a073c..d60532fcf75 100644 --- a/homeassistant/components/zwave_js/services.py +++ b/homeassistant/components/zwave_js/services.py @@ -2,7 +2,7 @@ from __future__ import annotations import asyncio -from collections.abc import Generator +from collections.abc import Generator, Sequence import logging from typing import Any @@ -12,7 +12,7 @@ from zwave_js_server.const import CommandClass, CommandStatus from zwave_js_server.exceptions import SetValueFailed from zwave_js_server.model.endpoint import Endpoint from zwave_js_server.model.node import Node as ZwaveNode -from zwave_js_server.model.value import get_value_id +from zwave_js_server.model.value import ValueDataType, get_value_id from zwave_js_server.util.multicast import async_multicast_set_value from zwave_js_server.util.node import ( async_bulk_set_partial_config_parameters, @@ -72,7 +72,7 @@ def broadcast_command(val: dict[str, Any]) -> dict[str, Any]: def get_valid_responses_from_results( - zwave_objects: set[ZwaveNode | Endpoint], results: tuple[Any, ...] + zwave_objects: Sequence[ZwaveNode | Endpoint], results: Sequence[Any] ) -> Generator[tuple[ZwaveNode | Endpoint, Any], None, None]: """Return valid responses from a list of results.""" for zwave_object, result in zip(zwave_objects, results): @@ -81,8 +81,8 @@ def get_valid_responses_from_results( def raise_exceptions_from_results( - zwave_objects: set[ZwaveNode | Endpoint] | tuple[ZwaveNode | str, ...], - results: tuple[Any, ...], + zwave_objects: Sequence[ZwaveNode | Endpoint], + results: Sequence[Any], ) -> None: """Raise list of exceptions from a list of results.""" if errors := [ @@ -153,12 +153,20 @@ class ZWaveServices: first_node = next((node for node in nodes), None) + if first_node and not all(node.client.driver is not None for node in nodes): + raise vol.Invalid(f"Driver not ready for all nodes: {nodes}") + # If any nodes don't have matching home IDs, we can't run the command because # we can't multicast across multiple networks - if first_node and any( - node.client.driver.controller.home_id - != first_node.client.driver.controller.home_id - for node in nodes + if ( + first_node + and first_node.client.driver # We checked the driver was ready above. + and any( + node.client.driver.controller.home_id + != first_node.client.driver.controller.home_id + for node in nodes + if node.client.driver is not None + ) ): raise vol.Invalid( "Multicast commands only work on devices in the same network" @@ -417,7 +425,8 @@ class ZWaveServices: ), return_exceptions=True, ) - for node, result in get_valid_responses_from_results(nodes, results): + nodes_list = list(nodes) + for node, result in get_valid_responses_from_results(nodes_list, results): zwave_value = result[0] cmd_status = result[1] if cmd_status == CommandStatus.ACCEPTED: @@ -428,7 +437,7 @@ class ZWaveServices: "%s with value %s. Parameter will be set when the device wakes up" ) _LOGGER.info(msg, zwave_value, node, new_value) - raise_exceptions_from_results(nodes, results) + raise_exceptions_from_results(nodes_list, results) async def async_bulk_set_partial_config_parameters( self, service: ServiceCall @@ -450,7 +459,8 @@ class ZWaveServices: return_exceptions=True, ) - for node, cmd_status in get_valid_responses_from_results(nodes, results): + nodes_list = list(nodes) + for node, cmd_status in get_valid_responses_from_results(nodes_list, results): if cmd_status == CommandStatus.ACCEPTED: msg = "Bulk set partials for configuration parameter %s on Node %s" else: @@ -461,7 +471,7 @@ class ZWaveServices: _LOGGER.info(msg, property_, node) - raise_exceptions_from_results(nodes, results) + raise_exceptions_from_results(nodes_list, results) async def async_poll_value(self, service: ServiceCall) -> None: """Poll value on a node.""" @@ -477,10 +487,10 @@ class ZWaveServices: async def async_set_value(self, service: ServiceCall) -> None: """Set a value on a node.""" nodes: set[ZwaveNode] = service.data[const.ATTR_NODES] - command_class = service.data[const.ATTR_COMMAND_CLASS] - property_ = service.data[const.ATTR_PROPERTY] - property_key = service.data.get(const.ATTR_PROPERTY_KEY) - endpoint = service.data.get(const.ATTR_ENDPOINT) + command_class: CommandClass = service.data[const.ATTR_COMMAND_CLASS] + property_: int | str = service.data[const.ATTR_PROPERTY] + property_key: int | str | None = service.data.get(const.ATTR_PROPERTY_KEY) + endpoint: int | None = service.data.get(const.ATTR_ENDPOINT) new_value = service.data[const.ATTR_VALUE] wait_for_result = service.data.get(const.ATTR_WAIT_FOR_RESULT) options = service.data.get(const.ATTR_OPTIONS) @@ -515,17 +525,18 @@ class ZWaveServices: ) results = await asyncio.gather(*coros, return_exceptions=True) + nodes_list = list(nodes) # multiple set_values my fail so we will track the entire list - set_value_failed_nodes_list = [] - for node, success in get_valid_responses_from_results(nodes, results): + set_value_failed_nodes_list: list[ZwaveNode | Endpoint] = [] + for node_, success in get_valid_responses_from_results(nodes_list, results): if success is False: # If we failed to set a value, add node to SetValueFailed exception list - set_value_failed_nodes_list.append(node) + set_value_failed_nodes_list.append(node_) # Add the SetValueFailed exception to the results and the nodes to the node # list. No-op if there are no SetValueFailed exceptions raise_exceptions_from_results( - (*nodes, *set_value_failed_nodes_list), + (*nodes_list, *set_value_failed_nodes_list), (*results, *([SET_VALUE_FAILED_EXC] * len(set_value_failed_nodes_list))), ) @@ -543,17 +554,17 @@ class ZWaveServices: await self.async_set_value(service) return - command_class = service.data[const.ATTR_COMMAND_CLASS] - property_ = service.data[const.ATTR_PROPERTY] - property_key = service.data.get(const.ATTR_PROPERTY_KEY) - endpoint = service.data.get(const.ATTR_ENDPOINT) + command_class: CommandClass = service.data[const.ATTR_COMMAND_CLASS] + property_: int | str = service.data[const.ATTR_PROPERTY] + property_key: int | str | None = service.data.get(const.ATTR_PROPERTY_KEY) + endpoint: int | None = service.data.get(const.ATTR_ENDPOINT) + + value = ValueDataType(commandClass=command_class, property=property_) + if property_key is not None: + value["propertyKey"] = property_key + if endpoint is not None: + value["endpoint"] = endpoint - value = { - "commandClass": command_class, - "property": property_, - "propertyKey": property_key, - "endpoint": endpoint, - } new_value = service.data[const.ATTR_VALUE] # If there are no nodes, we can assume there is only one config entry due to @@ -590,7 +601,7 @@ class ZWaveServices: success = await async_multicast_set_value( client=client, new_value=new_value, - value_data={k: v for k, v in value.items() if v is not None}, + value_data=value, nodes=None if broadcast else list(nodes), options=options, ) @@ -627,8 +638,9 @@ class ZWaveServices: ), return_exceptions=True, ) + endpoints_list = list(endpoints) for endpoint, result in get_valid_responses_from_results( - endpoints, results + endpoints_list, results ): _LOGGER.info( ( @@ -640,7 +652,7 @@ class ZWaveServices: endpoint, result, ) - raise_exceptions_from_results(endpoints, results) + raise_exceptions_from_results(endpoints_list, results) # If an endpoint is provided, we assume the user wants to call the CC API on # that endpoint for all target nodes From 4723119fad514fc51b756c55241ee77b10940607 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Wed, 25 May 2022 18:40:38 +0200 Subject: [PATCH 833/930] Clean zwave_js remaining typing issues (#72488) --- .../zwave_js/discovery_data_template.py | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 512cfafa63a..5c853d9ea9f 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -1,10 +1,10 @@ """Data template classes for discovery used to generate additional data for setup.""" from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from dataclasses import dataclass, field import logging -from typing import Any +from typing import Any, Union, cast from zwave_js_server.const import CommandClass from zwave_js_server.const.command_class.meter import ( @@ -242,7 +242,7 @@ class BaseDiscoverySchemaDataTemplate: """ return {} - def values_to_watch(self, resolved_data: Any) -> Iterable[ZwaveValue]: + def values_to_watch(self, resolved_data: Any) -> Iterable[ZwaveValue | None]: """ Return list of all ZwaveValues resolved by helper that should be watched. @@ -261,7 +261,7 @@ class BaseDiscoverySchemaDataTemplate: @staticmethod def _get_value_from_id( node: ZwaveNode, value_id_obj: ZwaveValueID - ) -> ZwaveValue | None: + ) -> ZwaveValue | ZwaveConfigurationValue | None: """Get a ZwaveValue from a node using a ZwaveValueDict.""" value_id = get_value_id( node, @@ -295,7 +295,9 @@ class DynamicCurrentTempClimateDataTemplate(BaseDiscoverySchemaDataTemplate): return data - def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]: + def values_to_watch( + self, resolved_data: dict[str, Any] + ) -> Iterable[ZwaveValue | None]: """Return list of all ZwaveValues resolved by helper that should be watched.""" return [ *resolved_data["lookup_table"].values(), @@ -331,8 +333,11 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): @staticmethod def find_key_from_matching_set( enum_value: MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType, - set_map: dict[ - str, set[MultilevelSensorType | MultilevelSensorScaleType | MeterScaleType] + set_map: Mapping[ + str, + set[MultilevelSensorType] + | set[MultilevelSensorScaleType] + | set[MeterScaleType], ], ) -> str | None: """Find a key in a set map that matches a given enum value.""" @@ -354,11 +359,11 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): return NumericSensorDataTemplateData(ENTITY_DESC_KEY_BATTERY, PERCENTAGE) if value.command_class == CommandClass.METER: - scale_type = get_meter_scale_type(value) - unit = self.find_key_from_matching_set(scale_type, METER_UNIT_MAP) + meter_scale_type = get_meter_scale_type(value) + unit = self.find_key_from_matching_set(meter_scale_type, METER_UNIT_MAP) # We do this because even though these are energy scales, they don't meet # the unit requirements for the energy device class. - if scale_type in ( + if meter_scale_type in ( ElectricScale.PULSE_COUNT, ElectricScale.KILOVOLT_AMPERE_HOUR, ElectricScale.KILOVOLT_AMPERE_REACTIVE_HOUR, @@ -368,19 +373,21 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate): ) # We do this because even though these are power scales, they don't meet # the unit requirements for the power device class. - if scale_type == ElectricScale.KILOVOLT_AMPERE_REACTIVE: + if meter_scale_type == ElectricScale.KILOVOLT_AMPERE_REACTIVE: return NumericSensorDataTemplateData(ENTITY_DESC_KEY_MEASUREMENT, unit) return NumericSensorDataTemplateData( - self.find_key_from_matching_set(scale_type, METER_DEVICE_CLASS_MAP), + self.find_key_from_matching_set( + meter_scale_type, METER_DEVICE_CLASS_MAP + ), unit, ) if value.command_class == CommandClass.SENSOR_MULTILEVEL: sensor_type = get_multilevel_sensor_type(value) - scale_type = get_multilevel_sensor_scale_type(value) + multilevel_sensor_scale_type = get_multilevel_sensor_scale_type(value) unit = self.find_key_from_matching_set( - scale_type, MULTILEVEL_SENSOR_UNIT_MAP + multilevel_sensor_scale_type, MULTILEVEL_SENSOR_UNIT_MAP ) if sensor_type == MultilevelSensorType.TARGET_TEMPERATURE: return NumericSensorDataTemplateData( @@ -410,7 +417,9 @@ class CoverTiltDataTemplate(BaseDiscoverySchemaDataTemplate, TiltValueMix): """Resolve helper class data for a discovered value.""" return {"tilt_value": self._get_value_from_id(value.node, self.tilt_value_id)} - def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]: + def values_to_watch( + self, resolved_data: dict[str, Any] + ) -> Iterable[ZwaveValue | None]: """Return list of all ZwaveValues resolved by helper that should be watched.""" return [resolved_data["tilt_value"]] @@ -492,24 +501,29 @@ class ConfigurableFanValueMappingDataTemplate( `configuration_option` to the value mapping object. """ - def resolve_data(self, value: ZwaveValue) -> dict[str, ZwaveConfigurationValue]: + def resolve_data( + self, value: ZwaveValue + ) -> dict[str, ZwaveConfigurationValue | None]: """Resolve helper class data for a discovered value.""" - zwave_value: ZwaveValue = self._get_value_from_id( - value.node, self.configuration_option + zwave_value = cast( # type: ignore[redundant-cast] + Union[ZwaveConfigurationValue, None], + self._get_value_from_id(value.node, self.configuration_option), ) return {"configuration_value": zwave_value} - def values_to_watch(self, resolved_data: dict[str, Any]) -> Iterable[ZwaveValue]: + def values_to_watch( + self, resolved_data: dict[str, ZwaveConfigurationValue | None] + ) -> Iterable[ZwaveConfigurationValue | None]: """Return list of all ZwaveValues that should be watched.""" return [ resolved_data["configuration_value"], ] def get_fan_value_mapping( - self, resolved_data: dict[str, ZwaveConfigurationValue] + self, resolved_data: dict[str, ZwaveConfigurationValue | None] ) -> FanValueMapping | None: """Get current fan properties from resolved data.""" - zwave_value: ZwaveValue = resolved_data["configuration_value"] + zwave_value = resolved_data["configuration_value"] if zwave_value is None: _LOGGER.warning("Unable to read device configuration value") From 9b40de18cd603fb6dbcc55a778b4fa1a461ef1a9 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 25 May 2022 12:49:04 -0400 Subject: [PATCH 834/930] Allow zwave_js/network_status WS API to accept device or entry ID (#72205) * Allow zwave_js/network_status WS API to accept device or entry ID * Fix based on upstream feedback * Fixt ests * Fixes --- homeassistant/components/zwave_js/api.py | 120 +++++++++++++++-------- tests/components/zwave_js/test_api.py | 94 +++++++++++++++++- 2 files changed, 167 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 1ab8e831b74..05591e8ecfb 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -236,6 +236,36 @@ QR_PROVISIONING_INFORMATION_SCHEMA = vol.All( QR_CODE_STRING_SCHEMA = vol.All(str, vol.Length(min=MINIMUM_QR_STRING_LENGTH)) +async def _async_get_entry( + hass: HomeAssistant, connection: ActiveConnection, msg: dict, entry_id: str +) -> tuple[ConfigEntry | None, Client | None, Driver | None]: + """Get config entry and client from message data.""" + entry = hass.config_entries.async_get_entry(entry_id) + if entry is None: + connection.send_error( + msg[ID], ERR_NOT_FOUND, f"Config entry {entry_id} not found" + ) + return None, None, None + + if entry.state is not ConfigEntryState.LOADED: + connection.send_error( + msg[ID], ERR_NOT_LOADED, f"Config entry {entry_id} not loaded" + ) + return None, None, None + + client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT] + + if client.driver is None: + connection.send_error( + msg[ID], + ERR_NOT_LOADED, + f"Config entry {msg[ENTRY_ID]} not loaded, driver not ready", + ) + return None, None, None + + return entry, client, client.driver + + def async_get_entry(orig_func: Callable) -> Callable: """Decorate async function to get entry.""" @@ -244,35 +274,33 @@ def async_get_entry(orig_func: Callable) -> Callable: hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Provide user specific data and store to function.""" - entry_id = msg[ENTRY_ID] - entry = hass.config_entries.async_get_entry(entry_id) - if entry is None: - connection.send_error( - msg[ID], ERR_NOT_FOUND, f"Config entry {entry_id} not found" - ) + entry, client, driver = await _async_get_entry( + hass, connection, msg, msg[ENTRY_ID] + ) + + if not entry and not client and not driver: return - if entry.state is not ConfigEntryState.LOADED: - connection.send_error( - msg[ID], ERR_NOT_LOADED, f"Config entry {entry_id} not loaded" - ) - return - - client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT] - - if client.driver is None: - connection.send_error( - msg[ID], - ERR_NOT_LOADED, - f"Config entry {entry_id} not loaded, driver not ready", - ) - return - - await orig_func(hass, connection, msg, entry, client, client.driver) + await orig_func(hass, connection, msg, entry, client, driver) return async_get_entry_func +async def _async_get_node( + hass: HomeAssistant, connection: ActiveConnection, msg: dict, device_id: str +) -> Node | None: + """Get node from message data.""" + try: + node = async_get_node_from_device_id(hass, device_id) + except ValueError as err: + error_code = ERR_NOT_FOUND + if "loaded" in err.args[0]: + error_code = ERR_NOT_LOADED + connection.send_error(msg[ID], error_code, err.args[0]) + return None + return node + + def async_get_node(orig_func: Callable) -> Callable: """Decorate async function to get node.""" @@ -281,15 +309,8 @@ def async_get_node(orig_func: Callable) -> Callable: hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Provide user specific data and store to function.""" - device_id = msg[DEVICE_ID] - - try: - node = async_get_node_from_device_id(hass, device_id) - except ValueError as err: - error_code = ERR_NOT_FOUND - if "loaded" in err.args[0]: - error_code = ERR_NOT_LOADED - connection.send_error(msg[ID], error_code, err.args[0]) + node = await _async_get_node(hass, connection, msg, msg[DEVICE_ID]) + if not node: return await orig_func(hass, connection, msg, node) @@ -388,24 +409,37 @@ def async_register_api(hass: HomeAssistant) -> None: @websocket_api.require_admin @websocket_api.websocket_command( - {vol.Required(TYPE): "zwave_js/network_status", vol.Required(ENTRY_ID): str} + { + vol.Required(TYPE): "zwave_js/network_status", + vol.Exclusive(DEVICE_ID, "id"): str, + vol.Exclusive(ENTRY_ID, "id"): str, + } ) @websocket_api.async_response -@async_get_entry async def websocket_network_status( - hass: HomeAssistant, - connection: ActiveConnection, - msg: dict, - entry: ConfigEntry, - client: Client, - driver: Driver, + hass: HomeAssistant, connection: ActiveConnection, msg: dict ) -> None: """Get the status of the Z-Wave JS network.""" + if ENTRY_ID in msg: + _, client, driver = await _async_get_entry(hass, connection, msg, msg[ENTRY_ID]) + if not client or not driver: + return + elif DEVICE_ID in msg: + node = await _async_get_node(hass, connection, msg, msg[DEVICE_ID]) + if not node: + return + client = node.client + assert client.driver + driver = client.driver + else: + connection.send_error( + msg[ID], ERR_INVALID_FORMAT, "Must specify either device_id or entry_id" + ) + return controller = driver.controller + await controller.async_get_state() client_version_info = client.version assert client_version_info # When client is connected version info is set. - - await controller.async_get_state() data = { "client": { "ws_server_url": client.ws_server_url, @@ -1723,6 +1757,7 @@ async def websocket_get_log_config( driver: Driver, ) -> None: """Get log configuration for the Z-Wave JS driver.""" + assert client and client.driver connection.send_result( msg[ID], dataclasses.asdict(driver.log_config), @@ -1781,6 +1816,7 @@ async def websocket_data_collection_status( driver: Driver, ) -> None: """Return data collection preference and status.""" + assert client and client.driver result = { OPTED_IN: entry.data.get(CONF_DATA_COLLECTION_OPTED_IN), ENABLED: await driver.async_is_statistics_enabled(), diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 2309e7b02bf..ee6a85db70f 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -28,7 +28,10 @@ from zwave_js_server.model.controller import ( ) from zwave_js_server.model.node import Node -from homeassistant.components.websocket_api.const import ERR_NOT_FOUND +from homeassistant.components.websocket_api.const import ( + ERR_INVALID_FORMAT, + ERR_NOT_FOUND, +) from homeassistant.components.zwave_js.api import ( ADDITIONAL_PROPERTIES, APPLICATION_VERSION, @@ -82,14 +85,19 @@ def get_device(hass, node): return dev_reg.async_get_device({device_id}) -async def test_network_status(hass, integration, hass_ws_client): +async def test_network_status(hass, multisensor_6, integration, hass_ws_client): """Test the network status websocket command.""" entry = integration ws_client = await hass_ws_client(hass) + # Try API call with entry ID with patch("zwave_js_server.model.controller.Controller.async_get_state"): await ws_client.send_json( - {ID: 2, TYPE: "zwave_js/network_status", ENTRY_ID: entry.entry_id} + { + ID: 1, + TYPE: "zwave_js/network_status", + ENTRY_ID: entry.entry_id, + } ) msg = await ws_client.receive_json() result = msg["result"] @@ -98,18 +106,94 @@ async def test_network_status(hass, integration, hass_ws_client): assert result["client"]["server_version"] == "1.0.0" assert result["controller"]["inclusion_state"] == InclusionState.IDLE - # Test sending command with not loaded entry fails + # Try API call with device ID + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_device( + identifiers={(DOMAIN, "3245146787-52")}, + ) + assert device + with patch("zwave_js_server.model.controller.Controller.async_get_state"): + await ws_client.send_json( + { + ID: 2, + TYPE: "zwave_js/network_status", + DEVICE_ID: device.id, + } + ) + msg = await ws_client.receive_json() + result = msg["result"] + + assert result["client"]["ws_server_url"] == "ws://test:3000/zjs" + assert result["client"]["server_version"] == "1.0.0" + assert result["controller"]["inclusion_state"] == InclusionState.IDLE + + # Test sending command with invalid config entry ID fails + await ws_client.send_json( + { + ID: 3, + TYPE: "zwave_js/network_status", + ENTRY_ID: "fake_id", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_FOUND + + # Test sending command with invalid device ID fails + await ws_client.send_json( + { + ID: 4, + TYPE: "zwave_js/network_status", + DEVICE_ID: "fake_id", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_FOUND + + # Test sending command with not loaded entry fails with config entry ID await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() await ws_client.send_json( - {ID: 3, TYPE: "zwave_js/network_status", ENTRY_ID: entry.entry_id} + { + ID: 5, + TYPE: "zwave_js/network_status", + ENTRY_ID: entry.entry_id, + } ) msg = await ws_client.receive_json() assert not msg["success"] assert msg["error"]["code"] == ERR_NOT_LOADED + # Test sending command with not loaded entry fails with device ID + await ws_client.send_json( + { + ID: 6, + TYPE: "zwave_js/network_status", + DEVICE_ID: device.id, + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_LOADED + + # Test sending command with no device ID or entry ID fails + await ws_client.send_json( + { + ID: 7, + TYPE: "zwave_js/network_status", + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_INVALID_FORMAT + async def test_node_ready( hass, From c8c4bf6c376b23906d40811934c80b180806b95e Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 25 May 2022 13:20:40 -0400 Subject: [PATCH 835/930] Bypass dynamic validation for zwave_js custom triggers (#72471) --- .../components/zwave_js/triggers/event.py | 11 +- .../components/zwave_js/triggers/helpers.py | 35 +++++ .../zwave_js/triggers/value_updated.py | 6 +- tests/components/zwave_js/test_trigger.py | 127 ++++++++++++++---- 4 files changed, 144 insertions(+), 35 deletions(-) create mode 100644 homeassistant/components/zwave_js/triggers/helpers.py diff --git a/homeassistant/components/zwave_js/triggers/event.py b/homeassistant/components/zwave_js/triggers/event.py index 94afd1e9117..17bb52fb392 100644 --- a/homeassistant/components/zwave_js/triggers/event.py +++ b/homeassistant/components/zwave_js/triggers/event.py @@ -30,12 +30,13 @@ from homeassistant.components.zwave_js.helpers import ( get_device_id, get_home_and_node_id_from_device_entry, ) -from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_PLATFORM from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.typing import ConfigType +from .helpers import async_bypass_dynamic_config_validation + # Platform type should be . PLATFORM_TYPE = f"{DOMAIN}.{__name__.rsplit('.', maxsplit=1)[-1]}" @@ -115,6 +116,9 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) + if async_bypass_dynamic_config_validation(hass, config): + return config + if config[ATTR_EVENT_SOURCE] == "node": config[ATTR_NODES] = async_get_nodes_from_targets(hass, config) if not config[ATTR_NODES]: @@ -126,12 +130,9 @@ async def async_validate_trigger_config( return config entry_id = config[ATTR_CONFIG_ENTRY_ID] - if (entry := hass.config_entries.async_get_entry(entry_id)) is None: + if hass.config_entries.async_get_entry(entry_id) is None: raise vol.Invalid(f"Config entry '{entry_id}' not found") - if entry.state is not ConfigEntryState.LOADED: - raise vol.Invalid(f"Config entry '{entry_id}' not loaded") - return config diff --git a/homeassistant/components/zwave_js/triggers/helpers.py b/homeassistant/components/zwave_js/triggers/helpers.py new file mode 100644 index 00000000000..2fbc585c887 --- /dev/null +++ b/homeassistant/components/zwave_js/triggers/helpers.py @@ -0,0 +1,35 @@ +"""Helpers for Z-Wave JS custom triggers.""" +from homeassistant.components.zwave_js.const import ATTR_CONFIG_ENTRY_ID, DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.typing import ConfigType + + +@callback +def async_bypass_dynamic_config_validation( + hass: HomeAssistant, config: ConfigType +) -> bool: + """Return whether target zwave_js config entry is not loaded.""" + # If the config entry is not loaded for a zwave_js device, entity, or the + # config entry ID provided, we can't perform dynamic validation + dev_reg = dr.async_get(hass) + ent_reg = er.async_get(hass) + trigger_devices = config.get(ATTR_DEVICE_ID, []) + trigger_entities = config.get(ATTR_ENTITY_ID, []) + return any( + entry.state != ConfigEntryState.LOADED + and ( + entry.entry_id == config.get(ATTR_CONFIG_ENTRY_ID) + or any( + device.id in trigger_devices + for device in dr.async_entries_for_config_entry(dev_reg, entry.entry_id) + ) + or ( + entity.entity_id in trigger_entities + for entity in er.async_entries_for_config_entry(ent_reg, entry.entry_id) + ) + ) + for entry in hass.config_entries.async_entries(DOMAIN) + ) diff --git a/homeassistant/components/zwave_js/triggers/value_updated.py b/homeassistant/components/zwave_js/triggers/value_updated.py index ac3aae1efed..4f15b87a6db 100644 --- a/homeassistant/components/zwave_js/triggers/value_updated.py +++ b/homeassistant/components/zwave_js/triggers/value_updated.py @@ -12,6 +12,7 @@ from homeassistant.components.automation import ( AutomationActionType, AutomationTriggerInfo, ) +from homeassistant.components.zwave_js.config_validation import VALUE_SCHEMA from homeassistant.components.zwave_js.const import ( ATTR_COMMAND_CLASS, ATTR_COMMAND_CLASS_NAME, @@ -37,7 +38,7 @@ from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers.typing import ConfigType -from ..config_validation import VALUE_SCHEMA +from .helpers import async_bypass_dynamic_config_validation # Platform type should be . PLATFORM_TYPE = f"{DOMAIN}.{__name__.rsplit('.', maxsplit=1)[-1]}" @@ -75,6 +76,9 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) + if async_bypass_dynamic_config_validation(hass, config): + return config + config[ATTR_NODES] = async_get_nodes_from_targets(hass, config) if not config[ATTR_NODES]: raise vol.Invalid( diff --git a/tests/components/zwave_js/test_trigger.py b/tests/components/zwave_js/test_trigger.py index 45de09e8b17..9758f566d81 100644 --- a/tests/components/zwave_js/test_trigger.py +++ b/tests/components/zwave_js/test_trigger.py @@ -10,6 +10,9 @@ from zwave_js_server.model.node import Node from homeassistant.components import automation from homeassistant.components.zwave_js import DOMAIN from homeassistant.components.zwave_js.trigger import async_validate_trigger_config +from homeassistant.components.zwave_js.triggers.helpers import ( + async_bypass_dynamic_config_validation, +) from homeassistant.const import SERVICE_RELOAD from homeassistant.helpers.device_registry import ( async_entries_for_config_entry, @@ -671,35 +674,6 @@ async def test_zwave_js_event_invalid_config_entry_id( caplog.clear() -async def test_zwave_js_event_unloaded_config_entry(hass, client, integration, caplog): - """Test zwave_js.event automation trigger fails when config entry is unloaded.""" - trigger_type = f"{DOMAIN}.event" - - await hass.config_entries.async_unload(integration.entry_id) - - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: [ - { - "trigger": { - "platform": trigger_type, - "config_entry_id": integration.entry_id, - "event_source": "controller", - "event": "inclusion started", - }, - "action": { - "event": "node_no_event_data_filter", - }, - } - ] - }, - ) - - assert f"Config entry '{integration.entry_id}' not loaded" in caplog.text - - async def test_async_validate_trigger_config(hass): """Test async_validate_trigger_config.""" mock_platform = AsyncMock() @@ -735,3 +709,98 @@ async def test_invalid_trigger_configs(hass): "property": "latchStatus", }, ) + + +async def test_zwave_js_trigger_config_entry_unloaded( + hass, client, lock_schlage_be469, integration +): + """Test zwave_js triggers bypass dynamic validation when needed.""" + dev_reg = async_get_dev_reg(hass) + device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0] + + # Test bypass check is False + assert not async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.value_updated", + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + ) + + await hass.config_entries.async_unload(integration.entry_id) + + # Test full validation for both events + assert await async_validate_trigger_config( + hass, + { + "platform": f"{DOMAIN}.value_updated", + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + ) + + assert await async_validate_trigger_config( + hass, + { + "platform": f"{DOMAIN}.event", + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "event_source": "node", + "event": "interview stage completed", + }, + ) + + # Test bypass check + assert async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.value_updated", + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + ) + + assert async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.value_updated", + "device_id": device.id, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + "from": "ajar", + }, + ) + + assert async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.event", + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "event_source": "node", + "event": "interview stage completed", + }, + ) + + assert async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.event", + "device_id": device.id, + "event_source": "node", + "event": "interview stage completed", + "event_data": {"stageName": "ProtocolInfo"}, + }, + ) + + assert async_bypass_dynamic_config_validation( + hass, + { + "platform": f"{DOMAIN}.event", + "config_entry_id": integration.entry_id, + "event_source": "controller", + "event": "nvm convert progress", + }, + ) From fe3fa0ae17569898144a5710933b2069f9a6122c Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Wed, 25 May 2022 13:46:55 -0400 Subject: [PATCH 836/930] Bump zwave-js-server-python to 0.37.0 (#72395) --- homeassistant/components/zwave_js/api.py | 3 +- homeassistant/components/zwave_js/climate.py | 2 +- .../components/zwave_js/device_action.py | 4 +- .../components/zwave_js/device_condition.py | 4 +- .../components/zwave_js/device_trigger.py | 4 +- .../zwave_js/discovery_data_template.py | 2 +- .../components/zwave_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../fixtures/aeon_smart_switch_6_state.json | 9 +- .../fixtures/climate_danfoss_lc_13_state.json | 58 ++- ..._ct100_plus_different_endpoints_state.json | 354 +++++++++++++++++- tests/components/zwave_js/test_api.py | 5 + tests/components/zwave_js/test_services.py | 18 +- 14 files changed, 445 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 05591e8ecfb..5d81dc46803 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -114,8 +114,7 @@ DRY_RUN = "dry_run" # constants for inclusion INCLUSION_STRATEGY = "inclusion_strategy" -# Remove type ignore when bumping library to 0.37.0 -INCLUSION_STRATEGY_NOT_SMART_START: dict[ # type: ignore[misc] +INCLUSION_STRATEGY_NOT_SMART_START: dict[ int, Literal[ InclusionStrategy.DEFAULT, diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index cad0b6a1e5c..7b07eb09619 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -238,7 +238,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): if self._current_mode is None or self._current_mode.value is None: # Thermostat(valve) with no support for setting a mode is considered heating-only return [ThermostatSetpointType.HEATING] - return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) # type: ignore[no-any-return] + return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) @property def temperature_unit(self) -> str: diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index 2494cb8216d..728b376228e 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -326,7 +326,9 @@ async def async_get_action_capabilities( vol.Required(ATTR_COMMAND_CLASS): vol.In( { CommandClass(cc.id).value: cc.name - for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return] + for cc in sorted( + node.command_classes, key=lambda cc: cc.name + ) } ), vol.Required(ATTR_PROPERTY): cv.string, diff --git a/homeassistant/components/zwave_js/device_condition.py b/homeassistant/components/zwave_js/device_condition.py index 549319d23f4..7775995437a 100644 --- a/homeassistant/components/zwave_js/device_condition.py +++ b/homeassistant/components/zwave_js/device_condition.py @@ -221,7 +221,9 @@ async def async_get_condition_capabilities( vol.Required(ATTR_COMMAND_CLASS): vol.In( { CommandClass(cc.id).value: cc.name - for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return] + for cc in sorted( + node.command_classes, key=lambda cc: cc.name + ) if cc.id != CommandClass.CONFIGURATION } ), diff --git a/homeassistant/components/zwave_js/device_trigger.py b/homeassistant/components/zwave_js/device_trigger.py index ad860089d1d..dfca2eb4b27 100644 --- a/homeassistant/components/zwave_js/device_trigger.py +++ b/homeassistant/components/zwave_js/device_trigger.py @@ -534,7 +534,9 @@ async def async_get_trigger_capabilities( vol.Required(ATTR_COMMAND_CLASS): vol.In( { CommandClass(cc.id).value: cc.name - for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return] + for cc in sorted( + node.command_classes, key=lambda cc: cc.name + ) if cc.id != CommandClass.CONFIGURATION } ), diff --git a/homeassistant/components/zwave_js/discovery_data_template.py b/homeassistant/components/zwave_js/discovery_data_template.py index 5c853d9ea9f..74847c3f4da 100644 --- a/homeassistant/components/zwave_js/discovery_data_template.py +++ b/homeassistant/components/zwave_js/discovery_data_template.py @@ -505,7 +505,7 @@ class ConfigurableFanValueMappingDataTemplate( self, value: ZwaveValue ) -> dict[str, ZwaveConfigurationValue | None]: """Resolve helper class data for a discovered value.""" - zwave_value = cast( # type: ignore[redundant-cast] + zwave_value = cast( Union[ZwaveConfigurationValue, None], self._get_value_from_id(value.node, self.configuration_option), ) diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index 934c3f9a3f5..555da5fe954 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave JS", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["zwave-js-server-python==0.36.1"], + "requirements": ["zwave-js-server-python==0.37.0"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["usb", "http", "websocket_api"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 9e6729d6af1..27c5a401bd4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2528,7 +2528,7 @@ zigpy==0.45.1 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.36.1 +zwave-js-server-python==0.37.0 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1d26030bedb..58a345adbfc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1665,7 +1665,7 @@ zigpy-znp==0.7.0 zigpy==0.45.1 # homeassistant.components.zwave_js -zwave-js-server-python==0.36.1 +zwave-js-server-python==0.37.0 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 diff --git a/tests/components/zwave_js/fixtures/aeon_smart_switch_6_state.json b/tests/components/zwave_js/fixtures/aeon_smart_switch_6_state.json index cc26ce14e3e..62b6205926b 100644 --- a/tests/components/zwave_js/fixtures/aeon_smart_switch_6_state.json +++ b/tests/components/zwave_js/fixtures/aeon_smart_switch_6_state.json @@ -51,7 +51,14 @@ "index": 0, "installerIcon": 1792, "userIcon": 1792, - "commandClasses": [] + "commandClasses": [ + { + "id": 50, + "name": "Meter", + "version": 3, + "isSecure": false + } + ] } ], "values": [ diff --git a/tests/components/zwave_js/fixtures/climate_danfoss_lc_13_state.json b/tests/components/zwave_js/fixtures/climate_danfoss_lc_13_state.json index a877f82b53f..cb8e78881df 100644 --- a/tests/components/zwave_js/fixtures/climate_danfoss_lc_13_state.json +++ b/tests/components/zwave_js/fixtures/climate_danfoss_lc_13_state.json @@ -126,7 +126,63 @@ "endpoints": [ { "nodeId": 5, - "index": 0 + "index": 0, + "commandClasses": [ + { + "id": 67, + "name": "Thermostat Setpoint", + "version": 2, + "isSecure": false + }, + { + "id": 70, + "name": "Climate Control Schedule", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 1, + "isSecure": false + }, + { + "id": 117, + "name": "Protection", + "version": 2, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 129, + "name": "Clock", + "version": 1, + "isSecure": false + }, + { + "id": 132, + "name": "Wake Up", + "version": 1, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 1, + "isSecure": false + }, + { + "id": 143, + "name": "Multi Command", + "version": 1, + "isSecure": false + } + ] } ], "values": [ diff --git a/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_different_endpoints_state.json b/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_different_endpoints_state.json index 15823a8f6ca..ca0efb56711 100644 --- a/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_different_endpoints_state.json +++ b/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_different_endpoints_state.json @@ -62,7 +62,123 @@ }, "mandatorySupportedCCs": [32, 114, 64, 67, 134], "mandatoryControlledCCs": [] - } + }, + "commandClasses": [ + { + "id": 49, + "name": "Multilevel Sensor", + "version": 5, + "isSecure": false + }, + { + "id": 64, + "name": "Thermostat Mode", + "version": 2, + "isSecure": false + }, + { + "id": 66, + "name": "Thermostat Operating State", + "version": 2, + "isSecure": false + }, + { + "id": 67, + "name": "Thermostat Setpoint", + "version": 2, + "isSecure": false + }, + { + "id": 68, + "name": "Thermostat Fan Mode", + "version": 1, + "isSecure": false + }, + { + "id": 69, + "name": "Thermostat Fan State", + "version": 1, + "isSecure": false + }, + { + "id": 89, + "name": "Association Group Information", + "version": 1, + "isSecure": false + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": false + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 96, + "name": "Multi Channel", + "version": 4, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": false + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 3, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 129, + "name": "Clock", + "version": 1, + "isSecure": false + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 2, + "isSecure": false + }, + { + "id": 135, + "name": "Indicator", + "version": 1, + "isSecure": false + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": false + } + ] }, { "nodeId": 26, @@ -82,7 +198,123 @@ }, "mandatorySupportedCCs": [32, 114, 64, 67, 134], "mandatoryControlledCCs": [] - } + }, + "commandClasses": [ + { + "id": 49, + "name": "Multilevel Sensor", + "version": 5, + "isSecure": false + }, + { + "id": 64, + "name": "Thermostat Mode", + "version": 2, + "isSecure": false + }, + { + "id": 66, + "name": "Thermostat Operating State", + "version": 2, + "isSecure": false + }, + { + "id": 67, + "name": "Thermostat Setpoint", + "version": 2, + "isSecure": false + }, + { + "id": 68, + "name": "Thermostat Fan Mode", + "version": 1, + "isSecure": false + }, + { + "id": 69, + "name": "Thermostat Fan State", + "version": 1, + "isSecure": false + }, + { + "id": 89, + "name": "Association Group Information", + "version": 1, + "isSecure": false + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": false + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 96, + "name": "Multi Channel", + "version": 4, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": false + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 3, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 129, + "name": "Clock", + "version": 1, + "isSecure": false + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 2, + "isSecure": false + }, + { + "id": 135, + "name": "Indicator", + "version": 1, + "isSecure": false + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": false + } + ] }, { "nodeId": 26, @@ -102,7 +334,123 @@ }, "mandatorySupportedCCs": [32, 49], "mandatoryControlledCCs": [] - } + }, + "commandClasses": [ + { + "id": 49, + "name": "Multilevel Sensor", + "version": 5, + "isSecure": false + }, + { + "id": 64, + "name": "Thermostat Mode", + "version": 2, + "isSecure": false + }, + { + "id": 66, + "name": "Thermostat Operating State", + "version": 2, + "isSecure": false + }, + { + "id": 67, + "name": "Thermostat Setpoint", + "version": 2, + "isSecure": false + }, + { + "id": 68, + "name": "Thermostat Fan Mode", + "version": 1, + "isSecure": false + }, + { + "id": 69, + "name": "Thermostat Fan State", + "version": 1, + "isSecure": false + }, + { + "id": 89, + "name": "Association Group Information", + "version": 1, + "isSecure": false + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": false + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 96, + "name": "Multi Channel", + "version": 4, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": false + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 3, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 129, + "name": "Clock", + "version": 1, + "isSecure": false + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 2, + "isSecure": false + }, + { + "id": 135, + "name": "Indicator", + "version": 1, + "isSecure": false + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": false + } + ] } ], "values": [ diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index ee6a85db70f..e59a923ff44 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -1147,6 +1147,8 @@ async def test_get_provisioning_entries(hass, integration, client, hass_ws_clien { "dsk": "test", "security_classes": [SecurityClass.S2_UNAUTHENTICATED], + "requested_security_classes": None, + "status": 0, "additional_properties": {"fake": "test"}, } ] @@ -1235,6 +1237,8 @@ async def test_parse_qr_code_string(hass, integration, client, hass_ws_client): "max_inclusion_request_interval": 1, "uuid": "test", "supported_protocols": [Protocols.ZWAVE], + "status": 0, + "requested_security_classes": None, "additional_properties": {}, } @@ -3535,6 +3539,7 @@ async def test_check_for_config_updates(hass, client, integration, hass_ws_clien client.async_send_command.return_value = { "updateAvailable": True, "newVersion": "test", + "installedVersion": "test", } await ws_client.send_json( { diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index dec4c3c0a9d..b5ef083bf64 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -1817,7 +1817,7 @@ async def test_invoke_cc_api( device_radio_thermostat.id, device_danfoss.id, ], - ATTR_COMMAND_CLASS: 132, + ATTR_COMMAND_CLASS: 67, ATTR_ENDPOINT: 0, ATTR_METHOD_NAME: "someMethod", ATTR_PARAMETERS: [1, 2], @@ -1827,7 +1827,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command.call_args_list) == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] @@ -1839,7 +1839,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command_no_wait.call_args_list) == 1 args = client.async_send_command_no_wait.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] @@ -1870,7 +1870,7 @@ async def test_invoke_cc_api( "select.living_connect_z_thermostat_local_protection_state", "sensor.living_connect_z_thermostat_node_status", ], - ATTR_COMMAND_CLASS: 132, + ATTR_COMMAND_CLASS: 67, ATTR_METHOD_NAME: "someMethod", ATTR_PARAMETERS: [1, 2], }, @@ -1879,7 +1879,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command.call_args_list) == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] @@ -1891,7 +1891,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command_no_wait.call_args_list) == 1 args = client.async_send_command_no_wait.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] @@ -1916,7 +1916,7 @@ async def test_invoke_cc_api( device_danfoss.id, device_radio_thermostat.id, ], - ATTR_COMMAND_CLASS: 132, + ATTR_COMMAND_CLASS: 67, ATTR_ENDPOINT: 0, ATTR_METHOD_NAME: "someMethod", ATTR_PARAMETERS: [1, 2], @@ -1926,7 +1926,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command.call_args_list) == 1 args = client.async_send_command.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] @@ -1938,7 +1938,7 @@ async def test_invoke_cc_api( assert len(client.async_send_command_no_wait.call_args_list) == 1 args = client.async_send_command_no_wait.call_args[0][0] assert args["command"] == "endpoint.invoke_cc_api" - assert args["commandClass"] == 132 + assert args["commandClass"] == 67 assert args["endpoint"] == 0 assert args["methodName"] == "someMethod" assert args["args"] == [1, 2] From f166fc009a8111be8e3b172a845009462b5c0d47 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 25 May 2022 20:29:42 +0200 Subject: [PATCH 837/930] Fix typo in ISY994 re-authentication dialog (#72497) --- homeassistant/components/isy994/strings.json | 2 +- homeassistant/components/isy994/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/isy994/strings.json b/homeassistant/components/isy994/strings.json index 74b54f406a0..821f8889978 100644 --- a/homeassistant/components/isy994/strings.json +++ b/homeassistant/components/isy994/strings.json @@ -13,7 +13,7 @@ "title": "Connect to your ISY" }, "reauth_confirm": { - "description": "The credentials for {host} is no longer valid.", + "description": "The credentials for {host} are no longer valid.", "title": "Reauthenticate your ISY", "data": { "username": "[%key:common::config_flow::data::username%]", diff --git a/homeassistant/components/isy994/translations/en.json b/homeassistant/components/isy994/translations/en.json index b221765cd99..3610b35c194 100644 --- a/homeassistant/components/isy994/translations/en.json +++ b/homeassistant/components/isy994/translations/en.json @@ -17,7 +17,7 @@ "password": "Password", "username": "Username" }, - "description": "The credentials for {host} is no longer valid.", + "description": "The credentials for {host} are no longer valid.", "title": "Reauthenticate your ISY" }, "user": { From 2bc093a04da7b8849357cd57d81cf22278c1a883 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 25 May 2022 20:39:15 +0200 Subject: [PATCH 838/930] Hardware integration MVP (#71677) --- CODEOWNERS | 4 ++ homeassistant/components/hardware/__init__.py | 17 +++++ homeassistant/components/hardware/const.py | 3 + homeassistant/components/hardware/hardware.py | 31 ++++++++ .../components/hardware/manifest.json | 7 ++ homeassistant/components/hardware/models.py | 34 +++++++++ .../components/hardware/websocket_api.py | 47 ++++++++++++ homeassistant/components/hassio/__init__.py | 27 +++++++ .../components/raspberry_pi/__init__.py | 26 +++++++ .../components/raspberry_pi/config_flow.py | 22 ++++++ .../components/raspberry_pi/const.py | 3 + .../components/raspberry_pi/hardware.py | 54 ++++++++++++++ .../components/raspberry_pi/manifest.json | 9 +++ .../components/rpi_power/config_flow.py | 2 + script/hassfest/manifest.py | 4 +- tests/components/hardware/__init__.py | 1 + .../components/hardware/test_websocket_api.py | 18 +++++ tests/components/hassio/test_init.py | 41 ++++++++++- tests/components/raspberry_pi/__init__.py | 1 + .../raspberry_pi/test_config_flow.py | 58 +++++++++++++++ .../components/raspberry_pi/test_hardware.py | 57 +++++++++++++++ tests/components/raspberry_pi/test_init.py | 72 +++++++++++++++++++ 22 files changed, 535 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/hardware/__init__.py create mode 100644 homeassistant/components/hardware/const.py create mode 100644 homeassistant/components/hardware/hardware.py create mode 100644 homeassistant/components/hardware/manifest.json create mode 100644 homeassistant/components/hardware/models.py create mode 100644 homeassistant/components/hardware/websocket_api.py create mode 100644 homeassistant/components/raspberry_pi/__init__.py create mode 100644 homeassistant/components/raspberry_pi/config_flow.py create mode 100644 homeassistant/components/raspberry_pi/const.py create mode 100644 homeassistant/components/raspberry_pi/hardware.py create mode 100644 homeassistant/components/raspberry_pi/manifest.json create mode 100644 tests/components/hardware/__init__.py create mode 100644 tests/components/hardware/test_websocket_api.py create mode 100644 tests/components/raspberry_pi/__init__.py create mode 100644 tests/components/raspberry_pi/test_config_flow.py create mode 100644 tests/components/raspberry_pi/test_hardware.py create mode 100644 tests/components/raspberry_pi/test_init.py diff --git a/CODEOWNERS b/CODEOWNERS index 8a7a058e01f..98d60fbfcb7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -416,6 +416,8 @@ build.json @home-assistant/supervisor /tests/components/guardian/ @bachya /homeassistant/components/habitica/ @ASMfreaK @leikoilja /tests/components/habitica/ @ASMfreaK @leikoilja +/homeassistant/components/hardware/ @home-assistant/core +/tests/components/hardware/ @home-assistant/core /homeassistant/components/harmony/ @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan /tests/components/harmony/ @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan /homeassistant/components/hassio/ @home-assistant/supervisor @@ -829,6 +831,8 @@ build.json @home-assistant/supervisor /tests/components/rainmachine/ @bachya /homeassistant/components/random/ @fabaff /tests/components/random/ @fabaff +/homeassistant/components/raspberry_pi/ @home-assistant/core +/tests/components/raspberry_pi/ @home-assistant/core /homeassistant/components/rdw/ @frenck /tests/components/rdw/ @frenck /homeassistant/components/recollect_waste/ @bachya diff --git a/homeassistant/components/hardware/__init__.py b/homeassistant/components/hardware/__init__.py new file mode 100644 index 00000000000..b3f342d4e32 --- /dev/null +++ b/homeassistant/components/hardware/__init__.py @@ -0,0 +1,17 @@ +"""The Hardware integration.""" +from __future__ import annotations + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.typing import ConfigType + +from . import websocket_api +from .const import DOMAIN + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up Hardware.""" + hass.data[DOMAIN] = {} + + websocket_api.async_setup(hass) + + return True diff --git a/homeassistant/components/hardware/const.py b/homeassistant/components/hardware/const.py new file mode 100644 index 00000000000..7fd64d5d968 --- /dev/null +++ b/homeassistant/components/hardware/const.py @@ -0,0 +1,3 @@ +"""Constants for the Hardware integration.""" + +DOMAIN = "hardware" diff --git a/homeassistant/components/hardware/hardware.py b/homeassistant/components/hardware/hardware.py new file mode 100644 index 00000000000..e07f70022f4 --- /dev/null +++ b/homeassistant/components/hardware/hardware.py @@ -0,0 +1,31 @@ +"""The Hardware integration.""" +from __future__ import annotations + +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.integration_platform import ( + async_process_integration_platforms, +) + +from .const import DOMAIN +from .models import HardwareProtocol + + +async def async_process_hardware_platforms(hass: HomeAssistant): + """Start processing hardware platforms.""" + hass.data[DOMAIN]["hardware_platform"] = {} + + await async_process_integration_platforms(hass, DOMAIN, _register_hardware_platform) + + return True + + +async def _register_hardware_platform( + hass: HomeAssistant, integration_domain: str, platform: HardwareProtocol +): + """Register a hardware platform.""" + if integration_domain == DOMAIN: + return + if not hasattr(platform, "async_info"): + raise HomeAssistantError(f"Invalid hardware platform {platform}") + hass.data[DOMAIN]["hardware_platform"][integration_domain] = platform diff --git a/homeassistant/components/hardware/manifest.json b/homeassistant/components/hardware/manifest.json new file mode 100644 index 00000000000..e7e156b6065 --- /dev/null +++ b/homeassistant/components/hardware/manifest.json @@ -0,0 +1,7 @@ +{ + "domain": "hardware", + "name": "Hardware", + "config_flow": false, + "documentation": "https://www.home-assistant.io/integrations/hardware", + "codeowners": ["@home-assistant/core"] +} diff --git a/homeassistant/components/hardware/models.py b/homeassistant/components/hardware/models.py new file mode 100644 index 00000000000..067c2d955df --- /dev/null +++ b/homeassistant/components/hardware/models.py @@ -0,0 +1,34 @@ +"""Models for Hardware.""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Protocol + +from homeassistant.core import HomeAssistant, callback + + +@dataclass +class BoardInfo: + """Board info type.""" + + hassio_board_id: str | None + manufacturer: str + model: str | None + revision: str | None + + +@dataclass +class HardwareInfo: + """Hardware info type.""" + + name: str | None + board: BoardInfo | None + url: str | None + + +class HardwareProtocol(Protocol): + """Define the format of hardware platforms.""" + + @callback + def async_info(self, hass: HomeAssistant) -> HardwareInfo: + """Return info.""" diff --git a/homeassistant/components/hardware/websocket_api.py b/homeassistant/components/hardware/websocket_api.py new file mode 100644 index 00000000000..388b9597481 --- /dev/null +++ b/homeassistant/components/hardware/websocket_api.py @@ -0,0 +1,47 @@ +"""The Hardware websocket API.""" +from __future__ import annotations + +import contextlib +from dataclasses import asdict + +import voluptuous as vol + +from homeassistant.components import websocket_api +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError + +from .const import DOMAIN +from .hardware import async_process_hardware_platforms +from .models import HardwareProtocol + + +@callback +def async_setup(hass: HomeAssistant) -> None: + """Set up the hardware websocket API.""" + websocket_api.async_register_command(hass, ws_info) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hardware/info", + } +) +@websocket_api.async_response +async def ws_info( + hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict +) -> None: + """Return hardware info.""" + hardware_info = [] + + if "hardware_platform" not in hass.data[DOMAIN]: + await async_process_hardware_platforms(hass) + + hardware_platform: dict[str, HardwareProtocol] = hass.data[DOMAIN][ + "hardware_platform" + ] + for platform in hardware_platform.values(): + if hasattr(platform, "async_info"): + with contextlib.suppress(HomeAssistantError): + hardware_info.append(asdict(platform.async_info(hass))) + + connection.send_result(msg["id"], {"hardware": hardware_info}) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 7e976536691..93d902e4bae 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -205,6 +205,10 @@ MAP_SERVICE_API = { ), } +HARDWARE_INTEGRATIONS = { + "rpi": "raspberry_pi", +} + @bind_hass async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict: @@ -705,6 +709,29 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: # Init add-on ingress panels await async_setup_addon_panel(hass, hassio) + # Setup hardware integration for the detected board type + async def _async_setup_hardware_integration(hass): + """Set up hardaware integration for the detected board type.""" + if (os_info := get_os_info(hass)) is None: + # os info not yet fetched from supervisor, retry later + async_track_point_in_utc_time( + hass, + _async_setup_hardware_integration, + utcnow() + HASSIO_UPDATE_INTERVAL, + ) + return + if (board := os_info.get("board")) is None: + return + if (hw_integration := HARDWARE_INTEGRATIONS.get(board)) is None: + return + hass.async_create_task( + hass.config_entries.flow.async_init( + hw_integration, context={"source": "system"} + ) + ) + + await _async_setup_hardware_integration(hass) + hass.async_create_task( hass.config_entries.flow.async_init(DOMAIN, context={"source": "system"}) ) diff --git a/homeassistant/components/raspberry_pi/__init__.py b/homeassistant/components/raspberry_pi/__init__.py new file mode 100644 index 00000000000..ab1114722c6 --- /dev/null +++ b/homeassistant/components/raspberry_pi/__init__.py @@ -0,0 +1,26 @@ +"""The Raspberry Pi integration.""" +from __future__ import annotations + +from homeassistant.components.hassio import get_os_info +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up a Raspberry Pi config entry.""" + if (os_info := get_os_info(hass)) is None: + # The hassio integration has not yet fetched data from the supervisor + raise ConfigEntryNotReady + + board: str + if (board := os_info.get("board")) is None or not board.startswith("rpi"): + # Not running on a Raspberry Pi, Home Assistant may have been migrated + hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) + return False + + await hass.config_entries.flow.async_init( + "rpi_power", context={"source": "onboarding"} + ) + + return True diff --git a/homeassistant/components/raspberry_pi/config_flow.py b/homeassistant/components/raspberry_pi/config_flow.py new file mode 100644 index 00000000000..db0f8643e5c --- /dev/null +++ b/homeassistant/components/raspberry_pi/config_flow.py @@ -0,0 +1,22 @@ +"""Config flow for the Raspberry Pi integration.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.config_entries import ConfigFlow +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN + + +class RaspberryPiConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for Raspberry Pi.""" + + VERSION = 1 + + async def async_step_system(self, data: dict[str, Any] | None = None) -> FlowResult: + """Handle the initial step.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + + return self.async_create_entry(title="Raspberry Pi", data={}) diff --git a/homeassistant/components/raspberry_pi/const.py b/homeassistant/components/raspberry_pi/const.py new file mode 100644 index 00000000000..48c004a6447 --- /dev/null +++ b/homeassistant/components/raspberry_pi/const.py @@ -0,0 +1,3 @@ +"""Constants for the Raspberry Pi integration.""" + +DOMAIN = "raspberry_pi" diff --git a/homeassistant/components/raspberry_pi/hardware.py b/homeassistant/components/raspberry_pi/hardware.py new file mode 100644 index 00000000000..343ba69d76b --- /dev/null +++ b/homeassistant/components/raspberry_pi/hardware.py @@ -0,0 +1,54 @@ +"""The Raspberry Pi hardware platform.""" +from __future__ import annotations + +from homeassistant.components.hardware.models import BoardInfo, HardwareInfo +from homeassistant.components.hassio import get_os_info +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError + +from .const import DOMAIN + +BOARD_NAMES = { + "rpi": "Raspberry Pi", + "rpi0": "Raspberry Pi Zero", + "rpi0-w": "Raspberry Pi Zero W", + "rpi2": "Raspberry Pi 2", + "rpi3": "Raspberry Pi 3 (32-bit)", + "rpi3-64": "Raspberry Pi 3", + "rpi4": "Raspberry Pi 4 (32-bit)", + "rpi4-64": "Raspberry Pi 4", +} + +MODELS = { + "rpi": "1", + "rpi0": "zero", + "rpi0-w": "zero_w", + "rpi2": "2", + "rpi3": "3", + "rpi3-64": "3", + "rpi4": "4", + "rpi4-64": "4", +} + + +@callback +def async_info(hass: HomeAssistant) -> HardwareInfo: + """Return board info.""" + if (os_info := get_os_info(hass)) is None: + raise HomeAssistantError + board: str + if (board := os_info.get("board")) is None: + raise HomeAssistantError + if not board.startswith("rpi"): + raise HomeAssistantError + + return HardwareInfo( + board=BoardInfo( + hassio_board_id=board, + manufacturer=DOMAIN, + model=MODELS.get(board), + revision=None, + ), + name=BOARD_NAMES.get(board, f"Unknown Raspberry Pi model '{board}'"), + url=None, + ) diff --git a/homeassistant/components/raspberry_pi/manifest.json b/homeassistant/components/raspberry_pi/manifest.json new file mode 100644 index 00000000000..5ba4f87e783 --- /dev/null +++ b/homeassistant/components/raspberry_pi/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "raspberry_pi", + "name": "Raspberry Pi", + "config_flow": false, + "documentation": "https://www.home-assistant.io/integrations/raspberry_pi", + "dependencies": ["hardware", "hassio"], + "codeowners": ["@home-assistant/core"], + "integration_type": "hardware" +} diff --git a/homeassistant/components/rpi_power/config_flow.py b/homeassistant/components/rpi_power/config_flow.py index ed8a45822b0..97814c2a866 100644 --- a/homeassistant/components/rpi_power/config_flow.py +++ b/homeassistant/components/rpi_power/config_flow.py @@ -36,6 +36,8 @@ class RPiPowerFlow(DiscoveryFlowHandler[Awaitable[bool]], domain=DOMAIN): self, data: dict[str, Any] | None = None ) -> FlowResult: """Handle a flow initialized by onboarding.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") has_devices = await self._discovery_function(self.hass) if not has_devices: diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index b66b33486cb..c478d16cf0f 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -52,6 +52,7 @@ NO_IOT_CLASS = [ "downloader", "ffmpeg", "frontend", + "hardware", "history", "homeassistant", "image", @@ -76,6 +77,7 @@ NO_IOT_CLASS = [ "profiler", "proxy", "python_script", + "raspberry_pi", "safe_mode", "script", "search", @@ -153,7 +155,7 @@ MANIFEST_SCHEMA = vol.Schema( { vol.Required("domain"): str, vol.Required("name"): str, - vol.Optional("integration_type"): "helper", + vol.Optional("integration_type"): vol.In(["hardware", "helper"]), vol.Optional("config_flow"): bool, vol.Optional("mqtt"): [str], vol.Optional("zeroconf"): [ diff --git a/tests/components/hardware/__init__.py b/tests/components/hardware/__init__.py new file mode 100644 index 00000000000..9b3acf65c59 --- /dev/null +++ b/tests/components/hardware/__init__.py @@ -0,0 +1 @@ +"""Tests for the Hardware integration.""" diff --git a/tests/components/hardware/test_websocket_api.py b/tests/components/hardware/test_websocket_api.py new file mode 100644 index 00000000000..116879aa628 --- /dev/null +++ b/tests/components/hardware/test_websocket_api.py @@ -0,0 +1,18 @@ +"""Test the hardware websocket API.""" +from homeassistant.components.hardware.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + + +async def test_board_info(hass: HomeAssistant, hass_ws_client) -> None: + """Test we can get the board info.""" + assert await async_setup_component(hass, DOMAIN, {}) + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "hardware/info"}) + msg = await client.receive_json() + + assert msg["id"] == 1 + assert msg["success"] + assert msg["result"] == {"hardware": []} diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 6f4b9a39a9f..ff595aaa602 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -20,8 +20,19 @@ from tests.common import MockConfigEntry, async_fire_time_changed MOCK_ENVIRON = {"HASSIO": "127.0.0.1", "HASSIO_TOKEN": "abcdefgh"} +@pytest.fixture() +def os_info(): + """Mock os/info.""" + return { + "json": { + "result": "ok", + "data": {"version_latest": "1.0.0", "version": "1.0.0"}, + } + } + + @pytest.fixture(autouse=True) -def mock_all(aioclient_mock, request): +def mock_all(aioclient_mock, request, os_info): """Mock all setup requests.""" aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"}) aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"}) @@ -64,7 +75,7 @@ def mock_all(aioclient_mock, request): ) aioclient_mock.get( "http://127.0.0.1/os/info", - json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}}, + **os_info, ) aioclient_mock.get( "http://127.0.0.1/supervisor/info", @@ -701,3 +712,29 @@ async def test_coordinator_updates(hass, caplog): ) assert refresh_updates_mock.call_count == 1 assert "Error on Supervisor API: Unknown" in caplog.text + + +@pytest.mark.parametrize( + "os_info", + [ + { + "json": { + "result": "ok", + "data": {"version_latest": "1.0.0", "version": "1.0.0", "board": "rpi"}, + } + } + ], +) +async def test_setup_hardware_integration(hass, aioclient_mock): + """Test setup initiates hardware integration.""" + + with patch.dict(os.environ, MOCK_ENVIRON), patch( + "homeassistant.components.raspberry_pi.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await async_setup_component(hass, "hassio", {"hassio": {}}) + assert result + await hass.async_block_till_done() + + assert aioclient_mock.call_count == 15 + assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/raspberry_pi/__init__.py b/tests/components/raspberry_pi/__init__.py new file mode 100644 index 00000000000..0b10b50c6f0 --- /dev/null +++ b/tests/components/raspberry_pi/__init__.py @@ -0,0 +1 @@ +"""Tests for the Raspberry Pi integration.""" diff --git a/tests/components/raspberry_pi/test_config_flow.py b/tests/components/raspberry_pi/test_config_flow.py new file mode 100644 index 00000000000..dfad1100cad --- /dev/null +++ b/tests/components/raspberry_pi/test_config_flow.py @@ -0,0 +1,58 @@ +"""Test the Raspberry Pi config flow.""" +from unittest.mock import patch + +from homeassistant.components.raspberry_pi.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_CREATE_ENTRY + +from tests.common import MockConfigEntry, MockModule, mock_integration + + +async def test_config_flow(hass: HomeAssistant) -> None: + """Test the config flow.""" + mock_integration(hass, MockModule("hassio")) + + with patch( + "homeassistant.components.raspberry_pi.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "system"} + ) + + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "Raspberry Pi" + assert result["data"] == {} + assert result["options"] == {} + assert len(mock_setup_entry.mock_calls) == 1 + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + assert config_entry.data == {} + assert config_entry.options == {} + assert config_entry.title == "Raspberry Pi" + + +async def test_config_flow_single_entry(hass: HomeAssistant) -> None: + """Test only a single entry is allowed.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Raspberry Pi", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.raspberry_pi.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "system"} + ) + + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "single_instance_allowed" + mock_setup_entry.assert_not_called() diff --git a/tests/components/raspberry_pi/test_hardware.py b/tests/components/raspberry_pi/test_hardware.py new file mode 100644 index 00000000000..748972c8d60 --- /dev/null +++ b/tests/components/raspberry_pi/test_hardware.py @@ -0,0 +1,57 @@ +"""Test the Raspberry Pi hardware platform.""" +import pytest + +from homeassistant.components.hassio import DATA_OS_INFO +from homeassistant.components.raspberry_pi.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import MockModule, mock_integration + + +async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: + """Test we can get the board info.""" + mock_integration(hass, MockModule("hassio")) + hass.data[DATA_OS_INFO] = {"board": "rpi"} + + assert await async_setup_component(hass, DOMAIN, {}) + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "hardware/info"}) + msg = await client.receive_json() + + assert msg["id"] == 1 + assert msg["success"] + assert msg["result"] == { + "hardware": [ + { + "board": { + "hassio_board_id": "rpi", + "manufacturer": "raspberry_pi", + "model": "1", + "revision": None, + }, + "name": "Raspberry Pi", + "url": None, + } + ] + } + + +@pytest.mark.parametrize("os_info", [None, {"board": None}, {"board": "other"}]) +async def test_hardware_info_fail(hass: HomeAssistant, hass_ws_client, os_info) -> None: + """Test async_info raises if os_info is not as expected.""" + mock_integration(hass, MockModule("hassio")) + hass.data[DATA_OS_INFO] = os_info + + assert await async_setup_component(hass, DOMAIN, {}) + + client = await hass_ws_client(hass) + + await client.send_json({"id": 1, "type": "hardware/info"}) + msg = await client.receive_json() + + assert msg["id"] == 1 + assert msg["success"] + assert msg["result"] == {"hardware": []} diff --git a/tests/components/raspberry_pi/test_init.py b/tests/components/raspberry_pi/test_init.py new file mode 100644 index 00000000000..dd86da7bce0 --- /dev/null +++ b/tests/components/raspberry_pi/test_init.py @@ -0,0 +1,72 @@ +"""Test the Raspberry Pi integration.""" +from unittest.mock import patch + +from homeassistant.components.raspberry_pi.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, MockModule, mock_integration + + +async def test_setup_entry(hass: HomeAssistant) -> None: + """Test setup of a config entry.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Raspberry Pi", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.raspberry_pi.get_os_info", + return_value={"board": "rpi"}, + ) as mock_get_os_info: + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + +async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None: + """Test setup of a config entry with wrong board type.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Raspberry Pi", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.raspberry_pi.get_os_info", + return_value={"board": "generic-x86-64"}, + ) as mock_get_os_info: + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + +async def test_setup_entry_wait_hassio(hass: HomeAssistant) -> None: + """Test setup of a config entry when hassio has not fetched os_info.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Raspberry Pi", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.raspberry_pi.get_os_info", + return_value=None, + ) as mock_get_os_info: + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + assert config_entry.state == ConfigEntryState.SETUP_RETRY From 3c246b78004917d494e6c81a389e73bee4f0bf70 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 25 May 2022 20:42:14 +0200 Subject: [PATCH 839/930] Update mypy to 0.960 (#72481) --- homeassistant/components/recorder/purge.py | 4 ++-- homeassistant/components/sonos/helpers.py | 2 +- homeassistant/util/color.py | 6 +++--- requirements_test.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/recorder/purge.py b/homeassistant/components/recorder/purge.py index 432e3993add..10136dfb5a6 100644 --- a/homeassistant/components/recorder/purge.py +++ b/homeassistant/components/recorder/purge.py @@ -315,7 +315,7 @@ def _select_unused_attributes_ids( seen_ids |= { attrs_id[0] for attrs_id in session.execute( - attributes_ids_exist_in_states(*attr_ids) + attributes_ids_exist_in_states(*attr_ids) # type: ignore[arg-type] ).all() if attrs_id[0] is not None } @@ -362,7 +362,7 @@ def _select_unused_event_data_ids( seen_ids |= { data_id[0] for data_id in session.execute( - data_ids_exist_in_events(*data_ids_group) + data_ids_exist_in_events(*data_ids_group) # type: ignore[arg-type] ).all() if data_id[0] is not None } diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index 22d9f6c08a5..9e8fcbd6a12 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -62,7 +62,7 @@ def soco_error( def wrapper(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> _R | None: """Wrap for all soco UPnP exception.""" - args_soco = next((arg for arg in args if isinstance(arg, SoCo)), None) # type: ignore[attr-defined] + args_soco = next((arg for arg in args if isinstance(arg, SoCo)), None) try: result = funct(self, *args, **kwargs) except (OSError, SoCoException, SoCoUPnPException) as err: diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index 21c877f9377..448b417cc97 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -3,7 +3,7 @@ from __future__ import annotations import colorsys import math -from typing import NamedTuple, cast +from typing import NamedTuple import attr @@ -295,9 +295,9 @@ def color_xy_brightness_to_RGB( # Apply reverse gamma correction. r, g, b = map( - lambda x: (12.92 * x) + lambda x: (12.92 * x) # type: ignore[no-any-return] if (x <= 0.0031308) - else ((1.0 + 0.055) * cast(float, pow(x, (1.0 / 2.4))) - 0.055), + else ((1.0 + 0.055) * pow(x, (1.0 / 2.4)) - 0.055), [r, g, b], ) diff --git a/requirements_test.txt b/requirements_test.txt index 29e7364059f..7e4e29de339 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -11,7 +11,7 @@ codecov==2.1.12 coverage==6.4 freezegun==1.2.1 mock-open==1.4.0 -mypy==0.950 +mypy==0.960 pre-commit==2.19.0 pylint==2.13.9 pipdeptree==2.2.1 From a947c80f3086a153ac938674fe320aa6d707b885 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Wed, 25 May 2022 13:42:27 -0500 Subject: [PATCH 840/930] Bump Frontend to 20220525.0 (#72496) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index b5fb12b92f6..6c8f568c4d2 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220524.0"], + "requirements": ["home-assistant-frontend==20220525.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7dd302fea28..5a6dc9891a6 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220524.0 +home-assistant-frontend==20220525.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 27c5a401bd4..fbcada246a7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220524.0 +home-assistant-frontend==20220525.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 58a345adbfc..f347a7ea082 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220524.0 +home-assistant-frontend==20220525.0 # homeassistant.components.home_connect homeconnect==0.7.0 From b8d260f6eabe3fe20cd444846e621e3ea794e01a Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 25 May 2022 20:49:10 +0200 Subject: [PATCH 841/930] Bumped version to 2022.6.0b0 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 74fe6adfdfd..e437c171c50 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0.dev0" +PATCH_VERSION: Final = "0b0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 825a4407012..c9b915b229a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0.dev0 +version = 2022.6.0b0 url = https://www.home-assistant.io/ [options] From d39de6e6994f24e2e4961199fae07cc9b14505cc Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 25 May 2022 13:00:48 -0700 Subject: [PATCH 842/930] Throw nest climate API errors as HomeAssistantErrors (#72474) --- homeassistant/components/nest/climate_sdm.py | 34 +++++++---- tests/components/nest/test_climate_sdm.py | 60 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/nest/climate_sdm.py b/homeassistant/components/nest/climate_sdm.py index bbbf83501f7..8a56f78028b 100644 --- a/homeassistant/components/nest/climate_sdm.py +++ b/homeassistant/components/nest/climate_sdm.py @@ -6,6 +6,7 @@ from typing import Any, cast from google_nest_sdm.device import Device from google_nest_sdm.device_manager import DeviceManager from google_nest_sdm.device_traits import FanTrait, TemperatureTrait +from google_nest_sdm.exceptions import ApiException from google_nest_sdm.thermostat_traits import ( ThermostatEcoTrait, ThermostatHeatCoolTrait, @@ -30,6 +31,7 @@ from homeassistant.components.climate.const import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -294,7 +296,10 @@ class ThermostatEntity(ClimateEntity): hvac_mode = HVACMode.OFF api_mode = THERMOSTAT_INV_MODE_MAP[hvac_mode] trait = self._device.traits[ThermostatModeTrait.NAME] - await trait.set_mode(api_mode) + try: + await trait.set_mode(api_mode) + except ApiException as err: + raise HomeAssistantError(f"Error setting HVAC mode: {err}") from err async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature.""" @@ -308,20 +313,26 @@ class ThermostatEntity(ClimateEntity): if ThermostatTemperatureSetpointTrait.NAME not in self._device.traits: return trait = self._device.traits[ThermostatTemperatureSetpointTrait.NAME] - if self.preset_mode == PRESET_ECO or hvac_mode == HVACMode.HEAT_COOL: - if low_temp and high_temp: - await trait.set_range(low_temp, high_temp) - elif hvac_mode == HVACMode.COOL and temp: - await trait.set_cool(temp) - elif hvac_mode == HVACMode.HEAT and temp: - await trait.set_heat(temp) + try: + if self.preset_mode == PRESET_ECO or hvac_mode == HVACMode.HEAT_COOL: + if low_temp and high_temp: + await trait.set_range(low_temp, high_temp) + elif hvac_mode == HVACMode.COOL and temp: + await trait.set_cool(temp) + elif hvac_mode == HVACMode.HEAT and temp: + await trait.set_heat(temp) + except ApiException as err: + raise HomeAssistantError(f"Error setting HVAC mode: {err}") from err async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new target preset mode.""" if preset_mode not in self.preset_modes: raise ValueError(f"Unsupported preset_mode '{preset_mode}'") trait = self._device.traits[ThermostatEcoTrait.NAME] - await trait.set_mode(PRESET_INV_MODE_MAP[preset_mode]) + try: + await trait.set_mode(PRESET_INV_MODE_MAP[preset_mode]) + except ApiException as err: + raise HomeAssistantError(f"Error setting HVAC mode: {err}") from err async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new target fan mode.""" @@ -331,4 +342,7 @@ class ThermostatEntity(ClimateEntity): duration = None if fan_mode != FAN_OFF: duration = MAX_FAN_DURATION - await trait.set_timer(FAN_INV_MODE_MAP[fan_mode], duration=duration) + try: + await trait.set_timer(FAN_INV_MODE_MAP[fan_mode], duration=duration) + except ApiException as err: + raise HomeAssistantError(f"Error setting HVAC mode: {err}") from err diff --git a/tests/components/nest/test_climate_sdm.py b/tests/components/nest/test_climate_sdm.py index 5f3efa362b3..123742607ad 100644 --- a/tests/components/nest/test_climate_sdm.py +++ b/tests/components/nest/test_climate_sdm.py @@ -6,8 +6,10 @@ pubsub subscriber. """ from collections.abc import Awaitable, Callable +from http import HTTPStatus from typing import Any +import aiohttp from google_nest_sdm.auth import AbstractAuth from google_nest_sdm.event import EventMessage import pytest @@ -41,6 +43,7 @@ from homeassistant.components.climate.const import ( ) from homeassistant.const import ATTR_TEMPERATURE from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from .common import ( DEVICE_COMMAND, @@ -1380,3 +1383,60 @@ async def test_thermostat_invalid_set_preset_mode( # Preset is unchanged assert thermostat.attributes[ATTR_PRESET_MODE] == PRESET_NONE assert thermostat.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_NONE] + + +async def test_thermostat_hvac_mode_failure( + hass: HomeAssistant, + setup_platform: PlatformSetup, + auth: FakeAuth, + create_device: CreateDevice, +) -> None: + """Test setting an hvac_mode that is not supported.""" + create_device.create( + { + "sdm.devices.traits.ThermostatHvac": {"status": "OFF"}, + "sdm.devices.traits.ThermostatMode": { + "availableModes": ["HEAT", "COOL", "HEATCOOL", "OFF"], + "mode": "OFF", + }, + "sdm.devices.traits.Fan": { + "timerMode": "OFF", + "timerTimeout": "2019-05-10T03:22:54Z", + }, + "sdm.devices.traits.ThermostatEco": { + "availableModes": ["MANUAL_ECO", "OFF"], + "mode": "OFF", + "heatCelsius": 15.0, + "coolCelsius": 28.0, + }, + } + ) + await setup_platform() + + assert len(hass.states.async_all()) == 1 + thermostat = hass.states.get("climate.my_thermostat") + assert thermostat is not None + assert thermostat.state == HVAC_MODE_OFF + assert thermostat.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF + + auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)] + with pytest.raises(HomeAssistantError): + await common.async_set_hvac_mode(hass, HVAC_MODE_HEAT) + await hass.async_block_till_done() + + auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)] + with pytest.raises(HomeAssistantError): + await common.async_set_temperature( + hass, hvac_mode=HVAC_MODE_HEAT, temperature=25.0 + ) + await hass.async_block_till_done() + + auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)] + with pytest.raises(HomeAssistantError): + await common.async_set_fan_mode(hass, FAN_ON) + await hass.async_block_till_done() + + auth.responses = [aiohttp.web.Response(status=HTTPStatus.BAD_REQUEST)] + with pytest.raises(HomeAssistantError): + await common.async_set_preset_mode(hass, PRESET_ECO) + await hass.async_block_till_done() From f9d9c3401897768de4e7fb637bddba24bfbd6de1 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 26 May 2022 22:15:44 +0200 Subject: [PATCH 843/930] Add hardkernel hardware integration (#72489) * Add hardkernel hardware integration * Remove debug prints * Improve tests * Improve test coverage --- CODEOWNERS | 2 + .../components/hardkernel/__init__.py | 22 +++++ .../components/hardkernel/config_flow.py | 22 +++++ homeassistant/components/hardkernel/const.py | 3 + .../components/hardkernel/hardware.py | 39 ++++++++ .../components/hardkernel/manifest.json | 9 ++ script/hassfest/manifest.py | 1 + tests/components/hardkernel/__init__.py | 1 + .../components/hardkernel/test_config_flow.py | 58 ++++++++++++ tests/components/hardkernel/test_hardware.py | 89 +++++++++++++++++++ tests/components/hardkernel/test_init.py | 72 +++++++++++++++ 11 files changed, 318 insertions(+) create mode 100644 homeassistant/components/hardkernel/__init__.py create mode 100644 homeassistant/components/hardkernel/config_flow.py create mode 100644 homeassistant/components/hardkernel/const.py create mode 100644 homeassistant/components/hardkernel/hardware.py create mode 100644 homeassistant/components/hardkernel/manifest.json create mode 100644 tests/components/hardkernel/__init__.py create mode 100644 tests/components/hardkernel/test_config_flow.py create mode 100644 tests/components/hardkernel/test_hardware.py create mode 100644 tests/components/hardkernel/test_init.py diff --git a/CODEOWNERS b/CODEOWNERS index 98d60fbfcb7..1b0d0ae4d3c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -416,6 +416,8 @@ build.json @home-assistant/supervisor /tests/components/guardian/ @bachya /homeassistant/components/habitica/ @ASMfreaK @leikoilja /tests/components/habitica/ @ASMfreaK @leikoilja +/homeassistant/components/hardkernel/ @home-assistant/core +/tests/components/hardkernel/ @home-assistant/core /homeassistant/components/hardware/ @home-assistant/core /tests/components/hardware/ @home-assistant/core /homeassistant/components/harmony/ @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan diff --git a/homeassistant/components/hardkernel/__init__.py b/homeassistant/components/hardkernel/__init__.py new file mode 100644 index 00000000000..6dfe30b9e75 --- /dev/null +++ b/homeassistant/components/hardkernel/__init__.py @@ -0,0 +1,22 @@ +"""The Hardkernel integration.""" +from __future__ import annotations + +from homeassistant.components.hassio import get_os_info +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up a Hardkernel config entry.""" + if (os_info := get_os_info(hass)) is None: + # The hassio integration has not yet fetched data from the supervisor + raise ConfigEntryNotReady + + board: str + if (board := os_info.get("board")) is None or not board.startswith("odroid"): + # Not running on a Hardkernel board, Home Assistant may have been migrated + hass.async_create_task(hass.config_entries.async_remove(entry.entry_id)) + return False + + return True diff --git a/homeassistant/components/hardkernel/config_flow.py b/homeassistant/components/hardkernel/config_flow.py new file mode 100644 index 00000000000..b0445fae231 --- /dev/null +++ b/homeassistant/components/hardkernel/config_flow.py @@ -0,0 +1,22 @@ +"""Config flow for the Hardkernel integration.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.config_entries import ConfigFlow +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN + + +class HardkernelConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for Hardkernel.""" + + VERSION = 1 + + async def async_step_system(self, data: dict[str, Any] | None = None) -> FlowResult: + """Handle the initial step.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + + return self.async_create_entry(title="Hardkernel", data={}) diff --git a/homeassistant/components/hardkernel/const.py b/homeassistant/components/hardkernel/const.py new file mode 100644 index 00000000000..2850f3d4ebb --- /dev/null +++ b/homeassistant/components/hardkernel/const.py @@ -0,0 +1,3 @@ +"""Constants for the Hardkernel integration.""" + +DOMAIN = "hardkernel" diff --git a/homeassistant/components/hardkernel/hardware.py b/homeassistant/components/hardkernel/hardware.py new file mode 100644 index 00000000000..804f105f2ed --- /dev/null +++ b/homeassistant/components/hardkernel/hardware.py @@ -0,0 +1,39 @@ +"""The Hardkernel hardware platform.""" +from __future__ import annotations + +from homeassistant.components.hardware.models import BoardInfo, HardwareInfo +from homeassistant.components.hassio import get_os_info +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError + +from .const import DOMAIN + +BOARD_NAMES = { + "odroid-c2": "Hardkernel Odroid-C2", + "odroid-c4": "Hardkernel Odroid-C4", + "odroid-n2": "Home Assistant Blue / Hardkernel Odroid-N2", + "odroid-xu4": "Hardkernel Odroid-XU4", +} + + +@callback +def async_info(hass: HomeAssistant) -> HardwareInfo: + """Return board info.""" + if (os_info := get_os_info(hass)) is None: + raise HomeAssistantError + board: str + if (board := os_info.get("board")) is None: + raise HomeAssistantError + if not board.startswith("odroid"): + raise HomeAssistantError + + return HardwareInfo( + board=BoardInfo( + hassio_board_id=board, + manufacturer=DOMAIN, + model=board, + revision=None, + ), + name=BOARD_NAMES.get(board, f"Unknown hardkernel Odroid model '{board}'"), + url=None, + ) diff --git a/homeassistant/components/hardkernel/manifest.json b/homeassistant/components/hardkernel/manifest.json new file mode 100644 index 00000000000..366ca245191 --- /dev/null +++ b/homeassistant/components/hardkernel/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "hardkernel", + "name": "Hardkernel", + "config_flow": false, + "documentation": "https://www.home-assistant.io/integrations/hardkernel", + "dependencies": ["hardware", "hassio"], + "codeowners": ["@home-assistant/core"], + "integration_type": "hardware" +} diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index c478d16cf0f..7f2e8e0d477 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -52,6 +52,7 @@ NO_IOT_CLASS = [ "downloader", "ffmpeg", "frontend", + "hardkernel", "hardware", "history", "homeassistant", diff --git a/tests/components/hardkernel/__init__.py b/tests/components/hardkernel/__init__.py new file mode 100644 index 00000000000..d63b70d5cc5 --- /dev/null +++ b/tests/components/hardkernel/__init__.py @@ -0,0 +1 @@ +"""Tests for the Hardkernel integration.""" diff --git a/tests/components/hardkernel/test_config_flow.py b/tests/components/hardkernel/test_config_flow.py new file mode 100644 index 00000000000..f74b4a4e658 --- /dev/null +++ b/tests/components/hardkernel/test_config_flow.py @@ -0,0 +1,58 @@ +"""Test the Hardkernel config flow.""" +from unittest.mock import patch + +from homeassistant.components.hardkernel.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_CREATE_ENTRY + +from tests.common import MockConfigEntry, MockModule, mock_integration + + +async def test_config_flow(hass: HomeAssistant) -> None: + """Test the config flow.""" + mock_integration(hass, MockModule("hassio")) + + with patch( + "homeassistant.components.hardkernel.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "system"} + ) + + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == "Hardkernel" + assert result["data"] == {} + assert result["options"] == {} + assert len(mock_setup_entry.mock_calls) == 1 + + config_entry = hass.config_entries.async_entries(DOMAIN)[0] + assert config_entry.data == {} + assert config_entry.options == {} + assert config_entry.title == "Hardkernel" + + +async def test_config_flow_single_entry(hass: HomeAssistant) -> None: + """Test only a single entry is allowed.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.hardkernel.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": "system"} + ) + + assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "single_instance_allowed" + mock_setup_entry.assert_not_called() diff --git a/tests/components/hardkernel/test_hardware.py b/tests/components/hardkernel/test_hardware.py new file mode 100644 index 00000000000..1c71959719c --- /dev/null +++ b/tests/components/hardkernel/test_hardware.py @@ -0,0 +1,89 @@ +"""Test the Hardkernel hardware platform.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.hardkernel.const import DOMAIN +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, MockModule, mock_integration + + +async def test_hardware_info(hass: HomeAssistant, hass_ws_client) -> None: + """Test we can get the board info.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.hardkernel.get_os_info", + return_value={"board": "odroid-n2"}, + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + client = await hass_ws_client(hass) + + with patch( + "homeassistant.components.hardkernel.hardware.get_os_info", + return_value={"board": "odroid-n2"}, + ): + await client.send_json({"id": 1, "type": "hardware/info"}) + msg = await client.receive_json() + + assert msg["id"] == 1 + assert msg["success"] + assert msg["result"] == { + "hardware": [ + { + "board": { + "hassio_board_id": "odroid-n2", + "manufacturer": "hardkernel", + "model": "odroid-n2", + "revision": None, + }, + "name": "Home Assistant Blue / Hardkernel Odroid-N2", + "url": None, + } + ] + } + + +@pytest.mark.parametrize("os_info", [None, {"board": None}, {"board": "other"}]) +async def test_hardware_info_fail(hass: HomeAssistant, hass_ws_client, os_info) -> None: + """Test async_info raises if os_info is not as expected.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.hardkernel.get_os_info", + return_value={"board": "odroid-n2"}, + ): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + client = await hass_ws_client(hass) + + with patch( + "homeassistant.components.hardkernel.hardware.get_os_info", + return_value=os_info, + ): + await client.send_json({"id": 1, "type": "hardware/info"}) + msg = await client.receive_json() + + assert msg["id"] == 1 + assert msg["success"] + assert msg["result"] == {"hardware": []} diff --git a/tests/components/hardkernel/test_init.py b/tests/components/hardkernel/test_init.py new file mode 100644 index 00000000000..f202777f530 --- /dev/null +++ b/tests/components/hardkernel/test_init.py @@ -0,0 +1,72 @@ +"""Test the Hardkernel integration.""" +from unittest.mock import patch + +from homeassistant.components.hardkernel.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry, MockModule, mock_integration + + +async def test_setup_entry(hass: HomeAssistant) -> None: + """Test setup of a config entry.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.hardkernel.get_os_info", + return_value={"board": "odroid-n2"}, + ) as mock_get_os_info: + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + +async def test_setup_entry_wrong_board(hass: HomeAssistant) -> None: + """Test setup of a config entry with wrong board type.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.hardkernel.get_os_info", + return_value={"board": "generic-x86-64"}, + ) as mock_get_os_info: + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + + +async def test_setup_entry_wait_hassio(hass: HomeAssistant) -> None: + """Test setup of a config entry when hassio has not fetched os_info.""" + mock_integration(hass, MockModule("hassio")) + + # Setup the config entry + config_entry = MockConfigEntry( + data={}, + domain=DOMAIN, + options={}, + title="Hardkernel", + ) + config_entry.add_to_hass(hass) + with patch( + "homeassistant.components.hardkernel.get_os_info", + return_value=None, + ) as mock_get_os_info: + assert not await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert len(mock_get_os_info.mock_calls) == 1 + assert config_entry.state == ConfigEntryState.SETUP_RETRY From 6bb78754dd8cdbbba47d83f8dac804437f65c8b6 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 26 May 2022 22:02:39 +0200 Subject: [PATCH 844/930] Move manual configuration of MQTT device_tracker to the integration key (#72493) --- homeassistant/components/mqtt/__init__.py | 4 +- .../mqtt/device_tracker/__init__.py | 10 ++- .../mqtt/device_tracker/schema_discovery.py | 26 +++++-- tests/components/mqtt/test_device_tracker.py | 69 +++++++++++++------ 4 files changed, 82 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index df5d52c443a..e8847375584 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -159,6 +159,7 @@ PLATFORMS = [ Platform.BUTTON, Platform.CAMERA, Platform.CLIMATE, + Platform.DEVICE_TRACKER, Platform.COVER, Platform.FAN, Platform.HUMIDIFIER, @@ -196,17 +197,18 @@ PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(Platform.CAMERA.value): cv.ensure_list, vol.Optional(Platform.CLIMATE.value): cv.ensure_list, vol.Optional(Platform.COVER.value): cv.ensure_list, + vol.Optional(Platform.DEVICE_TRACKER.value): cv.ensure_list, vol.Optional(Platform.FAN.value): cv.ensure_list, vol.Optional(Platform.HUMIDIFIER.value): cv.ensure_list, vol.Optional(Platform.LIGHT.value): cv.ensure_list, vol.Optional(Platform.LOCK.value): cv.ensure_list, + vol.Optional(Platform.NUMBER.value): cv.ensure_list, vol.Optional(Platform.SCENE.value): cv.ensure_list, vol.Optional(Platform.SELECT.value): cv.ensure_list, vol.Optional(Platform.SIREN.value): cv.ensure_list, vol.Optional(Platform.SENSOR.value): cv.ensure_list, vol.Optional(Platform.SWITCH.value): cv.ensure_list, vol.Optional(Platform.VACUUM.value): cv.ensure_list, - vol.Optional(Platform.NUMBER.value): cv.ensure_list, } ) diff --git a/homeassistant/components/mqtt/device_tracker/__init__.py b/homeassistant/components/mqtt/device_tracker/__init__.py index 03574e6554b..bcd5bbd4ee1 100644 --- a/homeassistant/components/mqtt/device_tracker/__init__.py +++ b/homeassistant/components/mqtt/device_tracker/__init__.py @@ -1,7 +1,15 @@ """Support for tracking MQTT enabled devices.""" +import voluptuous as vol + +from homeassistant.components import device_tracker + +from ..mixins import warn_for_legacy_schema from .schema_discovery import async_setup_entry_from_discovery from .schema_yaml import PLATFORM_SCHEMA_YAML, async_setup_scanner_from_yaml -PLATFORM_SCHEMA = PLATFORM_SCHEMA_YAML +# Configuring MQTT Device Trackers under the device_tracker platform key is deprecated in HA Core 2022.6 +PLATFORM_SCHEMA = vol.All( + PLATFORM_SCHEMA_YAML, warn_for_legacy_schema(device_tracker.DOMAIN) +) async_setup_scanner = async_setup_scanner_from_yaml async_setup_entry = async_setup_entry_from_discovery diff --git a/homeassistant/components/mqtt/device_tracker/schema_discovery.py b/homeassistant/components/mqtt/device_tracker/schema_discovery.py index a7b597d0689..aa7506bd5e3 100644 --- a/homeassistant/components/mqtt/device_tracker/schema_discovery.py +++ b/homeassistant/components/mqtt/device_tracker/schema_discovery.py @@ -1,4 +1,5 @@ -"""Support for tracking MQTT enabled devices identified through discovery.""" +"""Support for tracking MQTT enabled devices.""" +import asyncio import functools import voluptuous as vol @@ -22,13 +23,18 @@ from .. import MqttValueTemplate, subscription from ... import mqtt from ..const import CONF_QOS, CONF_STATE_TOPIC from ..debug_info import log_messages -from ..mixins import MQTT_ENTITY_COMMON_SCHEMA, MqttEntity, async_setup_entry_helper +from ..mixins import ( + MQTT_ENTITY_COMMON_SCHEMA, + MqttEntity, + async_get_platform_config_from_yaml, + async_setup_entry_helper, +) CONF_PAYLOAD_HOME = "payload_home" CONF_PAYLOAD_NOT_HOME = "payload_not_home" CONF_SOURCE_TYPE = "source_type" -PLATFORM_SCHEMA_DISCOVERY = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( +PLATFORM_SCHEMA_MODERN = mqtt.MQTT_RO_SCHEMA.extend( { vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_PAYLOAD_HOME, default=STATE_HOME): cv.string, @@ -37,11 +43,21 @@ PLATFORM_SCHEMA_DISCOVERY = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend( } ).extend(MQTT_ENTITY_COMMON_SCHEMA.schema) -DISCOVERY_SCHEMA = PLATFORM_SCHEMA_DISCOVERY.extend({}, extra=vol.REMOVE_EXTRA) +DISCOVERY_SCHEMA = PLATFORM_SCHEMA_MODERN.extend({}, extra=vol.REMOVE_EXTRA) async def async_setup_entry_from_discovery(hass, config_entry, async_add_entities): - """Set up MQTT device tracker dynamically through MQTT discovery.""" + """Set up MQTT device tracker configuration.yaml and dynamically through MQTT discovery.""" + # load and initialize platform config from configuration.yaml + await asyncio.gather( + *( + _async_setup_entity(hass, async_add_entities, config, config_entry) + for config in await async_get_platform_config_from_yaml( + hass, device_tracker.DOMAIN, PLATFORM_SCHEMA_MODERN + ) + ) + ) + # setup for discovery setup = functools.partial( _async_setup_entity, hass, async_add_entities, config_entry=config_entry ) diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py index c85fcef7dc4..020fbad6166 100644 --- a/tests/components/mqtt/test_device_tracker.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -1,22 +1,17 @@ -"""The tests for the MQTT device tracker platform.""" +"""The tests for the MQTT device tracker platform using configuration.yaml.""" from unittest.mock import patch -import pytest - from homeassistant.components.device_tracker.const import DOMAIN, SOURCE_TYPE_BLUETOOTH from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME from homeassistant.setup import async_setup_component +from .test_common import help_test_setup_manual_entity_from_yaml + from tests.common import async_fire_mqtt_message -@pytest.fixture(autouse=True) -def setup_comp(hass, mqtt_mock): - """Set up mqtt component.""" - pass - - -async def test_ensure_device_tracker_platform_validation(hass): +# Deprecated in HA Core 2022.6 +async def test_legacy_ensure_device_tracker_platform_validation(hass, mqtt_mock): """Test if platform validation was done.""" async def mock_setup_scanner(hass, config, see, discovery_info=None): @@ -37,7 +32,8 @@ async def test_ensure_device_tracker_platform_validation(hass): assert mock_sp.call_count == 1 -async def test_new_message(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_new_message(hass, mock_device_tracker_conf, mqtt_mock): """Test new message.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -53,7 +49,10 @@ async def test_new_message(hass, mock_device_tracker_conf): assert hass.states.get(entity_id).state == location -async def test_single_level_wildcard_topic(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_single_level_wildcard_topic( + hass, mock_device_tracker_conf, mqtt_mock +): """Test single level wildcard topic.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -72,7 +71,10 @@ async def test_single_level_wildcard_topic(hass, mock_device_tracker_conf): assert hass.states.get(entity_id).state == location -async def test_multi_level_wildcard_topic(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_multi_level_wildcard_topic( + hass, mock_device_tracker_conf, mqtt_mock +): """Test multi level wildcard topic.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -91,7 +93,10 @@ async def test_multi_level_wildcard_topic(hass, mock_device_tracker_conf): assert hass.states.get(entity_id).state == location -async def test_single_level_wildcard_topic_not_matching(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_single_level_wildcard_topic_not_matching( + hass, mock_device_tracker_conf, mqtt_mock +): """Test not matching single level wildcard topic.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -110,7 +115,10 @@ async def test_single_level_wildcard_topic_not_matching(hass, mock_device_tracke assert hass.states.get(entity_id) is None -async def test_multi_level_wildcard_topic_not_matching(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_multi_level_wildcard_topic_not_matching( + hass, mock_device_tracker_conf, mqtt_mock +): """Test not matching multi level wildcard topic.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -129,8 +137,9 @@ async def test_multi_level_wildcard_topic_not_matching(hass, mock_device_tracker assert hass.states.get(entity_id) is None -async def test_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf +# Deprecated in HA Core 2022.6 +async def test_legacy_matching_custom_payload_for_home_and_not_home( + hass, mock_device_tracker_conf, mqtt_mock ): """Test custom payload_home sets state to home and custom payload_not_home sets state to not_home.""" dev_id = "paulus" @@ -161,8 +170,9 @@ async def test_matching_custom_payload_for_home_and_not_home( assert hass.states.get(entity_id).state == STATE_NOT_HOME -async def test_not_matching_custom_payload_for_home_and_not_home( - hass, mock_device_tracker_conf +# Deprecated in HA Core 2022.6 +async def test_legacy_not_matching_custom_payload_for_home_and_not_home( + hass, mock_device_tracker_conf, mqtt_mock ): """Test not matching payload does not set state to home or not_home.""" dev_id = "paulus" @@ -191,7 +201,8 @@ async def test_not_matching_custom_payload_for_home_and_not_home( assert hass.states.get(entity_id).state != STATE_NOT_HOME -async def test_matching_source_type(hass, mock_device_tracker_conf): +# Deprecated in HA Core 2022.6 +async def test_legacy_matching_source_type(hass, mock_device_tracker_conf, mqtt_mock): """Test setting source type.""" dev_id = "paulus" entity_id = f"{DOMAIN}.{dev_id}" @@ -215,3 +226,21 @@ async def test_matching_source_type(hass, mock_device_tracker_conf): async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() assert hass.states.get(entity_id).attributes["source_type"] == SOURCE_TYPE_BLUETOOTH + + +async def test_setup_with_modern_schema( + hass, caplog, tmp_path, mock_device_tracker_conf +): + """Test setup using the modern schema.""" + dev_id = "jan" + entity_id = f"{DOMAIN}.{dev_id}" + topic = "/location/jan" + + hass.config.components = {"zone"} + config = {"name": dev_id, "state_topic": topic} + + await help_test_setup_manual_entity_from_yaml( + hass, caplog, tmp_path, DOMAIN, config + ) + + assert hass.states.get(entity_id) is not None From ff72c32b20248342cc7ab022cb03662cdb5deb6b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 25 May 2022 15:17:08 -1000 Subject: [PATCH 845/930] Fixes for logbook filtering and add it to the live stream (#72501) --- homeassistant/components/logbook/processor.py | 32 ++- .../components/logbook/queries/__init__.py | 12 +- .../components/logbook/queries/all.py | 18 +- .../components/logbook/queries/common.py | 42 +--- .../components/logbook/queries/devices.py | 7 +- .../components/logbook/queries/entities.py | 9 +- homeassistant/components/recorder/filters.py | 100 +++++++--- homeassistant/components/recorder/history.py | 4 +- homeassistant/components/recorder/models.py | 40 +++- tests/components/logbook/test_init.py | 5 +- .../components/logbook/test_websocket_api.py | 185 ++++++++++++++++++ 11 files changed, 340 insertions(+), 114 deletions(-) diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py index 03506695700..ea6002cc62c 100644 --- a/homeassistant/components/logbook/processor.py +++ b/homeassistant/components/logbook/processor.py @@ -173,12 +173,6 @@ class EventProcessor: self.filters, self.context_id, ) - if _LOGGER.isEnabledFor(logging.DEBUG): - _LOGGER.debug( - "Literal statement: %s", - stmt.compile(compile_kwargs={"literal_binds": True}), - ) - with session_scope(hass=self.hass) as session: return self.humanify(yield_rows(session.execute(stmt))) @@ -214,20 +208,16 @@ def _humanify( include_entity_name = logbook_run.include_entity_name format_time = logbook_run.format_time - def _keep_row(row: Row | EventAsRow, event_type: str) -> bool: + def _keep_row(row: EventAsRow) -> bool: """Check if the entity_filter rejects a row.""" assert entities_filter is not None - if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): + if entity_id := row.entity_id: return entities_filter(entity_id) - - if event_type in external_events: - # If the entity_id isn't described, use the domain that describes - # the event for filtering. - domain: str | None = external_events[event_type][0] - else: - domain = _row_event_data_extract(row, DOMAIN_JSON_EXTRACT) - - return domain is not None and entities_filter(f"{domain}._") + if entity_id := row.data.get(ATTR_ENTITY_ID): + return entities_filter(entity_id) + if domain := row.data.get(ATTR_DOMAIN): + return entities_filter(f"{domain}._") + return True # Process rows for row in rows: @@ -236,12 +226,12 @@ def _humanify( continue event_type = row.event_type if event_type == EVENT_CALL_SERVICE or ( - event_type is not PSUEDO_EVENT_STATE_CHANGED - and entities_filter is not None - and not _keep_row(row, event_type) + entities_filter + # We literally mean is EventAsRow not a subclass of EventAsRow + and type(row) is EventAsRow # pylint: disable=unidiomatic-typecheck + and not _keep_row(row) ): continue - if event_type is PSUEDO_EVENT_STATE_CHANGED: entity_id = row.entity_id assert entity_id is not None diff --git a/homeassistant/components/logbook/queries/__init__.py b/homeassistant/components/logbook/queries/__init__.py index 3672f1e761c..3c027823612 100644 --- a/homeassistant/components/logbook/queries/__init__.py +++ b/homeassistant/components/logbook/queries/__init__.py @@ -27,8 +27,16 @@ def statement_for_request( # No entities: logbook sends everything for the timeframe # limited by the context_id and the yaml configured filter if not entity_ids and not device_ids: - entity_filter = filters.entity_filter() if filters else None - return all_stmt(start_day, end_day, event_types, entity_filter, context_id) + states_entity_filter = filters.states_entity_filter() if filters else None + events_entity_filter = filters.events_entity_filter() if filters else None + return all_stmt( + start_day, + end_day, + event_types, + states_entity_filter, + events_entity_filter, + context_id, + ) # sqlalchemy caches object quoting, the # json quotable ones must be a different diff --git a/homeassistant/components/logbook/queries/all.py b/homeassistant/components/logbook/queries/all.py index da17c7bddeb..d321578f545 100644 --- a/homeassistant/components/logbook/queries/all.py +++ b/homeassistant/components/logbook/queries/all.py @@ -22,7 +22,8 @@ def all_stmt( start_day: dt, end_day: dt, event_types: tuple[str, ...], - entity_filter: ClauseList | None = None, + states_entity_filter: ClauseList | None = None, + events_entity_filter: ClauseList | None = None, context_id: str | None = None, ) -> StatementLambdaElement: """Generate a logbook query for all entities.""" @@ -37,12 +38,17 @@ def all_stmt( _states_query_for_context_id(start_day, end_day, context_id), legacy_select_events_context_id(start_day, end_day, context_id), ) - elif entity_filter is not None: - stmt += lambda s: s.union_all( - _states_query_for_all(start_day, end_day).where(entity_filter) - ) else: - stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day)) + if events_entity_filter is not None: + stmt += lambda s: s.where(events_entity_filter) + + if states_entity_filter is not None: + stmt += lambda s: s.union_all( + _states_query_for_all(start_day, end_day).where(states_entity_filter) + ) + else: + stmt += lambda s: s.union_all(_states_query_for_all(start_day, end_day)) + stmt += lambda s: s.order_by(Events.time_fired) return stmt diff --git a/homeassistant/components/logbook/queries/common.py b/homeassistant/components/logbook/queries/common.py index 237fde3f653..6049d6beb81 100644 --- a/homeassistant/components/logbook/queries/common.py +++ b/homeassistant/components/logbook/queries/common.py @@ -1,22 +1,20 @@ """Queries for logbook.""" from __future__ import annotations -from collections.abc import Callable from datetime import datetime as dt -import json -from typing import Any import sqlalchemy -from sqlalchemy import JSON, select, type_coerce -from sqlalchemy.orm import Query, aliased +from sqlalchemy import select +from sqlalchemy.orm import Query from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.expression import literal from sqlalchemy.sql.selectable import Select from homeassistant.components.proximity import DOMAIN as PROXIMITY_DOMAIN from homeassistant.components.recorder.models import ( - JSON_VARIENT_CAST, - JSONB_VARIENT_CAST, + OLD_FORMAT_ATTRS_JSON, + OLD_STATE, + SHARED_ATTRS_JSON, EventData, Events, StateAttributes, @@ -30,36 +28,6 @@ CONTINUOUS_ENTITY_ID_LIKE = [f"{domain}.%" for domain in CONTINUOUS_DOMAINS] UNIT_OF_MEASUREMENT_JSON = '"unit_of_measurement":' UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%" -OLD_STATE = aliased(States, name="old_state") - - -class JSONLiteral(JSON): # type: ignore[misc] - """Teach SA how to literalize json.""" - - def literal_processor(self, dialect: str) -> Callable[[Any], str]: - """Processor to convert a value to JSON.""" - - def process(value: Any) -> str: - """Dump json.""" - return json.dumps(value) - - return process - - -EVENT_DATA_JSON = type_coerce( - EventData.shared_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) -) -OLD_FORMAT_EVENT_DATA_JSON = type_coerce( - Events.event_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) -) - -SHARED_ATTRS_JSON = type_coerce( - StateAttributes.shared_attrs.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) -) -OLD_FORMAT_ATTRS_JSON = type_coerce( - States.attributes.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) -) - PSUEDO_EVENT_STATE_CHANGED = None # Since we don't store event_types and None diff --git a/homeassistant/components/logbook/queries/devices.py b/homeassistant/components/logbook/queries/devices.py index 5e7827b87a0..64a6477017e 100644 --- a/homeassistant/components/logbook/queries/devices.py +++ b/homeassistant/components/logbook/queries/devices.py @@ -4,24 +4,21 @@ from __future__ import annotations from collections.abc import Iterable from datetime import datetime as dt -from sqlalchemy import Column, lambda_stmt, select, union_all +from sqlalchemy import lambda_stmt, select, union_all from sqlalchemy.orm import Query from sqlalchemy.sql.elements import ClauseList from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import CTE, CompoundSelect -from homeassistant.components.recorder.models import Events, States +from homeassistant.components.recorder.models import DEVICE_ID_IN_EVENT, Events, States from .common import ( - EVENT_DATA_JSON, select_events_context_id_subquery, select_events_context_only, select_events_without_states, select_states_context_only, ) -DEVICE_ID_IN_EVENT: Column = EVENT_DATA_JSON["device_id"] - def _select_device_id_context_ids_sub_query( start_day: dt, diff --git a/homeassistant/components/logbook/queries/entities.py b/homeassistant/components/logbook/queries/entities.py index 844890c23a9..4fb211688f3 100644 --- a/homeassistant/components/logbook/queries/entities.py +++ b/homeassistant/components/logbook/queries/entities.py @@ -5,20 +5,20 @@ from collections.abc import Iterable from datetime import datetime as dt import sqlalchemy -from sqlalchemy import Column, lambda_stmt, select, union_all +from sqlalchemy import lambda_stmt, select, union_all from sqlalchemy.orm import Query from sqlalchemy.sql.lambdas import StatementLambdaElement from sqlalchemy.sql.selectable import CTE, CompoundSelect from homeassistant.components.recorder.models import ( + ENTITY_ID_IN_EVENT, ENTITY_ID_LAST_UPDATED_INDEX, + OLD_ENTITY_ID_IN_EVENT, Events, States, ) from .common import ( - EVENT_DATA_JSON, - OLD_FORMAT_EVENT_DATA_JSON, apply_states_filters, select_events_context_id_subquery, select_events_context_only, @@ -27,9 +27,6 @@ from .common import ( select_states_context_only, ) -ENTITY_ID_IN_EVENT: Column = EVENT_DATA_JSON["entity_id"] -OLD_ENTITY_ID_IN_EVENT: Column = OLD_FORMAT_EVENT_DATA_JSON["entity_id"] - def _select_entities_context_ids_sub_query( start_day: dt, diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index adc746379e6..7f1d0bc597f 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -1,14 +1,18 @@ """Provide pre-made queries on top of the recorder component.""" from __future__ import annotations -from sqlalchemy import not_, or_ +from collections.abc import Callable, Iterable +import json +from typing import Any + +from sqlalchemy import Column, not_, or_ from sqlalchemy.sql.elements import ClauseList from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE from homeassistant.helpers.entityfilter import CONF_ENTITY_GLOBS from homeassistant.helpers.typing import ConfigType -from .models import States +from .models import ENTITY_ID_IN_EVENT, OLD_ENTITY_ID_IN_EVENT, States DOMAIN = "history" HISTORY_FILTERS = "history_filters" @@ -59,50 +63,84 @@ class Filters: or self.included_entity_globs ) - def entity_filter(self) -> ClauseList: - """Generate the entity filter query.""" + def _generate_filter_for_columns( + self, columns: Iterable[Column], encoder: Callable[[Any], Any] + ) -> ClauseList: includes = [] if self.included_domains: - includes.append( - or_( - *[ - States.entity_id.like(f"{domain}.%") - for domain in self.included_domains - ] - ).self_group() - ) + includes.append(_domain_matcher(self.included_domains, columns, encoder)) if self.included_entities: - includes.append(States.entity_id.in_(self.included_entities)) - for glob in self.included_entity_globs: - includes.append(_glob_to_like(glob)) + includes.append(_entity_matcher(self.included_entities, columns, encoder)) + if self.included_entity_globs: + includes.append( + _globs_to_like(self.included_entity_globs, columns, encoder) + ) excludes = [] if self.excluded_domains: - excludes.append( - or_( - *[ - States.entity_id.like(f"{domain}.%") - for domain in self.excluded_domains - ] - ).self_group() - ) + excludes.append(_domain_matcher(self.excluded_domains, columns, encoder)) if self.excluded_entities: - excludes.append(States.entity_id.in_(self.excluded_entities)) - for glob in self.excluded_entity_globs: - excludes.append(_glob_to_like(glob)) + excludes.append(_entity_matcher(self.excluded_entities, columns, encoder)) + if self.excluded_entity_globs: + excludes.append( + _globs_to_like(self.excluded_entity_globs, columns, encoder) + ) if not includes and not excludes: return None if includes and not excludes: - return or_(*includes) + return or_(*includes).self_group() if not includes and excludes: - return not_(or_(*excludes)) + return not_(or_(*excludes).self_group()) - return or_(*includes) & not_(or_(*excludes)) + return or_(*includes).self_group() & not_(or_(*excludes).self_group()) + + def states_entity_filter(self) -> ClauseList: + """Generate the entity filter query.""" + + def _encoder(data: Any) -> Any: + """Nothing to encode for states since there is no json.""" + return data + + return self._generate_filter_for_columns((States.entity_id,), _encoder) + + def events_entity_filter(self) -> ClauseList: + """Generate the entity filter query.""" + _encoder = json.dumps + return or_( + (ENTITY_ID_IN_EVENT == _encoder(None)) + & (OLD_ENTITY_ID_IN_EVENT == _encoder(None)), + self._generate_filter_for_columns( + (ENTITY_ID_IN_EVENT, OLD_ENTITY_ID_IN_EVENT), _encoder + ).self_group(), + ) -def _glob_to_like(glob_str: str) -> ClauseList: +def _globs_to_like( + glob_strs: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] +) -> ClauseList: """Translate glob to sql.""" - return States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) + return or_( + column.like(encoder(glob_str.translate(GLOB_TO_SQL_CHARS))) + for glob_str in glob_strs + for column in columns + ) + + +def _entity_matcher( + entity_ids: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] +) -> ClauseList: + return or_( + column.in_([encoder(entity_id) for entity_id in entity_ids]) + for column in columns + ) + + +def _domain_matcher( + domains: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] +) -> ClauseList: + return or_( + column.like(encoder(f"{domain}.%")) for domain in domains for column in columns + ) diff --git a/homeassistant/components/recorder/history.py b/homeassistant/components/recorder/history.py index 845a2af62bf..7e8e97eafd4 100644 --- a/homeassistant/components/recorder/history.py +++ b/homeassistant/components/recorder/history.py @@ -236,7 +236,7 @@ def _significant_states_stmt( else: stmt += _ignore_domains_filter if filters and filters.has_config: - entity_filter = filters.entity_filter() + entity_filter = filters.states_entity_filter() stmt += lambda q: q.filter(entity_filter) stmt += lambda q: q.filter(States.last_updated > start_time) @@ -528,7 +528,7 @@ def _get_states_for_all_stmt( ) stmt += _ignore_domains_filter if filters and filters.has_config: - entity_filter = filters.entity_filter() + entity_filter = filters.states_entity_filter() stmt += lambda q: q.filter(entity_filter) if join_attributes: stmt += lambda q: q.outerjoin( diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index 90c2e5e5616..dff8edde79f 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -1,6 +1,7 @@ """Models for SQLAlchemy.""" from __future__ import annotations +from collections.abc import Callable from datetime import datetime, timedelta import json import logging @@ -9,6 +10,7 @@ from typing import Any, TypedDict, cast, overload import ciso8601 from fnvhash import fnv1a_32 from sqlalchemy import ( + JSON, BigInteger, Boolean, Column, @@ -22,11 +24,12 @@ from sqlalchemy import ( String, Text, distinct, + type_coerce, ) from sqlalchemy.dialects import mysql, oracle, postgresql, sqlite from sqlalchemy.engine.row import Row from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.orm import declarative_base, relationship +from sqlalchemy.orm import aliased, declarative_base, relationship from sqlalchemy.orm.session import Session from homeassistant.components.websocket_api.const import ( @@ -119,6 +122,21 @@ DOUBLE_TYPE = ( .with_variant(oracle.DOUBLE_PRECISION(), "oracle") .with_variant(postgresql.DOUBLE_PRECISION(), "postgresql") ) + + +class JSONLiteral(JSON): # type: ignore[misc] + """Teach SA how to literalize json.""" + + def literal_processor(self, dialect: str) -> Callable[[Any], str]: + """Processor to convert a value to JSON.""" + + def process(value: Any) -> str: + """Dump json.""" + return json.dumps(value) + + return process + + EVENT_ORIGIN_ORDER = [EventOrigin.local, EventOrigin.remote] EVENT_ORIGIN_TO_IDX = {origin: idx for idx, origin in enumerate(EVENT_ORIGIN_ORDER)} @@ -612,6 +630,26 @@ class StatisticsRuns(Base): # type: ignore[misc,valid-type] ) +EVENT_DATA_JSON = type_coerce( + EventData.shared_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) +) +OLD_FORMAT_EVENT_DATA_JSON = type_coerce( + Events.event_data.cast(JSONB_VARIENT_CAST), JSONLiteral(none_as_null=True) +) + +SHARED_ATTRS_JSON = type_coerce( + StateAttributes.shared_attrs.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) +OLD_FORMAT_ATTRS_JSON = type_coerce( + States.attributes.cast(JSON_VARIENT_CAST), JSON(none_as_null=True) +) + +ENTITY_ID_IN_EVENT: Column = EVENT_DATA_JSON["entity_id"] +OLD_ENTITY_ID_IN_EVENT: Column = OLD_FORMAT_EVENT_DATA_JSON["entity_id"] +DEVICE_ID_IN_EVENT: Column = EVENT_DATA_JSON["device_id"] +OLD_STATE = aliased(States, name="old_state") + + @overload def process_timestamp(ts: None) -> None: ... diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 101fb74e690..2903f29f5dc 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -510,7 +510,7 @@ async def test_exclude_described_event(hass, hass_client, recorder_mock): return { "name": "Test Name", "message": "tested a message", - "entity_id": event.data.get(ATTR_ENTITY_ID), + "entity_id": event.data[ATTR_ENTITY_ID], } def async_describe_events(hass, async_describe_event): @@ -2003,13 +2003,12 @@ async def test_include_events_domain_glob(hass, hass_client, recorder_mock): ) await async_recorder_block_till_done(hass) - # Should get excluded by domain hass.bus.async_fire( logbook.EVENT_LOGBOOK_ENTRY, { logbook.ATTR_NAME: "Alarm", logbook.ATTR_MESSAGE: "is triggered", - logbook.ATTR_DOMAIN: "switch", + logbook.ATTR_ENTITY_ID: "switch.any", }, ) hass.bus.async_fire(EVENT_HOMEASSISTANT_START) diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 8706ccf7617..02fea4f980f 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -14,16 +14,21 @@ from homeassistant.components.logbook import websocket_api from homeassistant.components.script import EVENT_SCRIPT_STARTED from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.const import ( + ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, ATTR_NAME, ATTR_UNIT_OF_MEASUREMENT, + CONF_DOMAINS, + CONF_ENTITIES, + CONF_EXCLUDE, EVENT_HOMEASSISTANT_START, STATE_OFF, STATE_ON, ) from homeassistant.core import Event, HomeAssistant, State from homeassistant.helpers import device_registry +from homeassistant.helpers.entityfilter import CONF_ENTITY_GLOBS from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -457,6 +462,186 @@ async def test_get_events_with_device_ids(hass, hass_ws_client, recorder_mock): assert isinstance(results[3]["when"], float) +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_excluded_entities( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with excluded entities.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "automation", "script") + ] + ) + await async_setup_component( + hass, + logbook.DOMAIN, + { + logbook.DOMAIN: { + CONF_EXCLUDE: { + CONF_ENTITIES: ["light.exc"], + CONF_DOMAINS: ["switch"], + CONF_ENTITY_GLOBS: "*.excluded", + } + }, + }, + ) + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + hass.states.async_set("light.exc", STATE_ON) + hass.states.async_set("light.exc", STATE_OFF) + hass.states.async_set("switch.any", STATE_ON) + hass.states.async_set("switch.any", STATE_OFF) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + {"id": 7, "type": "logbook/event_stream", "start_time": now.isoformat()} + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + assert msg["event"]["start_time"] == now.timestamp() + assert msg["event"]["end_time"] > msg["event"]["start_time"] + assert msg["event"]["partial"] is True + + hass.states.async_set("light.exc", STATE_ON) + hass.states.async_set("light.exc", STATE_OFF) + hass.states.async_set("switch.any", STATE_ON) + hass.states.async_set("switch.any", STATE_OFF) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + hass.states.async_set("light.alpha", "on") + hass.states.async_set("light.alpha", "off") + alpha_off_state: State = hass.states.get("light.alpha") + hass.states.async_set("light.zulu", "on", {"color": "blue"}) + hass.states.async_set("light.zulu", "off", {"effect": "help"}) + zulu_off_state: State = hass.states.get("light.zulu") + hass.states.async_set( + "light.zulu", "on", {"effect": "help", "color": ["blue", "green"]} + ) + zulu_on_state: State = hass.states.get("light.zulu") + await hass.async_block_till_done() + + hass.states.async_remove("light.zulu") + await hass.async_block_till_done() + + hass.states.async_set("light.zulu", "on", {"effect": "help", "color": "blue"}) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [ + { + "entity_id": "light.alpha", + "state": "off", + "when": alpha_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "off", + "when": zulu_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "on", + "when": zulu_on_state.last_updated.timestamp(), + }, + ] + + await async_wait_recording_done(hass) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "cover.excluded"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + { + ATTR_NAME: "Mock automation switch matching entity", + ATTR_ENTITY_ID: "switch.match_domain", + }, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation switch matching domain", ATTR_DOMAIN: "switch"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation matches nothing"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "light.keep"}, + ) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + await hass.async_block_till_done() + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + { + "context_id": ANY, + "domain": "automation", + "entity_id": None, + "message": "triggered", + "name": "Mock automation matches nothing", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": "light.keep", + "message": "triggered", + "name": "Mock automation 3", + "source": None, + "when": ANY, + }, + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) async def test_subscribe_unsubscribe_logbook_stream( hass, recorder_mock, hass_ws_client From 180b5cd2bb1c077e52231951b57b3ec871da1c5c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 25 May 2022 17:02:21 -1000 Subject: [PATCH 846/930] Fix flux_led taking a long time to recover after offline (#72507) --- homeassistant/components/flux_led/__init__.py | 21 +++++- .../components/flux_led/config_flow.py | 14 +++- homeassistant/components/flux_led/const.py | 2 + .../components/flux_led/coordinator.py | 5 +- .../components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/flux_led/test_init.py | 70 ++++++++++++++++++- 8 files changed, 110 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index 17dc28a5edf..e6c1393154a 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -15,7 +15,10 @@ from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STARTED, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr, entity_registry as er -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) from homeassistant.helpers.event import ( async_track_time_change, async_track_time_interval, @@ -27,6 +30,7 @@ from .const import ( DISCOVER_SCAN_TIMEOUT, DOMAIN, FLUX_LED_DISCOVERY, + FLUX_LED_DISCOVERY_SIGNAL, FLUX_LED_EXCEPTIONS, SIGNAL_STATE_UPDATED, STARTUP_SCAN_TIMEOUT, @@ -196,6 +200,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # to avoid a race condition where the add_update_listener is not # in place in time for the check in async_update_entry_from_discovery entry.async_on_unload(entry.add_update_listener(_async_update_listener)) + + async def _async_handle_discovered_device() -> None: + """Handle device discovery.""" + # Force a refresh if the device is now available + if not coordinator.last_update_success: + coordinator.force_next_update = True + await coordinator.async_refresh() + + entry.async_on_unload( + async_dispatcher_connect( + hass, + FLUX_LED_DISCOVERY_SIGNAL.format(entry_id=entry.entry_id), + _async_handle_discovered_device, + ) + ) return True diff --git a/homeassistant/components/flux_led/config_flow.py b/homeassistant/components/flux_led/config_flow.py index dfb6ff4a174..61395d744b3 100644 --- a/homeassistant/components/flux_led/config_flow.py +++ b/homeassistant/components/flux_led/config_flow.py @@ -21,6 +21,7 @@ from homeassistant.const import CONF_HOST from homeassistant.core import callback from homeassistant.data_entry_flow import AbortFlow, FlowResult from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import DiscoveryInfoType from . import async_wifi_bulb_for_host @@ -31,6 +32,7 @@ from .const import ( DEFAULT_EFFECT_SPEED, DISCOVER_SCAN_TIMEOUT, DOMAIN, + FLUX_LED_DISCOVERY_SIGNAL, FLUX_LED_EXCEPTIONS, TRANSITION_GRADUAL, TRANSITION_JUMP, @@ -109,12 +111,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): and ":" in entry.unique_id and mac_matches_by_one(entry.unique_id, mac) ): - if async_update_entry_from_discovery( - self.hass, entry, device, None, allow_update_mac + if ( + async_update_entry_from_discovery( + self.hass, entry, device, None, allow_update_mac + ) + or entry.state == config_entries.ConfigEntryState.SETUP_RETRY ): self.hass.async_create_task( self.hass.config_entries.async_reload(entry.entry_id) ) + else: + async_dispatcher_send( + self.hass, + FLUX_LED_DISCOVERY_SIGNAL.format(entry_id=entry.entry_id), + ) raise AbortFlow("already_configured") async def _async_handle_discovery(self) -> FlowResult: diff --git a/homeassistant/components/flux_led/const.py b/homeassistant/components/flux_led/const.py index 7fa841ec77f..db545aa1e68 100644 --- a/homeassistant/components/flux_led/const.py +++ b/homeassistant/components/flux_led/const.py @@ -74,3 +74,5 @@ EFFECT_SPEED_SUPPORT_MODES: Final = {ColorMode.RGB, ColorMode.RGBW, ColorMode.RG CONF_CUSTOM_EFFECT_COLORS: Final = "custom_effect_colors" CONF_CUSTOM_EFFECT_SPEED_PCT: Final = "custom_effect_speed_pct" CONF_CUSTOM_EFFECT_TRANSITION: Final = "custom_effect_transition" + +FLUX_LED_DISCOVERY_SIGNAL = "flux_led_discovery_{entry_id}" diff --git a/homeassistant/components/flux_led/coordinator.py b/homeassistant/components/flux_led/coordinator.py index 5f2c3c097c0..5a7b3c89216 100644 --- a/homeassistant/components/flux_led/coordinator.py +++ b/homeassistant/components/flux_led/coordinator.py @@ -30,6 +30,7 @@ class FluxLedUpdateCoordinator(DataUpdateCoordinator): self.device = device self.title = entry.title self.entry = entry + self.force_next_update = False super().__init__( hass, _LOGGER, @@ -45,6 +46,8 @@ class FluxLedUpdateCoordinator(DataUpdateCoordinator): async def _async_update_data(self) -> None: """Fetch all device and sensor data from api.""" try: - await self.device.async_update() + await self.device.async_update(force=self.force_next_update) except FLUX_LED_EXCEPTIONS as ex: raise UpdateFailed(ex) from ex + finally: + self.force_next_update = False diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index d2eb4e1e2e0..7ccd708f89b 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.28.29"], + "requirements": ["flux_led==0.28.30"], "quality_scale": "platinum", "codeowners": ["@icemanch", "@bdraco"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index fbcada246a7..054f38972db 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -657,7 +657,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.2 # homeassistant.components.flux_led -flux_led==0.28.29 +flux_led==0.28.30 # homeassistant.components.homekit # homeassistant.components.recorder diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f347a7ea082..f35d8da8ec8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -466,7 +466,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.2 # homeassistant.components.flux_led -flux_led==0.28.29 +flux_led==0.28.30 # homeassistant.components.homekit # homeassistant.components.recorder diff --git a/tests/components/flux_led/test_init.py b/tests/components/flux_led/test_init.py index b0a2c5dd33b..3504dbf3bea 100644 --- a/tests/components/flux_led/test_init.py +++ b/tests/components/flux_led/test_init.py @@ -2,10 +2,11 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from unittest.mock import AsyncMock, patch import pytest +from homeassistant import config_entries from homeassistant.components import flux_led from homeassistant.components.flux_led.const import ( CONF_REMOTE_ACCESS_ENABLED, @@ -19,6 +20,8 @@ from homeassistant.const import ( CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STARTED, + STATE_ON, + STATE_UNAVAILABLE, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -27,6 +30,7 @@ from homeassistant.util.dt import utcnow from . import ( DEFAULT_ENTRY_TITLE, + DHCP_DISCOVERY, FLUX_DISCOVERY, FLUX_DISCOVERY_PARTIAL, IP_ADDRESS, @@ -113,6 +117,70 @@ async def test_config_entry_retry(hass: HomeAssistant) -> None: assert config_entry.state == ConfigEntryState.SETUP_RETRY +async def test_config_entry_retry_right_away_on_discovery(hass: HomeAssistant) -> None: + """Test discovery makes the config entry reload if its in a retry state.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data={CONF_HOST: IP_ADDRESS}, unique_id=MAC_ADDRESS + ) + config_entry.add_to_hass(hass) + with _patch_discovery(no_device=True), _patch_wifibulb(no_device=True): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.SETUP_RETRY + + with _patch_discovery(), _patch_wifibulb(): + await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=DHCP_DISCOVERY, + ) + await hass.async_block_till_done() + assert config_entry.state == ConfigEntryState.LOADED + + +async def test_coordinator_retry_right_away_on_discovery_already_setup( + hass: HomeAssistant, +) -> None: + """Test discovery makes the coordinator force poll if its already setup.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE}, + unique_id=MAC_ADDRESS, + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + with _patch_discovery(), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + + assert config_entry.state == ConfigEntryState.LOADED + + entity_id = "light.bulb_rgbcw_ddeeff" + entity_registry = er.async_get(hass) + assert entity_registry.async_get(entity_id).unique_id == MAC_ADDRESS + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + now = utcnow() + bulb.async_update = AsyncMock(side_effect=RuntimeError) + async_fire_time_changed(hass, now + timedelta(seconds=50)) + await hass.async_block_till_done() + state = hass.states.get(entity_id) + assert state.state == STATE_UNAVAILABLE + bulb.async_update = AsyncMock() + + with _patch_discovery(), _patch_wifibulb(): + await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_DHCP}, + data=DHCP_DISCOVERY, + ) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == STATE_ON + + @pytest.mark.parametrize( "discovery,title", [ From f038d0892a8fd6543f290907822cf5f1f887aac6 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 26 May 2022 03:03:43 -0400 Subject: [PATCH 847/930] Update node statistics for zwave_js device diagnostics dump (#72509) --- homeassistant/components/zwave_js/diagnostics.py | 4 +++- tests/components/zwave_js/test_diagnostics.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index 4e1abe37b1b..3372b0eeec0 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -155,6 +155,8 @@ async def async_get_device_diagnostics( node = driver.controller.nodes[node_id] entities = get_device_entities(hass, node, device) assert client.version + node_state = redact_node_state(async_redact_data(node.data, KEYS_TO_REDACT)) + node_state["statistics"] = node.statistics.data return { "versionInfo": { "driverVersion": client.version.driver_version, @@ -163,5 +165,5 @@ async def async_get_device_diagnostics( "maxSchemaVersion": client.version.max_schema_version, }, "entities": entities, - "state": redact_node_state(async_redact_data(node.data, KEYS_TO_REDACT)), + "state": node_state, } diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 3fe3bdfeb89..3ac3f32b45a 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -92,7 +92,16 @@ async def test_device_diagnostics( assert len(diagnostics_data["entities"]) == len( list(async_discover_node_values(multisensor_6, device, {device.id: set()})) ) - assert diagnostics_data["state"] == multisensor_6.data + assert diagnostics_data["state"] == { + **multisensor_6.data, + "statistics": { + "commandsDroppedRX": 0, + "commandsDroppedTX": 0, + "commandsRX": 0, + "commandsTX": 0, + "timeoutResponse": 0, + }, + } async def test_device_diagnostics_error(hass, integration): From fa98b7e136b3af53906c8197825eb1630fc638b1 Mon Sep 17 00:00:00 2001 From: jack5mikemotown <72000916+jack5mikemotown@users.noreply.github.com> Date: Thu, 26 May 2022 16:01:23 -0400 Subject: [PATCH 848/930] Fix Google Assistant brightness calculation (#72514) Co-authored-by: Paulus Schoutsen --- homeassistant/components/google_assistant/trait.py | 4 ++-- tests/components/google_assistant/test_google_assistant.py | 2 +- tests/components/google_assistant/test_smart_home.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/google_assistant/trait.py b/homeassistant/components/google_assistant/trait.py index 20191c61668..42fc43197ea 100644 --- a/homeassistant/components/google_assistant/trait.py +++ b/homeassistant/components/google_assistant/trait.py @@ -249,7 +249,7 @@ class BrightnessTrait(_Trait): if domain == light.DOMAIN: brightness = self.state.attributes.get(light.ATTR_BRIGHTNESS) if brightness is not None: - response["brightness"] = int(100 * (brightness / 255)) + response["brightness"] = round(100 * (brightness / 255)) else: response["brightness"] = 0 @@ -1948,7 +1948,7 @@ class VolumeTrait(_Trait): level = self.state.attributes.get(media_player.ATTR_MEDIA_VOLUME_LEVEL) if level is not None: # Convert 0.0-1.0 to 0-100 - response["currentVolume"] = int(level * 100) + response["currentVolume"] = round(level * 100) muted = self.state.attributes.get(media_player.ATTR_MEDIA_VOLUME_MUTED) if muted is not None: diff --git a/tests/components/google_assistant/test_google_assistant.py b/tests/components/google_assistant/test_google_assistant.py index 8bf0e5573b2..e8a2603cae3 100644 --- a/tests/components/google_assistant/test_google_assistant.py +++ b/tests/components/google_assistant/test_google_assistant.py @@ -225,7 +225,7 @@ async def test_query_request(hass_fixture, assistant_client, auth_header): assert len(devices) == 4 assert devices["light.bed_light"]["on"] is False assert devices["light.ceiling_lights"]["on"] is True - assert devices["light.ceiling_lights"]["brightness"] == 70 + assert devices["light.ceiling_lights"]["brightness"] == 71 assert devices["light.ceiling_lights"]["color"]["temperatureK"] == 2631 assert devices["light.kitchen_lights"]["color"]["spectrumHsv"] == { "hue": 345, diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index c3bbd9336f4..4b11910999a 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -385,7 +385,7 @@ async def test_query_message(hass): "light.another_light": { "on": True, "online": True, - "brightness": 30, + "brightness": 31, "color": { "spectrumHsv": { "hue": 180, @@ -1510,7 +1510,7 @@ async def test_query_recover(hass, caplog): "payload": { "devices": { "light.bad": {"online": False}, - "light.good": {"on": True, "online": True, "brightness": 19}, + "light.good": {"on": True, "online": True, "brightness": 20}, } }, } From e1ba0717e21e3336179cf2670aa2c5fc1b4b877b Mon Sep 17 00:00:00 2001 From: Marcio Granzotto Rodrigues Date: Thu, 26 May 2022 01:12:43 -0300 Subject: [PATCH 849/930] Fix bond device state with v3 firmwares (#72516) --- homeassistant/components/bond/__init__.py | 2 +- homeassistant/components/bond/button.py | 2 +- homeassistant/components/bond/config_flow.py | 2 +- homeassistant/components/bond/cover.py | 2 +- homeassistant/components/bond/entity.py | 10 +++-- homeassistant/components/bond/fan.py | 2 +- homeassistant/components/bond/light.py | 2 +- homeassistant/components/bond/manifest.json | 4 +- homeassistant/components/bond/switch.py | 2 +- homeassistant/components/bond/utils.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bond/common.py | 2 +- tests/components/bond/test_button.py | 2 +- tests/components/bond/test_cover.py | 2 +- tests/components/bond/test_entity.py | 42 +++++++++++++++----- tests/components/bond/test_fan.py | 2 +- tests/components/bond/test_init.py | 2 +- tests/components/bond/test_light.py | 2 +- tests/components/bond/test_switch.py | 2 +- 20 files changed, 59 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/bond/__init__.py b/homeassistant/components/bond/__init__.py index 062c1d844c4..557e68272c2 100644 --- a/homeassistant/components/bond/__init__.py +++ b/homeassistant/components/bond/__init__.py @@ -5,7 +5,7 @@ import logging from typing import Any from aiohttp import ClientError, ClientResponseError, ClientTimeout -from bond_api import Bond, BPUPSubscriptions, start_bpup +from bond_async import Bond, BPUPSubscriptions, start_bpup from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( diff --git a/homeassistant/components/bond/button.py b/homeassistant/components/bond/button.py index 0152bedde23..0465e4c51fe 100644 --- a/homeassistant/components/bond/button.py +++ b/homeassistant/components/bond/button.py @@ -5,7 +5,7 @@ from dataclasses import dataclass import logging from typing import Any -from bond_api import Action, BPUPSubscriptions +from bond_async import Action, BPUPSubscriptions from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.config_entries import ConfigEntry diff --git a/homeassistant/components/bond/config_flow.py b/homeassistant/components/bond/config_flow.py index d3a7b4adf72..6eba9897468 100644 --- a/homeassistant/components/bond/config_flow.py +++ b/homeassistant/components/bond/config_flow.py @@ -6,7 +6,7 @@ import logging from typing import Any from aiohttp import ClientConnectionError, ClientResponseError -from bond_api import Bond +from bond_async import Bond import voluptuous as vol from homeassistant import config_entries, exceptions diff --git a/homeassistant/components/bond/cover.py b/homeassistant/components/bond/cover.py index a50f7b93bbb..3938de0d4bd 100644 --- a/homeassistant/components/bond/cover.py +++ b/homeassistant/components/bond/cover.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any -from bond_api import Action, BPUPSubscriptions, DeviceType +from bond_async import Action, BPUPSubscriptions, DeviceType from homeassistant.components.cover import ( ATTR_POSITION, diff --git a/homeassistant/components/bond/entity.py b/homeassistant/components/bond/entity.py index 583f1cd96f7..832e9b5d464 100644 --- a/homeassistant/components/bond/entity.py +++ b/homeassistant/components/bond/entity.py @@ -8,7 +8,7 @@ import logging from typing import Any from aiohttp import ClientError -from bond_api import BPUPSubscriptions +from bond_async import BPUPSubscriptions from homeassistant.const import ( ATTR_HW_VERSION, @@ -156,9 +156,13 @@ class BondEntity(Entity): self._apply_state(state) @callback - def _async_bpup_callback(self, state: dict) -> None: + def _async_bpup_callback(self, json_msg: dict) -> None: """Process a state change from BPUP.""" - self._async_state_callback(state) + topic = json_msg["t"] + if topic != f"devices/{self._device_id}/state": + return + + self._async_state_callback(json_msg["b"]) self.async_write_ha_state() async def async_added_to_hass(self) -> None: diff --git a/homeassistant/components/bond/fan.py b/homeassistant/components/bond/fan.py index 9acc7874657..f2f6b15f923 100644 --- a/homeassistant/components/bond/fan.py +++ b/homeassistant/components/bond/fan.py @@ -6,7 +6,7 @@ import math from typing import Any from aiohttp.client_exceptions import ClientResponseError -from bond_api import Action, BPUPSubscriptions, DeviceType, Direction +from bond_async import Action, BPUPSubscriptions, DeviceType, Direction import voluptuous as vol from homeassistant.components.fan import ( diff --git a/homeassistant/components/bond/light.py b/homeassistant/components/bond/light.py index c0c3fc428b8..55084f37b03 100644 --- a/homeassistant/components/bond/light.py +++ b/homeassistant/components/bond/light.py @@ -5,7 +5,7 @@ import logging from typing import Any from aiohttp.client_exceptions import ClientResponseError -from bond_api import Action, BPUPSubscriptions, DeviceType +from bond_async import Action, BPUPSubscriptions, DeviceType import voluptuous as vol from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity diff --git a/homeassistant/components/bond/manifest.json b/homeassistant/components/bond/manifest.json index 187602057c0..52e9dd1763f 100644 --- a/homeassistant/components/bond/manifest.json +++ b/homeassistant/components/bond/manifest.json @@ -3,10 +3,10 @@ "name": "Bond", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/bond", - "requirements": ["bond-api==0.1.18"], + "requirements": ["bond-async==0.1.20"], "zeroconf": ["_bond._tcp.local."], "codeowners": ["@bdraco", "@prystupa", "@joshs85", "@marciogranzotto"], "quality_scale": "platinum", "iot_class": "local_push", - "loggers": ["bond_api"] + "loggers": ["bond_async"] } diff --git a/homeassistant/components/bond/switch.py b/homeassistant/components/bond/switch.py index 01c224d8307..da0b19dd9ff 100644 --- a/homeassistant/components/bond/switch.py +++ b/homeassistant/components/bond/switch.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any from aiohttp.client_exceptions import ClientResponseError -from bond_api import Action, BPUPSubscriptions, DeviceType +from bond_async import Action, BPUPSubscriptions, DeviceType import voluptuous as vol from homeassistant.components.switch import SwitchEntity diff --git a/homeassistant/components/bond/utils.py b/homeassistant/components/bond/utils.py index fc78c5758c1..cba213d9450 100644 --- a/homeassistant/components/bond/utils.py +++ b/homeassistant/components/bond/utils.py @@ -5,7 +5,7 @@ import logging from typing import Any, cast from aiohttp import ClientResponseError -from bond_api import Action, Bond +from bond_async import Action, Bond from homeassistant.util.async_ import gather_with_concurrency diff --git a/requirements_all.txt b/requirements_all.txt index 054f38972db..2404e4331a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -417,7 +417,7 @@ blockchain==1.4.4 # bluepy==1.3.0 # homeassistant.components.bond -bond-api==0.1.18 +bond-async==0.1.20 # homeassistant.components.bosch_shc boschshcpy==0.2.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f35d8da8ec8..0153a1b3323 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -318,7 +318,7 @@ blebox_uniapi==1.3.3 blinkpy==0.19.0 # homeassistant.components.bond -bond-api==0.1.18 +bond-async==0.1.20 # homeassistant.components.bosch_shc boschshcpy==0.2.30 diff --git a/tests/components/bond/common.py b/tests/components/bond/common.py index 9c53c0afb8b..4b45a4016c0 100644 --- a/tests/components/bond/common.py +++ b/tests/components/bond/common.py @@ -8,7 +8,7 @@ from typing import Any from unittest.mock import MagicMock, patch from aiohttp.client_exceptions import ClientResponseError -from bond_api import DeviceType +from bond_async import DeviceType from homeassistant import core from homeassistant.components.bond.const import DOMAIN as BOND_DOMAIN diff --git a/tests/components/bond/test_button.py b/tests/components/bond/test_button.py index ee6e98b8462..4411b25657b 100644 --- a/tests/components/bond/test_button.py +++ b/tests/components/bond/test_button.py @@ -1,6 +1,6 @@ """Tests for the Bond button device.""" -from bond_api import Action, DeviceType +from bond_async import Action, DeviceType from homeassistant import core from homeassistant.components.bond.button import STEP_SIZE diff --git a/tests/components/bond/test_cover.py b/tests/components/bond/test_cover.py index ca467d4a38d..ccb44402a3e 100644 --- a/tests/components/bond/test_cover.py +++ b/tests/components/bond/test_cover.py @@ -1,7 +1,7 @@ """Tests for the Bond cover device.""" from datetime import timedelta -from bond_api import Action, DeviceType +from bond_async import Action, DeviceType from homeassistant import core from homeassistant.components.cover import ( diff --git a/tests/components/bond/test_entity.py b/tests/components/bond/test_entity.py index 122e9c2f04e..9245f4513ed 100644 --- a/tests/components/bond/test_entity.py +++ b/tests/components/bond/test_entity.py @@ -3,7 +3,8 @@ import asyncio from datetime import timedelta from unittest.mock import patch -from bond_api import BPUPSubscriptions, DeviceType +from bond_async import BPUPSubscriptions, DeviceType +from bond_async.bpup import BPUP_ALIVE_TIMEOUT from homeassistant import core from homeassistant.components import fan @@ -44,24 +45,47 @@ async def test_bpup_goes_offline_and_recovers_same_entity(hass: core.HomeAssista bpup_subs.notify( { "s": 200, - "t": "bond/test-device-id/update", + "t": "devices/test-device-id/state", "b": {"power": 1, "speed": 3, "direction": 0}, } ) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 100 + # Send a message for the wrong device to make sure its ignored + # we should never get this callback + bpup_subs.notify( + { + "s": 200, + "t": "devices/other-device-id/state", + "b": {"power": 1, "speed": 1, "direction": 0}, + } + ) + await hass.async_block_till_done() + assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 100 + + # Test we ignore messages for the wrong topic + bpup_subs.notify( + { + "s": 200, + "t": "devices/test-device-id/other_topic", + "b": {"power": 1, "speed": 1, "direction": 0}, + } + ) + await hass.async_block_till_done() + assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 100 + bpup_subs.notify( { "s": 200, - "t": "bond/test-device-id/update", + "t": "devices/test-device-id/state", "b": {"power": 1, "speed": 1, "direction": 0}, } ) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 33 - bpup_subs.last_message_time = 0 + bpup_subs.last_message_time = -BPUP_ALIVE_TIMEOUT with patch_bond_device_state(side_effect=asyncio.TimeoutError): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() @@ -75,7 +99,7 @@ async def test_bpup_goes_offline_and_recovers_same_entity(hass: core.HomeAssista bpup_subs.notify( { "s": 200, - "t": "bond/test-device-id/update", + "t": "devices/test-device-id/state", "b": {"power": 1, "speed": 2, "direction": 0}, } ) @@ -106,7 +130,7 @@ async def test_bpup_goes_offline_and_recovers_different_entity( bpup_subs.notify( { "s": 200, - "t": "bond/test-device-id/update", + "t": "devices/test-device-id/state", "b": {"power": 1, "speed": 3, "direction": 0}, } ) @@ -116,14 +140,14 @@ async def test_bpup_goes_offline_and_recovers_different_entity( bpup_subs.notify( { "s": 200, - "t": "bond/test-device-id/update", + "t": "devices/test-device-id/state", "b": {"power": 1, "speed": 1, "direction": 0}, } ) await hass.async_block_till_done() assert hass.states.get("fan.name_1").attributes[fan.ATTR_PERCENTAGE] == 33 - bpup_subs.last_message_time = 0 + bpup_subs.last_message_time = -BPUP_ALIVE_TIMEOUT with patch_bond_device_state(side_effect=asyncio.TimeoutError): async_fire_time_changed(hass, utcnow() + timedelta(seconds=230)) await hass.async_block_till_done() @@ -133,7 +157,7 @@ async def test_bpup_goes_offline_and_recovers_different_entity( bpup_subs.notify( { "s": 200, - "t": "bond/not-this-device-id/update", + "t": "devices/not-this-device-id/state", "b": {"power": 1, "speed": 2, "direction": 0}, } ) diff --git a/tests/components/bond/test_fan.py b/tests/components/bond/test_fan.py index 061e94595bf..7c860e68efc 100644 --- a/tests/components/bond/test_fan.py +++ b/tests/components/bond/test_fan.py @@ -4,7 +4,7 @@ from __future__ import annotations from datetime import timedelta from unittest.mock import call -from bond_api import Action, DeviceType, Direction +from bond_async import Action, DeviceType, Direction import pytest from homeassistant import core diff --git a/tests/components/bond/test_init.py b/tests/components/bond/test_init.py index 88615d98122..03eb490b65e 100644 --- a/tests/components/bond/test_init.py +++ b/tests/components/bond/test_init.py @@ -3,7 +3,7 @@ import asyncio from unittest.mock import MagicMock, Mock from aiohttp import ClientConnectionError, ClientResponseError -from bond_api import DeviceType +from bond_async import DeviceType import pytest from homeassistant.components.bond.const import DOMAIN diff --git a/tests/components/bond/test_light.py b/tests/components/bond/test_light.py index 6556c25efe2..c7d8f195423 100644 --- a/tests/components/bond/test_light.py +++ b/tests/components/bond/test_light.py @@ -1,7 +1,7 @@ """Tests for the Bond light device.""" from datetime import timedelta -from bond_api import Action, DeviceType +from bond_async import Action, DeviceType import pytest from homeassistant import core diff --git a/tests/components/bond/test_switch.py b/tests/components/bond/test_switch.py index 619eac69e71..b63bad2d431 100644 --- a/tests/components/bond/test_switch.py +++ b/tests/components/bond/test_switch.py @@ -1,7 +1,7 @@ """Tests for the Bond switch device.""" from datetime import timedelta -from bond_api import Action, DeviceType +from bond_async import Action, DeviceType import pytest from homeassistant import core From 3be5a354c0e860d2a9d829007e2b04c3c1e7718f Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 26 May 2022 15:17:44 -0400 Subject: [PATCH 850/930] Fix jitter in nzbget uptime sensor (#72518) --- homeassistant/components/nzbget/sensor.py | 30 +++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/nzbget/sensor.py b/homeassistant/components/nzbget/sensor.py index 9e5bd6e4ac9..a1097389020 100644 --- a/homeassistant/components/nzbget/sensor.py +++ b/homeassistant/components/nzbget/sensor.py @@ -1,7 +1,7 @@ """Monitor the NZBGet API.""" from __future__ import annotations -from datetime import timedelta +from datetime import datetime, timedelta import logging from homeassistant.components.sensor import ( @@ -105,15 +105,16 @@ class NZBGetSensor(NZBGetEntity, SensorEntity): description: SensorEntityDescription, ) -> None: """Initialize a new NZBGet sensor.""" - self.entity_description = description - self._attr_unique_id = f"{entry_id}_{description.key}" - super().__init__( coordinator=coordinator, entry_id=entry_id, name=f"{entry_name} {description.name}", ) + self.entity_description = description + self._attr_unique_id = f"{entry_id}_{description.key}" + self._native_value: datetime | None = None + @property def native_value(self): """Return the state of the sensor.""" @@ -122,14 +123,17 @@ class NZBGetSensor(NZBGetEntity, SensorEntity): if value is None: _LOGGER.warning("Unable to locate value for %s", sensor_type) - return None - - if "DownloadRate" in sensor_type and value > 0: + self._native_value = None + elif "DownloadRate" in sensor_type and value > 0: # Convert download rate from Bytes/s to MBytes/s - return round(value / 2**20, 2) + self._native_value = round(value / 2**20, 2) + elif "UpTimeSec" in sensor_type and value > 0: + uptime = utcnow().replace(microsecond=0) - timedelta(seconds=value) + if not isinstance(self._attr_native_value, datetime) or abs( + uptime - self._attr_native_value + ) > timedelta(seconds=5): + self._native_value = uptime + else: + self._native_value = value - if "UpTimeSec" in sensor_type and value > 0: - uptime = utcnow() - timedelta(seconds=value) - return uptime.replace(microsecond=0) - - return value + return self._native_value From e1c39d8c4b4d1bf571ca3dbfec6bf585915abfbe Mon Sep 17 00:00:00 2001 From: j-a-n Date: Thu, 26 May 2022 13:23:49 +0200 Subject: [PATCH 851/930] Fix Moehlenhoff Alpha2 set_target_temperature and set_heat_area_mode (#72533) Fix set_target_temperature and set_heat_area_mode --- .../components/moehlenhoff_alpha2/__init__.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/moehlenhoff_alpha2/__init__.py b/homeassistant/components/moehlenhoff_alpha2/__init__.py index 93ddaa781ab..86306a56033 100644 --- a/homeassistant/components/moehlenhoff_alpha2/__init__.py +++ b/homeassistant/components/moehlenhoff_alpha2/__init__.py @@ -98,7 +98,7 @@ class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]): update_data = {"T_TARGET": target_temperature} is_cooling = self.get_cooling() - heat_area_mode = self.data[heat_area_id]["HEATAREA_MODE"] + heat_area_mode = self.data["heat_areas"][heat_area_id]["HEATAREA_MODE"] if heat_area_mode == 1: if is_cooling: update_data["T_COOL_DAY"] = target_temperature @@ -116,7 +116,7 @@ class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]): raise HomeAssistantError( "Failed to set target temperature, communication error with alpha2 base" ) from http_err - self.data[heat_area_id].update(update_data) + self.data["heat_areas"][heat_area_id].update(update_data) for update_callback in self._listeners: update_callback() @@ -141,25 +141,25 @@ class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]): "Failed to set heat area mode, communication error with alpha2 base" ) from http_err - self.data[heat_area_id]["HEATAREA_MODE"] = heat_area_mode + self.data["heat_areas"][heat_area_id]["HEATAREA_MODE"] = heat_area_mode is_cooling = self.get_cooling() if heat_area_mode == 1: if is_cooling: - self.data[heat_area_id]["T_TARGET"] = self.data[heat_area_id][ - "T_COOL_DAY" - ] + self.data["heat_areas"][heat_area_id]["T_TARGET"] = self.data[ + "heat_areas" + ][heat_area_id]["T_COOL_DAY"] else: - self.data[heat_area_id]["T_TARGET"] = self.data[heat_area_id][ - "T_HEAT_DAY" - ] + self.data["heat_areas"][heat_area_id]["T_TARGET"] = self.data[ + "heat_areas" + ][heat_area_id]["T_HEAT_DAY"] elif heat_area_mode == 2: if is_cooling: - self.data[heat_area_id]["T_TARGET"] = self.data[heat_area_id][ - "T_COOL_NIGHT" - ] + self.data["heat_areas"][heat_area_id]["T_TARGET"] = self.data[ + "heat_areas" + ][heat_area_id]["T_COOL_NIGHT"] else: - self.data[heat_area_id]["T_TARGET"] = self.data[heat_area_id][ - "T_HEAT_NIGHT" - ] + self.data["heat_areas"][heat_area_id]["T_TARGET"] = self.data[ + "heat_areas" + ][heat_area_id]["T_HEAT_NIGHT"] for update_callback in self._listeners: update_callback() From a7fc1a4d62141ff32e3214def7f09bc6c39d16ee Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 May 2022 13:16:31 -0700 Subject: [PATCH 852/930] Bumped version to 2022.6.0b1 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index e437c171c50..ada9a6bbd06 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b0" +PATCH_VERSION: Final = "0b1" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index c9b915b229a..c62390253dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b0 +version = 2022.6.0b1 url = https://www.home-assistant.io/ [options] From 370e4c53f37c3ee839a9c4299b9301f4cbf12c36 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 26 May 2022 19:20:05 -0400 Subject: [PATCH 853/930] Add logbook entries for zwave_js events (#72508) * Add logbook entries for zwave_js events * Fix test * Update homeassistant/components/zwave_js/logbook.py Co-authored-by: Martin Hjelmare * Update homeassistant/components/zwave_js/logbook.py Co-authored-by: Martin Hjelmare * Update homeassistant/components/zwave_js/logbook.py Co-authored-by: Martin Hjelmare * Update homeassistant/components/zwave_js/logbook.py Co-authored-by: Martin Hjelmare * black * Remove value updated event Co-authored-by: Martin Hjelmare --- homeassistant/components/zwave_js/__init__.py | 6 +- homeassistant/components/zwave_js/logbook.py | 115 +++++++++++++++ tests/components/zwave_js/test_events.py | 2 +- tests/components/zwave_js/test_logbook.py | 132 ++++++++++++++++++ 4 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/zwave_js/logbook.py create mode 100644 tests/components/zwave_js/test_logbook.py diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 5c583d8321f..4f5756361c8 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -297,8 +297,8 @@ async def setup_driver( # noqa: C901 if not disc_info.assumed_state: return value_updates_disc_info[disc_info.primary_value.value_id] = disc_info - # If this is the first time we found a value we want to watch for updates, - # return early + # If this is not the first time we found a value we want to watch for updates, + # return early because we only need one listener for all values. if len(value_updates_disc_info) != 1: return # add listener for value updated events @@ -503,7 +503,7 @@ async def setup_driver( # noqa: C901 elif isinstance(notification, PowerLevelNotification): event_data.update( { - ATTR_COMMAND_CLASS_NAME: "Power Level", + ATTR_COMMAND_CLASS_NAME: "Powerlevel", ATTR_TEST_NODE_ID: notification.test_node_id, ATTR_STATUS: notification.status, ATTR_ACKNOWLEDGED_FRAMES: notification.acknowledged_frames, diff --git a/homeassistant/components/zwave_js/logbook.py b/homeassistant/components/zwave_js/logbook.py new file mode 100644 index 00000000000..1fe1ff79ec6 --- /dev/null +++ b/homeassistant/components/zwave_js/logbook.py @@ -0,0 +1,115 @@ +"""Describe Z-Wave JS logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from zwave_js_server.const import CommandClass + +from homeassistant.components.logbook.const import ( + LOGBOOK_ENTRY_MESSAGE, + LOGBOOK_ENTRY_NAME, +) +from homeassistant.const import ATTR_DEVICE_ID +from homeassistant.core import Event, HomeAssistant, callback +import homeassistant.helpers.device_registry as dr + +from .const import ( + ATTR_COMMAND_CLASS, + ATTR_COMMAND_CLASS_NAME, + ATTR_DATA_TYPE, + ATTR_DIRECTION, + ATTR_EVENT_LABEL, + ATTR_EVENT_TYPE, + ATTR_LABEL, + ATTR_VALUE, + DOMAIN, + ZWAVE_JS_NOTIFICATION_EVENT, + ZWAVE_JS_VALUE_NOTIFICATION_EVENT, +) + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + dev_reg = dr.async_get(hass) + + @callback + def async_describe_zwave_js_notification_event( + event: Event, + ) -> dict[str, str]: + """Describe Z-Wave JS notification event.""" + device = dev_reg.devices[event.data[ATTR_DEVICE_ID]] + # Z-Wave JS devices always have a name + device_name = device.name_by_user or device.name + assert device_name + + command_class = event.data[ATTR_COMMAND_CLASS] + command_class_name = event.data[ATTR_COMMAND_CLASS_NAME] + + data: dict[str, str] = {LOGBOOK_ENTRY_NAME: device_name} + prefix = f"fired {command_class_name} CC 'notification' event" + + if command_class == CommandClass.NOTIFICATION: + label = event.data[ATTR_LABEL] + event_label = event.data[ATTR_EVENT_LABEL] + return { + **data, + LOGBOOK_ENTRY_MESSAGE: f"{prefix} '{label}': '{event_label}'", + } + + if command_class == CommandClass.ENTRY_CONTROL: + event_type = event.data[ATTR_EVENT_TYPE] + data_type = event.data[ATTR_DATA_TYPE] + return { + **data, + LOGBOOK_ENTRY_MESSAGE: ( + f"{prefix} for event type '{event_type}' with data type " + f"'{data_type}'" + ), + } + + if command_class == CommandClass.SWITCH_MULTILEVEL: + event_type = event.data[ATTR_EVENT_TYPE] + direction = event.data[ATTR_DIRECTION] + return { + **data, + LOGBOOK_ENTRY_MESSAGE: ( + f"{prefix} for event type '{event_type}': '{direction}'" + ), + } + + return {**data, LOGBOOK_ENTRY_MESSAGE: prefix} + + @callback + def async_describe_zwave_js_value_notification_event( + event: Event, + ) -> dict[str, str]: + """Describe Z-Wave JS value notification event.""" + device = dev_reg.devices[event.data[ATTR_DEVICE_ID]] + # Z-Wave JS devices always have a name + device_name = device.name_by_user or device.name + assert device_name + + command_class = event.data[ATTR_COMMAND_CLASS_NAME] + label = event.data[ATTR_LABEL] + value = event.data[ATTR_VALUE] + + return { + LOGBOOK_ENTRY_NAME: device_name, + LOGBOOK_ENTRY_MESSAGE: ( + f"fired {command_class} CC 'value notification' event for '{label}': " + f"'{value}'" + ), + } + + async_describe_event( + DOMAIN, ZWAVE_JS_NOTIFICATION_EVENT, async_describe_zwave_js_notification_event + ) + async_describe_event( + DOMAIN, + ZWAVE_JS_VALUE_NOTIFICATION_EVENT, + async_describe_zwave_js_value_notification_event, + ) diff --git a/tests/components/zwave_js/test_events.py b/tests/components/zwave_js/test_events.py index 72da1fcb915..19f38d4aa57 100644 --- a/tests/components/zwave_js/test_events.py +++ b/tests/components/zwave_js/test_events.py @@ -312,7 +312,7 @@ async def test_power_level_notification(hass, hank_binary_switch, integration, c node.receive_event(event) await hass.async_block_till_done() assert len(events) == 1 - assert events[0].data["command_class_name"] == "Power Level" + assert events[0].data["command_class_name"] == "Powerlevel" assert events[0].data["command_class"] == 115 assert events[0].data["test_node_id"] == 1 assert events[0].data["status"] == 0 diff --git a/tests/components/zwave_js/test_logbook.py b/tests/components/zwave_js/test_logbook.py new file mode 100644 index 00000000000..eb02c1bbdcf --- /dev/null +++ b/tests/components/zwave_js/test_logbook.py @@ -0,0 +1,132 @@ +"""The tests for Z-Wave JS logbook.""" +from zwave_js_server.const import CommandClass + +from homeassistant.components.zwave_js.const import ( + ZWAVE_JS_NOTIFICATION_EVENT, + ZWAVE_JS_VALUE_NOTIFICATION_EVENT, +) +from homeassistant.components.zwave_js.helpers import get_device_id +from homeassistant.helpers import device_registry as dr +from homeassistant.setup import async_setup_component + +from tests.components.logbook.common import MockRow, mock_humanify + + +async def test_humanifying_zwave_js_notification_event( + hass, client, lock_schlage_be469, integration +): + """Test humanifying Z-Wave JS notification events.""" + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_device( + identifiers={get_device_id(client.driver, lock_schlage_be469)} + ) + assert device + + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + + events = mock_humanify( + hass, + [ + MockRow( + ZWAVE_JS_NOTIFICATION_EVENT, + { + "device_id": device.id, + "command_class": CommandClass.NOTIFICATION.value, + "command_class_name": "Notification", + "label": "label", + "event_label": "event_label", + }, + ), + MockRow( + ZWAVE_JS_NOTIFICATION_EVENT, + { + "device_id": device.id, + "command_class": CommandClass.ENTRY_CONTROL.value, + "command_class_name": "Entry Control", + "event_type": 1, + "data_type": 2, + }, + ), + MockRow( + ZWAVE_JS_NOTIFICATION_EVENT, + { + "device_id": device.id, + "command_class": CommandClass.SWITCH_MULTILEVEL.value, + "command_class_name": "Multilevel Switch", + "event_type": 1, + "direction": "up", + }, + ), + MockRow( + ZWAVE_JS_NOTIFICATION_EVENT, + { + "device_id": device.id, + "command_class": CommandClass.POWERLEVEL.value, + "command_class_name": "Powerlevel", + }, + ), + ], + ) + + assert events[0]["name"] == "Touchscreen Deadbolt" + assert events[0]["domain"] == "zwave_js" + assert ( + events[0]["message"] + == "fired Notification CC 'notification' event 'label': 'event_label'" + ) + + assert events[1]["name"] == "Touchscreen Deadbolt" + assert events[1]["domain"] == "zwave_js" + assert ( + events[1]["message"] + == "fired Entry Control CC 'notification' event for event type '1' with data type '2'" + ) + + assert events[2]["name"] == "Touchscreen Deadbolt" + assert events[2]["domain"] == "zwave_js" + assert ( + events[2]["message"] + == "fired Multilevel Switch CC 'notification' event for event type '1': 'up'" + ) + + assert events[3]["name"] == "Touchscreen Deadbolt" + assert events[3]["domain"] == "zwave_js" + assert events[3]["message"] == "fired Powerlevel CC 'notification' event" + + +async def test_humanifying_zwave_js_value_notification_event( + hass, client, lock_schlage_be469, integration +): + """Test humanifying Z-Wave JS value notification events.""" + dev_reg = dr.async_get(hass) + device = dev_reg.async_get_device( + identifiers={get_device_id(client.driver, lock_schlage_be469)} + ) + assert device + + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + + events = mock_humanify( + hass, + [ + MockRow( + ZWAVE_JS_VALUE_NOTIFICATION_EVENT, + { + "device_id": device.id, + "command_class": CommandClass.SCENE_ACTIVATION.value, + "command_class_name": "Scene Activation", + "label": "Scene ID", + "value": "001", + }, + ), + ], + ) + + assert events[0]["name"] == "Touchscreen Deadbolt" + assert events[0]["domain"] == "zwave_js" + assert ( + events[0]["message"] + == "fired Scene Activation CC 'value notification' event for 'Scene ID': '001'" + ) From f8e300ffbe9318ff66fe977a0594ec85a97ffac7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 27 May 2022 10:31:48 -0700 Subject: [PATCH 854/930] Include provider type in auth token response (#72560) --- homeassistant/components/auth/__init__.py | 19 +++++++++++++++---- tests/components/auth/test_init.py | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/auth/__init__.py b/homeassistant/components/auth/__init__.py index 30b36a40f32..10f974faa28 100644 --- a/homeassistant/components/auth/__init__.py +++ b/homeassistant/components/auth/__init__.py @@ -19,13 +19,15 @@ Exchange the authorization code retrieved from the login flow for tokens. Return value will be the access and refresh tokens. The access token will have a limited expiration. New access tokens can be requested using the refresh -token. +token. The value ha_auth_provider will contain the auth provider type that was +used to authorize the refresh token. { "access_token": "ABCDEFGH", "expires_in": 1800, "refresh_token": "IJKLMNOPQRST", - "token_type": "Bearer" + "token_type": "Bearer", + "ha_auth_provider": "homeassistant" } ## Grant type refresh_token @@ -342,7 +344,12 @@ class TokenView(HomeAssistantView): "expires_in": int( refresh_token.access_token_expiration.total_seconds() ), - } + "ha_auth_provider": credential.auth_provider_type, + }, + headers={ + "Cache-Control": "no-store", + "Pragma": "no-cache", + }, ) async def _async_handle_refresh_token(self, hass, data, remote_addr): @@ -399,7 +406,11 @@ class TokenView(HomeAssistantView): "expires_in": int( refresh_token.access_token_expiration.total_seconds() ), - } + }, + headers={ + "Cache-Control": "no-store", + "Pragma": "no-cache", + }, ) diff --git a/tests/components/auth/test_init.py b/tests/components/auth/test_init.py index f6d0695d97d..3c90d915966 100644 --- a/tests/components/auth/test_init.py +++ b/tests/components/auth/test_init.py @@ -81,6 +81,7 @@ async def test_login_new_user_and_trying_refresh_token(hass, aiohttp_client): assert ( await hass.auth.async_validate_access_token(tokens["access_token"]) is not None ) + assert tokens["ha_auth_provider"] == "insecure_example" # Use refresh token to get more tokens. resp = await client.post( From 16ab7f2bb1ffbdf248c3a15721cfe1a24f06b278 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 27 May 2022 05:48:52 +0200 Subject: [PATCH 855/930] Update frontend to 20220526.0 (#72567) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 6c8f568c4d2..48488bc8f47 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220525.0"], + "requirements": ["home-assistant-frontend==20220526.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 5a6dc9891a6..309d4b89e9f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220525.0 +home-assistant-frontend==20220526.0 httpx==0.22.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index 2404e4331a1..c7459285fa0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220525.0 +home-assistant-frontend==20220526.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0153a1b3323..7233824a3cc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220525.0 +home-assistant-frontend==20220526.0 # homeassistant.components.home_connect homeconnect==0.7.0 From bd02c9e5b309b9d9f6078b4e26ea47f9c191e64d Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 May 2022 22:15:20 -0700 Subject: [PATCH 856/930] Attach SSL context to SMTP notify and IMAP sensor (#72568) --- .../components/imap_email_content/sensor.py | 26 ++++++++++------ homeassistant/components/smtp/notify.py | 30 +++++++++++++------ tests/components/smtp/test_notify.py | 1 + 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/imap_email_content/sensor.py b/homeassistant/components/imap_email_content/sensor.py index d0d87e0b2d5..a8bd394a159 100644 --- a/homeassistant/components/imap_email_content/sensor.py +++ b/homeassistant/components/imap_email_content/sensor.py @@ -17,12 +17,14 @@ from homeassistant.const import ( CONF_PORT, CONF_USERNAME, CONF_VALUE_TEMPLATE, + CONF_VERIFY_SSL, CONTENT_TYPE_TEXT_PLAIN, ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.util.ssl import client_context _LOGGER = logging.getLogger(__name__) @@ -46,6 +48,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_FOLDER, default="INBOX"): cv.string, + vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, } ) @@ -58,11 +61,12 @@ def setup_platform( ) -> None: """Set up the Email sensor platform.""" reader = EmailReader( - config.get(CONF_USERNAME), - config.get(CONF_PASSWORD), - config.get(CONF_SERVER), - config.get(CONF_PORT), - config.get(CONF_FOLDER), + config[CONF_USERNAME], + config[CONF_PASSWORD], + config[CONF_SERVER], + config[CONF_PORT], + config[CONF_FOLDER], + config[CONF_VERIFY_SSL], ) if (value_template := config.get(CONF_VALUE_TEMPLATE)) is not None: @@ -70,8 +74,8 @@ def setup_platform( sensor = EmailContentSensor( hass, reader, - config.get(CONF_NAME) or config.get(CONF_USERNAME), - config.get(CONF_SENDERS), + config.get(CONF_NAME) or config[CONF_USERNAME], + config[CONF_SENDERS], value_template, ) @@ -82,21 +86,25 @@ def setup_platform( class EmailReader: """A class to read emails from an IMAP server.""" - def __init__(self, user, password, server, port, folder): + def __init__(self, user, password, server, port, folder, verify_ssl): """Initialize the Email Reader.""" self._user = user self._password = password self._server = server self._port = port self._folder = folder + self._verify_ssl = verify_ssl self._last_id = None self._unread_ids = deque([]) self.connection = None def connect(self): """Login and setup the connection.""" + ssl_context = client_context() if self._verify_ssl else None try: - self.connection = imaplib.IMAP4_SSL(self._server, self._port) + self.connection = imaplib.IMAP4_SSL( + self._server, self._port, ssl_context=ssl_context + ) self.connection.login(self._user, self._password) return True except imaplib.IMAP4.error: diff --git a/homeassistant/components/smtp/notify.py b/homeassistant/components/smtp/notify.py index 7b8e2dad1ed..866d7980d08 100644 --- a/homeassistant/components/smtp/notify.py +++ b/homeassistant/components/smtp/notify.py @@ -25,10 +25,12 @@ from homeassistant.const import ( CONF_SENDER, CONF_TIMEOUT, CONF_USERNAME, + CONF_VERIFY_SSL, ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.reload import setup_reload_service import homeassistant.util.dt as dt_util +from homeassistant.util.ssl import client_context from . import DOMAIN, PLATFORMS @@ -65,6 +67,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_SENDER_NAME): cv.string, vol.Optional(CONF_DEBUG, default=DEFAULT_DEBUG): cv.boolean, + vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, } ) @@ -73,16 +76,17 @@ def get_service(hass, config, discovery_info=None): """Get the mail notification service.""" setup_reload_service(hass, DOMAIN, PLATFORMS) mail_service = MailNotificationService( - config.get(CONF_SERVER), - config.get(CONF_PORT), - config.get(CONF_TIMEOUT), - config.get(CONF_SENDER), - config.get(CONF_ENCRYPTION), + config[CONF_SERVER], + config[CONF_PORT], + config[CONF_TIMEOUT], + config[CONF_SENDER], + config[CONF_ENCRYPTION], config.get(CONF_USERNAME), config.get(CONF_PASSWORD), - config.get(CONF_RECIPIENT), + config[CONF_RECIPIENT], config.get(CONF_SENDER_NAME), - config.get(CONF_DEBUG), + config[CONF_DEBUG], + config[CONF_VERIFY_SSL], ) if mail_service.connection_is_valid(): @@ -106,6 +110,7 @@ class MailNotificationService(BaseNotificationService): recipients, sender_name, debug, + verify_ssl, ): """Initialize the SMTP service.""" self._server = server @@ -118,18 +123,25 @@ class MailNotificationService(BaseNotificationService): self.recipients = recipients self._sender_name = sender_name self.debug = debug + self._verify_ssl = verify_ssl self.tries = 2 def connect(self): """Connect/authenticate to SMTP Server.""" + ssl_context = client_context() if self._verify_ssl else None if self.encryption == "tls": - mail = smtplib.SMTP_SSL(self._server, self._port, timeout=self._timeout) + mail = smtplib.SMTP_SSL( + self._server, + self._port, + timeout=self._timeout, + context=ssl_context, + ) else: mail = smtplib.SMTP(self._server, self._port, timeout=self._timeout) mail.set_debuglevel(self.debug) mail.ehlo_or_helo_if_needed() if self.encryption == "starttls": - mail.starttls() + mail.starttls(context=ssl_context) mail.ehlo() if self.username and self.password: mail.login(self.username, self.password) diff --git a/tests/components/smtp/test_notify.py b/tests/components/smtp/test_notify.py index 38f48c169ac..ac742e10ea1 100644 --- a/tests/components/smtp/test_notify.py +++ b/tests/components/smtp/test_notify.py @@ -76,6 +76,7 @@ def message(): ["recip1@example.com", "testrecip@test.com"], "Home Assistant", 0, + True, ) yield mailer From 828afd8c0559af30674fb1e4ec008661dec9e869 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 27 May 2022 05:51:24 +0200 Subject: [PATCH 857/930] fjaraskupan: Don't set hardware filters for service id (#72569) --- homeassistant/components/fjaraskupan/__init__.py | 4 ++-- homeassistant/components/fjaraskupan/config_flow.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 488139b080b..ec4528bc079 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -9,7 +9,7 @@ import logging from bleak import BleakScanner from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData -from fjaraskupan import UUID_SERVICE, Device, State, device_filter +from fjaraskupan import DEVICE_NAME, Device, State, device_filter from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform @@ -90,7 +90,7 @@ class EntryState: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Fjäråskupan from a config entry.""" - scanner = BleakScanner(filters={"UUIDs": [str(UUID_SERVICE)]}) + scanner = BleakScanner(filters={"Pattern": DEVICE_NAME, "DuplicateData": True}) state = EntryState(scanner, {}) hass.data.setdefault(DOMAIN, {}) diff --git a/homeassistant/components/fjaraskupan/config_flow.py b/homeassistant/components/fjaraskupan/config_flow.py index da0a7f1dd2b..3af34c0eef6 100644 --- a/homeassistant/components/fjaraskupan/config_flow.py +++ b/homeassistant/components/fjaraskupan/config_flow.py @@ -7,7 +7,7 @@ import async_timeout from bleak import BleakScanner from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData -from fjaraskupan import UUID_SERVICE, device_filter +from fjaraskupan import DEVICE_NAME, device_filter from homeassistant.core import HomeAssistant from homeassistant.helpers.config_entry_flow import register_discovery_flow @@ -27,7 +27,8 @@ async def _async_has_devices(hass: HomeAssistant) -> bool: event.set() async with BleakScanner( - detection_callback=detection, filters={"UUIDs": [str(UUID_SERVICE)]} + detection_callback=detection, + filters={"Pattern": DEVICE_NAME, "DuplicateData": True}, ): try: async with async_timeout.timeout(CONST_WAIT_TIME): From 9b779082d5ffdc31a4139b85a9699d34c49dbfde Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 May 2022 17:54:26 -1000 Subject: [PATCH 858/930] Fix memory leak when firing state_changed events (#72571) --- homeassistant/components/recorder/models.py | 2 +- homeassistant/core.py | 45 +++++++++++++++++---- tests/test_core.py | 45 +++++++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index dff8edde79f..70c816c2af5 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -746,7 +746,7 @@ class LazyState(State): def context(self) -> Context: # type: ignore[override] """State context.""" if self._context is None: - self._context = Context(id=None) # type: ignore[arg-type] + self._context = Context(id=None) return self._context @context.setter diff --git a/homeassistant/core.py b/homeassistant/core.py index 2753b801347..d7cae4e411e 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -37,7 +37,6 @@ from typing import ( ) from urllib.parse import urlparse -import attr import voluptuous as vol import yarl @@ -716,14 +715,26 @@ class HomeAssistant: self._stopped.set() -@attr.s(slots=True, frozen=False) class Context: """The context that triggered something.""" - user_id: str | None = attr.ib(default=None) - parent_id: str | None = attr.ib(default=None) - id: str = attr.ib(factory=ulid_util.ulid) - origin_event: Event | None = attr.ib(default=None, eq=False) + __slots__ = ("user_id", "parent_id", "id", "origin_event") + + def __init__( + self, + user_id: str | None = None, + parent_id: str | None = None, + id: str | None = None, # pylint: disable=redefined-builtin + ) -> None: + """Init the context.""" + self.id = id or ulid_util.ulid() + self.user_id = user_id + self.parent_id = parent_id + self.origin_event: Event | None = None + + def __eq__(self, other: Any) -> bool: + """Compare contexts.""" + return bool(self.__class__ == other.__class__ and self.id == other.id) def as_dict(self) -> dict[str, str | None]: """Return a dictionary representation of the context.""" @@ -1163,6 +1174,24 @@ class State: context, ) + def expire(self) -> None: + """Mark the state as old. + + We give up the original reference to the context to ensure + the context can be garbage collected by replacing it with + a new one with the same id to ensure the old state + can still be examined for comparison against the new state. + + Since we are always going to fire a EVENT_STATE_CHANGED event + after we remove a state from the state machine we need to make + sure we don't end up holding a reference to the original context + since it can never be garbage collected as each event would + reference the previous one. + """ + self.context = Context( + self.context.user_id, self.context.parent_id, self.context.id + ) + def __eq__(self, other: Any) -> bool: """Return the comparison of the state.""" return ( # type: ignore[no-any-return] @@ -1303,6 +1332,7 @@ class StateMachine: if old_state is None: return False + old_state.expire() self._bus.async_fire( EVENT_STATE_CHANGED, {"entity_id": entity_id, "old_state": old_state, "new_state": None}, @@ -1396,7 +1426,6 @@ class StateMachine: if context is None: context = Context(id=ulid_util.ulid(dt_util.utc_to_timestamp(now))) - state = State( entity_id, new_state, @@ -1406,6 +1435,8 @@ class StateMachine: context, old_state is None, ) + if old_state is not None: + old_state.expire() self._states[entity_id] = state self._bus.async_fire( EVENT_STATE_CHANGED, diff --git a/tests/test_core.py b/tests/test_core.py index ee1005a60b0..67513ea8b17 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,9 +6,11 @@ import array import asyncio from datetime import datetime, timedelta import functools +import gc import logging import os from tempfile import TemporaryDirectory +from typing import Any from unittest.mock import MagicMock, Mock, PropertyMock, patch import pytest @@ -1829,3 +1831,46 @@ async def test_event_context(hass): cancel2() assert dummy_event2.context.origin_event == dummy_event + + +def _get_full_name(obj) -> str: + """Get the full name of an object in memory.""" + objtype = type(obj) + name = objtype.__name__ + if module := getattr(objtype, "__module__", None): + return f"{module}.{name}" + return name + + +def _get_by_type(full_name: str) -> list[Any]: + """Get all objects in memory with a specific type.""" + return [obj for obj in gc.get_objects() if _get_full_name(obj) == full_name] + + +# The logger will hold a strong reference to the event for the life of the tests +# so we must patch it out +@pytest.mark.skipif( + not os.environ.get("DEBUG_MEMORY"), + reason="Takes too long on the CI", +) +@patch.object(ha._LOGGER, "debug", lambda *args: None) +async def test_state_changed_events_to_not_leak_contexts(hass): + """Test state changed events do not leak contexts.""" + gc.collect() + # Other tests can log Contexts which keep them in memory + # so we need to look at how many exist at the start + init_count = len(_get_by_type("homeassistant.core.Context")) + + assert len(_get_by_type("homeassistant.core.Context")) == init_count + for i in range(20): + hass.states.async_set("light.switch", str(i)) + await hass.async_block_till_done() + gc.collect() + + assert len(_get_by_type("homeassistant.core.Context")) == init_count + 2 + + hass.states.async_remove("light.switch") + await hass.async_block_till_done() + gc.collect() + + assert len(_get_by_type("homeassistant.core.Context")) == init_count From 0d03b850293836a055a726b51284b48df2c5ee82 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Fri, 27 May 2022 17:20:37 +1000 Subject: [PATCH 859/930] Bump httpx to 0.23.0 (#72573) Co-authored-by: J. Nick Koston --- homeassistant/package_constraints.txt | 6 +++--- requirements.txt | 2 +- script/gen_requirements_all.py | 4 ++-- setup.cfg | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 309d4b89e9f..f235cc3f02c 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -16,7 +16,7 @@ cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 home-assistant-frontend==20220526.0 -httpx==0.22.0 +httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 lru-dict==1.1.7 @@ -78,9 +78,9 @@ regex==2021.8.28 # these requirements are quite loose. As the entire stack has some outstanding issues, and # even newer versions seem to introduce new issues, it's useful for us to pin all these # requirements so we can directly link HA versions to these library versions. -anyio==3.5.0 +anyio==3.6.1 h11==0.12.0 -httpcore==0.14.7 +httpcore==0.15.0 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation diff --git a/requirements.txt b/requirements.txt index 0c13b9c319b..8321e70f8de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ awesomeversion==22.5.1 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 -httpx==0.22.0 +httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 PyJWT==2.4.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 369045e5124..f049080b7fa 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -94,9 +94,9 @@ regex==2021.8.28 # these requirements are quite loose. As the entire stack has some outstanding issues, and # even newer versions seem to introduce new issues, it's useful for us to pin all these # requirements so we can directly link HA versions to these library versions. -anyio==3.5.0 +anyio==3.6.1 h11==0.12.0 -httpcore==0.14.7 +httpcore==0.15.0 # Ensure we have a hyperframe version that works in Python 3.10 # 5.2.0 fixed a collections abc deprecation diff --git a/setup.cfg b/setup.cfg index c62390253dc..205fcd96086 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ install_requires = ciso8601==2.2.0 # When bumping httpx, please check the version pins of # httpcore, anyio, and h11 in gen_requirements_all - httpx==0.22.0 + httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 PyJWT==2.4.0 From a35edc67516b0d123d5be9111db9a602ff4ecf2a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 27 May 2022 02:52:24 -0700 Subject: [PATCH 860/930] Reduce the scope of the google calendar track deprecation (#72575) --- homeassistant/components/google/__init__.py | 1 - homeassistant/components/google/calendar.py | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index f034d48b9c5..b7263d2e469 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -117,7 +117,6 @@ CONFIG_SCHEMA = vol.Schema( _SINGLE_CALSEARCH_CONFIG = vol.All( cv.deprecated(CONF_MAX_RESULTS), - cv.deprecated(CONF_TRACK), vol.Schema( { vol.Required(CONF_NAME): cv.string, diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 01780702b7f..ba4368fefae 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -93,6 +93,11 @@ def _async_setup_entities( num_entities = len(disc_info[CONF_ENTITIES]) for data in disc_info[CONF_ENTITIES]: entity_enabled = data.get(CONF_TRACK, True) + if not entity_enabled: + _LOGGER.warning( + "The 'track' option in google_calendars.yaml has been deprecated. The setting " + "has been imported to the UI, and should now be removed from google_calendars.yaml" + ) entity_name = data[CONF_DEVICE_ID] entity_id = generate_entity_id(ENTITY_ID_FORMAT, entity_name, hass=hass) calendar_id = disc_info[CONF_CAL_ID] From 319275bbbd74102274390a843966d1d560dbb433 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 May 2022 22:15:43 -1000 Subject: [PATCH 861/930] Revert "Remove sqlite 3.34.1 downgrade workaround by reverting "Downgrade sqlite-libs on docker image (#55591)" (#72342)" (#72578) --- Dockerfile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Dockerfile b/Dockerfile index 13552d55a3d..1d6ce675e74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,21 @@ RUN \ -e ./homeassistant --use-deprecated=legacy-resolver \ && python3 -m compileall homeassistant/homeassistant +# Fix Bug with Alpine 3.14 and sqlite 3.35 +# https://gitlab.alpinelinux.org/alpine/aports/-/issues/12524 +ARG BUILD_ARCH +RUN \ + if [ "${BUILD_ARCH}" = "amd64" ]; then \ + export APK_ARCH=x86_64; \ + elif [ "${BUILD_ARCH}" = "i386" ]; then \ + export APK_ARCH=x86; \ + else \ + export APK_ARCH=${BUILD_ARCH}; \ + fi \ + && curl -O http://dl-cdn.alpinelinux.org/alpine/v3.13/main/${APK_ARCH}/sqlite-libs-3.34.1-r0.apk \ + && apk add --no-cache sqlite-libs-3.34.1-r0.apk \ + && rm -f sqlite-libs-3.34.1-r0.apk + # Home Assistant S6-Overlay COPY rootfs / From cc53ad12b3fbcdc491653d713906ffacaab4e5a0 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 27 May 2022 15:09:43 +0200 Subject: [PATCH 862/930] Simplify MQTT PLATFORM_CONFIG_SCHEMA_BASE (#72589) --- homeassistant/components/mqtt/__init__.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index e8847375584..78f64387435 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -190,26 +190,7 @@ MQTT_WILL_BIRTH_SCHEMA = vol.Schema( ) PLATFORM_CONFIG_SCHEMA_BASE = vol.Schema( - { - vol.Optional(Platform.ALARM_CONTROL_PANEL.value): cv.ensure_list, - vol.Optional(Platform.BINARY_SENSOR.value): cv.ensure_list, - vol.Optional(Platform.BUTTON.value): cv.ensure_list, - vol.Optional(Platform.CAMERA.value): cv.ensure_list, - vol.Optional(Platform.CLIMATE.value): cv.ensure_list, - vol.Optional(Platform.COVER.value): cv.ensure_list, - vol.Optional(Platform.DEVICE_TRACKER.value): cv.ensure_list, - vol.Optional(Platform.FAN.value): cv.ensure_list, - vol.Optional(Platform.HUMIDIFIER.value): cv.ensure_list, - vol.Optional(Platform.LIGHT.value): cv.ensure_list, - vol.Optional(Platform.LOCK.value): cv.ensure_list, - vol.Optional(Platform.NUMBER.value): cv.ensure_list, - vol.Optional(Platform.SCENE.value): cv.ensure_list, - vol.Optional(Platform.SELECT.value): cv.ensure_list, - vol.Optional(Platform.SIREN.value): cv.ensure_list, - vol.Optional(Platform.SENSOR.value): cv.ensure_list, - vol.Optional(Platform.SWITCH.value): cv.ensure_list, - vol.Optional(Platform.VACUUM.value): cv.ensure_list, - } + {vol.Optional(platform.value): cv.ensure_list for platform in PLATFORMS} ) CONFIG_SCHEMA_BASE = PLATFORM_CONFIG_SCHEMA_BASE.extend( From ad6529520160b94c45dbf0e4c2c20ec27ef79a52 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 27 May 2022 17:40:55 +0200 Subject: [PATCH 863/930] Require passing target player when resolving media (#72593) --- .../components/media_source/__init__.py | 23 +++++++++++++------ .../components/media_source/local_source.py | 4 ++-- .../components/media_source/models.py | 7 ++++-- .../components/dlna_dms/test_media_source.py | 8 +++---- tests/components/media_source/test_init.py | 19 +++++++++++++++ 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/media_source/__init__.py b/homeassistant/components/media_source/__init__.py index 3c42016f8f7..4818934d1dd 100644 --- a/homeassistant/components/media_source/__init__.py +++ b/homeassistant/components/media_source/__init__.py @@ -18,10 +18,11 @@ from homeassistant.components.media_player.browse_media import ( ) from homeassistant.components.websocket_api import ActiveConnection from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.frame import report from homeassistant.helpers.integration_platform import ( async_process_integration_platforms, ) -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import UNDEFINED, ConfigType, UndefinedType from homeassistant.loader import bind_hass from . import local_source @@ -80,15 +81,15 @@ async def _process_media_source_platform( @callback def _get_media_item( - hass: HomeAssistant, media_content_id: str | None + hass: HomeAssistant, media_content_id: str | None, target_media_player: str | None ) -> MediaSourceItem: """Return media item.""" if media_content_id: - item = MediaSourceItem.from_uri(hass, media_content_id) + item = MediaSourceItem.from_uri(hass, media_content_id, target_media_player) else: # We default to our own domain if its only one registered domain = None if len(hass.data[DOMAIN]) > 1 else DOMAIN - return MediaSourceItem(hass, domain, "") + return MediaSourceItem(hass, domain, "", target_media_player) if item.domain is not None and item.domain not in hass.data[DOMAIN]: raise ValueError("Unknown media source") @@ -108,7 +109,7 @@ async def async_browse_media( raise BrowseError("Media Source not loaded") try: - item = await _get_media_item(hass, media_content_id).async_browse() + item = await _get_media_item(hass, media_content_id, None).async_browse() except ValueError as err: raise BrowseError(str(err)) from err @@ -124,13 +125,21 @@ async def async_browse_media( @bind_hass -async def async_resolve_media(hass: HomeAssistant, media_content_id: str) -> PlayMedia: +async def async_resolve_media( + hass: HomeAssistant, + media_content_id: str, + target_media_player: str | None | UndefinedType = UNDEFINED, +) -> PlayMedia: """Get info to play media.""" if DOMAIN not in hass.data: raise Unresolvable("Media Source not loaded") + if target_media_player is UNDEFINED: + report("calls media_source.async_resolve_media without passing an entity_id") + target_media_player = None + try: - item = _get_media_item(hass, media_content_id) + item = _get_media_item(hass, media_content_id, target_media_player) except ValueError as err: raise Unresolvable(str(err)) from err diff --git a/homeassistant/components/media_source/local_source.py b/homeassistant/components/media_source/local_source.py index 89feba5317f..863380b7600 100644 --- a/homeassistant/components/media_source/local_source.py +++ b/homeassistant/components/media_source/local_source.py @@ -264,7 +264,7 @@ class UploadMediaView(http.HomeAssistantView): raise web.HTTPBadRequest() from err try: - item = MediaSourceItem.from_uri(self.hass, data["media_content_id"]) + item = MediaSourceItem.from_uri(self.hass, data["media_content_id"], None) except ValueError as err: LOGGER.error("Received invalid upload data: %s", err) raise web.HTTPBadRequest() from err @@ -328,7 +328,7 @@ async def websocket_remove_media( ) -> None: """Remove media.""" try: - item = MediaSourceItem.from_uri(hass, msg["media_content_id"]) + item = MediaSourceItem.from_uri(hass, msg["media_content_id"], None) except ValueError as err: connection.send_error(msg["id"], websocket_api.ERR_INVALID_FORMAT, str(err)) return diff --git a/homeassistant/components/media_source/models.py b/homeassistant/components/media_source/models.py index ceb57ef1fb4..0aee6ad1330 100644 --- a/homeassistant/components/media_source/models.py +++ b/homeassistant/components/media_source/models.py @@ -50,6 +50,7 @@ class MediaSourceItem: hass: HomeAssistant domain: str | None identifier: str + target_media_player: str | None async def async_browse(self) -> BrowseMediaSource: """Browse this item.""" @@ -94,7 +95,9 @@ class MediaSourceItem: return cast(MediaSource, self.hass.data[DOMAIN][self.domain]) @classmethod - def from_uri(cls, hass: HomeAssistant, uri: str) -> MediaSourceItem: + def from_uri( + cls, hass: HomeAssistant, uri: str, target_media_player: str | None + ) -> MediaSourceItem: """Create an item from a uri.""" if not (match := URI_SCHEME_REGEX.match(uri)): raise ValueError("Invalid media source URI") @@ -102,7 +105,7 @@ class MediaSourceItem: domain = match.group("domain") identifier = match.group("identifier") - return cls(hass, domain, identifier) + return cls(hass, domain, identifier, target_media_player) class MediaSource(ABC): diff --git a/tests/components/dlna_dms/test_media_source.py b/tests/components/dlna_dms/test_media_source.py index f2c3011e274..5f76b061590 100644 --- a/tests/components/dlna_dms/test_media_source.py +++ b/tests/components/dlna_dms/test_media_source.py @@ -49,7 +49,7 @@ async def test_get_media_source(hass: HomeAssistant) -> None: async def test_resolve_media_unconfigured(hass: HomeAssistant) -> None: """Test resolve_media without any devices being configured.""" source = DmsMediaSource(hass) - item = MediaSourceItem(hass, DOMAIN, "source_id/media_id") + item = MediaSourceItem(hass, DOMAIN, "source_id/media_id", None) with pytest.raises(Unresolvable, match="No sources have been configured"): await source.async_resolve_media(item) @@ -116,11 +116,11 @@ async def test_resolve_media_success( async def test_browse_media_unconfigured(hass: HomeAssistant) -> None: """Test browse_media without any devices being configured.""" source = DmsMediaSource(hass) - item = MediaSourceItem(hass, DOMAIN, "source_id/media_id") + item = MediaSourceItem(hass, DOMAIN, "source_id/media_id", None) with pytest.raises(BrowseError, match="No sources have been configured"): await source.async_browse_media(item) - item = MediaSourceItem(hass, DOMAIN, "") + item = MediaSourceItem(hass, DOMAIN, "", None) with pytest.raises(BrowseError, match="No sources have been configured"): await source.async_browse_media(item) @@ -239,7 +239,7 @@ async def test_browse_media_source_id( dms_device_mock.async_browse_metadata.side_effect = UpnpError # Browse by source_id - item = MediaSourceItem(hass, DOMAIN, f"{MOCK_SOURCE_ID}/:media-item-id") + item = MediaSourceItem(hass, DOMAIN, f"{MOCK_SOURCE_ID}/:media-item-id", None) dms_source = DmsMediaSource(hass) with pytest.raises(BrowseError): await dms_source.async_browse_media(item) diff --git a/tests/components/media_source/test_init.py b/tests/components/media_source/test_init.py index 491b1972cb6..f2a8ff13533 100644 --- a/tests/components/media_source/test_init.py +++ b/tests/components/media_source/test_init.py @@ -109,6 +109,25 @@ async def test_async_resolve_media(hass): assert media.mime_type == "audio/mpeg" +@patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()) +async def test_async_resolve_media_no_entity(hass, caplog): + """Test browse media.""" + assert await async_setup_component(hass, media_source.DOMAIN, {}) + await hass.async_block_till_done() + + media = await media_source.async_resolve_media( + hass, + media_source.generate_media_source_id(media_source.DOMAIN, "local/test.mp3"), + ) + assert isinstance(media, media_source.models.PlayMedia) + assert media.url == "/media/local/test.mp3" + assert media.mime_type == "audio/mpeg" + assert ( + "calls media_source.async_resolve_media without passing an entity_id" + in caplog.text + ) + + async def test_async_unresolve_media(hass): """Test browse media.""" assert await async_setup_component(hass, media_source.DOMAIN, {}) From 087c0b59edb4f6233849e2cf6eb9057474251934 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 27 May 2022 18:05:06 +0200 Subject: [PATCH 864/930] Update integrations to pass target player when resolving media (#72597) --- .../components/apple_tv/media_player.py | 4 ++- .../components/bluesound/media_player.py | 4 ++- homeassistant/components/cast/media_player.py | 4 ++- .../components/dlna_dmr/media_player.py | 4 ++- .../components/esphome/media_player.py | 4 ++- .../components/forked_daapd/media_player.py | 4 ++- .../components/gstreamer/media_player.py | 4 ++- homeassistant/components/heos/media_player.py | 4 ++- homeassistant/components/kodi/media_player.py | 4 ++- homeassistant/components/mpd/media_player.py | 4 ++- .../components/openhome/media_player.py | 4 ++- .../panasonic_viera/media_player.py | 4 ++- homeassistant/components/roku/media_player.py | 4 ++- .../components/slimproto/media_player.py | 4 ++- .../components/sonos/media_player.py | 4 ++- .../components/soundtouch/media_player.py | 4 ++- .../components/squeezebox/media_player.py | 4 ++- .../components/unifiprotect/media_player.py | 4 ++- homeassistant/components/vlc/media_player.py | 4 ++- .../components/vlc_telnet/media_player.py | 4 ++- .../yamaha_musiccast/media_player.py | 4 ++- tests/components/camera/test_media_source.py | 10 ++++---- .../dlna_dms/test_device_availability.py | 10 ++++---- .../dlna_dms/test_dms_device_source.py | 2 +- .../components/dlna_dms/test_media_source.py | 12 ++++----- tests/components/google_translate/test_tts.py | 2 +- tests/components/marytts/test_tts.py | 2 +- tests/components/media_source/test_init.py | 11 +++++--- .../components/motioneye/test_media_source.py | 15 ++++++++--- tests/components/nest/test_media_source.py | 25 +++++++++++-------- tests/components/netatmo/test_media_source.py | 2 +- tests/components/tts/test_init.py | 2 +- tests/components/tts/test_media_source.py | 14 +++++++---- tests/components/voicerss/test_tts.py | 2 +- tests/components/yandextts/test_tts.py | 2 +- 35 files changed, 128 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index 5a7298dcbee..30a397d953c 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -284,7 +284,9 @@ class AppleTvMediaPlayer(AppleTVEntity, MediaPlayerEntity): await self.atv.apps.launch_app(media_id) if media_source.is_media_source_id(media_id): - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url media_type = MEDIA_TYPE_MUSIC diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index 32f74743972..e22606b795f 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -1032,7 +1032,9 @@ class BluesoundPlayer(MediaPlayerEntity): return if media_source.is_media_source_id(media_id): - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url media_id = async_process_play_media_url(self.hass, media_id) diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index b64c3372c15..ea21259ccc4 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -605,7 +605,9 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity): """Play a piece of media.""" # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_type = sourced_media.mime_type media_id = sourced_media.url diff --git a/homeassistant/components/dlna_dmr/media_player.py b/homeassistant/components/dlna_dmr/media_player.py index fd1fc9b2bab..9ecf9f8ad40 100644 --- a/homeassistant/components/dlna_dmr/media_player.py +++ b/homeassistant/components/dlna_dmr/media_player.py @@ -597,7 +597,9 @@ class DlnaDmrEntity(MediaPlayerEntity): # If media is media_source, resolve it to url and MIME type, and maybe metadata if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_type = sourced_media.mime_type media_id = sourced_media.url _LOGGER.debug("sourced_media is %s", sourced_media) diff --git a/homeassistant/components/esphome/media_player.py b/homeassistant/components/esphome/media_player.py index 6e83d12a427..f9027142ae2 100644 --- a/homeassistant/components/esphome/media_player.py +++ b/homeassistant/components/esphome/media_player.py @@ -95,7 +95,9 @@ class EsphomeMediaPlayer( ) -> None: """Send the play command with media url to the media player.""" if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = sourced_media.url media_id = async_process_play_media_url(self.hass, media_id) diff --git a/homeassistant/components/forked_daapd/media_player.py b/homeassistant/components/forked_daapd/media_player.py index f2c64fa81da..25695dceeb5 100644 --- a/homeassistant/components/forked_daapd/media_player.py +++ b/homeassistant/components/forked_daapd/media_player.py @@ -666,7 +666,9 @@ class ForkedDaapdMaster(MediaPlayerEntity): """Play a URI.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if media_type == MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/gstreamer/media_player.py b/homeassistant/components/gstreamer/media_player.py index 545941f2924..723be2880ff 100644 --- a/homeassistant/components/gstreamer/media_player.py +++ b/homeassistant/components/gstreamer/media_player.py @@ -96,7 +96,9 @@ class GstreamerDevice(MediaPlayerEntity): """Play media.""" # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = sourced_media.url elif media_type != MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index dabe79afb03..4cfbe5fe408 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -192,7 +192,9 @@ class HeosMediaPlayer(MediaPlayerEntity): """Play a piece of media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC): diff --git a/homeassistant/components/kodi/media_player.py b/homeassistant/components/kodi/media_player.py index cea3adcde00..e19ffc6219c 100644 --- a/homeassistant/components/kodi/media_player.py +++ b/homeassistant/components/kodi/media_player.py @@ -713,7 +713,9 @@ class KodiEntity(MediaPlayerEntity): """Send the play_media command to the media player.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url media_type_lower = media_type.lower() diff --git a/homeassistant/components/mpd/media_player.py b/homeassistant/components/mpd/media_player.py index d3262a0d5da..ecee057a653 100644 --- a/homeassistant/components/mpd/media_player.py +++ b/homeassistant/components/mpd/media_player.py @@ -453,7 +453,9 @@ class MpdDevice(MediaPlayerEntity): """Send the media player the command for playing a playlist.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = async_process_play_media_url(self.hass, play_item.url) if media_type == MEDIA_TYPE_PLAYLIST: diff --git a/homeassistant/components/openhome/media_player.py b/homeassistant/components/openhome/media_player.py index fa9cce1cfb6..b6a0b549c40 100644 --- a/homeassistant/components/openhome/media_player.py +++ b/homeassistant/components/openhome/media_player.py @@ -209,7 +209,9 @@ class OpenhomeDevice(MediaPlayerEntity): """Send the play_media command to the media player.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if media_type != MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/panasonic_viera/media_player.py b/homeassistant/components/panasonic_viera/media_player.py index fd44c2853f1..7b75809f827 100644 --- a/homeassistant/components/panasonic_viera/media_player.py +++ b/homeassistant/components/panasonic_viera/media_player.py @@ -188,7 +188,9 @@ class PanasonicVieraTVEntity(MediaPlayerEntity): """Play media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_URL - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if media_type != MEDIA_TYPE_URL: diff --git a/homeassistant/components/roku/media_player.py b/homeassistant/components/roku/media_player.py index e6fe0d7dcf5..a47432694dd 100644 --- a/homeassistant/components/roku/media_player.py +++ b/homeassistant/components/roku/media_player.py @@ -384,7 +384,9 @@ class RokuMediaPlayer(RokuEntity, MediaPlayerEntity): # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_type = MEDIA_TYPE_URL media_id = sourced_media.url mime_type = sourced_media.mime_type diff --git a/homeassistant/components/slimproto/media_player.py b/homeassistant/components/slimproto/media_player.py index 6b1989830e2..2f85aa4b9df 100644 --- a/homeassistant/components/slimproto/media_player.py +++ b/homeassistant/components/slimproto/media_player.py @@ -180,7 +180,9 @@ class SlimProtoPlayer(MediaPlayerEntity): to_send_media_type: str | None = media_type # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = sourced_media.url to_send_media_type = sourced_media.mime_type diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 95834938953..45e11a810ae 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -551,7 +551,9 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): media_type = MEDIA_TYPE_MUSIC media_id = ( run_coroutine_threadsafe( - media_source.async_resolve_media(self.hass, media_id), + media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ), self.hass.loop, ) .result() diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py index 3172eb4aed6..7c9ade3bee1 100644 --- a/homeassistant/components/soundtouch/media_player.py +++ b/homeassistant/components/soundtouch/media_player.py @@ -357,7 +357,9 @@ class SoundTouchDevice(MediaPlayerEntity): async def async_play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" if media_source.is_media_source_id(media_id): - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = async_process_play_media_url(self.hass, play_item.url) await self.hass.async_add_executor_job( diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index bd1f29f4e69..d0d1cf89739 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -482,7 +482,9 @@ class SqueezeBoxEntity(MediaPlayerEntity): if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if media_type in MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/unifiprotect/media_player.py b/homeassistant/components/unifiprotect/media_player.py index 0b7c2a2f60d..1acd14be130 100644 --- a/homeassistant/components/unifiprotect/media_player.py +++ b/homeassistant/components/unifiprotect/media_player.py @@ -118,7 +118,9 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity): """Play a piece of media.""" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = async_process_play_media_url(self.hass, play_item.url) if media_type != MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/vlc/media_player.py b/homeassistant/components/vlc/media_player.py index 7312eacd1c6..88b663e09c6 100644 --- a/homeassistant/components/vlc/media_player.py +++ b/homeassistant/components/vlc/media_player.py @@ -168,7 +168,9 @@ class VlcDevice(MediaPlayerEntity): """Play media from a URL or file.""" # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = sourced_media.url elif media_type != MEDIA_TYPE_MUSIC: diff --git a/homeassistant/components/vlc_telnet/media_player.py b/homeassistant/components/vlc_telnet/media_player.py index 89fa1a3c323..75305acbb0c 100644 --- a/homeassistant/components/vlc_telnet/media_player.py +++ b/homeassistant/components/vlc_telnet/media_player.py @@ -296,7 +296,9 @@ class VlcDevice(MediaPlayerEntity): """Play media from a URL or file.""" # Handle media_source if media_source.is_media_source_id(media_id): - sourced_media = await media_source.async_resolve_media(self.hass, media_id) + sourced_media = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_type = sourced_media.mime_type media_id = sourced_media.url diff --git a/homeassistant/components/yamaha_musiccast/media_player.py b/homeassistant/components/yamaha_musiccast/media_player.py index d0141977f29..954942b2c6b 100644 --- a/homeassistant/components/yamaha_musiccast/media_player.py +++ b/homeassistant/components/yamaha_musiccast/media_player.py @@ -275,7 +275,9 @@ class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity): async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: """Play media.""" if media_source.is_media_source_id(media_id): - play_item = await media_source.async_resolve_media(self.hass, media_id) + play_item = await media_source.async_resolve_media( + self.hass, media_id, self.entity_id + ) media_id = play_item.url if self.state == STATE_OFF: diff --git a/tests/components/camera/test_media_source.py b/tests/components/camera/test_media_source.py index b7c273bb23a..4134e9b1151 100644 --- a/tests/components/camera/test_media_source.py +++ b/tests/components/camera/test_media_source.py @@ -62,7 +62,7 @@ async def test_resolving(hass, mock_camera_hls): return_value="http://example.com/stream", ): item = await media_source.async_resolve_media( - hass, "media-source://camera/camera.demo_camera" + hass, "media-source://camera/camera.demo_camera", None ) assert item is not None assert item.url == "http://example.com/stream" @@ -74,7 +74,7 @@ async def test_resolving_errors(hass, mock_camera_hls): with pytest.raises(media_source.Unresolvable) as exc_info: await media_source.async_resolve_media( - hass, "media-source://camera/camera.demo_camera" + hass, "media-source://camera/camera.demo_camera", None ) assert str(exc_info.value) == "Stream integration not loaded" @@ -82,7 +82,7 @@ async def test_resolving_errors(hass, mock_camera_hls): with pytest.raises(media_source.Unresolvable) as exc_info: await media_source.async_resolve_media( - hass, "media-source://camera/camera.non_existing" + hass, "media-source://camera/camera.non_existing", None ) assert str(exc_info.value) == "Could not resolve media item: camera.non_existing" @@ -91,13 +91,13 @@ async def test_resolving_errors(hass, mock_camera_hls): new_callable=PropertyMock(return_value=StreamType.WEB_RTC), ): await media_source.async_resolve_media( - hass, "media-source://camera/camera.demo_camera" + hass, "media-source://camera/camera.demo_camera", None ) assert str(exc_info.value) == "Camera does not support MJPEG or HLS streaming." with pytest.raises(media_source.Unresolvable) as exc_info: await media_source.async_resolve_media( - hass, "media-source://camera/camera.demo_camera" + hass, "media-source://camera/camera.demo_camera", None ) assert ( str(exc_info.value) == "camera.demo_camera does not support play stream service" diff --git a/tests/components/dlna_dms/test_device_availability.py b/tests/components/dlna_dms/test_device_availability.py index 67ad1024709..a3ec5326f00 100644 --- a/tests/components/dlna_dms/test_device_availability.py +++ b/tests/components/dlna_dms/test_device_availability.py @@ -152,15 +152,15 @@ async def test_unavailable_device( ) with pytest.raises(Unresolvable, match="DMS is not connected"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}//resolve_path" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}//resolve_path", None ) with pytest.raises(Unresolvable, match="DMS is not connected"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:resolve_object" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:resolve_object", None ) with pytest.raises(Unresolvable): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/?resolve_search" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/?resolve_search", None ) @@ -651,7 +651,7 @@ async def test_become_unavailable( # Check async_resolve_object currently works assert await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:object_id" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:object_id", None ) # Now break the network connection @@ -660,7 +660,7 @@ async def test_become_unavailable( # async_resolve_object should fail with pytest.raises(Unresolvable): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:object_id" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:object_id", None ) # The device should now be unavailable diff --git a/tests/components/dlna_dms/test_dms_device_source.py b/tests/components/dlna_dms/test_dms_device_source.py index 5e4021a5dda..622a3b8a4f9 100644 --- a/tests/components/dlna_dms/test_dms_device_source.py +++ b/tests/components/dlna_dms/test_dms_device_source.py @@ -45,7 +45,7 @@ async def async_resolve_media( ) -> DidlPlayMedia: """Call media_source.async_resolve_media with the test source's ID.""" result = await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/{media_content_id}" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/{media_content_id}", None ) assert isinstance(result, DidlPlayMedia) return result diff --git a/tests/components/dlna_dms/test_media_source.py b/tests/components/dlna_dms/test_media_source.py index 5f76b061590..35f34d0689b 100644 --- a/tests/components/dlna_dms/test_media_source.py +++ b/tests/components/dlna_dms/test_media_source.py @@ -60,31 +60,31 @@ async def test_resolve_media_bad_identifier( """Test trying to resolve an item that has an unresolvable identifier.""" # Empty identifier with pytest.raises(Unresolvable, match="No source ID.*"): - await media_source.async_resolve_media(hass, f"media-source://{DOMAIN}") + await media_source.async_resolve_media(hass, f"media-source://{DOMAIN}", None) # Identifier has media_id but no source_id # media_source.URI_SCHEME_REGEX won't let the ID through to dlna_dms with pytest.raises(Unresolvable, match="Invalid media source URI"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}//media_id" + hass, f"media-source://{DOMAIN}//media_id", None ) # Identifier has source_id but no media_id with pytest.raises(Unresolvable, match="No media ID.*"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/source_id/" + hass, f"media-source://{DOMAIN}/source_id/", None ) # Identifier is missing source_id/media_id separator with pytest.raises(Unresolvable, match="No media ID.*"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/source_id" + hass, f"media-source://{DOMAIN}/source_id", None ) # Identifier has an unknown source_id with pytest.raises(Unresolvable, match="Unknown source ID: unknown_source"): await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/unknown_source/media_id" + hass, f"media-source://{DOMAIN}/unknown_source/media_id", None ) @@ -105,7 +105,7 @@ async def test_resolve_media_success( dms_device_mock.async_browse_metadata.return_value = didl_item result = await media_source.async_resolve_media( - hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:{object_id}" + hass, f"media-source://{DOMAIN}/{MOCK_SOURCE_ID}/:{object_id}", None ) assert isinstance(result, DidlPlayMedia) assert result.url == f"{MOCK_DEVICE_BASE_URL}/{res_url}" diff --git a/tests/components/google_translate/test_tts.py b/tests/components/google_translate/test_tts.py index c81cea57090..cc80d9c64b9 100644 --- a/tests/components/google_translate/test_tts.py +++ b/tests/components/google_translate/test_tts.py @@ -25,7 +25,7 @@ async def get_media_source_url(hass, media_content_id): if media_source.DOMAIN not in hass.config.components: assert await async_setup_component(hass, media_source.DOMAIN, {}) - resolved = await media_source.async_resolve_media(hass, media_content_id) + resolved = await media_source.async_resolve_media(hass, media_content_id, None) return resolved.url diff --git a/tests/components/marytts/test_tts.py b/tests/components/marytts/test_tts.py index 843b6578746..60211f7dc0c 100644 --- a/tests/components/marytts/test_tts.py +++ b/tests/components/marytts/test_tts.py @@ -21,7 +21,7 @@ async def get_media_source_url(hass, media_content_id): if media_source.DOMAIN not in hass.config.components: assert await async_setup_component(hass, media_source.DOMAIN, {}) - resolved = await media_source.async_resolve_media(hass, media_content_id) + resolved = await media_source.async_resolve_media(hass, media_content_id, None) return resolved.url diff --git a/tests/components/media_source/test_init.py b/tests/components/media_source/test_init.py index f2a8ff13533..33dd263c46c 100644 --- a/tests/components/media_source/test_init.py +++ b/tests/components/media_source/test_init.py @@ -103,6 +103,7 @@ async def test_async_resolve_media(hass): media = await media_source.async_resolve_media( hass, media_source.generate_media_source_id(media_source.DOMAIN, "local/test.mp3"), + None, ) assert isinstance(media, media_source.models.PlayMedia) assert media.url == "/media/local/test.mp3" @@ -135,15 +136,17 @@ async def test_async_unresolve_media(hass): # Test no media content with pytest.raises(media_source.Unresolvable): - await media_source.async_resolve_media(hass, "") + await media_source.async_resolve_media(hass, "", None) # Test invalid media content with pytest.raises(media_source.Unresolvable): - await media_source.async_resolve_media(hass, "invalid") + await media_source.async_resolve_media(hass, "invalid", None) # Test invalid media source with pytest.raises(media_source.Unresolvable): - await media_source.async_resolve_media(hass, "media-source://media_source2") + await media_source.async_resolve_media( + hass, "media-source://media_source2", None + ) async def test_websocket_browse_media(hass, hass_ws_client): @@ -261,4 +264,4 @@ async def test_browse_resolve_without_setup(): await media_source.async_browse_media(Mock(data={}), None) with pytest.raises(media_source.Unresolvable): - await media_source.async_resolve_media(Mock(data={}), None) + await media_source.async_resolve_media(Mock(data={}), None, None) diff --git a/tests/components/motioneye/test_media_source.py b/tests/components/motioneye/test_media_source.py index 9b86b783d43..2cf31c21da7 100644 --- a/tests/components/motioneye/test_media_source.py +++ b/tests/components/motioneye/test_media_source.py @@ -367,6 +367,7 @@ async def test_async_resolve_media_success(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#movies#/foo.mp4" ), + None, ) assert media == PlayMedia(url="http://movie-url", mime_type="video/mp4") assert client.get_movie_url.call_args == call(TEST_CAMERA_ID, "/foo.mp4") @@ -379,6 +380,7 @@ async def test_async_resolve_media_success(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#images#/foo.jpg" ), + None, ) assert media == PlayMedia(url="http://image-url", mime_type="image/jpeg") assert client.get_image_url.call_args == call(TEST_CAMERA_ID, "/foo.jpg") @@ -409,18 +411,20 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: # URI doesn't contain necessary components. with pytest.raises(Unresolvable): - await media_source.async_resolve_media(hass, f"{const.URI_SCHEME}{DOMAIN}/foo") + await media_source.async_resolve_media( + hass, f"{const.URI_SCHEME}{DOMAIN}/foo", None + ) # Config entry doesn't exist. with pytest.raises(MediaSourceError): await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/1#2#3#4" + hass, f"{const.URI_SCHEME}{DOMAIN}/1#2#3#4", None ) # Device doesn't exist. with pytest.raises(MediaSourceError): await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{TEST_CONFIG_ENTRY_ID}#2#3#4" + hass, f"{const.URI_SCHEME}{DOMAIN}/{TEST_CONFIG_ENTRY_ID}#2#3#4", None ) # Device identifiers are incorrect (no camera id) @@ -431,6 +435,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{broken_device_1.id}#images#4" ), + None, ) # Device identifiers are incorrect (non integer camera id) @@ -441,6 +446,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{broken_device_2.id}#images#4" ), + None, ) # Kind is incorrect. @@ -448,6 +454,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: await media_source.async_resolve_media( hass, f"{const.URI_SCHEME}{DOMAIN}/{TEST_CONFIG_ENTRY_ID}#{device.id}#games#moo", + None, ) # Playback URL raises exception. @@ -459,6 +466,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#movies#/foo.mp4" ), + None, ) # Media path does not start with '/' @@ -470,6 +478,7 @@ async def test_async_resolve_media_failure(hass: HomeAssistant) -> None: f"{const.URI_SCHEME}{DOMAIN}" f"/{TEST_CONFIG_ENTRY_ID}#{device.id}#movies#foo.mp4" ), + None, ) # Media missing path. diff --git a/tests/components/nest/test_media_source.py b/tests/components/nest/test_media_source.py index 1536d0bee1e..09a3f9f625c 100644 --- a/tests/components/nest/test_media_source.py +++ b/tests/components/nest/test_media_source.py @@ -361,7 +361,7 @@ async def test_camera_event(hass, auth, hass_client): # Resolving the event links to the media media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier}" assert media.mime_type == "image/jpeg" @@ -374,7 +374,7 @@ async def test_camera_event(hass, auth, hass_client): # Resolving the device id points to the most recent event media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier}" assert media.mime_type == "image/jpeg" @@ -535,7 +535,7 @@ async def test_multiple_image_events_in_session(hass, auth, hass_client): # Resolve the most recent event media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier2}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier2}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier2}" assert media.mime_type == "image/jpeg" @@ -548,7 +548,7 @@ async def test_multiple_image_events_in_session(hass, auth, hass_client): # Resolving the event links to the media media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier1}" assert media.mime_type == "image/jpeg" @@ -632,7 +632,7 @@ async def test_multiple_clip_preview_events_in_session(hass, auth, hass_client): # to the same clip preview media clip object. # Resolve media for the first event media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier1}" assert media.mime_type == "video/mp4" @@ -645,7 +645,7 @@ async def test_multiple_clip_preview_events_in_session(hass, auth, hass_client): # Resolve media for the second event media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier1}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier1}" assert media.mime_type == "video/mp4" @@ -712,6 +712,7 @@ async def test_resolve_missing_event_id(hass, auth): await media_source.async_resolve_media( hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}", + None, ) @@ -723,6 +724,7 @@ async def test_resolve_invalid_device_id(hass, auth): await media_source.async_resolve_media( hass, f"{const.URI_SCHEME}{DOMAIN}/invalid-device-id/GXXWRWVeHNUlUU3V3MGV3bUOYW...", + None, ) @@ -740,6 +742,7 @@ async def test_resolve_invalid_event_id(hass, auth): media = await media_source.async_resolve_media( hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/GXXWRWVeHNUlUU3V3MGV3bUOYW...", + None, ) assert ( media.url == f"/api/nest/event_media/{device.id}/GXXWRWVeHNUlUU3V3MGV3bUOYW..." @@ -835,7 +838,7 @@ async def test_camera_event_clip_preview(hass, auth, hass_client, mp4): # Resolving the event links to the media media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier}" assert media.mime_type == "video/mp4" @@ -921,7 +924,7 @@ async def test_event_media_failure(hass, auth, hass_client): # Resolving the event links to the media media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{device.id}/{event_identifier}", None ) assert media.url == f"/api/nest/event_media/{device.id}/{event_identifier}" assert media.mime_type == "image/jpeg" @@ -1128,7 +1131,7 @@ async def test_media_store_persistence(hass, auth, hass_client, event_store): event_identifier = browse.children[0].identifier media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{event_identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{event_identifier}", None ) assert media.url == f"/api/nest/event_media/{event_identifier}" assert media.mime_type == "video/mp4" @@ -1182,7 +1185,7 @@ async def test_media_store_persistence(hass, auth, hass_client, event_store): event_identifier = browse.children[0].identifier media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{event_identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{event_identifier}", None ) assert media.url == f"/api/nest/event_media/{event_identifier}" assert media.mime_type == "video/mp4" @@ -1234,7 +1237,7 @@ async def test_media_store_save_filesystem_error(hass, auth, hass_client): event = browse.children[0] media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/{event.identifier}" + hass, f"{const.URI_SCHEME}{DOMAIN}/{event.identifier}", None ) assert media.url == f"/api/nest/event_media/{event.identifier}" assert media.mime_type == "video/mp4" diff --git a/tests/components/netatmo/test_media_source.py b/tests/components/netatmo/test_media_source.py index db1a79145b4..390da95496a 100644 --- a/tests/components/netatmo/test_media_source.py +++ b/tests/components/netatmo/test_media_source.py @@ -79,7 +79,7 @@ async def test_async_browse_media(hass): # Test successful event resolve media = await media_source.async_resolve_media( - hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672" + hass, f"{const.URI_SCHEME}{DOMAIN}/events/12:34:56:78:90:ab/1599152672", None ) assert media == PlayMedia( url="http:///files/high/index.m3u8", mime_type="application/x-mpegURL" diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 7fd8cc0facb..78fa49a8fc9 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -29,7 +29,7 @@ async def get_media_source_url(hass, media_content_id): if media_source.DOMAIN not in hass.config.components: assert await async_setup_component(hass, media_source.DOMAIN, {}) - resolved = await media_source.async_resolve_media(hass, media_content_id) + resolved = await media_source.async_resolve_media(hass, media_content_id, None) return resolved.url diff --git a/tests/components/tts/test_media_source.py b/tests/components/tts/test_media_source.py index 22edfef5358..8af1ad9d3bb 100644 --- a/tests/components/tts/test_media_source.py +++ b/tests/components/tts/test_media_source.py @@ -68,7 +68,7 @@ async def test_browsing(hass): async def test_resolving(hass, mock_get_tts_audio): """Test resolving.""" media = await media_source.async_resolve_media( - hass, "media-source://tts/demo?message=Hello%20World" + hass, "media-source://tts/demo?message=Hello%20World", None ) assert media.url.startswith("/api/tts_proxy/") assert media.mime_type == "audio/mpeg" @@ -82,7 +82,9 @@ async def test_resolving(hass, mock_get_tts_audio): # Pass language and options mock_get_tts_audio.reset_mock() media = await media_source.async_resolve_media( - hass, "media-source://tts/demo?message=Bye%20World&language=de&voice=Paulus" + hass, + "media-source://tts/demo?message=Bye%20World&language=de&voice=Paulus", + None, ) assert media.url.startswith("/api/tts_proxy/") assert media.mime_type == "audio/mpeg" @@ -98,16 +100,18 @@ async def test_resolving_errors(hass): """Test resolving.""" # No message added with pytest.raises(media_source.Unresolvable): - await media_source.async_resolve_media(hass, "media-source://tts/demo") + await media_source.async_resolve_media(hass, "media-source://tts/demo", None) # Non-existing provider with pytest.raises(media_source.Unresolvable): await media_source.async_resolve_media( - hass, "media-source://tts/non-existing?message=bla" + hass, "media-source://tts/non-existing?message=bla", None ) # Non-existing option with pytest.raises(media_source.Unresolvable): await media_source.async_resolve_media( - hass, "media-source://tts/non-existing?message=bla&non_existing_option=bla" + hass, + "media-source://tts/non-existing?message=bla&non_existing_option=bla", + None, ) diff --git a/tests/components/voicerss/test_tts.py b/tests/components/voicerss/test_tts.py index 3e74d9dc815..099b280625f 100644 --- a/tests/components/voicerss/test_tts.py +++ b/tests/components/voicerss/test_tts.py @@ -33,7 +33,7 @@ async def get_media_source_url(hass, media_content_id): if media_source.DOMAIN not in hass.config.components: assert await async_setup_component(hass, media_source.DOMAIN, {}) - resolved = await media_source.async_resolve_media(hass, media_content_id) + resolved = await media_source.async_resolve_media(hass, media_content_id, None) return resolved.url diff --git a/tests/components/yandextts/test_tts.py b/tests/components/yandextts/test_tts.py index fdc204384a5..8549b51c341 100644 --- a/tests/components/yandextts/test_tts.py +++ b/tests/components/yandextts/test_tts.py @@ -27,7 +27,7 @@ async def get_media_source_url(hass, media_content_id): if media_source.DOMAIN not in hass.config.components: assert await async_setup_component(hass, media_source.DOMAIN, {}) - resolved = await media_source.async_resolve_media(hass, media_content_id) + resolved = await media_source.async_resolve_media(hass, media_content_id, None) return resolved.url From 27908af61eb0c07106fa721c4b20b9b64e988658 Mon Sep 17 00:00:00 2001 From: xLarry Date: Fri, 27 May 2022 18:19:18 +0200 Subject: [PATCH 865/930] Bump laundrify_aio to v1.1.2 (#72605) --- homeassistant/components/laundrify/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/laundrify/manifest.json b/homeassistant/components/laundrify/manifest.json index 6a61446d31c..a5737b9cf97 100644 --- a/homeassistant/components/laundrify/manifest.json +++ b/homeassistant/components/laundrify/manifest.json @@ -3,7 +3,7 @@ "name": "laundrify", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/laundrify", - "requirements": ["laundrify_aio==1.1.1"], + "requirements": ["laundrify_aio==1.1.2"], "codeowners": ["@xLarry"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index c7459285fa0..e94929e8d16 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -927,7 +927,7 @@ krakenex==2.1.0 lakeside==0.12 # homeassistant.components.laundrify -laundrify_aio==1.1.1 +laundrify_aio==1.1.2 # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7233824a3cc..edcd957246f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -655,7 +655,7 @@ kostal_plenticore==0.2.0 krakenex==2.1.0 # homeassistant.components.laundrify -laundrify_aio==1.1.1 +laundrify_aio==1.1.2 # homeassistant.components.foscam libpyfoscam==1.0 From 07c7081adec21fcaa14038acabe8227ca25dc53f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 27 May 2022 10:30:40 -0700 Subject: [PATCH 866/930] Revert "Add service entity context (#71558)" (#72610) --- homeassistant/helpers/service.py | 11 ----------- tests/helpers/test_service.py | 16 +--------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 9a1e6caa27e..bc3451c24c0 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio from collections.abc import Awaitable, Callable, Iterable -from contextvars import ContextVar import dataclasses from functools import partial, wraps import logging @@ -64,15 +63,6 @@ _LOGGER = logging.getLogger(__name__) SERVICE_DESCRIPTION_CACHE = "service_description_cache" -_current_entity: ContextVar[str | None] = ContextVar("current_entity", default=None) - - -@callback -def async_get_current_entity() -> str | None: - """Get the current entity on which the service is called.""" - return _current_entity.get() - - class ServiceParams(TypedDict): """Type for service call parameters.""" @@ -716,7 +706,6 @@ async def _handle_entity_call( ) -> None: """Handle calling service method.""" entity.async_set_context(context) - _current_entity.set(entity.entity_id) if isinstance(func, str): result = hass.async_run_job(partial(getattr(entity, func), **data)) # type: ignore[arg-type] diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 76cf83e31bf..d08477dc917 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -19,12 +19,12 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.helpers import ( - config_validation as cv, device_registry as dev_reg, entity_registry as ent_reg, service, template, ) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import EntityCategory from homeassistant.setup import async_setup_component @@ -1206,17 +1206,3 @@ async def test_async_extract_config_entry_ids(hass): ) assert await service.async_extract_config_entry_ids(hass, call) == {"abc"} - - -async def test_current_entity_context(hass, mock_entities): - """Test we set the current entity context var.""" - - async def mock_service(entity, call): - assert entity.entity_id == service.async_get_current_entity() - - await service.entity_service_call( - hass, - [Mock(entities=mock_entities)], - mock_service, - ha.ServiceCall("test_domain", "test_service", {"entity_id": "light.kitchen"}), - ) From 2e2fa208a83c5c543313c6651b61922f177b69ef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 May 2022 07:32:26 -1000 Subject: [PATCH 867/930] Fix recorder system health when the db_url is lacking a hostname (#72612) --- .../recorder/system_health/__init__.py | 5 ++- .../components/recorder/test_system_health.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/recorder/system_health/__init__.py b/homeassistant/components/recorder/system_health/__init__.py index 8ba68a1649b..c4bf2c3bb89 100644 --- a/homeassistant/components/recorder/system_health/__init__.py +++ b/homeassistant/components/recorder/system_health/__init__.py @@ -2,8 +2,7 @@ from __future__ import annotations from typing import Any - -from yarl import URL +from urllib.parse import urlparse from homeassistant.components import system_health from homeassistant.components.recorder.core import Recorder @@ -60,7 +59,7 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: instance = get_instance(hass) run_history = instance.run_history - database_name = URL(instance.db_url).path.lstrip("/") + database_name = urlparse(instance.db_url).path.lstrip("/") db_engine_info = _async_get_db_engine_info(instance) db_stats: dict[str, Any] = {} diff --git a/tests/components/recorder/test_system_health.py b/tests/components/recorder/test_system_health.py index 80997b9df36..b465ee89ebe 100644 --- a/tests/components/recorder/test_system_health.py +++ b/tests/components/recorder/test_system_health.py @@ -53,6 +53,37 @@ async def test_recorder_system_health_alternate_dbms(hass, recorder_mock, dialec } +@pytest.mark.parametrize( + "dialect_name", [SupportedDialect.MYSQL, SupportedDialect.POSTGRESQL] +) +async def test_recorder_system_health_db_url_missing_host( + hass, recorder_mock, dialect_name +): + """Test recorder system health with a db_url without a hostname.""" + assert await async_setup_component(hass, "system_health", {}) + await async_wait_recording_done(hass) + + instance = get_instance(hass) + with patch( + "homeassistant.components.recorder.core.Recorder.dialect_name", dialect_name + ), patch.object( + instance, + "db_url", + "postgresql://homeassistant:blabla@/home_assistant?host=/config/socket", + ), patch( + "sqlalchemy.orm.session.Session.execute", + return_value=Mock(first=Mock(return_value=("1048576",))), + ): + info = await get_system_health_info(hass, "recorder") + assert info == { + "current_recorder_run": instance.run_history.current.start, + "oldest_recorder_run": instance.run_history.first.start, + "estimated_db_size": "1.00 MiB", + "database_engine": dialect_name.value, + "database_version": ANY, + } + + async def test_recorder_system_health_crashed_recorder_runs_table( hass: HomeAssistant, async_setup_recorder_instance: SetupRecorderInstanceT ): From 38c085f86931e08091296138c4bc1b10a8fa7d01 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Fri, 27 May 2022 11:32:38 -0600 Subject: [PATCH 868/930] Bump regenmaschine to 2022.05.0 (#72613) --- homeassistant/components/rainmachine/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index 331f191d029..bbe58e263b1 100644 --- a/homeassistant/components/rainmachine/manifest.json +++ b/homeassistant/components/rainmachine/manifest.json @@ -3,7 +3,7 @@ "name": "RainMachine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rainmachine", - "requirements": ["regenmaschine==2022.01.0"], + "requirements": ["regenmaschine==2022.05.0"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index e94929e8d16..6d7ca5cb6b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2065,7 +2065,7 @@ raincloudy==0.0.7 raspyrfm-client==1.2.8 # homeassistant.components.rainmachine -regenmaschine==2022.01.0 +regenmaschine==2022.05.0 # homeassistant.components.renault renault-api==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index edcd957246f..78caa172df7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1364,7 +1364,7 @@ rachiopy==1.0.3 radios==0.1.1 # homeassistant.components.rainmachine -regenmaschine==2022.01.0 +regenmaschine==2022.05.0 # homeassistant.components.renault renault-api==0.1.11 From 13f953f49d3f13069404ed46f921cde9e4fcc034 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 May 2022 08:11:33 -1000 Subject: [PATCH 869/930] Add explict type casts for postgresql filters (#72615) --- homeassistant/components/recorder/filters.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index 7f1d0bc597f..0a383d8ef2b 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -5,7 +5,7 @@ from collections.abc import Callable, Iterable import json from typing import Any -from sqlalchemy import Column, not_, or_ +from sqlalchemy import JSON, Column, Text, cast, not_, or_ from sqlalchemy.sql.elements import ClauseList from homeassistant.const import CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, CONF_INCLUDE @@ -110,8 +110,7 @@ class Filters: """Generate the entity filter query.""" _encoder = json.dumps return or_( - (ENTITY_ID_IN_EVENT == _encoder(None)) - & (OLD_ENTITY_ID_IN_EVENT == _encoder(None)), + (ENTITY_ID_IN_EVENT == JSON.NULL) & (OLD_ENTITY_ID_IN_EVENT == JSON.NULL), self._generate_filter_for_columns( (ENTITY_ID_IN_EVENT, OLD_ENTITY_ID_IN_EVENT), _encoder ).self_group(), @@ -123,7 +122,7 @@ def _globs_to_like( ) -> ClauseList: """Translate glob to sql.""" return or_( - column.like(encoder(glob_str.translate(GLOB_TO_SQL_CHARS))) + cast(column, Text()).like(encoder(glob_str.translate(GLOB_TO_SQL_CHARS))) for glob_str in glob_strs for column in columns ) @@ -133,7 +132,7 @@ def _entity_matcher( entity_ids: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: return or_( - column.in_([encoder(entity_id) for entity_id in entity_ids]) + cast(column, Text()).in_([encoder(entity_id) for entity_id in entity_ids]) for column in columns ) @@ -142,5 +141,7 @@ def _domain_matcher( domains: Iterable[str], columns: Iterable[Column], encoder: Callable[[Any], Any] ) -> ClauseList: return or_( - column.like(encoder(f"{domain}.%")) for domain in domains for column in columns + cast(column, Text()).like(encoder(f"{domain}.%")) + for domain in domains + for column in columns ) From e974a432aa629acc6093106c4e33f713e2d083a6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 27 May 2022 11:38:00 -0700 Subject: [PATCH 870/930] Bumped version to 2022.6.0b2 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ada9a6bbd06..acad8f2675a 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b1" +PATCH_VERSION: Final = "0b2" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 205fcd96086..3d26396deed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b1 +version = 2022.6.0b2 url = https://www.home-assistant.io/ [options] From afcc8679dd3b9782fd478abb6fe94cbf2ec892ff Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 May 2022 20:23:16 -0700 Subject: [PATCH 871/930] Handle OAuth2 rejection (#72040) --- .../helpers/config_entry_oauth2_flow.py | 30 +++++++--- homeassistant/strings.json | 1 + script/scaffold/generate.py | 1 + .../helpers/test_config_entry_oauth2_flow.py | 55 +++++++++++++++++++ 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index d369b872eb9..365ced24929 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -271,9 +271,10 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta): ) -> FlowResult: """Create an entry for auth.""" # Flow has been triggered by external data - if user_input: + if user_input is not None: self.external_data = user_input - return self.async_external_step_done(next_step_id="creation") + next_step = "authorize_rejected" if "error" in user_input else "creation" + return self.async_external_step_done(next_step_id=next_step) try: async with async_timeout.timeout(10): @@ -311,6 +312,13 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta): {"auth_implementation": self.flow_impl.domain, "token": token} ) + async def async_step_authorize_rejected(self, data: None = None) -> FlowResult: + """Step to handle flow rejection.""" + return self.async_abort( + reason="user_rejected_authorize", + description_placeholders={"error": self.external_data["error"]}, + ) + async def async_oauth_create_entry(self, data: dict) -> FlowResult: """Create an entry for the flow. @@ -400,10 +408,8 @@ class OAuth2AuthorizeCallbackView(http.HomeAssistantView): async def get(self, request: web.Request) -> web.Response: """Receive authorization code.""" - if "code" not in request.query or "state" not in request.query: - return web.Response( - text=f"Missing code or state parameter in {request.url}" - ) + if "state" not in request.query: + return web.Response(text="Missing state parameter") hass = request.app["hass"] @@ -412,9 +418,17 @@ class OAuth2AuthorizeCallbackView(http.HomeAssistantView): if state is None: return web.Response(text="Invalid state") + user_input: dict[str, Any] = {"state": state} + + if "code" in request.query: + user_input["code"] = request.query["code"] + elif "error" in request.query: + user_input["error"] = request.query["error"] + else: + return web.Response(text="Missing code or error parameter") + await hass.config_entries.flow.async_configure( - flow_id=state["flow_id"], - user_input={"state": state, "code": request.query["code"]}, + flow_id=state["flow_id"], user_input=user_input ) return web.Response( diff --git a/homeassistant/strings.json b/homeassistant/strings.json index e4d363c22be..9ae30becaee 100644 --- a/homeassistant/strings.json +++ b/homeassistant/strings.json @@ -74,6 +74,7 @@ "oauth2_missing_credentials": "The integration requires application credentials.", "oauth2_authorize_url_timeout": "Timeout generating authorize URL.", "oauth2_no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})", + "oauth2_user_rejected_authorize": "Account linking rejected: {error}", "reauth_successful": "Re-authentication was successful", "unknown_authorize_url_generation": "Unknown error generating an authorize URL.", "cloud_not_connected": "Not connected to Home Assistant Cloud." diff --git a/script/scaffold/generate.py b/script/scaffold/generate.py index 7f418868463..b7e4c58d1a1 100644 --- a/script/scaffold/generate.py +++ b/script/scaffold/generate.py @@ -188,6 +188,7 @@ def _custom_tasks(template, info: Info) -> None: "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]", "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]", "no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]", + "user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]", }, "create_entry": { "default": "[%key:common::config_flow::create_entry::authenticated%]" diff --git a/tests/helpers/test_config_entry_oauth2_flow.py b/tests/helpers/test_config_entry_oauth2_flow.py index 248f3b8dbb0..e5d220c55df 100644 --- a/tests/helpers/test_config_entry_oauth2_flow.py +++ b/tests/helpers/test_config_entry_oauth2_flow.py @@ -223,6 +223,61 @@ async def test_abort_if_oauth_error( assert result["reason"] == "oauth_error" +async def test_abort_if_oauth_rejected( + hass, + flow_handler, + local_impl, + hass_client_no_auth, + aioclient_mock, + current_request_with_host, +): + """Check bad oauth token.""" + flow_handler.async_register_implementation(hass, local_impl) + config_entry_oauth2_flow.async_register_implementation( + hass, TEST_DOMAIN, MockOAuth2Implementation() + ) + + result = await hass.config_entries.flow.async_init( + TEST_DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "pick_implementation" + + # Pick implementation + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={"implementation": TEST_DOMAIN} + ) + + state = config_entry_oauth2_flow._encode_jwt( + hass, + { + "flow_id": result["flow_id"], + "redirect_uri": "https://example.com/auth/external/callback", + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP + assert result["url"] == ( + f"{AUTHORIZE_URL}?response_type=code&client_id={CLIENT_ID}" + "&redirect_uri=https://example.com/auth/external/callback" + f"&state={state}&scope=read+write" + ) + + client = await hass_client_no_auth() + resp = await client.get( + f"/auth/external/callback?error=access_denied&state={state}" + ) + assert resp.status == 200 + assert resp.headers["content-type"] == "text/html; charset=utf-8" + + result = await hass.config_entries.flow.async_configure(result["flow_id"]) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "user_rejected_authorize" + assert result["description_placeholders"] == {"error": "access_denied"} + + async def test_step_discovery(hass, flow_handler, local_impl): """Check flow triggers from discovery.""" flow_handler.async_register_implementation(hass, local_impl) From 79340f85d28befe32a6d91695f533a45e5cb7258 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 28 May 2022 12:51:40 -0700 Subject: [PATCH 872/930] Don't import google calendar user pref for disabling new entities (#72652) --- homeassistant/components/google/__init__.py | 29 ++++---- tests/components/google/test_init.py | 82 ++++++++------------- 2 files changed, 42 insertions(+), 69 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index b7263d2e469..1336e9991e3 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -199,11 +199,18 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: _LOGGER.warning( "Configuration of Google Calendar in YAML in configuration.yaml is " "is deprecated and will be removed in a future release; Your existing " - "OAuth Application Credentials and other settings have been imported " + "OAuth Application Credentials and access settings have been imported " "into the UI automatically and can be safely removed from your " "configuration.yaml file" ) - + if conf.get(CONF_TRACK_NEW) is False: + # The track_new as False would previously result in new entries + # in google_calendars.yaml with track set to Fasle which is + # handled at calendar entity creation time. + _LOGGER.warning( + "You must manually set the integration System Options in the " + "UI to disable newly discovered entities going forward" + ) return True @@ -260,23 +267,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: def async_upgrade_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: """Upgrade the config entry if needed.""" - if DATA_CONFIG not in hass.data[DOMAIN] and entry.options: + if entry.options: return - - options = ( - entry.options - if entry.options - else { - CONF_CALENDAR_ACCESS: get_feature_access(hass).name, - } - ) - disable_new_entities = ( - not hass.data[DOMAIN].get(DATA_CONFIG, {}).get(CONF_TRACK_NEW, True) - ) hass.config_entries.async_update_entry( entry, - options=options, - pref_disable_new_entities=disable_new_entities, + options={ + CONF_CALENDAR_ACCESS: get_feature_access(hass).name, + }, ) diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index 93c0642514e..b6f7a6b4cbc 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -154,57 +154,6 @@ async def test_calendar_yaml_error( assert hass.states.get(TEST_API_ENTITY) -@pytest.mark.parametrize( - "google_config_track_new,calendars_config,expected_state", - [ - ( - None, - [], - State( - TEST_API_ENTITY, - STATE_OFF, - attributes={ - "offset_reached": False, - "friendly_name": TEST_API_ENTITY_NAME, - }, - ), - ), - ( - True, - [], - State( - TEST_API_ENTITY, - STATE_OFF, - attributes={ - "offset_reached": False, - "friendly_name": TEST_API_ENTITY_NAME, - }, - ), - ), - (False, [], None), - ], - ids=["default", "True", "False"], -) -async def test_track_new( - hass: HomeAssistant, - component_setup: ComponentSetup, - mock_calendars_list: ApiResult, - test_api_calendar: dict[str, Any], - mock_events_list: ApiResult, - mock_calendars_yaml: None, - expected_state: State, - setup_config_entry: MockConfigEntry, -) -> None: - """Test behavior of configuration.yaml settings for tracking new calendars not in the config.""" - - mock_calendars_list({"items": [test_api_calendar]}) - mock_events_list({}) - assert await component_setup() - - state = hass.states.get(TEST_API_ENTITY) - assert_state(state, expected_state) - - @pytest.mark.parametrize("calendars_config", [[]]) async def test_found_calendar_from_api( hass: HomeAssistant, @@ -263,7 +212,7 @@ async def test_load_application_credentials( @pytest.mark.parametrize( - "calendars_config_track,expected_state", + "calendars_config_track,expected_state,google_config_track_new", [ ( True, @@ -275,8 +224,35 @@ async def test_load_application_credentials( "friendly_name": TEST_YAML_ENTITY_NAME, }, ), + None, ), - (False, None), + ( + True, + State( + TEST_YAML_ENTITY, + STATE_OFF, + attributes={ + "offset_reached": False, + "friendly_name": TEST_YAML_ENTITY_NAME, + }, + ), + True, + ), + ( + True, + State( + TEST_YAML_ENTITY, + STATE_OFF, + attributes={ + "offset_reached": False, + "friendly_name": TEST_YAML_ENTITY_NAME, + }, + ), + False, # Has no effect + ), + (False, None, None), + (False, None, True), + (False, None, False), ], ) async def test_calendar_config_track_new( From 301f7647d1da2316dadc0aa0ac657c096313bc24 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 28 May 2022 20:28:22 -0700 Subject: [PATCH 873/930] Defer google calendar integration reload to a task to avoid races of reload during setup (#72608) --- homeassistant/components/google/__init__.py | 29 ++++------ homeassistant/components/google/api.py | 12 +++- tests/components/google/test_config_flow.py | 31 +++++----- tests/components/google/test_init.py | 64 +++++++++++++++++++++ 4 files changed, 101 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/google/__init__.py b/homeassistant/components/google/__init__.py index 1336e9991e3..2a40bfe7043 100644 --- a/homeassistant/components/google/__init__.py +++ b/homeassistant/components/google/__init__.py @@ -217,7 +217,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Google from a config entry.""" hass.data.setdefault(DOMAIN, {}) - async_upgrade_entry(hass, entry) implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry @@ -240,10 +239,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except aiohttp.ClientError as err: raise ConfigEntryNotReady from err - access = FeatureAccess[entry.options[CONF_CALENDAR_ACCESS]] - token_scopes = session.token.get("scope", []) - if access.scope not in token_scopes: - _LOGGER.debug("Scope '%s' not in scopes '%s'", access.scope, token_scopes) + if not async_entry_has_scopes(hass, entry): raise ConfigEntryAuthFailed( "Required scopes are not available, reauth required" ) @@ -254,27 +250,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await async_setup_services(hass, calendar_service) # Only expose the add event service if we have the correct permissions - if access is FeatureAccess.read_write: + if get_feature_access(hass, entry) is FeatureAccess.read_write: await async_setup_add_event_service(hass, calendar_service) hass.config_entries.async_setup_platforms(entry, PLATFORMS) - # Reload entry when options are updated entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True -def async_upgrade_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Upgrade the config entry if needed.""" - if entry.options: - return - hass.config_entries.async_update_entry( - entry, - options={ - CONF_CALENDAR_ACCESS: get_feature_access(hass).name, - }, - ) +def async_entry_has_scopes(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Verify that the config entry desired scope is present in the oauth token.""" + access = get_feature_access(hass, entry) + token_scopes = entry.data.get("token", {}).get("scope", []) + return access.scope in token_scopes async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -283,8 +273,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Reload the config entry when it changed.""" - await hass.config_entries.async_reload(entry.entry_id) + """Reload config entry if the access options change.""" + if not async_entry_has_scopes(hass, entry): + await hass.config_entries.async_reload(entry.entry_id) async def async_setup_services( diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index eeac854a2ae..4bb9de5d581 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -19,6 +19,7 @@ from oauth2client.client import ( ) from homeassistant.components.application_credentials import AuthImplementation +from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers.event import async_track_time_interval @@ -127,8 +128,17 @@ class DeviceFlow: ) -def get_feature_access(hass: HomeAssistant) -> FeatureAccess: +def get_feature_access( + hass: HomeAssistant, config_entry: ConfigEntry | None = None +) -> FeatureAccess: """Return the desired calendar feature access.""" + if ( + config_entry + and config_entry.options + and CONF_CALENDAR_ACCESS in config_entry.options + ): + return FeatureAccess[config_entry.options[CONF_CALENDAR_ACCESS]] + # This may be called during config entry setup without integration setup running when there # is no google entry in configuration.yaml return cast( diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 77ebe1e56cd..8ac017fcba4 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -540,9 +540,15 @@ async def test_options_flow_triggers_reauth( ) -> None: """Test load and unload of a ConfigEntry.""" config_entry.add_to_hass(hass) - await component_setup() + + with patch( + "homeassistant.components.google.async_setup_entry", return_value=True + ) as mock_setup: + await component_setup() + mock_setup.assert_called_once() + assert config_entry.state is ConfigEntryState.LOADED - assert config_entry.options == {"calendar_access": "read_write"} + assert config_entry.options == {} # Default is read_write result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == "form" @@ -557,14 +563,7 @@ async def test_options_flow_triggers_reauth( }, ) assert result["type"] == "create_entry" - - await hass.async_block_till_done() assert config_entry.options == {"calendar_access": "read_only"} - # Re-auth flow was initiated because access level changed - assert config_entry.state is ConfigEntryState.SETUP_ERROR - flows = hass.config_entries.flow.async_progress() - assert len(flows) == 1 - assert flows[0]["step_id"] == "reauth_confirm" async def test_options_flow_no_changes( @@ -574,9 +573,15 @@ async def test_options_flow_no_changes( ) -> None: """Test load and unload of a ConfigEntry.""" config_entry.add_to_hass(hass) - await component_setup() + + with patch( + "homeassistant.components.google.async_setup_entry", return_value=True + ) as mock_setup: + await component_setup() + mock_setup.assert_called_once() + assert config_entry.state is ConfigEntryState.LOADED - assert config_entry.options == {"calendar_access": "read_write"} + assert config_entry.options == {} # Default is read_write result = await hass.config_entries.options.async_init(config_entry.entry_id) assert result["type"] == "form" @@ -589,8 +594,4 @@ async def test_options_flow_no_changes( }, ) assert result["type"] == "create_entry" - - await hass.async_block_till_done() assert config_entry.options == {"calendar_access": "read_write"} - # Re-auth flow was initiated because access level changed - assert config_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/google/test_init.py b/tests/components/google/test_init.py index b6f7a6b4cbc..f2cf067f7bb 100644 --- a/tests/components/google/test_init.py +++ b/tests/components/google/test_init.py @@ -102,6 +102,24 @@ async def test_existing_token_missing_scope( assert flows[0]["step_id"] == "reauth_confirm" +@pytest.mark.parametrize("config_entry_options", [{CONF_CALENDAR_ACCESS: "read_only"}]) +async def test_config_entry_scope_reauth( + hass: HomeAssistant, + token_scopes: list[str], + component_setup: ComponentSetup, + config_entry: MockConfigEntry, +) -> None: + """Test setup where the config entry options requires reauth to match the scope.""" + config_entry.add_to_hass(hass) + assert await component_setup() + + assert config_entry.state is ConfigEntryState.SETUP_ERROR + + flows = hass.config_entries.flow.async_progress() + assert len(flows) == 1 + assert flows[0]["step_id"] == "reauth_confirm" + + @pytest.mark.parametrize("calendars_config", [[{"cal_id": "invalid-schema"}]]) async def test_calendar_yaml_missing_required_fields( hass: HomeAssistant, @@ -629,3 +647,49 @@ async def test_calendar_yaml_update( # No yaml config loaded that overwrites the entity name assert not hass.states.get(TEST_YAML_ENTITY) + + +async def test_update_will_reload( + hass: HomeAssistant, + component_setup: ComponentSetup, + setup_config_entry: Any, + mock_calendars_list: ApiResult, + test_api_calendar: dict[str, Any], + mock_events_list: ApiResult, + config_entry: MockConfigEntry, +) -> None: + """Test updating config entry options will trigger a reload.""" + mock_calendars_list({"items": [test_api_calendar]}) + mock_events_list({}) + await component_setup() + assert config_entry.state is ConfigEntryState.LOADED + assert config_entry.options == {} # read_write is default + + with patch( + "homeassistant.config_entries.ConfigEntries.async_reload", + return_value=None, + ) as mock_reload: + # No-op does not reload + hass.config_entries.async_update_entry( + config_entry, options={CONF_CALENDAR_ACCESS: "read_write"} + ) + await hass.async_block_till_done() + mock_reload.assert_not_called() + + # Data change does not trigger reload + hass.config_entries.async_update_entry( + config_entry, + data={ + **config_entry.data, + "example": "field", + }, + ) + await hass.async_block_till_done() + mock_reload.assert_not_called() + + # Reload when options changed + hass.config_entries.async_update_entry( + config_entry, options={CONF_CALENDAR_ACCESS: "read_only"} + ) + await hass.async_block_till_done() + mock_reload.assert_called_once() From c45dc492705f92241f63b4fab941eb34cc4500b7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 May 2022 11:38:29 -1000 Subject: [PATCH 874/930] Escape % and _ in history/logbook entity_globs, and use ? as _ (#72623) Co-authored-by: pyos --- homeassistant/components/recorder/filters.py | 11 +- tests/components/history/test_init.py | 12 +- .../components/logbook/test_websocket_api.py | 207 ++++++++++++++++++ 3 files changed, 223 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index 0a383d8ef2b..5dd1e4b7884 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -18,8 +18,11 @@ DOMAIN = "history" HISTORY_FILTERS = "history_filters" GLOB_TO_SQL_CHARS = { - 42: "%", # * - 46: "_", # . + ord("*"): "%", + ord("?"): "_", + ord("%"): "\\%", + ord("_"): "\\_", + ord("\\"): "\\\\", } @@ -122,7 +125,9 @@ def _globs_to_like( ) -> ClauseList: """Translate glob to sql.""" return or_( - cast(column, Text()).like(encoder(glob_str.translate(GLOB_TO_SQL_CHARS))) + cast(column, Text()).like( + encoder(glob_str).translate(GLOB_TO_SQL_CHARS), escape="\\" + ) for glob_str in glob_strs for column in columns ) diff --git a/tests/components/history/test_init.py b/tests/components/history/test_init.py index a2626ab2004..cbc5e86c37e 100644 --- a/tests/components/history/test_init.py +++ b/tests/components/history/test_init.py @@ -719,7 +719,7 @@ async def test_fetch_period_api_with_entity_glob_exclude( { "history": { "exclude": { - "entity_globs": ["light.k*"], + "entity_globs": ["light.k*", "binary_sensor.*_?"], "domains": "switch", "entities": "media_player.test", }, @@ -731,6 +731,9 @@ async def test_fetch_period_api_with_entity_glob_exclude( hass.states.async_set("light.match", "on") hass.states.async_set("switch.match", "on") hass.states.async_set("media_player.test", "on") + hass.states.async_set("binary_sensor.sensor_l", "on") + hass.states.async_set("binary_sensor.sensor_r", "on") + hass.states.async_set("binary_sensor.sensor", "on") await async_wait_recording_done(hass) @@ -740,9 +743,10 @@ async def test_fetch_period_api_with_entity_glob_exclude( ) assert response.status == HTTPStatus.OK response_json = await response.json() - assert len(response_json) == 2 - assert response_json[0][0]["entity_id"] == "light.cow" - assert response_json[1][0]["entity_id"] == "light.match" + assert len(response_json) == 3 + assert response_json[0][0]["entity_id"] == "binary_sensor.sensor" + assert response_json[1][0]["entity_id"] == "light.cow" + assert response_json[2][0]["entity_id"] == "light.match" async def test_fetch_period_api_with_entity_glob_include_and_exclude( diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 02fea4f980f..9d7146ec96c 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -22,6 +22,7 @@ from homeassistant.const import ( CONF_DOMAINS, CONF_ENTITIES, CONF_EXCLUDE, + CONF_INCLUDE, EVENT_HOMEASSISTANT_START, STATE_OFF, STATE_ON, @@ -642,6 +643,212 @@ async def test_subscribe_unsubscribe_logbook_stream_excluded_entities( assert sum(hass.bus.async_listeners().values()) == init_count +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_unsubscribe_logbook_stream_included_entities( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream with included entities.""" + test_entities = ( + "light.inc", + "switch.any", + "cover.included", + "cover.not_included", + "automation.not_included", + "binary_sensor.is_light", + ) + + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "automation", "script") + ] + ) + await async_setup_component( + hass, + logbook.DOMAIN, + { + logbook.DOMAIN: { + CONF_INCLUDE: { + CONF_ENTITIES: ["light.inc"], + CONF_DOMAINS: ["switch"], + CONF_ENTITY_GLOBS: "*.included", + } + }, + }, + ) + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + for entity_id in test_entities: + hass.states.async_set(entity_id, STATE_ON) + hass.states.async_set(entity_id, STATE_OFF) + + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + {"id": 7, "type": "logbook/event_stream", "start_time": now.isoformat()} + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + {"entity_id": "light.inc", "state": "off", "when": ANY}, + {"entity_id": "switch.any", "state": "off", "when": ANY}, + {"entity_id": "cover.included", "state": "off", "when": ANY}, + ] + assert msg["event"]["start_time"] == now.timestamp() + assert msg["event"]["end_time"] > msg["event"]["start_time"] + assert msg["event"]["partial"] is True + + for entity_id in test_entities: + hass.states.async_set(entity_id, STATE_ON) + hass.states.async_set(entity_id, STATE_OFF) + await hass.async_block_till_done() + + hass.states.async_remove("light.zulu") + await hass.async_block_till_done() + + hass.states.async_set("light.zulu", "on", {"effect": "help", "color": "blue"}) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [ + {"entity_id": "light.inc", "state": "on", "when": ANY}, + {"entity_id": "light.inc", "state": "off", "when": ANY}, + {"entity_id": "switch.any", "state": "on", "when": ANY}, + {"entity_id": "switch.any", "state": "off", "when": ANY}, + {"entity_id": "cover.included", "state": "on", "when": ANY}, + {"entity_id": "cover.included", "state": "off", "when": ANY}, + ] + + for _ in range(3): + for entity_id in test_entities: + hass.states.async_set(entity_id, STATE_ON) + hass.states.async_set(entity_id, STATE_OFF) + await async_wait_recording_done(hass) + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + {"entity_id": "light.inc", "state": "on", "when": ANY}, + {"entity_id": "light.inc", "state": "off", "when": ANY}, + {"entity_id": "switch.any", "state": "on", "when": ANY}, + {"entity_id": "switch.any", "state": "off", "when": ANY}, + {"entity_id": "cover.included", "state": "on", "when": ANY}, + {"entity_id": "cover.included", "state": "off", "when": ANY}, + ] + + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "cover.included"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "cover.excluded"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + { + ATTR_NAME: "Mock automation switch matching entity", + ATTR_ENTITY_ID: "switch.match_domain", + }, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation switch matching domain", ATTR_DOMAIN: "switch"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation matches nothing"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "light.inc"}, + ) + + await hass.async_block_till_done() + + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + { + "context_id": ANY, + "domain": "automation", + "entity_id": "cover.included", + "message": "triggered", + "name": "Mock automation 3", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": "switch.match_domain", + "message": "triggered", + "name": "Mock automation switch matching entity", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": None, + "message": "triggered", + "name": "Mock automation switch matching domain", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": None, + "message": "triggered", + "name": "Mock automation matches nothing", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": "light.inc", + "message": "triggered", + "name": "Mock automation 3", + "source": None, + "when": ANY, + }, + ] + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) async def test_subscribe_unsubscribe_logbook_stream( hass, recorder_mock, hass_ws_client From 3a06b5f3202d47bdc5bf2edb6bcf5a075726d4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Fri, 27 May 2022 23:37:19 +0200 Subject: [PATCH 875/930] Bump awesomeversion from 22.5.1 to 22.5.2 (#72624) --- homeassistant/package_constraints.txt | 2 +- requirements.txt | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index f235cc3f02c..a43b4f99f63 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -8,7 +8,7 @@ async-upnp-client==0.30.1 async_timeout==4.0.2 atomicwrites==1.4.0 attrs==21.2.0 -awesomeversion==22.5.1 +awesomeversion==22.5.2 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements.txt b/requirements.txt index 8321e70f8de..fe2bf87ad25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ astral==2.2 async_timeout==4.0.2 attrs==21.2.0 atomicwrites==1.4.0 -awesomeversion==22.5.1 +awesomeversion==22.5.2 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/setup.cfg b/setup.cfg index 3d26396deed..b7841c53361 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,7 +10,7 @@ install_requires = async_timeout==4.0.2 attrs==21.2.0 atomicwrites==1.4.0 - awesomeversion==22.5.1 + awesomeversion==22.5.2 bcrypt==3.1.7 certifi>=2021.5.30 ciso8601==2.2.0 From bd222a1fe00430508816b6a3e4304c67b8ac142f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 May 2022 22:49:55 -1000 Subject: [PATCH 876/930] Prevent config entries from being reloaded concurrently (#72636) * Prevent config entries being reloaded concurrently - Fixes Config entry has already been setup when two places try to reload the config entry at the same time. - This comes up quite a bit: https://github.com/home-assistant/core/issues?q=is%3Aissue+sort%3Aupdated-desc+%22Config+entry+has+already+been+setup%22+is%3Aclosed * Make sure plex creates mocks in the event loop * drop reload_lock, already inherits --- homeassistant/config_entries.py | 13 ++++++---- tests/components/plex/conftest.py | 2 +- tests/test_config_entries.py | 40 ++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 7dfbb131c1b..0ac02adb8d0 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -186,6 +186,7 @@ class ConfigEntry: "reason", "_async_cancel_retry_setup", "_on_unload", + "reload_lock", ) def __init__( @@ -275,6 +276,9 @@ class ConfigEntry: # Hold list for functions to call on unload. self._on_unload: list[CALLBACK_TYPE] | None = None + # Reload lock to prevent conflicting reloads + self.reload_lock = asyncio.Lock() + async def async_setup( self, hass: HomeAssistant, @@ -1005,12 +1009,13 @@ class ConfigEntries: if (entry := self.async_get_entry(entry_id)) is None: raise UnknownEntry - unload_result = await self.async_unload(entry_id) + async with entry.reload_lock: + unload_result = await self.async_unload(entry_id) - if not unload_result or entry.disabled_by: - return unload_result + if not unload_result or entry.disabled_by: + return unload_result - return await self.async_setup(entry_id) + return await self.async_setup(entry_id) async def async_set_disabled_by( self, entry_id: str, disabled_by: ConfigEntryDisabler | None diff --git a/tests/components/plex/conftest.py b/tests/components/plex/conftest.py index 47e7d96d2fe..506aadcce61 100644 --- a/tests/components/plex/conftest.py +++ b/tests/components/plex/conftest.py @@ -381,7 +381,7 @@ def hubs_music_library_fixture(): @pytest.fixture(name="entry") -def mock_config_entry(): +async def mock_config_entry(): """Return the default mocked config entry.""" return MockConfigEntry( domain=DOMAIN, diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 3611c204ba7..2602887d1d5 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -1497,7 +1497,7 @@ async def test_reload_entry_entity_registry_works(hass): ) await hass.async_block_till_done() - assert len(mock_unload_entry.mock_calls) == 1 + assert len(mock_unload_entry.mock_calls) == 2 async def test_unique_id_persisted(hass, manager): @@ -3080,3 +3080,41 @@ async def test_deprecated_disabled_by_str_set(hass, manager, caplog): ) assert entry.disabled_by is config_entries.ConfigEntryDisabler.USER assert " str for config entry disabled_by. This is deprecated " in caplog.text + + +async def test_entry_reload_concurrency(hass, manager): + """Test multiple reload calls do not cause a reload race.""" + entry = MockConfigEntry(domain="comp", state=config_entries.ConfigEntryState.LOADED) + entry.add_to_hass(hass) + + async_setup = AsyncMock(return_value=True) + loaded = 1 + + async def _async_setup_entry(*args, **kwargs): + await asyncio.sleep(0) + nonlocal loaded + loaded += 1 + return loaded == 1 + + async def _async_unload_entry(*args, **kwargs): + await asyncio.sleep(0) + nonlocal loaded + loaded -= 1 + return loaded == 0 + + mock_integration( + hass, + MockModule( + "comp", + async_setup=async_setup, + async_setup_entry=_async_setup_entry, + async_unload_entry=_async_unload_entry, + ), + ) + mock_entity_platform(hass, "config_flow.comp", None) + tasks = [] + for _ in range(15): + tasks.append(asyncio.create_task(manager.async_reload(entry.entry_id))) + await asyncio.gather(*tasks) + assert entry.state is config_entries.ConfigEntryState.LOADED + assert loaded == 1 From 50eaf2f47599cae0b5c8b7e26bd4c3494d841f1c Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Sat, 28 May 2022 19:55:50 +0200 Subject: [PATCH 877/930] Bump bimmer_connected to 0.9.2 (#72653) Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index d41a87ef2c1..c7130d12698 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.9.0"], + "requirements": ["bimmer_connected==0.9.2"], "codeowners": ["@gerard33", "@rikroe"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 6d7ca5cb6b2..f0cc4f6fb67 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -394,7 +394,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.9.0 +bimmer_connected==0.9.2 # homeassistant.components.bizkaibus bizkaibus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 78caa172df7..783542a1c69 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -309,7 +309,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.9.0 +bimmer_connected==0.9.2 # homeassistant.components.blebox blebox_uniapi==1.3.3 From b360f0280b8b9c6f7057b11fbda457731472c7c4 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sat, 28 May 2022 22:31:03 +0200 Subject: [PATCH 878/930] Manage stations via integrations configuration in Tankerkoenig (#72654) --- .../components/tankerkoenig/config_flow.py | 64 +++++++++++++------ .../components/tankerkoenig/strings.json | 2 +- .../tankerkoenig/translations/en.json | 4 +- .../tankerkoenig/test_config_flow.py | 23 +++++-- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/tankerkoenig/config_flow.py b/homeassistant/components/tankerkoenig/config_flow.py index 65c367d1ba4..af3b5273b16 100644 --- a/homeassistant/components/tankerkoenig/config_flow.py +++ b/homeassistant/components/tankerkoenig/config_flow.py @@ -17,7 +17,7 @@ from homeassistant.const import ( CONF_SHOW_ON_MAP, LENGTH_KILOMETERS, ) -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult import homeassistant.helpers.config_validation as cv from homeassistant.helpers.selector import ( @@ -29,6 +29,24 @@ from homeassistant.helpers.selector import ( from .const import CONF_FUEL_TYPES, CONF_STATIONS, DEFAULT_RADIUS, DOMAIN, FUEL_TYPES +async def async_get_nearby_stations( + hass: HomeAssistant, data: dict[str, Any] +) -> dict[str, Any]: + """Fetch nearby stations.""" + try: + return await hass.async_add_executor_job( + getNearbyStations, + data[CONF_API_KEY], + data[CONF_LOCATION][CONF_LATITUDE], + data[CONF_LOCATION][CONF_LONGITUDE], + data[CONF_RADIUS], + "all", + "dist", + ) + except customException as err: + return {"ok": False, "message": err, "exception": True} + + class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow.""" @@ -57,7 +75,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): selected_station_ids: list[str] = [] # add all nearby stations - nearby_stations = await self._get_nearby_stations(config) + nearby_stations = await async_get_nearby_stations(self.hass, config) for station in nearby_stations.get("stations", []): selected_station_ids.append(station["id"]) @@ -91,7 +109,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) self._abort_if_unique_id_configured() - data = await self._get_nearby_stations(user_input) + data = await async_get_nearby_stations(self.hass, user_input) if not data.get("ok"): return self._show_form_user( user_input, errors={CONF_API_KEY: "invalid_auth"} @@ -182,21 +200,6 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): options=options, ) - async def _get_nearby_stations(self, data: dict[str, Any]) -> dict[str, Any]: - """Fetch nearby stations.""" - try: - return await self.hass.async_add_executor_job( - getNearbyStations, - data[CONF_API_KEY], - data[CONF_LOCATION][CONF_LATITUDE], - data[CONF_LOCATION][CONF_LONGITUDE], - data[CONF_RADIUS], - "all", - "dist", - ) - except customException as err: - return {"ok": False, "message": err, "exception": True} - class OptionsFlowHandler(config_entries.OptionsFlow): """Handle an options flow.""" @@ -204,14 +207,36 @@ class OptionsFlowHandler(config_entries.OptionsFlow): def __init__(self, config_entry: config_entries.ConfigEntry) -> None: """Initialize options flow.""" self.config_entry = config_entry + self._stations: dict[str, str] = {} async def async_step_init( self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Handle options flow.""" if user_input is not None: + self.hass.config_entries.async_update_entry( + self.config_entry, + data={ + **self.config_entry.data, + CONF_STATIONS: user_input.pop(CONF_STATIONS), + }, + ) return self.async_create_entry(title="", data=user_input) + nearby_stations = await async_get_nearby_stations( + self.hass, dict(self.config_entry.data) + ) + if stations := nearby_stations.get("stations"): + for station in stations: + self._stations[ + station["id"] + ] = f"{station['brand']} {station['street']} {station['houseNumber']} - ({station['dist']}km)" + + # add possible extra selected stations from import + for selected_station in self.config_entry.data[CONF_STATIONS]: + if selected_station not in self._stations: + self._stations[selected_station] = f"id: {selected_station}" + return self.async_show_form( step_id="init", data_schema=vol.Schema( @@ -220,6 +245,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow): CONF_SHOW_ON_MAP, default=self.config_entry.options[CONF_SHOW_ON_MAP], ): bool, + vol.Required( + CONF_STATIONS, default=self.config_entry.data[CONF_STATIONS] + ): cv.multi_select(self._stations), } ), ) diff --git a/homeassistant/components/tankerkoenig/strings.json b/homeassistant/components/tankerkoenig/strings.json index 7c1ba54fcc0..5e0c367c192 100644 --- a/homeassistant/components/tankerkoenig/strings.json +++ b/homeassistant/components/tankerkoenig/strings.json @@ -32,7 +32,7 @@ "init": { "title": "Tankerkoenig options", "data": { - "scan_interval": "Update Interval", + "stations": "Stations", "show_on_map": "Show stations on map" } } diff --git a/homeassistant/components/tankerkoenig/translations/en.json b/homeassistant/components/tankerkoenig/translations/en.json index 399788de8f4..83cc36fd4c8 100644 --- a/homeassistant/components/tankerkoenig/translations/en.json +++ b/homeassistant/components/tankerkoenig/translations/en.json @@ -31,8 +31,8 @@ "step": { "init": { "data": { - "scan_interval": "Update Interval", - "show_on_map": "Show stations on map" + "show_on_map": "Show stations on map", + "stations": "Stations" }, "title": "Tankerkoenig options" } diff --git a/tests/components/tankerkoenig/test_config_flow.py b/tests/components/tankerkoenig/test_config_flow.py index 0a90b424b73..b18df0eed24 100644 --- a/tests/components/tankerkoenig/test_config_flow.py +++ b/tests/components/tankerkoenig/test_config_flow.py @@ -42,6 +42,15 @@ MOCK_STATIONS_DATA = { ], } +MOCK_OPTIONS_DATA = { + **MOCK_USER_DATA, + CONF_STATIONS: [ + "3bcd61da-xxxx-xxxx-xxxx-19d5523a7ae8", + "36b4b812-xxxx-xxxx-xxxx-c51735325858", + "54e2b642-xxxx-xxxx-xxxx-87cd4e9867f1", + ], +} + MOCK_IMPORT_DATA = { CONF_API_KEY: "269534f6-xxxx-xxxx-xxxx-yyyyzzzzxxxx", CONF_FUEL_TYPES: ["e5"], @@ -217,7 +226,7 @@ async def test_options_flow(hass: HomeAssistant): mock_config = MockConfigEntry( domain=DOMAIN, - data=MOCK_USER_DATA, + data=MOCK_OPTIONS_DATA, options={CONF_SHOW_ON_MAP: True}, unique_id=f"{DOMAIN}_{MOCK_USER_DATA[CONF_LOCATION][CONF_LATITUDE]}_{MOCK_USER_DATA[CONF_LOCATION][CONF_LONGITUDE]}", ) @@ -225,17 +234,23 @@ async def test_options_flow(hass: HomeAssistant): with patch( "homeassistant.components.tankerkoenig.async_setup_entry" - ) as mock_setup_entry: + ) as mock_setup_entry, patch( + "homeassistant.components.tankerkoenig.config_flow.getNearbyStations", + return_value=MOCK_NEARVY_STATIONS_OK, + ): await mock_config.async_setup(hass) await hass.async_block_till_done() assert mock_setup_entry.called - result = await hass.config_entries.options.async_init(mock_config.entry_id) + result = await hass.config_entries.options.async_init(mock_config.entry_id) assert result["type"] == RESULT_TYPE_FORM assert result["step_id"] == "init" result = await hass.config_entries.options.async_configure( result["flow_id"], - user_input={CONF_SHOW_ON_MAP: False}, + user_input={ + CONF_SHOW_ON_MAP: False, + CONF_STATIONS: MOCK_OPTIONS_DATA[CONF_STATIONS], + }, ) assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert not mock_config.options[CONF_SHOW_ON_MAP] From da62e2cc23207faec24f2f4cdc44baa66f4390e9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 28 May 2022 20:46:51 -0700 Subject: [PATCH 879/930] Bumped version to 2022.6.0b3 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index acad8f2675a..8d2bcd33f4e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b2" +PATCH_VERSION: Final = "0b3" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index b7841c53361..db64c7330ce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b2 +version = 2022.6.0b3 url = https://www.home-assistant.io/ [options] From f33517ef2c0635e601ebc3b1e484ef647211f71b Mon Sep 17 00:00:00 2001 From: Shawn Saenger Date: Sun, 29 May 2022 10:33:33 -0600 Subject: [PATCH 880/930] Incorporate various improvements for the ws66i integration (#71717) * Improve readability and remove unused code * Remove ws66i custom services. Scenes can be used instead. * Unmute WS66i Zone when volume changes * Raise CannotConnect instead of ConnectionError in validation method * Move _verify_connection() method to module level --- homeassistant/components/ws66i/__init__.py | 5 +- homeassistant/components/ws66i/config_flow.py | 48 +- homeassistant/components/ws66i/const.py | 6 +- homeassistant/components/ws66i/coordinator.py | 11 +- .../components/ws66i/media_player.py | 67 +-- homeassistant/components/ws66i/models.py | 2 - homeassistant/components/ws66i/services.yaml | 15 - homeassistant/components/ws66i/strings.json | 3 - .../components/ws66i/translations/en.json | 3 - tests/components/ws66i/test_config_flow.py | 6 +- tests/components/ws66i/test_init.py | 80 ++++ tests/components/ws66i/test_media_player.py | 414 +++++------------- 12 files changed, 251 insertions(+), 409 deletions(-) delete mode 100644 homeassistant/components/ws66i/services.yaml create mode 100644 tests/components/ws66i/test_init.py diff --git a/homeassistant/components/ws66i/__init__.py b/homeassistant/components/ws66i/__init__.py index 232c4390f19..dea1b470b9e 100644 --- a/homeassistant/components/ws66i/__init__.py +++ b/homeassistant/components/ws66i/__init__.py @@ -94,8 +94,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: zones=zones, ) + @callback def shutdown(event): - """Close the WS66i connection to the amplifier and save snapshots.""" + """Close the WS66i connection to the amplifier.""" ws66i.close() entry.async_on_unload(entry.add_update_listener(_update_listener)) @@ -119,6 +120,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unload_ok -async def _update_listener(hass: HomeAssistant, entry: ConfigEntry): +async def _update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: """Handle options update.""" await hass.config_entries.async_reload(entry.entry_id) diff --git a/homeassistant/components/ws66i/config_flow.py b/homeassistant/components/ws66i/config_flow.py index a8f098faadd..b84872da036 100644 --- a/homeassistant/components/ws66i/config_flow.py +++ b/homeassistant/components/ws66i/config_flow.py @@ -1,5 +1,6 @@ """Config flow for WS66i 6-Zone Amplifier integration.""" import logging +from typing import Any from pyws66i import WS66i, get_ws66i import voluptuous as vol @@ -50,22 +51,34 @@ def _sources_from_config(data): } -async def validate_input(hass: core.HomeAssistant, input_data): - """Validate the user input allows us to connect. +def _verify_connection(ws66i: WS66i) -> bool: + """Verify a connection can be made to the WS66i.""" + try: + ws66i.open() + except ConnectionError as err: + raise CannotConnect from err + + # Connection successful. Verify correct port was opened + # Test on FIRST_ZONE because this zone will always be valid + ret_val = ws66i.zone_status(FIRST_ZONE) + + ws66i.close() + + return bool(ret_val) + + +async def validate_input( + hass: core.HomeAssistant, input_data: dict[str, Any] +) -> dict[str, Any]: + """Validate the user input. Data has the keys from DATA_SCHEMA with values provided by the user. """ ws66i: WS66i = get_ws66i(input_data[CONF_IP_ADDRESS]) - await hass.async_add_executor_job(ws66i.open) - # No exception. run a simple test to make sure we opened correct port - # Test on FIRST_ZONE because this zone will always be valid - ret_val = await hass.async_add_executor_job(ws66i.zone_status, FIRST_ZONE) - if ret_val is None: - ws66i.close() - raise ConnectionError("Not a valid WS66i connection") - # Validation done. No issues. Close the connection - ws66i.close() + is_valid: bool = await hass.async_add_executor_job(_verify_connection, ws66i) + if not is_valid: + raise CannotConnect("Not a valid WS66i connection") # Return info that you want to store in the config entry. return {CONF_IP_ADDRESS: input_data[CONF_IP_ADDRESS]} @@ -82,17 +95,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if user_input is not None: try: info = await validate_input(self.hass, user_input) - # Data is valid. Add default values for options flow. + except CannotConnect: + errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + else: + # Data is valid. Create a config entry. return self.async_create_entry( title="WS66i Amp", data=info, options={CONF_SOURCES: INIT_OPTIONS_DEFAULT}, ) - except ConnectionError: - errors["base"] = "cannot_connect" - except Exception: # pylint: disable=broad-except - _LOGGER.exception("Unexpected exception") - errors["base"] = "unknown" return self.async_show_form( step_id="user", data_schema=DATA_SCHEMA, errors=errors diff --git a/homeassistant/components/ws66i/const.py b/homeassistant/components/ws66i/const.py index ec4439a690d..f824d991c1d 100644 --- a/homeassistant/components/ws66i/const.py +++ b/homeassistant/components/ws66i/const.py @@ -1,4 +1,5 @@ """Constants for the Soundavo WS66i 6-Zone Amplifier Media Player component.""" +from datetime import timedelta DOMAIN = "ws66i" @@ -20,5 +21,6 @@ INIT_OPTIONS_DEFAULT = { "6": "Source 6", } -SERVICE_SNAPSHOT = "snapshot" -SERVICE_RESTORE = "restore" +POLL_INTERVAL = timedelta(seconds=30) + +MAX_VOL = 38 diff --git a/homeassistant/components/ws66i/coordinator.py b/homeassistant/components/ws66i/coordinator.py index a9a274756b5..be8ae3aad38 100644 --- a/homeassistant/components/ws66i/coordinator.py +++ b/homeassistant/components/ws66i/coordinator.py @@ -1,7 +1,6 @@ """Coordinator for WS66i.""" from __future__ import annotations -from datetime import timedelta import logging from pyws66i import WS66i, ZoneStatus @@ -9,12 +8,12 @@ from pyws66i import WS66i, ZoneStatus from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from .const import POLL_INTERVAL + _LOGGER = logging.getLogger(__name__) -POLL_INTERVAL = timedelta(seconds=30) - -class Ws66iDataUpdateCoordinator(DataUpdateCoordinator): +class Ws66iDataUpdateCoordinator(DataUpdateCoordinator[list[ZoneStatus]]): """DataUpdateCoordinator to gather data for WS66i Zones.""" def __init__( @@ -43,11 +42,9 @@ class Ws66iDataUpdateCoordinator(DataUpdateCoordinator): data.append(data_zone) - # HA will call my entity's _handle_coordinator_update() return data async def _async_update_data(self) -> list[ZoneStatus]: """Fetch data for each of the zones.""" - # HA will call my entity's _handle_coordinator_update() - # The data I pass back here can be accessed through coordinator.data. + # The data that is returned here can be accessed through coordinator.data. return await self.hass.async_add_executor_job(self._update_all_zones) diff --git a/homeassistant/components/ws66i/media_player.py b/homeassistant/components/ws66i/media_player.py index c0e62fe773c..7cd897e9c1a 100644 --- a/homeassistant/components/ws66i/media_player.py +++ b/homeassistant/components/ws66i/media_player.py @@ -1,6 +1,4 @@ """Support for interfacing with WS66i 6 zone home audio controller.""" -from copy import deepcopy - from pyws66i import WS66i, ZoneStatus from homeassistant.components.media_player import ( @@ -10,22 +8,16 @@ from homeassistant.components.media_player import ( from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.entity_platform import ( - AddEntitiesCallback, - async_get_current_platform, -) +from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, SERVICE_RESTORE, SERVICE_SNAPSHOT +from .const import DOMAIN, MAX_VOL from .coordinator import Ws66iDataUpdateCoordinator from .models import Ws66iData PARALLEL_UPDATES = 1 -MAX_VOL = 38 - async def async_setup_entry( hass: HomeAssistant, @@ -48,23 +40,8 @@ async def async_setup_entry( for idx, zone_id in enumerate(ws66i_data.zones) ) - # Set up services - platform = async_get_current_platform() - platform.async_register_entity_service( - SERVICE_SNAPSHOT, - {}, - "snapshot", - ) - - platform.async_register_entity_service( - SERVICE_RESTORE, - {}, - "async_restore", - ) - - -class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): +class Ws66iZone(CoordinatorEntity[Ws66iDataUpdateCoordinator], MediaPlayerEntity): """Representation of a WS66i amplifier zone.""" def __init__( @@ -82,8 +59,6 @@ class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): self._ws66i_data: Ws66iData = ws66i_data self._zone_id: int = zone_id self._zone_id_idx: int = data_idx - self._coordinator = coordinator - self._snapshot: ZoneStatus = None self._status: ZoneStatus = coordinator.data[data_idx] self._attr_source_list = ws66i_data.sources.name_list self._attr_unique_id = f"{entry_id}_{self._zone_id}" @@ -131,20 +106,6 @@ class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): self._set_attrs_from_status() self.async_write_ha_state() - @callback - def snapshot(self): - """Save zone's current state.""" - self._snapshot = deepcopy(self._status) - - async def async_restore(self): - """Restore saved state.""" - if not self._snapshot: - raise HomeAssistantError("There is no snapshot to restore") - - await self.hass.async_add_executor_job(self._ws66i.restore_zone, self._snapshot) - self._status = self._snapshot - self._async_update_attrs_write_ha_state() - async def async_select_source(self, source): """Set input source.""" idx = self._ws66i_data.sources.name_id[source] @@ -180,24 +141,30 @@ class Ws66iZone(CoordinatorEntity, MediaPlayerEntity): async def async_set_volume_level(self, volume): """Set volume level, range 0..1.""" - await self.hass.async_add_executor_job( - self._ws66i.set_volume, self._zone_id, int(volume * MAX_VOL) - ) - self._status.volume = int(volume * MAX_VOL) + await self.hass.async_add_executor_job(self._set_volume, int(volume * MAX_VOL)) self._async_update_attrs_write_ha_state() async def async_volume_up(self): """Volume up the media player.""" await self.hass.async_add_executor_job( - self._ws66i.set_volume, self._zone_id, min(self._status.volume + 1, MAX_VOL) + self._set_volume, min(self._status.volume + 1, MAX_VOL) ) - self._status.volume = min(self._status.volume + 1, MAX_VOL) self._async_update_attrs_write_ha_state() async def async_volume_down(self): """Volume down media player.""" await self.hass.async_add_executor_job( - self._ws66i.set_volume, self._zone_id, max(self._status.volume - 1, 0) + self._set_volume, max(self._status.volume - 1, 0) ) - self._status.volume = max(self._status.volume - 1, 0) self._async_update_attrs_write_ha_state() + + def _set_volume(self, volume: int) -> None: + """Set the volume of the media player.""" + # Can't set a new volume level when this zone is muted. + # Follow behavior of keypads, where zone is unmuted when volume changes. + if self._status.mute: + self._ws66i.set_mute(self._zone_id, False) + self._status.mute = False + + self._ws66i.set_volume(self._zone_id, volume) + self._status.volume = volume diff --git a/homeassistant/components/ws66i/models.py b/homeassistant/components/ws66i/models.py index d84ee56a4a1..84f481b9a4a 100644 --- a/homeassistant/components/ws66i/models.py +++ b/homeassistant/components/ws66i/models.py @@ -7,8 +7,6 @@ from pyws66i import WS66i from .coordinator import Ws66iDataUpdateCoordinator -# A dataclass is basically a struct in C/C++ - @dataclass class SourceRep: diff --git a/homeassistant/components/ws66i/services.yaml b/homeassistant/components/ws66i/services.yaml deleted file mode 100644 index cedd1d3546a..00000000000 --- a/homeassistant/components/ws66i/services.yaml +++ /dev/null @@ -1,15 +0,0 @@ -snapshot: - name: Snapshot - description: Take a snapshot of the media player zone. - target: - entity: - integration: ws66i - domain: media_player - -restore: - name: Restore - description: Restore a snapshot of the media player zone. - target: - entity: - integration: ws66i - domain: media_player diff --git a/homeassistant/components/ws66i/strings.json b/homeassistant/components/ws66i/strings.json index fcfa64d7e22..ec5bc621a89 100644 --- a/homeassistant/components/ws66i/strings.json +++ b/homeassistant/components/ws66i/strings.json @@ -11,9 +11,6 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "unknown": "[%key:common::config_flow::error::unknown%]" - }, - "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" } }, "options": { diff --git a/homeassistant/components/ws66i/translations/en.json b/homeassistant/components/ws66i/translations/en.json index 30ef1e4205a..fd4b170b378 100644 --- a/homeassistant/components/ws66i/translations/en.json +++ b/homeassistant/components/ws66i/translations/en.json @@ -1,8 +1,5 @@ { "config": { - "abort": { - "already_configured": "Device is already configured" - }, "error": { "cannot_connect": "Failed to connect", "unknown": "Unexpected error" diff --git a/tests/components/ws66i/test_config_flow.py b/tests/components/ws66i/test_config_flow.py index d426e62c012..4fe3554941d 100644 --- a/tests/components/ws66i/test_config_flow.py +++ b/tests/components/ws66i/test_config_flow.py @@ -1,7 +1,7 @@ """Test the WS66i 6-Zone Amplifier config flow.""" from unittest.mock import patch -from homeassistant import config_entries, data_entry_flow, setup +from homeassistant import config_entries, data_entry_flow from homeassistant.components.ws66i.const import ( CONF_SOURCE_1, CONF_SOURCE_2, @@ -15,15 +15,15 @@ from homeassistant.components.ws66i.const import ( ) from homeassistant.const import CONF_IP_ADDRESS +from .test_media_player import AttrDict + from tests.common import MockConfigEntry -from tests.components.ws66i.test_media_player import AttrDict CONFIG = {CONF_IP_ADDRESS: "1.1.1.1"} async def test_form(hass): """Test we get the form.""" - await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) diff --git a/tests/components/ws66i/test_init.py b/tests/components/ws66i/test_init.py new file mode 100644 index 00000000000..557c53e97aa --- /dev/null +++ b/tests/components/ws66i/test_init.py @@ -0,0 +1,80 @@ +"""Test the WS66i 6-Zone Amplifier init file.""" +from unittest.mock import patch + +from homeassistant.components.ws66i.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState + +from .test_media_player import ( + MOCK_CONFIG, + MOCK_DEFAULT_OPTIONS, + MOCK_OPTIONS, + MockWs66i, +) + +from tests.common import MockConfigEntry + +ZONE_1_ID = "media_player.zone_11" + + +async def test_cannot_connect(hass): + """Test connection error.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: MockWs66i(fail_open=True), + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY + assert hass.states.get(ZONE_1_ID) is None + + +async def test_cannot_connect_2(hass): + """Test connection error pt 2.""" + # Another way to test same case as test_cannot_connect + ws66i = MockWs66i() + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_DEFAULT_OPTIONS + ) + config_entry.add_to_hass(hass) + + with patch.object(MockWs66i, "open", side_effect=ConnectionError): + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: ws66i, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY + assert hass.states.get(ZONE_1_ID) is None + + +async def test_unload_config_entry(hass): + """Test unloading config entry.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.ws66i.get_ws66i", + new=lambda *a: MockWs66i(), + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert hass.data[DOMAIN][config_entry.entry_id] + + with patch.object(MockWs66i, "close") as method_call: + await config_entry.async_unload(hass) + await hass.async_block_till_done() + + assert method_call.called + + assert not hass.data[DOMAIN] diff --git a/tests/components/ws66i/test_media_player.py b/tests/components/ws66i/test_media_player.py index 6fc1e00d827..fbe6a7b2782 100644 --- a/tests/components/ws66i/test_media_player.py +++ b/tests/components/ws66i/test_media_player.py @@ -2,28 +2,22 @@ from collections import defaultdict from unittest.mock import patch -import pytest - +from homeassistant.components.media_player import MediaPlayerEntityFeature from homeassistant.components.media_player.const import ( ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_VOLUME_LEVEL, DOMAIN as MEDIA_PLAYER_DOMAIN, SERVICE_SELECT_SOURCE, - SUPPORT_SELECT_SOURCE, - SUPPORT_TURN_OFF, - SUPPORT_TURN_ON, - SUPPORT_VOLUME_MUTE, - SUPPORT_VOLUME_SET, - SUPPORT_VOLUME_STEP, ) from homeassistant.components.ws66i.const import ( CONF_SOURCES, DOMAIN, INIT_OPTIONS_DEFAULT, - SERVICE_RESTORE, - SERVICE_SNAPSHOT, + MAX_VOL, + POLL_INTERVAL, ) +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import ( CONF_IP_ADDRESS, SERVICE_TURN_OFF, @@ -35,10 +29,10 @@ from homeassistant.const import ( STATE_ON, STATE_UNAVAILABLE, ) -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er +from homeassistant.util.dt import utcnow -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed MOCK_SOURCE_DIC = { "1": "one", @@ -125,47 +119,52 @@ class MockWs66i: async def test_setup_success(hass): """Test connection success.""" + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + with patch( "homeassistant.components.ws66i.get_ws66i", new=lambda *a: MockWs66i(), ): - config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS - ) - config_entry.add_to_hass(hass) await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - assert hass.states.get(ZONE_1_ID) is not None + + assert config_entry.state is ConfigEntryState.LOADED + assert hass.states.get(ZONE_1_ID) is not None async def _setup_ws66i(hass, ws66i) -> MockConfigEntry: + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_DEFAULT_OPTIONS + ) + config_entry.add_to_hass(hass) + with patch( "homeassistant.components.ws66i.get_ws66i", new=lambda *a: ws66i, ): - config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_DEFAULT_OPTIONS - ) - config_entry.add_to_hass(hass) await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - return config_entry + return config_entry async def _setup_ws66i_with_options(hass, ws66i) -> MockConfigEntry: + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS + ) + config_entry.add_to_hass(hass) + with patch( "homeassistant.components.ws66i.get_ws66i", new=lambda *a: ws66i, ): - config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS - ) - config_entry.add_to_hass(hass) await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - return config_entry + return config_entry async def _call_media_player_service(hass, name, data): @@ -174,172 +173,10 @@ async def _call_media_player_service(hass, name, data): ) -async def _call_ws66i_service(hass, name, data): - await hass.services.async_call(DOMAIN, name, service_data=data, blocking=True) - - -async def test_cannot_connect(hass): - """Test connection error.""" - with patch( - "homeassistant.components.ws66i.get_ws66i", - new=lambda *a: MockWs66i(fail_open=True), - ): - config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG) - config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - assert hass.states.get(ZONE_1_ID) is None - - -async def test_cannot_connect_2(hass): - """Test connection error pt 2.""" - # Another way to test same case as test_cannot_connect - ws66i = MockWs66i() - - with patch.object(MockWs66i, "open", side_effect=ConnectionError): - await _setup_ws66i(hass, ws66i) - assert hass.states.get(ZONE_1_ID) is None - - -async def test_service_calls_with_entity_id(hass): - """Test snapshot save/restore service calls.""" - _ = await _setup_ws66i_with_options(hass, MockWs66i()) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} - ) - - # Saving existing values - await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": ZONE_1_ID}) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} - ) - await hass.async_block_till_done() - - # Restoring other media player to its previous state - # The zone should not be restored - with pytest.raises(HomeAssistantError): - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_2_ID}) - await hass.async_block_till_done() - - # Checking that values were not (!) restored - state = hass.states.get(ZONE_1_ID) - - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "three" - - # Restoring media player to its previous state - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) - await hass.async_block_till_done() - - state = hass.states.get(ZONE_1_ID) - - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "one" - - -async def test_service_calls_with_all_entities(hass): - """Test snapshot save/restore service calls with entity id all.""" - _ = await _setup_ws66i_with_options(hass, MockWs66i()) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} - ) - - # Saving existing values - await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": "all"}) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} - ) - - # await coordinator.async_refresh() - # await hass.async_block_till_done() - - # Restoring media player to its previous state - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": "all"}) - await hass.async_block_till_done() - - state = hass.states.get(ZONE_1_ID) - - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "one" - - -async def test_service_calls_without_relevant_entities(hass): - """Test snapshot save/restore service calls with bad entity id.""" - config_entry = await _setup_ws66i_with_options(hass, MockWs66i()) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} - ) - - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator - await coordinator.async_refresh() - await hass.async_block_till_done() - - # Saving existing values - await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": "all"}) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "three"} - ) - - await coordinator.async_refresh() - await hass.async_block_till_done() - - # Restoring media player to its previous state - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": "light.demo"}) - await hass.async_block_till_done() - - state = hass.states.get(ZONE_1_ID) - - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 1.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "three" - - -async def test_restore_without_snapshot(hass): - """Test restore when snapshot wasn't called.""" - await _setup_ws66i(hass, MockWs66i()) - - with patch.object(MockWs66i, "restore_zone") as method_call: - with pytest.raises(HomeAssistantError): - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) - await hass.async_block_till_done() - - assert not method_call.called - - async def test_update(hass): """Test updating values from ws66i.""" ws66i = MockWs66i() - config_entry = await _setup_ws66i_with_options(hass, ws66i) + _ = await _setup_ws66i_with_options(hass, ws66i) # Changing media player to new state await _call_media_player_service( @@ -350,13 +187,10 @@ async def test_update(hass): ) ws66i.set_source(11, 3) - ws66i.set_volume(11, 38) - - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator + ws66i.set_volume(11, MAX_VOL) with patch.object(MockWs66i, "open") as method_call: - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() assert not method_call.called @@ -371,7 +205,7 @@ async def test_update(hass): async def test_failed_update(hass): """Test updating failure from ws66i.""" ws66i = MockWs66i() - config_entry = await _setup_ws66i_with_options(hass, ws66i) + _ = await _setup_ws66i_with_options(hass, ws66i) # Changing media player to new state await _call_media_player_service( @@ -382,26 +216,25 @@ async def test_failed_update(hass): ) ws66i.set_source(11, 3) - ws66i.set_volume(11, 38) - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator - await coordinator.async_refresh() + ws66i.set_volume(11, MAX_VOL) + + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() # Failed update, close called with patch.object(MockWs66i, "zone_status", return_value=None): - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() assert hass.states.is_state(ZONE_1_ID, STATE_UNAVAILABLE) # A connection re-attempt fails with patch.object(MockWs66i, "zone_status", return_value=None): - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() # A connection re-attempt succeeds - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() # confirm entity is back on @@ -418,12 +251,12 @@ async def test_supported_features(hass): state = hass.states.get(ZONE_1_ID) assert ( - SUPPORT_VOLUME_MUTE - | SUPPORT_VOLUME_SET - | SUPPORT_VOLUME_STEP - | SUPPORT_TURN_ON - | SUPPORT_TURN_OFF - | SUPPORT_SELECT_SOURCE + MediaPlayerEntityFeature.VOLUME_MUTE + | MediaPlayerEntityFeature.VOLUME_SET + | MediaPlayerEntityFeature.VOLUME_STEP + | MediaPlayerEntityFeature.TURN_ON + | MediaPlayerEntityFeature.TURN_OFF + | MediaPlayerEntityFeature.SELECT_SOURCE == state.attributes["supported_features"] ) @@ -462,15 +295,13 @@ async def test_select_source(hass): async def test_source_select(hass): - """Test behavior when device has unknown source.""" + """Test source selection simulated from keypad.""" ws66i = MockWs66i() - config_entry = await _setup_ws66i_with_options(hass, ws66i) + _ = await _setup_ws66i_with_options(hass, ws66i) ws66i.set_source(11, 5) - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() state = hass.states.get(ZONE_1_ID) @@ -512,10 +343,7 @@ async def test_mute_volume(hass): async def test_volume_up_down(hass): """Test increasing volume by one.""" ws66i = MockWs66i() - config_entry = await _setup_ws66i(hass, ws66i) - - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator + _ = await _setup_ws66i(hass, ws66i) await _call_media_player_service( hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} @@ -525,34 +353,89 @@ async def test_volume_up_down(hass): await _call_media_player_service( hass, SERVICE_VOLUME_DOWN, {"entity_id": ZONE_1_ID} ) - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() # should not go below zero assert ws66i.zones[11].volume == 0 await _call_media_player_service(hass, SERVICE_VOLUME_UP, {"entity_id": ZONE_1_ID}) - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() assert ws66i.zones[11].volume == 1 await _call_media_player_service( hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} ) - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() - assert ws66i.zones[11].volume == 38 + assert ws66i.zones[11].volume == MAX_VOL await _call_media_player_service(hass, SERVICE_VOLUME_UP, {"entity_id": ZONE_1_ID}) - await coordinator.async_refresh() + async_fire_time_changed(hass, utcnow() + POLL_INTERVAL) await hass.async_block_till_done() - # should not go above 38 - assert ws66i.zones[11].volume == 38 + # should not go above 38 (MAX_VOL) + assert ws66i.zones[11].volume == MAX_VOL await _call_media_player_service( hass, SERVICE_VOLUME_DOWN, {"entity_id": ZONE_1_ID} ) - assert ws66i.zones[11].volume == 37 + assert ws66i.zones[11].volume == MAX_VOL - 1 + + +async def test_volume_while_mute(hass): + """Test increasing volume by one.""" + ws66i = MockWs66i() + _ = await _setup_ws66i(hass, ws66i) + + # Set vol to a known value + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} + ) + assert ws66i.zones[11].volume == 0 + + # Set mute to a known value, False + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": False} + ) + assert not ws66i.zones[11].mute + + # Mute the zone + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": True} + ) + assert ws66i.zones[11].mute + + # Increase volume. Mute state should go back to unmutted + await _call_media_player_service(hass, SERVICE_VOLUME_UP, {"entity_id": ZONE_1_ID}) + assert ws66i.zones[11].volume == 1 + assert not ws66i.zones[11].mute + + # Mute the zone again + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": True} + ) + assert ws66i.zones[11].mute + + # Decrease volume. Mute state should go back to unmutted + await _call_media_player_service( + hass, SERVICE_VOLUME_DOWN, {"entity_id": ZONE_1_ID} + ) + assert ws66i.zones[11].volume == 0 + assert not ws66i.zones[11].mute + + # Mute the zone again + await _call_media_player_service( + hass, SERVICE_VOLUME_MUTE, {"entity_id": ZONE_1_ID, "is_volume_muted": True} + ) + assert ws66i.zones[11].mute + + # Set to max volume. Mute state should go back to unmutted + await _call_media_player_service( + hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} + ) + assert ws66i.zones[11].volume == MAX_VOL + assert not ws66i.zones[11].mute async def test_first_run_with_available_zones(hass): @@ -611,82 +494,3 @@ async def test_register_entities_in_1_amp_only(hass): entry = registry.async_get(ZONE_7_ID) assert entry is None - - -async def test_unload_config_entry(hass): - """Test unloading config entry.""" - with patch( - "homeassistant.components.ws66i.get_ws66i", - new=lambda *a: MockWs66i(), - ): - config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, options=MOCK_OPTIONS - ) - config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - assert hass.data[DOMAIN][config_entry.entry_id] - - with patch.object(MockWs66i, "close") as method_call: - await config_entry.async_unload(hass) - await hass.async_block_till_done() - - assert method_call.called - - assert not hass.data[DOMAIN] - - -async def test_restore_snapshot_on_reconnect(hass): - """Test restoring a saved snapshot when reconnecting to amp.""" - ws66i = MockWs66i() - config_entry = await _setup_ws66i_with_options(hass, ws66i) - - # Changing media player to new state - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 0.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "one"} - ) - - # Save a snapshot - await _call_ws66i_service(hass, SERVICE_SNAPSHOT, {"entity_id": ZONE_1_ID}) - - ws66i_data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = ws66i_data.coordinator - - # Failed update, - with patch.object(MockWs66i, "zone_status", return_value=None): - await coordinator.async_refresh() - await hass.async_block_till_done() - - assert hass.states.is_state(ZONE_1_ID, STATE_UNAVAILABLE) - - # A connection re-attempt succeeds - await coordinator.async_refresh() - await hass.async_block_till_done() - - # confirm entity is back on - state = hass.states.get(ZONE_1_ID) - - assert hass.states.is_state(ZONE_1_ID, STATE_ON) - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "one" - - # Change states - await _call_media_player_service( - hass, SERVICE_VOLUME_SET, {"entity_id": ZONE_1_ID, "volume_level": 1.0} - ) - await _call_media_player_service( - hass, SERVICE_SELECT_SOURCE, {"entity_id": ZONE_1_ID, "source": "six"} - ) - - # Now confirm that the snapshot before the disconnect works - await _call_ws66i_service(hass, SERVICE_RESTORE, {"entity_id": ZONE_1_ID}) - await hass.async_block_till_done() - - state = hass.states.get(ZONE_1_ID) - - assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.0 - assert state.attributes[ATTR_INPUT_SOURCE] == "one" From 6bf6a0f7bc292d175807d31b6d80e9b55bb1a76a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 May 2022 13:57:00 -0700 Subject: [PATCH 881/930] Convert media player enqueue to an enum (#72406) --- .../components/bluesound/media_player.py | 14 +------ homeassistant/components/heos/media_player.py | 16 +++++--- .../components/media_player/__init__.py | 37 ++++++++++++++++- .../media_player/reproduce_state.py | 3 +- .../components/media_player/services.yaml | 16 ++++++++ .../components/sonos/media_player.py | 9 ++--- .../components/squeezebox/media_player.py | 16 ++++---- tests/components/media_player/test_init.py | 40 +++++++++++++++++++ .../media_player/test_reproduce_state.py | 4 -- 9 files changed, 119 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/bluesound/media_player.py b/homeassistant/components/bluesound/media_player.py index e22606b795f..7f1c6b6553f 100644 --- a/homeassistant/components/bluesound/media_player.py +++ b/homeassistant/components/bluesound/media_player.py @@ -24,10 +24,7 @@ from homeassistant.components.media_player import ( from homeassistant.components.media_player.browse_media import ( async_process_play_media_url, ) -from homeassistant.components.media_player.const import ( - ATTR_MEDIA_ENQUEUE, - MEDIA_TYPE_MUSIC, -) +from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC from homeassistant.const import ( ATTR_ENTITY_ID, CONF_HOST, @@ -1023,11 +1020,7 @@ class BluesoundPlayer(MediaPlayerEntity): return await self.send_bluesound_command(f"Play?seek={float(position)}") async def async_play_media(self, media_type, media_id, **kwargs): - """ - Send the play_media command to the media player. - - If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue. - """ + """Send the play_media command to the media player.""" if self.is_grouped and not self.is_master: return @@ -1041,9 +1034,6 @@ class BluesoundPlayer(MediaPlayerEntity): url = f"Play?url={media_id}" - if kwargs.get(ATTR_MEDIA_ENQUEUE): - return await self.send_bluesound_command(url) - return await self.send_bluesound_command(url) async def async_volume_up(self): diff --git a/homeassistant/components/heos/media_player.py b/homeassistant/components/heos/media_player.py index 4cfbe5fe408..ad9225d9b21 100644 --- a/homeassistant/components/heos/media_player.py +++ b/homeassistant/components/heos/media_player.py @@ -12,6 +12,7 @@ from typing_extensions import ParamSpec from homeassistant.components import media_source from homeassistant.components.media_player import ( + MediaPlayerEnqueue, MediaPlayerEntity, MediaPlayerEntityFeature, ) @@ -73,6 +74,14 @@ CONTROL_TO_SUPPORT = { heos_const.CONTROL_PLAY_NEXT: MediaPlayerEntityFeature.NEXT_TRACK, } +HA_HEOS_ENQUEUE_MAP = { + None: heos_const.ADD_QUEUE_REPLACE_AND_PLAY, + MediaPlayerEnqueue.ADD: heos_const.ADD_QUEUE_ADD_TO_END, + MediaPlayerEnqueue.REPLACE: heos_const.ADD_QUEUE_REPLACE_AND_PLAY, + MediaPlayerEnqueue.NEXT: heos_const.ADD_QUEUE_PLAY_NEXT, + MediaPlayerEnqueue.PLAY: heos_const.ADD_QUEUE_PLAY_NOW, +} + _LOGGER = logging.getLogger(__name__) @@ -224,11 +233,8 @@ class HeosMediaPlayer(MediaPlayerEntity): playlist = next((p for p in playlists if p.name == media_id), None) if not playlist: raise ValueError(f"Invalid playlist '{media_id}'") - add_queue_option = ( - heos_const.ADD_QUEUE_ADD_TO_END - if kwargs.get(ATTR_MEDIA_ENQUEUE) - else heos_const.ADD_QUEUE_REPLACE_AND_PLAY - ) + add_queue_option = HA_HEOS_ENQUEUE_MAP.get(kwargs.get(ATTR_MEDIA_ENQUEUE)) + await self._player.add_to_queue(playlist, add_queue_option) return diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index bf006e2bd4e..f71f3fc2a1f 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -147,6 +147,19 @@ ENTITY_IMAGE_CACHE = {CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16 SCAN_INTERVAL = dt.timedelta(seconds=10) +class MediaPlayerEnqueue(StrEnum): + """Enqueue types for playing media.""" + + # add given media item to end of the queue + ADD = "add" + # play the given media item next, keep queue + NEXT = "next" + # play the given media item now, keep queue + PLAY = "play" + # play the given media item now, clear queue + REPLACE = "replace" + + class MediaPlayerDeviceClass(StrEnum): """Device class for media players.""" @@ -169,7 +182,9 @@ DEVICE_CLASS_RECEIVER = MediaPlayerDeviceClass.RECEIVER.value MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = { vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string, vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string, - vol.Optional(ATTR_MEDIA_ENQUEUE): cv.boolean, + vol.Optional(ATTR_MEDIA_ENQUEUE): vol.Any( + cv.boolean, vol.Coerce(MediaPlayerEnqueue) + ), vol.Optional(ATTR_MEDIA_EXTRA, default={}): dict, } @@ -350,10 +365,30 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: "async_select_sound_mode", [MediaPlayerEntityFeature.SELECT_SOUND_MODE], ) + + # Remove in Home Assistant 2022.9 + def _rewrite_enqueue(value): + """Rewrite the enqueue value.""" + if ATTR_MEDIA_ENQUEUE not in value: + pass + elif value[ATTR_MEDIA_ENQUEUE] is True: + value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.ADD + _LOGGER.warning( + "Playing media with enqueue set to True is deprecated. Use 'add' instead" + ) + elif value[ATTR_MEDIA_ENQUEUE] is False: + value[ATTR_MEDIA_ENQUEUE] = MediaPlayerEnqueue.PLAY + _LOGGER.warning( + "Playing media with enqueue set to False is deprecated. Use 'play' instead" + ) + + return value + component.async_register_entity_service( SERVICE_PLAY_MEDIA, vol.All( cv.make_entity_service_schema(MEDIA_PLAYER_PLAY_MEDIA_SCHEMA), + _rewrite_enqueue, _rename_keys( media_type=ATTR_MEDIA_CONTENT_TYPE, media_id=ATTR_MEDIA_CONTENT_ID, diff --git a/homeassistant/components/media_player/reproduce_state.py b/homeassistant/components/media_player/reproduce_state.py index 586ac61b4e1..bdfc0bf3acb 100644 --- a/homeassistant/components/media_player/reproduce_state.py +++ b/homeassistant/components/media_player/reproduce_state.py @@ -27,7 +27,6 @@ from .const import ( ATTR_INPUT_SOURCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, - ATTR_MEDIA_ENQUEUE, ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_SOUND_MODE, @@ -118,7 +117,7 @@ async def _async_reproduce_states( if features & MediaPlayerEntityFeature.PLAY_MEDIA: await call_service( SERVICE_PLAY_MEDIA, - [ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_ENQUEUE], + [ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID], ) already_playing = True diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 2e8585d0127..b2a8ac40262 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -151,6 +151,22 @@ play_media: selector: text: + enqueue: + name: Enqueue + description: If the content should be played now or be added to the queue. + required: false + selector: + select: + options: + - label: "Play now" + value: "play" + - label: "Play next" + value: "next" + - label: "Add to queue" + value: "add" + - label: "Play now and clear queue" + value: "replace" + select_source: name: Select source description: Send the media player the command to change input source. diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index 45e11a810ae..fd37e546105 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -18,6 +18,7 @@ import voluptuous as vol from homeassistant.components import media_source, spotify from homeassistant.components.media_player import ( + MediaPlayerEnqueue, MediaPlayerEntity, MediaPlayerEntityFeature, async_process_play_media_url, @@ -537,8 +538,6 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): If media_type is "playlist", media_id should be a Sonos Playlist name. Otherwise, media_id should be a URI. - - If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the queue. """ if spotify.is_spotify_media_type(media_type): media_type = spotify.resolve_spotify_media_type(media_type) @@ -575,7 +574,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): ) if result.shuffle: self.set_shuffle(True) - if kwargs.get(ATTR_MEDIA_ENQUEUE): + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: plex_plugin.add_to_queue(result.media) else: soco.clear_queue() @@ -585,7 +584,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): share_link = self.coordinator.share_link if share_link.is_share_link(media_id): - if kwargs.get(ATTR_MEDIA_ENQUEUE): + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: share_link.add_share_link_to_queue(media_id) else: soco.clear_queue() @@ -595,7 +594,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): # If media ID is a relative URL, we serve it from HA. media_id = async_process_play_media_url(self.hass, media_id) - if kwargs.get(ATTR_MEDIA_ENQUEUE): + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: soco.add_uri_to_queue(media_id) else: soco.play_uri(media_id, force_radio=is_radio) diff --git a/homeassistant/components/squeezebox/media_player.py b/homeassistant/components/squeezebox/media_player.py index d0d1cf89739..cd628a639c5 100644 --- a/homeassistant/components/squeezebox/media_player.py +++ b/homeassistant/components/squeezebox/media_player.py @@ -10,6 +10,7 @@ import voluptuous as vol from homeassistant.components import media_source from homeassistant.components.media_player import ( + MediaPlayerEnqueue, MediaPlayerEntity, MediaPlayerEntityFeature, ) @@ -469,16 +470,17 @@ class SqueezeBoxEntity(MediaPlayerEntity): await self._player.async_set_power(True) async def async_play_media(self, media_type, media_id, **kwargs): - """ - Send the play_media command to the media player. - - If ATTR_MEDIA_ENQUEUE is True, add `media_id` to the current playlist. - """ - cmd = "play" + """Send the play_media command to the media player.""" index = None - if kwargs.get(ATTR_MEDIA_ENQUEUE): + enqueue: MediaPlayerEnqueue | None = kwargs.get(ATTR_MEDIA_ENQUEUE) + + if enqueue == MediaPlayerEnqueue.ADD: cmd = "add" + elif enqueue == MediaPlayerEnqueue.NEXT: + cmd = "insert" + else: + cmd = "play" if media_source.is_media_source_id(media_id): media_type = MEDIA_TYPE_MUSIC diff --git a/tests/components/media_player/test_init.py b/tests/components/media_player/test_init.py index aa5e1b164f4..cb095cbcfe0 100644 --- a/tests/components/media_player/test_init.py +++ b/tests/components/media_player/test_init.py @@ -4,6 +4,8 @@ import base64 from http import HTTPStatus from unittest.mock import patch +import pytest + from homeassistant.components import media_player from homeassistant.components.media_player.browse_media import BrowseMedia from homeassistant.components.websocket_api.const import TYPE_RESULT @@ -251,3 +253,41 @@ async def test_group_members_available_when_off(hass): state = hass.states.get("media_player.bedroom") assert state.state == STATE_OFF assert "group_members" in state.attributes + + +@pytest.mark.parametrize( + "input,expected", + ( + (True, media_player.MediaPlayerEnqueue.ADD), + (False, media_player.MediaPlayerEnqueue.PLAY), + ("play", media_player.MediaPlayerEnqueue.PLAY), + ("next", media_player.MediaPlayerEnqueue.NEXT), + ("add", media_player.MediaPlayerEnqueue.ADD), + ("replace", media_player.MediaPlayerEnqueue.REPLACE), + ), +) +async def test_enqueue_rewrite(hass, input, expected): + """Test that group_members are still available when media_player is off.""" + await async_setup_component( + hass, "media_player", {"media_player": {"platform": "demo"}} + ) + await hass.async_block_till_done() + + # Fake group support for DemoYoutubePlayer + with patch( + "homeassistant.components.demo.media_player.DemoYoutubePlayer.play_media", + ) as mock_play_media: + await hass.services.async_call( + "media_player", + "play_media", + { + "entity_id": "media_player.bedroom", + "media_content_type": "music", + "media_content_id": "1234", + "enqueue": input, + }, + blocking=True, + ) + + assert len(mock_play_media.mock_calls) == 1 + assert mock_play_media.mock_calls[0][2]["enqueue"] == expected diff --git a/tests/components/media_player/test_reproduce_state.py b/tests/components/media_player/test_reproduce_state.py index f1a243337e1..f880130d4bd 100644 --- a/tests/components/media_player/test_reproduce_state.py +++ b/tests/components/media_player/test_reproduce_state.py @@ -6,7 +6,6 @@ from homeassistant.components.media_player.const import ( ATTR_INPUT_SOURCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, - ATTR_MEDIA_ENQUEUE, ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, ATTR_SOUND_MODE, @@ -253,7 +252,6 @@ async def test_play_media(hass): value_1 = "dummy_1" value_2 = "dummy_2" - value_3 = "dummy_3" await async_reproduce_states( hass, @@ -275,7 +273,6 @@ async def test_play_media(hass): { ATTR_MEDIA_CONTENT_TYPE: value_1, ATTR_MEDIA_CONTENT_ID: value_2, - ATTR_MEDIA_ENQUEUE: value_3, }, ) ], @@ -294,5 +291,4 @@ async def test_play_media(hass): "entity_id": ENTITY_1, ATTR_MEDIA_CONTENT_TYPE: value_1, ATTR_MEDIA_CONTENT_ID: value_2, - ATTR_MEDIA_ENQUEUE: value_3, } From ce4825c9e2886081ce8606336c2c7cd07ca918d0 Mon Sep 17 00:00:00 2001 From: Matrix Date: Mon, 30 May 2022 02:54:23 +0800 Subject: [PATCH 882/930] Fix yolink device unavailable on startup (#72579) * fetch device state on startup * Suggest change * suggest fix * fix * fix * Fix suggest * suggest fix --- homeassistant/components/yolink/__init__.py | 68 ++++++++++++--- .../components/yolink/binary_sensor.py | 44 ++++++---- homeassistant/components/yolink/const.py | 2 +- .../components/yolink/coordinator.py | 86 +++---------------- homeassistant/components/yolink/entity.py | 20 +++-- homeassistant/components/yolink/sensor.py | 38 ++++---- homeassistant/components/yolink/siren.py | 39 +++++---- homeassistant/components/yolink/switch.py | 41 +++++---- 8 files changed, 177 insertions(+), 161 deletions(-) diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 2c85344c54b..7eb6b0229f0 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -1,25 +1,28 @@ """The yolink integration.""" from __future__ import annotations +import asyncio from datetime import timedelta -import logging +import async_timeout from yolink.client import YoLinkClient +from yolink.device import YoLinkDevice +from yolink.exception import YoLinkAuthFailError, YoLinkClientError +from yolink.model import BRDP from yolink.mqtt_client import MqttClient from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow from . import api -from .const import ATTR_CLIENT, ATTR_COORDINATOR, ATTR_MQTT_CLIENT, DOMAIN +from .const import ATTR_CLIENT, ATTR_COORDINATORS, ATTR_DEVICE, ATTR_MQTT_CLIENT, DOMAIN from .coordinator import YoLinkCoordinator SCAN_INTERVAL = timedelta(minutes=5) -_LOGGER = logging.getLogger(__name__) PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SIREN, Platform.SWITCH] @@ -41,18 +44,63 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: yolink_http_client = YoLinkClient(auth_mgr) yolink_mqtt_client = MqttClient(auth_mgr) - coordinator = YoLinkCoordinator(hass, yolink_http_client, yolink_mqtt_client) - await coordinator.init_coordinator() + + def on_message_callback(message: tuple[str, BRDP]) -> None: + data = message[1] + device_id = message[0] + if data.event is None: + return + event_param = data.event.split(".") + event_type = event_param[len(event_param) - 1] + if event_type not in ( + "Report", + "Alert", + "StatusChange", + "getState", + ): + return + resolved_state = data.data + if resolved_state is None: + return + entry_data = hass.data[DOMAIN].get(entry.entry_id) + if entry_data is None: + return + device_coordinators = entry_data.get(ATTR_COORDINATORS) + if device_coordinators is None: + return + device_coordinator = device_coordinators.get(device_id) + if device_coordinator is None: + return + device_coordinator.async_set_updated_data(resolved_state) + try: - await coordinator.async_config_entry_first_refresh() - except ConfigEntryNotReady as ex: - _LOGGER.error("Fetching initial data failed: %s", ex) + async with async_timeout.timeout(10): + device_response = await yolink_http_client.get_auth_devices() + home_info = await yolink_http_client.get_general_info() + await yolink_mqtt_client.init_home_connection( + home_info.data["id"], on_message_callback + ) + except YoLinkAuthFailError as yl_auth_err: + raise ConfigEntryAuthFailed from yl_auth_err + except (YoLinkClientError, asyncio.TimeoutError) as err: + raise ConfigEntryNotReady from err hass.data[DOMAIN][entry.entry_id] = { ATTR_CLIENT: yolink_http_client, ATTR_MQTT_CLIENT: yolink_mqtt_client, - ATTR_COORDINATOR: coordinator, } + auth_devices = device_response.data[ATTR_DEVICE] + device_coordinators = {} + for device_info in auth_devices: + device = YoLinkDevice(device_info, yolink_http_client) + device_coordinator = YoLinkCoordinator(hass, device) + try: + await device_coordinator.async_config_entry_first_refresh() + except ConfigEntryNotReady: + # Not failure by fetching device state + device_coordinator.data = {} + device_coordinators[device.device_id] = device_coordinator + hass.data[DOMAIN][entry.entry_id][ATTR_COORDINATORS] = device_coordinators hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True diff --git a/homeassistant/components/yolink/binary_sensor.py b/homeassistant/components/yolink/binary_sensor.py index 42899e08a2c..cacba484fe9 100644 --- a/homeassistant/components/yolink/binary_sensor.py +++ b/homeassistant/components/yolink/binary_sensor.py @@ -3,6 +3,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from typing import Any from yolink.device import YoLinkDevice @@ -16,7 +17,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( - ATTR_COORDINATOR, + ATTR_COORDINATORS, ATTR_DEVICE_DOOR_SENSOR, ATTR_DEVICE_LEAK_SENSOR, ATTR_DEVICE_MOTION_SENSOR, @@ -32,7 +33,7 @@ class YoLinkBinarySensorEntityDescription(BinarySensorEntityDescription): exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True state_key: str = "state" - value: Callable[[str], bool | None] = lambda _: None + value: Callable[[Any], bool | None] = lambda _: None SENSOR_DEVICE_TYPE = [ @@ -47,14 +48,14 @@ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( icon="mdi:door", device_class=BinarySensorDeviceClass.DOOR, name="State", - value=lambda value: value == "open", + value=lambda value: value == "open" if value is not None else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR], ), YoLinkBinarySensorEntityDescription( key="motion_state", device_class=BinarySensorDeviceClass.MOTION, name="Motion", - value=lambda value: value == "alert", + value=lambda value: value == "alert" if value is not None else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_MOTION_SENSOR], ), YoLinkBinarySensorEntityDescription( @@ -62,7 +63,7 @@ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( name="Leak", icon="mdi:water", device_class=BinarySensorDeviceClass.MOISTURE, - value=lambda value: value == "alert", + value=lambda value: value == "alert" if value is not None else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_LEAK_SENSOR], ), ) @@ -74,18 +75,20 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up YoLink Sensor from a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] - sensor_devices = [ - device - for device in coordinator.yl_devices - if device.device_type in SENSOR_DEVICE_TYPE + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + binary_sensor_device_coordinators = [ + device_coordinator + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type in SENSOR_DEVICE_TYPE ] entities = [] - for sensor_device in sensor_devices: + for binary_sensor_device_coordinator in binary_sensor_device_coordinators: for description in SENSOR_TYPES: - if description.exists_fn(sensor_device): + if description.exists_fn(binary_sensor_device_coordinator.device): entities.append( - YoLinkBinarySensorEntity(coordinator, description, sensor_device) + YoLinkBinarySensorEntity( + binary_sensor_device_coordinator, description + ) ) async_add_entities(entities) @@ -99,18 +102,21 @@ class YoLinkBinarySensorEntity(YoLinkEntity, BinarySensorEntity): self, coordinator: YoLinkCoordinator, description: YoLinkBinarySensorEntityDescription, - device: YoLinkDevice, ) -> None: """Init YoLink Sensor.""" - super().__init__(coordinator, device) + super().__init__(coordinator) self.entity_description = description - self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" - self._attr_name = f"{device.device_name} ({self.entity_description.name})" + self._attr_unique_id = ( + f"{coordinator.device.device_id} {self.entity_description.key}" + ) + self._attr_name = ( + f"{coordinator.device.device_name} ({self.entity_description.name})" + ) @callback - def update_entity_state(self, state: dict) -> None: + def update_entity_state(self, state: dict[str, Any]) -> None: """Update HA Entity State.""" self._attr_is_on = self.entity_description.value( - state[self.entity_description.state_key] + state.get(self.entity_description.state_key) ) self.async_write_ha_state() diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index 00d6d6d028e..97252c5c989 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -5,7 +5,7 @@ MANUFACTURER = "YoLink" HOME_ID = "homeId" HOME_SUBSCRIPTION = "home_subscription" ATTR_PLATFORM_SENSOR = "sensor" -ATTR_COORDINATOR = "coordinator" +ATTR_COORDINATORS = "coordinators" ATTR_DEVICE = "devices" ATTR_DEVICE_TYPE = "type" ATTR_DEVICE_NAME = "name" diff --git a/homeassistant/components/yolink/coordinator.py b/homeassistant/components/yolink/coordinator.py index e5578eae4b2..68a1aef42f7 100644 --- a/homeassistant/components/yolink/coordinator.py +++ b/homeassistant/components/yolink/coordinator.py @@ -1,22 +1,18 @@ """YoLink DataUpdateCoordinator.""" from __future__ import annotations -import asyncio from datetime import timedelta import logging import async_timeout -from yolink.client import YoLinkClient from yolink.device import YoLinkDevice from yolink.exception import YoLinkAuthFailError, YoLinkClientError -from yolink.model import BRDP -from yolink.mqtt_client import MqttClient from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import ATTR_DEVICE, ATTR_DEVICE_STATE, DOMAIN +from .const import ATTR_DEVICE_STATE, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -24,9 +20,7 @@ _LOGGER = logging.getLogger(__name__) class YoLinkCoordinator(DataUpdateCoordinator[dict]): """YoLink DataUpdateCoordinator.""" - def __init__( - self, hass: HomeAssistant, yl_client: YoLinkClient, yl_mqtt_client: MqttClient - ) -> None: + def __init__(self, hass: HomeAssistant, device: YoLinkDevice) -> None: """Init YoLink DataUpdateCoordinator. fetch state every 30 minutes base on yolink device heartbeat interval @@ -35,75 +29,17 @@ class YoLinkCoordinator(DataUpdateCoordinator[dict]): super().__init__( hass, _LOGGER, name=DOMAIN, update_interval=timedelta(minutes=30) ) - self._client = yl_client - self._mqtt_client = yl_mqtt_client - self.yl_devices: list[YoLinkDevice] = [] - self.data = {} + self.device = device - def on_message_callback(self, message: tuple[str, BRDP]): - """On message callback.""" - data = message[1] - if data.event is None: - return - event_param = data.event.split(".") - event_type = event_param[len(event_param) - 1] - if event_type not in ( - "Report", - "Alert", - "StatusChange", - "getState", - ): - return - resolved_state = data.data - if resolved_state is None: - return - self.data[message[0]] = resolved_state - self.async_set_updated_data(self.data) - - async def init_coordinator(self): - """Init coordinator.""" + async def _async_update_data(self) -> dict: + """Fetch device state.""" try: async with async_timeout.timeout(10): - home_info = await self._client.get_general_info() - await self._mqtt_client.init_home_connection( - home_info.data["id"], self.on_message_callback - ) - async with async_timeout.timeout(10): - device_response = await self._client.get_auth_devices() - - except YoLinkAuthFailError as yl_auth_err: - raise ConfigEntryAuthFailed from yl_auth_err - - except (YoLinkClientError, asyncio.TimeoutError) as err: - raise ConfigEntryNotReady from err - - yl_devices: list[YoLinkDevice] = [] - - for device_info in device_response.data[ATTR_DEVICE]: - yl_devices.append(YoLinkDevice(device_info, self._client)) - - self.yl_devices = yl_devices - - async def fetch_device_state(self, device: YoLinkDevice): - """Fetch Device State.""" - try: - async with async_timeout.timeout(10): - device_state_resp = await device.fetch_state_with_api() - if ATTR_DEVICE_STATE in device_state_resp.data: - self.data[device.device_id] = device_state_resp.data[ - ATTR_DEVICE_STATE - ] + device_state_resp = await self.device.fetch_state_with_api() except YoLinkAuthFailError as yl_auth_err: raise ConfigEntryAuthFailed from yl_auth_err except YoLinkClientError as yl_client_err: - raise UpdateFailed( - f"Error communicating with API: {yl_client_err}" - ) from yl_client_err - - async def _async_update_data(self) -> dict: - fetch_tasks = [] - for yl_device in self.yl_devices: - fetch_tasks.append(self.fetch_device_state(yl_device)) - if fetch_tasks: - await asyncio.gather(*fetch_tasks) - return self.data + raise UpdateFailed from yl_client_err + if ATTR_DEVICE_STATE in device_state_resp.data: + return device_state_resp.data[ATTR_DEVICE_STATE] + return {} diff --git a/homeassistant/components/yolink/entity.py b/homeassistant/components/yolink/entity.py index 6954b117728..5365681739e 100644 --- a/homeassistant/components/yolink/entity.py +++ b/homeassistant/components/yolink/entity.py @@ -3,8 +3,6 @@ from __future__ import annotations from abc import abstractmethod -from yolink.device import YoLinkDevice - from homeassistant.core import callback from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -19,20 +17,24 @@ class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]): def __init__( self, coordinator: YoLinkCoordinator, - device_info: YoLinkDevice, ) -> None: """Init YoLink Entity.""" super().__init__(coordinator) - self.device = device_info @property def device_id(self) -> str: """Return the device id of the YoLink device.""" - return self.device.device_id + return self.coordinator.device.device_id + + async def async_added_to_hass(self) -> None: + """Update state.""" + await super().async_added_to_hass() + return self._handle_coordinator_update() @callback def _handle_coordinator_update(self) -> None: - data = self.coordinator.data.get(self.device.device_id) + """Update state.""" + data = self.coordinator.data if data is not None: self.update_entity_state(data) @@ -40,10 +42,10 @@ class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]): def device_info(self) -> DeviceInfo: """Return the device info for HA.""" return DeviceInfo( - identifiers={(DOMAIN, self.device.device_id)}, + identifiers={(DOMAIN, self.coordinator.device.device_id)}, manufacturer=MANUFACTURER, - model=self.device.device_type, - name=self.device.device_name, + model=self.coordinator.device.device_type, + name=self.coordinator.device.device_name, ) @callback diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index e33772c24be..463d8b14da4 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -19,7 +19,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import percentage from .const import ( - ATTR_COORDINATOR, + ATTR_COORDINATORS, ATTR_DEVICE_DOOR_SENSOR, ATTR_DEVICE_MOTION_SENSOR, ATTR_DEVICE_TH_SENSOR, @@ -54,7 +54,9 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( state_class=SensorStateClass.MEASUREMENT, value=lambda value: percentage.ordered_list_item_to_percentage( [1, 2, 3, 4], value - ), + ) + if value is not None + else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_DOOR_SENSOR, ATTR_DEVICE_TH_SENSOR, ATTR_DEVICE_MOTION_SENSOR], ), @@ -89,18 +91,21 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up YoLink Sensor from a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] - sensor_devices = [ - device - for device in coordinator.yl_devices - if device.device_type in SENSOR_DEVICE_TYPE + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + sensor_device_coordinators = [ + device_coordinator + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type in SENSOR_DEVICE_TYPE ] entities = [] - for sensor_device in sensor_devices: + for sensor_device_coordinator in sensor_device_coordinators: for description in SENSOR_TYPES: - if description.exists_fn(sensor_device): + if description.exists_fn(sensor_device_coordinator.device): entities.append( - YoLinkSensorEntity(coordinator, description, sensor_device) + YoLinkSensorEntity( + sensor_device_coordinator, + description, + ) ) async_add_entities(entities) @@ -114,18 +119,21 @@ class YoLinkSensorEntity(YoLinkEntity, SensorEntity): self, coordinator: YoLinkCoordinator, description: YoLinkSensorEntityDescription, - device: YoLinkDevice, ) -> None: """Init YoLink Sensor.""" - super().__init__(coordinator, device) + super().__init__(coordinator) self.entity_description = description - self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" - self._attr_name = f"{device.device_name} ({self.entity_description.name})" + self._attr_unique_id = ( + f"{coordinator.device.device_id} {self.entity_description.key}" + ) + self._attr_name = ( + f"{coordinator.device.device_name} ({self.entity_description.name})" + ) @callback def update_entity_state(self, state: dict) -> None: """Update HA Entity State.""" self._attr_native_value = self.entity_description.value( - state[self.entity_description.key] + state.get(self.entity_description.key) ) self.async_write_ha_state() diff --git a/homeassistant/components/yolink/siren.py b/homeassistant/components/yolink/siren.py index 7a621db6eca..7e67dfb12f1 100644 --- a/homeassistant/components/yolink/siren.py +++ b/homeassistant/components/yolink/siren.py @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_COORDINATOR, ATTR_DEVICE_SIREN, DOMAIN +from .const import ATTR_COORDINATORS, ATTR_DEVICE_SIREN, DOMAIN from .coordinator import YoLinkCoordinator from .entity import YoLinkEntity @@ -28,14 +28,14 @@ class YoLinkSirenEntityDescription(SirenEntityDescription): """YoLink SirenEntityDescription.""" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True - value: Callable[[str], bool | None] = lambda _: None + value: Callable[[Any], bool | None] = lambda _: None DEVICE_TYPES: tuple[YoLinkSirenEntityDescription, ...] = ( YoLinkSirenEntityDescription( key="state", name="State", - value=lambda value: value == "alert", + value=lambda value: value == "alert" if value is not None else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_SIREN], ), ) @@ -49,16 +49,20 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up YoLink siren from a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] - devices = [ - device for device in coordinator.yl_devices if device.device_type in DEVICE_TYPE + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + siren_device_coordinators = [ + device_coordinator + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type in DEVICE_TYPE ] entities = [] - for device in devices: + for siren_device_coordinator in siren_device_coordinators: for description in DEVICE_TYPES: - if description.exists_fn(device): + if description.exists_fn(siren_device_coordinator.device): entities.append( - YoLinkSirenEntity(config_entry, coordinator, description, device) + YoLinkSirenEntity( + config_entry, siren_device_coordinator, description + ) ) async_add_entities(entities) @@ -73,23 +77,26 @@ class YoLinkSirenEntity(YoLinkEntity, SirenEntity): config_entry: ConfigEntry, coordinator: YoLinkCoordinator, description: YoLinkSirenEntityDescription, - device: YoLinkDevice, ) -> None: """Init YoLink Siren.""" - super().__init__(coordinator, device) + super().__init__(coordinator) self.config_entry = config_entry self.entity_description = description - self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" - self._attr_name = f"{device.device_name} ({self.entity_description.name})" + self._attr_unique_id = ( + f"{coordinator.device.device_id} {self.entity_description.key}" + ) + self._attr_name = ( + f"{coordinator.device.device_name} ({self.entity_description.name})" + ) self._attr_supported_features = ( SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF ) @callback - def update_entity_state(self, state: dict) -> None: + def update_entity_state(self, state: dict[str, Any]) -> None: """Update HA Entity State.""" self._attr_is_on = self.entity_description.value( - state[self.entity_description.key] + state.get(self.entity_description.key) ) self.async_write_ha_state() @@ -97,7 +104,7 @@ class YoLinkSirenEntity(YoLinkEntity, SirenEntity): """Call setState api to change siren state.""" try: # call_device_http_api will check result, fail by raise YoLinkClientError - await self.device.call_device_http_api( + await self.coordinator.device.call_device_http_api( "setState", {"state": {"alarm": state}} ) except YoLinkAuthFailError as yl_auth_err: diff --git a/homeassistant/components/yolink/switch.py b/homeassistant/components/yolink/switch.py index b3756efb74c..f16dc781a9c 100644 --- a/homeassistant/components/yolink/switch.py +++ b/homeassistant/components/yolink/switch.py @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import ATTR_COORDINATOR, ATTR_DEVICE_OUTLET, DOMAIN +from .const import ATTR_COORDINATORS, ATTR_DEVICE_OUTLET, DOMAIN from .coordinator import YoLinkCoordinator from .entity import YoLinkEntity @@ -28,7 +28,7 @@ class YoLinkSwitchEntityDescription(SwitchEntityDescription): """YoLink SwitchEntityDescription.""" exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True - value: Callable[[str], bool | None] = lambda _: None + value: Callable[[Any], bool | None] = lambda _: None DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = ( @@ -36,7 +36,7 @@ DEVICE_TYPES: tuple[YoLinkSwitchEntityDescription, ...] = ( key="state", device_class=SwitchDeviceClass.OUTLET, name="State", - value=lambda value: value == "open", + value=lambda value: value == "open" if value is not None else None, exists_fn=lambda device: device.device_type in [ATTR_DEVICE_OUTLET], ), ) @@ -50,16 +50,20 @@ async def async_setup_entry( async_add_entities: AddEntitiesCallback, ) -> None: """Set up YoLink Sensor from a config entry.""" - coordinator = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATOR] - devices = [ - device for device in coordinator.yl_devices if device.device_type in DEVICE_TYPE + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + switch_device_coordinators = [ + device_coordinator + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type in DEVICE_TYPE ] entities = [] - for device in devices: + for switch_device_coordinator in switch_device_coordinators: for description in DEVICE_TYPES: - if description.exists_fn(device): + if description.exists_fn(switch_device_coordinator.device): entities.append( - YoLinkSwitchEntity(config_entry, coordinator, description, device) + YoLinkSwitchEntity( + config_entry, switch_device_coordinator, description + ) ) async_add_entities(entities) @@ -74,20 +78,23 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity): config_entry: ConfigEntry, coordinator: YoLinkCoordinator, description: YoLinkSwitchEntityDescription, - device: YoLinkDevice, ) -> None: """Init YoLink Outlet.""" - super().__init__(coordinator, device) + super().__init__(coordinator) self.config_entry = config_entry self.entity_description = description - self._attr_unique_id = f"{device.device_id} {self.entity_description.key}" - self._attr_name = f"{device.device_name} ({self.entity_description.name})" + self._attr_unique_id = ( + f"{coordinator.device.device_id} {self.entity_description.key}" + ) + self._attr_name = ( + f"{coordinator.device.device_name} ({self.entity_description.name})" + ) @callback - def update_entity_state(self, state: dict) -> None: + def update_entity_state(self, state: dict[str, Any]) -> None: """Update HA Entity State.""" self._attr_is_on = self.entity_description.value( - state[self.entity_description.key] + state.get(self.entity_description.key) ) self.async_write_ha_state() @@ -95,7 +102,9 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity): """Call setState api to change outlet state.""" try: # call_device_http_api will check result, fail by raise YoLinkClientError - await self.device.call_device_http_api("setState", {"state": state}) + await self.coordinator.device.call_device_http_api( + "setState", {"state": state} + ) except YoLinkAuthFailError as yl_auth_err: self.config_entry.async_start_reauth(self.hass) raise HomeAssistantError(yl_auth_err) from yl_auth_err From f41b2fa2cf9bda58690507ef08909f32055c934e Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Mon, 30 May 2022 23:37:28 +0200 Subject: [PATCH 883/930] Fix homewizard diagnostics and add tests (#72611) --- .coveragerc | 1 - .../components/homewizard/diagnostics.py | 9 ++-- tests/components/homewizard/conftest.py | 49 ++++++++++++++++++- .../components/homewizard/fixtures/data.json | 16 ++++++ .../homewizard/fixtures/device.json | 7 +++ .../components/homewizard/fixtures/state.json | 5 ++ .../components/homewizard/test_diagnostics.py | 47 ++++++++++++++++++ 7 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 tests/components/homewizard/fixtures/data.json create mode 100644 tests/components/homewizard/fixtures/device.json create mode 100644 tests/components/homewizard/fixtures/state.json create mode 100644 tests/components/homewizard/test_diagnostics.py diff --git a/.coveragerc b/.coveragerc index aced69a4714..62e6d3cb94e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -491,7 +491,6 @@ omit = homeassistant/components/homematic/* homeassistant/components/home_plus_control/api.py homeassistant/components/home_plus_control/switch.py - homeassistant/components/homewizard/diagnostics.py homeassistant/components/homeworks/* homeassistant/components/honeywell/__init__.py homeassistant/components/honeywell/climate.py diff --git a/homeassistant/components/homewizard/diagnostics.py b/homeassistant/components/homewizard/diagnostics.py index 3dd55933291..a97d2507098 100644 --- a/homeassistant/components/homewizard/diagnostics.py +++ b/homeassistant/components/homewizard/diagnostics.py @@ -1,6 +1,7 @@ """Diagnostics support for P1 Monitor.""" from __future__ import annotations +from dataclasses import asdict from typing import Any from homeassistant.components.diagnostics import async_redact_data @@ -21,10 +22,10 @@ async def async_get_config_entry_diagnostics( coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] meter_data = { - "device": coordinator.api.device.todict(), - "data": coordinator.api.data.todict(), - "state": coordinator.api.state.todict() - if coordinator.api.state is not None + "device": asdict(coordinator.data["device"]), + "data": asdict(coordinator.data["data"]), + "state": asdict(coordinator.data["state"]) + if coordinator.data["state"] is not None else None, } diff --git a/tests/components/homewizard/conftest.py b/tests/components/homewizard/conftest.py index 15993aa35ed..1617db35458 100644 --- a/tests/components/homewizard/conftest.py +++ b/tests/components/homewizard/conftest.py @@ -1,10 +1,15 @@ """Fixtures for HomeWizard integration tests.""" +import json +from unittest.mock import AsyncMock, patch + +from homewizard_energy.models import Data, Device, State import pytest from homeassistant.components.homewizard.const import DOMAIN from homeassistant.const import CONF_IP_ADDRESS +from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, load_fixture @pytest.fixture @@ -25,6 +30,46 @@ def mock_config_entry() -> MockConfigEntry: return MockConfigEntry( title="Product Name (aabbccddeeff)", domain=DOMAIN, - data={}, + data={CONF_IP_ADDRESS: "1.2.3.4"}, unique_id="aabbccddeeff", ) + + +@pytest.fixture +def mock_homewizardenergy(): + """Return a mocked P1 meter.""" + with patch( + "homeassistant.components.homewizard.coordinator.HomeWizardEnergy", + ) as device: + client = device.return_value + client.device = AsyncMock( + return_value=Device.from_dict( + json.loads(load_fixture("homewizard/device.json")) + ) + ) + client.data = AsyncMock( + return_value=Data.from_dict( + json.loads(load_fixture("homewizard/data.json")) + ) + ) + client.state = AsyncMock( + return_value=State.from_dict( + json.loads(load_fixture("homewizard/state.json")) + ) + ) + yield device + + +@pytest.fixture +async def init_integration( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_homewizardenergy: AsyncMock, +) -> MockConfigEntry: + """Set up the HomeWizard integration for testing.""" + mock_config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + return mock_config_entry diff --git a/tests/components/homewizard/fixtures/data.json b/tests/components/homewizard/fixtures/data.json new file mode 100644 index 00000000000..b6eada38038 --- /dev/null +++ b/tests/components/homewizard/fixtures/data.json @@ -0,0 +1,16 @@ +{ + "smr_version": 50, + "meter_model": "ISKRA 2M550T-101", + "wifi_ssid": "My Wi-Fi", + "wifi_strength": 100, + "total_power_import_t1_kwh": 1234.111, + "total_power_import_t2_kwh": 5678.222, + "total_power_export_t1_kwh": 4321.333, + "total_power_export_t2_kwh": 8765.444, + "active_power_w": -123, + "active_power_l1_w": -123, + "active_power_l2_w": 456, + "active_power_l3_w": 123.456, + "total_gas_m3": 1122.333, + "gas_timestamp": 210314112233 +} diff --git a/tests/components/homewizard/fixtures/device.json b/tests/components/homewizard/fixtures/device.json new file mode 100644 index 00000000000..493daa12b94 --- /dev/null +++ b/tests/components/homewizard/fixtures/device.json @@ -0,0 +1,7 @@ +{ + "product_type": "HWE-P1", + "product_name": "P1 Meter", + "serial": "3c39e7aabbcc", + "firmware_version": "2.11", + "api_version": "v1" +} diff --git a/tests/components/homewizard/fixtures/state.json b/tests/components/homewizard/fixtures/state.json new file mode 100644 index 00000000000..bbc0242ed58 --- /dev/null +++ b/tests/components/homewizard/fixtures/state.json @@ -0,0 +1,5 @@ +{ + "power_on": true, + "switch_lock": false, + "brightness": 255 +} diff --git a/tests/components/homewizard/test_diagnostics.py b/tests/components/homewizard/test_diagnostics.py new file mode 100644 index 00000000000..e477c94d914 --- /dev/null +++ b/tests/components/homewizard/test_diagnostics.py @@ -0,0 +1,47 @@ +"""Tests for diagnostics data.""" +from aiohttp import ClientSession + +from homeassistant.components.diagnostics import REDACTED +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry +from tests.components.diagnostics import get_diagnostics_for_config_entry + + +async def test_diagnostics( + hass: HomeAssistant, + hass_client: ClientSession, + init_integration: MockConfigEntry, +): + """Test diagnostics.""" + assert await get_diagnostics_for_config_entry( + hass, hass_client, init_integration + ) == { + "entry": {"ip_address": REDACTED}, + "data": { + "device": { + "product_name": "P1 Meter", + "product_type": "HWE-P1", + "serial": REDACTED, + "api_version": "v1", + "firmware_version": "2.11", + }, + "data": { + "smr_version": 50, + "meter_model": "ISKRA 2M550T-101", + "wifi_ssid": REDACTED, + "wifi_strength": 100, + "total_power_import_t1_kwh": 1234.111, + "total_power_import_t2_kwh": 5678.222, + "total_power_export_t1_kwh": 4321.333, + "total_power_export_t2_kwh": 8765.444, + "active_power_w": -123, + "active_power_l1_w": -123, + "active_power_l2_w": 456, + "active_power_l3_w": 123.456, + "total_gas_m3": 1122.333, + "gas_timestamp": "2021-03-14T11:22:33", + }, + "state": {"power_on": True, "switch_lock": False, "brightness": 255}, + }, + } From 4b524c077602db576e15c27ed813dbb0bc6c6774 Mon Sep 17 00:00:00 2001 From: BigMoby Date: Mon, 30 May 2022 08:26:05 +0200 Subject: [PATCH 884/930] iAlarm XR integration refinements (#72616) * fixing after MartinHjelmare review * fixing after MartinHjelmare review conversion alarm state to hass state * fixing after MartinHjelmare review conversion alarm state to hass state * manage the status in the alarm control * simplyfing return function --- .../components/ialarm_xr/__init__.py | 6 ++--- .../ialarm_xr/alarm_control_panel.py | 22 ++++++++++++++++--- .../components/ialarm_xr/config_flow.py | 4 ++-- homeassistant/components/ialarm_xr/const.py | 15 ------------- .../components/ialarm_xr/manifest.json | 4 ++-- .../components/ialarm_xr/strings.json | 1 + .../components/ialarm_xr/translations/en.json | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../components/ialarm_xr/test_config_flow.py | 22 ++----------------- tests/components/ialarm_xr/test_init.py | 10 --------- 11 files changed, 32 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/ialarm_xr/__init__.py b/homeassistant/components/ialarm_xr/__init__.py index 9a41b5ebab7..193bbe4fffc 100644 --- a/homeassistant/components/ialarm_xr/__init__.py +++ b/homeassistant/components/ialarm_xr/__init__.py @@ -24,7 +24,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .const import DOMAIN, IALARMXR_TO_HASS +from .const import DOMAIN from .utils import async_get_ialarmxr_mac PLATFORMS = [Platform.ALARM_CONTROL_PANEL] @@ -74,7 +74,7 @@ class IAlarmXRDataUpdateCoordinator(DataUpdateCoordinator): def __init__(self, hass: HomeAssistant, ialarmxr: IAlarmXR, mac: str) -> None: """Initialize global iAlarm data updater.""" self.ialarmxr: IAlarmXR = ialarmxr - self.state: str | None = None + self.state: int | None = None self.host: str = ialarmxr.host self.mac: str = mac @@ -90,7 +90,7 @@ class IAlarmXRDataUpdateCoordinator(DataUpdateCoordinator): status: int = self.ialarmxr.get_status() _LOGGER.debug("iAlarmXR status: %s", status) - self.state = IALARMXR_TO_HASS.get(status) + self.state = status async def _async_update_data(self) -> None: """Fetch data from iAlarmXR.""" diff --git a/homeassistant/components/ialarm_xr/alarm_control_panel.py b/homeassistant/components/ialarm_xr/alarm_control_panel.py index 7b47ce3d7fa..b64edb74391 100644 --- a/homeassistant/components/ialarm_xr/alarm_control_panel.py +++ b/homeassistant/components/ialarm_xr/alarm_control_panel.py @@ -1,11 +1,19 @@ """Interfaces with iAlarmXR control panels.""" from __future__ import annotations +from pyialarmxr import IAlarmXR + from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntity, AlarmControlPanelEntityFeature, ) from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, + STATE_ALARM_ARMED_HOME, + STATE_ALARM_DISARMED, + STATE_ALARM_TRIGGERED, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry from homeassistant.helpers.entity import DeviceInfo @@ -15,6 +23,13 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import IAlarmXRDataUpdateCoordinator from .const import DOMAIN +IALARMXR_TO_HASS = { + IAlarmXR.ARMED_AWAY: STATE_ALARM_ARMED_AWAY, + IAlarmXR.ARMED_STAY: STATE_ALARM_ARMED_HOME, + IAlarmXR.DISARMED: STATE_ALARM_DISARMED, + IAlarmXR.TRIGGERED: STATE_ALARM_TRIGGERED, +} + async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback @@ -24,7 +39,9 @@ async def async_setup_entry( async_add_entities([IAlarmXRPanel(coordinator)]) -class IAlarmXRPanel(CoordinatorEntity, AlarmControlPanelEntity): +class IAlarmXRPanel( + CoordinatorEntity[IAlarmXRDataUpdateCoordinator], AlarmControlPanelEntity +): """Representation of an iAlarmXR device.""" _attr_supported_features = ( @@ -37,7 +54,6 @@ class IAlarmXRPanel(CoordinatorEntity, AlarmControlPanelEntity): def __init__(self, coordinator: IAlarmXRDataUpdateCoordinator) -> None: """Initialize the alarm panel.""" super().__init__(coordinator) - self.coordinator: IAlarmXRDataUpdateCoordinator = coordinator self._attr_unique_id = coordinator.mac self._attr_device_info = DeviceInfo( manufacturer="Antifurto365 - Meian", @@ -48,7 +64,7 @@ class IAlarmXRPanel(CoordinatorEntity, AlarmControlPanelEntity): @property def state(self) -> str | None: """Return the state of the device.""" - return self.coordinator.state + return IALARMXR_TO_HASS.get(self.coordinator.state) def alarm_disarm(self, code: str | None = None) -> None: """Send disarm command.""" diff --git a/homeassistant/components/ialarm_xr/config_flow.py b/homeassistant/components/ialarm_xr/config_flow.py index 06509a82eb5..2a9cc406733 100644 --- a/homeassistant/components/ialarm_xr/config_flow.py +++ b/homeassistant/components/ialarm_xr/config_flow.py @@ -72,13 +72,13 @@ class IAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): "IAlarmXRGenericException with message: [ %s ]", ialarmxr_exception.message, ) - errors["base"] = "unknown" + errors["base"] = "cannot_connect" except IAlarmXRSocketTimeoutException as ialarmxr_socket_timeout_exception: _LOGGER.debug( "IAlarmXRSocketTimeoutException with message: [ %s ]", ialarmxr_socket_timeout_exception.message, ) - errors["base"] = "unknown" + errors["base"] = "timeout" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" diff --git a/homeassistant/components/ialarm_xr/const.py b/homeassistant/components/ialarm_xr/const.py index a208f5290b6..12122277340 100644 --- a/homeassistant/components/ialarm_xr/const.py +++ b/homeassistant/components/ialarm_xr/const.py @@ -1,18 +1,3 @@ """Constants for the iAlarmXR integration.""" -from pyialarmxr import IAlarmXR - -from homeassistant.const import ( - STATE_ALARM_ARMED_AWAY, - STATE_ALARM_ARMED_HOME, - STATE_ALARM_DISARMED, - STATE_ALARM_TRIGGERED, -) DOMAIN = "ialarm_xr" - -IALARMXR_TO_HASS = { - IAlarmXR.ARMED_AWAY: STATE_ALARM_ARMED_AWAY, - IAlarmXR.ARMED_STAY: STATE_ALARM_ARMED_HOME, - IAlarmXR.DISARMED: STATE_ALARM_DISARMED, - IAlarmXR.TRIGGERED: STATE_ALARM_TRIGGERED, -} diff --git a/homeassistant/components/ialarm_xr/manifest.json b/homeassistant/components/ialarm_xr/manifest.json index 4861e9c901f..f863f360242 100644 --- a/homeassistant/components/ialarm_xr/manifest.json +++ b/homeassistant/components/ialarm_xr/manifest.json @@ -1,8 +1,8 @@ { "domain": "ialarm_xr", "name": "Antifurto365 iAlarmXR", - "documentation": "https://www.home-assistant.io/integrations/ialarmxr", - "requirements": ["pyialarmxr==1.0.13"], + "documentation": "https://www.home-assistant.io/integrations/ialarm_xr", + "requirements": ["pyialarmxr==1.0.18"], "codeowners": ["@bigmoby"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/ialarm_xr/strings.json b/homeassistant/components/ialarm_xr/strings.json index 1650ae28c84..ea4f91fdbb9 100644 --- a/homeassistant/components/ialarm_xr/strings.json +++ b/homeassistant/components/ialarm_xr/strings.json @@ -12,6 +12,7 @@ }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "timeout": "[%key:common::config_flow::error::timeout_connect%]", "unknown": "[%key:common::config_flow::error::unknown%]" }, "abort": { diff --git a/homeassistant/components/ialarm_xr/translations/en.json b/homeassistant/components/ialarm_xr/translations/en.json index bf2bf989dcd..be59a5a1dc4 100644 --- a/homeassistant/components/ialarm_xr/translations/en.json +++ b/homeassistant/components/ialarm_xr/translations/en.json @@ -5,6 +5,7 @@ }, "error": { "cannot_connect": "Failed to connect", + "timeout": "Timeout establishing connection", "unknown": "Unexpected error" }, "step": { diff --git a/requirements_all.txt b/requirements_all.txt index f0cc4f6fb67..63f0f929c36 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1550,7 +1550,7 @@ pyhomeworks==0.0.6 pyialarm==1.9.0 # homeassistant.components.ialarm_xr -pyialarmxr==1.0.13 +pyialarmxr==1.0.18 # homeassistant.components.icloud pyicloud==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 783542a1c69..cb5f6a242e5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1038,7 +1038,7 @@ pyhomematic==0.1.77 pyialarm==1.9.0 # homeassistant.components.ialarm_xr -pyialarmxr==1.0.13 +pyialarmxr==1.0.18 # homeassistant.components.icloud pyicloud==1.0.0 diff --git a/tests/components/ialarm_xr/test_config_flow.py b/tests/components/ialarm_xr/test_config_flow.py index 22a70bda067..804249dd5cb 100644 --- a/tests/components/ialarm_xr/test_config_flow.py +++ b/tests/components/ialarm_xr/test_config_flow.py @@ -56,24 +56,6 @@ async def test_form(hass): assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_cannot_connect(hass): - """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.ialarm_xr.config_flow.IAlarmXR.get_mac", - side_effect=ConnectionError, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], TEST_DATA - ) - - assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result2["errors"] == {"base": "cannot_connect"} - - async def test_form_exception(hass): """Test we handle unknown exception.""" result = await hass.config_entries.flow.async_init( @@ -125,7 +107,7 @@ async def test_form_cannot_connect_throwing_socket_timeout_exception(hass): ) assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result2["errors"] == {"base": "unknown"} + assert result2["errors"] == {"base": "timeout"} async def test_form_cannot_connect_throwing_generic_exception(hass): @@ -143,7 +125,7 @@ async def test_form_cannot_connect_throwing_generic_exception(hass): ) assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result2["errors"] == {"base": "unknown"} + assert result2["errors"] == {"base": "cannot_connect"} async def test_form_already_exists(hass): diff --git a/tests/components/ialarm_xr/test_init.py b/tests/components/ialarm_xr/test_init.py index 8486b7049e6..0898b6bebf8 100644 --- a/tests/components/ialarm_xr/test_init.py +++ b/tests/components/ialarm_xr/test_init.py @@ -48,16 +48,6 @@ async def test_setup_entry(hass, ialarmxr_api, mock_config_entry): assert mock_config_entry.state is ConfigEntryState.LOADED -async def test_setup_not_ready(hass, ialarmxr_api, mock_config_entry): - """Test setup failed because we can't connect to the alarm system.""" - ialarmxr_api.return_value.get_mac = Mock(side_effect=ConnectionError) - - mock_config_entry.add_to_hass(hass) - assert not await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() - assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY - - async def test_unload_entry(hass, ialarmxr_api, mock_config_entry): """Test being able to unload an entry.""" ialarmxr_api.return_value.get_mac = Mock(return_value="00:00:54:12:34:56") From c62692dff14103e546fbc182270cff051464dae9 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 30 May 2022 15:36:58 -0600 Subject: [PATCH 885/930] Guard against missing data in 1st generation RainMachine controllers (#72632) --- .../components/rainmachine/binary_sensor.py | 16 +++---- .../components/rainmachine/sensor.py | 2 +- .../components/rainmachine/switch.py | 43 +++++++++++-------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index fb404adb199..730b51c142a 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -158,17 +158,17 @@ class CurrentRestrictionsBinarySensor(RainMachineEntity, BinarySensorEntity): def update_from_latest_data(self) -> None: """Update the state.""" if self.entity_description.key == TYPE_FREEZE: - self._attr_is_on = self.coordinator.data["freeze"] + self._attr_is_on = self.coordinator.data.get("freeze") elif self.entity_description.key == TYPE_HOURLY: - self._attr_is_on = self.coordinator.data["hourly"] + self._attr_is_on = self.coordinator.data.get("hourly") elif self.entity_description.key == TYPE_MONTH: - self._attr_is_on = self.coordinator.data["month"] + self._attr_is_on = self.coordinator.data.get("month") elif self.entity_description.key == TYPE_RAINDELAY: - self._attr_is_on = self.coordinator.data["rainDelay"] + self._attr_is_on = self.coordinator.data.get("rainDelay") elif self.entity_description.key == TYPE_RAINSENSOR: - self._attr_is_on = self.coordinator.data["rainSensor"] + self._attr_is_on = self.coordinator.data.get("rainSensor") elif self.entity_description.key == TYPE_WEEKDAY: - self._attr_is_on = self.coordinator.data["weekDay"] + self._attr_is_on = self.coordinator.data.get("weekDay") class ProvisionSettingsBinarySensor(RainMachineEntity, BinarySensorEntity): @@ -188,6 +188,6 @@ class UniversalRestrictionsBinarySensor(RainMachineEntity, BinarySensorEntity): def update_from_latest_data(self) -> None: """Update the state.""" if self.entity_description.key == TYPE_FREEZE_PROTECTION: - self._attr_is_on = self.coordinator.data["freezeProtectEnabled"] + self._attr_is_on = self.coordinator.data.get("freezeProtectEnabled") elif self.entity_description.key == TYPE_HOT_DAYS: - self._attr_is_on = self.coordinator.data["hotDaysExtraWatering"] + self._attr_is_on = self.coordinator.data.get("hotDaysExtraWatering") diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index b825faca7e1..a2b0f7cd539 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -198,7 +198,7 @@ class UniversalRestrictionsSensor(RainMachineEntity, SensorEntity): def update_from_latest_data(self) -> None: """Update the state.""" if self.entity_description.key == TYPE_FREEZE_TEMP: - self._attr_native_value = self.coordinator.data["freezeProtectTemp"] + self._attr_native_value = self.coordinator.data.get("freezeProtectTemp") class ZoneTimeRemainingSensor(RainMachineEntity, SensorEntity): diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index 007aec97a3e..a220aafa2a5 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -389,23 +389,32 @@ class RainMachineZone(RainMachineActivitySwitch): self._attr_is_on = bool(data["state"]) - self._attr_extra_state_attributes.update( - { - ATTR_AREA: round(data["waterSense"]["area"], 2), - ATTR_CURRENT_CYCLE: data["cycle"], - ATTR_FIELD_CAPACITY: round(data["waterSense"]["fieldCapacity"], 2), - ATTR_ID: data["uid"], - ATTR_NO_CYCLES: data["noOfCycles"], - ATTR_PRECIP_RATE: round(data["waterSense"]["precipitationRate"], 2), - ATTR_RESTRICTIONS: data["restriction"], - ATTR_SLOPE: SLOPE_TYPE_MAP.get(data["slope"], 99), - ATTR_SOIL_TYPE: SOIL_TYPE_MAP.get(data["soil"], 99), - ATTR_SPRINKLER_TYPE: SPRINKLER_TYPE_MAP.get(data["group_id"], 99), - ATTR_STATUS: RUN_STATE_MAP[data["state"]], - ATTR_SUN_EXPOSURE: SUN_EXPOSURE_MAP.get(data.get("sun")), - ATTR_VEGETATION_TYPE: VEGETATION_MAP.get(data["type"], 99), - } - ) + attrs = { + ATTR_CURRENT_CYCLE: data["cycle"], + ATTR_ID: data["uid"], + ATTR_NO_CYCLES: data["noOfCycles"], + ATTR_RESTRICTIONS: data("restriction"), + ATTR_SLOPE: SLOPE_TYPE_MAP.get(data["slope"], 99), + ATTR_SOIL_TYPE: SOIL_TYPE_MAP.get(data["soil"], 99), + ATTR_SPRINKLER_TYPE: SPRINKLER_TYPE_MAP.get(data["group_id"], 99), + ATTR_STATUS: RUN_STATE_MAP[data["state"]], + ATTR_SUN_EXPOSURE: SUN_EXPOSURE_MAP.get(data.get("sun")), + ATTR_VEGETATION_TYPE: VEGETATION_MAP.get(data["type"], 99), + } + + if "waterSense" in data: + if "area" in data["waterSense"]: + attrs[ATTR_AREA] = round(data["waterSense"]["area"], 2) + if "fieldCapacity" in data["waterSense"]: + attrs[ATTR_FIELD_CAPACITY] = round( + data["waterSense"]["fieldCapacity"], 2 + ) + if "precipitationRate" in data["waterSense"]: + attrs[ATTR_PRECIP_RATE] = round( + data["waterSense"]["precipitationRate"], 2 + ) + + self._attr_extra_state_attributes.update(attrs) class RainMachineZoneEnabled(RainMachineEnabledSwitch): From f039aac31c106c533c6907828f46fbfb0f3f9633 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sun, 29 May 2022 12:30:00 -0400 Subject: [PATCH 886/930] Fix zwave_js custom trigger validation bug (#72656) * Fix zwave_js custom trigger validation bug * update comments * Switch to ValueError * Switch to ValueError --- .../components/zwave_js/triggers/event.py | 36 +-- .../zwave_js/triggers/value_updated.py | 12 +- tests/components/zwave_js/test_trigger.py | 217 ++++++++++++++++++ 3 files changed, 241 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/zwave_js/triggers/event.py b/homeassistant/components/zwave_js/triggers/event.py index 17bb52fb392..784ae74777b 100644 --- a/homeassistant/components/zwave_js/triggers/event.py +++ b/homeassistant/components/zwave_js/triggers/event.py @@ -8,7 +8,7 @@ import voluptuous as vol from zwave_js_server.client import Client from zwave_js_server.model.controller import CONTROLLER_EVENT_MODEL_MAP from zwave_js_server.model.driver import DRIVER_EVENT_MODEL_MAP -from zwave_js_server.model.node import NODE_EVENT_MODEL_MAP, Node +from zwave_js_server.model.node import NODE_EVENT_MODEL_MAP from homeassistant.components.automation import ( AutomationActionType, @@ -20,7 +20,6 @@ from homeassistant.components.zwave_js.const import ( ATTR_EVENT_DATA, ATTR_EVENT_SOURCE, ATTR_NODE_ID, - ATTR_NODES, ATTR_PARTIAL_DICT_MATCH, DATA_CLIENT, DOMAIN, @@ -116,22 +115,20 @@ async def async_validate_trigger_config( """Validate config.""" config = TRIGGER_SCHEMA(config) + if ATTR_CONFIG_ENTRY_ID in config: + entry_id = config[ATTR_CONFIG_ENTRY_ID] + if hass.config_entries.async_get_entry(entry_id) is None: + raise vol.Invalid(f"Config entry '{entry_id}' not found") + if async_bypass_dynamic_config_validation(hass, config): return config - if config[ATTR_EVENT_SOURCE] == "node": - config[ATTR_NODES] = async_get_nodes_from_targets(hass, config) - if not config[ATTR_NODES]: - raise vol.Invalid( - f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s." - ) - - if ATTR_CONFIG_ENTRY_ID not in config: - return config - - entry_id = config[ATTR_CONFIG_ENTRY_ID] - if hass.config_entries.async_get_entry(entry_id) is None: - raise vol.Invalid(f"Config entry '{entry_id}' not found") + if config[ATTR_EVENT_SOURCE] == "node" and not async_get_nodes_from_targets( + hass, config + ): + raise vol.Invalid( + f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s." + ) return config @@ -145,7 +142,12 @@ async def async_attach_trigger( platform_type: str = PLATFORM_TYPE, ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" - nodes: set[Node] = config.get(ATTR_NODES, {}) + dev_reg = dr.async_get(hass) + nodes = async_get_nodes_from_targets(hass, config, dev_reg=dev_reg) + if config[ATTR_EVENT_SOURCE] == "node" and not nodes: + raise ValueError( + f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s." + ) event_source = config[ATTR_EVENT_SOURCE] event_name = config[ATTR_EVENT] @@ -200,8 +202,6 @@ async def async_attach_trigger( hass.async_run_hass_job(job, {"trigger": payload}) - dev_reg = dr.async_get(hass) - if not nodes: entry_id = config[ATTR_CONFIG_ENTRY_ID] client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT] diff --git a/homeassistant/components/zwave_js/triggers/value_updated.py b/homeassistant/components/zwave_js/triggers/value_updated.py index 4f15b87a6db..29b4b4d06d6 100644 --- a/homeassistant/components/zwave_js/triggers/value_updated.py +++ b/homeassistant/components/zwave_js/triggers/value_updated.py @@ -5,7 +5,6 @@ import functools import voluptuous as vol from zwave_js_server.const import CommandClass -from zwave_js_server.model.node import Node from zwave_js_server.model.value import Value, get_value_id from homeassistant.components.automation import ( @@ -20,7 +19,6 @@ from homeassistant.components.zwave_js.const import ( ATTR_CURRENT_VALUE_RAW, ATTR_ENDPOINT, ATTR_NODE_ID, - ATTR_NODES, ATTR_PREVIOUS_VALUE, ATTR_PREVIOUS_VALUE_RAW, ATTR_PROPERTY, @@ -79,8 +77,7 @@ async def async_validate_trigger_config( if async_bypass_dynamic_config_validation(hass, config): return config - config[ATTR_NODES] = async_get_nodes_from_targets(hass, config) - if not config[ATTR_NODES]: + if not async_get_nodes_from_targets(hass, config): raise vol.Invalid( f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s." ) @@ -96,7 +93,11 @@ async def async_attach_trigger( platform_type: str = PLATFORM_TYPE, ) -> CALLBACK_TYPE: """Listen for state changes based on configuration.""" - nodes: set[Node] = config[ATTR_NODES] + dev_reg = dr.async_get(hass) + if not (nodes := async_get_nodes_from_targets(hass, config, dev_reg=dev_reg)): + raise ValueError( + f"No nodes found for given {ATTR_DEVICE_ID}s or {ATTR_ENTITY_ID}s." + ) from_value = config[ATTR_FROM] to_value = config[ATTR_TO] @@ -163,7 +164,6 @@ async def async_attach_trigger( hass.async_run_hass_job(job, {"trigger": payload}) - dev_reg = dr.async_get(hass) for node in nodes: driver = node.client.driver assert driver is not None # The node comes from the driver. diff --git a/tests/components/zwave_js/test_trigger.py b/tests/components/zwave_js/test_trigger.py index 9758f566d81..48439eede0f 100644 --- a/tests/components/zwave_js/test_trigger.py +++ b/tests/components/zwave_js/test_trigger.py @@ -269,6 +269,122 @@ async def test_zwave_js_value_updated(hass, client, lock_schlage_be469, integrat await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True) +async def test_zwave_js_value_updated_bypass_dynamic_validation( + hass, client, lock_schlage_be469, integration +): + """Test zwave_js.value_updated trigger when bypassing dynamic validation.""" + trigger_type = f"{DOMAIN}.value_updated" + node: Node = lock_schlage_be469 + + no_value_filter = async_capture_events(hass, "no_value_filter") + + with patch( + "homeassistant.components.zwave_js.triggers.value_updated.async_bypass_dynamic_config_validation", + return_value=True, + ): + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + # no value filter + { + "trigger": { + "platform": trigger_type, + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + "action": { + "event": "no_value_filter", + }, + }, + ] + }, + ) + + # Test that no value filter is triggered + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": node.node_id, + "args": { + "commandClassName": "Door Lock", + "commandClass": 98, + "endpoint": 0, + "property": "latchStatus", + "newValue": "boo", + "prevValue": "hiss", + "propertyName": "latchStatus", + }, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + + assert len(no_value_filter) == 1 + + +async def test_zwave_js_value_updated_bypass_dynamic_validation_no_nodes( + hass, client, lock_schlage_be469, integration +): + """Test value_updated trigger when bypassing dynamic validation with no nodes.""" + trigger_type = f"{DOMAIN}.value_updated" + node: Node = lock_schlage_be469 + + no_value_filter = async_capture_events(hass, "no_value_filter") + + with patch( + "homeassistant.components.zwave_js.triggers.value_updated.async_bypass_dynamic_config_validation", + return_value=True, + ): + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + # no value filter + { + "trigger": { + "platform": trigger_type, + "entity_id": "sensor.test", + "command_class": CommandClass.DOOR_LOCK.value, + "property": "latchStatus", + }, + "action": { + "event": "no_value_filter", + }, + }, + ] + }, + ) + + # Test that no value filter is NOT triggered because automation failed setup + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": node.node_id, + "args": { + "commandClassName": "Door Lock", + "commandClass": 98, + "endpoint": 0, + "property": "latchStatus", + "newValue": "boo", + "prevValue": "hiss", + "propertyName": "latchStatus", + }, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + + assert len(no_value_filter) == 0 + + async def test_zwave_js_event(hass, client, lock_schlage_be469, integration): """Test for zwave_js.event automation trigger.""" trigger_type = f"{DOMAIN}.event" @@ -644,6 +760,107 @@ async def test_zwave_js_event(hass, client, lock_schlage_be469, integration): await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True) +async def test_zwave_js_event_bypass_dynamic_validation( + hass, client, lock_schlage_be469, integration +): + """Test zwave_js.event trigger when bypassing dynamic config validation.""" + trigger_type = f"{DOMAIN}.event" + node: Node = lock_schlage_be469 + + node_no_event_data_filter = async_capture_events(hass, "node_no_event_data_filter") + + with patch( + "homeassistant.components.zwave_js.triggers.event.async_bypass_dynamic_config_validation", + return_value=True, + ): + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + # node filter: no event data + { + "trigger": { + "platform": trigger_type, + "entity_id": SCHLAGE_BE469_LOCK_ENTITY, + "event_source": "node", + "event": "interview stage completed", + }, + "action": { + "event": "node_no_event_data_filter", + }, + }, + ] + }, + ) + + # Test that `node no event data filter` is triggered and `node event data filter` is not + event = Event( + type="interview stage completed", + data={ + "source": "node", + "event": "interview stage completed", + "stageName": "NodeInfo", + "nodeId": node.node_id, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + + assert len(node_no_event_data_filter) == 1 + + +async def test_zwave_js_event_bypass_dynamic_validation_no_nodes( + hass, client, lock_schlage_be469, integration +): + """Test event trigger when bypassing dynamic validation with no nodes.""" + trigger_type = f"{DOMAIN}.event" + node: Node = lock_schlage_be469 + + node_no_event_data_filter = async_capture_events(hass, "node_no_event_data_filter") + + with patch( + "homeassistant.components.zwave_js.triggers.event.async_bypass_dynamic_config_validation", + return_value=True, + ): + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + # node filter: no event data + { + "trigger": { + "platform": trigger_type, + "entity_id": "sensor.fake", + "event_source": "node", + "event": "interview stage completed", + }, + "action": { + "event": "node_no_event_data_filter", + }, + }, + ] + }, + ) + + # Test that `node no event data filter` is NOT triggered because automation failed + # setup + event = Event( + type="interview stage completed", + data={ + "source": "node", + "event": "interview stage completed", + "stageName": "NodeInfo", + "nodeId": node.node_id, + }, + ) + node.receive_event(event) + await hass.async_block_till_done() + + assert len(node_no_event_data_filter) == 0 + + async def test_zwave_js_event_invalid_config_entry_id( hass, client, integration, caplog ): From f8b7527bf0f526d2ccb42f1454b9b24a5dc44e06 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 30 May 2022 03:38:52 -0700 Subject: [PATCH 887/930] Allow removing a ring device (#72665) --- homeassistant/components/ring/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index a1ed1ac017b..0d8f87eef3c 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -16,6 +16,7 @@ from ring_doorbell import Auth, Ring from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform, __version__ from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType from homeassistant.util.async_ import run_callback_threadsafe @@ -146,6 +147,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + return True + + class GlobalDataUpdater: """Data storage for single API endpoint.""" From 6f01c13845e53c11498887374460aacbd1469f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 30 May 2022 03:14:43 +0200 Subject: [PATCH 888/930] Switch severity for gesture logging (#72668) --- homeassistant/components/nanoleaf/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nanoleaf/__init__.py b/homeassistant/components/nanoleaf/__init__.py index 9e9cf1d6ca4..f6fb2f8112b 100644 --- a/homeassistant/components/nanoleaf/__init__.py +++ b/homeassistant/components/nanoleaf/__init__.py @@ -85,9 +85,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Receive touch event.""" gesture_type = TOUCH_GESTURE_TRIGGER_MAP.get(event.gesture_id) if gesture_type is None: - _LOGGER.debug("Received unknown touch gesture ID %s", event.gesture_id) + _LOGGER.warning( + "Received unknown touch gesture ID %s", event.gesture_id + ) return - _LOGGER.warning("Received touch gesture %s", gesture_type) + _LOGGER.debug("Received touch gesture %s", gesture_type) hass.bus.async_fire( NANOLEAF_EVENT, {CONF_DEVICE_ID: device_entry.id, CONF_TYPE: gesture_type}, From 952433d16e4149a6cbb6236d3f491b866e7bb271 Mon Sep 17 00:00:00 2001 From: shbatm Date: Sun, 29 May 2022 11:00:18 -0500 Subject: [PATCH 889/930] Check ISY994 climate for unknown humidity on Z-Wave Thermostat (#72670) --- homeassistant/components/isy994/climate.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/isy994/climate.py b/homeassistant/components/isy994/climate.py index 1276207f23c..d68395f14da 100644 --- a/homeassistant/components/isy994/climate.py +++ b/homeassistant/components/isy994/climate.py @@ -6,6 +6,7 @@ from typing import Any from pyisy.constants import ( CMD_CLIMATE_FAN_SETTING, CMD_CLIMATE_MODE, + ISY_VALUE_UNKNOWN, PROP_HEAT_COOL_STATE, PROP_HUMIDITY, PROP_SETPOINT_COOL, @@ -116,6 +117,8 @@ class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): """Return the current humidity.""" if not (humidity := self._node.aux_properties.get(PROP_HUMIDITY)): return None + if humidity == ISY_VALUE_UNKNOWN: + return None return int(humidity.value) @property From 67ef3229fd11148d4f66712e412ee4102987515d Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sun, 29 May 2022 20:57:47 +0200 Subject: [PATCH 890/930] Address late review comments for Tankerkoenig (#72672) * address late review comment from #72654 * use entry_id instead of unique_id * remove not needed `_hass` property * fix skiping failing stations * remove not neccessary error log * set DeviceEntryType.SERVICE * fix use entry_id instead of unique_id * apply suggestions on tests * add return value also to other tests * invert data check to early return user form --- .../components/tankerkoenig/__init__.py | 45 +++++++++++++------ .../components/tankerkoenig/binary_sensor.py | 18 +++----- .../components/tankerkoenig/config_flow.py | 19 ++++---- .../components/tankerkoenig/sensor.py | 18 ++------ .../tankerkoenig/test_config_flow.py | 9 ++-- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/homeassistant/components/tankerkoenig/__init__.py b/homeassistant/components/tankerkoenig/__init__.py index 08520c8f5cc..e63add83fad 100644 --- a/homeassistant/components/tankerkoenig/__init__.py +++ b/homeassistant/components/tankerkoenig/__init__.py @@ -11,6 +11,7 @@ import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import ( + ATTR_ID, CONF_API_KEY, CONF_LATITUDE, CONF_LOCATION, @@ -24,8 +25,14 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.typing import ConfigType -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, + UpdateFailed, +) from .const import ( CONF_FUEL_TYPES, @@ -109,9 +116,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set a tankerkoenig configuration entry up.""" hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][ - entry.unique_id - ] = coordinator = TankerkoenigDataUpdateCoordinator( + hass.data[DOMAIN][entry.entry_id] = coordinator = TankerkoenigDataUpdateCoordinator( hass, entry, _LOGGER, @@ -140,7 +145,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload Tankerkoenig config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: - hass.data[DOMAIN].pop(entry.unique_id) + hass.data[DOMAIN].pop(entry.entry_id) return unload_ok @@ -172,7 +177,6 @@ class TankerkoenigDataUpdateCoordinator(DataUpdateCoordinator): self._api_key: str = entry.data[CONF_API_KEY] self._selected_stations: list[str] = entry.data[CONF_STATIONS] - self._hass = hass self.stations: dict[str, dict] = {} self.fuel_types: list[str] = entry.data[CONF_FUEL_TYPES] self.show_on_map: bool = entry.options[CONF_SHOW_ON_MAP] @@ -195,7 +199,7 @@ class TankerkoenigDataUpdateCoordinator(DataUpdateCoordinator): station_id, station_data["message"], ) - return False + continue self.add_station(station_data["station"]) if len(self.stations) > 10: _LOGGER.warning( @@ -215,7 +219,7 @@ class TankerkoenigDataUpdateCoordinator(DataUpdateCoordinator): # The API seems to only return at most 10 results, so split the list in chunks of 10 # and merge it together. for index in range(ceil(len(station_ids) / 10)): - data = await self._hass.async_add_executor_job( + data = await self.hass.async_add_executor_job( pytankerkoenig.getPriceList, self._api_key, station_ids[index * 10 : (index + 1) * 10], @@ -223,13 +227,11 @@ class TankerkoenigDataUpdateCoordinator(DataUpdateCoordinator): _LOGGER.debug("Received data: %s", data) if not data["ok"]: - _LOGGER.error( - "Error fetching data from tankerkoenig.de: %s", data["message"] - ) raise UpdateFailed(data["message"]) if "prices" not in data: - _LOGGER.error("Did not receive price information from tankerkoenig.de") - raise UpdateFailed("No prices in data") + raise UpdateFailed( + "Did not receive price information from tankerkoenig.de" + ) prices.update(data["prices"]) return prices @@ -244,3 +246,20 @@ class TankerkoenigDataUpdateCoordinator(DataUpdateCoordinator): self.stations[station_id] = station _LOGGER.debug("add_station called for station: %s", station) + + +class TankerkoenigCoordinatorEntity(CoordinatorEntity): + """Tankerkoenig base entity.""" + + def __init__( + self, coordinator: TankerkoenigDataUpdateCoordinator, station: dict + ) -> None: + """Initialize the Tankerkoenig base entity.""" + super().__init__(coordinator) + self._attr_device_info = DeviceInfo( + identifiers={(ATTR_ID, station["id"])}, + name=f"{station['brand']} {station['street']} {station['houseNumber']}", + model=station["brand"], + configuration_url="https://www.tankerkoenig.de", + entry_type=DeviceEntryType.SERVICE, + ) diff --git a/homeassistant/components/tankerkoenig/binary_sensor.py b/homeassistant/components/tankerkoenig/binary_sensor.py index 9a2b048e0b8..5f10b54f704 100644 --- a/homeassistant/components/tankerkoenig/binary_sensor.py +++ b/homeassistant/components/tankerkoenig/binary_sensor.py @@ -8,13 +8,11 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_ID, ATTR_LATITUDE, ATTR_LONGITUDE +from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import TankerkoenigDataUpdateCoordinator +from . import TankerkoenigCoordinatorEntity, TankerkoenigDataUpdateCoordinator from .const import DOMAIN _LOGGER = logging.getLogger(__name__) @@ -25,7 +23,7 @@ async def async_setup_entry( ) -> None: """Set up the tankerkoenig binary sensors.""" - coordinator: TankerkoenigDataUpdateCoordinator = hass.data[DOMAIN][entry.unique_id] + coordinator: TankerkoenigDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] stations = coordinator.stations.values() entities = [] @@ -41,7 +39,7 @@ async def async_setup_entry( async_add_entities(entities) -class StationOpenBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): +class StationOpenBinarySensorEntity(TankerkoenigCoordinatorEntity, BinarySensorEntity): """Shows if a station is open or closed.""" _attr_device_class = BinarySensorDeviceClass.DOOR @@ -53,18 +51,12 @@ class StationOpenBinarySensorEntity(CoordinatorEntity, BinarySensorEntity): show_on_map: bool, ) -> None: """Initialize the sensor.""" - super().__init__(coordinator) + super().__init__(coordinator, station) self._station_id = station["id"] self._attr_name = ( f"{station['brand']} {station['street']} {station['houseNumber']} status" ) self._attr_unique_id = f"{station['id']}_status" - self._attr_device_info = DeviceInfo( - identifiers={(ATTR_ID, station["id"])}, - name=f"{station['brand']} {station['street']} {station['houseNumber']}", - model=station["brand"], - configuration_url="https://www.tankerkoenig.de", - ) if show_on_map: self._attr_extra_state_attributes = { ATTR_LATITUDE: station["lat"], diff --git a/homeassistant/components/tankerkoenig/config_flow.py b/homeassistant/components/tankerkoenig/config_flow.py index af3b5273b16..345b034b027 100644 --- a/homeassistant/components/tankerkoenig/config_flow.py +++ b/homeassistant/components/tankerkoenig/config_flow.py @@ -1,6 +1,7 @@ """Config flow for Tankerkoenig.""" from __future__ import annotations +from collections.abc import Mapping from typing import Any from pytankerkoenig import customException, getNearbyStations @@ -30,7 +31,7 @@ from .const import CONF_FUEL_TYPES, CONF_STATIONS, DEFAULT_RADIUS, DOMAIN, FUEL_ async def async_get_nearby_stations( - hass: HomeAssistant, data: dict[str, Any] + hass: HomeAssistant, data: Mapping[str, Any] ) -> dict[str, Any]: """Fetch nearby stations.""" try: @@ -114,14 +115,12 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return self._show_form_user( user_input, errors={CONF_API_KEY: "invalid_auth"} ) - if stations := data.get("stations"): - for station in stations: - self._stations[ - station["id"] - ] = f"{station['brand']} {station['street']} {station['houseNumber']} - ({station['dist']}km)" - - else: + if len(stations := data.get("stations", [])) == 0: return self._show_form_user(user_input, errors={CONF_RADIUS: "no_stations"}) + for station in stations: + self._stations[ + station["id"] + ] = f"{station['brand']} {station['street']} {station['houseNumber']} - ({station['dist']}km)" self._data = user_input @@ -180,7 +179,7 @@ class FlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_RADIUS, default=user_input.get(CONF_RADIUS, DEFAULT_RADIUS) ): NumberSelector( NumberSelectorConfig( - min=0.1, + min=1.0, max=25, step=0.1, unit_of_measurement=LENGTH_KILOMETERS, @@ -224,7 +223,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return self.async_create_entry(title="", data=user_input) nearby_stations = await async_get_nearby_stations( - self.hass, dict(self.config_entry.data) + self.hass, self.config_entry.data ) if stations := nearby_stations.get("stations"): for station in stations: diff --git a/homeassistant/components/tankerkoenig/sensor.py b/homeassistant/components/tankerkoenig/sensor.py index 898a38c3c14..c63b0ea0e7e 100644 --- a/homeassistant/components/tankerkoenig/sensor.py +++ b/homeassistant/components/tankerkoenig/sensor.py @@ -7,17 +7,14 @@ from homeassistant.components.sensor import SensorEntity, SensorStateClass from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_ATTRIBUTION, - ATTR_ID, ATTR_LATITUDE, ATTR_LONGITUDE, CURRENCY_EURO, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import TankerkoenigDataUpdateCoordinator +from . import TankerkoenigCoordinatorEntity, TankerkoenigDataUpdateCoordinator from .const import ( ATTR_BRAND, ATTR_CITY, @@ -39,7 +36,7 @@ async def async_setup_entry( ) -> None: """Set up the tankerkoenig sensors.""" - coordinator: TankerkoenigDataUpdateCoordinator = hass.data[DOMAIN][entry.unique_id] + coordinator: TankerkoenigDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] stations = coordinator.stations.values() entities = [] @@ -62,7 +59,7 @@ async def async_setup_entry( async_add_entities(entities) -class FuelPriceSensor(CoordinatorEntity, SensorEntity): +class FuelPriceSensor(TankerkoenigCoordinatorEntity, SensorEntity): """Contains prices for fuel in a given station.""" _attr_state_class = SensorStateClass.MEASUREMENT @@ -70,19 +67,12 @@ class FuelPriceSensor(CoordinatorEntity, SensorEntity): def __init__(self, fuel_type, station, coordinator, show_on_map): """Initialize the sensor.""" - super().__init__(coordinator) + super().__init__(coordinator, station) self._station_id = station["id"] self._fuel_type = fuel_type self._attr_name = f"{station['brand']} {station['street']} {station['houseNumber']} {FUEL_TYPES[fuel_type]}" self._attr_native_unit_of_measurement = CURRENCY_EURO self._attr_unique_id = f"{station['id']}_{fuel_type}" - self._attr_device_info = DeviceInfo( - identifiers={(ATTR_ID, station["id"])}, - name=f"{station['brand']} {station['street']} {station['houseNumber']}", - model=station["brand"], - configuration_url="https://www.tankerkoenig.de", - ) - attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, ATTR_BRAND: station["brand"], diff --git a/tests/components/tankerkoenig/test_config_flow.py b/tests/components/tankerkoenig/test_config_flow.py index b18df0eed24..f48a09fd64b 100644 --- a/tests/components/tankerkoenig/test_config_flow.py +++ b/tests/components/tankerkoenig/test_config_flow.py @@ -95,7 +95,7 @@ async def test_user(hass: HomeAssistant): assert result["step_id"] == "user" with patch( - "homeassistant.components.tankerkoenig.async_setup_entry" + "homeassistant.components.tankerkoenig.async_setup_entry", return_value=True ) as mock_setup_entry, patch( "homeassistant.components.tankerkoenig.config_flow.getNearbyStations", return_value=MOCK_NEARVY_STATIONS_OK, @@ -147,6 +147,7 @@ async def test_user_already_configured(hass: HomeAssistant): ) assert result["type"] == RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" async def test_exception_security(hass: HomeAssistant): @@ -193,7 +194,7 @@ async def test_user_no_stations(hass: HomeAssistant): async def test_import(hass: HomeAssistant): """Test starting a flow by import.""" with patch( - "homeassistant.components.tankerkoenig.async_setup_entry" + "homeassistant.components.tankerkoenig.async_setup_entry", return_value=True ) as mock_setup_entry, patch( "homeassistant.components.tankerkoenig.config_flow.getNearbyStations", return_value=MOCK_NEARVY_STATIONS_OK, @@ -233,12 +234,12 @@ async def test_options_flow(hass: HomeAssistant): mock_config.add_to_hass(hass) with patch( - "homeassistant.components.tankerkoenig.async_setup_entry" + "homeassistant.components.tankerkoenig.async_setup_entry", return_value=True ) as mock_setup_entry, patch( "homeassistant.components.tankerkoenig.config_flow.getNearbyStations", return_value=MOCK_NEARVY_STATIONS_OK, ): - await mock_config.async_setup(hass) + await hass.config_entries.async_setup(mock_config.entry_id) await hass.async_block_till_done() assert mock_setup_entry.called From 2942986a7b08571c3b90e0bece7fb37b02072060 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Mon, 30 May 2022 08:52:58 +0200 Subject: [PATCH 891/930] Bump bimmer_connected to 0.9.3 (#72677) Bump bimmer_connected to 0.9.3, fix retrieved units Co-authored-by: rikroe --- homeassistant/components/bmw_connected_drive/coordinator.py | 3 ++- homeassistant/components/bmw_connected_drive/manifest.json | 2 +- homeassistant/components/bmw_connected_drive/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/coordinator.py b/homeassistant/components/bmw_connected_drive/coordinator.py index cff532ae3cb..47d1f358686 100644 --- a/homeassistant/components/bmw_connected_drive/coordinator.py +++ b/homeassistant/components/bmw_connected_drive/coordinator.py @@ -6,7 +6,7 @@ import logging from bimmer_connected.account import MyBMWAccount from bimmer_connected.api.regions import get_region_from_name -from bimmer_connected.vehicle.models import GPSPosition +from bimmer_connected.models import GPSPosition from httpx import HTTPError, TimeoutException from homeassistant.config_entries import ConfigEntry @@ -32,6 +32,7 @@ class BMWDataUpdateCoordinator(DataUpdateCoordinator): entry.data[CONF_PASSWORD], get_region_from_name(entry.data[CONF_REGION]), observer_position=GPSPosition(hass.config.latitude, hass.config.longitude), + use_metric_units=hass.config.units.is_metric, ) self.read_only = entry.options[CONF_READ_ONLY] self._entry = entry diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index c7130d12698..75ac3e982e8 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -2,7 +2,7 @@ "domain": "bmw_connected_drive", "name": "BMW Connected Drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", - "requirements": ["bimmer_connected==0.9.2"], + "requirements": ["bimmer_connected==0.9.3"], "codeowners": ["@gerard33", "@rikroe"], "config_flow": true, "iot_class": "cloud_polling", diff --git a/homeassistant/components/bmw_connected_drive/sensor.py b/homeassistant/components/bmw_connected_drive/sensor.py index 3021e180158..9f19673c398 100644 --- a/homeassistant/components/bmw_connected_drive/sensor.py +++ b/homeassistant/components/bmw_connected_drive/sensor.py @@ -6,8 +6,8 @@ from dataclasses import dataclass import logging from typing import cast +from bimmer_connected.models import ValueWithUnit from bimmer_connected.vehicle import MyBMWVehicle -from bimmer_connected.vehicle.models import ValueWithUnit from homeassistant.components.sensor import ( SensorDeviceClass, diff --git a/requirements_all.txt b/requirements_all.txt index 63f0f929c36..7ecc9b6769c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -394,7 +394,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.9.2 +bimmer_connected==0.9.3 # homeassistant.components.bizkaibus bizkaibus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cb5f6a242e5..eaf81f1b07f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -309,7 +309,7 @@ beautifulsoup4==4.11.1 bellows==0.30.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.9.2 +bimmer_connected==0.9.3 # homeassistant.components.blebox blebox_uniapi==1.3.3 From da7446bf520a9d6cfcf282fac5810f6f1b685111 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 30 May 2022 11:40:36 +0200 Subject: [PATCH 892/930] Bump hatasmota to 0.5.1 (#72696) --- .../components/tasmota/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/tasmota/test_cover.py | 45 ++++++++++++------- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/tasmota/manifest.json b/homeassistant/components/tasmota/manifest.json index 772105043fe..4268c4198b2 100644 --- a/homeassistant/components/tasmota/manifest.json +++ b/homeassistant/components/tasmota/manifest.json @@ -3,7 +3,7 @@ "name": "Tasmota", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/tasmota", - "requirements": ["hatasmota==0.5.0"], + "requirements": ["hatasmota==0.5.1"], "dependencies": ["mqtt"], "mqtt": ["tasmota/discovery/#"], "codeowners": ["@emontnemery"], diff --git a/requirements_all.txt b/requirements_all.txt index 7ecc9b6769c..e15aff574d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -795,7 +795,7 @@ hass-nabucasa==0.54.0 hass_splunk==0.1.1 # homeassistant.components.tasmota -hatasmota==0.5.0 +hatasmota==0.5.1 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index eaf81f1b07f..773091643a4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -571,7 +571,7 @@ hangups==0.4.18 hass-nabucasa==0.54.0 # homeassistant.components.tasmota -hatasmota==0.5.0 +hatasmota==0.5.1 # homeassistant.components.jewish_calendar hdate==0.10.4 diff --git a/tests/components/tasmota/test_cover.py b/tests/components/tasmota/test_cover.py index 843fd72ecf1..06471e11757 100644 --- a/tests/components/tasmota/test_cover.py +++ b/tests/components/tasmota/test_cover.py @@ -111,7 +111,7 @@ async def test_tilt_support(hass, mqtt_mock, setup_tasmota): assert state.attributes["supported_features"] == COVER_SUPPORT -async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): +async def test_controlling_state_via_mqtt_tilt(hass, mqtt_mock, setup_tasmota): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 3 @@ -281,7 +281,10 @@ async def test_controlling_state_via_mqtt(hass, mqtt_mock, setup_tasmota): assert state.attributes["current_position"] == 100 -async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmota): +@pytest.mark.parametrize("tilt", ("", ',"Tilt":0')) +async def test_controlling_state_via_mqtt_inverted( + hass, mqtt_mock, setup_tasmota, tilt +): """Test state update via MQTT.""" config = copy.deepcopy(DEFAULT_CONFIG) config["rl"][0] = 3 @@ -310,7 +313,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", - '{"Shutter1":{"Position":54,"Direction":-1}}', + '{"Shutter1":{"Position":54,"Direction":-1' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" @@ -319,21 +322,25 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", - '{"Shutter1":{"Position":100,"Direction":1}}', + '{"Shutter1":{"Position":100,"Direction":1' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" assert state.attributes["current_position"] == 0 async_fire_mqtt_message( - hass, "tasmota_49A3BC/tele/SENSOR", '{"Shutter1":{"Position":0,"Direction":0}}' + hass, + "tasmota_49A3BC/tele/SENSOR", + '{"Shutter1":{"Position":0,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" assert state.attributes["current_position"] == 100 async_fire_mqtt_message( - hass, "tasmota_49A3BC/tele/SENSOR", '{"Shutter1":{"Position":99,"Direction":0}}' + hass, + "tasmota_49A3BC/tele/SENSOR", + '{"Shutter1":{"Position":99,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" @@ -342,7 +349,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/tele/SENSOR", - '{"Shutter1":{"Position":100,"Direction":0}}', + '{"Shutter1":{"Position":100,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" @@ -352,7 +359,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":54,"Direction":-1}}}', + '{"StatusSNS":{"Shutter1":{"Position":54,"Direction":-1' + tilt + "}}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" @@ -361,7 +368,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":1}}}', + '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":1' + tilt + "}}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" @@ -370,7 +377,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":0,"Direction":0}}}', + '{"StatusSNS":{"Shutter1":{"Position":0,"Direction":0' + tilt + "}}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" @@ -379,7 +386,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":99,"Direction":0}}}', + '{"StatusSNS":{"Shutter1":{"Position":99,"Direction":0' + tilt + "}}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" @@ -388,7 +395,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/STATUS10", - '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":0}}}', + '{"StatusSNS":{"Shutter1":{"Position":100,"Direction":0' + tilt + "}}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" @@ -398,7 +405,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", - '{"Shutter1":{"Position":54,"Direction":-1}}', + '{"Shutter1":{"Position":54,"Direction":-1' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "opening" @@ -407,21 +414,25 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", - '{"Shutter1":{"Position":100,"Direction":1}}', + '{"Shutter1":{"Position":100,"Direction":1' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closing" assert state.attributes["current_position"] == 0 async_fire_mqtt_message( - hass, "tasmota_49A3BC/stat/RESULT", '{"Shutter1":{"Position":0,"Direction":0}}' + hass, + "tasmota_49A3BC/stat/RESULT", + '{"Shutter1":{"Position":0,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" assert state.attributes["current_position"] == 100 async_fire_mqtt_message( - hass, "tasmota_49A3BC/stat/RESULT", '{"Shutter1":{"Position":1,"Direction":0}}' + hass, + "tasmota_49A3BC/stat/RESULT", + '{"Shutter1":{"Position":1,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "open" @@ -430,7 +441,7 @@ async def test_controlling_state_via_mqtt_inverted(hass, mqtt_mock, setup_tasmot async_fire_mqtt_message( hass, "tasmota_49A3BC/stat/RESULT", - '{"Shutter1":{"Position":100,"Direction":0}}', + '{"Shutter1":{"Position":100,"Direction":0' + tilt + "}}", ) state = hass.states.get("cover.tasmota_cover_1") assert state.state == "closed" From 2809592e71729473aeca358a5b7678f2a09de213 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 30 May 2022 14:21:20 +0200 Subject: [PATCH 893/930] Improve handling of MQTT overridden settings (#72698) * Improve handling of MQTT overridden settings * Don't warn unless config entry overrides yaml --- homeassistant/components/mqtt/__init__.py | 13 +++++++------ tests/components/mqtt/test_init.py | 5 ----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 78f64387435..1728dd7f2c7 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -685,14 +685,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # User has configuration.yaml config, warn about config entry overrides elif any(key in conf for key in entry.data): shared_keys = conf.keys() & entry.data.keys() - override = {k: entry.data[k] for k in shared_keys} + override = {k: entry.data[k] for k in shared_keys if conf[k] != entry.data[k]} if CONF_PASSWORD in override: override[CONF_PASSWORD] = "********" - _LOGGER.warning( - "Deprecated configuration settings found in configuration.yaml. " - "These settings from your configuration entry will override: %s", - override, - ) + if override: + _LOGGER.warning( + "Deprecated configuration settings found in configuration.yaml. " + "These settings from your configuration entry will override: %s", + override, + ) # Merge advanced configuration values from configuration.yaml conf = _merge_extended_config(entry, conf) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index a370bd67ec1..07c39d70df0 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -1715,11 +1715,6 @@ async def test_update_incomplete_entry( "The 'broker' option is deprecated, please remove it from your configuration" in caplog.text ) - assert ( - "Deprecated configuration settings found in configuration.yaml. These settings " - "from your configuration entry will override: {'broker': 'yaml_broker'}" - in caplog.text - ) # Discover a device to verify the entry was setup correctly async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) From 72a79736a6b2cef5e44719e15af886c909308a1f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 30 May 2022 14:40:55 -0700 Subject: [PATCH 894/930] Bumped version to 2022.6.0b4 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 8d2bcd33f4e..fe454dbc8a1 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b3" +PATCH_VERSION: Final = "0b4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index db64c7330ce..19aefbb6f90 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b3 +version = 2022.6.0b4 url = https://www.home-assistant.io/ [options] From 77e4c86c079c65327c0761e8d703cc9d91fa146f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 30 May 2022 20:41:05 -0700 Subject: [PATCH 895/930] Add support for announce to play_media (#72566) --- .../components/media_player/__init__.py | 4 +++- .../components/media_player/const.py | 1 + .../components/media_player/services.yaml | 7 ++++++ homeassistant/components/tts/__init__.py | 2 ++ tests/components/media_player/test_init.py | 23 +++++++++++++++++++ tests/components/tts/test_init.py | 2 ++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index f71f3fc2a1f..dc2f3624a0e 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -76,6 +76,7 @@ from .const import ( # noqa: F401 ATTR_INPUT_SOURCE_LIST, ATTR_MEDIA_ALBUM_ARTIST, ATTR_MEDIA_ALBUM_NAME, + ATTR_MEDIA_ANNOUNCE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_CHANNEL, ATTR_MEDIA_CONTENT_ID, @@ -182,9 +183,10 @@ DEVICE_CLASS_RECEIVER = MediaPlayerDeviceClass.RECEIVER.value MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = { vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string, vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string, - vol.Optional(ATTR_MEDIA_ENQUEUE): vol.Any( + vol.Exclusive(ATTR_MEDIA_ENQUEUE, "enqueue_announce"): vol.Any( cv.boolean, vol.Coerce(MediaPlayerEnqueue) ), + vol.Exclusive(ATTR_MEDIA_ANNOUNCE, "enqueue_announce"): cv.boolean, vol.Optional(ATTR_MEDIA_EXTRA, default={}): dict, } diff --git a/homeassistant/components/media_player/const.py b/homeassistant/components/media_player/const.py index b12f0c4ae01..4d534467ad6 100644 --- a/homeassistant/components/media_player/const.py +++ b/homeassistant/components/media_player/const.py @@ -10,6 +10,7 @@ ATTR_ENTITY_PICTURE_LOCAL = "entity_picture_local" ATTR_GROUP_MEMBERS = "group_members" ATTR_INPUT_SOURCE = "source" ATTR_INPUT_SOURCE_LIST = "source_list" +ATTR_MEDIA_ANNOUNCE = "announce" ATTR_MEDIA_ALBUM_ARTIST = "media_album_artist" ATTR_MEDIA_ALBUM_NAME = "media_album_name" ATTR_MEDIA_ARTIST = "media_artist" diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index b2a8ac40262..b698b87aec6 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -166,6 +166,13 @@ play_media: value: "add" - label: "Play now and clear queue" value: "replace" + announce: + name: Announce + description: If the media should be played as an announcement. + required: false + example: "true" + selector: + boolean: select_source: name: Select source diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 4628ec8768a..706122c174c 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -21,6 +21,7 @@ import yarl from homeassistant.components.http import HomeAssistantView from homeassistant.components.media_player.const import ( + ATTR_MEDIA_ANNOUNCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP, @@ -224,6 +225,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: str(yarl.URL.build(path=p_type, query=params)), ), ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC, + ATTR_MEDIA_ANNOUNCE: True, }, blocking=True, context=service.context, diff --git a/tests/components/media_player/test_init.py b/tests/components/media_player/test_init.py index cb095cbcfe0..eceb7e9ec4f 100644 --- a/tests/components/media_player/test_init.py +++ b/tests/components/media_player/test_init.py @@ -5,6 +5,7 @@ from http import HTTPStatus from unittest.mock import patch import pytest +import voluptuous as vol from homeassistant.components import media_player from homeassistant.components.media_player.browse_media import BrowseMedia @@ -291,3 +292,25 @@ async def test_enqueue_rewrite(hass, input, expected): assert len(mock_play_media.mock_calls) == 1 assert mock_play_media.mock_calls[0][2]["enqueue"] == expected + + +async def test_enqueue_alert_exclusive(hass): + """Test that alert and enqueue cannot be used together.""" + await async_setup_component( + hass, "media_player", {"media_player": {"platform": "demo"}} + ) + await hass.async_block_till_done() + + with pytest.raises(vol.Invalid): + await hass.services.async_call( + "media_player", + "play_media", + { + "entity_id": "media_player.bedroom", + "media_content_type": "music", + "media_content_id": "1234", + "enqueue": "play", + "announce": True, + }, + blocking=True, + ) diff --git a/tests/components/tts/test_init.py b/tests/components/tts/test_init.py index 78fa49a8fc9..7b348489059 100644 --- a/tests/components/tts/test_init.py +++ b/tests/components/tts/test_init.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.components import media_source, tts from homeassistant.components.demo.tts import DemoProvider from homeassistant.components.media_player.const import ( + ATTR_MEDIA_ANNOUNCE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, DOMAIN as DOMAIN_MP, @@ -91,6 +92,7 @@ async def test_setup_component_and_test_service(hass, empty_cache_dir): ) assert len(calls) == 1 + assert calls[0].data[ATTR_MEDIA_ANNOUNCE] is True assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC assert ( await get_media_source_url(hass, calls[0].data[ATTR_MEDIA_CONTENT_ID]) From a202ffe4c10b8092489b584396f142d78cdd757b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 May 2022 14:34:32 -1000 Subject: [PATCH 896/930] Make logbook inherit the recorder filter (#72728) --- homeassistant/components/logbook/__init__.py | 16 +- homeassistant/components/recorder/filters.py | 46 ++++- .../components/logbook/test_websocket_api.py | 192 +++++++++++++++++- tests/components/recorder/test_filters.py | 114 +++++++++++ 4 files changed, 357 insertions(+), 11 deletions(-) create mode 100644 tests/components/recorder/test_filters.py diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index f66f1d5e920..1abfcaba6ff 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -7,7 +7,10 @@ from typing import Any import voluptuous as vol from homeassistant.components import frontend +from homeassistant.components.recorder.const import DOMAIN as RECORDER_DOMAIN from homeassistant.components.recorder.filters import ( + extract_include_exclude_filter_conf, + merge_include_exclude_filters, sqlalchemy_filter_from_include_exclude_conf, ) from homeassistant.const import ( @@ -115,9 +118,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass, "logbook", "logbook", "hass:format-list-bulleted-type" ) - if conf := config.get(DOMAIN, {}): - filters = sqlalchemy_filter_from_include_exclude_conf(conf) - entities_filter = convert_include_exclude_filter(conf) + recorder_conf = config.get(RECORDER_DOMAIN, {}) + logbook_conf = config.get(DOMAIN, {}) + recorder_filter = extract_include_exclude_filter_conf(recorder_conf) + logbook_filter = extract_include_exclude_filter_conf(logbook_conf) + merged_filter = merge_include_exclude_filters(recorder_filter, logbook_filter) + + possible_merged_entities_filter = convert_include_exclude_filter(merged_filter) + if not possible_merged_entities_filter.empty_filter: + filters = sqlalchemy_filter_from_include_exclude_conf(merged_filter) + entities_filter = possible_merged_entities_filter else: filters = None entities_filter = None diff --git a/homeassistant/components/recorder/filters.py b/homeassistant/components/recorder/filters.py index 5dd1e4b7884..0ceb013d8c5 100644 --- a/homeassistant/components/recorder/filters.py +++ b/homeassistant/components/recorder/filters.py @@ -25,6 +25,40 @@ GLOB_TO_SQL_CHARS = { ord("\\"): "\\\\", } +FILTER_TYPES = (CONF_EXCLUDE, CONF_INCLUDE) +FITLER_MATCHERS = (CONF_ENTITIES, CONF_DOMAINS, CONF_ENTITY_GLOBS) + + +def extract_include_exclude_filter_conf(conf: ConfigType) -> dict[str, Any]: + """Extract an include exclude filter from configuration. + + This makes a copy so we do not alter the original data. + """ + return { + filter_type: { + matcher: set(conf.get(filter_type, {}).get(matcher, [])) + for matcher in FITLER_MATCHERS + } + for filter_type in FILTER_TYPES + } + + +def merge_include_exclude_filters( + base_filter: dict[str, Any], add_filter: dict[str, Any] +) -> dict[str, Any]: + """Merge two filters. + + This makes a copy so we do not alter the original data. + """ + return { + filter_type: { + matcher: base_filter[filter_type][matcher] + | add_filter[filter_type][matcher] + for matcher in FITLER_MATCHERS + } + for filter_type in FILTER_TYPES + } + def sqlalchemy_filter_from_include_exclude_conf(conf: ConfigType) -> Filters | None: """Build a sql filter from config.""" @@ -46,13 +80,13 @@ class Filters: def __init__(self) -> None: """Initialise the include and exclude filters.""" - self.excluded_entities: list[str] = [] - self.excluded_domains: list[str] = [] - self.excluded_entity_globs: list[str] = [] + self.excluded_entities: Iterable[str] = [] + self.excluded_domains: Iterable[str] = [] + self.excluded_entity_globs: Iterable[str] = [] - self.included_entities: list[str] = [] - self.included_domains: list[str] = [] - self.included_entity_globs: list[str] = [] + self.included_entities: Iterable[str] = [] + self.included_domains: Iterable[str] = [] + self.included_entity_globs: Iterable[str] = [] @property def has_config(self) -> bool: diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 9d7146ec96c..1d35d6d897d 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -483,7 +483,7 @@ async def test_subscribe_unsubscribe_logbook_stream_excluded_entities( CONF_EXCLUDE: { CONF_ENTITIES: ["light.exc"], CONF_DOMAINS: ["switch"], - CONF_ENTITY_GLOBS: "*.excluded", + CONF_ENTITY_GLOBS: ["*.excluded"], } }, }, @@ -672,7 +672,7 @@ async def test_subscribe_unsubscribe_logbook_stream_included_entities( CONF_INCLUDE: { CONF_ENTITIES: ["light.inc"], CONF_DOMAINS: ["switch"], - CONF_ENTITY_GLOBS: "*.included", + CONF_ENTITY_GLOBS: ["*.included"], } }, }, @@ -849,6 +849,194 @@ async def test_subscribe_unsubscribe_logbook_stream_included_entities( assert sum(hass.bus.async_listeners().values()) == init_count +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_logbook_stream_excluded_entities_inherits_filters_from_recorder( + hass, recorder_mock, hass_ws_client +): + """Test subscribe/unsubscribe logbook stream inherts filters from recorder.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "automation", "script") + ] + ) + await async_setup_component( + hass, + logbook.DOMAIN, + { + logbook.DOMAIN: { + CONF_EXCLUDE: { + CONF_ENTITIES: ["light.additional_excluded"], + } + }, + recorder.DOMAIN: { + CONF_EXCLUDE: { + CONF_ENTITIES: ["light.exc"], + CONF_DOMAINS: ["switch"], + CONF_ENTITY_GLOBS: ["*.excluded", "*.no_matches"], + } + }, + }, + ) + await hass.async_block_till_done() + init_count = sum(hass.bus.async_listeners().values()) + + hass.states.async_set("light.exc", STATE_ON) + hass.states.async_set("light.exc", STATE_OFF) + hass.states.async_set("switch.any", STATE_ON) + hass.states.async_set("switch.any", STATE_OFF) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + hass.states.async_set("light.additional_excluded", STATE_ON) + hass.states.async_set("light.additional_excluded", STATE_OFF) + hass.states.async_set("binary_sensor.is_light", STATE_ON) + hass.states.async_set("binary_sensor.is_light", STATE_OFF) + state: State = hass.states.get("binary_sensor.is_light") + await hass.async_block_till_done() + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + {"id": 7, "type": "logbook/event_stream", "start_time": now.isoformat()} + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + { + "entity_id": "binary_sensor.is_light", + "state": "off", + "when": state.last_updated.timestamp(), + } + ] + assert msg["event"]["start_time"] == now.timestamp() + assert msg["event"]["end_time"] > msg["event"]["start_time"] + assert msg["event"]["partial"] is True + + hass.states.async_set("light.exc", STATE_ON) + hass.states.async_set("light.exc", STATE_OFF) + hass.states.async_set("switch.any", STATE_ON) + hass.states.async_set("switch.any", STATE_OFF) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + hass.states.async_set("light.additional_excluded", STATE_ON) + hass.states.async_set("light.additional_excluded", STATE_OFF) + hass.states.async_set("light.alpha", "on") + hass.states.async_set("light.alpha", "off") + alpha_off_state: State = hass.states.get("light.alpha") + hass.states.async_set("light.zulu", "on", {"color": "blue"}) + hass.states.async_set("light.zulu", "off", {"effect": "help"}) + zulu_off_state: State = hass.states.get("light.zulu") + hass.states.async_set( + "light.zulu", "on", {"effect": "help", "color": ["blue", "green"]} + ) + zulu_on_state: State = hass.states.get("light.zulu") + await hass.async_block_till_done() + + hass.states.async_remove("light.zulu") + await hass.async_block_till_done() + + hass.states.async_set("light.zulu", "on", {"effect": "help", "color": "blue"}) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [] + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert "partial" not in msg["event"]["events"] + assert msg["event"]["events"] == [ + { + "entity_id": "light.alpha", + "state": "off", + "when": alpha_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "off", + "when": zulu_off_state.last_updated.timestamp(), + }, + { + "entity_id": "light.zulu", + "state": "on", + "when": zulu_on_state.last_updated.timestamp(), + }, + ] + + await async_wait_recording_done(hass) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "cover.excluded"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + { + ATTR_NAME: "Mock automation switch matching entity", + ATTR_ENTITY_ID: "switch.match_domain", + }, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation switch matching domain", ATTR_DOMAIN: "switch"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation matches nothing"}, + ) + hass.bus.async_fire( + EVENT_AUTOMATION_TRIGGERED, + {ATTR_NAME: "Mock automation 3", ATTR_ENTITY_ID: "light.keep"}, + ) + hass.states.async_set("cover.excluded", STATE_ON) + hass.states.async_set("cover.excluded", STATE_OFF) + await hass.async_block_till_done() + msg = await websocket_client.receive_json() + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [ + { + "context_id": ANY, + "domain": "automation", + "entity_id": None, + "message": "triggered", + "name": "Mock automation matches nothing", + "source": None, + "when": ANY, + }, + { + "context_id": ANY, + "domain": "automation", + "entity_id": "light.keep", + "message": "triggered", + "name": "Mock automation 3", + "source": None, + "when": ANY, + }, + ] + + await websocket_client.send_json( + {"id": 8, "type": "unsubscribe_events", "subscription": 7} + ) + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + + assert msg["id"] == 8 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count + + @patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) async def test_subscribe_unsubscribe_logbook_stream( hass, recorder_mock, hass_ws_client diff --git a/tests/components/recorder/test_filters.py b/tests/components/recorder/test_filters.py new file mode 100644 index 00000000000..fa80df6e345 --- /dev/null +++ b/tests/components/recorder/test_filters.py @@ -0,0 +1,114 @@ +"""The tests for recorder filters.""" + +from homeassistant.components.recorder.filters import ( + extract_include_exclude_filter_conf, + merge_include_exclude_filters, +) +from homeassistant.helpers.entityfilter import ( + CONF_DOMAINS, + CONF_ENTITIES, + CONF_ENTITY_GLOBS, + CONF_EXCLUDE, + CONF_INCLUDE, +) + +SIMPLE_INCLUDE_FILTER = { + CONF_INCLUDE: { + CONF_DOMAINS: ["homeassistant"], + CONF_ENTITIES: ["sensor.one"], + CONF_ENTITY_GLOBS: ["climate.*"], + } +} +SIMPLE_INCLUDE_FILTER_DIFFERENT_ENTITIES = { + CONF_INCLUDE: { + CONF_DOMAINS: ["other"], + CONF_ENTITIES: ["not_sensor.one"], + CONF_ENTITY_GLOBS: ["not_climate.*"], + } +} +SIMPLE_EXCLUDE_FILTER = { + CONF_EXCLUDE: { + CONF_DOMAINS: ["homeassistant"], + CONF_ENTITIES: ["sensor.one"], + CONF_ENTITY_GLOBS: ["climate.*"], + } +} +SIMPLE_INCLUDE_EXCLUDE_FILTER = {**SIMPLE_INCLUDE_FILTER, **SIMPLE_EXCLUDE_FILTER} + + +def test_extract_include_exclude_filter_conf(): + """Test we can extract a filter from configuration without altering it.""" + include_filter = extract_include_exclude_filter_conf(SIMPLE_INCLUDE_FILTER) + assert include_filter == { + CONF_EXCLUDE: { + CONF_DOMAINS: set(), + CONF_ENTITIES: set(), + CONF_ENTITY_GLOBS: set(), + }, + CONF_INCLUDE: { + CONF_DOMAINS: {"homeassistant"}, + CONF_ENTITIES: {"sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*"}, + }, + } + + exclude_filter = extract_include_exclude_filter_conf(SIMPLE_EXCLUDE_FILTER) + assert exclude_filter == { + CONF_INCLUDE: { + CONF_DOMAINS: set(), + CONF_ENTITIES: set(), + CONF_ENTITY_GLOBS: set(), + }, + CONF_EXCLUDE: { + CONF_DOMAINS: {"homeassistant"}, + CONF_ENTITIES: {"sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*"}, + }, + } + + include_exclude_filter = extract_include_exclude_filter_conf( + SIMPLE_INCLUDE_EXCLUDE_FILTER + ) + assert include_exclude_filter == { + CONF_INCLUDE: { + CONF_DOMAINS: {"homeassistant"}, + CONF_ENTITIES: {"sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*"}, + }, + CONF_EXCLUDE: { + CONF_DOMAINS: {"homeassistant"}, + CONF_ENTITIES: {"sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*"}, + }, + } + + include_exclude_filter[CONF_EXCLUDE][CONF_ENTITIES] = {"cover.altered"} + # verify it really is a copy + assert SIMPLE_INCLUDE_EXCLUDE_FILTER[CONF_EXCLUDE][CONF_ENTITIES] != { + "cover.altered" + } + + +def test_merge_include_exclude_filters(): + """Test we can merge two filters together.""" + include_exclude_filter_base = extract_include_exclude_filter_conf( + SIMPLE_INCLUDE_EXCLUDE_FILTER + ) + include_filter_add = extract_include_exclude_filter_conf( + SIMPLE_INCLUDE_FILTER_DIFFERENT_ENTITIES + ) + merged_filter = merge_include_exclude_filters( + include_exclude_filter_base, include_filter_add + ) + assert merged_filter == { + CONF_EXCLUDE: { + CONF_DOMAINS: {"homeassistant"}, + CONF_ENTITIES: {"sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*"}, + }, + CONF_INCLUDE: { + CONF_DOMAINS: {"other", "homeassistant"}, + CONF_ENTITIES: {"not_sensor.one", "sensor.one"}, + CONF_ENTITY_GLOBS: {"climate.*", "not_climate.*"}, + }, + } From a98528c93f52e717b324bb658d379e61cd0ca5a2 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 30 May 2022 22:56:59 -0500 Subject: [PATCH 897/930] Bump plexapi to 4.11.2 (#72729) --- homeassistant/components/plex/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/manifest.json b/homeassistant/components/plex/manifest.json index 2e2db01de77..912732efe98 100644 --- a/homeassistant/components/plex/manifest.json +++ b/homeassistant/components/plex/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/plex", "requirements": [ - "plexapi==4.11.1", + "plexapi==4.11.2", "plexauth==0.0.6", "plexwebsocket==0.0.13" ], diff --git a/requirements_all.txt b/requirements_all.txt index e15aff574d5..544c16a58dd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1236,7 +1236,7 @@ pillow==9.1.1 pizzapi==0.0.3 # homeassistant.components.plex -plexapi==4.11.1 +plexapi==4.11.2 # homeassistant.components.plex plexauth==0.0.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 773091643a4..58be8ff0459 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -838,7 +838,7 @@ pilight==0.1.1 pillow==9.1.1 # homeassistant.components.plex -plexapi==4.11.1 +plexapi==4.11.2 # homeassistant.components.plex plexauth==0.0.6 From b842c76fbd59ed212b6ea85e18d569089c436486 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 31 May 2022 01:05:09 -0400 Subject: [PATCH 898/930] Bump zwave-js-server-python to 0.37.1 (#72731) Co-authored-by: Paulus Schoutsen --- homeassistant/components/zwave_js/api.py | 11 ++++------- homeassistant/components/zwave_js/climate.py | 2 +- homeassistant/components/zwave_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 5d81dc46803..5b8b95e951c 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -1231,7 +1231,7 @@ async def websocket_replace_failed_node( try: result = await controller.async_replace_failed_node( - node_id, + controller.nodes[node_id], INCLUSION_STRATEGY_NOT_SMART_START[inclusion_strategy.value], force_security=force_security, provisioning=provisioning, @@ -1290,11 +1290,8 @@ async def websocket_remove_failed_node( connection.subscriptions[msg["id"]] = async_cleanup msg[DATA_UNSUBSCRIBE] = unsubs = [controller.on("node removed", node_removed)] - result = await controller.async_remove_failed_node(node.node_id) - connection.send_result( - msg[ID], - result, - ) + await controller.async_remove_failed_node(node) + connection.send_result(msg[ID]) @websocket_api.require_admin @@ -1416,7 +1413,7 @@ async def websocket_heal_node( assert driver is not None # The node comes from the driver instance. controller = driver.controller - result = await controller.async_heal_node(node.node_id) + result = await controller.async_heal_node(node) connection.send_result( msg[ID], result, diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 7b07eb09619..d8037643488 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -66,7 +66,7 @@ ZW_HVAC_MODE_MAP: dict[int, HVACMode] = { ThermostatMode.AUTO: HVACMode.HEAT_COOL, ThermostatMode.AUXILIARY: HVACMode.HEAT, ThermostatMode.FAN: HVACMode.FAN_ONLY, - ThermostatMode.FURNANCE: HVACMode.HEAT, + ThermostatMode.FURNACE: HVACMode.HEAT, ThermostatMode.DRY: HVACMode.DRY, ThermostatMode.AUTO_CHANGE_OVER: HVACMode.HEAT_COOL, ThermostatMode.HEATING_ECON: HVACMode.HEAT, diff --git a/homeassistant/components/zwave_js/manifest.json b/homeassistant/components/zwave_js/manifest.json index 555da5fe954..1c7eabb4e86 100644 --- a/homeassistant/components/zwave_js/manifest.json +++ b/homeassistant/components/zwave_js/manifest.json @@ -3,7 +3,7 @@ "name": "Z-Wave JS", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zwave_js", - "requirements": ["zwave-js-server-python==0.37.0"], + "requirements": ["zwave-js-server-python==0.37.1"], "codeowners": ["@home-assistant/z-wave"], "dependencies": ["usb", "http", "websocket_api"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 544c16a58dd..dedebd00dc8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2528,7 +2528,7 @@ zigpy==0.45.1 zm-py==0.5.2 # homeassistant.components.zwave_js -zwave-js-server-python==0.37.0 +zwave-js-server-python==0.37.1 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 58be8ff0459..adbad6fa960 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1665,7 +1665,7 @@ zigpy-znp==0.7.0 zigpy==0.45.1 # homeassistant.components.zwave_js -zwave-js-server-python==0.37.0 +zwave-js-server-python==0.37.1 # homeassistant.components.zwave_me zwave_me_ws==0.2.4 From 15bdfb2a45a849f243282bc7b6f3b27812552070 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 30 May 2022 17:48:42 -0600 Subject: [PATCH 899/930] Fix invalid RainMachine syntax (#72732) --- homeassistant/components/rainmachine/switch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/rainmachine/switch.py b/homeassistant/components/rainmachine/switch.py index a220aafa2a5..8d339682305 100644 --- a/homeassistant/components/rainmachine/switch.py +++ b/homeassistant/components/rainmachine/switch.py @@ -393,7 +393,7 @@ class RainMachineZone(RainMachineActivitySwitch): ATTR_CURRENT_CYCLE: data["cycle"], ATTR_ID: data["uid"], ATTR_NO_CYCLES: data["noOfCycles"], - ATTR_RESTRICTIONS: data("restriction"), + ATTR_RESTRICTIONS: data["restriction"], ATTR_SLOPE: SLOPE_TYPE_MAP.get(data["slope"], 99), ATTR_SOIL_TYPE: SOIL_TYPE_MAP.get(data["soil"], 99), ATTR_SPRINKLER_TYPE: SPRINKLER_TYPE_MAP.get(data["group_id"], 99), From a4e2d31a199a6e5dd7e4fd01d3227911098c648c Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 30 May 2022 18:58:08 -0600 Subject: [PATCH 900/930] Bump regenmaschine to 2022.05.1 (#72735) --- homeassistant/components/rainmachine/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index bbe58e263b1..98dc9a6c877 100644 --- a/homeassistant/components/rainmachine/manifest.json +++ b/homeassistant/components/rainmachine/manifest.json @@ -3,7 +3,7 @@ "name": "RainMachine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rainmachine", - "requirements": ["regenmaschine==2022.05.0"], + "requirements": ["regenmaschine==2022.05.1"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index dedebd00dc8..464bf2997b7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2065,7 +2065,7 @@ raincloudy==0.0.7 raspyrfm-client==1.2.8 # homeassistant.components.rainmachine -regenmaschine==2022.05.0 +regenmaschine==2022.05.1 # homeassistant.components.renault renault-api==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index adbad6fa960..0686886aa9f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1364,7 +1364,7 @@ rachiopy==1.0.3 radios==0.1.1 # homeassistant.components.rainmachine -regenmaschine==2022.05.0 +regenmaschine==2022.05.1 # homeassistant.components.renault renault-api==0.1.11 From 48d36e49f0f61608d0d3c379d146620f65fed32b Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Mon, 30 May 2022 23:04:53 -0600 Subject: [PATCH 901/930] Bump simplisafe-python to 2022.05.2 (#72740) --- homeassistant/components/simplisafe/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index cb1b02e37ae..f62da735f92 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -3,7 +3,7 @@ "name": "SimpliSafe", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/simplisafe", - "requirements": ["simplisafe-python==2022.05.1"], + "requirements": ["simplisafe-python==2022.05.2"], "codeowners": ["@bachya"], "iot_class": "cloud_polling", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 464bf2997b7..3203dd79f6a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2168,7 +2168,7 @@ simplehound==0.3 simplepush==1.1.4 # homeassistant.components.simplisafe -simplisafe-python==2022.05.1 +simplisafe-python==2022.05.2 # homeassistant.components.sisyphus sisyphus-control==3.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0686886aa9f..171ae908991 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1425,7 +1425,7 @@ sharkiq==0.0.1 simplehound==0.3 # homeassistant.components.simplisafe -simplisafe-python==2022.05.1 +simplisafe-python==2022.05.2 # homeassistant.components.slack slackclient==2.5.0 From 103f324c52199435eaef382a4c4943c458f6beb5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 30 May 2022 22:57:22 -0700 Subject: [PATCH 902/930] Bumped version to 2022.6.0b5 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fe454dbc8a1..bd49bb0e1e2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b4" +PATCH_VERSION: Final = "0b5" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 19aefbb6f90..3d52bda7738 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b4 +version = 2022.6.0b5 url = https://www.home-assistant.io/ [options] From 6e06b6c9ede838883c14737b7f6d35ef14082caa Mon Sep 17 00:00:00 2001 From: eyager1 <44526531+eyager1@users.noreply.github.com> Date: Mon, 30 May 2022 18:32:52 -0400 Subject: [PATCH 903/930] Add empty string to list of invalid states (#72590) Add null state to list of invalid states --- homeassistant/components/statistics/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/statistics/sensor.py b/homeassistant/components/statistics/sensor.py index ac62b63e8ca..3f33fa015b9 100644 --- a/homeassistant/components/statistics/sensor.py +++ b/homeassistant/components/statistics/sensor.py @@ -350,7 +350,7 @@ class StatisticsSensor(SensorEntity): if new_state.state == STATE_UNAVAILABLE: self.attributes[STAT_SOURCE_VALUE_VALID] = None return - if new_state.state in (STATE_UNKNOWN, None): + if new_state.state in (STATE_UNKNOWN, None, ""): self.attributes[STAT_SOURCE_VALUE_VALID] = False return From 4bf5132a06fc2adb33a90ede356c22448662e20a Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Tue, 31 May 2022 16:35:29 +0200 Subject: [PATCH 904/930] SmartThings issue with unique_id (#72715) Co-authored-by: Jan Bouwhuis --- homeassistant/components/smartthings/smartapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/smartthings/smartapp.py b/homeassistant/components/smartthings/smartapp.py index 28b60b57447..fbd63d41373 100644 --- a/homeassistant/components/smartthings/smartapp.py +++ b/homeassistant/components/smartthings/smartapp.py @@ -405,7 +405,7 @@ async def _continue_flow( ( flow for flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN) - if flow["context"]["unique_id"] == unique_id + if flow["context"].get("unique_id") == unique_id ), None, ) From 1b2cb4eab73db6164c1bcb82f501d3d3404872fa Mon Sep 17 00:00:00 2001 From: Khole Date: Tue, 31 May 2022 16:55:00 +0100 Subject: [PATCH 905/930] Fix hive authentication process (#72719) * Fix hive authentication process * Update hive test scripts to add new data --- homeassistant/components/hive/__init__.py | 7 ++- homeassistant/components/hive/config_flow.py | 1 + homeassistant/components/hive/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/hive/test_config_flow.py | 60 +++++++++++++++++++- 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/hive/__init__.py b/homeassistant/components/hive/__init__.py index 00c3a327578..292bbe62ae1 100644 --- a/homeassistant/components/hive/__init__.py +++ b/homeassistant/components/hive/__init__.py @@ -76,8 +76,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Hive from a config entry.""" websession = aiohttp_client.async_get_clientsession(hass) - hive = Hive(websession) hive_config = dict(entry.data) + hive = Hive( + websession, + deviceGroupKey=hive_config["device_data"][0], + deviceKey=hive_config["device_data"][1], + devicePassword=hive_config["device_data"][2], + ) hive_config["options"] = {} hive_config["options"].update( diff --git a/homeassistant/components/hive/config_flow.py b/homeassistant/components/hive/config_flow.py index 2632a24e360..9c391f13294 100644 --- a/homeassistant/components/hive/config_flow.py +++ b/homeassistant/components/hive/config_flow.py @@ -103,6 +103,7 @@ class HiveFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): # Setup the config entry self.data["tokens"] = self.tokens + self.data["device_data"] = await self.hive_auth.getDeviceData() if self.context["source"] == config_entries.SOURCE_REAUTH: self.hass.config_entries.async_update_entry( self.entry, title=self.data["username"], data=self.data diff --git a/homeassistant/components/hive/manifest.json b/homeassistant/components/hive/manifest.json index 19958b51bd7..472adc137ba 100644 --- a/homeassistant/components/hive/manifest.json +++ b/homeassistant/components/hive/manifest.json @@ -3,7 +3,7 @@ "name": "Hive", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/hive", - "requirements": ["pyhiveapi==0.4.2"], + "requirements": ["pyhiveapi==0.5.4"], "codeowners": ["@Rendili", "@KJonline"], "iot_class": "cloud_polling", "loggers": ["apyhiveapi"] diff --git a/requirements_all.txt b/requirements_all.txt index 3203dd79f6a..1821fb8bbc9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1538,7 +1538,7 @@ pyheos==0.7.2 pyhik==0.3.0 # homeassistant.components.hive -pyhiveapi==0.4.2 +pyhiveapi==0.5.4 # homeassistant.components.homematic pyhomematic==0.1.77 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 171ae908991..1f89c83485b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1029,7 +1029,7 @@ pyhaversion==22.4.1 pyheos==0.7.2 # homeassistant.components.hive -pyhiveapi==0.4.2 +pyhiveapi==0.5.4 # homeassistant.components.homematic pyhomematic==0.1.77 diff --git a/tests/components/hive/test_config_flow.py b/tests/components/hive/test_config_flow.py index ce13e52fe96..bb567b0bdfc 100644 --- a/tests/components/hive/test_config_flow.py +++ b/tests/components/hive/test_config_flow.py @@ -13,7 +13,7 @@ USERNAME = "username@home-assistant.com" UPDATED_USERNAME = "updated_username@home-assistant.com" PASSWORD = "test-password" UPDATED_PASSWORD = "updated-password" -INCORRECT_PASSWORD = "incoreect-password" +INCORRECT_PASSWORD = "incorrect-password" SCAN_INTERVAL = 120 UPDATED_SCAN_INTERVAL = 60 MFA_CODE = "1234" @@ -33,6 +33,13 @@ async def test_import_flow(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.getDeviceData", + return_value=[ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], ), patch( "homeassistant.components.hive.async_setup", return_value=True ) as mock_setup, patch( @@ -57,6 +64,11 @@ async def test_import_flow(hass): }, "ChallengeName": "SUCCESS", }, + "device_data": [ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], } assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(mock_setup.mock_calls) == 1 @@ -81,6 +93,13 @@ async def test_user_flow(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.getDeviceData", + return_value=[ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], ), patch( "homeassistant.components.hive.async_setup", return_value=True ) as mock_setup, patch( @@ -105,6 +124,11 @@ async def test_user_flow(hass): }, "ChallengeName": "SUCCESS", }, + "device_data": [ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], } assert len(mock_setup.mock_calls) == 1 @@ -148,6 +172,13 @@ async def test_user_flow_2fa(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.getDeviceData", + return_value=[ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], ), patch( "homeassistant.components.hive.async_setup", return_value=True ) as mock_setup, patch( @@ -171,6 +202,11 @@ async def test_user_flow_2fa(hass): }, "ChallengeName": "SUCCESS", }, + "device_data": [ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], } assert len(mock_setup.mock_calls) == 1 @@ -243,7 +279,15 @@ async def test_option_flow(hass): entry = MockConfigEntry( domain=DOMAIN, title=USERNAME, - data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}, + data={ + CONF_USERNAME: USERNAME, + CONF_PASSWORD: PASSWORD, + "device_data": [ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], + }, ) entry.add_to_hass(hass) @@ -317,6 +361,13 @@ async def test_user_flow_2fa_send_new_code(hass): "AccessToken": "mock-access-token", }, }, + ), patch( + "homeassistant.components.hive.config_flow.Auth.getDeviceData", + return_value=[ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], ), patch( "homeassistant.components.hive.async_setup", return_value=True ) as mock_setup, patch( @@ -340,6 +391,11 @@ async def test_user_flow_2fa_send_new_code(hass): }, "ChallengeName": "SUCCESS", }, + "device_data": [ + "mock-device-group-key", + "mock-device-key", + "mock-device-password", + ], } assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 From 7c2f73ddbaf4ade60be3206e4abcc672a432ec61 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Tue, 31 May 2022 13:09:07 -0600 Subject: [PATCH 906/930] Alter RainMachine to not create entities if the underlying data is missing (#72733) --- .coveragerc | 1 + .../components/rainmachine/__init__.py | 3 -- .../components/rainmachine/binary_sensor.py | 44 ++++++++----------- homeassistant/components/rainmachine/model.py | 1 + .../components/rainmachine/sensor.py | 32 ++++++-------- homeassistant/components/rainmachine/util.py | 14 ++++++ 6 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 homeassistant/components/rainmachine/util.py diff --git a/.coveragerc b/.coveragerc index 62e6d3cb94e..9e2c1e8c678 100644 --- a/.coveragerc +++ b/.coveragerc @@ -965,6 +965,7 @@ omit = homeassistant/components/rainmachine/model.py homeassistant/components/rainmachine/sensor.py homeassistant/components/rainmachine/switch.py + homeassistant/components/rainmachine/util.py homeassistant/components/raspyrfm/* homeassistant/components/recollect_waste/__init__.py homeassistant/components/recollect_waste/sensor.py diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index d212f1638b4..6d51be9d921 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -50,10 +50,7 @@ from .const import ( LOGGER, ) -DEFAULT_ATTRIBUTION = "Data provided by Green Electronics LLC" -DEFAULT_ICON = "mdi:water" DEFAULT_SSL = True -DEFAULT_UPDATE_INTERVAL = timedelta(seconds=15) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index 730b51c142a..1818222a8f4 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -1,6 +1,5 @@ """This platform provides binary sensors for key RainMachine data.""" from dataclasses import dataclass -from functools import partial from homeassistant.components.binary_sensor import ( BinarySensorEntity, @@ -21,6 +20,7 @@ from .const import ( DOMAIN, ) from .model import RainMachineDescriptionMixinApiCategory +from .util import key_exists TYPE_FLOW_SENSOR = "flow_sensor" TYPE_FREEZE = "freeze" @@ -46,6 +46,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( name="Flow Sensor", icon="mdi:water-pump", api_category=DATA_PROVISION_SETTINGS, + data_key="useFlowSensor", ), RainMachineBinarySensorDescription( key=TYPE_FREEZE, @@ -53,6 +54,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( icon="mdi:cancel", entity_category=EntityCategory.DIAGNOSTIC, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="freeze", ), RainMachineBinarySensorDescription( key=TYPE_FREEZE_PROTECTION, @@ -60,6 +62,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( icon="mdi:weather-snowy", entity_category=EntityCategory.DIAGNOSTIC, api_category=DATA_RESTRICTIONS_UNIVERSAL, + data_key="freezeProtectEnabled", ), RainMachineBinarySensorDescription( key=TYPE_HOT_DAYS, @@ -67,6 +70,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( icon="mdi:thermometer-lines", entity_category=EntityCategory.DIAGNOSTIC, api_category=DATA_RESTRICTIONS_UNIVERSAL, + data_key="hotDaysExtraWatering", ), RainMachineBinarySensorDescription( key=TYPE_HOURLY, @@ -75,6 +79,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="hourly", ), RainMachineBinarySensorDescription( key=TYPE_MONTH, @@ -83,6 +88,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="month", ), RainMachineBinarySensorDescription( key=TYPE_RAINDELAY, @@ -91,6 +97,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="rainDelay", ), RainMachineBinarySensorDescription( key=TYPE_RAINSENSOR, @@ -99,6 +106,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="rainSensor", ), RainMachineBinarySensorDescription( key=TYPE_WEEKDAY, @@ -107,6 +115,7 @@ BINARY_SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, api_category=DATA_RESTRICTIONS_CURRENT, + data_key="weekDay", ), ) @@ -118,35 +127,20 @@ async def async_setup_entry( controller = hass.data[DOMAIN][entry.entry_id][DATA_CONTROLLER] coordinators = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR] - @callback - def async_get_sensor_by_api_category(api_category: str) -> partial: - """Generate the appropriate sensor object for an API category.""" - if api_category == DATA_PROVISION_SETTINGS: - return partial( - ProvisionSettingsBinarySensor, - entry, - coordinators[DATA_PROVISION_SETTINGS], - ) - - if api_category == DATA_RESTRICTIONS_CURRENT: - return partial( - CurrentRestrictionsBinarySensor, - entry, - coordinators[DATA_RESTRICTIONS_CURRENT], - ) - - return partial( - UniversalRestrictionsBinarySensor, - entry, - coordinators[DATA_RESTRICTIONS_UNIVERSAL], - ) + api_category_sensor_map = { + DATA_PROVISION_SETTINGS: ProvisionSettingsBinarySensor, + DATA_RESTRICTIONS_CURRENT: CurrentRestrictionsBinarySensor, + DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsBinarySensor, + } async_add_entities( [ - async_get_sensor_by_api_category(description.api_category)( - controller, description + api_category_sensor_map[description.api_category]( + entry, coordinator, controller, description ) for description in BINARY_SENSOR_DESCRIPTIONS + if (coordinator := coordinators[description.api_category]) is not None + and key_exists(coordinator.data, description.data_key) ] ) diff --git a/homeassistant/components/rainmachine/model.py b/homeassistant/components/rainmachine/model.py index 9f638d486aa..680a47c5d42 100644 --- a/homeassistant/components/rainmachine/model.py +++ b/homeassistant/components/rainmachine/model.py @@ -7,6 +7,7 @@ class RainMachineDescriptionMixinApiCategory: """Define an entity description mixin for binary and regular sensors.""" api_category: str + data_key: str @dataclass diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index a2b0f7cd539..522c57cf7a2 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -3,7 +3,6 @@ from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timedelta -from functools import partial from homeassistant.components.sensor import ( SensorDeviceClass, @@ -33,6 +32,7 @@ from .model import ( RainMachineDescriptionMixinApiCategory, RainMachineDescriptionMixinUid, ) +from .util import key_exists DEFAULT_ZONE_COMPLETION_TIME_WOBBLE_TOLERANCE = timedelta(seconds=5) @@ -68,6 +68,7 @@ SENSOR_DESCRIPTIONS = ( entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorClicksPerCubicMeter", ), RainMachineSensorDescriptionApiCategory( key=TYPE_FLOW_SENSOR_CONSUMED_LITERS, @@ -78,6 +79,7 @@ SENSOR_DESCRIPTIONS = ( entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorWateringClicks", ), RainMachineSensorDescriptionApiCategory( key=TYPE_FLOW_SENSOR_START_INDEX, @@ -87,6 +89,7 @@ SENSOR_DESCRIPTIONS = ( native_unit_of_measurement="index", entity_registry_enabled_default=False, api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorStartIndex", ), RainMachineSensorDescriptionApiCategory( key=TYPE_FLOW_SENSOR_WATERING_CLICKS, @@ -97,6 +100,7 @@ SENSOR_DESCRIPTIONS = ( entity_registry_enabled_default=False, state_class=SensorStateClass.MEASUREMENT, api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorWateringClicks", ), RainMachineSensorDescriptionApiCategory( key=TYPE_FREEZE_TEMP, @@ -107,6 +111,7 @@ SENSOR_DESCRIPTIONS = ( device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, api_category=DATA_RESTRICTIONS_UNIVERSAL, + data_key="freezeProtectTemp", ), ) @@ -118,27 +123,18 @@ async def async_setup_entry( controller = hass.data[DOMAIN][entry.entry_id][DATA_CONTROLLER] coordinators = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR] - @callback - def async_get_sensor_by_api_category(api_category: str) -> partial: - """Generate the appropriate sensor object for an API category.""" - if api_category == DATA_PROVISION_SETTINGS: - return partial( - ProvisionSettingsSensor, - entry, - coordinators[DATA_PROVISION_SETTINGS], - ) - - return partial( - UniversalRestrictionsSensor, - entry, - coordinators[DATA_RESTRICTIONS_UNIVERSAL], - ) + api_category_sensor_map = { + DATA_PROVISION_SETTINGS: ProvisionSettingsSensor, + DATA_RESTRICTIONS_UNIVERSAL: UniversalRestrictionsSensor, + } sensors = [ - async_get_sensor_by_api_category(description.api_category)( - controller, description + api_category_sensor_map[description.api_category]( + entry, coordinator, controller, description ) for description in SENSOR_DESCRIPTIONS + if (coordinator := coordinators[description.api_category]) is not None + and key_exists(coordinator.data, description.data_key) ] zone_coordinator = coordinators[DATA_ZONES] diff --git a/homeassistant/components/rainmachine/util.py b/homeassistant/components/rainmachine/util.py new file mode 100644 index 00000000000..27a0636688e --- /dev/null +++ b/homeassistant/components/rainmachine/util.py @@ -0,0 +1,14 @@ +"""Define RainMachine utilities.""" +from __future__ import annotations + +from typing import Any + + +def key_exists(data: dict[str, Any], search_key: str) -> bool: + """Return whether a key exists in a nested dict.""" + for key, value in data.items(): + if key == search_key: + return True + if isinstance(value, dict): + return key_exists(value, search_key) + return False From ca8c750a5aa4bdb2ce52b051314e56b6766f3844 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 May 2022 22:41:33 -1000 Subject: [PATCH 907/930] Small performance improvement for matching logbook rows (#72750) --- homeassistant/components/logbook/processor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py index ea6002cc62c..b3a43c2ca35 100644 --- a/homeassistant/components/logbook/processor.py +++ b/homeassistant/components/logbook/processor.py @@ -407,7 +407,8 @@ class ContextAugmenter: def _rows_match(row: Row | EventAsRow, other_row: Row | EventAsRow) -> bool: """Check of rows match by using the same method as Events __hash__.""" if ( - (state_id := row.state_id) is not None + row is other_row + or (state_id := row.state_id) is not None and state_id == other_row.state_id or (event_id := row.event_id) is not None and event_id == other_row.event_id From 6b3a284135a9712379b5c11cd3a3c8a3236d3f95 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Wed, 1 Jun 2022 04:34:52 +1000 Subject: [PATCH 908/930] Make zone condition more robust by ignoring unavailable and unknown entities (#72751) * ignore entities with state unavailable or unknown * test for unavailable entity --- homeassistant/helpers/condition.py | 6 +++ tests/components/geo_location/test_trigger.py | 42 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index 8985b7b721c..a628cdefff4 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -829,6 +829,12 @@ def zone( else: entity_id = entity.entity_id + if entity.state in ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + ): + return False + latitude = entity.attributes.get(ATTR_LATITUDE) longitude = entity.attributes.get(ATTR_LONGITUDE) diff --git a/tests/components/geo_location/test_trigger.py b/tests/components/geo_location/test_trigger.py index bbf5f42ed60..de6276545b7 100644 --- a/tests/components/geo_location/test_trigger.py +++ b/tests/components/geo_location/test_trigger.py @@ -4,7 +4,12 @@ import logging import pytest from homeassistant.components import automation, zone -from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL, SERVICE_TURN_OFF +from homeassistant.const import ( + ATTR_ENTITY_ID, + ENTITY_MATCH_ALL, + SERVICE_TURN_OFF, + STATE_UNAVAILABLE, +) from homeassistant.core import Context from homeassistant.setup import async_setup_component @@ -189,6 +194,41 @@ async def test_if_fires_on_zone_leave(hass, calls): assert len(calls) == 1 +async def test_if_fires_on_zone_leave_2(hass, calls): + """Test for firing on zone leave for unavailable entity.""" + hass.states.async_set( + "geo_location.entity", + "hello", + {"latitude": 32.880586, "longitude": -117.237564, "source": "test_source"}, + ) + await hass.async_block_till_done() + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": { + "platform": "geo_location", + "source": "test_source", + "zone": "zone.test", + "event": "enter", + }, + "action": {"service": "test.automation"}, + } + }, + ) + + hass.states.async_set( + "geo_location.entity", + STATE_UNAVAILABLE, + {"source": "test_source"}, + ) + await hass.async_block_till_done() + + assert len(calls) == 0 + + async def test_if_not_fires_for_leave_on_zone_enter(hass, calls): """Test for not firing on zone enter.""" hass.states.async_set( From 82ed6869d05bfaa38214b2e6de80f24406928b35 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 31 May 2022 15:51:38 +0200 Subject: [PATCH 909/930] Improve integration sensor's time unit handling (#72759) --- .../components/integration/sensor.py | 17 +++++-- tests/components/integration/test_sensor.py | 46 ++++++++++++++++++- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/integration/sensor.py b/homeassistant/components/integration/sensor.py index 5c982c6ec5e..5d0dde3e4de 100644 --- a/homeassistant/components/integration/sensor.py +++ b/homeassistant/components/integration/sensor.py @@ -154,17 +154,26 @@ class IntegrationSensor(RestoreEntity, SensorEntity): self._method = integration_method self._attr_name = name if name is not None else f"{source_entity} integral" - self._unit_template = ( - f"{'' if unit_prefix is None else unit_prefix}{{}}{unit_time}" - ) + self._unit_template = f"{'' if unit_prefix is None else unit_prefix}{{}}" self._unit_of_measurement = None self._unit_prefix = UNIT_PREFIXES[unit_prefix] self._unit_time = UNIT_TIME[unit_time] + self._unit_time_str = unit_time self._attr_state_class = SensorStateClass.TOTAL self._attr_icon = "mdi:chart-histogram" self._attr_should_poll = False self._attr_extra_state_attributes = {ATTR_SOURCE_ID: source_entity} + def _unit(self, source_unit: str) -> str: + """Derive unit from the source sensor, SI prefix and time unit.""" + unit_time = self._unit_time_str + if source_unit.endswith(f"/{unit_time}"): + integral_unit = source_unit[0 : (-(1 + len(unit_time)))] + else: + integral_unit = f"{source_unit}{unit_time}" + + return self._unit_template.format(integral_unit) + async def async_added_to_hass(self): """Handle entity which will be added.""" await super().async_added_to_hass() @@ -203,7 +212,7 @@ class IntegrationSensor(RestoreEntity, SensorEntity): update_state = False unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) if unit is not None: - new_unit_of_measurement = self._unit_template.format(unit) + new_unit_of_measurement = self._unit(unit) if self._unit_of_measurement != new_unit_of_measurement: self._unit_of_measurement = new_unit_of_measurement update_state = True diff --git a/tests/components/integration/test_sensor.py b/tests/components/integration/test_sensor.py index e4173d62eb4..8999c1f8d04 100644 --- a/tests/components/integration/test_sensor.py +++ b/tests/components/integration/test_sensor.py @@ -5,12 +5,15 @@ from unittest.mock import patch from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, + DATA_KILOBYTES, + DATA_RATE_BYTES_PER_SECOND, ENERGY_KILO_WATT_HOUR, ENERGY_WATT_HOUR, POWER_KILO_WATT, POWER_WATT, STATE_UNAVAILABLE, STATE_UNKNOWN, + TIME_HOURS, TIME_SECONDS, ) from homeassistant.core import HomeAssistant, State @@ -300,7 +303,9 @@ async def test_suffix(hass): assert await async_setup_component(hass, "sensor", config) entity_id = config["sensor"]["source"] - hass.states.async_set(entity_id, 1000, {ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT}) + hass.states.async_set( + entity_id, 1000, {ATTR_UNIT_OF_MEASUREMENT: DATA_RATE_BYTES_PER_SECOND} + ) await hass.async_block_till_done() now = dt_util.utcnow() + timedelta(seconds=10) @@ -308,7 +313,7 @@ async def test_suffix(hass): hass.states.async_set( entity_id, 1000, - {ATTR_UNIT_OF_MEASUREMENT: POWER_KILO_WATT}, + {ATTR_UNIT_OF_MEASUREMENT: DATA_RATE_BYTES_PER_SECOND}, force_update=True, ) await hass.async_block_till_done() @@ -318,6 +323,43 @@ async def test_suffix(hass): # Testing a network speed sensor at 1000 bytes/s over 10s = 10kbytes assert round(float(state.state)) == 10 + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == DATA_KILOBYTES + + +async def test_suffix_2(hass): + """Test integration sensor state.""" + config = { + "sensor": { + "platform": "integration", + "name": "integration", + "source": "sensor.cubic_meters_per_hour", + "round": 2, + "unit_time": TIME_HOURS, + } + } + + assert await async_setup_component(hass, "sensor", config) + + entity_id = config["sensor"]["source"] + hass.states.async_set(entity_id, 1000, {ATTR_UNIT_OF_MEASUREMENT: "m³/h"}) + await hass.async_block_till_done() + + now = dt_util.utcnow() + timedelta(hours=1) + with patch("homeassistant.util.dt.utcnow", return_value=now): + hass.states.async_set( + entity_id, + 1000, + {ATTR_UNIT_OF_MEASUREMENT: "m³/h"}, + force_update=True, + ) + await hass.async_block_till_done() + + state = hass.states.get("sensor.integration") + assert state is not None + + # Testing a flow sensor at 1000 m³/h over 1h = 1000 m³ + assert round(float(state.state)) == 1000 + assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m³" async def test_units(hass): From d268c828ee87db85867936c931c5c6e8bf22d78a Mon Sep 17 00:00:00 2001 From: "David F. Mulcahey" Date: Tue, 31 May 2022 15:24:35 -0400 Subject: [PATCH 910/930] Bump ZHA quirks lib to 0.0.75 (#72765) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 8d6e6162d76..4f16b1c113e 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -7,7 +7,7 @@ "bellows==0.30.0", "pyserial==3.5", "pyserial-asyncio==0.6", - "zha-quirks==0.0.74", + "zha-quirks==0.0.75", "zigpy-deconz==0.16.0", "zigpy==0.45.1", "zigpy-xbee==0.14.0", diff --git a/requirements_all.txt b/requirements_all.txt index 1821fb8bbc9..d9fd7a6a34e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2501,7 +2501,7 @@ zengge==0.2 zeroconf==0.38.6 # homeassistant.components.zha -zha-quirks==0.0.74 +zha-quirks==0.0.75 # homeassistant.components.zhong_hong zhong_hong_hvac==1.0.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1f89c83485b..52e6d0a0ea9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1647,7 +1647,7 @@ youless-api==0.16 zeroconf==0.38.6 # homeassistant.components.zha -zha-quirks==0.0.74 +zha-quirks==0.0.75 # homeassistant.components.zha zigpy-deconz==0.16.0 From f4d280b59db28dda196a93b69112368de5e9d24e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 31 May 2022 20:30:33 +0200 Subject: [PATCH 911/930] Update frontend to 20220531.0 (#72775) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index 48488bc8f47..d9e80b4eff8 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20220526.0"], + "requirements": ["home-assistant-frontend==20220531.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a43b4f99f63..a3d8a00bcfb 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ ciso8601==2.2.0 cryptography==36.0.2 fnvhash==0.1.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220526.0 +home-assistant-frontend==20220531.0 httpx==0.23.0 ifaddr==0.1.7 jinja2==3.1.2 diff --git a/requirements_all.txt b/requirements_all.txt index d9fd7a6a34e..23b333113b0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -822,7 +822,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220526.0 +home-assistant-frontend==20220531.0 # homeassistant.components.home_connect homeconnect==0.7.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 52e6d0a0ea9..3eabedeb942 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -589,7 +589,7 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220526.0 +home-assistant-frontend==20220531.0 # homeassistant.components.home_connect homeconnect==0.7.0 From a54a5b2d2098fe962010cc47ebb84beb62e91115 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 May 2022 09:24:18 -1000 Subject: [PATCH 912/930] Fix queries for logbook context_ids running in the wrong executor (#72778) --- homeassistant/components/logbook/websocket_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/logbook/websocket_api.py b/homeassistant/components/logbook/websocket_api.py index 0bb7877b95b..82b1db1081c 100644 --- a/homeassistant/components/logbook/websocket_api.py +++ b/homeassistant/components/logbook/websocket_api.py @@ -475,7 +475,7 @@ async def ws_get_events( ) connection.send_message( - await hass.async_add_executor_job( + await get_instance(hass).async_add_executor_job( _ws_formatted_get_events, msg["id"], start_time, From 647df29a0038a225ca57831631baf91a95f5196a Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 31 May 2022 22:08:50 +0200 Subject: [PATCH 913/930] Don't set headers kwargs multiple times (#72779) --- homeassistant/helpers/config_entry_oauth2_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/config_entry_oauth2_flow.py b/homeassistant/helpers/config_entry_oauth2_flow.py index 365ced24929..9322d6e9dc1 100644 --- a/homeassistant/helpers/config_entry_oauth2_flow.py +++ b/homeassistant/helpers/config_entry_oauth2_flow.py @@ -493,13 +493,13 @@ async def async_oauth2_request( This method will not refresh tokens. Use OAuth2 session for that. """ session = async_get_clientsession(hass) - + headers = kwargs.pop("headers", {}) return await session.request( method, url, **kwargs, headers={ - **(kwargs.get("headers") or {}), + **headers, "authorization": f"Bearer {token['access_token']}", }, ) From 9effb78a7fd4c154b931c5c4d412b0038dea4883 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 May 2022 10:08:04 -1000 Subject: [PATCH 914/930] Prevent live logbook from sending state changed events when we only want device ids (#72780) --- homeassistant/components/logbook/helpers.py | 6 ++++++ tests/components/logbook/test_websocket_api.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/homeassistant/components/logbook/helpers.py b/homeassistant/components/logbook/helpers.py index cc9ea238f8b..de021994b8d 100644 --- a/homeassistant/components/logbook/helpers.py +++ b/homeassistant/components/logbook/helpers.py @@ -132,6 +132,12 @@ def async_subscribe_events( if not _is_state_filtered(ent_reg, state): target(event) + if device_ids and not entity_ids: + # No entities to subscribe to but we are filtering + # on device ids so we do not want to get any state + # changed events + return + if entity_ids: subscriptions.append( async_track_state_change_event( diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 1d35d6d897d..291c487b35b 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -1743,6 +1743,8 @@ async def test_subscribe_unsubscribe_logbook_stream_device( assert msg["type"] == "event" assert msg["event"]["events"] == [] + hass.states.async_set("binary_sensor.should_not_appear", STATE_ON) + hass.states.async_set("binary_sensor.should_not_appear", STATE_OFF) hass.bus.async_fire("mock_event", {"device_id": device.id}) await hass.async_block_till_done() From c3acdcb2c8614c87db11cf4160fa4aab9aa053b2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 31 May 2022 13:22:38 -0700 Subject: [PATCH 915/930] Bumped version to 2022.6.0b6 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index bd49bb0e1e2..1eb39c69f9d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b5" +PATCH_VERSION: Final = "0b6" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 3d52bda7738..d7bef63ded2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b5 +version = 2022.6.0b6 url = https://www.home-assistant.io/ [options] From de0c672cc24054ad03709c67172d776f6a9aff21 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 May 2022 11:35:28 -1000 Subject: [PATCH 916/930] Ensure the statistics_meta table is using the dynamic row format (#72784) --- homeassistant/components/recorder/migration.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/homeassistant/components/recorder/migration.py b/homeassistant/components/recorder/migration.py index eadfc543b59..bc636d34b10 100644 --- a/homeassistant/components/recorder/migration.py +++ b/homeassistant/components/recorder/migration.py @@ -712,6 +712,17 @@ def _apply_update( # noqa: C901 elif new_version == 29: # Recreate statistics_meta index to block duplicated statistic_id _drop_index(session_maker, "statistics_meta", "ix_statistics_meta_statistic_id") + if engine.dialect.name == SupportedDialect.MYSQL: + # Ensure the row format is dynamic or the index + # unique will be too large + with session_scope(session=session_maker()) as session: + connection = session.connection() + # This is safe to run multiple times and fast since the table is small + connection.execute( + text( + "ALTER TABLE statistics_meta ENGINE=InnoDB, ROW_FORMAT=DYNAMIC" + ) + ) try: _create_index( session_maker, "statistics_meta", "ix_statistics_meta_statistic_id" From 860644784860a72ef0a312434e348940ee8929af Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 1 Jun 2022 05:35:56 +0200 Subject: [PATCH 917/930] Improve cast HLS detection (#72787) --- homeassistant/components/cast/helpers.py | 9 +++++---- tests/components/cast/test_helpers.py | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/cast/helpers.py b/homeassistant/components/cast/helpers.py index dfeb9fce25b..d7419f69563 100644 --- a/homeassistant/components/cast/helpers.py +++ b/homeassistant/components/cast/helpers.py @@ -266,10 +266,8 @@ async def parse_m3u(hass, url): hls_content_types = ( # https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10 "application/vnd.apple.mpegurl", - # Some sites serve these as the informal HLS m3u type. - "application/x-mpegurl", - "audio/mpegurl", - "audio/x-mpegurl", + # Additional informal types used by Mozilla gecko not included as they + # don't reliably indicate HLS streams ) m3u_data = await _fetch_playlist(hass, url, hls_content_types) m3u_lines = m3u_data.splitlines() @@ -292,6 +290,9 @@ async def parse_m3u(hass, url): elif line.startswith("#EXT-X-VERSION:"): # HLS stream, supported by cast devices raise PlaylistSupported("HLS") + elif line.startswith("#EXT-X-STREAM-INF:"): + # HLS stream, supported by cast devices + raise PlaylistSupported("HLS") elif line.startswith("#"): # Ignore other extensions continue diff --git a/tests/components/cast/test_helpers.py b/tests/components/cast/test_helpers.py index d729d36a225..8ae73449b43 100644 --- a/tests/components/cast/test_helpers.py +++ b/tests/components/cast/test_helpers.py @@ -27,6 +27,11 @@ from tests.common import load_fixture "rthkaudio2.m3u8", "application/vnd.apple.mpegurl", ), + ( + "https://rthkaudio2-lh.akamaihd.net/i/radio2_1@355865/master.m3u8", + "rthkaudio2.m3u8", + None, + ), ), ) async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, content_type): @@ -38,11 +43,12 @@ async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, conten @pytest.mark.parametrize( - "url,fixture,expected_playlist", + "url,fixture,content_type,expected_playlist", ( ( "https://sverigesradio.se/topsy/direkt/209-hi-mp3.m3u", "209-hi-mp3.m3u", + "audio/x-mpegurl", [ PlaylistItem( length=["-1"], @@ -54,6 +60,7 @@ async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, conten ( "https://sverigesradio.se/topsy/direkt/209-hi-mp3.m3u", "209-hi-mp3_bad_extinf.m3u", + "audio/x-mpegurl", [ PlaylistItem( length=None, @@ -65,6 +72,7 @@ async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, conten ( "https://sverigesradio.se/topsy/direkt/209-hi-mp3.m3u", "209-hi-mp3_no_extinf.m3u", + "audio/x-mpegurl", [ PlaylistItem( length=None, @@ -76,6 +84,7 @@ async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, conten ( "http://sverigesradio.se/topsy/direkt/164-hi-aac.pls", "164-hi-aac.pls", + "audio/x-mpegurl", [ PlaylistItem( length="-1", @@ -86,9 +95,12 @@ async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, conten ), ), ) -async def test_parse_playlist(hass, aioclient_mock, url, fixture, expected_playlist): +async def test_parse_playlist( + hass, aioclient_mock, url, fixture, content_type, expected_playlist +): """Test playlist parsing of HLS playlist.""" - aioclient_mock.get(url, text=load_fixture(fixture, "cast")) + headers = {"content-type": content_type} + aioclient_mock.get(url, text=load_fixture(fixture, "cast"), headers=headers) playlist = await parse_playlist(hass, url) assert expected_playlist == playlist From e60dc1b503963b9b18396d6e793865668133c53d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 31 May 2022 23:57:16 +0200 Subject: [PATCH 918/930] Stringify mikrotik device_tracker name (#72788) Co-authored-by: J. Nick Koston --- homeassistant/components/mikrotik/device_tracker.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/mikrotik/device_tracker.py b/homeassistant/components/mikrotik/device_tracker.py index dee4a5de08d..16c3ed233d8 100644 --- a/homeassistant/components/mikrotik/device_tracker.py +++ b/homeassistant/components/mikrotik/device_tracker.py @@ -102,7 +102,8 @@ class MikrotikHubTracker(ScannerEntity): @property def name(self) -> str: """Return the name of the client.""" - return self.device.name + # Stringify to ensure we return a string + return str(self.device.name) @property def hostname(self) -> str: From 0db986374634a892b1fb3e46662e81eb77ac3a99 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 31 May 2022 14:58:45 -0700 Subject: [PATCH 919/930] Sync entities when enabling/disabling Google Assistant (#72791) --- homeassistant/components/cloud/google_config.py | 9 ++++++++- homeassistant/components/google_assistant/helpers.py | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index 8f190103e87..f30be66cb42 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -195,6 +195,8 @@ class CloudGoogleConfig(AbstractConfig): ): await async_setup_component(self.hass, GOOGLE_DOMAIN, {}) + sync_entities = False + if self.should_report_state != self.is_reporting_state: if self.should_report_state: self.async_enable_report_state() @@ -203,7 +205,7 @@ class CloudGoogleConfig(AbstractConfig): # State reporting is reported as a property on entities. # So when we change it, we need to sync all entities. - await self.async_sync_entities_all() + sync_entities = True # If entity prefs are the same or we have filter in config.yaml, # don't sync. @@ -215,12 +217,17 @@ class CloudGoogleConfig(AbstractConfig): if self.enabled and not self.is_local_sdk_active: self.async_enable_local_sdk() + sync_entities = True elif not self.enabled and self.is_local_sdk_active: self.async_disable_local_sdk() + sync_entities = True self._cur_entity_prefs = prefs.google_entity_configs self._cur_default_expose = prefs.google_default_expose + if sync_entities: + await self.async_sync_entities_all() + @callback def _handle_entity_registry_updated(self, event: Event) -> None: """Handle when entity registry updated.""" diff --git a/homeassistant/components/google_assistant/helpers.py b/homeassistant/components/google_assistant/helpers.py index b425367f5c3..15a8d832403 100644 --- a/homeassistant/components/google_assistant/helpers.py +++ b/homeassistant/components/google_assistant/helpers.py @@ -213,6 +213,9 @@ class AbstractConfig(ABC): async def async_sync_entities_all(self): """Sync all entities to Google for all registered agents.""" + if not self._store.agent_user_ids: + return 204 + res = await gather( *( self.async_sync_entities(agent_user_id) From 668f56f103e87d904aeed989b1d58fca1cc1d01b Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Wed, 1 Jun 2022 04:40:42 +0100 Subject: [PATCH 920/930] Fix #72749 (#72794) --- homeassistant/components/utility_meter/sensor.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index 8df86b3e5a8..d2a2d2ba8ca 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -99,6 +99,13 @@ PAUSED = "paused" COLLECTING = "collecting" +def validate_is_number(value): + """Validate value is a number.""" + if is_number(value): + return value + raise vol.Invalid("Value is not a number") + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -167,7 +174,7 @@ async def async_setup_entry( platform.async_register_entity_service( SERVICE_CALIBRATE_METER, - {vol.Required(ATTR_VALUE): vol.Coerce(Decimal)}, + {vol.Required(ATTR_VALUE): validate_is_number}, "async_calibrate", ) @@ -244,7 +251,7 @@ async def async_setup_platform( platform.async_register_entity_service( SERVICE_CALIBRATE_METER, - {vol.Required(ATTR_VALUE): vol.Coerce(Decimal)}, + {vol.Required(ATTR_VALUE): validate_is_number}, "async_calibrate", ) @@ -446,8 +453,8 @@ class UtilityMeterSensor(RestoreSensor): async def async_calibrate(self, value): """Calibrate the Utility Meter with a given value.""" - _LOGGER.debug("Calibrate %s = %s", self._name, value) - self._state = value + _LOGGER.debug("Calibrate %s = %s type(%s)", self._name, value, type(value)) + self._state = Decimal(str(value)) self.async_write_ha_state() async def async_added_to_hass(self): From 17a3c628217c3a1b9e94181831dfd8b1a0c5160a Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 31 May 2022 22:36:45 -0500 Subject: [PATCH 921/930] Support add/next/play/replace enqueue options in Sonos (#72800) --- .../components/sonos/media_player.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index fd37e546105..b970b32b87a 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -576,6 +576,14 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): self.set_shuffle(True) if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: plex_plugin.add_to_queue(result.media) + elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + MediaPlayerEnqueue.NEXT, + MediaPlayerEnqueue.PLAY, + ): + pos = (self.media.queue_position or 0) + 1 + new_pos = plex_plugin.add_to_queue(result.media, position=pos) + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + soco.play_from_queue(new_pos - 1) else: soco.clear_queue() plex_plugin.add_to_queue(result.media) @@ -586,6 +594,14 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): if share_link.is_share_link(media_id): if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: share_link.add_share_link_to_queue(media_id) + elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + MediaPlayerEnqueue.NEXT, + MediaPlayerEnqueue.PLAY, + ): + pos = (self.media.queue_position or 0) + 1 + new_pos = share_link.add_share_link_to_queue(media_id, position=pos) + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + soco.play_from_queue(new_pos - 1) else: soco.clear_queue() share_link.add_share_link_to_queue(media_id) @@ -596,6 +612,14 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: soco.add_uri_to_queue(media_id) + elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + MediaPlayerEnqueue.NEXT, + MediaPlayerEnqueue.PLAY, + ): + pos = (self.media.queue_position or 0) + 1 + new_pos = soco.add_uri_to_queue(media_id, position=pos) + if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + soco.play_from_queue(new_pos - 1) else: soco.play_uri(media_id, force_radio=is_radio) elif media_type == MEDIA_TYPE_PLAYLIST: From 354149e43c320409e2e5909fcd39abe4dedba2a0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 31 May 2022 20:41:59 -0700 Subject: [PATCH 922/930] Bumped version to 2022.6.0b7 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 1eb39c69f9d..0562776d589 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b6" +PATCH_VERSION: Final = "0b7" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index d7bef63ded2..88cf80344f6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b6 +version = 2022.6.0b7 url = https://www.home-assistant.io/ [options] From 1274448de13629f33f1f87d7ecd953d6835c623c Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Wed, 1 Jun 2022 02:04:35 -0400 Subject: [PATCH 923/930] Add package constraint for pydantic (#72799) Co-authored-by: J. Nick Koston --- homeassistant/package_constraints.txt | 4 ++++ script/gen_requirements_all.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index a3d8a00bcfb..c1bbe51755f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -106,3 +106,7 @@ authlib<1.0 # Pin backoff for compatibility until most libraries have been updated # https://github.com/home-assistant/core/pull/70817 backoff<2.0 + +# Breaking change in version +# https://github.com/samuelcolvin/pydantic/issues/4092 +pydantic!=1.9.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index f049080b7fa..0fd31430aa4 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -122,6 +122,10 @@ authlib<1.0 # Pin backoff for compatibility until most libraries have been updated # https://github.com/home-assistant/core/pull/70817 backoff<2.0 + +# Breaking change in version +# https://github.com/samuelcolvin/pydantic/issues/4092 +pydantic!=1.9.1 """ IGNORE_PRE_COMMIT_HOOK_ID = ( From 384cb44d15e480d9b95382221fa39afba7324da6 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 31 May 2022 23:39:07 -0500 Subject: [PATCH 924/930] Cleanup handling of new enqueue & announce features in Sonos (#72801) --- .../components/sonos/media_player.py | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/sonos/media_player.py b/homeassistant/components/sonos/media_player.py index b970b32b87a..cd129d82843 100644 --- a/homeassistant/components/sonos/media_player.py +++ b/homeassistant/components/sonos/media_player.py @@ -25,6 +25,7 @@ from homeassistant.components.media_player import ( ) from homeassistant.components.media_player.const import ( ATTR_INPUT_SOURCE, + ATTR_MEDIA_ANNOUNCE, ATTR_MEDIA_ENQUEUE, MEDIA_TYPE_ALBUM, MEDIA_TYPE_ARTIST, @@ -527,7 +528,9 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): self.coordinator.soco.clear_queue() @soco_error() - def play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: + def play_media( # noqa: C901 + self, media_type: str, media_id: str, **kwargs: Any + ) -> None: """ Send the play_media command to the media player. @@ -539,6 +542,12 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): If media_type is "playlist", media_id should be a Sonos Playlist name. Otherwise, media_id should be a URI. """ + # Use 'replace' as the default enqueue option + enqueue = kwargs.get(ATTR_MEDIA_ENQUEUE, MediaPlayerEnqueue.REPLACE) + if kwargs.get(ATTR_MEDIA_ANNOUNCE): + # Temporary workaround until announce support is added + enqueue = MediaPlayerEnqueue.PLAY + if spotify.is_spotify_media_type(media_type): media_type = spotify.resolve_spotify_media_type(media_type) media_id = spotify.spotify_uri_from_media_browser_url(media_id) @@ -574,17 +583,17 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): ) if result.shuffle: self.set_shuffle(True) - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: + if enqueue == MediaPlayerEnqueue.ADD: plex_plugin.add_to_queue(result.media) - elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = plex_plugin.add_to_queue(result.media, position=pos) - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) - else: + elif enqueue == MediaPlayerEnqueue.REPLACE: soco.clear_queue() plex_plugin.add_to_queue(result.media) soco.play_from_queue(0) @@ -592,17 +601,17 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): share_link = self.coordinator.share_link if share_link.is_share_link(media_id): - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: + if enqueue == MediaPlayerEnqueue.ADD: share_link.add_share_link_to_queue(media_id) - elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = share_link.add_share_link_to_queue(media_id, position=pos) - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) - else: + elif enqueue == MediaPlayerEnqueue.REPLACE: soco.clear_queue() share_link.add_share_link_to_queue(media_id) soco.play_from_queue(0) @@ -610,17 +619,17 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity): # If media ID is a relative URL, we serve it from HA. media_id = async_process_play_media_url(self.hass, media_id) - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.ADD: + if enqueue == MediaPlayerEnqueue.ADD: soco.add_uri_to_queue(media_id) - elif kwargs.get(ATTR_MEDIA_ENQUEUE) in ( + elif enqueue in ( MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY, ): pos = (self.media.queue_position or 0) + 1 new_pos = soco.add_uri_to_queue(media_id, position=pos) - if kwargs.get(ATTR_MEDIA_ENQUEUE) == MediaPlayerEnqueue.PLAY: + if enqueue == MediaPlayerEnqueue.PLAY: soco.play_from_queue(new_pos - 1) - else: + elif enqueue == MediaPlayerEnqueue.REPLACE: soco.play_uri(media_id, force_radio=is_radio) elif media_type == MEDIA_TYPE_PLAYLIST: if media_id.startswith("S:"): From 9bd2e3ad7c4d614d419547d9cd765527b9fe8315 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 1 Jun 2022 03:12:54 -0700 Subject: [PATCH 925/930] Don't trigger entity sync when Google Assistant gets disabled (#72805) --- homeassistant/components/cloud/google_config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/cloud/google_config.py b/homeassistant/components/cloud/google_config.py index f30be66cb42..a0a68aaf84a 100644 --- a/homeassistant/components/cloud/google_config.py +++ b/homeassistant/components/cloud/google_config.py @@ -220,7 +220,6 @@ class CloudGoogleConfig(AbstractConfig): sync_entities = True elif not self.enabled and self.is_local_sdk_active: self.async_disable_local_sdk() - sync_entities = True self._cur_entity_prefs = prefs.google_entity_configs self._cur_default_expose = prefs.google_default_expose From 9e723f9b6d3ba79687e66df312bf4ffce6c4d782 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 May 2022 22:56:05 -1000 Subject: [PATCH 926/930] Bump sqlalchemy to 1.4.37 (#72809) Fixes a bug where reconnects might fail with MySQL 8.0.24+ Changelog: https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html#change-1.4.37 --- homeassistant/components/recorder/manifest.json | 2 +- homeassistant/components/sql/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/recorder/manifest.json b/homeassistant/components/recorder/manifest.json index 0fb44f99ae2..38897c42e1a 100644 --- a/homeassistant/components/recorder/manifest.json +++ b/homeassistant/components/recorder/manifest.json @@ -2,7 +2,7 @@ "domain": "recorder", "name": "Recorder", "documentation": "https://www.home-assistant.io/integrations/recorder", - "requirements": ["sqlalchemy==1.4.36", "fnvhash==0.1.0", "lru-dict==1.1.7"], + "requirements": ["sqlalchemy==1.4.37", "fnvhash==0.1.0", "lru-dict==1.1.7"], "codeowners": ["@home-assistant/core"], "quality_scale": "internal", "iot_class": "local_push" diff --git a/homeassistant/components/sql/manifest.json b/homeassistant/components/sql/manifest.json index c779e4567cd..4562b945008 100644 --- a/homeassistant/components/sql/manifest.json +++ b/homeassistant/components/sql/manifest.json @@ -2,7 +2,7 @@ "domain": "sql", "name": "SQL", "documentation": "https://www.home-assistant.io/integrations/sql", - "requirements": ["sqlalchemy==1.4.36"], + "requirements": ["sqlalchemy==1.4.37"], "codeowners": ["@dgomes", "@gjohansson-ST"], "config_flow": true, "iot_class": "local_polling" diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index c1bbe51755f..ad2539233f4 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -29,7 +29,7 @@ pyudev==0.22.0 pyyaml==6.0 requests==2.27.1 scapy==2.4.5 -sqlalchemy==1.4.36 +sqlalchemy==1.4.37 typing-extensions>=3.10.0.2,<5.0 voluptuous-serialize==2.5.0 voluptuous==0.13.1 diff --git a/requirements_all.txt b/requirements_all.txt index 23b333113b0..d1bffba6971 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2223,7 +2223,7 @@ spotipy==2.19.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.36 +sqlalchemy==1.4.37 # homeassistant.components.srp_energy srpenergy==1.3.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3eabedeb942..2c819e1ef3c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1465,7 +1465,7 @@ spotipy==2.19.0 # homeassistant.components.recorder # homeassistant.components.sql -sqlalchemy==1.4.36 +sqlalchemy==1.4.37 # homeassistant.components.srp_energy srpenergy==1.3.6 From 1139136365b8624e4f4f242140952e27153ef25b Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Wed, 1 Jun 2022 12:33:13 +0200 Subject: [PATCH 927/930] Add Motionblinds WoodShutter support (#72814) --- .../components/motion_blinds/cover.py | 58 +++++++++++++++++++ .../components/motion_blinds/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index a9f6df82ae0..7bac3a5fb20 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -9,6 +9,7 @@ from homeassistant.components.cover import ( ATTR_TILT_POSITION, CoverDeviceClass, CoverEntity, + CoverEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -64,6 +65,10 @@ TILT_DEVICE_MAP = { BlindType.VerticalBlindRight: CoverDeviceClass.BLIND, } +TILT_ONLY_DEVICE_MAP = { + BlindType.WoodShutter: CoverDeviceClass.BLIND, +} + TDBU_DEVICE_MAP = { BlindType.TopDownBottomUp: CoverDeviceClass.SHADE, } @@ -108,6 +113,16 @@ async def async_setup_entry( ) ) + elif blind.type in TILT_ONLY_DEVICE_MAP: + entities.append( + MotionTiltOnlyDevice( + coordinator, + blind, + TILT_ONLY_DEVICE_MAP[blind.type], + sw_version, + ) + ) + elif blind.type in TDBU_DEVICE_MAP: entities.append( MotionTDBUDevice( @@ -356,6 +371,49 @@ class MotionTiltDevice(MotionPositionDevice): await self.hass.async_add_executor_job(self._blind.Stop) +class MotionTiltOnlyDevice(MotionTiltDevice): + """Representation of a Motion Blind Device.""" + + _restore_tilt = False + + @property + def supported_features(self): + """Flag supported features.""" + supported_features = ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.STOP_TILT + ) + + if self.current_cover_tilt_position is not None: + supported_features |= CoverEntityFeature.SET_TILT_POSITION + + return supported_features + + @property + def current_cover_position(self): + """Return current position of cover.""" + return None + + @property + def is_closed(self): + """Return if the cover is closed or not.""" + if self._blind.angle is None: + return None + return self._blind.angle == 0 + + async def async_set_absolute_position(self, **kwargs): + """Move the cover to a specific absolute position (see TDBU).""" + angle = kwargs.get(ATTR_TILT_POSITION) + if angle is not None: + angle = angle * 180 / 100 + async with self._api_lock: + await self.hass.async_add_executor_job( + self._blind.Set_angle, + angle, + ) + + class MotionTDBUDevice(MotionPositionDevice): """Representation of a Motion Top Down Bottom Up blind Device.""" diff --git a/homeassistant/components/motion_blinds/manifest.json b/homeassistant/components/motion_blinds/manifest.json index 1e8ad0eb0a1..bc09d3e9e38 100644 --- a/homeassistant/components/motion_blinds/manifest.json +++ b/homeassistant/components/motion_blinds/manifest.json @@ -3,7 +3,7 @@ "name": "Motion Blinds", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/motion_blinds", - "requirements": ["motionblinds==0.6.7"], + "requirements": ["motionblinds==0.6.8"], "dependencies": ["network"], "dhcp": [ { "registered_devices": true }, diff --git a/requirements_all.txt b/requirements_all.txt index d1bffba6971..6341528b09a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1038,7 +1038,7 @@ mitemp_bt==0.0.5 moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds -motionblinds==0.6.7 +motionblinds==0.6.8 # homeassistant.components.motioneye motioneye-client==0.3.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2c819e1ef3c..b5767c1e468 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -715,7 +715,7 @@ minio==5.0.10 moehlenhoff-alpha2==1.2.1 # homeassistant.components.motion_blinds -motionblinds==0.6.7 +motionblinds==0.6.8 # homeassistant.components.motioneye motioneye-client==0.3.12 From 2f3359f376bc9e69e8118ef7e7f63cef8b81964d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 31 May 2022 23:56:06 -1000 Subject: [PATCH 928/930] Fix purge of legacy database events that are not state changed (#72815) --- homeassistant/components/recorder/queries.py | 2 +- tests/components/recorder/test_purge.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/recorder/queries.py b/homeassistant/components/recorder/queries.py index 5532c5c0703..e27d3d692cc 100644 --- a/homeassistant/components/recorder/queries.py +++ b/homeassistant/components/recorder/queries.py @@ -631,7 +631,7 @@ def find_legacy_event_state_and_attributes_and_data_ids_to_purge( lambda: select( Events.event_id, Events.data_id, States.state_id, States.attributes_id ) - .join(States, Events.event_id == States.event_id) + .outerjoin(States, Events.event_id == States.event_id) .filter(Events.time_fired < purge_before) .limit(MAX_ROWS_TO_PURGE) ) diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index c8ba5e9d076..f4e998c5388 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -26,7 +26,7 @@ from homeassistant.components.recorder.services import ( ) from homeassistant.components.recorder.tasks import PurgeTask from homeassistant.components.recorder.util import session_scope -from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON +from homeassistant.const import EVENT_STATE_CHANGED, EVENT_THEMES_UPDATED, STATE_ON from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType from homeassistant.util import dt as dt_util @@ -925,6 +925,15 @@ async def test_purge_without_state_attributes_filtered_states_to_empty( time_fired=timestamp, ) ) + session.add( + Events( + event_id=event_id + 1, + event_type=EVENT_THEMES_UPDATED, + event_data="{}", + origin="LOCAL", + time_fired=timestamp, + ) + ) service_data = {"keep_days": 10} _add_db_entries(hass) From bf47d86d30f374e6e2cd08b3c7e747eb9e30a083 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Jun 2022 00:33:46 -1000 Subject: [PATCH 929/930] Fix logbook spinner never disappearing when all entities are filtered (#72816) --- .../components/logbook/websocket_api.py | 4 +- .../components/logbook/test_websocket_api.py | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logbook/websocket_api.py b/homeassistant/components/logbook/websocket_api.py index 82b1db1081c..1af44440803 100644 --- a/homeassistant/components/logbook/websocket_api.py +++ b/homeassistant/components/logbook/websocket_api.py @@ -356,7 +356,7 @@ async def ws_event_stream( ) await _async_wait_for_recorder_sync(hass) - if not subscriptions: + if msg_id not in connection.subscriptions: # Unsubscribe happened while waiting for recorder return @@ -388,6 +388,8 @@ async def ws_event_stream( if not subscriptions: # Unsubscribe happened while waiting for formatted events + # or there are no supported entities (all UOM or state class) + # or devices return live_stream.task = asyncio.create_task( diff --git a/tests/components/logbook/test_websocket_api.py b/tests/components/logbook/test_websocket_api.py index 291c487b35b..2dd08ec44ce 100644 --- a/tests/components/logbook/test_websocket_api.py +++ b/tests/components/logbook/test_websocket_api.py @@ -2089,3 +2089,52 @@ async def test_recorder_is_far_behind(hass, recorder_mock, hass_ws_client, caplo assert msg["success"] assert "Recorder is behind" in caplog.text + + +@patch("homeassistant.components.logbook.websocket_api.EVENT_COALESCE_TIME", 0) +async def test_subscribe_all_entities_have_uom(hass, recorder_mock, hass_ws_client): + """Test subscribe/unsubscribe logbook stream with entities that are always filtered.""" + now = dt_util.utcnow() + await asyncio.gather( + *[ + async_setup_component(hass, comp, {}) + for comp in ("homeassistant", "logbook", "automation", "script") + ] + ) + await async_wait_recording_done(hass) + + init_count = sum(hass.bus.async_listeners().values()) + hass.states.async_set("sensor.uom", "1", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + hass.states.async_set("sensor.uom", "2", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + hass.states.async_set("sensor.uom", "3", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + + await async_wait_recording_done(hass) + websocket_client = await hass_ws_client() + await websocket_client.send_json( + { + "id": 7, + "type": "logbook/event_stream", + "start_time": now.isoformat(), + "entity_ids": ["sensor.uom"], + } + ) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == TYPE_RESULT + assert msg["success"] + + hass.states.async_set("sensor.uom", "1", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + hass.states.async_set("sensor.uom", "2", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + hass.states.async_set("sensor.uom", "3", {ATTR_UNIT_OF_MEASUREMENT: "any"}) + + msg = await asyncio.wait_for(websocket_client.receive_json(), 2) + assert msg["id"] == 7 + assert msg["type"] == "event" + assert msg["event"]["events"] == [] + + await websocket_client.close() + await hass.async_block_till_done() + + # Check our listener got unsubscribed + assert sum(hass.bus.async_listeners().values()) == init_count From 39da7a93ecc15cb414b0a13c2b0ee48deb17756e Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 1 Jun 2022 13:04:12 +0200 Subject: [PATCH 930/930] Bumped version to 2022.6.0 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 0562776d589..428aa84d5fb 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 6 -PATCH_VERSION: Final = "0b7" +PATCH_VERSION: Final = "0" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/setup.cfg b/setup.cfg index 88cf80344f6..7f1e739803b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -version = 2022.6.0b7 +version = 2022.6.0 url = https://www.home-assistant.io/ [options]